From 25b16f1c5824b8bfc048584fc0eddb9aef1ced06 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 12:54:57 +0000 Subject: [PATCH 001/146] Remove AMQP code and Proton library dependencies & flags --- configure.ac | 35 --- contrib/amqp/amqp_sub.py | 48 --- contrib/debian/copyright | 220 -------------- contrib/gitian-descriptors/gitian-linux.yml | 2 +- depends/Makefile | 5 +- depends/packages/packages.mk | 1 - depends/packages/proton.mk | 24 -- depends/patches/proton/minimal-build.patch | 314 -------------------- doc/amqp.md | 136 --------- qa/bitcoinz/updatecheck.py | 4 - qa/pull-tester/rpc-tests.sh | 4 - qa/pull-tester/tests-config.sh.in | 1 - qa/rpc-tests/proton_test.py | 119 -------- src/Makefile.am | 20 -- src/Makefile.gtest.include | 3 - src/Makefile.test.include | 4 - src/amqp/amqpabstractnotifier.cpp | 21 -- src/amqp/amqpabstractnotifier.h | 43 --- src/amqp/amqpconfig.h | 33 -- src/amqp/amqpnotificationinterface.cpp | 136 --------- src/amqp/amqpnotificationinterface.h | 36 --- src/amqp/amqppublishnotifier.cpp | 179 ----------- src/amqp/amqppublishnotifier.h | 56 ---- src/amqp/amqpsender.h | 115 ------- src/init.cpp | 39 --- zcutil/build.sh | 17 +- 26 files changed, 5 insertions(+), 1610 deletions(-) delete mode 100644 contrib/amqp/amqp_sub.py delete mode 100644 depends/packages/proton.mk delete mode 100644 depends/patches/proton/minimal-build.patch delete mode 100644 doc/amqp.md delete mode 100755 qa/rpc-tests/proton_test.py delete mode 100644 src/amqp/amqpabstractnotifier.cpp delete mode 100644 src/amqp/amqpabstractnotifier.h delete mode 100644 src/amqp/amqpconfig.h delete mode 100644 src/amqp/amqpnotificationinterface.cpp delete mode 100644 src/amqp/amqpnotificationinterface.h delete mode 100644 src/amqp/amqppublishnotifier.cpp delete mode 100644 src/amqp/amqppublishnotifier.h delete mode 100644 src/amqp/amqpsender.h diff --git a/configure.ac b/configure.ac index 7b0bd3f74..464f3de0a 100644 --- a/configure.ac +++ b/configure.ac @@ -96,12 +96,6 @@ AC_ARG_ENABLE([mining], [enable_mining=$enableval], [enable_mining=yes]) -AC_ARG_ENABLE([proton], - [AS_HELP_STRING([--enable-proton], - [enable Proton (AMQP messaging) (default is no)])], - [use_proton=$enableval], - [use_proton=no]) - AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]), [use_tests=$enableval], @@ -666,31 +660,6 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ [AC_MSG_RESULT([checking for version of libsodium... yes])], [AC_MSG_ERROR([Wrong libsodium: version >= 1.0.8 required])]) -dnl Check Qpid Proton headers and library exist -if test x$use_proton = xyes; then - AC_CHECK_HEADERS([proton/connection.hpp], - [], - [AC_MSG_WARN([Proton headers not found, disabling Proton support]) - use_proton=no]) - AC_CHECK_LIB([qpid-proton-cpp-static], [main], - [PROTON_LIBS="-lqpid-proton-cpp-static"], - [AC_MSG_WARN([Proton qpid-proton-cpp-static library not found, disabling Proton support]) - use_proton=no]) - AC_CHECK_LIB([qpid-proton-core-static], [main], - [PROTON_LIBS+=" -lqpid-proton-core-static"], - [AC_MSG_WARN([Proton qpid-proton-core-static library not found, disabling Proton support]) - use_proton=no]) - AC_CHECK_LIB([qpid-proton-static], [main], - [PROTON_LIBS+=" -lqpid-proton-static"], - [AC_MSG_WARN([Proton qpid-proton-static library not found, disabling Proton support]) - use_proton=no]) -fi -if test x$use_proton = xyes; then - AC_DEFINE(ENABLE_PROTON, 1, [Define to 1 to enable Proton functions]) -else - AC_DEFINE(ENABLE_PROTON, 0, [Define to 1 to enable Proton functions]) -fi - if test x$build_bitcoin_utils$build_bitcoind$use_tests = xnonono; then use_boost=no else @@ -883,8 +852,6 @@ fi AM_CONDITIONAL([ENABLE_ZMQ], [test "x$use_zmq" = "xyes"]) -AM_CONDITIONAL([ENABLE_PROTON], [test "x$use_proton" = "xyes"]) - AC_MSG_CHECKING([whether to build test_bitcoinz]) if test x$use_tests = xyes; then AC_MSG_RESULT([yes]) @@ -953,7 +920,6 @@ AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) AC_SUBST(LIBZCASH_LIBS) -AC_SUBST(PROTON_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh]) AC_CONFIG_FILES([qa/pull-tester/tests-config.sh],[chmod +x qa/pull-tester/tests-config.sh]) @@ -1000,7 +966,6 @@ esac echo echo "Options used to compile and link:" echo " with wallet = $enable_wallet" -echo " with proton = $use_proton" echo " with zmq = $use_zmq" echo " with test = $use_tests" echo " sanitizers = $use_sanitizers" diff --git a/contrib/amqp/amqp_sub.py b/contrib/amqp/amqp_sub.py deleted file mode 100644 index de21dbb25..000000000 --- a/contrib/amqp/amqp_sub.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python2 -# Copyright (c) 2017-2020 The BitcoinZ Community -# Copyright (c) 2017-2019 The Zcash developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or https://www.opensource.org/licenses/mit-license.php . - -# Requirements: -# pip install python-qpid-proton - -import binascii -from proton.handlers import MessagingHandler -from proton.reactor import Container - -port = 5672 - -class Server(MessagingHandler): - def __init__(self, url): - super(Server, self).__init__() - self.url = url - self.senders = {} - - def on_start(self, event): - print "Listening on:", self.url - self.container = event.container - self.acceptor = event.container.listen(self.url) - - def on_message(self, event): - m = event.message - topic = m.subject - body = m.body - sequence = str( m.properties['x-opt-sequence-number'] ) - if topic == "hashablock": - print '- HASH BLOCK ('+sequence+') -' - print binascii.hexlify(body) - elif topic == "hashtx": - print '- HASH TX ('+sequence+') -' - print binascii.hexlify(body) - elif topic == "rawblock": - print '- RAW BLOCK HEADER ('+sequence+') -' - print binascii.hexlify(body[:80]) - elif topic == "rawtx": - print '- RAW TX ('+sequence+') -' - print binascii.hexlify(body) - -try: - Container(Server("127.0.0.1:%i" % port)).run() -except KeyboardInterrupt: - pass diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 32a2cafb1..733d50125 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -113,10 +113,6 @@ Files: depends/sources/google*.tar.gz Copyright: 2008 Google Inc. License: BSD-3clause-Google -Files: depends/sources/qpid-proton-*.tar.gz -Copyright: 2012-2017 The Apache Software Foundation -License: Apache-Qpid-Proton-with-BSD-Subcomponents - Files: depends/sources/utfcpp-*.tar.gz Copyright: 2006 Nemanja Trifunovic License: Boost-Software-License-1.0 @@ -1079,222 +1075,6 @@ Comment: You should have received a copy of the GNU General Public License along with this program. If not, see . -License: Apache-Qpid-Proton-with-BSD-Subcomponents - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - . - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - . - 1. Definitions. - . - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - . - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - . - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - . - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - . - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - . - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - . - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - . - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - . - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - . - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - . - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - . - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - . - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - . - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - . - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - . - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - . - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - . - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - . - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - . - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - . - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - . - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - . - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - . - END OF TERMS AND CONDITIONS - . - APPENDIX: How to apply the Apache License to your work. - . - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - . - Copyright [yyyy] [name of copyright owner] - . - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - . - PROTON SUBCOMPONENTS: - . - Proton includes freegetopt with a separate BSD license. Your use - of the source code for freegetopt is subject to the terms and - conditions of its license in examples/include/pncompat/internal/LICENSE. - . - The setup scripts for the python bindings include files derived by - PyZMQ and are licensed with a separate Modified BSD license. Use of - the source code in these setup files are subject to the terms and - conditions in the license: - proton-c/bindings/python/setuputils/PYZMQ_LICENSE.BSD. - License: GNU-All-permissive-License Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index ca7e52f40..79b346507 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -87,7 +87,7 @@ script: | BASEPREFIX=`pwd`/depends # Build dependencies for each host for i in $HOSTS; do - NO_PROTON="x" make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" + make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" done # Faketime for binaries diff --git a/depends/Makefile b/depends/Makefile index 8c81804e2..6b51cb20f 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -78,10 +78,7 @@ include packages/packages.mk wallet_packages_$(NO_WALLET) = $(wallet_packages) -NO_PROTON_$(WITH_PROTON) = 1 -proton_packages_$(NO_PROTON_) = $(proton_packages) - -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(rust_packages) $(proton_packages_) $(wallet_packages_) +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(rust_packages) $(wallet_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) all_packages = $(packages) $(native_packages) diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index fac5f4938..a789ab0c6 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -33,7 +33,6 @@ rust_crates := \ crate_winapi \ crate_winapi_x86_64_pc_windows_gnu rust_packages := rust $(rust_crates) librustzcash -proton_packages := proton zcash_packages := libsodium utfcpp packages := boost libevent zeromq $(zcash_packages) googletest native_packages := native_ccache diff --git a/depends/packages/proton.mk b/depends/packages/proton.mk deleted file mode 100644 index d974fa5fa..000000000 --- a/depends/packages/proton.mk +++ /dev/null @@ -1,24 +0,0 @@ -package=proton -$(package)_version=0.30.0 -$(package)_download_path=https://archive.apache.org/dist/qpid/proton/$($(package)_version) -$(package)_file_name=qpid-proton-$($(package)_version).tar.gz -$(package)_sha256_hash=e37fd8fb13391c3996f927839969a8f66edf35612392d0611eeac6e39e48dd33 -$(package)_patches=minimal-build.patch - -define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/minimal-build.patch && \ - mkdir -p build/proton-c/src -endef - -define $(package)_config_cmds - cd build; cmake .. -DCMAKE_CXX_STANDARD=11 -DCMAKE_INSTALL_PREFIX=/ -DSYSINSTALL_BINDINGS=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_PYTHON=OFF -DBUILD_RUBY=OFF -DBUILD_GO=OFF -DBUILD_STATIC_LIBS=ON -DLIB_SUFFIX= -DENABLE_JSONCPP= -endef - -define $(package)_build_cmds - cd build; $(MAKE) VERBOSE=1 -endef - -define $(package)_stage_cmds - cd build; $(MAKE) VERBOSE=1 DESTDIR=$($(package)_staging_prefix_dir) install -endef - diff --git a/depends/patches/proton/minimal-build.patch b/depends/patches/proton/minimal-build.patch deleted file mode 100644 index 1c0c972b6..000000000 --- a/depends/patches/proton/minimal-build.patch +++ /dev/null @@ -1,314 +0,0 @@ -diff -ur a/c/CMakeLists.txt b/c/CMakeLists.txt ---- a/c/CMakeLists.txt 2019-12-09 07:17:00.000000000 -0700 -+++ b/c/CMakeLists.txt 2020-01-08 16:15:26.837987469 -0700 -@@ -428,18 +428,18 @@ - # Can't use target_link_libraries() because cmake 2.8.12 doesn't allow object libraries as the first param - # otherwise for cmake 3.9 and on this would be: - # target_link_libraries (qpid-proton-core-objects ${SSL_LIB} ${SASL_LIB} ${TIME_LIB} ${PLATFORM_LIBS}) --target_compile_definitions(qpid-proton-core-objects PRIVATE $) --target_compile_options (qpid-proton-core-objects PRIVATE $) --target_include_directories(qpid-proton-core-objects PRIVATE $) -- --add_library (qpid-proton-core SHARED $) --target_link_libraries (qpid-proton-core ${SSL_LIB} ${SASL_LIB} ${TIME_LIB} ${PLATFORM_LIBS}) --set_target_properties (qpid-proton-core -- PROPERTIES -- VERSION "${PN_LIB_CORE_VERSION}" -- SOVERSION "${PN_LIB_CORE_MAJOR_VERSION}" -- LINK_FLAGS "${CATCH_UNDEFINED} ${LTO}" --) -+#target_compile_definitions(qpid-proton-core-objects PRIVATE $) -+#target_compile_options (qpid-proton-core-objects PRIVATE $) -+#target_include_directories(qpid-proton-core-objects PRIVATE $) -+ -+#add_library (qpid-proton-core SHARED $) -+#target_link_libraries (qpid-proton-core ${SSL_LIB} ${SASL_LIB} ${TIME_LIB} ${PLATFORM_LIBS}) -+#set_target_properties (qpid-proton-core -+# PROPERTIES -+# VERSION "${PN_LIB_CORE_VERSION}" -+# SOVERSION "${PN_LIB_CORE_MAJOR_VERSION}" -+# LINK_FLAGS "${CATCH_UNDEFINED} ${LTO}" -+#) - - if (BUILD_STATIC_LIBS) - add_library (qpid-proton-core-static STATIC ${qpid-proton-core-src}) -@@ -454,14 +454,14 @@ - ${qpid-proton-include-extra} - ) - --add_library (qpid-proton SHARED $ ${qpid-proton-noncore-src}) --target_link_libraries (qpid-proton LINK_PRIVATE ${SSL_LIB} ${SASL_LIB} ${TIME_LIB} ${PLATFORM_LIBS} ${PROACTOR_LIBS}) --set_target_properties (qpid-proton -- PROPERTIES -- VERSION "${PN_LIB_LEGACY_VERSION}" -- SOVERSION "${PN_LIB_LEGACY_MAJOR_VERSION}" -- LINK_FLAGS "${CATCH_UNDEFINED} ${LTO}" --) -+# add_library (qpid-proton SHARED $ ${qpid-proton-noncore-src}) -+# target_link_libraries (qpid-proton LINK_PRIVATE ${SSL_LIB} ${SASL_LIB} ${TIME_LIB} ${PLATFORM_LIBS} ${PROACTOR_LIBS}) -+# set_target_properties (qpid-proton -+# PROPERTIES -+# VERSION "${PN_LIB_LEGACY_VERSION}" -+# SOVERSION "${PN_LIB_LEGACY_MAJOR_VERSION}" -+# LINK_FLAGS "${CATCH_UNDEFINED} ${LTO}" -+# ) - - if (BUILD_STATIC_LIBS) - add_library(qpid-proton-static STATIC ${qpid-proton-core-src} ${qpid-proton-noncore-src}) -@@ -482,15 +482,15 @@ - - if (qpid-proton-proactor) - set(HAS_PROACTOR True) -- add_library (qpid-proton-proactor SHARED ${qpid-proton-proactor}) -- target_link_libraries (qpid-proton-proactor LINK_PUBLIC qpid-proton-core) -- target_link_libraries (qpid-proton-proactor LINK_PRIVATE ${PLATFORM_LIBS} ${PROACTOR_LIBS}) -- set_target_properties (qpid-proton-proactor -- PROPERTIES -- VERSION "${PN_LIB_PROACTOR_VERSION}" -- SOVERSION "${PN_LIB_PROACTOR_MAJOR_VERSION}" -- LINK_FLAGS "${CATCH_UNDEFINED} ${LTO}" -- ) -+ # add_library (qpid-proton-proactor SHARED ${qpid-proton-proactor}) -+ # target_link_libraries (qpid-proton-proactor LINK_PUBLIC qpid-proton-core) -+ # target_link_libraries (qpid-proton-proactor LINK_PRIVATE ${PLATFORM_LIBS} ${PROACTOR_LIBS}) -+ # set_target_properties (qpid-proton-proactor -+ # PROPERTIES -+ # VERSION "${PN_LIB_PROACTOR_VERSION}" -+ # SOVERSION "${PN_LIB_PROACTOR_MAJOR_VERSION}" -+ # LINK_FLAGS "${CATCH_UNDEFINED} ${LTO}" -+ # ) - if (BUILD_STATIC_LIBS) - add_library (qpid-proton-proactor-static STATIC ${qpid-proton-proactor}) - endif(BUILD_STATIC_LIBS) -@@ -500,11 +500,11 @@ - if (BUILD_STATIC_LIBS) - set(STATIC_LIBS qpid-proton-static qpid-proton-core-static) - endif() --install(TARGETS qpid-proton qpid-proton-core ${STATIC_LIBS} -- EXPORT proton -- RUNTIME DESTINATION bin -- ARCHIVE DESTINATION ${LIB_INSTALL_DIR} -- LIBRARY DESTINATION ${LIB_INSTALL_DIR}) -+# install(TARGETS qpid-proton qpid-proton-core ${STATIC_LIBS} -+# EXPORT proton -+# RUNTIME DESTINATION bin -+# ARCHIVE DESTINATION ${LIB_INSTALL_DIR} -+# LIBRARY DESTINATION ${LIB_INSTALL_DIR}) - - # Install windows pdb files - if (MSVC) -@@ -520,11 +520,11 @@ - if (BUILD_STATIC_LIBS) - set(STATIC_LIBS qpid-proton-proactor-static) - endif() -- install(TARGETS qpid-proton-proactor ${STATIC_LIBS} -- EXPORT proton -- RUNTIME DESTINATION bin -- ARCHIVE DESTINATION ${LIB_INSTALL_DIR} -- LIBRARY DESTINATION ${LIB_INSTALL_DIR}) -+ # install(TARGETS qpid-proton-proactor ${STATIC_LIBS} -+ # EXPORT proton -+ # RUNTIME DESTINATION bin -+ # ARCHIVE DESTINATION ${LIB_INSTALL_DIR} -+ # LIBRARY DESTINATION ${LIB_INSTALL_DIR}) - - # Install windows pdb files - if (MSVC) -@@ -576,10 +576,10 @@ - ${CMAKE_CURRENT_BINARY_DIR}/ProtonConfigVersion.cmake - DESTINATION ${LIB_INSTALL_DIR}/cmake/Proton) - --add_subdirectory(docs) --add_subdirectory(examples) --add_subdirectory(tests) --add_subdirectory(tools) -+# add_subdirectory(docs) -+# add_subdirectory(examples) -+# add_subdirectory(tests) -+# add_subdirectory(tools) - - install (DIRECTORY examples/ - DESTINATION "${PROTON_SHARE}/examples/c" -diff -ur a/CMakeLists.txt b/CMakeLists.txt ---- a/CMakeLists.txt 2019-12-09 07:17:00.000000000 -0700 -+++ b/CMakeLists.txt 2019-12-19 18:11:57.128248724 -0700 -@@ -24,7 +24,7 @@ - set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/tools/cmake/Modules") - set (CMAKE_THREAD_PREFER_PTHREAD TRUE) - --include (CTest) -+# include (CTest) - include (CheckLanguage) - include (CheckLibraryExists) - include (CheckSymbolExists) -@@ -33,13 +33,13 @@ - find_package (OpenSSL) - find_package (Threads) - find_package (PythonInterp REQUIRED) --find_package (SWIG) -+# find_package (SWIG) - find_package (CyrusSASL) - --enable_testing () -+#enable_testing () - - # Set up runtime checks (valgrind, sanitizers etc.) --include(tests/RuntimeCheck.cmake) -+# include(tests/RuntimeCheck.cmake) - - ## Variables used across components - -@@ -260,7 +260,7 @@ - - set (SYSINSTALL_BINDINGS OFF CACHE BOOL "If SYSINSTALL_BINDINGS is OFF then proton bindings will be installed underneath ${BINDINGS_DIR} and each user will need to modify their interpreter configuration to load the appropriate binding. If SYSINSTALL_BINDINGS is ON, then each language interpreter will be queried for the appropriate directory and proton bindings will be installed and available system wide with no additional per user configuration.") - --set (BINDING_LANGS PYTHON RUBY) -+# set (BINDING_LANGS PYTHON RUBY) - - foreach (LANG ${BINDING_LANGS}) - set (SYSINSTALL_${LANG} OFF CACHE BOOL "Install ${LANG} bindings into interpreter specified location.") -@@ -315,7 +315,7 @@ - endif() - - # Prerequisites for Go --find_program(GO_EXE go) -+# find_program(GO_EXE go) - mark_as_advanced(GO_EXE) - if (GO_EXE) - set (DEFAULT_GO ON) -diff -ur a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt ---- a/cpp/CMakeLists.txt 2019-12-09 07:17:00.000000000 -0700 -+++ b/cpp/CMakeLists.txt 2020-01-08 16:20:18.855394195 -0700 -@@ -174,30 +174,30 @@ - set (CMAKE_DEBUG_POSTFIX "d") - endif () - --add_library(qpid-proton-cpp SHARED ${qpid-proton-cpp-source}) -+# add_library(qpid-proton-cpp SHARED ${qpid-proton-cpp-source}) - if(BUILD_STATIC_LIBS) - add_library(qpid-proton-cpp-static STATIC ${qpid-proton-cpp-source}) - set(STATIC_LIBS qpid-proton-cpp-static) - endif(BUILD_STATIC_LIBS) - --target_link_libraries (qpid-proton-cpp LINK_PRIVATE ${PLATFORM_LIBS} qpid-proton-core qpid-proton-proactor ${CONNECT_CONFIG_LIBS}) -+# target_link_libraries (qpid-proton-cpp LINK_PRIVATE ${PLATFORM_LIBS} qpid-proton-core qpid-proton-proactor ${CONNECT_CONFIG_LIBS}) - --set_target_properties ( -- qpid-proton-cpp -- PROPERTIES -- LINKER_LANGUAGE CXX -- VERSION "${PN_LIB_CPP_VERSION}" -- SOVERSION "${PN_LIB_CPP_MAJOR_VERSION}" -- LINK_FLAGS "${CATCH_UNDEFINED} ${LTO}" -- ) -+# set_target_properties ( -+# qpid-proton-cpp -+# PROPERTIES -+# LINKER_LANGUAGE CXX -+# VERSION "${PN_LIB_CPP_VERSION}" -+# SOVERSION "${PN_LIB_CPP_MAJOR_VERSION}" -+# LINK_FLAGS "${CATCH_UNDEFINED} ${LTO}" -+# ) - - ## Install - --install(TARGETS qpid-proton-cpp ${STATIC_LIBS} -- EXPORT proton-cpp -- RUNTIME DESTINATION bin -- ARCHIVE DESTINATION ${LIB_INSTALL_DIR} -- LIBRARY DESTINATION ${LIB_INSTALL_DIR}) -+# install(TARGETS qpid-proton-cpp ${STATIC_LIBS} -+# EXPORT proton-cpp -+# RUNTIME DESTINATION bin -+# ARCHIVE DESTINATION ${LIB_INSTALL_DIR} -+# LIBRARY DESTINATION ${LIB_INSTALL_DIR}) - - # Install windows qpid-proton-cpp pdb files - if (MSVC) -@@ -209,12 +209,12 @@ - - install (DIRECTORY "include/proton" DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.hpp") - install (FILES "${CMAKE_CURRENT_BINARY_DIR}/config_presets.hpp" DESTINATION "${INCLUDE_INSTALL_DIR}/proton/internal") --install (DIRECTORY "examples/" -- DESTINATION "${PROTON_SHARE}/examples/cpp" -- USE_SOURCE_PERMISSIONS -- PATTERN "ProtonCppConfig.cmake" EXCLUDE) -+# install (DIRECTORY "examples/" -+# DESTINATION "${PROTON_SHARE}/examples/cpp" -+# USE_SOURCE_PERMISSIONS -+# PATTERN "ProtonCppConfig.cmake" EXCLUDE) - --add_subdirectory(examples) -+# add_subdirectory(examples) - add_subdirectory(docs) - - # Pkg config file -@@ -268,40 +268,40 @@ - set(test_env ${test_env} "PATH=$") - endif() - --macro(add_cpp_test test) -- add_executable (${test} src/${test}.cpp) -- target_link_libraries (${test} qpid-proton-cpp ${PLATFORM_LIBS}) -- add_test (NAME cpp-${test} -- COMMAND ${PN_ENV_SCRIPT} -- ${test_env} ${TEST_EXE_PREFIX_CMD} $ ${ARGN}) --endmacro(add_cpp_test) -- --add_cpp_test(codec_test) --add_cpp_test(connection_driver_test) --add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests) --add_cpp_test(message_test) --add_cpp_test(map_test) --add_cpp_test(scalar_test) --add_cpp_test(value_test) --add_cpp_test(container_test) --add_cpp_test(reconnect_test) --add_cpp_test(link_test) --add_cpp_test(credit_test) --if (ENABLE_JSONCPP) -- add_cpp_test(connect_config_test) -- target_link_libraries(connect_config_test qpid-proton-core) # For pn_sasl_enabled -- set_tests_properties(cpp-connect_config_test PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") -- # Test data and output directories for connect_config_test -- file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/testdata" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") --endif() -+# macro(add_cpp_test test) -+# add_executable (${test} src/${test}.cpp) -+# target_link_libraries (${test} qpid-proton-cpp ${PLATFORM_LIBS}) -+# add_test (NAME cpp-${test} -+# COMMAND ${PN_ENV_SCRIPT} -- ${test_env} ${TEST_EXE_PREFIX_CMD} $ ${ARGN}) -+# endmacro(add_cpp_test) -+# -+# add_cpp_test(codec_test) -+# add_cpp_test(connection_driver_test) -+# add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests) -+# add_cpp_test(message_test) -+# add_cpp_test(map_test) -+# add_cpp_test(scalar_test) -+# add_cpp_test(value_test) -+# add_cpp_test(container_test) -+# add_cpp_test(reconnect_test) -+# add_cpp_test(link_test) -+# add_cpp_test(credit_test) -+# if (ENABLE_JSONCPP) -+# add_cpp_test(connect_config_test) -+# target_link_libraries(connect_config_test qpid-proton-core) # For pn_sasl_enabled -+# set_tests_properties(cpp-connect_config_test PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") -+# # Test data and output directories for connect_config_test -+# file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/testdata" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") -+# endif() - - # TODO aconway 2018-10-31: Catch2 tests - # This is a simple example of a C++ test using the Catch2 framework. - # See c/tests/ for more interesting examples. - # Eventually all the C++ tests will migrate to Catch2. - --include_directories(${CMAKE_SOURCE_DIR}/tests/include) --add_executable(cpp-test src/cpp-test.cpp src/url_test.cpp) --target_link_libraries(cpp-test qpid-proton-cpp ${PLATFORM_LIBS}) -+#include_directories(${CMAKE_SOURCE_DIR}/tests/include) -+#add_executable(cpp-test src/cpp-test.cpp src/url_test.cpp) -+#target_link_libraries(cpp-test qpid-proton-cpp ${PLATFORM_LIBS}) - - macro(add_catch_test tag) - add_test ( diff --git a/doc/amqp.md b/doc/amqp.md deleted file mode 100644 index 08e46fdc2..000000000 --- a/doc/amqp.md +++ /dev/null @@ -1,136 +0,0 @@ -# Block and Transaction Broadcasting With AMQP 1.0 (Experimental Feature) - -[AMQP](https://www.amqp.org/) is an enterprise-level message queuing -protocol for the reliable passing of real-time data and business -transactions between applications. AMQP supports both broker and -brokerless messaging. AMQP 1.0 is an open standard and has been -ratified as ISO/IEC 19464. - -The BitcoinZ daemon can be configured to act as a trusted "border -router", implementing the BitcoinZ P2P protocol and relay, making -consensus decisions, maintaining the local blockchain database, -broadcasting locally generated transactions into the network, and -providing a queryable RPC interface to interact on a polled basis for -requesting blockchain related data. However, there exists only a -limited service to notify external software of events like the arrival -of new blocks or transactions. - -The AMQP facility implements a notification interface through a set -of specific notifiers. Currently there are notifiers that publish -blocks and transactions. This read-only facility requires only the -connection of a corresponding AMQP subscriber port in receiving -software. - -Currently the facility is not authenticated nor is there any two-way -protocol involvement. Therefore, subscribers should validate the -received data since it may be out of date, incomplete or even invalid. - -Because AMQP is message oriented, subscribers receive transactions -and blocks all-at-once and do not need to implement any sort of -buffering or reassembly. - -## Prerequisites - -The AMQP feature in BitcoinZ requires [Qpid Proton](https://qpid.apache.org/proton/) -version 0.17 or newer, which you will need to install if you are not -using the depends system. Typically, it is packaged by distributions as -something like *libqpid-proton*. The C++ wrapper for AMQP *is* required. - -In order to run the example Python client scripts in contrib/ one must -also install *python-qpid-proton*, though this is not necessary for -daemon operation. - -## Security WARNING - -Enabling this feature even on the loopback interface only (e.g. binding -it to localhost or 127.0.0.1) will still expose it to the wilds of the -Internet, because of an attack vector called DNS rebinding. DNS -rebinding allows an attacker located remotely on the Internet to trick -applications that you're running on the same computer as Zcashd to -contact your supposedly localhost-only AMQP port, then, depending on the -program they may be able to attempt to attack it. - -Do not enable this feature unless you are sure that you know what you -are doing, and that you have a strong reason for thinking that you are -not vulnerable to this type of attack. - -## Enabling - -By default, the AMQP feature is automatically compiled in if the -necessary prerequisites are found. To disable, use --disable-proton -during the *configure* step of building bitcoinzd: - - $ ./configure --disable-proton (other options) - -To actually enable operation, one must set the appropriate options on -the commandline or in the configuration file. - -## Usage - -AMQP support is currently an experimental feature, so you must pass -the option: - - -experimentalfeatures - -Currently, the following notifications are supported: - - -amqppubhashtx=address - -amqppubhashblock=address - -amqppubrawblock=address - -amqppubrawtx=address - -The address must be a valid AMQP address, where the same address can be -used in more than notification. Note that SSL and SASL addresses are -not currently supported. - -Launch bitcoinzd like this: - - $ bitcoinzd -amqppubhashtx=amqp://127.0.0.1:5672 - -Or this: - - $ bitcoinzd -amqppubhashtx=amqp://127.0.0.1:5672 \ - -amqppubrawtx=amqp://127.0.0.1:5672 \ - -amqppubrawblock=amqp://127.0.0.1:5672 \ - -amqppubhashblock=amqp://127.0.0.1:5672 \ - -debug=amqp - -The debug category `amqp` enables AMQP-related logging. - -Each notification has a topic and body, where the header corresponds -to the notification type. For instance, for the notification `-amqpubhashtx` -the topic is `hashtx` (no null terminator) and the body is the hexadecimal -transaction hash (32 bytes). This transaction hash and the block hash -found in `hashblock` are in RPC byte order. - -These options can also be provided in bitcoinz.conf. - -Please see `contrib/amqp/amqp_sub.py` for a working example of an -AMQP server listening for messages. - -## Remarks - -From the perspective of bitcoinzd, the local end of an AMQP link is write-only. - -No information is broadcast that wasn't already received from the public -P2P network. - -No authentication or authorization is done on peers that bitcoinzd connects -to; it is assumed that the AMQP link is exposed only to trusted entities, -using other means such as firewalling. - -TLS support may be added once OpenSSL has been removed from the BitcoinZ -project and alternative TLS implementations have been evaluated. - -SASL support may be added in a future update for secure communication. - -Note that when the block chain tip changes, a reorganisation may occur -and just the tip will be notified. It is up to the subscriber to -retrieve the chain from the last known block to the new tip. - -At present, bitcoinzd does not try to resend a notification if there was -a problem confirming receipt. Support for delivery guarantees such as -*at-least-once* and *exactly-once* will be added in in a future update. - -Currently, bitcoinzd appends an up-counting sequence number to each notification -which allows listeners to detect lost notifications. diff --git a/qa/bitcoinz/updatecheck.py b/qa/bitcoinz/updatecheck.py index 238787a9c..2dc21e104 100755 --- a/qa/bitcoinz/updatecheck.py +++ b/qa/bitcoinz/updatecheck.py @@ -66,10 +66,6 @@ def get_dependency_list(): GithubTagReleaseLister("openssl", "openssl", "^OpenSSL_(\d+)_(\d+)_(\d+)([a-z]+)?$", { "OpenSSL_1_1_1b": (1, 1, 1, 'b'), "OpenSSL_1_1_1-pre9": None }), DependsVersionGetter("openssl")), - Dependency("proton", - GithubTagReleaseLister("apache", "qpid-proton", "^(\d+)\.(\d+)(?:\.(\d+))?$", - { "0.27.0": (0, 27, 0), "0.10": (0, 10), "0.12.0-rc": None }), - DependsVersionGetter("proton")), Dependency("rust", GithubTagReleaseLister("rust-lang", "rust", "^(\d+)\.(\d+)(?:\.(\d+))?$", { "1.33.0": (1, 33, 0), "0.9": (0, 9) }), diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 2684bb04f..101c5063c 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -102,10 +102,6 @@ if [ "x$ENABLE_ZMQ" = "x1" ]; then testScripts+=('zmq_test.py') fi -if [ "x$ENABLE_PROTON" = "x1" ]; then - testScripts+=('proton_test.py') -fi - extArg="-extended" passOn=${@#$extArg} diff --git a/qa/pull-tester/tests-config.sh.in b/qa/pull-tester/tests-config.sh.in index 66dcdbc8e..1eddb004f 100755 --- a/qa/pull-tester/tests-config.sh.in +++ b/qa/pull-tester/tests-config.sh.in @@ -11,7 +11,6 @@ EXEEXT="@EXEEXT@" @BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=1 @BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=1 @ENABLE_ZMQ_TRUE@ENABLE_ZMQ=1 -@ENABLE_PROTON_TRUE@ENABLE_PROTON=1 REAL_BITCOIND="$BUILDDIR/src/bitcoinzd${EXEEXT}" REAL_BITCOINCLI="$BUILDDIR/src/bitcoinz-cli${EXEEXT}" diff --git a/qa/rpc-tests/proton_test.py b/qa/rpc-tests/proton_test.py deleted file mode 100755 index 820a9d7be..000000000 --- a/qa/rpc-tests/proton_test.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2017 The Zcash developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or https://www.opensource.org/licenses/mit-license.php . - -# -# Test Proton interface (provides AMQP 1.0 messaging support). -# -# Requirements: -# Python library for Qpid Proton: -# https://pypi.python.org/pypi/python-qpid-proton -# To install: -# pip install python-qpid-proton -# - -import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, bytes_to_hex_str, \ - start_nodes - -from proton.handlers import MessagingHandler -from proton.reactor import Container - -import threading - - -class Server(MessagingHandler): - - def __init__(self, url, limit): - super(Server, self).__init__() - self.url = url - self.counter = limit - self.blockhashes = [] - self.txids = [] - self.blockseq = -1 - self.txidseq = -1 - - def on_start(self, event): - print "Proton listening on:", self.url - self.container = event.container - self.acceptor = event.container.listen(self.url) - - def on_message(self, event): - m = event.message - hash = bytes_to_hex_str(m.body) - sequence = m.properties['x-opt-sequence-number'] - if m.subject == "hashtx": - self.txids.append(hash) - - # Test that sequence id is incrementing - assert(sequence == 1 + self.txidseq) - self.txidseq = sequence - elif m.subject == "hashblock": - self.blockhashes.append(hash) - - # Test that sequence id is incrementing - assert(sequence == 1 + self.blockseq) - self.blockseq = sequence - - self.counter = self.counter - 1 - if self.counter == 0: - self.container.stop() - - -class ProtonTest (BitcoinTestFramework): - - port = 25672 - numblocks = 10 # must be even, as two nodes generate equal number - assert(numblocks % 2 == 0) - - def setup_nodes(self): - - # Launch proton server in background thread - # It terminates after receiving numblocks * 2 messages (one for coinbase, one for block) - self.server = Server("127.0.0.1:%i" % self.port, self.numblocks * 2) - self.container = Container(self.server) - self.t1 = threading.Thread(target=self.container.run) - self.t1.start() - - return start_nodes(4, self.options.tmpdir, extra_args=[ - ['-experimentalfeatures', '-debug=amqp', '-amqppubhashtx=amqp://127.0.0.1:'+str(self.port), - '-amqppubhashblock=amqp://127.0.0.1:'+str(self.port)], - [], - [], - [] - ]) - - def run_test(self): - self.sync_all() - baseheight = self.nodes[0].getblockcount() # 200 blocks already mined - - # generate some blocks - self.nodes[0].generate(self.numblocks/2) - self.sync_all() - self.nodes[1].generate(self.numblocks/2) - self.sync_all() - - # wait for server to finish - self.t1.join() - - # sequence numbers have already been checked in the server's message handler - - # sanity check that we have the right number of block hashes and coinbase txids - assert_equal(len(self.server.blockhashes), self.numblocks) - assert_equal(len(self.server.txids), self.numblocks) - - # verify that each block has the correct coinbase txid - for i in xrange(0, self.numblocks): - height = baseheight + i + 1 - blockhash = self.nodes[0].getblockhash(height) - assert_equal(blockhash, self.server.blockhashes[i]) - resp = self.nodes[0].getblock(blockhash) - coinbase = resp["tx"][0] - assert_equal(coinbase, self.server.txids[i]) - - -if __name__ == '__main__': - ProtonTest().main() diff --git a/src/Makefile.am b/src/Makefile.am index 9dfbf81fd..0d747782c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,9 +45,6 @@ LIBZCASH=libzcash.a if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a endif -if ENABLE_PROTON -LIBBITCOIN_PROTON=libbitcoin_proton.a -endif if BUILD_BITCOIN_LIBS LIBZCASH_CONSENSUS=libzcashconsensus.la endif @@ -71,7 +68,6 @@ EXTRA_LIBRARIES += \ $(LIBBITCOIN_CLI) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) \ - $(LIBBITCOIN_PROTON) \ $(LIBZCASH) lib_LTLIBRARIES = $(LIBZCASH_CONSENSUS) @@ -110,11 +106,6 @@ BITCOIN_CORE_H = \ addrman.h \ alert.h \ amount.h \ - amqp/amqpabstractnotifier.h \ - amqp/amqpconfig.h \ - amqp/amqpnotificationinterface.h \ - amqp/amqppublishnotifier.h \ - amqp/amqpsender.h \ arith_uint256.h \ asyncrpcoperation.h \ asyncrpcqueue.h \ @@ -294,15 +285,6 @@ libbitcoin_zmq_a_SOURCES = \ zmq/zmqpublishnotifier.cpp endif -if ENABLE_PROTON -libbitcoin_proton_a_CPPFLAGS = $(BITCOIN_INCLUDES) -libbitcoin_proton_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -libbitcoin_proton_a_SOURCES = \ - amqp/amqpabstractnotifier.cpp \ - amqp/amqpnotificationinterface.cpp \ - amqp/amqppublishnotifier.cpp -endif - # wallet: bitcoinzd, but only linked when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -454,7 +436,6 @@ bitcoinzd_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_ZMQ) \ - $(LIBBITCOIN_PROTON) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH) \ $(LIBLEVELDB) \ @@ -467,7 +448,6 @@ bitcoinzd_LDADD += \ $(EVENT_PTHREADS_LIBS) \ $(EVENT_LIBS) \ $(ZMQ_LIBS) \ - $(PROTON_LIBS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH_LIBS) diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 37711468d..454b9b3cd 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -70,7 +70,6 @@ bitcoinz_gtest_LDADD = \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_ZMQ) \ - $(LIBBITCOIN_PROTON) \ $(LIBBITCOIN_CRYPTO) \ $(LIBUNIVALUE) \ $(LIBLEVELDB) \ @@ -85,12 +84,10 @@ bitcoinz_gtest_LDADD += \ $(EVENT_PTHREADS_LIBS) \ $(EVENT_LIBS) \ $(ZMQ_LIBS) \ - $(PROTON_LIBS) \ $(LIBZCASH) \ $(LIBRUSTZCASH) \ $(LIBZCASH_LIBS) - bitcoinz_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static bitcoinz-gtest_check: bitcoinz-gtest FORCE diff --git a/src/Makefile.test.include b/src/Makefile.test.include index b79ac362c..1fe041add 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -137,10 +137,6 @@ if ENABLE_ZMQ test_test_bitcoinz_LDADD += $(ZMQ_LIBS) endif -if ENABLE_PROTON -test_test_bitcoinz_LDADD += $(PROTON_LIBS) -endif - nodist_test_test_bitcoinz_SOURCES = $(GENERATED_TEST_FILES) $(BITCOIN_TESTS): $(GENERATED_TEST_FILES) diff --git a/src/amqp/amqpabstractnotifier.cpp b/src/amqp/amqpabstractnotifier.cpp deleted file mode 100644 index 30481b89d..000000000 --- a/src/amqp/amqpabstractnotifier.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#include "amqpabstractnotifier.h" -#include "util.h" - - -AMQPAbstractNotifier::~AMQPAbstractNotifier() -{ -} - -bool AMQPAbstractNotifier::NotifyBlock(const CBlockIndex * /*CBlockIndex*/) -{ - return true; -} - -bool AMQPAbstractNotifier::NotifyTransaction(const CTransaction &/*transaction*/) -{ - return true; -} diff --git a/src/amqp/amqpabstractnotifier.h b/src/amqp/amqpabstractnotifier.h deleted file mode 100644 index f68109612..000000000 --- a/src/amqp/amqpabstractnotifier.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_AMQP_AMQPABSTRACTNOTIFIER_H -#define ZCASH_AMQP_AMQPABSTRACTNOTIFIER_H - -#include "amqpconfig.h" - -class CBlockIndex; -class AMQPAbstractNotifier; - -typedef AMQPAbstractNotifier* (*AMQPNotifierFactory)(); - -class AMQPAbstractNotifier -{ -public: - AMQPAbstractNotifier() { } - virtual ~AMQPAbstractNotifier(); - - template - static AMQPAbstractNotifier* Create() - { - return new T(); - } - - std::string GetType() const { return type; } - void SetType(const std::string &t) { type = t; } - std::string GetAddress() const { return address; } - void SetAddress(const std::string &a) { address = a; } - - virtual bool Initialize() = 0; - virtual void Shutdown() = 0; - - virtual bool NotifyBlock(const CBlockIndex *pindex); - virtual bool NotifyTransaction(const CTransaction &transaction); - -protected: - std::string type; - std::string address; -}; - -#endif // ZCASH_AMQP_AMQPABSTRACTNOTIFIER_H diff --git a/src/amqp/amqpconfig.h b/src/amqp/amqpconfig.h deleted file mode 100644 index 8b2fd6a88..000000000 --- a/src/amqp/amqpconfig.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_AMQP_AMQPCONFIG_H -#define ZCASH_AMQP_AMQPCONFIG_H - -#if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" -#endif - -#include -#include - -#if ENABLE_PROTON -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#include "primitives/block.h" -#include "primitives/transaction.h" - -#endif // ZCASH_AMQP_AMQPCONFIG_H diff --git a/src/amqp/amqpnotificationinterface.cpp b/src/amqp/amqpnotificationinterface.cpp deleted file mode 100644 index 0dfae00b9..000000000 --- a/src/amqp/amqpnotificationinterface.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#include "amqpnotificationinterface.h" -#include "amqppublishnotifier.h" - -#include "version.h" -#include "main.h" -#include "streams.h" -#include "util.h" - -// AMQP 1.0 Support -// -// The boost::signals2 signals and slot system is thread safe, so CValidationInterface listeners -// can be invoked from any thread. -// -// Currently signals are fired from main.cpp so the callbacks should be invoked on the same thread. -// It should be safe to share objects responsible for sending, as they should not be run concurrently -// across different threads. -// -// Developers should be mindful of where notifications are fired to avoid potential race conditions. -// For example, different signals targeting the same address could be fired from different threads -// in different parts of the system around the same time. -// -// Like the ZMQ notification interface, if a notifier fails to send a message, the notifier is shut down. -// - -AMQPNotificationInterface::AMQPNotificationInterface() -{ -} - -AMQPNotificationInterface::~AMQPNotificationInterface() -{ - Shutdown(); - - for (std::list::iterator i = notifiers.begin(); i != notifiers.end(); ++i) { - delete *i; - } -} - -AMQPNotificationInterface* AMQPNotificationInterface::CreateWithArguments(const std::map &args) -{ - AMQPNotificationInterface* notificationInterface = nullptr; - std::map factories; - std::list notifiers; - - factories["pubhashblock"] = AMQPAbstractNotifier::Create; - factories["pubhashtx"] = AMQPAbstractNotifier::Create; - factories["pubrawblock"] = AMQPAbstractNotifier::Create; - factories["pubrawtx"] = AMQPAbstractNotifier::Create; - - for (std::map::const_iterator i=factories.begin(); i!=factories.end(); ++i) { - std::map::const_iterator j = args.find("-amqp" + i->first); - if (j!=args.end()) { - AMQPNotifierFactory factory = i->second; - std::string address = j->second; - AMQPAbstractNotifier *notifier = factory(); - notifier->SetType(i->first); - notifier->SetAddress(address); - notifiers.push_back(notifier); - } - } - - if (!notifiers.empty()) { - notificationInterface = new AMQPNotificationInterface(); - notificationInterface->notifiers = notifiers; - - if (!notificationInterface->Initialize()) { - delete notificationInterface; - notificationInterface = nullptr; - } - } - - return notificationInterface; -} - -// Called at startup to conditionally set up -bool AMQPNotificationInterface::Initialize() -{ - LogPrint("amqp", "amqp: Initialize notification interface\n"); - - std::list::iterator i = notifiers.begin(); - for (; i != notifiers.end(); ++i) { - AMQPAbstractNotifier *notifier = *i; - if (notifier->Initialize()) { - LogPrint("amqp", "amqp: Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress()); - } else { - LogPrint("amqp", "amqp: Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress()); - break; - } - } - - if (i != notifiers.end()) { - return false; - } - - return true; -} - -// Called during shutdown sequence -void AMQPNotificationInterface::Shutdown() -{ - LogPrint("amqp", "amqp: Shutdown notification interface\n"); - - for (std::list::iterator i = notifiers.begin(); i != notifiers.end(); ++i) { - AMQPAbstractNotifier *notifier = *i; - notifier->Shutdown(); - } -} - -void AMQPNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindex) -{ - for (std::list::iterator i = notifiers.begin(); i != notifiers.end(); ) { - AMQPAbstractNotifier *notifier = *i; - if (notifier->NotifyBlock(pindex)) { - i++; - } else { - notifier->Shutdown(); - i = notifiers.erase(i); - } - } -} - -void AMQPNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlock *pblock) -{ - for (std::list::iterator i = notifiers.begin(); i != notifiers.end(); ) { - AMQPAbstractNotifier *notifier = *i; - if (notifier->NotifyTransaction(tx)) { - i++; - } else { - notifier->Shutdown(); - i = notifiers.erase(i); - } - } -} diff --git a/src/amqp/amqpnotificationinterface.h b/src/amqp/amqpnotificationinterface.h deleted file mode 100644 index 77495a85c..000000000 --- a/src/amqp/amqpnotificationinterface.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_AMQP_AMQPNOTIFICATIONINTERFACE_H -#define ZCASH_AMQP_AMQPNOTIFICATIONINTERFACE_H - -#include "validationinterface.h" -#include -#include - -class CBlockIndex; -class AMQPAbstractNotifier; - -class AMQPNotificationInterface : public CValidationInterface -{ -public: - virtual ~AMQPNotificationInterface(); - - static AMQPNotificationInterface* CreateWithArguments(const std::map &args); - -protected: - bool Initialize(); - void Shutdown(); - - // CValidationInterface - void SyncTransaction(const CTransaction &tx, const CBlock *pblock); - void UpdatedBlockTip(const CBlockIndex *pindex); - -private: - AMQPNotificationInterface(); - - std::list notifiers; -}; - -#endif // ZCASH_AMQP_AMQPNOTIFICATIONINTERFACE_H diff --git a/src/amqp/amqppublishnotifier.cpp b/src/amqp/amqppublishnotifier.cpp deleted file mode 100644 index 227ca11d5..000000000 --- a/src/amqp/amqppublishnotifier.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#include "amqppublishnotifier.h" -#include "chainparams.h" -#include "main.h" -#include "util.h" - -#include "amqpsender.h" - -#include -#include - -static std::multimap mapPublishNotifiers; - -static const char *MSG_HASHBLOCK = "hashblock"; -static const char *MSG_HASHTX = "hashtx"; -static const char *MSG_RAWBLOCK = "rawblock"; -static const char *MSG_RAWTX = "rawtx"; - -// Invoke this method from a new thread to run the proton container event loop. -void AMQPAbstractPublishNotifier::SpawnProtonContainer() -{ - try { - proton::default_container(*handler_).run(); - } - catch (const proton::error_condition &e) { - LogPrint("amqp", "amqp: container error: %s\n", e.what()); - } - catch (const std::runtime_error &e) { - LogPrint("amqp", "amqp: runtime error: %s\n", e.what()); - } - catch (const std::exception &e) { - LogPrint("amqp", "amqp: exception: %s\n", e.what()); - } - catch (...) { - LogPrint("amqp", "amqp: unknown error\n"); - } - handler_->terminate(); -} - -bool AMQPAbstractPublishNotifier::Initialize() -{ - std::multimap::iterator i = mapPublishNotifiers.find(address); - - if (i == mapPublishNotifiers.end()) { - try { - handler_ = std::make_shared(address); - thread_ = std::make_shared(&AMQPAbstractPublishNotifier::SpawnProtonContainer, this); - } - catch (std::exception &e) { - LogPrint("amqp", "amqp: initialization error: %s\n", e.what()); - return false; - } - mapPublishNotifiers.insert(std::make_pair(address, this)); - } else { - // copy the shared ptrs to the message handler and the thread where the proton container is running - handler_ = i->second->handler_; - thread_ = i->second->thread_; - mapPublishNotifiers.insert(std::make_pair(address, this)); - } - - return true; -} - - -void AMQPAbstractPublishNotifier::Shutdown() -{ - LogPrint("amqp", "amqp: Shutdown notifier %s at %s\n", GetType(), GetAddress()); - - int count = mapPublishNotifiers.count(address); - - // remove this notifier from the list of publishers using this address - typedef std::multimap::iterator iterator; - std::pair iterpair = mapPublishNotifiers.equal_range(address); - - for (iterator it = iterpair.first; it != iterpair.second; ++it) { - if (it->second == this) { - mapPublishNotifiers.erase(it); - break; - } - } - - // terminate the connection if this is the last publisher using this address - if (count == 1) { - handler_->terminate(); - if (thread_.get() != nullptr) { - if (thread_->joinable()) { - thread_->join(); - } - } - } -} - - -bool AMQPAbstractPublishNotifier::SendMessage(const char *command, const void* data, size_t size) -{ - try { - proton::binary content; - const char *p = (const char *)data; - content.assign(p, p + size); - - proton::message message(content); - message.subject(std::string(command)); - proton::message::property_map & props = message.properties(); - props.put("x-opt-sequence-number", sequence_); - handler_->publish(message); - - } catch (proton::error_condition &e) { - LogPrint("amqp", "amqp: error : %s\n", e.what()); - return false; - } - catch (const std::runtime_error &e) { - LogPrint("amqp", "amqp: runtime error: %s\n", e.what()); - return false; - } - catch (const std::exception &e) { - LogPrint("amqp", "amqp: exception: %s\n", e.what()); - return false; - } - catch (...) { - LogPrint("amqp", "amqp: unknown error\n"); - return false; - } - - sequence_++; - - return true; -} - -bool AMQPPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex) -{ - uint256 hash = pindex->GetBlockHash(); - LogPrint("amqp", "amqp: Publish hashblock %s\n", hash.GetHex()); - char data[32]; - for (unsigned int i = 0; i < 32; i++) - data[31 - i] = hash.begin()[i]; - return SendMessage(MSG_HASHBLOCK, data, 32); -} - -bool AMQPPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction) -{ - uint256 hash = transaction.GetHash(); - LogPrint("amqp", "amqp: Publish hashtx %s\n", hash.GetHex()); - char data[32]; - for (unsigned int i = 0; i < 32; i++) - data[31 - i] = hash.begin()[i]; - return SendMessage(MSG_HASHTX, data, 32); -} - -bool AMQPPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex) -{ - LogPrint("amqp", "amqp: Publish rawblock %s\n", pindex->GetBlockHash().GetHex()); - - const Consensus::Params& consensusParams = Params().GetConsensus(); - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - { - LOCK(cs_main); - CBlock block; - if(!ReadBlockFromDisk(block, pindex, consensusParams)) { - LogPrint("amqp", "amqp: Can't read block from disk"); - return false; - } - - ss << block; - } - - return SendMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size()); -} - -bool AMQPPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction) -{ - uint256 hash = transaction.GetHash(); - LogPrint("amqp", "amqp: Publish rawtx %s\n", hash.GetHex()); - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << transaction; - return SendMessage(MSG_RAWTX, &(*ss.begin()), ss.size()); -} diff --git a/src/amqp/amqppublishnotifier.h b/src/amqp/amqppublishnotifier.h deleted file mode 100644 index 2eca74444..000000000 --- a/src/amqp/amqppublishnotifier.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_AMQP_AMQPPUBLISHNOTIFIER_H -#define ZCASH_AMQP_AMQPPUBLISHNOTIFIER_H - -#include "amqpabstractnotifier.h" -#include "amqpconfig.h" -#include "amqpsender.h" - -#include -#include - -class CBlockIndex; - -class AMQPAbstractPublishNotifier : public AMQPAbstractNotifier -{ -private: - uint64_t sequence_; // memory only, per notifier instance: upcounting message sequence number - - std::shared_ptr thread_; // proton container thread, may be shared between notifiers - std::shared_ptr handler_; // proton container message handler, may be shared between notifiers - -public: - bool SendMessage(const char *command, const void* data, size_t size); - bool Initialize(); - void Shutdown(); - void SpawnProtonContainer(); -}; - -class AMQPPublishHashBlockNotifier : public AMQPAbstractPublishNotifier -{ -public: - bool NotifyBlock(const CBlockIndex *pindex); -}; - -class AMQPPublishHashTransactionNotifier : public AMQPAbstractPublishNotifier -{ -public: - bool NotifyTransaction(const CTransaction &transaction); -}; - -class AMQPPublishRawBlockNotifier : public AMQPAbstractPublishNotifier -{ -public: - bool NotifyBlock(const CBlockIndex *pindex); -}; - -class AMQPPublishRawTransactionNotifier : public AMQPAbstractPublishNotifier -{ -public: - bool NotifyTransaction(const CTransaction &transaction); -}; - -#endif // ZCASH_AMQP_AMQPPUBLISHNOTIFIER_H diff --git a/src/amqp/amqpsender.h b/src/amqp/amqpsender.h deleted file mode 100644 index eb365d3ec..000000000 --- a/src/amqp/amqpsender.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2017 The Zcash developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef ZCASH_AMQP_AMQPSENDER_H -#define ZCASH_AMQP_AMQPSENDER_H - -#include "amqpconfig.h" - -#include -#include -#include -#include - -class AMQPSender : public proton::messaging_handler { - private: - std::deque messages_; - proton::url url_; - proton::connection conn_; - proton::sender sender_; - std::mutex lock_; - std::atomic terminated_ = {false}; - - public: - - AMQPSender(const std::string& url) : url_(url) {} - - // Callback to initialize the container when run() is invoked - void on_container_start(proton::container& c) override { - proton::duration t(10000); // milliseconds - proton::connection_options opts = proton::connection_options().idle_timeout(t); - conn_ = c.connect(url_, opts); - sender_ = conn_.open_sender(url_.path()); - } - - // Remote end signals when the local end can send (i.e. has credit) - void on_sendable(proton::sender &s) override { - dispatch(); - } - - // Publish message by adding to queue and trying to dispatch it - void publish(const proton::message &m) { - add_message(m); - dispatch(); - } - - // Add message to queue - void add_message(const proton::message &m) { - std::lock_guard guard(lock_); - messages_.push_back(m); - } - - // Send messages in queue - void dispatch() { - std::lock_guard guard(lock_); - - if (isTerminated()) { - throw std::runtime_error("amqp connection was terminated"); - } - - if (!conn_.active()) { - throw std::runtime_error("amqp connection is not active"); - } - - while (messages_.size() > 0) { - if (sender_.credit()) { - const proton::message& m = messages_.front(); - sender_.send(m); - messages_.pop_front(); - } else { - break; - } - } - } - - // Close connection to remote end. Container event-loop, by default, will auto-stop. - void terminate() { - std::lock_guard guard(lock_); - conn_.close(); - terminated_.store(true); - } - - bool isTerminated() const { - return terminated_.load(); - } - - void on_transport_error(proton::transport &t) override { - t.connection().close(); - throw t.error(); - } - - void on_connection_error(proton::connection &c) override { - c.close(); - throw c.error(); - } - - void on_session_error(proton::session &s) override { - s.connection().close(); - throw s.error(); - } - - void on_receiver_error(proton::receiver &r) override { - r.connection().close(); - throw r.error(); - } - - void on_sender_error(proton::sender &s) override { - s.connection().close(); - throw s.error(); - } - -}; - - -#endif //ZCASH_AMQP_AMQPSENDER_H diff --git a/src/init.cpp b/src/init.cpp index 738887b8f..18aa9e986 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -64,10 +64,6 @@ #include "zmq/zmqnotificationinterface.h" #endif -#if ENABLE_PROTON -#include "amqp/amqpnotificationinterface.h" -#endif - #include "librustzcash.h" using namespace std; @@ -91,10 +87,6 @@ static const int64_t WALLET_INITIAL_SYNC_TIMEOUT = 1000 * 60 * 60 * 2; static CZMQNotificationInterface* pzmqNotificationInterface = NULL; #endif -#if ENABLE_PROTON -static AMQPNotificationInterface* pAMQPNotificationInterface = NULL; -#endif - #ifdef WIN32 // Win32 LevelDB doesn't use file descriptors, and the ones used for // accessing block files don't count towards the fd_set size limit @@ -254,14 +246,6 @@ void Shutdown() } #endif -#if ENABLE_PROTON - if (pAMQPNotificationInterface) { - UnregisterValidationInterface(pAMQPNotificationInterface); - delete pAMQPNotificationInterface; - pAMQPNotificationInterface = NULL; - } -#endif - #ifndef WIN32 try { boost::filesystem::remove(GetPidFile()); @@ -423,14 +407,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-zmqpubrawtx=
", _("Enable publish raw transaction in
")); #endif -#if ENABLE_PROTON - strUsage += HelpMessageGroup(_("AMQP 1.0 notification options:")); - strUsage += HelpMessageOpt("-amqppubhashblock=
", _("Enable publish hash block in
")); - strUsage += HelpMessageOpt("-amqppubhashtx=
", _("Enable publish hash transaction in
")); - strUsage += HelpMessageOpt("-amqppubrawblock=
", _("Enable publish raw block in
")); - strUsage += HelpMessageOpt("-amqppubrawtx=
", _("Enable publish raw transaction in
")); -#endif - strUsage += HelpMessageGroup(_("Debugging/Testing options:")); if (showDebug) { @@ -1423,21 +1399,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif -#if ENABLE_PROTON - pAMQPNotificationInterface = AMQPNotificationInterface::CreateWithArguments(mapArgs); - - if (pAMQPNotificationInterface) { - - // AMQP support is currently an experimental feature, so fail if user configured AMQP notifications - // without enabling experimental features. - if (!GetBoolArg("-experimentalfeatures", false)) { - return InitError(_("AMQP support requires -experimentalfeatures.")); - } - - RegisterValidationInterface(pAMQPNotificationInterface); - } -#endif - // ********************************************************* Step 7: load block chain fReindex = GetBoolArg("-reindex", false); diff --git a/zcutil/build.sh b/zcutil/build.sh index a212341bb..8e799a845 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -46,7 +46,7 @@ Usage: $0 --help Show this help message and exit. -$0 [ --enable-proton ] [ MAKEARGS... ] +$0 [ MAKEARGS... ] Build BitcoinZ and most of its transitive dependencies from source. MAKEARGS are applied to both dependencies and BitcoinZ itself. @@ -56,9 +56,6 @@ $0 [ --enable-proton ] [ MAKEARGS... ] CONFIGURE_FLAGS="--enable-lcov --disable-hardening" ./zcutil/build.sh - If --enable-proton is passed, BitcoinZ is configured to build the Apache Qpid Proton - library required for AMQP support. This library is not built by default. - For verbose output, use: ./zcutil/build.sh V=1 EOF @@ -67,19 +64,11 @@ fi set -x -# If --enable-proton is the next argument, enable building Proton code: -PROTON_ARG='' -if [ "x${1:-}" = 'x--enable-proton' ] -then - PROTON_ARG='--enable-proton' - shift -fi - eval "$MAKE" --version as --version ld -v -HOST="$HOST" BUILD="$BUILD" WITH_PROTON="$PROTON_ARG" "$MAKE" "$@" -C ./depends/ +HOST="$HOST" BUILD="$BUILD" "$MAKE" "$@" -C ./depends/ ./autogen.sh -CONFIG_SITE="$PWD/depends/$HOST/share/config.site" ./configure "$PROTON_ARG" $CONFIGURE_FLAGS +CONFIG_SITE="$PWD/depends/$HOST/share/config.site" ./configure $CONFIGURE_FLAGS "$MAKE" "$@" From 8544d32496d83194722df766bf84c458d8224b74 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 12:55:35 +0000 Subject: [PATCH 002/146] depends: Remove ccache --- depends/config.site.in | 1 - depends/packages/native_ccache.mk | 25 ------------------------- depends/packages/packages.mk | 1 - 3 files changed, 27 deletions(-) delete mode 100644 depends/packages/native_ccache.mk diff --git a/depends/config.site.in b/depends/config.site.in index 8cdbcd2e4..f1c0c0738 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -35,7 +35,6 @@ CC="@CC@" CXX="@CXX@" OBJC="${CC}" OBJCXX="${CXX}" -CCACHE=$depends_prefix/native/bin/ccache if test -n "@AR@"; then AR=@AR@ diff --git a/depends/packages/native_ccache.mk b/depends/packages/native_ccache.mk deleted file mode 100644 index 4c5dde76d..000000000 --- a/depends/packages/native_ccache.mk +++ /dev/null @@ -1,25 +0,0 @@ -package=native_ccache -$(package)_version=3.7.12 -$(package)_download_path=https://github.com/ccache/ccache/releases/download/v$($(package)_version) -$(package)_file_name=ccache-$($(package)_version).tar.gz -$(package)_sha256_hash=d2abe88d4c283ce960e233583061127b156ffb027c6da3cf10770fc0c7244194 - -define $(package)_set_vars -$(package)_config_opts= -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef - -define $(package)_postprocess_cmds - rm -rf lib include -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index a789ab0c6..1fbc8d39d 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -35,7 +35,6 @@ rust_crates := \ rust_packages := rust $(rust_crates) librustzcash zcash_packages := libsodium utfcpp packages := boost libevent zeromq $(zcash_packages) googletest -native_packages := native_ccache wallet_packages=bdb From 712523861ab33520ea85b15c38adde8bca7c135e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 12:56:06 +0000 Subject: [PATCH 003/146] Upgrade autoconf to latest versions --- autogen.sh | 21 +- build-aux/m4/ax_boost_base.m4 | 260 ++++---- build-aux/m4/ax_boost_chrono.m4 | 11 +- build-aux/m4/ax_boost_filesystem.m4 | 7 +- build-aux/m4/ax_boost_program_options.m4 | 10 +- build-aux/m4/ax_boost_system.m4 | 7 +- build-aux/m4/ax_boost_thread.m4 | 179 +++--- build-aux/m4/ax_boost_unit_test_framework.m4 | 13 +- build-aux/m4/ax_check_compile_flag.m4 | 33 +- build-aux/m4/ax_check_link_flag.m4 | 33 +- build-aux/m4/ax_check_preproc_flag.m4 | 33 +- build-aux/m4/ax_cxx_compile_stdcxx.m4 | 64 +- build-aux/m4/ax_gcc_func_attribute.m4 | 31 +- build-aux/m4/ax_pthread.m4 | 635 ++++++++++--------- build-aux/m4/bitcoin_find_bdb.m4 | 147 +++-- build-aux/m4/bitcoin_subdir_to_include.m4 | 6 +- build-aux/m4/l_atomic.m4 | 25 +- 17 files changed, 834 insertions(+), 681 deletions(-) diff --git a/autogen.sh b/autogen.sh index cb526759f..314f2176f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -2,15 +2,30 @@ # Copyright (c) 2017-2024 The BitcoinZ community # Copyright (c) 2016-2019 The Zcash developers # Copyright (c) 2013-2019 The Bitcoin Core developers -# Copyright (c) 2013-2019 Bitcoin Developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . +export LC_ALL=C set -e -srcdir="$(dirname $0)" +srcdir="$(dirname "$0")" cd "$srcdir" -if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="`which glibtoolize 2>/dev/null`"; then +if [ -z "${LIBTOOLIZE}" ] && GLIBTOOLIZE="$(command -v glibtoolize)"; then LIBTOOLIZE="${GLIBTOOLIZE}" export LIBTOOLIZE fi +command -v autoreconf >/dev/null || \ + (echo "configuration failed, please install autoconf first" && exit 1) autoreconf --install --force --warnings=all + +if expr "'$(build-aux/config.guess --timestamp)" \< "'$(depends/config.guess --timestamp)" > /dev/null; then + chmod ug+w build-aux/config.guess + chmod ug+w src/secp256k1/build-aux/config.guess + cp depends/config.guess build-aux + cp depends/config.guess src/secp256k1/build-aux +fi +if expr "'$(build-aux/config.sub --timestamp)" \< "'$(depends/config.sub --timestamp)" > /dev/null; then + chmod ug+w build-aux/config.sub + chmod ug+w src/secp256k1/build-aux/config.sub + cp depends/config.sub build-aux + cp depends/config.sub src/secp256k1/build-aux +fi diff --git a/build-aux/m4/ax_boost_base.m4 b/build-aux/m4/ax_boost_base.m4 index 296e05558..8485cc92d 100644 --- a/build-aux/m4/ax_boost_base.m4 +++ b/build-aux/m4/ax_boost_base.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html +# https://www.gnu.org/software/autoconf-archive/ax_boost_base.html # =========================================================================== # # SYNOPSIS @@ -11,9 +11,9 @@ # Test for the Boost C++ libraries of a particular version (or newer) # # If no path to the installed boost library is given the macro searches -# under /usr, /usr/local, /opt and /opt/local and evaluates the -# $BOOST_ROOT environment variable. Further documentation is available at -# . +# under /usr, /usr/local, /opt, /opt/local and /opt/homebrew and evaluates +# the $BOOST_ROOT environment variable. Further documentation is available +# at . # # This macro calls: # @@ -33,7 +33,15 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 26 +#serial 54 + +# example boost program (need to pass version) +m4_define([_AX_BOOST_BASE_PROGRAM], + [AC_LANG_PROGRAM([[ +#include +]],[[ +(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))])); +]])]) AC_DEFUN([AX_BOOST_BASE], [ @@ -44,110 +52,123 @@ AC_ARG_WITH([boost], or disable it (ARG=no) @<:@ARG=yes@:>@ ])], [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ac_boost_path="" - else - want_boost="yes" - ac_boost_path="$withval" - fi + AS_CASE([$withval], + [no],[want_boost="no";_AX_BOOST_BASE_boost_path=""], + [yes],[want_boost="yes";_AX_BOOST_BASE_boost_path=""], + [want_boost="yes";_AX_BOOST_BASE_boost_path="$withval"]) ], [want_boost="yes"]) AC_ARG_WITH([boost-libdir], - AS_HELP_STRING([--with-boost-libdir=LIB_DIR], - [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), - [ - if test -d "$withval" - then - ac_boost_lib_path="$withval" - else - AC_MSG_ERROR(--with-boost-libdir expected directory name) - fi - ], - [ac_boost_lib_path=""] -) + [AS_HELP_STRING([--with-boost-libdir=LIB_DIR], + [Force given directory for boost libraries. + Note that this will override library path detection, + so use this parameter only if default library detection fails + and you know exactly where your boost libraries are located.])], + [ + AS_IF([test -d "$withval"], + [_AX_BOOST_BASE_boost_lib_path="$withval"], + [AC_MSG_ERROR([--with-boost-libdir expected directory name])]) + ], + [_AX_BOOST_BASE_boost_lib_path=""]) -if test "x$want_boost" = "xyes"; then - boost_lib_version_req=ifelse([$1], ,1.20.0,$1) - boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` - boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` - boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` - boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` - if test "x$boost_lib_version_req_sub_minor" = "x" ; then - boost_lib_version_req_sub_minor="0" - fi - WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` - AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) +BOOST_LDFLAGS="" +BOOST_CPPFLAGS="" +AS_IF([test "x$want_boost" = "xyes"], + [_AX_BOOST_BASE_RUNDETECT([$1],[$2],[$3])]) +AC_SUBST(BOOST_CPPFLAGS) +AC_SUBST(BOOST_LDFLAGS) +]) + + +# convert a version string in $2 to numeric and affect to polymorphic var $1 +AC_DEFUN([_AX_BOOST_BASE_TONUMERICVERSION],[ + AS_IF([test "x$2" = "x"],[_AX_BOOST_BASE_TONUMERICVERSION_req="1.20.0"],[_AX_BOOST_BASE_TONUMERICVERSION_req="$2"]) + _AX_BOOST_BASE_TONUMERICVERSION_req_shorten=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '\([[0-9]]*\.[[0-9]]*\)'` + _AX_BOOST_BASE_TONUMERICVERSION_req_major=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '\([[0-9]]*\)'` + AS_IF([test "x$_AX_BOOST_BASE_TONUMERICVERSION_req_major" = "x"], + [AC_MSG_ERROR([You should at least specify libboost major version])]) + _AX_BOOST_BASE_TONUMERICVERSION_req_minor=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '[[0-9]]*\.\([[0-9]]*\)'` + AS_IF([test "x$_AX_BOOST_BASE_TONUMERICVERSION_req_minor" = "x"], + [_AX_BOOST_BASE_TONUMERICVERSION_req_minor="0"]) + _AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + AS_IF([test "X$_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor" = "X"], + [_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor="0"]) + _AX_BOOST_BASE_TONUMERICVERSION_RET=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req_major \* 100000 \+ $_AX_BOOST_BASE_TONUMERICVERSION_req_minor \* 100 \+ $_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor` + AS_VAR_SET($1,$_AX_BOOST_BASE_TONUMERICVERSION_RET) +]) + +dnl Run the detection of boost should be run only if $want_boost +AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[ + _AX_BOOST_BASE_TONUMERICVERSION(WANT_BOOST_VERSION,[$1]) succeeded=no + + AC_REQUIRE([AC_CANONICAL_HOST]) dnl On 64-bit systems check for system libraries in both lib64 and lib. dnl The former is specified by FHS, but e.g. Debian does not adhere to dnl this (as it rises problems for generic multi-arch support). dnl The last entry in the list is chosen by default when no libraries dnl are found, e.g. when only header-only libraries are installed! - libsubdirs="lib" - ax_arch=`uname -m` - case $ax_arch in - x86_64) - libsubdirs="lib64 libx32 lib lib64" - ;; - ppc64|s390x|sparc64|aarch64|ppc64le) - libsubdirs="lib64 lib lib64 ppc64le" - ;; - esac + AS_CASE([${host_cpu}], + [x86_64],[libsubdirs="lib64 libx32 lib lib64"], + [mips*64*],[libsubdirs="lib64 lib32 lib lib64"], + [ppc64|powerpc64|s390x|sparc64|aarch64|ppc64le|powerpc64le|riscv64|e2k|loongarch64],[libsubdirs="lib64 lib lib64"], + [libsubdirs="lib"] + ) dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give dnl them priority over the other paths since, if libs are found there, they dnl are almost assuredly the ones desired. - AC_REQUIRE([AC_CANONICAL_HOST]) - libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs" - - case ${host_cpu} in - i?86) - libsubdirs="lib/i386-${host_os} $libsubdirs" - ;; - esac - - dnl some arches may advertise a cpu type that doesn't line up with their - dnl prefix's cpu type. For example, uname may report armv7l while libs are - dnl installed to /usr/lib/arm-linux-gnueabihf. Try getting the compiler's - dnl value for an extra chance of finding the correct path. - libsubdirs="lib/`$CXX -dumpmachine 2>/dev/null` $libsubdirs" + AS_CASE([${host_cpu}], + [i?86],[multiarch_libsubdir="lib/i386-${host_os}"], + [armv7l],[multiarch_libsubdir="lib/arm-${host_os}"], + [multiarch_libsubdir="lib/${host_cpu}-${host_os}"] + ) dnl first we check the system location for boost libraries dnl this location is chosen if boost libraries are installed with the --layout=system option dnl or if you install boost with RPM - if test "$ac_boost_path" != ""; then - BOOST_CPPFLAGS="-I$ac_boost_path/include" - for ac_boost_path_tmp in $libsubdirs; do - if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then - BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" - break - fi - done - elif test "$cross_compiling" != yes; then - for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then - for libsubdir in $libsubdirs ; do - if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + AS_IF([test "x$_AX_BOOST_BASE_boost_path" != "x"],[ + AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION) includes in "$_AX_BOOST_BASE_boost_path/include"]) + AS_IF([test -d "$_AX_BOOST_BASE_boost_path/include" && test -r "$_AX_BOOST_BASE_boost_path/include"],[ + AC_MSG_RESULT([yes]) + BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path/include" + for _AX_BOOST_BASE_boost_path_tmp in $multiarch_libsubdir $libsubdirs; do + AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION) lib path in "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp"]) + AS_IF([test -d "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp" && test -r "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp" ],[ + AC_MSG_RESULT([yes]) + BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp"; + break; + ], + [AC_MSG_RESULT([no])]) + done],[ + AC_MSG_RESULT([no])]) + ],[ + if test X"$cross_compiling" = Xyes; then + search_libsubdirs=$multiarch_libsubdir + else + search_libsubdirs="$multiarch_libsubdir $libsubdirs" + fi + for _AX_BOOST_BASE_boost_path_tmp in /usr /usr/local /opt /opt/local /opt/homebrew ; do + if test -d "$_AX_BOOST_BASE_boost_path_tmp/include/boost" && test -r "$_AX_BOOST_BASE_boost_path_tmp/include/boost" ; then + for libsubdir in $search_libsubdirs ; do + if ls "$_AX_BOOST_BASE_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done - BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" - BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path_tmp/$libsubdir" + BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path_tmp/include" break; fi done - fi + ]) dnl overwrite ld flags if we have required special directory with dnl --with-boost-libdir parameter - if test "$ac_boost_lib_path" != ""; then - BOOST_LDFLAGS="-L$ac_boost_lib_path" - fi + AS_IF([test "x$_AX_BOOST_BASE_boost_lib_path" != "x"], + [BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_lib_path"]) + AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION)]) CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS @@ -158,15 +179,7 @@ if test "x$want_boost" = "xyes"; then AC_REQUIRE([AC_PROG_CXX]) AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ + AC_COMPILE_IFELSE([_AX_BOOST_BASE_PROGRAM($WANT_BOOST_VERSION)],[ AC_MSG_RESULT(yes) succeeded=yes found_system=yes @@ -178,40 +191,50 @@ if test "x$want_boost" = "xyes"; then dnl if we found no boost with system layout we search for boost libraries dnl built and installed without the --layout=system option or for a staged(not installed) version - if test "x$succeeded" != "xyes"; then + if test "x$succeeded" != "xyes" ; then CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" BOOST_CPPFLAGS= - BOOST_LDFLAGS= + if test -z "$_AX_BOOST_BASE_boost_lib_path" ; then + BOOST_LDFLAGS= + fi _version=0 - if test "$ac_boost_path" != ""; then - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + if test -n "$_AX_BOOST_BASE_boost_path" ; then + if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path"; then + for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then + if test "x$V_CHECK" = "x1" ; then _version=$_version_tmp fi VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` - BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path/include/boost-$VERSION_UNDERSCORE" done dnl if nothing found search for layout used in Windows distributions if test -z "$BOOST_CPPFLAGS"; then - if test -d "$ac_boost_path/boost" && test -r "$ac_boost_path/boost"; then - BOOST_CPPFLAGS="-I$ac_boost_path" + if test -d "$_AX_BOOST_BASE_boost_path/boost" && test -r "$_AX_BOOST_BASE_boost_path/boost"; then + BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path" fi fi + dnl if we found something and BOOST_LDFLAGS was unset before + dnl (because "$_AX_BOOST_BASE_boost_lib_path" = ""), set it here. + if test -n "$BOOST_CPPFLAGS" && test -z "$BOOST_LDFLAGS"; then + for libsubdir in $libsubdirs ; do + if ls "$_AX_BOOST_BASE_boost_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi + done + BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path/$libsubdir" + fi fi else - if test "$cross_compiling" != yes; then - for ac_boost_path in /usr /usr/local /opt /opt/local ; do - if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then - for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do - _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + if test "x$cross_compiling" != "xyes" ; then + for _AX_BOOST_BASE_boost_path in /usr /usr/local /opt /opt/local /opt/homebrew ; do + if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path" ; then + for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` V_CHECK=`expr $_version_tmp \> $_version` - if test "$V_CHECK" = "1" ; then + if test "x$V_CHECK" = "x1" ; then _version=$_version_tmp - best_path=$ac_boost_path + best_path=$_AX_BOOST_BASE_boost_path fi done fi @@ -219,7 +242,7 @@ if test "x$want_boost" = "xyes"; then VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" - if test "$ac_boost_lib_path" = ""; then + if test -z "$_AX_BOOST_BASE_boost_lib_path" ; then for libsubdir in $libsubdirs ; do if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done @@ -227,7 +250,7 @@ if test "x$want_boost" = "xyes"; then fi fi - if test "x$BOOST_ROOT" != "x"; then + if test -n "$BOOST_ROOT" ; then for libsubdir in $libsubdirs ; do if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done @@ -236,7 +259,7 @@ if test "x$want_boost" = "xyes"; then stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` V_CHECK=`expr $stage_version_shorten \>\= $_version` - if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then + if test "x$V_CHECK" = "x1" && test -z "$_AX_BOOST_BASE_boost_lib_path" ; then AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) BOOST_CPPFLAGS="-I$BOOST_ROOT" BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" @@ -251,15 +274,7 @@ if test "x$want_boost" = "xyes"; then export LDFLAGS AC_LANG_PUSH(C++) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - #if BOOST_VERSION >= $WANT_BOOST_VERSION - // Everything is okay - #else - # error Boost version is too old - #endif - ]])],[ + AC_COMPILE_IFELSE([_AX_BOOST_BASE_PROGRAM($WANT_BOOST_VERSION)],[ AC_MSG_RESULT(yes) succeeded=yes found_system=yes @@ -268,17 +283,15 @@ if test "x$want_boost" = "xyes"; then AC_LANG_POP([C++]) fi - if test "$succeeded" != "yes" ; then - if test "$_version" = "0" ; then - AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) + if test "x$succeeded" != "xyes" ; then + if test "x$_version" = "x0" ; then + AC_MSG_NOTICE([[We could not detect the boost libraries (version $1 or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) else AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) fi # execute ACTION-IF-NOT-FOUND (if present): ifelse([$3], , :, [$3]) else - AC_SUBST(BOOST_CPPFLAGS) - AC_SUBST(BOOST_LDFLAGS) AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) # execute ACTION-IF-FOUND (if present): ifelse([$2], , :, [$2]) @@ -286,6 +299,5 @@ if test "x$want_boost" = "xyes"; then CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" -fi ]) diff --git a/build-aux/m4/ax_boost_chrono.m4 b/build-aux/m4/ax_boost_chrono.m4 index 318ecea17..4cd3b8604 100644 --- a/build-aux/m4/ax_boost_chrono.m4 +++ b/build-aux/m4/ax_boost_chrono.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_chrono.html +# https://www.gnu.org/software/autoconf-archive/ax_boost_chrono.html # =========================================================================== # # SYNOPSIS @@ -8,7 +8,7 @@ # # DESCRIPTION # -# Test for System library from the Boost C++ libraries. The macro requires +# Test for Chrono library from the Boost C++ libraries. The macro requires # a preceding call to AX_BOOST_BASE. Further documentation is available at # . # @@ -29,7 +29,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 1 +#serial 5 AC_DEFUN([AX_BOOST_CHRONO], [ @@ -68,7 +68,7 @@ AC_DEFUN([AX_BOOST_CHRONO], CXXFLAGS_SAVE=$CXXFLAGS AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::chrono::system_clock::time_point time;]])], + [[boost::chrono::system_clock::time_point* time = new boost::chrono::system_clock::time_point; delete time;]])], ax_cv_boost_chrono=yes, ax_cv_boost_chrono=no) CXXFLAGS=$CXXFLAGS_SAVE AC_LANG_POP([C++]) @@ -81,7 +81,6 @@ AC_DEFUN([AX_BOOST_CHRONO], LDFLAGS_SAVE=$LDFLAGS if test "x$ax_boost_user_chrono_lib" = "x"; then - ax_lib= for libextension in `ls $BOOSTLIBDIR/libboost_chrono*.so* $BOOSTLIBDIR/libboost_chrono*.dylib* $BOOSTLIBDIR/libboost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_chrono.*\)\.so.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.a.*$;\1;'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, @@ -106,7 +105,7 @@ AC_DEFUN([AX_BOOST_CHRONO], fi if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the boost_chrono library!) + AC_MSG_ERROR(Could not find a version of the Boost::Chrono library!) fi if test "x$link_chrono" = "xno"; then AC_MSG_ERROR(Could not link against $ax_lib !) diff --git a/build-aux/m4/ax_boost_filesystem.m4 b/build-aux/m4/ax_boost_filesystem.m4 index f5c9d5647..12f7bc5e2 100644 --- a/build-aux/m4/ax_boost_filesystem.m4 +++ b/build-aux/m4/ax_boost_filesystem.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_filesystem.html +# https://www.gnu.org/software/autoconf-archive/ax_boost_filesystem.html # =========================================================================== # # SYNOPSIS @@ -31,7 +31,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 26 +#serial 28 AC_DEFUN([AX_BOOST_FILESYSTEM], [ @@ -80,7 +80,6 @@ AC_DEFUN([AX_BOOST_FILESYSTEM], if test "x$ax_cv_boost_filesystem" = "xyes"; then AC_DEFINE(HAVE_BOOST_FILESYSTEM,,[define if the Boost::Filesystem library is available]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - ax_lib= if test "x$ax_boost_user_filesystem_lib" = "x"; then for libextension in `ls -r $BOOSTLIBDIR/libboost_filesystem* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do ax_lib=${libextension} @@ -105,7 +104,7 @@ AC_DEFUN([AX_BOOST_FILESYSTEM], fi if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the boost_filesystem library!) + AC_MSG_ERROR(Could not find a version of the Boost::Filesystem library!) fi if test "x$link_filesystem" != "xyes"; then AC_MSG_ERROR(Could not link against $ax_lib !) diff --git a/build-aux/m4/ax_boost_program_options.m4 b/build-aux/m4/ax_boost_program_options.m4 index 2bdb59371..7d23da464 100644 --- a/build-aux/m4/ax_boost_program_options.m4 +++ b/build-aux/m4/ax_boost_program_options.m4 @@ -1,6 +1,6 @@ -# ============================================================================ -# http://www.gnu.org/software/autoconf-archive/ax_boost_program_options.html -# ============================================================================ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_boost_program_options.html +# ============================================================================= # # SYNOPSIS # @@ -29,7 +29,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 24 +#serial 26 AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], [ @@ -96,7 +96,7 @@ AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], done fi if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the boost_program_options library!) + AC_MSG_ERROR(Could not find a version of the Boost::Program_Options library!) fi if test "x$link_program_options" != "xyes"; then AC_MSG_ERROR([Could not link against [$ax_lib] !]) diff --git a/build-aux/m4/ax_boost_system.m4 b/build-aux/m4/ax_boost_system.m4 index 1c05450cb..323e2a676 100644 --- a/build-aux/m4/ax_boost_system.m4 +++ b/build-aux/m4/ax_boost_system.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_system.html +# https://www.gnu.org/software/autoconf-archive/ax_boost_system.html # =========================================================================== # # SYNOPSIS @@ -31,7 +31,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 18 +#serial 20 AC_DEFUN([AX_BOOST_SYSTEM], [ @@ -84,7 +84,6 @@ AC_DEFUN([AX_BOOST_SYSTEM], LDFLAGS_SAVE=$LDFLAGS if test "x$ax_boost_user_system_lib" = "x"; then - ax_lib= for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do ax_lib=${libextension} AC_CHECK_LIB($ax_lib, exit, @@ -109,7 +108,7 @@ AC_DEFUN([AX_BOOST_SYSTEM], fi if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the boost_system library!) + AC_MSG_ERROR(Could not find a version of the Boost::System library!) fi if test "x$link_system" = "xno"; then AC_MSG_ERROR(Could not link against $ax_lib !) diff --git a/build-aux/m4/ax_boost_thread.m4 b/build-aux/m4/ax_boost_thread.m4 index 9f0bd0b23..75e80e6e7 100644 --- a/build-aux/m4/ax_boost_thread.m4 +++ b/build-aux/m4/ax_boost_thread.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_boost_thread.html +# https://www.gnu.org/software/autoconf-archive/ax_boost_thread.html # =========================================================================== # # SYNOPSIS @@ -30,73 +30,96 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 27 +#serial 33 AC_DEFUN([AX_BOOST_THREAD], [ - AC_ARG_WITH([boost-thread], - AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@], - [use the Thread library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-thread=boost_thread-gcc-mt ]), + AC_ARG_WITH([boost-thread], + AS_HELP_STRING([--with-boost-thread@<:@=special-lib@:>@], + [use the Thread library from boost - + it is possible to specify a certain library for the linker + e.g. --with-boost-thread=boost_thread-gcc-mt ]), [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then + if test "$withval" = "yes"; then want_boost="yes" ax_boost_user_thread_lib="" else - want_boost="yes" - ax_boost_user_thread_lib="$withval" - fi + want_boost="yes" + ax_boost_user_thread_lib="$withval" + fi ], [want_boost="yes"] - ) + ) - if test "x$want_boost" = "xyes"; then + if test "x$want_boost" = "xyes"; then AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS AC_CACHE_CHECK(whether the Boost::Thread library is available, - ax_cv_boost_thread, + ax_cv_boost_thread, [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS + CXXFLAGS_SAVE=$CXXFLAGS + + case "x$host_os" in + xsolaris ) + CXXFLAGS="-pthreads $CXXFLAGS" + break; + ;; + xmingw32 ) + CXXFLAGS="-mthreads $CXXFLAGS" + break; + ;; + *android* ) + break; + ;; + * ) + CXXFLAGS="-pthread $CXXFLAGS" + break; + ;; + esac - if test "x$host_os" = "xsolaris" ; then - CXXFLAGS="-pthreads $CXXFLAGS" - elif test "x$host_os" = "xmingw32" ; then - CXXFLAGS="-mthreads $CXXFLAGS" - else - CXXFLAGS="-pthread $CXXFLAGS" - fi - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::thread_group thrds; - return 0;]])], - ax_cv_boost_thread=yes, ax_cv_boost_thread=no) - CXXFLAGS=$CXXFLAGS_SAVE + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM( + [[@%:@include ]], + [[boost::thread_group thrds; + return 0;]])], + ax_cv_boost_thread=yes, ax_cv_boost_thread=no) + CXXFLAGS=$CXXFLAGS_SAVE AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_thread" = "xyes"; then - if test "x$host_os" = "xsolaris" ; then - BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" - elif test "x$host_os" = "xmingw32" ; then - BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" - else - BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" - fi + ]) + if test "x$ax_cv_boost_thread" = "xyes"; then + case "x$host_os" in + xsolaris ) + BOOST_CPPFLAGS="-pthreads $BOOST_CPPFLAGS" + break; + ;; + xmingw32 ) + BOOST_CPPFLAGS="-mthreads $BOOST_CPPFLAGS" + break; + ;; + *android* ) + break; + ;; + * ) + BOOST_CPPFLAGS="-pthread $BOOST_CPPFLAGS" + break; + ;; + esac - AC_SUBST(BOOST_CPPFLAGS) + AC_SUBST(BOOST_CPPFLAGS) - AC_DEFINE(HAVE_BOOST_THREAD,,[define if the Boost::Thread library is available]) + AC_DEFINE(HAVE_BOOST_THREAD,, + [define if the Boost::Thread library is available]) BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - LDFLAGS_SAVE=$LDFLAGS + LDFLAGS_SAVE=$LDFLAGS case "x$host_os" in *bsd* ) LDFLAGS="-pthread $LDFLAGS" @@ -104,47 +127,61 @@ AC_DEFUN([AX_BOOST_THREAD], ;; esac if test "x$ax_boost_user_thread_lib" = "x"; then - ax_lib= for libextension in `ls -r $BOOSTLIBDIR/libboost_thread* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'`; do ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + AC_CHECK_LIB($ax_lib, exit, + [link_thread="yes"; break], [link_thread="no"]) - done + done if test "x$link_thread" != "xyes"; then for libextension in `ls -r $BOOSTLIBDIR/boost_thread* 2>/dev/null | sed 's,.*/,,' | sed 's,\..*,,'`; do ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + AC_CHECK_LIB($ax_lib, exit, + [link_thread="yes"; break], [link_thread="no"]) - done + done fi else for ax_lib in $ax_boost_user_thread_lib boost_thread-$ax_boost_user_thread_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_THREAD_LIB="-l$ax_lib"; AC_SUBST(BOOST_THREAD_LIB) link_thread="yes"; break], + AC_CHECK_LIB($ax_lib, exit, + [link_thread="yes"; break], [link_thread="no"]) done fi if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the boost_thread library!) + AC_MSG_ERROR(Could not find a version of the Boost::Thread library!) fi - if test "x$link_thread" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - else - case "x$host_os" in - *bsd* ) - BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" - break; - ;; - esac - - fi - fi + if test "x$link_thread" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + else + BOOST_THREAD_LIB="-l$ax_lib" + case "x$host_os" in + *bsd* ) + BOOST_LDFLAGS="-pthread $BOOST_LDFLAGS" + break; + ;; + xsolaris ) + BOOST_THREAD_LIB="$BOOST_THREAD_LIB -lpthread" + break; + ;; + xmingw32 ) + break; + ;; + *android* ) + break; + ;; + * ) + BOOST_THREAD_LIB="$BOOST_THREAD_LIB -lpthread" + break; + ;; + esac + AC_SUBST(BOOST_THREAD_LIB) + fi + fi - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi ]) diff --git a/build-aux/m4/ax_boost_unit_test_framework.m4 b/build-aux/m4/ax_boost_unit_test_framework.m4 index 4efd1e2f1..4cca32fcf 100644 --- a/build-aux/m4/ax_boost_unit_test_framework.m4 +++ b/build-aux/m4/ax_boost_unit_test_framework.m4 @@ -1,6 +1,6 @@ -# ================================================================================ -# http://www.gnu.org/software/autoconf-archive/ax_boost_unit_test_framework.html -# ================================================================================ +# ================================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_boost_unit_test_framework.html +# ================================================================================= # # SYNOPSIS # @@ -29,7 +29,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 19 +#serial 22 AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK], [ @@ -66,7 +66,7 @@ AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK], [AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], [[using boost::unit_test::test_suite; - test_suite* test= BOOST_TEST_SUITE( "Unit test example 1" ); return 0;]])], + test_suite* test= BOOST_TEST_SUITE( "Unit test example 1" ); if (test == NULL) { return 1; } else { return 0; }]])], ax_cv_boost_unit_test_framework=yes, ax_cv_boost_unit_test_framework=no) AC_LANG_POP([C++]) ]) @@ -76,7 +76,6 @@ AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK], if test "x$ax_boost_user_unit_test_framework_lib" = "x"; then saved_ldflags="${LDFLAGS}" - ax_lib= for monitor_library in `ls $BOOSTLIBDIR/libboost_unit_test_framework*.so* $BOOSTLIBDIR/libboost_unit_test_framework*.dylib* $BOOSTLIBDIR/libboost_unit_test_framework*.a* 2>/dev/null` ; do if test -r $monitor_library ; then libextension=`echo $monitor_library | sed 's,.*/,,' | sed -e 's;^lib\(boost_unit_test_framework.*\)\.so.*$;\1;' -e 's;^lib\(boost_unit_test_framework.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_unit_test_framework.*\)\.a.*$;\1;'` @@ -125,7 +124,7 @@ AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK], done fi if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the boost_unit_test_framework library!) + AC_MSG_ERROR(Could not find a version of the Boost::Unit_Test_Framework library!) fi if test "x$link_unit_test_framework" != "xyes"; then AC_MSG_ERROR(Could not link against $ax_lib !) diff --git a/build-aux/m4/ax_check_compile_flag.m4 b/build-aux/m4/ax_check_compile_flag.m4 index ca3639715..bd753b34d 100644 --- a/build-aux/m4/ax_check_compile_flag.m4 +++ b/build-aux/m4/ax_check_compile_flag.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS @@ -29,33 +29,12 @@ # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. -#serial 4 +#serial 6 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF diff --git a/build-aux/m4/ax_check_link_flag.m4 b/build-aux/m4/ax_check_link_flag.m4 index eb01a6ce1..03a30ce4c 100644 --- a/build-aux/m4/ax_check_link_flag.m4 +++ b/build-aux/m4/ax_check_link_flag.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html +# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html # =========================================================================== # # SYNOPSIS @@ -29,33 +29,12 @@ # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. -#serial 4 +#serial 6 AC_DEFUN([AX_CHECK_LINK_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF diff --git a/build-aux/m4/ax_check_preproc_flag.m4 b/build-aux/m4/ax_check_preproc_flag.m4 index ca1d5ee2b..e43560fbd 100644 --- a/build-aux/m4/ax_check_preproc_flag.m4 +++ b/build-aux/m4/ax_check_preproc_flag.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_check_preproc_flag.html +# https://www.gnu.org/software/autoconf-archive/ax_check_preproc_flag.html # =========================================================================== # # SYNOPSIS @@ -29,33 +29,12 @@ # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. -#serial 4 +#serial 6 AC_DEFUN([AX_CHECK_PREPROC_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF diff --git a/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/build-aux/m4/ax_cxx_compile_stdcxx.m4 index 43087b2e6..51a35054d 100644 --- a/build-aux/m4/ax_cxx_compile_stdcxx.m4 +++ b/build-aux/m4/ax_cxx_compile_stdcxx.m4 @@ -10,13 +10,13 @@ # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and -# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) -# or '14' (for the C++14 standard). +# CXXCPP to enable support. VERSION may be '11', '14', '17', or '20' for +# the respective C++ standard version. # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with -# preference for an extended mode. +# preference for no added switch, and then for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is @@ -35,13 +35,15 @@ # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016, 2018 Krzesimir Nowak # Copyright (c) 2019 Enji Cooper +# Copyright (c) 2020 Jason Merrill +# Copyright (c) 2021 Jörn Heusipp # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 11 +#serial 14 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). @@ -50,6 +52,7 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [$1], [20], [ax_cxx_compile_alternatives="20"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], @@ -62,6 +65,16 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl AC_LANG_PUSH([C++])dnl ac_success=no + m4_if([$2], [], [dnl + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi]) + m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do @@ -140,7 +153,6 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) - dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], @@ -148,12 +160,24 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) +dnl Test body for checking C++17 support + m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) +dnl Test body for checking C++20 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_20 +) + + dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ @@ -949,3 +973,33 @@ namespace cxx17 #endif // __cplusplus < 201703L ]]) + + +dnl Tests for new features in C++20 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[ + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 202002L + +#error "This is not a C++20 compiler" + +#else + +#include + +namespace cxx20 +{ + +// As C++20 supports feature test macros in the standard, there is no +// immediate need to actually test for feature availability on the +// Autoconf side. + +} // namespace cxx20 + +#endif // __cplusplus < 202002L + +]]) diff --git a/build-aux/m4/ax_gcc_func_attribute.m4 b/build-aux/m4/ax_gcc_func_attribute.m4 index 02b597357..fa4e089d6 100644 --- a/build-aux/m4/ax_gcc_func_attribute.m4 +++ b/build-aux/m4/ax_gcc_func_attribute.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_gcc_func_attribute.html +# https://www.gnu.org/software/autoconf-archive/ax_gcc_func_attribute.html # =========================================================================== # # SYNOPSIS @@ -38,9 +38,11 @@ # dllimport # error # externally_visible +# fallthrough # flatten # format # format_arg +# gnu_format # gnu_inline # hot # ifunc @@ -53,6 +55,8 @@ # nothrow # optimize # pure +# sentinel +# sentinel_position # unused # used # visibility @@ -61,9 +65,9 @@ # weak # weakref # -# Unsupported function attributes will be tested with a prototype returning -# an int and not accepting any arguments and the result of the check might -# be wrong or meaningless so use with care. +# Unsupported function attributes will be tested with a prototype +# returning an int and not accepting any arguments and the result of the +# check might be wrong or meaningless so use with care. # # LICENSE # @@ -74,7 +78,7 @@ # and this notice are preserved. This file is offered as-is, without any # warranty. -#serial 3 +#serial 13 AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ AS_VAR_PUSHDEF([ac_var], [ax_cv_have_func_attribute_$1]) @@ -128,12 +132,18 @@ AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ [externally_visible], [ int foo( void ) __attribute__(($1)); ], + [fallthrough], [ + void foo( int x ) {switch (x) { case 1: __attribute__(($1)); case 2: break ; }}; + ], [flatten], [ int foo( void ) __attribute__(($1)); ], [format], [ int foo(const char *p, ...) __attribute__(($1(printf, 1, 2))); ], + [gnu_format], [ + int foo(const char *p, ...) __attribute__((format(gnu_printf, 1, 2))); + ], [format_arg], [ char *foo(const char *p) __attribute__(($1(1))); ], @@ -175,6 +185,15 @@ AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ [pure], [ int foo( void ) __attribute__(($1)); ], + [sentinel], [ + int foo(void *p, ...) __attribute__(($1)); + ], + [sentinel_position], [ + int foo(void *p, ...) __attribute__(($1(1))); + ], + [returns_nonnull], [ + void *foo( void ) __attribute__(($1)); + ], [unused], [ int foo( void ) __attribute__(($1)); ], @@ -209,7 +228,7 @@ AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ dnl GCC doesn't exit with an error if an unknown attribute is dnl provided but only outputs a warning, so accept the attribute dnl only if no warning were issued. - [AS_IF([test -s conftest.err], + [AS_IF([grep -- -Wattributes conftest.err], [AS_VAR_SET([ac_var], [no])], [AS_VAR_SET([ac_var], [yes])])], [AS_VAR_SET([ac_var], [no])]) diff --git a/build-aux/m4/ax_pthread.m4 b/build-aux/m4/ax_pthread.m4 index d218d1af7..9f35d1391 100644 --- a/build-aux/m4/ax_pthread.m4 +++ b/build-aux/m4/ax_pthread.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS @@ -14,20 +14,24 @@ # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # -# Also sets PTHREAD_CC to any special C compiler that is needed for -# multi-threaded programs (defaults to the value of CC otherwise). (This -# is necessary on AIX to use the special cc_r compiler alias.) +# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is +# needed for multi-threaded programs (defaults to the value of CC +# respectively CXX otherwise). (This is necessary on e.g. AIX to use the +# special cc_r/CC_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" +# CXX="$PTHREAD_CXX" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to @@ -55,6 +59,7 @@ # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. +# Copyright (c) 2019 Marc Stevens # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -67,7 +72,7 @@ # Public License for more details. # # You should have received a copy of the GNU General Public License along -# with this program. If not, see . +# with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure @@ -82,7 +87,7 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 22 +#serial 31 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ @@ -100,22 +105,23 @@ ax_pthread_ok=no # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then - ax_pthread_save_CC="$CC" - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) - AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) - AC_MSG_RESULT([$ax_pthread_ok]) - if test "x$ax_pthread_ok" = "xno"; then - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" - fi - CC="$ax_pthread_save_CC" - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different @@ -123,10 +129,12 @@ fi # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). -# Create a list of thread flags to try. Items starting with a "-" are -# C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all, and "pthread-config" -# which is a program returning the flags for the Pth emulation library. +# Create a list of thread flags to try. Items with a "," contain both +# C compiler flags (before ",") and linker flags (after ","). Other items +# starting with a "-" are C compiler flags, and remaining items are +# library names, except for "none" which indicates that we try without +# any flags at all, and "pthread-config" which is a program returning +# the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" @@ -152,334 +160,363 @@ ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread -- case $host_os in - freebsd*) + freebsd*) - # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) - # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) - ax_pthread_flags="-kthread lthread $ax_pthread_flags" - ;; + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; - hpux*) + hpux*) - # From the cc(1) man page: "[-mt] Sets various -D flags to enable - # multi-threading and also sets -lpthread." + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." - ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" - ;; + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; - openedition*) + openedition*) - # IBM z/OS requires a feature-test macro to be defined in order to - # enable POSIX threads at all, so give the user a hint if this is - # not set. (We don't define these ourselves, as they can affect - # other portions of the system API in unpredictable ways.) + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) - AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], - [ -# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) - AX_PTHREAD_ZOS_MISSING -# endif - ], - [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) - ;; + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; - solaris*) + solaris*) - # On Solaris (at least, for some versions), libc contains stubbed - # (non-functional) versions of the pthreads routines, so link-based - # tests will erroneously succeed. (N.B.: The stubs are missing - # pthread_cleanup_push, or rather a function called by this macro, - # so we could check for that, but who knows whether they'll stub - # that too in a future libc.) So we'll check first for the - # standard Solaris way of linking pthreads (-mt -lpthread). + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). - ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" - ;; + ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags" + ;; esac +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + + # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) +# Note that for GCC and Clang -pthread generally implies -lpthread, +# except when -nostdlib is passed. +# This is problematic using libtool to build C++ shared libraries with pthread: +# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460 +# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333 +# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555 +# To solve this, first try -pthread together with -lpthread for GCC + AS_IF([test "x$GCC" = "xyes"], - [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"]) + +# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first + +AS_IF([test "x$ax_pthread_clang" = "xyes"], + [ax_pthread_flags="-pthread,-lpthread -pthread"]) + # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in - darwin* | hpux* | linux* | osf* | solaris*) - ax_pthread_check_macro="_REENTRANT" - ;; + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; - aix* | freebsd*) - ax_pthread_check_macro="_THREAD_SAFE" - ;; + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; - *) - ax_pthread_check_macro="--" - ;; + *) + ax_pthread_check_macro="--" + ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) -# Are we compiling with Clang? -AC_CACHE_CHECK([whether $CC is Clang], - [ax_cv_PTHREAD_CLANG], - [ax_cv_PTHREAD_CLANG=no - # Note that Autoconf sets GCC=yes for Clang as well as GCC - if test "x$GCC" = "xyes"; then - AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], - [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ -# if defined(__clang__) && defined(__llvm__) - AX_PTHREAD_CC_IS_CLANG -# endif - ], - [ax_cv_PTHREAD_CLANG=yes]) - fi - ]) -ax_pthread_clang="$ax_cv_PTHREAD_CLANG" +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + *,*) + PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"` + PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"` + AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void *some_global = NULL; + static void routine(void *a) + { + /* To avoid any unused-parameter or + unused-but-set-parameter warning. */ + some_global = a; + } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi -ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then - # Clang takes -pthread; it has never supported any other flag - - # (Note 1: This will need to be revisited if a system that Clang - # supports has POSIX threads in a separate library. This tends not - # to be the way of modern systems, but it's conceivable.) - - # (Note 2: On some systems, notably Darwin, -pthread is not needed - # to get POSIX threads support; the API is always present and - # active. We could reasonably leave PTHREAD_CFLAGS empty. But - # -pthread does define _REENTRANT, and while the Darwin headers - # ignore this macro, third-party headers might not.) - - PTHREAD_CFLAGS="-pthread" - PTHREAD_LIBS= - - ax_pthread_ok=yes - - # However, older versions of Clang make a point of warning the user - # that, in an invocation where only linking and no compilation is - # taking place, the -pthread option has no effect ("argument unused - # during compilation"). They expect -pthread to be passed in only - # when source code is being compiled. - # - # Problem is, this is at odds with the way Automake and most other - # C build frameworks function, which is that the same flags used in - # compilation (CFLAGS) are also used in linking. Many systems - # supported by AX_PTHREAD require exactly this for POSIX threads - # support, and in fact it is often not straightforward to specify a - # flag that is used only in the compilation phase and not in - # linking. Such a scenario is extremely rare in practice. - # - # Even though use of the -pthread flag in linking would only print - # a warning, this can be a nuisance for well-run software projects - # that build with -Werror. So if the active version of Clang has - # this misfeature, we search for an option to squash it. - - AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], - [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], - [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown - # Create an alternate version of $ac_link that compiles and - # links in two steps (.c -> .o, .o -> exe) instead of one - # (.c -> exe), because the warning occurs only in the second - # step - ax_pthread_save_ac_link="$ac_link" - ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' - ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` - ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" - ax_pthread_save_CFLAGS="$CFLAGS" - for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do - AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) - CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" - ac_link="$ax_pthread_save_ac_link" - AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], - [ac_link="$ax_pthread_2step_ac_link" - AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], - [break]) - ]) - done - ac_link="$ax_pthread_save_ac_link" - CFLAGS="$ax_pthread_save_CFLAGS" - AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) - ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" - ]) - - case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in - no | unknown) ;; - *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; - esac + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac fi # $ax_pthread_clang = yes -if test "x$ax_pthread_ok" = "xno"; then -for ax_pthread_try_flag in $ax_pthread_flags; do - case $ax_pthread_try_flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -mt,pthread) - AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) - PTHREAD_CFLAGS="-mt" - PTHREAD_LIBS="-lpthread" - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) - PTHREAD_CFLAGS="$ax_pthread_try_flag" - ;; - - pthread-config) - AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) - AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) - PTHREAD_LIBS="-l$ax_pthread_try_flag" - ;; - esac - - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - - # Check for various functions. We must include pthread.h, - # since some functions may be macros. (On the Sequent, we - # need a special flag -Kthread to make this header compile.) - # We check for pthread_join because it is in -lpthread on IRIX - # while pthread_create is in libc. We check for pthread_attr_init - # due to DEC craziness with -lpthreads. We check for - # pthread_cleanup_push because it is one of the few pthread - # functions on Solaris that doesn't have a non-functional libc stub. - # We try pthread_create on general principles. - - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include -# if $ax_pthread_check_cond -# error "$ax_pthread_check_macro must be defined" -# endif - static void routine(void *a) { a = 0; } - static void *start_routine(void *a) { return a; }], - [pthread_t th; pthread_attr_t attr; - pthread_create(&th, 0, start_routine, 0); - pthread_join(th, 0); - pthread_attr_init(&attr); - pthread_cleanup_push(routine, 0); - pthread_cleanup_pop(0) /* ; */])], - [ax_pthread_ok=yes], - []) - - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" - - AC_MSG_RESULT([$ax_pthread_ok]) - AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" -done -fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - - # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. - AC_CACHE_CHECK([for joinable pthread attribute], - [ax_cv_PTHREAD_JOINABLE_ATTR], - [ax_cv_PTHREAD_JOINABLE_ATTR=unknown - for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [int attr = $ax_pthread_attr; return attr /* ; */])], - [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], - []) - done - ]) - AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ - test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ - test "x$ax_pthread_joinable_attr_defined" != "xyes"], - [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], - [$ax_cv_PTHREAD_JOINABLE_ATTR], - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - ax_pthread_joinable_attr_defined=yes - ]) - - AC_CACHE_CHECK([whether more special flags are required for pthreads], - [ax_cv_PTHREAD_SPECIAL_FLAGS], - [ax_cv_PTHREAD_SPECIAL_FLAGS=no - case $host_os in - solaris*) - ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" - ;; - esac - ]) - AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ - test "x$ax_pthread_special_flags_added" != "xyes"], - [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" - ax_pthread_special_flags_added=yes]) - - AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], - [ax_cv_PTHREAD_PRIO_INHERIT], - [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[int i = PTHREAD_PRIO_INHERIT;]])], - [ax_cv_PTHREAD_PRIO_INHERIT=yes], - [ax_cv_PTHREAD_PRIO_INHERIT=no]) - ]) - AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ - test "x$ax_pthread_prio_inherit_defined" != "xyes"], - [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) - ax_pthread_prio_inherit_defined=yes - ]) - - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" - - # More AIX lossage: compile with *_r variant - if test "x$GCC" != "xyes"; then - case $host_os in - aix*) - AS_CASE(["x/$CC"], - [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], - [#handle absolute path differently from PATH based program lookup - AS_CASE(["x$CC"], - [x/*], - [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], - [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) - ;; - esac - fi + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) + + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT; + return i;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [ + AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"]) + AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])]) + ], + [ + AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC]) + AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])]) + ] + ) + ]) + ;; + esac + fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" +test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) +AC_SUBST([PTHREAD_CXX]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then - ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) - : + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : else - ax_pthread_ok=no - $2 + ax_pthread_ok=no + $2 fi AC_LANG_POP ])dnl AX_PTHREAD diff --git a/build-aux/m4/bitcoin_find_bdb.m4 b/build-aux/m4/bitcoin_find_bdb.m4 index e6c365271..a9749c3a2 100644 --- a/build-aux/m4/bitcoin_find_bdb.m4 +++ b/build-aux/m4/bitcoin_find_bdb.m4 @@ -1,72 +1,99 @@ +dnl Copyright (c) 2017-2024 The BitcoinZ community dnl Copyright (c) 2016-2019 The Zcash developers dnl Copyright (c) 2013-2019 The Bitcoin Core developers -dnl Copyright (c) 2013-2019 Bitcoin Developers dnl Distributed under the MIT software license, see the accompanying dnl file COPYING or https://www.opensource.org/licenses/mit-license.php . AC_DEFUN([BITCOIN_FIND_BDB62],[ - AC_MSG_CHECKING([for Berkeley DB C++ headers]) - BDB_CPPFLAGS= - BDB_LIBS= - bdbpath=X - bdb62path=X - bdbdirlist= - for _vn in 6.2 62 6 ''; do - for _pfx in b lib ''; do - bdbdirlist="$bdbdirlist ${_pfx}db${_vn}" + AC_ARG_VAR([BDB_CFLAGS], [C compiler flags for BerkeleyDB, bypasses autodetection]) + AC_ARG_VAR([BDB_LIBS], [Linker flags for BerkeleyDB, bypasses autodetection]) + + if test "$use_bdb" = "no"; then + use_bdb=no + elif test "$BDB_CFLAGS" = ""; then + AC_MSG_CHECKING([for Berkeley DB C++ headers]) + BDB_CPPFLAGS= + bdbpath=X + bdb62path=X + bdbdirlist= + for _vn in 6.2 62 6 ''; do + for _pfx in b lib ''; do + bdbdirlist="$bdbdirlist ${_pfx}db${_vn}" + done + done + for searchpath in $bdbdirlist ''; do + test -n "${searchpath}" && searchpath="${searchpath}/" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include <${searchpath}db_cxx.h> + ]],[[ + #if !((DB_VERSION_MAJOR == 6 && DB_VERSION_MINOR >= 2) || DB_VERSION_MAJOR > 6) + #error "failed to find bdb 6.2+" + #endif + ]])],[ + if test "$bdbpath" = "X"; then + bdbpath="${searchpath}" + fi + ],[ + continue + ]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include <${searchpath}db_cxx.h> + ]],[[ + #if !(DB_VERSION_MAJOR == 6 && DB_VERSION_MINOR == 2) + #error "failed to find bdb 6.2" + #endif + ]])],[ + bdb62path="${searchpath}" + break + ],[]) done - done - for searchpath in $bdbdirlist ''; do - test -n "${searchpath}" && searchpath="${searchpath}/" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include <${searchpath}db_cxx.h> - ]],[[ - #if !((DB_VERSION_MAJOR == 6 && DB_VERSION_MINOR >= 2) || DB_VERSION_MAJOR > 6) - #error "failed to find bdb 6.2+" - #endif - ]])],[ - if test "x$bdbpath" = "xX"; then - bdbpath="${searchpath}" - fi - ],[ - continue - ]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include <${searchpath}db_cxx.h> - ]],[[ - #if !(DB_VERSION_MAJOR == 6 && DB_VERSION_MINOR == 2) - #error "failed to find bdb 6.2" - #endif - ]])],[ - bdb62path="${searchpath}" - break - ],[]) - done - if test "x$bdbpath" = "xX"; then - AC_MSG_RESULT([no]) - AC_MSG_ERROR([libdb_cxx headers missing, Bitcoin Core requires this library for wallet functionality (--disable-wallet to disable wallet functionality)]) - elif test "x$bdb62path" = "xX"; then - BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdbpath}],db_cxx) - AC_ARG_WITH([incompatible-bdb],[AS_HELP_STRING([--with-incompatible-bdb], [allow using a bdb version other than 6.2])],[ - AC_MSG_WARN([Found Berkeley DB other than 6.2; wallets opened by this build will not be portable!]) - ],[ - AC_MSG_ERROR([Found Berkeley DB other than 6.2, required for portable wallets (--with-incompatible-bdb to ignore or --disable-wallet to disable wallet functionality)]) - ]) + if test "$bdbpath" = "X"; then + use_bdb=no + AC_MSG_RESULT([no]) + AC_MSG_WARN([libdb_cxx headers missing]) + AC_MSG_WARN(AC_PACKAGE_NAME[ requires this library for BDB (legacy) wallet support]) + AC_MSG_WARN([Passing --without-bdb will suppress this warning]) + elif test "$bdb62path" = "X"; then + BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdbpath}],db_cxx) + AC_ARG_WITH([incompatible-bdb],[AS_HELP_STRING([--with-incompatible-bdb], [allow using a bdb version other than 6.2])],[ + AC_MSG_WARN([Found Berkeley DB other than 6.2]) + AC_MSG_WARN([BDB (legacy) wallets opened by this build will not be portable!]) + use_bdb=yes + ],[ + AC_MSG_WARN([Found Berkeley DB other than 6.2]) + AC_MSG_WARN([BDB (legacy) wallets opened by this build would not be portable!]) + AC_MSG_WARN([If this is intended, pass --with-incompatible-bdb]) + AC_MSG_WARN([Passing --without-bdb will suppress this warning]) + use_bdb=no + ]) + else + BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdb62path}],db_cxx) + bdbpath="${bdb62path}" + use_bdb=yes + fi else - BITCOIN_SUBDIR_TO_INCLUDE(BDB_CPPFLAGS,[${bdb62path}],db_cxx) - bdbpath="${bdb62path}" + BDB_CPPFLAGS=${BDB_CFLAGS} fi AC_SUBST(BDB_CPPFLAGS) - - # TODO: Ideally this could find the library version and make sure it matches the headers being used - for searchlib in db_cxx-6.2 db_cxx; do - AC_CHECK_LIB([$searchlib],[main],[ - BDB_LIBS="-l${searchlib}" - break - ]) - done - if test "x$BDB_LIBS" = "x"; then - AC_MSG_ERROR([libdb_cxx missing, Bitcoin Core requires this library for wallet functionality (--disable-wallet to disable wallet functionality)]) + + if test "$use_bdb" = "no"; then + use_bdb=no + elif test "$BDB_LIBS" = ""; then + # TODO: Ideally this could find the library version and make sure it matches the headers being used + for searchlib in db_cxx-6.2 db_cxx; do + AC_CHECK_LIB([$searchlib],[main],[ + BDB_LIBS="-l${searchlib}" + break + ]) + done + if test "$BDB_LIBS" = ""; then + AC_MSG_WARN([libdb_cxx headers missing]) + AC_MSG_WARN(AC_PACKAGE_NAME[ requires this library for BDB (legacy) wallet support]) + AC_MSG_WARN([Passing --without-bdb will suppress this warning]) + fi + fi + if test "$use_bdb" != "no"; then + AC_DEFINE([USE_BDB], [1], [Define if BDB support should be compiled in]) + use_bdb=yes fi - AC_SUBST(BDB_LIBS) ]) diff --git a/build-aux/m4/bitcoin_subdir_to_include.m4 b/build-aux/m4/bitcoin_subdir_to_include.m4 index 52da5ef9f..e32e7ca90 100644 --- a/build-aux/m4/bitcoin_subdir_to_include.m4 +++ b/build-aux/m4/bitcoin_subdir_to_include.m4 @@ -1,19 +1,19 @@ +dnl Copyright (c) 2017-2024 The BitcoinZ community dnl Copyright (c) 2016-2019 The Zcash developers dnl Copyright (c) 2013-2019 The Bitcoin Core developers -dnl Copyright (c) 2013-2019 Bitcoin Developers dnl Distributed under the MIT software license, see the accompanying dnl file COPYING or https://www.opensource.org/licenses/mit-license.php . dnl BITCOIN_SUBDIR_TO_INCLUDE([CPPFLAGS-VARIABLE-NAME],[SUBDIRECTORY-NAME],[HEADER-FILE]) dnl SUBDIRECTORY-NAME must end with a path separator AC_DEFUN([BITCOIN_SUBDIR_TO_INCLUDE],[ - if test "x$2" = "x"; then + if test "$2" = ""; then AC_MSG_RESULT([default]) else echo "#include <$2$3.h>" >conftest.cpp newinclpath=`${CXXCPP} ${CPPFLAGS} -M conftest.cpp 2>/dev/null | [ tr -d '\\n\\r\\\\' | sed -e 's/^.*[[:space:]:]\(\/[^[:space:]]*\)]$3[\.h[[:space:]].*$/\1/' -e t -e d`] AC_MSG_RESULT([${newinclpath}]) - if test "x${newinclpath}" != "x"; then + if test "${newinclpath}" != ""; then eval "$1=\"\$$1\"' -I${newinclpath}'" fi fi diff --git a/build-aux/m4/l_atomic.m4 b/build-aux/m4/l_atomic.m4 index 75c43f9a9..859ddaabb 100644 --- a/build-aux/m4/l_atomic.m4 +++ b/build-aux/m4/l_atomic.m4 @@ -4,18 +4,34 @@ dnl permitted in any medium without royalty provided the copyright notice dnl and this notice are preserved. This file is offered as-is, without any dnl warranty. -# Some versions of gcc/libstdc++ require linking with -latomic if -# using the C++ atomic library. +# Clang, when building for 32-bit, +# and linking against libstdc++, requires linking with +# -latomic if using the C++ atomic library. +# Can be tested with: clang++ -std=c++20 test.cpp -m32 # # Sourced from http://bugs.debian.org/797228 m4_define([_CHECK_ATOMIC_testbody], [[ #include #include + #include + + using namespace std::chrono_literals; int main() { - std::atomic a{}; + std::atomic lock{true}; + lock.exchange(false); + + std::atomic t{0s}; + t.store(2s); + auto t1 = t.load(); + t.compare_exchange_strong(t1, 3s); + std::atomic d{}; + d.store(3.14); + auto d1 = d.load(); + + std::atomic a{}; int64_t v = 5; int64_t r = a.fetch_add(v); return static_cast(r); @@ -25,6 +41,8 @@ m4_define([_CHECK_ATOMIC_testbody], [[ AC_DEFUN([CHECK_ATOMIC], [ AC_LANG_PUSH(C++) + TEMP_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" AC_MSG_CHECKING([whether std::atomic can be used without link library]) @@ -42,5 +60,6 @@ AC_DEFUN([CHECK_ATOMIC], [ ]) ]) + CXXFLAGS="$TEMP_CXXFLAGS" AC_LANG_POP ]) From 98485ed502910fb015a52ecbe8663569c9e982c8 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:01:11 +0000 Subject: [PATCH 004/146] Update secp256k1 subtree to latest upstream --- src/key.cpp | 80 +- src/secp256k1/.cirrus.yml | 98 + src/secp256k1/.gitattributes | 2 + .../install-homebrew-valgrind/action.yml | 33 + .../actions/run-in-docker-action/action.yml | 54 + src/secp256k1/.github/workflows/ci.yml | 879 + src/secp256k1/.gitignore | 41 +- src/secp256k1/.travis.yml | 69 - src/secp256k1/CHANGELOG.md | 152 + src/secp256k1/CMakeLists.txt | 373 + src/secp256k1/CMakePresets.json | 19 + src/secp256k1/CONTRIBUTING.md | 108 + src/secp256k1/Makefile.am | 259 +- src/secp256k1/README.md | 118 +- src/secp256k1/SECURITY.md | 15 + src/secp256k1/TODO | 3 - .../build-aux/m4/ax_jni_include_dir.m4 | 140 - .../build-aux/m4/ax_prog_cc_for_build.m4 | 125 - src/secp256k1/build-aux/m4/bitcoin_secp.m4 | 134 +- src/secp256k1/ci/ci.sh | 148 + src/secp256k1/ci/linux-debian.Dockerfile | 79 + src/secp256k1/cmake/CheckArm32Assembly.cmake | 6 + .../cmake/CheckMemorySanitizer.cmake | 18 + .../cmake/CheckStringOptionValue.cmake | 10 + src/secp256k1/cmake/CheckX86_64Assembly.cmake | 14 + src/secp256k1/cmake/FindValgrind.cmake | 41 + .../cmake/GeneratePkgConfigFile.cmake | 8 + src/secp256k1/cmake/TryAppendCFlags.cmake | 24 + .../cmake/arm-linux-gnueabihf.toolchain.cmake | 3 + src/secp256k1/cmake/config.cmake.in | 5 + src/secp256k1/cmake/source_arm32.s | 9 + .../cmake/x86_64-w64-mingw32.toolchain.cmake | 3 + src/secp256k1/configure.ac | 750 +- src/secp256k1/contrib/lax_der_parsing.c | 22 +- src/secp256k1/contrib/lax_der_parsing.h | 20 +- .../contrib/lax_der_privatekey_parsing.c | 13 +- .../contrib/lax_der_privatekey_parsing.h | 19 +- src/secp256k1/doc/ellswift.md | 483 + src/secp256k1/doc/release-process.md | 94 + src/secp256k1/doc/safegcd_implementation.md | 819 + src/secp256k1/examples/CMakeLists.txt | 34 + src/secp256k1/examples/EXAMPLES_COPYING | 121 + src/secp256k1/examples/ecdh.c | 122 + src/secp256k1/examples/ecdsa.c | 139 + src/secp256k1/examples/ellswift.c | 123 + src/secp256k1/examples/examples_util.h | 108 + src/secp256k1/examples/schnorr.c | 156 + src/secp256k1/include/secp256k1.h | 716 +- src/secp256k1/include/secp256k1_ecdh.h | 52 +- src/secp256k1/include/secp256k1_ellswift.h | 200 + src/secp256k1/include/secp256k1_extrakeys.h | 247 + .../include/secp256k1_preallocated.h | 134 + src/secp256k1/include/secp256k1_recovery.h | 65 +- src/secp256k1/include/secp256k1_schnorrsig.h | 190 + src/secp256k1/libsecp256k1.pc.in | 1 - src/secp256k1/obj/.gitignore | 0 src/secp256k1/sage/gen_exhaustive_groups.sage | 156 + .../sage/gen_split_lambda_constants.sage | 114 + src/secp256k1/sage/group_prover.sage | 87 +- ....sage => prove_group_implementations.sage} | 163 +- src/secp256k1/sage/secp256k1_params.sage | 39 + src/secp256k1/sage/weierstrass_prover.sage | 45 +- src/secp256k1/src/CMakeLists.txt | 173 + src/secp256k1/src/asm/field_10x26_arm.s | 19 +- src/secp256k1/src/assumptions.h | 87 + src/secp256k1/src/basic-config.h | 33 - src/secp256k1/src/bench.c | 279 + src/secp256k1/src/bench.h | 188 +- src/secp256k1/src/bench_ecmult.c | 367 + src/secp256k1/src/bench_internal.c | 450 +- src/secp256k1/src/bench_sign.c | 56 - src/secp256k1/src/bench_verify.c | 112 - src/secp256k1/src/checkmem.h | 102 + src/secp256k1/src/ctime_tests.c | 209 + src/secp256k1/src/ecdsa.h | 12 +- src/secp256k1/src/ecdsa_impl.h | 139 +- src/secp256k1/src/eckey.h | 14 +- src/secp256k1/src/eckey_impl.h | 40 +- src/secp256k1/src/ecmult.h | 68 +- src/secp256k1/src/ecmult_compute_table.h | 16 + src/secp256k1/src/ecmult_compute_table_impl.h | 49 + src/secp256k1/src/ecmult_const.h | 33 +- src/secp256k1/src/ecmult_const_impl.h | 545 +- src/secp256k1/src/ecmult_gen.h | 150 +- src/secp256k1/src/ecmult_gen_compute_table.h | 14 + .../src/ecmult_gen_compute_table_impl.h | 108 + src/secp256k1/src/ecmult_gen_impl.h | 430 +- src/secp256k1/src/ecmult_impl.h | 985 +- src/secp256k1/src/field.h | 373 +- src/secp256k1/src/field_10x26.h | 41 +- src/secp256k1/src/field_10x26_impl.h | 398 +- src/secp256k1/src/field_5x52.h | 47 +- src/secp256k1/src/field_5x52_asm_impl.h | 502 - src/secp256k1/src/field_5x52_impl.h | 365 +- src/secp256k1/src/field_5x52_int128_impl.h | 293 +- src/secp256k1/src/field_impl.h | 493 +- src/secp256k1/src/gen_context.c | 74 - src/secp256k1/src/group.h | 130 +- src/secp256k1/src/group_impl.h | 818 +- src/secp256k1/src/hash.h | 14 +- src/secp256k1/src/hash_impl.h | 111 +- src/secp256k1/src/hsort.h | 33 + src/secp256k1/src/hsort_impl.h | 125 + src/secp256k1/src/int128.h | 90 + src/secp256k1/src/int128_impl.h | 18 + src/secp256k1/src/int128_native.h | 19 + src/secp256k1/src/int128_native_impl.h | 94 + src/secp256k1/src/int128_struct.h | 14 + src/secp256k1/src/int128_struct_impl.h | 205 + .../src/java/org/bitcoin/NativeSecp256k1.java | 446 - .../java/org/bitcoin/NativeSecp256k1Test.java | 226 - .../java/org/bitcoin/NativeSecp256k1Util.java | 45 - .../java/org/bitcoin/Secp256k1Context.java | 51 - .../src/java/org_bitcoin_NativeSecp256k1.c | 377 - .../src/java/org_bitcoin_NativeSecp256k1.h | 119 - .../src/java/org_bitcoin_Secp256k1Context.c | 15 - .../src/java/org_bitcoin_Secp256k1Context.h | 22 - src/secp256k1/src/modinv32.h | 43 + src/secp256k1/src/modinv32_impl.h | 727 + src/secp256k1/src/modinv64.h | 47 + src/secp256k1/src/modinv64_impl.h | 782 + .../src/modules/ecdh/Makefile.am.include | 6 +- .../ecdh/bench_impl.h} | 47 +- src/secp256k1/src/modules/ecdh/main_impl.h | 83 +- src/secp256k1/src/modules/ecdh/tests_impl.h | 135 +- .../src/modules/ellswift/Makefile.am.include | 5 + .../src/modules/ellswift/bench_impl.h | 106 + .../src/modules/ellswift/main_impl.h | 590 + .../modules/ellswift/tests_exhaustive_impl.h | 39 + .../src/modules/ellswift/tests_impl.h | 436 + .../src/modules/extrakeys/Makefile.am.include | 4 + .../src/modules/extrakeys/main_impl.h | 285 + .../modules/extrakeys/tests_exhaustive_impl.h | 68 + .../src/modules/extrakeys/tests_impl.h | 483 + .../src/modules/recovery/Makefile.am.include | 7 +- .../recovery/bench_impl.h} | 42 +- .../src/modules/recovery/main_impl.h | 76 +- .../modules/recovery/tests_exhaustive_impl.h | 148 + .../src/modules/recovery/tests_impl.h | 281 +- .../modules/schnorrsig/Makefile.am.include | 5 + .../src/modules/schnorrsig/bench_impl.h | 104 + .../src/modules/schnorrsig/main_impl.h | 267 + .../schnorrsig/tests_exhaustive_impl.h | 214 + .../src/modules/schnorrsig/tests_impl.h | 982 + src/secp256k1/src/num.h | 74 - src/secp256k1/src/num_gmp.h | 20 - src/secp256k1/src/num_gmp_impl.h | 288 - src/secp256k1/src/num_impl.h | 24 - src/secp256k1/src/precompute_ecmult.c | 90 + src/secp256k1/src/precompute_ecmult_gen.c | 100 + src/secp256k1/src/precomputed_ecmult.c | 16456 ++++++++++++++++ src/secp256k1/src/precomputed_ecmult.h | 38 + src/secp256k1/src/precomputed_ecmult_gen.c | 1779 ++ src/secp256k1/src/precomputed_ecmult_gen.h | 26 + src/secp256k1/src/scalar.h | 77 +- src/secp256k1/src/scalar_4x64.h | 10 +- src/secp256k1/src/scalar_4x64_impl.h | 650 +- src/secp256k1/src/scalar_8x32.h | 10 +- src/secp256k1/src/scalar_8x32_impl.h | 410 +- src/secp256k1/src/scalar_impl.h | 510 +- src/secp256k1/src/scalar_low.h | 19 +- src/secp256k1/src/scalar_low_impl.h | 158 +- src/secp256k1/src/scratch.h | 42 + src/secp256k1/src/scratch_impl.h | 99 + src/secp256k1/src/secp256k1.c | 615 +- src/secp256k1/src/selftest.h | 32 + src/secp256k1/src/testrand.h | 40 +- src/secp256k1/src/testrand_impl.h | 199 +- src/secp256k1/src/tests.c | 6730 +++++-- src/secp256k1/src/tests_exhaustive.c | 522 +- src/secp256k1/src/testutil.h | 148 + src/secp256k1/src/util.h | 344 +- .../src/wycheproof/WYCHEPROOF_COPYING | 212 + .../ecdsa_secp256k1_sha256_bitcoin_test.h | 1564 ++ .../ecdsa_secp256k1_sha256_bitcoin_test.json | 6360 ++++++ src/secp256k1/tools/check-abi.sh | 67 + .../tools/tests_wycheproof_generate.py | 115 + 177 files changed, 52978 insertions(+), 9400 deletions(-) create mode 100644 src/secp256k1/.cirrus.yml create mode 100644 src/secp256k1/.gitattributes create mode 100644 src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml create mode 100644 src/secp256k1/.github/actions/run-in-docker-action/action.yml create mode 100644 src/secp256k1/.github/workflows/ci.yml delete mode 100644 src/secp256k1/.travis.yml create mode 100644 src/secp256k1/CHANGELOG.md create mode 100644 src/secp256k1/CMakeLists.txt create mode 100644 src/secp256k1/CMakePresets.json create mode 100644 src/secp256k1/CONTRIBUTING.md create mode 100644 src/secp256k1/SECURITY.md delete mode 100644 src/secp256k1/TODO delete mode 100644 src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 delete mode 100644 src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 create mode 100755 src/secp256k1/ci/ci.sh create mode 100644 src/secp256k1/ci/linux-debian.Dockerfile create mode 100644 src/secp256k1/cmake/CheckArm32Assembly.cmake create mode 100644 src/secp256k1/cmake/CheckMemorySanitizer.cmake create mode 100644 src/secp256k1/cmake/CheckStringOptionValue.cmake create mode 100644 src/secp256k1/cmake/CheckX86_64Assembly.cmake create mode 100644 src/secp256k1/cmake/FindValgrind.cmake create mode 100644 src/secp256k1/cmake/GeneratePkgConfigFile.cmake create mode 100644 src/secp256k1/cmake/TryAppendCFlags.cmake create mode 100644 src/secp256k1/cmake/arm-linux-gnueabihf.toolchain.cmake create mode 100644 src/secp256k1/cmake/config.cmake.in create mode 100644 src/secp256k1/cmake/source_arm32.s create mode 100644 src/secp256k1/cmake/x86_64-w64-mingw32.toolchain.cmake create mode 100644 src/secp256k1/doc/ellswift.md create mode 100644 src/secp256k1/doc/release-process.md create mode 100644 src/secp256k1/doc/safegcd_implementation.md create mode 100644 src/secp256k1/examples/CMakeLists.txt create mode 100644 src/secp256k1/examples/EXAMPLES_COPYING create mode 100644 src/secp256k1/examples/ecdh.c create mode 100644 src/secp256k1/examples/ecdsa.c create mode 100644 src/secp256k1/examples/ellswift.c create mode 100644 src/secp256k1/examples/examples_util.h create mode 100644 src/secp256k1/examples/schnorr.c create mode 100644 src/secp256k1/include/secp256k1_ellswift.h create mode 100644 src/secp256k1/include/secp256k1_extrakeys.h create mode 100644 src/secp256k1/include/secp256k1_preallocated.h create mode 100644 src/secp256k1/include/secp256k1_schnorrsig.h delete mode 100644 src/secp256k1/obj/.gitignore create mode 100644 src/secp256k1/sage/gen_exhaustive_groups.sage create mode 100644 src/secp256k1/sage/gen_split_lambda_constants.sage rename src/secp256k1/sage/{secp256k1.sage => prove_group_implementations.sage} (72%) create mode 100644 src/secp256k1/sage/secp256k1_params.sage create mode 100644 src/secp256k1/src/CMakeLists.txt create mode 100644 src/secp256k1/src/assumptions.h delete mode 100644 src/secp256k1/src/basic-config.h create mode 100644 src/secp256k1/src/bench.c create mode 100644 src/secp256k1/src/bench_ecmult.c delete mode 100644 src/secp256k1/src/bench_sign.c delete mode 100644 src/secp256k1/src/bench_verify.c create mode 100644 src/secp256k1/src/checkmem.h create mode 100644 src/secp256k1/src/ctime_tests.c create mode 100644 src/secp256k1/src/ecmult_compute_table.h create mode 100644 src/secp256k1/src/ecmult_compute_table_impl.h create mode 100644 src/secp256k1/src/ecmult_gen_compute_table.h create mode 100644 src/secp256k1/src/ecmult_gen_compute_table_impl.h delete mode 100644 src/secp256k1/src/field_5x52_asm_impl.h delete mode 100644 src/secp256k1/src/gen_context.c create mode 100644 src/secp256k1/src/hsort.h create mode 100644 src/secp256k1/src/hsort_impl.h create mode 100644 src/secp256k1/src/int128.h create mode 100644 src/secp256k1/src/int128_impl.h create mode 100644 src/secp256k1/src/int128_native.h create mode 100644 src/secp256k1/src/int128_native_impl.h create mode 100644 src/secp256k1/src/int128_struct.h create mode 100644 src/secp256k1/src/int128_struct_impl.h delete mode 100644 src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java delete mode 100644 src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java delete mode 100644 src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java delete mode 100644 src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java delete mode 100644 src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c delete mode 100644 src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h delete mode 100644 src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c delete mode 100644 src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h create mode 100644 src/secp256k1/src/modinv32.h create mode 100644 src/secp256k1/src/modinv32_impl.h create mode 100644 src/secp256k1/src/modinv64.h create mode 100644 src/secp256k1/src/modinv64_impl.h rename src/secp256k1/src/{bench_ecdh.c => modules/ecdh/bench_impl.h} (57%) create mode 100644 src/secp256k1/src/modules/ellswift/Makefile.am.include create mode 100644 src/secp256k1/src/modules/ellswift/bench_impl.h create mode 100644 src/secp256k1/src/modules/ellswift/main_impl.h create mode 100644 src/secp256k1/src/modules/ellswift/tests_exhaustive_impl.h create mode 100644 src/secp256k1/src/modules/ellswift/tests_impl.h create mode 100644 src/secp256k1/src/modules/extrakeys/Makefile.am.include create mode 100644 src/secp256k1/src/modules/extrakeys/main_impl.h create mode 100644 src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h create mode 100644 src/secp256k1/src/modules/extrakeys/tests_impl.h rename src/secp256k1/src/{bench_recover.c => modules/recovery/bench_impl.h} (61%) mode change 100755 => 100644 src/secp256k1/src/modules/recovery/main_impl.h create mode 100644 src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h create mode 100644 src/secp256k1/src/modules/schnorrsig/Makefile.am.include create mode 100644 src/secp256k1/src/modules/schnorrsig/bench_impl.h create mode 100644 src/secp256k1/src/modules/schnorrsig/main_impl.h create mode 100644 src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h create mode 100644 src/secp256k1/src/modules/schnorrsig/tests_impl.h delete mode 100644 src/secp256k1/src/num.h delete mode 100644 src/secp256k1/src/num_gmp.h delete mode 100644 src/secp256k1/src/num_gmp_impl.h delete mode 100644 src/secp256k1/src/num_impl.h create mode 100644 src/secp256k1/src/precompute_ecmult.c create mode 100644 src/secp256k1/src/precompute_ecmult_gen.c create mode 100644 src/secp256k1/src/precomputed_ecmult.c create mode 100644 src/secp256k1/src/precomputed_ecmult.h create mode 100644 src/secp256k1/src/precomputed_ecmult_gen.c create mode 100644 src/secp256k1/src/precomputed_ecmult_gen.h create mode 100644 src/secp256k1/src/scratch.h create mode 100644 src/secp256k1/src/scratch_impl.h create mode 100644 src/secp256k1/src/selftest.h create mode 100644 src/secp256k1/src/testutil.h create mode 100644 src/secp256k1/src/wycheproof/WYCHEPROOF_COPYING create mode 100644 src/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h create mode 100644 src/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json create mode 100755 src/secp256k1/tools/check-abi.sh create mode 100755 src/secp256k1/tools/tests_wycheproof_generate.py diff --git a/src/key.cpp b/src/key.cpp index 6e693921d..573023a1d 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -34,46 +34,46 @@ static secp256k1_context* secp256k1_context_sign = NULL; * * out32 must point to an output buffer of length at least 32 bytes. */ -static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) { - const unsigned char *end = privkey + privkeylen; +static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) { + const unsigned char *end = seckey + seckeylen; memset(out32, 0, 32); /* sequence header */ - if (end - privkey < 1 || *privkey != 0x30u) { + if (end - seckey < 1 || *seckey != 0x30u) { return 0; } - privkey++; + seckey++; /* sequence length constructor */ - if (end - privkey < 1 || !(*privkey & 0x80u)) { + if (end - seckey < 1 || !(*seckey & 0x80u)) { return 0; } - size_t lenb = *privkey & ~0x80u; privkey++; + size_t lenb = *seckey & ~0x80u; seckey++; if (lenb < 1 || lenb > 2) { return 0; } - if (end - privkey < lenb) { + if (end - seckey < lenb) { return 0; } /* sequence length */ - size_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u); - privkey += lenb; - if (end - privkey < len) { + size_t len = seckey[lenb-1] | (lenb > 1 ? seckey[lenb-2] << 8 : 0u); + seckey += lenb; + if (end - seckey < len) { return 0; } /* sequence element 0: version number (=1) */ - if (end - privkey < 3 || privkey[0] != 0x02u || privkey[1] != 0x01u || privkey[2] != 0x01u) { + if (end - seckey < 3 || seckey[0] != 0x02u || seckey[1] != 0x01u || seckey[2] != 0x01u) { return 0; } - privkey += 3; + seckey += 3; /* sequence element 1: octet string, up to 32 bytes */ - if (end - privkey < 2 || privkey[0] != 0x04u) { + if (end - seckey < 2 || seckey[0] != 0x04u) { return 0; } - size_t oslen = privkey[1]; - privkey += 2; - if (oslen > 32 || end - privkey < oslen) { + size_t oslen = seckey[1]; + seckey += 2; + if (oslen > 32 || end - seckey < oslen) { return 0; } - memcpy(out32 + (32 - oslen), privkey, oslen); + memcpy(out32 + (32 - oslen), seckey, oslen); if (!secp256k1_ec_seckey_verify(ctx, out32)) { memset(out32, 0, 32); return 0; @@ -86,17 +86,17 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou * . The optional parameters and publicKey fields are * included. * - * privkey must point to an output buffer of length at least CKey::PRIVATE_KEY_SIZE bytes. - * privkeylen must initially be set to the size of the privkey buffer. Upon return it + * seckey must point to an output buffer of length at least CKey::PRIVATE_KEY_SIZE bytes. + * seckeylen must initially be set to the size of the seckey buffer. Upon return it * will be set to the number of bytes used in the buffer. * key32 must point to a 32-byte raw private key. */ -static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) { - assert(*privkeylen >= CKey::PRIVATE_KEY_SIZE); +static int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, int compressed) { + assert(*seckeylen >= CKey::PRIVATE_KEY_SIZE); secp256k1_pubkey pubkey; size_t pubkeylen = 0; if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { - *privkeylen = 0; + *seckeylen = 0; return 0; } if (compressed) { @@ -114,15 +114,15 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E, 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00 }; - unsigned char *ptr = privkey; + unsigned char *ptr = seckey; memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); memcpy(ptr, key32, 32); ptr += 32; memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); pubkeylen = CPubKey::COMPRESSED_PUBLIC_KEY_SIZE; secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); ptr += pubkeylen; - *privkeylen = ptr - privkey; - assert(*privkeylen == CKey::COMPRESSED_PRIVATE_KEY_SIZE); + *seckeylen = ptr - seckey; + assert(*seckeylen == CKey::COMPRESSED_PRIVATE_KEY_SIZE); } else { static const unsigned char begin[] = { 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 @@ -140,15 +140,15 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E, 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00 }; - unsigned char *ptr = privkey; + unsigned char *ptr = seckey; memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); memcpy(ptr, key32, 32); ptr += 32; memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); pubkeylen = CPubKey::PUBLIC_KEY_SIZE; secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); ptr += pubkeylen; - *privkeylen = ptr - privkey; - assert(*privkeylen == CKey::PRIVATE_KEY_SIZE); + *seckeylen = ptr - seckey; + assert(*seckeylen == CKey::PRIVATE_KEY_SIZE); } return 1; } @@ -165,8 +165,8 @@ void CKey::MakeNewKey(bool fCompressedIn) { fCompressed = fCompressedIn; } -bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { - if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size())) +bool CKey::SetPrivKey(const CPrivKey &seckey, bool fCompressedIn) { + if (!ec_seckey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &seckey[0], seckey.size())) return false; fCompressed = fCompressedIn; fValid = true; @@ -175,15 +175,15 @@ bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { CPrivKey CKey::GetPrivKey() const { assert(fValid); - CPrivKey privkey; + CPrivKey seckey; int ret; - size_t privkeylen; - privkey.resize(PRIVATE_KEY_SIZE); - privkeylen = PRIVATE_KEY_SIZE; - ret = ec_privkey_export_der(secp256k1_context_sign, (unsigned char*)&privkey[0], &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); + size_t seckeylen; + seckey.resize(PRIVATE_KEY_SIZE); + seckeylen = PRIVATE_KEY_SIZE; + ret = ec_seckey_export_der(secp256k1_context_sign, (unsigned char*)&seckey[0], &seckeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); assert(ret); - privkey.resize(privkeylen); - return privkey; + seckey.resize(seckeylen); + return seckey; } CPubKey CKey::GetPubKey() const { @@ -243,8 +243,8 @@ bool CKey::SignCompact(const uint256 &hash, std::vector& vchSig) return true; } -bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { - if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size())) +bool CKey::Load(CPrivKey &seckey, CPubKey &vchPubKey, bool fSkipCheck=false) { + if (!ec_seckey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &seckey[0], seckey.size())) return false; fCompressed = vchPubKey.IsCompressed(); fValid = true; @@ -270,7 +270,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const } memcpy(ccChild.begin(), out+32, 32); memcpy((unsigned char*)keyChild.begin(), begin(), 32); - bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), out); + bool ret = secp256k1_ec_seckey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), out); UnlockObject(out); keyChild.fCompressed = true; keyChild.fValid = ret; diff --git a/src/secp256k1/.cirrus.yml b/src/secp256k1/.cirrus.yml new file mode 100644 index 000000000..0c1e01dc9 --- /dev/null +++ b/src/secp256k1/.cirrus.yml @@ -0,0 +1,98 @@ +env: + ### cirrus config + CIRRUS_CLONE_DEPTH: 1 + ### compiler options + HOST: + WRAPPER_CMD: + # Specific warnings can be disabled with -Wno-error=foo. + # -pedantic-errors is not equivalent to -Werror=pedantic and thus not implied by -Werror according to the GCC manual. + WERROR_CFLAGS: -Werror -pedantic-errors + MAKEFLAGS: -j4 + BUILD: check + ### secp256k1 config + ECMULTWINDOW: 15 + ECMULTGENKB: 22 + ASM: no + WIDEMUL: auto + WITH_VALGRIND: yes + EXTRAFLAGS: + ### secp256k1 modules + EXPERIMENTAL: no + ECDH: no + RECOVERY: no + EXTRAKEYS: no + SCHNORRSIG: no + ELLSWIFT: no + ### test options + SECP256K1_TEST_ITERS: + BENCH: yes + SECP256K1_BENCH_ITERS: 2 + CTIMETESTS: yes + # Compile and run the tests + EXAMPLES: yes + +cat_logs_snippet: &CAT_LOGS + always: + cat_tests_log_script: + - cat tests.log || true + cat_noverify_tests_log_script: + - cat noverify_tests.log || true + cat_exhaustive_tests_log_script: + - cat exhaustive_tests.log || true + cat_ctime_tests_log_script: + - cat ctime_tests.log || true + cat_bench_log_script: + - cat bench.log || true + cat_config_log_script: + - cat config.log || true + cat_test_env_script: + - cat test_env.log || true + cat_ci_env_script: + - env + +linux_arm64_container_snippet: &LINUX_ARM64_CONTAINER + env_script: + - env | tee /tmp/env + build_script: + - DOCKER_BUILDKIT=1 docker build --file "ci/linux-debian.Dockerfile" --tag="ci_secp256k1_arm" + - docker image prune --force # Cleanup stale layers + test_script: + - docker run --rm --mount "type=bind,src=./,dst=/ci_secp256k1" --env-file /tmp/env --replace --name "ci_secp256k1_arm" "ci_secp256k1_arm" bash -c "cd /ci_secp256k1/ && ./ci/ci.sh" + +task: + name: "ARM64: Linux (Debian stable)" + persistent_worker: + labels: + type: arm64 + env: + ECDH: yes + RECOVERY: yes + EXTRAKEYS: yes + SCHNORRSIG: yes + ELLSWIFT: yes + matrix: + # Currently only gcc-snapshot, the other compilers are tested on GHA with QEMU + - env: { CC: 'gcc-snapshot' } + << : *LINUX_ARM64_CONTAINER + << : *CAT_LOGS + +task: + name: "ARM64: Linux (Debian stable), Valgrind" + persistent_worker: + labels: + type: arm64 + env: + ECDH: yes + RECOVERY: yes + EXTRAKEYS: yes + SCHNORRSIG: yes + ELLSWIFT: yes + WRAPPER_CMD: 'valgrind --error-exitcode=42' + SECP256K1_TEST_ITERS: 2 + matrix: + - env: { CC: 'gcc' } + - env: { CC: 'clang' } + - env: { CC: 'gcc-snapshot' } + - env: { CC: 'clang-snapshot' } + << : *LINUX_ARM64_CONTAINER + << : *CAT_LOGS diff --git a/src/secp256k1/.gitattributes b/src/secp256k1/.gitattributes new file mode 100644 index 000000000..30efb2244 --- /dev/null +++ b/src/secp256k1/.gitattributes @@ -0,0 +1,2 @@ +src/precomputed_ecmult.c linguist-generated +src/precomputed_ecmult_gen.c linguist-generated diff --git a/src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml b/src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml new file mode 100644 index 000000000..ce10eb268 --- /dev/null +++ b/src/secp256k1/.github/actions/install-homebrew-valgrind/action.yml @@ -0,0 +1,33 @@ +name: "Install Valgrind" +description: "Install Homebrew's Valgrind package and cache it." +runs: + using: "composite" + steps: + - run: | + brew tap LouisBrunner/valgrind + brew fetch --HEAD LouisBrunner/valgrind/valgrind + echo "CI_HOMEBREW_CELLAR_VALGRIND=$(brew --cellar valgrind)" >> "$GITHUB_ENV" + shell: bash + + - run: | + sw_vers > valgrind_fingerprint + brew --version >> valgrind_fingerprint + git -C "$(brew --cache)/valgrind--git" rev-parse HEAD >> valgrind_fingerprint + cat valgrind_fingerprint + shell: bash + + - uses: actions/cache@v4 + id: cache + with: + path: ${{ env.CI_HOMEBREW_CELLAR_VALGRIND }} + key: ${{ github.job }}-valgrind-${{ hashFiles('valgrind_fingerprint') }} + + - if: steps.cache.outputs.cache-hit != 'true' + run: | + brew install --HEAD LouisBrunner/valgrind/valgrind + shell: bash + + - if: steps.cache.outputs.cache-hit == 'true' + run: | + brew link valgrind + shell: bash diff --git a/src/secp256k1/.github/actions/run-in-docker-action/action.yml b/src/secp256k1/.github/actions/run-in-docker-action/action.yml new file mode 100644 index 000000000..74933686a --- /dev/null +++ b/src/secp256k1/.github/actions/run-in-docker-action/action.yml @@ -0,0 +1,54 @@ +name: 'Run in Docker with environment' +description: 'Run a command in a Docker container, while passing explicitly set environment variables into the container.' +inputs: + dockerfile: + description: 'A Dockerfile that defines an image' + required: true + tag: + description: 'A tag of an image' + required: true + command: + description: 'A command to run in a container' + required: false + default: ./ci/ci.sh +runs: + using: "composite" + steps: + - uses: docker/setup-buildx-action@v3 + + - uses: docker/build-push-action@v5 + id: main_builder + continue-on-error: true + with: + context: . + file: ${{ inputs.dockerfile }} + tags: ${{ inputs.tag }} + load: true + cache-from: type=gha + + - uses: docker/build-push-action@v5 + id: retry_builder + if: steps.main_builder.outcome == 'failure' + with: + context: . + file: ${{ inputs.dockerfile }} + tags: ${{ inputs.tag }} + load: true + cache-from: type=gha + + - # Workaround for https://github.com/google/sanitizers/issues/1614 . + # The underlying issue has been fixed in clang 18.1.3. + run: sudo sysctl -w vm.mmap_rnd_bits=28 + shell: bash + + - # Tell Docker to pass environment variables in `env` into the container. + run: > + docker run \ + $(echo '${{ toJSON(env) }}' | jq -r 'keys[] | "--env \(.) "') \ + --volume ${{ github.workspace }}:${{ github.workspace }} \ + --workdir ${{ github.workspace }} \ + ${{ inputs.tag }} bash -c " + git config --global --add safe.directory ${{ github.workspace }} + ${{ inputs.command }} + " + shell: bash diff --git a/src/secp256k1/.github/workflows/ci.yml b/src/secp256k1/.github/workflows/ci.yml new file mode 100644 index 000000000..e238f3b7a --- /dev/null +++ b/src/secp256k1/.github/workflows/ci.yml @@ -0,0 +1,879 @@ +name: CI +on: + pull_request: + push: + branches: + - '**' + tags-ignore: + - '**' + +concurrency: + group: ${{ github.event_name != 'pull_request' && github.run_id || github.ref }} + cancel-in-progress: true + +env: + ### compiler options + HOST: + WRAPPER_CMD: + # Specific warnings can be disabled with -Wno-error=foo. + # -pedantic-errors is not equivalent to -Werror=pedantic and thus not implied by -Werror according to the GCC manual. + WERROR_CFLAGS: '-Werror -pedantic-errors' + MAKEFLAGS: '-j4' + BUILD: 'check' + ### secp256k1 config + ECMULTWINDOW: 15 + ECMULTGENKB: 86 + ASM: 'no' + WIDEMUL: 'auto' + WITH_VALGRIND: 'yes' + EXTRAFLAGS: + ### secp256k1 modules + EXPERIMENTAL: 'no' + ECDH: 'no' + RECOVERY: 'no' + EXTRAKEYS: 'no' + SCHNORRSIG: 'no' + ELLSWIFT: 'no' + ### test options + SECP256K1_TEST_ITERS: + BENCH: 'yes' + SECP256K1_BENCH_ITERS: 2 + CTIMETESTS: 'yes' + # Compile and run the examples. + EXAMPLES: 'yes' + +jobs: + docker_cache: + name: "Build Docker image" + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + # See: https://github.com/moby/buildkit/issues/3969. + driver-opts: | + network=host + + - name: Build container + uses: docker/build-push-action@v5 + with: + file: ./ci/linux-debian.Dockerfile + tags: linux-debian-image + cache-from: type=gha + cache-to: type=gha,mode=min + + linux_debian: + name: "x86_64: Linux (Debian stable)" + runs-on: ubuntu-latest + needs: docker_cache + + strategy: + fail-fast: false + matrix: + configuration: + - env_vars: { WIDEMUL: 'int64', RECOVERY: 'yes' } + - env_vars: { WIDEMUL: 'int64', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - env_vars: { WIDEMUL: 'int128' } + - env_vars: { WIDEMUL: 'int128_struct', ELLSWIFT: 'yes' } + - env_vars: { WIDEMUL: 'int128', RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - env_vars: { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes' } + - env_vars: { WIDEMUL: 'int128', ASM: 'x86_64', ELLSWIFT: 'yes' } + - env_vars: { RECOVERY: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes' } + - env_vars: { CTIMETESTS: 'no', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', CPPFLAGS: '-DVERIFY' } + - env_vars: { BUILD: 'distcheck', WITH_VALGRIND: 'no', CTIMETESTS: 'no', BENCH: 'no' } + - env_vars: { CPPFLAGS: '-DDETERMINISTIC' } + - env_vars: { CFLAGS: '-O0', CTIMETESTS: 'no' } + - env_vars: { CFLAGS: '-O1', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - env_vars: { ECMULTGENKB: 2, ECMULTWINDOW: 2 } + - env_vars: { ECMULTGENKB: 86, ECMULTWINDOW: 4 } + cc: + - 'gcc' + - 'clang' + - 'gcc-snapshot' + - 'clang-snapshot' + + env: + CC: ${{ matrix.cc }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + env: ${{ matrix.configuration.env_vars }} + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + i686_debian: + name: "i686: Linux (Debian stable)" + runs-on: ubuntu-latest + needs: docker_cache + + strategy: + fail-fast: false + matrix: + cc: + - 'i686-linux-gnu-gcc' + - 'clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include' + + env: + HOST: 'i686-linux-gnu' + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CC: ${{ matrix.cc }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + s390x_debian: + name: "s390x (big-endian): Linux (Debian stable, QEMU)" + runs-on: ubuntu-latest + needs: docker_cache + + env: + WRAPPER_CMD: 'qemu-s390x' + SECP256K1_TEST_ITERS: 16 + HOST: 's390x-linux-gnu' + WITH_VALGRIND: 'no' + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CTIMETESTS: 'no' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + arm32_debian: + name: "ARM32: Linux (Debian stable, QEMU)" + runs-on: ubuntu-latest + needs: docker_cache + + strategy: + fail-fast: false + matrix: + configuration: + - env_vars: {} + - env_vars: { EXPERIMENTAL: 'yes', ASM: 'arm32' } + + env: + WRAPPER_CMD: 'qemu-arm' + SECP256K1_TEST_ITERS: 16 + HOST: 'arm-linux-gnueabihf' + WITH_VALGRIND: 'no' + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CTIMETESTS: 'no' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + env: ${{ matrix.configuration.env_vars }} + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + arm64_debian: + name: "ARM64: Linux (Debian stable, QEMU)" + runs-on: ubuntu-latest + needs: docker_cache + + env: + WRAPPER_CMD: 'qemu-aarch64' + SECP256K1_TEST_ITERS: 16 + HOST: 'aarch64-linux-gnu' + WITH_VALGRIND: 'no' + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CTIMETESTS: 'no' + + strategy: + fail-fast: false + matrix: + configuration: + - env_vars: { } # gcc + - env_vars: # clang + CC: 'clang --target=aarch64-linux-gnu' + - env_vars: # clang-snapshot + CC: 'clang-snapshot --target=aarch64-linux-gnu' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + env: ${{ matrix.configuration.env_vars }} + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + ppc64le_debian: + name: "ppc64le: Linux (Debian stable, QEMU)" + runs-on: ubuntu-latest + needs: docker_cache + + env: + WRAPPER_CMD: 'qemu-ppc64le' + SECP256K1_TEST_ITERS: 16 + HOST: 'powerpc64le-linux-gnu' + WITH_VALGRIND: 'no' + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CTIMETESTS: 'no' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + valgrind_debian: + name: "Valgrind (memcheck)" + runs-on: ubuntu-latest + needs: docker_cache + + strategy: + fail-fast: false + matrix: + configuration: + - env_vars: { CC: 'clang', ASM: 'auto' } + - env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'auto' } + - env_vars: { CC: 'clang', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 } + - env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 } + + env: + # The `--error-exitcode` is required to make the test fail if valgrind found errors, + # otherwise it will return 0 (https://www.valgrind.org/docs/manual/manual-core.html). + WRAPPER_CMD: 'valgrind --error-exitcode=42' + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CTIMETESTS: 'no' + SECP256K1_TEST_ITERS: 2 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + env: ${{ matrix.configuration.env_vars }} + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + sanitizers_debian: + name: "UBSan, ASan, LSan" + runs-on: ubuntu-latest + needs: docker_cache + + strategy: + fail-fast: false + matrix: + configuration: + - env_vars: { CC: 'clang', ASM: 'auto' } + - env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'auto' } + - env_vars: { CC: 'clang', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 } + - env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 } + + env: + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CTIMETESTS: 'no' + CFLAGS: '-fsanitize=undefined,address -g' + UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1' + ASAN_OPTIONS: 'strict_string_checks=1:detect_stack_use_after_return=1:detect_leaks=1' + LSAN_OPTIONS: 'use_unaligned=1' + SECP256K1_TEST_ITERS: 32 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + env: ${{ matrix.configuration.env_vars }} + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + msan_debian: + name: "MSan" + runs-on: ubuntu-latest + needs: docker_cache + + strategy: + fail-fast: false + matrix: + configuration: + - env_vars: + CTIMETESTS: 'yes' + CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -g' + - env_vars: + ECMULTGENKB: 2 + ECMULTWINDOW: 2 + CTIMETESTS: 'yes' + CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -g -O3' + - env_vars: + # -fsanitize-memory-param-retval is clang's default, but our build system disables it + # when ctime_tests when enabled. + CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -fsanitize-memory-param-retval -g' + CTIMETESTS: 'no' + + env: + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CC: 'clang' + SECP256K1_TEST_ITERS: 32 + ASM: 'no' + WITH_VALGRIND: 'no' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + env: ${{ matrix.configuration.env_vars }} + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + mingw_debian: + name: ${{ matrix.configuration.job_name }} + runs-on: ubuntu-latest + needs: docker_cache + + env: + WRAPPER_CMD: 'wine' + WITH_VALGRIND: 'no' + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + CTIMETESTS: 'no' + + strategy: + fail-fast: false + matrix: + configuration: + - job_name: 'x86_64 (mingw32-w64): Windows (Debian stable, Wine)' + env_vars: + HOST: 'x86_64-w64-mingw32' + - job_name: 'i686 (mingw32-w64): Windows (Debian stable, Wine)' + env_vars: + HOST: 'i686-w64-mingw32' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + env: ${{ matrix.configuration.env_vars }} + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + x86_64-macos-native: + name: "x86_64: macOS Monterey, Valgrind" + # See: https://github.com/actions/runner-images#available-images. + runs-on: macos-12 + + env: + CC: 'clang' + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 + + strategy: + fail-fast: false + matrix: + env_vars: + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128_struct', ECMULTGENKB: 2, ECMULTWINDOW: 4 } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc', WRAPPER_CMD: 'valgrind --error-exitcode=42', SECP256K1_TEST_ITERS: 2 } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY', CTIMETESTS: 'no' } + - BUILD: 'distcheck' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Homebrew packages + run: | + brew install automake libtool gcc + ln -s $(brew --prefix gcc)/bin/gcc-?? /usr/local/bin/gcc + + - name: Install and cache Valgrind + uses: ./.github/actions/install-homebrew-valgrind + + - name: CI script + env: ${{ matrix.env_vars }} + run: ./ci/ci.sh + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + arm64-macos-native: + name: "ARM64: macOS Sonoma" + # See: https://github.com/actions/runner-images#available-images. + runs-on: macos-14 + + env: + CC: 'clang' + HOMEBREW_NO_AUTO_UPDATE: 1 + HOMEBREW_NO_INSTALL_CLEANUP: 1 + WITH_VALGRIND: 'no' + CTIMETESTS: 'no' + + strategy: + fail-fast: false + matrix: + env_vars: + - { WIDEMUL: 'int64', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128_struct', ECMULTGENPRECISION: 2, ECMULTWINDOW: 4 } + - { WIDEMUL: 'int128', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CC: 'gcc' } + - { WIDEMUL: 'int128', RECOVERY: 'yes', ECDH: 'yes', EXTRAKEYS: 'yes', SCHNORRSIG: 'yes', ELLSWIFT: 'yes', CPPFLAGS: '-DVERIFY' } + - BUILD: 'distcheck' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Homebrew packages + run: | + brew install automake libtool gcc + ln -s $(brew --prefix gcc)/bin/gcc-?? /usr/local/bin/gcc + + - name: CI script + env: ${{ matrix.env_vars }} + run: ./ci/ci.sh + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + win64-native: + name: ${{ matrix.configuration.job_name }} + # See: https://github.com/actions/runner-images#available-images. + runs-on: windows-2022 + + strategy: + fail-fast: false + matrix: + configuration: + - job_name: 'x64 (MSVC): Windows (VS 2022, shared)' + cmake_options: '-A x64 -DBUILD_SHARED_LIBS=ON' + - job_name: 'x64 (MSVC): Windows (VS 2022, static)' + cmake_options: '-A x64 -DBUILD_SHARED_LIBS=OFF' + - job_name: 'x64 (MSVC): Windows (VS 2022, int128_struct)' + cmake_options: '-A x64 -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct' + - job_name: 'x64 (MSVC): Windows (VS 2022, int128_struct with __(u)mulh)' + cmake_options: '-A x64 -DSECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int128_struct' + cpp_flags: '/DSECP256K1_MSVC_MULH_TEST_OVERRIDE' + - job_name: 'x86 (MSVC): Windows (VS 2022)' + cmake_options: '-A Win32' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Generate buildsystem + run: cmake -E env CFLAGS="/WX ${{ matrix.configuration.cpp_flags }}" cmake -B build -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DSECP256K1_BUILD_EXAMPLES=ON ${{ matrix.configuration.cmake_options }} + + - name: Build + run: cmake --build build --config RelWithDebInfo -- /p:UseMultiToolTask=true /maxCpuCount + + - name: Binaries info + # Use the bash shell included with Git for Windows. + shell: bash + run: | + cd build/src/RelWithDebInfo && file *tests.exe bench*.exe libsecp256k1-*.dll || true + + - name: Check + run: | + ctest -C RelWithDebInfo --test-dir build -j ([int]$env:NUMBER_OF_PROCESSORS + 1) + build\src\RelWithDebInfo\bench_ecmult.exe + build\src\RelWithDebInfo\bench_internal.exe + build\src\RelWithDebInfo\bench.exe + + win64-native-headers: + name: "x64 (MSVC): C++ (public headers)" + # See: https://github.com/actions/runner-images#available-images. + runs-on: windows-2022 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Add cl.exe to PATH + uses: ilammy/msvc-dev-cmd@v1 + + - name: C++ (public headers) + run: | + cl.exe -c -WX -TP include/*.h + + cxx_fpermissive_debian: + name: "C++ -fpermissive (entire project)" + runs-on: ubuntu-latest + needs: docker_cache + + env: + CC: 'g++' + CFLAGS: '-fpermissive -g' + CPPFLAGS: '-DSECP256K1_CPLUSPLUS_TEST_OVERRIDE' + WERROR_CFLAGS: + ECDH: 'yes' + RECOVERY: 'yes' + EXTRAKEYS: 'yes' + SCHNORRSIG: 'yes' + ELLSWIFT: 'yes' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + + - run: cat tests.log || true + if: ${{ always() }} + - run: cat noverify_tests.log || true + if: ${{ always() }} + - run: cat exhaustive_tests.log || true + if: ${{ always() }} + - run: cat ctime_tests.log || true + if: ${{ always() }} + - run: cat bench.log || true + if: ${{ always() }} + - run: cat config.log || true + if: ${{ always() }} + - run: cat test_env.log || true + if: ${{ always() }} + - name: CI env + run: env + if: ${{ always() }} + + cxx_headers_debian: + name: "C++ (public headers)" + runs-on: ubuntu-latest + needs: docker_cache + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + uses: ./.github/actions/run-in-docker-action + with: + dockerfile: ./ci/linux-debian.Dockerfile + tag: linux-debian-image + command: | + g++ -Werror include/*.h + clang -Werror -x c++-header include/*.h + + sage: + name: "SageMath prover" + runs-on: ubuntu-latest + container: + image: sagemath/sagemath:latest + options: --user root + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: CI script + run: | + cd sage + sage prove_group_implementations.sage + + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - run: ./autogen.sh && ./configure --enable-dev-mode && make distcheck + + - name: Check installation with Autotools + env: + CI_INSTALL: ${{ runner.temp }}/${{ github.run_id }}${{ github.action }}/install + run: | + ./autogen.sh && ./configure --prefix=${{ env.CI_INSTALL }} && make clean && make install && ls -RlAh ${{ env.CI_INSTALL }} + gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=${{ env.CI_INSTALL }}/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"${{ env.CI_INSTALL }}/lib" && ./ecdsa + + - name: Check installation with CMake + env: + CI_BUILD: ${{ runner.temp }}/${{ github.run_id }}${{ github.action }}/build + CI_INSTALL: ${{ runner.temp }}/${{ github.run_id }}${{ github.action }}/install + run: | + cmake -B ${{ env.CI_BUILD }} -DCMAKE_INSTALL_PREFIX=${{ env.CI_INSTALL }} && cmake --build ${{ env.CI_BUILD }} && cmake --install ${{ env.CI_BUILD }} && ls -RlAh ${{ env.CI_INSTALL }} + gcc -o ecdsa examples/ecdsa.c -I ${{ env.CI_INSTALL }}/include -L ${{ env.CI_INSTALL }}/lib*/ -l secp256k1 -Wl,-rpath,"${{ env.CI_INSTALL }}/lib",-rpath,"${{ env.CI_INSTALL }}/lib64" && ./ecdsa diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore index 87fea161b..18e3259f5 100644 --- a/src/secp256k1/.gitignore +++ b/src/secp256k1/.gitignore @@ -1,17 +1,23 @@ -bench_inv -bench_ecdh -bench_sign -bench_verify -bench_schnorr_verify -bench_recover +bench +bench_ecmult bench_internal +noverify_tests tests exhaustive_tests -gen_context +precompute_ecmult_gen +precompute_ecmult +ctime_tests +ecdh_example +ecdsa_example +schnorr_example +ellswift_example *.exe *.so *.a -!.gitignore +*.csv +*.log +*.trs +*.sage.py Makefile configure @@ -21,6 +27,7 @@ aclocal.m4 autom4te.cache/ config.log config.status +conftest* *.tar.gz *.la libtool @@ -29,9 +36,15 @@ libtool *.lo *.o *~ -src/libsecp256k1-config.h -src/libsecp256k1-config.h.in -src/ecmult_static_context.h + +coverage/ +coverage.html +coverage.*.html +*.gcda +*.gcno +*.gcov + +build-aux/ar-lib build-aux/config.guess build-aux/config.sub build-aux/depcomp @@ -45,5 +58,9 @@ build-aux/m4/ltversion.m4 build-aux/missing build-aux/compile build-aux/test-driver -src/stamp-h1 libsecp256k1.pc + +### CMake +/CMakeUserPresets.json +# Default CMake build directory. +/build diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml deleted file mode 100644 index 243952924..000000000 --- a/src/secp256k1/.travis.yml +++ /dev/null @@ -1,69 +0,0 @@ -language: c -sudo: false -addons: - apt: - packages: libgmp-dev -compiler: - - clang - - gcc -cache: - directories: - - src/java/guava/ -env: - global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no - - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar - matrix: - - SCALAR=32bit RECOVERY=yes - - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes - - SCALAR=64bit - - FIELD=64bit RECOVERY=yes - - FIELD=64bit ENDOMORPHISM=yes - - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes - - FIELD=64bit ASM=x86_64 - - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - - FIELD=32bit ENDOMORPHISM=yes - - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes - - BIGNUM=no STATICPRECOMPUTATION=no - - BUILD=distcheck - - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC - - EXTRAFLAGS=CFLAGS=-O0 - - BUILD=check-java ECDH=yes EXPERIMENTAL=yes -matrix: - fast_finish: true - include: - - compiler: clang - env: HOST=i686-linux-gnu ENDOMORPHISM=yes - addons: - apt: - packages: - - gcc-multilib - - libgmp-dev:i386 - - compiler: clang - env: HOST=i686-linux-gnu - addons: - apt: - packages: - - gcc-multilib - - compiler: gcc - env: HOST=i686-linux-gnu ENDOMORPHISM=yes - addons: - apt: - packages: - - gcc-multilib - - compiler: gcc - env: HOST=i686-linux-gnu - addons: - apt: - packages: - - gcc-multilib - - libgmp-dev:i386 -before_install: mkdir -p `dirname $GUAVA_JAR` -install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi -before_script: ./autogen.sh -script: - - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD -os: linux diff --git a/src/secp256k1/CHANGELOG.md b/src/secp256k1/CHANGELOG.md new file mode 100644 index 000000000..0868e7548 --- /dev/null +++ b/src/secp256k1/CHANGELOG.md @@ -0,0 +1,152 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.5.1] - 2024-08-01 + +#### Added + - Added usage example for an ElligatorSwift key exchange. + +#### Changed + - The default size of the precomputed table for signing was changed from 22 KiB to 86 KiB. The size can be changed with the configure option `--ecmult-gen-kb` (`SECP256K1_ECMULT_GEN_KB` for CMake). + - "auto" is no longer an accepted value for the `--with-ecmult-window` and `--with-ecmult-gen-kb` configure options (this also applies to `SECP256K1_ECMULT_WINDOW_SIZE` and `SECP256K1_ECMULT_GEN_KB` in CMake). To achieve the same configuration as previously provided by the "auto" value, omit setting the configure option explicitly. + +#### Fixed + - Fixed compilation when the extrakeys module is disabled. + +#### ABI Compatibility +The ABI is backward compatible with versions 0.5.0, 0.4.x and 0.3.x. + +## [0.5.0] - 2024-05-06 + +#### Added + - New function `secp256k1_ec_pubkey_sort` that sorts public keys using lexicographic (of compressed serialization) order. + +#### Changed + - The implementation of the point multiplication algorithm used for signing and public key generation was changed, resulting in improved performance for those operations. + - The related configure option `--ecmult-gen-precision` was replaced with `--ecmult-gen-kb` (`SECP256K1_ECMULT_GEN_KB` for CMake). + - This changes the supported precomputed table sizes for these operations. The new supported sizes are 2 KiB, 22 KiB, or 86 KiB (while the old supported sizes were 32 KiB, 64 KiB, or 512 KiB). + +#### ABI Compatibility +The ABI is backward compatible with versions 0.4.x and 0.3.x. + +## [0.4.1] - 2023-12-21 + +#### Changed + - The point multiplication algorithm used for ECDH operations (module `ecdh`) was replaced with a slightly faster one. + - Optional handwritten x86_64 assembly for field operations was removed because modern C compilers are able to output more efficient assembly. This change results in a significant speedup of some library functions when handwritten x86_64 assembly is enabled (`--with-asm=x86_64` in GNU Autotools, `-DSECP256K1_ASM=x86_64` in CMake), which is the default on x86_64. Benchmarks with GCC 10.5.0 show a 10% speedup for `secp256k1_ecdsa_verify` and `secp256k1_schnorrsig_verify`. + +#### ABI Compatibility +The ABI is backward compatible with versions 0.4.0 and 0.3.x. + +## [0.4.0] - 2023-09-04 + +#### Added + - New module `ellswift` implements ElligatorSwift encoding for public keys and x-only Diffie-Hellman key exchange for them. + ElligatorSwift permits representing secp256k1 public keys as 64-byte arrays which cannot be distinguished from uniformly random. See: + - Header file `include/secp256k1_ellswift.h` which defines the new API. + - Document `doc/ellswift.md` which explains the mathematical background of the scheme. + - The [paper](https://eprint.iacr.org/2022/759) on which the scheme is based. + - We now test the library with unreleased development snapshots of GCC and Clang. This gives us an early chance to catch miscompilations and constant-time issues introduced by the compiler (such as those that led to the previous two releases). + +#### Fixed + - Fixed symbol visibility in Windows DLL builds, where three internal library symbols were wrongly exported. + +#### Changed + - When consuming libsecp256k1 as a static library on Windows, the user must now define the `SECP256K1_STATIC` macro before including `secp256k1.h`. + +#### ABI Compatibility +This release is backward compatible with the ABI of 0.3.0, 0.3.1, and 0.3.2. Symbol visibility is now believed to be handled properly on supported platforms and is now considered to be part of the ABI. Please report any improperly exported symbols as a bug. + +## [0.3.2] - 2023-05-13 +We strongly recommend updating to 0.3.2 if you use or plan to use GCC >=13 to compile libsecp256k1. When in doubt, check the GCC version using `gcc -v`. + +#### Security + - Module `ecdh`: Fix "constant-timeness" issue with GCC 13.1 (and potentially future versions of GCC) that could leave applications using libsecp256k1's ECDH module vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow during ECDH computations when libsecp256k1 is compiled with GCC 13.1. + +#### Fixed + - Fixed an old bug that permitted compilers to potentially output bad assembly code on x86_64. In theory, it could lead to a crash or a read of unrelated memory, but this has never been observed on any compilers so far. + +#### Changed + - Various improvements and changes to CMake builds. CMake builds remain experimental. + - Made API versioning consistent with GNU Autotools builds. + - Switched to `BUILD_SHARED_LIBS` variable for controlling whether to build a static or a shared library. + - Added `SECP256K1_INSTALL` variable for the controlling whether to install the build artefacts. + - Renamed asm build option `arm` to `arm32`. Use `--with-asm=arm32` instead of `--with-asm=arm` (GNU Autotools), and `-DSECP256K1_ASM=arm32` instead of `-DSECP256K1_ASM=arm` (CMake). + +#### ABI Compatibility +The ABI is compatible with versions 0.3.0 and 0.3.1. + +## [0.3.1] - 2023-04-10 +We strongly recommend updating to 0.3.1 if you use or plan to use Clang >=14 to compile libsecp256k1, e.g., Xcode >=14 on macOS has Clang >=14. When in doubt, check the Clang version using `clang -v`. + +#### Security + - Fix "constant-timeness" issue with Clang >=14 that could leave applications using libsecp256k1 vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow and secret-dependent memory accesses in conditional moves of memory objects when libsecp256k1 is compiled with Clang >=14. + +#### Added + - Added tests against [Project Wycheproof's](https://github.com/google/wycheproof/) set of ECDSA test vectors (Bitcoin "low-S" variant), a fixed set of test cases designed to trigger various edge cases. + +#### Changed + - Increased minimum required CMake version to 3.13. CMake builds remain experimental. + +#### ABI Compatibility +The ABI is compatible with version 0.3.0. + +## [0.3.0] - 2023-03-08 + +#### Added + - Added experimental support for CMake builds. Traditional GNU Autotools builds (`./configure` and `make`) remain fully supported. + - Usage examples: Added a recommended method for securely clearing sensitive data, e.g., secret keys, from memory. + - Tests: Added a new test binary `noverify_tests`. This binary runs the tests without some additional checks present in the ordinary `tests` binary and is thereby closer to production binaries. The `noverify_tests` binary is automatically run as part of the `make check` target. + +#### Fixed + - Fixed declarations of API variables for MSVC (`__declspec(dllimport)`). This fixes MSVC builds of programs which link against a libsecp256k1 DLL dynamically and use API variables (and not only API functions). Unfortunately, the MSVC linker now will emit warning `LNK4217` when trying to link against libsecp256k1 statically. Pass `/ignore:4217` to the linker to suppress this warning. + +#### Changed + - Forbade cloning or destroying `secp256k1_context_static`. Create a new context instead of cloning the static context. (If this change breaks your code, your code is probably wrong.) + - Forbade randomizing (copies of) `secp256k1_context_static`. Randomizing a copy of `secp256k1_context_static` did not have any effect and did not provide defense-in-depth protection against side-channel attacks. Create a new context if you want to benefit from randomization. + +#### Removed + - Removed the configuration header `src/libsecp256k1-config.h`. We recommend passing flags to `./configure` or `cmake` to set configuration options (see `./configure --help` or `cmake -LH`). If you cannot or do not want to use one of the supported build systems, pass configuration flags such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG` manually to the compiler (see the file `configure.ac` for supported flags). + +#### ABI Compatibility +Due to changes in the API regarding `secp256k1_context_static` described above, the ABI is *not* compatible with previous versions. + +## [0.2.0] - 2022-12-12 + +#### Added + - Added usage examples for common use cases in a new `examples/` directory. + - Added `secp256k1_selftest`, to be used in conjunction with `secp256k1_context_static`. + - Added support for 128-bit wide multiplication on MSVC for x86_64 and arm64, giving roughly a 20% speedup on those platforms. + +#### Changed + - Enabled modules `schnorrsig`, `extrakeys` and `ecdh` by default in `./configure`. + - The `secp256k1_nonce_function_rfc6979` nonce function, used by default by `secp256k1_ecdsa_sign`, now reduces the message hash modulo the group order to match the specification. This only affects improper use of ECDSA signing API. + +#### Deprecated + - Deprecated context flags `SECP256K1_CONTEXT_VERIFY` and `SECP256K1_CONTEXT_SIGN`. Use `SECP256K1_CONTEXT_NONE` instead. + - Renamed `secp256k1_context_no_precomp` to `secp256k1_context_static`. + - Module `schnorrsig`: renamed `secp256k1_schnorrsig_sign` to `secp256k1_schnorrsig_sign32`. + +#### ABI Compatibility +Since this is the first release, we do not compare application binary interfaces. +However, there are earlier unreleased versions of libsecp256k1 that are *not* ABI compatible with this version. + +## [0.1.0] - 2013-03-05 to 2021-12-25 + +This version was in fact never released. +The number was given by the build system since the introduction of autotools in Jan 2014 (ea0fe5a5bf0c04f9cc955b2966b614f5f378c6f6). +Therefore, this version number does not uniquely identify a set of source files. + +[0.5.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.5.0...v0.5.1 +[0.5.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.4.1...v0.5.0 +[0.4.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.2...v0.4.0 +[0.3.2]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/bitcoin-core/secp256k1/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/bitcoin-core/secp256k1/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/bitcoin-core/secp256k1/compare/423b6d19d373f1224fd671a982584d7e7900bc93..v0.2.0 +[0.1.0]: https://github.com/bitcoin-core/secp256k1/commit/423b6d19d373f1224fd671a982584d7e7900bc93 diff --git a/src/secp256k1/CMakeLists.txt b/src/secp256k1/CMakeLists.txt new file mode 100644 index 000000000..7a8768605 --- /dev/null +++ b/src/secp256k1/CMakeLists.txt @@ -0,0 +1,373 @@ +cmake_minimum_required(VERSION 3.16) + +project(libsecp256k1 + # The package (a.k.a. release) version is based on semantic versioning 2.0.0 of + # the API. All changes in experimental modules are treated as + # backwards-compatible and therefore at most increase the minor version. + VERSION 0.5.1 + DESCRIPTION "Optimized C library for ECDSA signatures and secret/public key operations on curve secp256k1." + HOMEPAGE_URL "https://github.com/bitcoin-core/secp256k1" + LANGUAGES C +) + +if(CMAKE_VERSION VERSION_LESS 3.21) + # Emulates CMake 3.21+ behavior. + if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(PROJECT_IS_TOP_LEVEL ON) + set(${PROJECT_NAME}_IS_TOP_LEVEL ON) + else() + set(PROJECT_IS_TOP_LEVEL OFF) + set(${PROJECT_NAME}_IS_TOP_LEVEL OFF) + endif() +endif() + +# The library version is based on libtool versioning of the ABI. The set of +# rules for updating the version can be found here: +# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +# All changes in experimental modules are treated as if they don't affect the +# interface and therefore only increase the revision. +set(${PROJECT_NAME}_LIB_VERSION_CURRENT 4) +set(${PROJECT_NAME}_LIB_VERSION_REVISION 1) +set(${PROJECT_NAME}_LIB_VERSION_AGE 2) + +set(CMAKE_C_STANDARD 90) +set(CMAKE_C_EXTENSIONS OFF) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) + +option(BUILD_SHARED_LIBS "Build shared libraries." ON) +option(SECP256K1_DISABLE_SHARED "Disable shared library. Overrides BUILD_SHARED_LIBS." OFF) +if(SECP256K1_DISABLE_SHARED) + set(BUILD_SHARED_LIBS OFF) +endif() + +option(SECP256K1_INSTALL "Enable installation." ${PROJECT_IS_TOP_LEVEL}) + +## Modules + +# We declare all options before processing them, to make sure we can express +# dependendencies while processing. +option(SECP256K1_ENABLE_MODULE_ECDH "Enable ECDH module." ON) +option(SECP256K1_ENABLE_MODULE_RECOVERY "Enable ECDSA pubkey recovery module." OFF) +option(SECP256K1_ENABLE_MODULE_EXTRAKEYS "Enable extrakeys module." ON) +option(SECP256K1_ENABLE_MODULE_SCHNORRSIG "Enable schnorrsig module." ON) +option(SECP256K1_ENABLE_MODULE_ELLSWIFT "Enable ElligatorSwift module." ON) + +# Processing must be done in a topological sorting of the dependency graph +# (dependent module first). +if(SECP256K1_ENABLE_MODULE_ELLSWIFT) + add_compile_definitions(ENABLE_MODULE_ELLSWIFT=1) +endif() + +if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) + if(DEFINED SECP256K1_ENABLE_MODULE_EXTRAKEYS AND NOT SECP256K1_ENABLE_MODULE_EXTRAKEYS) + message(FATAL_ERROR "Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.") + endif() + set(SECP256K1_ENABLE_MODULE_EXTRAKEYS ON) + add_compile_definitions(ENABLE_MODULE_SCHNORRSIG=1) +endif() + +if(SECP256K1_ENABLE_MODULE_EXTRAKEYS) + add_compile_definitions(ENABLE_MODULE_EXTRAKEYS=1) +endif() + +if(SECP256K1_ENABLE_MODULE_RECOVERY) + add_compile_definitions(ENABLE_MODULE_RECOVERY=1) +endif() + +if(SECP256K1_ENABLE_MODULE_ECDH) + add_compile_definitions(ENABLE_MODULE_ECDH=1) +endif() + +option(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS "Enable external default callback functions." OFF) +if(SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS) + add_compile_definitions(USE_EXTERNAL_DEFAULT_CALLBACKS=1) +endif() + +set(SECP256K1_ECMULT_WINDOW_SIZE 15 CACHE STRING "Window size for ecmult precomputation for verification, specified as integer in range [2..24]. The default value is a reasonable setting for desktop machines (currently 15). [default=15]") +set_property(CACHE SECP256K1_ECMULT_WINDOW_SIZE PROPERTY STRINGS 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24) +include(CheckStringOptionValue) +check_string_option_value(SECP256K1_ECMULT_WINDOW_SIZE) +add_compile_definitions(ECMULT_WINDOW_SIZE=${SECP256K1_ECMULT_WINDOW_SIZE}) + +set(SECP256K1_ECMULT_GEN_KB 86 CACHE STRING "The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms). Larger values result in possibly better signing or key generation performance at the cost of a larger table. Valid choices are 2, 22, 86. The default value is a reasonable setting for desktop machines (currently 86). [default=86]") +set_property(CACHE SECP256K1_ECMULT_GEN_KB PROPERTY STRINGS 2 22 86) +check_string_option_value(SECP256K1_ECMULT_GEN_KB) +if(SECP256K1_ECMULT_GEN_KB EQUAL 2) + add_compile_definitions(COMB_BLOCKS=2) + add_compile_definitions(COMB_TEETH=5) +elseif(SECP256K1_ECMULT_GEN_KB EQUAL 22) + add_compile_definitions(COMB_BLOCKS=11) + add_compile_definitions(COMB_TEETH=6) +elseif(SECP256K1_ECMULT_GEN_KB EQUAL 86) + add_compile_definitions(COMB_BLOCKS=43) + add_compile_definitions(COMB_TEETH=6) +endif() + +set(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY "OFF" CACHE STRING "Test-only override of the (autodetected by the C code) \"widemul\" setting. Legal values are: \"OFF\", \"int128_struct\", \"int128\" or \"int64\". [default=OFF]") +set_property(CACHE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY PROPERTY STRINGS "OFF" "int128_struct" "int128" "int64") +check_string_option_value(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) +if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) + string(TOUPPER "${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}" widemul_upper_value) + add_compile_definitions(USE_FORCE_WIDEMUL_${widemul_upper_value}=1) +endif() +mark_as_advanced(FORCE SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) + +set(SECP256K1_ASM "AUTO" CACHE STRING "Assembly to use: \"AUTO\", \"OFF\", \"x86_64\" or \"arm32\" (experimental). [default=AUTO]") +set_property(CACHE SECP256K1_ASM PROPERTY STRINGS "AUTO" "OFF" "x86_64" "arm32") +check_string_option_value(SECP256K1_ASM) +if(SECP256K1_ASM STREQUAL "arm32") + enable_language(ASM) + include(CheckArm32Assembly) + check_arm32_assembly() + if(HAVE_ARM32_ASM) + add_compile_definitions(USE_EXTERNAL_ASM=1) + else() + message(FATAL_ERROR "ARM32 assembly requested but not available.") + endif() +elseif(SECP256K1_ASM) + include(CheckX86_64Assembly) + check_x86_64_assembly() + if(HAVE_X86_64_ASM) + set(SECP256K1_ASM "x86_64") + add_compile_definitions(USE_ASM_X86_64=1) + elseif(SECP256K1_ASM STREQUAL "AUTO") + set(SECP256K1_ASM "OFF") + else() + message(FATAL_ERROR "x86_64 assembly requested but not available.") + endif() +endif() + +option(SECP256K1_EXPERIMENTAL "Allow experimental configuration options." OFF) +if(NOT SECP256K1_EXPERIMENTAL) + if(SECP256K1_ASM STREQUAL "arm32") + message(FATAL_ERROR "ARM32 assembly is experimental. Use -DSECP256K1_EXPERIMENTAL=ON to allow.") + endif() +endif() + +set(SECP256K1_VALGRIND "AUTO" CACHE STRING "Build with extra checks for running inside Valgrind. [default=AUTO]") +set_property(CACHE SECP256K1_VALGRIND PROPERTY STRINGS "AUTO" "OFF" "ON") +check_string_option_value(SECP256K1_VALGRIND) +if(SECP256K1_VALGRIND) + find_package(Valgrind MODULE) + if(Valgrind_FOUND) + set(SECP256K1_VALGRIND ON) + include_directories(${Valgrind_INCLUDE_DIR}) + add_compile_definitions(VALGRIND) + elseif(SECP256K1_VALGRIND STREQUAL "AUTO") + set(SECP256K1_VALGRIND OFF) + else() + message(FATAL_ERROR "Valgrind support requested but valgrind/memcheck.h header not available.") + endif() +endif() + +option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." ON) +option(SECP256K1_BUILD_TESTS "Build tests." ON) +option(SECP256K1_BUILD_EXHAUSTIVE_TESTS "Build exhaustive tests." ON) +option(SECP256K1_BUILD_CTIME_TESTS "Build constant-time tests." ${SECP256K1_VALGRIND}) +option(SECP256K1_BUILD_EXAMPLES "Build examples." OFF) + +# Redefine configuration flags. +# We leave assertions on, because they are only used in the examples, and we want them always on there. +if(MSVC) + string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") + string(REGEX REPLACE "/DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}") +else() + string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") + string(REGEX REPLACE "-DNDEBUG[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}") + # Prefer -O2 optimization level. (-O3 is CMake's default for Release for many compilers.) + string(REGEX REPLACE "-O3( |$)" "-O2\\1" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") +endif() + +# Define custom "Coverage" build type. +set(CMAKE_C_FLAGS_COVERAGE "${CMAKE_C_FLAGS_RELWITHDEBINFO} -O0 -DCOVERAGE=1 --coverage" CACHE STRING + "Flags used by the C compiler during \"Coverage\" builds." + FORCE +) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING + "Flags used for linking binaries during \"Coverage\" builds." + FORCE +) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} --coverage" CACHE STRING + "Flags used by the shared libraries linker during \"Coverage\" builds." + FORCE +) +mark_as_advanced( + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE +) + +if(PROJECT_IS_TOP_LEVEL) + get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + set(default_build_type "RelWithDebInfo") + if(is_multi_config) + set(CMAKE_CONFIGURATION_TYPES "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" CACHE STRING + "Supported configuration types." + FORCE + ) + else() + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY + STRINGS "${default_build_type}" "Release" "Debug" "MinSizeRel" "Coverage" + ) + if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to \"${default_build_type}\" as none was specified") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING + "Choose the type of build." + FORCE + ) + endif() + endif() +endif() + +include(TryAppendCFlags) +if(MSVC) + # Keep the following commands ordered lexicographically. + try_append_c_flags(/W3) # Production quality warning level. + try_append_c_flags(/wd4146) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned". + try_append_c_flags(/wd4244) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data". + try_append_c_flags(/wd4267) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data". + # Eliminate deprecation warnings for the older, less secure functions. + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) +else() + # Keep the following commands ordered lexicographically. + try_append_c_flags(-pedantic) + try_append_c_flags(-Wall) # GCC >= 2.95 and probably many other compilers. + try_append_c_flags(-Wcast-align) # GCC >= 2.95. + try_append_c_flags(-Wcast-align=strict) # GCC >= 8.0. + try_append_c_flags(-Wconditional-uninitialized) # Clang >= 3.0 only. + try_append_c_flags(-Wextra) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions. + try_append_c_flags(-Wnested-externs) + try_append_c_flags(-Wno-long-long) # GCC >= 3.0, -Wlong-long is implied by -pedantic. + try_append_c_flags(-Wno-overlength-strings) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic. + try_append_c_flags(-Wno-unused-function) # GCC >= 3.0, -Wunused-function is implied by -Wall. + try_append_c_flags(-Wreserved-identifier) # Clang >= 13.0 only. + try_append_c_flags(-Wshadow) + try_append_c_flags(-Wstrict-prototypes) + try_append_c_flags(-Wundef) +endif() + +set(CMAKE_C_VISIBILITY_PRESET hidden) + +set(print_msan_notice) +if(SECP256K1_BUILD_CTIME_TESTS) + include(CheckMemorySanitizer) + check_memory_sanitizer(msan_enabled) + if(msan_enabled) + try_append_c_flags(-fno-sanitize-memory-param-retval) + set(print_msan_notice YES) + endif() + unset(msan_enabled) +endif() + +include(CTest) +# We do not use CTest's BUILD_TESTING because a single toggle for all tests is too coarse for our needs. +mark_as_advanced(BUILD_TESTING) +if(SECP256K1_BUILD_BENCHMARK OR SECP256K1_BUILD_TESTS OR SECP256K1_BUILD_EXHAUSTIVE_TESTS OR SECP256K1_BUILD_CTIME_TESTS OR SECP256K1_BUILD_EXAMPLES) + enable_testing() +endif() + +set(SECP256K1_APPEND_CFLAGS "" CACHE STRING "Compiler flags that are appended to the command line after all other flags added by the build system. This variable is intended for debugging and special builds.") +if(SECP256K1_APPEND_CFLAGS) + # Appending to this low-level rule variable is the only way to + # guarantee that the flags appear at the end of the command line. + string(APPEND CMAKE_C_COMPILE_OBJECT " ${SECP256K1_APPEND_CFLAGS}") +endif() + +add_subdirectory(src) +if(SECP256K1_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +message("\n") +message("secp256k1 configure summary") +message("===========================") +message("Build artifacts:") +if(BUILD_SHARED_LIBS) + set(library_type "Shared") +else() + set(library_type "Static") +endif() + +message(" library type ........................ ${library_type}") +message("Optional modules:") +message(" ECDH ................................ ${SECP256K1_ENABLE_MODULE_ECDH}") +message(" ECDSA pubkey recovery ............... ${SECP256K1_ENABLE_MODULE_RECOVERY}") +message(" extrakeys ........................... ${SECP256K1_ENABLE_MODULE_EXTRAKEYS}") +message(" schnorrsig .......................... ${SECP256K1_ENABLE_MODULE_SCHNORRSIG}") +message(" ElligatorSwift ...................... ${SECP256K1_ENABLE_MODULE_ELLSWIFT}") +message("Parameters:") +message(" ecmult window size .................. ${SECP256K1_ECMULT_WINDOW_SIZE}") +message(" ecmult gen table size ............... ${SECP256K1_ECMULT_GEN_KB} KiB") +message("Optional features:") +message(" assembly ............................ ${SECP256K1_ASM}") +message(" external callbacks .................. ${SECP256K1_USE_EXTERNAL_DEFAULT_CALLBACKS}") +if(SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY) + message(" wide multiplication (test-only) ..... ${SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY}") +endif() +message("Optional binaries:") +message(" benchmark ........................... ${SECP256K1_BUILD_BENCHMARK}") +message(" noverify_tests ...................... ${SECP256K1_BUILD_TESTS}") +set(tests_status "${SECP256K1_BUILD_TESTS}") +if(CMAKE_BUILD_TYPE STREQUAL "Coverage") + set(tests_status OFF) +endif() +message(" tests ............................... ${tests_status}") +message(" exhaustive tests .................... ${SECP256K1_BUILD_EXHAUSTIVE_TESTS}") +message(" ctime_tests ......................... ${SECP256K1_BUILD_CTIME_TESTS}") +message(" examples ............................ ${SECP256K1_BUILD_EXAMPLES}") +message("") +if(CMAKE_CROSSCOMPILING) + set(cross_status "TRUE, for ${CMAKE_SYSTEM_NAME}, ${CMAKE_SYSTEM_PROCESSOR}") +else() + set(cross_status "FALSE") +endif() +message("Cross compiling ....................... ${cross_status}") +message("Valgrind .............................. ${SECP256K1_VALGRIND}") +get_directory_property(definitions COMPILE_DEFINITIONS) +string(REPLACE ";" " " definitions "${definitions}") +message("Preprocessor defined macros ........... ${definitions}") +message("C compiler ............................ ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}, ${CMAKE_C_COMPILER}") +message("CFLAGS ................................ ${CMAKE_C_FLAGS}") +get_directory_property(compile_options COMPILE_OPTIONS) +string(REPLACE ";" " " compile_options "${compile_options}") +message("Compile options ....................... " ${compile_options}) +if(NOT is_multi_config) + message("Build type:") + message(" - CMAKE_BUILD_TYPE ................... ${CMAKE_BUILD_TYPE}") + string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type) + message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_${build_type}}") + message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_${build_type}}") + message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_${build_type}}") +else() + message("Supported configurations .............. ${CMAKE_CONFIGURATION_TYPES}") + message("RelWithDebInfo configuration:") + message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_RELWITHDEBINFO}") + message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}") + message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}") + message("Debug configuration:") + message(" - CFLAGS ............................. ${CMAKE_C_FLAGS_DEBUG}") + message(" - LDFLAGS for executables ............ ${CMAKE_EXE_LINKER_FLAGS_DEBUG}") + message(" - LDFLAGS for shared libraries ....... ${CMAKE_SHARED_LINKER_FLAGS_DEBUG}") +endif() +if(SECP256K1_APPEND_CFLAGS) + message("SECP256K1_APPEND_CFLAGS ............... ${SECP256K1_APPEND_CFLAGS}") +endif() +message("") +if(print_msan_notice) + message( + "Note:\n" + " MemorySanitizer detected, tried to add -fno-sanitize-memory-param-retval to compile options\n" + " to avoid false positives in ctime_tests. Pass -DSECP256K1_BUILD_CTIME_TESTS=OFF to avoid this.\n" + ) +endif() +if(SECP256K1_EXPERIMENTAL) + message( + " ******\n" + " WARNING: experimental build\n" + " Experimental features do not have stable APIs or properties, and may not be safe for production use.\n" + " ******\n" + ) +endif() diff --git a/src/secp256k1/CMakePresets.json b/src/secp256k1/CMakePresets.json new file mode 100644 index 000000000..2f37b6a18 --- /dev/null +++ b/src/secp256k1/CMakePresets.json @@ -0,0 +1,19 @@ +{ + "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, + "version": 3, + "configurePresets": [ + { + "name": "dev-mode", + "displayName": "Development mode (intended only for developers of the library)", + "cacheVariables": { + "SECP256K1_EXPERIMENTAL": "ON", + "SECP256K1_ENABLE_MODULE_RECOVERY": "ON", + "SECP256K1_BUILD_EXAMPLES": "ON" + }, + "warnings": { + "dev": true, + "uninitialized": true + } + } + ] +} diff --git a/src/secp256k1/CONTRIBUTING.md b/src/secp256k1/CONTRIBUTING.md new file mode 100644 index 000000000..a366d38b0 --- /dev/null +++ b/src/secp256k1/CONTRIBUTING.md @@ -0,0 +1,108 @@ +# Contributing to libsecp256k1 + +## Scope + +libsecp256k1 is a library for elliptic curve cryptography on the curve secp256k1, not a general-purpose cryptography library. +The library primarily serves the needs of the Bitcoin Core project but provides additional functionality for the benefit of the wider Bitcoin ecosystem. + +## Adding new functionality or modules + +The libsecp256k1 project welcomes contributions in the form of new functionality or modules, provided they are within the project's scope. + +It is the responsibility of the contributors to convince the maintainers that the proposed functionality is within the project's scope, high-quality and maintainable. +Contributors are recommended to provide the following in addition to the new code: + +* **Specification:** + A specification can help significantly in reviewing the new code as it provides documentation and context. + It may justify various design decisions, give a motivation and outline security goals. + If the specification contains pseudocode, a reference implementation or test vectors, these can be used to compare with the proposed libsecp256k1 code. +* **Security Arguments:** + In addition to a defining the security goals, it should be argued that the new functionality meets these goals. + Depending on the nature of the new functionality, a wide range of security arguments are acceptable, ranging from being "obviously secure" to rigorous proofs of security. +* **Relevance Arguments:** + The relevance of the new functionality for the Bitcoin ecosystem should be argued by outlining clear use cases. + +These are not the only factors taken into account when considering to add new functionality. +The proposed new libsecp256k1 code must be of high quality, including API documentation and tests, as well as featuring a misuse-resistant API design. + +We recommend reaching out to other contributors (see [Communication Channels](#communication-channels)) and get feedback before implementing new functionality. + +## Communication channels + +Most communication about libsecp256k1 occurs on the GitHub repository: in issues, pull request or on the discussion board. + +Additionally, there is an IRC channel dedicated to libsecp256k1, with biweekly meetings (see channel topic). +The channel is `#secp256k1` on Libera Chat. +The easiest way to participate on IRC is with the web client, [web.libera.chat](https://web.libera.chat/#secp256k1). +Chat history logs can be found at https://gnusha.org/secp256k1/. + +## Contributor workflow & peer review + +The Contributor Workflow & Peer Review in libsecp256k1 are similar to Bitcoin Core's workflow and review processes described in its [CONTRIBUTING.md](https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md). + +### Coding conventions + +In addition, libsecp256k1 tries to maintain the following coding conventions: + +* No runtime heap allocation (e.g., no `malloc`) unless explicitly requested by the caller (via `secp256k1_context_create` or `secp256k1_scratch_space_create`, for example). Moreover, it should be possible to use the library without any heap allocations. +* The tests should cover all lines and branches of the library (see [Test coverage](#coverage)). +* Operations involving secret data should be tested for being constant time with respect to the secrets (see [src/ctime_tests.c](src/ctime_tests.c)). +* Local variables containing secret data should be cleared explicitly to try to delete secrets from memory. +* Use `secp256k1_memcmp_var` instead of `memcmp` (see [#823](https://github.com/bitcoin-core/secp256k1/issues/823)). +* As a rule of thumb, the default values for configuration options should target standard desktop machines and align with Bitcoin Core's defaults, and the tests should mostly exercise the default configuration (see [#1549](https://github.com/bitcoin-core/secp256k1/issues/1549#issuecomment-2200559257)). + +#### Style conventions + +* Commits should be atomic and diffs should be easy to read. For this reason, do not mix any formatting fixes or code moves with actual code changes. Make sure each individual commit is hygienic: that it builds successfully on its own without warnings, errors, regressions, or test failures. +* New code should adhere to the style of existing, in particular surrounding, code. Other than that, we do not enforce strict rules for code formatting. +* The code conforms to C89. Most notably, that means that only `/* ... */` comments are allowed (no `//` line comments). Moreover, any declarations in a `{ ... }` block (e.g., a function) must appear at the beginning of the block before any statements. When you would like to declare a variable in the middle of a block, you can open a new block: + ```C + void secp256k_foo(void) { + unsigned int x; /* declaration */ + int y = 2*x; /* declaration */ + x = 17; /* statement */ + { + int a, b; /* declaration */ + a = x + y; /* statement */ + secp256k_bar(x, &b); /* statement */ + } + } + ``` +* Use `unsigned int` instead of just `unsigned`. +* Use `void *ptr` instead of `void* ptr`. +* Arguments of the publicly-facing API must have a specific order defined in [include/secp256k1.h](include/secp256k1.h). +* User-facing comment lines in headers should be limited to 80 chars if possible. +* All identifiers in file scope should start with `secp256k1_`. +* Avoid trailing whitespace. + +### Tests + +#### Coverage + +This library aims to have full coverage of reachable lines and branches. + +To create a test coverage report, configure with `--enable-coverage` (use of GCC is necessary): + + $ ./configure --enable-coverage + +Run the tests: + + $ make check + +To create a report, `gcovr` is recommended, as it includes branch coverage reporting: + + $ gcovr --exclude 'src/bench*' --print-summary + +To create a HTML report with coloured and annotated source code: + + $ mkdir -p coverage + $ gcovr --exclude 'src/bench*' --html --html-details -o coverage/coverage.html + +#### Exhaustive tests + +There are tests of several functions in which a small group replaces secp256k1. +These tests are *exhaustive* since they provide all elements and scalars of the small group as input arguments (see [src/tests_exhaustive.c](src/tests_exhaustive.c)). + +### Benchmarks + +See `src/bench*.c` for examples of benchmarks. diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am index c071fbe27..8723b53b2 100644 --- a/src/secp256k1/Makefile.am +++ b/src/secp256k1/Makefile.am @@ -1,13 +1,12 @@ ACLOCAL_AMFLAGS = -I build-aux/m4 +# AM_CFLAGS will be automatically prepended to CFLAGS by Automake when compiling some foo +# which does not have an explicit foo_CFLAGS variable set. +AM_CFLAGS = $(SECP_CFLAGS) + lib_LTLIBRARIES = libsecp256k1.la -if USE_JNI -JNI_LIB = libsecp256k1_jni.la -noinst_LTLIBRARIES = $(JNI_LIB) -else -JNI_LIB = -endif include_HEADERS = include/secp256k1.h +include_HEADERS += include/secp256k1_preallocated.h noinst_HEADERS = noinst_HEADERS += src/scalar.h noinst_HEADERS += src/scalar_4x64.h @@ -19,29 +18,44 @@ noinst_HEADERS += src/scalar_8x32_impl.h noinst_HEADERS += src/scalar_low_impl.h noinst_HEADERS += src/group.h noinst_HEADERS += src/group_impl.h -noinst_HEADERS += src/num_gmp.h -noinst_HEADERS += src/num_gmp_impl.h noinst_HEADERS += src/ecdsa.h noinst_HEADERS += src/ecdsa_impl.h noinst_HEADERS += src/eckey.h noinst_HEADERS += src/eckey_impl.h noinst_HEADERS += src/ecmult.h noinst_HEADERS += src/ecmult_impl.h +noinst_HEADERS += src/ecmult_compute_table.h +noinst_HEADERS += src/ecmult_compute_table_impl.h noinst_HEADERS += src/ecmult_const.h noinst_HEADERS += src/ecmult_const_impl.h noinst_HEADERS += src/ecmult_gen.h noinst_HEADERS += src/ecmult_gen_impl.h -noinst_HEADERS += src/num.h -noinst_HEADERS += src/num_impl.h +noinst_HEADERS += src/ecmult_gen_compute_table.h +noinst_HEADERS += src/ecmult_gen_compute_table_impl.h noinst_HEADERS += src/field_10x26.h noinst_HEADERS += src/field_10x26_impl.h noinst_HEADERS += src/field_5x52.h noinst_HEADERS += src/field_5x52_impl.h noinst_HEADERS += src/field_5x52_int128_impl.h -noinst_HEADERS += src/field_5x52_asm_impl.h -noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h -noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h +noinst_HEADERS += src/modinv32.h +noinst_HEADERS += src/modinv32_impl.h +noinst_HEADERS += src/modinv64.h +noinst_HEADERS += src/modinv64_impl.h +noinst_HEADERS += src/precomputed_ecmult.h +noinst_HEADERS += src/precomputed_ecmult_gen.h +noinst_HEADERS += src/assumptions.h +noinst_HEADERS += src/checkmem.h +noinst_HEADERS += src/testutil.h noinst_HEADERS += src/util.h +noinst_HEADERS += src/int128.h +noinst_HEADERS += src/int128_impl.h +noinst_HEADERS += src/int128_native.h +noinst_HEADERS += src/int128_native_impl.h +noinst_HEADERS += src/int128_struct.h +noinst_HEADERS += src/int128_struct_impl.h +noinst_HEADERS += src/scratch.h +noinst_HEADERS += src/scratch_impl.h +noinst_HEADERS += src/selftest.h noinst_HEADERS += src/testrand.h noinst_HEADERS += src/testrand_impl.h noinst_HEADERS += src/hash.h @@ -49,17 +63,28 @@ noinst_HEADERS += src/hash_impl.h noinst_HEADERS += src/field.h noinst_HEADERS += src/field_impl.h noinst_HEADERS += src/bench.h +noinst_HEADERS += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h +noinst_HEADERS += src/hsort.h +noinst_HEADERS += src/hsort_impl.h noinst_HEADERS += contrib/lax_der_parsing.h noinst_HEADERS += contrib/lax_der_parsing.c noinst_HEADERS += contrib/lax_der_privatekey_parsing.h noinst_HEADERS += contrib/lax_der_privatekey_parsing.c +noinst_HEADERS += examples/examples_util.h + +PRECOMPUTED_LIB = libsecp256k1_precomputed.la +noinst_LTLIBRARIES = $(PRECOMPUTED_LIB) +libsecp256k1_precomputed_la_SOURCES = src/precomputed_ecmult.c src/precomputed_ecmult_gen.c +# We need `-I$(top_srcdir)/src` in VPATH builds if libsecp256k1_precomputed_la_SOURCES have been recreated in the build tree. +# This helps users and packagers who insist on recreating the precomputed files (e.g., Gentoo). +libsecp256k1_precomputed_la_CPPFLAGS = -I$(top_srcdir)/src $(SECP_CONFIG_DEFINES) if USE_EXTERNAL_ASM COMMON_LIB = libsecp256k1_common.la -noinst_LTLIBRARIES = $(COMMON_LIB) else COMMON_LIB = endif +noinst_LTLIBRARIES += $(COMMON_LIB) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libsecp256k1.pc @@ -71,102 +96,174 @@ endif endif libsecp256k1_la_SOURCES = src/secp256k1.c -libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) -libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB) - -libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c -libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES) +libsecp256k1_la_CPPFLAGS = $(SECP_CONFIG_DEFINES) +libsecp256k1_la_LIBADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) +libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE) noinst_PROGRAMS = if USE_BENCHMARK -noinst_PROGRAMS += bench_verify bench_sign bench_internal -bench_verify_SOURCES = src/bench_verify.c -bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) -bench_sign_SOURCES = src/bench_sign.c -bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +noinst_PROGRAMS += bench bench_internal bench_ecmult +bench_SOURCES = src/bench.c +bench_LDADD = libsecp256k1.la +bench_CPPFLAGS = $(SECP_CONFIG_DEFINES) bench_internal_SOURCES = src/bench_internal.c -bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) -bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) +bench_internal_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) +bench_internal_CPPFLAGS = $(SECP_CONFIG_DEFINES) +bench_ecmult_SOURCES = src/bench_ecmult.c +bench_ecmult_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) +bench_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES) endif TESTS = if USE_TESTS -noinst_PROGRAMS += tests -tests_SOURCES = src/tests.c -tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) +TESTS += noverify_tests +noinst_PROGRAMS += noverify_tests +noverify_tests_SOURCES = src/tests.c +noverify_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES) +noverify_tests_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) +noverify_tests_LDFLAGS = -static if !ENABLE_COVERAGE -tests_CPPFLAGS += -DVERIFY -endif -tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) -tests_LDFLAGS = -static TESTS += tests +noinst_PROGRAMS += tests +tests_SOURCES = $(noverify_tests_SOURCES) +tests_CPPFLAGS = $(noverify_tests_CPPFLAGS) -DVERIFY +tests_LDADD = $(noverify_tests_LDADD) +tests_LDFLAGS = $(noverify_tests_LDFLAGS) +endif +endif + +if USE_CTIME_TESTS +noinst_PROGRAMS += ctime_tests +ctime_tests_SOURCES = src/ctime_tests.c +ctime_tests_LDADD = libsecp256k1.la +ctime_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES) endif if USE_EXHAUSTIVE_TESTS noinst_PROGRAMS += exhaustive_tests exhaustive_tests_SOURCES = src/tests_exhaustive.c -exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDES) +exhaustive_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES) if !ENABLE_COVERAGE exhaustive_tests_CPPFLAGS += -DVERIFY endif -exhaustive_tests_LDADD = $(SECP_LIBS) +# Note: do not include $(PRECOMPUTED_LIB) in exhaustive_tests (it uses runtime-generated tables). +exhaustive_tests_LDADD = $(COMMON_LIB) exhaustive_tests_LDFLAGS = -static TESTS += exhaustive_tests endif -JAVAROOT=src/java -JAVAORG=org/bitcoin -JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar -CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA) -JAVA_FILES= \ - $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \ - $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \ - $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \ - $(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java +if USE_EXAMPLES +noinst_PROGRAMS += ecdsa_example +ecdsa_example_SOURCES = examples/ecdsa.c +ecdsa_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC +ecdsa_example_LDADD = libsecp256k1.la +ecdsa_example_LDFLAGS = -static +if BUILD_WINDOWS +ecdsa_example_LDFLAGS += -lbcrypt +endif +TESTS += ecdsa_example +if ENABLE_MODULE_ECDH +noinst_PROGRAMS += ecdh_example +ecdh_example_SOURCES = examples/ecdh.c +ecdh_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC +ecdh_example_LDADD = libsecp256k1.la +ecdh_example_LDFLAGS = -static +if BUILD_WINDOWS +ecdh_example_LDFLAGS += -lbcrypt +endif +TESTS += ecdh_example +endif +if ENABLE_MODULE_SCHNORRSIG +noinst_PROGRAMS += schnorr_example +schnorr_example_SOURCES = examples/schnorr.c +schnorr_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC +schnorr_example_LDADD = libsecp256k1.la +schnorr_example_LDFLAGS = -static +if BUILD_WINDOWS +schnorr_example_LDFLAGS += -lbcrypt +endif +TESTS += schnorr_example +endif +if ENABLE_MODULE_ELLSWIFT +noinst_PROGRAMS += ellswift_example +ellswift_example_SOURCES = examples/ellswift.c +ellswift_example_CPPFLAGS = -I$(top_srcdir)/include -DSECP256K1_STATIC +ellswift_example_LDADD = libsecp256k1.la +ellswift_example_LDFLAGS = -static +if BUILD_WINDOWS +ellswift_example_LDFLAGS += -lbcrypt +endif +TESTS += ellswift_example +endif +endif -if USE_JNI +### Precomputed tables +EXTRA_PROGRAMS = precompute_ecmult precompute_ecmult_gen +CLEANFILES = $(EXTRA_PROGRAMS) -$(JAVA_GUAVA): - @echo Guava is missing. Fetch it via: \ - wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@) - @false +precompute_ecmult_SOURCES = src/precompute_ecmult.c +precompute_ecmult_CPPFLAGS = $(SECP_CONFIG_DEFINES) -DVERIFY +precompute_ecmult_LDADD = $(COMMON_LIB) -.stamp-java: $(JAVA_FILES) - @echo Compiling $^ - $(AM_V_at)$(CLASSPATH_ENV) javac $^ - @touch $@ +precompute_ecmult_gen_SOURCES = src/precompute_ecmult_gen.c +precompute_ecmult_gen_CPPFLAGS = $(SECP_CONFIG_DEFINES) -DVERIFY +precompute_ecmult_gen_LDADD = $(COMMON_LIB) -if USE_TESTS +# See Automake manual, Section "Errors with distclean". +# We don't list any dependencies for the prebuilt files here because +# otherwise make's decision whether to rebuild them (even in the first +# build by a normal user) depends on mtimes, and thus is very fragile. +# This means that rebuilds of the prebuilt files always need to be +# forced by deleting them. +src/precomputed_ecmult.c: + $(MAKE) $(AM_MAKEFLAGS) precompute_ecmult$(EXEEXT) + ./precompute_ecmult$(EXEEXT) +src/precomputed_ecmult_gen.c: + $(MAKE) $(AM_MAKEFLAGS) precompute_ecmult_gen$(EXEEXT) + ./precompute_ecmult_gen$(EXEEXT) -check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java - $(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test +PRECOMP = src/precomputed_ecmult_gen.c src/precomputed_ecmult.c +precomp: $(PRECOMP) -endif -endif +# Ensure the prebuilt files will be build first (only if they don't exist, +# e.g., after `make maintainer-clean`). +BUILT_SOURCES = $(PRECOMP) -if USE_ECMULT_STATIC_PRECOMPUTATION -CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) -CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function +.PHONY: clean-precomp +clean-precomp: + rm -f $(PRECOMP) +maintainer-clean-local: clean-precomp -gen_context_OBJECTS = gen_context.o -gen_context_BIN = gen_context$(BUILD_EXEEXT) -gen_%.o: src/gen_%.c - $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@ +### Pregenerated test vectors +### (see the comments in the previous section for detailed rationale) +TESTVECTORS = src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h -$(gen_context_BIN): $(gen_context_OBJECTS) - $(CC_FOR_BUILD) $^ -o $@ +src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h: + mkdir -p $(@D) + python3 $(top_srcdir)/tools/tests_wycheproof_generate.py $(top_srcdir)/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json > $@ -$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h -$(tests_OBJECTS): src/ecmult_static_context.h -$(bench_internal_OBJECTS): src/ecmult_static_context.h +testvectors: $(TESTVECTORS) -src/ecmult_static_context.h: $(gen_context_BIN) - ./$(gen_context_BIN) +BUILT_SOURCES += $(TESTVECTORS) -CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java -endif +.PHONY: clean-testvectors +clean-testvectors: + rm -f $(TESTVECTORS) +maintainer-clean-local: clean-testvectors -EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES) +### Additional files to distribute +EXTRA_DIST = autogen.sh CHANGELOG.md SECURITY.md +EXTRA_DIST += doc/release-process.md doc/safegcd_implementation.md +EXTRA_DIST += examples/EXAMPLES_COPYING +EXTRA_DIST += sage/gen_exhaustive_groups.sage +EXTRA_DIST += sage/gen_split_lambda_constants.sage +EXTRA_DIST += sage/group_prover.sage +EXTRA_DIST += sage/prove_group_implementations.sage +EXTRA_DIST += sage/secp256k1_params.sage +EXTRA_DIST += sage/weierstrass_prover.sage +EXTRA_DIST += src/wycheproof/WYCHEPROOF_COPYING +EXTRA_DIST += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json +EXTRA_DIST += tools/tests_wycheproof_generate.py if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include @@ -175,3 +272,15 @@ endif if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif + +if ENABLE_MODULE_EXTRAKEYS +include src/modules/extrakeys/Makefile.am.include +endif + +if ENABLE_MODULE_SCHNORRSIG +include src/modules/schnorrsig/Makefile.am.include +endif + +if ENABLE_MODULE_ELLSWIFT +include src/modules/ellswift/Makefile.am.include +endif diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md index 8cd344ea8..ed93e0519 100644 --- a/src/secp256k1/README.md +++ b/src/secp256k1/README.md @@ -1,19 +1,26 @@ libsecp256k1 ============ -[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1) +![Dependencies: None](https://img.shields.io/badge/dependencies-none-success) +[![irc.libera.chat #secp256k1](https://img.shields.io/badge/irc.libera.chat-%23secp256k1-success)](https://web.libera.chat/#secp256k1) -Optimized C library for EC operations on curve secp256k1. +High-performance high-assurance C library for digital signatures and other cryptographic primitives on the secp256k1 elliptic curve. -This library is a work in progress and is being used to research best practices. Use at your own risk. +This library is intended to be the highest quality publicly available library for cryptography on the secp256k1 curve. However, the primary focus of its development has been for usage in the Bitcoin system and usage unlike Bitcoin's may be less well tested, verified, or suffer from a less well thought out interface. Correct usage requires some care and consideration that the library is fit for your application's purpose. Features: * secp256k1 ECDSA signing/verification and key generation. -* Adding/multiplying private/public keys. -* Serialization/parsing of private keys, public keys, signatures. -* Constant time, constant memory access signing and pubkey generation. -* Derandomized DSA (via RFC6979 or with a caller provided function.) +* Additive and multiplicative tweaking of secret/public keys. +* Serialization/parsing of secret keys, public keys, signatures. +* Constant time, constant memory access signing and public key generation. +* Derandomized ECDSA (via RFC6979 or with a caller provided function.) * Very efficient implementation. +* Suitable for embedded systems. +* No runtime dependencies. +* Optional module for public key recovery. +* Optional module for ECDH key exchange. +* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). +* Optional module for ElligatorSwift key exchange according to [BIP-324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki). Implementation details ---------------------- @@ -23,16 +30,18 @@ Implementation details * Extensive testing infrastructure. * Structured to facilitate review and analysis. * Intended to be portable to any system with a C89 compiler and uint64_t support. + * No use of floating types. * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") * Field operations * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). - * Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys). - * Using 10 26-bit limbs. - * Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman). + * Using 5 52-bit limbs + * Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan). + * This is an experimental feature that has not received enough scrutiny to satisfy the standard of quality of this library but is made available for testing and review by the community. * Scalar operations * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. * Using 4 64-bit limbs (relying on __int128 support in the compiler). * Using 8 32-bit limbs. +* Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman). * Group operations * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). * Use addition between points in Jacobian and affine coordinates where possible. @@ -42,20 +51,91 @@ Implementation details * Use wNAF notation for point multiplicands. * Use a much larger window for multiples of G, using precomputed multiples. * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. - * Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. + * Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. * Point multiplication for signing * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. - * Access the table with branch-free conditional moves so memory access is uniform. - * No data-dependent branches - * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally. + * Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains) + * Access the table with branch-free conditional moves so memory access is uniform. + * No data-dependent branches + * Optional runtime blinding which attempts to frustrate differential power analysis. + * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally. -Build steps ------------ - -libsecp256k1 is built using autotools: +Building with Autotools +----------------------- $ ./autogen.sh $ ./configure $ make - $ ./tests + $ make check # run the test suite $ sudo make install # optional + +To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. + +Building with CMake (experimental) +---------------------------------- + +To maintain a pristine source tree, CMake encourages to perform an out-of-source build by using a separate dedicated build tree. + +### Building on POSIX systems + + $ mkdir build && cd build + $ cmake .. + $ cmake --build . + $ ctest # run the test suite + $ sudo cmake --install . # optional + +To compile optional modules (such as Schnorr signatures), you need to run `cmake` with additional flags (such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON`). Run `cmake .. -LH` to see the full list of available flags. + +### Cross compiling + +To alleviate issues with cross compiling, preconfigured toolchain files are available in the `cmake` directory. +For example, to cross compile for Windows: + + $ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/x86_64-w64-mingw32.toolchain.cmake + +To cross compile for Android with [NDK](https://developer.android.com/ndk/guides/cmake) (using NDK's toolchain file, and assuming the `ANDROID_NDK_ROOT` environment variable has been set): + + $ cmake .. -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=28 + +### Building on Windows + +To build on Windows with Visual Studio, a proper [generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators) must be specified for a new build tree. + +The following example assumes using of Visual Studio 2022 and CMake v3.21+. + +In "Developer Command Prompt for VS 2022": + + >cmake -G "Visual Studio 17 2022" -A x64 -S . -B build + >cmake --build build --config RelWithDebInfo + +Usage examples +----------- +Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. + * [ECDSA example](examples/ecdsa.c) + * [Schnorr signatures example](examples/schnorr.c) + * [Deriving a shared secret (ECDH) example](examples/ecdh.c) + * [ElligatorSwift key exchange example](examples/ellswift.c) + +To compile the Schnorr signature and ECDH examples, you also need to configure with `--enable-module-schnorrsig` and `--enable-module-ecdh`. + +Benchmark +------------ +If configured with `--enable-benchmark` (which is the default), binaries for benchmarking the libsecp256k1 functions will be present in the root directory after the build. + +To print the benchmark result to the command line: + + $ ./bench_name + +To create a CSV file for the benchmark result : + + $ ./bench_name | sed '2d;s/ \{1,\}//g' > bench_name.csv + +Reporting a vulnerability +------------ + +See [SECURITY.md](SECURITY.md) + +Contributing to libsecp256k1 +------------ + +See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/src/secp256k1/SECURITY.md b/src/secp256k1/SECURITY.md new file mode 100644 index 000000000..b515cc1c8 --- /dev/null +++ b/src/secp256k1/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Reporting a Vulnerability + +To report security issues send an email to secp256k1-security@bitcoincore.org (not for support). + +The following keys may be used to communicate sensitive information to developers: + +| Name | Fingerprint | +|------|-------------| +| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 | +| Jonas Nick | 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366 | +| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 | + +You can import a key by running the following command with that individual’s fingerprint: `gpg --keyserver hkps://keys.openpgp.org --recv-keys ""` Ensure that you put quotes around fingerprints containing spaces. diff --git a/src/secp256k1/TODO b/src/secp256k1/TODO deleted file mode 100644 index a300e1c5e..000000000 --- a/src/secp256k1/TODO +++ /dev/null @@ -1,3 +0,0 @@ -* Unit tests for fieldelem/groupelem, including ones intended to - trigger fieldelem's boundary cases. -* Complete constant-time operations for signing/keygen diff --git a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 b/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 deleted file mode 100644 index 1fc362761..000000000 --- a/src/secp256k1/build-aux/m4/ax_jni_include_dir.m4 +++ /dev/null @@ -1,140 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_JNI_INCLUDE_DIR -# -# DESCRIPTION -# -# AX_JNI_INCLUDE_DIR finds include directories needed for compiling -# programs using the JNI interface. -# -# JNI include directories are usually in the Java distribution. This is -# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in -# that order. When this macro completes, a list of directories is left in -# the variable JNI_INCLUDE_DIRS. -# -# Example usage follows: -# -# AX_JNI_INCLUDE_DIR -# -# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS -# do -# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR" -# done -# -# If you want to force a specific compiler: -# -# - at the configure.in level, set JAVAC=yourcompiler before calling -# AX_JNI_INCLUDE_DIR -# -# - at the configure level, setenv JAVAC -# -# Note: This macro can work with the autoconf M4 macros for Java programs. -# This particular macro is not part of the original set of macros. -# -# LICENSE -# -# Copyright (c) 2008 Don Anderson -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 10 - -AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR]) -AC_DEFUN([AX_JNI_INCLUDE_DIR],[ - -JNI_INCLUDE_DIRS="" - -if test "x$JAVA_HOME" != x; then - _JTOPDIR="$JAVA_HOME" -else - if test "x$JAVAC" = x; then - JAVAC=javac - fi - AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no]) - if test "x$_ACJNI_JAVAC" = xno; then - AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME]) - fi - _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC") - _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'` -fi - -case "$host_os" in - darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` - _JINC="$_JTOPDIR/Headers";; - *) _JINC="$_JTOPDIR/include";; -esac -_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR]) -_AS_ECHO_LOG([_JINC=$_JINC]) - -# On Mac OS X 10.6.4, jni.h is a symlink: -# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h -# -> ../../CurrentJDK/Headers/jni.h. - -AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path, -[ -if test -f "$_JINC/jni.h"; then - ac_cv_jni_header_path="$_JINC" - JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" -else - _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` - if test -f "$_JTOPDIR/include/jni.h"; then - ac_cv_jni_header_path="$_JTOPDIR/include" - JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" - else - ac_cv_jni_header_path=none - fi -fi -]) - - - -# get the likely subdirectories for system specific java includes -case "$host_os" in -bsdi*) _JNI_INC_SUBDIRS="bsdos";; -darwin*) _JNI_INC_SUBDIRS="darwin";; -freebsd*) _JNI_INC_SUBDIRS="freebsd";; -linux*) _JNI_INC_SUBDIRS="linux genunix";; -osf*) _JNI_INC_SUBDIRS="alpha";; -solaris*) _JNI_INC_SUBDIRS="solaris";; -mingw*) _JNI_INC_SUBDIRS="win32";; -cygwin*) _JNI_INC_SUBDIRS="win32";; -*) _JNI_INC_SUBDIRS="genunix";; -esac - -if test "x$ac_cv_jni_header_path" != "xnone"; then - # add any subdirectories that are present - for JINCSUBDIR in $_JNI_INC_SUBDIRS - do - if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then - JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR" - fi - done -fi -]) - -# _ACJNI_FOLLOW_SYMLINKS -# Follows symbolic links on , -# finally setting variable _ACJNI_FOLLOWED -# ---------------------------------------- -AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[ -# find the include directory relative to the javac executable -_cur="$1" -while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do - AC_MSG_CHECKING([symlink for $_cur]) - _slink=`ls -ld "$_cur" | sed 's/.* -> //'` - case "$_slink" in - /*) _cur="$_slink";; - # 'X' avoids triggering unwanted echo options. - *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";; - esac - AC_MSG_RESULT([$_cur]) -done -_ACJNI_FOLLOWED="$_cur" -])# _ACJNI diff --git a/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 b/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 deleted file mode 100644 index 77fd346a7..000000000 --- a/src/secp256k1/build-aux/m4/ax_prog_cc_for_build.m4 +++ /dev/null @@ -1,125 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PROG_CC_FOR_BUILD -# -# DESCRIPTION -# -# This macro searches for a C compiler that generates native executables, -# that is a C compiler that surely is not a cross-compiler. This can be -# useful if you have to generate source code at compile-time like for -# example GCC does. -# -# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything -# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD). -# The value of these variables can be overridden by the user by specifying -# a compiler with an environment variable (like you do for standard CC). -# -# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object -# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if -# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are -# substituted in the Makefile. -# -# LICENSE -# -# Copyright (c) 2008 Paolo Bonzini -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 8 - -AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD]) -AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl -AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([AC_PROG_CPP])dnl -AC_REQUIRE([AC_EXEEXT])dnl -AC_REQUIRE([AC_CANONICAL_HOST])dnl - -dnl Use the standard macros, but make them use other variable names -dnl -pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl -pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl -pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl -pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl -pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl -pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl -pushdef([ac_cv_objext], ac_cv_build_objext)dnl -pushdef([ac_exeext], ac_build_exeext)dnl -pushdef([ac_objext], ac_build_objext)dnl -pushdef([CC], CC_FOR_BUILD)dnl -pushdef([CPP], CPP_FOR_BUILD)dnl -pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl -pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl -pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl -pushdef([host], build)dnl -pushdef([host_alias], build_alias)dnl -pushdef([host_cpu], build_cpu)dnl -pushdef([host_vendor], build_vendor)dnl -pushdef([host_os], build_os)dnl -pushdef([ac_cv_host], ac_cv_build)dnl -pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl -pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl -pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl -pushdef([ac_cv_host_os], ac_cv_build_os)dnl -pushdef([ac_cpp], ac_build_cpp)dnl -pushdef([ac_compile], ac_build_compile)dnl -pushdef([ac_link], ac_build_link)dnl - -save_cross_compiling=$cross_compiling -save_ac_tool_prefix=$ac_tool_prefix -cross_compiling=no -ac_tool_prefix= - -AC_PROG_CC -AC_PROG_CPP -AC_EXEEXT - -ac_tool_prefix=$save_ac_tool_prefix -cross_compiling=$save_cross_compiling - -dnl Restore the old definitions -dnl -popdef([ac_link])dnl -popdef([ac_compile])dnl -popdef([ac_cpp])dnl -popdef([ac_cv_host_os])dnl -popdef([ac_cv_host_vendor])dnl -popdef([ac_cv_host_cpu])dnl -popdef([ac_cv_host_alias])dnl -popdef([ac_cv_host])dnl -popdef([host_os])dnl -popdef([host_vendor])dnl -popdef([host_cpu])dnl -popdef([host_alias])dnl -popdef([host])dnl -popdef([LDFLAGS])dnl -popdef([CPPFLAGS])dnl -popdef([CFLAGS])dnl -popdef([CPP])dnl -popdef([CC])dnl -popdef([ac_objext])dnl -popdef([ac_exeext])dnl -popdef([ac_cv_objext])dnl -popdef([ac_cv_exeext])dnl -popdef([ac_cv_prog_cc_g])dnl -popdef([ac_cv_prog_cc_cross])dnl -popdef([ac_cv_prog_cc_works])dnl -popdef([ac_cv_prog_gcc])dnl -popdef([ac_cv_prog_CPP])dnl - -dnl Finally, set Makefile variables -dnl -BUILD_EXEEXT=$ac_build_exeext -BUILD_OBJEXT=$ac_build_objext -AC_SUBST(BUILD_EXEEXT)dnl -AC_SUBST(BUILD_OBJEXT)dnl -AC_SUBST([CFLAGS_FOR_BUILD])dnl -AC_SUBST([CPPFLAGS_FOR_BUILD])dnl -AC_SUBST([LDFLAGS_FOR_BUILD])dnl -]) diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 index b74acb8c1..25a65eccf 100644 --- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 +++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4 @@ -1,69 +1,91 @@ -dnl libsecp25k1 helper checks -AC_DEFUN([SECP_INT128_CHECK],[ -has_int128=$ac_cv_type___int128 -]) - dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell. -AC_DEFUN([SECP_64BIT_ASM_CHECK],[ +AC_DEFUN([SECP_X86_64_ASM_CHECK],[ AC_MSG_CHECKING(for x86_64 assembly availability) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]],[[ uint64_t a = 11, tmp; __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); - ]])],[has_64bit_asm=yes],[has_64bit_asm=no]) -AC_MSG_RESULT([$has_64bit_asm]) + ]])], [has_x86_64_asm=yes], [has_x86_64_asm=no]) +AC_MSG_RESULT([$has_x86_64_asm]) ]) -dnl -AC_DEFUN([SECP_OPENSSL_CHECK],[ - has_libcrypto=no - m4_ifdef([PKG_CHECK_MODULES],[ - PKG_CHECK_MODULES([CRYPTO], [libcrypto], [has_libcrypto=yes],[has_libcrypto=no]) - if test x"$has_libcrypto" = x"yes"; then - TEMP_LIBS="$LIBS" - LIBS="$LIBS $CRYPTO_LIBS" - AC_CHECK_LIB(crypto, main,[AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])],[has_libcrypto=no]) - LIBS="$TEMP_LIBS" - fi - ]) - if test x$has_libcrypto = xno; then - AC_CHECK_HEADER(openssl/crypto.h,[ - AC_CHECK_LIB(crypto, main,[ - has_libcrypto=yes - CRYPTO_LIBS=-lcrypto - AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed]) - ]) - ]) - LIBS= - fi -if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then - AC_MSG_CHECKING(for EC functions in libcrypto) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - #include ]],[[ - EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1); - ECDSA_sign(0, NULL, 0, NULL, NULL, eckey); - ECDSA_verify(0, NULL, 0, NULL, 0, eckey); - EC_KEY_free(eckey); - ECDSA_SIG *sig_openssl; - sig_openssl = ECDSA_SIG_new(); - (void)sig_openssl->r; - ECDSA_SIG_free(sig_openssl); - ]])],[has_openssl_ec=yes],[has_openssl_ec=no]) - AC_MSG_RESULT([$has_openssl_ec]) -fi +AC_DEFUN([SECP_ARM32_ASM_CHECK], [ + AC_MSG_CHECKING(for ARM32 assembly availability) + SECP_ARM32_ASM_CHECK_CFLAGS_saved_CFLAGS="$CFLAGS" + CFLAGS="-x assembler" + AC_LINK_IFELSE([AC_LANG_SOURCE([[ + .syntax unified + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .text + .global main + main: + ldr r0, =0x002A + mov r7, #1 + swi 0 + ]])], [has_arm32_asm=yes], [has_arm32_asm=no]) + AC_MSG_RESULT([$has_arm32_asm]) + CFLAGS="$SECP_ARM32_ASM_CHECK_CFLAGS_saved_CFLAGS" ]) -dnl -AC_DEFUN([SECP_GMP_CHECK],[ -if test x"$has_gmp" != x"yes"; then +AC_DEFUN([SECP_VALGRIND_CHECK],[ +AC_MSG_CHECKING([for valgrind support]) +if test x"$has_valgrind" != x"yes"; then CPPFLAGS_TEMP="$CPPFLAGS" - CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS" - LIBS_TEMP="$LIBS" - LIBS="$GMP_LIBS $LIBS" - AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])]) + CPPFLAGS="$VALGRIND_CPPFLAGS $CPPFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], [[ + #if defined(NVALGRIND) + # error "Valgrind does not support this platform." + #endif + ]])], [has_valgrind=yes]) CPPFLAGS="$CPPFLAGS_TEMP" - LIBS="$LIBS_TEMP" fi +AC_MSG_RESULT($has_valgrind) +]) + +AC_DEFUN([SECP_MSAN_CHECK], [ +AC_MSG_CHECKING(whether MemorySanitizer is enabled) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #if defined(__has_feature) + # if __has_feature(memory_sanitizer) + /* MemorySanitizer is enabled. */ + # elif + # error "MemorySanitizer is disabled." + # endif + #else + # error "__has_feature is not defined." + #endif + ]])], [msan_enabled=yes], [msan_enabled=no]) +AC_MSG_RESULT([$msan_enabled]) +]) + +dnl SECP_TRY_APPEND_CFLAGS(flags, VAR) +dnl Append flags to VAR if CC accepts them. +AC_DEFUN([SECP_TRY_APPEND_CFLAGS], [ + AC_MSG_CHECKING([if ${CC} supports $1]) + SECP_TRY_APPEND_CFLAGS_saved_CFLAGS="$CFLAGS" + CFLAGS="$1 $CFLAGS" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], [flag_works=yes], [flag_works=no]) + AC_MSG_RESULT($flag_works) + CFLAGS="$SECP_TRY_APPEND_CFLAGS_saved_CFLAGS" + if test x"$flag_works" = x"yes"; then + $2="$$2 $1" + fi + unset flag_works + AC_SUBST($2) +]) + +dnl SECP_SET_DEFAULT(VAR, default, default-dev-mode) +dnl Set VAR to default or default-dev-mode, depending on whether dev mode is enabled +AC_DEFUN([SECP_SET_DEFAULT], [ + if test "${enable_dev_mode+set}" != set; then + AC_MSG_ERROR([[Set enable_dev_mode before calling SECP_SET_DEFAULT]]) + fi + if test x"$enable_dev_mode" = x"yes"; then + $1="$3" + else + $1="$2" + fi ]) diff --git a/src/secp256k1/ci/ci.sh b/src/secp256k1/ci/ci.sh new file mode 100755 index 000000000..a6c608c29 --- /dev/null +++ b/src/secp256k1/ci/ci.sh @@ -0,0 +1,148 @@ +#!/bin/sh + +set -eux + +export LC_ALL=C + +# Print commit and relevant CI environment to allow reproducing the job outside of CI. +git show --no-patch +print_environment() { + # Turn off -x because it messes up the output + set +x + # There are many ways to print variable names and their content. This one + # does not rely on bash. + for var in WERROR_CFLAGS MAKEFLAGS BUILD \ + ECMULTWINDOW ECMULTGENKB ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \ + EXPERIMENTAL ECDH RECOVERY EXTRAKEYS SCHNORRSIG ELLSWIFT \ + SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\ + EXAMPLES \ + HOST WRAPPER_CMD \ + CC CFLAGS CPPFLAGS AR NM \ + UBSAN_OPTIONS ASAN_OPTIONS LSAN_OPTIONS + do + eval "isset=\${$var+x}" + if [ -n "$isset" ]; then + eval "val=\${$var}" + # shellcheck disable=SC2154 + printf '%s="%s" ' "$var" "$val" + fi + done + echo "$0" + set -x +} +print_environment + +env >> test_env.log + +# If gcc is requested, assert that it's in fact gcc (and not some symlinked Apple clang). +case "${CC:-undefined}" in + *gcc*) + $CC -v 2>&1 | grep -q "gcc version" || exit 1; + ;; +esac + +if [ -n "${CC+x}" ]; then + # The MSVC compiler "cl" doesn't understand "-v" + $CC -v || true +fi +if [ "$WITH_VALGRIND" = "yes" ]; then + valgrind --version +fi +if [ -n "$WRAPPER_CMD" ]; then + $WRAPPER_CMD --version +fi + +# Workaround for https://bugs.kde.org/show_bug.cgi?id=452758 (fixed in valgrind 3.20.0). +case "${CC:-undefined}" in + clang*) + if [ "$CTIMETESTS" = "yes" ] && [ "$WITH_VALGRIND" = "yes" ] + then + export CFLAGS="${CFLAGS:+$CFLAGS }-gdwarf-4" + else + case "$WRAPPER_CMD" in + valgrind*) + export CFLAGS="${CFLAGS:+$CFLAGS }-gdwarf-4" + ;; + esac + fi + ;; +esac + +./autogen.sh + +./configure \ + --enable-experimental="$EXPERIMENTAL" \ + --with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \ + --with-ecmult-window="$ECMULTWINDOW" \ + --with-ecmult-gen-kb="$ECMULTGENKB" \ + --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-ellswift="$ELLSWIFT" \ + --enable-module-extrakeys="$EXTRAKEYS" \ + --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-examples="$EXAMPLES" \ + --enable-ctime-tests="$CTIMETESTS" \ + --with-valgrind="$WITH_VALGRIND" \ + --host="$HOST" $EXTRAFLAGS + +# We have set "-j" in MAKEFLAGS. +build_exit_code=0 +make > make.log 2>&1 || build_exit_code=$? +cat make.log +if [ $build_exit_code -ne 0 ]; then + case "${CC:-undefined}" in + *snapshot*) + # Ignore internal compiler errors in gcc-snapshot and clang-snapshot + grep -e "internal compiler error:" -e "PLEASE submit a bug report" make.log + return $?; + ;; + *) + return 1; + ;; + esac +fi + +# Print information about binaries so that we can see that the architecture is correct +file *tests* || true +file bench* || true +file .libs/* || true + +# This tells `make check` to wrap test invocations. +export LOG_COMPILER="$WRAPPER_CMD" + +make "$BUILD" + +# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool +EXEC='./libtool --mode=execute' +if [ -n "$WRAPPER_CMD" ] +then + EXEC="$EXEC $WRAPPER_CMD" +fi + +if [ "$BENCH" = "yes" ] +then + { + $EXEC ./bench_ecmult + $EXEC ./bench_internal + $EXEC ./bench + } >> bench.log 2>&1 +fi + +if [ "$CTIMETESTS" = "yes" ] +then + if [ "$WITH_VALGRIND" = "yes" ]; then + ./libtool --mode=execute valgrind --error-exitcode=42 ./ctime_tests > ctime_tests.log 2>&1 + else + $EXEC ./ctime_tests > ctime_tests.log 2>&1 + fi +fi + +# Rebuild precomputed files (if not cross-compiling). +if [ -z "$HOST" ] +then + make clean-precomp clean-testvectors + make precomp testvectors +fi + +# Check that no repo files have been modified by the build. +# (This fails for example if the precomp files need to be updated in the repo.) +git diff --exit-code diff --git a/src/secp256k1/ci/linux-debian.Dockerfile b/src/secp256k1/ci/linux-debian.Dockerfile new file mode 100644 index 000000000..c0f018c27 --- /dev/null +++ b/src/secp256k1/ci/linux-debian.Dockerfile @@ -0,0 +1,79 @@ +FROM debian:stable-slim + +SHELL ["/bin/bash", "-c"] + +WORKDIR /root + +# A too high maximum number of file descriptors (with the default value +# inherited from the docker host) can cause issues with some of our tools: +# - sanitizers hanging: https://github.com/google/sanitizers/issues/1662 +# - valgrind crashing: https://stackoverflow.com/a/75293014 +# This is not be a problem on our CI hosts, but developers who run the image +# on their machines may run into this (e.g., on Arch Linux), so warn them. +# (Note that .bashrc is only executed in interactive bash shells.) +RUN echo 'if [[ $(ulimit -n) -gt 200000 ]]; then echo "WARNING: Very high value reported by \"ulimit -n\". Consider passing \"--ulimit nofile=32768\" to \"docker run\"."; fi' >> /root/.bashrc + +RUN dpkg --add-architecture i386 && \ + dpkg --add-architecture s390x && \ + dpkg --add-architecture armhf && \ + dpkg --add-architecture arm64 && \ + dpkg --add-architecture ppc64el + +# dkpg-dev: to make pkg-config work in cross-builds +# llvm: for llvm-symbolizer, which is used by clang's UBSan for symbolized stack traces +RUN apt-get update && apt-get install --no-install-recommends -y \ + git ca-certificates \ + make automake libtool pkg-config dpkg-dev valgrind qemu-user \ + gcc clang llvm libclang-rt-dev libc6-dbg \ + g++ \ + gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 libubsan1:i386 libasan8:i386 \ + gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \ + gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \ + gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \ + gcc-mingw-w64-x86-64-win32 wine64 wine \ + gcc-mingw-w64-i686-win32 wine32 \ + python3 && \ + if ! ( dpkg --print-architecture | grep --quiet "arm64" ) ; then \ + apt-get install --no-install-recommends -y \ + gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 ;\ + fi && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Build and install gcc snapshot +ARG GCC_SNAPSHOT_MAJOR=14 +RUN apt-get update && apt-get install --no-install-recommends -y wget libgmp-dev libmpfr-dev libmpc-dev flex && \ + mkdir gcc && cd gcc && \ + wget --progress=dot:giga --https-only --recursive --accept '*.tar.xz' --level 1 --no-directories "https://gcc.gnu.org/pub/gcc/snapshots/LATEST-${GCC_SNAPSHOT_MAJOR}" && \ + wget "https://gcc.gnu.org/pub/gcc/snapshots/LATEST-${GCC_SNAPSHOT_MAJOR}/sha512.sum" && \ + sha512sum --check --ignore-missing sha512.sum && \ + # We should have downloaded exactly one tar.xz file + ls && \ + [ $(ls *.tar.xz | wc -l) -eq "1" ] && \ + tar xf *.tar.xz && \ + mkdir gcc-build && cd gcc-build && \ + ../*/configure --prefix=/opt/gcc-snapshot --enable-languages=c --disable-bootstrap --disable-multilib --without-isl && \ + make -j $(nproc) && \ + make install && \ + cd ../.. && rm -rf gcc && \ + ln -s /opt/gcc-snapshot/bin/gcc /usr/bin/gcc-snapshot && \ + apt-get autoremove -y wget libgmp-dev libmpfr-dev libmpc-dev flex && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install clang snapshot, see https://apt.llvm.org/ +RUN \ + # Setup GPG keys of LLVM repository + apt-get update && apt-get install --no-install-recommends -y wget && \ + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && \ + # Add repository for this Debian release + . /etc/os-release && echo "deb http://apt.llvm.org/${VERSION_CODENAME} llvm-toolchain-${VERSION_CODENAME} main" >> /etc/apt/sources.list && \ + apt-get update && \ + # Determine the version number of the LLVM development branch + LLVM_VERSION=$(apt-cache search --names-only '^clang-[0-9]+$' | sort -V | tail -1 | cut -f1 -d" " | cut -f2 -d"-" ) && \ + # Install + apt-get install --no-install-recommends -y "clang-${LLVM_VERSION}" && \ + # Create symlink + ln -s "/usr/bin/clang-${LLVM_VERSION}" /usr/bin/clang-snapshot && \ + # Clean up + apt-get autoremove -y wget && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + diff --git a/src/secp256k1/cmake/CheckArm32Assembly.cmake b/src/secp256k1/cmake/CheckArm32Assembly.cmake new file mode 100644 index 000000000..baeeff029 --- /dev/null +++ b/src/secp256k1/cmake/CheckArm32Assembly.cmake @@ -0,0 +1,6 @@ +function(check_arm32_assembly) + try_compile(HAVE_ARM32_ASM + ${PROJECT_BINARY_DIR}/check_arm32_assembly + SOURCES ${PROJECT_SOURCE_DIR}/cmake/source_arm32.s + ) +endfunction() diff --git a/src/secp256k1/cmake/CheckMemorySanitizer.cmake b/src/secp256k1/cmake/CheckMemorySanitizer.cmake new file mode 100644 index 000000000..d9ef681e6 --- /dev/null +++ b/src/secp256k1/cmake/CheckMemorySanitizer.cmake @@ -0,0 +1,18 @@ +include_guard(GLOBAL) +include(CheckCSourceCompiles) + +function(check_memory_sanitizer output) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + check_c_source_compiles(" + #if defined(__has_feature) + # if __has_feature(memory_sanitizer) + /* MemorySanitizer is enabled. */ + # elif + # error \"MemorySanitizer is disabled.\" + # endif + #else + # error \"__has_feature is not defined.\" + #endif + " HAVE_MSAN) + set(${output} ${HAVE_MSAN} PARENT_SCOPE) +endfunction() diff --git a/src/secp256k1/cmake/CheckStringOptionValue.cmake b/src/secp256k1/cmake/CheckStringOptionValue.cmake new file mode 100644 index 000000000..5a4d939b9 --- /dev/null +++ b/src/secp256k1/cmake/CheckStringOptionValue.cmake @@ -0,0 +1,10 @@ +function(check_string_option_value option) + get_property(expected_values CACHE ${option} PROPERTY STRINGS) + if(expected_values) + if(${option} IN_LIST expected_values) + return() + endif() + message(FATAL_ERROR "${option} value is \"${${option}}\", but must be one of ${expected_values}.") + endif() + message(AUTHOR_WARNING "The STRINGS property must be set before invoking `check_string_option_value' function.") +endfunction() diff --git a/src/secp256k1/cmake/CheckX86_64Assembly.cmake b/src/secp256k1/cmake/CheckX86_64Assembly.cmake new file mode 100644 index 000000000..ae82cd476 --- /dev/null +++ b/src/secp256k1/cmake/CheckX86_64Assembly.cmake @@ -0,0 +1,14 @@ +include(CheckCSourceCompiles) + +function(check_x86_64_assembly) + check_c_source_compiles(" + #include + + int main() + { + uint64_t a = 11, tmp; + __asm__ __volatile__(\"movq $0x100000000,%1; mulq %%rsi\" : \"+a\"(a) : \"S\"(tmp) : \"cc\", \"%rdx\"); + } + " HAVE_X86_64_ASM) + set(HAVE_X86_64_ASM ${HAVE_X86_64_ASM} PARENT_SCOPE) +endfunction() diff --git a/src/secp256k1/cmake/FindValgrind.cmake b/src/secp256k1/cmake/FindValgrind.cmake new file mode 100644 index 000000000..3af5e691e --- /dev/null +++ b/src/secp256k1/cmake/FindValgrind.cmake @@ -0,0 +1,41 @@ +if(CMAKE_HOST_APPLE) + find_program(BREW_COMMAND brew) + execute_process( + COMMAND ${BREW_COMMAND} --prefix valgrind + OUTPUT_VARIABLE valgrind_brew_prefix + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif() + +set(hints_paths) +if(valgrind_brew_prefix) + set(hints_paths ${valgrind_brew_prefix}/include) +endif() + +find_path(Valgrind_INCLUDE_DIR + NAMES valgrind/memcheck.h + HINTS ${hints_paths} +) + +if(Valgrind_INCLUDE_DIR) + include(CheckCSourceCompiles) + set(CMAKE_REQUIRED_INCLUDES ${Valgrind_INCLUDE_DIR}) + check_c_source_compiles(" + #include + #if defined(NVALGRIND) + # error \"Valgrind does not support this platform.\" + #endif + + int main() {} + " Valgrind_WORKS) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Valgrind + REQUIRED_VARS Valgrind_INCLUDE_DIR Valgrind_WORKS +) + +mark_as_advanced( + Valgrind_INCLUDE_DIR +) diff --git a/src/secp256k1/cmake/GeneratePkgConfigFile.cmake b/src/secp256k1/cmake/GeneratePkgConfigFile.cmake new file mode 100644 index 000000000..9c1d7f1dd --- /dev/null +++ b/src/secp256k1/cmake/GeneratePkgConfigFile.cmake @@ -0,0 +1,8 @@ +function(generate_pkg_config_file in_file) + set(prefix ${CMAKE_INSTALL_PREFIX}) + set(exec_prefix \${prefix}) + set(libdir \${exec_prefix}/${CMAKE_INSTALL_LIBDIR}) + set(includedir \${prefix}/${CMAKE_INSTALL_INCLUDEDIR}) + set(PACKAGE_VERSION ${PROJECT_VERSION}) + configure_file(${in_file} ${PROJECT_NAME}.pc @ONLY) +endfunction() diff --git a/src/secp256k1/cmake/TryAppendCFlags.cmake b/src/secp256k1/cmake/TryAppendCFlags.cmake new file mode 100644 index 000000000..1d81a9317 --- /dev/null +++ b/src/secp256k1/cmake/TryAppendCFlags.cmake @@ -0,0 +1,24 @@ +include(CheckCCompilerFlag) + +function(secp256k1_check_c_flags_internal flags output) + string(MAKE_C_IDENTIFIER "${flags}" result) + string(TOUPPER "${result}" result) + set(result "C_SUPPORTS_${result}") + if(NOT MSVC) + set(CMAKE_REQUIRED_FLAGS "-Werror") + endif() + + # This avoids running a linker. + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + check_c_compiler_flag("${flags}" ${result}) + + set(${output} ${${result}} PARENT_SCOPE) +endfunction() + +# Append flags to the COMPILE_OPTIONS directory property if CC accepts them. +macro(try_append_c_flags) + secp256k1_check_c_flags_internal("${ARGV}" result) + if(result) + add_compile_options(${ARGV}) + endif() +endmacro() diff --git a/src/secp256k1/cmake/arm-linux-gnueabihf.toolchain.cmake b/src/secp256k1/cmake/arm-linux-gnueabihf.toolchain.cmake new file mode 100644 index 000000000..0d91912b6 --- /dev/null +++ b/src/secp256k1/cmake/arm-linux-gnueabihf.toolchain.cmake @@ -0,0 +1,3 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) diff --git a/src/secp256k1/cmake/config.cmake.in b/src/secp256k1/cmake/config.cmake.in new file mode 100644 index 000000000..46b180ab1 --- /dev/null +++ b/src/secp256k1/cmake/config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") + +check_required_components(@PROJECT_NAME@) diff --git a/src/secp256k1/cmake/source_arm32.s b/src/secp256k1/cmake/source_arm32.s new file mode 100644 index 000000000..0bbd22d1c --- /dev/null +++ b/src/secp256k1/cmake/source_arm32.s @@ -0,0 +1,9 @@ +.syntax unified +.eabi_attribute 24, 1 +.eabi_attribute 25, 1 +.text +.global main +main: + ldr r0, =0x002A + mov r7, #1 + swi 0 diff --git a/src/secp256k1/cmake/x86_64-w64-mingw32.toolchain.cmake b/src/secp256k1/cmake/x86_64-w64-mingw32.toolchain.cmake new file mode 100644 index 000000000..96119b72d --- /dev/null +++ b/src/secp256k1/cmake/x86_64-w64-mingw32.toolchain.cmake @@ -0,0 +1,3 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac index e5fcbcb4e..6c4c11ddc 100644 --- a/src/secp256k1/configure.ac +++ b/src/secp256k1/configure.ac @@ -1,203 +1,286 @@ AC_PREREQ([2.60]) -AC_INIT([libsecp256k1],[0.1]) + +# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of +# the API. All changes in experimental modules are treated as +# backwards-compatible and therefore at most increase the minor version. +define(_PKG_VERSION_MAJOR, 0) +define(_PKG_VERSION_MINOR, 5) +define(_PKG_VERSION_PATCH, 1) +define(_PKG_VERSION_IS_RELEASE, true) + +# The library version is based on libtool versioning of the ABI. The set of +# rules for updating the version can be found here: +# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +# All changes in experimental modules are treated as if they don't affect the +# interface and therefore only increase the revision. +define(_LIB_VERSION_CURRENT, 4) +define(_LIB_VERSION_REVISION, 1) +define(_LIB_VERSION_AGE, 2) + +AC_INIT([libsecp256k1],m4_join([.], _PKG_VERSION_MAJOR, _PKG_VERSION_MINOR, _PKG_VERSION_PATCH)m4_if(_PKG_VERSION_IS_RELEASE, [true], [], [-dev]),[https://github.com/bitcoin-core/secp256k1/issues],[libsecp256k1],[https://github.com/bitcoin-core/secp256k1]) + AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux/m4]) AC_CANONICAL_HOST -AH_TOP([#ifndef LIBSECP256K1_CONFIG_H]) -AH_TOP([#define LIBSECP256K1_CONFIG_H]) -AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/]) -AM_INIT_AUTOMAKE([foreign subdir-objects]) -LT_INIT - -dnl make the compilation flags quiet unless V=1 is used -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -PKG_PROG_PKG_CONFIG +# Require Automake 1.11.2 for AM_PROG_AR +AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects]) -AC_PATH_TOOL(AR, ar) -AC_PATH_TOOL(RANLIB, ranlib) -AC_PATH_TOOL(STRIP, strip) -AX_PROG_CC_FOR_BUILD +# Make the compilation flags quiet unless V=1 is used. +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -if test "x$CFLAGS" = "x"; then - CFLAGS="-g" +if test "${CFLAGS+set}" = "set"; then + CFLAGS_overridden=yes +else + CFLAGS_overridden=no fi +AC_PROG_CC +AM_PROG_AS +AM_PROG_AR -AM_PROG_CC_C_O +# Clear some cache variables as a workaround for a bug that appears due to a bad +# interaction between AM_PROG_AR and LT_INIT when combining MSVC's archiver lib.exe. +# https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54421 +AS_UNSET(ac_cv_prog_AR) +AS_UNSET(ac_cv_prog_ac_ct_AR) +LT_INIT([win32-dll]) -AC_PROG_CC_C89 -if test x"$ac_cv_prog_cc_c89" = x"no"; then - AC_MSG_ERROR([c89 compiler support required]) -fi -AM_PROG_AS +build_windows=no case $host_os in *darwin*) if test x$cross_compiling != xyes; then - AC_PATH_PROG([BREW],brew,) - if test x$BREW != x; then - dnl These Homebrew packages may be keg-only, meaning that they won't be found - dnl in expected paths because they may conflict with system files. Ask - dnl Homebrew where each one is located, then adjust paths accordingly. - - openssl_prefix=`$BREW --prefix openssl 2>/dev/null` - gmp_prefix=`$BREW --prefix gmp 2>/dev/null` - if test x$openssl_prefix != x; then - PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" - export PKG_CONFIG_PATH - fi - if test x$gmp_prefix != x; then - GMP_CPPFLAGS="-I$gmp_prefix/include" - GMP_LIBS="-L$gmp_prefix/lib" + AC_CHECK_PROG([BREW], brew, brew) + if test x$BREW = xbrew; then + # These Homebrew packages may be keg-only, meaning that they won't be found + # in expected paths because they may conflict with system files. Ask + # Homebrew where each one is located, then adjust paths accordingly. + if $BREW list --versions valgrind >/dev/null; then + valgrind_prefix=$($BREW --prefix valgrind 2>/dev/null) + VALGRIND_CPPFLAGS="-I$valgrind_prefix/include" fi else - AC_PATH_PROG([PORT],port,) - dnl if homebrew isn't installed and macports is, add the macports default paths - dnl as a last resort. - if test x$PORT != x; then + AC_CHECK_PROG([PORT], port, port) + # If homebrew isn't installed and macports is, add the macports default paths + # as a last resort. + if test x$PORT = xport; then CPPFLAGS="$CPPFLAGS -isystem /opt/local/include" LDFLAGS="$LDFLAGS -L/opt/local/lib" fi fi fi ;; + cygwin*|mingw*) + build_windows=yes + ;; esac -CFLAGS="$CFLAGS -W" - -warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings" -saved_CFLAGS="$CFLAGS" -CFLAGS="$CFLAGS $warn_CFLAGS" -AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}]) -AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], - [ AC_MSG_RESULT([yes]) ], - [ AC_MSG_RESULT([no]) - CFLAGS="$saved_CFLAGS" - ]) - -saved_CFLAGS="$CFLAGS" -CFLAGS="$CFLAGS -fvisibility=hidden" -AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) -AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], - [ AC_MSG_RESULT([yes]) ], - [ AC_MSG_RESULT([no]) - CFLAGS="$saved_CFLAGS" - ]) +# Try if some desirable compiler flags are supported and append them to SECP_CFLAGS. +# +# These are our own flags, so we append them to our own SECP_CFLAGS variable (instead of CFLAGS) as +# recommended in the automake manual (Section "Flag Variables Ordering"). CFLAGS belongs to the user +# and we are not supposed to touch it. In the Makefile, we will need to ensure that SECP_CFLAGS +# is prepended to CFLAGS when invoking the compiler so that the user always has the last word (flag). +# +# Another advantage of not touching CFLAGS is that the contents of CFLAGS will be picked up by +# libtool for compiling helper executables. For example, when compiling for Windows, libtool will +# generate entire wrapper executables (instead of simple wrapper scripts as on Unix) to ensure +# proper operation of uninstalled programs linked by libtool against the uninstalled shared library. +# These executables are compiled from C source file for which our flags may not be appropriate, +# e.g., -std=c89 flag has lead to undesirable warnings in the past. +# +# TODO We should analogously not touch CPPFLAGS and LDFLAGS but currently there are no issues. +AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [ + # GCC and compatible (incl. clang) + if test "x$GCC" = "xyes"; then + # Try to append -Werror to CFLAGS temporarily. Otherwise checks for some unsupported + # flags will succeed. + # Note that failure to append -Werror does not necessarily mean that -Werror is not + # supported. The compiler may already be warning about something unrelated, for example + # about some path issue. If that is the case, -Werror cannot be used because all + # of those warnings would be turned into errors. + SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS" + SECP_TRY_APPEND_CFLAGS([-Werror], CFLAGS) + + SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic. + SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic. + SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers + SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall. + SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions. + SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95 + SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0 + SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only + SECP_TRY_APPEND_CFLAGS([-Wreserved-identifier], $1) # Clang >= 13.0 only + SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0 + + CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS" + fi + + # MSVC + # Assume MSVC if we're building for Windows but not with GCC or compatible; + # libtool makes the same assumption internally. + # Note that "/opt" and "-opt" are equivalent for MSVC; we use "-opt" because "/opt" looks like a path. + if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then + SECP_TRY_APPEND_CFLAGS([-W3], $1) # Production quality warning level. + SECP_TRY_APPEND_CFLAGS([-wd4146], $1) # Disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned". + SECP_TRY_APPEND_CFLAGS([-wd4244], $1) # Disable warning C4244 "'conversion' conversion from 'type1' to 'type2', possible loss of data". + SECP_TRY_APPEND_CFLAGS([-wd4267], $1) # Disable warning C4267 "'var' : conversion from 'size_t' to 'type', possible loss of data". + # Eliminate deprecation warnings for the older, less secure functions. + CPPFLAGS="-D_CRT_SECURE_NO_WARNINGS $CPPFLAGS" + fi +]) +SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS) + +### +### Define config arguments +### + +# In dev mode, we enable all binaries and modules by default but individual options can still be overridden explicitly. +# Check for dev mode first because SECP_SET_DEFAULT needs enable_dev_mode set. +AC_ARG_ENABLE(dev_mode, [], [], + [enable_dev_mode=no]) AC_ARG_ENABLE(benchmark, - AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]), - [use_benchmark=$enableval], - [use_benchmark=no]) + AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), [], + [SECP_SET_DEFAULT([enable_benchmark], [yes], [yes])]) AC_ARG_ENABLE(coverage, - AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), - [enable_coverage=$enableval], - [enable_coverage=no]) + AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]), [], + [SECP_SET_DEFAULT([enable_coverage], [no], [no])]) AC_ARG_ENABLE(tests, - AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), - [use_tests=$enableval], - [use_tests=yes]) + AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [], + [SECP_SET_DEFAULT([enable_tests], [yes], [yes])]) -AC_ARG_ENABLE(openssl_tests, - AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]), - [enable_openssl_tests=$enableval], - [enable_openssl_tests=auto]) +AC_ARG_ENABLE(ctime_tests, + AS_HELP_STRING([--enable-ctime-tests],[compile constant-time tests [default=yes if valgrind enabled]]), [], + [SECP_SET_DEFAULT([enable_ctime_tests], [auto], [auto])]) AC_ARG_ENABLE(experimental, - AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), - [use_experimental=$enableval], - [use_experimental=no]) + AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [], + [SECP_SET_DEFAULT([enable_experimental], [no], [yes])]) AC_ARG_ENABLE(exhaustive_tests, - AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]), - [use_exhaustive_tests=$enableval], - [use_exhaustive_tests=yes]) + AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), [], + [SECP_SET_DEFAULT([enable_exhaustive_tests], [yes], [yes])]) -AC_ARG_ENABLE(endomorphism, - AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]), - [use_endomorphism=$enableval], - [use_endomorphism=no]) - -AC_ARG_ENABLE(ecmult_static_precomputation, - AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]), - [use_ecmult_static_precomputation=$enableval], - [use_ecmult_static_precomputation=auto]) +AC_ARG_ENABLE(examples, + AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [], + [SECP_SET_DEFAULT([enable_examples], [no], [yes])]) AC_ARG_ENABLE(module_ecdh, - AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]), - [enable_module_ecdh=$enableval], - [enable_module_ecdh=no]) + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=yes]]), [], + [SECP_SET_DEFAULT([enable_module_ecdh], [yes], [yes])]) AC_ARG_ENABLE(module_recovery, - AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]), - [enable_module_recovery=$enableval], - [enable_module_recovery=no]) - -AC_ARG_ENABLE(jni, - AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]), - [use_jni=$enableval], - [use_jni=auto]) - -AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], -[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) - -AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto], -[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto]) - -AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto], -[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto]) - -AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto] -[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto]) - -AC_CHECK_TYPES([__int128]) + AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [], + [SECP_SET_DEFAULT([enable_module_recovery], [no], [yes])]) + +AC_ARG_ENABLE(module_extrakeys, + AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module [default=yes]]), [], + [SECP_SET_DEFAULT([enable_module_extrakeys], [yes], [yes])]) + +AC_ARG_ENABLE(module_schnorrsig, + AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=yes]]), [], + [SECP_SET_DEFAULT([enable_module_schnorrsig], [yes], [yes])]) + +AC_ARG_ENABLE(module_ellswift, + AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module [default=yes]]), [], + [SECP_SET_DEFAULT([enable_module_ellswift], [yes], [yes])]) + +AC_ARG_ENABLE(external_default_callbacks, + AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], + [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) + +# Test-only override of the (autodetected by the C code) "widemul" setting. +# Legal values are: +# * int64 (for [u]int64_t), +# * int128 (for [unsigned] __int128), +# * int128_struct (for int128 implemented as a structure), +# * and auto (the default). +AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto]) + +AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm32|no|auto], +[assembly to use (experimental: arm32) [default=auto]])],[req_asm=$withval], [req_asm=auto]) + +AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE], +[window size for ecmult precomputation for verification, specified as integer in range [2..24].] +[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.] +[The table will store 2^(SIZE-1) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.] +[A window size larger than 15 will require you delete the prebuilt precomputed_ecmult.c file so that it can be rebuilt.] +[For very large window sizes, use "make -j 1" to reduce memory use during compilation.] +[The default value is a reasonable setting for desktop machines (currently 15). [default=15]] +)], +[set_ecmult_window=$withval], [set_ecmult_window=15]) + +AC_ARG_WITH([ecmult-gen-kb], [AS_HELP_STRING([--with-ecmult-gen-kb=2|22|86], +[The size of the precomputed table for signing in multiples of 1024 bytes (on typical platforms).] +[Larger values result in possibly better signing/keygeneration performance at the cost of a larger table.] +[The default value is a reasonable setting for desktop machines (currently 86). [default=86]] +)], +[set_ecmult_gen_kb=$withval], [set_ecmult_gen_kb=86]) + +AC_ARG_WITH([valgrind], [AS_HELP_STRING([--with-valgrind=yes|no|auto], +[Build with extra checks for running inside Valgrind [default=auto]] +)], +[req_valgrind=$withval], [req_valgrind=auto]) + +### +### Handle config options (except for modules) +### + +if test x"$req_valgrind" = x"no"; then + enable_valgrind=no +else + SECP_VALGRIND_CHECK + if test x"$has_valgrind" != x"yes"; then + if test x"$req_valgrind" = x"yes"; then + AC_MSG_ERROR([Valgrind support explicitly requested but valgrind/memcheck.h header not available]) + fi + enable_valgrind=no + else + enable_valgrind=yes + fi +fi -AC_MSG_CHECKING([for __builtin_expect]) -AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])], - [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ], - [ AC_MSG_RESULT([no]) - ]) +if test x"$enable_ctime_tests" = x"auto"; then + enable_ctime_tests=$enable_valgrind +fi -if test x"$enable_coverage" = x"yes"; then - AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code]) - CFLAGS="$CFLAGS -O0 --coverage" - LDFLAGS="--coverage" -else - CFLAGS="$CFLAGS -O3" +print_msan_notice=no +if test x"$enable_ctime_tests" = x"yes"; then + SECP_MSAN_CHECK + # MSan on Clang >=16 reports unitialized memory in function parameters and return values, even if + # the uninitalized variable is never actually "used". This is called "eager" checking, and it's + # sounds like good idea for normal use of MSan. However, it yields many false positives in the + # ctime_tests because many return values depend on secret (i.e., "uninitialized") values, and + # we're only interested in detecting branches (which count as "uses") on secret data. + if test x"$msan_enabled" = x"yes"; then + SECP_TRY_APPEND_CFLAGS([-fno-sanitize-memory-param-retval], SECP_CFLAGS) + print_msan_notice=yes + fi fi -if test x"$use_ecmult_static_precomputation" != x"no"; then - save_cross_compiling=$cross_compiling - cross_compiling=no - TEMP_CC="$CC" - CC="$CC_FOR_BUILD" - AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}]) - AC_RUN_IFELSE( - [AC_LANG_PROGRAM([], [return 0])], - [working_native_cc=yes], - [working_native_cc=no],[dnl]) - CC="$TEMP_CC" - cross_compiling=$save_cross_compiling - - if test x"$working_native_cc" = x"no"; then - set_precomp=no - if test x"$use_ecmult_static_precomputation" = x"yes"; then - AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) - else - AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) +if test x"$enable_coverage" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOVERAGE=1" + SECP_CFLAGS="-O0 --coverage $SECP_CFLAGS" + # If coverage is enabled, and the user has not overridden CFLAGS, + # override Autoconf's value "-g -O2" with "-g". Otherwise we'd end up + # with "-O0 --coverage -g -O2". + if test "$CFLAGS_overridden" = "no"; then + CFLAGS="-g" fi - else - AC_MSG_RESULT([ok]) - set_precomp=yes - fi + LDFLAGS="--coverage $LDFLAGS" else - set_precomp=no + # Most likely the CFLAGS already contain -O2 because that is autoconf's default. + # We still add it here because passing it twice is not an issue, and handling + # this case would just add unnecessary complexity (see #896). + SECP_CFLAGS="-O2 $SECP_CFLAGS" fi if test x"$req_asm" = x"auto"; then - SECP_64BIT_ASM_CHECK - if test x"$has_64bit_asm" = x"yes"; then + SECP_X86_64_ASM_CHECK + if test x"$has_x86_64_asm" = x"yes"; then set_asm=x86_64 fi if test x"$set_asm" = x; then @@ -207,287 +290,214 @@ else set_asm=$req_asm case $set_asm in x86_64) - SECP_64BIT_ASM_CHECK - if test x"$has_64bit_asm" != x"yes"; then - AC_MSG_ERROR([x86_64 assembly optimization requested but not available]) - fi - ;; - arm) - ;; - no) - ;; - *) - AC_MSG_ERROR([invalid assembly optimization selection]) - ;; - esac -fi - -if test x"$req_field" = x"auto"; then - if test x"set_asm" = x"x86_64"; then - set_field=64bit - fi - if test x"$set_field" = x; then - SECP_INT128_CHECK - if test x"$has_int128" = x"yes"; then - set_field=64bit - fi - fi - if test x"$set_field" = x; then - set_field=32bit - fi -else - set_field=$req_field - case $set_field in - 64bit) - if test x"$set_asm" != x"x86_64"; then - SECP_INT128_CHECK - if test x"$has_int128" != x"yes"; then - AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available]) - fi - fi - ;; - 32bit) - ;; - *) - AC_MSG_ERROR([invalid field implementation selection]) - ;; - esac -fi - -if test x"$req_scalar" = x"auto"; then - SECP_INT128_CHECK - if test x"$has_int128" = x"yes"; then - set_scalar=64bit - fi - if test x"$set_scalar" = x; then - set_scalar=32bit - fi -else - set_scalar=$req_scalar - case $set_scalar in - 64bit) - SECP_INT128_CHECK - if test x"$has_int128" != x"yes"; then - AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available]) + SECP_X86_64_ASM_CHECK + if test x"$has_x86_64_asm" != x"yes"; then + AC_MSG_ERROR([x86_64 assembly requested but not available]) fi ;; - 32bit) - ;; - *) - AC_MSG_ERROR([invalid scalar implementation selected]) - ;; - esac -fi - -if test x"$req_bignum" = x"auto"; then - SECP_GMP_CHECK - if test x"$has_gmp" = x"yes"; then - set_bignum=gmp - fi - - if test x"$set_bignum" = x; then - set_bignum=no - fi -else - set_bignum=$req_bignum - case $set_bignum in - gmp) - SECP_GMP_CHECK - if test x"$has_gmp" != x"yes"; then - AC_MSG_ERROR([gmp bignum explicitly requested but libgmp not available]) + arm32) + SECP_ARM32_ASM_CHECK + if test x"$has_arm32_asm" != x"yes"; then + AC_MSG_ERROR([ARM32 assembly requested but not available]) fi ;; no) ;; *) - AC_MSG_ERROR([invalid bignum implementation selection]) + AC_MSG_ERROR([invalid assembly selection]) ;; esac fi -# select assembly optimization -use_external_asm=no +# Select assembly +enable_external_asm=no case $set_asm in x86_64) - AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_ASM_X86_64=1" ;; -arm) - use_external_asm=yes +arm32) + enable_external_asm=yes ;; no) ;; *) - AC_MSG_ERROR([invalid assembly optimizations]) + AC_MSG_ERROR([invalid assembly selection]) ;; esac -# select field implementation -case $set_field in -64bit) - AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation]) +if test x"$enable_external_asm" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_ASM=1" +fi + + +# Select wide multiplication implementation +case $set_widemul in +int128_struct) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128_STRUCT=1" + ;; +int128) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT128=1" ;; -32bit) - AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation]) +int64) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_FORCE_WIDEMUL_INT64=1" + ;; +auto) ;; *) - AC_MSG_ERROR([invalid field implementation]) + AC_MSG_ERROR([invalid wide multiplication implementation]) ;; esac -# select bignum implementation -case $set_bignum in -gmp) - AC_DEFINE(HAVE_LIBGMP, 1, [Define this symbol if libgmp is installed]) - AC_DEFINE(USE_NUM_GMP, 1, [Define this symbol to use the gmp implementation for num]) - AC_DEFINE(USE_FIELD_INV_NUM, 1, [Define this symbol to use the num-based field inverse implementation]) - AC_DEFINE(USE_SCALAR_INV_NUM, 1, [Define this symbol to use the num-based scalar inverse implementation]) - ;; -no) - AC_DEFINE(USE_NUM_NONE, 1, [Define this symbol to use no num implementation]) - AC_DEFINE(USE_FIELD_INV_BUILTIN, 1, [Define this symbol to use the native field inverse implementation]) - AC_DEFINE(USE_SCALAR_INV_BUILTIN, 1, [Define this symbol to use the native scalar inverse implementation]) +error_window_size=['window size for ecmult precomputation not an integer in range [2..24]'] +case $set_ecmult_window in +''|*[[!0-9]]*) + # no valid integer + AC_MSG_ERROR($error_window_size) ;; *) - AC_MSG_ERROR([invalid bignum implementation]) + if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then + # not in range + AC_MSG_ERROR($error_window_size) + fi + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DECMULT_WINDOW_SIZE=$set_ecmult_window" ;; esac -#select scalar implementation -case $set_scalar in -64bit) - AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation]) +case $set_ecmult_gen_kb in +2) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=2 -DCOMB_TEETH=5" + ;; +22) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=11 -DCOMB_TEETH=6" ;; -32bit) - AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation]) +86) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOMB_BLOCKS=43 -DCOMB_TEETH=6" ;; *) - AC_MSG_ERROR([invalid scalar implementation]) + AC_MSG_ERROR(['ecmult gen table size not 2, 22 or 86']) ;; esac -if test x"$use_tests" = x"yes"; then - SECP_OPENSSL_CHECK - if test x"$has_openssl_ec" = x"yes"; then - if test x"$enable_openssl_tests" != x"no"; then - AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available]) - SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS" - SECP_TEST_LIBS="$CRYPTO_LIBS" - - case $host in - *mingw*) - SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32" - ;; - esac - fi - else - if test x"$enable_openssl_tests" = x"yes"; then - AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available]) - fi - fi -else - if test x"$enable_openssl_tests" = x"yes"; then - AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled]) - fi +if test x"$enable_valgrind" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES $VALGRIND_CPPFLAGS -DVALGRIND" fi -if test x"$use_jni" != x"no"; then - AX_JNI_INCLUDE_DIR - have_jni_dependencies=yes - if test x"$enable_module_ecdh" = x"no"; then - have_jni_dependencies=no - fi - if test "x$JNI_INCLUDE_DIRS" = "x"; then - have_jni_dependencies=no - fi - if test "x$have_jni_dependencies" = "xno"; then - if test x"$use_jni" = x"yes"; then - AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.]) - fi - AC_MSG_WARN([jni headers/dependencies not found. jni support disabled]) - use_jni=no - else - use_jni=yes - for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do - JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR" - done - fi -fi +# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI). +# We don't want to set the user variable CFLAGS in CI because this would disable +# autoconf's logic for setting default CFLAGS, which we would like to test in CI. +SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" -if test x"$set_bignum" = x"gmp"; then - SECP_LIBS="$SECP_LIBS $GMP_LIBS" - SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS" -fi +### +### Handle module options +### -if test x"$use_endomorphism" = x"yes"; then - AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization]) +# Processing must be done in a reverse topological sorting of the dependency graph +# (dependent module first). +if test x"$enable_module_ellswift" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ELLSWIFT=1" fi -if test x"$set_precomp" = x"yes"; then - AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table]) +if test x"$enable_module_schnorrsig" = x"yes"; then + if test x"$enable_module_extrakeys" = x"no"; then + AC_MSG_ERROR([Module dependency error: You have disabled the extrakeys module explicitly, but it is required by the schnorrsig module.]) + fi + enable_module_extrakeys=yes + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_SCHNORRSIG=1" fi -if test x"$enable_module_ecdh" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) +if test x"$enable_module_extrakeys" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_EXTRAKEYS=1" fi if test x"$enable_module_recovery" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_RECOVERY=1" fi -AC_C_BIGENDIAN() +if test x"$enable_module_ecdh" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DENABLE_MODULE_ECDH=1" +fi -if test x"$use_external_asm" = x"yes"; then - AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) +if test x"$enable_external_default_callbacks" = x"yes"; then + SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DUSE_EXTERNAL_DEFAULT_CALLBACKS=1" fi -AC_MSG_NOTICE([Using static precomputation: $set_precomp]) -AC_MSG_NOTICE([Using assembly optimizations: $set_asm]) -AC_MSG_NOTICE([Using field implementation: $set_field]) -AC_MSG_NOTICE([Using bignum implementation: $set_bignum]) -AC_MSG_NOTICE([Using scalar implementation: $set_scalar]) -AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism]) -AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage]) -AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) -AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery]) -AC_MSG_NOTICE([Using jni: $use_jni]) +### +### Check for --enable-experimental if necessary +### -if test x"$enable_experimental" = x"yes"; then - AC_MSG_NOTICE([******]) - AC_MSG_NOTICE([WARNING: experimental build]) - AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) - AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) - AC_MSG_NOTICE([******]) -else - if test x"$enable_module_ecdh" = x"yes"; then - AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) - fi - if test x"$set_asm" = x"arm"; then - AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) +if test x"$enable_experimental" = x"no"; then + if test x"$set_asm" = x"arm32"; then + AC_MSG_ERROR([ARM32 assembly is experimental. Use --enable-experimental to allow.]) fi fi -AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) +### +### Generate output +### + AC_CONFIG_FILES([Makefile libsecp256k1.pc]) -AC_SUBST(JNI_INCLUDES) -AC_SUBST(SECP_INCLUDES) -AC_SUBST(SECP_LIBS) -AC_SUBST(SECP_TEST_LIBS) -AC_SUBST(SECP_TEST_INCLUDES) +AC_SUBST(SECP_CFLAGS) +AC_SUBST(SECP_CONFIG_DEFINES) AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"]) -AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) -AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) -AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) -AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) +AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"]) +AM_CONDITIONAL([USE_CTIME_TESTS], [test x"$enable_ctime_tests" = x"yes"]) +AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"]) +AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"]) +AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) -AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"]) -AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) -AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) - -dnl make sure nothing new is exported so that we don't break the cache -PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" -unset PKG_CONFIG_PATH -PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" +AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"]) +AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) +AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm32"]) +AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) +AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT) +AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION) +AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE) AC_OUTPUT + +echo +echo "Build Options:" +echo " with external callbacks = $enable_external_default_callbacks" +echo " with benchmarks = $enable_benchmark" +echo " with tests = $enable_tests" +echo " with ctime tests = $enable_ctime_tests" +echo " with coverage = $enable_coverage" +echo " with examples = $enable_examples" +echo " module ecdh = $enable_module_ecdh" +echo " module recovery = $enable_module_recovery" +echo " module extrakeys = $enable_module_extrakeys" +echo " module schnorrsig = $enable_module_schnorrsig" +echo " module ellswift = $enable_module_ellswift" +echo +echo " asm = $set_asm" +echo " ecmult window size = $set_ecmult_window" +echo " ecmult gen table size = $set_ecmult_gen_kb KiB" +# Hide test-only options unless they're used. +if test x"$set_widemul" != xauto; then +echo " wide multiplication = $set_widemul" +fi +echo +echo " valgrind = $enable_valgrind" +echo " CC = $CC" +echo " CPPFLAGS = $CPPFLAGS" +echo " SECP_CFLAGS = $SECP_CFLAGS" +echo " CFLAGS = $CFLAGS" +echo " LDFLAGS = $LDFLAGS" + +if test x"$print_msan_notice" = x"yes"; then + echo + echo "Note:" + echo " MemorySanitizer detected, tried to add -fno-sanitize-memory-param-retval to SECP_CFLAGS" + echo " to avoid false positives in ctime_tests. Pass --disable-ctime-tests to avoid this." +fi + +if test x"$enable_experimental" = x"yes"; then + echo + echo "WARNING: Experimental build" + echo " Experimental features do not have stable APIs or properties, and may not be safe for" + echo " production use." +fi diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c index 200dcff75..bf562303e 100644 --- a/src/secp256k1/contrib/lax_der_parsing.c +++ b/src/secp256k1/contrib/lax_der_parsing.c @@ -1,11 +1,10 @@ -/************************************************************************ - * Copyright (c) 2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #include -#include #include "lax_der_parsing.h" @@ -32,7 +31,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; - if (pos + lenbyte > inputlen) { + if (lenbyte > inputlen - pos) { return 0; } pos += lenbyte; @@ -51,7 +50,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; - if (pos + lenbyte > inputlen) { + if (lenbyte > inputlen - pos) { return 0; } while (lenbyte > 0 && input[pos] == 0) { @@ -89,7 +88,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; - if (pos + lenbyte > inputlen) { + if (lenbyte > inputlen - pos) { return 0; } while (lenbyte > 0 && input[pos] == 0) { @@ -112,7 +111,6 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ return 0; } spos = pos; - pos += slen; /* Ignore leading zeroes in R */ while (rlen > 0 && input[rpos] == 0) { @@ -122,7 +120,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ /* Copy R value */ if (rlen > 32) { overflow = 1; - } else { + } else if (rlen) { memcpy(tmpsig + 32 - rlen, input + rpos, rlen); } @@ -134,7 +132,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ /* Copy S value */ if (slen > 32) { overflow = 1; - } else { + } else if (slen) { memcpy(tmpsig + 64 - slen, input + spos, slen); } diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h index 4287bc6af..37c8c691f 100644 --- a/src/secp256k1/contrib/lax_der_parsing.h +++ b/src/secp256k1/contrib/lax_der_parsing.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ /**** * Please do not link this file directly. It is not part of the libsecp256k1 @@ -51,7 +51,13 @@ #ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H #define SECP256K1_CONTRIB_LAX_DER_PARSING_H +/* #include secp256k1.h only when it hasn't been included yet. + This enables this file to be #included directly in other project + files (such as tests.c) without the need to set an explicit -I flag, + which would be necessary to locate secp256k1.h. */ +#ifndef SECP256K1_H #include +#endif #ifdef __cplusplus extern "C" { @@ -61,8 +67,8 @@ extern "C" { * * Returns: 1 when the signature could be parsed, 0 otherwise. * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input: a pointer to the signature to be parsed + * Out: sig: pointer to a signature object + * In: input: pointer to the signature to be parsed * inputlen: the length of the array pointed to be input * * This function will accept any valid DER encoded signature, even if the diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.c b/src/secp256k1/contrib/lax_der_privatekey_parsing.c index f7f4fdca7..a1b820007 100644 --- a/src/secp256k1/contrib/lax_der_privatekey_parsing.c +++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.c @@ -1,11 +1,10 @@ -/************************************************************************ - * Copyright (c) 2014, 2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #include -#include #include "lax_der_privatekey_parsing.h" @@ -45,7 +44,7 @@ int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, co if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) { return 0; } - memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]); + if (privkey[1]) memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]); if (!secp256k1_ec_seckey_verify(ctx, out32)) { memset(out32, 0, 32); return 0; diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/secp256k1/contrib/lax_der_privatekey_parsing.h index 6a652b0d1..3749e418f 100644 --- a/src/secp256k1/contrib/lax_der_privatekey_parsing.h +++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2014, 2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ /**** * Please do not link this file directly. It is not part of the libsecp256k1 @@ -28,7 +28,13 @@ #ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H #define SECP256K1_CONTRIB_BER_PRIVATEKEY_H +/* #include secp256k1.h only when it hasn't been included yet. + This enables this file to be #included directly in other project + files (such as tests.c) without the need to set an explicit -I flag, + which would be necessary to locate secp256k1.h. */ +#ifndef SECP256K1_H #include +#endif #ifdef __cplusplus extern "C" { @@ -37,8 +43,7 @@ extern "C" { /** Export a private key in DER format. * * Returns: 1 if the private key was valid. - * Args: ctx: pointer to a context object, initialized for signing (cannot - * be NULL) + * Args: ctx: pointer to a context object (not secp256k1_context_static). * Out: privkey: pointer to an array for storing the private key in BER. * Should have space for 279 bytes, and cannot be NULL. * privkeylen: Pointer to an int where the length of the private key in diff --git a/src/secp256k1/doc/ellswift.md b/src/secp256k1/doc/ellswift.md new file mode 100644 index 000000000..9d60e6be0 --- /dev/null +++ b/src/secp256k1/doc/ellswift.md @@ -0,0 +1,483 @@ +# ElligatorSwift for secp256k1 explained + +In this document we explain how the `ellswift` module implementation is related to the +construction in the +["SwiftEC: Shallue–van de Woestijne Indifferentiable Function To Elliptic Curves"](https://eprint.iacr.org/2022/759) +paper by Jorge Chávez-Saab, Francisco Rodríguez-Henríquez, and Mehdi Tibouchi. + +* [1. Introduction](#1-introduction) +* [2. The decoding function](#2-the-decoding-function) + + [2.1 Decoding for `secp256k1`](#21-decoding-for-secp256k1) +* [3. The encoding function](#3-the-encoding-function) + + [3.1 Switching to *v, w* coordinates](#31-switching-to-v-w-coordinates) + + [3.2 Avoiding computing all inverses](#32-avoiding-computing-all-inverses) + + [3.3 Finding the inverse](#33-finding-the-inverse) + + [3.4 Dealing with special cases](#34-dealing-with-special-cases) + + [3.5 Encoding for `secp256k1`](#35-encoding-for-secp256k1) +* [4. Encoding and decoding full *(x, y)* coordinates](#4-encoding-and-decoding-full-x-y-coordinates) + + [4.1 Full *(x, y)* coordinates for `secp256k1`](#41-full-x-y-coordinates-for-secp256k1) + +## 1. Introduction + +The `ellswift` module effectively introduces a new 64-byte public key format, with the property +that (uniformly random) public keys can be encoded as 64-byte arrays which are computationally +indistinguishable from uniform byte arrays. The module provides functions to convert public keys +from and to this format, as well as convenience functions for key generation and ECDH that operate +directly on ellswift-encoded keys. + +The encoding consists of the concatenation of two (32-byte big endian) encoded field elements $u$ +and $t.$ Together they encode an x-coordinate on the curve $x$, or (see further) a full point $(x, y)$ on +the curve. + +**Decoding** consists of decoding the field elements $u$ and $t$ (values above the field size $p$ +are taken modulo $p$), and then evaluating $F_u(t)$, which for every $u$ and $t$ results in a valid +x-coordinate on the curve. The functions $F_u$ will be defined in [Section 2](#2-the-decoding-function). + +**Encoding** a given $x$ coordinate is conceptually done as follows: +* Loop: + * Pick a uniformly random field element $u.$ + * Compute the set $L = F_u^{-1}(x)$ of $t$ values for which $F_u(t) = x$, which may have up to *8* elements. + * With probability $1 - \dfrac{\\#L}{8}$, restart the loop. + * Select a uniformly random $t \in L$ and return $(u, t).$ + +This is the *ElligatorSwift* algorithm, here given for just x-coordinates. An extension to full +$(x, y)$ points will be given in [Section 4](#4-encoding-and-decoding-full-x-y-coordinates). +The algorithm finds a uniformly random $(u, t)$ among (almost all) those +for which $F_u(t) = x.$ Section 3.2 in the paper proves that the number of such encodings for +almost all x-coordinates on the curve (all but at most 39) is close to two times the field size +(specifically, it lies in the range $2q \pm (22\sqrt{q} + O(1))$, where $q$ is the size of the field). + +## 2. The decoding function + +First some definitions: +* $\mathbb{F}$ is the finite field of size $q$, of characteristic 5 or more, and $q \equiv 1 \mod 3.$ + * For `secp256k1`, $q = 2^{256} - 2^{32} - 977$, which satisfies that requirement. +* Let $E$ be the elliptic curve of points $(x, y) \in \mathbb{F}^2$ for which $y^2 = x^3 + ax + b$, with $a$ and $b$ + public constants, for which $\Delta_E = -16(4a^3 + 27b^2)$ is a square, and at least one of $(-b \pm \sqrt{-3 \Delta_E} / 36)/2$ is a square. + This implies that the order of $E$ is either odd, or a multiple of *4*. + If $a=0$, this condition is always fulfilled. + * For `secp256k1`, $a=0$ and $b=7.$ +* Let the function $g(x) = x^3 + ax + b$, so the $E$ curve equation is also $y^2 = g(x).$ +* Let the function $h(x) = 3x^3 + 4a.$ +* Define $V$ as the set of solutions $(x_1, x_2, x_3, z)$ to $z^2 = g(x_1)g(x_2)g(x_3).$ +* Define $S_u$ as the set of solutions $(X, Y)$ to $X^2 + h(u)Y^2 = -g(u)$ and $Y \neq 0.$ +* $P_u$ is a function from $\mathbb{F}$ to $S_u$ that will be defined below. +* $\psi_u$ is a function from $S_u$ to $V$ that will be defined below. + +**Note**: In the paper: +* $F_u$ corresponds to $F_{0,u}$ there. +* $P_u(t)$ is called $P$ there. +* All $S_u$ sets together correspond to $S$ there. +* All $\psi_u$ functions together (operating on elements of $S$) correspond to $\psi$ there. + +Note that for $V$, the left hand side of the equation $z^2$ is square, and thus the right +hand must also be square. As multiplying non-squares results in a square in $\mathbb{F}$, +out of the three right-hand side factors an even number must be non-squares. +This implies that exactly *1* or exactly *3* out of +$\\{g(x_1), g(x_2), g(x_3)\\}$ must be square, and thus that for any $(x_1,x_2,x_3,z) \in V$, +at least one of $\\{x_1, x_2, x_3\\}$ must be a valid x-coordinate on $E.$ There is one exception +to this, namely when $z=0$, but even then one of the three values is a valid x-coordinate. + +**Define** the decoding function $F_u(t)$ as: +* Let $(x_1, x_2, x_3, z) = \psi_u(P_u(t)).$ +* Return the first element $x$ of $(x_3, x_2, x_1)$ which is a valid x-coordinate on $E$ (i.e., $g(x)$ is square). + +$P_u(t) = (X(u, t), Y(u, t))$, where: + +$$ +\begin{array}{lcl} +X(u, t) & = & \left\\{\begin{array}{ll} + \dfrac{g(u) - t^2}{2t} & a = 0 \\ + \dfrac{g(u) + h(u)(Y_0(u) - X_0(u)t)^2}{X_0(u)(1 + h(u)t^2)} & a \neq 0 +\end{array}\right. \\ +Y(u, t) & = & \left\\{\begin{array}{ll} + \dfrac{X(u, t) + t}{u \sqrt{-3}} = \dfrac{g(u) + t^2}{2tu\sqrt{-3}} & a = 0 \\ + Y_0(u) + t(X(u, t) - X_0(u)) & a \neq 0 +\end{array}\right. +\end{array} +$$ + +$P_u(t)$ is defined: +* For $a=0$, unless: + * $u = 0$ or $t = 0$ (division by zero) + * $g(u) = -t^2$ (would give $Y=0$). +* For $a \neq 0$, unless: + * $X_0(u) = 0$ or $h(u)t^2 = -1$ (division by zero) + * $Y_0(u) (1 - h(u)t^2) = 2X_0(u)t$ (would give $Y=0$). + +The functions $X_0(u)$ and $Y_0(u)$ are defined in Appendix A of the paper, and depend on various properties of $E.$ + +The function $\psi_u$ is the same for all curves: $\psi_u(X, Y) = (x_1, x_2, x_3, z)$, where: + +$$ +\begin{array}{lcl} + x_1 & = & \dfrac{X}{2Y} - \dfrac{u}{2} && \\ + x_2 & = & -\dfrac{X}{2Y} - \dfrac{u}{2} && \\ + x_3 & = & u + 4Y^2 && \\ + z & = & \dfrac{g(x_3)}{2Y}(u^2 + ux_1 + x_1^2 + a) = \dfrac{-g(u)g(x_3)}{8Y^3} +\end{array} +$$ + +### 2.1 Decoding for `secp256k1` + +Put together and specialized for $a=0$ curves, decoding $(u, t)$ to an x-coordinate is: + +**Define** $F_u(t)$ as: +* Let $X = \dfrac{u^3 + b - t^2}{2t}.$ +* Let $Y = \dfrac{X + t}{u\sqrt{-3}}.$ +* Return the first $x$ in $(u + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u}{2}, \dfrac{X}{2Y} - \dfrac{u}{2})$ for which $g(x)$ is square. + +To make sure that every input decodes to a valid x-coordinate, we remap the inputs in case +$P_u$ is not defined (when $u=0$, $t=0$, or $g(u) = -t^2$): + +**Define** $F_u(t)$ as: +* Let $u'=u$ if $u \neq 0$; $1$ otherwise (guaranteeing $u' \neq 0$). +* Let $t'=t$ if $t \neq 0$; $1$ otherwise (guaranteeing $t' \neq 0$). +* Let $t''=t'$ if $g(u') \neq -t'^2$; $2t'$ otherwise (guaranteeing $t'' \neq 0$ and $g(u') \neq -t''^2$). +* Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$ +* Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$ +* Return the first $x$ in $(u' + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u'}{2}, \dfrac{X}{2Y} - \dfrac{u'}{2})$ for which $x^3 + b$ is square. + +The choices here are not strictly necessary. Just returning a fixed constant in any of the undefined cases would suffice, +but the approach here is simple enough and gives fairly uniform output even in these cases. + +**Note**: in the paper these conditions result in $\infty$ as output, due to the use of projective coordinates there. +We wish to avoid the need for callers to deal with this special case. + +This is implemented in `secp256k1_ellswift_xswiftec_frac_var` (which decodes to an x-coordinate represented as a fraction), and +in `secp256k1_ellswift_xswiftec_var` (which outputs the actual x-coordinate). + +## 3. The encoding function + +To implement $F_u^{-1}(x)$, the function to find the set of inverses $t$ for which $F_u(t) = x$, we have to reverse the process: +* Find all the $(X, Y) \in S_u$ that could have given rise to $x$, through the $x_1$, $x_2$, or $x_3$ formulas in $\psi_u.$ +* Map those $(X, Y)$ solutions to $t$ values using $P_u^{-1}(X, Y).$ +* For each of the found $t$ values, verify that $F_u(t) = x.$ +* Return the remaining $t$ values. + +The function $P_u^{-1}$, which finds $t$ given $(X, Y) \in S_u$, is significantly simpler than $P_u:$ + +$$ +P_u^{-1}(X, Y) = \left\\{\begin{array}{ll} +Yu\sqrt{-3} - X & a = 0 \\ +\dfrac{Y-Y_0(u)}{X-X_0(u)} & a \neq 0 \land X \neq X_0(u) \\ +\dfrac{-X_0(u)}{h(u)Y_0(u)} & a \neq 0 \land X = X_0(u) \land Y = Y_0(u) +\end{array}\right. +$$ + +The third step above, verifying that $F_u(t) = x$, is necessary because for the $(X, Y)$ values found through the $x_1$ and $x_2$ expressions, +it is possible that decoding through $\psi_u(X, Y)$ yields a valid $x_3$ on the curve, which would take precedence over the +$x_1$ or $x_2$ decoding. These $(X, Y)$ solutions must be rejected. + +Since we know that exactly one or exactly three out of $\\{x_1, x_2, x_3\\}$ are valid x-coordinates for any $t$, +the case where either $x_1$ or $x_2$ is valid and in addition also $x_3$ is valid must mean that all three are valid. +This means that instead of checking whether $x_3$ is on the curve, it is also possible to check whether the other one out of +$x_1$ and $x_2$ is on the curve. This is significantly simpler, as it turns out. + +Observe that $\psi_u$ guarantees that $x_1 + x_2 = -u.$ So given either $x = x_1$ or $x = x_2$, the other one of the two can be computed as +$-u - x.$ Thus, when encoding $x$ through the $x_1$ or $x_2$ expressions, one can simply check whether $g(-u-x)$ is a square, +and if so, not include the corresponding $t$ values in the returned set. As this does not need $X$, $Y$, or $t$, this condition can be determined +before those values are computed. + +It is not possible that an encoding found through the $x_1$ expression decodes to a different valid x-coordinate using $x_2$ (which would +take precedence), for the same reason: if both $x_1$ and $x_2$ decodings were valid, $x_3$ would be valid as well, and thus take +precedence over both. Because of this, the $g(-u-x)$ being square test for $x_1$ and $x_2$ is the only test necessary to guarantee the found $t$ +values round-trip back to the input $x$ correctly. This is the reason for choosing the $(x_3, x_2, x_1)$ precedence order in the decoder; +any order which does not place $x_3$ first requires more complicated round-trip checks in the encoder. + +### 3.1 Switching to *v, w* coordinates + +Before working out the formulas for all this, we switch to different variables for $S_u.$ Let $v = (X/Y - u)/2$, and +$w = 2Y.$ Or in the other direction, $X = w(u/2 + v)$ and $Y = w/2:$ +* $S_u'$ becomes the set of $(v, w)$ for which $w^2 (u^2 + uv + v^2 + a) = -g(u)$ and $w \neq 0.$ +* For $a=0$ curves, $P_u^{-1}$ can be stated for $(v,w)$ as $P_u^{'-1}(v, w) = w\left(\frac{\sqrt{-3}-1}{2}u - v\right).$ +* $\psi_u$ can be stated for $(v, w)$ as $\psi_u'(v, w) = (x_1, x_2, x_3, z)$, where + +$$ +\begin{array}{lcl} + x_1 & = & v \\ + x_2 & = & -u - v \\ + x_3 & = & u + w^2 \\ + z & = & \dfrac{g(x_3)}{w}(u^2 + uv + v^2 + a) = \dfrac{-g(u)g(x_3)}{w^3} +\end{array} +$$ + +We can now write the expressions for finding $(v, w)$ given $x$ explicitly, by solving each of the $\\{x_1, x_2, x_3\\}$ +expressions for $v$ or $w$, and using the $S_u'$ equation to find the other variable: +* Assuming $x = x_1$, we find $v = x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions). +* Assuming $x = x_2$, we find $v = -u-x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions). +* Assuming $x = x_3$, we find $w = \pm\sqrt{x-u}$ and $v = -u/2 \pm \sqrt{-w^2(4g(u) + w^2h(u))}/(2w^2)$ (four solutions). + +### 3.2 Avoiding computing all inverses + +The *ElligatorSwift* algorithm as stated in Section 1 requires the computation of $L = F_u^{-1}(x)$ (the +set of all $t$ such that $(u, t)$ decode to $x$) in full. This is unnecessary. + +Observe that the procedure of restarting with probability $(1 - \frac{\\#L}{8})$ and otherwise returning a +uniformly random element from $L$ is actually equivalent to always padding $L$ with $\bot$ values up to length 8, +picking a uniformly random element from that, restarting whenever $\bot$ is picked: + +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Compute the set $L = F_u^{-1}(x).$ + * Let $T$ be the 8-element vector consisting of the elements of $L$, plus $8 - \\#L$ times $\\{\bot\\}.$ + * Select a uniformly random $t \in T.$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. + +Now notice that the order of elements in $T$ does not matter, as all we do is pick a uniformly +random element in it, so we do not need to have all $\bot$ values at the end. +As we have 8 distinct formulas for finding $(v, w)$ (taking the variants due to $\pm$ into account), +we can associate every index in $T$ with exactly one of those formulas, making sure that: +* Formulas that yield no solutions (due to division by zero or non-existing square roots) or invalid solutions are made to return $\bot.$ +* For the $x_1$ and $x_2$ cases, if $g(-u-x)$ is a square, $\bot$ is returned instead (the round-trip check). +* In case multiple formulas would return the same non- $\bot$ result, all but one of those must be turned into $\bot$ to avoid biasing those. + +The last condition above only occurs with negligible probability for cryptographically-sized curves, but is interesting +to take into account as it allows exhaustive testing in small groups. See [Section 3.4](#34-dealing-with-special-cases) +for an analysis of all the negligible cases. + +If we define $T = (G_{0,u}(x), G_{1,u}(x), \ldots, G_{7,u}(x))$, with each $G_{i,u}$ matching one of the formulas, +the loop can be simplified to only compute one of the inverses instead of all of them: + +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Pick a uniformly random integer $c$ in $[0,8).$ + * Let $t = G_{c,u}(x).$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. + +This is implemented in `secp256k1_ellswift_xelligatorswift_var`. + +### 3.3 Finding the inverse + +To implement $G_{c,u}$, we map $c=0$ to the $x_1$ formula, $c=1$ to the $x_2$ formula, and $c=2$ and $c=3$ to the $x_3$ formula. +Those are then repeated as $c=4$ through $c=7$ for the other sign of $w$ (noting that in each formula, $w$ is a square root of some expression). +Ignoring the negligible cases, we get: + +**Define** $G_{c,u}(x)$ as: +* If $c \in \\{0, 1, 4, 5\\}$ (for $x_1$ and $x_2$ formulas): + * If $g(-u-x)$ is square, return $\bot$ (as $x_3$ would be valid and take precedence). + * If $c \in \\{0, 4\\}$ (the $x_1$ formula) let $v = x$, otherwise let $v = -u-x$ (the $x_2$ formula) + * Let $s = -g(u)/(u^2 + uv + v^2 + a)$ (using $s = w^2$ in what follows). +* Otherwise, when $c \in \\{2, 3, 6, 7\\}$ (for $x_3$ formulas): + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}.$ + * Let $v = (r/s - u)/2$ if $c \in \\{3, 7\\}$; $(-r/s - u)/2$ otherwise. +* Let $w = \sqrt{s}.$ +* Depending on $c:$ + * If $c \in \\{0, 1, 2, 3\\}:$ return $P_u^{'-1}(v, w).$ + * If $c \in \\{4, 5, 6, 7\\}:$ return $P_u^{'-1}(v, -w).$ + +Whenever a square root of a non-square is taken, $\bot$ is returned; for both square roots this happens with roughly +50% on random inputs. Similarly, when a division by 0 would occur, $\bot$ is returned as well; this will only happen +with negligible probability. A division by 0 in the first branch in fact cannot occur at all, because $u^2 + uv + v^2 + a = 0$ +implies $g(-u-x) = g(x)$ which would mean the $g(-u-x)$ is square condition has triggered +and $\bot$ would have been returned already. + +**Note**: In the paper, the $case$ variable corresponds roughly to the $c$ above, but only takes on 4 possible values (1 to 4). +The conditional negation of $w$ at the end is done randomly, which is equivalent, but makes testing harder. We choose to +have the $G_{c,u}$ be deterministic, and capture all choices in $c.$ + +Now observe that the $c \in \\{1, 5\\}$ and $c \in \\{3, 7\\}$ conditions effectively perform the same $v \rightarrow -u-v$ +transformation. Furthermore, that transformation has no effect on $s$ in the first branch +as $u^2 + ux + x^2 + a = u^2 + u(-u-x) + (-u-x)^2 + a.$ Thus we can extract it out and move it down: + +**Define** $G_{c,u}(x)$ as: +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a).$ + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}.$ +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w).$ + * If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w).$ + * If $c \in \\{4, 6\\}:$ return $P_u^{'-1}(v, -w).$ + * If $c \in \\{5, 7\\}:$ return $P_u^{'-1}(-u-v, -w).$ + +This shows there will always be exactly 0, 4, or 8 $t$ values for a given $(u, x)$ input. +There can be 0, 1, or 2 $(v, w)$ pairs before invoking $P_u^{'-1}$, and each results in 4 distinct $t$ values. + +### 3.4 Dealing with special cases + +As mentioned before there are a few cases to deal with which only happen in a negligibly small subset of inputs. +For cryptographically sized fields, if only random inputs are going to be considered, it is unnecessary to deal with these. Still, for completeness +we analyse them here. They generally fall into two categories: cases in which the encoder would produce $t$ values that +do not decode back to $x$ (or at least cannot guarantee that they do), and cases in which the encoder might produce the same +$t$ value for multiple $c$ inputs (thereby biasing that encoding): + +* In the branch for $x_1$ and $x_2$ (where $c \in \\{0, 1, 4, 5\\}$): + * When $g(u) = 0$, we would have $s=w=Y=0$, which is not on $S_u.$ This is only possible on even-ordered curves. + Excluding this also removes the one condition under which the simplified check for $x_3$ on the curve + fails (namely when $g(x_1)=g(x_2)=0$ but $g(x_3)$ is not square). + This does exclude some valid encodings: when both $g(u)=0$ and $u^2+ux+x^2+a=0$ (also implying $g(x)=0$), + the $S_u'$ equation degenerates to $0 = 0$, and many valid $t$ values may exist. Yet, these cannot be targeted uniformly by the + encoder anyway as there will generally be more than 8. + * When $g(x) = 0$, the same $t$ would be produced as in the $x_3$ branch (where $c \in \\{2, 3, 6, 7\\}$) which we give precedence + as it can deal with $g(u)=0$. + This is again only possible on even-ordered curves. +* In the branch for $x_3$ (where $c \in \\{2, 3, 6, 7\\}$): + * When $s=0$, a division by zero would occur. + * When $v = -u-v$ and $c \in \\{3, 7\\}$, the same $t$ would be returned as in the $c \in \\{2, 6\\}$ cases. + It is equivalent to checking whether $r=0$. + This cannot occur in the $x_1$ or $x_2$ branches, as it would trigger the $g(-u-x)$ is square condition. + A similar concern for $w = -w$ does not exist, as $w=0$ is already impossible in both branches: in the first + it requires $g(u)=0$ which is already outlawed on even-ordered curves and impossible on others; in the second it would trigger division by zero. +* Curve-specific special cases also exist that need to be rejected, because they result in $(u,t)$ which is invalid to the decoder, or because of division by zero in the encoder: + * For $a=0$ curves, when $u=0$ or when $t=0$. The latter can only be reached by the encoder when $g(u)=0$, which requires an even-ordered curve. + * For $a \neq 0$ curves, when $X_0(u)=0$, when $h(u)t^2 = -1$, or when $w(u + 2v) = 2X_0(u)$ while also either $w \neq 2Y_0(u)$ or $h(u)=0$. + +**Define** a version of $G_{c,u}(x)$ which deals with all these cases: +* If $a=0$ and $u=0$, return $\bot.$ +* If $a \neq 0$ and $X_0(u)=0$, return $\bot.$ +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only). + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square. + * If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$ + * If $s = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* If $a \neq 0$ and $w(u+2v) = 2X_0(u)$ and either $w \neq 2Y_0(u)$ or $h(u) = 0$, return $\bot.$ +* Depending on $c:$ + * If $c \in \\{0, 2\\}$, let $t = P_u^{'-1}(v, w).$ + * If $c \in \\{1, 3\\}$, let $t = P_u^{'-1}(-u-v, w).$ + * If $c \in \\{4, 6\\}$, let $t = P_u^{'-1}(v, -w).$ + * If $c \in \\{5, 7\\}$, let $t = P_u^{'-1}(-u-v, -w).$ +* If $a=0$ and $t=0$, return $\bot$ (even curves only). +* If $a \neq 0$ and $h(u)t^2 = -1$, return $\bot.$ +* Return $t.$ + +Given any $u$, using this algorithm over all $x$ and $c$ values, every $t$ value will be reached exactly once, +for an $x$ for which $F_u(t) = x$ holds, except for these cases that will not be reached: +* All cases where $P_u(t)$ is not defined: + * For $a=0$ curves, when $u=0$, $t=0$, or $g(u) = -t^2.$ + * For $a \neq 0$ curves, when $h(u)t^2 = -1$, $X_0(u) = 0$, or $Y_0(u) (1 - h(u) t^2) = 2X_0(u)t.$ +* When $g(u)=0$, the potentially many $t$ values that decode to an $x$ satisfying $g(x)=0$ using the $x_2$ formula. These were excluded by the $g(u)=0$ condition in the $c \in \\{0, 1, 4, 5\\}$ branch. + +These cases form a negligible subset of all $(u, t)$ for cryptographically sized curves. + +### 3.5 Encoding for `secp256k1` + +Specialized for odd-ordered $a=0$ curves: + +**Define** $G_{c,u}(x)$ as: +* If $u=0$, return $\bot.$ +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $(-u-x)^3 + b$ is square, return $\bot$ + * Let $s = -(u^3 + b)/(u^2 + ux + x^2)$ (cannot cause division by 0). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4(u^3 + b) + 3su^2)}$; return $\bot$ if not square. + * If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$ + * If $s = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $w(\frac{\sqrt{-3}-1}{2}u - v).$ + * If $c \in \\{1, 3\\}:$ return $w(\frac{\sqrt{-3}+1}{2}u + v).$ + * If $c \in \\{4, 6\\}:$ return $w(\frac{-\sqrt{-3}+1}{2}u + v).$ + * If $c \in \\{5, 7\\}:$ return $w(\frac{-\sqrt{-3}-1}{2}u - v).$ + +This is implemented in `secp256k1_ellswift_xswiftec_inv_var`. + +And the x-only ElligatorSwift encoding algorithm is still: + +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Pick a uniformly random integer $c$ in $[0,8).$ + * Let $t = G_{c,u}(x).$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. + +Note that this logic does not take the remapped $u=0$, $t=0$, and $g(u) = -t^2$ cases into account; it just avoids them. +While it is not impossible to make the encoder target them, this would increase the maximum number of $t$ values for a given $(u, x)$ +combination beyond 8, and thereby slow down the ElligatorSwift loop proportionally, for a negligible gain in uniformity. + +## 4. Encoding and decoding full *(x, y)* coordinates + +So far we have only addressed encoding and decoding x-coordinates, but in some cases an encoding +for full points with $(x, y)$ coordinates is desirable. It is possible to encode this information +in $t$ as well. + +Note that for any $(X, Y) \in S_u$, $(\pm X, \pm Y)$ are all on $S_u.$ Moreover, all of these are +mapped to the same x-coordinate. Negating $X$ or negating $Y$ just results in $x_1$ and $x_2$ +being swapped, and does not affect $x_3.$ This will not change the outcome x-coordinate as the order +of $x_1$ and $x_2$ only matters if both were to be valid, and in that case $x_3$ would be used instead. + +Still, these four $(X, Y)$ combinations all correspond to distinct $t$ values, so we can encode +the sign of the y-coordinate in the sign of $X$ or the sign of $Y.$ They correspond to the +four distinct $P_u^{'-1}$ calls in the definition of $G_{u,c}.$ + +**Note**: In the paper, the sign of the y coordinate is encoded in a separately-coded bit. + +To encode the sign of $y$ in the sign of $Y:$ + +**Define** *Decode(u, t)* for full $(x, y)$ as: +* Let $(X, Y) = P_u(t).$ +* Let $x$ be the first value in $(u + 4Y^2, \frac{-X}{2Y} - \frac{u}{2}, \frac{X}{2Y} - \frac{u}{2})$ for which $g(x)$ is square. +* Let $y = \sqrt{g(x)}.$ +* If $sign(y) = sign(Y)$, return $(x, y)$; otherwise return $(x, -y).$ + +And encoding would be done using a $G_{c,u}(x, y)$ function defined as: + +**Define** $G_{c,u}(x, y)$ as: +* If $c \in \\{0, 1\\}:$ + * If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only). + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square. + * If $c = 3$ and $r = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* Let $w' = w$ if $sign(w/2) = sign(y)$; $-w$ otherwise. +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w').$ + * If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w').$ + +Note that $c$ now only ranges $[0,4)$, as the sign of $w'$ is decided based on that of $y$, rather than on $c.$ +This change makes some valid encodings unreachable: when $y = 0$ and $sign(Y) \neq sign(0)$. + +In the above logic, $sign$ can be implemented in several ways, such as parity of the integer representation +of the input field element (for prime-sized fields) or the quadratic residuosity (for fields where +$-1$ is not square). The choice does not matter, as long as it only takes on two possible values, and for $x \neq 0$ it holds that $sign(x) \neq sign(-x)$. + +### 4.1 Full *(x, y)* coordinates for `secp256k1` + +For $a=0$ curves, there is another option. Note that for those, +the $P_u(t)$ function translates negations of $t$ to negations of (both) $X$ and $Y.$ Thus, we can use $sign(t)$ to +encode the y-coordinate directly. Combined with the earlier remapping to guarantee all inputs land on the curve, we get +as decoder: + +**Define** *Decode(u, t)* as: +* Let $u'=u$ if $u \neq 0$; $1$ otherwise. +* Let $t'=t$ if $t \neq 0$; $1$ otherwise. +* Let $t''=t'$ if $u'^3 + b + t'^2 \neq 0$; $2t'$ otherwise. +* Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$ +* Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$ +* Let $x$ be the first element of $(u' + 4Y^2, \frac{-X}{2Y} - \frac{u'}{2}, \frac{X}{2Y} - \frac{u'}{2})$ for which $g(x)$ is square. +* Let $y = \sqrt{g(x)}.$ +* Return $(x, y)$ if $sign(y) = sign(t)$; $(x, -y)$ otherwise. + +This is implemented in `secp256k1_ellswift_swiftec_var`. The used $sign(x)$ function is the parity of $x$ when represented as in integer in $[0,q).$ + +The corresponding encoder would invoke the x-only one, but negating the output $t$ if $sign(t) \neq sign(y).$ + +This is implemented in `secp256k1_ellswift_elligatorswift_var`. + +Note that this is only intended for encoding points where both the x-coordinate and y-coordinate are unpredictable. When encoding x-only points +where the y-coordinate is implicitly even (or implicitly square, or implicitly in $[0,q/2]$), the encoder in +[Section 3.5](#35-encoding-for-secp256k1) must be used, or a bias is reintroduced that undoes all the benefit of using ElligatorSwift +in the first place. diff --git a/src/secp256k1/doc/release-process.md b/src/secp256k1/doc/release-process.md new file mode 100644 index 000000000..a64bae0f0 --- /dev/null +++ b/src/secp256k1/doc/release-process.md @@ -0,0 +1,94 @@ +# Release process + +This document outlines the process for releasing versions of the form `$MAJOR.$MINOR.$PATCH`. + +We distinguish between two types of releases: *regular* and *maintenance* releases. +Regular releases are releases of a new major or minor version as well as patches of the most recent release. +Maintenance releases, on the other hand, are required for patches of older releases. + +You should coordinate with the other maintainers on the release date, if possible. +This date will be part of the release entry in [CHANGELOG.md](../CHANGELOG.md) and it should match the dates of the remaining steps in the release process (including the date of the tag and the GitHub release). +It is best if the maintainers are present during the release, so they can help ensure that the process is followed correctly and, in the case of a regular release, they are aware that they should not modify the master branch between merging the PR in step 1 and the PR in step 3. + +This process also assumes that there will be no minor releases for old major releases. + +We aim to cut a regular release every 3-4 months, approximately twice as frequent as major Bitcoin Core releases. Every second release should be published one month before the feature freeze of the next major Bitcoin Core release, allowing sufficient time to update the library in Core. + +## Sanity checks +Perform these checks when reviewing the release PR (see below): + +1. Ensure `make distcheck` doesn't fail. + ```shell + ./autogen.sh && ./configure --enable-dev-mode && make distcheck + ``` +2. Check installation with autotools: + ```shell + dir=$(mktemp -d) + ./autogen.sh && ./configure --prefix=$dir && make clean && make install && ls -RlAh $dir + gcc -o ecdsa examples/ecdsa.c $(PKG_CONFIG_PATH=$dir/lib/pkgconfig pkg-config --cflags --libs libsecp256k1) -Wl,-rpath,"$dir/lib" && ./ecdsa + ``` +3. Check installation with CMake: + ```shell + dir=$(mktemp -d) + build=$(mktemp -d) + cmake -B $build -DCMAKE_INSTALL_PREFIX=$dir && cmake --build $build && cmake --install $build && ls -RlAh $dir + gcc -o ecdsa examples/ecdsa.c -I $dir/include -L $dir/lib*/ -l secp256k1 -Wl,-rpath,"$dir/lib",-rpath,"$dir/lib64" && ./ecdsa + ``` +4. Use the [`check-abi.sh`](/tools/check-abi.sh) tool to verify that there are no unexpected ABI incompatibilities and that the version number and the release notes accurately reflect all potential ABI changes. To run this tool, the `abi-dumper` and `abi-compliance-checker` packages are required. + ```shell + tools/check-abi.sh + ``` + +## Regular release + +1. Open a PR to the master branch with a commit (using message `"release: prepare for $MAJOR.$MINOR.$PATCH"`, for example) that + * finalizes the release notes in [CHANGELOG.md](../CHANGELOG.md) by + * adding a section for the release (make sure that the version number is a link to a diff between the previous and new version), + * removing the `[Unreleased]` section header, + * ensuring that the release notes are not missing entries (check the `needs-changelog` label on github), and + * including an entry for `### ABI Compatibility` if it doesn't exist, + * sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and, + * if this is not a patch release, + * updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac`, and + * updates `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_*` in `CMakeLists.txt`. +2. Perform the [sanity checks](#sanity-checks) on the PR branch. +3. After the PR is merged, tag the commit, and push the tag: + ``` + RELEASE_COMMIT= + git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" $RELEASE_COMMIT + git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH + ``` +4. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that + * sets `_PKG_VERSION_IS_RELEASE` to `false` and increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`, + * increments the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`, and + * adds an `[Unreleased]` section header to the [CHANGELOG.md](../CHANGELOG.md). + + If other maintainers are not present to approve the PR, it can be merged without ACKs. +5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). +6. Send an announcement email to the bitcoin-dev mailing list. + +## Maintenance release + +Note that bug fixes need to be backported only to releases for which no compatible release without the bug exists. + +1. If there's no maintenance branch `$MAJOR.$MINOR`, create one: + ``` + git checkout -b $MAJOR.$MINOR v$MAJOR.$MINOR.$((PATCH - 1)) + git push git@github.com:bitcoin-core/secp256k1.git $MAJOR.$MINOR + ``` +2. Open a pull request to the `$MAJOR.$MINOR` branch that + * includes the bug fixes, + * finalizes the release notes similar to a regular release, + * increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac` + and the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt` + (with commit message `"release: bump versions for $MAJOR.$MINOR.$PATCH"`, for example). +3. Perform the [sanity checks](#sanity-checks) on the PR branch. +4. After the PRs are merged, update the release branch, tag the commit, and push the tag: + ``` + git checkout $MAJOR.$MINOR && git pull + git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" + git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH + ``` +6. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). +7. Send an announcement email to the bitcoin-dev mailing list. +8. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md). diff --git a/src/secp256k1/doc/safegcd_implementation.md b/src/secp256k1/doc/safegcd_implementation.md new file mode 100644 index 000000000..5dbbb7bbd --- /dev/null +++ b/src/secp256k1/doc/safegcd_implementation.md @@ -0,0 +1,819 @@ +# The safegcd implementation in libsecp256k1 explained + +This document explains the modular inverse and Jacobi symbol implementations in the `src/modinv*.h` files. +It is based on the paper +["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd) +by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version. + +The actual implementation is in C of course, but for demonstration purposes Python3 is used here. +Most implementation aspects and optimizations are explained, except those that depend on the specific +number representation used in the C code. + +## 1. Computing the Greatest Common Divisor (GCD) using divsteps + +The algorithm from the paper (section 11), at a very high level, is this: + +```python +def gcd(f, g): + """Compute the GCD of an odd integer f and another integer g.""" + assert f & 1 # require f to be odd + delta = 1 # additional state variable + while g != 0: + assert f & 1 # f will be odd in every iteration + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g - f) // 2 + elif g & 1: + delta, f, g = 1 + delta, f, (g + f) // 2 + else: + delta, f, g = 1 + delta, f, (g ) // 2 + return abs(f) +``` + +It computes the greatest common divisor of an odd integer *f* and any integer *g*. Its inner loop +keeps rewriting the variables *f* and *g* alongside a state variable *δ* that starts at *1*, until +*g=0* is reached. At that point, *|f|* gives the GCD. Each of the transitions in the loop is called a +"division step" (referred to as divstep in what follows). + +For example, *gcd(21, 14)* would be computed as: +- Start with *δ=1 f=21 g=14* +- Take the third branch: *δ=2 f=21 g=7* +- Take the first branch: *δ=-1 f=7 g=-7* +- Take the second branch: *δ=0 f=7 g=0* +- The answer *|f| = 7*. + +Why it works: +- Divsteps can be decomposed into two steps (see paragraph 8.2 in the paper): + - (a) If *g* is odd, replace *(f,g)* with *(g,g-f)* or (f,g+f), resulting in an even *g*. + - (b) Replace *(f,g)* with *(f,g/2)* (where *g* is guaranteed to be even). +- Neither of those two operations change the GCD: + - For (a), assume *gcd(f,g)=c*, then it must be the case that *f=a c* and *g=b c* for some integers *a* + and *b*. As *(g,g-f)=(b c,(b-a)c)* and *(f,f+g)=(a c,(a+b)c)*, the result clearly still has + common factor *c*. Reasoning in the other direction shows that no common factor can be added by + doing so either. + - For (b), we know that *f* is odd, so *gcd(f,g)* clearly has no factor *2*, and we can remove + it from *g*. +- The algorithm will eventually converge to *g=0*. This is proven in the paper (see theorem G.3). +- It follows that eventually we find a final value *f'* for which *gcd(f,g) = gcd(f',0)*. As the + gcd of *f'* and *0* is *|f'|* by definition, that is our answer. + +Compared to more [traditional GCD algorithms](https://en.wikipedia.org/wiki/Euclidean_algorithm), this one has the property of only ever looking at +the low-order bits of the variables to decide the next steps, and being easy to make +constant-time (in more low-level languages than Python). The *δ* parameter is necessary to +guide the algorithm towards shrinking the numbers' magnitudes without explicitly needing to look +at high order bits. + +Properties that will become important later: +- Performing more divsteps than needed is not a problem, as *f* does not change anymore after *g=0*. +- Only even numbers are divided by *2*. This means that when reasoning about it algebraically we + do not need to worry about rounding. +- At every point during the algorithm's execution the next *N* steps only depend on the bottom *N* + bits of *f* and *g*, and on *δ*. + + +## 2. From GCDs to modular inverses + +We want an algorithm to compute the inverse *a* of *x* modulo *M*, i.e. the number a such that *a x=1 +mod M*. This inverse only exists if the GCD of *x* and *M* is *1*, but that is always the case if *M* is +prime and *0 < x < M*. In what follows, assume that the modular inverse exists. +It turns out this inverse can be computed as a side effect of computing the GCD by keeping track +of how the internal variables can be written as linear combinations of the inputs at every step +(see the [extended Euclidean algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm)). +Since the GCD is *1*, such an algorithm will compute numbers *a* and *b* such that a x + b M = 1*. +Taking that expression *mod M* gives *a x mod M = 1*, and we see that *a* is the modular inverse of *x +mod M*. + +A similar approach can be used to calculate modular inverses using the divsteps-based GCD +algorithm shown above, if the modulus *M* is odd. To do so, compute *gcd(f=M,g=x)*, while keeping +track of extra variables *d* and *e*, for which at every step *d = f/x (mod M)* and *e = g/x (mod M)*. +*f/x* here means the number which multiplied with *x* gives *f mod M*. As *f* and *g* are initialized to *M* +and *x* respectively, *d* and *e* just start off being *0* (*M/x mod M = 0/x mod M = 0*) and *1* (*x/x mod M += 1*). + +```python +def div2(M, x): + """Helper routine to compute x/2 mod M (where M is odd).""" + assert M & 1 + if x & 1: # If x is odd, make it even by adding M. + x += M + # x must be even now, so a clean division by 2 is possible. + return x // 2 + +def modinv(M, x): + """Compute the inverse of x mod M (given that it exists, and M is odd).""" + assert M & 1 + delta, f, g, d, e = 1, M, x, 0, 1 + while g != 0: + # Note that while division by two for f and g is only ever done on even inputs, this is + # not true for d and e, so we need the div2 helper function. + if delta > 0 and g & 1: + delta, f, g, d, e = 1 - delta, g, (g - f) // 2, e, div2(M, e - d) + elif g & 1: + delta, f, g, d, e = 1 + delta, f, (g + f) // 2, d, div2(M, e + d) + else: + delta, f, g, d, e = 1 + delta, f, (g ) // 2, d, div2(M, e ) + # Verify that the invariants d=f/x mod M, e=g/x mod M are maintained. + assert f % M == (d * x) % M + assert g % M == (e * x) % M + assert f == 1 or f == -1 # |f| is the GCD, it must be 1 + # Because of invariant d = f/x (mod M), 1/x = d/f (mod M). As |f|=1, d/f = d*f. + return (d * f) % M +``` + +Also note that this approach to track *d* and *e* throughout the computation to determine the inverse +is different from the paper. There (see paragraph 12.1 in the paper) a transition matrix for the +entire computation is determined (see section 3 below) and the inverse is computed from that. +The approach here avoids the need for 2x2 matrix multiplications of various sizes, and appears to +be faster at the level of optimization we're able to do in C. + + +## 3. Batching multiple divsteps + +Every divstep can be expressed as a matrix multiplication, applying a transition matrix *(1/2 t)* +to both vectors *[f, g]* and *[d, e]* (see paragraph 8.1 in the paper): + +``` + t = [ u, v ] + [ q, r ] + + [ out_f ] = (1/2 * t) * [ in_f ] + [ out_g ] = [ in_g ] + + [ out_d ] = (1/2 * t) * [ in_d ] (mod M) + [ out_e ] [ in_e ] +``` + +where *(u, v, q, r)* is *(0, 2, -1, 1)*, *(2, 0, 1, 1)*, or *(2, 0, 0, 1)*, depending on which branch is +taken. As above, the resulting *f* and *g* are always integers. + +Performing multiple divsteps corresponds to a multiplication with the product of all the +individual divsteps' transition matrices. As each transition matrix consists of integers +divided by *2*, the product of these matrices will consist of integers divided by *2N* (see also +theorem 9.2 in the paper). These divisions are expensive when updating *d* and *e*, so we delay +them: we compute the integer coefficients of the combined transition matrix scaled by *2N*, and +do one division by *2N* as a final step: + +```python +def divsteps_n_matrix(delta, f, g): + """Compute delta and transition matrix t after N divsteps (multiplied by 2^N).""" + u, v, q, r = 1, 0, 0, 1 # start with identity matrix + for _ in range(N): + if delta > 0 and g & 1: + delta, f, g, u, v, q, r = 1 - delta, g, (g - f) // 2, 2*q, 2*r, q-u, r-v + elif g & 1: + delta, f, g, u, v, q, r = 1 + delta, f, (g + f) // 2, 2*u, 2*v, q+u, r+v + else: + delta, f, g, u, v, q, r = 1 + delta, f, (g ) // 2, 2*u, 2*v, q , r + return delta, (u, v, q, r) +``` + +As the branches in the divsteps are completely determined by the bottom *N* bits of *f* and *g*, this +function to compute the transition matrix only needs to see those bottom bits. Furthermore all +intermediate results and outputs fit in *(N+1)*-bit numbers (unsigned for *f* and *g*; signed for *u*, *v*, +*q*, and *r*) (see also paragraph 8.3 in the paper). This means that an implementation using 64-bit +integers could set *N=62* and compute the full transition matrix for 62 steps at once without any +big integer arithmetic at all. This is the reason why this algorithm is efficient: it only needs +to update the full-size *f*, *g*, *d*, and *e* numbers once every *N* steps. + +We still need functions to compute: + +``` + [ out_f ] = (1/2^N * [ u, v ]) * [ in_f ] + [ out_g ] ( [ q, r ]) [ in_g ] + + [ out_d ] = (1/2^N * [ u, v ]) * [ in_d ] (mod M) + [ out_e ] ( [ q, r ]) [ in_e ] +``` + +Because the divsteps transformation only ever divides even numbers by two, the result of *t [f,g]* is always even. When *t* is a composition of *N* divsteps, it follows that the resulting *f* +and *g* will be multiple of *2N*, and division by *2N* is simply shifting them down: + +```python +def update_fg(f, g, t): + """Multiply matrix t/2^N with [f, g].""" + u, v, q, r = t + cf, cg = u*f + v*g, q*f + r*g + # (t / 2^N) should cleanly apply to [f,g] so the result of t*[f,g] should have N zero + # bottom bits. + assert cf % 2**N == 0 + assert cg % 2**N == 0 + return cf >> N, cg >> N +``` + +The same is not true for *d* and *e*, and we need an equivalent of the `div2` function for division by *2N mod M*. +This is easy if we have precomputed *1/M mod 2N* (which always exists for odd *M*): + +```python +def div2n(M, Mi, x): + """Compute x/2^N mod M, given Mi = 1/M mod 2^N.""" + assert (M * Mi) % 2**N == 1 + # Find a factor m such that m*M has the same bottom N bits as x. We want: + # (m * M) mod 2^N = x mod 2^N + # <=> m mod 2^N = (x / M) mod 2^N + # <=> m mod 2^N = (x * Mi) mod 2^N + m = (Mi * x) % 2**N + # Subtract that multiple from x, cancelling its bottom N bits. + x -= m * M + # Now a clean division by 2^N is possible. + assert x % 2**N == 0 + return (x >> N) % M + +def update_de(d, e, t, M, Mi): + """Multiply matrix t/2^N with [d, e], modulo M.""" + u, v, q, r = t + cd, ce = u*d + v*e, q*d + r*e + return div2n(M, Mi, cd), div2n(M, Mi, ce) +``` + +With all of those, we can write a version of `modinv` that performs *N* divsteps at once: + +```python3 +def modinv(M, Mi, x): + """Compute the modular inverse of x mod M, given Mi=1/M mod 2^N.""" + assert M & 1 + delta, f, g, d, e = 1, M, x, 0, 1 + while g != 0: + # Compute the delta and transition matrix t for the next N divsteps (this only needs + # (N+1)-bit signed integer arithmetic). + delta, t = divsteps_n_matrix(delta, f % 2**N, g % 2**N) + # Apply the transition matrix t to [f, g]: + f, g = update_fg(f, g, t) + # Apply the transition matrix t to [d, e]: + d, e = update_de(d, e, t, M, Mi) + return (d * f) % M +``` + +This means that in practice we'll always perform a multiple of *N* divsteps. This is not a problem +because once *g=0*, further divsteps do not affect *f*, *g*, *d*, or *e* anymore (only *δ* keeps +increasing). For variable time code such excess iterations will be mostly optimized away in later +sections. + + +## 4. Avoiding modulus operations + +So far, there are two places where we compute a remainder of big numbers modulo *M*: at the end of +`div2n` in every `update_de`, and at the very end of `modinv` after potentially negating *d* due to the +sign of *f*. These are relatively expensive operations when done generically. + +To deal with the modulus operation in `div2n`, we simply stop requiring *d* and *e* to be in range +*[0,M)* all the time. Let's start by inlining `div2n` into `update_de`, and dropping the modulus +operation at the end: + +```python +def update_de(d, e, t, M, Mi): + """Multiply matrix t/2^N with [d, e] mod M, given Mi=1/M mod 2^N.""" + u, v, q, r = t + cd, ce = u*d + v*e, q*d + r*e + # Cancel out bottom N bits of cd and ce. + md = -((Mi * cd) % 2**N) + me = -((Mi * ce) % 2**N) + cd += md * M + ce += me * M + # And cleanly divide by 2**N. + return cd >> N, ce >> N +``` + +Let's look at bounds on the ranges of these numbers. It can be shown that *|u|+|v|* and *|q|+|r|* +never exceed *2N* (see paragraph 8.3 in the paper), and thus a multiplication with *t* will have +outputs whose absolute values are at most *2N* times the maximum absolute input value. In case the +inputs *d* and *e* are in *(-M,M)*, which is certainly true for the initial values *d=0* and *e=1* assuming +*M > 1*, the multiplication results in numbers in range *(-2NM,2NM)*. Subtracting less than *2N* +times *M* to cancel out *N* bits brings that up to *(-2N+1M,2NM)*, and +dividing by *2N* at the end takes it to *(-2M,M)*. Another application of `update_de` would take that +to *(-3M,2M)*, and so forth. This progressive expansion of the variables' ranges can be +counteracted by incrementing *d* and *e* by *M* whenever they're negative: + +```python + ... + if d < 0: + d += M + if e < 0: + e += M + cd, ce = u*d + v*e, q*d + r*e + # Cancel out bottom N bits of cd and ce. + ... +``` + +With inputs in *(-2M,M)*, they will first be shifted into range *(-M,M)*, which means that the +output will again be in *(-2M,M)*, and this remains the case regardless of how many `update_de` +invocations there are. In what follows, we will try to make this more efficient. + +Note that increasing *d* by *M* is equal to incrementing *cd* by *u M* and *ce* by *q M*. Similarly, +increasing *e* by *M* is equal to incrementing *cd* by *v M* and *ce* by *r M*. So we could instead write: + +```python + ... + cd, ce = u*d + v*e, q*d + r*e + # Perform the equivalent of incrementing d, e by M when they're negative. + if d < 0: + cd += u*M + ce += q*M + if e < 0: + cd += v*M + ce += r*M + # Cancel out bottom N bits of cd and ce. + md = -((Mi * cd) % 2**N) + me = -((Mi * ce) % 2**N) + cd += md * M + ce += me * M + ... +``` + +Now note that we have two steps of corrections to *cd* and *ce* that add multiples of *M*: this +increment, and the decrement that cancels out bottom bits. The second one depends on the first +one, but they can still be efficiently combined by only computing the bottom bits of *cd* and *ce* +at first, and using that to compute the final *md*, *me* values: + +```python +def update_de(d, e, t, M, Mi): + """Multiply matrix t/2^N with [d, e], modulo M.""" + u, v, q, r = t + md, me = 0, 0 + # Compute what multiples of M to add to cd and ce. + if d < 0: + md += u + me += q + if e < 0: + md += v + me += r + # Compute bottom N bits of t*[d,e] + M*[md,me]. + cd, ce = (u*d + v*e + md*M) % 2**N, (q*d + r*e + me*M) % 2**N + # Correct md and me such that the bottom N bits of t*[d,e] + M*[md,me] are zero. + md -= (Mi * cd) % 2**N + me -= (Mi * ce) % 2**N + # Do the full computation. + cd, ce = u*d + v*e + md*M, q*d + r*e + me*M + # And cleanly divide by 2**N. + return cd >> N, ce >> N +``` + +One last optimization: we can avoid the *md M* and *me M* multiplications in the bottom bits of *cd* +and *ce* by moving them to the *md* and *me* correction: + +```python + ... + # Compute bottom N bits of t*[d,e]. + cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N + # Correct md and me such that the bottom N bits of t*[d,e]+M*[md,me] are zero. + # Note that this is not the same as {md = (-Mi * cd) % 2**N} etc. That would also result in N + # zero bottom bits, but isn't guaranteed to be a reduction of [0,2^N) compared to the + # previous md and me values, and thus would violate our bounds analysis. + md -= (Mi*cd + md) % 2**N + me -= (Mi*ce + me) % 2**N + ... +``` + +The resulting function takes *d* and *e* in range *(-2M,M)* as inputs, and outputs values in the same +range. That also means that the *d* value at the end of `modinv` will be in that range, while we want +a result in *[0,M)*. To do that, we need a normalization function. It's easy to integrate the +conditional negation of *d* (based on the sign of *f*) into it as well: + +```python +def normalize(sign, v, M): + """Compute sign*v mod M, where v is in range (-2*M,M); output in [0,M).""" + assert sign == 1 or sign == -1 + # v in (-2*M,M) + if v < 0: + v += M + # v in (-M,M). Now multiply v with sign (which can only be 1 or -1). + if sign == -1: + v = -v + # v in (-M,M) + if v < 0: + v += M + # v in [0,M) + return v +``` + +And calling it in `modinv` is simply: + +```python + ... + return normalize(f, d, M) +``` + + +## 5. Constant-time operation + +The primary selling point of the algorithm is fast constant-time operation. What code flow still +depends on the input data so far? + +- the number of iterations of the while *g ≠ 0* loop in `modinv` +- the branches inside `divsteps_n_matrix` +- the sign checks in `update_de` +- the sign checks in `normalize` + +To make the while loop in `modinv` constant time it can be replaced with a constant number of +iterations. The paper proves (Theorem 11.2) that *741* divsteps are sufficient for any *256*-bit +inputs, and [safegcd-bounds](https://github.com/sipa/safegcd-bounds) shows that the slightly better bound *724* is +sufficient even. Given that every loop iteration performs *N* divsteps, it will run a total of +*⌈724/N⌉* times. + +To deal with the branches in `divsteps_n_matrix` we will replace them with constant-time bitwise +operations (and hope the C compiler isn't smart enough to turn them back into branches; see +`ctime_tests.c` for automated tests that this isn't the case). To do so, observe that a +divstep can be written instead as (compare to the inner loop of `gcd` in section 1). + +```python + x = -f if delta > 0 else f # set x equal to (input) -f or f + if g & 1: + g += x # set g to (input) g-f or g+f + if delta > 0: + delta = -delta + f += g # set f to (input) g (note that g was set to g-f before) + delta += 1 + g >>= 1 +``` + +To convert the above to bitwise operations, we rely on a trick to negate conditionally: per the +definition of negative numbers in two's complement, (*-v == ~v + 1*) holds for every number *v*. As +*-1* in two's complement is all *1* bits, bitflipping can be expressed as xor with *-1*. It follows +that *-v == (v ^ -1) - (-1)*. Thus, if we have a variable *c* that takes on values *0* or *-1*, then +*(v ^ c) - c* is *v* if *c=0* and *-v* if *c=-1*. + +Using this we can write: + +```python + x = -f if delta > 0 else f +``` + +in constant-time form as: + +```python + c1 = (-delta) >> 63 + # Conditionally negate f based on c1: + x = (f ^ c1) - c1 +``` + +To use that trick, we need a helper mask variable *c1* that resolves the condition *δ>0* to *-1* +(if true) or *0* (if false). We compute *c1* using right shifting, which is equivalent to dividing by +the specified power of *2* and rounding down (in Python, and also in C under the assumption of a typical two's complement system; see +`assumptions.h` for tests that this is the case). Right shifting by *63* thus maps all +numbers in range *[-263,0)* to *-1*, and numbers in range *[0,263)* to *0*. + +Using the facts that *x&0=0* and *x&(-1)=x* (on two's complement systems again), we can write: + +```python + if g & 1: + g += x +``` + +as: + +```python + # Compute c2=0 if g is even and c2=-1 if g is odd. + c2 = -(g & 1) + # This masks out x if g is even, and leaves x be if g is odd. + g += x & c2 +``` + +Using the conditional negation trick again we can write: + +```python + if g & 1: + if delta > 0: + delta = -delta +``` + +as: + +```python + # Compute c3=-1 if g is odd and delta>0, and 0 otherwise. + c3 = c1 & c2 + # Conditionally negate delta based on c3: + delta = (delta ^ c3) - c3 +``` + +Finally: + +```python + if g & 1: + if delta > 0: + f += g +``` + +becomes: + +```python + f += g & c3 +``` + +It turns out that this can be implemented more efficiently by applying the substitution +*η=-δ*. In this representation, negating *δ* corresponds to negating *η*, and incrementing +*δ* corresponds to decrementing *η*. This allows us to remove the negation in the *c1* +computation: + +```python + # Compute a mask c1 for eta < 0, and compute the conditional negation x of f: + c1 = eta >> 63 + x = (f ^ c1) - c1 + # Compute a mask c2 for odd g, and conditionally add x to g: + c2 = -(g & 1) + g += x & c2 + # Compute a mask c for (eta < 0) and odd (input) g, and use it to conditionally negate eta, + # and add g to f: + c3 = c1 & c2 + eta = (eta ^ c3) - c3 + f += g & c3 + # Incrementing delta corresponds to decrementing eta. + eta -= 1 + g >>= 1 +``` + +A variant of divsteps with better worst-case performance can be used instead: starting *δ* at +*1/2* instead of *1*. This reduces the worst case number of iterations to *590* for *256*-bit inputs +(which can be shown using convex hull analysis). In this case, the substitution *ζ=-(δ+1/2)* +is used instead to keep the variable integral. Incrementing *δ* by *1* still translates to +decrementing *ζ* by *1*, but negating *δ* now corresponds to going from *ζ* to *-(ζ+1)*, or +*~ζ*. Doing that conditionally based on *c3* is simply: + +```python + ... + c3 = c1 & c2 + zeta ^= c3 + ... +``` + +By replacing the loop in `divsteps_n_matrix` with a variant of the divstep code above (extended to +also apply all *f* operations to *u*, *v* and all *g* operations to *q*, *r*), a constant-time version of +`divsteps_n_matrix` is obtained. The full code will be in section 7. + +These bit fiddling tricks can also be used to make the conditional negations and additions in +`update_de` and `normalize` constant-time. + + +## 6. Variable-time optimizations + +In section 5, we modified the `divsteps_n_matrix` function (and a few others) to be constant time. +Constant time operations are only necessary when computing modular inverses of secret data. In +other cases, it slows down calculations unnecessarily. In this section, we will construct a +faster non-constant time `divsteps_n_matrix` function. + +To do so, first consider yet another way of writing the inner loop of divstep operations in +`gcd` from section 1. This decomposition is also explained in the paper in section 8.2. We use +the original version with initial *δ=1* and *η=-δ* here. + +```python +for _ in range(N): + if g & 1 and eta < 0: + eta, f, g = -eta, g, -f + if g & 1: + g += f + eta -= 1 + g >>= 1 +``` + +Whenever *g* is even, the loop only shifts *g* down and decreases *η*. When *g* ends in multiple zero +bits, these iterations can be consolidated into one step. This requires counting the bottom zero +bits efficiently, which is possible on most platforms; it is abstracted here as the function +`count_trailing_zeros`. + +```python +def count_trailing_zeros(v): + """ + When v is zero, consider all N zero bits as "trailing". + For a non-zero value v, find z such that v=(d<>= zeros + i -= zeros + if i == 0: + break + # We know g is odd now + if eta < 0: + eta, f, g = -eta, g, -f + g += f + # g is even now, and the eta decrement and g shift will happen in the next loop. +``` + +We can now remove multiple bottom *0* bits from *g* at once, but still need a full iteration whenever +there is a bottom *1* bit. In what follows, we will get rid of multiple *1* bits simultaneously as +well. + +Observe that as long as *η ≥ 0*, the loop does not modify *f*. Instead, it cancels out bottom +bits of *g* and shifts them out, and decreases *η* and *i* accordingly - interrupting only when *η* +becomes negative, or when *i* reaches *0*. Combined, this is equivalent to adding a multiple of *f* to +*g* to cancel out multiple bottom bits, and then shifting them out. + +It is easy to find what that multiple is: we want a number *w* such that *g+w f* has a few bottom +zero bits. If that number of bits is *L*, we want *g+w f mod 2L = 0*, or *w = -g/f mod 2L*. Since *f* +is odd, such a *w* exists for any *L*. *L* cannot be more than *i* steps (as we'd finish the loop before +doing more) or more than *η+1* steps (as we'd run `eta, f, g = -eta, g, -f` at that point), but +apart from that, we're only limited by the complexity of computing *w*. + +This code demonstrates how to cancel up to 4 bits per step: + +```python +NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n +i = N +while True: + zeros = min(i, count_trailing_zeros(g)) + eta -= zeros + g >>= zeros + i -= zeros + if i == 0: + break + # We know g is odd now + if eta < 0: + eta, f, g = -eta, g, -f + # Compute limit on number of bits to cancel + limit = min(min(eta + 1, i), 4) + # Compute w = -g/f mod 2**limit, using the table value for -1/f mod 2**4. Note that f is + # always odd, so its inverse modulo a power of two always exists. + w = (g * NEGINV16[(f & 15) // 2]) % (2**limit) + # As w = -g/f mod (2**limit), g+w*f mod 2**limit = 0 mod 2**limit. + g += w * f + assert g % (2**limit) == 0 + # The next iteration will now shift out at least limit bottom zero bits from g. +``` + +By using a bigger table more bits can be cancelled at once. The table can also be implemented +as a formula. Several formulas are known for computing modular inverses modulo powers of two; +some can be found in Hacker's Delight second edition by Henry S. Warren, Jr. pages 245-247. +Here we need the negated modular inverse, which is a simple transformation of those: + +- Instead of a 3-bit table: + - *-f* or *f ^ 6* +- Instead of a 4-bit table: + - *1 - f(f + 1)* + - *-(f + (((f + 1) & 4) << 1))* +- For larger tables the following technique can be used: if *w=-1/f mod 2L*, then *w(w f+2)* is + *-1/f mod 22L*. This allows extending the previous formulas (or tables). In particular we + have this 6-bit function (based on the 3-bit function above): + - *f(f2 - 2)* + +This loop, again extended to also handle *u*, *v*, *q*, and *r* alongside *f* and *g*, placed in +`divsteps_n_matrix`, gives a significantly faster, but non-constant time version. + + +## 7. Final Python version + +All together we need the following functions: + +- A way to compute the transition matrix in constant time, using the `divsteps_n_matrix` function + from section 2, but with its loop replaced by a variant of the constant-time divstep from + section 5, extended to handle *u*, *v*, *q*, *r*: + +```python +def divsteps_n_matrix(zeta, f, g): + """Compute zeta and transition matrix t after N divsteps (multiplied by 2^N).""" + u, v, q, r = 1, 0, 0, 1 # start with identity matrix + for _ in range(N): + c1 = zeta >> 63 + # Compute x, y, z as conditionally-negated versions of f, u, v. + x, y, z = (f ^ c1) - c1, (u ^ c1) - c1, (v ^ c1) - c1 + c2 = -(g & 1) + # Conditionally add x, y, z to g, q, r. + g, q, r = g + (x & c2), q + (y & c2), r + (z & c2) + c1 &= c2 # reusing c1 here for the earlier c3 variable + zeta = (zeta ^ c1) - 1 # inlining the unconditional zeta decrement here + # Conditionally add g, q, r to f, u, v. + f, u, v = f + (g & c1), u + (q & c1), v + (r & c1) + # When shifting g down, don't shift q, r, as we construct a transition matrix multiplied + # by 2^N. Instead, shift f's coefficients u and v up. + g, u, v = g >> 1, u << 1, v << 1 + return zeta, (u, v, q, r) +``` + +- The functions to update *f* and *g*, and *d* and *e*, from section 2 and section 4, with the constant-time + changes to `update_de` from section 5: + +```python +def update_fg(f, g, t): + """Multiply matrix t/2^N with [f, g].""" + u, v, q, r = t + cf, cg = u*f + v*g, q*f + r*g + return cf >> N, cg >> N + +def update_de(d, e, t, M, Mi): + """Multiply matrix t/2^N with [d, e], modulo M.""" + u, v, q, r = t + d_sign, e_sign = d >> 257, e >> 257 + md, me = (u & d_sign) + (v & e_sign), (q & d_sign) + (r & e_sign) + cd, ce = (u*d + v*e) % 2**N, (q*d + r*e) % 2**N + md -= (Mi*cd + md) % 2**N + me -= (Mi*ce + me) % 2**N + cd, ce = u*d + v*e + M*md, q*d + r*e + M*me + return cd >> N, ce >> N +``` + +- The `normalize` function from section 4, made constant time as well: + +```python +def normalize(sign, v, M): + """Compute sign*v mod M, where v in (-2*M,M); output in [0,M).""" + v_sign = v >> 257 + # Conditionally add M to v. + v += M & v_sign + c = (sign - 1) >> 1 + # Conditionally negate v. + v = (v ^ c) - c + v_sign = v >> 257 + # Conditionally add M to v again. + v += M & v_sign + return v +``` + +- And finally the `modinv` function too, adapted to use *ζ* instead of *δ*, and using the fixed + iteration count from section 5: + +```python +def modinv(M, Mi, x): + """Compute the modular inverse of x mod M, given Mi=1/M mod 2^N.""" + zeta, f, g, d, e = -1, M, x, 0, 1 + for _ in range((590 + N - 1) // N): + zeta, t = divsteps_n_matrix(zeta, f % 2**N, g % 2**N) + f, g = update_fg(f, g, t) + d, e = update_de(d, e, t, M, Mi) + return normalize(f, d, M) +``` + +- To get a variable time version, replace the `divsteps_n_matrix` function with one that uses the + divsteps loop from section 5, and a `modinv` version that calls it without the fixed iteration + count: + +```python +NEGINV16 = [15, 5, 3, 9, 7, 13, 11, 1] # NEGINV16[n//2] = (-n)^-1 mod 16, for odd n +def divsteps_n_matrix_var(eta, f, g): + """Compute eta and transition matrix t after N divsteps (multiplied by 2^N).""" + u, v, q, r = 1, 0, 0, 1 + i = N + while True: + zeros = min(i, count_trailing_zeros(g)) + eta, i = eta - zeros, i - zeros + g, u, v = g >> zeros, u << zeros, v << zeros + if i == 0: + break + if eta < 0: + eta, f, u, v, g, q, r = -eta, g, q, r, -f, -u, -v + limit = min(min(eta + 1, i), 4) + w = (g * NEGINV16[(f & 15) // 2]) % (2**limit) + g, q, r = g + w*f, q + w*u, r + w*v + return eta, (u, v, q, r) + +def modinv_var(M, Mi, x): + """Compute the modular inverse of x mod M, given Mi = 1/M mod 2^N.""" + eta, f, g, d, e = -1, M, x, 0, 1 + while g != 0: + eta, t = divsteps_n_matrix_var(eta, f % 2**N, g % 2**N) + f, g = update_fg(f, g, t) + d, e = update_de(d, e, t, M, Mi) + return normalize(f, d, Mi) +``` + +## 8. From GCDs to Jacobi symbol + +We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an +extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we +make corresponding updates to *j* using +[properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties): +* *((g/2) | f)* is either *(g | f)* or *-(g | f)*, depending on the value of *f mod 8* (negating if it's *3* or *5*). +* *(f | g)* is either *(g | f)* or *-(g | f)*, depending on *f mod 4* and *g mod 4* (negating if both are *3*). + +These updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied +very quickly, as long as we keep track of a few additional bits of *f* and *g*. Overall, this +calculation is slightly simpler than the one for the modular inverse because we no longer need to +keep track of *d* and *e*. + +However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for +positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative +values. We resolve this by using the following modified steps: + +```python + # Before + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g - f) // 2 + + # After + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g + f) // 2 +``` + +The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4 +and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm +will converge. The justification for posdivsteps is completely empirical: in practice, it appears +that the vast majority of nonzero inputs converge to *f=g=gcd(f0, g0)* in a +number of steps proportional to their logarithm. + +Note that: +- We require inputs to satisfy *gcd(x, M) = 1*, as otherwise *f=1* is not reached. +- We require inputs *x &neq; 0*, because applying posdivstep with *g=0* has no effect. +- We need to update the termination condition from *g=0* to *f=1*. + +We account for the possibility of nonconvergence by only performing a bounded number of +posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not +yet been found. + +The optimizations in sections 3-7 above are described in the context of the original divsteps, but +in the C implementation we also adapt most of them (not including "avoiding modulus operations", +since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate +Jacobi symbols for secret data) to the posdivsteps version. diff --git a/src/secp256k1/examples/CMakeLists.txt b/src/secp256k1/examples/CMakeLists.txt new file mode 100644 index 000000000..fd1ebce39 --- /dev/null +++ b/src/secp256k1/examples/CMakeLists.txt @@ -0,0 +1,34 @@ +function(add_example name) + set(target_name ${name}_example) + add_executable(${target_name} ${name}.c) + target_include_directories(${target_name} PRIVATE + ${PROJECT_SOURCE_DIR}/include + ) + target_link_libraries(${target_name} + secp256k1 + $<$:bcrypt> + ) + set(test_name ${name}_example) + add_test(NAME ${test_name} COMMAND ${target_name}) + if(BUILD_SHARED_LIBS AND MSVC) + # The DLL must reside either in the same folder where the executable is + # or somewhere in PATH. Using the latter option. + set_tests_properties(${test_name} PROPERTIES + ENVIRONMENT "PATH=$;$ENV{PATH}" + ) + endif() +endfunction() + +add_example(ecdsa) + +if(SECP256K1_ENABLE_MODULE_ECDH) + add_example(ecdh) +endif() + +if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) + add_example(schnorr) +endif() + +if(SECP256K1_ENABLE_MODULE_ELLSWIFT) + add_example(ellswift) +endif() diff --git a/src/secp256k1/examples/EXAMPLES_COPYING b/src/secp256k1/examples/EXAMPLES_COPYING new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/src/secp256k1/examples/EXAMPLES_COPYING @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/src/secp256k1/examples/ecdh.c b/src/secp256k1/examples/ecdh.c new file mode 100644 index 000000000..d71fd2f60 --- /dev/null +++ b/src/secp256k1/examples/ecdh.c @@ -0,0 +1,122 @@ +/************************************************************************* + * Written in 2020-2022 by Elichai Turkel * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +#include +#include +#include + +#include +#include + +#include "examples_util.h" + +int main(void) { + unsigned char seckey1[32]; + unsigned char seckey2[32]; + unsigned char compressed_pubkey1[33]; + unsigned char compressed_pubkey2[33]; + unsigned char shared_secret1[32]; + unsigned char shared_secret2[32]; + unsigned char randomize[32]; + int return_val; + size_t len; + secp256k1_pubkey pubkey1; + secp256k1_pubkey pubkey2; + + /* Before we can call actual API functions, we need to create a "context". */ + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + if (!fill_random(randomize, sizeof(randomize))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Randomizing the context is recommended to protect against side-channel + * leakage See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + return_val = secp256k1_context_randomize(ctx, randomize); + assert(return_val); + + /*** Key Generation ***/ + + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + while (1) { + if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) { + printf("Failed to generate randomness\n"); + return 1; + } + if (secp256k1_ec_seckey_verify(ctx, seckey1) && secp256k1_ec_seckey_verify(ctx, seckey2)) { + break; + } + } + + /* Public key creation using a valid context with a verified secret key should never fail */ + return_val = secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1); + assert(return_val); + return_val = secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2); + assert(return_val); + + /* Serialize pubkey1 in a compressed form (33 bytes), should always return 1 */ + len = sizeof(compressed_pubkey1); + return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey1, &len, &pubkey1, SECP256K1_EC_COMPRESSED); + assert(return_val); + /* Should be the same size as the size of the output, because we passed a 33 byte array. */ + assert(len == sizeof(compressed_pubkey1)); + + /* Serialize pubkey2 in a compressed form (33 bytes) */ + len = sizeof(compressed_pubkey2); + return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey2, &len, &pubkey2, SECP256K1_EC_COMPRESSED); + assert(return_val); + /* Should be the same size as the size of the output, because we passed a 33 byte array. */ + assert(len == sizeof(compressed_pubkey2)); + + /*** Creating the shared secret ***/ + + /* Perform ECDH with seckey1 and pubkey2. Should never fail with a verified + * seckey and valid pubkey */ + return_val = secp256k1_ecdh(ctx, shared_secret1, &pubkey2, seckey1, NULL, NULL); + assert(return_val); + + /* Perform ECDH with seckey2 and pubkey1. Should never fail with a verified + * seckey and valid pubkey */ + return_val = secp256k1_ecdh(ctx, shared_secret2, &pubkey1, seckey2, NULL, NULL); + assert(return_val); + + /* Both parties should end up with the same shared secret */ + return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1)); + assert(return_val == 0); + + printf("Secret Key1: "); + print_hex(seckey1, sizeof(seckey1)); + printf("Compressed Pubkey1: "); + print_hex(compressed_pubkey1, sizeof(compressed_pubkey1)); + printf("\nSecret Key2: "); + print_hex(seckey2, sizeof(seckey2)); + printf("Compressed Pubkey2: "); + print_hex(compressed_pubkey2, sizeof(compressed_pubkey2)); + printf("\nShared Secret: "); + print_hex(shared_secret1, sizeof(shared_secret1)); + + /* This will clear everything from the context and free the memory */ + secp256k1_context_destroy(ctx); + + /* It's best practice to try to clear secrets from memory after using them. + * This is done because some bugs can allow an attacker to leak memory, for + * example through "out of bounds" array access (see Heartbleed), or the OS + * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. + * + * Here we are preventing these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + secure_erase(seckey1, sizeof(seckey1)); + secure_erase(seckey2, sizeof(seckey2)); + secure_erase(shared_secret1, sizeof(shared_secret1)); + secure_erase(shared_secret2, sizeof(shared_secret2)); + + return 0; +} diff --git a/src/secp256k1/examples/ecdsa.c b/src/secp256k1/examples/ecdsa.c new file mode 100644 index 000000000..d5c4613d9 --- /dev/null +++ b/src/secp256k1/examples/ecdsa.c @@ -0,0 +1,139 @@ +/************************************************************************* + * Written in 2020-2022 by Elichai Turkel * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +#include +#include +#include + +#include + +#include "examples_util.h" + +int main(void) { + /* Instead of signing the message directly, we must sign a 32-byte hash. + * Here the message is "Hello, world!" and the hash function was SHA-256. + * An actual implementation should just call SHA-256, but this example + * hardcodes the output to avoid depending on an additional library. + * See https://bitcoin.stackexchange.com/questions/81115/if-someone-wanted-to-pretend-to-be-satoshi-by-posting-a-fake-signature-to-defrau/81116#81116 */ + unsigned char msg_hash[32] = { + 0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4, + 0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64, + 0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34, + 0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3, + }; + unsigned char seckey[32]; + unsigned char randomize[32]; + unsigned char compressed_pubkey[33]; + unsigned char serialized_signature[64]; + size_t len; + int is_signature_valid, is_signature_valid2; + int return_val; + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + /* Before we can call actual API functions, we need to create a "context". */ + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + if (!fill_random(randomize, sizeof(randomize))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Randomizing the context is recommended to protect against side-channel + * leakage See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + return_val = secp256k1_context_randomize(ctx, randomize); + assert(return_val); + + /*** Key Generation ***/ + + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + while (1) { + if (!fill_random(seckey, sizeof(seckey))) { + printf("Failed to generate randomness\n"); + return 1; + } + if (secp256k1_ec_seckey_verify(ctx, seckey)) { + break; + } + } + + /* Public key creation using a valid context with a verified secret key should never fail */ + return_val = secp256k1_ec_pubkey_create(ctx, &pubkey, seckey); + assert(return_val); + + /* Serialize the pubkey in a compressed form(33 bytes). Should always return 1. */ + len = sizeof(compressed_pubkey); + return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED); + assert(return_val); + /* Should be the same size as the size of the output, because we passed a 33 byte array. */ + assert(len == sizeof(compressed_pubkey)); + + /*** Signing ***/ + + /* Generate an ECDSA signature `noncefp` and `ndata` allows you to pass a + * custom nonce function, passing `NULL` will use the RFC-6979 safe default. + * Signing with a valid context, verified secret key + * and the default nonce function should never fail. */ + return_val = secp256k1_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL); + assert(return_val); + + /* Serialize the signature in a compact form. Should always return 1 + * according to the documentation in secp256k1.h. */ + return_val = secp256k1_ecdsa_signature_serialize_compact(ctx, serialized_signature, &sig); + assert(return_val); + + + /*** Verification ***/ + + /* Deserialize the signature. This will return 0 if the signature can't be parsed correctly. */ + if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, serialized_signature)) { + printf("Failed parsing the signature\n"); + return 1; + } + + /* Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */ + if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))) { + printf("Failed parsing the public key\n"); + return 1; + } + + /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */ + is_signature_valid = secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey); + + printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false"); + printf("Secret Key: "); + print_hex(seckey, sizeof(seckey)); + printf("Public Key: "); + print_hex(compressed_pubkey, sizeof(compressed_pubkey)); + printf("Signature: "); + print_hex(serialized_signature, sizeof(serialized_signature)); + + /* This will clear everything from the context and free the memory */ + secp256k1_context_destroy(ctx); + + /* Bonus example: if all we need is signature verification (and no key + generation or signing), we don't need to use a context created via + secp256k1_context_create(). We can simply use the static (i.e., global) + context secp256k1_context_static. See its description in + include/secp256k1.h for details. */ + is_signature_valid2 = secp256k1_ecdsa_verify(secp256k1_context_static, + &sig, msg_hash, &pubkey); + assert(is_signature_valid2 == is_signature_valid); + + /* It's best practice to try to clear secrets from memory after using them. + * This is done because some bugs can allow an attacker to leak memory, for + * example through "out of bounds" array access (see Heartbleed), or the OS + * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. + * + * Here we are preventing these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + secure_erase(seckey, sizeof(seckey)); + + return 0; +} diff --git a/src/secp256k1/examples/ellswift.c b/src/secp256k1/examples/ellswift.c new file mode 100644 index 000000000..52be7eebf --- /dev/null +++ b/src/secp256k1/examples/ellswift.c @@ -0,0 +1,123 @@ +/************************************************************************* + * Written in 2024 by Sebastian Falbesoner * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +/** This file demonstrates how to use the ElligatorSwift module to perform + * a key exchange according to BIP 324. Additionally, see the documentation + * in include/secp256k1_ellswift.h and doc/ellswift.md. + */ + +#include +#include +#include + +#include +#include + +#include "examples_util.h" + +int main(void) { + secp256k1_context* ctx; + unsigned char randomize[32]; + unsigned char auxrand1[32]; + unsigned char auxrand2[32]; + unsigned char seckey1[32]; + unsigned char seckey2[32]; + unsigned char ellswift_pubkey1[64]; + unsigned char ellswift_pubkey2[64]; + unsigned char shared_secret1[32]; + unsigned char shared_secret2[32]; + int return_val; + + /* Create a secp256k1 context */ + ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + if (!fill_random(randomize, sizeof(randomize))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Randomizing the context is recommended to protect against side-channel + * leakage. See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + return_val = secp256k1_context_randomize(ctx, randomize); + assert(return_val); + + /*** Generate secret keys ***/ + + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + while (1) { + if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) { + printf("Failed to generate randomness\n"); + return 1; + } + if (secp256k1_ec_seckey_verify(ctx, seckey1) && secp256k1_ec_seckey_verify(ctx, seckey2)) { + break; + } + } + + /* Generate ElligatorSwift public keys. This should never fail with valid context and + verified secret keys. Note that providing additional randomness (fourth parameter) is + optional, but recommended. */ + if (!fill_random(auxrand1, sizeof(auxrand1)) || !fill_random(auxrand2, sizeof(auxrand2))) { + printf("Failed to generate randomness\n"); + return 1; + } + return_val = secp256k1_ellswift_create(ctx, ellswift_pubkey1, seckey1, auxrand1); + assert(return_val); + return_val = secp256k1_ellswift_create(ctx, ellswift_pubkey2, seckey2, auxrand2); + assert(return_val); + + /*** Create the shared secret on each side ***/ + + /* Perform x-only ECDH with seckey1 and ellswift_pubkey2. Should never fail + * with a verified seckey and valid pubkey. Note that both parties pass both + * EllSwift pubkeys in the same order; the pubkey of the calling party is + * determined by the "party" boolean (sixth parameter). */ + return_val = secp256k1_ellswift_xdh(ctx, shared_secret1, ellswift_pubkey1, ellswift_pubkey2, + seckey1, 0, secp256k1_ellswift_xdh_hash_function_bip324, NULL); + assert(return_val); + + /* Perform x-only ECDH with seckey2 and ellswift_pubkey1. Should never fail + * with a verified seckey and valid pubkey. */ + return_val = secp256k1_ellswift_xdh(ctx, shared_secret2, ellswift_pubkey1, ellswift_pubkey2, + seckey2, 1, secp256k1_ellswift_xdh_hash_function_bip324, NULL); + assert(return_val); + + /* Both parties should end up with the same shared secret */ + return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1)); + assert(return_val == 0); + + printf( " Secret Key1: "); + print_hex(seckey1, sizeof(seckey1)); + printf( "EllSwift Pubkey1: "); + print_hex(ellswift_pubkey1, sizeof(ellswift_pubkey1)); + printf("\n Secret Key2: "); + print_hex(seckey2, sizeof(seckey2)); + printf( "EllSwift Pubkey2: "); + print_hex(ellswift_pubkey2, sizeof(ellswift_pubkey2)); + printf("\n Shared Secret: "); + print_hex(shared_secret1, sizeof(shared_secret1)); + + /* This will clear everything from the context and free the memory */ + secp256k1_context_destroy(ctx); + + /* It's best practice to try to clear secrets from memory after using them. + * This is done because some bugs can allow an attacker to leak memory, for + * example through "out of bounds" array access (see Heartbleed), or the OS + * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. + * + * Here we are preventing these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + secure_erase(seckey1, sizeof(seckey1)); + secure_erase(seckey2, sizeof(seckey2)); + secure_erase(shared_secret1, sizeof(shared_secret1)); + secure_erase(shared_secret2, sizeof(shared_secret2)); + + return 0; +} diff --git a/src/secp256k1/examples/examples_util.h b/src/secp256k1/examples/examples_util.h new file mode 100644 index 000000000..3293b6403 --- /dev/null +++ b/src/secp256k1/examples/examples_util.h @@ -0,0 +1,108 @@ +/************************************************************************* + * Copyright (c) 2020-2021 Elichai Turkel * + * Distributed under the CC0 software license, see the accompanying file * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +/* + * This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems. + * It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below. + * + * Platform randomness sources: + * Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom + * macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html + * FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 + * OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom + * Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom + */ + +#if defined(_WIN32) +/* + * The defined WIN32_NO_STATUS macro disables return code definitions in + * windows.h, which avoids "macro redefinition" MSVC warnings in ntstatus.h. + */ +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS +#include +#include +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(__OpenBSD__) +#include +#else +#error "Couldn't identify the OS" +#endif + +#include +#include +#include + + +/* Returns 1 on success, and 0 on failure. */ +static int fill_random(unsigned char* data, size_t size) { +#if defined(_WIN32) + NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (res != STATUS_SUCCESS || size > ULONG_MAX) { + return 0; + } else { + return 1; + } +#elif defined(__linux__) || defined(__FreeBSD__) + /* If `getrandom(2)` is not available you should fallback to /dev/urandom */ + ssize_t res = getrandom(data, size, 0); + if (res < 0 || (size_t)res != size ) { + return 0; + } else { + return 1; + } +#elif defined(__APPLE__) || defined(__OpenBSD__) + /* If `getentropy(2)` is not available you should fallback to either + * `SecRandomCopyBytes` or /dev/urandom */ + int res = getentropy(data, size); + if (res == 0) { + return 1; + } else { + return 0; + } +#endif + return 0; +} + +static void print_hex(unsigned char* data, size_t size) { + size_t i; + printf("0x"); + for (i = 0; i < size; i++) { + printf("%02x", data[i]); + } + printf("\n"); +} + +#if defined(_MSC_VER) +// For SecureZeroMemory +#include +#endif +/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */ +static void secure_erase(void *ptr, size_t len) { +#if defined(_MSC_VER) + /* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */ + SecureZeroMemory(ptr, len); +#elif defined(__GNUC__) + /* We use a memory barrier that scares the compiler away from optimizing out the memset. + * + * Quoting Adam Langley in commit ad1907fe73334d6c696c8539646c21b11178f20f + * in BoringSSL (ISC License): + * As best as we can tell, this is sufficient to break any optimisations that + * might try to eliminate "superfluous" memsets. + * This method used in memzero_explicit() the Linux kernel, too. Its advantage is that it is + * pretty efficient, because the compiler can still implement the memset() efficiently, + * just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by + * Yang et al. (USENIX Security 2017) for more background. + */ + memset(ptr, 0, len); + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#else + void *(*volatile const volatile_memset)(void *, int, size_t) = memset; + volatile_memset(ptr, 0, len); +#endif +} diff --git a/src/secp256k1/examples/schnorr.c b/src/secp256k1/examples/schnorr.c new file mode 100644 index 000000000..b0409b986 --- /dev/null +++ b/src/secp256k1/examples/schnorr.c @@ -0,0 +1,156 @@ +/************************************************************************* + * Written in 2020-2022 by Elichai Turkel * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include "examples_util.h" + +int main(void) { + unsigned char msg[12] = "Hello World!"; + unsigned char msg_hash[32]; + unsigned char tag[17] = "my_fancy_protocol"; + unsigned char seckey[32]; + unsigned char randomize[32]; + unsigned char auxiliary_rand[32]; + unsigned char serialized_pubkey[32]; + unsigned char signature[64]; + int is_signature_valid, is_signature_valid2; + int return_val; + secp256k1_xonly_pubkey pubkey; + secp256k1_keypair keypair; + /* Before we can call actual API functions, we need to create a "context". */ + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + if (!fill_random(randomize, sizeof(randomize))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Randomizing the context is recommended to protect against side-channel + * leakage See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + return_val = secp256k1_context_randomize(ctx, randomize); + assert(return_val); + + /*** Key Generation ***/ + + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + while (1) { + if (!fill_random(seckey, sizeof(seckey))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Try to create a keypair with a valid context, it should only fail if + * the secret key is zero or out of range. */ + if (secp256k1_keypair_create(ctx, &keypair, seckey)) { + break; + } + } + + /* Extract the X-only public key from the keypair. We pass NULL for + * `pk_parity` as the parity isn't needed for signing or verification. + * `secp256k1_keypair_xonly_pub` supports returning the parity for + * other use cases such as tests or verifying Taproot tweaks. + * This should never fail with a valid context and public key. */ + return_val = secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, &keypair); + assert(return_val); + + /* Serialize the public key. Should always return 1 for a valid public key. */ + return_val = secp256k1_xonly_pubkey_serialize(ctx, serialized_pubkey, &pubkey); + assert(return_val); + + /*** Signing ***/ + + /* Instead of signing (possibly very long) messages directly, we sign a + * 32-byte hash of the message in this example. + * + * We use secp256k1_tagged_sha256 to create this hash. This function expects + * a context-specific "tag", which restricts the context in which the signed + * messages should be considered valid. For example, if protocol A mandates + * to use the tag "my_fancy_protocol" and protocol B mandates to use the tag + * "my_boring_protocol", then signed messages from protocol A will never be + * valid in protocol B (and vice versa), even if keys are reused across + * protocols. This implements "domain separation", which is considered good + * practice. It avoids attacks in which users are tricked into signing a + * message that has intended consequences in the intended context (e.g., + * protocol A) but would have unintended consequences if it were valid in + * some other context (e.g., protocol B). */ + return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg)); + assert(return_val); + + /* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */ + if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) { + printf("Failed to generate randomness\n"); + return 1; + } + + /* Generate a Schnorr signature. + * + * We use the secp256k1_schnorrsig_sign32 function that provides a simple + * interface for signing 32-byte messages (which in our case is a hash of + * the actual message). BIP-340 recommends passing 32 bytes of randomness + * to the signing function to improve security against side-channel attacks. + * Signing with a valid context, a 32-byte message, a verified keypair, and + * any 32 bytes of auxiliary random data should never fail. */ + return_val = secp256k1_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand); + assert(return_val); + + /*** Verification ***/ + + /* Deserialize the public key. This will return 0 if the public key can't + * be parsed correctly */ + if (!secp256k1_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) { + printf("Failed parsing the public key\n"); + return 1; + } + + /* Compute the tagged hash on the received messages using the same tag as the signer. */ + return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg)); + assert(return_val); + + /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */ + is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey); + + + printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false"); + printf("Secret Key: "); + print_hex(seckey, sizeof(seckey)); + printf("Public Key: "); + print_hex(serialized_pubkey, sizeof(serialized_pubkey)); + printf("Signature: "); + print_hex(signature, sizeof(signature)); + + /* This will clear everything from the context and free the memory */ + secp256k1_context_destroy(ctx); + + /* Bonus example: if all we need is signature verification (and no key + generation or signing), we don't need to use a context created via + secp256k1_context_create(). We can simply use the static (i.e., global) + context secp256k1_context_static. See its description in + include/secp256k1.h for details. */ + is_signature_valid2 = secp256k1_schnorrsig_verify(secp256k1_context_static, + signature, msg_hash, 32, &pubkey); + assert(is_signature_valid2 == is_signature_valid); + + /* It's best practice to try to clear secrets from memory after using them. + * This is done because some bugs can allow an attacker to leak memory, for + * example through "out of bounds" array access (see Heartbleed), or the OS + * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. + * + * Here we are preventing these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + secure_erase(seckey, sizeof(seckey)); + return 0; +} diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h index 3e9c098d1..cfbdd528c 100644 --- a/src/secp256k1/include/secp256k1.h +++ b/src/secp256k1/include/secp256k1.h @@ -7,14 +7,16 @@ extern "C" { #include -/* These rules specify the order of arguments in API calls: +/** Unless explicitly stated all pointer arguments must not be NULL. + * + * The following rules specify the order of arguments in API calls: * * 1. Context pointers go first, followed by output arguments, combined * output/input arguments, and finally input-only arguments. - * 2. Array lengths always immediately the follow the argument whose length + * 2. Array lengths always immediately follow the argument whose length * they describe, even if this violates rule 1. * 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated - * later go first. This means: signatures, public nonces, private nonces, + * later go first. This means: signatures, public nonces, secret nonces, * messages, public keys, secret keys, tweaks. * 4. Arguments that are not data pointers go last, from more complex to less * complex: function pointers, algorithm names, messages, void pointers, @@ -22,33 +24,52 @@ extern "C" { * 5. Opaque data pointers follow the function pointer they are to be passed to. */ -/** Opaque data structure that holds context information (precomputed tables etc.). +/** Opaque data structure that holds context information * - * The purpose of context structures is to cache large precomputed data tables - * that are expensive to construct, and also to maintain the randomization data - * for blinding. + * The primary purpose of context objects is to store randomization data for + * enhanced protection against side-channel leakage. This protection is only + * effective if the context is randomized after its creation. See + * secp256k1_context_create for creation of contexts and + * secp256k1_context_randomize for randomization. * - * Do not create a new context object for each operation, as construction is - * far slower than all other API calls (~100 times slower than an ECDSA - * verification). + * A secondary purpose of context objects is to store pointers to callback + * functions that the library will call when certain error states arise. See + * secp256k1_context_set_error_callback as well as + * secp256k1_context_set_illegal_callback for details. Future library versions + * may use context objects for additional purposes. * * A constructed context can safely be used from multiple threads - * simultaneously, but API call that take a non-const pointer to a context + * simultaneously, but API calls that take a non-const pointer to a context * need exclusive access to it. In particular this is the case for - * secp256k1_context_destroy and secp256k1_context_randomize. + * secp256k1_context_destroy, secp256k1_context_preallocated_destroy, + * and secp256k1_context_randomize. * * Regarding randomization, either do it once at creation time (in which case * you do not need any locking for the other calls), or use a read-write lock. */ typedef struct secp256k1_context_struct secp256k1_context; +/** Opaque data structure that holds rewritable "scratch space" + * + * The purpose of this structure is to replace dynamic memory allocations, + * because we target architectures where this may not be available. It is + * essentially a resizable (within specified parameters) block of bytes, + * which is initially created either by memory allocation or TODO as a pointer + * into some fixed rewritable space. + * + * Unlike the context object, this cannot safely be shared between threads + * without additional synchronization logic. + */ +typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space; + /** Opaque data structure that holds a parsed and valid public key. * * The exact representation of data inside is implementation defined and not * guaranteed to be portable between different platforms or versions. It is * however guaranteed to be 64 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage, transmission, or - * comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. + * If you need to convert to a format suitable for storage or transmission, + * use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. To + * compare keys, use secp256k1_ec_pubkey_cmp. */ typedef struct { unsigned char data[64]; @@ -101,35 +122,52 @@ typedef int (*secp256k1_nonce_function)( # endif # endif -# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) -# if SECP256K1_GNUC_PREREQ(2,7) -# define SECP256K1_INLINE __inline__ -# elif (defined(_MSC_VER)) -# define SECP256K1_INLINE __inline -# else -# define SECP256K1_INLINE +/* When this header is used at build-time the SECP256K1_BUILD define needs to be set + * to correctly setup export attributes and nullness checks. This is normally done + * by secp256k1.c but to guard against this header being included before secp256k1.c + * has had a chance to set the define (e.g. via test harnesses that just includes + * secp256k1.c) we set SECP256K1_NO_BUILD when this header is processed without the + * BUILD define so this condition can be caught. + */ +#ifndef SECP256K1_BUILD +# define SECP256K1_NO_BUILD +#endif + +/* Symbol visibility. */ +#if defined(_WIN32) + /* GCC for Windows (e.g., MinGW) accepts the __declspec syntax + * for MSVC compatibility. A __declspec declaration implies (but is not + * exactly equivalent to) __attribute__ ((visibility("default"))), and so we + * actually want __declspec even on GCC, see "Microsoft Windows Function + * Attributes" in the GCC manual and the recommendations in + * https://gcc.gnu.org/wiki/Visibility. */ +# if defined(SECP256K1_BUILD) +# if defined(DLL_EXPORT) || defined(SECP256K1_DLL_EXPORT) + /* Building libsecp256k1 as a DLL. + * 1. If using Libtool, it defines DLL_EXPORT automatically. + * 2. In other cases, SECP256K1_DLL_EXPORT must be defined. */ +# define SECP256K1_API extern __declspec (dllexport) # endif -# else -# define SECP256K1_INLINE inline + /* The user must define SECP256K1_STATIC when consuming libsecp256k1 as a static + * library on Windows. */ +# elif !defined(SECP256K1_STATIC) + /* Consuming libsecp256k1 as a DLL. */ +# define SECP256K1_API extern __declspec (dllimport) # endif - +#endif #ifndef SECP256K1_API -# if defined(_WIN32) -# ifdef SECP256K1_BUILD -# define SECP256K1_API __declspec(dllexport) -# else -# define SECP256K1_API -# endif -# elif defined(__GNUC__) && defined(SECP256K1_BUILD) -# define SECP256K1_API __attribute__ ((visibility ("default"))) +# if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD) + /* Building libsecp256k1 on non-Windows using GCC or compatible. */ +# define SECP256K1_API extern __attribute__ ((visibility ("default"))) # else -# define SECP256K1_API + /* All cases not captured above. */ +# define SECP256K1_API extern # endif #endif -/**Warning attributes - * NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out - * some paranoid null checks. */ +/* Warning attributes + * NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out + * some paranoid null checks. */ # if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) # define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__)) # else @@ -141,21 +179,39 @@ typedef int (*secp256k1_nonce_function)( # define SECP256K1_ARG_NONNULL(_x) # endif -/** All flags' lower 8 bits indicate what they're for. Do not use directly. */ +/* Attribute for marking functions, types, and variables as deprecated */ +#if !defined(SECP256K1_BUILD) && defined(__has_attribute) +# if __has_attribute(__deprecated__) +# define SECP256K1_DEPRECATED(_msg) __attribute__ ((__deprecated__(_msg))) +# else +# define SECP256K1_DEPRECATED(_msg) +# endif +#else +# define SECP256K1_DEPRECATED(_msg) +#endif + +/* All flags' lower 8 bits indicate what they're for. Do not use directly. */ #define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1) #define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0) #define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1) -/** The higher bits contain the actual data. Do not use directly. */ +/* The higher bits contain the actual data. Do not use directly. */ #define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8) #define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) +#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY (1 << 10) #define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) -/** Flags to pass to secp256k1_context_create. */ +/** Context flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and + * secp256k1_context_preallocated_create. */ +#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT) + +/** Deprecated context flags. These flags are treated equivalent to SECP256K1_CONTEXT_NONE. */ #define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) #define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN) -#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT) -/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */ +/* Testing flag. Do not use. */ +#define SECP256K1_CONTEXT_DECLASSIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY) + +/** Flag to pass to secp256k1_ec_pubkey_serialize. */ #define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) #define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) @@ -166,34 +222,104 @@ typedef int (*secp256k1_nonce_function)( #define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06 #define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07 -/** Create a secp256k1 context object. +/** A built-in constant secp256k1 context object with static storage duration, to be + * used in conjunction with secp256k1_selftest. + * + * This context object offers *only limited functionality* , i.e., it cannot be used + * for API functions that perform computations involving secret keys, e.g., signing + * and public key generation. If this restriction applies to a specific API function, + * it is mentioned in its documentation. See secp256k1_context_create if you need a + * full context object that supports all functionality offered by the library. + * + * It is highly recommended to call secp256k1_selftest before using this context. + */ +SECP256K1_API const secp256k1_context *secp256k1_context_static; + +/** Deprecated alias for secp256k1_context_static. */ +SECP256K1_API const secp256k1_context *secp256k1_context_no_precomp +SECP256K1_DEPRECATED("Use secp256k1_context_static instead"); + +/** Perform basic self tests (to be used in conjunction with secp256k1_context_static) + * + * This function performs self tests that detect some serious usage errors and + * similar conditions, e.g., when the library is compiled for the wrong endianness. + * This is a last resort measure to be used in production. The performed tests are + * very rudimentary and are not intended as a replacement for running the test + * binaries. + * + * It is highly recommended to call this before using secp256k1_context_static. + * It is not necessary to call this function before using a context created with + * secp256k1_context_create (or secp256k1_context_preallocated_create), which will + * take care of performing the self tests. + * + * If the tests fail, this function will call the default error handler to abort the + * program (see secp256k1_context_set_error_callback). + */ +SECP256K1_API void secp256k1_selftest(void); + + +/** Create a secp256k1 context object (in dynamically allocated memory). + * + * This function uses malloc to allocate memory. It is guaranteed that malloc is + * called at most once for every call of this function. If you need to avoid dynamic + * memory allocation entirely, see secp256k1_context_static and the functions in + * secp256k1_preallocated.h. + * + * Returns: pointer to a newly created context object. + * In: flags: Always set to SECP256K1_CONTEXT_NONE (see below). * - * Returns: a newly created context object. - * In: flags: which parts of the context to initialize. + * The only valid non-deprecated flag in recent library versions is + * SECP256K1_CONTEXT_NONE, which will create a context sufficient for all functionality + * offered by the library. All other (deprecated) flags will be treated as equivalent + * to the SECP256K1_CONTEXT_NONE flag. Though the flags parameter primarily exists for + * historical reasons, future versions of the library may introduce new flags. * - * See also secp256k1_context_randomize. + * If the context is intended to be used for API functions that perform computations + * involving secret keys, e.g., signing and public key generation, then it is highly + * recommended to call secp256k1_context_randomize on the context before calling + * those API functions. This will provide enhanced protection against side-channel + * leakage, see secp256k1_context_randomize for details. + * + * Do not create a new context object for each operation, as construction and + * randomization can take non-negligible time. */ -SECP256K1_API secp256k1_context* secp256k1_context_create( +SECP256K1_API secp256k1_context *secp256k1_context_create( unsigned int flags ) SECP256K1_WARN_UNUSED_RESULT; -/** Copies a secp256k1 context object. +/** Copy a secp256k1 context object (into dynamically allocated memory). + * + * This function uses malloc to allocate memory. It is guaranteed that malloc is + * called at most once for every call of this function. If you need to avoid dynamic + * memory allocation entirely, see the functions in secp256k1_preallocated.h. + * + * Cloning secp256k1_context_static is not possible, and should not be emulated by + * the caller (e.g., using memcpy). Create a new context instead. * - * Returns: a newly created context object. - * Args: ctx: an existing context to copy (cannot be NULL) + * Returns: pointer to a newly created context object. + * Args: ctx: pointer to a context to copy (not secp256k1_context_static). */ -SECP256K1_API secp256k1_context* secp256k1_context_clone( - const secp256k1_context* ctx +SECP256K1_API secp256k1_context *secp256k1_context_clone( + const secp256k1_context *ctx ) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; -/** Destroy a secp256k1 context object. +/** Destroy a secp256k1 context object (created in dynamically allocated memory). * * The context pointer may not be used afterwards. - * Args: ctx: an existing context to destroy (cannot be NULL) + * + * The context to destroy must have been created using secp256k1_context_create + * or secp256k1_context_clone. If the context has instead been created using + * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone, the + * behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must + * be used instead. + * + * Args: ctx: pointer to a context to destroy, constructed using + * secp256k1_context_create or secp256k1_context_clone + * (i.e., not secp256k1_context_static). */ SECP256K1_API void secp256k1_context_destroy( - secp256k1_context* ctx -); + secp256k1_context *ctx +) SECP256K1_ARG_NONNULL(1); /** Set a callback function to be called when an illegal argument is passed to * an API call. It will only trigger for violations that are mentioned @@ -209,20 +335,40 @@ SECP256K1_API void secp256k1_context_destroy( * to cause a crash, though its return value and output arguments are * undefined. * - * Args: ctx: an existing context object (cannot be NULL) - * In: fun: a pointer to a function to call when an illegal argument is - * passed to the API, taking a message and an opaque pointer - * (NULL restores a default handler that calls abort). - * data: the opaque pointer to pass to fun above. + * When this function has not been called (or called with fn==NULL), then the + * default handler will be used. The library provides a default handler which + * writes the message to stderr and calls abort. This default handler can be + * replaced at link time if the preprocessor macro + * USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build + * has been configured with --enable-external-default-callbacks. Then the + * following two symbols must be provided to link against: + * - void secp256k1_default_illegal_callback_fn(const char *message, void *data); + * - void secp256k1_default_error_callback_fn(const char *message, void *data); + * The library can call these default handlers even before a proper callback data + * pointer could have been set using secp256k1_context_set_illegal_callback or + * secp256k1_context_set_error_callback, e.g., when the creation of a context + * fails. In this case, the corresponding default handler will be called with + * the data pointer argument set to NULL. + * + * Args: ctx: pointer to a context object. + * In: fun: pointer to a function to call when an illegal argument is + * passed to the API, taking a message and an opaque pointer. + * (NULL restores the default handler.) + * data: the opaque pointer to pass to fun above, must be NULL for the default handler. + * + * See also secp256k1_context_set_error_callback. */ SECP256K1_API void secp256k1_context_set_illegal_callback( - secp256k1_context* ctx, - void (*fun)(const char* message, void* data), - const void* data + secp256k1_context *ctx, + void (*fun)(const char *message, void *data), + const void *data ) SECP256K1_ARG_NONNULL(1); /** Set a callback function to be called when an internal consistency check - * fails. The default is crashing. + * fails. + * + * The default callback writes an error message to stderr and calls abort + * to abort the program. * * This can only trigger in case of a hardware failure, miscompilation, * memory corruption, serious bug in the library, or other error would can @@ -231,23 +377,49 @@ SECP256K1_API void secp256k1_context_set_illegal_callback( * for that). After this callback returns, anything may happen, including * crashing. * - * Args: ctx: an existing context object (cannot be NULL) - * In: fun: a pointer to a function to call when an internal error occurs, - * taking a message and an opaque pointer (NULL restores a default - * handler that calls abort). - * data: the opaque pointer to pass to fun above. + * Args: ctx: pointer to a context object. + * In: fun: pointer to a function to call when an internal error occurs, + * taking a message and an opaque pointer (NULL restores the + * default handler, see secp256k1_context_set_illegal_callback + * for details). + * data: the opaque pointer to pass to fun above, must be NULL for the default handler. + * + * See also secp256k1_context_set_illegal_callback. */ SECP256K1_API void secp256k1_context_set_error_callback( - secp256k1_context* ctx, - void (*fun)(const char* message, void* data), - const void* data + secp256k1_context *ctx, + void (*fun)(const char *message, void *data), + const void *data +) SECP256K1_ARG_NONNULL(1); + +/** Create a secp256k1 scratch space object. + * + * Returns: a newly created scratch space. + * Args: ctx: pointer to a context object. + * In: size: amount of memory to be available as scratch space. Some extra + * (<100 bytes) will be allocated for extra accounting. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space *secp256k1_scratch_space_create( + const secp256k1_context *ctx, + size_t size +) SECP256K1_ARG_NONNULL(1); + +/** Destroy a secp256k1 scratch space. + * + * The pointer may not be used afterwards. + * Args: ctx: pointer to a context object. + * scratch: space to destroy + */ +SECP256K1_API void secp256k1_scratch_space_destroy( + const secp256k1_context *ctx, + secp256k1_scratch_space *scratch ) SECP256K1_ARG_NONNULL(1); /** Parse a variable-length public key into the pubkey object. * * Returns: 1 if the public key was fully valid. * 0 if the public key could not be parsed or is invalid. - * Args: ctx: a secp256k1 context object. + * Args: ctx: pointer to a context object. * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a * parsed version of input. If not, its value is undefined. * In: input: pointer to a serialized public key @@ -258,8 +430,8 @@ SECP256K1_API void secp256k1_context_set_error_callback( * byte 0x06 or 0x07) format public keys. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse( - const secp256k1_context* ctx, - secp256k1_pubkey* pubkey, + const secp256k1_context *ctx, + secp256k1_pubkey *pubkey, const unsigned char *input, size_t inputlen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); @@ -267,65 +439,94 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse( /** Serialize a pubkey object into a serialized byte sequence. * * Returns: 1 always. - * Args: ctx: a secp256k1 context object. - * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if + * Args: ctx: pointer to a context object. + * Out: output: pointer to a 65-byte (if compressed==0) or 33-byte (if * compressed==1) byte array to place the serialized key * in. - * In/Out: outputlen: a pointer to an integer which is initially set to the + * In/Out: outputlen: pointer to an integer which is initially set to the * size of output, and is overwritten with the written * size. - * In: pubkey: a pointer to a secp256k1_pubkey containing an + * In: pubkey: pointer to a secp256k1_pubkey containing an * initialized public key. * flags: SECP256K1_EC_COMPRESSED if serialization should be in * compressed format, otherwise SECP256K1_EC_UNCOMPRESSED. */ SECP256K1_API int secp256k1_ec_pubkey_serialize( - const secp256k1_context* ctx, + const secp256k1_context *ctx, unsigned char *output, size_t *outputlen, - const secp256k1_pubkey* pubkey, + const secp256k1_pubkey *pubkey, unsigned int flags ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +/** Compare two public keys using lexicographic (of compressed serialization) order + * + * Returns: <0 if the first public key is less than the second + * >0 if the first public key is greater than the second + * 0 if the two public keys are equal + * Args: ctx: pointer to a context object + * In: pubkey1: first public key to compare + * pubkey2: second public key to compare + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp( + const secp256k1_context *ctx, + const secp256k1_pubkey *pubkey1, + const secp256k1_pubkey *pubkey2 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Sort public keys using lexicographic (of compressed serialization) order + * + * Returns: 0 if the arguments are invalid. 1 otherwise. + * + * Args: ctx: pointer to a context object + * In: pubkeys: array of pointers to pubkeys to sort + * n_pubkeys: number of elements in the pubkeys array + */ +SECP256K1_API int secp256k1_ec_pubkey_sort( + const secp256k1_context *ctx, + const secp256k1_pubkey **pubkeys, + size_t n_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + /** Parse an ECDSA signature in compact (64 bytes) format. * * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input64: a pointer to the 64-byte array to parse + * Args: ctx: pointer to a context object + * Out: sig: pointer to a signature object + * In: input64: pointer to the 64-byte array to parse * * The signature must consist of a 32-byte big endian R value, followed by a * 32-byte big endian S value. If R or S fall outside of [0..order-1], the * encoding is invalid. R and S with value 0 are allowed in the encoding. * * After the call, sig will always be initialized. If parsing failed or R or - * S are zero, the resulting sig value is guaranteed to fail validation for any - * message and public key. + * S are zero, the resulting sig value is guaranteed to fail verification for + * any message and public key. */ SECP256K1_API int secp256k1_ecdsa_signature_parse_compact( - const secp256k1_context* ctx, - secp256k1_ecdsa_signature* sig, + const secp256k1_context *ctx, + secp256k1_ecdsa_signature *sig, const unsigned char *input64 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Parse a DER ECDSA signature. * * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input: a pointer to the signature to be parsed + * Args: ctx: pointer to a context object + * Out: sig: pointer to a signature object + * In: input: pointer to the signature to be parsed * inputlen: the length of the array pointed to be input * * This function will accept any valid DER encoded signature, even if the * encoded numbers are out of range. * * After the call, sig will always be initialized. If parsing failed or the - * encoded numbers are out of range, signature validation with it is + * encoded numbers are out of range, signature verification with it is * guaranteed to fail for every message and public key. */ SECP256K1_API int secp256k1_ecdsa_signature_parse_der( - const secp256k1_context* ctx, - secp256k1_ecdsa_signature* sig, + const secp256k1_context *ctx, + secp256k1_ecdsa_signature *sig, const unsigned char *input, size_t inputlen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); @@ -333,71 +534,77 @@ SECP256K1_API int secp256k1_ecdsa_signature_parse_der( /** Serialize an ECDSA signature in DER format. * * Returns: 1 if enough space was available to serialize, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: output: a pointer to an array to store the DER serialization - * In/Out: outputlen: a pointer to a length integer. Initially, this integer + * Args: ctx: pointer to a context object + * Out: output: pointer to an array to store the DER serialization + * In/Out: outputlen: pointer to a length integer. Initially, this integer * should be set to the length of output. After the call * it will be set to the length of the serialization (even * if 0 was returned). - * In: sig: a pointer to an initialized signature object + * In: sig: pointer to an initialized signature object */ SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( - const secp256k1_context* ctx, + const secp256k1_context *ctx, unsigned char *output, size_t *outputlen, - const secp256k1_ecdsa_signature* sig + const secp256k1_ecdsa_signature *sig ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Serialize an ECDSA signature in compact (64 byte) format. * * Returns: 1 - * Args: ctx: a secp256k1 context object - * Out: output64: a pointer to a 64-byte array to store the compact serialization - * In: sig: a pointer to an initialized signature object + * Args: ctx: pointer to a context object + * Out: output64: pointer to a 64-byte array to store the compact serialization + * In: sig: pointer to an initialized signature object * * See secp256k1_ecdsa_signature_parse_compact for details about the encoding. */ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( - const secp256k1_context* ctx, + const secp256k1_context *ctx, unsigned char *output64, - const secp256k1_ecdsa_signature* sig + const secp256k1_ecdsa_signature *sig ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Verify an ECDSA signature. * * Returns: 1: correct signature * 0: incorrect or unparseable signature - * Args: ctx: a secp256k1 context object, initialized for verification. - * In: sig: the signature being verified (cannot be NULL) - * msg32: the 32-byte message hash being verified (cannot be NULL) - * pubkey: pointer to an initialized public key to verify with (cannot be NULL) + * Args: ctx: pointer to a context object + * In: sig: the signature being verified. + * msghash32: the 32-byte message hash being verified. + * The verifier must make sure to apply a cryptographic + * hash function to the message by itself and not accept an + * msghash32 value directly. Otherwise, it would be easy to + * create a "valid" signature without knowledge of the + * secret key. See also + * https://bitcoin.stackexchange.com/a/81116/35586 for more + * background on this topic. + * pubkey: pointer to an initialized public key to verify with. * * To avoid accepting malleable signatures, only ECDSA signatures in lower-S * form are accepted. * * If you need to accept ECDSA signatures from sources that do not obey this * rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to - * validation, but be aware that doing so results in malleable signatures. + * verification, but be aware that doing so results in malleable signatures. * * For details, see the comments for that function. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( - const secp256k1_context* ctx, + const secp256k1_context *ctx, const secp256k1_ecdsa_signature *sig, - const unsigned char *msg32, + const unsigned char *msghash32, const secp256k1_pubkey *pubkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Convert a signature to a normalized lower-S form. * * Returns: 1 if sigin was not normalized, 0 if it already was. - * Args: ctx: a secp256k1 context object - * Out: sigout: a pointer to a signature to fill with the normalized form, + * Args: ctx: pointer to a context object + * Out: sigout: pointer to a signature to fill with the normalized form, * or copy if the input was already normalized. (can be NULL if * you're only interested in whether the input was already * normalized). - * In: sigin: a pointer to a signature to check/normalize (cannot be NULL, - * can be identical to sigout) + * In: sigin: pointer to a signature to check/normalize (can be identical to sigout) * * With ECDSA a third-party can forge a second distinct signature of the same * message, given a single initial signature, but without knowing the key. This @@ -431,7 +638,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( * secp256k1_ecdsa_signature_normalize must be called before verification. */ SECP256K1_API int secp256k1_ecdsa_signature_normalize( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3); @@ -440,179 +647,274 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize( * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of * extra entropy. */ -SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; +SECP256K1_API const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ -SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; +SECP256K1_API const secp256k1_nonce_function secp256k1_nonce_function_default; /** Create an ECDSA signature. * * Returns: 1: signature created - * 0: the nonce generation function failed, or the private key was invalid. - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) - * In: msg32: the 32-byte message hash being signed (cannot be NULL) - * seckey: pointer to a 32-byte secret key (cannot be NULL) - * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used - * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + * 0: the nonce generation function failed, or the secret key was invalid. + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * Out: sig: pointer to an array where the signature will be placed. + * In: msghash32: the 32-byte message hash being signed. + * seckey: pointer to a 32-byte secret key. + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_default is used. + * ndata: pointer to arbitrary data used by the nonce generation function + * (can be NULL). If it is non-NULL and + * secp256k1_nonce_function_default is used, then ndata must be a + * pointer to 32-bytes of additional data. * * The created signature is always in lower-S form. See * secp256k1_ecdsa_signature_normalize for more details. */ SECP256K1_API int secp256k1_ecdsa_sign( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_ecdsa_signature *sig, - const unsigned char *msg32, + const unsigned char *msghash32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void *ndata ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Verify an ECDSA secret key. + * + * A secret key is valid if it is not 0 and less than the secp256k1 curve order + * when interpreted as an integer (most significant byte first). The + * probability of choosing a 32-byte string uniformly at random which is an + * invalid secret key is negligible. * * Returns: 1: secret key is valid * 0: secret key is invalid - * Args: ctx: pointer to a context object (cannot be NULL) - * In: seckey: pointer to a 32-byte secret key (cannot be NULL) + * Args: ctx: pointer to a context object. + * In: seckey: pointer to a 32-byte secret key. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( - const secp256k1_context* ctx, + const secp256k1_context *ctx, const unsigned char *seckey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); /** Compute the public key for a secret key. * - * Returns: 1: secret was valid, public key stores - * 0: secret was invalid, try again - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: pubkey: pointer to the created public key (cannot be NULL) - * In: seckey: pointer to a 32-byte private key (cannot be NULL) + * Returns: 1: secret was valid, public key stores. + * 0: secret was invalid, try again. + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * Out: pubkey: pointer to the created public key. + * In: seckey: pointer to a 32-byte secret key. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Negates a private key in place. +/** Negates a secret key in place. * - * Returns: 1 always - * Args: ctx: pointer to a context object - * In/Out: pubkey: pointer to the public key to be negated (cannot be NULL) + * Returns: 0 if the given secret key is invalid according to + * secp256k1_ec_seckey_verify. 1 otherwise + * Args: ctx: pointer to a context object + * In/Out: seckey: pointer to the 32-byte secret key to be negated. If the + * secret key is invalid according to + * secp256k1_ec_seckey_verify, this function returns 0 and + * seckey will be set to some unspecified value. */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate( - const secp256k1_context* ctx, +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate( + const secp256k1_context *ctx, unsigned char *seckey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); +/** Same as secp256k1_ec_seckey_negate, but DEPRECATED. Will be removed in + * future versions. */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate( + const secp256k1_context *ctx, + unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) + SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_negate instead"); + /** Negates a public key in place. * * Returns: 1 always * Args: ctx: pointer to a context object - * In/Out: pubkey: pointer to the public key to be negated (cannot be NULL) + * In/Out: pubkey: pointer to the public key to be negated. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_negate( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_pubkey *pubkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); -/** Tweak a private key by adding tweak to it. - * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for - * uniformly random 32-byte arrays, or if the resulting private key - * would be invalid (only when the tweak is the complement of the - * private key). 1 otherwise. - * Args: ctx: pointer to a context object (cannot be NULL). - * In/Out: seckey: pointer to a 32-byte private key. - * In: tweak: pointer to a 32-byte tweak. +/** Tweak a secret key by adding tweak to it. + * + * Returns: 0 if the arguments are invalid or the resulting secret key would be + * invalid (only when the tweak is the negation of the secret key). 1 + * otherwise. + * Args: ctx: pointer to a context object. + * In/Out: seckey: pointer to a 32-byte secret key. If the secret key is + * invalid according to secp256k1_ec_seckey_verify, this + * function returns 0. seckey will be set to some unspecified + * value if this function returns 0. + * In: tweak32: pointer to a 32-byte tweak, which must be valid according to + * secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly + * random 32-byte tweaks, the chance of being invalid is + * negligible (around 1 in 2^128). */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( - const secp256k1_context* ctx, +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_add( + const secp256k1_context *ctx, unsigned char *seckey, - const unsigned char *tweak + const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +/** Same as secp256k1_ec_seckey_tweak_add, but DEPRECATED. Will be removed in + * future versions. */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( + const secp256k1_context *ctx, + unsigned char *seckey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) + SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_add instead"); + /** Tweak a public key by adding tweak times the generator to it. - * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for - * uniformly random 32-byte arrays, or if the resulting public key - * would be invalid (only when the tweak is the complement of the - * corresponding private key). 1 otherwise. - * Args: ctx: pointer to a context object initialized for validation - * (cannot be NULL). - * In/Out: pubkey: pointer to a public key object. - * In: tweak: pointer to a 32-byte tweak. + * + * Returns: 0 if the arguments are invalid or the resulting public key would be + * invalid (only when the tweak is the negation of the corresponding + * secret key). 1 otherwise. + * Args: ctx: pointer to a context object. + * In/Out: pubkey: pointer to a public key object. pubkey will be set to an + * invalid value if this function returns 0. + * In: tweak32: pointer to a 32-byte tweak, which must be valid according to + * secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly + * random 32-byte tweaks, the chance of being invalid is + * negligible (around 1 in 2^128). */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_pubkey *pubkey, - const unsigned char *tweak + const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Tweak a private key by multiplying it by a tweak. - * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for - * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. - * Args: ctx: pointer to a context object (cannot be NULL). - * In/Out: seckey: pointer to a 32-byte private key. - * In: tweak: pointer to a 32-byte tweak. +/** Tweak a secret key by multiplying it by a tweak. + * + * Returns: 0 if the arguments are invalid. 1 otherwise. + * Args: ctx: pointer to a context object. + * In/Out: seckey: pointer to a 32-byte secret key. If the secret key is + * invalid according to secp256k1_ec_seckey_verify, this + * function returns 0. seckey will be set to some unspecified + * value if this function returns 0. + * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to + * secp256k1_ec_seckey_verify, this function returns 0. For + * uniformly random 32-byte arrays the chance of being invalid + * is negligible (around 1 in 2^128). */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( - const secp256k1_context* ctx, +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_tweak_mul( + const secp256k1_context *ctx, unsigned char *seckey, - const unsigned char *tweak + const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +/** Same as secp256k1_ec_seckey_tweak_mul, but DEPRECATED. Will be removed in + * future versions. */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( + const secp256k1_context *ctx, + unsigned char *seckey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) + SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_mul instead"); + /** Tweak a public key by multiplying it by a tweak value. - * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for - * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. - * Args: ctx: pointer to a context object initialized for validation - * (cannot be NULL). - * In/Out: pubkey: pointer to a public key obkect. - * In: tweak: pointer to a 32-byte tweak. + * + * Returns: 0 if the arguments are invalid. 1 otherwise. + * Args: ctx: pointer to a context object. + * In/Out: pubkey: pointer to a public key object. pubkey will be set to an + * invalid value if this function returns 0. + * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according to + * secp256k1_ec_seckey_verify, this function returns 0. For + * uniformly random 32-byte arrays the chance of being invalid + * is negligible (around 1 in 2^128). */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_pubkey *pubkey, - const unsigned char *tweak + const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -/** Updates the context randomization to protect against side-channel leakage. - * Returns: 1: randomization successfully updated +/** Randomizes the context to provide enhanced protection against side-channel leakage. + * + * Returns: 1: randomization successful * 0: error - * Args: ctx: pointer to a context object (cannot be NULL) - * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state) + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state). * - * While secp256k1 code is written to be constant-time no matter what secret - * values are, it's possible that a future compiler may output code which isn't, + * While secp256k1 code is written and tested to be constant-time no matter what + * secret values are, it is possible that a compiler may output code which is not, * and also that the CPU may not emit the same radio frequencies or draw the same - * amount power for all values. - * - * This function provides a seed which is combined into the blinding value: that - * blinding value is added before each multiplication (and removed afterwards) so - * that it does not affect function results, but shields against attacks which - * rely on any input-dependent behaviour. - * - * You should call this after secp256k1_context_create or - * secp256k1_context_clone, and may call this repeatedly afterwards. + * amount of power for all values. Randomization of the context shields against + * side-channel observations which aim to exploit secret-dependent behaviour in + * certain computations which involve secret keys. + * + * It is highly recommended to call this function on contexts returned from + * secp256k1_context_create or secp256k1_context_clone (or from the corresponding + * functions in secp256k1_preallocated.h) before using these contexts to call API + * functions that perform computations involving secret keys, e.g., signing and + * public key generation. It is possible to call this function more than once on + * the same context, and doing so before every few computations involving secret + * keys is recommended as a defense-in-depth measure. Randomization of the static + * context secp256k1_context_static is not supported. + * + * Currently, the random seed is mainly used for blinding multiplications of a + * secret scalar with the elliptic curve base point. Multiplications of this + * kind are performed by exactly those API functions which are documented to + * require a context that is not secp256k1_context_static. As a rule of thumb, + * these are all functions which take a secret key (or a keypair) as an input. + * A notable exception to that rule is the ECDH module, which relies on a different + * kind of elliptic curve point multiplication and thus does not benefit from + * enhanced protection against side-channel leakage currently. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( - secp256k1_context* ctx, + secp256k1_context *ctx, const unsigned char *seed32 ) SECP256K1_ARG_NONNULL(1); /** Add a number of public keys together. + * * Returns: 1: the sum of the public keys is valid. * 0: the sum of the public keys is not valid. - * Args: ctx: pointer to a context object - * Out: out: pointer to a public key object for placing the resulting public key - * (cannot be NULL) - * In: ins: pointer to array of pointers to public keys (cannot be NULL) - * n: the number of public keys to add together (must be at least 1) + * Args: ctx: pointer to a context object. + * Out: out: pointer to a public key object for placing the resulting public key. + * In: ins: pointer to array of pointers to public keys. + * n: the number of public keys to add together (must be at least 1). */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_pubkey *out, - const secp256k1_pubkey * const * ins, + const secp256k1_pubkey * const *ins, size_t n -) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute a tagged hash as defined in BIP-340. + * + * This is useful for creating a message hash and achieving domain separation + * through an application-specific tag. This function returns + * SHA256(SHA256(tag)||SHA256(tag)||msg). Therefore, tagged hash + * implementations optimized for a specific tag can precompute the SHA256 state + * after hashing the tag hashes. + * + * Returns: 1 always. + * Args: ctx: pointer to a context object + * Out: hash32: pointer to a 32-byte array to store the resulting hash + * In: tag: pointer to an array containing the tag + * taglen: length of the tag array + * msg: pointer to an array containing the message + * msglen: length of the message array + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_tagged_sha256( + const secp256k1_context *ctx, + unsigned char *hash32, + const unsigned char *tag, + size_t taglen, + const unsigned char *msg, + size_t msglen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); #ifdef __cplusplus } diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h index 88492dc1a..4d9da3461 100644 --- a/src/secp256k1/include/secp256k1_ecdh.h +++ b/src/secp256k1/include/secp256k1_ecdh.h @@ -7,21 +7,53 @@ extern "C" { #endif +/** A pointer to a function that hashes an EC point to obtain an ECDH secret + * + * Returns: 1 if the point was successfully hashed. + * 0 will cause secp256k1_ecdh to fail and return 0. + * Other return values are not allowed, and the behaviour of + * secp256k1_ecdh is undefined for other return values. + * Out: output: pointer to an array to be filled by the function + * In: x32: pointer to a 32-byte x coordinate + * y32: pointer to a 32-byte y coordinate + * data: arbitrary data pointer that is passed through + */ +typedef int (*secp256k1_ecdh_hash_function)( + unsigned char *output, + const unsigned char *x32, + const unsigned char *y32, + void *data +); + +/** An implementation of SHA256 hash function that applies to compressed public key. + * Populates the output parameter with 32 bytes. */ +SECP256K1_API const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256; + +/** A default ECDH hash function (currently equal to secp256k1_ecdh_hash_function_sha256). + * Populates the output parameter with 32 bytes. */ +SECP256K1_API const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default; + /** Compute an EC Diffie-Hellman secret in constant time + * * Returns: 1: exponentiation was successful - * 0: scalar was invalid (zero or overflow) - * Args: ctx: pointer to a context object (cannot be NULL) - * Out: result: a 32-byte array which will be populated by an ECDH - * secret computed from the point and scalar - * In: pubkey: a pointer to a secp256k1_pubkey containing an - * initialized public key - * privkey: a 32-byte scalar with which to multiply the point + * 0: scalar was invalid (zero or overflow) or hashfp returned 0 + * Args: ctx: pointer to a context object. + * Out: output: pointer to an array to be filled by hashfp. + * In: pubkey: pointer to a secp256k1_pubkey containing an initialized public key. + * seckey: a 32-byte scalar with which to multiply the point. + * hashfp: pointer to a hash function. If NULL, + * secp256k1_ecdh_hash_function_sha256 is used + * (in which case, 32 bytes will be written to output). + * data: arbitrary data pointer that is passed through to hashfp + * (can be NULL for secp256k1_ecdh_hash_function_sha256). */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( - const secp256k1_context* ctx, - unsigned char *result, + const secp256k1_context *ctx, + unsigned char *output, const secp256k1_pubkey *pubkey, - const unsigned char *privkey + const unsigned char *seckey, + secp256k1_ecdh_hash_function hashfp, + void *data ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); #ifdef __cplusplus diff --git a/src/secp256k1/include/secp256k1_ellswift.h b/src/secp256k1/include/secp256k1_ellswift.h new file mode 100644 index 000000000..ae37287f8 --- /dev/null +++ b/src/secp256k1/include/secp256k1_ellswift.h @@ -0,0 +1,200 @@ +#ifndef SECP256K1_ELLSWIFT_H +#define SECP256K1_ELLSWIFT_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This module provides an implementation of ElligatorSwift as well as a + * version of x-only ECDH using it (including compatibility with BIP324). + * + * ElligatorSwift is described in https://eprint.iacr.org/2022/759 by + * Chavez-Saab, Rodriguez-Henriquez, and Tibouchi. It permits encoding + * uniformly chosen public keys as 64-byte arrays which are indistinguishable + * from uniformly random arrays. + * + * Let f be the function from pairs of field elements to point X coordinates, + * defined as follows (all operations modulo p = 2^256 - 2^32 - 977) + * f(u,t): + * - Let C = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852, + * a square root of -3. + * - If u=0, set u=1 instead. + * - If t=0, set t=1 instead. + * - If u^3 + t^2 + 7 = 0, multiply t by 2. + * - Let X = (u^3 + 7 - t^2) / (2 * t) + * - Let Y = (X + t) / (C * u) + * - Return the first in [u + 4 * Y^2, (-X/Y - u) / 2, (X/Y - u) / 2] that is an + * X coordinate on the curve (at least one of them is, for any u and t). + * + * Then an ElligatorSwift encoding of x consists of the 32-byte big-endian + * encodings of field elements u and t concatenated, where f(u,t) = x. + * The encoding algorithm is described in the paper, and effectively picks a + * uniformly random pair (u,t) among those which encode x. + * + * If the Y coordinate is relevant, it is given the same parity as t. + * + * Changes w.r.t. the the paper: + * - The u=0, t=0, and u^3+t^2+7=0 conditions result in decoding to the point + * at infinity in the paper. Here they are remapped to finite points. + * - The paper uses an additional encoding bit for the parity of y. Here the + * parity of t is used (negating t does not affect the decoded x coordinate, + * so this is possible). + * + * For mathematical background about the scheme, see the doc/ellswift.md file. + */ + +/** A pointer to a function used by secp256k1_ellswift_xdh to hash the shared X + * coordinate along with the encoded public keys to a uniform shared secret. + * + * Returns: 1 if a shared secret was successfully computed. + * 0 will cause secp256k1_ellswift_xdh to fail and return 0. + * Other return values are not allowed, and the behaviour of + * secp256k1_ellswift_xdh is undefined for other return values. + * Out: output: pointer to an array to be filled by the function + * In: x32: pointer to the 32-byte serialized X coordinate + * of the resulting shared point (will not be NULL) + * ell_a64: pointer to the 64-byte encoded public key of party A + * (will not be NULL) + * ell_b64: pointer to the 64-byte encoded public key of party B + * (will not be NULL) + * data: arbitrary data pointer that is passed through + */ +typedef int (*secp256k1_ellswift_xdh_hash_function)( + unsigned char *output, + const unsigned char *x32, + const unsigned char *ell_a64, + const unsigned char *ell_b64, + void *data +); + +/** An implementation of an secp256k1_ellswift_xdh_hash_function which uses + * SHA256(prefix64 || ell_a64 || ell_b64 || x32), where prefix64 is the 64-byte + * array pointed to by data. */ +SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_prefix; + +/** An implementation of an secp256k1_ellswift_xdh_hash_function compatible with + * BIP324. It returns H_tag(ell_a64 || ell_b64 || x32), where H_tag is the + * BIP340 tagged hash function with tag "bip324_ellswift_xonly_ecdh". Equivalent + * to secp256k1_ellswift_xdh_hash_function_prefix with prefix64 set to + * SHA256("bip324_ellswift_xonly_ecdh")||SHA256("bip324_ellswift_xonly_ecdh"). + * The data argument is ignored. */ +SECP256K1_API const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_bip324; + +/** Construct a 64-byte ElligatorSwift encoding of a given pubkey. + * + * Returns: 1 always. + * Args: ctx: pointer to a context object + * Out: ell64: pointer to a 64-byte array to be filled + * In: pubkey: pointer to a secp256k1_pubkey containing an + * initialized public key + * rnd32: pointer to 32 bytes of randomness + * + * It is recommended that rnd32 consists of 32 uniformly random bytes, not + * known to any adversary trying to detect whether public keys are being + * encoded, though 16 bytes of randomness (padded to an array of 32 bytes, + * e.g., with zeros) suffice to make the result indistinguishable from + * uniform. The randomness in rnd32 must not be a deterministic function of + * the pubkey (it can be derived from the private key, though). + * + * It is not guaranteed that the computed encoding is stable across versions + * of the library, even if all arguments to this function (including rnd32) + * are the same. + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellswift_encode( + const secp256k1_context *ctx, + unsigned char *ell64, + const secp256k1_pubkey *pubkey, + const unsigned char *rnd32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Decode a 64-bytes ElligatorSwift encoded public key. + * + * Returns: always 1 + * Args: ctx: pointer to a context object + * Out: pubkey: pointer to a secp256k1_pubkey that will be filled + * In: ell64: pointer to a 64-byte array to decode + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellswift_decode( + const secp256k1_context *ctx, + secp256k1_pubkey *pubkey, + const unsigned char *ell64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute an ElligatorSwift public key for a secret key. + * + * Returns: 1: secret was valid, public key was stored. + * 0: secret was invalid, try again. + * Args: ctx: pointer to a context object + * Out: ell64: pointer to a 64-byte array to receive the ElligatorSwift + * public key + * In: seckey32: pointer to a 32-byte secret key + * auxrnd32: (optional) pointer to 32 bytes of randomness + * + * Constant time in seckey and auxrnd32, but not in the resulting public key. + * + * It is recommended that auxrnd32 contains 32 uniformly random bytes, though + * it is optional (and does result in encodings that are indistinguishable from + * uniform even without any auxrnd32). It differs from the (mandatory) rnd32 + * argument to secp256k1_ellswift_encode in this regard. + * + * This function can be used instead of calling secp256k1_ec_pubkey_create + * followed by secp256k1_ellswift_encode. It is safer, as it uses the secret + * key as entropy for the encoding (supplemented with auxrnd32, if provided). + * + * Like secp256k1_ellswift_encode, this function does not guarantee that the + * computed encoding is stable across versions of the library, even if all + * arguments (including auxrnd32) are the same. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create( + const secp256k1_context *ctx, + unsigned char *ell64, + const unsigned char *seckey32, + const unsigned char *auxrnd32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Given a private key, and ElligatorSwift public keys sent in both directions, + * compute a shared secret using x-only Elliptic Curve Diffie-Hellman (ECDH). + * + * Returns: 1: shared secret was successfully computed + * 0: secret was invalid or hashfp returned 0 + * Args: ctx: pointer to a context object. + * Out: output: pointer to an array to be filled by hashfp. + * In: ell_a64: pointer to the 64-byte encoded public key of party A + * (will not be NULL) + * ell_b64: pointer to the 64-byte encoded public key of party B + * (will not be NULL) + * seckey32: pointer to our 32-byte secret key + * party: boolean indicating which party we are: zero if we are + * party A, non-zero if we are party B. seckey32 must be + * the private key corresponding to that party's ell_?64. + * This correspondence is not checked. + * hashfp: pointer to a hash function. + * data: arbitrary data pointer passed through to hashfp. + * + * Constant time in seckey32. + * + * This function is more efficient than decoding the public keys, and performing + * ECDH on them. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_xdh( + const secp256k1_context *ctx, + unsigned char *output, + const unsigned char *ell_a64, + const unsigned char *ell_b64, + const unsigned char *seckey32, + int party, + secp256k1_ellswift_xdh_hash_function hashfp, + void *data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ELLSWIFT_H */ diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h new file mode 100644 index 000000000..ad70b92f9 --- /dev/null +++ b/src/secp256k1/include/secp256k1_extrakeys.h @@ -0,0 +1,247 @@ +#ifndef SECP256K1_EXTRAKEYS_H +#define SECP256K1_EXTRAKEYS_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Opaque data structure that holds a parsed and valid "x-only" public key. + * An x-only pubkey encodes a point whose Y coordinate is even. It is + * serialized using only its X coordinate (32 bytes). See BIP-340 for more + * information about x-only pubkeys. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, use + * use secp256k1_xonly_pubkey_serialize and secp256k1_xonly_pubkey_parse. To + * compare keys, use secp256k1_xonly_pubkey_cmp. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_xonly_pubkey; + +/** Opaque data structure that holds a keypair consisting of a secret and a + * public key. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 96 bytes in size, and can be safely copied/moved. + */ +typedef struct { + unsigned char data[96]; +} secp256k1_keypair; + +/** Parse a 32-byte sequence into a xonly_pubkey object. + * + * Returns: 1 if the public key was fully valid. + * 0 if the public key could not be parsed or is invalid. + * + * Args: ctx: pointer to a context object. + * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a + * parsed version of input. If not, it's set to an invalid value. + * In: input32: pointer to a serialized xonly_pubkey. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse( + const secp256k1_context *ctx, + secp256k1_xonly_pubkey *pubkey, + const unsigned char *input32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an xonly_pubkey object into a 32-byte sequence. + * + * Returns: 1 always. + * + * Args: ctx: pointer to a context object. + * Out: output32: pointer to a 32-byte array to place the serialized key in. + * In: pubkey: pointer to a secp256k1_xonly_pubkey containing an initialized public key. + */ +SECP256K1_API int secp256k1_xonly_pubkey_serialize( + const secp256k1_context *ctx, + unsigned char *output32, + const secp256k1_xonly_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compare two x-only public keys using lexicographic order + * + * Returns: <0 if the first public key is less than the second + * >0 if the first public key is greater than the second + * 0 if the two public keys are equal + * Args: ctx: pointer to a context object. + * In: pubkey1: first public key to compare + * pubkey2: second public key to compare + */ +SECP256K1_API int secp256k1_xonly_pubkey_cmp( + const secp256k1_context *ctx, + const secp256k1_xonly_pubkey *pk1, + const secp256k1_xonly_pubkey *pk2 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey. + * + * Returns: 1 always. + * + * Args: ctx: pointer to a context object. + * Out: xonly_pubkey: pointer to an x-only public key object for placing the converted public key. + * pk_parity: Ignored if NULL. Otherwise, pointer to an integer that + * will be set to 1 if the point encoded by xonly_pubkey is + * the negation of the pubkey and set to 0 otherwise. + * In: pubkey: pointer to a public key that is converted. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey( + const secp256k1_context *ctx, + secp256k1_xonly_pubkey *xonly_pubkey, + int *pk_parity, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Tweak an x-only public key by adding the generator multiplied with tweak32 + * to it. + * + * Note that the resulting point can not in general be represented by an x-only + * pubkey because it may have an odd Y coordinate. Instead, the output_pubkey + * is a normal secp256k1_pubkey. + * + * Returns: 0 if the arguments are invalid or the resulting public key would be + * invalid (only when the tweak is the negation of the corresponding + * secret key). 1 otherwise. + * + * Args: ctx: pointer to a context object. + * Out: output_pubkey: pointer to a public key to store the result. Will be set + * to an invalid value if this function returns 0. + * In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to. + * tweak32: pointer to a 32-byte tweak, which must be valid + * according to secp256k1_ec_seckey_verify or 32 zero + * bytes. For uniformly random 32-byte tweaks, the chance of + * being invalid is negligible (around 1 in 2^128). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add( + const secp256k1_context *ctx, + secp256k1_pubkey *output_pubkey, + const secp256k1_xonly_pubkey *internal_pubkey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Checks that a tweaked pubkey is the result of calling + * secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32. + * + * The tweaked pubkey is represented by its 32-byte x-only serialization and + * its pk_parity, which can both be obtained by converting the result of + * tweak_add to a secp256k1_xonly_pubkey. + * + * Note that this alone does _not_ verify that the tweaked pubkey is a + * commitment. If the tweak is not chosen in a specific way, the tweaked pubkey + * can easily be the result of a different internal_pubkey and tweak. + * + * Returns: 0 if the arguments are invalid or the tweaked pubkey is not the + * result of tweaking the internal_pubkey with tweak32. 1 otherwise. + * Args: ctx: pointer to a context object. + * In: tweaked_pubkey32: pointer to a serialized xonly_pubkey. + * tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization + * is passed in as tweaked_pubkey32). This must match the + * pk_parity value that is returned when calling + * secp256k1_xonly_pubkey with the tweaked pubkey, or + * this function will fail. + * internal_pubkey: pointer to an x-only public key object to apply the tweak to. + * tweak32: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_check( + const secp256k1_context *ctx, + const unsigned char *tweaked_pubkey32, + int tweaked_pk_parity, + const secp256k1_xonly_pubkey *internal_pubkey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Compute the keypair for a secret key. + * + * Returns: 1: secret was valid, keypair is ready to use + * 0: secret was invalid, try again with a different secret + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * Out: keypair: pointer to the created keypair. + * In: seckey: pointer to a 32-byte secret key. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create( + const secp256k1_context *ctx, + secp256k1_keypair *keypair, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Get the secret key from a keypair. + * + * Returns: 1 always. + * Args: ctx: pointer to a context object. + * Out: seckey: pointer to a 32-byte buffer for the secret key. + * In: keypair: pointer to a keypair. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec( + const secp256k1_context *ctx, + unsigned char *seckey, + const secp256k1_keypair *keypair +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Get the public key from a keypair. + * + * Returns: 1 always. + * Args: ctx: pointer to a context object. + * Out: pubkey: pointer to a pubkey object, set to the keypair public key. + * In: keypair: pointer to a keypair. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub( + const secp256k1_context *ctx, + secp256k1_pubkey *pubkey, + const secp256k1_keypair *keypair +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Get the x-only public key from a keypair. + * + * This is the same as calling secp256k1_keypair_pub and then + * secp256k1_xonly_pubkey_from_pubkey. + * + * Returns: 1 always. + * Args: ctx: pointer to a context object. + * Out: pubkey: pointer to an xonly_pubkey object, set to the keypair + * public key after converting it to an xonly_pubkey. + * pk_parity: Ignored if NULL. Otherwise, pointer to an integer that will be set to the + * pk_parity argument of secp256k1_xonly_pubkey_from_pubkey. + * In: keypair: pointer to a keypair. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub( + const secp256k1_context *ctx, + secp256k1_xonly_pubkey *pubkey, + int *pk_parity, + const secp256k1_keypair *keypair +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Tweak a keypair by adding tweak32 to the secret key and updating the public + * key accordingly. + * + * Calling this function and then secp256k1_keypair_pub results in the same + * public key as calling secp256k1_keypair_xonly_pub and then + * secp256k1_xonly_pubkey_tweak_add. + * + * Returns: 0 if the arguments are invalid or the resulting keypair would be + * invalid (only when the tweak is the negation of the keypair's + * secret key). 1 otherwise. + * + * Args: ctx: pointer to a context object. + * In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to + * an invalid value if this function returns 0. + * In: tweak32: pointer to a 32-byte tweak, which must be valid according to + * secp256k1_ec_seckey_verify or 32 zero bytes. For uniformly + * random 32-byte tweaks, the chance of being invalid is + * negligible (around 1 in 2^128). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add( + const secp256k1_context *ctx, + secp256k1_keypair *keypair, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_EXTRAKEYS_H */ diff --git a/src/secp256k1/include/secp256k1_preallocated.h b/src/secp256k1/include/secp256k1_preallocated.h new file mode 100644 index 000000000..f2d95c245 --- /dev/null +++ b/src/secp256k1/include/secp256k1_preallocated.h @@ -0,0 +1,134 @@ +#ifndef SECP256K1_PREALLOCATED_H +#define SECP256K1_PREALLOCATED_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* The module provided by this header file is intended for settings in which it + * is not possible or desirable to rely on dynamic memory allocation. It provides + * functions for creating, cloning, and destroying secp256k1 context objects in a + * contiguous fixed-size block of memory provided by the caller. + * + * Context objects created by functions in this module can be used like contexts + * objects created by functions in secp256k1.h, i.e., they can be passed to any + * API function that expects a context object (see secp256k1.h for details). The + * only exception is that context objects created by functions in this module + * must be destroyed using secp256k1_context_preallocated_destroy (in this + * module) instead of secp256k1_context_destroy (in secp256k1.h). + * + * It is guaranteed that functions in this module will not call malloc or its + * friends realloc, calloc, and free. + */ + +/** Determine the memory size of a secp256k1 context object to be created in + * caller-provided memory. + * + * The purpose of this function is to determine how much memory must be provided + * to secp256k1_context_preallocated_create. + * + * Returns: the required size of the caller-provided memory block + * In: flags: which parts of the context to initialize. + */ +SECP256K1_API size_t secp256k1_context_preallocated_size( + unsigned int flags +) SECP256K1_WARN_UNUSED_RESULT; + +/** Create a secp256k1 context object in caller-provided memory. + * + * The caller must provide a pointer to a rewritable contiguous block of memory + * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably + * aligned to hold an object of any type. + * + * The block of memory is exclusively owned by the created context object during + * the lifetime of this context object, which begins with the call to this + * function and ends when a call to secp256k1_context_preallocated_destroy + * (which destroys the context object again) returns. During the lifetime of the + * context object, the caller is obligated not to access this block of memory, + * i.e., the caller may not read or write the memory, e.g., by copying the memory + * contents to a different location or trying to create a second context object + * in the memory. In simpler words, the prealloc pointer (or any pointer derived + * from it) should not be used during the lifetime of the context object. + * + * Returns: pointer to newly created context object. + * In: prealloc: pointer to a rewritable contiguous block of memory of + * size at least secp256k1_context_preallocated_size(flags) + * bytes, as detailed above. + * flags: which parts of the context to initialize. + * + * See secp256k1_context_create (in secp256k1.h) for further details. + * + * See also secp256k1_context_randomize (in secp256k1.h) + * and secp256k1_context_preallocated_destroy. + */ +SECP256K1_API secp256k1_context *secp256k1_context_preallocated_create( + void *prealloc, + unsigned int flags +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Determine the memory size of a secp256k1 context object to be copied into + * caller-provided memory. + * + * Returns: the required size of the caller-provided memory block. + * In: ctx: pointer to a context to copy. + */ +SECP256K1_API size_t secp256k1_context_preallocated_clone_size( + const secp256k1_context *ctx +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Copy a secp256k1 context object into caller-provided memory. + * + * The caller must provide a pointer to a rewritable contiguous block of memory + * of size at least secp256k1_context_preallocated_size(flags) bytes, suitably + * aligned to hold an object of any type. + * + * The block of memory is exclusively owned by the created context object during + * the lifetime of this context object, see the description of + * secp256k1_context_preallocated_create for details. + * + * Cloning secp256k1_context_static is not possible, and should not be emulated by + * the caller (e.g., using memcpy). Create a new context instead. + * + * Returns: pointer to a newly created context object. + * Args: ctx: pointer to a context to copy (not secp256k1_context_static). + * In: prealloc: pointer to a rewritable contiguous block of memory of + * size at least secp256k1_context_preallocated_size(flags) + * bytes, as detailed above. + */ +SECP256K1_API secp256k1_context *secp256k1_context_preallocated_clone( + const secp256k1_context *ctx, + void *prealloc +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_WARN_UNUSED_RESULT; + +/** Destroy a secp256k1 context object that has been created in + * caller-provided memory. + * + * The context pointer may not be used afterwards. + * + * The context to destroy must have been created using + * secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone. + * If the context has instead been created using secp256k1_context_create or + * secp256k1_context_clone, the behaviour is undefined. In that case, + * secp256k1_context_destroy must be used instead. + * + * If required, it is the responsibility of the caller to deallocate the block + * of memory properly after this function returns, e.g., by calling free on the + * preallocated pointer given to secp256k1_context_preallocated_create or + * secp256k1_context_preallocated_clone. + * + * Args: ctx: pointer to a context to destroy, constructed using + * secp256k1_context_preallocated_create or + * secp256k1_context_preallocated_clone + * (i.e., not secp256k1_context_static). + */ +SECP256K1_API void secp256k1_context_preallocated_destroy( + secp256k1_context *ctx +) SECP256K1_ARG_NONNULL(1); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_PREALLOCATED_H */ diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h index cf6c5ed7f..341b8bac6 100644 --- a/src/secp256k1/include/secp256k1_recovery.h +++ b/src/secp256k1/include/secp256k1_recovery.h @@ -28,14 +28,14 @@ typedef struct { /** Parse a compact ECDSA signature (64 bytes + recovery id). * * Returns: 1 when the signature could be parsed, 0 otherwise - * Args: ctx: a secp256k1 context object - * Out: sig: a pointer to a signature object - * In: input64: a pointer to a 64-byte compact signature + * Args: ctx: pointer to a context object + * Out: sig: pointer to a signature object + * In: input64: pointer to a 64-byte compact signature * recid: the recovery id (0, 1, 2 or 3) */ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact( - const secp256k1_context* ctx, - secp256k1_ecdsa_recoverable_signature* sig, + const secp256k1_context *ctx, + secp256k1_ecdsa_recoverable_signature *sig, const unsigned char *input64, int recid ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); @@ -43,45 +43,48 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact( /** Convert a recoverable signature into a normal signature. * * Returns: 1 - * Out: sig: a pointer to a normal signature (cannot be NULL). - * In: sigin: a pointer to a recoverable signature (cannot be NULL). + * Args: ctx: pointer to a context object. + * Out: sig: pointer to a normal signature. + * In: sigin: pointer to a recoverable signature. */ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert( - const secp256k1_context* ctx, - secp256k1_ecdsa_signature* sig, - const secp256k1_ecdsa_recoverable_signature* sigin + const secp256k1_context *ctx, + secp256k1_ecdsa_signature *sig, + const secp256k1_ecdsa_recoverable_signature *sigin ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Serialize an ECDSA signature in compact format (64 bytes + recovery id). * * Returns: 1 - * Args: ctx: a secp256k1 context object - * Out: output64: a pointer to a 64-byte array of the compact signature (cannot be NULL) - * recid: a pointer to an integer to hold the recovery id (can be NULL). - * In: sig: a pointer to an initialized signature object (cannot be NULL) + * Args: ctx: pointer to a context object. + * Out: output64: pointer to a 64-byte array of the compact signature. + * recid: pointer to an integer to hold the recovery id. + * In: sig: pointer to an initialized signature object. */ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact( - const secp256k1_context* ctx, + const secp256k1_context *ctx, unsigned char *output64, int *recid, - const secp256k1_ecdsa_recoverable_signature* sig + const secp256k1_ecdsa_recoverable_signature *sig ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Create a recoverable ECDSA signature. * * Returns: 1: signature created - * 0: the nonce generation function failed, or the private key was invalid. - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) - * In: msg32: the 32-byte message hash being signed (cannot be NULL) - * seckey: pointer to a 32-byte secret key (cannot be NULL) - * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used - * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + * 0: the nonce generation function failed, or the secret key was invalid. + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * Out: sig: pointer to an array where the signature will be placed. + * In: msghash32: the 32-byte message hash being signed. + * seckey: pointer to a 32-byte secret key. + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_default is used. + * ndata: pointer to arbitrary data used by the nonce generation function + * (can be NULL for secp256k1_nonce_function_default). */ SECP256K1_API int secp256k1_ecdsa_sign_recoverable( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_ecdsa_recoverable_signature *sig, - const unsigned char *msg32, + const unsigned char *msghash32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void *ndata @@ -91,16 +94,16 @@ SECP256K1_API int secp256k1_ecdsa_sign_recoverable( * * Returns: 1: public key successfully recovered (which guarantees a correct signature). * 0: otherwise. - * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL) - * Out: pubkey: pointer to the recovered public key (cannot be NULL) - * In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL) - * msg32: the 32-byte message hash assumed to be signed (cannot be NULL) + * Args: ctx: pointer to a context object. + * Out: pubkey: pointer to the recovered public key. + * In: sig: pointer to initialized signature that supports pubkey recovery. + * msghash32: the 32-byte message hash assumed to be signed. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( - const secp256k1_context* ctx, + const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *sig, - const unsigned char *msg32 + const unsigned char *msghash32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); #ifdef __cplusplus diff --git a/src/secp256k1/include/secp256k1_schnorrsig.h b/src/secp256k1/include/secp256k1_schnorrsig.h new file mode 100644 index 000000000..23163de2f --- /dev/null +++ b/src/secp256k1/include/secp256k1_schnorrsig.h @@ -0,0 +1,190 @@ +#ifndef SECP256K1_SCHNORRSIG_H +#define SECP256K1_SCHNORRSIG_H + +#include "secp256k1.h" +#include "secp256k1_extrakeys.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This module implements a variant of Schnorr signatures compliant with + * Bitcoin Improvement Proposal 340 "Schnorr Signatures for secp256k1" + * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). + */ + +/** A pointer to a function to deterministically generate a nonce. + * + * Same as secp256k1_nonce function with the exception of accepting an + * additional pubkey argument and not requiring an attempt argument. The pubkey + * argument can protect signature schemes with key-prefixed challenge hash + * inputs against reusing the nonce when signing with the wrong precomputed + * pubkey. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to + * return an error. + * Out: nonce32: pointer to a 32-byte array to be filled by the function + * In: msg: the message being verified. Is NULL if and only if msglen + * is 0. + * msglen: the length of the message + * key32: pointer to a 32-byte secret key (will not be NULL) + * xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32 + * (will not be NULL) + * algo: pointer to an array describing the signature + * algorithm (will not be NULL) + * algolen: the length of the algo array + * data: arbitrary data pointer that is passed through + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the key, the pubkey, the algorithm description, and data. + */ +typedef int (*secp256k1_nonce_function_hardened)( + unsigned char *nonce32, + const unsigned char *msg, + size_t msglen, + const unsigned char *key32, + const unsigned char *xonly_pk32, + const unsigned char *algo, + size_t algolen, + void *data +); + +/** An implementation of the nonce generation function as defined in Bitcoin + * Improvement Proposal 340 "Schnorr Signatures for secp256k1" + * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). + * + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * auxiliary random data as defined in BIP-340. If the data pointer is NULL, + * the nonce derivation procedure follows BIP-340 by setting the auxiliary + * random data to zero. The algo argument must be non-NULL, otherwise the + * function will fail and return 0. The hash will be tagged with algo. + * Therefore, to create BIP-340 compliant signatures, algo must be set to + * "BIP0340/nonce" and algolen to 13. + */ +SECP256K1_API const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340; + +/** Data structure that contains additional arguments for schnorrsig_sign_custom. + * + * A schnorrsig_extraparams structure object can be initialized correctly by + * setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT. + * + * Members: + * magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization + * and has no other function than making sure the object is + * initialized. + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_bip340 is used + * ndata: pointer to arbitrary data used by the nonce generation function + * (can be NULL). If it is non-NULL and + * secp256k1_nonce_function_bip340 is used, then ndata must be a + * pointer to 32-byte auxiliary randomness as per BIP-340. + */ +typedef struct { + unsigned char magic[4]; + secp256k1_nonce_function_hardened noncefp; + void *ndata; +} secp256k1_schnorrsig_extraparams; + +#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC { 0xda, 0x6f, 0xb3, 0x8c } +#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\ + SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\ + NULL,\ + NULL\ +} + +/** Create a Schnorr signature. + * + * Does _not_ strictly follow BIP-340 because it does not verify the resulting + * signature. Instead, you can manually use secp256k1_schnorrsig_verify and + * abort if it fails. + * + * This function only signs 32-byte messages. If you have messages of a + * different size (or the same size but without a context-specific tag + * prefix), it is recommended to create a 32-byte message hash with + * secp256k1_tagged_sha256 and then sign the hash. Tagged hashing allows + * providing an context-specific tag for domain separation. This prevents + * signatures from being valid in multiple contexts by accident. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * Out: sig64: pointer to a 64-byte array to store the serialized signature. + * In: msg32: the 32-byte message being signed. + * keypair: pointer to an initialized keypair. + * aux_rand32: 32 bytes of fresh randomness. While recommended to provide + * this, it is only supplemental to security and can be NULL. A + * NULL argument is treated the same as an all-zero one. See + * BIP-340 "Default Signing" for a full explanation of this + * argument and for guidance if randomness is expensive. + */ +SECP256K1_API int secp256k1_schnorrsig_sign32( + const secp256k1_context *ctx, + unsigned char *sig64, + const unsigned char *msg32, + const secp256k1_keypair *keypair, + const unsigned char *aux_rand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Same as secp256k1_schnorrsig_sign32, but DEPRECATED. Will be removed in + * future versions. */ +SECP256K1_API int secp256k1_schnorrsig_sign( + const secp256k1_context *ctx, + unsigned char *sig64, + const unsigned char *msg32, + const secp256k1_keypair *keypair, + const unsigned char *aux_rand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) + SECP256K1_DEPRECATED("Use secp256k1_schnorrsig_sign32 instead"); + +/** Create a Schnorr signature with a more flexible API. + * + * Same arguments as secp256k1_schnorrsig_sign except that it allows signing + * variable length messages and accepts a pointer to an extraparams object that + * allows customizing signing by passing additional arguments. + * + * Equivalent to secp256k1_schnorrsig_sign32(..., aux_rand32) if msglen is 32 + * and extraparams is initialized as follows: + * ``` + * secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + * extraparams.ndata = (unsigned char*)aux_rand32; + * ``` + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object (not secp256k1_context_static). + * Out: sig64: pointer to a 64-byte array to store the serialized signature. + * In: msg: the message being signed. Can only be NULL if msglen is 0. + * msglen: length of the message. + * keypair: pointer to an initialized keypair. + * extraparams: pointer to an extraparams object (can be NULL). + */ +SECP256K1_API int secp256k1_schnorrsig_sign_custom( + const secp256k1_context *ctx, + unsigned char *sig64, + const unsigned char *msg, + size_t msglen, + const secp256k1_keypair *keypair, + secp256k1_schnorrsig_extraparams *extraparams +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5); + +/** Verify a Schnorr signature. + * + * Returns: 1: correct signature + * 0: incorrect signature + * Args: ctx: pointer to a context object. + * In: sig64: pointer to the 64-byte signature to verify. + * msg: the message being verified. Can only be NULL if msglen is 0. + * msglen: length of the message + * pubkey: pointer to an x-only public key to verify with + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( + const secp256k1_context *ctx, + const unsigned char *sig64, + const unsigned char *msg, + size_t msglen, + const secp256k1_xonly_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SCHNORRSIG_H */ diff --git a/src/secp256k1/libsecp256k1.pc.in b/src/secp256k1/libsecp256k1.pc.in index a0d006f11..0fb6f48a6 100644 --- a/src/secp256k1/libsecp256k1.pc.in +++ b/src/secp256k1/libsecp256k1.pc.in @@ -8,6 +8,5 @@ Description: Optimized C library for EC operations on curve secp256k1 URL: https://github.com/bitcoin-core/secp256k1 Version: @PACKAGE_VERSION@ Cflags: -I${includedir} -Libs.private: @SECP_LIBS@ Libs: -L${libdir} -lsecp256k1 diff --git a/src/secp256k1/obj/.gitignore b/src/secp256k1/obj/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/secp256k1/sage/gen_exhaustive_groups.sage b/src/secp256k1/sage/gen_exhaustive_groups.sage new file mode 100644 index 000000000..070bc1285 --- /dev/null +++ b/src/secp256k1/sage/gen_exhaustive_groups.sage @@ -0,0 +1,156 @@ +load("secp256k1_params.sage") + +MAX_ORDER = 1000 + +# Set of (curve) orders we have encountered so far. +orders_done = set() + +# Map from (subgroup) orders to [b, int(gen.x), int(gen.y), gen, lambda] for those subgroups. +solutions = {} + +# Iterate over curves of the form y^2 = x^3 + B. +for b in range(1, P): + # There are only 6 curves (up to isomorphism) of the form y^2 = x^3 + B. Stop once we have tried all. + if len(orders_done) == 6: + break + + E = EllipticCurve(F, [0, b]) + print("Analyzing curve y^2 = x^3 + %i" % b) + n = E.order() + + # Skip curves with an order we've already tried + if n in orders_done: + print("- Isomorphic to earlier curve") + print() + continue + orders_done.add(n) + + # Skip curves isomorphic to the real secp256k1 + if n.is_pseudoprime(): + assert E.is_isomorphic(C) + print("- Isomorphic to secp256k1") + print() + continue + + print("- Finding prime subgroups") + + # Map from group_order to a set of independent generators for that order. + curve_gens = {} + + for g in E.gens(): + # Find what prime subgroups of group generated by g exist. + g_order = g.order() + for f, _ in g.order().factor(): + # Skip subgroups that have bad size. + if f < 4: + print(f" - Subgroup of size {f}: too small") + continue + if f > MAX_ORDER: + print(f" - Subgroup of size {f}: too large") + continue + + # Construct a generator for that subgroup. + gen = g * (g_order // f) + assert(gen.order() == f) + + # Add to set the minimal multiple of gen. + curve_gens.setdefault(f, set()).add(min([j*gen for j in range(1, f)])) + print(f" - Subgroup of size {f}: ok") + + for f in sorted(curve_gens.keys()): + print(f"- Constructing group of order {f}") + cbrts = sorted([int(c) for c in Integers(f)(1).nth_root(3, all=true) if c != 1]) + gens = list(curve_gens[f]) + sol_count = 0 + no_endo_count = 0 + + # Consider all non-zero linear combinations of the independent generators. + for j in range(1, f**len(gens)): + gen = sum(gens[k] * ((j // f**k) % f) for k in range(len(gens))) + assert not gen.is_zero() + assert (f*gen).is_zero() + + # Find lambda for endomorphism. Skip if none can be found. + lam = None + for l in cbrts: + if l*gen == E(BETA*gen[0], gen[1]): + lam = l + break + + if lam is None: + no_endo_count += 1 + else: + sol_count += 1 + solutions.setdefault(f, []).append((b, int(gen[0]), int(gen[1]), gen, lam)) + + print(f" - Found {sol_count} generators (plus {no_endo_count} without endomorphism)") + + print() + +def output_generator(g, name): + print(f"#define {name} SECP256K1_GE_CONST(\\") + print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) + print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[0]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) + print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x,\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4))) + print(" 0x%08x, 0x%08x, 0x%08x, 0x%08x\\" % tuple((int(g[1]) >> (32 * (7 - i))) & 0xffffffff for i in range(4, 8))) + print(")") + +def output_b(b): + print(f"#define SECP256K1_B {int(b)}") + +print() +print("To be put in src/group_impl.h:") +print() +print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */") +for f in sorted(solutions.keys()): + # Use as generator/2 the one with lowest b, and lowest (x, y) generator (interpreted as non-negative integers). + b, _, _, HALF_G, lam = min(solutions[f]) + output_generator(2 * HALF_G, f"SECP256K1_G_ORDER_{f}") +print("/** Generator for secp256k1, value 'g' defined in") +print(" * \"Standards for Efficient Cryptography\" (SEC2) 2.7.1.") +print(" */") +output_generator(G, "SECP256K1_G") +print("/* These exhaustive group test orders and generators are chosen such that:") +print(" * - The field size is equal to that of secp256k1, so field code is the same.") +print(" * - The curve equation is of the form y^2=x^3+B for some small constant B.") +print(" * - The subgroup has a generator 2*P, where P.x is as small as possible.") +print(f" * - The subgroup has size less than {MAX_ORDER} to permit exhaustive testing.") +print(" * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y).") +print(" */") +print("#if defined(EXHAUSTIVE_TEST_ORDER)") +first = True +for f in sorted(solutions.keys()): + b, _, _, _, lam = min(solutions[f]) + print(f"# {'if' if first else 'elif'} EXHAUSTIVE_TEST_ORDER == {f}") + first = False + print() + print(f"static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_{f};") + output_b(b) + print() +print("# else") +print("# error No known generator for the specified exhaustive test group order.") +print("# endif") +print("#else") +print() +print("static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G;") +output_b(7) +print() +print("#endif") +print("/* End of section generated by sage/gen_exhaustive_groups.sage. */") + + +print() +print() +print("To be put in src/scalar_impl.h:") +print() +print("/* Begin of section generated by sage/gen_exhaustive_groups.sage. */") +first = True +for f in sorted(solutions.keys()): + _, _, _, _, lam = min(solutions[f]) + print("# %s EXHAUSTIVE_TEST_ORDER == %i" % ("if" if first else "elif", f)) + first = False + print("# define EXHAUSTIVE_TEST_LAMBDA %i" % lam) +print("# else") +print("# error No known lambda for the specified exhaustive test group order.") +print("# endif") +print("/* End of section generated by sage/gen_exhaustive_groups.sage. */") diff --git a/src/secp256k1/sage/gen_split_lambda_constants.sage b/src/secp256k1/sage/gen_split_lambda_constants.sage new file mode 100644 index 000000000..7d4359e0f --- /dev/null +++ b/src/secp256k1/sage/gen_split_lambda_constants.sage @@ -0,0 +1,114 @@ +""" Generates the constants used in secp256k1_scalar_split_lambda. + +See the comments for secp256k1_scalar_split_lambda in src/scalar_impl.h for detailed explanations. +""" + +load("secp256k1_params.sage") + +def inf_norm(v): + """Returns the infinity norm of a vector.""" + return max(map(abs, v)) + +def gauss_reduction(i1, i2): + v1, v2 = i1.copy(), i2.copy() + while True: + if inf_norm(v2) < inf_norm(v1): + v1, v2 = v2, v1 + # This is essentially + # m = round((v1[0]*v2[0] + v1[1]*v2[1]) / (inf_norm(v1)**2)) + # (rounding to the nearest integer) without relying on floating point arithmetic. + m = ((v1[0]*v2[0] + v1[1]*v2[1]) + (inf_norm(v1)**2) // 2) // (inf_norm(v1)**2) + if m == 0: + return v1, v2 + v2[0] -= m*v1[0] + v2[1] -= m*v1[1] + +def find_split_constants_gauss(): + """Find constants for secp256k1_scalar_split_lamdba using gauss reduction.""" + (v11, v12), (v21, v22) = gauss_reduction([0, N], [1, int(LAMBDA)]) + + # We use related vectors in secp256k1_scalar_split_lambda. + A1, B1 = -v21, -v11 + A2, B2 = v22, -v21 + + return A1, B1, A2, B2 + +def find_split_constants_explicit_tof(): + """Find constants for secp256k1_scalar_split_lamdba using the trace of Frobenius. + + See Benjamin Smith: "Easy scalar decompositions for efficient scalar multiplication on + elliptic curves and genus 2 Jacobians" (https://eprint.iacr.org/2013/672), Example 2 + """ + assert P % 3 == 1 # The paper says P % 3 == 2 but that appears to be a mistake, see [10]. + assert C.j_invariant() == 0 + + t = C.trace_of_frobenius() + + c = Integer(sqrt((4*P - t**2)/3)) + A1 = Integer((t - c)/2 - 1) + B1 = c + + A2 = Integer((t + c)/2 - 1) + B2 = Integer(1 - (t - c)/2) + + # We use a negated b values in secp256k1_scalar_split_lambda. + B1, B2 = -B1, -B2 + + return A1, B1, A2, B2 + +A1, B1, A2, B2 = find_split_constants_explicit_tof() + +# For extra fun, use an independent method to recompute the constants. +assert (A1, B1, A2, B2) == find_split_constants_gauss() + +# PHI : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n. +def PHI(a,b): + return Z(a + LAMBDA*b) + +# Check that (A1, B1) and (A2, B2) are in the kernel of PHI. +assert PHI(A1, B1) == Z(0) +assert PHI(A2, B2) == Z(0) + +# Check that the parallelogram generated by (A1, A2) and (B1, B2) +# is a fundamental domain by containing exactly N points. +# Since the LHS is the determinant and N != 0, this also checks that +# (A1, A2) and (B1, B2) are linearly independent. By the previous +# assertions, (A1, A2) and (B1, B2) are a basis of the kernel. +assert A1*B2 - B1*A2 == N + +# Check that their components are short enough. +assert (A1 + A2)/2 < sqrt(N) +assert B1 < sqrt(N) +assert B2 < sqrt(N) + +G1 = round((2**384)*B2/N) +G2 = round((2**384)*(-B1)/N) + +def rnddiv2(v): + if v & 1: + v += 1 + return v >> 1 + +def scalar_lambda_split(k): + """Equivalent to secp256k1_scalar_lambda_split().""" + c1 = rnddiv2((k * G1) >> 383) + c2 = rnddiv2((k * G2) >> 383) + c1 = (c1 * -B1) % N + c2 = (c2 * -B2) % N + r2 = (c1 + c2) % N + r1 = (k + r2 * -LAMBDA) % N + return (r1, r2) + +# The result of scalar_lambda_split can depend on the representation of k (mod n). +SPECIAL = (2**383) // G2 + 1 +assert scalar_lambda_split(SPECIAL) != scalar_lambda_split(SPECIAL + N) + +print(' A1 =', hex(A1)) +print(' -B1 =', hex(-B1)) +print(' A2 =', hex(A2)) +print(' -B2 =', hex(-B2)) +print(' =', hex(Z(-B2))) +print(' -LAMBDA =', hex(-LAMBDA)) + +print(' G1 =', hex(G1)) +print(' G2 =', hex(G2)) diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage index 8521f0799..bb0929536 100644 --- a/src/secp256k1/sage/group_prover.sage +++ b/src/secp256k1/sage/group_prover.sage @@ -42,7 +42,7 @@ # as we assume that all constraints in it are complementary with each other. # # Based on the sage verification scripts used in the Explicit-Formulas Database -# by Tanja Lange and others, see http://hyperelliptic.org/EFD +# by Tanja Lange and others, see https://hyperelliptic.org/EFD class fastfrac: """Fractions over rings.""" @@ -65,7 +65,7 @@ class fastfrac: return self.top in I and self.bot not in I def reduce(self,assumeZero): - zero = self.R.ideal(map(numerator, assumeZero)) + zero = self.R.ideal(list(map(numerator, assumeZero))) return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot)) def __add__(self,other): @@ -100,7 +100,7 @@ class fastfrac: """Multiply something else with a fraction.""" return self.__mul__(other) - def __div__(self,other): + def __truediv__(self,other): """Divide two fractions.""" if parent(other) == ZZ: return fastfrac(self.R,self.top,self.bot * other) @@ -108,6 +108,11 @@ class fastfrac: return fastfrac(self.R,self.top * other.bot,self.bot * other.top) return NotImplemented + # Compatibility wrapper for Sage versions based on Python 2 + def __div__(self,other): + """Divide two fractions.""" + return self.__truediv__(other) + def __pow__(self,other): """Compute a power of a fraction.""" if parent(other) == ZZ: @@ -159,6 +164,9 @@ class constraints: def negate(self): return constraints(zero=self.nonzero, nonzero=self.zero) + def map(self, fun): + return constraints(zero={fun(k): v for k, v in self.zero.items()}, nonzero={fun(k): v for k, v in self.nonzero.items()}) + def __add__(self, other): zero = self.zero.copy() zero.update(other.zero) @@ -172,10 +180,34 @@ class constraints: def __repr__(self): return "%s" % self +def normalize_factor(p): + """Normalizes the sign of primitive polynomials (as returned by factor()) + + This function ensures that the polynomial has a positive leading coefficient. + + This is necessary because recent sage versions (starting with v9.3 or v9.4, + we don't know) are inconsistent about the placement of the minus sign in + polynomial factorizations: + ``` + sage: R. = PolynomialRing(QQ,8,order='invlex') + sage: R((-2 * (bx - ax)) ^ 1).factor() + (-2) * (bx - ax) + sage: R((-2 * (bx - ax)) ^ 2).factor() + (4) * (-bx + ax)^2 + sage: R((-2 * (bx - ax)) ^ 3).factor() + (8) * (-bx + ax)^3 + ``` + """ + # Assert p is not 0 and that its non-zero coefficients are coprime. + # (We could just work with the primitive part p/p.content() but we want to be + # aware if factor() does not return a primitive part in future sage versions.) + assert p.content() == 1 + # Ensure that the first non-zero coefficient is positive. + return p if p.lc() > 0 else -p def conflicts(R, con): """Check whether any of the passed non-zero assumptions is implied by the zero assumptions""" - zero = R.ideal(map(numerator, con.zero)) + zero = R.ideal(list(map(numerator, con.zero))) if 1 in zero: return True # First a cheap check whether any of the individual nonzero terms conflict on @@ -195,20 +227,20 @@ def conflicts(R, con): def get_nonzero_set(R, assume): """Calculate a simple set of nonzero expressions""" - zero = R.ideal(map(numerator, assume.zero)) + zero = R.ideal(list(map(numerator, assume.zero))) nonzero = set() for nz in map(numerator, assume.nonzero): for (f,n) in nz.factor(): - nonzero.add(f) + nonzero.add(normalize_factor(f)) rnz = zero.reduce(nz) for (f,n) in rnz.factor(): - nonzero.add(f) + nonzero.add(normalize_factor(f)) return nonzero def prove_nonzero(R, exprs, assume): """Check whether an expression is provably nonzero, given assumptions""" - zero = R.ideal(map(numerator, assume.zero)) + zero = R.ideal(list(map(numerator, assume.zero))) nonzero = get_nonzero_set(R, assume) expl = set() ok = True @@ -217,27 +249,27 @@ def prove_nonzero(R, exprs, assume): return (False, [exprs[expr]]) allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1) for (f, n) in allexprs.factor(): - if f not in nonzero: + if normalize_factor(f) not in nonzero: ok = False if ok: return (True, None) ok = True - for (f, n) in zero.reduce(numerator(allexprs)).factor(): - if f not in nonzero: + for (f, n) in zero.reduce(allexprs).factor(): + if normalize_factor(f) not in nonzero: ok = False if ok: return (True, None) ok = True for expr in exprs: for (f,n) in numerator(expr).factor(): - if f not in nonzero: + if normalize_factor(f) not in nonzero: ok = False if ok: return (True, None) ok = True for expr in exprs: for (f,n) in zero.reduce(numerator(expr)).factor(): - if f not in nonzero: + if normalize_factor(f) not in nonzero: expl.add(exprs[expr]) if expl: return (False, list(expl)) @@ -249,8 +281,8 @@ def prove_zero(R, exprs, assume): """Check whether all of the passed expressions are provably zero, given assumptions""" r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume) if not r: - return (False, map(lambda x: "Possibly zero denominator: %s" % x, e)) - zero = R.ideal(map(numerator, assume.zero)) + return (False, list(map(lambda x: "Possibly zero denominator: %s" % x, e))) + zero = R.ideal(list(map(numerator, assume.zero))) nonzero = prod(x for x in assume.nonzero) expl = [] for expr in exprs: @@ -265,8 +297,8 @@ def describe_extra(R, assume, assumeExtra): """Describe what assumptions are added, given existing assumptions""" zerox = assume.zero.copy() zerox.update(assumeExtra.zero) - zero = R.ideal(map(numerator, assume.zero)) - zeroextra = R.ideal(map(numerator, zerox)) + zero = R.ideal(list(map(numerator, assume.zero))) + zeroextra = R.ideal(list(map(numerator, zerox))) nonzero = get_nonzero_set(R, assume) ret = set() # Iterate over the extra zero expressions @@ -274,8 +306,8 @@ def describe_extra(R, assume, assumeExtra): if base not in zero: add = [] for (f, n) in numerator(base).factor(): - if f not in nonzero: - add += ["%s" % f] + if normalize_factor(f) not in nonzero: + add += ["%s" % normalize_factor(f)] if add: ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base]) # Iterate over the extra nonzero expressions @@ -283,8 +315,8 @@ def describe_extra(R, assume, assumeExtra): nzr = zeroextra.reduce(numerator(nz)) if nzr not in zeroextra: for (f,n) in nzr.factor(): - if zeroextra.reduce(f) not in nonzero: - ret.add("%s != 0" % zeroextra.reduce(f)) + if normalize_factor(zeroextra.reduce(f)) not in nonzero: + ret.add("%s != 0" % normalize_factor(zeroextra.reduce(f))) return ", ".join(x for x in ret) @@ -294,22 +326,21 @@ def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require): if conflicts(R, assume): # This formula does not apply - return None + return (True, None) describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert) + if describe != "": + describe = " (assuming " + describe + ")" ok, msg = prove_zero(R, require.zero, assume) if not ok: - return "FAIL, %s fails (assuming %s)" % (str(msg), describe) + return (False, "FAIL, %s fails%s" % (str(msg), describe)) res, expl = prove_nonzero(R, require.nonzero, assume) if not res: - return "FAIL, %s fails (assuming %s)" % (str(expl), describe) + return (False, "FAIL, %s fails%s" % (str(expl), describe)) - if describe != "": - return "OK (assuming %s)" % describe - else: - return "OK" + return (True, "OK%s" % describe) def concrete_verify(c): diff --git a/src/secp256k1/sage/secp256k1.sage b/src/secp256k1/sage/prove_group_implementations.sage similarity index 72% rename from src/secp256k1/sage/secp256k1.sage rename to src/secp256k1/sage/prove_group_implementations.sage index a97e732f7..23799be52 100644 --- a/src/secp256k1/sage/secp256k1.sage +++ b/src/secp256k1/sage/prove_group_implementations.sage @@ -8,25 +8,20 @@ load("weierstrass_prover.sage") def formula_secp256k1_gej_double_var(a): """libsecp256k1's secp256k1_gej_double_var, used by various addition functions""" rz = a.Z * a.Y - rz = rz * 2 - t1 = a.X^2 - t1 = t1 * 3 - t2 = t1^2 - t3 = a.Y^2 - t3 = t3 * 2 - t4 = t3^2 - t4 = t4 * 2 - t3 = t3 * a.X - rx = t3 - rx = rx * 4 - rx = -rx - rx = rx + t2 - t2 = -t2 - t3 = t3 * 6 - t3 = t3 + t2 - ry = t1 * t3 - t2 = -t4 - ry = ry + t2 + s = a.Y^2 + l = a.X^2 + l = l * 3 + l = l / 2 + t = -s + t = t * a.X + rx = l^2 + rx = rx + t + rx = rx + t + s = s^2 + t = t + rx + ry = t * l + ry = ry + s + ry = -ry return jacobianpoint(rx, ry, rz) def formula_secp256k1_gej_add_var(branch, a, b): @@ -45,29 +40,26 @@ def formula_secp256k1_gej_add_var(branch, a, b): s2 = s2 * a.Z h = -u1 h = h + u2 - i = -s1 - i = i + s2 + i = -s2 + i = i + s1 if branch == 2: r = formula_secp256k1_gej_double_var(a) return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r) if branch == 3: return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity()) - i2 = i^2 + t = h * b.Z + rz = a.Z * t h2 = h^2 + h2 = -h2 h3 = h2 * h - h = h * b.Z - rz = a.Z * h t = u1 * h2 - rx = t - rx = rx * 2 + rx = i^2 rx = rx + h3 - rx = -rx - rx = rx + i2 - ry = -rx - ry = ry + t - ry = ry * i + rx = rx + t + rx = rx + t + t = t + rx + ry = t * i h3 = h3 * s1 - h3 = -h3 ry = ry + h3 return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) @@ -85,28 +77,25 @@ def formula_secp256k1_gej_add_ge_var(branch, a, b): s2 = s2 * a.Z h = -u1 h = h + u2 - i = -s1 - i = i + s2 + i = -s2 + i = i + s1 if (branch == 2): r = formula_secp256k1_gej_double_var(a) return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) if (branch == 3): return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) - i2 = i^2 - h2 = h^2 - h3 = h * h2 rz = a.Z * h + h2 = h^2 + h2 = -h2 + h3 = h2 * h t = u1 * h2 - rx = t - rx = rx * 2 + rx = i^2 rx = rx + h3 - rx = -rx - rx = rx + i2 - ry = -rx - ry = ry + t - ry = ry * i + rx = rx + t + rx = rx + t + t = t + rx + ry = t * i h3 = h3 * s1 - h3 = -h3 ry = ry + h3 return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) @@ -114,14 +103,15 @@ def formula_secp256k1_gej_add_zinv_var(branch, a, b): """libsecp256k1's secp256k1_gej_add_zinv_var""" bzinv = b.Z^(-1) if branch == 0: - return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a) - if branch == 1: + rinf = b.Infinity bzinv2 = bzinv^2 bzinv3 = bzinv2 * bzinv rx = b.X * bzinv2 ry = b.Y * bzinv3 rz = 1 - return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz)) + return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz, rinf)) + if branch == 1: + return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) azz = a.Z * bzinv z12 = azz^2 u1 = a.X @@ -131,29 +121,25 @@ def formula_secp256k1_gej_add_zinv_var(branch, a, b): s2 = s2 * azz h = -u1 h = h + u2 - i = -s1 - i = i + s2 + i = -s2 + i = i + s1 if branch == 2: r = formula_secp256k1_gej_double_var(a) return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) if branch == 3: return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) - i2 = i^2 + rz = a.Z * h h2 = h^2 - h3 = h * h2 - rz = a.Z - rz = rz * h + h2 = -h2 + h3 = h2 * h t = u1 * h2 - rx = t - rx = rx * 2 + rx = i^2 rx = rx + h3 - rx = -rx - rx = rx + i2 - ry = -rx - ry = ry + t - ry = ry * i + rx = rx + t + rx = rx + t + t = t + rx + ry = t * i h3 = h3 * s1 - h3 = -h3 ry = ry + h3 return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) @@ -162,7 +148,7 @@ def formula_secp256k1_gej_add_ge(branch, a, b): zeroes = {} nonzeroes = {} a_infinity = False - if (branch & 4) != 0: + if (branch & 2) != 0: nonzeroes.update({a.Infinity : 'a_infinite'}) a_infinity = True else: @@ -181,15 +167,11 @@ def formula_secp256k1_gej_add_ge(branch, a, b): m_alt = -u2 tt = u1 * m_alt rr = rr + tt - degenerate = (branch & 3) == 3 - if (branch & 1) != 0: + degenerate = (branch & 1) != 0 + if degenerate: zeroes.update({m : 'm_zero'}) else: nonzeroes.update({m : 'm_nonzero'}) - if (branch & 2) != 0: - zeroes.update({rr : 'rr_zero'}) - else: - nonzeroes.update({rr : 'rr_nonzero'}) rr_alt = s1 rr_alt = rr_alt * 2 m_alt = m_alt + u1 @@ -197,21 +179,13 @@ def formula_secp256k1_gej_add_ge(branch, a, b): rr_alt = rr m_alt = m n = m_alt^2 - q = n * t + q = -t + q = q * n n = n^2 if degenerate: n = m t = rr_alt^2 rz = a.Z * m_alt - infinity = False - if (branch & 8) != 0: - if not a_infinity: - infinity = True - zeroes.update({rz : 'r.z=0'}) - else: - nonzeroes.update({rz : 'r.z!=0'}) - rz = rz * 2 - q = -q t = t + q rx = t t = t * 2 @@ -219,14 +193,16 @@ def formula_secp256k1_gej_add_ge(branch, a, b): t = t * rr_alt t = t + n ry = -t - rx = rx * 4 - ry = ry * 4 + ry = ry / 2 if a_infinity: rx = b.X ry = b.Y rz = 1 - if infinity: + if (branch & 4) != 0: + zeroes.update({rz : 'r.z = 0'}) return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity()) + else: + nonzeroes.update({rz : 'r.z != 0'}) return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz)) def formula_secp256k1_gej_add_ge_old(branch, a, b): @@ -292,15 +268,18 @@ def formula_secp256k1_gej_add_ge_old(branch, a, b): return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz)) if __name__ == "__main__": - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge) - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old) + success = True + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 8, formula_secp256k1_gej_add_ge) + success = success & (not check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)) if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive": - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43) - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 8, formula_secp256k1_gej_add_ge, 43) + success = success & (not check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)) + + sys.exit(int(not success)) diff --git a/src/secp256k1/sage/secp256k1_params.sage b/src/secp256k1/sage/secp256k1_params.sage new file mode 100644 index 000000000..68f95adec --- /dev/null +++ b/src/secp256k1/sage/secp256k1_params.sage @@ -0,0 +1,39 @@ +"""Prime order of finite field underlying secp256k1 (2^256 - 2^32 - 977)""" +P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F + +"""Finite field underlying secp256k1""" +F = FiniteField(P) + +"""Elliptic curve secp256k1: y^2 = x^3 + 7""" +C = EllipticCurve([F(0), F(7)]) + +"""Base point of secp256k1""" +G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798) +if int(G[1]) & 1: + # G.y is even + G = -G + +"""Prime order of secp256k1""" +N = C.order() + +"""Finite field of scalars of secp256k1""" +Z = FiniteField(N) + +""" Beta value of secp256k1 non-trivial endomorphism: lambda * (x, y) = (beta * x, y)""" +BETA = F(2)^((P-1)/3) + +""" Lambda value of secp256k1 non-trivial endomorphism: lambda * (x, y) = (beta * x, y)""" +LAMBDA = Z(3)^((N-1)/3) + +assert is_prime(P) +assert is_prime(N) + +assert BETA != F(1) +assert BETA^3 == F(1) +assert BETA^2 + BETA + 1 == 0 + +assert LAMBDA != Z(1) +assert LAMBDA^3 == Z(1) +assert LAMBDA^2 + LAMBDA + 1 == 0 + +assert Integer(LAMBDA)*G == C(BETA*G[0], G[1]) diff --git a/src/secp256k1/sage/weierstrass_prover.sage b/src/secp256k1/sage/weierstrass_prover.sage index 03ef2ec90..be9cfd4c7 100644 --- a/src/secp256k1/sage/weierstrass_prover.sage +++ b/src/secp256k1/sage/weierstrass_prover.sage @@ -175,24 +175,25 @@ laws_jacobian_weierstrass = { def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p): """Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field""" F = Integers(p) - print "Formula %s on Z%i:" % (name, p) + print("Formula %s on Z%i:" % (name, p)) points = [] - for x in xrange(0, p): - for y in xrange(0, p): + for x in range(0, p): + for y in range(0, p): point = affinepoint(F(x), F(y)) r, e = concrete_verify(on_weierstrass_curve(A, B, point)) if r: points.append(point) - for za in xrange(1, p): - for zb in xrange(1, p): + ret = True + for za in range(1, p): + for zb in range(1, p): for pa in points: for pb in points: - for ia in xrange(2): - for ib in xrange(2): + for ia in range(2): + for ib in range(2): pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia) pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib) - for branch in xrange(0, branches): + for branch in range(0, branches): assumeAssert, assumeBranch, pC = formula(branch, pA, pB) pC.X = F(pC.X) pC.Y = F(pC.Y) @@ -206,13 +207,16 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p): r, e = concrete_verify(assumeLaw) if r: if match: - print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity) + print(" multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity)) else: match = True r, e = concrete_verify(require) if not r: - print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e) - print + ret = False + print(" failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)) + + print() + return ret def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC): @@ -242,23 +246,30 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): for key in laws_jacobian_weierstrass: res[key] = [] - print ("Formula " + name + ":") + print("Formula " + name + ":") count = 0 - for branch in xrange(branches): + ret = True + for branch in range(branches): assumeFormula, assumeBranch, pC = formula(branch, pA, pB) + assumeBranch = assumeBranch.map(lift) + assumeFormula = assumeFormula.map(lift) pC.X = lift(pC.X) pC.Y = lift(pC.Y) pC.Z = lift(pC.Z) pC.Infinity = lift(pC.Infinity) for key in laws_jacobian_weierstrass: - res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch)) + success, msg = check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC) + if not success: + ret = False + res[key].append((msg, branch)) for key in res: - print " %s:" % key + print(" %s:" % key) val = res[key] for x in val: if x[0] is not None: - print " branch %i: %s" % (x[1], x[0]) + print(" branch %i: %s" % (x[1], x[0])) - print + print() + return ret diff --git a/src/secp256k1/src/CMakeLists.txt b/src/secp256k1/src/CMakeLists.txt new file mode 100644 index 000000000..4cbaeb914 --- /dev/null +++ b/src/secp256k1/src/CMakeLists.txt @@ -0,0 +1,173 @@ +# Must be included before CMAKE_INSTALL_INCLUDEDIR is used. +include(GNUInstallDirs) + +add_library(secp256k1_precomputed OBJECT EXCLUDE_FROM_ALL + precomputed_ecmult.c + precomputed_ecmult_gen.c +) + +# Add objects explicitly rather than linking to the object libs to keep them +# from being exported. +add_library(secp256k1 secp256k1.c $) + +add_library(secp256k1_asm INTERFACE) +if(SECP256K1_ASM STREQUAL "arm32") + add_library(secp256k1_asm_arm OBJECT EXCLUDE_FROM_ALL) + target_sources(secp256k1_asm_arm PUBLIC + asm/field_10x26_arm.s + ) + target_sources(secp256k1 PRIVATE $) + target_link_libraries(secp256k1_asm INTERFACE secp256k1_asm_arm) +endif() + +if(WIN32) + # Define our export symbol only for shared libs. + set_target_properties(secp256k1 PROPERTIES DEFINE_SYMBOL SECP256K1_DLL_EXPORT) + target_compile_definitions(secp256k1 INTERFACE $<$>:SECP256K1_STATIC>) +endif() + +# Object libs don't know if they're being built for a shared or static lib. +# Grab the PIC property from secp256k1 which knows. +get_target_property(use_pic secp256k1 POSITION_INDEPENDENT_CODE) +set_target_properties(secp256k1_precomputed PROPERTIES POSITION_INDEPENDENT_CODE ${use_pic}) + +target_include_directories(secp256k1 INTERFACE + # Add the include path for parent projects so that they don't have to manually add it. + $>:${PROJECT_SOURCE_DIR}/include>> + $ +) + +# This emulates Libtool to make sure Libtool and CMake agree on the ABI version, +# see below "Calculate the version variables" in build-aux/ltmain.sh. +math(EXPR ${PROJECT_NAME}_soversion "${${PROJECT_NAME}_LIB_VERSION_CURRENT} - ${${PROJECT_NAME}_LIB_VERSION_AGE}") +set_target_properties(secp256k1 PROPERTIES + SOVERSION ${${PROJECT_NAME}_soversion} +) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set_target_properties(secp256k1 PROPERTIES + VERSION ${${PROJECT_NAME}_soversion}.${${PROJECT_NAME}_LIB_VERSION_AGE}.${${PROJECT_NAME}_LIB_VERSION_REVISION} + ) +elseif(APPLE) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) + math(EXPR ${PROJECT_NAME}_compatibility_version "${${PROJECT_NAME}_LIB_VERSION_CURRENT} + 1") + set_target_properties(secp256k1 PROPERTIES + MACHO_COMPATIBILITY_VERSION ${${PROJECT_NAME}_compatibility_version} + MACHO_CURRENT_VERSION ${${PROJECT_NAME}_compatibility_version}.${${PROJECT_NAME}_LIB_VERSION_REVISION} + ) + unset(${PROJECT_NAME}_compatibility_version) + elseif(BUILD_SHARED_LIBS) + message(WARNING + "The 'compatibility version' and 'current version' values of the DYLIB " + "will diverge from the values set by the GNU Libtool. To ensure " + "compatibility, it is recommended to upgrade CMake to at least version 3.17." + ) + endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(${PROJECT_NAME}_windows "secp256k1") + if(MSVC) + set(${PROJECT_NAME}_windows "${PROJECT_NAME}") + endif() + set_target_properties(secp256k1 PROPERTIES + ARCHIVE_OUTPUT_NAME "${${PROJECT_NAME}_windows}" + RUNTIME_OUTPUT_NAME "${${PROJECT_NAME}_windows}-${${PROJECT_NAME}_soversion}" + ) + unset(${PROJECT_NAME}_windows) +endif() +unset(${PROJECT_NAME}_soversion) + +if(SECP256K1_BUILD_BENCHMARK) + add_executable(bench bench.c) + target_link_libraries(bench secp256k1) + add_executable(bench_internal bench_internal.c) + target_link_libraries(bench_internal secp256k1_precomputed secp256k1_asm) + add_executable(bench_ecmult bench_ecmult.c) + target_link_libraries(bench_ecmult secp256k1_precomputed secp256k1_asm) +endif() + +if(SECP256K1_BUILD_TESTS) + add_executable(noverify_tests tests.c) + target_link_libraries(noverify_tests secp256k1_precomputed secp256k1_asm) + add_test(NAME noverify_tests COMMAND noverify_tests) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Coverage") + add_executable(tests tests.c) + target_compile_definitions(tests PRIVATE VERIFY) + target_link_libraries(tests secp256k1_precomputed secp256k1_asm) + add_test(NAME tests COMMAND tests) + endif() +endif() + +if(SECP256K1_BUILD_EXHAUSTIVE_TESTS) + # Note: do not include secp256k1_precomputed in exhaustive_tests (it uses runtime-generated tables). + add_executable(exhaustive_tests tests_exhaustive.c) + target_link_libraries(exhaustive_tests secp256k1_asm) + target_compile_definitions(exhaustive_tests PRIVATE $<$>:VERIFY>) + add_test(NAME exhaustive_tests COMMAND exhaustive_tests) +endif() + +if(SECP256K1_BUILD_CTIME_TESTS) + add_executable(ctime_tests ctime_tests.c) + target_link_libraries(ctime_tests secp256k1) +endif() + +if(SECP256K1_INSTALL) + install(TARGETS secp256k1 + EXPORT ${PROJECT_NAME}-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + set(${PROJECT_NAME}_headers + "${PROJECT_SOURCE_DIR}/include/secp256k1.h" + "${PROJECT_SOURCE_DIR}/include/secp256k1_preallocated.h" + ) + if(SECP256K1_ENABLE_MODULE_ECDH) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ecdh.h") + endif() + if(SECP256K1_ENABLE_MODULE_RECOVERY) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_recovery.h") + endif() + if(SECP256K1_ENABLE_MODULE_EXTRAKEYS) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_extrakeys.h") + endif() + if(SECP256K1_ENABLE_MODULE_SCHNORRSIG) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_schnorrsig.h") + endif() + if(SECP256K1_ENABLE_MODULE_ELLSWIFT) + list(APPEND ${PROJECT_NAME}_headers "${PROJECT_SOURCE_DIR}/include/secp256k1_ellswift.h") + endif() + install(FILES ${${PROJECT_NAME}_headers} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) + + install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}-targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) + + include(CMakePackageConfigHelpers) + configure_package_config_file( + ${PROJECT_SOURCE_DIR}/cmake/config.cmake.in + ${PROJECT_NAME}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + NO_SET_AND_CHECK_MACRO + ) + write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake + COMPATIBILITY SameMinorVersion + ) + + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) + + include(GeneratePkgConfigFile) + generate_pkg_config_file(${PROJECT_SOURCE_DIR}/libsecp256k1.pc.in) + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + ) +endif() diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s index 04f5b07a1..664b92140 100644 --- a/src/secp256k1/src/asm/field_10x26_arm.s +++ b/src/secp256k1/src/asm/field_10x26_arm.s @@ -1,9 +1,9 @@ @ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm: -/************************************************************************ - * Copyright (c) 2014 Wladimir J. van der Laan * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Wladimir J. van der Laan * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ /* ARM implementation of field_10x26 inner loops. @@ -16,15 +16,9 @@ Note: */ .syntax unified - .arch armv7-a @ eabi attributes - see readelf -A - .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes - .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no - .eabi_attribute 10, 0 @ Tag_FP_arch = none .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP - .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed - .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 .text @ Field constants @@ -35,6 +29,7 @@ Note: .align 2 .global secp256k1_fe_mul_inner .type secp256k1_fe_mul_inner, %function + .hidden secp256k1_fe_mul_inner @ Arguments: @ r0 r Restrict: can overlap with a, not with b @ r1 a @@ -522,6 +517,7 @@ secp256k1_fe_mul_inner: .align 2 .global secp256k1_fe_sqr_inner .type secp256k1_fe_sqr_inner, %function + .hidden secp256k1_fe_sqr_inner @ Arguments: @ r0 r Can overlap with a @ r1 a @@ -917,3 +913,4 @@ secp256k1_fe_sqr_inner: ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner + .section .note.GNU-stack,"",%progbits diff --git a/src/secp256k1/src/assumptions.h b/src/secp256k1/src/assumptions.h new file mode 100644 index 000000000..796100535 --- /dev/null +++ b/src/secp256k1/src/assumptions.h @@ -0,0 +1,87 @@ +/*********************************************************************** + * Copyright (c) 2020 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_ASSUMPTIONS_H +#define SECP256K1_ASSUMPTIONS_H + +#include + +#include "util.h" +#if defined(SECP256K1_INT128_NATIVE) +#include "int128_native.h" +#endif + +/* This library, like most software, relies on a number of compiler implementation defined (but not undefined) + behaviours. Although the behaviours we require are essentially universal we test them specifically here to + reduce the odds of experiencing an unwelcome surprise. +*/ + +#if defined(__has_attribute) +# if __has_attribute(__unavailable__) +__attribute__((__unavailable__("Don't call this function. It only exists because STATIC_ASSERT cannot be used outside a function."))) +# endif +#endif +static void secp256k1_assumption_checker(void) { + /* Bytes are 8 bits. */ + STATIC_ASSERT(CHAR_BIT == 8); + + /* No integer promotion for uint32_t. This ensures that we can multiply uintXX_t values where XX >= 32 + without signed overflow, which would be undefined behaviour. */ + STATIC_ASSERT(UINT_MAX <= UINT32_MAX); + + /* Conversions from unsigned to signed outside of the bounds of the signed type are + implementation-defined. Verify that they function as reinterpreting the lower + bits of the input in two's complement notation. Do this for conversions: + - from uint(N)_t to int(N)_t with negative result + - from uint(2N)_t to int(N)_t with negative result + - from int(2N)_t to int(N)_t with negative result + - from int(2N)_t to int(N)_t with positive result */ + + /* To int8_t. */ + STATIC_ASSERT(((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55)); + STATIC_ASSERT((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33); + STATIC_ASSERT((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF); + STATIC_ASSERT((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34); + + /* To int16_t. */ + STATIC_ASSERT((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322); + STATIC_ASSERT((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C); + STATIC_ASSERT((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4); + STATIC_ASSERT((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678); + + /* To int32_t. */ + STATIC_ASSERT((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B); + STATIC_ASSERT((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE); + STATIC_ASSERT((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8); + STATIC_ASSERT((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789); + + /* To int64_t. */ + STATIC_ASSERT((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL); +#if defined(SECP256K1_INT128_NATIVE) + STATIC_ASSERT((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL); + STATIC_ASSERT(((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL); + STATIC_ASSERT(((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL); + + /* To int128_t. */ + STATIC_ASSERT((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL)); +#endif + + /* Right shift on negative signed values is implementation defined. Verify that it + acts as a right shift in two's complement with sign extension (i.e duplicating + the top bit into newly added bits). */ + STATIC_ASSERT((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA); + STATIC_ASSERT((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A); + STATIC_ASSERT((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48); + STATIC_ASSERT((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL); +#if defined(SECP256K1_INT128_NATIVE) + STATIC_ASSERT((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL)); +#endif + + /* This function is not supposed to be called. */ + VERIFY_CHECK(0); +} + +#endif /* SECP256K1_ASSUMPTIONS_H */ diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h deleted file mode 100644 index 5af52c2c2..000000000 --- a/src/secp256k1/src/basic-config.h +++ /dev/null @@ -1,33 +0,0 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#ifndef SECP256K1_BASIC_CONFIG_H -#define SECP256K1_BASIC_CONFIG_H - -#ifdef USE_BASIC_CONFIG - -#undef USE_ASM_X86_64 -#undef USE_ENDOMORPHISM -#undef USE_FIELD_10X26 -#undef USE_FIELD_5X52 -#undef USE_FIELD_INV_BUILTIN -#undef USE_FIELD_INV_NUM -#undef USE_NUM_GMP -#undef USE_NUM_NONE -#undef USE_SCALAR_4X64 -#undef USE_SCALAR_8X32 -#undef USE_SCALAR_INV_BUILTIN -#undef USE_SCALAR_INV_NUM - -#define USE_NUM_NONE 1 -#define USE_FIELD_INV_BUILTIN 1 -#define USE_SCALAR_INV_BUILTIN 1 -#define USE_FIELD_10X26 1 -#define USE_SCALAR_8X32 1 - -#endif /* USE_BASIC_CONFIG */ - -#endif /* SECP256K1_BASIC_CONFIG_H */ diff --git a/src/secp256k1/src/bench.c b/src/secp256k1/src/bench.c new file mode 100644 index 000000000..1127df67a --- /dev/null +++ b/src/secp256k1/src/bench.c @@ -0,0 +1,279 @@ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#include +#include + +#include "../include/secp256k1.h" +#include "util.h" +#include "bench.h" + +static void help(int default_iters) { + printf("Benchmarks the following algorithms:\n"); + printf(" - ECDSA signing/verification\n"); + +#ifdef ENABLE_MODULE_ECDH + printf(" - ECDH key exchange (optional module)\n"); +#endif + +#ifdef ENABLE_MODULE_RECOVERY + printf(" - Public key recovery (optional module)\n"); +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG + printf(" - Schnorr signatures (optional module)\n"); +#endif + + printf("\n"); + printf("The default number of iterations for each benchmark is %d. This can be\n", default_iters); + printf("customized using the SECP256K1_BENCH_ITERS environment variable.\n"); + printf("\n"); + printf("Usage: ./bench [args]\n"); + printf("By default, all benchmarks will be run.\n"); + printf("args:\n"); + printf(" help : display this help and exit\n"); + printf(" ecdsa : all ECDSA algorithms--sign, verify, recovery (if enabled)\n"); + printf(" ecdsa_sign : ECDSA siging algorithm\n"); + printf(" ecdsa_verify : ECDSA verification algorithm\n"); + printf(" ec : all EC public key algorithms (keygen)\n"); + printf(" ec_keygen : EC public key generation\n"); + +#ifdef ENABLE_MODULE_RECOVERY + printf(" ecdsa_recover : ECDSA public key recovery algorithm\n"); +#endif + +#ifdef ENABLE_MODULE_ECDH + printf(" ecdh : ECDH key exchange algorithm\n"); +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG + printf(" schnorrsig : all Schnorr signature algorithms (sign, verify)\n"); + printf(" schnorrsig_sign : Schnorr sigining algorithm\n"); + printf(" schnorrsig_verify : Schnorr verification algorithm\n"); +#endif + +#ifdef ENABLE_MODULE_ELLSWIFT + printf(" ellswift : all ElligatorSwift benchmarks (encode, decode, keygen, ecdh)\n"); + printf(" ellswift_encode : ElligatorSwift encoding\n"); + printf(" ellswift_decode : ElligatorSwift decoding\n"); + printf(" ellswift_keygen : ElligatorSwift key generation\n"); + printf(" ellswift_ecdh : ECDH on ElligatorSwift keys\n"); +#endif + + printf("\n"); +} + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + unsigned char key[32]; + unsigned char sig[72]; + size_t siglen; + unsigned char pubkey[33]; + size_t pubkeylen; +} bench_data; + +static void bench_verify(void* arg, int iters) { + int i; + bench_data* data = (bench_data*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->pubkey, data->pubkeylen) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(data->ctx, &sig, data->sig, data->siglen) == 1); + CHECK(secp256k1_ecdsa_verify(data->ctx, &sig, data->msg, &pubkey) == (i == 0)); + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} + +static void bench_sign_setup(void* arg) { + int i; + bench_data *data = (bench_data*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = i + 1; + } + for (i = 0; i < 32; i++) { + data->key[i] = i + 65; + } +} + +static void bench_sign_run(void* arg, int iters) { + int i; + bench_data *data = (bench_data*)arg; + + unsigned char sig[74]; + for (i = 0; i < iters; i++) { + size_t siglen = 74; + int j; + secp256k1_ecdsa_signature signature; + CHECK(secp256k1_ecdsa_sign(data->ctx, &signature, data->msg, data->key, NULL, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(data->ctx, sig, &siglen, &signature)); + for (j = 0; j < 32; j++) { + data->msg[j] = sig[j]; + data->key[j] = sig[j + 32]; + } + } +} + +static void bench_keygen_setup(void* arg) { + int i; + bench_data *data = (bench_data*)arg; + + for (i = 0; i < 32; i++) { + data->key[i] = i + 65; + } +} + +static void bench_keygen_run(void *arg, int iters) { + int i; + bench_data *data = (bench_data*)arg; + + for (i = 0; i < iters; i++) { + unsigned char pub33[33]; + size_t len = 33; + secp256k1_pubkey pubkey; + CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->key)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pub33, &len, &pubkey, SECP256K1_EC_COMPRESSED)); + memcpy(data->key, pub33 + 1, 32); + } +} + + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/bench_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/bench_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/bench_impl.h" +#endif + +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/bench_impl.h" +#endif + +int main(int argc, char** argv) { + int i; + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + bench_data data; + + int d = argc == 1; + int default_iters = 20000; + int iters = get_iters(default_iters); + + /* Check for invalid user arguments */ + char* valid_args[] = {"ecdsa", "verify", "ecdsa_verify", "sign", "ecdsa_sign", "ecdh", "recover", + "ecdsa_recover", "schnorrsig", "schnorrsig_verify", "schnorrsig_sign", "ec", + "keygen", "ec_keygen", "ellswift", "encode", "ellswift_encode", "decode", + "ellswift_decode", "ellswift_keygen", "ellswift_ecdh"}; + size_t valid_args_size = sizeof(valid_args)/sizeof(valid_args[0]); + int invalid_args = have_invalid_args(argc, argv, valid_args, valid_args_size); + + if (argc > 1) { + if (have_flag(argc, argv, "-h") + || have_flag(argc, argv, "--help") + || have_flag(argc, argv, "help")) { + help(default_iters); + return 0; + } else if (invalid_args) { + fprintf(stderr, "./bench: unrecognized argument.\n\n"); + help(default_iters); + return 1; + } + } + +/* Check if the user tries to benchmark optional module without building it */ +#ifndef ENABLE_MODULE_ECDH + if (have_flag(argc, argv, "ecdh")) { + fprintf(stderr, "./bench: ECDH module not enabled.\n"); + fprintf(stderr, "Use ./configure --enable-module-ecdh.\n\n"); + return 1; + } +#endif + +#ifndef ENABLE_MODULE_RECOVERY + if (have_flag(argc, argv, "recover") || have_flag(argc, argv, "ecdsa_recover")) { + fprintf(stderr, "./bench: Public key recovery module not enabled.\n"); + fprintf(stderr, "Use ./configure --enable-module-recovery.\n\n"); + return 1; + } +#endif + +#ifndef ENABLE_MODULE_SCHNORRSIG + if (have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "schnorrsig_sign") || have_flag(argc, argv, "schnorrsig_verify")) { + fprintf(stderr, "./bench: Schnorr signatures module not enabled.\n"); + fprintf(stderr, "Use ./configure --enable-module-schnorrsig.\n\n"); + return 1; + } +#endif + +#ifndef ENABLE_MODULE_ELLSWIFT + if (have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "ellswift_encode") || have_flag(argc, argv, "ellswift_decode") || + have_flag(argc, argv, "encode") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_keygen") || + have_flag(argc, argv, "ellswift_ecdh")) { + fprintf(stderr, "./bench: ElligatorSwift module not enabled.\n"); + fprintf(stderr, "Use ./configure --enable-module-ellswift.\n\n"); + return 1; + } +#endif + + /* ECDSA benchmark */ + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + for (i = 0; i < 32; i++) { + data.msg[i] = 1 + i; + } + for (i = 0; i < 32; i++) { + data.key[i] = 33 + i; + } + data.siglen = 72; + CHECK(secp256k1_ecdsa_sign(data.ctx, &sig, data.msg, data.key, NULL, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(data.ctx, data.sig, &data.siglen, &sig)); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &pubkey, data.key)); + data.pubkeylen = 33; + CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + + print_output_table_header_row(); + if (d || have_flag(argc, argv, "ecdsa") || have_flag(argc, argv, "verify") || have_flag(argc, argv, "ecdsa_verify")) run_benchmark("ecdsa_verify", bench_verify, NULL, NULL, &data, 10, iters); + + if (d || have_flag(argc, argv, "ecdsa") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "ecdsa_sign")) run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ec") || have_flag(argc, argv, "keygen") || have_flag(argc, argv, "ec_keygen")) run_benchmark("ec_keygen", bench_keygen_run, bench_keygen_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); + +#ifdef ENABLE_MODULE_ECDH + /* ECDH benchmarks */ + run_ecdh_bench(iters, argc, argv); +#endif + +#ifdef ENABLE_MODULE_RECOVERY + /* ECDSA recovery benchmarks */ + run_recovery_bench(iters, argc, argv); +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG + /* Schnorr signature benchmarks */ + run_schnorrsig_bench(iters, argc, argv); +#endif + +#ifdef ENABLE_MODULE_ELLSWIFT + /* ElligatorSwift benchmarks */ + run_ellswift_bench(iters, argc, argv); +#endif + + return 0; +} diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h index 24511e01c..94343f53d 100644 --- a/src/secp256k1/src/bench.h +++ b/src/secp256k1/src/bench.h @@ -1,50 +1,115 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_BENCH_H #define SECP256K1_BENCH_H +#include +#include #include -#include -#include "sys/time.h" +#include -static double gettimedouble(void) { +#if (defined(_MSC_VER) && _MSC_VER >= 1900) +# include +#else +# include +#endif + +static int64_t gettime_i64(void) { +#if (defined(_MSC_VER) && _MSC_VER >= 1900) + /* C11 way to get wallclock time */ + struct timespec tv; + if (!timespec_get(&tv, TIME_UTC)) { + fputs("timespec_get failed!", stderr); + exit(1); + } + return (int64_t)tv.tv_nsec / 1000 + (int64_t)tv.tv_sec * 1000000LL; +#else struct timeval tv; gettimeofday(&tv, NULL); - return tv.tv_usec * 0.000001 + tv.tv_sec; + return (int64_t)tv.tv_usec + (int64_t)tv.tv_sec * 1000000LL; +#endif } -void print_number(double x) { - double y = x; - int c = 0; - if (y < 0.0) { - y = -y; +#define FP_EXP (6) +#define FP_MULT (1000000LL) + +/* Format fixed point number. */ +static void print_number(const int64_t x) { + int64_t x_abs, y; + int c, i, rounding, g; /* g = integer part size, c = fractional part size */ + size_t ptr; + char buffer[30]; + + if (x == INT64_MIN) { + /* Prevent UB. */ + printf("ERR"); + return; } - while (y > 0 && y < 100.0) { - y *= 10.0; + x_abs = x < 0 ? -x : x; + + /* Determine how many decimals we want to show (more than FP_EXP makes no + * sense). */ + y = x_abs; + c = 0; + while (y > 0LL && y < 100LL * FP_MULT && c < FP_EXP) { + y *= 10LL; c++; } - printf("%.*f", c, x); + + /* Round to 'c' decimals. */ + y = x_abs; + rounding = 0; + for (i = c; i < FP_EXP; ++i) { + rounding = (y % 10) >= 5; + y /= 10; + } + y += rounding; + + /* Format and print the number. */ + ptr = sizeof(buffer) - 1; + buffer[ptr] = 0; + g = 0; + if (c != 0) { /* non zero fractional part */ + for (i = 0; i < c; ++i) { + buffer[--ptr] = '0' + (y % 10); + y /= 10; + } + } else if (c == 0) { /* fractional part is 0 */ + buffer[--ptr] = '0'; + } + buffer[--ptr] = '.'; + do { + buffer[--ptr] = '0' + (y % 10); + y /= 10; + g++; + } while (y != 0); + if (x < 0) { + buffer[--ptr] = '-'; + g++; + } + printf("%5.*s", g, &buffer[ptr]); /* Prints integer part */ + printf("%-*s", FP_EXP, &buffer[ptr + g]); /* Prints fractional part */ } -void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) { +static void run_benchmark(char *name, void (*benchmark)(void*, int), void (*setup)(void*), void (*teardown)(void*, int), void* data, int count, int iter) { int i; - double min = HUGE_VAL; - double sum = 0.0; - double max = 0.0; + int64_t min = INT64_MAX; + int64_t sum = 0; + int64_t max = 0; for (i = 0; i < count; i++) { - double begin, total; + int64_t begin, total; if (setup != NULL) { setup(data); } - begin = gettimedouble(); - benchmark(data); - total = gettimedouble() - begin; + begin = gettime_i64(); + benchmark(data, iter); + total = gettime_i64() - begin; if (teardown != NULL) { - teardown(data); + teardown(data, iter); } if (total < min) { min = total; @@ -54,13 +119,70 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v } sum += total; } - printf("%s: min ", name); - print_number(min * 1000000.0 / iter); - printf("us / avg "); - print_number((sum / count) * 1000000.0 / iter); - printf("us / max "); - print_number(max * 1000000.0 / iter); - printf("us\n"); + /* ',' is used as a column delimiter */ + printf("%-30s, ", name); + print_number(min * FP_MULT / iter); + printf(" , "); + print_number(((sum * FP_MULT) / count) / iter); + printf(" , "); + print_number(max * FP_MULT / iter); + printf("\n"); +} + +static int have_flag(int argc, char** argv, char *flag) { + char** argm = argv + argc; + argv++; + while (argv != argm) { + if (strcmp(*argv, flag) == 0) { + return 1; + } + argv++; + } + return 0; +} + +/* takes an array containing the arguments that the user is allowed to enter on the command-line + returns: + - 1 if the user entered an invalid argument + - 0 if all the user entered arguments are valid */ +static int have_invalid_args(int argc, char** argv, char** valid_args, size_t n) { + size_t i; + int found_valid; + char** argm = argv + argc; + argv++; + + while (argv != argm) { + found_valid = 0; + for (i = 0; i < n; i++) { + if (strcmp(*argv, valid_args[i]) == 0) { + found_valid = 1; /* user entered a valid arg from the list */ + break; + } + } + if (found_valid == 0) { + return 1; /* invalid arg found */ + } + argv++; + } + return 0; +} + +static int get_iters(int default_iters) { + char* env = getenv("SECP256K1_BENCH_ITERS"); + if (env) { + return strtol(env, NULL, 0); + } else { + return default_iters; + } +} + +static void print_output_table_header_row(void) { + char* bench_str = "Benchmark"; /* left justified */ + char* min_str = " Min(us) "; /* center alignment */ + char* avg_str = " Avg(us) "; + char* max_str = " Max(us) "; + printf("%-30s,%-15s,%-15s,%-15s\n", bench_str, min_str, avg_str, max_str); + printf("\n"); } #endif /* SECP256K1_BENCH_H */ diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c new file mode 100644 index 000000000..7dc52ad87 --- /dev/null +++ b/src/secp256k1/src/bench_ecmult.c @@ -0,0 +1,367 @@ +/*********************************************************************** + * Copyright (c) 2017 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ +#include + +#include "secp256k1.c" +#include "../include/secp256k1.h" + +#include "util.h" +#include "hash_impl.h" +#include "field_impl.h" +#include "group_impl.h" +#include "scalar_impl.h" +#include "ecmult_impl.h" +#include "bench.h" + +#define POINTS 32768 + +static void help(char **argv) { + printf("Benchmark EC multiplication algorithms\n"); + printf("\n"); + printf("Usage: %s \n", argv[0]); + printf("The output shows the number of multiplied and summed points right after the\n"); + printf("function name. The letter 'g' indicates that one of the points is the generator.\n"); + printf("The benchmarks are divided by the number of points.\n"); + printf("\n"); + printf("default (ecmult_multi): picks pippenger_wnaf or strauss_wnaf depending on the\n"); + printf(" batch size\n"); + printf("pippenger_wnaf: for all batch sizes\n"); + printf("strauss_wnaf: for all batch sizes\n"); + printf("simple: multiply and sum each point individually\n"); +} + +typedef struct { + /* Setup once in advance */ + secp256k1_context* ctx; + secp256k1_scratch_space* scratch; + secp256k1_scalar* scalars; + secp256k1_ge* pubkeys; + secp256k1_gej* pubkeys_gej; + secp256k1_scalar* seckeys; + secp256k1_gej* expected_output; + secp256k1_ecmult_multi_func ecmult_multi; + + /* Changes per benchmark */ + size_t count; + int includes_g; + + /* Changes per benchmark iteration, used to pick different scalars and pubkeys + * in each run. */ + size_t offset1; + size_t offset2; + + /* Benchmark output. */ + secp256k1_gej* output; +} bench_data; + +/* Hashes x into [0, POINTS) twice and store the result in offset1 and offset2. */ +static void hash_into_offset(bench_data* data, size_t x) { + data->offset1 = (x * 0x537b7f6f + 0x8f66a481) % POINTS; + data->offset2 = (x * 0x7f6f537b + 0x6a1a8f49) % POINTS; +} + +/* Check correctness of the benchmark by computing + * sum(outputs) ?= (sum(scalars_gen) + sum(seckeys)*sum(scalars))*G */ +static void bench_ecmult_teardown_helper(bench_data* data, size_t* seckey_offset, size_t* scalar_offset, size_t* scalar_gen_offset, int iters) { + int i; + secp256k1_gej sum_output, tmp; + secp256k1_scalar sum_scalars; + + secp256k1_gej_set_infinity(&sum_output); + secp256k1_scalar_clear(&sum_scalars); + for (i = 0; i < iters; ++i) { + secp256k1_gej_add_var(&sum_output, &sum_output, &data->output[i], NULL); + if (scalar_gen_offset != NULL) { + secp256k1_scalar_add(&sum_scalars, &sum_scalars, &data->scalars[(*scalar_gen_offset+i) % POINTS]); + } + if (seckey_offset != NULL) { + secp256k1_scalar s = data->seckeys[(*seckey_offset+i) % POINTS]; + secp256k1_scalar_mul(&s, &s, &data->scalars[(*scalar_offset+i) % POINTS]); + secp256k1_scalar_add(&sum_scalars, &sum_scalars, &s); + } + } + secp256k1_ecmult_gen(&data->ctx->ecmult_gen_ctx, &tmp, &sum_scalars); + CHECK(secp256k1_gej_eq_var(&tmp, &sum_output)); +} + +static void bench_ecmult_setup(void* arg) { + bench_data* data = (bench_data*)arg; + /* Re-randomize offset to ensure that we're using different scalars and + * group elements in each run. */ + hash_into_offset(data, data->offset1); +} + +static void bench_ecmult_gen(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + int i; + + for (i = 0; i < iters; ++i) { + secp256k1_ecmult_gen(&data->ctx->ecmult_gen_ctx, &data->output[i], &data->scalars[(data->offset1+i) % POINTS]); + } +} + +static void bench_ecmult_gen_teardown(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + bench_ecmult_teardown_helper(data, NULL, NULL, &data->offset1, iters); +} + +static void bench_ecmult_const(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + int i; + + for (i = 0; i < iters; ++i) { + secp256k1_ecmult_const(&data->output[i], &data->pubkeys[(data->offset1+i) % POINTS], &data->scalars[(data->offset2+i) % POINTS]); + } +} + +static void bench_ecmult_const_teardown(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + bench_ecmult_teardown_helper(data, &data->offset1, &data->offset2, NULL, iters); +} + +static void bench_ecmult_1p(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + int i; + + for (i = 0; i < iters; ++i) { + secp256k1_ecmult(&data->output[i], &data->pubkeys_gej[(data->offset1+i) % POINTS], &data->scalars[(data->offset2+i) % POINTS], NULL); + } +} + +static void bench_ecmult_1p_teardown(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + bench_ecmult_teardown_helper(data, &data->offset1, &data->offset2, NULL, iters); +} + +static void bench_ecmult_0p_g(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + int i; + + for (i = 0; i < iters; ++i) { + secp256k1_ecmult(&data->output[i], NULL, &secp256k1_scalar_zero, &data->scalars[(data->offset1+i) % POINTS]); + } +} + +static void bench_ecmult_0p_g_teardown(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + bench_ecmult_teardown_helper(data, NULL, NULL, &data->offset1, iters); +} + +static void bench_ecmult_1p_g(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + int i; + + for (i = 0; i < iters/2; ++i) { + secp256k1_ecmult(&data->output[i], &data->pubkeys_gej[(data->offset1+i) % POINTS], &data->scalars[(data->offset2+i) % POINTS], &data->scalars[(data->offset1+i) % POINTS]); + } +} + +static void bench_ecmult_1p_g_teardown(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + bench_ecmult_teardown_helper(data, &data->offset1, &data->offset2, &data->offset1, iters/2); +} + +static void run_ecmult_bench(bench_data* data, int iters) { + char str[32]; + sprintf(str, "ecmult_gen"); + run_benchmark(str, bench_ecmult_gen, bench_ecmult_setup, bench_ecmult_gen_teardown, data, 10, iters); + sprintf(str, "ecmult_const"); + run_benchmark(str, bench_ecmult_const, bench_ecmult_setup, bench_ecmult_const_teardown, data, 10, iters); + /* ecmult with non generator point */ + sprintf(str, "ecmult_1p"); + run_benchmark(str, bench_ecmult_1p, bench_ecmult_setup, bench_ecmult_1p_teardown, data, 10, iters); + /* ecmult with generator point */ + sprintf(str, "ecmult_0p_g"); + run_benchmark(str, bench_ecmult_0p_g, bench_ecmult_setup, bench_ecmult_0p_g_teardown, data, 10, iters); + /* ecmult with generator and non-generator point. The reported time is per point. */ + sprintf(str, "ecmult_1p_g"); + run_benchmark(str, bench_ecmult_1p_g, bench_ecmult_setup, bench_ecmult_1p_g_teardown, data, 10, 2*iters); +} + +static int bench_ecmult_multi_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, void* arg) { + bench_data* data = (bench_data*)arg; + if (data->includes_g) ++idx; + if (idx == 0) { + *sc = data->scalars[data->offset1]; + *ge = secp256k1_ge_const_g; + } else { + *sc = data->scalars[(data->offset1 + idx) % POINTS]; + *ge = data->pubkeys[(data->offset2 + idx - 1) % POINTS]; + } + return 1; +} + +static void bench_ecmult_multi(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + + int includes_g = data->includes_g; + int iter; + int count = data->count; + iters = iters / data->count; + + for (iter = 0; iter < iters; ++iter) { + data->ecmult_multi(&data->ctx->error_callback, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_ecmult_multi_callback, arg, count - includes_g); + data->offset1 = (data->offset1 + count) % POINTS; + data->offset2 = (data->offset2 + count - 1) % POINTS; + } +} + +static void bench_ecmult_multi_setup(void* arg) { + bench_data* data = (bench_data*)arg; + hash_into_offset(data, data->count); +} + +static void bench_ecmult_multi_teardown(void* arg, int iters) { + bench_data* data = (bench_data*)arg; + int iter; + iters = iters / data->count; + /* Verify the results in teardown, to avoid doing comparisons while benchmarking. */ + for (iter = 0; iter < iters; ++iter) { + secp256k1_gej tmp; + secp256k1_gej_add_var(&tmp, &data->output[iter], &data->expected_output[iter], NULL); + CHECK(secp256k1_gej_is_infinity(&tmp)); + } +} + +static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) { + secp256k1_sha256 sha256; + unsigned char c[10] = {'e', 'c', 'm', 'u', 'l', 't', 0, 0, 0, 0}; + unsigned char buf[32]; + int overflow = 0; + c[6] = num; + c[7] = num >> 8; + c[8] = num >> 16; + c[9] = num >> 24; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, c, sizeof(c)); + secp256k1_sha256_finalize(&sha256, buf); + secp256k1_scalar_set_b32(scalar, buf, &overflow); + CHECK(!overflow); +} + +static void run_ecmult_multi_bench(bench_data* data, size_t count, int includes_g, int num_iters) { + char str[32]; + size_t iters = 1 + num_iters / count; + size_t iter; + + data->count = count; + data->includes_g = includes_g; + + /* Compute (the negation of) the expected results directly. */ + hash_into_offset(data, data->count); + for (iter = 0; iter < iters; ++iter) { + secp256k1_scalar tmp; + secp256k1_scalar total = data->scalars[(data->offset1++) % POINTS]; + size_t i = 0; + for (i = 0; i + 1 < count; ++i) { + secp256k1_scalar_mul(&tmp, &data->seckeys[(data->offset2++) % POINTS], &data->scalars[(data->offset1++) % POINTS]); + secp256k1_scalar_add(&total, &total, &tmp); + } + secp256k1_scalar_negate(&total, &total); + secp256k1_ecmult(&data->expected_output[iter], NULL, &secp256k1_scalar_zero, &total); + } + + /* Run the benchmark. */ + if (includes_g) { + sprintf(str, "ecmult_multi_%ip_g", (int)count - 1); + } else { + sprintf(str, "ecmult_multi_%ip", (int)count); + } + run_benchmark(str, bench_ecmult_multi, bench_ecmult_multi_setup, bench_ecmult_multi_teardown, data, 10, count * iters); +} + +int main(int argc, char **argv) { + bench_data data; + int i, p; + size_t scratch_size; + + int iters = get_iters(10000); + + data.ecmult_multi = secp256k1_ecmult_multi_var; + + if (argc > 1) { + if(have_flag(argc, argv, "-h") + || have_flag(argc, argv, "--help") + || have_flag(argc, argv, "help")) { + help(argv); + return 0; + } else if(have_flag(argc, argv, "pippenger_wnaf")) { + printf("Using pippenger_wnaf:\n"); + data.ecmult_multi = secp256k1_ecmult_pippenger_batch_single; + } else if(have_flag(argc, argv, "strauss_wnaf")) { + printf("Using strauss_wnaf:\n"); + data.ecmult_multi = secp256k1_ecmult_strauss_batch_single; + } else if(have_flag(argc, argv, "simple")) { + printf("Using simple algorithm:\n"); + } else { + fprintf(stderr, "%s: unrecognized argument '%s'.\n\n", argv[0], argv[1]); + help(argv); + return 1; + } + } + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16; + if (!have_flag(argc, argv, "simple")) { + data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size); + } else { + data.scratch = NULL; + } + + /* Allocate stuff */ + data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS); + data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS); + data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS); + data.pubkeys_gej = malloc(sizeof(secp256k1_gej) * POINTS); + data.expected_output = malloc(sizeof(secp256k1_gej) * (iters + 1)); + data.output = malloc(sizeof(secp256k1_gej) * (iters + 1)); + + /* Generate a set of scalars, and private/public keypairs. */ + secp256k1_gej_set_ge(&data.pubkeys_gej[0], &secp256k1_ge_const_g); + secp256k1_scalar_set_int(&data.seckeys[0], 1); + for (i = 0; i < POINTS; ++i) { + generate_scalar(i, &data.scalars[i]); + if (i) { + secp256k1_gej_double_var(&data.pubkeys_gej[i], &data.pubkeys_gej[i - 1], NULL); + secp256k1_scalar_add(&data.seckeys[i], &data.seckeys[i - 1], &data.seckeys[i - 1]); + } + } + secp256k1_ge_set_all_gej_var(data.pubkeys, data.pubkeys_gej, POINTS); + + + print_output_table_header_row(); + /* Initialize offset1 and offset2 */ + hash_into_offset(&data, 0); + run_ecmult_bench(&data, iters); + + for (i = 1; i <= 8; ++i) { + run_ecmult_multi_bench(&data, i, 1, iters); + } + + /* This is disabled with low count of iterations because the loop runs 77 times even with iters=1 + * and the higher it goes the longer the computation takes(more points) + * So we don't run this benchmark with low iterations to prevent slow down */ + if (iters > 2) { + for (p = 0; p <= 11; ++p) { + for (i = 9; i <= 16; ++i) { + run_ecmult_multi_bench(&data, i << p, 1, iters); + } + } + } + + if (data.scratch != NULL) { + secp256k1_scratch_space_destroy(data.ctx, data.scratch); + } + secp256k1_context_destroy(data.ctx); + free(data.scalars); + free(data.pubkeys); + free(data.pubkeys_gej); + free(data.seckeys); + free(data.output); + free(data.expected_output); + + return(0); +} diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c index b630482de..a70068492 100644 --- a/src/secp256k1/src/bench_internal.c +++ b/src/secp256k1/src/bench_internal.c @@ -1,382 +1,436 @@ -/************************************************************************ - * Copyright (c) 2014-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #include -#include "include/secp256k1.h" +#include "secp256k1.c" +#include "../include/secp256k1.h" +#include "assumptions.h" #include "util.h" #include "hash_impl.h" -#include "num_impl.h" #include "field_impl.h" #include "group_impl.h" #include "scalar_impl.h" -#include "ecmult_const_impl.h" #include "ecmult_impl.h" #include "bench.h" -#include "secp256k1.c" + +static void help(int default_iters) { + printf("Benchmarks various internal routines.\n"); + printf("\n"); + printf("The default number of iterations for each benchmark is %d. This can be\n", default_iters); + printf("customized using the SECP256K1_BENCH_ITERS environment variable.\n"); + printf("\n"); + printf("Usage: ./bench_internal [args]\n"); + printf("By default, all benchmarks will be run.\n"); + printf("args:\n"); + printf(" help : display this help and exit\n"); + printf(" scalar : all scalar operations (add, half, inverse, mul, negate, split)\n"); + printf(" field : all field operations (half, inverse, issquare, mul, normalize, sqr, sqrt)\n"); + printf(" group : all group operations (add, double, to_affine)\n"); + printf(" ecmult : all point multiplication operations (ecmult_wnaf) \n"); + printf(" hash : all hash algorithms (hmac, rng6979, sha256)\n"); + printf(" context : all context object operations (context_create)\n"); + printf("\n"); +} typedef struct { - secp256k1_scalar scalar_x, scalar_y; - secp256k1_fe fe_x, fe_y; - secp256k1_ge ge_x, ge_y; - secp256k1_gej gej_x, gej_y; + secp256k1_scalar scalar[2]; + secp256k1_fe fe[4]; + secp256k1_ge ge[2]; + secp256k1_gej gej[2]; unsigned char data[64]; int wnaf[256]; } bench_inv; -void bench_setup(void* arg) { +static void bench_setup(void* arg) { bench_inv *data = (bench_inv*)arg; - static const unsigned char init_x[32] = { - 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, - 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35, - 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, - 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83 + static const unsigned char init[4][32] = { + /* Initializer for scalar[0], fe[0], first half of data, the X coordinate of ge[0], + and the (implied affine) X coordinate of gej[0]. */ + { + 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, + 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35, + 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, + 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83 + }, + /* Initializer for scalar[1], fe[1], first half of data, the X coordinate of ge[1], + and the (implied affine) X coordinate of gej[1]. */ + { + 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83, + 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5, + 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9, + 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3 + }, + /* Initializer for fe[2] and the Z coordinate of gej[0]. */ + { + 0x3d, 0x2d, 0xef, 0xf4, 0x25, 0x98, 0x4f, 0x5d, + 0xe2, 0xca, 0x5f, 0x41, 0x3f, 0x3f, 0xce, 0x44, + 0xaa, 0x2c, 0x53, 0x8a, 0xc6, 0x59, 0x1f, 0x38, + 0x38, 0x23, 0xe4, 0x11, 0x27, 0xc6, 0xa0, 0xe7 + }, + /* Initializer for fe[3] and the Z coordinate of gej[1]. */ + { + 0xbd, 0x21, 0xa5, 0xe1, 0x13, 0x50, 0x73, 0x2e, + 0x52, 0x98, 0xc8, 0x9e, 0xab, 0x00, 0xa2, 0x68, + 0x43, 0xf5, 0xd7, 0x49, 0x80, 0x72, 0xa7, 0xf3, + 0xd7, 0x60, 0xe6, 0xab, 0x90, 0x92, 0xdf, 0xc5 + } }; - static const unsigned char init_y[32] = { - 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83, - 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5, - 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9, - 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3 - }; + secp256k1_scalar_set_b32(&data->scalar[0], init[0], NULL); + secp256k1_scalar_set_b32(&data->scalar[1], init[1], NULL); + secp256k1_fe_set_b32_limit(&data->fe[0], init[0]); + secp256k1_fe_set_b32_limit(&data->fe[1], init[1]); + secp256k1_fe_set_b32_limit(&data->fe[2], init[2]); + secp256k1_fe_set_b32_limit(&data->fe[3], init[3]); + CHECK(secp256k1_ge_set_xo_var(&data->ge[0], &data->fe[0], 0)); + CHECK(secp256k1_ge_set_xo_var(&data->ge[1], &data->fe[1], 1)); + secp256k1_gej_set_ge(&data->gej[0], &data->ge[0]); + secp256k1_gej_rescale(&data->gej[0], &data->fe[2]); + secp256k1_gej_set_ge(&data->gej[1], &data->ge[1]); + secp256k1_gej_rescale(&data->gej[1], &data->fe[3]); + memcpy(data->data, init[0], 32); + memcpy(data->data + 32, init[1], 32); +} - secp256k1_scalar_set_b32(&data->scalar_x, init_x, NULL); - secp256k1_scalar_set_b32(&data->scalar_y, init_y, NULL); - secp256k1_fe_set_b32(&data->fe_x, init_x); - secp256k1_fe_set_b32(&data->fe_y, init_y); - CHECK(secp256k1_ge_set_xo_var(&data->ge_x, &data->fe_x, 0)); - CHECK(secp256k1_ge_set_xo_var(&data->ge_y, &data->fe_y, 1)); - secp256k1_gej_set_ge(&data->gej_x, &data->ge_x); - secp256k1_gej_set_ge(&data->gej_y, &data->ge_y); - memcpy(data->data, init_x, 32); - memcpy(data->data + 32, init_y, 32); +static void bench_scalar_add(void* arg, int iters) { + int i, j = 0; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]); + } + CHECK(j <= iters); } -void bench_scalar_add(void* arg) { +static void bench_scalar_negate(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 2000000; i++) { - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + for (i = 0; i < iters; i++) { + secp256k1_scalar_negate(&data->scalar[0], &data->scalar[0]); } } -void bench_scalar_negate(void* arg) { +static void bench_scalar_half(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; + secp256k1_scalar s = data->scalar[0]; - for (i = 0; i < 2000000; i++) { - secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x); + for (i = 0; i < iters; i++) { + secp256k1_scalar_half(&s, &s); } + + data->scalar[0] = s; } -void bench_scalar_sqr(void* arg) { +static void bench_scalar_mul(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 200000; i++) { - secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x); + for (i = 0; i < iters; i++) { + secp256k1_scalar_mul(&data->scalar[0], &data->scalar[0], &data->scalar[1]); } } -void bench_scalar_mul(void* arg) { - int i; +static void bench_scalar_split(void* arg, int iters) { + int i, j = 0; bench_inv *data = (bench_inv*)arg; + secp256k1_scalar tmp; - for (i = 0; i < 200000; i++) { - secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y); + for (i = 0; i < iters; i++) { + secp256k1_scalar_split_lambda(&tmp, &data->scalar[1], &data->scalar[0]); + j += secp256k1_scalar_add(&data->scalar[0], &tmp, &data->scalar[1]); } + CHECK(j <= iters); } -#ifdef USE_ENDOMORPHISM -void bench_scalar_split(void* arg) { - int i; +static void bench_scalar_inverse(void* arg, int iters) { + int i, j = 0; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 20000; i++) { - secp256k1_scalar l, r; - secp256k1_scalar_split_lambda(&l, &r, &data->scalar_x); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + for (i = 0; i < iters; i++) { + secp256k1_scalar_inverse(&data->scalar[0], &data->scalar[0]); + j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]); } + CHECK(j <= iters); } -#endif -void bench_scalar_inverse(void* arg) { - int i; +static void bench_scalar_inverse_var(void* arg, int iters) { + int i, j = 0; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 2000; i++) { - secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + for (i = 0; i < iters; i++) { + secp256k1_scalar_inverse_var(&data->scalar[0], &data->scalar[0]); + j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]); } + CHECK(j <= iters); } -void bench_scalar_inverse_var(void* arg) { +static void bench_field_half(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 2000; i++) { - secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + for (i = 0; i < iters; i++) { + secp256k1_fe_half(&data->fe[0]); } } -void bench_field_normalize(void* arg) { +static void bench_field_normalize(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 2000000; i++) { - secp256k1_fe_normalize(&data->fe_x); + for (i = 0; i < iters; i++) { + secp256k1_fe_normalize(&data->fe[0]); } } -void bench_field_normalize_weak(void* arg) { +static void bench_field_normalize_weak(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 2000000; i++) { - secp256k1_fe_normalize_weak(&data->fe_x); + for (i = 0; i < iters; i++) { + secp256k1_fe_normalize_weak(&data->fe[0]); } } -void bench_field_mul(void* arg) { +static void bench_field_mul(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 200000; i++) { - secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y); + for (i = 0; i < iters; i++) { + secp256k1_fe_mul(&data->fe[0], &data->fe[0], &data->fe[1]); } } -void bench_field_sqr(void* arg) { +static void bench_field_sqr(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 200000; i++) { - secp256k1_fe_sqr(&data->fe_x, &data->fe_x); + for (i = 0; i < iters; i++) { + secp256k1_fe_sqr(&data->fe[0], &data->fe[0]); } } -void bench_field_inverse(void* arg) { +static void bench_field_inverse(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 20000; i++) { - secp256k1_fe_inv(&data->fe_x, &data->fe_x); - secp256k1_fe_add(&data->fe_x, &data->fe_y); + for (i = 0; i < iters; i++) { + secp256k1_fe_inv(&data->fe[0], &data->fe[0]); + secp256k1_fe_add(&data->fe[0], &data->fe[1]); } } -void bench_field_inverse_var(void* arg) { +static void bench_field_inverse_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 20000; i++) { - secp256k1_fe_inv_var(&data->fe_x, &data->fe_x); - secp256k1_fe_add(&data->fe_x, &data->fe_y); + for (i = 0; i < iters; i++) { + secp256k1_fe_inv_var(&data->fe[0], &data->fe[0]); + secp256k1_fe_add(&data->fe[0], &data->fe[1]); } } -void bench_field_sqrt(void* arg) { - int i; +static void bench_field_sqrt(void* arg, int iters) { + int i, j = 0; bench_inv *data = (bench_inv*)arg; + secp256k1_fe t; - for (i = 0; i < 20000; i++) { - secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); - secp256k1_fe_add(&data->fe_x, &data->fe_y); + for (i = 0; i < iters; i++) { + t = data->fe[0]; + j += secp256k1_fe_sqrt(&data->fe[0], &t); + secp256k1_fe_add(&data->fe[0], &data->fe[1]); } + CHECK(j <= iters); } -void bench_group_double_var(void* arg) { - int i; +static void bench_field_is_square_var(void* arg, int iters) { + int i, j = 0; bench_inv *data = (bench_inv*)arg; + secp256k1_fe t = data->fe[0]; - for (i = 0; i < 200000; i++) { - secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL); + for (i = 0; i < iters; i++) { + j += secp256k1_fe_is_square_var(&t); + secp256k1_fe_add(&t, &data->fe[1]); + secp256k1_fe_normalize_var(&t); } + CHECK(j <= iters); } -void bench_group_add_var(void* arg) { +static void bench_group_double_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 200000; i++) { - secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL); + for (i = 0; i < iters; i++) { + secp256k1_gej_double_var(&data->gej[0], &data->gej[0], NULL); } } -void bench_group_add_affine(void* arg) { +static void bench_group_add_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 200000; i++) { - secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y); + for (i = 0; i < iters; i++) { + secp256k1_gej_add_var(&data->gej[0], &data->gej[0], &data->gej[1], NULL); } } -void bench_group_add_affine_var(void* arg) { +static void bench_group_add_affine(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 200000; i++) { - secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL); + for (i = 0; i < iters; i++) { + secp256k1_gej_add_ge(&data->gej[0], &data->gej[0], &data->ge[1]); } } -void bench_group_jacobi_var(void* arg) { +static void bench_group_add_affine_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 20000; i++) { - secp256k1_gej_has_quad_y_var(&data->gej_x); + for (i = 0; i < iters; i++) { + secp256k1_gej_add_ge_var(&data->gej[0], &data->gej[0], &data->ge[1], NULL); } } -void bench_ecmult_wnaf(void* arg) { +static void bench_group_add_zinv_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 20000; i++) { - secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + for (i = 0; i < iters; i++) { + secp256k1_gej_add_zinv_var(&data->gej[0], &data->gej[0], &data->ge[1], &data->gej[0].y); } } -void bench_wnaf_const(void* arg) { +static void bench_group_to_affine_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; - for (i = 0; i < 20000; i++) { - secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A); - secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + for (i = 0; i < iters; ++i) { + secp256k1_ge_set_gej_var(&data->ge[1], &data->gej[0]); + /* Use the output affine X/Y coordinates to vary the input X/Y/Z coordinates. + Note that the resulting coordinates will generally not correspond to a point + on the curve, but this is not a problem for the code being benchmarked here. + Adding and normalizing have less overhead than EC operations (which could + guarantee the point remains on the curve). */ + secp256k1_fe_add(&data->gej[0].x, &data->ge[1].y); + secp256k1_fe_add(&data->gej[0].y, &data->fe[2]); + secp256k1_fe_add(&data->gej[0].z, &data->ge[1].x); + secp256k1_fe_normalize_var(&data->gej[0].x); + secp256k1_fe_normalize_var(&data->gej[0].y); + secp256k1_fe_normalize_var(&data->gej[0].z); } } +static void bench_ecmult_wnaf(void* arg, int iters) { + int i, bits = 0, overflow = 0; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar[0], WINDOW_A); + overflow += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]); + } + CHECK(overflow >= 0); + CHECK(bits <= 256*iters); +} -void bench_sha256(void* arg) { +static void bench_sha256(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; secp256k1_sha256 sha; - for (i = 0; i < 20000; i++) { + for (i = 0; i < iters; i++) { secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, data->data, 32); secp256k1_sha256_finalize(&sha, data->data); } } -void bench_hmac_sha256(void* arg) { +static void bench_hmac_sha256(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; secp256k1_hmac_sha256 hmac; - for (i = 0; i < 20000; i++) { + for (i = 0; i < iters; i++) { secp256k1_hmac_sha256_initialize(&hmac, data->data, 32); secp256k1_hmac_sha256_write(&hmac, data->data, 32); secp256k1_hmac_sha256_finalize(&hmac, data->data); } } -void bench_rfc6979_hmac_sha256(void* arg) { +static void bench_rfc6979_hmac_sha256(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; secp256k1_rfc6979_hmac_sha256 rng; - for (i = 0; i < 20000; i++) { + for (i = 0; i < iters; i++) { secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64); secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32); } } -void bench_context_verify(void* arg) { +static void bench_context(void* arg, int iters) { int i; (void)arg; - for (i = 0; i < 20; i++) { - secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY)); + for (i = 0; i < iters; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_NONE)); } } -void bench_context_sign(void* arg) { - int i; - (void)arg; - for (i = 0; i < 200; i++) { - secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_SIGN)); - } -} - -#ifndef USE_NUM_NONE -void bench_num_jacobi(void* arg) { - int i; - bench_inv *data = (bench_inv*)arg; - secp256k1_num nx, norder; - - secp256k1_scalar_get_num(&nx, &data->scalar_x); - secp256k1_scalar_order_get_num(&norder); - secp256k1_scalar_get_num(&norder, &data->scalar_y); - - for (i = 0; i < 200000; i++) { - secp256k1_num_jacobi(&nx, &norder); - } -} -#endif - -int have_flag(int argc, char** argv, char *flag) { - char** argm = argv + argc; - argv++; - if (argv == argm) { - return 1; - } - while (argv != NULL && argv != argm) { - if (strcmp(*argv, flag) == 0) { - return 1; +int main(int argc, char **argv) { + bench_inv data; + int default_iters = 20000; + int iters = get_iters(default_iters); + int d = argc == 1; /* default */ + + if (argc > 1) { + if (have_flag(argc, argv, "-h") + || have_flag(argc, argv, "--help") + || have_flag(argc, argv, "help")) { + help(default_iters); + return 0; } - argv++; } - return 0; -} -int main(int argc, char **argv) { - bench_inv data; - if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000); - if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000); - if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000); - if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, 200000); -#ifdef USE_ENDOMORPHISM - if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, 20000); -#endif - if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000); - if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000); - - if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, 2000000); - if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, 2000000); - if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, 200000); - if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000); - if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000); - if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000); - if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000); - - if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000); - if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000); - if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000); - if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000); - if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000); - - if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000); - if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000); - - if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, 20000); - if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, 20000); - if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, 20000); - - if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20); - if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200); - -#ifndef USE_NUM_NONE - if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000); -#endif + print_output_table_header_row(); + + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "half")) run_benchmark("scalar_half", bench_scalar_half, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, iters); + + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "half")) run_benchmark("field_half", bench_field_half, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, iters*100); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "issquare")) run_benchmark("field_is_square_var", bench_field_is_square_var, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, iters); + + if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_zinv_var", bench_group_add_zinv_var, bench_setup, NULL, &data, 10, iters*10); + if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "to_affine")) run_benchmark("group_to_affine_var", bench_group_to_affine_var, bench_setup, NULL, &data, 10, iters); + + if (d || have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, iters); + + if (d || have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, iters); + + if (d || have_flag(argc, argv, "context")) run_benchmark("context_create", bench_context, bench_setup, NULL, &data, 10, iters); + return 0; } diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c deleted file mode 100644 index 4f4611d02..000000000 --- a/src/secp256k1/src/bench_sign.c +++ /dev/null @@ -1,56 +0,0 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#include "include/secp256k1.h" -#include "util.h" -#include "bench.h" - -typedef struct { - secp256k1_context* ctx; - unsigned char msg[32]; - unsigned char key[32]; -} bench_sign; - -static void bench_sign_setup(void* arg) { - int i; - bench_sign *data = (bench_sign*)arg; - - for (i = 0; i < 32; i++) { - data->msg[i] = i + 1; - } - for (i = 0; i < 32; i++) { - data->key[i] = i + 65; - } -} - -static void bench_sign_run(void* arg) { - int i; - bench_sign *data = (bench_sign*)arg; - - unsigned char sig[74]; - for (i = 0; i < 20000; i++) { - size_t siglen = 74; - int j; - secp256k1_ecdsa_signature signature; - CHECK(secp256k1_ecdsa_sign(data->ctx, &signature, data->msg, data->key, NULL, NULL)); - CHECK(secp256k1_ecdsa_signature_serialize_der(data->ctx, sig, &siglen, &signature)); - for (j = 0; j < 32; j++) { - data->msg[j] = sig[j]; - data->key[j] = sig[j + 32]; - } - } -} - -int main(void) { - bench_sign data; - - data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - - run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, 20000); - - secp256k1_context_destroy(data.ctx); - return 0; -} diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c deleted file mode 100644 index fec74efe6..000000000 --- a/src/secp256k1/src/bench_verify.c +++ /dev/null @@ -1,112 +0,0 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#include -#include - -#include "include/secp256k1.h" -#include "util.h" -#include "bench.h" - -#ifdef ENABLE_OPENSSL_TESTS -#include -#include -#include -#endif - -typedef struct { - secp256k1_context *ctx; - unsigned char msg[32]; - unsigned char key[32]; - unsigned char sig[72]; - size_t siglen; - unsigned char pubkey[33]; - size_t pubkeylen; -#ifdef ENABLE_OPENSSL_TESTS - EC_GROUP* ec_group; -#endif -} benchmark_verify_t; - -static void benchmark_verify(void* arg) { - int i; - benchmark_verify_t* data = (benchmark_verify_t*)arg; - - for (i = 0; i < 20000; i++) { - secp256k1_pubkey pubkey; - secp256k1_ecdsa_signature sig; - data->sig[data->siglen - 1] ^= (i & 0xFF); - data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); - data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); - CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->pubkey, data->pubkeylen) == 1); - CHECK(secp256k1_ecdsa_signature_parse_der(data->ctx, &sig, data->sig, data->siglen) == 1); - CHECK(secp256k1_ecdsa_verify(data->ctx, &sig, data->msg, &pubkey) == (i == 0)); - data->sig[data->siglen - 1] ^= (i & 0xFF); - data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); - data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); - } -} - -#ifdef ENABLE_OPENSSL_TESTS -static void benchmark_verify_openssl(void* arg) { - int i; - benchmark_verify_t* data = (benchmark_verify_t*)arg; - - for (i = 0; i < 20000; i++) { - data->sig[data->siglen - 1] ^= (i & 0xFF); - data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); - data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); - { - EC_KEY *pkey = EC_KEY_new(); - const unsigned char *pubkey = &data->pubkey[0]; - int result; - - CHECK(pkey != NULL); - result = EC_KEY_set_group(pkey, data->ec_group); - CHECK(result); - result = (o2i_ECPublicKey(&pkey, &pubkey, data->pubkeylen)) != NULL; - CHECK(result); - result = ECDSA_verify(0, &data->msg[0], sizeof(data->msg), &data->sig[0], data->siglen, pkey) == (i == 0); - CHECK(result); - EC_KEY_free(pkey); - } - data->sig[data->siglen - 1] ^= (i & 0xFF); - data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); - data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); - } -} -#endif - -int main(void) { - int i; - secp256k1_pubkey pubkey; - secp256k1_ecdsa_signature sig; - benchmark_verify_t data; - - data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - - for (i = 0; i < 32; i++) { - data.msg[i] = 1 + i; - } - for (i = 0; i < 32; i++) { - data.key[i] = 33 + i; - } - data.siglen = 72; - CHECK(secp256k1_ecdsa_sign(data.ctx, &sig, data.msg, data.key, NULL, NULL)); - CHECK(secp256k1_ecdsa_signature_serialize_der(data.ctx, data.sig, &data.siglen, &sig)); - CHECK(secp256k1_ec_pubkey_create(data.ctx, &pubkey, data.key)); - data.pubkeylen = 33; - CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); - - run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000); -#ifdef ENABLE_OPENSSL_TESTS - data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1); - run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000); - EC_GROUP_free(data.ec_group); -#endif - - secp256k1_context_destroy(data.ctx); - return 0; -} diff --git a/src/secp256k1/src/checkmem.h b/src/secp256k1/src/checkmem.h new file mode 100644 index 000000000..7e333ce5f --- /dev/null +++ b/src/secp256k1/src/checkmem.h @@ -0,0 +1,102 @@ +/*********************************************************************** + * Copyright (c) 2022 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +/* The code here is inspired by Kris Kwiatkowski's approach in + * https://github.com/kriskwiatkowski/pqc/blob/main/src/common/ct_check.h + * to provide a general interface for memory-checking mechanisms, primarily + * for constant-time checking. + */ + +/* These macros are defined by this header file: + * + * - SECP256K1_CHECKMEM_ENABLED: + * - 1 if memory-checking integration is available, 0 otherwise. + * This is just a compile-time macro. Use the next macro to check it is actually + * available at runtime. + * - SECP256K1_CHECKMEM_RUNNING(): + * - Acts like a function call, returning 1 if memory checking is available + * at runtime. + * - SECP256K1_CHECKMEM_CHECK(p, len): + * - Assert or otherwise fail in case the len-byte memory block pointed to by p is + * not considered entirely defined. + * - SECP256K1_CHECKMEM_CHECK_VERIFY(p, len): + * - Like SECP256K1_CHECKMEM_CHECK, but only works in VERIFY mode. + * - SECP256K1_CHECKMEM_UNDEFINE(p, len): + * - marks the len-byte memory block pointed to by p as undefined data (secret data, + * in the context of constant-time checking). + * - SECP256K1_CHECKMEM_DEFINE(p, len): + * - marks the len-byte memory pointed to by p as defined data (public data, in the + * context of constant-time checking). + * - SECP256K1_CHECKMEM_MSAN_DEFINE(p, len): + * - Like SECP256K1_CHECKMEM_DEFINE, but applies only to memory_sanitizer. + * + */ + +#ifndef SECP256K1_CHECKMEM_H +#define SECP256K1_CHECKMEM_H + +/* Define a statement-like macro that ignores the arguments. */ +#define SECP256K1_CHECKMEM_NOOP(p, len) do { (void)(p); (void)(len); } while(0) + +/* If compiling under msan, map the SECP256K1_CHECKMEM_* functionality to msan. + * Choose this preferentially, even when VALGRIND is defined, as msan-compiled + * binaries can't be run under valgrind anyway. */ +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# include +# define SECP256K1_CHECKMEM_ENABLED 1 +# define SECP256K1_CHECKMEM_UNDEFINE(p, len) __msan_allocated_memory((p), (len)) +# define SECP256K1_CHECKMEM_DEFINE(p, len) __msan_unpoison((p), (len)) +# define SECP256K1_CHECKMEM_MSAN_DEFINE(p, len) __msan_unpoison((p), (len)) +# define SECP256K1_CHECKMEM_CHECK(p, len) __msan_check_mem_is_initialized((p), (len)) +# define SECP256K1_CHECKMEM_RUNNING() (1) +# endif +#endif + +#if !defined SECP256K1_CHECKMEM_MSAN_DEFINE +# define SECP256K1_CHECKMEM_MSAN_DEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +#endif + +/* If valgrind integration is desired (through the VALGRIND define), implement the + * SECP256K1_CHECKMEM_* macros using valgrind. */ +#if !defined SECP256K1_CHECKMEM_ENABLED +# if defined VALGRIND +# include +# if defined(__clang__) && defined(__APPLE__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreserved-identifier" +# endif +# include +# if defined(__clang__) && defined(__APPLE__) +# pragma clang diagnostic pop +# endif +# define SECP256K1_CHECKMEM_ENABLED 1 +# define SECP256K1_CHECKMEM_UNDEFINE(p, len) VALGRIND_MAKE_MEM_UNDEFINED((p), (len)) +# define SECP256K1_CHECKMEM_DEFINE(p, len) VALGRIND_MAKE_MEM_DEFINED((p), (len)) +# define SECP256K1_CHECKMEM_CHECK(p, len) VALGRIND_CHECK_MEM_IS_DEFINED((p), (len)) + /* VALGRIND_MAKE_MEM_DEFINED returns 0 iff not running on memcheck. + * This is more precise than the RUNNING_ON_VALGRIND macro, which + * checks for valgrind in general instead of memcheck specifically. */ +# define SECP256K1_CHECKMEM_RUNNING() (VALGRIND_MAKE_MEM_DEFINED(NULL, 0) != 0) +# endif +#endif + +/* As a fall-back, map these macros to dummy statements. */ +#if !defined SECP256K1_CHECKMEM_ENABLED +# define SECP256K1_CHECKMEM_ENABLED 0 +# define SECP256K1_CHECKMEM_UNDEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +# define SECP256K1_CHECKMEM_DEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +# define SECP256K1_CHECKMEM_CHECK(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +# define SECP256K1_CHECKMEM_RUNNING() (0) +#endif + +#if defined VERIFY +#define SECP256K1_CHECKMEM_CHECK_VERIFY(p, len) SECP256K1_CHECKMEM_CHECK((p), (len)) +#else +#define SECP256K1_CHECKMEM_CHECK_VERIFY(p, len) SECP256K1_CHECKMEM_NOOP((p), (len)) +#endif + +#endif /* SECP256K1_CHECKMEM_H */ diff --git a/src/secp256k1/src/ctime_tests.c b/src/secp256k1/src/ctime_tests.c new file mode 100644 index 000000000..a384e8315 --- /dev/null +++ b/src/secp256k1/src/ctime_tests.c @@ -0,0 +1,209 @@ +/*********************************************************************** + * Copyright (c) 2020 Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#include + +#include "../include/secp256k1.h" +#include "assumptions.h" +#include "checkmem.h" + +#if !SECP256K1_CHECKMEM_ENABLED +# error "This tool cannot be compiled without memory-checking interface (valgrind or msan)" +#endif + +#ifdef ENABLE_MODULE_ECDH +# include "../include/secp256k1_ecdh.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "../include/secp256k1_recovery.h" +#endif + +#ifdef ENABLE_MODULE_EXTRAKEYS +# include "../include/secp256k1_extrakeys.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG +#include "../include/secp256k1_schnorrsig.h" +#endif + +#ifdef ENABLE_MODULE_ELLSWIFT +#include "../include/secp256k1_ellswift.h" +#endif + +static void run_tests(secp256k1_context *ctx, unsigned char *key); + +int main(void) { + secp256k1_context* ctx; + unsigned char key[32]; + int ret, i; + + if (!SECP256K1_CHECKMEM_RUNNING()) { + fprintf(stderr, "This test can only usefully be run inside valgrind because it was not compiled under msan.\n"); + fprintf(stderr, "Usage: libtool --mode=execute valgrind ./ctime_tests\n"); + return 1; + } + ctx = secp256k1_context_create(SECP256K1_CONTEXT_DECLASSIFY); + /** In theory, testing with a single secret input should be sufficient: + * If control flow depended on secrets the tool would generate an error. + */ + for (i = 0; i < 32; i++) { + key[i] = i + 65; + } + + run_tests(ctx, key); + + /* Test context randomisation. Do this last because it leaves the context + * tainted. */ + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_context_randomize(ctx, key); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret); + + secp256k1_context_destroy(ctx); + return 0; +} + +static void run_tests(secp256k1_context *ctx, unsigned char *key) { + secp256k1_ecdsa_signature signature; + secp256k1_pubkey pubkey; + size_t siglen = 74; + size_t outputlen = 33; + int i; + int ret; + unsigned char msg[32]; + unsigned char sig[74]; + unsigned char spubkey[33]; +#ifdef ENABLE_MODULE_RECOVERY + secp256k1_ecdsa_recoverable_signature recoverable_signature; + int recid; +#endif +#ifdef ENABLE_MODULE_EXTRAKEYS + secp256k1_keypair keypair; +#endif +#ifdef ENABLE_MODULE_ELLSWIFT + unsigned char ellswift[64]; + static const unsigned char prefix[64] = {'t', 'e', 's', 't'}; +#endif + + for (i = 0; i < 32; i++) { + msg[i] = i + 1; + } + + /* Test keygen. */ + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key); + SECP256K1_CHECKMEM_DEFINE(&pubkey, sizeof(secp256k1_pubkey)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret); + CHECK(secp256k1_ec_pubkey_serialize(ctx, spubkey, &outputlen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + + /* Test signing. */ + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ecdsa_sign(ctx, &signature, msg, key, NULL, NULL); + SECP256K1_CHECKMEM_DEFINE(&signature, sizeof(secp256k1_ecdsa_signature)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature)); + +#ifdef ENABLE_MODULE_ECDH + /* Test ECDH. */ + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); +#endif + +#ifdef ENABLE_MODULE_RECOVERY + /* Test signing a recoverable signature. */ + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ecdsa_sign_recoverable(ctx, &recoverable_signature, msg, key, NULL, NULL); + SECP256K1_CHECKMEM_DEFINE(&recoverable_signature, sizeof(recoverable_signature)); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &recoverable_signature)); + CHECK(recid >= 0 && recid <= 3); +#endif + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ec_seckey_verify(ctx, key); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ec_seckey_negate(ctx, key); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(msg, 32); + ret = secp256k1_ec_seckey_tweak_add(ctx, key, msg); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(msg, 32); + ret = secp256k1_ec_seckey_tweak_mul(ctx, key, msg); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + /* Test keypair_create and keypair_xonly_tweak_add. */ +#ifdef ENABLE_MODULE_EXTRAKEYS + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_keypair_create(ctx, &keypair, key); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + /* The tweak is not treated as a secret in keypair_tweak_add */ + SECP256K1_CHECKMEM_DEFINE(msg, 32); + ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_UNDEFINE(&keypair, sizeof(keypair)); + ret = secp256k1_keypair_sec(ctx, key, &keypair); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_keypair_create(ctx, &keypair, key); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + ret = secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); +#endif + +#ifdef ENABLE_MODULE_ELLSWIFT + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ellswift_create(ctx, ellswift, key, NULL); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + ret = secp256k1_ellswift_create(ctx, ellswift, key, ellswift); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + for (i = 0; i < 2; i++) { + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_DEFINE(&ellswift, sizeof(ellswift)); + ret = secp256k1_ellswift_xdh(ctx, msg, ellswift, ellswift, key, i, secp256k1_ellswift_xdh_hash_function_bip324, NULL); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + + SECP256K1_CHECKMEM_UNDEFINE(key, 32); + SECP256K1_CHECKMEM_DEFINE(&ellswift, sizeof(ellswift)); + ret = secp256k1_ellswift_xdh(ctx, msg, ellswift, ellswift, key, i, secp256k1_ellswift_xdh_hash_function_prefix, (void *)prefix); + SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret)); + CHECK(ret == 1); + } + +#endif +} diff --git a/src/secp256k1/src/ecdsa.h b/src/secp256k1/src/ecdsa.h index 20a068970..4441b0839 100644 --- a/src/secp256k1/src/ecdsa.h +++ b/src/secp256k1/src/ecdsa.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECDSA_H #define SECP256K1_ECDSA_H @@ -15,7 +15,7 @@ static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *r, secp256k1_scalar *s, const unsigned char *sig, size_t size); static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar *r, const secp256k1_scalar *s); -static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); +static int secp256k1_ecdsa_sig_verify(const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid); #endif /* SECP256K1_ECDSA_H */ diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h index d7ce5feb9..ce36e85e6 100644 --- a/src/secp256k1/src/ecdsa_impl.h +++ b/src/secp256k1/src/ecdsa_impl.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2013-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECDSA_IMPL_H @@ -16,17 +16,8 @@ #include "ecdsa.h" /** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1 - * sage: for t in xrange(1023, -1, -1): - * .. p = 2**256 - 2**32 - t - * .. if p.is_prime(): - * .. print '%x'%p - * .. break - * 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' - * sage: a = 0 - * sage: b = 7 - * sage: F = FiniteField (p) - * sage: '%x' % (EllipticCurve ([F (a), F (b)]).order()) - * 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' + * $ sage -c 'load("secp256k1_params.sage"); print(hex(N))' + * 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 */ static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, @@ -35,79 +26,81 @@ static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST /** Difference between field and order, values 'p' and 'n' values defined in * "Standards for Efficient Cryptography" (SEC2) 2.7.1. - * sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F - * sage: a = 0 - * sage: b = 7 - * sage: F = FiniteField (p) - * sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order()) - * '14551231950b75fc4402da1722fc9baee' + * $ sage -c 'load("secp256k1_params.sage"); print(hex(P-N))' + * 0x14551231950b75fc4402da1722fc9baee */ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL ); -static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) { - int lenleft, b1; - size_t ret = 0; +static int secp256k1_der_read_len(size_t *len, const unsigned char **sigp, const unsigned char *sigend) { + size_t lenleft; + unsigned char b1; + VERIFY_CHECK(len != NULL); + *len = 0; if (*sigp >= sigend) { - return -1; + return 0; } b1 = *((*sigp)++); if (b1 == 0xFF) { /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ - return -1; + return 0; } if ((b1 & 0x80) == 0) { /* X.690-0207 8.1.3.4 short form length octets */ - return b1; + *len = b1; + return 1; } if (b1 == 0x80) { /* Indefinite length is not allowed in DER. */ - return -1; + return 0; } /* X.690-207 8.1.3.5 long form length octets */ - lenleft = b1 & 0x7F; - if (lenleft > sigend - *sigp) { - return -1; + lenleft = b1 & 0x7F; /* lenleft is at least 1 */ + if (lenleft > (size_t)(sigend - *sigp)) { + return 0; } if (**sigp == 0) { /* Not the shortest possible length encoding. */ - return -1; + return 0; } - if ((size_t)lenleft > sizeof(size_t)) { + if (lenleft > sizeof(size_t)) { /* The resulting length would exceed the range of a size_t, so - * certainly longer than the passed array size. - */ - return -1; + * it is certainly longer than the passed array size. */ + return 0; } while (lenleft > 0) { - ret = (ret << 8) | **sigp; - if (ret + lenleft > (size_t)(sigend - *sigp)) { - /* Result exceeds the length of the passed array. */ - return -1; - } + *len = (*len << 8) | **sigp; (*sigp)++; lenleft--; } - if (ret < 128) { + if (*len > (size_t)(sigend - *sigp)) { + /* Result exceeds the length of the passed array. + (Checking this is the responsibility of the caller but it + can't hurt do it here, too.) */ + return 0; + } + if (*len < 128) { /* Not the shortest possible length encoding. */ - return -1; + return 0; } - return ret; + return 1; } static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) { int overflow = 0; unsigned char ra[32] = {0}; - int rlen; + size_t rlen; if (*sig == sigend || **sig != 0x02) { /* Not a primitive integer (X.690-0207 8.3.1). */ return 0; } (*sig)++; - rlen = secp256k1_der_read_len(sig, sigend); - if (rlen <= 0 || (*sig) + rlen > sigend) { + if (secp256k1_der_read_len(&rlen, sig, sigend) == 0) { + return 0; + } + if (rlen == 0 || rlen > (size_t)(sigend - *sig)) { /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ return 0; } @@ -123,8 +116,11 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char /* Negative. */ overflow = 1; } - while (rlen > 0 && **sig == 0) { - /* Skip leading zero bytes */ + /* There is at most one leading zero byte: + * if there were two leading zero bytes, we would have failed and returned 0 + * because of excessive 0x00 padding already. */ + if (rlen > 0 && **sig == 0) { + /* Skip leading zero byte */ rlen--; (*sig)++; } @@ -132,7 +128,7 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char overflow = 1; } if (!overflow) { - memcpy(ra + 32 - rlen, *sig, rlen); + if (rlen) memcpy(ra + 32 - rlen, *sig, rlen); secp256k1_scalar_set_b32(r, ra, &overflow); } if (overflow) { @@ -144,18 +140,16 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) { const unsigned char *sigend = sig + size; - int rlen; + size_t rlen; if (sig == sigend || *(sig++) != 0x30) { /* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */ return 0; } - rlen = secp256k1_der_read_len(&sig, sigend); - if (rlen < 0 || sig + rlen > sigend) { - /* Tuple exceeds bounds */ + if (secp256k1_der_read_len(&rlen, &sig, sigend) == 0) { return 0; } - if (sig + rlen != sigend) { - /* Garbage after tuple. */ + if (rlen != (size_t)(sigend - sig)) { + /* Tuple exceeds bounds or garage after tuple. */ return 0; } @@ -198,7 +192,7 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const return 1; } -static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) { +static int secp256k1_ecdsa_sig_verify(const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) { unsigned char c[32]; secp256k1_scalar sn, u1, u2; #if !defined(EXHAUSTIVE_TEST_ORDER) @@ -215,7 +209,7 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar_mul(&u1, &sn, message); secp256k1_scalar_mul(&u2, &sn, sigr); secp256k1_gej_set_ge(&pubkeyj, pubkey); - secp256k1_ecmult(ctx, &pr, &pubkeyj, &u2, &u1); + secp256k1_ecmult(&pr, &pubkeyj, &u2, &u1); if (secp256k1_gej_is_infinity(&pr)) { return 0; } @@ -233,7 +227,8 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const } #else secp256k1_scalar_get_b32(c, sigr); - secp256k1_fe_set_b32(&xr, c); + /* we can ignore the fe_set_b32_limit return value, because we know the input is in range */ + (void)secp256k1_fe_set_b32_limit(&xr, c); /** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n) * in xr. Naively, we would extract the x coordinate from pr (requiring a inversion modulo p), @@ -274,6 +269,7 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec secp256k1_ge r; secp256k1_scalar n; int overflow = 0; + int high; secp256k1_ecmult_gen(ctx, &rp, nonce); secp256k1_ge_set_gej(&r, &rp); @@ -281,15 +277,11 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec secp256k1_fe_normalize(&r.y); secp256k1_fe_get_b32(b, &r.x); secp256k1_scalar_set_b32(sigr, b, &overflow); - /* These two conditions should be checked before calling */ - VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr)); - VERIFY_CHECK(overflow == 0); - if (recid) { /* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log * of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria. */ - *recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0); + *recid = (overflow << 1) | secp256k1_fe_is_odd(&r.y); } secp256k1_scalar_mul(&n, sigr, seckey); secp256k1_scalar_add(&n, &n, message); @@ -298,16 +290,15 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec secp256k1_scalar_clear(&n); secp256k1_gej_clear(&rp); secp256k1_ge_clear(&r); - if (secp256k1_scalar_is_zero(sigs)) { - return 0; - } - if (secp256k1_scalar_is_high(sigs)) { - secp256k1_scalar_negate(sigs, sigs); - if (recid) { - *recid ^= 1; - } + high = secp256k1_scalar_is_high(sigs); + secp256k1_scalar_cond_negate(sigs, high); + if (recid) { + *recid ^= high; } - return 1; + /* P.x = order is on the curve, so technically sig->r could end up being zero, which would be an invalid signature. + * This is cryptographically unreachable as hitting it requires finding the discrete log of P.x = N. + */ + return (int)(!secp256k1_scalar_is_zero(sigr)) & (int)(!secp256k1_scalar_is_zero(sigs)); } #endif /* SECP256K1_ECDSA_IMPL_H */ diff --git a/src/secp256k1/src/eckey.h b/src/secp256k1/src/eckey.h index 85cbbdc03..d54d44c99 100644 --- a/src/secp256k1/src/eckey.h +++ b/src/secp256k1/src/eckey.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECKEY_H #define SECP256K1_ECKEY_H @@ -18,8 +18,8 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed); static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak); -static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge *key, const secp256k1_scalar *tweak); static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); -static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_pubkey_tweak_mul(secp256k1_ge *key, const secp256k1_scalar *tweak); #endif /* SECP256K1_ECKEY_H */ diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h index 9fb910e4c..121966f8b 100644 --- a/src/secp256k1/src/eckey_impl.h +++ b/src/secp256k1/src/eckey_impl.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECKEY_IMPL_H #define SECP256K1_ECKEY_IMPL_H @@ -17,10 +17,10 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) { secp256k1_fe x; - return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); - } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) { + return secp256k1_fe_set_b32_limit(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); + } else if (size == 65 && (pub[0] == SECP256K1_TAG_PUBKEY_UNCOMPRESSED || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { secp256k1_fe x, y; - if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { + if (!secp256k1_fe_set_b32_limit(&x, pub+1) || !secp256k1_fe_set_b32_limit(&y, pub+33)) { return 0; } secp256k1_ge_set_xy(elem, &x, &y); @@ -54,18 +54,13 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) { secp256k1_scalar_add(key, key, tweak); - if (secp256k1_scalar_is_zero(key)) { - return 0; - } - return 1; + return !secp256k1_scalar_is_zero(key); } -static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { +static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge *key, const secp256k1_scalar *tweak) { secp256k1_gej pt; - secp256k1_scalar one; secp256k1_gej_set_ge(&pt, key); - secp256k1_scalar_set_int(&one, 1); - secp256k1_ecmult(ctx, &pt, &pt, &one, tweak); + secp256k1_ecmult(&pt, &pt, &secp256k1_scalar_one, tweak); if (secp256k1_gej_is_infinity(&pt)) { return 0; @@ -75,24 +70,21 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, } static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) { - if (secp256k1_scalar_is_zero(tweak)) { - return 0; - } + int ret; + ret = !secp256k1_scalar_is_zero(tweak); secp256k1_scalar_mul(key, key, tweak); - return 1; + return ret; } -static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { - secp256k1_scalar zero; +static int secp256k1_eckey_pubkey_tweak_mul(secp256k1_ge *key, const secp256k1_scalar *tweak) { secp256k1_gej pt; if (secp256k1_scalar_is_zero(tweak)) { return 0; } - secp256k1_scalar_set_int(&zero, 0); secp256k1_gej_set_ge(&pt, key); - secp256k1_ecmult(ctx, &pt, &pt, tweak, &zero); + secp256k1_ecmult(&pt, &pt, tweak, &secp256k1_scalar_zero); secp256k1_ge_set_gej(key, &pt); return 1; } diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h index ac3e344b2..326a5eeb4 100644 --- a/src/secp256k1/src/ecmult.h +++ b/src/secp256k1/src/ecmult.h @@ -1,31 +1,61 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECMULT_H #define SECP256K1_ECMULT_H -#include "num.h" #include "group.h" +#include "scalar.h" +#include "scratch.h" -typedef struct { - /* For accelerating the computation of a*P + b*G: */ - secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */ -#ifdef USE_ENDOMORPHISM - secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */ +#ifndef ECMULT_WINDOW_SIZE +# define ECMULT_WINDOW_SIZE 15 +# ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_MSG("ECMULT_WINDOW_SIZE undefined, assuming default value") +# endif #endif -} secp256k1_ecmult_context; -static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx); -static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb); -static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, - const secp256k1_ecmult_context *src, const secp256k1_callback *cb); -static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx); -static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx); +#ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_DEF(ECMULT_WINDOW_SIZE) +#endif + +/* No one will ever need more than a window size of 24. The code might + * be correct for larger values of ECMULT_WINDOW_SIZE but this is not + * tested. + * + * The following limitations are known, and there are probably more: + * If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect + * because the size of the memory object that we allocate (in bytes) + * will not fit in a size_t. + * If WINDOW_G > 31 and int has 32 bits, then the code is incorrect + * because certain expressions will overflow. + */ +#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24 +# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24]. +#endif + +/** The number of entries a table with precomputed multiples needs to have. */ +#define ECMULT_TABLE_SIZE(w) (1L << ((w)-2)) /** Double multiply: R = na*A + ng*G */ -static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); +static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); + +typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data); + +/** + * Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai. + * Chooses the right algorithm for a given number of points and scratch space + * size. Resets and overwrites the given scratch space. If the points do not + * fit in the scratch space the algorithm is repeatedly run with batches of + * points. If no scratch space is given then a simple algorithm is used that + * simply multiplies the points with the corresponding scalars and adds them up. + * Returns: 1 on success (including when inp_g_sc is NULL and n is 0) + * 0 if there is not enough scratch space for a single point or + * callback returns 0 + */ +static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n); #endif /* SECP256K1_ECMULT_H */ diff --git a/src/secp256k1/src/ecmult_compute_table.h b/src/secp256k1/src/ecmult_compute_table.h new file mode 100644 index 000000000..665f87ff3 --- /dev/null +++ b/src/secp256k1/src/ecmult_compute_table.h @@ -0,0 +1,16 @@ +/***************************************************************************************************** + * Copyright (c) 2013, 2014, 2017, 2021 Pieter Wuille, Andrew Poelstra, Jonas Nick, Russell O'Connor * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php. * + *****************************************************************************************************/ + +#ifndef SECP256K1_ECMULT_COMPUTE_TABLE_H +#define SECP256K1_ECMULT_COMPUTE_TABLE_H + +/* Construct table of all odd multiples of gen in range 1..(2**(window_g-1)-1). */ +static void secp256k1_ecmult_compute_table(secp256k1_ge_storage* table, int window_g, const secp256k1_gej* gen); + +/* Like secp256k1_ecmult_compute_table, but one for both gen and gen*2^128. */ +static void secp256k1_ecmult_compute_two_tables(secp256k1_ge_storage* table, secp256k1_ge_storage* table_128, int window_g, const secp256k1_ge* gen); + +#endif /* SECP256K1_ECMULT_COMPUTE_TABLE_H */ diff --git a/src/secp256k1/src/ecmult_compute_table_impl.h b/src/secp256k1/src/ecmult_compute_table_impl.h new file mode 100644 index 000000000..69d59ce59 --- /dev/null +++ b/src/secp256k1/src/ecmult_compute_table_impl.h @@ -0,0 +1,49 @@ +/***************************************************************************************************** + * Copyright (c) 2013, 2014, 2017, 2021 Pieter Wuille, Andrew Poelstra, Jonas Nick, Russell O'Connor * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php. * + *****************************************************************************************************/ + +#ifndef SECP256K1_ECMULT_COMPUTE_TABLE_IMPL_H +#define SECP256K1_ECMULT_COMPUTE_TABLE_IMPL_H + +#include "ecmult_compute_table.h" +#include "group_impl.h" +#include "field_impl.h" +#include "ecmult.h" +#include "util.h" + +static void secp256k1_ecmult_compute_table(secp256k1_ge_storage* table, int window_g, const secp256k1_gej* gen) { + secp256k1_gej gj; + secp256k1_ge ge, dgen; + int j; + + gj = *gen; + secp256k1_ge_set_gej_var(&ge, &gj); + secp256k1_ge_to_storage(&table[0], &ge); + + secp256k1_gej_double_var(&gj, gen, NULL); + secp256k1_ge_set_gej_var(&dgen, &gj); + + for (j = 1; j < ECMULT_TABLE_SIZE(window_g); ++j) { + secp256k1_gej_set_ge(&gj, &ge); + secp256k1_gej_add_ge_var(&gj, &gj, &dgen, NULL); + secp256k1_ge_set_gej_var(&ge, &gj); + secp256k1_ge_to_storage(&table[j], &ge); + } +} + +/* Like secp256k1_ecmult_compute_table, but one for both gen and gen*2^128. */ +static void secp256k1_ecmult_compute_two_tables(secp256k1_ge_storage* table, secp256k1_ge_storage* table_128, int window_g, const secp256k1_ge* gen) { + secp256k1_gej gj; + int i; + + secp256k1_gej_set_ge(&gj, gen); + secp256k1_ecmult_compute_table(table, window_g, &gj); + for (i = 0; i < 128; ++i) { + secp256k1_gej_double_var(&gj, &gj, NULL); + } + secp256k1_ecmult_compute_table(table_128, window_g, &gj); +} + +#endif /* SECP256K1_ECMULT_COMPUTE_TABLE_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h index 9e4e8a07a..080e04bc8 100644 --- a/src/secp256k1/src/ecmult_const.h +++ b/src/secp256k1/src/ecmult_const.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2015 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECMULT_CONST_H #define SECP256K1_ECMULT_CONST_H @@ -10,6 +10,29 @@ #include "scalar.h" #include "group.h" +/** + * Multiply: R = q*A (in constant-time for q) + */ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); +/** + * Same as secp256k1_ecmult_const, but takes in an x coordinate of the base point + * only, specified as fraction n/d (numerator/denominator). Only the x coordinate of the result is + * returned. + * + * If known_on_curve is 0, a verification is performed that n/d is a valid X + * coordinate, and 0 is returned if not. Otherwise, 1 is returned. + * + * d being NULL is interpreted as d=1. If non-NULL, d must not be zero. q must not be zero. + * + * Constant time in the value of q, but not any other inputs. + */ +static int secp256k1_ecmult_const_xonly( + secp256k1_fe *r, + const secp256k1_fe *n, + const secp256k1_fe *d, + const secp256k1_scalar *q, + int known_on_curve +); + #endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h index fd71d6b24..0d78f7c3c 100644 --- a/src/secp256k1/src/ecmult_const_impl.h +++ b/src/secp256k1/src/ecmult_const_impl.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015, 2022 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECMULT_CONST_IMPL_H #define SECP256K1_ECMULT_CONST_IMPL_H @@ -12,229 +12,388 @@ #include "ecmult_const.h" #include "ecmult_impl.h" -#ifdef USE_ENDOMORPHISM - #define WNAF_BITS 128 +#if defined(EXHAUSTIVE_TEST_ORDER) +/* We need 2^ECMULT_CONST_GROUP_SIZE - 1 to be less than EXHAUSTIVE_TEST_ORDER, because + * the tables cannot have infinities in them (this breaks the effective-affine technique's + * z-ratio tracking) */ +# if EXHAUSTIVE_TEST_ORDER == 199 +# define ECMULT_CONST_GROUP_SIZE 4 +# elif EXHAUSTIVE_TEST_ORDER == 13 +# define ECMULT_CONST_GROUP_SIZE 3 +# elif EXHAUSTIVE_TEST_ORDER == 7 +# define ECMULT_CONST_GROUP_SIZE 2 +# else +# error "Unknown EXHAUSTIVE_TEST_ORDER" +# endif #else - #define WNAF_BITS 256 +/* Group size 4 or 5 appears optimal. */ +# define ECMULT_CONST_GROUP_SIZE 5 #endif -#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w)) -/* This is like `ECMULT_TABLE_GET_GE` but is constant time */ -#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ - int m; \ - int abs_n = (n) * (((n) > 0) * 2 - 1); \ - int idx_n = abs_n / 2; \ +#define ECMULT_CONST_TABLE_SIZE (1L << (ECMULT_CONST_GROUP_SIZE - 1)) +#define ECMULT_CONST_GROUPS ((129 + ECMULT_CONST_GROUP_SIZE - 1) / ECMULT_CONST_GROUP_SIZE) +#define ECMULT_CONST_BITS (ECMULT_CONST_GROUPS * ECMULT_CONST_GROUP_SIZE) + +/** Fill a table 'pre' with precomputed odd multiples of a. + * + * The resulting point set is brought to a single constant Z denominator, stores the X and Y + * coordinates as ge points in pre, and stores the global Z in globalz. + * + * 'pre' must be an array of size ECMULT_CONST_TABLE_SIZE. + */ +static void secp256k1_ecmult_const_odd_multiples_table_globalz(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) { + secp256k1_fe zr[ECMULT_CONST_TABLE_SIZE]; + + secp256k1_ecmult_odd_multiples_table(ECMULT_CONST_TABLE_SIZE, pre, zr, globalz, a); + secp256k1_ge_table_set_globalz(ECMULT_CONST_TABLE_SIZE, pre, zr); +} + +/* Given a table 'pre' with odd multiples of a point, put in r the signed-bit multiplication of n with that point. + * + * For example, if ECMULT_CONST_GROUP_SIZE is 4, then pre is expected to contain 8 entries: + * [1*P, 3*P, 5*P, 7*P, 9*P, 11*P, 13*P, 15*P]. n is then expected to be a 4-bit integer (range 0-15), and its + * bits are interpreted as signs of powers of two to look up. + * + * For example, if n=4, which is 0100 in binary, which is interpreted as [- + - -], so the looked up value is + * [ -(2^3) + (2^2) - (2^1) - (2^0) ]*P = -7*P. Every valid n translates to an odd number in range [-15,15], + * which means we just need to look up one of the precomputed values, and optionally negate it. + */ +#define ECMULT_CONST_TABLE_GET_GE(r,pre,n) do { \ + unsigned int m = 0; \ + /* If the top bit of n is 0, we want the negation. */ \ + volatile unsigned int negative = ((n) >> (ECMULT_CONST_GROUP_SIZE - 1)) ^ 1; \ + /* Let n[i] be the i-th bit of n, then the index is + * sum(cnot(n[i]) * 2^i, i=0..l-2) + * where cnot(b) = b if n[l-1] = 1 and 1 - b otherwise. + * For example, if n = 4, in binary 0100, the index is 3, in binary 011. + * + * Proof: + * Let + * x = sum((2*n[i] - 1)*2^i, i=0..l-1) + * = 2*sum(n[i] * 2^i, i=0..l-1) - 2^l + 1 + * be the value represented by n. + * The index is (x - 1)/2 if x > 0 and -(x + 1)/2 otherwise. + * Case x > 0: + * n[l-1] = 1 + * index = sum(n[i] * 2^i, i=0..l-1) - 2^(l-1) + * = sum(n[i] * 2^i, i=0..l-2) + * Case x <= 0: + * n[l-1] = 0 + * index = -(2*sum(n[i] * 2^i, i=0..l-1) - 2^l + 2)/2 + * = 2^(l-1) - 1 - sum(n[i] * 2^i, i=0..l-1) + * = sum((1 - n[i]) * 2^i, i=0..l-2) + */ \ + unsigned int index = ((unsigned int)(-negative) ^ n) & ((1U << (ECMULT_CONST_GROUP_SIZE - 1)) - 1U); \ secp256k1_fe neg_y; \ - VERIFY_CHECK(((n) & 1) == 1); \ - VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ - VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ - VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \ - VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \ - for (m = 0; m < ECMULT_TABLE_SIZE(w); m++) { \ + VERIFY_CHECK((n) < (1U << ECMULT_CONST_GROUP_SIZE)); \ + VERIFY_CHECK(index < (1U << (ECMULT_CONST_GROUP_SIZE - 1))); \ + /* Unconditionally set r->x = (pre)[m].x. r->y = (pre)[m].y. because it's either the correct one + * or will get replaced in the later iterations, this is needed to make sure `r` is initialized. */ \ + (r)->x = (pre)[m].x; \ + (r)->y = (pre)[m].y; \ + for (m = 1; m < ECMULT_CONST_TABLE_SIZE; m++) { \ /* This loop is used to avoid secret data in array indices. See * the comment in ecmult_gen_impl.h for rationale. */ \ - secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \ - secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == idx_n); \ + secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == index); \ + secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == index); \ } \ (r)->infinity = 0; \ secp256k1_fe_negate(&neg_y, &(r)->y, 1); \ - secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \ + secp256k1_fe_cmov(&(r)->y, &neg_y, negative); \ } while(0) +/* For K as defined in the comment of secp256k1_ecmult_const, we have several precomputed + * formulas/constants. + * - in exhaustive test mode, we give an explicit expression to compute it at compile time: */ +#ifdef EXHAUSTIVE_TEST_ORDER +static const secp256k1_scalar secp256k1_ecmult_const_K = ((SECP256K1_SCALAR_CONST(0, 0, 0, (1U << (ECMULT_CONST_BITS - 128)) - 2U, 0, 0, 0, 0) + EXHAUSTIVE_TEST_ORDER - 1U) * (1U + EXHAUSTIVE_TEST_LAMBDA)) % EXHAUSTIVE_TEST_ORDER; +/* - for the real secp256k1 group we have constants for various ECMULT_CONST_BITS values. */ +#elif ECMULT_CONST_BITS == 129 +/* For GROUP_SIZE = 1,3. */ +static const secp256k1_scalar secp256k1_ecmult_const_K = SECP256K1_SCALAR_CONST(0xac9c52b3ul, 0x3fa3cf1ful, 0x5ad9e3fdul, 0x77ed9ba4ul, 0xa880b9fcul, 0x8ec739c2ul, 0xe0cfc810ul, 0xb51283ceul); +#elif ECMULT_CONST_BITS == 130 +/* For GROUP_SIZE = 2,5. */ +static const secp256k1_scalar secp256k1_ecmult_const_K = SECP256K1_SCALAR_CONST(0xa4e88a7dul, 0xcb13034eul, 0xc2bdd6bful, 0x7c118d6bul, 0x589ae848ul, 0x26ba29e4ul, 0xb5c2c1dcul, 0xde9798d9ul); +#elif ECMULT_CONST_BITS == 132 +/* For GROUP_SIZE = 4,6 */ +static const secp256k1_scalar secp256k1_ecmult_const_K = SECP256K1_SCALAR_CONST(0x76b1d93dul, 0x0fae3c6bul, 0x3215874bul, 0x94e93813ul, 0x7937fe0dul, 0xb66bcaaful, 0xb3749ca5ul, 0xd7b6171bul); +#else +# error "Unknown ECMULT_CONST_BITS" +#endif -/** Convert a number to WNAF notation. - * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. - * It has the following guarantees: - * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) - * - each wnaf[i] is nonzero - * - the number of words set is always WNAF_SIZE(w) + 1 - * - * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar - * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) - * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlagy Berlin Heidelberg 2003 - * - * Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335 - */ -static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { - int global_sign; - int skew = 0; - int word = 0; - - /* 1 2 3 */ - int u_last; - int u; - - int flip; - int bit; - secp256k1_scalar neg_s; - int not_neg_one; - /* Note that we cannot handle even numbers by negating them to be odd, as is - * done in other implementations, since if our scalars were specified to have - * width < 256 for performance reasons, their negations would have width 256 - * and we'd lose any performance benefit. Instead, we use a technique from - * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even) - * or 2 (for odd) to the number we are encoding, returning a skew value indicating - * this, and having the caller compensate after doing the multiplication. */ - - /* Negative numbers will be negated to keep their bit representation below the maximum width */ - flip = secp256k1_scalar_is_high(&s); - /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ - bit = flip ^ !secp256k1_scalar_is_even(&s); - /* We check for negative one, since adding 2 to it will cause an overflow */ - secp256k1_scalar_negate(&neg_s, &s); - not_neg_one = !secp256k1_scalar_is_one(&neg_s); - secp256k1_scalar_cadd_bit(&s, bit, not_neg_one); - /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects - * that we added two to it and flipped it. In fact for -1 these operations are - * identical. We only flipped, but since skewing is required (in the sense that - * the skew must be 1 or 2, never zero) and flipping is not, we need to change - * our flags to claim that we only skewed. */ - global_sign = secp256k1_scalar_cond_negate(&s, flip); - global_sign *= not_neg_one * 2 - 1; - skew = 1 << bit; - - /* 4 */ - u_last = secp256k1_scalar_shr_int(&s, w); - while (word * w < WNAF_BITS) { - int sign; - int even; - - /* 4.1 4.4 */ - u = secp256k1_scalar_shr_int(&s, w); - /* 4.2 */ - even = ((u & 1) == 0); - sign = 2 * (u_last > 0) - 1; - u += sign * even; - u_last -= sign * even * (1 << w); - - /* 4.3, adapted for global sign change */ - wnaf[word++] = u_last * global_sign; - - u_last = u; - } - wnaf[word] = u * global_sign; - - VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); - VERIFY_CHECK(word == WNAF_SIZE(w)); - return skew; -} - +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q) { + /* The approach below combines the signed-digit logic from Mike Hamburg's + * "Fast and compact elliptic-curve cryptography" (https://eprint.iacr.org/2012/309) + * Section 3.3, with the GLV endomorphism. + * + * The idea there is to interpret the bits of a scalar as signs (1 = +, 0 = -), and compute a + * point multiplication in that fashion. Let v be an n-bit non-negative integer (0 <= v < 2^n), + * and v[i] its i'th bit (so v = sum(v[i] * 2^i, i=0..n-1)). Then define: + * + * C_l(v, A) = sum((2*v[i] - 1) * 2^i*A, i=0..l-1) + * + * Then it holds that C_l(v, A) = sum((2*v[i] - 1) * 2^i*A, i=0..l-1) + * = (2*sum(v[i] * 2^i, i=0..l-1) + 1 - 2^l) * A + * = (2*v + 1 - 2^l) * A + * + * Thus, one can compute q*A as C_256((q + 2^256 - 1) / 2, A). This is the basis for the + * paper's signed-digit multi-comb algorithm for multiplication using a precomputed table. + * + * It is appealing to try to combine this with the GLV optimization: the idea that a scalar + * s can be written as s1 + lambda*s2, where lambda is a curve-specific constant such that + * lambda*A is easy to compute, and where s1 and s2 are small. In particular we have the + * secp256k1_scalar_split_lambda function which performs such a split with the resulting s1 + * and s2 in range (-2^128, 2^128) mod n. This does work, but is uninteresting: + * + * To compute q*A: + * - Let s1, s2 = split_lambda(q) + * - Let R1 = C_256((s1 + 2^256 - 1) / 2, A) + * - Let R2 = C_256((s2 + 2^256 - 1) / 2, lambda*A) + * - Return R1 + R2 + * + * The issue is that while s1 and s2 are small-range numbers, (s1 + 2^256 - 1) / 2 (mod n) + * and (s2 + 2^256 - 1) / 2 (mod n) are not, undoing the benefit of the splitting. + * + * To make it work, we want to modify the input scalar q first, before splitting, and then only + * add a 2^128 offset of the split results (so that they end up in the single 129-bit range + * [0,2^129]). A slightly smaller offset would work due to the bounds on the split, but we pick + * 2^128 for simplicity. Let s be the scalar fed to split_lambda, and f(q) the function to + * compute it from q: + * + * To compute q*A: + * - Compute s = f(q) + * - Let s1, s2 = split_lambda(s) + * - Let v1 = s1 + 2^128 (mod n) + * - Let v2 = s2 + 2^128 (mod n) + * - Let R1 = C_l(v1, A) + * - Let R2 = C_l(v2, lambda*A) + * - Return R1 + R2 + * + * l will thus need to be at least 129, but we may overshoot by a few bits (see + * further), so keep it as a variable. + * + * To solve for s, we reason: + * q*A = R1 + R2 + * <=> q*A = C_l(s1 + 2^128, A) + C_l(s2 + 2^128, lambda*A) + * <=> q*A = (2*(s1 + 2^128) + 1 - 2^l) * A + (2*(s2 + 2^128) + 1 - 2^l) * lambda*A + * <=> q*A = (2*(s1 + s2*lambda) + (2^129 + 1 - 2^l) * (1 + lambda)) * A + * <=> q = 2*(s1 + s2*lambda) + (2^129 + 1 - 2^l) * (1 + lambda) (mod n) + * <=> q = 2*s + (2^129 + 1 - 2^l) * (1 + lambda) (mod n) + * <=> s = (q + (2^l - 2^129 - 1) * (1 + lambda)) / 2 (mod n) + * <=> f(q) = (q + K) / 2 (mod n) + * where K = (2^l - 2^129 - 1)*(1 + lambda) (mod n) + * + * We will process the computation of C_l(v1, A) and C_l(v2, lambda*A) in groups of + * ECMULT_CONST_GROUP_SIZE, so we set l to the smallest multiple of ECMULT_CONST_GROUP_SIZE + * that is not less than 129; this equals ECMULT_CONST_BITS. + */ -static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar) { - secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_ge tmpa; - secp256k1_fe Z; + /* The offset to add to s1 and s2 to make them non-negative. Equal to 2^128. */ + static const secp256k1_scalar S_OFFSET = SECP256K1_SCALAR_CONST(0, 0, 0, 1, 0, 0, 0, 0); + secp256k1_scalar s, v1, v2; + secp256k1_ge pre_a[ECMULT_CONST_TABLE_SIZE]; + secp256k1_ge pre_a_lam[ECMULT_CONST_TABLE_SIZE]; + secp256k1_fe global_z; + int group, i; - int skew_1; - int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; -#ifdef USE_ENDOMORPHISM - secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; - int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)]; - int skew_lam; - secp256k1_scalar q_1, q_lam; -#endif + /* We're allowed to be non-constant time in the point, and the code below (in particular, + * secp256k1_ecmult_const_odd_multiples_table_globalz) cannot deal with infinity in a + * constant-time manner anyway. */ + if (secp256k1_ge_is_infinity(a)) { + secp256k1_gej_set_infinity(r); + return; + } - int i; - secp256k1_scalar sc = *scalar; + /* Compute v1 and v2. */ + secp256k1_scalar_add(&s, q, &secp256k1_ecmult_const_K); + secp256k1_scalar_half(&s, &s); + secp256k1_scalar_split_lambda(&v1, &v2, &s); + secp256k1_scalar_add(&v1, &v1, &S_OFFSET); + secp256k1_scalar_add(&v2, &v2, &S_OFFSET); - /* build wnaf representation for q. */ -#ifdef USE_ENDOMORPHISM - /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ - secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc); - skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1); - skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1); -#else - skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1); +#ifdef VERIFY + /* Verify that v1 and v2 are in range [0, 2^129-1]. */ + for (i = 129; i < 256; ++i) { + VERIFY_CHECK(secp256k1_scalar_get_bits_limb32(&v1, i, 1) == 0); + VERIFY_CHECK(secp256k1_scalar_get_bits_limb32(&v2, i, 1) == 0); + } #endif - /* Calculate odd multiples of a. + /* Calculate odd multiples of A and A*lambda. * All multiples are brought to the same Z 'denominator', which is stored - * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * in global_z. Due to secp256k1' isomorphism we can do all operations pretending * that the Z coordinate was 1, use affine addition formulae, and correct * the Z coordinate of the result once at the end. */ secp256k1_gej_set_ge(r, a); - secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, r); - for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_fe_normalize_weak(&pre_a[i].y); - } -#ifdef USE_ENDOMORPHISM - for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ecmult_const_odd_multiples_table_globalz(pre_a, &global_z, r); + for (i = 0; i < ECMULT_CONST_TABLE_SIZE; i++) { secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); } -#endif - /* first loop iteration (separated out so we can directly set r, rather - * than having it start at infinity, get doubled several times, then have - * its new value added to it) */ - i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)]; - VERIFY_CHECK(i != 0); - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); - secp256k1_gej_set_ge(r, &tmpa); -#ifdef USE_ENDOMORPHISM - i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)]; - VERIFY_CHECK(i != 0); - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); - secp256k1_gej_add_ge(r, r, &tmpa); -#endif - /* remaining loop iterations */ - for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) { - int n; + /* Next, we compute r = C_l(v1, A) + C_l(v2, lambda*A). + * + * We proceed in groups of ECMULT_CONST_GROUP_SIZE bits, operating on that many bits + * at a time, from high in v1, v2 to low. Call these bits1 (from v1) and bits2 (from v2). + * + * Now note that ECMULT_CONST_TABLE_GET_GE(&t, pre_a, bits1) loads into t a point equal + * to C_{ECMULT_CONST_GROUP_SIZE}(bits1, A), and analogously for pre_lam_a / bits2. + * This means that all we need to do is add these looked up values together, multiplied + * by 2^(ECMULT_GROUP_SIZE * group). + */ + for (group = ECMULT_CONST_GROUPS - 1; group >= 0; --group) { + /* Using the _var get_bits function is ok here, since it's only variable in offset and count, not in the scalar. */ + unsigned int bits1 = secp256k1_scalar_get_bits_var(&v1, group * ECMULT_CONST_GROUP_SIZE, ECMULT_CONST_GROUP_SIZE); + unsigned int bits2 = secp256k1_scalar_get_bits_var(&v2, group * ECMULT_CONST_GROUP_SIZE, ECMULT_CONST_GROUP_SIZE); + secp256k1_ge t; int j; - for (j = 0; j < WINDOW_A - 1; ++j) { - secp256k1_gej_double_nonzero(r, r, NULL); - } - n = wnaf_1[i]; - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); - VERIFY_CHECK(n != 0); - secp256k1_gej_add_ge(r, r, &tmpa); -#ifdef USE_ENDOMORPHISM - n = wnaf_lam[i]; - ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); - VERIFY_CHECK(n != 0); - secp256k1_gej_add_ge(r, r, &tmpa); -#endif + ECMULT_CONST_TABLE_GET_GE(&t, pre_a, bits1); + if (group == ECMULT_CONST_GROUPS - 1) { + /* Directly set r in the first iteration. */ + secp256k1_gej_set_ge(r, &t); + } else { + /* Shift the result so far up. */ + for (j = 0; j < ECMULT_CONST_GROUP_SIZE; ++j) { + secp256k1_gej_double(r, r); + } + secp256k1_gej_add_ge(r, r, &t); + } + ECMULT_CONST_TABLE_GET_GE(&t, pre_a_lam, bits2); + secp256k1_gej_add_ge(r, r, &t); } - secp256k1_fe_mul(&r->z, &r->z, &Z); + /* Map the result back to the secp256k1 curve from the isomorphic curve. */ + secp256k1_fe_mul(&r->z, &r->z, &global_z); +} - { - /* Correct for wNAF skew */ - secp256k1_ge correction = *a; - secp256k1_ge_storage correction_1_stor; -#ifdef USE_ENDOMORPHISM - secp256k1_ge_storage correction_lam_stor; -#endif - secp256k1_ge_storage a2_stor; - secp256k1_gej tmpj; - secp256k1_gej_set_ge(&tmpj, &correction); - secp256k1_gej_double_var(&tmpj, &tmpj, NULL); - secp256k1_ge_set_gej(&correction, &tmpj); - secp256k1_ge_to_storage(&correction_1_stor, a); -#ifdef USE_ENDOMORPHISM - secp256k1_ge_to_storage(&correction_lam_stor, a); -#endif - secp256k1_ge_to_storage(&a2_stor, &correction); +static int secp256k1_ecmult_const_xonly(secp256k1_fe* r, const secp256k1_fe *n, const secp256k1_fe *d, const secp256k1_scalar *q, int known_on_curve) { - /* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */ - secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2); -#ifdef USE_ENDOMORPHISM - secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2); -#endif + /* This algorithm is a generalization of Peter Dettman's technique for + * avoiding the square root in a random-basepoint x-only multiplication + * on a Weierstrass curve: + * https://mailarchive.ietf.org/arch/msg/cfrg/7DyYY6gg32wDgHAhgSb6XxMDlJA/ + * + * + * === Background: the effective affine technique === + * + * Let phi_u be the isomorphism that maps (x, y) on secp256k1 curve y^2 = x^3 + 7 to + * x' = u^2*x, y' = u^3*y on curve y'^2 = x'^3 + u^6*7. This new curve has the same order as + * the original (it is isomorphic), but moreover, has the same addition/doubling formulas, as + * the curve b=7 coefficient does not appear in those formulas (or at least does not appear in + * the formulas implemented in this codebase, both affine and Jacobian). See also Example 9.5.2 + * in https://www.math.auckland.ac.nz/~sgal018/crypto-book/ch9.pdf. + * + * This means any linear combination of secp256k1 points can be computed by applying phi_u + * (with non-zero u) on all input points (including the generator, if used), computing the + * linear combination on the isomorphic curve (using the same group laws), and then applying + * phi_u^{-1} to get back to secp256k1. + * + * Switching to Jacobian coordinates, note that phi_u applied to (X, Y, Z) is simply + * (X, Y, Z/u). Thus, if we want to compute (X1, Y1, Z) + (X2, Y2, Z), with identical Z + * coordinates, we can use phi_Z to transform it to (X1, Y1, 1) + (X2, Y2, 1) on an isomorphic + * curve where the affine addition formula can be used instead. + * If (X3, Y3, Z3) = (X1, Y1) + (X2, Y2) on that curve, then our answer on secp256k1 is + * (X3, Y3, Z3*Z). + * + * This is the effective affine technique: if we have a linear combination of group elements + * to compute, and all those group elements have the same Z coordinate, we can simply pretend + * that all those Z coordinates are 1, perform the computation that way, and then multiply the + * original Z coordinate back in. + * + * The technique works on any a=0 short Weierstrass curve. It is possible to generalize it to + * other curves too, but there the isomorphic curves will have different 'a' coefficients, + * which typically does affect the group laws. + * + * + * === Avoiding the square root for x-only point multiplication === + * + * In this function, we want to compute the X coordinate of q*(n/d, y), for + * y = sqrt((n/d)^3 + 7). Its negation would also be a valid Y coordinate, but by convention + * we pick whatever sqrt returns (which we assume to be a deterministic function). + * + * Let g = y^2*d^3 = n^3 + 7*d^3. This also means y = sqrt(g/d^3). + * Further let v = sqrt(d*g), which must exist as d*g = y^2*d^4 = (y*d^2)^2. + * + * The input point (n/d, y) also has Jacobian coordinates: + * + * (n/d, y, 1) + * = (n/d * v^2, y * v^3, v) + * = (n/d * d*g, y * sqrt(d^3*g^3), v) + * = (n/d * d*g, sqrt(y^2 * d^3*g^3), v) + * = (n*g, sqrt(g/d^3 * d^3*g^3), v) + * = (n*g, sqrt(g^4), v) + * = (n*g, g^2, v) + * + * It is easy to verify that both (n*g, g^2, v) and its negation (n*g, -g^2, v) have affine X + * coordinate n/d, and this holds even when the square root function doesn't have a + * deterministic sign. We choose the (n*g, g^2, v) version. + * + * Now switch to the effective affine curve using phi_v, where the input point has coordinates + * (n*g, g^2). Compute (X, Y, Z) = q * (n*g, g^2) there. + * + * Back on secp256k1, that means q * (n*g, g^2, v) = (X, Y, v*Z). This last point has affine X + * coordinate X / (v^2*Z^2) = X / (d*g*Z^2). Determining the affine Y coordinate would involve + * a square root, but as long as we only care about the resulting X coordinate, no square root + * is needed anywhere in this computation. + */ - /* Apply the correction */ - secp256k1_ge_from_storage(&correction, &correction_1_stor); - secp256k1_ge_neg(&correction, &correction); - secp256k1_gej_add_ge(r, r, &correction); + secp256k1_fe g, i; + secp256k1_ge p; + secp256k1_gej rj; -#ifdef USE_ENDOMORPHISM - secp256k1_ge_from_storage(&correction, &correction_lam_stor); - secp256k1_ge_neg(&correction, &correction); - secp256k1_ge_mul_lambda(&correction, &correction); - secp256k1_gej_add_ge(r, r, &correction); -#endif + /* Compute g = (n^3 + B*d^3). */ + secp256k1_fe_sqr(&g, n); + secp256k1_fe_mul(&g, &g, n); + if (d) { + secp256k1_fe b; + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero(d)); + secp256k1_fe_sqr(&b, d); + VERIFY_CHECK(SECP256K1_B <= 8); /* magnitude of b will be <= 8 after the next call */ + secp256k1_fe_mul_int(&b, SECP256K1_B); + secp256k1_fe_mul(&b, &b, d); + secp256k1_fe_add(&g, &b); + if (!known_on_curve) { + /* We need to determine whether (n/d)^3 + 7 is square. + * + * is_square((n/d)^3 + 7) + * <=> is_square(((n/d)^3 + 7) * d^4) + * <=> is_square((n^3 + 7*d^3) * d) + * <=> is_square(g * d) + */ + secp256k1_fe c; + secp256k1_fe_mul(&c, &g, d); + if (!secp256k1_fe_is_square_var(&c)) return 0; + } + } else { + secp256k1_fe_add_int(&g, SECP256K1_B); + if (!known_on_curve) { + /* g at this point equals x^3 + 7. Test if it is square. */ + if (!secp256k1_fe_is_square_var(&g)) return 0; + } } + + /* Compute base point P = (n*g, g^2), the effective affine version of (n*g, g^2, v), which has + * corresponding affine X coordinate n/d. */ + secp256k1_fe_mul(&p.x, &g, n); + secp256k1_fe_sqr(&p.y, &g); + p.infinity = 0; + + /* Perform x-only EC multiplication of P with q. */ + VERIFY_CHECK(!secp256k1_scalar_is_zero(q)); + secp256k1_ecmult_const(&rj, &p, q); + VERIFY_CHECK(!secp256k1_gej_is_infinity(&rj)); + + /* The resulting (X, Y, Z) point on the effective-affine isomorphic curve corresponds to + * (X, Y, Z*v) on the secp256k1 curve. The affine version of that has X coordinate + * (X / (Z^2*d*g)). */ + secp256k1_fe_sqr(&i, &rj.z); + secp256k1_fe_mul(&i, &i, &g); + if (d) secp256k1_fe_mul(&i, &i, d); + secp256k1_fe_inv(&i, &i); + secp256k1_fe_mul(r, &rj.x, &i); + + return 1; } #endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h index 8045394b7..43dd10c38 100644 --- a/src/secp256k1/src/ecmult_gen.h +++ b/src/secp256k1/src/ecmult_gen.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) Pieter Wuille, Peter Dettman * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECMULT_GEN_H #define SECP256K1_ECMULT_GEN_H @@ -10,30 +10,130 @@ #include "scalar.h" #include "group.h" + +/* Configuration parameters for the signed-digit multi-comb algorithm: + * + * - COMB_BLOCKS is the number of blocks the input is split into. Each + * has a corresponding table. + * - COMB_TEETH is the number of bits simultaneously covered by one table. + * - COMB_RANGE is the number of bits in supported scalars. For production + * purposes, only 256 is reasonable, but smaller numbers are supported for + * exhaustive test mode. + * + * The comb's spacing (COMB_SPACING), or the distance between the teeth, + * is defined as ceil(COMB_RANGE / (COMB_BLOCKS * COMB_TEETH)). Each block covers + * COMB_SPACING * COMB_TEETH consecutive bits in the input. + * + * The size of the precomputed table is COMB_BLOCKS * (1 << (COMB_TEETH - 1)) + * secp256k1_ge_storages. + * + * The number of point additions equals COMB_BLOCKS * COMB_SPACING. Each point + * addition involves a cmov from (1 << (COMB_TEETH - 1)) table entries and a + * conditional negation. + * + * The number of point doublings is COMB_SPACING - 1. */ + +#if defined(EXHAUSTIVE_TEST_ORDER) +/* We need to control these values for exhaustive tests because + * the table cannot have infinities in them (secp256k1_ge_storage + * doesn't support infinities) */ +# undef COMB_BLOCKS +# undef COMB_TEETH +# if EXHAUSTIVE_TEST_ORDER == 7 +# define COMB_RANGE 3 +# define COMB_BLOCKS 1 +# define COMB_TEETH 2 +# elif EXHAUSTIVE_TEST_ORDER == 13 +# define COMB_RANGE 4 +# define COMB_BLOCKS 1 +# define COMB_TEETH 2 +# elif EXHAUSTIVE_TEST_ORDER == 199 +# define COMB_RANGE 8 +# define COMB_BLOCKS 2 +# define COMB_TEETH 3 +# else +# error "Unknown exhaustive test order" +# endif +# if (COMB_RANGE >= 32) || ((EXHAUSTIVE_TEST_ORDER >> (COMB_RANGE - 1)) != 1) +# error "COMB_RANGE != ceil(log2(EXHAUSTIVE_TEST_ORDER+1))" +# endif +#else /* !defined(EXHAUSTIVE_TEST_ORDER) */ +# define COMB_RANGE 256 +#endif /* defined(EXHAUSTIVE_TEST_ORDER) */ + +/* Use (11, 6) as default configuration, which results in a 22 kB table. */ +#ifndef COMB_BLOCKS +# define COMB_BLOCKS 11 +# ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_MSG("COMB_BLOCKS undefined, assuming default value") +# endif +#endif +#ifndef COMB_TEETH +# define COMB_TEETH 6 +# ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_MSG("COMB_TEETH undefined, assuming default value") +# endif +#endif +/* Use ceil(COMB_RANGE / (COMB_BLOCKS * COMB_TEETH)) as COMB_SPACING. */ +#define COMB_SPACING CEIL_DIV(COMB_RANGE, COMB_BLOCKS * COMB_TEETH) + +/* Range checks on the parameters. */ + +/* The remaining COMB_* parameters are derived values, don't modify these. */ +/* - The number of bits covered by all the blocks; must be at least COMB_RANGE. */ +#define COMB_BITS (COMB_BLOCKS * COMB_TEETH * COMB_SPACING) +/* - The number of entries per table. */ +#define COMB_POINTS (1 << (COMB_TEETH - 1)) + +/* Sanity checks. */ +#if !(1 <= COMB_BLOCKS && COMB_BLOCKS <= 256) +# error "COMB_BLOCKS must be in the range [1, 256]" +#endif +#if !(1 <= COMB_TEETH && COMB_TEETH <= 8) +# error "COMB_TEETH must be in the range [1, 8]" +#endif +#if COMB_BITS < COMB_RANGE +# error "COMB_BLOCKS * COMB_TEETH * COMB_SPACING is too low" +#endif + +/* These last 2 checks are not strictly required, but prevent gratuitously inefficient + * configurations. Note that they compare with 256 rather than COMB_RANGE, so they do + * permit somewhat excessive values for the exhaustive test case, where testing with + * suboptimal parameters may be desirable. */ +#if (COMB_BLOCKS - 1) * COMB_TEETH * COMB_SPACING >= 256 +# error "COMB_BLOCKS can be reduced" +#endif +#if COMB_BLOCKS * (COMB_TEETH - 1) * COMB_SPACING >= 256 +# error "COMB_TEETH can be reduced" +#endif + +#ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_DEF(COMB_RANGE) +# pragma message DEBUG_CONFIG_DEF(COMB_BLOCKS) +# pragma message DEBUG_CONFIG_DEF(COMB_TEETH) +# pragma message DEBUG_CONFIG_DEF(COMB_SPACING) +#endif + typedef struct { - /* For accelerating the computation of a*G: - * To harden against timing attacks, use the following mechanism: - * * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63. - * * Compute sum(n_i * 16^i * G + U_i, i=0..63), where: - * * U_i = U * 2^i (for i=0..62) - * * U_i = U * (1-2^63) (for i=63) - * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0. - * For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is - * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63). - * None of the resulting prec group elements have a known scalar, and neither do any of - * the intermediate sums while computing a*G. - */ - secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ - secp256k1_scalar blind; - secp256k1_gej initial; + /* Whether the context has been built. */ + int built; + + /* Values chosen such that + * + * n*G == comb(n + scalar_offset, G/2) + ge_offset. + * + * This expression lets us use scalar blinding and optimize the comb precomputation. See + * ecmult_gen_impl.h for more details. */ + secp256k1_scalar scalar_offset; + secp256k1_ge ge_offset; + + /* Factor used for projective blinding. This value is used to rescale the Z + * coordinate of the first table lookup. */ + secp256k1_fe proj_blind; } secp256k1_ecmult_gen_context; -static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx); -static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb); -static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, - const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb); +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx); static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx); -static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx); /** Multiply with the generator: R = a*G */ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp256k1_gej *r, const secp256k1_scalar *a); diff --git a/src/secp256k1/src/ecmult_gen_compute_table.h b/src/secp256k1/src/ecmult_gen_compute_table.h new file mode 100644 index 000000000..bd41803a8 --- /dev/null +++ b/src/secp256k1/src/ecmult_gen_compute_table.h @@ -0,0 +1,14 @@ +/*********************************************************************** + * Copyright (c) Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_ECMULT_GEN_COMPUTE_TABLE_H +#define SECP256K1_ECMULT_GEN_COMPUTE_TABLE_H + +#include "ecmult_gen.h" + +static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int blocks, int teeth, int spacing); + +#endif /* SECP256K1_ECMULT_GEN_COMPUTE_TABLE_H */ diff --git a/src/secp256k1/src/ecmult_gen_compute_table_impl.h b/src/secp256k1/src/ecmult_gen_compute_table_impl.h new file mode 100644 index 000000000..6aa8d8408 --- /dev/null +++ b/src/secp256k1/src/ecmult_gen_compute_table_impl.h @@ -0,0 +1,108 @@ +/*********************************************************************** + * Copyright (c) Pieter Wuille, Gregory Maxwell, Peter Dettman * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_ECMULT_GEN_COMPUTE_TABLE_IMPL_H +#define SECP256K1_ECMULT_GEN_COMPUTE_TABLE_IMPL_H + +#include "ecmult_gen_compute_table.h" +#include "group_impl.h" +#include "field_impl.h" +#include "scalar_impl.h" +#include "ecmult_gen.h" +#include "util.h" + +static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int blocks, int teeth, int spacing) { + size_t points = ((size_t)1) << (teeth - 1); + size_t points_total = points * blocks; + secp256k1_ge* prec = checked_malloc(&default_error_callback, points_total * sizeof(*prec)); + secp256k1_gej* ds = checked_malloc(&default_error_callback, teeth * sizeof(*ds)); + secp256k1_gej* vs = checked_malloc(&default_error_callback, points_total * sizeof(*vs)); + secp256k1_gej u; + size_t vs_pos = 0; + secp256k1_scalar half; + int block, i; + + VERIFY_CHECK(points_total > 0); + + /* u is the running power of two times gen we're working with, initially gen/2. */ + secp256k1_scalar_half(&half, &secp256k1_scalar_one); + secp256k1_gej_set_infinity(&u); + for (i = 255; i >= 0; --i) { + /* Use a very simple multiplication ladder to avoid dependency on ecmult. */ + secp256k1_gej_double_var(&u, &u, NULL); + if (secp256k1_scalar_get_bits_limb32(&half, i, 1)) { + secp256k1_gej_add_ge_var(&u, &u, gen, NULL); + } + } +#ifdef VERIFY + { + /* Verify that u*2 = gen. */ + secp256k1_gej double_u; + secp256k1_gej_double_var(&double_u, &u, NULL); + VERIFY_CHECK(secp256k1_gej_eq_ge_var(&double_u, gen)); + } +#endif + + for (block = 0; block < blocks; ++block) { + int tooth; + /* Here u = 2^(block*teeth*spacing) * gen/2. */ + secp256k1_gej sum; + secp256k1_gej_set_infinity(&sum); + for (tooth = 0; tooth < teeth; ++tooth) { + /* Here u = 2^((block*teeth + tooth)*spacing) * gen/2. */ + /* Make sum = sum(2^((block*teeth + t)*spacing), t=0..tooth) * gen/2. */ + secp256k1_gej_add_var(&sum, &sum, &u, NULL); + /* Make u = 2^((block*teeth + tooth)*spacing + 1) * gen/2. */ + secp256k1_gej_double_var(&u, &u, NULL); + /* Make ds[tooth] = u = 2^((block*teeth + tooth)*spacing + 1) * gen/2. */ + ds[tooth] = u; + /* Make u = 2^((block*teeth + tooth + 1)*spacing) * gen/2, unless at the end. */ + if (block + tooth != blocks + teeth - 2) { + int bit_off; + for (bit_off = 1; bit_off < spacing; ++bit_off) { + secp256k1_gej_double_var(&u, &u, NULL); + } + } + } + /* Now u = 2^((block*teeth + teeth)*spacing) * gen/2 + * = 2^((block+1)*teeth*spacing) * gen/2 */ + + /* Next, compute the table entries for block number block in Jacobian coordinates. + * The entries will occupy vs[block*points + i] for i=0..points-1. + * We start by computing the first (i=0) value corresponding to all summed + * powers of two times G being negative. */ + secp256k1_gej_neg(&vs[vs_pos++], &sum); + /* And then teeth-1 times "double" the range of i values for which the table + * is computed: in each iteration, double the table by taking an existing + * table entry and adding ds[tooth]. */ + for (tooth = 0; tooth < teeth - 1; ++tooth) { + size_t stride = ((size_t)1) << tooth; + size_t index; + for (index = 0; index < stride; ++index, ++vs_pos) { + secp256k1_gej_add_var(&vs[vs_pos], &vs[vs_pos - stride], &ds[tooth], NULL); + } + } + } + VERIFY_CHECK(vs_pos == points_total); + + /* Convert all points simultaneously from secp256k1_gej to secp256k1_ge. */ + secp256k1_ge_set_all_gej_var(prec, vs, points_total); + /* Convert all points from secp256k1_ge to secp256k1_ge_storage output. */ + for (block = 0; block < blocks; ++block) { + size_t index; + for (index = 0; index < points; ++index) { + VERIFY_CHECK(!secp256k1_ge_is_infinity(&prec[block * points + index])); + secp256k1_ge_to_storage(&table[block * points + index], &prec[block * points + index]); + } + } + + /* Free memory. */ + free(vs); + free(ds); + free(prec); +} + +#endif /* SECP256K1_ECMULT_GEN_COMPUTE_TABLE_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h index 5c3eedcf1..2fbf623ca 100644 --- a/src/secp256k1/src/ecmult_gen_impl.h +++ b/src/secp256k1/src/ecmult_gen_impl.h @@ -1,140 +1,239 @@ -/************************************************************************ - * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) Pieter Wuille, Gregory Maxwell, Peter Dettman * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_ECMULT_GEN_IMPL_H #define SECP256K1_ECMULT_GEN_IMPL_H +#include "util.h" #include "scalar.h" #include "group.h" #include "ecmult_gen.h" #include "hash_impl.h" -#ifdef USE_ECMULT_STATIC_PRECOMPUTATION -#include "ecmult_static_context.h" -#endif -static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) { - ctx->prec = NULL; -} - -static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) { -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - secp256k1_ge prec[1024]; - secp256k1_gej gj; - secp256k1_gej nums_gej; - int i, j; -#endif - - if (ctx->prec != NULL) { - return; - } -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec)); - - /* get the generator */ - secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); - - /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */ - { - static const unsigned char nums_b32[33] = "The scalar for this x is unknown"; - secp256k1_fe nums_x; - secp256k1_ge nums_ge; - int r; - r = secp256k1_fe_set_b32(&nums_x, nums_b32); - (void)r; - VERIFY_CHECK(r); - r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0); - (void)r; - VERIFY_CHECK(r); - secp256k1_gej_set_ge(&nums_gej, &nums_ge); - /* Add G to make the bits in x uniformly distributed. */ - secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL); - } +#include "precomputed_ecmult_gen.h" - /* compute prec. */ - { - secp256k1_gej precj[1024]; /* Jacobian versions of prec. */ - secp256k1_gej gbase; - secp256k1_gej numsbase; - gbase = gj; /* 16^j * G */ - numsbase = nums_gej; /* 2^j * nums. */ - for (j = 0; j < 64; j++) { - /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */ - precj[j*16] = numsbase; - for (i = 1; i < 16; i++) { - secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL); - } - /* Multiply gbase by 16. */ - for (i = 0; i < 4; i++) { - secp256k1_gej_double_var(&gbase, &gbase, NULL); - } - /* Multiply numbase by 2. */ - secp256k1_gej_double_var(&numsbase, &numsbase, NULL); - if (j == 62) { - /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ - secp256k1_gej_neg(&numsbase, &numsbase); - secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); - } - } - secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb); - } - for (j = 0; j < 64; j++) { - for (i = 0; i < 16; i++) { - secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); - } - } -#else - (void)cb; - ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context; -#endif +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx) { secp256k1_ecmult_gen_blind(ctx, NULL); + ctx->built = 1; } static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx) { - return ctx->prec != NULL; + return ctx->built; } -static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, - const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) { - if (src->prec == NULL) { - dst->prec = NULL; - } else { -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec)); - memcpy(dst->prec, src->prec, sizeof(*dst->prec)); -#else - (void)cb; - dst->prec = src->prec; -#endif - dst->initial = src->initial; - dst->blind = src->blind; - } +static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { + ctx->built = 0; + secp256k1_scalar_clear(&ctx->scalar_offset); + secp256k1_ge_clear(&ctx->ge_offset); + secp256k1_fe_clear(&ctx->proj_blind); } -static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { -#ifndef USE_ECMULT_STATIC_PRECOMPUTATION - free(ctx->prec); -#endif - secp256k1_scalar_clear(&ctx->blind); - secp256k1_gej_clear(&ctx->initial); - ctx->prec = NULL; +/* Compute the scalar (2^COMB_BITS - 1) / 2, the difference between the gn argument to + * secp256k1_ecmult_gen, and the scalar whose encoding the table lookup bits are drawn + * from (before applying blinding). */ +static void secp256k1_ecmult_gen_scalar_diff(secp256k1_scalar* diff) { + int i; + + /* Compute scalar -1/2. */ + secp256k1_scalar neghalf; + secp256k1_scalar_half(&neghalf, &secp256k1_scalar_one); + secp256k1_scalar_negate(&neghalf, &neghalf); + + /* Compute offset = 2^(COMB_BITS - 1). */ + *diff = secp256k1_scalar_one; + for (i = 0; i < COMB_BITS - 1; ++i) { + secp256k1_scalar_add(diff, diff, diff); + } + + /* The result is the sum 2^(COMB_BITS - 1) + (-1/2). */ + secp256k1_scalar_add(diff, diff, &neghalf); } static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) { + uint32_t comb_off; secp256k1_ge add; + secp256k1_fe neg; secp256k1_ge_storage adds; - secp256k1_scalar gnb; - int bits; - int i, j; + secp256k1_scalar d; + /* Array of uint32_t values large enough to store COMB_BITS bits. Only the bottom + * 8 are ever nonzero, but having the zero padding at the end if COMB_BITS>256 + * avoids the need to deal with out-of-bounds reads from a scalar. */ + uint32_t recoded[(COMB_BITS + 31) >> 5] = {0}; + int first = 1, i; + memset(&adds, 0, sizeof(adds)); - *r = ctx->initial; - /* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */ - secp256k1_scalar_add(&gnb, gn, &ctx->blind); - add.infinity = 0; - for (j = 0; j < 64; j++) { - bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4); - for (i = 0; i < 16; i++) { + + /* We want to compute R = gn*G. + * + * To blind the scalar used in the computation, we rewrite this to be + * R = (gn - b)*G + b*G, with a blinding value b determined by the context. + * + * The multiplication (gn-b)*G will be performed using a signed-digit multi-comb (see Section + * 3.3 of "Fast and compact elliptic-curve cryptography" by Mike Hamburg, + * https://eprint.iacr.org/2012/309). + * + * Let comb(s, P) = sum((2*s[i]-1)*2^i*P for i=0..COMB_BITS-1), where s[i] is the i'th bit of + * the binary representation of scalar s. So the s[i] values determine whether -2^i*P (s[i]=0) + * or +2^i*P (s[i]=1) are added together. COMB_BITS is at least 256, so all bits of s are + * covered. By manipulating: + * + * comb(s, P) = sum((2*s[i]-1)*2^i*P for i=0..COMB_BITS-1) + * <=> comb(s, P) = sum((2*s[i]-1)*2^i for i=0..COMB_BITS-1) * P + * <=> comb(s, P) = (2*sum(s[i]*2^i for i=0..COMB_BITS-1) - sum(2^i for i=0..COMB_BITS-1)) * P + * <=> comb(s, P) = (2*s - (2^COMB_BITS - 1)) * P + * + * If we wanted to compute (gn-b)*G as comb(s, G), it would need to hold that + * + * (gn - b) * G = (2*s - (2^COMB_BITS - 1)) * G + * <=> s = (gn - b + (2^COMB_BITS - 1))/2 (mod order) + * + * We use an alternative here that avoids the modular division by two: instead we compute + * (gn-b)*G as comb(d, G/2). For that to hold it must be the case that + * + * (gn - b) * G = (2*d - (2^COMB_BITS - 1)) * (G/2) + * <=> d = gn - b + (2^COMB_BITS - 1)/2 (mod order) + * + * Adding precomputation, our final equations become: + * + * ctx->scalar_offset = (2^COMB_BITS - 1)/2 - b (mod order) + * ctx->ge_offset = b*G + * d = gn + ctx->scalar_offset (mod order) + * R = comb(d, G/2) + ctx->ge_offset + * + * comb(d, G/2) function is then computed by summing + or - 2^(i-1)*G, for i=0..COMB_BITS-1, + * depending on the value of the bits d[i] of the binary representation of scalar d. + */ + + /* Compute the scalar d = (gn + ctx->scalar_offset). */ + secp256k1_scalar_add(&d, &ctx->scalar_offset, gn); + /* Convert to recoded array. */ + for (i = 0; i < 8 && i < ((COMB_BITS + 31) >> 5); ++i) { + recoded[i] = secp256k1_scalar_get_bits_limb32(&d, 32 * i, 32); + } + secp256k1_scalar_clear(&d); + + /* In secp256k1_ecmult_gen_prec_table we have precomputed sums of the + * (2*d[i]-1) * 2^(i-1) * G points, for various combinations of i positions. + * We rewrite our equation in terms of these table entries. + * + * Let mask(b) = sum(2^((b*COMB_TEETH + t)*COMB_SPACING) for t=0..COMB_TEETH-1), + * with b ranging from 0 to COMB_BLOCKS-1. So for example with COMB_BLOCKS=11, + * COMB_TEETH=6, COMB_SPACING=4, we would have: + * mask(0) = 2^0 + 2^4 + 2^8 + 2^12 + 2^16 + 2^20, + * mask(1) = 2^24 + 2^28 + 2^32 + 2^36 + 2^40 + 2^44, + * mask(2) = 2^48 + 2^52 + 2^56 + 2^60 + 2^64 + 2^68, + * ... + * mask(10) = 2^240 + 2^244 + 2^248 + 2^252 + 2^256 + 2^260 + * + * We will split up the bits d[i] using these masks. Specifically, each mask is + * used COMB_SPACING times, with different shifts: + * + * d = (d & mask(0)<<0) + (d & mask(1)<<0) + ... + (d & mask(COMB_BLOCKS-1)<<0) + + * (d & mask(0)<<1) + (d & mask(1)<<1) + ... + (d & mask(COMB_BLOCKS-1)<<1) + + * ... + * (d & mask(0)<<(COMB_SPACING-1)) + ... + * + * Now define table(b, m) = (m - mask(b)/2) * G, and we will precompute these values for + * b=0..COMB_BLOCKS-1, and for all values m which (d & mask(b)) can take (so m can take on + * 2^COMB_TEETH distinct values). + * + * If m=(d & mask(b)), then table(b, m) is the sum of 2^i * (2*d[i]-1) * G/2, with i + * iterating over the set bits in mask(b). In our example, table(2, 2^48 + 2^56 + 2^68) + * would equal (2^48 - 2^52 + 2^56 - 2^60 - 2^64 + 2^68) * G/2. + * + * With that, we can rewrite comb(d, G/2) as: + * + * 2^0 * (table(0, d>>0 & mask(0)) + ... + table(COMB_BLOCKS-1, d>>0 & mask(COMP_BLOCKS-1))) + * + 2^1 * (table(0, d>>1 & mask(0)) + ... + table(COMB_BLOCKS-1, d>>1 & mask(COMP_BLOCKS-1))) + * + 2^2 * (table(0, d>>2 & mask(0)) + ... + table(COMB_BLOCKS-1, d>>2 & mask(COMP_BLOCKS-1))) + * + ... + * + 2^(COMB_SPACING-1) * (table(0, d>>(COMB_SPACING-1) & mask(0)) + ...) + * + * Or more generically as + * + * sum(2^i * sum(table(b, d>>i & mask(b)), b=0..COMB_BLOCKS-1), i=0..COMB_SPACING-1) + * + * This is implemented using an outer loop that runs in reverse order over the lines of this + * equation, which in each iteration runs an inner loop that adds the terms of that line and + * then doubles the result before proceeding to the next line. + * + * In pseudocode: + * c = infinity + * for comb_off in range(COMB_SPACING - 1, -1, -1): + * for block in range(COMB_BLOCKS): + * c += table(block, (d >> comb_off) & mask(block)) + * if comb_off > 0: + * c = 2*c + * return c + * + * This computes c = comb(d, G/2), and thus finally R = c + ctx->ge_offset. Note that it would + * be possible to apply an initial offset instead of a final offset (moving ge_offset to take + * the place of infinity above), but the chosen approach allows using (in a future improvement) + * an incomplete addition formula for most of the multiplication. + * + * The last question is how to implement the table(b, m) function. For any value of b, + * m=(d & mask(b)) can only take on at most 2^COMB_TEETH possible values (the last one may have + * fewer as there mask(b) may exceed the curve order). So we could create COMB_BLOCK tables + * which contain a value for each such m value. + * + * Now note that if m=(d & mask(b)), then flipping the relevant bits of m results in negating + * the result of table(b, m). This is because table(b,m XOR mask(b)) = table(b, mask(b) - m) = + * (mask(b) - m - mask(b)/2)*G = (-m + mask(b)/2)*G = -(m - mask(b)/2)*G = -table(b, m). + * Because of this it suffices to only store the first half of the m values for every b. If an + * entry from the second half is needed, we look up its bit-flipped version instead, and negate + * it. + * + * secp256k1_ecmult_gen_prec_table[b][index] stores the table(b, m) entries. Index + * is the relevant mask(b) bits of m packed together without gaps. */ + + /* Outer loop: iterate over comb_off from COMB_SPACING - 1 down to 0. */ + comb_off = COMB_SPACING - 1; + while (1) { + uint32_t block; + uint32_t bit_pos = comb_off; + /* Inner loop: for each block, add table entries to the result. */ + for (block = 0; block < COMB_BLOCKS; ++block) { + /* Gather the mask(block)-selected bits of d into bits. They're packed: + * bits[tooth] = d[(block*COMB_TEETH + tooth)*COMB_SPACING + comb_off]. */ + uint32_t bits = 0, sign, abs, index, tooth; + /* Instead of reading individual bits here to construct the bits variable, + * build up the result by xoring rotated reads together. In every iteration, + * one additional bit is made correct, starting at the bottom. The bits + * above that contain junk. This reduces leakage by avoiding computations + * on variables that can have only a low number of possible values (e.g., + * just two values when reading a single bit into a variable.) See: + * https://www.usenix.org/system/files/conference/usenixsecurity18/sec18-alam.pdf + */ + for (tooth = 0; tooth < COMB_TEETH; ++tooth) { + /* Construct bitdata s.t. the bottom bit is the bit we'd like to read. + * + * We could just set bitdata = recoded[bit_pos >> 5] >> (bit_pos & 0x1f) + * but this would simply discard the bits that fall off at the bottom, + * and thus, for example, bitdata could still have only two values if we + * happen to shift by exactly 31 positions. We use a rotation instead, + * which ensures that bitdata doesn't loose entropy. This relies on the + * rotation being atomic, i.e., the compiler emitting an actual rot + * instruction. */ + uint32_t bitdata = secp256k1_rotr32(recoded[bit_pos >> 5], bit_pos & 0x1f); + + /* Clear the bit at position tooth, but sssh, don't tell clang. */ + uint32_t volatile vmask = ~(1 << tooth); + bits &= vmask; + + /* Write the bit into position tooth (and junk into higher bits). */ + bits ^= bitdata << tooth; + bit_pos += COMB_SPACING; + } + + /* If the top bit of bits is 1, flip them all (corresponding to looking up + * the negated table value), and remember to negate the result in sign. */ + sign = (bits >> (COMB_TEETH - 1)) & 1; + abs = (bits ^ -sign) & (COMB_POINTS - 1); + VERIFY_CHECK(sign == 0 || sign == 1); + VERIFY_CHECK(abs < COMB_POINTS); + /** This uses a conditional move to avoid any secret data in array indexes. * _Any_ use of secret indexes has been demonstrated to result in timing * sidechannels, even when the cache-line access patterns are uniform. @@ -143,68 +242,99 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 * (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and * "Cache Attacks and Countermeasures: the Case of AES", RSA 2006, * by Dag Arne Osvik, Adi Shamir, and Eran Tromer - * (http://www.tau.ac.il/~tromer/papers/cache.pdf) + * (https://www.tau.ac.il/~tromer/papers/cache.pdf) */ - secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); + for (index = 0; index < COMB_POINTS; ++index) { + secp256k1_ge_storage_cmov(&adds, &secp256k1_ecmult_gen_prec_table[block][index], index == abs); + } + + /* Set add=adds or add=-adds, in constant time, based on sign. */ + secp256k1_ge_from_storage(&add, &adds); + secp256k1_fe_negate(&neg, &add.y, 1); + secp256k1_fe_cmov(&add.y, &neg, sign); + + /* Add the looked up and conditionally negated value to r. */ + if (EXPECT(first, 0)) { + /* If this is the first table lookup, we can skip addition. */ + secp256k1_gej_set_ge(r, &add); + /* Give the entry a random Z coordinate to blind intermediary results. */ + secp256k1_gej_rescale(r, &ctx->proj_blind); + first = 0; + } else { + secp256k1_gej_add_ge(r, r, &add); + } } - secp256k1_ge_from_storage(&add, &adds); - secp256k1_gej_add_ge(r, r, &add); + + /* Double the result, except in the last iteration. */ + if (comb_off-- == 0) break; + secp256k1_gej_double(r, r); } - bits = 0; + + /* Correct for the scalar_offset added at the start (ge_offset = b*G, while b was + * subtracted from the input scalar gn). */ + secp256k1_gej_add_ge(r, r, &ctx->ge_offset); + + /* Cleanup. */ + secp256k1_fe_clear(&neg); secp256k1_ge_clear(&add); - secp256k1_scalar_clear(&gnb); + memset(&adds, 0, sizeof(adds)); + memset(&recoded, 0, sizeof(recoded)); } /* Setup blinding values for secp256k1_ecmult_gen. */ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) { secp256k1_scalar b; + secp256k1_scalar diff; secp256k1_gej gb; - secp256k1_fe s; + secp256k1_fe f; unsigned char nonce32[32]; secp256k1_rfc6979_hmac_sha256 rng; - int retry; - unsigned char keydata[64] = {0}; + unsigned char keydata[64]; + + /* Compute the (2^COMB_BITS - 1)/2 term once. */ + secp256k1_ecmult_gen_scalar_diff(&diff); + if (seed32 == NULL) { - /* When seed is NULL, reset the initial point and blinding value. */ - secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); - secp256k1_gej_neg(&ctx->initial, &ctx->initial); - secp256k1_scalar_set_int(&ctx->blind, 1); + /* When seed is NULL, reset the final point and blinding value. */ + secp256k1_ge_neg(&ctx->ge_offset, &secp256k1_ge_const_g); + secp256k1_scalar_add(&ctx->scalar_offset, &secp256k1_scalar_one, &diff); + ctx->proj_blind = secp256k1_fe_one; + return; } /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ - secp256k1_scalar_get_b32(nonce32, &ctx->blind); + secp256k1_scalar_get_b32(keydata, &ctx->scalar_offset); /** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, * and guards against weak or adversarial seeds. This is a simpler and safer interface than * asking the caller for blinding values directly and expecting them to retry on failure. */ - memcpy(keydata, nonce32, 32); - if (seed32 != NULL) { - memcpy(keydata + 32, seed32, 32); - } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); + VERIFY_CHECK(seed32 != NULL); + memcpy(keydata + 32, seed32, 32); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64); memset(keydata, 0, sizeof(keydata)); - /* Retry for out of range results to achieve uniformity. */ - do { - secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); - retry = !secp256k1_fe_set_b32(&s, nonce32); - retry |= secp256k1_fe_is_zero(&s); - } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */ - /* Randomize the projection to defend against multiplier sidechannels. */ - secp256k1_gej_rescale(&ctx->initial, &s); - secp256k1_fe_clear(&s); - do { - secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); - secp256k1_scalar_set_b32(&b, nonce32, &retry); - /* A blinding value of 0 works, but would undermine the projection hardening. */ - retry |= secp256k1_scalar_is_zero(&b); - } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ + + /* Compute projective blinding factor (cannot be 0). */ + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + secp256k1_fe_set_b32_mod(&f, nonce32); + secp256k1_fe_cmov(&f, &secp256k1_fe_one, secp256k1_fe_normalizes_to_zero(&f)); + ctx->proj_blind = f; + + /* For a random blinding value b, set scalar_offset=diff-b, ge_offset=bG */ + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + secp256k1_scalar_set_b32(&b, nonce32, NULL); + /* The blinding value cannot be zero, as that would mean ge_offset = infinity, + * which secp256k1_gej_add_ge cannot handle. */ + secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b)); secp256k1_rfc6979_hmac_sha256_finalize(&rng); memset(nonce32, 0, 32); secp256k1_ecmult_gen(ctx, &gb, &b); secp256k1_scalar_negate(&b, &b); - ctx->blind = b; - ctx->initial = gb; + secp256k1_scalar_add(&ctx->scalar_offset, &b, &diff); + secp256k1_ge_set_gej(&ctx->ge_offset, &gb); + + /* Clean up. */ secp256k1_scalar_clear(&b); secp256k1_gej_clear(&gb); + secp256k1_fe_clear(&f); } #endif /* SECP256K1_ECMULT_GEN_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h index 01339b275..07d8477eb 100644 --- a/src/secp256k1/src/ecmult_impl.h +++ b/src/secp256k1/src/ecmult_impl.h @@ -1,17 +1,20 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/****************************************************************************** + * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php. * + ******************************************************************************/ #ifndef SECP256K1_ECMULT_IMPL_H #define SECP256K1_ECMULT_IMPL_H #include +#include +#include "util.h" #include "group.h" #include "scalar.h" #include "ecmult.h" +#include "precomputed_ecmult.h" #if defined(EXHAUSTIVE_TEST_ORDER) /* We need to lower these values for exhaustive tests because @@ -19,39 +22,57 @@ * affine-isomorphism stuff which tracks z-ratios) */ # if EXHAUSTIVE_TEST_ORDER > 128 # define WINDOW_A 5 -# define WINDOW_G 8 # elif EXHAUSTIVE_TEST_ORDER > 8 # define WINDOW_A 4 -# define WINDOW_G 4 # else # define WINDOW_A 2 -# define WINDOW_G 2 # endif #else /* optimal for 128-bit and 256-bit exponents. */ -#define WINDOW_A 5 -/** larger numbers may result in slightly better performance, at the cost of - exponentially larger precomputed tables. */ -#ifdef USE_ENDOMORPHISM -/** Two tables for window size 15: 1.375 MiB. */ -#define WINDOW_G 15 -#else -/** One table for window size 16: 1.375 MiB. */ -#define WINDOW_G 16 -#endif +# define WINDOW_A 5 +/** Larger values for ECMULT_WINDOW_SIZE result in possibly better + * performance at the cost of an exponentially larger precomputed + * table. The exact table size is + * (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes, + * where sizeof(secp256k1_ge_storage) is typically 64 bytes but can + * be larger due to platform-specific padding and alignment. + * Two tables of this size are used (due to the endomorphism + * optimization). + */ #endif -/** The number of entries a table with precomputed multiples needs to have. */ -#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) +#define WNAF_BITS 128 +#define WNAF_SIZE_BITS(bits, w) CEIL_DIV(bits, w) +#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w) + +/* The number of objects allocated on the scratch space for ecmult_multi algorithms */ +#define PIPPENGER_SCRATCH_OBJECTS 6 +#define STRAUSS_SCRATCH_OBJECTS 5 + +#define PIPPENGER_MAX_BUCKET_WINDOW 12 + +/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */ +#define ECMULT_PIPPENGER_THRESHOLD 88 + +#define ECMULT_MAX_POINTS_PER_BATCH 5000000 -/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain - * the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will - * contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z. - * Prej's Z values are undefined, except for the last value. +/** Fill a table 'pre_a' with precomputed odd multiples of a. + * pre_a will contain [1*a,3*a,...,(2*n-1)*a], so it needs space for n group elements. + * zr needs space for n field elements. + * + * Although pre_a is an array of _ge rather than _gej, it actually represents elements + * in Jacobian coordinates with their z coordinates omitted. The omitted z-coordinates + * can be recovered using z and zr. Using the notation z(b) to represent the omitted + * z coordinate of b: + * - z(pre_a[n-1]) = 'z' + * - z(pre_a[i-1]) = z(pre_a[i]) / zr[i] for n > i > 0 + * + * Lastly the zr[0] value, which isn't used above, is set so that: + * - a.z = z(pre_a[0]) / zr[0] */ -static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, secp256k1_fe *zr, const secp256k1_gej *a) { - secp256k1_gej d; - secp256k1_ge a_ge, d_ge; +static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_ge *pre_a, secp256k1_fe *zr, secp256k1_fe *z, const secp256k1_gej *a) { + secp256k1_gej d, ai; + secp256k1_ge d_ge; int i; VERIFY_CHECK(!a->infinity); @@ -59,170 +80,76 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, sec secp256k1_gej_double_var(&d, a, NULL); /* - * Perform the additions on an isomorphism where 'd' is affine: drop the z coordinate - * of 'd', and scale the 1P starting value's x/y coordinates without changing its z. + * Perform the additions using an isomorphic curve Y^2 = X^3 + 7*C^6 where C := d.z. + * The isomorphism, phi, maps a secp256k1 point (x, y) to the point (x*C^2, y*C^3) on the other curve. + * In Jacobian coordinates phi maps (x, y, z) to (x*C^2, y*C^3, z) or, equivalently to (x, y, z/C). + * + * phi(x, y, z) = (x*C^2, y*C^3, z) = (x, y, z/C) + * d_ge := phi(d) = (d.x, d.y, 1) + * ai := phi(a) = (a.x*C^2, a.y*C^3, a.z) + * + * The group addition functions work correctly on these isomorphic curves. + * In particular phi(d) is easy to represent in affine coordinates under this isomorphism. + * This lets us use the faster secp256k1_gej_add_ge_var group addition function that we wouldn't be able to use otherwise. */ - d_ge.x = d.x; - d_ge.y = d.y; - d_ge.infinity = 0; - - secp256k1_ge_set_gej_zinv(&a_ge, a, &d.z); - prej[0].x = a_ge.x; - prej[0].y = a_ge.y; - prej[0].z = a->z; - prej[0].infinity = 0; + secp256k1_ge_set_xy(&d_ge, &d.x, &d.y); + secp256k1_ge_set_gej_zinv(&pre_a[0], a, &d.z); + secp256k1_gej_set_ge(&ai, &pre_a[0]); + ai.z = a->z; + /* pre_a[0] is the point (a.x*C^2, a.y*C^3, a.z*C) which is equivalent to a. + * Set zr[0] to C, which is the ratio between the omitted z(pre_a[0]) value and a.z. + */ zr[0] = d.z; + for (i = 1; i < n; i++) { - secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); + secp256k1_gej_add_ge_var(&ai, &ai, &d_ge, &zr[i]); + secp256k1_ge_set_xy(&pre_a[i], &ai.x, &ai.y); } - /* - * Each point in 'prej' has a z coordinate too small by a factor of 'd.z'. Only - * the final point's z coordinate is actually used though, so just update that. + /* Multiply the last z-coordinate by C to undo the isomorphism. + * Since the z-coordinates of the pre_a values are implied by the zr array of z-coordinate ratios, + * undoing the isomorphism here undoes the isomorphism for all pre_a values. */ - secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z); + secp256k1_fe_mul(z, &ai.z, &d.z); } -/** Fill a table 'pre' with precomputed odd multiples of a. - * - * There are two versions of this function: - * - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its - * resulting point set to a single constant Z denominator, stores the X and Y - * coordinates as ge_storage points in pre, and stores the global Z in rz. - * It only operates on tables sized for WINDOW_A wnaf multiples. - * - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its - * resulting point set to actually affine points, and stores those in pre. - * It operates on tables of any size, but uses heap-allocated temporaries. - * - * To compute a*P + b*G, we compute a table for P using the first function, - * and for G using the second (which requires an inverse, but it only needs to - * happen once). - */ -static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) { - secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; - - /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a); - /* Bring them to the same Z denominator. */ - secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); +SECP256K1_INLINE static void secp256k1_ecmult_table_verify(int n, int w) { + (void)n; + (void)w; + VERIFY_CHECK(((n) & 1) == 1); + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); } -static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage *pre, const secp256k1_gej *a, const secp256k1_callback *cb) { - secp256k1_gej *prej = (secp256k1_gej*)checked_malloc(cb, sizeof(secp256k1_gej) * n); - secp256k1_ge *prea = (secp256k1_ge*)checked_malloc(cb, sizeof(secp256k1_ge) * n); - secp256k1_fe *zr = (secp256k1_fe*)checked_malloc(cb, sizeof(secp256k1_fe) * n); - int i; - - /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(n, prej, zr, a); - /* Convert them in batch to affine coordinates. */ - secp256k1_ge_set_table_gej_var(prea, prej, zr, n); - /* Convert them to compact storage form. */ - for (i = 0; i < n; i++) { - secp256k1_ge_to_storage(&pre[i], &prea[i]); +SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge(secp256k1_ge *r, const secp256k1_ge *pre, int n, int w) { + secp256k1_ecmult_table_verify(n,w); + if (n > 0) { + *r = pre[(n-1)/2]; + } else { + *r = pre[(-n-1)/2]; + secp256k1_fe_negate(&(r->y), &(r->y), 1); } - - free(prea); - free(prej); - free(zr); -} - -/** The following two macro retrieves a particular odd multiple from a table - * of precomputed multiples. */ -#define ECMULT_TABLE_GET_GE(r,pre,n,w) do { \ - VERIFY_CHECK(((n) & 1) == 1); \ - VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ - VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ - if ((n) > 0) { \ - *(r) = (pre)[((n)-1)/2]; \ - } else { \ - secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \ - } \ -} while(0) - -#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \ - VERIFY_CHECK(((n) & 1) == 1); \ - VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ - VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ - if ((n) > 0) { \ - secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \ - } else { \ - secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \ - secp256k1_ge_neg((r), (r)); \ - } \ -} while(0) - -static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { - ctx->pre_g = NULL; -#ifdef USE_ENDOMORPHISM - ctx->pre_g_128 = NULL; -#endif } -static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) { - secp256k1_gej gj; - - if (ctx->pre_g != NULL) { - return; - } - - /* get the generator */ - secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); - - ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); - - /* precompute the tables with odd multiples */ - secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb); - -#ifdef USE_ENDOMORPHISM - { - secp256k1_gej g_128j; - int i; - - ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); - - /* calculate 2^128*generator */ - g_128j = gj; - for (i = 0; i < 128; i++) { - secp256k1_gej_double_var(&g_128j, &g_128j, NULL); - } - secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb); +SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_lambda(secp256k1_ge *r, const secp256k1_ge *pre, const secp256k1_fe *x, int n, int w) { + secp256k1_ecmult_table_verify(n,w); + if (n > 0) { + secp256k1_ge_set_xy(r, &x[(n-1)/2], &pre[(n-1)/2].y); + } else { + secp256k1_ge_set_xy(r, &x[(-n-1)/2], &pre[(-n-1)/2].y); + secp256k1_fe_negate(&(r->y), &(r->y), 1); } -#endif } -static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, - const secp256k1_ecmult_context *src, const secp256k1_callback *cb) { - if (src->pre_g == NULL) { - dst->pre_g = NULL; +SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_storage(secp256k1_ge *r, const secp256k1_ge_storage *pre, int n, int w) { + secp256k1_ecmult_table_verify(n,w); + if (n > 0) { + secp256k1_ge_from_storage(r, &pre[(n-1)/2]); } else { - size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); - dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); - memcpy(dst->pre_g, src->pre_g, size); + secp256k1_ge_from_storage(r, &pre[(-n-1)/2]); + secp256k1_fe_negate(&(r->y), &(r->y), 1); } -#ifdef USE_ENDOMORPHISM - if (src->pre_g_128 == NULL) { - dst->pre_g_128 = NULL; - } else { - size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); - dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); - memcpy(dst->pre_g_128, src->pre_g_128, size); - } -#endif -} - -static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx) { - return ctx->pre_g != NULL; -} - -static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { - free(ctx->pre_g); -#ifdef USE_ENDOMORPHISM - free(ctx->pre_g_128); -#endif - secp256k1_ecmult_context_init(ctx); } /** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits), @@ -233,7 +160,7 @@ static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { * than the number of bits in the (absolute value) of the input. */ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) { - secp256k1_scalar s = *a; + secp256k1_scalar s; int last_set_bit = -1; int bit = 0; int sign = 1; @@ -246,7 +173,8 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, memset(wnaf, 0, len * sizeof(wnaf[0])); - if (secp256k1_scalar_get_bits(&s, 255, 1)) { + s = *a; + if (secp256k1_scalar_get_bits_limb32(&s, 255, 1)) { secp256k1_scalar_negate(&s, &s); sign = -1; } @@ -254,7 +182,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, while (bit < len) { int now; int word; - if (secp256k1_scalar_get_bits(&s, bit, 1) == (unsigned int)carry) { + if (secp256k1_scalar_get_bits_limb32(&s, bit, 1) == (unsigned int)carry) { bit++; continue; } @@ -275,127 +203,139 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, bit += now; } #ifdef VERIFY - CHECK(carry == 0); - while (bit < 256) { - CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); - } + { + int verify_bit = bit; + + VERIFY_CHECK(carry == 0); + + while (verify_bit < 256) { + VERIFY_CHECK(secp256k1_scalar_get_bits_limb32(&s, verify_bit, 1) == 0); + verify_bit++; + } + } #endif return last_set_bit + 1; } -static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { - secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; +struct secp256k1_strauss_point_state { + int wnaf_na_1[129]; + int wnaf_na_lam[129]; + int bits_na_1; + int bits_na_lam; +}; + +struct secp256k1_strauss_state { + /* aux is used to hold z-ratios, and then used to hold pre_a[i].x * BETA values. */ + secp256k1_fe* aux; + secp256k1_ge* pre_a; + struct secp256k1_strauss_point_state* ps; +}; + +static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state *state, secp256k1_gej *r, size_t num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { secp256k1_ge tmpa; secp256k1_fe Z; -#ifdef USE_ENDOMORPHISM - secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; - secp256k1_scalar na_1, na_lam; - /* Splitted G factors. */ + /* Split G factors. */ secp256k1_scalar ng_1, ng_128; - int wnaf_na_1[130]; - int wnaf_na_lam[130]; - int bits_na_1; - int bits_na_lam; int wnaf_ng_1[129]; - int bits_ng_1; + int bits_ng_1 = 0; int wnaf_ng_128[129]; - int bits_ng_128; -#else - int wnaf_na[256]; - int bits_na; - int wnaf_ng[256]; - int bits_ng; -#endif + int bits_ng_128 = 0; int i; - int bits; + int bits = 0; + size_t np; + size_t no = 0; + + secp256k1_fe_set_int(&Z, 1); + for (np = 0; np < num; ++np) { + secp256k1_gej tmp; + secp256k1_scalar na_1, na_lam; + if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) { + continue; + } + /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&na_1, &na_lam, &na[np]); + + /* build wnaf representation for na_1 and na_lam. */ + state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 129, &na_1, WINDOW_A); + state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 129, &na_lam, WINDOW_A); + VERIFY_CHECK(state->ps[no].bits_na_1 <= 129); + VERIFY_CHECK(state->ps[no].bits_na_lam <= 129); + if (state->ps[no].bits_na_1 > bits) { + bits = state->ps[no].bits_na_1; + } + if (state->ps[no].bits_na_lam > bits) { + bits = state->ps[no].bits_na_lam; + } -#ifdef USE_ENDOMORPHISM - /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ - secp256k1_scalar_split_lambda(&na_1, &na_lam, na); + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + * The exception is the precomputed G table points, which are actually + * affine. Compared to the base used for other points, they have a Z ratio + * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same + * isomorphism to efficiently add with a known Z inverse. + */ + tmp = a[np]; + if (no) { + secp256k1_gej_rescale(&tmp, &Z); + } + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp); + if (no) secp256k1_fe_mul(state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &(a[np].z)); - /* build wnaf representation for na_1 and na_lam. */ - bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, 130, &na_1, WINDOW_A); - bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, 130, &na_lam, WINDOW_A); - VERIFY_CHECK(bits_na_1 <= 130); - VERIFY_CHECK(bits_na_lam <= 130); - bits = bits_na_1; - if (bits_na_lam > bits) { - bits = bits_na_lam; + ++no; } -#else - /* build wnaf representation for na. */ - bits_na = secp256k1_ecmult_wnaf(wnaf_na, 256, na, WINDOW_A); - bits = bits_na; -#endif - /* Calculate odd multiples of a. - * All multiples are brought to the same Z 'denominator', which is stored - * in Z. Due to secp256k1' isomorphism we can do all operations pretending - * that the Z coordinate was 1, use affine addition formulae, and correct - * the Z coordinate of the result once at the end. - * The exception is the precomputed G table points, which are actually - * affine. Compared to the base used for other points, they have a Z ratio - * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same - * isomorphism to efficiently add with a known Z inverse. - */ - secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, a); + /* Bring them to the same Z denominator. */ + if (no) { + secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->aux); + } -#ifdef USE_ENDOMORPHISM - for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + for (np = 0; np < no; ++np) { + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_fe_mul(&state->aux[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i].x, &secp256k1_const_beta); + } } - /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ - secp256k1_scalar_split_128(&ng_1, &ng_128, ng); + if (ng) { + /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ + secp256k1_scalar_split_128(&ng_1, &ng_128, ng); - /* Build wnaf representation for ng_1 and ng_128 */ - bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); - bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); - if (bits_ng_1 > bits) { - bits = bits_ng_1; - } - if (bits_ng_128 > bits) { - bits = bits_ng_128; - } -#else - bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G); - if (bits_ng > bits) { - bits = bits_ng; + /* Build wnaf representation for ng_1 and ng_128 */ + bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); + bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); + if (bits_ng_1 > bits) { + bits = bits_ng_1; + } + if (bits_ng_128 > bits) { + bits = bits_ng_128; + } } -#endif secp256k1_gej_set_infinity(r); for (i = bits - 1; i >= 0; i--) { int n; secp256k1_gej_double_var(r, r, NULL); -#ifdef USE_ENDOMORPHISM - if (i < bits_na_1 && (n = wnaf_na_1[i])) { - ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); - secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); - } - if (i < bits_na_lam && (n = wnaf_na_lam[i])) { - ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); - secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + for (np = 0; np < no; ++np) { + if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) { + secp256k1_ecmult_table_get_ge(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) { + secp256k1_ecmult_table_get_ge_lambda(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } } if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { - ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_ecmult_table_get_ge_storage(&tmpa, secp256k1_pre_g, n, WINDOW_G); secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); } if (i < bits_ng_128 && (n = wnaf_ng_128[i])) { - ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G); - secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); - } -#else - if (i < bits_na && (n = wnaf_na[i])) { - ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); - secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); - } - if (i < bits_ng && (n = wnaf_ng[i])) { - ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_ecmult_table_get_ge_storage(&tmpa, secp256k1_pre_g_128, n, WINDOW_G); secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); } -#endif } if (!r->infinity) { @@ -403,4 +343,521 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej } } +static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { + secp256k1_fe aux[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + struct secp256k1_strauss_point_state ps[1]; + struct secp256k1_strauss_state state; + + state.aux = aux; + state.pre_a = pre_a; + state.ps = ps; + secp256k1_ecmult_strauss_wnaf(&state, r, 1, a, na, ng); +} + +static size_t secp256k1_strauss_scratch_size(size_t n_points) { + static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); + return n_points*point_size; +} + +static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { + secp256k1_gej* points; + secp256k1_scalar* scalars; + struct secp256k1_strauss_state state; + size_t i; + const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); + + secp256k1_gej_set_infinity(r); + if (inp_g_sc == NULL && n_points == 0) { + return 1; + } + + /* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space. If these + * allocations change, make sure to update the STRAUSS_SCRATCH_OBJECTS + * constant and strauss_scratch_size accordingly. */ + points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej)); + scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar)); + state.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); + state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); + state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); + + if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.ps == NULL) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 0; + } + + for (i = 0; i < n_points; i++) { + secp256k1_ge point; + if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 0; + } + secp256k1_gej_set_ge(&points[i], &point); + } + secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc); + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 1; +} + +/* Wrapper for secp256k1_ecmult_multi_func interface */ +static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { + return secp256k1_ecmult_strauss_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0); +} + +static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) { + return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1); +} + +/** Convert a number to WNAF notation. + * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. + * It has the following guarantees: + * - each wnaf[i] is either 0 or an odd integer between -(1 << w) and (1 << w) + * - the number of words set is always WNAF_SIZE(w) + * - the returned skew is 0 or 1 + */ +static int secp256k1_wnaf_fixed(int *wnaf, const secp256k1_scalar *s, int w) { + int skew = 0; + int pos; + int max_pos; + int last_w; + const secp256k1_scalar *work = s; + + if (secp256k1_scalar_is_zero(s)) { + for (pos = 0; pos < WNAF_SIZE(w); pos++) { + wnaf[pos] = 0; + } + return 0; + } + + if (secp256k1_scalar_is_even(s)) { + skew = 1; + } + + wnaf[0] = secp256k1_scalar_get_bits_var(work, 0, w) + skew; + /* Compute last window size. Relevant when window size doesn't divide the + * number of bits in the scalar */ + last_w = WNAF_BITS - (WNAF_SIZE(w) - 1) * w; + + /* Store the position of the first nonzero word in max_pos to allow + * skipping leading zeros when calculating the wnaf. */ + for (pos = WNAF_SIZE(w) - 1; pos > 0; pos--) { + int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w); + if(val != 0) { + break; + } + wnaf[pos] = 0; + } + max_pos = pos; + pos = 1; + + while (pos <= max_pos) { + int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w); + if ((val & 1) == 0) { + wnaf[pos - 1] -= (1 << w); + wnaf[pos] = (val + 1); + } else { + wnaf[pos] = val; + } + /* Set a coefficient to zero if it is 1 or -1 and the proceeding digit + * is strictly negative or strictly positive respectively. Only change + * coefficients at previous positions because above code assumes that + * wnaf[pos - 1] is odd. + */ + if (pos >= 2 && ((wnaf[pos - 1] == 1 && wnaf[pos - 2] < 0) || (wnaf[pos - 1] == -1 && wnaf[pos - 2] > 0))) { + if (wnaf[pos - 1] == 1) { + wnaf[pos - 2] += 1 << w; + } else { + wnaf[pos - 2] -= 1 << w; + } + wnaf[pos - 1] = 0; + } + ++pos; + } + + return skew; +} + +struct secp256k1_pippenger_point_state { + int skew_na; + size_t input_pos; +}; + +struct secp256k1_pippenger_state { + int *wnaf_na; + struct secp256k1_pippenger_point_state* ps; +}; + +/* + * pippenger_wnaf computes the result of a multi-point multiplication as + * follows: The scalars are brought into wnaf with n_wnaf elements each. Then + * for every i < n_wnaf, first each point is added to a "bucket" corresponding + * to the point's wnaf[i]. Second, the buckets are added together such that + * r += 1*bucket[0] + 3*bucket[1] + 5*bucket[2] + ... + */ +static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_window, struct secp256k1_pippenger_state *state, secp256k1_gej *r, const secp256k1_scalar *sc, const secp256k1_ge *pt, size_t num) { + size_t n_wnaf = WNAF_SIZE(bucket_window+1); + size_t np; + size_t no = 0; + int i; + int j; + + for (np = 0; np < num; ++np) { + if (secp256k1_scalar_is_zero(&sc[np]) || secp256k1_ge_is_infinity(&pt[np])) { + continue; + } + state->ps[no].input_pos = np; + state->ps[no].skew_na = secp256k1_wnaf_fixed(&state->wnaf_na[no*n_wnaf], &sc[np], bucket_window+1); + no++; + } + secp256k1_gej_set_infinity(r); + + if (no == 0) { + return 1; + } + + for (i = n_wnaf - 1; i >= 0; i--) { + secp256k1_gej running_sum; + + for(j = 0; j < ECMULT_TABLE_SIZE(bucket_window+2); j++) { + secp256k1_gej_set_infinity(&buckets[j]); + } + + for (np = 0; np < no; ++np) { + int n = state->wnaf_na[np*n_wnaf + i]; + struct secp256k1_pippenger_point_state point_state = state->ps[np]; + secp256k1_ge tmp; + int idx; + + if (i == 0) { + /* correct for wnaf skew */ + int skew = point_state.skew_na; + if (skew) { + secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]); + secp256k1_gej_add_ge_var(&buckets[0], &buckets[0], &tmp, NULL); + } + } + if (n > 0) { + idx = (n - 1)/2; + secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &pt[point_state.input_pos], NULL); + } else if (n < 0) { + idx = -(n + 1)/2; + secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]); + secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &tmp, NULL); + } + } + + for(j = 0; j < bucket_window; j++) { + secp256k1_gej_double_var(r, r, NULL); + } + + secp256k1_gej_set_infinity(&running_sum); + /* Accumulate the sum: bucket[0] + 3*bucket[1] + 5*bucket[2] + 7*bucket[3] + ... + * = bucket[0] + bucket[1] + bucket[2] + bucket[3] + ... + * + 2 * (bucket[1] + 2*bucket[2] + 3*bucket[3] + ...) + * using an intermediate running sum: + * running_sum = bucket[0] + bucket[1] + bucket[2] + ... + * + * The doubling is done implicitly by deferring the final window doubling (of 'r'). + */ + for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j > 0; j--) { + secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[j], NULL); + secp256k1_gej_add_var(r, r, &running_sum, NULL); + } + + secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[0], NULL); + secp256k1_gej_double_var(r, r, NULL); + secp256k1_gej_add_var(r, r, &running_sum, NULL); + } + return 1; +} + +/** + * Returns optimal bucket_window (number of bits of a scalar represented by a + * set of buckets) for a given number of points. + */ +static int secp256k1_pippenger_bucket_window(size_t n) { + if (n <= 1) { + return 1; + } else if (n <= 4) { + return 2; + } else if (n <= 20) { + return 3; + } else if (n <= 57) { + return 4; + } else if (n <= 136) { + return 5; + } else if (n <= 235) { + return 6; + } else if (n <= 1260) { + return 7; + } else if (n <= 4420) { + return 9; + } else if (n <= 7880) { + return 10; + } else if (n <= 16050) { + return 11; + } else { + return PIPPENGER_MAX_BUCKET_WINDOW; + } +} + +/** + * Returns the maximum optimal number of points for a bucket_window. + */ +static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) { + switch(bucket_window) { + case 1: return 1; + case 2: return 4; + case 3: return 20; + case 4: return 57; + case 5: return 136; + case 6: return 235; + case 7: return 1260; + case 8: return 1260; + case 9: return 4420; + case 10: return 7880; + case 11: return 16050; + case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX; + } + return 0; +} + + +SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) { + secp256k1_scalar tmp = *s1; + secp256k1_scalar_split_lambda(s1, s2, &tmp); + secp256k1_ge_mul_lambda(p2, p1); + + if (secp256k1_scalar_is_high(s1)) { + secp256k1_scalar_negate(s1, s1); + secp256k1_ge_neg(p1, p1); + } + if (secp256k1_scalar_is_high(s2)) { + secp256k1_scalar_negate(s2, s2); + secp256k1_ge_neg(p2, p2); + } +} + +/** + * Returns the scratch size required for a given number of points (excluding + * base point G) without considering alignment. + */ +static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) { + size_t entries = 2*n_points + 2; + size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int); + return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size; +} + +static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { + const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch); + /* Use 2(n+1) with the endomorphism, when calculating batch + * sizes. The reason for +1 is that we add the G scalar to the list of + * other scalars. */ + size_t entries = 2*n_points + 2; + secp256k1_ge *points; + secp256k1_scalar *scalars; + secp256k1_gej *buckets; + struct secp256k1_pippenger_state *state_space; + size_t idx = 0; + size_t point_idx = 0; + int i, j; + int bucket_window; + + secp256k1_gej_set_infinity(r); + if (inp_g_sc == NULL && n_points == 0) { + return 1; + } + bucket_window = secp256k1_pippenger_bucket_window(n_points); + + /* We allocate PIPPENGER_SCRATCH_OBJECTS objects on the scratch space. If + * these allocations change, make sure to update the + * PIPPENGER_SCRATCH_OBJECTS constant and pippenger_scratch_size + * accordingly. */ + points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points)); + scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars)); + state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space)); + if (points == NULL || scalars == NULL || state_space == NULL) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 0; + } + state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps)); + state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int)); + buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, ((size_t)1 << bucket_window) * sizeof(*buckets)); + if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 0; + } + + if (inp_g_sc != NULL) { + scalars[0] = *inp_g_sc; + points[0] = secp256k1_ge_const_g; + idx++; + secp256k1_ecmult_endo_split(&scalars[0], &scalars[1], &points[0], &points[1]); + idx++; + } + + while (point_idx < n_points) { + if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) { + secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); + return 0; + } + idx++; + secp256k1_ecmult_endo_split(&scalars[idx - 1], &scalars[idx], &points[idx - 1], &points[idx]); + idx++; + point_idx++; + } + + secp256k1_ecmult_pippenger_wnaf(buckets, bucket_window, state_space, r, scalars, points, idx); + + /* Clear data */ + for(i = 0; (size_t)i < idx; i++) { + secp256k1_scalar_clear(&scalars[i]); + state_space->ps[i].skew_na = 0; + for(j = 0; j < WNAF_SIZE(bucket_window+1); j++) { + state_space->wnaf_na[i * WNAF_SIZE(bucket_window+1) + j] = 0; + } + } + for(i = 0; i < 1< max_alloc) { + break; + } + space_for_points = max_alloc - space_overhead; + + n_points = space_for_points/entry_size; + n_points = n_points > max_points ? max_points : n_points; + if (n_points > res) { + res = n_points; + } + if (n_points < max_points) { + /* A larger bucket_window may support even more points. But if we + * would choose that then the caller couldn't safely use any number + * smaller than what this function returns */ + break; + } + } + return res; +} + +/* Computes ecmult_multi by simply multiplying and adding each point. Does not + * require a scratch space */ +static int secp256k1_ecmult_multi_simple_var(secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) { + size_t point_idx; + secp256k1_gej tmpj; + + secp256k1_gej_set_infinity(r); + secp256k1_gej_set_infinity(&tmpj); + /* r = inp_g_sc*G */ + secp256k1_ecmult(r, &tmpj, &secp256k1_scalar_zero, inp_g_sc); + for (point_idx = 0; point_idx < n_points; point_idx++) { + secp256k1_ge point; + secp256k1_gej pointj; + secp256k1_scalar scalar; + if (!cb(&scalar, &point, point_idx, cbdata)) { + return 0; + } + /* r += scalar*point */ + secp256k1_gej_set_ge(&pointj, &point); + secp256k1_ecmult(&tmpj, &pointj, &scalar, NULL); + secp256k1_gej_add_var(r, r, &tmpj, NULL); + } + return 1; +} + +/* Compute the number of batches and the batch size given the maximum batch size and the + * total number of points */ +static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n_batch_points, size_t max_n_batch_points, size_t n) { + if (max_n_batch_points == 0) { + return 0; + } + if (max_n_batch_points > ECMULT_MAX_POINTS_PER_BATCH) { + max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH; + } + if (n == 0) { + *n_batches = 0; + *n_batch_points = 0; + return 1; + } + /* Compute ceil(n/max_n_batch_points) and ceil(n/n_batches) */ + *n_batches = CEIL_DIV(n, max_n_batch_points); + *n_batch_points = CEIL_DIV(n, *n_batches); + return 1; +} + +typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_callback* error_callback, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t); +static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { + size_t i; + + int (*f)(const secp256k1_callback* error_callback, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t); + size_t n_batches; + size_t n_batch_points; + + secp256k1_gej_set_infinity(r); + if (inp_g_sc == NULL && n == 0) { + return 1; + } else if (n == 0) { + secp256k1_ecmult(r, r, &secp256k1_scalar_zero, inp_g_sc); + return 1; + } + if (scratch == NULL) { + return secp256k1_ecmult_multi_simple_var(r, inp_g_sc, cb, cbdata, n); + } + + /* Compute the batch sizes for Pippenger's algorithm given a scratch space. If it's greater than + * a threshold use Pippenger's algorithm. Otherwise use Strauss' algorithm. + * As a first step check if there's enough space for Pippenger's algo (which requires less space + * than Strauss' algo) and if not, use the simple algorithm. */ + if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(error_callback, scratch), n)) { + return secp256k1_ecmult_multi_simple_var(r, inp_g_sc, cb, cbdata, n); + } + if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) { + f = secp256k1_ecmult_pippenger_batch; + } else { + if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(error_callback, scratch), n)) { + return secp256k1_ecmult_multi_simple_var(r, inp_g_sc, cb, cbdata, n); + } + f = secp256k1_ecmult_strauss_batch; + } + for(i = 0; i < n_batches; i++) { + size_t nbp = n < n_batch_points ? n : n_batch_points; + size_t offset = n_batch_points*i; + secp256k1_gej tmp; + if (!f(error_callback, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) { + return 0; + } + secp256k1_gej_add_var(r, r, &tmp, NULL); + n -= nbp; + } + return 1; +} + #endif /* SECP256K1_ECMULT_IMPL_H */ diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h index f07884545..8c65a3aff 100644 --- a/src/secp256k1/src/field.h +++ b/src/secp256k1/src/field.h @@ -1,132 +1,355 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_FIELD_H #define SECP256K1_FIELD_H -/** Field element module. - * - * Field elements can be represented in several ways, but code accessing - * it (and implementations) need to take certain properties into account: - * - Each field element can be normalized or not. - * - Each field element has a magnitude, which represents how far away - * its representation is away from normalization. Normalized elements - * always have a magnitude of 1, but a magnitude of 1 doesn't imply - * normality. - */ +#include "util.h" -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" +/* This file defines the generic interface for working with secp256k1_fe + * objects, which represent field elements (integers modulo 2^256 - 2^32 - 977). + * + * The actual definition of the secp256k1_fe type depends on the chosen field + * implementation; see the field_5x52.h and field_10x26.h files for details. + * + * All secp256k1_fe objects have implicit properties that determine what + * operations are permitted on it. These are purely a function of what + * secp256k1_fe_ operations are applied on it, generally (implicitly) fixed at + * compile time, and do not depend on the chosen field implementation. Despite + * that, what these properties actually entail for the field representation + * values depends on the chosen field implementation. These properties are: + * - magnitude: an integer in [0,32] + * - normalized: 0 or 1; normalized=1 implies magnitude <= 1. + * + * In VERIFY mode, they are materialized explicitly as fields in the struct, + * allowing run-time verification of these properties. In that case, the field + * implementation also provides a secp256k1_fe_verify routine to verify that + * these fields match the run-time value and perform internal consistency + * checks. */ +#ifdef VERIFY +# define SECP256K1_FE_VERIFY_FIELDS \ + int magnitude; \ + int normalized; +#else +# define SECP256K1_FE_VERIFY_FIELDS #endif -#if defined(USE_FIELD_10X26) -#include "field_10x26.h" -#elif defined(USE_FIELD_5X52) +#if defined(SECP256K1_WIDEMUL_INT128) #include "field_5x52.h" +#elif defined(SECP256K1_WIDEMUL_INT64) +#include "field_10x26.h" #else -#error "Please select field implementation" +#error "Please select wide multiplication implementation" #endif -#include "util.h" +#ifdef VERIFY +/* Magnitude and normalized value for constants. */ +#define SECP256K1_FE_VERIFY_CONST(d7, d6, d5, d4, d3, d2, d1, d0) \ + /* Magnitude is 0 for constant 0; 1 otherwise. */ \ + , (((d7) | (d6) | (d5) | (d4) | (d3) | (d2) | (d1) | (d0)) != 0) \ + /* Normalized is 1 unless sum(d_i<<(32*i) for i=0..7) exceeds field modulus. */ \ + , (!(((d7) & (d6) & (d5) & (d4) & (d3) & (d2)) == 0xfffffffful && ((d1) == 0xfffffffful || ((d1) == 0xfffffffe && (d0 >= 0xfffffc2f))))) +#else +#define SECP256K1_FE_VERIFY_CONST(d7, d6, d5, d4, d3, d2, d1, d0) +#endif -/** Normalize a field element. */ +/** This expands to an initializer for a secp256k1_fe valued sum((i*32) * d_i, i=0..7) mod p. + * + * It has magnitude 1, unless d_i are all 0, in which case the magnitude is 0. + * It is normalized, unless sum(2^(i*32) * d_i, i=0..7) >= p. + * + * SECP256K1_FE_CONST_INNER is provided by the implementation. + */ +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)) SECP256K1_FE_VERIFY_CONST((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)) } + +static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); +static const secp256k1_fe secp256k1_const_beta = SECP256K1_FE_CONST( + 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, + 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul +); + +#ifndef VERIFY +/* In non-VERIFY mode, we #define the fe operations to be identical to their + * internal field implementation, to avoid the potential overhead of a + * function call (even though presumably inlinable). */ +# define secp256k1_fe_normalize secp256k1_fe_impl_normalize +# define secp256k1_fe_normalize_weak secp256k1_fe_impl_normalize_weak +# define secp256k1_fe_normalize_var secp256k1_fe_impl_normalize_var +# define secp256k1_fe_normalizes_to_zero secp256k1_fe_impl_normalizes_to_zero +# define secp256k1_fe_normalizes_to_zero_var secp256k1_fe_impl_normalizes_to_zero_var +# define secp256k1_fe_set_int secp256k1_fe_impl_set_int +# define secp256k1_fe_clear secp256k1_fe_impl_clear +# define secp256k1_fe_is_zero secp256k1_fe_impl_is_zero +# define secp256k1_fe_is_odd secp256k1_fe_impl_is_odd +# define secp256k1_fe_cmp_var secp256k1_fe_impl_cmp_var +# define secp256k1_fe_set_b32_mod secp256k1_fe_impl_set_b32_mod +# define secp256k1_fe_set_b32_limit secp256k1_fe_impl_set_b32_limit +# define secp256k1_fe_get_b32 secp256k1_fe_impl_get_b32 +# define secp256k1_fe_negate_unchecked secp256k1_fe_impl_negate_unchecked +# define secp256k1_fe_mul_int_unchecked secp256k1_fe_impl_mul_int_unchecked +# define secp256k1_fe_add secp256k1_fe_impl_add +# define secp256k1_fe_mul secp256k1_fe_impl_mul +# define secp256k1_fe_sqr secp256k1_fe_impl_sqr +# define secp256k1_fe_cmov secp256k1_fe_impl_cmov +# define secp256k1_fe_to_storage secp256k1_fe_impl_to_storage +# define secp256k1_fe_from_storage secp256k1_fe_impl_from_storage +# define secp256k1_fe_inv secp256k1_fe_impl_inv +# define secp256k1_fe_inv_var secp256k1_fe_impl_inv_var +# define secp256k1_fe_get_bounds secp256k1_fe_impl_get_bounds +# define secp256k1_fe_half secp256k1_fe_impl_half +# define secp256k1_fe_add_int secp256k1_fe_impl_add_int +# define secp256k1_fe_is_square_var secp256k1_fe_impl_is_square_var +#endif /* !defined(VERIFY) */ + +/** Normalize a field element. + * + * On input, r must be a valid field element. + * On output, r represents the same value but has normalized=1 and magnitude=1. + */ static void secp256k1_fe_normalize(secp256k1_fe *r); -/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */ +/** Give a field element magnitude 1. + * + * On input, r must be a valid field element. + * On output, r represents the same value but has magnitude=1. Normalized is unchanged. + */ static void secp256k1_fe_normalize_weak(secp256k1_fe *r); -/** Normalize a field element, without constant-time guarantee. */ +/** Normalize a field element, without constant-time guarantee. + * + * Identical in behavior to secp256k1_fe_normalize, but not constant time in r. + */ static void secp256k1_fe_normalize_var(secp256k1_fe *r); -/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field - * implementation may optionally normalize the input, but this should not be relied upon. */ -static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r); +/** Determine whether r represents field element 0. + * + * On input, r must be a valid field element. + * Returns whether r = 0 (mod p). + */ +static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r); -/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field - * implementation may optionally normalize the input, but this should not be relied upon. */ -static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r); +/** Determine whether r represents field element 0, without constant-time guarantee. + * + * Identical in behavior to secp256k1_normalizes_to_zero, but not constant time in r. + */ +static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r); -/** Set a field element equal to a small integer. Resulting field element is normalized. */ +/** Set a field element to an integer in range [0,0x7FFF]. + * + * On input, r does not need to be initialized, a must be in [0,0x7FFF]. + * On output, r represents value a, is normalized and has magnitude (a!=0). + */ static void secp256k1_fe_set_int(secp256k1_fe *r, int a); -/** Sets a field element equal to zero, initializing all fields. */ +/** Set a field element to 0. + * + * On input, a does not need to be initialized. + * On output, a represents 0, is normalized and has magnitude 0. + */ static void secp256k1_fe_clear(secp256k1_fe *a); -/** Verify whether a field element is zero. Requires the input to be normalized. */ +/** Determine whether a represents field element 0. + * + * On input, a must be a valid normalized field element. + * Returns whether a = 0 (mod p). + * + * This behaves identical to secp256k1_normalizes_to_zero{,_var}, but requires + * normalized input (and is much faster). + */ static int secp256k1_fe_is_zero(const secp256k1_fe *a); -/** Check the "oddness" of a field element. Requires the input to be normalized. */ +/** Determine whether a (mod p) is odd. + * + * On input, a must be a valid normalized field element. + * Returns (int(a) mod p) & 1. + */ static int secp256k1_fe_is_odd(const secp256k1_fe *a); -/** Compare two field elements. Requires magnitude-1 inputs. */ +/** Determine whether two field elements are equal. + * + * On input, a and b must be valid field elements with magnitudes not exceeding + * 1 and 31, respectively. + * Returns a = b (mod p). + */ static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b); -/** Same as secp256k1_fe_equal, but may be variable time. */ -static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b); - -/** Compare two field elements. Requires both inputs to be normalized */ +/** Compare the values represented by 2 field elements, without constant-time guarantee. + * + * On input, a and b must be valid normalized field elements. + * Returns 1 if a > b, -1 if a < b, and 0 if a = b (comparisons are done as integers + * in range 0..p-1). + */ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b); -/** Set a field element equal to 32-byte big endian value. If successful, the resulting field element is normalized. */ -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a); +/** Set a field element equal to the element represented by a provided 32-byte big endian value + * interpreted modulo p. + * + * On input, r does not need to be initialized. a must be a pointer to an initialized 32-byte array. + * On output, r = a (mod p). It will have magnitude 1, and not be normalized. + */ +static void secp256k1_fe_set_b32_mod(secp256k1_fe *r, const unsigned char *a); + +/** Set a field element equal to a provided 32-byte big endian value, checking for overflow. + * + * On input, r does not need to be initialized. a must be a pointer to an initialized 32-byte array. + * On output, r = a if (a < p), it will be normalized with magnitude 1, and 1 is returned. + * If a >= p, 0 is returned, and r will be made invalid (and must not be used without overwriting). + */ +static int secp256k1_fe_set_b32_limit(secp256k1_fe *r, const unsigned char *a); -/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +/** Convert a field element to 32-byte big endian byte array. + * On input, a must be a valid normalized field element, and r a pointer to a 32-byte array. + * On output, r = a (mod p). + */ static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a); -/** Set a field element equal to the additive inverse of another. Takes a maximum magnitude of the input - * as an argument. The magnitude of the output is one higher. */ -static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m); +/** Negate a field element. + * + * On input, r does not need to be initialized. a must be a valid field element with + * magnitude not exceeding m. m must be an integer constant expression in [0,31]. + * Performs {r = -a}. + * On output, r will not be normalized, and will have magnitude m+1. + */ +#define secp256k1_fe_negate(r, a, m) ASSERT_INT_CONST_AND_DO(m, secp256k1_fe_negate_unchecked(r, a, m)) + +/** Like secp256k1_fe_negate_unchecked but m is not checked to be an integer constant expression. + * + * Should not be called directly outside of tests. + */ +static void secp256k1_fe_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m); + +/** Add a small integer to a field element. + * + * Performs {r += a}. The magnitude of r increases by 1, and normalized is cleared. + * a must be in range [0,0x7FFF]. + */ +static void secp256k1_fe_add_int(secp256k1_fe *r, int a); + +/** Multiply a field element with a small integer. + * + * On input, r must be a valid field element. a must be an integer constant expression in [0,32]. + * The magnitude of r times a must not exceed 32. + * Performs {r *= a}. + * On output, r's magnitude is multiplied by a, and r will not be normalized. + */ +#define secp256k1_fe_mul_int(r, a) ASSERT_INT_CONST_AND_DO(a, secp256k1_fe_mul_int_unchecked(r, a)) -/** Multiplies the passed field element with a small integer constant. Multiplies the magnitude by that - * small integer. */ -static void secp256k1_fe_mul_int(secp256k1_fe *r, int a); +/** Like secp256k1_fe_mul_int but a is not checked to be an integer constant expression. + * + * Should not be called directly outside of tests. + */ +static void secp256k1_fe_mul_int_unchecked(secp256k1_fe *r, int a); -/** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */ +/** Increment a field element by another. + * + * On input, r and a must be valid field elements, not necessarily normalized. + * The sum of their magnitudes must not exceed 32. + * Performs {r += a}. + * On output, r will not be normalized, and will have magnitude incremented by a's. + */ static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a); -/** Sets a field element to be the product of two others. Requires the inputs' magnitudes to be at most 8. - * The output magnitude is 1 (but not guaranteed to be normalized). */ +/** Multiply two field elements. + * + * On input, a and b must be valid field elements; r does not need to be initialized. + * r and a may point to the same object, but neither may point to the object pointed + * to by b. The magnitudes of a and b must not exceed 8. + * Performs {r = a * b} + * On output, r will have magnitude 1, but won't be normalized. + */ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b); -/** Sets a field element to be the square of another. Requires the input's magnitude to be at most 8. - * The output magnitude is 1 (but not guaranteed to be normalized). */ +/** Square a field element. + * + * On input, a must be a valid field element; r does not need to be initialized. The magnitude + * of a must not exceed 8. + * Performs {r = a**2} + * On output, r will have magnitude 1, but won't be normalized. + */ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); -/** If a has a square root, it is computed in r and 1 is returned. If a does not - * have a square root, the root of its negation is computed and 0 is returned. - * The input's magnitude can be at most 8. The output magnitude is 1 (but not - * guaranteed to be normalized). The result in r will always be a square - * itself. */ -static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a); - -/** Checks whether a field element is a quadratic residue. */ -static int secp256k1_fe_is_quad_var(const secp256k1_fe *a); +/** Compute a square root of a field element. + * + * On input, a must be a valid field element with magnitude<=8; r need not be initialized. + * If sqrt(a) exists, performs {r = sqrt(a)} and returns 1. + * Otherwise, sqrt(-a) exists. The function performs {r = sqrt(-a)} and returns 0. + * The resulting value represented by r will be a square itself. + * Variables r and a must not point to the same object. + * On output, r will have magnitude 1 but will not be normalized. + */ +static int secp256k1_fe_sqrt(secp256k1_fe * SECP256K1_RESTRICT r, const secp256k1_fe * SECP256K1_RESTRICT a); -/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be - * at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */ +/** Compute the modular inverse of a field element. + * + * On input, a must be a valid field element; r need not be initialized. + * Performs {r = a**(p-2)} (which maps 0 to 0, and every other element to its + * inverse). + * On output, r will have magnitude (a.magnitude != 0) and be normalized. + */ static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); -/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */ +/** Compute the modular inverse of a field element, without constant-time guarantee. + * + * Behaves identically to secp256k1_fe_inv, but is not constant-time in a. + */ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a); -/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be - * at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and - * outputs must not overlap in memory. */ -static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len); - -/** Convert a field element to the storage type. */ +/** Convert a field element to secp256k1_fe_storage. + * + * On input, a must be a valid normalized field element. + * Performs {r = a}. + */ static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); -/** Convert a field element back from the storage type. */ +/** Convert a field element back from secp256k1_fe_storage. + * + * On input, r need not be initialized. + * Performs {r = a}. + * On output, r will be normalized and will have magnitude 1. + */ static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a); -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag); -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +/** Conditionally move a field element in constant time. + * + * On input, both r and a must be valid field elements. Flag must be 0 or 1. + * Performs {r = flag ? a : r}. + * + * On output, r's magnitude will be the maximum of both input magnitudes. + * It will be normalized if and only if both inputs were normalized. + */ static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); +/** Halve the value of a field element modulo the field prime in constant-time. + * + * On input, r must be a valid field element. + * On output, r will be normalized and have magnitude floor(m/2) + 1 where m is + * the magnitude of r on input. + */ +static void secp256k1_fe_half(secp256k1_fe *r); + +/** Sets r to a field element with magnitude m, normalized if (and only if) m==0. + * The value is chosen so that it is likely to trigger edge cases related to + * internal overflows. */ +static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m); + +/** Determine whether a is a square (modulo p). + * + * On input, a must be a valid field element. + */ +static int secp256k1_fe_is_square_var(const secp256k1_fe *a); + +/** Check invariants on a field element (no-op unless VERIFY is enabled). */ +static void secp256k1_fe_verify(const secp256k1_fe *a); +#define SECP256K1_FE_VERIFY(a) secp256k1_fe_verify(a) + +/** Check that magnitude of a is at most m (no-op unless VERIFY is enabled). */ +static void secp256k1_fe_verify_magnitude(const secp256k1_fe *a, int m); +#define SECP256K1_FE_VERIFY_MAGNITUDE(a, m) secp256k1_fe_verify_magnitude(a, m) + #endif /* SECP256K1_FIELD_H */ diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h index 30699eca8..203c10167 100644 --- a/src/secp256k1/src/field_10x26.h +++ b/src/secp256k1/src/field_10x26.h @@ -1,21 +1,36 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_FIELD_REPR_H #define SECP256K1_FIELD_REPR_H #include +/** This field implementation represents the value as 10 uint32_t limbs in base + * 2^26. */ typedef struct { - /* X = sum(i=0..9, elem[i]*2^26) mod n */ + /* A field element f represents the sum(i=0..9, f.n[i] << (i*26)) mod p, + * where p is the field modulus, 2^256 - 2^32 - 977. + * + * The individual limbs f.n[i] can exceed 2^26; the field's magnitude roughly + * corresponds to how much excess is allowed. The value + * sum(i=0..9, f.n[i] << (i*26)) may exceed p, unless the field element is + * normalized. */ uint32_t n[10]; -#ifdef VERIFY - int magnitude; - int normalized; -#endif + /* + * Magnitude m requires: + * n[i] <= 2 * m * (2^26 - 1) for i=0..8 + * n[9] <= 2 * m * (2^22 - 1) + * + * Normalized requires: + * n[i] <= (2^26 - 1) for i=0..8 + * sum(i=0..9, n[i] << (i*26)) < p + * (together these imply n[9] <= 2^22 - 1) + */ + SECP256K1_FE_VERIFY_FIELDS } secp256k1_fe; /* Unpacks a constant into a overlapping multi-limbed FE element. */ @@ -32,12 +47,6 @@ typedef struct { (((uint32_t)d7) >> 10) \ } -#ifdef VERIFY -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} -#else -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} -#endif - typedef struct { uint32_t n[8]; } secp256k1_fe_storage; diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h index d7918ac15..666068c71 100644 --- a/src/secp256k1/src/field_10x26_impl.h +++ b/src/secp256k1/src/field_10x26_impl.h @@ -1,46 +1,56 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_FIELD_REPR_IMPL_H #define SECP256K1_FIELD_REPR_IMPL_H +#include "checkmem.h" #include "util.h" -#include "num.h" #include "field.h" +#include "modinv32_impl.h" #ifdef VERIFY -static void secp256k1_fe_verify(const secp256k1_fe *a) { +static void secp256k1_fe_impl_verify(const secp256k1_fe *a) { const uint32_t *d = a->n; - int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; - r &= (d[0] <= 0x3FFFFFFUL * m); - r &= (d[1] <= 0x3FFFFFFUL * m); - r &= (d[2] <= 0x3FFFFFFUL * m); - r &= (d[3] <= 0x3FFFFFFUL * m); - r &= (d[4] <= 0x3FFFFFFUL * m); - r &= (d[5] <= 0x3FFFFFFUL * m); - r &= (d[6] <= 0x3FFFFFFUL * m); - r &= (d[7] <= 0x3FFFFFFUL * m); - r &= (d[8] <= 0x3FFFFFFUL * m); - r &= (d[9] <= 0x03FFFFFUL * m); - r &= (a->magnitude >= 0); - r &= (a->magnitude <= 32); + int m = a->normalized ? 1 : 2 * a->magnitude; + VERIFY_CHECK(d[0] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[1] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[2] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[3] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[4] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[5] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[6] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[7] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[8] <= 0x3FFFFFFUL * m); + VERIFY_CHECK(d[9] <= 0x03FFFFFUL * m); if (a->normalized) { - r &= (a->magnitude <= 1); - if (r && (d[9] == 0x03FFFFFUL)) { + if (d[9] == 0x03FFFFFUL) { uint32_t mid = d[8] & d[7] & d[6] & d[5] & d[4] & d[3] & d[2]; if (mid == 0x3FFFFFFUL) { - r &= ((d[1] + 0x40UL + ((d[0] + 0x3D1UL) >> 26)) <= 0x3FFFFFFUL); + VERIFY_CHECK((d[1] + 0x40UL + ((d[0] + 0x3D1UL) >> 26)) <= 0x3FFFFFFUL); } } } - VERIFY_CHECK(r == 1); } #endif -static void secp256k1_fe_normalize(secp256k1_fe *r) { +static void secp256k1_fe_impl_get_bounds(secp256k1_fe *r, int m) { + r->n[0] = 0x3FFFFFFUL * 2 * m; + r->n[1] = 0x3FFFFFFUL * 2 * m; + r->n[2] = 0x3FFFFFFUL * 2 * m; + r->n[3] = 0x3FFFFFFUL * 2 * m; + r->n[4] = 0x3FFFFFFUL * 2 * m; + r->n[5] = 0x3FFFFFFUL * 2 * m; + r->n[6] = 0x3FFFFFFUL * 2 * m; + r->n[7] = 0x3FFFFFFUL * 2 * m; + r->n[8] = 0x3FFFFFFUL * 2 * m; + r->n[9] = 0x03FFFFFUL * 2 * m; +} + +static void secp256k1_fe_impl_normalize(secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; @@ -87,15 +97,9 @@ static void secp256k1_fe_normalize(secp256k1_fe *r) { r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize_weak(secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; @@ -119,14 +123,9 @@ static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize_var(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize_var(secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; @@ -174,15 +173,9 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) { r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { +static int secp256k1_fe_impl_normalizes_to_zero(const secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; @@ -211,7 +204,7 @@ static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { return (z0 == 0) | (z1 == 0x3FFFFFFUL); } -static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { +static int secp256k1_fe_impl_normalizes_to_zero_var(const secp256k1_fe *r) { uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; uint32_t z0, z1; uint32_t x; @@ -263,52 +256,29 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { return (z0 == 0) | (z1 == 0x3FFFFFFUL); } -SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_impl_set_int(secp256k1_fe *r, int a) { r->n[0] = a; r->n[1] = r->n[2] = r->n[3] = r->n[4] = r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { +SECP256K1_INLINE static int secp256k1_fe_impl_is_zero(const secp256k1_fe *a) { const uint32_t *t = a->n; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif return (t[0] | t[1] | t[2] | t[3] | t[4] | t[5] | t[6] | t[7] | t[8] | t[9]) == 0; } -SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static int secp256k1_fe_impl_is_odd(const secp256k1_fe *a) { return a->n[0] & 1; } -SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { +SECP256K1_INLINE static void secp256k1_fe_impl_clear(secp256k1_fe *a) { int i; -#ifdef VERIFY - a->magnitude = 0; - a->normalized = 1; -#endif for (i=0; i<10; i++) { a->n[i] = 0; } } -static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { +static int secp256k1_fe_impl_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { int i; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - VERIFY_CHECK(b->normalized); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); -#endif for (i = 9; i >= 0; i--) { if (a->n[i] > b->n[i]) { return 1; @@ -320,7 +290,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { return 0; } -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { +static void secp256k1_fe_impl_set_b32_mod(secp256k1_fe *r, const unsigned char *a) { r->n[0] = (uint32_t)a[31] | ((uint32_t)a[30] << 8) | ((uint32_t)a[29] << 16) | ((uint32_t)(a[28] & 0x3) << 24); r->n[1] = (uint32_t)((a[28] >> 2) & 0x3f) | ((uint32_t)a[27] << 6) | ((uint32_t)a[26] << 14) | ((uint32_t)(a[25] & 0xf) << 22); r->n[2] = (uint32_t)((a[25] >> 4) & 0xf) | ((uint32_t)a[24] << 4) | ((uint32_t)a[23] << 12) | ((uint32_t)(a[22] & 0x3f) << 20); @@ -331,24 +301,15 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { r->n[7] = (uint32_t)((a[9] >> 6) & 0x3) | ((uint32_t)a[8] << 2) | ((uint32_t)a[7] << 10) | ((uint32_t)a[6] << 18); r->n[8] = (uint32_t)a[5] | ((uint32_t)a[4] << 8) | ((uint32_t)a[3] << 16) | ((uint32_t)(a[2] & 0x3) << 24); r->n[9] = (uint32_t)((a[2] >> 2) & 0x3f) | ((uint32_t)a[1] << 6) | ((uint32_t)a[0] << 14); +} - if (r->n[9] == 0x3FFFFFUL && (r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL && (r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL) { - return 0; - } -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif - return 1; +static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char *a) { + secp256k1_fe_impl_set_b32_mod(r, a); + return !((r->n[9] == 0x3FFFFFUL) & ((r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL) & ((r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); } /** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ -static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif +static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) { r[0] = (a->n[9] >> 14) & 0xff; r[1] = (a->n[9] >> 6) & 0xff; r[2] = ((a->n[9] & 0x3F) << 2) | ((a->n[8] >> 24) & 0x3); @@ -383,11 +344,15 @@ static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { r[31] = a->n[0] & 0xff; } -SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= m); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) { + /* For all legal values of m (0..31), the following properties hold: */ + VERIFY_CHECK(0x3FFFC2FUL * 2 * (m + 1) >= 0x3FFFFFFUL * 2 * m); + VERIFY_CHECK(0x3FFFFBFUL * 2 * (m + 1) >= 0x3FFFFFFUL * 2 * m); + VERIFY_CHECK(0x3FFFFFFUL * 2 * (m + 1) >= 0x3FFFFFFUL * 2 * m); + VERIFY_CHECK(0x03FFFFFUL * 2 * (m + 1) >= 0x03FFFFFUL * 2 * m); + + /* Due to the properties above, the left hand in the subtractions below is never less than + * the right hand. */ r->n[0] = 0x3FFFC2FUL * 2 * (m + 1) - a->n[0]; r->n[1] = 0x3FFFFBFUL * 2 * (m + 1) - a->n[1]; r->n[2] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[2]; @@ -398,14 +363,9 @@ SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k r->n[7] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[7]; r->n[8] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[8]; r->n[9] = 0x03FFFFFUL * 2 * (m + 1) - a->n[9]; -#ifdef VERIFY - r->magnitude = m + 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_impl_mul_int_unchecked(secp256k1_fe *r, int a) { r->n[0] *= a; r->n[1] *= a; r->n[2] *= a; @@ -416,17 +376,9 @@ SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { r->n[7] *= a; r->n[8] *= a; r->n[9] *= a; -#ifdef VERIFY - r->magnitude *= a; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_add(secp256k1_fe *r, const secp256k1_fe *a) { r->n[0] += a->n[0]; r->n[1] += a->n[1]; r->n[2] += a->n[2]; @@ -437,11 +389,10 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_f r->n[7] += a->n[7]; r->n[8] += a->n[8]; r->n[9] += a->n[9]; -#ifdef VERIFY - r->magnitude += a->magnitude; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif +} + +SECP256K1_INLINE static void secp256k1_fe_impl_add_int(secp256k1_fe *r, int a) { + r->n[0] += a; } #if defined(USE_EXTERNAL_ASM) @@ -452,11 +403,7 @@ void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a); #else -#ifdef VERIFY #define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) -#else -#define VERIFY_BITS(x, n) do { } while(0) -#endif SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b) { uint64_t c, d; @@ -486,7 +433,8 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t VERIFY_BITS(b[9], 26); /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. - * px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * for 0 <= x <= 9, px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * for 9 <= x <= 18, px is a shorthand for sum(a[i]*b[x-i], i=(x-9)..9) * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. */ @@ -1062,38 +1010,19 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t } #endif -static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - VERIFY_CHECK(b->magnitude <= 8); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); - VERIFY_CHECK(r != b); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { secp256k1_fe_mul_inner(r->n, a->n, b->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_sqr(secp256k1_fe *r, const secp256k1_fe *a) { secp256k1_fe_sqr_inner(r->n, a->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { +SECP256K1_INLINE static void secp256k1_fe_impl_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint32_t mask0, mask1; - mask0 = flag + ~((uint32_t)0); + volatile int vflag = flag; + SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); + mask0 = vflag + ~((uint32_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); @@ -1105,17 +1034,78 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_ r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1); r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1); -#ifdef VERIFY - if (a->magnitude > r->magnitude) { - r->magnitude = a->magnitude; - } - r->normalized &= a->normalized; -#endif +} + +static SECP256K1_INLINE void secp256k1_fe_impl_half(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + uint32_t one = (uint32_t)1; + uint32_t mask = -(t0 & one) >> 6; + + /* Bounds analysis (over the rationals). + * + * Let m = r->magnitude + * C = 0x3FFFFFFUL * 2 + * D = 0x03FFFFFUL * 2 + * + * Initial bounds: t0..t8 <= C * m + * t9 <= D * m + */ + + t0 += 0x3FFFC2FUL & mask; + t1 += 0x3FFFFBFUL & mask; + t2 += mask; + t3 += mask; + t4 += mask; + t5 += mask; + t6 += mask; + t7 += mask; + t8 += mask; + t9 += mask >> 4; + + VERIFY_CHECK((t0 & one) == 0); + + /* t0..t8: added <= C/2 + * t9: added <= D/2 + * + * Current bounds: t0..t8 <= C * (m + 1/2) + * t9 <= D * (m + 1/2) + */ + + r->n[0] = (t0 >> 1) + ((t1 & one) << 25); + r->n[1] = (t1 >> 1) + ((t2 & one) << 25); + r->n[2] = (t2 >> 1) + ((t3 & one) << 25); + r->n[3] = (t3 >> 1) + ((t4 & one) << 25); + r->n[4] = (t4 >> 1) + ((t5 & one) << 25); + r->n[5] = (t5 >> 1) + ((t6 & one) << 25); + r->n[6] = (t6 >> 1) + ((t7 & one) << 25); + r->n[7] = (t7 >> 1) + ((t8 & one) << 25); + r->n[8] = (t8 >> 1) + ((t9 & one) << 25); + r->n[9] = (t9 >> 1); + + /* t0..t8: shifted right and added <= C/4 + 1/2 + * t9: shifted right + * + * Current bounds: t0..t8 <= C * (m/2 + 1/2) + * t9 <= D * (m/2 + 1/4) + * + * Therefore the output magnitude (M) has to be set such that: + * t0..t8: C * M >= C * (m/2 + 1/2) + * t9: D * M >= D * (m/2 + 1/4) + * + * It suffices for all limbs that, for any input magnitude m: + * M >= m/2 + 1/2 + * + * and since we want the smallest such integer value for M: + * M == floor(m/2) + 1 + */ } static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { uint32_t mask0, mask1; - mask0 = flag + ~((uint32_t)0); + volatile int vflag = flag; + SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); + mask0 = vflag + ~((uint32_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); @@ -1127,10 +1117,7 @@ static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); } -static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif +static void secp256k1_fe_impl_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { r->n[0] = a->n[0] | a->n[1] << 26; r->n[1] = a->n[1] >> 6 | a->n[2] << 20; r->n[2] = a->n[2] >> 12 | a->n[3] << 14; @@ -1141,7 +1128,7 @@ static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe r->n[7] = a->n[8] >> 16 | a->n[9] << 10; } -static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { +static SECP256K1_INLINE void secp256k1_fe_impl_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { r->n[0] = a->n[0] & 0x3FFFFFFUL; r->n[1] = a->n[0] >> 26 | ((a->n[1] << 6) & 0x3FFFFFFUL); r->n[2] = a->n[1] >> 20 | ((a->n[2] << 12) & 0x3FFFFFFUL); @@ -1152,10 +1139,101 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se r->n[7] = a->n[5] >> 22 | ((a->n[6] << 10) & 0x3FFFFFFUL); r->n[8] = a->n[6] >> 16 | ((a->n[7] << 16) & 0x3FFFFFFUL); r->n[9] = a->n[7] >> 10; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; -#endif +} + +static void secp256k1_fe_from_signed30(secp256k1_fe *r, const secp256k1_modinv32_signed30 *a) { + const uint32_t M26 = UINT32_MAX >> 6; + const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4], + a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8]; + + /* The output from secp256k1_modinv32{_var} should be normalized to range [0,modulus), and + * have limbs in [0,2^30). The modulus is < 2^256, so the top limb must be below 2^(256-30*8). + */ + VERIFY_CHECK(a0 >> 30 == 0); + VERIFY_CHECK(a1 >> 30 == 0); + VERIFY_CHECK(a2 >> 30 == 0); + VERIFY_CHECK(a3 >> 30 == 0); + VERIFY_CHECK(a4 >> 30 == 0); + VERIFY_CHECK(a5 >> 30 == 0); + VERIFY_CHECK(a6 >> 30 == 0); + VERIFY_CHECK(a7 >> 30 == 0); + VERIFY_CHECK(a8 >> 16 == 0); + + r->n[0] = a0 & M26; + r->n[1] = (a0 >> 26 | a1 << 4) & M26; + r->n[2] = (a1 >> 22 | a2 << 8) & M26; + r->n[3] = (a2 >> 18 | a3 << 12) & M26; + r->n[4] = (a3 >> 14 | a4 << 16) & M26; + r->n[5] = (a4 >> 10 | a5 << 20) & M26; + r->n[6] = (a5 >> 6 | a6 << 24) & M26; + r->n[7] = (a6 >> 2 ) & M26; + r->n[8] = (a6 >> 28 | a7 << 2) & M26; + r->n[9] = (a7 >> 24 | a8 << 6); +} + +static void secp256k1_fe_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_fe *a) { + const uint32_t M30 = UINT32_MAX >> 2; + const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4], + a5 = a->n[5], a6 = a->n[6], a7 = a->n[7], a8 = a->n[8], a9 = a->n[9]; + + r->v[0] = (a0 | a1 << 26) & M30; + r->v[1] = (a1 >> 4 | a2 << 22) & M30; + r->v[2] = (a2 >> 8 | a3 << 18) & M30; + r->v[3] = (a3 >> 12 | a4 << 14) & M30; + r->v[4] = (a4 >> 16 | a5 << 10) & M30; + r->v[5] = (a5 >> 20 | a6 << 6) & M30; + r->v[6] = (a6 >> 24 | a7 << 2 + | a8 << 28) & M30; + r->v[7] = (a8 >> 2 | a9 << 24) & M30; + r->v[8] = a9 >> 6; +} + +static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_fe = { + {{-0x3D1, -4, 0, 0, 0, 0, 0, 0, 65536}}, + 0x2DDACACFL +}; + +static void secp256k1_fe_impl_inv(secp256k1_fe *r, const secp256k1_fe *x) { + secp256k1_fe tmp = *x; + secp256k1_modinv32_signed30 s; + + secp256k1_fe_normalize(&tmp); + secp256k1_fe_to_signed30(&s, &tmp); + secp256k1_modinv32(&s, &secp256k1_const_modinfo_fe); + secp256k1_fe_from_signed30(r, &s); +} + +static void secp256k1_fe_impl_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { + secp256k1_fe tmp = *x; + secp256k1_modinv32_signed30 s; + + secp256k1_fe_normalize_var(&tmp); + secp256k1_fe_to_signed30(&s, &tmp); + secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_fe); + secp256k1_fe_from_signed30(r, &s); +} + +static int secp256k1_fe_impl_is_square_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv32_signed30 s; + int jac, ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + /* secp256k1_jacobi32_maybe_var cannot deal with input 0. */ + if (secp256k1_fe_is_zero(&tmp)) return 1; + secp256k1_fe_to_signed30(&s, &tmp); + jac = secp256k1_jacobi32_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (jac == 0) { + /* secp256k1_jacobi32_maybe_var failed to compute the Jacobi symbol. Fall back + * to computing a square root. This should be extremely rare with random + * input (except in VERIFY mode, where a lower iteration count is used). */ + secp256k1_fe dummy; + ret = secp256k1_fe_sqrt(&dummy, &tmp); + } else { + ret = jac >= 0; + } + return ret; } #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h index 87a068765..f20c246fd 100644 --- a/src/secp256k1/src/field_5x52.h +++ b/src/secp256k1/src/field_5x52.h @@ -1,21 +1,36 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_FIELD_REPR_H #define SECP256K1_FIELD_REPR_H #include +/** This field implementation represents the value as 5 uint64_t limbs in base + * 2^52. */ typedef struct { - /* X = sum(i=0..4, elem[i]*2^52) mod n */ + /* A field element f represents the sum(i=0..4, f.n[i] << (i*52)) mod p, + * where p is the field modulus, 2^256 - 2^32 - 977. + * + * The individual limbs f.n[i] can exceed 2^52; the field's magnitude roughly + * corresponds to how much excess is allowed. The value + * sum(i=0..4, f.n[i] << (i*52)) may exceed p, unless the field element is + * normalized. */ uint64_t n[5]; -#ifdef VERIFY - int magnitude; - int normalized; -#endif + /* + * Magnitude m requires: + * n[i] <= 2 * m * (2^52 - 1) for i=0..3 + * n[4] <= 2 * m * (2^48 - 1) + * + * Normalized requires: + * n[i] <= (2^52 - 1) for i=0..3 + * sum(i=0..4, n[i] << (i*52)) < p + * (together these imply n[4] <= 2^48 - 1) + */ + SECP256K1_FE_VERIFY_FIELDS } secp256k1_fe; /* Unpacks a constant into a overlapping multi-limbed FE element. */ @@ -27,12 +42,6 @@ typedef struct { ((uint64_t)(d6) >> 16) | (((uint64_t)(d7)) << 16) \ } -#ifdef VERIFY -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} -#else -#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} -#endif - typedef struct { uint64_t n[4]; } secp256k1_fe_storage; @@ -44,4 +53,10 @@ typedef struct { (d6) | (((uint64_t)(d7)) << 32) \ }} +#define SECP256K1_FE_STORAGE_CONST_GET(d) \ + (uint32_t)(d.n[3] >> 32), (uint32_t)d.n[3], \ + (uint32_t)(d.n[2] >> 32), (uint32_t)d.n[2], \ + (uint32_t)(d.n[1] >> 32), (uint32_t)d.n[1], \ + (uint32_t)(d.n[0] >> 32), (uint32_t)d.n[0] + #endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/secp256k1/src/field_5x52_asm_impl.h b/src/secp256k1/src/field_5x52_asm_impl.h deleted file mode 100644 index 3c5ccc6da..000000000 --- a/src/secp256k1/src/field_5x52_asm_impl.h +++ /dev/null @@ -1,502 +0,0 @@ -/************************************************************************ - * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -/** - * Changelog: - * - March 2013, Diederik Huys: original version - * - November 2014, Pieter Wuille: updated to use Peter Dettman's parallel multiplication algorithm - * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly - */ - -#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H -#define SECP256K1_FIELD_INNER5X52_IMPL_H - -SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { -/** - * Registers: rdx:rax = multiplication accumulator - * r9:r8 = c - * r15:rcx = d - * r10-r14 = a0-a4 - * rbx = b - * rdi = r - * rsi = a / t? - */ - uint64_t tmp1, tmp2, tmp3; -__asm__ __volatile__( - "movq 0(%%rsi),%%r10\n" - "movq 8(%%rsi),%%r11\n" - "movq 16(%%rsi),%%r12\n" - "movq 24(%%rsi),%%r13\n" - "movq 32(%%rsi),%%r14\n" - - /* d += a3 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r13\n" - "movq %%rax,%%rcx\n" - "movq %%rdx,%%r15\n" - /* d += a2 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a1 * b2 */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d = a0 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r10\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* c = a4 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r14\n" - "movq %%rax,%%r8\n" - "movq %%rdx,%%r9\n" - /* d += (c & M) * R */ - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* c >>= 52 (%%r8 only) */ - "shrdq $52,%%r9,%%r8\n" - /* t3 (tmp1) = d & M */ - "movq %%rcx,%%rsi\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rsi\n" - "movq %%rsi,%q1\n" - /* d >>= 52 */ - "shrdq $52,%%r15,%%rcx\n" - "xorq %%r15,%%r15\n" - /* d += a4 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a3 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a2 * b2 */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a1 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a0 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r10\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += c * R */ - "movq %%r8,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* t4 = d & M (%%rsi) */ - "movq %%rcx,%%rsi\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rsi\n" - /* d >>= 52 */ - "shrdq $52,%%r15,%%rcx\n" - "xorq %%r15,%%r15\n" - /* tx = t4 >> 48 (tmp3) */ - "movq %%rsi,%%rax\n" - "shrq $48,%%rax\n" - "movq %%rax,%q3\n" - /* t4 &= (M >> 4) (tmp2) */ - "movq $0xffffffffffff,%%rax\n" - "andq %%rax,%%rsi\n" - "movq %%rsi,%q2\n" - /* c = a0 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r10\n" - "movq %%rax,%%r8\n" - "movq %%rdx,%%r9\n" - /* d += a4 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a3 * b2 */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a2 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a1 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* u0 = d & M (%%rsi) */ - "movq %%rcx,%%rsi\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rsi\n" - /* d >>= 52 */ - "shrdq $52,%%r15,%%rcx\n" - "xorq %%r15,%%r15\n" - /* u0 = (u0 << 4) | tx (%%rsi) */ - "shlq $4,%%rsi\n" - "movq %q3,%%rax\n" - "orq %%rax,%%rsi\n" - /* c += u0 * (R >> 4) */ - "movq $0x1000003d1,%%rax\n" - "mulq %%rsi\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* r[0] = c & M */ - "movq %%r8,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq %%rax,0(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += a1 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* c += a0 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r10\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d += a4 * b2 */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a3 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a2 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* c += (d & M) * R */ - "movq %%rcx,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d >>= 52 */ - "shrdq $52,%%r15,%%rcx\n" - "xorq %%r15,%%r15\n" - /* r[1] = c & M */ - "movq %%r8,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq %%rax,8(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += a2 * b0 */ - "movq 0(%%rbx),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* c += a1 * b1 */ - "movq 8(%%rbx),%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* c += a0 * b2 (last use of %%r10 = a0) */ - "movq 16(%%rbx),%%rax\n" - "mulq %%r10\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* fetch t3 (%%r10, overwrites a0), t4 (%%rsi) */ - "movq %q2,%%rsi\n" - "movq %q1,%%r10\n" - /* d += a4 * b3 */ - "movq 24(%%rbx),%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* d += a3 * b4 */ - "movq 32(%%rbx),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rcx\n" - "adcq %%rdx,%%r15\n" - /* c += (d & M) * R */ - "movq %%rcx,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d >>= 52 (%%rcx only) */ - "shrdq $52,%%r15,%%rcx\n" - /* r[2] = c & M */ - "movq %%r8,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq %%rax,16(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += t3 */ - "addq %%r10,%%r8\n" - /* c += d * R */ - "movq %%rcx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* r[3] = c & M */ - "movq %%r8,%%rax\n" - "movq $0xfffffffffffff,%%rdx\n" - "andq %%rdx,%%rax\n" - "movq %%rax,24(%%rdi)\n" - /* c >>= 52 (%%r8 only) */ - "shrdq $52,%%r9,%%r8\n" - /* c += t4 (%%r8 only) */ - "addq %%rsi,%%r8\n" - /* r[4] = c */ - "movq %%r8,32(%%rdi)\n" -: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) -: "b"(b), "D"(r) -: "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" -); -} - -SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { -/** - * Registers: rdx:rax = multiplication accumulator - * r9:r8 = c - * rcx:rbx = d - * r10-r14 = a0-a4 - * r15 = M (0xfffffffffffff) - * rdi = r - * rsi = a / t? - */ - uint64_t tmp1, tmp2, tmp3; -__asm__ __volatile__( - "movq 0(%%rsi),%%r10\n" - "movq 8(%%rsi),%%r11\n" - "movq 16(%%rsi),%%r12\n" - "movq 24(%%rsi),%%r13\n" - "movq 32(%%rsi),%%r14\n" - "movq $0xfffffffffffff,%%r15\n" - - /* d = (a0*2) * a3 */ - "leaq (%%r10,%%r10,1),%%rax\n" - "mulq %%r13\n" - "movq %%rax,%%rbx\n" - "movq %%rdx,%%rcx\n" - /* d += (a1*2) * a2 */ - "leaq (%%r11,%%r11,1),%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* c = a4 * a4 */ - "movq %%r14,%%rax\n" - "mulq %%r14\n" - "movq %%rax,%%r8\n" - "movq %%rdx,%%r9\n" - /* d += (c & M) * R */ - "andq %%r15,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* c >>= 52 (%%r8 only) */ - "shrdq $52,%%r9,%%r8\n" - /* t3 (tmp1) = d & M */ - "movq %%rbx,%%rsi\n" - "andq %%r15,%%rsi\n" - "movq %%rsi,%q1\n" - /* d >>= 52 */ - "shrdq $52,%%rcx,%%rbx\n" - "xorq %%rcx,%%rcx\n" - /* a4 *= 2 */ - "addq %%r14,%%r14\n" - /* d += a0 * a4 */ - "movq %%r10,%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d+= (a1*2) * a3 */ - "leaq (%%r11,%%r11,1),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d += a2 * a2 */ - "movq %%r12,%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d += c * R */ - "movq %%r8,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* t4 = d & M (%%rsi) */ - "movq %%rbx,%%rsi\n" - "andq %%r15,%%rsi\n" - /* d >>= 52 */ - "shrdq $52,%%rcx,%%rbx\n" - "xorq %%rcx,%%rcx\n" - /* tx = t4 >> 48 (tmp3) */ - "movq %%rsi,%%rax\n" - "shrq $48,%%rax\n" - "movq %%rax,%q3\n" - /* t4 &= (M >> 4) (tmp2) */ - "movq $0xffffffffffff,%%rax\n" - "andq %%rax,%%rsi\n" - "movq %%rsi,%q2\n" - /* c = a0 * a0 */ - "movq %%r10,%%rax\n" - "mulq %%r10\n" - "movq %%rax,%%r8\n" - "movq %%rdx,%%r9\n" - /* d += a1 * a4 */ - "movq %%r11,%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d += (a2*2) * a3 */ - "leaq (%%r12,%%r12,1),%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* u0 = d & M (%%rsi) */ - "movq %%rbx,%%rsi\n" - "andq %%r15,%%rsi\n" - /* d >>= 52 */ - "shrdq $52,%%rcx,%%rbx\n" - "xorq %%rcx,%%rcx\n" - /* u0 = (u0 << 4) | tx (%%rsi) */ - "shlq $4,%%rsi\n" - "movq %q3,%%rax\n" - "orq %%rax,%%rsi\n" - /* c += u0 * (R >> 4) */ - "movq $0x1000003d1,%%rax\n" - "mulq %%rsi\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* r[0] = c & M */ - "movq %%r8,%%rax\n" - "andq %%r15,%%rax\n" - "movq %%rax,0(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* a0 *= 2 */ - "addq %%r10,%%r10\n" - /* c += a0 * a1 */ - "movq %%r10,%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d += a2 * a4 */ - "movq %%r12,%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* d += a3 * a3 */ - "movq %%r13,%%rax\n" - "mulq %%r13\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* c += (d & M) * R */ - "movq %%rbx,%%rax\n" - "andq %%r15,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d >>= 52 */ - "shrdq $52,%%rcx,%%rbx\n" - "xorq %%rcx,%%rcx\n" - /* r[1] = c & M */ - "movq %%r8,%%rax\n" - "andq %%r15,%%rax\n" - "movq %%rax,8(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += a0 * a2 (last use of %%r10) */ - "movq %%r10,%%rax\n" - "mulq %%r12\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* fetch t3 (%%r10, overwrites a0),t4 (%%rsi) */ - "movq %q2,%%rsi\n" - "movq %q1,%%r10\n" - /* c += a1 * a1 */ - "movq %%r11,%%rax\n" - "mulq %%r11\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d += a3 * a4 */ - "movq %%r13,%%rax\n" - "mulq %%r14\n" - "addq %%rax,%%rbx\n" - "adcq %%rdx,%%rcx\n" - /* c += (d & M) * R */ - "movq %%rbx,%%rax\n" - "andq %%r15,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* d >>= 52 (%%rbx only) */ - "shrdq $52,%%rcx,%%rbx\n" - /* r[2] = c & M */ - "movq %%r8,%%rax\n" - "andq %%r15,%%rax\n" - "movq %%rax,16(%%rdi)\n" - /* c >>= 52 */ - "shrdq $52,%%r9,%%r8\n" - "xorq %%r9,%%r9\n" - /* c += t3 */ - "addq %%r10,%%r8\n" - /* c += d * R */ - "movq %%rbx,%%rax\n" - "movq $0x1000003d10,%%rdx\n" - "mulq %%rdx\n" - "addq %%rax,%%r8\n" - "adcq %%rdx,%%r9\n" - /* r[3] = c & M */ - "movq %%r8,%%rax\n" - "andq %%r15,%%rax\n" - "movq %%rax,24(%%rdi)\n" - /* c >>= 52 (%%r8 only) */ - "shrdq $52,%%r9,%%r8\n" - /* c += t4 (%%r8 only) */ - "addq %%rsi,%%r8\n" - /* r[4] = c */ - "movq %%r8,32(%%rdi)\n" -: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) -: "D"(r) -: "%rax", "%rbx", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" -); -} - -#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h index 2ad914f16..76031f755 100644 --- a/src/secp256k1/src/field_5x52_impl.h +++ b/src/secp256k1/src/field_5x52_impl.h @@ -1,57 +1,46 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_FIELD_REPR_IMPL_H #define SECP256K1_FIELD_REPR_IMPL_H -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - +#include "checkmem.h" #include "util.h" -#include "num.h" #include "field.h" +#include "modinv64_impl.h" -#if defined(USE_ASM_X86_64) -#include "field_5x52_asm_impl.h" -#else #include "field_5x52_int128_impl.h" -#endif - -/** Implements arithmetic modulo FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F, - * represented as 5 uint64_t's in base 2^52. The values are allowed to contain >52 each. In particular, - * each FieldElem has a 'magnitude' associated with it. Internally, a magnitude M means each element - * is at most M*(2^53-1), except the most significant one, which is limited to M*(2^49-1). All operations - * accept any input with magnitude at most M, and have different rules for propagating magnitude to their - * output. - */ #ifdef VERIFY -static void secp256k1_fe_verify(const secp256k1_fe *a) { +static void secp256k1_fe_impl_verify(const secp256k1_fe *a) { const uint64_t *d = a->n; - int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + int m = a->normalized ? 1 : 2 * a->magnitude; /* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ - r &= (d[0] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[1] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m); - r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m); - r &= (a->magnitude >= 0); - r &= (a->magnitude <= 2048); + VERIFY_CHECK(d[0] <= 0xFFFFFFFFFFFFFULL * m); + VERIFY_CHECK(d[1] <= 0xFFFFFFFFFFFFFULL * m); + VERIFY_CHECK(d[2] <= 0xFFFFFFFFFFFFFULL * m); + VERIFY_CHECK(d[3] <= 0xFFFFFFFFFFFFFULL * m); + VERIFY_CHECK(d[4] <= 0x0FFFFFFFFFFFFULL * m); if (a->normalized) { - r &= (a->magnitude <= 1); - if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { - r &= (d[0] < 0xFFFFEFFFFFC2FULL); + if ((d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { + VERIFY_CHECK(d[0] < 0xFFFFEFFFFFC2FULL); } } - VERIFY_CHECK(r == 1); } #endif -static void secp256k1_fe_normalize(secp256k1_fe *r) { +static void secp256k1_fe_impl_get_bounds(secp256k1_fe *r, int m) { + r->n[0] = 0xFFFFFFFFFFFFFULL * 2 * m; + r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * m; + r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * m; + r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * m; + r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * m; +} + +static void secp256k1_fe_impl_normalize(secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; /* Reduce t4 at the start so there will be at most a single carry from the first pass */ @@ -86,15 +75,9 @@ static void secp256k1_fe_normalize(secp256k1_fe *r) { t4 &= 0x0FFFFFFFFFFFFULL; r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize_weak(secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; /* Reduce t4 at the start so there will be at most a single carry from the first pass */ @@ -111,14 +94,9 @@ static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { VERIFY_CHECK(t4 >> 49 == 0); r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_normalize_var(secp256k1_fe *r) { +static void secp256k1_fe_impl_normalize_var(secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; /* Reduce t4 at the start so there will be at most a single carry from the first pass */ @@ -154,15 +132,9 @@ static void secp256k1_fe_normalize_var(secp256k1_fe *r) { } r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; - -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { +static int secp256k1_fe_impl_normalizes_to_zero(const secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ @@ -185,7 +157,7 @@ static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); } -static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { +static int secp256k1_fe_impl_normalizes_to_zero_var(const secp256k1_fe *r) { uint64_t t0, t1, t2, t3, t4; uint64_t z0, z1; uint64_t x; @@ -226,52 +198,29 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); } -SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_impl_set_int(secp256k1_fe *r, int a) { r->n[0] = a; r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { +SECP256K1_INLINE static int secp256k1_fe_impl_is_zero(const secp256k1_fe *a) { const uint64_t *t = a->n; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif return (t[0] | t[1] | t[2] | t[3] | t[4]) == 0; } -SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static int secp256k1_fe_impl_is_odd(const secp256k1_fe *a) { return a->n[0] & 1; } -SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { +SECP256K1_INLINE static void secp256k1_fe_impl_clear(secp256k1_fe *a) { int i; -#ifdef VERIFY - a->magnitude = 0; - a->normalized = 1; -#endif for (i=0; i<5; i++) { a->n[i] = 0; } } -static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { +static int secp256k1_fe_impl_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { int i; -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - VERIFY_CHECK(b->normalized); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); -#endif for (i = 4; i >= 0; i--) { if (a->n[i] > b->n[i]) { return 1; @@ -283,7 +232,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { return 0; } -static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { +static void secp256k1_fe_impl_set_b32_mod(secp256k1_fe *r, const unsigned char *a) { r->n[0] = (uint64_t)a[31] | ((uint64_t)a[30] << 8) | ((uint64_t)a[29] << 16) @@ -318,23 +267,15 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { | ((uint64_t)a[2] << 24) | ((uint64_t)a[1] << 32) | ((uint64_t)a[0] << 40); - if (r->n[4] == 0x0FFFFFFFFFFFFULL && (r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL && r->n[0] >= 0xFFFFEFFFFFC2FULL) { - return 0; - } -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; - secp256k1_fe_verify(r); -#endif - return 1; +} + +static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char *a) { + secp256k1_fe_impl_set_b32_mod(r, a); + return !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL)); } /** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ -static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); - secp256k1_fe_verify(a); -#endif +static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a) { r[0] = (a->n[4] >> 40) & 0xFF; r[1] = (a->n[4] >> 32) & 0xFF; r[2] = (a->n[4] >> 24) & 0xFF; @@ -369,101 +310,121 @@ static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { r[31] = a->n[0] & 0xFF; } -SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= m); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) { + /* For all legal values of m (0..31), the following properties hold: */ + VERIFY_CHECK(0xFFFFEFFFFFC2FULL * 2 * (m + 1) >= 0xFFFFFFFFFFFFFULL * 2 * m); + VERIFY_CHECK(0xFFFFFFFFFFFFFULL * 2 * (m + 1) >= 0xFFFFFFFFFFFFFULL * 2 * m); + VERIFY_CHECK(0x0FFFFFFFFFFFFULL * 2 * (m + 1) >= 0x0FFFFFFFFFFFFULL * 2 * m); + + /* Due to the properties above, the left hand in the subtractions below is never less than + * the right hand. */ r->n[0] = 0xFFFFEFFFFFC2FULL * 2 * (m + 1) - a->n[0]; r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[1]; r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[2]; r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[3]; r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * (m + 1) - a->n[4]; -#ifdef VERIFY - r->magnitude = m + 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { +SECP256K1_INLINE static void secp256k1_fe_impl_mul_int_unchecked(secp256k1_fe *r, int a) { r->n[0] *= a; r->n[1] *= a; r->n[2] *= a; r->n[3] *= a; r->n[4] *= a; -#ifdef VERIFY - r->magnitude *= a; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_add_int(secp256k1_fe *r, int a) { + r->n[0] += a; +} + +SECP256K1_INLINE static void secp256k1_fe_impl_add(secp256k1_fe *r, const secp256k1_fe *a) { r->n[0] += a->n[0]; r->n[1] += a->n[1]; r->n[2] += a->n[2]; r->n[3] += a->n[3]; r->n[4] += a->n[4]; -#ifdef VERIFY - r->magnitude += a->magnitude; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - VERIFY_CHECK(b->magnitude <= 8); - secp256k1_fe_verify(a); - secp256k1_fe_verify(b); - VERIFY_CHECK(r != b); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { secp256k1_fe_mul_inner(r->n, a->n, b->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->magnitude <= 8); - secp256k1_fe_verify(a); -#endif +SECP256K1_INLINE static void secp256k1_fe_impl_sqr(secp256k1_fe *r, const secp256k1_fe *a) { secp256k1_fe_sqr_inner(r->n, a->n); -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 0; - secp256k1_fe_verify(r); -#endif } -static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { +SECP256K1_INLINE static void secp256k1_fe_impl_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { uint64_t mask0, mask1; - mask0 = flag + ~((uint64_t)0); + volatile int vflag = flag; + SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); + mask0 = vflag + ~((uint64_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); -#ifdef VERIFY - if (a->magnitude > r->magnitude) { - r->magnitude = a->magnitude; - } - r->normalized &= a->normalized; -#endif +} + +static SECP256K1_INLINE void secp256k1_fe_impl_half(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + uint64_t one = (uint64_t)1; + uint64_t mask = -(t0 & one) >> 12; + + /* Bounds analysis (over the rationals). + * + * Let m = r->magnitude + * C = 0xFFFFFFFFFFFFFULL * 2 + * D = 0x0FFFFFFFFFFFFULL * 2 + * + * Initial bounds: t0..t3 <= C * m + * t4 <= D * m + */ + + t0 += 0xFFFFEFFFFFC2FULL & mask; + t1 += mask; + t2 += mask; + t3 += mask; + t4 += mask >> 4; + + VERIFY_CHECK((t0 & one) == 0); + + /* t0..t3: added <= C/2 + * t4: added <= D/2 + * + * Current bounds: t0..t3 <= C * (m + 1/2) + * t4 <= D * (m + 1/2) + */ + + r->n[0] = (t0 >> 1) + ((t1 & one) << 51); + r->n[1] = (t1 >> 1) + ((t2 & one) << 51); + r->n[2] = (t2 >> 1) + ((t3 & one) << 51); + r->n[3] = (t3 >> 1) + ((t4 & one) << 51); + r->n[4] = (t4 >> 1); + + /* t0..t3: shifted right and added <= C/4 + 1/2 + * t4: shifted right + * + * Current bounds: t0..t3 <= C * (m/2 + 1/2) + * t4 <= D * (m/2 + 1/4) + * + * Therefore the output magnitude (M) has to be set such that: + * t0..t3: C * M >= C * (m/2 + 1/2) + * t4: D * M >= D * (m/2 + 1/4) + * + * It suffices for all limbs that, for any input magnitude m: + * M >= m/2 + 1/2 + * + * and since we want the smallest such integer value for M: + * M == floor(m/2) + 1 + */ } static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { uint64_t mask0, mask1; - mask0 = flag + ~((uint64_t)0); + volatile int vflag = flag; + SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n)); + mask0 = vflag + ~((uint64_t)0); mask1 = ~mask0; r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); @@ -471,26 +432,98 @@ static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); } -static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { -#ifdef VERIFY - VERIFY_CHECK(a->normalized); -#endif +static void secp256k1_fe_impl_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { r->n[0] = a->n[0] | a->n[1] << 52; r->n[1] = a->n[1] >> 12 | a->n[2] << 40; r->n[2] = a->n[2] >> 24 | a->n[3] << 28; r->n[3] = a->n[3] >> 36 | a->n[4] << 16; } -static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { +static SECP256K1_INLINE void secp256k1_fe_impl_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { r->n[0] = a->n[0] & 0xFFFFFFFFFFFFFULL; r->n[1] = a->n[0] >> 52 | ((a->n[1] << 12) & 0xFFFFFFFFFFFFFULL); r->n[2] = a->n[1] >> 40 | ((a->n[2] << 24) & 0xFFFFFFFFFFFFFULL); r->n[3] = a->n[2] >> 28 | ((a->n[3] << 36) & 0xFFFFFFFFFFFFFULL); r->n[4] = a->n[3] >> 16; -#ifdef VERIFY - r->magnitude = 1; - r->normalized = 1; -#endif +} + +static void secp256k1_fe_from_signed62(secp256k1_fe *r, const secp256k1_modinv64_signed62 *a) { + const uint64_t M52 = UINT64_MAX >> 12; + const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4]; + + /* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and + * have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4). + */ + VERIFY_CHECK(a0 >> 62 == 0); + VERIFY_CHECK(a1 >> 62 == 0); + VERIFY_CHECK(a2 >> 62 == 0); + VERIFY_CHECK(a3 >> 62 == 0); + VERIFY_CHECK(a4 >> 8 == 0); + + r->n[0] = a0 & M52; + r->n[1] = (a0 >> 52 | a1 << 10) & M52; + r->n[2] = (a1 >> 42 | a2 << 20) & M52; + r->n[3] = (a2 >> 32 | a3 << 30) & M52; + r->n[4] = (a3 >> 22 | a4 << 40); +} + +static void secp256k1_fe_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_fe *a) { + const uint64_t M62 = UINT64_MAX >> 2; + const uint64_t a0 = a->n[0], a1 = a->n[1], a2 = a->n[2], a3 = a->n[3], a4 = a->n[4]; + + r->v[0] = (a0 | a1 << 52) & M62; + r->v[1] = (a1 >> 10 | a2 << 42) & M62; + r->v[2] = (a2 >> 20 | a3 << 32) & M62; + r->v[3] = (a3 >> 30 | a4 << 22) & M62; + r->v[4] = a4 >> 40; +} + +static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_fe = { + {{-0x1000003D1LL, 0, 0, 0, 256}}, + 0x27C7F6E22DDACACFLL +}; + +static void secp256k1_fe_impl_inv(secp256k1_fe *r, const secp256k1_fe *x) { + secp256k1_fe tmp = *x; + secp256k1_modinv64_signed62 s; + + secp256k1_fe_normalize(&tmp); + secp256k1_fe_to_signed62(&s, &tmp); + secp256k1_modinv64(&s, &secp256k1_const_modinfo_fe); + secp256k1_fe_from_signed62(r, &s); +} + +static void secp256k1_fe_impl_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { + secp256k1_fe tmp = *x; + secp256k1_modinv64_signed62 s; + + secp256k1_fe_normalize_var(&tmp); + secp256k1_fe_to_signed62(&s, &tmp); + secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_fe); + secp256k1_fe_from_signed62(r, &s); +} + +static int secp256k1_fe_impl_is_square_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv64_signed62 s; + int jac, ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + /* secp256k1_jacobi64_maybe_var cannot deal with input 0. */ + if (secp256k1_fe_is_zero(&tmp)) return 1; + secp256k1_fe_to_signed62(&s, &tmp); + jac = secp256k1_jacobi64_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (jac == 0) { + /* secp256k1_jacobi64_maybe_var failed to compute the Jacobi symbol. Fall back + * to computing a square root. This should be extremely rare with random + * input (except in VERIFY mode, where a lower iteration count is used). */ + secp256k1_fe dummy; + ret = secp256k1_fe_sqrt(&dummy, &tmp); + } else { + ret = jac >= 0; + } + return ret; } #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h index 0137481ad..f23f8ee1c 100644 --- a/src/secp256k1/src/field_5x52_int128_impl.h +++ b/src/secp256k1/src/field_5x52_int128_impl.h @@ -1,22 +1,22 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_FIELD_INNER5X52_IMPL_H #define SECP256K1_FIELD_INNER5X52_IMPL_H #include -#ifdef VERIFY +#include "int128.h" +#include "util.h" + #define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) -#else -#define VERIFY_BITS(x, n) do { } while(0) -#endif +#define VERIFY_BITS_128(x, n) VERIFY_CHECK(secp256k1_u128_check_bits((x), (n))) SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { - uint128_t c, d; + secp256k1_uint128 c, d; uint64_t t3, t4, tx, u0; uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; @@ -32,130 +32,129 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t VERIFY_BITS(b[3], 56); VERIFY_BITS(b[4], 52); VERIFY_CHECK(r != b); + VERIFY_CHECK(a != b); /* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. - * px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * for 0 <= x <= 4, px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * for 4 <= x <= 8, px is a shorthand for sum(a[i]*b[x-i], i=(x-4)..4) * Note that [x 0 0 0 0 0] = [x*R]. */ - d = (uint128_t)a0 * b[3] - + (uint128_t)a1 * b[2] - + (uint128_t)a2 * b[1] - + (uint128_t)a3 * b[0]; - VERIFY_BITS(d, 114); + secp256k1_u128_mul(&d, a0, b[3]); + secp256k1_u128_accum_mul(&d, a1, b[2]); + secp256k1_u128_accum_mul(&d, a2, b[1]); + secp256k1_u128_accum_mul(&d, a3, b[0]); + VERIFY_BITS_128(&d, 114); /* [d 0 0 0] = [p3 0 0 0] */ - c = (uint128_t)a4 * b[4]; - VERIFY_BITS(c, 112); + secp256k1_u128_mul(&c, a4, b[4]); + VERIFY_BITS_128(&c, 112); /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - d += (c & M) * R; c >>= 52; - VERIFY_BITS(d, 115); - VERIFY_BITS(c, 60); - /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - t3 = d & M; d >>= 52; + secp256k1_u128_accum_mul(&d, R, secp256k1_u128_to_u64(&c)); secp256k1_u128_rshift(&c, 64); + VERIFY_BITS_128(&d, 115); + VERIFY_BITS_128(&c, 48); + /* [(c<<12) 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + t3 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52); VERIFY_BITS(t3, 52); - VERIFY_BITS(d, 63); - /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + VERIFY_BITS_128(&d, 63); + /* [(c<<12) 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - d += (uint128_t)a0 * b[4] - + (uint128_t)a1 * b[3] - + (uint128_t)a2 * b[2] - + (uint128_t)a3 * b[1] - + (uint128_t)a4 * b[0]; - VERIFY_BITS(d, 115); - /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - d += c * R; - VERIFY_BITS(d, 116); + secp256k1_u128_accum_mul(&d, a0, b[4]); + secp256k1_u128_accum_mul(&d, a1, b[3]); + secp256k1_u128_accum_mul(&d, a2, b[2]); + secp256k1_u128_accum_mul(&d, a3, b[1]); + secp256k1_u128_accum_mul(&d, a4, b[0]); + VERIFY_BITS_128(&d, 115); + /* [(c<<12) 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + secp256k1_u128_accum_mul(&d, R << 12, secp256k1_u128_to_u64(&c)); + VERIFY_BITS_128(&d, 116); /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - t4 = d & M; d >>= 52; + t4 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52); VERIFY_BITS(t4, 52); - VERIFY_BITS(d, 64); + VERIFY_BITS_128(&d, 64); /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ tx = (t4 >> 48); t4 &= (M >> 4); VERIFY_BITS(tx, 4); VERIFY_BITS(t4, 48); /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - c = (uint128_t)a0 * b[0]; - VERIFY_BITS(c, 112); + secp256k1_u128_mul(&c, a0, b[0]); + VERIFY_BITS_128(&c, 112); /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ - d += (uint128_t)a1 * b[4] - + (uint128_t)a2 * b[3] - + (uint128_t)a3 * b[2] - + (uint128_t)a4 * b[1]; - VERIFY_BITS(d, 115); + secp256k1_u128_accum_mul(&d, a1, b[4]); + secp256k1_u128_accum_mul(&d, a2, b[3]); + secp256k1_u128_accum_mul(&d, a3, b[2]); + secp256k1_u128_accum_mul(&d, a4, b[1]); + VERIFY_BITS_128(&d, 114); /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - u0 = d & M; d >>= 52; + u0 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52); VERIFY_BITS(u0, 52); - VERIFY_BITS(d, 63); + VERIFY_BITS_128(&d, 62); /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ u0 = (u0 << 4) | tx; VERIFY_BITS(u0, 56); /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - c += (uint128_t)u0 * (R >> 4); - VERIFY_BITS(c, 115); + secp256k1_u128_accum_mul(&c, u0, R >> 4); + VERIFY_BITS_128(&c, 113); /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - r[0] = c & M; c >>= 52; + r[0] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52); VERIFY_BITS(r[0], 52); - VERIFY_BITS(c, 61); + VERIFY_BITS_128(&c, 61); /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ - c += (uint128_t)a0 * b[1] - + (uint128_t)a1 * b[0]; - VERIFY_BITS(c, 114); + secp256k1_u128_accum_mul(&c, a0, b[1]); + secp256k1_u128_accum_mul(&c, a1, b[0]); + VERIFY_BITS_128(&c, 114); /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ - d += (uint128_t)a2 * b[4] - + (uint128_t)a3 * b[3] - + (uint128_t)a4 * b[2]; - VERIFY_BITS(d, 114); + secp256k1_u128_accum_mul(&d, a2, b[4]); + secp256k1_u128_accum_mul(&d, a3, b[3]); + secp256k1_u128_accum_mul(&d, a4, b[2]); + VERIFY_BITS_128(&d, 114); /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - c += (d & M) * R; d >>= 52; - VERIFY_BITS(c, 115); - VERIFY_BITS(d, 62); + secp256k1_u128_accum_mul(&c, secp256k1_u128_to_u64(&d) & M, R); secp256k1_u128_rshift(&d, 52); + VERIFY_BITS_128(&c, 115); + VERIFY_BITS_128(&d, 62); /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - r[1] = c & M; c >>= 52; + r[1] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52); VERIFY_BITS(r[1], 52); - VERIFY_BITS(c, 63); + VERIFY_BITS_128(&c, 63); /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - c += (uint128_t)a0 * b[2] - + (uint128_t)a1 * b[1] - + (uint128_t)a2 * b[0]; - VERIFY_BITS(c, 114); + secp256k1_u128_accum_mul(&c, a0, b[2]); + secp256k1_u128_accum_mul(&c, a1, b[1]); + secp256k1_u128_accum_mul(&c, a2, b[0]); + VERIFY_BITS_128(&c, 114); /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint128_t)a3 * b[4] - + (uint128_t)a4 * b[3]; - VERIFY_BITS(d, 114); + secp256k1_u128_accum_mul(&d, a3, b[4]); + secp256k1_u128_accum_mul(&d, a4, b[3]); + VERIFY_BITS_128(&d, 114); /* [d 0 0 t4 t3 c t1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += (d & M) * R; d >>= 52; - VERIFY_BITS(c, 115); - VERIFY_BITS(d, 62); - /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + secp256k1_u128_accum_mul(&c, R, secp256k1_u128_to_u64(&d)); secp256k1_u128_rshift(&d, 64); + VERIFY_BITS_128(&c, 115); + VERIFY_BITS_128(&d, 50); + /* [(d<<12) 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[2] = c & M; c >>= 52; + r[2] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52); VERIFY_BITS(r[2], 52); - VERIFY_BITS(c, 63); - /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += d * R + t3; - VERIFY_BITS(c, 100); + VERIFY_BITS_128(&c, 63); + /* [(d<<12) 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + secp256k1_u128_accum_mul(&c, R << 12, secp256k1_u128_to_u64(&d)); + secp256k1_u128_accum_u64(&c, t3); + VERIFY_BITS_128(&c, 100); /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[3] = c & M; c >>= 52; + r[3] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52); VERIFY_BITS(r[3], 52); - VERIFY_BITS(c, 48); + VERIFY_BITS_128(&c, 48); /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += t4; - VERIFY_BITS(c, 49); - /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[4] = c; + r[4] = secp256k1_u128_to_u64(&c) + t4; VERIFY_BITS(r[4], 49); /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ } SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { - uint128_t c, d; + secp256k1_uint128 c, d; uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; - int64_t t3, t4, tx, u0; + uint64_t t3, t4, tx, u0; const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; VERIFY_BITS(a[0], 56); @@ -169,107 +168,105 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t * Note that [x 0 0 0 0 0] = [x*R]. */ - d = (uint128_t)(a0*2) * a3 - + (uint128_t)(a1*2) * a2; - VERIFY_BITS(d, 114); + secp256k1_u128_mul(&d, a0*2, a3); + secp256k1_u128_accum_mul(&d, a1*2, a2); + VERIFY_BITS_128(&d, 114); /* [d 0 0 0] = [p3 0 0 0] */ - c = (uint128_t)a4 * a4; - VERIFY_BITS(c, 112); + secp256k1_u128_mul(&c, a4, a4); + VERIFY_BITS_128(&c, 112); /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - d += (c & M) * R; c >>= 52; - VERIFY_BITS(d, 115); - VERIFY_BITS(c, 60); - /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ - t3 = d & M; d >>= 52; + secp256k1_u128_accum_mul(&d, R, secp256k1_u128_to_u64(&c)); secp256k1_u128_rshift(&c, 64); + VERIFY_BITS_128(&d, 115); + VERIFY_BITS_128(&c, 48); + /* [(c<<12) 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + t3 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52); VERIFY_BITS(t3, 52); - VERIFY_BITS(d, 63); - /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + VERIFY_BITS_128(&d, 63); + /* [(c<<12) 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ a4 *= 2; - d += (uint128_t)a0 * a4 - + (uint128_t)(a1*2) * a3 - + (uint128_t)a2 * a2; - VERIFY_BITS(d, 115); - /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - d += c * R; - VERIFY_BITS(d, 116); + secp256k1_u128_accum_mul(&d, a0, a4); + secp256k1_u128_accum_mul(&d, a1*2, a3); + secp256k1_u128_accum_mul(&d, a2, a2); + VERIFY_BITS_128(&d, 115); + /* [(c<<12) 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + secp256k1_u128_accum_mul(&d, R << 12, secp256k1_u128_to_u64(&c)); + VERIFY_BITS_128(&d, 116); /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - t4 = d & M; d >>= 52; + t4 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52); VERIFY_BITS(t4, 52); - VERIFY_BITS(d, 64); + VERIFY_BITS_128(&d, 64); /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ tx = (t4 >> 48); t4 &= (M >> 4); VERIFY_BITS(tx, 4); VERIFY_BITS(t4, 48); /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ - c = (uint128_t)a0 * a0; - VERIFY_BITS(c, 112); + secp256k1_u128_mul(&c, a0, a0); + VERIFY_BITS_128(&c, 112); /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ - d += (uint128_t)a1 * a4 - + (uint128_t)(a2*2) * a3; - VERIFY_BITS(d, 114); + secp256k1_u128_accum_mul(&d, a1, a4); + secp256k1_u128_accum_mul(&d, a2*2, a3); + VERIFY_BITS_128(&d, 114); /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - u0 = d & M; d >>= 52; + u0 = secp256k1_u128_to_u64(&d) & M; secp256k1_u128_rshift(&d, 52); VERIFY_BITS(u0, 52); - VERIFY_BITS(d, 62); + VERIFY_BITS_128(&d, 62); /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ u0 = (u0 << 4) | tx; VERIFY_BITS(u0, 56); /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - c += (uint128_t)u0 * (R >> 4); - VERIFY_BITS(c, 113); + secp256k1_u128_accum_mul(&c, u0, R >> 4); + VERIFY_BITS_128(&c, 113); /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ - r[0] = c & M; c >>= 52; + r[0] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52); VERIFY_BITS(r[0], 52); - VERIFY_BITS(c, 61); + VERIFY_BITS_128(&c, 61); /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ a0 *= 2; - c += (uint128_t)a0 * a1; - VERIFY_BITS(c, 114); + secp256k1_u128_accum_mul(&c, a0, a1); + VERIFY_BITS_128(&c, 114); /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ - d += (uint128_t)a2 * a4 - + (uint128_t)a3 * a3; - VERIFY_BITS(d, 114); + secp256k1_u128_accum_mul(&d, a2, a4); + secp256k1_u128_accum_mul(&d, a3, a3); + VERIFY_BITS_128(&d, 114); /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - c += (d & M) * R; d >>= 52; - VERIFY_BITS(c, 115); - VERIFY_BITS(d, 62); + secp256k1_u128_accum_mul(&c, secp256k1_u128_to_u64(&d) & M, R); secp256k1_u128_rshift(&d, 52); + VERIFY_BITS_128(&c, 115); + VERIFY_BITS_128(&d, 62); /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - r[1] = c & M; c >>= 52; + r[1] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52); VERIFY_BITS(r[1], 52); - VERIFY_BITS(c, 63); + VERIFY_BITS_128(&c, 63); /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ - c += (uint128_t)a0 * a2 - + (uint128_t)a1 * a1; - VERIFY_BITS(c, 114); + secp256k1_u128_accum_mul(&c, a0, a2); + secp256k1_u128_accum_mul(&c, a1, a1); + VERIFY_BITS_128(&c, 114); /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ - d += (uint128_t)a3 * a4; - VERIFY_BITS(d, 114); + secp256k1_u128_accum_mul(&d, a3, a4); + VERIFY_BITS_128(&d, 114); /* [d 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += (d & M) * R; d >>= 52; - VERIFY_BITS(c, 115); - VERIFY_BITS(d, 62); - /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[2] = c & M; c >>= 52; + secp256k1_u128_accum_mul(&c, R, secp256k1_u128_to_u64(&d)); secp256k1_u128_rshift(&d, 64); + VERIFY_BITS_128(&c, 115); + VERIFY_BITS_128(&d, 50); + /* [(d<<12) 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52); VERIFY_BITS(r[2], 52); - VERIFY_BITS(c, 63); - /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + VERIFY_BITS_128(&c, 63); + /* [(d<<12) 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += d * R + t3; - VERIFY_BITS(c, 100); + secp256k1_u128_accum_mul(&c, R << 12, secp256k1_u128_to_u64(&d)); + secp256k1_u128_accum_u64(&c, t3); + VERIFY_BITS_128(&c, 100); /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[3] = c & M; c >>= 52; + r[3] = secp256k1_u128_to_u64(&c) & M; secp256k1_u128_rshift(&c, 52); VERIFY_BITS(r[3], 52); - VERIFY_BITS(c, 48); + VERIFY_BITS_128(&c, 48); /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - c += t4; - VERIFY_BITS(c, 49); - /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ - r[4] = c; + r[4] = secp256k1_u128_to_u64(&c) + t4; VERIFY_BITS(r[4], 49); /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ } diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h index bb30b2af2..989e9cdb2 100644 --- a/src/secp256k1/src/field_impl.h +++ b/src/secp256k1/src/field_impl.h @@ -1,41 +1,36 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_FIELD_IMPL_H #define SECP256K1_FIELD_IMPL_H -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - +#include "field.h" #include "util.h" -#if defined(USE_FIELD_10X26) -#include "field_10x26_impl.h" -#elif defined(USE_FIELD_5X52) +#if defined(SECP256K1_WIDEMUL_INT128) #include "field_5x52_impl.h" +#elif defined(SECP256K1_WIDEMUL_INT64) +#include "field_10x26_impl.h" #else -#error "Please select field implementation" +#error "Please select wide multiplication implementation" #endif SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { secp256k1_fe na; - secp256k1_fe_negate(&na, a, 1); - secp256k1_fe_add(&na, b); - return secp256k1_fe_normalizes_to_zero(&na); -} + SECP256K1_FE_VERIFY(a); + SECP256K1_FE_VERIFY(b); + SECP256K1_FE_VERIFY_MAGNITUDE(a, 1); + SECP256K1_FE_VERIFY_MAGNITUDE(b, 31); -SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) { - secp256k1_fe na; secp256k1_fe_negate(&na, a, 1); secp256k1_fe_add(&na, b); - return secp256k1_fe_normalizes_to_zero_var(&na); + return secp256k1_fe_normalizes_to_zero(&na); } -static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { +static int secp256k1_fe_sqrt(secp256k1_fe * SECP256K1_RESTRICT r, const secp256k1_fe * SECP256K1_RESTRICT a) { /** Given that p is congruent to 3 mod 4, we can compute the square root of * a mod p as the (p+1)/4'th power of a. * @@ -46,7 +41,11 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { * itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)). */ secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; - int j; + int j, ret; + + VERIFY_CHECK(r != a); + SECP256K1_FE_VERIFY(a); + SECP256K1_FE_VERIFY_MAGNITUDE(a, 8); /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in * { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: @@ -130,186 +129,334 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { /* Check that a square root was actually calculated */ secp256k1_fe_sqr(&t1, r); - return secp256k1_fe_equal(&t1, a); + ret = secp256k1_fe_equal(&t1, a); + +#ifdef VERIFY + if (!ret) { + secp256k1_fe_negate(&t1, &t1, 1); + secp256k1_fe_normalize_var(&t1); + VERIFY_CHECK(secp256k1_fe_equal(&t1, a)); + } +#endif + return ret; } -static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) { - secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; - int j; +#ifndef VERIFY +static void secp256k1_fe_verify(const secp256k1_fe *a) { (void)a; } +static void secp256k1_fe_verify_magnitude(const secp256k1_fe *a, int m) { (void)a; (void)m; } +#else +static void secp256k1_fe_impl_verify(const secp256k1_fe *a); +static void secp256k1_fe_verify(const secp256k1_fe *a) { + /* Magnitude between 0 and 32. */ + SECP256K1_FE_VERIFY_MAGNITUDE(a, 32); + /* Normalized is 0 or 1. */ + VERIFY_CHECK((a->normalized == 0) || (a->normalized == 1)); + /* If normalized, magnitude must be 0 or 1. */ + if (a->normalized) SECP256K1_FE_VERIFY_MAGNITUDE(a, 1); + /* Invoke implementation-specific checks. */ + secp256k1_fe_impl_verify(a); +} - /** The binary representation of (p - 2) has 5 blocks of 1s, with lengths in - * { 1, 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: - * [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] - */ +static void secp256k1_fe_verify_magnitude(const secp256k1_fe *a, int m) { + VERIFY_CHECK(m >= 0); + VERIFY_CHECK(m <= 32); + VERIFY_CHECK(a->magnitude <= m); +} - secp256k1_fe_sqr(&x2, a); - secp256k1_fe_mul(&x2, &x2, a); +static void secp256k1_fe_impl_normalize(secp256k1_fe *r); +SECP256K1_INLINE static void secp256k1_fe_normalize(secp256k1_fe *r) { + SECP256K1_FE_VERIFY(r); - secp256k1_fe_sqr(&x3, &x2); - secp256k1_fe_mul(&x3, &x3, a); + secp256k1_fe_impl_normalize(r); + r->magnitude = 1; + r->normalized = 1; - x6 = x3; - for (j=0; j<3; j++) { - secp256k1_fe_sqr(&x6, &x6); - } - secp256k1_fe_mul(&x6, &x6, &x3); + SECP256K1_FE_VERIFY(r); +} - x9 = x6; - for (j=0; j<3; j++) { - secp256k1_fe_sqr(&x9, &x9); - } - secp256k1_fe_mul(&x9, &x9, &x3); +static void secp256k1_fe_impl_normalize_weak(secp256k1_fe *r); +SECP256K1_INLINE static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { + SECP256K1_FE_VERIFY(r); - x11 = x9; - for (j=0; j<2; j++) { - secp256k1_fe_sqr(&x11, &x11); - } - secp256k1_fe_mul(&x11, &x11, &x2); + secp256k1_fe_impl_normalize_weak(r); + r->magnitude = 1; - x22 = x11; - for (j=0; j<11; j++) { - secp256k1_fe_sqr(&x22, &x22); - } - secp256k1_fe_mul(&x22, &x22, &x11); + SECP256K1_FE_VERIFY(r); +} - x44 = x22; - for (j=0; j<22; j++) { - secp256k1_fe_sqr(&x44, &x44); - } - secp256k1_fe_mul(&x44, &x44, &x22); +static void secp256k1_fe_impl_normalize_var(secp256k1_fe *r); +SECP256K1_INLINE static void secp256k1_fe_normalize_var(secp256k1_fe *r) { + SECP256K1_FE_VERIFY(r); - x88 = x44; - for (j=0; j<44; j++) { - secp256k1_fe_sqr(&x88, &x88); - } - secp256k1_fe_mul(&x88, &x88, &x44); + secp256k1_fe_impl_normalize_var(r); + r->magnitude = 1; + r->normalized = 1; - x176 = x88; - for (j=0; j<88; j++) { - secp256k1_fe_sqr(&x176, &x176); - } - secp256k1_fe_mul(&x176, &x176, &x88); + SECP256K1_FE_VERIFY(r); +} - x220 = x176; - for (j=0; j<44; j++) { - secp256k1_fe_sqr(&x220, &x220); - } - secp256k1_fe_mul(&x220, &x220, &x44); +static int secp256k1_fe_impl_normalizes_to_zero(const secp256k1_fe *r); +SECP256K1_INLINE static int secp256k1_fe_normalizes_to_zero(const secp256k1_fe *r) { + SECP256K1_FE_VERIFY(r); - x223 = x220; - for (j=0; j<3; j++) { - secp256k1_fe_sqr(&x223, &x223); - } - secp256k1_fe_mul(&x223, &x223, &x3); + return secp256k1_fe_impl_normalizes_to_zero(r); +} - /* The final result is then assembled using a sliding window over the blocks. */ +static int secp256k1_fe_impl_normalizes_to_zero_var(const secp256k1_fe *r); +SECP256K1_INLINE static int secp256k1_fe_normalizes_to_zero_var(const secp256k1_fe *r) { + SECP256K1_FE_VERIFY(r); - t1 = x223; - for (j=0; j<23; j++) { - secp256k1_fe_sqr(&t1, &t1); - } - secp256k1_fe_mul(&t1, &t1, &x22); - for (j=0; j<5; j++) { - secp256k1_fe_sqr(&t1, &t1); - } - secp256k1_fe_mul(&t1, &t1, a); - for (j=0; j<3; j++) { - secp256k1_fe_sqr(&t1, &t1); - } - secp256k1_fe_mul(&t1, &t1, &x2); - for (j=0; j<2; j++) { - secp256k1_fe_sqr(&t1, &t1); - } - secp256k1_fe_mul(r, a, &t1); + return secp256k1_fe_impl_normalizes_to_zero_var(r); } -static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) { -#if defined(USE_FIELD_INV_BUILTIN) - secp256k1_fe_inv(r, a); -#elif defined(USE_FIELD_INV_NUM) - secp256k1_num n, m; - static const secp256k1_fe negone = SECP256K1_FE_CONST( - 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, - 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0xFFFFFC2EUL - ); - /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ - static const unsigned char prime[32] = { - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F - }; - unsigned char b[32]; - int res; - secp256k1_fe c = *a; - secp256k1_fe_normalize_var(&c); - secp256k1_fe_get_b32(b, &c); - secp256k1_num_set_bin(&n, b, 32); - secp256k1_num_set_bin(&m, prime, 32); - secp256k1_num_mod_inverse(&n, &n, &m); - secp256k1_num_get_bin(b, 32, &n); - res = secp256k1_fe_set_b32(r, b); - (void)res; - VERIFY_CHECK(res); - /* Verify the result is the (unique) valid inverse using non-GMP code. */ - secp256k1_fe_mul(&c, &c, r); - secp256k1_fe_add(&c, &negone); - CHECK(secp256k1_fe_normalizes_to_zero_var(&c)); -#else -#error "Please select field inverse implementation" -#endif +static void secp256k1_fe_impl_set_int(secp256k1_fe *r, int a); +SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { + VERIFY_CHECK(0 <= a && a <= 0x7FFF); + + secp256k1_fe_impl_set_int(r, a); + r->magnitude = (a != 0); + r->normalized = 1; + + SECP256K1_FE_VERIFY(r); } -static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) { - secp256k1_fe u; - size_t i; - if (len < 1) { - return; - } +static void secp256k1_fe_impl_add_int(secp256k1_fe *r, int a); +SECP256K1_INLINE static void secp256k1_fe_add_int(secp256k1_fe *r, int a) { + VERIFY_CHECK(0 <= a && a <= 0x7FFF); + SECP256K1_FE_VERIFY(r); - VERIFY_CHECK((r + len <= a) || (a + len <= r)); + secp256k1_fe_impl_add_int(r, a); + r->magnitude += 1; + r->normalized = 0; - r[0] = a[0]; + SECP256K1_FE_VERIFY(r); +} - i = 0; - while (++i < len) { - secp256k1_fe_mul(&r[i], &r[i - 1], &a[i]); - } +static void secp256k1_fe_impl_clear(secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { + a->magnitude = 0; + a->normalized = 1; + secp256k1_fe_impl_clear(a); + + SECP256K1_FE_VERIFY(a); +} + +static int secp256k1_fe_impl_is_zero(const secp256k1_fe *a); +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { + SECP256K1_FE_VERIFY(a); + VERIFY_CHECK(a->normalized); + + return secp256k1_fe_impl_is_zero(a); +} + +static int secp256k1_fe_impl_is_odd(const secp256k1_fe *a); +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { + SECP256K1_FE_VERIFY(a); + VERIFY_CHECK(a->normalized); - secp256k1_fe_inv_var(&u, &r[--i]); + return secp256k1_fe_impl_is_odd(a); +} + +static int secp256k1_fe_impl_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b); +SECP256K1_INLINE static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { + SECP256K1_FE_VERIFY(a); + SECP256K1_FE_VERIFY(b); + VERIFY_CHECK(a->normalized); + VERIFY_CHECK(b->normalized); + + return secp256k1_fe_impl_cmp_var(a, b); +} + +static void secp256k1_fe_impl_set_b32_mod(secp256k1_fe *r, const unsigned char *a); +SECP256K1_INLINE static void secp256k1_fe_set_b32_mod(secp256k1_fe *r, const unsigned char *a) { + secp256k1_fe_impl_set_b32_mod(r, a); + r->magnitude = 1; + r->normalized = 0; - while (i > 0) { - size_t j = i--; - secp256k1_fe_mul(&r[j], &r[i], &u); - secp256k1_fe_mul(&u, &u, &a[j]); + SECP256K1_FE_VERIFY(r); +} + +static int secp256k1_fe_impl_set_b32_limit(secp256k1_fe *r, const unsigned char *a); +SECP256K1_INLINE static int secp256k1_fe_set_b32_limit(secp256k1_fe *r, const unsigned char *a) { + if (secp256k1_fe_impl_set_b32_limit(r, a)) { + r->magnitude = 1; + r->normalized = 1; + SECP256K1_FE_VERIFY(r); + return 1; + } else { + /* Mark the output field element as invalid. */ + r->magnitude = -1; + return 0; } +} - r[0] = u; +static void secp256k1_fe_impl_get_b32(unsigned char *r, const secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { + SECP256K1_FE_VERIFY(a); + VERIFY_CHECK(a->normalized); + + secp256k1_fe_impl_get_b32(r, a); } -static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { -#ifndef USE_NUM_NONE - unsigned char b[32]; - secp256k1_num n; - secp256k1_num m; - /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ - static const unsigned char prime[32] = { - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F - }; - - secp256k1_fe c = *a; - secp256k1_fe_normalize_var(&c); - secp256k1_fe_get_b32(b, &c); - secp256k1_num_set_bin(&n, b, 32); - secp256k1_num_set_bin(&m, prime, 32); - return secp256k1_num_jacobi(&n, &m) >= 0; -#else - secp256k1_fe r; - return secp256k1_fe_sqrt(&r, a); -#endif +static void secp256k1_fe_impl_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m); +SECP256K1_INLINE static void secp256k1_fe_negate_unchecked(secp256k1_fe *r, const secp256k1_fe *a, int m) { + SECP256K1_FE_VERIFY(a); + VERIFY_CHECK(m >= 0 && m <= 31); + SECP256K1_FE_VERIFY_MAGNITUDE(a, m); + + secp256k1_fe_impl_negate_unchecked(r, a, m); + r->magnitude = m + 1; + r->normalized = 0; + + SECP256K1_FE_VERIFY(r); +} + +static void secp256k1_fe_impl_mul_int_unchecked(secp256k1_fe *r, int a); +SECP256K1_INLINE static void secp256k1_fe_mul_int_unchecked(secp256k1_fe *r, int a) { + SECP256K1_FE_VERIFY(r); + + VERIFY_CHECK(a >= 0 && a <= 32); + VERIFY_CHECK(a*r->magnitude <= 32); + secp256k1_fe_impl_mul_int_unchecked(r, a); + r->magnitude *= a; + r->normalized = 0; + + SECP256K1_FE_VERIFY(r); } +static void secp256k1_fe_impl_add(secp256k1_fe *r, const secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { + SECP256K1_FE_VERIFY(r); + SECP256K1_FE_VERIFY(a); + VERIFY_CHECK(r->magnitude + a->magnitude <= 32); + + secp256k1_fe_impl_add(r, a); + r->magnitude += a->magnitude; + r->normalized = 0; + + SECP256K1_FE_VERIFY(r); +} + +static void secp256k1_fe_impl_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b); +SECP256K1_INLINE static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { + SECP256K1_FE_VERIFY(a); + SECP256K1_FE_VERIFY(b); + SECP256K1_FE_VERIFY_MAGNITUDE(a, 8); + SECP256K1_FE_VERIFY_MAGNITUDE(b, 8); + VERIFY_CHECK(r != b); + VERIFY_CHECK(a != b); + + secp256k1_fe_impl_mul(r, a, b); + r->magnitude = 1; + r->normalized = 0; + + SECP256K1_FE_VERIFY(r); +} + +static void secp256k1_fe_impl_sqr(secp256k1_fe *r, const secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { + SECP256K1_FE_VERIFY(a); + SECP256K1_FE_VERIFY_MAGNITUDE(a, 8); + + secp256k1_fe_impl_sqr(r, a); + r->magnitude = 1; + r->normalized = 0; + + SECP256K1_FE_VERIFY(r); +} + +static void secp256k1_fe_impl_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); +SECP256K1_INLINE static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { + VERIFY_CHECK(flag == 0 || flag == 1); + SECP256K1_FE_VERIFY(a); + SECP256K1_FE_VERIFY(r); + + secp256k1_fe_impl_cmov(r, a, flag); + if (a->magnitude > r->magnitude) r->magnitude = a->magnitude; + if (!a->normalized) r->normalized = 0; + + SECP256K1_FE_VERIFY(r); +} + +static void secp256k1_fe_impl_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); +SECP256K1_INLINE static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { + SECP256K1_FE_VERIFY(a); + VERIFY_CHECK(a->normalized); + + secp256k1_fe_impl_to_storage(r, a); +} + +static void secp256k1_fe_impl_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a); +SECP256K1_INLINE static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { + secp256k1_fe_impl_from_storage(r, a); + r->magnitude = 1; + r->normalized = 1; + + SECP256K1_FE_VERIFY(r); +} + +static void secp256k1_fe_impl_inv(secp256k1_fe *r, const secp256k1_fe *x); +SECP256K1_INLINE static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *x) { + int input_is_zero = secp256k1_fe_normalizes_to_zero(x); + SECP256K1_FE_VERIFY(x); + + secp256k1_fe_impl_inv(r, x); + r->magnitude = x->magnitude > 0; + r->normalized = 1; + + VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == input_is_zero); + SECP256K1_FE_VERIFY(r); +} + +static void secp256k1_fe_impl_inv_var(secp256k1_fe *r, const secp256k1_fe *x); +SECP256K1_INLINE static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { + int input_is_zero = secp256k1_fe_normalizes_to_zero(x); + SECP256K1_FE_VERIFY(x); + + secp256k1_fe_impl_inv_var(r, x); + r->magnitude = x->magnitude > 0; + r->normalized = 1; + + VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == input_is_zero); + SECP256K1_FE_VERIFY(r); +} + +static int secp256k1_fe_impl_is_square_var(const secp256k1_fe *x); +SECP256K1_INLINE static int secp256k1_fe_is_square_var(const secp256k1_fe *x) { + int ret; + secp256k1_fe tmp = *x, sqrt; + SECP256K1_FE_VERIFY(x); + + ret = secp256k1_fe_impl_is_square_var(x); + secp256k1_fe_normalize_weak(&tmp); + VERIFY_CHECK(ret == secp256k1_fe_sqrt(&sqrt, &tmp)); + return ret; +} + +static void secp256k1_fe_impl_get_bounds(secp256k1_fe* r, int m); +SECP256K1_INLINE static void secp256k1_fe_get_bounds(secp256k1_fe* r, int m) { + VERIFY_CHECK(m >= 0); + VERIFY_CHECK(m <= 32); + + secp256k1_fe_impl_get_bounds(r, m); + r->magnitude = m; + r->normalized = (m == 0); + + SECP256K1_FE_VERIFY(r); +} + +static void secp256k1_fe_impl_half(secp256k1_fe *r); +SECP256K1_INLINE static void secp256k1_fe_half(secp256k1_fe *r) { + SECP256K1_FE_VERIFY(r); + SECP256K1_FE_VERIFY_MAGNITUDE(r, 31); + + secp256k1_fe_impl_half(r); + r->magnitude = (r->magnitude >> 1) + 1; + r->normalized = 0; + + SECP256K1_FE_VERIFY(r); +} + +#endif /* defined(VERIFY) */ + #endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c deleted file mode 100644 index 8016875bf..000000000 --- a/src/secp256k1/src/gen_context.c +++ /dev/null @@ -1,74 +0,0 @@ -/************************************************************************ - * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#define USE_BASIC_CONFIG 1 - -#include "basic-config.h" -#include "include/secp256k1.h" -#include "field_impl.h" -#include "scalar_impl.h" -#include "group_impl.h" -#include "ecmult_gen_impl.h" - -static void default_error_callback_fn(const char* str, void* data) { - (void)data; - fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); - abort(); -} - -static const secp256k1_callback default_error_callback = { - default_error_callback_fn, - NULL -}; - -int main(int argc, char **argv) { - secp256k1_ecmult_gen_context ctx; - int inner; - int outer; - FILE* fp; - - (void)argc; - (void)argv; - - fp = fopen("src/ecmult_static_context.h","w"); - if (fp == NULL) { - fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n"); - return -1; - } - - fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); - fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); - fprintf(fp, "#include \"group.h\"\n"); - fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n"); - fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n"); - - secp256k1_ecmult_gen_context_init(&ctx); - secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback); - for(outer = 0; outer != 64; outer++) { - fprintf(fp,"{\n"); - for(inner = 0; inner != 16; inner++) { - fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner])); - if (inner != 15) { - fprintf(fp,",\n"); - } else { - fprintf(fp,"\n"); - } - } - if (outer != 63) { - fprintf(fp,"},\n"); - } else { - fprintf(fp,"}\n"); - } - } - fprintf(fp,"};\n"); - secp256k1_ecmult_gen_context_clear(&ctx); - - fprintf(fp, "#undef SC\n"); - fprintf(fp, "#endif\n"); - fclose(fp); - - return 0; -} diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h index 6d9d7fa1e..d81deb426 100644 --- a/src/secp256k1/src/group.h +++ b/src/secp256k1/src/group.h @@ -1,16 +1,18 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_GROUP_H #define SECP256K1_GROUP_H -#include "num.h" #include "field.h" -/** A group element of the secp256k1 curve, in affine coordinates. */ +/** A group element in affine coordinates on the secp256k1 curve, + * or occasionally on an isomorphic curve of the form y^2 = x^3 + 7*t^6. + * Note: For exhaustive test mode, secp256k1 is replaced by a small subgroup of a different curve. + */ typedef struct { secp256k1_fe x; secp256k1_fe y; @@ -20,7 +22,9 @@ typedef struct { #define SECP256K1_GE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), 0} #define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} -/** A group element of the secp256k1 curve, in jacobian coordinates. */ +/** A group element of the secp256k1 curve, in jacobian coordinates. + * Note: For exhastive test mode, secp256k1 is replaced by a small subgroup of a different curve. + */ typedef struct { secp256k1_fe x; /* actual X: x/z^2 */ secp256k1_fe y; /* actual Y: y/z^3 */ @@ -40,44 +44,69 @@ typedef struct { #define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y) +/** Maximum allowed magnitudes for group element coordinates + * in affine (x, y) and jacobian (x, y, z) representation. */ +#define SECP256K1_GE_X_MAGNITUDE_MAX 4 +#define SECP256K1_GE_Y_MAGNITUDE_MAX 3 +#define SECP256K1_GEJ_X_MAGNITUDE_MAX 4 +#define SECP256K1_GEJ_Y_MAGNITUDE_MAX 4 +#define SECP256K1_GEJ_Z_MAGNITUDE_MAX 1 + /** Set a group element equal to the point with given X and Y coordinates */ static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y); -/** Set a group element (affine) equal to the point with the given X coordinate - * and a Y coordinate that is a quadratic residue modulo p. The return value - * is true iff a coordinate with the given X coordinate exists. - */ -static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); - /** Set a group element (affine) equal to the point with the given X coordinate, and given oddness * for Y. Return value indicates whether the result is valid. */ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); +/** Determine whether x is a valid X coordinate on the curve. */ +static int secp256k1_ge_x_on_curve_var(const secp256k1_fe *x); + +/** Determine whether fraction xn/xd is a valid X coordinate on the curve (xd != 0). */ +static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp256k1_fe *xd); + /** Check whether a group element is the point at infinity. */ static int secp256k1_ge_is_infinity(const secp256k1_ge *a); /** Check whether a group element is valid (i.e., on the curve). */ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a); +/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a); -/** Set a group element equal to another which is given in jacobian coordinates */ +/** Set a group element equal to another which is given in jacobian coordinates. Constant time. */ static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); +/** Set a group element equal to another which is given in jacobian coordinates. */ +static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a); + /** Set a batch of group elements equal to the inputs given in jacobian coordinates */ -static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb); +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len); + +/** Bring a batch of inputs to the same global z "denominator", based on ratios between + * (omitted) z coordinates of adjacent elements. + * + * Although the elements a[i] are _ge rather than _gej, they actually represent elements + * in Jacobian coordinates with their z coordinates omitted. + * + * Using the notation z(b) to represent the omitted z coordinate of b, the array zr of + * z coordinate ratios must satisfy zr[i] == z(a[i]) / z(a[i-1]) for 0 < 'i' < len. + * The zr[0] value is unused. + * + * This function adjusts the coordinates of 'a' in place so that for all 'i', z(a[i]) == z(a[len-1]). + * In other words, the initial value of z(a[len-1]) becomes the global z "denominator". Only the + * a[i].x and a[i].y coordinates are explicitly modified; the adjustment of the omitted z coordinate is + * implicit. + * + * The coordinates of the final element a[len-1] are not changed. + */ +static void secp256k1_ge_table_set_globalz(size_t len, secp256k1_ge *a, const secp256k1_fe *zr); -/** Set a batch of group elements equal to the inputs given in jacobian - * coordinates (with known z-ratios). zr must contain the known z-ratios such - * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */ -static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len); +/** Check two group elements (affine) for equality in variable time. */ +static int secp256k1_ge_eq_var(const secp256k1_ge *a, const secp256k1_ge *b); -/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to - * the same global z "denominator". zr must contain the known z-ratios such - * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. The x and y - * coordinates of the result are stored in r, the common z coordinate is - * stored in globalz. */ -static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr); +/** Set a group element (affine) equal to the point at infinity. */ +static void secp256k1_ge_set_infinity(secp256k1_ge *r); /** Set a group element (jacobian) equal to the point at infinity. */ static void secp256k1_gej_set_infinity(secp256k1_gej *r); @@ -85,7 +114,14 @@ static void secp256k1_gej_set_infinity(secp256k1_gej *r); /** Set a group element (jacobian) equal to another which is given in affine coordinates. */ static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a); -/** Compare the X coordinate of a group element (jacobian). */ +/** Check two group elements (jacobian) for equality in variable time. */ +static int secp256k1_gej_eq_var(const secp256k1_gej *a, const secp256k1_gej *b); + +/** Check two group elements (jacobian and affine) for equality in variable time. */ +static int secp256k1_gej_eq_ge_var(const secp256k1_gej *a, const secp256k1_ge *b); + +/** Compare the X coordinate of a group element (jacobian). + * The magnitude of the group element's X coordinate must not exceed 31. */ static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a); /** Set r equal to the inverse of a (i.e., mirrored around the X axis) */ @@ -94,17 +130,13 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a); /** Check whether a group element is the point at infinity. */ static int secp256k1_gej_is_infinity(const secp256k1_gej *a); -/** Check whether a group element's y coordinate is a quadratic residue. */ -static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); +/** Set r equal to the double of a. Constant time. */ +static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a); -/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). - * a may not be zero. Constant time. */ -static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); - -/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */ +/** Set r equal to the double of a. If rzr is not-NULL this sets *rzr such that r->z == a->z * *rzr (where infinity means an implicit z = 0). */ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); -/** Set r equal to the sum of a and b. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ +/** Set r equal to the sum of a and b. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr); /** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */ @@ -112,16 +144,14 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const /** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time - guarantee, and b is allowed to be infinity. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ + guarantee, and b is allowed to be infinity. If rzr is non-NULL this sets *rzr such that r->z == a->z * *rzr (a cannot be infinity in that case). */ static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr); /** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv); -#ifdef USE_ENDOMORPHISM /** Set r to be equal to lambda times a, where lambda is chosen in a way such that this is very fast. */ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a); -#endif /** Clear a secp256k1_gej to prevent leaking sensitive information. */ static void secp256k1_gej_clear(secp256k1_gej *r); @@ -135,10 +165,32 @@ static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge /** Convert a group element back from the storage type. */ static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a); -/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ +static void secp256k1_gej_cmov(secp256k1_gej *r, const secp256k1_gej *a, int flag); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag); /** Rescale a jacobian point by b which must be non-zero. Constant-time. */ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); +/** Determine if a point (which is assumed to be on the curve) is in the correct (sub)group of the curve. + * + * In normal mode, the used group is secp256k1, which has cofactor=1 meaning that every point on the curve is in the + * group, and this function returns always true. + * + * When compiling in exhaustive test mode, a slightly different curve equation is used, leading to a group with a + * (very) small subgroup, and that subgroup is what is used for all cryptographic operations. In that mode, this + * function checks whether a point that is on the curve is in fact also in that subgroup. + */ +static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge); + +/** Check invariants on an affine group element (no-op unless VERIFY is enabled). */ +static void secp256k1_ge_verify(const secp256k1_ge *a); +#define SECP256K1_GE_VERIFY(a) secp256k1_ge_verify(a) + +/** Check invariants on a Jacobian group element (no-op unless VERIFY is enabled). */ +static void secp256k1_gej_verify(const secp256k1_gej *a); +#define SECP256K1_GEJ_VERIFY(a) secp256k1_gej_verify(a) + #endif /* SECP256K1_GROUP_H */ diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h index 80c3122c6..537be32ff 100644 --- a/src/secp256k1/src/group_impl.h +++ b/src/secp256k1/src/group_impl.h @@ -1,104 +1,163 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_GROUP_IMPL_H #define SECP256K1_GROUP_IMPL_H -#include "num.h" #include "field.h" #include "group.h" - -/* These points can be generated in sage as follows: - * - * 0. Setup a worksheet with the following parameters. - * b = 4 # whatever CURVE_B will be set to - * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) - * C = EllipticCurve ([F (0), F (b)]) - * - * 1. Determine all the small orders available to you. (If there are - * no satisfactory ones, go back and change b.) - * print C.order().factor(limit=1000) - * - * 2. Choose an order as one of the prime factors listed in the above step. - * (You can also multiply some to get a composite order, though the - * tests will crash trying to invert scalars during signing.) We take a - * random point and scale it to drop its order to the desired value. - * There is some probability this won't work; just try again. - * order = 199 - * P = C.random_point() - * P = (int(P.order()) / int(order)) * P - * assert(P.order() == order) - * - * 3. Print the values. You'll need to use a vim macro or something to - * split the hex output into 4-byte chunks. - * print "%x %x" % P.xy() +#include "util.h" + +/* Begin of section generated by sage/gen_exhaustive_groups.sage. */ +#define SECP256K1_G_ORDER_7 SECP256K1_GE_CONST(\ + 0x66625d13, 0x317ffe44, 0x63d32cff, 0x1ca02b9b,\ + 0xe5c6d070, 0x50b4b05e, 0x81cc30db, 0xf5166f0a,\ + 0x1e60e897, 0xa7c00c7c, 0x2df53eb6, 0x98274ff4,\ + 0x64252f42, 0x8ca44e17, 0x3b25418c, 0xff4ab0cf\ +) +#define SECP256K1_G_ORDER_13 SECP256K1_GE_CONST(\ + 0xa2482ff8, 0x4bf34edf, 0xa51262fd, 0xe57921db,\ + 0xe0dd2cb7, 0xa5914790, 0xbc71631f, 0xc09704fb,\ + 0x942536cb, 0xa3e49492, 0x3a701cc3, 0xee3e443f,\ + 0xdf182aa9, 0x15b8aa6a, 0x166d3b19, 0xba84b045\ +) +#define SECP256K1_G_ORDER_199 SECP256K1_GE_CONST(\ + 0x7fb07b5c, 0xd07c3bda, 0x553902e2, 0x7a87ea2c,\ + 0x35108a7f, 0x051f41e5, 0xb76abad5, 0x1f2703ad,\ + 0x0a251539, 0x5b4c4438, 0x952a634f, 0xac10dd4d,\ + 0x6d6f4745, 0x98990c27, 0x3a4f3116, 0xd32ff969\ +) +/** Generator for secp256k1, value 'g' defined in + * "Standards for Efficient Cryptography" (SEC2) 2.7.1. + */ +#define SECP256K1_G SECP256K1_GE_CONST(\ + 0x79be667e, 0xf9dcbbac, 0x55a06295, 0xce870b07,\ + 0x029bfcdb, 0x2dce28d9, 0x59f2815b, 0x16f81798,\ + 0x483ada77, 0x26a3c465, 0x5da4fbfc, 0x0e1108a8,\ + 0xfd17b448, 0xa6855419, 0x9c47d08f, 0xfb10d4b8\ +) +/* These exhaustive group test orders and generators are chosen such that: + * - The field size is equal to that of secp256k1, so field code is the same. + * - The curve equation is of the form y^2=x^3+B for some small constant B. + * - The subgroup has a generator 2*P, where P.x is as small as possible. + * - The subgroup has size less than 1000 to permit exhaustive testing. + * - The subgroup admits an endomorphism of the form lambda*(x,y) == (beta*x,y). */ #if defined(EXHAUSTIVE_TEST_ORDER) -# if EXHAUSTIVE_TEST_ORDER == 199 -const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( - 0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069, - 0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18, - 0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868, - 0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED -); - -const int CURVE_B = 4; +# if EXHAUSTIVE_TEST_ORDER == 7 + +static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_7; +#define SECP256K1_B 6 + # elif EXHAUSTIVE_TEST_ORDER == 13 -const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( - 0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0, - 0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15, - 0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e, - 0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac -); -const int CURVE_B = 2; + +static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_13; +#define SECP256K1_B 2 + +# elif EXHAUSTIVE_TEST_ORDER == 199 + +static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G_ORDER_199; +#define SECP256K1_B 4 + # else # error No known generator for the specified exhaustive test group order. # endif #else -/** Generator for secp256k1, value 'g' defined in - * "Standards for Efficient Cryptography" (SEC2) 2.7.1. - */ -static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( - 0x79BE667EUL, 0xF9DCBBACUL, 0x55A06295UL, 0xCE870B07UL, - 0x029BFCDBUL, 0x2DCE28D9UL, 0x59F2815BUL, 0x16F81798UL, - 0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL, - 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL -); - -const int CURVE_B = 7; + +static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_G; +#define SECP256K1_B 7 + #endif +/* End of section generated by sage/gen_exhaustive_groups.sage. */ + +static void secp256k1_ge_verify(const secp256k1_ge *a) { + SECP256K1_FE_VERIFY(&a->x); + SECP256K1_FE_VERIFY(&a->y); + SECP256K1_FE_VERIFY_MAGNITUDE(&a->x, SECP256K1_GE_X_MAGNITUDE_MAX); + SECP256K1_FE_VERIFY_MAGNITUDE(&a->y, SECP256K1_GE_Y_MAGNITUDE_MAX); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); + (void)a; +} + +static void secp256k1_gej_verify(const secp256k1_gej *a) { + SECP256K1_FE_VERIFY(&a->x); + SECP256K1_FE_VERIFY(&a->y); + SECP256K1_FE_VERIFY(&a->z); + SECP256K1_FE_VERIFY_MAGNITUDE(&a->x, SECP256K1_GEJ_X_MAGNITUDE_MAX); + SECP256K1_FE_VERIFY_MAGNITUDE(&a->y, SECP256K1_GEJ_Y_MAGNITUDE_MAX); + SECP256K1_FE_VERIFY_MAGNITUDE(&a->z, SECP256K1_GEJ_Z_MAGNITUDE_MAX); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); + (void)a; +} +/* Set r to the affine coordinates of Jacobian point (a.x, a.y, 1/zi). */ static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { secp256k1_fe zi2; secp256k1_fe zi3; + SECP256K1_GEJ_VERIFY(a); + SECP256K1_FE_VERIFY(zi); + VERIFY_CHECK(!a->infinity); + secp256k1_fe_sqr(&zi2, zi); secp256k1_fe_mul(&zi3, &zi2, zi); secp256k1_fe_mul(&r->x, &a->x, &zi2); secp256k1_fe_mul(&r->y, &a->y, &zi3); r->infinity = a->infinity; + + SECP256K1_GE_VERIFY(r); +} + +/* Set r to the affine coordinates of Jacobian point (a.x, a.y, 1/zi). */ +static void secp256k1_ge_set_ge_zinv(secp256k1_ge *r, const secp256k1_ge *a, const secp256k1_fe *zi) { + secp256k1_fe zi2; + secp256k1_fe zi3; + SECP256K1_GE_VERIFY(a); + SECP256K1_FE_VERIFY(zi); + VERIFY_CHECK(!a->infinity); + + secp256k1_fe_sqr(&zi2, zi); + secp256k1_fe_mul(&zi3, &zi2, zi); + secp256k1_fe_mul(&r->x, &a->x, &zi2); + secp256k1_fe_mul(&r->y, &a->y, &zi3); + r->infinity = a->infinity; + + SECP256K1_GE_VERIFY(r); } static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) { + SECP256K1_FE_VERIFY(x); + SECP256K1_FE_VERIFY(y); + r->infinity = 0; r->x = *x; r->y = *y; + + SECP256K1_GE_VERIFY(r); } static int secp256k1_ge_is_infinity(const secp256k1_ge *a) { + SECP256K1_GE_VERIFY(a); + return a->infinity; } static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a) { + SECP256K1_GE_VERIFY(a); + *r = *a; secp256k1_fe_normalize_weak(&r->y); secp256k1_fe_negate(&r->y, &r->y, 1); + + SECP256K1_GE_VERIFY(r); } static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { secp256k1_fe z2, z3; + SECP256K1_GEJ_VERIFY(a); + r->infinity = a->infinity; secp256k1_fe_inv(&a->z, &a->z); secp256k1_fe_sqr(&z2, &a->z); @@ -108,78 +167,99 @@ static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { secp256k1_fe_set_int(&a->z, 1); r->x = a->x; r->y = a->y; + + SECP256K1_GEJ_VERIFY(a); + SECP256K1_GE_VERIFY(r); } static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { secp256k1_fe z2, z3; - r->infinity = a->infinity; - if (a->infinity) { + SECP256K1_GEJ_VERIFY(a); + + if (secp256k1_gej_is_infinity(a)) { + secp256k1_ge_set_infinity(r); return; } + r->infinity = 0; secp256k1_fe_inv_var(&a->z, &a->z); secp256k1_fe_sqr(&z2, &a->z); secp256k1_fe_mul(&z3, &a->z, &z2); secp256k1_fe_mul(&a->x, &a->x, &z2); secp256k1_fe_mul(&a->y, &a->y, &z3); secp256k1_fe_set_int(&a->z, 1); - r->x = a->x; - r->y = a->y; + secp256k1_ge_set_xy(r, &a->x, &a->y); + + SECP256K1_GEJ_VERIFY(a); + SECP256K1_GE_VERIFY(r); } -static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) { - secp256k1_fe *az; - secp256k1_fe *azi; +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) { + secp256k1_fe u; size_t i; - size_t count = 0; - az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len); + size_t last_i = SIZE_MAX; +#ifdef VERIFY for (i = 0; i < len; i++) { - if (!a[i].infinity) { - az[count++] = a[i].z; + SECP256K1_GEJ_VERIFY(&a[i]); + } +#endif + + for (i = 0; i < len; i++) { + if (a[i].infinity) { + secp256k1_ge_set_infinity(&r[i]); + } else { + /* Use destination's x coordinates as scratch space */ + if (last_i == SIZE_MAX) { + r[i].x = a[i].z; + } else { + secp256k1_fe_mul(&r[i].x, &r[last_i].x, &a[i].z); + } + last_i = i; } } + if (last_i == SIZE_MAX) { + return; + } + secp256k1_fe_inv_var(&u, &r[last_i].x); - azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); - secp256k1_fe_inv_all_var(azi, az, count); - free(az); + i = last_i; + while (i > 0) { + i--; + if (!a[i].infinity) { + secp256k1_fe_mul(&r[last_i].x, &r[i].x, &u); + secp256k1_fe_mul(&u, &u, &a[last_i].z); + last_i = i; + } + } + VERIFY_CHECK(!a[last_i].infinity); + r[last_i].x = u; - count = 0; for (i = 0; i < len; i++) { - r[i].infinity = a[i].infinity; if (!a[i].infinity) { - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]); + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x); } } - free(azi); -} - -static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) { - size_t i = len - 1; - secp256k1_fe zi; - - if (len > 0) { - /* Compute the inverse of the last z coordinate, and use it to compute the last affine output. */ - secp256k1_fe_inv(&zi, &a[i].z); - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); - /* Work out way backwards, using the z-ratios to scale the x/y values. */ - while (i > 0) { - secp256k1_fe_mul(&zi, &zi, &zr[i]); - i--; - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); - } +#ifdef VERIFY + for (i = 0; i < len; i++) { + SECP256K1_GE_VERIFY(&r[i]); } +#endif } -static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr) { - size_t i = len - 1; +static void secp256k1_ge_table_set_globalz(size_t len, secp256k1_ge *a, const secp256k1_fe *zr) { + size_t i; secp256k1_fe zs; +#ifdef VERIFY + for (i = 0; i < len; i++) { + SECP256K1_GE_VERIFY(&a[i]); + SECP256K1_FE_VERIFY(&zr[i]); + } +#endif if (len > 0) { - /* The z of the final point gives us the "global Z" for the table. */ - r[i].x = a[i].x; - r[i].y = a[i].y; - *globalz = a[i].z; - r[i].infinity = 0; + i = len - 1; + /* Ensure all y values are in weak normal form for fast negation of points */ + secp256k1_fe_normalize_weak(&a[i].y); zs = zr[i]; /* Work our way backwards, using the z-ratios to scale the x/y values. */ @@ -188,9 +268,15 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp secp256k1_fe_mul(&zs, &zs, &zr[i]); } i--; - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs); + secp256k1_ge_set_ge_zinv(&a[i], &a[i], &zs); } } + +#ifdef VERIFY + for (i = 0; i < len; i++) { + SECP256K1_GE_VERIFY(&a[i]); + } +#endif } static void secp256k1_gej_set_infinity(secp256k1_gej *r) { @@ -198,6 +284,16 @@ static void secp256k1_gej_set_infinity(secp256k1_gej *r) { secp256k1_fe_clear(&r->x); secp256k1_fe_clear(&r->y); secp256k1_fe_clear(&r->z); + + SECP256K1_GEJ_VERIFY(r); +} + +static void secp256k1_ge_set_infinity(secp256k1_ge *r) { + r->infinity = 1; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + + SECP256K1_GE_VERIFY(r); } static void secp256k1_gej_clear(secp256k1_gej *r) { @@ -205,108 +301,169 @@ static void secp256k1_gej_clear(secp256k1_gej *r) { secp256k1_fe_clear(&r->x); secp256k1_fe_clear(&r->y); secp256k1_fe_clear(&r->z); + + SECP256K1_GEJ_VERIFY(r); } static void secp256k1_ge_clear(secp256k1_ge *r) { r->infinity = 0; secp256k1_fe_clear(&r->x); secp256k1_fe_clear(&r->y); + + SECP256K1_GE_VERIFY(r); } -static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { - secp256k1_fe x2, x3, c; +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { + secp256k1_fe x2, x3; + int ret; + SECP256K1_FE_VERIFY(x); + r->x = *x; secp256k1_fe_sqr(&x2, x); secp256k1_fe_mul(&x3, x, &x2); r->infinity = 0; - secp256k1_fe_set_int(&c, CURVE_B); - secp256k1_fe_add(&c, &x3); - return secp256k1_fe_sqrt(&r->y, &c); -} - -static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { - if (!secp256k1_ge_set_xquad(r, x)) { - return 0; - } + secp256k1_fe_add_int(&x3, SECP256K1_B); + ret = secp256k1_fe_sqrt(&r->y, &x3); secp256k1_fe_normalize_var(&r->y); if (secp256k1_fe_is_odd(&r->y) != odd) { secp256k1_fe_negate(&r->y, &r->y, 1); } - return 1; + SECP256K1_GE_VERIFY(r); + return ret; } static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { + SECP256K1_GE_VERIFY(a); + r->infinity = a->infinity; r->x = a->x; r->y = a->y; secp256k1_fe_set_int(&r->z, 1); + + SECP256K1_GEJ_VERIFY(r); +} + +static int secp256k1_gej_eq_var(const secp256k1_gej *a, const secp256k1_gej *b) { + secp256k1_gej tmp; + SECP256K1_GEJ_VERIFY(b); + SECP256K1_GEJ_VERIFY(a); + + secp256k1_gej_neg(&tmp, a); + secp256k1_gej_add_var(&tmp, &tmp, b, NULL); + return secp256k1_gej_is_infinity(&tmp); +} + +static int secp256k1_gej_eq_ge_var(const secp256k1_gej *a, const secp256k1_ge *b) { + secp256k1_gej tmp; + SECP256K1_GEJ_VERIFY(a); + SECP256K1_GE_VERIFY(b); + + secp256k1_gej_neg(&tmp, a); + secp256k1_gej_add_ge_var(&tmp, &tmp, b, NULL); + return secp256k1_gej_is_infinity(&tmp); +} + +static int secp256k1_ge_eq_var(const secp256k1_ge *a, const secp256k1_ge *b) { + secp256k1_fe tmp; + SECP256K1_GE_VERIFY(a); + SECP256K1_GE_VERIFY(b); + + if (a->infinity != b->infinity) return 0; + if (a->infinity) return 1; + + tmp = a->x; + secp256k1_fe_normalize_weak(&tmp); + if (!secp256k1_fe_equal(&tmp, &b->x)) return 0; + + tmp = a->y; + secp256k1_fe_normalize_weak(&tmp); + if (!secp256k1_fe_equal(&tmp, &b->y)) return 0; + + return 1; } static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a) { - secp256k1_fe r, r2; + secp256k1_fe r; + SECP256K1_FE_VERIFY(x); + SECP256K1_GEJ_VERIFY(a); VERIFY_CHECK(!a->infinity); + secp256k1_fe_sqr(&r, &a->z); secp256k1_fe_mul(&r, &r, x); - r2 = a->x; secp256k1_fe_normalize_weak(&r2); - return secp256k1_fe_equal_var(&r, &r2); + return secp256k1_fe_equal(&r, &a->x); } static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a) { + SECP256K1_GEJ_VERIFY(a); + r->infinity = a->infinity; r->x = a->x; r->y = a->y; r->z = a->z; secp256k1_fe_normalize_weak(&r->y); secp256k1_fe_negate(&r->y, &r->y, 1); + + SECP256K1_GEJ_VERIFY(r); } static int secp256k1_gej_is_infinity(const secp256k1_gej *a) { - return a->infinity; -} + SECP256K1_GEJ_VERIFY(a); -static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) { - secp256k1_fe y2, x3, z2, z6; - if (a->infinity) { - return 0; - } - /** y^2 = x^3 + 7 - * (Y/Z^3)^2 = (X/Z^2)^3 + 7 - * Y^2 / Z^6 = X^3 / Z^6 + 7 - * Y^2 = X^3 + 7*Z^6 - */ - secp256k1_fe_sqr(&y2, &a->y); - secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); - secp256k1_fe_sqr(&z2, &a->z); - secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2); - secp256k1_fe_mul_int(&z6, CURVE_B); - secp256k1_fe_add(&x3, &z6); - secp256k1_fe_normalize_weak(&x3); - return secp256k1_fe_equal_var(&y2, &x3); + return a->infinity; } static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { - secp256k1_fe y2, x3, c; + secp256k1_fe y2, x3; + SECP256K1_GE_VERIFY(a); + if (a->infinity) { return 0; } /* y^2 = x^3 + 7 */ secp256k1_fe_sqr(&y2, &a->y); secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); - secp256k1_fe_set_int(&c, CURVE_B); - secp256k1_fe_add(&x3, &c); - secp256k1_fe_normalize_weak(&x3); - return secp256k1_fe_equal_var(&y2, &x3); + secp256k1_fe_add_int(&x3, SECP256K1_B); + return secp256k1_fe_equal(&y2, &x3); } -static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { - /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate. - * - * Note that there is an implementation described at - * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - * which trades a multiply for a square, but in practice this is actually slower, - * mainly because it requires more normalizations. +static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a) { + /* Operations: 3 mul, 4 sqr, 8 add/half/mul_int/negate */ + secp256k1_fe l, s, t; + SECP256K1_GEJ_VERIFY(a); + + r->infinity = a->infinity; + + /* Formula used: + * L = (3/2) * X1^2 + * S = Y1^2 + * T = -X1*S + * X3 = L^2 + 2*T + * Y3 = -(L*(X3 + T) + S^2) + * Z3 = Y1*Z1 */ - secp256k1_fe t1,t2,t3,t4; + + secp256k1_fe_mul(&r->z, &a->z, &a->y); /* Z3 = Y1*Z1 (1) */ + secp256k1_fe_sqr(&s, &a->y); /* S = Y1^2 (1) */ + secp256k1_fe_sqr(&l, &a->x); /* L = X1^2 (1) */ + secp256k1_fe_mul_int(&l, 3); /* L = 3*X1^2 (3) */ + secp256k1_fe_half(&l); /* L = 3/2*X1^2 (2) */ + secp256k1_fe_negate(&t, &s, 1); /* T = -S (2) */ + secp256k1_fe_mul(&t, &t, &a->x); /* T = -X1*S (1) */ + secp256k1_fe_sqr(&r->x, &l); /* X3 = L^2 (1) */ + secp256k1_fe_add(&r->x, &t); /* X3 = L^2 + T (2) */ + secp256k1_fe_add(&r->x, &t); /* X3 = L^2 + 2*T (3) */ + secp256k1_fe_sqr(&s, &s); /* S' = S^2 (1) */ + secp256k1_fe_add(&t, &r->x); /* T' = X3 + T (4) */ + secp256k1_fe_mul(&r->y, &t, &l); /* Y3 = L*(X3 + T) (1) */ + secp256k1_fe_add(&r->y, &s); /* Y3 = L*(X3 + T) + S^2 (2) */ + secp256k1_fe_negate(&r->y, &r->y, 2); /* Y3 = -(L*(X3 + T) + S^2) (3) */ + + SECP256K1_GEJ_VERIFY(r); +} + +static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { + SECP256K1_GEJ_VERIFY(a); + /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity, * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have * y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p. @@ -317,8 +474,8 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s * the infinity flag even though the point doubles to infinity, and the result * point will be gibberish (z = 0 but infinity = 0). */ - r->infinity = a->infinity; - if (r->infinity) { + if (a->infinity) { + secp256k1_gej_set_infinity(r); if (rzr != NULL) { secp256k1_fe_set_int(rzr, 1); } @@ -328,46 +485,24 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s if (rzr != NULL) { *rzr = a->y; secp256k1_fe_normalize_weak(rzr); - secp256k1_fe_mul_int(rzr, 2); } - secp256k1_fe_mul(&r->z, &a->z, &a->y); - secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */ - secp256k1_fe_sqr(&t1, &a->x); - secp256k1_fe_mul_int(&t1, 3); /* T1 = 3*X^2 (3) */ - secp256k1_fe_sqr(&t2, &t1); /* T2 = 9*X^4 (1) */ - secp256k1_fe_sqr(&t3, &a->y); - secp256k1_fe_mul_int(&t3, 2); /* T3 = 2*Y^2 (2) */ - secp256k1_fe_sqr(&t4, &t3); - secp256k1_fe_mul_int(&t4, 2); /* T4 = 8*Y^4 (2) */ - secp256k1_fe_mul(&t3, &t3, &a->x); /* T3 = 2*X*Y^2 (1) */ - r->x = t3; - secp256k1_fe_mul_int(&r->x, 4); /* X' = 8*X*Y^2 (4) */ - secp256k1_fe_negate(&r->x, &r->x, 4); /* X' = -8*X*Y^2 (5) */ - secp256k1_fe_add(&r->x, &t2); /* X' = 9*X^4 - 8*X*Y^2 (6) */ - secp256k1_fe_negate(&t2, &t2, 1); /* T2 = -9*X^4 (2) */ - secp256k1_fe_mul_int(&t3, 6); /* T3 = 12*X*Y^2 (6) */ - secp256k1_fe_add(&t3, &t2); /* T3 = 12*X*Y^2 - 9*X^4 (8) */ - secp256k1_fe_mul(&r->y, &t1, &t3); /* Y' = 36*X^3*Y^2 - 27*X^6 (1) */ - secp256k1_fe_negate(&t2, &t4, 2); /* T2 = -8*Y^4 (3) */ - secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ -} - -static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { - VERIFY_CHECK(!secp256k1_gej_is_infinity(a)); - secp256k1_gej_double_var(r, a, rzr); + secp256k1_gej_double(r, a); + + SECP256K1_GEJ_VERIFY(r); } static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) { - /* Operations: 12 mul, 4 sqr, 2 normalize, 12 mul_int/add/negate */ - secp256k1_fe z22, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + /* 12 mul, 4 sqr, 11 add/negate/normalizes_to_zero (ignoring special cases) */ + secp256k1_fe z22, z12, u1, u2, s1, s2, h, i, h2, h3, t; + SECP256K1_GEJ_VERIFY(a); + SECP256K1_GEJ_VERIFY(b); if (a->infinity) { VERIFY_CHECK(rzr == NULL); *r = *b; return; } - if (b->infinity) { if (rzr != NULL) { secp256k1_fe_set_int(rzr, 1); @@ -376,7 +511,6 @@ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, cons return; } - r->infinity = 0; secp256k1_fe_sqr(&z22, &b->z); secp256k1_fe_sqr(&z12, &a->z); secp256k1_fe_mul(&u1, &a->x, &z22); @@ -384,7 +518,7 @@ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, cons secp256k1_fe_mul(&s1, &a->y, &z22); secp256k1_fe_mul(&s1, &s1, &b->z); secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); - secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + secp256k1_fe_negate(&i, &s2, 1); secp256k1_fe_add(&i, &s1); if (secp256k1_fe_normalizes_to_zero_var(&h)) { if (secp256k1_fe_normalizes_to_zero_var(&i)) { secp256k1_gej_double_var(r, a, rzr); @@ -392,28 +526,42 @@ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, cons if (rzr != NULL) { secp256k1_fe_set_int(rzr, 0); } - r->infinity = 1; + secp256k1_gej_set_infinity(r); } return; } - secp256k1_fe_sqr(&i2, &i); - secp256k1_fe_sqr(&h2, &h); - secp256k1_fe_mul(&h3, &h, &h2); - secp256k1_fe_mul(&h, &h, &b->z); + + r->infinity = 0; + secp256k1_fe_mul(&t, &h, &b->z); if (rzr != NULL) { - *rzr = h; + *rzr = t; } - secp256k1_fe_mul(&r->z, &a->z, &h); + secp256k1_fe_mul(&r->z, &a->z, &t); + + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_negate(&h2, &h2, 1); + secp256k1_fe_mul(&h3, &h2, &h); secp256k1_fe_mul(&t, &u1, &h2); - r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); - secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); - secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + + secp256k1_fe_sqr(&r->x, &i); + secp256k1_fe_add(&r->x, &h3); + secp256k1_fe_add(&r->x, &t); + secp256k1_fe_add(&r->x, &t); + + secp256k1_fe_add(&t, &r->x); + secp256k1_fe_mul(&r->y, &t, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_add(&r->y, &h3); + + SECP256K1_GEJ_VERIFY(r); } static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr) { - /* 8 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ - secp256k1_fe z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + /* Operations: 8 mul, 3 sqr, 11 add/negate/normalizes_to_zero (ignoring special cases) */ + secp256k1_fe z12, u1, u2, s1, s2, h, i, h2, h3, t; + SECP256K1_GEJ_VERIFY(a); + SECP256K1_GE_VERIFY(b); + if (a->infinity) { VERIFY_CHECK(rzr == NULL); secp256k1_gej_set_ge(r, b); @@ -426,15 +574,14 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, c *r = *a; return; } - r->infinity = 0; secp256k1_fe_sqr(&z12, &a->z); - u1 = a->x; secp256k1_fe_normalize_weak(&u1); + u1 = a->x; secp256k1_fe_mul(&u2, &b->x, &z12); - s1 = a->y; secp256k1_fe_normalize_weak(&s1); + s1 = a->y; secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); - secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); - secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + secp256k1_fe_negate(&h, &u1, SECP256K1_GEJ_X_MAGNITUDE_MAX); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s2, 1); secp256k1_fe_add(&i, &s1); if (secp256k1_fe_normalizes_to_zero_var(&h)) { if (secp256k1_fe_normalizes_to_zero_var(&i)) { secp256k1_gej_double_var(r, a, rzr); @@ -442,32 +589,43 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, c if (rzr != NULL) { secp256k1_fe_set_int(rzr, 0); } - r->infinity = 1; + secp256k1_gej_set_infinity(r); } return; } - secp256k1_fe_sqr(&i2, &i); - secp256k1_fe_sqr(&h2, &h); - secp256k1_fe_mul(&h3, &h, &h2); + + r->infinity = 0; if (rzr != NULL) { *rzr = h; } secp256k1_fe_mul(&r->z, &a->z, &h); + + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_negate(&h2, &h2, 1); + secp256k1_fe_mul(&h3, &h2, &h); secp256k1_fe_mul(&t, &u1, &h2); - r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); - secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); - secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + + secp256k1_fe_sqr(&r->x, &i); + secp256k1_fe_add(&r->x, &h3); + secp256k1_fe_add(&r->x, &t); + secp256k1_fe_add(&r->x, &t); + + secp256k1_fe_add(&t, &r->x); + secp256k1_fe_mul(&r->y, &t, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_add(&r->y, &h3); + + SECP256K1_GEJ_VERIFY(r); + if (rzr != NULL) SECP256K1_FE_VERIFY(rzr); } static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv) { - /* 9 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ - secp256k1_fe az, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + /* Operations: 9 mul, 3 sqr, 11 add/negate/normalizes_to_zero (ignoring special cases) */ + secp256k1_fe az, z12, u1, u2, s1, s2, h, i, h2, h3, t; + SECP256K1_GEJ_VERIFY(a); + SECP256K1_GE_VERIFY(b); + SECP256K1_FE_VERIFY(bzinv); - if (b->infinity) { - *r = *a; - return; - } if (a->infinity) { secp256k1_fe bzinv2, bzinv3; r->infinity = b->infinity; @@ -476,9 +634,13 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe_mul(&r->x, &b->x, &bzinv2); secp256k1_fe_mul(&r->y, &b->y, &bzinv3); secp256k1_fe_set_int(&r->z, 1); + SECP256K1_GEJ_VERIFY(r); + return; + } + if (b->infinity) { + *r = *a; return; } - r->infinity = 0; /** We need to calculate (rx,ry,rz) = (ax,ay,az) + (bx,by,1/bzinv). Due to * secp256k1's isomorphism we can multiply the Z coordinates on both sides @@ -491,42 +653,53 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe_mul(&az, &a->z, bzinv); secp256k1_fe_sqr(&z12, &az); - u1 = a->x; secp256k1_fe_normalize_weak(&u1); + u1 = a->x; secp256k1_fe_mul(&u2, &b->x, &z12); - s1 = a->y; secp256k1_fe_normalize_weak(&s1); + s1 = a->y; secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &az); - secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); - secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + secp256k1_fe_negate(&h, &u1, SECP256K1_GEJ_X_MAGNITUDE_MAX); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s2, 1); secp256k1_fe_add(&i, &s1); if (secp256k1_fe_normalizes_to_zero_var(&h)) { if (secp256k1_fe_normalizes_to_zero_var(&i)) { secp256k1_gej_double_var(r, a, NULL); } else { - r->infinity = 1; + secp256k1_gej_set_infinity(r); } return; } - secp256k1_fe_sqr(&i2, &i); + + r->infinity = 0; + secp256k1_fe_mul(&r->z, &a->z, &h); + secp256k1_fe_sqr(&h2, &h); - secp256k1_fe_mul(&h3, &h, &h2); - r->z = a->z; secp256k1_fe_mul(&r->z, &r->z, &h); + secp256k1_fe_negate(&h2, &h2, 1); + secp256k1_fe_mul(&h3, &h2, &h); secp256k1_fe_mul(&t, &u1, &h2); - r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); - secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); - secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + + secp256k1_fe_sqr(&r->x, &i); + secp256k1_fe_add(&r->x, &h3); + secp256k1_fe_add(&r->x, &t); + secp256k1_fe_add(&r->x, &t); + + secp256k1_fe_add(&t, &r->x); + secp256k1_fe_mul(&r->y, &t, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_add(&r->y, &h3); + + SECP256K1_GEJ_VERIFY(r); } static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b) { - /* Operations: 7 mul, 5 sqr, 4 normalize, 21 mul_int/add/negate/cmov */ - static const secp256k1_fe fe_1 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + /* Operations: 7 mul, 5 sqr, 21 add/cmov/half/mul_int/negate/normalizes_to_zero */ secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; secp256k1_fe m_alt, rr_alt; - int infinity, degenerate; + int degenerate; + SECP256K1_GEJ_VERIFY(a); + SECP256K1_GE_VERIFY(b); VERIFY_CHECK(!b->infinity); - VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); - /** In: + /* In: * Eric Brier and Marc Joye, Weierstrass Elliptic Curves and Side-Channel Attacks. * In D. Naccache and P. Paillier, Eds., Public Key Cryptography, vol. 2274 of Lecture Notes in Computer Science, pages 335-345. Springer-Verlag, 2002. * we find as solution for a unified addition/doubling formula: @@ -540,11 +713,11 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const * Z = Z1*Z2 * T = U1+U2 * M = S1+S2 - * Q = T*M^2 + * Q = -T*M^2 * R = T^2-U1*U2 - * X3 = 4*(R^2-Q) - * Y3 = 4*(R*(3*Q-2*R^2)-M^4) - * Z3 = 2*M*Z + * X3 = R^2+Q + * Y3 = -(R*(2*X3+Q)+M^4)/2 + * Z3 = M*Z * (Note that the paper uses xi = Xi / Zi and yi = Yi / Zi instead.) * * This formula has the benefit of being the same for both addition @@ -577,82 +750,104 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const */ secp256k1_fe_sqr(&zz, &a->z); /* z = Z1^2 */ - u1 = a->x; secp256k1_fe_normalize_weak(&u1); /* u1 = U1 = X1*Z2^2 (1) */ + u1 = a->x; /* u1 = U1 = X1*Z2^2 (GEJ_X_M) */ secp256k1_fe_mul(&u2, &b->x, &zz); /* u2 = U2 = X2*Z1^2 (1) */ - s1 = a->y; secp256k1_fe_normalize_weak(&s1); /* s1 = S1 = Y1*Z2^3 (1) */ + s1 = a->y; /* s1 = S1 = Y1*Z2^3 (GEJ_Y_M) */ secp256k1_fe_mul(&s2, &b->y, &zz); /* s2 = Y2*Z1^2 (1) */ secp256k1_fe_mul(&s2, &s2, &a->z); /* s2 = S2 = Y2*Z1^3 (1) */ - t = u1; secp256k1_fe_add(&t, &u2); /* t = T = U1+U2 (2) */ - m = s1; secp256k1_fe_add(&m, &s2); /* m = M = S1+S2 (2) */ + t = u1; secp256k1_fe_add(&t, &u2); /* t = T = U1+U2 (GEJ_X_M+1) */ + m = s1; secp256k1_fe_add(&m, &s2); /* m = M = S1+S2 (GEJ_Y_M+1) */ secp256k1_fe_sqr(&rr, &t); /* rr = T^2 (1) */ - secp256k1_fe_negate(&m_alt, &u2, 1); /* Malt = -X2*Z1^2 */ - secp256k1_fe_mul(&tt, &u1, &m_alt); /* tt = -U1*U2 (2) */ - secp256k1_fe_add(&rr, &tt); /* rr = R = T^2-U1*U2 (3) */ - /** If lambda = R/M = 0/0 we have a problem (except in the "trivial" - * case that Z = z1z2 = 0, and this is special-cased later on). */ - degenerate = secp256k1_fe_normalizes_to_zero(&m) & - secp256k1_fe_normalizes_to_zero(&rr); + secp256k1_fe_negate(&m_alt, &u2, 1); /* Malt = -X2*Z1^2 (2) */ + secp256k1_fe_mul(&tt, &u1, &m_alt); /* tt = -U1*U2 (1) */ + secp256k1_fe_add(&rr, &tt); /* rr = R = T^2-U1*U2 (2) */ + /* If lambda = R/M = R/0 we have a problem (except in the "trivial" + * case that Z = z1z2 = 0, and this is special-cased later on). */ + degenerate = secp256k1_fe_normalizes_to_zero(&m); /* This only occurs when y1 == -y2 and x1^3 == x2^3, but x1 != x2. * This means either x1 == beta*x2 or beta*x1 == x2, where beta is * a nontrivial cube root of one. In either case, an alternate * non-indeterminate expression for lambda is (y1 - y2)/(x1 - x2), * so we set R/M equal to this. */ rr_alt = s1; - secp256k1_fe_mul_int(&rr_alt, 2); /* rr = Y1*Z2^3 - Y2*Z1^3 (2) */ - secp256k1_fe_add(&m_alt, &u1); /* Malt = X1*Z2^2 - X2*Z1^2 */ + secp256k1_fe_mul_int(&rr_alt, 2); /* rr_alt = Y1*Z2^3 - Y2*Z1^3 (GEJ_Y_M*2) */ + secp256k1_fe_add(&m_alt, &u1); /* Malt = X1*Z2^2 - X2*Z1^2 (GEJ_X_M+2) */ - secp256k1_fe_cmov(&rr_alt, &rr, !degenerate); - secp256k1_fe_cmov(&m_alt, &m, !degenerate); - /* Now Ralt / Malt = lambda and is guaranteed not to be 0/0. + secp256k1_fe_cmov(&rr_alt, &rr, !degenerate); /* rr_alt (GEJ_Y_M*2) */ + secp256k1_fe_cmov(&m_alt, &m, !degenerate); /* m_alt (GEJ_X_M+2) */ + /* Now Ralt / Malt = lambda and is guaranteed not to be Ralt / 0. * From here on out Ralt and Malt represent the numerator * and denominator of lambda; R and M represent the explicit * expressions x1^2 + x2^2 + x1x2 and y1 + y2. */ secp256k1_fe_sqr(&n, &m_alt); /* n = Malt^2 (1) */ - secp256k1_fe_mul(&q, &n, &t); /* q = Q = T*Malt^2 (1) */ + secp256k1_fe_negate(&q, &t, + SECP256K1_GEJ_X_MAGNITUDE_MAX + 1); /* q = -T (GEJ_X_M+2) */ + secp256k1_fe_mul(&q, &q, &n); /* q = Q = -T*Malt^2 (1) */ /* These two lines use the observation that either M == Malt or M == 0, * so M^3 * Malt is either Malt^4 (which is computed by squaring), or * zero (which is "computed" by cmov). So the cost is one squaring * versus two multiplications. */ - secp256k1_fe_sqr(&n, &n); - secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (2) */ + secp256k1_fe_sqr(&n, &n); /* n = Malt^4 (1) */ + secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (GEJ_Y_M+1) */ secp256k1_fe_sqr(&t, &rr_alt); /* t = Ralt^2 (1) */ - secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Malt*Z (1) */ - infinity = secp256k1_fe_normalizes_to_zero(&r->z) * (1 - a->infinity); - secp256k1_fe_mul_int(&r->z, 2); /* r->z = Z3 = 2*Malt*Z (2) */ - secp256k1_fe_negate(&q, &q, 1); /* q = -Q (2) */ - secp256k1_fe_add(&t, &q); /* t = Ralt^2-Q (3) */ - secp256k1_fe_normalize_weak(&t); - r->x = t; /* r->x = Ralt^2-Q (1) */ - secp256k1_fe_mul_int(&t, 2); /* t = 2*x3 (2) */ - secp256k1_fe_add(&t, &q); /* t = 2*x3 - Q: (4) */ - secp256k1_fe_mul(&t, &t, &rr_alt); /* t = Ralt*(2*x3 - Q) (1) */ - secp256k1_fe_add(&t, &n); /* t = Ralt*(2*x3 - Q) + M^3*Malt (3) */ - secp256k1_fe_negate(&r->y, &t, 3); /* r->y = Ralt*(Q - 2x3) - M^3*Malt (4) */ - secp256k1_fe_normalize_weak(&r->y); - secp256k1_fe_mul_int(&r->x, 4); /* r->x = X3 = 4*(Ralt^2-Q) */ - secp256k1_fe_mul_int(&r->y, 4); /* r->y = Y3 = 4*Ralt*(Q - 2x3) - 4*M^3*Malt (4) */ - - /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */ + secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Z3 = Malt*Z (1) */ + secp256k1_fe_add(&t, &q); /* t = Ralt^2 + Q (2) */ + r->x = t; /* r->x = X3 = Ralt^2 + Q (2) */ + secp256k1_fe_mul_int(&t, 2); /* t = 2*X3 (4) */ + secp256k1_fe_add(&t, &q); /* t = 2*X3 + Q (5) */ + secp256k1_fe_mul(&t, &t, &rr_alt); /* t = Ralt*(2*X3 + Q) (1) */ + secp256k1_fe_add(&t, &n); /* t = Ralt*(2*X3 + Q) + M^3*Malt (GEJ_Y_M+2) */ + secp256k1_fe_negate(&r->y, &t, + SECP256K1_GEJ_Y_MAGNITUDE_MAX + 2); /* r->y = -(Ralt*(2*X3 + Q) + M^3*Malt) (GEJ_Y_M+3) */ + secp256k1_fe_half(&r->y); /* r->y = Y3 = -(Ralt*(2*X3 + Q) + M^3*Malt)/2 ((GEJ_Y_M+3)/2 + 1) */ + + /* In case a->infinity == 1, replace r with (b->x, b->y, 1). */ secp256k1_fe_cmov(&r->x, &b->x, a->infinity); secp256k1_fe_cmov(&r->y, &b->y, a->infinity); - secp256k1_fe_cmov(&r->z, &fe_1, a->infinity); - r->infinity = infinity; + secp256k1_fe_cmov(&r->z, &secp256k1_fe_one, a->infinity); + + /* Set r->infinity if r->z is 0. + * + * If a->infinity is set, then r->infinity = (r->z == 0) = (1 == 0) = false, + * which is correct because the function assumes that b is not infinity. + * + * Now assume !a->infinity. This implies Z = Z1 != 0. + * + * Case y1 = -y2: + * In this case we could have a = -b, namely if x1 = x2. + * We have degenerate = true, r->z = (x1 - x2) * Z. + * Then r->infinity = ((x1 - x2)Z == 0) = (x1 == x2) = (a == -b). + * + * Case y1 != -y2: + * In this case, we can't have a = -b. + * We have degenerate = false, r->z = (y1 + y2) * Z. + * Then r->infinity = ((y1 + y2)Z == 0) = (y1 == -y2) = false. */ + r->infinity = secp256k1_fe_normalizes_to_zero(&r->z); + + SECP256K1_GEJ_VERIFY(r); } static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *s) { /* Operations: 4 mul, 1 sqr */ secp256k1_fe zz; - VERIFY_CHECK(!secp256k1_fe_is_zero(s)); + SECP256K1_GEJ_VERIFY(r); + SECP256K1_FE_VERIFY(s); + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(s)); + secp256k1_fe_sqr(&zz, s); secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */ secp256k1_fe_mul(&r->y, &r->y, &zz); secp256k1_fe_mul(&r->y, &r->y, s); /* r->y *= s^3 */ secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */ + + SECP256K1_GEJ_VERIFY(r); } static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a) { secp256k1_fe x, y; + SECP256K1_GE_VERIFY(a); VERIFY_CHECK(!a->infinity); + x = a->x; secp256k1_fe_normalize(&x); y = a->y; @@ -665,6 +860,20 @@ static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storag secp256k1_fe_from_storage(&r->x, &a->x); secp256k1_fe_from_storage(&r->y, &a->y); r->infinity = 0; + + SECP256K1_GE_VERIFY(r); +} + +static SECP256K1_INLINE void secp256k1_gej_cmov(secp256k1_gej *r, const secp256k1_gej *a, int flag) { + SECP256K1_GEJ_VERIFY(r); + SECP256K1_GEJ_VERIFY(a); + + secp256k1_fe_cmov(&r->x, &a->x, flag); + secp256k1_fe_cmov(&r->y, &a->y, flag); + secp256k1_fe_cmov(&r->z, &a->z, flag); + r->infinity ^= (r->infinity ^ a->infinity) & flag; + + SECP256K1_GEJ_VERIFY(r); } static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) { @@ -672,29 +881,64 @@ static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, secp256k1_fe_storage_cmov(&r->y, &a->y, flag); } -#ifdef USE_ENDOMORPHISM static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { - static const secp256k1_fe beta = SECP256K1_FE_CONST( - 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, - 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul - ); + SECP256K1_GE_VERIFY(a); + *r = *a; - secp256k1_fe_mul(&r->x, &r->x, &beta); + secp256k1_fe_mul(&r->x, &r->x, &secp256k1_const_beta); + + SECP256K1_GE_VERIFY(r); } -#endif -static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { - secp256k1_fe yz; +static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) { +#ifdef EXHAUSTIVE_TEST_ORDER + secp256k1_gej out; + int i; + SECP256K1_GE_VERIFY(ge); - if (a->infinity) { - return 0; + /* A very simple EC multiplication ladder that avoids a dependency on ecmult. */ + secp256k1_gej_set_infinity(&out); + for (i = 0; i < 32; ++i) { + secp256k1_gej_double_var(&out, &out, NULL); + if ((((uint32_t)EXHAUSTIVE_TEST_ORDER) >> (31 - i)) & 1) { + secp256k1_gej_add_ge_var(&out, &out, ge, NULL); + } } + return secp256k1_gej_is_infinity(&out); +#else + SECP256K1_GE_VERIFY(ge); - /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as - * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z - is */ - secp256k1_fe_mul(&yz, &a->y, &a->z); - return secp256k1_fe_is_quad_var(&yz); + (void)ge; + /* The real secp256k1 group has cofactor 1, so the subgroup is the entire curve. */ + return 1; +#endif +} + +static int secp256k1_ge_x_on_curve_var(const secp256k1_fe *x) { + secp256k1_fe c; + secp256k1_fe_sqr(&c, x); + secp256k1_fe_mul(&c, &c, x); + secp256k1_fe_add_int(&c, SECP256K1_B); + return secp256k1_fe_is_square_var(&c); +} + +static int secp256k1_ge_x_frac_on_curve_var(const secp256k1_fe *xn, const secp256k1_fe *xd) { + /* We want to determine whether (xn/xd) is on the curve. + * + * (xn/xd)^3 + 7 is square <=> xd*xn^3 + 7*xd^4 is square (multiplying by xd^4, a square). + */ + secp256k1_fe r, t; + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(xd)); + + secp256k1_fe_mul(&r, xd, xn); /* r = xd*xn */ + secp256k1_fe_sqr(&t, xn); /* t = xn^2 */ + secp256k1_fe_mul(&r, &r, &t); /* r = xd*xn^3 */ + secp256k1_fe_sqr(&t, xd); /* t = xd^2 */ + secp256k1_fe_sqr(&t, &t); /* t = xd^4 */ + VERIFY_CHECK(SECP256K1_B <= 31); + secp256k1_fe_mul_int(&t, SECP256K1_B); /* t = 7*xd^4 */ + secp256k1_fe_add(&r, &t); /* r = xd*xn^3 + 7*xd^4 */ + return secp256k1_fe_is_square_var(&r); } #endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h index 7aacc85bf..4e0384cfb 100644 --- a/src/secp256k1/src/hash.h +++ b/src/secp256k1/src/hash.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_HASH_H #define SECP256K1_HASH_H @@ -12,8 +12,8 @@ typedef struct { uint32_t s[8]; - uint32_t buf[16]; /* In big endian */ - size_t bytes; + unsigned char buf[64]; + uint64_t bytes; } secp256k1_sha256; static void secp256k1_sha256_initialize(secp256k1_sha256 *hash); diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h index 2f3f7922b..89f75ace7 100644 --- a/src/secp256k1/src/hash_impl.h +++ b/src/secp256k1/src/hash_impl.h @@ -1,13 +1,14 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_HASH_IMPL_H #define SECP256K1_HASH_IMPL_H #include "hash.h" +#include "util.h" #include #include @@ -27,12 +28,6 @@ (h) = t1 + t2; \ } while(0) -#ifdef WORDS_BIGENDIAN -#define BE32(x) (x) -#else -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#endif - static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) { hash->s[0] = 0x6a09e667ul; hash->s[1] = 0xbb67ae85ul; @@ -46,26 +41,26 @@ static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) { } /** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */ -static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { +static void secp256k1_sha256_transform(uint32_t* s, const unsigned char* buf) { uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; - Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = BE32(chunk[0])); - Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = BE32(chunk[1])); - Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = BE32(chunk[2])); - Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = BE32(chunk[3])); - Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = BE32(chunk[4])); - Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = BE32(chunk[5])); - Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = BE32(chunk[6])); - Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = BE32(chunk[7])); - Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = BE32(chunk[8])); - Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = BE32(chunk[9])); - Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = BE32(chunk[10])); - Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = BE32(chunk[11])); - Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = BE32(chunk[12])); - Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = BE32(chunk[13])); - Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = BE32(chunk[14])); - Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = BE32(chunk[15])); + Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = secp256k1_read_be32(&buf[0])); + Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = secp256k1_read_be32(&buf[4])); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = secp256k1_read_be32(&buf[8])); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = secp256k1_read_be32(&buf[12])); + Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = secp256k1_read_be32(&buf[16])); + Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = secp256k1_read_be32(&buf[20])); + Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = secp256k1_read_be32(&buf[24])); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = secp256k1_read_be32(&buf[28])); + Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = secp256k1_read_be32(&buf[32])); + Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = secp256k1_read_be32(&buf[36])); + Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = secp256k1_read_be32(&buf[40])); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = secp256k1_read_be32(&buf[44])); + Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = secp256k1_read_be32(&buf[48])); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = secp256k1_read_be32(&buf[52])); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = secp256k1_read_be32(&buf[56])); + Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = secp256k1_read_be32(&buf[60])); Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); @@ -131,42 +126,57 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) { size_t bufsize = hash->bytes & 0x3F; hash->bytes += len; - while (bufsize + len >= 64) { + VERIFY_CHECK(hash->bytes >= len); + while (len >= 64 - bufsize) { /* Fill the buffer, and process it. */ - memcpy(((unsigned char*)hash->buf) + bufsize, data, 64 - bufsize); - data += 64 - bufsize; - len -= 64 - bufsize; + size_t chunk_len = 64 - bufsize; + memcpy(hash->buf + bufsize, data, chunk_len); + data += chunk_len; + len -= chunk_len; secp256k1_sha256_transform(hash->s, hash->buf); bufsize = 0; } if (len) { /* Fill the buffer with what remains. */ - memcpy(((unsigned char*)hash->buf) + bufsize, data, len); + memcpy(hash->buf + bufsize, data, len); } } static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32) { - static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint32_t sizedesc[2]; - uint32_t out[8]; - int i = 0; - sizedesc[0] = BE32(hash->bytes >> 29); - sizedesc[1] = BE32(hash->bytes << 3); + static const unsigned char pad[64] = {0x80}; + unsigned char sizedesc[8]; + int i; + /* The maximum message size of SHA256 is 2^64-1 bits. */ + VERIFY_CHECK(hash->bytes < ((uint64_t)1 << 61)); + secp256k1_write_be32(&sizedesc[0], hash->bytes >> 29); + secp256k1_write_be32(&sizedesc[4], hash->bytes << 3); secp256k1_sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64)); - secp256k1_sha256_write(hash, (const unsigned char*)sizedesc, 8); + secp256k1_sha256_write(hash, sizedesc, 8); for (i = 0; i < 8; i++) { - out[i] = BE32(hash->s[i]); + secp256k1_write_be32(&out32[4*i], hash->s[i]); hash->s[i] = 0; } - memcpy(out32, (const unsigned char*)out, 32); +} + +/* Initializes a sha256 struct and writes the 64 byte string + * SHA256(tag)||SHA256(tag) into it. */ +static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) { + unsigned char buf[32]; + secp256k1_sha256_initialize(hash); + secp256k1_sha256_write(hash, tag, taglen); + secp256k1_sha256_finalize(hash, buf); + + secp256k1_sha256_initialize(hash); + secp256k1_sha256_write(hash, buf, 32); + secp256k1_sha256_write(hash, buf, 32); } static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) { - int n; + size_t n; unsigned char rkey[64]; - if (keylen <= 64) { + if (keylen <= sizeof(rkey)) { memcpy(rkey, key, keylen); - memset(rkey + keylen, 0, 64 - keylen); + memset(rkey + keylen, 0, sizeof(rkey) - keylen); } else { secp256k1_sha256 sha256; secp256k1_sha256_initialize(&sha256); @@ -176,17 +186,17 @@ static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const } secp256k1_sha256_initialize(&hash->outer); - for (n = 0; n < 64; n++) { + for (n = 0; n < sizeof(rkey); n++) { rkey[n] ^= 0x5c; } - secp256k1_sha256_write(&hash->outer, rkey, 64); + secp256k1_sha256_write(&hash->outer, rkey, sizeof(rkey)); secp256k1_sha256_initialize(&hash->inner); - for (n = 0; n < 64; n++) { + for (n = 0; n < sizeof(rkey); n++) { rkey[n] ^= 0x5c ^ 0x36; } - secp256k1_sha256_write(&hash->inner, rkey, 64); - memset(rkey, 0, 64); + secp256k1_sha256_write(&hash->inner, rkey, sizeof(rkey)); + memset(rkey, 0, sizeof(rkey)); } static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) { @@ -269,7 +279,6 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 rng->retry = 0; } -#undef BE32 #undef Round #undef sigma1 #undef sigma0 diff --git a/src/secp256k1/src/hsort.h b/src/secp256k1/src/hsort.h new file mode 100644 index 000000000..d54995caa --- /dev/null +++ b/src/secp256k1/src/hsort.h @@ -0,0 +1,33 @@ +/*********************************************************************** + * Copyright (c) 2021 Russell O'Connor, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_HSORT_H +#define SECP256K1_HSORT_H + +#include +#include + +/* In-place, iterative heapsort with an interface matching glibc's qsort_r. This + * is preferred over standard library implementations because they generally + * make no guarantee about being fast for malicious inputs. + * Remember that heapsort is unstable. + * + * In/Out: ptr: pointer to the array to sort. The contents of the array are + * sorted in ascending order according to the comparison function. + * In: count: number of elements in the array. + * size: size in bytes of each element. + * cmp: pointer to a comparison function that is called with two + * arguments that point to the objects being compared. The cmp_data + * argument of secp256k1_hsort is passed as third argument. The + * function must return an integer less than, equal to, or greater + * than zero if the first argument is considered to be respectively + * less than, equal to, or greater than the second. + * cmp_data: pointer passed as third argument to cmp. + */ +static void secp256k1_hsort(void *ptr, size_t count, size_t size, + int (*cmp)(const void *, const void *, void *), + void *cmp_data); +#endif diff --git a/src/secp256k1/src/hsort_impl.h b/src/secp256k1/src/hsort_impl.h new file mode 100644 index 000000000..1c674ff1c --- /dev/null +++ b/src/secp256k1/src/hsort_impl.h @@ -0,0 +1,125 @@ +/*********************************************************************** + * Copyright (c) 2021 Russell O'Connor, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_HSORT_IMPL_H +#define SECP256K1_HSORT_IMPL_H + +#include "hsort.h" + +/* An array is a heap when, for all non-zero indexes i, the element at index i + * compares as less than or equal to the element at index parent(i) = (i-1)/2. + */ + +static SECP256K1_INLINE size_t secp256k1_heap_child1(size_t i) { + VERIFY_CHECK(i <= (SIZE_MAX - 1)/2); + return 2*i + 1; +} + +static SECP256K1_INLINE size_t secp256k1_heap_child2(size_t i) { + VERIFY_CHECK(i <= SIZE_MAX/2 - 1); + return secp256k1_heap_child1(i)+1; +} + +static SECP256K1_INLINE void secp256k1_heap_swap64(unsigned char *a, unsigned char *b, size_t len) { + unsigned char tmp[64]; + VERIFY_CHECK(len <= 64); + memcpy(tmp, a, len); + memmove(a, b, len); + memcpy(b, tmp, len); +} + +static SECP256K1_INLINE void secp256k1_heap_swap(unsigned char *arr, size_t i, size_t j, size_t stride) { + unsigned char *a = arr + i*stride; + unsigned char *b = arr + j*stride; + size_t len = stride; + while (64 < len) { + secp256k1_heap_swap64(a + (len - 64), b + (len - 64), 64); + len -= 64; + } + secp256k1_heap_swap64(a, b, len); +} + +/* This function accepts an array arr containing heap_size elements, each of + * size stride. The elements in the array at indices >i satisfy the max-heap + * property, i.e., for any element at index j (where j > i), all of its children + * are smaller than the element itself. The purpose of the function is to update + * the array so that all elements at indices >=i satisfy the max-heap + * property. */ +static SECP256K1_INLINE void secp256k1_heap_down(unsigned char *arr, size_t i, size_t heap_size, size_t stride, + int (*cmp)(const void *, const void *, void *), void *cmp_data) { + while (i < heap_size/2) { + VERIFY_CHECK(i <= SIZE_MAX/2 - 1); + /* Proof: + * i < heap_size/2 + * i + 1 <= heap_size/2 + * 2*i + 2 <= heap_size <= SIZE_MAX + * 2*i <= SIZE_MAX - 2 + */ + + VERIFY_CHECK(secp256k1_heap_child1(i) < heap_size); + /* Proof: + * i < heap_size/2 + * i + 1 <= heap_size/2 + * 2*i + 2 <= heap_size + * 2*i + 1 < heap_size + * child1(i) < heap_size + */ + + /* Let [x] be notation for the contents at arr[x*stride]. + * + * If [child1(i)] > [i] and [child2(i)] > [i], + * swap [i] with the larger child to ensure the new parent is larger + * than both children. When [child1(i)] == [child2(i)], swap [i] with + * [child2(i)]. + * Else if [child1(i)] > [i], swap [i] with [child1(i)]. + * Else if [child2(i)] > [i], swap [i] with [child2(i)]. + */ + if (secp256k1_heap_child2(i) < heap_size + && 0 <= cmp(arr + secp256k1_heap_child2(i)*stride, arr + secp256k1_heap_child1(i)*stride, cmp_data)) { + if (0 < cmp(arr + secp256k1_heap_child2(i)*stride, arr + i*stride, cmp_data)) { + secp256k1_heap_swap(arr, i, secp256k1_heap_child2(i), stride); + i = secp256k1_heap_child2(i); + } else { + /* At this point we have [child2(i)] >= [child1(i)] and we have + * [child2(i)] <= [i], and thus [child1(i)] <= [i] which means + * that the next comparison can be skipped. */ + return; + } + } else if (0 < cmp(arr + secp256k1_heap_child1(i)*stride, arr + i*stride, cmp_data)) { + secp256k1_heap_swap(arr, i, secp256k1_heap_child1(i), stride); + i = secp256k1_heap_child1(i); + } else { + return; + } + } + /* heap_size/2 <= i + * heap_size/2 < i + 1 + * heap_size < 2*i + 2 + * heap_size <= 2*i + 1 + * heap_size <= child1(i) + * Thus child1(i) and child2(i) are now out of bounds and we are at a leaf. + */ +} + +/* In-place heap sort. */ +static void secp256k1_hsort(void *ptr, size_t count, size_t size, + int (*cmp)(const void *, const void *, void *), + void *cmp_data) { + size_t i; + + for (i = count/2; 0 < i; --i) { + secp256k1_heap_down(ptr, i-1, count, size, cmp, cmp_data); + } + for (i = count; 1 < i; --i) { + /* Extract the largest value from the heap */ + secp256k1_heap_swap(ptr, 0, i-1, size); + + /* Repair the heap condition */ + secp256k1_heap_down(ptr, 0, i-1, size, cmp, cmp_data); + } +} + +#endif diff --git a/src/secp256k1/src/int128.h b/src/secp256k1/src/int128.h new file mode 100644 index 000000000..5355fbfae --- /dev/null +++ b/src/secp256k1/src/int128.h @@ -0,0 +1,90 @@ +#ifndef SECP256K1_INT128_H +#define SECP256K1_INT128_H + +#include "util.h" + +#if defined(SECP256K1_WIDEMUL_INT128) +# if defined(SECP256K1_INT128_NATIVE) +# include "int128_native.h" +# elif defined(SECP256K1_INT128_STRUCT) +# include "int128_struct.h" +# else +# error "Please select int128 implementation" +# endif + +/* Construct an unsigned 128-bit value from a high and a low 64-bit value. */ +static SECP256K1_INLINE void secp256k1_u128_load(secp256k1_uint128 *r, uint64_t hi, uint64_t lo); + +/* Multiply two unsigned 64-bit values a and b and write the result to r. */ +static SECP256K1_INLINE void secp256k1_u128_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b); + +/* Multiply two unsigned 64-bit values a and b and add the result to r. + * The final result is taken modulo 2^128. + */ +static SECP256K1_INLINE void secp256k1_u128_accum_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b); + +/* Add an unsigned 64-bit value a to r. + * The final result is taken modulo 2^128. + */ +static SECP256K1_INLINE void secp256k1_u128_accum_u64(secp256k1_uint128 *r, uint64_t a); + +/* Unsigned (logical) right shift. + * Non-constant time in n. + */ +static SECP256K1_INLINE void secp256k1_u128_rshift(secp256k1_uint128 *r, unsigned int n); + +/* Return the low 64-bits of a 128-bit value as an unsigned 64-bit value. */ +static SECP256K1_INLINE uint64_t secp256k1_u128_to_u64(const secp256k1_uint128 *a); + +/* Return the high 64-bits of a 128-bit value as an unsigned 64-bit value. */ +static SECP256K1_INLINE uint64_t secp256k1_u128_hi_u64(const secp256k1_uint128 *a); + +/* Write an unsigned 64-bit value to r. */ +static SECP256K1_INLINE void secp256k1_u128_from_u64(secp256k1_uint128 *r, uint64_t a); + +/* Tests if r is strictly less than to 2^n. + * n must be strictly less than 128. + */ +static SECP256K1_INLINE int secp256k1_u128_check_bits(const secp256k1_uint128 *r, unsigned int n); + +/* Construct an signed 128-bit value from a high and a low 64-bit value. */ +static SECP256K1_INLINE void secp256k1_i128_load(secp256k1_int128 *r, int64_t hi, uint64_t lo); + +/* Multiply two signed 64-bit values a and b and write the result to r. */ +static SECP256K1_INLINE void secp256k1_i128_mul(secp256k1_int128 *r, int64_t a, int64_t b); + +/* Multiply two signed 64-bit values a and b and add the result to r. + * Overflow or underflow from the addition is undefined behaviour. + */ +static SECP256K1_INLINE void secp256k1_i128_accum_mul(secp256k1_int128 *r, int64_t a, int64_t b); + +/* Compute a*d - b*c from signed 64-bit values and write the result to r. */ +static SECP256K1_INLINE void secp256k1_i128_det(secp256k1_int128 *r, int64_t a, int64_t b, int64_t c, int64_t d); + +/* Signed (arithmetic) right shift. + * Non-constant time in b. + */ +static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned int b); + +/* Return the input value modulo 2^64. */ +static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a); + +/* Return the value as a signed 64-bit value. + * Requires the input to be between INT64_MIN and INT64_MAX. + */ +static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a); + +/* Write a signed 64-bit value to r. */ +static SECP256K1_INLINE void secp256k1_i128_from_i64(secp256k1_int128 *r, int64_t a); + +/* Compare two 128-bit values for equality. */ +static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, const secp256k1_int128 *b); + +/* Tests if r is equal to sign*2^n (sign must be 1 or -1). + * n must be strictly less than 127. + */ +static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign); + +#endif + +#endif diff --git a/src/secp256k1/src/int128_impl.h b/src/secp256k1/src/int128_impl.h new file mode 100644 index 000000000..cfc573408 --- /dev/null +++ b/src/secp256k1/src/int128_impl.h @@ -0,0 +1,18 @@ +#ifndef SECP256K1_INT128_IMPL_H +#define SECP256K1_INT128_IMPL_H + +#include "util.h" + +#include "int128.h" + +#if defined(SECP256K1_WIDEMUL_INT128) +# if defined(SECP256K1_INT128_NATIVE) +# include "int128_native_impl.h" +# elif defined(SECP256K1_INT128_STRUCT) +# include "int128_struct_impl.h" +# else +# error "Please select int128 implementation" +# endif +#endif + +#endif diff --git a/src/secp256k1/src/int128_native.h b/src/secp256k1/src/int128_native.h new file mode 100644 index 000000000..7c97aafc7 --- /dev/null +++ b/src/secp256k1/src/int128_native.h @@ -0,0 +1,19 @@ +#ifndef SECP256K1_INT128_NATIVE_H +#define SECP256K1_INT128_NATIVE_H + +#include +#include "util.h" + +#if !defined(UINT128_MAX) && defined(__SIZEOF_INT128__) +SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; +SECP256K1_GNUC_EXT typedef __int128 int128_t; +# define UINT128_MAX ((uint128_t)(-1)) +# define INT128_MAX ((int128_t)(UINT128_MAX >> 1)) +# define INT128_MIN (-INT128_MAX - 1) +/* No (U)INT128_C macros because compilers providing __int128 do not support 128-bit literals. */ +#endif + +typedef uint128_t secp256k1_uint128; +typedef int128_t secp256k1_int128; + +#endif diff --git a/src/secp256k1/src/int128_native_impl.h b/src/secp256k1/src/int128_native_impl.h new file mode 100644 index 000000000..7f02e1590 --- /dev/null +++ b/src/secp256k1/src/int128_native_impl.h @@ -0,0 +1,94 @@ +#ifndef SECP256K1_INT128_NATIVE_IMPL_H +#define SECP256K1_INT128_NATIVE_IMPL_H + +#include "int128.h" +#include "util.h" + +static SECP256K1_INLINE void secp256k1_u128_load(secp256k1_uint128 *r, uint64_t hi, uint64_t lo) { + *r = (((uint128_t)hi) << 64) + lo; +} + +static SECP256K1_INLINE void secp256k1_u128_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b) { + *r = (uint128_t)a * b; +} + +static SECP256K1_INLINE void secp256k1_u128_accum_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b) { + *r += (uint128_t)a * b; +} + +static SECP256K1_INLINE void secp256k1_u128_accum_u64(secp256k1_uint128 *r, uint64_t a) { + *r += a; +} + +static SECP256K1_INLINE void secp256k1_u128_rshift(secp256k1_uint128 *r, unsigned int n) { + VERIFY_CHECK(n < 128); + *r >>= n; +} + +static SECP256K1_INLINE uint64_t secp256k1_u128_to_u64(const secp256k1_uint128 *a) { + return (uint64_t)(*a); +} + +static SECP256K1_INLINE uint64_t secp256k1_u128_hi_u64(const secp256k1_uint128 *a) { + return (uint64_t)(*a >> 64); +} + +static SECP256K1_INLINE void secp256k1_u128_from_u64(secp256k1_uint128 *r, uint64_t a) { + *r = a; +} + +static SECP256K1_INLINE int secp256k1_u128_check_bits(const secp256k1_uint128 *r, unsigned int n) { + VERIFY_CHECK(n < 128); + return (*r >> n == 0); +} + +static SECP256K1_INLINE void secp256k1_i128_load(secp256k1_int128 *r, int64_t hi, uint64_t lo) { + *r = (((uint128_t)(uint64_t)hi) << 64) + lo; +} + +static SECP256K1_INLINE void secp256k1_i128_mul(secp256k1_int128 *r, int64_t a, int64_t b) { + *r = (int128_t)a * b; +} + +static SECP256K1_INLINE void secp256k1_i128_accum_mul(secp256k1_int128 *r, int64_t a, int64_t b) { + int128_t ab = (int128_t)a * b; + VERIFY_CHECK(0 <= ab ? *r <= INT128_MAX - ab : INT128_MIN - ab <= *r); + *r += ab; +} + +static SECP256K1_INLINE void secp256k1_i128_det(secp256k1_int128 *r, int64_t a, int64_t b, int64_t c, int64_t d) { + int128_t ad = (int128_t)a * d; + int128_t bc = (int128_t)b * c; + VERIFY_CHECK(0 <= bc ? INT128_MIN + bc <= ad : ad <= INT128_MAX + bc); + *r = ad - bc; +} + +static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned int n) { + VERIFY_CHECK(n < 128); + *r >>= n; +} + +static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a) { + return (uint64_t)*a; +} + +static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a) { + VERIFY_CHECK(INT64_MIN <= *a && *a <= INT64_MAX); + return *a; +} + +static SECP256K1_INLINE void secp256k1_i128_from_i64(secp256k1_int128 *r, int64_t a) { + *r = a; +} + +static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, const secp256k1_int128 *b) { + return *a == *b; +} + +static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign) { + VERIFY_CHECK(n < 127); + VERIFY_CHECK(sign == 1 || sign == -1); + return (*r == (int128_t)((uint128_t)sign << n)); +} + +#endif diff --git a/src/secp256k1/src/int128_struct.h b/src/secp256k1/src/int128_struct.h new file mode 100644 index 000000000..6156f82cc --- /dev/null +++ b/src/secp256k1/src/int128_struct.h @@ -0,0 +1,14 @@ +#ifndef SECP256K1_INT128_STRUCT_H +#define SECP256K1_INT128_STRUCT_H + +#include +#include "util.h" + +typedef struct { + uint64_t lo; + uint64_t hi; +} secp256k1_uint128; + +typedef secp256k1_uint128 secp256k1_int128; + +#endif diff --git a/src/secp256k1/src/int128_struct_impl.h b/src/secp256k1/src/int128_struct_impl.h new file mode 100644 index 000000000..962a71d13 --- /dev/null +++ b/src/secp256k1/src/int128_struct_impl.h @@ -0,0 +1,205 @@ +#ifndef SECP256K1_INT128_STRUCT_IMPL_H +#define SECP256K1_INT128_STRUCT_IMPL_H + +#include "int128.h" +#include "util.h" + +#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) /* MSVC */ +# include +# if defined(_M_ARM64) || defined(SECP256K1_MSVC_MULH_TEST_OVERRIDE) +/* On ARM64 MSVC, use __(u)mulh for the upper half of 64x64 multiplications. + (Define SECP256K1_MSVC_MULH_TEST_OVERRIDE to test this code path on X64, + which supports both __(u)mulh and _umul128.) */ +# if defined(SECP256K1_MSVC_MULH_TEST_OVERRIDE) +# pragma message(__FILE__ ": SECP256K1_MSVC_MULH_TEST_OVERRIDE is defined, forcing use of __(u)mulh.") +# endif +static SECP256K1_INLINE uint64_t secp256k1_umul128(uint64_t a, uint64_t b, uint64_t* hi) { + *hi = __umulh(a, b); + return a * b; +} + +static SECP256K1_INLINE int64_t secp256k1_mul128(int64_t a, int64_t b, int64_t* hi) { + *hi = __mulh(a, b); + return (uint64_t)a * (uint64_t)b; +} +# else +/* On x84_64 MSVC, use native _(u)mul128 for 64x64->128 multiplications. */ +# define secp256k1_umul128 _umul128 +# define secp256k1_mul128 _mul128 +# endif +#else +/* On other systems, emulate 64x64->128 multiplications using 32x32->64 multiplications. */ +static SECP256K1_INLINE uint64_t secp256k1_umul128(uint64_t a, uint64_t b, uint64_t* hi) { + uint64_t ll = (uint64_t)(uint32_t)a * (uint32_t)b; + uint64_t lh = (uint32_t)a * (b >> 32); + uint64_t hl = (a >> 32) * (uint32_t)b; + uint64_t hh = (a >> 32) * (b >> 32); + uint64_t mid34 = (ll >> 32) + (uint32_t)lh + (uint32_t)hl; + *hi = hh + (lh >> 32) + (hl >> 32) + (mid34 >> 32); + return (mid34 << 32) + (uint32_t)ll; +} + +static SECP256K1_INLINE int64_t secp256k1_mul128(int64_t a, int64_t b, int64_t* hi) { + uint64_t ll = (uint64_t)(uint32_t)a * (uint32_t)b; + int64_t lh = (uint32_t)a * (b >> 32); + int64_t hl = (a >> 32) * (uint32_t)b; + int64_t hh = (a >> 32) * (b >> 32); + uint64_t mid34 = (ll >> 32) + (uint32_t)lh + (uint32_t)hl; + *hi = hh + (lh >> 32) + (hl >> 32) + (mid34 >> 32); + return (mid34 << 32) + (uint32_t)ll; +} +#endif + +static SECP256K1_INLINE void secp256k1_u128_load(secp256k1_uint128 *r, uint64_t hi, uint64_t lo) { + r->hi = hi; + r->lo = lo; +} + +static SECP256K1_INLINE void secp256k1_u128_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b) { + r->lo = secp256k1_umul128(a, b, &r->hi); +} + +static SECP256K1_INLINE void secp256k1_u128_accum_mul(secp256k1_uint128 *r, uint64_t a, uint64_t b) { + uint64_t lo, hi; + lo = secp256k1_umul128(a, b, &hi); + r->lo += lo; + r->hi += hi + (r->lo < lo); +} + +static SECP256K1_INLINE void secp256k1_u128_accum_u64(secp256k1_uint128 *r, uint64_t a) { + r->lo += a; + r->hi += r->lo < a; +} + +/* Unsigned (logical) right shift. + * Non-constant time in n. + */ +static SECP256K1_INLINE void secp256k1_u128_rshift(secp256k1_uint128 *r, unsigned int n) { + VERIFY_CHECK(n < 128); + if (n >= 64) { + r->lo = r->hi >> (n-64); + r->hi = 0; + } else if (n > 0) { +#if defined(_MSC_VER) && defined(_M_X64) + VERIFY_CHECK(n < 64); + r->lo = __shiftright128(r->lo, r->hi, n); +#else + r->lo = ((1U * r->hi) << (64-n)) | r->lo >> n; +#endif + r->hi >>= n; + } +} + +static SECP256K1_INLINE uint64_t secp256k1_u128_to_u64(const secp256k1_uint128 *a) { + return a->lo; +} + +static SECP256K1_INLINE uint64_t secp256k1_u128_hi_u64(const secp256k1_uint128 *a) { + return a->hi; +} + +static SECP256K1_INLINE void secp256k1_u128_from_u64(secp256k1_uint128 *r, uint64_t a) { + r->hi = 0; + r->lo = a; +} + +static SECP256K1_INLINE int secp256k1_u128_check_bits(const secp256k1_uint128 *r, unsigned int n) { + VERIFY_CHECK(n < 128); + return n >= 64 ? r->hi >> (n - 64) == 0 + : r->hi == 0 && r->lo >> n == 0; +} + +static SECP256K1_INLINE void secp256k1_i128_load(secp256k1_int128 *r, int64_t hi, uint64_t lo) { + r->hi = hi; + r->lo = lo; +} + +static SECP256K1_INLINE void secp256k1_i128_mul(secp256k1_int128 *r, int64_t a, int64_t b) { + int64_t hi; + r->lo = (uint64_t)secp256k1_mul128(a, b, &hi); + r->hi = (uint64_t)hi; +} + +static SECP256K1_INLINE void secp256k1_i128_accum_mul(secp256k1_int128 *r, int64_t a, int64_t b) { + int64_t hi; + uint64_t lo = (uint64_t)secp256k1_mul128(a, b, &hi); + r->lo += lo; + hi += r->lo < lo; + /* Verify no overflow. + * If r represents a positive value (the sign bit is not set) and the value we are adding is a positive value (the sign bit is not set), + * then we require that the resulting value also be positive (the sign bit is not set). + * Note that (X <= Y) means (X implies Y) when X and Y are boolean values (i.e. 0 or 1). + */ + VERIFY_CHECK((r->hi <= 0x7fffffffffffffffu && (uint64_t)hi <= 0x7fffffffffffffffu) <= (r->hi + (uint64_t)hi <= 0x7fffffffffffffffu)); + /* Verify no underflow. + * If r represents a negative value (the sign bit is set) and the value we are adding is a negative value (the sign bit is set), + * then we require that the resulting value also be negative (the sign bit is set). + */ + VERIFY_CHECK((r->hi > 0x7fffffffffffffffu && (uint64_t)hi > 0x7fffffffffffffffu) <= (r->hi + (uint64_t)hi > 0x7fffffffffffffffu)); + r->hi += hi; +} + +static SECP256K1_INLINE void secp256k1_i128_dissip_mul(secp256k1_int128 *r, int64_t a, int64_t b) { + int64_t hi; + uint64_t lo = (uint64_t)secp256k1_mul128(a, b, &hi); + hi += r->lo < lo; + /* Verify no overflow. + * If r represents a positive value (the sign bit is not set) and the value we are subtracting is a negative value (the sign bit is set), + * then we require that the resulting value also be positive (the sign bit is not set). + */ + VERIFY_CHECK((r->hi <= 0x7fffffffffffffffu && (uint64_t)hi > 0x7fffffffffffffffu) <= (r->hi - (uint64_t)hi <= 0x7fffffffffffffffu)); + /* Verify no underflow. + * If r represents a negative value (the sign bit is set) and the value we are subtracting is a positive value (the sign sign bit is not set), + * then we require that the resulting value also be negative (the sign bit is set). + */ + VERIFY_CHECK((r->hi > 0x7fffffffffffffffu && (uint64_t)hi <= 0x7fffffffffffffffu) <= (r->hi - (uint64_t)hi > 0x7fffffffffffffffu)); + r->hi -= hi; + r->lo -= lo; +} + +static SECP256K1_INLINE void secp256k1_i128_det(secp256k1_int128 *r, int64_t a, int64_t b, int64_t c, int64_t d) { + secp256k1_i128_mul(r, a, d); + secp256k1_i128_dissip_mul(r, b, c); +} + +/* Signed (arithmetic) right shift. + * Non-constant time in n. + */ +static SECP256K1_INLINE void secp256k1_i128_rshift(secp256k1_int128 *r, unsigned int n) { + VERIFY_CHECK(n < 128); + if (n >= 64) { + r->lo = (uint64_t)((int64_t)(r->hi) >> (n-64)); + r->hi = (uint64_t)((int64_t)(r->hi) >> 63); + } else if (n > 0) { + r->lo = ((1U * r->hi) << (64-n)) | r->lo >> n; + r->hi = (uint64_t)((int64_t)(r->hi) >> n); + } +} + +static SECP256K1_INLINE uint64_t secp256k1_i128_to_u64(const secp256k1_int128 *a) { + return a->lo; +} + +static SECP256K1_INLINE int64_t secp256k1_i128_to_i64(const secp256k1_int128 *a) { + /* Verify that a represents a 64 bit signed value by checking that the high bits are a sign extension of the low bits. */ + VERIFY_CHECK(a->hi == -(a->lo >> 63)); + return (int64_t)secp256k1_i128_to_u64(a); +} + +static SECP256K1_INLINE void secp256k1_i128_from_i64(secp256k1_int128 *r, int64_t a) { + r->hi = (uint64_t)(a >> 63); + r->lo = (uint64_t)a; +} + +static SECP256K1_INLINE int secp256k1_i128_eq_var(const secp256k1_int128 *a, const secp256k1_int128 *b) { + return a->hi == b->hi && a->lo == b->lo; +} + +static SECP256K1_INLINE int secp256k1_i128_check_pow2(const secp256k1_int128 *r, unsigned int n, int sign) { + VERIFY_CHECK(n < 127); + VERIFY_CHECK(sign == 1 || sign == -1); + return n >= 64 ? r->hi == (uint64_t)sign << (n - 64) && r->lo == 0 + : r->hi == (uint64_t)(sign >> 1) && r->lo == (uint64_t)sign << n; +} + +#endif diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java deleted file mode 100644 index 1c67802fb..000000000 --- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * Copyright 2014-2016 the libsecp256k1 contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoin; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import java.math.BigInteger; -import com.google.common.base.Preconditions; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import static org.bitcoin.NativeSecp256k1Util.*; - -/** - *

This class holds native methods to handle ECDSA verification.

- * - *

You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1

- * - *

To build secp256k1 for use with bitcoinj, run - * `./configure --enable-jni --enable-experimental --enable-module-ecdh` - * and `make` then copy `.libs/libsecp256k1.so` to your system library path - * or point the JVM to the folder containing it with -Djava.library.path - *

- */ -public class NativeSecp256k1 { - - private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); - private static final Lock r = rwl.readLock(); - private static final Lock w = rwl.writeLock(); - private static ThreadLocal nativeECDSABuffer = new ThreadLocal(); - /** - * Verifies the given secp256k1 signature in native code. - * Calling when enabled == false is undefined (probably library not loaded) - * - * @param data The data which was signed, must be exactly 32 bytes - * @param signature The signature - * @param pub The public key which did the signing - */ - public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{ - Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < 520) { - byteBuff = ByteBuffer.allocateDirect(520); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(data); - byteBuff.put(signature); - byteBuff.put(pub); - - byte[][] retByteArray; - - r.lock(); - try { - return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1; - } finally { - r.unlock(); - } - } - - /** - * libsecp256k1 Create an ECDSA signature. - * - * @param data Message hash, 32 bytes - * @param key Secret key, 32 bytes - * - * Return values - * @param sig byte array of signature - */ - public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{ - Preconditions.checkArgument(data.length == 32 && sec.length <= 32); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < 32 + 32) { - byteBuff = ByteBuffer.allocateDirect(32 + 32); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(data); - byteBuff.put(sec); - - byte[][] retByteArray; - - r.lock(); - try { - retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext()); - } finally { - r.unlock(); - } - - byte[] sigArr = retByteArray[0]; - int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); - int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); - - assertEquals(sigArr.length, sigLen, "Got bad signature length."); - - return retVal == 0 ? new byte[0] : sigArr; - } - - /** - * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid - * - * @param seckey ECDSA Secret key, 32 bytes - */ - public static boolean secKeyVerify(byte[] seckey) { - Preconditions.checkArgument(seckey.length == 32); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < seckey.length) { - byteBuff = ByteBuffer.allocateDirect(seckey.length); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(seckey); - - r.lock(); - try { - return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1; - } finally { - r.unlock(); - } - } - - - /** - * libsecp256k1 Compute Pubkey - computes public key from secret key - * - * @param seckey ECDSA Secret key, 32 bytes - * - * Return values - * @param pubkey ECDSA Public key, 33 or 65 bytes - */ - //TODO add a 'compressed' arg - public static byte[] computePubkey(byte[] seckey) throws AssertFailException{ - Preconditions.checkArgument(seckey.length == 32); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < seckey.length) { - byteBuff = ByteBuffer.allocateDirect(seckey.length); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(seckey); - - byte[][] retByteArray; - - r.lock(); - try { - retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext()); - } finally { - r.unlock(); - } - - byte[] pubArr = retByteArray[0]; - int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); - int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); - - assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); - - return retVal == 0 ? new byte[0]: pubArr; - } - - /** - * libsecp256k1 Cleanup - This destroys the secp256k1 context object - * This should be called at the end of the program for proper cleanup of the context. - */ - public static synchronized void cleanup() { - w.lock(); - try { - secp256k1_destroy_context(Secp256k1Context.getContext()); - } finally { - w.unlock(); - } - } - - public static long cloneContext() { - r.lock(); - try { - return secp256k1_ctx_clone(Secp256k1Context.getContext()); - } finally { r.unlock(); } - } - - /** - * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it - * - * @param tweak some bytes to tweak with - * @param seckey 32-byte seckey - */ - public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{ - Preconditions.checkArgument(privkey.length == 32); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { - byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(privkey); - byteBuff.put(tweak); - - byte[][] retByteArray; - r.lock(); - try { - retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext()); - } finally { - r.unlock(); - } - - byte[] privArr = retByteArray[0]; - - int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; - int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); - - assertEquals(privArr.length, privLen, "Got bad pubkey length."); - - assertEquals(retVal, 1, "Failed return value check."); - - return privArr; - } - - /** - * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it - * - * @param tweak some bytes to tweak with - * @param seckey 32-byte seckey - */ - public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{ - Preconditions.checkArgument(privkey.length == 32); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { - byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(privkey); - byteBuff.put(tweak); - - byte[][] retByteArray; - r.lock(); - try { - retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext()); - } finally { - r.unlock(); - } - - byte[] privArr = retByteArray[0]; - - int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; - int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); - - assertEquals(privArr.length, privLen, "Got bad pubkey length."); - - assertEquals(retVal, 1, "Failed return value check."); - - return privArr; - } - - /** - * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it - * - * @param tweak some bytes to tweak with - * @param pubkey 32-byte seckey - */ - public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{ - Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { - byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(pubkey); - byteBuff.put(tweak); - - byte[][] retByteArray; - r.lock(); - try { - retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length); - } finally { - r.unlock(); - } - - byte[] pubArr = retByteArray[0]; - - int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; - int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); - - assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); - - assertEquals(retVal, 1, "Failed return value check."); - - return pubArr; - } - - /** - * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it - * - * @param tweak some bytes to tweak with - * @param pubkey 32-byte seckey - */ - public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{ - Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { - byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(pubkey); - byteBuff.put(tweak); - - byte[][] retByteArray; - r.lock(); - try { - retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length); - } finally { - r.unlock(); - } - - byte[] pubArr = retByteArray[0]; - - int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; - int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); - - assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); - - assertEquals(retVal, 1, "Failed return value check."); - - return pubArr; - } - - /** - * libsecp256k1 create ECDH secret - constant time ECDH calculation - * - * @param seckey byte array of secret key used in exponentiaion - * @param pubkey byte array of public key used in exponentiaion - */ - public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{ - Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) { - byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(seckey); - byteBuff.put(pubkey); - - byte[][] retByteArray; - r.lock(); - try { - retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length); - } finally { - r.unlock(); - } - - byte[] resArr = retByteArray[0]; - int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); - - assertEquals(resArr.length, 32, "Got bad result length."); - assertEquals(retVal, 1, "Failed return value check."); - - return resArr; - } - - /** - * libsecp256k1 randomize - updates the context randomization - * - * @param seed 32-byte random seed - */ - public static synchronized boolean randomize(byte[] seed) throws AssertFailException{ - Preconditions.checkArgument(seed.length == 32 || seed == null); - - ByteBuffer byteBuff = nativeECDSABuffer.get(); - if (byteBuff == null || byteBuff.capacity() < seed.length) { - byteBuff = ByteBuffer.allocateDirect(seed.length); - byteBuff.order(ByteOrder.nativeOrder()); - nativeECDSABuffer.set(byteBuff); - } - byteBuff.rewind(); - byteBuff.put(seed); - - w.lock(); - try { - return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1; - } finally { - w.unlock(); - } - } - - private static native long secp256k1_ctx_clone(long context); - - private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context); - - private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context); - - private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context); - - private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen); - - private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen); - - private static native void secp256k1_destroy_context(long context); - - private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen); - - private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context); - - private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context); - - private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context); - - private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen); - - private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen); - -} diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java deleted file mode 100644 index c00d08899..000000000 --- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java +++ /dev/null @@ -1,226 +0,0 @@ -package org.bitcoin; - -import com.google.common.io.BaseEncoding; -import java.util.Arrays; -import java.math.BigInteger; -import javax.xml.bind.DatatypeConverter; -import static org.bitcoin.NativeSecp256k1Util.*; - -/** - * This class holds test cases defined for testing this library. - */ -public class NativeSecp256k1Test { - - //TODO improve comments/add more tests - /** - * This tests verify() for a valid signature - */ - public static void testVerifyPos() throws AssertFailException{ - boolean result = false; - byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" - byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); - byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); - - result = NativeSecp256k1.verify( data, sig, pub); - assertEquals( result, true , "testVerifyPos"); - } - - /** - * This tests verify() for a non-valid signature - */ - public static void testVerifyNeg() throws AssertFailException{ - boolean result = false; - byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing" - byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); - byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); - - result = NativeSecp256k1.verify( data, sig, pub); - //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); - assertEquals( result, false , "testVerifyNeg"); - } - - /** - * This tests secret key verify() for a valid secretkey - */ - public static void testSecKeyVerifyPos() throws AssertFailException{ - boolean result = false; - byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); - - result = NativeSecp256k1.secKeyVerify( sec ); - //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); - assertEquals( result, true , "testSecKeyVerifyPos"); - } - - /** - * This tests secret key verify() for a invalid secretkey - */ - public static void testSecKeyVerifyNeg() throws AssertFailException{ - boolean result = false; - byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); - - result = NativeSecp256k1.secKeyVerify( sec ); - //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); - assertEquals( result, false , "testSecKeyVerifyNeg"); - } - - /** - * This tests public key create() for a valid secretkey - */ - public static void testPubKeyCreatePos() throws AssertFailException{ - byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); - - byte[] resultArr = NativeSecp256k1.computePubkey( sec); - String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos"); - } - - /** - * This tests public key create() for a invalid secretkey - */ - public static void testPubKeyCreateNeg() throws AssertFailException{ - byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); - - byte[] resultArr = NativeSecp256k1.computePubkey( sec); - String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( pubkeyString, "" , "testPubKeyCreateNeg"); - } - - /** - * This tests sign() for a valid secretkey - */ - public static void testSignPos() throws AssertFailException{ - - byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" - byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); - - byte[] resultArr = NativeSecp256k1.sign(data, sec); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos"); - } - - /** - * This tests sign() for a invalid secretkey - */ - public static void testSignNeg() throws AssertFailException{ - byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" - byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); - - byte[] resultArr = NativeSecp256k1.sign(data, sec); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( sigString, "" , "testSignNeg"); - } - - /** - * This tests private key tweak-add - */ - public static void testPrivKeyTweakAdd_1() throws AssertFailException { - byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); - byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" - - byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data ); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1"); - } - - /** - * This tests private key tweak-mul - */ - public static void testPrivKeyTweakMul_1() throws AssertFailException { - byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); - byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" - - byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data ); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1"); - } - - /** - * This tests private key tweak-add uncompressed - */ - public static void testPrivKeyTweakAdd_2() throws AssertFailException { - byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); - byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" - - byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data ); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2"); - } - - /** - * This tests private key tweak-mul uncompressed - */ - public static void testPrivKeyTweakMul_2() throws AssertFailException { - byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); - byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" - - byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data ); - String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2"); - } - - /** - * This tests seed randomization - */ - public static void testRandomize() throws AssertFailException { - byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random" - boolean result = NativeSecp256k1.randomize(seed); - assertEquals( result, true, "testRandomize"); - } - - public static void testCreateECDHSecret() throws AssertFailException{ - - byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); - byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); - - byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub); - String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); - assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret"); - } - - public static void main(String[] args) throws AssertFailException{ - - - System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n"); - - assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" ); - - //Test verify() success/fail - testVerifyPos(); - testVerifyNeg(); - - //Test secKeyVerify() success/fail - testSecKeyVerifyPos(); - testSecKeyVerifyNeg(); - - //Test computePubkey() success/fail - testPubKeyCreatePos(); - testPubKeyCreateNeg(); - - //Test sign() success/fail - testSignPos(); - testSignNeg(); - - //Test privKeyTweakAdd() 1 - testPrivKeyTweakAdd_1(); - - //Test privKeyTweakMul() 2 - testPrivKeyTweakMul_1(); - - //Test privKeyTweakAdd() 3 - testPrivKeyTweakAdd_2(); - - //Test privKeyTweakMul() 4 - testPrivKeyTweakMul_2(); - - //Test randomize() - testRandomize(); - - //Test ECDH - testCreateECDHSecret(); - - NativeSecp256k1.cleanup(); - - System.out.println(" All tests passed." ); - - } -} diff --git a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java deleted file mode 100644 index 04732ba04..000000000 --- a/src/secp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-2016 the libsecp256k1 contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoin; - -public class NativeSecp256k1Util{ - - public static void assertEquals( int val, int val2, String message ) throws AssertFailException{ - if( val != val2 ) - throw new AssertFailException("FAIL: " + message); - } - - public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{ - if( val != val2 ) - throw new AssertFailException("FAIL: " + message); - else - System.out.println("PASS: " + message); - } - - public static void assertEquals( String val, String val2, String message ) throws AssertFailException{ - if( !val.equals(val2) ) - throw new AssertFailException("FAIL: " + message); - else - System.out.println("PASS: " + message); - } - - public static class AssertFailException extends Exception { - public AssertFailException(String message) { - super( message ); - } - } -} diff --git a/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java b/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java deleted file mode 100644 index 216c986a8..000000000 --- a/src/secp256k1/src/java/org/bitcoin/Secp256k1Context.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014-2016 the libsecp256k1 contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.bitcoin; - -/** - * This class holds the context reference used in native methods - * to handle ECDSA operations. - */ -public class Secp256k1Context { - private static final boolean enabled; //true if the library is loaded - private static final long context; //ref to pointer to context obj - - static { //static initializer - boolean isEnabled = true; - long contextRef = -1; - try { - System.loadLibrary("secp256k1"); - contextRef = secp256k1_init_context(); - } catch (UnsatisfiedLinkError e) { - System.out.println("UnsatisfiedLinkError: " + e.toString()); - isEnabled = false; - } - enabled = isEnabled; - context = contextRef; - } - - public static boolean isEnabled() { - return enabled; - } - - public static long getContext() { - if(!enabled) return -1; //sanity check - return context; - } - - private static native long secp256k1_init_context(); -} diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c deleted file mode 100644 index bcef7b32c..000000000 --- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.c +++ /dev/null @@ -1,377 +0,0 @@ -#include -#include -#include -#include "org_bitcoin_NativeSecp256k1.h" -#include "include/secp256k1.h" -#include "include/secp256k1_ecdh.h" -#include "include/secp256k1_recovery.h" - - -SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone - (JNIEnv* env, jclass classObject, jlong ctx_l) -{ - const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - - jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx); - - (void)classObject;(void)env; - - return ctx_clone_l; - -} - -SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - - const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - (void)classObject; - - return secp256k1_context_randomize(ctx, seed); - -} - -SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context - (JNIEnv* env, jclass classObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - - secp256k1_context_destroy(ctx); - - (void)classObject;(void)env; -} - -SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - - unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* sigdata = { (unsigned char*) (data + 32) }; - const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) }; - - secp256k1_ecdsa_signature sig; - secp256k1_pubkey pubkey; - - int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen); - - if( ret ) { - ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); - - if( ret ) { - ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey); - } - } - - (void)classObject; - - return ret; -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - unsigned char* secKey = (unsigned char*) (data + 32); - - jobjectArray retArray; - jbyteArray sigArray, intsByteArray; - unsigned char intsarray[2]; - - secp256k1_ecdsa_signature sig[72]; - - int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL ); - - unsigned char outputSer[72]; - size_t outputLen = 72; - - if( ret ) { - int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - sigArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, sigArray); - - intsByteArray = (*env)->NewByteArray(env, 2); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - -SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - (void)classObject; - - return secp256k1_ec_seckey_verify(ctx, secKey); -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - - secp256k1_pubkey pubkey; - - jobjectArray retArray; - jbyteArray pubkeyArray, intsByteArray; - unsigned char intsarray[2]; - - int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey); - - unsigned char outputSer[65]; - size_t outputLen = 65; - - if( ret ) { - int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubkeyArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray); - - intsByteArray = (*env)->NewByteArray(env, 2); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; - -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* tweak = (unsigned char*) (privkey + 32); - - jobjectArray retArray; - jbyteArray privArray, intsByteArray; - unsigned char intsarray[2]; - - int privkeylen = 32; - - int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak); - - intsarray[0] = privkeylen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - privArray = (*env)->NewByteArray(env, privkeylen); - (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); - (*env)->SetObjectArrayElement(env, retArray, 0, privArray); - - intsByteArray = (*env)->NewByteArray(env, 2); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* tweak = (unsigned char*) (privkey + 32); - - jobjectArray retArray; - jbyteArray privArray, intsByteArray; - unsigned char intsarray[2]; - - int privkeylen = 32; - - int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak); - - intsarray[0] = privkeylen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - privArray = (*env)->NewByteArray(env, privkeylen); - (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); - (*env)->SetObjectArrayElement(env, retArray, 0, privArray); - - intsByteArray = (*env)->NewByteArray(env, 2); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; -/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/ - unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* tweak = (unsigned char*) (pkey + publen); - - jobjectArray retArray; - jbyteArray pubArray, intsByteArray; - unsigned char intsarray[2]; - unsigned char outputSer[65]; - size_t outputLen = 65; - - secp256k1_pubkey pubkey; - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); - - if( ret ) { - ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak); - } - - if( ret ) { - int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); - - intsByteArray = (*env)->NewByteArray(env, 2); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* tweak = (unsigned char*) (pkey + publen); - - jobjectArray retArray; - jbyteArray pubArray, intsByteArray; - unsigned char intsarray[2]; - unsigned char outputSer[65]; - size_t outputLen = 65; - - secp256k1_pubkey pubkey; - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); - - if ( ret ) { - ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak); - } - - if( ret ) { - int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; - } - - intsarray[0] = outputLen; - intsarray[1] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - pubArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); - (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); - - intsByteArray = (*env)->NewByteArray(env, 2); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} - -SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine - (JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys) -{ - (void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys; - - return 0; -} - -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) -{ - secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; - const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject); - const unsigned char* pubdata = (const unsigned char*) (secdata + 32); - - jobjectArray retArray; - jbyteArray outArray, intsByteArray; - unsigned char intsarray[1]; - secp256k1_pubkey pubkey; - unsigned char nonce_res[32]; - size_t outputLen = 32; - - int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); - - if (ret) { - ret = secp256k1_ecdh( - ctx, - nonce_res, - &pubkey, - secdata - ); - } - - intsarray[0] = ret; - - retArray = (*env)->NewObjectArray(env, 2, - (*env)->FindClass(env, "[B"), - (*env)->NewByteArray(env, 1)); - - outArray = (*env)->NewByteArray(env, outputLen); - (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res); - (*env)->SetObjectArrayElement(env, retArray, 0, outArray); - - intsByteArray = (*env)->NewByteArray(env, 1); - (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray); - (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); - - (void)classObject; - - return retArray; -} diff --git a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h deleted file mode 100644 index fe613c9e9..000000000 --- a/src/secp256k1/src/java/org_bitcoin_NativeSecp256k1.h +++ /dev/null @@ -1,119 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -#include "include/secp256k1.h" -/* Header for class org_bitcoin_NativeSecp256k1 */ - -#ifndef _Included_org_bitcoin_NativeSecp256k1 -#define _Included_org_bitcoin_NativeSecp256k1 -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_ctx_clone - * Signature: (J)J - */ -SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone - (JNIEnv *, jclass, jlong); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_context_randomize - * Signature: (Ljava/nio/ByteBuffer;J)I - */ -SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_privkey_tweak_add - * Signature: (Ljava/nio/ByteBuffer;J)[[B - */ -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_privkey_tweak_mul - * Signature: (Ljava/nio/ByteBuffer;J)[[B - */ -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_pubkey_tweak_add - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add - (JNIEnv *, jclass, jobject, jlong, jint); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_pubkey_tweak_mul - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul - (JNIEnv *, jclass, jobject, jlong, jint); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_destroy_context - * Signature: (J)V - */ -SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context - (JNIEnv *, jclass, jlong); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_ecdsa_verify - * Signature: (Ljava/nio/ByteBuffer;JII)I - */ -SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify - (JNIEnv *, jclass, jobject, jlong, jint, jint); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_ecdsa_sign - * Signature: (Ljava/nio/ByteBuffer;J)[[B - */ -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_ec_seckey_verify - * Signature: (Ljava/nio/ByteBuffer;J)I - */ -SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_ec_pubkey_create - * Signature: (Ljava/nio/ByteBuffer;J)[[B - */ -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create - (JNIEnv *, jclass, jobject, jlong); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_ec_pubkey_parse - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse - (JNIEnv *, jclass, jobject, jlong, jint); - -/* - * Class: org_bitcoin_NativeSecp256k1 - * Method: secp256k1_ecdh - * Signature: (Ljava/nio/ByteBuffer;JI)[[B - */ -SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh - (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen); - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c deleted file mode 100644 index a52939e7e..000000000 --- a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include "org_bitcoin_Secp256k1Context.h" -#include "include/secp256k1.h" - -SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context - (JNIEnv* env, jclass classObject) -{ - secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - - (void)classObject;(void)env; - - return (uintptr_t)ctx; -} - diff --git a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h b/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h deleted file mode 100644 index 0d2bc84b7..000000000 --- a/src/secp256k1/src/java/org_bitcoin_Secp256k1Context.h +++ /dev/null @@ -1,22 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -#include "include/secp256k1.h" -/* Header for class org_bitcoin_Secp256k1Context */ - -#ifndef _Included_org_bitcoin_Secp256k1Context -#define _Included_org_bitcoin_Secp256k1Context -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: org_bitcoin_Secp256k1Context - * Method: secp256k1_init_context - * Signature: ()J - */ -SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context - (JNIEnv *, jclass); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/secp256k1/src/modinv32.h b/src/secp256k1/src/modinv32.h new file mode 100644 index 000000000..846c642f8 --- /dev/null +++ b/src/secp256k1/src/modinv32.h @@ -0,0 +1,43 @@ +/*********************************************************************** + * Copyright (c) 2020 Peter Dettman * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODINV32_H +#define SECP256K1_MODINV32_H + +#include "util.h" + +/* A signed 30-bit limb representation of integers. + * + * Its value is sum(v[i] * 2^(30*i), i=0..8). */ +typedef struct { + int32_t v[9]; +} secp256k1_modinv32_signed30; + +typedef struct { + /* The modulus in signed30 notation, must be odd and in [3, 2^256]. */ + secp256k1_modinv32_signed30 modulus; + + /* modulus^{-1} mod 2^30 */ + uint32_t modulus_inv30; +} secp256k1_modinv32_modinfo; + +/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus). + * If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of + * x and modulus must be 1). These rules are automatically satisfied if the modulus is prime. + * + * On output, all of x's limbs will be in [0, 2^30). + */ +static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); + +/* Same as secp256k1_modinv32_var, but constant time in x (not in the modulus). */ +static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); + +/* Compute the Jacobi symbol for (x | modinfo->modulus). x must be coprime with modulus (and thus + * cannot be 0, as modulus >= 3). All limbs of x must be non-negative. Returns 0 if the result + * cannot be computed. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); + +#endif /* SECP256K1_MODINV32_H */ diff --git a/src/secp256k1/src/modinv32_impl.h b/src/secp256k1/src/modinv32_impl.h new file mode 100644 index 000000000..75eb354ff --- /dev/null +++ b/src/secp256k1/src/modinv32_impl.h @@ -0,0 +1,727 @@ +/*********************************************************************** + * Copyright (c) 2020 Peter Dettman * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODINV32_IMPL_H +#define SECP256K1_MODINV32_IMPL_H + +#include "modinv32.h" + +#include "util.h" + +#include + +/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and + * modular inversion" by Daniel J. Bernstein and Bo-Yin Yang. + * + * For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an + * implementation for N=30, using 30-bit signed limbs represented as int32_t. + */ + +#ifdef VERIFY +static const secp256k1_modinv32_signed30 SECP256K1_SIGNED30_ONE = {{1}}; + +/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^30). */ +static void secp256k1_modinv32_mul_30(secp256k1_modinv32_signed30 *r, const secp256k1_modinv32_signed30 *a, int alen, int32_t factor) { + const int32_t M30 = (int32_t)(UINT32_MAX >> 2); + int64_t c = 0; + int i; + for (i = 0; i < 8; ++i) { + if (i < alen) c += (int64_t)a->v[i] * factor; + r->v[i] = (int32_t)c & M30; c >>= 30; + } + if (8 < alen) c += (int64_t)a->v[8] * factor; + VERIFY_CHECK(c == (int32_t)c); + r->v[8] = (int32_t)c; +} + +/* Return -1 for ab*factor. A consists of alen limbs; b has 9. */ +static int secp256k1_modinv32_mul_cmp_30(const secp256k1_modinv32_signed30 *a, int alen, const secp256k1_modinv32_signed30 *b, int32_t factor) { + int i; + secp256k1_modinv32_signed30 am, bm; + secp256k1_modinv32_mul_30(&am, a, alen, 1); /* Normalize all but the top limb of a. */ + secp256k1_modinv32_mul_30(&bm, b, 9, factor); + for (i = 0; i < 8; ++i) { + /* Verify that all but the top limb of a and b are normalized. */ + VERIFY_CHECK(am.v[i] >> 30 == 0); + VERIFY_CHECK(bm.v[i] >> 30 == 0); + } + for (i = 8; i >= 0; --i) { + if (am.v[i] < bm.v[i]) return -1; + if (am.v[i] > bm.v[i]) return 1; + } + return 0; +} +#endif + +/* Take as input a signed30 number in range (-2*modulus,modulus), and add a multiple of the modulus + * to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the + * process. The input must have limbs in range (-2^30,2^30). The output will have limbs in range + * [0,2^30). */ +static void secp256k1_modinv32_normalize_30(secp256k1_modinv32_signed30 *r, int32_t sign, const secp256k1_modinv32_modinfo *modinfo) { + const int32_t M30 = (int32_t)(UINT32_MAX >> 2); + int32_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4], + r5 = r->v[5], r6 = r->v[6], r7 = r->v[7], r8 = r->v[8]; + volatile int32_t cond_add, cond_negate; + +#ifdef VERIFY + /* Verify that all limbs are in range (-2^30,2^30). */ + int i; + for (i = 0; i < 9; ++i) { + VERIFY_CHECK(r->v[i] >= -M30); + VERIFY_CHECK(r->v[i] <= M30); + } + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, -2) > 0); /* r > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */ +#endif + + /* In a first step, add the modulus if the input is negative, and then negate if requested. + * This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input + * limbs are in range (-2^30,2^30), this cannot overflow an int32_t. Note that the right + * shifts below are signed sign-extending shifts (see assumptions.h for tests that that is + * indeed the behavior of the right shift operator). */ + cond_add = r8 >> 31; + r0 += modinfo->modulus.v[0] & cond_add; + r1 += modinfo->modulus.v[1] & cond_add; + r2 += modinfo->modulus.v[2] & cond_add; + r3 += modinfo->modulus.v[3] & cond_add; + r4 += modinfo->modulus.v[4] & cond_add; + r5 += modinfo->modulus.v[5] & cond_add; + r6 += modinfo->modulus.v[6] & cond_add; + r7 += modinfo->modulus.v[7] & cond_add; + r8 += modinfo->modulus.v[8] & cond_add; + cond_negate = sign >> 31; + r0 = (r0 ^ cond_negate) - cond_negate; + r1 = (r1 ^ cond_negate) - cond_negate; + r2 = (r2 ^ cond_negate) - cond_negate; + r3 = (r3 ^ cond_negate) - cond_negate; + r4 = (r4 ^ cond_negate) - cond_negate; + r5 = (r5 ^ cond_negate) - cond_negate; + r6 = (r6 ^ cond_negate) - cond_negate; + r7 = (r7 ^ cond_negate) - cond_negate; + r8 = (r8 ^ cond_negate) - cond_negate; + /* Propagate the top bits, to bring limbs back to range (-2^30,2^30). */ + r1 += r0 >> 30; r0 &= M30; + r2 += r1 >> 30; r1 &= M30; + r3 += r2 >> 30; r2 &= M30; + r4 += r3 >> 30; r3 &= M30; + r5 += r4 >> 30; r4 &= M30; + r6 += r5 >> 30; r5 &= M30; + r7 += r6 >> 30; r6 &= M30; + r8 += r7 >> 30; r7 &= M30; + + /* In a second step add the modulus again if the result is still negative, bringing r to range + * [0,modulus). */ + cond_add = r8 >> 31; + r0 += modinfo->modulus.v[0] & cond_add; + r1 += modinfo->modulus.v[1] & cond_add; + r2 += modinfo->modulus.v[2] & cond_add; + r3 += modinfo->modulus.v[3] & cond_add; + r4 += modinfo->modulus.v[4] & cond_add; + r5 += modinfo->modulus.v[5] & cond_add; + r6 += modinfo->modulus.v[6] & cond_add; + r7 += modinfo->modulus.v[7] & cond_add; + r8 += modinfo->modulus.v[8] & cond_add; + /* And propagate again. */ + r1 += r0 >> 30; r0 &= M30; + r2 += r1 >> 30; r1 &= M30; + r3 += r2 >> 30; r2 &= M30; + r4 += r3 >> 30; r3 &= M30; + r5 += r4 >> 30; r4 &= M30; + r6 += r5 >> 30; r5 &= M30; + r7 += r6 >> 30; r6 &= M30; + r8 += r7 >> 30; r7 &= M30; + + r->v[0] = r0; + r->v[1] = r1; + r->v[2] = r2; + r->v[3] = r3; + r->v[4] = r4; + r->v[5] = r5; + r->v[6] = r6; + r->v[7] = r7; + r->v[8] = r8; + + VERIFY_CHECK(r0 >> 30 == 0); + VERIFY_CHECK(r1 >> 30 == 0); + VERIFY_CHECK(r2 >> 30 == 0); + VERIFY_CHECK(r3 >> 30 == 0); + VERIFY_CHECK(r4 >> 30 == 0); + VERIFY_CHECK(r5 >> 30 == 0); + VERIFY_CHECK(r6 >> 30 == 0); + VERIFY_CHECK(r7 >> 30 == 0); + VERIFY_CHECK(r8 >> 30 == 0); + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 0) >= 0); /* r >= 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(r, 9, &modinfo->modulus, 1) < 0); /* r < modulus */ +} + +/* Data type for transition matrices (see section 3 of explanation). + * + * t = [ u v ] + * [ q r ] + */ +typedef struct { + int32_t u, v, q, r; +} secp256k1_modinv32_trans2x2; + +/* Compute the transition matrix and zeta for 30 divsteps. + * + * Input: zeta: initial zeta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final zeta + * + * Implements the divsteps_n_matrix function from the explanation. + */ +static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) { + /* u,v,q,r are the elements of the transformation matrix being built up, + * starting with the identity matrix. Semantically they are signed integers + * in range [-2^30,2^30], but here represented as unsigned mod 2^32. This + * permits left shifting (which is UB for negative numbers). The range + * being inside [-2^31,2^31) means that casting to signed works correctly. + */ + uint32_t u = 1, v = 0, q = 0, r = 1; + volatile uint32_t c1, c2; + uint32_t mask1, mask2, f = f0, g = g0, x, y, z; + int i; + + for (i = 0; i < 30; ++i) { + VERIFY_CHECK((f & 1) == 1); /* f must always be odd */ + VERIFY_CHECK((u * f0 + v * g0) == f << i); + VERIFY_CHECK((q * f0 + r * g0) == g << i); + /* Compute conditional masks for (zeta < 0) and for (g & 1). */ + c1 = zeta >> 31; + mask1 = c1; + c2 = g & 1; + mask2 = -c2; + /* Compute x,y,z, conditionally negated versions of f,u,v. */ + x = (f ^ mask1) - mask1; + y = (u ^ mask1) - mask1; + z = (v ^ mask1) - mask1; + /* Conditionally add x,y,z to g,q,r. */ + g += x & mask2; + q += y & mask2; + r += z & mask2; + /* In what follows, mask1 is a condition mask for (zeta < 0) and (g & 1). */ + mask1 &= mask2; + /* Conditionally change zeta into -zeta-2 or zeta-1. */ + zeta = (zeta ^ mask1) - 1; + /* Conditionally add g,q,r to f,u,v. */ + f += g & mask1; + u += q & mask1; + v += r & mask1; + /* Shifts */ + g >>= 1; + u <<= 1; + v <<= 1; + /* Bounds on zeta that follow from the bounds on iteration count (max 20*30 divsteps). */ + VERIFY_CHECK(zeta >= -601 && zeta <= 601); + } + /* Return data in t and return value. */ + t->u = (int32_t)u; + t->v = (int32_t)v; + t->q = (int32_t)q; + t->r = (int32_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2, the + * aggregate of 30 of them will have determinant 2^30. */ + VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30); + return zeta; +} + +/* secp256k1_modinv32_inv256[i] = -(2*i+1)^-1 (mod 256) */ +static const uint8_t secp256k1_modinv32_inv256[128] = { + 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, + 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, + 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, + 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, + 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, + 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, + 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, + 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, + 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, + 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, + 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 +}; + +/* Compute the transition matrix and eta for 30 divsteps (variable time). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final eta + * + * Implements the divsteps_n_matrix_var function from the explanation. + */ +static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) { + /* Transformation matrix; see comments in secp256k1_modinv32_divsteps_30. */ + uint32_t u = 1, v = 0, q = 0, r = 1; + uint32_t f = f0, g = g0, m; + uint16_t w; + int i = 30, limit, zeros; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* We're done once we've done 30 divsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i)); + /* Bounds on eta that follow from the bounds on iteration count (max 25*30 divsteps). */ + VERIFY_CHECK(eta >= -751 && eta <= 751); + /* If eta is negative, negate it and replace f,g with g,-f. */ + if (eta < 0) { + uint32_t tmp; + eta = -eta; + tmp = f; f = g; g = -tmp; + tmp = u; u = q; q = -tmp; + tmp = v; v = r; r = -tmp; + } + /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more + * than i can be cancelled out (as we'd be done before that point), and no more than eta+1 + * can be done as its sign will flip once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */ + VERIFY_CHECK(limit > 0 && limit <= 30); + m = (UINT32_MAX >> (32 - limit)) & 255U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; + /* Do so. */ + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int32_t)u; + t->v = (int32_t)v; + t->q = (int32_t)q; + t->r = (int32_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2, the + * aggregate of 30 of them will have determinant 2^30. */ + VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30); + return eta; +} + +/* Compute the transition matrix and eta for 30 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^32 rather than 2^30, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Input/Output: (*jacp & 1) is bitflipped if and only if the Jacobi symbol of (f | g) changes sign + * by applying the returned transformation matrix to it. The other bits of *jacp may + * change, but are meaningless. + * Return: final eta + */ +static int32_t secp256k1_modinv32_posdivsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t, int *jacp) { + /* Transformation matrix. */ + uint32_t u = 1, v = 0, q = 0, r = 1; + uint32_t f = f0, g = g0, m; + uint16_t w; + int i = 30, limit, zeros; + int jac = *jacp; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 30 posdivsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint32_t tmp; + eta = -eta; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + tmp = f; f = g; g = tmp; + tmp = u; u = q; q = tmp; + tmp = v; v = r; r = tmp; + } + /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more + * than i can be cancelled out (as we'd be done before that point), and no more than eta+1 + * can be done as its sign will flip once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */ + VERIFY_CHECK(limit > 0 && limit <= 30); + m = (UINT32_MAX >> (32 - limit)) & 255U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; + /* Do so. */ + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int32_t)u; + t->v = (int32_t)v; + t->q = (int32_t)q; + t->r = (int32_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2 or -2, + * the aggregate of 30 of them will have determinant 2^30 or -2^30. */ + VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30 || + (int64_t)t->u * t->r - (int64_t)t->v * t->q == -(((int64_t)1) << 30)); + *jacp = jac; + return eta; +} + +/* Compute (t/2^30) * [d, e] mod modulus, where t is a transition matrix for 30 divsteps. + * + * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range + * (-2^30,2^30). + * + * This implements the update_de function from the explanation. + */ +static void secp256k1_modinv32_update_de_30(secp256k1_modinv32_signed30 *d, secp256k1_modinv32_signed30 *e, const secp256k1_modinv32_trans2x2 *t, const secp256k1_modinv32_modinfo* modinfo) { + const int32_t M30 = (int32_t)(UINT32_MAX >> 2); + const int32_t u = t->u, v = t->v, q = t->q, r = t->r; + int32_t di, ei, md, me, sd, se; + int64_t cd, ce; + int i; + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */ + VERIFY_CHECK(labs(u) <= (M30 + 1 - labs(v))); /* |u|+|v| <= 2^30 */ + VERIFY_CHECK(labs(q) <= (M30 + 1 - labs(r))); /* |q|+|r| <= 2^30 */ + + /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */ + sd = d->v[8] >> 31; + se = e->v[8] >> 31; + md = (u & sd) + (v & se); + me = (q & sd) + (r & se); + /* Begin computing t*[d,e]. */ + di = d->v[0]; + ei = e->v[0]; + cd = (int64_t)u * di + (int64_t)v * ei; + ce = (int64_t)q * di + (int64_t)r * ei; + /* Correct md,me so that t*[d,e]+modulus*[md,me] has 30 zero bottom bits. */ + md -= (modinfo->modulus_inv30 * (uint32_t)cd + md) & M30; + me -= (modinfo->modulus_inv30 * (uint32_t)ce + me) & M30; + /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */ + cd += (int64_t)modinfo->modulus.v[0] * md; + ce += (int64_t)modinfo->modulus.v[0] * me; + /* Verify that the low 30 bits of the computation are indeed zero, and then throw them away. */ + VERIFY_CHECK(((int32_t)cd & M30) == 0); cd >>= 30; + VERIFY_CHECK(((int32_t)ce & M30) == 0); ce >>= 30; + /* Now iteratively compute limb i=1..8 of t*[d,e]+modulus*[md,me], and store them in output + * limb i-1 (shifting down by 30 bits). */ + for (i = 1; i < 9; ++i) { + di = d->v[i]; + ei = e->v[i]; + cd += (int64_t)u * di + (int64_t)v * ei; + ce += (int64_t)q * di + (int64_t)r * ei; + cd += (int64_t)modinfo->modulus.v[i] * md; + ce += (int64_t)modinfo->modulus.v[i] * me; + d->v[i - 1] = (int32_t)cd & M30; cd >>= 30; + e->v[i - 1] = (int32_t)ce & M30; ce >>= 30; + } + /* What remains is limb 9 of t*[d,e]+modulus*[md,me]; store it as output limb 8. */ + d->v[8] = (int32_t)cd; + e->v[8] = (int32_t)ce; + + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, -2) > 0); /* d > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(d, 9, &modinfo->modulus, 1) < 0); /* d < modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, -2) > 0); /* e > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(e, 9, &modinfo->modulus, 1) < 0); /* e < modulus */ +} + +/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps. + * + * This implements the update_fg function from the explanation. + */ +static void secp256k1_modinv32_update_fg_30(secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) { + const int32_t M30 = (int32_t)(UINT32_MAX >> 2); + const int32_t u = t->u, v = t->v, q = t->q, r = t->r; + int32_t fi, gi; + int64_t cf, cg; + int i; + /* Start computing t*[f,g]. */ + fi = f->v[0]; + gi = g->v[0]; + cf = (int64_t)u * fi + (int64_t)v * gi; + cg = (int64_t)q * fi + (int64_t)r * gi; + /* Verify that the bottom 30 bits of the result are zero, and then throw them away. */ + VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30; + VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30; + /* Now iteratively compute limb i=1..8 of t*[f,g], and store them in output limb i-1 (shifting + * down by 30 bits). */ + for (i = 1; i < 9; ++i) { + fi = f->v[i]; + gi = g->v[i]; + cf += (int64_t)u * fi + (int64_t)v * gi; + cg += (int64_t)q * fi + (int64_t)r * gi; + f->v[i - 1] = (int32_t)cf & M30; cf >>= 30; + g->v[i - 1] = (int32_t)cg & M30; cg >>= 30; + } + /* What remains is limb 9 of t*[f,g]; store it as output limb 8. */ + f->v[8] = (int32_t)cf; + g->v[8] = (int32_t)cg; +} + +/* Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps. + * + * Version that operates on a variable number of limbs in f and g. + * + * This implements the update_fg function from the explanation in modinv64_impl.h. + */ +static void secp256k1_modinv32_update_fg_30_var(int len, secp256k1_modinv32_signed30 *f, secp256k1_modinv32_signed30 *g, const secp256k1_modinv32_trans2x2 *t) { + const int32_t M30 = (int32_t)(UINT32_MAX >> 2); + const int32_t u = t->u, v = t->v, q = t->q, r = t->r; + int32_t fi, gi; + int64_t cf, cg; + int i; + VERIFY_CHECK(len > 0); + /* Start computing t*[f,g]. */ + fi = f->v[0]; + gi = g->v[0]; + cf = (int64_t)u * fi + (int64_t)v * gi; + cg = (int64_t)q * fi + (int64_t)r * gi; + /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */ + VERIFY_CHECK(((int32_t)cf & M30) == 0); cf >>= 30; + VERIFY_CHECK(((int32_t)cg & M30) == 0); cg >>= 30; + /* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting + * down by 30 bits). */ + for (i = 1; i < len; ++i) { + fi = f->v[i]; + gi = g->v[i]; + cf += (int64_t)u * fi + (int64_t)v * gi; + cg += (int64_t)q * fi + (int64_t)r * gi; + f->v[i - 1] = (int32_t)cf & M30; cf >>= 30; + g->v[i - 1] = (int32_t)cg & M30; cg >>= 30; + } + /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */ + f->v[len - 1] = (int32_t)cf; + g->v[len - 1] = (int32_t)cg; +} + +/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */ +static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { + /* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */ + secp256k1_modinv32_signed30 d = {{0}}; + secp256k1_modinv32_signed30 e = {{1}}; + secp256k1_modinv32_signed30 f = modinfo->modulus; + secp256k1_modinv32_signed30 g = *x; + int i; + int32_t zeta = -1; /* zeta = -(delta+1/2); delta is initially 1/2. */ + + /* Do 20 iterations of 30 divsteps each = 600 divsteps. 590 suffices for 256-bit inputs. */ + for (i = 0; i < 20; ++i) { + /* Compute transition matrix and new zeta after 30 divsteps. */ + secp256k1_modinv32_trans2x2 t; + zeta = secp256k1_modinv32_divsteps_30(zeta, f.v[0], g.v[0], &t); + /* Update d,e using that transition matrix. */ + secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo); + /* Update f,g using that transition matrix. */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */ + + secp256k1_modinv32_update_fg_30(&f, &g, &t); + + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) > 0); /* f > -modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, -1) > 0); /* g > -modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &modinfo->modulus, 1) < 0); /* g < modulus */ + } + + /* At this point sufficient iterations have been performed that g must have reached 0 + * and (if g was not originally 0) f must now equal +/- GCD of the initial f, g + * values i.e. +/- 1, and d now contains +/- the modular inverse. */ + + /* g == 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, 9, &SECP256K1_SIGNED30_ONE, 0) == 0); + /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, -1) == 0 || + secp256k1_modinv32_mul_cmp_30(&f, 9, &SECP256K1_SIGNED30_ONE, 1) == 0 || + (secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 && + secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 && + (secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, 1) == 0 || + secp256k1_modinv32_mul_cmp_30(&f, 9, &modinfo->modulus, -1) == 0))); + + /* Optionally negate d, normalize to [0,modulus), and return it. */ + secp256k1_modinv32_normalize_30(&d, f.v[8], modinfo); + *x = d; +} + +/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */ +static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { + /* Start with d=0, e=1, f=modulus, g=x, eta=-1. */ + secp256k1_modinv32_signed30 d = {{0, 0, 0, 0, 0, 0, 0, 0, 0}}; + secp256k1_modinv32_signed30 e = {{1, 0, 0, 0, 0, 0, 0, 0, 0}}; + secp256k1_modinv32_signed30 f = modinfo->modulus; + secp256k1_modinv32_signed30 g = *x; +#ifdef VERIFY + int i = 0; +#endif + int j, len = 9; + int32_t eta = -1; /* eta = -delta; delta is initially 1 (faster for the variable-time code) */ + int32_t cond, fn, gn; + + /* Do iterations of 30 divsteps each until g=0. */ + while (1) { + /* Compute transition matrix and new eta after 30 divsteps. */ + secp256k1_modinv32_trans2x2 t; + eta = secp256k1_modinv32_divsteps_30_var(eta, f.v[0], g.v[0], &t); + /* Update d,e using that transition matrix. */ + secp256k1_modinv32_update_de_30(&d, &e, &t, modinfo); + /* Update f,g using that transition matrix. */ + + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ + + secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t); + /* If the bottom limb of g is 0, there is a chance g=0. */ + if (g.v[0] == 0) { + cond = 0; + /* Check if all other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= g.v[j]; + } + /* If so, we're done. */ + if (cond == 0) break; + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int32_t)len - 2) >> 31; + cond |= fn ^ (fn >> 31); + cond |= gn ^ (gn >> 31); + /* If so, reduce length, propagating the sign of f and g's top limb into the one below. */ + if (cond == 0) { + f.v[len - 2] |= (uint32_t)fn << 30; + g.v[len - 2] |= (uint32_t)gn << 30; + --len; + } + + VERIFY_CHECK(++i < 25); /* We should never need more than 25*30 = 750 divsteps */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ + } + + /* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of + * the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */ + + /* g == 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &SECP256K1_SIGNED30_ONE, 0) == 0); + /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, -1) == 0 || + secp256k1_modinv32_mul_cmp_30(&f, len, &SECP256K1_SIGNED30_ONE, 1) == 0 || + (secp256k1_modinv32_mul_cmp_30(x, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 && + secp256k1_modinv32_mul_cmp_30(&d, 9, &SECP256K1_SIGNED30_ONE, 0) == 0 && + (secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) == 0 || + secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, -1) == 0))); + + /* Optionally negate d, normalize to [0,modulus), and return it. */ + secp256k1_modinv32_normalize_30(&d, f.v[len - 1], modinfo); + *x = d; +} + +/* Do up to 50 iterations of 30 posdivsteps (up to 1500 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (750, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY +#define JACOBI32_ITERATIONS 25 +#else +#define JACOBI32_ITERATIONS 50 +#endif + +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv32_signed30 f = modinfo->modulus; + secp256k1_modinv32_signed30 g = *x; + int j, len = 9; + int32_t eta = -1; /* eta = -delta; delta is initially 1 */ + int32_t cond, fn, gn; + int jac = 0; + int count; + + /* The input limbs must all be non-negative. */ + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0 && g.v[5] >= 0 && g.v[6] >= 0 && g.v[7] >= 0 && g.v[8] >= 0); + + /* If x > 0, then if the loop below converges, it converges to f=g=gcd(x,modulus). Since we + * require that gcd(x,modulus)=1 and modulus>=3, x cannot be 0. Thus, we must reach f=1 (or + * time out). */ + VERIFY_CHECK((g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4] | g.v[5] | g.v[6] | g.v[7] | g.v[8]) != 0); + + for (count = 0; count < JACOBI32_ITERATIONS; ++count) { + /* Compute transition matrix and new eta after 30 posdivsteps. */ + secp256k1_modinv32_trans2x2 t; + eta = secp256k1_modinv32_posdivsteps_30_var(eta, f.v[0] | ((uint32_t)f.v[1] << 30), g.v[0] | ((uint32_t)g.v[1] << 30), &t, &jac); + /* Update f,g using that transition matrix. */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ + + secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t); + /* If the bottom limb of f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. If f=1, the Jacobi symbol (g | f)=1. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int32_t)len - 2) >> 31; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; + + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ + } + + /* The loop failed to converge to f=g after 1500 iterations. Return 0, indicating unknown result. */ + return 0; +} + +#endif /* SECP256K1_MODINV32_IMPL_H */ diff --git a/src/secp256k1/src/modinv64.h b/src/secp256k1/src/modinv64.h new file mode 100644 index 000000000..f4208e6c2 --- /dev/null +++ b/src/secp256k1/src/modinv64.h @@ -0,0 +1,47 @@ +/*********************************************************************** + * Copyright (c) 2020 Peter Dettman * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODINV64_H +#define SECP256K1_MODINV64_H + +#include "util.h" + +#ifndef SECP256K1_WIDEMUL_INT128 +#error "modinv64 requires 128-bit wide multiplication support" +#endif + +/* A signed 62-bit limb representation of integers. + * + * Its value is sum(v[i] * 2^(62*i), i=0..4). */ +typedef struct { + int64_t v[5]; +} secp256k1_modinv64_signed62; + +typedef struct { + /* The modulus in signed62 notation, must be odd and in [3, 2^256]. */ + secp256k1_modinv64_signed62 modulus; + + /* modulus^{-1} mod 2^62 */ + uint64_t modulus_inv62; +} secp256k1_modinv64_modinfo; + +/* Replace x with its modular inverse mod modinfo->modulus. x must be in range [0, modulus). + * If x is zero, the result will be zero as well. If not, the inverse must exist (i.e., the gcd of + * x and modulus must be 1). These rules are automatically satisfied if the modulus is prime. + * + * On output, all of x's limbs will be in [0, 2^62). + */ +static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); + +/* Same as secp256k1_modinv64_var, but constant time in x (not in the modulus). */ +static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); + +/* Compute the Jacobi symbol for (x | modinfo->modulus). x must be coprime with modulus (and thus + * cannot be 0, as modulus >= 3). All limbs of x must be non-negative. Returns 0 if the result + * cannot be computed. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); + +#endif /* SECP256K1_MODINV64_H */ diff --git a/src/secp256k1/src/modinv64_impl.h b/src/secp256k1/src/modinv64_impl.h new file mode 100644 index 000000000..0dc1e8069 --- /dev/null +++ b/src/secp256k1/src/modinv64_impl.h @@ -0,0 +1,782 @@ +/*********************************************************************** + * Copyright (c) 2020 Peter Dettman * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_MODINV64_IMPL_H +#define SECP256K1_MODINV64_IMPL_H + +#include "int128.h" +#include "modinv64.h" + +/* This file implements modular inversion based on the paper "Fast constant-time gcd computation and + * modular inversion" by Daniel J. Bernstein and Bo-Yin Yang. + * + * For an explanation of the algorithm, see doc/safegcd_implementation.md. This file contains an + * implementation for N=62, using 62-bit signed limbs represented as int64_t. + */ + +/* Data type for transition matrices (see section 3 of explanation). + * + * t = [ u v ] + * [ q r ] + */ +typedef struct { + int64_t u, v, q, r; +} secp256k1_modinv64_trans2x2; + +#ifdef VERIFY +/* Helper function to compute the absolute value of an int64_t. + * (we don't use abs/labs/llabs as it depends on the int sizes). */ +static int64_t secp256k1_modinv64_abs(int64_t v) { + VERIFY_CHECK(v > INT64_MIN); + if (v < 0) return -v; + return v; +} + +static const secp256k1_modinv64_signed62 SECP256K1_SIGNED62_ONE = {{1}}; + +/* Compute a*factor and put it in r. All but the top limb in r will be in range [0,2^62). */ +static void secp256k1_modinv64_mul_62(secp256k1_modinv64_signed62 *r, const secp256k1_modinv64_signed62 *a, int alen, int64_t factor) { + const uint64_t M62 = UINT64_MAX >> 2; + secp256k1_int128 c, d; + int i; + secp256k1_i128_from_i64(&c, 0); + for (i = 0; i < 4; ++i) { + if (i < alen) secp256k1_i128_accum_mul(&c, a->v[i], factor); + r->v[i] = secp256k1_i128_to_u64(&c) & M62; secp256k1_i128_rshift(&c, 62); + } + if (4 < alen) secp256k1_i128_accum_mul(&c, a->v[4], factor); + secp256k1_i128_from_i64(&d, secp256k1_i128_to_i64(&c)); + VERIFY_CHECK(secp256k1_i128_eq_var(&c, &d)); + r->v[4] = secp256k1_i128_to_i64(&c); +} + +/* Return -1 for ab*factor. A has alen limbs; b has 5. */ +static int secp256k1_modinv64_mul_cmp_62(const secp256k1_modinv64_signed62 *a, int alen, const secp256k1_modinv64_signed62 *b, int64_t factor) { + int i; + secp256k1_modinv64_signed62 am, bm; + secp256k1_modinv64_mul_62(&am, a, alen, 1); /* Normalize all but the top limb of a. */ + secp256k1_modinv64_mul_62(&bm, b, 5, factor); + for (i = 0; i < 4; ++i) { + /* Verify that all but the top limb of a and b are normalized. */ + VERIFY_CHECK(am.v[i] >> 62 == 0); + VERIFY_CHECK(bm.v[i] >> 62 == 0); + } + for (i = 4; i >= 0; --i) { + if (am.v[i] < bm.v[i]) return -1; + if (am.v[i] > bm.v[i]) return 1; + } + return 0; +} + +/* Check if the determinant of t is equal to 1 << n. If abs, check if |det t| == 1 << n. */ +static int secp256k1_modinv64_det_check_pow2(const secp256k1_modinv64_trans2x2 *t, unsigned int n, int abs) { + secp256k1_int128 a; + secp256k1_i128_det(&a, t->u, t->v, t->q, t->r); + if (secp256k1_i128_check_pow2(&a, n, 1)) return 1; + if (abs && secp256k1_i128_check_pow2(&a, n, -1)) return 1; + return 0; +} +#endif + +/* Take as input a signed62 number in range (-2*modulus,modulus), and add a multiple of the modulus + * to it to bring it to range [0,modulus). If sign < 0, the input will also be negated in the + * process. The input must have limbs in range (-2^62,2^62). The output will have limbs in range + * [0,2^62). */ +static void secp256k1_modinv64_normalize_62(secp256k1_modinv64_signed62 *r, int64_t sign, const secp256k1_modinv64_modinfo *modinfo) { + const int64_t M62 = (int64_t)(UINT64_MAX >> 2); + int64_t r0 = r->v[0], r1 = r->v[1], r2 = r->v[2], r3 = r->v[3], r4 = r->v[4]; + volatile int64_t cond_add, cond_negate; + +#ifdef VERIFY + /* Verify that all limbs are in range (-2^62,2^62). */ + int i; + for (i = 0; i < 5; ++i) { + VERIFY_CHECK(r->v[i] >= -M62); + VERIFY_CHECK(r->v[i] <= M62); + } + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, -2) > 0); /* r > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */ +#endif + + /* In a first step, add the modulus if the input is negative, and then negate if requested. + * This brings r from range (-2*modulus,modulus) to range (-modulus,modulus). As all input + * limbs are in range (-2^62,2^62), this cannot overflow an int64_t. Note that the right + * shifts below are signed sign-extending shifts (see assumptions.h for tests that that is + * indeed the behavior of the right shift operator). */ + cond_add = r4 >> 63; + r0 += modinfo->modulus.v[0] & cond_add; + r1 += modinfo->modulus.v[1] & cond_add; + r2 += modinfo->modulus.v[2] & cond_add; + r3 += modinfo->modulus.v[3] & cond_add; + r4 += modinfo->modulus.v[4] & cond_add; + cond_negate = sign >> 63; + r0 = (r0 ^ cond_negate) - cond_negate; + r1 = (r1 ^ cond_negate) - cond_negate; + r2 = (r2 ^ cond_negate) - cond_negate; + r3 = (r3 ^ cond_negate) - cond_negate; + r4 = (r4 ^ cond_negate) - cond_negate; + /* Propagate the top bits, to bring limbs back to range (-2^62,2^62). */ + r1 += r0 >> 62; r0 &= M62; + r2 += r1 >> 62; r1 &= M62; + r3 += r2 >> 62; r2 &= M62; + r4 += r3 >> 62; r3 &= M62; + + /* In a second step add the modulus again if the result is still negative, bringing + * r to range [0,modulus). */ + cond_add = r4 >> 63; + r0 += modinfo->modulus.v[0] & cond_add; + r1 += modinfo->modulus.v[1] & cond_add; + r2 += modinfo->modulus.v[2] & cond_add; + r3 += modinfo->modulus.v[3] & cond_add; + r4 += modinfo->modulus.v[4] & cond_add; + /* And propagate again. */ + r1 += r0 >> 62; r0 &= M62; + r2 += r1 >> 62; r1 &= M62; + r3 += r2 >> 62; r2 &= M62; + r4 += r3 >> 62; r3 &= M62; + + r->v[0] = r0; + r->v[1] = r1; + r->v[2] = r2; + r->v[3] = r3; + r->v[4] = r4; + + VERIFY_CHECK(r0 >> 62 == 0); + VERIFY_CHECK(r1 >> 62 == 0); + VERIFY_CHECK(r2 >> 62 == 0); + VERIFY_CHECK(r3 >> 62 == 0); + VERIFY_CHECK(r4 >> 62 == 0); + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 0) >= 0); /* r >= 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(r, 5, &modinfo->modulus, 1) < 0); /* r < modulus */ +} + +/* Compute the transition matrix and eta for 59 divsteps (where zeta=-(delta+1/2)). + * Note that the transformation matrix is scaled by 2^62 and not 2^59. + * + * Input: zeta: initial zeta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final zeta + * + * Implements the divsteps_n_matrix function from the explanation. + */ +static int64_t secp256k1_modinv64_divsteps_59(int64_t zeta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) { + /* u,v,q,r are the elements of the transformation matrix being built up, + * starting with the identity matrix times 8 (because the caller expects + * a result scaled by 2^62). Semantically they are signed integers + * in range [-2^62,2^62], but here represented as unsigned mod 2^64. This + * permits left shifting (which is UB for negative numbers). The range + * being inside [-2^63,2^63) means that casting to signed works correctly. + */ + uint64_t u = 8, v = 0, q = 0, r = 8; + volatile uint64_t c1, c2; + uint64_t mask1, mask2, f = f0, g = g0, x, y, z; + int i; + + for (i = 3; i < 62; ++i) { + VERIFY_CHECK((f & 1) == 1); /* f must always be odd */ + VERIFY_CHECK((u * f0 + v * g0) == f << i); + VERIFY_CHECK((q * f0 + r * g0) == g << i); + /* Compute conditional masks for (zeta < 0) and for (g & 1). */ + c1 = zeta >> 63; + mask1 = c1; + c2 = g & 1; + mask2 = -c2; + /* Compute x,y,z, conditionally negated versions of f,u,v. */ + x = (f ^ mask1) - mask1; + y = (u ^ mask1) - mask1; + z = (v ^ mask1) - mask1; + /* Conditionally add x,y,z to g,q,r. */ + g += x & mask2; + q += y & mask2; + r += z & mask2; + /* In what follows, c1 is a condition mask for (zeta < 0) and (g & 1). */ + mask1 &= mask2; + /* Conditionally change zeta into -zeta-2 or zeta-1. */ + zeta = (zeta ^ mask1) - 1; + /* Conditionally add g,q,r to f,u,v. */ + f += g & mask1; + u += q & mask1; + v += r & mask1; + /* Shifts */ + g >>= 1; + u <<= 1; + v <<= 1; + /* Bounds on zeta that follow from the bounds on iteration count (max 10*59 divsteps). */ + VERIFY_CHECK(zeta >= -591 && zeta <= 591); + } + /* Return data in t and return value. */ + t->u = (int64_t)u; + t->v = (int64_t)v; + t->q = (int64_t)q; + t->r = (int64_t)r; + + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2, the + * aggregate of 59 of them will have determinant 2^59. Multiplying with the initial + * 8*identity (which has determinant 2^6) means the overall outputs has determinant + * 2^65. */ + VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 65, 0)); + + return zeta; +} + +/* Compute the transition matrix and eta for 62 divsteps (variable time, eta=-delta). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final eta + * + * Implements the divsteps_n_matrix_var function from the explanation. + */ +static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t) { + /* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */ + uint64_t u = 1, v = 0, q = 0, r = 1; + uint64_t f = f0, g = g0, m; + uint32_t w; + int i = 62, limit, zeros; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* We're done once we've done 62 divsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i)); + /* Bounds on eta that follow from the bounds on iteration count (max 12*62 divsteps). */ + VERIFY_CHECK(eta >= -745 && eta <= 745); + /* If eta is negative, negate it and replace f,g with g,-f. */ + if (eta < 0) { + uint64_t tmp; + eta = -eta; + tmp = f; f = g; g = -tmp; + tmp = u; u = q; q = -tmp; + tmp = v; v = r; r = -tmp; + /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled + * out (as we'd be done before that point), and no more than eta+1 can be done as its + * sign will flip again once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 6) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 63U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 6) + * bits. */ + w = (f * g * (f * f - 2)) & m; + } else { + /* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as + * eta tends to be smaller here. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 4) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 15U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 4) + * bits. */ + w = f + (((f + 1) & 4) << 1); + w = (-w * g) & m; + } + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int64_t)u; + t->v = (int64_t)v; + t->q = (int64_t)q; + t->r = (int64_t)r; + + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2, the + * aggregate of 62 of them will have determinant 2^62. */ + VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 62, 0)); + + return eta; +} + +/* Compute the transition matrix and eta for 62 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^64 rather than 2^62, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Input/Output: (*jacp & 1) is bitflipped if and only if the Jacobi symbol of (f | g) changes sign + * by applying the returned transformation matrix to it. The other bits of *jacp may + * change, but are meaningless. + * Return: final eta + */ +static int64_t secp256k1_modinv64_posdivsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t, int *jacp) { + /* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */ + uint64_t u = 1, v = 0, q = 0, r = 1; + uint64_t f = f0, g = g0, m; + uint32_t w; + int i = 62, limit, zeros; + int jac = *jacp; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 62 posdivsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint64_t tmp; + eta = -eta; + tmp = f; f = g; g = tmp; + tmp = u; u = q; q = tmp; + tmp = v; v = r; r = tmp; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled + * out (as we'd be done before that point), and no more than eta+1 can be done as its + * sign will flip again once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 6) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 63U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 6) + * bits. */ + w = (f * g * (f * f - 2)) & m; + } else { + /* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as + * eta tends to be smaller here. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 4) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 15U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 4) + * bits. */ + w = f + (((f + 1) & 4) << 1); + w = (-w * g) & m; + } + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int64_t)u; + t->v = (int64_t)v; + t->q = (int64_t)q; + t->r = (int64_t)r; + + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2 or -2, + * the aggregate of 62 of them will have determinant 2^62 or -2^62. */ + VERIFY_CHECK(secp256k1_modinv64_det_check_pow2(t, 62, 1)); + + *jacp = jac; + return eta; +} + +/* Compute (t/2^62) * [d, e] mod modulus, where t is a transition matrix scaled by 2^62. + * + * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range + * (-2^62,2^62). + * + * This implements the update_de function from the explanation. + */ +static void secp256k1_modinv64_update_de_62(secp256k1_modinv64_signed62 *d, secp256k1_modinv64_signed62 *e, const secp256k1_modinv64_trans2x2 *t, const secp256k1_modinv64_modinfo* modinfo) { + const uint64_t M62 = UINT64_MAX >> 2; + const int64_t d0 = d->v[0], d1 = d->v[1], d2 = d->v[2], d3 = d->v[3], d4 = d->v[4]; + const int64_t e0 = e->v[0], e1 = e->v[1], e2 = e->v[2], e3 = e->v[3], e4 = e->v[4]; + const int64_t u = t->u, v = t->v, q = t->q, r = t->r; + int64_t md, me, sd, se; + secp256k1_int128 cd, ce; + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */ + VERIFY_CHECK(secp256k1_modinv64_abs(u) <= (((int64_t)1 << 62) - secp256k1_modinv64_abs(v))); /* |u|+|v| <= 2^62 */ + VERIFY_CHECK(secp256k1_modinv64_abs(q) <= (((int64_t)1 << 62) - secp256k1_modinv64_abs(r))); /* |q|+|r| <= 2^62 */ + + /* [md,me] start as zero; plus [u,q] if d is negative; plus [v,r] if e is negative. */ + sd = d4 >> 63; + se = e4 >> 63; + md = (u & sd) + (v & se); + me = (q & sd) + (r & se); + /* Begin computing t*[d,e]. */ + secp256k1_i128_mul(&cd, u, d0); + secp256k1_i128_accum_mul(&cd, v, e0); + secp256k1_i128_mul(&ce, q, d0); + secp256k1_i128_accum_mul(&ce, r, e0); + /* Correct md,me so that t*[d,e]+modulus*[md,me] has 62 zero bottom bits. */ + md -= (modinfo->modulus_inv62 * secp256k1_i128_to_u64(&cd) + md) & M62; + me -= (modinfo->modulus_inv62 * secp256k1_i128_to_u64(&ce) + me) & M62; + /* Update the beginning of computation for t*[d,e]+modulus*[md,me] now md,me are known. */ + secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[0], md); + secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[0], me); + /* Verify that the low 62 bits of the computation are indeed zero, and then throw them away. */ + VERIFY_CHECK((secp256k1_i128_to_u64(&cd) & M62) == 0); secp256k1_i128_rshift(&cd, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&ce) & M62) == 0); secp256k1_i128_rshift(&ce, 62); + /* Compute limb 1 of t*[d,e]+modulus*[md,me], and store it as output limb 0 (= down shift). */ + secp256k1_i128_accum_mul(&cd, u, d1); + secp256k1_i128_accum_mul(&cd, v, e1); + secp256k1_i128_accum_mul(&ce, q, d1); + secp256k1_i128_accum_mul(&ce, r, e1); + if (modinfo->modulus.v[1]) { /* Optimize for the case where limb of modulus is zero. */ + secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[1], md); + secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[1], me); + } + d->v[0] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); + e->v[0] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); + /* Compute limb 2 of t*[d,e]+modulus*[md,me], and store it as output limb 1. */ + secp256k1_i128_accum_mul(&cd, u, d2); + secp256k1_i128_accum_mul(&cd, v, e2); + secp256k1_i128_accum_mul(&ce, q, d2); + secp256k1_i128_accum_mul(&ce, r, e2); + if (modinfo->modulus.v[2]) { /* Optimize for the case where limb of modulus is zero. */ + secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[2], md); + secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[2], me); + } + d->v[1] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); + e->v[1] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); + /* Compute limb 3 of t*[d,e]+modulus*[md,me], and store it as output limb 2. */ + secp256k1_i128_accum_mul(&cd, u, d3); + secp256k1_i128_accum_mul(&cd, v, e3); + secp256k1_i128_accum_mul(&ce, q, d3); + secp256k1_i128_accum_mul(&ce, r, e3); + if (modinfo->modulus.v[3]) { /* Optimize for the case where limb of modulus is zero. */ + secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[3], md); + secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[3], me); + } + d->v[2] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); + e->v[2] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); + /* Compute limb 4 of t*[d,e]+modulus*[md,me], and store it as output limb 3. */ + secp256k1_i128_accum_mul(&cd, u, d4); + secp256k1_i128_accum_mul(&cd, v, e4); + secp256k1_i128_accum_mul(&ce, q, d4); + secp256k1_i128_accum_mul(&ce, r, e4); + secp256k1_i128_accum_mul(&cd, modinfo->modulus.v[4], md); + secp256k1_i128_accum_mul(&ce, modinfo->modulus.v[4], me); + d->v[3] = secp256k1_i128_to_u64(&cd) & M62; secp256k1_i128_rshift(&cd, 62); + e->v[3] = secp256k1_i128_to_u64(&ce) & M62; secp256k1_i128_rshift(&ce, 62); + /* What remains is limb 5 of t*[d,e]+modulus*[md,me]; store it as output limb 4. */ + d->v[4] = secp256k1_i128_to_i64(&cd); + e->v[4] = secp256k1_i128_to_i64(&ce); + + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, -2) > 0); /* d > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(d, 5, &modinfo->modulus, 1) < 0); /* d < modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, -2) > 0); /* e > -2*modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(e, 5, &modinfo->modulus, 1) < 0); /* e < modulus */ +} + +/* Compute (t/2^62) * [f, g], where t is a transition matrix scaled by 2^62. + * + * This implements the update_fg function from the explanation. + */ +static void secp256k1_modinv64_update_fg_62(secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) { + const uint64_t M62 = UINT64_MAX >> 2; + const int64_t f0 = f->v[0], f1 = f->v[1], f2 = f->v[2], f3 = f->v[3], f4 = f->v[4]; + const int64_t g0 = g->v[0], g1 = g->v[1], g2 = g->v[2], g3 = g->v[3], g4 = g->v[4]; + const int64_t u = t->u, v = t->v, q = t->q, r = t->r; + secp256k1_int128 cf, cg; + /* Start computing t*[f,g]. */ + secp256k1_i128_mul(&cf, u, f0); + secp256k1_i128_accum_mul(&cf, v, g0); + secp256k1_i128_mul(&cg, q, f0); + secp256k1_i128_accum_mul(&cg, r, g0); + /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */ + VERIFY_CHECK((secp256k1_i128_to_u64(&cf) & M62) == 0); secp256k1_i128_rshift(&cf, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&cg) & M62) == 0); secp256k1_i128_rshift(&cg, 62); + /* Compute limb 1 of t*[f,g], and store it as output limb 0 (= down shift). */ + secp256k1_i128_accum_mul(&cf, u, f1); + secp256k1_i128_accum_mul(&cf, v, g1); + secp256k1_i128_accum_mul(&cg, q, f1); + secp256k1_i128_accum_mul(&cg, r, g1); + f->v[0] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[0] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + /* Compute limb 2 of t*[f,g], and store it as output limb 1. */ + secp256k1_i128_accum_mul(&cf, u, f2); + secp256k1_i128_accum_mul(&cf, v, g2); + secp256k1_i128_accum_mul(&cg, q, f2); + secp256k1_i128_accum_mul(&cg, r, g2); + f->v[1] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[1] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + /* Compute limb 3 of t*[f,g], and store it as output limb 2. */ + secp256k1_i128_accum_mul(&cf, u, f3); + secp256k1_i128_accum_mul(&cf, v, g3); + secp256k1_i128_accum_mul(&cg, q, f3); + secp256k1_i128_accum_mul(&cg, r, g3); + f->v[2] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[2] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + /* Compute limb 4 of t*[f,g], and store it as output limb 3. */ + secp256k1_i128_accum_mul(&cf, u, f4); + secp256k1_i128_accum_mul(&cf, v, g4); + secp256k1_i128_accum_mul(&cg, q, f4); + secp256k1_i128_accum_mul(&cg, r, g4); + f->v[3] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[3] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + /* What remains is limb 5 of t*[f,g]; store it as output limb 4. */ + f->v[4] = secp256k1_i128_to_i64(&cf); + g->v[4] = secp256k1_i128_to_i64(&cg); +} + +/* Compute (t/2^62) * [f, g], where t is a transition matrix for 62 divsteps. + * + * Version that operates on a variable number of limbs in f and g. + * + * This implements the update_fg function from the explanation. + */ +static void secp256k1_modinv64_update_fg_62_var(int len, secp256k1_modinv64_signed62 *f, secp256k1_modinv64_signed62 *g, const secp256k1_modinv64_trans2x2 *t) { + const uint64_t M62 = UINT64_MAX >> 2; + const int64_t u = t->u, v = t->v, q = t->q, r = t->r; + int64_t fi, gi; + secp256k1_int128 cf, cg; + int i; + VERIFY_CHECK(len > 0); + /* Start computing t*[f,g]. */ + fi = f->v[0]; + gi = g->v[0]; + secp256k1_i128_mul(&cf, u, fi); + secp256k1_i128_accum_mul(&cf, v, gi); + secp256k1_i128_mul(&cg, q, fi); + secp256k1_i128_accum_mul(&cg, r, gi); + /* Verify that the bottom 62 bits of the result are zero, and then throw them away. */ + VERIFY_CHECK((secp256k1_i128_to_u64(&cf) & M62) == 0); secp256k1_i128_rshift(&cf, 62); + VERIFY_CHECK((secp256k1_i128_to_u64(&cg) & M62) == 0); secp256k1_i128_rshift(&cg, 62); + /* Now iteratively compute limb i=1..len of t*[f,g], and store them in output limb i-1 (shifting + * down by 62 bits). */ + for (i = 1; i < len; ++i) { + fi = f->v[i]; + gi = g->v[i]; + secp256k1_i128_accum_mul(&cf, u, fi); + secp256k1_i128_accum_mul(&cf, v, gi); + secp256k1_i128_accum_mul(&cg, q, fi); + secp256k1_i128_accum_mul(&cg, r, gi); + f->v[i - 1] = secp256k1_i128_to_u64(&cf) & M62; secp256k1_i128_rshift(&cf, 62); + g->v[i - 1] = secp256k1_i128_to_u64(&cg) & M62; secp256k1_i128_rshift(&cg, 62); + } + /* What remains is limb (len) of t*[f,g]; store it as output limb (len-1). */ + f->v[len - 1] = secp256k1_i128_to_i64(&cf); + g->v[len - 1] = secp256k1_i128_to_i64(&cg); +} + +/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (constant time in x). */ +static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { + /* Start with d=0, e=1, f=modulus, g=x, zeta=-1. */ + secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}}; + secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}}; + secp256k1_modinv64_signed62 f = modinfo->modulus; + secp256k1_modinv64_signed62 g = *x; + int i; + int64_t zeta = -1; /* zeta = -(delta+1/2); delta starts at 1/2. */ + + /* Do 10 iterations of 59 divsteps each = 590 divsteps. This suffices for 256-bit inputs. */ + for (i = 0; i < 10; ++i) { + /* Compute transition matrix and new zeta after 59 divsteps. */ + secp256k1_modinv64_trans2x2 t; + zeta = secp256k1_modinv64_divsteps_59(zeta, f.v[0], g.v[0], &t); + /* Update d,e using that transition matrix. */ + secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo); + /* Update f,g using that transition matrix. */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */ + + secp256k1_modinv64_update_fg_62(&f, &g, &t); + + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) > 0); /* f > -modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, -1) > 0); /* g > -modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &modinfo->modulus, 1) < 0); /* g < modulus */ + } + + /* At this point sufficient iterations have been performed that g must have reached 0 + * and (if g was not originally 0) f must now equal +/- GCD of the initial f, g + * values i.e. +/- 1, and d now contains +/- the modular inverse. */ + + /* g == 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, 5, &SECP256K1_SIGNED62_ONE, 0) == 0); + /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, -1) == 0 || + secp256k1_modinv64_mul_cmp_62(&f, 5, &SECP256K1_SIGNED62_ONE, 1) == 0 || + (secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 && + secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 && + (secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, 1) == 0 || + secp256k1_modinv64_mul_cmp_62(&f, 5, &modinfo->modulus, -1) == 0))); + + /* Optionally negate d, normalize to [0,modulus), and return it. */ + secp256k1_modinv64_normalize_62(&d, f.v[4], modinfo); + *x = d; +} + +/* Compute the inverse of x modulo modinfo->modulus, and replace x with it (variable time). */ +static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { + /* Start with d=0, e=1, f=modulus, g=x, eta=-1. */ + secp256k1_modinv64_signed62 d = {{0, 0, 0, 0, 0}}; + secp256k1_modinv64_signed62 e = {{1, 0, 0, 0, 0}}; + secp256k1_modinv64_signed62 f = modinfo->modulus; + secp256k1_modinv64_signed62 g = *x; +#ifdef VERIFY + int i = 0; +#endif + int j, len = 5; + int64_t eta = -1; /* eta = -delta; delta is initially 1 */ + int64_t cond, fn, gn; + + /* Do iterations of 62 divsteps each until g=0. */ + while (1) { + /* Compute transition matrix and new eta after 62 divsteps. */ + secp256k1_modinv64_trans2x2 t; + eta = secp256k1_modinv64_divsteps_62_var(eta, f.v[0], g.v[0], &t); + /* Update d,e using that transition matrix. */ + secp256k1_modinv64_update_de_62(&d, &e, &t, modinfo); + /* Update f,g using that transition matrix. */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ + + secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t); + /* If the bottom limb of g is zero, there is a chance that g=0. */ + if (g.v[0] == 0) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= g.v[j]; + } + /* If so, we're done. */ + if (cond == 0) break; + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0 or -1. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int64_t)len - 2) >> 63; + cond |= fn ^ (fn >> 63); + cond |= gn ^ (gn >> 63); + /* If so, reduce length, propagating the sign of f and g's top limb into the one below. */ + if (cond == 0) { + f.v[len - 2] |= (uint64_t)fn << 62; + g.v[len - 2] |= (uint64_t)gn << 62; + --len; + } + + VERIFY_CHECK(++i < 12); /* We should never need more than 12*62 = 744 divsteps */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) > 0); /* f > -modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, -1) > 0); /* g > -modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ + } + + /* At this point g is 0 and (if g was not originally 0) f must now equal +/- GCD of + * the initial f, g values i.e. +/- 1, and d now contains +/- the modular inverse. */ + + /* g == 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &SECP256K1_SIGNED62_ONE, 0) == 0); + /* |f| == 1, or (x == 0 and d == 0 and |f|=modulus) */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, -1) == 0 || + secp256k1_modinv64_mul_cmp_62(&f, len, &SECP256K1_SIGNED62_ONE, 1) == 0 || + (secp256k1_modinv64_mul_cmp_62(x, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 && + secp256k1_modinv64_mul_cmp_62(&d, 5, &SECP256K1_SIGNED62_ONE, 0) == 0 && + (secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) == 0 || + secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, -1) == 0))); + + /* Optionally negate d, normalize to [0,modulus), and return it. */ + secp256k1_modinv64_normalize_62(&d, f.v[len - 1], modinfo); + *x = d; +} + +/* Do up to 25 iterations of 62 posdivsteps (up to 1550 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (744, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY +#define JACOBI64_ITERATIONS 12 +#else +#define JACOBI64_ITERATIONS 25 +#endif + +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv64_signed62 f = modinfo->modulus; + secp256k1_modinv64_signed62 g = *x; + int j, len = 5; + int64_t eta = -1; /* eta = -delta; delta is initially 1 */ + int64_t cond, fn, gn; + int jac = 0; + int count; + + /* The input limbs must all be non-negative. */ + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0); + + /* If x > 0, then if the loop below converges, it converges to f=g=gcd(x,modulus). Since we + * require that gcd(x,modulus)=1 and modulus>=3, x cannot be 0. Thus, we must reach f=1 (or + * time out). */ + VERIFY_CHECK((g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4]) != 0); + + for (count = 0; count < JACOBI64_ITERATIONS; ++count) { + /* Compute transition matrix and new eta after 62 posdivsteps. */ + secp256k1_modinv64_trans2x2 t; + eta = secp256k1_modinv64_posdivsteps_62_var(eta, f.v[0] | ((uint64_t)f.v[1] << 62), g.v[0] | ((uint64_t)g.v[1] << 62), &t, &jac); + /* Update f,g using that transition matrix. */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ + + secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t); + /* If the bottom limb of f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. When f=1, the Jacobi symbol (g | f)=1. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int64_t)len - 2) >> 63; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; + + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ + } + + /* The loop failed to converge to f=g after 1550 iterations. Return 0, indicating unknown result. */ + return 0; +} + +#endif /* SECP256K1_MODINV64_IMPL_H */ diff --git a/src/secp256k1/src/modules/ecdh/Makefile.am.include b/src/secp256k1/src/modules/ecdh/Makefile.am.include index e3088b469..7f7f95f1f 100644 --- a/src/secp256k1/src/modules/ecdh/Makefile.am.include +++ b/src/secp256k1/src/modules/ecdh/Makefile.am.include @@ -1,8 +1,4 @@ include_HEADERS += include/secp256k1_ecdh.h noinst_HEADERS += src/modules/ecdh/main_impl.h noinst_HEADERS += src/modules/ecdh/tests_impl.h -if USE_BENCHMARK -noinst_PROGRAMS += bench_ecdh -bench_ecdh_SOURCES = src/bench_ecdh.c -bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) -endif +noinst_HEADERS += src/modules/ecdh/bench_impl.h diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/modules/ecdh/bench_impl.h similarity index 57% rename from src/secp256k1/src/bench_ecdh.c rename to src/secp256k1/src/modules/ecdh/bench_impl.h index 74cc66a39..c23aaa94d 100644 --- a/src/secp256k1/src/bench_ecdh.c +++ b/src/secp256k1/src/modules/ecdh/bench_impl.h @@ -1,25 +1,23 @@ -/************************************************************************ - * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ -#include +#ifndef SECP256K1_MODULE_ECDH_BENCH_H +#define SECP256K1_MODULE_ECDH_BENCH_H -#include "include/secp256k1.h" -#include "include/secp256k1_ecdh.h" -#include "util.h" -#include "bench.h" +#include "../../../include/secp256k1_ecdh.h" typedef struct { secp256k1_context *ctx; secp256k1_pubkey point; unsigned char scalar[32]; -} bench_ecdh; +} bench_ecdh_data; static void bench_ecdh_setup(void* arg) { int i; - bench_ecdh *data = (bench_ecdh*)arg; + bench_ecdh_data *data = (bench_ecdh_data*)arg; const unsigned char point[] = { 0x03, 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, @@ -28,27 +26,32 @@ static void bench_ecdh_setup(void* arg) { 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f }; - /* create a context with no capabilities */ - data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); for (i = 0; i < 32; i++) { data->scalar[i] = i + 1; } CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1); } -static void bench_ecdh(void* arg) { +static void bench_ecdh(void* arg, int iters) { int i; unsigned char res[32]; - bench_ecdh *data = (bench_ecdh*)arg; + bench_ecdh_data *data = (bench_ecdh_data*)arg; - for (i = 0; i < 20000; i++) { - CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1); + for (i = 0; i < iters; i++) { + CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL, NULL) == 1); } } -int main(void) { - bench_ecdh data; +static void run_ecdh_bench(int iters, int argc, char** argv) { + bench_ecdh_data data; + int d = argc == 1; + + /* create a context with no capabilities */ + data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); - run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000); - return 0; + if (d || have_flag(argc, argv, "ecdh")) run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); } + +#endif /* SECP256K1_MODULE_ECDH_BENCH_H */ diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h index 1c7a54cd6..82b082a9f 100644 --- a/src/secp256k1/src/modules/ecdh/main_impl.h +++ b/src/secp256k1/src/modules/ecdh/main_impl.h @@ -1,54 +1,71 @@ -/************************************************************************ - * Copyright (c) 2015 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_MODULE_ECDH_MAIN_H #define SECP256K1_MODULE_ECDH_MAIN_H -#include "include/secp256k1_ecdh.h" -#include "ecmult_const_impl.h" +#include "../../../include/secp256k1_ecdh.h" +#include "../../ecmult_const_impl.h" -int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) { +static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) { + unsigned char version = (y32[31] & 0x01) | 0x02; + secp256k1_sha256 sha; + (void)data; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &version, 1); + secp256k1_sha256_write(&sha, x32, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + +const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256 = ecdh_hash_function_sha256; +const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default = ecdh_hash_function_sha256; + +int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pubkey *point, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) { int ret = 0; int overflow = 0; secp256k1_gej res; secp256k1_ge pt; secp256k1_scalar s; + unsigned char x[32]; + unsigned char y[32]; + VERIFY_CHECK(ctx != NULL); - ARG_CHECK(result != NULL); + ARG_CHECK(output != NULL); ARG_CHECK(point != NULL); ARG_CHECK(scalar != NULL); + if (hashfp == NULL) { + hashfp = secp256k1_ecdh_hash_function_default; + } + secp256k1_pubkey_load(ctx, &pt, point); secp256k1_scalar_set_b32(&s, scalar, &overflow); - if (overflow || secp256k1_scalar_is_zero(&s)) { - ret = 0; - } else { - unsigned char x[32]; - unsigned char y[1]; - secp256k1_sha256 sha; - - secp256k1_ecmult_const(&res, &pt, &s); - secp256k1_ge_set_gej(&pt, &res); - /* Compute a hash of the point in compressed form - * Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not - * expect its output to be secret and has a timing sidechannel. */ - secp256k1_fe_normalize(&pt.x); - secp256k1_fe_normalize(&pt.y); - secp256k1_fe_get_b32(x, &pt.x); - y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y); - - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, y, sizeof(y)); - secp256k1_sha256_write(&sha, x, sizeof(x)); - secp256k1_sha256_finalize(&sha, result); - ret = 1; - } + overflow |= secp256k1_scalar_is_zero(&s); + secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow); + + secp256k1_ecmult_const(&res, &pt, &s); + secp256k1_ge_set_gej(&pt, &res); + + /* Compute a hash of the point */ + secp256k1_fe_normalize(&pt.x); + secp256k1_fe_normalize(&pt.y); + secp256k1_fe_get_b32(x, &pt.x); + secp256k1_fe_get_b32(y, &pt.y); + + ret = hashfp(output, x, y, data); + + memset(x, 0, 32); + memset(y, 0, 32); secp256k1_scalar_clear(&s); - return ret; + + return !!ret & !overflow; } #endif /* SECP256K1_MODULE_ECDH_MAIN_H */ diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h index b7562952c..3c3acdaf8 100644 --- a/src/secp256k1/src/modules/ecdh/tests_impl.h +++ b/src/secp256k1/src/modules/ecdh/tests_impl.h @@ -1,76 +1,87 @@ -/************************************************************************ - * Copyright (c) 2015 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_MODULE_ECDH_TESTS_H #define SECP256K1_MODULE_ECDH_TESTS_H -void test_ecdh_api(void) { - /* Setup context that just counts errors */ - secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); +static int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { + (void)output; + (void)x; + (void)y; + (void)data; + return 0; +} + +static int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { + (void)data; + /* Save x and y as uncompressed public key */ + output[0] = 0x04; + memcpy(output + 1, x, 32); + memcpy(output + 33, y, 32); + return 1; +} + +static void test_ecdh_api(void) { secp256k1_pubkey point; unsigned char res[32]; unsigned char s_one[32] = { 0 }; - int32_t ecount = 0; s_one[31] = 1; - secp256k1_context_set_error_callback(tctx, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(tctx, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_one) == 1); /* Check all NULLs are detected */ - CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); - CHECK(ecount == 0); - CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); - CHECK(ecount == 3); - - /* Cleanup */ - secp256k1_context_destroy(tctx); + CHECK(secp256k1_ecdh(CTX, res, &point, s_one, NULL, NULL) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdh(CTX, NULL, &point, s_one, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdh(CTX, res, NULL, s_one, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdh(CTX, res, &point, NULL, NULL, NULL)); + CHECK(secp256k1_ecdh(CTX, res, &point, s_one, NULL, NULL) == 1); } -void test_ecdh_generator_basepoint(void) { +static void test_ecdh_generator_basepoint(void) { unsigned char s_one[32] = { 0 }; secp256k1_pubkey point[2]; int i; s_one[31] = 1; /* Check against pubkey creation when the basepoint is the generator */ - for (i = 0; i < 100; ++i) { + for (i = 0; i < 2 * COUNT; ++i) { secp256k1_sha256 sha; unsigned char s_b32[32]; - unsigned char output_ecdh[32]; + unsigned char output_ecdh[65]; unsigned char output_ser[32]; - unsigned char point_ser[33]; + unsigned char point_ser[65]; size_t point_ser_len = sizeof(point_ser); secp256k1_scalar s; - random_scalar_order(&s); + testutil_random_scalar_order(&s); secp256k1_scalar_get_b32(s_b32, &s); - /* compute using ECDH function */ - CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1); - CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point[0], s_one) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point[1], s_b32) == 1); + + /* compute using ECDH function with custom hash function */ + CHECK(secp256k1_ecdh(CTX, output_ecdh, &point[0], s_b32, ecdh_hash_function_custom, NULL) == 1); /* compute "explicitly" */ - CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1); - CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); - CHECK(point_ser_len == sizeof(point_ser)); + CHECK(secp256k1_ec_pubkey_serialize(CTX, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1); + /* compare */ + CHECK(secp256k1_memcmp_var(output_ecdh, point_ser, 65) == 0); + + /* compute using ECDH function with default hash function */ + CHECK(secp256k1_ecdh(CTX, output_ecdh, &point[0], s_b32, NULL, NULL) == 1); + /* compute "explicitly" */ + CHECK(secp256k1_ec_pubkey_serialize(CTX, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, point_ser, point_ser_len); secp256k1_sha256_finalize(&sha, output_ser); /* compare */ - CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0); + CHECK(secp256k1_memcmp_var(output_ecdh, output_ser, 32) == 0); } } -void test_bad_scalar(void) { +static void test_bad_scalar(void) { unsigned char s_zero[32] = { 0 }; unsigned char s_overflow[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -84,22 +95,58 @@ void test_bad_scalar(void) { secp256k1_pubkey point; /* Create random point */ - random_scalar_order(&rand); + testutil_random_scalar_order(&rand); secp256k1_scalar_get_b32(s_rand, &rand); - CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_rand) == 1); /* Try to multiply it by bad values */ - CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0); - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0); + CHECK(secp256k1_ecdh(CTX, output, &point, s_zero, NULL, NULL) == 0); + CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, NULL, NULL) == 0); /* ...and a good one */ s_overflow[31] -= 1; - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1); + CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, NULL, NULL) == 1); + + /* Hash function failure results in ecdh failure */ + CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0); +} + +/** Test that ECDH(sG, 1/s) == ECDH((1/s)G, s) == ECDH(G, 1) for a few random s. */ +static void test_result_basepoint(void) { + secp256k1_pubkey point; + secp256k1_scalar rand; + unsigned char s[32]; + unsigned char s_inv[32]; + unsigned char out[32]; + unsigned char out_inv[32]; + unsigned char out_base[32]; + int i; + + unsigned char s_one[32] = { 0 }; + s_one[31] = 1; + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_one) == 1); + CHECK(secp256k1_ecdh(CTX, out_base, &point, s_one, NULL, NULL) == 1); + + for (i = 0; i < 2 * COUNT; i++) { + testutil_random_scalar_order(&rand); + secp256k1_scalar_get_b32(s, &rand); + secp256k1_scalar_inverse(&rand, &rand); + secp256k1_scalar_get_b32(s_inv, &rand); + + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s) == 1); + CHECK(secp256k1_ecdh(CTX, out, &point, s_inv, NULL, NULL) == 1); + CHECK(secp256k1_memcmp_var(out, out_base, 32) == 0); + + CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_inv) == 1); + CHECK(secp256k1_ecdh(CTX, out_inv, &point, s, NULL, NULL) == 1); + CHECK(secp256k1_memcmp_var(out_inv, out_base, 32) == 0); + } } -void run_ecdh_tests(void) { +static void run_ecdh_tests(void) { test_ecdh_api(); test_ecdh_generator_basepoint(); test_bad_scalar(); + test_result_basepoint(); } #endif /* SECP256K1_MODULE_ECDH_TESTS_H */ diff --git a/src/secp256k1/src/modules/ellswift/Makefile.am.include b/src/secp256k1/src/modules/ellswift/Makefile.am.include new file mode 100644 index 000000000..8251231ea --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/Makefile.am.include @@ -0,0 +1,5 @@ +include_HEADERS += include/secp256k1_ellswift.h +noinst_HEADERS += src/modules/ellswift/bench_impl.h +noinst_HEADERS += src/modules/ellswift/main_impl.h +noinst_HEADERS += src/modules/ellswift/tests_impl.h +noinst_HEADERS += src/modules/ellswift/tests_exhaustive_impl.h diff --git a/src/secp256k1/src/modules/ellswift/bench_impl.h b/src/secp256k1/src/modules/ellswift/bench_impl.h new file mode 100644 index 000000000..b16a3a368 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/bench_impl.h @@ -0,0 +1,106 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_BENCH_H +#define SECP256K1_MODULE_ELLSWIFT_BENCH_H + +#include "../../../include/secp256k1_ellswift.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point[256]; + unsigned char rnd64[64]; +} bench_ellswift_data; + +static void bench_ellswift_setup(void *arg) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + static const unsigned char init[64] = { + 0x78, 0x1f, 0xb7, 0xd4, 0x67, 0x7f, 0x08, 0x68, + 0xdb, 0xe3, 0x1d, 0x7f, 0x1b, 0xb0, 0xf6, 0x9e, + 0x0a, 0x64, 0xca, 0x32, 0x9e, 0xc6, 0x20, 0x79, + 0x03, 0xf3, 0xd0, 0x46, 0x7a, 0x0f, 0xd2, 0x21, + 0xb0, 0x2c, 0x46, 0xd8, 0xba, 0xca, 0x26, 0x4f, + 0x8f, 0x8c, 0xd4, 0xdd, 0x2d, 0x04, 0xbe, 0x30, + 0x48, 0x51, 0x1e, 0xd4, 0x16, 0xfd, 0x42, 0x85, + 0x62, 0xc9, 0x02, 0xf9, 0x89, 0x84, 0xff, 0xdc + }; + memcpy(data->rnd64, init, 64); + for (i = 0; i < 256; ++i) { + int j; + CHECK(secp256k1_ellswift_decode(data->ctx, &data->point[i], data->rnd64)); + for (j = 0; j < 64; ++j) { + data->rnd64[j] += 1; + } + } + CHECK(secp256k1_ellswift_encode(data->ctx, data->rnd64, &data->point[255], init + 16)); +} + +static void bench_ellswift_encode(void *arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + CHECK(secp256k1_ellswift_encode(data->ctx, data->rnd64, &data->point[i & 255], data->rnd64 + 16)); + } +} + +static void bench_ellswift_create(void *arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + unsigned char buf[64]; + CHECK(secp256k1_ellswift_create(data->ctx, buf, data->rnd64, data->rnd64 + 32)); + memcpy(data->rnd64, buf, 64); + } +} + +static void bench_ellswift_decode(void *arg, int iters) { + int i; + secp256k1_pubkey out; + size_t len; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + CHECK(secp256k1_ellswift_decode(data->ctx, &out, data->rnd64) == 1); + len = 33; + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->rnd64 + (i % 32), &len, &out, SECP256K1_EC_COMPRESSED)); + } +} + +static void bench_ellswift_xdh(void *arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + int party = i & 1; + CHECK(secp256k1_ellswift_xdh(data->ctx, + data->rnd64 + (i % 33), + data->rnd64, + data->rnd64, + data->rnd64 + ((i + 16) % 33), + party, + secp256k1_ellswift_xdh_hash_function_bip324, + NULL) == 1); + } +} + +void run_ellswift_bench(int iters, int argc, char **argv) { + bench_ellswift_data data; + int d = argc == 1; + + /* create a context with signing capabilities */ + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "encode") || have_flag(argc, argv, "ellswift_encode")) run_benchmark("ellswift_encode", bench_ellswift_encode, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_decode")) run_benchmark("ellswift_decode", bench_ellswift_decode, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "keygen") || have_flag(argc, argv, "ellswift_keygen")) run_benchmark("ellswift_keygen", bench_ellswift_create, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "ecdh") || have_flag(argc, argv, "ellswift_ecdh")) run_benchmark("ellswift_ecdh", bench_ellswift_xdh, bench_ellswift_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); +} + +#endif diff --git a/src/secp256k1/src/modules/ellswift/main_impl.h b/src/secp256k1/src/modules/ellswift/main_impl.h new file mode 100644 index 000000000..b54ec08a2 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/main_impl.h @@ -0,0 +1,590 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_MAIN_H +#define SECP256K1_MODULE_ELLSWIFT_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_ellswift.h" +#include "../../eckey.h" +#include "../../hash.h" + +/** c1 = (sqrt(-3)-1)/2 */ +static const secp256k1_fe secp256k1_ellswift_c1 = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa40); +/** c2 = (-sqrt(-3)-1)/2 = -(c1+1) */ +static const secp256k1_fe secp256k1_ellswift_c2 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ee); +/** c3 = (-sqrt(-3)+1)/2 = -c1 = c2+1 */ +static const secp256k1_fe secp256k1_ellswift_c3 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ef); +/** c4 = (sqrt(-3)+1)/2 = -c2 = c1+1 */ +static const secp256k1_fe secp256k1_ellswift_c4 = SECP256K1_FE_CONST(0x851695d4, 0x9a83f8ef, 0x919bb861, 0x53cbcb16, 0x630fb68a, 0xed0a766a, 0x3ec693d6, 0x8e6afa41); + +/** Decode ElligatorSwift encoding (u, t) to a fraction xn/xd representing a curve X coordinate. */ +static void secp256k1_ellswift_xswiftec_frac_var(secp256k1_fe *xn, secp256k1_fe *xd, const secp256k1_fe *u, const secp256k1_fe *t) { + /* The implemented algorithm is the following (all operations in GF(p)): + * + * - Let c0 = sqrt(-3) = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852. + * - If u = 0, set u = 1. + * - If t = 0, set t = 1. + * - If u^3+7+t^2 = 0, set t = 2*t. + * - Let X = (u^3+7-t^2)/(2*t). + * - Let Y = (X+t)/(c0*u). + * - If x3 = u+4*Y^2 is a valid x coordinate, return it. + * - If x2 = (-X/Y-u)/2 is a valid x coordinate, return it. + * - Return x1 = (X/Y-u)/2 (which is now guaranteed to be a valid x coordinate). + * + * Introducing s=t^2, g=u^3+7, and simplifying x1=-(x2+u) we get: + * + * - Let c0 = ... + * - If u = 0, set u = 1. + * - If t = 0, set t = 1. + * - Let s = t^2 + * - Let g = u^3+7 + * - If g+s = 0, set t = 2*t, s = 4*s + * - Let X = (g-s)/(2*t). + * - Let Y = (X+t)/(c0*u) = (g+s)/(2*c0*t*u). + * - If x3 = u+4*Y^2 is a valid x coordinate, return it. + * - If x2 = (-X/Y-u)/2 is a valid x coordinate, return it. + * - Return x1 = -(x2+u). + * + * Now substitute Y^2 = -(g+s)^2/(12*s*u^2) and X/Y = c0*u*(g-s)/(g+s). This + * means X and Y do not need to be evaluated explicitly anymore. + * + * - ... + * - If g+s = 0, set s = 4*s. + * - If x3 = u-(g+s)^2/(3*s*u^2) is a valid x coordinate, return it. + * - If x2 = (-c0*u*(g-s)/(g+s)-u)/2 is a valid x coordinate, return it. + * - Return x1 = -(x2+u). + * + * Simplifying x2 using 2 additional constants: + * + * - Let c1 = (c0-1)/2 = 0x851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40. + * - Let c2 = (-c0-1)/2 = 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee. + * - ... + * - If x2 = u*(c1*s+c2*g)/(g+s) is a valid x coordinate, return it. + * - ... + * + * Writing x3 as a fraction: + * + * - ... + * - If x3 = (3*s*u^3-(g+s)^2)/(3*s*u^2) ... + * - ... + + * Overall, we get: + * + * - Let c1 = 0x851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40. + * - Let c2 = 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee. + * - If u = 0, set u = 1. + * - If t = 0, set s = 1, else set s = t^2. + * - Let g = u^3+7. + * - If g+s = 0, set s = 4*s. + * - If x3 = (3*s*u^3-(g+s)^2)/(3*s*u^2) is a valid x coordinate, return it. + * - If x2 = u*(c1*s+c2*g)/(g+s) is a valid x coordinate, return it. + * - Return x1 = -(x2+u). + */ + secp256k1_fe u1, s, g, p, d, n, l; + u1 = *u; + if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&u1), 0)) u1 = secp256k1_fe_one; + secp256k1_fe_sqr(&s, t); + if (EXPECT(secp256k1_fe_normalizes_to_zero_var(t), 0)) s = secp256k1_fe_one; + secp256k1_fe_sqr(&l, &u1); /* l = u^2 */ + secp256k1_fe_mul(&g, &l, &u1); /* g = u^3 */ + secp256k1_fe_add_int(&g, SECP256K1_B); /* g = u^3 + 7 */ + p = g; /* p = g */ + secp256k1_fe_add(&p, &s); /* p = g+s */ + if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&p), 0)) { + secp256k1_fe_mul_int(&s, 4); + /* Recompute p = g+s */ + p = g; /* p = g */ + secp256k1_fe_add(&p, &s); /* p = g+s */ + } + secp256k1_fe_mul(&d, &s, &l); /* d = s*u^2 */ + secp256k1_fe_mul_int(&d, 3); /* d = 3*s*u^2 */ + secp256k1_fe_sqr(&l, &p); /* l = (g+s)^2 */ + secp256k1_fe_negate(&l, &l, 1); /* l = -(g+s)^2 */ + secp256k1_fe_mul(&n, &d, &u1); /* n = 3*s*u^3 */ + secp256k1_fe_add(&n, &l); /* n = 3*s*u^3-(g+s)^2 */ + if (secp256k1_ge_x_frac_on_curve_var(&n, &d)) { + /* Return x3 = n/d = (3*s*u^3-(g+s)^2)/(3*s*u^2) */ + *xn = n; + *xd = d; + return; + } + *xd = p; + secp256k1_fe_mul(&l, &secp256k1_ellswift_c1, &s); /* l = c1*s */ + secp256k1_fe_mul(&n, &secp256k1_ellswift_c2, &g); /* n = c2*g */ + secp256k1_fe_add(&n, &l); /* n = c1*s+c2*g */ + secp256k1_fe_mul(&n, &n, &u1); /* n = u*(c1*s+c2*g) */ + /* Possible optimization: in the invocation below, p^2 = (g+s)^2 is computed, + * which we already have computed above. This could be deduplicated. */ + if (secp256k1_ge_x_frac_on_curve_var(&n, &p)) { + /* Return x2 = n/p = u*(c1*s+c2*g)/(g+s) */ + *xn = n; + return; + } + secp256k1_fe_mul(&l, &p, &u1); /* l = u*(g+s) */ + secp256k1_fe_add(&n, &l); /* n = u*(c1*s+c2*g)+u*(g+s) */ + secp256k1_fe_negate(xn, &n, 2); /* n = -u*(c1*s+c2*g)-u*(g+s) */ + + VERIFY_CHECK(secp256k1_ge_x_frac_on_curve_var(xn, &p)); + /* Return x3 = n/p = -(u*(c1*s+c2*g)/(g+s)+u) */ +} + +/** Decode ElligatorSwift encoding (u, t) to X coordinate. */ +static void secp256k1_ellswift_xswiftec_var(secp256k1_fe *x, const secp256k1_fe *u, const secp256k1_fe *t) { + secp256k1_fe xn, xd; + secp256k1_ellswift_xswiftec_frac_var(&xn, &xd, u, t); + secp256k1_fe_inv_var(&xd, &xd); + secp256k1_fe_mul(x, &xn, &xd); +} + +/** Decode ElligatorSwift encoding (u, t) to point P. */ +static void secp256k1_ellswift_swiftec_var(secp256k1_ge *p, const secp256k1_fe *u, const secp256k1_fe *t) { + secp256k1_fe x; + secp256k1_ellswift_xswiftec_var(&x, u, t); + secp256k1_ge_set_xo_var(p, &x, secp256k1_fe_is_odd(t)); +} + +/* Try to complete an ElligatorSwift encoding (u, t) for X coordinate x, given u and x. + * + * There may be up to 8 distinct t values such that (u, t) decodes back to x, but also + * fewer, or none at all. Each such partial inverse can be accessed individually using a + * distinct input argument c (in range 0-7), and some or all of these may return failure. + * The following guarantees exist: + * - Given (x, u), no two distinct c values give the same successful result t. + * - Every successful result maps back to x through secp256k1_ellswift_xswiftec_var. + * - Given (x, u), all t values that map back to x can be reached by combining the + * successful results from this function over all c values, with the exception of: + * - this function cannot be called with u=0 + * - no result with t=0 will be returned + * - no result for which u^3 + t^2 + 7 = 0 will be returned. + * + * The rather unusual encoding of bits in c (a large "if" based on the middle bit, and then + * using the low and high bits to pick signs of square roots) is to match the paper's + * encoding more closely: c=0 through c=3 match branches 1..4 in the paper, while c=4 through + * c=7 are copies of those with an additional negation of sqrt(w). + */ +static int secp256k1_ellswift_xswiftec_inv_var(secp256k1_fe *t, const secp256k1_fe *x_in, const secp256k1_fe *u_in, int c) { + /* The implemented algorithm is this (all arithmetic, except involving c, is mod p): + * + * - If (c & 2) = 0: + * - If (-x-u) is a valid X coordinate, fail. + * - Let s=-(u^3+7)/(u^2+u*x+x^2). + * - If s is not square, fail. + * - Let v=x. + * - If (c & 2) = 2: + * - Let s=x-u. + * - If s is not square, fail. + * - Let r=sqrt(-s*(4*(u^3+7)+3*u^2*s)); fail if it doesn't exist. + * - If (c & 1) = 1 and r = 0, fail. + * - If s=0, fail. + * - Let v=(r/s-u)/2. + * - Let w=sqrt(s). + * - If (c & 5) = 0: return -w*(c3*u + v). + * - If (c & 5) = 1: return w*(c4*u + v). + * - If (c & 5) = 4: return w*(c3*u + v). + * - If (c & 5) = 5: return -w*(c4*u + v). + */ + secp256k1_fe x = *x_in, u = *u_in, g, v, s, m, r, q; + int ret; + + secp256k1_fe_normalize_weak(&x); + secp256k1_fe_normalize_weak(&u); + + VERIFY_CHECK(c >= 0 && c < 8); + VERIFY_CHECK(secp256k1_ge_x_on_curve_var(&x)); + + if (!(c & 2)) { + /* c is in {0, 1, 4, 5}. In this case we look for an inverse under the x1 (if c=0 or + * c=4) formula, or x2 (if c=1 or c=5) formula. */ + + /* If -u-x is a valid X coordinate, fail. This would yield an encoding that roundtrips + * back under the x3 formula instead (which has priority over x1 and x2, so the decoding + * would not match x). */ + m = x; /* m = x */ + secp256k1_fe_add(&m, &u); /* m = u+x */ + secp256k1_fe_negate(&m, &m, 2); /* m = -u-x */ + /* Test if (-u-x) is a valid X coordinate. If so, fail. */ + if (secp256k1_ge_x_on_curve_var(&m)) return 0; + + /* Let s = -(u^3 + 7)/(u^2 + u*x + x^2) [first part] */ + secp256k1_fe_sqr(&s, &m); /* s = (u+x)^2 */ + secp256k1_fe_negate(&s, &s, 1); /* s = -(u+x)^2 */ + secp256k1_fe_mul(&m, &u, &x); /* m = u*x */ + secp256k1_fe_add(&s, &m); /* s = -(u^2 + u*x + x^2) */ + + /* Note that at this point, s = 0 is impossible. If it were the case: + * s = -(u^2 + u*x + x^2) = 0 + * => u^2 + u*x + x^2 = 0 + * => (u + 2*x) * (u^2 + u*x + x^2) = 0 + * => 2*x^3 + 3*x^2*u + 3*x*u^2 + u^3 = 0 + * => (x + u)^3 + x^3 = 0 + * => x^3 = -(x + u)^3 + * => x^3 + B = (-u - x)^3 + B + * + * However, we know x^3 + B is square (because x is on the curve) and + * that (-u-x)^3 + B is not square (the secp256k1_ge_x_on_curve_var(&m) + * test above would have failed). This is a contradiction, and thus the + * assumption s=0 is false. */ + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(&s)); + + /* If s is not square, fail. We have not fully computed s yet, but s is square iff + * -(u^3+7)*(u^2+u*x+x^2) is square (because a/b is square iff a*b is square and b is + * nonzero). */ + secp256k1_fe_sqr(&g, &u); /* g = u^2 */ + secp256k1_fe_mul(&g, &g, &u); /* g = u^3 */ + secp256k1_fe_add_int(&g, SECP256K1_B); /* g = u^3+7 */ + secp256k1_fe_mul(&m, &s, &g); /* m = -(u^3 + 7)*(u^2 + u*x + x^2) */ + if (!secp256k1_fe_is_square_var(&m)) return 0; + + /* Let s = -(u^3 + 7)/(u^2 + u*x + x^2) [second part] */ + secp256k1_fe_inv_var(&s, &s); /* s = -1/(u^2 + u*x + x^2) [no div by 0] */ + secp256k1_fe_mul(&s, &s, &g); /* s = -(u^3 + 7)/(u^2 + u*x + x^2) */ + + /* Let v = x. */ + v = x; + } else { + /* c is in {2, 3, 6, 7}. In this case we look for an inverse under the x3 formula. */ + + /* Let s = x-u. */ + secp256k1_fe_negate(&m, &u, 1); /* m = -u */ + s = m; /* s = -u */ + secp256k1_fe_add(&s, &x); /* s = x-u */ + + /* If s is not square, fail. */ + if (!secp256k1_fe_is_square_var(&s)) return 0; + + /* Let r = sqrt(-s*(4*(u^3+7)+3*u^2*s)); fail if it doesn't exist. */ + secp256k1_fe_sqr(&g, &u); /* g = u^2 */ + secp256k1_fe_mul(&q, &s, &g); /* q = s*u^2 */ + secp256k1_fe_mul_int(&q, 3); /* q = 3*s*u^2 */ + secp256k1_fe_mul(&g, &g, &u); /* g = u^3 */ + secp256k1_fe_mul_int(&g, 4); /* g = 4*u^3 */ + secp256k1_fe_add_int(&g, 4 * SECP256K1_B); /* g = 4*(u^3+7) */ + secp256k1_fe_add(&q, &g); /* q = 4*(u^3+7)+3*s*u^2 */ + secp256k1_fe_mul(&q, &q, &s); /* q = s*(4*(u^3+7)+3*u^2*s) */ + secp256k1_fe_negate(&q, &q, 1); /* q = -s*(4*(u^3+7)+3*u^2*s) */ + if (!secp256k1_fe_is_square_var(&q)) return 0; + ret = secp256k1_fe_sqrt(&r, &q); /* r = sqrt(-s*(4*(u^3+7)+3*u^2*s)) */ +#ifdef VERIFY + VERIFY_CHECK(ret); +#else + (void)ret; +#endif + + /* If (c & 1) = 1 and r = 0, fail. */ + if (EXPECT((c & 1) && secp256k1_fe_normalizes_to_zero_var(&r), 0)) return 0; + + /* If s = 0, fail. */ + if (EXPECT(secp256k1_fe_normalizes_to_zero_var(&s), 0)) return 0; + + /* Let v = (r/s-u)/2. */ + secp256k1_fe_inv_var(&v, &s); /* v = 1/s [no div by 0] */ + secp256k1_fe_mul(&v, &v, &r); /* v = r/s */ + secp256k1_fe_add(&v, &m); /* v = r/s-u */ + secp256k1_fe_half(&v); /* v = (r/s-u)/2 */ + } + + /* Let w = sqrt(s). */ + ret = secp256k1_fe_sqrt(&m, &s); /* m = sqrt(s) = w */ + VERIFY_CHECK(ret); + + /* Return logic. */ + if ((c & 5) == 0 || (c & 5) == 5) { + secp256k1_fe_negate(&m, &m, 1); /* m = -w */ + } + /* Now m = {-w if c&5=0 or c&5=5; w otherwise}. */ + secp256k1_fe_mul(&u, &u, c&1 ? &secp256k1_ellswift_c4 : &secp256k1_ellswift_c3); + /* u = {c4 if c&1=1; c3 otherwise}*u */ + secp256k1_fe_add(&u, &v); /* u = {c4 if c&1=1; c3 otherwise}*u + v */ + secp256k1_fe_mul(t, &m, &u); + return 1; +} + +/** Use SHA256 as a PRNG, returning SHA256(hasher || cnt). + * + * hasher is a SHA256 object to which an incrementing 4-byte counter is written to generate randomness. + * Writing 13 bytes (4 bytes for counter, plus 9 bytes for the SHA256 padding) cannot cross a + * 64-byte block size boundary (to make sure it only triggers a single SHA256 compression). */ +static void secp256k1_ellswift_prng(unsigned char* out32, const secp256k1_sha256 *hasher, uint32_t cnt) { + secp256k1_sha256 hash = *hasher; + unsigned char buf4[4]; +#ifdef VERIFY + size_t blocks = hash.bytes >> 6; +#endif + buf4[0] = cnt; + buf4[1] = cnt >> 8; + buf4[2] = cnt >> 16; + buf4[3] = cnt >> 24; + secp256k1_sha256_write(&hash, buf4, 4); + secp256k1_sha256_finalize(&hash, out32); + + /* Writing and finalizing together should trigger exactly one SHA256 compression. */ + VERIFY_CHECK(((hash.bytes) >> 6) == (blocks + 1)); +} + +/** Find an ElligatorSwift encoding (u, t) for X coordinate x, and random Y coordinate. + * + * u32 is the 32-byte big endian encoding of u; t is the output field element t that still + * needs encoding. + * + * hasher is a hasher in the secp256k1_ellswift_prng sense, with the same restrictions. */ +static void secp256k1_ellswift_xelligatorswift_var(unsigned char *u32, secp256k1_fe *t, const secp256k1_fe *x, const secp256k1_sha256 *hasher) { + /* Pool of 3-bit branch values. */ + unsigned char branch_hash[32]; + /* Number of 3-bit values in branch_hash left. */ + int branches_left = 0; + /* Field elements u and branch values are extracted from RNG based on hasher for consecutive + * values of cnt. cnt==0 is first used to populate a pool of 64 4-bit branch values. The 64 + * cnt values that follow are used to generate field elements u. cnt==65 (and multiples + * thereof) are used to repopulate the pool and start over, if that were ever necessary. + * On average, 4 iterations are needed. */ + uint32_t cnt = 0; + while (1) { + int branch; + secp256k1_fe u; + /* If the pool of branch values is empty, populate it. */ + if (branches_left == 0) { + secp256k1_ellswift_prng(branch_hash, hasher, cnt++); + branches_left = 64; + } + /* Take a 3-bit branch value from the branch pool (top bit is discarded). */ + --branches_left; + branch = (branch_hash[branches_left >> 1] >> ((branches_left & 1) << 2)) & 7; + /* Compute a new u value by hashing. */ + secp256k1_ellswift_prng(u32, hasher, cnt++); + /* overflow is not a problem (we prefer uniform u32 over uniform u). */ + secp256k1_fe_set_b32_mod(&u, u32); + /* Since u is the output of a hash, it should practically never be 0. We could apply the + * u=0 to u=1 correction here too to deal with that case still, but it's such a low + * probability event that we do not bother. */ + VERIFY_CHECK(!secp256k1_fe_normalizes_to_zero_var(&u)); + + /* Find a remainder t, and return it if found. */ + if (EXPECT(secp256k1_ellswift_xswiftec_inv_var(t, x, &u, branch), 0)) break; + } +} + +/** Find an ElligatorSwift encoding (u, t) for point P. + * + * This is similar secp256k1_ellswift_xelligatorswift_var, except it takes a full group element p + * as input, and returns an encoding that matches the provided Y coordinate rather than a random + * one. + */ +static void secp256k1_ellswift_elligatorswift_var(unsigned char *u32, secp256k1_fe *t, const secp256k1_ge *p, const secp256k1_sha256 *hasher) { + secp256k1_ellswift_xelligatorswift_var(u32, t, &p->x, hasher); + secp256k1_fe_normalize_var(t); + if (secp256k1_fe_is_odd(t) != secp256k1_fe_is_odd(&p->y)) { + secp256k1_fe_negate(t, t, 1); + secp256k1_fe_normalize_var(t); + } +} + +/** Set hash state to the BIP340 tagged hash midstate for "secp256k1_ellswift_encode". */ +static void secp256k1_ellswift_sha256_init_encode(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0xd1a6524bul; + hash->s[1] = 0x028594b3ul; + hash->s[2] = 0x96e42f4eul; + hash->s[3] = 0x1037a177ul; + hash->s[4] = 0x1b8fcb8bul; + hash->s[5] = 0x56023885ul; + hash->s[6] = 0x2560ede1ul; + hash->s[7] = 0xd626b715ul; + + hash->bytes = 64; +} + +int secp256k1_ellswift_encode(const secp256k1_context *ctx, unsigned char *ell64, const secp256k1_pubkey *pubkey, const unsigned char *rnd32) { + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(rnd32 != NULL); + + if (secp256k1_pubkey_load(ctx, &p, pubkey)) { + secp256k1_fe t; + unsigned char p64[64] = {0}; + size_t ser_size; + int ser_ret; + secp256k1_sha256 hash; + + /* Set up hasher state; the used RNG is H(pubkey || "\x00"*31 || rnd32 || cnt++), using + * BIP340 tagged hash with tag "secp256k1_ellswift_encode". */ + secp256k1_ellswift_sha256_init_encode(&hash); + ser_ret = secp256k1_eckey_pubkey_serialize(&p, p64, &ser_size, 1); +#ifdef VERIFY + VERIFY_CHECK(ser_ret && ser_size == 33); +#else + (void)ser_ret; +#endif + secp256k1_sha256_write(&hash, p64, sizeof(p64)); + secp256k1_sha256_write(&hash, rnd32, 32); + + /* Compute ElligatorSwift encoding and construct output. */ + secp256k1_ellswift_elligatorswift_var(ell64, &t, &p, &hash); /* puts u in ell64[0..32] */ + secp256k1_fe_get_b32(ell64 + 32, &t); /* puts t in ell64[32..64] */ + return 1; + } + /* Only reached in case the provided pubkey is invalid. */ + memset(ell64, 0, 64); + return 0; +} + +/** Set hash state to the BIP340 tagged hash midstate for "secp256k1_ellswift_create". */ +static void secp256k1_ellswift_sha256_init_create(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0xd29e1bf5ul; + hash->s[1] = 0xf7025f42ul; + hash->s[2] = 0x9b024773ul; + hash->s[3] = 0x094cb7d5ul; + hash->s[4] = 0xe59ed789ul; + hash->s[5] = 0x03bc9786ul; + hash->s[6] = 0x68335b35ul; + hash->s[7] = 0x4e363b53ul; + + hash->bytes = 64; +} + +int secp256k1_ellswift_create(const secp256k1_context *ctx, unsigned char *ell64, const unsigned char *seckey32, const unsigned char *auxrnd32) { + secp256k1_ge p; + secp256k1_fe t; + secp256k1_sha256 hash; + secp256k1_scalar seckey_scalar; + int ret; + static const unsigned char zero32[32] = {0}; + + /* Sanity check inputs. */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + memset(ell64, 0, 64); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey32 != NULL); + + /* Compute (affine) public key */ + ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey32); + secp256k1_declassify(ctx, &p, sizeof(p)); /* not constant time in produced pubkey */ + secp256k1_fe_normalize_var(&p.x); + secp256k1_fe_normalize_var(&p.y); + + /* Set up hasher state. The used RNG is H(privkey || "\x00"*32 [|| auxrnd32] || cnt++), + * using BIP340 tagged hash with tag "secp256k1_ellswift_create". */ + secp256k1_ellswift_sha256_init_create(&hash); + secp256k1_sha256_write(&hash, seckey32, 32); + secp256k1_sha256_write(&hash, zero32, sizeof(zero32)); + secp256k1_declassify(ctx, &hash, sizeof(hash)); /* private key is hashed now */ + if (auxrnd32) secp256k1_sha256_write(&hash, auxrnd32, 32); + + /* Compute ElligatorSwift encoding and construct output. */ + secp256k1_ellswift_elligatorswift_var(ell64, &t, &p, &hash); /* puts u in ell64[0..32] */ + secp256k1_fe_get_b32(ell64 + 32, &t); /* puts t in ell64[32..64] */ + + secp256k1_memczero(ell64, 64, !ret); + secp256k1_scalar_clear(&seckey_scalar); + + return ret; +} + +int secp256k1_ellswift_decode(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *ell64) { + secp256k1_fe u, t; + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(ell64 != NULL); + + secp256k1_fe_set_b32_mod(&u, ell64); + secp256k1_fe_set_b32_mod(&t, ell64 + 32); + secp256k1_fe_normalize_var(&t); + secp256k1_ellswift_swiftec_var(&p, &u, &t); + secp256k1_pubkey_save(pubkey, &p); + return 1; +} + +static int ellswift_xdh_hash_function_prefix(unsigned char *output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) { + secp256k1_sha256 sha; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, data, 64); + secp256k1_sha256_write(&sha, ell_a64, 64); + secp256k1_sha256_write(&sha, ell_b64, 64); + secp256k1_sha256_write(&sha, x32, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + +/** Set hash state to the BIP340 tagged hash midstate for "bip324_ellswift_xonly_ecdh". */ +static void secp256k1_ellswift_sha256_init_bip324(secp256k1_sha256* hash) { + secp256k1_sha256_initialize(hash); + hash->s[0] = 0x8c12d730ul; + hash->s[1] = 0x827bd392ul; + hash->s[2] = 0x9e4fb2eeul; + hash->s[3] = 0x207b373eul; + hash->s[4] = 0x2292bd7aul; + hash->s[5] = 0xaa5441bcul; + hash->s[6] = 0x15c3779ful; + hash->s[7] = 0xcfb52549ul; + + hash->bytes = 64; +} + +static int ellswift_xdh_hash_function_bip324(unsigned char* output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) { + secp256k1_sha256 sha; + + (void)data; + + secp256k1_ellswift_sha256_init_bip324(&sha); + secp256k1_sha256_write(&sha, ell_a64, 64); + secp256k1_sha256_write(&sha, ell_b64, 64); + secp256k1_sha256_write(&sha, x32, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + +const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_prefix = ellswift_xdh_hash_function_prefix; +const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_bip324 = ellswift_xdh_hash_function_bip324; + +int secp256k1_ellswift_xdh(const secp256k1_context *ctx, unsigned char *output, const unsigned char *ell_a64, const unsigned char *ell_b64, const unsigned char *seckey32, int party, secp256k1_ellswift_xdh_hash_function hashfp, void *data) { + int ret = 0; + int overflow; + secp256k1_scalar s; + secp256k1_fe xn, xd, px, u, t; + unsigned char sx[32]; + const unsigned char* theirs64; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(ell_a64 != NULL); + ARG_CHECK(ell_b64 != NULL); + ARG_CHECK(seckey32 != NULL); + ARG_CHECK(hashfp != NULL); + + /* Load remote public key (as fraction). */ + theirs64 = party ? ell_a64 : ell_b64; + secp256k1_fe_set_b32_mod(&u, theirs64); + secp256k1_fe_set_b32_mod(&t, theirs64 + 32); + secp256k1_ellswift_xswiftec_frac_var(&xn, &xd, &u, &t); + + /* Load private key (using one if invalid). */ + secp256k1_scalar_set_b32(&s, seckey32, &overflow); + overflow = secp256k1_scalar_is_zero(&s); + secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow); + + /* Compute shared X coordinate. */ + secp256k1_ecmult_const_xonly(&px, &xn, &xd, &s, 1); + secp256k1_fe_normalize(&px); + secp256k1_fe_get_b32(sx, &px); + + /* Invoke hasher */ + ret = hashfp(output, sx, ell_a64, ell_b64, data); + + memset(sx, 0, 32); + secp256k1_fe_clear(&px); + secp256k1_scalar_clear(&s); + + return !!ret & !overflow; +} + +#endif diff --git a/src/secp256k1/src/modules/ellswift/tests_exhaustive_impl.h b/src/secp256k1/src/modules/ellswift/tests_exhaustive_impl.h new file mode 100644 index 000000000..839c24aee --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/tests_exhaustive_impl.h @@ -0,0 +1,39 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_TESTS_EXHAUSTIVE_H +#define SECP256K1_MODULE_ELLSWIFT_TESTS_EXHAUSTIVE_H + +#include "../../../include/secp256k1_ellswift.h" +#include "main_impl.h" + +static void test_exhaustive_ellswift(const secp256k1_context *ctx, const secp256k1_ge *group) { + int i; + + /* Note that SwiftEC/ElligatorSwift are inherently curve operations, not + * group operations, and this test only checks the curve points which are in + * a tiny subgroup. In that sense it can't be really seen as exhaustive as + * it doesn't (and for computational reasons obviously cannot) test the + * entire domain ellswift operates under. */ + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { + secp256k1_scalar scalar_i; + unsigned char sec32[32]; + unsigned char ell64[64]; + secp256k1_pubkey pub_decoded; + secp256k1_ge ge_decoded; + + /* Construct ellswift pubkey from exhaustive loop scalar i. */ + secp256k1_scalar_set_int(&scalar_i, i); + secp256k1_scalar_get_b32(sec32, &scalar_i); + CHECK(secp256k1_ellswift_create(ctx, ell64, sec32, NULL)); + + /* Decode ellswift pubkey and check that it matches the precomputed group element. */ + secp256k1_ellswift_decode(ctx, &pub_decoded, ell64); + secp256k1_pubkey_load(ctx, &ge_decoded, &pub_decoded); + CHECK(secp256k1_ge_eq_var(&ge_decoded, &group[i])); + } +} + +#endif diff --git a/src/secp256k1/src/modules/ellswift/tests_impl.h b/src/secp256k1/src/modules/ellswift/tests_impl.h new file mode 100644 index 000000000..ed5658f34 --- /dev/null +++ b/src/secp256k1/src/modules/ellswift/tests_impl.h @@ -0,0 +1,436 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_TESTS_H +#define SECP256K1_MODULE_ELLSWIFT_TESTS_H + +#include "../../../include/secp256k1_ellswift.h" + +struct ellswift_xswiftec_inv_test { + int enc_bitmap; + secp256k1_fe u; + secp256k1_fe x; + secp256k1_fe encs[8]; +}; + +struct ellswift_decode_test { + unsigned char enc[64]; + secp256k1_fe x; + int odd_y; +}; + +struct ellswift_xdh_test { + unsigned char priv_ours[32]; + unsigned char ellswift_ours[64]; + unsigned char ellswift_theirs[64]; + int initiating; + unsigned char shared_secret[32]; +}; + +/* Set of (point, encodings) test vectors, selected to maximize branch coverage, part of the BIP324 + * test vectors. Created using an independent implementation, and tested decoding against paper + * authors' code. */ +static const struct ellswift_xswiftec_inv_test ellswift_xswiftec_inv_tests[] = { + {0xcc, SECP256K1_FE_CONST(0x05ff6bda, 0xd900fc32, 0x61bc7fe3, 0x4e2fb0f5, 0x69f06e09, 0x1ae437d3, 0xa52e9da0, 0xcbfb9590), SECP256K1_FE_CONST(0x80cdf637, 0x74ec7022, 0xc89a5a85, 0x58e373a2, 0x79170285, 0xe0ab2741, 0x2dbce510, 0xbdfe23fc), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x45654798, 0xece071ba, 0x79286d04, 0xf7f3eb1c, 0x3f1d17dd, 0x883610f2, 0xad2efd82, 0xa287466b), SECP256K1_FE_CONST(0x0aeaa886, 0xf6b76c71, 0x58452418, 0xcbf5033a, 0xdc5747e9, 0xe9b5d3b2, 0x303db969, 0x36528557), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xba9ab867, 0x131f8e45, 0x86d792fb, 0x080c14e3, 0xc0e2e822, 0x77c9ef0d, 0x52d1027c, 0x5d78b5c4), SECP256K1_FE_CONST(0xf5155779, 0x0948938e, 0xa7badbe7, 0x340afcc5, 0x23a8b816, 0x164a2c4d, 0xcfc24695, 0xc9ad76d8)}}, + {0x33, SECP256K1_FE_CONST(0x1737a85f, 0x4c8d146c, 0xec96e3ff, 0xdca76d99, 0x03dcf3bd, 0x53061868, 0xd478c78c, 0x63c2aa9e), SECP256K1_FE_CONST(0x39e48dd1, 0x50d2f429, 0xbe088dfd, 0x5b61882e, 0x7e840748, 0x3702ae9a, 0x5ab35927, 0xb15f85ea), {SECP256K1_FE_CONST(0x1be8cc0b, 0x04be0c68, 0x1d0c6a68, 0xf733f82c, 0x6c896e0c, 0x8a262fcd, 0x392918e3, 0x03a7abf4), SECP256K1_FE_CONST(0x605b5814, 0xbf9b8cb0, 0x66667c9e, 0x5480d22d, 0xc5b6c92f, 0x14b4af3e, 0xe0a9eb83, 0xb03685e3), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xe41733f4, 0xfb41f397, 0xe2f39597, 0x08cc07d3, 0x937691f3, 0x75d9d032, 0xc6d6e71b, 0xfc58503b), SECP256K1_FE_CONST(0x9fa4a7eb, 0x4064734f, 0x99998361, 0xab7f2dd2, 0x3a4936d0, 0xeb4b50c1, 0x1f56147b, 0x4fc9764c), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0x1aaa1cce, 0xbf9c7241, 0x91033df3, 0x66b36f69, 0x1c4d902c, 0x228033ff, 0x4516d122, 0xb2564f68), SECP256K1_FE_CONST(0xc7554125, 0x9d3ba98f, 0x207eaa30, 0xc69634d1, 0x87d0b6da, 0x594e719e, 0x420f4898, 0x638fc5b0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x2323a1d0, 0x79b0fd72, 0xfc8bb62e, 0xc34230a8, 0x15cb0596, 0xc2bfac99, 0x8bd6b842, 0x60f5dc26), SECP256K1_FE_CONST(0x239342df, 0xb675500a, 0x34a19631, 0x0b8d87d5, 0x4f49dcac, 0x9da50c17, 0x43ceab41, 0xa7b249ff), {SECP256K1_FE_CONST(0xf63580b8, 0xaa49c484, 0x6de56e39, 0xe1b3e73f, 0x171e881e, 0xba8c66f6, 0x14e67e5c, 0x975dfc07), SECP256K1_FE_CONST(0xb6307b33, 0x2e699f1c, 0xf77841d9, 0x0af25365, 0x404deb7f, 0xed5edb30, 0x90db49e6, 0x42a156b6), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x09ca7f47, 0x55b63b7b, 0x921a91c6, 0x1e4c18c0, 0xe8e177e1, 0x45739909, 0xeb1981a2, 0x68a20028), SECP256K1_FE_CONST(0x49cf84cc, 0xd19660e3, 0x0887be26, 0xf50dac9a, 0xbfb21480, 0x12a124cf, 0x6f24b618, 0xbd5ea579), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x2dc90e64, 0x0cb646ae, 0x9164c0b5, 0xa9ef0169, 0xfebe34dc, 0x4437d6e4, 0x6acb0e27, 0xe219d1e8), SECP256K1_FE_CONST(0xd236f19b, 0xf349b951, 0x6e9b3f4a, 0x5610fe96, 0x0141cb23, 0xbbc8291b, 0x9534f1d7, 0x1de62a47), {SECP256K1_FE_CONST(0xe69df7d9, 0xc026c366, 0x00ebdf58, 0x80726758, 0x47c0c431, 0xc8eb7306, 0x82533e96, 0x4b6252c9), SECP256K1_FE_CONST(0x4f18bbdf, 0x7c2d6c5f, 0x818c1880, 0x2fa35cd0, 0x69eaa79f, 0xff74e4fc, 0x837c80d9, 0x3fece2f8), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x19620826, 0x3fd93c99, 0xff1420a7, 0x7f8d98a7, 0xb83f3bce, 0x37148cf9, 0x7dacc168, 0xb49da966), SECP256K1_FE_CONST(0xb0e74420, 0x83d293a0, 0x7e73e77f, 0xd05ca32f, 0x96155860, 0x008b1b03, 0x7c837f25, 0xc0131937), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0x3edd7b39, 0x80e2f2f3, 0x4d1409a2, 0x07069f88, 0x1fda5f96, 0xf08027ac, 0x4465b63d, 0xc278d672), SECP256K1_FE_CONST(0x053a98de, 0x4a27b196, 0x1155822b, 0x3a3121f0, 0x3b2a1445, 0x8bd80eb4, 0xa560c4c7, 0xa85c149c), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb3dae4b7, 0xdcf858e4, 0xc6968057, 0xcef2b156, 0x46543152, 0x6538199c, 0xf52dc1b2, 0xd62fda30), SECP256K1_FE_CONST(0x4aa77dd5, 0x5d6b6d3c, 0xfa10cc9d, 0x0fe42f79, 0x232e4575, 0x661049ae, 0x36779c1d, 0x0c666d88), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x4c251b48, 0x2307a71b, 0x39697fa8, 0x310d4ea9, 0xb9abcead, 0x9ac7e663, 0x0ad23e4c, 0x29d021ff), SECP256K1_FE_CONST(0xb558822a, 0xa29492c3, 0x05ef3362, 0xf01bd086, 0xdcd1ba8a, 0x99efb651, 0xc98863e1, 0xf3998ea7)}}, + {0x00, SECP256K1_FE_CONST(0x4295737e, 0xfcb1da6f, 0xb1d96b9c, 0xa7dcd1e3, 0x20024b37, 0xa736c494, 0x8b625981, 0x73069f70), SECP256K1_FE_CONST(0xfa7ffe4f, 0x25f88362, 0x831c087a, 0xfe2e8a9b, 0x0713e2ca, 0xc1ddca6a, 0x383205a2, 0x66f14307), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xff, SECP256K1_FE_CONST(0x587c1a0c, 0xee91939e, 0x7f784d23, 0xb963004a, 0x3bf44f5d, 0x4e32a008, 0x1995ba20, 0xb0fca59e), SECP256K1_FE_CONST(0x2ea98853, 0x0715e8d1, 0x0363907f, 0xf2512452, 0x4d471ba2, 0x454d5ce3, 0xbe3f0419, 0x4dfd3a3c), {SECP256K1_FE_CONST(0xcfd5a094, 0xaa0b9b88, 0x91b76c6a, 0xb9438f66, 0xaa1c095a, 0x65f9f701, 0x35e81712, 0x92245e74), SECP256K1_FE_CONST(0xa89057d7, 0xc6563f0d, 0x6efa19ae, 0x84412b8a, 0x7b47e791, 0xa191ecdf, 0xdf2af84f, 0xd97bc339), SECP256K1_FE_CONST(0x475d0ae9, 0xef46920d, 0xf07b3411, 0x7be5a081, 0x7de1023e, 0x3cc32689, 0xe9be145b, 0x406b0aef), SECP256K1_FE_CONST(0xa0759178, 0xad802324, 0x54f827ef, 0x05ea3e72, 0xad8d7541, 0x8e6d4cc1, 0xcd4f5306, 0xc5e7c453), SECP256K1_FE_CONST(0x302a5f6b, 0x55f46477, 0x6e489395, 0x46bc7099, 0x55e3f6a5, 0x9a0608fe, 0xca17e8ec, 0x6ddb9dbb), SECP256K1_FE_CONST(0x576fa828, 0x39a9c0f2, 0x9105e651, 0x7bbed475, 0x84b8186e, 0x5e6e1320, 0x20d507af, 0x268438f6), SECP256K1_FE_CONST(0xb8a2f516, 0x10b96df2, 0x0f84cbee, 0x841a5f7e, 0x821efdc1, 0xc33cd976, 0x1641eba3, 0xbf94f140), SECP256K1_FE_CONST(0x5f8a6e87, 0x527fdcdb, 0xab07d810, 0xfa15c18d, 0x52728abe, 0x7192b33e, 0x32b0acf8, 0x3a1837dc)}}, + {0xcc, SECP256K1_FE_CONST(0x5fa88b33, 0x65a635cb, 0xbcee003c, 0xce9ef51d, 0xd1a310de, 0x277e441a, 0xbccdb7be, 0x1e4ba249), SECP256K1_FE_CONST(0x79461ff6, 0x2bfcbcac, 0x4249ba84, 0xdd040f2c, 0xec3c63f7, 0x25204dc7, 0xf464c16b, 0xf0ff3170), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x6bb700e1, 0xf4d7e236, 0xe8d193ff, 0x4a76c1b3, 0xbcd4e2b2, 0x5acac3d5, 0x1c8dac65, 0x3fe909a0), SECP256K1_FE_CONST(0xf4c73410, 0x633da7f6, 0x3a4f1d55, 0xaec6dd32, 0xc4c6d89e, 0xe74075ed, 0xb5515ed9, 0x0da9e683), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x9448ff1e, 0x0b281dc9, 0x172e6c00, 0xb5893e4c, 0x432b1d4d, 0xa5353c2a, 0xe3725399, 0xc016f28f), SECP256K1_FE_CONST(0x0b38cbef, 0x9cc25809, 0xc5b0e2aa, 0x513922cd, 0x3b392761, 0x18bf8a12, 0x4aaea125, 0xf25615ac)}}, + {0xcc, SECP256K1_FE_CONST(0x6fb31c75, 0x31f03130, 0xb42b155b, 0x952779ef, 0xbb46087d, 0xd9807d24, 0x1a48eac6, 0x3c3d96d6), SECP256K1_FE_CONST(0x56f81be7, 0x53e8d4ae, 0x4940ea6f, 0x46f6ec9f, 0xda66a6f9, 0x6cc95f50, 0x6cb2b574, 0x90e94260), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x59059774, 0x795bdb7a, 0x837fbe11, 0x40a5fa59, 0x984f48af, 0x8df95d57, 0xdd6d1c05, 0x437dcec1), SECP256K1_FE_CONST(0x22a644db, 0x79376ad4, 0xe7b3a009, 0xe58b3f13, 0x137c54fd, 0xf911122c, 0xc93667c4, 0x7077d784), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xa6fa688b, 0x86a42485, 0x7c8041ee, 0xbf5a05a6, 0x67b0b750, 0x7206a2a8, 0x2292e3f9, 0xbc822d6e), SECP256K1_FE_CONST(0xdd59bb24, 0x86c8952b, 0x184c5ff6, 0x1a74c0ec, 0xec83ab02, 0x06eeedd3, 0x36c9983a, 0x8f8824ab)}}, + {0x00, SECP256K1_FE_CONST(0x704cd226, 0xe71cb682, 0x6a590e80, 0xdac90f2d, 0x2f5830f0, 0xfdf135a3, 0xeae3965b, 0xff25ff12), SECP256K1_FE_CONST(0x138e0afa, 0x68936ee6, 0x70bd2b8d, 0xb53aedbb, 0x7bea2a85, 0x97388b24, 0xd0518edd, 0x22ad66ec), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x725e9147, 0x92cb8c89, 0x49e7e116, 0x8b7cdd8a, 0x8094c91c, 0x6ec2202c, 0xcd53a6a1, 0x8771edeb), SECP256K1_FE_CONST(0x8da16eb8, 0x6d347376, 0xb6181ee9, 0x74832275, 0x7f6b36e3, 0x913ddfd3, 0x32ac595d, 0x788e0e44), {SECP256K1_FE_CONST(0xdd357786, 0xb9f68733, 0x30391aa5, 0x62580965, 0x4e43116e, 0x82a5a5d8, 0x2ffd1d66, 0x24101fc4), SECP256K1_FE_CONST(0xa0b7efca, 0x01814594, 0xc59c9aae, 0x8e497001, 0x86ca5d95, 0xe88bcc80, 0x399044d9, 0xc2d8613d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x22ca8879, 0x460978cc, 0xcfc6e55a, 0x9da7f69a, 0xb1bcee91, 0x7d5a5a27, 0xd002e298, 0xdbefdc6b), SECP256K1_FE_CONST(0x5f481035, 0xfe7eba6b, 0x3a636551, 0x71b68ffe, 0x7935a26a, 0x1774337f, 0xc66fbb25, 0x3d279af2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0x78fe6b71, 0x7f2ea4a3, 0x2708d79c, 0x151bf503, 0xa5312a18, 0xc0963437, 0xe865cc6e, 0xd3f6ae97), SECP256K1_FE_CONST(0x8701948e, 0x80d15b5c, 0xd8f72863, 0xeae40afc, 0x5aced5e7, 0x3f69cbc8, 0x179a3390, 0x2c094d98), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x44, SECP256K1_FE_CONST(0x7c37bb9c, 0x5061dc07, 0x413f11ac, 0xd5a34006, 0xe64c5c45, 0x7fdb9a43, 0x8f217255, 0xa961f50d), SECP256K1_FE_CONST(0x5c1a76b4, 0x4568eb59, 0xd6789a74, 0x42d9ed7c, 0xdc6226b7, 0x752b4ff8, 0xeaf8e1a9, 0x5736e507), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb94d30cd, 0x7dbff60b, 0x64620c17, 0xca0fafaa, 0x40b3d1f5, 0x2d077a60, 0xa2e0cafd, 0x145086c2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x46b2cf32, 0x824009f4, 0x9b9df3e8, 0x35f05055, 0xbf4c2e0a, 0xd2f8859f, 0x5d1f3501, 0xebaf756d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0x82388888, 0x967f82a6, 0xb444438a, 0x7d44838e, 0x13c0d478, 0xb9ca060d, 0xa95a41fb, 0x94303de6), SECP256K1_FE_CONST(0x29e96541, 0x70628fec, 0x8b497289, 0x8b113cf9, 0x8807f460, 0x9274f4f3, 0x140d0674, 0x157c90a0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0x91298f57, 0x70af7a27, 0xf0a47188, 0xd24c3b7b, 0xf98ab299, 0x0d84b0b8, 0x98507e3c, 0x561d6472), SECP256K1_FE_CONST(0x144f4ccb, 0xd9a74698, 0xa88cbf6f, 0xd00ad886, 0xd339d29e, 0xa19448f2, 0xc572cac0, 0xa07d5562), {SECP256K1_FE_CONST(0xe6a0ffa3, 0x807f09da, 0xdbe71e0f, 0x4be4725f, 0x2832e76c, 0xad8dc1d9, 0x43ce8393, 0x75eff248), SECP256K1_FE_CONST(0x837b8e68, 0xd4917544, 0x764ad090, 0x3cb11f86, 0x15d2823c, 0xefbb06d8, 0x9049dbab, 0xc69befda), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x195f005c, 0x7f80f625, 0x2418e1f0, 0xb41b8da0, 0xd7cd1893, 0x52723e26, 0xbc317c6b, 0x8a1009e7), SECP256K1_FE_CONST(0x7c847197, 0x2b6e8abb, 0x89b52f6f, 0xc34ee079, 0xea2d7dc3, 0x1044f927, 0x6fb62453, 0x39640c55), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0xb682f3d0, 0x3bbb5dee, 0x4f54b5eb, 0xfba931b4, 0xf52f6a19, 0x1e5c2f48, 0x3c73c66e, 0x9ace97e1), SECP256K1_FE_CONST(0x904717bf, 0x0bc0cb78, 0x73fcdc38, 0xaa97f19e, 0x3a626309, 0x72acff92, 0xb24cc6dd, 0xa197cb96), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x77, SECP256K1_FE_CONST(0xc17ec69e, 0x665f0fb0, 0xdbab48d9, 0xc2f94d12, 0xec8a9d7e, 0xacb58084, 0x83309180, 0x1eb0b80b), SECP256K1_FE_CONST(0x147756e6, 0x6d96e31c, 0x426d3cc8, 0x5ed0c4cf, 0xbef6341d, 0xd8b28558, 0x5aa574ea, 0x0204b55e), {SECP256K1_FE_CONST(0x6f4aea43, 0x1a0043bd, 0xd03134d6, 0xd9159119, 0xce034b88, 0xc32e50e8, 0xe36c4ee4, 0x5eac7ae9), SECP256K1_FE_CONST(0xfd5be16d, 0x4ffa2690, 0x126c67c3, 0xef7cb9d2, 0x9b74d397, 0xc78b06b3, 0x605fda34, 0xdc9696a6), SECP256K1_FE_CONST(0x5e9c6079, 0x2a2f000e, 0x45c6250f, 0x296f875e, 0x174efc0e, 0x9703e628, 0x706103a9, 0xdd2d82c7), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x90b515bc, 0xe5ffbc42, 0x2fcecb29, 0x26ea6ee6, 0x31fcb477, 0x3cd1af17, 0x1c93b11a, 0xa1538146), SECP256K1_FE_CONST(0x02a41e92, 0xb005d96f, 0xed93983c, 0x1083462d, 0x648b2c68, 0x3874f94c, 0x9fa025ca, 0x23696589), SECP256K1_FE_CONST(0xa1639f86, 0xd5d0fff1, 0xba39daf0, 0xd69078a1, 0xe8b103f1, 0x68fc19d7, 0x8f9efc55, 0x22d27968), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0xc25172fc, 0x3f29b6fc, 0x4a1155b8, 0x57523315, 0x5486b274, 0x64b74b8b, 0x260b499a, 0x3f53cb14), SECP256K1_FE_CONST(0x1ea9cbdb, 0x35cf6e03, 0x29aa31b0, 0xbb0a702a, 0x65123ed0, 0x08655a93, 0xb7dcd528, 0x0e52e1ab), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x7422edc7, 0x843136af, 0x0053bb88, 0x54448a82, 0x99994f9d, 0xdcefd3a9, 0xa92d4546, 0x2c59298a), SECP256K1_FE_CONST(0x78c7774a, 0x266f8b97, 0xea23d05d, 0x064f033c, 0x77319f92, 0x3f6b78bc, 0xe4e20bf0, 0x5fa5398d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x8bdd1238, 0x7bcec950, 0xffac4477, 0xabbb757d, 0x6666b062, 0x23102c56, 0x56d2bab8, 0xd3a6d2a5), SECP256K1_FE_CONST(0x873888b5, 0xd9907468, 0x15dc2fa2, 0xf9b0fcc3, 0x88ce606d, 0xc0948743, 0x1b1df40e, 0xa05ac2a2)}}, + {0x00, SECP256K1_FE_CONST(0xcab6626f, 0x832a4b12, 0x80ba7add, 0x2fc5322f, 0xf011caed, 0xedf7ff4d, 0xb6735d50, 0x26dc0367), SECP256K1_FE_CONST(0x2b2bef08, 0x52c6f7c9, 0x5d72ac99, 0xa23802b8, 0x75029cd5, 0x73b248d1, 0xf1b3fc80, 0x33788eb6), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x33, SECP256K1_FE_CONST(0xd8621b4f, 0xfc85b9ed, 0x56e99d8d, 0xd1dd24ae, 0xdcecb147, 0x63b861a1, 0x7112dc77, 0x1a104fd2), SECP256K1_FE_CONST(0x812cabe9, 0x72a22aa6, 0x7c7da0c9, 0x4d8a9362, 0x96eb9949, 0xd70c37cb, 0x2b248757, 0x4cb3ce58), {SECP256K1_FE_CONST(0xfbc5febc, 0x6fdbc9ae, 0x3eb88a93, 0xb982196e, 0x8b6275a6, 0xd5a73c17, 0x387e000c, 0x711bd0e3), SECP256K1_FE_CONST(0x8724c96b, 0xd4e5527f, 0x2dd195a5, 0x1c468d2d, 0x211ba2fa, 0xc7cbe0b4, 0xb3434253, 0x409fb42d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x043a0143, 0x90243651, 0xc147756c, 0x467de691, 0x749d8a59, 0x2a58c3e8, 0xc781fff2, 0x8ee42b4c), SECP256K1_FE_CONST(0x78db3694, 0x2b1aad80, 0xd22e6a5a, 0xe3b972d2, 0xdee45d05, 0x38341f4b, 0x4cbcbdab, 0xbf604802), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0xda463164, 0xc6f4bf71, 0x29ee5f0e, 0xc00f65a6, 0x75a8adf1, 0xbd931b39, 0xb64806af, 0xdcda9a22), SECP256K1_FE_CONST(0x25b9ce9b, 0x390b408e, 0xd611a0f1, 0x3ff09a59, 0x8a57520e, 0x426ce4c6, 0x49b7f94f, 0x2325620d), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0xdafc971e, 0x4a3a7b6d, 0xcfb42a08, 0xd9692d82, 0xad9e7838, 0x523fcbda, 0x1d4827e1, 0x4481ae2d), SECP256K1_FE_CONST(0x250368e1, 0xb5c58492, 0x304bd5f7, 0x2696d27d, 0x526187c7, 0xadc03425, 0xe2b7d81d, 0xbb7e4e02), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x370c28f1, 0xbe665efa, 0xcde6aa43, 0x6bf86fe2, 0x1e6e314c, 0x1e53dd04, 0x0e6c73a4, 0x6b4c8c49), SECP256K1_FE_CONST(0xcd8acee9, 0x8ffe5653, 0x1a84d7eb, 0x3e48fa40, 0x34206ce8, 0x25ace907, 0xd0edf0ea, 0xeb5e9ca2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xc8f3d70e, 0x4199a105, 0x321955bc, 0x9407901d, 0xe191ceb3, 0xe1ac22fb, 0xf1938c5a, 0x94b36fe6), SECP256K1_FE_CONST(0x32753116, 0x7001a9ac, 0xe57b2814, 0xc1b705bf, 0xcbdf9317, 0xda5316f8, 0x2f120f14, 0x14a15f8d)}}, + {0x44, SECP256K1_FE_CONST(0xe0294c8b, 0xc1a36b41, 0x66ee92bf, 0xa70a5c34, 0x976fa982, 0x9405efea, 0x8f9cd54d, 0xcb29b99e), SECP256K1_FE_CONST(0xae9690d1, 0x3b8d20a0, 0xfbbf37be, 0xd8474f67, 0xa04e142f, 0x56efd787, 0x70a76b35, 0x9165d8a1), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xdcd45d93, 0x5613916a, 0xf167b029, 0x058ba3a7, 0x00d37150, 0xb9df3472, 0x8cb05412, 0xc16d4182), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x232ba26c, 0xa9ec6e95, 0x0e984fd6, 0xfa745c58, 0xff2c8eaf, 0x4620cb8d, 0x734fabec, 0x3e92baad), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0x00, SECP256K1_FE_CONST(0xe148441c, 0xd7b92b8b, 0x0e4fa3bd, 0x68712cfd, 0x0d709ad1, 0x98cace61, 0x1493c10e, 0x97f5394e), SECP256K1_FE_CONST(0x164a6397, 0x94d74c53, 0xafc4d329, 0x4e79cdb3, 0xcd25f99f, 0x6df45c00, 0x0f758aba, 0x54d699c0), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xff, SECP256K1_FE_CONST(0xe4b00ec9, 0x7aadcca9, 0x7644d3b0, 0xc8a931b1, 0x4ce7bcf7, 0xbc877954, 0x6d6e35aa, 0x5937381c), SECP256K1_FE_CONST(0x94e9588d, 0x41647b3f, 0xcc772dc8, 0xd83c67ce, 0x3be00353, 0x8517c834, 0x103d2cd4, 0x9d62ef4d), {SECP256K1_FE_CONST(0xc88d25f4, 0x1407376b, 0xb2c03a7f, 0xffeb3ec7, 0x811cc434, 0x91a0c3aa, 0xc0378cdc, 0x78357bee), SECP256K1_FE_CONST(0x51c02636, 0xce00c234, 0x5ecd89ad, 0xb6089fe4, 0xd5e18ac9, 0x24e3145e, 0x6669501c, 0xd37a00d4), SECP256K1_FE_CONST(0x205b3512, 0xdb40521c, 0xb200952e, 0x67b46f67, 0xe09e7839, 0xe0de4400, 0x4138329e, 0xbd9138c5), SECP256K1_FE_CONST(0x58aab390, 0xab6fb55c, 0x1d1b8089, 0x7a207ce9, 0x4a78fa5b, 0x4aa61a33, 0x398bcae9, 0xadb20d3e), SECP256K1_FE_CONST(0x3772da0b, 0xebf8c894, 0x4d3fc580, 0x0014c138, 0x7ee33bcb, 0x6e5f3c55, 0x3fc87322, 0x87ca8041), SECP256K1_FE_CONST(0xae3fd9c9, 0x31ff3dcb, 0xa1327652, 0x49f7601b, 0x2a1e7536, 0xdb1ceba1, 0x9996afe2, 0x2c85fb5b), SECP256K1_FE_CONST(0xdfa4caed, 0x24bfade3, 0x4dff6ad1, 0x984b9098, 0x1f6187c6, 0x1f21bbff, 0xbec7cd60, 0x426ec36a), SECP256K1_FE_CONST(0xa7554c6f, 0x54904aa3, 0xe2e47f76, 0x85df8316, 0xb58705a4, 0xb559e5cc, 0xc6743515, 0x524deef1)}}, + {0x00, SECP256K1_FE_CONST(0xe5bbb9ef, 0x360d0a50, 0x1618f006, 0x7d36dceb, 0x75f5be9a, 0x620232aa, 0x9fd5139d, 0x0863fde5), SECP256K1_FE_CONST(0xe5bbb9ef, 0x360d0a50, 0x1618f006, 0x7d36dceb, 0x75f5be9a, 0x620232aa, 0x9fd5139d, 0x0863fde5), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xff, SECP256K1_FE_CONST(0xe6bcb5c3, 0xd63467d4, 0x90bfa54f, 0xbbc6092a, 0x7248c25e, 0x11b248dc, 0x2964a6e1, 0x5edb1457), SECP256K1_FE_CONST(0x19434a3c, 0x29cb982b, 0x6f405ab0, 0x4439f6d5, 0x8db73da1, 0xee4db723, 0xd69b591d, 0xa124e7d8), {SECP256K1_FE_CONST(0x67119877, 0x832ab8f4, 0x59a82165, 0x6d8261f5, 0x44a553b8, 0x9ae4f25c, 0x52a97134, 0xb70f3426), SECP256K1_FE_CONST(0xffee02f5, 0xe649c07f, 0x0560eff1, 0x867ec7b3, 0x2d0e595e, 0x9b1c0ea6, 0xe2a4fc70, 0xc97cd71f), SECP256K1_FE_CONST(0xb5e0c189, 0xeb5b4bac, 0xd025b744, 0x4d74178b, 0xe8d5246c, 0xfa4a9a20, 0x7964a057, 0xee969992), SECP256K1_FE_CONST(0x5746e459, 0x1bf7f4c3, 0x044609ea, 0x372e9086, 0x03975d27, 0x9fdef834, 0x9f0b08d3, 0x2f07619d), SECP256K1_FE_CONST(0x98ee6788, 0x7cd5470b, 0xa657de9a, 0x927d9e0a, 0xbb5aac47, 0x651b0da3, 0xad568eca, 0x48f0c809), SECP256K1_FE_CONST(0x0011fd0a, 0x19b63f80, 0xfa9f100e, 0x7981384c, 0xd2f1a6a1, 0x64e3f159, 0x1d5b038e, 0x36832510), SECP256K1_FE_CONST(0x4a1f3e76, 0x14a4b453, 0x2fda48bb, 0xb28be874, 0x172adb93, 0x05b565df, 0x869b5fa7, 0x1169629d), SECP256K1_FE_CONST(0xa8b91ba6, 0xe4080b3c, 0xfbb9f615, 0xc8d16f79, 0xfc68a2d8, 0x602107cb, 0x60f4f72b, 0xd0f89a92)}}, + {0x33, SECP256K1_FE_CONST(0xf28fba64, 0xaf766845, 0xeb2f4302, 0x456e2b9f, 0x8d80affe, 0x57e7aae4, 0x2738d7cd, 0xdb1c2ce6), SECP256K1_FE_CONST(0xf28fba64, 0xaf766845, 0xeb2f4302, 0x456e2b9f, 0x8d80affe, 0x57e7aae4, 0x2738d7cd, 0xdb1c2ce6), {SECP256K1_FE_CONST(0x4f867ad8, 0xbb3d8404, 0x09d26b67, 0x307e6210, 0x0153273f, 0x72fa4b74, 0x84becfa1, 0x4ebe7408), SECP256K1_FE_CONST(0x5bbc4f59, 0xe452cc5f, 0x22a99144, 0xb10ce898, 0x9a89a995, 0xec3cea1c, 0x91ae10e8, 0xf721bb5d), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xb0798527, 0x44c27bfb, 0xf62d9498, 0xcf819def, 0xfeacd8c0, 0x8d05b48b, 0x7b41305d, 0xb1418827), SECP256K1_FE_CONST(0xa443b0a6, 0x1bad33a0, 0xdd566ebb, 0x4ef31767, 0x6576566a, 0x13c315e3, 0x6e51ef16, 0x08de40d2), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, + {0xcc, SECP256K1_FE_CONST(0xf455605b, 0xc85bf48e, 0x3a908c31, 0x023faf98, 0x381504c6, 0xc6d3aeb9, 0xede55f8d, 0xd528924d), SECP256K1_FE_CONST(0xd31fbcd5, 0xcdb798f6, 0xc00db669, 0x2f8fe896, 0x7fa9c79d, 0xd10958f4, 0xa194f013, 0x74905e99), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0x0c00c571, 0x5b56fe63, 0x2d814ad8, 0xa77f8e66, 0x628ea47a, 0x6116834f, 0x8c1218f3, 0xa03cbd50), SECP256K1_FE_CONST(0xdf88e44f, 0xac84fa52, 0xdf4d59f4, 0x8819f18f, 0x6a8cd415, 0x1d162afa, 0xf773166f, 0x57c7ff46), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0xf3ff3a8e, 0xa4a9019c, 0xd27eb527, 0x58807199, 0x9d715b85, 0x9ee97cb0, 0x73ede70b, 0x5fc33edf), SECP256K1_FE_CONST(0x20771bb0, 0x537b05ad, 0x20b2a60b, 0x77e60e70, 0x95732bea, 0xe2e9d505, 0x088ce98f, 0xa837fce9)}}, + {0xff, SECP256K1_FE_CONST(0xf58cd4d9, 0x830bad32, 0x2699035e, 0x8246007d, 0x4be27e19, 0xb6f53621, 0x317b4f30, 0x9b3daa9d), SECP256K1_FE_CONST(0x78ec2b3d, 0xc0948de5, 0x60148bbc, 0x7c6dc963, 0x3ad5df70, 0xa5a5750c, 0xbed72180, 0x4f082a3b), {SECP256K1_FE_CONST(0x6c4c580b, 0x76c75940, 0x43569f9d, 0xae16dc28, 0x01c16a1f, 0xbe128608, 0x81b75f8e, 0xf929bce5), SECP256K1_FE_CONST(0x94231355, 0xe7385c5f, 0x25ca436a, 0xa6419147, 0x1aea4393, 0xd6e86ab7, 0xa35fe2af, 0xacaefd0d), SECP256K1_FE_CONST(0xdff2a195, 0x1ada6db5, 0x74df8340, 0x48149da3, 0x397a75b8, 0x29abf58c, 0x7e69db1b, 0x41ac0989), SECP256K1_FE_CONST(0xa52b66d3, 0xc9070355, 0x48028bf8, 0x04711bf4, 0x22aba95f, 0x1a666fc8, 0x6f4648e0, 0x5f29caae), SECP256K1_FE_CONST(0x93b3a7f4, 0x8938a6bf, 0xbca96062, 0x51e923d7, 0xfe3e95e0, 0x41ed79f7, 0x7e48a070, 0x06d63f4a), SECP256K1_FE_CONST(0x6bdcecaa, 0x18c7a3a0, 0xda35bc95, 0x59be6eb8, 0xe515bc6c, 0x29179548, 0x5ca01d4f, 0x5350ff22), SECP256K1_FE_CONST(0x200d5e6a, 0xe525924a, 0x8b207cbf, 0xb7eb625c, 0xc6858a47, 0xd6540a73, 0x819624e3, 0xbe53f2a6), SECP256K1_FE_CONST(0x5ad4992c, 0x36f8fcaa, 0xb7fd7407, 0xfb8ee40b, 0xdd5456a0, 0xe5999037, 0x90b9b71e, 0xa0d63181)}}, + {0x00, SECP256K1_FE_CONST(0xfd7d912a, 0x40f182a3, 0x588800d6, 0x9ebfb504, 0x8766da20, 0x6fd7ebc8, 0xd2436c81, 0xcbef6421), SECP256K1_FE_CONST(0x8d37c862, 0x054debe7, 0x31694536, 0xff46b273, 0xec122b35, 0xa9bf1445, 0xac3c4ff9, 0xf262c952), {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}}, +}; + +/* Set of (encoding, xcoord) test vectors, selected to maximize branch coverage, part of the BIP324 + * test vectors. Created using an independent implementation, and tested decoding against the paper + * authors' code. */ +static const struct ellswift_decode_test ellswift_decode_tests[] = { + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd3, 0x47, 0x5b, 0xf7, 0x65, 0x5b, 0x0f, 0xb2, 0xd8, 0x52, 0x92, 0x10, 0x35, 0xb2, 0xef, 0x60, 0x7f, 0x49, 0x06, 0x9b, 0x97, 0x45, 0x4e, 0x67, 0x95, 0x25, 0x10, 0x62, 0x74, 0x17, 0x71}, SECP256K1_FE_CONST(0xb5da00b7, 0x3cd65605, 0x20e7c364, 0x086e7cd2, 0x3a34bf60, 0xd0e707be, 0x9fc34d4c, 0xd5fdfa2c), 1}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x27, 0x7c, 0x4a, 0x71, 0xf9, 0xd2, 0x2e, 0x66, 0xec, 0xe5, 0x23, 0xf8, 0xfa, 0x08, 0x74, 0x1a, 0x7c, 0x09, 0x12, 0xc6, 0x6a, 0x69, 0xce, 0x68, 0x51, 0x4b, 0xfd, 0x35, 0x15, 0xb4, 0x9f}, SECP256K1_FE_CONST(0xf482f2e2, 0x41753ad0, 0xfb89150d, 0x8491dc1e, 0x34ff0b8a, 0xcfbb442c, 0xfe999e2e, 0x5e6fd1d2), 1}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x21, 0xcc, 0x93, 0x0e, 0x77, 0xc9, 0xf5, 0x14, 0xb6, 0x91, 0x5c, 0x3d, 0xbe, 0x2a, 0x94, 0xc6, 0xd8, 0xf6, 0x90, 0xb5, 0xb7, 0x39, 0x86, 0x4b, 0xa6, 0x78, 0x9f, 0xb8, 0xa5, 0x5d, 0xd0}, SECP256K1_FE_CONST(0x9f59c402, 0x75f5085a, 0x006f05da, 0xe77eb98c, 0x6fd0db1a, 0xb4a72ac4, 0x7eae90a4, 0xfc9e57e0), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41}, SECP256K1_FE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b), 1}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x9c, 0x18, 0x2d, 0x27, 0x59, 0xcd, 0x99, 0x82, 0x42, 0x28, 0xd9, 0x47, 0x99, 0xf8, 0xc6, 0x55, 0x7c, 0x38, 0xa1, 0xc0, 0xd6, 0x77, 0x9b, 0x9d, 0x4b, 0x72, 0x9c, 0x6f, 0x1c, 0xcc, 0x42}, SECP256K1_FE_CONST(0x70720db7, 0xe238d041, 0x21f5b1af, 0xd8cc5ad9, 0xd18944c6, 0xbdc94881, 0xf502b7a3, 0xaf3aecff), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x64, 0xbb, 0xd5}, SECP256K1_FE_CONST(0x50873db3, 0x1badcc71, 0x890e4f67, 0x753a6575, 0x7f97aaa7, 0xdd5f1e82, 0xb753ace3, 0x2219064b), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x28, 0xde, 0x7d}, SECP256K1_FE_CONST(0x1eea9cc5, 0x9cfcf2fa, 0x151ac6c2, 0x74eea411, 0x0feb4f7b, 0x68c59657, 0x32e9992e, 0x976ef68e), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xcf, 0xb7, 0xe7}, SECP256K1_FE_CONST(0x12303941, 0xaedc2088, 0x80735b1f, 0x1795c8e5, 0x5be520ea, 0x93e10335, 0x7b5d2adb, 0x7ed59b8e), 0}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x11, 0x3a, 0xd9}, SECP256K1_FE_CONST(0x7eed6b70, 0xe7b0767c, 0x7d7feac0, 0x4e57aa2a, 0x12fef5e0, 0xf48f878f, 0xcbb88b3b, 0x6b5e0783), 0}, + {{0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x532167c1, 0x1200b08c, 0x0e84a354, 0xe74dcc40, 0xf8b25f4f, 0xe686e308, 0x69526366, 0x278a0688), 0}, + {{0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x532167c1, 0x1200b08c, 0x0e84a354, 0xe74dcc40, 0xf8b25f4f, 0xe686e308, 0x69526366, 0x278a0688), 0}, + {{0x0f, 0xfd, 0xe9, 0xca, 0x81, 0xd7, 0x51, 0xe9, 0xcd, 0xaf, 0xfc, 0x1a, 0x50, 0x77, 0x92, 0x45, 0x32, 0x0b, 0x28, 0x99, 0x6d, 0xba, 0xf3, 0x2f, 0x82, 0x2f, 0x20, 0x11, 0x7c, 0x22, 0xfb, 0xd6, 0xc7, 0x4d, 0x99, 0xef, 0xce, 0xaa, 0x55, 0x0f, 0x1a, 0xd1, 0xc0, 0xf4, 0x3f, 0x46, 0xe7, 0xff, 0x1e, 0xe3, 0xbd, 0x01, 0x62, 0xb7, 0xbf, 0x55, 0xf2, 0x96, 0x5d, 0xa9, 0xc3, 0x45, 0x06, 0x46}, SECP256K1_FE_CONST(0x74e880b3, 0xffd18fe3, 0xcddf7902, 0x522551dd, 0xf97fa4a3, 0x5a3cfda8, 0x197f9470, 0x81a57b8f), 0}, + {{0x0f, 0xfd, 0xe9, 0xca, 0x81, 0xd7, 0x51, 0xe9, 0xcd, 0xaf, 0xfc, 0x1a, 0x50, 0x77, 0x92, 0x45, 0x32, 0x0b, 0x28, 0x99, 0x6d, 0xba, 0xf3, 0x2f, 0x82, 0x2f, 0x20, 0x11, 0x7c, 0x22, 0xfb, 0xd6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x6c, 0xa8, 0x96}, SECP256K1_FE_CONST(0x377b643f, 0xce2271f6, 0x4e5c8101, 0x566107c1, 0xbe498074, 0x50917838, 0x04f65478, 0x1ac9217c), 1}, + {{0x12, 0x36, 0x58, 0x44, 0x4f, 0x32, 0xbe, 0x8f, 0x02, 0xea, 0x20, 0x34, 0xaf, 0xa7, 0xef, 0x4b, 0xbe, 0x8a, 0xdc, 0x91, 0x8c, 0xeb, 0x49, 0xb1, 0x27, 0x73, 0xb6, 0x25, 0xf4, 0x90, 0xb3, 0x68, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8d, 0xc5, 0xfe, 0x11}, SECP256K1_FE_CONST(0xed16d65c, 0xf3a9538f, 0xcb2c139f, 0x1ecbc143, 0xee148271, 0x20cbc265, 0x9e667256, 0x800b8142), 0}, + {{0x14, 0x6f, 0x92, 0x46, 0x4d, 0x15, 0xd3, 0x6e, 0x35, 0x38, 0x2b, 0xd3, 0xca, 0x5b, 0x0f, 0x97, 0x6c, 0x95, 0xcb, 0x08, 0xac, 0xdc, 0xf2, 0xd5, 0xb3, 0x57, 0x06, 0x17, 0x99, 0x08, 0x39, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x31, 0x45, 0xe9, 0x3b}, SECP256K1_FE_CONST(0x0d5cd840, 0x427f941f, 0x65193079, 0xab8e2e83, 0x024ef2ee, 0x7ca558d8, 0x8879ffd8, 0x79fb6657), 0}, + {{0x15, 0xfd, 0xf5, 0xcf, 0x09, 0xc9, 0x07, 0x59, 0xad, 0xd2, 0x27, 0x2d, 0x57, 0x4d, 0x2b, 0xb5, 0xfe, 0x14, 0x29, 0xf9, 0xf3, 0xc1, 0x4c, 0x65, 0xe3, 0x19, 0x4b, 0xf6, 0x1b, 0x82, 0xaa, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0xcf, 0xd9, 0x06}, SECP256K1_FE_CONST(0x16d0e439, 0x46aec93f, 0x62d57eb8, 0xcde68951, 0xaf136cf4, 0xb307938d, 0xd1447411, 0xe07bffe1), 1}, + {{0x1f, 0x67, 0xed, 0xf7, 0x79, 0xa8, 0xa6, 0x49, 0xd6, 0xde, 0xf6, 0x00, 0x35, 0xf2, 0xfa, 0x22, 0xd0, 0x22, 0xdd, 0x35, 0x90, 0x79, 0xa1, 0xa1, 0x44, 0x07, 0x3d, 0x84, 0xf1, 0x9b, 0x92, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x025661f9, 0xaba9d15c, 0x3118456b, 0xbe980e3e, 0x1b8ba2e0, 0x47c737a4, 0xeb48a040, 0xbb566f6c), 0}, + {{0x1f, 0x67, 0xed, 0xf7, 0x79, 0xa8, 0xa6, 0x49, 0xd6, 0xde, 0xf6, 0x00, 0x35, 0xf2, 0xfa, 0x22, 0xd0, 0x22, 0xdd, 0x35, 0x90, 0x79, 0xa1, 0xa1, 0x44, 0x07, 0x3d, 0x84, 0xf1, 0x9b, 0x92, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x025661f9, 0xaba9d15c, 0x3118456b, 0xbe980e3e, 0x1b8ba2e0, 0x47c737a4, 0xeb48a040, 0xbb566f6c), 0}, + {{0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x98bec3b2, 0xa351fa96, 0xcfd191c1, 0x77835193, 0x1b9e9ba9, 0xad1149f6, 0xd9eadca8, 0x0981b801), 0}, + {{0x40, 0x56, 0xa3, 0x4a, 0x21, 0x0e, 0xec, 0x78, 0x92, 0xe8, 0x82, 0x06, 0x75, 0xc8, 0x60, 0x09, 0x9f, 0x85, 0x7b, 0x26, 0xaa, 0xd8, 0x54, 0x70, 0xee, 0x6d, 0x3c, 0xf1, 0x30, 0x4a, 0x9d, 0xcf, 0x37, 0x5e, 0x70, 0x37, 0x42, 0x71, 0xf2, 0x0b, 0x13, 0xc9, 0x98, 0x6e, 0xd7, 0xd3, 0xc1, 0x77, 0x99, 0x69, 0x8c, 0xfc, 0x43, 0x5d, 0xbe, 0xd3, 0xa9, 0xf3, 0x4b, 0x38, 0xc8, 0x23, 0xc2, 0xb4}, SECP256K1_FE_CONST(0x868aac20, 0x03b29dbc, 0xad1a3e80, 0x3855e078, 0xa89d1654, 0x3ac64392, 0xd1224172, 0x98cec76e), 0}, + {{0x41, 0x97, 0xec, 0x37, 0x23, 0xc6, 0x54, 0xcf, 0xdd, 0x32, 0xab, 0x07, 0x55, 0x06, 0x64, 0x8b, 0x2f, 0xf5, 0x07, 0x03, 0x62, 0xd0, 0x1a, 0x4f, 0xff, 0x14, 0xb3, 0x36, 0xb7, 0x8f, 0x96, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb3, 0xab, 0x1e, 0x95}, SECP256K1_FE_CONST(0xba5a6314, 0x502a8952, 0xb8f456e0, 0x85928105, 0xf665377a, 0x8ce27726, 0xa5b0eb7e, 0xc1ac0286), 0}, + {{0x47, 0xeb, 0x3e, 0x20, 0x8f, 0xed, 0xcd, 0xf8, 0x23, 0x4c, 0x94, 0x21, 0xe9, 0xcd, 0x9a, 0x7a, 0xe8, 0x73, 0xbf, 0xbd, 0xbc, 0x39, 0x37, 0x23, 0xd1, 0xba, 0x1e, 0x1e, 0x6a, 0x8e, 0x6b, 0x24, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7c, 0xd1, 0x2c, 0xb1}, SECP256K1_FE_CONST(0xd192d520, 0x07e541c9, 0x807006ed, 0x0468df77, 0xfd214af0, 0xa795fe11, 0x9359666f, 0xdcf08f7c), 0}, + {{0x5e, 0xb9, 0x69, 0x6a, 0x23, 0x36, 0xfe, 0x2c, 0x3c, 0x66, 0x6b, 0x02, 0xc7, 0x55, 0xdb, 0x4c, 0x0c, 0xfd, 0x62, 0x82, 0x5c, 0x7b, 0x58, 0x9a, 0x7b, 0x7b, 0xb4, 0x42, 0xe1, 0x41, 0xc1, 0xd6, 0x93, 0x41, 0x3f, 0x00, 0x52, 0xd4, 0x9e, 0x64, 0xab, 0xec, 0x6d, 0x58, 0x31, 0xd6, 0x6c, 0x43, 0x61, 0x28, 0x30, 0xa1, 0x7d, 0xf1, 0xfe, 0x43, 0x83, 0xdb, 0x89, 0x64, 0x68, 0x10, 0x02, 0x21}, SECP256K1_FE_CONST(0xef6e1da6, 0xd6c7627e, 0x80f7a723, 0x4cb08a02, 0x2c1ee1cf, 0x29e4d0f9, 0x642ae924, 0xcef9eb38), 1}, + {{0x7b, 0xf9, 0x6b, 0x7b, 0x6d, 0xa1, 0x5d, 0x34, 0x76, 0xa2, 0xb1, 0x95, 0x93, 0x4b, 0x69, 0x0a, 0x3a, 0x3d, 0xe3, 0xe8, 0xab, 0x84, 0x74, 0x85, 0x68, 0x63, 0xb0, 0xde, 0x3a, 0xf9, 0x0b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x50851dfc, 0x9f418c31, 0x4a437295, 0xb24feeea, 0x27af3d0c, 0xd2308348, 0xfda6e21c, 0x463e46ff), 0}, + {{0x7b, 0xf9, 0x6b, 0x7b, 0x6d, 0xa1, 0x5d, 0x34, 0x76, 0xa2, 0xb1, 0x95, 0x93, 0x4b, 0x69, 0x0a, 0x3a, 0x3d, 0xe3, 0xe8, 0xab, 0x84, 0x74, 0x85, 0x68, 0x63, 0xb0, 0xde, 0x3a, 0xf9, 0x0b, 0x0e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x50851dfc, 0x9f418c31, 0x4a437295, 0xb24feeea, 0x27af3d0c, 0xd2308348, 0xfda6e21c, 0x463e46ff), 0}, + {{0x85, 0x1b, 0x1c, 0xa9, 0x45, 0x49, 0x37, 0x1c, 0x4f, 0x1f, 0x71, 0x87, 0x32, 0x1d, 0x39, 0xbf, 0x51, 0xc6, 0xb7, 0xfb, 0x61, 0xf7, 0xcb, 0xf0, 0x27, 0xc9, 0xda, 0x62, 0x02, 0x1b, 0x7a, 0x65, 0xfc, 0x54, 0xc9, 0x68, 0x37, 0xfb, 0x22, 0xb3, 0x62, 0xed, 0xa6, 0x3e, 0xc5, 0x2e, 0xc8, 0x3d, 0x81, 0xbe, 0xdd, 0x16, 0x0c, 0x11, 0xb2, 0x2d, 0x96, 0x5d, 0x9f, 0x4a, 0x6d, 0x64, 0xd2, 0x51}, SECP256K1_FE_CONST(0x3e731051, 0xe12d3323, 0x7eb324f2, 0xaa5b16bb, 0x868eb49a, 0x1aa1fadc, 0x19b6e876, 0x1b5a5f7b), 1}, + {{0x94, 0x3c, 0x2f, 0x77, 0x51, 0x08, 0xb7, 0x37, 0xfe, 0x65, 0xa9, 0x53, 0x1e, 0x19, 0xf2, 0xfc, 0x2a, 0x19, 0x7f, 0x56, 0x03, 0xe3, 0xa2, 0x88, 0x1d, 0x1d, 0x83, 0xe4, 0x00, 0x8f, 0x91, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x311c61f0, 0xab2f32b7, 0xb1f0223f, 0xa72f0a78, 0x752b8146, 0xe46107f8, 0x876dd9c4, 0xf92b2942), 0}, + {{0x94, 0x3c, 0x2f, 0x77, 0x51, 0x08, 0xb7, 0x37, 0xfe, 0x65, 0xa9, 0x53, 0x1e, 0x19, 0xf2, 0xfc, 0x2a, 0x19, 0x7f, 0x56, 0x03, 0xe3, 0xa2, 0x88, 0x1d, 0x1d, 0x83, 0xe4, 0x00, 0x8f, 0x91, 0x25, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x311c61f0, 0xab2f32b7, 0xb1f0223f, 0xa72f0a78, 0x752b8146, 0xe46107f8, 0x876dd9c4, 0xf92b2942), 0}, + {{0xa0, 0xf1, 0x84, 0x92, 0x18, 0x3e, 0x61, 0xe8, 0x06, 0x3e, 0x57, 0x36, 0x06, 0x59, 0x14, 0x21, 0xb0, 0x6b, 0xc3, 0x51, 0x36, 0x31, 0x57, 0x8a, 0x73, 0xa3, 0x9c, 0x1c, 0x33, 0x06, 0x23, 0x9f, 0x2f, 0x32, 0x90, 0x4f, 0x0d, 0x2a, 0x33, 0xec, 0xca, 0x8a, 0x54, 0x51, 0x70, 0x5b, 0xb5, 0x37, 0xd3, 0xbf, 0x44, 0xe0, 0x71, 0x22, 0x60, 0x25, 0xcd, 0xbf, 0xd2, 0x49, 0xfe, 0x0f, 0x7a, 0xd6}, SECP256K1_FE_CONST(0x97a09cf1, 0xa2eae7c4, 0x94df3c6f, 0x8a9445bf, 0xb8c09d60, 0x832f9b0b, 0x9d5eabe2, 0x5fbd14b9), 0}, + {{0xa1, 0xed, 0x0a, 0x0b, 0xd7, 0x9d, 0x8a, 0x23, 0xcf, 0xe4, 0xec, 0x5f, 0xef, 0x5b, 0xa5, 0xcc, 0xcf, 0xd8, 0x44, 0xe4, 0xff, 0x5c, 0xb4, 0xb0, 0xf2, 0xe7, 0x16, 0x27, 0x34, 0x1f, 0x1c, 0x5b, 0x17, 0xc4, 0x99, 0x24, 0x9e, 0x0a, 0xc0, 0x8d, 0x5d, 0x11, 0xea, 0x1c, 0x2c, 0x8c, 0xa7, 0x00, 0x16, 0x16, 0x55, 0x9a, 0x79, 0x94, 0xea, 0xde, 0xc9, 0xca, 0x10, 0xfb, 0x4b, 0x85, 0x16, 0xdc}, SECP256K1_FE_CONST(0x65a89640, 0x744192cd, 0xac64b2d2, 0x1ddf989c, 0xdac75007, 0x25b645be, 0xf8e2200a, 0xe39691f2), 0}, + {{0xba, 0x94, 0x59, 0x4a, 0x43, 0x27, 0x21, 0xaa, 0x35, 0x80, 0xb8, 0x4c, 0x16, 0x1d, 0x0d, 0x13, 0x4b, 0xc3, 0x54, 0xb6, 0x90, 0x40, 0x4d, 0x7c, 0xd4, 0xec, 0x57, 0xc1, 0x6d, 0x3f, 0xbe, 0x98, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0x50, 0x7d, 0xd7}, SECP256K1_FE_CONST(0x5e0d7656, 0x4aae92cb, 0x347e01a6, 0x2afd389a, 0x9aa401c7, 0x6c8dd227, 0x543dc9cd, 0x0efe685a), 0}, + {{0xbc, 0xaf, 0x72, 0x19, 0xf2, 0xf6, 0xfb, 0xf5, 0x5f, 0xe5, 0xe0, 0x62, 0xdc, 0xe0, 0xe4, 0x8c, 0x18, 0xf6, 0x81, 0x03, 0xf1, 0x0b, 0x81, 0x98, 0xe9, 0x74, 0xc1, 0x84, 0x75, 0x0e, 0x1b, 0xe3, 0x93, 0x20, 0x16, 0xcb, 0xf6, 0x9c, 0x44, 0x71, 0xbd, 0x1f, 0x65, 0x6c, 0x6a, 0x10, 0x7f, 0x19, 0x73, 0xde, 0x4a, 0xf7, 0x08, 0x6d, 0xb8, 0x97, 0x27, 0x70, 0x60, 0xe2, 0x56, 0x77, 0xf1, 0x9a}, SECP256K1_FE_CONST(0x2d97f96c, 0xac882dfe, 0x73dc44db, 0x6ce0f1d3, 0x1d624135, 0x8dd5d74e, 0xb3d3b500, 0x03d24c2b), 0}, + {{0xbc, 0xaf, 0x72, 0x19, 0xf2, 0xf6, 0xfb, 0xf5, 0x5f, 0xe5, 0xe0, 0x62, 0xdc, 0xe0, 0xe4, 0x8c, 0x18, 0xf6, 0x81, 0x03, 0xf1, 0x0b, 0x81, 0x98, 0xe9, 0x74, 0xc1, 0x84, 0x75, 0x0e, 0x1b, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x65, 0x07, 0xd0, 0x9a}, SECP256K1_FE_CONST(0xe7008afe, 0x6e8cbd50, 0x55df120b, 0xd748757c, 0x686dadb4, 0x1cce75e4, 0xaddcc5e0, 0x2ec02b44), 1}, + {{0xc5, 0x98, 0x1b, 0xae, 0x27, 0xfd, 0x84, 0x40, 0x1c, 0x72, 0xa1, 0x55, 0xe5, 0x70, 0x7f, 0xbb, 0x81, 0x1b, 0x2b, 0x62, 0x06, 0x45, 0xd1, 0x02, 0x8e, 0xa2, 0x70, 0xcb, 0xe0, 0xee, 0x22, 0x5d, 0x4b, 0x62, 0xaa, 0x4d, 0xca, 0x65, 0x06, 0xc1, 0xac, 0xdb, 0xec, 0xc0, 0x55, 0x25, 0x69, 0xb4, 0xb2, 0x14, 0x36, 0xa5, 0x69, 0x2e, 0x25, 0xd9, 0x0d, 0x3b, 0xc2, 0xeb, 0x7c, 0xe2, 0x40, 0x78}, SECP256K1_FE_CONST(0x948b40e7, 0x181713bc, 0x018ec170, 0x2d3d054d, 0x15746c59, 0xa7020730, 0xdd13ecf9, 0x85a010d7), 0}, + {{0xc8, 0x94, 0xce, 0x48, 0xbf, 0xec, 0x43, 0x30, 0x14, 0xb9, 0x31, 0xa6, 0xad, 0x42, 0x26, 0xd7, 0xdb, 0xd8, 0xea, 0xa7, 0xb6, 0xe3, 0xfa, 0xa8, 0xd0, 0xef, 0x94, 0x05, 0x2b, 0xcf, 0x8c, 0xff, 0x33, 0x6e, 0xeb, 0x39, 0x19, 0xe2, 0xb4, 0xef, 0xb7, 0x46, 0xc7, 0xf7, 0x1b, 0xbc, 0xa7, 0xe9, 0x38, 0x32, 0x30, 0xfb, 0xbc, 0x48, 0xff, 0xaf, 0xe7, 0x7e, 0x8b, 0xcc, 0x69, 0x54, 0x24, 0x71}, SECP256K1_FE_CONST(0xf1c91acd, 0xc2525330, 0xf9b53158, 0x434a4d43, 0xa1c547cf, 0xf29f1550, 0x6f5da4eb, 0x4fe8fa5a), 1}, + {{0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x872d81ed, 0x8831d999, 0x8b67cb71, 0x05243edb, 0xf86c10ed, 0xfebb786c, 0x110b02d0, 0x7b2e67cd), 0}, + {{0xd9, 0x17, 0xb7, 0x86, 0xda, 0xc3, 0x56, 0x70, 0xc3, 0x30, 0xc9, 0xc5, 0xae, 0x59, 0x71, 0xdf, 0xb4, 0x95, 0xc8, 0xae, 0x52, 0x3e, 0xd9, 0x7e, 0xe2, 0x42, 0x01, 0x17, 0xb1, 0x71, 0xf4, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x01, 0xf6, 0xf6}, SECP256K1_FE_CONST(0xe45b71e1, 0x10b831f2, 0xbdad8651, 0x994526e5, 0x8393fde4, 0x328b1ec0, 0x4d598971, 0x42584691), 1}, + {{0xe2, 0x8b, 0xd8, 0xf5, 0x92, 0x9b, 0x46, 0x7e, 0xb7, 0x0e, 0x04, 0x33, 0x23, 0x74, 0xff, 0xb7, 0xe7, 0x18, 0x02, 0x18, 0xad, 0x16, 0xea, 0xa4, 0x6b, 0x71, 0x61, 0xaa, 0x67, 0x9e, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x66b8c980, 0xa75c72e5, 0x98d383a3, 0x5a62879f, 0x844242ad, 0x1e73ff12, 0xedaa59f4, 0xe58632b5), 0}, + {{0xe2, 0x8b, 0xd8, 0xf5, 0x92, 0x9b, 0x46, 0x7e, 0xb7, 0x0e, 0x04, 0x33, 0x23, 0x74, 0xff, 0xb7, 0xe7, 0x18, 0x02, 0x18, 0xad, 0x16, 0xea, 0xa4, 0x6b, 0x71, 0x61, 0xaa, 0x67, 0x9e, 0xb4, 0x26, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x66b8c980, 0xa75c72e5, 0x98d383a3, 0x5a62879f, 0x844242ad, 0x1e73ff12, 0xedaa59f4, 0xe58632b5), 0}, + {{0xe7, 0xee, 0x58, 0x14, 0xc1, 0x70, 0x6b, 0xf8, 0xa8, 0x93, 0x96, 0xa9, 0xb0, 0x32, 0xbc, 0x01, 0x4c, 0x2c, 0xac, 0x9c, 0x12, 0x11, 0x27, 0xdb, 0xf6, 0xc9, 0x92, 0x78, 0xf8, 0xbb, 0x53, 0xd1, 0xdf, 0xd0, 0x4d, 0xbc, 0xda, 0x8e, 0x35, 0x24, 0x66, 0xb6, 0xfc, 0xd5, 0xf2, 0xde, 0xa3, 0xe1, 0x7d, 0x5e, 0x13, 0x31, 0x15, 0x88, 0x6e, 0xda, 0x20, 0xdb, 0x8a, 0x12, 0xb5, 0x4d, 0xe7, 0x1b}, SECP256K1_FE_CONST(0xe842c6e3, 0x529b2342, 0x70a5e977, 0x44edc34a, 0x04d7ba94, 0xe44b6d25, 0x23c9cf01, 0x95730a50), 1}, + {{0xf2, 0x92, 0xe4, 0x68, 0x25, 0xf9, 0x22, 0x5a, 0xd2, 0x3d, 0xc0, 0x57, 0xc1, 0xd9, 0x1c, 0x4f, 0x57, 0xfc, 0xb1, 0x38, 0x6f, 0x29, 0xef, 0x10, 0x48, 0x1c, 0xb1, 0xd2, 0x25, 0x18, 0x59, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x11, 0xc9, 0x89}, SECP256K1_FE_CONST(0x3cea2c53, 0xb8b01701, 0x66ac7da6, 0x7194694a, 0xdacc84d5, 0x6389225e, 0x330134da, 0xb85a4d55), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x01, 0xd3, 0x47, 0x5b, 0xf7, 0x65, 0x5b, 0x0f, 0xb2, 0xd8, 0x52, 0x92, 0x10, 0x35, 0xb2, 0xef, 0x60, 0x7f, 0x49, 0x06, 0x9b, 0x97, 0x45, 0x4e, 0x67, 0x95, 0x25, 0x10, 0x62, 0x74, 0x17, 0x71}, SECP256K1_FE_CONST(0xb5da00b7, 0x3cd65605, 0x20e7c364, 0x086e7cd2, 0x3a34bf60, 0xd0e707be, 0x9fc34d4c, 0xd5fdfa2c), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee}, SECP256K1_FE_CONST(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaa9, 0xfffffd6b), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x82, 0x27, 0x7c, 0x4a, 0x71, 0xf9, 0xd2, 0x2e, 0x66, 0xec, 0xe5, 0x23, 0xf8, 0xfa, 0x08, 0x74, 0x1a, 0x7c, 0x09, 0x12, 0xc6, 0x6a, 0x69, 0xce, 0x68, 0x51, 0x4b, 0xfd, 0x35, 0x15, 0xb4, 0x9f}, SECP256K1_FE_CONST(0xf482f2e2, 0x41753ad0, 0xfb89150d, 0x8491dc1e, 0x34ff0b8a, 0xcfbb442c, 0xfe999e2e, 0x5e6fd1d2), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x84, 0x21, 0xcc, 0x93, 0x0e, 0x77, 0xc9, 0xf5, 0x14, 0xb6, 0x91, 0x5c, 0x3d, 0xbe, 0x2a, 0x94, 0xc6, 0xd8, 0xf6, 0x90, 0xb5, 0xb7, 0x39, 0x86, 0x4b, 0xa6, 0x78, 0x9f, 0xb8, 0xa5, 0x5d, 0xd0}, SECP256K1_FE_CONST(0x9f59c402, 0x75f5085a, 0x006f05da, 0xe77eb98c, 0x6fd0db1a, 0xb4a72ac4, 0x7eae90a4, 0xfc9e57e0), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xd1, 0x9c, 0x18, 0x2d, 0x27, 0x59, 0xcd, 0x99, 0x82, 0x42, 0x28, 0xd9, 0x47, 0x99, 0xf8, 0xc6, 0x55, 0x7c, 0x38, 0xa1, 0xc0, 0xd6, 0x77, 0x9b, 0x9d, 0x4b, 0x72, 0x9c, 0x6f, 0x1c, 0xcc, 0x42}, SECP256K1_FE_CONST(0x70720db7, 0xe238d041, 0x21f5b1af, 0xd8cc5ad9, 0xd18944c6, 0xbdc94881, 0xf502b7a3, 0xaf3aecff), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0xedd1fd3e, 0x327ce90c, 0xc7a35426, 0x14289aee, 0x9682003e, 0x9cf7dcc9, 0xcf2ca974, 0x3be5aa0c), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x26, 0x64, 0xbb, 0xd5}, SECP256K1_FE_CONST(0x50873db3, 0x1badcc71, 0x890e4f67, 0x753a6575, 0x7f97aaa7, 0xdd5f1e82, 0xb753ace3, 0x2219064b), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x28, 0xde, 0x7d}, SECP256K1_FE_CONST(0x1eea9cc5, 0x9cfcf2fa, 0x151ac6c2, 0x74eea411, 0x0feb4f7b, 0x68c59657, 0x32e9992e, 0x976ef68e), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xcf, 0xb7, 0xe7}, SECP256K1_FE_CONST(0x12303941, 0xaedc2088, 0x80735b1f, 0x1795c8e5, 0x5be520ea, 0x93e10335, 0x7b5d2adb, 0x7ed59b8e), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x11, 0x3a, 0xd9}, SECP256K1_FE_CONST(0x7eed6b70, 0xe7b0767c, 0x7d7feac0, 0x4e57aa2a, 0x12fef5e0, 0xf48f878f, 0xcbb88b3b, 0x6b5e0783), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13, 0xce, 0xa4, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x64998443, 0x5b62b4a2, 0x5d40c613, 0x3e8d9ab8, 0xc53d4b05, 0x9ee8a154, 0xa3be0fcf, 0x4e892edb), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x13, 0xce, 0xa4, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x64998443, 0x5b62b4a2, 0x5d40c613, 0x3e8d9ab8, 0xc53d4b05, 0x9ee8a154, 0xa3be0fcf, 0x4e892edb), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x02, 0x8c, 0x59, 0x00, 0x63, 0xf6, 0x4d, 0x5a, 0x7f, 0x1c, 0x14, 0x91, 0x5c, 0xd6, 0x1e, 0xac, 0x88, 0x6a, 0xb2, 0x95, 0xbe, 0xbd, 0x91, 0x99, 0x25, 0x04, 0xcf, 0x77, 0xed, 0xb0, 0x28, 0xbd, 0xd6, 0x26, 0x7f}, SECP256K1_FE_CONST(0x3fde5713, 0xf8282eea, 0xd7d39d42, 0x01f44a7c, 0x85a5ac8a, 0x0681f35e, 0x54085c6b, 0x69543374), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0x15, 0xde, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x3524f77f, 0xa3a6eb43, 0x89c3cb5d, 0x27f1f914, 0x62086429, 0xcd6c0cb0, 0xdf43ea8f, 0x1e7b3fb4), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0x15, 0xde, 0x86, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x3524f77f, 0xa3a6eb43, 0x89c3cb5d, 0x27f1f914, 0x62086429, 0xcd6c0cb0, 0xdf43ea8f, 0x1e7b3fb4), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x2c, 0x57, 0x09, 0xe7, 0x15, 0x6c, 0x41, 0x77, 0x17, 0xf2, 0xfe, 0xab, 0x14, 0x71, 0x41, 0xec, 0x3d, 0xa1, 0x9f, 0xb7, 0x59, 0x57, 0x5c, 0xc6, 0xe3, 0x7b, 0x2e, 0xa5, 0xac, 0x93, 0x09, 0xf2, 0x6f, 0x0f, 0x66}, SECP256K1_FE_CONST(0xd2469ab3, 0xe04acbb2, 0x1c65a180, 0x9f39caaf, 0xe7a77c13, 0xd10f9dd3, 0x8f391c01, 0xdc499c52), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x08, 0xcc, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x60, 0xe9, 0xf0}, SECP256K1_FE_CONST(0x38e2a5ce, 0x6a93e795, 0xe16d2c39, 0x8bc99f03, 0x69202ce2, 0x1e8f09d5, 0x6777b40f, 0xc512bccc), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0x91, 0x25, 0x7d, 0x93, 0x20, 0x16, 0xcb, 0xf6, 0x9c, 0x44, 0x71, 0xbd, 0x1f, 0x65, 0x6c, 0x6a, 0x10, 0x7f, 0x19, 0x73, 0xde, 0x4a, 0xf7, 0x08, 0x6d, 0xb8, 0x97, 0x27, 0x70, 0x60, 0xe2, 0x56, 0x77, 0xf1, 0x9a}, SECP256K1_FE_CONST(0x864b3dc9, 0x02c37670, 0x9c10a93a, 0xd4bbe29f, 0xce0012f3, 0xdc8672c6, 0x286bba28, 0xd7d6d6fc), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0x5d, 0x6c, 0x1c, 0x32, 0x2c, 0xad, 0xf5, 0x99, 0xdb, 0xb8, 0x64, 0x81, 0x52, 0x2b, 0x3c, 0xc5, 0x5f, 0x15, 0xa6, 0x79, 0x32, 0xdb, 0x2a, 0xfa, 0x01, 0x11, 0xd9, 0xed, 0x69, 0x81, 0xbc, 0xd1, 0x24, 0xbf, 0x44}, SECP256K1_FE_CONST(0x766dfe4a, 0x700d9bee, 0x288b903a, 0xd58870e3, 0xd4fe2f0e, 0xf780bcac, 0x5c823f32, 0x0d9a9bef), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e, 0x42, 0x6f, 0x03, 0x92, 0x38, 0x90, 0x78, 0xc1, 0x2b, 0x1a, 0x89, 0xe9, 0x54, 0x2f, 0x05, 0x93, 0xbc, 0x96, 0xb6, 0xbf, 0xde, 0x82, 0x24, 0xf8, 0x65, 0x4e, 0xf5, 0xd5, 0xcd, 0xa9, 0x35, 0xa3, 0x58, 0x21, 0x94}, SECP256K1_FE_CONST(0xfaec7bc1, 0x987b6323, 0x3fbc5f95, 0x6edbf37d, 0x54404e74, 0x61c58ab8, 0x631bc68e, 0x451a0478), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x91, 0x19, 0x21, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45, 0xf0, 0xf1, 0xeb}, SECP256K1_FE_CONST(0xec29a50b, 0xae138dbf, 0x7d8e2482, 0x5006bb5f, 0xc1a2cc12, 0x43ba335b, 0xc6116fb9, 0xe498ec1f), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x98, 0xeb, 0x9a, 0xb7, 0x6e, 0x84, 0x49, 0x9c, 0x48, 0x3b, 0x3b, 0xf0, 0x62, 0x14, 0xab, 0xfe, 0x06, 0x5d, 0xdd, 0xf4, 0x3b, 0x86, 0x01, 0xde, 0x59, 0x6d, 0x63, 0xb9, 0xe4, 0x5a, 0x16, 0x6a, 0x58, 0x05, 0x41, 0xfe}, SECP256K1_FE_CONST(0x1e0ff2de, 0xe9b09b13, 0x6292a9e9, 0x10f0d6ac, 0x3e552a64, 0x4bba39e6, 0x4e9dd3e3, 0xbbd3d4d4), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x77, 0xb7, 0xf2, 0xc7, 0x4d, 0x99, 0xef, 0xce, 0xaa, 0x55, 0x0f, 0x1a, 0xd1, 0xc0, 0xf4, 0x3f, 0x46, 0xe7, 0xff, 0x1e, 0xe3, 0xbd, 0x01, 0x62, 0xb7, 0xbf, 0x55, 0xf2, 0x96, 0x5d, 0xa9, 0xc3, 0x45, 0x06, 0x46}, SECP256K1_FE_CONST(0x8b7dd5c3, 0xedba9ee9, 0x7b70eff4, 0x38f22dca, 0x9849c825, 0x4a2f3345, 0xa0a572ff, 0xeaae0928), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9b, 0x77, 0xb7, 0xf2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x6c, 0xa8, 0x96}, SECP256K1_FE_CONST(0x0881950c, 0x8f51d6b9, 0xa6387465, 0xd5f12609, 0xef1bb254, 0x12a08a74, 0xcb2dfb20, 0x0c74bfbf), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa2, 0xf5, 0xcd, 0x83, 0x88, 0x16, 0xc1, 0x6c, 0x4f, 0xe8, 0xa1, 0x66, 0x1d, 0x60, 0x6f, 0xdb, 0x13, 0xcf, 0x9a, 0xf0, 0x4b, 0x97, 0x9a, 0x2e, 0x15, 0x9a, 0x09, 0x40, 0x9e, 0xbc, 0x86, 0x45, 0xd5, 0x8f, 0xde, 0x02}, SECP256K1_FE_CONST(0x2f083207, 0xb9fd9b55, 0x0063c31c, 0xd62b8746, 0xbd543bdc, 0x5bbf10e3, 0xa35563e9, 0x27f440c8), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x3f, 0x75, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x4f51e0be, 0x078e0cdd, 0xab274215, 0x6adba7e7, 0xa148e731, 0x57072fd6, 0x18cd6094, 0x2b146bd0), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x3f, 0x75, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x4f51e0be, 0x078e0cdd, 0xab274215, 0x6adba7e7, 0xa148e731, 0x57072fd6, 0x18cd6094, 0x2b146bd0), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xbc, 0x1f, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SECP256K1_FE_CONST(0x16c2ccb5, 0x4352ff4b, 0xd794f6ef, 0xd613c721, 0x97ab7082, 0xda5b563b, 0xdf9cb3ed, 0xaafe74c2), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xbc, 0x1f, 0x8d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f}, SECP256K1_FE_CONST(0x16c2ccb5, 0x4352ff4b, 0xd794f6ef, 0xd613c721, 0x97ab7082, 0xda5b563b, 0xdf9cb3ed, 0xaafe74c2), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x64, 0xd1, 0x62, 0x75, 0x05, 0x46, 0xce, 0x42, 0xb0, 0x43, 0x13, 0x61, 0xe5, 0x2d, 0x4f, 0x52, 0x42, 0xd8, 0xf2, 0x4f, 0x33, 0xe6, 0xb1, 0xf9, 0x9b, 0x59, 0x16, 0x47, 0xcb, 0xc8, 0x08, 0xf4, 0x62, 0xaf, 0x51}, SECP256K1_FE_CONST(0xd41244d1, 0x1ca4f652, 0x40687759, 0xf95ca9ef, 0xbab767ed, 0xedb38fd1, 0x8c36e18c, 0xd3b6f6a9), 1}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xe5, 0xbe, 0x52, 0x37, 0x2d, 0xd6, 0xe8, 0x94, 0xb2, 0xa3, 0x26, 0xfc, 0x36, 0x05, 0xa6, 0xe8, 0xf3, 0xc6, 0x9c, 0x71, 0x0b, 0xf2, 0x7d, 0x63, 0x0d, 0xfe, 0x20, 0x04, 0x98, 0x8b, 0x78, 0xeb, 0x6e, 0xab, 0x36}, SECP256K1_FE_CONST(0x64bf84dd, 0x5e03670f, 0xdb24c0f5, 0xd3c2c365, 0x736f51db, 0x6c92d950, 0x10716ad2, 0xd36134c8), 0}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfb, 0xb9, 0x82, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0xd6, 0xdb, 0x1f}, SECP256K1_FE_CONST(0x1c92ccdf, 0xcf4ac550, 0xc28db57c, 0xff0c8515, 0xcb26936c, 0x786584a7, 0x0114008d, 0x6c33a34b), 0}, +}; + +/* Set of expected ellswift_xdh BIP324 shared secrets, given private key, encodings, initiating, + * taken from the BIP324 test vectors. Created using an independent implementation, and tested + * against the paper authors' decoding code. */ +static const struct ellswift_xdh_test ellswift_xdh_tests_bip324[] = { + {{0x61, 0x06, 0x2e, 0xa5, 0x07, 0x1d, 0x80, 0x0b, 0xbf, 0xd5, 0x9e, 0x2e, 0x8b, 0x53, 0xd4, 0x7d, 0x19, 0x4b, 0x09, 0x5a, 0xe5, 0xa4, 0xdf, 0x04, 0x93, 0x6b, 0x49, 0x77, 0x2e, 0xf0, 0xd4, 0xd7}, {0xec, 0x0a, 0xdf, 0xf2, 0x57, 0xbb, 0xfe, 0x50, 0x0c, 0x18, 0x8c, 0x80, 0xb4, 0xfd, 0xd6, 0x40, 0xf6, 0xb4, 0x5a, 0x48, 0x2b, 0xbc, 0x15, 0xfc, 0x7c, 0xef, 0x59, 0x31, 0xde, 0xff, 0x0a, 0xa1, 0x86, 0xf6, 0xeb, 0x9b, 0xba, 0x7b, 0x85, 0xdc, 0x4d, 0xcc, 0x28, 0xb2, 0x87, 0x22, 0xde, 0x1e, 0x3d, 0x91, 0x08, 0xb9, 0x85, 0xe2, 0x96, 0x70, 0x45, 0x66, 0x8f, 0x66, 0x09, 0x8e, 0x47, 0x5b}, {0xa4, 0xa9, 0x4d, 0xfc, 0xe6, 0x9b, 0x4a, 0x2a, 0x0a, 0x09, 0x93, 0x13, 0xd1, 0x0f, 0x9f, 0x7e, 0x7d, 0x64, 0x9d, 0x60, 0x50, 0x1c, 0x9e, 0x1d, 0x27, 0x4c, 0x30, 0x0e, 0x0d, 0x89, 0xaa, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xaf, 0x88, 0xd5}, 1, {0xc6, 0x99, 0x2a, 0x11, 0x7f, 0x5e, 0xdb, 0xea, 0x70, 0xc3, 0xf5, 0x11, 0xd3, 0x2d, 0x26, 0xb9, 0x79, 0x8b, 0xe4, 0xb8, 0x1a, 0x62, 0xea, 0xee, 0x1a, 0x5a, 0xca, 0xa8, 0x45, 0x9a, 0x35, 0x92}}, + {{0x1f, 0x9c, 0x58, 0x1b, 0x35, 0x23, 0x18, 0x38, 0xf0, 0xf1, 0x7c, 0xf0, 0xc9, 0x79, 0x83, 0x5b, 0xac, 0xcb, 0x7f, 0x3a, 0xbb, 0xbb, 0x96, 0xff, 0xcc, 0x31, 0x8a, 0xb7, 0x1e, 0x6e, 0x12, 0x6f}, {0xa1, 0x85, 0x5e, 0x10, 0xe9, 0x4e, 0x00, 0xba, 0xa2, 0x30, 0x41, 0xd9, 0x16, 0xe2, 0x59, 0xf7, 0x04, 0x4e, 0x49, 0x1d, 0xa6, 0x17, 0x12, 0x69, 0x69, 0x47, 0x63, 0xf0, 0x18, 0xc7, 0xe6, 0x36, 0x93, 0xd2, 0x95, 0x75, 0xdc, 0xb4, 0x64, 0xac, 0x81, 0x6b, 0xaa, 0x1b, 0xe3, 0x53, 0xba, 0x12, 0xe3, 0x87, 0x6c, 0xba, 0x76, 0x28, 0xbd, 0x0b, 0xd8, 0xe7, 0x55, 0xe7, 0x21, 0xeb, 0x01, 0x40}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0, {0xa0, 0x13, 0x8f, 0x56, 0x4f, 0x74, 0xd0, 0xad, 0x70, 0xbc, 0x33, 0x7d, 0xac, 0xc9, 0xd0, 0xbf, 0x1d, 0x23, 0x49, 0x36, 0x4c, 0xaf, 0x11, 0x88, 0xa1, 0xe6, 0xe8, 0xdd, 0xb3, 0xb7, 0xb1, 0x84}}, + {{0x02, 0x86, 0xc4, 0x1c, 0xd3, 0x09, 0x13, 0xdb, 0x0f, 0xdf, 0xf7, 0xa6, 0x4e, 0xbd, 0xa5, 0xc8, 0xe3, 0xe7, 0xce, 0xf1, 0x0f, 0x2a, 0xeb, 0xc0, 0x0a, 0x76, 0x50, 0x44, 0x3c, 0xf4, 0xc6, 0x0d}, {0xd1, 0xee, 0x8a, 0x93, 0xa0, 0x11, 0x30, 0xcb, 0xf2, 0x99, 0x24, 0x9a, 0x25, 0x8f, 0x94, 0xfe, 0xb5, 0xf4, 0x69, 0xe7, 0xd0, 0xf2, 0xf2, 0x8f, 0x69, 0xee, 0x5e, 0x9a, 0xa8, 0xf9, 0xb5, 0x4a, 0x60, 0xf2, 0xc3, 0xff, 0x2d, 0x02, 0x36, 0x34, 0xec, 0x7f, 0x41, 0x27, 0xa9, 0x6c, 0xc1, 0x16, 0x62, 0xe4, 0x02, 0x89, 0x4c, 0xf1, 0xf6, 0x94, 0xfb, 0x9a, 0x7e, 0xaa, 0x5f, 0x1d, 0x92, 0x44}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22, 0xd5, 0xe4, 0x41, 0x52, 0x4d, 0x57, 0x1a, 0x52, 0xb3, 0xde, 0xf1, 0x26, 0x18, 0x9d, 0x3f, 0x41, 0x68, 0x90, 0xa9, 0x9d, 0x4d, 0xa6, 0xed, 0xe2, 0xb0, 0xcd, 0xe1, 0x76, 0x0c, 0xe2, 0xc3, 0xf9, 0x84, 0x57, 0xae}, 1, {0x25, 0x0b, 0x93, 0x57, 0x0d, 0x41, 0x11, 0x49, 0x10, 0x5a, 0xb8, 0xcb, 0x0b, 0xc5, 0x07, 0x99, 0x14, 0x90, 0x63, 0x06, 0x36, 0x8c, 0x23, 0xe9, 0xd7, 0x7c, 0x2a, 0x33, 0x26, 0x5b, 0x99, 0x4c}}, + {{0x6c, 0x77, 0x43, 0x2d, 0x1f, 0xda, 0x31, 0xe9, 0xf9, 0x42, 0xf8, 0xaf, 0x44, 0x60, 0x7e, 0x10, 0xf3, 0xad, 0x38, 0xa6, 0x5f, 0x8a, 0x4b, 0xdd, 0xae, 0x82, 0x3e, 0x5e, 0xff, 0x90, 0xdc, 0x38}, {0xd2, 0x68, 0x50, 0x70, 0xc1, 0xe6, 0x37, 0x6e, 0x63, 0x3e, 0x82, 0x52, 0x96, 0x63, 0x4f, 0xd4, 0x61, 0xfa, 0x9e, 0x5b, 0xdf, 0x21, 0x09, 0xbc, 0xeb, 0xd7, 0x35, 0xe5, 0xa9, 0x1f, 0x3e, 0x58, 0x7c, 0x5c, 0xb7, 0x82, 0xab, 0xb7, 0x97, 0xfb, 0xf6, 0xbb, 0x50, 0x74, 0xfd, 0x15, 0x42, 0xa4, 0x74, 0xf2, 0xa4, 0x5b, 0x67, 0x37, 0x63, 0xec, 0x2d, 0xb7, 0xfb, 0x99, 0xb7, 0x37, 0xbb, 0xb9}, {0x56, 0xbd, 0x0c, 0x06, 0xf1, 0x03, 0x52, 0xc3, 0xa1, 0xa9, 0xf4, 0xb4, 0xc9, 0x2f, 0x6f, 0xa2, 0xb2, 0x6d, 0xf1, 0x24, 0xb5, 0x78, 0x78, 0x35, 0x3c, 0x1f, 0xc6, 0x91, 0xc5, 0x1a, 0xbe, 0xa7, 0x7c, 0x88, 0x17, 0xda, 0xee, 0xb9, 0xfa, 0x54, 0x6b, 0x77, 0xc8, 0xda, 0xf7, 0x9d, 0x89, 0xb2, 0x2b, 0x0e, 0x1b, 0x87, 0x57, 0x4e, 0xce, 0x42, 0x37, 0x1f, 0x00, 0x23, 0x7a, 0xa9, 0xd8, 0x3a}, 0, {0x19, 0x18, 0xb7, 0x41, 0xef, 0x5f, 0x9d, 0x1d, 0x76, 0x70, 0xb0, 0x50, 0xc1, 0x52, 0xb4, 0xa4, 0xea, 0xd2, 0xc3, 0x1b, 0xe9, 0xae, 0xcb, 0x06, 0x81, 0xc0, 0xcd, 0x43, 0x24, 0x15, 0x08, 0x53}}, + {{0xa6, 0xec, 0x25, 0x12, 0x7c, 0xa1, 0xaa, 0x4c, 0xf1, 0x6b, 0x20, 0x08, 0x4b, 0xa1, 0xe6, 0x51, 0x6b, 0xaa, 0xe4, 0xd3, 0x24, 0x22, 0x28, 0x8e, 0x9b, 0x36, 0xd8, 0xbd, 0xdd, 0x2d, 0xe3, 0x5a}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x3d, 0x7e, 0xcc, 0xa5, 0x3e, 0x33, 0xe1, 0x85, 0xa8, 0xb9, 0xbe, 0x4e, 0x76, 0x99, 0xa9, 0x7c, 0x6f, 0xf4, 0xc7, 0x95, 0x52, 0x2e, 0x59, 0x18, 0xab, 0x7c, 0xd6, 0xb6, 0x88, 0x4f, 0x67, 0xe6, 0x83, 0xf3, 0xdc}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x73, 0x0b, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 1, {0xdd, 0x21, 0x0a, 0xa6, 0x62, 0x9f, 0x20, 0xbb, 0x32, 0x8e, 0x5d, 0x89, 0xda, 0xa6, 0xeb, 0x2a, 0xc3, 0xd1, 0xc6, 0x58, 0xa7, 0x25, 0x53, 0x6f, 0xf1, 0x54, 0xf3, 0x1b, 0x53, 0x6c, 0x23, 0xb2}}, + {{0x0a, 0xf9, 0x52, 0x65, 0x9e, 0xd7, 0x6f, 0x80, 0xf5, 0x85, 0x96, 0x6b, 0x95, 0xab, 0x6e, 0x6f, 0xd6, 0x86, 0x54, 0x67, 0x28, 0x27, 0x87, 0x86, 0x84, 0xc8, 0xb5, 0x47, 0xb1, 0xb9, 0x4f, 0x5a}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0x10, 0x17, 0xfd, 0x92, 0xfd, 0x31, 0x63, 0x7c, 0x26, 0xc9, 0x06, 0xb4, 0x20, 0x92, 0xe1, 0x1c, 0xc0, 0xd3, 0xaf, 0xae, 0x8d, 0x90, 0x19, 0xd2, 0x57, 0x8a, 0xf2, 0x27, 0x35, 0xce, 0x7b, 0xc4, 0x69, 0xc7, 0x2d}, {0x96, 0x52, 0xd7, 0x8b, 0xae, 0xfc, 0x02, 0x8c, 0xd3, 0x7a, 0x6a, 0x92, 0x62, 0x5b, 0x8b, 0x8f, 0x85, 0xfd, 0xe1, 0xe4, 0xc9, 0x44, 0xad, 0x3f, 0x20, 0xe1, 0x98, 0xbe, 0xf8, 0xc0, 0x2f, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xe9, 0x18, 0x70}, 0, {0x35, 0x68, 0xf2, 0xae, 0xa2, 0xe1, 0x4e, 0xf4, 0xee, 0x4a, 0x3c, 0x2a, 0x8b, 0x8d, 0x31, 0xbc, 0x5e, 0x31, 0x87, 0xba, 0x86, 0xdb, 0x10, 0x73, 0x9b, 0x4f, 0xf8, 0xec, 0x92, 0xff, 0x66, 0x55}}, + {{0xf9, 0x0e, 0x08, 0x0c, 0x64, 0xb0, 0x58, 0x24, 0xc5, 0xa2, 0x4b, 0x25, 0x01, 0xd5, 0xae, 0xaf, 0x08, 0xaf, 0x38, 0x72, 0xee, 0x86, 0x0a, 0xa8, 0x0b, 0xdc, 0xd4, 0x30, 0xf7, 0xb6, 0x34, 0x94}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11, 0x51, 0x73, 0x76, 0x5d, 0xc2, 0x02, 0xcf, 0x02, 0x9a, 0xd3, 0xf1, 0x54, 0x79, 0x73, 0x5d, 0x57, 0x69, 0x7a, 0xf1, 0x2b, 0x01, 0x31, 0xdd, 0x21, 0x43, 0x0d, 0x57, 0x72, 0xe4, 0xef, 0x11, 0x47, 0x4d, 0x58, 0xb9}, {0x12, 0xa5, 0x0f, 0x3f, 0xaf, 0xea, 0x7c, 0x1e, 0xea, 0xda, 0x4c, 0xf8, 0xd3, 0x37, 0x77, 0x70, 0x4b, 0x77, 0x36, 0x14, 0x53, 0xaf, 0xc8, 0x3b, 0xda, 0x91, 0xee, 0xf3, 0x49, 0xae, 0x04, 0x4d, 0x20, 0x12, 0x6c, 0x62, 0x00, 0x54, 0x7e, 0xa5, 0xa6, 0x91, 0x17, 0x76, 0xc0, 0x5d, 0xee, 0x2a, 0x7f, 0x1a, 0x9b, 0xa7, 0xdf, 0xba, 0xbb, 0xbd, 0x27, 0x3c, 0x3e, 0xf2, 0x9e, 0xf4, 0x6e, 0x46}, 1, {0xe2, 0x54, 0x61, 0xfb, 0x0e, 0x4c, 0x16, 0x2e, 0x18, 0x12, 0x3e, 0xcd, 0xe8, 0x83, 0x42, 0xd5, 0x4d, 0x44, 0x96, 0x31, 0xe9, 0xb7, 0x5a, 0x26, 0x6f, 0xd9, 0x26, 0x0c, 0x2b, 0xb2, 0xf4, 0x1d}}, +}; + +/** This is a hasher for ellswift_xdh which just returns the shared X coordinate. + * + * This is generally a bad idea as it means changes to the encoding of the + * exchanged public keys do not affect the shared secret. However, it's used here + * in tests to be able to verify the X coordinate through other means. + */ +static int ellswift_xdh_hash_x32(unsigned char *output, const unsigned char *x32, const unsigned char *ell_a64, const unsigned char *ell_b64, void *data) { + (void)ell_a64; + (void)ell_b64; + (void)data; + memcpy(output, x32, 32); + return 1; +} + +void run_ellswift_tests(void) { + int i = 0; + /* Test vectors. */ + for (i = 0; (unsigned)i < sizeof(ellswift_xswiftec_inv_tests) / sizeof(ellswift_xswiftec_inv_tests[0]); ++i) { + const struct ellswift_xswiftec_inv_test *testcase = &ellswift_xswiftec_inv_tests[i]; + int c; + for (c = 0; c < 8; ++c) { + secp256k1_fe t; + int ret = secp256k1_ellswift_xswiftec_inv_var(&t, &testcase->x, &testcase->u, c); + CHECK(ret == ((testcase->enc_bitmap >> c) & 1)); + if (ret) { + secp256k1_fe x2; + CHECK(fe_equal(&t, &testcase->encs[c])); + secp256k1_ellswift_xswiftec_var(&x2, &testcase->u, &testcase->encs[c]); + CHECK(fe_equal(&testcase->x, &x2)); + } + } + } + for (i = 0; (unsigned)i < sizeof(ellswift_decode_tests) / sizeof(ellswift_decode_tests[0]); ++i) { + const struct ellswift_decode_test *testcase = &ellswift_decode_tests[i]; + secp256k1_pubkey pubkey; + secp256k1_ge ge; + int ret; + ret = secp256k1_ellswift_decode(CTX, &pubkey, testcase->enc); + CHECK(ret); + ret = secp256k1_pubkey_load(CTX, &ge, &pubkey); + CHECK(ret); + CHECK(fe_equal(&testcase->x, &ge.x)); + CHECK(secp256k1_fe_is_odd(&ge.y) == testcase->odd_y); + } + for (i = 0; (unsigned)i < sizeof(ellswift_xdh_tests_bip324) / sizeof(ellswift_xdh_tests_bip324[0]); ++i) { + const struct ellswift_xdh_test *test = &ellswift_xdh_tests_bip324[i]; + unsigned char shared_secret[32]; + int ret; + int party = !test->initiating; + const unsigned char* ell_a64 = party ? test->ellswift_theirs : test->ellswift_ours; + const unsigned char* ell_b64 = party ? test->ellswift_ours : test->ellswift_theirs; + ret = secp256k1_ellswift_xdh(CTX, shared_secret, + ell_a64, ell_b64, + test->priv_ours, + party, + secp256k1_ellswift_xdh_hash_function_bip324, + NULL); + CHECK(ret); + CHECK(secp256k1_memcmp_var(shared_secret, test->shared_secret, 32) == 0); + } + /* Verify that secp256k1_ellswift_encode + decode roundtrips. */ + for (i = 0; i < 1000 * COUNT; i++) { + unsigned char rnd32[32]; + unsigned char ell64[64]; + secp256k1_ge g, g2; + secp256k1_pubkey pubkey, pubkey2; + /* Generate random public key and random randomizer. */ + testutil_random_ge_test(&g); + secp256k1_pubkey_save(&pubkey, &g); + testrand256(rnd32); + /* Convert the public key to ElligatorSwift and back. */ + secp256k1_ellswift_encode(CTX, ell64, &pubkey, rnd32); + secp256k1_ellswift_decode(CTX, &pubkey2, ell64); + secp256k1_pubkey_load(CTX, &g2, &pubkey2); + /* Compare with original. */ + CHECK(secp256k1_ge_eq_var(&g, &g2)); + } + /* Verify the behavior of secp256k1_ellswift_create */ + for (i = 0; i < 400 * COUNT; i++) { + unsigned char auxrnd32[32], sec32[32]; + secp256k1_scalar sec; + secp256k1_gej res; + secp256k1_ge dec; + secp256k1_pubkey pub; + unsigned char ell64[64]; + int ret; + /* Generate random secret key and random randomizer. */ + if (i & 1) testrand256_test(auxrnd32); + testutil_random_scalar_order_test(&sec); + secp256k1_scalar_get_b32(sec32, &sec); + /* Construct ElligatorSwift-encoded public keys for that key. */ + ret = secp256k1_ellswift_create(CTX, ell64, sec32, (i & 1) ? auxrnd32 : NULL); + CHECK(ret); + /* Decode it, and compare with traditionally-computed public key. */ + secp256k1_ellswift_decode(CTX, &pub, ell64); + secp256k1_pubkey_load(CTX, &dec, &pub); + secp256k1_ecmult(&res, NULL, &secp256k1_scalar_zero, &sec); + CHECK(secp256k1_gej_eq_ge_var(&res, &dec)); + } + /* Verify that secp256k1_ellswift_xdh computes the right shared X coordinate. */ + for (i = 0; i < 800 * COUNT; i++) { + unsigned char ell64[64], sec32[32], share32[32]; + secp256k1_scalar sec; + secp256k1_ge dec, res; + secp256k1_fe share_x; + secp256k1_gej decj, resj; + secp256k1_pubkey pub; + int ret; + /* Generate random secret key. */ + testutil_random_scalar_order_test(&sec); + secp256k1_scalar_get_b32(sec32, &sec); + /* Generate random ElligatorSwift encoding for the remote key and decode it. */ + testrand256_test(ell64); + testrand256_test(ell64 + 32); + secp256k1_ellswift_decode(CTX, &pub, ell64); + secp256k1_pubkey_load(CTX, &dec, &pub); + secp256k1_gej_set_ge(&decj, &dec); + /* Compute the X coordinate of seckey*pubkey using ellswift_xdh. Note that we + * pass ell64 as claimed (but incorrect) encoding for sec32 here; this works + * because the "hasher" function we use here ignores the ell64 arguments. */ + ret = secp256k1_ellswift_xdh(CTX, share32, ell64, ell64, sec32, i & 1, &ellswift_xdh_hash_x32, NULL); + CHECK(ret); + (void)secp256k1_fe_set_b32_limit(&share_x, share32); /* no overflow is possible */ + SECP256K1_FE_VERIFY(&share_x); + /* Compute seckey*pubkey directly. */ + secp256k1_ecmult(&resj, &decj, &sec, NULL); + secp256k1_ge_set_gej(&res, &resj); + /* Compare. */ + CHECK(fe_equal(&res.x, &share_x)); + } + /* Verify the joint behavior of secp256k1_ellswift_xdh */ + for (i = 0; i < 200 * COUNT; i++) { + unsigned char auxrnd32a[32], auxrnd32b[32], auxrnd32a_bad[32], auxrnd32b_bad[32]; + unsigned char sec32a[32], sec32b[32], sec32a_bad[32], sec32b_bad[32]; + secp256k1_scalar seca, secb; + unsigned char ell64a[64], ell64b[64], ell64a_bad[64], ell64b_bad[64]; + unsigned char share32a[32], share32b[32], share32_bad[32]; + unsigned char prefix64[64]; + secp256k1_ellswift_xdh_hash_function hash_function; + void* data; + int ret; + + /* Pick hasher to use. */ + if ((i % 3) == 0) { + hash_function = ellswift_xdh_hash_x32; + data = NULL; + } else if ((i % 3) == 1) { + hash_function = secp256k1_ellswift_xdh_hash_function_bip324; + data = NULL; + } else { + hash_function = secp256k1_ellswift_xdh_hash_function_prefix; + testrand256_test(prefix64); + testrand256_test(prefix64 + 32); + data = prefix64; + } + + /* Generate random secret keys and random randomizers. */ + testrand256_test(auxrnd32a); + testrand256_test(auxrnd32b); + testutil_random_scalar_order_test(&seca); + /* Draw secb uniformly at random to make sure that the secret keys + * differ */ + testutil_random_scalar_order(&secb); + secp256k1_scalar_get_b32(sec32a, &seca); + secp256k1_scalar_get_b32(sec32b, &secb); + + /* Construct ElligatorSwift-encoded public keys for those keys. */ + /* For A: */ + ret = secp256k1_ellswift_create(CTX, ell64a, sec32a, auxrnd32a); + CHECK(ret); + /* For B: */ + ret = secp256k1_ellswift_create(CTX, ell64b, sec32b, auxrnd32b); + CHECK(ret); + + /* Compute the shared secret both ways and compare with each other. */ + /* For A: */ + ret = secp256k1_ellswift_xdh(CTX, share32a, ell64a, ell64b, sec32a, 0, hash_function, data); + CHECK(ret); + /* For B: */ + ret = secp256k1_ellswift_xdh(CTX, share32b, ell64a, ell64b, sec32b, 1, hash_function, data); + CHECK(ret); + /* And compare: */ + CHECK(secp256k1_memcmp_var(share32a, share32b, 32) == 0); + + /* Verify that the shared secret doesn't match if other side's public key is incorrect. */ + /* For A (using a bad public key for B): */ + memcpy(ell64b_bad, ell64b, sizeof(ell64a_bad)); + testrand_flip(ell64b_bad, sizeof(ell64b_bad)); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b_bad, sec32a, 0, hash_function, data); + CHECK(ret); /* Mismatching encodings don't get detected by secp256k1_ellswift_xdh. */ + CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); + /* For B (using a bad public key for A): */ + memcpy(ell64a_bad, ell64a, sizeof(ell64a_bad)); + testrand_flip(ell64a_bad, sizeof(ell64a_bad)); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a_bad, ell64b, sec32b, 1, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); + + /* Verify that the shared secret doesn't match if the private key is incorrect. */ + /* For A: */ + memcpy(sec32a_bad, sec32a, sizeof(sec32a_bad)); + testrand_flip(sec32a_bad, sizeof(sec32a_bad)); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32a_bad, 0, hash_function, data); + CHECK(!ret || secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); + /* For B: */ + memcpy(sec32b_bad, sec32b, sizeof(sec32b_bad)); + testrand_flip(sec32b_bad, sizeof(sec32b_bad)); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32b_bad, 1, hash_function, data); + CHECK(!ret || secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); + + if (hash_function != ellswift_xdh_hash_x32) { + /* Verify that the shared secret doesn't match when a different encoding of the same public key is used. */ + /* For A (changing B's public key): */ + memcpy(auxrnd32b_bad, auxrnd32b, sizeof(auxrnd32b_bad)); + testrand_flip(auxrnd32b_bad, sizeof(auxrnd32b_bad)); + ret = secp256k1_ellswift_create(CTX, ell64b_bad, sec32b, auxrnd32b_bad); + CHECK(ret); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b_bad, sec32a, 0, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); + /* For B (changing A's public key): */ + memcpy(auxrnd32a_bad, auxrnd32a, sizeof(auxrnd32a_bad)); + testrand_flip(auxrnd32a_bad, sizeof(auxrnd32a_bad)); + ret = secp256k1_ellswift_create(CTX, ell64a_bad, sec32a, auxrnd32a_bad); + CHECK(ret); + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a_bad, ell64b, sec32b, 1, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); + + /* Verify that swapping sides changes the shared secret. */ + /* For A (claiming to be B): */ + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32a, 1, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32a, 32) != 0); + /* For B (claiming to be A): */ + ret = secp256k1_ellswift_xdh(CTX, share32_bad, ell64a, ell64b, sec32b, 0, hash_function, data); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32_bad, share32b, 32) != 0); + } + } + + /* Test hash initializers. */ + { + secp256k1_sha256 sha, sha_optimized; + static const unsigned char encode_tag[25] = "secp256k1_ellswift_encode"; + static const unsigned char create_tag[25] = "secp256k1_ellswift_create"; + static const unsigned char bip324_tag[26] = "bip324_ellswift_xonly_ecdh"; + + /* Check that hash initialized by + * secp256k1_ellswift_sha256_init_encode has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, encode_tag, sizeof(encode_tag)); + secp256k1_ellswift_sha256_init_encode(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_ellswift_sha256_init_create has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, create_tag, sizeof(create_tag)); + secp256k1_ellswift_sha256_init_create(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_ellswift_sha256_init_bip324 has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, bip324_tag, sizeof(bip324_tag)); + secp256k1_ellswift_sha256_init_bip324(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + } +} + +#endif diff --git a/src/secp256k1/src/modules/extrakeys/Makefile.am.include b/src/secp256k1/src/modules/extrakeys/Makefile.am.include new file mode 100644 index 000000000..0d901ec1f --- /dev/null +++ b/src/secp256k1/src/modules/extrakeys/Makefile.am.include @@ -0,0 +1,4 @@ +include_HEADERS += include/secp256k1_extrakeys.h +noinst_HEADERS += src/modules/extrakeys/tests_impl.h +noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h +noinst_HEADERS += src/modules/extrakeys/main_impl.h diff --git a/src/secp256k1/src/modules/extrakeys/main_impl.h b/src/secp256k1/src/modules/extrakeys/main_impl.h new file mode 100644 index 000000000..0c7e26677 --- /dev/null +++ b/src/secp256k1/src/modules/extrakeys/main_impl.h @@ -0,0 +1,285 @@ +/*********************************************************************** + * Copyright (c) 2020 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_EXTRAKEYS_MAIN_H +#define SECP256K1_MODULE_EXTRAKEYS_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_extrakeys.h" +#include "../../util.h" + +static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) { + return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey); +} + +static SECP256K1_INLINE void secp256k1_xonly_pubkey_save(secp256k1_xonly_pubkey *pubkey, secp256k1_ge *ge) { + secp256k1_pubkey_save((secp256k1_pubkey *) pubkey, ge); +} + +int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, const unsigned char *input32) { + secp256k1_ge pk; + secp256k1_fe x; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(input32 != NULL); + + if (!secp256k1_fe_set_b32_limit(&x, input32)) { + return 0; + } + if (!secp256k1_ge_set_xo_var(&pk, &x, 0)) { + return 0; + } + if (!secp256k1_ge_is_in_correct_subgroup(&pk)) { + return 0; + } + secp256k1_xonly_pubkey_save(pubkey, &pk); + return 1; +} + +int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output32, const secp256k1_xonly_pubkey *pubkey) { + secp256k1_ge pk; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output32 != NULL); + memset(output32, 0, 32); + ARG_CHECK(pubkey != NULL); + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + secp256k1_fe_get_b32(output32, &pk.x); + return 1; +} + +int secp256k1_xonly_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_xonly_pubkey* pk0, const secp256k1_xonly_pubkey* pk1) { + unsigned char out[2][32]; + const secp256k1_xonly_pubkey* pk[2]; + int i; + + VERIFY_CHECK(ctx != NULL); + pk[0] = pk0; pk[1] = pk1; + for (i = 0; i < 2; i++) { + /* If the public key is NULL or invalid, xonly_pubkey_serialize will + * call the illegal_callback and return 0. In that case we will + * serialize the key as all zeros which is less than any valid public + * key. This results in consistent comparisons even if NULL or invalid + * pubkeys are involved and prevents edge cases such as sorting + * algorithms that use this function and do not terminate as a + * result. */ + if (!secp256k1_xonly_pubkey_serialize(ctx, out[i], pk[i])) { + /* Note that xonly_pubkey_serialize should already set the output to + * zero in that case, but it's not guaranteed by the API, we can't + * test it and writing a VERIFY_CHECK is more complex than + * explicitly memsetting (again). */ + memset(out[i], 0, sizeof(out[i])); + } + } + return secp256k1_memcmp_var(out[0], out[1], sizeof(out[1])); +} + +/** Keeps a group element as is if it has an even Y and otherwise negates it. + * y_parity is set to 0 in the former case and to 1 in the latter case. + * Requires that the coordinates of r are normalized. */ +static int secp256k1_extrakeys_ge_even_y(secp256k1_ge *r) { + int y_parity = 0; + VERIFY_CHECK(!secp256k1_ge_is_infinity(r)); + + if (secp256k1_fe_is_odd(&r->y)) { + secp256k1_fe_negate(&r->y, &r->y, 1); + y_parity = 1; + } + return y_parity; +} + +int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *pk_parity, const secp256k1_pubkey *pubkey) { + secp256k1_ge pk; + int tmp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(xonly_pubkey != NULL); + ARG_CHECK(pubkey != NULL); + + if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + tmp = secp256k1_extrakeys_ge_even_y(&pk); + if (pk_parity != NULL) { + *pk_parity = tmp; + } + secp256k1_xonly_pubkey_save(xonly_pubkey, &pk); + return 1; +} + +int secp256k1_xonly_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { + secp256k1_ge pk; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output_pubkey != NULL); + memset(output_pubkey, 0, sizeof(*output_pubkey)); + ARG_CHECK(internal_pubkey != NULL); + ARG_CHECK(tweak32 != NULL); + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey) + || !secp256k1_ec_pubkey_tweak_add_helper(&pk, tweak32)) { + return 0; + } + secp256k1_pubkey_save(output_pubkey, &pk); + return 1; +} + +int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { + secp256k1_ge pk; + unsigned char pk_expected32[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(internal_pubkey != NULL); + ARG_CHECK(tweaked_pubkey32 != NULL); + ARG_CHECK(tweak32 != NULL); + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey) + || !secp256k1_ec_pubkey_tweak_add_helper(&pk, tweak32)) { + return 0; + } + secp256k1_fe_normalize_var(&pk.x); + secp256k1_fe_normalize_var(&pk.y); + secp256k1_fe_get_b32(pk_expected32, &pk.x); + + return secp256k1_memcmp_var(&pk_expected32, tweaked_pubkey32, 32) == 0 + && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity; +} + +static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) { + secp256k1_scalar_get_b32(&keypair->data[0], sk); + secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk); +} + + +static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) { + int ret; + + ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]); + /* We can declassify ret here because sk is only zero if a keypair function + * failed (which zeroes the keypair) and its return value is ignored. */ + secp256k1_declassify(ctx, &ret, sizeof(ret)); + ARG_CHECK(ret); + return ret; +} + +/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk + * and ARG_CHECKs that the keypair is not invalid. It always initializes sk and + * pk with dummy values. */ +static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) { + int ret; + const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32]; + + /* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's + * invalid. */ + secp256k1_declassify(ctx, pubkey, sizeof(*pubkey)); + ret = secp256k1_pubkey_load(ctx, pk, pubkey); + if (sk != NULL) { + ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair); + } + if (!ret) { + *pk = secp256k1_ge_const_g; + if (sk != NULL) { + *sk = secp256k1_scalar_one; + } + } + return ret; +} + +int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) { + secp256k1_scalar sk; + secp256k1_ge pk; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(keypair != NULL); + memset(keypair, 0, sizeof(*keypair)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey32 != NULL); + + ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32); + secp256k1_keypair_save(keypair, &sk, &pk); + secp256k1_memczero(keypair, sizeof(*keypair), !ret); + + secp256k1_scalar_clear(&sk); + return ret; +} + +int secp256k1_keypair_sec(const secp256k1_context* ctx, unsigned char *seckey, const secp256k1_keypair *keypair) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + memset(seckey, 0, 32); + ARG_CHECK(keypair != NULL); + + memcpy(seckey, &keypair->data[0], 32); + return 1; +} + +int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(keypair != NULL); + + memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey)); + return 1; +} + +int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) { + secp256k1_ge pk; + int tmp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(keypair != NULL); + + if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) { + return 0; + } + tmp = secp256k1_extrakeys_ge_even_y(&pk); + if (pk_parity != NULL) { + *pk_parity = tmp; + } + secp256k1_xonly_pubkey_save(pubkey, &pk); + + return 1; +} + +int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *tweak32) { + secp256k1_ge pk; + secp256k1_scalar sk; + int y_parity; + int ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(keypair != NULL); + ARG_CHECK(tweak32 != NULL); + + ret = secp256k1_keypair_load(ctx, &sk, &pk, keypair); + memset(keypair, 0, sizeof(*keypair)); + + y_parity = secp256k1_extrakeys_ge_even_y(&pk); + if (y_parity == 1) { + secp256k1_scalar_negate(&sk, &sk); + } + + ret &= secp256k1_ec_seckey_tweak_add_helper(&sk, tweak32); + ret &= secp256k1_ec_pubkey_tweak_add_helper(&pk, tweak32); + + secp256k1_declassify(ctx, &ret, sizeof(ret)); + if (ret) { + secp256k1_keypair_save(keypair, &sk, &pk); + } + + secp256k1_scalar_clear(&sk); + return ret; +} + +#endif diff --git a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h new file mode 100644 index 000000000..645bae2d4 --- /dev/null +++ b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h @@ -0,0 +1,68 @@ +/*********************************************************************** + * Copyright (c) 2020 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H +#define SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H + +#include "../../../include/secp256k1_extrakeys.h" +#include "main_impl.h" + +static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp256k1_ge* group) { + secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1]; + secp256k1_pubkey pubkey[EXHAUSTIVE_TEST_ORDER - 1]; + secp256k1_xonly_pubkey xonly_pubkey[EXHAUSTIVE_TEST_ORDER - 1]; + int parities[EXHAUSTIVE_TEST_ORDER - 1]; + unsigned char xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - 1][32]; + int i; + + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { + secp256k1_fe fe; + secp256k1_scalar scalar_i; + unsigned char buf[33]; + int parity; + + secp256k1_scalar_set_int(&scalar_i, i); + secp256k1_scalar_get_b32(buf, &scalar_i); + + /* Construct pubkey and keypair. */ + CHECK(secp256k1_keypair_create(ctx, &keypair[i - 1], buf)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey[i - 1], buf)); + + /* Construct serialized xonly_pubkey from keypair. */ + CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey[i - 1], &parities[i - 1], &keypair[i - 1])); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey_bytes[i - 1], &xonly_pubkey[i - 1])); + + /* Parse the xonly_pubkey back and verify it matches the previously serialized value. */ + CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pubkey[i - 1], xonly_pubkey_bytes[i - 1])); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf, &xonly_pubkey[i - 1])); + CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], buf, 32) == 0); + + /* Construct the xonly_pubkey from the pubkey, and verify it matches the same. */ + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pubkey[i - 1], &parity, &pubkey[i - 1])); + CHECK(parity == parities[i - 1]); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf, &xonly_pubkey[i - 1])); + CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], buf, 32) == 0); + + /* Compare the xonly_pubkey bytes against the precomputed group. */ + secp256k1_fe_set_b32_mod(&fe, xonly_pubkey_bytes[i - 1]); + CHECK(secp256k1_fe_equal(&fe, &group[i].x)); + + /* Check the parity against the precomputed group. */ + fe = group[i].y; + secp256k1_fe_normalize_var(&fe); + CHECK(secp256k1_fe_is_odd(&fe) == parities[i - 1]); + + /* Verify that the higher half is identical to the lower half mirrored. */ + if (i > EXHAUSTIVE_TEST_ORDER / 2) { + CHECK(secp256k1_memcmp_var(xonly_pubkey_bytes[i - 1], xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - i - 1], 32) == 0); + CHECK(parities[i - 1] == 1 - parities[EXHAUSTIVE_TEST_ORDER - i - 1]); + } + } + + /* TODO: keypair/xonly_pubkey tweak tests */ +} + +#endif diff --git a/src/secp256k1/src/modules/extrakeys/tests_impl.h b/src/secp256k1/src/modules/extrakeys/tests_impl.h new file mode 100644 index 000000000..ab4ef4a74 --- /dev/null +++ b/src/secp256k1/src/modules/extrakeys/tests_impl.h @@ -0,0 +1,483 @@ +/*********************************************************************** + * Copyright (c) 2020 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_H +#define SECP256K1_MODULE_EXTRAKEYS_TESTS_H + +#include "../../../include/secp256k1_extrakeys.h" + +static void test_xonly_pubkey(void) { + secp256k1_pubkey pk; + secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp; + secp256k1_ge pk1; + secp256k1_ge pk2; + secp256k1_fe y; + unsigned char sk[32]; + unsigned char xy_sk[32]; + unsigned char buf32[32]; + unsigned char ones32[32]; + unsigned char zeros64[64] = { 0 }; + int pk_parity; + int i; + + testrand256(sk); + memset(ones32, 0xFF, 32); + testrand256(xy_sk); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); + + /* Test xonly_pubkey_from_pubkey */ + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_from_pubkey(CTX, NULL, &pk_parity, &pk)); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk) == 1); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, NULL)); + memset(&pk, 0, sizeof(pk)); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk)); + + /* Choose a secret key such that the resulting pubkey and xonly_pubkey match. */ + memset(sk, 0, sizeof(sk)); + sk[0] = 1; + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_memcmp_var(&pk, &xonly_pk, sizeof(pk)) == 0); + CHECK(pk_parity == 0); + + /* Choose a secret key such that pubkey and xonly_pubkey are each others + * negation. */ + sk[0] = 2; + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_memcmp_var(&xonly_pk, &pk, sizeof(xonly_pk)) != 0); + CHECK(pk_parity == 1); + secp256k1_pubkey_load(CTX, &pk1, &pk); + secp256k1_pubkey_load(CTX, &pk2, (secp256k1_pubkey *) &xonly_pk); + CHECK(secp256k1_fe_equal(&pk1.x, &pk2.x) == 1); + secp256k1_fe_negate(&y, &pk2.y, 1); + CHECK(secp256k1_fe_equal(&pk1.y, &y) == 1); + + /* Test xonly_pubkey_serialize and xonly_pubkey_parse */ + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_serialize(CTX, NULL, &xonly_pk)); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_serialize(CTX, buf32, NULL)); + CHECK(secp256k1_memcmp_var(buf32, zeros64, 32) == 0); + { + /* A pubkey filled with 0s will fail to serialize due to pubkey_load + * special casing. */ + secp256k1_xonly_pubkey pk_tmp; + memset(&pk_tmp, 0, sizeof(pk_tmp)); + /* pubkey_load calls illegal callback */ + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_serialize(CTX, buf32, &pk_tmp)); + } + + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &xonly_pk) == 1); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_parse(CTX, NULL, buf32)); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, NULL)); + + /* Serialization and parse roundtrip */ + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk_tmp, buf32) == 1); + CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(xonly_pk)) == 0); + + /* Test parsing invalid field elements */ + memset(&xonly_pk, 1, sizeof(xonly_pk)); + /* Overflowing field element */ + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, ones32) == 0); + CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); + memset(&xonly_pk, 1, sizeof(xonly_pk)); + /* There's no point with x-coordinate 0 on secp256k1 */ + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, zeros64) == 0); + CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); + /* If a random 32-byte string can not be parsed with ec_pubkey_parse + * (because interpreted as X coordinate it does not correspond to a point on + * the curve) then xonly_pubkey_parse should fail as well. */ + for (i = 0; i < COUNT; i++) { + unsigned char rand33[33]; + testrand256(&rand33[1]); + rand33[0] = SECP256K1_TAG_PUBKEY_EVEN; + if (!secp256k1_ec_pubkey_parse(CTX, &pk, rand33, 33)) { + memset(&xonly_pk, 1, sizeof(xonly_pk)); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, &rand33[1]) == 0); + CHECK(secp256k1_memcmp_var(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); + } else { + CHECK(secp256k1_xonly_pubkey_parse(CTX, &xonly_pk, &rand33[1]) == 1); + } + } +} + +static void test_xonly_pubkey_comparison(void) { + unsigned char pk1_ser[32] = { + 0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11, + 0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23 + }; + const unsigned char pk2_ser[32] = { + 0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d, + 0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c + }; + secp256k1_xonly_pubkey pk1; + secp256k1_xonly_pubkey pk2; + + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk1, pk1_ser) == 1); + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk2, pk2_ser) == 1); + + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_xonly_pubkey_cmp(CTX, NULL, &pk2) < 0)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, NULL) > 0)); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, &pk2) < 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk2, &pk1) > 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, &pk1) == 0); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk2, &pk2) == 0); + memset(&pk1, 0, sizeof(pk1)); /* illegal pubkey */ + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, &pk2) < 0)); + { + int32_t ecount = 0; + secp256k1_context_set_illegal_callback(CTX, counting_callback_fn, &ecount); + CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk1, &pk1) == 0); + CHECK(ecount == 2); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); + } + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_xonly_pubkey_cmp(CTX, &pk2, &pk1) > 0)); +} + +static void test_xonly_pubkey_tweak(void) { + unsigned char zeros64[64] = { 0 }; + unsigned char overflows[32]; + unsigned char sk[32]; + secp256k1_pubkey internal_pk; + secp256k1_xonly_pubkey internal_xonly_pk; + secp256k1_pubkey output_pk; + int pk_parity; + unsigned char tweak[32]; + int i; + + memset(overflows, 0xff, sizeof(overflows)); + testrand256(tweak); + testrand256(sk); + CHECK(secp256k1_ec_pubkey_create(CTX, &internal_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &internal_xonly_pk, &pk_parity, &internal_pk) == 1); + + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_tweak_add(CTX, NULL, &internal_xonly_pk, tweak)); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, NULL, tweak)); + /* NULL internal_xonly_pk zeroes the output_pk */ + CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, NULL)); + /* NULL tweak zeroes the output_pk */ + CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); + + /* Invalid tweak zeroes the output_pk */ + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, overflows) == 0); + CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); + + /* A zero tweak is fine */ + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, zeros64) == 1); + + /* Fails if the resulting key was infinity */ + for (i = 0; i < COUNT; i++) { + secp256k1_scalar scalar_tweak; + /* Because sk may be negated before adding, we need to try with tweak = + * sk as well as tweak = -sk. */ + secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL); + secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak); + secp256k1_scalar_get_b32(tweak, &scalar_tweak); + CHECK((secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, sk) == 0) + || (secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 0)); + CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); + } + + /* Invalid pk with a valid tweak */ + memset(&internal_xonly_pk, 0, sizeof(internal_xonly_pk)); + testrand256(tweak); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak)); + CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); +} + +static void test_xonly_pubkey_tweak_check(void) { + unsigned char zeros64[64] = { 0 }; + unsigned char overflows[32]; + unsigned char sk[32]; + secp256k1_pubkey internal_pk; + secp256k1_xonly_pubkey internal_xonly_pk; + secp256k1_pubkey output_pk; + secp256k1_xonly_pubkey output_xonly_pk; + unsigned char output_pk32[32]; + unsigned char buf32[32]; + int pk_parity; + unsigned char tweak[32]; + + memset(overflows, 0xff, sizeof(overflows)); + testrand256(tweak); + testrand256(sk); + CHECK(secp256k1_ec_pubkey_create(CTX, &internal_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &internal_xonly_pk, &pk_parity, &internal_pk) == 1); + + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &output_xonly_pk, &pk_parity, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &output_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_tweak_add_check(CTX, NULL, pk_parity, &internal_xonly_pk, tweak)); + /* invalid pk_parity value */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, 2, &internal_xonly_pk, tweak) == 0); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, NULL, tweak)); + CHECK_ILLEGAL(CTX, secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, NULL)); + + memset(tweak, 1, sizeof(tweak)); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &internal_xonly_pk, NULL, &internal_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &output_xonly_pk, &pk_parity, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, output_pk32, &output_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk32, pk_parity, &internal_xonly_pk, tweak) == 1); + + /* Wrong pk_parity */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk32, !pk_parity, &internal_xonly_pk, tweak) == 0); + /* Wrong public key */ + CHECK(secp256k1_xonly_pubkey_serialize(CTX, buf32, &internal_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, buf32, pk_parity, &internal_xonly_pk, tweak) == 0); + + /* Overflowing tweak not allowed */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk32, pk_parity, &internal_xonly_pk, overflows) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk, &internal_xonly_pk, overflows) == 0); + CHECK(secp256k1_memcmp_var(&output_pk, zeros64, sizeof(output_pk)) == 0); +} + +/* Starts with an initial pubkey and recursively creates N_PUBKEYS - 1 + * additional pubkeys by calling tweak_add. Then verifies every tweak starting + * from the last pubkey. */ +#define N_PUBKEYS 32 +static void test_xonly_pubkey_tweak_recursive(void) { + unsigned char sk[32]; + secp256k1_pubkey pk[N_PUBKEYS]; + unsigned char pk_serialized[32]; + unsigned char tweak[N_PUBKEYS - 1][32]; + int i; + + testrand256(sk); + CHECK(secp256k1_ec_pubkey_create(CTX, &pk[0], sk) == 1); + /* Add tweaks */ + for (i = 0; i < N_PUBKEYS - 1; i++) { + secp256k1_xonly_pubkey xonly_pk; + memset(tweak[i], i + 1, sizeof(tweak[i])); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk[i]) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &pk[i + 1], &xonly_pk, tweak[i]) == 1); + } + + /* Verify tweaks */ + for (i = N_PUBKEYS - 1; i > 0; i--) { + secp256k1_xonly_pubkey xonly_pk; + int pk_parity; + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk[i]) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk_serialized, &xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk[i - 1]) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, pk_serialized, pk_parity, &xonly_pk, tweak[i - 1]) == 1); + } +} +#undef N_PUBKEYS + +static void test_keypair(void) { + unsigned char sk[32]; + unsigned char sk_tmp[32]; + unsigned char zeros96[96] = { 0 }; + unsigned char overflows[32]; + secp256k1_keypair keypair; + secp256k1_pubkey pk, pk_tmp; + secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp; + int pk_parity, pk_parity_tmp; + + CHECK(sizeof(zeros96) == sizeof(keypair)); + memset(overflows, 0xFF, sizeof(overflows)); + + /* Test keypair_create */ + testrand256(sk); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) != 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) != 0); + CHECK_ILLEGAL(CTX, secp256k1_keypair_create(CTX, NULL, sk)); + CHECK_ILLEGAL(CTX, secp256k1_keypair_create(CTX, &keypair, NULL)); + CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_keypair_create(STATIC_CTX, &keypair, sk)); + CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); + + /* Invalid secret key */ + CHECK(secp256k1_keypair_create(CTX, &keypair, zeros96) == 0); + CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); + CHECK(secp256k1_keypair_create(CTX, &keypair, overflows) == 0); + CHECK(secp256k1_memcmp_var(zeros96, &keypair, sizeof(keypair)) == 0); + + /* Test keypair_pub */ + testrand256(sk); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_pub(CTX, &pk, &keypair) == 1); + CHECK_ILLEGAL(CTX, secp256k1_keypair_pub(CTX, NULL, &keypair)); + CHECK_ILLEGAL(CTX, secp256k1_keypair_pub(CTX, &pk, NULL)); + CHECK(secp256k1_memcmp_var(zeros96, &pk, sizeof(pk)) == 0); + + /* Using an invalid keypair is fine for keypair_pub */ + memset(&keypair, 0, sizeof(keypair)); + CHECK(secp256k1_keypair_pub(CTX, &pk, &keypair) == 1); + CHECK(secp256k1_memcmp_var(zeros96, &pk, sizeof(pk)) == 0); + + /* keypair holds the same pubkey as pubkey_create */ + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_pub(CTX, &pk_tmp, &keypair) == 1); + CHECK(secp256k1_memcmp_var(&pk, &pk_tmp, sizeof(pk)) == 0); + + /** Test keypair_xonly_pub **/ + testrand256(sk); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, &keypair) == 1); + CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_pub(CTX, NULL, &pk_parity, &keypair)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, NULL, &keypair) == 1); + CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, NULL)); + CHECK(secp256k1_memcmp_var(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0); + /* Using an invalid keypair will set the xonly_pk to 0 (first reset + * xonly_pk). */ + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, &keypair) == 1); + memset(&keypair, 0, sizeof(keypair)); + CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_pub(CTX, &xonly_pk, &pk_parity, &keypair)); + CHECK(secp256k1_memcmp_var(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0); + + /** keypair holds the same xonly pubkey as pubkey_create **/ + CHECK(secp256k1_ec_pubkey_create(CTX, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1); + CHECK(secp256k1_memcmp_var(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0); + CHECK(pk_parity == pk_parity_tmp); + + /* Test keypair_seckey */ + testrand256(sk); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_sec(CTX, sk_tmp, &keypair) == 1); + CHECK_ILLEGAL(CTX, secp256k1_keypair_sec(CTX, NULL, &keypair)); + CHECK_ILLEGAL(CTX, secp256k1_keypair_sec(CTX, sk_tmp, NULL)); + CHECK(secp256k1_memcmp_var(zeros96, sk_tmp, sizeof(sk_tmp)) == 0); + + /* keypair returns the same seckey it got */ + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_sec(CTX, sk_tmp, &keypair) == 1); + CHECK(secp256k1_memcmp_var(sk, sk_tmp, sizeof(sk_tmp)) == 0); + + + /* Using an invalid keypair is fine for keypair_seckey */ + memset(&keypair, 0, sizeof(keypair)); + CHECK(secp256k1_keypair_sec(CTX, sk_tmp, &keypair) == 1); + CHECK(secp256k1_memcmp_var(zeros96, sk_tmp, sizeof(sk_tmp)) == 0); +} + +static void test_keypair_add(void) { + unsigned char sk[32]; + secp256k1_keypair keypair; + unsigned char overflows[32]; + unsigned char zeros96[96] = { 0 }; + unsigned char tweak[32]; + int i; + + CHECK(sizeof(zeros96) == sizeof(keypair)); + testrand256(sk); + testrand256(tweak); + memset(overflows, 0xFF, 32); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); + CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_tweak_add(CTX, NULL, tweak)); + CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_tweak_add(CTX, &keypair, NULL)); + /* This does not set the keypair to zeroes */ + CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) != 0); + + /* Invalid tweak zeroes the keypair */ + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, overflows) == 0); + CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0); + + /* A zero tweak is fine */ + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, zeros96) == 1); + + /* Fails if the resulting keypair was (sk=0, pk=infinity) */ + for (i = 0; i < COUNT; i++) { + secp256k1_scalar scalar_tweak; + secp256k1_keypair keypair_tmp; + testrand256(sk); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + memcpy(&keypair_tmp, &keypair, sizeof(keypair)); + /* Because sk may be negated before adding, we need to try with tweak = + * sk as well as tweak = -sk. */ + secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL); + secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak); + secp256k1_scalar_get_b32(tweak, &scalar_tweak); + CHECK((secp256k1_keypair_xonly_tweak_add(CTX, &keypair, sk) == 0) + || (secp256k1_keypair_xonly_tweak_add(CTX, &keypair_tmp, tweak) == 0)); + CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0 + || secp256k1_memcmp_var(&keypair_tmp, zeros96, sizeof(keypair_tmp)) == 0); + } + + /* Invalid keypair with a valid tweak */ + memset(&keypair, 0, sizeof(keypair)); + testrand256(tweak); + CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak)); + CHECK(secp256k1_memcmp_var(&keypair, zeros96, sizeof(keypair)) == 0); + /* Only seckey part of keypair invalid */ + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + memset(&keypair, 0, 32); + CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak)); + /* Only pubkey part of keypair invalid */ + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + memset(&keypair.data[32], 0, 64); + CHECK_ILLEGAL(CTX, secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak)); + + /* Check that the keypair_tweak_add implementation is correct */ + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + for (i = 0; i < COUNT; i++) { + secp256k1_xonly_pubkey internal_pk; + secp256k1_xonly_pubkey output_pk; + secp256k1_pubkey output_pk_xy; + secp256k1_pubkey output_pk_expected; + unsigned char pk32[32]; + unsigned char sk32[32]; + int pk_parity; + + testrand256(tweak); + CHECK(secp256k1_keypair_xonly_pub(CTX, &internal_pk, NULL, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &output_pk, &pk_parity, &keypair) == 1); + + /* Check that it passes xonly_pubkey_tweak_add_check */ + CHECK(secp256k1_xonly_pubkey_serialize(CTX, pk32, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, pk32, pk_parity, &internal_pk, tweak) == 1); + + /* Check that the resulting pubkey matches xonly_pubkey_tweak_add */ + CHECK(secp256k1_keypair_pub(CTX, &output_pk_xy, &keypair) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(CTX, &output_pk_expected, &internal_pk, tweak) == 1); + CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0); + + /* Check that the secret key in the keypair is tweaked correctly */ + CHECK(secp256k1_keypair_sec(CTX, sk32, &keypair) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &output_pk_expected, sk32) == 1); + CHECK(secp256k1_memcmp_var(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0); + } +} + +static void run_extrakeys_tests(void) { + /* xonly key test cases */ + test_xonly_pubkey(); + test_xonly_pubkey_tweak(); + test_xonly_pubkey_tweak_check(); + test_xonly_pubkey_tweak_recursive(); + test_xonly_pubkey_comparison(); + + /* keypair tests */ + test_keypair(); + test_keypair_add(); +} + +#endif diff --git a/src/secp256k1/src/modules/recovery/Makefile.am.include b/src/secp256k1/src/modules/recovery/Makefile.am.include index bf23c26e7..156ea690f 100644 --- a/src/secp256k1/src/modules/recovery/Makefile.am.include +++ b/src/secp256k1/src/modules/recovery/Makefile.am.include @@ -1,8 +1,5 @@ include_HEADERS += include/secp256k1_recovery.h noinst_HEADERS += src/modules/recovery/main_impl.h noinst_HEADERS += src/modules/recovery/tests_impl.h -if USE_BENCHMARK -noinst_PROGRAMS += bench_recover -bench_recover_SOURCES = src/bench_recover.c -bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) -endif +noinst_HEADERS += src/modules/recovery/tests_exhaustive_impl.h +noinst_HEADERS += src/modules/recovery/bench_impl.h diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/modules/recovery/bench_impl.h similarity index 61% rename from src/secp256k1/src/bench_recover.c rename to src/secp256k1/src/modules/recovery/bench_impl.h index 531c02165..57108d452 100644 --- a/src/secp256k1/src/bench_recover.c +++ b/src/secp256k1/src/modules/recovery/bench_impl.h @@ -1,27 +1,27 @@ -/************************************************************************ - * Copyright (c) 2014-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ -#include "include/secp256k1.h" -#include "include/secp256k1_recovery.h" -#include "util.h" -#include "bench.h" +#ifndef SECP256K1_MODULE_RECOVERY_BENCH_H +#define SECP256K1_MODULE_RECOVERY_BENCH_H + +#include "../../../include/secp256k1_recovery.h" typedef struct { secp256k1_context *ctx; unsigned char msg[32]; unsigned char sig[64]; -} bench_recover; +} bench_recover_data; -void bench_recover(void* arg) { +static void bench_recover(void* arg, int iters) { int i; - bench_recover *data = (bench_recover*)arg; + bench_recover_data *data = (bench_recover_data*)arg; secp256k1_pubkey pubkey; unsigned char pubkeyc[33]; - for (i = 0; i < 20000; i++) { + for (i = 0; i < iters; i++) { int j; size_t pubkeylen = 33; secp256k1_ecdsa_recoverable_signature sig; @@ -36,9 +36,9 @@ void bench_recover(void* arg) { } } -void bench_recover_setup(void* arg) { +static void bench_recover_setup(void* arg) { int i; - bench_recover *data = (bench_recover*)arg; + bench_recover_data *data = (bench_recover_data*)arg; for (i = 0; i < 32; i++) { data->msg[i] = 1 + i; @@ -48,13 +48,15 @@ void bench_recover_setup(void* arg) { } } -int main(void) { - bench_recover data; +static void run_recovery_bench(int iters, int argc, char** argv) { + bench_recover_data data; + int d = argc == 1; - data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000); + if (d || have_flag(argc, argv, "ecdsa") || have_flag(argc, argv, "recover") || have_flag(argc, argv, "ecdsa_recover")) run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, iters); secp256k1_context_destroy(data.ctx); - return 0; } + +#endif /* SECP256K1_MODULE_RECOVERY_BENCH_H */ diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h old mode 100755 new mode 100644 index da2140894..76a005e01 --- a/src/secp256k1/src/modules/recovery/main_impl.h +++ b/src/secp256k1/src/modules/recovery/main_impl.h @@ -1,13 +1,13 @@ -/************************************************************************ - * Copyright (c) 2013-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_MODULE_RECOVERY_MAIN_H #define SECP256K1_MODULE_RECOVERY_MAIN_H -#include "include/secp256k1_recovery.h" +#include "../../../include/secp256k1_recovery.h" static void secp256k1_ecdsa_recoverable_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const secp256k1_ecdsa_recoverable_signature* sig) { (void)ctx; @@ -40,7 +40,7 @@ int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* int ret = 1; int overflow = 0; - (void)ctx; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(sig != NULL); ARG_CHECK(input64 != NULL); ARG_CHECK(recid >= 0 && recid <= 3); @@ -60,7 +60,7 @@ int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, int *recid, const secp256k1_ecdsa_recoverable_signature* sig) { secp256k1_scalar r, s; - (void)ctx; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(output64 != NULL); ARG_CHECK(sig != NULL); ARG_CHECK(recid != NULL); @@ -75,7 +75,7 @@ int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx, secp256k1_scalar r, s; int recid; - (void)ctx; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(sig != NULL); ARG_CHECK(sigin != NULL); @@ -84,7 +84,7 @@ int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx, return 1; } -static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) { +static int secp256k1_ecdsa_sig_recover(const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) { unsigned char brx[32]; secp256k1_fe fx; secp256k1_ge x; @@ -98,7 +98,7 @@ static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, cons } secp256k1_scalar_get_b32(brx, sigr); - r = secp256k1_fe_set_b32(&fx, brx); + r = secp256k1_fe_set_b32_limit(&fx, brx); (void)r; VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */ if (recid & 2) { @@ -115,73 +115,39 @@ static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, cons secp256k1_scalar_mul(&u1, &rn, message); secp256k1_scalar_negate(&u1, &u1); secp256k1_scalar_mul(&u2, &rn, sigs); - secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1); + secp256k1_ecmult(&qj, &xj, &u2, &u1); secp256k1_ge_set_gej_var(pubkey, &qj); return !secp256k1_gej_is_infinity(&qj); } -int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { +int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msghash32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { secp256k1_scalar r, s; - secp256k1_scalar sec, non, msg; - int recid; - int ret = 0; - int overflow = 0; + int ret, recid; VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(msg32 != NULL); + ARG_CHECK(msghash32 != NULL); ARG_CHECK(signature != NULL); ARG_CHECK(seckey != NULL); - if (noncefp == NULL) { - noncefp = secp256k1_nonce_function_default; - } - secp256k1_scalar_set_b32(&sec, seckey, &overflow); - /* Fail if the secret key is invalid. */ - if (!overflow && !secp256k1_scalar_is_zero(&sec)) { - unsigned char nonce32[32]; - unsigned int count = 0; - secp256k1_scalar_set_b32(&msg, msg32, NULL); - while (1) { - ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); - if (!ret) { - break; - } - secp256k1_scalar_set_b32(&non, nonce32, &overflow); - if (!secp256k1_scalar_is_zero(&non) && !overflow) { - if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) { - break; - } - } - count++; - } - memset(nonce32, 0, 32); - secp256k1_scalar_clear(&msg); - secp256k1_scalar_clear(&non); - secp256k1_scalar_clear(&sec); - } - if (ret) { - secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid); - } else { - memset(signature, 0, sizeof(*signature)); - } + ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msghash32, seckey, noncefp, noncedata); + secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid); return ret; } -int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) { +int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msghash32) { secp256k1_ge q; secp256k1_scalar r, s; secp256k1_scalar m; int recid; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(msg32 != NULL); + ARG_CHECK(msghash32 != NULL); ARG_CHECK(signature != NULL); ARG_CHECK(pubkey != NULL); secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature); VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */ - secp256k1_scalar_set_b32(&m, msg32, NULL); - if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) { + secp256k1_scalar_set_b32(&m, msghash32, NULL); + if (secp256k1_ecdsa_sig_recover(&r, &s, &q, &m, recid)) { secp256k1_pubkey_save(pubkey, &q); return 1; } else { diff --git a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h new file mode 100644 index 000000000..6bbc02b9a --- /dev/null +++ b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h @@ -0,0 +1,148 @@ +/*********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H +#define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H + +#include "main_impl.h" +#include "../../../include/secp256k1_recovery.h" + +static void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { + int i, j, k; + uint64_t iter = 0; + + /* Loop */ + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { /* message */ + for (j = 1; j < EXHAUSTIVE_TEST_ORDER; j++) { /* key */ + if (skip_section(&iter)) continue; + for (k = 1; k < EXHAUSTIVE_TEST_ORDER; k++) { /* nonce */ + const int starting_k = k; + secp256k1_fe r_dot_y_normalized; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + int expected_recid; + int recid; + int overflow; + secp256k1_scalar_set_int(&msg, i); + secp256k1_scalar_set_int(&sk, j); + secp256k1_scalar_get_b32(sk32, &sk); + secp256k1_scalar_get_b32(msg32, &msg); + + secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + /* Check directly */ + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig); + r_from_k(&expected_r, group, k, &overflow); + CHECK(r == expected_r); + CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER); + /* The recid's second bit is for conveying overflow (R.x value >= group order). + * In the actual secp256k1 this is an astronomically unlikely event, but in the + * small group used here, it will almost certainly be the case for all points. + * Note that this isn't actually useful; full recovery would need to convey + * floor(R.x / group_order), but only one bit is used as that is sufficient + * in the real group. */ + expected_recid = overflow ? 2 : 0; + r_dot_y_normalized = group[k].y; + secp256k1_fe_normalize(&r_dot_y_normalized); + /* Also the recovery id is flipped depending if we hit the low-s branch */ + if ((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER) { + expected_recid |= secp256k1_fe_is_odd(&r_dot_y_normalized); + } else { + expected_recid |= !secp256k1_fe_is_odd(&r_dot_y_normalized); + } + CHECK(recid == expected_recid); + + /* Convert to a standard sig then check */ + secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); + secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); + /* Note that we compute expected_r *after* signing -- this is important + * because our nonce-computing function function might change k during + * signing. */ + r_from_k(&expected_r, group, k, NULL); + CHECK(r == expected_r); + CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } +} + +static void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group) { + /* This is essentially a copy of test_exhaustive_verify, with recovery added */ + int s, r, msg, key; + uint64_t iter = 0; + for (s = 1; s < EXHAUSTIVE_TEST_ORDER; s++) { + for (r = 1; r < EXHAUSTIVE_TEST_ORDER; r++) { + for (msg = 1; msg < EXHAUSTIVE_TEST_ORDER; msg++) { + for (key = 1; key < EXHAUSTIVE_TEST_ORDER; key++) { + secp256k1_ge nonconst_ge; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pk; + secp256k1_scalar sk_s, msg_s, r_s, s_s; + secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s; + int recid = 0; + int k, should_verify; + unsigned char msg32[32]; + + if (skip_section(&iter)) continue; + + secp256k1_scalar_set_int(&s_s, s); + secp256k1_scalar_set_int(&r_s, r); + secp256k1_scalar_set_int(&msg_s, msg); + secp256k1_scalar_set_int(&sk_s, key); + secp256k1_scalar_get_b32(msg32, &msg_s); + + /* Verify by hand */ + /* Run through every k value that gives us this r and check that *one* works. + * Note there could be none, there could be multiple, ECDSA is weird. */ + should_verify = 0; + for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) { + secp256k1_scalar check_x_s; + r_from_k(&check_x_s, group, k, NULL); + if (r_s == check_x_s) { + secp256k1_scalar_set_int(&s_times_k_s, k); + secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); + secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s); + secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s); + should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s); + } + } + /* nb we have a "high s" rule */ + should_verify &= !secp256k1_scalar_is_high(&s_s); + + /* We would like to try recovering the pubkey and checking that it matches, + * but pubkey recovery is impossible in the exhaustive tests (the reason + * being that there are 12 nonzero r values, 12 nonzero points, and no + * overlap between the sets, so there are no valid signatures). */ + + /* Verify by converting to a standard signature and calling verify */ + secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid); + secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} + +static void test_exhaustive_recovery(const secp256k1_context *ctx, const secp256k1_ge *group) { + test_exhaustive_recovery_sign(ctx, group); + test_exhaustive_recovery_verify(ctx, group); +} + +#endif /* SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H */ diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h index 81a126878..7a28a3ce6 100644 --- a/src/secp256k1/src/modules/recovery/tests_impl.h +++ b/src/secp256k1/src/modules/recovery/tests_impl.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2013-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_MODULE_RECOVERY_TESTS_H #define SECP256K1_MODULE_RECOVERY_TESTS_H @@ -25,22 +25,17 @@ static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned c } /* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */ memset(nonce32, 1, 32); - return secp256k1_rand_bits(1); + return testrand_bits(1); } -void test_ecdsa_recovery_api(void) { +static void test_ecdsa_recovery_api(void) { /* Setup contexts that just count errors */ - secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); secp256k1_pubkey pubkey; secp256k1_pubkey recpubkey; secp256k1_ecdsa_signature normal_sig; secp256k1_ecdsa_recoverable_signature recsig; unsigned char privkey[32] = { 1 }; unsigned char message[32] = { 2 }; - int32_t ecount = 0; int recid = 0; unsigned char sig[74]; unsigned char zero_privkey[32] = { 0 }; @@ -49,105 +44,55 @@ void test_ecdsa_recovery_api(void) { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); - /* Construct and verify corresponding public key. */ - CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, privkey) == 1); /* Check bad contexts and NULLs for signing */ - ecount = 0; - CHECK(secp256k1_ecdsa_sign_recoverable(none, &recsig, message, privkey, NULL, NULL) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_sign_recoverable(sign, &recsig, message, privkey, NULL, NULL) == 1); - CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_sign_recoverable(vrfy, &recsig, message, privkey, NULL, NULL) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_sign_recoverable(both, NULL, message, privkey, NULL, NULL) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, NULL, privkey, NULL, NULL) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, NULL, NULL, NULL) == 0); - CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, privkey, NULL, NULL) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_sign_recoverable(CTX, NULL, message, privkey, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_sign_recoverable(CTX, &recsig, NULL, privkey, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, NULL, NULL, NULL)); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_ecdsa_sign_recoverable(STATIC_CTX, &recsig, message, privkey, NULL, NULL)); /* This will fail or succeed randomly, and in either case will not ARG_CHECK failure */ - secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, recovery_test_nonce_function, NULL); - CHECK(ecount == 5); + secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, privkey, recovery_test_nonce_function, NULL); /* These will all fail, but not in ARG_CHECK way */ - CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, zero_privkey, NULL, NULL) == 0); - CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, over_privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, zero_privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, over_privkey, NULL, NULL) == 0); /* This one will succeed. */ - CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); - CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, privkey, NULL, NULL) == 1); /* Check signing with a goofy nonce function */ /* Check bad contexts and NULLs for recovery */ - ecount = 0; - CHECK(secp256k1_ecdsa_recover(none, &recpubkey, &recsig, message) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_recover(sign, &recpubkey, &recsig, message) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_recover(vrfy, &recpubkey, &recsig, message) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, message) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_recover(both, NULL, &recsig, message) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_recover(both, &recpubkey, NULL, message) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, NULL) == 0); - CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_recover(CTX, &recpubkey, &recsig, message) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recover(CTX, NULL, &recsig, message)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recover(CTX, &recpubkey, NULL, message)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recover(CTX, &recpubkey, &recsig, NULL)); /* Check NULLs for conversion */ - CHECK(secp256k1_ecdsa_sign(both, &normal_sig, message, privkey, NULL, NULL) == 1); - ecount = 0; - CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, NULL, &recsig) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, NULL) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, &recsig) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &normal_sig, message, privkey, NULL, NULL) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_convert(CTX, NULL, &recsig)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_convert(CTX, &normal_sig, NULL)); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &normal_sig, &recsig) == 1); /* Check NULLs for de/serialization */ - CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); - ecount = 0; - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, NULL, &recid, &recsig) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, NULL, &recsig) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, NULL) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, &recsig) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &recsig, message, privkey, NULL, NULL) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, NULL, &recid, &recsig)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, NULL, &recsig)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, &recid, NULL)); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, &recid, &recsig) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, NULL, sig, recid) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, NULL, recid) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, -1) == 0); - CHECK(ecount == 6); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, 5) == 0); - CHECK(ecount == 7); - /* overflow in signature will fail but not affect ecount */ + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, NULL, sig, recid)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, NULL, recid)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, sig, -1)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, sig, 5)); + /* overflow in signature will not result in calling illegal_callback */ memcpy(sig, over_privkey, 32); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, recid) == 0); - CHECK(ecount == 7); - - /* cleanup */ - secp256k1_context_destroy(none); - secp256k1_context_destroy(sign); - secp256k1_context_destroy(vrfy); - secp256k1_context_destroy(both); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, sig, recid) == 0); } -void test_ecdsa_recovery_end_to_end(void) { +static void test_ecdsa_recovery_end_to_end(void) { unsigned char extra[32] = {0x00}; unsigned char privkey[32]; unsigned char message[32]; @@ -161,52 +106,52 @@ void test_ecdsa_recovery_end_to_end(void) { /* Generate a random key and message. */ { secp256k1_scalar msg, key; - random_scalar_order_test(&msg); - random_scalar_order_test(&key); + testutil_random_scalar_order_test(&msg); + testutil_random_scalar_order_test(&key); secp256k1_scalar_get_b32(privkey, &key); secp256k1_scalar_get_b32(message, &msg); } /* Construct and verify corresponding public key. */ - CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, privkey) == 1); /* Serialize/parse compact and verify/recover. */ extra[0] = 0; - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[0], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[4], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[1], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[1], message, privkey, NULL, extra) == 1); extra[31] = 1; - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[2], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[2], message, privkey, NULL, extra) == 1); extra[31] = 0; extra[0] = 1; - CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); - CHECK(memcmp(&signature[4], &signature[0], 64) == 0); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(CTX, &rsignature[3], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, &recid, &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_memcmp_var(&signature[4], &signature[0], 64) == 0); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[4], message, &pubkey) == 1); memset(&rsignature[4], 0, sizeof(rsignature[4])); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[4], message, &pubkey) == 1); /* Parse compact (with recovery id) and recover. */ - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 1); - CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &recpubkey, &rsignature[4], message) == 1); + CHECK(secp256k1_memcmp_var(&pubkey, &recpubkey, sizeof(pubkey)) == 0); /* Serialize/destroy/parse signature and verify again. */ - CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); - sig[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); - CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(CTX, sig, &recid, &rsignature[4]) == 1); + sig[testrand_bits(6)] += 1 + testrand_int(255); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(CTX, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[4], message, &pubkey) == 0); /* Recover again */ - CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 || - memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0); + CHECK(secp256k1_ecdsa_recover(CTX, &recpubkey, &rsignature[4], message) == 0 || + secp256k1_memcmp_var(&pubkey, &recpubkey, sizeof(pubkey)) != 0); } /* Tests several edge cases. */ -void test_ecdsa_recovery_edge_cases(void) { +static void test_ecdsa_recovery_edge_cases(void) { const unsigned char msg32[32] = { 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 'v', 'e', 'r', 'y', ' ', 's', @@ -215,7 +160,7 @@ void test_ecdsa_recovery_edge_cases(void) { }; const unsigned char sig64[64] = { /* Generated by signing the above message with nonce 'This is the nonce we will use...' - * and secret key 0 (which is not valid), resulting in recid 0. */ + * and secret key 0 (which is not valid), resulting in recid 1. */ 0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8, 0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96, 0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63, @@ -242,14 +187,14 @@ void test_ecdsa_recovery_edge_cases(void) { secp256k1_ecdsa_signature sig; int recid; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 0)); - CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 1)); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 2)); - CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 3)); - CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sig64, 0)); + CHECK(!secp256k1_ecdsa_recover(CTX, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sig64, 1)); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sig64, 2)); + CHECK(!secp256k1_ecdsa_recover(CTX, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sig64, 3)); + CHECK(!secp256k1_ecdsa_recover(CTX, &pubkey, &rsig, msg32)); for (recid = 0; recid < 4; recid++) { int i; @@ -294,40 +239,40 @@ void test_ecdsa_recovery_edge_cases(void) { 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x45, 0x02, 0x01, 0x04 }; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 1); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigb64, recid) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkeyb, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 1); for (recid2 = 0; recid2 < 4; recid2++) { secp256k1_pubkey pubkey2b; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid2) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkey2b, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigb64, recid2) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkey2b, &rsig, msg32) == 1); /* Verifying with (order + r,4) should always fail. */ - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderlong, sizeof(sigbderlong)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); } /* DER parsing tests. */ /* Zero length r/s. */ - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0); /* Leading zeros. */ - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0); sigbderalt3[4] = 1; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); sigbderalt4[7] = 1; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); /* Damage signature. */ sigbder[7]++; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); sigbder[7]--; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, 6) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder) - 1) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, 6) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, sizeof(sigbder) - 1) == 0); for(i = 0; i < 8; i++) { int c; unsigned char orig = sigbder[i]; @@ -337,7 +282,7 @@ void test_ecdsa_recovery_edge_cases(void) { continue; } sigbder[i] = c; - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyb) == 0); } sigbder[i] = orig; } @@ -358,33 +303,33 @@ void test_ecdsa_recovery_edge_cases(void) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, }; secp256k1_pubkey pubkeyc; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyc, &rsig, msg32) == 1); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkeyc, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyc) == 1); sigcder[4] = 0; sigc64[31] = 0; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyc) == 0); sigcder[4] = 1; sigcder[7] = 0; sigc64[31] = 1; sigc64[63] = 0; - CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); - CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(CTX, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg32, &pubkeyc) == 0); } } -void run_recovery_tests(void) { +static void run_recovery_tests(void) { int i; - for (i = 0; i < count; i++) { + for (i = 0; i < COUNT; i++) { test_ecdsa_recovery_api(); } - for (i = 0; i < 64*count; i++) { + for (i = 0; i < 64*COUNT; i++) { test_ecdsa_recovery_end_to_end(); } test_ecdsa_recovery_edge_cases(); diff --git a/src/secp256k1/src/modules/schnorrsig/Makefile.am.include b/src/secp256k1/src/modules/schnorrsig/Makefile.am.include new file mode 100644 index 000000000..654fa2e5a --- /dev/null +++ b/src/secp256k1/src/modules/schnorrsig/Makefile.am.include @@ -0,0 +1,5 @@ +include_HEADERS += include/secp256k1_schnorrsig.h +noinst_HEADERS += src/modules/schnorrsig/main_impl.h +noinst_HEADERS += src/modules/schnorrsig/tests_impl.h +noinst_HEADERS += src/modules/schnorrsig/tests_exhaustive_impl.h +noinst_HEADERS += src/modules/schnorrsig/bench_impl.h diff --git a/src/secp256k1/src/modules/schnorrsig/bench_impl.h b/src/secp256k1/src/modules/schnorrsig/bench_impl.h new file mode 100644 index 000000000..93a878ede --- /dev/null +++ b/src/secp256k1/src/modules/schnorrsig/bench_impl.h @@ -0,0 +1,104 @@ +/*********************************************************************** + * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_SCHNORRSIG_BENCH_H +#define SECP256K1_MODULE_SCHNORRSIG_BENCH_H + +#include "../../../include/secp256k1_schnorrsig.h" + +#define MSGLEN 32 + +typedef struct { + secp256k1_context *ctx; + int n; + + const secp256k1_keypair **keypairs; + const unsigned char **pk; + const unsigned char **sigs; + const unsigned char **msgs; +} bench_schnorrsig_data; + +static void bench_schnorrsig_sign(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i; + unsigned char msg[MSGLEN] = {0}; + unsigned char sig[64]; + + for (i = 0; i < iters; i++) { + msg[0] = i; + msg[1] = i >> 8; + CHECK(secp256k1_schnorrsig_sign_custom(data->ctx, sig, msg, MSGLEN, data->keypairs[i], NULL)); + } +} + +static void bench_schnorrsig_verify(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i; + + for (i = 0; i < iters; i++) { + secp256k1_xonly_pubkey pk; + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[i]) == 1); + CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], MSGLEN, &pk)); + } +} + +static void run_schnorrsig_bench(int iters, int argc, char** argv) { + int i; + bench_schnorrsig_data data; + int d = argc == 1; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + data.keypairs = (const secp256k1_keypair **)malloc(iters * sizeof(secp256k1_keypair *)); + data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + + CHECK(MSGLEN >= 4); + for (i = 0; i < iters; i++) { + unsigned char sk[32]; + unsigned char *msg = (unsigned char *)malloc(MSGLEN); + unsigned char *sig = (unsigned char *)malloc(64); + secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair)); + unsigned char *pk_char = (unsigned char *)malloc(32); + secp256k1_xonly_pubkey pk; + msg[0] = sk[0] = i; + msg[1] = sk[1] = i >> 8; + msg[2] = sk[2] = i >> 16; + msg[3] = sk[3] = i >> 24; + memset(&msg[4], 'm', MSGLEN - 4); + memset(&sk[4], 's', 28); + + data.keypairs[i] = keypair; + data.pk[i] = pk_char; + data.msgs[i] = msg; + data.sigs[i] = sig; + + CHECK(secp256k1_keypair_create(data.ctx, keypair, sk)); + CHECK(secp256k1_schnorrsig_sign_custom(data.ctx, sig, msg, MSGLEN, keypair, NULL)); + CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair)); + CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1); + } + + if (d || have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "schnorrsig_sign")) run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters); + if (d || have_flag(argc, argv, "schnorrsig") || have_flag(argc, argv, "verify") || have_flag(argc, argv, "schnorrsig_verify")) run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters); + + for (i = 0; i < iters; i++) { + free((void *)data.keypairs[i]); + free((void *)data.pk[i]); + free((void *)data.msgs[i]); + free((void *)data.sigs[i]); + } + + /* Casting to (void *) avoids a stupid warning in MSVC. */ + free((void *)data.keypairs); + free((void *)data.pk); + free((void *)data.msgs); + free((void *)data.sigs); + + secp256k1_context_destroy(data.ctx); +} + +#endif diff --git a/src/secp256k1/src/modules/schnorrsig/main_impl.h b/src/secp256k1/src/modules/schnorrsig/main_impl.h new file mode 100644 index 000000000..26727e465 --- /dev/null +++ b/src/secp256k1/src/modules/schnorrsig/main_impl.h @@ -0,0 +1,267 @@ +/*********************************************************************** + * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_SCHNORRSIG_MAIN_H +#define SECP256K1_MODULE_SCHNORRSIG_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_schnorrsig.h" +#include "../../hash.h" + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0340/nonce")||SHA256("BIP0340/nonce"). */ +static void secp256k1_nonce_function_bip340_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x46615b35ul; + sha->s[1] = 0xf4bfbff7ul; + sha->s[2] = 0x9f8dc671ul; + sha->s[3] = 0x83627ab3ul; + sha->s[4] = 0x60217180ul; + sha->s[5] = 0x57358661ul; + sha->s[6] = 0x21a29e54ul; + sha->s[7] = 0x68b07b4cul; + + sha->bytes = 64; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0340/aux")||SHA256("BIP0340/aux"). */ +static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x24dd3219ul; + sha->s[1] = 0x4eba7e70ul; + sha->s[2] = 0xca0fabb9ul; + sha->s[3] = 0x0fa3166dul; + sha->s[4] = 0x3afbe4b1ul; + sha->s[5] = 0x4c44df97ul; + sha->s[6] = 0x4aac2739ul; + sha->s[7] = 0x249e850aul; + + sha->bytes = 64; +} + +/* algo argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340 + * by using the correct tagged hash function. */ +static const unsigned char bip340_algo[13] = "BIP0340/nonce"; + +static const unsigned char schnorrsig_extraparams_magic[4] = SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC; + +static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { + secp256k1_sha256 sha; + unsigned char masked_key[32]; + int i; + + if (algo == NULL) { + return 0; + } + + if (data != NULL) { + secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha); + secp256k1_sha256_write(&sha, data, 32); + secp256k1_sha256_finalize(&sha, masked_key); + for (i = 0; i < 32; i++) { + masked_key[i] ^= key32[i]; + } + } else { + /* Precomputed TaggedHash("BIP0340/aux", 0x0000...00); */ + static const unsigned char ZERO_MASK[32] = { + 84, 241, 105, 207, 201, 226, 229, 114, + 116, 128, 68, 31, 144, 186, 37, 196, + 136, 244, 97, 199, 11, 94, 165, 220, + 170, 247, 175, 105, 39, 10, 165, 20 + }; + for (i = 0; i < 32; i++) { + masked_key[i] = key32[i] ^ ZERO_MASK[i]; + } + } + + /* Tag the hash with algo which is important to avoid nonce reuse across + * algorithms. If this nonce function is used in BIP-340 signing as defined + * in the spec, an optimized tagging implementation is used. */ + if (algolen == sizeof(bip340_algo) + && secp256k1_memcmp_var(algo, bip340_algo, algolen) == 0) { + secp256k1_nonce_function_bip340_sha256_tagged(&sha); + } else { + secp256k1_sha256_initialize_tagged(&sha, algo, algolen); + } + + /* Hash masked-key||pk||msg using the tagged hash as per the spec */ + secp256k1_sha256_write(&sha, masked_key, 32); + secp256k1_sha256_write(&sha, xonly_pk32, 32); + secp256k1_sha256_write(&sha, msg, msglen); + secp256k1_sha256_finalize(&sha, nonce32); + return 1; +} + +const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340; + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0340/challenge")||SHA256("BIP0340/challenge"). */ +static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x9cecba11ul; + sha->s[1] = 0x23925381ul; + sha->s[2] = 0x11679112ul; + sha->s[3] = 0xd1627e0ful; + sha->s[4] = 0x97c87550ul; + sha->s[5] = 0x003cc765ul; + sha->s[6] = 0x90f61164ul; + sha->s[7] = 0x33e9b66aul; + sha->bytes = 64; +} + +static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned char *r32, const unsigned char *msg, size_t msglen, const unsigned char *pubkey32) +{ + unsigned char buf[32]; + secp256k1_sha256 sha; + + /* tagged hash(r.x, pk.x, msg) */ + secp256k1_schnorrsig_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, r32, 32); + secp256k1_sha256_write(&sha, pubkey32, 32); + secp256k1_sha256_write(&sha, msg, msglen); + secp256k1_sha256_finalize(&sha, buf); + /* Set scalar e to the challenge hash modulo the curve order as per + * BIP340. */ + secp256k1_scalar_set_b32(e, buf, NULL); +} + +static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) { + secp256k1_scalar sk; + secp256k1_scalar e; + secp256k1_scalar k; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_ge r; + unsigned char buf[32] = { 0 }; + unsigned char pk_buf[32]; + unsigned char seckey[32]; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(msg != NULL || msglen == 0); + ARG_CHECK(keypair != NULL); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_bip340; + } + + ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair); + /* Because we are signing for a x-only pubkey, the secret key is negated + * before signing if the point corresponding to the secret key does not + * have an even Y. */ + if (secp256k1_fe_is_odd(&pk.y)) { + secp256k1_scalar_negate(&sk, &sk); + } + + secp256k1_scalar_get_b32(seckey, &sk); + secp256k1_fe_get_b32(pk_buf, &pk.x); + ret &= !!noncefp(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), ndata); + secp256k1_scalar_set_b32(&k, buf, NULL); + ret &= !secp256k1_scalar_is_zero(&k); + secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + + /* We declassify r to allow using it as a branch point. This is fine + * because r is not a secret. */ + secp256k1_declassify(ctx, &r, sizeof(r)); + secp256k1_fe_normalize_var(&r.y); + if (secp256k1_fe_is_odd(&r.y)) { + secp256k1_scalar_negate(&k, &k); + } + secp256k1_fe_normalize_var(&r.x); + secp256k1_fe_get_b32(&sig64[0], &r.x); + + secp256k1_schnorrsig_challenge(&e, &sig64[0], msg, msglen, pk_buf); + secp256k1_scalar_mul(&e, &e, &sk); + secp256k1_scalar_add(&e, &e, &k); + secp256k1_scalar_get_b32(&sig64[32], &e); + + secp256k1_memczero(sig64, 64, !ret); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&sk); + memset(seckey, 0, sizeof(seckey)); + + return ret; +} + +int secp256k1_schnorrsig_sign32(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) { + /* We cast away const from the passed aux_rand32 argument since we know the default nonce function does not modify it. */ + return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, (unsigned char*)aux_rand32); +} + +int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) { + return secp256k1_schnorrsig_sign32(ctx, sig64, msg32, keypair, aux_rand32); +} + +int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_schnorrsig_extraparams *extraparams) { + secp256k1_nonce_function_hardened noncefp = NULL; + void *ndata = NULL; + VERIFY_CHECK(ctx != NULL); + + if (extraparams != NULL) { + ARG_CHECK(secp256k1_memcmp_var(extraparams->magic, + schnorrsig_extraparams_magic, + sizeof(extraparams->magic)) == 0); + noncefp = extraparams->noncefp; + ndata = extraparams->ndata; + } + return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata); +} + +int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) { + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_gej pkj; + secp256k1_fe rx; + secp256k1_ge r; + unsigned char buf[32]; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(msg != NULL || msglen == 0); + ARG_CHECK(pubkey != NULL); + + if (!secp256k1_fe_set_b32_limit(&rx, &sig64[0])) { + return 0; + } + + secp256k1_scalar_set_b32(&s, &sig64[32], &overflow); + if (overflow) { + return 0; + } + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + + /* Compute e. */ + secp256k1_fe_get_b32(buf, &pk.x); + secp256k1_schnorrsig_challenge(&e, &sig64[0], msg, msglen, buf); + + /* Compute rj = s*G + (-e)*pkj */ + secp256k1_scalar_negate(&e, &e); + secp256k1_gej_set_ge(&pkj, &pk); + secp256k1_ecmult(&rj, &pkj, &e, &s); + + secp256k1_ge_set_gej_var(&r, &rj); + if (secp256k1_ge_is_infinity(&r)) { + return 0; + } + + secp256k1_fe_normalize_var(&r.y); + return !secp256k1_fe_is_odd(&r.y) && + secp256k1_fe_equal(&rx, &r.x); +} + +#endif diff --git a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h new file mode 100644 index 000000000..601b54975 --- /dev/null +++ b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h @@ -0,0 +1,214 @@ +/*********************************************************************** + * Copyright (c) 2020 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H +#define SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H + +#include "../../../include/secp256k1_schnorrsig.h" +#include "main_impl.h" + +static const unsigned char invalid_pubkey_bytes[][32] = { + /* 0 */ + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + /* 2 */ + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 + }, + /* order */ + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ((EXHAUSTIVE_TEST_ORDER + 0UL) >> 24) & 0xFF, + ((EXHAUSTIVE_TEST_ORDER + 0UL) >> 16) & 0xFF, + ((EXHAUSTIVE_TEST_ORDER + 0UL) >> 8) & 0xFF, + (EXHAUSTIVE_TEST_ORDER + 0UL) & 0xFF + }, + /* order + 1 */ + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ((EXHAUSTIVE_TEST_ORDER + 1UL) >> 24) & 0xFF, + ((EXHAUSTIVE_TEST_ORDER + 1UL) >> 16) & 0xFF, + ((EXHAUSTIVE_TEST_ORDER + 1UL) >> 8) & 0xFF, + (EXHAUSTIVE_TEST_ORDER + 1UL) & 0xFF + }, + /* field size */ + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F + }, + /* field size + 1 (note that 1 is legal) */ + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 + }, + /* 2^256 - 1 */ + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + } +}; + +#define NUM_INVALID_KEYS (sizeof(invalid_pubkey_bytes) / sizeof(invalid_pubkey_bytes[0])) + +static int secp256k1_hardened_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg, + size_t msglen, + const unsigned char *key32, const unsigned char *xonly_pk32, + const unsigned char *algo, size_t algolen, + void* data) { + secp256k1_scalar s; + int *idata = data; + (void)msg; + (void)msglen; + (void)key32; + (void)xonly_pk32; + (void)algo; + (void)algolen; + secp256k1_scalar_set_int(&s, *idata); + secp256k1_scalar_get_b32(nonce32, &s); + return 1; +} + +static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, const secp256k1_xonly_pubkey* pubkeys, unsigned char (*xonly_pubkey_bytes)[32], const int* parities) { + int d; + uint64_t iter = 0; + /* Iterate over the possible public keys to verify against (through their corresponding DL d). */ + for (d = 1; d <= EXHAUSTIVE_TEST_ORDER / 2; ++d) { + int actual_d; + unsigned k; + unsigned char pk32[32]; + memcpy(pk32, xonly_pubkey_bytes[d - 1], 32); + actual_d = parities[d - 1] ? EXHAUSTIVE_TEST_ORDER - d : d; + /* Iterate over the possible valid first 32 bytes in the signature, through their corresponding DL k. + Values above EXHAUSTIVE_TEST_ORDER/2 refer to the entries in invalid_pubkey_bytes. */ + for (k = 1; k <= EXHAUSTIVE_TEST_ORDER / 2 + NUM_INVALID_KEYS; ++k) { + unsigned char sig64[64]; + int actual_k = -1; + int e_done[EXHAUSTIVE_TEST_ORDER] = {0}; + int e_count_done = 0; + if (skip_section(&iter)) continue; + if (k <= EXHAUSTIVE_TEST_ORDER / 2) { + memcpy(sig64, xonly_pubkey_bytes[k - 1], 32); + actual_k = parities[k - 1] ? EXHAUSTIVE_TEST_ORDER - k : k; + } else { + memcpy(sig64, invalid_pubkey_bytes[k - 1 - EXHAUSTIVE_TEST_ORDER / 2], 32); + } + /* Randomly generate messages until all challenges have been hit. */ + while (e_count_done < EXHAUSTIVE_TEST_ORDER) { + secp256k1_scalar e; + unsigned char msg32[32]; + testrand256(msg32); + secp256k1_schnorrsig_challenge(&e, sig64, msg32, sizeof(msg32), pk32); + /* Only do work if we hit a challenge we haven't tried before. */ + if (!e_done[e]) { + /* Iterate over the possible valid last 32 bytes in the signature. + 0..order=that s value; order+1=random bytes */ + int count_valid = 0; + unsigned int s; + for (s = 0; s <= EXHAUSTIVE_TEST_ORDER + 1; ++s) { + int expect_valid, valid; + if (s <= EXHAUSTIVE_TEST_ORDER) { + memset(sig64 + 32, 0, 32); + secp256k1_write_be32(sig64 + 60, s); + expect_valid = actual_k != -1 && s != EXHAUSTIVE_TEST_ORDER && + (s == (actual_k + actual_d * e) % EXHAUSTIVE_TEST_ORDER); + } else { + testrand256(sig64 + 32); + expect_valid = 0; + } + valid = secp256k1_schnorrsig_verify(ctx, sig64, msg32, sizeof(msg32), &pubkeys[d - 1]); + CHECK(valid == expect_valid); + count_valid += valid; + } + /* Exactly one s value must verify, unless R is illegal. */ + CHECK(count_valid == (actual_k != -1)); + /* Don't retry other messages that result in the same challenge. */ + e_done[e] = 1; + ++e_count_done; + } + } + } + } +} + +static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsigned char (*xonly_pubkey_bytes)[32], const secp256k1_keypair* keypairs, const int* parities) { + int d, k; + uint64_t iter = 0; + secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + + /* Loop over keys. */ + for (d = 1; d < EXHAUSTIVE_TEST_ORDER; ++d) { + int actual_d = d; + if (parities[d - 1]) actual_d = EXHAUSTIVE_TEST_ORDER - d; + /* Loop over nonces. */ + for (k = 1; k < EXHAUSTIVE_TEST_ORDER; ++k) { + int e_done[EXHAUSTIVE_TEST_ORDER] = {0}; + int e_count_done = 0; + unsigned char msg32[32]; + unsigned char sig64[64]; + int actual_k = k; + if (skip_section(&iter)) continue; + extraparams.noncefp = secp256k1_hardened_nonce_function_smallint; + extraparams.ndata = &k; + if (parities[k - 1]) actual_k = EXHAUSTIVE_TEST_ORDER - k; + /* Generate random messages until all challenges have been tried. */ + while (e_count_done < EXHAUSTIVE_TEST_ORDER) { + secp256k1_scalar e; + testrand256(msg32); + secp256k1_schnorrsig_challenge(&e, xonly_pubkey_bytes[k - 1], msg32, sizeof(msg32), xonly_pubkey_bytes[d - 1]); + /* Only do work if we hit a challenge we haven't tried before. */ + if (!e_done[e]) { + secp256k1_scalar expected_s = (actual_k + e * actual_d) % EXHAUSTIVE_TEST_ORDER; + unsigned char expected_s_bytes[32]; + secp256k1_scalar_get_b32(expected_s_bytes, &expected_s); + /* Invoke the real function to construct a signature. */ + CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig64, msg32, sizeof(msg32), &keypairs[d - 1], &extraparams)); + /* The first 32 bytes must match the xonly pubkey for the specified k. */ + CHECK(secp256k1_memcmp_var(sig64, xonly_pubkey_bytes[k - 1], 32) == 0); + /* The last 32 bytes must match the expected s value. */ + CHECK(secp256k1_memcmp_var(sig64 + 32, expected_s_bytes, 32) == 0); + /* Don't retry other messages that result in the same challenge. */ + e_done[e] = 1; + ++e_count_done; + } + } + } + } +} + +static void test_exhaustive_schnorrsig(const secp256k1_context *ctx) { + secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1]; + secp256k1_xonly_pubkey xonly_pubkey[EXHAUSTIVE_TEST_ORDER - 1]; + int parity[EXHAUSTIVE_TEST_ORDER - 1]; + unsigned char xonly_pubkey_bytes[EXHAUSTIVE_TEST_ORDER - 1][32]; + unsigned i; + + /* Verify that all invalid_pubkey_bytes are actually invalid. */ + for (i = 0; i < NUM_INVALID_KEYS; ++i) { + secp256k1_xonly_pubkey pk; + CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk, invalid_pubkey_bytes[i])); + } + + /* Construct keypairs and xonly-pubkeys for the entire group. */ + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; ++i) { + secp256k1_scalar scalar_i; + unsigned char buf[32]; + secp256k1_scalar_set_int(&scalar_i, i); + secp256k1_scalar_get_b32(buf, &scalar_i); + CHECK(secp256k1_keypair_create(ctx, &keypair[i - 1], buf)); + CHECK(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey[i - 1], &parity[i - 1], &keypair[i - 1])); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey_bytes[i - 1], &xonly_pubkey[i - 1])); + } + + test_exhaustive_schnorrsig_sign(ctx, xonly_pubkey_bytes, keypair, parity); + test_exhaustive_schnorrsig_verify(ctx, xonly_pubkey, xonly_pubkey_bytes, parity); +} + +#endif diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h new file mode 100644 index 000000000..aa4fc3827 --- /dev/null +++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h @@ -0,0 +1,982 @@ +/*********************************************************************** + * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_SCHNORRSIG_TESTS_H +#define SECP256K1_MODULE_SCHNORRSIG_TESTS_H + +#include "../../../include/secp256k1_schnorrsig.h" + +/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many + * bytes) changes the hash function + */ +static void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes, size_t msglen, size_t algolen) { + unsigned char nonces[2][32]; + CHECK(nonce_function_bip340(nonces[0], args[0], msglen, args[1], args[2], args[3], algolen, args[4]) == 1); + testrand_flip(args[n_flip], n_bytes); + CHECK(nonce_function_bip340(nonces[1], args[0], msglen, args[1], args[2], args[3], algolen, args[4]) == 1); + CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0); +} + +static void run_nonce_function_bip340_tests(void) { + unsigned char tag[13] = "BIP0340/nonce"; + unsigned char aux_tag[11] = "BIP0340/aux"; + unsigned char algo[13] = "BIP0340/nonce"; + size_t algolen = sizeof(algo); + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + unsigned char nonce[32], nonce_z[32]; + unsigned char msg[32]; + size_t msglen = sizeof(msg); + unsigned char key[32]; + unsigned char pk[32]; + unsigned char aux_rand[32]; + unsigned char *args[5]; + int i; + + /* Check that hash initialized by + * secp256k1_nonce_function_bip340_sha256_tagged has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag)); + secp256k1_nonce_function_bip340_sha256_tagged(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_nonce_function_bip340_sha256_tagged_aux has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag)); + secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + testrand256(msg); + testrand256(key); + testrand256(pk); + testrand256(aux_rand); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = msg; + args[1] = key; + args[2] = pk; + args[3] = algo; + args[4] = aux_rand; + for (i = 0; i < COUNT; i++) { + nonce_function_bip340_bitflip(args, 0, 32, msglen, algolen); + nonce_function_bip340_bitflip(args, 1, 32, msglen, algolen); + nonce_function_bip340_bitflip(args, 2, 32, msglen, algolen); + /* Flip algo special case "BIP0340/nonce" */ + nonce_function_bip340_bitflip(args, 3, algolen, msglen, algolen); + /* Flip algo again */ + nonce_function_bip340_bitflip(args, 3, algolen, msglen, algolen); + nonce_function_bip340_bitflip(args, 4, 32, msglen, algolen); + } + + /* NULL algo is disallowed */ + CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, NULL, 0, NULL) == 0); + CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1); + /* Other algo is fine */ + testrand_bytes_test(algo, algolen); + CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1); + + for (i = 0; i < COUNT; i++) { + unsigned char nonce2[32]; + uint32_t offset = testrand_int(msglen - 1); + size_t msglen_tmp = (msglen + offset) % msglen; + size_t algolen_tmp; + + /* Different msglen gives different nonce */ + CHECK(nonce_function_bip340(nonce2, msg, msglen_tmp, key, pk, algo, algolen, NULL) == 1); + CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); + + /* Different algolen gives different nonce */ + offset = testrand_int(algolen - 1); + algolen_tmp = (algolen + offset) % algolen; + CHECK(nonce_function_bip340(nonce2, msg, msglen, key, pk, algo, algolen_tmp, NULL) == 1); + CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); + } + + /* NULL aux_rand argument is allowed, and identical to passing all zero aux_rand. */ + memset(aux_rand, 0, 32); + CHECK(nonce_function_bip340(nonce_z, msg, msglen, key, pk, algo, algolen, &aux_rand) == 1); + CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1); + CHECK(secp256k1_memcmp_var(nonce_z, nonce, 32) == 0); +} + +static void test_schnorrsig_api(void) { + unsigned char sk1[32]; + unsigned char sk2[32]; + unsigned char sk3[32]; + unsigned char msg[32]; + secp256k1_keypair keypairs[3]; + secp256k1_keypair invalid_keypair = {{ 0 }}; + secp256k1_xonly_pubkey pk[3]; + secp256k1_xonly_pubkey zero_pk; + unsigned char sig[64]; + secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + secp256k1_schnorrsig_extraparams invalid_extraparams = {{ 0 }, NULL, NULL}; + + testrand256(sk1); + testrand256(sk2); + testrand256(sk3); + testrand256(msg); + CHECK(secp256k1_keypair_create(CTX, &keypairs[0], sk1) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypairs[1], sk2) == 1); + CHECK(secp256k1_keypair_create(CTX, &keypairs[2], sk3) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk[0], NULL, &keypairs[0]) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk[1], NULL, &keypairs[1]) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk[2], NULL, &keypairs[2]) == 1); + memset(&zero_pk, 0, sizeof(zero_pk)); + + /** main test body **/ + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypairs[0], NULL) == 1); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign32(CTX, NULL, msg, &keypairs[0], NULL)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign32(CTX, sig, NULL, &keypairs[0], NULL)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign32(CTX, sig, msg, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign32(CTX, sig, msg, &invalid_keypair, NULL)); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_schnorrsig_sign32(STATIC_CTX, sig, msg, &keypairs[0], NULL)); + + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 1); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign_custom(CTX, NULL, msg, sizeof(msg), &keypairs[0], &extraparams)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign_custom(CTX, sig, NULL, sizeof(msg), &keypairs[0], &extraparams)); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, NULL, 0, &keypairs[0], &extraparams) == 1); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), NULL, &extraparams)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &invalid_keypair, &extraparams)); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypairs[0], NULL) == 1); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypairs[0], &invalid_extraparams)); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_schnorrsig_sign_custom(STATIC_CTX, sig, msg, sizeof(msg), &keypairs[0], &extraparams)); + + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypairs[0], NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &pk[0]) == 1); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_verify(CTX, NULL, msg, sizeof(msg), &pk[0])); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_verify(CTX, sig, NULL, sizeof(msg), &pk[0])); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, NULL, 0, &pk[0]) == 0); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), NULL)); + CHECK_ILLEGAL(CTX, secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &zero_pk)); +} + +/* Checks that hash initialized by secp256k1_schnorrsig_sha256_tagged has the + * expected state. */ +static void test_schnorrsig_sha256_tagged(void) { + unsigned char tag[17] = "BIP0340/challenge"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + + secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag)); + secp256k1_schnorrsig_sha256_tagged(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); +} + +/* Helper function for schnorrsig_bip_vectors + * Signs the message and checks that it's the same as expected_sig. */ +static void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *aux_rand, const unsigned char *msg, size_t msglen, const unsigned char *expected_sig) { + unsigned char sig[64]; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey pk, pk_expected; + + secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + extraparams.ndata = (unsigned char*)aux_rand; + + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, msglen, &keypair, &extraparams)); + CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0); + if (msglen == 32) { + memset(sig, 0, 64); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypair, aux_rand)); + CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0); + } + + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk_expected, pk_serialized)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); + CHECK(secp256k1_memcmp_var(&pk, &pk_expected, sizeof(pk)) == 0); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, msglen, &pk)); +} + +/* Helper function for schnorrsig_bip_vectors + * Checks that both verify and verify_batch (TODO) return the same value as expected. */ +static void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg, size_t msglen, const unsigned char *sig, int expected) { + secp256k1_xonly_pubkey pk; + + CHECK(secp256k1_xonly_pubkey_parse(CTX, &pk, pk_serialized)); + CHECK(expected == secp256k1_schnorrsig_verify(CTX, sig, msg, msglen, &pk)); +} + +/* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See + * https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv. */ +static void test_schnorrsig_bip_vectors(void) { + { + /* Test vector 0 */ + const unsigned char sk[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 + }; + const unsigned char pk[32] = { + 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, + 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, + 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, + 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char msg[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig[64] = { + 0xE9, 0x07, 0x83, 0x1F, 0x80, 0x84, 0x8D, 0x10, + 0x69, 0xA5, 0x37, 0x1B, 0x40, 0x24, 0x10, 0x36, + 0x4B, 0xDF, 0x1C, 0x5F, 0x83, 0x07, 0xB0, 0x08, + 0x4C, 0x55, 0xF1, 0xCE, 0x2D, 0xCA, 0x82, 0x15, + 0x25, 0xF6, 0x6A, 0x4A, 0x85, 0xEA, 0x8B, 0x71, + 0xE4, 0x82, 0xA7, 0x4F, 0x38, 0x2D, 0x2C, 0xE5, + 0xEB, 0xEE, 0xE8, 0xFD, 0xB2, 0x17, 0x2F, 0x47, + 0x7D, 0xF4, 0x90, 0x0D, 0x31, 0x05, 0x36, 0xC0 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 1 */ + const unsigned char sk[32] = { + 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, + 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, + 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, + 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF + }; + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x68, 0x96, 0xBD, 0x60, 0xEE, 0xAE, 0x29, 0x6D, + 0xB4, 0x8A, 0x22, 0x9F, 0xF7, 0x1D, 0xFE, 0x07, + 0x1B, 0xDE, 0x41, 0x3E, 0x6D, 0x43, 0xF9, 0x17, + 0xDC, 0x8D, 0xCF, 0x8C, 0x78, 0xDE, 0x33, 0x41, + 0x89, 0x06, 0xD1, 0x1A, 0xC9, 0x76, 0xAB, 0xCC, + 0xB2, 0x0B, 0x09, 0x12, 0x92, 0xBF, 0xF4, 0xEA, + 0x89, 0x7E, 0xFC, 0xB6, 0x39, 0xEA, 0x87, 0x1C, + 0xFA, 0x95, 0xF6, 0xDE, 0x33, 0x9E, 0x4B, 0x0A + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 2 */ + const unsigned char sk[32] = { + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC9 + }; + const unsigned char pk[32] = { + 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, + 0x12, 0x1F, 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, + 0x01, 0x39, 0x71, 0x53, 0x09, 0xB0, 0x86, 0xC9, + 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 + }; + const unsigned char aux_rand[32] = { + 0xC8, 0x7A, 0xA5, 0x38, 0x24, 0xB4, 0xD7, 0xAE, + 0x2E, 0xB0, 0x35, 0xA2, 0xB5, 0xBB, 0xBC, 0xCC, + 0x08, 0x0E, 0x76, 0xCD, 0xC6, 0xD1, 0x69, 0x2C, + 0x4B, 0x0B, 0x62, 0xD7, 0x98, 0xE6, 0xD9, 0x06 + }; + const unsigned char msg[32] = { + 0x7E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig[64] = { + 0x58, 0x31, 0xAA, 0xEE, 0xD7, 0xB4, 0x4B, 0xB7, + 0x4E, 0x5E, 0xAB, 0x94, 0xBA, 0x9D, 0x42, 0x94, + 0xC4, 0x9B, 0xCF, 0x2A, 0x60, 0x72, 0x8D, 0x8B, + 0x4C, 0x20, 0x0F, 0x50, 0xDD, 0x31, 0x3C, 0x1B, + 0xAB, 0x74, 0x58, 0x79, 0xA5, 0xAD, 0x95, 0x4A, + 0x72, 0xC4, 0x5A, 0x91, 0xC3, 0xA5, 0x1D, 0x3C, + 0x7A, 0xDE, 0xA9, 0x8D, 0x82, 0xF8, 0x48, 0x1E, + 0x0E, 0x1E, 0x03, 0x67, 0x4A, 0x6F, 0x3F, 0xB7 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 3 */ + const unsigned char sk[32] = { + 0x0B, 0x43, 0x2B, 0x26, 0x77, 0x93, 0x73, 0x81, + 0xAE, 0xF0, 0x5B, 0xB0, 0x2A, 0x66, 0xEC, 0xD0, + 0x12, 0x77, 0x30, 0x62, 0xCF, 0x3F, 0xA2, 0x54, + 0x9E, 0x44, 0xF5, 0x8E, 0xD2, 0x40, 0x17, 0x10 + }; + const unsigned char pk[32] = { + 0x25, 0xD1, 0xDF, 0xF9, 0x51, 0x05, 0xF5, 0x25, + 0x3C, 0x40, 0x22, 0xF6, 0x28, 0xA9, 0x96, 0xAD, + 0x3A, 0x0D, 0x95, 0xFB, 0xF2, 0x1D, 0x46, 0x8A, + 0x1B, 0x33, 0xF8, 0xC1, 0x60, 0xD8, 0xF5, 0x17 + }; + const unsigned char aux_rand[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char msg[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char sig[64] = { + 0x7E, 0xB0, 0x50, 0x97, 0x57, 0xE2, 0x46, 0xF1, + 0x94, 0x49, 0x88, 0x56, 0x51, 0x61, 0x1C, 0xB9, + 0x65, 0xEC, 0xC1, 0xA1, 0x87, 0xDD, 0x51, 0xB6, + 0x4F, 0xDA, 0x1E, 0xDC, 0x96, 0x37, 0xD5, 0xEC, + 0x97, 0x58, 0x2B, 0x9C, 0xB1, 0x3D, 0xB3, 0x93, + 0x37, 0x05, 0xB3, 0x2B, 0xA9, 0x82, 0xAF, 0x5A, + 0xF2, 0x5F, 0xD7, 0x88, 0x81, 0xEB, 0xB3, 0x27, + 0x71, 0xFC, 0x59, 0x22, 0xEF, 0xC6, 0x6E, 0xA3 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 4 */ + const unsigned char pk[32] = { + 0xD6, 0x9C, 0x35, 0x09, 0xBB, 0x99, 0xE4, 0x12, + 0xE6, 0x8B, 0x0F, 0xE8, 0x54, 0x4E, 0x72, 0x83, + 0x7D, 0xFA, 0x30, 0x74, 0x6D, 0x8B, 0xE2, 0xAA, + 0x65, 0x97, 0x5F, 0x29, 0xD2, 0x2D, 0xC7, 0xB9 + }; + const unsigned char msg[32] = { + 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2, + 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24, + 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B, + 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03 + }; + const unsigned char sig[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F, + 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28, + 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63, + 0x76, 0xAF, 0xB1, 0x54, 0x8A, 0xF6, 0x03, 0xB3, + 0xEB, 0x45, 0xC9, 0xF8, 0x20, 0x7D, 0xEE, 0x10, + 0x60, 0xCB, 0x71, 0xC0, 0x4E, 0x80, 0xF5, 0x93, + 0x06, 0x0B, 0x07, 0xD2, 0x83, 0x08, 0xD7, 0xF4 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 5 */ + const unsigned char pk[32] = { + 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, 0x50, + 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, 0x21, + 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, 0x87, + 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, 0x34 + }; + secp256k1_xonly_pubkey pk_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_xonly_pubkey_parse(CTX, &pk_parsed, pk)); + } + { + /* Test vector 6 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0xFF, 0xF9, 0x7B, 0xD5, 0x75, 0x5E, 0xEE, 0xA4, + 0x20, 0x45, 0x3A, 0x14, 0x35, 0x52, 0x35, 0xD3, + 0x82, 0xF6, 0x47, 0x2F, 0x85, 0x68, 0xA1, 0x8B, + 0x2F, 0x05, 0x7A, 0x14, 0x60, 0x29, 0x75, 0x56, + 0x3C, 0xC2, 0x79, 0x44, 0x64, 0x0A, 0xC6, 0x07, + 0xCD, 0x10, 0x7A, 0xE1, 0x09, 0x23, 0xD9, 0xEF, + 0x7A, 0x73, 0xC6, 0x43, 0xE1, 0x66, 0xBE, 0x5E, + 0xBE, 0xAF, 0xA3, 0x4B, 0x1A, 0xC5, 0x53, 0xE2 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + } + { + /* Test vector 7 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x1F, 0xA6, 0x2E, 0x33, 0x1E, 0xDB, 0xC2, 0x1C, + 0x39, 0x47, 0x92, 0xD2, 0xAB, 0x11, 0x00, 0xA7, + 0xB4, 0x32, 0xB0, 0x13, 0xDF, 0x3F, 0x6F, 0xF4, + 0xF9, 0x9F, 0xCB, 0x33, 0xE0, 0xE1, 0x51, 0x5F, + 0x28, 0x89, 0x0B, 0x3E, 0xDB, 0x6E, 0x71, 0x89, + 0xB6, 0x30, 0x44, 0x8B, 0x51, 0x5C, 0xE4, 0xF8, + 0x62, 0x2A, 0x95, 0x4C, 0xFE, 0x54, 0x57, 0x35, + 0xAA, 0xEA, 0x51, 0x34, 0xFC, 0xCD, 0xB2, 0xBD + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + } + { + /* Test vector 8 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA, + 0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F, + 0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96, + 0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69, + 0x96, 0x17, 0x64, 0xB3, 0xAA, 0x9B, 0x2F, 0xFC, + 0xB6, 0xEF, 0x94, 0x7B, 0x68, 0x87, 0xA2, 0x26, + 0xE8, 0xD7, 0xC9, 0x3E, 0x00, 0xC5, 0xED, 0x0C, + 0x18, 0x34, 0xFF, 0x0D, 0x0C, 0x2E, 0x6D, 0xA6 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + } + { + /* Test vector 9 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x3D, 0xDA, 0x83, 0x28, 0xAF, 0x9C, 0x23, + 0xA9, 0x4C, 0x1F, 0xEE, 0xCF, 0xD1, 0x23, 0xBA, + 0x4F, 0xB7, 0x34, 0x76, 0xF0, 0xD5, 0x94, 0xDC, + 0xB6, 0x5C, 0x64, 0x25, 0xBD, 0x18, 0x60, 0x51 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + } + { + /* Test vector 10 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x76, 0x15, 0xFB, 0xAF, 0x5A, 0xE2, 0x88, 0x64, + 0x01, 0x3C, 0x09, 0x97, 0x42, 0xDE, 0xAD, 0xB4, + 0xDB, 0xA8, 0x7F, 0x11, 0xAC, 0x67, 0x54, 0xF9, + 0x37, 0x80, 0xD5, 0xA1, 0x83, 0x7C, 0xF1, 0x97 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + } + { + /* Test vector 11 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03, + 0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7, + 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F, + 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + } + { + /* Test vector 12 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, + 0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03, + 0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7, + 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F, + 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + } + { + /* Test vector 13 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA, + 0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F, + 0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96, + 0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, + 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 0); + } + { + /* Test vector 14 */ + const unsigned char pk[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 + }; + secp256k1_xonly_pubkey pk_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_xonly_pubkey_parse(CTX, &pk_parsed, pk)); + } + { + /* Test vector 15 */ + const unsigned char sk[32] = { + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + }; + const unsigned char pk[32] = { + 0x77, 0x8C, 0xAA, 0x53, 0xB4, 0x39, 0x3A, 0xC4, + 0x67, 0x77, 0x4D, 0x09, 0x49, 0x7A, 0x87, 0x22, + 0x4B, 0xF9, 0xFA, 0xB6, 0xF6, 0xE6, 0x8B, 0x23, + 0x08, 0x64, 0x97, 0x32, 0x4D, 0x6F, 0xD1, 0x17, + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + /* const unsigned char msg[0] = {}; */ + const unsigned char sig[64] = { + 0x71, 0x53, 0x5D, 0xB1, 0x65, 0xEC, 0xD9, 0xFB, + 0xBC, 0x04, 0x6E, 0x5F, 0xFA, 0xEA, 0x61, 0x18, + 0x6B, 0xB6, 0xAD, 0x43, 0x67, 0x32, 0xFC, 0xCC, + 0x25, 0x29, 0x1A, 0x55, 0x89, 0x54, 0x64, 0xCF, + 0x60, 0x69, 0xCE, 0x26, 0xBF, 0x03, 0x46, 0x62, + 0x28, 0xF1, 0x9A, 0x3A, 0x62, 0xDB, 0x8A, 0x64, + 0x9F, 0x2D, 0x56, 0x0F, 0xAC, 0x65, 0x28, 0x27, + 0xD1, 0xAF, 0x05, 0x74, 0xE4, 0x27, 0xAB, 0x63, + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, NULL, 0, sig); + test_schnorrsig_bip_vectors_check_verify(pk, NULL, 0, sig, 1); + } + { + /* Test vector 16 */ + const unsigned char sk[32] = { + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + }; + const unsigned char pk[32] = { + 0x77, 0x8C, 0xAA, 0x53, 0xB4, 0x39, 0x3A, 0xC4, + 0x67, 0x77, 0x4D, 0x09, 0x49, 0x7A, 0x87, 0x22, + 0x4B, 0xF9, 0xFA, 0xB6, 0xF6, 0xE6, 0x8B, 0x23, + 0x08, 0x64, 0x97, 0x32, 0x4D, 0x6F, 0xD1, 0x17, + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + const unsigned char msg[] = { 0x11 }; + const unsigned char sig[64] = { + 0x08, 0xA2, 0x0A, 0x0A, 0xFE, 0xF6, 0x41, 0x24, + 0x64, 0x92, 0x32, 0xE0, 0x69, 0x3C, 0x58, 0x3A, + 0xB1, 0xB9, 0x93, 0x4A, 0xE6, 0x3B, 0x4C, 0x35, + 0x11, 0xF3, 0xAE, 0x11, 0x34, 0xC6, 0xA3, 0x03, + 0xEA, 0x31, 0x73, 0xBF, 0xEA, 0x66, 0x83, 0xBD, + 0x10, 0x1F, 0xA5, 0xAA, 0x5D, 0xBC, 0x19, 0x96, + 0xFE, 0x7C, 0xAC, 0xFC, 0x5A, 0x57, 0x7D, 0x33, + 0xEC, 0x14, 0x56, 0x4C, 0xEC, 0x2B, 0xAC, 0xBF, + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 17 */ + const unsigned char sk[32] = { + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + }; + const unsigned char pk[32] = { + 0x77, 0x8C, 0xAA, 0x53, 0xB4, 0x39, 0x3A, 0xC4, + 0x67, 0x77, 0x4D, 0x09, 0x49, 0x7A, 0x87, 0x22, + 0x4B, 0xF9, 0xFA, 0xB6, 0xF6, 0xE6, 0x8B, 0x23, + 0x08, 0x64, 0x97, 0x32, 0x4D, 0x6F, 0xD1, 0x17, + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + const unsigned char msg[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, + }; + const unsigned char sig[64] = { + 0x51, 0x30, 0xF3, 0x9A, 0x40, 0x59, 0xB4, 0x3B, + 0xC7, 0xCA, 0xC0, 0x9A, 0x19, 0xEC, 0xE5, 0x2B, + 0x5D, 0x86, 0x99, 0xD1, 0xA7, 0x1E, 0x3C, 0x52, + 0xDA, 0x9A, 0xFD, 0xB6, 0xB5, 0x0A, 0xC3, 0x70, + 0xC4, 0xA4, 0x82, 0xB7, 0x7B, 0xF9, 0x60, 0xF8, + 0x68, 0x15, 0x40, 0xE2, 0x5B, 0x67, 0x71, 0xEC, + 0xE1, 0xE5, 0xA3, 0x7F, 0xD8, 0x0E, 0x5A, 0x51, + 0x89, 0x7C, 0x55, 0x66, 0xA9, 0x7E, 0xA5, 0xA5, + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } + { + /* Test vector 18 */ + const unsigned char sk[32] = { + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, + }; + const unsigned char pk[32] = { + 0x77, 0x8C, 0xAA, 0x53, 0xB4, 0x39, 0x3A, 0xC4, + 0x67, 0x77, 0x4D, 0x09, 0x49, 0x7A, 0x87, 0x22, + 0x4B, 0xF9, 0xFA, 0xB6, 0xF6, 0xE6, 0x8B, 0x23, + 0x08, 0x64, 0x97, 0x32, 0x4D, 0x6F, 0xD1, 0x17, + }; + const unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + const unsigned char sig[64] = { + 0x40, 0x3B, 0x12, 0xB0, 0xD8, 0x55, 0x5A, 0x34, + 0x41, 0x75, 0xEA, 0x7E, 0xC7, 0x46, 0x56, 0x63, + 0x03, 0x32, 0x1E, 0x5D, 0xBF, 0xA8, 0xBE, 0x6F, + 0x09, 0x16, 0x35, 0x16, 0x3E, 0xCA, 0x79, 0xA8, + 0x58, 0x5E, 0xD3, 0xE3, 0x17, 0x08, 0x07, 0xE7, + 0xC0, 0x3B, 0x72, 0x0F, 0xC5, 0x4C, 0x7B, 0x23, + 0x89, 0x7F, 0xCB, 0xA0, 0xE9, 0xD0, 0xB4, 0xA0, + 0x68, 0x94, 0xCF, 0xD2, 0x49, 0xF2, 0x23, 0x67, + }; + unsigned char msg[100]; + memset(msg, 0x99, sizeof(msg)); + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sizeof(msg), sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sizeof(msg), sig, 1); + } +} + +/* Nonce function that returns constant 0 */ +static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { + (void) msg; + (void) msglen; + (void) key32; + (void) xonly_pk32; + (void) algo; + (void) algolen; + (void) data; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { + (void) msg; + (void) msglen; + (void) key32; + (void) xonly_pk32; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0, 32); + return 1; +} + +/* Nonce function that sets nonce to 0xFF...0xFF */ +static int nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { + (void) msg; + (void) msglen; + (void) key32; + (void) xonly_pk32; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0xFF, 32); + return 1; +} + +static void test_schnorrsig_sign(void) { + unsigned char sk[32]; + secp256k1_xonly_pubkey pk; + secp256k1_keypair keypair; + const unsigned char msg[32] = "this is a msg for a schnorrsig.."; + unsigned char sig[64]; + unsigned char sig2[64]; + unsigned char zeros64[64] = { 0 }; + secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + unsigned char aux_rand[32]; + + testrand256(sk); + testrand256(aux_rand); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &pk)); + /* Check that deprecated alias gives the same result */ + CHECK(secp256k1_schnorrsig_sign(CTX, sig2, msg, &keypair, NULL) == 1); + CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0); + + /* Test different nonce functions */ + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &pk)); + memset(sig, 1, sizeof(sig)); + extraparams.noncefp = nonce_function_failing; + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 0); + CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0); + memset(&sig, 1, sizeof(sig)); + extraparams.noncefp = nonce_function_0; + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 0); + CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0); + memset(&sig, 1, sizeof(sig)); + extraparams.noncefp = nonce_function_overflowing; + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &pk)); + + /* When using the default nonce function, schnorrsig_sign_custom produces + * the same result as schnorrsig_sign with aux_rand = extraparams.ndata */ + extraparams.noncefp = NULL; + extraparams.ndata = aux_rand; + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig2, msg, &keypair, extraparams.ndata) == 1); + CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0); +} + +#define N_SIGS 3 +/* Creates N_SIGS valid signatures and verifies them with verify and + * verify_batch (TODO). Then flips some bits and checks that verification now + * fails. */ +static void test_schnorrsig_sign_verify(void) { + unsigned char sk[32]; + unsigned char msg[N_SIGS][32]; + unsigned char sig[N_SIGS][64]; + size_t i; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey pk; + secp256k1_scalar s; + + testrand256(sk); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair)); + + for (i = 0; i < N_SIGS; i++) { + testrand256(msg[i]); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[i], msg[i], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[i], msg[i], sizeof(msg[i]), &pk)); + } + + { + /* Flip a few bits in the signature and in the message and check that + * verify and verify_batch (TODO) fail */ + size_t sig_idx = testrand_int(N_SIGS); + size_t byte_idx = testrand_bits(5); + unsigned char xorbyte = testrand_int(254)+1; + sig[sig_idx][byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); + sig[sig_idx][byte_idx] ^= xorbyte; + + byte_idx = testrand_bits(5); + sig[sig_idx][32+byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); + sig[sig_idx][32+byte_idx] ^= xorbyte; + + byte_idx = testrand_bits(5); + msg[sig_idx][byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); + msg[sig_idx][byte_idx] ^= xorbyte; + + /* Check that above bitflips have been reversed correctly */ + CHECK(secp256k1_schnorrsig_verify(CTX, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk)); + } + + /* Test overflowing s */ + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[0], msg[0], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], msg[0], sizeof(msg[0]), &pk)); + memset(&sig[0][32], 0xFF, 32); + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[0], msg[0], sizeof(msg[0]), &pk)); + + /* Test negative s */ + CHECK(secp256k1_schnorrsig_sign32(CTX, sig[0], msg[0], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], msg[0], sizeof(msg[0]), &pk)); + secp256k1_scalar_set_b32(&s, &sig[0][32], NULL); + secp256k1_scalar_negate(&s, &s); + secp256k1_scalar_get_b32(&sig[0][32], &s); + CHECK(!secp256k1_schnorrsig_verify(CTX, sig[0], msg[0], sizeof(msg[0]), &pk)); + + /* The empty message can be signed & verified */ + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig[0], NULL, 0, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], NULL, 0, &pk) == 1); + + { + /* Test varying message lengths */ + unsigned char msg_large[32 * 8]; + uint32_t msglen = testrand_int(sizeof(msg_large)); + for (i = 0; i < sizeof(msg_large); i += 32) { + testrand256(&msg_large[i]); + } + CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig[0], msg_large, msglen, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], msg_large, msglen, &pk) == 1); + /* Verification for a random wrong message length fails */ + msglen = (msglen + (sizeof(msg_large) - 1)) % sizeof(msg_large); + CHECK(secp256k1_schnorrsig_verify(CTX, sig[0], msg_large, msglen, &pk) == 0); + } +} +#undef N_SIGS + +static void test_schnorrsig_taproot(void) { + unsigned char sk[32]; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey internal_pk; + unsigned char internal_pk_bytes[32]; + secp256k1_xonly_pubkey output_pk; + unsigned char output_pk_bytes[32]; + unsigned char tweak[32]; + int pk_parity; + unsigned char msg[32]; + unsigned char sig[64]; + + /* Create output key */ + testrand256(sk); + CHECK(secp256k1_keypair_create(CTX, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &internal_pk, NULL, &keypair) == 1); + /* In actual taproot the tweak would be hash of internal_pk */ + CHECK(secp256k1_xonly_pubkey_serialize(CTX, tweak, &internal_pk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(CTX, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_pub(CTX, &output_pk, &pk_parity, &keypair) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(CTX, output_pk_bytes, &output_pk) == 1); + + /* Key spend */ + testrand256(msg); + CHECK(secp256k1_schnorrsig_sign32(CTX, sig, msg, &keypair, NULL) == 1); + /* Verify key spend */ + CHECK(secp256k1_xonly_pubkey_parse(CTX, &output_pk, output_pk_bytes) == 1); + CHECK(secp256k1_schnorrsig_verify(CTX, sig, msg, sizeof(msg), &output_pk) == 1); + + /* Script spend */ + CHECK(secp256k1_xonly_pubkey_serialize(CTX, internal_pk_bytes, &internal_pk) == 1); + /* Verify script spend */ + CHECK(secp256k1_xonly_pubkey_parse(CTX, &internal_pk, internal_pk_bytes) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1); +} + +static void run_schnorrsig_tests(void) { + int i; + run_nonce_function_bip340_tests(); + + test_schnorrsig_api(); + test_schnorrsig_sha256_tagged(); + test_schnorrsig_bip_vectors(); + for (i = 0; i < COUNT; i++) { + test_schnorrsig_sign(); + test_schnorrsig_sign_verify(); + } + test_schnorrsig_taproot(); +} + +#endif diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h deleted file mode 100644 index eee697d57..000000000 --- a/src/secp256k1/src/num.h +++ /dev/null @@ -1,74 +0,0 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#ifndef SECP256K1_NUM_H -#define SECP256K1_NUM_H - -#ifndef USE_NUM_NONE - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#if defined(USE_NUM_GMP) -#include "num_gmp.h" -#else -#error "Please select num implementation" -#endif - -/** Copy a number. */ -static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a); - -/** Convert a number's absolute value to a binary big-endian string. - * There must be enough place. */ -static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a); - -/** Set a number to the value of a binary big-endian string. */ -static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen); - -/** Compute a modular inverse. The input must be less than the modulus. */ -static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m); - -/** Compute the jacobi symbol (a|b). b must be positive and odd. */ -static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b); - -/** Compare the absolute value of two numbers. */ -static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b); - -/** Test whether two number are equal (including sign). */ -static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b); - -/** Add two (signed) numbers. */ -static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); - -/** Subtract two (signed) numbers. */ -static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); - -/** Multiply two (signed) numbers. */ -static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); - -/** Replace a number by its remainder modulo m. M's sign is ignored. The result is a number between 0 and m-1, - even if r was negative. */ -static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m); - -/** Right-shift the passed number by bits bits. */ -static void secp256k1_num_shift(secp256k1_num *r, int bits); - -/** Check whether a number is zero. */ -static int secp256k1_num_is_zero(const secp256k1_num *a); - -/** Check whether a number is one. */ -static int secp256k1_num_is_one(const secp256k1_num *a); - -/** Check whether a number is strictly negative. */ -static int secp256k1_num_is_neg(const secp256k1_num *a); - -/** Change a number's sign. */ -static void secp256k1_num_negate(secp256k1_num *r); - -#endif - -#endif /* SECP256K1_NUM_H */ diff --git a/src/secp256k1/src/num_gmp.h b/src/secp256k1/src/num_gmp.h deleted file mode 100644 index 1885ab2e2..000000000 --- a/src/secp256k1/src/num_gmp.h +++ /dev/null @@ -1,20 +0,0 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#ifndef SECP256K1_NUM_REPR_H -#define SECP256K1_NUM_REPR_H - -#include - -#define NUM_LIMBS ((256+GMP_NUMB_BITS-1)/GMP_NUMB_BITS) - -typedef struct { - mp_limb_t data[2*NUM_LIMBS]; - int neg; - int limbs; -} secp256k1_num; - -#endif /* SECP256K1_NUM_REPR_H */ diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h deleted file mode 100644 index ed67fb4fa..000000000 --- a/src/secp256k1/src/num_gmp_impl.h +++ /dev/null @@ -1,288 +0,0 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#ifndef SECP256K1_NUM_REPR_IMPL_H -#define SECP256K1_NUM_REPR_IMPL_H - -#include -#include -#include - -#include "util.h" -#include "num.h" - -#ifdef VERIFY -static void secp256k1_num_sanity(const secp256k1_num *a) { - VERIFY_CHECK(a->limbs == 1 || (a->limbs > 1 && a->data[a->limbs-1] != 0)); -} -#else -#define secp256k1_num_sanity(a) do { } while(0) -#endif - -static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a) { - *r = *a; -} - -static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a) { - unsigned char tmp[65]; - int len = 0; - int shift = 0; - if (a->limbs>1 || a->data[0] != 0) { - len = mpn_get_str(tmp, 256, (mp_limb_t*)a->data, a->limbs); - } - while (shift < len && tmp[shift] == 0) shift++; - VERIFY_CHECK(len-shift <= (int)rlen); - memset(r, 0, rlen - len + shift); - if (len > shift) { - memcpy(r + rlen - len + shift, tmp + shift, len - shift); - } - memset(tmp, 0, sizeof(tmp)); -} - -static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen) { - int len; - VERIFY_CHECK(alen > 0); - VERIFY_CHECK(alen <= 64); - len = mpn_set_str(r->data, a, alen, 256); - if (len == 0) { - r->data[0] = 0; - len = 1; - } - VERIFY_CHECK(len <= NUM_LIMBS*2); - r->limbs = len; - r->neg = 0; - while (r->limbs > 1 && r->data[r->limbs-1]==0) { - r->limbs--; - } -} - -static void secp256k1_num_add_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { - mp_limb_t c = mpn_add(r->data, a->data, a->limbs, b->data, b->limbs); - r->limbs = a->limbs; - if (c != 0) { - VERIFY_CHECK(r->limbs < 2*NUM_LIMBS); - r->data[r->limbs++] = c; - } -} - -static void secp256k1_num_sub_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { - mp_limb_t c = mpn_sub(r->data, a->data, a->limbs, b->data, b->limbs); - (void)c; - VERIFY_CHECK(c == 0); - r->limbs = a->limbs; - while (r->limbs > 1 && r->data[r->limbs-1]==0) { - r->limbs--; - } -} - -static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m) { - secp256k1_num_sanity(r); - secp256k1_num_sanity(m); - - if (r->limbs >= m->limbs) { - mp_limb_t t[2*NUM_LIMBS]; - mpn_tdiv_qr(t, r->data, 0, r->data, r->limbs, m->data, m->limbs); - memset(t, 0, sizeof(t)); - r->limbs = m->limbs; - while (r->limbs > 1 && r->data[r->limbs-1]==0) { - r->limbs--; - } - } - - if (r->neg && (r->limbs > 1 || r->data[0] != 0)) { - secp256k1_num_sub_abs(r, m, r); - r->neg = 0; - } -} - -static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m) { - int i; - mp_limb_t g[NUM_LIMBS+1]; - mp_limb_t u[NUM_LIMBS+1]; - mp_limb_t v[NUM_LIMBS+1]; - mp_size_t sn; - mp_size_t gn; - secp256k1_num_sanity(a); - secp256k1_num_sanity(m); - - /** mpn_gcdext computes: (G,S) = gcdext(U,V), where - * * G = gcd(U,V) - * * G = U*S + V*T - * * U has equal or more limbs than V, and V has no padding - * If we set U to be (a padded version of) a, and V = m: - * G = a*S + m*T - * G = a*S mod m - * Assuming G=1: - * S = 1/a mod m - */ - VERIFY_CHECK(m->limbs <= NUM_LIMBS); - VERIFY_CHECK(m->data[m->limbs-1] != 0); - for (i = 0; i < m->limbs; i++) { - u[i] = (i < a->limbs) ? a->data[i] : 0; - v[i] = m->data[i]; - } - sn = NUM_LIMBS+1; - gn = mpn_gcdext(g, r->data, &sn, u, m->limbs, v, m->limbs); - (void)gn; - VERIFY_CHECK(gn == 1); - VERIFY_CHECK(g[0] == 1); - r->neg = a->neg ^ m->neg; - if (sn < 0) { - mpn_sub(r->data, m->data, m->limbs, r->data, -sn); - r->limbs = m->limbs; - while (r->limbs > 1 && r->data[r->limbs-1]==0) { - r->limbs--; - } - } else { - r->limbs = sn; - } - memset(g, 0, sizeof(g)); - memset(u, 0, sizeof(u)); - memset(v, 0, sizeof(v)); -} - -static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) { - int ret; - mpz_t ga, gb; - secp256k1_num_sanity(a); - secp256k1_num_sanity(b); - VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1)); - - mpz_inits(ga, gb, NULL); - - mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data); - mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data); - if (a->neg) { - mpz_neg(ga, ga); - } - - ret = mpz_jacobi(ga, gb); - - mpz_clears(ga, gb, NULL); - - return ret; -} - -static int secp256k1_num_is_one(const secp256k1_num *a) { - return (a->limbs == 1 && a->data[0] == 1); -} - -static int secp256k1_num_is_zero(const secp256k1_num *a) { - return (a->limbs == 1 && a->data[0] == 0); -} - -static int secp256k1_num_is_neg(const secp256k1_num *a) { - return (a->limbs > 1 || a->data[0] != 0) && a->neg; -} - -static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b) { - if (a->limbs > b->limbs) { - return 1; - } - if (a->limbs < b->limbs) { - return -1; - } - return mpn_cmp(a->data, b->data, a->limbs); -} - -static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b) { - if (a->limbs > b->limbs) { - return 0; - } - if (a->limbs < b->limbs) { - return 0; - } - if ((a->neg && !secp256k1_num_is_zero(a)) != (b->neg && !secp256k1_num_is_zero(b))) { - return 0; - } - return mpn_cmp(a->data, b->data, a->limbs) == 0; -} - -static void secp256k1_num_subadd(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b, int bneg) { - if (!(b->neg ^ bneg ^ a->neg)) { /* a and b have the same sign */ - r->neg = a->neg; - if (a->limbs >= b->limbs) { - secp256k1_num_add_abs(r, a, b); - } else { - secp256k1_num_add_abs(r, b, a); - } - } else { - if (secp256k1_num_cmp(a, b) > 0) { - r->neg = a->neg; - secp256k1_num_sub_abs(r, a, b); - } else { - r->neg = b->neg ^ bneg; - secp256k1_num_sub_abs(r, b, a); - } - } -} - -static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { - secp256k1_num_sanity(a); - secp256k1_num_sanity(b); - secp256k1_num_subadd(r, a, b, 0); -} - -static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { - secp256k1_num_sanity(a); - secp256k1_num_sanity(b); - secp256k1_num_subadd(r, a, b, 1); -} - -static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { - mp_limb_t tmp[2*NUM_LIMBS+1]; - secp256k1_num_sanity(a); - secp256k1_num_sanity(b); - - VERIFY_CHECK(a->limbs + b->limbs <= 2*NUM_LIMBS+1); - if ((a->limbs==1 && a->data[0]==0) || (b->limbs==1 && b->data[0]==0)) { - r->limbs = 1; - r->neg = 0; - r->data[0] = 0; - return; - } - if (a->limbs >= b->limbs) { - mpn_mul(tmp, a->data, a->limbs, b->data, b->limbs); - } else { - mpn_mul(tmp, b->data, b->limbs, a->data, a->limbs); - } - r->limbs = a->limbs + b->limbs; - if (r->limbs > 1 && tmp[r->limbs - 1]==0) { - r->limbs--; - } - VERIFY_CHECK(r->limbs <= 2*NUM_LIMBS); - mpn_copyi(r->data, tmp, r->limbs); - r->neg = a->neg ^ b->neg; - memset(tmp, 0, sizeof(tmp)); -} - -static void secp256k1_num_shift(secp256k1_num *r, int bits) { - if (bits % GMP_NUMB_BITS) { - /* Shift within limbs. */ - mpn_rshift(r->data, r->data, r->limbs, bits % GMP_NUMB_BITS); - } - if (bits >= GMP_NUMB_BITS) { - int i; - /* Shift full limbs. */ - for (i = 0; i < r->limbs; i++) { - int index = i + (bits / GMP_NUMB_BITS); - if (index < r->limbs && index < 2*NUM_LIMBS) { - r->data[i] = r->data[index]; - } else { - r->data[i] = 0; - } - } - } - while (r->limbs>1 && r->data[r->limbs-1]==0) { - r->limbs--; - } -} - -static void secp256k1_num_negate(secp256k1_num *r) { - r->neg ^= 1; -} - -#endif /* SECP256K1_NUM_REPR_IMPL_H */ diff --git a/src/secp256k1/src/num_impl.h b/src/secp256k1/src/num_impl.h deleted file mode 100644 index a254f91a3..000000000 --- a/src/secp256k1/src/num_impl.h +++ /dev/null @@ -1,24 +0,0 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#ifndef SECP256K1_NUM_IMPL_H -#define SECP256K1_NUM_IMPL_H - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif - -#include "num.h" - -#if defined(USE_NUM_GMP) -#include "num_gmp_impl.h" -#elif defined(USE_NUM_NONE) -/* Nothing. */ -#else -#error "Please select num implementation" -#endif - -#endif /* SECP256K1_NUM_IMPL_H */ diff --git a/src/secp256k1/src/precompute_ecmult.c b/src/secp256k1/src/precompute_ecmult.c new file mode 100644 index 000000000..5ef198a77 --- /dev/null +++ b/src/secp256k1/src/precompute_ecmult.c @@ -0,0 +1,90 @@ +/***************************************************************************************************** + * Copyright (c) 2013, 2014, 2017, 2021 Pieter Wuille, Andrew Poelstra, Jonas Nick, Russell O'Connor * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php. * + *****************************************************************************************************/ + +#include +#include + +#include "../include/secp256k1.h" + +#include "assumptions.h" +#include "util.h" + +#include "field_impl.h" +#include "group_impl.h" +#include "int128_impl.h" +#include "ecmult.h" +#include "ecmult_compute_table_impl.h" + +static void print_table(FILE *fp, const char *name, int window_g, const secp256k1_ge_storage* table) { + int j; + int i; + + fprintf(fp, "const secp256k1_ge_storage %s[ECMULT_TABLE_SIZE(WINDOW_G)] = {\n", name); + fprintf(fp, " S(%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32 + ",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32")\n", + SECP256K1_GE_STORAGE_CONST_GET(table[0])); + + j = 1; + for(i = 3; i <= window_g; ++i) { + fprintf(fp, "#if WINDOW_G > %d\n", i-1); + for(;j < ECMULT_TABLE_SIZE(i); ++j) { + fprintf(fp, ",S(%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32 + ",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32")\n", + SECP256K1_GE_STORAGE_CONST_GET(table[j])); + } + fprintf(fp, "#endif\n"); + } + fprintf(fp, "};\n"); +} + +static void print_two_tables(FILE *fp, int window_g) { + secp256k1_ge_storage* table = malloc(ECMULT_TABLE_SIZE(window_g) * sizeof(secp256k1_ge_storage)); + secp256k1_ge_storage* table_128 = malloc(ECMULT_TABLE_SIZE(window_g) * sizeof(secp256k1_ge_storage)); + + secp256k1_ecmult_compute_two_tables(table, table_128, window_g, &secp256k1_ge_const_g); + + print_table(fp, "secp256k1_pre_g", window_g, table); + print_table(fp, "secp256k1_pre_g_128", window_g, table_128); + + free(table); + free(table_128); +} + +int main(void) { + /* Always compute all tables for window sizes up to 15. */ + int window_g = (ECMULT_WINDOW_SIZE < 15) ? 15 : ECMULT_WINDOW_SIZE; + const char outfile[] = "src/precomputed_ecmult.c"; + FILE* fp; + + fp = fopen(outfile, "w"); + if (fp == NULL) { + fprintf(stderr, "Could not open %s for writing!\n", outfile); + return -1; + } + + fprintf(fp, "/* This file was automatically generated by precompute_ecmult. */\n"); + fprintf(fp, "/* This file contains an array secp256k1_pre_g with odd multiples of the base point G and\n"); + fprintf(fp, " * an array secp256k1_pre_g_128 with odd multiples of 2^128*G for accelerating the computation of a*P + b*G.\n"); + fprintf(fp, " */\n"); + fprintf(fp, "#include \"group.h\"\n"); + fprintf(fp, "#include \"ecmult.h\"\n"); + fprintf(fp, "#include \"precomputed_ecmult.h\"\n"); + fprintf(fp, "#define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u)\n"); + fprintf(fp, "#if ECMULT_WINDOW_SIZE > %d\n", window_g); + fprintf(fp, " #error configuration mismatch, invalid ECMULT_WINDOW_SIZE. Try deleting precomputed_ecmult.c before the build.\n"); + fprintf(fp, "#endif\n"); + fprintf(fp, "#ifdef EXHAUSTIVE_TEST_ORDER\n"); + fprintf(fp, "# error Cannot compile precomputed_ecmult.c in exhaustive test mode\n"); + fprintf(fp, "#endif /* EXHAUSTIVE_TEST_ORDER */\n"); + fprintf(fp, "#define WINDOW_G ECMULT_WINDOW_SIZE\n"); + + print_two_tables(fp, window_g); + + fprintf(fp, "#undef S\n"); + fclose(fp); + + return 0; +} diff --git a/src/secp256k1/src/precompute_ecmult_gen.c b/src/secp256k1/src/precompute_ecmult_gen.c new file mode 100644 index 000000000..4d153a657 --- /dev/null +++ b/src/secp256k1/src/precompute_ecmult_gen.c @@ -0,0 +1,100 @@ +/********************************************************************************* + * Copyright (c) 2013, 2014, 2015, 2021 Thomas Daede, Cory Fields, Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php. * + *********************************************************************************/ + +#include +#include + +#include "../include/secp256k1.h" + +#include "assumptions.h" +#include "util.h" + +#include "group.h" +#include "int128_impl.h" +#include "ecmult_gen.h" +#include "ecmult_gen_compute_table_impl.h" + +static const int CONFIGS[][2] = { + {2, 5}, + {11, 6}, + {43, 6} +}; + +static void print_table(FILE* fp, int blocks, int teeth) { + int spacing = CEIL_DIV(256, blocks * teeth); + size_t points = ((size_t)1) << (teeth - 1); + int outer; + size_t inner; + + secp256k1_ge_storage* table = checked_malloc(&default_error_callback, blocks * points * sizeof(secp256k1_ge_storage)); + secp256k1_ecmult_gen_compute_table(table, &secp256k1_ge_const_g, blocks, teeth, spacing); + + fprintf(fp, "#elif (COMB_BLOCKS == %d) && (COMB_TEETH == %d) && (COMB_SPACING == %d)\n", blocks, teeth, spacing); + for (outer = 0; outer != blocks; outer++) { + fprintf(fp,"{"); + for (inner = 0; inner != points; inner++) { + fprintf(fp, "S(%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32 + ",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32")", + SECP256K1_GE_STORAGE_CONST_GET(table[outer * points + inner])); + if (inner != points - 1) { + fprintf(fp,",\n"); + } + } + if (outer != blocks - 1) { + fprintf(fp,"},\n"); + } else { + fprintf(fp,"}\n"); + } + } + free(table); +} + +int main(int argc, char **argv) { + const char outfile[] = "src/precomputed_ecmult_gen.c"; + FILE* fp; + size_t config; + int did_current_config = 0; + + (void)argc; + (void)argv; + + fp = fopen(outfile, "w"); + if (fp == NULL) { + fprintf(stderr, "Could not open %s for writing!\n", outfile); + return -1; + } + + fprintf(fp, "/* This file was automatically generated by precompute_ecmult_gen. */\n"); + fprintf(fp, "/* See ecmult_gen_impl.h for details about the contents of this file. */\n"); + fprintf(fp, "#include \"group.h\"\n"); + fprintf(fp, "#include \"ecmult_gen.h\"\n"); + fprintf(fp, "#include \"precomputed_ecmult_gen.h\"\n"); + fprintf(fp, "#ifdef EXHAUSTIVE_TEST_ORDER\n"); + fprintf(fp, "# error Cannot compile precomputed_ecmult_gen.c in exhaustive test mode\n"); + fprintf(fp, "#endif /* EXHAUSTIVE_TEST_ORDER */\n"); + fprintf(fp, "#define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u)\n"); + + fprintf(fp, "const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS] = {\n"); + fprintf(fp, "#if 0\n"); + for (config = 0; config < sizeof(CONFIGS) / sizeof(*CONFIGS); ++config) { + print_table(fp, CONFIGS[config][0], CONFIGS[config][1]); + if (CONFIGS[config][0] == COMB_BLOCKS && CONFIGS[config][1] == COMB_TEETH) { + did_current_config = 1; + } + } + if (!did_current_config) { + print_table(fp, COMB_BLOCKS, COMB_TEETH); + } + fprintf(fp, "#else\n"); + fprintf(fp, "# error Configuration mismatch, invalid COMB_* parameters. Try deleting precomputed_ecmult_gen.c before the build.\n"); + fprintf(fp, "#endif\n"); + + fprintf(fp, "};\n"); + fprintf(fp, "#undef S\n"); + fclose(fp); + + return 0; +} diff --git a/src/secp256k1/src/precomputed_ecmult.c b/src/secp256k1/src/precomputed_ecmult.c new file mode 100644 index 000000000..cbd030ce5 --- /dev/null +++ b/src/secp256k1/src/precomputed_ecmult.c @@ -0,0 +1,16456 @@ +/* This file was automatically generated by precompute_ecmult. */ +/* This file contains an array secp256k1_pre_g with odd multiples of the base point G and + * an array secp256k1_pre_g_128 with odd multiples of 2^128*G for accelerating the computation of a*P + b*G. + */ +#include "group.h" +#include "ecmult.h" +#include "precomputed_ecmult.h" +#define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u) +#if ECMULT_WINDOW_SIZE > 15 + #error configuration mismatch, invalid ECMULT_WINDOW_SIZE. Try deleting precomputed_ecmult.c before the build. +#endif +#ifdef EXHAUSTIVE_TEST_ORDER +# error Cannot compile precomputed_ecmult.c in exhaustive test mode +#endif /* EXHAUSTIVE_TEST_ORDER */ +#define WINDOW_G ECMULT_WINDOW_SIZE +const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = { + S(79be667e,f9dcbbac,55a06295,ce870b07,29bfcdb,2dce28d9,59f2815b,16f81798,483ada77,26a3c465,5da4fbfc,e1108a8,fd17b448,a6855419,9c47d08f,fb10d4b8) +#if WINDOW_G > 2 +,S(f9308a01,9258c310,49344f85,f89d5229,b531c845,836f99b0,8601f113,bce036f9,388f7b0f,632de814,fe337e6,2a37f356,6500a999,34c2231b,6cb9fd75,84b8e672) +#endif +#if WINDOW_G > 3 +,S(2f8bde4d,1a072093,55b4a725,a5c5128,e88b84bd,dc619ab7,cba8d569,b240efe4,d8ac2226,36e5e3d6,d4dba9dd,a6c9c426,f788271b,ab0d6840,dca87d3a,a6ac62d6) +,S(5cbdf064,6e5db4ea,a398f365,f2ea7a0e,3d419b7e,330e39c,e92bdded,cac4f9bc,6aebca40,ba255960,a3178d6d,861a54db,a813d0b8,13fde7b5,a5082628,87264da) +#endif +#if WINDOW_G > 4 +,S(acd484e2,f0c7f653,9ad178a,9f559abd,e0979697,4c57e714,c35f110d,fc27ccbe,cc338921,b0a7d9fd,64380971,763b61e9,add888a4,375f8e0f,5cc262a,c64f9c37) +,S(774ae7f8,58a9411e,5ef4246b,70c65aac,5649980b,e5c17891,bbec1789,5da008cb,d984a032,eb6b5e19,243dd56,d7b7b365,372db1e2,dff9d6a8,301d74c9,c953c61b) +,S(f28773c2,d975288b,c7d1d205,c3748651,b075fbc6,610e58cd,deeddf8f,19405aa8,ab0902e,8d880a89,758212eb,65cdaf47,3a1a06da,521fa91f,29b5cb52,db03ed81) +,S(d7924d4f,7d43ea96,5a465ae3,95ff411,31e5946f,3c85f79e,44adbcf8,e27e080e,581e2872,a86c72a6,83842ec2,28cc6def,ea40af2b,d896d3a5,c504dc9f,f6a26b58) +#endif +#if WINDOW_G > 5 +,S(defdea4c,db677750,a420fee8,7eacf21,eb9898ae,79b97687,66e4faa0,4a2d4a34,4211ab06,94635168,e997b0ea,d2a93dae,ced1f4a0,4a95c0f6,cfb199f6,9e56eb77) +,S(2b4ea0a7,97a443d2,93ef5cff,444f4979,f06acfeb,d7e86d27,74756561,38385b6c,85e89bc0,37945d93,b343083b,5a1c8613,1a01f60c,50269763,b570c854,e5c09b7a) +,S(352bbf4a,4cdd1256,4f93fa33,2ce33330,1d9ad402,71f81071,81340aef,25be59d5,321eb407,5348f534,d59c1825,9dda3e1f,4a1b3b2e,71b1039c,67bd3d8b,cf81998c) +,S(2fa2104d,6b38d11b,2300105,59879124,e42ab8df,eff5ff29,dc9cdadd,4ecacc3f,2de1068,295dd865,b6456933,5bd5dd80,181d70ec,fc882648,423ba76b,532b7d67) +,S(9248279b,9b4d68d,ab21a9b0,66edda83,263c3d84,e09572e2,69ca0cd7,f5453714,73016f7b,f234aade,5d1aa71b,dea2b1ff,3fc0de2a,887912ff,e54a32ce,97cb3402) +,S(daed4f2b,e3a8bf27,8e70132f,b0beb752,2f570e14,4bf615c0,7e996d44,3dee8729,a69dce4a,7d6c98e8,d4a1aca8,7ef8d700,3f83c230,f3afa726,ab40e522,90be1c55) +,S(c44d12c7,65d812e,8acf28d7,cbb19f90,11ecd9e9,fdf281b0,e6a3b5e8,7d22e7db,2119a460,ce326cdc,76c45926,c982fdac,e106e86,1edf61c5,a039063f,e0e6482) +,S(6a245bf6,dc698504,c89a20cf,ded60853,152b6953,36c28063,b61c65cb,d269e6b4,e022cf42,c2bd4a70,8b3f5126,f16a24ad,8b33ba48,d0423b6e,fd5e6348,100d8a82) +#endif +#if WINDOW_G > 6 +,S(1697ffa6,fd9de627,c077e3d2,fe541084,ce13300b,bec1146,f95ae57f,d0bd6a5,b9c398f1,86806f5d,27561506,e4557433,a2cf1500,9e498ae7,adee9d63,d01b2396) +,S(605bdb01,9981718b,986d0f07,e834cb0d,9deb8360,ffb7f61d,f982345e,f27a7479,2972d2d,e4f8d206,81a78d93,ec96fe23,c26bfae8,4fb14db4,3b01e1e9,56b8c49) +,S(62d14dab,4150bf49,7402fdc4,5a215e10,dcb01c35,4959b10c,fe31c7e9,d87ff33d,80fc06bd,8cc5b010,98088a19,50eed0db,1aa1329,67ab4722,35f56424,83b25eaf) +,S(80c60ad0,40f27da,de5b4b06,c408e56b,2c50e9f5,6b9b8b42,5e555c2f,86308b6f,1c38303f,1cc5c30f,26e66bad,7fe72f70,a65eed4c,be7024eb,1aa01f56,430bd57a) +,S(7a9375ad,6167ad54,aa74c634,8cc54d34,4cc5dc94,87d84704,9d5eabb0,fa03c8fb,d0e3fa9,eca87269,9559e0d,79269046,bdc59ea1,c70ce2b,2d499ec,224dc7f7) +,S(d528ecd9,b696b54c,907a9ed0,45447a79,bb408ec3,9b68df50,4bb51f45,9bc3ffc9,eecf4125,3136e5f9,9966f218,81fd656e,bc434540,5c520dbc,63465b5,21409933) +,S(49370a4,b5f43412,ea25f514,e8ecdad0,5266115e,4a7ecb13,87231808,f8b45963,758f3f41,afd6ed42,8b3081b0,512fd62a,54c3f3af,bb5b6764,b653052a,12949c9a) +,S(77f23093,6ee88cbb,d73df930,d64702ef,881d811e,e1498e2,f1c13eb1,fc345d74,958ef42a,7886b640,a08266e,9ba1b378,96c95330,d97077cb,be8eb3c7,671c60d6) +,S(f2dac991,cc4ce4b9,ea44887e,5c7c0bce,58c80074,ab9d4dba,eb28531b,7739f530,e0dedc9b,3b2f8dad,4da1f32d,ec2531df,9eb5fbeb,598e4fd,1a117dba,703a3c37) +,S(463b3d9f,662621fb,1b4be8fb,be252012,5a216cdf,c9dae3de,bcba4850,c690d45b,5ed430d7,8c296c35,43114306,dd8622d7,c622e27c,970a1de3,1cb377b0,1af7307e) +,S(f16f8042,44e46e2a,9232d4a,ff3b5997,6b98fac1,4328a2d1,a32496b4,9998f247,cedabd9b,82203f7e,13d206fc,df4e33d9,2a6c53c2,6e5cce26,d6579962,c4e31df6) +,S(caf75427,2dc84563,b0352b7a,14311af5,5d245315,ace27c65,369e15f7,151d41d1,cb474660,ef35f5f2,a41b643f,a5e46057,5f4fa9b7,962232a5,c32f9083,18a04476) +,S(2600ca4b,282cb986,f85d0f17,9979d8b,44a09c07,cb86d7c1,24497bc8,6f082120,4119b887,53c15bd6,a693b03f,cddbb45d,5ac6be74,ab5f0ef4,4b0be947,5a7e4b40) +,S(7635ca72,d7e8432c,338ec53c,d12220bc,1c48685,e24f7dc8,c602a774,6998e435,91b6496,9489d61,3d1d5e59,f78e6d7,4ecfc061,d57048ba,d9e76f30,2c5b9c61) +,S(754e3239,f325570c,dbbf4a87,deee8a66,b7f2b334,79d468fb,c1a50743,bf56cc18,673fb86,e5bda30f,b3cd0ed3,4ea49a0,23ee33d0,197a695d,c5d9809,3c536683) +,S(e3e6bd10,71a1e96a,ff57859c,82d570f0,33080066,1d1c952f,9fe26946,91d9b9e8,59c9e0bb,a394e76f,40c0aa58,379a3cb6,a5a22839,93e90c41,67002af4,920e37f5) +#endif +#if WINDOW_G > 7 +,S(186b483d,56a0338,26ae73d8,8f732985,c4ccb1f3,2ba35f4b,4cc47fdc,f04aa6eb,3b952d32,c67cf77e,2e17446e,204180ab,21fb8090,895138b4,a4a797f8,6e80888b) +,S(df9d70a6,b9876ce5,44c98561,f4be4f72,5442e6d2,b737d9c9,1a832172,4ce0963f,55eb2daf,d84d6ccd,5f862b78,5dc39d4a,b1572227,20ef9da2,17b8c45c,f2ba2417) +,S(5edd5cc2,3c51e87a,497ca815,d5dce0f8,ab52554f,849ed899,5de64c5f,34ce7143,efae9c8d,bc141306,61e8cec0,30c89ad0,c13c66c0,d17a2905,cdc706ab,7399a868) +,S(290798c2,b6476830,da12fe02,287e9e77,7aa3fba1,c355b17a,722d362f,84614fba,e38da76d,cd440621,988d00bc,f79af25d,5b29c094,db2a2314,6d003afd,41943e7a) +,S(af3c423a,95d9f5b3,54754ef,a150ac39,cd29552f,e3602573,62dfdece,f4053b45,f98a3fd8,31eb2b74,9a93b0e6,f35cfb40,c8cd5aa6,67a15581,bc2feded,498fd9c6) +,S(766dbb24,d134e745,cccaa28c,99bf2749,6bb66b2,6dcf98df,8d2fed50,d884249a,744b1152,eacbe5e3,8dcc8879,80da38b8,97584a65,fa06cedd,2c924f97,cbac5996) +,S(59dbf46f,8c94759b,a21277c3,3784f416,45f7b44f,6c596a58,ce92e666,191abe3e,c534ad44,175fbc30,f4ea6ce,648309a0,42ce739a,7919798c,d85e216c,4a307f6e) +,S(f13ada95,103c4537,305e691e,74e9a4a8,dd647e71,1a95e73c,b62dc601,8cfd87b8,e13817b4,4ee14de6,63bf4bc8,8341f32,6949e21a,6a75c257,778419b,daf5733d) +,S(7754b4fa,e8aced0,6d4167a2,c59cca4c,da1869c0,6ebadfb6,48855001,5a88522c,30e93e86,4e669d82,224b967c,3020b8fa,8d1e4e35,b6cbcc5,37a48b57,841163a2) +,S(948dcadf,5990e048,aa3874d4,6abef9d7,1858f95,de8041d2,a6828c99,e2262519,e491a425,37f6e597,d5d28a32,24b1bc25,df9154ef,bd2ef1d2,cbba2cae,5347d57e) +,S(79624144,50c76c16,89c7b48f,8202ec37,fb224cf5,ac0bfa15,70328a8a,3d7c77ab,100b610e,c4ffb476,d5c1fc1,33ef6f6b,12507a05,1f04ac57,60afa5b2,9db83437) +,S(35140878,34964b54,b15b1606,44d91548,5a169772,25b8847b,b0dd0851,37ec47ca,ef0afbb2,5620544,8e1652c4,8e8127fc,6039e77c,15c2378b,7e7d15a0,de293311) +,S(d3cc30ad,6b483e4b,c79ce2c9,dd8bc549,93e947eb,8df787b4,42943d3f,7b527eaf,8b378a22,d827278d,89c5e9be,8f9508ae,3c2ad462,90358630,afb34db0,4eede0a4) +,S(1624d847,80732860,ce1c78fc,bfefe08b,2b29823d,b913f649,3975ba0f,f4847610,68651cf9,b6da903e,914448c,6cd9d4ca,896878f5,282be4c8,cc06e2a4,4078575) +,S(733ce80d,a955a8a2,6902c956,33e62a98,5192474b,5af207da,6df7b4fd,5fc61cd4,f5435a2b,d2badf7d,485a4d8b,8db9fcce,3e1ef8e0,201e4578,c54673bc,1dc5ea1d) +,S(15d94412,54945064,cf1a1c33,bbd3b49f,8966c509,2171e699,ef258dfa,b81c045c,d56eb30b,69463e72,34f5137b,73b84177,434800ba,cebfc685,fc37bbe9,efe4070d) +,S(a1d0fcf2,ec9de675,b612136e,5ce70d27,1c21417c,9d2b8aaa,ac138599,d0717940,edd77f50,bcb5a3ca,b2e90737,309667f2,641462a5,4070f3d5,19212d39,c197a629) +,S(e22fbe15,c0af8ccc,5780c073,5f84dbe9,a790bade,e8245c06,c7ca3733,1cb36980,a855bab,ad5cd60c,88b430a6,9f53a1a7,a3828915,4964799b,e43d06d7,7d31da06) +,S(311091dd,9860e8e2,ee13473,c1155f5f,69635e39,4704eaa7,40094522,46cfa9b3,66db656f,87d1f04f,ffd1f047,88c06830,871ec5a6,4feee685,bd80f0b1,286d8374) +,S(34c1fd04,d301be89,b31c0442,d3e6ac24,883928b4,5a934078,1867d423,2ec2dbdf,9414685,e97b1b59,54bd46f7,30174136,d57f1cee,b487443d,c5321857,ba73abee) +,S(f219ea5d,6b54701c,1c14de5b,557eb42a,8d13f3ab,bcd08aff,cc2a5e6b,49b8d63,4cb95957,e83d40b0,f73af454,4cccf6b1,f4b08d3c,7b27fb8,d8c2962a,400766d1) +,S(d7b8740f,74a8fbaa,b1f683db,8f45de26,543a5490,bca62708,72369124,69a0b448,fa779681,28d9c92e,e1010f33,7ad4717e,ff15db5e,d3c049b3,411e0315,eaa4593b) +,S(32d31c22,2f8f6f0e,f86f7c98,d3a3335e,ad5bcd32,abdd9428,9fe4d309,1aa824bf,5f3032f5,892156e3,9ccd3d79,15b9e1da,2e6dac9e,6f26e961,118d14b8,462e1661) +,S(7461f371,914ab326,71045a15,5d9831ea,8793d77c,d59592c4,340f86cb,c18347b5,8ec0ba23,8b96bec0,cbdddcae,aa44254,2eee1ff5,c986ea6,b39847b3,cc092ff6) +,S(ee079adb,1df18600,74356a25,aa38206a,6d716b2c,3e67453d,287698ba,d7b2b2d6,8dc2412a,afe3be5c,4c5f37e0,ecc5f9f6,a446989a,f04c4e25,ebaac479,ec1c8c1e) +,S(16ec93e4,47ec83f0,467b1830,2ee620f7,e65de331,874c9dc7,2bfd8616,ba9da6b5,5e463115,e62fb40,d0e8c2a7,ca5804a3,9d58186a,50e49713,9626778e,25b0674d) +,S(eaa5f980,c245f6f0,38978290,afa70b6b,d8855897,f98b6aa4,85b96065,d537bd99,f65f5d3e,292c2e08,19a52839,1c994624,d784869d,7e6ea67f,b1804102,4edc07dc) +,S(78c9407,544ac132,692ee191,a024399,58ae0487,7151342e,a96c4b6b,35a49f51,f3e03191,69eb9b85,d5404795,539a5e68,fa1fbd58,3c064d24,62b675f1,94a3ddb4) +,S(494f4be2,19a1a770,16dcd838,431aea00,1cdc8ae,7a6fc688,726578d9,702857a5,42242a96,9283a5f3,39ba7f07,5e36ba2a,f925ce30,d767ed6e,55f4b031,880d562c) +,S(a598a803,da6d86c,6bc7f2f5,144ea549,d28211ea,58faa70e,bf4c1e66,5c1fe9b5,204b5d6f,84822c30,7e4b4a71,40737aec,23fc63b6,5b35f86a,10026dbd,2d864e6b) +,S(c4191636,5abb2b5d,9192f5f,2dbeafec,208f020f,12570a18,4dbadc3e,58595997,4f14351,d0087efa,49d245b3,28984989,d5caf945,f34bfc0,ed16e96b,58fa9913) +,S(841d6063,a586fa47,5a724604,da03bc5b,92a2e0d2,e0a36acf,e4c73a55,14742881,73867f5,9c0659e8,1904f9a1,c7543698,e62562d6,744c169c,e7a36de0,1a8d6154) +#endif +#if WINDOW_G > 8 +,S(5e95bb39,9a6971d3,76026947,f89bde2f,282b3381,928be4d,ed112ac4,d70e20d5,39f23f36,6809085b,eebfc711,81313775,a99c9aed,7d8ba38b,161384c7,46012865) +,S(36e4641a,53948fd4,76c39f8a,99fd974e,5ec07564,b5315d8b,f99471bc,a0ef2f66,d2424b1b,1abe4eb8,164227b0,85c9aa94,56ea1349,3fd563e0,6fd51cf5,694c78fc) +,S(336581e,a7bfbbb2,90c191a2,f507a41c,f5643842,170e914f,aeab27c2,c579f726,ead12168,595fe1be,99252129,b6e56b33,91f7ab14,10cd1e0e,f3dcdcab,d2fda224) +,S(8ab89816,dadfd6b6,a1f2634f,cf00ec84,3781025,ed6890c4,84974270,6bd43ede,6fdcef09,f2f6d0a0,44e654ae,f624136f,503d459c,3e898458,58a47a91,29cdd24e) +,S(1e33f1a7,46c9c577,8133344d,9299fcaa,20b0938e,8acff254,4bb40284,b8c5fb94,6066025,7dd11b3a,a9c8ed61,8d24edff,2306d320,f1d03010,e33a7d20,57f3b3b6) +,S(85b7c1dc,b3cec1b7,ee7f30de,d79dd20a,ed1f4cc,18cbcfcf,a410361f,d8f08f31,3d98a9cd,d026dd43,f39048f2,5a8847f4,fcafad18,95d7a633,c6fed3c3,5e999511) +,S(29df9fbd,8d9e4650,9275f4b1,25d6d45d,7fbe9a3b,878a7af8,72a28006,61ac5f51,b4c4fe9,9c775a60,6e2d8862,179139ff,da61dc86,1c019e55,cd2876eb,2a27d84b) +,S(a0b1cae0,6b0a847a,3fea6e67,1aaf8adf,dfe58ca2,f768105c,8082b2e4,49fce252,ae434102,edde0958,ec4b19d9,17a6a28e,6b72da18,34aff0e6,50f04950,3a296cf2) +,S(4e8ceaf,b9b3e9a1,36dc7ff6,7e840295,b499dfb3,b2133e4b,a113f2e4,c0e121e5,cf217411,8c8b6d7a,4b48f6d5,34ce5c79,422c086a,63460502,b827ce62,a326683c) +,S(d24a44e0,47e19b6f,5afb81c7,ca2f6908,a507668,9a010919,f42725c2,b789a33b,6fb8d559,1b466f8f,c63db50f,1c0f1c69,13f9968,87b8244d,2cdec417,afea8fa3) +,S(ea01606a,7a6c9cdd,249fdfcf,acb99584,1edd28,abbab77b,5104e98e,8e3b35d4,322af490,8c7312b0,cfbfe369,f7a7b3cd,b7d4494b,c2823700,cfd65218,8a3ea98d) +,S(af8addbf,2b661c8a,6c632865,5eb96651,252007d8,c5ea31be,4ad196de,8ce2131f,6749e67c,29b85f5,2a034eaf,d096836b,25208186,80e26ac8,f3dfbcdb,71749700) +,S(e3ae19,74566ca0,6cc516d4,7e0fb165,a674a3da,bcfca15e,722f0e34,50f45889,2aeabe7e,45315101,16217f07,bf4d0730,de97e48,74f81f53,3420a72e,eb0bd6a4) +,S(591ee355,313d9972,1cf6993f,fed1e3e3,1993ff3,ed258802,75ea8ce,d397e246,b0ea558a,113c30be,a60fc477,5460c790,1ff0b053,d25ca2bd,eee98f1a,4be5d196) +,S(11396d55,fda54c49,f19aa973,18d8da61,fa8584e4,7b084945,77cf032,55b52984,998c74a8,cd45ac01,289d5833,a7beb474,4ff536b0,1b257be4,c5767bea,93ea57a4) +,S(3c5d2a1b,a39c5a17,90000738,c9e0c40b,8dcdfd54,68754b64,5540157,e017aa7a,b2284279,995a34e2,f9d4de73,96fc18b8,f9b8b9f,dd270f66,61f79ca4,c81bd257) +,S(cc8704b8,a60a0def,a3a99a72,99f2e9c3,fbc395af,b04ac078,425ef8a1,793cc030,bdd46039,feed1788,1d1e0862,db347f8c,f395b74f,c4bcdc4e,940b74e3,ac1f1b13) +,S(c533e4f7,ea8555aa,cd9777ac,5cad29b9,7dd4defc,cc53ee7e,a204119b,2889b197,6f0a256b,c5efdf42,9a2fb624,2f1a43a2,d9b925bb,4a4b3a26,bb8e0f45,eb596096) +,S(c14f8f2,ccb27d6f,109f6d08,d03cc96a,69ba8c34,eec07bbc,f566d48e,33da6593,c359d692,3bb398f7,fd4473e1,6fe1c284,75b740dd,98075e6,c0e86491,13dc3a38) +,S(a6cbc304,6bc6a450,bac24789,fa17115a,4c9739ed,75f8f21c,e441f72e,b90e6ef,21ae7f4,680e889b,b130619e,2c0f95a3,60ceb573,c7060313,9862afd6,17fa9b9f) +,S(347d6d9a,2c48927,ebfb86c1,359b1caf,130a3c02,67d11ce6,344b39f9,9d43cc38,60ea7f61,a353524d,1c987f6e,cec92f08,6d565ab6,87870cb1,2689ff1e,31c74448) +,S(da6545d2,181db8d9,83f7dcb3,75ef5866,d47c67b1,bf31c8cf,855ef743,7b72656a,49b96715,ab6878a7,9e78f07c,e5680c5d,6673051b,4935bd89,7fea824b,77dc208a) +,S(c40747cc,9d012cb1,a13b8148,309c6de7,ec25d694,5d657146,b9d5994b,8feb1111,5ca56075,3be2a12f,c6de6caf,2cb48956,5db93615,6b9514e1,bb5e8303,7e0fa2d4) +,S(4e42c8ec,82c99798,ccf3a610,be870e78,338c7f71,3348bd34,c8203ef4,37f3502,7571d74e,e5e0fb92,a7a8b33a,7783341,a5492144,cc54bcc4,a944736,93606437) +,S(3775ab70,89bc6af8,23aba2e1,af70b236,d251cadb,c867432,87522a1b,3b0dedea,be52d107,bcfa09d8,bcb9736a,828cfa7f,ac8db17b,f7a76a2c,42ad9614,9018cf7) +,S(cee31cbf,7e34ec37,9d94fb81,4d3d775a,d954595d,1314ba88,46959e3e,82f74e26,8fd64a14,c06b589c,26b947ae,2bcf6bfa,149ef0b,e14ed4d8,f448a01,c43b1c6d) +,S(b4f9eaea,9b69176,19f6ea6a,4eb5464e,fddb58fd,45b1ebef,cdc1a01d,8b47986,39e5c992,5b5a54b0,7433a4f1,8c61726f,8bb131c0,12ca542e,b24a8ac0,7200682a) +,S(d4263dfc,3d2df923,a0179a48,966d30ce,84e2515a,fc3dccc1,b7790779,2ebcc60e,62dfaf07,a0f78feb,30e30d62,95853ce1,89e12776,ad6cf7f,ae164e12,2a208d54) +,S(48457524,820fa65a,4f8d35eb,6930857c,32acc0,a4a2de42,2233eeda,897612c4,25a748ab,367979d9,8733c38a,1fa1c2e7,dc6cc07d,b2d60a9a,e7a76aaa,49bd0f77) +,S(dfeeef18,81101f2c,b11644f3,a2afdfc2,45e1991,9152923f,367a1767,c11cceda,ecfb7056,cf1de042,f9420bab,396793c0,c390bde7,4b4bbdff,16a83ae0,9a9a7517) +,S(6d7ef6b1,7543f837,3c573f44,e1f38983,5d89bcbc,6062ced3,6c82df83,b8fae859,cd450ec3,35438986,dfefa10c,57fea9bc,c521a095,9b2d80bb,f74b190d,ca712d10) +,S(e75605d5,9102a5a2,684500d3,b991f2e3,f3c88b93,22554703,5af25af6,6e04541f,f5c54754,a8f71ee5,40b9b487,28473e31,4f729ac5,308b0693,8360990e,2bfad125) +,S(eb98660f,4c4dfaa0,6a2be453,d5020bc9,9a0c2e60,abe38845,7dd43fef,b1ed620c,6cb9a887,6d9cb852,609af3a,dd26cd20,a0a7cd8a,9411131c,e85f4410,99223e) +,S(13e87b02,7d8514d3,5939f2e6,892b1992,21545969,41888336,dc3563e3,b8dba942,fef5a3c6,8059a6de,c5d62411,4bf1e91a,ac2b9da5,68d6abeb,2570d556,46b8adf1) +,S(ee163026,e9fd6fe0,17c38f06,a5be6fc1,25424b37,1ce2708e,7bf44916,91e5764a,1acb250f,255dd61c,43d94ccc,670d0f58,f49ae3fa,15b96623,e5430da0,ad6c62b2) +,S(b268f5ef,9ad51e4d,78de3a75,c2dc89b,1e626d43,50586799,9932e5db,33af3d80,5f310d4b,3c99b9eb,b19f77d4,1c1dee01,8cf0d34f,d4191614,3e945a,1216e423) +,S(ff07f311,8a9df035,e9fad85e,b6c7bfe4,2b02f01c,a99ceea3,bf7ffdba,93c4750d,438136d6,3e858a3,a5c440c3,8eccbadd,c1d29421,14e2eddd,4740d098,ced1f0d8) +,S(8d8b9855,c7c052a3,4146fd20,ffb658be,a4b9f69e,d825ebe,c16e8c3c,e2b526a1,cdb559ee,dc2d79f9,26baf44f,b84ea4d4,4bcf50fe,e51d7ceb,30e2e7f4,63036758) +,S(52db0b53,84dfbf05,bfa9d472,d7ae26df,e4b851ce,ca91b1eb,a5426318,da32b63,c3b997d,50ee5d4,23ebaf66,a6db9f57,b3180c90,2875679d,e924b69d,84a7b375) +,S(e62f9490,d3d51da6,395efd24,e80919cc,7d0f29c3,f3fa48c6,fff543be,cbd43352,6d89ad7b,a4876b0b,22c2ca28,c682862,f342c859,1f1daf51,70e07bfd,9ccafa7d) +,S(7f30ea24,76b399b4,957509c8,8f77d019,1afa2ff5,cb7b14fd,6d8e7d65,aaab1193,ca5ef7d4,b231c94c,3b15389a,5f6311e9,daff7bb6,7b103e98,80ef4bff,637acaec) +,S(5098ff1e,1d9f14fb,46a210fa,da6c903f,ef0fb7b4,a1dd1d9a,c60a0361,800b7a00,9731141,d81fc8f8,84d37c6,e7542006,b3ee1b40,d60dfe53,62a5b132,fd17ddc0) +,S(32b78c7d,e9ee512a,72895be6,b9cbefa6,e2f3c4cc,ce445c96,b9f2c81e,2778ad58,ee1849f5,13df71e3,2efc3896,ee28260c,73bb8054,7ae2275b,a4972377,94c8753c) +,S(e2cb74fd,dc8e9fbc,d076eef2,a7c72b0c,e37d50f0,8269dfc0,74b58155,547a4f7,d3aa2ed7,1c9dd224,7a62df06,2736eb0b,addea9e3,6122d2be,8641abcb,5cc4a4) +,S(84384475,66d4d7be,dadc2994,96ab3574,26009a35,f235cb14,1be0d99c,d10ae3a8,c4e10209,16980a4d,a5d01ac5,e6ad3307,34ef0d79,6631c4f,2390426b,2edd791f) +,S(4162d488,b8940203,9b584c6f,c6c30887,587d9c4,6f660b87,8ab65c82,c711d67e,67163e90,3236289f,776f22c2,5fb8a3af,c1732f2b,84b4e95d,bda47ae5,a0852649) +,S(3fad3fa8,4caf0f34,f0f89bfd,2dcf54fc,175d767a,ec3e5068,4f3ba4a4,bf5f683d,cd1bc7c,b6cc407b,b2f0ca64,7c718a73,cf71872,e7d0d2a5,3fa20efc,dfe61826) +,S(674f2600,a3007a00,568c1a7c,e05d0816,c1fb84bf,1370798f,1c69532f,aeb1a86b,299d21f9,413f33b3,edf43b25,7004580b,70db57da,b182259,e09eecc6,9e0d38a5) +,S(d32f4da5,4ade74ab,b81b815a,d1fb3b26,3d82d6c6,92714bcf,f87d29bd,5ee9f08f,f9429e73,8b8e53b9,68e99016,c0597077,82e14f45,35359d58,2fc41691,b3eea87) +,S(30e4e670,43538555,6e593657,135845d3,6fbb6931,f72b08cb,1ed954f1,e3ce3ff6,462f9bce,61989863,84993501,13bbc9b1,a878d35,da70740d,c695a559,eb88db7b) +,S(be206200,3c51cc30,4682904,330e4dee,7f3dcd10,b01e580b,f1971b04,d4cad297,62188bc4,9d61e542,8573d48a,74e1c655,b1c61090,905682a0,d5558ed7,2dccb9bc) +,S(93144423,ace3451e,d29e0fb9,ac2af211,cb6e84a6,1df5993,c419859f,ff5df04a,7c10dfb1,64c3425f,5c71a3f9,d7992038,f1065224,f72bb9d1,d902a6d1,3037b47c) +,S(b015f804,4f5fcbdc,f21ca26d,6c34fb81,97829205,c7b7d2a7,cb66418c,157b112c,ab8c1e08,6d04e813,744a655b,2df8d5f8,3b3cdc6f,aa3088c1,d3aea145,4e3a1d5f) +,S(d5e9e1da,649d97d8,9e486811,7a465a3a,4f8a18de,57a140d3,6b3f2af3,41a21b52,4cb04437,f391ed73,111a13cc,1d4dd0db,1693465c,2240480d,8955e859,2f27447a) +,S(d3ae4104,7dd7ca06,5dbf8ed7,7b992439,983005cd,72e16d6f,996a5316,d36966bb,bd1aeb21,ad22ebb2,2a10f030,3417c6d9,64f8cdd7,df0aca61,4b10dc14,d125ac46) +,S(463e2763,d885f958,fc66cdd2,2800f0a4,87197d0a,82e377b4,9f80af87,c897b065,bfefacdb,e5d0fd7,df3a311a,94de062b,26b80c61,fbc97508,b7999267,1ef7ca7f) +,S(7985fdfd,127c0567,c6f53ec1,bb63ec31,58e597c4,bfe747c,83cddfc9,10641917,603c12da,f3d9862e,f2b25fe1,de289aed,24ed291e,ec67087,3a5bd56,7f32ed03) +,S(74a1ad6b,5f76e39d,b2dd2494,10eac7f9,9e74c59c,b83d2d0e,d5ff1543,da7703e9,cc6157ef,18c9c63c,d6193d83,631bbea0,93e0968,942e8c33,d5737fd7,90e0db08) +,S(30682a50,703375f6,2d41666,4ba19b7f,c9bab42c,72747463,a71d0896,b22f6da3,553e04f6,b018b4fa,6c8f39e7,f311d317,6290d0e0,f19ca73f,17714d99,77a22ff8) +,S(9e2158f0,d7c0d5f2,6c3791ef,efa79597,654e7a2b,2464f52b,1ee6c134,7769ef57,712fcdd,1b9053f0,9003a348,1fa7762e,9ffd7c8e,f35a3850,9e2fbf26,29008373) +,S(176e2698,9a43c9cf,eba4029c,202538c2,8172e566,e3c4fce7,322857f3,be327d66,ed8cc9d0,4b29eb87,7d270b48,78dc43c1,9aefd31f,4eee09ee,7b47834c,1fa4b1c3) +,S(75d46efe,a3771e6e,68abb89a,13ad747e,cf189239,3dfc4f1b,7004788c,50374da8,9852390a,99507679,fd0b86fd,2b39a868,d7efc221,51346e1a,3ca47265,86a6bed8) +,S(809a20c6,7d64900f,fb698c4c,825f6d5f,2310fb04,51c86934,5b7319f6,45605721,9e994980,d9917e22,b76b0619,27fa0414,3d096ccc,54963e6a,5ebfa5f3,f8e286c1) +,S(1b38903a,43f7f114,ed4500b4,eac7083f,defece1c,f29c6352,8d563446,f972c180,4036edc9,31a60ae8,89353f77,fd53de4a,2708b26b,6f5da72a,d3394119,daf408f9) +#endif +#if WINDOW_G > 9 +,S(90a80db6,eb294b9e,ab0b4e8d,dfa3efe7,263458ce,2d07566d,f4e6c588,68feef23,753c8b9f,9754f18d,87f21145,d9e2936b,5ee050b2,7bbd9681,442c76e9,2fcf91e6) +,S(c2c80f84,4b705998,12d62546,f60340e,3e6f3605,4a14546e,6dc25d47,376bea9b,86ca160d,68f4d4e7,18b495b8,91d3b1b5,73b871a7,2b4cf61,23abd448,3aa79c64) +,S(9cf60674,4cf4b5f3,fdf989d3,f19fb265,2d00cfe1,d5fcd692,a323ce11,a28e7553,8147cbf7,b973fcc1,5b57b6a3,cfad6863,edd0f30e,3c45b85d,c300c513,c247759d) +,S(57488fa2,8742c6b2,5a493fd6,60d936e,a6280b0c,742005ab,ce98f585,5ad82208,31b3ca45,5073bea5,58adbe56,c27b470b,af949ae6,50213921,dc287844,f1a29574) +,S(f1133cbe,6be8bbc8,dc8df2b8,d75963c2,d40ed616,c758cdc8,4edbc5eb,4899447d,57fc2447,2225b23f,5714626d,8d67d561,10bd3a60,dd7a1687,cbbb893,f652f50f) +,S(95083e75,3301bd78,7f8989c7,9065bb81,3f3d69bf,f3e42505,f4e0417,5bbe89c0,844adb5c,e7d10de9,4617c73c,a77040e4,ee4e92e0,156b3c70,cc593fa4,94b33482) +,S(1a908355,cbb75675,5e576ed2,9c99af63,8668c7b3,63c8d973,62100443,bc5c75c6,d765466c,6e556e35,2f778722,25627d80,a7353807,4b44ff27,57ad22e,2f2454a2) +,S(c5922f74,bd343d5,aa867308,fad97f9f,8a2d1f63,c5f31db4,f04df3be,f349b648,77b1f068,7cfcdbe8,812605e5,d8b752c,da811844,236a4c43,77f53c94,6e7bd648) +,S(64e1b196,9f910297,7691a404,31b0b672,55dcf31,163897d9,96434420,e6c95dc9,c16f60c7,c11fc3c9,eb27fa26,a9035b66,9bfb77d2,1cef371d,dce94e32,9222550c) +,S(33b2e76,687744ed,6c521bad,3333dd37,c602f8a7,549e9ce7,808fb7ea,7ce08de,e1bcfe7f,c8ed8ae9,5cf6c243,7fdd94bf,d742e8ca,a6de7811,4c25112a,86988efd) +,S(20f18f4c,866d8a1c,c2a31033,17b4ac31,89fbf30f,f294a75c,951473be,45e4f294,8d6857c9,d08ef7b4,fd888336,3d37bee7,fe8529f,7173f589,43fcae81,d2d0ea0e) +,S(4d1623c9,44c9c716,a0eb4c68,5e2a8b9d,2df34653,54643bef,d1444176,d7b69a8b,ddf1b9fe,8744ad03,f996bf6b,96ec3496,2b601bd5,ed952f78,54f58388,8917be80) +,S(a901b0db,e8ab292d,280d6b36,85894785,4faad0a4,dd0da7e2,d4ad0ff5,3db079e0,3f27e7e1,834f1a61,af6f04dc,61e7ae64,716bc5e0,a6b063b3,1d0e60e,47298a9d) +,S(7e0af071,30218ffd,50bd66f4,484645b1,2f42a24f,7c80889b,3031c9a6,ebfc9a70,50bc23f3,926cd0c4,9f53fbb2,35eb1e89,d579517,f5bdc3ab,2416db78,5aaedb3f) +,S(7ba8187e,1a7b25a2,c185d335,440a9038,b47f0528,546e9da4,ef82aab0,5aebf20d,6e6aee6c,9625370a,f866c25c,7ca5dd78,527efbc,e7d8b3a3,9ab24930,9a185187) +,S(8c050fc3,4d83b279,b6000816,e18fca38,9767b796,e926772,55b84a39,d93a6807,986314ef,75b68fb2,827c2965,4198139,5d699fcd,81cf23ce,7019bc41,35174870) +,S(53b7849a,78e4df86,25860583,a5249948,9d7201a2,cbf50620,2a7b8b1b,c99c2ec9,4e31ea12,ac607d07,5de4b22d,e1be2c52,e0a44d25,4728d2c5,44d2ddf9,e3e469c0) +,S(9bdf9e67,a5d0c995,6a075a01,fe762be,b6335004,31dee78e,febc527e,53313b33,94264621,a5960e0e,e24c2792,6f16cad2,907f2636,762e8d5a,17e94afd,8e9d2bb0) +,S(7caa72b3,7a8ab3bd,bac031a,47606f89,17d9f42c,6ec2d2fb,429fd990,4a381f34,5b5853ab,7ee5de8d,34e3d6be,b201094f,ff8fbd1e,682f7f1,ef87ddd6,5d7303c9) +,S(2ef29b9f,9827975,79c0295f,c3f48db7,925d62c7,5532493d,de16b97e,3993d81a,496c944d,d9875ba6,a537ef9,6bf4c714,a0afff24,387d95e8,9b42337a,33110753) +,S(df157cad,95b07875,573c1860,ae5d02c6,4029e952,ec354e6a,9e5c34be,97317ff8,f2eccac7,75922b50,899c979a,2b3cc30,b629e62e,85693ba4,70f6ee38,1284c162) +,S(dd55c150,a29ca526,b6182e64,3b9eb544,e651d236,b71920e7,b15a9870,16454b1d,44c757a5,42f4ea2e,b39605d4,268c2510,ac685aab,d77a8f5c,4d95e23f,4c2e9368) +,S(16886cf4,6ed42c79,19147763,63d3256,c4d5d393,87f01723,25b9e4b8,98227f27,7421a220,7ee73299,d46192fc,93ca03de,c824ed8d,e2f48367,ec538317,a17fffb) +,S(6ff180fc,daa30618,8e8b306,d6f0acff,27968c22,484ff45e,56aeaa7b,2b60732f,7d16d654,f0c2aff0,fc254dad,63761a2,6c8d4022,ea85b8cc,22f3ea1e,f69961a9) +,S(3ea4511,a00dc2a0,3eb4f51f,40ee677c,aa912b55,39f685c4,f8bcc8ea,dc395e36,6c9ed1f1,528b0215,93a39839,340ddb53,a2f2e36,5290c498,24b035c6,73c9259d) +,S(b82cd70,dc3de9ea,b38742d8,f32dfb8d,53e4150a,835e54b6,3c7cca20,f253081d,e8bcbfe,1f7f6e75,d32e2049,9329765f,2effc56,a922f268,60d4bc0a,add0e24d) +,S(fe2fc3e0,748745,84ee23bf,105a69a6,6d056f0,17327d49,b7b38b57,a196c77f,3e18941c,c3c6d297,cc9a32f6,95807b1c,7da8561d,e4fde71d,4f9bbdb6,e9bf3916) +,S(4b90176,cdaa3693,47e8778b,12db9d6e,e8b00114,46ea35ec,845dbf57,4bb7858b,f60547ab,6e9c5fd3,eca6e349,b85880c6,1fdad0fc,2f7ab155,295caaec,b973c154) +,S(35f38251,1d34600b,4b8c86a9,f0dbc9ed,defc4272,f59528a0,cd3ec10a,5944c6d2,29a835f6,ef7fa1e5,f6f37a80,cf96ca98,43762bb1,b12a0dae,ae83234b,d0b5ccd5) +,S(1d74b297,311b7ff,a1027e26,587d3f5b,e1d0e9ac,3f0111cd,f3cc2371,722cb94a,5c7bcf8b,57f114e0,b73bcfb8,10f5c60d,35dc99ae,9dc7f0e2,606cc1f7,28c2071e) +,S(50a094f3,9c6f956,b020737,b9ec722e,4f75d1b7,c41593e6,f934a68a,98450428,a286e222,dfe10cfd,9689eaba,6a81f044,89c86db6,869aa1b5,54a90f1e,83778eee) +,S(9b65bb81,2129157c,dfecf12e,275ec38c,282dbcd9,14b48105,99b0a6d6,27c63db7,c582db1a,3f0f2242,1913b2e9,51e98a78,660b4c40,ad08fd65,528593bc,18223188) +,S(8b4544fc,1fdfa06e,456c1115,a1dc831c,85e7f1c5,e620eca5,1c20802d,36a4bc6b,e3e77c41,288f2602,e722af7f,4b70e64d,e4116fb9,955b03b0,6ea8b19f,7a20350d) +,S(6c709880,b959eb7c,5179b29c,c5578fdc,6cb2ae13,ddcede29,d5f81d95,de0ab4aa,c9e33fae,bd8eba42,6736c0c7,6f3deaba,ee2b59c5,953fb43,c2dcc513,9e7c4bdc) +,S(77760b51,37ba6a71,95d891f7,94a087a0,76fc9d67,802b81e7,85b5677,3d537806,f5202cf5,aaeea58b,f4f58c7e,df4417be,1b87ffde,e68e77f0,d7e81abe,158e3a25) +,S(1a8bd783,6a0b0c82,e9a904a8,a8c91a67,e23cd4f8,efd625d0,df4c426e,7e163102,61fe64ca,b0952cae,3c574f28,2f74a87d,c2a96316,b7009f2e,4e9c5fcc,12285844) +,S(fe217db6,59079913,fb1e453e,d24d91d6,a3fb3099,e69471d7,53db5390,864abc30,5dcf9abb,a9625ed6,80b0f20f,b1f047d5,93a0c61c,53969253,8cdf6b03,4d730b58) +,S(2504d637,54afd5eb,c38f58b6,5ead696d,7e3abd7,48cb6c5f,212aed49,f5b33b91,79a6bf43,75f1469c,4f5321c6,c72fbbf4,ba7cec10,5675f437,b5e013ad,7b5d75d4) +,S(b06f702,f47b22d7,89a9bd3f,687105c3,6160abbf,5cc8976b,7fbddcaf,db197b5c,7669bbd4,19a4d491,f592a35b,6aa3dfe4,5bd2fe7f,d179c778,1cd5f918,d732f63d) +,S(803b203b,b31f9cf9,4034eeb9,31b54480,a6f3f99e,bd23d0ac,bc2128a6,d044e23,308abc8d,f271f759,59b20c5c,7fa62baf,bfc9ccbf,49b946a9,54e5381c,1728d1c7) +,S(266a9cb4,c5f5cead,bb50e5bd,a03a7312,e52de1de,8e95a8dc,d57289fe,302749a,9eea970b,a856b2fa,a3e82877,cc84ed4f,3dc0efba,1e7c3baa,8b386ffc,46e0ae7e) +,S(fd8a9d95,d80c7ad5,2599a7ab,98163df3,64c4c141,e9abea35,5d7360bc,f84eba94,a9fb1702,100953b3,59b2e268,8ae7fd33,a30377da,47bfda71,3e2d7d73,dfb1030c) +,S(a7322df3,9f28f23,59fc339a,8b2c80be,6e84acc5,b7b0b8f8,f2cb6f26,f9db0a7d,22f6fe9d,21749501,7fdb7f5b,2f12fa57,95f40e1,31714885,c12a2ea1,6edb6be6) +,S(82a8c10f,336a6649,63a104dd,bf7f0f18,bd4c461a,ea569ffc,82c3c7e4,cb052d36,737ceca2,c0ef7227,8b90501c,cb71b671,5e5c31d4,cd0478c1,18fe1287,95f1dd0c) +,S(9b50d1b6,8e3bf795,7cd12f,5a60c26,6c4ef2b7,5ba5c516,c54784a9,4f15d6df,2afc8d09,b79176d8,d003fd2a,4f18d526,403fff27,2d47e778,7376feb7,cbddd8fd) +,S(3f9083dd,c8b423fe,7de3a822,81d3056a,b8dcb9d7,ee82cb80,6718595f,bae08d32,cb13c152,fd511d91,a9e0ed90,afa021a0,81f77f6d,20cc1376,e2195ffc,f28fa758) +,S(c75c85c1,ee17c1a2,56eff6bd,592666cb,c9231706,59d50bfa,dbd1074e,f2167faf,1ab4eabe,5e09409d,75cca892,2647f48d,bd698a16,d4f7cc85,96daf169,40023a52) +,S(c5341fea,f8a0f5d3,b4d0cf0d,2f7aad7c,60ea8e2b,3d4b7fb9,5c68d576,98656045,95f9f4e9,7e5b9f,a48fa422,a26ab982,dc48a4d5,4d712398,6e6d3ab9,74e88915) +,S(83acda3e,2a8997e0,d52bd4c6,8705dd22,220852b7,752d67fd,8967a032,60c2d89b,dce1bae1,d655ba51,7f5b5580,99711757,a77cd3b,dd4b8e8e,330e9779,1bc31df0) +,S(5b819146,8b299074,5b9c4164,e29d594c,f1c0d571,6c5d3962,5bd279b3,25237b,cc3636a0,3fddddfa,bed88daa,b081f359,1c48d2ca,71ba34fc,f6989f4a,f7625d8e) +,S(64778122,214e38ef,f8041796,166104e7,32f5f664,d38d7721,9b89045e,2c3b0e6c,329cf049,7e15eec7,b8eafc4a,b8a7d1c,d8b63203,8d4aef81,974cf984,4611a32d) +,S(ed4d826a,fe5762f4,79509909,9aee8664,2b475a9d,6da1017c,43d0cb9f,1af12323,8c6f81be,3fafa5ee,c8296f92,8ac7919d,c4d88c9a,59442274,d0531b7b,f7e48e78) +,S(38b42924,419aecc3,acd6f551,346fd61a,4d82ac2b,55f7afe9,7a06eb40,cd109c4a,7f42c096,2feb2f73,b2b0965a,1f359a6a,de49d768,a2ce6b07,b5acb92b,73e05583) +,S(c3cad4a8,d8bb94a7,b434cf70,183e8615,bb2a8f62,24f216e3,446ac2e9,82138911,f649be27,8cad9764,20742ce3,82dce3a1,420e372e,f1b25b27,59a8ed38,7282765e) +,S(2d408ff4,d3d236fd,54fae40d,ce3ea9ec,d9212e57,36591a9e,55588e4a,54bd6538,d596adf0,e8692a06,bc6284bf,299bef6,85e2a171,585aa132,4b9a05b5,ce815b7) +,S(ee7adf6d,247f25fb,76e90cf8,13f888eb,d67423a3,a3c6fdae,bafb7eaa,7a33c854,e077184b,4ae8f705,6c10dd9e,f541689d,143f6871,789e1801,deaefc1d,527a8fb4) +,S(2f9457c8,a9ffaca1,3d91151d,c4c5e89d,dd5d37a3,7c9a864b,7c811f3e,1144b34,eb4c9848,9093d573,a295407e,1d6fc48a,787120ce,b3d3dcfb,b40634e0,e75e221d) +,S(d3f332b8,a0f11582,1ce3478c,efe18de3,60120483,ef531c27,7b30c46e,b7fec294,ea75b9b2,5d717861,d1af1c01,9c372941,c8968b90,ef134f9f,323215e1,bb0b2155) +,S(183408d3,38b05aad,3521fcd8,6ef36dd7,5f3ddb86,66b52f7e,9a4cdf1f,8e152b91,66998520,6edf4ac6,f39be21f,20c98824,210e204c,e4499809,5de35537,1641218c) +,S(283fec5d,b1145e53,ba8f1f0f,f9cf89a7,21faffd6,c2534686,3d395609,5f40374e,70b01237,74af550e,68e68e5f,65ca6e98,8846e03,cf39af77,8511be82,bc32fefe) +,S(ce7570a,4f943cfa,413bd249,d8e7dbfc,ebc73579,770fd6da,f54a0dfb,dd52fa62,e115b14b,ef4695cf,fb85bdf9,8ba3985c,bd5e5b89,83e05390,7c36f9ce,8b75d41d) +,S(7e9c4f19,c8f4ec3f,1269f648,cd919525,df790315,74cbeb15,37794a4c,838fd470,e9d9dbfe,8cf5f5cc,a855d6cc,bd11f480,60fafa8d,ad6bd3e9,c86df5ce,b0fa5270) +,S(e2a9bbe6,d5d5bfe,a7c7f919,df2309f9,ba04f4c,722a3ec2,3bf451b4,64cb001b,e4177ce1,c3cd6ac7,78925bd6,7e72cb77,d1925b91,d06a7f16,98411a47,86393fb0) +,S(504512a4,3e17ef50,e43bf37d,42a94990,f55e641b,1558c265,e7099002,75271012,954a5fd8,57ba3acf,2d4b1f41,e8e1f2cd,1f21c4b9,6899781b,742a49d2,e61ed18b) +,S(81d1f013,a6bb325f,4b2d1d51,ba72c721,859945d8,a17b3411,cd5cbe87,285f850d,2d5d2fb1,f0c30855,3b1fe249,298b2059,259d3d49,d4d7071a,dce4bcc5,dc937193) +,S(5b66c2df,c1d28266,18a87276,7e66c33d,d90dd514,14a3b87c,a733383d,1d895022,9bd0178e,38189569,2217267b,7407e987,27fcaeda,12d8cf54,49eb5472,d554e0ff) +,S(aeb5f70e,98ec5e38,dbd2d544,bdbff8ab,99b583d9,af58c597,afaf8688,20381186,618bd6b0,d25ca70d,f08b7692,9336e421,691b0973,f2f5a05,2e7adc17,3584427b) +,S(b289eff,e841943b,84761e3c,67a9c02a,557679ca,76ad753a,707a9821,2505052e,7a981f0,c21862a8,53b4f895,dc62482c,530ed738,5e5d1e33,cfb9d0f,e879992c) +,S(abae3945,8b12199e,6b0c8360,cfd28288,3f585917,e44e1200,f81bd356,f619291c,adb23bcd,b3d069c5,e83be30b,2469b068,b2a81b7,b667e934,233b75ef,b5753f28) +,S(4a9583a6,485b5a5a,81ac224a,518eb29d,1e0f658c,8d91b013,9419c809,55fbacaa,d8003c9e,e3c842f5,ede375a8,a7768db4,803ecf11,9b7b37de,cea15631,b4e8dbca) +,S(d52f630e,dba6f7cb,65fcf465,44ab0d9e,ea236ac1,460f17ae,3a210102,10ebc169,21155748,9fa93b88,3e5bea50,da005c53,68e21a0c,41bc83d9,145c13e1,370d26d0) +,S(bdc5237,82c75858,f5c50fc0,52e4c1e9,c74a2a63,35bca9bf,8d10e120,9add6a4d,abb1d9f8,74637668,e214efba,fbf529d3,12ff023b,c1d5723e,58540436,6834f189) +,S(44770a33,8bf0aab8,3bb64e47,6eb6167a,88156d16,8f13ce86,26ee0912,e59ad087,5b5930f1,2e9c40bc,b3393a89,5c2d6457,6a3abd23,b7291b99,c965c33d,ef60a55c) +,S(b15e7b32,2e404aee,319ac203,23e36672,6503108d,8ee8e1c8,3e32d924,515e1679,5246a819,bbfb291,5f82ed56,50796f50,5ced5c25,87347d57,a873ceaf,3d997e7a) +,S(a1ed7557,5225cd0,f2c50f75,8a1c1df9,665ae108,d5e04190,27bbd9ae,ddb00f22,3c83145d,ad9f8748,7b97e746,4850ed02,d71dbd04,93281a1,3d212776,6a791ee2) +,S(e8aaf361,6a1bc60f,d9bfc43c,2c60580f,479e9ec9,c23a37a2,3cf8afb3,1d918af5,6ce693b6,4a37c672,7e141041,ab9a0d58,9ab9c303,a5ac3d3e,c89b6f27,9e79827c) +,S(5dc6f8cd,2c855e63,52a4a4ef,6187a6d6,759c043,38a3db76,c5a3aa37,54c20a3,f602c342,8593a9a8,d671d1bc,7c1d8834,fe9f5f5,2e6a7f0f,bb870146,4e6f4838) +,S(63327311,67bed8af,68a063ef,22aa489c,f6563620,461af26a,5f1a07cb,6b42f3a6,b8f7c3b2,20701320,f20ca036,761d3e56,bf94a700,9a919f1a,3ea0cb81,b74424a6) +,S(8a40d925,9a393b38,2305c201,7e8654db,ad66e50a,d798a0d3,535230f9,48080263,afb6a74d,9849454e,dd7f703a,5c6616d1,43f9cbcc,9a9a5d6f,6a7b5d1f,9d9fcaff) +,S(e7147107,27c7420a,f517fd3f,9a05b7de,a6a02c8b,cc20b17d,cdfdeaf8,2078645a,da7d67cb,c1ede9d4,fedd5dcd,c96b04f9,a3561ba0,2581b055,eaa144eb,4217daca) +,S(6131291c,d95fb878,1e42a68,553952c2,9922bce8,91c026c0,cae1f69c,9661c82d,160e1c1c,13342fd4,59d4f989,8ae632b8,42b89479,13733b89,384fc104,2d30bf01) +,S(4bc4f845,b6764692,d0a9bfa8,1788809e,fc5e2aa9,da5003bf,b782bcf1,d1ca4951,87092dcb,b9c3d254,e3b055ff,3a76ec05,64c4a7c5,7fb1783c,efdc40fc,10b751f0) +,S(45a880a2,7bbee9df,29f9bff5,c985f364,52865b5d,582a201f,698e6eca,a2be67df,fe49a6a8,b5e46bf1,ef679714,dabd590e,a831d46b,8ee94eb6,13132ba3,7855fabb) +,S(6a826a38,317c0c86,64d6847a,220145d1,877e5495,b21500d3,f21f1a0d,4af4f2a4,4521954e,fcc98263,df2f14e0,e6e6b47a,f6b83f0b,bc20722c,15445f87,e05f4513) +,S(15356506,f255f7e9,6cc8aa1b,9dce572,8bd860de,7c6cc75f,613e8a34,366a23a9,cd15abbc,d744d485,d5e401f,1f89a5df,122f37b4,e362b4ce,e3e53b1c,110bf3) +,S(f3bc12ae,f53d9f5f,6b865178,2dac2ec,cacff3a5,cca6443a,2b5e1ca0,f2b89b91,cfd4d36b,eb2e11f2,41fd0f36,7a0737ad,303e915f,f247f131,368ca509,18e00957) +,S(7e3c8c6d,fa04a536,f7a26ef1,8b387649,22320bef,58453373,6f728297,335c0fd4,72ea16b5,32a7336d,3332400b,303c0236,b6a1294d,88ce7fe9,15571284,d1f7c189) +,S(198cbfcf,a0575fc2,c161c696,d85155fe,6943ab9b,d6e17223,d8844608,ad0369d8,d5e6268b,30952422,be59fe0e,fe7ba2e7,3215994,827a46c2,f2972b26,153cf7ae) +,S(1e056e89,b68cf35a,22183c08,9089b90d,5a147caa,780b1fd6,3aeb1350,afb0e5e8,b8241453,abc44c57,ddad6ff3,86d416f4,3e258a39,c6f8837,9f80472b,943f32b9) +,S(dc7ff974,8d827e7e,a6173b2f,1a646d47,d8108144,ce7f98fb,3fac729e,72faaa21,c1fdac5a,ef4c6f0f,fcb8e1c5,c4417c71,3e3d5f07,146daa1a,aaf2e7fe,e70c4914) +,S(71b95efc,c4981e07,5354bc1,1cdfbc48,36b2eff0,bf8f8ec2,9a99da1b,2fd28e79,fd5a3197,6fad6aee,c304752c,c3ebbc51,1f3695b0,9a737fa3,af42cc6,efd684cc) +,S(43854caf,29dc2bd6,c9f3e8ff,a25bba83,f6b96121,897044ae,6876883a,de542b3a,56365176,897632f,8dde3167,7a24f558,34c5c9a5,5c1cbf36,fd8b4480,3c9c6c81) +,S(2adfe17,90e9f9c,708c9b73,d5fd084,b6eff990,fb877961,45c2ecf2,d427b222,b5ce3160,5d6dd9c6,22cae425,ccd28912,c4439820,c06950cd,4c86d9b4,53abd7ed) +,S(a123452c,2b7eaf31,15b3a534,3b3ff31a,9f70c54,ae33c620,471e3e82,27a9d6f9,933d3483,78a71f44,3788194a,afc545e7,f53e37a6,f779f96e,8fa14ccd,ede3b4eb) +,S(9b89a3c2,ca995a81,86c15217,61348737,aab166ae,7decca60,3d06e32c,cec0a6ab,2a7d3701,a8724b12,bd7c4830,224ac083,cbea83d0,543b5414,80ba8c8a,e7731232) +,S(64dd7457,e7d9d739,8e2b9a0,dc45272b,384b0433,9ed8b2ed,c9079646,11e9e9b2,ea90f8aa,e214ee16,ed608a72,36699899,4e311dc7,780ef885,b29290c3,823c470e) +,S(59227431,be607c6b,d327fd71,4eb71c87,20abba42,1c7f550a,6b35767d,6fa2176c,cb7571c4,71c5527,65bd289e,a3cf3f38,796da2b1,2c953d0b,8705125c,4861d598) +,S(53d765cd,adb26e9e,1c80ddf1,99374363,843b7d08,a7237bdc,8c5106ef,795fe2c2,7bbbb198,eb39973b,76d87f81,94d45150,a66d4f3b,128a40be,c989a405,ad7c287b) +,S(e507de9e,c16b3bf3,523a989c,f5ff6c1,452ee90,9b66ffc1,6d7b519a,57bb66af,a2f2f02a,8272de6e,3dc8b395,8959ad2,51b6d3d0,4c81952,59a501e2,8ff7892a) +,S(16f48c6,eb84fb2,81903b8c,b9f60b7a,65601d76,e2a57983,5569c983,39b4a6f2,8613bb84,8398681d,66f1e75d,5b6ef44f,d827b629,f4956a4,41d8f503,dd32b289) +,S(650471ae,774265e3,270b5132,33d12d85,bb98e38,2a3b3af9,cab6339,e1446056,838e7793,34aa6fe6,cae90a62,d359c339,187b4032,15d97cda,4e62724a,a5a50306) +,S(15dea416,fa34584f,cc90e19d,69825fae,348d1ba1,fd7ac821,559aac2a,bc21dda8,1fcabf2,1e19ce68,ee12a3d2,84bd1304,10fa5f5,d45f9c15,d4070243,a8433047) +,S(b42b2495,4f1f70ed,3db90087,8357ba46,ee9d6a07,b4f7c751,dc5cba07,b05b46e2,e15723eb,e0bdbe6,d6f28d6d,a0443c63,4851f5b4,c551bec6,9f9196a0,969ed71) +,S(8e9e4f5,c6aeac31,1dab1125,dec9b460,6ab10b7e,8e250960,a17fc57f,c0230f83,ffb0e211,c79fbb79,78bd4e53,a05a267f,f1e32c34,d6287dee,64576d31,ab959ab2) +,S(87be7323,73bd4b73,8627fb63,bd4d50bf,d6f2bb81,f804b528,29549fe9,3fe1ac2e,f6a9186f,f147b9b5,ffc844b2,ec0e255a,1ae5537d,75624288,ce8421f8,7e94e1a4) +,S(43601d61,c8363874,85e9514a,b5c8924d,d2cfd466,af34ac95,2727e1,659d60f7,8791c000,7c09c94d,b328034b,88c5bbbc,11333536,6679eb09,9a5e75b5,83bc2c2a) +,S(341b1580,f83071c5,365f0bcb,ba66af96,6902e394,2a2560ac,a0daafa3,2ab49d0d,4b985b13,c5499026,7ff564d2,d4649c6f,7e8fdbe1,ba101d94,1c034e14,64877b20) +,S(175e7cb3,ce4a3a43,7c7181e2,c79fb154,33ac1aa8,e56492eb,57627171,f14dad95,31ce61a8,7834f52e,fcf8703f,93696f42,58130155,63ca5d9c,e92d8fc2,81135b0d) +,S(5ad430cc,64e61c61,e3b3c848,2ca3ecac,89c1e495,4c80ba98,249e45c1,307165ad,bea2a060,505c13bc,317ca083,c5a8b85c,9ead5f6e,1ac23fbe,ac7cecea,9251c791) +,S(41dce0d9,6dace318,988602df,7fa84c1,80f0ce3,dd7d09f2,8aefefa6,db8b837,74962c3f,ed9a6e9c,896635ea,855323b6,8850091,f84dee33,3cdff8d0,d2827928) +,S(a5ef4498,87104dda,103c1dc2,52067643,9aed2d5e,432fe5b,a23cc142,39961bcc,1cbcf83e,a363e0d9,3e6ecc32,8653ba7a,a165c526,765b09f0,696b0d61,f122db3a) +,S(4da26ece,9ad46003,38bdf68b,852a2cbe,18225f2e,2d6d5e62,6db57235,fb3a9d45,d10b5a63,7ab546bf,cc610e2d,1c3d61f4,61b0a806,e7ba29c7,3d3de909,e9fae659) +,S(6e621e6f,53d2408e,488d8eb1,6a19a4f7,e9d95585,11e69111,29dedc69,f98f4763,7b148ef2,73e1b131,341ad477,9342c7bc,7b945a2c,b52c448e,4bb5fd50,3cea1a19) +,S(ebaf5764,5bed7469,9b57ea75,8a395a90,66bae20a,8f082ab6,da4554d5,278be83b,5847f4e0,6c653033,5e29ea94,389ee3e6,d916314a,60126028,650ab9e0,bfcfbba7) +,S(c0f88a71,711b632d,24b55dbf,52b15d2,faa38ca1,1438c17a,6a6ff635,3310182f,2cbfad4d,16c07021,86611eec,408082fb,fb2e9898,141a5248,1c59e44e,cd0676ff) +,S(5d9b6c18,84b79498,c6244fbf,262922c6,dc1cddb7,3cf70ae0,1b5287b0,5b5c6350,328e831d,2f2b162c,4abe1644,bb54cc85,18db178c,5b6ae97e,5e85110c,7d7fdf1d) +,S(d1d1360f,37ed6e69,d4f214c6,323a53b7,e57d7595,55904016,654c49f0,4e02e21c,627eea93,c6c9b53f,94559414,40a8b100,6eba68d4,6c922b6a,1521f394,6dd15e4e) +,S(efc987cb,f1023af5,58acfa18,97b1b2b2,ace29a83,65674703,e4969ccb,ee411731,195a6a65,d3790bec,71642986,3bcef432,e38242fc,9f565dbb,e159bc42,f5740c69) +,S(f3026b97,163df3bd,61b88b78,73864480,968d1d7b,83ef6b01,31090faa,18284ff0,177c3a61,682363ab,bf281615,d59f06ff,5f87644c,84d670e9,a6c56ac1,b611509b) +,S(5d34ff5f,123b5b69,92ac92c6,8c9cff46,deeecf9,68ff830b,5622090d,682c5873,2d1a0b9c,8eaba065,43204df1,48eb1618,25443efa,80f8aff3,d49b6626,c5955ac8) +,S(cbf9ba17,94a95247,c39da065,84308cc8,e0ee591d,31a9b0bb,dac67280,468447f4,549b18c3,10feaea1,225fade,934112d3,4058101d,74f00538,1b82796a,d0461736) +,S(920975ba,9e2261b,bf5982a6,b57a7344,8e7747b8,368d7a53,79acacd4,c7dcd31f,e95e0508,81af550d,9221f5a,e9541031,6367d24b,d545bdcb,434e7638,acb46dbd) +,S(815b2ae4,6fdcb55d,926cdce8,2b4f25d0,39132312,3bc180ff,33fcf132,7eeca64,62f637c6,7d886374,8f1e2e26,865118b9,99285b87,55f25512,c968b4fe,49b8c971) +,S(1bb9a6c2,8e28d4ba,30ea8639,7a4d387e,27ca8025,da231926,de3c454,f7e0b16e,a0cbc016,5e32171c,8184265e,ac7e0147,206349d5,41035a94,f56efc49,dcd7ab93) +,S(6f0153fe,dffd83ea,b099d29d,dde278f1,9c05a4ba,78eb4c3d,34d337c6,da68bc22,a532d00d,35013f85,9f4041d3,aa231f2b,9fc49967,e0e2f82,4d5051f9,e7f0c626) +,S(3454f73b,3bee77a4,d00d384,71bf555a,ed23e5e6,c6dae855,2e9cb7a9,1b20258a,92c20846,cfdf1e8f,3fe5bcaa,6bdedc3,9926833a,3f40d28f,23a8f952,d8d18dde) +,S(367807c9,a3606b4e,1b8c2616,ad528030,1dfcf686,40eddf02,fc59317c,230e9a86,1f023f2f,a2bbece7,3dba14c,124095cb,fdc4f92f,281a14,8304a412,c16ecae6) +,S(8ec4fdc3,9891f6af,1374e06f,c44b815,1b82541,75fc4909,acba5941,201af62b,2dc6cae5,cac2d887,83dca0e5,3c798f8,fe067bcf,5fc29751,13756cf7,ef4e5f1b) +#endif +#if WINDOW_G > 10 +,S(5cc24a6d,4c5b24f9,14542f91,e5fa937f,ffa08551,51b8b842,8729b06a,9178a263,b1da8635,81531a1f,bfb38a4e,419fa1fe,ca8d55a8,3ddbcb98,da19d5cf,fb7da472) +,S(83905926,c03905c3,a9644a6c,da810dd2,92602a50,50c52a21,9134fc4d,e3599e9f,4293260f,e8af6792,a20b115e,aa837638,9094298b,21d9de16,cf20e0c5,7a46089a) +,S(944b097e,4721e9dd,f8204ac3,d3878fa,e8fa6c14,34ae4822,481b2985,6589b6c7,5fc47565,30e9b095,f8b79643,1e745b99,1525bd4c,4764e8e,e8af4b96,9bd6ddf6) +,S(ab0b4a3f,cfb7c134,e1caaf04,a63a7331,17327a1e,5fa90301,7b5ebbf,3423b73d,e1e79263,a5bbba8a,cd78c92c,faeccd3a,b84944d3,785c0781,3763bf1a,ce96c9da) +,S(57a344b5,220f2b0e,f7bf7fbf,5a2e4e7,1aa2c3d8,7bf090bc,f803dfee,fe8b85f,f82b7d0e,8ef9620,e48c9f13,53a72a95,a9e11a3c,1678cf10,576e639b,b1a7d04e) +,S(6e053e1a,800b7c4c,51f8c4c9,5cf0f4ff,3608e396,ec46188d,a1a9263f,c8d81ac5,86389e98,e3c823c9,e1b5384f,fce428c3,62e36579,7202fdba,dc3d8c49,395e7473) +,S(62d76406,1804717f,b30d4a7c,7567b545,48a289ec,7f083f1c,59deae25,ce485cb7,f1d6314b,661d1f8d,8531d57b,9470fb53,d1509b2d,a626a0fc,4cafc941,b668be84) +,S(e5db28f2,219fb2aa,830cf108,bd2449a,5c4d0800,82d34658,9e347f31,dd29250f,c296e646,32f97dd5,ba3b7012,ead44b6f,324fcfd2,f35f8b24,8e58fe68,888bd453) +,S(4725b3e9,a3d00de4,8a53177c,9fff831f,733ec89b,c2994ab2,3fe815f1,9b32729,ac3b7747,de38c00f,145fdb74,1482bb52,324127cd,2979ee12,d4a9e689,b9f8e778) +,S(d0d3afb6,492c72e7,394ed918,7013e347,b036b65e,a76e0569,bbe9e346,41d72b3e,2dd3a45b,94aafe07,53061caa,a28560,bd952b0b,2f63f13,96f42fb7,8e02fef3) +,S(7853e735,d717c857,97b85654,a24acff3,104143ed,cf4b4b4c,7869068f,c304632c,a873d9,e70fd14a,c2777a8f,b5a02922,bae3a31a,14938e69,cb535355,de1efadd) +,S(6db26b3,7d4fdc59,13a1a01a,14c92356,ee44e2c9,7f9d72ca,7789de33,8ee904a4,14fe2225,bf2ba0cc,28c1c409,e9849c4a,d8adf792,63869b68,54a28ae9,631941fb) +,S(b52f0869,bb98af3c,b2f7f5c,669fa43e,538e400f,63a9cce6,99aa2ef8,eb2848df,24566b24,55bc454a,e7b378cc,a0e57b6a,8b821c8c,a76fb858,bb616e17,8907be5a) +,S(8614dde1,1ee6af03,eb34a9e9,970fa7c3,234152c0,1384f7e4,c1e1f93a,197b448,2a1125a2,ac996870,2ba22b5a,d230c40e,4e5c7e55,d8ba6ba1,5e54d3cc,7b72f3cc) +,S(982a37ae,625f6e5b,78e71c18,f20bdd0,8b3308eb,59d0cab2,dbc20937,938c1cfa,671fe16f,a65128,591249e2,b5070bf,2a689e2b,dd6c57dc,18e5309a,b6a40d0c) +,S(6bea9305,26b4829c,ec99742f,c9231c00,627a09af,22ca9d9b,4081a2fd,4c3e703a,aec5402a,54517b4c,1f344d14,b1943e69,d7541f10,c45bd483,2b02b059,499a6cc1) +,S(b5724781,8694487e,7cc188b3,36d116b0,54016a99,20731920,ba7bd29,96583edb,5cbbd186,1f80a4bd,3d58262d,ebc58ffd,1d278fb3,b4d7fec6,208f6286,77845cdc) +,S(de1ade62,7ba00e91,786f4f53,18ac5392,4df5a534,704edbb6,2e0e9e2d,997c5412,7ef486c5,bf23bfb,fc6dc15a,41c0673f,a9d003e6,e1d09a8e,4bffee9d,ae2021e0) +,S(3738a57c,b1721c41,9f9e465d,fb80e1d7,33720398,bc01166d,d476d36f,3398c0a0,e1f25bb2,1662b16d,6d28a629,ad84385a,43df566c,52924bfd,11854a78,b70c22f5) +,S(4cae21c1,6b2a1239,a85575f1,2ddf6daa,1955feb8,b7502e37,6940006e,7a81885d,2affe855,388660d8,27671e89,c82832b2,25fa2c9b,29d559e2,37af565c,b4dc99c3) +,S(4c511a1f,ba8a0be3,6e37cf55,85bcc3a7,97bfa7ee,1baa6039,5732faf8,dcc2bd7b,ea69f794,5c6babff,23400fd4,a95d6833,abb27269,887c372d,8a6a45ca,b25651af) +,S(d811d8c4,323b4370,2607c25a,de936c0a,c2e4a44b,2ba51f83,20c5179e,745a7e29,6ff970a2,17bf47f8,da0aeab,cb490e3b,46c6df3f,69f9e93c,3c4dd94a,e7c05345) +,S(101eb5d3,b5e5aaff,e39bbafd,f13b5ffa,33db68ae,1fb09b0b,1cae25e6,16c43eee,9d6a5514,9867725a,607a1c96,eb03120c,a4970939,34e0cf4a,1631a63a,bac5ddb3) +,S(89eb1152,b8dde45f,e141f21f,62fead1,ecdc7f70,eff8f968,de21cf8b,72480519,f8a337c4,3181e3d5,69ec99d7,f2bc4770,cb6703a8,63fbd95b,41fdb07c,b697b447) +,S(53d633f3,f48ea44d,273d8f1e,455019a9,49d95eeb,68de70cd,bd1e964d,9ad0dc16,1b26ed76,964dcf1b,3f7fe6f3,2e6db7e6,8e8ff4ee,48fe63af,48ffd33b,cd1645fe) +,S(de106910,49891106,59a94d26,b9cedb64,6ce3db4b,8398ad1a,92f8f95f,d8d9be6a,87640b41,dbd99d16,578a3d06,d23d4088,8768a4e6,dcf83e7b,5b9d9133,5eb43d32) +,S(40ed1e6f,b9ee245,ac189a9a,7809da10,cc6daa7b,41a163ca,f761773c,6af5bea2,ffed1501,7298fb9a,78eb7bdf,1430ac04,82bae82e,a7197adb,50bcc1e1,fc217b2e) +,S(55faaaee,59a20b9a,3784d171,9ea529cc,1b7ab3a3,29f26dfa,a3b11bd6,67ba15c1,a9f8939b,52ac53e3,5ed4771d,8e47065c,d7f2805,a7e5475c,37353ddf,182f4a65) +,S(b51de64e,21cf88af,2180ca17,24956d10,a95ac607,f034fccb,a53bd02b,fa8af3fb,175d9b70,1c652ebf,46b99f1f,6e66c4a6,9b840019,a48b1ac4,878b386c,3b7f81e) +,S(5156b0dc,bcd91e82,4bb235be,9367cf40,7b8927e8,cd874171,556f997b,7b07b143,1d457c0f,a29d4c3f,e65ded19,1127016d,d40ada90,cbb0d8de,c2af4bf8,20cc91dc) +,S(3bf51d72,60b4973,161fac55,88e441a7,f1993c06,791b6bcd,e11d8e,96dd63d,7bc6b470,90a7afdb,fc63905d,df698499,73e1e4e2,af74f126,63eae8a4,f4722d49) +,S(d2971091,20088102,1154f4d3,ba6f2da1,22fa74c6,4faae05a,d76db7b0,9ad61fe5,7350ebaa,f2ccf3c8,f940cc14,151c23c2,bc33cb73,65528156,92648f6b,628e3fc8) +,S(c2ee47ca,8e17112f,a255a520,21ffd30b,6d7a3b7e,2341526c,559b60a9,c768013d,2e6e8b6b,d83b7f5b,1dc8e83b,edb3b23a,80d9ff08,2029831a,45305016,2bece448) +,S(559998d6,9ddcce0d,9e0da39e,96cc4c00,c5ef444b,561dbaaa,e475adbd,3e70b6cf,c49c2a00,95aeee5b,8deb5e8c,db4d21e4,cf0f3173,1af2fac5,25c534c2,34ffb328) +,S(4b4b02c3,e4c8badd,e45305c3,9721a98d,7c1955dd,90dcb2f9,e9d54901,6bbaf363,a0b67b7e,be8f59b3,c25d546e,3dc304d,d22a6a64,615bcfb9,25b685e3,18d43468) +,S(fbafe7fd,7836b427,51cf897c,5d42897c,7650ade8,ee1ed01f,e0d7dd2c,aac549c5,2f511943,52d07e51,7b7841f5,8eda5225,229a7e28,9a453aeb,5e70110e,12668436) +,S(ccd306f9,c65b8a0,7884e620,b73ac4e6,81b21bd0,2a4b219f,954de1b6,7076c06c,823fa47f,a7b0b50c,907056dc,73e04574,3f68a7,5a4c8566,8ab5f5b1,5657510b) +,S(bafbd838,995a691d,3b5a870a,7847452d,9124155d,d89a9980,820b8d56,18195a3b,8df4c9fa,c94829ee,fb0e7f52,1020b5b0,4f05f4d5,839c8687,4e893e7d,8ad92a3) +,S(d125cc3a,8073156e,a166af8,ff940a64,713d8f37,b86919fb,157cc380,224f458f,a030e8b6,ac4e37a7,df01054a,ea23a78e,1bb7a22e,f26052a3,a034ecbc,bc35abde) +,S(7d491f28,1b5ddaba,ef2c6433,be22e42d,f2d1bbfd,8a7e8866,8cbf278e,cb8187c2,ca18161d,70360966,ca381ea3,f760b2fe,28b040c7,ebc82af9,bea27ba6,f4dfe8ed) +,S(31ccda33,9f29123a,86c2995c,6a9f4979,6d70a595,5079b961,6015f07b,cdf8c39e,aeb27d0b,edf61fac,99528a9f,f060d1f2,376626b0,bc807a19,561c2e4f,e8b49f65) +,S(79e64c7,f45f9189,6c92073e,71b76fb1,e5ec912f,de11ba7,d7439f4a,297807ba,57d93436,b354963c,5891abe3,825d89b4,d17685bc,c9632e7e,fe85ded7,5823c921) +,S(d6232edf,fccb3868,71ef057b,60bda43f,69f422d2,debce06c,78a31f6a,8c42274e,13daefa0,b2e2e09d,36f472f2,29b5f7d1,58a18f63,ac3dc4d6,d62cc7d7,ec0ef826) +,S(21c0f298,b2d6e3a5,3738fc00,c8289458,722d78d2,48a33ea6,a7ebb667,446e368e,867553ec,17d2fd95,b895eede,15da569e,cbb3f25a,e5f334a2,b20660df,df062383) +,S(1d1a3d01,dcbf799,d551621c,54f74720,eaa2311c,3fd9b1d0,f4f2caee,4c3196c8,d5a7a115,9900f90c,879b1a30,13945d8c,5453bf1c,7e5e33d,6f8faeae,439734eb) +,S(57488680,8fa99ede,ca97d1,8582a151,62e26d5c,7753a561,4f4bb1dc,28e76735,debda859,7117ede0,dcc0ecc5,4ec1a494,b3bbee34,95db2b1e,ebaa1db9,2fc17c6f) +,S(b6dd34d3,4ce1dffa,4e1e820c,4c8bde9f,607194d3,1c1ce07c,9f6fadc8,9791715a,8bf48868,e405d533,2adc0ec9,49bc7cf,1487e554,3a043200,71786521,8a39cc9d) +,S(90f9ded5,69088772,ca2bc887,1522df9b,5821df6c,9e20b160,f3504bd,4a91d0de,d17e64a,dd534a6e,fdd0af26,b4494339,f76b3c1b,126397f9,a2beba76,eaf902ac) +,S(c55f34e8,58d19353,9106e4a0,c3002873,db07b239,9b10299d,260acc65,6b2cfa59,7d9f38d4,74b5a22e,72949f7f,e2cefb34,9a8fe1fb,c16b8dda,11f162de,30e50f5b) +,S(6ae44192,d38981c5,9b3f8452,c4a2b627,f39c16d2,b6f52575,c5f1722,56b8ecad,9bbba8dd,7c49204a,9edc4b91,e38e4973,fa61de1c,eda6ad61,603d9715,7f0b099d) +,S(33a7d639,423e1b36,6a93ad99,eab6444c,bd0c251f,6cd8b79a,e0344eef,6fdcffbc,c26112cc,6dca2a,fa52684f,c9ed22d3,2f067891,d5bac2c0,2bb86e3b,9411c61e) +,S(48471331,eac48670,28fa6642,a76ca6c5,3380c1d9,f52a4b2,ea640d47,f159af0a,b4ea8546,93fde01c,fb903a31,18bb61f5,c5047b94,705a714d,5b586d47,a0142704) +,S(565ac39,9ae731c0,4cffaab7,43d24b71,72defd79,beb8ce86,d46012a5,c2587917,deb56aa5,d45031ea,fc411fec,e09a9646,14664989,fd4f40d7,79c02981,98bbab48) +,S(964838b5,3b6c2b7d,6a8d017d,c5a32fc,99549094,6dcbf773,eaa7085c,98314c0b,2daf9f3c,ec44034f,c8a71ee0,ee76bd77,ac6fdc9e,fa042a2f,6caf9c5e,fd130b97) +,S(bc00da90,7b8d078b,9d83522d,ae548b14,6f9bff0d,2ef887c,4aae2f1e,c4eb88d5,6bd0fd11,f9e2db73,65f21dd1,34cb29f6,fef4d7f,b419abef,eafe15a4,20ddb152) +,S(1a59f855,f89f0c,f13dd8ae,9f5e550c,a2082bb3,27fb111d,75455ab,dba7bea4,7ea2fb40,d460adff,c9e25c53,a65c508,e77e6e76,e2e435c2,150c870a,7600c823) +,S(7a3b611b,d3bffc6a,d4508162,45695024,452706a3,279a6fda,d88598a5,4eccc764,16e09589,68066e52,c873d6ef,e549d3aa,83d30bd6,a1d730d3,68aa1ad2,32233145) +,S(2fed5577,9cbe0a5a,786fa95b,5c56e27a,a0a1cc28,51112bef,4cd5e1b7,15d6d91,7eeacc27,b2297fb5,63d691fa,36dc650a,f66fd3e6,6fe5e637,9959d46e,2d4bddfa) +,S(267ad217,952edf65,673efaea,6bebb44b,18326dd,f1b36d02,f0154a0d,774a9558,77593cc0,7e9d5e5e,688a7b33,bf54a01,703cc9b7,a0485e6d,907515bb,11a13fef) +,S(eb6ed62c,239b99c8,6978ccec,a5f32145,e0d341b,ca7eab10,6fa05ee1,7f6687e8,ad899e22,50f6a8e8,ce29b225,83ad8755,2a6e6ab8,9061d33b,6629fbfc,52c0a241) +,S(ca5cf17c,e03d214f,b4ef33c,41686c4f,5087a59b,f88ca7b1,891094d,430a9e5b,17e44eeb,473f75ba,cffae683,c8ea0299,e1935180,7257803e,f9963ad3,d0989ce9) +,S(4c7bc29c,acf0642e,63f035ec,a93e6b6c,c82f21cf,46623a2f,e4c7215e,51e82f7c,7c55e76e,8756796c,143f64e7,e40a402b,eb537be4,f1f5322,e59d2be2,82776875) +,S(57cab8e9,b42289f0,503e5013,acf4dd7,79783184,2389fd20,80b61bc3,671058e3,78225318,6671273d,a7b0b884,e72e9bee,2b048c45,9430d2ca,7c398aaa,9fd9e2bc) +,S(3c2bf23d,f1120c34,de813a82,a56843f5,abf3d272,ed2e6c27,53ff6310,9bc4823e,1759db63,88aed233,ce0c5b47,ec9dcb88,76740928,e12fe9d0,d08eb759,2d3f1990) +,S(c010a9ac,83e0742a,d3348dc,3dcfddd2,34170aa2,28d36856,d8581b1a,eab38d2,634c97ed,644a68f,e1f1da1f,190cf920,2b2dc810,446b78ea,9cd27684,3c76aafd) +,S(a4abd9ee,548ba468,e4cb632,b3d9c8a9,4f1b773f,ba3a9660,5504593a,92071994,3a14266a,ba263297,fc6ab00a,8403565d,c9390b48,ebffcb61,11b5e8ef,c8f87182) +,S(dc02c852,efc115f1,c5c08540,deceffe5,d68b82e5,99b78711,5a7a333d,e8dda172,e4e6593a,8e1ae842,ca7f4eae,d8cccaa2,8a35f8dc,cb8549ba,2367faf5,5f6c29c6) +,S(ee4769e8,223822ff,c28a5a4b,5f29fd7c,a54fc81,72175cac,1b6db5a2,91ec57c8,fca2c6bc,4076c8eb,a0978b9a,2b0158c4,33890514,28094619,38413fe6,c020385f) +,S(f3006c1d,34910c0f,d435a6de,cf088c38,2cf990cf,3124f2ab,b11d2727,d93ef066,7629f5,aa367f51,a29ebaf1,235cf267,f8016a6c,88dfcd9b,82d36904,c949eae2) +,S(7cfb203d,7bf5669,129f0a3e,325c3fd6,b1b9c804,4932f0f3,794b9357,1e7313b7,4f76b9fd,24339c38,c88c4217,387b016d,e4ae08fe,67182d39,82a45bb4,e82bc60f) +,S(cca3ce0,36e00993,9d30be8,e70455ad,efeabe71,83aeb206,a324d0eb,32312c0a,9674ae58,fe947c6d,34c63725,2058c88c,91b437dd,98da58b2,efd7e8d,1615b9e5) +,S(9fb64148,81cd5c27,82da071c,1f98d71,d9815fd2,2389d212,c66ace66,ab0d9c8,4c25454,1a513742,5f75e515,c04f1c73,d11d5047,9c76b09b,ce23c13e,d1813251) +,S(1dbbebf,3e3d459,10d39de2,32560b4d,eb5d2ad8,6aa1542,c2c070b6,51558b25,ec68e71c,2e94e490,4194753f,d7484ca1,940eae51,69831876,877b9ce7,2d0b9db1) +,S(7751d219,92f47421,7742856b,4c84d41,5a890ee9,24185e21,5a210a3f,a46bb5f7,2915a9c5,b770c86c,edd32749,3f698c17,6371f6a1,4cac5a22,73a3c648,1b237fef) +,S(39a849a0,5b189c3e,20782983,ecf664b1,e7b89c96,4e11d737,70797fca,ecf36a2d,d3e6f8f1,6191db06,be2274d1,56685895,48e69253,54f41114,f43b5ed1,13690c5) +,S(67e56125,b29ced20,f9f95522,d412d67c,80e3d628,b8bfddea,768117cb,e79b25bb,608dfc15,7e3c23f4,4aa0fd88,3d02d293,3c83edcf,66ea57f,9167b712,8d03da21) +,S(a51aeda,2f7d59d,d31eec20,e95839f6,ecdd01ef,529327bb,5cb229b7,b78f1525,704967df,93fb862b,3d38b18f,4c6818ab,6621c7d0,80d92cdd,1f0092c0,d7847903) +,S(c980693e,73a1cb46,f5eb71b7,73b00389,f8bcb36f,fb489e7a,aae64c23,6ac1745d,bf3b808e,fe6b7efb,771853e5,6fbba6e8,b3b21fef,706bd4,274cc058,b6fae474) +,S(c78df930,bcf54c1c,28e27aab,2975e9f3,94167bd2,948d4713,6b17a374,a1391a9,9a424f36,6d3a79be,55bb5e9a,283be1e0,163bfe22,1bc65e5e,318f0de,304d9ae7) +,S(9a75f7bc,c6d2f3a9,e6168ebc,8f7f1e50,a998b1e4,e67316e5,fb242fc4,6d284089,cb478e6,dff8b9aa,3ad06c6e,8fc7e35a,39bf6ec3,787977bf,d40f3159,bf169e01) +,S(76b2131a,db0bb3e7,db48277d,95ce73e4,7eba1e7c,b6f801c0,d20b71a3,8b6e6224,32e3cda,ef60e55b,a9e36e6,cd287737,196c5af8,120c47b2,f79d2492,9979263a) +,S(21898361,4ec5971d,f55f96c9,3da89483,cdcc4c46,deb8de68,f32a42b3,5c1384e1,2c396c50,44a4953a,b22a2d22,47769741,c5eda54a,d9c9ac97,d6486b5f,126dac3e) +,S(ac0b0a49,4e9d180d,101dbe1c,a528fecb,a08449a6,cf5d82a9,aa14f875,e3db8adc,26d1d412,25a1b583,2dd53b9c,56a6a8e8,371dd19f,31462dd9,b2aefe3e,70412554) +,S(f869b58c,851a65f,bd6d3329,75f596a2,9d1a78cd,bbf04a1b,6dca6c27,30e04625,40dee344,fc6f5c72,c18b1603,e149949c,a0cfb31b,b8b91b09,c3cbabb0,c6a5bff0) +,S(f74f02db,2406250f,8984a5f2,273c63ed,a640a43a,8e7d72aa,ecf78d6e,b9544c3a,882e3a6d,cacc1e92,8a3c1a61,85f2bcf1,5e364f7a,1eeeaaea,1c0af593,cf86185b) +,S(c0613eb6,1d6755eb,2ebc9284,a0aa69d2,88c39050,4b7e869f,f3d943aa,67ea65d,72f61ae5,27b5c82e,4afa8966,55d9289d,29931c1b,f0d36c09,fe4213c5,27848cba) +,S(eb66ff98,60ba08a6,3c0fd8c4,7117aeb0,ae0dbf63,524307a7,eb739f08,f31285db,c3142ca,54bcf2ae,8b05dfb9,4e40f4ba,7d3662f3,774e616,55c7515a,87ddc5d5) +,S(3a7108e6,a1256061,fc25d58b,ca534602,e41c6b15,156c15b8,71a516f0,ec4a861d,daae2d02,41cabb2,191b5be,c17e7e88,957bfcd7,66df1226,a183064e,3c4393a6) +,S(d17e3a58,c83169d1,33ee7371,7b205f2c,ea1a2ff3,4e744958,949e9403,b8f89d5f,98c35f7d,69e5d98b,920ffafa,6d15d349,2d75fcb9,81ca5022,5ac7ff32,618b1a20) +,S(d6e32892,cefbb8ed,14350cbf,2618e2fe,98caf6d4,f7679385,fff36bab,bf6541c,7175462e,be0f5ec0,7872b55,356f2498,976196bc,edbe021e,8081ebd,b2636298) +,S(318d49ff,60e900ae,6e8e2182,f38ec006,82ddc8ae,93aa6291,86d5ddf8,affef75,6e97ec27,dd7d614d,c1ed4fd8,fdf6aa70,8dcc3eb,be288831,a9888166,66333b2) +,S(fd6f9e30,1772db8b,a68ea5ae,b585abbd,c075e72b,68d6f67c,5a2f8144,98cf6346,dadd16e7,64775bd5,ca5c5f51,35843556,d7608230,aaff4125,4d4eab38,1d28d2c0) +,S(8fe25b38,a2c74761,a90ed08d,4bce768c,f074282a,e9560d31,566ae43a,182ddd36,6ee1e84a,77c6ab78,a2cda1b3,2fbdfe2e,51d66843,271d15c7,3f8e1a82,1e4c23a2) +,S(37cf0ed2,88ed8fef,4ae17bcf,f6bfb815,3e12bab2,a46d7e5d,2e6feb99,9793bef8,92e7a57,18eb0751,c99254f4,ddb5c278,e17a417b,21793339,119a146d,bbe3e0d3) +,S(688c2061,6b9d0c8,76deb812,b60000e8,eca0e04f,531ed1e0,9158442,7bf403c,bab4b2ba,41ba4d53,df512e76,32200bdc,11d8583,f2ceb5bd,a8b7eb14,5ccc01fe) +,S(4b4caaaa,f5399535,d822371d,63965216,d1b53584,d89a84e9,d868395a,70b3d804,3ddd27af,cd18049a,3b01d043,7761e4ea,652fbd91,115c470a,e4c7bd40,51ee73d2) +,S(6f5acbc4,4135da60,758ed9a5,18b39b5f,47cca398,24e20e56,1444dd52,e2fa1d2e,5dc9d46,e5f6d94d,48af2efc,2f197a2,f729f1d7,335fa569,524d37c,4acd58c4) +,S(79d2d449,3dffcb91,6fda527b,f8b6b622,661d6f23,738288c6,ce94313d,c267fc42,b2683bf1,3fa8f7fc,be302823,dfbc9153,8d902791,d5d6dde1,d2805f37,a0c08db) +,S(33a8e87e,246b7b9c,9c77a6b0,7cc67d41,f3915dbc,2180118,c0800b33,f9f95524,baa8244c,584fd05f,2655e36b,e2e5c459,207d30ef,dcdb2294,70b34333,9d16ff8f) +,S(9a169132,f3887df8,20561a7a,e4bd5461,3235ddb2,fba7a19,3f4daaba,79868e95,686fc317,995446c4,42945216,1a96595a,a233b100,e5f8bbc0,71990229,44630bcd) +,S(790b74f4,8405ce3f,9abdc7e9,439f2f5,c859989e,2ca5e6fc,29046104,8783ac42,8961c913,562385ca,990593a3,77111a5d,1dc6762b,b1a928a6,6ebb304b,8a24a701) +,S(e2a3d0cf,ae1fc2d9,87caaeb6,9f32fcd6,95471785,f524e438,b5487aa3,72f9e49c,9cc24205,ecf2708c,357edb54,308ce452,11f7dc03,98fb48db,4982d337,14c93046) +,S(cb30c43,5992c195,76d372b1,96f2ae68,cfd2653e,63e3ba7a,2b3c21ec,960bcbea,f65735b9,708338c9,acc9cba1,4b491b8a,fb683058,266a483d,4987cdd3,89ccd0b4) +,S(1faedc95,d6310bee,6298ffb1,6fb3e6a3,296ca66b,7c995d2d,fe924381,10e8b46a,35039e3f,8201e904,e5378d03,2bd8b766,26d9f0ce,4425d2ce,e7bbd24f,b8725089) +,S(a646cf5a,2be8c1d1,3f05410c,ed51b3f5,8e93ea53,1e28bf31,8f6c750b,52b3e8f9,58c58ecb,7727a920,b47a7e78,e1e51d11,e427d7e4,465429bd,a01f4650,a4c102bb) +,S(32734727,18537d38,e9b8c397,a5cc322d,1c77c0c6,15eded39,dcebc020,debe2205,c2debc89,58c3f60b,d4c7b072,d475a7d5,c1ea784b,c3e532bb,72c02490,9a42d65) +,S(16a1a718,855a212,c068c095,d930a270,9906dd22,e7db3f4d,4ce2f393,d572827e,d389d64b,b30ba9eb,6ce80335,cbe8f7dd,b1ef3016,a8b74660,2196b724,d115605c) +,S(b4bf4d14,5de25571,33694b86,76166e90,eeb1ebf8,4a7402e9,a61af4cf,23687596,49db20a4,d82397e5,6318640a,6b605e96,37d38961,a58ae3db,fac2fc11,bbd11242) +,S(d4ed67a9,ccf4665e,418a0de0,8d9380cb,fc311413,e90a964b,bf367b9b,b892630,8ff7e57c,e79a0883,1fe8a972,92493764,7472b77,61b055fa,37105d18,b51df131) +,S(8ad256f1,9f413ef2,eae906f,c1c2ca4,44d5dfdc,f916d366,b32f1f47,f54fe70d,bdeeeb41,8d2c4dc5,82547f65,91c97219,34778f5e,d25eff4,94e243ed,a6d34da7) +,S(8dd3e2fa,fe55b5f1,f13723e0,1a5587b9,c67d22ee,21f1f62f,7f62a15,31f17f8e,aa23d2a4,7d6b717e,55d7c6b6,5d9c46c6,8f2db41c,ba66960b,4b4bf93f,5fc88cb0) +,S(44fd9e28,2a8e30af,ac34db01,a7b0e939,3c13917,554d9a67,8e577a5d,4b3d0f6,cb079061,353319b3,a1669d59,295bdbe2,18927d06,ccb1290e,4840cc16,911a61d0) +,S(5d249a02,4464ed65,831d2aa9,c9645f46,2db7c9f4,e5a46477,746b1a71,88b7aa42,2bff7386,527d6b5c,2c595f80,e08b4d20,23bb8dfb,f294da12,b54f8b14,77281d0) +,S(d98eb458,1edcd51d,465e123c,d891c481,ddd18b54,b9408ca,85ac47c8,e10fb23,15bfd3be,d3756ac6,f2f1f9e6,f254c9b,2b153717,1b265081,6a2cc21b,78b4dcf2) +,S(2f12c369,19a2aeb6,2132134d,2e85150f,6edb486e,9d672eb5,876804d6,44db6082,c0111edd,3b4d28c4,edd264b8,dc2bd0e3,20834dc7,bda82dd0,827133c5,7f8cf73b) +,S(4d949684,7099c9f8,421d35df,ebce57aa,71326034,8f07e3e6,f03017c3,144b4624,9afb5a57,d71369c7,28db308f,b8b07da0,b91f954d,b182eadb,126b1fff,ddb673da) +,S(cf1db910,5e95f1c8,e360b39a,752ab17e,fca8f24,d9ddc8b2,a9768700,e2af2466,e7c99f66,c49c8674,a6c1b94b,4b964019,5e645ca7,b3d2e004,ad8dc73,f647ca5f) +,S(e71eb425,7d2ffa8a,b79d63a2,42c67585,d4b1747f,5b27f22,a9ee4284,6aa62b85,a906aeec,f901a376,7d721f45,da53e342,3bc0a779,870e33c3,d10f6426,6a2c8865) +,S(bcd987d3,b2575f4,3bc5c3e1,b4a299bf,21c58b6c,a27ebb21,8f9882ab,30559887,403804c9,4339ae27,2bafd894,e63a4eb4,690c1b4c,9289db00,761e9b3a,1d483f65) +,S(b74d5cf7,48af7162,395d2d5e,8a69f740,7a2bebba,69564042,ea9c7e34,b1f8cab0,b5c5fddb,332bfb59,835e9a23,4afa4543,5728dfaa,7dcfca3e,a3c57deb,e849b336) +,S(46f3def5,9835c9ae,1a7b3b13,a920bea9,e9bc12ee,633b3edc,57251b72,3560837d,587b2e1f,2e8eb9cb,37aa6977,870214ec,818fb71c,1035eeca,91d53e68,150ce67b) +,S(8d3418fd,eb3f0968,de51f4d7,cbb60299,a50ceba4,1915d8b6,5774cfb7,7667d572,cee79910,16a28462,aa9da7c0,f67b4c7,e8b3dd38,f4b2203,9d05115a,ac661db8) +,S(98cb0f09,4510695d,78a0a672,eb67253a,30f4327b,7864fbcf,529cf212,ad6f7c10,25952dac,4e1b5035,9b47e9b5,52852d1f,300fde75,a6bd532f,9fd56af,2a0ab333) +,S(96045e4c,ca075fc,4a5383f3,f03de105,a34c7c4c,b030ceff,b58b98e1,2b39a3cf,f527b8fd,92234151,ba11f24f,7bd5dfed,6ebd03e3,a5048c6e,8f2d5231,7201bafe) +,S(f262e891,42addef8,cba26a9c,ad779761,c3f4b3e5,5990a93e,703bf56a,99cc6030,275cc764,86c86a90,f597dd6,7092ecff,5382c7b7,33939ca1,d1f3218d,6f0b594b) +,S(cd79c02,ee175336,5f9f7900,be51bb2b,ad75ef81,3c6c5634,7cc717db,156d17fe,b5ebe8ff,b30de5bd,a5326044,16abbae1,4ff9198e,b491794a,c4500031,947a985d) +,S(28b4bf22,5b1d40dd,e0122e03,e630830a,d9ba4629,cc51c9d2,80a225f9,48f858f4,24756189,98bb414d,4a534c6b,e300297b,aadf4ffc,b10e6795,4ea36e25,2cf3cac0) +,S(f018f206,7dd55df3,9c53344b,c6128a5f,cc765c36,c7b0bea0,16c8465c,c6ed2ff7,d9fc5ab2,cd619d4a,c66c1cd2,251504cc,cda34a76,86c5661a,960a00f8,e4aa7e66) +,S(496968cf,8086d02,9a5dd5c7,4f47cbda,7f661ef1,488b3942,18ed886c,424a7a13,d7d8df98,442060e0,f17a363,579c6600,360b5124,2774e2df,c8d4b787,1e1874e7) +,S(242d4c59,e9113b77,68e48574,93600da7,b984355c,a1d07949,8921016b,22efedab,470dcfd7,b0c8c2ed,63a28074,a505573b,42f50920,6b0ae577,27a8b796,7a0f42b1) +,S(b6b804cc,1a1a59d3,84c4afac,c5e5050f,466280bc,d5ab1586,1d20f4dd,1385e8c7,2f789346,2a636a9,9ba2a1d2,32829f41,39b57967,bb26d0c,898798bb,6521d439) +,S(dc30a3d0,7ecc5fdd,38b8048a,f281024b,46904b2d,2a4c51ca,d1b49b7,1d918a4e,326409b0,70550e97,58f53698,7936a54a,b701f7b6,78a7d10f,ebb511a6,c18cc917) +,S(e1fe434d,345bf330,83abb628,f4f44ac,5fb22934,977813c2,c015f2b,43d3fab8,1b251ca5,2af897ac,4c08df48,ce3d1607,d3b31b2c,2dcd7f7a,59a95b07,774c4d83) +,S(a8ed88da,e08ac5e2,afbe4a40,ee775645,82c2f513,cfd72060,bd1fd5d1,76fb8d08,f4fb60b0,74419630,fbaf5b53,717b2a6f,5cc2d98f,1d29481b,77b1f9f3,84175729) +,S(b52cf828,de1e9cd7,c95c083e,ec50d228,8e770713,a0e2543f,76fa5790,2c7c6481,635a3953,46fbbf,903dc729,d3e3b52b,43eebdc5,3748ed83,3c95eaf7,1b75790f) +,S(702079ae,f76d9bfd,ccb957a9,4aad93fc,b1297c54,d634978e,4dc78292,161d5e83,4983c08b,f2d81c52,d3b3a202,e0c6ddb9,32a43a45,c4b95f82,c8c10642,5a783b52) +,S(4638d4d1,8e9de1ce,ef4fe8cb,db4378e2,3ed3dcb9,513cc9a0,dd73ee2e,be57bbf5,561ee032,45cc3066,968853f7,51fa36be,cb50740,a5951e87,cf77ef20,cb27e110) +,S(eeb34eb7,5facadde,d561dc5e,e3d0a039,d98e4880,910439cb,ecfb22d9,3b386bfc,f2b23cff,5ebdc8c,558db8c,1c311a94,e9d8f63a,d1cf4af4,c21c2349,a6e57c4c) +,S(7f2c6e5,becf3213,c1d07df0,cfbe8e39,f70a8c64,3df7575e,5c56859e,c52c45ca,950499c0,19719dae,fda0424,8d851e52,cf9d66ee,b211d89a,77be40de,22b6c89d) +,S(6ec76722,1ec5d27c,f7e11064,d4f8b016,3cea090f,6a950f81,85623303,23e53647,9b03c757,9704e839,c8eec7d0,f063c4fd,d0aa7ba0,5ad75254,984b1703,ae69a3d6) +,S(98f6f69d,320d5eca,1f184b0,378f67cd,b44a707a,c5b2af54,99e9f8a4,524dea7e,26ef6b3d,7b037663,979333fe,56bc33a,ca54a3dd,78c9244d,79ef5426,f2b69a02) +,S(cf1ef445,58b82c76,5d77ac99,8c0170c9,2d308668,2d9ed7af,2961cf05,5b47e390,8bcc1d05,fb880ec7,8762d8f3,f5afefe8,f4345bd8,36397d23,befdf446,498511c3) +,S(10e01651,efa26f2e,f88cc5c1,594f378,9ab62b82,5a0875aa,37c280a1,8c6f07e8,2e6a15e1,1f776335,30fb2cdb,788aaa98,83105da3,7e28c0a,8fad503e,b9779df2) +,S(b94e2fe0,41ce209b,6efd77d7,44983130,af90945,120b9a58,5427966,e93eee4a,8f7abf1,103e92f5,5dcb1dde,746df10d,a86bfb8d,2776a0f0,1d07142d,c9bca443) +,S(cf9e7d02,c671a9ea,145d233e,244d8af6,6d71cc34,e17f2ccc,1225d2b3,16d41154,82eeb8a1,f0b966f,bbc3858a,3878f969,a853e2bb,e1b60c4e,e13eff85,d2d2ca7) +,S(da3a3c29,94ad3b91,319d18eb,dcf4b934,df8e4f2b,e052f436,6d539aff,4bd48bb2,28f9d653,c4042478,831ccfe7,bec86663,5e0d32ed,f6db4f54,25669a91,d1dc0193) +,S(ab812818,2b80378b,d914c9fc,d2831f30,a0a095c9,d0e52450,d4ba27b0,f1eec101,5abc582,274876d6,3dce366a,fbb1d80a,2286dc2a,dc0c078f,1424d45e,26666b3) +,S(ea5b82a2,bf02caa5,d5d64391,c0965c53,f0edd967,f8df94ab,831408f7,e5c4977a,81fe8299,f320837,acddd504,7b888e6,2518d53e,897dacac,5d28bce8,ff3d9339) +,S(50605c0b,ab201d7d,3dd618b,e3da2d29,318aeac1,7df58d2c,a95b8fbc,fbe70fa2,e50b1e,7b5693,c4ecc160,d928cd6e,d4c978fc,6c2a68af,c03f397d,72a60383) +,S(f76c50b6,6d7dcace,f45409d8,854f79b6,51356661,108b7168,31b58b25,ea1191dd,7d055b74,b0195140,bc0e2e93,a97f07b3,a3040148,7720df86,d3bd1810,6df3cc72) +,S(de5053ba,d716047c,cc2367db,272e4e11,41312690,2bd79b32,f6d3d959,93260e7d,1c54a2d3,9c34b0db,deb61b5,b5f46033,b1e4c581,5113bf3,d3f38de1,c97cc7e2) +,S(ad3e719f,180e946b,1b98a332,bde18e54,437f1a24,949d712,1aff7c8d,fa06499d,b458d7f1,390fa4d7,d2473863,abedc063,ef310adf,7ca2f2c8,82bcad3e,2f1b554) +,S(270a0234,5560af6b,6310c867,332f8b79,839a3ae7,6e2666ec,b46c840c,c36bae4b,8daab84b,92157d16,ca0f160,99877a1,ef7ef072,1bdb5bc6,89d16a19,4ebedeaf) +,S(eb5ed17e,3027c9c4,c87ffdb2,94a84ff7,25ba2b5b,2bf72d30,f98334ee,68624621,39fffcb3,8f74d471,9be8d1a2,14c61ad3,df4a91bf,d599c3ae,3b54b4ec,860a942) +,S(b9c11df2,8a0b98cc,623f4e80,75acd469,fb1f6621,a7572d6a,7bac135c,2ce0a5dd,5bfa8a78,d42b95ef,327eb0c3,692b3ba9,5eb506a2,4e2fca1e,391ec545,40a76278) +,S(eed17043,80abe259,fe661106,6eda0408,f60f1399,3c49a21c,a8a6310c,ebe74b5c,6dcb6276,cbe37b91,162a243f,57afcd9a,8cc2c661,652c324a,567d5db7,22a0d750) +,S(8e4b0f6f,5f5b9cf4,68e5524a,4adf62b6,8c68e310,5ee374c2,5265000c,53aa97a7,d1f880ae,c18d0191,b74578c1,6d0418f6,c7e59f2d,157c0d56,e25137ec,c1b7f74a) +,S(77ae21bd,fc6596b9,ae85ce9c,93112b1d,a7f393da,1238d76e,90cf59c8,caa5ebbe,f4484581,1e3266cd,f4625390,e729154f,51694717,3206ef9b,9106c547,ff4a21d1) +,S(1d379ca8,71c02ef4,59a8bd35,f5066265,92922533,f0503999,e16d6b07,dbd2346f,c0550b9a,72899a7b,42197cfe,d195c704,a6333d5e,178d91c1,d50b772,4140cad0) +,S(625017f3,28fc16ae,99367199,5f02772a,5c7ea721,7d47c296,cbad670c,3fef1ccc,1b44e253,af3b214f,edbeb4ac,50110df6,d7056d06,617c2bf3,6189a74d,64450603) +,S(c7a4fe49,6bdc3509,3ae3f508,d43877e8,d1019d16,740a5eb7,8eaa72b8,638412d,1b5b12a8,95fb7130,e8792406,74b8f735,6730938b,675996fb,a30a9744,960febef) +,S(cff9f83c,e8f9c4ef,3e80102c,bc48c409,b65c6166,b74f052f,a5628348,b2441d3f,4cc418f8,2180207e,fc846a41,ed6973f2,814ceb07,a8bd252e,338e32cd,e352ee6f) +,S(84027710,45b8c295,c938ae98,a9ea11dc,2fd23ae5,7ade6ef8,2c604721,66700541,72946592,d3c11404,881b972c,eba81d4f,f3f80dcd,40b15da5,f56e0723,841e4f99) +,S(f7ab1330,e00cb9ec,61a346b,d8f5522d,5e304a4e,a94f84af,b5c70614,d2e0d2c,5d4ac851,89350a54,af58def4,6f3f3d69,2ef0f6a3,9dd4ef86,6bb1101b,df5e6144) +,S(a7c84e19,2303b778,8c1a4d5b,786c9829,d0da4ddc,c13f692c,7851884c,6fd4e618,56e32e5b,8fc2db2f,21d64bad,35e3f115,65928533,a829d744,939ba456,bb1774fe) +,S(b8b0c884,a680dac0,363bc55a,fa677a72,c65f4139,7f5cdcc,f199d80d,ac98c43a,43e4188a,c92606c1,cd6bec5b,a4edb045,6569f87a,625db932,d0f8e71e,516b8127) +,S(816ec443,d0576135,5a3bc33c,1db4a179,c5bef98e,943af8dd,ac250482,a65e0df6,89a7b2c9,e9f3588d,990f6fa1,96e366b6,54f9a30,1218a2a9,c9b87c48,55f0b360) +,S(8ded2103,823d07d5,33fba24f,7fccc47c,b101960f,3b717fd2,af39d49a,a91e28a0,3f03ed1f,be1a661e,f43ef9f7,d753eaad,e3e1a391,2a682ad4,fb19526c,c0011728) +,S(b1c4b39c,e874d478,bfbdf9e3,ce8c3165,ff4f7c4,bee20c87,4849aeaa,d0d6260b,38af8b61,30701881,fbbfe724,5396adc9,32c25b84,dfbc4dbd,a54bd236,c04b4fde) +,S(7d5bce3b,e5953eb3,a17f657c,fabc8209,f011bcf2,7e10b90d,e75dad9,fab2c971,7380d686,5f5fd782,b0708b3b,62c39e49,864b2ba7,4066167c,bbcede2a,b0f84787) +,S(2af5111a,422af2cd,14e7717f,bc7171c4,96af178,8b229497,a54d2b87,4c00f285,4468bf1b,ee43f100,7e800b68,b7b3b243,2f3445c5,c8aca59f,7534beae,bb771f41) +,S(b46e29c0,6dc8ddef,f8bdfb65,f4043b61,fe010dae,a2d0955d,c81a7b49,637af766,62777495,ae16314f,d7d0cef8,24a27a9f,748c5c9b,9c51645,9c1ec9dd,9d7b59f5) +,S(105786d3,f44ea40f,71eb3bd5,339cd514,aef6d8fa,5923b4a5,89b219c,fba6b1ea,2a4ea7da,c87522cb,7d92c450,8e86d0b9,39fad024,9fc2cf15,1ba005cd,b743d0bc) +,S(1f3c0c81,be856e30,18934573,892ceb22,d2d2d8ef,f08b7857,5c624f48,16adb23f,7321690,de24499c,f52f4840,89c4d7b4,11158d52,d32731c9,faaea567,8bf6072b) +,S(16dcbae3,a95a95fa,912484f,f833ba0e,e437876b,f16f9c9f,5abf46aa,468bd2b3,56727c07,247b12d6,c1ade473,3c691700,30e5582a,cde14034,3449f241,59ffa44d) +,S(114be56c,d6723c8f,924ff3c0,f5edabc6,e42314a6,49edbfff,1c794438,dae70726,984dd72,855cdeae,7c580046,b32cfa7a,45b61b75,944735e1,d5fd9065,268ac521) +,S(8d0cb2e6,1f98472,e169dbac,7f5babff,51766b3a,8d74e17d,747b7eac,d903fdaf,d1593bf5,b9e793f7,25cea368,2ceabb11,f06b36ab,899e160a,f1f736a,80bc9b92) +,S(5b420754,188c82e4,db72f3a2,c8049691,34e3b43e,484b45ca,ab4727f5,31714db3,4a89307d,27d7f360,7acd1643,719be148,1c7eed5c,77598883,8bf6cc13,a4723d9f) +,S(91e3761d,95761d96,8d856a72,800fe0b5,9f01c62e,1cc446ed,18c1b58f,88cbf74b,b5ab61e3,96f88376,28f3389e,9999ef55,b73a58c0,d33debc7,25812530,2b96c772) +,S(31380b83,dc77e5f2,9c2f5548,abe052a,16e0f1f,2d8aad3,5b05d29c,5c1a4655,23ccafae,cc84ea72,3c434342,b839cd0a,b42173bc,1aa1db20,1073fe26,6593403d) +,S(ae40198c,bcd54264,904b44c7,c9019553,8f3f3727,a0c9639a,764cda80,b21edd27,3a57f08,ffecccfc,cd9a0e7c,827d98a2,ffceea9a,6c39d200,5cfae623,34eeba8f) +,S(f4729787,abeac0f1,249b1d3a,c19bb025,5764a854,727b415f,73e24c88,a8a981c8,e31161a5,68d81328,c7f9783f,de146d07,b51aae15,bfe719d,ab4e9e,ae65d172) +,S(f92f02b8,34148934,f7059b61,7e84d0a6,f52d3b8c,a7d7b69,ce4c8175,7ed5884a,faed7ef8,82733020,888eb530,5c6dea4b,cacd208f,fbb66b7,6f3cf41,a8864080) +,S(a2bed8aa,e93bd682,be8cc4d,d1f84fc2,9efe4022,b2b0dec4,9ed3ce17,7cc93363,287ab9c9,62126fb7,e0340f29,9edeb89e,7bf235b3,b3f08213,d630c9df,198ada1d) +,S(5a42cfcd,3d1811d1,99b09dc8,9308134,cf6219e2,e029598e,899418f9,b7af4724,95ce721c,39929d76,b0ea83b1,80bf25c2,e301413d,60fb8717,2e0ee8f0,593f8423) +,S(1d27ab05,67e8bf19,323f1eca,44175085,2e768a6b,a1436789,40ba2b94,5b0e530f,18439939,496d29a1,58a532be,76b04932,a23e3ddd,d51a1b07,c6c33e6a,11e1afa8) +,S(592bf859,22c05bf4,4b42fcf6,e4cdf3fd,bbbf8088,b7263d31,9bc74914,6eab4326,84cbd8c3,8dccf0ef,de3946f3,ba2eac25,3aaab976,8f7f601e,7d95bc74,4c7f65) +,S(66730a70,5ae3dab8,bfc01f4a,deeadc41,d4df156,d03c4fcf,1dd9e16f,9359d5,c04c7cc4,4ea1d8b2,273b9170,19b9efb3,6e8f422c,4863f718,582f57a0,778273ee) +,S(3d002600,532420ac,9fb246d,84e48560,1ce6897b,979491ae,7c5914e,dbb7fe83,5bd73264,2f16d4e7,7bcc4855,72494523,c221fa1d,321c552a,53241dd7,94c7d98e) +,S(d42ae36,2b5dd22f,d2089d37,d22e2c2a,750395ad,710aa1c0,61ed4275,43a24487,84439e00,1571e1ea,53bcc24b,a11b6f7c,ded0a649,b2a12fc9,c2a618fe,cb39ada7) +,S(37c761ca,486fabfd,b306d,c18e9074,409e101e,5bdd1670,edef1253,aa253743,e53840e3,1110db1,89ec241a,e7b2cb89,29669da9,5c07f3d0,6277f667,b90cad12) +,S(4e672ef8,bbafbc8e,eb819f37,15ac0d6c,2afa9781,a197345b,2889ed0a,f3b0e788,9c10f0db,bb16d807,4680133f,e62e0eab,130cb0b9,51937852,4afcf4c,38814061) +,S(c1f2943b,23c07929,e9c635ae,66f62949,8e1df12,f35aba68,ed8791d6,8fd51fec,3376b6c8,93ad5ec5,991f7ef9,6a6b1105,2f2262c6,680045fd,fc4d4bfe,ea64ae75) +,S(3df2461e,739cd984,72cf10cd,f663e651,351e826d,7afdcffc,e17602ae,c0370be6,5e48fcb3,3ed9c0ec,3594eeb0,81adaaa,bd5ec5a5,7eeaf01f,c9f00d02,c692cb06) +,S(d4d3d528,f98b92d5,f4c4ecfb,ab60fd85,50f12327,5b63b3fd,5518c1f3,d62a82b7,64496d50,85435de6,991a838e,d984bc09,2554053b,54b05a2e,490d2ce,da7d5b76) +,S(ecd8399b,4aa3368f,1a7a4113,63ff03a7,ad644847,e225108,b3ee7d02,1209236b,2e1209fb,a7921c29,e351e61,73c02cb5,c629d88f,4ca51af,30f37123,75355213) +,S(d5990be5,7e6a0da9,333354a,fb344d59,416d1a70,785cdae3,8c332e09,cdf78722,b2a2cd8b,73f76f3d,1079198d,34441008,278f73c8,93979ece,eefc5911,7ba17814) +,S(7598ab65,1d8ced00,af3d5978,661d801e,b57e9089,287bd74f,85ef042a,90374b1b,589fc426,9466b1ff,b9c0cbd4,b022e9ee,7edaae87,8f4cc240,b60db60d,6c28f04f) +,S(fd071417,b12d2d31,852048b7,61c5301b,be088cfd,98ebce7d,7e0922cf,41f694e7,2d9c53f1,ee63b93,2adf679d,96b548d8,d47d8f74,58ef8b45,9664da23,be32c363) +,S(5a5b4990,e290868a,d1d571ac,8e9347f2,b1e513bf,cbc2b8d1,5e56ce01,2b72ca0e,c76852f5,7159524,541569f6,3c4c9c7d,424285e7,e83145d5,e5161998,efebf968) +,S(93deec24,3cd4f146,42f5a415,f6ea6f36,25c90081,5f3d20f5,9728c056,fde7f2bd,88f919be,b9993a16,61f78145,f54149f4,6abfa859,1907736d,d5e85f25,bf2dc7cb) +,S(9224d17b,a65ad967,969104c9,3068d439,9c9bec7e,7613a602,f5c9917e,46df82b6,346c98e3,2bc2a4e8,c7689e54,eeb1064c,1b5691fb,cf5a4710,55cefbcf,18a32f65) +,S(851acc91,24ddff9c,14cf054b,87347a71,cfc91637,6e559189,f055143,3b79c38e,d959bdf0,58d21cc3,430527e8,cc49dc56,2dd20bf1,30f0e13a,55cc69cc,1e116f35) +,S(ff6c1303,b7c111c5,87e01ef,44829510,eff0a612,931f9212,2b71075f,2cea4d96,24f09bbf,7bcf50aa,ca239be,b925505e,7e99d0ff,5a959574,5370eea1,7b4841f6) +,S(f23bd9f0,55e7bd1d,433fb51e,c8b6ecb6,fd5014f,4f4ca9d0,6a4bc636,435fdac0,9652b458,9d269ad9,68ee7012,aee47447,25ba917d,f120bda6,9d2194ee,a56d7b60) +,S(9d622c56,ea6d5d19,7b823e60,1bbd4af0,1fe070f9,391bb564,2a05527a,49f46ff7,8061bb94,d6485fe,d9fbd557,66db3dca,c648c7c4,2e025494,e6c3a91c,fd5c609e) +,S(9494efe4,cff5d5e7,6394b67c,407b9961,2915afde,56933b80,370d0adf,2f020c13,ab0c7e38,c85ee1cc,27964b2,de42372f,fd86ecb8,fd3f0a81,84f5af38,58a7e4ea) +,S(a8e4d7ba,ffbc52fc,a8cb6c,2938a01c,e028c259,97403268,dbe1600a,952e1a86,8d5b83d,dc3022c,4f79723,c6f16722,cc7eeeb9,c964496f,c9f67c29,351a0bed) +,S(83841f64,938e460f,eff191ad,4d72d7a5,203c01af,1519cdf1,f17c3bb5,e4927ac5,f5868f2,c2e87249,21373397,40415c4a,ecb1f30d,b204fe41,80e01d79,eb536800) +,S(ee8974d9,5ef03f8a,860dcba6,7b597fb2,af2a2df6,950aeacb,d623c883,20612f6,5cf10d74,3085dd7,cc695145,3f9f763d,6596abfa,eb7cf216,34ee07f8,a4f141ac) +,S(68e88d8c,23616ca3,b136e5fa,3427628d,a92dde6b,2691d857,b2d01a60,b6579606,5a9090,3766d6eb,c58c5d60,9b6a5675,e5ec7b73,86a17c41,2574d179,a0fd5450) +,S(fe34dd8a,c62ec8b6,20dbea79,44ccca40,cfe6ab5a,ce3b2e7c,50ac589a,9522345b,88cf0055,adf1e122,535611d7,bd3e00a7,30c9edd5,898d25bf,f0abb9bf,6bd6c8c0) +,S(423a7a9c,c0f81489,aaa62479,c1317368,9989366,fd18bff7,f6a5f235,8251bc1f,c926b3dd,519241d,2e776ea,16112e41,259ce896,472ce71f,de4261bd,29c678ee) +,S(7fa60bf9,7cbf54ff,e61bb840,b2e08d91,ffb0eb4,727c0e73,ed4f157,ad0b98f4,d5b1aadf,84d387b8,fddc7b26,a7f736a6,7e296fed,7978e81f,6e7b3b09,e94d733) +,S(9e53cb0e,f1eddebc,77edf8a6,6917f915,8294815d,eef898c,f1e77433,60153a66,5358daf2,60fea2a1,b9a4c6f1,b8bebe09,4983499c,fe5577a2,ed8f817f,46b41ccf) +,S(adf93641,c616c677,99b44ad5,354332c0,1404718e,8ec91e96,31e69552,3ad6f119,da3c66af,e4cfb3df,f55f6761,5c6ac42a,8dc17241,5ad38619,dfe2a13b,7e42ff9e) +,S(701e3ec,485db367,b0687ddb,2be15f4b,5b6242c4,396f6f0c,9726ecb6,474e4ca8,1098fa76,13dce1c5,29f7fea,30740377,691aa3cf,3d13cf94,6f2d8191,2d3b9e83) +,S(d8fc5ba5,d3ecaa05,9e9a7e04,2c52500a,d513645e,6149e84,aac0d910,f8ccfefc,72724b4d,eec94da,7494b4d9,83a2df06,df96ee83,e578d920,c2597089,7b4b3d8a) +,S(7bc88f9a,8279ffb7,658a186,faaa5f0c,c900cb9d,4a33b8dc,c9eea83e,ff71398e,cdcd21df,e5ed8151,1a2d8c77,e08b76f7,4939d39f,1f5581b0,aa956066,fa14a7c) +,S(8578d531,bc79e1f,6e219bc7,7f2172a3,e5fb92a5,efbf9919,f46ea11,6f5ef0e5,fcd1b2ac,39c262c6,94eef5e0,52338f9c,cac2d04d,ed17c290,768759cd,13b7d550) +,S(98bc22d7,3ea2661b,545eb5f4,37e1cc0a,d4124020,44bb9093,5cdde758,a8020a62,143c7dde,d33ba5b,c2ac8d83,cc462f94,2607bff2,a1aa32d9,f32e14c2,82232226) +,S(7fcf821e,95d2176d,d088106c,a5bf0855,9980f265,ea3c401a,aca44b0a,d5e8ee10,27c341ae,87e7afee,f8af4598,e8e27cab,c127b054,1b389cfb,266ec9d2,fe400284) +,S(898a39b9,d440ef05,f87e195b,6a334f93,67acd67a,1fc76ca8,316292ea,216a5f1,eb642fbb,fe3274e5,2fd1cae9,744fabfa,c0db80e6,2bd4f7c3,fc2ca43b,18ce52ef) +,S(3ee974a2,8918c216,a962480,1f18bf9a,87d6a627,83323e3c,bf138bf4,3f2192a4,f5b386df,ac71eb35,499d5b2a,960fd20e,fde39fc,10dd5c63,dc50b55b,cd660d9c) +,S(bdd10d2f,de76df34,9ab753ee,d06c1251,9766bbf4,2fb610a5,7ddc3f36,4047c14a,504532ed,34f97eb9,b8b27664,ab440032,2b8b47f6,9c403bbd,6bc74330,16923dec) +,S(2c179437,39784124,4ff9a4a0,eb9ff292,477a5148,c4176b23,c000a1ce,3bedd63d,a367b584,4f9b7b2b,a82fccca,b9a12a8c,2c1ca5f7,3e630173,e37e600f,f2fcfb6) +,S(282ef30e,f2d61d13,6f71e6df,6ee97ebd,a83036a3,2d3cd841,36289798,b3e71580,90b0cd33,c350ea2e,54c25264,7a3fc91f,5b4f9dc3,1f58e86f,c47825dc,e3d0f43a) +,S(a42b4d55,42acfa7c,b54ab3d5,ee0bebd,85bf6ea,db138ef7,1ea38c73,899eff23,17e38880,f10b9927,764c4997,326b0b9f,63d0e03a,fd2f0bd,b1ff3cca,1f566bb7) +,S(b23acc64,c5b00c8f,4b580d47,49589559,a06fcea5,75f3d38b,1a02251e,ff7ef00e,66bb180a,7eaa64d8,4bfe6a17,d457f5e7,73964ac,ff908729,3bc540aa,1cb6464f) +,S(942dce6,96aeb063,4681ce7a,407516e9,89df029c,ecc77023,411a628f,75885ea7,e208be8a,88be7b3f,6b6b06ce,a3aafb45,c439f0ba,d517a784,ba13f80b,707caf14) +,S(b8a6a2b9,52194ec0,8238e4db,e71eb948,23e64a94,e10ea0e1,610465ab,88392d16,f8029f31,b8e5ee3f,77a758ae,343be258,34c7bef4,c1b555c7,4cb63d51,cba5fec1) +,S(a36d3369,a1ee05f7,b1fdb083,ea614cb6,f15feac4,17a416d9,cc83fc2,868fc20,e4abd7f9,fe40f27d,5b7b7651,6a9fd714,aacd2ae0,5f0a1f10,e94327e3,76f0af52) +,S(4ea30108,3d711ae6,ffccc89b,9d6fd4d5,6f03bd68,f4508cc0,ebef5e43,88dda1d,81e9997a,8b1c5949,6554bcf,154166d3,40f9ea5f,ce55ec83,eed793d5,5ca3e513) +,S(20228716,1c1c89c7,64742e62,e427b712,fee118f,406a175e,ce2f424e,2affdc55,7837965e,cf26a7f3,428d2864,41e1d09a,91eaff0c,5ecd2c28,bc817766,67fa35b1) +,S(d3c072fc,8e4bf160,5790d429,61206123,95be9092,f166784c,f5adb7e8,6070d20c,72688262,594c75bb,bb55c0fa,af7991c8,f412e0eb,afd1c0c,f5bcab82,d8daf31a) +,S(294dac7c,e307fa7c,cf0e8f34,28a354d2,94003dbd,ca763dad,3e6df60e,530ec82,479fc222,a7bcaa62,aee807c4,ba6309e6,cfbcc783,afd49ecc,fb9b45f,cf84673d) +,S(3956ca5c,c09fe8d9,e4042a8d,67b276fe,225de609,ea24575c,7075d9ac,4e732fae,1c965fe7,a8219052,9d25ee0c,e1fd62f3,7196431d,7bd78302,e287a26a,2eeaa23e) +,S(24d2e396,da144b48,e736eb06,84940175,4f4109d6,6d87a9e6,97ee82bf,91fb3def,bb79e772,24b02fcf,dd1e3c9c,edbad212,ea875c75,1fe7c81e,76d7d0d9,a372236e) +,S(638d4042,f16e5c07,fcbf9619,dd9c2f91,18852889,3e12cd3a,b49e2b9b,99a2aa5e,d0d97842,4f0010a4,df9eb8b,8e5d8147,f618da43,74b8a4db,222e4ab2,b32b3d92) +,S(f91ad5b,a99972ba,bb323a1f,d3032e1d,94202d88,63680c5c,d7d67828,8f0d866c,60b66d45,a693175a,3a38bb4f,efb32dac,1cb558e1,b0d13571,e24a14d5,9452bdc0) +,S(3154137d,779c25f1,4ff1fb88,a6d0370b,9e6af48d,88e7fc39,c6417272,eaa69b7c,eb3bd91e,9c09178a,398e112a,9264484e,c1980c19,b1993b13,560d39fb,92295f7a) +,S(f8821e1f,dfdf9fc8,1e3acb3c,1d1d7a65,f710be2d,94a19355,721cb93,61549597,bb507868,c1e05282,51b635b9,dcf28e5a,c5770c05,c7afc2a3,f4b4c45b,7d8993ec) +,S(8409463a,521404a5,a2d8aa90,50e3ad8,ec5d6faf,870333b0,6dd4f63e,8354a655,cc99452b,85092ca5,39cc1b66,83b7ab37,1b8a6525,269a1ecf,91b857be,e1db3bd4) +,S(96802411,12d370b5,6da22eb5,35745d9e,314380e5,68229e09,f7241066,3bc471,ddac2d37,7f03c201,ffa0419d,6596d103,27d6c703,13bb492f,f495f946,285d8f38) +,S(9d1abaec,9f5715a1,5c762824,4170951e,f85e87f,68ca5393,d3f9fc3f,a23a69c8,f21ee700,50dbb61c,238c89e6,29423538,71b010e7,98867bdd,149ad28b,3f28cadf) +,S(1fb96691,8db3af46,c37234b6,a4b04371,9886d6a0,5859ba32,f72742d6,141f7ae6,8bc13ecc,207efd91,f7f4c442,6e9a425a,a17b5578,20404eca,b09d5582,d41f7379) +,S(22d9e364,b9274dab,98bcb23,e0428e8a,416d54f0,5a781281,ee221db6,9e1ec7b8,5dc23258,f93d7db5,e5799195,b74e9519,4c300a91,28f924c1,c1e4865e,2d8407d) +,S(625fa450,aed083fb,30166766,d5874131,adb168c0,247cbff8,3987297b,f873e45d,720a8104,473dc904,65585ed2,1a0244f0,fbb5a286,5b8e5117,5a21f644,8776bb7b) +,S(24acb1c1,9b6dfc25,defb01c2,e2681ae8,2deacc0f,f21ae8ff,1f82f37,a6a2147f,f729d335,210e8061,8a8abdcd,b71aabe1,93f50ada,dfaf7d52,783991dd,d3ca5d4b) +,S(d2856803,a99d30d4,f3a328e3,bbf7db3b,6c6d1896,ba5b339f,33c1302c,f75ab555,9649994d,64705b87,62c98bbf,1b41b725,2b814a89,3e780fdf,5689c9da,692d6ff) +,S(80529e65,9d196884,b15e95ef,871e5fd8,8fb5298f,3bc7831b,50a0bc98,4cb5fe0a,868b3e4f,9ae4bea8,efde50bf,c5a5b268,b74b4a8,74e86de6,e9ae476f,10b3b778) +,S(7f9199aa,3b8201ed,5d288e49,49c43277,e0bb316,953f1dde,d0674d7e,8728e183,1aba673,d0372029,546c174a,3a72268c,ee9d7e41,dd97de2f,fcbe1f9f,1a5288b9) +,S(7d32c885,8e959f6,48c4674c,dcccb191,29b4566d,644d2fb7,6d0c8966,2c29ecbc,d90e94ec,b50bfee8,c951afb8,e65c7386,875ea99e,31f553e8,66e00342,6d39698) +,S(f2b961c0,9291ecc8,576ce67e,bbd1bf01,1f1727ff,ad9eb74c,bf8819e3,2d1abfbf,ebfe5849,5a1e6a36,46c38220,8656f638,ed0aec0a,d40c0524,1707102,3126f795) +,S(d9a0c689,95283291,972d6a72,897181b2,6b4ae317,4e98a676,f75bbfbf,81be876e,d36ae886,a0acbc9a,d1564d97,4d3f0309,e9039b93,bb350d92,2b7f8c7a,c6bfa042) +,S(c7a36324,6aeb7c8c,991b2aa7,10abdf5c,fff29912,30b3a69f,be2dd481,7c7c3e0a,1298fdd7,e448d2d,79986302,6b4b2d49,2b32148,d6d1e5f8,15f5b0c0,5c9e21f) +#endif +#if WINDOW_G > 11 +,S(635cd7a0,5064d3bc,66535a0,4dbf563a,640d2464,c7fe0ac4,8304214f,e4985a86,e40265,913e77f6,46735cfc,af9e2f30,e8d5f047,f3281a4c,e0453e27,e9e3ae1f) +,S(5da624f,38edea25,37f5b632,695b481a,eca54bb7,2169cadc,c5b91e10,989ee5f5,d3ea6dba,a1080300,fffaeeb2,43a5256e,48c28822,37b16505,a220d771,ca721779) +,S(aa4f64a2,b19775ec,92c1f687,1fe4b6d5,ce05278c,2976c80e,fe19284f,e87d4d5e,f39f117e,95fce0fb,6976e949,9583a66c,224ea028,8396518e,293793dc,3ed4f240) +,S(9b6518ea,99ff1b42,22a93f26,62e05551,d60969ec,ebe378c1,477b5c36,427e0641,2e9f1655,8650fb3b,43aea9dd,30532ff9,bebdf7c5,1d421656,27b7487d,8f93165a) +,S(6c9b9aa2,b5972e0f,a1e01138,1ddcbeb8,6547f47f,fb101159,4b193d00,ae97b562,af09d91c,8d4c71d2,be523d0f,9d205bda,a6e78eb2,67476d3b,1624a038,6275e27d) +,S(a2a1c880,3cdc95c5,5e636541,c7112a0,cf80aec6,a37e5f71,f5366612,db467cbe,c4cf570f,3638f040,ffe5410d,50d4b175,802e2e1a,c422fc2,73eb9b2d,4e23fc09) +,S(63d3750,c961ccd6,34a5af10,48897366,13102bae,20f2c8a9,ca8437e5,ab650427,87415332,74b39a76,72de90dc,698742a1,f21125f0,71393fa0,6d670045,8e339248) +,S(8d070e66,e4428255,a53e8fb,5845d14c,bcc97dbf,19b6930e,1f5160b2,52bdd04a,4bc98c82,4f2b4f04,c9884099,f173cd22,48cdef90,ceef4bb2,497da8dd,99487ecd) +,S(f1fe56a7,2e5f008b,b5014395,cc1658f,e3f3d4df,7b361456,175bfd2b,4a91c8bd,be5ffebe,e57a7da5,d01e302a,d3688567,a19caa5f,fe8a57b2,be2c866e,f5f275f2) +,S(802c9adb,d2eb969e,9ba71a95,675bcc7,b412399d,5aecd635,7476a1fe,73554b21,42132026,f547ce69,66e6527d,370f9c0e,cc3344c9,be1047ef,b7422e13,6772bc5e) +,S(95b1b6c0,9d6ced22,f0a5cd,abc7a594,9d24cda4,f178a745,c718204b,1493db56,bc143681,53033d16,ac8ce5e9,a6cf240d,27b2331d,36cb23dd,f69009d4,b6adb01d) +,S(eaed529,1f94eae,183b2aa4,e17022f1,7b79a06e,d76b169d,2d6483a6,a6ede35d,53d5b276,91c5899c,ddac1efa,3ad6baf0,bc6e377c,99ac7f8f,9cb27fb9,5dd559b3) +,S(77c44fe5,9f6f448c,72eb485b,6ef3f7f3,906d690e,367beabe,ac7b7359,9c27d770,bcd75022,6c440f80,2ccf0360,4e3d6b38,e6629c9f,facc49d3,83322eeb,fb1102fd) +,S(e863b2e2,f00f0a46,b6752002,cd936a88,11b6279,5411944e,fadae5fb,e40a0306,b5c79ba3,6da29f17,5b9caff,631e8ead,c2ed937d,66008358,b36fe044,67243a5f) +,S(226f5bef,93df71d0,b2feedda,4ada3098,3883adb4,f3dcedec,2721e72e,eef8f191,a09204b5,2bcebc14,8a0a08fb,e8458618,f966078b,d75ccfbc,c13233ce,741707c3) +,S(aa222fb0,8c1e7c53,9f3a82b3,9c6f08de,c33beda4,aff7c46e,3a4e9036,83467184,41105cf,21cafd39,821a7c3a,a6dff931,c1653d29,906b5d4a,c5dd6431,7c1c3765) +,S(1879c2cd,26ef4ed3,dc5220bb,397b1501,f94482cc,1236da82,239754f1,e1fec96a,912d5f35,75eade14,91fb1609,21d63042,4fc68951,cb73d351,464725b7,71f0d67d) +,S(be39e4a4,e7eed88b,f4a4cfc5,970d4b7e,6df6211e,bdc2bb76,7657ecb1,ac9902e0,d8b1e4f1,13fca7b7,6306d9a2,b4cf5a49,32ae54a4,5615c899,f94cc476,7e27cb4e) +,S(1ea72fce,33378c3d,d0302836,86c3f8a8,1c2912e4,7cf44631,d3fbdc2f,752d5786,e20f4558,fe1cdd14,ccff261a,fe506b78,cb6e2b0,807fc7c9,aed0b888,3748906e) +,S(56e33dd,67bff57d,e8c638e6,f32bc396,2d0ca011,b74db242,3ac662fa,da036806,4039c0cc,165e7130,9317d4e,41de57bf,50d78ecf,3e14ee2e,fbb3b93f,c393f461) +,S(51981691,a5f1d8df,1cafb95a,738b9e3e,d036ce54,f10a7447,c470cc2,e80f37ad,9fb9047,67cf2a99,766e3c2,6e97f045,d03fabdc,471ef664,d31aa662,c5972261) +,S(ef3a5685,77a59783,6b0be7f9,5f927996,b90db9b9,bcd57f30,16ec5979,98869dbd,51cfe457,ff22749e,949782c6,ae1f4783,fabe1136,4bb524d3,72ac3d8c,1b7c5e07) +,S(efc652a1,8c3a85ba,7ee9e0b1,6e3c6433,79a66882,5304a22,523c060d,af196415,8a656c14,cef91af3,1db2498,f5987083,8fcee71c,98c09d49,737447b3,54f7b2a5) +,S(cdda1fef,f4d5ce2c,d9802198,389880f1,8adc7962,c04a95de,f07370a1,884bbf82,8bc26ffc,b0c9dc05,d799174b,4a478162,4417f9ab,8536235a,e55c9b0,49e31d6c) +,S(6f3f3985,3454aa13,134b76d7,77dedd40,d46bb6ac,dc3a3a3f,93886847,b96cf0d4,a8ba499c,4f7ca1b5,ae7e5192,ea408344,b6e32491,e5649637,e4eec5c9,2cadba81) +,S(e52d11af,91abfb83,ea842065,ed8d804f,a2a3627d,85149ee8,61cd5df,e54b13e4,59f3106d,619e5672,cb21f6ed,4e73285d,2e132a3d,e3ed069,46838e12,a9cfff97) +,S(89fec736,f0de1cb6,45654d13,b32b3cab,8fb91869,c30e2ac0,66c0cfa3,9e7256ff,f9417ae8,4d3e1a6d,b6ab938d,9dfde62d,73e708db,b58f5ded,46b455ae,7e44274) +,S(5140f56,91b10553,ce86ce73,3028749f,52261520,a8903f75,31bdac09,22b70b29,91a1962b,9e02c23d,61f4ce,21c0ce90,ad0acf0b,fa250afd,dd67d72,aa2dc024) +,S(3c16130,93109917,1075e199,ed32f94b,579d0eb,85520d09,c0f90fc8,7d267b5c,d50d04df,e4040db9,3aa0010,7693ac01,e9f156ca,5e800e51,a5f7d36b,78ac64c3) +,S(fd87690e,63a50237,258b8a58,f0240552,a2d0b952,911d1fa8,be13eeb8,9be8bac9,406dc1ac,ccb968f5,6fa3ee35,247fc66b,b40f04e3,ac512474,58e4fea4,8a1b9225) +,S(8e4d1de,17e78f3,2ce8ad59,973b328e,99005ce0,6a8d9347,ebc7434b,d5a12a81,c6348f3e,744ca65d,744c843b,4bbf0d8a,992bafa8,8c31b6da,42919401,b1a2a8ae) +,S(382c7bcf,ea62c678,173b6eeb,ba58caaa,f4988104,1677bff6,ea739a0c,6c7743c2,190a5da,77e64243,53df7f27,e01c0f6a,67e9addc,afcd3523,7cf5539a,69c5eb7d) +,S(f56a8b4e,74b84807,539fa471,bea50795,25b0dac9,53bf5d29,e87d90d5,f914ed0f,5456d908,da57e612,3ab881c6,56a6ea24,8f9f201c,13915a48,c50151fb,c096db8b) +,S(908e9ffb,6a5e557a,1fef8ea2,b16b80c4,3af3652c,55f12a04,4ecfdb06,17965b80,beae28c9,d1b14cf0,f8182ca0,7ebc3137,b9c01094,ed6b6043,562de75a,878bc2ce) +,S(a6c30718,433381fd,16b22533,52934eb,3fad254b,99d41e78,7ef2e6e9,bec86ef4,47a9a286,a60d5102,4d129e4,e0309c72,9e266f82,5ee26f4,4bf90057,623fb818) +,S(948bad86,6c3e2c26,2d46163,52be20ae,30a652e3,4a299361,4766eb7,98b3e903,30d30622,ad478a2b,99c43b0f,915de0c,4321da4a,76a18d33,95b4c484,d9c35164) +,S(d6c92b9d,cb8a17ae,77788125,18782d36,9d536edc,f96d2c9f,c9e84d2,1cf6a3f2,dd9367a0,93518377,97b0ae88,1aec5d4b,ed68624,8ec2207b,9e0104af,2ade09b9) +,S(f9ff3d7b,7d8b13d6,46c641ca,64df802f,7b082209,a75ae328,54cef0fd,65566abd,ac58619e,1f192095,f7223022,43d811d1,b9269d28,a18711c8,c84e5960,32deec98) +,S(3dae1dba,3a70e0f7,209a920,42837ed5,7f4e767f,834d14e1,c3528f6e,92c8d5e0,7ae2468d,6ea1f2cf,e1fcdfad,3ffe87c9,5c7816d9,ea99332e,8b4d055,280f797f) +,S(27045105,6fcc937c,9cbffe13,f2aaeb0,8ffd227e,dbbc7ca,d0b4b01d,19b49937,11766a54,6c24fdfe,220d3724,661d6c75,bbd801f5,c4286ad4,d08a698f,1ffa4397) +,S(f9d95ff9,6c3260ee,cd434075,262fba81,2024c556,649c4865,bb90d65e,20defd22,37cbf7fd,8f741411,80a632ba,8304730b,a89d54a,1a5b1dda,24b026bc,17a13c2d) +,S(4e8df18,f90f67e9,fed8c6d5,b66cfbe6,ff0a86bb,db990214,48ab57c5,c5a7021f,7e69f8c5,ccf56413,453a2b1b,3b34843c,e9badfd,3a83729e,a2293fda,f2a621f5) +,S(8c2a4d2a,710a7fb2,1ba78383,63c66458,ab3d32bd,3be35821,c8a5b779,d2ffef9f,d532447d,27e42583,46171515,71bd6577,f82d8f91,ff39d205,caa5aac6,365511d6) +,S(28b7f3a0,19749cce,6fc677af,a8fae72e,c10e811e,d4b04e19,63143cef,87654b75,30471eb,3245ab39,1597c881,e71f4a1d,e241ba31,a678fe39,2ced5a63,845ec782) +,S(cb474d0f,fcb8e72c,257b5acb,999ec8e,d89f6494,cf4b008,8f6d7db,59be416b,ff7cd0ce,fc293cbb,8ab9a950,8759f856,9a0e59b3,effc00c7,e3a3051a,a16622fd) +,S(c33e6982,5dbebc40,265567fb,ede2a4a5,1a180034,2e9963f5,bd57b35f,4055b36c,22ff3ff5,7ce265ce,253b5061,599ab066,82c0cb5,91a51e0d,b798ec2c,c546be18) +,S(84bfc106,44d37123,71a441a8,4444a9ab,257e0745,b3d0ca0e,ee341838,19bd2237,7cbe56d5,7b7af706,c33d13ff,b3e4197b,afd7f421,1ece235a,e1c2187b,289ef6f1) +,S(9c312ec,d53b244e,fd40f003,f7e0835f,6063028a,5b0488dc,73599653,f97b713a,11bcbd0f,58fc77ef,854d0b16,188b4e8,59698ba5,fee41fe6,165449b1,4eb76d14) +,S(5c06af59,a22d6f2d,cd83bf4,e3bbcf21,d474f61a,e01d0433,2d0a82dc,af15cefc,476b4be9,f1c265fd,a8c6d70d,6c669aaf,41fbefb1,425e2be2,9048165a,4322aae8) +,S(777259c6,81a3ed43,b4c0eb54,7af39ebb,f44f0d71,c84602bf,2d6d45b3,f1b6bdcf,8a9d3753,5481858a,eec44851,4c834667,d6742b9c,f563ebba,22fde187,fb3bf754) +,S(b370601e,ae1fc1d3,14e4328b,4d0244de,174b8c4,766283c,e3820c50,41266842,8ef05079,2174c3b0,752a9abc,8d980ee3,eda7b5aa,4042a932,3e136834,bc125ac0) +,S(5af94ac0,7f8d6492,a472ad40,67097206,78086856,b01528bf,8248ac8e,dafe6584,8a54c7ce,57eade51,1d5f32ef,cef5f56c,b40dffd7,8cfe5655,2a9c3966,e62daf1a) +,S(8fe3c19c,e2e87055,385d4f23,c5832f37,4f84b41f,3f945945,81acb8e7,5b0586c6,f923099f,d16f44c3,a4314d49,1af193c9,2f5e2017,156f860,674f5ac,91728c28) +,S(d580961d,2642bd1e,5f9211b3,9b65eb5,e531d17f,a37bafc3,b5f288fc,13ac9a71,500c0554,337e8b43,36e50a6e,b12fa86,d9678cdc,5b38f650,e4f897e9,1b4185cb) +,S(255dced,529fa623,49e46033,86790c32,234e7967,5a9c5405,256cfbf1,4295e82b,7eeda9c4,7f3e1c7b,fc3253d3,faa46317,5dfba7cd,94f6cb10,c8f4b4a,7d799815) +,S(4b7ebfa7,9240451f,ab8476a4,ae48e55f,1a27c338,a8dd8458,bdbe422,ec891557,dfb260e9,f89135f6,fca23cf,ad7ed812,c12622d7,e4285284,8c63d83e,85b59085) +,S(919d0780,9e6092a9,a3b5819e,c67719d,3119569d,3bd14c7d,8f47f555,1f2fbf28,ca0b6af8,76d70f2c,ecbb2fa5,98d6118,c204d3d4,a30e24fd,cbcaf283,843d8094) +,S(466867a9,de89a944,5bed73bd,5ae342a4,9b8c896b,59eebf42,393dd536,8c70ee78,2a5d9707,b3e15823,79bd6ca,e8b7ed09,9cbb9f93,4ee96029,385d5678,aae042e1) +,S(71274d08,c8784517,8921526f,cf71258d,fc740d5b,be655f3c,d985cba2,c1db3f7b,e0af0130,a5be1434,f8d652bc,4406177b,3cdd2ca6,6abe7f56,d848def,fb4b25e0) +,S(a46e9048,2d25241d,13c693c1,f4e6068f,b6e8256d,1fb3111f,a0ae0e37,7ebe2bcd,7567700c,f22aab69,8126e7ac,961726ad,fdd708bf,d5ae5111,40f921be,94474eda) +,S(f7590b91,f2431126,85873b0f,77be6851,10ea918b,a26d3b5b,aec02bfa,a5e84abd,481848f8,594c94d0,c9b5d6cc,858dcbc8,2d25128d,5ea28e2e,ded935a4,62708999) +,S(2d7aa8e5,1282e1da,dad39d64,398f0790,4f966edf,aff5e3bc,9a22edde,428c2edf,12b2133c,42032099,eb33ea24,f1755f9d,a30b4da1,708cdfb5,7635b61a,335d9d79) +,S(91313f40,3302c47f,d9f113a5,581f135c,82280927,5f658607,99550fa9,7236d0e3,f8b6ec07,5e7804bf,461daee9,1f21285b,aa9e96c2,2e0cff27,c323f627,90838a77) +,S(da248119,9d385a7,8468e86e,1b146fbd,e0be031c,3a19d0de,2f44a1d5,6c6743cb,c07c897b,5f76f6ee,f2c62511,19b37c47,5d418b6,1d2cda66,6d39ef67,40348cbf) +,S(95e36a24,b1bc2e3,fb5e6082,de9ed432,760ec3f9,6f01dbd6,2e820268,dde82220,be6990d7,3abc4a3a,8f03b209,dcb4202b,52a74a87,1e1e3d01,7665e9d1,1ce1cb63) +,S(8b05b060,3abd75b0,c57489e4,51f811e1,afe54a87,15045cdf,4888333f,3ebc6e8b,1d10f881,45db40fb,889e2ddc,e81bda7c,27f5b615,acd6179d,bb30f4fe,7f40fb39) +,S(ffabca3b,764a327a,750bbb2b,a1cb8329,d43ec0b7,dcd8f728,55a9d30c,f3020f54,93970a65,c90ba260,5b2fb393,a4e994b4,c791965c,b60a1302,cf585443,6dd0f05a) +,S(b3a85aef,ef35e4a9,8f0f05fa,9752998a,d3514868,b133e7c0,a18fb053,b866c72,30115ee1,c21e5b9e,3d6b36c,54e700e8,9ee78c7,e88a2752,38f46d1f,5b6005e2) +,S(a4f1fd5f,4c233cf2,b9c5659,f666d51,5b78fc32,ebcb52d5,d155250c,c95b7ab5,91d19f85,726a1258,444419fe,c5219c7,33754325,89d92d52,c363d61c,81faa0da) +,S(97b84b4b,7baecf13,94bf3923,2b58b5ed,9eb14637,d29b3c98,bec3e624,15bbb0a,497db4af,be8e7b3a,13493d73,ca92b4b6,5eac7f30,d86dbbf4,8b86f495,2d162435) +,S(2ceba0d9,203e3666,75e78e96,51b5b57b,a9de5f82,3cc8569a,1c274d19,946312b3,d4c2fe86,f042cfde,41f0a88a,6d77c188,b7805db6,c6ab440a,488a75c3,8ed2b437) +,S(ab8b1930,6cddaca2,8be57b78,b4836998,912391cb,c6a41e10,e91b86a2,3f2b7009,fdbbabdc,79fba1ae,9d8472a1,7a648206,7abed651,36892a03,35128650,374172cf) +,S(2763029,1ff140b1,57eac2b5,9938f26b,c149705d,aa2dacd,6bb0d305,1a15b394,c2b6b32a,38dcb9e2,f4640838,c52f5ca0,55359479,86ecd875,a91504ac,49374ee6) +,S(22d1edbd,1cc1514c,7f91d793,3e9dd124,9e703fd0,8c65ed8e,6cc79eb5,cafc78c5,7453e7ee,a53ae741,13024fca,208a3816,a93942c1,2378ff7a,5e65678,c0a85efe) +,S(70d869c0,c5729e94,270fa559,bdb3fb8,b6146527,5ecedb7c,c8e7c9bd,acffe222,4bfcad32,8ce78fb4,617baec8,48c2c3a8,f8d41474,9c131657,115dcbb8,f6ec30c5) +,S(a25b45de,da8d5782,64f60dcc,da97bd28,ed9039c0,6bb7c97d,d4a24eb8,791b1647,8fd84983,fd2ad956,32376555,4c43a664,f306f11e,2f030c7d,fe1c68d8,aa43db8f) +,S(823fcd6c,9b237c0d,5aa38336,6737066f,3e77fa14,b975a5cf,39ca8a74,3b1024b8,e691a44d,29a4f2dd,e99c2ba3,bb6d4345,d1433e4b,d1c65d72,198f16b5,4424dde0) +,S(39a5a63,8244aef6,432b1247,d394d305,ee594bd2,6ed2433a,3a96c4ce,5a158d0c,147ced39,dd8b8494,d57ff37e,a24cb108,ee4b9f43,50c556cd,32df8b27,626a8023) +,S(8f9febb5,b85bb2dc,74aaf7b,81b27796,2a18f6bb,4879f5f8,de94aa56,b3206487,48daebd6,d4175a00,e37dbde,c2b57b3d,dc7474f2,ab6a093c,32a86acb,f610c2a2) +,S(fc16fb14,2f500856,4509158a,5f6b4e35,6a08db53,36e9d3f8,4f7a03d,9143f60c,8e9326f2,7e02b73a,10f4ada5,9db85fe7,4027584e,6b32aeaf,58bcf804,dc1f7a36) +,S(7db09ea7,17446f6e,9115d8f0,987eba83,eaecae01,f4201d78,8e292486,85c5a281,b4469cee,426d455d,f736b2ad,c12fd17e,79fd2167,c5a6f864,2733e327,c215742b) +,S(d45d1bf5,28baef1c,3ea56fda,b1f8d3c6,3df7caf5,c7ecb0b8,564fbc94,a4d9a57e,91e26984,64830e89,1cd21d3d,81ad7000,ad0c84d1,3c05a191,c1a172e1,16aeccc1) +,S(3faf0b75,1b6f85d8,550a9ffa,a7a26bb1,6a4d9c99,ae97678a,d43a1a91,75f03901,8a5d062a,a4e43cf6,5caaa39f,6386311c,be775d14,3b8d9368,b555c436,1844328f) +,S(54608cbe,8e08a472,79cc5e39,c65e1ec0,1eefdba3,3b127107,4926b06c,76efd121,a7695bc,40def81e,48ec5f91,cc98fc2b,9bdc2776,71d05afd,cb789e20,c540f038) +,S(559a0d89,50dfcd4,364e6955,23b56972,e8a201da,4e196d46,64dd580d,909823ec,7bf8e1ad,d6528b90,cedb8d5c,87f5953,5641e32b,6a6a7b4,c46e8052,e8dc71c3) +,S(166e8858,b5e2b9f2,b1648c34,41e5fc28,bb305ea9,b0679c3,1a996b94,acf467a8,9c9b306d,ff82fb42,acd8a6b9,108f9994,dc1b7c37,dd0c9d2a,f64205a3,92e5bdb2) +,S(40e29e8c,e66e83ba,a94ec5fc,3ae438ee,e5284f94,c5efe5a8,d41da738,38fe1941,7c71f71c,c01e7868,1cf154e2,73a23d0b,84ae684f,b0e709c3,9551884d,39acb2dc) +,S(6829c8f9,eb659ce6,7f87fc76,4661b400,6aeb8b3d,bf674408,1a844380,72d4ee7e,1d35530a,8332dc05,54e82522,5e9ff30b,6647fcb3,304fd908,ea506945,ca22913b) +,S(4a3fe05a,322e9fd3,93393b13,6e2af237,e25a2540,42d10e7a,dbb35d41,13658196,859a41f8,d4527641,a93c40d9,ef0ab175,78dbdc45,7edeaea1,f147018c,7901574c) +,S(de5817c9,a675eb2e,c1567ad5,a6467cc2,9e9ddec2,177f382,25a5e101,9aa44f94,4050ebd0,5413311c,df17e625,6d4bf102,71b45e17,4c8fcaf5,ec36567a,ccf723a4) +,S(fde29245,4f07f411,abdf1a2,6f3d8f38,daa7b9b3,1b51fe7a,155da5eb,5662a108,f2a4e6d3,7b39e929,ccf347a3,ac6c719f,f09d1756,e407371e,d98dfdc8,b727808a) +,S(f28d3cfb,6eb98ba1,76fc536d,6a772861,b0917415,d08c22e8,a43a90ed,eff42436,4508be2b,b328250d,e043dc82,d8894aa4,1971fe8c,42e6cb61,e9d02735,1279d1bf) +,S(c7655c30,4d1cb5b7,d0992afb,119a6df7,7bb65496,420f78c7,117b951,7f16e3ee,898162c2,cdb730dc,4b986b1c,b0de79c1,a348b0be,51b9070d,2e427e2d,e71dfdfd) +,S(e57d97c7,2fa3ee66,71d67936,eb62f4d8,53ffba74,f2158d0d,7ae3eca0,7b4b147,b1e3012c,a3d92975,91ab80b9,a668ca13,1ed42660,47c34958,c07c720f,e1fecd64) +,S(eada8642,155025bf,c0644f92,fe014ccb,6cd3d37b,76686a44,bccdf8c1,e22b0c01,89035d7d,71d71bb0,117c37c5,540f82a5,7152aac,af79fe91,6aed7d2,9dfeeed) +,S(3e76267e,3f172198,9f9d7e8e,11fa3ff,62dbf8f9,77503e27,5567ec84,e60d0e25,a1835790,9631c6db,a453001e,c7e3973a,416f8bc7,e2fe0871,82d8a236,6e1160b6) +,S(a11d97d8,9c262f35,2279e69,2d66be7e,5de7235,18e6750c,6972d250,91a3df70,d18df8a3,dd6fb016,df2e48fe,ab889165,44768d77,e9488b71,fd31c6db,d52a69dd) +,S(b16e3f75,cefec06e,edd54bf8,dae7cc29,a5ab5208,fd58d787,922f4221,f5f20651,57fbfeb4,5a6e32ed,74fc1989,16909891,233ae90,c539b81d,a758761a,a6d0eb2d) +,S(37de800a,3f73ea6e,9eb564c2,faf147a4,837339b3,44a33d9f,beb25784,c74628b2,59339259,9da55803,196b7e5,3596aad7,e33fc96c,fa2ae0a0,2f41ec13,e6254050) +,S(6f68412d,65b3abaf,6fcb781d,8b6e6baa,4ae57fb0,9325a1f1,9beec438,cbd68055,884fd59d,406171bc,b31042,21abb5d,27dc1433,ef49af08,ec031543,2f8d3bcc) +,S(91479a9e,6bc5e261,24cf0ce2,b52f070f,980f1f9a,f60bca79,572902d6,cb6e4366,2304f70a,22de4435,99f6025d,4b13be27,47932524,112f64a,ef935d1a,9c2f5288) +,S(8ba6b0df,36ac8708,8c1e7c1e,4177dcc9,4cceabc7,4a6c7701,8358c194,4d19bd8a,e96b720e,e9d515e2,922abe8a,177ac755,989dff1b,789a85ba,ad17f859,62ab9ed4) +,S(ab152195,871f3fcc,58f59a8c,a35514f0,f11b8a69,54e4ec9e,f4aacb7b,29d567cc,c1ddad48,623ebda1,42899ad1,75f88ff1,965ec1ca,651b419c,3c8ea037,f267fb4) +,S(7eb23f48,5eb92bd0,1c0e6448,e19adc22,6ca45c46,d4d5c756,e552ce7d,551600c5,953e9b12,4278b939,7b3de07,63e30c55,a300ef97,a8587328,b3dc27b1,a7c57ac5) +,S(b23f4c2b,a4b2700d,c7520a92,25890491,e0212fb7,eeb1f0a,2f624905,4e21ee17,a38b6772,9e95b3af,2cc7d5a5,c10386c2,8a2c287e,6349f811,457ae0dd,8b598530) +,S(85d2e723,387e55b6,a2c1a02b,96cfad64,67a6c12e,b75424a9,a10ab77,2769c2a4,727abdac,223898af,1b077fb,7d481434,ded380cf,21a131d0,db845150,c8e4d303) +,S(27c1715e,8dba4cc,41383d59,e669f66a,54ae6901,88a794be,9fd8da6c,9b09273d,2767ca54,306e6ca2,3517d884,1e7f30f3,e16e22c5,cf2bbf9d,998d5cd4,b5b1a266) +,S(e70b8cd3,7f9a5e8e,9e0c4fb5,6f85a6ab,42cf4042,4080350f,4465cf8b,95eed89,2e3d8c58,94788d1b,9734e93c,7508cad,faacdf06,319854fe,5ec4b0fe,6b0cb31a) +,S(e23dd455,24058396,98c81a39,f3f076ae,93932c61,d0e7f65e,e2c2013c,dde956b9,6c53cbd0,cd8ab8,bf5cef85,a8aa0b64,52b57e79,cb53315b,1dcdb68b,78cf77c9) +,S(5e923f81,ccc49813,11a09f2c,47677cbb,9aaf0832,10c683b7,d7a07399,4d919c9f,604d7112,e64cad06,fe0e64a8,af7054fe,7059667c,fb5159f7,738b97f4,38306873) +,S(23d9f253,1a9fa278,e02b71d7,113fde6b,1380e543,633286a5,60037ab7,70142507,55dc8247,b6eb6119,a6ef49f6,bb4e85e3,a9fd201,4f696564,42d3372b,fe44a9ac) +,S(eeb23095,ad2551ef,15dbb281,5ef6d252,886c79ef,12c7318a,50f5d89,d73b8f24,d1a71b94,b9810ce8,eb0c3896,3e7cfb90,56a80f82,9d384eac,67d9859b,11ba1ddb) +,S(64a6de7b,57bdf7e2,54beee53,6efeea9b,899a8436,e421e3c0,e7a2682b,54b7c21b,6427936e,2e344fca,c402e2d,2902fc6f,181ad190,e3f0d537,cf40c96f,27b4755b) +,S(9e4676ce,d76daf3f,1c1aad5e,51e700b8,8207fd45,3cec846d,779f02c2,cc270073,5685c781,1d7610ad,4655019,5d8a844b,6ffc4acc,eb458c38,d9d74f4b,cbdc9e42) +,S(95ee1228,80616ba1,76e0ee23,14428c7b,1d83c635,6ec843e5,d134b359,8d645473,7be34c64,41914c07,58081278,9b254c71,49a60d13,c6ee3a3,7d12e29e,a3bf4054) +,S(e53ffeef,d92081a2,f91df506,6453cbdf,d0b1089a,c598da9,94fe4c03,fc6516ad,560f02c1,4a600d02,a2e70b28,5164618f,5dcd1b67,ac071929,ecc1d891,e4c59c2f) +,S(9c332744,7c75488c,5a14f1ad,fa3d9974,90d4eb10,63afba83,fafcc3ea,62a7b68,9cc759fb,9a7fc3b,4d0ff7d2,24d0cc54,cacfb78f,5590eb8a,d689b6ee,19791c63) +,S(e826d500,2f4315ff,77eb22b0,c8b74142,f1d1037e,81e3c58e,4ea39430,973d3c45,3a466e58,82f971f4,3ab9316d,fc7d53a4,e8ab16e0,2ae75045,5d8d1999,bc8b65de) +,S(ced4df0e,af93b5c4,8dcf196c,6e9b16bc,dd38a87b,3fd67512,d387b388,115e13c7,fe08a63b,dd149dca,b4f2c930,99cd11e1,8c754271,cedd597e,910462c6,d68b74cc) +,S(b056ed27,88ea50df,2b220182,df38f54f,8065e36d,e0218b27,ee8c5416,db7eb33d,97338375,cef457f9,ebe43aa8,28e9a1e9,8d514d9f,fc300a6b,a1a83db,dacf6409) +,S(35dec74,2fef94a,7fe60b66,7341f6a3,eba030c0,4061d156,1c9ee288,5b7830df,2599121,39c810fd,e7f02fe6,eb6e9221,3eb8be6e,4d8f045c,aba1f058,789b933e) +,S(3f23cc8,7733213f,d2e15cb9,942af44f,d7981e97,37c5af1f,81addfb,324e020b,24e8ccc0,d43ccde3,4e392455,dcdec100,7ffc0c61,3b5f865a,c8afc051,756e8f83) +,S(9ba96c76,c0805cea,d7ecfa43,dba0d86f,c8471dcc,8bce9bc1,e06537ef,2bbabbdb,c1a3c433,1c2ae04d,be2f8e3a,8f4f07b1,622d9820,607ee3ae,e93bc37,f943def3) +,S(83c1479a,31475395,8d510396,5decc9ce,1d414f41,610e2ac5,c2e887d7,d16a3815,84d4d018,1354cd46,e5f39bbf,f9fffbba,c123d470,d07c292c,7f085740,289b5b7a) +,S(bacf627a,2db11d88,76e6714a,2857db8,a8d7010d,7b349ab1,fcb5ebc7,efe0a32d,a287dc0d,a44c448e,68b777b7,36ace20a,14b3e586,bab7ef5e,d57ae303,e8c83129) +,S(73063c55,30129f79,10bfd2d3,a0147167,4ca888d1,d64d24d4,619c5cff,2776ebd7,d57941d9,44ccf8c8,2fab0362,b14986c5,9d71906c,d28584ea,368c4bb2,2c159489) +,S(4ee8632,df55ad15,12ba4ce3,4ddb403e,a3f51a12,d445a011,4d08a00b,3b5d6637,c29f7199,dc30275a,39ff01a3,19723d19,91687901,9616fd81,88ae6c45,92b14820) +,S(fc46e66d,68bbd41f,98b18a16,a23efaca,8f5345ef,b567786c,cd42afe7,d7e7ef28,8b42a2df,abf8849d,53daf38a,4a4c28b9,a0238231,ec8f69d5,d702715,a643c18a) +,S(a71ad1b0,158d7978,7eece5e,2a3907b1,3f9b6b98,aefc7963,8a9de618,b3a7dc13,6fd7e86d,a956c464,e538a864,41b9b7d2,ccfc5c44,e80a66ea,4375e6a1,ef08bf90) +,S(602185f3,6075a67f,d7bb9957,60c6af59,61a7c553,d51d828d,ed37768,f1860a3a,90d312db,706b835e,647a027c,e7a89d39,2265cd4,dd52e802,165c32bd,4d10b2e7) +,S(db0fca83,76729191,b9d9976d,37dc62c3,36b4437f,943706b5,98e7b7df,607a77ca,23dedd6e,8eba7ebf,e0c9f0b3,2a1802fa,78699a68,99507dd9,c4f4841d,82efc29d) +,S(6919b861,598d4f09,2a159909,fff8de82,237d0aec,339d884,7618f257,a4a54939,ab9f82fe,1fa53aa3,b85c0784,59a4e191,27a1a214,392c3ff2,f3e1cbdb,6f34b90c) +,S(f4a7d2e9,f1f51ba7,c3cf0a5d,5d53a00e,877ec118,f7e39e39,299efb2e,8074bd59,9f050900,6aa6ace2,da4e6380,cbce7983,c9bea9d,99741be0,ef17e190,2c5bd257) +,S(8a5d7210,d4f1de13,1091ae23,2fffbd96,3927317e,54197656,366b2e35,cd453da3,9aff8a1b,5b71da0e,a6558a05,a9306adb,4cae1004,e532aa98,abcd1644,b0580a54) +,S(99eb23dc,e40d7159,b0a0406b,ea04b87a,2c9195c2,2afd79f3,f938018e,b8e86f16,2ef21938,a66efdec,7ff6cdda,a79fcabf,b1f17fde,9e80db37,82b47d41,64ecff66) +,S(3bf16e84,1a874418,ac7f0d0b,9abbe781,611ad0e4,7acbe904,a99ba65a,b4d37472,1d3cb9f4,e1948cb2,fc88c0f1,9277437c,7d1138b2,8aef4355,1fb35121,a3e714ad) +,S(a3ef83ff,19eb394e,b87c133,36388a86,561811ce,5449895a,f459fe4f,e7f01f3e,1955dd39,8bdcaccb,cdc7ddb4,711d08e1,9d687ccd,d335a4a1,45d13a4,8178cbab) +,S(26816f51,da9b4e81,3edc024c,223dd6b4,5a344d11,f5ade552,63ef17e,70b1af90,46278e1d,1d6b67a3,57280967,6604e897,65611b7f,bbccfd30,22007a60,294aabd6) +,S(5e1653c0,d640f148,ecbb14f6,97940b0f,1d47dcc9,48e7525a,909e8444,afdea823,9b8b81ed,e7cb8350,ecfaef22,92bdc528,8e499cc9,6feedc86,cd45cccf,4cb092c7) +,S(1bd88e45,b0d3694,56348c23,125b12b6,b7725d6f,addc08f2,47fe9dbd,e828de9e,182939e3,2d8bddda,577dae7c,a1d40164,9f6faf68,69808fe2,98dba2f,b8a95db0) +,S(4b03fe06,3adc4e40,315e754f,bebb7802,b80ab716,d4201fbd,31b12c5f,d9b73c02,a864d0a8,e2c5e519,ced537,e03f98e3,a5623c31,ea4a430e,73e8cf54,1c80fb06) +,S(e20a515,7818f33,1ed927f1,3c46f3b5,2146860f,d085e68c,5884f1e8,45d35f61,a1c12002,1a79d6c5,89e466c4,3fd48f66,a924e9a0,a3d20db0,484875e6,96bcf328) +,S(e3d11a4e,74a1e1cd,8be24d87,4885e1eb,4aaecea1,c802ba73,2db925c7,f81e37be,8f1ec01e,a1ef01f,63806787,b8831d4b,22b0a9c,7b44bc7f,73d2a3b6,861645f5) +,S(8a233215,57fe0dbe,e8e92b58,4729b163,da71fa32,f400617,d1e2a081,3e37f6e5,af5e471a,bb8c7505,aea1ed74,bd7798cb,267dbd29,61c35f5e,93889056,aecce950) +,S(3bbc0a2c,b43f4683,151070e0,7015cd6,997e8dfa,99685cbe,99f9a76d,6851d20d,9a1dfb8e,a5f14c40,b9968e88,b6083111,fd140843,96fa8278,bb0e351b,7cb4f97d) +,S(8c04d781,82d83ab,e8c59dac,9407b4b9,65380f38,39431184,811e133c,911d1deb,4d74e493,fdb22316,fcf21340,88039534,c0a3e098,7198ceab,4a65260b,4c0a3db4) +,S(467d4d0e,72312cde,df0aa7c4,62e03644,1ed9f915,c9e7e8c8,5770a4d6,d6a0291f,83db40a9,d7c2954a,30c624dd,f11d565a,f117e240,183a81fc,d4dda1d,70a1ce32) +,S(b62f60cd,8e55a101,ab060186,941cbd52,ce73ae2d,e19ac195,d55f498c,45a3dc8c,59da48b0,7c6d2562,eeb0ed09,158cd20b,4a5108b8,fbf6fcef,428cc301,b314ef41) +,S(6b5d6210,f98aee61,df36d7f1,9546efe9,166e88cc,6492183,379dd1b5,cb9e681f,dbfb1748,59c88a6b,72c8afca,830045c2,7c6de87a,c47228e7,7f246e6f,698a0d76) +,S(88744d04,e8c7842b,68d2cc9e,f58977cc,548768db,48d3c286,49ecd0a,22300c97,84573aa5,1ea3c51e,5a0b0e42,5c7c90b6,149d2993,91085bc0,393f58a2,8fb9c38c) +,S(fdf2c824,40f2ac58,19b06571,216ad938,d0218ba5,be4d3d4b,ada80e32,c32e37c8,c2abc4b5,9e961ffb,e48c6f20,58602dfd,9172031a,740668b5,5876d5aa,5d538f74) +,S(4d2fdf64,1f5a883b,322273bd,24755405,15178ce0,9b4ce8a9,36648957,ef777559,22ba1d01,210bf31c,6421b89b,9b3217ae,b2837f01,c344a9fa,bed0b385,4aa41b11) +,S(ec7cbca1,e743bfdf,81af3fae,a5c64a8f,5a54dccc,2afca269,9c461592,34ddc633,9f193da9,ea76a050,7b528c05,e5a10107,dc5263e4,98fcee29,5f57816e,4eab0917) +,S(ff2fa113,771998b8,b801e779,3cb804c,da9cd6df,9d371943,c7fabbc0,44b44fdf,962127f2,1ac57670,c79d966d,b8cd6f0a,2aec6824,22cac54,23746bcb,2df6093c) +,S(6d1b406b,2dfb02d2,34ec21be,c436d6af,4fd418c9,29f175ee,6475e77a,8a57580,b8945668,9dba8e2f,8f0693d6,d3e7b602,f157efe5,38b9ef07,b2dea23d,bce895eb) +,S(4abb9dfb,449e00ce,46476727,dc6b96,317bff7,74c21df5,87bef539,644d7963,6086f2b7,c016c69b,2ad80786,89af1c98,4d6dbba8,7545d8e4,9c2c58bd,f86e6407) +,S(db0c51cc,634a4096,374b0b89,5584a3ca,2fb3bea4,fd0ee236,1f8db63a,650fcee6,7ec0bd2b,aea1ae18,4bd16fd3,97b0e64d,5d28257f,85836486,367fe33c,c5b6e6a0) +,S(ae6f6dbc,d0664919,a65e02ed,646de704,6996a382,21aa8e74,4954d22c,37fdb287,abe70eae,f8ae7404,11931379,19f9102e,c5bbb2f5,f5fefd1b,b76fb6a1,c3f2f201) +,S(aea3eb97,cb321f30,bd0771a9,17fc9770,f4079a62,fac51fda,63dd3ac8,d35f6227,b6a53416,398515c5,e9133704,a287dfdf,878145a1,4416b525,6f1ae46,6abf39b6) +,S(90c9f8cf,711d50de,81f7234e,f6c13af8,39355c06,3e891ded,51bca1c,852233be,8d95778,18f19649,f2d3d452,81358783,17d2c32e,aa01551e,d7fe21c8,3421bff5) +,S(5ad7ece5,ab1d0344,5528dcde,34b48efe,fd954aab,920260e2,1495ed8c,86d976e0,2df2163b,2c3c63ab,bef274e9,3c46b6e0,62ac9444,b17843d8,70014b03,f78569b0) +,S(b6ba2e2c,8e942ef0,16e6d3ee,13ab0cac,1d80ae7d,588a831c,b7844e8e,3bd28d9a,41180a35,f923dfdf,501b6b06,7d241160,a2ebf453,d00b501d,855ddb7,1bef7fb4) +,S(7e409bc9,bba26c7d,fabdd450,bc84589c,799555a9,c6809cc4,13a4c674,7ff5f37d,c518961,9cc5afe2,e41c4260,481d8fdf,6066b034,19fe030c,e423dee7,79c9e6b1) +,S(9486cd14,70b2f2ee,28d01060,6c7c4c22,2c72636a,a50b64e0,3008516e,de4f602c,886ccb8c,fc5b006f,df931785,f732367e,c062e9a3,49f452ce,aacfb74b,deb467af) +,S(e7c11d7b,c8b07205,1e1af6d8,745ba05e,94b1ee9c,5482d83b,1af9ed8b,21338d2a,fdc4d293,828cab1e,fc9062aa,192f35a9,6ad4683d,797fcf79,c74852de,d75c8e50) +,S(dfe627b,ff315cb0,1b1b67b2,776ded35,19b1a85d,8e802217,c98000f0,93d50bcd,993c55eb,bb65087b,6fdd2480,c9d68e6b,ea0f69d1,b95efc28,5f32a8ee,55f9e773) +,S(a0f0cc17,fd00a2cb,5fc7c981,860f02f3,eb31c26d,54841dde,189eac65,a446f516,bfae884c,31d58a3b,f11077a7,d3edd37e,14bbe4ea,bd30fa74,ff5c295e,4b3b8969) +,S(c1dbb6bc,bc14286e,d6eef926,c946b52b,d37e7f53,91fe1dd9,dd7b72ba,a3e37338,67b02d87,bd336882,f4b6fdc8,1a3a88d7,aa5e2735,139e1bd0,e793e6a6,12df7bf0) +,S(20b9065b,c7d495c4,c92cb1f0,6eedf5ef,7025511,10343eeb,e82505bc,bb4bebb2,a6987316,5c231d92,bd21a4f9,60242d9,4fec71a7,4b24fbd,55d51246,10756835) +,S(7bedfdd0,c4d8c38,dda9544d,38fc8786,55ab9a66,4f8dbf2e,3c54810,eda69190,9994f0b0,d4f52de,6ead0049,172fee05,365556e5,3c9d4591,690715a6,f8af77a1) +,S(26762eaf,997983f6,ac63815b,720e97cb,5adcadd2,419d368,df4d385e,6b5918e0,81f5448c,e11b4dbc,24be2e9,be5620fd,25f0c0d6,76aae549,2792188d,5a27e6be) +,S(7148cae7,ee18e152,c3354ff5,dda86e13,ea14f175,92d0d3d9,a944f4cd,f387bb79,bdb35fa,f6f86e90,41465efe,dac6bc0e,ffd42a0b,eee83365,93dcb4de,2310b18d) +,S(27e45dd9,33ae2c33,a69ae00b,156845b5,bb4d8f06,593c2723,5038ce17,87bdbc90,a2bce33f,a93579a3,f8d9c75c,9271a2b1,7dfe36a0,590f2db1,f7cabfd6,51801ac9) +,S(af0c7215,5b28ae8c,2a2d79c7,d81e603c,d0539c11,fed2aa98,fa2bf2b2,d7d6b7d7,ae6bdfcf,a25109c0,b80bfc,71cfac3e,3b5b26e1,e4f2c856,40d2c69a,a7ac41f9) +,S(7b63c408,d4f8d34f,314e609b,21e8e372,de0f5a19,1accabec,7ecfe02a,63cbabf4,aa08009a,78fa96bd,8ceb6f65,e88859b6,236925d0,bccba7c,e0db29be,94f16327) +,S(ab768853,14f3e88b,d63cffb6,8f4f3232,4f4b4801,41aeff93,94d58f9b,85c6890c,1a71c833,d6d96bc1,c33610e,d7ff1481,c9ff9841,157c59e0,5d3a036f,cf7f192c) +,S(641437f0,7ad75112,f8375487,cc0ed859,ccd308da,4b25e5a9,953e351c,9e3bd32e,3c58f7e6,b255346a,f50f1ba8,3f008662,7d2ec950,84c326b0,ccc3a388,81a9b131) +,S(f532fd2d,600ed478,c1dcceaf,fd01ae87,60f7ecc8,7739580b,9b4a7649,67a082cb,7393514c,71117d7f,7cdbb4d6,c803c9f3,221847ad,697c945e,440bcfa1,1285fa96) +,S(33679959,2c4bab44,d89e6f60,8f882938,db73dbd4,75f0dc3e,e1fbd075,b1c7631,4c1f8004,6fe89cf3,6d078251,5ff57cde,17125d62,5cf675aa,1fd70163,49694fbc) +,S(e184c16d,2a34ca57,b06b5396,28cac285,6f18b7e0,32970a5b,e2221a07,be56c9cb,7dffc17b,51ef9c4d,f16ab068,8c66df57,d646cd34,46b509c7,7f85e03d,e2c5bf73) +,S(f8a97d16,2fb49d55,bb8de990,9a20c1e3,4f6ce6d6,efb8c68f,e26c884,6415d419,3cd3cc53,4dbfca01,9492e161,61f2e86c,5f20a83a,866874a7,ded4c2e4,42061146) +,S(9eb6aa2f,6c6ad821,d46dd612,38d3dc53,28cfc406,ad71964e,b32de0ea,12475781,14501a83,79d32e09,3664ce34,1a419b28,53dfe3e8,fbc204af,cb33e347,6c45c299) +,S(ebc2566a,68e68955,e1e42740,323fd401,7c40315f,ceba7642,5f887a3c,3602668a,75e8109,96ff56e2,655f9f85,ae9ec9a5,fab3bdc1,42ef063a,a06931e4,d4c7ea39) +,S(55646438,a966ce5c,2fa5b29d,ba4840cb,ab9c5e4b,4074a9fd,674b7eb2,f52bc392,34631a86,76034a89,cfc3f68a,45cd52e6,2987d80c,e9a8e28f,a37020f,981c8d66) +,S(b87f8309,befb81f3,313257b8,c7dd914c,82eff5d,6729f2e7,eceb0337,7b16575e,bea66832,59e4002d,6fbdf03f,b80bec1b,eda5e1c2,cc55c630,deedb7fd,a96ea298) +,S(45957585,b440d63e,acf598b2,16e435e,24b6cf15,2892c8d7,953471df,70a0a93e,f4cb6260,776a4f4f,23ac366a,565067fb,b29739fc,233493a0,b3eaaea,c4b71427) +,S(2c90f64c,8d9c42bb,1824c821,8f9cc057,3fe8d648,b960ee11,1487bbe4,9648ca87,829bcd57,ac643cb6,9f000a9,f22fcf57,69bebf7d,6b1d8ab4,da21be41,d3c25a8a) +,S(eb327ac7,779c9abd,9546a3fc,c7731729,2e753912,2ba9f7be,36db0548,144b5efe,21ac2c85,8d2fcd38,8c32c594,d6e74803,2cbbf365,238565a0,8c439190,518c9435) +,S(95ec7e2,272ccca7,aa2b07fd,d37d0f08,daa35418,1ae30e6a,dba0cb1d,2c6d007d,733786ef,59df6050,552ac913,c10b8890,2d589e7e,4f740f33,75f78fc5,2ba12b5f) +,S(4b7a6698,8b4fa080,f27c845d,7ff17371,91104a29,2c6195f5,fc367c69,fc0cbbd1,8b043bd0,a3605612,87b38fd4,d99b29e4,8e52494d,8241207c,9284581b,63a85b8f) +,S(89b2c58b,6df581df,352ab28a,619027c0,a9233792,fc6525e7,47dabd31,a342e6b2,300b1b34,d459e65b,e265edea,203b74bc,9b053feb,888e81f4,10045a10,d7f53a2b) +,S(41099b5d,9f6a28d0,e4a7ff0d,b75ec487,1c926327,89f0c800,7ada8114,9bc43cf1,2a0aba96,2e2b92b6,e949253b,e06588d1,3e07919a,3594b26,995c15b7,7a62cae5) +,S(bcb0fc64,9ec508c3,507396b2,9a663555,8825ea9c,1389c817,af7fd616,7229be4,168c7992,cf3bf5ab,6c3a12ae,922e647,13664db9,2d7bfe5c,2c746933,3a07d606) +,S(7a3fc958,5a6256af,9e314ca7,55a8096,9cd3b3d6,f70349a5,ec719cd,ed8b762e,fe8b280c,29cd0716,35288526,b7179d90,dadecd78,39128f22,17d445cb,d3df1346) +,S(377093e1,b968a35e,e0f229e1,e656da9a,7583c1e6,d06fe1,c89e3f06,61a8176a,e7c0860d,f6c9f461,7e6a4dd0,b8c0d800,6aba8e88,20aeb82,e6e0f8a0,c2c601d9) +,S(c5eff3da,f4a5cccc,f1f27a93,361301a1,919b1dd2,5a98ea37,5ba63f14,4aafecd8,34e11634,26f983dc,18c8a02c,2222c2fd,a94129f,4e3f1d67,f424964c,dcb97a1c) +,S(fc7919,eb560d33,44230f72,8368afcb,3c0fa6f1,52ec54e9,84ea9ccb,e63ffb27,1c7d3782,af766ff9,bc1cb838,8e4a7dd0,a04bc4ce,adad3fa9,4132842a,9859a6b3) +,S(2783d7fb,49386d72,54434cd6,edc6f46f,b3118322,43d381c6,287d8b24,861ca5bd,a3aaf271,b806ac0,e3c45026,28f89c79,e4c68574,81710111,a9316e38,c7fa5c70) +,S(7407a478,68d4e0d0,f77f8949,5e5bbf95,88f54a6f,fd35d229,8ad4b30f,2d8a02f3,1250d562,96e18cb0,aa5b393e,a933297e,debf3d94,59ac1ce,24cc11b5,87fdec9c) +,S(c2d2cce3,2940ecda,77ac108e,71544079,c5899fa4,f8461df6,cc54dd21,a861a321,f0beb1fa,40bb8071,928b05e3,ecc8545b,a919eaa,e830db72,9e6de85b,f05a1909) +,S(8cf6b189,c5051956,6a94e1f5,ab5e9530,d459a935,4cb13eb,349fac3e,ade8a56f,bf6a9d70,32e52886,ce6d06d3,b22d47a8,327aa788,1621338c,1606054b,cfcf9c96) +,S(d6b07586,ed9f8471,f96ac5f,53277d1e,2deeaa2b,7fd09d51,ece6766d,29a94968,dab4d4b4,ce5655cf,4aef0d0d,5e3fa292,2f79041e,d2e23c9c,75be83f5,f09c5326) +,S(a720b733,60a03edf,f33a0921,90795834,9e661d33,44da800f,b857227b,ef4d647,1f213c6a,721c2d82,6037e3a5,a0f4867f,13d79dff,bd44f5b1,1bb5c0b5,4884ea63) +,S(558a5698,3b03c6c6,717db862,e59dd075,b39f9ba2,ca01b0ac,95a69cec,1f3a5ff7,7e07c752,db21b68b,21738458,a045fc60,fe3ae869,887fd557,92ecc6c7,d4ffb1a4) +,S(b927dfc3,278668a8,c5329941,f11d0e0a,a7c2d9d7,f8d6c263,9cab5550,10e14ca4,3ce83725,9954227b,b420e35f,77705225,72f4d854,999af552,4b8795c2,b723388c) +,S(2b70d499,1085aff5,88e23ed5,ab55a374,3c35f71c,651c0165,13f98ecc,4f0e6da8,df413b4d,aa1e5c9c,a2398469,7d82762f,a701413,1cae25cd,1a94f02a,506a8b06) +,S(b8836cdc,f72f6e00,99a9181d,94ba77e9,e1368e54,a62e1144,1421f2ef,2daefe56,9ad452f,e31f26d,cad7014b,68ceaeb6,1beecaa,5394c359,afa52209,e50427ce) +,S(252b297b,65f0e23a,9788b48,f33809a,ec66b445,cd90db00,4dcb6a80,c4af93a7,8cc4e0c4,3c346211,736a3bcf,c24a58bf,9f77c7d0,c53350e8,6cb0de2,60916838) +,S(9f3d30ca,1c15cc66,8efd5fa0,3d6b7bdb,d34f2704,cd729892,aad9171c,debe6330,448b6434,f95ee7a,bc056974,f1476171,353d9b98,4e22057b,38662327,b23211b0) +,S(8225e16e,5bc9add9,ff8a771b,5bc7bce1,653b545e,4e54b9e6,6e2d2985,2fd35e49,6254c5e3,aaefa2c1,6ca4e1de,87f09d94,df836b9d,d100612c,7174aa75,7d0570fa) +,S(a7dcee48,4d6e99b1,b72a80b7,4de255b1,74f4767,4d3c5799,4669ca6,aad02ebe,1d6f0cfa,76a2901f,4861f541,bef7a948,78586178,8c7c42a4,2db583ba,1c170a7) +,S(a91445a8,5d4ce90e,98d3372f,d2c664b8,9a6baa2b,a396fd1e,210e315e,809976b4,adc4c68b,99b072b1,b51d1512,cd94007c,72deacf9,4e7c5449,f1d83876,78b1d100) +,S(7462c9ce,907c9655,75c41096,3510e00f,6f6ea6ad,886cdc79,ed685ee2,2a7ce1f1,4ffa41c5,f02c2299,c42df5ac,79cc4140,47a854e8,332f8f2a,e67ffa20,bb71c7df) +,S(d42895f3,dea495bd,af8e36bb,6a79f234,dfd85544,965a8506,5d38ca86,28cd4f40,2fedeb18,13a11c42,e7869b20,7bb8486c,9821710f,a80d9bbd,59c92ae0,b50d9cf5) +,S(ef9b6b62,ce85588a,f13434b2,7ef53d12,e8291e6d,76a65669,c29d99a1,4d388adc,159f5fcf,9af0c962,ed907955,e91407ca,80261255,45b6938a,db18aee0,9bbf912f) +,S(4003f1a0,bc659daf,df5fb7bd,bc96afff,92877c7e,2a693f39,2afb06c8,fdbc9f32,ebffcaa1,7458f0b0,28ff1dcb,74be865d,7faeafa6,c2707cc9,ea685eb5,7961fef9) +,S(4390488b,6b03d851,648786f0,7486f5cd,aa9ae1d9,4867de,bed7036a,6ac09a8b,78329de5,a10db56b,1c35b88a,21ddf393,452001c1,29a0bd6e,5dd76a63,8682f07a) +,S(ae6da0d2,9ea3fb66,b6006c56,5dff8b41,6074ee16,f9ff86b9,60574067,abfadea1,74caaa63,dd012a63,e76f0916,f370614e,29e3f01e,b88b49c0,5ca33234,c11cdac7) +,S(78f879df,98614721,b7c65652,4e806f6,35942e0,950514fb,2fad8a86,bbcfc72a,4194e58c,478ae80b,fab661b7,ffb85cc7,2318aad3,88d61c68,a755296d,38281ac6) +,S(80684365,4f41b0c6,d6cd51cc,838355dc,352e028,7b1c1d1d,edd2f96e,5fc97e01,8b0c223f,13c61b4e,1e141a0d,35f29a7,7546bc7e,3b1e5c45,262033eb,92b62461) +,S(29509d9f,4fd975f4,6efd1169,1d97d0aa,f83b5934,9c797010,5cb3908,4b87466c,1477eb75,69a0af96,c9cfccbe,135e3c86,a33de585,2ead38da,8a0eba6a,6d0da81) +,S(191d1443,3d9c722f,c1532fe,33c63a9c,4e3d5741,6ebf799c,2d8ffeae,df1dfa4a,5bd00409,b6b571ad,745ecf83,91884eed,a5e3c7b6,65ec9706,eae4b4dd,703a2cd8) +,S(2843d42e,d2738168,5eed65af,eea82bf2,a50f0e1e,3db201e1,ebf8b159,1b0155a8,807cea66,c0fa9f53,b47e6e2c,232cb225,77edafb8,99a6f98,ff87d091,bbc63ee8) +,S(5568b7b0,9656d36f,5347514b,8e8212f8,e96ed02d,4516c018,4d653bd1,2bc8f644,5b44eba6,54836dc4,3fbda30d,7ea36406,4960459b,a4128911,8b2d8638,50e60d87) +,S(5b84e0e0,809e44a6,9e8d81ab,595b32f8,4a7dd421,c073fd6e,5f38ec5e,b145e803,1a65a2d0,fa791ced,72fa1522,ae431155,7e6109c0,fe934243,cb43d3ef,ad13d398) +,S(ef42c56b,dd241071,47e1519a,18d32910,675ba842,92cc8ede,900a64ea,bedfa672,70151747,33359977,611e0c25,4addbf6b,26a618a2,31474e3c,c9e7bcd3,8bff75ac) +,S(48dff2dc,b50d4a56,4be6ed00,3b1415b4,adc653ec,87eae94c,92b7c129,5656eb63,a9be97fb,53afecf6,f0bc59e9,8bfad542,fa29e63d,f701f6b5,f0f66081,8d643928) +,S(672e90f5,246ad174,10c9a2df,c22b098c,d1923bd4,19fdc337,f881e4fb,4ae938d4,73fd3380,1fda6db6,883e678b,31e5f09e,f331b624,5617ec9e,97aa28b9,a1781f19) +,S(59cdb9b1,71fb819c,909e094a,95dc15de,dbfe5521,4a7cb723,ee7cb761,4b315cde,55ac212,670b4a63,ecf3f8e7,3082009d,7f2d38d9,e490ca18,7a6db01b,e88351a2) +,S(5ce38d0a,357a58ab,134db879,b99994c4,6b827f82,8e1ce8f0,43108de,ad2ddb18,5e816202,d9c1d86e,c2f84b33,ddbcc51b,94748c67,1089c9c1,745a14d9,3b97f716) +,S(c982196a,7466fbbb,b0e27a94,b6af926,c1a74d5a,d07128c8,2824a11b,5398afda,7a91f9ea,e64438af,b9ce6448,a1c133db,2d8fb925,4e4546b6,f001637d,50901f55) +,S(383cb084,79e61bec,dca81d29,7796d00f,60bfbcc2,83c83d31,725f5329,839ec818,26e26fa2,49008933,8bde0498,15f34d9,bbc3426,ad8644ff,db1a190f,79dec91e) +,S(e6945731,2ad4f14a,71917b52,448011df,28b88c9b,6ac84550,248ca4fd,102f612b,30ed2082,51fc701e,5ebeddda,c9eb0b44,c81cc86f,9706889c,1b1c3386,4d76f53d) +,S(2d0071ba,8f799352,212168d2,1262a729,250809f2,24fa58a7,f283c4c6,bb9976f7,cdca6901,4e4873f1,16945544,800079d,ccc118f1,a5709b03,c872b017,5f1dfd8b) +,S(c82b9457,65b74fbf,91cf35f0,679877b9,da35c2b0,8437d789,8d748f22,1e927e,2e504dd1,bb06bfc,7e057dae,9aac8f07,b57a0f2a,39b1533d,9313d9f2,45fd6a2) +,S(9c1a4c2e,7eac21a9,fed135ed,e737dd19,2654eda2,fb3a4227,63cb6328,75bbf0a6,f3134b6c,12f71c58,226c9128,9b7b5a06,80eb6646,e9da2e60,20008efe,10e9197f) +,S(971d3d9b,bc693222,ed8bd645,e80b7cf9,9b170a36,2d57753b,6a2ac27e,8d6c9b28,d63d49a,e296dc2c,6c4a73af,7fc5e86d,e2039433,f3858980,80a36f7f,91d22e2e) +,S(f5493a73,86bc1151,23f29f2a,e0619e05,660a317a,bf5ac23f,a67802fa,8b917d2b,e6adbccb,8b780530,2b305157,b427b8b5,38455e02,80133e23,9642306e,65e0f1d7) +,S(831f123e,500e0a0a,e497a321,5ebde2ad,763244fd,40b386da,dde05537,99b6e014,35616862,2ff588ac,552fa508,ceef5ef9,ac04af0a,9db6b950,f44719c8,cb221d6c) +,S(9b8ce06d,18474c,3394c334,2ef385d6,7a338c22,98e92975,f1728c87,897cfb35,76940220,b8dc3e04,7cda50c4,c1f53747,7ce8757,a4a3731d,712cea05,42a40d72) +,S(f70a916b,a98d2bc9,60172410,42bf3e14,14f06cad,dffa7cb,1e8137a7,3fc5b855,f1b8c1f4,70d7896f,f39a3fbe,997a88f3,b5426d84,43c4023f,ff98fffb,280275a2) +,S(2d2ab7bb,c44e7fe4,ab283661,2f621d05,a64d6909,fd94e599,92d52afa,c0763d56,53450e00,2a6991c8,68fe044e,8b61c2d3,c5fec8af,3605728a,e57d32cc,c55328e8) +,S(8178deb8,c516978c,ea25423f,3a913157,321ff7c5,5d8058f3,dc9b9d46,f4d10005,16a71b2b,46c9d303,f4ae6a28,235249bd,f9c00146,a26b6bfb,5983bfb6,8d6ff4bc) +,S(b470bb76,d012ba7e,67ef9c32,add59940,1f700ca,16003011,26e6a692,36b56a64,f7d093e0,7d179861,871f36f1,58d26987,1775f082,a2fbfa69,27cc2272,614fd95c) +,S(164f2aba,837cac12,19b48eb3,30f02141,d3a89921,1cdb3f78,fe17133f,e2de29ce,3bc05608,95f7d034,4a9ec79e,b821c06,c2837c50,66925197,2c076374,871d28dc) +,S(c77a3a04,2a86d2e,f512268b,35cbb89d,e6a210ea,883e2283,ec59fec5,12e725f4,2ebcd56b,edf1a07f,e587e592,8ed602b3,6d1db4e2,5b49e914,8dc9eedc,131cfba2) +,S(e2defa1,f714cf71,557a3f1d,71c6abe8,11df5504,da38e9f,60ad335,3b11349,5ed64303,7a53bbc7,2f41912e,a657d944,c5107da,3b9ab54b,d88b545a,e045f229) +,S(ee2d12cb,b4cd9e4c,163fca1e,5a4330af,35280ce9,cc1d57b2,d2112443,b6313e3c,e28b1dc1,a4377c65,838ecafa,bfffb696,4057fb6f,dd4baf08,49f9223a,ee153262) +,S(4392c96,73e6deca,f3c39c34,f5be7c3d,a2109b45,2271467c,78f256d6,7d9e28bd,7fe4c183,ae352533,b049edd5,343a6bb4,747d63d7,d0f406fd,5902a886,110c50) +,S(5972e2ee,167374ad,93a655f1,6008f49a,f5586b2,f81197a6,712ff31c,a0d6960,6d70fc55,d0a57c74,8a2e485a,d903bd72,779343fc,7c090847,bc3dec1a,6f7c71d6) +,S(a85d800a,fc2c0315,889f1faa,29e5766a,a180f31a,400e3c19,85589af0,302c78aa,7429d7f4,903c76a0,a4437b41,92865b7a,ff5aca7a,52dd32a5,8f8b00d,b97ec85e) +,S(c9836b0f,d594fcc,9a89e85d,7d506386,ccee3f38,72759cdc,9548569f,a5f1c245,9f0b1926,93b7fed6,2f1a7d06,fd716727,bed1c2c5,9bb431ed,943b7082,d094fac0) +,S(ca2184fd,b2b84654,d0348e5f,6d963193,75f2c441,ae64622b,588cae7f,d202bd3b,d1222820,3d657aac,493dbb2,fe9729b9,e2563cc8,1f671626,83c4f0fd,726a4de9) +,S(9e0515c6,3c9b9664,8762704,80605d56,b5dea0cc,c7a4c66b,44a2e605,36941fd8,29de3224,a2eb2d86,afef789e,2dba061e,95f0b762,117ede50,6d4b21c9,54be831a) +,S(42042c25,33203a7f,c22cf7a,fa5f3d79,8a3447da,51d66f60,53a8f000,af5dc1cc,f075a67a,6d6c3872,315420d5,bc13d13,c54f3c1d,61f73d9d,ff688da7,f5e6c05a) +,S(6645a1bd,4eab02be,45884ffd,288955fe,3f5a68ca,9ae431bc,cd3967c7,12295b96,231f79f9,1d264ed4,28ed4b30,f0e92c4c,bf902865,24050b67,30cccd12,bb84cfad) +,S(4e281e6d,2c316e88,231482ab,1587de37,6898f3a8,870f475f,30bd1ee8,d4b32f9f,f48cadc8,1c119edd,e5f78062,cddc3416,c3f708d5,f37e4910,11ef7581,c065602c) +,S(77236414,96e41e9f,f13c7ffb,c7a66203,4fbaf7d3,e432ce02,fcd54ef0,4aaf5136,222e0174,d6cc01e,fb4b4bf2,9000f697,98bec7a0,657f328c,80083074,242dd218) +,S(3f2b704c,8d467859,ada6070c,b1221840,18ab009f,f7b5ebdd,af31d977,dd45860,a47167a5,146aeb5e,8c38dd21,5d61a2df,e2b6ab8b,fd05ea9f,b59faea8,3b4dd88a) +,S(794ec130,d243d811,2ebb70f0,35f983db,84b3acd0,ae4c5b51,640b9e92,ecad4146,49edecbe,95c25c7c,eeca537f,937aef5c,658f2dd5,d8c0de71,c80777aa,d07be84d) +,S(750ff9ec,e363e12b,79b04cf4,a7b1c0b1,49c554cd,6e9fe3ba,808c9614,95a96aa3,5a28269d,d1c5a3ed,f4e70f14,9a060b42,2d4474bf,1e4c6dfb,2de1502f,7a1d59a9) +,S(d65c05d7,df6668ec,b96c9ec1,fd129462,263b082c,9b7f4169,6335fb2a,b66272cf,d7728c5,1c10d42d,242cc5b7,fc77fdad,7e48be06,6b2f7c37,891ba4f0,de188839) +,S(d3804cb6,8cb2e9e9,b5b9d270,db9a172,aecffe39,72a3d2f6,1225bef5,595190c3,11fb350c,9568648b,30af4131,46053428,2879023e,6ff0d613,482c2471,b5e30d5e) +,S(f762de26,35c6349f,6cc95c61,9ecf8d37,6a938241,1106db3d,b34e9e84,70351bf6,dc67f9e0,c8a50f9e,e753352d,a99112e4,ccfe22d,3e88410b,1cadc0ba,bc9fd814) +,S(ab18b4bf,a827ff4b,17ee163e,d17e3927,d7dcecee,ba87ce7d,ac5e5767,53b87a71,6db83bd5,73e40926,c882bc09,c4d07f72,29706d1c,d2b1ded5,b57ea9b9,2b0da919) +,S(f754c248,fec21ae8,da343a87,459622a,b12478ad,8f63ad8b,f5c66695,198c95e,2cfb76a2,5f9c8776,b848d8e6,7f544774,e056ca7a,32bbae52,93b27992,6052b1bd) +,S(f8d9473e,3c3f0798,f893ecdb,716bca16,103516c3,1341c8e2,c2462d4f,9a46c51c,55f0065f,a422fe3a,26b314da,c7547835,5c53bd3b,e11b4686,9e05be92,d7924e02) +,S(8639942,d294cbc8,9a75fa22,5b29fda9,2f7c5ab4,bfbc4395,352d050d,754eb741,3913ea2a,445de35a,36d79abd,db3ed7cf,741da883,4cb81c29,6779d8a9,e7d11e4d) +,S(2a588c6c,706a388b,f1719230,b5a71b4a,6857282,83aec61,794d1a78,bb95d5c2,af25ed77,1a56f6d9,6407f07e,a7e11f97,61f458ae,bfbb1b00,96842c4b,6cc47592) +,S(137bd1ee,8931b9a8,b26dba4a,d9a5edb0,a5459717,22e1fe3,37ee8d54,20a0cb6,cde9fb7b,8197c77d,273e9f0e,e87d76a1,eaea105e,21154b7f,f5848a30,17ceea62) +,S(46580345,c686313c,97152371,13af2c2a,f9b5fea,a1e0bb48,d21bbe32,d2de2bbf,75bc5d7b,b6329ffb,2bfbc79d,b66f4c2a,7edade8e,a16bb051,a3a8ee9,ec92ef64) +,S(4a149252,afaf5acd,982fc491,c26af545,2ef93d6,1b282a85,56d5ef64,391bd233,fbc100bc,10b55b60,96656ea3,863f5d4b,bb9e83fc,5a25e59a,3b53d78f,60cfe285) +,S(894ebda6,fc0bcf8f,e025131,3475d64b,95bdf089,7d9753fa,ad0f92f2,15a01f18,b25f3150,155772cf,c69d0251,97d37ae0,1e4ede02,e2994377,9fae26cf,5f5ec056) +,S(e70d8a93,a91b891,30def1b2,bfd720ac,2deedca6,f9e5a85a,3c2a6eae,45eb032e,bd3aee53,a3b0ff6b,45fb96ea,4911cb02,fe102681,878a1b0a,693fdf11,4cd9f9cc) +,S(7cb1fdc3,e5ccb1dd,4a16765b,f9546b6a,1189883f,135d447e,eb245fbb,4eb36852,90ee0d6,becf5ff7,b80edbe1,75dbf258,d1889008,a8fed4e0,1968e2ca,8fca582d) +,S(51278ad8,331da17,8fc90a21,432b9058,b39f57d0,31210685,bf9bfbc2,87b9bfc1,5aaf547,f39ce678,8c3ad759,b25db3e5,7bc1f9e1,f75bc040,536ddce9,d3a66b6f) +,S(f0eeaa1f,c52b3068,48b56308,22fcf0f3,fee5008,54e276ce,e9fe9904,54f59cdd,90ff89b,944d70bd,eb5e5e01,6afbeb30,d51cf7fa,81182e63,ab3bb2c9,f2124f02) +,S(36fc9bf2,8bf90fd2,613670c2,27b51c00,df5b4caf,252b5df6,648ddb6b,3aeea71,887c3876,f1ac6c28,606c8161,4ddbbbe7,d975ecc6,9fd9075e,13d0e3a3,e7836683) +,S(6e31e26b,cf9436c,e14d2d04,1c29f62e,135c549d,5e65bacd,ba4df2a8,9b91b68b,2389684,9e63bdaa,b5f7d9f7,7a778b31,94c1af87,d09a9a85,85896188,acd0e7ae) +,S(c5044649,a4b1c2bf,ac6a8a1c,ead33aa0,3da9fad8,4f742eac,35792672,1d98e864,6d2602d2,83e696bb,717d1327,cc1143b,45fdc84d,b8d400af,537b201d,a7a1027b) +,S(a933e71,f13c44d5,bda8e963,b5a007bb,b9b2e84c,c45211e6,eca804ec,6a99ca28,931bf3bc,ec45e2f6,e6571207,31d42fdc,c3ce0279,542114f5,c1c0f659,f6200faa) +,S(9003f61d,840c2d62,1ba98884,e117038b,b8559d0,b132a1aa,83adf65d,f8e4a58b,50c75124,f72b3952,9feb7ece,cba26854,1388a1ac,73631532,b67f56dc,e8d30895) +,S(fc807c18,3cb370cc,8638abdf,c17281a6,914e21a9,e9f3e2c5,33fb7c37,caa0a886,3ac79d3e,75ac1d5b,a9af7263,9d564256,1ccf6fd0,6b8d2cde,e9d24a1b,e8af00ba) +,S(3d072ae4,aca4ac23,b43b608f,150bcd4b,cd780dae,7e4073c1,86293cce,dfee9cbd,6d9e443,f721c9f8,a98fc563,8d7627,f9c60414,67da3857,92fd55d2,7e65d9b6) +,S(d0cb8594,7061b4a7,d2bc5eed,522faf8b,ba6e26bc,7acb1d3d,106aabd9,a7f80dc0,fbfd0f26,e9d0dcc1,a0107b82,d1a9876b,8d7562f1,41f0892c,9b739e47,7b012e0a) +,S(e4edc0ab,6416c644,1e59f46c,c7221cc0,267aadac,242cee32,41a6d25b,635bc9fd,f41c3ae9,cea7b4c4,4834482,980807d1,c496fd4e,bb06964,b84c1fc3,4fcdc71e) +,S(6233b50b,2d3d9101,8f5a83c8,84be0f5e,ffb80641,1b0b5271,557d32ab,dd7eb4b2,8dfaad24,ffdd2ac2,5770f21e,328588b1,34a3a2b,dda40399,7911ef19,855dbcc5) +,S(6ca00162,e27265e2,27b4e544,7e981f6b,c427e65e,bde295df,9434b7dc,52a19ab5,d33f5f63,a8f227e0,a43fe297,babb3a07,223e8916,e153a5c,e0f57e65,1154cce1) +,S(c01205aa,5d2a8d5d,c0d328ae,7ce7cb16,d6c9d992,8b6105b3,cd68666e,36fefcfc,b65e5f99,c4f48095,e1bd6f6f,6af8ba7e,cf2988ab,ffa9bb45,3ad01859,e831d3e) +,S(e3965393,e6268cb,61d6fb19,58050f39,7b8a597,4a6c4abb,4e0b78d2,dae0e155,1f78ad46,2ed6e1d9,18004892,85383b2c,3f971fa5,cda0c828,a2f289bf,80731737) +,S(27bfd0f3,702a9d43,50c1a6,9fd990fd,3dc03af8,50bd84f8,1248222,6cb099ce,22e16b60,e8caaebd,19b0c0bc,e40be7c0,c49fd3f0,d9094f39,fec1b1c,bc643de8) +,S(8210fbc0,23319565,9eec1777,12997a89,2a37172c,6f75e3c6,cbf080a0,6f96a2f0,1ddd7792,101166cb,bb11d552,85e1eeb3,bfacbb25,6d1a5097,16dea91e,46afee8d) +,S(15cc66b6,5fcbbd80,df15298a,25e2ff80,ae4d5065,f8befab0,89cd21a7,4e347b5e,944b47e9,35748296,deb324c9,f5616d28,7b225b34,488fb34b,6ddacd32,7937c97) +,S(eb7b7aa2,45960169,1ea1a482,a9e6c35e,c7bb08fc,726c2c4b,ff801ae0,94c97976,4d2e9570,cc187791,fcbd20e9,3f0656a,f5596f4e,104c3f2,27c23f82,2ed1b1f6) +,S(4598fafc,ed29e844,c22436e0,996a8469,18e7c106,b40a5ea1,5fb0e7df,5e9ba12c,e73d96b6,d292ea2c,39779dfe,7f716609,978a8555,e5b09629,7dce228f,f35e6093) +,S(49835a86,3612e594,572a2591,76f90e18,e57d4dfc,1910c705,8e37eeb3,1a8600f7,3b7613a,fbd2cc1,cc23b147,e0e80d36,464c08a2,e488d7b9,8250ed37,dfcfa531) +,S(3a6d6fed,1fcc8067,32a3177b,4dbdc33,3cbfe0b7,d19305b9,965916f7,ce335351,cc3f3d8a,314d617f,148a6706,d7f10641,11eb2b56,1bd1dd0d,f2986925,5a6624ea) +,S(21208586,6860fe30,ce92d2ef,b2035b74,f672a024,ee7baa78,fb0d6c7b,3ad7a9f5,aaf145be,51b1ec,3fee8be1,1f6e9d73,96ff0ce3,41f25d7c,e1074a09,78bebbb7) +,S(f819132a,c9bb2f09,663d609c,51cb11b9,833ac082,6ceffcc8,67cda04d,266e4e06,b1fde638,ec6a305c,aaffd1a0,6404aeec,c52f48ef,7646abb3,7e227239,4c891de5) +,S(536c05d8,8786640c,43734d72,aed62d9,168385a1,213c4428,423a9437,c2ec009d,7f4be100,d827de18,68bc2b43,c83a08c5,fd84ddee,1fa9ef13,4b489325,db6df0d5) +,S(4328123e,c4c6db76,37ee3ed3,42c8a859,9de2fb1,a39a24bd,650b73ba,a1df722b,81c002ea,1277753,e6a22fa7,1b4f86f5,4ee11f10,f5d5b15,94c9d7e9,63a40857) +,S(f3e156fe,f8529951,86a9e536,f985c1,6a85fc32,d50e9a08,1550da68,112a5f17,91ffd66b,56ff0e6f,65d67e83,c362b853,a2f441ce,7418d091,5d5dd09a,b48892d2) +,S(32ba7359,84e13f42,d2e8a716,a54c40ac,6645a1af,eaf030e6,8242a8d8,8b456cc9,67ffcf1e,1c32b150,a82ab317,8ef24c93,f055765c,9e7e6200,f868b183,bcb4c6f5) +,S(cb9c2b3a,43d105e,518674cf,64d8b4aa,9c868b93,18abbddd,ccabc0ac,59829311,1fc67ef2,233f076b,ded3ac47,99c859d1,834bea5f,bd2bd5db,242607ee,842b691f) +,S(e14f2858,b7bf7070,32349695,e09bcd3b,5effa957,abc19e85,6efa31bd,2e9234f0,3e04e49d,d3f3fd39,883cbc25,80d5ff06,cf8cbf0d,4a895761,f3cd15e4,dce08bdb) +,S(156beb7b,1c27b8e3,cc78113,c97ff026,74810dec,58409e01,d7b7388,87550d82,c711730a,80216ec8,352adf2f,d5939991,3a8e92c6,c16c17b,16e27a91,90bc5371) +,S(de5ea7e6,b9b46864,4b80e495,9a2ddaa1,a24a3399,f8039355,7b798b5b,2c55d600,8901a027,e6a823d2,dc0a9d24,e182cd52,b9b1de67,22b7f7d5,3a03c615,8cd48ee8) +,S(8eddd368,8bfaed46,25b7312e,7b2bf0ec,aea07414,a1281ade,b79b9e3f,ec17c3c7,2db43af9,26171d34,d5ca48a8,9223e57b,59b885b1,5c4f7231,6d8edb6c,7955da7b) +,S(5c971b6a,665ad1de,8fa489ae,22237ed9,87872e0f,ef95eb85,fb2ad3d7,f0b1d4e3,4d71f598,37c2af6f,af32ff7,42ba01d9,aa8d4d7b,302d39a6,c4fc693a,5f8b1f66) +,S(edfb253c,28212b6a,d5b66589,a2a8f1cd,bd3586ef,34254768,85cfb70a,46b9e228,3e5b6608,b7e81f69,b8cb26b8,4f306292,594e42e0,25134c1d,7f7b2a24,b2616c00) +,S(4395f24a,e127772c,db163dd4,e22574bb,d2373cc6,b2c67384,737c56b7,dd525c13,bac6e725,4c800c2c,64896913,df739574,676581dc,1b4447a,819be64c,10103526) +,S(81100ad5,73a766b7,f8913b64,b5f5a87f,8c782353,5f62a3eb,182f0ff1,946866f5,52cac16e,325b0899,9d068f7d,51dd9d5,c6afb8ba,9870a9c9,d28b4170,e9a00c73) +,S(fd3a0551,41168146,6ae358c3,aa14216d,dee3a05e,e5ddee40,7f72347b,eb95120f,2af2f30d,45ceb12a,b89b7b88,b7e04b20,11a3c607,58ce7250,e4c7bcd3,da8ee5b7) +,S(17a55b58,a7eceefa,3ff05b6d,fa17d6c2,76d1a032,abd198fa,4e3fc3ef,e6435ff9,d7273958,a3ad67c1,fdcee72,e71f0536,418f35a4,57e06240,bc99bf71,b9574d4f) +,S(870cb7b8,e2ca5ea0,8c5c73ae,e15e8b22,793fd005,5ed7003b,c49b4089,3b789b6d,c33c8b40,7b701caf,26d7a9bc,e22644a9,4badbec,ad509cc6,a7b0ee04,1be67aa3) +,S(f951c5fc,dc46ef00,adc53c89,a50ca0a7,ffbf7c36,d80e4897,5452cc1b,4e71f8a7,6ae2f821,7849cc24,30864d7d,24df9e59,c1705b92,5792e648,75120738,c792c95f) +,S(aeb8077f,3b92c676,ae11bb06,a18ada3f,6cbd832a,1637ed61,31e4902c,96a0caf1,29f41d1d,f5d4a430,94e75fb9,8bf317ff,6c8a6d7d,b37ccfd,e0232e1e,afcc1e35) +,S(d46f4d97,5d2f2e58,6d18a7e,5d8587c3,8482b78b,595de60c,b9d2035f,313b78f9,95d781aa,3bdc1176,396154ef,31fee42,f704e199,4316e33c,1053491a,e86392f0) +,S(6afb44,4b4bd89f,2e21b54a,c49b9ec3,af810b97,73c76eaf,4f3a5f1c,7a29b201,3502901f,d7d3b4ce,134ed90d,1388ab37,64db460d,bed334e,af061822,6ebf932f) +,S(5f02e582,a38b1703,a7bf6086,9587af5d,e6634954,cfaf8394,932f7ed,ebd86976,a0487cde,92f3a796,b24b06bf,740b8fe2,ccfa5d0c,a2b68a7d,ef46a005,2d7afec2) +,S(26427482,60432ced,5a07b885,f354667f,ad70eb75,9c556e33,c6652d3a,a0599fe,119238fe,2560382f,d76ea737,9340453b,a63867cf,6e044c62,949f858e,b828ce85) +,S(6b2a3e5a,d327adda,52b19a7d,d5e837ed,4f03a5a7,e1e5b7ff,2cd66660,469f91dc,2f9f4526,6b482fa,f336560,1da9e945,354ed265,e8d7e57a,22f04bce,8a54c6d9) +,S(2588b88b,7e214fba,ab10934d,def13e8a,e5fbaf77,d7497dc4,fcc0345c,435daa79,3e5cf90b,d3ba6d4c,8d7f142c,ef1a51da,61ca644f,acba35e1,51ebc114,c5435bbf) +,S(d9484146,92852eef,fa8178b4,a01404a,e4e3d846,c20c3fcb,cff5cff9,de551a93,d5b0fd7f,e628c427,434a1219,6ad99862,13ea0abd,969864cc,dbb8258b,96a6d5a4) +,S(5cf81fdc,3a2f2087,cb64b1c,9b6f4754,5ccc1e78,67cee5eb,9fe9c4ea,3b0de2ff,e0b0ab1c,f6bb8e5d,f7ddc700,5ec73471,e7ff3125,daf113,1b78fb8c,15bc0d7d) +,S(41b4ef7e,b719fdb5,704b3f17,4132c694,f8ed2471,1677a320,3b866660,dbe879a7,a6fe65b1,cbae2235,cda8b459,eeac19a4,9f44c2aa,1c8a881a,e12093b8,3621c85f) +,S(43ed572c,41f691e2,a8dd1691,86404ec8,7a4c0a3f,406f3bdc,5c2709a9,bb117af7,b7b9e1b8,90b40334,6bacbbab,7be4d477,90a2b093,dafbac3,fd4d2073,fa481da3) +,S(4e010a19,223086a6,314bb526,2f1955e,81722b41,e85fae42,1c338e46,1b397de3,f0d3f11c,3475ed8,44876e7b,3bd0bb8c,283b44e3,70c46ec4,a6159a27,eba65fa2) +,S(b3609cbb,7fc86bcc,4dbc385e,81edcd84,45e15d2a,7d355e22,41dd442f,ee11f36b,8607e5ac,a3df34ff,804017dc,977bba88,c308911a,aa1698e2,e6190c8,dc9c5f68) +,S(445e60b2,543fbd3,7bd68cdf,6db1125e,d1bdd6b6,b27249d1,df96e43e,df965b4e,a5fa458a,8c4ab47a,bebfdc7c,f922e713,c65f8b52,287a65b0,b5dcf0b,c7715fd1) +,S(709f8227,9264c99f,86b59a7b,ef14c189,be875b49,e00cb64c,f9dbb643,10f50fc9,168027f0,cd28d2be,8a69064b,468e4de5,3d4903ff,faaaf628,83f15031,58960d10) +,S(62aa200d,cbd1240b,a252e725,a333f21c,8a2e9d5e,fbf18cdb,e1567ff9,29fb4ba3,19aafd6c,7017bdbc,4b906b46,843e41ac,1ec3282b,12dee1b7,b18e3f28,9e0a1347) +,S(2ba73958,8ed54646,fa9f2389,802e2ab9,e123987f,c881ae11,f38f10ac,b882c09e,f37daa1c,9c0a98ae,f86cc45d,34801353,d8f8a193,77518423,721f3e12,375a25a6) +,S(8672b9cc,e8fbb69a,ae5c0f54,9b59dfe1,e266e9e0,3592a397,7314e6f,79005de8,20d8476a,efb61c79,adcaac68,fbf9eae,c411e875,a66b5aa3,99a97a86,9b05af65) +,S(334cb002,84202999,79fdbac1,5ee5c17,1f32bee6,133300ed,a26571e8,bdab6ab5,8827d99f,3da7878f,46b45bfd,579c5cb8,e4e972b6,49d6438d,96e5a188,5aa574d4) +,S(32b30e07,d260ec07,354cad6a,2f64f81c,e9f25f4e,47bf1afe,602f229a,35ac6d4d,71ffcf0d,64ee9078,6f290418,1126c44e,5c5e3688,f3063941,85660874,3c3c0427) +,S(e5099658,7a4b4e63,43e401ff,299e9bfb,3b5ce402,5c38fa05,761e600,a39a8451,7b0382a8,65429a86,94eacccf,5c6b3380,683a3916,70fc7241,f132c465,b3bb2ece) +,S(b3872c4c,2bf539bc,e0aea035,3b3d786d,ca684121,29db66ab,d316e930,e5d6477d,7ce04af,e4c8c86f,3b37ed1f,a9dd4161,59391c71,22d0f4f9,2424d090,901e285) +,S(d2b56fdf,9156e3ab,2e414d96,49f4c418,6095e74,a2df8908,f3b406de,9935d07b,443c2c1b,67668c56,a84b4925,946a8e5f,b06a33af,371552aa,adc3b500,efe3b23f) +,S(f69867e,1074c765,923f32e1,ec9f41b3,4e8288fd,62b0a081,15db15a9,f37fbacb,fd6c6e25,2a11a516,9ce04c6e,d719c22b,68709be7,6fdd0e58,cd0505c6,413fca16) +,S(bc1830b4,a9ab11c1,ad8778c8,688fccec,cae23898,555b6579,62670d8a,b9e64988,b6244089,f3c46133,1979a0fa,f3d0c8f2,8e95cdcb,12db9ea5,b6d95ac4,c22e3c81) +,S(fabaae21,dc729fda,98c4bbe8,3cd5fb92,126cfa1e,a21f43b7,445e4b47,f38dbafc,1b569775,64465832,c73513b3,ed6aed9f,177ce1ba,b2c09fbc,c4c33600,582d8cdc) +,S(93386101,4d5d4c3d,f912497e,a248d8e3,bcd3fc03,216dd57b,5473d96d,791f8bd4,3d8fc96a,c141163c,4ff3add5,44a6ad71,9e57f22b,8e806d95,1d630fa3,6c936725) +,S(c5312d5f,85c42820,59546b8e,ec6b186e,da0a4324,d30147f2,94a83fe,a6fd07f8,f8ae6e2a,79ba0756,523be12c,8d544734,332898e8,b35b651b,edaf806c,45d2992f) +,S(a08f2496,52c52f40,6db75c4d,da022f41,b73b1128,11aa7388,558480b5,f203b057,a396c7de,9ab5e1d9,5e9aa925,1ecfe3ca,191d7a25,37c4cccc,845dc745,2f3eb2e5) +,S(93d5330f,e8df6fa6,a74b34f,a251a6,a036e0b5,89b8a05b,c525e4bd,40f634df,ca15faea,10f424e5,5d6774f7,7c87da28,c4df567,7d153a08,f5ac560b,71838756) +,S(4622bcc,dfcab72b,e850a994,1f652110,5c35b813,f05a4f6,17055f92,aab51fa2,d960702d,31eeb256,d25cd245,695b53c8,e883d2a4,60965a8c,d29b41e9,d7ca2d4f) +,S(a326036c,7dd885d9,94ba5872,8138c30d,fb6c83e,d17c8d88,7aee086,e0ef9fe5,b465ce69,3719a155,31caafcd,e7689a17,456a6ce1,c75e5a53,4893cd67,32cb9bf7) +,S(2354417e,19637e79,71b0c887,bfa5c553,f3d5e7a9,84a6c946,b1d20b5a,7eef77a5,815a9721,b6ead3cb,b254fbf9,9f34f409,ea740053,3cf4ed4d,2169f48b,29f4bd4b) +,S(9d346d6d,fc53174f,5cda746b,23810d7,3a407819,ddd5df9f,2b07cc8b,b7ce6fca,9b6cc06b,64293e5c,a55a8ff9,f1e90551,4a199b03,df08e9b9,8512b357,9776c806) +,S(73c82b8e,c643a7b7,f95f000a,debe210f,bba638b4,5508564,bb5a3330,fd48dd01,fff15699,a0885c61,fe35f61b,84cb2bda,7d2acf5c,df0d6f07,5fff1369,805b614) +,S(902469d5,a0b2d5d1,cf931672,88506b95,f2d29bf,6ca291af,c9b39cbf,fc798ac8,356d011,d71c769d,342378bf,fcafe4ff,cd78addb,abe8b0d4,fa63de89,1fc3ade4) +,S(e670a0fb,3a0c86f0,53523afb,819b8944,dc4013e9,fd500371,b1efe23e,1f110637,869136b,cd13dcb2,157db5a3,d07e59ef,272557e0,9dcb250a,9bf659df,260b31b8) +,S(f3fef06d,526581e6,b0f68e27,cfb9cc9,b57f11e5,849545fc,b9ad3d5b,8a05c395,32b2e997,144420ec,c4c45176,efa5fbbd,570752d1,4b092f4,ff375544,19b8f553) +,S(72a37663,3958d844,560fc28a,8cc19d88,f942c09c,471d2cac,70ca5d39,396a05bd,de9b1b0e,6e121070,f684b3b8,402e8d8d,729ce846,d2f7ba8,503f4548,735ee1ad) +,S(38b78ad9,c43b7def,4119c3a8,190cde68,4172894f,a84cdae2,2b637bd8,805300b0,a181567,6c4f5f51,87204f17,5bff32e9,de7cc69,dc851c84,23de74a8,b3d01e6f) +,S(db968ac8,1d2e7c53,6c66fac9,e194fb23,540e2e89,8cbd3af,68e158ff,2b34752e,4d5c57ab,79d9972b,ac6b4af0,a6347271,4179e8e,fd14c769,a24e5a73,aab48a0a) +,S(2ace1679,2d3a4d83,71114f28,c1ed35b2,505dd4f9,97c72435,a145d367,a8aad318,5314312a,246874e4,cec63b87,f685f6d1,405b0ef,a1ae3539,cc88e73f,3626f625) +,S(32d80e69,c390ded,db8a4600,862727c4,bb1fda18,2563277e,852f93af,3dbae108,3c7d96f,ea89d06a,6d3cb3f8,181f7cdb,bc1c3e12,3d9f1d08,cbd557a9,60dc7c07) +,S(3ef49e8a,e7fe6b40,628e61a2,945db3cb,f1bf050d,e525d521,d8e8a408,76ee3908,4381c75c,62f45fb2,4d431b54,d63115af,3ff72d27,58c3c92b,d3d1de08,1a123f2) +,S(853808d5,cdb82cd0,39b517d4,1c88d7b0,e280ee6,59e5c640,5aa27542,3c9722da,c1677c44,9b2a9626,698a652f,81722242,3ed18d03,92d4200f,b4dc229f,5f7e8f5e) +,S(572fd483,490c05fe,857520bd,8861fccb,306ee4f3,29adef49,58c1db78,1fc8584f,82acc906,10392302,cf0107c0,6b3b6194,d38e9155,b31ebca0,be300036,3413f4a5) +,S(b1cbabb2,9aa8ca31,75aa9a2d,d4d956e4,fa771344,d4592481,38c93583,376e1386,888cc0f1,7f9afdf0,6ba1de03,41409c1,265cb2f6,c015fc,bc58d2dd,66292ec9) +,S(f856cb92,42393134,7580a65a,149a6e81,2c5c960d,670701a1,6ddbbb98,2f6bab20,c33511ba,192c00e9,991f580f,65c111df,77d9d6c2,d5d63869,c5595011,48536674) +,S(e1601cd4,773e129e,2c042887,b56bbb3b,9eed92fb,6c128e23,fffc3a7,ee73920e,180748c8,a38c7796,ee247c71,49ba1fcc,a78648c,a0508ec9,6f4a016f,301054ca) +,S(98ee20d4,5ffb6177,5ca206a5,b0cbf1d8,e8b37cab,97489534,a98928e3,367c7e72,2df4ace1,ea42db51,68ab5e6e,88fc405d,854b70e0,4521bc30,6978e465,6273c610) +,S(4796ca57,acde905a,31dcfe95,bedd1e13,5417bc41,f5acc246,ec0ad4ee,ddfe179f,7413f522,395a3802,b37637e5,9564c239,5bc565a3,463f815f,8ff962a1,f0a367a8) +,S(b367c864,38adb0b8,d5b627fd,56c7df9f,cfd55536,9abbdd40,6c68bdd,1a341200,c0364744,b99e816,7982f365,72268702,b4986cf5,c9c5749d,fd22499e,2e8fba51) +,S(db36884b,723ddecf,7e2244c0,20de844,e740760a,4f27d386,a747f4a7,88914d29,8ae487cc,2a743a3c,2a1e0750,7def0f2b,fef90e24,fda0e3b4,e5f3333,7d79e023) +,S(65971818,f71c212b,a8e45821,31eef4f9,76b4732e,6c6c3123,ccf5c6a0,99a316c4,415f6791,353350d3,3b087f39,13761119,526a2831,c70375db,4cd07754,e13604db) +,S(f2f4eb3a,13a8f4b2,42182a63,97eb6cd9,9053903f,961a1338,fcbccac7,60ebd65a,a575d67b,667e94d7,20adc8b7,4eab8ebc,18811b3f,3a3bcb6e,7950cafe,8ef9ca8) +,S(a24e7a0d,a058f0c6,3328fa51,fd3c927f,884b3f77,cc068a47,e8c6b4d3,1d113a49,be8db830,79aca569,9eb2fe3f,829719d5,bcee615,645e4c81,a9a7212f,450ef78b) +,S(e59a3ccb,77e11797,7db6cc32,5ebd5981,42212da7,48f0a4b8,981db72,41efad5a,a8e07545,fdda2668,cc0a10a2,2389a2d8,d6903416,a4a05299,9d4c2d4e,4a800d9) +,S(3191b263,a3c70be5,bec721f9,3dabe9c9,fc6c1a75,aaa94e9f,daf6060,fe4b8f32,339192cc,1c7a9bc7,2a9a87c4,ea3cbab0,c7af7b27,53e74491,717b1f99,5df38a96) +,S(63dd5fc6,4f9ddb0,8fd47e27,5e839b20,12f408e5,3c139463,8b97874f,9187023a,1af048b4,2e8b7e6,d456cccf,d12ef7ef,67e0910,4162239b,84f35cbc,8504bd9a) +,S(a92e457,cd8312ec,cb87229b,7f751c4b,2e1d8640,c2b8e5c1,2d3dbf4b,671bcf13,d7aedb56,5447eb5d,18449241,3a07d672,be46ab4f,3d5b4b3f,551f292e,5ffba768) +,S(1ee6b970,b9e09d22,2b20f02,14f00bb4,4789f55e,585f35ba,6c0dd6f8,39b10495,7c910fec,eff9eef5,d1a3c8a0,46252885,9dcf73bb,d4a0b2be,3f8d10e0,dc9e9462) +,S(b19c23b9,c2ac1957,5328d7c,43d04870,1abe9e97,30285ceb,e4fd1659,8f06a76a,79a811be,c07c8b70,6f380f8c,fe369e45,c605607c,d08c8a49,2745b283,37a38398) +,S(3c20db9f,9ed35fc2,7c0888f5,e266a455,4f3fbf98,b8b2a016,b1e9aef3,14f49a5,bb3ac294,3f29e63a,f610815a,4a1e5a12,9327c1b5,ccf489ea,7d8950e3,5bfbc4d5) +,S(fd1ccdba,b4746e9e,44eb5904,d338244,efa29ffc,8ce8014,f6b922c5,2eea4389,e1065546,52146f90,fa52439f,c43ce4eb,73961a52,10f6c040,97a82032,9a8df06e) +,S(e30fd18e,abdeb8a6,ef252dc7,3c2b0d75,83641a29,8db37ab5,1e2da099,9b64020c,49663672,1824f481,962bcab8,6716eb41,8e195d29,6afe9704,cfbc24c1,28dce88b) +,S(4bc1f789,26ddf930,e1aea784,83a9dc09,5cd72300,5540548,e6e217e7,515bda37,966b32b6,95dc1b00,8dcbfa2e,734bd9ab,36d8a26c,2a52c471,63a4cfcd,d200ca69) +,S(23d3e27b,9c864b8b,40f0f7d2,6ece9bc4,9004f98e,d46b539a,affffea1,54f036d4,be4617eb,f680a27c,cc4143d6,ac150d51,b3dfefbb,2892a6c7,1b36c794,8ac0b05d) +,S(28adcbd0,14cd3b70,f46e0a89,477b5cc0,4a513091,cb6c1aab,56de4894,3e6009c0,7c49a838,75204cca,38f250b4,608cfe4c,25040dc7,3ded4fda,f4a92cce,fce31601) +,S(776084b8,a86f0823,a5044ab8,70d84f60,ea52ec7b,a545ea0c,c86f219f,1ca29d08,60bbbecb,1048d250,b410bc38,49e567db,def17272,58570b2c,d0834532,581c60db) +,S(45df2e0f,fd32ef48,95d9667b,c6044951,e3db94b9,ed6af5fb,d4fe1e7d,228f2350,a732464b,1a8cfe06,69e4076e,d4164b98,4de155cb,68bb3994,73fd7918,63eaf095) +,S(6904976b,46a5fd1f,a74bff80,c493a2fc,8ae9bbe8,783cfebe,7f69f0e1,be7dd7ea,19d071b8,df9d1436,6241a22,1a00b340,30f1df45,5e66fcfd,5ad621ae,ca2c3e64) +,S(849d8728,f977d075,a8de805c,8e80980f,e01def35,1ee80f0b,ef528e85,29aef659,d84955c4,6c886283,b9d7adad,bc069d0d,8042dd9a,ec7246ff,edf31594,ae5bf428) +,S(e3d64b4c,234506e6,e7facec,8d7534ac,54588dc7,ea685281,e27c6746,472d58eb,4a32ad3c,cf822a84,74447baf,68aea598,11a9c839,4c9970ef,bc61f363,ae5a36eb) +,S(f41c7928,57d15a31,19b6f132,3b67639a,889877aa,e9213ece,c787d53,5463bbaa,73cfcd0e,c84c55,4d007594,152d9ddc,630c458e,7ea456d0,7cc60503,794d9328) +,S(e1d21747,47ab84c,e47a4a02,caed939f,e0cba1c,41e7b21d,67f52fd7,79186079,17b0b628,aa4bc0a3,ed9728a3,e24a8101,fb26da66,9ac92f63,af81aafe,7db2b668) +,S(a8a0018a,f8f86795,3189da2d,d52b4db0,5d7ddb4b,8d290c80,95a8e7dc,d86ef1d8,8bb9606c,84b88a1e,df980c7e,9360823e,25b927d7,9cab4c17,fc8aba7d,6826518b) +,S(7383c671,6ee05e44,daba57c0,50ebda60,e4e0c0e1,3df338f4,cd8d627f,736fffed,3bdcb946,5f164a74,638cb973,fc77daaf,38eafd48,9521cba0,4ad896c,8e89f0e0) +,S(61fa7e1c,8bc8ac97,96be6431,20858063,1ca36af4,520cc1e,858f90b,ad372dfd,3f0d6768,36c3e694,32287909,c8841de0,a5f646b0,94b0c207,8d634486,c3eba33d) +,S(86029e2b,ca0ac601,99092def,cf4882a8,b4f14349,43bf116d,3cb310a3,b2c4a740,b8be906e,cd9056f0,f3261cff,d12216c8,ef5f1553,dd708480,15463b7d,241c2f7c) +,S(e172f7e5,912aa487,589d6c4c,d0d6b9ab,48c4a6b0,5fc5f90e,2f6474c0,4fad95cc,edbd23c1,2b0a0ed3,b899920c,e8b64d7d,d7bfd79,5009c290,ea0673dc,8acc401) +,S(7986ba8d,322efc24,8b7b22ab,f838de93,2d931c78,cd6a8616,d1f37dbe,32873494,693eab22,4c750592,319b7544,d29fe0c,2420a5d3,d3b7e01e,e6ebc968,12a13634) +,S(ba594138,4a727673,7600a6d8,8d36b133,400bb5d3,6a8a9d46,3e8c65c,e1e00383,746f4e28,bc1b812b,227fc953,531058bf,d9c68545,2f6219bd,86799dbf,3c2ce557) +,S(45ff41c9,92d6b117,c86eeb77,bb17789f,6bf96cd7,2cf0c6b9,802af869,20476c8f,9324f5bf,6422e3ae,2010c9e1,27167d8,4f522e86,87fc95cb,ec3d6972,2002b59f) +,S(bad79e35,487a4da0,7171c282,e8666ed4,d00bada4,d9576a7d,9a93ecc3,c16ed68f,19397a2e,efb184a4,31a02e12,91db0cd4,5b153517,9038a920,7a6d50dc,787b57d2) +,S(ddfe2107,2d8875ec,60c4d583,59755bfc,22cf890d,d3665b79,cfa403bc,9e3c8a41,c62bd917,491d0a8a,9215674c,28c28872,8dbe0d3f,87c10d90,ce22b608,473f62e7) +,S(925f6a53,c07f923f,968ccadf,bef9d95c,d7594269,a3d9fa64,b1180edb,667eb299,1080e8ff,1f918e6,6f08b26b,7f9cee48,58f00038,b8ca50b7,b2939960,b1e7809c) +,S(c28e49dd,a75f0283,38e5d2c7,d1f03af0,12768bd0,6b0e0d0d,87878ba8,2720dd80,670f8989,c941a49b,cc512c5b,4987e167,ec5ae6b4,d7f68a98,331845ab,5d0db12b) +,S(7a9053e,95035be8,8188c196,d5e8e05a,b21dae48,d59406f3,7b9f86ca,2c837cbb,40ee088e,8e7694b7,994774e7,9d097d76,6de026f6,f7fbe352,e7f2bd0f,443a7e1c) +,S(33af78ea,bfbb00c5,4d88ef81,e9661b16,e0b32c85,a1b61b95,6c6a79fd,e036aa2,713e7aaf,537a796,c4d79644,c855d0b,15331988,5ef4629,d8048c69,d3cfd3c0) +,S(c588d014,5a3f3d33,29695573,96d674c3,1326dfa4,797d63d,f9a0c720,49583bd0,b5c0cfd1,6c67cdad,ababaa8f,cfd2cd76,7f747024,8caf60d7,37531c66,ac53cdfa) +,S(96b3d5ed,b93742c6,7c48ef12,c649341b,97223f22,1f0edd5a,2f2ab523,8038d29e,db475caa,38a3a05d,3719a2e6,4ce7b4ed,bf25fe57,4b28d6aa,9961ec0c,b0e44c4e) +,S(412f4b6e,3d894ee6,fb0cb00b,14662a9c,1eea3a42,5a286782,a62da77f,5644f0c7,a84ef636,ab9bc805,bffde813,93874429,a8503e89,8c03f1d6,f139af86,f6a676fe) +,S(25f0a785,c7b43028,52f1421d,a32c418d,32fdfdc4,89c39049,3794e1fa,e4563252,efeb090d,457190b5,89c953a3,aebef406,da716af5,1dc6e334,a0226883,6004e4bc) +,S(50fd4ab7,f367faf4,acb4403a,49ea3e7f,f02fed58,4327acfd,e0bd58a4,ed741c6f,78abc1a7,3c9dcd48,6520cb52,adb1c3a,2d08abcf,a079607e,a56307c6,3d46dd5f) +,S(157583d4,cfaf7f79,33b8fa66,430f107e,c357621f,4b94f313,bbcfbed1,46a477a,9aa6b235,ce8cd1fe,60cd4355,c46d4859,fe35ed8d,659d2aaf,fc09cdea,297e8db5) +,S(3c44bb62,5d262603,e44affc1,65b35976,f2edcedf,8be6b494,f4b3bc4a,5b3300be,88b1c561,67973ab8,5ecd4594,90598152,1ef0e8c7,1ff707b9,79ec2bf,3cbc2535) +,S(31d4264d,e1606b97,f2f71d19,33f23cc4,8fc5d9c6,e087dd93,5ee25b8f,ba298958,460609c0,a3aa1d26,2865180c,98555b98,c8e41db6,2e62b2c4,4ae01190,718251cb) +,S(1f551f0a,8ff14724,60ab206d,3dc1834b,352d1d1d,80a8fb78,a87247ae,5d03ad2f,24253c06,a4c8b148,db944f81,802bee34,fcb97ab6,50957b32,eccea33e,c116a6e3) +,S(de2c4e1d,27f08407,31334311,9ff5a8c4,206a1f73,2aaec3a8,7aab6bb5,c1cf333e,dc813ba1,40fecd3a,31ff4a8e,41fa498d,9226ca2a,cb2d5a71,c5a5cfe2,8195170d) +,S(6fdc5db2,adf54ac8,132c4a22,c382c2f0,c1f54bd1,8aed5620,a54bc4c1,18a95612,db3fa4e5,f318e94d,c0c11053,61d21643,cf60b0ca,a6dc4d26,944e8915,970e221e) +,S(c7eb76c6,ea0e1c51,a3aea6f4,dd7eba10,bb76650d,85df0fc,7fd77b73,53680603,5ae1c1d7,b788092a,3c41c3d,7bc32ef2,a4f4a39,a82b53e0,e6f47712,df5bf655) +,S(c81505a2,b7e78b5b,64ce048e,6312bfba,1a52c7b7,8219d857,b4813a98,c8aa89ed,46db2842,6d6ce211,5e1e49be,459f033d,c222be7f,96b247fa,87d5dfc0,df096806) +,S(cad75255,15f9c3d5,8705ff0d,83ac3508,f7dec312,bdc71c9,a17eb9e,78503bf8,32b36a02,76cfdb97,ecd2ddc7,638e4d61,6231be5,25b6b4c5,2d19c12c,6e6bd719) +,S(94d22144,e22b7e30,56eca8a4,7d6b15d8,f17f3c65,82533fab,c70e900d,be4cdd25,12c0760d,5fef2f85,d89dfbbd,bd0124cb,828bf72d,79b4ae5f,6121a84d,47d93bf9) +,S(325c5c23,ef00601f,ddf52040,7721bde2,13cba19f,c0f2e225,3b112b8b,916731a3,59d3cb98,c09c1643,993f3dd3,64c7d29,5cb38cb2,45d8f70e,9628e5ee,d62498e0) +,S(9c907089,544b8316,4a251516,81bdfda6,7d187438,3e7f4a7c,da96d861,91ee2318,f639dee7,e7010630,364539eb,5c225059,547117de,9fbe4911,d9718e6f,8372d20d) +,S(34f14807,ede9c06c,4cb2a1d8,97a4fb81,ec5544dc,eac1bfc0,f32f9835,302b7a82,78437a8c,ec6aa228,f5f18813,a68bde68,3c54c367,5d6be482,8d01f2a4,96d9c57) +,S(9498d30b,3f080828,e03d459a,fcb81fdd,490b47ff,c344d75,3887b600,1639788b,7a5c8305,b97abe3e,b241627d,c5187a9c,677865b3,567cfd2e,ce136852,e42522f7) +,S(eba08f6f,efd36e37,aa53ec32,d3cef88e,5d4f36ff,ebb978c8,76d452f5,7754f226,fe0e66b5,43aa54ca,7afeade9,b38cbe0b,39e17178,4d029470,608c7e38,bd2015c1) +,S(555d4bc2,bd21729,f6119b77,ae8c9bce,c8ef49bd,992cde50,4c794484,7240091b,cccebdb8,69521e33,2ca86941,593eb4a7,6c9052ff,a54f8315,d30119cc,7f437b54) +,S(33729b9c,19da3474,42f33833,d1723d45,3f3060ec,c692d823,679f087b,ffe8872,a94b6a4f,283af604,eb885405,6bfb8044,fac2f007,d7ebc800,b4abd64c,5c34fc30) +,S(2a1a889f,afe64678,647b060a,73ab92cf,78124ee0,d44c7c32,d8ae15e0,df1dc11,626f963d,3bf11f8e,686ba46b,e6dbbf0e,75aba0d0,7ef91218,98605ff3,fc8b75d8) +,S(4702854c,a303e6fc,ffc62f70,258bb2f5,639148b3,4e3927ff,f2dbcb0b,7c197d6f,7da261b2,8513257c,d1475c2d,e059372e,2da76eb3,f3b2c51,95c44447,d71e1156) +,S(7002edc5,a0cc23a0,65015cae,6e49e7b,51d90cfd,7da04e03,4857c596,cd319c5b,6f2845bd,6b7aa58,7ad2d2ae,19fd6115,78c47b56,84fc6975,db63e230,c3db73bb) +,S(273294d4,483d610d,b2169f05,71d5450f,f4cc5bc8,725d8ae6,7c1adf0b,6f48d485,99b241a2,ffec9e0,a039a003,18239aa2,a71e3cc1,1cfbb6d3,7268874b,67d942b1) +,S(1beccf82,3055b440,fad26a53,5a852a5d,710c72a,c4d28e83,b0335e8f,e98c9a69,f3ab566,ac0eee20,7c8ce0a4,d471eac,2c5db716,5e24b489,e2fde03f,7b8aecfc) +,S(dbe4eecf,4f033d53,5d949cae,c8fe672c,a1902323,77892197,fae8e192,53128160,b3f50bc7,927f0e5b,56241c72,24960fc5,896fc806,1c0a8a1f,5fd3d47c,d5aa566f) +,S(6643fc75,bed80c69,4591887a,5dc573ff,caf7f484,b0ae2631,133ecf4a,39bd1e20,4701285,f7ac7c93,bac1875f,dc4a26e3,8ec1e69c,293c3b7a,1f8b5c5f,878d58e5) +,S(99f90bde,cf1dcd5e,7158280,53f820e7,ef19d936,bca63f14,516342fe,5422a21,a04dfd30,6dfc615b,5390a86,8bebead6,5cfd7719,33f91e2a,180d2d79,b99cd4fd) +,S(54e7ce48,3184b7fe,45fb4e35,192b5678,bcf71f7e,49c033e,aad1dd32,86e33433,cac2833b,65ba9cb7,83a8b85c,b7f4e4e2,4d4f8546,3c6a2c3c,4decec88,14c0e8ef) +,S(cf864a4f,fb60993e,444f4e7b,8f4a9973,4705aa16,295627d3,1f7686b8,4bf02e08,7a98d20f,b736eb4d,465db874,ce98bdf1,6b8a1951,7744aa51,af276c7c,db6f1bde) +,S(6ff160d6,516fff91,5ae2cd45,7a98afe2,a2569345,60a7f26,c9e07212,6d59d1ae,5d1f1355,bf1410ac,e4f8b415,350f9c75,7db5d5ed,1f34de,aff5c197,b4eedc4a) +,S(9adba62,aefd2690,1d77f2a2,c6287da9,a56fe35b,3702da46,f231fe4c,9f5dfcc9,ba58ec45,cb4a359b,7126af,fd2c4bdd,2e708d43,41d96e7e,4825f153,abb04161) +,S(204ea41e,bbdcdc48,cd4ea945,72c10dfe,b5d90060,e9dcf5d6,52c0e2aa,14a2402d,7b2889ac,a1952f3c,b2c5db8a,9924a6fa,fb0b9e32,3ec09809,789f780b,ac36ae38) +,S(f7d6c669,364d3114,225a6826,c20d26ba,a4b4f190,e842696a,82aace97,5fc8ba22,15abb177,b42538c5,67d160,cb3c761b,1ff47cf4,4b0cbd16,36f67a5c,879d3470) +,S(7bc4d30a,87dd28c7,8b066e00,c02bd221,e50a7e64,45b57f0d,cb466f27,7d352587,f4ed4854,2e7e0164,da66e0c9,fcb04a1e,6efbc9c1,206b552e,c68cab96,d0c294d4) +,S(55442584,b046bee6,c245dfb9,59f20629,c200eb21,b42509d7,8564943b,1e316ef1,2e2aa971,c8111ed9,6666353a,103f2e4e,a29c4162,d19da280,905031a9,48580d82) +,S(8e7df8c0,de88903e,71224c97,3a8ca65a,923f872,841f2dcc,bcd69e64,b1dce1eb,e4153083,9ae86d51,e7147873,f1fba8db,c31205e2,34c94662,52ae43c1,25d2a0cc) +,S(672321dc,69d8aa18,25569d06,65fae079,b84a9bc9,35bda491,5d546f51,c651de8f,8ad6430b,8708897d,be809ab1,803a6eee,f977b1f1,6b34c188,d333d6f6,e7ef005f) +,S(f4e2bbc1,43e99b00,52032718,285d7698,1b8beb9a,f693dbfc,2d20f472,89ac3abc,e54990f5,dc330eab,2e722b7f,70cd426f,6d80e93e,205eba89,7eabf0b1,5d651a2a) +,S(5aa3aa5f,c6d2258c,907c7275,805c160a,72694f46,6b5fdad8,9134f1,d39ffff,22d72170,617eafa5,b843e82b,5f670cc2,3146aa98,cb07644a,7f4c0600,48ea9531) +,S(34752ff9,d2bf499a,c6b3acbd,1b78633c,9e9a31ea,45c34383,be3eb008,7be2e7ef,b53ea374,2cf3d886,51f7d076,3f67c8bb,39d73ca9,8306bedd,91a6db47,2c1104f8) +,S(d6502624,2ea44f7,d9b4c9b,5069e1c5,88977062,db2bad6b,51735366,27047327,52f5c99a,f2f2a8be,c034db3f,b573757e,d8014321,86db2c28,8c7151fc,4ab16f2d) +,S(52b3aec6,553d025f,d164a0e0,b1f39abd,915d4a9,ed5db2ac,b4593c30,11f4cc9,51547402,1c17b2bb,9d401683,931b25d7,77af18bc,e0e7bba2,432a7717,f4e2f8c9) +,S(8cbb677e,f7979224,12728418,68b79c12,eaa0ec6e,ba4f9b17,4d99547e,827ef9ca,a32809b8,b07e1312,b6a40ec6,411b60be,92974f98,bfedce19,ae590f98,40183f2) +,S(d8f55de0,e2eef17d,e94b1564,d74734b9,3f89f2d6,c317a248,7f1724e3,d531d47b,e9fd46f5,da576370,e0416f71,f271e64c,fa7dc64c,5e086930,523e92c,9ab7d770) +,S(fa6fc9bb,faa2b31f,f9d57301,4777169f,a4ddb8ed,ed4bab79,d0f4cf62,356b6fdb,b6ff57cd,dfdd790c,7aefd1e4,d4b111d8,43a2adcf,9f781b33,1b4420e0,f57d494) +,S(239f028c,e748edd9,69f752d9,abad0498,f9359017,653e6ae6,bb93f09d,61b58e9a,17dffeb7,8831f6ba,7ba463b7,bb7b142d,d55c8aa2,91bdaf7c,644e8ec,14f8b516) +,S(2ef6cbfb,728d976f,99c71db0,5389bba4,ecad3c14,8f2597bd,99af8f74,965e4f3b,19a27ba7,a382078c,3f1ebe8c,1d93a37b,deeef542,cf174273,b411b60f,79b0fd00) +,S(972d732b,9af8315e,88f042f1,3377871e,1a429247,d8537436,98b975d9,33064a0e,308153ba,ba4c3b87,40a380fe,e4bcbba2,a5c2ae3f,25fde1d4,f40af4db,aa4fd28b) +,S(3d378b34,ba4998f1,833aa295,185414be,e788bced,af422ed3,3c4f2747,6e062a4c,46a4559c,9217f919,80887b50,29f75c01,3c3d5238,d3758170,93eda1a8,f2c4c8a3) +,S(fbb2003b,1fc1b3aa,fd32b289,abe3b362,b13d70f2,7d6451e3,76bf882d,395c3087,97c866a5,c462362a,4ceb96bb,9c17db03,f28e5b1d,4e418a81,653a315c,4dfdabd) +,S(28fa94e,8eeab1fa,bd27a9,258e26c4,295efdb7,afb5a47e,6e14de8f,877a0471,f7619d0a,f2eee8f3,c0c7b5e2,8ce9d0f,62221919,4d1762a5,5145a909,9d23276) +,S(4708a285,7eb9c115,71627ede,6fa8fe29,4be27233,8798f25,c12cca0e,f9308ae9,f35f9964,2e773990,b2e0ab60,1a92cf56,85403466,87a8c35,50e36b91,ca4fa449) +,S(93408097,6ff4b874,d88903dc,b3ceed6e,a82427cb,36b62c6a,6364f2f8,9848a62f,e25a6962,d4e26764,9ab2d4fe,a0babf7,daf64d48,98bc60ae,ff3cfb63,e95f3c6f) +,S(f4a42ba0,818201ca,e3195da9,3a566623,9e0d24dd,256cbcac,3959dbc,720ddf9a,8fd26fcc,be3eff0,75d194af,e7391285,10bd4c8e,7230f96a,60fd27e0,a000f86) +,S(c5ada616,e65bcd40,cf39ef6,197e0a97,791cacef,b41b0991,9c51b166,e3a6240c,e4c65d68,fe41c62d,c85b0b32,39106ae9,cc26787a,60d351a0,4a7008a7,f96eb8c3) +,S(7c193ec3,d3f1d91b,cbd7c62e,5061a5a5,e8b1fa48,561102ba,ca2ae100,80c640e7,cc7cf895,511408f3,e400ff31,8a53685,3b8e068,1d4b333a,26b59cad,97873ccd) +,S(77542543,8e7cf128,32165629,3e6a1c19,22b76355,1ec716f1,d96002b0,ad1bd3c3,3747169d,212c37be,1906eec2,8860af9,edf7d932,c187c4,debbf800,8bd2137f) +,S(92dd5399,fa75e786,fa5c0f7f,90abd1a8,bb48ff43,e54dac5a,3b9990f6,27d90992,5cb1be8e,f93783e0,c2e5c824,ac6c5348,ee493231,9932e9a4,6e54921,3f11b7ba) +,S(c1770828,468eeb90,10b55053,2a532fe0,4f66e7a7,b7a1cb37,6eb6ddef,d693157d,da5643d,a0147d47,fd29e262,f61ae4a2,70e5e9a,77a76bc2,d2e7d24a,819a2e11) +,S(1372e116,e90ae522,4649a534,111392fd,a8baabe5,be8a8ad5,33cfddcc,af6378c8,5efbd4a2,695775bb,dc38c2aa,f8da6f45,1d0a1cf3,2bc584d6,e9b2b6e5,c8346af9) +,S(2bbdf21e,9fa1a2b2,bcd52009,7f30929d,5e2b7fa,e1038c00,8f32d86a,65dda936,51b78998,6008bddc,616c0dcd,2a883361,1806ebcc,5843b64c,b0ba02c9,339d45ec) +,S(36694ca4,d0495ed7,78230d49,e6f21217,9b63cc7c,26ee3f7f,6a7438bd,753005e1,2e49e3f3,bbdcb133,3ff1e17d,b18d5267,a87f48b6,95b5f907,5f5aa0d1,70bc0af8) +,S(243d84b1,dc9ce143,a6913bf8,d13c4aaf,6fbc1373,6bb8a7e8,5e648958,32013b8,39317ad,b6bec71d,ffe3c2dc,6dec5e10,21a3dd83,2dffeb91,108abd5b,6e7f28bf) +,S(1d888387,5255f3f6,838eab82,1a12a8fe,ba6dfde,f190585d,e039542b,2d91ec92,a9d38be2,e919f815,5d633538,d2f0aef3,9ccabdad,dbd912bc,46c2037f,3324b489) +,S(39a21f75,4c0d3376,f7560e2a,a743751d,5512d628,7a480955,6456f9ac,b1be3765,3cf324a5,2a7fd79e,3bc188c4,d55bb449,ef4950b1,3a4531bd,e0b2ea9d,4035e005) +,S(a10ff60c,d8e25df2,a1d394a9,52c67f6d,18ab0a1f,a56569e9,48d9278e,b42e21c8,b45f2d63,3ce82ef8,1dc0c0bd,4f891473,89fd2b35,f02cb237,bfd4f502,df9e991) +,S(bc269792,d5042f7,1051f7a6,46b971f9,bb801acd,dc24a2b2,5bbc459e,b7bcbcb1,b70445fc,1c032061,61b01530,c9205d7e,c7fab6de,1d83f1f4,78b35bd7,71235665) +,S(3d7279fe,fd5a3682,a82a7f95,d1db4442,5f0ef448,f6bccda9,9a61c240,9037cb95,df5226ca,e6365b7e,22b42cca,2ba2494d,9a2ea193,cd92162,2de6602f,70159740) +,S(e0c56d30,ef92af00,b936c2bd,951b3d51,bc439ad0,e4216e2f,d552c2d5,e8dbe588,c2bd177d,2a75c44,40dbd186,27669a0f,f35189c7,a4873e0f,6484b3b,68555fee) +,S(9bf960e5,2b7bbf52,fd839830,634baf1c,f0d6e332,4272bc15,d9999ae2,7873315b,b28719d,20e01dcc,b0016cb1,6a453739,551862f7,d9cc81c9,6662387,1d8d0405) +,S(20df196b,e60ba71a,e617a6b1,44757e0e,87fffb45,d447d9dc,d5480a0,34351198,9c722d43,c0d071b,a07d4953,9ad0abaa,f12ba358,42fee0ae,9960ce1f,26eb07d8) +,S(843c136,208b40b,9459693d,8154980,455a5f71,23b5f633,a26661d,ca9bac17,d5b5bd8d,9095fc40,d2d1820d,31dba542,d0c74f23,86770ede,c9b4df46,f2f12078) +,S(9109b4df,702c1ea0,dea4ee0c,ab46d6ef,708a3fcb,72f9dc03,e05bc722,408d11a0,75baa2c1,8be5801c,b5315195,4c2c3024,7348e9c7,913bb242,cf4ecc8b,96dc507a) +,S(f10cd1f5,c960a9bb,12adff03,c203c59d,8e51552e,3727edb2,329eef3c,5991113c,a84ac8e1,6f65daa6,16574689,8fee662c,9d2e03c6,4796151b,8f3f0bf1,5e8c465a) +,S(9f07713c,ffd3602f,ad077b69,a8a5f539,ae0d6d0,df58ba07,1eafe6ad,10e747a8,adfcc873,42b59083,cdc3d735,782f8ec1,cbb4ea3a,57970afe,58cd5e5c,8fee867b) +,S(6d1d9d0e,32905734,13b2b870,fff8978c,5ce9ec6c,b120d9f3,64063342,cd95801a,650be4a8,95d3b602,4d745f7c,23bbdce3,25a3f597,922e50ef,ba154f8c,6831018f) +,S(497185a5,6b3acc94,7a27791c,7e1fb149,5c649f99,dc767fa7,12820cc6,6cdcbd82,7d511b7c,11dae1b1,7e4078b2,fe7565a0,f33ce5bf,74181c3d,f5b6a951,fe6568c) +,S(fff738f9,848eeb9c,4ddb8bc7,52cd87c1,9a35f7be,14a470a2,c3154ff3,100a743f,106b8a55,cd913c97,76837cdc,f45c58a0,211b979a,17085df5,7bdff373,2f2c38ec) +,S(320391a4,8d3546cb,4466876a,37d60015,41899ef4,5dfc8e38,ccb9021b,981b4830,46f63030,b4559411,e742f303,25707b4b,23fa2d7d,e633800f,9c71205,6651b102) +,S(be44ce40,a925e88d,26937f7f,b7631f18,55879106,122813f9,adf16e00,5d85fe9e,1755ade7,d22e6dc6,7e6806d4,bf44b6e2,ca6948ca,9a418893,24db0266,2b6e1dc4) +,S(8d3f06b1,58ddd609,f83b0531,466fc2a3,da6aa80b,433a92dd,eeb20435,cf33ddae,2d554a99,efde513b,b8b6e5f4,dbd2e942,f1d0a641,98c3df2c,4c74705d,4b37af63) +,S(f814a79c,f258f553,255c1cb3,a054fcc0,d0c71d74,742b6627,210ea846,91596729,119fc95,be48b4b2,23c80c42,fd1f3d45,271ce8a,edeb82dc,31813f75,a32d867d) +,S(95e8fd46,1c37f1db,5da62bfb,ee2ad305,d77e57fb,ef917ec8,109e6425,e942fb60,ddc28b1e,dfdbcda1,aa5ace31,60b458b9,d3d5b1fe,306b4d09,a030302a,8e2db93) +,S(c06543fc,47e18816,bc720604,cfc20826,6f4e5cc0,f436e149,e2dab0e8,a7981e77,22070465,f3a4a7c2,1134819a,c194cc9d,28185431,17ec634e,e6634831,97021441) +,S(4983d95b,3716aefa,4a14d116,bded84e1,fd5b050,bd6001ca,a2b97086,b4d5c68e,1373426d,a2efcd14,333d47bc,ebd3befc,f5e609a6,6fac1b02,80cdae2c,7f0a279) +,S(a2bd5cc6,92e84b97,3ba2cd09,25e0850f,ad8054ed,e6b73ef6,1fdcc958,3eafe6ab,cca6e78a,f9141b1e,6159011b,99f8024d,33d8d797,9795aa4e,4f0b2767,e6a5ce2b) +,S(61c99231,a18f4e73,95076281,b367d084,b8f85226,3117ab60,bf698d4f,6b6d741a,82314b97,9e7f1d30,64861609,a08c019,af886db0,67d49929,9d340814,6e6cbfe5) +,S(4a5acfef,32c55299,3a7114fc,7913321a,d4072a2f,6c6bcdb7,3ed60bfd,6ab34304,7295a29,c06859b2,69bf9f29,64b26dbf,1323e89,4affa4da,9f61b056,9a0c03c9) +,S(e0467755,866f494c,2b36dcb6,c65ecac6,604e5013,4216ad48,7d4f5b68,bb7f4023,dead03c5,974dfa1b,f532f955,826189ad,ae945975,28ece029,d9e5a42c,30b336b3) +,S(686fdf05,c9265fdf,5b54ca74,b5b1e231,c4e8be60,20844596,40dc0d2b,bb215ea7,f4d43e1c,edf9b974,aef950e,bff3677b,c93723f2,c5901710,b6561e53,d57ea7da) +,S(12476985,f9b20c,ea62b7d7,b0d96d2f,e2bcd924,d1f15cb9,fce5ecbb,8bd21253,d3437cb3,9e904fc6,43a7b356,b4389c0b,3f1950f2,43dd7842,e32de16d,2b522004) +,S(656bdcec,9a87c9b2,e5b32291,9f657b,b5eb1e1e,d2fa724e,45388026,2ad17b1b,c7748d0c,47b6e4f7,f63f704b,3fdc08ff,cdfc830,d17c1d11,6aa3dce2,f92fa64d) +,S(5b7f710e,f721612,ca79b24,483ced12,7a3d403e,60ebc04c,3aebeb96,b483e4b5,1b5157a7,f0688aee,634196e8,de5a9eec,11db4b72,bd96b86b,698f7284,bfb08080) +,S(bfca66d0,552ba6f5,5795bf18,40f90d85,2545213d,81d2cdea,bc8d3d04,a1e6b4da,4910b0cc,290d4257,c04c4638,30cb3a10,223043a0,bddd8690,84b6dd1a,f1754e75) +,S(663fa68f,b79dcbbe,4798f8ef,2057bd14,1447c11b,cec70924,7f566032,88496f16,3ace2efa,1f3bbd23,d885598a,91d1f420,a42597a2,ab30f951,f2b27aa6,83c41786) +,S(27cce333,8a3db8f2,1fef2f86,ede68a25,7c1675ae,80cf004,6085f1de,495c4321,7156bd8c,8babe472,eb144a00,263d4fdf,7aefa69f,da2a9c29,4b16a82b,24d373ae) +,S(d49c06c4,52ec5c09,be27940e,13c575ef,b727be4b,1c0e8ce3,5aa5bc4d,64bd9560,2c653518,b298100a,72a66469,f9a03635,5d7ad789,546df3c2,f5175238,e78d18b0) +,S(6960807e,bd028026,d3cecd25,140d3bd6,3d7bf4fd,10afd129,cfa017f7,544ad08a,a785044f,28befae7,2b7cb546,5bda6bdf,6f9a6383,aa9abf4c,f7baeac0,920e7c7c) +,S(8382bf36,54b63a71,a3b75abe,ba57dbae,fc9263c5,5a54faa1,edeb5325,3df8faa1,e05eb9dc,c319cb53,4461da26,9627661e,dc9bef3d,945766dc,b6d0fcf1,849b011) +,S(803b9379,30ec876e,78c8cca9,fa932f22,b7e7059a,f605379a,cc45a3d1,4ab0bffb,7db2341c,5202d1c2,c6fec7d0,b7869471,cb90d1dd,17cd1152,b52af046,55e5790f) +,S(cb4db129,ab6ae9f,11165ff4,f73fab02,cb78fb0e,89647e08,f998ba11,d2c5292c,ccaef9a,1776384b,b5a80ad6,5bb366fe,745c2408,d754ee9e,f6154188,d7d1c9c2) +,S(fab28757,50e9f672,3838be8a,fc070fc7,8fd6af7b,84c9579a,a8152acd,ecd115a2,d59fe028,89cd562d,5397d3b8,3ae85303,764ee931,d9c6e495,6c311490,c0b3f065) +,S(dd879eba,f870ee3d,f6e3f03,60833e12,fbdf844c,d6a5b76c,19802485,6649bbc1,e7bb04c3,a99a5c11,dd7a324c,1d5696c8,b041a12a,c6a538f7,3094716c,9943c55a) +,S(7aa4afbe,29b06a1e,da9319d5,72572b13,1df68f74,1b5f8d8e,d00ccc04,80f8016e,15f79a9f,b77b8587,7f02e3c1,3202b972,423fd00d,937c0d2d,c9d94b17,53fe7130) +#endif +#if WINDOW_G > 12 +,S(9145f3f5,876a3265,5d7fee64,f2d6e660,c34354e9,1571d68b,a19e4cc5,8c39f890,45e0a95d,cf369fd0,5bc9ba7f,da108d6f,37650c1d,5766b6c9,98ecda28,b285d8ff) +,S(5450b752,36fb010d,1ef37afa,2077f3dc,a5a7f6c8,91a21317,13df740f,4511ac9e,a7c7ed57,62bef86d,4de1084d,3ded2d7b,13ff563a,67109ef0,8b6f0180,fe6ba175) +,S(ba6c0d72,5e48de2c,cb8256d6,7417d075,bbf2766f,d13501b8,4c32cf88,c0666a0f,cd2a9132,7137299f,50eda669,3a599032,bdecd64b,91bcc640,5ed186f3,e525e442) +,S(b94821ff,1feeebb4,6fd1006b,798fbdf5,d25a649d,aad58b05,53adb7b3,9605c921,21589ba0,79bb92a2,c7c3d950,c574afcd,f45ccfae,eec4365a,72dc58ab,f9077c05) +,S(46436446,497f7c76,b70e0d7a,5a963cc8,432f14de,93d61f81,98f8879f,da0681d,29344a4e,90c4d810,10da8aab,5ea25e5f,48c367e5,b3ec9239,8a3e608d,49c83a5f) +,S(bd8e5ac6,a2e210be,4959185b,7bcb0cf1,bd3be917,5baa0d08,2bb0b38,e4c1ae1d,6b381be4,6dcc9755,da7773a5,eec888a6,69c6f71a,7c3ec0a9,4edf08aa,2db1fa19) +,S(c27c1e1a,c428a7a5,f4c3405,58ace76d,7fb64593,b3942319,74cd18ff,99f22493,fc95d661,c12f7b3a,1cb1884c,fa9f5994,25108af3,c05e04a4,9544f919,c59cf57c) +,S(a1765561,837e4ca5,5268168e,ed90466c,44c17c01,4040fc02,9e53af22,77eb30d5,8a2dede2,b8d488bf,cd655d0b,30e6e2f7,3032c775,d0fa15ae,cfc8b66c,b6c28eb5) +,S(2bf6930d,35cacc91,87e9279f,678ba368,e9eea737,8300ef93,b03e98c1,81ddbea9,8a2d4d43,3aaa8bb0,3ffee538,38ca8e16,d0f3f930,c35d0db4,6e2fa069,e625209d) +,S(766a7725,9cc5aefd,90f005f4,d6755924,a36d085a,3a5856a,6a066a70,6877c8d7,db3fac97,4fd9e2e7,f05bfad1,5e938ef0,90b46d04,bf40bdc4,171c1e33,48049190) +,S(d25a60cd,953f21d4,5fcc333c,97329da,134196e3,9a913242,88792ff9,18461268,89bf2be8,bd04835f,dbb874e7,12352980,a07ad598,9035270f,397f24ce,790c7863) +,S(c7b28e9e,8b7125b2,6721f467,b665eafd,9bf56986,8ca180b3,60dfe429,cad95d68,ac28d58,b3a7b61a,71f947ed,8bca4768,9ec7bf34,7646a167,b37c3aa7,9e95c398) +,S(e2a5208a,c61f8c29,ada2d7a7,4cfce985,20c29160,f1f7a97,ce8b34dd,7c59bc24,de8ba14,49708a04,ccb4483b,4e90e383,a152ce27,777fce4a,880d9f5b,3dc70830) +,S(e1e8e4cd,2e8df95,d3b2b152,a540967d,6976e001,88287ca5,3e844adf,12e205b1,a5dbc6f9,cdc7547b,cb3f0091,8233723e,d34aa97,f546a491,a77d8cef,43bfc9e2) +,S(b20f0a51,4a684e55,2b228480,3f3b1c2a,3c3143b3,b895f75b,ea454bf0,9d2b6512,bc3db380,676d0d14,9c0b0c3,1941a07c,f7c95585,cd44ec31,e0edda96,e80d02c9) +,S(b71a6923,b1adbf77,94cd9d8d,407ac96f,9582cbc9,b9748949,9cfa696a,c29d3ec,75182389,9d59558f,b93113c8,d38c72d2,7b6d75ee,7679fb1c,89bfce1a,64b4f151) +,S(ca47adac,68ba38d1,8ccef76c,7565788,a82f415f,d9dda65,2004d5c6,a7d3bb3f,bd598ab,1b783382,44035385,e165fd5a,ccd8454c,3969d378,42fad050,4a8701df) +,S(5fade148,aceee9ee,9fb4eeec,edd5e4c9,4fb989c4,d99f65ee,a45f99d0,2d40e441,ba6fca3a,e82e5581,ac5104ca,c8dd2c00,15afd1cb,3943f1cb,5e61dd4c,d73831f0) +,S(767dc9d2,14fccf00,900ba01,a1cf30fa,e834c851,e633849e,8adada63,fe5f84e9,8c650911,aca30e56,2d6b97a5,2fd7cbfd,1a5bcd1,9ae197d6,9ee3215e,64404639) +,S(27f8f961,445e2a20,5daa7648,fc3c06d9,523544d7,477e686b,91d7178b,7e4f06d9,39110b27,82cd7be9,75596bce,6b79e5de,37933242,21be8172,427cdfd,82a73a60) +,S(8de0485b,343b5e4b,ada4070b,f79dde48,8a1b2889,839735bc,6a403165,3f3de668,90305767,6f43e0f7,d7d1c1a4,4c59124a,12fbba72,8302e143,1c2cfae2,ed5729d) +,S(5e43db6c,8770e8ae,8ab8d318,8e700c28,5b6af8a1,eec95fe1,b68e5cc5,952df0ff,6882628d,d85fbcfe,3a2a1091,15e59c78,9aeebb3d,37075f90,ae02a439,506c7aa6) +,S(7db2baab,4bf4300d,e7caf4e3,52df1423,6102863c,edfeb7a0,89311f42,59a4ed54,387561a2,fbbb44be,303fbbcd,685909cd,e553a84c,a3d4c0cb,d33026fe,178e8a84) +,S(96da8525,39106887,d1fdf9e8,232db3e4,cdbefdcc,abb90bbe,fc04d10d,b996c6f1,65271a31,49127341,9fb5d651,3dc33287,45948c92,b7fcc509,4023d300,9bf9edcc) +,S(84eff1cb,14eb1284,f5ba51fa,838b485e,64cd4c0f,acb97b00,81ba7848,add50613,926c9c8,a6540a8d,7b1e602b,6ce818c7,609f4511,32f284fc,7f578c52,a6febd9e) +,S(1eb96371,8e2a2a0f,f5ee6000,7bf94a00,416e6a92,25d59705,7fe19941,e2adfbbe,4870aad8,9635ddfa,dc3d4011,500639aa,d3cf0523,cff0dbb0,9c9f6a1,3005b855) +,S(2994a36a,6ea3f307,1bda8696,baf193df,c75fd4a1,f7683fd8,d454dcb8,77bdc098,e465e8ee,a7273d4c,d786bd0b,e96fd3e8,9969bf03,1b76aeb3,fc82c4c4,bf856766) +,S(25e7fb70,f3d357a2,c29b115b,5e2d97d9,d2b68fcb,b7f2fc00,237758d5,9d26c414,4959a370,a3bb895e,bf530fa0,13a86ab8,206b7330,efaca631,fed26773,f356c4f2) +,S(fc387f98,80bf6bbe,19f0481a,2d66bc5b,c3945c52,588860ea,dfd5893,347c0623,89d7b80e,83caf032,9c9d07af,734604aa,7ba33274,c1081ff,16e663eb,b49d67c4) +,S(c50af368,c25fc9fe,10220fa8,2f177720,ef5c00b4,85fa99,e2f03294,fe01e1d4,c3777b26,41983094,912b80d,9e94af7b,27d9f10f,978c931b,a4294cb6,b97579fb) +,S(a0f9d109,d524d9ba,a2e00362,941a5f20,d7015c48,554f5bea,7c97617e,10041f94,28c51c02,daa5e108,3e77ff2,c64560e1,2eae923a,3a699f1,daf3c62a,710e9ad0) +,S(41d12627,58b708,58727138,908004b3,2138bc23,cbcdfa28,5da862a3,48316de1,6277029,153bcec9,5c95f451,3d515d90,341d90a4,90eb2999,d29df4f0,7c1b9673) +,S(944c58ce,fe6cf543,d93afb77,e33adb3a,5774448c,d61d621c,fb80f856,6e99ca87,c071ec1c,9b7fc717,ca91c557,66bcb222,3294958a,b7d7b056,e7473170,284d66b5) +,S(3307ab12,e06d71ce,9c029bf1,88eebb58,9a27be59,17290635,f2babebf,be437145,889c3cb9,28cf9091,4748dd9b,f89e9809,4a6966d0,97d854c3,c6b6a949,c861d213) +,S(42baf011,1eb2224e,4ca03abd,24307b6,72b74572,2db6a8ac,dee1b7f3,45cb9ae6,bf32e128,d608dea2,4b564bec,38d22c5e,879515d2,7379cb11,25695faa,2e12ee06) +,S(856baabb,be7b4bd0,49fbe898,110d1175,166c69c0,a2bc86bf,e734c854,c402c621,24a323c9,b6d31fd3,cd424161,be229e89,1f825197,9ed4721b,943d7418,4912ac0c) +,S(38dbc18e,d998143d,874c224e,de83834d,95aeeadc,805cac1c,3216d830,d24a9cd4,273ec126,f7e85778,ea38e2ea,a90ab5c5,271c6eee,7f934a03,570891e5,167eae4a) +,S(359dc85b,ffcd2329,3166bb20,c8374af1,5e24c065,a5f48d92,c981eb81,4988dc59,de5dedf7,deef588b,90237d06,e437bdfe,c4a05703,f77b584a,e58162fa,bfbf39c6) +,S(e1068065,e2e91c88,3de2f73a,6a5f3da7,1d872a74,9d7f283b,b1ca35b7,c63666ea,810c3a52,b5df0851,87928afa,a7685035,a7e155fa,c8dd4523,c5ed1f01,73752cd9) +,S(50767f71,37e7c9a6,6e317183,cddfdfc,b564956d,cbd5f3c7,fd8506d,dbb19d83,e1c2dd5f,5e9d5fed,807d24dd,3f2e0ae2,393b6167,563db9fa,5e338664,a0d4fff8) +,S(44cecbf0,cdf88355,f1216d0d,efcea137,5e01275f,f53d8a72,5a0b5980,e6e65864,469568e,5094e9b9,ee290550,e67bc636,e8e3f022,f15c8d2e,25c3ad6,f51cd40e) +,S(1830c8ea,43cbb07e,fa2fe597,6afca2bb,87ed8a92,602e05e8,86ce3447,96aa18b3,df551328,e064aee7,67f56203,7fa31c11,3c8ce2fa,b5445d65,8bf1dd84,2f1da49d) +,S(58d54b7a,473a7ab2,51c33417,a41f4ee4,708245ea,c4f8eb79,9bf90aa4,bd9326c6,a191f338,15e2e07c,ca4c331b,fb1cb2ca,a1692093,faf5e789,1ce45a2e,c49b2a85) +,S(f0dce604,9507812e,603af104,42fc9861,70a18b6e,44e81af7,ba192b55,12c6792c,c221a221,b5e86ed6,23e48df5,5cf0f560,4cc60eb4,c54d95ce,3ada7b09,bbb966e4) +,S(42fb09e5,72c0302b,286aaa04,644b26ae,e4b958cf,ae5293d4,d7ba505,59dd2ab5,79c10573,a00e6888,fbc7ec76,983e7d17,f46e1ba8,27494099,43781bd5,19f2299f) +,S(7adf1551,ddae0c9a,8628db25,e0dad10c,935465bc,88b005de,ea2d0412,f3502ecb,657c0ba2,143cea92,c9ef5c3,69d110a1,2e031e67,9d945a9b,b3c5886f,29acfd20) +,S(dd5a48b9,56fdf6a6,d6271310,afc2f5bd,3a7bfdc9,28125345,4dffd91c,282c7a5a,7390d037,2e067f36,fab52107,29590464,2ac6b0cd,91a161e3,85df7cc1,7ca52657) +,S(631850b5,15374594,be1c783b,104d2ce0,13ac763b,1d36301d,2936c2d8,8a1466fe,2490ce1e,a9b67e44,a9241416,c5729ace,2016d202,b77efa85,78bf3228,c590882e) +,S(332e39e4,d6732229,aed85cd5,83a3ccc4,24466869,bdc6a8e0,741b0e06,f3a0bca5,90840f03,60db52ae,a58b1b4,74ad65c,b66ac30d,f4d465aa,60244d48,5d7578e7) +,S(de019556,f0c7e1c1,6567224e,826532f1,d72c0eb2,91e70980,de7513a4,362f0c97,39a3d79d,1cc98840,4162227a,66382529,d75fa5bd,64977e47,9af0aed8,68411ea3) +,S(4c44c0c8,e675da80,c6ffbf2,61b82e46,4f81b76c,a3f902a6,8094a2cb,24ed859f,59e1ee88,1ecc45de,d8ff2800,243ab8ac,c5497897,1a3a2246,63b2b593,e45ac34) +,S(e60339b8,481e19e,5c1f4d1a,85ddcec6,a4e0dcbe,113e7d28,2793654f,309286a2,9a1eeacc,a607941f,b51c7998,ed2a5a98,bc58e3f9,4737f72d,1b08a706,a49d9d59) +,S(10f42ee3,e7292f3,9f4bc472,708ffec5,c6ad955,c5285be0,d44d02c3,d8a25e66,6f365f0,53ebe28f,db9d5f2b,c967cf14,183e130d,f01a5106,34a975c,ec916a25) +,S(7e0931ca,d1286687,7d2fb0f3,1c58d74b,703af3ae,593a9548,b6d53d86,6729c445,5e4b9fc,75eab41d,663caf7e,8056f4c8,a9c34af9,22773ffb,f5a12174,1f6b0b0b) +,S(36313f7e,f9613c6c,ff14cd1a,78362f7d,b7f1acc0,db278f1c,6a011435,57963f22,1e6946ab,2a94231,4ac6627f,5494205c,33960a91,ba663a69,8641bab4,43d7edc2) +,S(223eeed8,8eedc265,b2e18a63,ceaa2e9e,83f9ba59,2fc8f3bb,55cec574,2869fe01,72122173,4ae29bfc,f3632cc8,92d72d10,5593f7b8,d687e5ff,eff21095,c1bbd71c) +,S(b6bd5ec9,33302c5e,3fb9b32,18795a11,60dbb2bc,f9a9b5fc,31b7a5a6,28b3ec20,fb9f87f2,98a8e5fe,b15f051d,63b48bac,34f373f3,9b42a42c,d0f1d2d9,c99dd495) +,S(a365a760,3b1c24fb,157f69ce,8fd57ae,5a802dbf,3b8f92c9,8f0a8b01,4ce24668,73e6422b,7746abda,2a38cc7,298180e6,578005b4,1b0d3760,6f2bec3f,5c0a89f9) +,S(e86f8fac,43e436f6,f4adba30,9d446a07,e24bac1e,d379e13a,a235ca1e,d7cebe6e,1e7bb4ef,aa75a9bd,713a592c,42a01134,930b7402,77cc1cd4,a913d6db,f4a166af) +,S(f5a57dfa,d2b25571,dad09304,cfde1da2,5cab3e2,e85895cc,9933e868,eb9bd5ac,d265de5e,8fc90e41,d518dad2,68997a24,8eb90906,becba9b0,c768c5e9,877c1ff6) +,S(e1dcd17a,7259374,7e6fd8ca,b2464a9d,6656607c,a2bf10f1,45c45947,75f76c7b,efa2c310,23f86d2,6489feb6,1232bd44,dff819ce,6b95678a,130de640,4af9a20e) +,S(83f8abdd,84996ad,1be74b80,a05da9ab,cb3774fd,cca40c3b,afd386cd,cfa801ff,267ebd64,329fb10e,56ee792d,966868d8,1c85ff37,6aa24287,3d37c63a,4bc3f63d) +,S(54cc9d79,6ca85a90,27487c71,6511485b,ca324b1a,f34fe454,ea9ba54b,4f259fae,18c39071,ef0b861d,7d016e9e,deb6e83a,3c6ce8c1,517ce0f2,e201da0b,fab2da2a) +,S(9d12a52a,e0091654,e2875ac1,b11f8744,1c3f65ac,940e6a51,6351c219,31402ba4,5ffbb334,276219af,418d17f3,9f5fad63,c6d7e42d,d53f6e52,bbee2f01,38e81d2e) +,S(659bfdb7,66821a91,cd030917,ff21aea4,488007f0,b7ef3824,4f23b665,e6496998,2ad708bc,4506e85a,341ef96b,78bc247f,d7321a8e,bc21f9db,8444e7ca,254784e0) +,S(e2cc4e33,d4a08374,4866a253,81549a6a,76ba1b4f,57312413,d601296e,5eb4670b,528b2b25,347e7f34,7b79780e,a64c3718,80497f0b,a3b28e18,79714089,a4672476) +,S(529c0b9c,9e21c36c,9b2164c0,fa505601,e8dbbac4,cfcac131,e248e103,e6edec68,7fa44821,72a0ca01,33669e6a,bd2e0386,6cb74f5f,9cbb7e4,96770129,567125f4) +,S(289d6baf,be6a7d1c,154d6532,a80fb65b,3d89e57b,eb2f1dda,654d3553,683b63a1,801f4863,d3ce8d61,60e2c62c,55267877,db149ff0,c3cdbb53,38893168,7d005400) +,S(d16a84c9,e3b36660,7c005bb3,425ab432,2eab9b58,58d1899e,51f2ecf0,790f14a1,2c6f5dab,10715328,f9ffc5ea,4ad6e0f1,46ec79cf,19cb2313,79bb5198,5156dd07) +,S(d03cac39,4fdabfbf,440a6893,e410dab2,a7062df3,ecbe1aff,d30703b1,6b6348f1,152313ce,f530b317,70d85c08,7de95b1b,5dc4a86d,38e4040a,e5d1ba34,449d19f9) +,S(b66b3c6c,a673c4a9,77ca7d85,1f969858,d4c4b604,bc497101,c89207b5,a863379b,54ba2563,771c6aac,482c63cb,7156961c,4b00faff,187f0560,9a470b06,31624da4) +,S(be684644,11c47cd7,dfd3127f,4e7d7308,2a43fb68,79c5e86,236e4516,a98c8c45,79290f83,c61d368c,2e395aae,b9cc50e9,7d78ddc2,fdee9f19,a15fae85,826733da) +,S(478ac3a0,9f34f463,4d61de70,9ee01878,4ca421f,91d20e0c,5c62bfb7,afcbde9b,f752f3b2,ccbc1096,1ae7526f,5574b5f6,e4bc7d4b,f77edc34,f6e37655,7b0988bf) +,S(745f355a,f2ed71a9,9d4d7de9,c9bd4a13,7271a2a0,c077c796,e7064179,8f3dcfc8,18ea61f4,6b0c87f0,9ed05816,c31e1992,d4fe8826,630c5f6e,67242063,86e288b9) +,S(54e7bd6f,9878a8b0,909aede5,9286427c,a3157a8e,38ab94b2,18db520e,86760ce0,d7b7d80d,c1eab448,d1f3c638,675dc0b4,e04bdba5,340d3f2d,dc1d24f3,dfb8e49) +,S(ff56afb6,55eb431e,f3c6f5c7,fcc6febf,99b018ae,3dc95208,27e4a01f,a07d510e,9bfabdb8,42d7b1c7,9f4388b4,49a28d6,ec723210,ea27509,655e9b44,2591e371) +,S(c2bcc099,ed298fc8,679d0250,6d8ea790,ea511b41,5ff3ebe3,82d39d21,fe9cda72,6c7bf2c4,35ecf773,5b6c31a5,82fd38c4,825de4f0,58efb070,aa1ecb58,530f971f) +,S(2383f144,9811331e,6c54b9a1,19c681fe,c8549e36,890b80f5,99a80187,8e2ec4e,13ab411f,d846b6b5,938a999a,704470f0,d8fff03e,9dd35cb1,44860a0e,3533a1f9) +,S(2f2271c0,d4fa1d8b,f8a44584,ba4f5266,9953f72,89de16b8,17d6bf0c,6e096048,646ef05d,b40e50e8,b6f32c22,63ef553f,a957173d,a6f14ab5,7cd60b60,2fb42fc4) +,S(b7b49b1b,f067a27,eedbe55c,bb011eeb,809e7ada,ada5d08,9bf1f26a,25e951c7,fec760ac,a8160525,ad6ec95c,7cb0329,4a9b7f20,45907331,5b2dee2c,7f7c33ed) +,S(36c8e1ca,d9933e5c,971866eb,12daf81e,7765d4cb,9f0af557,94181aa5,e5409223,895eb6d0,a0252282,d71cc730,9116ea95,22f2e35f,52498853,e291932a,dc8b8fdf) +,S(3cbe9127,63283592,13173e01,8cc2b0e0,fa5b8a11,5684cd6a,6ee7bde3,bc1ed523,7b2d3dc9,22f96c9b,9970aa9d,c75f1e15,c5149ab3,d0993055,e4df30cf,ce44dc81) +,S(76e94d41,c4639419,23448d38,8b239e6,fe0aec99,314ed16,4e67e97b,6247213c,8ee4a6c3,2f010749,103460bf,d75fa38f,f9efe4e4,bf283cb5,a3bc186b,21fa1278) +,S(493925cb,12c0e7e7,a15749e0,7fe9dba,5b265d27,8eec0c08,506f3433,4b1851ec,70900c6b,3960a465,6cab318f,9f730a66,e4a2400e,d819da9a,1f5b16bc,4c62b193) +,S(b37fdaba,d1a1393d,79820eb4,a4823d38,3873306a,a9f5e6b7,51173aed,c43cdcbf,8506f0de,57fad139,e20df67b,e30a75a7,dfd325f2,9e35652f,28fec608,f27030a) +,S(4451960c,48e95c49,32b0c90e,9f90724,8f2b7023,9c6c703,3c29f4a5,b0a5e8c1,83553a18,d9ce9f66,961c9e1a,47766a4e,28bc404f,bef524de,53b1b687,d4d7490c) +,S(9b0ac1a8,697c69b9,80cab20e,b3f98718,7857721b,c869c704,caf63b80,dfea3449,210e4c53,bf1bbf3f,71e32a58,22809853,61b61723,72e845d,65b5661d,fa7e60e9) +,S(e6697355,ccae4f7e,122d62de,1c00dcc5,32eca4fa,7dbd1ceb,e92684b4,781e0851,1f122cff,dbe5ef3c,944353f7,18806a82,9d3ded91,427bdda1,736ba236,e9bdc3cb) +,S(ffa890f1,1a1aa4ca,1f26f86,1288bac7,ad480a4e,1cc47f2a,fe39f260,f167201e,1688dfc4,1716fc3d,473c4149,77d0ee8c,ac569cda,b01ab5d,69d449ca,85439cb4) +,S(9088d4f2,6ce238cf,63eb09b1,5c04a04c,c41fcc57,946c6c14,cd3b1b4f,54b6bf47,9e380417,bc61004e,3d5c9943,b3d359db,2ae4ec81,2b70e909,bfe265d5,e4d2c2b9) +,S(a3395dda,c1e760e0,fe7f7e5f,e3296bc2,99949ca2,2c034707,67c08ffc,d9576a91,4f8a879a,54036d17,16f07016,23dfcfba,3141850c,de003870,6ee940e7,452393d3) +,S(11e54d64,54aac1e0,58cef234,51f686a9,1a77300d,75330ecb,d208678b,2b8ad651,749b2960,5a8419f9,c5521dfe,f552a24d,aaa53f45,908d2679,6e08b396,c9c020f2) +,S(c8f13cdf,7b41e822,ab6d5a70,7c701d29,98f5f0da,8e4c9746,75e808fc,64327f30,489a69df,17c09c1c,dec329d6,a1f34f60,445d7aad,d609d94d,44168947,c3199404) +,S(9175e6dc,cf674838,a94c628c,3e1a9dc2,9487f394,8a25e50c,d463cf6e,92d84442,8705713f,ed377125,51e37e51,78ca7a7,c13e193b,1b83c729,8cb08186,8fde1c22) +,S(8eed492f,b6d5f0e9,ee78f6b8,953ac0a0,5df8b9ec,db17a09d,22863bf4,3620099b,23a45742,17672e98,d9cf8b94,62439ec8,82e1acfd,40db9d9,9ece889d,1e0ff1bf) +,S(cb95ce23,dbac06ef,79793f54,3105f5d7,5cde90bc,651b7756,bebfa367,d5c2dd9,9612a6ba,c023b0c0,73b5ed99,271ddeb4,57d22d74,f7ecea4f,c618be5e,164a9a48) +,S(667218c8,bbd3ff37,d42a8ebf,8b5a7791,d8fdd493,9ce447c9,15dac906,67a3c980,dd401179,9e19afdd,46b44aab,1055b554,452a21e7,6750f2a,ce98083c,8cec26c0) +,S(884799c6,ded4851,ce53e855,dc429698,c7627b5,a0ed3852,19b59af8,32f76e5f,238d75ed,84e6408c,79cd2d37,152cde78,22e34377,fccf05ac,3afda662,c21c4a6b) +,S(8d98f36b,5fc0082c,597a05ba,e24ea6d2,2cc8bb90,3a6bf96a,256eeead,6911d143,26e8ad1a,fc902a6d,505b2fcf,80168177,de7df931,b1dc1516,f2ad7b38,e0ab3cd3) +,S(ba9062ca,c0ddc6d3,27f2cbc2,8d2efb1f,d1db2388,c86741ae,7403f4b8,3fd70ad2,473b335b,3036e754,d9d71ccd,53814455,674539fd,7b73d9f7,8f24bb0a,668b38c2) +,S(4a2d36d7,3bf0e609,7a69be67,55621051,7453ca53,11e288f0,1ff1dc03,acdca29,ff691e7,a90331cd,44e83ac6,c298f5fa,9d694a81,48db01d8,595a4386,b2ca8f83) +,S(e868f28f,d99c4b16,b64fb0d9,32c325c8,6b365136,e0055c4f,cd362e1d,cf37d0c4,ad3346c1,83401034,8e8e0440,e889109a,40e5fa11,37d94bca,2742adec,4cd4a9eb) +,S(6c5e8689,830a6ca5,12d77e56,62239833,ea77e696,da40feea,99da3519,2babbf3b,c29e05f1,5fd00b60,7bdc333d,d753647f,56b9830d,46a597c1,2bee9465,ad779540) +,S(b3a172a5,6ebf2dff,aeb91cc8,6b9d5a91,c317adc6,250ae210,e1a25bec,1c046344,28f6dd79,9ae40d6d,bd31fff9,9a4c2fa,f06401c0,de7a5d11,9fc5faf8,3b4ac3d5) +,S(198f05d2,e2f9d779,a50aed67,39468a8c,ac4a22ec,71a3ab80,91a94e2,100d8f47,cc6a183f,c9813173,caee3baa,f3fcb223,39708968,360c02b3,cbf899e0,b7cd0414) +,S(78b2178f,a943a791,4bc7703a,e850b07b,41ae77db,79fc39d8,9f0f50aa,b077f48e,85fd3e34,f2cbb092,8bb89004,e1801be1,d78fceec,c29eabeb,11b9cb15,625274e7) +,S(6f70df00,1d4c5175,f997a795,170e28a4,23828ef9,d5f72dcc,cec43fb8,d9943f3e,26315213,b77183e4,5979c34b,e730e2a6,956a66c2,a8dfbee5,f0f1eedc,ce8315ce) +,S(ab813144,3c946d29,d05509e3,b45b5926,e3964b2d,1cd0fbd0,9cfc359d,b5a9dccc,aebdf315,c1273af,a0c5e587,6c6c6132,16b7710b,3f505062,6336ca9,4f5c5ac) +,S(e72b3791,dd751127,23723e62,77e5e6ed,6b94569e,ba3321a2,a3e883e,5473ad3,131b3074,2dbcaaa0,fd3109ab,5e3a2847,9c2091ac,8830b19f,d4df4c82,9b28a3d8) +,S(ca5a3296,ecdd5e50,38fdf77a,fcc507da,bb525d7c,56b50c74,61928432,ce3ded63,8bdb6794,b7e177ba,9027258e,42b365bc,5a14209d,2bdce54b,3f865fc6,ffc4ab9e) +,S(eaa3be9f,26ae8cff,cedbe59d,cc03b202,e7d2e815,a7269819,c881a700,e31a9222,1b5a2cd7,a367d34c,60ef8026,51df2d3b,36ac2816,7cd7eacc,735e45c1,9524ec1c) +,S(ebba882d,3ab4ff6a,e162076d,56791554,35a011e6,74872206,582b03de,e8e97728,94222a05,3d8dd2cd,cdaf2ac7,69ed8c65,8a004c58,7bdfd0ed,355be474,65be16ac) +,S(ca0d97ea,537a5dd1,147cb784,eda20601,ba88e0d1,68e8df44,6d8923c8,353aba86,874ac858,89eb86f1,88ebb979,a4490e65,96c97892,874b5b68,167ca99,f68fba1f) +,S(51de2965,34807454,7e798f98,c5fc19c5,84a90a0b,e1dda24,7718cfb3,ebb797e1,81374e3a,ad04e6b9,55c1952b,fcdf9a7,ace0ee4b,99e487bf,87ceff58,3f7c54d) +,S(69d611f,3136be2,cce39bd,a1d48fb6,87b52135,365b8df5,151c6ec8,c1387509,75e2d820,563b64ca,c4d127e1,a5c29720,fb84c0aa,6b271fa7,e9da2c22,3195bde0) +,S(31163083,558660f,23220456,1dcde218,6fa4a4c0,de450a9c,b2f57de1,3a8aa135,25d6d496,b1341688,3008c636,ae1d8b76,29e14207,e5fbb817,539ddbf6,ff46eae2) +,S(84e342aa,efa9cd4d,4d82e5b4,4d0ad9a9,11f4623d,8684c274,cc3cd561,2c7ac334,fb90dbff,260f9997,c84f0a32,4fe5d2f6,f552affb,50aed654,fb8f660,2a92a70e) +,S(95efc402,969db115,22446814,3d4e3aa0,fa812aac,8867a5bc,64d66692,67bf6562,315bcc31,6149252c,56f3f00d,445255fa,e4567d9f,d77ab4b3,e82c1359,9f371883) +,S(5223623c,f87e9da0,bd289993,a55bb9be,55b40149,bb501507,e3d5c9e8,d78d07fe,6bfd0a21,dd2df437,9b1c16,b0076f88,832e921,db3cbdef,fa5808fc,ac613b56) +,S(f212d199,a6b707e,61d87ab3,109e1178,ea180f19,66b0a55d,f5f4cb97,c0cb0a4f,3280446c,7edbb64e,e47c3142,7c0ea113,7f511422,19ec3824,12f6b069,a0f7c84b) +,S(6b55063f,f25d76f8,2154431b,20058f96,e1e19ce2,210e1b50,835cf58c,31cfe8c9,5427dd62,36450762,46e99707,6f60ffed,558a82a3,dd7e31c6,492088da,9069f8bf) +,S(2539e5cf,a09690c0,12ae0c12,9e6f8b27,c9388d41,a82e1e96,47219d57,9b23045,94cb83f6,1e26a4dc,d1b93000,e5113347,31d804b4,a259e24a,77c457ad,d9021ac3) +,S(aa9b6ad3,4aaf1c30,71aed31e,8f28c3f7,9106421e,f0e53fb6,297fae54,a47d51d,dc0958e4,9fe4ae00,98b4e7db,10ab4d99,58cbd87e,ca443d3e,d1c9de77,83544c47) +,S(a850a313,aad224fb,f5ed6e63,5a3f6b0f,e08964e5,e9ca262f,780532f6,13487ad,7e476ad5,4cbc55c2,3b9708ca,eef3479,d9a5e570,26f15a4c,43b83363,ed4ccbe7) +,S(314fec33,f4fd0c96,d04889fa,b18e29d2,5da44132,6477b28c,7418d4ae,5fa09c1a,a2c9513,a98ffb7,9b5dd4f5,49534fdc,d9b4b8d3,1aa6a4a9,76b7b94f,ebd25f42) +,S(6e7dc3c8,9ec57e79,ae1581ae,1afbdcef,183998b8,cf52dc85,329afa35,4f3d0963,6b636805,6d5a8e9f,9651e3a8,2598ab1b,8f057830,fccac964,a83515f8,9d9f87d8) +,S(596df693,fc4ba2d5,20497a50,7d1841c4,38b6cfc0,58c274ad,9557d400,573337c5,6609ede9,1c97b09b,3a171723,e9ec5cfe,74b75c33,d5ccb3c6,bd03fd04,b37f4ff5) +,S(235f8dd4,10de7800,58550e8e,77bd42d4,ee3ba259,97bcb4ee,13cf0a12,14e8afb7,bf6e0ccd,b8e8e136,800f8d59,510a4455,53b72c33,824b71bd,189e158b,6cfa6712) +,S(a9d8ba97,b259e91f,125fbb28,c80e81f0,3f03906a,ef692279,4c9cc23,13db8b47,e668b954,19b580f2,67472afe,221f5895,5e6ce6f6,45ba737b,f14754ac,ec048656) +,S(53b44dc6,4f74c170,142bc629,51349b88,c4e2cc35,b7d9a7f4,827b07e8,4a288e5c,4aff13e1,404562c4,d8f006ea,dfee7b5a,32216187,d8c96c7e,5896e157,97075d4) +,S(76e6e038,a6baf9f7,1acb56ca,1494486a,a9187279,486f62f,947b5a02,580af1a0,51a5d110,66884076,44c18348,be045910,3b57e0d7,2266ffd4,465e6b4d,e1f0f0fa) +,S(7761ff19,2fc70129,10eda284,dde864bc,1c45b4c7,1bec7870,2bcf53bf,9bc1e6b5,80980f70,a82f7196,61f7e1fd,fc3e7d37,e0bb75cb,b1ab47c1,d947dac4,296e11b2) +,S(8199c9d6,1224f51f,e6dcdc33,3869d860,95c0bd8e,210d2d7f,8fed2804,a89aadf9,be89724f,5cbd2384,ae9bbd73,f030dc74,a158ee7d,2a9d292d,daae3057,4b1ec89b) +,S(15c279fc,760a2556,bbd62680,3bd779d8,8bd4dcb,e508b3b7,99d27405,91c55c0e,fd5038bf,ed80c963,4915d352,bca040fd,ea0b9b0,67215dee,3373e4d3,53390c39) +,S(fb3f8d06,b2804047,6f7b46a3,ecc2e7ed,9724ace4,14831a8,3c111615,f8a39b46,f9250cb6,ff851b61,3b3fd70c,63c1bd72,3177ed50,ac991185,4fbfb3cf,6bd2154e) +,S(39b2b547,3c964125,da2723e3,97c3fde,f9faf415,db0b389b,5eac9a6e,ba012edb,8ac1c2a5,a586f413,bd68aedf,1f65f231,6bce7bae,ddf9564,2882f2,1bfb7547) +,S(42d5dffa,e476151a,1d3f6d0b,bb24e8cd,bf72d73e,ac87e848,20cff44a,47f1552b,631d9645,d941c001,627ebe3a,f403bcf6,663f6e46,17d86ee2,922fadb4,b8847ee4) +,S(84dc96c6,24ecabf0,a5edd071,1e922d9a,630a0bd2,6d9c6158,e311fb7a,e6e0bd8f,40af318a,16b7324d,2acff10a,8bd2fc25,c795cc71,c0aa5a8,2cc8eae3,ef8703c6) +,S(636e760a,797bceee,11b812af,37bf7cbb,4663ba02,c36d93bf,1873983,4bbf505e,ed5c5e4b,6cc8cf76,436dab98,104b9458,69924f40,57d92ff,d74f703a,65f724d2) +,S(137fd025,4bb5ee78,669ca6f4,ae278064,7d32bc0f,f6175090,d4ff7580,98d06ae4,282d6aa2,25eaaaa0,e9b186a3,36e92e7,e07ec23d,e9ce4bc8,8fb7f09,328d0fd4) +,S(218ab848,39e49256,b55bc7a5,cb250fc0,df781f60,17abcb61,1b6734d2,12459e92,7eb14f56,b53fb0dc,430c49c8,d32f39f0,1a7212fc,701ca444,c994b3ce,4d0e1599) +,S(6b3f7ba0,106f22f8,df69e3a7,8f137605,1b2f0a99,ebb4f4fb,61915495,5f8eda65,c5788c4,4c946bef,86d9a4ab,481e8873,6c831000,5d36d3b7,aaafb7b7,2330d299) +,S(9b414ecb,a2de1195,196fdf19,62eb5b86,131ad1ca,f7fefd08,c5de31fd,d9e8ceca,38e6f31f,9550df40,80db0626,9da9f4e6,51e94e85,95e74797,639225e,a725a637) +,S(436b07a1,857425e1,86933af0,d1d444d6,156a4a8e,f3f2df8e,cbaf661b,26940653,94a52f35,f124ebde,ca642713,724fcc63,c6822dd1,bce4c53a,ce155b31,443f2d44) +,S(cc0a9338,9b25f82,33419bd1,87b5e290,b8d76f28,2099b4d1,fff32cd5,128fbc6b,cc7e86d,ec7ac7a9,448cb627,875500e0,e713ad7a,430ee9ec,220bfe87,6e3f458e) +,S(5a1b316e,aaf877af,cc7b1907,eaf8dcab,7ea684ea,72fbf888,8f21fe83,c3a19858,4af0339a,da08846a,6b6b3466,da69412e,74411094,22dbc450,cf7424ce,cd81e7e1) +,S(faf89165,74444bb2,1fe1e411,40398b25,ac4a5725,2f9ff274,e7d409,6d6c53ee,cfa7795f,af75547f,5dcb6426,3b16c9c5,d87984ac,81083cb3,99714f6c,99a43100) +,S(39e5fd8a,ca141623,1840c68c,6c6cb028,c94cd22d,49619e33,aa9e4dea,d174f248,f8121f65,7083da65,74013e78,88f66276,a8d3686,528aa105,61d10a6f,8c3c1ff1) +,S(20a135ec,147d0cf2,ccd3cddf,6fbe1356,a64b8ed4,219bc0e9,25743098,325cbc1,b17514ab,ea1a3725,4c101fd2,675d30a9,fa1d170a,811f06a7,15622ae6,ee82c013) +,S(a181a1b8,af48f73f,a13d69e2,ac75b13b,9903ca11,8a8851fa,b461c2b4,2c72320a,c96c8467,3f487e62,3d72c432,ad5887a0,a66fbd3a,c43aa5a,219c84b2,97a6df20) +,S(395fc031,720a5da2,faf5d76a,9516de8d,eb462695,5eb87fc2,752462c7,7db3ee4f,4aebc86b,53783895,ab4e14e7,c3ec449c,962ac869,9b340523,1288bf41,c759ad6a) +,S(a36de9ec,f6df2b96,87d632a6,1bb6337d,3fffce5,71b9fd17,4246b33d,1b64fa6f,784e4c4f,a133d140,1b212e14,77deb40d,620fe29f,d0f05ea9,cdbdc862,651999f4) +,S(8cb5a899,ecee4430,f7381820,4535f57,e55a3ab8,3dbdc359,d04746e2,9840ba8c,12a7282a,e9c3a3c1,2c1df0ec,c3d28b4d,731dedb1,ef02cb2c,b92da5de,17ff59a) +,S(de441e1,4d9c9ba1,90e37201,22fd1fbc,4cee72d8,b26b1f29,9c450b66,60b60985,add1a30e,d12ddd33,d4f740d4,427fbeb6,2927b3c6,75032110,508e2114,271b63e) +,S(88e38b56,b5e5544a,f2f3c1b,7128d118,520646c3,97295067,4078207a,9b9fbab7,8833208e,d44e9656,e5bac529,cbdaf184,dc8ea020,a2d8f4b7,8101d8e6,1000e5e3) +,S(a4f0f992,f5616420,b9ffa3d1,58d2d729,3ee217c9,60f5266c,6c5f3374,f4640a7,da6741a8,437a86bc,40ec9188,a1af42fd,b131a8e,81289e71,3d80e201,f82e6ddb) +,S(5f00a8cf,f2d1aaae,604a40be,e451d8d2,86899fbf,37f96ad7,efe52407,1c5c945b,135fc8b6,9ea76d6e,39d1549f,b4692d5b,b48ead3,ae6c662a,424b63d2,b59deb12) +,S(59351a0e,bfd6fca2,ee04c799,838ddb8f,9d168967,a9e6cda0,b416ce31,88f99218,6e01a267,3184f4a8,28e27b6d,4291e7c8,fc4159d,73a465b6,4fe3d4e9,d50fdb34) +,S(175ee35,f5c533b3,de87387a,5f263d0c,6705ef73,e77f39f7,2c796f91,2cb1aecf,a3792715,4540e426,1e0d40fc,4c5fbd60,5fca3b63,3cd7b506,a80d0280,b9d9513f) +,S(75189b41,597b18ac,9ab6362f,a7905ad4,5655951d,fad033b0,2047dfbb,f347b8fe,f536fd21,ffa10ea,57c023de,256a59ed,aa246842,845afed5,b0177b77,21b2527e) +,S(91af9ec3,2522d65c,70bd2a31,57efd23a,43885573,6b5a1c72,49a414,a19bd4ae,312007c3,8462b858,6b44898e,d0b4419b,31c9cc19,a2ca6d6e,6436bb08,741a32de) +,S(ddb2fc8d,41fad5d0,5d109443,2f283c5c,732273a4,1a14db04,712a1d84,a4565c27,4f7e30c5,2cec6f4,201673a0,9893d092,8f6ac9b6,d7f2da38,d63ba2fa,d69c1d0a) +,S(9ce65d22,d7240dc3,50144936,ea412539,ad40a907,f2b04f96,f7ddc1df,f63641a6,af93e587,7f25bc64,2460b425,76062f54,c3037e43,80542340,cf927609,79d97c8c) +,S(7dc0d0d4,1fedec58,91c3932e,b0bf492d,5489f1b0,1e7f95f5,4936d76f,776e9a2b,d55a279e,760c3c13,bfa1c7a4,93831a14,1e9bbcf5,a535934f,a5269995,b107c47a) +,S(d01215c,b6b4d728,9dd8d351,f3e4aa7e,f190d40f,3281c8c1,63d03a95,4c0e5ead,ef98344b,ccbd7eee,723ce7fe,641854d,c68be6c6,99eea25,48b2c881,b0fdc662) +,S(d4d1c31,a9faef8f,5991b0d2,9bdff3d4,bb6498c6,2933eaf3,e1d06767,8d7cb92f,98f3a848,ac6b91fa,85bccd42,f2185e46,715faedb,a4f6590a,4df5f862,ae6a4831) +,S(cc659ecf,42abf24c,3da2d83c,84b1e32,3d718f0c,41a1b5a4,9fad2f24,c6080839,7bbdfd55,4d843399,b6edf249,ae795758,c7aef094,3f84ccbb,f0f2761a,c81f963c) +,S(19ba1567,d5cff385,9d074e94,478bef34,f0eb4775,6708d04e,594d1641,71ea4b04,34ea281b,d536b02e,da858311,4e59c110,30555c91,c8b90ed5,d4f53485,4a820f9a) +,S(3f0f02e3,1a6e5907,631960e5,fd52d2da,e825d8e7,529586a5,437798f5,d7bbc30f,b93e27c3,3aecd4da,59079ddc,be2cd6a5,62a3bf29,7b40519a,3509061,e3e63c3b) +,S(9884c48f,35f63fd9,5a9dd895,b0f382b4,8d0fb096,e5fab23e,df07924,149ab12,5688c266,7070437c,3d5e321f,58b6f41b,e011545e,989feaa0,13f241fb,4180eb44) +,S(940f2a93,79bc9c55,3082e85a,c2f83e5a,bb4a6fe0,2b23cb47,4489bb7c,c46c7b4d,b4b82380,77ba9d8f,51e7c735,cbc6a784,2aa7429d,2cae2284,700c0e2c,7f21fd9d) +,S(a9a7699c,e5353d9f,41afeccb,b5343ea4,5f4beb03,42f9001c,e0742eb,58b782f0,3c759eaa,b3e18cf1,a4341f77,8a601ac9,b0536fbc,5fb498b4,c7ca9597,9587c994) +,S(a21e348a,9cdb00,56e36cc2,82d0112a,93f4da1d,23f274af,b1522263,b218913,141543fa,652f9506,6ecb8e28,a701e663,c70b1873,b1e778da,588c1f4c,7ebe89ed) +,S(d6d54d95,1121e118,52b298d6,18ce738,e8142906,306a5ca4,1f048fb3,5a5fd383,897dccb8,de891fe2,304fe9d0,38d8eed2,8f76c8f3,535a2912,41253445,828a7a83) +,S(7e0a635d,c937eafb,488c7f7b,5efb12fa,6232b464,a3a47ebe,21cc0f1d,c31e6cda,d2d1d4a,75525e1d,2ce5b34e,3adcbf36,74489c86,ced61e82,2d63ac63,8de921e2) +,S(4ce2080f,66a21c68,428870e8,36b3fae9,55bafa14,fac200d3,afea00b8,d451a659,29e1fa26,565b02fd,318d0f3e,f0df93ae,f0ca1b92,73e31b1d,891f055f,6c7da4c4) +,S(dab86b87,b633bf65,59cd5caf,1b89e815,e6d4b7b4,d8f58f7f,c091f542,712bad2e,8e49b109,d79a55f3,8d8159d7,f55bd930,4506a634,7e57bbdf,8fd15eb3,53c85b5e) +,S(c6a14ac2,90fa32e4,e8b5386e,a7672d39,ba16224a,152897a5,6ae29001,b365aca9,42aadcd6,dcc588fe,2421967a,42ff615e,45cd5f2d,fa258164,b2c0eac7,735dbc67) +,S(2599fa40,880b108f,28f24400,4f3458f6,63510bac,c8a1041d,e694e214,ea450a27,54792ad,4ae1cdf4,6a7a973a,1a02bfd2,a424c46e,548c13e6,951d94c8,7ba57a01) +,S(f6ab0cb4,a6afa8f7,aaed8fe9,e79ec43d,31265533,6fa4553b,bf3d7476,8ed9f9ff,4ea12589,c4ba0090,48c1a476,1907fcd6,82e913bc,57af13f9,73fd9746,845026ef) +,S(f51bd48b,ab8f020e,2d97ecdb,d3ff4da7,72738ff2,bc71dbeb,dfdd48b2,707f1dfc,6b168ba0,947f47fa,8a7fe7bc,e7ef90f9,b361c752,248bde43,8c93f3f,4a697c40) +,S(b601f114,6c9ca90c,af798f2b,1d0bf7d2,a50b286,53521362,5c898f0a,4a2a3443,a6ca3041,36b6b675,5f317a11,59f0f110,6877fed0,fcc167ed,f0cf2391,c61f87dd) +,S(792dd67a,e5957c04,76f0511f,c5b7191,5be6a158,f2871ccb,37e90651,f85f974b,9b7f5cb9,bec59f69,3a44774d,d2f463e1,1a4c1c5c,84ac3bcb,94175809,4b1dd44b) +,S(7d884669,33b6f4ad,b3700eb4,f02514ac,4c29c420,33c34377,93101302,8bb093f9,2fc8c35e,674500db,5287ead3,5e46c306,2ebeb5f8,a5134885,a2f9461f,b5e2cb3b) +,S(9c76eb91,6d0f860b,21b82038,77089435,ead4bc41,df82c249,10edb80,692a5351,72badbf3,38800156,1e235a6d,a8c7769f,e51d1e0,d6de06b0,d9998290,f5c88b62) +,S(a49b5638,3d8a93e1,55e52a2a,a1e47c33,8fef18d8,2dafc24e,cf5bb0f3,1fd807f9,1bc271b3,9bf34c4c,ee4b0760,bc934646,9dc392e6,1fce976d,bee58e8d,901d3ae0) +,S(4d3135a,675bb0b0,ca9d5cc9,68f1c72e,a704c9fe,77aea9e0,a4d6247c,dc634460,ae5d7cbe,72f5f4bc,f2f1ffea,cf02b13a,cb74bcef,68cced38,395a76ee,280cfc4e) +,S(59572fbe,17dd6e38,fc0421b9,b42f192c,eff95583,a9204e37,11f33089,9e53f588,57134640,a8cb3f9e,6a1ab805,1d60a047,a01c8f2f,9b72782a,39c45421,de1c7c24) +,S(73cccc6d,49b4780e,caaa1c61,228d2e9a,a5fff08,f5605a5,cb5ea1e6,712d9511,463a8d74,f986c8f1,1c12ea8b,59b158d3,6a52c06d,6a85b08e,dbb2cd4f,e3752cf4) +,S(b6c8ebfd,db620aa9,476f7280,e8fdcf6e,ae3885f1,87683d1e,e503c2a3,8c4c1e46,9a6faed9,f0210048,be575c2a,c8194a5e,76a25d55,f3215dde,b4091060,e7d39802) +,S(b4162e0f,92de5493,da996c53,93067de3,b9cc4a8d,4c21df7e,b507fc9c,4c5e392e,ccce10d0,c9211d36,46397873,3060d982,eecf2217,7b8ed120,a4052561,6a3b385b) +,S(46bf2566,ca4235d7,abe91955,2a0b8d40,a581008d,6544d9b7,a2a469d7,8d2b33a2,6f66fd6b,b8278841,92d82e39,cd0bc9d5,285b5cf0,2171ef1,b77c87f,c6785040) +,S(78446da4,e869a9cf,8badfcd1,b89c135d,b5422a68,2a9ef6b7,419a914e,3d59ab2f,4215b131,db7e46c1,9e03c051,f33bebde,3f5f31bf,2d428983,27d2b586,5a8c9ce3) +,S(86886a05,511080cd,42bdc762,95eb8edb,fa2b01ca,5cbd1d1f,4bebf001,5605f3b7,c71b69f3,5579c0dc,7078854a,83add404,f9b753ed,597a77d7,67507e77,f7914466) +,S(a02e675e,37f8fc43,fd38675a,744406e0,36d5fafc,b28a7371,e16d94bc,83713388,d6f9a541,835c80d4,eae16251,277e9eac,4ee66bd5,a1899c36,de3173de,fc41239e) +,S(a16b6aa8,432c6ced,cd110096,697bc04c,9b299777,ee287f14,ea4cc889,8492bf26,5c11a70,ef9e42ac,d0ed644,6ea7e14e,1e734da1,89ef1ace,d2fd7241,4e0db298) +,S(66a3f2fe,775fe5e5,d0ba1205,f7b81fca,12d7fc2b,f8685c5c,97114b98,e0f45b5a,5059bd14,a49ad49d,550daad0,30b4eb21,e626e2,95ed01c8,c1823050,5e5e3eaf) +,S(9a47a3ce,bf9a04c6,755c6321,61150ba,a1f09460,ea855785,96213784,5314b3a1,22da5ece,8f387e6a,f37c7045,b9296927,efd3a3f2,2b95eacb,c6cefc0e,ec40a80a) +,S(1c014014,9027b9a1,e3ce3c28,8f18fa87,12b25a53,49f9ac6f,97af6273,5b6628c5,672190fc,d048d3d6,b78bc34f,f160f978,87b49934,5753bd35,3a456c96,e448a4f) +,S(c2d9670a,1de5e876,5ac965cd,497e0765,8d137400,9e4ea31d,57effe5,2e8d84cc,ff818647,a6ddfbb1,3ab56b58,52956d08,b97a2e8c,f852379,cc7a7271,f13ccdf1) +,S(1a9fec47,16fd6a69,70ce094c,4141d68f,d21339e5,5396a691,e01b1e5,c996ec1d,e38eb2eb,6c8373fe,94f7639b,595de291,d29b8738,11f5d40c,24006d8b,93f8557e) +,S(b7df52ed,15e91a6d,40e9e500,6b1ab26c,53a51466,98d63bae,77856afb,1d8c464,ea60f1bf,d915fd8f,99d0706f,ff180d51,705c360d,a5fdac1,162ec530,b6e21254) +,S(2f95cca,bd092e2f,bdc86a53,40353350,918ba7f8,8488b9d1,295f9959,d7db6b5f,31456814,fe6ced86,59ea9c3d,f09bf38d,d5b0f893,8db1ea7b,5234648d,a1693ad2) +,S(763926c8,64dfe4db,6a1a432c,a37e4022,3b6ae1a3,7def3fea,883a6612,bd8a7c1c,f34d5b72,4378d10e,bb4b3e63,d80f1f98,a3e741e5,b9e4fa41,bd3f0a5,c361bd1f) +,S(2e43be7a,12916cf6,f312a513,fcb6c98b,708ce2dd,18dc4ebf,72a807c9,c8a31b0d,db919224,ce1b7e57,16e57b6c,ed514cd0,a3a9dc09,8cb59c5,971e99d0,dc24acfa) +,S(7d65e48f,1a867d28,76731984,afc6873c,2d1acdfb,13a5adf9,a56960c1,cd4a5561,9c73b955,f0957ad2,ff0e2d4b,caf795d8,bc992436,be7e69da,7b97ccbe,d983ebfc) +,S(1c6becc0,3131b772,cae166f0,4d715390,6b132c87,c5c76b7c,10b94a71,7819d725,2c3c64ff,94998533,97dd26c,dafdf608,41b84a22,cfb4906e,318c4d24,b23280ad) +,S(f5042a5d,5d118102,32ac83c7,d7edf079,aff339b0,7e9d20f8,198b2a64,13347675,19c10fbc,e644fc6d,2f238802,a045052,b2723f57,5a8bdd20,b597dffd,79a3e4c8) +,S(7c387866,c2c0353e,6e00c887,240e084f,e0ee9f0,2de12539,b4833588,bd4be300,ccc66f94,587fc58d,42fc49c6,d074800f,56f08855,6caf4ab4,c37b43,ef2b8f79) +,S(ece8ad23,d33adc2b,6ce38194,58ba2ede,104b35d1,1691f4e0,b7b206e4,eab3c140,eec976c0,7fa1d3f5,f3c245a1,2c9b728f,1c7e672f,53665e7c,5c6a408f,7c0c0a88) +,S(822b0926,a274dc38,c9eefecc,7d021774,b951cf19,3e2cbc56,a1baf58f,19d12b9d,48bcb10a,f9cac375,8314d2c4,9fcf0512,3638f008,510fe63f,1be0c2de,44118313) +,S(d26a3a03,6ef5f428,fa058d25,7ce62fe3,401dfe47,80c468a8,5cd2e77b,f85b8c3,98c39762,3a644329,7d5c6759,a308c2db,62f1bbcf,9bae4ba0,10fb1175,126a6fa7) +,S(7febb81d,8aa0cbbd,3002ab6,ca247d1d,24c8fcdf,bb1664be,b5b3c346,93019090,ee625bcb,12ca6da7,544ca6c0,2a84b9bf,966dc923,d8b23b44,6cfa4604,a6c3ec26) +,S(839e44e7,2d43b6bb,e6f63a75,cc3644ba,b7aa002a,6de2c087,33df2b0e,51d3473c,8368f635,26f41422,a563806,d775bf68,2d876d98,542b3da4,3e591e31,340fb9a9) +,S(52e947da,1c96e07c,9e2c84f3,23f2eed9,7e049b97,86d21135,ea958eac,db0d7afe,6bcfdec9,e0c70d3d,39de19a9,9fa72ba,bdbbe50e,dc44897d,9fa670e6,d473a831) +,S(72ec5426,5e4a743,1d7f6dd7,1c8e9e4c,c8b22074,cfcb00ce,db18c6b4,c67f3603,ddb9360b,7f9def94,f5ea6d71,51eed4ba,49455cf8,15bc9023,6156d4fa,786b5ee5) +,S(370a3f15,59a55689,16bf9ba,f2863d11,2ac051e9,d6a90f36,af39add3,1406c849,854cbda6,8517cee6,95ab5d3,20b1067b,ee80d993,235d1dda,ba3a0ec6,1f005840) +,S(12e57888,60d2fbdb,cb3af527,577ef6a1,41d1cf0c,f719ac6a,714f9a39,9bef5dc2,f5af7aca,5dd8126f,7dbb79ea,d5a4b475,a094969c,64adc821,ab46c5c1,86bd9ea2) +,S(b494abcd,b2fa5de,17b1abad,757bf9d2,8b7905c8,aca0a5bd,fe2ad8c3,30a2b722,c397ffd6,dad8619d,9ff8d8ae,e401a4f8,cbacf710,c6409adf,bf991d72,9294cb87) +,S(2957692d,900d0b94,b208245c,f71bdcb,118ea6e4,499dea83,1064605e,8ab9d89c,a3c55447,9284afea,37e94,fc24d50,f129342b,7ebbaa91,857d655a,40444a7b) +,S(61101879,f325e072,bca13642,dc14a5e7,1969aed3,2e49ccda,39cbf723,a1bd20eb,4e199c82,4816e4bb,d5b67269,e9d8eb0b,e5c2483e,d66757a9,c1d59847,e443b3) +,S(9eea48b2,99f3474f,8aa8c648,c2721fbe,c65f6ad4,990ab4e4,29a9c41a,ba55ab18,13954697,35ced1bc,7e857049,7f2b5a1f,b6802892,fbee0654,c1d7d89c,1f234443) +,S(dbb054d7,8ba7b707,ca475982,6a36808d,3fff42b6,31f1bc5c,c9df403b,518bd97a,396ef6c0,96343a55,dd404885,427e781f,a55a1f8,1ab95d4a,89d87975,611b923a) +,S(dba95bd5,7fdd2ef4,9dbcc0ea,35c14dc9,b2f62541,5b08e75c,d70f3caa,b61bbcf1,2af0965e,b7a17e46,18b6dba8,415159ba,b8f76ac,ae9384cc,64a90e32,d20a3d) +,S(6c312c9,7979bb0f,e72d8feb,c3c755db,bf1a132,722f6ef5,297beafb,8ad0e5be,36366388,db74739d,df78d5ea,3d8fab54,6c435961,71f78643,8be00718,8202c6ea) +,S(65eb7057,a494bc8f,549b8638,e18cd717,ad987030,aa3161e5,b9fe31b6,dc8825cd,458202a1,67f285e0,9196b4ac,f87068e2,160b7b42,2095bc92,7656694c,2fa80312) +,S(a0696917,b0e868da,ed5db7cb,d2aebd1e,c1117426,d842c0d6,b567e2b9,4effa7f6,c874d20d,a1bb4dc8,f01cb828,be8bda8b,c797ec3e,37feb0f5,ba55675c,41ecba78) +,S(4191bb82,19372a13,2dd37830,1086b64d,10874479,1d3b7879,3b27c65,782cced2,c74907c9,8ce4d218,d1ee52bf,82e130bc,49335279,2497b4a,34892fca,67dc3f2c) +,S(75f37d33,721293ee,5923b9b2,b95c958f,945e3f2,909c38f,3c3e7f5c,79b218c3,ac041f8e,c6b222a7,a2f28e18,1cf11aa2,a7d2fda1,1f402a33,9afdeace,a5928be7) +,S(cbc3173a,b58b2f29,20b3bef,6f18a84a,6004d1f7,1dd6c65,78571afb,96fb154e,c413a0b7,6f264414,2d3c166d,e9577ee1,b8f36ec9,618e8892,26de4de0,f9fd5c37) +,S(64e56b09,a139fa59,69688832,419cd1bd,64f212f4,5bf4cdd9,2a5e4370,dae226cc,a9194eee,f2429016,4b41eb3d,b429415,d10915a8,62a02fa7,671bc19e,50fbbbe) +,S(e33795c5,e2886cf2,9a22c2fb,8e02f913,34e13350,9cfc14f,73bcfb49,355285a6,7890fd21,f33d48c7,24e580cb,7b4bb065,f7c575d0,c6cdf5e1,5c2d6423,1afb304) +,S(7975e6b,7a73d0a0,d1543d2c,76c79233,6e6a0994,35eb7699,8f25ce75,a6b0b6dc,10b160ae,c68993ed,d5e48d5f,7562bc24,8f16eb10,a27f7115,121ce2f,f63eb06f) +,S(6d0d13cb,f9c13967,92e093d0,801f441b,c1ca4484,92ce3ce0,2ad71cdd,d1580f6f,17ae4238,84caf022,bbddcfe3,37a44309,4bdb25d4,71da56f1,bae4bf2c,ebb45746) +,S(2195f14a,23af2b59,c9fd3f90,42608037,60d39867,3fbd0856,a974a20,1bdf0154,fc05b38c,9a44ef01,f4db3a10,b500a372,5ff1aa58,86ff6111,da53ee14,f8136a8d) +,S(47679c73,60acf8b9,d1b54e16,7d1dd1a2,1f558288,3844fc4d,ac2a7fdf,8b713628,82a02225,f3b8e81d,e4d4bbde,94f7cd97,7ca0180a,ff3a4751,a93f6ca,17803ea7) +,S(b0bd02f5,1636a69b,23ffe514,cc2166bf,8cb4ff2e,2f7c8655,ac9aa7c9,de30831a,73fad62,48bacbc5,2a7ffc51,fbeba3d8,f4843b3f,ba6c8814,48add0d0,8fd5f518) +,S(dfaeffd6,bfe9f81f,ed3f81af,adbec5b8,aab5f15,23cb7452,c689b01,a3660158,3f54d105,c3ef4814,e6ca4bf9,ccb5b54f,30c4bf80,10d7b24d,f3b5f3da,c9240583) +,S(eb016abb,da661e49,8f53d2e7,96fd6be4,80dffae3,eb7e4ba7,46ae06b,f40d42ed,310d6dbb,aa7659a9,6cd4d50a,dc2e5e85,10f005ed,6d29d1f5,18bf83a2,7af895be) +,S(989e1bc4,3c0880b3,6d35b5eb,137f8e61,7263784c,d2f35173,e517afb0,c99fb2ed,4c4ceb5f,110cccc5,3a35963d,11df77d8,999b3bb4,c2cd92fe,b53b7476,9dfaa1d8) +,S(beb0170,be4a7bbf,1f65bd40,776d5efc,dd801d6f,909250bb,fbee1198,c2033811,1356b98a,1d329a38,57a99820,26d3ce6c,f52349d,fcc59644,ff4dcc13,8486461f) +,S(2d90bc1,986f9a4d,6f75e596,96c4296a,ea9bc55d,5b60d12e,337cb42a,ffc0b145,7d57cf64,734d1997,bf84b721,e67eafb6,b377ccbe,d2ef3b5b,48dc9f06,f71a0c4c) +,S(6b7a6a54,c9608062,de196848,dc3673bd,59fd53f3,fee72481,e829ca77,789f51bf,6a9ee208,157b8a69,f0879f30,c58afbe3,dd4dc42b,43cbf21a,5795224e,6c09af23) +,S(c99244c7,32cbdfe7,68df8fa8,7b7e7eeb,16faf0e8,8870fa8b,f4abcb68,368e8393,20ad1b55,53ba2ad2,40563453,b2dae2e8,58cc9b8c,c6a49951,bc0c0fc2,875620ae) +,S(553fc07b,402a8dc5,5f1cf4fc,609a01fd,350fef26,9c1f93f6,d2871f94,70f01b6b,3b518c75,39616def,fa7cd0dc,7d7d53ab,9b310217,7e470290,9c199137,34e5a824) +,S(c5930378,6ed8a7c,b3cdfc80,a34c85bb,721fa927,9492e369,a4e4bec0,c0fe2f69,3699e469,8a3e9129,3796dea6,213fd702,d7580e67,9f446300,1e1c7a53,3717c149) +,S(cb6f1118,f4d59964,b8f4613d,8c349d74,8cda0e7f,f1d11c6a,79dbecc8,911e7eb5,62b0ae88,98b0d5cf,723cc88f,48257c47,ca0fe3d,7de676f4,cf4835c8,fdde20ee) +,S(da2cab8f,8c26668d,d9722ae6,1decb99c,78b5d48c,966889a9,46fc522a,31a26db9,6a03377e,234f949,4a614cb7,c93b131,1808496b,12485ad8,fc21e1a7,6d0c7e98) +,S(fc6a4d42,3f9be3c7,fe337411,f03eef,fa595b83,9a6eca82,b228d5e6,b43b7d86,6c089544,eb8fbe1a,3dc78fdd,ac253fa8,27dfbd6e,269196f5,bb476865,a298beff) +,S(73c9a91a,b9c6e0d0,773258e8,3ca9460c,3d09fb49,9426fe09,db73756d,e3dee882,8b314327,efc0508a,d5a90259,c9ed876b,52040a10,45d06224,78cd635f,e571d2b3) +,S(a69f061,45b8cd1c,343e6127,14b80996,2bc02848,ae976762,933744ca,3b4516d3,87518d90,b4503741,ec16caf0,18615e27,96b6d91b,47f649ae,a86da209,d70ebe3d) +,S(1cbe4a25,70df40b7,1cddfb2a,c15ba441,376a430f,446a2572,fc95235f,28ed9691,12554ecc,f262e1aa,cf2a2d10,ab3953ee,c8ac69f3,99c5380b,6ef5d202,5613de85) +,S(8dea49c7,406dccfb,4305ac85,ba08b191,4ee2f11a,8827852f,d13c837d,b788e27d,c73d61a8,3b517c3e,bab8b15a,b7a0506c,2b6071e5,697b4056,d902957b,cfe9d9b0) +,S(de926377,b175cc3b,1ecab5af,81d38d6a,5e4b717d,55bc94f,6e4c43a4,86d0ade5,29d5f3e7,9169d974,d4289115,d8d4b8b1,6be387ac,e60d1cb9,73c387d5,17d0e11f) +,S(6e76eaa7,aeb37ab4,ab455a2f,3d525140,f6e8a9cc,5eb18fa3,8a9a25b0,d3b41497,7e3cc5a,78909d15,bbffb936,a47aabd9,44c40a3c,d690908a,89f193e4,95e066fa) +,S(37131ecf,22d57f0d,a662b03f,c9891f99,4678539d,37f5ff8f,9eed0f8,bc607574,c03b2d21,69d42968,f611d596,e173d4b9,281323aa,6abae6ac,c177a1c9,1249f3b3) +,S(72088814,82c89957,28e76633,bcdf0ebb,65155c8f,4979a4e7,66713f37,4a5d8152,6783c59f,8c0adc8b,da8e26f8,1990a00c,588a7974,b48aee92,d2a762e5,c974eedc) +,S(1b8263df,baca44b2,a7fa7a7a,dbf9d3ce,d4567fcd,144d9e8a,db712365,d3241864,1fc7e3e,24f01143,1944b98d,b2dac036,edf36680,80196785,783a41be,d788606d) +,S(7a372de,27ca7f2b,8199714d,bd583edf,ea41bae0,5a1313df,f1a8c260,cef5fd43,a0e3216b,b32c5785,b5c60f22,f5e4ed5,a5d3ab64,ba0eb2e2,916f1b9,b6281142) +,S(71815daa,55254491,c8bb9432,755ac1f0,c30f22fd,79d4232a,72bec0ea,8cd02300,8b341392,8de15c16,5fafcc68,84ebe3c0,3ade0918,4c463804,876f1600,c76471b4) +,S(1a54b7a,d3cfcc64,b62d866a,9f2056e,5535d4c,12c89c78,5d1c5c53,ff654d2b,b7c77ae3,9f4b87bd,22c77395,a91ec307,e9f5aef9,dd2892e8,41d8390b,47a12f19) +,S(85cb4457,1fc973e5,dcb0b695,3d75a2ec,22cd2820,74039987,7262b7ed,999c44a6,efdd1df3,bd13fe66,c0d4ef02,8e142093,58037a7a,bd06ed53,280d3318,9d0f6aa5) +,S(98e8649c,64a010c9,424b305a,2407a36a,cd72df8e,138df962,c35bdf75,fcfd4e4e,a804c569,34395d7c,549f7770,4c3faed1,624a8cf6,fcce08ef,cd3aa486,d5a6c60b) +,S(1eac47e6,a297e5a8,8f433895,874fac8b,5019a164,69c6a881,7e500b94,74a5a72b,8b7daa6b,784ddeae,f380bb1f,fe64b9ed,43a10a2,95c30325,f519e4ae,da11105) +,S(f079ac65,91f27363,17e9e0ab,6a723e25,e29950c7,a02a66bc,96c4ae68,44f42f5,ea87d1bf,c99f4374,4acc3ca0,214a1ef7,490186d0,3d924562,24981469,c432dfc5) +,S(d6d09d79,bf1f30e0,f011c3ad,a077b1d8,fa0d94e8,8683186e,ba471caa,32539643,8770bc64,fac8f541,f730494d,cdad2d48,ef510037,466bc049,876f5cc9,7caa8adf) +,S(376e4bd9,f8963368,7059565,68313f89,d6416ca6,b1e4e91e,3aff3ba0,b8351f65,ba436de5,67248f1d,5e99e8b9,6447cb28,497deea,47fae95b,78a5378d,e65f13f1) +,S(270f3a18,4b42c799,a2193dae,c96f834c,b17cb53,bba1610c,6578c741,b9f20d7,ccceb12b,9ab98bbb,a0fb0d0,1673377e,3a7054a9,f4a3b000,21b67328,57e7e5bc) +,S(ae271b64,1a1e5348,29e47a8a,93b496d0,b055d2b7,fa98ade2,f505a5d,ec1a599,14e9552f,39b481f2,522a9a23,f87afa9d,5847908a,53c1581e,45dfb244,7fc2bdd3) +,S(569eacfb,177d8a74,a9288ad6,4ee5390e,db36a93a,943fd6a2,5ec869b5,a7f28c8c,1cf62c32,9127642b,99a7508a,a6f4f136,bfda9af5,45f52844,cb9c71b,5f732f7c) +,S(77c7f27f,fdc4e7ba,a326a246,89790f8c,41bfb5ed,6d365e46,f277cf81,5f15b558,4d3440dc,138be5d9,566a6828,852c2ec0,e0f970eb,4501a43,25af933b,821c3007) +,S(d66292db,f1730306,a87f67e9,b044fef9,7d46761e,7b8301ed,132ed7d8,94adbb25,ceef7755,8ceca4ed,92145f23,8bfcb32b,3d36db14,6fc50209,cb0e58f6,2c8b83c5) +,S(1c42832e,12ea6f44,b77927ef,7ebb1f3,8769e3bb,f5507a49,25e8565e,a77ed0d6,8b9383a4,b31b4f5d,a717917d,9e1e41da,786a62a3,327be11e,280e538a,9c29d5ee) +,S(63e57eff,942262a8,cc362911,fb98cf30,60832435,3cd394cc,5d6abf77,b21f7968,9e3c1e2d,fb9083f2,4c344e7d,322ea530,146062e2,a25fb6b0,4502ea35,59b009be) +,S(be233fa1,86f38a5c,72c88e59,19094ea8,cf3ed3a9,b4ea3f9b,2a51489,97b83c90,402eb2b3,362fadb2,6e389ef9,edc537bd,40bd48ea,6296956f,efd19d0d,eddba564) +,S(636ddb80,4e7c74fa,9c5f4d06,5a730fce,f2cc6956,4a24b579,aaa0c3f1,61844b78,6a4a3569,583212c6,22feb59,897eac8a,6aa31ffb,916262e7,deef47eb,e6a2496c) +,S(d251c1d1,7250d87f,f7b5af1e,ee2f2566,215ae7c2,b6e69e60,5bdd6c2c,dee1c5e7,245accf5,7df52fa8,cab5c986,2b96658c,5de60d06,c84584cb,f554b518,7fb4ae78) +,S(405d4cb8,c62f133b,7051cbe0,54b06f42,9055f917,3ba8248c,c3bdc744,198ed407,8463df10,a8963cb4,298e8031,bd2bc3ed,523553fc,60746720,1a9abba7,305deb54) +,S(97f05aee,9ab33a07,e14a0314,4fda5be4,ab329394,1ce8f003,546ed8e3,9be983b8,59e62a26,7fd9138b,c4c70b15,533782cb,d321f120,1e3d1a7c,3be4ad5b,eb849261) +,S(3388161,18e125c0,6a9060f2,6b9ad691,c99fdf8d,4309553d,ea160749,eb91ac8,583e918b,5b8e1a68,c053af0d,245a9902,a571788f,dbdc0c88,7281767d,9404384b) +,S(ddd20c46,6bbe6a25,9f706b1a,ddc3b607,8cf65366,3369fc31,dc8f7fd3,3d17a9f9,b2df7b29,b4ea9868,bbaadcf4,6621ccf1,7833e159,acb43daa,7cf78e89,c1de5a99) +,S(923c6d2f,71ae0ee5,596c26bc,31389cbc,eecd0712,e8df91cf,dfe00172,3bdfc9f7,6fdb28ad,27c41243,6a41ae96,99a03d40,5ff2ed87,3207f082,414a4370,34be948) +,S(6d42a6bd,28d57e87,66a12461,b8a7d111,a448876c,149071a7,fde9df18,dca09e65,4df07ab8,1dbe97c,3585554d,f0226234,dcdd2826,747f2775,640bed6b,95fd28bf) +,S(7fd42961,59f9f259,d3c3610d,8539ece1,70efe104,31c9a3f0,4b9630ec,21d761ae,fd695441,b8ce2bf5,1037d00c,4d50e640,e85b6001,6d5d26dc,787f1df1,25d7e280) +,S(48c1050b,128d97df,e7900a01,70559990,944045dd,f408d8a7,5b59bb25,ea6770b5,8f7ceda0,4b6e319c,1cc61b31,53538a09,d450e49f,8863061e,f9b017c1,6cf66559) +,S(54462d9d,a12d62dc,96abaa1b,4883c67c,b3214c40,de5a4a09,689658f7,dee2a686,7673ad6f,916011d7,fd33bb5,eee819e,6a3f0016,d51ae37,bcfec2e,59ccd957) +,S(698534c4,e4e11d89,82a5a46e,44b771f0,95c5fe47,54d7a261,44373982,1e6435e8,36d602ac,b2720c71,8d619a8a,5e7d6873,9faba099,f71c1137,6a8f41d4,e1cbba18) +,S(4493f759,51c681c3,bb376b0d,7c88ecd9,d0bc2a21,26fd341a,d8c4832a,be77ecc,e553e21d,d4d99aad,97f15857,4bfd54be,df7143f4,e829c12a,39109892,c120351e) +,S(b054daa9,330080e1,89784cf5,f917c024,6c06325e,687e6ec8,cf8be248,ccf8313b,51f04715,5c9ce55a,c755f2a4,602420d,4ed52a5a,1dee9a83,b0688b1d,b767d866) +,S(4b4272a4,fa996b1f,9ffe6796,e6519db2,c1f8cfc9,606fbc57,1fe03713,c029583e,9526f2d0,dce7c53d,5cb36f8c,fcbc517b,cecde833,4384d10c,f5fc80a6,ff0edb83) +,S(d61eb681,c69de82e,caed8e64,7d1a3948,6cf63f83,f1f04bff,46a04aa3,63e29604,9b5a92fb,156d7b8f,37c6d9a4,30ae28eb,f62fdf5d,5bf441df,aa386259,721f08b8) +,S(bf8f002,57f979c0,9f973c8f,3dc15eb8,8349e340,efd8d57d,5763e4e0,c2bf507f,561d862f,1041e670,fdc265a8,36d363a3,43346739,f4f177d9,f66414a0,820614fe) +,S(63b254e2,a7a6fad6,4d9ca03f,7293a86f,b471fd45,51ec72d5,eb03e289,774cb498,f4145111,3a4ce922,dc7b3819,a2de49cd,f5be7d53,6befd880,837bbe7,51948c8b) +,S(bae6facd,e1da6ed8,63491f33,c4c9efe7,50fdd908,6b93c09a,e0418a29,d3968725,7c7dc955,e483176,f6bdf74c,6654e6a2,e4ca46c5,2526349b,b4d66090,6ea30533) +,S(f56285f8,ea15aa08,f3ed8139,59c660b0,d30a6317,5380429b,59584623,aceb34af,110033c1,a202194a,40703c9d,ea2dcb9a,d9a0f6da,7b99b8ee,31eb1ce9,55a9eb9) +,S(876c7c95,3de3d451,edb9191c,60e8e36a,eafbad2,198ec6a5,40d72633,8151d87a,95efff74,72ce0c6c,4010d17,7e52a859,cf55011d,1005bd27,df54e25e,972279b8) +,S(e956c7ed,77c7bd5,68096902,4ab1c758,a024d86d,ba3f30e3,fbfd83ac,940a6d30,1b5dfca6,f0d19ab7,7a57a9da,259a564a,5aa5759,ce6826b,45c71771,7aad9da) +,S(f53fde61,abdb4543,1456e9c0,65048cd7,ab743b5a,81a468bd,8fe2ffaa,99dd3f16,1d28b539,47ea22b5,178e86b,ab3561bd,a65ac78e,e0e2a6bc,1a26b64e,d790222f) +,S(b91a8681,52eb435d,475b9103,936a7c62,5e49b0b4,f7bcc3b5,f5d36449,c8043ad9,c758d17,56939bc0,61696f3d,11dd1bd5,722dc90d,1e8be88b,b3430062,a13aaa75) +,S(5a87bc70,23e32927,db4c55d0,abc97536,61f5adcb,b24c2897,b16f08cf,8d8f8cc7,9ceaae4b,aa6a0d5c,46d2120,fbff74b4,9d1ac32d,88a89b2c,3d2aa6e0,6f731e54) +,S(ac0b1b43,e54eb354,3bca80b9,efc5cdb1,215622a8,66b66e80,de979f79,71ceb034,a891575e,5731e152,17bc2b87,197da9b5,439477fb,d2bf07f6,9bd13f3,f4638d8d) +,S(d3c41ebc,2017cd82,76f4d6f3,f6ba2370,e84f1949,df3abdc1,45073467,e85ca915,52ab1d52,8be55945,1f6b1e91,610fcf35,bc1e9576,3ba3ece6,9da5e4d,8cdcf7b7) +,S(68cffc57,f4c82cc6,f7900d00,fe1b0f36,ab842e4f,49ee9063,4d1ebe95,47923b02,729955bf,aac10741,c3fda281,c18a306b,367ba904,baed26b2,6fe6a485,9ab1d8e2) +,S(7e0f58e1,246299ee,e08566b3,35324a66,579dc765,343f874c,d30f2553,8c177d9c,39652583,587293f0,809af7b,5d366740,86b9015,6a05928d,cd340196,8821447c) +,S(c72abbdb,d1816498,eb53d4bf,1b325fd6,5dc45086,73257bbb,97a15eb8,e4c9b542,3c8fafa5,5df5f3d2,88441ed3,e46cd318,841a068c,b681b782,88e77afb,2b25a7b9) +,S(74747f5d,3eb09144,1f64d76c,ef690378,943654ca,73fe15a1,b720abb5,d2363d0d,12c6724,bc7e8f7,321f993a,5caf0547,bf54c1a6,41f959b9,746fad7c,1c443649) +,S(f2a702d1,ab1de7c4,34cb5d71,dad689a9,29db862c,d1fb06e1,212ffc8f,91b67067,5b91e2c,c730ac9a,cb7452bc,7fb4dcfb,1d5ec11a,bd146429,62c7bd16,37afd854) +,S(a92d4f08,14e76b46,5fda87b6,201c46e8,c2535b3d,4be91e1b,530b5634,d00d9340,4d829a8f,3c02fb40,569fd9bf,e754dad8,38d96dbc,2350f0be,6f4ba065,5cd0277) +,S(79e30cb1,ac06c3b3,f90ea320,60a80fba,df5ea142,84a15746,2aaf917a,26f205a1,ad220a0a,81459495,5ac8a79e,d0cba974,7f17364a,9f6168d5,3521f276,c8d5c82f) +,S(6d55dc53,d412577f,463252b,e240fa32,88c8d6a3,babd6ff3,20231b6,8c9401d1,83a41c96,aa3b32c0,59155345,2679f056,c98e17c8,a0a5e154,d732fd3,7b006eb3) +,S(cf9c9f1f,604eb909,b37ea5eb,4cb8a0c2,11ef14e2,d3e2f48,7c83b56a,49b3f20a,2b90b9f6,4e649988,77260b5b,ecb441de,da24eaf9,df56ff93,dd3d1c8b,a247dff8) +,S(60d4299f,29ed994a,b3367780,9ef6fcac,e30f435b,ba9a8346,a2fd925e,186b35dd,df69b88,b8aab4c5,8ff86ddd,2d8e424b,40d51a4,9d189437,37b23695,4760331e) +,S(d93a6edd,1b674a60,6b6bbb85,4114a3bf,7de3be59,82cd2122,887c5376,d9493540,87da37d7,dc79197b,c0159006,4f921ae7,acfdb08c,447ed230,423c51bf,f9e5f7ff) +,S(cf8c3d37,7e108d98,b72bb6b3,1cb7b5f5,fd5869da,1a06fc60,25d9191a,bd652962,6f5cd02,bb96ad02,db42b034,321b3cae,fc54271a,879edc93,afc9c8b1,34128fbb) +,S(6ca99247,4637ecf9,827ab65a,c7b9ca0c,715b9048,f24c42a6,90847e68,8cdf29eb,51378da5,fefa454e,d96a446e,dab57c3e,8bb6160d,d015c1f6,d93983e4,feb55439) +,S(6f7d2c04,3cb813d5,dea67758,cacf2e3f,3cc3064d,7b9f08c5,58ed686d,28d17afd,4794e278,13ecec48,33f9106c,17c5e161,b590a5f2,ac293c8,c65a6cc6,5233bc6f) +,S(f8aee0d0,bc9883f,ab6a82b0,184e9199,e818d01a,7d99f475,d2ca8a24,e924f3e1,acf9cb2b,69135df0,9426bfa,c6b1b3de,44f1055e,5cd21b8a,b526f34a,839442b5) +,S(8b00fcbf,c1a203f4,4bf123fc,7f4c91c1,a85c8ea,e9187f9d,22242b46,ce781c,61e9e58a,3e81e690,ec026428,ee5442a,a8de699,dadfbcac,478985cc,89d35bc9) +,S(fbacbb07,8e84a2b2,c201ebbc,1cc3212c,a7278523,e984bc1a,b0eb7fa1,39d6dd4d,2c6c9dd6,f5eb0899,797df675,ccbbaa25,4e557d5f,d16c6083,3aa84a7e,4c3a0f52) +,S(1556875c,2f32f053,8d28f993,53ba2804,5ba601bb,3df8a175,bfa33dac,ac5c0611,53623050,c3da9464,8e9fb540,5888e387,d2eb783a,10e45cf8,ebaafe84,a4e240b7) +,S(b602b095,7f5b5e98,ce3e690e,c683f803,49a8ced0,5985f79e,2e972bb0,2d93d77,78868390,ddd6b821,e30046ac,b1bb7b31,ff358838,af5b89d9,60d39b4e,85435227) +,S(2a110165,23a5244e,89d66f8,a06f0553,7a846216,75fe4377,24f51501,da2dd17a,24d3b525,e1ca62e9,b02de498,30737f3a,b881d078,7d2c2bff,938c53a1,b8825159) +,S(7314759,1148f9a9,ce326a8e,4f43d2d9,a13edb92,56fc9d38,35a35da6,457efce0,b195cc94,80d0aa04,d3ac7cfa,45766472,1ad5adf0,3060b790,c7fb0ff7,77b2d8b3) +,S(a36e4fe4,c7e23d7,16362979,c5a9d8e0,525d864,37d96e3d,b2f45edd,b36206fc,56ace785,a428db4,a8a70de8,708339a4,346699f,697a6707,c745635e,66844751) +,S(97864be7,a2c54118,5d61b61d,5a2494f4,85b877d7,e8864402,3444d778,f43cac9a,43332a78,a85b8391,551dcd90,e93e71c,3a6c5428,71b980ab,138eda2,b9f2ff69) +,S(34a52dbf,2449eea6,da2c92c8,45dcd47f,daa7ca41,5ff02458,b74eb996,8519893e,add41829,900f26c9,f7ba8d55,21f3bb23,b3545a35,699d41b9,4270547f,e303ece4) +,S(e250aba9,94eea617,5fb7b2b4,8631f5bb,d8bdbbb,799f6fcf,5afde8a6,2ff3d629,803f34f5,9db0fa86,bcb6fae,689530fa,2e92c4ec,bff1757,409bd836,8143b0ea) +,S(18356a6d,b5946e2,1149347a,4558e116,7953042e,e3b2a372,869ae29,b871ae00,56ef0971,6b176f81,e1f98ec8,dca127c4,ba6de23,ba8c1f08,f19e9abb,995385cb) +,S(35a8ebf5,e5306084,365ef3f,30c29e56,ca5932d6,f73d688e,ed3cf2a9,10205533,9eae5d95,42954de,23d41032,5ada759e,20d30fd5,c2d058c4,53a18520,7f259958) +,S(37e06f5,25ea8fe9,ea862b92,53a965f,7b3dbe90,9ca487bd,a705041f,e6a4e1b9,83919fd3,5e489bea,23f13b60,41a19940,3f2f6568,fdcf60d7,3f57fb6,1fceb97c) +,S(abbbe697,ac6c002f,11062947,6e4a6ffd,48316646,718d8713,1a4dec19,5e131c27,b2c926cb,9d5122ff,7f8d8a24,a20eaf4e,fd704944,9aa48300,92971572,b12f9dcb) +,S(4df36acb,1484dcc3,f7c29c8b,78915315,5a1a3636,aeaca043,68a585a4,69b05878,fe16fe6f,c08af026,3fc417fb,19564534,aa059f4b,b7fced09,d4fc20de,6a41c41d) +,S(5ff14ee3,10256a0a,889570e5,ebdaf6af,c8f49c60,dc7c4731,23a7d29b,e799d0d7,96ff1538,55eb2fac,8f2a923e,ead9851a,95382d6c,56cfd0d8,9891d2bc,f7e65d7b) +,S(16a12c52,c1d422f4,791281a1,b7ab7186,a24dbac8,36af6810,4f62dea2,855d119a,7a6ef272,f67356de,46996cb3,8bbaaba9,d36a4bf4,60e3b2a,b3a81723,24c17478) +,S(8f4c9c33,83688920,a48b3d15,4fd567e4,5fcdbac,bea44eda,60600ffa,a1489ef5,2bb5bc4d,e06444c7,b2bc0082,d629e2e5,a4b0abd4,8cbb9b4e,37d564ca,db885717) +,S(498bf39b,7547010,854ca399,e50399ca,19236d2f,d05f5777,6abd6d2a,aab83310,8e2debff,cb58e36,bcc7beb5,8a45d4ba,b5edcd46,2c355d0c,4dde51a6,6afd691c) +,S(78147e0a,2978d396,b22247d1,74958336,2ba51d90,11e89c11,71e6e136,eefb5996,a66b7b0c,a73b661b,e71bf0fb,2eb4410c,4c75bff5,b8b05fd4,1d830d27,ff85989) +,S(351298d9,fb236c7e,5477aea1,d2df0048,47e112a2,e16adf66,3f4d77dd,828d4927,6c9c91af,8b6f23a9,d15c3884,e618ae51,241094d4,5878ff20,67f2ac81,dcfe00f9) +,S(fb5ffc0c,88fb609c,24cffc14,848549ad,c1814668,9e5f8a67,a342bcbe,16399d10,a995e13f,9ced94cb,90feff32,8e92e978,88cf5791,c885ca09,46f46e5f,b8229ab3) +,S(8352b7c4,4cc09c5d,717bd197,40dcaaa0,89565e94,77cd76b3,6c7a4fa0,4832097b,9507c998,fa25d9,758ca78,6774ae68,f91c8fab,694c5e23,6f8af6c7,7d0fe6bc) +,S(1be65c0f,273a2746,d6d014d7,6bb1c595,5d5ed1db,62ad940b,d8204115,85919f4a,dac98f30,2a38f839,a3d470a8,3ef83ae8,f044e93c,80b23552,da3da359,5c09a3f2) +,S(7107f3a5,2f4be227,eeb65037,ae800f64,8de14d89,ebf743a7,ed65f98c,b58418e,bfcf1c09,bd1af327,d14e429d,18eeae45,927b2df2,aa15ce60,69f6a74a,69fca7a4) +,S(117284b,e8edae5b,1ce54a13,3b1f9d98,983389cc,a4ccff,9dd04973,97312e3e,91e48c1e,724fed49,8bf51999,4c84d0b8,84e3af77,86e61539,638b45c3,8212b03d) +,S(a08d86de,b1ff9ab4,651f1cd0,d89b6714,41c243b6,c7e655de,d31b44a7,b00de54e,4d0fe7f6,344d39d1,57764fee,f74cc949,a72ed8d4,c2dbc12c,ec2629c,830f0d2f) +,S(9c44c1ce,df67418a,eef2cbf3,68d48a08,98cfc633,de711550,3f78ecb5,a4e8c25f,3cda2020,86d3f096,31da959b,9f9870c3,de3d3c6d,e4c1b35f,81f64dbe,31e743fa) +,S(d933e475,5215c065,bab118da,6f24d714,f8597949,a52ae319,250338b2,2339f149,5fb0f28e,313af04d,de3abfa6,4981287,e277e39,d2f19dcb,547d6fc,88baec45) +,S(7fe1f197,ddd0532c,a2010528,9f243bc6,4c2f9e53,65aad5ff,63c0f1d3,188a42cb,813b133f,a1869218,ee361828,6561e75d,a9ec6bac,6ba2ac7c,d3f5678f,fe472af7) +,S(e3568521,89612102,7ad0789c,72dc6d57,7ba412ae,b3d7551a,96aab952,c507f79c,7645c505,8e2ca8ac,ae5fe225,eddf4d45,7d820114,cd5e69a8,4966465a,423e3a30) +,S(c56afd46,8a5644a2,abebcefc,3b6a9da0,c667a183,b822780d,146ecf82,b6ebece4,9e41c655,a80e2844,89fc587c,b686549a,2915ba14,1c691e75,cc635f9,54f1a8d4) +,S(b242eb39,60e6a8f0,73b6042e,1095f0e1,440f9f8f,d1abed56,ad1f26,a1589345,944da76d,f063f0a0,3602decb,696f2a34,378579ad,e8469ca4,bb5041e5,876dfde0) +,S(3a52fd66,6d2e2e2e,888a43f2,fce68703,78f835a6,b5c3bd42,af57b107,323bb827,12e51177,a7b37a46,2755cea0,18f4f29e,96c7d6,d0e434cc,4874da99,a7af91c7) +,S(b77d5615,c8831ad1,147072d2,598a8c6b,4a71c0fe,967a6cf,70877aea,24d28f53,25d5b7cf,a068a665,deadf83d,5f793605,f5cff2c3,36a8d176,d18c634,9ebf3132) +,S(e4420c3b,61e435a8,53784728,8426aa27,f23d224f,4ff7a14d,79898369,c08530dc,422b04b4,857984b9,9bebd74e,1588ad96,9b3c67a9,89b119a7,f7cd2c84,ecca8572) +,S(888bfdbf,c6ab299e,3ba0fadd,a614fb01,236e3c44,98ad07ce,cae08105,630aaaea,86e7253d,58cfe580,628fb94a,a1a8f40b,7ada97c9,d8713453,f06f4773,3770eb3c) +,S(f74d258b,f7d78b30,ddbde398,a1398164,d850f2d,48616cce,8ef4c436,10ef419c,d13d49af,1e6dadfc,94332213,cbb12d32,5d60eb6,d48cec9d,fcaa7ff2,53029204) +,S(a8b33e90,98d60483,a7009786,11b92257,65cea0d4,b7ac0ae0,67914110,5cda6df0,58cc238c,c08d957b,1172598e,96ff4762,1182b6c0,652c9ddc,650f3b51,3521c553) +,S(5af8b154,7477fad9,8e5dbbe3,d4a1a15e,7a4a6cb6,5ae13cb8,9699470a,1a3ef1ff,525ed201,d0c61832,36c8fac1,366e8173,3c0a8c15,b6ef7672,cc84b1a3,7d6f25bf) +,S(155c454,6d4c5c4a,21d14383,3df8662a,fa71e665,ab7ff0bc,8119f29e,3bec21c,20e99f67,2f641185,cdceaefe,8fd7247f,63842282,edeff6d0,378966bf,50411aae) +,S(53918586,ee182d66,e7c22aea,5a663770,c8fd7f0c,6738d473,1010660c,7425a56e,c7fea092,3052ec1d,7e2564a1,e70b8924,cdbf727a,7212052f,abc58d9d,1856152e) +,S(f7964dd9,a0fab716,74804abb,a7740f4d,cc52d7f2,8d5acd4f,67998bba,3bad6414,6a989f64,aba0f60c,e7bdc5c2,aec11de6,d469a122,f1bbfccb,31cac834,89f62c59) +,S(741c8c8a,f514f2de,7880a909,b327c6ee,ffd72ee2,b5b1658e,72059edb,cd663183,f35d276d,e5de1460,dc53532d,48afe736,e40b6b31,fb6fdb49,1224544,a487b2ae) +,S(de8c69bf,6cc2a28a,fc6ea9f1,26ef4f22,606e55f1,312177e9,b2ef8835,8b47945a,8de62853,6900d614,780e2fec,b4ff7066,70d84742,3b1c852c,8b85ca4b,40a48a02) +,S(81e16f39,619bb41c,d8a523b2,d3dd0135,d6a8d5fd,9a8446af,cd058ea3,6f57c537,6786fc73,7cd2ecca,d146de9b,951deec4,375c434d,7dbac32f,2395c265,bbf4b31e) +,S(459e9589,d079b22e,8c5625b8,a725b30a,e70f7f04,8ab21a20,f0123aae,7a6f988d,a56ede93,5b71a292,c4ad26ac,cc3506cc,915de21a,4557512e,e767e8c4,33f38e08) +,S(b079d755,4faba7ff,20818806,a70b4ead,f2e5bb51,d1abb758,2284f385,868ebddd,495ddc2d,552f174b,210f6577,3e434fa9,20b6c0c4,574601dc,fcf5b701,fe74dffe) +,S(75391bbb,5983fb21,41580de1,dd5b10b4,bb30af17,3f05c100,569abd2c,674f288b,57c7a6ba,c9154ab9,eb66032f,2135a52a,b319f534,d1f1c80d,b58a4af7,de958d67) +,S(71436957,f2848f75,fc461069,e4fc691f,5bda94f7,32fcbe7d,5a89e136,41524db4,1287f852,ef4fadd5,65a2813c,51b93c81,95658a5a,5cef224f,e92511db,df3f0007) +,S(b9038b33,fa75d097,dcc74c9,e25970ec,94ad076b,7279f785,3b7c98f1,41543f9c,a28c3169,69295ed0,3fbc1bf4,8e39c8d6,af59cef3,d6f66660,73470ab0,dbcf838e) +,S(b2c63c28,a9fbd900,fbc664e6,36dbe0cb,f259bdb9,d67c34b0,83b8af17,a5f86196,a8f050fc,69c63c4,67308421,89510ca5,b7920723,6f5c12b0,ee7103ee,cd49ebdd) +,S(cdd2a5be,a32b2195,f4a7edfa,dfb7c785,d06bf346,f38e928a,5c92cc7a,2417f39c,ab66e78,ac421c36,3203ee7,22b380ae,2133864c,83829f04,d2daddd6,b7fa621b) +,S(825d0864,b079a737,acef03a8,28838ec5,a27d740c,724f9c39,780cfa61,9f3db80f,bf3ee8b7,749e44a6,7f27e427,fe05eba5,c2154fad,bc2e2b43,a877219b,461071d3) +,S(37b79e3c,bf6bf402,c96930a8,79f4ce3d,d0c36477,f0b0e218,608b890f,bf5e24c7,d97142c7,cec5630b,a090ff4c,9213c2f0,c32a5bb2,f7b0ee29,2045ad03,10f66f1d) +,S(fba64ad0,678ea75d,409105bf,5b452ebf,89560b0e,fbb079d8,aed22c33,552b6135,e665362d,e4e637a8,a85dd844,1a806ad7,5c628f19,7d6dbe3b,df63c0b2,d1f938c) +,S(76787b8e,77ed36f,9d84897d,e84c476f,d3bd490d,184327a8,59ea3f97,ecd9cf72,cf79f14,1a3f38c3,cc126011,d1d78dfc,e3ba228b,33d64158,210cd942,3980a9b3) +,S(26babdd0,ffb3a334,85e6ba1c,5661a551,f16a129e,42f9fe8a,3691a50d,157b31,1b3a4ae2,b1cf1a46,fb666e33,a20370b2,e66a6c71,27edc8ee,e4c4072d,8c2c530d) +,S(15aedbb7,7a2fa1e9,7a6aa864,7729f28,7ca4d3f2,36046008,c1b031d6,9f76307a,3342a142,bf8b2360,6a574643,9c05f36b,f9408878,5354c788,a6dcfd96,ff073649) +,S(98ece0a3,9bb7cd4f,df4d5767,c6ef4cc1,c2b072a0,dfde5fb3,de100f57,466fe467,c4e924a8,218c93e7,eb829d0f,839556c6,1e679d29,6a0a6bba,6f4f463e,ab227e28) +,S(7d867505,fc213eed,4cdffafa,b067bb71,8a48cda2,fb323304,1989b6a8,3ff373a9,5df51ff0,3c212078,197c417e,f4a4014c,96e167f8,8cea0c1b,9199ef75,7c97d47c) +,S(f24f19d7,43578e1f,be3ece8b,7b00d5e2,bf269a48,9d76d17f,410b73a2,95b3001f,644a43ff,55448b66,7b048bbb,b34f4c79,16d2b547,5148020d,53e53627,623631f) +,S(93853304,5c22296e,1e911281,ed4bea22,1fd1061d,2039a27b,923dd3bc,800efd59,22988cf6,cd2d15,f799bbf,e4cc905e,2607da21,e9fad162,8c2fe699,8beb7040) +,S(f090c705,39834f16,253b14ee,8e4d2685,b91ff4ec,9897df0d,af1531c9,46319743,c1171885,1d30271d,a8a08391,f53a02b6,28c59c8c,9af3ad2a,bf158341,280bd522) +,S(e8fc8ec,ab5bf3b7,4a0234ae,b2a8eafc,df9bb08,a90078e6,7fb6cd3e,fea78c6e,b08165,34ce9bb7,46efcd71,362e0a75,a8397c0b,490e75c2,9b007b28,26ddabe5) +,S(46258cd1,87cb43e7,3003cef6,94af37e3,c7426dba,c8033aa0,9b0a0d71,9f126785,846141a7,5f060822,764dd82e,2c369821,75576ebf,7082c6d4,601ad237,55ad4fc4) +,S(e7f38b3b,db3887ef,529d27c3,f433410b,94109bf3,45f9b7a1,e65bee6a,cca3e430,f52440f1,5eaec702,90adec0c,b07e8ac,ab1fafb7,adbc8f81,9fac349,2acd589f) +,S(2d29e833,1472798d,75fd5b75,1f569da,fa9a0167,f73fc62e,da5b03a5,2f537aec,52d3dd1a,b117b9fa,a17c58cf,c0593603,d9e2f55f,5a230001,d18976e9,792d2be6) +,S(a3b2ec9c,abf7a7b7,77fb44c9,c7552dd8,f47609a4,6f943c7c,8ac97d1c,3891ffb6,6ce38c70,f091381f,53093385,22a5ad66,4d528061,5b01b417,d559cbbf,3cfc19de) +,S(afcb3c1e,7582a56c,e6504dd,ab3d1a8e,6cefe762,58543015,a21f65d3,3100f473,6baf1f9d,fbeb8499,8a6afc61,8e193103,67edbd87,57b2c4a3,8e5a76c6,8b171af8) +,S(88b2f212,dc899567,bf62bbea,9466a8c2,6650f74a,490b86ec,cdd35625,d90e2ac8,73fd757b,def6153f,d7f6d4f5,da1723e2,6ecd6c42,d994a08e,7e458a64,d18c30a5) +,S(bfdea9f,4ca32916,651c971d,7da6a2a7,395c5945,5c4940d4,93d991be,1dadef3c,82747044,7ef3f684,4200adea,5b8077cc,4166bc65,1f6d4a17,578b64d8,21bc20d8) +,S(5d45cb81,aa765d69,ca52e386,9491ecf0,e8fdf6a6,3d64e65b,5213647e,e4973ae5,a4a4a32b,51a76d77,773517e7,c103a7dc,fdab36fe,3cafa2bd,b17f82b1,2fd019db) +,S(f3a503ce,e14e9994,aa34fa53,1c43e5db,6d6fe092,45d2f3ae,87d06753,7ae7109d,2b1714c0,a24d50b4,20722daa,d1951b63,568aa5ba,7325785,ac555e55,f3ec49e8) +,S(94c3a76d,6f812ed2,454c225e,ab8e6b8a,4261953,37d1d2d9,b666ade3,88cbeeb5,bb89cee6,aa406746,dd4893b9,5bf1818a,3d1351fa,d66e104c,bc8d63a1,efa953e3) +,S(23dbfe81,5232403,39d5a662,51fda5bd,b9a2d3d1,181dea3c,c0c908c8,f0f4c050,caef3a71,229f10ef,488f8385,c79ed0e4,b5c16899,d40ab679,4e45b84b,4deae6b2) +,S(3907e3d1,20ef8619,c11dc69d,c57935f5,d2dcbeca,1b4bbb88,e4629ec6,acbbf1d2,438f96b7,d74e2b9d,cc17b0a6,c936844,e6aad1bc,58a20055,ba298446,1415e853) +,S(c140b19,3eeb04fd,80aa6a37,3407f591,651c944b,33788b71,e6f05e1,672b7d9c,949bfd15,59e24543,9d5fef6b,70763b63,899450ef,430d2e64,1f7077b9,29e44072) +,S(977cf05b,c96a39c8,1dd2fe26,930f75e4,2563c3b6,b06e28d1,740a547e,f2ea56c,3802e3a,8508164c,36e95e23,46ca468b,7e15878,36a9edc0,e70c0e6b,9aa10d52) +,S(1069c0d6,3995e0d4,cad51e5a,9e4e57f6,795a7bb3,9ee3c376,1e827b20,d8a034e3,e1aa1487,9c0e4c65,581d89c4,7abb5e48,47963aac,788fe804,1d2c194c,5a7fe33b) +,S(ff78c680,c3414181,ff3ab10c,74e2a755,42419af5,3527a45b,ba3825b3,c93e3682,eeb70adb,f05390f3,76b2382e,20b2dbed,86c8c574,df2f256e,1fc4a376,24d3615e) +,S(5ec9eb03,3acd3d6a,9c04c3ff,5838a6a0,2b0cb26b,d5c37d05,9056afd9,86bcddc7,92a287f6,eed0df0,17c0bed1,ed9445c3,e39d92b4,8c3a9c8b,9ae26749,732aff03) +,S(eaa206ba,792dd34b,7ee67e14,d8dbc7e8,bef0a54d,a7dc7ee9,339d8ec8,471b37d1,5577e65e,be719ca0,7fef2530,c40c1617,55e00d21,1d78e5e2,695107c6,8d1ee92e) +,S(26d062f5,6034012a,3d76789d,455cefa2,689ef08e,d3d87f54,af952afd,ceb3ed7,5eff1dac,ef6f5877,99c340c5,f0d1f6dd,dbe6faa2,5ace9109,4e7b5836,d1323424) +,S(800d415a,e50db62a,4f49e449,d11eda8e,1a2413f9,c341bbd1,492f2271,1e602d48,569cad6d,5d19b5d,2299ec5c,fd12d9f3,d5c8cb0e,756036fb,dc5957f5,7c02eb50) +,S(6c01a352,bc16dc19,973aaca5,f1a21676,e1531fbe,f8586bb,9853c627,9c9a1c90,833dd742,c9596a0f,2767226a,b2548139,a39a17d2,d64d6765,4c073b18,f8cc04e7) +,S(3accc922,f1b3ff0c,504e7c4d,4738d1ad,f0b914b8,d08ef8e8,a54e431,37742c26,aa9cd483,27d60b34,304dee26,c52b2979,52a2d118,82e10b17,86a09acb,3cb0ad7) +,S(a0df5c72,828b73f2,a380f75,3f8e6352,f8d978c1,ee6ebedf,2ec70ff1,d0f8ec72,b230adbe,f1e0bd13,1e622689,379e7232,b1327db6,eb0ee306,80d7a89f,a4ac6670) +,S(9d5f0d94,9776ec98,607a8dbb,d54865c6,d4bbc970,d3ff85a4,49d57b97,33b52c73,ff08efba,f4893add,54b8a056,ff60d345,ad7faf15,7d11acf2,2cea0f2,afc1a8) +,S(def5d92d,9ea1822e,dea5b293,58a51ec8,228f54b5,a6738917,fb7f2108,6dd3d84e,b6c740d,b94aebc3,138d3ad0,202bff37,9d5bc20a,d7c0307a,479599ee,8b3129) +,S(fa90b105,cdc8d74e,3f87136c,f4d075f,643fb0f1,ba6eb832,73642e65,84df33de,3e6b28c3,73f58eba,49e9e852,f0f0e244,3a180d92,52624c27,3c079dea,50bba36d) +,S(8212e821,ff2f7b90,c01c8461,3d4d9df9,ce077cb2,9706a349,575b8cd1,a3d0eb52,d59c05db,2fa3e756,fda56e8f,33540639,d24bb5ed,2c8aeb5b,8f8e43ff,703185f0) +,S(ee340cc8,1d1f71d7,4b1a641e,101d15e5,24f155b7,2afb4e49,f9cd8b20,ec61220d,915d833f,25355fd7,f1efa796,4a8e8b04,86f8141e,f5811438,f0083382,ae286f37) +,S(2c535525,dd2e3d83,740b257f,bb7223e2,302b5009,7f8366b5,aa26f65,442afee1,1cd4514e,f945b668,4f38f966,2f3b5402,92d43a79,7f73f947,67272713,b61cf765) +,S(df7763cf,8b57a288,cf4a9083,a2a5f977,404b9e1d,6e814f2b,aae9ad45,fa67d94d,4f7dc336,31fdb48c,612276bd,d388ee9d,bf05295c,1a92709c,b4fc33ac,b1580ce2) +,S(3514c240,d6d0f218,11374b92,8cd6d063,8ff99b8f,4d5e4da1,8fd126a0,ecdcee60,2a0d1f4a,1a1baf80,3c98421c,7777ed1a,ac17abb2,720975ce,e530a5c6,4197fca2) +,S(3de75d9,e58bcb81,94dde551,ab541d54,237a1b58,d935f60a,b54bd3c,6ae40a27,9ada14db,be2db058,c6bad8c4,86f25037,3ec422da,fdd5bddc,455b2b8d,25d97c75) +,S(a728ce0b,1804355b,c5c144f8,1d5eb1c2,90ad3d52,f502c5dd,ae28a08a,6de0420f,4387cca8,f72b51a0,cfb4893f,a85491fd,42cce3b9,ec3344f7,ac93d001,47ddad3d) +,S(ec3c9a8a,5b844c0,947b22e0,2503a815,1d8234ab,aa94032e,16c1d874,5551c39b,7b08bcef,45f0fa93,660845ca,44edfae8,c8ac51ce,e836b043,3825e410,8ebd7fa6) +,S(b0173d56,24945acb,3bab7e6c,617532cf,e619e7cc,e9872e93,26a8ec55,a24761db,78d27b5f,3e9a0761,d0feff12,198dcb11,72a81df3,1320f10d,44591ae3,5f1b381) +,S(d9eb3bf3,cd43dc8b,977c3890,ed3a4b8f,4e04bd11,f95963a7,970d9600,40cd73f6,98196b5c,c42e808a,3f1db718,98c50048,510b9e4,c9b69c41,e7f5b2fd,63b23044) +,S(fbda51af,8a40230a,c7602606,21f92b38,29bbf1b0,1c027f10,7ca54c08,65a84e74,64833994,aa453bd5,4c8729e4,e9010fcb,8fd427ba,3bbe7c71,e812513b,fcd6ccf5) +,S(ae4b9a7,51be1966,f564661,61262faf,472b14c5,f0532cf8,8af872f,ba583f9d,8b2c248b,aa9d7e5a,6f0c6eed,9ad3ab5f,e2bd4078,da4fa281,f4b672e4,9695ee5e) +,S(58040152,6088026,1668c6a2,e462845c,f29764e0,31edf6dd,a40b6e0c,4ccf6ff5,d2f96ae6,4a83c04d,a072f9a4,e39976d8,a2e5f88f,fed070cd,5fb1a701,db329485) +,S(4c28d686,57380f81,639abbd9,fafcf47f,cf477e26,1c46a03b,57ca7326,4c7e029e,694b8a9c,4fa75e14,56a67e8,acfac16,3122a941,150da022,59fb024e,ced8a70) +,S(de205ab3,9e005587,f23b51a7,3d806f81,6984fc77,7179a6b9,465a92c,ff25c9a4,ed84325e,12efc1c2,db42cf1f,eacc12c,48f68e42,9aec9135,5af5c327,c5273d83) +,S(5ea82868,c2cb0a7,4b658dd5,721e6571,b968f589,6d22a5f1,b3ec6b69,594273f4,88d10eb0,8db264f8,13adecce,636f6c58,e1828539,1e538ad,6e2237d2,384a9499) +,S(189cf7c8,64497416,420bd98a,3e3db4e7,c3b0dd46,d7b9bc9c,37f2d036,324998ba,bb3409ce,d46dff28,36601b1c,f7dd606d,7422118d,723f7da2,4d25802,1edaa083) +,S(70a17187,38f50d43,8868026,58100245,3c3c41d7,8ee5e14d,3cf536f9,bc82789f,15c95120,e112537e,41f33870,18f09d1e,101dc445,a9f36296,2b9462d7,bc27c1d6) +,S(578ac0ff,622adb6e,8c20eed,f3011085,8e4271aa,46833865,cff46b9b,fb3a368,f711a1dd,f5f3172d,2b9b2f7d,9d50818a,6b0ceaf5,1d67e15f,1f01ca5b,bbd7f851) +,S(dc9fbc03,476aa747,d64738ba,ddce8a5b,5887f177,fa6cdaea,3e3b05e2,8d840071,c0f6752c,8087dd25,f8d94a19,d294e550,8bd26774,cf4e7ab9,515f0a4e,89eee1b4) +,S(9749acff,a348cd5f,2e84dec0,b3d73151,4bb817dc,4596d78f,e0295215,47b0294d,df667dea,35e54810,daeec20b,e0e7d203,d89856c9,94501167,23de84e0,5781a1c6) +,S(e39fbdcf,2fd2730d,c3359f21,fa815478,ae8e7a98,d6b132e8,e92d343e,32657d85,bafe9881,70a78596,76a6ae3e,364885a2,62441578,8c31c283,20a06b0b,e5b5e01c) +,S(6009014e,27f13712,c00bda4,dc913516,e04b692a,ea32b89,e0fab8bf,91402c09,34910894,f6bd1cd2,cb796f58,8ddba661,eb4006a8,6cec32e5,7086099d,42359f6b) +,S(bf04f247,d02e46d8,2fb9bec4,f76b3eef,b644c92d,43622162,d419488e,87f2f773,d646f4e8,bb3c2e4c,f19ab00,4674e354,302e125b,c1ed684d,32b1cebc,7c2172f6) +,S(9609f7ec,b122b353,85871efd,915c6d99,3e5468ab,c30e9acf,f5b4dfaa,31e5e3c3,106bc52e,b933f0b8,71578ac7,74e2ad89,ebb4d9f9,eeb95592,5d6b50dd,988f9a27) +,S(b690cc3a,e1ebe25,a07440fb,5cbdf56f,f5ae581d,4dec7366,8da2ad62,bab9abe,83cd0023,fd3a3ddb,2cda9603,2b6c1657,35e5f9f6,249a535f,b9666a97,e049dbd1) +,S(72da508c,ed507744,1aa2bc11,4a9098d8,2c947948,395bf38d,6cd00977,91d326d4,165baa93,3433ea2b,e978036,4fec922e,e5d743db,b8117e09,5eafb467,3f668457) +,S(e0fd116e,540a3009,2954322f,5125a72d,1133201b,985d1017,29b1c9b7,a9851c37,c6bc7276,48aa707e,549e899d,630d3155,a516a06b,777997ac,8509045e,e5b88a92) +,S(9d4b8bea,85d4e412,ee0d6389,e883f9db,297f01c,d0695c13,abe2b682,94c10af1,5e2280d7,3f7c0832,d9ae62fd,6a884973,5bb66ebb,e68a38d3,8a95258a,97556181) +,S(eef84395,aa4b6e04,ddc56123,cd5eb31d,69f1255,1c216d25,5057a2d4,388e08ff,45098aa4,34e14364,40d8b26c,73179c48,87797e6d,59963928,b1d6b61,d62d2376) +,S(66fe5c7d,56e38e48,6134371e,76ef427,b4652daf,6e60cde7,39aa1165,c90d7654,2d72926b,b6edc93b,7e40ad43,3cbb6c4d,96d4c384,966ca582,a108a84d,c11a2fba) +,S(5135f9cc,1818caec,cc55a304,aa983c98,82f9e1c2,939f27af,dc2c6df2,c08ea2ed,5a526dd2,7dfe1750,4f8baf4e,115ae1b4,2619b5e1,47986954,27ca3621,e03f22d6) +,S(1a37c089,af7f4a9c,2ffa2bc,c22a3e29,82e3318a,ce1ae29c,3fdf9b62,3e6487ca,d389c033,8522230b,a96b33d2,373672cf,509b0312,35d2ec18,3c955ff0,3d2464a5) +,S(49f7a5ff,15daaf40,913c68df,c9016f95,ed1edb22,22fb1576,e4a11848,47a248a0,f9b55b01,3d637bc9,e9794246,fa6bbef4,d5ebec82,1a5d7f61,1a18c7a7,b9abd8fd) +,S(8247845e,5d9ef839,207f0f1e,26eb0470,3b87f835,ac3af130,253aaf88,1e641ed7,577d790c,1a23984e,7f1b1947,4cb42ad7,b4409c8f,23f391de,3e2440a9,e53c5f1e) +,S(8295984e,cd160241,ace92ef6,10853e8e,57b7823e,25eea4fb,8f118593,d90e0749,635321a6,6798d5f7,39a450e7,67b1c38a,2c5dac96,780c83c1,4c38dbc8,c0ac8353) +,S(d4c32bf6,d8fcf5ff,be0ba34e,770c68b5,fb5db412,b1341537,7b2e9c80,3dd75461,b3c73602,e23845b3,2c44272b,4eda70b9,a1a71e51,66d13353,74153f66,4485f93d) +,S(1a08e32d,4a968e6,a44703af,6b526a32,3babe7ca,6882cd84,9fd3f769,6cc897ff,46ed98bd,b77bed4a,25c2e19b,e2b14a8d,3fdc8209,70344b39,9a6b2a8e,afad4141) +,S(8559be0e,96723f04,4b05965,da8c9777,6b40e1ee,ce49de82,e7cfdc34,354f4af7,63e24b88,854e20d1,2c3f049b,2dd0906a,ba4db6de,aba42dc0,5af03dd4,91333ef6) +,S(e0e0b223,fc7a478a,849c0ee3,f2ef2a6,edd0f08d,e8ccc8c5,5e28bdc6,5e211e8,ca38832c,3aaffac1,206d38e1,25b8847a,6609a5e,d1288d1a,511d6037,9fca60fc) +,S(a237be98,2d8ed250,75801eaa,5917582c,265c3424,5679b4e2,34539ce,68e40a19,f4cd227,976a3780,454d5de4,7026d668,dc1bfc32,9003c87c,f48982a5,5a231318) +,S(28a84882,1eb08d07,f03fe770,5a16f2bc,5edbd0d6,b4367ef2,474f9b60,1205ccf2,9067d3e9,e7d77628,ba0526b1,71d23da9,162551bd,f00fa301,eecb5fc3,f856a17c) +,S(142fe8d5,42bd6b5d,325b8fdc,3820de5b,cc83e2ee,13d59d16,f4c69adc,98c1d5e8,2973f278,32533b9,559d1421,38d7fa60,1b66d106,1bf9c453,9c8310a1,dc75b150) +,S(5b3b611c,5d20649d,19bb00a,11b4b928,8f3d5ffb,700fb6b9,9c97b012,98ad87d2,849059c8,cde0e12,cdfa7621,9ed70bb4,192ead87,5a22c58f,8ebcb58a,f2519182) +,S(248a2249,5d4905a3,f61f74c3,5f35f6f5,2edbdc63,9454cf6b,192c7ce,50beb8af,b5a6f3ff,b707c108,1cd97b24,38867a1d,61eaf73e,9b89b5c3,b1136ac7,bbfd4c83) +,S(8a474a2a,292b50e9,d4d1fa99,a229023e,31ea2093,2d3a5d2,875384d3,dbff075c,7745285c,7d5341b1,e5c39289,16cf8fd2,45b5f045,d30d5dc8,721d6b65,ef804dd9) +,S(3fa01255,7fc042ea,67cbe4ea,86637313,826d10d2,48d5bb22,8ca8d113,5015ffec,10b9d325,f59b4b8e,ecbdca5a,58c48e18,162d436c,a3228b25,e3f75f27,26a937b3) +,S(b77b5c0e,7b94f1a4,dd88f007,1af049b3,6b778d5f,c8a828c0,46ac2195,9fd65648,31246bc0,6722bbea,8c9d5722,26edd609,e2b65d2e,9df6c21a,d54cb157,faaeccc5) +,S(2635f482,67e82b97,6c2ccad7,89bd4bb4,5671846e,d5d1d8e0,8f2c41ff,8535b44a,7edc804e,956091fb,667f9e4d,47b7353d,d2ce39c,b53234d,afb6d5a7,a72b8def) +,S(2306b52d,ae0952c9,c6b6f39b,6d1ea36b,ed0012da,18d77238,4a4e0866,361b474d,438250b8,8d7b8f6b,9a63688b,b2cefcf4,bd767472,bcd21221,582b56cf,325486c0) +,S(caa866b8,dde8284,8f586f9e,ed5a1d1f,aa5604ee,a8c0016e,248d897b,a9f74b4c,2818f140,349f78c4,572834ce,b805a424,e33746a7,d58fb8f2,9954a55f,dc09c341) +,S(818def5a,a0793d50,5f5e9cc4,29c01ed3,fb5e577b,b25277cb,b74c70fe,88bb1641,da9eee80,f7621031,80e2d54d,796367d1,f48ea2b4,93a1445f,bddb3ee2,ee4f276d) +,S(2c62cdfa,568d412f,cd764ed0,1bcac5bb,c36e11c3,5a295bf0,8b12df92,447bf736,e8b8e025,58117062,705ef985,9aba6418,ee73eabf,195325a8,f82c051a,3ec31979) +,S(28ab54d9,9db06e8c,d4a2f7cd,517d049,4e477f1a,b6eb3416,59bce807,e1f04adb,993b5319,a5cb9602,be656276,bf3e7d84,4fb00304,1ecbfb08,72aa29e6,7494541c) +,S(493e7c16,2254a40c,54b4eca4,223a8241,d6c2abf5,f15ebbb6,3ea4814c,512b6061,56387f7b,588d9d4c,fee36b59,7ae8f21e,5fcf817b,f097ec89,7def80ba,3ceed628) +,S(672e11be,e7439165,4fe118dd,a2c5d389,8922a68e,222c9bce,902d2062,b9c51d4f,3555134a,2382a9d,b4bcfbca,45e9d62b,db6bbfa,bfce7c5f,c5102dc3,23dc36e1) +,S(8f8d0f6e,e3b86889,8de86c06,b59f8e5c,27524b2c,2959e701,38b2c3,8eda550a,e7def281,9e27abc5,9940fed6,687bcb3b,f364296c,26bf1fd4,5d417103,49212f34) +,S(da8c5783,4a11f0a5,797cf4c7,9179f30b,118f2802,1205d495,e8213fb5,78b293e3,6ecfe35c,6f663975,86bb1c3,ab5dc518,7c7a0ce1,b6338d28,a2c01df3,89c35f91) +,S(4953e0f5,3b1965ff,3efec2d6,fa99ba0c,fbce09b3,83e20ef4,2657faa1,49681955,c210c78d,9d1635b2,3e468324,7980766,3c28fa97,18d0ca39,fc09b541,975541c6) +,S(5491fdaf,48a0e1e1,be21f7fd,be910bd6,5cd3e0c8,16da38db,6c742042,3775f293,65273688,5d1c5338,a3be9b29,9b51f4f3,9c82de96,318643e,3d34ecd2,fc7ffae7) +,S(b9c4afa8,42363b9,fb583885,8f17d1b6,b0a8529,43c907f0,af3ec9b5,abc168d1,ebd3106b,1b75ebed,5d013aa1,eca7d8d5,e24d511b,6d4178e,514fe156,4b550d9a) +,S(918da36c,444847a1,b5f9826d,eab809d5,d6802e73,3abca386,24237683,92d2bd62,6e6237af,d7cc87e6,15e14113,ab38989e,889d5e6e,6166eeca,abf1ddc,8a53e218) +,S(777d2994,da1f514a,800240e,59b743cc,748eb42f,4572ac93,a2e42e37,1d3392a,6193d7,e9eb3195,2cab96f4,14e1d24f,9d92505a,9d21f864,5e6dd3a3,8a9680c6) +,S(108e6ea0,9a08c5c5,e27b8b3e,af24ef55,3988c650,34ef954c,4d17f232,5723bf3d,1a8b974b,86db6bb4,7c314ebf,f811937a,b8a0dd76,e824dbe2,88fde1bf,fc13b2eb) +,S(36d9dadb,65a54d49,1198a486,8deae1fd,3c2dcef4,4a682839,b5dac54a,456bf185,25e84356,d93b5d86,25eec306,565bef19,1edd9bf8,347dcd8d,785024b,2147bbf7) +,S(3e0ebab1,43ece776,729d8a9e,302726ce,aab89654,23292fe3,ab2e71d3,66531fa7,6e7b2608,556d6236,8948f32b,2fb04b34,8e38eae1,3d9abd5c,691383d9,958d41cd) +,S(923dc76a,f315f31a,a1de9521,a13ee9ab,ab08ab42,157f483,2babdb5c,18fa3728,7a8f17b6,7439857b,3141bfca,6d89e649,7facf685,6331003e,f6bee07c,ed3ae47) +,S(f87fff32,2dfeae3,4ce9cb81,51ce2e17,6bee02a9,37baac6d,e85c4ea0,3d6a6618,74e49037,aa726e0a,357ace25,603cdbfe,7c14a4cc,2e1e4e13,c7176121,585a03e0) +,S(1388065d,dd7f69a0,11dec106,b539f7e9,e00b5aff,7568820,e33f9c1,18881ed,acab1d8f,140661dd,422f72a5,5ab4242c,65d1a932,23fe732c,92b32317,4d13408e) +,S(e5476b1e,a99b6a08,83731542,7a3751b8,3d685b34,acc5201e,59e9b623,ac4b6941,57d5b2f1,c4dd795a,323c794c,aa8fa754,bc86dd67,3cc8577b,b418b2e9,5f3b4e21) +,S(f938ff60,f1e8ebea,4c229d89,4c98418e,90c14981,4ed7909c,3dd47cb0,15cd1f15,d7172212,1a0cc646,a0576e29,372bfbd6,37fe2c5,b6ed6821,4da50318,eebb13e1) +,S(b31bd410,7c4a5108,1795544d,5854f9bd,180b494f,a08878ad,8ad5a3bd,98a30aa3,94261097,6bd58962,5e941aff,a007eab3,d33828f0,e12b31c1,8fb6f5c0,50519e82) +,S(b3ed87ba,83210d01,1a2df141,b9a6c6d3,33abd692,55da3af7,b4447d03,f4c1cb2f,949dd379,29c757e1,a970273c,b6341e66,b90be71b,347e747a,be0914aa,4dd632b3) +,S(60e6c240,1ea790d8,4bb95e78,731bca08,89a57c0e,38b87f46,668ee16d,31221e17,f247bf35,c722098f,2a5c800a,571eaf0,36738704,4050d98c,c03bd146,ca9465c2) +,S(2ae806b0,4cc8d047,e7dd8d37,713cd71,eaf3e16a,f74c3b87,994afaf1,c3750a80,a8d978ac,b29771b5,1a87fafe,605ca0ee,6facf6a2,41aa135a,e38de4eb,b2e2e954) +,S(9af178a4,bbba2d29,e7a0bb15,51f6f3a9,d2384386,3779f439,58a424c2,29bb5c47,c1031fba,a0238857,a487ac85,72fbb3d1,4052a4ba,adcdf259,78101a21,ae79abd3) +,S(9dd6a721,e9cb27af,2e7da097,c9e8a250,fd78a531,330ecb2c,3a116d99,6008f523,e6837b7d,647309c9,4712adb1,4bc65787,fc4e975d,f4e82173,57dc9d3f,e7f36080) +,S(cf5f16be,d7a1a1e5,3b3e3f1d,e44efbb7,fcb717b6,9a28bf0f,e31a1da,a5e91c52,9549ebd5,4d87207b,c63912c9,99920cf4,b0c80a0d,8a69d4b1,4852834a,c4487497) +,S(c6794890,810954d0,782466a3,c52a0149,de577cfb,3a2001af,d45e905e,dfa64722,4a4fdf51,deb64031,bfca5fbd,5c827829,8621b625,1be69187,fcc2460d,f75ca468) +,S(a25fffcb,bee9e0ff,1659c1ea,41406aa,b03b4920,fe1a63e4,442d5241,30599713,176b9e06,5a821189,ac3fc28a,fb72a1ba,40fab791,4de987e2,1236f244,bf41486a) +,S(6079a722,fd5d923b,16acfafc,7248e329,128cab6a,cae9e5ec,dca49774,e0c5bf71,c09972de,99ca5a45,c4f6f1d9,10337efe,23f5292,bd5ecc45,df0f7aeb,d18c4bcc) +,S(188cb43d,9d4078e7,18fffc42,8b16723e,f64b950f,cab7582c,8139dcb9,2b66b403,a038fa37,ec9ea240,9a2879,9556916d,fcc8c2f3,358ab14c,140f7f1a,23af5404) +,S(e310fc40,cb575c68,f5792506,65567ec8,c445ca34,68cfd02c,d781b27e,f819bb30,31328a65,5ba312ee,5e901def,66e7b946,e0c18336,a0f1784d,df66b89e,2abe625e) +,S(dd86485d,1c8245dd,d940bdeb,1f462cc8,f15693df,246c3c8e,52b574bf,77dea4ac,cd6bb7c2,56c9a270,4c690175,ba298a09,8963a8b4,7e743b48,33fc09c6,9966ce2) +,S(1b7ed20c,f81567b8,6f7dc67c,ab69e197,dba77399,9b36097b,de85d02a,7019aef,f814e92a,d1cb4386,550d89b5,9a4bc378,df428d54,d314d531,be6fc35e,f0e3d1bb) +,S(a15587b,2d528338,ed146a03,670b3f1d,9b41cd0c,a0645897,a0f719a7,b5ded284,2aefe34b,5aba1a4e,e30d225c,c4023ee7,12fc1522,ad6b452b,fe6ea727,122e925d) +,S(57bb9f9,c99da691,98cfcf20,9a0940ce,8d19463,9e4d1054,484953e,936a5484,63d0fba8,d90226e8,2c7be5e4,1d77bf0f,700dc57b,d8d3079e,9a6a47c8,331660e4) +,S(224295b3,7fa1b4a0,d7d7f7a1,2113b21,291f84fb,8fa4b746,bab925d5,93bd262c,d60ac40,b1e4b0da,7563eb74,c222660a,46ed388e,c4517ddc,ea2962b9,38195796) +,S(2434eae6,a34ec53e,7c031b2,9e74354e,d6101ca0,f228055e,53b39a6b,5453b0e3,2ff5ed61,d57a04bb,138f3956,33a20e60,8b04f775,6476d0ed,6ce7a382,743ce23d) +,S(448de592,907c1113,bdb04c64,9fb22b05,ef2e6ab6,36d0a03b,700a5871,89885ce9,d7aacffd,6ab35068,debe6dea,25b02930,63af8b10,d4592880,3d0421cc,a879eabc) +,S(8405cf73,5aef7674,7feb92e7,1aa3ba0,defc0a1c,c1da698f,7c2a3718,6393dd76,5795d006,49cd10a0,4a661c78,3f4a8711,89c9ef2f,ba86853f,c7955258,e6c407f2) +,S(698974c1,e5c1487d,fe9349aa,371eb86,9318accd,72c66d5,581211cd,6e08efc9,37c2238c,dc8f4bf5,1f76536c,2ab9af61,bcec872a,bb84ce15,90bc1c0f,ef84509c) +,S(8a37857a,be3cabc6,7197781d,f315f78b,e6548c61,1b846d8d,ce2e5732,643fee05,29882f68,9154cd0,7e455e4d,9e3866d4,5e28e99b,21dde0b7,8b87698b,4cf674ed) +,S(a871b34f,5cdd39fe,13104c96,87a8a717,25ec8cdf,fa60846,793bce03,2397eaef,84f96417,9de103c0,9f7e2dc6,86c9213c,642312de,8de6cc5d,f3ffd7a2,c387cb28) +,S(49bcafb8,9f78a7f5,a8857905,e99e74ac,9ec66ec7,aeab8fb4,fd964ce6,8286f44a,e7db8ccb,5641ab74,a791d2ca,7bd193ea,71124a61,2716d94f,4eae407c,ed9eff6f) +,S(d5c7d92f,2f05c9e5,8f586289,dffe8499,40d9b84b,79feb686,c3f71300,f68b6df6,a68b2a5c,219ea38e,a04b272c,e1cf42bb,6fcb2b2b,fce36043,4f1802af,1d51c08e) +,S(2ac2fe3c,fea4cd7b,9c74a249,cb0c8ed0,6a825cbe,2826c56c,fe4a8db5,3b286696,7d90a659,b008a51e,ca2f6031,c6ca6078,505bb46c,f00a3834,a67e7804,b60f43f) +,S(7c09acbc,62827ecf,4f63a85f,5a0eda7a,5b0069ef,b03b51d9,2f61c5aa,10a39234,e07d2539,c29128c5,685f8641,45fdc318,854b56f2,962b001,5a96c688,69624836) +,S(e886dd56,7e1b42e7,69941ae7,13b2fad3,3bb4ff02,34a27a7f,2c496bbf,6ffbf9f0,d4b2277b,abeef80f,371c097f,df4e5de,5a4666f3,c630b010,81d1172d,c667a877) +,S(edf65cc8,e83983,c5a178f0,ff60bb1b,8ca8a6e5,85344464,73a199d,59c2dc,a2426713,f0c5309f,84a4710a,2de1cdfb,118a4db,1070d23a,92c76244,89ab2d5e) +,S(29c12f04,b78a6ebb,9356dbbe,5b152323,435ad7bb,a3d2d036,a746af6e,d26d17f8,8399275,bf6211aa,78ea211f,39ec451a,b3336d27,fa059935,8b7fcece,216eae4a) +,S(d5db8b8c,280e9914,f0a034ac,3e0f9fcf,169f2046,4a618789,994f8a79,6663c3e2,41163c3a,85a0039,f5e5635a,819941f7,98f27078,aa65ecbb,eb60d577,c263412b) +,S(990a4fa5,630eb00d,958e57ac,8bb4f7fe,10e1055a,c547d165,7000a17c,d9135f8d,f25ee025,491fa289,446a0364,68c71217,eb252414,f8fcf291,2ecb23d7,4bc9653b) +,S(d784ecf0,1c7849dc,ec901c3b,3315cf53,4e3049ec,c68d71b7,41637b57,9e9c0935,5de98d22,cc382aed,d8c82a84,1ec6fd46,30672008,8567dde,6b2d64fb,6b4205ce) +,S(6576cd0,58850cbb,aaf53b67,bdf3c19d,9b4d38e5,19c99338,3204cd35,6ce38193,a329993e,92d1e56e,31c1fbeb,27648fe3,d984960e,30bf3420,658f74c6,5710eaca) +,S(957788e5,f675c0c6,4649a808,1c4549ff,20ccb177,3d26e4d8,bb94844e,bc35efce,7ea4431c,dc4bee37,d95c3948,615e17cf,1d32906,9bc10a8c,515aa0e,521d9b5b) +,S(8af7ca80,5053b537,ec501abd,78c13ff0,da5549f3,b045096b,1452c05,e39e4a2e,25e86225,aac042e4,cfb235e6,da40543,6a436206,e9d67fb8,e1b5836a,2d7e8402) +,S(49bf0393,e16e634e,202ff642,ad79c179,e6eb7456,637b2ba2,623937d0,e215236c,8590613f,401de0d4,1cc0ce1e,e9a13441,41e5f80c,179ac722,d603e697,a431a85d) +,S(c82e2a25,2572863,527b6858,18c9b6dd,df3653ed,a264b5af,dc5ebd19,d4192063,3b86f800,cd87c1bd,3d70af04,47f9e939,c2717ff7,376f91c7,b2675e3e,39a6de8c) +,S(9ce43920,2b9a8f7c,3aefc9c3,fe4d227d,42ec1355,158b2041,a3712dfd,d8eedbf3,dbbb5b05,859aedd3,faef15d5,9a939ff9,53a54bb3,dc2ee6e2,6d5a20c9,ff5b52f6) +,S(e07009e,49de05ca,52d20221,3ecab4b8,baa707ad,96ef24a,168bdb6b,16fe6227,6a35b0bf,e752ba26,81b865b0,ade8d4eb,2583833c,a1523b2e,4178f9ce,6cbc33c9) +,S(eb4dcdb4,7e09af61,e4694814,79612715,d588615f,f4c23fab,65015bd,821da888,63e38168,584602fa,1e06536a,d4c8115c,86305c,41040083,4a55d153,e9661b8f) +,S(be5f8681,2bf20113,1d257015,dd29dacf,a2c65907,26078f7b,f864f339,10e49db6,d0f420e4,56174aff,5995a92d,a73fa511,43c4f1b8,c40e9f30,8f04011d,956d9da7) +,S(3b32dc39,10e75f9d,98fa1318,edd7e8ef,db214a5c,b31d5ed7,4a61f881,355b99a8,1a12de86,19c2e9e5,9fddf756,e752e54b,6e4e0aac,572fbc42,18baf1a2,a3a18bc5) +,S(95ac5dce,5c06e885,65d2922e,a3f386b7,dde10735,169729d0,a3c3e7a4,86e04db1,1a22cd87,ab1b1821,e3c49e85,616b7b49,680edb77,251969fd,e77b5b27,3ef39838) +,S(6e4ed0fb,a4b0f13e,335d1b74,a5ed295e,7dad591e,a8dc5cc,d301836d,7ce708eb,9056166a,c61c4e09,ed988e6e,59cd9ab0,16a2ef99,e99c1a54,ac37f152,a34b931) +,S(8b7c3394,208e0ee1,374925e1,7827d849,ea8d9a79,91d2d859,898eba2c,23a42e10,b6ab9237,96720bcc,910e6440,75ef3ff,2c8bebf6,2f21ad0d,3abeb2,7043d193) +,S(fda7b47,f6690d73,884965b9,8b6f5dca,dd48ab4d,8f96edcc,8e20584b,b0f871c6,9b402435,a272cc6d,28b487e8,b54b473c,30929222,55e47560,e18b8e6b,ca926c5d) +,S(f15da0f5,395a5a1a,ef8beb53,4c0e9aa3,6e63018b,cc91a55d,ad6106a3,3dbcb217,28cf69ea,bb3b0151,917571ac,48609723,80be252f,bcf98f6d,db6ebf76,578a8f1c) +,S(c6e266dd,2c7817e0,c92378e6,38709a97,d9a208b8,7fa832f4,abfd881b,bcaf9253,2af100c1,fa9bb3a3,c8e95e3,5d0bef58,5ffa7adc,93232082,84291ee7,cb769626) +,S(4bf8aa02,fdd201e,488c21a8,33925687,7f5a727a,4fdab13f,d133d08,b6e28753,58812658,ad9c4d59,a15c6bd7,9e6c622,d8fe5f05,95cbdd88,f39be9d3,cd5fc8b8) +,S(aa985f28,6dd485a,5589fc39,da77fb95,5cda673,db61e48b,972fc129,7cd0a6d3,abb0f897,2f1163b3,29a5a92d,a1ff02e0,7c277710,10e24a04,e1e5f60d,1e4b75b2) +,S(8174d101,ec13a1cf,4eb9f88d,88c14121,6c2fa04b,ced197c,8e1732ec,61c41c9d,c6765dc2,31b02e7f,eaf7a662,5a639b00,c6e5d94b,1a791bbb,46f6a758,8fd79aa7) +,S(f9f64ea9,d6039297,b728fe09,85a9a3a2,92573aac,fdbe6cbb,8702f04f,6df3d876,4146c7c1,406067d0,160950f2,1172e879,188069fb,d87eef9b,1a67d2c9,9a4ca01a) +,S(3e643a70,5a6885eb,3d34454a,2a5de925,f8067cbc,1ca92951,d8865ecc,a8f1b3b4,2deca704,5525534e,de9d54d1,d777a996,8b5e5882,7162ef11,b0fe08b7,e2fb0355) +,S(444df411,fcc3ac07,7e95d200,d6f76d03,caae45d2,b87a882e,4c0966f3,376c63c5,b4566598,643d964a,a7cd1a4d,f2decf3d,ec520c93,ac6e32bc,64350aa5,5416cc23) +,S(12a2f61f,58be35fe,314a7beb,1a6810a4,19cdaa6a,8424d465,bbb7a87a,646946a0,9a7bdf44,94c2cc8f,f1953946,1a69fbca,48410b31,95d79069,8dfd535d,290ba47d) +,S(bdd72692,462573c7,3d48af94,b687bd22,511b2b7a,93f48793,3d52b2b7,e264cb9f,56e6c4e,b794fab0,bd5ac992,3f7469aa,858e6df7,dc40f4ed,955c0df7,a2123874) +,S(e1bd5558,77c3f55d,12b4f156,ea4da9ef,c974bd29,d7d043d6,ec7fe56,a4066bdd,8dcd9b99,fbe7436a,2382bea5,6c5a32f7,789cbf1b,1c44433f,f5a95075,c3233170) +,S(a7c68de4,781c8294,bea46609,f4a1f550,fc064eb8,b071985d,804351d2,60132ad9,a08278b6,3d9efeb5,1062b240,d45989ba,6f6bbf9b,ebfa0766,8428862b,adb42c32) +,S(4f3a8769,b4c6e369,cbdeb90f,5137d07a,5fa0d66,3d8ca79a,56b3c70d,2a9aa855,f6c051c,5f701e15,348ed9f2,c4707584,b9d6db2c,b62bf93e,35b6486e,ea6ea169) +,S(deaabc1c,8089ebfd,841cac66,d8a6bb58,36fd047f,cb435a8d,74463dde,f444f232,fb7cc4ba,49c26e7a,a826e654,7c8a6d64,ccd14a49,62a6d4f9,f50c293,c228e8a1) +,S(c411af61,547b1b38,12551e1f,4ef2b32e,fd7f33ab,7cfce378,8b5b8f93,319c0695,d2bee7d7,3d50f82a,e9266d21,2fcfbb76,294ace4e,f6222ab4,797bbf88,558cb66b) +,S(d0ff6a94,485bdda8,d0c28494,5b9dddc4,4a35f101,d0a89637,8210bdea,909ec5e7,ab3f60fd,2923a8f2,d02b7e83,d8f953c4,cb1910ff,83a92a65,e2121f22,364f620f) +,S(a0d05cc,9555ed92,2851baea,35ad6cb1,6f2744,b3fe424,98235aa7,1f08c0f1,843b1105,237158a7,bd7e67c,a20b2705,50afe2ad,318077dc,4714c7c9,d6339948) +,S(bea08445,eb295fba,5c83ef6,7d909978,7d599642,893fb9d0,568e5920,1e3f5d32,2a81a7ff,b3f7f453,d78bb2bb,41d12ed1,e26594d6,bd4f5cc7,908f322a,6cce10e3) +,S(e8ebcc4c,431be689,9eb5aea6,f6ab9700,551c3098,c5acece3,e822c7de,e6f0d5f0,ef99a461,760ae4f5,8fbd511f,59a2ae79,6d65db64,eaa0d61e,97d0a3b,31fa902e) +,S(4948606c,dd73dc44,ecf1db77,c796d8b6,806bab5,85877cd1,d9630bce,966e8fd2,3c67333f,786c8c67,841630aa,639446a8,cb8c13ed,f3db9123,bc64b925,beb95f80) +,S(4f259b32,40d9efba,4e3e40cc,a66cd494,fb5a19a6,5b5c8a16,5c18792e,9ea778c2,fdd88087,78c26999,c4a00605,49f4ea2c,b1590f7f,4748cefb,1b68eaae,4d17446d) +,S(f303a493,e14d7f52,f276e2b4,3e118eeb,1e7e918d,e7aa7f33,7e350fa4,7adfcc29,c49fa46d,befe53f,5f5bc49d,aa6ad265,453f7977,c5fe4163,cfa1bd4b,69141b57) +,S(e00c4b17,f4c0800e,c1b11859,c40a560d,58b9fdfc,29df5a41,ea4b2f53,c1df595d,62441a66,c3ec4d29,d18d0d95,7b4ccd18,a72a1003,d34832fb,8c81fb75,9763b99d) +,S(484b8486,1af6af52,a47603bd,2b754be7,ca16bef2,21581171,4cfb2301,db72ce70,36f84ff5,a0ac79a3,fe696865,2a0a6759,cf72e5c5,8c76359,d477e72c,4d32fbc7) +,S(88be58ea,d8b51f44,a1be6a8a,7cbc149b,ca07fece,11a7adab,210467b8,4cd93f14,94c10caf,7037fbc1,71cd6cd6,a0697b19,8ebbebf4,9f00c67d,4d46ebca,92687fdc) +,S(abf6ceaf,9fddd7ef,44a75e11,b10119b7,7372a3d4,7c163b77,384de944,47da781b,5cadb30c,6320c67a,e9dfe2f3,b9bb4ef5,41718392,81c4a59a,7960eccc,6795d290) +,S(e8c26522,9ef665b2,67fcf10a,17c6c3cd,4e221e0d,95663deb,74f8384e,b89d173a,9a69fbad,f9b050ef,d231965b,30d91646,bec7ba26,9fcd0934,35ae11f,ca2aeeb3) +,S(66652bd8,bf72ecb9,5e670a24,e8683155,e031f3ea,91fff58d,f60fa4ff,d9c6c23e,4844ec08,3f182b73,22c2fbb6,6f6630a6,5fa6901,52dcfb3b,4b65a3a9,30be994e) +,S(207880c2,d8421040,dc8202c4,45bf6d58,75cac841,532aaf0b,adf252dd,4c2c0964,2b1819d7,3eb24de1,ae4ef316,7c92a7b1,a562d93,80fdc54,e916d1f0,c1860cfd) +,S(46b9bde4,53d0692e,ec933485,ed0dff68,7e8f262c,7d9270cc,763fd959,2013f581,58af9be6,3ae599c0,f499b0d6,3085b7d5,394cc786,9f2fe225,6ecabd2c,a14f9bf7) +,S(5d9b2574,dbebe5d0,9756e2f,29af3aa,a2fa2555,d629a78b,82b1d679,75ea3e11,ed9f40ac,aa6ac68f,cee3dd50,7629758f,f3a64190,1658f0f0,2b87b645,ee59263) +,S(ecb5ad82,8b986425,9a76a649,1774338b,dc598fd7,77cfec52,d086c172,6e590e4f,2b78936,73c6d87e,a954b5bf,52f8212f,65d33ea7,ccc4b4e3,1d98df2,c9887886) +,S(b32b64c1,5e711b8a,1d174725,de55c,ddd62e99,fc90e96e,8f3ca532,3924ea67,af5d61d3,6dceadf2,34a7a94d,e7e4be1a,4d81b84d,e0fe16a5,403b0c24,a6ac2083) +,S(5137f71b,71582334,a6d57aa1,5cf6ab3c,8b7f6842,a800b4ab,1c308fc5,820cd364,f7ea5fec,c9b17dab,c6a248d,49757711,e4a3b38,95658381,efa652a4,4df1eab4) +,S(b9bb0e1a,390b5889,3e113a89,801c3f5c,25f94e65,63161539,e7c56297,ce9ea866,d8db3c90,3c3d35fb,ae4e14ec,e81e4e31,247f60a8,2539880e,6b66951f,8324a11) +,S(3f091967,6da40ec2,4691c21,1c42f47b,88cc1192,ec1827d3,17beb468,924fe9af,f1b44826,3e882c1f,18ae6a81,238d0724,ba4c04bd,d3fcd395,8775df5c,16bb8de9) +,S(b5fa25f1,dad4600e,8d55e7af,c70a1b2c,3767da47,d625ced8,d55ac88,d893b4fe,7575f661,f2ba296c,7ed3a190,8a49d099,83e49503,ba17a70e,1d6939b2,8ad22347) +,S(a0371ce2,dc6a025b,78db797e,90373069,e4ca06ea,6b1cc018,3d4c6f7d,680d17b0,92b5a8a8,9a5005ce,e851c521,4599b774,24f0ff74,5c4af6bc,6937c432,e859919d) +,S(5aae6e90,a49107,78863967,d7d42020,cd0a9828,da25ac8c,7b19942a,1ee2f210,ed9cc20,82a54f3f,fd7218f7,b79ec399,7e82fdc4,cd783957,5760e4f7,b7ee8fd7) +,S(54e7daec,f4a9b6f4,b100f964,850deb65,64efddb2,e6ce7b05,3fed2a28,1a0841a0,d9ada32a,54773a81,d5a857ad,8b34d96,70d46785,70943dae,cc83deca,97c9c63d) +,S(e9b29910,d8bcef46,b48d8be0,c7649b84,383ed752,391ecd7a,bdb454c8,8fbf44e8,eb268094,f669e91f,a5173965,50e4c40b,d7a26792,9a8df3de,b84c703c,261d6b69) +,S(43f95222,f254a513,1977c589,577d1d9d,3f537a1c,9fe14ef6,384e1779,da546a97,d7d3c653,43345c47,f245b305,b662a2df,eef80b3,97b3f5e1,4b0eceb5,71cb18cc) +,S(9fb45fd5,3999fb92,830bc090,216ae485,6723385c,7224b1d,cc91294b,a6c3b103,5b47081b,dbfd82d3,90d7bf7f,39a494bb,6731b506,b827213a,277f7ac9,febdf451) +,S(dd92282b,835529cd,3295919b,2eb466f5,9ca840e,20ad87f2,3cbc824b,b71dcce9,ec45f6e7,bc59d668,b1ef4a29,d1e23bcd,f3796268,3b58fb7a,d58a9e1c,5f726371) +,S(7482f012,9fbdd7,bf99def4,f3de23c5,fe29036f,c1ad5c38,686c05da,dc16348d,a43c4a0d,5497f6f0,bf2b2a20,b65bad11,73fc0dba,c24e65f8,db1663a6,b840abd1) +,S(fa4cfd95,97fd6278,1dd2f587,a5b03195,537fca30,f7a9a377,a69a6869,70ebd9a5,4568b6c9,9ddbf778,bd31d269,be18a8fd,e2a35031,a30505c,1b8ebe1d,c7ae823a) +,S(51e0d3da,e7763e8e,81905e1b,4f1ffebe,2f1078d3,27944c53,d9b7d110,3fa60b9e,118da0c4,ab572fac,cf9f20b9,2108ed0,1da673a5,2395f3f7,bfa2ff2a,36da6740) +,S(7d8e567c,c0552335,38c3b857,289e96f4,502fc674,f40ebf86,c482a668,dbc9530,76c3fc46,c6446a,4b9fe552,ba8b2614,7484581c,585c8ad7,600c2ea6,8eb37571) +,S(d924c6d3,b400196f,dd1f216f,dfa18c79,1c316d67,9ef4c0ed,8d12b94a,cd1dfb1f,ec33cee,881c04b8,b11eb3f2,88dd9b92,90b9d119,26c692e5,9d82aa80,4febcea8) +,S(8dcb330b,6f629c8,d18d28b9,7a99d82b,3965cb1a,bf953b8f,33074457,5e93c0db,3d8a2d19,8323902d,1b129b57,313f81a8,b58b7340,f6f7c97a,b6d74f37,a46857a3) +,S(dd1c4a3b,94b06378,8cf86797,ddaeb671,57910f1d,35a32e80,d3f749f6,bea5d2b5,efbec0c7,5a69583d,643e5be9,ab78cb78,3d2afebc,73d7c64c,869bede,b3230777) +,S(2b03de34,1dfad7f7,395f6ec4,84d292c1,ec0275f2,bc762dab,7658a885,37f5e772,3235e8fb,3818582c,55bd48bb,49f78e5e,803d7d1,ae49bbdf,97166a76,c3c0d0e3) +,S(3a795b0f,ba145bc9,7e7a80ef,a6b77132,677f06bd,4c3e7f39,47978c83,1428dde2,f4125b9d,df038a26,37cbc9e1,eed40aec,81af644e,a3f1352b,526df579,44914691) +,S(ecf813b,a133d6cb,9c46c081,b0a851db,121f5c21,788d1e59,ef8c0588,684fcbbf,69e0daf2,a7b7b473,7f2becd0,789529c2,597e3915,ebe12093,ecd7738,13e53560) +,S(637c4982,c14163e4,1037191d,d4e09d3f,74383245,5368cc33,57669186,d6217d5a,da5a2ab8,fd83d2e3,25baaa5c,a378318,f6fb3747,5ac64b77,a74da573,deecb410) +,S(19fe73a2,d8875b57,dcff5fac,20530da0,54255658,1251f7c6,b198b3e3,71b90e87,3b03ac18,2ccc2518,c367a77a,1fe5e8b9,a3264681,28fc1ad3,5a39f7a0,d8151ca0) +,S(16ea62a,d4cdc00,9d151b3,b6680b0,c68b4cbd,f144c234,71d55b7f,3bb7ebb2,8cac4a1c,53672e59,338612a8,995980c1,35f02060,2e3b9f6a,8fffce9d,e53fa629) +,S(6b9d68b5,454c6d21,678e5413,279f9a28,e1eed485,fbf512e8,49f35394,22a40970,b63f5044,98ebe37d,af9e6969,da888eb7,907f8565,4b05d472,fa4b1b27,ae365482) +,S(1fa20c8c,8f0812c,2e7dc948,afb7ec92,32c3c27d,23165d05,868dd127,b5ed3e6,c5257f4d,82133642,7df68606,2c229fea,6cd16857,280a60cf,ed84deb6,29838076) +,S(73a83c92,7daeb4de,1cf1df6b,31d8f536,af15bb38,be506263,e8e8a6a1,bf6acc86,a08acbe4,cd6c30b,82b4e24d,e8c9b891,26a21dab,e4df40ab,2fb7ad6a,82eb8adc) +,S(3a995664,924e2dee,ca278cf9,3ce265e2,85ba90ce,fd2a55a4,8e1761ee,7588bdbe,f3221adc,99eb33d3,52465d93,91caf18e,d011a1bd,3b7cf39,dd48d45c,44cbbb0e) +,S(7c65ee19,b032f5cc,1d45cd9b,9c160e86,5ca516fa,b4f88596,5e07f269,3d429c26,47c7faf9,e3df277,d31076ae,4be2224d,8cc54a5e,3743e287,ff9572a4,23704c5) +,S(c010694c,6750dbbd,bd953118,dccb8d03,2f05e6d9,65db8fb0,3dc00b22,e4d397dc,3b4914c6,dd6c5441,59dfde10,a196f132,5a5e6101,52d41be2,39583b80,6c8e730c) +,S(e255848e,d28d8b6d,6f95a12b,92136131,a33dcb2c,a723fae7,5e317cf6,62519d57,62f7d5aa,1757dad7,98207dcd,18183b24,377c9ac0,1e106fe4,d36d9264,3fcfb7b6) +,S(f31418e5,73232992,3a878dfb,f1de30d1,a9669da7,76effc45,bd1a56d0,2c236ed9,e62ab884,dce0d946,55e34c8a,630b5aef,16a1eff6,1f889552,af7234b8,ae56b84c) +,S(9adb945,b876313b,ac9c290d,47c1e6aa,1f3f3c9a,e8c67770,61512c2b,bd703181,b21fc142,b4632616,4820b82b,edddf2b2,51531d0e,df0f0f86,50f74d1e,9f0908fd) +,S(c4682b2b,9d4a63a1,855132e9,b35b390b,ca04b846,b83bf36,1726c3c5,808da322,f25a906a,7100ba32,1b659762,23e36f04,14e9075a,835ce5a2,6eeb5d03,d35f6972) +,S(ebb0fd6d,60f49c47,8167c405,597c765e,559159f3,ae04003d,a99a1066,ea80d63a,eb37d160,667ac862,1da2ffcf,28b431c8,c4202f67,706f4350,afdc868e,6aa0b0f7) +,S(78f12d20,70d8bcb4,267cbb,bff637f1,65ffb098,95f5734e,3551bcbc,9a5597a,42976ab3,36621756,1bb574d2,c87d99cb,7419a3e3,39973740,7de17090,2bbb78f8) +,S(3dffc4a,f6214b63,9839fbc2,b949621a,35ae41bb,e7679eee,5798afbe,85919f69,58174b6b,1b4021ae,22dd15a4,29d97502,a13480fe,902860e9,36fead8,57f2c456) +,S(410c9393,a4ed42ca,2115198f,4db76eb8,bd16bf0d,324528bf,55d5d745,b55a15cc,404ff4b1,df71b89b,c14c2cf9,bca25176,83c2981a,731fc3d7,59faa0a,5ed58306) +,S(6b4d5156,87d6b079,3df36632,6fe6977c,b93e6569,66797196,6c16c495,be4e3649,647a4894,bbac207f,5d2a0315,2db1760c,1a25560a,95511c81,5d6c11a5,e969f4e3) +,S(58ffad7a,e87d5986,f0bccfd8,5005d9c1,6f0d8724,6be4dfd9,4a771c5b,a5f34247,ad8b7ad8,f9ab15fa,f50d6f93,ec8629c3,b33fba6b,ee36ace5,e505cf00,f0069817) +,S(e6d3e41d,1835fade,af27738,23e883b4,adb95669,72892c72,b310eacf,a6a757fd,a136c88c,b580ece7,cef03e42,5dbdb8ef,d6395cab,b427ae61,45a0ef0,21359c15) +,S(fc47c62b,43aaa030,ac104479,21bc1026,3b561b42,a2d32a5,573ae947,d953b35,5d318d1d,65fc7806,732b5810,40a1f511,62723626,b393b29,50230600,246224f0) +,S(ceae2867,cbcb37bb,8e78da9,3553866,d0a1b8a2,933cb1ca,36bacb7c,6fdb251b,48bf2c7e,c163aa95,79417b37,343727b7,956cc629,3a188c16,d4c2d1dd,abb3dfd) +,S(abb12378,c84735b5,c23408d8,2516d887,9195fb74,c8972d78,daa8243e,1befeab0,5a0bf64b,da5a465,d9d0be91,3c8a56d9,ce901c36,67041a02,abb591b3,3f66698e) +,S(d7781e39,8330c7c9,2185302b,e116316,1a576ce9,43a4824f,274ea700,3159eeaf,6341a432,4e07c5c7,3ee7398d,d1be3d7d,261aed99,96f6f310,fbb464af,d06651a) +,S(a6918dcd,46ec2045,72d71646,d2a21df1,6f3ad133,dfd41a6d,be5b7684,7e4c760c,c0e51aba,378d498d,9a70e4b5,3521397c,1d6c7cfb,2f6634e5,1f056e7d,932fa9c) +,S(413a4ee4,fbb5de60,392b340a,65fd9706,7785e2e0,248d2770,6ef044ca,83d1a204,a0362506,c18a8f34,3bebc668,d1b08fd1,6595a674,51db27c3,43af172f,8a3dd197) +,S(e1a7bc24,de8b0920,f6c4aa08,2fbd1126,87fd8766,237311a8,9681581c,5e04001c,3e3ba9fd,d0a2873d,149aeea2,61a55c65,3a3d90b1,efa8e49b,29bab100,1a6bed16) +,S(ff9292a0,a147ccc4,b5ae077f,8a337e05,ab1dde9b,1795942d,8a9a0fc3,dd333a75,e50f027c,5f7a3463,7654b412,25ad5dfe,b555ea2b,8305c7a4,3b6f5b78,7a23a166) +,S(eae56623,437c9dd3,9dc7a36b,45c2d3,afe849c6,1980140d,900039d4,4ccb9c0c,f1b8c9e9,92847436,dcab5a9b,22fe7e78,55e62e40,372d77be,39f68974,dd32e043) +,S(7dd20cbe,3467a3df,e1eda0f3,48c39e5,7f9c6004,bcc89227,3d002cb5,54bd5282,ec4d2f23,f035d2d5,4cf4c3f8,31772a46,ee922582,8ddfd4c6,4eb6a689,7c1485d0) +,S(359811cc,5acf1291,547b8870,33bda3c3,e4e245eb,db0dbfc7,3a02da3f,12a877eb,ed2f5087,8b97a3de,6ff916f,511c3a27,7702d1be,2b683a59,bb6acd98,e908a792) +,S(fb33e89c,f2bcca4e,4ead57ea,1a24d1b,5a6c76ca,bc605a2,c031102e,49a71c24,c75e02f5,ca602705,7ab5b958,42a9272f,d7fca940,c26e980e,192861d,2f1c9f45) +,S(a1cd33e,5eed74a0,1534f836,b05a5129,b7b2970e,f293b606,9d1ebb56,e5f687aa,f90d1ab9,ccfc345,3b2b60cc,1e4380e9,bdefc37a,896a1abc,c9978d08,1ec4a6b0) +,S(461696b9,3643b7a,bfb383cf,ced8ac92,5d9f169e,1db30999,b8553504,c4213614,674f5c77,5f300fa0,c8f40a85,9aebffd3,ee91c4ce,695998d6,de0d59a,3033bc72) +,S(6c6aaa22,1b589f6b,ccc885ae,93f7e8aa,1fd5cdf2,27471727,f390d5bd,e14ab025,a77c5e03,40e1b90d,1ad64483,fb50fa76,aeb84417,88ca415c,9f8d2cbb,aa077538) +,S(c918d391,927f3310,d032c7ee,6e729e6d,792eff1a,72acc268,3874fcd7,4e61c38b,58fd746c,346048f8,eda1bed0,e5b6cb58,fce24a72,6e27f965,74243c80,780dfb5b) +,S(48679e16,1ca0a3f5,e4ac2654,213c494d,9240c3f0,33fbe2c2,142ec589,6dea5918,556f93fe,300cafe3,e93a2966,767840e3,b2a19fe7,9cf204a9,a30d1f1e,3e1d1a41) +,S(c68c05a9,e46b6440,a72d6eed,eaac085a,5e945659,ac46a584,69968a30,c6e7b858,b8b4b5dc,35d4ff80,be3d64a1,d036a117,c768746a,4d2bf2b6,1b126460,15de450d) +,S(d133ef10,6358489f,8f976dc4,b6c54699,21e3dfe3,6f326dc9,f0538757,9d444e6a,2c66abc4,db690d2c,272b0f57,afd66a6e,327ec3eb,e32b45be,cbf3c502,baf968cb) +,S(53544f59,8d759dea,b10766dc,48a7e2da,61ca0a60,a201d193,92dad05b,3df976ae,449a205e,61cb7aef,204e3f0,3a199ad7,993d711,537e17e8,79a42bfe,2869cd47) +,S(3108a074,fda5133,39e26385,14b6b7c0,9254c65d,ee374a62,2fe34acb,83b8b331,42edc90d,54263fea,8b1a473d,ac2f2244,d6fc05d1,f59e115b,5d930132,268b6243) +,S(1666c40d,52c1c04,d9e95099,aeb55861,2ee58ac0,6b22f962,154cd8ca,7cb4b23c,5065bb41,ae046638,aab55923,e0752954,abafc247,d201e84f,b6c444b1,93a82fb9) +,S(c96d07eb,d3eee6a8,f001290d,f76b2d82,ed80fa90,bbd5ead5,3a572457,ee47722e,d3d69c04,447fb7e9,a3416af1,798b749d,13ba06e4,78a432c8,d00dc2cc,7e20baf3) +,S(a9ffc39,b11b5b05,78769a9e,8ce7542c,647fa7bd,7b2c3479,fce84d91,1d81b49d,bec21248,82e928f7,e98894dc,25840712,80a64449,38e53747,375d2122,68bcbee7) +,S(52ca128c,cd4c9380,8e55d3f5,29320fd3,e825787f,1a600f27,37fc5fbb,f7af0632,846e8a80,95a98cb7,b4ebab01,4d9dbfb9,37ffa493,83676ec3,70f42cde,3135f7c4) +,S(ff92aac1,77fd9927,7b8abc41,e49f7c94,698525,2329b9ae,8e0fa34e,a9407c20,af0d8cdd,5aaf5273,ebbbf2a9,b7230373,dafd9a93,42a47331,a3379f45,8ffdcfb5) +,S(a692a73e,d3c10a3c,38163c97,314d02fa,5fb653b1,d729ff09,a541d01b,27991e0c,722f55a,3e5bd3e8,d863d00,6b82415a,aa05c949,9a537809,cff9be69,e468b905) +,S(d9e59c3f,7a629a93,e32fc4cd,2c98d480,6b813ba6,315ce4e2,e2fc7fed,4120aefd,24ce2594,23073119,4f5bf658,909a47fd,3234be39,13aa8d30,eb08e14f,af0214bb) +,S(74e5270f,7771b109,64991ef7,9abcf05b,4dde8d5a,605ab587,14b8e534,4a0f6ff,2ad861a3,a4a83f09,687a285f,1356b1e3,71ced066,69154887,14bb0461,b3b1670f) +,S(b9169230,1c43f7e7,61a85615,7957c3a2,744668a4,8587ff58,7a201f59,31d4420c,bd36065b,c1268280,4cd818e0,c6515a5b,659ea423,f5b23e5b,ebe1f74e,5819536f) +,S(37faf253,864568da,972d7211,505b7fb2,cd01fdc9,5953bcd8,79239d90,364d51ee,b69ae2ce,749db3e7,e6aea736,a3bc961,d8c12523,6f7be823,65749ead,3bc15f85) +,S(e194114c,4a1c138b,34882477,f0dcd065,1c379f98,c198614b,43b087bb,a20cf8d2,7a3afb4b,b90ae22e,3b182cf0,6514a998,889f2ad3,d71d4d67,89e3758a,e898a93b) +,S(2ce2db01,d3fb451,7388e4b3,c81588d1,1c8b13ef,917333c,ca054379,fa17ddaa,9232da9d,af0e76a9,85e1529a,ed1a5568,74285cb3,8010c922,e2c30d77,367bbbc0) +,S(c292ec48,4bdb7045,3dce2e82,1fea7688,bd66c6e,59635f49,98618f9f,3d6f051f,210a74f0,6b81e69c,4d0af49b,fd153247,12b77736,126f4887,2b119ec8,97b57d6d) +,S(15bcc9c0,97618aef,697b423c,cd83ad3e,933db83,d77b42ad,3e132036,a5acfeee,e9fc7ef1,1bb996fd,bfb3c490,14912ca1,a1bb98a9,e9611c,1a83a806,b88a7a33) +,S(46119caf,50f13bd7,97be45fe,55f2d298,848639c6,99d5b113,fd24cb24,6e1e7785,6a203f91,f4c56eb9,7762cf8f,9ef932b4,ba301501,5aeb1215,b1327a93,842ea3b) +,S(f3a17794,3e4df182,a9549a3d,2da1f9fd,cf141094,5e6b3a64,9d9df885,b6f9ecf4,b34eb7cc,5e82ad39,fc2b6619,859f8328,444a1dff,c82ee115,c80cec10,6ecf7e8f) +,S(cef90a74,84a7f468,db9cbd45,f5b9a0c5,fa98bdfa,50297d67,536075c6,b7913fe4,d090eead,2a43fbc2,86702946,52507875,fb79a555,e5c55737,30fa40a2,6a301815) +,S(21d8a214,987aa938,ec4b88b2,4bfa5661,26c528b6,b9060b06,833c6ed2,a35702ce,c354340d,5b65a23b,c628167c,8a2830,f736dfe9,a501fc8e,94b66f26,d84579e4) +,S(6b18ff27,9049be4a,62a04f6c,f82dbd24,e19196fa,46e0761d,5d139518,3a1ca53e,ef806f0c,52c01367,8f81e9b2,6698fa32,6343f0d7,2522f8d8,6cacfdb2,c5ffa3b7) +,S(7d8ef4f2,452cc04,d5332afb,ea3e673c,bab07c36,c081a0ee,850ae31d,cb466e6f,32461667,55a2ceef,273367d0,df10f6c9,d224da47,f19117bc,137a6244,2cfa0c0d) +,S(c7de1821,55d15649,f5bb6327,65ac74a2,3413560,b1d31afd,6994b488,20d12cab,d9965856,c5fb2c0f,c93b8659,5c3240d8,7fbe0136,11d7e844,48252ce7,96eaa23a) +,S(4cecb33a,915e2c3b,189eb6ee,45fa4eb3,eddeff09,729945c6,f893bd,4381b294,cf17f112,b5e61cb8,44625035,7b700cd7,3534d6e1,3310b6ff,d1bbbd8e,1ea3aeb1) +,S(826cf464,bfce606b,b4c3ab5d,4cf00fe4,1c9bc72b,285c8c10,204aa658,66255749,6ba3b431,527d285b,5156fabf,b150aa2f,6d7ca564,b18011cb,b878e235,7b6849c4) +,S(c812dac6,f497d245,5192ba92,2d6fc10d,de51d608,d82e828e,b2187e7,648f8f62,d02e8c7,73630680,8126ee1a,efaf53fc,2be94dd8,7773e8f6,1ba6b4ab,b08b317e) +,S(5269550e,bd967f47,e45eaa8b,c9f14ebb,aa45926b,6e38cd1e,3c6d5d3f,e0ff8851,a1597d20,26e3eced,443422ff,cd1b3fd1,b45963d0,3086a95a,38b8661f,99a7455b) +,S(7fcfcb29,80e652cf,bc42721f,cdc40ba8,c7c2ae6d,54a51e9b,af1afee7,e84d7676,abf72d90,4a408e34,ec6bcbaa,ac8f19d6,a4d044f5,66a9cce9,f95debf8,882cfcdd) +,S(85f01aa0,4597e104,848e18a2,132a6a07,3d5f25eb,8baa857c,79dc4b6,64a9f451,f7463ceb,8a058187,7bbb71ab,35bcf456,5398cec1,cb4e0132,4b4ec47,e5f081af) +,S(95b4a1a,7dcd860f,ac3fee1d,10febeec,dc1a5fbc,224035fd,a01f8dff,92885628,2a0cdf9c,5bd94467,efa9dfa6,94c6ee3e,1f80bced,23738d97,f70406ec,bef7f772) +,S(4f624a6,24dddd0b,b254b38c,e37753d8,8cd95cac,ec4709f0,49b90ecd,7f6c51c5,874a9a83,2efecd9,2a20dcd,8ccfd454,668de4eb,1b976bd0,70ce584b,c77a4fa5) +,S(39e06912,e18b537b,ce440f58,545e9c6e,a2914d85,698d4043,437dd66d,7506b48d,eff20e0a,16e93096,abc80153,c128aa12,f06c6d,4695bef1,8db7a167,6f352e0) +,S(52edc132,66d67831,74ff4f59,cadb979,4c1d41b6,5efb9310,7a717b46,78f1ace9,e569e202,c6781e3f,e32c0bf4,9684d7d3,cb07cf55,8f46927c,3af6bbc1,c4d402d1) +,S(e3764b48,964b8107,3e287f07,c915fc8d,e4e9458d,6f2a8d3a,1dfa43f3,4328465f,bde2a64b,fb13e0ba,351b460f,6d0d7574,5b7261aa,53a79b6c,3ad5c8f3,4cfdb023) +,S(80efe507,7c77698e,3ec38768,ef81732b,e1c81aad,9c42cebe,75627eff,bcf513e6,690c671b,c54391e9,695e6059,953d3d9e,a9df2e17,5aed4bc3,483f104a,388b709) +,S(484ac7d5,ebcc497,a2f2bcac,87cd23ef,19b0bf7e,93f8e728,5173f70f,c908b641,1d9570e6,f3b17236,9f2b8551,653f3271,5bc8c995,14c2aeb6,1b44ea5e,90ef371e) +,S(9c989e3f,bf1fbdd8,4dfca53e,4415cdfa,adf26c26,3801ff65,36b8fb5f,77e3d124,a34ae61f,5755163b,964b3b80,28fee3ac,2993c442,d7f16dd4,c79aab0c,88212a38) +,S(8b8fb0f,601afe7f,6456570f,19e37f80,32846dd2,97b0d8e0,9308c57d,75a5066b,7858afe,ffc4e6c9,b2e32f10,fd67a1,d6166c72,26f03b3c,240b29e0,e848c9e7) +,S(e37d0e74,ee1d79a,981baa79,e0a5d807,bce0953a,211c00ef,451d9d60,7ee78177,d28a1285,2ad25f4,df1b5625,bfe59875,a07d0593,aab4ee6f,4bd23ebf,b09f8f9c) +,S(80c8577e,9a003683,ad658e75,47657b1f,281052d5,54cd0e19,4b2a9e70,1325c147,b3fd0cd,7ca3a2ea,ec7ba8a0,d7297347,fe2baf99,bcfbbfe4,32a6346f,564d945c) +,S(e7045d6d,34121e5,536f7902,67ae01a9,50f6ba8d,2d05349a,ddeb3833,3ea8c6a1,7306a8db,326994df,e15571d9,c2a5dd24,60b37c2f,e97559e4,e7d88ab8,8a181fba) +,S(55d2da4b,850a47fb,6b3af0b1,3c03889a,d87554f7,cb346980,4f2978c8,652e0d71,6ac0f982,c861d0f6,cd20b7b4,f22eee5d,189a56d,5cc46780,72361e79,dfe16e7c) +,S(ce562e09,55a22f7f,c83a5ff2,11500c03,a1e86d1c,404019f5,5bd1cdb0,b1a38d2f,91de004d,b4aac47b,c6b0a165,edc5f82e,47407032,6f1ced31,4bd6d96b,521bb865) +,S(74e00e1f,596af05,7f91bd64,53107f1d,cd0eb8,6b93b6fe,cc7592e3,7854773b,11ca88a3,38e52a53,eb311867,b10a146d,6fa9f015,11791374,e13fb749,d8e1d217) +,S(ae8f0b6b,c194f6c9,1d83e400,ac92ed97,68214d39,95868530,a2e99f9e,2104eb48,e973fc42,dc0a930,36127de9,eb57cbf8,d4aa8f73,14646362,c855ceec,d7ce4cf2) +,S(40a8c06c,8df83675,e108e0c0,565f33d3,7854e722,6f76b674,1582fdcd,ef891ae7,16ba7850,b5116afd,3d29e0b0,40faabed,b8ff9c09,83af624e,a100555d,887bf7ed) +,S(61ea9e54,5c57f6fa,a2ab410f,861f1fff,926795b0,4d240e48,7f6df1bc,6df83b33,c69ec12a,e0b217c3,72e1791f,7d4529f2,b9063e67,38a8c03c,49071581,da79ed15) +,S(1c889cd4,440caaa4,fcdf55f7,2145fc17,d73e25ec,264831cc,30a0cb5e,c1d37632,41b5246,131fa67c,7314a223,7db5e4b0,574f5b86,11560166,ee55bc94,22d72fdf) +,S(ff295890,791d87e7,982a99d3,ab50bfa5,c58e0d06,25392abb,4ab166a1,8e49525d,a09664de,19951364,19649baf,88806fbe,c370879f,efbe4a58,e619a01a,19454570) +,S(5eb2eabf,cfca1fd0,7c939e5,b8f890cd,74e399ae,d9bdf785,7bca8767,84e95f99,4c42e970,1c9a783c,201aff12,16265d8,1f46f25,fe110f63,31fb8fb,f72c9871) +,S(43e5508c,dbccc93b,5b3f8ce1,7a071c1b,3b17bf4d,8153f61f,cfc7b6b4,f6cbaf91,b5ed8f78,438ba71e,509ec388,d09a5307,c853bb73,db240638,15a07343,9bdfdcfc) +,S(f5d16930,23cb20df,c8607261,cafe533c,eb513f87,7f6e2c99,5b0c0f75,fdb56f53,e539b168,1528332,f7fc4bf8,73e2965c,2557dd40,6eb2c8f4,92ce0dc2,126ece68) +,S(db9d66d1,5dc5a918,46180a62,d662a2eb,72313c6f,ba511eb6,c7fe0c0f,78aba2a1,c96de8a3,61f0dc5a,9e2762d9,2257857c,3cfb46b5,81c3f0b1,f89a20f2,a1517227) +,S(e760d3d8,46fd63bc,4f565bfe,7bf5133b,56ffce08,27b99ab6,833a64b8,868ecdf2,60ee6685,8be2eb4,1821cc82,54062e01,2c1c1e12,285c8381,e60e5d4,12f102ce) +,S(2bdb2299,caca7ab,d22533e7,12e37004,e3a956b0,31da4d99,1351d790,5c33e026,64675cdf,c2738105,dac3e877,e27d00e7,80193286,24cbbf82,a784713f,b6e44731) +,S(1cce0bfd,5992134,b7813773,9fd6c844,7be48d20,e001bbbf,c3c87b9a,8166bee4,2d4a8ee5,1ed7fe30,c3a1253c,4cd9e83e,b5eeb73e,c166f4cc,42788bbd,cad37057) +,S(6d6be3c3,7f5dae7b,ff5b5bd8,ec6b8c65,ec8405c5,bb6575bd,6ebf89a5,77641075,4fd3cc1f,5c6fd3bc,6011507f,7c4eaf4b,24d1938d,35159b80,39246ccf,ac9c2c7f) +,S(d82c180,d96e781d,783fb1ca,8758fa1d,b3c67f2f,143e836c,c6007d8b,bdb22351,3802f669,952aada8,93b3e9a9,74a43218,37415af6,7964b1e1,51a7b8e2,7ad0c7d9) +,S(4ec61edd,7e03fd34,f91dfed1,b546f1b8,fbf9fcee,8c73e343,b228ce18,3db3cd71,e1199709,b5b2e2ec,8af90340,de9ec3c6,db7d948f,aa941cff,b1984856,b03ba9a9) +,S(31aad469,73aea11b,a54744d7,bead13ee,a8df7a8f,8e735643,add781e9,e17a034b,64676f41,3e49f606,3f68bdf5,7ced7486,a94680f8,e60e2913,f13b674f,84b37adb) +,S(a9842189,2fccdb8e,f6515903,b589ea62,1f946bd,97e2931a,5eb1ce4d,4d173e02,50be76ac,3b2dd7a3,a3370e13,3cd6fb37,7ea266d,a566049,abe488a4,89a3908b) +,S(822b54f3,3ec00433,7a6cfa09,277540ce,fee79b08,a50695e3,f8fe0ece,b7bef099,393623c6,20d3f604,7343ddce,d8805bf0,7405d336,fda8c712,8d044851,236bf7f6) +,S(100f6b55,fe3d7000,c5f8de14,ebfc5d7e,15793d10,e99dea3d,33adf25c,15065e0b,d1bb44f7,58c4dde2,7c928267,6d6c1114,a352ffd0,dcec8e51,9b2cd74a,4eecb4c2) +,S(ca5d8318,6a09c227,481aa6ec,d195d094,6a54257c,5adb71bb,2d4e8836,d6142f7f,d890847f,b9747a0a,f3ba0c38,84ae057f,878d8d31,fc73933d,e014f4d9,2677b42d) +,S(e91c25f4,2234d20e,2931a352,f98ff610,e8ffe53f,101b56ca,fa5da12e,24bfbf14,b1ec15e5,441ec52b,c71740f6,d60e99a7,7a26bcf5,143ceb41,872b4a22,964dbb86) +,S(57a8c8dc,310902d8,8569b290,22e99ae7,acb54547,2ccf7065,9e9d42c,1ae34445,85224541,442e09d,f4e92db5,64866aee,50e7cc3f,b8c20acc,b9c6161e,30ab1fb7) +,S(232b8c67,2502d16d,badd0d41,28c02b6f,55a8531d,a6fe87d9,581045b0,7104500f,38110a0e,9e7d8587,227c476f,6549492f,4f5255d9,e618cf39,9a7a40f5,85f93fb9) +,S(7d0be68a,d71f69b3,4f5d4876,319f7e7a,750d5c7,e3dc876,187627fe,77004fec,3e72a050,7d0af3e6,1d6f5402,8a5b971e,a12b1d18,2ca45e67,ca09efcd,45124633) +,S(4dd85f0b,77c2a271,c95fa436,90c43485,b70b02c4,9a46cd83,6ba7a2d2,7af93292,843642e5,c9554d98,48ab6ff8,5dada7a0,f139e64d,2081978e,8c9e3bf5,1c11d597) +,S(537a93f5,88f02004,70b531a1,74ba9a7a,3eb2e5c1,eb767214,9e264e2f,712e52f8,b0d9b3a0,dc009266,8e25f06,71895af3,94efa1f9,78257ebb,3f839b61,340e161f) +,S(13faf471,325b1e57,98ce60a4,6f82bfd1,ab346590,82588ae8,b972b331,9ee48684,a6720384,e84eb529,e22f2585,79b2201a,dce7ba18,845b8bfa,49a21ade,7634f743) +,S(691e24ed,5861b344,e015dbea,809dede7,ca17c482,22d9e06b,e1be4405,cfe8acb8,e3564adc,c197b0f,cc520d90,9d9c6347,5ad00294,3d0f3f96,6f49b76b,91c529ab) +,S(36543b91,aaaea850,15aa394f,fb334923,b754b3f8,9047f73f,423c15dc,7d3d7ea2,73b4693,b79b800e,8e48c838,9218094b,9dff44fe,5a1fafd5,2d68a2f1,bbbe5799) +,S(ff25abe0,394180cb,1e045045,5320ea80,e007bb0,fe3bc8a2,24f20b07,8433f05b,a512b109,26dfe650,e6dbd678,ad9db412,938f69e0,8aee6c2,f903631e,530b7e51) +,S(1dec07b,5faafdaf,51051f57,cd0247ac,cc607bc1,70c53222,72d389a1,7d87e443,7e77e937,382b2bc0,5a2207a9,2598dad4,d870ce67,d0e138d3,bb35094b,9fa9a325) +,S(2f10f776,f62f8632,ea20147e,d14e0919,485ab78d,fc636236,29c2994,9f413c32,3b2c5fc6,e760462e,b9d8c8b,5aa6b1f6,35c23668,33e7ef5a,d25269ec,68d2184a) +,S(7974f394,e61c2cdb,acf09681,4bcafb4a,ade31943,cc180188,31160022,dabd73ca,e1b34da0,3bc1aa66,87df854e,813ba451,2be4cde8,310e4056,73c83600,89e98309) +,S(54c5f39,3c5cc416,56577469,84aa3695,32af1ede,3a51b14,436748cf,1701c332,3dc67b49,49769cc5,22b0626a,eeab6ba,ddbde8ac,ef78a0cd,800c1b6a,5043e4e) +,S(199aa296,f047c7eb,1d8a972a,318585ae,8092e905,e555915d,81c1a3b,425f3783,f5823679,64d17a96,b4ca7946,dfd3a266,585154e1,cee02e2,b87e8fc7,eb46c262) +,S(d15d39d6,fb616dfa,6c292a3b,a1fc58e7,cbcdf0be,7976be87,9a7f088a,9bb7f299,4c8ae97c,4e37f837,bdbd4912,f43bfc79,679dd418,b57d061,f705ba60,2bd7a900) +,S(76d65134,880e7dfc,81a332c6,a04b82bd,bff26eca,9a5aedf5,a6f6683f,364ef58b,b71ae708,bbd22d83,a1ec9048,6975b9a2,7b4aa5bc,5bf1246a,6464406b,401507e7) +,S(1eb4d2a5,d4dffe36,30ae9ead,792ffa9b,452ef0e1,94c06a02,a0308143,dcae51b7,88d978c1,e6ab8e79,9a286af4,58cb3eda,7cc94b67,589b144c,9a55cdbd,6ff47b0b) +,S(dee03251,35708790,361c3451,6710623d,e474bdd2,4513562f,95512f0c,dddf571d,d22dbcfd,cbf6d809,2c68a28d,c118ffec,496d9623,b20667c3,7fc487a,1690f565) +,S(2eb0272d,c98ac6bd,ff68f0d5,5f13f509,bf1dbd9d,f36385ae,ef02356e,56d4760a,f80d4355,4aaf4eba,27aa1832,90573687,5c69ad44,7211f746,111ca02b,26a04277) +,S(eaf62e69,7509672d,8ff6ff7f,c7788822,b75853ea,27636b5,c39b0321,ab0eabf3,442e56aa,1c8fe22d,71b88b96,90733f1c,8614e160,35070095,6effdedb,2e0a3b7c) +,S(2059070a,13d96fd9,6bdc442c,236ef463,cbc70185,b8799d3b,15ef72ae,4592858,bc9042e8,3f259ae8,f6221d7f,46ae56d8,f426fbfe,901b94a2,80da34bd,14961ec8) +,S(ff2ef7fd,9e7f3e7e,6f7b4cee,a44ec281,4b8b4a44,208c462a,f925d27,ad2f52fe,6ace74fb,fa6f72b4,8156cb52,a67f5137,956a447,6b69fe3b,233ce4f3,df0a5e24) +,S(e3e5cef6,fc4553fb,d337a7b0,4357ff1d,d8fd0de6,4df17bf6,20f7f9d8,9d14d83,acddfda1,baa7b60f,d0d1863f,769a475f,e667b6fe,8a047365,1b84f44d,238f0460) +,S(97a8726a,41c02880,e9942341,a66b9c21,a0c64577,bde3c54e,c11119cd,bf57a6c3,b5f37cae,27023d07,78cc5c7,26d845e1,fe160aff,3459d164,681cc38d,8b67b4e8) +,S(ab01e6e9,4862f943,8639484a,119eadb4,12fd9002,1d7781c4,1b020450,9dcc81e6,6ae50c32,101b5c3a,51fab05,2ed79e5,48853cf4,15a30043,6b11ee05,7483b14b) +,S(c464410,da2dcf6b,2e6ba5a,c8d8479e,58ed9a4c,a8c2b0d,5a2ea15f,7673668f,3698c590,62392c7d,e597f333,cd3ce45b,eadee944,5209d361,adbbc386,b499945) +,S(17eacc68,5f721f3e,2013b0e6,76c10795,ee49d524,26edbd86,48f098f4,17867534,cc3116e9,cb19f49,bbf7214b,b3ab76fb,fec0f856,e994a01b,d6b63ece,189ef300) +,S(c040dd40,2908ef17,539b77bd,5befd53d,3ed2e8bc,d7d3608f,2b114dea,d4c00d73,7a6384ae,55ec2580,4d1403d8,45113dce,b2053ecb,6ce0d3d7,427835b4,15279c00) +,S(a5963498,3658f70c,2ec25fed,6659a290,41bf2ba2,ba92db93,41b03e39,2ebddb42,dc114caf,260ec4a7,a8e8bf4,1695785d,1ce7c2d2,82359600,a950ade4,d79f4872) +,S(44fe22d4,51dcf63f,c7e33d09,533836c7,66382372,339de279,9897cea1,1b29aa0d,1718c4e7,3d4bd434,929e9ffa,e2496f79,4f3bf80f,ae805a9e,53b577cc,4c4279b3) +,S(8bd718aa,1358c15e,fa3b246d,e244bfd8,4fc6b5f3,ed5ec91c,ff466a11,2c71647b,172f1a88,ba6c11f0,1b7deb8d,e4002793,65a0a3c8,443b2fa5,f406dfe8,4bd737fa) +,S(8ebdcc25,bbe6aa23,809aa53c,3ff3a7a5,d7ad4cd5,5dcf3cfc,d9ff8ce1,738680d1,5c563b26,af86adfa,c3728c,f886d83c,5be64766,78ac5872,cd798875,45e5641a) +,S(5cf54e1f,688faabc,99742fb8,be896cfa,b469e5d2,feb72f4e,9dbc67e0,e91713d2,d82d6590,d06330d8,2f86393e,190155b3,4920e8a8,86324b18,5694786b,e4fd09c5) +,S(6c0d96e4,3f01a1a7,2ec16ff4,e04571ed,387185c1,4c8a2f8c,db4c187a,85845f1a,f399a14b,763ec2ec,f3415c1b,b9bb6f70,abcf013a,3117ddd6,e55a96a6,b0798c08) +,S(eae928fe,fb6a2fdc,d05f1515,5726a133,9a15ef0b,ea716ad6,90d32dc3,92b74046,bdf87ad9,f086339d,cbfda797,8d6ed40e,f6fd4626,60d6db09,84ecc577,14c76bc) +,S(e5e5e30c,bbe72fde,d9f0dbbd,e08dda,b61d5cb3,1aa688f0,d572008e,447b3a21,9fb4adb8,f80b6e67,47c6ec03,d5171ce8,5de65682,a105435c,5610065a,85b248ae) +,S(711212ac,c9e26bb7,c5ace176,26b300bc,78df6b0c,a79a0b79,7dbcca,138223ee,dc68858,4ef44ddd,9e6b343b,c4c73fe3,6b873239,18e11cbb,dc3aa72a,ef027757) +,S(3b52ebb3,531aa4f6,ddbb9ea3,fb5e5784,c4cf11b2,a4c67d50,6b6340f0,21c5e22c,3da3eebd,dc0046a1,3facf02e,46818640,b698c8a6,3c5fb9de,55a9996b,b190938a) +,S(30f28481,110345e2,7dee7292,fe1c7a56,45294a17,e96cadf9,1eb76bdc,cf56b78b,70c610fc,26ece34b,f047864d,25116fac,3c149164,a1deb08,8e6d702b,77abb049) +,S(4a844a8,12c55b6,4e4d37b6,1ca4a1f1,5cfef527,699d5ab5,edf1564d,e930ff58,49874548,508de6e4,dbbe6bd3,8d0a9ff6,5826f9c0,63274cd3,f3189d59,683dfa3c) +,S(a5ca561e,a7c8484a,642e0fd9,a999e14c,5ce2b5d,edc51026,4519e6c6,7868b06f,cc917942,a5e2eeea,ac09b00e,affa7f6b,7abe386a,241de5a4,7a7c43a3,85afa7ed) +,S(f61b7ed3,1e16c456,c3851a68,e10f614a,1fa547c6,456e57ef,3c1f8ee4,dfd6e72c,cb26d0b7,fc401865,91f5fbdb,4d17bfa6,a0c5a811,4d099a54,734a3280,5f622f2b) +,S(f8e4d462,889700c4,b52859f9,e766fbe6,55976866,a22207fc,aee56185,15f678b2,30bb039f,c1b16fb0,aa09c271,bfbb76b1,6791e95,9dd51ae,6f01aa4e,bac77554) +,S(89cafc1a,5d25915,b2f6f71,71814a2f,d5d97a06,a717a91c,a392909d,332ab653,1a24a0ab,b4e4858,d29c0055,714f34e1,5eac02da,60e87ce1,6fe2f144,257ab76e) +,S(d285370b,d04f5dcf,caa01dca,7a2ad510,33c5b555,6aea60bb,4a48d97d,477c4f20,d4b427b7,32e885d0,13a2adf8,ec705145,c61386b2,c6981b41,6c44d202,4693b9d4) +,S(3c532f2,d9bdbfb3,219dc90d,17aba269,fd335066,e1fd4974,9026f66a,4df217c0,86fd5e7c,a6c97fd0,2c33c7b4,28413977,2791a6f9,f8c80576,75b3e306,66ac5d59) +,S(24d1950b,fc35706a,21b17a16,53037226,2984453e,37e26924,b730314c,cc8ac1ee,4d22a45d,6a31a90,bb247c68,59ae9a75,b05bb9e0,8b57ded8,4b9c1ea9,c9f918ac) +,S(cc6ab0f3,6227b1ab,149f8ace,ea036fc2,d8505341,ab67d44,59582b36,4e7615a9,f9ad93cc,40b1258,67c806b1,20fa6f21,bac918af,54a0f7d7,107680a3,83761cb8) +,S(de24a954,f36e94b6,ca816bcf,f730b99d,6320f20d,9b141cde,f08ff71b,25318cf,3e34b1d9,1ac24a49,b982d235,44afd62f,8475c35e,a48394d9,67904a41,db899933) +,S(77ab04c3,4900aba9,d83a12c,7953cfbd,5f8243db,48fab618,f83ff252,5a1ffec7,6265ac27,3bcd77ff,496265af,75a8db31,231c871a,30a32bd,ae873efe,bcac2f00) +,S(a47af706,5f4699e5,e0079885,d19580a2,51ff8dee,1dab741e,ce84ad51,16e78209,393cbbfd,f1b1f78b,a563f7cb,3465ab97,5b786088,3daffabf,bb8f34b9,7a257440) +,S(a9f5f9fd,c55c7b7,4dd170fb,d0322026,93d709e1,d52a37ea,8a545e9e,9cd7016d,98717925,4badf22e,8fd847b2,e9e80ab2,54f0d327,f6b6162b,8734b105,f1d5e0e1) +,S(d57e9ebc,96b938a2,8fa200c4,e586e7e1,ba866864,a947b71,6cc7cd63,4e18ec38,9aa0d2ef,68239110,5b4c8b7c,b412df87,25de16ea,f28596a3,62a6aac,d1b6bd36) +,S(7463a8d8,fb4dc14d,5618e66c,aa85d73a,e27ccc1c,ab820611,9f506829,faa58e9,82fd6ab2,275af504,aa0645a2,8438db86,ae7990c4,b4c389f,569aad0,68d4829f) +,S(2e7a708a,29c44831,9a02d03e,4eb331b1,3927ae64,534db2fc,a3c6e7cf,d53bf412,84f3f199,73500cdc,aeae0073,f39b3b2b,ebdf6824,ec018160,fd2e3b3d,4b1e11d1) +,S(8707b0bf,540bc058,faf2f3c2,e8ba304e,180d094b,b7fa8dda,414d30c4,d9dd5e03,70b61175,fb791033,50c6bd25,aa0594d7,18db5ee9,9667c25d,fbb49c20,f100b915) +,S(3fdb370c,3ce6cd02,886070a7,28e66749,22f67dce,1b6c08b9,613aa2d6,53ca842d,163518f2,474a0344,a5c4c5ec,10b1efef,5d3a5ef9,94010063,17a486b0,c8e0dafc) +,S(c37da302,412cd549,a01411e7,6f58d4b,33077960,94b31af,2a283d89,48472f62,aa08f287,16c642f4,8702d1da,aa0a82bd,ca849000,b8ad1d26,801c6fe1,3a21f92a) +,S(3eba0550,381ab0f9,251f923b,5c7a339,60ade9ec,2b0b47cf,25726fc7,a5e08a1c,1427b0eb,b681dd3a,88639652,5afc4e00,492a1ad9,a0c5a396,55741a5f,cf0fa7d9) +,S(96723ea1,8ce5d5d4,bf64628f,7754fa0e,e43f20a7,85356e01,94eaf8e6,bb50cee8,5ad09fbb,b9bbbf10,e86eef51,2d448aed,6cf51b5b,b28c8e8a,713066a5,81f49bdd) +,S(d7645211,7ecfb6cd,3a09d346,55d5c58,ab822892,67910f13,29f8d604,a3663748,ab7cc104,d20fde08,2c889dbe,2baaa9cf,bf2c9b10,c225da46,eebb8031,c28540f7) +,S(1f6b23dc,7e0ab136,20d907af,ad4bc944,8c866065,b4ac7e43,4dca8906,d0ada5e6,8c37b25d,6bc639da,ad092040,5daa9eb1,56041440,e48f6602,398ec256,763e247c) +,S(46ff04bb,aa06a168,79479e64,bc0e8ed7,6abb5a12,8afd1f9d,9bd80d50,75459945,b1104ec2,6af2ba0e,39133bb9,e47a61ab,e7e9d229,41d71764,91ef9da4,f51bcd4a) +,S(ca1b3c25,38888069,3b076e54,58bb0451,3bcdffb3,d824af86,f3f4a883,2a597ce2,f02916fb,dd1b6dc4,76109d11,bd9e81ed,dfc9d6f5,79847622,51bfe2,a1e0285a) +,S(c961e9a,adb69ec4,8efec7fe,dcdbdfb0,5fad1277,2b801763,ee747a10,99ece6b4,469a6c33,e0b253e5,270bb70a,d16c4b70,56bb9b3c,35f0a2a9,84e2f269,9d8fb6e4) +,S(3a2ea279,1418cc94,d3f81708,9d44e777,7b3132f5,b9b67393,ea4f28bf,939d99dc,15805143,c035d921,be217dc7,18d13e2e,b981bf8e,6525f88,31a96c65,6bf164be) +,S(416dcdf,b3a1f0be,1a3fd6ad,52fb1c03,4e24d48c,cf34d189,e7dedea6,b65a7a4a,76909ec2,bbc93359,ac177781,2132db31,47e90a0a,3c6ca35b,ebadd113,60c204c3) +,S(b397c245,9aefc175,8ba5177,2d4b3ed3,2ed9d1c4,85d2a2b1,7318545b,93c63eb3,a106a360,30763676,ab418ffc,692043ee,22813574,e3117a7e,4471ccb6,f5e71748) +,S(75c5589b,efd61646,329dbb75,5e809b2a,c1bdb8c0,d29a3065,32ef3909,d40637df,26f3e1e5,1492105c,90ea19dd,81fce6bb,95e60295,a1fe347d,cba27adf,a3e6f631) +,S(2eeecd12,a3517432,17d5158c,439a9046,e4b0dc80,119e58b6,620e6f08,969135a5,e1deebd0,48c58ed3,1b53d753,9c4c5791,13606467,14147707,806f491d,2daaba4c) +,S(a981f091,5d39d650,66b1d52e,5addda67,8cab860a,c75ac124,aa100593,9cbe65d1,4cb09fcb,7ab60293,2d98b23c,96e5109,88d4f149,4f334e7b,66c60040,7d3ed069) +,S(1a19fae3,a7e81d02,a1260c9,a759e79e,545cd869,2666b31a,9aa4c0a0,9bd4ef62,2807d657,8af25c69,e29bf7bf,d877dc7b,79714df0,68ae0707,89c5b72a,b0937902) +,S(cff8696c,1ee7d196,d76f139,2662e776,9b0f4314,99d30567,1952813d,2420c61a,8497e013,8a399690,25c04b1a,136bb816,3581f122,b62b70c1,47fc52c5,e6d16df8) +,S(6817a420,4a8b3479,caaa9582,4d48eb04,9669115a,9e5a5cd6,6cc905e2,1e27ce8c,9585468b,c3377a5c,98725465,35c8385e,8182a0c2,763226fd,95e72937,7b3052ad) +,S(9ceffc84,6e0ecc26,af13eb99,579478f3,7d114b19,59253233,bcd33d6c,8dd9d58e,e01aca1a,5cf8fa1b,67c4d4a,fef1c7ec,51060234,3ea64426,856d0aa8,fa5d2582) +,S(6a6e1dc6,f203f7fd,d9796589,2301e5fb,995a3731,8c410543,835f0edc,d3456c49,2a072b98,98b93e9e,b05f9ad8,6a97546d,83b579bf,6efd3482,f93baca1,3784496b) +,S(7e7bae1a,588d761b,5158b4f0,fb9186e8,8ba3a521,89ac36d2,4dd31d7c,d2a9129b,5198f63b,37995cc7,289e60e5,7d0f8738,17e6bbbc,d40d29d,cc4856fa,ab3dbdd6) +,S(13fc3a8,63deab2b,ed54966f,fa85e553,ffa15863,ee12f9f8,5dc5b35,6bd253,e31c3245,91275056,ffca59e7,6e76a957,a8b77c82,702e5d58,b3b5b577,73821459) +,S(ffe28deb,3f39d917,f6b6dbed,ae89ea4b,e5650326,d148ccb,becbc6d1,16e9c167,8d8a52b3,b878444d,e5385760,4e02b3fd,383c4be1,128c5b2c,262aaed6,9dbb988a) +,S(28ed423,9bf26de7,d0379a88,30fa0ecc,481c4354,1dcbc3cd,44483b0f,5cc57be,192122e2,ea018e67,4520d860,b4b8d859,2d560872,19bd0ac3,7bc0405b,10ce126e) +,S(7bf3827c,7be8c484,52acc00,eee769da,5aa4ca38,2f806d72,76c72c8b,bf8a708,925f3eee,e505529b,45c601fa,1d71d706,24a75d07,1ad29172,6bb243e8,a559eebe) +,S(cf1a7e99,21a01c3,f8ede862,71f7b6bd,2acb5aab,330a17a4,a7be1380,6fce7000,21365a83,7b3ba611,16a3e740,33a3463,5f1e70a3,f38a878a,7c3a155d,59d3d673) +,S(e779ea37,d1517cc4,e36c812,1bd0a986,af3bdd67,52626944,afcbcae9,11bb29c7,32c635ac,4b19775a,a04e2c6c,29c6f42,6472bb6d,45c1bf81,e8ac8015,d78a8a2f) +,S(10309d2b,76b54210,1599785d,6c381d52,f9697728,8e0182a1,638b7f68,f26c2a92,21edf83,4c859870,efb9b37,7f931316,f3abceac,c19b6a1a,438d020,658265a9) +,S(79927b09,701e46d4,2eb2240d,e9132036,f2b6b311,a7140701,813e721a,533a27f9,fb7c6513,a81886c1,a4c75640,36136104,8a0f47ee,b8d2b905,3b3d84ed,3b58920f) +,S(743b4e33,77da3429,9f593e68,3ce24e3f,85b1de0,4a3dd37e,fdd8c73,1528849e,37900069,b5ee63ec,7b3f178c,aae73281,fbba7de6,849e14f1,8164d6b6,742f3216) +,S(d73cd2e1,ce1bb7f,1aacd9b4,9951fe6a,e46064fb,a73acbf,49868605,c4f04da,e6a762e4,4cc6e71f,f5887fcd,6a33ff1c,cae7f84e,2f22a096,14b7f4ad,b253dde8) +,S(3e1c00d3,f563246b,3ff318a1,77b3ebf1,ba632481,47c073e8,ed32d697,95f6fa68,df2b60cf,87d5c129,1036aecb,80e381d,18ce2e00,c23fcef6,b4a72f0d,2d0776d3) +,S(96e07f90,92da1653,e5cf10e6,87526db4,ed90c2e4,c629b221,a32fdc6c,d968053c,d65c1c3b,b8a2ea6b,9ee20ea5,bfad3b9a,1f8517fe,2e5e3616,4ab20787,4dc2bd66) +,S(6d230440,26b1ee02,531cc124,b2e0796a,d27a7652,33862854,d8e177aa,afe1debb,f3a8f995,faa14689,50fbde47,1e4ec008,e3dc6228,a8cfd2c2,5983bc09,ed79bbd3) +,S(8dda9f5c,1f949dc0,314e43c4,9e716138,ad7d4511,a4b06d9d,c76fcef1,e5707781,d0563faa,7585b0a5,12b674f5,db737435,ad9d9bef,ae5ba77,5c78a582,6801310d) +,S(5070613f,98667363,f5eda369,fffda417,b4bf17fa,b4c276c4,e5aa357b,bbde97cd,b20eff,4fe77ea3,4185751f,9b16db86,eb40e47e,486a4817,8a0cf09b,dc34ebc2) +,S(3a4a70f0,bfab1456,b9b8838e,ac1aa188,4986d74,6a9b3a8e,fc792eb0,af16bce7,e4524a93,19168eda,a1832980,c04e8423,843b3e89,82381e24,8bdf5a3c,adc4f3e5) +,S(2cdcf892,183159a9,da9a82f5,4630773e,db5fba99,3bcdda38,b5291230,aab33532,c88ea6ea,c9113f32,85e299e7,42abce8d,fd95692,32292044,e0a35870,757677b9) +,S(1a85928d,f8f15f61,12e0cd6,f33199ea,2b7fed02,98c203d,781a2dde,28955c99,1b3986b2,821c76a3,fe13a6b0,30725c29,534a124,51ebe945,5682ce39,74db4299) +,S(86d14da1,d6b03065,a1520663,41b9f010,dde01966,3e622c1a,6b66b84f,1da1d388,679514ec,764f5cc9,e884a6f9,f38042cd,cfe5683e,1f6e5055,fd1380c2,8399b2de) +,S(e41f754f,bdfc879,43334341,5a5078e3,30045999,7c245441,565d0357,16201cc2,dd059641,c21d191e,dfef119f,492ee9b4,743f5e0,bb3cee47,dcbb0fdd,790e8e8e) +,S(b7ec9268,45b5bec4,79668d8f,4e444643,2946e17a,8338e2fc,756996d3,4475cae4,ab4a18b,b7539e90,10e210f5,779476b4,d945e350,978ec2cb,7cc62e71,4fdda53b) +,S(240fd9dc,f1eade19,1662554a,a25850b,bf3ecfbc,83249d4,56b769d4,b7518912,af03fa5e,e0ba3908,853a3a62,eaa6a804,5318a897,31352207,76a06b08,d104515b) +,S(353c4ec,ee7d3114,979e39d2,88cc3faa,2b88aadf,8f1cc129,a5c57237,875bb769,9fa7ec40,90af4e12,e940118b,ad70ab8e,28e607b8,5ae33cf0,2a0da4fc,d12d147e) +,S(f4eed58a,a8556688,11606ae1,1628fd1b,89340988,cbe0482d,39e80376,2bd933de,4278cf64,fe74e070,d071969c,a8ceb40d,18b40bfa,52d7feb9,1606f0f8,df3b3d44) +,S(698ffae2,e2d02660,434c17d5,b5552d4,b284f0a,736bc0fa,1a1290e,266ef1ce,1966cc8d,7845b74,1da7e53a,496fc12f,8bd50cff,b0bd143f,ab608198,e1ffc970) +,S(b53b596d,5c88c140,d6e0587d,417c8945,22d884a0,4fb596b,5957121a,65314098,ddff60b9,9befe76,c87de485,76c32eec,25deeb70,4cc19a56,77197ae8,8f2c745a) +,S(97604b21,d4393bf9,d6e4cba6,7e9f2768,79151865,9f626d65,52712fb7,1a13066a,e7aef384,e1cf7f7c,65c5b0d1,7357f266,7d871c54,fa027616,36062623,4a8d17f8) +,S(7a35c9ba,6edaee60,312b234c,1062f7bd,3b38760e,da1d47ed,b63484e9,51bb8623,33897a7b,a4546761,6ae92b58,6ef704a0,769f3a57,86ef8245,7ebd6a42,4d84a5b1) +,S(86d40c75,f989115d,ca26e0cc,9263010e,c9638b17,86707e01,b6a20d95,d1e2804,759bd762,4ffd8c1e,e71dcc8,a846b5c9,dddf2842,c4170410,c0f38742,ec315ca3) +,S(603bff47,9a4bb79f,663ddea4,93b90e5d,fc77f6e9,b2a401ce,3e8deb31,609badb7,62879fc,65281aaa,6aeeefff,cd33a02f,358c86dd,dc03720e,e1e23998,9da4f4b4) +,S(5c8b8f3,c7dc7a7d,721e0bbf,8a75a36c,fa13ebdc,14987d7b,5dee68b1,32203ed,6b601f38,2bc4c6bd,7d9d304d,88fcd6e5,3faa43ca,141bd90c,e7749340,ce08e7d3) +,S(313c1ec5,a91694c2,41e7a55f,f83b7378,25a6f27e,90090505,a7963ce8,7544fb05,9c3153ff,9a4365fa,fb644699,bc4462c5,1ed858e,24752e59,82566037,103c6384) +,S(51aca51,4616aefb,30268cb2,968e0fa0,150b4bd5,a79746bf,98b01543,a9d923bd,7c51f9b4,73a7747,bfd63ca4,f4e2b361,a0a66ec4,31df41cc,203457a7,4ee028b4) +,S(4ae8d399,27ea0648,4936d3f9,2bbe97a9,353071d6,7743ca12,2ab7b6c3,2619677d,f9dc519b,c251bce8,38ea332b,9110538,f350f5e,4352e7c9,19e677d4,3c8a47a3) +,S(6bd1c95d,3f9cb5a0,8c1a054a,884d4bbf,b100c289,c4af257b,aa0784de,a256dcca,7936283b,de39b12f,6f71fc40,1b07de78,80192357,a8887e71,39654ef8,6b803b5b) +,S(e2278a29,4642caad,ae370a62,e07fbbcb,5737f700,9ef9e8ce,19bdd283,aa49480e,43122fcd,f3943ce7,35b570b7,ed82f61d,87bc8b5,83d64f77,b36f143b,4837dd01) +,S(68537cd5,9f9469fd,d104bb01,93335830,8d24fb2c,b22471f,feef4ac4,fe96c644,765360ef,41107a51,dbf99ff3,26e3b19c,4003ab45,979de4a7,32063c81,8400050e) +,S(5ec9ffc6,69c99f9f,8f8a0735,fc088824,70be08ef,3f81bb54,b1290a07,765af330,abb54008,e76f8716,78c3229,56b47c0,1f4f1cdc,dd1cbb38,1a421397,1163b222) +,S(4e00188e,5d4930fb,a9471aba,465154a1,1a330f6,15cae31d,b2399874,704dfae9,7e76f30,282436aa,73f656dd,35b40209,5ed3fe29,d2086bba,57fbb81d,4d317868) +,S(4ae347a4,d480152e,ffd39ede,b4c2f3fb,a2659ce4,bd3b6e3c,8aaddfd5,f15ed984,383e2f46,d44cfdf,4fab8ba7,ba72c635,65bb6ac1,f35c7307,ca2c5c3,62d4e523) +,S(32cade24,a8d3ac0e,f96d90fc,86509a5e,35fd09c4,d8f7719c,603ce1b8,9c6665ef,87273873,5c63b137,5b63c5aa,6ca89f9a,266939f2,e0c936af,55c10d0a,fb8bea36) +,S(8e4ef195,60147576,6ace6891,f87c22f,20c703c3,d4f7204f,5f422444,51cfcbee,2fa72e3,89d97c1b,79eda0af,c00fbcfe,cd5b9d0c,c949c666,459706e,6ed47af) +,S(35fd575c,4adc03f6,c4d54991,18607a01,559d7316,9efc2509,aae7e7ff,a4a2c30a,f9a5779d,274881e7,cb700926,dcd10ac7,dbf3eecd,9c948aa1,3474add1,ca48e000) +,S(932e4c50,7fd02595,148929a5,2c4126c7,efc12f5f,5f5db287,3c635fed,c90a9cf6,ce6bca21,bbaca19c,29f6b1b2,4d1982c0,9e84effb,cdbf1f28,75f61ef9,ba8ba783) +,S(cad8de44,22a051c1,be36a37f,2f459707,55d3e2ad,35debfb2,11a45d73,49fbea11,48b0df1d,4f6669f2,80a41c90,34f86f11,308eebd7,8a9fd3e0,53323a66,f01c347a) +,S(9fc7d304,c847fae7,d016aa64,6cb36383,1919ffc8,fceb757b,462fbe90,6c9594d6,dd17c0e1,3bc6a187,cda9dd38,a7afdca4,1963676c,e4d4bc9e,516e3a45,d9860ff) +,S(41ba6cdf,10d4a05d,c6865453,894debb8,8300e4f7,4c17c0b4,c8935761,988b5849,7eb62b15,8c3f0965,e571e278,b2833321,ece57e84,cf153ac5,9309841f,e47ad4c5) +,S(1ff9fe5a,aef36ae6,a2d74248,cefb8693,d5bbf30d,f057e884,b227ba44,11c7377b,6ba24b6c,585c0038,49c9cf41,fcb55f09,50690850,5be64692,16520575,f1e74278) +,S(299d7c44,d706d29,e6d9d070,4856c272,6d134814,1f8f7ebd,893f38c6,cbf5d660,e4a74f3,4d0374e0,ca9b4887,76de9acc,62f7d1ff,9b494040,fc85eea,2d03ff32) +,S(a0333ed9,ec1c987,6e318ceb,83d6e93a,b607c2d,cea512d6,9f7b2bfd,72e3a67d,da7dd365,5f51769d,35b37393,21fe7b50,19f83e3b,b6b8941e,d685919,fba0539e) +,S(5a4412a2,2e52a330,534093d0,ba96865f,2821cf01,564ea0d2,e75b84fa,404251e0,6a4788e0,7e6c570e,8390bc0a,b06d293b,3675ab27,54eb700c,7cd4078d,8f0e4313) +,S(d772ec4c,3b141dc6,e534ea6,a69f9b65,783351d,c2fe7cdc,3e823cf6,32b7b0b5,bd93400d,1036487e,7090979d,ddab3821,c9ccd92f,db89bcfd,da929be6,689ac0df) +,S(949782d2,c7694c3b,ffb3e1cd,6e40f807,d4452fa4,eca1321c,d04a2367,6df89336,e7c5e67f,b8ba22b5,4372e5db,bcd63c55,9f4e2c14,c97bc930,b68d459c,bad436dc) +,S(7ee7d5f5,75e6f59f,3e0a9e4d,4c49c71f,6d9ca0c8,6177470e,2334f616,a457e6de,4462da9c,76123e66,c9074add,76fa310a,9cbbbe02,d6a07d01,97e732ee,d7d9f3ee) +,S(7a4b630a,e08d47b5,b785ba7c,15417951,8a554847,bb8320b,50faebb7,b13f843e,ca24e44f,b86801e9,7163a524,db4980de,69a3b219,7f9e5dc,6b5d7076,9d937c00) +,S(d2d90a42,428ac40a,40aa5503,2bb623c1,3a18eef3,2994f793,9c31c6cb,1aa82b96,75852e33,2367d59f,30039e06,40b1e0e1,394a3b6e,582dfb66,7464d5a3,f234972a) +,S(d92499ab,6206dfb3,da1b5606,62ed4f2f,a4809b5c,48349b91,340e0b16,e95d13e0,423544ed,26a4fe1a,8f940074,993aba60,fa4dfc64,be002941,11f29b2a,b271be38) +,S(ec0c1ec2,58875e66,d24e2d31,5b7ff87a,bba6f2be,6c6bf4dc,8d5830f5,c5d09a23,e526a7f1,648abd18,1c49a237,e608bf79,8b798c98,75804d7a,ee8ba416,28591421) +,S(69380968,835b5989,885b4805,290ea050,3a6ae508,17798c2e,f30e1009,6bba8863,71bea39d,80f0ba63,45297137,a14d07c7,7522df75,8192f345,a6170f5e,2f11e5f4) +,S(d66b1da1,9fd1637e,44cfe8d2,e1ff2fed,15a40a5d,f596c62b,ebbadc8d,fca9ab23,68cf64eb,bd2b4bb7,1ca17c4e,607b3315,5cc5d91d,26583b68,78a69476,586b9946) +,S(9dd98ca2,c6fa62cb,92750d70,c9931641,5036475c,a6b59695,a510dc88,3298c95f,55a88bb8,53b99cd6,f8d278b7,e63b31db,40750905,d98ffc81,87a0e07d,acfbe821) +,S(1b66bc2c,842cf1bb,ea4ecccd,c1e2d71,4efd848c,db7a9b92,5b9a9ade,d141bfa2,fe0b4701,55fe82b8,af43d88b,68423cb6,402b3501,8831ca89,ddb9a70,83673bea) +,S(b280dc4,8a9231de,d396a61c,2922e003,c7d2d5c9,f0e8312,c1c075ca,c8eb688a,81b745a6,a2f8be66,b32cb3ad,62babaf0,498c8261,531cfff7,da9ce306,d2738a74) +,S(7bc5951c,9eb3ba1c,828125ad,510e2e92,413e906c,f015680f,8cf1cdc5,f18a6b5d,41b905d8,480ba8bd,b8a6fc7f,49adc6d4,fbda2f0,ee229a1c,d43af5a6,98352a07) +,S(69ff74d1,2318548d,76a53095,25ca8695,11647908,dc4f2ea3,4ed2c53b,f6eabfc1,f3a3e628,8eb99f78,12eaca1e,b3adf5fd,1d617397,dcc95bde,d4be775,afde2419) +,S(5fe6dd3f,1f6d3a5,9fa8f8cd,de7f689a,ffa46098,aa0f03b9,2c552e1a,c84af209,9eeae901,acb8d022,4ed197a9,15510eaf,f356cc16,274a1aac,d6dbc74f,60dbfb55) +,S(469e9122,cb0475db,7469fafe,9c53f911,d3b52bc7,8cce23b1,21d22e59,c505f5f0,fe7487a8,38b2ba6b,f5a5825f,a4d22c26,80d8a580,9db637e,cb84e2fc,1058d812) +,S(1f4a3ebf,6b62923,b2df816e,8b30f8e4,72a5e718,70c1cc1c,6d63b4d,9160e6ab,98da1295,7602ae19,89938bd4,298a67d,1ebeebde,88706d39,b77c6a61,4dc89e2f) +,S(d8ea95fe,1c12ff0c,cec226ac,26f510a0,4117222d,d0fa4773,c2f78ebd,53b86b0f,155bef9e,dde6f293,b88bebb3,ce92c249,f154b59b,a01d7f5b,25fef472,8c6288d9) +,S(1ad8a3c7,97b38071,2214b2cf,87045300,2bb36f5d,dc7f2527,1797bd4f,1227cac6,680a77aa,3755237f,15418957,6fcf51ed,58398c5d,4d56a1c,cfa3c05f,4857814c) +,S(ffea7104,5d1d26e,d942646,9f0b0f88,355540b2,c32aa92a,3fdec318,24fa7d66,eca244e2,2d8da06c,44ec8db7,5702edd4,81f67af,2ca64ed4,cb5d04c2,2434eac8) +,S(4ac55ba3,f7fc8898,54a141c8,cfd6f675,d3abe9ba,55d90f3f,c1764ac8,b959d8ab,9c7b2b91,f135fb7b,fd252d14,118b104b,714821b4,1bd879ce,15bbc93b,f8be2bd2) +,S(5f814467,7aa9a4ce,f29de0eb,865b8a02,bc7061a9,ca525536,fe27af50,bb718ac4,bda965dc,4062b26f,e6434071,6301fb02,2b8cb894,73e39b36,69acf101,90d9c042) +,S(f372cb04,37917498,4b29f213,5981f32,da5f96a4,d1acb903,88beb154,75dd88bb,82d68f5c,1e62d500,1a0a0ea0,7694194c,7ceda2ec,e32c4efc,b2a0ab46,1a367d89) +,S(fb12bc5,45760478,36313375,3fbfa4b2,878ec6f2,243e2692,7a3c5320,bb59b128,3f820118,67dae1b5,bb5ac0cb,a24ca681,96193077,fd7bfe17,a926be5c,4953aef5) +,S(3d9759ac,918a4a34,f4307560,17a9747f,4adafea1,b0a92447,316ff819,60c0ea07,cd9dbcf,6d6a330a,b671d5dd,aa96497c,caa317a6,77bebecc,6fca559a,56018aaa) +,S(eae0aab3,699c521c,3fb49be4,669bc7dd,9f476fdf,58e42909,84455c82,fe3e00ce,badce8dd,450ee7c2,99b77b56,25e808a3,62c7ef1e,11e30bdd,9777fae1,4f17259e) +,S(fed252ee,c2c0ceaa,57625d0e,925ee5d3,43718a7d,24ca3384,55e0abed,451eb851,66bbb6ae,56ceefcc,21d66b27,70756f64,dd07211c,5cf0bb4c,457a5a8a,e82c648f) +,S(da639ebd,22ac3c1,417fb04d,3bab0b22,2362af2f,9d3013c9,bc08793a,2d7f9dbd,83f27620,55f1e9f2,fa74ed0d,536bbe54,3627aaea,3e7a031c,10aa7b4e,822466d5) +,S(524d24de,d77345af,4d4d3ac6,7c71df91,32ced2a6,acae682c,e097cfc2,7eab90dd,e4cf5cf7,c6927c5c,31b6f55c,90fe7cff,2d010a00,887b3b01,b2f7a074,fab7f5c2) +,S(7dac2ed5,fb52b60c,35c7cde0,c28617b4,28672da4,5fbc21c4,b24c5268,54ae4f94,cc6acc7a,e9168866,70b5c4a2,bd2466c2,f6cc11b6,d282b09e,a578ee0f,e7794038) +,S(5f46b64d,da61f59f,6b99d10d,e9291a,887e5d9c,a3893ea6,f7c51cbe,e56083ae,6043d055,da252655,506dcfb9,c5572929,9bfa6ba,7e1b582e,5ee1f3d9,e283a8ae) +,S(8863ec33,b6a25f21,6e641ec2,b8c5df0f,48ef0fc2,38a7f635,175961a1,7de14aa3,9d6618f3,1e52581c,2f829277,7490655b,45b2a583,da0ff7a1,17dcb12b,eb42541) +,S(85403446,3ac459ce,6d8184c7,ef26f91f,b377f3f7,3e92ea03,a1bd6a68,33a2f9f4,2d1f1296,8ac56499,b252a5b1,8313fe35,8f7719f2,3b0c9430,438f7278,c5b399cb) +,S(bf6b3f6,217e412b,fe651dbc,362266b4,6a325d27,5eb7f201,379cadd6,5e223c7f,a1160954,c14aa455,fb041e7d,ceee3ded,ebfad90d,6af1beaf,a9b1aef,97ba748b) +,S(4b20202d,3c186135,4aa079ea,409aa4cf,860ca219,8ba1bffc,77124e5a,bed9e59d,9f684503,c107c0b0,ba5d858f,2f11d904,7703eb45,510602ba,b555e8a9,f5b8873) +,S(4087f880,6d679a4b,3a24f533,127b5931,99c050f2,627cbe28,2686a5aa,6421dd2,4838ad84,b3d9d488,33c8c883,698f5a59,69880041,589f921b,5a5707d2,bd88e8b9) +,S(15a79287,cb2e7406,a2db91e0,5a4fc34,53877504,c790eac7,66d9131a,fe97b79c,9f4ef322,76572e3,d3fce497,c2c6160f,1bd597cf,b96a83fa,df2a41a6,b94a238a) +,S(1c4fa063,c0524bc4,ff233d61,bcb3d40a,404fb10a,7e222812,2739b343,e3bcecbe,2a8561ec,a2d82eeb,97209e9e,bd730b2b,d39c90b2,bf4d0353,3aa8c73d,62d48bd6) +,S(a0708ba7,43c1b2c5,267936c1,85312f2a,bb50b8a6,d4886c26,56d163b5,adf2a636,958f45f1,916988cc,6c9b8155,12e1d2c,7dc66a62,359a4324,5845e94d,371a20ea) +,S(d7960b4d,f01a42e6,e109d71f,2e33ffd,9b79754,b129a858,c213793c,e8acad7d,8caf19aa,5a841d2b,61c3d0e1,dc65730a,33566193,63118707,d81c1e19,dbdeb920) +,S(7a03dc6b,f8ba70d5,96e25ba3,8fca1b32,a7c4506e,6e8a3789,daac3240,bf8ba611,70f19e22,292d6856,5a63e378,b773e170,48a3681,d096b881,fcb84a4f,7f7d9028) +,S(53a9a662,a8fa3411,c3989850,c6c83aa5,da516ca0,726fddba,c2f38f25,c7443b97,5deac250,6179c31b,f313279e,844d1e5e,2adcfdc5,b803036e,140ff59e,3945e25) +,S(86b946b6,3dbb0d86,ffe69af6,3c1bbaa9,b7ee99b2,876836f2,a450d9c,feb2cdf7,44407783,d3d0e2a6,cb18ea1b,a1e4f133,7cbc39c0,19997a62,ed74b598,f74b1fa) +,S(ce81cf5,cb884b46,f6956726,20d2423f,6b28c8ff,ba71d9ba,bef152e6,ef752d8e,75afd973,8120644c,349bf924,c04c1aaa,979177ff,2890c926,2d0b5404,5ea9248c) +,S(41106e41,3a464d2d,9f86cb9e,47d57d47,7b791d43,133a2b8f,bc6ce92d,e1172ed9,e2ca44c2,30e920b8,5601a2,773a0f88,2ce4e42a,7379b374,d2ec6fa1,f76741c6) +,S(e9c1f2a0,4e830a12,e546541e,c7ac6f6b,bca3f1ca,2052c152,4e141b4a,20b49777,51df3843,8dd12c2,29973fcd,516712c8,5ca73721,f5d50308,86b91f9b,32a58124) +,S(8bb9e12d,c5e8ff3e,4ba25775,f1552e3b,9be21f27,4250d511,fce5cf15,e73bbf2c,8a822b7a,273c3730,1c24fcf5,c19c4de,b4d0968a,bc23962f,91ef11a8,d00d1152) +,S(b0058ef1,da227791,e4694c3e,ef66e031,697f3f3e,297d139,9ad597a7,acb6bd0f,7f5bab4a,5793814,d8c0bee6,8ffe8025,e5deb644,7d2ba978,c805b2f0,be205a2a) +,S(1dedd42d,8df5fdd6,52e02b78,4dfcab0,6dfc3557,4b8725c8,db95368c,ac2522a5,3055a449,23730d25,18ff4a8f,983b61b8,24b918de,f357654f,5b46559e,b6bca540) +,S(761a5248,c6794ba9,3f45b0b5,12bc39db,e471813d,9d5c5f7d,a46f2ef4,380ea544,185ded30,c4f5ceac,5e18d0a3,95ba8ed6,9f703408,5fad1fc5,c60d10b0,832c1292) +,S(f1d5b60,e515d0cc,8dc20982,a13c1b5b,a1749687,501a4f2f,fefab898,e3aa5caf,55fd766c,395fd8c2,173a9e4,8eb63f07,ee6f9ab7,54994708,e42ed9cf,78a0d91d) +,S(569cb44c,9b7900aa,576ba0bf,61c4455a,fb09ff4b,c30242d7,fba993fd,bb37425f,dfdeb8d2,ff1ae3d4,1e78f646,ac7dda3d,c1131de8,e2566d0,d3340272,cc350b1) +,S(3e59b69a,d99839e8,51947a78,7d8f850b,9c2dda11,7dcd0776,d22c787c,ba1851eb,732cd2a4,8bfeb030,6bdd9358,37ec4f27,7050745e,b2c9f6d4,34cd8834,592e20b3) +,S(a46d6621,7b45682,98250240,3fc0886d,81acec57,32c3087b,e6ea4c4,95296967,140cccf4,29a5b062,24097a73,f9a8208d,46147884,e6127464,51acef,b9763d33) +,S(b920e59c,f25c53ba,7c00c427,d1575c9c,4ad70efe,daeb7927,74d16c46,5079ab1c,606160f1,d4ba888d,1d5435a6,1656b937,9201ec5f,37f585c9,d43197c1,f045b67f) +,S(12cf1ae1,e7d837b9,eafda1ef,db270db6,3b88b6d9,8d05c713,896e06b7,f8bfa8c5,e0d66613,181b934b,9f23d7b,e9cfd8b3,c4c1d66d,485075d9,1e8cca69,39c99c0f) +,S(6aad27ae,a4eaca2,e60620e5,a328154e,eb93a900,be13a302,87ff39d3,8dc0ec1f,932155c5,caa21539,e27e8cd2,dc57fd7a,d4681474,342c0811,2fe626bb,94a1f46a) +,S(134cd94e,7cb166be,82b48916,6c06c1b8,8d168bf9,21f75c36,ce9343ec,acf870a0,42a673c5,e956fd45,e82a51cf,ff09a399,2f7c8fe7,e73d14c0,fab88c3b,9453236) +,S(d5dcd58c,7b7cc786,8c42971f,a1c85cee,429b1d18,dca68e2c,56d9f5b5,5a3989ee,e0ca4130,ef1bcae4,ecda0529,7a338bc7,45bedd0a,f0bb75c5,e316624e,21208100) +,S(951d6534,c344908b,905fa4cd,b691f33b,3e3567bd,1538a861,f66ac9d0,e0d8c8e,eef35922,eb847928,6681a990,345255f6,95a5f691,9a7d79f9,d575f306,ed7046f1) +,S(cedba44b,25f7964d,76d84c9c,9b521156,690af0ba,966ede27,b33665cf,c1cdd0fc,806a9049,cb8e9af1,d187b6b6,493316b6,187e7124,91a8f7d9,4c21b79a,87c79e7e) +,S(4ff03660,d1306bee,29abd39d,32beca15,d1c7d2d1,14492a20,49c6f4aa,d8a10b54,c2c75e69,8fd89951,e36e8bc8,be71f689,4990f25d,d2ff1893,d99e2773,6d1e574) +,S(c08bd508,8dbd6465,e87b7702,dfb7200,c78061a2,683b9824,389042ce,ce2e0ca7,562019d6,1343497a,b1640884,23a9f171,1db329b6,8c63e78,41793255,f88d7307) +,S(48dfdcaa,6fd2ceb2,3b85cdd2,28bac318,955bdda8,274a1e3a,7afd5db,4bf9857d,2a46622e,896167ba,5154e76f,ce429554,af60f4b5,1431d171,d55ffa2,ea0287ce) +,S(40420900,d81af883,cbf76b47,17025382,802c1479,a33a3cee,bbfacff3,16a1f236,866044e0,dbbcb53b,308f50fa,2b54255c,7831e96e,f19e1b4b,e4168d1b,7b85e3f7) +,S(96a3747b,23b667c7,70ec2018,4e99208e,ec1e3142,3b1156f,8b12f37e,322bdc42,67b0b3c8,df8c4647,3310b04b,ad325d6f,dc7545f4,a4a07af7,98c7cd86,3cf22ab2) +,S(cf07017d,e4037969,24361f0c,b151484b,f1db3e91,a8d16784,5039132b,c8460182,b7a1b107,62f29e30,d0f6e762,f6940638,e2eb60c9,e65ad067,99b1bb39,3797939d) +,S(12b5b8b2,5b0bb6f7,ad1b90de,d75fdd5b,93507aa3,dd7276dc,805cf5a,218beb15,5d3d4767,8cefa19b,80ed6368,d40ca199,5a5bc165,b3609ae,e89b56ed,b78933da) +,S(879b449c,e866e6d9,3998b9ac,e18b6f19,77ecd299,bd9b727d,cd99c37a,13a46765,b0c480eb,758cfb12,acf50c4e,91a8d9e5,f0c45215,f03e6bae,c6c13a83,63781c63) +,S(2ec48783,e9d3a01d,d1fa401d,d3fef249,e502280c,cb36d21d,d2b7b90a,cc7dead,fb029124,78f9e2ca,1a5e6150,5eb469a2,83fd8f81,6798703,ca2f0c7e,85a67fce) +,S(79cca258,27bedb07,39fe8fcd,2145956a,5b7878fb,bd873a83,aee22093,437c3ce7,ff488a93,d82e8750,14cb7c48,3eb74b30,bee990b2,2c1915a8,d3381cea,9cac467e) +,S(c444a4ca,3d2b8b7a,826b9eed,40616aa6,8e146a80,d49a9cd9,3e93cdd5,5adbd207,4d1368a9,63d504c2,541ae338,1ded0684,ac00ad0f,b8fc3231,9d1fc273,92d0f18) +,S(3ee47c50,cc0e44b2,5864e205,835e8d6,dbed328a,60493c26,7ee5529b,cff3e7a8,fa555339,56ea87b,c3249640,145e243c,b6229a30,3d1b91f,84e1ca9e,90a5f095) +,S(e963c987,3036bb7f,422251a2,9f9b0f47,8e69e2d9,2d1920a8,1c012d7d,c809980c,ff95bb0,779bbf2f,1242bfa4,880b173e,140f92d6,c7f8e5e6,18027a3c,55c52fd6) +,S(aaf6075b,b6a1b8b9,afe4b747,a46fa394,23738e50,e686c8bd,3f711cc6,548fc07d,4cdb0aec,6d11ca1c,1e85bd16,2eb411ea,53ab326e,bbdf898b,1bbd5219,aaf2b0cb) +,S(691e4817,d8e9756d,8e174a18,d5d708aa,842b44d5,ea921298,6e4c7585,4157ac0f,9eaeba0b,3bfa7ad7,80d0eb15,72f92873,4bb9a8df,29f508c2,d1ff4076,a0c0c222) +,S(c3e6fdbb,45af4a48,941c9d4c,583dad8c,c77dbfb3,1caf3fd8,1d9e087f,e744996a,ca998d1e,65ab29b8,fee07bfd,1ac4e979,b869474,b5498a73,e642bc9,608366f2) +,S(edbd6320,a55d25cf,21f2630b,e7c5c996,923eadf8,333d386,8cec00b3,940701f,d65192e7,afcef5e4,c88cd12b,fe28f94a,5e3d3d50,f6b2c3e3,f7a14243,4de4889e) +,S(c959ed83,a80ae0b8,ac2fd249,43c37412,b29684ed,9d4d85da,d7cb4c34,ea95d336,80c1dabb,8dccf252,ad8b0e97,25970c1,4de5f58e,12f7e5f,8b59a2e1,a72b168b) +,S(778cc574,471004e9,d88c9022,4d2d5cfa,2c5bc6ed,da64a312,74e3df83,3eed9237,eea23d31,6b2a1c02,7b92e502,db921d9d,675720fe,51cc3609,46b2dc93,92916c17) +,S(a1f87438,8c3656af,43f299f8,1d98d2c0,173e7e46,d3fbed9a,5c2afc96,63b42055,10bc8443,820d6c5,b3150e79,b9936785,5d874afe,dc72a84e,abb987a7,d56f4afc) +,S(c8ad7f0b,2d5f8b8a,cd9a1839,c3d043ee,8f7fa583,7540f6a3,d002091f,68224a5d,3018b580,eebe5b9a,2d1507cf,18a7fe4a,2a1cd4b5,1157dec3,8e65f191,c7aebf8b) +,S(c25d5264,b93fd7b,b7e4e426,1ae767f7,1ecf3840,a2a7ad20,bea810db,4820055f,fa15c8c7,6e3f2b0f,a29fffbc,eb48ed32,bf331aec,3a147524,c349e8e2,465ad684) +,S(d0b972eb,7e4cbfad,baf224dc,cb2d873b,28316877,88b5f2f2,ccc62bf5,e978f92b,ef5d3ca1,606a5852,aafbcb4c,f23e787a,75610b31,8420bf56,e94e420d,f7ab8e3b) +,S(f8b44c83,58564479,6448791a,b98132b6,d393e851,bbc294ab,56c8042b,1b0a2d07,c28242bf,42040be,e9851362,fc8e764e,2dbe0c01,d5bb0ed0,b8e1edfe,e870a27c) +,S(e7cb8901,6f72aade,39410adc,12fd1234,5d942419,51a6d36,8acb7c83,da5666b7,de367522,46b6d9d7,efd4ee6c,53a1656f,f0c87e80,cb0f8f63,95341c35,d6b21697) +,S(8b2128f,14e3f4aa,92e89478,64c5eaf5,f40f93e4,95cce167,fb2c7424,c279d87f,e7ed384a,f469b4b1,1812068,a830303e,699926d5,b8304c11,cbeb0e49,cf5d246) +,S(f34db5db,3cbcd586,38b6a5f3,ab5b31f4,1f3f631d,785e8317,658408f5,93b4afb,dfce7372,bb2c8466,fa31a1c7,4ffc1ac4,5bf11263,4c98ef2d,56e30fd5,c3dd3d9) +,S(c3689e09,a445d685,daca48ea,bca533b9,10b4fee4,dd251c86,a965d5c2,ecaabdb8,7804d05a,6a263fbb,c33efbe4,f4075d1f,d62e187e,3a5917a1,55c1f22f,1679e3f4) +,S(7ff995ec,f776a10d,7a66d62,6095892f,f1e47d4a,2b47ca78,d708a0cb,3f005cbc,edcdc774,5f17e430,cdca4d11,56d08288,e3911d0,e92999f5,cd5c5eac,ca6ea90d) +,S(3662a262,62234bf9,69b18351,2423ebb5,db78bd6,1cfca8e4,1c0253ec,427d4d27,88a62042,f07e69c,f40f671,ccaca36b,d550ef9b,79b4999f,c41cabac,1c2e1b09) +,S(2c127c2e,6d9c42e,970e66ee,24a07388,b6b4ebe9,1614b951,1763b414,3f54e8d1,a4773eb8,dacbadfa,afa26c8f,f4adae30,856be023,6022d93a,aae59c9d,fdd6629) +,S(c420ffa6,116f161f,fd94863a,9fe30b7e,b7dfcb0b,8cf9acb3,7f6d6515,4ac17802,e6cc469c,25c68f6c,8499e9cf,95eda8c1,7fd76d97,2ee8fbc0,79b143b,e50a028c) +,S(77896743,220343dd,58c2f06f,d3217468,34d386a7,e7fc969b,50c77847,eae3de0d,4350d59e,4408dcd9,710bcd7d,629e92a8,cd906759,627921f8,57054a4d,21fd9369) +,S(cf7ea42c,1a14dcb4,3334f9b,636ff0f7,7147688f,19133c83,dc127139,349f5827,b9c02c80,166bf9dd,139c2846,895e4aa6,ad1a689,66460a29,f8e951dd,d99e6d9d) +,S(d1793439,ab1fabdd,ada8ab1b,33c9f96e,417b1c84,7f753630,9d815fa5,a673a91f,ac13d659,3b5941b2,3b19f6f7,f9a67d0f,b429add2,5a40d05f,9bc0d1cf,ced3b3e0) +,S(bb1568d6,727dde70,c52f5863,133dc9e,54722577,4a74026b,e9864f02,9e92b420,b5654756,268d74c6,cc6adf80,ceee29c0,9e137d2b,e15aafe7,b170c95,22af3992) +,S(732912d7,83e8a5a3,a7468177,231ba93f,ce07c42d,778dc205,35cdea7a,db877dcc,d34b8388,1b128d23,e68ffd7c,29c7a945,1664f5d3,fed3dbab,6f78c8e,77df80a2) +,S(c74f0f71,fb8532e8,2c3e1865,1684d3b7,4927b6dd,22504afa,9084688c,7297074c,9da5ea61,c0bd178e,8bfe5a92,dd040c6e,35984bd4,1102806a,53a9f1a7,bd006ac1) +,S(bd4fa939,8bda5a0c,5a4e9e49,43060d9b,1785a613,3c970aac,91fcbc68,9e3283b0,631d4fed,b6614b49,459be1ca,ccbac0d6,e79cfdbb,f14de95c,5d2a5cda,30e433a) +,S(9fa65462,733f13d3,2483e0c8,15cb6bd3,e9bc2d79,7b89ba72,f2465c64,a341cc8f,7051fc5b,afc5b18d,748ada32,c4a27151,443505fc,2f14f130,10fa02e2,6a12db04) +,S(eaea6f7d,ab3dbf1e,816434a9,ddbe53bf,715168fe,2c1ebd4a,6cd40744,c35e31aa,b96962c2,4f202b96,c2080619,ff4ba905,a594b9b0,1f709e6a,76a1a7ab,9784d61d) +,S(b3df7ab6,3b947504,f33e6d5b,7b8e2553,9a117c8a,497f8751,6b97bfc5,3b17a2fc,72df7500,a35ae6ce,d6fa6265,1a759dd,f11f00ea,9152a5b5,ec51bbc4,9d758b9b) +,S(ef875ecc,46da48ae,45619be9,950e294f,4341df09,726417fa,3a8d1ac6,c3281bbf,7ab9e204,af4c05b6,d6d0eb6e,8306c987,51ced2c2,b9e8dbc2,8a642bb3,7f1c72f5) +,S(74c3c446,576a8a3a,5082f234,afca508b,3d748757,4cbf14e7,fc26f36c,70024fdc,73d57916,4ddb6fa8,72dd2afb,f9d8a307,b6ccac9c,94a4eae8,8c4fe8e1,dc136506) +,S(959c4b2c,409f2e3e,6c020493,33ba2f18,c06b7182,5a664e1a,cf33846d,cf39274,17cab35c,7401629f,b73cd398,dc5bedd,d319919f,1a3995d9,45f42f68,2c9aad47) +,S(404f0dd6,94195313,1953b53c,67fdfab7,f67c24bc,92fb4d69,a9479a9b,6c9175fb,857c75e7,f40a48d3,ab156085,1d42b037,7e7d3e79,86efd570,147917bd,f24d87b3) +,S(3e040511,233b0f5a,53df4d5a,b0854137,2db4ecef,170b566,807c4923,6ce82075,f9337271,dd443843,77a35ebf,56753b22,1fda08e0,1b790e02,b9603827,600df3d3) +,S(942ea853,10dfdc3e,1f7993e7,47bdac04,ecc8692b,94847bd3,e044cdf2,2cd77e6e,bbf2f7c8,e688b6e9,b4510bbe,58c41b1d,f33e9afa,5428ce51,e02e624b,9ea155e5) +,S(1e577a74,9883a5fb,85593809,7d0180a1,dce21791,e10b2c0a,3fe6cea1,cd03b532,a147662f,856f5e15,dba53000,7278e878,7b575612,d5dcd790,19417ec3,ac41c003) +,S(ebc9d95a,8a237b64,212e9dfa,607fe4a3,7a05fbfe,7c5594c8,ae472c4f,6ffeaa5a,39406dc0,e311a47c,cf15fb45,8f8b48d3,8fec2fd8,6b4afc05,b1747957,e7f1162a) +,S(c78eaf60,1afdfc20,2123d228,8496189,b9081637,1fb2c475,2773faf0,63cac063,95325b1,e0c22dc5,df6e84c5,16d396f9,bacb42cf,6c1fe9c7,6df83b3f,3eb42de7) +,S(b5918719,a1d504d2,b44a3d0e,117c2798,4f2e536a,3571db8,752685cf,df6d34a0,9b01e8cb,585cb7c8,fa29828a,ac899050,16beacb2,5e62ea35,6d54e9a6,18184abe) +,S(8706a73e,b59fc9d1,92907dfd,cc6f75b6,454f1045,4a5e9584,bdac978e,8e0042cd,8b1b5f56,6f1f78d6,f528405a,2b6a87e8,4265e2dd,1e34c1ce,7109286d,5bdc3a49) +,S(93e9256e,4ddb4bfb,6f4a9289,6e2ad04,793e5642,3a22230a,e645529d,712d6ca,83b242db,d9b3dfec,3640ea62,f0e3989b,d874f247,90a75cd6,9756a8ed,6eafdcea) +,S(2dad49af,8702f39b,cb353956,a1d5601b,a24fffbf,58ab527f,e9ac6ff9,ea2ac295,b5af68b8,e6040762,55c0704e,2cace8c0,ed403611,40dc02fe,2773a625,27e4675b) +,S(5ffc1899,4da42fa4,54962719,5ebe332c,86a2c364,f43b14f7,59710c54,4f6950a0,22eac0a8,79815faa,220e6141,9db053cd,4542ad2f,9ba9f63f,4331ad5,c2c9bb88) +,S(71be09bb,1832a799,c4e21bb8,a2bb5ad0,324c639c,e6e62b6e,69fa60bf,d41c51ba,c5227fc5,719a5b8f,28c40767,b54eb249,950588dc,c15e1656,e4ff238d,54dda472) +,S(4f94305,f92799f0,a64a3cb3,7df2bac4,56d661d6,44568277,bac0ed6,6a587212,4c48691f,bd8683d6,b94ddc7c,ad8d1d7e,6baf796d,6d00f344,511fc13,13c054ab) +,S(55ef868b,6fc2923e,e624fc09,65f61852,b3aeb38c,e2c6b3bc,54b86b3a,60f0ab81,ec198aea,ab16b4cc,5354d0ad,c66051fb,76cff6df,22dc1a0e,72ce6e79,1622efab) +,S(26ad5590,46e19dbf,e60953e,d40a3958,a357f6bc,23642279,cbcc355,74fdab68,af6fc5ef,906721c0,553c6a74,60bc6894,e5bb77e7,4dbc25e2,184fb3f0,fa795508) +,S(739fb43a,b1b07155,39c0b0ae,43901bf2,bc051a69,8ee8d503,84fcc1f0,ec37312a,c1223704,3c882226,2d4abe33,1de6ab4d,ff90d6b8,4c0b59e7,a193e763,6c6b7e28) +,S(2144649d,508cf5da,a7b422f8,22e9125f,d5a10965,a9d32c6c,c299bad4,6616771d,1153178c,e33c445,f03ef96b,35067ceb,76ddeaca,d9ddba1a,74acd229,d01ca76e) +,S(62cc463c,16ee8e8f,16ea8517,5508278d,4d5577e3,556d3580,ec8d90e3,bbb97072,cd294843,e299c500,846a6c91,c913395b,56b48efa,2f9ab3fc,b81863fe,8a6a891e) +,S(1196301,33edf190,d14fdb00,640f74cc,4317e1dd,b9dff1c9,707eac33,53ef9c21,f16ab4e5,e3a3ee6d,74385187,6da07034,caa4f519,6106a0c8,3136d21,5420494c) +,S(b250bf86,3fdaf93f,28f15bae,d9555287,777d4e34,2673c16,2b4a6319,2289adcd,295c05be,de343d28,87d3543f,6b40d5f9,da1e06ef,39fad6f8,28ad5ac1,39b3d2c1) +,S(93a40bbc,ae584739,9f10945a,72c199b1,27ef008,14e87e69,6f4f46f6,483e7c6c,81ed70c0,29809674,f5f289a4,5926864b,f088e2e0,af66173,89fe8cbc,aa858a09) +,S(b9e6761b,4e909d75,b17e76b3,4e9825e0,19ff7ddd,4faa9f90,eff8c0c0,f60978cc,640c1503,5fb91270,8881b70f,bf2c8575,65c75e3b,c98d211b,76279090,7b5aa117) +,S(4dc22812,fe3d9ba4,687e260c,f5b2dc08,ae454751,b022b9be,ebf4360d,10cfec24,9c35ef4a,56858135,552e986d,a4d68056,27f0822e,43031c43,5b2df5f,7f9a90d1) +,S(431276b3,1127f9c2,406b94ba,683d2d3b,e198e225,23663540,9348286c,4c9deadf,2265ed84,c7f1df86,4b46280d,ddc8cbee,c26d39c8,ddd5b743,13938c4a,e01efb3e) +,S(75c5b6c3,c99058f4,2b4b81d7,6cfaa917,328f1736,b8788360,aa810acb,3683632a,549f009f,def17b7f,70a309ef,49a890f1,4a5aaa1b,a80f0464,affdb5c5,a8f719d) +,S(3c900027,205af742,976b1a37,7a4801df,26d4d4e9,12ea757c,815ef99d,cea827d7,e28cff3,37eca7de,2387fa7c,339dab2,7a2446,fe7184c,83c578b1,8649cc0) +,S(5660f31a,3676b95f,9fa8ab0,d5c23f30,c89637bd,3a8ea97f,3a87aeab,3310d9ff,d6069cd6,6bc4ca0a,7d892ff3,e4d9aff3,955bb76c,2ba3f839,efbd786c,b11c568b) +,S(2b8b68a4,1b45dc86,7f7979ce,5b71d778,e6733cd4,79af6f61,8cd786ff,5f3babe9,7c831244,d5441b33,c20923f6,3ad9a093,840f89c4,6c1421ab,40ac52c5,8de4097a) +,S(8635004d,b817bcdc,4b357886,96b3fca0,1d369d4b,303e9290,44229cea,89c18610,2472da40,2bd46e6b,e2f84cc0,5158da1c,7e9f3512,1fc4b00c,4c3ec329,6eb909e2) +,S(6c991a6b,6eeb4f97,9b7afbde,46e1eccd,b0d39d03,dc64fb69,3a56a234,88cb28d0,1e5df74f,c2cd6350,50277188,d3eb4473,e0b753e,4cd41372,2bc119ef,65c95620) +,S(acdac19d,4283cc91,b9698ed7,e92288b8,554569a4,962ffa0c,a52a197e,9f41570c,fa536d87,523c8541,f530c6bf,e71742ba,451d41a6,f97d9afe,ea03a520,c6d7b66e) +,S(b1496ba3,f696cbb9,548b107c,3d28a7cb,85bba7f0,aeb50abb,a3e2a596,465fc6c0,77fb6cd5,727a4a6e,e0fc8f8e,ae807827,487740bb,ff5cc57a,8572ef3d,c86318ad) +,S(3fd97db5,6098e9ca,fccab31,d6d128e6,c2c9f878,adb07ec9,4d854034,d9b09126,1bd77c36,1494e374,fc45127,38b246f7,24e37d4b,27e14a2,2c0401f3,222b4578) +,S(5e8df855,f2a468ed,5476d5c4,409c7cfa,eb174cff,ad793338,8982ae2e,c2aefce9,52e73a82,3bb8ca88,a0977184,38701841,d50da8ec,c46f357a,17eb8cb1,36a6845f) +,S(99f106eb,1417cb69,e95e6fb0,a0fb932c,62021a1d,b940c661,3f814086,c64672bd,9e7fb038,e1e70011,6a926e6,a1c27fe5,e484d843,d184d533,7e9825ff,f7a6501d) +,S(686fc498,5526d87f,fd11d686,4ff0e208,68bb14fd,4a5c71f7,ac43063a,7e51ef8c,1d28f532,d793af85,bc96dd6b,2e0f23fe,4137dc5f,5b3d6244,22f60919,44588e5b) +,S(cfa5df56,cc450bca,914f3a85,b370f864,e4c83600,560bfb20,93df96bc,bff6b83d,cefb9a46,bab7ffc9,1b1d96c7,48f81706,61cf56f0,264e203f,78b05f3b,8d37604b) +,S(991b6b4c,3fbb9693,f7641eed,534e7cbe,8937606a,962a83e8,b39089e4,713e404e,3ffeba6b,be556688,d3b8a0a2,f4b92b8a,98dab79,d5847c71,13bb60ee,be3f8d3e) +,S(d104c5d3,b9b0121c,58a1fa14,f226c513,2955c4e3,56938bcd,6891bb92,13c8c0d7,8c8399a,814d7cfd,32cea21,85c77738,91ddb00,a091196a,3d90d056,b0caa6e9) +,S(76192853,a45c3648,749c9a40,719424eb,415a06f7,c74b31bc,ad93194b,e355dfce,31139287,3cbb5012,d7ebc934,52613c62,b5517b37,91475ecf,84ef3745,324ad2fc) +,S(173304a8,509c1f4,c05b1de5,c51093b2,10c7f9b9,81a7d6c8,8059fb1a,e5070725,43bfbd98,c8e08394,543db2e9,c692297a,800cdc8b,398c13d5,f9e21f89,341d353e) +,S(aca057e1,d28c5514,107f5b6a,9e46fc95,9eaccaa8,b14d4c4,6b7ae515,bda104a,e17f1717,70bd76ff,18095be9,a4b72b8c,cb00e6cd,23f6d702,4dcba433,e6bf6ce8) +,S(fb202550,e0098fb1,c30a7385,900a7dc3,85b2945f,60e6eb04,b7e8489d,3476dbaa,1aaf8420,3f72c68d,bd3bc98c,77f473cf,cedda922,dfc3c996,45e4f565,ef9a0f28) +,S(e95b710c,a0227ab8,e5b66c66,c2958be3,af1aa298,422c501c,8f879bc,e11ae7ac,e645f4e1,ab5cf4b7,da909022,cedd01f8,c745a968,d9ca30a2,c5525d50,9f97cf19) +,S(6db4525c,3589bff9,605cf203,cced45ff,ab136c6c,ce77ea15,14766f3d,e844770a,e6b7f403,f5f7584b,2331d295,82c0698b,799cd7bb,3c59c903,eb5d6848,72658c81) +,S(8893aa9e,49daeae7,15fc2959,e4db9c45,1690438a,6964a2c8,fbe73133,35085eb3,70fbd83e,ae803f3b,6377a4b0,265f62c9,bd15cec5,baec8c10,d01c7094,e5987cdd) +,S(be57f6cf,ac6e8cda,871e3ece,8aa78bc9,2cd9e2c4,8f3adfd5,6e39414d,8e0207a0,78170872,99abf89c,fdcd141d,899aa7ba,8893eedd,36bb3aa1,58120f2b,ee93d5f2) +,S(77fa6d2,85a01129,c5e05ddd,8d02b5d4,a2f69bf4,5a9e7562,6718cb54,20b34925,315bb23c,13a3d218,ce886288,ec0edfeb,37a2da99,e017b97,ce04db45,326455cc) +,S(2fed7bfe,8414dc33,62c834ca,767ffb0e,61094ad,978ddd00,b2ea09b8,d1f4dc03,14e5e8f7,aa0b3d31,565e434f,11c61b,63332a60,b6c4cff2,a6de2c5,98117e01) +,S(9f4c2cd0,5a871ba1,fcb43df2,bf03ee3f,7f8af242,ce8ee6a1,1aeac89,b4d66c6,eb421f52,f2427887,8ff8a2ab,dc384e23,d0fd7df5,fc341acf,83bc5940,be922920) +,S(53c0e0b6,824a04cc,b7203ef6,e37383f0,15f0a48b,bbc1d3ce,7c8fa7ca,5dbea647,6a9b598d,9246ca1a,539729f9,be809397,13c59d5e,105a8e91,836fd3a1,4d618cf9) +,S(dbc0cc82,d7906b84,b6b7bba5,433d68eb,bb20fdbf,7b4eefd5,c0a3ad8a,ad17d714,5ebc1f86,23ff2c3a,6f81b090,4e5e6338,8aee42e2,85306e37,a4fc7676,2dcd7211) +,S(7862af32,a3c08e04,7b1a8cbb,8131d55e,ca93a478,40c52165,af20aa6d,248f5f0b,bb5f6607,6b10353c,8a367a64,587b3bf9,7e8c272e,8da92684,f1efdc33,8bfc15d8) +,S(d3571fc,800158b7,6ed2bad8,15c93b1,f8238fc9,b8753752,4e44c0ca,931049ee,162dc9e8,3e6630aa,9d0872fa,915af449,70a9e6f9,526bf15f,4d26bc74,82a44bc3) +,S(a851c894,cebbc758,642b98e5,5df3584f,b9f531da,88faea26,e908b0b7,88589d9b,74702d5c,adfac1ef,71df3898,3ae433ad,1e1af1f9,b5fae0e1,151258e3,b375b422) +,S(a92e43f4,12effaa,e0978a08,cdb2e885,2d9af1f9,1f0931e,972016ce,da58012d,d8f5a09d,5560170e,b35d7bd1,28252096,535c687b,23bc13c6,bb903b95,d02fb668) +,S(75cafe44,8b4369c8,42e658c6,4760ecda,bb29e129,d283c24c,9007fb5,f3cff6bb,ca0e12e2,43c73553,1e2affab,51d4738a,db1ea5a2,fba215f5,3c9477b1,b55dc39) +,S(ab0066,4db959ec,2f17a233,78023b5a,c48c4177,7b6e6b56,a2148453,8c41d006,8cf9e2fc,35545169,f46762fb,940758ad,e3d3387d,eb72e9b7,b5d37175,9f5d8b31) +,S(27510ef8,2e4b2134,180855bf,469d1f0,f89b9afd,573f2781,4a1bea50,9db7ecb7,20fe00e8,86b54c06,1fe07355,5be99fd3,b0a1b20c,dec68aeb,90055c34,acfb87e1) +,S(773ff5a8,d388277b,7921405c,99c9c207,f4a6c2ee,3b59ac1d,7452c8ca,4db16d3a,cf45e631,f51cc65b,afc806bd,87318e3a,f75caf,3e734284,8d7435be,f8a52f8e) +,S(2f8f2c98,e69ebfc3,b7277e35,9da1a8ef,4293fc37,2e24386f,e14cc455,1ef45746,f85d23ec,c4dcaba0,23c3e406,604aaae8,8150a6c8,feea2c36,42ccad40,9968ec06) +,S(1878acda,ab8dd0e2,a0884952,26356ce5,74575678,64da41c0,61a83de9,6277f67f,412c868d,64e092c,ade96f05,bfa48af3,ecbcc128,e6d09bb,9684565a,d5477020) +,S(8cf281a,eac4e4e2,7ec4f3cd,d1373f62,c5a17b0f,49cf69c4,cad8c4bc,4f5618c1,2f17f995,29c9ea27,cda3911e,de33029a,7dbf09db,64ffa625,cd9fd86f,29ed5973) +,S(3a597c9f,f165369e,5d90d191,3dc2aefe,a0957936,717f1ec3,9ea20698,d08939f9,8c2203b2,90c9a2de,5b464b0,988e07,a7b48c86,f5891926,da462b4e,b00a14d4) +,S(cbe3205,96f10ff,5de9c861,55290393,b9ceec90,bbac68ac,5054facb,950ca15e,446cb06a,b76aba12,c32b4855,716001cb,9bf7c72c,ade16bd0,5472b947,4fee27f4) +,S(4313c9cf,8224e70,18413b09,e6544731,1cde7100,5ee60bd5,3a8db041,56fed594,dfaf359d,d3045a8c,b181021f,9a025208,e80bbd4,abc4a331,7ab9d735,1b329a96) +,S(cead2b51,f8d8adec,2d89651b,2da5ffa5,1f5cbfab,19fb7688,2bdf0faa,bf73c60e,ab2a2480,6992f0e8,c73e7932,2bf144ae,6dd3bda7,ed2d221b,f8451ca4,56a25897) +,S(9ce75c8d,848762f,645164b8,45ee6eee,98e34dad,9f8be0e5,2d8f27ae,4a7b79a0,e0556d4f,e11ba906,6b97c04c,8b742a26,9f627559,b9163571,d0a29dbc,9aa30a05) +,S(6024489c,621de255,f92106bf,8c23847b,10214b83,1d653a9,6ba00889,326741d7,950eb24,80a90200,ec932621,6b82e5e,abf63e08,6626faa9,b1fd8e2e,104ea858) +,S(d3c746f0,108f6bcd,406c2638,c07255fd,1f55e1a3,c6845e11,546d22ed,5f08e51d,d124dbe5,d349c739,e641a776,c57ef3db,35ed4e1a,6ffc165e,25d0bcce,438dfab6) +,S(a1eb5f5c,d85dc222,f97b9e11,e08594dd,a69c69f9,bcfa4c63,52cf2717,136514b8,46ed3d35,a393e017,76247ddd,7dda3ba0,ff5e3f76,30253b68,750a7c,f8a15fff) +,S(2b24a357,fa3ac2cb,faa5f1c6,1c14147c,e4406556,b82c7639,721f7561,485a8736,d67219bd,707aad55,1a8fd7ae,d5721d45,ad192a5f,5d0b643c,18330cd,1bd73ade) +,S(9901a1ab,9dc60159,63cc9cfb,42ecb41a,ab6a623e,62e9f306,126a4f6c,fd87b2ec,8f766594,17cd7f2,fff7a3df,6cb414e7,eb5950c1,ee265730,633ccd8f,f0339317) +,S(311e8445,5890e2f1,e1d8e600,37d58c83,28a2a4e1,b21c3763,4aeb9965,8477e8da,cf593f67,6cc75762,a1b0dd00,f6703bc1,e017e927,1e61c829,a0459056,ecce030c) +,S(905f72e3,97140f6e,298ad8b8,c8faf66f,122d27e4,6fee88b3,67a8ecc7,9e83bb77,c03f155d,bde9c265,d90b94fc,1b5b253a,2d98528a,a12e19b4,698714a6,f1b65257) +,S(61df77a5,d5940001,2fcc2f02,199cbb4c,39eafd33,27dc85e,4a3f55b,a339350c,27c88b7d,de9b3f,73b0603f,2d6e40f5,f3664020,8d6dc05f,684626a2,d9049161) +,S(e1f8e199,886d4b1d,5dd6af90,6eec06c7,de36dd4,ef026129,5bc42bac,5df18c28,502c12e9,b956eaf1,2865bc4b,ae7e6348,56a98db5,4c65a5e2,9ecf8ab7,51ca45a8) +,S(ade2f5b8,3804a21c,7dfe6a7,7044cf96,9180e154,79a4aac1,9b686076,fbb8565a,d0d65e1d,7c8168c,308985d6,f5d267e5,98a9e6f6,45715eee,3751bc26,ef066aca) +,S(c55820bb,154f972,ac345dd5,c62880b8,9f7f6b85,a1755b88,ea5821de,6f268187,911b0fea,1e6ca659,263b698c,8a17488d,4dc35f5a,d817e7a2,1241e56b,cc613086) +,S(ea20966e,321020f4,53964352,b8ccc2d2,1285888c,152ddd08,9f7039c6,6c7778b5,c942f591,cdc81e52,6abdc9bd,643429c1,66f5476c,dde18d39,59e29f26,9f4fc18c) +,S(3c57552d,9c2dfe5a,67301bbd,3e1ef84a,220c908a,8e80c707,9f9f4df4,607e2bf5,d49fa0ff,bc4a6397,3e71a0a1,7265ff65,6fd34754,ed508522,b9f0200b,e71f39e3) +,S(f188ccc6,e1043ec0,59e8e133,94ada165,a3fd5c0b,cf7f071f,eb5a0951,9bd4e1c3,5f1f371f,e53e79d0,2d6fb190,8207649b,db045c41,a2fe1cf,94486053,7bfdbe4) +,S(a0145fd2,7e72232e,8055a146,aa7bfb49,447112a2,d1ca53ca,d4835e42,c11c8c6a,7bc018a0,e5e70ccc,c4bb675,f0709cb0,ceb874bf,22c7ecc5,243e49c,3de679cc) +,S(53400de8,c221153,1fb85706,b558185,c17b7bd8,b17416bd,9c93df80,f16ff48e,e1228e47,3ba565f1,e50840b6,a36ea970,2df87320,8a66a44c,89cd33da,41a6cc6f) +,S(82115d2d,9d37e3,50ce3482,1d59af85,cc8960b4,52b99904,8c6c6674,ce64b1ac,98deaa09,659a0c24,4aadab0,3b9d1881,bffc1b3d,e9226f05,e75fb9ef,b61eb048) +,S(273d8c18,53d86f1c,c49e6e9d,450dce26,8669cb7c,3083968,1fc17894,59fe7c3c,6c5531aa,8ad026c8,174c25a2,51efbadb,14974e1f,7f7f88af,2e16d101,c2ea00) +,S(616e16,a7e38191,70e33f4f,bf428fcc,41e68ccb,5b059bfe,4036751b,ba5ee319,7dbcad21,182a3efd,b99173f5,cca99374,7c7dc3c7,3e8af2ea,ba84b0e4,f506748a) +,S(6761896a,76a39812,1f773402,d033f210,dc566610,275f6774,52c37c01,60b82812,7fdd01cf,1b40939f,8091184c,8e2c8ce5,11848df1,a23607eb,e760782e,67302c9b) +,S(77f7abaa,e97605da,12535bd9,e177b6b0,bd6eb020,92345fe6,dd4a6d4f,89e508b9,fb04996c,cc7b830d,fcf5fd19,bb7c9332,dbf20fa,10d61231,889e4b65,cc7f0b9e) +,S(c12f6717,3a2e2bba,3da97378,501f9cf1,bc4b4a8a,67b0c5a4,a0d8caa3,98bce311,de930986,44d31401,510012aa,3b6cb541,97cd133,6e583853,a330319f,779a47a8) +,S(6553a2de,19c7552c,8db9d268,ddf3a034,4afe8ffa,a76bb304,ee77ef95,c18e66aa,91a72010,7c180bb7,2af4dbb7,752d374d,69f95bce,c9bbb7f5,37377060,70edb93c) +,S(f54b4be,353f4185,22e2e936,3400caa9,bba98023,e6a3d3ee,c1fc5046,b7b834e3,96be786c,bdd5f1f0,a835afde,219745ab,ab6f78b4,577d3a5,1246437c,8e96f17e) +,S(c2d317c2,a0fa6522,bbc806e,45b4dc8b,f86be37c,4b677c64,c0d5b4b4,eadbb2a,c5710969,bd68cabb,718cb16d,fc9994a7,100f078d,4a3aeaf5,8e8eb35e,d313ab6a) +,S(52a567c6,a5b8a64f,dc74b9c9,57f5d472,cc43e143,4136fcce,fe1ff313,1b89f384,332631bf,a314298a,dd45f7d6,b8192956,d7de27e9,7d835d48,5c16f7a2,9c9e8f53) +,S(f7dc086c,891a4412,88543527,831247bd,3530f0f1,99b4c5e9,8d24a207,1dba4e20,734ed712,452e59ae,b0ef5f75,10485860,da055003,ff1067e7,b82de345,b74444aa) +,S(954f9fa9,7bb6dbc0,cbed8d8c,de6f8f75,b3f2ae51,e512cd3e,90520d8b,22ec06c6,b65b43bb,8bd9660f,4bdb3017,bb4f8214,5426f614,8f04378b,f65c39db,185dd9bb) +,S(20e69b98,41416588,20b66947,f775cd96,6def2dbe,416b545e,8188db14,2604e87e,e6514659,bd12b9b,35ae45a3,d43f6023,dcce04d,87df1f51,caa67f89,28632539) +,S(5cde5b82,2dc42f04,44187fb5,33029577,71bed484,2ab39dc3,709e7c57,c72ed1c,a0f7bad8,64e405b5,94416e7,53fd6137,cea3dcbe,7625fe41,b4929299,a9a0ea9f) +,S(b586cb4c,6ba6b0dc,2aafd03f,49d4d6e8,45b20460,5de71c34,6e73b131,fb26421f,ced613e7,c26ea3b2,a31de623,506afb86,6767b469,98bb116d,2b4ac9ab,58948744) +,S(7d3f6170,35803a82,c8e13dbf,a8d601c5,4e192a54,377e4a8b,2a2a5de6,90ba6ede,138e810e,62f413c2,fb56477,80c0bcbd,77eaf84b,e78e900f,1969f4d5,2ec6cb15) +,S(e7a314be,fc4af863,e7fe6713,5304ab81,c9fa32dd,80fa9b41,d8577ec2,8f7eb925,cd978600,f0e04acf,fa99b6d0,7da970c1,13ec94ce,c50808fb,91a44f4,80f3eb1f) +,S(179c3be4,9f41902e,e21cd8e8,10f0dfc8,ba5fc0ad,9e454231,ec090263,d3c6a577,75f9f60e,fb9b9aa0,29eb03f5,a50b7e4e,64021689,a4e889dc,5b9ff0cb,4c9cf184) +,S(10a55871,464bc182,b957808e,25754fa5,8d76269,5cf8c6f,e50610b0,91e6401f,d36493b9,6d2bda2c,e305c679,f906ba9e,99a787f2,aa8cb8b3,29516a3,41b56ff4) +,S(4a903722,5e858c84,15508d45,b31ec7f6,585827a2,f3a9c99f,96af3e9a,e88e555e,da4d45a7,fb9e44d0,cc52463f,20fcde14,31c9a477,d02c464c,70c7d132,3247d145) +,S(6d109d8c,2505808f,7b4052d6,b170ec80,c68373bb,e02f2b62,1cd29773,a96acb3e,c8dea0de,511f6e40,5141088e,cb023bff,200c870b,9cb952ad,c5038b28,eadc9a57) +,S(9c219898,6d202467,c055c734,73557505,6105b3e,2874dbf2,8f7f0c39,66e1af88,8637496c,8eddff12,f9dd4146,36565e0f,efddf5cd,52d48455,ec9fb2d,8dd0914a) +#endif +#if WINDOW_G > 13 +,S(22ca5039,e660f60,1036dd75,2bb973b,dcd104b5,dcb8f2e7,4de8edc6,c292b03a,86fd4471,1ef6b81e,4416449a,708fa0c9,cb6731ba,c403e58e,da5e915f,646b11e8) +,S(cfc18f02,cc004640,f2116fdd,1f6ca202,2e39be25,df75e27c,80bde842,e6b6f938,d62b58df,4f79d0e5,97ba2923,f708cbe5,c09236a4,d9d01398,6451d684,6290df7d) +,S(3388bcc2,3425458e,991f46ca,6235c50a,57490556,becbaf25,9d3c14f9,9a80a087,7d4aa2f8,6f65783,1c22316,6958fbf3,6c3dca5,d4ac413b,3102e13e,a645c016) +,S(d26bd013,1b8c12de,3dd8fefc,136d9f95,44ac963d,4cbcbaa8,2ba941e,f0b46a19,66f89ea3,31b6795c,6b694872,4cab492a,d045a7d,a6780653,cb10ac25,b68d3a99) +,S(dcd3370c,db67e8f2,163960ed,fef5f66b,465a03d4,d447f5ca,1d99d29e,57d1fdb9,8b93747a,320abf87,2cf8d448,393bf732,8a9eb4bd,9b64ded9,922616a7,347084ef) +,S(74074d05,e602ad56,337105af,59c63819,21d449bb,3fe0806f,80589f3f,4f8d8e7a,1a4d4bd4,adee80ad,44fd515,a1dd5236,bf0dd8f6,c44782cf,e19b8414,b03d574d) +,S(a89cc81,f3a23ebb,6a8df438,a78092a9,97120394,d6bb4dd,732373a8,2f05c174,4b6f83dd,676df063,4e5ddc9f,db979dec,326a9ce7,5707fd77,873a9644,a3c4fda4) +,S(8f1a10ba,fa913f0e,1902ae36,b07f964a,6443c540,b60ace03,7dda380f,6616424c,1028d644,45c177c3,24416ad4,e740f31d,14e3a462,d5a0326e,86998555,5de5525f) +,S(ad8fda79,14f9a236,a7957268,ada499ad,9154a2f8,478fa6d6,f4c74048,89e27b51,9ca1d2ab,1f7da6c6,f76deffc,5c3c933,703b74f0,26063834,6174e4be,320e82ae) +,S(6c6e990a,797b2970,4408cc24,6f238e2f,2192700f,e4fb3c87,4d15e32c,fd67fbe7,1ffc1798,f355659,9fa5b32c,c040da31,39811b87,93d4d9e1,be83061a,3f91dac1) +,S(202b13ea,31ad2abd,9635351d,e09f237b,9f86f75c,a340a47c,edc1f9a3,25118969,c52fb505,a14e37f4,d5111184,ebc81873,868a4521,700004c0,fe3cc535,181bcba0) +,S(5355f0a1,25142fa,b0dc7c7c,f9855953,34053c6,12326f5f,d2284291,d7b748a5,75bbe36a,553501f6,c1c8ff9d,e60ee3ef,9cddd36a,781c518f,4b1ccf17,7e281949) +,S(b8852d78,eacbdec9,d1cf1664,4a83aec2,79a39ab1,34706235,f913aeac,c29829d7,763bab50,52b3c61f,ae42735d,2e89ec3b,8d51eed0,f729e0fa,eb431613,df183156) +,S(cab2e44b,294df78e,1c17d4d6,a09ecd0d,696d18e8,b0f188b,eecced5b,7057ae2,f351b09a,501a499a,ced49607,c55b506e,de28bfad,5b20ed5,4059a068,19ff2f43) +,S(1400b9c9,9c6d555a,2bd2355e,b54b756b,b16f9f36,b9b86658,9d46853c,57056b31,8b290816,62f41167,a0a6993b,3d4211df,5709b91f,96f24436,e569c32a,dfa077a8) +,S(52ae372e,973e9c1c,687ae7a9,a111446b,b3d31b19,5588d8a5,fbec6e6,bfd9141e,f982b40d,e6d0e42d,b9638721,a4590db4,1f87cfcc,d8ece56b,71ebfe0d,82351ccb) +,S(3d14ffbd,40824625,be241e14,850dc030,1139b708,b937f2e0,fe5f65db,61899c24,b10a64fa,477ee047,4d9cf16b,8d2213a2,799882ef,d3766872,981fc761,88a1dfb7) +,S(c6f52adf,54e0c197,a8bbc270,d4c987b,e7ab3fd1,a95fe46e,82415ecd,baa48930,7524c06f,b53873b4,9050281c,99fa44b,b9ef9193,2adeba8f,77e5afd9,3856811d) +,S(90089264,bdc47cb6,7a3c838a,4c79c1c0,5f2d1dd8,6d18f55,9fe60a3e,c944c1aa,18963846,d70b3226,6a9d582c,c430e4b6,ca140bcb,91de7064,eea0fb39,d2ee04de) +,S(e27c24ea,478b37c1,6275b2d4,ce8212a5,2db74166,96f3c6eb,c1bf7091,efda0662,27ec5279,c3a267cb,fad532bb,5031791b,bb99f4da,fc3b7b7d,8537cf09,12783de3) +,S(ca5daa12,9cf1d6eb,70025d85,7955d3bf,2549b8ee,6064092b,917eff84,c40a3eb2,682d4905,500f6f7d,33e7c7c2,7d420b4b,7c4aa5f2,77b89e9e,f6a84259,de7a9811) +,S(9069ec0f,2b2ac688,46623fb5,c89d1424,6c98ac5b,4250c170,4871699e,fc877f55,cc500ec4,eff83712,16c0617e,e2407dfe,a6c3c3a8,c8b80266,975d3a56,e71fb05e) +,S(5fe1feeb,bd5ee1b,6ba742d3,c5e8aba0,536393c,592650f,b8cb1391,c3bda16c,db11b327,1ac64ed,3425ba06,8072f76f,ea02ae6a,4375daf5,3bf942b7,9865a34b) +,S(a4a91a5,f168f883,49035b6c,f90e4e0a,6bf2602a,85e0ccb8,31866c4a,ac1b2c34,5bfeea8,fe36ed1d,18af2ff8,e2b898aa,ff7265a9,c08f4857,ce082030,7f837596) +,S(f4135f36,f0c35b52,4a870505,4eaabe4d,43f5366e,d3c7d9e3,5f06f746,fa884849,1c6707f1,b8b6c078,b4d9d0ea,43075d75,ba1a5d30,c3a9c52d,d55fc4b2,650c526b) +,S(d5a8998a,9363fbae,d2b44992,883e84b8,14a504d,92b41e0f,dcc3a9f3,94380203,96016b03,6fddc805,8bb8ae7c,6d46cd7f,2f20c5af,a62abd4b,bf9b3da,ea60e9fb) +,S(8804ca34,4b30dbd,a07fb62,210c1938,d50297e7,9910dfa8,cc38b9be,4bf3d176,5b64bee5,79927c63,7788c55f,f3871631,eb65db50,b11cac91,24caede7,57d37b0d) +,S(8691c1fc,440c21b6,bc48e78e,78bda5a5,c947c74,7d098e74,ac5dbf6,2b811021,fd8ec1c5,35f51181,556c330a,f02a7110,682f4539,f66c1c09,3d48bca,d7b1787e) +,S(445fc1f6,343be2e1,fdebfe3f,894e9293,c3ee5fab,b0ed01eb,58a40637,ef093229,857f17fe,6737f853,d04aa1b7,3eded13b,c3c087f3,48da90af,32907bdb,7d85b605) +,S(5c397fde,a657d1b0,b2e3aaf9,5b88c4d6,af1a5555,a1dcf966,7d1c5001,a76493b4,9ce74820,7b6f3c75,2d736aee,6a9fc54f,ce5cb52c,f487fd32,de79a06f,df0aff0a) +,S(9b5565cb,28d7e028,361d2bc5,bdfe1912,d9f53584,372d30c1,d7e36fa4,c7fc1b7e,7d5cfa8d,2de2a832,58a57f05,4e3f6c6d,a23af075,d98b062d,82799b11,63879eea) +,S(afbdec63,1bbf870d,3971b165,71bb195a,a5229a9f,1012bb4b,89654da3,c9194f1d,cfcba749,b9492525,f9874ad1,bbb0a267,83fe5714,1f54316a,773c2453,8f27e296) +,S(7712abcb,ca3a5347,5cbc6160,5e9e9ec7,a7cc11c6,2c4b3f4d,e01d688,acad916b,7b91c9bc,eb12555,ad2b43af,8c3bd332,b57cbbfd,1ffefead,c68f0a2,6a4b82fb) +,S(3c452346,f2935e41,2e5fe506,fdf5066b,11b43a11,66e75c9f,7a2d38b,1ca2b7e2,fd3f6de1,27f96829,3af532f8,d4811fd9,e02038a,352b6696,2c608c7f,f59cd800) +,S(bd8582f,b4dbda3f,c7da53d6,70c994b8,e04eda44,ddf0b054,7d182db0,aa48b7be,799e2769,c18268bb,8c16b282,ee7d0828,ef0dc4b7,9767033a,d6de8c63,45285c68) +,S(c41010a7,8dd9dd40,62bfb71c,1691338f,befe03ea,d99eb719,74dce6cf,b2c84112,5008c39d,e71ebc22,3b1c5dbc,f824a63c,f270f807,3143e08d,29b162da,42a2ff83) +,S(e89e807e,6cf4ba27,6586a6,a509bc8f,f786bc5d,dedb5f4a,1b6253cc,2521d311,aadecb79,7c4948de,9379fcfc,4c040029,2228f9c1,c099117a,ad6d9949,f2fea2cc) +,S(363df1b3,be2dbf90,64137919,51218c34,84091f73,866f3fdb,dca1b90f,dcb08577,aefd04e,5634e36c,49621493,f7e76f20,f9ea5b64,6309a605,7e3bdf0f,75848bae) +,S(5c8d1638,83d2824e,48121322,85cc44bb,ca8fe33a,34d5c3be,cd4a3c8b,c78ea16e,f66f91d9,c7ce66c0,89500591,49f7525a,355347df,8f31a685,872aebae,1ac0456f) +,S(10ac6676,83583ed,d837789e,ed9254da,4181dd58,be75dff4,8085e87,eacbe126,6d3eaade,23f3e7bb,7465acb,c6b25af,72e0dba0,1815ea1a,1f57ebe,bb17d22d) +,S(1e79a1ea,7bb65540,44567796,2208ea3e,974c4c0f,9b61f7da,8dba697a,8c153048,b63e3d79,20a34888,68987406,4a8008eb,e07d35be,33a055dd,394ef2a5,f0787fc4) +,S(db0d1c73,97b0a23f,508df2ab,b00ff13e,11699118,11d6476a,7bebcd09,53fa9a5b,1c9d0877,3ca74f1e,d1972431,2b5c8eef,2f85fd41,6629cdc9,d0b9e328,4900dfe8) +,S(e1fdd7b3,481ece8d,266bdc22,92e775bf,96904223,e620f622,3a03ed,cb385f27,3cfab39f,28953ff7,d6dee65,83fdc6a7,d9b31f93,e2103e6d,9e4066a,970a4c77) +,S(96797134,2b69bb8f,3d6701c9,22130c8f,9ed85fe5,bf189880,3b2df11a,a223ffd3,2d43343f,630b6f9f,7c4b6d93,cd29a3e6,632c0dc6,c095794e,5997e47,e631d31) +,S(dfb40218,6e27d494,c69d2a0f,399dd480,ec0a776d,9a99fba8,cba81417,d9c33efc,332d91dc,e9f93273,15e45bcb,79603050,9bd8501c,17820f48,61f7b12b,5d37796e) +,S(6b65c5eb,ed13dca4,51608da7,b5c1a331,171de004,7fbe35e5,189b8468,c27681ec,afd0ee8,e0aebc0b,2f246b51,f6fd4f85,3e71fc4d,f5bb9f43,e2679c8e,89a34f62) +,S(bc1c3da1,60b03965,e26396aa,5d426df3,21e5c22,fdfcd004,80a390f5,28c4b619,fd657174,65e4fea9,ad7862c,422c11b,86ad94cd,35acbbc8,76ed464,aca31640) +,S(589b1d17,96d6ca35,53f8a6a8,cbc587e1,9026406,c2c6f8e2,de613dfa,8c05913a,e655d5ec,ac254f46,e279365d,e3bba2a4,3c7b6469,da45ec87,9742cbae,503fb3e0) +,S(9e726385,6d1e6c2a,94dc2ef5,c92d94bd,6ada63cf,cea75677,6125bf98,78ae4ed5,8c238df5,1e5b38f8,b628a48e,dc7aa2b,a83bc40,43c91896,69f5341,60e304dd) +,S(12269d78,14e0f74f,ba40c551,b2080e00,4f9bd3af,255218d5,7eab5dbd,c6764263,f0f70bd,6584975c,106febcd,bbd16920,daee58c7,c22dda70,4a95f864,e385b3d6) +,S(d3212f2f,cc509014,e0c48cdd,6273effc,5c73fdbf,1676faa3,15e9173b,573088a6,39af39f1,673f51f,362618c8,49c6934c,ff59b6ea,853fafc0,9db64084,4832df82) +,S(c535f72b,9bcc0bad,44014e72,4f649623,4ef288fd,9f42adcc,dae45a20,84250052,3f79985b,6fb7631b,62814a17,f7ce829a,88447302,293000a7,4655c7ee,4a271022) +,S(4f5b7126,9b169fa5,8602bb18,60ed0df7,271e3912,ec0d7c12,1c80fad3,93399605,b60fc104,7f2a58ea,9102c6cd,b97c932a,576f63f2,6204fb5,dbe41363,cdcfd0a7) +,S(cb459b3b,244c38b1,af2c2863,e0029c14,ec70e7d,737a7851,de9c8f1b,b7d5d065,7b67b15d,3c0376c8,2f646241,1e890b3a,a888bd15,692c90ea,53a74a4f,1957fbd0) +,S(787d61c7,ebde0c5,18f121bb,7f434196,d389f563,d1a46597,ea72a1fa,4e19054e,5baf56f2,5fcf8cb8,c271cf39,6b3d150b,f42e9ebb,d72b54ba,4b93bcbe,ae72a658) +,S(c38afc2a,31d0b0c9,4b77d97c,bd639082,3373538a,602352ba,cc93b6eb,c5f6115c,b925dfca,b138fcc2,4b9a26a4,c645f8d5,875e098b,d2e58469,d842688,43ee529f) +,S(37661e3b,8e606e95,b9dc6ad5,e5b9b25f,39a5dd0b,8ab7a82a,d6ec879a,ff406d0a,ab735e5d,430d8fce,1317e07b,d6cf9441,60d8bc39,132382a4,df1b1638,a7e9b977) +,S(e5127851,b8c22b47,c3d8e934,8dc0c677,88ea5ad9,f64e6062,abd68a16,51e256ba,5847f77e,349eb7,90b4b7dc,5537ffa,5b1e7d4d,b87044b3,f947f387,270405c6) +,S(f3c7f45d,a6b8a8a1,3713e7cb,7380fe06,127a3698,520bb41,955647b9,ad7382e9,2acf9a11,eb1b9d31,f71decd0,90093677,4b469301,719d4a51,ddfe5fbd,f63e7c87) +,S(86de4047,e8d5b252,523733a1,848a990a,79125f95,3bb0e4de,ff4b2f0,84d745ba,71e6a472,dac005d,2b284cf7,d5de92d3,56921105,1a9ee007,4279a746,71bc969a) +,S(c6115c30,72259d66,d960cd96,3f3aa16a,3029d9ca,c0c5ec63,5e2875c2,f3f57504,18d690d0,b33fdd78,d797f56c,23c821a1,ced02401,3443c770,50d12850,9f7a712f) +,S(cb545099,8b43473b,91838cbd,76929b05,8bfae0f1,e2bfbc6a,c61d3674,42f53c5b,4119d74d,79e34f08,cd7684b4,f9b9df55,c00ba8ba,7bfb4ddf,de38db78,91572bbf) +,S(d0dce704,b9362bb4,fb1771e2,660a98a2,a333b73,c6d09372,5a752f28,52922d2a,f92c3b70,7947ce0d,f1f3891a,259cc2ee,124d3f54,a4c518ff,de5a3915,96e37e47) +,S(db93e238,e5aa5986,38eab857,655cc53,d4270259,e7d73a96,249a19bf,216b20a4,8d19dc8b,670e8eb6,50b4f60c,d26ef657,3ab17895,c62fc94c,815d7eb5,fbbcc7c5) +,S(c387c37f,d8cebef8,29e07e54,b0796129,4d6c6cfc,bbf11835,ba85d4ef,68c3f486,852580d9,ae7eb300,6ca0f7d7,f12d7fe7,c8e3ae0d,b6258897,994fe78e,11ddeda9) +,S(ebb58d34,f04de551,37c01b5b,192bb4b6,4d3b22dd,23ad9092,f943749c,7606c232,1b979d05,9c99f9d4,db1d3e82,905abc9b,d92ae72c,509c912b,c59488fc,ddd1ae16) +,S(7891cc66,acfda29b,765810b1,730cad13,2f13178a,fbf238d8,2525ce42,96e38e6b,22193729,20188c6c,462987ad,7ecaec3,a21ff6e7,db00adb9,72facf65,367caaf1) +,S(c0b02542,e4bc1e8d,2c2578a5,3793fd8c,acec800,c0a311f4,72c50de3,5814d081,24bdc71,f5917c0,1ae6bb5,71c09197,76b1a884,ebffd673,13698bc1,b57f8371) +,S(ea15d574,b8704507,f12fd9a3,e40ea603,c2000cf4,4eb3d4b2,f07a36b5,c426d2dd,b6596d4f,4d8ca39f,4e0faf58,ab7e3894,c5cdc16c,589a524b,6ca25a51,8a30944e) +,S(77d387df,c35a6af7,b88d5726,a249d34e,70877698,1358c672,6e51b779,cf9604de,e5daeecd,2d70a962,d2d3c235,14fa2b1d,45916506,de017249,93fcd9a4,f08b4a87) +,S(a92742cb,7d1a24fb,c1a4dc2f,9d6fefbb,1064f9d4,c3152db8,3296a9dc,9dccd7ec,665a9cf9,8cdaf0a4,ec346df6,823f3c6,66d4f163,b3e7225a,d9d18e42,31679f76) +,S(253e54f7,9f0c393d,5d34053d,37b55304,f62de9da,29e0eb6f,36cb105f,a6316a46,54e168cd,ed78ec0c,32a9417a,c5b27ef0,67c8577f,8fe0bfd7,b2ae33eb,ce7155e0) +,S(d13048e0,e9c38720,6179586f,9e38029a,55fc01c7,b33d8443,4ee45cc0,3fbf2,7c4466db,94502477,85490689,7b251c39,af257840,b9bc61e3,64075080,84743f37) +,S(e9234110,387c2122,2887d079,7bdbc9aa,a632b237,2a5632c8,8e604653,70a284a2,2b98b08c,4ca3521b,4ffb3afe,66f98ed9,839a2651,f740f63a,e80b0cfb,6f6523c4) +,S(302e03a7,ab6978ef,73891a5a,7bb25764,7428341c,94f8a713,32ba9dad,83a29be5,4abe0adb,210c35be,b2ee850d,2e56bb3f,e8430db,644082ee,a98a2f83,5710120b) +,S(53701389,44400f30,dca0571e,eb0f8c7a,4fea8bdc,31d68859,8d7cd156,11665368,ca345664,39c1f221,a5731bbe,5a9a36e1,a712e2db,f16a6d19,71b1a74f,eaa97cbe) +,S(8c0e8186,645d7cb8,fad2b6f7,a5a2d399,656aaadd,5423cff4,9579529a,f28383cd,2ef275ea,14c44981,9307319d,18fbb482,63c6d24e,c94e18ff,cdcd7757,4c48d27f) +,S(64a70831,26baa3c9,a11615c4,5ff8ca99,adddc512,c697901d,afc43d7a,5fad8af5,ba43c48b,59a65f6a,c3a45c35,4bb17968,148efe4a,288f742c,43253e70,aafe29d5) +,S(fdbd066e,82ff8e24,b2f785e2,e166245f,63559acf,b4f56680,51db47aa,5d438e06,60dadcf,a19ed8c2,376a5d38,28b9499e,875731d5,b2417def,144e2a84,12973d63) +,S(b287ccb8,1ff90731,19ddba01,d9461512,82cf15cf,2dcb3e0a,f1b26f41,eead174f,6ff15339,7a9a0bee,4b38a463,5db66ed2,36d1beab,c0039bd1,acfbdc09,fb74e090) +,S(18047a48,b65234a4,fa5f2188,8b3032fb,3b3f8457,1bdec3c,962d5cd7,fd068116,13627669,18198832,306c5444,6cea5681,80134209,101a117b,1f34e15a,624574b) +,S(f1d3b54f,be4e2ce5,88a2dd32,8a41e136,6b74ffe9,5bd4aaae,ce7e7a8,235f914,37c25d41,300e817e,b4ae1471,e8486232,c0c21165,97efe33b,e86d6277,a8a3981a) +,S(9b0da9e9,5046dfab,7ff509a6,10ad56d,9770da36,c5e22a83,9ce3a36d,853bec4a,27d60345,30c25a36,e80bbc3f,f43829cf,df757c19,1a2924d9,6da77503,38135faa) +,S(d0833bd9,5155d18b,be55d40,3717568e,2d3e4d24,f2eb65f9,f13d8d65,d899c5cc,e509aec3,b5044ab5,11fb1e4c,56db7146,ffeefb82,ec4e9de1,3c473bc9,964741e8) +,S(8c41627c,185f17e4,28a01b40,27980e6b,ed62ad62,17c9f8a6,26c329e2,2fc37117,75ca226c,3316a552,37e8a1e7,483f73ee,a36da441,c732dca7,7d95b7b9,3a3662f7) +,S(371f0105,e43aaa30,5cc7099e,9a13b97c,2d75c26e,5b50b11e,7cc6283b,27728d27,c969dbe7,d0976d68,8cc64312,4538ead,b1194426,cedf149b,293b80c,6ab3ce99) +,S(5102f59a,4238a358,f8b4d3a1,85d864c2,d94aeb83,7a8f3e03,541ffbdc,2ccb1710,9e14c4f9,adb63c3f,4cd30606,cd41cabd,bbffdc3,89e996ce,91522093,85ef1445) +,S(ca21b6c,fd40403c,78353c7,836162f0,a7f57eaf,bd4a8cc2,6df2baa7,1ff08beb,108a3c46,47eba55b,3fd9f2ef,751edb0f,d6b68482,1424bffe,e6ae3e58,1f7f12f9) +,S(d9cd44b3,ff40d19c,e8368bc8,200e3dc9,7ec3642c,acc40769,7c94e68a,31d775d9,b6b6fb6c,683ef701,d4cf7101,79de4af,fd7c4a1b,5babf3cb,85c95eb,485f0901) +,S(9a1354b2,cbcb6888,bb564426,71bd4904,510da508,3fcc61e1,4fbdf44,15a10360,3f5f5291,707c48b4,4da3a2a8,93e24037,bc4927f3,81dd9b9b,710e8b82,874f953a) +,S(c5e55e0f,271a81c4,ae7deb0b,ceb0fdbe,20fc60c5,38692941,bd50ab9d,ad0fdf79,138a0f35,eab49301,7a53e7c,c62ae93a,85ed9e7d,708a00ae,cb10776f,fdc83a63) +,S(5ebc2f64,7f66bd3d,2290e4dc,6cc5f662,7c67ea72,599d2c6,9a004aed,9060f919,68f7dbce,df2085e2,27cf7920,65369637,3ec1e860,b10c8f1e,56e996d6,854659c) +,S(4f28eb9c,7d060dd0,5051a5a7,9a79c960,161d7081,d3a58b2d,a421075c,cf966e03,ec378861,5434741,bb2aeb8e,4a2afe78,44849f3d,2cf12a6c,2eff3e05,d267fafc) +,S(f50e20d1,ce113e09,850c7a9a,10b347c9,9fa039ee,57ec85c0,efe15b4a,1a83fe8f,4db7c231,90df41a2,8218595f,61407f20,cee46478,27d54aa9,4db9bfc3,fae975be) +,S(cce6b2d0,1adeab95,dd78cdf2,b55882bc,79de945c,d4249e76,ab2841a3,abeff5ef,1d690674,1312140a,fdf764bb,560b1bd5,bab1dbaf,9fa26bd5,604427e5,34626e52) +,S(55a65174,fb10557d,2ecc7312,15afcec1,e4830be5,2d34bbbf,bc80ed6e,9f2d8475,c69795f1,de1e537a,970dd412,65493806,3f0623d4,e7353eec,611c8917,278012c0) +,S(78221cfa,7b4db3dc,7e22779e,97ac8d46,d0c1d819,76594ab0,267ed28a,6127f290,8ddcd5ed,fd8e8c62,4883a158,2eab5652,ace0660c,2e358b66,3ad53e90,cbc46a54) +,S(9b985039,441ee434,ed4da39d,6010782c,9006cce2,8f92ff96,2b2d6fa,986ed178,e1cf85c8,7b537b2b,78bf67d2,2d9388c2,8a50dae1,73f003bb,85fe5f9c,12c6cfa4) +,S(9aa2ffb1,4a9e48de,57db9d78,d9b6757a,6c27e7b0,291dc4f3,8ae9f204,f7edf20,771c7be3,78e0f794,fe0066ef,8592952d,8f80d7d5,2772ca11,5ff8b7f6,377d1040) +,S(d1059083,66a0b463,17c510cb,40922a0c,949de251,6d80ec02,1e359a89,c6937e1f,84053a25,c9ea4414,a897155c,c45e0da,e5e6f2f,aec780ac,99c41545,fac8630e) +,S(1f3ecacf,7456984a,fedc2d81,2a81f7f,1ac58ad9,c70ba7c2,3e7a2ff1,f9c5d066,c6add8fa,2bfd24f1,73f4539e,f21681f8,15f1f362,e0f98d94,cd435d80,71a23e54) +,S(a312c709,a16d0cad,25551ece,86c8f947,5e504d06,eb3524fe,569cdca0,da2c4371,865ecc2a,ea04c5da,2089ea2c,66b5bb84,8c1fc29,ac7bf969,c778039,b3ae7b03) +,S(f21c4675,e829ba78,fbcf6410,9c2eed38,8c3493db,1017d69d,953f32f6,744197af,fd2c673e,43377caf,5db0ee06,1a37b03d,43d9373,482d3ef2,b37f74ad,98b2f57) +,S(116174ea,2a029a02,fbcb2eb6,1851ab31,f4836933,a73bd426,8feed7c0,18865692,2123d027,e1cead9d,b5fca2a2,d6f5d1d5,25cf3855,44b64352,ee772d4d,d70aaefd) +,S(358e77d0,919e4c6c,f64a2117,357e72d6,2334fcd0,f5e7e60a,962335fa,73f9d663,7ef8d620,6f99f403,10781985,988ac7cf,dcf5cdaf,a086a0cb,8b5b6eb6,7837bc08) +,S(509f7e0d,9e81f146,39157e20,3165faac,8056deb3,a813460d,b86c6daa,baa33fa9,55148e1a,340eb6c4,e6a8645f,93f1ad3b,fd12165f,c3bff090,a095b8d2,a0db6ea9) +,S(358e325c,185f0e26,1e9e30c4,9346f21c,a15c95bd,438020b9,ad9c7e6a,d2a4ed09,5fcc074,c97715b2,812ef690,3b3d6185,54aa11bd,34789b3d,97f9b65f,2d18c4b8) +,S(1e1c712d,bc00af2d,37f37e4e,a2589e02,e314c310,47890d26,8d36be3,dda2fc4c,4898f9a,e3136bbe,36798cf5,ad30b38b,974e7e75,42d4c14,5bfb1f6a,490655dc) +,S(a6516f9a,aa3f79b0,ff3b794d,c80a9590,64e31720,7361b918,bd797b57,23ffcd1d,c5934c6b,59f3b830,53f77b07,47c7c312,812373dc,ebdbcf9b,ed550300,3a54f5d4) +,S(e46b48d8,8b1e1b84,b9fab824,3044469,50cb5633,da599b79,295011b8,b1098155,43e9ba24,4a339976,e342e551,25974350,3e7c8a80,cd950a80,a5d79365,b0c94f51) +,S(ca7a5099,97a9c2e0,625105b,1df9e714,e1cc2d40,1227400,e583370d,3f65aa0b,dd8ca4e4,d2eecd1a,20d994fc,ef7cb0ef,19c61844,cd2ce039,b7a1eafe,66e1f071) +,S(326b5ec0,d29b576f,111352d4,b7786c40,8486599e,ec01ed0d,a390d586,96313f08,f4a3f171,a4ac654b,e7aa6063,3d5b2a0d,c2d5d3a0,3da4d264,95e828a8,1011d384) +,S(b762982,54f5c75a,a557038d,bfe9bad9,7c5bf8ee,e512fe2b,39293228,e70d306d,8b808f9f,b36e3e86,2c75bb2d,d7de8be,cc85c75,398496fb,f92db3db,143606c5) +,S(8bd624e1,2b1d36ff,dde50a09,6f1a811b,ae66f1a5,790654f5,ef79a21b,872286ca,6687ea2e,f4538961,b6731c74,744234af,8bf6a2a2,170544d0,fe9b8057,ad1f02df) +,S(ed25eac7,2a9f9290,bd414cc,cf846c65,509d1c33,d5573da4,2d78d25d,5de48a9,556fa9ed,2710fd03,b6d1583c,682cd7bd,8705a9d0,1f73a8b9,8f3a1eb5,d89eaa) +,S(f1943b6c,9ed76a84,9c67bc94,c755fb30,e8facc1,de280060,df4b9e7,8bc02bf,98c7ba08,85dc4e0f,6acbb646,42cb876f,a3156232,df1f84b0,4818ca56,882a40a7) +,S(c5b60b14,2de3ab8f,b0c24276,dcedae7b,83422821,6ac8ef39,1d351832,a5e9e5d3,ce86e4da,1174e609,f55e10d9,14d4a5e,33259578,e07c1260,e912acdf,9eb5cd69) +,S(b06638e,706cfe63,3359647b,253f99e1,c7778200,c168d4f5,92e2c409,493755ab,e6401fab,1b4d5229,136e2493,39f95357,e16dd37b,87e7f69d,3e88a383,cb44bbd5) +,S(cef8c1b5,486dacfd,29650ea5,432fa563,f0ec5fe4,6814bcd8,9f539b7d,264b14cf,80cc2c1,8d0b0be3,939ea,761d7281,5ed4fd2a,b252d66c,9316aac1,1a5a2fcf) +,S(f616904d,f868f2a1,5fcdccac,4198b1c1,62defb40,269754f3,8e546da2,4b85d7ce,fb971012,344024c9,f0bb93e0,b73b31cf,de8b4d37,aaeae3e4,7f62633a,7b8255b3) +,S(97386144,bc7e4a7b,33a51697,902fef4d,1613d8e5,ed80a599,a59f62c3,f98632e3,bd12d584,58429939,2dd834a9,5431af27,8e56065,ba79bb6a,5a806d36,cfc16b4) +,S(946f130c,7a70fa00,f403a56f,6656b279,1eb08ddc,561311b,551ce437,bb0200c8,acd698f6,a5037e60,7f5a923f,3af05784,186d9794,a7bf7105,7a66949,e1945b8d) +,S(ee0cf184,72c453b0,c38c064,fc0d2589,d5f1afc3,3423687d,91740bdc,c3929230,d817775a,7fecd397,e954db12,fca24990,8c773c28,b36e991f,79c78264,9fff8dc8) +,S(f335f7fe,9542a587,9d48b4f,4ab1287f,ef007289,4d93bc21,23463a77,7d535d7b,336e49a3,2465d29,35389c06,d8eafb4d,7639dae9,a6b19bf7,45518502,e3350283) +,S(55bf8179,4fb37f8d,dcc200d7,b76a9040,b1326d6c,559d7f06,f0eb0e0f,c97e7739,ca33cf00,79155cc1,e902ade6,f7d703ed,3053f6a4,f49d0866,32e930a2,afe69bfc) +,S(dffccf81,6143da46,5ee2195f,abb6d943,1033b901,2fe0c623,49b7d6f6,29de29b0,9bfdf3f6,e1dc4ae7,dc29fc06,f85e1588,c04bc39d,5fded90b,bfe75b9c,74f0aff7) +,S(b9748c94,25fc6fa2,fe5c1395,b556bb12,c435bc11,2e8f577f,3c5e7d82,3539dec3,f5238bb2,b995d23b,5e96b233,6d5141ec,f76d31bc,63aa96e3,dfaca8a8,c94007a2) +,S(92c43c54,e951b157,38f29e33,5f52ad9a,2c5349e5,fa6a39a3,fb615878,ae97f7ca,7c7a6b73,dedd1bca,308924fc,6364b62a,bdc4ae79,d68a62bf,71bb0195,612b4a17) +,S(b1937ea,25995e55,ee1ef139,d7a58923,5677afe9,f4726d96,40b66b0,d21eb272,d0f95a57,544ac7cf,e6c3e0ca,5e44494c,5585c503,a8ea9919,5a993180,cf043dd6) +,S(2c1cd64d,bd7ee24b,2bc5a6b7,2009b788,e2a5047a,8d10dfc9,4a46d807,c133f457,fe8a63df,ce7d7a4b,e7c31a1f,5e68826,7a9449fa,9e9ce30c,fd8684b7,660aabe3) +,S(4ff10a78,be6ea8a2,b6830649,b0a403d6,6d544368,6cc854ed,447f53fc,45004834,b3cb30c3,56d50596,2e81cf5b,f4e2db0d,e43384d7,28f5d8bd,b362d287,e58765d8) +,S(33f55b66,6c90665a,b60a7c2a,c714591,f877e8c8,517344d,9da03558,f43d28de,399848ab,6aa95ed5,8ae0ea80,c93931a2,af146754,385edce0,8c2a4f64,2d5163f6) +,S(b9712359,5a7237eb,18b7cc15,ff2d6be,408da66d,5885a636,20887ab,340d3e04,a283a3bd,643ac5a4,1b3f0ab8,fc693146,dd125f88,fa87cead,817788b3,be72363a) +,S(d20f9067,6efa9435,f602c1e9,fff7338f,5a4204a5,4d984dcc,f0ba5fc1,60a1035b,ac3a3c7a,452003f8,7244406c,ea315175,ca1af26f,6645486f,3faae77b,8f404139) +,S(322d6ea3,157c9f0d,444b2d0b,d072e192,81d8b3e0,3ee2ef5,4e294b53,fef41f2d,838147f1,9dc12dde,676a9e67,6a638c42,6e096df6,ea62ba97,ec359b4d,f92936b3) +,S(95925c08,68341b6,b7564ea7,bae00b0d,1d2bfacb,94aae4cf,8f29ed9e,fa0c26d5,c63edb9,13152232,c255557b,2207c2ec,edf14cfc,bdd246d0,2b46f7f8,76b92130) +,S(7592aab5,d43618dd,a13fba71,e3993cd7,517a712d,3da49664,c06ee1bd,3d1f70af,554ee877,af74284d,5ac0aef1,ccfa8ab2,7a9222ae,977a1b45,7d79d386,16eaa410) +,S(ed9d298b,e13eb71f,8631ad31,1b391680,8062574a,6053f503,671d9a59,1209e0eb,97a9abc0,dab8886d,9a07caa3,f40d87d,7b479efc,d500eb6f,e54ba1b8,9d85c3cb) +,S(cdfef636,a5900bc3,bd28daf3,308d469b,78ae69a2,ffc39fcb,9f8e4942,fa355fad,f8338fcc,54c0ef4e,a46a1c25,591607aa,f4f6c7c5,378f2d51,79f80e46,520db6d7) +,S(f638cf22,ca3bd6a,ca7a8fc4,26d9f5db,dbc0943d,6a587584,8ca52e6a,7949db49,56a267dd,8309eab7,90b49003,9d595141,653a7926,9fd1f732,f3691e0a,41675295) +,S(4340fef2,9b1e43f5,3c76cab,163920b3,7a66163a,bc942e1e,6387a2b1,8f14bd1c,2d5a782d,6889d353,c6c908f0,b6f7a9dd,59ff2f15,8bbb9eb6,ad62bc2b,832210a0) +,S(78af8680,5e3db13,cd2c01f,beeb826c,1aa83296,c6ed1a30,bf2822f1,a9b80991,19c7ca36,37a6ce91,2680d95b,6b94f835,ec458cdd,86206a2a,49d41af6,248c3725) +,S(d87e590f,e6a4bef7,54a831c0,13b3d561,72064279,38b96d49,58a2ae11,c9c41ef4,3928a93a,c9b43489,f1fff97f,f8ed4ebf,53b97aa1,b50896b3,e30b40b9,be1d1dc6) +,S(b59d19f3,69c547e9,4155cfd0,a0b9076e,85ea8569,195b8a7f,630a8446,ddc54c32,b8f5588a,ffe7abe0,e2b83d55,28162c5f,2d50827e,c44a4357,f81ff085,da661865) +,S(34f643da,7cd59b00,3aa64cd,81c143ab,662726bd,5eaf543a,6d83d30c,60f3ee78,41574cf2,91a7f573,5a0bd29b,3ee5a36,91906c1e,3fa47b22,d5204446,f4501ee8) +,S(56ef37d0,583e9de4,33f78757,dc31c968,fd007bf5,6b05db4e,cac395d6,233dbbf3,a140e01a,b5c898f9,10ae2807,53ba14ea,ba8cb5df,b9e9629a,92c14b9a,df5c9286) +,S(1e43f34,815b568b,1139ca5c,b3a5dd42,fc54dd76,78454fc8,7e3dd00a,edc957d7,5cc38274,ac96ee20,78a82d9e,64dc2bdc,8e947afa,d043302c,85d724f7,7e334cc8) +,S(ce79e49,35d2db7c,d9a9046d,3d81b7dd,f8b06c82,98f78eab,ad93062f,c339a952,fc2f6eee,1b90626a,5ad8c494,3bc3cf5b,8765c8f5,fdc05dc7,fffa08a1,b84f9686) +,S(b4144804,ca862c26,70cc3365,e1a00cf8,f6766cc3,4b3dce2c,76c40c31,86fe4fd9,e4368a1f,ca9b6e00,a3f752f0,3a00fd4,29cbdaab,5e2a04b8,7175b9ad,51b2219c) +,S(eaa29724,7406f824,8e79cf65,ced49d39,5416341e,c1bc46a7,b71d422,1f4fd39f,68e7f81b,e3b75cc3,de4d9932,85e21842,d0d4b6cf,38a1d46,248dd28b,a97c14a6) +,S(4e2d10ff,1f7288bc,3b8c6caa,3dcbfc72,eb7371c9,f42c6999,e7a2ec50,30f5b6a5,68da3c5c,df2a4fb0,a93515ca,3e417d00,6ba793f5,eb678f82,be88200f,a5a8774) +,S(834f21e1,114dec96,2a8a4b45,c67c2894,7d073b94,457c1adc,e009dda2,230659f1,e9fa61b6,edb5a0a0,2f7acb27,d04a67c3,39263adb,6320ca6e,f78ac7fb,25adc89c) +,S(a84b44ac,a0a64455,a97268d7,cd3d68fd,c43caed9,f9bdc381,ece7139e,30c7d0c7,2f903f3b,74e702bb,d2fbde43,f919af7b,d1267879,3882cd6d,a05af259,bbf6b74) +,S(21219e06,98bcb977,47b1df66,f9e0fa6e,600cbe7c,bb432cbd,d413481f,241d6789,7a4388d3,c8961874,a897d968,8b5d3266,2e6970be,324bb736,aefd1892,70954055) +,S(fa514e1f,93bfccf6,88b4bbdb,e4f49879,7b946549,82a27562,6867b22f,9baeafd4,ccd48215,52572b4a,9e087b51,8c930e09,2c2f6156,c26a730a,478f257d,a4f91b74) +,S(b60afbf1,643099f0,c0533a4a,99bd8c2d,2204d4f5,3f2fdffa,4cafa02c,79451ffb,fed8f90,1a029926,cd4b3bd2,76bda6e9,d321e006,3eef3f43,886eef8b,4b21294b) +,S(f29cf9a3,196ce34a,86efcca6,554694e8,17c2b765,39c6cdef,c74850e8,a7ed76cd,5130863a,f7c333cf,b37f1bf6,2db5bacc,994b2977,9951e8f6,2471129f,88c9c5f7) +,S(9cd9e23a,cb00bfc5,1a3af34b,3e23f1d,d8fe314a,322254ed,563b9275,c5084e4d,ecf0235c,c66ea710,151d6c1a,17b31705,4f31bd4c,f8d2a77d,4d36fc4e,1298e1d5) +,S(56f889b0,2cfa048a,f96d99a6,be56a282,18ed6691,ab2b25bf,f3d3b6f5,f6e5d65c,352b20ea,4a2372af,f674f824,8dc8fca1,bb8ecc29,ea0c8d92,1b7ea07b,241fe4bc) +,S(de32beb7,f8fad8fc,16d69a7d,d3def556,8e2366e5,246bfdea,16c936ab,5dff2a08,d731f11c,93a71c22,46c2bd06,fe265fab,eaa216cc,b0de1acb,a7c004d4,249e145f) +,S(85ec7dc1,dc1729d8,c03dfa58,fefaacdb,7d12dc3a,f984a693,968895e,2a6ba256,523038af,8e9aabcb,f98c73b5,afba31dd,89e4b9f2,ba1a841b,194b0b45,9a4a747c) +,S(f7948995,60528ec1,ed7f5efe,42db8bf6,e223280e,2b26d4ab,e23a5caf,41e2e141,9f7594e2,96ed1f98,86837e9f,da8d6d9e,58871e56,16cdd2fc,dd6fcb7b,80c6ff98) +,S(588d6fd3,b4208825,3d76e8ee,f01a93be,866bd53a,e228c137,62199ce0,f3454f10,a5fc9653,3e41208b,ba3f29ad,e31b7e32,6320b81,ad0ded2c,6d922a4b,b42af573) +,S(186fe08d,bbe5c8a7,56f87d4e,8976777b,b01db294,e834659c,bf423de,1ccc7443,d99ded39,a46f7820,3437a93a,435fd0ca,29f12e26,a6449bdc,a256f99e,9bdfbdc0) +,S(e549cb08,3055abf3,3b7eac38,cee13336,2af22e98,2f576bb,5a06d5de,59bd3d25,cdf37f20,e7b51483,6641c7f7,afc35ae5,5ababde4,34a945fd,4fe0325c,7d332381) +,S(a4e42afb,fd7f5158,5c86108f,cfe84b58,b930df79,f53a86e1,8ac93389,224422b,e9f8e1b,3963102,b58d6177,f915d7ac,541e551c,bbea3e2f,1ea47960,ce1e30) +,S(5f7ed844,42022f42,6b09e9cf,b7750fa4,4755fba9,7199fd4e,d09efdca,d803dadb,91d5ded,d06a8eb0,9c9592fc,c9096f1c,e852d9c3,9bfcdadd,ccbe1bdd,2b76ffad) +,S(3fac4743,32f064e1,b33fa22,3f67bd15,91dcf7f9,afa38f1,868e180e,c2938d66,45744e67,6b203799,3386d10a,b11730bb,eebf270f,9ea65920,6f93da61,e2aabe5c) +,S(5fb983ff,f058d214,2e4d13df,97d8e784,d4b10e0f,6a18c6b1,90e64bdf,fb7a7aa9,33394601,8dc540cd,eb73f73,a9733d74,ec581181,8a6d58e8,3460f1a6,8ddabd6f) +,S(baf35c3,fa939a2,db15eec1,ab083664,9c6e941e,19d3e0e0,43e0f5e6,df41f88a,42583cd3,e53fa1bc,4eebdc3f,e3b8b069,9de41445,54fed966,214108d2,f68e9201) +,S(97641f5a,a51e7ff0,f1d86183,a3fb3d60,4266c4d9,acdb9f9a,c704a05,809a8602,134ffa32,aa9fbdbe,d8d82fc9,1cfa78f,1ab169fd,459da39b,5b0d3287,ad9517dd) +,S(96a3711e,acdaf6dc,5adaa8fb,4be05c37,f59febca,1a876a5e,e23c6b55,2727292e,8fb0cfb9,681ebb7b,5e1fce0e,67ad5d81,34be9123,56100e96,b82ecd3f,a5da4392) +,S(ae64428b,8e540c6d,28d38892,6136320a,9f9f16b0,3e9d5e2a,14faadef,4f154b0a,db202e5e,b9bf73cd,b9ed5193,b0ff59e7,cc68aded,6fe6c2e4,d5b79493,fc118aeb) +,S(2c5932df,65c0c2c1,14b532cf,15b48433,e16424ff,ba87438d,42229075,6cc984fb,161ef8c4,b908a056,d395deec,38b9ff4d,1c643628,2ceb1e47,f59a2c24,c7d29130) +,S(76e21c78,6937ba69,18252d56,3ebfcbfe,2415389d,bc692071,f9c48f55,7aecca3e,de7786fe,9db47d61,e3b61d0d,49309fe9,ca634d28,8d71aa77,a3fa4efd,4533c8e6) +,S(7170dcef,ca8a138d,1d20b346,6afc6a46,99df363,fc964a36,eaa70805,b267900f,196bfd39,476f1455,98f89f7b,f870f8f8,440562e4,623fb203,3b36d5d,d8c0bb8d) +,S(98eddeb0,75bdd245,aa1fb466,ed1e89f7,61a95662,e35836f2,e6907c5b,691b6ccd,af3fb5f6,b59124b8,c9c2ddc2,108a3a2c,bdbcdc99,ab296814,731a841f,570c91be) +,S(bf42f806,59ae606f,a5e36fc5,c58379c1,8dd1c34e,fa98e67,68bf6da8,ba8e8849,d343fb70,6e4adb94,46f49578,26bae8c4,ca74a0d1,21cee98f,26be68f8,7555b8af) +,S(40c1c4a5,891e8dc6,29075c36,56249fc2,c42fd65c,8c338a0,d82df955,4662e1cd,667223d0,e68ef6c3,7195d819,2bcc12b3,f2025327,3a05703a,e5aa10e9,cc689ec3) +,S(162dd310,c8e3fe4,8384cf1b,1a0a2d6f,bfd3ef3c,5492fc92,b921133e,8f883dc1,1f2311d5,c82f02b9,f7197685,cf490e1c,3d0ebf4e,86ea0e21,a653c8e0,db5cff0) +,S(ff0f8146,89eaae3c,f3f302e5,1089ec58,5d63c4fd,5157bb3d,bdfc7e40,cc6331cb,4dc1fdb9,4eb529e3,bae54973,778d1daa,f8814cdf,5a3b0c2d,ac2b5647,551bf2e) +,S(143ae674,b011b557,2f472b22,44c6d9ab,14b3e641,54f4a033,c3ea3917,4607b78e,1528d582,faefba2,6018820c,a03dfd7,ca20298a,e199c99f,db9cb421,3601c35c) +,S(f3adce28,8fffd45f,96fa0efd,32974d98,4115a80e,cd6b86f3,5e17b017,8d9009a3,20202f00,96329bb0,69a0915e,494a586b,805580a4,eabd4ba7,2d20665b,4916a20e) +,S(d2270829,e2730d0e,2c8e6bc6,fa4c2247,4904ad22,f44fb2f8,5ad07558,c4d8cccf,d5a6b4ac,9d5bf669,5989741d,f262e412,27c66c7a,ae067a93,c6302f5e,8879a3bc) +,S(26d17eb6,7d9bf2a8,45c57a94,82bb2f3d,8347538a,65c9f567,56ecadf4,352964a6,954af3d4,7d67c345,bcfc4a52,a49aa70,b8703d1a,fcd21c6b,7f7719c1,e2068fb2) +,S(e4437f8b,e104c899,5cd1618c,e5ee7d10,36a52d99,31346476,bf68f21,e7fac74b,d6c156d5,b8256e6c,1a820dbe,3b56fc8c,f9b015bd,b81c756e,27b70db8,d995cb81) +,S(8932e04e,96db6079,662b63ed,29b5a39d,ac5bb51c,d1ec70,851474ef,a2d3c1d4,5437e77f,e030225d,53f5166,8c3551c9,fc912a0d,a3bd336e,c0587544,77e58541) +,S(fe776976,465b86c0,5b2003c7,36c8141c,933ed0e4,5c6dfd0,c7c82517,16617b26,b279dc5f,d576d506,d302b0af,359ddd50,94364d3e,255102aa,472e6d9c,c52aac39) +,S(6c60a4ff,b2a29224,5c84ecbe,1633817d,f69eb73c,a6b9f72c,b86fc91c,535b45f1,278dee6f,814bcc1c,70301a51,f9c7853,6489af2b,d23d6223,1b55cd29,2a2a0a2c) +,S(3132e58f,625f902a,98d556c5,ec9e8617,a955beb4,6e9c2a81,e121e473,9c9af04d,a5880964,a2443c5e,14db9220,446904c5,cf0a957c,945c5006,9438d869,45a9e461) +,S(b05cee36,361a9bf6,eb9003fd,c8d38587,640e180a,9de822dc,57e5012e,34903f6f,fb473886,29519a28,b745ce16,d625d85d,26fe21a2,30d798c5,305d8e82,545c9a5a) +,S(e202120f,5659e580,7a14e68d,e3aad7cc,23cd2f6c,abd86739,3a05f16,12005e83,49928b05,b66af992,d937cb4d,c05fbaf2,a70e8fac,7c081544,849d55e,fada136) +,S(57a91d89,2e5409e0,b07e68f0,450614cb,9a9bf0cd,8c5b3459,2f198633,c7a796b,47905cce,59018fa8,baa612c3,124b1192,1dbf7706,c5d8d16b,bd5c1e9f,8498e7fc) +,S(e092c40a,3ba465c2,d59e7c5,8265ddde,91c0468d,67428ee1,dd153b64,a5a7e8da,83f4e2b4,bdfc4248,2d4ab1d3,13b097f7,baa51411,134eb042,feb26f71,5bffd39b) +,S(73a3306e,7baa4fc4,a081a9c2,c335d3df,7c1ee84f,f5a46554,f78a6009,1de0ab38,1f68c8f9,2022c326,e37a7043,9552191a,3d93f684,3af0a235,bce893d,eda1f938) +,S(7c7a79f8,6e904961,73552caf,4a1401db,e99883c5,a39e493a,6a6eabbe,5f3a4434,85ceb252,e4883931,96c8ce8a,c0b596cc,a2670981,2c293554,15e17f8e,26e869bb) +,S(6a9df561,ce4ff772,b37054fa,e513657f,19f03086,eda93918,623a1c47,84a37788,bcf0274a,d9b3bf58,331ba398,a6611fed,fe719867,106eadb4,2b25b623,fdb65280) +,S(fe7bb24d,47b3adff,1c545d2d,ad2c6aff,81934cb5,bf2299ee,f3f8c53b,ff91c950,7fe65f86,9d2623c,930c27d6,f0a292cf,a881738b,4af0c5b,efde5c40,8a574e7f) +,S(26d1d2f2,7944b77c,290ba60e,17d05347,82ce2b64,ae815453,1da42907,30407d3d,ec3c5024,3106000b,d0b2372,53581384,51b768d1,d5aa206f,4d1055b4,b4f0f495) +,S(f00e51f9,38f91573,4ac563ff,8ea44de3,fb85cf4d,8f034806,e6c096d,8e73e2b8,7b7bffed,3a39a491,d3ab6a13,6fd88e0a,5854081d,21380c70,3c596771,a22c727b) +,S(73e15472,f42f8750,93abe811,a4d4bca8,8bd179d1,4e3e3b30,9e011e27,37cfc4d8,392ef4ae,58321768,1106426c,9b00b4a9,c53c826b,84bcda34,b5d0609,f85b9231) +,S(ed4667a9,aff30464,c851d86e,479df8e4,d29d2348,edaf8333,a7faf560,a9568092,77367875,6892d42d,469a9ab5,ff577e0,6d9f4fda,679882e4,3dadc7e6,3d49bb0c) +,S(d6ec16ab,10811cbd,dcc346a2,64dbfe0f,51066f2c,79034d74,af5b0ef2,541dde26,981f1cab,7ac7be22,9a91b733,6c0b89bf,4763b01e,4a1b715e,f4857e9e,4ad0527e) +,S(284ef47e,691931ac,8241d92d,2ee5b717,3f762ef2,bd1f48e6,8dca4b53,ab4c593f,ab739ee1,e6a7be47,83e1288,7ffe1b6b,b53a4d2e,3bfad3e1,c7ac5415,1bcf5e82) +,S(530f9e7,5a267d0a,fc3ba53a,c49018ef,44b490bc,835774f6,c6cc3f87,8d505c3,630c481e,44e5e9cb,cf6d7fba,8f7fded4,bee9fa70,4b88a8d3,6e2b3bc3,4e85e3f1) +,S(66a9b4fd,81d0165b,6737a4b3,8025d2da,ef9c4d20,3c49a2bc,1ba4d59c,eaa905f8,2770c44e,6a4dead9,7dd7311c,195d3051,12c86bca,257955f1,21b7b1cd,4e525e8c) +,S(25812e7e,dca47b64,9b18d739,7474de25,39bb7898,96ddb94f,60192135,23a289be,4bba3d83,72aa76e,6ba76f7f,18cb8644,788d3ab8,b58d0e5c,795c6213,6f17e2da) +,S(3c81be70,af162448,170f88b5,8fbf587e,df1a0e8a,3576cfd7,24acba77,dfb29608,ab16e48e,da59f480,7a0b5250,7102e9ba,ed9dc9fa,e0de8700,27bd5ff4,2aee13d4) +,S(ab626dcd,a5142933,ece82c8d,125aada8,39626e90,85c57525,2fb24f76,e0d6e39d,8add774b,34fa4b1b,d571f2a3,4d8ebfc2,bb7be73d,dab73cf,e7eedc01,764abff6) +,S(47f25106,120720ff,8cad6532,1a3e2873,aaaaed57,98e3933d,e1ccfe0b,f36bd85d,f6d472e,729512e4,ecb74ea3,f96583ac,cb44e013,ecd0bc60,c38b16f9,20585b60) +,S(6fd30299,f7398f1b,3c538d59,71b8bb0,e1640926,752d0842,36148b9f,23c26b1b,3b8d6166,a79ce235,1a2e9c4d,c5c89ff5,7810bef0,277f3d33,372e05bb,ba6799da) +,S(dd5b07c,784e1cd6,58c51ab7,45c4a09d,49db1287,dd7871e3,f449bd66,4a02e040,b26dad8a,62a491b8,dcced2a7,c0feed9b,6a04597c,d8a0f857,a4731078,d68d8659) +,S(3cf5836,104b4227,aa275e5f,16b84bca,15b61a8a,700b56b7,dff2adf2,167fcdad,1cc6f65a,2cb579fd,f6812340,88018dc9,445af726,2db43b5c,e9e49e4a,ce00c9fb) +,S(683b5455,8b9421c,c3aea163,d74c043d,d4cd804d,23857f41,3e01d28d,4919bb58,1bbe4576,2cd5c056,8add6943,8111cfed,691e2369,55022da,17cfddbe,79a11f0a) +,S(d78661b7,897a40dc,5f42f7ec,4202ae53,e83ff0e0,d73ce7d2,19503279,e43b724c,465299b0,e3f478ec,fc3db82a,61d5f290,aeead2fe,d0ec4a02,6652b29d,cad20b21) +,S(1258c664,fc15e794,e64dbb87,b4eebf1b,32e89e7d,50ec114a,1afa5506,19e15a21,327c4a92,81a73880,70d1a7de,c2a0e5c7,28c5997,bbc21571,94b0521a,141be0b1) +,S(f2d04fb5,1cffe2bd,c6e43cdf,ae7314c5,387f4dd2,4bf7fbb1,a6ccd627,86e8be00,89af3f8d,435f2152,8d32ee47,a86a2ae1,78d3a7da,c27ceb37,93250a8c,27061c9e) +,S(b67bbec1,4ab23ad8,e52cc402,def0083b,d21551a1,92a9df6b,b335481e,3f0dfb08,d6ca6bd1,93838993,5284528,56587554,3757cc13,fee01fe6,698b30f8,10a64a23) +,S(1baef1b9,fa401fd6,a053ce90,f2d661b8,8a466584,83a8489d,90d48312,2b79235e,28a19a14,a2f7817d,922b3872,cba558ad,bc1a69c6,c0b65ee8,3352494,a31699d7) +,S(6c0f066b,23623a48,b2b6a746,801baa85,f58de56c,e5287c79,c3c10ff1,b851aa8e,ec837256,84cb0f8,e528bd94,e1c86327,2347a479,2059f357,4ccdbb93,15ff0868) +,S(fbd5bb09,65bb9d22,bea6d6cb,7aebcf28,a0c815d6,18b79c0d,94913dd9,f63619d7,3303574d,5e605e5b,fe298f77,e20f3f6c,60ccc063,37a29c7e,6612dfca,3f00b924) +,S(2246042a,c4b22334,af6c81ac,8106bd1a,f8bd5f2,8dad88fe,7e568fa0,b35b1739,1da0d8eb,9c10116f,ce29bd9,61f92e36,9e538282,345a4f79,7cdabc70,5aff6e7c) +,S(470a914f,2fff2d37,835b3caf,542cf50b,9606cf6d,fd294c51,5632d9f3,4182bc4a,6b3805a8,d2c74856,a5be3cc9,e9c6a8eb,4398c606,81ce12db,b3a58660,1d426e) +,S(4e03f50b,ffb6d933,4282153b,a3f909d0,44bb7a11,50300c1e,cc8027b0,321b4be0,596c58ae,963e71ec,f1fe8f9c,b4de8cbc,9149b125,31e83e0,95f6e5a,b08c504c) +,S(5a6c4fea,b806bb76,191567e9,139dde28,188a26b4,a1803b7,d999278,fc57455e,6e81d07c,6505366b,c28ea476,a718e5f7,353d73c4,2f551459,d59a7d7,82e6ce49) +,S(ed73c9b2,9adf52e2,1280bb1b,ef63dd4d,74f4e09d,1be56c3,a246dc37,9b29bd5f,e9917b6,19ebe0ac,c7d1fa5b,7c4b7c52,e272ad9f,2df62161,59dc87b3,f913828b) +,S(68c6145b,38cbf9ed,4af10608,503ca13f,710dd34c,2386fa99,bdf93b69,c01c04bd,c963585e,607d9eac,5016f884,df535a9e,e7cb74d2,2e0c1e9d,e928d7ca,3fedc73a) +,S(1b651863,927317bc,1dc6094c,3f5c91cc,92269a12,59879b6,a8439a77,b0454126,31c6125f,8ac3d09b,d8c1fe92,c31bf13,c42b1915,e7147cf9,1371dc01,e8f5009f) +,S(45593ccc,dadbde3d,456b74f7,a3b705de,7229ff20,90a0df0d,38166d4e,6a09c52c,13d6657d,89fd0205,3c2b9a31,1ee964cd,e14634dd,20d85866,a43a4ea9,bc0bd737) +,S(1409c06e,d6f2fa2f,cd532983,d28c2109,e4dd7c1a,4ce77960,566d3eb4,37927da5,dc05f98d,4206fdba,e3b69836,e694b71e,fbeda304,537bc623,90edcad1,460984e4) +,S(d9e08c6b,c307b056,d4323921,5426febe,5d6e5cc8,4eb73707,4e2f00dc,e85f4580,a23c1647,c6b47a43,a3ab52ad,da32605d,172f4de4,201834f9,15d5a4b6,292c632d) +,S(7df319f3,d3c2b6b7,3533a916,64d5fa26,5ac0f18b,bbd5e8ee,1aa15dba,bd62fb32,9120f6af,e023bb98,1d6d8d96,31de0814,a9bd169b,a91af0b7,9c3db58d,2bc9f8c0) +,S(1a94e587,38b9fd1f,93146232,e2165798,11d74244,9bad3711,a373445,6986d7a5,332a27b3,17eee7ae,fb4604b,197f2db2,fc32549,ad86765a,74820428,abe4352e) +,S(dfa2cb4d,6db266db,2a82ef8d,fc0cf871,4aefff41,895ff691,fa0781f9,1b619a47,c5e5dbab,3a28a6c1,8cf20fef,4cba62f9,d2a1a0f7,6ea7056e,45acfe4c,49ad8b19) +,S(c2201007,be6bf76b,5e82c135,2a6e053b,acf4e2eb,c10a417b,f35e4c79,3a876730,92408aec,b9ae4d74,3af97c15,9c387343,9a735de8,90d1abe7,c8d14dd,fbefa581) +,S(d45632d2,ab087f0f,9d0d4d85,26730742,2f25c794,efa3815e,33e8285a,1bd7edc3,b7ce3697,c4994fa8,2479e99f,9c9e4b1d,814e7258,da7ba569,5543d6ef,8d5ff44f) +,S(8cef362b,cdc292b4,885f34ca,923a171a,df6c25b8,f6b7cded,c9003b71,cf5df93f,b3910f34,ed8b7fa2,bfa4f1c1,9d41d99a,d702e13b,5dab6bdf,2608186d,d09aaf5e) +,S(247f7be1,874c2831,17d60431,8184878d,3a415ae5,59595bfc,7f17583b,3cbf9838,90e235e6,a779b4fc,f9521da,65f65091,efd513e8,df338464,2f8a7cc8,f1f30eb8) +,S(b8c0cef1,c8703adf,411dd0b0,acaa2b82,f2609348,e36aac56,b0148ad8,981cf568,a0419411,47019aa1,4b8e74a8,1d49a97b,45e7aaa4,54c0f916,a33472ae,1275f14) +,S(8e55776e,6e1c791,2aa6b43a,4dd1bd17,a35ab0d,aad792c0,ab51bd86,8208cf09,dfe4ccc3,a025abbf,c2d12bc,6d462e6a,cec7307f,6daa9c01,2c8a7ad8,70822d56) +,S(f1cf6d3,fc9d9824,4e122790,de390241,db813b70,c48b667c,5f05846a,c3ad1ad,4925c6ab,3952d1b2,35ef1ec7,7ae7a84d,75006993,b173a6f6,c8b969af,c3984799) +,S(b49dbf88,dce5ee49,2b4495ab,9db762c3,81164147,cacbc00a,9e56670a,edb9ea31,c0f92f17,ab5797c7,758d703e,fd288a46,8971f7c2,de8923fe,b8b7acfa,ffa9ef2a) +,S(d6afe2ad,257b57ed,15573c50,a25f7dcc,399e2643,9e64acab,9a8c9f75,d8ca362,5df236b8,6919b56b,17140249,16282b11,38d355a3,561c5b42,b47ac6f3,8d35749d) +,S(bb0284c5,f23365fa,5972a439,fcee4b4b,45b48a9e,6844a44b,53e12004,1ae8e89f,684bcc0d,e09e5932,79018fb2,aed55329,53221dca,6f14f493,6ae76099,ba265300) +,S(57e95714,8a409697,1b8be621,98a04177,c4779627,ab5a77e4,6e82d847,e64467ec,714bd1b1,14ce2580,7c6d3a8,f12a5375,fd8e7cd2,5e854c12,6bb519f2,cd443f13) +,S(b0c82f60,b33c3b47,523ef2a3,de804521,a1e14478,eb637757,d17526d9,1e713094,cfc8d1b9,d763f52e,6522f70a,c93f7fd9,23d163d6,5a82b2ee,ac1af994,a2cea63c) +,S(2ba76170,404785bd,5932c185,c6d98ed0,667d8324,ff7f85a3,5c24a4bd,553e1ee3,1810790,74262af5,607167d6,b55164d1,e56aef19,f3e2f307,205d5603,eeea35b9) +,S(5bd54e13,d508b920,fb1d15c8,27e0ca59,3eb1c85,a7228895,b5c9235c,6ed3e88c,d65d35fd,5607e193,ea70ab1e,4ac7d822,878017ac,ef3bd6fc,2b1933af,660cf0c9) +,S(b0bbc2d2,753bd89a,b2bf1664,e6cf212c,a590d65f,5664e694,7ccdf977,3ea8d09a,9057b6c1,a1078bab,a1419065,4cc67052,7225741,1f34d47e,47f09bdc,8f993c23) +,S(c1701203,3cca38ac,ddc0fe41,95452129,b38b4630,20e8c459,224e9dc6,cd3133a8,c495d87b,1cfb6f67,46c07188,86c08616,624f78b5,ebf4fb14,fb6b636a,f5557e9a) +,S(1e0bd20e,5f1e49ff,32287961,7fbc28c4,7c9fa4dd,d93532ea,74930a24,5fa21bda,2d407400,e98f15d,e5da681b,2472fa11,b2a5869c,66be1cd5,7038e160,43dd6177) +,S(5cfc223f,ec4c6618,ff12a974,8533a70,f39defa3,bc05f88b,3a1b158,317ec15f,7d928bd4,89e3719e,6386ec22,6c738b09,eb4468f7,b6718cf9,3da33544,aff21411) +,S(f506560a,bebd668e,57e514e,bad47a84,5da345ce,50ac0e88,1cccc2c,81efa67d,e2ff0bf7,c9c2a99a,bc3580ad,17f64a26,63111fd8,15bdcd63,9d9a7d3a,ca0fc7e9) +,S(8448fe98,ec887485,45a46e4c,449d0168,1d00dcae,5bf96ef8,306e7bd8,26c54f45,d0f07229,17500322,2dda8271,33481376,85b20045,f69d656a,66aeedab,ea6f0407) +,S(c9a6857f,db037c2e,36d5ca71,32942f34,2480cf28,a98644f5,6bc455ba,97cffc55,a69e08b9,8f678e61,6c0b8326,ced6f97d,f742d974,b68cd1c6,b798e367,8faa149f) +,S(b5d13f5,c611eb3b,c82f7db2,d89ab19f,b8fc286d,8e4699d5,3dc664d,bac4011d,27eb1650,7cdc0fdf,5b92493f,f2487d0a,ef2692d8,9c702bb6,259808b1,324b7adc) +,S(bf9ec5f3,2e0ed4ac,c001b887,18c29c6e,90218471,d7f1afad,75abb34e,da35ce44,13846488,892b4065,b79224ed,368c47a4,a1734f68,7386546b,dd837a80,467ef2d1) +,S(2c3812be,4d3b1c13,aed1f6bd,1ca6f5b8,893c5a87,ad6a8f2e,d6d92ac1,3a526e19,366b82f1,45d551e0,b7bcf877,db340bcb,1b11edad,1466baf3,24a94cef,3433aece) +,S(77bdef28,dc8ef6c8,7fcd0a13,c08a26e5,9fd17253,8d8bd3a6,f21c5c86,ca33f572,da66b0c7,bf3bd49b,ec1fec,aa580a51,6977ba8a,83f957ef,8167f69,ea3c3a1b) +,S(bc4a799d,3185877b,cd86503e,3f34c606,8693bac0,654eeb15,4effd116,8b71afe5,53fcd7f8,99a8190e,a7159c99,e261525b,f59abf6b,d331be66,4f7f7757,7255569e) +,S(301fd884,733dce05,6190e4c7,d0375078,6fccdb6f,2e13f9dc,e275c188,2efcd040,79e4f9d5,185b9f51,fd835cef,64be607c,312c910c,e25cb73,5de2c88a,51e25cb6) +,S(241b4a9b,be5e7e54,214d0bda,13719c42,133f8377,e50db2f3,87b6018a,814adb70,c66edc08,fa4fd22e,1f76bca2,7fcc00cb,a8ce2100,248b3959,3c164847,b410bdb0) +,S(be6f1639,68191289,3c5c1351,d633d51d,dcd28c16,f3294981,56ea1602,d8c79585,6f674a7e,1a65539e,a7f8a966,8f5fe5e6,1ade5da0,a543335a,5f7a71e5,fbafff21) +,S(c8b5008f,79c69e2a,7e209ef6,a2d14b9,b3f7bfba,b7fcb285,e237cb31,3dd3a283,1dc79348,6c3cf7e0,bc36dd7,46efcb36,3cea15ca,a63008f5,2d83ec0a,6bb54bb9) +,S(5014af9e,aad5ed62,4e997ec3,a796ad1e,b038edac,77cf27d4,eeda7ff4,c06c0998,e01d786f,b8faa1c5,2d44a77a,987b4b6d,91143953,34977151,1a488602,3189dd7d) +,S(6ffefde6,2c40643e,da2d4f28,4f28fcdd,b0845ec0,8d882d2b,45dadd8c,a7bb989a,8e2b183c,782d3a9e,2229e4bd,9a2becbd,ff0a5480,6a6f4471,7fb6fe5c,8411cc0e) +,S(ac762bbf,190e1374,d32a7e5d,b4c0c36,ad0140f,c1e92290,4aaff1f1,487d463f,ccd79f18,31016b40,a381bfec,5db6b12f,bc34d850,ed81aa7e,435a513e,5f3adefe) +,S(973496d6,3ec05c75,93244e7,b737b1db,d791a5cd,e828f342,7968eae0,e811cb24,4b2c9bc0,44e3351,f637ff52,5c3f34f3,d7420b65,2f57bb7a,bf573e67,25647533) +,S(a9d89166,5f77bc30,74357753,9ea39e36,c39ce300,6fa03429,1a799b34,84a15d32,d09c3a17,fdedb8fc,f4f9ce98,20635a50,56dd884f,ccfaa96b,8d0813b8,4b80e213) +,S(108a8ffd,aa2b544d,8b8a7419,f2421bae,cbab636f,60d6ef76,9eca9a20,a0c5710b,ad3e9fc,ab04ad58,a265776a,75f0e389,cebbb273,9df328d7,b125665c,d4e0249d) +,S(ef969d7b,c8843c57,bfc0ac6d,f5e4495f,9543878d,95120a36,de8c5bed,727c5fb5,5ab81bb2,26738036,b145d81a,1deaada5,d1d4d653,7b80b02a,fc1cf85c,361f16fb) +,S(ab825bc2,4d505453,f12dafb2,8f70b245,d74200f2,410f5162,b0ba3096,43c39b62,789e7df2,23774415,da7e31da,277a81c9,708098b8,d455594c,6f9cb065,b1281a1d) +,S(4984a4e5,2c173c02,ec79198c,f8496972,eeed4e39,9933bac0,59856bb0,48bf2622,4596b67b,79be2e61,a0887a9c,2cc3ccf6,f199e924,3991f434,8e7cf897,acc5df3f) +,S(285c6ff4,93c1be13,f4f60d0d,8aa7cb24,4bf0f76a,23f3efbc,cfa4a132,625f7272,39dfe9a5,cf9cef28,d5cfd998,49db9b23,c4caace5,e8a27c12,12d7b3d9,d3ed49d3) +,S(1e14b1d1,7e721971,e9266717,13100aa9,9bc6e506,c278ac7b,348b708f,843db340,63a239d,d705f627,347fe464,c4d4143c,394d7dfc,5f79ea11,64d05f04,4ce41cee) +,S(b8f73b9,ed71cd1e,f61dcf30,9cd48587,3b1394c0,4cf318fc,10140bb5,be17c8aa,8af5dae0,bd66ec85,2ff4823a,a1be65ed,745676a,5ac42ec5,827107e6,776f665c) +,S(7e0c7297,e878fcf6,cb636776,d15afd0,49c995d3,1cd49d15,e624bf6e,8714540a,df5795f7,4c512043,f48b7568,bb271a00,ffd59530,a0280e5,8aaaf0fd,aeddd658) +,S(9f0ed1d,465bed9f,a2b51133,fcddfe48,10999011,b754acba,de5fd027,6b798545,7005ec9c,73bb0e00,64badde0,12aa99d9,d85b6c38,e63256e9,21799990,14a8459f) +,S(2d556f5e,b5c5af0,160608d,80e23cef,cc8e4000,b1e35b12,5a0a51aa,9c8e12bf,7600144,25bc3baf,1dc7f6fc,1fd86f41,d24d4488,f80ab8a9,4e4422ca,15f12000) +,S(2617667d,7637e02c,b479524,d9c7dcc1,2cc14ce5,4149928e,e8d81bcc,835ed3f2,521573ba,2cd809eb,8f5496be,478ca639,7a2c4e16,1b299ba,bc6ae419,4780884b) +,S(6247916c,5b519a64,7b76e10f,8810a3f3,4cae40cf,ed05a492,ba1b061f,d9aac10,ffd02570,535ba978,906a193c,500da1d4,602b0969,c3e9f4ca,f2d33026,eee7f098) +,S(c40b27e0,8ed50a88,60017e78,ececfb9,ea14c64a,95137db4,fa55d4cf,9a3c52af,2415cb8c,7d025b84,fd9d1e97,7105aa54,c4149fb8,30ea801b,a86f1acd,e07b6493) +,S(ba5aec8a,54a3a56f,cd1bf17b,ceba9c4f,ad7103ab,f0666974,8b66578d,3e0de12,348dab11,abecc4bf,3c7474d2,8ea5ba60,a705de60,39f9b70d,c49d58b0,6d57ffbd) +,S(ba14e658,f8dd294e,1331262c,ffb3f31a,c1e9e504,e55c7f67,5c002248,94076268,ced74905,fc43e55e,c70cb36c,66dd9a10,c221b0e3,a326e365,9b96de4a,c81aa1dd) +,S(3375af69,8880c7fa,29e718cb,4139ab30,f9c1d00e,a1c22d4,cfbdfa16,a7f4aff4,cfa8b491,a83be5b1,b66fa0b8,5e26c68e,8d27db79,ce55d726,ddde67ee,ce295a27) +,S(68d2614b,306cfdfa,e9fa109a,2c4b276f,5b9a8ee2,f7d502d5,efc94aa4,bd9f01fe,148c36bc,f04365ed,7ddf54e8,4d40fb59,ef9e1d48,8408c8f3,661386c8,5f48550e) +,S(93facea5,597703b0,ad2990d7,1d63387b,900ece6b,6c3677b,7fc47b7f,1c533edd,3c801bfa,8fa114e8,b5c19985,39a1f6c1,c20bbe44,d8ee7179,7454797c,942ad5f4) +,S(f11dd4a6,72f73118,95775ae1,76c90b2a,5a0a61f1,6ca205d6,eef6c275,75054277,bc06a16d,2ef2ac0f,7ea2083c,ac18d0d0,6c307d45,51e0a0e8,3f59b568,b9556ef6) +,S(42ebcc5d,710ba454,fd29a94e,f83c8004,b71fe85,9725c010,f10ca39c,864204f7,f8aa84eb,643101ed,4c7d5cdb,271d067c,e633c17c,dc698e40,279064d2,d65f223b) +,S(d40877dc,d96d5a94,87b6fbc,e89d19a0,549e96da,13f1fb2e,67e9e3e1,85c80e52,be9bb9b3,85f20fcb,8e57b59a,ffc01442,c0be8306,f5d9087c,837e234c,5e789444) +,S(c18b2c98,cd2110ab,888ec934,239240a7,aa40c1bb,ab8d7e39,8fa74358,2562931f,d9621034,9147d74c,75df18cd,1066c40b,b83f189c,937c01cb,24c2ce97,155773f8) +,S(154d6b22,1a44f841,1cf4fa0c,4bb4debf,6fe94658,c648f9e4,d14f88e3,e05c0bd1,df0bc307,c705332f,328bbb6a,dd94e63f,7c1074f3,ebc55976,de552a59,5875559a) +,S(a38259e5,629f93b1,a397d228,59842dbc,24485d59,c3ab86e0,a9326a35,e1a26f15,69b5accc,2fe52a63,b3874932,e8a79b8c,1713274f,f435d892,367bb9ea,91f783fb) +,S(9d729eee,e91e3493,9925a7d5,2eeded18,14827029,6f822013,d4db2a3c,763fcd19,6d832154,3cc8621,166f731f,2d874fb0,9cadc09e,4e330339,657aa2f0,e3e89a1f) +,S(2674f008,86307c7e,576cede7,944a9f9b,46398c93,8a0f8b39,b25701b3,67751e3a,3e9eb42,6ac9d1ef,837638d0,7b0f1f4e,e31bdec7,a9d22fa2,12a666b8,193f83cf) +,S(b2a06d5e,adb1b856,dd28e9fd,bb626c0f,b7c1642c,6f70c2c2,743f3451,4e237f41,5d4caf8b,8673b268,227baed4,481ad60b,de579a98,4d005f5f,f3bcead6,3bf664d3) +,S(20b27d64,d61debdc,ec3e4ec9,2040f05d,2b278c0f,1415ba60,f0bf23e1,222d713d,481d85e7,ff5c2df3,5cd26488,6056750e,c8c5c809,bb3afffb,41e447b7,a4e516a3) +,S(4f4a610c,450eaa25,f60d7c15,20c551f7,5270930f,45a7c51b,fc7cd5c5,3ce3f2e5,df1e8e11,db416228,672d8d8e,f9d68ed,e824234e,fd7de39c,2b32fd90,3a47f328) +,S(9b375ed8,504358,25570b4b,e359bc07,bc06c989,14659e9e,776fba36,a8aceaa9,3d056593,385c0069,915de5b1,f59c3483,22683440,86dcdf26,d1e6ae22,35e90927) +,S(43640804,5d432c87,31f865e7,2390b7ff,c13aff87,37fcfba4,60c0192,2ec9d1be,e2338eaf,6fc73fe1,e3df2aac,54bb4fbe,5aff2a92,5b8b16eb,2346b220,6070cbeb) +,S(a6e59c4f,24461172,4161f35c,fbe88d4e,59287f2e,f2b9ea39,4c476127,f41e0b66,75c70747,f5dd75f9,41cfa470,c4cf718a,57a8ae2b,2e7ab277,5f5ec5fe,7d9f334d) +,S(ff639617,ae7351fd,977bf1e8,46a6d33a,5293a959,74b296ed,7c9a007b,25322d51,e6123dab,fa2355f7,653f81ff,ee798353,e21dca71,ef40639f,79d73116,53dac457) +,S(77914840,1710d8e6,8a37cbc,5ad5e284,c254dfee,1c8b043,5f8ddbe9,4ef4faf3,d97f44e9,9ef3d4c7,4075dc3,894d2a6a,623be446,32c98eca,a54b8ed8,e0611414) +,S(f68910aa,29254aa,10551c00,adc135c4,947cf1fc,6dd8ee5,d42579fb,79484aad,40bdfbdb,33a56d63,ad587ea,fc9784de,69f9f5c4,3f5c7c94,1b9b59e4,c052c1fc) +,S(15be8f81,473d4cef,719c7501,1d560d84,f2d4d8d2,def1d696,29f62464,1b6e0fd3,7e66f926,fc991b41,c451ead0,6d93267,64b85ea8,849047bb,1d28eeee,70fd9a2a) +,S(e113d5a,2e22302e,f4327297,7c41c8b,d0c5c26f,9a0bd9db,9091abdd,8a84e9ca,2e4862d8,34c6dc45,17a6b70b,2f1b2c5b,b54941d1,cb803530,26a257ca,f5574dc0) +,S(96b10770,485bba67,7f620d9d,ec0d1be2,2c94dab0,57d6d595,aa78dfe7,47bea688,e060aaae,222726a,c1a1bd58,fc779d9a,ff854ac2,4a4996a5,2259e9f1,bbdbf30b) +,S(81742f00,5555954e,17e5e899,d2cfd8c,57f6f7c7,5a38e183,de878934,2405545d,a1763fc2,e5f705f2,e6f2fbb0,73578fb0,a8d742c6,8f92236e,df1ee03d,2ed2fc0e) +,S(17c19f13,96988f62,77cb72ea,f90d37bb,86bc7a0b,e16c1c17,fcc481ff,bb78a480,cb1290d5,210164eb,621c2642,ae3e51b9,3f430e60,718f9fd9,c5ec3bb6,53c78833) +,S(7b0f06,e8746ec9,c6648b2b,bb861548,bfeee507,63c01c3f,40319beb,a2497a3c,c6949378,84297d8e,308b318f,1caa0c7b,1634b758,9d6e8c6a,2fdb778e,d6a70020) +,S(715c2b06,2414183f,9a67d8c2,8a89b754,55a80517,cb1b8134,5f56e91,12c5e8b3,f89b89c8,9a795dc7,1e2d4cca,7f84b4d5,23167f03,97787b33,9205773f,2520370b) +,S(e0d0ce00,f974fc78,76777662,efe5dbf5,57aed09,dd89a4c7,8f1f40dc,8f74d3b6,acc5be65,ed704847,94bc7f05,c4c5511f,c253befe,7cb3c0d3,e85704e2,e3e3eb56) +,S(37553289,76fbb8b7,9a5aaf75,ecd9d2a1,bdba9a87,5bad6222,e770ab1f,d26432a2,6dbbe233,1b7e064b,d80bc7d0,12b2a088,1087e7c,2c5c1d77,eb016108,c2ced92d) +,S(2e95c3ef,51ea9036,8683f6b0,8b2afb04,f46ca651,2abdc699,446ab3a0,6fe3d3ff,905ea87,b1d8dfe0,48463b94,c61dbc62,1556c66,b2b498,39cb40c3,299b554a) +,S(25310e1e,721f3245,7f35c67f,b73553f6,6467580d,b5916cf4,fe7d75a,c4ff8eb5,9c5e52f9,be42eeb5,425a359b,87dcab1d,46ecd662,c17eb041,16b5430,786b4ace) +,S(25932b1b,2b24fc2d,d7877911,7d7cb506,27a23b7d,7faf53b0,cf707f51,b7830b7b,7e8e23b4,acdb26b0,1d7969a6,ddf3a2ae,2215e206,109cf3e,4e32d91,df536765) +,S(7d232af6,251f7667,ba69ba44,397f5b8a,892e135e,b09b684e,22b16a44,73c5ed3e,a3e8f638,c8a589b5,8cfdea61,d9824f1c,131b9045,9509b92c,1cd26571,a6d8356e) +,S(4d56caa0,1b65ea87,ba3e05b0,a4495606,6afc8645,4847633,cb9e9755,c8c720f9,14c34678,36e65197,ca3f1ada,936f0348,7c1eaee1,977ae08f,50f41771,77537330) +,S(71abd165,ca6ff8bb,775c81f4,2c722ed7,2615b503,3727dc2c,122efbd9,c500a88b,79ba2467,7284f86,2fb1c46a,e11ac4b9,a5969352,64224eb9,c39ab884,492620f1) +,S(8462fccc,c3810519,cc449634,5c5d9e56,30215e74,c41179f4,2002e906,56a48965,535a966,cd7fb998,bc548a7e,29fc4640,f0eb75a7,5c3c05cb,64d09048,a7787d4b) +,S(f68eac93,fed751d0,b578b5c,665684a8,b9c2b104,6fe45ae3,286e9351,36c09826,98e0c32b,98b0a8a2,8f119ff2,fa50e54b,66f7d8d5,732a1831,7334fc45,fd48bff5) +,S(61447250,9a8d935,9c0e4014,f6ff569a,43467dc7,e4940b16,15f442b2,d9f72041,fb7177f,c7f57ee7,deea7108,dfbea888,fa58f209,2aeb2d2e,b2f33141,7ac8f807) +,S(ea69ee11,4ffa1bd2,e2259a26,e2d88cf1,c9846ac9,379a1497,fc28ecc5,4b15614f,589b81b5,1dd6d750,91014108,3dd6cf3d,63183d1b,809b62c9,b17b768c,5ce4e2de) +,S(34727b24,3404de96,641a1ce0,d7ff150b,38e6aa15,c51fab78,1464c660,4e5d5263,9198326b,cf56ca77,35b9baad,dc0069d3,1920bff2,d5bbf804,435289d4,d827d4f2) +,S(952452ae,613778dc,29e2d275,6e98b4f0,eeebf4d9,526cead9,52f8d0d9,9e217461,c08b3a28,c195fb5a,f9a50eb1,1b1411b,c17f6582,dc14b5ff,797175b,2ad486a8) +,S(fbca3f18,dc4a8bc7,7e9afeba,66610306,401b8342,bde2dbfd,2d4a156a,e78e49ff,2b829d6d,6027b2de,eda0db7b,c76b924f,4cf75573,bb9329b6,c7b7e443,f94129c9) +,S(c7ddf11d,7ddc643a,957dff64,a08a4a70,5302bfff,b940882b,57bc4c15,5bd8c141,e9ca11f1,ac93b018,3fe8146,839be79b,b3711ee1,8a3daa88,89d55669,6e708e20) +,S(71043a30,86e3063e,b831f7e4,4e682181,1b3f2353,9cb6efa2,cd0ee4a6,70ce31b8,4db3f338,ef9fd273,4aa9cc92,55e2984d,9334c164,5320b411,ebcb7cb4,89322c9d) +,S(6de4ea40,15347cbb,d9349d36,80fa375c,5d70f044,799134c3,dbb6a776,48602585,3b76588,65ab52db,eea8d839,47025cf2,458a3243,38c73d70,f1d77df2,7f960418) +,S(29faa79f,467cecb7,ea17e23b,6f7ba03d,db9a0cc0,e1fa6215,bc425696,ba4fecbe,e5aa691f,c42fbc29,c1f027d0,46bdf106,64a2245e,d2ff1154,e50ffa1b,48f34838) +,S(ff880bfc,25ae5b22,78e5f15a,2bf8f5b,d750f8b8,74910f79,c8c42f93,16aafc99,9535f70a,728f79,37825bd8,1bb502db,60e7a166,bc8a0b10,5eeeee82,3a5747df) +,S(b781b6a0,3837f6f5,8520c566,352e8c09,60eff0b2,42524eee,f6a6bd2f,a4a2a378,acd7b7d4,6c683dd9,43722ee7,3c12b69d,cbf8389e,62755e4f,c44fffbf,4cdbbb62) +,S(6d976593,e8bac5d0,da732c64,82e4ac2b,b3f19062,59045990,ee540a3b,467406db,30c353e4,af6d8161,6c0bb9c0,f2a4aaed,6cbc1825,9a8b9658,3bd7e9c1,2f2a8b6b) +,S(18ed676a,4dd3e0db,46212112,9fad1e13,683ec782,2a6d9401,5d0866bd,6cdc3032,c86b06e1,5dbfa82a,5f7b9762,65e03670,6f9a6e7,7ee319bb,8ef26b16,a51bd5) +,S(3138e38,e9440ffc,c2cbce4d,aefd4b8d,d1e859d3,b951d18c,eb3540ca,db5ce2a1,df8ab1b2,a11a8667,dd660a13,490c6e6f,fef4bc40,94efa9a3,430e28c9,6895c241) +,S(757df4ac,2025c9aa,b8ab1e76,149a9762,163ce608,f4903c3a,91571cf,e8b00c95,ff0af5fe,ab36c5d8,47d15ab7,65000765,c56bd7cb,4d0fd8c4,a37d4ecc,dfa03430) +,S(8a697a52,7156589b,d13319a0,81e3b2f2,ce09de8,450b95cc,9aa70409,884c27e1,9eeb93c,b6029e9e,37f2815a,d332d5d9,5a5edde6,bbf03ca1,3df09f86,ad51f39d) +,S(b0bb6987,36f9e0b1,6cfc2198,e37a84d5,d35eb89b,d4f4c0b1,650d60e3,e58c721b,33d5b200,ce25b501,6f09808f,b38e82d,b855615,2f48c1eb,b3b6363b,ed6e00dc) +,S(1ca0edde,5dde4634,2637882a,d2f8ff7,ef6d6f5e,906ae2d8,d9f1b545,c837efed,19efb5ae,4cff683d,a7d5d4c0,f5eb9f3e,4c4d3a4f,ac58ee41,acb9ebda,a82863bc) +,S(780e5b0e,b9a4e57d,66a84931,1e1b765,d59ad11a,de4e921e,d90fc422,4d474aa7,5ebd9ccc,58cc5f21,2da579f4,c79aa3a4,1d8f34ff,418516a3,d339320c,133ab811) +,S(f7e92ce9,39808505,2d2af620,cdf210a7,30ec079a,54bf1468,edf686da,34eb4c9d,2c559dcf,e113a961,58fd54c0,32f14ada,57c23496,7a88b585,7b1f7ab9,78f93666) +,S(3c3afa20,75bb20a7,8f9ff809,20d80d61,3a5688a4,269336f5,4bc7da5c,4a89f938,7ef38308,1dd235d6,e563a9f5,46435f22,3bd836d9,3f1d132b,978f3f9c,7b888c8d) +,S(71d6f01d,ee60f9b5,75dec59e,7e56123,89fb9edc,3019115,af4fb155,f22097ee,e1ccc245,4a31a35c,52854091,fb414321,38da4fd2,eaf1680,2ef0470e,1c80bfee) +,S(9726660f,5705ec80,908a77b2,8e6257de,b107fc31,c58ab6ea,1f5f75b9,38c4fa2d,94cc07ae,3102d1e9,6118c213,b6db0c65,d3636fe7,8ea40c6,90c86949,b48cd4c1) +,S(ace5d0c7,54b2a1af,691a1a2,97e8b632,297fad12,ec1fc4b8,7fd02ac2,b2802719,924f9f24,8d3e725c,eba491f0,5f188d6a,7fc843ca,d24d0858,1f4c9485,8b9f85eb) +,S(fa51306b,34800ff0,ccd778f2,838a4a2a,8200eafd,622e98b8,8fa566b3,85943f64,b60ee616,6b144873,a1cee030,fc92b50d,4b67757,ad34d5c8,5f05c871,4296e293) +,S(85df9018,329d25a6,79c097ee,458eafcc,b716a59,611730f1,7c19d584,2dbea60a,c555d5b6,46d211c,e99ad7c0,9e787774,b7bc64c6,f2f6980a,e62e8911,897ab0fa) +,S(2f77c00d,c2024467,ab461b52,ae634a99,337ff5f4,780637f3,efb989ce,24af5e3a,c7f4a678,ebcbfabd,870c52b0,be7efba6,c5d28e7,aef2b253,3f0e7fa,905e3e39) +,S(52d8a23a,720050d6,ff6a499,7b802bf5,1f238df5,b0ef6001,58fc48c2,228bb34e,98ae81a7,861b8e1a,162d29b5,1c30c786,cebb1812,c91780f2,ccd302db,803d9136) +,S(a7f3e479,eaca0049,22fb8320,47555b5c,701daf22,407462fd,37b0edb1,7212b211,80f6190c,8c4ca7c1,7ba68498,d1bac039,3e33767c,c476fae8,e926500b,9fbdaada) +,S(31ef3150,e67bd326,61bbf4d7,942382af,73db5799,8f4880,ea04607c,a2356103,7db8f5d6,655d28d1,93b50bb5,14b16ed5,bc03c46a,e3d2c954,631834b1,a8a9a8ee) +,S(ee82f155,469c0299,39ad1ee3,52edea30,599b0cd3,868131fc,eabee245,3b8b058d,fd0c0d4e,2d77c2c4,2add9ed6,cadb898c,32de9398,f0394400,eed2ff30,5092b10c) +,S(dec7c7a,bfcedd6f,a9f7d45,8a071575,27354990,b456873e,206d1f7e,556b4586,af841ab4,cad597f4,186f0912,b79fe320,2c7626c7,b85cb276,73c18010,747200c4) +,S(c28e3370,48cd6fe9,7591ba40,5519bd80,2bf34f12,ee48762e,526b34d1,f4d2369d,e00abdd,da456731,7f486dba,6976ba13,b8640f52,39325a64,7d50eb17,f0636e8b) +,S(47157ca9,2c275f65,c29e9d,b0f23531,37027731,1cf05a93,7145123,df222195,648b17fa,c911fd25,af87e9ed,441cc874,9fefeb72,b34f8dc4,f430e0ce,89cbef6e) +,S(84d11702,745c53ce,d81b5503,79ff1947,9a0a9253,a7ab3868,f5471a64,6ca73241,4f16b6a2,c3c087a,4efb1353,644e64cd,fc593ec1,d8d656b6,2c0e0bc2,25f858c5) +,S(3bba3022,196060b8,6b2b2f28,ea8811c7,c74e8128,8e91adcc,b7fbe2d7,97d63fc9,7f34054a,f0945373,a77c5aa1,2ea4f54c,879d3f98,b6e7ef3a,9d73d2a2,a734e286) +,S(85637f4,7a2c6143,2437485a,b3d40e70,b9f74606,aa8538f4,425e8a06,98651c6b,bc6a95d5,6fb42b1e,67b07576,83a5036,f76e8531,a30d0096,c3500ad9,93396054) +,S(25302e98,5e75019a,21ed1605,dd3c543e,e8021a42,a40a967d,8ea6ed31,4dbbd123,849ccbe0,23097591,d46f9b4b,a13713f9,c0220639,11fdd0f3,267a4d8e,66797a83) +,S(e59a30bf,88e393e3,c08d73da,ba720ae3,485fde0f,b6b85f8e,4117f5e4,f43bea40,6fd7824f,e222cb37,1a0fa6bd,33e4bf83,83ebbb64,87872fb4,42284498,6197dfd5) +,S(c5902df6,97981a60,589e9bc3,b935de2e,de05331d,2c1365bd,a83f8183,f6247e4e,c67eb14f,5af551ef,1ab5ecb5,8feb7ce1,21d0736e,b93dac91,f865ac7d,2d75fe3c) +,S(df8bdac7,fe27127e,c679a9b4,74e3c809,b8b6409c,2e6c4c1b,277ab4e6,d5e66ad,3ff26fe4,9f0b3896,54d802ed,72eb4708,2738256f,c0266dc7,645b1027,717d43e1) +,S(1d55404e,9c7cb8ca,c39c7c02,b810d5f0,9eff9125,9166cf3,f1e8cf76,2c65a82c,c1787023,f6a1c8a3,5c499f64,a2fc4d23,e5a760ea,9a04707,c24bb9bd,b2a7632f) +,S(35f390c9,b035fdfb,68506073,d585e450,2d040e96,67a1b4ef,804ee088,df43b44,6a85cd88,86aad8b7,7dc36fec,50eb9754,4c3cad2a,da43d038,3c55da38,bff512a2) +,S(fbb0840d,ee888fa6,f26d7005,b2449cfd,1d22786e,d43d06d5,88f744c4,c5e059b0,6460514f,d89a70f2,3a4ffa21,9f651bae,203b05f4,bd3d45a6,508d2b25,cdb8d3bf) +,S(24056cf0,1c2275dc,44608ac4,66931e91,61fa0ae1,3353cac9,ce481271,a34d78d7,43ebd3c6,7e7381d1,bf03088f,434a5275,965a3fa0,d86a51bf,7dda1b63,cbc7751f) +,S(3b1043df,e8447c78,8e07c938,cd72f6b3,54f9f745,d7e71297,e7791972,70f9cd78,28e028c4,abf0d511,126c29d4,b2b185c7,b67ba361,e16c50c,34af129a,5e40d6fc) +,S(aef909d1,41793638,80d75b23,6635805c,8088b08f,5c577a81,a5bedd20,69a040e6,cbb4c6f,de26b6c,5d0c2b43,df40a3fa,93350cc0,1c497382,d8a1bb7c,8600dc51) +,S(c8d494f7,386331cb,6ff59aef,5f229bc7,913367b8,10f407ac,2c9f9d3,ca833f28,cb2c76f9,4b6285d4,d39afdac,bd3ef114,c7b85027,8d62da67,e70aa74b,b10de4d2) +,S(25afcff7,af14ca32,3dc74443,71de7b5f,c1b52e17,6fe5f3f5,f4f39139,34f75b28,d867a4a4,7f9d7e97,6cb1f57f,8d505a66,5010cdfb,4af4b781,75271722,6942b789) +,S(2436c86,f773930d,872c220d,c8bd72e5,d407682d,7708e2f8,19ad9632,3d32e2c7,6e396dea,92af663c,c44de690,24e1407f,20a95887,37f01662,bc89cdea,88ab67a5) +,S(61ceebd5,9e5733c7,910bcb86,e19159ee,57638bb8,7f3cc56b,d0f4d25,147f349d,2a850545,e9b0ed08,fb63082e,741a30ea,7d85218f,86282aef,3e74b15c,f2fadb9e) +,S(5a1133b5,d45d0071,42b91a73,79e2a46f,dcbfee6,e102d16d,d77e817e,b4b6c187,2ffd96ae,afd6d9c4,3c9c0517,133a4b7d,34c1c2e8,f398f81f,f6f0305,28d492e1) +,S(b74a3849,8b701b41,29eb5abe,8dcb13b9,fff0f191,314dd293,9c38d0f,4563c8ac,9d844e57,e50a20fd,864d6f7c,5a677855,689be528,f8431186,ffa96679,9cb9014f) +,S(c35c9cd0,4afb20b4,3fd1c337,5a952ad4,8e9a806c,a64aa4b7,52fcb204,5f89d26c,b041c6aa,4a83efcb,dee79d84,19e195b2,48c765c3,f069d5a4,b7e87792,79a9c65a) +,S(9a85aa58,753868b3,f04b2af4,479141d7,11703dde,e5b6e5e8,fea8ad37,d0b82b96,465c70ab,aa16f03b,d24b8075,233ec626,4e062041,3bf485f2,551d9952,7bd75d27) +,S(3ec71bcc,e051f3e5,12d83c14,cbd23f49,b3e825a3,cfa4c61b,d12eb057,5c86fe14,f198c21d,e2daa56a,55294c5a,b033f1aa,45caba3a,9a950007,db35f05e,e9915bdc) +,S(ee1cbe22,939e1ede,9010e23,535bf33f,6b09e579,3737db45,aea48f00,b359ae63,bf604026,d4444df6,27f7d297,1dab6a91,acecb6d9,de53228d,47178017,921b90a5) +,S(ebcbf3e1,71de3a62,ef47677d,1ff4d027,5a8cd92b,bc889ee7,8dcf0c99,6746073e,878d63c7,bc9c7224,914a3f3,2dadeb9,95c6d890,a3823f98,5c3f574,9ef6ebe8) +,S(e4e92ee4,ce72ef15,b87c57d5,259520d9,b6066430,c090dae5,6e81b2fd,ab9db797,3faac465,4a03a753,559f47db,f5877452,6d21838c,9c1f8217,ebef6965,655f944c) +,S(fc1ce758,8e292b03,7b4ef743,12eb1f67,cf275fc6,7c1e3d63,5bc42dd0,deac1cc1,176b79bf,44f7d9af,7f7c791e,8cafcb7a,495bc392,c23899b4,2fe7963d,e65186d7) +,S(f1f5bfff,179e6a6c,692a86b2,1c95ef85,49482b3a,b97a491f,9e9c7056,ba94fc56,389257a8,a2cd3bc3,fb4f0620,5c2aae70,67041512,10c62093,13a2584f,8a39e143) +,S(980e7414,61c3e001,ba6bfef8,d175a141,2c7aaa2e,748c3323,ac744fd6,ab793eb9,6e37ddae,29e05e83,b3cdde28,d4286005,34cbb33a,636c4259,c7fa7625,1b31826c) +,S(f519cff7,686a5257,71986dc,7e6c1955,adb7faa6,a6f7a07f,66a257ed,a1b1c179,3e803eb0,8c765c98,7941d008,a93b8503,174ed4ed,1c31e97f,9eeb8f2,a55a807) +,S(2dd8d9ea,3851fea9,1ba9bafd,bcc5cc0,9011d939,10edceee,144e2755,ba8f8061,6147f1af,d4224689,37cd058b,4ef52618,7adfdea9,ce970dc1,365fe04d,1da52ae3) +,S(860c4262,9483537,47ac0c41,5aa00f63,2854f88c,d7d09b35,edefd780,1454a4ae,a2a7a22a,7d826d25,8e7dfc56,1eef0d85,d15378a2,80535cef,95ce6e2e,d73392f4) +,S(e76b20ec,17d82c3d,987d4ea,20535209,e0697f90,2c964b28,ae97ad0b,aed732a6,c97844b6,85f6613b,fdb7cec9,cc47f701,32bc1137,d997f8f4,ad182492,67f54bf0) +,S(26731d0d,12fba582,636862b1,62a417b7,718ec9cd,e3d047a6,50455efd,910ab88,3c10efb3,b464a61,6e2023b,b0736d6e,8d298609,47ba5714,3b1a71fc,9b542192) +,S(50cb4878,52db811a,87fe46f5,ed014db0,3fc4570e,8b2d8ea8,9865df9d,a224dcde,bb8488a8,7937ae89,88f572a0,c100e450,c69db48d,303d06f9,5d54bd83,8b39621d) +,S(fb69453b,1d67ffdb,670f9fb9,2b3fae5f,64b0c4d,cb28ba0e,7ebeeb21,ffb02a60,b0fdef36,92d881b6,3ebb1387,984120f8,d1ba17af,477364ca,41e303ee,a4fd2c39) +,S(a8f1280b,4b720226,19faec4f,ea3f784d,3f4061a,56dbbb99,38c7706d,8bb249b4,ebb3b571,ac505913,4572e501,4be7be33,a97e12b9,bc1fd9d4,484703f4,6e39b8cc) +,S(4292ce09,df305790,f17cb3be,30996016,420f83c1,bc19abb8,78b49027,6eacaa98,1b14d94c,bf2b1d1d,82cf1873,3461b76c,f32c8d5b,6746e2f9,aa2aa248,1c745d36) +,S(ac241177,f16f747f,b796750,49c83f8f,8e1a062c,9796b05b,8c5a5587,a4c9464f,dd057503,8261d044,ec84eb03,73065687,266b695c,ef5cb41d,1cba3b5d,9176cf58) +,S(692eca73,26bf2da5,7fd9d640,acf7f846,78e75c9b,86b18e7d,d7fd2545,751823d3,17d6d462,548ea78a,18557320,289d61da,14c3850,4ff03347,8f17fdd3,71c8e97f) +,S(d01b1a44,55ef5011,e4ecf946,ec02e6f5,d0e129d4,d2f59889,e823cc5b,6f10e216,84a4d89e,55e33edd,d54f38f2,1649f5fd,807879e3,388df988,34556ae6,57f91a0f) +,S(1eba61f2,96a788d8,9273a67,18f2a0cf,fb0798c2,393330f1,2f890bc7,9bfc1e9b,89f298cb,e8471046,2df0a6a6,b6c4b6af,505da2c0,9ee13266,bc47cd5d,2ddc9661) +,S(26dff8da,94603121,4649fe1f,721edcc1,a2300ca,b1a6d548,9c9b68d7,415980ea,d8264a21,55e1bbdf,c3b65862,efb9548f,3c4a0a5f,97f5b713,9d3da15c,90004e60) +,S(4e642b51,425e1822,1275f64f,283e9eb1,a3116adc,a6e25047,cee3bc0f,323fe676,f28b7d65,9125468c,4951db0a,bc35d4dd,65b9c5a0,ed27d742,47a68a23,89eafbb2) +,S(354aff5c,ee9a27db,37e7c97b,4cea476b,b4f3700c,e9c26b21,e94d2e1a,78f448a2,8d6907d1,624b79e5,6392d008,2f6a794e,99b4c4d1,6ca78dd5,97709c86,2930fcdb) +,S(5286d074,949369fe,8f69f020,c0b65f75,e76995ee,5f49b9dc,d570b956,8ffbdc3d,255ef2e5,3dd2c40c,1e462a45,27e6ee00,e0a23d3e,b16b05e2,2781de6,879703ab) +,S(b84a8346,c86e2299,9acc8ff9,1a893592,6bddeeb8,6fde121c,266d5f60,3268add7,31179a8c,79f40238,e1b80c87,81e46597,29f5c8d3,d8bea0a3,f65b403b,ef20789f) +,S(1e7bd5e6,63fc9a50,2258b9f1,14fc51c3,1b9dfc27,9022c67e,fde7872,dd12bb94,6287a9b4,555b146,7d250f11,e77ee8b5,7accefe,6d91cb6b,927611f2,c2c1b84) +,S(7ee68a21,37289ebe,7a5cfde6,be672ca6,9d28acc4,54d6b8da,9c96b2ad,37d2bc81,3ec34064,a6c3ca,f2a8aeee,9b24e023,3d7dcbe,b7acddcc,8a53db84,c570c83e) +,S(29d4b10c,f4be3bfa,a9d07fdd,da6ba7ae,f3adef75,67c3c33f,8d4d914c,f8ce570e,b6a74385,96a4f40a,40d03c25,2c7070da,2ec79e08,dbef71f2,422392ce,1058bf4c) +,S(7907c5d9,5c5cd647,e312a43b,42c3f053,50218f10,da6279c9,895516d7,328c359e,6c65a9e1,ec062a1b,f2abca52,fdeeac18,26888378,58f985d4,c8f1c987,64d6a625) +,S(cc436eae,ce11bbc3,36014fe7,bc68fcfa,e5bc2c55,c53430ad,fc994b8d,959d31b1,16160de7,d6ffbf64,d644e608,96052985,6b2bd539,66d7edb3,a50c924b,fa9706a2) +,S(cf023d5d,278d4201,ae659d55,6239dfa2,1b5fb30b,14adbea,7351b620,150ec1f0,d6007395,ffcec07a,6e71c26e,7575cdb4,34c473b7,57d4574,9e5154e4,d4990741) +,S(1a2a093,f718c59b,d4d35b73,2c547b61,12b6a257,e807a38d,71b04125,e7746a21,2674b542,652fa39a,1a2cadcc,3924796e,16c0f8b0,5d83f919,f8b93547,98fd4c95) +,S(534ed420,4b8ac51,7518fdff,224ffae,f712e4c7,6a84215,fac925b7,eeb799d7,35e5852,3ea6199f,b9fe497f,576dfae8,fa30782f,d24cd439,c0708118,dbd82155) +,S(8d31e4ad,9e6f30ae,6c78ff18,8f4b0457,e9ba646b,3a26138c,f11d84c1,4a00d08e,f0dbefc1,909396c1,3a1e36a4,844b2a87,e4b0b1cd,4b30e996,47839bcd,4dd5feb2) +,S(81748452,267405a,bf664379,16ff7f73,c418439f,86b73159,54c15fd2,2d2b3412,13d1209b,435b50b,1409389,9961f5ae,9f6f95fd,280beba7,cb3f3f5d,a0f19bbc) +,S(bca87f72,e604e885,64552b,edf380ca,45842270,57efe12a,6cc23847,658aaa3,ee508af5,28b77125,72c123c6,276ba23f,af26538d,5752d84b,54ee82ba,df9e3d0e) +,S(de2baa9,a19652f6,c0c0365c,d6e78cb9,d527e546,1240fc52,e8c59183,7fcd3fb2,aeb418ef,466c32fd,4cb5f2be,571672dd,86573941,a9cf9bd,73377aca,1ed38905) +,S(7b8ce1e9,14df7919,b54a0591,c077135e,556f6cab,c7665e5a,c0004390,22acca9e,a60a3ff3,9cd8ad49,c100b72,4338a54,d06a96a9,f3fdc7c2,fb8ae598,c0b40628) +,S(cd71c179,13fe7443,19bc80f8,205d7617,4d6d44fd,a6ac3d1b,402279b,eca50e75,9f1e73bf,cf3438d0,1f01d1a9,3f633929,ae385812,528ad211,967bf38c,71419064) +,S(42cad02e,32dc8dc3,4ba36161,d0816af1,619b70e,117da444,f4f1e454,73ca8fe4,97cb2134,5a259cff,8870823d,fe11031a,79e59d7e,dd2593c6,65128a8c,36e75568) +,S(98a89130,4f0652c6,2986644a,62a1dfab,48331756,c8b9e1f6,d473c03c,b98ff8c7,c130f2ce,cc506903,9ca49b3e,534ad484,b277e0ad,7b2a8733,98864000,e9a824e8) +,S(23c45c76,84762aa,7bfd39ff,284403b0,d7a4a128,6bd7b455,22c09d2e,df8a4328,f4ab7317,95c46f47,c3d46dc7,cb0d43de,d57cb274,ff0c350a,f0d79548,bb303cde) +,S(8b700037,d584c4f2,f65416f2,dbd37c35,9e6a7b83,108dad34,3a6c4d0c,6a21d856,8371ec03,9ed0066d,4e8e7d86,69169f9a,5a172cf6,c324c2b,ecdfc76a,c365ca35) +,S(da6164a8,d0fb4111,ca1d78f2,83e8180,4f4ae8,50ae8596,cdb97d9a,a30f8a7b,f9a8463,717e4f17,a77caf4e,10c11101,1cc0b5a1,c788307f,c7aba482,2a9a342d) +,S(49f37d65,a9d1a54d,1929b9c2,6db7d7e,c45924e2,9e6010fa,2202d47d,eb5fd6b7,f9b9b785,28f62629,7bd0dbba,9340c142,1e64895,b1befa94,ca677de7,686aa51e) +,S(8755f088,e26859a0,1a56b78,ba1898ab,12d80837,308677cc,b38e9c53,e36efa10,5d67013d,1eec7a88,215b4768,f55f83f6,434906a9,f2f6a9a9,de5df75f,6ef7e478) +,S(35321796,b7aba6dd,873550fc,5a35e081,a4e6d134,a127c534,69ff777,c609045c,47ff0f43,efa93f4e,f197a680,4f1e1133,c5c8301c,b49de8ba,6fe10167,f0a1e832) +,S(3abedd2b,644bd2bb,e77ab5ea,2823b42,83479fec,df2b3421,56fe9ace,612559b3,4269b08e,6c13b818,131824ba,11e3369a,87bbccfa,330e408f,17985b21,e5e32b06) +,S(e30112a2,2c23462a,a803dc53,e3277d7,c14b62cc,92787f77,5be98ffc,7c956de8,d0c7e3d0,dbb10e8b,7a9f4e0f,f93e7581,99d90f6d,d869368a,8d2ad369,4898da40) +,S(6250858,439c4b4f,30d47f6c,b9259103,bcdf497b,f09d3762,f1073f5b,973cd06b,3437f5e4,84ce4258,5584407a,fb0e4383,87e4c930,2fcc902e,cd26b721,5075328e) +,S(77ad4485,b8c94cfa,4e904bc3,90e93e3f,66838d50,fb16e09b,dcb39ad6,88f826ea,4dbc505a,9798879e,137c8444,750ec3c3,34c2ecb5,485bab73,6b0b9bbf,806001d4) +,S(5dcd3e92,e413601d,b9dce337,7a8e67e4,3852b8cf,ac41066c,ad579199,3ac0b7db,acccd081,35efb43a,306760ed,ec278f7f,36996ed0,d6367f85,417754cb,a4e997c9) +,S(3a26663b,adce051f,c41d2e6b,9b931851,e65ca385,15f59737,fc8c27f5,caff788d,2d647eab,af01d846,70110e,7272cb4,9b9db0b8,88a8113,bab5f89b,90abc2bb) +,S(f57cceb9,51dc98fc,712fe9c2,c024031f,4e3b0f02,5067ac4,b8bb0096,e4b1aec0,696093eb,7a8aebed,a751455,782f99fb,ddb2c66d,438a7bca,9c4df301,b7a1ea25) +,S(a89a4778,bca06e14,bdbd46c,948ccd36,df47c83a,2dbba9b0,93589406,be9b4a0c,f4224dd6,9d08262d,22b98e2e,4e781e6e,939d69ed,d59f30d1,d23f9ea1,6ef366cf) +,S(64738381,84fe8247,2b463f4c,7ff04a6f,6a3be92f,ba295d72,adf065b8,10b962a9,bcd100c0,d09f6e93,4fe9d58d,d58004d0,9ba164fa,324010bb,c973709a,2ce32de) +,S(4a683a38,21261e28,2e0196ba,d9f8727a,f7bcaa4c,afd5e686,6c7cffaa,3adc7774,b5aba4d0,ec3d9aa4,39b37f0b,15f40fed,913af371,b1b94f20,2cc93378,954ff3b0) +,S(1025e16e,fdaeccd4,436cfd9e,b8ee1614,53fee046,e420296f,53cc2e5a,bb5cb3c7,f806b96b,67ce92a3,e159b807,58f38f73,704e4d8,3bd611c2,25314400,f0c48f3b) +,S(6149d809,675a5353,6fe2217b,7f6777c7,60039cb3,5a0840e4,552e2ac6,db6650d2,e4ae636c,9a9808aa,4f49b03f,bd12f786,c72f07f2,9e397a11,68154505,b1a96ca9) +,S(ba24112,43888342,54ee8327,4bb03dd3,2fff3edb,4d607047,d8c141d4,c3fec47f,8a6bf123,29c3fcdd,38e6a18d,ed28c8a1,3717e997,9d0c366d,aa640dd,bad5c35e) +,S(c20df096,3c7cac7e,9dbaa259,538af550,35c00550,94ad4993,2190688e,7856a961,491e995d,16cf7ae9,85b8dd8d,6a5890b8,fb4f2c42,aea7c33b,ad84835f,b245d40a) +,S(6f53d8ca,4dbb4d72,d9ef3dcb,93dd2ec1,aac9f38b,1b4cb2a5,c76449d,4faf0c50,6784a02d,f449beb7,24156de2,486753c0,77bbecc3,27caee12,f7888687,4e7ae24a) +,S(e57e78bd,71a08f60,51c8a046,184757eb,6364ae0f,8eca86b3,51317712,737bdda3,a0623c7e,8a535f66,57116c82,da77554e,31a1d263,28ec0e8f,116976d0,f4385342) +,S(f52f6f29,aad496ab,9e0abd0e,a7cb53a7,7853e8e1,a6e5ffff,285e97c4,f7ab099a,3df7c1fc,6d5c5e81,8b5de4b7,66f2995d,c361511c,198a68e0,b7196204,559d55a3) +,S(da74f5fb,5c0a6d09,6fb26268,b33d9c86,ec5cf08d,8bd65fe2,59f836a7,3efb0dc3,7e50e989,de5e2847,7e31c816,42a51edd,8d0ea031,35423f3e,ffea07a9,48a62c47) +,S(2a2c9aa8,7bd45217,f341d35,9a9d2de8,35d14414,cfd3eed8,a2337a64,e051f4ff,bf9efdeb,99335f68,a01fd7e5,d8aec9f9,49688221,59d18bf8,a466c6ef,9648a1ce) +,S(2a3440b1,91dc5314,c6d0da7d,b07e2cf3,7d13bcef,1ebfd23b,492ad632,92022266,bdec24f5,c993a020,2f2fda24,e8707947,e4c1e9f2,2c34efc6,dd1e62d3,e2ae2b40) +,S(a3a9bd74,3d1d36ad,fad36e8b,d5544500,e0accea,d53d081c,28740109,e10942a2,cc33decf,f8cae7d8,dedab1fc,ea1b4c16,2cb71104,ff75c16b,66db334f,ec794ca0) +,S(5b83d48f,70a2f212,93dc724,77f4d87f,80db7eac,2b08d70d,308953d1,88804950,f78c0488,a10c66c2,e11368d7,690fcc4d,982ecdb4,7ed539c3,30d4b14,4248b545) +,S(dd5b46a5,6e3e4a22,39e210,a871db8e,d901ab96,3c6fec29,4140d77b,be9736a,d9b76eba,ebdd45ac,35c52268,ccd35df8,9e7136ef,879848ce,ae647c13,d421d46b) +,S(2f74db5a,25486127,6236bc5b,22214097,d1ff781f,3798f64,62cc6dcb,3f61a6d3,b6d635ed,256f6d2f,a91b868e,a3014db3,64647147,ac89248e,ca983c09,53149c1c) +,S(32adc0c4,1f7c939a,58640b8b,fde1c8ba,119bff26,dfe4b0be,c5058ddf,23ab2e09,cf317248,7f0bb297,d7d7772d,2a01d917,40beed83,3a35a293,66bbb0dd,3946ae3f) +,S(4fe53ca7,172da8,272a152e,d74c07ed,4e336cb5,85cb2ae9,36f9f301,87c6cdce,81f6d1c4,bd711c16,21cc2986,916578c6,baf42870,58b84843,27e46e98,81e3a8ce) +,S(601934f5,577fa2a6,702f316e,b4dfed9a,3b08dc06,5d3e58ae,63f8548d,9fb2569f,6df7d882,4360fd7f,b5f96f68,93b1f1de,55e19690,f6acf954,362074d0,30f714cc) +,S(79da708,8ff4806c,64492ee5,2e9dda2f,e1bec3c6,79fa3129,8173c0f7,aa79d321,2b1eca29,76f2884a,35167e8a,bd2055af,44be734b,b0e0847f,f90abe8b,17f9a47) +,S(602b8209,c39f299c,bc208b51,ca8bf691,5b8d4fc8,d8399186,7e589dce,79262974,f8b6cda,5d7ca83,6aac43f9,4791a5d8,a5ed1f05,af90f5f2,267cd9da,567f5555) +,S(57a4f368,868a8a6d,572991e4,84e66481,ff14c05,c0fa0232,75251151,fe0e53d1,d6cc87c,5bc29b83,368e1786,9e964f2f,53d52ea3,aa3e5a9e,fa1fa578,123a0c6d) +,S(4f9b48f0,ae9df110,70c4c5ae,2b012cd6,4599063e,5bd32b54,43548b78,6a06db2a,d5a7a0fe,3cafc78f,66f563eb,f6fe42ae,980cd621,bf18aa15,7bdeded7,dd5ea016) +,S(e46a5308,4b11349f,44661589,38bb663d,e1910aad,35de0708,5a053b5f,6c5e7375,da46da88,9583632d,fbf923be,9f07ace2,cb8c3d2b,fa6bfc47,9e1a7d32,e1183ce9) +,S(3643d766,b94b161e,323e64de,3c925040,d62f2ad6,2b60a674,15c8b962,1c071ea,faf69af1,1f2b9e05,db29dcff,a020f6d4,80835d4f,83d35a96,7aff1b5b,9f16fa98) +,S(96769694,c026152,1bbe1b48,544e05dc,e6bea57b,7fa31478,f6ff1b1b,bf497dca,4eb5a6fd,7d0dae14,9673b033,bd97c08f,8210fd12,c759293c,40782702,b3e30b03) +,S(2e41def,2c479931,89be0ff4,b4110e28,7f80fda6,f4134ef6,5c867e66,59a24d07,26a14d7b,b8388b12,a3ba1f50,e56ffb3a,69bde3c1,17441f52,158531d,c874c313) +,S(f39d941a,f6eafe0e,9a44fbf7,71eb63e4,db94cbf0,854a837d,b7f284c5,ac03c29b,1ffed770,dac4d12,2241f8b5,507e04b0,72cd5934,c03b0183,d8b62e73,8cbd7b73) +,S(90900d34,4576beea,911f8dc,c2d90625,1cdac045,28017d1c,59576449,50136419,60d38bbb,2a338102,da044498,a2b2c5c,1457cee6,eb34d666,b0c0956a,48161ad6) +,S(3a5e4627,1007f4a6,2e744226,f1d2b782,44a523e3,97b6509e,e3e1a513,d8ac2e06,50f01fc1,74cfc3f,256dee2,fa415f35,bcdfcfe,57b79d90,f75226ba,e8202449) +,S(f66ea256,f6d711e4,1ea5492e,f6f6e46a,b13ac258,a8f0eb87,a06b6c3f,13e24355,fb8a1c47,980ea6c,87ec2401,4c0f6d2a,8f32a2a0,6f3fd0b5,7fa00307,26243841) +,S(cada0520,19a16ccc,fbb6d4ed,6c7436d0,c946a485,ab3362a8,5c62580b,d3038fa0,e249940a,34804324,ac706963,91ae7393,421de971,af0f203,2b5f2d21,7d32ae6) +,S(d16f5b5e,820b7295,96bd5192,9dd830b2,ce208413,5c2db712,2c4e75fa,cb759cf1,70edb084,cfbebcd7,ddd36380,75e05147,6ecdd585,e228749c,e61e3784,6a448ed8) +,S(bbccce8d,dc6bae0d,cb6e5149,8c666f31,3dc31ac9,d4d949a4,84751134,d5f71535,4da772fc,63ff92be,5271b479,68e7bbf5,c8941f68,989fa6e,a23ce5ad,23f91eed) +,S(515f28f5,cd8b75c4,325e940d,6821eba,e7d5d2f8,e836b291,5f606152,ed4243fe,a13143cc,f5d2b8fe,14a09f46,68229258,e1855a82,6f7db2a2,4ba2f1c4,63668b47) +,S(bd2f46ae,a78c462d,8691ed6f,9d381278,68d356b5,b8fd7f79,df6ed845,21233837,8f645ed3,f3cc803f,7eb32f70,ed7d6fe2,84ddd9ae,62ab8324,b92bd7c8,79c4af11) +,S(8576f13c,c3d32122,69e10b51,2011a450,4f8d7d3,92e0f133,47d7d65d,f9e37dfa,fbbf2048,3fa0e131,2309eb8c,19eede8b,86ff9028,45b66195,8d19d903,f68b1a4f) +,S(70be1e91,8fdf4e47,d73f262a,bf2361fd,763a83f2,d31cf41a,23b8c9f3,8309db4a,82f80575,e4246ef4,c13fa48,8892943b,a1897ac9,dc3ef188,29f3f708,428bc573) +,S(bb8a093d,1d586b53,9bf54a46,1a353af9,173529a,12312361,c8064e91,4b41180f,460e0ed7,8456a251,cd68d66b,ccf5de7e,28707540,d221d65e,72334d85,af478269) +,S(7817ae4f,34d9194c,f4b30628,7071fa1e,7b976a84,754c1860,6dc4a124,bc5c2e69,25f4ec56,c4e3b1b5,bc6ed346,e4f8b340,6afe1c0c,f84ad7b,481be5dd,2cd55cbc) +,S(85c760dc,411f59fa,6feed8e2,a0e5bb5f,e72af5f1,2734c9a5,bccfed11,bf66abcc,92583db0,b57c9c71,bf0f1248,23a67e5b,89a8910e,6500e8ad,10666b26,cbcb8cc4) +,S(20d80d35,4d9d762f,f2c739e3,b1e7377b,d9ee7123,47ea979a,d4c81abc,5d5086c3,6e191d7b,8017a675,e8123b7f,1f1351b5,ef6ed6b,1b225cf7,48941df7,62f0efe5) +,S(3306922d,43f2c4b0,65f6d9c8,d1289804,ec26cefc,97090fde,86dc3e0,fdba5657,98a8cc9f,c93dd7ac,7079f76e,fa1c4065,86f0e941,9f87cd88,1b865c30,13c7efc4) +,S(b35c7597,54d37f12,440f7703,39223681,8a1b9aac,b085fc1,8acde6c7,3b2fb001,484b90,69b08485,95c58ffb,df5968e,2ea789b4,6f93d660,a7af6b75,13cd649d) +,S(9bd41b53,7f8bc303,6cc9d010,45922b1a,5c647f8,e90fd587,86ef0975,474e1f33,590afbf6,55ece125,da486bfe,1ff389a5,8b866776,8d2013ae,c25d03bc,bf0274ac) +,S(2a3a672c,a9b3aef0,bce89703,a38e0dcb,6950d93a,7359420e,5278f160,541ded42,4c3a94b6,13f3383d,b908ebef,6b62bab2,c95f850c,f88aea5f,c6af3b89,c2693d8e) +,S(31ba6af2,a2d94f8,d4df3800,353806dc,c8db945f,16dc5bf6,93794382,58f74d3d,ad19dd02,b290eb1c,ba5d915e,f60344c5,fc1e6a82,c5427f4a,236238cc,2329df5a) +,S(fa2199bd,74b014f0,5df0f9b8,ebf44a98,587c0251,e9c49bb0,5bb12952,bc869720,4328917d,6efdf5f4,b0f5c483,860e1e9b,41ab6ee,b1f5631a,66d2d213,b163f45e) +,S(417b9046,c7f53c0f,5ccf2847,692ab63d,8aaac02,e3bfd99f,30a469a9,8be22d52,78c15e65,5f309788,dff6eae6,91706e00,7aede9a8,3bdb08e0,d495d6a4,863b82c0) +,S(56ac91f2,47d51fd8,8a0e6eca,67a69ffa,6158f498,1714964,f33e43d5,6f18bc2a,351b7d5c,9e1b1a4b,717c518b,37b81c64,429beeae,26e7a740,c54b1a66,dd0680c7) +,S(aeba32f8,83307ef3,152d835a,a0babec9,e164ae70,10c90250,68507d65,37215f8a,8d747d39,35cf5f2e,6ff16557,7ccef843,7ce7e3c6,a41cc40f,dcde2ae8,582c4e21) +,S(42c7b219,9af00c97,693d6bcc,256ed047,79ee10eb,e4dbe63,d47d681d,21db5813,a15fb708,1fb1c7cb,2ce44a4d,f8958432,a5fd875d,1fcf7c75,c3c4865b,b219378f) +,S(4337b297,ecf365a8,88bfe19d,1bcd5fe,25e0dba2,9c39e2c,36ca116d,a24e5d98,49c723ab,c6541edb,cbc83348,5e06db73,28d1ef8b,c9cd9722,915415bb,2242b7ad) +,S(18998c76,1481932c,e968af0c,eb84b250,aeefbf25,27e4ef84,a2519a1d,b7f857bf,455ebc85,64518443,5cac9ae7,7f3289d0,91133054,d0bf9ba3,9b7c8128,26c97fa2) +,S(64c76320,fa24572a,590516f,1bfd8b4f,87767b64,1e65588e,69cb895b,6a49b2b,a39a2a5,c4795095,a03ae76f,86825ec1,78366c8a,8799d395,d39fe8cf,74b9603f) +,S(41657405,e35eb87c,a5ba5f2d,3c6f12a6,dea917a3,3b3b44e1,ebbd3bf4,812b0ad7,8e2169ae,36f1452b,4577fe78,7adccb53,4d0ed75c,2096b491,6b68dd6f,95ff815d) +,S(aa200fa3,768a4b89,16e110c8,f4a6315,7bfd4a11,2c10cb6,8b76e30d,c0391f6e,567bd959,28b21555,dfec898b,d7d198a8,c49b79d4,6905afde,7fb3534e,a584695b) +,S(7aa78328,841d2f54,b843ba48,c8c9c623,eb529bd6,9e584966,555f9bba,77131599,6b4a2715,e4b8478b,24da7e7a,64e2f6f1,32711246,29a583ba,1bb44af3,69f698ed) +,S(94b1a7bd,6a2767ff,459234f,c78fbbbe,48446b4f,64bbdfdf,dae34475,396969a1,8f3065ef,2273a871,dd29320a,8b9aa41e,e77e2178,e8b8854f,df1e88cf,991cc33d) +,S(6b957ba7,1bfeebf5,294c8cb2,7681e0c0,5407df71,37a34710,35fd98ce,d526573a,67e4808d,3fc1853c,5e5cd4ff,6efb5092,35c6df0e,c0a5f7f4,583fdb88,7a2724a0) +,S(8b274330,ea34100,3be93892,ab1fcb2b,f9a6dfb6,80bd9cb2,b8aa6916,43717f7f,54fa2abb,77855883,e627f828,49c71389,4ed141aa,9c9cda3,c99afaf1,7998db90) +,S(d3f64f9b,67362339,491274de,77ac665f,3b686372,499a12e7,5d145e26,7b58db05,c06116c8,52bd15f4,145284f4,3899faa8,9bea5850,4018310e,5c82a050,c9a52449) +,S(198926f8,ce0e4329,1bb9280,390f795e,870d071e,801b4d4b,c4aeb555,f237ba79,76125bd9,5b8d07e7,5e27658f,a0dcf10f,5d6c10d0,4548dd76,a833f206,d2b3d34e) +,S(1bbd4d,595152f8,39746cfb,6ddc2584,ee4358fc,6f878309,9adca58b,cf01a13f,18f534de,cceac5ea,7083cdfe,3de5ebdf,ebdc70fc,4f0856e0,b587e9fc,df8d7733) +,S(88737f45,b2639272,37cb551f,52022693,52457f,21db398c,1312e275,f5a3132a,79a5672c,2895e1e8,4b1a7b22,21ba0c5e,e3e61cfe,c95aa029,b2a179d,e0c496ed) +,S(cbf3e18b,1f277773,34cca35e,8fe1cfa9,2d68497e,46e7a252,4e14bf,cccb1e83,db00c395,65bd456c,3c9d242a,5f24748c,7b91f770,926ba9a4,1ff5c94,86bc3f99) +,S(8070ef0c,f186efae,5f9c8846,72a93a98,8ab65b20,2720893c,40b1a641,c7022f70,da9805b0,46946a05,46824cf5,a87fd838,cc85c853,a745d8ca,a249d881,127bd9e5) +,S(acaf984b,8fa45dbb,e84c5064,8b6fe23,5c57dcb6,21243e21,90286980,c3f71eab,46f4ec16,8259fa2e,f144dfb0,bbae2907,801b8c1d,f17dd0b5,d1db3bd1,2ceb278e) +,S(2893a05,91f9ecea,735e8a32,a57e3d25,e8916252,52a44099,4eeda971,fbfdc965,6ea29941,91e4b123,12b5186,efb7d255,372aa6c2,aaf12a1,8c3b15a0,7bcfbb6a) +,S(64974631,68f0cb60,9c926e28,5ed12df0,cfccab0a,58f11b39,dc05b644,c2be951d,ea74181b,2fa885e2,4344bb21,9f2509c7,d432b5a9,c178b406,160489a,31eafb7c) +,S(b938443e,513394c8,b2d4f7c2,9811039,918621c,5d3a86e2,d1bf0e88,ba1faeef,af47f2a8,c7d98642,50784f38,45ff9414,c52473c8,9b669be0,96387daf,c353fc85) +,S(bd1ee245,ad10f50e,9f8fd4a7,876346b9,24f4758c,cf69bb8d,42f6dac5,9d2a5320,776e2e3e,69d88d5,c26477ac,74ffe92c,134ce443,64b39518,af787348,e0039605) +,S(cb7ec68,805e855f,17e7dc05,4279d8fb,6d8e1320,67a486a9,7d49df43,c660650f,f50790b2,84c04103,11f302eb,71525fd,b12d7339,6d259b5a,91ce4fd8,229e903c) +,S(ef07cf7e,affbb5a,29e22a1c,7c84cda7,1968c615,fbde2a15,e1c1211b,ab30cf83,98f4a6c9,422630d4,103fc2fe,e4c80f2c,2ca5565e,25859979,815c425c,70ef574f) +,S(4ffc1948,4d7fcc59,ccce4ca0,d11400f1,13dc5064,8626781,4eec26ad,3e7b8fc6,be9ebe07,98ff3d83,cb5ac107,5dc87f12,d94b65e3,937ee669,61b4e587,7fb75aa9) +,S(cbdd5373,339ed493,b8cdf0ef,ac6371b0,fad1ad6b,2da4db63,b4ed68bc,61d18396,527de487,c3212d47,1f538b6f,f709c130,3784a7d7,c803e5fa,c77bfa1a,d6bdb13f) +,S(ea035b2b,d8a1d7e9,db7f6b06,5e003f99,4e650aa0,e3035fed,65e7bf5b,c67cb5d2,aecb4370,2219f488,86772e91,afe81c6b,2cd57e96,2a3b0edd,dbc64608,8c3761c4) +,S(de1a4fd6,c1d6240a,d6e9dda6,b2d43fda,911648f6,901ed0cd,99ba8ef5,99f8db89,effc94d1,61c0ff41,d1ca6085,371e2e62,f85f90b1,542d2cd,3a4223ea,884da03e) +,S(2e644141,e8d87ed6,e1215787,8bcffebc,d75e118a,f00fb7f0,e58cf5b7,e6a076ed,4aa858dd,f2ba62d8,6b8a7011,26d22c2,b45c240d,f65f85b7,320e80ff,cb34b8d2) +,S(3844386a,90d6b878,90fb78ad,8f2d28e,47ff8879,2d275c3e,956883b3,71b73a33,f7f5df12,1a8149c0,75f6bde1,2ca8ee89,ff284557,f17cd57c,714fd474,b365cd18) +,S(e1fe9b32,1b91d362,2b618cd8,9ae8203d,bde185e0,bab2fae8,ff7e96f0,74358e42,c590fe90,1797132e,a25eff10,f2bf2a76,cef4f064,acaba1d5,f8537b4a,ddf3e1c4) +,S(560a00da,2f57d821,b6a0144d,45898af0,aefbbded,a1e08a49,9cc727c4,b27a3c0b,64874e4e,39db1377,3bcb79ff,983b152e,5db86d63,4dbbeeaa,a8b0112a,e795e208) +,S(9f33fba7,7ec8de2a,c1b16424,856e2814,1afe9b1d,bd0cd1dc,e5cbb2f2,f379094d,97cb8cd8,1838e2a7,26034f19,b5decd61,d972abd9,bffe3848,92fb0a9c,26fe94b0) +,S(98822fa8,24057512,48f9da04,2a0cfb15,e46ee253,e06a1226,da75047e,72d7a1b9,3e9eea35,7bc54ac,da547561,1f9590f3,db975d4c,ce7c4cc2,a553df14,feb19285) +,S(b814d1ac,4b757268,6ad88327,32f0f84b,bc72875e,7e21707c,c4086a06,ac1dbd88,b6364488,3749a122,401b8ab3,c4a7b8a8,c03462ff,18d69c36,cf145299,f18a98) +,S(246484c4,1e42113c,cd39f7b7,eed9dfcf,b39cf842,85c28cd8,6233c1a2,bd043e97,f35025b0,2e31bdd8,41d36f7,9d0b75e6,80f431eb,44a50502,3877f5d0,eb9552d3) +,S(9f330365,7b41b2ec,d87fff0e,4284e4c3,bbe8c524,91d358e,847afb98,ff51db91,4b9a7bcb,221c8870,de176a43,d4f7cce5,adf519c1,75613dad,317f4337,72d5e70d) +,S(c2e78e9b,3c75433c,5f5623a,8f5a4dc2,b0e71951,4b16161c,225b9bca,9d529d25,1e75c8f1,938376ca,c6387b20,9b24a2e3,e794362a,d62441b9,50588a3c,ab9079bb) +,S(89340ec4,b6e81b7c,855c9636,b548aeae,2125c40d,2907f86,7ce5e6ba,a7695391,f739a94e,263e796d,df1cc2c1,3e2bc10,2b2a9c9d,55248671,575c3ea3,792c72c6) +,S(9204ff15,f1443cc2,78facd4,8d38a6c3,5d66a93d,8fd8e2da,a7e2bbe2,d6629d22,2b9de875,456b50e9,6205020e,c293ff3c,13120555,82727253,be10263a,7559c019) +,S(f156da10,41122fd7,ce6b518d,c325f5de,61fad9e4,a7bd684d,7abc495e,ebff04d6,c73294ab,f4f2adfe,dcbaa78d,2031a5d9,7f2c404d,739f5703,76e46a0e,1b631d4) +,S(771a2d78,37d6413a,13099861,6050c362,2c379a55,26506265,9fa7da02,3280fa75,8b6bc39f,5cfe1f5b,82bef0b0,f6c8dd1b,e4270304,d5bc70f6,6b600b26,5d4c9aca) +,S(14cd58e0,36bb2c59,f63b52b1,f40e51cd,aec29f0e,b1cc0401,8e512366,85a18f69,79c254fd,ed354641,744c7262,39ea4e8f,ea15b19e,746b372e,f270c8d9,9c5e2006) +,S(e5f54077,1060072,5800bff1,390a67de,cd539cc9,52b6b43c,22bff27b,1a8633f1,d6e1c6c3,9baa86c7,5838e691,f9af8a06,80274da9,b0f54b5f,4c3b2028,8d666367) +,S(b0eaaf42,9c072037,fbfd14ba,79693d93,6791344b,3d37c3d7,5b8eaf8a,e7429197,d72661e,4a171b65,4f124d9b,d0201710,bc237382,6a2f0341,51051716,9b7af26d) +,S(e022d70b,44edaed,35957383,284e4ea0,5380f92e,cf62ce88,3c2e11e6,604d67c2,423c55d4,3ba0ed0d,1455228a,3153e9c2,7aed89b,2c3ddc71,52d2b11c,91ab4143) +,S(92a2d73e,b2eaaaf2,65a70f07,b949511b,af1a3eda,9e5d8651,349a665a,4bc5ab5f,4492d419,4794d254,9a089fff,53a32631,5f1703c,5ac34049,92d68121,e00fa973) +,S(43409686,de50a9c,cf35ac44,47a9ceeb,b12f102e,3ee6003e,c96235a9,8db9cb30,2f781040,952e1017,8e4e38ad,bd9c6c8b,c8e1e898,7f316cb0,37d2cddc,4e10bba9) +,S(d349dd1b,643f8d4,1b83aa5a,d735dc4e,94542643,3888a4e1,aaaa0ade,2394cd12,67f2f057,1da2b6ac,295c71c9,ea00efe9,cf082257,ece35bc6,72adece,433249a5) +,S(9a909a95,7fb87ebb,35f6834d,272d8ce7,babad39d,8f8323d9,c6813781,66dd10e6,ce3ead1a,32771a28,7f624835,2a2f6001,c12f16d9,f7093cec,86386572,a2ddbca1) +,S(f38b1e18,df2a2a64,9a3be25d,3686b5fb,1d77f971,1214d452,a67bccf0,ac07fa0b,5c9e9a,7f71b201,ba11ced5,9c0a4623,419a20d8,c488f9d4,591671ef,71e65ea6) +,S(42360fca,26b8d095,df52738b,c2762ed2,7b909784,c12b79a6,a86e06c8,31535a67,6b6f4ce5,e43ec003,be88bc43,2bddc453,db93aec1,657f6f66,5dec1912,468345e4) +,S(e3d7198d,a7c5e3b9,997993ea,a32377b1,b87325c0,5a002a94,53404c36,303f4a1e,72437218,ece6148c,df742be8,35fabfe4,a05f19cf,3205e65f,5c813781,2053ef4d) +,S(39412945,a8301110,6b54f625,8191529e,2f93db5,bd68db48,d6bc6d9,687cb59b,b8db822c,60f5c319,18b05784,40a958b4,e08b0a80,a1015d9f,d2826506,f250f08d) +,S(4d275f7,7e0ee98e,76b857c7,ed9368ce,d93dc914,d54ad4f4,21d2c097,cd241fc2,ecb803d4,9b502911,f619d361,f952ab13,935448e8,90cc2f05,277e48ab,a39d7473) +,S(29364876,c1db9cbd,a3efcd5b,6599ed59,160b145,3c8a9e03,c013f807,869556ce,2bdb4af4,cc44950c,84f56237,95941f7e,43ffb56a,77df186c,2dc31910,f23f6ede) +,S(7531f4a9,63dccdf7,ab83ef10,d3ba1333,6febcf41,f4b21e55,d380ee6d,e4878034,4753da9f,91d56bc2,1e9628ca,2d6269b1,e1dcb1bb,87c55e,9c44034a,1ba36ae8) +,S(8266d626,3d12feca,440dce7a,4bdf8ac1,6050ca28,5bc16777,a3d9450c,5c286e08,2500f2bf,fb8a4eb6,6a5e7e36,399f633c,96d908e2,5cda4877,5455df2c,afe2328b) +,S(66480001,4f339f15,8c39b26e,84389f87,6d5e62aa,59cb63d6,9eaa86d1,4bc589e9,a0699b3f,e3eb5a37,9f078526,375ac319,f145ffc1,da8a42f5,96c7016c,4ad92e2a) +,S(f8efcc85,8b9c0fba,605921b5,e6c89343,f641e2b8,e1f45134,86b77ab6,4cc70d49,ba97202f,7086f0d9,af5a0ae3,35d9a3c,5f4de2e2,79278834,f8b1d875,50a9d607) +,S(a5c1227d,254324a3,b1ec627c,93abfb3d,6f2485b0,1d8e2e73,6c542e09,9c992540,c9189db4,8a163bb5,6d99bed7,f38a7f7a,64cfef45,cf977da5,a1d16dbc,86ecc7cc) +,S(271b2619,a5b253df,fbb655d,7702dec5,6c85bb00,9260d893,8e050606,b8bbaf3,58ccf3fc,22cb8f27,dcdbd174,6abe5591,72bca7e5,d5af3bd8,d86c9a49,3ecd2ce5) +,S(c9e1382b,20c79741,d81800c,e721d4f2,1202ba30,92cac36a,d90c1dfd,570ae16,7eb1fcab,2a47cea0,3a92f707,d799749,6f7a0dfb,f9a43f6,90d213b8,1231e5b2) +,S(574b25,65232a04,1d58cb75,12b48945,45897e6a,8bd47dec,b1e0941e,309f2,bbfb86a9,567d74bf,93fbf2cc,6a9c9dd3,474db8fa,c2b5fb72,38b3af8,fe4615a9) +,S(9efe86ac,a6305eda,4b520e00,4dff6c56,1135e7ec,6953d2b4,a988d508,63046c38,4f92a291,32cb0818,af7cbe67,ec3b4d8a,7afa4f79,91a9ae42,551ad831,aae9af70) +,S(82ea48a0,ff76fa17,374bf4c3,d0918e6a,df26cafa,33d87284,13aa2dcb,d813f254,6a029539,9bb555aa,39de1ca1,417a8950,49b4c2d6,26ddf8e4,a7086b1d,bf2ac9f2) +,S(c289483e,817bb06c,31352a24,ba60adfb,e3af2feb,de329f4f,5fbde755,bdd2baaa,3b7ee90d,8b4aecef,62b6854a,1feb595f,9c49945e,c19685c0,4b13a1ce,1c780a53) +,S(6bde46cb,b5580d58,ea68deb3,1b0587b6,1698660,d78a8300,1c740b4,96e12cc4,3188a6a7,c1cd166a,4894ce75,c79d076,a41cc5c,d142ed91,e096eb32,e9293292) +,S(d1d2a3ae,2844615e,57f0a11d,66fd7571,176738f,a066cbdf,4519ac1f,65827cf4,7c0cfff7,c01c196,8b7697d7,c51562eb,8271f936,5c7daee7,f963122,67f603db) +,S(b74c0c87,4d195557,3b551c85,2abf9a5c,e276b088,cfaa0be8,f6515ed7,ba27370a,f0cb0e,380c571e,fe5e307b,ec4ed8c2,6f0c325e,114d71b5,51e0f1f7,45a662ab) +,S(a9a71b9a,9f126c3f,a17c3041,2317e118,84950077,baee298f,47d42609,f9015874,c05f3ffd,b31bf0cb,7fa4be1d,f634cecf,f1b1137f,551220f0,8f34c2a6,a42e0586) +,S(d6de1153,a161f27,ddb51416,2b3f823b,2b1b99fa,3fa21c25,9b02274a,80dbc68c,6c620863,659785a5,eec15d25,9acbdefc,714fab61,4e766215,348c80bd,bc59764a) +,S(dcaa1dbc,3acdca5c,51f8c22f,359487fa,73a7dc36,2a796df,11292a74,776b73f1,dc4131fc,c22c607b,4833719b,1715179,b4b4d49a,958c7514,7c8c4a8f,cc85159) +,S(135537a0,c4be13,1c3aa93c,677094d5,5f3a1300,95499d21,2206a244,5ab6c,7ef69f1f,d2d3df67,335f0d1d,dd52370b,8f3ebcf,e3483330,48c2f045,64fb0f69) +,S(28f1d817,c23262e6,4cd572bb,e3fd99ae,751dd6e5,5e9e804a,fb3fe9d8,39de93ac,24f5861,987e3da4,2dae0e89,67c835ac,8f5dfe8b,d41e5cb0,97029b48,39e2cb7e) +,S(f7c4ef83,6e8fb7fb,8eb31155,c08102df,e0779b87,9ad641e2,5f8ce135,44294289,7f9a781,db339621,d6150f09,16eb9682,16470d23,6957a0d1,3629964f,1f6bce2c) +,S(f1f5ae5e,1ddc9b35,18b66f7e,25a735d4,ddc8d018,5112faed,a8da8f66,1a28b3e6,9dbff12c,97924d80,50a4a8df,24fe7a8f,5c5c3486,3edb7cb4,1c6d9ddf,b9ad4288) +,S(95f83173,d170ce9c,1ca0dbc7,4578b639,257c10a1,eb65827d,c2afbd73,8b654f,63a83ebc,8294a88,413c3c91,c6918a31,7d5ab83b,7bee35b8,b75d1cbc,3412487) +,S(f4ada65c,9f095e11,eb88c8e3,77d60553,96474935,91be3a7d,f8c83c09,6a3ff750,e7683f28,a38545b2,e6cbc87c,3dbcc4a5,2ede1676,1b0dd78d,3ac51a5c,ad861ed3) +,S(41afac96,f714db3b,1b7bcd59,1dc7bc24,40b82756,d9394fd0,3afe7ae4,299e5fd2,9c2e9c6a,3292ff12,426e98ae,ac654a44,61c52d00,5da7af20,474e9a47,499fedc) +,S(37f30b1b,2cb81c9b,61941fe9,cca35f0d,e1b8dffd,c42f3ffd,ccaa16e,e8487405,92bf7a85,e3401d51,d9fdd08e,1086b649,8086bb3c,a4c9a2bb,61b1d54d,8cb88a0f) +,S(2f577166,646e7eaa,21531202,2ce0c8eb,605a2199,454c205b,9d760716,584fb3ce,f0d7326f,e43dcead,5ae0dc68,4d04b969,af32e8da,ed624ee0,98567dff,3d5f446) +,S(8abbc780,92c5512b,5eba0af9,4affdbf8,3c2e24ff,bf9fe7d,fd57e5ba,64b5150a,58f5998f,c2ec19a,a495b974,ffeccf6d,bb87dec,49152327,91a167d7,f6896b75) +,S(e41ace8f,a01376b3,27c73fe4,7e57cfc,f6da7cf5,c2810aed,219dd065,c147571c,b590aada,3c2a714c,322d0459,272f98e5,715d4545,196127a3,7d8a94bf,776844a2) +,S(300c9877,8b6355ee,ddd80f7f,4124789d,7c0c84fc,9d9ebb5a,be714cd9,890f1d88,dac2ba62,c5718731,c8f5c7a,4e1a7b80,68c5076c,834dd385,20f5e93b,e39c9002) +,S(69e339d2,19d40f84,25a072c1,f7876c3f,dcdb6623,26ca1b5c,87681707,12e24c61,8d439150,59a44700,2157e849,9b535921,4cd47280,937405a1,bad0b86d,38babc50) +,S(e56493df,8e47a99f,8ab3ccf6,41fab7b4,cddd779f,51fc746d,a2fdd484,cbbdb909,68dba20a,559e14fe,d0806036,d41c18b5,9745319d,758c1145,b6e49ca3,e3f5ee59) +,S(37a91c57,8998511c,d39c11e5,86e00eb0,f76532de,574d9c61,1a34d38d,e7f50587,e3a188af,d2d8a24,735975dc,9da895,feac2710,ab9d381e,e5d1dc43,b3145f18) +,S(c5f059fb,5cc262e1,12c629c7,194d783a,6224016f,65e4d24a,86c52742,dee88c1f,1c85bf52,1a96a95a,5177792d,590b46d5,8c090705,1efa0623,ab15578f,b38c890a) +,S(566edecc,d605ceb1,a298c59d,78401acf,c6323c63,f2845827,62678924,9b8716a1,ee0d1373,5adf6f20,789f85b9,32d332ff,8c40b3af,c02010a1,5ad062d4,5a44cc90) +,S(327e7033,c6ef4f87,52e33d,bccb4642,3e6cf4e2,c3ebb4c8,db69ccde,db2c7c7b,fc49092b,2be0f29b,4aa3e266,709aeb1c,12266239,f7bd9263,4aae16e9,53e8db36) +,S(989a7f69,3ac0ac86,55f29704,ea6caff6,84ef5ae2,8c5f5e1a,1eae7009,97778024,573d127b,3396458e,74bb5bd4,4acfea64,e8eb1fd7,3b0b292b,d1b7b642,80dadfd0) +,S(9ca7672f,14f27f54,ed1cec4b,74f49a57,7ce313fa,4a665976,9c22cc61,b30fc94,f920c4d7,31d52a4a,30803f81,82df8d30,6ab14104,eeec11dc,d2d6ca0f,3904e4a1) +,S(db100645,654fe8e1,95831687,17e2721a,6097850,ed2ef0cd,e1d5555e,7847f5f5,2b2c3ece,a9c90ecf,34fcdf89,5f76c8b6,619287ce,e745014c,96281928,31056567) +,S(4e2a0c9e,82743b42,5e6110a8,f3bff14e,418a34fa,65269c07,bb94d00c,d53295a9,512ac2e3,e00bb04b,6b4b9059,a3ac69c,8a34e5ec,8dfa14b3,d98b2c4e,3cccca7a) +,S(c12b3f70,24ea8acb,5136f98,185fb1cc,60c71a77,2943b68d,7334352f,66ac77f6,b799f99b,6f4b76f0,1f09f5a7,bfbe2526,d67a195,83327323,9c07bddf,2544803) +,S(64e5a2e,daf34e1d,ebd7c0cf,616413ff,d6c09b82,bfa783fe,aa755464,8ffd7e58,e020e09,66f032e2,e905d611,adf0926e,7509a2a4,b51844ff,72651f34,3c2f4a50) +,S(2b0b8b55,fa734380,f6ad1857,e03f02dd,3d66ec57,2cfb3b9f,ef040895,3f0a838a,ac897251,a9146597,8e0b45ba,e1c880a4,147ce01c,3408e476,5bd9013d,9e73a61a) +,S(ae0e3d30,a2026133,d3a54529,965a9679,6523e8b4,1efd4c5b,d6778930,2a88525e,fb6918ff,85632d24,250be67b,339ab260,c80c087d,da1efee9,7ec5dfb5,38f80552) +,S(2ff9c711,4dfea166,ffae46cb,1f44c3,3bfa1530,edf23454,a83de862,8fe00dd9,53f667a,4441817,a3e38351,78195c5d,acb0d707,bd20e759,9b5e82c4,6f318783) +,S(cc027014,88b959c6,c043f60c,c42b1449,9e2614e4,c19f5726,3edb116a,4e7bf695,eab3f590,9b22f677,666ce655,149a5f07,7b5325c,2036dfe2,e572e64a,5bb6007a) +,S(1801efd1,3c8035a6,5efe3b47,68495d27,f9dbf45a,f019d455,84d68be8,28a945d5,dff2d19e,fcb5968e,f4ef49d5,9a82f8b,f44a5abc,e0b68568,1acbda1b,fa59c4fe) +,S(6da2a9d5,7b60962,357c09de,4c7ababd,5b698fe3,6e747f8b,e6ab78fc,3cdd7cbf,1e274e70,4aeb8ae2,295ac9f9,b37c0af1,71310b82,99acc378,d505bdcc,e275060c) +,S(7e8fc96f,6a9215dd,3bf9e8a6,ea944ff0,c7dfe23a,1a99f44c,832a11ad,bafc82e4,e9a4fbe1,e0c26ca7,f739ed00,c63f3886,57d09503,a808ed51,ddc872e3,b1342bff) +,S(a6362f3a,77d6d6f7,fe266ab3,78707641,9f53e133,718ccf27,559bb448,7e0caf64,c01622da,3fc2385c,60a5cd86,66f751b,e29d8539,f473cd70,8784d61,d2cc787e) +,S(51865222,28b47493,a3da19b4,7076c1a9,8582bd55,37c25557,44970075,9b37d2b3,f0f0b979,68638d9d,93fca65b,f40cb8ce,826558b8,52451ad1,7d0afbd0,44287be1) +,S(a2a0909b,a9f1b9c3,8c714ff4,f71995ab,4d80212,9e8ee8e6,7e8bc3d1,81d472ce,4123900b,d45ec7bf,71bfdf98,ec453392,8b364d1e,cd925a6a,1e8f5c5e,ec40068d) +,S(da518122,454e7f36,a63f5ac2,a1bf979e,f1357bea,f9f0c921,2b031f87,b83aef2d,bf877efd,d415e80,c828c5e,1b9dd8b9,6cb7f3f8,b9a0cb32,b6bd8f37,686da722) +,S(41c40c54,1a983125,e59aa572,910c5aa4,8a698306,d483f82e,6cbd7506,3947a87d,76dabb65,6b89910a,bba9f129,ffbdf5e7,ef840033,c30f4c81,4e86f101,a3bfc57f) +,S(c49fde83,87f2e464,6847c980,acc50d88,28745e4c,88a41318,709099cc,9aaddbfd,a65bc512,dfd07bd3,79cbaf9e,628be7f8,c7ddb4ee,6611fa94,bbb48c7e,ae7227e1) +,S(85b17d61,b6ee743d,4df21940,6d7b7426,3a6c6b88,e8c51071,7a1f0183,e0fceae,22b9e8a2,74c71feb,5046ce02,2f4bffbb,ebddc53e,704f6be8,92d3092e,55868fef) +,S(c8e1d37f,e6d677f1,3e752189,505639ee,3914afc2,c8d4e43e,883f5c54,f117964f,b0cacd18,d08f5a91,af46ba55,91f99497,ee4a7ab4,36ab48e4,f88ad5ab,39bfc21e) +,S(dfec0030,7d25888c,5a7d6f83,33112995,1a30baa0,b97add9,866078a7,e483836b,4e5a8a06,bf2e9447,ef466416,82af8e44,f407ca0b,6fb97809,56e13a90,bafaa00d) +,S(eac6c438,7c18d8c6,eed7618d,c9d3076e,c842ff6e,5f4d4b20,92594d7f,7f0be9d6,19d69bbf,fc0c1b82,ba71802a,c0feebbd,77a86a67,d734a163,4e823a6b,2265b8d1) +,S(9ff38c4e,4dcddec,a4824db7,97f6ac80,69432c15,9cbc99b7,4d642011,b3424cde,f72fa660,b7c1bbfe,df45b0c9,3cc84b81,266bd041,b94f54f9,1d26221b,51619ebc) +,S(5e78593f,3a5a3293,5f9e4f8c,4fe28555,9153f69d,d44b2c1c,146fc644,a5313f36,9d9d5844,dc63bdd1,b2d95b70,5d63c97,58cbc5a6,1b2d114b,89da9b9a,8afd9c47) +,S(a107c7a4,89284b6f,b01ba38b,3e221c2,ad31a48,77af9b32,d22c8544,71009ae4,66072d4,6be46591,ee386cd5,bc0f9f2b,653b580b,c515d4c4,a0fc369a,27d9f6b5) +,S(241f1d0,308d9845,fe2445ba,67929976,ff98e88e,cc23a8f4,190ce5fa,41b02af1,886697ec,d413a88d,fa15723a,daf91d32,9c881ee,c5ac9c74,366b7ea8,a7e194a9) +,S(319ee8d2,627f906e,83d5c6d2,6eac3b42,34186016,8b9a9283,c8790c6b,e48a72fa,59e496ed,a67816e0,3409f849,1cb458c5,76034453,65fb281,9d0cc427,3e9eaa57) +,S(1866811a,fdf9bd,937f4f7e,96985759,e0100866,192878e1,a2614625,bfca4822,215e0465,e85f9c88,2783a131,6b65fa7c,585f0319,1a642501,72e51269,d5b7ae19) +,S(7effbed7,b949000,5db429fb,8cf64099,3989675a,f7394ce6,9ff2f769,53992f26,6efbb5ba,28f26d4b,5256affa,cbf2dc0b,616d8b6a,c6cd2aa7,a1d09ac8,40c7c749) +,S(d8c72bd0,e59315f2,1fee45c1,937952b2,665c8d66,70f14bf0,957a4d25,2b168867,64c02d62,fdd40a6f,d11e2cc3,a73fc60a,8a2da31b,2c99ae6e,5a946c61,be56fbcd) +,S(710ec622,ce64f381,43b96bbc,b0b0d691,87bbfd51,fab4d47a,bcf18308,18d0db4b,edfd1086,526c3af0,c2e9b961,890f531d,3d275c36,beabb0c5,4963314e,4534d994) +,S(9427964b,9aeaedd4,9c168e76,38086ddb,70574f27,9e4c3348,68e4c595,30d51ca6,110b41cf,2a6b0067,eb3b312e,928583d9,e0026bea,684c57af,7300a611,5c4e2dd8) +,S(aef79435,a8b3ed9c,9eb515c0,6db62165,c5a2e816,5eaa4a5c,46a59b39,8f55565f,6616e8f2,1274c425,c50cb93d,8942aab3,3653511,3c3552a0,118b17a4,5584b91d) +,S(3ebdd832,3546063a,68f40193,59d72f26,f49f0ce,4a5af994,a4ac67f3,3074b5a3,12367d4,b29549ee,d0eba318,f395d712,db7962f2,c8e2d9be,ed895006,78460238) +,S(a38d9e5e,c52b0871,d0bc6bc3,b673c848,19af86e2,534d60dd,729b405e,426aeff2,ebe46771,d9ae6c5d,13895538,3c8de4d5,f6d13428,8ac36a1c,b01c75e9,f32cad02) +,S(e7d29f82,619cbb19,90c407f7,b452cb8e,334369a1,16901b0f,9f4ec342,48a7d624,7595ced4,618b5160,6b8ce230,45d7616f,15b6ce14,aa8eee07,42bec5b5,9fc4e614) +,S(ec869eb0,279227b9,4ae333e2,6018f00c,7523de5c,e3e8c4ed,f81cefcf,4043ceae,a421aa55,2c89c841,a48bdd26,4e67d82b,ca3fe577,64101556,ec6ba7dc,711ac92b) +,S(4549d908,e44a8c8b,6fc5c057,144a352a,6f07523c,e026fae7,c070ff35,c6ec5e71,71d8e320,7707e04,10208c19,e2c8dc79,64b48600,1a6d640f,44c1138b,7ab5dd7a) +,S(51bb6e9e,fef7b8c9,b0a4f381,3e1406b3,2cc7a53d,165b385f,f98144b0,a5b070b2,65282be9,dd794fd0,8a31de11,d46df7cd,5268eedc,aadf3bef,de5956b5,a467044e) +,S(7cdc3297,4c7caa82,44203afc,d48e625e,aa2027d4,d91613e4,5a83eba0,696d001f,b92b9943,3b1a55b3,b78027a7,a289e5a2,6060a411,6c2cca36,19253490,83519565) +,S(3ca9c6be,b8e989b6,a67722f6,d817a6be,aa478fba,10146fc9,3099575,609c9314,5502ed7f,f4b81bc3,8386bba3,4124ca30,74c6e0da,bd148e4d,6bb7f096,ceca2c0f) +,S(aa2abe6c,45edcada,f01bc1c8,38ed5e63,90e1a229,cc920436,864111e8,ad354d0c,1b8229c6,6f91304a,24c61251,f1ba2b26,95bb3da8,87154d47,c44dc9f2,f824f6cd) +,S(b52cae3d,f28c9d59,319f226d,ef73b60f,75466aad,fbe6f75a,2ab6936d,8e3bc7ee,ebee735e,22c091d,cc873848,8a7c958a,421e0759,2e17fa3a,4462db88,8bcfe220) +,S(c1085703,62a6dcb6,13da6283,23496169,635469c3,47b65661,838d9053,3821ef54,fe6442c8,c1e6a8eb,2dc2cac1,40ad4c7c,44511cdd,82b79cde,c38cf9b3,4ceaf517) +,S(f09e9d1c,fcaf127f,59d45476,56422907,83477fcd,2fa6c768,d5a3965d,af08c11d,f01a722,1f44fb15,bf428fc9,c5d7efd9,a5b2e663,cfecc9a2,7e8f964e,8bc15d10) +,S(602462ef,1e473ebe,d88b3b17,9d71b597,fd3e805f,e03d341a,36c277f,495c4527,f91db461,71411dce,aaad9f23,53f2159e,fed2a9b1,eb9d2ce2,844e1f6d,63a079c3) +,S(273a881e,8e0d99fa,104ae355,8126a20d,6b828d13,96bbc70,720197fe,9bb582d,5bb26c58,14a7b914,cd3b2f72,7f76b6c0,d1b40775,79a2c3da,29534a1a,684b0d5d) +,S(7c1df346,616cb72,b42f4172,36fc98f9,e8cd0504,1da47d9f,566c8df2,e80cd54d,ad3e7e0d,d602b9be,45ba8d45,6bc12558,73153c2e,c16773b9,390ad630,4fc41dd7) +,S(796e2634,657ff114,7394551f,3ce800b7,9ad644b0,539786d3,c62c6551,3d942f94,b65bbe38,ed587111,c480eb9,c30081a4,1e8ec66a,8400db11,bb1cc7c2,a14f1c60) +,S(5e2761fa,9985f68a,a469953a,2a6642c8,62b0565b,602fc206,193bded6,1a302bd1,5ba09b70,9db24f5d,2f47c07,51367d0,3715c9da,b7d430b4,713dbac5,7253e274) +,S(46458c92,fd341a0f,eb1ab038,5a246b40,5c004c22,98db90e6,164126c5,4fabe794,859d68b,345341b8,58afc324,263f1ca6,9d018fae,eecf33ea,f02a3dd8,d7446725) +,S(fc481830,bf817c1c,ffc9ddc3,d6f4e624,a410e134,cbfb952c,5c7379a5,81153265,a188b228,bd3d7da1,44a48e89,fd9db4ab,8dfe385f,ace8e169,a43a2787,59322551) +,S(42d9767d,9771a550,3c702742,ce2aedbf,3a780466,804ad901,7792610d,5eed03e6,df69432e,9764f337,3a2c782b,e02d2433,d2e0f568,d0dd83ec,e89ea1c2,5ad50417) +,S(37122c49,993ab297,72e60222,615ed912,462deefd,d28e04dc,dc72f4e1,b9477148,627ff4f0,924cfbc3,aa0422cc,36402b8e,410b3ede,a552408e,f1d3820c,5996e39d) +,S(9c548182,94bafcbe,743e52cc,a76e1ead,3619a557,a5cb8bb7,3140ea1a,5c6896c3,5fcc472e,b88e8528,ec71dafb,f70504c2,ec0478b3,209494c5,b53bf05,b38f7fcc) +,S(7b9d7ad7,aa5b1f69,75c4e025,17123608,a59277f,d63f2a07,7ae69b80,f65a2db9,6cde1ee8,714aa229,d01c14d6,f514ecae,c4a7f15c,a756812f,abfd61fd,d7d1bf8) +,S(af5ddaf2,c985560a,d656d948,e41e082e,6990e950,6c77d53e,171cdea,58a9a102,e348451b,72896ff3,c4839ff7,6ce16b59,1f685348,eff943f5,fd4581f9,eca5b505) +,S(42667f18,e957bd19,5fab366,2e0d1565,d26394b4,14e2a9a3,95becb69,2111293d,288dd2b2,69e9bb63,4e7fb16c,1fe25797,3c0076b1,aecaa2a2,e57020d5,66bbc7a5) +,S(5e9c7a1d,50ab8326,74c2a5a6,f32665ce,9be97161,5e59b618,587541b7,40b276d7,92283461,1f39af42,34292c61,2c70744a,ec466418,ee08b5b1,a298456d,5fa11f17) +,S(98c60ffa,f486b582,2b4d9ac9,7b320b97,8b52b506,8b46bc48,9a2a4f28,dfed8b2a,a1c26113,c2534955,812f42fa,306edbe5,70416d84,aedc132b,5807b29b,745280f9) +,S(92bfa982,1ed52cd9,3c286e71,f350e5a7,f309481f,d866516c,f0a7599d,c385babc,a3fc9796,c71ab4b1,b7a54e73,8dfe24f7,6f527692,9516bcb8,2fae9ed,b2c1ddb4) +,S(9be27408,bd8eb59f,ace413e8,402b0c37,7b7798b9,3bb18c4,ec197382,fa5b865f,11085580,19646a39,68eed2c8,1bb92dde,def3ec29,5024c027,f54e8bfd,a67a2f61) +,S(27cd6a6c,b7bab059,6b791ce3,8c0918bf,7751bc59,db9e5827,5b1b9c46,976350bc,890d5167,8fce2ac5,f13761df,68651e7,2f41d04d,4feb4011,a16f80ee,83faeda8) +,S(e896e14d,aaf96139,4c8f6597,a0c66463,5f22be3c,96f30e1f,5d8881ce,f468f2eb,beb004b0,512c1ab1,dc4d971a,fd88baca,efea7e84,dadd2726,bb9f2b3,b196030b) +,S(24e2d269,ac485401,634b3779,efab2c0f,a9dd2389,850bb3da,54ec0d67,e53f8ec0,de702ec2,642bd4c2,3bca2ca,663a05a3,f5beda5b,e41fa460,8929aa58,219e525) +,S(97a627c9,48c1c9e4,5d50159b,4b121a50,9b5c4d01,de0a7db9,5a9d9c09,681d1676,19bd095b,55399c29,fcad4303,7f5d5c21,8d5f3cf9,a7c90b9f,fef7ee92,6f3203d9) +,S(a70d8251,e604ec5f,777234c7,f9d3c850,33ed6760,1fa0814b,40bd5370,7195b08f,df28d907,74e23de3,f152c016,ac710d43,85325a89,6a859f4e,e126d50,acbccc4f) +,S(2d676c43,4b276472,ce01a007,13d78b17,27743a97,36f3412e,a7c8c15a,fa42f42b,be28cde4,6ef5d32,403b8f94,1082b684,52fa703c,339498ea,24c08ff,c9fa7312) +,S(f66c25e1,75167275,5db857cf,cf27c036,46c89d29,b4a0d729,28fd42b4,b29cf5,3ee23ad4,22c4a884,4c049150,cd9c18ca,8d2a0231,bad69b6e,5926de9,c7193201) +,S(dba47c4b,9d39d87e,ef44f81d,24e8e005,ce6e77ad,8e4fdc6c,29393d7d,2427060,2d5c6492,e53d000f,977eedd,4dc3b5c5,5240f28b,9fcfb597,338edbbb,3d8d7622) +,S(a17601d9,4f627dc5,5895aeda,e91da3b6,94788f65,f083b01b,bfdf8815,27e10f0f,74089fc8,cfea1b15,afb025bd,99905b3a,baded5b,45e30890,fa3d8403,360a1183) +,S(dd70309f,b84afe90,7e261a12,5fb7ac8d,1c24c6c3,54eb83d5,188f3a13,4d80e141,32014ac0,ac93a77f,9d35b786,2c88ddde,4bdfd566,f6555868,2a836535,44bed563) +,S(79fd503d,e746baf9,701253f0,4c6a2f3,cadefb93,920c83f7,83b57768,87037afd,3c5cb658,63180931,3fa20c26,647a523e,86a4e600,8e8b17f,1e0d690d,eca61b93) +,S(d1aaf37d,77c7d63b,1f4eb896,16a29a4a,1e72658a,93560956,6b92c122,ba0ff6a0,e535b7ae,5cfbba07,2bdf0e0b,bf435bdc,c7cac39d,9d9ae32e,cfd13945,3ca93bec) +,S(68ff1b82,b351d94b,9b6b74fc,2cda4345,fe525d58,d6599644,5c752757,aad93c4f,abbb0ef1,c422ac08,d06194aa,c5d93bdd,9186e374,8518192b,79818f3a,b465a13c) +,S(22785c97,18063e5d,5db73bc,f4ca6487,14393ea2,62ece24a,5c41c059,9610552d,9c08026a,d7a7a80c,24c5e75c,8a75cd6e,1933e64a,25cb98c4,157181aa,ae1d8774) +,S(626cb5e7,563fb757,5f066258,3fc21762,e2678063,a77d8580,8891f244,4f5ffebf,8369ff6,72c3c3f2,43516184,5a9851c1,953a6da2,5662e11c,ce170cbc,668498b4) +,S(a99f0ef4,d6f46105,80ffa430,b88a4f28,61c8c4e4,b6fadac5,5e668f5a,80d12d2a,972adf29,39db0083,77802c40,6ae2aef6,4e2f064a,f8304d3e,28fff33e,b1053d06) +,S(b243ce27,2ff0b2db,cb80a3fb,dfbe94ab,784afe3c,cab3fe61,1931de9,c7f02645,db2ead19,fbb8c820,c7233df8,fea51836,3e85b858,4622be9f,41a0448e,360b7559) +,S(c74e3c9d,685cb62c,6a71b21a,a1dff5,43efb264,543222f3,f8bdf570,4363deb3,987d8261,fbf23d05,a38358ef,c4efb61a,5cd550b6,1e833ed4,38734486,edc4b2ec) +,S(ff2f64a7,fc984d4b,d1c885f3,4c096e9d,d34710c2,43dc3047,8886ef0d,ddf71c47,14c06258,fd5e2fdf,1eec9125,7690c0d7,9eac60d,785f2497,65e1a85a,a06e906d) +,S(7d92c4e1,3f4cab24,d36e8a8b,140f743a,6819b514,f6a19bad,ec162141,bb712085,606b5762,2ec7d7d2,a9b19330,9da686b8,3e0dc920,e6be33d1,b726f416,81b698c8) +,S(3a43d4d,9ef45289,89c9487c,77951602,4ce0cc20,ffbcd518,a0bd2546,fe13b6c,5f24d325,5768905e,6ba28d61,e6b126a8,eedad2a1,53499912,dfba3fc0,d9deb36e) +,S(8e72f02,80105e4d,15cc67e7,371d0c5e,6e59fd05,b3c66308,67342a6c,eea25f8f,94ddc51,58fdb86d,f09870da,47b79b03,1eada5ad,9bbdd509,9ea1d9de,96b368e) +,S(b89cfc23,6dc02762,ca77f9d1,8343f12e,ab8789b0,e5872d6b,fd9d013c,ccef54dd,9c9a7285,45a5996b,4a6ce6f9,df860d11,c3ad135c,f1f7437d,2ba7b904,411141cd) +,S(e85ee008,979868b4,bb190b53,5624b3b5,6744a43e,2c473ebb,c64e4910,41ccb59b,d3afe70f,9edec50c,227e3b3d,7eddd1d,e1c9a8b,f842d723,a8075a52,12cbd6a2) +,S(d8dd6bf2,774d6d15,93cc8e16,d5134066,f7f90651,39915b3b,fb0d5d93,b6cb97b5,5a203094,5f734f3f,11431862,b85e9f8b,25f7e6d1,91512b2c,6d256f4a,535e26f4) +,S(77e081c5,328fbdeb,45fbd554,5d133d84,33968ef,98d36aec,3e6385a2,76aa94e8,2ce853f7,ca25f2f8,45a6291a,3e080504,ba597fb7,91a21669,c0cb6be1,14c2959a) +,S(510786ea,e8a49682,6820b82f,549f07a2,b5b9203d,b1ad18c4,fb9c479f,67c904ab,dc130385,4e4dc69f,f63e21ae,71dc8674,b39382c1,eab1d8a8,fbd96867,af9cd96b) +,S(2fbafeb3,fbdad5b6,4c121056,e080d0c2,1169e433,3abc0dda,bb642789,a242639d,a85d82f,d9e6a14d,e599ef0a,11d3eb39,5ff199c9,5ea5f1c9,2f3f8a7c,14405cd3) +,S(200dbfeb,6e6e463c,1f3fbbc2,6b5f00c6,aec80809,1e28eae6,73b2b5e4,7b12ae0a,4852a219,bdaa3d97,83e1f071,15491827,83212776,fcd1b44a,c59dc01c,bba95ecf) +,S(27b140f6,259f7dc,93d46b6,e52cdb85,3ec22572,54218ab4,1acff2be,24b8a3d9,3ceed09f,70cbadba,d40054c4,f130e312,28bb5d63,71a74f7b,c85aa7ff,c0583387) +,S(fe4941ee,535c5311,8222ec35,14c2b02c,e175cca,d728ca0f,cd1d64ab,e95b7e71,e9cdc9ae,286d651b,d89cb442,2a64ada3,748e0ade,585cb70e,d357004e,e51f56f6) +,S(317d209b,4cae94bd,36f443d7,65be2f17,ce47ced,59935d88,5a03925e,75ff1cdf,c191495,68fe571c,ff945af,4b6a801e,c942a5df,78a62864,5434a5eb,8a262d72) +,S(f951a777,764e30e4,7705f8ab,150e9ab,df32e80b,593cecea,d9f9a143,cc93f9ea,efb951d6,25f2223e,e6ccc6b8,b12cddde,6ac2613b,11ae3052,65570f15,23afd925) +,S(71adf0eb,f5fd8e8b,f0f229a9,ffe496aa,4f251af6,6e5a284d,3091f344,3389c8ef,13f47463,293a5536,674dfdf7,2dc73add,98ed5672,e7676425,39ce655f,6b6969f4) +,S(2ea0406,58c0e80d,499cff39,ad463412,c3fbab1e,81f6eea5,911c45,236be75,be52c74,6073721,f09743d2,9a0a4e3,d82c08ed,feee2234,29163bc8,954f923f) +,S(42352390,4b3e80c2,750c4222,33f0b1f3,a373442d,96278935,d9b7e51,b6182aea,7f582349,b27b5166,30c8e48b,a9603224,fe09e8f,814fa966,395fed67,d9221d91) +,S(524d3c82,a92b285b,96064bf8,e9abe0b5,7702a29e,dc08ce53,771fab9e,fa7dda91,a47a0217,fdc6b2dc,d4b68e05,331bfd5c,366a8477,d3ed894,ec747240,f0f29c3a) +,S(f6ee1e08,5ce04de6,6aaeb5f5,f232329c,fc83caf,d16bcc48,776bae16,c056f7e8,3328b26a,7e2bd49a,aa193977,4947d7b4,ccbc955d,18b27c75,c2f6ac6d,bccea4d9) +,S(9c2f7d2b,b88f1417,722b5d11,95077d20,2ea84e6f,9bd93dae,3134d16b,17273978,1089f003,66c8b435,ce73c592,7c5e56bd,d338a4ca,ee5307da,a571be3c,2dab2a3a) +,S(593a1eb6,730fb6e5,1fd24f15,253ae1e7,5897b7b,48efe28f,9b9813c6,ca99cc3c,fcfb7820,fbd22d91,bc03a630,f74e519,9e35c1a,a3b1cace,40433001,7744f079) +,S(62ba10c3,83a7bacc,bcc4bc60,37f87a5e,8ec0eaff,de5a39f4,ef352b1,51f957e3,c267a1b0,dc84a161,a565fa19,aa0a2501,9fd01e0e,d7a660be,c2a7e9fc,cc7bdd45) +,S(1de2ac10,deae62a1,63f641fe,3a4a9da,39e12390,14a77c6a,6d73c619,5f731f64,60f43c95,b6d7969d,427b5ad,782214fc,a512b75f,a6f50006,d7141a89,dbc8d2b2) +,S(f11a5552,709083c8,83d08612,31772f18,c48e9271,6acbaa6d,a43416c2,fd4e1d3,a6d0ad64,7cf48c41,b6ce397b,38d5b3b0,2be51554,da2f3f4c,df593514,a2d742f4) +,S(f4454640,d75697e5,21d5cc01,fca5c7f5,3dbad43f,116c243d,2aeae96d,7cdc33fc,42972cef,46fe7704,5d6d7053,1a74ec87,6b25996b,c4cf66cf,b3784f79,66549102) +,S(2bfaf073,97be067c,1b7c20fa,3b319fad,10905b8d,8ecdad4,cad2ec8d,a1b8780a,ed526499,ea97c967,3d28bdee,9970f4fb,9077deb1,5f823cd5,775cc526,4e590285) +,S(fc1abc81,2b5fed90,bde3ca16,3422f73a,6df6e058,8ed69222,cfec54f7,4cf5f73b,db1c6edb,471cfcbe,468481dc,4b1cd1c4,7ac68f6f,4fe9dd8f,af0d1136,972b975f) +,S(d838c2ac,82e02f9b,a376f92d,293c02ad,b717d65f,be31cc76,12476e6f,4c506a8d,d87a3067,388f8e0,3c176f6c,a7eab123,532dd1cb,3e0f6ae2,51d7b31a,35189377) +,S(4b3a82ff,7295749,a259d54c,a9a5e539,32c53ff1,33a1c4b9,2f697e06,b13ee38c,44372b41,a788456d,3f66a0c4,6c815210,bf60734b,e570a290,32db3075,ba8d6265) +,S(d77cbbf3,6731ad4e,325eaf6a,9bd52f70,8e79d290,e379c2f2,b8d6ad3,fce411aa,ee466d25,f07813c7,91a81237,748bda9a,51f4266,4a385b50,5259bf84,ff8372f0) +,S(3867f6b2,58920b82,6eca75dc,953ac307,bed1872c,c0f1c6ec,84efc6d2,eeea1483,cc6a6adc,e6078097,a013ff18,dca9c5c9,254d70f4,68ac1cf4,b8267135,af88aee3) +,S(7b008875,e85fc84f,618ea7df,f20f7f17,2123fe98,c07b5279,930b52ef,cc1b0a9a,3f3b30bb,8dbc9169,a5c55a1c,4150772e,331f7834,b50bb904,a8c56600,bf075f90) +,S(ba5b44d9,16304a49,87579560,4d4b796,907e9b7a,4da49a5,ba7a1d77,8e34ef3a,16b49baa,48219e3b,9bae124f,dcd4de,4d47ea76,3ed39e13,f874c75b,cf333661) +,S(691eafe2,7fe4120d,852b3a23,b5bfa646,910fe521,355a3594,342d629c,e2b8d9df,43a06097,42184dca,7c294fd6,a5227dbd,bd71ea1d,c4bca878,1887f9f8,b6c71d85) +,S(13a6cbb,c9f5a86f,35173e38,a45dca65,551377d7,c37542d8,44006e48,54a54123,ecf3809c,9a964d9e,ffdcc2e6,2e8faf9e,8a43d38f,3023c4b8,84d18948,8488620) +,S(33fd2af3,fb595cd2,93d731e9,1b4f0e5a,da665420,2dcc36ce,b712e8d5,e6f10e45,e26a307a,a8bdda4,4b981bf9,808cfa3a,80ca5331,b3a94c31,f6331e60,98ed2cf) +,S(75954033,59e47f78,76b3ba57,5a758493,b0033298,22f4e44c,15cbe58b,34b774f6,7ee9a040,70b8568d,b0a99855,3442c000,b6e6651c,a7ad1253,f8e703de,5e0458a9) +,S(79199fb0,c3b17e68,4e815e7e,8414bef4,b3dd3665,10d032f3,6505da59,6f5b9480,95bca973,f358c9cf,ef3aacba,54a055b8,abaab383,4191a1b5,54ac3ee,53d9a0ab) +,S(9705f4e7,962ece0b,2e7ed64e,a33fd40,6edd0bc1,ac8997b5,d6d70000,a51aa352,4d4ce757,ad4547a8,a126193d,8921d6d8,58f40855,f71ab99d,b2bdd728,499ff347) +,S(881af067,b841ab5f,30560b98,3376674d,cadeeb75,b8b16e12,df7da3ce,312fd777,ccc13fd4,512ef05d,7c10f62d,8b144b1d,7588be3d,1a3a3701,9accdec9,3483b84f) +,S(fe8f7a44,33c2c9d8,f42e14cd,97ad0794,41bfc3b7,2fd412fb,1f6d5f5c,10e99621,e5b576ee,cf34374,96dc4ee3,a846c79b,4c8babf3,cc46a657,2a93fdd4,b6b3b2a9) +,S(ff43c8da,18e08b38,3f92f832,3c34e83,84f40064,9e0506cb,91893abc,5c4d798f,a575a059,23a9da02,c0c9dde1,981dbd86,f5a75433,a4bb2fd5,2427db01,7ddf2e7f) +,S(4715056,b97f4e21,13485242,74bb1b33,d8f9d841,73678a42,ed3c6fd5,28db276b,b4c0d146,3521f1d9,fe8cd71e,fb98aa28,73a18bee,66fae12f,cabfa201,854302b2) +,S(85c9d808,c5f51d5a,c2a51394,cb83e7ee,acd85eeb,31a88814,9744871c,271fff51,2a7c3bf,23829b88,66991454,d6ec7f5e,185050d,1982deb8,b4992bbe,5b666a7d) +,S(c02b94ef,8b1e368e,3cb4b25,903445cf,f46b60c7,6d6d2f24,529997f9,7d70dac9,bbb97d29,bcff81e4,8d417825,ade1d28b,10fadba2,acf961d6,80a343c1,e725f3a0) +,S(4b1593bd,28e26f7e,431ac439,3d353a1e,fcb8b63,fee7adb3,e29472a8,38241df6,8a67a728,f4966855,26225b11,3ac9d858,3f184f8b,3ccc8ef0,d6e4e34e,f2fdcc56) +,S(f640755b,ba51dee3,ad650555,9d52869b,7a8e7f6e,4b9dcdd2,31d1cd4f,26a64dd4,7387f800,a53e290b,d4c29f6e,ec24ac07,91e0e3c8,55d14882,a48d0539,6e87e1c8) +,S(949346a9,436836c6,3df6f42c,d708cef8,16d0a522,914a606e,2276979,7789515b,a903932d,952a0201,ef65c847,b8754174,31dd41d3,ede5c073,31383b9,a8e2b356) +,S(1824099e,c769edfc,2390b1f6,698c22f2,cdcc9783,c5cbe45c,de46008a,67f91a60,f8405fa,81e04dd5,6ce051f3,bfa088e2,804a8780,37892ae0,adf42767,311bd1e2) +,S(e6b46fd8,e4633d92,8805f34c,ce95f3dc,683aa792,139c62f9,2ec8acd1,d601d341,533db813,f3fea63a,528faf4a,7cbf58d8,d6c0bea4,c28391d1,6391d300,cc1d824a) +,S(63799b30,dc19b18d,d48d150d,3d4139d3,a04c6252,f176015d,60a436cf,ff992b93,26205112,5ebf4dee,d00e72f5,ff064178,7f485cb,2ba35507,3c3cdb0d,16dcc41b) +,S(8cd02f5a,b1d5a02d,e85b88d3,ae370b0f,7fac5324,9f1fd676,670a29f1,2ead7338,358b42ac,ceb689d6,612cb70f,bc9bba7,511d56ee,c4390627,8e2f0961,bcc0d8e4) +,S(d5e167e6,d48bd1dd,e95aba41,bf33b065,f7612766,4a0a8991,d3ea1a9e,ddf82fab,6cc23982,d653cdf4,fc4ec9cb,3cd86ae2,aaf45f62,38257d70,849c2686,ae71d1f7) +,S(c4073500,6987f2df,1334f09d,97f00c19,d72b1942,5864b0cf,4ff19dcf,7c9bfdba,f6566846,6b461326,a9b366bf,30378f82,b760ff0a,ca568ca8,2bf0c9d3,ff7ff262) +,S(9df1e71,9c501bfb,8a21db2,a838318e,29a7c2c0,fb2deb91,1840c7aa,f0c4fde2,cd628cae,6d0e0c02,b0315ae5,4901e703,e977c75b,43defea5,80a5325c,fad00fc0) +,S(95176429,df73c309,23108ee,6922c597,1c91c249,ad7165b,7d08b84d,2a4c696c,3ecffc49,3b62dbcc,82208470,375c929,1994fbb7,240740cb,dd8086cb,e9c0fbe4) +,S(708c48e8,692b0d35,e13a7e0e,94be8fb6,f57fe187,34f5556d,a318f600,c5628c6e,9534e302,deee1d1f,27e0432e,9f6a2f00,5af7e601,3f7da9bf,e1cde320,a105a664) +,S(2775e17a,b82dc739,b5d62079,4b26be15,46275ec0,9d32622b,2991cca1,4e5b4c09,3aa6ee7c,80e8359f,deeb52c2,8da1328f,338ab228,8744d52e,8a9b7908,1b1ccddc) +,S(47c1aac2,39c4f31,cf334959,b8fb48a0,44da6b34,3c383270,be19229c,30192da,5dad651c,5ba6c21d,845cd402,59a051c4,e5813f5a,21228355,1fde158b,600d7cc0) +,S(f80f445a,4b864c96,1bb2bf5,9cfa25f1,eef459d4,fa28ac70,d77daa1b,b8bad15e,a383de04,6b8c6996,6d0b9da6,ce9f88e5,83cd57f4,a5f12c88,63f7ff70,b374ff16) +,S(2924e7b9,1ad33619,a8df436d,578b7172,b2208125,9f2d5bf6,f9cb903e,bd4d2f66,b8642169,e2319f54,348e4f3f,e15d1f06,afa41255,ad62ceff,a768bdb8,48841875) +,S(37d6087b,91a64ed2,8341854e,3a2b9ac4,4310785e,77e32e2a,f13405f3,76dc4d83,35a23917,e068bccb,a823092f,32e6fcdd,a3ac9f09,2c251b1f,9bdb9971,3df25003) +,S(d748a96d,c79f1be0,6b43691c,bcf84b4f,e000d2ce,8294cfd8,391dcb92,10f4fab2,75b4ade0,65da902e,ce57e5c0,4b817228,975c0927,aafb76c8,223c4da6,f05bf51c) +,S(d05d2a25,eb5084ff,262361ea,33b6fb97,9492414e,c255fa8f,a0233d06,e42cc30c,14bbbeee,cecdc8f3,85b51429,8aa03d9,ba57a198,5ba98f4e,75efab60,69a6f2d2) +,S(69ba995b,d137edbe,5aff3906,764eca9c,b4684bf5,e8a8c302,78c89d97,483ff793,2b1cf451,b6226596,9833e864,91c5db95,a79ad96c,701580b2,46de1936,5bb63e32) +,S(7f01e9a1,5d0ecf28,fde0342f,f506104b,3a50aff2,f1ec997d,9a82ff71,54419461,1c3c9594,a6b8d375,361e25d1,fedcab63,d2704065,a83470e5,e03dcc43,ad12dc68) +,S(719232fd,193b9041,fd201bc4,7184e4cc,f2413d03,f0f8619d,813a409e,56e48cc6,161fc718,7dccb053,5337ff60,9eae05ab,12fb55a0,65f82d1e,68b19d3c,ae29a953) +,S(15b84120,155f7310,3386009a,57bd28bf,9a63bcfa,46c8bd08,2f53dc86,76e78c27,47dd637e,c9017e2f,e9804e0c,840aca4f,188171a0,ea9fb605,1402c503,31737816) +,S(ff429d97,bd380047,7db52ed4,eba69914,4a95ce2,7414da2c,74c074,bbfec567,7b3a6fbd,6e635bab,34973f62,4df77c03,6708c00a,cbcb8f25,69a5aa01,d57b3048) +,S(ccd705b,16be74c,cf1f476d,12c77e81,37917404,b2611ed8,858b1e55,d50a6ee3,8741ade7,5cc7b252,4dd55e17,897f78d6,946cb4c,31ad9f1f,737affc3,40e87c01) +,S(65b61a5e,24b3c57f,91678e81,8300ecaa,94e4b406,8d499534,d54c37d5,bb65b27,90d75e28,aab97b82,f1b86fda,2ae0851e,f65ac4c7,a5c664fd,79c16a76,75ed0c40) +,S(671529df,8f4073d9,43f82bdd,758fb8e5,4ffe0487,c1a38cf1,73c1419f,7ba06ef0,c46d9f2d,1f23349a,dd1121e1,d51ebe4,454fd141,c7076950,4d854cfd,14fede59) +,S(2af8a76,e7a1697,7af8e70,30bbd2c0,73da3fdd,4c6735b6,8e823cff,901b0440,55565be7,9ada7153,c7165976,91ee33cd,7c34f5ad,d65f543f,72ffd026,26c3c1cb) +,S(acd81d6f,c5ffbb87,4fcb3b1f,b7cc730a,4def3fd5,ef59fc4,2fb9472a,6f575707,8e319a8e,c8b8ea9e,11ef47eb,5071b696,b87c0ec9,63e851cc,e0929646,1d181a3) +,S(ab77e7d5,3ab9e8bc,2052d0bb,360aa7f2,ae062ca9,9b91441b,d60ce8db,80ffb912,5f92b6d9,cb0ab95b,9eb992e5,e1d70412,aca90998,b5d6ed8f,dfa4c752,2df96b5a) +,S(798b8195,1a6a8c7e,f8beda09,b06a2303,1d7efaf3,7b663265,e0188ab3,78124046,9592da1b,baea430,3644a7e5,4e67476f,d91382b5,f1181de7,a1bf9d44,e02b1bf6) +,S(f2486aae,c3be77d,8dddea22,488aeb4c,9dbff93a,24ff6f29,ed6d528b,9777b096,8e2b2d50,fe75c847,7c50327f,e36afb0b,d8409b62,8d83a8cc,41325aa8,4829126b) +,S(222e3d09,3b8aea8b,e0f64ee3,3aeeee72,d16ca088,264f4333,f9397488,146d2d54,eff500f,bfe6bbc4,1c2dd16e,8a0ebd79,37ba1f78,ccb098a4,351b539d,cacdcaa9) +,S(6768d3fe,7ca93476,245a18ee,ab53e002,4f99241d,9c321bd0,165b7d04,c121c1c9,402c9d73,c9b99722,2319ff1d,9e1c778,c4dc825,d030a43,823aee8b,90272877) +,S(6eb6740f,b2f0880c,eff25697,bfd7815d,b0b62884,cf8c8ef2,5137f098,f22dda79,cf7e8531,c12f5713,c302061c,1f42e6ac,79c4b695,e458db68,5209f03f,1485c86f) +,S(7101f348,4eb05a7e,aa0f385f,367b6a7e,5ede8959,71729ac2,950006aa,965fe51b,8d1fb913,a040fb75,778fff9f,c71afc2c,2605595a,1e44944b,e264f893,10d5c3f3) +,S(75a9e45c,acc6a25e,3fe4b25f,12030362,a0de03ea,cefba87a,11c8e6d3,e886579,52a85995,e91986d9,26a02cf3,1001fd82,854c49f9,55683f7a,dbe7c02b,cff69c6e) +,S(31194d92,bad76572,98bffc54,27f99ade,ceac14f7,830ac1b2,954d6730,3737dcbc,99e81c45,f5318eee,9d2449fa,33cc5bc6,8c556ebc,dac2ef19,a2d1ff3f,5fdd2832) +,S(904fa3fe,76fd6f43,86ac6138,70b4cce3,c1d743c3,8457d23c,522958ac,bd505af,94273262,1adf4633,79e929e4,54c70595,8dae9bfb,3957a304,5c8aae41,a7c1763b) +,S(1d1861f3,1a828b32,3699aede,f3fa265b,40de142,78a7df26,b5fa93ff,a4a8e0fd,b9657781,f13ffeb2,8e1306e3,b84bb965,3ffc30c2,63a65f61,1995f44d,d899f7dc) +,S(3bd33def,6de1a5d5,e5ef0289,e8565dce,aac95922,8443c09b,73ce545e,6819ad64,993f0109,eb560bd4,97436627,9a70e9bf,8bc9851d,f52441c2,97535ed5,d4f9c810) +,S(ca1b7d45,2a39aed2,a7f27b24,8e5a07c7,e4c257eb,7150e61c,7bec7f6c,58958634,ab11fe1b,c0357c37,b7da9804,39fb5481,179c0f7,442af2c7,298facda,3c4afcff) +,S(273a95ca,dee8386b,53ff29a3,6ce7d9ac,df145f7d,5f928f61,da6d57d1,8671aa58,88a6359d,bb06537d,1a6c6d83,24065284,315a031c,73c53ac2,adb14f95,1a5bbe2b) +,S(7ea4c0fc,a139bbac,553ad79c,d3921c01,c06923f5,29446279,d0f910f9,ac5561e6,bd049d47,45830e2,f31e7982,2639f198,2eb71c37,71aed93a,54dfa0bb,296dec96) +,S(71f1801c,a2babe5,d4793240,15ccc684,d3823f68,fcd12ce8,bc49607,9607d336,41ed07d0,8384e54,209b7de3,7e8fe7a6,14430c0e,439dca6c,f5ae6ddb,1ff1b4b1) +,S(b2838873,652ba1ee,ae8aec51,55541a3d,7000ffd9,6279cf2a,1c90582d,21cd8828,47ff155,73f5e3a5,245faf65,86afcc31,ee4d19f2,a1efc79d,2a0b4811,d27dce5e) +,S(34ca8da7,22634bc1,6873fbdc,c089224b,d9f8164a,6bf0acb5,b68d898e,b55a3e21,d8afb760,36a9c91c,7769055,d85a3ff7,1f041a90,5192df4b,73f9100c,e5c5d986) +,S(212cb397,d97341af,ca0cf5b2,ef6d796b,4b76eb87,89c3915a,a0e72337,4b19cc8f,f06e2cec,5f29f06f,97685f50,1126c50d,ff17e273,2f78de64,875c35d6,42f170a1) +,S(bc0367f6,43bd13dc,32582dfd,e7893f69,d5f29d2f,ce4b42e9,ffa8091c,6ca6228a,9bec6c89,d1251f06,4aff3e44,ab59ac32,c1764179,7b96f73f,527ff429,74556e78) +,S(8fcb1423,5d8e8894,5ab169bb,4d198e4a,5e489e33,cdeb69d8,dec100f1,a9bd5672,fa1cb396,bab390b2,60302a46,b4408427,c45ae320,5b1d1f5f,e52db790,e7fc057c) +,S(70ad8505,6675ec20,cbc43e80,11df60f4,d4917e84,cca25044,e15c2fa,e51e64ae,f4a51356,af9fa7c9,a5e62475,df5b713a,b63d68e0,cbef44e8,c004fa61,1d5a800d) +,S(ada7af47,48074168,86e6df81,f9718c06,bcf6e6d9,846fa880,3ede1f6c,b9695842,f66d51f,50f427d4,dc748bbe,771d952,98369c91,c41dd,575b76db,988a582f) +,S(f1b87d8d,7323ebb1,1bb8108f,9d34f8e9,bf1e977b,a527c52c,7f045d8b,1102eeee,bd95703,51ebe7a0,9ede5842,47b755b3,7e2aa0e7,7e380b7,9bddf768,b4346556) +,S(93659d5a,d55264ea,c900c301,cfed7b83,1fbf7c51,766cc833,cc591356,492a0553,b69389e9,646d0165,afa137df,1c1d02a3,519ff65d,8856c89c,cf46de05,c0154beb) +,S(e9d2bc49,2a9b4ffd,c482f54a,415beeec,409a62e5,d1781f48,b1738f7d,8ed4ffe2,893e4652,2dccbf80,ddde313,f4f25383,8d639208,36150631,60830408,44305df5) +,S(1424160a,7bf1ad91,18103eea,372da6a5,825e0bc1,a8760744,229e2af7,906cb210,c94da226,96a75d71,1e6f1d0e,10b23e96,4f1519e9,1f63743f,209ef8a1,3996af23) +,S(7f902712,402e4bd4,aabe434a,eb5fa7e9,8686d058,e6f26300,c458cee8,9c9efd87,bc00460b,2aefb418,b16cdcdd,c88446a7,1b8b018c,15261602,669188ac,62f95fb5) +,S(d327ce7c,66cb0f53,e86e5f16,c8dc2936,2a133f6a,38147c01,255c61f5,6f72745c,4dc1498a,c3cb39c1,6908b85,b8c52dca,618cc6d4,1c6f95f8,353758a3,86bea436) +,S(f044eb19,c9d81106,13803b,e8bbfc31,137dffce,547bfd1a,d5396ad,e32db3b3,5ad4428d,9c4c3e66,7f8551ce,ab282cfe,8128a8de,ffccfaf7,c102b9df,7e526f2a) +,S(7a6c5237,b95d19be,af46a81e,bdd54403,d135bbb7,f3c03d55,f67c5cbc,e18daf4,62a2e8b8,47fceb5c,484fc6e7,4ff68fed,bd492c66,8a70c1d7,cce2922,a204385b) +,S(59c7fcdc,b069c0b7,bb772afa,f5c20846,ffae949b,47589bb5,80495bc1,b59f82f0,82ec588c,6657566b,71ba4d79,ac8e2c7a,c09b3652,f24d46ec,3358eeba,126c38ad) +,S(f8cf3c21,1ed4558c,c40d3dc6,dbb85e54,81d99fdb,cdf27ae0,ea01bd4,6b6543d7,bd8049d0,1c3df630,8951f5db,dcce3043,5506a5c3,e0a8bb20,54de9dc,87a9778b) +,S(3855d764,b2389d4f,1ff47189,33aca82a,4d2dd23f,cfd13764,2782ce71,524504bc,82c4ea25,4af2abd0,2724ec34,6aae3aa3,b14a71ee,df13df95,9446c0bc,759ef72d) +,S(77644b31,1de4f0d8,d30b1d73,35553dab,6d3e0e6d,af7e6ffa,48cddd1a,52de5431,645e17ce,c0a0735,737730f9,92bb7431,7581c930,695829b4,cdbf5214,f45916b4) +,S(a4d4af43,afd6afbc,b2a511da,a6537abd,56bfc0cf,76659d34,56530663,d9ca138d,be6c9fd0,dd2df31b,b907f073,7d63df34,88b5db5f,3d5ff747,a25a2beb,73bb09dd) +,S(417ffa3f,e673a081,f72c3d64,5e64bc61,2da352ca,fb66d1aa,aa8fc060,74a75be5,9ab8be2f,90849474,809472af,d5a7490e,cfd9ca4a,7f738be1,de093e0,68c50151) +,S(78175154,93115041,6cbd772c,c74212c4,4f19eb04,95e88622,f75ee838,caef73cd,37d4d5c8,ffec3650,26381b91,d7cb18de,fdaf2cb0,a5d94ac2,dd7bf7fd,34d0d3e6) +,S(365c560c,3acc28ea,f560c66c,7e76625a,3c2ee7a4,8f62ef0f,736253fa,b1b5121,25cbc6fa,96c0f15e,38b03f88,338f8dd1,6990aa15,dcec58d6,4517d37c,5b74868f) +,S(c945ad25,1010023e,9126b6cb,3a19a925,6c6757fd,d88cf808,a94fa182,8f5bac7c,c9a8e90c,b4ac43bc,ef08ae3d,fc66f6db,99df4abb,2679de5b,174df92a,d654e2bb) +,S(4aad85ac,45e6d57d,73822387,632863de,ac8c3fb7,b7d3beec,629c0e1e,2c2e0213,38eee8e1,9e81bf64,9641c192,de53a071,29c03689,376f6595,2130b63c,fd6a2708) +,S(2bc10248,b0f6596,adcf9f66,3f624461,907e37ea,7d35367f,24b9f69a,26a291df,e0ac3ed3,3016b37a,91a780f6,b90b7743,24ad0c30,50812b2f,4f35426f,4311c79) +,S(cdc05991,abd28f93,e81a2c0,9a04e1fa,eca89400,81fcfcff,eb91c8d3,93ddfde2,dfda4ac4,4458e2b0,3feb9852,d1a8822f,8bf90a4e,bc79ab32,afc9a1b7,580193a5) +,S(5a24eff0,af2dc429,ff4ea857,f8aab10,31d285ae,9c8beb7d,793bf9d,db46a047,179db914,59739160,897f40dd,b7af0b8,a98b6f61,6aef7327,b53b6005,9c4c8f9a) +,S(16c1e53f,51603ca9,374aae6b,57d15fdb,e7c1d2b0,b0545467,636fe2ac,7838183a,bf858ef0,56e09bbf,5d8af50c,7e0fdc90,8d1f6c1a,570594b,a5368976,63a183e3) +,S(792f1eea,71b92f9a,d893a116,ac177801,a2f2405b,1c6846eb,c3a6559,eb01ccf4,bc062f0c,c610ef39,311ecf87,a7866b9c,52c0df0b,be1e0129,3ccd0b7a,2832a79d) +,S(1b399c40,f057719c,f544dc51,51137fe3,917dabac,f0804dd7,335ba65d,a57378e1,4d6794d3,b6e8a085,41de872c,1dd333a2,bfdd0712,aa846f6d,380a775c,600b425f) +,S(138b313e,b9c45ced,e0cd8219,b888c51f,9f8cd278,abe16ede,c210bffa,ba238e4f,a199a357,cbc8042f,4b6d4951,2f0acd26,13f0f76d,cd6dbf7f,d9e8723c,e6ac56d6) +,S(3e06c311,9c765664,d0068b24,cf4ae1a4,8f401811,17c93122,84ffc24d,eb542338,63a65596,e5607f1b,6a9c4596,39a90d06,93b08ab3,2807b696,498e7557,673624f) +,S(3f32f9b,49c88932,574e5455,c179e9de,80f0f6de,ed1ed25f,8a3de8b4,bbf8bd2e,a2d2bb1a,5fc96f89,ef490257,bf42a9dc,91f49ce,e2b86472,8551fa6b,6a589f17) +,S(89adc417,ffccc50f,a129602b,f5b1b82a,2c4f5efb,e3382b04,1fe77299,46414ec5,4ce7e75c,291b751d,155c291,5aa619ef,6c3daa28,25da8bc8,e2c7ef54,8c253215) +,S(f33fbd62,6bec8c9f,6597d486,2f54f65,e9f208fa,7c1d619,a13feaae,5ce4e9b5,c8f0602b,ebb9f152,87fe2eda,3050b032,a6f43c8b,7726825e,df02b424,51593774) +,S(eccc8ab,7b90e2f7,e45f7703,a0e97406,117fa04f,4512071d,568e3e64,a4a7d6b8,8456f705,a9c4a0d8,f2f3c233,3c8bb367,fcc8a645,f1fc560,48e1699a,58a1b6f4) +,S(f6962b33,a7ad862e,58181c75,42743ed0,e6e31d5,4df1fdfc,a4e3e95a,157de915,af3d47fb,48515ceb,f9f7e8ad,2a0159aa,55c155a,83359cf2,9ee3202,ea9e6605) +,S(3f1f3c6,7996fb10,65ed5df0,2095926a,5c45d2f2,38ff59e8,42b9d234,daace24d,d585c9e4,f0a29f0a,28e3c30b,22228dab,73750706,5690811f,1104bd4a,6d0ea28e) +,S(fcae019,36290e43,d8bf93c1,6ad2cd5e,dc33592d,4081b0e3,4aedd451,6e6e589c,8e98579e,cc42a444,2dc710d8,8cd09996,c8acb958,b9702c24,3a207ba1,471165cc) +,S(548f27ba,355229d,23b7922b,5fc8efad,abc4247d,baa312c6,d89570ab,151f522c,e5597d8,bbb47cd7,c8cba774,ff37c81e,b7b63c63,39b9600a,cb77f1b8,86ab6fa9) +,S(e2f64914,e8bf347e,d465cf1e,f086b7be,49797db8,611803ab,6e439bbd,5c146fa0,8e69e76f,7c641e4f,15248257,77aef9b,88b27a94,72d565d1,d2ed7463,fc6ae76e) +,S(8c532a0,2d970c0f,1d8f9762,2298b40d,c7d362c8,f38cd122,2cdbeaba,2c13db1d,90f8a2e9,428d66d,9e8ffc9f,bcb7606f,568251b6,2dec2768,1b6ffa8,7062bd0e) +,S(64646efe,e3c51972,f6af7e0c,8052aec4,6fc0ed97,af566f6d,3c323218,62291a23,9e29e150,f6ca370b,36a76ee7,171f2ddb,a10eb88a,986ffc50,d6c8f4f7,287a69fe) +,S(6e8ba4da,1df8dff7,cbff69a,6ef65c9,329199bf,9a67d1b5,1eb1e1f3,473be659,ae7562c8,959441a7,35ff2188,3e537bd7,eb5a34a2,7e144a06,f674679,815d852a) +,S(8c21abc8,f7812c0f,6de0d7e1,cadbf1f6,c94c871c,d5b8e9ca,e76c1f33,2b6bcaef,5a7c9cc3,13be03e2,5410ed03,7e0cefe9,5a36d6fe,68ff46b2,663a0ca9,747ed53e) +,S(ce0e383a,7c487e14,26636ff7,657a28f0,10ce5102,1eb89fea,3426b269,2f9f6353,3f95a465,d43f7804,2804e9da,4c73c8ec,97fcb9f6,fdace280,532c0c0d,6232e03a) +,S(7e33097f,51477c4f,f4459848,34673fc2,8078b6c7,c01cb700,80a37ee,1d698eb8,c1275bc0,7317c679,d881d274,e49065a2,e282b7e6,970065ad,141c1ca5,ca2d75a7) +,S(ef4d9cd6,b1487e61,fb31cf42,9eff2731,d686eb66,cfb3f867,9317b0e4,116837a6,c4f6beb4,280ddd5a,24f6461a,6ea1eba2,3bd2ff58,7ff612ec,d118b76a,3d63be1f) +,S(f3edc145,2ee49324,f7890bcb,9a5f8ba3,ce53f4a2,df0c94b9,92c573b2,f3716290,995446ab,fbc9b27a,8db301f0,87c2ae93,5c390565,491c0dbf,552a9820,d3fb2f2d) +,S(c1f62251,7398e914,d6134160,318d20c6,87d17427,2bc62c92,bd170ca8,2bcaa975,1c85ae27,36735b76,d5328d59,3292f718,f6d6da52,1c75cab,fcb06720,87769e99) +,S(a7e81df9,605f0881,d4856153,3e310715,b206c601,9a55b889,7141321e,463dea8e,3ae220fd,1f361be0,f0b70dbb,a7df796a,eb23deb,5c5440ad,aa16fef9,7cae8a4f) +,S(1b69bf29,8f5df296,7a3ac028,87d4ae5e,f46af5ac,5a8cf326,2fe7282b,e46a536b,c9c624aa,2d99a71,ea375c68,419b38a2,d7e9d7fd,8880dc60,28c24f2e,a846a91f) +,S(cddfef74,31763b9,8df15cb9,6b3b3127,b31c9987,fd17251d,b56122ed,dbbb44bb,972fef4,2e935aa3,4c679ce2,6a32a38c,225f0867,8f780b65,854459e7,f24869f5) +,S(32fe1cef,2c8a5163,66b47d30,9ef01e20,ee07f235,e7c0129c,fcb70fff,60e957ff,9e861731,bd376124,8b33babe,c354ea,3d13b2c7,c45b436f,e754edf2,4fb4588e) +,S(a382fbe9,f4914119,d31d0ccf,6afb61fe,4a759657,26556908,505225b8,79c24b0d,aa300360,39a1bfdc,273e0287,c7c222fe,cdde6318,c9838a55,5136cda8,d2111857) +,S(b53b5439,9ae330ea,e13e2467,7c0344d,59e305a7,96a228bc,6d6914ec,1a80ef88,99809053,8dfc6c5d,748f43cc,8ba8a8e2,969d8d1c,47551a36,8f30ee30,d5fadc7a) +,S(621b5253,f11cfec3,fb4bbfed,98b0ad9c,710fe2af,6aedb8d,f521d0da,fb64169a,2a234dc4,8b716af6,22dd025b,63e05c5f,3b93005c,13614bb3,abe0dbe9,260378aa) +,S(83332b6c,cc9c3c7f,3163f442,80f5758c,1e5979fa,b9a500be,d5b315e0,f9435b04,85360612,258c1f1c,bda41025,c402dd84,60c77df6,8762d2d4,16aa16af,dcd30ea0) +,S(27c8d8e3,91202622,2f72d926,da01b10e,fd54e21a,7a4746b6,68aa868f,dbf6538a,88768e3f,b8f2484d,56c9fe83,fde0fff7,4d0c0a81,213ac090,e90d2b42,22d6eb91) +,S(f349c70b,bcd82005,2fd2e05a,c42556ef,6ee768d4,f08b7a91,55d1ebfe,60067e5,6d595c19,4011d050,c7c3c4e9,e7aea90a,93fdc5b0,71d6c9fb,9f621a4e,750c6604) +,S(4966567a,b2029966,62b1368b,ac3f89e1,a0183b20,173a7156,edb827a7,13b24f90,6ef89436,c2bb3bb0,94910ee7,6c083e35,a097b859,16658947,1f59c33,37e21ab) +,S(4909501b,491da13f,78645b84,9bc63fea,75611b76,5ff6eb6,9a7414bf,913e0886,2c20c6b6,f2ee070d,c1dd4c95,6822ac5c,38b1acf7,5e093f17,c54b65aa,81014df8) +,S(1ed2e667,f5a2d140,18cd3ee4,f4163f7,bd5f898e,ee3dda72,bf84e356,10cddb76,bb4970c5,64fa4cea,e204babd,d869e36e,934810ef,68a22794,f9919e3,26db8e7d) +,S(c47bab41,a0bc37f,addf9c8c,7de86d68,c15d1a0d,85257d57,2a1d60d,4612f17b,e52acb1,36f47de1,1128091a,1a3cc3e0,e84ce767,cf8395cb,b09eea81,a2db09e3) +,S(e21068c1,4993cfc4,35490b52,5ece7657,f70825f3,dd2138c1,8f5ec6a0,c67a7394,cd610137,d48f8d9b,2bf99755,35fab6fb,65360fc,9db04a12,43027083,c732da) +,S(635f547d,47a72f5e,b069e144,6508a46,bd6a6f,11eefb87,da07e4f4,3ffaf917,8ea0a9f2,3da273b4,f324c8ed,81cb0bf0,21fecb57,54eaa642,21736923,2be64128) +,S(24632c0a,8648b4b1,fff2f79a,b3f8e64d,3837b6ae,9329eaa8,48565f02,45bdb47b,9c6d0788,c9e155d3,3c260360,862ab236,177ff875,a3823179,8146e442,f66e0426) +,S(43748dc3,cfb229d4,8549cc56,ecbcb01b,146541e1,5d2c3b5e,d59fbf7a,6b7bf503,9975a1ed,23a09906,3b87ea7f,2ccd88fc,b20607e,1aded9a9,265f34c1,45fb9c5c) +,S(8f848323,b8c41d68,31603b40,69be1dda,bb8750d0,11527a58,4bffdca,c3ae3ac0,77a8ac87,128260c2,86f5805a,b17e5a45,be81f83a,2a575c6e,2c88288a,c2d1b37f) +,S(29ebc368,b54d846d,bb5e8be4,81c19c80,2d8907db,c54412cc,6e2952ba,2948422c,e6b793a2,81d59d90,53aeb2d6,f79cbd1a,426f79a1,6860310a,ec72fe26,37c7e845) +,S(9414cbc4,ccf4012c,8c0cbe21,eb714953,d84bc602,9f8e4810,5ba79967,23ed345,ad1eeb92,89e4a109,2d132790,ece35cbd,da45bb64,bb6ae72b,39b9eb80,d57fdeee) +,S(1bd8565b,769d0024,613ab814,90f214c8,bea90a5,db1e75d4,11ec3dc7,288f756e,c050a46c,fc781e91,b79cfcb0,2abdae50,b69c2cd7,dcc56b13,1df2175d,c1d375d8) +,S(4f7584c,35ad66be,3d409154,eb8ded67,14ed0ae3,288593f2,76cec90b,629cb01,f4be9248,232a120e,11ccf1b3,e9c5a17e,1520fee1,8f1da048,fb01eb3c,17712bb0) +,S(5b0009de,7e85b317,ab127401,fd2f3ced,cffd1684,c4772b35,2b9c783e,1dd9f13b,f4585704,f531c34e,3377cb7c,e672c78d,cb8839be,ecfe048d,bb412328,b973ba64) +,S(c66f31c3,79396f01,2ea5fdbc,4222df38,37f85353,3f3ed9c3,ad13bcac,63b32815,7d7b808,4ae5e9cb,c123ca86,1b505242,4113ee84,bbac5ca3,ea1cd3da,6eb22668) +,S(33097f5d,c8b09934,26ba51b3,ee99e696,3d7ba455,e91a7af1,a57c85e6,5e63abd6,fbd26b3b,200c189d,c8b9e120,61869766,987401ed,66e4ed80,63835922,c270d7c4) +,S(1ace5245,a3b7ec60,b9a922d5,ffbbe43d,aa71cf0b,84e395f8,c37dd2a9,c85ffff4,72c0ed67,94708f9b,f29c51cb,b6cbb69c,6ad3f4d,10d62987,1c444da1,bb25084f) +,S(bc197b9e,4cce4c2c,bfb22524,93f9202c,6ed54e32,cd52f512,692a5af3,6b0c4471,d5137663,5182065d,a4d6be56,cec50889,3cad9b95,4d5d17df,adb084fb,dd673f70) +,S(66997931,334bdbed,174cfa73,3ecad896,13c6f500,b79a7fab,76ad0bc8,dcc7df70,fdbd3301,948e198e,ee3e7d3a,504ac560,ab00afdf,26940331,d49bd2c,6599435a) +,S(3ba86cdf,b38d82f9,7c20b962,103f79a,5637422f,370b08b3,89083cc3,1a6eb4bf,acec9811,76debb13,9bd7f981,9b476e1b,5377e32a,bdecafee,33dd6ad2,3258dfc8) +,S(f62432f,9db971d8,4c3acdbd,c2d915d6,6395199f,76e81243,fed930db,ef1603cd,ee89cf3d,177b4cc6,e5856acd,5de8757e,82269f4e,eb11a444,c2e0c797,4a7b5f28) +,S(84b41bdc,6a6dea46,ad6e650,5b280a1,b6d81250,794e0b1d,688aeea9,c067bbaf,9f78703c,b535b4bb,2b88cdf7,b5f0f0cb,e452fe6e,5cd0f2b2,ab8c2439,9fc00e7c) +,S(8e2dfea9,3ea64a14,e28f4883,98b8d455,48c52ac,a5399f88,28a42c1a,c89d4c67,fec01549,cda53605,335df10d,d8ed04eb,e09d7474,f69deff7,5935a499,f2781271) +,S(c3bc6dae,5401ac1d,3fd75e36,4cbc2fa1,dbd2f6a2,40e912f1,18b27759,9b105e0b,170fdcf5,5b739326,fd234506,303700b1,9c5dba48,b6f0cc34,a650c3f,3b63d6b4) +,S(33093e93,1b1e52e7,ac5a289e,27ff810,c2defced,e935795d,a10ecc89,a3c14691,7fbba584,5bd55ebd,a67dc842,1ae10fb9,2114f0b,d68bc47a,7ee87a7f,70deec38) +,S(21b35438,b6d1a3ce,6c765e,dc78dd89,3f121132,ecbcc208,a36fe734,d8141ef9,f6115a5,ed237dc8,f61a82b3,66fb508d,e2d516be,3b166a7f,2566bf06,eae300d0) +,S(ced66583,aa1bed17,30248edf,28e9e4fb,ade70821,1c2e48de,3782fc6,d3892df3,4f43ad36,f29da03b,f0fe634e,3c441c42,c3a81835,2407f1b6,cb38e738,18fdec96) +,S(c5a74420,f541a8ae,b0673122,29fbccaf,37f6eb7c,ccf2bec0,23a8573b,ee6f71f8,ae634c02,44263d36,c00a6e25,d25fa592,1612a535,4934d709,6c864865,60adb259) +,S(304a048b,f30f6b4b,2a725521,15b15ea4,8311c51f,c4cd212e,f0a4bac1,48b9de9e,8f935900,3d6ebf0d,129cb5dc,8a953fab,11ceeb85,b2123fec,154b065e,63bb3cd) +,S(ff96b5ae,7eba0af5,ac9875e,97811e91,6ecb66f1,3bb06519,72ebfbcd,5b9c2048,24af2186,bca71cea,e58a0b1d,b56aed1b,3de89cc8,70fd4e3c,31919e2f,6352a887) +,S(5909ce1c,df732eed,4147243,76fdb3ce,8b6d38b9,4a0b35d0,50bdce27,b1b701a3,2e44944,665e761,825cf223,90383a3f,7d3731bb,bf7792b0,37fc05e0,b224dfdf) +,S(ff6b771f,86d66610,6fa032b7,92844548,8ef9974,caf05ad7,cfb6b30e,abd1d527,6abd2a9c,9393e91c,ff77e055,7e9f6a86,9fcd0bae,3544bf50,b25f0427,908ba4b3) +,S(943ccf04,94dd1fad,1efda48a,12d9ace7,be919685,7e7fc5f4,befd7d23,3a1aa8d9,4f08b4cc,2dd93f8b,3751a23f,ca7e4f10,6014e01c,58791c52,3d01134c,1a0a7bd6) +,S(a79a7a7,a89e1cdc,908e0968,bf26c341,47a4638b,1d9cb5b3,ee6c49f7,405f172c,cb53d967,14b5be3a,e5497f34,ec0325b3,ebadcb76,b9838cd1,b3cee90f,f3bb8ceb) +,S(28ff2efc,71176efa,a861667c,d173f78a,4f548b36,aa33db2f,a50c0515,1c7b89dd,475b3707,db4ba15f,1485e165,68d7dbdf,8e67cdb3,37d79988,dc6e2249,d6fe4941) +,S(85b2c332,16a86885,c0704fa2,55b4ca00,223e897a,f0ae7557,f8687eb3,b1012968,a1f58d45,ba664056,efbcbe7a,f0ef479e,1f2bcbe7,98d055c5,7045d735,d657ea11) +,S(e9fc2d09,eefb32de,4068e2ad,d7bd3692,7a02b3e0,d354dd47,c2a70c9a,5cbb8778,ee009aa5,1216d068,3ee219b,bf53ccf4,f249d7ec,bd8c9cdf,80d0ec5a,5b65c312) +,S(da2dcb50,afd510d3,26e38af9,5a22850a,a1cc88d0,620fed65,763042f9,a0005d9e,1eef47b1,c3700963,5e16af23,f1308de9,ae0eab9a,b16c9443,6d501ebe,133eaeab) +,S(20daa29c,c6b166eb,ce717402,70873566,327891d3,ff7b6f08,b9b7da3c,1eb352e0,2ff6982c,ecd6fa4d,ec1d6692,92c3a326,a30cb526,4dc27a4,299dd3f7,7ae8a3fb) +,S(e16b08dd,2ae5be13,67547a53,496f9f4e,3aab8440,6cc34bdd,71b2ebf7,53674d9b,b70f724b,d9522b1c,8e43d60d,6357b5ba,48a1afc8,b7136c1d,9317a1d9,1ba06846) +,S(888a2daf,2d69db34,ff9485e,b06d44,c59670ec,3c68475,80753567,e56b93d3,ddbe4fa7,792e0b82,ac4753f0,5b271aba,8e804384,b7949a9e,9ba79fb6,31d4e3f5) +,S(747768e2,9090fdd5,2c22e308,131f97b2,ee6d8e0c,bf43d704,d50d60c7,616abd12,eaa75f40,8d1ce0b4,bb52879e,5ee7ff36,27b26645,c8ce3840,99dbac60,9cb61058) +,S(8569f2ac,1c7d131a,8d337cc8,87c00c1f,80897a58,f9131143,5a17d79c,fd8d6eda,504f0b44,252d06e8,2f8e700a,746a3393,85e43baa,a4e5da8e,dda7ed6d,83eacf71) +,S(4f15f3f9,a67221dc,4fca1d68,429b874,c4418df8,be480591,e57d6841,e11860c9,982c0d56,1783a9c1,a16bda10,87d29061,3f1154bc,4cc62a82,b795c54,a496b7ed) +,S(21e51a20,1f226e1e,3ebd56ce,6f7258d6,4e4f9db,aa425943,9e1bf7c0,144a92ce,4c47a801,b6a5a4ad,25c1486f,ec9bd0aa,428f3167,45136359,18d47079,75aac869) +,S(fac7e42e,8865ec0c,d0b4c4f9,dff6bf8b,e0f28336,395bdf2e,d5e2a118,9c0211a0,36812d9c,8337199e,fb1fece1,1997307a,ce656fb7,3bad65c6,41e6bba2,c4dd695e) +,S(a147839e,c91bc77e,cc568671,442d29ee,57f0f42d,7b9cb469,91064549,1a26d225,f404350e,bf7267f1,86120257,fa29fc1a,fc0d064e,2f14eeda,b730770a,c85bc5a4) +,S(d1212cf9,e5086804,44883c1f,4158f3e3,44a7900f,716ade2b,df19e41c,18636b8c,720b851,7b91665,128183a0,f3235c6a,2f9c21c8,e8c6ec0,5553acb0,27fc0964) +,S(13d5f334,a7ab2578,977c125,1a07d7a,2e4852ca,8927ce7,2dd9fe90,8b948975,973869e2,d1d0abb5,1ce31db1,ae8c3816,ff998753,479fe82a,7d87b6eb,798773ca) +,S(1f066961,6dae9e8e,76a9bec3,26bc2136,5ec754a5,34c787e6,b9abdfb0,e463507d,da48070e,b4ea111f,881f9fc1,2993a9bf,c6f99974,b3353ce,b5bd1cd1,c75b262d) +,S(729e187,38681dc3,c4ad9a49,59f975f5,907b15d0,114e029,8d4aad5f,69150269,dbf2ed8e,c60ea01b,6de0022c,8b035a7e,c1901c82,9e8d83f2,4560659f,8d455442) +,S(e92fcbe9,61353477,4d42435f,de190c26,8e3692ee,c2042fa0,2ee21687,1dfda12a,a75b5c0e,3f5280d6,41e36957,5e6ec5cf,585b1451,1f01da84,fb852cc8,2d64f2ff) +,S(85c2e61a,c69f8d39,e9c4dfbf,93f6b904,1ad35bcd,7f2b5c1c,789812d4,c8da36df,ca6d9187,694429e1,a5ca50b3,332ab11a,575264db,bf297752,f38fabb3,c0b53f45) +,S(10d17632,c33cc6b4,f5c2791,b5ae4f00,3adf9958,879c24f,10f147e1,bd1bc05c,e15ee734,f1ddec6f,45dbae00,88e4c5c0,be8464f,31205718,a8b2c929,939a687d) +,S(a6942345,25938972,f39820bd,27ea0bbc,92511dd6,827476f0,28fec86b,e25d609a,e06b3616,870f73f4,93f6d1b6,6256cb48,af49ee6c,b5296bd,ea73083,4f11d6e9) +,S(b4a7d8ed,2b3dae3f,43eba052,45b6c4c7,c401a675,6bb0d906,4f66d88,df94d8ac,ffc81c63,1a2cdb5a,fd7def11,9d07af35,74dcb04,e10a5ecf,d2b0a46d,90963456) +,S(5aebe21d,aebd3c57,285cb660,d9aa55d3,f84fb2ff,584f1266,3031e2ab,dda6fd17,22ea513,6a51b532,d178c40,cd5c8453,8f5994ef,7b35cff6,d9dcd999,daf1abe8) +,S(ac684664,194388d5,b796d64c,b4563aa6,69388d0d,83861c51,278316e7,2c8c7f87,cb9426d8,2dc90b01,97bf114,3f693e93,4d710988,26586e5f,f8913ab3,9ef41ac1) +,S(6f94bd49,f89e621,874c58c2,f27ce8a9,76ad2764,1627e835,6b82157c,24955250,d24330ce,4a54dc7c,5c7d9cf6,8f8622f5,e3633a85,8ec0d2ec,d8df3f9,bc7c5614) +,S(3f96c8c,e149947a,6d9ec5c,9ac5ff44,775e068a,785a2fa9,d0940905,a008f137,a91dcfaf,7111a506,3cf613d4,e360c9fc,9b7d9e40,30e00ce2,b120eacc,805f3f40) +,S(d805b05c,dfd675d3,b862a4c1,74909974,405a5b61,9b2a73c8,f3470959,779c707d,a68b544c,c7279f2d,e3a2abdb,fbb7fcb1,84366ef1,3cc3a66,b6d8cec8,3c3b6865) +,S(d2293c0b,d4189cb6,6be0efa7,874abe9b,59142233,6f2f71d4,808be34e,79df0e4,5fbc3248,87c54960,b9bfe70a,5cea3871,7f24f79a,3fe0d313,18cc068f,212682b1) +,S(931f090b,e22623fe,7ad49d26,e76b3c5d,e3f107fb,556f9657,facad9c,5b209d5f,cc255e79,d2a89899,865daf74,81aea395,848f57c0,1850c144,d5ad1a17,4b1cc087) +,S(118a6283,6707ff6b,c8ea72f6,285403a4,6e4e690a,db340987,a0359732,39e5ddc9,91f00fe5,e65a9058,36f9d447,d96af84,b6f5a18d,85c127ad,72221980,f7ff818b) +,S(81096ceb,5b6cb49a,d014ba42,d1d49e1,b811ca2d,2e6614be,1891c6a4,1f6a985a,da051c5,f8f9f3e5,78050a3c,1354b674,3f494860,6b04b8f5,b4dfe970,b7607e5d) +,S(9a80a438,5ff45ed6,2dd3559e,783eb47c,351b7e43,6ddc468b,9f410989,300cae06,845d1f99,21b69c5b,4628aee8,b0c55398,bee65659,e3e41cad,30e57bf8,b804b6d0) +,S(6a3168f3,b89955b9,7fac1d59,61e95fea,2143954d,fbbb6090,81c2408f,46747906,5b5f1f88,6778f108,3ae44a3f,82c901b6,12c741e,811d5403,c483c572,8cccc005) +,S(9a03ab11,78d165ba,23bd4298,58bc8ed7,797710fe,f4f8d8a7,3db0a90f,e1214af8,70888ce7,e61e4bf4,91512f2b,fa556f51,59a5550a,1d50a65c,a652b5bc,89e85665) +,S(8c90c18d,78b8d9e1,59a61575,48c14971,296c8c41,991dce25,e7878ef6,f4ea2f5d,2a5428cf,de628e51,a1644a5e,5dbb7227,2b97c0f9,b6909a68,2814882,cb1ca074) +,S(2e9bab7f,3062024d,84f7a5da,a02c9ed0,93327923,339fdd43,b68d6dd3,508a8957,c9af80b7,3f3803c9,f21e21f1,9523b2df,ea7e4f8c,9a376aca,4a147530,d2a43345) +,S(2734bf2e,718ecfef,64a95e7c,1fbe6a37,e2129898,343af84d,b5c17671,9a466322,f530098a,9f115f0c,e36a0ce3,4697312c,7d0a1e4b,df8a3ca8,f0a63631,e684216e) +,S(ab9ecaf9,500e4a4e,6e81b997,579b5214,4af3257b,8c8f40d5,e21c2a51,d56aab58,f7d38746,8e081844,9bd5e9d1,938a8859,d0aaede7,5743dbc3,ff30d51c,3de0f047) +,S(ae25f546,a4c09ef2,e5a9cd3e,85baa880,292b4d4d,4e8440c0,1696435a,cb1af368,46a2b3b3,7373840f,b739a9f2,2953c6c4,80bd59f2,11013d59,e051055e,f7f3da84) +,S(54285da6,4ef21cda,f597efaa,884fc00f,4e35abb2,bec04ace,54a83dc1,c12e6142,483beb2,144d7b5b,a47c2dc2,9225dd5c,b962a849,d46ff6e,5220b6c3,20cdbc3d) +,S(bece8434,4bc231f,60ad542e,d6a7857c,35a627ee,a2914874,c8deb661,37dca65e,a229fb80,2f9e72f,8e477fb8,de081254,fe1c93ad,a4d3b4f8,e9c2f4eb,3e2f6a95) +,S(3d55747,b148b857,472fe5af,1e820bbf,89994a2d,ae6c326b,26fd9cb4,fb5e81d1,dedeca5a,e86728a4,f635bc4f,1b2b162f,c9ea7dfb,efd5852d,b90001cf,bde465df) +,S(13c68219,d1633c73,59f6361a,88bf6a72,1846b520,33475715,fbc97dcf,75cde5d4,ce8a3cc2,39fc7f20,96abfe7c,52d79e9f,bddf0ce2,ab2b6e55,935413f9,37f83af4) +,S(e9e40ae1,a8041a47,274c481a,dab52f18,36690a29,be837433,43650126,d5b0a6ac,c19958b7,9c9839b8,1371f314,749147cd,9a22fa22,a55da9a9,577646f2,38c6bb7f) +,S(2e3b4553,7460a1a5,f7353a53,78db61c9,13af4371,9a268eea,6321b4c8,a1493068,f7ba5e56,104966c8,4959d3e2,290e5501,5ccf5cf2,b6fb5bb6,e0452c5d,cba54dcf) +,S(9855bc82,b9214b00,67eaa40e,bf670dce,6a59f3dc,4cca491e,db1f0f48,b4c62b8,a7d93f6e,3be6c73c,e9e37d6b,5b071603,87184e21,38478009,8d4c1c80,ccfcf435) +,S(37f820a9,b9342913,6d44bee2,69cf27df,67486bc5,233b8866,982f8476,15253979,fdcaea9d,aa629d37,7f345a5a,37c566a5,50dd893b,9d7ba88b,2a568a28,7870ca15) +,S(e82d2c98,a92a3b0c,690f6ba2,8070c59e,3e0cd0a2,a384d3b0,3cba9d1f,ded41a98,31e73a32,32d85b36,14833d34,4c7d502d,d09d7ecd,614b060,95c86be0,c8501460) +,S(bd492fc3,6dd4c906,6a071182,7eaece3b,ad46901e,b400bf3a,24b93799,9a923419,caada3c5,1e1fd5a4,6676dbd0,a7fd4049,8b93da93,bcd25af5,fdc6a0c9,1dd7798) +,S(6c8b46da,42d1ec4f,3ca2c4dc,15a69c80,cd8f0d99,7bdf3964,1cded32b,8629577a,d3ad4f62,729e42e0,f72ef596,5bcbc239,cbeb988e,e62b30a9,7f124004,c718c956) +,S(78b1e9e6,799bd3dd,854a25d3,4d356b4f,effee71a,ad0c8f05,ea9ebcc0,f8f5dd62,31503ed6,a26788dc,c07d7005,7a300665,d726ce2b,702e65c1,dd25f892,3931145d) +,S(f2ed02a6,63c5523e,3c68cda6,bfa4ab6b,cb53fa5c,15f0375,c8974eea,5aebdccf,3bde5c3,afdc0320,e3b92241,3220ad32,937720a8,d1477b67,773e725a,3cf36382) +,S(7e43f643,a45800bf,7e54d83,5ff6ba20,f67ab101,5d1aaa60,cde0967d,42caffd3,1f456eda,f473399,b57fbb37,807b5269,58600d12,b556879a,76e21459,5798d68a) +,S(df15e177,5eb73697,aa4c0a62,4b4d2ce9,7ccde2ca,66555d07,6430b656,61319be8,496cdc,f1543818,c2173dfb,8a60ee2d,8397cca,b16e4e53,b35079cf,b27311ad) +,S(edf1d706,295bb20,12dc1648,7940e84b,6707a3fb,91a14fce,ef37f669,c782e2ae,e3a0f2f6,fcd76c97,b20b2dd6,8e888eaf,9ed8f598,f08e4d7d,239a8964,fe019a2e) +,S(c15022b2,6b15821f,3be3c313,73d0464d,fd92cecd,6ba87c2d,9b18fb5c,16f8f6c0,78cd2370,d94e2842,e07961e6,92e9fa04,65d57f25,a80feda9,327581bd,ce136c2e) +,S(bd4df6b,b6ae15e0,109886a2,8a8f8c90,c6cc2bcb,b52dd105,e277da7e,c76001d9,499659b2,b0b4cb96,5b8e5029,96ced2e3,a6f6fadc,da01b875,72f8727d,8ef1446f) +,S(dfcec232,873894ce,525f9b4f,162f180e,1e2d6eb4,c846bed4,b2109700,8fbc0b76,afbc761b,f4c88e71,9c318f05,9a10ab3c,5a477ec5,33243b95,3f8ef006,69b4f92e) +,S(b0076c4d,e3c94fae,23659b00,5b8b41fc,b8473935,764e48e1,a9eb1fef,f5c94e54,e1eb3255,3c55a687,6f42ee5e,44830a09,fe17fda1,b84e8551,f3ea2308,f9c7d4f) +,S(9a1fe9c,17c4255c,c11e9fe3,3d66787a,fe9ebb0,99be88be,94b4c3f0,da2d0c11,e6aef709,99a8e739,6e23e1aa,c7ad1cab,25f6fd6e,62df02b0,96af568d,e88e8379) +,S(1d89a934,126bbc15,d9b99b88,b6c65106,e1d8b16e,799630f2,2576e9e4,15212899,b7e32256,92db0722,e9a79ef,7d95c509,37d64644,ed36cfd7,56780d59,a6f8f4eb) +,S(c60f5c68,b67b3796,3c462d4f,7530edfa,34546956,21bc80a6,ac2be433,700c7fa4,88f8b071,97fff0e3,a19b5a67,60dc92d3,b5507b53,5cec02c6,53f75a60,bf8a7e08) +,S(52cd2b6f,d9b2699,1b76499a,d301be43,6205761c,f768eec9,e6b7d6c2,52d9d949,d127cc66,1fa33508,a0d11a3e,ac782b26,26356382,f547e4aa,13802138,30ec835) +,S(1e44499e,a639835f,f384f107,fd16bb19,c21fbea5,cf4e3be9,20a34024,6d05dda9,41c6a8e9,a9042f9,950d12db,74fd0f21,8f8adf23,e961488d,27412c2,f2d1f53) +,S(139144f0,a56394ba,d1e53bae,360136eb,984ecc56,56a05cb1,c6727543,fc6861de,70394737,ff37e9b2,1bf174ab,6c042bb8,964f3d01,6ea1edc1,25ed3b4,2ed0742a) +,S(934ca02a,2454df18,61fcb954,6634e685,34fe75c8,b6db6eb7,5a2a74f8,654a2280,779e9784,266cfca0,b83caee,74fd24f1,6865ce3a,6be78be4,375fa15c,db195761) +,S(a64f047d,9e1c2459,77da8c32,9d35ecd4,af913baa,2d4b679a,7acd326d,886a3be6,e11e830f,f3b4ed6d,102145e3,b24759de,3210f309,8ec57581,e6014818,79112b2b) +,S(e1c688bd,cd629088,8d8820cc,df15fe8f,cfcf45c4,b8bd434e,2a15428e,818757e1,bc10c6e1,71e7c8e0,13b8e00,e77bb745,a34e7d1a,15267600,d130f6f2,18c77f7d) +,S(b1a30c95,123170f2,d9456de6,827afcc9,ba2bded,d955354,c6deeaac,68137358,9c76a0ab,c16c1caf,d3844346,9040373e,a678797e,dfa1b298,5a17d410,b04071f5) +,S(24cc27b5,fbb0291c,3468ba23,58f5e253,4c7cf99f,699af4d0,d6892bea,e6166e85,eaf369af,2516ba42,673c52e3,ff0768fe,d51aabce,2312b8dd,d7aa1ed8,cf9f71d5) +,S(8bb4017a,6166d1e2,f7fe8921,cbd31cee,bacab577,84da8a92,217aab2d,948f6a57,c899740e,6c476986,356e34fc,7182996d,a09cad72,93d78c8a,5c30d56b,88dc331) +,S(55d8b7dd,d82fdd96,e0ec17dc,20b303fd,e6dafef5,a527368b,ec99a33,60497f6f,fd624511,78bd66f,4361709e,44e72e9f,f4f29230,d1fff657,bbf84762,b7b8beca) +,S(82aa2def,5a5531af,519cbfe6,105dbde3,109d4626,ec47a845,5dbe2852,34c94df3,61160bea,9f83a2e5,8c2df91,925fbb25,8d1e60fa,6a147c4,ebc0ee38,cd811511) +,S(36c1277c,c6df7ff8,47c2cfbf,3cfa9198,804b03b0,c28e1636,ad3438cf,2cf6f7f4,2304fba2,74f81c74,5455d821,ea58e047,a7bb3b60,e32f020,aee5a2a5,16fe4833) +,S(f376fdcc,5b44cad7,e16c9e86,c00b366,d57b8925,fa49b18c,eb8856a,e0cd9119,2d6acd32,e4dee81f,489e3ef,11acfc8d,c3d0e7ac,bdf5c6ac,74fe7aa0,50a028cb) +,S(ea4e10dc,5372e9f5,d96e8ecb,907d3a89,97150b6c,39ecfd1c,c7aaacca,5dc5030b,b0896ea3,93b626bf,c2f486d2,9fdeb897,36d6be60,144fc3f9,5206666c,36d96ff5) +,S(74f392b4,b2664a17,9d3f0a2c,71b144b8,a376ea19,f1a9d91a,d181ca3,a6fdeb08,9bd4db21,428d588f,64ecef67,99b46735,c9523036,406d2636,c6eee083,e09e1fc0) +,S(3a36c842,59ba7774,a41a17da,f0e4c821,11c13a6b,58b82774,ca6962ef,cd4855eb,6ddad1ad,9ede928c,d65720c7,15ef39e5,f35e46ba,45d8ff48,2f095d2b,e0fa5e3c) +,S(9d77b2ad,5636a44f,16e73981,46b17ea9,7635ef18,8f32764d,eae50d2a,5c98c019,532dd027,a775861a,b9392b8a,cb2db097,eb5936cd,9d2f7234,f30c371a,22ee3eef) +,S(2e7402e9,450e2f80,e05168a0,f0c5f1bc,deb6117,ee46ac1b,39aae7e7,bea3c4ab,41e8f36b,5606fe2b,8ed6b3fe,f8821013,cf85721c,f242eb60,34b82afa,cd82fc3b) +,S(aeb2cb25,4ed0b5bd,81b33d35,e8f00a5c,6a6806f1,5314b320,de0c376e,533ebe6f,6b8116f5,2d7bfa2c,b3028249,fc83f317,5d761c0f,76833d0b,142c4d6a,29b9d59c) +,S(c86224bd,55d62c97,a00adabe,3a797929,4cca663a,ba3a3655,fbbcc3f4,5cca7895,a319a3b7,104f7935,36693adf,a6009db4,1d857353,fa950c21,844323bb,cd1cc214) +,S(1d1164cc,2c576a0d,d7b5a28b,cfdce6f4,d53ddde0,534b3a66,aaccfbba,3eefe561,b85890de,aa25d897,fccba694,7c470e45,ec600989,243ea91c,4670a,b44995a7) +,S(2b54fab4,13b915b5,ce76620a,5c493ccf,5e7f0f41,8bcd793b,92f26118,a440e3c8,8aff8d25,56fead8d,7e9bdfef,bddddf37,7a61ce4b,2effcdb,30ad0016,4f9bf5d1) +,S(1f1f2a24,a4e72a1f,609428ac,853e46d7,a9718c25,f2df9887,9a3aa60,5729b340,450dca4,f4920bba,bcf94f39,26871032,b631120,4b217170,991df216,2c421cbd) +,S(412f658e,e6777e4d,24e5682c,5007e057,83fbc559,f22f3200,f185ee52,c8ebd4e5,c2e0b64a,9d9394fe,b930b3d7,afa64cb1,c8399e3,27690e32,887b789b,6571c8f4) +,S(8495dcb5,567791de,976fec9a,367e250d,95102ef9,f92c11fb,f561f854,4cf4912b,65f809c5,7d3d824e,f5e63f19,153bda4f,542e511f,464e11c5,e2a9c32a,2a152443) +,S(f8e7bdc0,d841c95e,3a945b8b,c85ca4c7,b4a18a9,7a5646ee,1bb4ff08,5956cfde,cd0ac83,f93a3679,37c9d28c,24b9fc08,6d6660d6,15df5011,1b4edd5d,f0ed6d42) +,S(8324625a,4e4c1071,24876751,7df28ad4,8c8c5648,76e4a131,64f2f730,62854c94,761b1960,49922825,609c7ec3,70626025,de65e2cb,240b8356,1f5cc230,1f9bfaeb) +,S(8b1c49ff,99f87e22,af4b6bef,d353df1e,16a8e160,3023f2cf,213ed859,13ca04d4,cd4513d1,e178865,2d1cff72,822de250,6abbf975,7e2774c0,2ca1bde3,23ad6c23) +,S(e2ad61a1,20d24d80,8c830e89,d630b466,59deacf2,87aeb790,bcaacb82,96f4f138,51cd19dc,fc1c4867,daad2940,5acdc7ca,91849769,6003321a,bf5dbc6f,b2186291) +,S(e8dcd7e,a9a9a6f3,16a6aa91,53105601,e10356ce,7f4793ba,9eae3ad0,34c197a4,c835bad7,91f3aaf5,d32e2a99,e71c4add,95f69892,6a68695c,2e61ca85,38ea6b5e) +,S(f0b748e3,5875b8ef,361bba69,cfb05c4,d8643f95,9b207556,e46908e9,bcf81233,6a264e51,5d163a7b,678765f6,c29ca61,c1c0aeab,a9ff423e,91e9401b,2dd6e908) +,S(900de96f,9975a16,766dd305,bffe2423,e6eef8ae,93ebd796,2ce97d53,39af3a87,894ab2ec,d9c058a3,a331bf65,dbe69a49,f33f8e9b,12121439,650aa700,4d164478) +,S(8cbe7930,d2ea2342,730e306,7e9ecf95,ce7fbda1,e6d34645,a6e214a0,ed88aa53,867b156b,f875a67d,ae27f2c8,e232c934,85092c70,ec071ac9,919ddd85,70186b83) +,S(a0cbb1c9,5b35d243,945a65c0,fc5706d4,e79237df,c13583e6,9a292fec,9a25e68d,b6d7e9d1,3cd61a45,f81c8a55,40dc06e1,3c6d7024,5e850a40,55ba4eb1,82047e4f) +,S(148aac72,a3fa8cb5,aa6d3bbe,5e987fc,d8050c3d,63b47f4c,832a4488,262463cf,7f29de49,a6e24485,5aa45f6d,36f2e2bb,72749f91,8258c0b2,92a33322,677e4a61) +,S(3b9fed07,3a1afd6d,e6ecaf7e,fa20e2e6,c28616fa,25a218ce,fb07cf33,66f3977f,f0a85b3c,5084c964,a1f0c936,e96ad3e3,4fa9e7a1,348ed0fe,ac7003c,12c65fc9) +,S(7de45ced,5228bfd9,557b443c,30fa431a,7c9cbcea,dfbfe0fe,cc565ac7,ce3537aa,515ddd37,69107033,bcb794f3,ac55062d,78e0118a,60c98fc1,1999cdde,fa2686f0) +,S(9cc1a35a,534f562,98010274,74f3a857,c17cf99c,eba1d5aa,251e61b2,55913dd2,c092cda8,f30fa4f9,96115da3,7bf8e12f,c653e243,af2bc7cc,c691684c,3e433ae5) +,S(d82f1968,759b3b9c,c76ad729,828a7283,10fc71fb,22258562,920b690,7ab52ec6,54cd0a05,26983b0c,73808f8c,ae1c9d81,1b372082,dcac3306,c470af50,8818c607) +,S(16e6a947,3d95a95,c31bffc,c291b60d,1f9c548,85e5a498,24195ead,b87b7586,19e29938,581b6b8c,2e50d365,5a957c04,80ff4c8e,4ed73276,f882c558,dbd8aafd) +,S(d07e7af3,ee4f24,e46b4670,477c3463,ad57a74e,57a17197,ff098e52,3b5cd237,fff34e0f,43c91656,69b1019b,ef618888,d4d175de,b6f20b1b,c696f24c,135d0e20) +,S(2bf644e5,6dc027fc,63a118a3,43faa4e9,8ffe42b9,c983014,50a1f4d9,e5665380,bef5c175,77be1fc5,f8f66fb7,a06b55f7,eb84ca5f,66ea4477,c924f925,5a262ec5) +,S(518f0d37,db1db93,48c80101,442597c6,7a88107e,5e25fa3a,b97ed524,fc5cb045,dca8750e,a4e9ed01,66f113a1,44974250,6eb9f3f4,df8ab741,8dba0397,694d7294) +,S(bd48eb6,c50774a1,2be9893f,8c21f624,57bf54ba,8799928e,9a9c725d,6f0554c7,200a6da8,cd8f0300,6774bad9,769f738,dd58f350,632742d2,e2cfa787,f570f6e1) +,S(ccc8d93a,871169e3,69336622,b64fa1c0,800c5b45,e3c0b130,2aa99e34,db1517e2,40ee7e4,d45e6478,efa11f1b,c4548668,88837309,4809f056,11efd6c3,6d01d845) +,S(86f6392c,75877992,311d2e9a,27be9ea7,27e8d01c,fc27dab,8f4527f1,dbf6bd1a,7ee29611,b647aa2a,3d2b1304,c49ee690,57150518,fd46add3,e7caf34d,10f7922) +,S(e034f4e5,927b0fa6,c3794105,338751f9,9bb5a381,6da1dc1b,bfc41d83,b7d7fdfd,dc9f02cb,838242e4,13282b05,cbedcab9,e902567b,b279fb6f,f2248e75,41a5f89d) +,S(8fc36a16,a9dbf00,5ee3a66a,b825510,75eadfef,b86e1062,837b49cc,6ab51045,c7ee2c40,9605981d,217d9957,a004a4be,66a88527,9ec9f74a,70594999,23d200a6) +,S(d7bb40bc,54077d96,d6489b71,e65dcfe3,997130de,b517d12d,9c10adc8,e73278e0,e2096366,2f64bae4,5aa0706b,d96b5cb6,5897c0be,3a8a9b46,8ce9074a,a68b0a4b) +,S(d7357685,8054c714,1147d2d6,c265ce40,3c882681,157b07e0,b8be0a88,63bd2ac5,94f8754b,2fc94239,c254099e,9cd5804d,d2ce03a5,380c59c2,4f42ebe5,85a9932b) +,S(5b6f8aae,d9353ffa,71da7e99,bd3fe165,3e720ffd,657dc2b6,669ae0da,858f8392,4eeafd05,369c6eed,fa6f85ec,34e245f3,e4966840,30253c10,6e74f473,418f9089) +,S(39444d12,67cc29e4,384b4f4,9c4f4886,ab4076a0,19a0fb39,f7b72c4e,42222000,65c2502a,af90ee2d,8de48adf,35388eb9,329f3057,1b77d0ba,50ea01a1,ca83d771) +,S(53de00db,f1efb0a3,c159c4a7,dbc9888e,16ebad95,cec2d003,a18488b,1b0752c7,328ecbd9,99475dfa,752d6228,5e7bc51a,87dc6166,4d14a6d3,3a556322,5e2fbda) +,S(48a3baf,9b4f7213,e280eef0,d5fb6033,194d1bce,eb48d9e3,d4814a4c,bd5ead39,3b4ac9c9,e04a3852,bbc6f7fb,cc94b20f,7e749eed,44a9a9ce,7099a8bb,28334186) +,S(f00d7630,503c1ced,3c418237,27e22d7,acccf406,fa16eb6e,b524707e,5b3ac541,9a1ddd3f,ec177626,b1254605,df8ed593,c9e2bcff,db7d0404,f47571b7,31ca95b5) +,S(e1ff1994,3ce7eb59,e570858,2e8c21a0,c4f8eec8,40815ee8,9a482658,8941eaac,528e5572,746e72e6,ce61b53d,78aa57e9,dbf27e3e,bc36c3dd,3b4972,d7353b7e) +,S(e4f13c20,c48e750e,e15a665,275c27be,23b3aa04,5915ac55,2562bef0,7459bd49,bebc7ece,c6e51f99,a2e6b9d6,a7b1bbb3,63901053,362a9ebc,a2d21cc7,6e87ca30) +,S(e36e8cf3,824b66e8,7083d371,60cf2719,598c6c04,ac693ea4,d2dd5c83,886f5f20,4ea0e193,8afe57df,3db74910,481a54dc,f5be9fe8,64f249f6,c88d0cbd,a086c60b) +,S(51e1f319,3286d2fb,29cb4a06,84d7c547,797ff5dc,fde9572a,63dd1e0d,2646365f,ac470e3c,6f30ecc6,31a90d7e,6c4c3d43,b8640ced,b9465cb2,ace2cc87,52370e3a) +,S(8e82daa9,c40d4e72,605dadd6,45bc26dc,22277f41,56d2a248,29bcf79c,d5a5fae0,7f8155fd,be9057b2,2b191dd7,233e291b,b01b961e,32a124d7,33ad99b8,167c53ee) +,S(79e56744,273c7aa4,34ffd5e9,525fa788,38bea674,64e2a595,9881b359,3e2c41e3,625089a3,c50defb9,f00ec764,b47122d7,eb786a20,f278bc33,e806e7cb,f385ecab) +,S(a97e00e2,d19c1957,51deb891,84e24a22,af7fe156,f2f1e068,79ded3f6,62743f79,881d7ebd,8bcb2c49,354726bb,c44ff91e,f3f5835d,ea73b282,944a5097,cd8284a0) +,S(3bf10546,7bbf5c3,14e1c3fb,40542378,9bc52fbe,8fb38aa8,f4a70727,b338542,e789586c,fad5b7ff,6dc68e5f,ef840e05,9a87ba51,e58462b6,39042c64,24f6367) +,S(80296c77,58791dff,39b4ee87,ecf3406a,49cce0cf,8437fb2e,1f4880b,55d9cc5d,3b06be11,3c7f781f,ce28753b,708bc514,902e4834,bdb09284,846c4ddb,90152dcb) +,S(48e2cb7a,a3eedbfd,f6a9cc12,359c4ff1,b7b0fe4e,87e57023,59896506,20ba52fe,dc78417a,fa909bbc,594411ba,87f72ee0,a0a45631,3cff4aac,7ad564b0,9ee8ed1b) +,S(dc91351f,cb775488,286b482a,a187d79c,31a3db2e,99730b6b,7b4805a9,73403d46,ed720e0d,2d13192e,8c180ecd,5e09fa5f,fc52e35c,4e509b2f,fa93ff96,f7adb1a3) +,S(5d37c139,48ceb4fa,9cd05b15,1fb344e9,3a2b2653,5b052b9f,1797319,e990a670,f790933c,2590ba48,7a832ad,6b940634,b62e16d2,7c9e748a,1856d53a,1974cb01) +,S(98bcbab0,f8d69b22,6fd7eece,a848ff4,15c8e325,ad9e8157,708eca17,77070aa2,7671e3b3,db715487,e6affd61,5218a7ef,a08aa949,9bb10206,e2de58d,ea150212) +,S(60e2c1cb,3de9486e,fa594384,d5903340,12ce308d,2247a18,8fa5c112,3f35b1c5,6de7031b,9767c66f,5c3c11a3,853bf722,4d58d086,b68e08fa,63942f2f,9369d1bb) +,S(814716c5,aa8256b7,50bf2f70,2e2f9bcd,aaf513bb,329eb467,d873ea72,304885c7,eca644ee,8fa96189,13fba31e,23533de9,eb7efac9,a0fcb58,6300a42d,c7d297ee) +,S(25789f3f,1db412ef,af60f7b,636686d5,9fb71586,200e8e3b,e6d934fb,72f8de77,e5222798,99d1e21b,5734a6f7,7881a686,863bc03b,8c58cce9,633c8302,2c7de0ac) +,S(5382f40c,98bff8e5,646a87fd,f3fcb6a6,27f2a91e,780a1527,ef18cdf2,a0f1bdcd,231d01de,e41ca3a1,da068ce1,39d97a16,39c027d8,38c7879d,9a1e85c6,4a32b502) +,S(448cfd20,9db2fadf,fcaab14d,8f57dd01,67d9ecb3,f18e9dc6,e573238b,a11d1f2b,bad8e2e5,5a4f56c2,435e8d18,14d2824b,2ba1bb67,9520587,5a11c315,9166fea6) +,S(d917ca4b,fe6335f,5012b69d,5444bbf9,f6c4893d,eb612ab5,667afbe8,2e5653d,3fde9a4b,e6a49756,27950f1f,f17ce5f0,aeabc055,3102c33a,4bb85bae,f32bad17) +,S(c7ee9f5,17a883ad,733de053,be3ef583,1dd974e6,a4e8570,db35f24e,91ba8f72,8db7738d,26d367bf,657d2a31,c6a08ce2,e06a7e4d,2df39f3a,2bffd062,79bba44) +,S(83f0a19d,983ed6de,7a2b173,77c843bc,819a77b8,a32acaed,b7085e0,94d9a30b,d2ab3e96,67be4473,d8748b63,6113be60,880acd47,6d574e00,c37f4875,2cafe6a7) +,S(f3f8aa6a,61687c40,98fb56bd,e74d83dd,59674079,7c52f2e,2b299277,2313989d,3a690c9d,98503a3,2d201ff9,a896365c,caa1b54c,36857a21,ad63994f,41a109fb) +,S(4c1a11ca,a30bd6c7,d95d6e8b,3ebcb4e7,914fdd40,4384716a,283ee1ff,5032249e,e7e718c9,4653ef40,d214c604,e65339d0,6598f17c,37f82880,9b7e5215,b9424da0) +,S(2941b952,3419a018,53f3bdb0,420f3182,529ebbc8,5a1c8f6c,1d922c19,3ee5477d,377f226c,29ca66a0,aaa05baa,7f6c1ede,80482b51,8982763,864392dd,3e628413) +,S(9d04d07,aad49af8,65bb3eb1,8c7078ac,4b5f25ef,e18572c3,3842161d,8591a6a1,f572968d,ec2ca89f,b45ae02d,119644b6,779eb0ca,1d48f724,f8512ba2,2c83cd34) +,S(4401f767,c3c3a101,b99a983c,9a622824,d660c50,177bfef4,a2646b06,43d26e20,7952faf7,8e1b114e,16429309,1d0669e6,999f8bde,ee980c0b,b7d669de,86d4e342) +,S(94b6f427,fc4017d3,b328665b,acc863a8,ab8a1c4,7b283fa6,8d7b7f0c,1bbba31f,1336067d,35f0d2d4,6ec8199e,99dd07a9,9cbe7725,a9981868,e09a0217,df40d85a) +,S(41621ba2,92c79bbd,463b87cd,de75574b,f2fba59f,92025445,7389b4a2,4de7288f,355337d5,b6f30b78,81389951,fe67b943,3bc70a07,b294d11b,717ca2e1,a2cd3188) +,S(b0838415,6256c1d3,55900dde,6126818b,8a9b27ba,ab14d73f,c33c399e,fda3594e,2aaf9190,55b162e2,91cc5371,b6a4285f,843ef8d1,5069ec7d,c5d68475,c5a954a5) +,S(cc99a14f,ef418fea,5de9f437,7ccc1426,7d910f13,e8448fb,8ba92746,6c0dc9bd,2e30b64,7c01c12d,d42f164b,ac0c9bf2,22f7b3d9,2cffd7f5,c77aebb7,18536f28) +,S(8d2bb24a,dbec1858,d565d59c,1d805d8e,7b21d6b5,b967dd50,a2f420dd,5764e37,cdc6d730,e78daeba,712537c7,8a09322,fc371d0d,6a5645ef,e7014bf8,999f712f) +,S(63cee6de,f18cf4a,e6ca1871,7750e328,dbb94c6f,27422192,3b233a3,e50f7c9b,81230dad,ad2d233a,1b631b8f,7a885828,ce005031,e1365436,7ca34173,1efdfeb8) +,S(62084e9c,1dc6dd03,9a68d5cb,fba19dda,b2e4d07f,b8f46114,926a59e6,92755a0c,6dbeb4f4,62fb1af0,dc273a,aa827e0a,cc9c7f1a,fbcdf02c,c4bd98b7,5061a26) +,S(110c0eba,f65b6280,14e368ec,17072acc,3f34a937,7df79238,c2773625,5d8dd827,1a4ba716,6f236258,961d5865,b020b83b,c3c3e74,2dafaba9,649ff85b,2ae75c6d) +,S(d848a51e,50fb234b,a807306c,fab97405,baa50a5d,23cd12b6,8927c9e6,f1c87e00,9db9be62,88fb00c8,4cae4b1c,c99449ed,51fdb49f,3cb7aadc,16a72b6f,a96a4fd8) +,S(42311893,a2706ab4,52547f3b,3901b9ee,a8cf2660,581b0023,96bc9a7c,e14c2b5a,750f86db,9ba6222c,199c2a9f,77609a47,d6129a1c,e9ef4d70,daad3d2c,ae4165bc) +,S(4439c4ee,471adc41,32f45496,77ca1e32,6b252f1e,5f17f741,9fdaec0e,3834d37f,bd75c32c,22c5c3de,62f16ab6,6f86f93b,617c9565,b3859195,81a00ee5,75897e87) +,S(4dfce3da,b19231da,648b0bbd,8c52fdb6,e02e9e79,9806fbbf,36bacafe,c113b053,de8c5f1f,4b727fba,440a1c3e,147cc1a2,9d647c59,42feb42f,69d45a2a,13042c3a) +,S(3bba2b2e,e8e72736,f54f9158,281f6c14,9d0ba6f3,ec6d89ef,cddeff9b,8117dc9d,ff087274,77897e56,e5e93f94,7af6ca0f,6f9a2186,a6a60d2b,af690a30,da16ab24) +,S(a2fede43,37bc6b2,482d92ff,f55ab0c,e239a00f,4fbb0595,27f7d67d,8317deae,2a791508,43beffd7,ef08e195,d0ce022c,f780116,2f1852da,98c3638d,45869ed4) +,S(221c4e2f,f503eb4c,4a1aa8e6,77370085,b3d64d09,8430a185,2cb1cc3e,304bc0a3,c3c37b1b,f954e79,c124fd70,ad3f8765,a70a7929,4a262de1,99e11dd1,95b7ae41) +,S(a0c32f80,f7e4ba05,caf945b,49b91306,f909c8bd,559e4bfc,a58ef3c5,14740ed0,d3eb8f01,46ab4700,41de4cfb,dced45c9,966aa1c6,38a42c90,41ba1891,36f9562a) +,S(762e7a18,b4fe627f,1e1ca7,57740811,bf33395,cc962aa1,dff79be4,18da85c6,a0df0f46,50461c0f,4f8a743c,3455b842,5875f795,4af56b93,dce93234,c4f51ec5) +,S(2f02d935,cd95caf3,d56cfbe4,4337b1f,78389f34,1146f561,b8d632c4,b93a29f0,173470d8,23f190b4,f8008872,b23f1a32,9c45441,c12fe87b,e74e927,18f44569) +,S(c9f877d7,2eb816df,1c5adbd5,9fc98a39,c5877d6e,24ef1612,314e6392,e7eab212,14ce9917,88cf7eec,5a5a52b5,8fcaa9f5,88bb0052,754b3ad1,b0858e4,6a067c4c) +,S(cc4dff69,18a05cb7,a6f296df,6cbf5d0e,d6dd1c23,ae76c5df,239d2179,a0baa172,1cd438b1,b8471750,261b0650,663b4a56,e4dda0ba,b5390cd5,b7869448,654a4dc6) +,S(faaac74b,3ed2713a,29c10219,a78acd51,ca015a8a,65646a5b,cccec828,efcbfaa1,adcad43b,dcc2ec0d,bd65c46b,7fe79549,b2e74cc9,13fbcecb,f8bf661a,7d30d934) +,S(171356c9,30fa9bd0,da1444b7,35be6c0e,8a0025f2,3d1a2480,dd0aefc4,44fdbf46,770e9edc,991efb9d,ab44361e,d2398e54,62e2464d,2ee2a14e,b2e149bb,9c9bce3f) +,S(933f56a,e0473763,d2d491af,55f9fb19,c055017a,7a81b43,b641007b,e5680214,66059c47,481cdb63,481ea647,f494e541,ad1d70ff,93b1eba9,f85a9cab,a0ea76ad) +,S(a300f22,b182ad37,d7e44691,bc9c9c9,8a397f76,1ec1acf6,64ac1fe0,5bbb6b34,bd6f2ffc,a90974b9,460de269,4722f215,684abe44,6228b500,7b42bab4,5433593d) +,S(fc5e57ed,6e901355,f97c1d62,dee4d630,878b79b5,528e3fd6,85d6e3ec,af8fe1ea,1122127a,6c21cbb9,e598b1b5,68507bee,bc7c8549,345c9d49,8c4a9b2b,2f5bab01) +,S(5a2f40e2,7a64811f,bd4721c1,99019523,899e956,ea340bff,f3f452fb,5231ef3d,6b2bf95d,12578c79,74a06d26,39d71c31,d8c4a53a,6279886c,20a122d3,2093d547) +,S(59783333,df0b99c5,be76610d,67d7ef62,fb15757d,8dd1f310,6f9179b5,64f2084f,f2a9ea80,dd3d170c,813f4623,ba7583eb,7d309d97,c34f4c4f,a7676694,7e5b7f5a) +,S(cf61124,6b513fc8,73f72bb3,87a29c2d,a1c8bed7,4ca7ca08,1546db1f,37672d95,d23c3c76,697f0443,9ed1d1a5,661fb0fe,1e182cf4,2e07c787,3eff7abc,2f8c70ee) +,S(edd14f05,133a1480,e26eb7eb,8063f8eb,1cb871c9,606957ba,ac14b173,2ffe7d52,d682f280,d295d528,4c1b3aa8,445c274e,ee603308,ca4792a,61b5f8d6,34b1b70a) +,S(f9b0fce9,cdc8af3,8b26e790,2c95926c,33cb8d30,a5de80d1,75fd95c7,fc1a1713,ec7e8df6,f3d163d7,181083cd,c8cf4a89,df958b8b,d6fce2b5,d4752240,f2a3e6ac) +,S(ae906599,54187f57,89306981,7417c566,d9ea4315,f07094e4,e46bfd24,1746a3b7,2241bdad,c09d3313,e9d006ff,2a04709d,17335573,5be5b10,6775457f,231a129b) +,S(55f0f800,76492f7b,f5e134cd,e581ae3b,f68fe9f3,44107540,c54b350e,47d956e9,22a0891d,e68488a1,dfbb4b8a,35fe9835,e258dcb5,c70ccb98,2c8a48cb,c33956ff) +,S(90c8ab06,86fb40e2,31923bb1,df86647c,6cb7e3c4,c451d0c1,b871994c,19663bbb,dc748cd9,570eeb2d,c6fbb2c5,c417979,cb30d718,94c8e463,e3b7be3f,fcc80ab4) +,S(cbf47c2d,6b21ebb9,fb268e73,56ea849d,3e476d6b,ac09eb60,4bb2e27c,84a4c694,f0288fe6,8042894a,39abf8bb,e5962421,416ed9df,dab081cf,16e86fba,7abb2873) +,S(7c30885f,c18b68b2,8fff758,2148d738,d8f1bfb2,46543b16,37e9fce9,7d11fe80,3e2d1cb9,31a11c15,b26c6d37,8c886e10,52c06718,a63e621c,7ea51c76,3c013f2f) +,S(ad614919,d1bcba0,dd2a9da9,de9207db,e137c772,37f333e6,4f11433b,f66d7753,fcaea18b,3512a188,9d604619,2ba759b4,190b72d7,ec53d0b,aad9e01e,d239753d) +,S(8df7041b,af8c9c15,70ab50a8,1ed335b4,a7bc4171,a3939715,a295ca7c,4e5e29a2,e892e43,82677bdd,f3566b24,c7ddc6dc,e12eabc3,84a19a13,99b64ba4,bdce02e7) +,S(9f4c1e57,61c8520e,9a751891,ac1bb5f6,217f4e77,7dd88fbf,2a127804,2cfdadd6,fda210ad,c6cee889,c35831e,b3e12c7a,ed4b6963,9f3d4860,9d9b960f,4e7d0f50) +,S(a436f4dd,23ac70a2,c093bacf,91de3093,174de618,7525947c,2ee8ccab,a515acbf,c0e9d9d9,2a615b9a,9439a450,927bc128,5879e8b3,7d460ec,9fe57eff,7e19afb9) +,S(a38eed16,699743d9,16d8627c,5039695c,37497c67,c59547d5,67bdfa20,b86f1930,6e3e3ed6,effc3b5a,c9b5b5fe,3f26c91d,3be89bb1,48414c18,bb0e5454,49d9d7fd) +,S(5f4590d2,d4043a1,faf659e3,36b0f24c,b6f1de5,88e92586,83361eba,e2fbf3d6,250c0a73,42ceec0,3ec1af11,764e7ff6,ccc833b7,240466,15890be0,55197db) +,S(7565860d,c6ebb900,68ed16ff,8f7282a3,d52beac7,73cebc55,1be51f44,34cf56c0,4b56ecbe,526ed458,5428ad7d,e9d7f3eb,9b84687f,e01e1346,c32461f0,bae24c26) +,S(3e7c7fce,3a56ef01,cabad50f,2003a5a,77292742,be80b5de,c537b50f,95a9320c,98cd94b7,926e7111,f3778904,599fa9d9,d5dc342c,c495b914,a32d2722,93dbfaf1) +,S(131eb457,14cecf84,abf1aaf5,ec4c62ab,2ac91a0f,6af57552,2eb07274,b7cda208,a93aed4f,f0512604,46c61393,faec55aa,9b017a13,60d13031,27bfd897,e320839e) +,S(371bc601,e46963d7,388bff6,9c4b5f42,a98e68e0,2db3e6f1,637dd24e,a2a11ec8,e2fbeef1,932fa241,a9785238,c1b89269,21bbc7f1,3aa6bdd3,7b39d94a,49f84622) +,S(4770db04,92ca73e8,a0136bc2,2c45185,b4d45934,e4430237,1bcf5045,cae47d6d,e1af748c,25b1d906,ab77e080,d21df4bf,99a92f7b,4b1d790a,f1aeef36,2d41ec22) +,S(b31a8643,5af5db57,759dbae1,67ce3af5,e8e2ae34,20786fe0,da82812c,9594bfff,3793425e,860dc99a,a96a85a2,585b06a4,f5aaabd0,551fe0cf,3c743be3,c09aa4a2) +,S(e05eec52,84a2501,fddfe6dd,1619c374,a94121bd,7d1c99bf,9b2e665,bd3631f4,c0115f17,2122f806,99a24ea4,bd742425,4d883d77,6d269ed4,f6cc5626,ffb8e622) +,S(e55ddc84,ee407d3,45b84305,d97d3f57,e57810ef,bff3b35c,e4d8a325,2973d5c8,71f77bb5,b0fa6141,7e43e425,78ebb13c,abb87a6a,fa0cd1a1,26e8936,427ee677) +,S(ed7b6ab1,a5c8a9aa,284bf3ea,5501d33e,1aabbe76,f26a9f0e,d46dd23e,decd788f,2280bb54,19d1a620,18454a4b,cdfd82fb,824027d8,c51770d3,77233e01,1d0db63e) +,S(d4e331fc,3da4d8ae,e13a3329,acd4ee69,ce5cb71e,224f2001,f9606714,8090e79a,895141aa,2f63cd30,9f4a6b42,d6a88112,77821e51,6d08c52,1ae8e158,655e10d6) +,S(4f0ada0e,a8ec1ffe,6682545a,bd8152c7,2654718e,120d3d54,d648b487,896d4417,a603b9ac,6026ab46,47f5acf5,5a9fbaf7,20aaff54,56c88cd4,dc5ec7f5,935aa591) +,S(a2d13541,b08eeee2,4f086a9d,20df7528,a884cc05,4106b1b7,543eae3e,fdca84,5ac0b166,294d6b91,527a5249,7fd605ca,4bb67c35,85e002d1,74503b7f,5c803a45) +,S(4c1d520,14a0459,2b8887fd,afa945ff,cbfc4722,2ce008f,64fe8123,b5b6c8a4,243c4280,17b7918e,57d06597,f348efd4,ebcebff4,3950a608,3bb3e334,50ed738d) +,S(5f9e5a6,7d13e9b4,cc35b8e6,b2305cb0,fc2f5b7d,ff32ac55,1a5970a9,2bc54390,74808637,96965f7d,bc45833c,e8bedf00,85eb0cb7,26f3276b,30d9543b,7395ff6f) +,S(d76c6f80,95381b17,80ad28a8,80e29ab4,4447c297,34a4d286,98534bca,8954575,44b6c3f8,6b30e01a,61205178,90e429de,aa8273f1,277bc498,59c87300,a561b27b) +,S(7577729f,f78775af,703a9eae,b5864a18,6ad19f1d,dac658,e7a8a05b,d9df3344,6b519916,cceaaa53,92d2f822,98e7ecf3,3fa7c5b1,2e705345,32ec3f99,2f0afec9) +,S(50378f2b,457f04ba,d0b19425,86ada992,78700c8b,36bba67,fca916c5,8edc17db,cbd1451c,26bdcd49,1adbf40b,ac5338cf,26957364,f82fcad0,5fea3c2f,76273fc1) +,S(3748dc68,64365160,26384ba0,ec078ff2,7606eb44,e3d62b50,b9138be8,cc9ba86c,6c04e414,f624ae9c,ddd005a0,4e01828d,6e7bfb81,d350d271,96583c99,6611d709) +,S(b23a59be,8ea8d561,fbdeaea4,49eac729,3402f342,d05fd404,7f9c5d1c,57eea53b,bea47c68,f2d35b82,ce3c359c,83f5fa1a,95d40eff,702ed9c4,2fef0e84,406dfb9b) +,S(e4b451e8,7b42a5be,5c5f443,5701d2bb,9538fc06,925ccb49,ebfd71c1,16a64770,2af84669,9a790749,48efa86b,2b79b985,4cb261a6,8a9081e9,e1467b77,2f156da4) +,S(864ac6ec,65b8b086,346e671e,8651ddf9,de74a215,64f3da12,42ff7548,96dff165,f35738a7,7ab48555,91ca5103,c8d2bebd,b2cbf902,dfa93188,d68eb600,271a8730) +,S(b7d04a3d,a6a863d9,b7809022,56643864,fd36c5d7,9a056ac4,ecb27257,f02ecab0,cc40634c,6ba5dc23,e59e4e3b,fe6a07c0,1519abbe,7530f1a1,5ec7ef70,6f83b0c1) +,S(ddfc1af9,b51eddd8,2020de0c,4a8377cf,bd6e2531,ebc844c,7ca8fca2,714c1a0e,cf77c8c,1742c4e8,b22d9f54,a25daab9,c30f23f4,da3e1ad9,57b9660e,3ffe3951) +,S(78fcfe69,d5bb91e3,a13de26a,614d8479,e00c828f,8868fc30,ea75b47c,d51fcbe3,9ec67963,6b7053ba,b0cb231a,ac28143a,98021b48,4340f060,66da6e72,47f2d47d) +,S(dcd918de,16961d71,8222b4f8,19e76ecb,138c9884,7833a8fc,d4204dda,1115863f,abc3d8ba,c8d808cf,ca12318d,d13c2932,3ff7fb0d,813a5ed2,f5e52aba,cb23bb04) +,S(39ce2ef0,5de08106,93294eef,537bc212,e6f64fa2,96ccd2b2,94a0806c,c6c3177e,25eaf4cb,b7697a3,97a0ca92,6f3a266a,bbb5dda,78943492,39fd29db,78730556) +,S(495e4db6,43d4e89e,df50e937,e97ac4ba,1464514f,d3a46b5a,bae6a53d,3157a04,7d327e18,b2960d69,ebe4251c,f8c416b4,f84bb81b,a20ce6d4,6e6b1c57,e6ec1701) +,S(91c17e8c,b6357c0c,1c36dde5,29f9e6f7,a6cb6e97,f5b67dbe,fcaf5b96,637c33c1,2fe52097,fedb8ac2,efde2692,20f586af,f07b76e0,f685ce85,c965ec1b,a54c39ba) +,S(e3c4f480,3dffed44,6291f11e,cc5c9590,1c28749b,d6ae18d4,c0371221,d5ecfdbc,5a2e7102,571e27ce,974ad71d,acef8b28,4e7fb827,5453e40c,65b2be92,cbdc62d8) +,S(b3ee17f5,a0e81e7b,48814377,af9dcbb8,59ae7bea,5680f33a,cc15d8ed,75c25073,135a3c0c,9a6d2825,a6035afe,139ede23,b46332d5,5739ecb,6d092e7d,6cbfe86d) +,S(e4cca844,2bc91bed,342628e1,f6492335,7ca6e8d9,5e5b5732,29866066,eae76b75,dd55a555,39b5670f,f70b42b0,f319ac72,409f74e2,31d9cec,ecc8d90e,516f76b9) +,S(ab39cfc1,81c96089,998c02a4,64eae7ae,e517e4ea,962b6359,b5ebfbf7,da223b2c,def6dc57,b4dcbbe2,e6dcfec1,8281a189,72ba3b50,f996096,a22e4ea9,77d2fc4) +,S(22a32663,9e6e0469,fe4cad40,964f081a,677572d1,94b3ae01,f8e45c84,dfab402a,1c9c9d95,bd4e0ef0,5acf6148,44c98fcc,db44cf50,5d42af26,1b855f98,40d2b437) +,S(2bc788cc,eff94bf0,307c5ead,4fbc4dea,db269da1,7790a903,7c2fcc24,21f9cae9,172f3461,648c5520,44081c43,a8c78c3c,ccecb248,9d60d384,aa703e4e,24bc4b41) +,S(b930dbd2,a6720601,35a75bb8,8bbe69e8,3872c636,9fbcfe4,c622a300,2052729,9983f03,a872e8b8,2830e7f9,a7438b39,ff3f6836,3334debd,83940ff5,ae06f366) +,S(b435738a,2aa9ea40,702db448,da6f186f,29bcd03e,872a3466,f33b2e39,7514e3d3,5828d246,2d3c671f,85617e55,bcc34905,1678cb8e,f61c74e,92c54473,9098636b) +,S(c91f2e81,3acc9baf,2c52df27,d4180dd6,ee1fe67f,9db6223b,532dffb9,3072bd25,1c9c57d6,96c12f13,13afa0c2,b377e1e2,b027350f,bd24f455,69c15f07,85b73c0c) +,S(bb0946c2,8099ba93,8f28068f,416d9003,bad0d06,f8c7d31d,2b995f00,5c8d36c8,8e3878d9,3b575fe9,78fe1a62,1e978f28,1560421f,7782c164,8673906b,ed76da4a) +,S(485e3aa1,2e823374,cd506521,a707f367,5298dbf7,cb44f398,c31a06a4,8f085c8d,a12c8af5,7079b2b1,5a339d4c,302fc262,af0153e0,dfb4f878,a4b60017,86ca9a61) +,S(850f9448,d3282256,940da03f,e8a96eaa,f21d02a,78867db3,e8a9fb9a,713e844d,4551e4fe,3201ba2c,a74df3a,f58a820d,3a67836,a5a8d82,59ab2442,3bfccb7b) +,S(a74816b3,f66c1fe4,9f8e51b1,8eaf51d9,449cd4c0,54b6dc43,682c853b,be39884,4e7d5fb2,47ed7ea1,45af0cfd,c4cc1059,396626e4,80ec804f,15ecef7e,b372f7fc) +,S(2af5c56f,c71da217,79abcc1,73412016,e1eb702d,6b98af77,85ead6b7,7b20136d,7f3110a2,c6a82623,b2c49e95,a31a6b8e,783911d9,b532932,1b651541,86b706bd) +,S(9291ec91,a61adf1e,90d63135,fae60edb,1a313714,97260903,9684fee4,339b5834,d530506b,e8b0dbb3,fc09649f,1832de93,feef42bc,ade2de09,bac1dd17,bdd03884) +,S(160ad3c3,659a7816,4e9cb47,4e18ff6b,a8581fd1,823ce8e1,14444292,cc605b54,1618464d,5f28d719,a61f939e,b1eb91d9,a59f2c1,818dbb58,b6e1f426,a4d141fe) +,S(352599bb,8640920c,2d85a5d7,673dad8d,ea4280f8,6358a572,b4071934,60ba9d58,a78a591b,e8064f2b,22d7707a,d3c95cac,aa2ac3d8,638d564a,50f7d1c2,8b538d0c) +,S(ec20efbd,9a8c634d,92f7a728,1e9e15d5,adb37ce1,2236bf33,4d12c4fb,7b6ba527,acdb29bc,2a5a3115,3919315e,27af9d8a,e8c54792,fd5421e2,a9a57b3b,d51b70d) +,S(40d1e4da,8bdac6ec,55ee847d,c59781e9,bbd38785,1863c64c,5cfad460,e2dbebc3,c630581c,b7c92e68,a67c7d5b,dba3ec18,4e3755ff,27fb53ac,d3a2655a,316e9ab0) +,S(fbe12740,2faadf2e,2b1ac1c3,98b66df4,81674c2d,f0ffb512,7dbba444,e7d08c7,d00edcd7,f97914d0,3ac87182,14491698,f3dfee34,e6e8587c,52f2f7b8,b7f412d6) +,S(d781b5a7,47d191fb,8850fbd8,fb1e123,4070d9d6,2643008b,b0cd930d,c191eb03,a4aa95a2,5c74ab55,f95f2924,519b2911,9762ef30,248db792,d906b18f,346afa11) +,S(6472fc42,d926bc7c,115045d0,395b5e9,22790c39,ee520529,44a446f0,9ccf02cd,512014,a92593de,e2264e37,633c842b,45d1ab5,e2ad068,63db7894,4605a99d) +,S(eee180c2,e09601e8,6a517a93,54607a2c,5fa6cf26,4c80ffd2,c5715ed6,395e0c21,c8cba798,221d6fc5,b4fdaff7,f785ca3,49490af1,ae655711,e0649669,7483e7ea) +,S(900b262d,a172ad41,15bec071,c5763077,9103bed4,db500ab4,fad4b750,45ada2d6,316457f2,bbadfe91,3f418e7f,b3fa34d4,82c4c1d8,516755aa,371423e0,51e2cbaf) +,S(7e93db87,4e1e5070,772d3371,b9ef29d1,5d5e03b1,e66bce20,420801a2,13a34c9e,cdeaa624,750032a6,f5faa0fe,e00cdc10,63f3ec47,b5bfb56a,bc2c2339,e38465a8) +,S(461fb424,65f3a55d,e66761b8,44abfe3a,d172877a,3edb1f5,da98c82f,89a379ca,768261fd,ba032718,f5501dad,c05e41c1,1f584aa6,6b8e732b,7f15285b,18e815d9) +,S(1d9f581a,f0f61a0f,e5d0f0cc,91ba4f93,6de28aa6,5f35059e,997de778,9419e09f,7fe9c98c,7d59dc56,160302e1,89676f54,3f02db1d,622251f8,adf369b4,891bf6cd) +,S(8cd70b52,904b2aa7,240f7a70,e4efe379,66386dff,3ccdf62b,e2343821,767542c6,b3c3d03f,4c7e0b1f,f08bb2,f72bc6ba,e9e293d,886541f1,ff86bec9,b5ddc2f2) +,S(a0b547ea,9c08865b,99a5b8b3,800daefd,99097b23,4e442a2e,819aa628,eb4a5261,607ef115,e585100d,c003cb4e,1a27eb60,fd9d1e18,f2d23a22,80cddd89,f2ca2952) +,S(ea8a60f6,547038e3,683a649c,27e81ab4,192406f5,5fdbd775,78dd6360,4344d289,98afcac0,b05ad5ea,8fcb9f3e,3bc22f16,881b5fcf,5060c691,ac10f746,79822fb0) +,S(e6051a9d,882f9ef,77d488fe,55bb7829,48bce506,91350755,1a8cd4a,14b0a711,1c9cdabe,9d9eb555,cb9257b8,a60af75c,c1ec0c82,4d933498,ffaa14f6,2e920fce) +,S(600876c9,c50fd337,6ce5efb,c07e0e5b,165338c5,4f8eaa4b,39a525e9,88765674,401a671e,f52b21f3,83df5da0,aa0b215b,b044202e,606a746a,213796e4,dbea4189) +,S(3b045b9c,5ef6fdd0,9b3efe12,169e0414,3c0eca56,5b7e0185,274f1e97,f6b2ff40,aa5773ac,b21107c4,2a8085d0,5d6914c3,a135a47b,cb136dfa,d20e6813,62780a28) +,S(dce98d32,ee0b55e0,4d67807f,ded9cef5,f9504b1d,e552a2dc,643ee7f4,b1f0f1b6,d03ac27c,9e992497,cbb746d6,7acf5427,aba058e2,801df1b8,d435a1de,1f3fb086) +,S(365b1148,6e8a60fe,8a91c07a,93686787,dd5aea7f,22fa1449,58be984c,e378fd76,2c7f5217,319e5d50,4c5ecbbc,bdbb8ec1,b36e2767,54f405c3,f852b761,ef01c9c0) +,S(2e37935a,4dc465ed,ee8dc3ca,d4ede356,3c3720e8,57c986cb,b9c73b2e,ab6b5806,bd0873f1,5278f46d,802dc7f9,d7f81a4a,839cb690,10c6d522,c0e3945d,4bce346b) +,S(49b68e1,93745ad7,b60ddf92,70d71958,81cd8585,6e47b0bb,fbae94,f1647568,ca1d4bf0,13e43ef,53bcb2d0,5f4b85be,1dc82d31,f4a7795,755693c0,72819dc1) +,S(6df53d6c,c79c5a88,2c2b6f66,e9ede075,6c8cc6ba,3e60620c,b8130fcc,fa744564,48c3d6ed,3b2b7c59,2472f291,41a61495,4597f77c,afb576e1,729804d1,26431344) +,S(d99f8f06,9104e3b9,26163672,767145a2,2f9467c1,c8e32b8b,d28e170d,bf4865a1,ccfc46b0,55aa5d93,64e67c4e,2c06cb18,c2b43333,e4111479,fd1bfe50,52543d7f) +,S(77727c17,ebbf8338,ba807572,8558a059,e23af7fd,c68c342a,538ead93,e59930d1,a037dc7e,2cde1802,fe3bc65,75f50ca4,ede75194,133e7083,66de0338,40c629ea) +,S(1d40ead8,563a4ff7,c14c84cf,ad1339ac,36ae3789,99ec2663,77e0ca2f,6be22604,8497b443,b093917f,ee65a5ac,1c4dfb87,3a6b0c2d,a968d713,a711ec12,20d1face) +,S(2a39f41,adf5ab33,71a27c20,51840d37,e3226999,17a3d0ef,40590710,406bfae0,cdaf7a8,58e99fb8,23a36b04,8e844c18,8dd1a49e,c70488ed,155f2ac6,905c27c5) +,S(aaae4b1,d8b08cc4,8971cc92,fd974a21,66f5ea92,b6ce215e,28137088,19c684e0,9237995,c10a348a,84fd5cd5,5151e33,c63739d1,3a425bde,a34d78e9,e3cd8f0c) +,S(a5b0c042,591a53b6,334939d2,9ac2c9e2,11315a54,831ef440,965fdafc,e6477d40,ac3e7cfd,5d1f0248,b362b206,b3c7dd60,bc9904c8,d2a416d7,e117a5cc,4a62ca6) +,S(c1b7164,e6166a96,a8f643b4,7f094be4,3c16e3b5,3ebf5a1,2782e41f,27c63f0b,8469161a,4ffaf1ff,5b50ba81,1de3ac79,e34810bf,73ed1207,fcc0f01e,75663a98) +,S(98fd825,4b7963f0,4d8e0f2c,8eb26dd0,342e605d,8fdf28b4,57b98e14,adca63bc,e297a80d,213f6664,77f9dba6,5cbcb99d,2dc14325,be12098f,22061115,b1a192d6) +,S(f1f08649,71b6ed49,2d34127d,8c2e6f19,48c464cd,816acdc1,63eb3aad,594c3281,26ae2a0,f9e04fa9,13b8954d,85602e6f,506a0de5,2fdec31a,346338ab,f31f5c) +,S(ff470cd,c324d2ac,a63441ea,c0506fb5,b63af83a,61a23a91,17240e23,930dd197,e601f66b,b18e77e8,4607a772,a1efa73b,30734b4b,8fd31eba,5a5260d2,5627788f) +,S(90eabc6d,968aa196,b5808127,baabbaa8,ad0f82b9,332ed6dc,d04442df,bc6c63a6,d6df0f67,ffb23cd7,2dffc4be,44476b2b,2faed3e3,dcdc30f9,4c1fa4ee,bb5038f1) +,S(e217b126,acc507a1,b41e0826,b300363e,bc0a43b6,feaf3866,ecd4b8bb,cc7e11af,7eb28def,83db33d5,8b8eb733,2b27a386,921e3a5f,b0321ecd,a8d1fc6e,49e98f30) +,S(2a196fd5,82b85d5e,1772bc21,84d8aad2,b5d008ee,c795628d,20de68a9,fce1d184,994b4657,ee9ee3d4,fdf8dc8c,cad2ff1f,68526c67,89d82230,c3399f6,62201303) +,S(bc9e3bc8,3a6eca7e,8a9897c8,1118f7be,ca770cbd,7e66c2e6,1321d026,7ade4342,9d7ef7e2,d544a561,ab899291,75f35d24,e2b07661,2f84b0a3,f346542d,714f3f4b) +,S(ff2813f3,842a9f84,fea9e367,6f12f209,7e76b8ed,e691ccdf,6f6512aa,b2f198b3,206e3bd6,66d0c161,ac6de438,809f485,b8b6682a,f402bf76,18a484a2,c3fe1949) +,S(6e7599df,2d38d63e,be142321,769d7ca,34a50bbe,4e0adbaf,6c7479b8,d1af05f7,233dcf1d,4b0e4d88,4a9ed56e,b6b4946b,25614345,ecd182cd,5556faa5,e3654c8d) +,S(1edd9cd5,6c82eb28,14d844d1,a9e5c167,8397662e,b576f9f9,ba250719,33666146,a00f4a85,b2ee83e,949b5cb0,4d16820e,39d48ddc,95dd965f,e24251be,3df43f4c) +,S(2723de06,1f794cd7,76a4a090,738a81ea,818a2f73,92d7ab07,18d79fb6,407324af,35c7a9be,f9810c50,3c53429e,344ab888,38daad72,77cd0e78,3db1c1fe,c6bbf41f) +,S(4c4fb244,4cb54def,6d9659ee,b43c13c2,3874ec2b,a7c6b53a,f469ca26,ab9a213d,bbd2eafe,56447a9f,feb9ad15,349b338f,d4a1aed8,5a356472,82751e5,5b8efa3d) +,S(736359f7,347995a8,8e306977,1fb3fbcd,12847ed2,8e4e612f,25095720,667593e3,23bb37ed,4bce0512,35a215ca,6b8f9868,e303fda4,80d655f9,72c95bf9,12bd3741) +,S(15cb9594,ee936bd1,4b981394,60879bee,6f33b0f7,c0ccb293,825794d9,85f595be,aaad772a,2ec81ab6,13775e9,edb274ef,e2e133e6,5e58949a,c7f25429,fb1bb152) +,S(3b5489ab,97e5cf1e,84a90eb8,20b8772a,df574777,a61f0a9b,41e6c567,6be78fa4,145d13c8,e9674a17,ccecad63,f113d64a,d59a1eb,bbb10cf9,e7134cf6,562219be) +,S(78d1b99a,a6f9d385,b09bec7b,b59ce26b,23323e4c,3a259c56,68417597,a29dd2f5,1c8d11f8,e883b210,cf54369c,e9f6a280,a6b890ce,15b4a0ef,3cd8837b,7e16a122) +,S(385e4e8c,5ceffbbd,3a813e3f,938912e,d2fd2d18,2be81210,70cd15fb,39661e85,f4a1c414,9f5cfbc5,213ceb21,c1cc08ca,c9b168e2,58cdec70,36e2dacb,ab68069c) +,S(310f8c70,d8e46c6e,48930df3,c53e9292,a7c87230,292d6b1f,c28d9711,951aec6a,6fe319d3,c92c07b2,56715154,d5d7046f,59d92d5f,6dcedefd,b0386f5c,3417146f) +,S(764f3c30,23d1d50a,e170b80d,48d58923,c1f91a96,42210912,94066585,d7c70bbb,bd92fb1a,becdb4a0,ed941146,878c99cf,c3ee9eb8,c8f2b8e4,b547cb40,e3dca724) +,S(3d5d23ba,b357a37f,1bd3c86f,84010b8a,b37a8075,3c47b1ab,36f7ef72,a3e68126,2bbd1841,bdcf0265,838893c5,f5f9c0dd,7ccbb461,93068e0,8211595d,6b34255b) +,S(a70431ad,cdfc7b8b,febd2aff,d6759e94,6d4ddd7d,50f50dc0,87e256b8,2a7f8a1e,3ca4362b,9362b2ea,c33d77e8,befeccb8,23163921,86ff5f20,384247f5,9147f593) +,S(2a388212,2c1b2ecf,3c800432,7c5dd403,2263e9da,245dc697,bf7d63e6,300e8a7d,ac96d188,53b87bf0,3dc5f2fa,fa9b73a9,a339c0d9,a7245175,fd72c822,12f6ff78) +,S(e99c59a4,ae3022e,b50bc545,f57e3013,56b49bff,33069a9a,b4d17c0a,424b5451,29b24b79,fe55fab7,6c3737b5,61505916,b4fe1a98,20837349,baf445d6,2e9bada0) +,S(a0a42ed1,bd5ac5ed,f63f1a3c,3c3fe2d3,c0be26d6,b0da384b,f19c034e,83306d4,c39ff3d2,d4442374,7a293e28,8c320ee8,8aad0879,672679e4,e5bcf611,5ca0bbc3) +,S(b952e5d6,9ba9f063,f1d1670e,1419730f,5cc17e87,b6f0b01,c0b86ebb,c09e0053,c1e8099f,a230b8da,6e71c9b5,bbf250ae,273a4f9a,f53cd0ca,fe8f0c7,2dc46ae0) +,S(3aee273a,130f5f4e,463892b4,512621c9,be83c18c,655d5a2e,620f83fe,95a4e904,edf475c6,921fb6dc,f2c3c6a0,b93f9470,9237b035,9e8c9131,eb7eeb0f,bf0af7e7) +,S(96ddcdc,dc65af06,acb10d91,b87827fc,aa2f9e65,d28d0449,65ce255d,ba66ceac,2e6d0368,17fd4024,c830d4fe,310bbc23,9e2bf37e,60203584,215f852e,d100ab39) +,S(fd50f406,c757ec9d,b10e111c,32941fa1,e0d07ae7,9e3584d5,af45fae4,d30334a3,71b77494,d49c068f,c7db1d36,4f8db288,ed9ccb0e,c6137348,324f4bcf,d53fde6f) +,S(6f2d8268,11954523,614915c,9568ab9b,bd061112,bca3ba77,d3c64f91,6ba097d1,abe41199,849ca7c4,d317fe72,2a5647a2,97261d68,d025f5af,e880ef78,34787a87) +,S(d88c0db7,6dffb62a,27683d2c,e832a312,9c5099db,e2623c9d,3f064247,21cc1400,5134f15a,df6d7e92,5d81726d,f08732b6,ba5aa289,431bb4ea,542e6352,8dcec51f) +,S(a8de9ae4,ddde3f28,ead01363,2202fe4c,b6c0fd4a,a71d5562,d95f557e,747105fe,179e946b,5a71b614,90bc39a5,17dc752a,53682043,6efa907e,383e3da,a3be8803) +,S(626c6827,f7dc3d40,9daa4811,80e65b0d,799c94ef,e48077ae,6eb250aa,8fcd45ac,3d659db,87cf7b28,32e4a2db,8db88e5e,b30b7ea1,7819c00,fcec5d63,31bf2f32) +,S(28a3a2c,f00efa47,db4b2ef6,9f56cf02,1666ebef,b5220495,5b5484f4,2ca77d02,8dd00ed,fc9870ed,99ce90fb,927086c7,fc16837,9794db01,f7799b16,8393c82b) +,S(df0b029f,27c46405,245d0dc2,f178a48e,c3b67275,7fb92bd5,dbb29370,c1545786,f133ed05,c7e159a8,810a5ad4,e1019f15,757b474b,fea1679c,6ccd18a4,2a1099a) +,S(dc6f57af,9c1e0be5,2d2162a4,e8a6b63a,c549783,4c7c2c8b,421bbc5f,dc1a49ef,6f586aaf,e610fe5c,962ee20d,9b389bab,66fca44a,1c19379e,9e97e104,10f1a0c4) +,S(db796ea2,e0d2c690,19adcc0f,a81c1d93,8162472e,a0e3eac4,bf13b398,b255cd15,2e60c6a7,32e1fb44,641e0766,2191e28e,79375420,43bdb3e2,9474313c,7d05ac88) +,S(569b669e,72ea98fd,a5ba3efc,e74f88ce,881fe269,30b063ca,59fe369,35633a2d,badd8eaf,2551f872,5db3f740,6eb5e376,220b6a6f,ff4f8ca2,f76b7755,f8ca0c8c) +,S(36132420,1f9a33bb,d4b6b6f0,e2d175c6,ec795111,85fd2451,421ac333,78468ae4,f47b56ce,f7bc7366,6eeba135,14756486,62a58e17,d955dee,2821618f,fb3c4314) +,S(cdeb2037,1e4d2014,918fa243,182db0ff,855a0783,c92fd5b1,b604ed33,494a469c,7d3c7718,6829381d,2fd0098b,c84ee506,970a7ed5,5518b393,a83c6f79,d8dfa7d8) +,S(6035cfa9,d750c5ec,d3b721,ade88fff,ff6c4d74,f8db7755,c8717cfc,171598ef,fe1798da,160b2436,412dcde6,5482d202,9bb129d7,d58c9cb7,49f9fdb6,7dd675a) +,S(b1f52e97,e49998e4,e0e75b0c,47178f4,bb250b5d,97d92ac1,8dc9e41a,3b79c0f7,d83f3d65,4e4cfd8c,5f24370c,a2651c5f,de484cf8,1024f33d,8087a50a,d3df795f) +,S(aa799b53,4c73da0,9f011395,dd5f709b,f1a5e056,42536477,d7862b21,6df1b641,3a327047,b5e95b64,593f6887,9f38aa86,dd592007,cbbe7338,a003d2d3,33248646) +,S(da4a1964,45bdf4fc,479e56b6,6d339a45,262d629b,dd8c63c1,8d35e9bb,a445aa03,7b3a5eda,25cc63a4,66cff29,634d5eca,353376e7,26c2c6d5,63f3f92c,b69cb70d) +,S(c300b4ad,c2346d0d,433eba80,f0eef071,7e620d46,a07b384c,231e733f,a1b8d656,32083bb9,48d27ac7,f36aa439,a100b95a,f73448da,454de356,6f4a8771,e3cdba42) +,S(c559a528,67937244,d639bf7b,90df2b2,ed64a907,e20cebe7,6a358b2b,94359f04,9fcac1aa,5c08c983,20d671b5,63f4434a,806d78da,15964474,d2470cd,49bf5977) +,S(39b10147,a46dbf4,f074bca9,a83c10b1,b0911ceb,d3e795fa,9b96333b,dfe83540,ad35dcc8,c3b22743,4daaa313,cd6334e0,168da417,d162855e,64294196,2d308278) +,S(39c9cab7,d2dec601,7f840597,19994c9,4129ab2e,a2779d6b,34774a04,2f7d6d42,c73ebd6b,835a8a13,354cdd45,37ddfb3e,72ca72b4,c8049362,3afdce9e,43781845) +,S(1a625998,8a30b462,adec6097,2e218ec3,f8f81c4d,44131466,fc7b5eee,eec679ed,739beabb,1a97c9b,bf6776e6,2e213bd7,3651a39d,ba042037,bf5f8cdd,334114ab) +,S(396ed910,cd35c308,d412761e,bf283a98,85f33ecc,3ef643bf,3f422dfa,ea4c4308,721cdda6,a08a614a,4dc48cf8,442a46b1,3945e158,83671b7f,556d19e1,144dfe57) +,S(a50e06e8,a2e8ccfd,94aee96a,b0bd831c,83e15340,25e3abec,af7b7af3,9b299a47,593205b2,59a18063,fe5a4575,e8e085e1,4521fc1a,3ab5e14f,3fcd64fe,2bbeba52) +,S(4a9f8023,17b0d1c0,e62c8bcf,7393fa34,dbe15cd6,46e8f4a3,dc3f26b7,11c3e1de,40eff6d,915fff,cdb30c24,434e8928,239867c3,962655ac,2894bea9,f7c6bb71) +,S(8ce5e20d,9decb7e6,10b18562,e2ffbb88,7f323262,55ed9f05,5d2da2fc,8d608b73,eb69b6c,f363e164,e92371,b592727a,63f024b2,aa7011b5,4f1f698e,1ac720de) +,S(f32695d7,9b53e9e5,1f2525e2,8a5540ed,84143ed6,3700be0a,36726f9b,b483f24b,e8d36488,eea2055f,6592ca1f,6fe23493,1d81bd16,1c61523e,f1ee1907,c08471c8) +,S(3826fd86,3bf6592e,5b51ad14,6e02089,d9274881,25d25959,4856de1d,d6c34d07,ea86f1e6,a29035c7,43e82058,a7ff9f82,20da001,a8901e97,26583b1c,ffe4cc83) +,S(7b1afcd3,2e32b0f5,1ba09868,4ec72762,ae8611a8,98e87ddd,dc6410a,40ccd551,deba6ef7,79e10931,8036ee3,9bb6d0a8,a3ce0eb0,bc5620ef,c70828d3,d2e37884) +,S(3eb1fdfe,d2c27587,540d39b1,b66a36d1,8dedddcc,7f7f7b63,348460c1,35a91bd4,b8aeaaca,536ae794,619506cc,a812a67a,642c5345,8cfcf7b9,eb898f96,d26b36cb) +,S(9d61dac8,aae9fc5f,532290cb,683eea2e,b71b4d9e,901b7c45,c214beb5,9b56da8f,2e7caca4,2ea1b22e,7024fc3e,e267ce99,280a8f1e,78a2a271,7938762b,3a446036) +,S(2db372dc,3167faa8,e8e1e37a,85ed5546,e91a43ef,fddba39b,e38d0eae,3e11c7d8,16ab6f61,bc1abd6a,16e79697,fb59f0e8,84f00534,b7196380,9b06e10b,59707ffe) +,S(98f7cef9,f7a2b538,538e83cc,9ad3132a,5b03dd67,f7fc4030,2ff023d2,8805dc80,3b40f823,2813e6c6,bd56df89,1e96a175,468bedd3,f9518dd8,d658105f,aeb98943) +,S(eeab2e6c,c8ab0d24,d5df5852,8a89db42,c48f0861,4d488c7a,363cd195,edb62548,607c41ed,65d3328d,72be2503,f5edf14,523a7f74,402822cc,1709927,c82649da) +,S(955feeff,b2161c70,3469a9d6,c5fb31b7,b7cb5ed1,ada6b537,6d935705,b3db942a,a9195a48,a561c342,779c984,6a710059,283fcd0a,682cb5a3,fdf48ee4,15c0864b) +,S(219a4d2d,70d2ee9e,d3c8f541,1cdac36f,4db23bdd,a981a5c9,d44ce5ac,dccd85a7,67441968,43008ec1,71284aef,2c64f7dd,3dbfdda9,5c6c8e4,70a564f5,c4ef53b4) +,S(4976290d,7794b161,2e2f5cbd,282c1036,d594571,722e28a5,ed542972,f91ddb23,4cfd95f1,60fc5655,2fc8354b,446dc510,23571c24,56f57aec,79004616,ce17ebc4) +,S(472d53e0,268ead1d,bbbb99ed,599f5676,1866e0b3,c14d4a4b,5c44d722,b072b1c1,e9b9c009,6115d4d4,a8ed42e3,1d967b89,e4dfe82b,ec642db8,e31693ec,c2831232) +,S(2a6f5b7e,7a4db95c,af875d61,75a6e1b0,3f3462fb,c0dd5d50,da3327b4,70ab18ef,91b247a3,92aadc19,7d9f5b78,f0b013ce,c9f3724e,a2f37347,9ade7e03,8659d506) +,S(dc1815a3,bf48d81b,b5e5e654,fe0c8c3c,7431a62b,9d1065ba,17df9c45,23146c19,fe20729a,311550f0,e47e2af7,4747effa,d837c5f7,b08c8f36,d0fbdf2f,d594be8f) +,S(8e7953ec,ad8feeb8,97a95db4,f03ffb98,a8b47ecf,3e68bc4f,1df0b6e6,98397776,5dadf2ef,81b21cc4,26c40a39,51f31462,12f7c6fc,dabb5157,893b1637,c141ac5d) +,S(a4d91e6b,49d5724b,9e899fe1,c6686c89,faf96a05,6e666a74,72ee44cc,119b41a5,abd68615,e296498,f5abb820,cbdcfc26,fef10b2a,ba72e474,14ae9de1,960a4893) +,S(b97081a4,f3e426b9,d3a66dd6,8e0e442,754c4922,acae141d,d0294843,6b00eee9,558f6c83,99214bb4,baa9cb64,11550b12,2c8c2f77,8e28a4f1,ea61ed6c,b8f153da) +,S(6882b6ed,82279bce,70a73c6e,f6412c54,b873a5a6,b634a25e,a8c34210,fb825848,c6fdeee9,c8233e39,2ea79c61,6689e8a3,1e3a3b09,21f2b5ae,d6b9e14b,9ff21b3b) +,S(9c542731,1c715709,cabbf11f,a4fdeb3d,1a0dba80,ad50d1a4,62c7ee1a,44717fe8,aa041b10,78163458,c446bd40,ca77f760,ef4b6471,c4f2058d,7ea42975,d1f2b045) +,S(73a14da6,776b5c12,e838779d,e3be58de,4a5ed917,3b195d9,3577330c,780cd32c,c4068ffd,e98ec4d5,a7467bb7,e8bf2c89,ceb58574,cccb3a78,b0ce4a70,7ad2c49c) +,S(6488a286,27d26c4d,bade9d26,1e6ddb7e,8748d835,d9def8eb,fdc6c576,b6af91b5,27f8b1f0,b0501191,8af0916a,945bb07f,3c7f0695,5ada697,b5c601bd,3d6d8ff3) +,S(96546e7a,5b544c98,db9ee2ef,47dcfbce,cca1d38e,1978f71b,d8c9d4d0,5151046c,d04fe32f,8fe9ecff,220f07d2,2095c982,3b10f772,b261189e,84160ca7,5f4309d) +,S(ba046f56,4598b143,a02cdb90,972022ed,e769986c,82d28066,761463ed,8cfecdf8,fc6e23ab,d42457c1,5407d37c,d3d9daa,3e57bc0d,778dd68,53603232,3e27250f) +,S(39554751,e633ca7d,df6d82ed,86a8204,5e76557f,b13ca7df,310b80af,5ad0e4ec,a456ba84,f040f20e,977df5e9,5faa3f67,cdb2ba3c,2fb9bb4f,89486b02,a06d3b1c) +,S(c5f550e7,5fef9fd7,d96c924,11046c3d,ba56b8d7,e1b17c,d46a68a4,989c22aa,548582e0,3aeab617,556a987a,cee7d0fb,8d65ea42,dc8f2cc2,173c3636,27ac3fa9) +,S(2ccc1610,a48d8db3,9a80b2a0,ee063a43,26d4ba75,79b2727e,b999aacd,6e5fa050,de6dd7db,2114002c,8dcb17b,179e5843,fa205d69,4928eb70,a073f97f,f8348c84) +,S(93b07ce7,3e941c84,1e4089ff,2d8e6464,3ae59cf8,fe6e92c5,add89e10,f7084b3f,a6c0476,c5a7267e,54ec362d,3629a1ae,533efcfe,e18d3634,cb5a80e4,4018fa63) +,S(453c01f0,55c14679,5e3aeec8,26859b16,b0ec2707,61cbba3e,438c0566,5a91e7e9,f28eaec9,b9d3c8a2,2ef73843,6088425b,e14ac99,e52e73d9,8745bc49,a56766b2) +,S(3c6df1fd,ac078c28,a0a148fa,d15595b8,c1d09a4c,4a96794,6f8d6465,e80f12f1,4619a84d,4eaa134b,a06a6821,2e7ed292,d443db5c,150b54f,6dfcf267,82a5b58d) +,S(1b7f6968,39959785,30542989,59020dbf,f12054f9,2705efd6,dcc583,11b98630,279dc63e,e3e8fa8c,fbd731c9,b88ec6f0,67ee9e15,d0c14a37,372d9c20,c645c25) +,S(e1b7b953,9d49d0b9,9b90a642,74d2276c,6256f2f6,9cd03006,97ad842f,bffccc19,2b23b96f,51dc6569,8504628b,19e81534,f2acfedb,27f93316,fd0dd8d0,d5f9500f) +,S(404cf0c5,6fbf4233,4bd5f79b,ba97464f,9ce5525f,a56212bf,4bc817c8,af54f911,b5920609,88300588,5c61a6e0,75e3657a,23c04b5,79897c0f,2e22ca64,1f1ef662) +,S(43f1d986,da626bd8,efee817f,a09a3440,e7819aec,17ea971f,43fe2ec9,caae0c1,c5fa5ace,ce891aa,d2811f51,8175179f,3e93f438,2b3ab583,e51a200b,2a74f9ad) +,S(a18845c6,b3209951,16a183b8,1b762112,7bbcaae8,b67ee8fc,d23ecaf6,c5b9c1f,980ed5d9,d7e07e6b,2cfc5350,e818671b,8f54e7f1,5cebbb02,dfcc2951,bbef1f44) +,S(4d9c7eb,af99824,605cc17b,e03c929c,254c38ad,c026d5aa,2a304920,e7ac01ed,c64c5b35,bb0dce53,2273cf00,f3360f74,97065bb9,9fa9b1c9,70d41c19,53e781d3) +,S(e18d22f2,fbc3db09,bcd31783,3e8aa605,55953ef7,4c64814,edaeadc8,97e7c25d,cc258a81,71152072,24f7989d,fcaa8700,f15b8b2,85700b59,53ef2a22,efc7e07e) +,S(160575f9,f220904f,8d2ec9a6,c1417e8,35083aa7,9bc37d5a,3c8bdbe5,2a47879d,1e56b4a6,127e978d,41191c60,ad439fed,2c38704b,309d34ce,d655f93,5279a5e5) +,S(3b651bc7,57b1d626,4d6b7ebd,d3c5355b,4c3c9f6a,a1437e53,f9aa0372,5192d514,977a8774,95990312,ff1d8ec,5dc8a49e,5feb285a,8a1e2e4,19e56186,80231c3f) +,S(9085bdef,56facc02,76025015,498ab286,a9660e96,1fd6bb0c,d9579a8f,16ba532a,fd05d108,a557559c,5e7f791d,90e80e7b,68364c16,8f6b93b8,c55510f1,7ae9fe89) +,S(4224306d,f64a3862,fd33aab8,c1f0ace8,67cf1b25,76e1cc21,fef45448,e40569ec,8740b667,5279bc1d,ce887c2d,c59e42e4,63e72395,ff967249,8bd13d58,f60661bc) +,S(b7376697,4a512e31,a6f806b3,5bc55ee6,e0e2b2c0,ff5a0918,2f83ca35,dc22935c,d350c820,2676fade,ee4152a2,48fd5cbd,890b3e03,9d688462,51c2a082,85420103) +,S(73ba4f8e,d3b5d4d0,a7c505ab,1c1a7486,bb82e068,cba81574,557424da,4d0eb97a,93af3914,552dc360,549b4a2d,863d3f9,3a58ba9b,72541215,adca4bfe,26188271) +,S(285a014e,174724d9,8f0576c4,5694b052,ae93540,6bce1bef,524be03,6fd3d8ea,6fdad1d8,d2ce7757,e6ff5213,f91d4db7,9d406765,968ce8ca,b393e7c8,6a9af7dc) +,S(b0934dd7,27bfb54b,a2a5cea7,c6f8c0ab,84c2fd78,eca0d180,963869de,c28e768f,dcaf0d11,56029f31,4bebbb26,edb09913,484c94f7,d14d71bf,2bc709d8,f0b982c6) +,S(3b627add,8b69a9dc,b751c8bc,cef15d0a,ffcb49cf,357bb65e,c45c8c0f,5b681561,bda1980,9e380b7,2da13364,9d27cff7,693cd2bb,a77215e1,536296fc,dc46d19d) +,S(7e48fdec,1f2289df,a052e3f9,a4766daa,e4593876,5c8aa7c9,a62a369,52cb5ac2,e87af87a,ac116885,66e1e10c,5bedb49d,68449b8e,612939ac,61e384b9,bd8cb5f2) +,S(4031f08a,5de33bba,b6a2c267,feec2e40,d094d890,caa1008b,181ce43e,263ffd9d,cfdc4fd9,9737a5f0,9a789beb,91dfef45,a8f23be3,11b946b5,8a79b7f0,51b0be07) +,S(ba652b7c,61dddbb5,bf6e656f,4d441cc0,ec00a22c,7b900b1d,d5407ebf,931ed764,87fcb392,d1228156,a330b5d8,d3c67e00,1d207095,9a590088,a3b44d07,29556) +,S(fb936572,d1017653,ac7dd95d,2d8c30f3,62c967b9,ba0d3b1b,ffdc1ed0,6590e72d,adfa37af,db6b6a31,a779965,9958be2e,94038246,6b4a4587,b0879882,4836d8ed) +,S(294473cb,5e7c0852,f7c6fdb7,8861129d,b97ff328,19685148,99198870,b03bae7b,bd905536,f991867a,78a09f95,1bd5b4a0,78463e5a,6c767617,246eec38,7d90c86a) +,S(2fc0f44f,73a633fd,91737ced,1683b74c,7687dc8c,6d3a4c09,44a6c873,c5bc574e,29917cee,ffa063f9,4e949991,2ae373eb,ac572f50,9a451184,15e07354,1d22ad1f) +,S(811fb251,882c5a7e,f59d34f2,c8ed8a31,8da210dc,83bb8c40,b9ac0251,116d569e,74df6547,2cb55afa,4ee04ad3,f940de6a,d480e407,5c3cc00,ad291918,d8a0c55d) +,S(45a9e17c,c1d6cf09,1f451894,ad4e7e53,c47996c5,82fdc57b,a9e7e614,b7fc5d07,5169dc4f,2661f5,fb2d1920,89832b7,4e19196d,c37fdbbb,16d1caad,7b069be1) +,S(d577f7fc,45cda7ad,4743d96a,ab5c7bd4,abd06538,b78c0102,645d09ae,b5588e67,55172b71,7ea257df,ced1db61,88c78cea,765e2c2c,30bfd25e,52107128,303be07a) +,S(a6429a95,8098aa6e,39b71e08,d4b0cefe,4fb22d8b,777a59c0,650e11b1,9b06e44f,af494e62,5587055d,cac440f5,786240ab,36ab1825,f2c3a7dc,f6aa64b1,947dd2ed) +,S(f77c304,b89314b4,258b1c80,cf478ee8,50bca37a,63e20679,7396967,a39a743d,c487a176,29d914be,60845213,60f40f19,1c32f414,558e2c72,7e78637f,8dc1c7ee) +,S(41c6f2f6,15981247,71ff0a82,e1ededea,cd76ea2f,fb2f9f5d,ef766165,96d1c988,58a0f544,146c1a07,5ff9ed4f,f9b687c6,3642770e,45dbf1d3,407b52a9,5accb68) +,S(c8724ec8,c13d37af,9eaaacb,35765e3f,7ac2d2d7,1fdb5916,cdf66ed3,2eb968d0,70ecf721,42103e6d,e7d4bbf2,42bd9a6e,6ade66ed,a7862989,5ecd255e,44d81221) +,S(e2888c46,dd58e36,be2204a2,b75fab66,3b139e4a,e070c650,653ba9f3,ff1fbbf2,715910da,e1393b8a,b7d88879,720bd601,bc158498,9522108c,3312b353,2342509e) +,S(f1f4c384,d72d80b8,6b5999c9,aaa66f99,f56cf736,11ea9ec1,c14ac36f,66d834dd,b5543bc2,480142c8,ebb8527a,3c9aa786,2198388a,696b3c44,835e7375,24cc7e3f) +,S(487477f5,832fb7db,768d412,6edd7f5a,17aec3a6,3419de73,f61812b4,31940a5b,90322dde,4203fa98,bc44e3dc,32a6d56a,284f556,36db7c54,c5f78d5e,1b2608a7) +,S(a726963a,583167e2,4b8f957e,b3ee9f5,c55df02,9fe1229b,ff8af037,14ca75af,8fd7a984,d68929c0,81154d15,6b6541ff,4a636925,53a5a32d,99f6516d,c08f7449) +,S(d4c20db4,cf80eb1c,9d93a231,e699a68c,baa763c2,e2013033,8762ac5b,8b99e97c,ba1c77ba,2555a50c,84aa2071,39190a35,f5abc50e,589287a,426c6f9f,6ceca01f) +,S(7cf9c53,89ae702b,bedb5a55,ad8d0f36,5159ba1d,ac56fafa,fa59efe2,3a748168,ece3b324,338c7bbc,a2cec347,c31348ef,489bee0a,6ea4296f,6ec38502,b72336b8) +,S(920108e3,c1ebd4af,3979f9c3,326806f,eec92833,b4dbaf49,6992541b,44b73e55,3e22b133,d8adc483,4f03f348,eba48e65,6cf8f478,524f5395,ba92e200,95f466fc) +,S(1566af8a,98c48762,9b361337,2ad32a53,fc760538,49566a8a,4feb69c4,26479e90,2848c566,4bd72be4,c797db98,62fe1c7b,da5d3ba,eb3f7926,daba8516,da997796) +,S(a4db5e71,2eb8ceb,c7d7a704,12b0a8b1,d2f6d9be,271ef044,c0f76abc,af61723c,663e16e0,75a73ddd,f604bc0a,27a9407b,272f5f2a,f2b9f6a5,6958c8ca,c42e8ca4) +,S(d0c9cf53,8bbdb8f9,625d110,6c79cf1b,33d90e77,60778c13,8493bca7,8d53e5fb,b6ecce0e,8b9fd407,cc0a2125,b8bd30c,9975ad88,7dc1bd9,68379063,8a1d9a60) +,S(765fba92,480f57bd,70596d67,bcf3a389,4a58d514,54e3a04c,e657ce78,ffd8391f,5cb1dbba,1035350e,5a3b552e,3f41ab4c,50879bbd,5f3ae3db,af6ec902,1e18302f) +,S(9f9da9e8,e846702b,4aaa7b9b,68b652f8,9bd3f88e,5af4a503,2c51ee6f,8bfb4a5e,5dbd499d,7ecf17b0,eb17320c,a0688aca,1d8da08d,d2c0684d,edfa2d7f,5696a0f6) +,S(af86c83b,db95894b,72fb1a16,61aef5f0,ad949c9c,b465a5bf,b1192022,13fdd3f4,ec06827d,55410119,603d2b25,cc41c26f,4a7bb9,b1b88e55,123f3c12,d799e117) +,S(fbb48993,2fccbc8d,772b3fc,c80be33b,64c8e3b2,2ccbb09c,d94fa350,fd587e77,f9cdd24,279333da,b3451abd,634075a6,58598ad4,257bbe04,81111b6c,d8c0b858) +,S(40ecb1a3,ad97c2af,62b15eca,e2adc9c0,621f36f8,90a9269b,ae6edc1e,7ed60684,ff9ec194,fa3a617d,920e3e2b,32301fb9,9a41f4b3,ed9845f2,88daa005,3558b32e) +,S(31f892f1,c762b42,fcbd9014,bfe0238f,39873c30,c7d3b691,9736009c,18878f52,d9bd5005,f31f7345,53131066,ef6b92c1,5cd610b8,f7d4fff7,be536734,95718c95) +,S(ba122529,e9f6051e,2d94d150,c5d7739f,921f8193,8cab68f3,1c696fc8,24886c5c,c33c071f,7e9bd539,1b0577d3,37ac9c20,7c02859c,c396bc20,d9c7249a,856b1ed1) +,S(35316412,9a26d311,c993ecbc,5b9a0263,ef45d993,d58158c5,30c215f,2924cf03,86a620d9,521623bb,9ce3e2d9,c5b6385b,f92ef06f,91834e3f,6a9c6d35,7a3a9742) +,S(3024770b,e4134aa6,35ece92c,a4ec7f9c,4a4aa7ac,7851b3eb,f9718a21,52c8e462,1fcfafbd,72147afe,16dc1579,ce7384e4,ab3f8faa,f818d225,bbceb050,ffc8c8ee) +,S(f5ba313d,a40ee86c,5b6dbf16,9941d8e2,533c1ba6,4dd0871a,672d01c8,6bda6564,10a910d,b9697907,c96e95ec,15557649,8f965282,437f58c7,2944bbad,b800647d) +,S(685e17e2,972ccd05,34f7f426,b18c9518,e485a23e,132c18eb,b33b59c5,7264e2f1,96545b38,be84aac1,a77a3f47,51156daf,470fd42d,dea0fa0c,b930144e,b9f291c6) +,S(e01285fd,facc9822,a59e1bca,7e91c07d,fdd481dc,61d268e0,504f65d1,9bf119eb,ae82153a,2479daba,ef83615b,a7202b99,40bf5b5e,41c65063,f77d6e5,9dc39909) +,S(c61e4586,327710ec,784d6720,93201e4,93798a93,7635f220,b5421cb0,55839601,e461fbf5,aa5b64e7,a1e3e016,3ba555ca,5d42fcf1,7433c26d,cdeb8cb2,746c04ea) +,S(1c427acb,4e5890b6,e24d8ca2,513b61d9,3106fc34,90029af3,be87d65f,1fd192d4,1d96e025,1518528b,9bb04fa6,ec3430a1,84935ad5,ee8ae3e9,c9cec0c4,36cf29d0) +,S(5e577bfd,6d51897b,4b6ddb3b,a7d470b8,5a932619,b5982f54,431df6f1,f12fd8e7,c1a17292,9734c19d,3a836e90,8a5a7fd0,123cb044,c7ad1ff9,203d4d79,211c6eb4) +,S(cc78b333,1f968a40,bafaefc8,dfbcadf2,adc6ae76,6d3ea8a6,8173b40a,28237b30,5b7691b2,a5bc068,ff1ef04b,41238cb4,dfc2ce7c,9436f8aa,14eb0a40,45857d42) +,S(3855c437,487b44f2,9e5dc288,faf3ecea,c445372a,95adc16a,b1830a4,ec919fb6,df2e1e9a,48cbfa28,54fbcd5b,308bdbd5,ed1949f5,e1a39519,11c287c8,289359d2) +,S(c05233b4,a83356f,c5ffcf6b,97db3d6c,dc62cb21,b7de7c92,8f158466,b9d16b19,b1bda39,ce78847,3fa0eac6,51e8de54,956616f5,16233275,25ee3bf7,9e54f500) +,S(6c064d3c,f34226a7,d9a00024,5901f355,b736c1e,6561810b,455e7dec,99dfffb,160d9442,d25ec1a1,f80f8fd2,a1c5c42e,aa544477,28d242e9,925824cd,609eb9a7) +,S(9658eb89,70bb1aa5,1b61a93a,de6a7d,b3e20772,4789d009,bf37ba9d,8b714cf2,cd4ba4af,f7da8f8b,7821a903,f40d89e0,96be8949,997d5ff7,a58301ea,92c51cc2) +,S(6e023910,a5546f70,91f2b0c4,c3f996f7,5cf1f969,a71c31b8,7a3e7ea6,4bb4f0c8,f6ed65d7,90ec734d,d9ed8cfc,7d8921b8,7faa9b36,651c3589,99b9addb,34c459c5) +,S(3a35ac98,e9700499,f903528e,330537f,9262901b,d60c7a14,1e68f899,54c7ff6e,913d9516,da631c30,5394ac0c,9eda3824,1f247ff8,a7644eb6,c4d38da1,972069ed) +,S(d089fa51,15dfaa45,a8b6def3,b1948be,2317c66f,249763db,7350e57,76af212,4e6ae973,f326fac4,77a5120c,f3ffa113,a18e5dac,4a190f6a,b12fdc83,18a6da9c) +,S(e733c52f,2aa62ab4,748ea0f2,33d9531c,5b9cd9d5,f790b6ab,45bb2c2,d8cf2ef,701155c9,3c677293,acb23ab2,8d7ff753,36ac4885,70c52d89,fb80ddf1,d582a291) +,S(4b6f7b7d,c006684f,e64aa6f2,42babf86,1eb4770b,b8195b9b,e8caf763,e7f14c7a,6c58b780,ac757b33,c1805d8,a53a04c9,7bfaea05,d8f21d5a,2b0645eb,2194e00d) +,S(1b520402,cd1fac0a,c4d7aeb,8c552e73,44ff51c2,3b4778b4,81126e5f,e267f79d,8ee0d00,56649304,a0c4ee94,39add4d8,30b252f1,93d93c1,9e8ec375,37db1a39) +,S(8936e6d5,d7f1ed85,fc2da4c6,38a885e6,3400cc3f,43d864f5,bf9af2be,b97139b1,29812a20,b4b932a3,770cdc8a,6fd32625,8879f217,a5bd7c47,78d2b041,829b402b) +,S(6808ae7f,4813554d,aa36c043,a55d9171,aebd396f,5ba1a1ee,c4f11045,c96b518f,6afcae6e,88728ddc,578e85fd,5f41575c,f581b983,7c749c22,31b993a5,9c425810) +,S(586c6694,44c73f66,1211bba7,601d8009,93e23293,55a9b10f,2367924a,35a2cd1c,74b0901f,f6eba3cb,408f5507,d37cb7c7,873537dd,ef0671c8,862012ac,fe5416c7) +,S(789c825d,deadf1e0,4c788453,e918541e,b183aa5,99ccc66b,7897be8,1ff89e11,8106aa6f,641bb13c,6be7480b,89f98a9b,e40cc357,ffe9903c,b8f78938,e46cb5ab) +,S(d7ab80bb,fd26faa6,620d0ddf,97f64bca,e520426e,e1d8e076,acd7cfbe,a9419797,26af9f72,a6d1b103,aef4a85f,5139d14e,6e50a58b,9f32eefb,c7184537,90c5a823) +,S(18f712d4,4daf1ab8,4612b311,b1ebc418,bddf0f33,ca02b315,4c256d2f,36f67f7f,c40b1950,f92fd4ce,edb3c3f4,34ff7bf8,6b06d8d3,215d3ee9,f2d9bda6,77d25bc) +,S(8873276d,9f6d188f,78e2a7cb,f998bcc6,fc399d32,21de85b3,eb9e6ea6,c7e5a06e,297e8137,8ee9b9e8,ee820a3a,7e566178,dac97743,c04b6b4e,54a7081,e7c9e1a6) +,S(101b8101,5bd217eb,c08f57cd,fdf431cc,30fadd66,4df79157,1452ca66,c688e0ad,fa6c1b2f,ad203831,185816d4,a8f8dc9,4c7542b4,6c94dab3,39b73718,5d179e6a) +,S(77b3c157,76c31460,11e520a8,cb2071fb,abe3b3ca,2984bb6c,d81d4c53,7f42dd57,8d0bceb1,dab601b7,1c084c9b,443ef5ac,40cce5d,14f0c244,92589905,fc4a645a) +,S(151f2d52,56ac60b6,ff7aca33,8ba66f12,fbe2c189,33c805d3,ecf6a6a0,fddaec7a,d2aacd77,10cd4bf9,917038d6,49e94235,c4833c88,9cd57ea6,74ece9f0,764f62d6) +,S(dd75cf27,f41e417b,46338dbe,24b46df8,9508811c,111bd1a2,29a3809,171d32c7,e328b855,a3389b80,4ab03c33,cc91d86c,ee22170c,d8fb53dd,46c32224,19b67be5) +,S(faa1feb,bdd1d9c4,9de366ab,3f661b1e,6a3f6b76,335b428a,494d7eee,791d7a83,df361d4f,8fce02ab,8c248ee5,61e46555,948bb29d,81a569c4,13b5713d,b825d572) +,S(3cc10dbe,5bc2bb31,fddee65b,66789594,a4cadadf,8c6a7545,671dafed,4d8f8be5,802d45ef,8c66ad6f,e692a3a,9d42c974,7101120a,ea42510d,bc08e657,7039c2df) +,S(9544e725,325dec1c,914dee7f,752802e1,856cc485,35e3a1e,af576bec,a7b93ba2,cec7dba6,81513c04,ebf39b03,717338cc,aaf137dd,e5087686,aad83115,deeeeb49) +,S(94a0fbdc,d5d06236,7b17ff8b,4dcdf6c1,54eec66e,2779deac,38be37c,9f83a964,f25f34fd,5ef06c81,b7223e16,9a42e2e3,fea74213,5a2a30e5,76f29ef4,cfefe82a) +,S(989e29b2,c3fc7e2f,695fbd4b,f217ba43,14f67c3b,9f77633,100e84b3,c6b579c7,608a6d28,cd00297,2b4f3e9,917daaf9,ed003a43,1c22771,17250efd,b621c888) +,S(c38c5fc4,4295a59d,3b0e979b,5934ac2,49b4bd5c,e94097f1,c81064bf,73494a5,16240bf6,d354b36a,648e471a,e0ebc167,1d1dbc71,7cbeeecb,7600462d,61d1e2fb) +,S(de9cf667,35c4046c,58b41d8c,5c658efa,7dc656b,7ad877e1,546c138f,fa7a63b,2fc2824f,ba9162e3,e2372927,5448010b,ecc19de7,d9b94cb7,57586de3,506639ac) +,S(24cfd37,e56708b5,79d84b3,88c33c9f,f1dd9e08,4675a3c4,c8c440c0,f6c06522,1cbcc8ea,9820157,b4b4d289,55ac8672,1aee676a,cdb7df00,7383ac79,c42af897) +,S(9d6e3108,6ad8b9b6,2f4414a9,5e091e9d,2b275d8f,62641ed7,26421639,e87cc121,b924700a,6792a67d,4dab59ad,5a743cf0,43c46ab4,de751bca,757979b6,ead3abe0) +,S(52a89ae,28b28395,739c21e5,38ed38df,74aea22d,d2765d38,673be080,8609354d,c183ed57,9b65830b,d37cccd2,76b2f61,469b06d5,987b5708,89c5b1e,d8334e51) +,S(2ffd3272,491d1807,243f5d31,d727caf5,cda3bf8c,2c172550,46bd3420,da27571d,812e8fbd,6f38b334,a31e48be,6deaef58,599934b5,d3419d52,131cc9a6,535b6ad5) +,S(5d60c609,4269a43a,35e51b66,db193b16,65df689c,270662f2,eba959c5,9c973fca,f591324f,d1afd619,2b31bb7e,3f40cd40,6d3b9285,969c448a,3aff1f48,850efadc) +,S(23e0c40a,bfe53353,f4426340,e1c1b560,4c86daf9,85784d0a,e6da6b4b,3d12ff75,e90844b5,fc86092a,91baf68,280ca144,f19d5a79,9bacf827,f9921511,28191d9d) +,S(945818ab,5d4a1c12,ca6f20d7,7f37da80,50e67d79,30bd2bb1,155885ce,7c6094f6,b5f2c1d0,e2f9ae6d,aa669c96,9ededb60,32ca8163,fafcbb6f,9a11cd56,4a695e03) +,S(d1f4db7d,2586bf6a,187a469c,a0cd7bcd,5043be5d,6206d24b,c41ecd8f,d8724c8c,64054f82,77c4774b,bd480ce9,b2929c66,b1525eab,1c6f365,a1ca5ace,12206839) +,S(4f5c4d4f,24586e9c,1a85f2d5,b42a3d11,158b4f2a,5cf75e18,60f7f4ec,ec14a18d,57576e71,6d547960,6953be2b,e7ffbec2,1e48bb9e,970fb350,986b4a31,b0efdbe5) +,S(c13b605b,dcb31443,d8a3cf5c,5e4f903d,3f8edfc5,1e9a118,c3840cb9,4998150a,8dce2eaf,99f1fbf5,2e62a8ea,948b01c0,6c368e49,f41922b5,2f603cb2,16cb8435) +,S(1937b32a,b66f254e,4964a5b5,a1ceeb5b,a3df2466,e2669753,39174cd0,2429cbb6,9712b0a0,6f1b075c,bbceabb0,53fffb1b,181e079,813e022b,b5701738,ef2e385) +,S(e4a5333f,2f1772f3,84ef386,c3be1f58,d757bbdb,7ad2c4d7,47e61b07,419ba2e4,e84f7da7,2ec2e4b9,389b717,8bc15642,132571cc,9bc14c7c,5af14a7a,cb6b6f56) +,S(2f8a38b0,a3f42a8c,5080a0cf,5cc1085d,d57a8341,ae154dcf,fc7fdb4b,5606cca6,8764c6a5,2c301db6,56408d9c,4eed6294,2fa2a72e,1b1264f1,134f38c6,8e4681c1) +,S(3d2bdc2,c51c2d77,b6c585b6,3ceab5fe,acd14599,b841870b,8735712a,81a55ff7,33f749ad,bbe263df,a99a808f,f0476e44,92246036,a3e8b5e9,496b8dcd,b6d3e9e0) +,S(fc87f808,d6764ce3,376b9acf,62bfa3e2,1ecec215,22dbdb3e,6e5f3cc1,efab7d3a,ce75eb03,c1f4f937,70815dd8,750a4ac1,c0343024,ce1bc581,717cb971,2835176a) +,S(7f81b85c,62dee11e,be8885dc,cc98485,32354952,1c7e6cb4,374270bf,5b1e68d5,b7adef7d,6a84db61,c15ba062,d96cf739,1750aa4a,ed5c97f3,ad70d2a7,6a9c4558) +,S(d618d43f,7afd3ae5,e8dd8f47,f62ef91e,33cc8f89,3c21688c,8268b903,e1fd5557,6180817b,c8ab9b0,82b21267,9900c7e4,42ce9e8d,fc8c3fb4,fc5bcaa6,e0cd34b8) +,S(ba5a4c4f,f2379b06,daff540f,c8383b9b,694430bc,df76c931,ccf94cb,896e4940,3a221d1a,b138e6f,4ed309b2,4f2e9333,25299de5,534a528d,b91075a8,fd07b1f2) +,S(c69189b6,bee3746,88359141,5b35bac3,80136395,c83098ab,cec29fa3,18cfdf56,a185dbc2,b862da3a,cdc232fa,7e3407b1,9795e61e,29b7a79c,5828bd49,d739268c) +,S(51f67d44,32fb401,ab863423,b99d403a,70dffd86,87e5ad71,bcd3732c,1cefae12,88b17485,a2c404a3,2bcae68a,3cbf2166,403e23f,8a64df5c,a8eb2d94,61e10e59) +,S(cdacfe09,e5585f8d,4346fb69,a94b8b65,6001e117,ed3cca36,fa8bee6d,eb7c4bd5,ddf1bc5a,993131d0,15812176,206699d7,af5fc403,59455ece,fdb84492,6e028542) +,S(8129e771,e1fce476,9c701d70,cce90888,481eaaa1,d1661be3,19f33c93,8bf0e769,3eb5713d,a864bc56,39ed8b6f,974966af,8a4db189,b5446d70,d40a816,f045c6e2) +,S(b97062ef,6d415bc0,f302fc0f,8aa730d4,f39c14aa,f82889ad,a68132bf,5abce3ab,8ad70688,cd76c93c,fd8dfdd3,8522247c,6644c9e5,51116829,490f313,d53db3a6) +,S(19c3c98b,4e811b5f,ea5d6af8,c4e86be6,50de67ec,5154bca4,22a6a14f,4f0bd913,bc523f96,48bdc993,30e1b5c7,385097cb,c18b9d5e,924767bd,d5d746fa,a190a109) +,S(b9ef34c7,3616ee74,b1ebe43f,889118e2,ad7697b,efe63559,67a9b3c2,b5b42b9c,12f1cddd,d55b1ac1,b89be271,5e623952,e8dc05aa,3570e254,3b70ea68,5b661bea) +,S(7718e34f,58e2cdb5,80f9c39a,96d84dc7,59dd2a3e,bdebaca8,6e8edb97,f503c34a,a6d105fb,dc0841b8,12dcd8f9,6c4bd17d,cf5e3728,8e0e3093,13567b10,3e96df7) +,S(dab3884b,8e6de737,63bba89b,8d35a369,e259c1b1,8afb6ed6,21fcc871,c77e8dba,f44b6f29,c59a2d42,2babd4b5,edb4a009,c9316e09,2aef953e,e503a278,14a11577) +,S(cb3d0e10,1139782d,e5e6a897,8b5be6df,a6736751,e847ca18,76aedab1,e67f4366,27fe888d,d227943e,67969b33,be48f1d9,572aff67,69c150cf,189f9709,bd57d3ea) +,S(eb1b1ff8,55619d41,8d193db,bf62b4b7,656ea564,6ea2e79f,cca7fb3b,e8c3c6a0,c27fa0c7,5b73d5c1,d7113741,40384565,d36ae93a,49b327a5,cedd03c9,62282ea4) +,S(5d5079e6,bc40f11,f75a1117,3ab6bdc,bee8f9f3,d2e57aeb,58709786,1c39c5dd,14ae112b,a40db9d5,dd65829b,4be7bdf9,4d8435de,1872bab,80581221,446e46d0) +,S(1060ef7,6beb6af0,6d28acd2,6b214bb1,b708098d,e0300502,384fcc0a,a1a7b38e,ce40f39f,f6c6c642,9585464d,aa2183d7,34252c05,5207e2e2,75d0ec48,c3bdf9a3) +,S(9651c463,c001f731,8947c271,ac274529,e7bcc894,70d63e1a,fd723873,aa170695,4e362e7f,e8ff06da,73d6d17f,e8b63c99,3b16ba7a,5a9b154d,21837fb0,e654eaf7) +,S(5c422b76,592821b4,2f6822cd,cb428b19,895cda4c,d224400e,e0928f27,1f363dcc,2fdb4f4f,9e4c77ca,3448258f,f6c07f5,4cb6b4d8,15281484,44e5fcf6,492b9400) +,S(db147d2b,5d98250d,73a3ce51,9452cc4a,ab3868a6,cd4f43d2,b7224d93,a9faba2c,73254380,ad3c6acd,fa343227,a7299684,9fdebffe,64c4925,8a391a7a,50ed0de) +,S(9a6763a0,f97abfd1,65223767,8881e899,e1beca26,ed84d384,a41d16cb,e5454b4c,bb51407d,b6aa5817,32f1a3d5,51082908,e7952f75,92c8bcb2,737a01d3,3890306a) +,S(36fe7933,e9108af4,f4e5c889,ef94f584,7b45a9e,8c520000,7db6a00,e5daea8,d024a5ba,ecdd5a21,c8c3a0f4,a71e15c3,1943c7bb,66b459e2,7fc85bb4,25227ee5) +,S(40dae663,f04f7e68,ce782fb,87529681,44c749ea,5b7386af,c445568a,3bc784e7,49a463fd,ac6bcdd0,2458c85,423cf252,cea2ab47,44b16bb9,47811176,5755050b) +,S(ba1616d,c8fff3b0,8e8c6d10,b15afd9d,da55faba,79fa8a75,42c5c9cb,adc5b8d1,ef096c42,a6233c66,1965a3ac,eca095b2,7710455e,5f0e1019,7641ab25,b0a786ff) +,S(30486f67,23a54952,4d68c948,dbc71273,d69b4a05,5fb9e7d,a702d027,619c4a66,38ca4d4d,279ec9c0,85ea6369,5470a4b9,fd6acc6d,9a049ab6,5743ecba,c2444c76) +,S(1b83ee94,95ead1d5,80f67174,dec2a026,d6accd93,9a89d970,90f25bc8,d9e6932a,196d448,fb5e4851,e1b73d6d,a2e7d0eb,3d263034,9c2b3e0e,34584bc1,d6e20196) +,S(4ea9dc2e,edfa5a11,2fa76671,5c1579d9,328b6132,14ee64f,2e30f10c,b7517396,7414efc9,6ef83e29,5883f0b1,4019b41c,d4a192d9,d06cac9c,21e8f0f5,b06e50c2) +,S(4166c19d,bee9ad08,83b217aa,3b657436,87677792,d5cb9b31,1a097fcd,2eb033da,62c5065c,5fae6aa6,932b4d29,ab375ac6,b80e113f,d38d81f8,abc236eb,7eed541c) +,S(b17567eb,b112f54a,69c511a8,7d3d2f96,bbbc6686,af8e3f31,a2bbe0a,85a74899,f6676341,3788be54,ae3fc693,4b8936df,a6936721,c671cbb1,bdbfd72d,4c9f52c2) +,S(cd2b6515,2ab97f34,89d81fd,f3513131,a6df0685,5022102,12c2b8a9,be8095a5,cfe7952f,9ca41935,15ee40c7,2d44d023,22afff84,b8700bb4,5f492e31,e78f6a53) +,S(375603c4,9f33140a,3241a72,907ac0c7,2086c979,3aeaf74a,44732097,3618229f,7bb740f6,6322a773,e11ddb46,300975db,de08ba13,84480699,6681637b,eb85e837) +,S(102067d4,390a3dd9,6d83c176,f341dde9,7e10955d,2e001632,978f202e,efbb7432,7cf6cfd5,4d28ccc3,a7019f11,b6ba9df6,1d8b7eef,7e70722d,afa81a2f,839ba83e) +,S(47617e73,e3fbc53b,a703042c,f8b48fad,374ef42f,e8c2dd54,4fb7625c,de63081d,1fa4f423,c92a95b,b2c40f07,f43caaff,50cbae67,87c3e2a0,e4782f39,c1d439e8) +,S(2c431baf,57f75062,9f57aac0,8e28a060,46b814bd,318d74ca,82e1174e,f880f84,ddb9e7bc,68574b32,c1004108,9853f41e,62755a52,ac58badb,b8ee58c6,f209de50) +,S(91963643,8143b6e4,19b86b1f,79b708ab,3f4df44b,d774b41,338570dd,8343d599,9034ee17,7171a0d2,9fb91d68,26b87bbc,21be334,90f44049,69622a8b,76312f0d) +,S(bf28c201,af656c7b,838d42db,95683b06,2871859,2b016ddd,a4a4f7f7,c4b7754e,bbd4f04,dda510d2,a8a66c2f,46ab0681,ce6ffb5a,ac320e20,c50b6df3,a345d109) +,S(fcac7386,21825f4,4e1d531b,f598e40b,1aa4262c,a5aeb5a1,babb4f35,9518b137,3b196b7a,957bbea1,f3f2d1e4,1ba37eef,7dcca7fe,87e7c593,6ffff685,52e67b64) +,S(9cfea1df,caaee192,141f0278,e467f809,8ad89cc9,b47ed982,3029c1dd,726e9331,72d81e4e,7f30ee05,8fd0b646,95086e30,96a071c1,5b75df47,b10146b,18db211d) +,S(548d3f42,123553c7,965c7f21,f2802c30,455c5151,99b98e20,1e966f34,6a1334da,612ea25,cebb03d2,89538eeb,15b94686,8c0afb1,311278a,42694a0e,956418ae) +,S(e5be619a,ccf84c26,9b5c545,e8c4990e,ebd46756,c628de22,a6c8bdd6,74295bc0,640864a5,7d841af7,ad83aa74,fa858b91,89e246cf,9b361837,a742e2a0,71b08bf3) +,S(b560933e,3400ed4a,eea8f733,d2368c8f,a260460d,f734e209,e194e9ae,a1ac668f,5438f93d,31337853,9feab182,6cf17bbc,ac0975f4,de29a886,1b1c0c02,2c5f9da3) +,S(21dd9a97,baca3e79,77ecf7d,d74433fd,dedf2d96,8c3153c0,2c652aa2,1ccf60b1,de6e60e0,e048b0b7,b699bbd7,cda0c0c9,b851c4cd,34f4b8d9,17c07cd0,2684797e) +,S(5cfa560c,6d39439b,4c9b0a52,f9bda301,2f147874,48c9ff37,bcaf2900,fd934bbd,c7927a63,cc1959b,f3556998,ce936f29,29d5a5d3,3f0dda32,35affab9,a5b11b06) +,S(4a3b95bb,5adcd41,5036204b,394fcc95,6377afe7,b4902f49,461c4b6,866d4fc6,d59e3ee2,f1bd1e4c,c089179a,4db9387d,8d6be47b,df708023,2a0d00af,a8e84f2f) +,S(cc49a313,b4c69c9,13c06b7b,add06f17,404edb0a,dcdb3d4a,81c4b765,35f4671f,4bd722ec,c35e525e,af36c194,3d375592,ba483715,2c7a91b2,4dd57a89,687bc278) +,S(9d75085a,7426a69d,9ccf4f90,d8b4ee0,e07fdac0,18df3d37,40b264cc,d4146d98,5991253,de04b32d,4772f44,de49467d,ce174ae9,3f941ae6,a4f6e3c2,2cc4b0a9) +,S(dfd064df,737bdadd,2eb12cf5,d9f1e192,caa87c0e,fad20d18,b95b0d06,83210fb8,519f883d,a5bf8d33,50b1d2b,89012fd5,9f317299,3eae1226,13d81b92,9103805a) +,S(2b50a35c,d2356c35,d609013e,a66318bc,1cf20582,685c12e3,bf618102,807eaf9a,7d0b51d,303aeefe,b81f5a77,d9b5829e,fdf4241e,435380f6,ce74abab,1dc76335) +,S(e26e8f4b,2cf175e7,c76e2a59,459cd45e,f3202f3,3027f0c3,70481fa8,685abbc7,4c82e7ba,aa0d5161,53076702,997c34dd,716e5fe2,460eac0f,194f72fc,3ac2013a) +,S(5903db7d,54f4d857,6a584057,ce78dc07,1fd90e51,f825c959,b46d3483,5d0f87fb,c55e2fd3,e9ce119e,47c00332,9d18d6f7,febf9440,a18de0f3,81608011,1e9e99c8) +,S(4bbb0ecd,342a82d,c8b881e9,635241b6,1040e6ee,28b98204,fde953d4,4d25e9c1,12eb090,f3445b7,52abee8d,604e8784,82e97e9,b44d2335,8211a8c8,2cb0ef04) +,S(4a2455a,ae87bf97,c6e6eb38,fd3279e2,7029967d,41b9575b,647550f9,4bc4e8d2,5a340ac4,ffe3bee6,2d52bea1,e276178f,9af3422,f9c61c47,4a595550,73936074) +,S(f086d68d,c8ca19aa,54c329c4,c07b530f,94470abd,dce244ad,1decaf87,53477623,911c49d3,84a16af1,65be4161,cc4869ab,4d9e8ba2,808afd80,20d30971,b4bdbd08) +,S(4f1b177e,83b76c4e,24a53f91,2095f1b5,d4b7cdf7,29788cb4,c989f921,b7ba856c,a5568c4e,b060909a,fc3dc2ff,5bb9534b,99debb69,dc0b811,e1044705,7d80eaf2) +,S(eba87899,29a7e45a,23d2fcbc,884778d,c4fb5ae7,fa67954c,76534303,97db5823,e6f8c729,b3570917,9fc46b4,7f03af9e,801b26a6,1724aafe,e730217a,2dcbf68f) +,S(df117b13,34c9a5a7,92d58084,4444559f,130ee539,89cc6e7d,87723b63,a82282c2,dd87ca57,8eea3c51,2aec5492,ea55111c,1a2fc55d,4e2335a8,c9be5641,516f7120) +,S(d775543b,3e68ab88,36eaa1b,df5f6a06,1f5768e4,baa01a68,621321c6,e6ee82c5,70221b05,47c97398,3cee7a26,c4197f9c,27a2bf46,10506e18,493a9ee,1881436e) +,S(a7f10dd5,5a231ae5,abc5c611,68cd58ca,5e006f7f,fdf064c7,1482febf,4578515d,492a33a7,b66b9694,e225eb38,d1d99399,b2c24af2,d69c31e,b4797a9e,703c13b0) +,S(c79545ef,3119abd9,11336c86,c6b74846,801c6b3,812da05b,5d36e9c,620120ec,24c8fcdb,95b6eae4,51473f8e,f7dea06c,738a33c4,f1214382,bd7204dd,d3c28718) +,S(98bab5e,744f888f,5f843ba2,9e2104a3,afa14dd6,6a2206ec,8e783e0c,52c2cb11,83b5699f,e5e87ed8,c51d929b,8146bcd6,a7abaed4,636afcbe,b2c30d2,bcb94df6) +,S(6dce2828,7b6fa44a,c8213053,d39dfff5,60d84a84,ac264110,547752ef,dcac586e,1e1eb62a,b9d60cc4,5ab2fa8d,d065d1a4,c79be399,53578a5c,adcfc629,e237ed66) +,S(e386a59,287a73c9,306b091b,5b24ad8f,db06a9dd,8f6f0c1,b7fb0ace,623dfa83,4a7e73da,663b64a5,e624ce89,9e16fcd5,c0d3b221,42284ed6,852960a9,d05b34e6) +,S(8fdff782,e62fbe31,98c283fc,9b9543ac,cea0a318,2626d180,4beaa27,ea4fe4d5,fef8f471,9fe0d95b,673ed650,8107e887,a4f5301f,4317c708,c5222d2a,ac086675) +,S(4c9093ae,7842b148,1bbb8b8,4ea564e9,36ed1cc4,35c89089,a10e0442,292eaa37,2d683334,93d245fa,e5ac0903,fe9356f7,8642362b,f85a7426,ea2950a0,a65298dd) +,S(cc0634c5,5c5c52a9,d92b80f9,be072a35,56318c6,fd76cf2f,e866d5ba,33103a1b,750b7d41,b13cb69e,6d236eb0,87fc8600,4004aa77,1b699e88,7e8f0b08,d07f0b32) +,S(9749a673,41fa7ac5,8bd33f5f,9cca4ad9,1d53c7ec,cff76656,9a56f71e,3b918255,b685f1c1,9dc7d6,7692a2aa,fe37d9da,5e68fd69,cfa58d87,ec6ced8,68916068) +,S(b04abcef,b57862f2,5a57fdc,b7da0b1b,a25bd253,72bd91bc,9e0377b4,697d8e75,50479a64,66ad4be3,489b5869,d8b9bbe,62dcb8af,da8e7d42,78b5014a,19e168f6) +,S(d11f7de8,5481fbed,f1a3b395,785f00d6,295059b7,c8049768,22624e85,45c78902,1d163368,be0c1290,1a7369d8,11266982,b48928cb,448039c3,17333658,6f5c3416) +,S(a21c65f4,65c4c7a4,93225af,73b63cb2,fd615160,b29b2b90,970b880b,5756fd4b,69a26b89,394de60e,7dde3476,7048a295,57f1e3e2,5f7a586c,7666aa30,9345ecfe) +,S(53c626fb,b080360e,9243d399,9a58ced0,36ccda8b,186c76a4,fdeefee1,6a497a1f,ad926008,62a7ff1e,519106fb,933616a8,8264d7a,c40476bb,eda7e1b7,742ac9b7) +,S(cf67b587,1b2a3ad8,d0364fcc,8741f10a,11d96933,11b72870,60116c4e,74e6ff0e,2c963b7c,10ccd74e,f68ac068,cce1e30a,7be6537f,259abcae,6d36d29d,22b68422) +,S(50216d8,8cc8700,29da89a4,b34cde0d,36428829,de9f58d7,31ecd6a6,c90b4bc7,920c1df0,96bc4891,8defce32,2a512d5a,36f65d7e,65525e0c,123d9fa7,49e38b21) +,S(1d845594,f4686fbb,7b0cc62d,af790aff,3d6000bd,e26ec1a2,67766c3d,93d1aef3,eae11ba6,bd9dda02,ef134035,1847dc6c,92450b36,cd6a9734,25c09f9f,3134d260) +,S(11ad4f50,b6c5b97d,767d590,859e2ab5,2e7750a5,6f7e70c3,d5c52bb7,9e4785e1,2c2bba36,8725d9c5,2152496,f14cfa71,1826e846,fbf7e7ac,4e263db5,a8023b94) +,S(3568d27c,d72373e1,29a4a519,86082a1f,f76ee425,969d6e3d,12b242b0,bd49388c,e36dfa9,6d094047,16e35668,10a8e57,8eff108d,839cf3da,f04c76d5,8754fdb3) +,S(3772ab35,45e09d0c,5d2abb91,97007742,46310f72,bdc0b8ab,afaa2c17,f17801d7,25229a8c,eae0c746,2d7a50cd,a8b012e0,775693af,8edb7260,5340042,a78884e) +,S(8151b24f,72c4701a,e4624779,c8b93ad2,44f3b299,d1ee131f,a13054e5,c29274f2,58938307,25ffd619,de73fca0,50c28d77,73420724,4d2e4c99,86ed5512,b8bb6429) +,S(46a4855a,d4101f8c,60c59705,e537080,1b57a81d,9227f4c8,69234946,5d539c9a,8fa66dcf,21117195,e2c117ae,e2f20bb,9517ad24,5fea8864,beed3e68,89e36b59) +,S(b81bd54,3e654f3d,eec15001,c38bd2a4,efa7c45f,4ece2d4f,fd893c6,15803d16,15c3f7aa,31299a76,34431622,8f93b6a3,2b3947fe,5a894c4a,75d2ba01,4e9d3e75) +,S(3e169a1c,f2a0324d,2478c054,94d58801,423f10f2,4cfc9fab,a5cd5cad,12e7706,20405bdd,d814ac7c,53469075,a700b4bf,4d454b,fca920f9,86923562,19389135) +,S(74283e4a,b161b33f,f67a1b8,cde619bf,557f39b8,5443efdf,534bdd2d,4da65956,22296be2,89785c04,6773ad2b,c881744d,380c1717,1c77b159,bfee3689,d0b3df3a) +,S(62715786,cb76bc9f,b65da0a2,3b7954e6,1a617fb4,de828092,bde3da39,ea6b756e,3c27bbe3,8829a150,3b563337,bf995110,eec54d58,21e1f17d,34a5fd90,52c2db32) +,S(96200105,db23b893,84336bc9,d5596740,2c93aee2,ca35df73,9c55a000,6f7c8aae,3192968d,400a2604,c80307c5,d9865f49,ea41cdd8,e3fd3b19,a2bdfa17,84a6ba26) +,S(bd477798,bd2691b0,8cc52fb5,cbf8b2c5,31787535,541b5c31,3aad9696,b71d13fb,be2b51c9,a4ab0033,c21dae50,c4e7b2b,51978b39,4a66d50b,6be7e140,abddfc3b) +,S(fa149388,e46ff849,bb5490e8,3aac6788,70da101a,f52dab05,9039c305,475289e7,8455577f,a8d85674,3b32080a,dcd26a1e,c75af3d1,c184d5e3,7f22030b,8867b2a7) +,S(4228db83,af1ffb1,d4bbfae7,3781a70a,8ab50d3c,47200e86,ff436ae0,3391ba90,45db1d0a,b0e6f0a1,801560b6,419bd2e3,2413ddc9,4b4ad637,d4071d8c,b7a270a7) +,S(a97ab279,fe0df5a0,3d494c02,eb69c686,1dc3a39a,fca2bd49,f4161ff3,817c85c,5006c8bf,f128de17,3d72f99f,7cd6deec,5d4b7441,6b1ca290,e535c8d,c59bec7d) +,S(c0d46c57,6bdcc661,4e1a3a1c,c19fba37,9f27dc45,5135eb63,f8d44666,904c668f,396b60a3,b28b250,2c97d7cc,f93c7609,3d19172f,47fffa71,f190936d,cb215f58) +,S(73c5d7e0,4599fa7d,e09b8c2c,25c5bca2,b7ae4c6,e1b88e63,6de3363f,b1a8dbbd,1d9ed08d,558bd642,9e986b0,c431d67,9229adf,6fa8dea3,faf17da2,747bc617) +,S(370481d,a2ef4843,8d897c6e,f1a01750,b4788c65,62ad3ab7,bbf3c89e,e24827a7,a88b3876,c2051901,1d84acc3,3000f0f1,ccf794b4,5385aaaa,190e435c,760fa7b1) +,S(2dcbe303,3006f0a4,99dbb7d8,d34790be,f2b68cbf,649b9d54,eda8819d,637807ec,b82b46e3,c7ab329e,b915de2d,f3fa28d6,92cf43d,584f9bda,5d6f58f7,1a90c1e1) +,S(f45f3ca9,16db45c5,de35d957,c24f7a59,44052b64,f8f423c9,8a442f6,17bb4b7,79ddf02f,b3154d6c,da1d6a79,5c64de61,5b4a0ff7,810bbfc1,dad68e60,229b5a96) +,S(500caa8b,cad0567a,465c99bc,c4802553,ee17346b,e3a9ae93,4fe7e95d,e787605e,40847ed0,75dfc8d6,ae19dccf,73ddd256,4e1beea4,7f822a58,f0570c62,f238370a) +,S(30562986,a23962c5,5b746191,e23b893b,7bcd8b87,6fa85557,c525eec4,d64c3aff,2d5abe,41c45383,1b60c034,7bb55464,e19d8d98,8a5d5fe9,37337507,80577a53) +,S(f687d51a,32526b72,9ed3f690,be1e6205,ec72382b,6a30478f,63a18e0e,b48e422e,efce1b35,19c94f7a,fc54da2e,d36b15bc,f197e2ce,a0c2d680,211e12ab,7d515704) +,S(104a63f0,d385351c,c9ce207e,500ae7eb,22199ea7,dea65018,472884cf,a1ca505d,69c21887,50b20b3c,5af1600a,a2a2edc,a6de0be9,33d2e67c,e05ac4a8,30239cc5) +,S(50966099,4b7b8ca1,abf963e2,ff4d295a,eb3d59bd,43029c03,5e6ac9a3,ea80fc6e,d48589cd,40d019b1,81211674,854aea3e,56be56a7,bc8ae519,72963adf,327287b1) +,S(3106d6e7,94e5e135,1d788e85,eb0f2c7c,7ac22e9f,d1c86a0d,d22cf88f,54513c95,c05e593c,4568cc2,992849d8,4006ae18,4ad2888c,82ce8dfa,dbf984bd,2fa2bca0) +,S(4c967d1c,9977bdce,84f76ac7,13b9e5cf,d1081b43,de681235,e7914f6e,2059fdce,866470b2,a3e99b74,15c2cb09,83a5c7dc,fcc10d02,a28e90ee,206be515,e88968cd) +,S(96319099,ae8d7741,1d8bbb7f,bf58f62d,b4027b02,ff5e32b2,e4aff910,3a781a52,6c110074,daf2849,a3b148f8,dbba2f0b,d776eb4d,b787ced4,22edc5f5,67947389) +,S(7c93bb23,87a36cea,9dd7406d,95af56f2,b972242b,759728b3,bdf30fa3,58247ee7,21e4de98,33487c86,6da01e16,895a0e24,17cb34f6,e3d64e01,8f7c4d99,9e989942) +,S(c65e4a03,88de1618,6d9db143,387a3229,c764dabb,7c6ddcdd,c3ba9195,7f1e074,d586d143,8005b28,cea25a45,7c45c1fa,2137bc2,fd78417b,daa499bb,cfca658b) +,S(d895a924,90c28692,bcda380c,71d8d1b8,381b1fb1,a8108f35,4d9a17d8,64d674d5,d30bda25,a63013f,79ef5728,dbbcd259,c7cf2ba2,b00af73d,9804f758,7fde17f5) +,S(90467c3a,5b36b9bb,faffa164,63d1f70,c892e23a,3c7f1132,27bff1e1,5fdef988,4ec70191,40247d9b,3cfccb41,ba22ad76,20522577,1e33d568,946c8081,ea9b15a7) +,S(5cd03d63,d0e025a,7af268f7,8f53efc7,b4d38e5d,58da5981,abcad74,3e294c7e,c3729f6e,5517d41c,ab1510cf,ee4e0e48,f168e043,34dd8781,1a10b70f,b42dfb3e) +,S(5130c49c,652bc5ce,a3dd7629,bf362294,8bf69c02,f9806988,fc700a25,c7e964b,7e46e219,4bc9dbd3,99f98c7,b7a44313,a027ef23,1abe336b,7381c215,6f2cf563) +,S(95899c2a,33b8d4e9,13947306,6b924812,261a3155,e5a7ddbc,c5447796,8edd34da,8967be99,84240292,9ea7277b,5be0d045,bac79fb4,93a08a41,d4dc5991,da6b7cb3) +,S(3928013,8cb3e93f,7c4650c8,8184dfcf,6e55d238,2e75e604,9dd6e40e,f489745f,70b25e98,b897f3a1,c89bd6d4,72f2555a,56bdf04,6fb43799,58709471,f8006a52) +,S(a47e1331,b5fdbedc,e490497c,bff1088d,ba72070d,3cfc7997,fa388fc3,2f80b451,ca54b8be,fff00465,4086c75b,da3ccb9c,6244746,522124ec,ec22666e,a9576b19) +,S(cc1a608e,16c57528,3cbbb134,4083cfd3,f1b325cb,b48df6c8,2d50bed1,e0cfb96a,5b8aca44,67d9b224,413b0c98,78e09213,591b8f87,b024bab5,55120a83,38cc6734) +,S(d8354bc7,591ebaeb,7ae4d1df,2db3bde3,b7438d1d,69b5991a,5d70f6c2,c3fc3cea,efc89693,6765f03f,29422703,97591bed,7d95afc7,29807a31,fe266f08,9eab5ad3) +,S(6ac1c963,ceb2740b,212ef42e,6da07647,481ee21c,4e3fdd6f,f2ae3da2,8ddef8aa,4fc8817,1b6d5ae2,45453c94,f5f2031c,c52140d7,a110d9f,b1bbf3e8,e5164281) +,S(be130f59,b52e4148,f9c47469,778fb36e,74d4da3d,a00f5c4,dddd2e47,6f18b42d,16dcd9e2,f739a58a,22a7d76,1c1cdf61,c5b9f8b9,46449516,9398dc1,fbc47069) +,S(685db8cc,39864a55,68b56e59,96265bb1,24ee71f9,1f9f4f3f,fc3c79e6,c063bd2f,730a7141,8e5b2f7c,2d333b8e,5be3021,22b396bf,9a75f493,37bae5c,86b50f29) +,S(39f41ff3,89f74c1a,e25e564,b9bc189b,f31f9af,cf0ee4ea,aad66ee6,55d743ae,fbea0f6d,e25d4fee,53f0aad0,9338f739,26fdaeb2,edf9d8d8,8e1e520d,d66c8622) +,S(d7a69574,41bc9639,994737b3,d483bc0f,c29b1b62,cbf59f28,b12b208c,4c20ed1f,ce44e2da,cdf8158e,9c07ca25,1b9a3d06,91eea29f,41f824ce,ef2d631b,65ba8cb) +,S(85bad160,9139cbb7,2be86793,42b37884,22f5981c,5fdf30ba,4914395e,c1eaec9a,99fcd461,29e576b2,b25c57e8,8e3fe3ea,362055c7,843838db,4196a485,cf14dfec) +,S(2380bac4,d428a159,23c2ffbe,8f17da09,74d986fe,4210b72d,51a1eb80,23ea49e5,f4af7313,fde4c7f5,e45e38bf,c4d44bbb,3ca1015b,d209065e,5a6ab074,5ed6f54b) +,S(c0561359,b7d61dd0,e359108d,f221202b,97c1acfb,894f4e9e,46fc29fd,364f9cf2,3c377d80,1c72a9f6,2cfea5ff,350ceb32,75b16103,cecf492e,2901327,d27d83dd) +,S(72c499c1,b8f4f2a,eb9181d3,9bba5b2,76481c04,a588fc6,4ed438c4,920c2a06,6c994c8e,60825314,2a34867b,1f82bc1a,f05ff8ff,b3045e7a,72d01243,b2b1ce53) +,S(8de84cbe,2b66209,f8f8e2f3,f06feb45,66e02c9c,b5df26f8,84e20454,ffcf477d,c7f2f680,c4d07d88,69348e98,6a3b7ac2,ac9e8542,a019f379,6410b993,742a34a6) +,S(dbfec9d8,6581d762,e5ac8048,886b9d2e,9a2d02ee,63c5f442,7d02d12e,401710f2,6e611ff9,827bb1ac,a885acb5,906648d9,b46496a8,98a13c90,48868d,c0daafea) +,S(9dd651f8,6099edd1,b3d0ade7,82aae6b3,9588dc2a,291577f9,f2456f23,d1d0453d,4f6db617,f0d7860e,18be81a,dfb6d773,cd3f905f,98611845,5f5a0b05,ee715ebd) +,S(2fd8161a,c869de35,e777a885,ee608f96,356445f2,fa2f33d1,926260bf,9bd66a15,1d2c21e9,674c2d92,b64163eb,12de6347,98c6e7a2,9787daa,7c6b4c80,fd307f25) +,S(3f25bf89,24f505b2,3f1538ce,4940edf4,8b6d8765,f88778a8,495e8a84,a29dc391,70253583,e7e0782,18ab0252,52c613b8,34438254,9dc57c30,afd47297,2c8164f2) +,S(3c0f0359,d5f72344,f8f3d2fe,28458f30,467dc770,b55eab47,ff2e1ee2,6c773f0b,49583a39,459f19c4,5ac50d78,74626524,b9c25030,e45f2c7b,12729ca8,1edb33c7) +,S(56380666,ec1b5df2,36045855,fa0dd93c,87e38d70,fb170dd7,3be5c308,36b73a15,700690f0,b210ac6a,9aa91252,c8af8f60,b5cbbf93,8291fb11,7bce1f0a,759921a7) +,S(71ca710b,b9a5f7f,ec6b228a,af3fc47e,25f90201,fbe3c673,8bc6d2fa,9c9298bd,7dd2528d,c0750899,b1891287,170159ae,c759bca3,51e243f8,fcc9efe3,2f9abc89) +,S(78ddec3c,c938850e,7c2ec20f,98e1be3c,6ed87fae,827c9102,5112d0b3,5d264575,19072951,766a4050,c8585048,d6f19e60,22b9c163,f1799ddf,d486dedb,3d680321) +,S(9aaebafd,8852857e,8c670950,93dac29a,5813249,ed03e67f,3226ab3c,7b4ed70b,8c6a2acc,b541bd51,65e13bc,a3c4d2f3,2457bdcc,406ba71c,7b9cecd3,4705fffb) +,S(586eca74,941180fd,da16c7b2,8b9bf139,a3096a6a,20ab9bcc,6d12be5b,db2f79e2,b3b1d12,361609e5,afc5a7f8,26a21ea,ac4bfde,56016ad5,bfde6f93,a3488d71) +,S(1857bc3e,dbe4de2c,86fed7d4,cc3ee63,c0e0b6b1,36acf97e,e3638183,8d50ddf8,ea5e807b,e7a588cd,ec759c67,3584675a,8a7fbdb7,cc5a1835,119a4f64,8c275363) +,S(aa4cc744,7572c62,22a6cd,bcb8813b,64206796,27f5f7ba,863d8566,78e5b01a,ba969ac9,d259244a,9a3b7f9b,b3821a61,a4a97b8d,af7e08bf,f39f099f,8fa2f836) +,S(4588ec09,59174367,6885fad0,a43d85cf,d6f736b,cb276179,e794da35,204f36aa,fb835e7e,cdc681c6,ee1c89d1,dcbae3b,a9511692,a54c9c37,2f86f4b,cc7c62e) +,S(867acd03,7a3e5f86,99e33f22,72f7b1fc,25ce6622,5020a4e,ca3b3111,e880653c,e9112ac7,57d3f905,3249ba3c,ff64640e,639cd94e,5d6cf946,d8d7f3ad,25764646) +,S(5e0a4169,e4f81dc2,28ae4663,9ae3bd4,b527814f,4616fc13,318fd5db,26df370b,5bd335a8,fc23934e,bee2e32c,3bacad4c,c38cd624,8cb34ca9,e58f8add,3140d001) +,S(90053469,39df2418,1b5b2e57,82a82f15,542a55a3,adfe8263,663b4fff,b101c0f6,4ff0d589,973985f6,858f3837,89c3a5e1,74460778,3a1b1476,e3f1ea51,37e3d991) +,S(a99439a5,53df33ba,6a32f33,d90b1500,4e2db077,22a73561,8f4c3b0d,63cec63f,198e0929,f11fc777,ea9cd8a2,e4e6987e,6f9c1fc5,f703ce7e,7e6423db,39656cd4) +,S(51f2a136,1381ba14,c8fab71d,f82aa2e7,1c7789b1,b78c355,a0e2fdf1,5f085282,fd5f129f,ca15fea4,d6f23bbe,43197a3e,6e2b7b26,84934ea5,a97197cf,eb2d16a2) +,S(3a91f81c,8b064b2c,99509777,864c5b4f,bed208b8,f541f68e,82053158,2ea91d3a,9d59003a,d4ae39e1,95077329,e585a8e8,a542b594,b6010404,8573a01,23a513c9) +,S(71e99d50,aa22fa17,3f026dee,e728c114,5e491e84,e140b462,1056af19,9736c5c8,ff3e4f77,68588846,7c662729,1d947e77,699806e3,9bb0bb26,53f67ec4,176a4995) +,S(ad149e93,3a0344f7,3fb53286,ace2f3cc,46f7450e,e859980e,6d392001,7c8aaa39,7dbf4798,85a4f354,b4f14060,39a83789,65a5ab3f,f1157e3f,2a4f6fae,909e81bd) +,S(44f0a37f,5ad78f6c,4a40ca20,7969105e,86e79ed7,7ebbcc32,1a247f91,751ccb01,f404d867,911eb3fb,efb1e559,66b5128,70836d9a,12891905,af649555,c353044a) +,S(4f77dcc3,de8de08c,4c0a4a8b,29717868,50f5c0e3,b271ec1d,c0d38a61,b30ad5d,73c7424f,8ae80ee9,2118aa5a,b0aa65b3,58dc7300,806ef2fa,55836a39,4df57c69) +,S(794a108b,102a0923,6366efd3,8ef658c1,6d6d5c24,e5e2dc0d,38d71371,e6005555,871965ef,efd79050,69acf0fd,c1f84798,7c9e304b,5fdcbf75,1a6f6737,ce46ba2c) +,S(f1988561,234b863a,df5bd49f,5a0b1252,a72e297c,ea3083a8,22da48d7,14e8e594,7adba507,d0ab51a1,8c036612,dfda3f8f,abd2f573,d31aa03f,ca6eb361,a83fc418) +,S(48bed556,c07c99e0,71249b8f,7790aaee,f3fd27db,f82d1c75,491a18bd,cc3f4a80,9dae5280,21be418d,1b4dd08c,be5dbca8,46cf0122,d4943543,8561e555,66316e94) +,S(bbfef3fb,e53779d3,8f376fef,974bac74,ec8449bf,c2dc8a1f,3bfcb33f,943ab9c1,bea5b772,ee798aff,c610e849,5be0ea8c,ed462563,848030b0,8006c6cd,e312551b) +,S(42af3415,27b3cd52,3806b04b,ecc175c4,156839af,eebc212e,b74bd6,95b7f06,686e5fe5,ebf16ff6,2376d096,188de980,f91a5518,25bfa137,e7b46df,59dcef4f) +,S(3c56d78b,74ae87c4,6efea376,d58b7d5e,30749726,40c24d69,38ade127,71ce10e6,ce97decc,f9d1e215,d363b36b,6ca2bc6c,39fed00b,511bf883,50732752,48d54a41) +,S(4b122599,1944ef0a,ae6404a9,e48f69ce,67b52a1c,da4f291d,33afdb09,b789b61a,5c8be350,27a3f992,5377932c,b6d92bc0,62b1bc80,78e4611b,f3ebbe66,b6089571) +,S(9e42867,c02c9314,a3658f18,14d8c59d,71674955,2b00be9a,cd37350d,18d49db5,5d26d035,1ff90d73,d783155d,afe20cf0,39f65b1e,6d8d5d0d,831977c7,17a8c761) +,S(9643390c,5a95345,2a027d0d,47bf6fa8,306ab31e,5dbd8b94,402db973,275359ae,b354d7f7,1019e9c1,d23560d0,50fbd4bb,abbf492d,f595c0ce,a699721a,b27a3933) +,S(8b4916fb,86f77deb,505374f9,1f15782,1b6f7884,feae9f77,bacf5620,7e126b69,9a83c0,6d86db13,55c3d59,cbc87fae,ce6d2af3,64f9c213,816cb326,28be1fd4) +,S(d29df4b,109782c4,1f5ee9ab,b8488f7c,11de8efe,9552eff3,7ba3cf0c,d0054b9f,1bf06237,ea242c4f,bbe08f6,a79b6808,d1a5a444,1989fd1c,98a60981,40ca07a8) +,S(2aaf397b,a5405044,539167d0,55113acb,d4a20265,206ddede,203b8ea7,21dbd41d,374e32c7,1132bbac,91e4b4a8,aa32f308,13a81c2,4f7b3c66,63d8821e,9ef73081) +,S(2239b3d2,23eace6b,8ebd287c,8b6a398c,1ca58a94,5d8b7b81,7b1133f9,cf57ef48,fda880b7,4b54a8b2,8af4b26,78e03f28,f01d70fb,3e4ebbda,94ccbd43,e2b4cd46) +,S(8b3287bc,f47e72df,7edc9178,51edb145,b88ee233,9f254f62,e86ec624,a7c7e365,54a06bc0,9f60d54e,5f900868,c0c3d37c,db6d9969,1eb605be,321f0f20,9fecabb) +,S(d9accea3,3e3cfee9,10d7e1ff,3e87539d,ace742c7,24d6d36d,d8d981ca,984f7993,6adc974a,a901e5d2,d8cf5a05,a20bd02d,62947f81,5425e690,565638a8,427dc88b) +,S(d7fc57f8,34c348bd,e6692b50,474ee038,4af245da,40b8f6b3,8df2ae5,a8b3206d,b253e4b,41fd9fea,9b28e04b,a6db0324,231edb1,1f9af406,e38d2b30,244e2d19) +,S(25808fe0,ec4a1409,36b34e16,595021e2,b815f660,7a23d505,9ad5b5c3,14d9588a,d0664ad5,b1d46ef8,8c8a1eef,bb9df1ff,a75bc16c,e8bbcec6,6f4798f7,c58e7b96) +,S(e4474780,113955d6,72152fa5,70da35a7,59e67a76,29a39ecc,3226ea13,99927914,493cc170,aa2f0486,da6834d3,3566c78e,ad4eb3d2,837ef5ea,c1adf3f9,43793bee) +,S(c5040dbe,2af9d706,76ddd900,d00f1046,44e2b985,82d46f11,47602b91,126311ad,840aab11,a2316171,cc9b6466,778a9ce5,a7cabc1b,1077586b,91a8d280,324ddb15) +,S(23391baa,316d33eb,3a7ca123,6c47d4e4,8116cd4f,93bda3a8,12a228eb,437d9f14,1d81341d,9e6d76fc,1ca69af,f6d8d119,bc97c79,c7cb8b7f,1ebb54f5,cb7efd4d) +,S(13211756,3de3ba72,2fc6682c,beb39494,8aeb8fd8,c0b95eb9,45eab34f,bc39ac38,9b772721,8a4b0823,7c961431,e26cbfb9,aa527905,41b753df,89eb9ad8,8d74f006) +,S(3b8bb898,b27a2fc7,a1cf896b,bb5cd7e1,708b943b,afee896d,2ff77a95,ba789708,d81e541c,668003e5,5a1e13e9,6a1635de,7fdcbf79,c34068c9,248100c8,9d36c1cb) +,S(e1d70a2c,9af5c4ba,c505e4f5,5c0040ff,d94f162c,815a2e21,c7ce899f,e0217b4d,b25630d6,d20e0e4b,3686d66d,e2b1e53d,eb063b99,e79c212c,2fd3cfec,97a4532e) +,S(1b1e3823,a51778fa,d38f0c56,e2b36dff,a7e681c8,30fb41b5,ff1aaa7b,d912de1c,f2e99a90,a951201d,ce725855,6bf7fd27,bebe1626,8328116c,d593ff32,2584d4c1) +,S(1e31d838,94f56191,2c134817,55e467e1,8745a91c,d903102,e48f7282,21bda7a1,11dcef88,35dae18f,3c02c6d9,7f80896e,477a7b27,41203aee,5048c94c,7cd610ce) +,S(cb9ac34,795cbc99,bf67a7ff,2bc3b497,311f687a,51b5028c,422b0301,8ab7100e,5e5f0621,c804b35c,e01e95e9,a8dc13f7,e5ff32dd,8a44f320,74e44836,3cd0db1d) +,S(afc34ac4,67dad38,a0e326fa,1a656c48,24dcb884,f00a20ce,56c73835,b4e11c1b,c8b1c404,3f6d647a,382ef031,a02c10cb,77a033ae,bfc6dde1,d7508136,28bdcb96) +,S(3a6fc160,ca85aba1,619430bc,ba14e12,3330f636,7a78273e,603f85fb,e270aa81,594a2b72,86dbb390,945a7a48,3707bc32,cb72cfc9,19c241bb,61774dba,222bc0a) +,S(a840f695,657c9c3a,3f64f56e,af18debd,bdc1e4a8,e3eaee43,46b19601,433ce7d4,8a8a7ac0,ca41e2df,461eae96,bc00131b,1a77b824,7273269d,7a41f49f,ea126fbc) +,S(c044e01,70ec6914,c39d08c3,4a293268,65cd7762,d6e0f3e5,5f69d80a,e80a39d6,7bd63082,7ae51516,25ee0b5e,c2f97b37,6dd5fd7b,9205852a,92dbf922,3a0b4657) +,S(cb90135b,352b969b,45eb5c6,7cfdd288,2bf9271a,41da4b2a,a89dde94,eaa00b3a,14cbeca0,762d9b11,fe4b6d26,7277a8f7,b4dac82c,52634b49,2a84b616,2d79be93) +,S(fa7bc437,a24dab0f,21c7c063,3aa4a8ff,c8216676,75ea6963,c58c84d0,b182a0e,e24267c6,d7300c6b,6dd09326,c49359e0,fdf72114,401f244b,383eee54,d1a5aa35) +,S(e6354cdd,3f503d25,37b2bef9,91533ad8,35e95657,3bc8afcd,d0c1c05e,57b3299a,d6c17b70,6291c8a1,989955b9,ae9ef9c7,cdc2b6f3,f5cada0a,3c4d0b86,859ead7f) +,S(9da36530,7e815e3,df0dce9d,50d7d812,132ec23d,2bf0453b,483af1f,bb45cce8,bb374566,98a28ace,a86a1b10,2909ae91,3bbc4b23,22f4d09e,bd298e51,d067a8dc) +,S(dc457a1e,1a19cdc7,6ed1c3b,a416a886,13b25788,22d691ba,76319b8a,38c9d4db,39f93f66,de5a650d,d49919fa,294638a7,85e25bd8,f8285776,dfce813f,bbdc3b58) +,S(b2377b1f,1b1a0e98,5ff57bc6,64ca456f,ec216c6e,b7b389a9,62b9d82a,ccf0374d,383a5580,d723509c,bf114e74,b3eb9746,7a040fc0,6232adb5,dac199ac,77578c1a) +,S(8b1a6446,755d3a85,3851fabe,b95c24d8,2a5a86ce,5e5df65,78051124,acfc7e50,42b885d4,cb632a67,51a88aef,192a5bfd,ed1cd461,438cb623,1caf3346,9655cf75) +,S(9408819c,bacf1a0c,9e5f1416,23a907a7,c3a7efa2,30a15b3e,7d5a6ff2,5103f5b1,a9758ec9,d6f84f73,fd107451,f0ab79a2,1df08c30,cbb2456b,d6c68cb,c506f98) +,S(8bfef047,7b3f8cb,99d7c933,a884da4e,9ce6d473,c243d3ee,9c319870,b06ce25c,8bbcbd87,d06af17,550d17ee,83017c58,627f1d84,e8a07e37,ca73c12c,f7e5c498) +,S(2a857f0,c12202e1,1dcb84b8,adf582f9,8eff647f,c3803cc0,dc1d294b,414bdb61,9bac456d,a7fd9318,244af8c6,48ea23aa,62c8ae1d,af7fa9af,e430efec,46db6548) +,S(937b8c5a,90485ed3,d06b224,2ff53e6b,b94b1ee6,a493c835,25e3acb6,5d5cb5ec,d37b136f,f9fb374a,50f5a311,ae366ca6,c9c3d867,e9b7e788,da3b2766,a7ccd808) +,S(5f544d53,e2bfd6be,d9150ff3,6a1e5c5,22b24933,489201a0,65a0e0a0,3a43c77a,ba9c20e2,8c47cd98,1752c413,70ced32,8f4c847e,20165562,c3137e0b,12990f51) +,S(67b63388,90bf8baf,a5b64fa7,eb9fa4c9,e3249cfa,81a55cbd,9c297e7a,a7c807b9,dc185675,2d94b811,42b54577,edf808a5,d871ec1,6c8ba567,62061dbe,ae7625e6) +,S(587a9dd5,99cb448f,9f92ffa3,d6328032,b76604d8,386a2e2f,bd324628,98439442,496998e9,a893faa7,a49e73ca,2dbbf533,488aa687,51ffbdee,6b6d6dcd,dbae9870) +,S(36fb7b1e,c576ff9f,81077312,35d18612,18dfe51a,58642a56,8f80294a,63c29b38,34a45ac9,b82cb1bf,2c96fc1a,67de8a63,5c1a1589,61baf8e8,a2f01572,1566c4aa) +,S(87b71c8c,a0afecd2,dd8f49c5,109ff434,ab8a273d,4e4efcae,f775624c,71f4ba95,8519151f,51122588,76527c56,e1e6d9c8,5b3626a1,bd6ad0b8,1bc382cc,525676f9) +,S(acf65cf2,805c370b,a3bed962,c3ba3b0c,56c98,b81a696c,f433a0e7,a463e040,f093929b,f45be16a,f962762b,52f57ef2,dc7b0e23,cd68e3d6,63b3b402,15692d5b) +,S(e092bfed,8b010c85,b91f1674,1cdfb1d,e3e045ba,81297f44,8990475f,2147acf1,e0bb9842,ecc8683d,e4016072,86070da9,8c4d18af,8bdda91b,65770a98,77902c37) +,S(9bdfd7b9,8260bcbf,17812735,c70501db,ba386b7f,28ece691,97c871ca,610a52ae,9bc65398,3f8c4cce,731e01ba,a5f509f4,887d9a78,40620d9a,ffbcfdeb,5e371eeb) +,S(3db22321,d9311589,5c85587a,9095421c,afc8a80d,86a4240e,9a4a2f82,8643545f,42af30b8,2c57868d,e0f23c9a,fb19947e,fdd6bb45,4efc68bf,ddbf7349,8fb78eae) +,S(e9d3f14,a6a77ffc,f86decbd,c5e43fa3,c3757a64,22d37da6,25627961,622b3e5f,83ac0bed,1e2a714c,c6ed0764,da92cc17,fd18ee84,36d667f0,2f90f818,30e67de7) +,S(412486e4,f29b4887,e1448af,ad59599,b31b9085,8682580f,891d51e,9842f2a4,c31c5931,8c0d6790,c2fbbb03,9c2e761c,bc5577b,53161a8f,80d397f,de7ee3d) +,S(e9deec71,41ed506f,e2dafbe0,c9ab75f9,609f8422,a4b2da26,7c529d92,36b4ddaa,8728544,d8463bc2,d846029e,5d2a0a40,8dfb767f,9adb79eb,2209478f,6d94a21d) +,S(5a0a2292,553c46a0,823a1761,2b8925e1,bbf01c2c,5bcb7173,5f0fd6ff,b264218a,3097cdf0,adda0f10,a261f897,6447ff9c,12de696,8f05009c,e90d1575,e55dbae2) +,S(49cb2a37,8b681ad1,ba1dc23d,8d12f186,563a40f9,ce0ffd00,5bade11a,4928d1f,f235e2af,c0f65fa0,ff15a938,ad804a3c,fbecc5b6,e276bbd6,32a06959,3d6732d6) +,S(d4d71cb6,36881177,b05ad510,a621cd44,3dd1afd4,84d177cc,f99abdb9,1615feb3,3ad65378,de3eb9b1,606f385f,a950d533,5316c363,fb076c02,ac7f12f0,7562645b) +,S(591b2e1,4bdd6e00,64f798c3,14e86cc5,529c99b8,3f47d148,b7e3e642,35bfab2f,4f686266,fd2a4c66,4d6bbaf1,2a368d59,12a51789,5d783aec,a986a568,a8fa59b0) +,S(ee503c85,c984bc49,3d7b63c2,92eac9de,be253400,f4988086,970be236,47356876,bd828abb,6f9e89cc,741704e9,614d6711,39449bb3,a7ebdccb,976c573c,4bdaa47e) +,S(2f9b4fa7,2e5a1fc,ca36a3f9,8987c3af,756e78c9,77fd5697,758c95b3,14b3f89e,9668615b,3d5b3c74,4ea2e1ce,e909d9c3,956657f2,1a65fcf3,3f0ea150,1ca4a15d) +,S(18b6d06a,a34124f8,5f92b204,e2a010aa,a1f4aebc,6e13a62,34eb1c92,7afd46c3,66cc31b9,bdf600b0,7e624bf,a3e079f3,258b0ba5,5437264f,b460481d,1f4bef4e) +,S(3bc07963,bf758dd8,fff37d0f,30db6eb,67662a4d,65395688,1cc30340,ddb44ba0,60e11ef2,68209a1c,2df9a3c2,276db6f8,8e6dc1ec,b74548a3,57d770c1,ad057e5d) +,S(bf585d6,5b325423,afd49af3,c9fa68a,91498b5a,f0ee9e3d,d089b288,53a46a8,2f8944e9,ea484b9a,51256e2c,fa9e5396,4c000c1,3451cc05,94bcf6ad,e38e66a6) +,S(887c0a2e,be7a0257,5d5b59a9,11f40ef8,f0cf1438,d7f05a7f,64e9c133,cfb69294,45f334c8,4f9bc66f,50fde594,94175491,9c37c30d,d20f6a58,40271e71,60aec5c9) +,S(9dad7af9,a73325c,59a17700,fc3dc6bc,3718e79f,804a2116,1bf9a36,622f7d4f,2b7c225e,99c6a94,c7e326ec,b89eb8dd,40644bad,1893136,6442985a,e6159525) +,S(847e4f95,72bd29f7,4f3e9a2e,d73cdfcf,63bcb61c,9caf8694,7cb84594,a7dd551b,ba6ed282,e0a92c62,fa86286d,370cb344,f6f1182,a101c4b8,1ea89a15,b393ee16) +,S(25213205,df1c3600,4673c4b4,49256c70,b5d8c62c,cdd3d580,2684af47,f047a593,36dc81de,5af4709,fae2a47a,8a205647,95aabfec,42a79f3c,54763d67,ef096837) +,S(75228cd8,4866df7a,4713a25e,f2fb29fc,95ebfd70,5d73229a,b170cbce,1058e87a,fba4879b,cc02d3f4,fd25f056,c0854415,68a47355,929bb812,a7dbb30b,c012fe6d) +,S(74945539,22867c08,6d02fb07,c0424890,981a1337,42c63fe4,b6adadbb,59b68568,7d774917,8df8b19c,8af0bf0b,5247e751,3e3b8d04,46465180,341ebb54,1d1af3e2) +,S(69db7f53,2899e39c,fa0cbadf,c29946f2,71d28ce3,57f47f7a,609e0fcb,ffc9b04f,cb0243bb,103255da,ad423ffe,e1d50f9f,26da7cdd,764a11f0,a6694b63,199feabc) +,S(727ecdc1,fb4a0dbe,fd2fc37,8902f922,ec4aedf5,53dae225,1d173c29,2929f8c7,ae5bb2ed,13778d60,66446ee5,e5754db2,bfc4f7c1,64bc035c,224d495e,651f453b) +,S(903ced8f,ec2544b,e207f5c9,e2c7f2bd,91213873,5eebc382,b129e334,7ef25b72,2117ca29,ee13d31,5e89bf33,afa8b7db,ff75c795,6be40c8b,b2e41cd5,943190a6) +,S(c4d467ef,5149e117,3638a5ad,6fd36373,1c8906f0,80336ca3,3a6bbb99,1a03f33b,ea5b5b24,4c536bcf,ce3d437e,abf2fcec,431ad80c,ff975b63,bf398163,629b5c7) +,S(16b577a6,5f6d09f7,b23b2b7a,dd80a2f1,cff7a8a6,c36bfcf9,d325e37c,ea37326f,66ba8e2,14674e5e,228a6576,a52a791,84d98be1,db1a2dcb,e9073934,d09f15c1) +,S(ad4bcaf2,e480adfc,e1e45384,6bd7ddf7,67fceedb,d2bf1f5a,6768531d,9b63412f,51fa0360,47810f59,11b1b3a4,525ee72c,c4c891b7,56737f4a,a61a380,e9e741ac) +,S(b249eb38,52d2e787,4a52d776,d1bcb399,51ca65de,dd9e9dd5,87d73362,c33b571a,3e260824,14e4ece8,83cd84e,b177096c,62dd7706,a79c31d9,b1103232,53a66258) +,S(634192c7,696a01ae,81310d3a,59d2c53b,74dd0560,7e3be9db,ccfd1ec4,bde14a65,455b4850,e366e077,d86dcd70,9c787b18,4fd4cce4,4b92954a,dcbe5222,57b0e00f) +,S(ffbecbf5,d433f80,49e159cc,c14b198a,acc3c5ed,fbb1ace6,fd295dcc,4a2f5099,422e7aba,1ec66cf4,36489acf,9c03ba33,dd68d368,8a48685c,aaa1de52,440be3c0) +,S(65b4c4dc,1067e823,9cf4f1a9,d0632da,b6a8c83b,509a75ad,8c133dd1,5c1868af,f7cf5d9d,d7654618,5183f8ae,7f42a03,20c67817,9d5226d8,40dac1e5,f94d260e) +,S(d380ecce,9d603502,d518dbed,405b8d0c,8de48dbb,35d5b559,24c1a560,9bf9c67e,1ae84480,fd18ab10,9eb0ac15,265c609f,8b241b4d,a80b13ae,50c35f8f,db64c128) +,S(17189d28,b5ce1bbe,d78a1d96,15dd1a50,3fce43f7,19d042df,63484ff4,ce44511c,fbca28f4,7c7b3e39,4121b948,ba561289,85c53298,8db2fbb5,7596a473,a782350d) +,S(fbe332c2,e2a8b07d,a85ae4e6,1925bf3d,be685e4,fafcdee7,3f558382,80c2e84d,aa917342,5e187da0,e3f8c6d7,79b42cbe,b11c43e0,6b594eb3,e1a5797,ea4e29ca) +,S(8b9e8a87,8b5e725f,8b4bd518,22e9cd21,2100d1f5,cdaaf210,2d7963b,a0bc834f,331fb31c,c28f56bd,5ff1d6ad,80b65702,b2e873dc,4552c563,bed77a08,cc3ed659) +,S(97e92411,a3c0bcad,17a798ac,eca61ac2,34e1a68a,b668aa3b,d730cc8e,8de111ca,b0f170a4,bac15e17,2e75fb75,93c21a41,4794976,bafc7eb,7cdc4bf1,6947f48f) +,S(20358813,33a13c6e,cc8c76f8,2c279577,49aa3c1f,d4d51691,9cd23b25,d044eea8,480afaa4,8c86af0a,ece2f951,7df0f344,259fc4f1,4d9a5c0,ee73c891,780c7f29) +,S(caa1eccb,1575740a,90155103,9849befe,617579e6,22b7c343,a665beb7,a67dbcff,aa695bf9,f1c972ff,b6fd3451,1bf042d9,1aee5113,9c1ea577,e0bd52e0,a4e0a20b) +,S(8e7c2ba2,36f7a8e,fdaa212a,7fd6b883,f331049e,6e8f2bbd,138a3adb,c2620719,bd03702f,1434fb3f,5846c4e2,e779f8c0,d9b6e031,1feced02,20a618f8,2eec105c) +,S(e07dc033,6dcd04c1,fcfebf09,40c07783,88fbad11,8e4eb3b2,4dd637c7,baace975,b13bd0a0,e5eecb0,762ccf31,1bd53156,ef6f9268,50044195,10d6616a,b32a2c2c) +,S(6606c825,2679af01,9d8a904,4db0b28c,95aba791,d1c33072,97afa2df,bb8ce0cb,7505e59c,a9807993,5710de77,747868fb,10fe6c57,36ba2054,6477cac8,8e1d3cc) +,S(9fbf4cdc,ec48226c,b45450f,b0de5dae,2b31f935,2b99e0b5,22ab9935,1de6f061,f2e4d0a2,3da020e8,60a0d58a,2362b7dc,e9c5b646,7034ec91,a7dfa601,1ef850dd) +,S(e06d697,5195ffc2,988b0403,e977d32c,b7cc8167,e800db1b,976f6186,286916bc,8015370a,27d3d221,7af80e04,a49003ea,68302bb,c1a5c83,63244c0b,c30bb8bb) +,S(adcd67ab,21d2aac,65112fd4,e6e16cd7,ff792577,e9785a6b,cf4a3f0a,9293a6e2,6527b620,29062aaa,5058608,f5cc3bad,8e4f40d4,9b5be93d,faa5c2b,57fd7fb8) +,S(1d9069ea,b43a64cb,1187e963,d96809b3,5a447317,68910e13,3e0bf7a2,e1f2bd38,ab03dede,854b8fe9,4ad2f9a6,8ef5415c,fc78c28f,a4ec33bb,f15df31a,91e29108) +,S(f1f6b776,b8ec892f,148168ae,8ce83f4b,603206f7,2a38140e,3a422e67,a20a768,892ba036,3413da21,17d9b11a,7bb25cd9,e7136a7d,82a7f45d,14abfcb2,b9bff25a) +,S(a884f94a,ce79f4cd,f3a8d1b,f8a2dc3b,c21d1c23,433a7fb0,401c5df,1efab379,5f53356c,6e45248c,ec282fbe,ef03ddf7,c611affa,5d0d3082,dd630f80,dea05c96) +,S(b1988430,197a8800,e1016564,b48d4f7,1fb2be08,35e9efe9,81a80f10,e61da9ff,d5c7864d,ea7d12d2,5be7ace5,86e141a0,e515a41,5ec70bdf,44a68d8e,4dd849b9) +,S(15b73aa7,529d5b84,bd5c1db8,2b6c93aa,46985aae,942f6d4d,1094434e,d27fedb,297e32a2,bc8ad13c,42011259,b1ca3d40,632ac6de,f2fca262,75dabc8,ee721f89) +,S(339a76e0,443c1fb1,65992630,116e523f,8e35cc7a,16cd0f3f,57f7782c,7d97d40f,8990652e,fecd784c,33e75620,13b52e6f,914f0f6b,c413aa22,9e5bd773,cdfe9659) +,S(bef26cff,fb80ddab,9458312e,9b09044b,b1c535e1,84f4e51b,f64b5183,b67adfef,3818929a,ef076a83,58f20518,c5b310f5,2c6943f2,1675bbb5,ba1bde45,9d37490b) +,S(cdc56c81,8dafc273,4f4dae10,84f91b08,3dd40d86,23125894,c71d9243,8b2427e,f4b309d,e9c828b5,44542569,45579f91,b4376935,3f586c71,f07107f0,1f503cca) +,S(3ddefe81,b1f38479,ec9be80a,1961c5fb,a09312a6,2a775911,cd65200e,7d75bc6d,75d83edd,1eb9b479,4effae4e,ba8007d2,7c7e2d51,a9e95d3,a1e58358,56df055d) +,S(da7c6776,99032cbc,4c2bae12,59212cf2,a18879d2,63aaf99e,76339041,10576826,2e7a6d88,360ac45b,9871f779,9e80698e,4314cc3c,3f1cb1d0,c07ae9f3,38553f08) +,S(2611b435,e0bc20c5,88cfaa80,17f4555a,b8aba4c4,ae1c0385,9dfaa911,aa80d927,eb58f81a,e51dc504,465ff8c1,c0529fac,e47549d2,429e5b41,7ce0cad3,7d4508d4) +,S(b0fbe548,36f07c4a,a715aa08,806b71d6,b8f4c09,6945a7bc,3defec7b,67962961,cc60a224,66e7744d,c58668f,ede30ca2,1c1749b0,45ee50de,2a881a53,83e5abe0) +,S(1a03336f,dc51ee75,facb7688,b4603c86,77d371eb,5ededa9e,b50bb49e,c9020581,3fcf1b03,9c0edf8,99855fc3,6acfdbc7,bd507145,9be7e18c,e9f715e4,98daa7d7) +,S(763a1215,c8349155,aa849b20,f2f68a36,cf72892,49e4d3c3,810ee72f,78c159ed,37826a1c,cf30e1ed,a1795fba,d1f2f227,7b68d5c6,9ffd390b,d7dde5e6,58482684) +,S(7fc7b4c2,30183e37,dce0a586,a6f0188c,f7e664d8,5fa05eff,63f2c27d,cada4edd,fd7a780e,6ea89a06,f0649343,abe7bcf,6338d2fa,fe457f50,ac415c91,2bed6a8f) +,S(222807c9,b0686f55,d9eb7d7,412588f,dc543bfc,f016c8d7,74726135,dc65ddd8,b9670902,456c22ef,cb36d155,319393da,9a34b2b4,301c898b,43d957b0,a56cc2ab) +,S(79d8eb36,98d8d06e,6ca33f69,ca04d3c6,db636346,abfd603,ed0189f0,fc5fda77,bffc97e1,1b0234a9,8dd1d460,e19db522,931fd49e,26d7ab28,f1326517,23d955f1) +,S(a2366618,8e72e1b6,d5ab7713,decf6e3,1f801a6a,ac88ddc5,d464d127,ebadd34b,ef23da36,f3e71913,2774cd1c,d150ffb0,70e99ac8,5193faeb,b1736abe,77d60ed1) +,S(b6bad04d,dde6816d,9123e8e7,43fa46a5,b1fcbdb3,fa1f13be,480fc1bb,49dd9612,585fae09,a290b273,a68b7f34,645b80b1,850c6507,d959b546,7f1b0100,b6a4e511) +,S(2bf21c25,3353e715,283113db,8bb5018d,33fcbd58,f3d6face,b491e1d2,f4c3dfbd,e3a033a7,4e8a4d5b,c8c49121,43a47b07,b9fec4d8,a0d5b3ef,569c44c9,cf8d896e) +,S(64c27a41,e978e35d,63f3a90,e71091f4,41e9ad26,6a05edda,358dddd2,5d842744,fcbb625e,8fda695,988aac9e,cfe67c16,bed76802,3b527d27,521c1339,3dae32d1) +,S(620a1ef6,fdfe5b5e,9a4ef435,63cd7c8,af62acad,a02fb399,b7542d2b,6481a58d,b0b79e3c,550393a6,fa07f105,c547e203,a45fa7c5,ec825ae5,7bec305a,ddb8054f) +,S(1523f131,7cef7154,347ef68b,424ee0c6,15e71251,a06bf8a1,ecd74675,307b95cd,c72554cc,1d8ad664,dc1fe67a,3f11e5b4,a55806c5,53dc2612,bf5516ff,f0743b1d) +,S(d765303c,a9c8794,c00d4d8c,2cd73fcd,b43f763c,b753700,f66d7294,4eea0cfd,bafcaca5,204dd77a,a3a17b73,da3064dc,35b4cd22,ae01a623,781603d2,57bc267b) +,S(a75eb61a,fafce965,65198759,c25cbc41,ffbbf87b,61839a8e,a17f04ad,64c468b6,99802893,240f48aa,53c49b18,1a6d50e7,a8eba321,64d587b8,41497d9,65869873) +,S(7fa41b4c,d0bb91b6,c25813c7,1241e691,e7053f7d,beb0bcfa,4084e06b,bce59f2a,a0941779,755a695f,8317e7e2,f6a2c57,9431e22c,4aa09f,7e583b1c,e184aec7) +,S(816c0db7,2240f12a,d8deb440,8ae2276f,3bfa1bfb,d820bd6f,cc5ccff6,16e4dbf5,2a52c93d,d91ee658,9449dd4b,bf70255e,e57acbd0,a981ac9f,67c16604,3f7e52d0) +,S(33f635f1,317f1425,51390ac7,346905e0,ddbb3d87,b0c1780f,bb7dc950,c23414f8,7435a155,84eb1988,b974d570,bbc2b2c1,15123580,2bf93bd,b626a543,e54bfbcb) +,S(d2943ad7,4b0bb03,35307185,fbc438a7,bdfe11e,7b5e0eb3,e94bec46,a251999c,2dca8fcf,19aaa314,ebde41de,fff6e9fc,b6097d6b,3bfec06f,19aa4941,676d577e) +,S(26aa1fbf,c54375a6,fec54390,b5e44a0c,fb524b65,c10d1516,b0112525,9dfb1da6,49eceaa6,4c575ce,337d0b6,ee7e8198,83973c6f,abfa2f,d4b24e03,99a91b02) +,S(780aec1a,f866a030,5ca6d7da,1948f2f6,ab18717d,e82300cd,65eb58cf,a7d28d20,da650041,e45a0e1e,a9075b6d,107b6f7a,414b3a02,18aaf929,92cf2978,bc08b91a) +,S(1dcff259,8e59dd4a,3e515853,b3cc41e5,ef98113f,29ff7b3d,5eed6d50,2cb88b67,d7978630,7c6871e4,5654307a,e3836e07,aed86cbe,a9ab92f2,8c680172,1ab77271) +,S(b19af4a2,77321937,93e367eb,5ece1c63,8f336f3a,9cdb4b01,e2488ee4,373ff70d,a3ea8839,a5105a2d,4277f27a,26a8ee73,b9e73fcd,d54f641d,c214d5f7,9c821dec) +,S(1c8c1f29,548a9df6,f28b6126,b9324972,35f24e2d,a488e066,acf782f6,b4f6755e,1a79ab52,6a1e1aec,5ccc7e2a,3196a07e,78108b23,a86630c9,1795ef96,11ff432d) +,S(b6ff8dda,1ad6c3c2,3c8a10b7,b4313968,e4d70075,12c421d4,f0b1153e,bd5716ce,4d3869c6,b7b74167,ca0d21e4,edb3e01c,af8dbec2,aa87a32d,7c9306f9,a5ce61b8) +,S(dceff4ac,f61ba84a,81821932,9fa0ad60,ec08cf9d,830e6ccb,375488ce,d0f47b5c,949aece1,92bc61b4,883d5508,aa4a4aa8,fb1b8db0,385613b1,736a6eb5,c137d6f9) +,S(5561e7a7,b709689c,97c4aa18,9a3ce841,843ccf98,be851c8b,131eeb37,dca5ebef,4b108c17,f298be36,430e6b7f,26b414a2,33b924a6,e65dd5a7,9a1f15b,a99a14da) +,S(4d989577,89b01595,8e5d5bb,2e064e9d,9c56a254,5b3c8802,7ca0c997,a395a257,aaebaf33,ac950756,c5b200c9,f5b009c4,506964ce,5b706675,7951f221,40b6e0d0) +,S(82b58e6e,6373c4b,30ad55a9,d66cc804,e9da512f,bf4af668,1246a12f,e33bfc8e,8378122d,98dc1e37,2c33b5c2,1ff4c429,d6d7577d,2ff353d3,87cc0478,1a5ffb38) +,S(f49fa88a,a773784b,fe57d880,9efac44f,714c3d75,3669463a,d1199be8,2c6cdf3,41aff6c0,2164a411,c8be7281,493483be,aa9855b0,49820772,b96aa39f,9a18b3f4) +,S(67d485db,ac8445a1,8c1b97a7,8c1852a5,ce910f0,d78b073,969c58ce,c0efd78c,71dda92a,24ff2b31,5fcaa5a8,28bf07d9,596f49b9,cf19f4be,44e6f320,84981e97) +,S(9145f6f2,c19038c3,cff99883,b5a71760,30a46f7e,8b5d350a,db2a9610,4704d4cd,9d9179e7,852ac5fe,1d4664d5,4d778b3b,6c8e422b,b6e78add,56700dbe,6a22b0bc) +,S(492d5247,efc2f5f1,1cf3848c,e0719abd,53674089,808de4b3,5503e23f,46b2631b,374b0762,7812c898,7ed68877,c995a4d4,9f021d0b,29270863,d50b8c40,808fd750) +,S(5b8ddfb8,bb811678,9298d231,72bdb9c5,25c6610b,46187549,a381ca6,17dd1dec,b47d3764,fce3218d,17d0dee0,133d4f3b,c7ae170c,3e8831ce,a92a3531,7b1f7568) +,S(88db73ba,25a61641,af5e603c,cd54c6d4,63c6feb,da11ec5c,6a35d30f,e406c65,39b06f7b,1c1dd7b7,884c67a,e295f8c0,9b8b5be1,c14456b,4027bf1b,4800a753) +,S(3495cc9a,a23c1eb4,2cd85070,bb095563,2a7037bd,e330f721,fb38091a,9c382451,7feabb21,6f3982df,74429957,3c7557bf,b82ff4f,462fc64d,d9b3e10f,b69799ef) +,S(e8a5cea6,970e10c7,93f75666,9d2309b1,a4f7b52,c19db4f1,cd69a652,c445d7a2,daacdabf,bd5259bb,96d2dbf3,668e8c16,fab47624,294d9982,b961f0ea,32402b96) +,S(6e8c78a,e81a7ade,3989c483,7d9efe50,51af33f2,b2b2ee32,b5700b43,67931f9a,e5664504,751b41a1,45b5412c,be10a782,7d1d5193,30dd69ca,47be2975,2c291d35) +,S(b8f520a3,10c5b2f,8291722b,1e3a92ec,b9551db7,744dd208,ab4d1709,310a539e,65901c3a,b6712ded,8f66451e,1915113f,a96d3d1e,d9074790,9e291d00,970f3d75) +,S(a0538458,64459815,888bd917,3a2c16fc,ae0deaa7,cb804adf,e49dec0b,b2d8005a,27882f00,23dcc9fb,890dab73,f98925d3,e712608b,3d6069c8,ca58ca6d,de0d7a67) +,S(41280416,aa7f0924,1ed8b631,fc2959be,2117cf03,9c12f600,1faf8a05,15222144,c2fef80d,fd8e8433,ee55c363,c43f7e06,613f354e,32aef8e1,808817b1,af7f58e4) +,S(c4b8ff36,cbf83d10,51d130b7,e3d613b2,8b536f0e,b050a41a,7dd8f8aa,18c7a1ee,87d3c44c,8867337b,466ba76b,47512399,f6cf6c14,ad4977fc,276d67ec,5e7b4d47) +,S(40eb693a,7cb938e3,a6b85010,6b7cd257,1de630b9,86da02a9,3b7a7f5a,a0a51ac1,27b875e0,14d3417d,cbc3b770,c4cb8805,c7ede7a4,efa9f89f,a51e36c3,af6d8d18) +,S(f7491902,54e30180,75365d67,5f2ddf24,ac0c9d84,df371fb8,6e94a9bd,7f81dad4,6a271b36,c549080f,dc9bf1b5,ad275d62,68af0cc7,2ec68c7c,17ac24cd,5c5a6e46) +,S(cf13261d,e4ce19da,14e6996c,97a23f8e,f16388ac,c364f2e3,6a053254,849f287e,5568858d,be1a7447,f7e4ab51,bb872a78,8218c483,c771ab7c,4a0a968,73adac10) +,S(9c6bda62,9dd087df,d5516606,62226847,94cfd517,72c72a81,7263c761,ad3e8cb3,2c77dc7b,9c7fca7e,bb35628e,d62ffc79,9e5fbcc5,e25bb0be,5b1b67a,10c0eb3) +,S(d8d1035f,64439dec,20ecc9a5,5b314959,c1526d7b,d15b1922,bb7ab7e5,b9d26bf3,ab7e34d6,ccfdfea3,f607f697,b941bbf7,b922a436,b9cb4314,3eb1b948,ccdbbe30) +,S(c6f40a23,b8227a60,de32e104,7080f087,7d53aea5,b00a22a9,39cbc228,d1c1d424,e16bc58f,cf4b8c5c,9106f1f4,b8b2374a,63a3136b,e5a8c64d,7fe6bd0b,c1d80345) +,S(3a69da22,9477d3a5,7147c2de,cce22de1,d1e007cd,61d22509,49400b8f,63a43536,c79c4fd5,c4ba6062,c7cc95e1,c9b79cfa,46b98cd7,389cbc39,25cc2fbe,49a0e284) +,S(1b5fb8e5,492fc48a,46b7f6fc,a658f218,814c3d82,90aa339b,7bc7b794,325fc9d,a6290b12,4c9e0819,2b6f49f4,c4935b01,d2ca4235,8efea982,1646fbe7,dc7fff39) +,S(fdba0f02,b004b4ea,dc9e1c37,d1a7fa7e,8a755922,31f054c7,47bbe8ec,349a3845,42da61c9,e96471c2,6fedefd7,e30c43c9,6c3c09d,d72d662a,e2b82022,391646fe) +,S(c39391bf,cb7b9662,be7279f7,6338988,7c166343,2faf1760,f38f0cb3,a35140df,ea14e034,53dc82cc,484b8b3f,ff79d81b,449f9f81,d7a52d66,4c5b563c,9af556e1) +,S(254ff13d,c716cb2a,1ac0e36c,c6cb20c4,19aedd0a,2219321,675e1744,859cbcb,2644cf3,916d26af,9e36bfe3,247f5be8,896e02cf,83eb3701,c400ad2b,92e35921) +,S(24e4aa60,e9b464ad,a2c8974e,98568026,1769d35d,aa7c8c5b,71c9f57,28930474,9df7c2d1,82a94fe0,80a2244b,ccc9406f,6ed37c38,d9cbaaf7,8d973538,e8479f8e) +,S(7ad9bbad,f906685c,3efd81b3,8599fc02,fb19a4c1,8bff1ef8,702c4f6,d17543a6,9878b970,9c4018a4,f713ac8f,d8a533dc,da45e243,44926df0,133aad64,c5246f95) +,S(97d8fcd9,383a8717,e0c9eb2d,f7dfbaa8,c6e08339,be5fa2b5,1d12565,62d8ae53,1879ca4c,f5784f86,7f8363e1,5d85afa7,b1f114ca,7f8f474c,4a2c028f,7c6a777) +,S(3e0fc785,5a08c32e,1e14d1a9,2c56ffac,2af1acef,505ed72c,b8771ddd,8afe7572,6d79b7fc,fd1e8894,f0844fc9,da30b555,bb1768f3,df15a0b5,884e5b09,6e149390) +,S(d9da17db,b132d864,1da3a9b5,389d6f5e,fda483e7,fc7577ec,55b7ecfb,65602562,80e471e4,5feb0d70,2ee7f9e6,3f8f23b0,33a4f35e,8a0841a7,b67f8c63,c623b70b) +,S(1848f766,afcd1e34,82935daa,7b71212b,f38e2853,9d6fb9ec,a3d9809b,6fca18f3,ca9a7882,645d54f2,49d35df6,3748ed94,5f9196f4,a93d4b7d,ee70f44d,b6f5884) +,S(ed3744ae,9a7dac76,5ad65b2d,4fc2d15f,1a9d0047,f72c6be0,a849ca04,497a9f2f,3cf76d2e,d4e5c6f7,e4b23d03,23c477ef,2fdc5248,7066ef36,b763444f,6ab5fb5b) +,S(c4607b73,edf3545a,b10a3a65,e0312707,91212acc,a86226b7,b1ccfefe,5f3676d7,31ad465b,8595f783,d5c0cd03,6230e3dd,2628ac9c,45dc6d77,28c42093,3d7eb3ec) +,S(1c0962ed,2aaf69d7,8b20af04,93a3fa50,bfb9ad1b,64eb0699,641e4b0a,bc0e96f0,49f11af6,e088e3a2,a429c5b0,a87a7f6a,591b5a60,a397c123,2d56a4a,adaede44) +,S(6a0476c,9a41959e,98dc6ca,fb7b4381,82250e1a,ae62445b,1f09a098,ddc19454,56842c97,8ae3c4c9,2c266cd3,7c8e3514,ab4288b9,ccc7e33f,be552ec6,f511c7e1) +,S(24da0f6,53a30aa0,a6b14999,329f03c6,cdb59a80,e9ca68f6,b9367830,a0191f1c,83e79080,aa78bcf5,b810683a,f061c614,cdbd3b69,9e200b53,d1a2c757,2c138235) +,S(6c3483,22479d07,f5871901,3264402,a078728f,227c75f,6f33743f,9a1a7764,a377af74,4a125408,6bf128d8,45bda458,be779210,f0d85bff,eb192c0b,a0dcc4c6) +,S(212aabbb,4d14cb26,b26cf249,882b36e1,1cdc26f4,a1494905,15478f99,d5c43baf,e9140a0a,81d1582b,98eed57d,7ba8e584,11c22ea3,fc265148,ca1d6050,3847e281) +,S(a27478f1,2afca20a,8d46824e,5b0ba272,f26ee0c4,24a951ed,f371425a,b343b10e,51e74b86,a7fc97aa,5a3bdecb,67818d7a,70ad0ba3,7c7cd759,8ec80a4b,bc4036a3) +,S(e1c1269a,7171f9eb,a2e2fa7e,8f55e0e2,b9b8a507,e8cdbfe5,716f711a,572e293,41cfa170,8953242,f98d8e24,2e827684,5562c4a7,6d034848,e639335c,32afba46) +,S(204d544c,19418e19,bb328e1,ecc0fbd8,62f0f78b,24208780,4d5827c0,f5e09efb,c66098e3,1f5d1587,10a3799c,b4f980c0,18cf4ed4,534e49f0,6d057059,93d52fb7) +,S(16eddf0a,11a6f2f2,df230de2,f78b4cf0,980138b2,3ab196ee,361486c5,7a5172ad,e3db358f,ed8ab3c1,9204a792,6102c420,1327bdb4,fb81718b,b39a0ce2,b5992684) +,S(7c9f7831,2298d96b,65e6b7c1,2315379,3df7001a,79cb080e,af6829d3,2b8995f1,2037cc3c,90e3a0f1,b242018f,c53cb32e,b2481dd1,bbb700e4,a6019546,edf51e5c) +,S(129c2d46,1dbeb8f6,c1b913a5,b2d69602,63aee7e9,530c6dee,21168b16,6198fb1c,29ffeeb9,61c027f9,a2a14b72,a287d566,7bbd80bf,7cea0dc1,747ac25f,55137964) +,S(8be2e565,30d4932e,f6d1f9da,a3225b52,de204927,e0633b12,570a049c,1f6175aa,364b4e87,1a130cd1,40007056,e3c0951f,fabfe4bd,6a687404,23cc1800,505510c8) +,S(40b43f08,42ae9c67,ab0ceb62,453b4c41,42a6e12e,5f880c5e,27c90997,6252fa6c,6d1ac6c5,de1b2e5b,b225e46f,2c5a2d2c,248f11fe,d123ecca,4432a04b,cea2068f) +,S(47e2238a,2f34df3,cf1caa55,93c49db5,4c2cba9b,e3724f17,87473a0,5ac669df,4d04cfa2,3ee255f,eca104a3,60a52f7c,acfc6127,608dd908,9399be82,9cf31f3a) +,S(17286c55,3a45c6da,b4843323,84c565d7,b3de474b,5df8ed60,7c7a5c9e,e1cfc35a,6172d347,75a45151,60b104b2,79543107,5b194d24,793340cf,6d8723c9,6320007f) +,S(3451af9d,3c1492ca,d090bd34,45632ac5,a3e96c9,4d9ed258,b7fb2042,8dd7bb31,dd493383,21236e06,e7284fd7,84c1a3b0,7687c513,927cc79a,cc5f8b41,b14974d6) +,S(b00aa481,eccc5e7f,cb5bee2d,a6e0ac44,ed9afa3a,5b2a9619,d23d0252,14e5ce2,687b486,1f0032ce,f42ceb7,441f81fa,10d732a9,e7e76242,4a2ca8fb,e88866a1) +,S(53ec8e9c,c75bc91f,f248a239,34d735b2,ef706d4,c0761e2f,a8a55c7e,a0b23311,1b11dfc0,63c4b1ed,1b5104e0,15d5c5a8,c352f54b,9fd82768,8b5104f6,45d45919) +,S(1ecde1b3,9c072c9a,3b3eddd8,d71e3383,2fc80d78,7abb70a4,cdc9186c,a5efab66,4f5aec7,5a799762,b70cecb1,82049cd0,239f0c30,948a25f3,daae0bc,ae6dd626) +,S(1fe2fc3,702b558c,7ab9b479,fa08efa5,1e90239d,677052e6,5afc8754,9bb1394e,f6b586ae,3a395d3a,c216df2b,66d48fe6,11a64c0c,8a7db26d,30fbd720,c6845c2c) +,S(b53d0b2f,b8ac2ad5,582ba0c2,f031c7b,76e64cc5,28a5b9ba,872718b1,e06d5dec,8984e589,51ab87aa,64bb034c,17d23252,4101577,9476887b,351f034e,92e3a289) +,S(54e516,3bc2366,673fe5d8,1f335d81,a06ca310,a2116ea7,bc0ade12,8f37cf95,f471c826,9deb2c2b,f1b7206d,6e817355,9236607a,4b9930b4,c5b3a781,7312bd4a) +,S(e7efb772,a1d423a2,d39e2224,4c8bab91,3b8087ac,65d51f59,8c4db283,67b29a7b,f2dfb586,31d08b0,bf2b47f4,17f6a18f,704a39a0,a152e3bd,587e45c7,93a9b6e7) +,S(5ec59daa,e8a263a8,2afb4309,79ef0321,b163f2a,dc083e67,3446b98,d0b519fe,2eab4cc3,6a3deae5,30dc6aab,94e5584f,a8a5316f,5c05b04e,84215ea8,79ef82b9) +,S(427b5cd9,20589dfb,dae975c8,efc1f098,1119f69,c40d1216,58ccb903,d4bb62f5,d055e6e3,b8794842,4162803c,35fca878,6500b5a3,6871b18,d9846866,56d1213d) +,S(54954b15,fc594f3d,8f422c7,9dd42cb8,2cc30dd8,c0d10736,554844ef,9d05bcad,86a7cc65,73569c20,2aff8e04,21a00e4a,9c6b88f5,ebcc3975,64b4734c,f735373a) +,S(ff6f8f8d,23670fbf,2a1aa7b3,39568653,573eb480,67a2a1f0,1b55a1e6,b1f32559,7a53f1a6,c7cabc57,d731552f,9fd9447,36cd33f4,3f8f57cd,2d4ac1dc,1ac7e218) +,S(74d725e9,9021c210,e82d852f,41e0cb8a,3bba8efb,8a01552b,8420c265,958a2381,4ee69646,aa9f70bb,883d6ca4,e1016dd9,92f18de6,26ca27c,aeb5854,f5bb2bb0) +,S(e9439f7e,fc0342c9,c2d68fbb,c58d85d9,feca0570,d77448d7,6d3c3251,e49c845b,27da0d98,1b7e257a,eee90b45,9d0d7065,566667e7,a9287592,2031be02,3e233916) +,S(9684a48d,81a60856,6dd26622,b3ffd133,5facb32,9f2c4f1e,2c055cb0,a5c0b911,92038ad2,50adfbaa,5d587c31,f5e7b4e7,294bbd25,e7af1816,1be3532b,f24e68b2) +,S(1d003af9,469e26e,c7f0f121,4dc58728,9e4cf837,5f9d19b4,16277c97,1a0e7609,99c7c3a5,30822c72,b31dad8,146d2604,ed795f1a,7c37e139,c84afe9e,f71e5123) +,S(f96468f8,73cfaab7,76c32fa5,89f72c09,d680abf,c601642d,1c649ca,e197a149,1ffb4bc4,8495d31,b24a7ff3,353857e3,b6708186,c18936e9,9793e4d4,fa083b1d) +,S(b6ad4106,e30294d2,bcac86a2,131150bf,d93c14f6,51ec65f5,32904412,b3f89d32,19a366a6,74279641,d055201c,c4c42b43,4fa40792,90c1c3ee,5db2a4e6,38d228ce) +,S(875472f7,190bf305,9120fec3,ebb69fb2,2d47828,6cb1c108,a977e7cf,e45b9b9a,1adeffc5,f5dc59ad,e15d163a,95f8049f,a172d8e,30544562,50a7898,55a97c21) +,S(5445a9fa,f4245414,e64f7ae2,a1cb1b1f,54bb6fd5,af4a7407,4d9eeb05,9a27a670,496c5207,7da2e082,3e48978c,a3fb7a1d,3bdf07ea,61fde94c,e87741c4,e9418018) +,S(dae18625,704bac88,51f35026,7856830f,74d3a8f9,7c1f662b,f4c4cc3d,7814d138,abc059fd,7ba90619,83988ee6,35ec835f,c20f62fb,c68e2bff,373bda91,3be6f87) +,S(8a088f12,e8523de5,c7ce659c,107e1929,3d009b81,1fe94d02,deeca893,9d9845c9,4251212a,fb8ebb76,5d2d703c,bc18b208,41f8df47,fc5e44ba,ef7b0f76,7e29114a) +,S(d78f46a8,8f3b08d1,334ed25c,78f57910,716755cf,2bceaf6e,c25b52e4,d7af6d01,eb1855f2,29cb2010,f20b3f01,9cbf1b2d,929046d3,242446ee,5c9526ef,5b06f44d) +,S(1e0b4312,78ae0e27,5e55c4ca,cfefcdd6,f589809c,6faaefa8,3ec0d5c5,f99b034b,f38c53c5,544d3ab4,2ebd74ed,b8348503,fb523fae,9d73657,8bba2881,904918a7) +,S(f03155b2,9edb119c,f411f14,4a588f34,ab02d7f7,c415638,cd534465,ed7cbbd1,72d153e3,ff4fff14,b41d07cd,31960565,31736749,7f9c6495,599d3f06,b8e85813) +,S(f61fd68,3edc7fb4,d14c84fe,3416654b,8df66578,6d0bab41,53a96e39,e5de4c8e,952db567,cf0913b1,885d6884,d260fe4e,e11fc139,f58da12f,d38472e3,d42e5f92) +,S(78310e7a,29e437b2,5cb00e3b,13a4d86e,1ede6d8,4b1f344d,7ee9c7f7,e017dd77,26ff358a,4d5629e0,4c20f700,97c202d4,7030bd3f,43134a51,a7d101b6,e182f2d2) +,S(63ae8c4d,6b7329cb,a61509c2,d0b61ebf,4e6ac94b,c8a096b9,7a4027e1,1f781a3a,a8e954cf,bf407857,d19629cb,338a93a1,af16c929,ef8b96ce,2e483e95,dcd14f88) +,S(948d592a,594b354e,e2d03961,6338574c,ae662f26,ff79afa9,16579725,7cca739,37ff98e2,5ea0396e,89d3992f,773bd6d5,ce02758a,4ee25557,edc867b,2f1876c) +,S(653cf16f,a3140368,50b04de1,8bfee880,660a4161,4dc67a55,398e2d34,46100f45,acaaaede,5ff1e552,6766741a,ee43105b,ea4d3419,3e615c36,49c1db35,125a9202) +,S(46fb6882,8a5edc8b,f24fd1ee,e6a7789,141e1888,64508d2a,f3c511ff,8e8cc782,9d4ae53f,d52ce9bd,6ac9ff4a,e82c2e36,f4d26698,47ec90cf,ef383e01,37fd8887) +,S(5f11a0c7,23ea2a71,7c1face2,b4b0f28a,4ca44208,7a2ae0ea,e24c2005,7138c0ed,5ecaec84,ad97612f,ba7925dd,f126e4e6,3906eacf,6f991d5,bb273316,b80c8452) +,S(b9f49e1c,929bef7a,6d6b6b3b,deb10890,dbf2ba3b,3eba62c0,320bdc84,c2a9039f,98729e76,7e4f8b7b,bd0ecc1c,534aa030,6e1b684c,6f42d15c,2bf6820c,b501e558) +,S(ec8a102,e7d2905f,f273eea7,6c9a4608,965dc2b0,3631d2dc,63c38e72,15ade2c3,d8c899e7,242d96cb,49383686,8f671612,706ffe5c,368ccb6d,be5a3edc,f74371c0) +,S(107f02b3,c351805a,6c92a3c7,742a5259,582c5b0,39cf6daf,cdace604,2be236c6,517236bf,1ab47601,57d74685,8dc724a7,c5c2d55e,82c4a3e,685f389c,a99bfb9f) +,S(f0fb14a1,a5c1c016,d7b8c1fe,a8317872,b10d637a,daf6f6cf,b9860653,56298971,e1d9ed14,d33e65d,4258e87c,7280001c,c40b89ff,b3164337,92fbc547,964540d1) +,S(a5371adb,ea784ede,f6cce83c,ebde068,78a1c80a,ff83b47a,333f9b35,44489459,f5780572,127f5d74,773acbe7,b2a22cbc,7e3d09bc,ccd13edd,48ece3f2,c8129224) +,S(3c031e5e,1b0ba576,13c10971,5fa7d92e,2bbbb817,9603d3b9,99e4fadf,be17300d,d275a3fb,392036dd,474e65a2,cd7b6cc4,79cb40d7,1c363e6b,bf9c272e,ae9c83f3) +,S(87b5899e,685db530,a0969a68,9392080d,f8a1b9ae,523ce18b,2171c69c,5a5318f,6ae3204,e3f0afa5,a2d40b01,a42e84d1,1b1166fa,f99cec08,1f5af48f,fda775d4) +,S(d9f077dd,c002c16d,f9d8eedb,56b7e7b1,ea00d04e,3fe838dd,2ae40501,9b8bf74,6518d51e,503118e2,53cfc486,f70cb9ee,11c27302,ee5512f2,78f21265,af3ffeaf) +,S(9c09ab63,8d15acc9,609ce305,32277f63,7558a492,aefb98e,c9bc721c,f8921af7,b2cac58d,535abc10,8bed6ab3,165225f1,bf2f84a,9fbdf3d9,1caa4b79,e0f6fc) +,S(6ed1ef8b,4c2d9232,5cf8948a,5c369a24,52ab250d,ed0e48ae,1676dc73,9a8a7fc0,c1b7a08c,2cfe5154,fca74136,863da75a,5c6423bc,1c904585,f54ac87e,f6742008) +,S(882e8d70,154b7f6c,279f71c1,e2ba6087,cfc458a9,3079d8f1,3fbd3be2,506928d1,8949b22e,97c41872,9094ac62,e67ac1f2,8b31565,edbb3d1d,9b23a73a,6fe64131) +,S(708472a0,24c86ea0,7119d0af,b2509814,516840e9,fc23246a,a6da139f,b37aba52,9809b7a5,1d551532,a5b378d6,594a3665,3a8b057b,80cd530d,b7a53fbb,128fe5ac) +,S(3a44fad5,9a8b708,4ada1250,d8d08353,769d573d,71a225eb,d87a8cc3,af9fac3e,f29f4551,1250d0d0,a1974bf8,46124813,f9186e45,2d3c5c85,cb0c69fb,4b7f20cd) +,S(a3519dd8,e09e1bd4,160564c0,41c23362,36ba4c84,5899f90a,fcc370b3,cdfb9f30,64cff159,7c910711,d0199faa,6c74d08e,7150088e,34fce674,72dd3ec6,1b881d7e) +,S(5af16ce3,765f5c96,4eb3513f,19d99ff2,bea1affa,d68f829,1c4f0767,5cfcf1dc,864eb5,3bd711ed,4a842db6,b0164a78,511ff7b8,a87aa3dc,1f2402eb,a3815daa) +,S(1ff9a522,19cae5c2,5de1ec08,2641040,18848bd1,37a5949b,4021c2d0,563d54a8,546d634c,122276e1,b5cfb919,a5374309,8f85d7dc,149b14d4,791c83b0,1718e031) +,S(d7a6dcad,72a51ed8,f84926eb,cfab368c,7f02fcdc,aa66482a,c6b6dd6f,43d3e938,9a273cd6,3ea6ac86,5a68f378,e4466ae9,8598a058,6c97278,bde63b22,14e41c96) +,S(154f63e5,5bdccc7d,a696dfb5,2b6cd4c6,3c874b93,1f221282,3f4a8d19,1f32d797,e98f9722,17bca1c8,ea7269b0,d2aa6bc6,f0f29d7d,b340c67a,2df59135,77f76fae) +,S(f6ade08b,8513615c,ccd29095,c21bbdea,dbdbb7da,87ce9991,c27ec758,4c399bf1,98c81be0,19fccaba,131af074,b28a6311,3fc1c5ca,1318e96a,f037ad33,8f234ad1) +,S(9661cec0,3b2715f6,c6d29048,65fec2bc,b786b3f6,943a883a,3e51cb2c,c747637e,eaa1f7d9,9b34104b,74a8056a,9ec739f,cb9c3735,95f0b254,7fe75620,4f5358bf) +,S(e0a5323a,27d608c7,34c03465,f03705c3,480520cc,f5ac777f,53d498b4,655d19a1,1f727d78,a8b8c562,ebc60679,8462975f,63d88eb5,8e7cbaee,836bd629,260cc606) +,S(bab82efd,4646f2,f281980d,5250c6af,8206e95,dece4118,c24ce31d,282f6409,2a60b1dc,26a20126,8df8375a,5656c55f,cafbdbbb,46b7977d,137cd584,8d1fb82a) +,S(389e1399,ebbe958b,1806e9b3,ee51baef,2d7edd20,79e6a2c2,21e814ad,e1eb2631,6c158633,1f3cfe45,9e71fb4d,112a624c,e48adabd,435e6fd5,2c9459f6,ce9ae2be) +,S(b78179ca,c0dc553d,19f08174,7fa4511d,87d8961,1ba44045,8c307727,729d4515,7020be98,c496a50,6e4caa5a,94577d71,ce0a80c9,bb974c97,a6892351,e83e0de2) +,S(867ecd3d,d170f9a7,123d13fc,fd90058e,baa6a6f4,12ecadc9,f1e14d55,ff104306,3c0db3e1,bb91ee23,8fb27689,79f2b543,d698c5e2,fde48dc9,e2535e4a,946759bb) +,S(be71654c,26a1025d,41fd4d08,384b2ccb,db25bb2b,c15704e5,697be2f6,d4eecd38,316ed20f,c37a2dc7,a0af8d18,e4da0efd,2dcdaee4,44b2c1df,cdfaf6af,49406afa) +,S(5a8e1cb0,24d62685,e755b9a2,ad1e0165,41690544,7963dfda,6b6deba9,76e9079c,4c793173,743330de,7a8f4351,68dde95e,3889db2a,91de0a27,d088a222,f11ebba7) +,S(3bfad00b,2347bd0c,f13a761a,d0630453,bb884d0f,ae3d31f8,57216412,b82e6131,ef33c560,94157fec,3f7fe8e6,36d48183,71e3cbab,6f5b0518,96ebdb42,aa545f61) +,S(2447ede,684dce09,c896c2d2,b5c68cb7,60e02635,3b4f2dc7,428191ef,5d2c1961,ad38a82f,e80f860b,62b39e55,44438242,5ad6cb54,d6e7f377,62427f30,1ef7bfcb) +,S(a5eae321,eeb4b9f8,e8807e49,ba8de764,e7685f07,417c5239,9d857d4,8bcf985d,2acf8aa9,f5c1b7d3,2ad5f523,39e65595,17881d,48a7696e,ed3b1023,ab4def1d) +,S(97de995c,5337d938,3cca5b97,3ab05e8f,dc035e11,5c435251,7e575a,e890b13c,4fb2d969,2e308a29,34c3fb4d,c4dcef50,9202505b,cd891f79,e8f91210,e069e6dd) +,S(b8caf25f,a864918a,f803dad8,d0abdf95,25b70f44,546b1f3c,c6b1726c,4438f479,7c51b5fa,c0f56eb3,6ffa8c5a,76c074a,c30e2f36,b069ed79,23663da,c9c05c9) +,S(b5bc868c,1b96e2a1,b24f3001,af140107,b2cade8a,5b1f3443,44521764,ad07b2e6,8d6cfe29,4e929f33,d5aaa456,f545c33b,672a8c07,b214a6f2,38bba367,4938d639) +,S(5f51587c,242a3b22,10db666b,f2414c28,32ea2662,1f13f6e9,784a8a1,373ff602,2c3b260f,ebf20a4e,b853950f,99ccbb9f,1b39418a,66adb427,f5886944,8b46c858) +,S(1d0be0f9,5009a2b5,12a52e17,e044f3f7,4f08e695,60d0dfeb,822b4674,7a2bcfde,c3a82bc3,dbd81877,3783181c,f7b81ddb,a742c3b2,4f866538,9335ce4d,8f3a159b) +,S(3ef0ec28,b8003331,a6621bcd,6ce30ed2,9e0e62d5,b492592c,cecaf4bc,8d22fb8a,fe2c84ad,77d40e86,c92a03da,e43cc7f7,512fd363,98db0133,7d929ba7,a144b16d) +,S(84c29a24,a4ec04da,c76bef42,732a3648,f62f12b0,e7f0ead3,2ef9649,b7555fc5,e7620ebb,283a54a7,10eb454c,f22e49fb,929ec453,38e457ec,b18a7f0a,794f970e) +,S(6bba6634,e75d5fe9,21f40f4e,5f1c177b,a309cd73,107f1e9,28331573,eacf43a8,33ffe08c,42168243,1f086b44,12209c56,5b47fdea,54671a97,9d86be36,fb421582) +,S(13b67af0,51b0c01b,c49a6046,d9e5b6ea,488f6298,9559dbfa,47cf1674,6def0150,d0e8914b,1c1bf0b,5337cc35,d84acacb,a54add59,2321509e,e29f1d93,a0237608) +,S(161221bf,b5dcb62a,ebeabc94,6ccc4a26,fdf779cb,63640720,2c5cf13e,93af9300,d3fde2c5,dd9d6df8,70a66bd3,2b5213c,d1a212db,61aec7b7,2a49b7e9,2cf83b2c) +,S(30c5360c,c62f5846,3af60277,2b52201,7eda1fbf,866dc3d3,9a590bf8,e2eb508b,c8e91af5,9b9a2b,8837dbe8,edeac615,6c30e8c0,1b7679f0,1aab4a81,cac44efe) +,S(c0659a7f,dd9151f5,ea67f38b,7249727e,640364f0,87371007,a38b800b,ec3576,a6f648cb,6fa131c3,5458b5a7,77440361,8c57faae,1ca33ded,648f1601,a5cf47e3) +,S(3215224d,b795a892,446472a,8e1ab1cb,7d50fe95,a6844be,43e2787f,2679386,25750662,97f28d86,690c7eb5,92cffc5f,faba831b,efc3e38c,c5e501f9,358a9bf0) +,S(257654e2,a87a7048,3bd15445,da5c554f,fe776264,998975b3,b19a68a1,a1871145,fcb9319d,c67f14f,9d7fc4ee,fca05b18,9efe2430,96691043,9c28c88d,32636ec5) +,S(a4786a01,e504f154,f3d7926e,6e6908a8,17c8a66d,6db33e80,ce98b17,82f1b49,576e4d42,fc2e4f9f,e81a2967,8b7cfa7a,7f86e8e2,662afbfd,b028612,d323a2fc) +,S(5140f7a9,260c9856,4493ae62,a8af369c,90b484f7,e82e1e38,3c5a59a6,62db1e71,4e5172c2,b0879dfb,63a6aa92,cebf9153,94ddcdc0,985261fe,2f64714d,350bae86) +,S(f559d60c,f0f50b7,9c7d6d90,18a579d7,b781fe13,5210d342,31e6cf32,4ca66dfc,12d9ceae,8e5852a5,ca95fb56,abda8e42,5f1f05dc,ca29e08e,2052bab0,8d4eed15) +,S(88907ff5,fa2d01b,c970e0be,53a05354,a4fb73df,9a337857,cc869a1,b0547e7c,f5853b56,cc1211b6,427145f2,6fca6aaa,5b26401b,c00db73d,ab9157b9,7fa3d2f5) +,S(cc1e4bf6,4541506d,8e91fba3,1e1d8ffb,c8ac82f2,4def27ec,fe4b3ea6,e58c45ca,7617497d,a875183c,684585a6,306956d,1efcf1db,2753fb77,5902a63f,38d27592) +,S(7ee2c437,ef744210,5b6a6f9e,f1edde01,6c39557c,93454dd4,eca10daa,cbca99d5,21754734,5eeff5d9,f30503f6,9515da57,3e6d7b70,dbdee5b1,8274968f,6f3772d6) +,S(72feea2,39f9b4a9,a3f3af3e,f6bf420c,3605ba4b,b42da90f,6539e05e,33e2465c,a036ff,df7adedd,c8c2ad9a,d029428c,85482cd1,6b0cd20a,36d2aed9,e9f553cc) +,S(363da33e,94a0dd00,669b627c,4d7a3a6f,c8a0cbae,2641707c,938c8f5e,99914a,9bda9a77,ef1307a4,e236099,c1506f47,a2f936dc,d478b59c,3d0e98c0,7af58bd9) +,S(76540864,c8394290,9af4a48b,bbe05fdf,93af0f8d,1ec9b6e1,a7e91860,f2c133fe,f06923d7,738ceb02,2213a236,daf3d6f1,b3cc98f5,a5ed4845,83ce5c3e,54a3ecc2) +,S(573f0821,7a318a9f,8bd945c1,62e9a4a2,51e38189,34709606,c94cb401,f2b239c4,7e78e4f2,7de69e4b,948b105f,ec3ebd14,496cc128,d7c73fbe,7ca1d53a,b746bef5) +,S(8537981,89234b93,b5009656,f57e5ed6,f90d2184,819a5467,213a934e,22b01f5f,c94a79e8,cc990998,e1c69bbe,89ccefa2,9281fc0e,c99fa820,ed26f01f,62e475f8) +,S(53388512,29890336,3b1ccd4c,1cf0ed85,6f00a30c,78004512,57f6132c,edeb6041,d56e2a52,f856d1b9,a4f8feac,dbc9a844,dd22f55,469bf722,fd78b44b,6983334e) +,S(41ede48a,750ef7b6,5c49f913,afb98961,c5cb478c,39c4c85,2f33aaae,b0e4796d,d74c7bf9,4b6c4680,18892ea7,f04b53c9,4bdfe9c,d1a4c65b,8de2409c,32559de4) +,S(97e931f3,6596f736,992eece4,eed6a9df,fe762139,3acffeef,b6dd3b53,e5f08148,3da89933,53b9c52a,9e76ce7,7404fba4,39a24614,b62d42c0,9b660e2a,14dd5f1b) +,S(4caf4eea,3ca8b104,39847633,5252b37b,5c55b664,cc4979a5,4db00937,b222579d,c266732,a93bdc0,7cf88580,771d58a3,45a19100,4c0d1464,62171cd6,291607bb) +,S(857c1e90,5b3483c3,3ab24ee4,e51e3839,b52960f8,21a8706b,b7ea7c84,9e7b1ca4,8d6e9d0c,7a578771,b358380c,4a2e6bc5,1c6b09fd,e9b40c01,be5915ba,fe69dd80) +,S(410ef70b,e0dbf7ad,a68fc124,8dcba3cb,10ec39a4,9686b22c,1e7ed2c1,bcb5a182,d26e9ea3,331a56f5,cef074ae,e0b4a5c0,e6925d79,808ffdb3,b7cb9423,247f9e94) +,S(8caf87a8,c831eb10,817e5ec3,7aa431a,89dff6f9,a7e63f7a,30710419,5751029f,3f0bd6e1,c8dda6c3,c8e65ba0,7c3a4649,13aad207,3c5da058,bfe90872,67c8890a) +,S(eedee2cf,e92d2483,54a6a8a3,9d314fb1,b35ca18b,ffb9b779,8c16d5fa,fef10e8e,cb515ab0,21f0befe,4b31181f,3f9ce1dc,9fcd12c9,6793d624,c1eb35aa,e514c4c3) +,S(8cbf7ac7,1e77b7f7,7bad6a4e,9116db52,48bb2a75,744d4312,f02eecaf,2aebcee1,765c4b31,f67b26b5,3d17f97c,30d09636,b6f6697f,db6b799,169a2c05,779b7320) +,S(70cd45b4,cd449aaf,34ff0627,f0e01122,715dc6cb,98642dfc,c19fc672,54995db2,82e59456,761497b1,84320eac,3ada16c6,ee184ad9,10b60c41,20f0c538,2f535c8e) +,S(cfe217f9,783fcfd9,1e79d558,e37bec3f,50e5146,e0f442ea,702fdfb,6c7c45d5,537cbd0e,86453fbf,c57d70d8,78c7c3ac,da225186,17bb4dfc,7b6f7079,95c419d5) +,S(9826c0df,6486bb8e,2bf3d4f7,4cfafa71,ccabd2cb,12bf317e,651790f,48579d52,3dbf3586,f86d6253,d3749c05,2fc36d16,ae3bb457,8d4eeda7,34f172dd,b9c57342) +,S(ea18fcef,381b4bc2,c6b3fa3e,3744e9c4,a13e13df,576c8e74,d0f4f596,e28a02c4,c3e6bf9d,2bc445d7,87103c7f,e595a30a,41c9aa4,c41d1874,d7ffe69f,ee09f397) +,S(6de2c465,c09d5a54,61c618be,c9c16ffa,21b82e5d,673033a0,e88cf90c,fe6d8f4a,367acbd2,8950882b,428a8199,d3b21f5b,e1c4ccb0,3280bf5d,28b65cf2,bc4ee2ba) +,S(d6286e13,ee10c7d4,5ae50f80,3d1a5417,cecc9c68,9efd2847,6ffa73f,1a183f4e,7ab92bb7,55204e89,c7e3ca41,1c829e41,e965ab93,57db88ad,37f13e93,6aa56f56) +,S(7526e6fa,ea67460a,25525962,fdb6f209,73f0c861,ef3c364d,fce83df7,23c3bf58,2c76a8e0,d5a8611b,d3274411,cb323751,25a21fac,12e35ae8,f008f48c,18e3a674) +,S(fccaaada,56d64a3d,6d1d5d89,719f51f6,e737c803,893e8b2a,b785070f,8879308d,8262c566,3facf792,efd750ef,a969fab9,a2ee95d3,fe6e7d7d,8685c6a8,479362d1) +,S(b60d13b8,eb8a5f43,cc448379,7a397847,bd6d91a8,b3a6888a,fef4b115,59b57fe3,abf73ebe,5d2b8585,76104acb,ec885f1f,8405b053,3755b8ae,94ecf838,60ebaa54) +,S(332d4b0f,4a0dd872,ffd6d2c1,4379f925,4250fd66,dfaa2d90,127e6ce8,c377f8be,37f3143,47c2350b,f4333d7d,9e0b17eb,110dfacd,d87ce354,b7887cf3,1bc6232b) +,S(e99b2ee6,2ce6d633,1e0e880,37bd138e,5d021620,b555b94e,52def87b,4cc5788f,c9ea06fe,dabcca9f,6df1df26,8bb3e550,e2858dc2,b91a7c6d,2c048416,4e5546bc) +,S(89ead438,2a977aee,7fe692a1,d7199bdc,5af24191,e80573b1,dfb056dc,49c54353,b572ddce,db2d776c,2a967f70,6b7010ad,7fec43b,ebe5f19c,2de0f9af,d9994ece) +,S(cdad98e,529b413f,b46f6fa,98c74058,4777baff,b090d488,59587b4e,d598268f,d405a95e,543e639,470f10db,821f8786,ceabc281,788b0cac,3efee830,bfed034e) +,S(5096ef41,1cc1332b,e67f69ad,3cc3140a,c269849d,5e0f4f5b,e41290e4,86ee2cc3,7f33869f,ccd4b06b,ed61f312,30a7b3f1,49ef34a6,f2d46bb4,18c45343,73f8268a) +,S(bc0bea70,a0966734,8607253a,2c8be987,8c4d59ed,638ecdbb,8b9f60e2,a4a5be61,e19111ac,3c2b4e0b,4a86129,e6c4c275,54d1ff0,29ddf320,873e20cd,16873b78) +,S(2d166569,ac8f5a08,e0674d3,8aa7d747,b024bb8a,5c8407a7,dc451403,87223ec1,48af1b38,62216a5f,12a7d92d,4eec4e59,445c1587,e22238f7,3dd1bd77,e19a597a) +,S(b8cbcfae,bb8b1150,ddce37b3,26ec77b5,2947cddf,e3cf1d40,f27e8b7f,8145ba30,d5234cab,8b2ad2dc,8630ec02,2868a1e8,ffc2c3fb,cd5f5a05,62b6fddb,34823ca4) +,S(fb396092,563f12f4,ddddb9c0,f1ac19d9,fb2e3ebe,608eea22,7a9aaec6,dc960ee7,65e565df,fda1a394,3d27f661,c2eb01e9,646e88bb,6943953,58c9e62e,c9823c45) +,S(1c6e9d76,b8dcdfa,90625150,db2274b2,f8da06ee,21000225,80affa54,a414b297,706b450d,9ab0b0,5466edcb,bbed8b10,85679fb9,78d74057,607bc2ef,a5666255) +,S(2e043dc1,46ecff0e,6e566b13,d985cecb,77f55a0d,555e0277,dc660351,b9fca1c3,1c4f1ca,f75ceaf0,a051103b,90a7abf,665da500,2aac4e14,28a71e45,726d8398) +,S(5ae17b0c,a1bf8e90,e663ee12,d97d7855,9549f9ee,2d89592c,b4b0bc9a,585f482f,bb0df734,931700bc,73c6966c,1e8e5a77,a71806ad,88d731c7,16f236bc,b4fc8111) +,S(fa8bf342,6a24c3e5,d3c11c46,e75eda94,fca910df,c906a71c,1b08f468,a7caafc4,ca69817c,db07fbd9,e35e6c84,65b3cf05,4e2884f9,e57c0043,5dcada4c,2f5954e2) +,S(2cdff1c1,846dd7f7,b9df67ae,a3f4394d,1e1031b5,de7083cb,f905afb,991a88a4,4bbd724f,3827e42b,7d0530fe,de304711,6ea0d88f,8e564d3b,507ff3e2,ef76f39e) +,S(6fadbc00,55ca49ec,afdf82b2,96504f89,c5bb8291,ca942df9,5f5b3322,9d3b6905,f7671184,33a8326,a56f9472,8c411917,911a3053,f593e868,c06f57ef,54b7df62) +,S(c514695a,2f26aa40,dceb15f5,5d5ea8d2,6f4a1e06,12591fca,2f5a00e4,b8dd0841,5056ba08,2e78b06a,90937567,73fc4ef1,652905b1,16d8fa6e,8005283a,b113266c) +,S(83dcb8d4,93474f85,54c368dd,3e188c11,aad758ee,fdd1f064,66c11e16,cb7ff933,104e5b3d,4e57192,7c67a029,e119e79f,d6ed6fb,e61e288,e9f8bd84,630a53c7) +,S(bc65cf8a,cf9a0b26,43b69089,565f9a9,5f9ae882,2e2b1127,a8bcabb1,fcc8a93f,fdf9716b,a31ddd06,a080ac90,f6699b78,23ff28c1,155a79c3,ae9c292a,14fb2143) +,S(48778fa9,520b12a4,12d74b47,65ad37a2,695e37d6,4070d53c,1e09cbd2,4c9f140b,cdc64805,bec15940,387b4c02,bc51b469,446dd3c5,b2e35b39,fbeca21d,769c373e) +,S(9db2c42c,6a735886,8edc8831,2ed6873f,28077bb5,32d186e8,41f67ddf,a301dc25,e01cb235,ec9db45e,e1193e06,46a325b0,aa0ac5d4,786d0ac8,56990262,bfb3a0dc) +,S(cbcff58f,62bf025a,43a1ef44,256b27ff,37c6c8f,2496d2ae,fb280734,9cf8da6b,4fd99ed5,2da32753,3a9251f,e1e136bd,28f5331b,c1101da2,818de6e5,3bb1896d) +,S(9d520b97,87e68c45,817a12a2,93b1df96,70cbcf3,91788270,93367253,6f4f3639,615e115d,2b539895,d91885ca,a325625,41fec07,9d8fb6bb,22d9ecef,b6bc0f14) +,S(c648ddc4,57c2bc,bc23d182,8cc6d6e,4b0bfd3d,f83b2e7b,3ef6a341,9225dbbd,ac9a863,38dd1966,9bb47e99,e67a3d9d,ec16fe61,dc17ecaf,209a7fbc,518ab3d8) +,S(ae11f835,ea96f767,afde3f1e,79221dee,b80ead29,7b45e29a,7e596461,e82137dc,20453bec,b65a62f2,8996e1b5,f1953acb,4c5dfee5,4e5f9a15,c2a2178e,4c452596) +,S(a35549ad,56f8a64d,d23b9293,d8ef5128,699f6fe,365c0fec,29280d0,a364d46f,c84041cb,a38c9981,3015ffce,17ffece0,8f55a292,3c64868,f11cd6e1,bfbbe75) +,S(75877029,af5575e1,8dbfaef,37dac89a,2aad8308,d473a64,9c347097,e35f8077,843b0497,d19851bb,ed243762,177cc69,603cd674,216cd65b,2e5a85cf,5fc2b8b6) +,S(44ee3168,ca7ffd50,c467b1f6,83e532c1,ae00b9e9,f2eb1ca,917607fb,32d4ea99,10a20ea8,120b5a13,beda9f9a,9110eef,22564e3,9b96c141,1e73eb7f,c92c3270) +,S(9a013c89,8dafaf0b,90678f92,f3bb98c9,461e4595,42cb07d6,477b6f66,7a5337d4,11f8485a,c96623a4,e6c9b773,f5cd9fcb,fec396f1,ff53205e,8774ba1f,da3c2a73) +,S(b45e28a1,44d54182,20819a61,5f50b349,3fa12d17,2b8b5bea,83e73d69,b6b47d5c,d987db83,ccdacd21,dfff1dec,43997253,fb1c2092,95bcae4,5b76c306,92c29f4b) +,S(9e7d6a53,49845888,a8865d07,310192f8,9a659205,1d2a603,cdc03d35,a641a0c9,ca1774ed,ace29fa7,f57e5690,b4d1c0b2,ce5f1fbc,b21fb323,3c001498,54f462d9) +,S(113a1429,be1b613c,567b306d,5805c163,e433a940,8ef14b01,b9afea43,54991f47,225ee5fa,6d26cbb8,49191ec1,a51e385c,321e801,9152a3c7,50014567,7b928697) +,S(32ec8dd1,2cf85df,931e2597,f6b005e9,6d6eb0a6,d0dd7964,77655d71,418d9181,fd718dd0,78b3e4a5,9bef7f4d,9c430764,3423bc05,e1aa22ab,dd7bbb,aff9d8b9) +,S(ada43267,f2cedae4,5e1a5f1,46151f89,b145db70,ef477865,b1218e91,72e9246e,d148bee0,4d1f4d29,f9f15c57,8b047469,16e39686,c2b2ca54,1f3e0d4b,247cf82c) +,S(818a0abe,debd74a6,91fe662b,edba1a52,65f5ca07,2017c6bb,bf7b9847,95bf0cbe,e7b2d06a,1872c73,6988da9b,ce273b7b,ac0f03b,90903bc8,da719ce3,89c0a53) +,S(2f49eb55,ef77da10,804c1a1b,f596f09e,ae76026d,f2d12f14,d80be810,7d0b3c94,6a225810,2f1118,eb689aa1,e6d4ced1,1a79036b,802caffa,5694e383,3e038b83) +,S(7fe54d70,54cb025f,6bef8029,bedbd15e,bf66d5c7,986c678b,a5cc5353,7afaf74a,fdc617ec,72ac6632,6d16afb6,69188554,a47a82fd,db757695,2296c10,b4ad89b0) +,S(84f59366,2b8284a0,c9db8675,db9c55d7,411ec9ba,deec1319,56aa3f75,8eed2689,836e65d9,6bef6ed7,825119c4,c4511c89,48042592,f41eddfe,4de98e83,acd0d88) +,S(c90aa830,1a84820d,e06eb6b7,1ccc9bfc,a46b1612,5e2b5f52,da0a6fb5,4185ee1b,ea86d7c0,b285d82,9331e05c,d99c58a1,cc213449,d9226efb,16135237,f90dc8df) +,S(ffb4d576,abae49be,69f047ef,636142a9,8669b2ad,9cfdcadd,c057d96e,192ef100,3740cd14,b2d5e018,33a700e4,c80f6a8d,3d95966d,a5238120,80a9101b,b7ec6c7a) +,S(408a8d89,67ad6bcc,eb7ebb61,89afefff,12c24b5d,2c33ca3c,8b78fca,cd403de0,3b761fa6,378cc42e,a28fb66f,f1d8171d,4acd3557,fd6313a2,cdcbce47,e1caecf3) +,S(580a33f6,153109db,fdde27e3,f1b0ab04,6c80c04f,18326712,3e3482e8,7c53b787,bb8073c3,fddc81e0,e99ac5ae,69702ff6,f70a33a3,5639add8,1d353c0,e428431a) +,S(2d310a94,7054ccca,c46b1100,88349ad1,4c7860db,38c698c2,505e789f,8344130f,1ea2069c,3c6ec90e,9c11be0a,913d90e,2df1875a,2e26aa9a,5d28dc81,3e3db697) +,S(2874282b,e7b11cad,3f22f2e3,ab835aed,5d82b424,24697b32,985231f8,6a7aa450,286332d,b2e1b922,222e3860,e41d90ba,cf39ce03,21f26b74,cfa23171,f0415c42) +,S(af9e779f,f77bf3a0,10af5e41,82b77981,6940f85b,9530eea4,8b36e401,eb6231cf,9d3d4350,dbaf15c9,269d958b,86d6b88f,4deb7083,ab1ff389,c13f7541,98e84c67) +,S(445edb8d,515ca735,26fd2757,73b2778c,bc93ad8c,a661b35,4471d035,688c1ef3,94917a3c,fde762f8,3b774361,1f36a13e,e94b759a,455382f5,55d64f25,7090dc49) +,S(e010c20a,370d5306,f89993c,f7ec34d1,3e76feba,2b6612aa,dc75012,701811b4,cb45172d,280d9be0,52d57a21,4cb3cc47,798a6426,f215e3ce,e3af64ff,106fd8af) +,S(d34277ca,4625c7e5,b1134080,cfb44cd2,ef548b3b,67bb73f9,eed42352,ae5af8f9,a35f5779,7a628f7e,bb37bd60,9e3f44ee,c391909a,ce27ab44,1ece58c8,d7e4a4df) +,S(be0b9399,3bd54241,b684977f,ea4733e3,d96182c3,c9fb1c5d,d665eeca,cb1d628d,37904353,744ed26a,df76272,8ec41898,13218b0f,5fb09da,dbf5be84,fb7fdb8) +,S(308295f1,ed4a21e9,1ed9f48f,d6b42829,edd9bd34,e812f09d,c3b2c319,fd8f3980,6835c34d,5c360e1a,49a14c31,4ee4cca3,797d434,897c4181,a8ba5d7e,aeac0230) +,S(66cfeb51,61aa879d,fd791604,e10f5fa5,c2e07a5e,66a37b41,2b88db32,dc495b1a,a0e07a9b,f8a2fc01,3e229602,b483bc26,5ffba9c5,fb6ebfe3,f9ff9c9b,3252a1e1) +,S(f42b8fb,849cea70,4cfb3d9d,334a4a11,bf7b3aef,9b55f648,ec885a2d,6fe39ba4,b60621e2,a36d4aff,9f75db65,dd381ae1,f5b0fb4a,bc9f19a7,36d160a,61e8b22f) +,S(429c1785,22906323,2ecfdc46,2666f8f4,c57b7fda,608cc8e3,85a31254,bfab034f,e190ba59,1a868f57,40bbfcb0,481aa04e,50b3969e,abc941f1,8e69816b,2962af35) +,S(c8939d7c,94789a0c,b95e4237,fb378ee4,9895b985,6fc67d7d,fafdf7b8,debf611,143aff62,6e94617,c36d05e2,3062d2d4,47feb77e,9b15e2b0,4370f81f,1cad3682) +,S(5e4155bf,56303243,24e15ae4,142ad81e,2b82aeb7,8dc3ccfb,1d36f3cc,4398ef94,6801f527,2325a3b9,a9f3807f,216d7425,b9083364,203f3b75,ed6f4ff5,eb5da55a) +,S(1196b198,53f093be,7bbfa851,a7114e23,eb01c530,6078965b,5e9dbd3f,8d1b573,794cf0,54bfee20,d80dea44,d183db44,fe79dced,fef0a97e,5c557fd2,bc628795) +,S(d818d920,74bb737b,7db8fcd0,168030e3,39803e5f,6b76ba1d,ea836ed2,c73a6094,6ac7dbb5,1f63118a,80cd2aac,fb5f086e,ff15d51b,fb4ccafe,9d96f179,176e504) +,S(c0276b4b,f5ccabe9,ed9a433e,eae5c989,7039042d,b273ed88,51db464f,b06c6204,489270ae,6349ac81,d481a582,a8581520,972593d4,9b3facc5,fd52efca,24f4756b) +,S(94b2a1c,ca23cd30,eb9b300b,43d0b24a,b898906,1c4d8a7a,d3343d7,cdd83307,47c5ed56,b709bdca,5b960801,58338b9e,e6e74683,99a50640,6075302f,9e481df1) +,S(f2aee379,df2bf54e,e4a1c385,c28fb64,9f157d2b,d345995a,66868876,ae7d5108,ae766bae,9c90a1f9,c9079d35,1676eefb,19a7bf8f,bd56a311,13f2dd56,35c8ef08) +,S(fc8fe44f,3dbdc4b2,afa9cd04,3601c6a9,e81e4da0,456ce222,8306bb85,ba9833ad,6a321b78,a98f52d7,64330a52,ea5082b9,2b07655e,ce8c5094,ce307538,6d56fe15) +,S(d38cdd79,19a4f3cb,fbdaa3eb,e2ddc10d,7444d04b,5830eb7c,5b8b464e,4c255c14,6a0f1ef1,823a2fdb,94a311fe,cdaebe7,e1904095,1ddaae6f,1a565551,2c8c15fa) +,S(ac02e6f6,13d8690,d0c71943,a26b5e9f,916ea119,d7773f4c,14247538,9a4b41f,eadc499e,436b4eab,bed9c5e7,68fd2f67,72659819,437708f3,99531ba6,c05ea2b1) +,S(3948b1a,c36bd462,ac7b5cd0,7076f9ce,dc6d1a75,1ff65177,d7d9b701,d06419c8,ca91fceb,a01c03c7,ab141ea7,1cceb61f,72d5d18e,77964bd2,732bb95d,191765ba) +,S(4c7dbc8d,21d600da,6dd4c0c2,cffd8842,ea9e73d0,5dbcb554,35e31971,7e706e9a,86a77f44,b87b7abd,29c2f412,97f7e859,6db46ebd,8cdb442d,35dc17f5,278230fe) +,S(75228daa,b3825e2d,9f8c2f7,a0743b0d,b7dec731,d9df5e06,98ee11e4,8244f623,963c2987,e122f107,dba37bdf,ed282ea4,75e33665,260a5aa4,51021a88,780a1676) +,S(aa3a86b3,2ed2161f,8f6ea92c,71b7a47a,b584a73f,102c0147,632d2c2b,e80a579,b8fe249d,2b4ec64f,dd345d3,210244d7,f7e517ff,7722c2ab,bc4601ad,ae4e6cb7) +,S(702b912d,9828039d,a58b992f,7d6f3af9,4c03227d,3d3c368a,bcfd3164,193f22f7,25892c49,3a32cd78,130a4e14,c714cc7e,b576ccba,396ad36f,71de5c42,c6bac387) +,S(4ca10c88,6d44bfa6,2a221dfc,10c57011,269f703b,8f6f3567,829d3b5c,9c90ba75,3323eab2,9282e358,d4de6f27,72d77db3,95b04a22,7f0374b9,dac3ecfb,4ffde3e7) +,S(1619ea36,30da6972,2caa436,2c174efc,f3601c35,2e946d34,2da56738,92b9c325,22ad140e,13b8679e,a6d88dd6,148ded7b,a1c12697,91c7ede4,b98a72f9,f0e881e) +,S(21dfdc38,18debfb4,9cd409a8,a302f8ac,64d589ba,b65074c6,365bd398,d34d3032,bbbe7ec2,e835204,a786ddcd,bb6026e6,106d193e,21725c45,79eb17ce,784099a9) +,S(60fbe5dd,802f1ab2,cbae99aa,12cce0ef,36a472e,bbb1bd1,c0ddbd40,85069fe1,ea83b59,2dd304d6,82fd07a6,85408f0b,99feb1b2,b78b1316,7f9be524,a7349dc3) +,S(c804b465,fdcdc5bb,a848ee97,559405d3,dd7c2479,4dc4866a,e29676cc,1e147f96,aa4a2eb7,1fd82b70,ebebf43a,9b828e4f,fb10cca2,cf9bb522,3a7eeef2,5badc5a2) +,S(77a6e3e5,51373b55,dc51b338,3c32f6c0,5c74cfbb,3192fb7b,d96d9d3d,d50336d9,21d2326e,86efac71,48e075c1,ffcd573,5de35d6e,3856f2b6,7d358b6b,c6f047f) +,S(86ddc187,4a72eed2,666706ff,26a4ec65,a89d82cf,64e3d8c9,4731d62d,d5f7a5a,51f7cc40,ce45a123,a9217816,ba3e6dcb,40507b5e,4cde95c,443ce5ab,4f0cc29c) +,S(cf49d0f8,db8eeac4,6c108675,a155e327,8c80d5ef,84ea28ce,4a596f3f,c7d3beca,6ffdc741,ebae75c3,ed82dcd6,a6191d68,7cdd64a5,c3146d41,235b96df,420086fc) +,S(1d75ef89,57eb6722,2d60e872,1e126cec,c9f5c851,5fe18381,518cb54,b75875f4,82c5dfdd,9288561d,dbe4ae2f,5fa1d429,c2076625,7765b3e3,7b99dc66,b78dbdb4) +,S(bea4b735,f62e6122,9d8d9466,69503348,5bb5b7e3,c6ddb6dc,a292ca89,e7ad0689,90e3278a,8ea9e10,e7d9a451,7b7c01f2,7cda5836,f52a011c,d8e20ad3,7a08901) +,S(e85b0ade,a90e5170,18d0aaa2,75d5989e,577f5cf0,b3acb728,1af396a9,6169f2bb,d42970cd,3ca175c6,3916fec5,757b7f5a,30ef1269,d4083358,667f599,e650aaa8) +,S(d97592dd,24050526,e41ed550,fcf7cc2c,82ca5584,c0228f5e,3ffe42d8,a3928934,eac6c169,6bf01c3b,759a3852,a0236f4e,4de0db45,26f2d26a,2174dc4e,fe2d102e) +,S(e023ea4,50b620e2,378e60a9,a8c65778,fd772575,5aeb53eb,edd35ee1,666e00e7,4e4ed850,6dbdcf89,b060f0e7,8915f506,6bdd544c,e37e1e30,6a966353,6894797e) +,S(615b5c95,5cd4fd6e,a581b9f7,e6a857be,a7c0ec2d,c302dc9c,c8304ec2,d935e5a,c221c766,2602325b,bcd845bf,f5d754d9,46fc8074,f36a78d3,6e421b14,fd5f4d46) +,S(b1dfa434,2e12021,b9ce34d6,1391bdb,fa2a0d85,a004985b,c2cc7358,96ec13a7,fa3d115a,8162f828,1dbb7e9b,6f818b4,13c2a5cc,58993ead,d103ef59,9748438e) +,S(b64b601f,afbe47fd,ac675b83,5e4ecdc8,68c7e1ab,32598974,36ba3c9c,bbeada7c,af78c039,d2315746,feb60662,462dbc9a,652ae90d,573487d8,5ea52374,e9dbe68d) +,S(5429d346,fda1d9c8,6c5027d4,e94d7565,b0981f23,52715336,4a0f3264,6bb579cf,5b8ce197,dfecd92e,390754dd,2d74636e,162d6659,51464f37,ac696a32,995b1880) +,S(890694f2,56d6718,b040bb78,1041749c,20e21669,f3787f94,ff954b16,2a6f005b,d966934a,890dbbfc,bb90d7b2,26afd0f8,7bf505b7,31b94df3,5df1c141,2d753341) +,S(3ae75292,cd0526b7,fa6987a0,5fe060c8,269b5d1e,44a2867c,cb92c2b6,743cc117,10959cc1,5a74a82d,8f7d5416,a33256bc,f004eee9,87b82871,d22a2dd5,f2416a94) +,S(9d745a9b,ff717d55,5a353fd5,f1b9fa1d,347bbade,8a4abce9,3bb5dc7f,299b0707,cef338ae,d513c60c,7304f615,ae734de6,10461cff,ade0f69b,bac28e6,7ede8134) +,S(699e9c2c,8130c939,105ce7f4,f922c060,abf1b896,648509f0,aaab9519,d144f166,882b490e,1fb9f944,95d14583,83809d69,bfb7da29,97d244a4,38da39ed,7c3a19cd) +,S(e3a01cc1,dc520509,a91ea704,bcbfe298,ae79b4a5,4f433550,3884a1a7,85591f87,f3c641f3,68d19d57,fca87e54,6ddaf495,2480a891,37490b96,6617fd4,1edcc64e) +,S(5d150dc3,d9df375d,46dd2362,34498514,cc7ccee6,fd8ff5f1,ee5ee38d,6ac433d1,2c091250,d2e2ea0e,7d80263f,18401092,399a83fe,bfadc061,a32316eb,52ab0316) +,S(49fb9761,bb12ba3f,644aea5b,5e011f80,ec477880,58a6eb16,c100c5c2,e0996a66,7134220f,b7208914,58499caf,21654c5c,d9086b1b,92978370,89cfd06,d655e23c) +,S(34c55eb3,a4512dfb,de799021,1adcddc0,bdea055a,1f4f7b13,d530c945,2a44738e,258cc688,beff401f,9a910e26,d0c979e5,fdbb695,323321f2,ba39a4c5,4f98fd16) +,S(7068fcf0,7c3efe00,4ff7b82c,2af0c9cb,9b1c39f2,30dc602a,b2508d42,30a3a98c,e48730d8,844578c5,2e7657bf,202b5df5,2fe50679,7e2e1d77,aaab197a,e37ef1fc) +,S(e1d8910,a987aaa1,12ba1f95,ae8e346e,f49e5254,a2b01909,f73b874f,6b355a1e,8ad0311b,cc4947d8,999c2c14,9fd5c59d,d4a56a20,a0b90235,d7bab907,fe3cdb8) +,S(92509b88,ba8631b,16c6b2d,c60df45c,6bdf06d8,ed4dbd57,c10ac3cb,72d2caf2,3ea47a31,633b6299,a9f95651,b95f1411,ee6f0f33,35ddb8c3,3978af58,746a3ccf) +,S(90fd402a,e8d505e9,96aec994,db7d29a5,6446471e,34dc7512,6a356ac,1a66a519,68c0ff7,bcdc36e0,ada31816,ec4f0e67,9a6dc658,46aaaa62,e1958ba5,bada3b6b) +,S(b380ecb,7d9bca8e,1e35b0dd,d412b128,4cbc77f,c66c5f5,eb390d9f,a9400704,fd2a2092,77d9b783,9df69a7,229d55cd,18fa71db,e1f5f8a7,d78f450c,846a01a4) +,S(40e2e041,aecbb6cf,6f866140,b6149257,f3d029ef,ccf2a752,d3ef7b0,32b83f0c,122710a5,3c83888f,54e26679,417f2329,e1eda641,8e525326,704744c6,bc2291f0) +,S(8e609fc4,3f4e30c9,3893a38e,9aeacceb,7d254eb9,f77b49c8,c99c20a8,aaa03583,947c4c6e,9a821504,7f259a86,b1d70378,837fb57b,e0d696d,66a3dc3,7ac1a3e8) +,S(b871c5b2,2bfe9fd1,6898d00,1af89a8f,8d9c9324,52fce0bb,2ecc0835,f435f5ce,e302e5a3,ecbd0ee9,9f87226b,6f038003,507fe8ee,60b64,cd656f26,8f1d3078) +,S(526daa7e,39e891a,bb524170,96fff4d5,c6f80f57,cce87f83,2ac07cd5,50841682,69f04499,4fb7130e,758fd397,5ad40f68,c477bc9c,cc5c5f43,c5f5a554,b3d210df) +,S(c15c244a,514e3057,ce67cc07,3fb66fa1,5c031f51,8add9ba2,edc12f94,861ad25a,db1021ac,ecc3c897,8de34780,bbc9a8fc,16449ddf,5cb05b90,1ba39598,72846c50) +,S(37e20b19,466a038d,b3b63873,50dd3dc5,d3494876,cdb7f344,3e234173,eac1d388,b80a24,546541e2,ecabcfa2,7db57aba,5ea5bb79,f69ecf25,2d68ef63,fc3b89f0) +,S(6d091f28,feae0142,b311cd81,f57958a5,6a0da5c9,7ff5eab,c598e4fb,564ea528,48bc97f0,4d7ea6c1,38189719,6c85dd22,6e9d9f7c,5a8ba74c,27c29063,fdd07906) +,S(3bdbd416,a76efb56,ed01664f,b819dbfe,d3e545cd,3edd48d3,5ca635e0,bb54fd71,a6954d72,3ed7253f,ef301621,91fbad8f,9a63a893,82b98d26,a6bf36f5,2ceb0639) +,S(cd4e36a9,f111b55f,8c05d41a,4bc33ef0,c4109540,d9871237,4018b2e4,7e9d61ce,478dfa16,52632e69,6ef95ec,452df807,abded9bc,264e6dda,df0c6215,454e3e57) +,S(f23cc4a,9c59c0e,3bf21413,170ac67e,24845771,2e77f1c6,272a9f11,2df24efd,e933bc72,99b6effd,5cf58fc0,72f7a466,dc71cdce,74a3d3da,9ed5e7dc,577fa4b2) +,S(d98c6585,8c97eb35,b6846840,cb6db623,d7a349,435d4989,31a695ad,ed3c33e0,443fea24,ffe8825d,eb6c953c,70627395,ba13781a,164f2eb2,a85b862d,e3114990) +,S(2269daa6,c055dae1,b6d2b9d1,6c74a9bd,65325ddc,a249085f,a946e66b,6c744225,159d01f9,a61f0456,b08f0fe6,a42efdcd,30e14ff3,bb47fdee,2352dc9b,f5bfb3dd) +,S(335044fd,8eea8792,8de3880a,546b01ad,eb4d75b4,23ae7ba4,87740c03,167c48d5,69e503f5,26266bbc,63b0f2bb,757b3be8,cfe1d9c,e63d8226,8187a309,ceda1cc1) +,S(ee7a263d,86f9a83e,54cee87b,7bcae75,a601da9b,637aba50,46bc5f9a,3ff6c512,9fdd3192,203268e9,2388ac1c,840e7635,22271483,161f8f60,42d82909,41373d56) +,S(529b33b3,36f1631b,6c7b6111,ef44a888,bf95f344,766a2d97,6c5e5d5a,53f44245,12856106,93da6f4a,2fabc1a0,3280249f,6f29f9ee,9ccd7000,4cf0a857,ea061099) +,S(f2b3346d,a6cee9f5,1a933718,2d655a88,6a251353,8ebd243d,d3e3cd65,abdf1849,d41df628,be7703fe,9e866526,2f6278d3,55aaefcb,324df2e1,f483b0f7,77b77da3) +,S(535e9dbc,f03d98c6,70d03d04,5d638c7d,1db7a12a,22f3837b,3f559b70,4582befa,2c233228,bad897c5,bd4fa98d,67f8384a,62ea4761,4f64e4e7,40bc1f65,9e358392) +,S(c131688b,70da6ac6,5ee3172d,dff34624,b019bf78,85033970,8253b4a0,b299a7f1,fef187b6,e0e748d9,69a11d82,e9f996f8,d6e1aff,8b84d20,7dd78519,dce5f3cc) +,S(75dc84d3,f7ab7985,10ceddf3,da6bf832,a984d00c,98726a64,5dc71b21,754d3ba9,f5d9edf3,5e110491,2cc5e4d2,82eeaa53,84f62deb,1dc2a183,af5b232a,e3841c50) +,S(591dc7bc,9136c38e,ae727310,25c5fd52,82c6dad7,6f98c648,78847ed4,f32a36c5,fccfa4c4,f81cd382,e003aabb,4cb6c5e8,8d4875b7,21d44233,95397268,d7a421a0) +,S(83a59c89,2a8392ba,e7548fad,b87acf7a,d2c78db6,4c588f0f,b14753ce,12712596,7d00ac1a,33b12065,d67c598,8822ff3c,46f090e3,15cf919b,e3e5030c,e7c5873b) +,S(e9b0dfa5,8c52b2f4,a9c1b8c3,76391eff,6608f968,ec45bfc7,6ca93e2c,ba6f6f83,6449b28d,5846de0d,27489124,feb3f3fb,5c1f0839,a7809be2,e5cb5def,2e0a7c8d) +,S(16cd2f9,67b090c3,d151c002,d3a3d25c,c76adf50,d55a6f81,b1b9a650,b72bc03b,c592601e,2fe77090,39ccc091,bd78458b,a23db74f,848ee06f,50ffe4c6,8ad63a7b) +,S(f347d3fd,1bcde363,69e3d9e2,44d6e5f5,d80b8dba,7b1866b6,f584a38a,3aff1cbe,1c435ab9,ae38a13,98dcfc6d,64125e8,c7349f81,1584fa97,66dc4d24,7e87977a) +,S(5b190d87,d644854c,90c49f11,34921545,f349edd2,b4e9926d,534374cf,da428b1b,8a4eacc3,63efd6bb,5c93ab25,65d2c157,9c1d3176,b2713ea8,56bc97be,fa5401f3) +,S(a7786cb2,d7314ce8,f21e9665,bccacad,c49e78f3,7772c3ab,105f67d6,1c30834f,4fc7acd4,18982e3f,2fb1f911,cb70bfc0,6c4ea71b,a05f6371,46e96bd4,a9441953) +,S(d3654f53,e9071a18,8c5faedd,c0ef3616,abf74d26,f073ddcf,545a01fb,2fbf33cb,a56f20c7,213c0394,5978502f,c4a716f3,77c7a3d9,26c3cde1,880f103b,432f4383) +,S(e1138a87,400acbda,69d8ac3,bb6db4cd,27d03176,ee88994,2039a93,7822399c,f29a8fbf,dd3f6d46,5c640de6,b6853e40,1803cd95,f5e012c,9ce967d5,13a162c1) +,S(d29eb63c,91c0f4e5,b3c49c6,9c9ca952,5714730e,76bb87e7,374ac995,86317708,4ad93702,4e9180a5,9d22ab4a,c856de6a,d9d38c9f,1cb3fdd9,d73aff61,fea5f1a1) +,S(a9d403ba,a6717fd7,6577da1,d8b8ecb3,d2124b30,d18df147,ca48b482,26ee49f8,1eedfe7a,8c5ee1ee,50937bca,d9591144,56f1e49e,db74133f,f45176b7,26d5323b) +,S(ff7b58e8,dbb6055a,eeac6bfd,112d89c9,8d8f3763,98e41e39,ca076bd0,9ef57528,4e4e39d1,ad7a51e7,d6cb57c,836c0685,8e39f1f5,bd41aee6,cf5d250a,fc381121) +,S(43c50a9d,f7839f61,e1b6db23,4e2f146e,1672ea1d,566f5613,1e7bafd1,bd362979,f20fda98,81541a8b,a83e908f,69e05218,1e215885,f60cfbd6,1ada74ea,83f449ae) +,S(f608a9b7,1c91298,75662ae1,6be6d043,5803a9d5,29d3be53,9843f227,174ac30c,5ef364fa,e919dd42,1bad3243,413bb565,d788d266,d7b508f2,83c486a,327ad5bf) +,S(c913e029,dd1ef043,f9bc48d0,c267657,3cc11722,5cdad56,8d013327,445a117e,97573f0b,9c31cbb3,7bdcf07c,13526a04,32a38f18,43e26d8b,ace85a50,19a83049) +,S(aae0c49d,b17e7bc1,66c0c58a,cca847f,61318bb1,3482f63c,ce7dc55c,82a7d728,41170bf3,f68c3dac,22051aae,658cdc0e,810396ff,9d5d9251,6a7e6377,5c6be0b8) +,S(f2805f1f,45a0b622,59b9b15e,3de4a05d,e306c8b5,ebc5ac96,d35aa388,ebc846c8,f5d72739,6ba37244,57725cdd,912fa38,b36e201c,26409dc9,9789d087,6b8d1267) +,S(6969e4e6,e150d12e,4a230e59,37d865a0,f7d2323a,781270d8,7b0a7a90,fa4f39,c71c33d6,d323ed0c,3e77ebe0,8595dc9f,e030a0db,aae16f88,8dab7187,5e14de3a) +,S(aec2bf0,7a0e257a,be4fbdb8,e872dfbe,2aa0643a,411943e3,e4cef039,8d988f3e,d8e9f557,e686c380,c84bc528,fff0a830,bcc3b6a2,724d9df6,ef21d545,c8c931d0) +,S(9c6ff99a,13dfc35c,a502890b,69d91dd5,198f561c,790bf322,6bb243bd,4926f710,2aef920c,84a4f5ad,9d88f541,1ff46839,2f7f4e63,3422b11e,9b683403,f7ef849d) +,S(fa43745b,37a5e238,df0246d6,7eede652,ade75aec,b5dd8aef,afb8e6e0,96d7f1c2,df9d5aa4,2a3c6540,3793c8f3,fef939ee,48aba7f8,59fe9ead,dc44e82d,a98689b1) +,S(694a3288,6c4e26c7,eb3b84d0,9e777f96,ce3b74e,58658741,27643dda,a78d46b9,a54d6c6d,7645e5,6e8909be,922cc5a,a700d932,cb1318b0,1446a9e1,8bdfe67a) +,S(65499597,5be06b28,b4c339a2,b126210d,930fe920,7cc72be4,3d4ad17a,c08f38a0,bdee23bb,467fdce3,2d2cd92c,e81e3f5b,d29fe8b0,8b35abe1,c684443f,72f7900c) +,S(3791c8f6,75bb14e4,b7d4f084,483aec44,ea38eb2,9b108137,400099b0,799f0bd,2dc8f74a,da5e5eb6,f3b96ddb,cbee5ee5,8b2a3f45,c31cb16d,15a4e918,eef7bc76) +,S(d5e41065,eaf890e9,e9046936,acf6a43,59795c7a,1939d8ed,42941a8c,6ef31364,191ec4c8,a559fd4e,7abbb6b8,f2e4c32c,bd2f30cb,d06ff6d,21f156cb,f65bbcad) +,S(16ce2280,fa0b404d,28a29acb,62058b99,e504e6f2,eaaa3ae8,b77ad650,2c970dec,3d4683f4,486addb9,e54bc252,74e590c,b6eaec5e,7f96e9b8,8174b81d,56efb09b) +,S(a6b0ab30,6cdfb1f3,90e5b447,816841b7,e08f997e,d7d47016,bf3b501,c6107d07,2a62842f,b67e2794,92ed337f,d72abc9b,94e96e4,a10f658d,9cb9a4b6,a80abf7a) +,S(d7ca6d38,760b320b,21cb779c,d709f376,52b9d08a,8ff6ad90,21e00628,56033583,d6c05f72,866d59b9,6b91b6cb,b1401619,5e2d1cfa,9982db04,6d672fff,849e7bb5) +,S(78ab0563,2d3f707e,738cb134,7121c73f,e162405d,61a0bef3,75f6cb45,f5609d33,fe1a390c,10c7cb4f,297a4b42,4baa5c50,350eba49,55fb74f2,1094ec5f,ee75a6b0) +,S(c053553a,5b9fe803,65fc5a9,60e48fd9,218e2128,28935879,bd1072c5,180d6f04,e02da774,214dab04,e11c45cf,57be2802,7d0d28dc,51cc08a4,7528869a,3e719192) +,S(bf85d713,e2a995c3,ef98d403,30fc1fb3,5faf8e95,81385b4e,b65d5ebd,23f02d03,a5d45947,58d7b0e1,a8542a6f,21ddd207,dcb68e96,88284bbd,6b940748,b3e59b18) +,S(fb73b6c9,2be578b8,fe2ee0d,eb0aeec0,13816905,9e55031d,69a4d0f1,1bd280a,72ce7893,9bf5a55f,d4430bd2,55bde818,e15bbc98,37e3dcf3,e31d22c5,9614cd16) +,S(56496e5a,ed338dc9,201b04f7,8f4b6c2c,d9928135,83360dfc,1e2f370c,1628aa79,81dbf48a,824fca70,12315d3f,a56d5150,92ffa54,d1300725,86c8a476,be8c2db1) +,S(d698e395,9e81f098,485edcc6,2f2a421a,aecc0a79,58c9f6df,7905c931,b7a2130c,5903debc,e61eaa39,dac01a8e,3e970a39,79408bbe,1d0ffba4,5ce78c9c,79563d27) +,S(84b32e40,86709450,b32ade00,d3404b34,7fad8d7b,9387d931,780764e4,c566ff80,4c80d078,56cc4296,2b7941e3,195c5350,9bb5b0e4,325c24,4aa3a581,bb1cfdc8) +,S(7129c799,2a3689b4,c1873a45,f1ed3327,6528e243,28f30cf0,5ac38a61,8db2ed6,362b4fe8,482125a5,d4d15456,79009aa8,44f86619,cf3fca68,ce38995a,660810e4) +,S(93164c87,fa48b6ea,6b6cac08,8c991c34,5f8cad7e,f68c5b98,78139d28,d180d824,a3ac0d5c,c91cf0f9,cb97771,d16396ab,bd183221,c8d36a87,2785e847,b5d9e26b) +,S(4f90e8e2,37c4937c,a384c7b1,f929f329,94744d5e,8b89ad94,808ed9b0,9f305a68,cde776e0,2b6b484b,83f417c,fceed07a,ce725e00,343c4e70,14748f08,992430b8) +,S(4d9603bc,9f40716a,e6913fae,253eccd3,4442f6a1,3c058ed6,a10f91b6,75f716a1,92b679fc,278ce355,97165827,aa6fb450,9c52a412,284aa493,c6654ee4,3924add6) +,S(8b6f651f,91c54143,12c94f3b,c1632e13,f90cd718,67f906a5,aed13c4c,4ab17203,88c159a4,b80f2556,bb4e2b78,c126c40d,9ded995,bac6ac13,bf83655,210c7066) +,S(6f58b852,cab98347,e86448f0,78d49916,1909e1eb,60556ee9,8cdc20bd,b25ec256,3a4c05f,233d9305,e6abd30d,da0cebea,1681dab2,ad805cd3,2fa7023c,98239885) +,S(6be7f6a8,4021411c,acb710da,3ffafa32,8bb6ca91,59a91008,6cb98071,100c44bd,bf64a6f9,d461dfb4,581c8d59,b5fc1191,86f339ff,320e968b,b5810145,4836bee9) +,S(b2fcd0b,b8ea9e66,f2cfdbe1,3ecff9aa,99661f0f,2ac1844a,b3983d2c,ae102d39,a046989,bb3b8fa3,eb2cbba,d3a7e810,27c38cb0,ea33ec74,1444e8ce,7182bb86) +,S(a1d6ed0e,ab559b29,edf7770b,50cc5913,3916826b,6d99ef12,91bf744a,79a6fd96,bb91657e,c9255ae,173bf93b,51848094,2f17df30,ac6b391,df2237b4,c04ab69b) +,S(28f6570d,ed7130d7,35366201,e9ab639d,440da5e7,e1c9701b,d08a811e,912a9575,39191f60,119c59b2,3e91a7c7,fbf4c2c,e2216e5e,955db62e,dece6adc,f2d2884a) +,S(14c8ced,dbb83de4,218b089d,101d7b6c,31cfce22,49637853,b9c804f,90271fa3,48052d2,a5d02e71,135cba3d,24c7894b,de1293d,44a2f84d,7a8fece4,ea9a41fc) +,S(eaff9f05,fec737d3,d2a28fa1,bc8450a7,a75f563b,9221bca9,eda2f8fa,e12d489b,d6d4419b,5c9873d,7d3349fa,520f463a,2ad9f348,1a111c3d,d3fb70a9,23b8c139) +,S(e8ee4c6e,b42a3712,85b52457,52e7fc3c,ce0757e0,96932a43,d92bed15,14b35e95,d86bd4c,27ef1831,f107ff4e,6246cb7c,ee55abfc,cbc5de2d,5f230a2b,6a3b748a) +,S(98c61909,8213b06,6b70759,1c88a51a,d5e90922,6778386,a0776e6a,ee41e7e4,eb3d2e2c,f98bb904,42e90e2d,38ce4c53,fd72da8,1a37fefb,fc241aef,f65d4c8c) +,S(d4f66b81,f5d62986,c57a0dbb,c38bcb5d,89319806,4e28e17d,946cf7f3,f946dd29,c1930f8d,eedf1a00,cb7c5579,d814b5b8,2a12298e,530bb32,867821fb,7b7a5fc2) +,S(14d73ce7,5f8919b,55d54369,69866f0d,a13dd4c2,ad447c4e,b9879060,1411087f,69b94622,b9cb0d3c,5d106df2,caf4208b,b71c48f2,629d467d,16ee06c6,93ededf8) +,S(57efc935,59c7a0c,6ac51105,8a50a8d5,5646ab40,8a711711,5bc34c20,23da3aec,fca9329a,60b855c1,74bfba0d,92ae5529,27f959f0,5332cc4b,26272c02,b5c5dfd1) +,S(91f13a86,63da80d,a90b9458,55a9f3e4,171213f5,3683873d,cb6372fd,95b3677f,7405e0d8,6734f110,c329a7ae,92055639,e0332ece,134156d0,3cab4efb,419a90ea) +,S(dcc84d0b,46145e25,a83dfd64,b40dc5c2,c3322716,d1337767,5f9d6aff,a1607592,e46a4cf4,8e4dfeac,8df1526,f53246a,51b95161,a7030adc,8bba8ba8,1d2d6c66) +,S(28ab06ac,d5a6f432,116011e,6bf3c6a0,df0bfa1f,df16adb0,54250174,3348676c,51272a1c,967f49da,e9d1d6b7,a4ffcfef,cc31f42b,e0da52d4,27e56b63,97b0cb9a) +,S(7c01b21,f8d1dfdf,d3f2adb4,5915c636,d9c2a92,320c2d00,beff0f45,21d8b240,b4a48fcc,99906d81,fc6f78fd,aaa96b3d,6e7ffd62,d36d1aed,f76fa25c,542c8b48) +,S(f94eeb78,30f8e95f,2a362423,9c0e8fb,a0d3279b,e0cb5f5b,428a99ac,d14d98f1,c96d5b64,69676b9c,a5dc91be,8bb4a93f,7157221d,9513cd23,5cfe3ca,43649527) +,S(ea4674e4,7788cda4,bde0dd8,5f5e32d3,9a6d6477,30e9045b,d406f5f8,11ec35ca,769bf79,a9b9122,3070032b,a18bd769,fb622a61,424e9a0f,e1a46230,a75a235a) +,S(da5a7419,7f8f8685,1317260,682bfe28,19b7b38d,376745d1,7283c250,1fb40112,a60b5eec,5871e18,1325ef5f,5d6373bc,b5a19f41,2107290c,eb663e3b,95c37a39) +,S(c1017fa5,52560fda,18c6820,ded7b76d,a75ebcec,23b8fb6c,22ff72f2,26e9d41d,e947b19d,af2647f9,167da3b8,dff05897,9b5dc1da,6782929b,1b078114,349fed39) +,S(be7c0e1,4e814476,78a15238,b5809a75,1b964658,19bf0b16,4b3e914b,29216a7f,a30ebe97,f64219a6,f0b072b1,5d648e81,880840b,8a36204e,4f1627a0,feb392e) +,S(6758eec1,a4d6c0fd,cc118405,62fff82c,59e0fff2,cf80414f,cb53b139,97007cea,4653bc66,20fcd852,422c8898,862631e8,a0149bba,d0b9cedc,b4fe3eaf,7cb316b3) +,S(9cbb51d3,f88f4d99,9d80cf05,9e8735b3,11014de,69acddae,70d850dc,405b6116,dfa2e748,cad75b52,800e0ed5,17efc4d2,47c6381,9190a3d6,e2510bb,25902360) +,S(63a5aae7,b170792f,17350229,eaa5ad18,65328bd5,3421cdea,3d6e1510,d8bf0bc3,1125afd0,f25d5f49,f7e6250a,ed3634c2,12a261d7,8ce58ea1,95afb883,52da1010) +,S(adf96249,3e5f0328,ebae11fb,243da21,4e036d24,1da62430,b0fe2c89,b2f10657,509834ba,fcd8ddb3,2d018b16,d17d4b5f,219a29de,8a03f7f7,84f34b57,9b1ea1e) +,S(a0d8f5b,7fb44a15,93583364,e04d7225,bfdfa549,c2b25f32,f00765d9,e34d472f,61eab66,f84c2687,949da99f,3d97fe3d,630b6c22,f9f7c48d,d539b122,ef998ab2) +,S(b98308ea,9596a2db,adcdbf81,56d8025e,7a3ee0d2,5b27c33e,f686690b,ae61fc1,cdb4e317,7566a4ce,b4cc582a,3cfd259,445bae6e,586786f5,1250cd05,27ddbd37) +,S(e1a64f1d,37e3d6cc,119eefe3,793ba303,cf3969c8,db4abf63,7f0232af,70b98134,588d7ad8,885dd2f5,ec4d2084,b3f581b5,ddbd3567,8f4ecfc2,f955ced4,1c3231da) +,S(fbd5dfed,e8543aa6,e72253a1,bd711911,318929ee,7af14eca,49c2276a,65fdaaed,317cef0a,432c3516,3c405d51,65b8c310,f234891a,dbd0d6f2,f76cfbf0,193b78bc) +,S(fac4b7f3,b0a39510,84f1a882,2f7b6f20,30b87c4d,c0178a2a,e3077ce6,84da6bc2,7029b920,9e9522fc,160a5c54,a628fdd4,386ce0d1,55f19fae,390b2995,bc867d75) +,S(ca8643de,834fcf50,802c0296,41b5fc7d,3c29fc51,5c75c53e,e64a8185,43ef7c2b,907a6c27,111e296e,2436fd4d,ad9d9fa,f8ecea31,cee714c3,b26fa964,afcf17f9) +,S(71873649,d700ea1,7ea26b19,efe4b48b,a4a73b22,bc54066b,1202c96c,f3315f1f,f6cd8447,4bb374b7,753fc614,de5cf429,51512748,2042215a,bd82c11d,87ded2fd) +,S(67e738ff,21c3c732,26e57482,677c63f0,175b768f,776cbe6f,15ac3c53,48c5e3d7,14e5140f,2343dd72,577a5064,47cf6093,ebd2854,5d1326bf,ac79350e,eaf2bdb2) +,S(514b3f31,f875253f,205339f6,434ee17f,6c5a5c42,662ce934,280f55f1,9a198893,4eb33993,a2fbec70,4cd1c538,ce7cde4d,a61c0fc2,dca9669f,e469807e,4ae9a090) +,S(426acfa6,eab03e75,6ab30a41,418a0392,ca53c216,f84b3586,f9858501,8bc2dab3,c3d63eab,21cc10c5,c0a06682,d0573bb6,d34944d1,749da74,acb59aad,f1083926) +,S(b946bb46,68c05737,ffdcbee,94597c85,b8ad4a72,c58205ca,d420e8f7,a2d7a09c,cc700ed4,12633826,567e63a5,e65bc603,969ea13a,49feeb2c,dc723bad,1e3401b9) +,S(e6a844b3,f6c8b45f,511d7d3c,ccd75df4,1d1e38f8,fb28adcd,80335b95,d98e33ac,7f24789,3939890c,74a3268e,e1f717ab,6c4aa109,cc9257e2,c0b176c2,ec58cce2) +,S(43e62bbf,d9e052e8,c1636b41,5524b98c,493e903f,2313dcd,117d140f,96458001,f481853d,a90d528,30999300,dcff31d,ebee23bb,49ecbcec,8e147799,a63b352d) +,S(c44ef40e,3427476d,42e23e1c,41ddb9bd,e555a844,9125aad9,e94f44b8,8047f817,bedd4313,e43ee593,f2818e14,776f4ae2,38f664b2,db84c9d2,498b9ea3,8e236fac) +,S(a1b35313,dcc11895,c096f93f,5f8e4997,7f58f2fa,afd9b7e8,d408bde0,53212385,bd7eb25a,bf0d50c3,7309f9cf,5093d817,135a9ec0,20262c55,bdc8e8a7,a3079f5) +,S(48a6f3f9,fd6201c9,a37f2f0c,735bd98e,1382229a,d1589c91,b6748a92,1b5e964e,1b545155,3d41a8a0,64862c81,fa9d5966,2c73e039,53a5dc91,9712c50c,5b4481b4) +,S(8e01f574,651db06e,8a19181d,c8eecf1b,75f050cd,c5b2354d,b06285d9,a4061e9f,9d156ea0,9800e7d5,b6c6e90a,2eecada,7d7ba964,ba1f0cae,256fbfb2,275e108b) +,S(4bc0f8db,9e6db576,3eb68b19,5a79f8e6,28e375f3,1694a58f,43da4dca,fa05345d,c6a70789,a1e6b164,bba380e8,126a4a69,6338053b,6a32d9d9,8d0f215c,43a5b555) +,S(966fd0cb,5f5edead,d6230a66,50b7dab8,f4c7ee8d,b00ce55f,32d54a23,ba97a768,9d644e43,bb4f9b65,f5819a1d,ee0a16b8,1e8929c,a9e544e1,6546cf79,811a994b) +,S(6ab519ad,49522ea6,592dc7c7,4438e337,20d50eb8,862467f8,2962c510,857b0d8a,c5ba3a34,2b7250a2,62b67ec3,554c21ee,e82653a0,378ee0da,8a809151,a3ae44d) +,S(f8b0b8e4,3e5060f5,39bd0a0c,d076e163,e55f18a4,25327856,d526d6ef,aa76c62f,743de052,a65853fa,58cf29c8,62839aa1,7d5c95ba,43673ad1,fe1ecf6f,d4d48ba4) +,S(c4509ef8,579d8ff0,ab585c0e,5223d5fa,967e9763,9d15b0e1,6587a125,3ed18bad,7b91f7e8,ee979a58,5a36d8d3,574b8ae7,81199dc0,86f11cf9,7fe58e4f,1db08362) +,S(9a00135a,6ff450f8,e2207c53,894c0e4c,4cef732,cfab43c3,aacb00fd,ab42379f,393ec748,3684c5b1,4760902a,28cabb90,b481596e,8b847d59,4dfb8ff8,1fa9975d) +,S(3d775bdf,173f8d7c,de90c17c,25d3dee2,a484d331,4e525e3e,aa4a405b,6f488601,7e6b08bc,a560f6a,5bf287f5,59d74e25,38a4025b,89972046,b3de98c7,b30e51d4) +,S(cebafd3e,a60118b4,513380a8,d10a29a,a95e5002,d703d7b8,f5d0e983,7b03b529,19477ab6,46f59f61,ff603594,e33ba046,5f6d8b1b,f55290c0,fd61534a,87b74b4e) +,S(ae8a3cac,1ff3e3e1,ef01ac16,2e49b237,7ecbc963,f58adde1,58cb1987,cfa731b2,5a4b0675,2052b749,733f6af9,e8dc773a,ee26fd2b,114a7ac2,baca1e9e,51531efe) +,S(d74d38bb,8bd47123,5c118ede,e477ac4d,fd39b635,d0fbeb28,30843753,ade3b38e,f3e505b6,f17a2839,7a985084,31ba6de2,1ec0a0fd,3936dc32,5d66bb37,7cb54451) +,S(f4287675,7780785c,d14dc1be,fe075d1d,4d4f0b33,b0ce9db2,49595b14,f6beb2a8,a3df2231,eefc62dd,c658bf6d,a361ad6f,950f34d5,3ff25536,4eb1e1d8,60514b38) +,S(e91d576b,da19ad62,76bcaaa8,69cb2099,e6277688,3ca1d454,50dc9401,46378e94,b57e2038,c8da2cb9,9ac313db,83265d8c,20e7918,37e75db8,7acca971,3840779c) +,S(269944e8,7d77e176,3791b53d,af2aaa43,e035e1dd,f3c7daec,7e830cfe,38c66ea2,cb011764,34bdc7fb,528ca43,9b323c9,56764145,7b7eeea7,39ace76c,193707f6) +,S(ed41e68a,8f0b3681,1aa61c68,57bb1765,fb099317,2ff6ea5e,e94e01fb,644a3d1,f2cc907b,880cbe04,26ba1e44,83d95800,71c918e0,8cc085dc,1dd0deb8,c508231c) +,S(6947664a,96cfcba5,e2e36e4f,53a33d69,538192f1,3322113a,b0b79642,dfec0de,6481d1bc,5b65b6fb,a91b76c4,d6ade333,25610195,decf5dc7,7f95dc2f,a38fb6cd) +,S(aa3e187c,e22b63df,6a80a42b,ba9265b0,4379012c,70e8ef66,8ea85c57,1e27fb03,a638dd6b,98109fd1,96ffcab7,3010543,e8732f48,e5cdb29c,38fa91ff,8d011be) +,S(5de4e1,a4bef94d,b4a9fbab,6543fd6e,bfd5ea37,9124e1c5,eff9be18,f84a944b,21c4a282,693be294,b699513,f7744761,58f8aed1,a7ec7442,9679ba9e,7957e2d6) +,S(6533dcc7,2c87b477,81c96c79,3af0af22,fdd18fee,a33a30a0,9646c2b4,287a7ce5,7f03e5f2,3d82f9bb,ce9589fc,23442174,482748cf,a3aeccd3,f8c37058,837ccc51) +,S(139e5f3c,129e584a,bda008ab,85343531,c5109f23,a70b9871,a5ebb00a,64fdf354,497b7e57,41fb8eaf,a53182ee,98352b62,6bf011e4,826ac9de,6190a2f7,773f0a98) +,S(493fe9cc,f7e1e950,83cea991,8d8f2561,3ce57b81,81915f0c,7c779aee,8161f7c3,e69763fd,62fce1d0,454aff6a,6a3bb448,639ab615,8bcc2f40,88cbe4c1,67020cd5) +,S(ed675fdb,d8dd70b8,641ac2fb,4903ca77,900e3c15,766ec8a7,446e6b36,52a064a9,a61bb379,85cef5b4,6e0669b4,b0188ae,23ccffb2,5ce777fd,1f26f313,68416de9) +,S(173670a8,9a769e3a,797c14e3,15393d52,b6d48444,3861cb83,136b0ca9,9a260a4d,4dd43921,c1ba4d2e,95ea1b28,e4908410,d140b3f7,7dd82bc6,d683da27,c7bd4904) +,S(d5a5e3a0,8f87e154,6f11e05a,c106e80,a1da933d,2cca528c,df9e1f09,635d1610,7c6c55e0,daf3c092,8d0a9c6f,2c1ffb0d,aa20df74,20a35167,2552914e,5d175352) +,S(54f5e9a0,4ab321c,692f2e2b,94d6697d,a0a99932,74ee4ebe,4175a338,daf6fa2a,58cf13d1,75fa6e47,34aeb761,4f828dda,58a7d9ca,855ff42b,5cbd66d8,89f2de21) +,S(d6682898,154de995,62e5544a,442622b2,fd9a632d,94445d4c,9e8725bc,7febe6da,98091e3b,9ba3b857,50e02c50,a7971aa3,b2d5045,f760c1eb,d1371dc6,3190f206) +,S(253019b9,2af2c4c4,8d9f6e1a,82decd36,610becff,18c908ca,fef49bf6,cdc2da08,617d335a,d9509d74,e5eebdce,810872e2,3ffbdd3b,da53aa5a,495b86ba,397c680a) +,S(56f64ca,461bebde,560d2bcf,ac08f292,ced72574,50e44f16,bab67cd5,104da6cb,5b2085cf,dc278328,a992bca8,f2ee8142,757cb553,b903a3ee,bd83bf3d,593aee44) +,S(f39c9571,1e153a9a,f0e5c192,87e3ee38,5a31a1e9,bef312f6,4dded245,8f553420,4cc94ac1,1c206b9f,7879df90,8498c132,e742ef62,48bdfa97,4b882930,e3b0b1cc) +,S(437eaa8,cbdf873c,32d3dad,95c5aae5,b2f3034a,5de0c536,8c8ddd62,c13128b5,27d8ea0e,b00310bb,59424599,3d60da59,2f8d1b2d,df0c1fcc,721e3080,993c59c6) +,S(ade1a589,3f5d6798,4490ae62,11c57534,3f13e7a6,14ef71f,a79eb4b,a880e9b2,bd49c17c,537e6a3b,ac7404d8,c3c1b429,e801882b,7f241304,9022e262,3791c715) +,S(936275d5,c3091a5a,b9f57d97,7a4e1b6c,a87ef978,c7ad198f,d16e2d93,3ac1224e,6852690a,d8bd84c5,eab75d96,c6e224d0,ed598851,b17b857e,5aa01207,df6b9f32) +,S(29e55fd,3e8fd5cd,e046db20,5c80b9d3,e294971d,76b5e8c,c33fb5a7,6b9b786e,769e57a,7eb934b9,4578312f,720ce44,2bbf3e2a,d61a6f57,af6cde49,8c010a0d) +,S(df8362a7,350185ff,198b5275,45e16f78,3ae37804,7647c584,168b159b,809051ef,afcb3446,fbd4a3bd,e826b604,84b16c3b,735ece80,ee48da26,3a6cae0b,62af6bab) +,S(5f443123,9a35fe0,f9566037,96cc619d,b9a01e83,96cf433c,25415ece,58a427e,385c2812,fc0aabc5,29272b60,56aa9c17,37d81c72,48fafc90,6fd7d4c2,b0901ac0) +,S(c19c4215,53c6037c,525b77a3,1693fc4c,8c5323ff,5d3dd63f,4b7e3448,15dd2de5,9f76ea8a,879177e1,27116189,6d9e6b20,a19d2255,92c99822,159c8ec1,a93900a4) +,S(8abf3a1d,d1e02332,ead62fdb,36394246,14d19d92,a82113f1,d62a1423,af2d45ac,325a967f,dcf5ec3f,75e7a984,ecc2d9f2,defc2941,6dd507ec,7de1b598,a7e36dde) +,S(c8bfdf90,b93161e6,c4d4a58f,a71ea84a,57c1dd76,c0607b94,33fa3cdd,f759ae7a,23dc38a3,d172fb63,ed47efd3,c3e5aa3c,fd7990cf,1535ce5,b51944f3,270b20f9) +,S(976666c,35c55997,a759e7ab,85f90d7a,a26ccc40,5b77b8b4,43c7e86e,2d2c78d,6d342c3e,44facf25,bf2db2e4,e20cceef,92c52cfe,b4370f89,60551614,381ed0ff) +,S(e7790ba7,7737e4e1,c7bb8e8a,5bd89c3a,1fd65b6c,feb2273a,1c12a8cd,f963252b,2635e20,1534b58f,70e754ea,db28998d,f7138ff2,a9e92e1a,810259dc,ffbdba42) +,S(42cb817f,6bfcec83,f60e3ecd,5b986ac3,b471cec9,975f3efe,37577d81,4576fb0d,ed35e128,b2f9bd90,f2162050,a5737909,144a8596,5075fcce,2708a4de,c45cf49a) +,S(a0886870,1a239333,ffc60397,ae11f145,31902428,a4fd58f0,2450e0f,412f3cbb,c8486f37,1ad6e034,8e51995,b4887ebb,fc6002c9,9cc00ad6,d8a81f99,1883af52) +,S(a40cca41,9f101b8b,40fd8e8,b364a52e,e6a9642c,22661aab,3486f25,732c4bb3,f421b69,3ec096f0,2d1e611d,f0701cc3,b0655b9b,95009285,babb150a,9445a910) +,S(90aef978,bbb0472c,5a75b70b,c9f5755e,c532940f,141a350f,efe604a4,35beb721,f9f05c70,737cf6e4,ca88cde9,2e478ea5,93f58510,9479d187,a5f87e11,e14bbf0d) +,S(ffc23a81,a0f61f39,600292f6,8db9acb6,d73ae433,bb827f1a,7bc6997,ac3407c3,b76a0612,bce5e139,277c7463,d47cce0,4f313b37,a0746803,6865f284,574d4b66) +,S(d3ca7b94,40bdf990,4262bc16,298425a6,742c852e,df99ded,2ab0ff50,20a112a5,9daecbac,ca7e825c,4f44281f,9813ce2c,5ef191b7,341fa2cb,1f9780ff,de4d637a) +,S(2a53a2bb,720ab31c,2a6b7589,ce0e1ac7,469b21ac,d55eeb41,b23b3cca,51bb9f06,11aff933,ffde9646,7a4d0f52,220d06ec,27f7810d,ba44b256,c21a57e3,d451f84e) +,S(487e5356,80c40287,a6fd2f77,bb8f5cbf,22b2fc71,7fb7487d,5a08474,ada1ac96,53bd7fd5,46070d63,77b65326,d5130445,29c9625,520c3a73,1e990d0d,5aa849c8) +,S(3e5a5fcf,291da0c1,fc332cfd,62f4b629,cf23c0b4,8209fa29,63de5b99,667b3e16,86987f2c,43389a1f,4b028df,773bb130,8b33d7ef,7f55dc97,b060ded0,fab86825) +,S(4f90afc5,15a847c2,2c42493d,22dd5704,601840fb,57086b8e,d5f7d779,e3e256f,46e31958,ac2e6892,335f6b08,9ac0d3c2,4ef93994,2b585e33,226ded0f,aab831be) +,S(be07392b,40be3ea7,a6a73807,121be982,c2ff4b38,a4585a17,82c585c1,e23601fc,3a158457,657c9e8e,7bd775b8,1dbe2109,43172771,e66ec38d,a57a8216,f1f258db) +,S(86c274e5,2140375f,222f9e6d,47c24e06,ecc2f741,51b5a603,e0563412,fa1108ed,4a02fab9,4065d590,32eb90b6,4c98987d,2703f276,f92c03a2,aa009692,40a96ef8) +,S(4fd3a6b,e7923898,127b3056,1dac13fe,287277aa,21127626,2678e04f,3573cf97,6498a1a1,63cfecb6,c6cf4b4b,43bc7e96,f07ebea2,3b4380d2,38c0342d,f11173e4) +,S(cfa435ca,5423bdd7,8aafe78d,1fccfd07,cd2a5f86,ba81918c,b68c7695,dd117c94,bf4c891c,2dff79e,35b77854,66845389,c399b53b,4a9f4cef,d7d68357,10ce1fe8) +,S(aa5acb1b,79533cc5,9b29e927,77469afd,414119e6,1f3c90c1,cdf93e2c,3e1d446c,b0df29b7,8713c001,fb6d3854,e183767e,8b11bf10,9c75dbc2,6bb408f4,22a498ab) +,S(1b93c2ec,c21bf558,ddbfa1c4,41bca52f,435c09f5,4d06d175,6353e0a1,545c3d15,56c455ff,718190e6,5ace8a5f,d5c6d263,f72e67b1,a20dcc4a,eb53fb4a,33195ed2) +,S(1744cbf0,2566d20b,f639438b,55f72b0d,c8480092,246410ac,d8551498,2fb91760,7e77a7a2,32b0b5c5,9d1d5da5,7a7d2570,68dc0f8,4bda1f6c,683f5229,df4c66f5) +,S(a0be98a5,ed448c02,8c2f8ffc,607b316,1b9cb9a7,a2291bed,d24a8407,ff02582b,e7cc2c6a,2393525,b4d44462,cf6ac8f,76b86981,ddd369a2,274c1971,11da6ea) +,S(e0e593f5,dab5f7a9,562b2591,d8d75ea3,8b2f2b19,cdc8fe71,14891006,7b845882,9088c762,15e35db5,b3bc2614,1a42ce2c,b5464dc6,49f469c7,e785f5c2,cad67334) +,S(7824d895,a4982eab,d6f1b928,de7b0d65,ffe5796c,5e492b40,8b9fc879,c29290f5,ae175c68,8e428528,38a2d8bb,fc232c60,d6bcf117,57cdad44,8a4af01a,bcc7ee88) +,S(49d2e57e,e0b611dc,214e4a2c,d019ae2d,2aa51393,e976cb5c,4211cf10,f9d96e66,ffa0c4ca,88011f72,779b7919,829bb9fb,11c81e4f,a78154d2,5cfa3cb2,bd5b770a) +,S(da605f6b,8f701a85,86012e8f,83898aea,eeb3de32,e9c7bee8,be2139cf,c75ead00,5ba0c12c,e98a1c1a,89f0ca5e,a93f50fa,5a24307c,6751f633,d699e5ce,9411d246) +,S(aeeaabeb,cde94e6d,5a1de2bd,1d9c380a,9307be3c,1c32e785,652ffd9c,bac16ff2,30094755,8510ecde,500a52b,f107093f,b62e491a,c2596460,6641abaf,d50bf840) +,S(62f1892b,35f3fe64,b1ad1e1f,4305f9e8,66ca0a0b,e5cb778c,b0e1feb1,a10c40b7,1a44205e,e600bf4a,2e287f09,c5bedbf,f1f89052,2d14a883,8e1813a7,3f5780d) +,S(c2091ec5,46772d69,28ebe9f5,50a68083,3a567b33,dae25e94,4ac4d589,96979382,599a765b,d2e89d31,735fa722,a1fe1946,eb610c69,2d5a82b2,3828502d,cad4b31e) +,S(bfd7c15b,19519235,700e1b23,79fe7c22,e50a2455,5f83dba5,97d00fdf,1b242367,ac6de7de,545ec735,ea127213,fafd287,f4cddac4,8d6fe694,372bd46,bc66b17b) +,S(d4d4e784,3cabfdf7,903d8507,3831da81,9581629f,74f3cfd1,16b5f635,132ddc7f,1e49611d,582cbacb,fce65a28,44755cb1,d1cd6d4e,2b8f082d,219742d4,61758a6) +,S(1e575009,c378b34d,f3b434a4,95dd8c31,4945b4f8,df617d02,cddc11c8,8f908fdc,5ce37fa2,810c95a1,2697458c,b8f209cc,10bf2123,3ffa8565,5bd8587f,cf6cdb7) +,S(b5a66a2e,ac237eb8,a1f5e690,ffdf65e5,c9268c6d,37104ee1,108f95c3,b9408fd0,99005ac0,aaa4f6ea,60842ba8,68668c6f,f8cc3280,307630de,3c43ef28,cbf72798) +,S(1da39d35,2599593c,66c90b06,3baeaf8d,66bc3a30,21e3f6e6,cfc3ee6,7f26fe0d,9272a05e,b6b0151b,5470d076,c01c3000,f7e58aa7,8fc7fe48,9f285b85,c941311f) +,S(7f3b10b6,b10000a7,652862d7,21f428fe,4e900c5a,f3844500,2b374434,b5a971b2,b94a2fe0,7907ee33,113bd4c9,256fa08,8c4a4f74,df3c3a9e,1f5d205c,a2ecd385) +,S(426107c6,61850f9b,f6beb61a,e200c2da,e5dc368a,3f5d18cb,baf84cbd,7a4eb2e0,e1fa7c78,a9e6ed0f,8d713488,bd3afea4,857cece,8e8e54f5,40f8c70c,67e511af) +,S(555e2656,708fc4c4,312093c2,1489cfa9,d6c94099,680f53e2,fa374a43,45be9697,f5efb7,1e3bb76a,22566e06,c674ba11,a7822422,c6f44cb0,2f2e2be,284db6e7) +,S(7170b17b,5736e07c,262a927e,725b709d,3360625,4642a8b3,af8fdb12,917dd290,ef437ea3,f8998813,b5ad86e6,9a77ce0b,98b2c6a6,b6df5608,627ae735,30973b1) +,S(1ca8ab3e,66fefd13,49e329d0,72a44f1,eb4de646,c8930172,aaaec311,5a5c2180,6fc641c9,2a87d776,81dda2a0,f4984ed2,c70103a1,5531b274,fd20efa6,3b0a3c49) +,S(8352546a,f6d42e82,bf150c1e,41c1a72b,85f057d3,487b4797,7d5e4f8d,c05366b8,76be5ab2,7ca25b12,a888d7e7,21c2b1c9,ac92b0e6,ea0c484b,1383a835,86c5fdb1) +,S(2f112f7b,1730b9d5,63f988ea,765c48ef,353123a1,4d92e44c,3e988459,9c904cd7,eb348e97,f6487d5a,32f70b16,d2ad1740,27d7a8bd,41a031bb,743a6825,2e34a44f) +,S(e84023c,164dc6c7,1d72494a,d410a5e4,2eb6fd09,16a70f1d,5192508f,3ea5648a,634f3585,29be0328,89b2f510,622816bd,225aa031,ab145b8a,48a6fc80,1ef4462d) +,S(951c4d17,45daf527,b803929,114d57f4,4da40342,31c669af,d6e40127,8e28fc6b,f9083b11,a8b30fbb,2c696f60,8ae82627,b9592ef1,c72fc921,bb2ae4ad,5a27f0a7) +,S(adaa9457,f60b9f3e,e0a5548c,51f945b9,78845841,51ae87fd,689b892f,8ccb19f4,834657f9,2145fefb,8df9b047,55674997,f899b951,40ce5830,2e468588,1761caa6) +,S(649296b3,9800a3b,516ef3d8,52d7fba4,597f3e35,1d303397,7495fdee,619e6ef7,44ce9c90,10215167,fdc5f078,e2edfebc,5c8b441d,d88cf853,5c78533e,d61df105) +,S(2c1d7e97,908575bf,38afe2ba,7875f1d6,9f1e9db6,b11f92ce,4bc94d3f,6006266f,9eb250bc,30abd08,bbbb4e4b,bc09cb03,8c6c8ec1,fd1a6ff5,ba23726,da2cf7f) +,S(b130ff8b,ed4ebc32,78293b10,52fa197,b44adaa7,d1bbd74f,f6d56f9,744647ec,4a0a45e9,b785c7f6,76fd0a95,66e4228,136f60b,dcc1806,212590ff,eea5eb33) +,S(4f2a5997,62ecb2d9,31ae9c74,ccd71cc,29bf684a,d68f0117,32a0cd50,cb0e6231,debf8db4,5cfc7083,fa700dde,7309edc0,fc44216,cf7bc237,377c0bf5,7ccf17de) +,S(97f9e9a1,7fbe6e25,e41419a1,8a5a24da,a178c0b,a99e30d1,cab0d2de,7a23c0b,1ab1226b,cceb480,fb4fdc8,70fb5386,6cb622bb,e71d3c0a,4ddda232,57559128) +,S(e17f3fe5,e9f5d17e,40182067,3b710940,bfd486f1,ad3ef4c,bb93b47d,9d9d8cb,f1126a52,615d3a8a,6b360b26,305124af,44ad7d62,e03ed96b,59c2903b,b5272f58) +,S(1a39f846,7d638182,900c3e94,538a0fe3,3ae66853,aac36688,7a5a8bb6,7b2fa2de,ae7c1399,5a625a4e,42b4dc01,9d7c7501,18492f3f,bb4567bf,28ddef5b,82903aa5) +,S(8a5ddc61,27f6fa44,90a93e52,f4faffb8,baa60581,5142be68,cd18692e,b42f5320,5eb62325,853dddf1,a42559eb,bd5dfcc0,328e69a5,fc787389,74c80d1a,896b0d8e) +,S(117dbfb5,74b7d6fc,d47dc17d,56f5b5da,b864906f,f08d190f,7afe1f9c,fe38c299,4acd2151,30b7144e,437bc923,302640a8,c504712f,6b903b26,bd5db8dd,5d90196b) +,S(b0c31228,431c7eb5,7310706d,950a2a60,eec84ec7,418199f3,f2985a39,8a70a537,a7de9b34,792876ff,e98c3237,260d8237,85e47c,438fe419,a1048b00,b064ff78) +,S(7c210cb9,f493275a,e17e6d5b,517606be,736cbc86,5a5897e0,8d1d1f03,f243946f,17b07523,a8bd185c,ea4a92d8,7af1f1dc,920a7fac,30faedb1,c38e3529,1759c63a) +,S(172e5167,578bed44,6397b519,2b0eca17,177b14b2,f4570aa1,38771610,6ed6e650,9c15752e,4776b805,d63de803,83c73ad3,9d3ba817,c1f88cee,1737cd85,33830ba7) +,S(161afc40,df25fda1,3637317,3dad8046,c95a6ef3,aa9c19ca,47e9ce2a,20030686,5e6fd083,c1265f76,b4b9c819,3a45e0e8,9926f160,b6257759,ab90d6f0,126089ce) +,S(b991f2a8,e18aed33,4d769cf8,b6e39144,2d194e90,ee1518d9,459d3ec3,8a16a85f,1d2ba9a6,b89f71c8,ea169d3e,923d2c1,7e6e590e,21b28e8b,1a0d0cd6,1bcab7b0) +,S(dae0689f,5a6cee77,f796f488,1fb3647c,7d623348,7d5ab502,6f1ca30d,44ca8afc,ee67b670,f730bae2,a15e6964,33be5b95,43ccc1b0,314cdfa8,c6cc8873,c39c329c) +,S(47a58ccd,75f89c15,46a17ba1,4629fb5b,d72805a4,16a12e61,aeafd8ec,8a7e4e41,c15e7fed,1bbb810e,b4be60bb,61fc1f0a,a4bc43b9,73d94767,9b954f3c,29fdd889) +,S(f7ae2fd0,2e3b6ee3,30c0697c,94ef65b6,d7582f69,5c509698,cef09b44,6b040e3c,ade9f8aa,c527139b,c54d8e6d,ddd41ac9,5eb49565,79934ea3,6a9dd8ad,d419ca03) +,S(155f9ea8,9be2ae20,8d7e9c21,4c244a58,ae61cf24,5d5f8dc8,b6c448d9,7c12989d,1204f5f3,840cd19a,fda04abe,6cbf4490,cf5ea60f,4cd17680,8c1ce9d3,65b009ff) +,S(a83cc9c3,933b5f14,fa5348a0,e0d3ee39,e35057b9,13c5a51,89a61663,1e9d74f6,40aa66d7,fad68476,1e2a9c78,e1fbe478,52b91d11,a283e1db,e30d4baf,f072a74a) +,S(ca4951cf,c07b5c23,298a20e0,fa44b554,d7acefbd,bf2a6cce,11fc3ea5,67b5f5df,186a8bd7,6700cafa,c79791a6,b28c799a,35aac545,78586050,b8012c4f,9f4afc2) +,S(cf92dde6,1bad101f,5e71f2a0,dbc2438d,d6760ba5,b84791a9,ae0ce712,9eb6787c,a7d351b7,f3e31e6b,cbcc1f05,329458e2,6c9d8a9d,1137d595,28f53e23,5c3bcbd8) +,S(b1ab0b3e,cfd8283a,3b79df9b,e83e8b37,b06154a9,6e2a687d,8f51985,129c1179,cdb7f2f3,4bab534a,41f4cd97,b812900c,99e97395,3837f058,12d5d0d6,e9506bbf) +,S(a1743329,9cd4c9e8,4fb99295,f3febad9,197296a2,98b30354,24e9524a,e1789ce,40994ef4,4cf53577,6f078088,adcfa6d7,5bee11a4,4f2f3fc4,4282f5d7,f77e1afe) +,S(241b486,4efcac7c,3c7b946a,61a55317,38b31846,607f1e0b,d0a1a2b,d9c0573b,5cce8848,eec61cd3,325f3ba8,fbca403c,2f980159,bad3997c,832f5c98,1fa0d574) +,S(81f88209,64997984,699559ad,f799ab0b,e3efd354,d258137e,d753e3e4,de91b387,8f9fedb8,5bfd7061,1b9e5caa,5f3cc8da,43bda599,afa29967,a32c71e6,aa26d15c) +,S(49d64231,bd2c2145,200793d6,4a2ec254,c22da96b,655706fe,8fbf5d49,464e5a5b,faec8eda,b0d72cad,81df8121,671c6dd9,bf986440,3f1a702b,548d4333,c751e825) +,S(74c14c32,4fad3a41,1c7e1f15,9b740980,6daea24d,9476e938,7e77db6d,66e0ae4,af795203,1fbee5cb,f031a2cb,946e6b65,3ee0165d,abdb89fd,5aa73880,e0641a41) +,S(88bc209c,7342d94c,be3f4215,81383c6c,930f9c19,d09d91b2,a984b6d7,12a1e7c3,acb7d745,76dd8723,89a88866,e287610d,b817ebe6,1581aa23,46ac994c,4c5b3e08) +,S(486c8c68,c81ca88c,df7b93c8,5a1525f4,9bee242,676aae77,c1bd85ce,2a6eb3f8,e0fec94b,35fa97c,d5e64870,6f5e6849,d6249004,fb33e34e,c1add26f,4fe52d54) +,S(4a1efa11,6c588e23,7b5e30fb,5d0ddf37,ba39043d,6e5faf14,e28ceb90,4a681a3,bb196737,3c907165,a8937dd3,1bce476f,65f2acc1,41bd8d53,8fcfbf1a,997b64f4) +,S(d7745c06,c69609ed,db17f30,8efe7bb4,2632e85b,7f7f792f,8da44294,78eb28d2,7575a75,4d1b2bd5,5fc46e11,b1addb1a,5371f007,f702a97c,ad13f082,39b96a73) +,S(bd752f2e,a3db27c6,a6a08ced,df74a87c,dc333d50,fd9995f4,9ca7afa4,2be68def,378f8aaf,67478d20,ba4725ed,26d50c62,ef5c576d,da9c24d7,91dec38b,a5e9491a) +,S(ef588333,15a3c7eb,c6602bff,ace00d5c,55eb304c,7f3301f9,4be457b0,1f224d21,56eb3aab,6253dfbe,3d9a95f7,e843751d,eb52e054,cb5a523f,f46a0c12,b517abd) +,S(2c1de374,7355825d,a09beea4,f42df241,6b495ce,7edf1f1f,c1dc6043,5253727a,98593660,9082f5d8,8f9986bd,f77672a8,5ff92cd0,28d2c588,73a3b1d3,87a150c2) +,S(49f8d71b,23e60140,f50c9001,6f39b2e8,b78e2a22,a88f2535,324ff50f,280daf99,f66bb665,4807e7dc,330ec339,3f6fde20,308111c8,1fe42546,22b93a86,b58b04ec) +,S(365c9f85,754b61ca,2b2a30f9,954f3a52,d18fdf78,db593ceb,6df617ab,189f37be,c43fefe6,aca56bcb,ffa6aced,60a32794,7681b601,22369a3a,f1405a41,5132825e) +,S(1cf2cfe9,e89668ed,c1a446c4,da311cf2,7e2cb53e,1984a310,8e24e8bf,21f1b5af,71e56dac,43c53094,7b78f104,1a685f23,aebc7b12,e2caf803,6731ef5a,20c966b7) +,S(f3303479,62660226,f391c26f,a7e87796,219694f8,e01cbd52,25dce636,b48ca3ec,7dea0bf4,c110e945,dbb59ab8,74f83a42,afa585c8,56a93002,6f48f8fa,62762827) +,S(b689244c,4bc16c88,398664f9,2297ea0,8756a969,1ae7adf0,587c3253,e4b27154,9b488748,1357368c,c97f89a9,b4a75a0,bd7483ae,5c700364,c27d19a8,e450c856) +,S(bf34fcd2,d6b4371c,fef2f874,5e4be021,a78b4302,240d4517,f3eb1140,638cbe41,ce6888d2,7e326eae,8e772914,e1b43bc0,1ef6a238,27f67546,5464e195,5f97ac02) +,S(c2097a6d,231bc796,ce85a594,44c00250,d51ffe63,c45b9cd1,10ed4821,118d74fe,58b06413,fc4ac173,e1e3a85a,ca674885,d1f92b3e,5b99f72b,1350b6a0,bfe4ac87) +,S(874a6222,3d8804f3,a11b1de,3c647aea,e3e81798,40db2618,4d46a330,417afa81,4132a5f6,7986e622,976b181d,5c98f356,b31668e0,ef70f8f,6b13a0ed,af80b429) +,S(ac7c6e8a,1a05d592,8142227a,b0c3ed46,1f28463c,ccaa3b85,b9784e0a,f5bc605c,1a59a195,581e3397,eba60a0a,963f5c71,c107a9db,68794da4,a08b14b3,118a2186) +,S(3f13e2d8,d825a3f6,8875c01,a1715a5f,44bc12b8,2f254a65,575b163a,3333342c,baff6882,fb2c2611,ffef76a6,8b5e2293,6a2e6ecf,d7ba6724,3393fa1c,5825e3b7) +,S(1ebeb354,515e7f39,dc3e2631,51d0630d,aa7e400f,4c9f7cec,4d5f95ec,bfa8fe2d,a2e35cef,c2cdcf,384a2473,c185f3a4,c70724b9,84c72dac,a6ca11f7,ae5c85cf) +,S(c2dcdb65,b211e765,e6c59218,d12b45d8,b47d8f9c,e22b99a0,d2d19a1d,de02b2ab,e0aa5b9c,a960117a,7289326e,9259f886,8722e032,c96b5237,b146f50e,24f3be50) +,S(495d69f7,e2a9d144,8e2ed8a9,c038f6c6,7f360b8,270c9b85,841e8791,64bc0d45,5fb3fe2a,1dabce7b,83b4465d,74fac6f7,c48584be,134283e7,bdf59e0,957088d2) +,S(ceac4569,2139e4f9,701be43d,8da3f515,270a9477,bad5969d,2037ac87,3453bab2,bd1fd9d8,15f4e872,b602f2bd,9604b6b2,f385ea40,d5520b3b,ca32c160,44908070) +,S(7ed1d0b9,3196b8c0,1cd284cb,c0a206af,4e6dba1a,d6cd406,82f55897,9407e0d,e95a3ac9,6ba60be6,22f4ee73,2d75a9c5,11018ec0,d840ce0a,f1bca18a,1d2801c5) +,S(72fb366,4f42cec8,940fd436,5ba7cc97,ac5fe4b2,131250fb,a24b7178,a1040b04,c182b1a,93a211f5,f5de5c9b,4b52ce90,63d551dc,a21ee9bc,dbba2653,c47dce41) +,S(dd06cd8c,9ca91e45,d6720043,d3e6d857,4690192d,59154309,2160f04,6008beea,cdc17327,6eebea3b,f9fa8a7e,e83ebd81,4189705b,900b3ce2,72b85dc6,c41e7ecd) +,S(80f6129,e90a7f0f,1e650439,8bfedb10,3c708c43,2e2c743,da8a9a99,3978cd13,74d97812,145feff4,a3ef594f,85b644a6,d95a3082,3828c00f,e7226d4f,39deb82a) +,S(56af34da,f56b508a,eee970af,bafb9dd2,b48f83c,4c051b24,f5bc27e7,105995b6,d41dfe52,9e788e19,89105a4c,c219dcd3,decca65a,dcc5f34d,8a3ab0de,d1ae68bf) +,S(820225ba,795565b4,c45e29c,4689d91c,148cf693,a1a1c1f6,ca33c44e,4e3f91ee,3622ae5b,c89b9c2e,66bf5a3,17826928,11a0f4f1,4b387334,b24028be,ba4b7db4) +,S(7925fdbf,cfcc202a,895a1b7c,a8c7f09f,ad388db1,5652b703,3bf232be,a2ea57d2,46f7ef01,c9cb7dc9,1559cf5e,c6d3f5f2,1ebcc21c,a8414d0a,9dc37309,4e90525c) +,S(4b224b6a,f38ae731,5b316eb,8dbd2b1e,6638794a,9bff79f6,1027f60d,5d81808,3143ea08,b1633002,bbb2ad2e,11ffe5b4,b6dc6888,1fc669d4,23ff9cb1,595c3dac) +,S(2c0767fa,4135f4eb,602a1a36,e6ce488e,2c1bdd64,aa4113cd,28daf20c,687380ee,ddc3effc,e78d3061,7cc2da71,1373de0,6a65d2b,1050b89a,5f71be3b,fbc16a7) +,S(83837d3e,e7179a84,ef6ed2d9,41a9f835,37b5a2c2,5d511ef1,982d9bba,50a7ae7c,62201e16,60957b4c,a86f1a49,5a6027b5,fe2294f2,7946c8d8,556024b6,e3b66dbe) +,S(cf075b34,e560f057,53a8e011,835bf1da,25e50d9e,1d70dd9b,da427109,5895387a,c73209d0,330ff4d2,38e19f6f,33df38d7,2b11d8dd,180d6a14,4b07184,a2127018) +,S(d583f8ff,9b1875d1,ef468030,e3ebb7,9d06ba2c,1008329d,cb01b883,7c8931a4,f8cfcf1c,3f092ba,6bfcfcc6,9596b508,aaac7c9c,333ba58c,d55be53c,6fae3292) +,S(551fe9b6,21d2389b,fc185372,342edb1b,27568bb2,da1fe220,3431b792,43eaefaa,6c76904a,e48563cc,c6aff505,7f31119f,ff48e5fe,971223d8,c3badd02,563c24e0) +,S(1bb778fc,74062f24,b0962be7,bce7c990,51f06394,bc8e6da9,a9d63f6e,d16a80b2,2754f53d,2f4ed167,3d2700b6,8ff036fa,60e9352f,1dd7bbdb,14be1740,61d88318) +,S(a150af7,1261582f,8949f1a9,3ff8d539,aa0744c4,e97cfbe6,e4a3fe10,e7e364aa,e22a1afe,a68671c2,f9e12471,56e1bf47,100737ed,5f96fab9,9e0df721,eaea4773) +,S(c94ec3df,727fdf91,d7963a03,8e93d68,835ed2bc,2578780b,7242e15a,e72e2a9e,a476a0e9,7bda53c5,46312b35,f0fad09e,11b2810,b3f570d5,a934d21,7152009c) +,S(3366b9db,ebe13231,7c542739,4235a72a,f186bda1,784c7f25,5f8b65f7,f146875a,5ea2478,95f52889,42321383,5439195e,6b620619,20171862,eea32726,1345d3cf) +,S(616c8e6c,e52265ec,cb85ef36,5092b2bb,58bec6be,444c2373,974c38e5,e0e8ba25,4d3d5543,3d6e258c,d8b286f4,5f41249a,724a9890,2f1ef3ca,ba049bf1,cdaa0970) +,S(74df2177,2e38e84e,8b81d86e,f45dc4a7,2617b3a6,32094b1d,432291f,6a651827,4c32baf,30f09527,fc4abc6f,9c9b9a57,2a9ccf1c,6a1b360c,48746d8c,22e01334) +,S(1814a1bf,76b74532,979966d1,5ef42faa,532f8dd9,bee0cce5,d68fc500,21accbd1,5f5df5ff,da9d439,b2205ad2,5fa93b9b,7af1746e,9b2eef3,154bfbad,acd46bdc) +,S(28911e94,98ff7526,badf2287,8e85fbdb,5f444d66,c422a975,c7476c02,b98625cc,5a341cf3,5cf26006,9d869542,fb221ba6,eac150bb,e3809ada,5e4c903c,1e638537) +,S(d5acff71,e82a455c,3a1ce292,689e8686,f441ce1b,e644e79a,bd6d0efe,29270865,aac6d48f,1b46e970,a044971a,ad13f033,ca8cde96,958d870e,dc7d80,1d26d5e8) +,S(5cf51c1b,2210d85,9d765e17,32109514,8f03fc57,51004b6a,91f098e2,e2711596,1eeb19e0,610df459,2c31e58e,aa2a2148,17fe9ee,a3995838,f395bdcf,26d5c3b3) +#endif +#if WINDOW_G > 14 +,S(21456873,b58e3687,52c75800,8d3bcae,9efaf1f5,c5727842,25e3d854,8fd421cb,a2f2d10,c85e8a0f,4a136ad0,5df991ce,7d3c5585,a263d5cf,da50a4f4,3db76cb0) +,S(c10de5c0,51ae2d73,28ac06ca,cca840b4,ed7ab204,21c6122f,1d68fe7f,7893d38d,ee30e086,a891484,2ad4041,f9ab9c57,cff1a315,f0642d31,b31c2914,faac99e0) +,S(be778032,f12c1b77,9bba3d9e,d290ca90,30ac7050,bdd77a2,7eac09be,eea65c0b,8657348b,a1e27a63,1dd2a54b,e2d5270f,4cca817a,219c5378,4d4f73ba,2c932e63) +,S(a05e7551,f8f090c1,bfc8ffcf,fbf7fd57,9d033163,67d5ee64,b85c4f69,33d0ff6b,8eba561a,f42d43c4,99e1fafd,43b49698,a8f3babc,c9c94c4c,4822cbed,a741b309) +,S(6e4d47f6,b17501b8,f3b220d3,83cf5f11,a395c0a,f0023988,c4e7b8b0,ef66ed23,e01c4330,16e3e6a9,535f1d40,905e9b3f,8be9f05c,b53e6143,e81091f5,4b76b57d) +,S(f626750e,b7eacac1,7cf24afd,43345019,c6c32292,c72503ad,d4125d40,67b7e66e,d669f903,67f407d4,5307578c,ee91fa01,f030bb9c,b66c7111,34b91757,1c993f4d) +,S(437acc60,c555f7c4,778595af,db4676a2,1fc8b3cf,2c8538f,cecbae12,811511f1,985fd2c0,7385fba8,4cf25a1f,46cd2c3e,de8dd359,1c6d20ac,584b4a8d,8a65dd67) +,S(8d50555c,6245e43c,a619fb76,ce45441e,dc585c7c,4fb2f33e,1c07965e,d4e35f5c,ea828c37,331ad0aa,44d168ce,c328a9f1,c7deaa35,47ee4757,c776fc46,439ea5d2) +,S(87f5f6ae,580ebfe7,1b2c19c8,cfe770ce,cf8d4223,62718914,eb853f1b,7f4cbaec,eb61df09,12e06f58,ed6d85da,7240dd72,aad00c3c,6a3a9c11,1f378664,cf359386) +,S(c1cfcacc,b95e6577,4e2a7d12,3cb0071b,3325c27c,5d58beb6,781a30d,ff6306d3,fa9ba55,cd95e721,12d98a19,8e3769ef,8cebb355,dde5b62e,e6f3b8e8,52a3e81d) +,S(d68bf50c,89f25969,a765af90,854bdb89,d67acdf8,f1e16bff,64868338,8b88e311,b62d1866,55835dcf,8064e13d,aca1e896,2157576,a02e178c,78d27c99,9cce81a9) +,S(b8d7c25b,8c20438e,702197cd,a3bfc05d,c6577717,c9b6a527,15ef84a0,2c0df867,316b527d,4be0e62a,2015f15a,17a412fb,72ebbffd,b02e53ab,6e5e9791,b771b45c) +,S(d98b3403,a299106c,9c6fd52a,8abd9ee5,cea9ab0f,17a15b4,7eafc809,c34e356f,bebcd24a,9300c739,9bb3af8c,12fa813c,78c6838,dbfa00a7,4ab09669,4df9700c) +,S(dcd15a1a,7cccd53e,db08c2b2,c6367126,de55cc46,a4eaf5d3,335ccff2,1238bbdc,7de57d26,bf74764d,bcc7e15b,72ec272c,58061f47,cc0715ec,48bcc032,60e63a81) +,S(2f6a844e,e758e3cc,8f299e01,ba5753a9,d35e6c51,6a87a683,8f5ce28a,fca17c62,5c29172f,f907c0ef,95e78768,2625c8ff,27e26eb2,cd86df92,b9371203,8b332e73) +,S(e63add7a,e511eed6,93f62498,17f89221,c7a3a909,253faef9,23f37a64,9e2a2f67,c392df22,5ffa1438,c3d10c6f,813c3956,4f94367d,6fa26c63,1d562049,99a77e0a) +,S(f9955ec9,20fc68b6,cfcd163f,34bd033f,4ed7dc8c,30da7f16,e41adb,908c9b7b,4dfc49e4,ab583975,90bdfae8,56ce97af,91c92f60,a17df9fe,9ee923b9,9fe2cf14) +,S(d974d713,bce1db76,c5ea1143,b63ace02,5a2362ae,fe6b84b0,ebb0c49f,22341d7c,991181fa,3ae5f188,38a94047,9ea9e4dc,e8ff9366,78583189,bc340d2b,40481577) +,S(4f0af691,424d441c,698dfa3e,c3e56bea,e2c52fa3,bfd9ca45,763d7805,33a3037e,caac2486,81590630,b0c67160,89db929d,26a4e15e,b06aa9a5,2e19c92f,a9f9817e) +,S(ef6300a0,70060df2,3f9818c1,6ff4d315,fb1c7d4d,fef41e85,c96760d1,3cea49e7,a0622360,2def9738,d87803e8,6405bc00,88afa82e,39666246,10f5d935,993334fd) +,S(fef30954,1cd2a90e,1d473ae3,699ea7cc,ef69fe75,54f7d710,33b1f23,c9e96886,c2479c48,59423c87,5d76f6b1,5c671ed5,c9489af7,79266341,4ca68675,cab64fec) +,S(457cdf17,b1230409,c3d35a88,390f1bef,859994df,3ba21d2e,ba5f826b,6dcb4fc6,5aff71af,708863df,3074c236,446ddd33,5695c0c1,a45bc482,e0787788,a0e4ede3) +,S(a66e86f,fe0e9cfc,686ab0fa,11b641f4,28499f3,450d69a0,dbce1895,6181ea39,de0cb1e5,ca720556,7c6756ce,868df6b,5174887a,61afd354,653e759d,b3e3ec64) +,S(ddad5a09,c37f0de6,67eab59b,fa2cb47f,b79bf6f5,b8b0403b,f689acfa,627f1014,8aed1b91,c9e567b1,9806082c,79ec433e,1a152279,69df00bd,239fd8f3,f8e8d385) +,S(45c5156b,f1439eaf,16a43b39,5c0a393f,a674e17f,76f96d53,9aa6c99b,49042c42,c9bcc2ed,c5b5c8c4,b17d633c,90d45b9e,1583898e,41bb9d90,1d1b5097,57f5f516) +,S(c83b3c9b,564906dd,60049c3e,9f6ed0fa,b848366c,3b93650b,7923e9ab,8172a8d7,5709dd74,1326ab97,8853304c,a02315f1,1f0bed5e,bffab6f,467816b4,4c60a332) +,S(a857354,dd08dc83,ce7b0324,7594e4c6,99692c95,9a4887e5,344cd0a8,7970174e,becb7001,ddafd72f,1f845579,ca8d56d7,2145c8a9,be816924,957ba324,3c396691) +,S(8bba6b47,88259a19,578adb4b,6f7f51f8,d09eb70,ea790d62,abb2ed45,84f94f33,afa42da3,8526a485,60919f12,d30549e7,1d19608a,81f4cf6e,8e49cb00,2492b143) +,S(bb4d27eb,412e73ca,d2697d6c,46a143ca,e4420ec9,30440025,47aefb71,c99a53d0,9ec07a19,842a1e5c,9cf9f76b,55ad98d1,9485e683,b6b6700b,c905e190,bd9da19) +,S(af6f6827,d197688,f00dd3f5,ded1849c,11fc2abd,f90fc7f2,18b33776,e5753277,f9227693,c9405a2b,10e9b725,aac7ed35,eef4281b,ca04e,bd75b143,89323646) +,S(40453814,445cc67b,e7a4b71a,55e0b993,2b8b3477,f093df6b,f27f55a2,8bad2e1b,ec71d7d5,4e823687,d01d7558,6de9a1a3,7edc927f,e221941f,46051747,a69baab4) +,S(f35a6c29,bd596f0d,93cabc3d,c07b5f68,300f6ab,8ecde5d5,da8299d,112c7bcf,73fe2c46,e1b13112,718526f9,7a39f1f0,3d47ced1,84a2e4cf,1e32c168,1c470121) +,S(ecc7ce7e,bbe5da1a,596bf41c,4d19b51,b77f7019,bc431aba,e5ecea57,b095fa93,1ab89d03,9e7c6ddc,7751c9bc,4eb84ed1,b077cc8e,dc828ffa,37426609,e089c8aa) +,S(559820d6,bb3e47e,f68f48fe,1259da06,cd0b380,1f6bedb7,970c079b,7e373bd9,2373137a,a4d88574,151540cd,ab8cbdbc,5831fb7c,4b901c27,8c9a593,172a64e0) +,S(9c9b7e18,4e76bd16,856addae,9352590e,310d653d,809ec800,415f3c64,149be4a0,182cd167,55eedfc3,21d71199,7543b26b,d08047d7,c9363e23,20bb9516,da37a146) +,S(88e4e3d2,2cc3e6f6,19e62ffa,c7a4aff,63b16733,202e5410,52cedde6,9cda2733,ec6e32aa,498a7a30,e1c47136,5671d356,bf174630,d1b984ec,d9453e24,d275e067) +,S(7f2f8043,8e7d7fcf,593e337,a91b06ad,3ad1c461,fcee7bd5,82df516d,cb1c198f,2cb484f7,5a4472b3,230369f0,79f5e654,8dfdce60,98c4e561,a1310224,13dc1d80) +,S(fe0c5945,c5e67d74,ed498120,dae194cf,33a3fe5b,ef0f1ac,2c64c292,59827d7c,4ebff1a1,7d59c8c,469fad0c,79ef819a,8a897ce4,c0fa1121,741c03f6,cab0f659) +,S(ac0662e0,1be37c28,be457bc9,4af19e72,34a9d3e0,8667c009,ec58ee79,7e539642,b3adc375,bdc81a76,8385c9e7,2ecec61e,9b2b21d2,55f65450,c5956187,837f3767) +,S(15e0f7bc,d74e77a,af933c13,6cdc5b1a,1622de61,251090a8,f8509b05,d7dd527e,4d51a063,1fb81f2,fa1ef534,5fa306c1,a0c64f94,66961cc8,b574b06e,646767ea) +,S(e4119d0f,b79bd8c,e1687abd,4ad63790,814f9972,50fac9a4,f1b52d71,93ce282c,cabde097,8f9a566e,32ab229e,63bdfbd3,89f01378,c7d27b0b,f101cc3b,36bf3fe6) +,S(27a5b030,2005a21,5d89c0b3,4e4ee323,5e94742c,262a89b9,29e286c0,ab8e3c24,4548d58c,3792f7fb,18238cfc,993fdb26,f755379c,e0ed1b0,4df26132,8f987a02) +,S(a18ec59f,7621c8a0,59bcad5a,12d3f536,142d5c94,4c7a54d8,b9206132,d993e08,abd8204e,867035ac,8cde70cc,f7daa29b,d47b888e,1faa9be9,b20744e6,ec5a43a) +,S(9bcc19a6,71b787cb,9da72c91,8d7b2264,b9497ab6,313de85d,c3efbeaa,2f492219,4bea790f,67b8100b,9ad9a301,61e3bd9e,583daec8,b77f4628,568ed554,70894bbc) +,S(7ccb2731,cc3eb3a3,36d36af7,f44a0a64,e23aa0b1,12ba0e6a,11280c5e,1ab36205,8d60552,1813ede4,dcfcbe46,c75c5aef,7ec40c69,99cf301b,28b0e875,3031c6af) +,S(94107245,fa13427a,7d21f7c7,cfe0c4e3,2943821f,da0f77b4,23fde091,ba596939,89846c62,7868b1c7,4c546492,dd4821d0,cfe15fae,36896af4,547deea9,295ccf84) +,S(959bf7d3,989d3460,5f4b8f3c,cd12ed86,cd0a2a93,a8d4e1fa,aeb5d6bf,24f1115d,7cc703f3,5417d7b2,b7229626,558be68c,66a915fd,7cc52829,dc98c81e,c0162b7f) +,S(8e92173,ff2de4bd,f468e2cb,fbf6264c,5fff8f23,2fb57e1d,2a07ee75,3b45e7ac,2655133c,8833040e,4ef4c98c,e5c75818,be781f42,cc0b7314,7baa3ef7,99cc017d) +,S(4acbd2e6,98f349cc,42998c5c,53de7b6,3dc29b1,c2b8a569,48cd3489,190f0255,8837f8d4,15b3551,4544878e,1a71922a,ba4a0790,22c74b60,325c6f49,a411b978) +,S(f79cfd2e,b63f6be3,bf0f12d1,fea3524b,1188959a,425f5e38,fa569648,df433fce,9e412cf1,698805e2,a92114de,5694a925,17c31f49,ddca7e0d,b2d83d80,74d92b2b) +,S(41910c81,78f7a61d,7957c065,5d7e2596,a0e6d5e9,a0a9cf23,28f23569,5d818c76,26b318c7,e8a880ec,94c59a6b,d6f1cad,84031d0,e62ded95,1e265ba8,c3603367) +,S(5a864466,9fd276ac,ad8782d3,51bc27b4,9445835b,75f70a80,2ef42a0b,4885cfe3,b2510d98,60be4e66,edb8c935,3c8ff8d,2e008c37,6dd271b0,1f77276b,f3a5a48b) +,S(5e3974b8,1b87348e,e3ae5c83,2dc56d1,85004b16,90445b74,1b8e262b,ecfe0e01,63f644bc,397a1809,a4d57ba8,f18f0372,cd4fd083,1c3d3449,1ef2a654,46275568) +,S(154e9c88,589ff342,6fe2eb5e,dd6e1db,e253498b,2c0b3e0,52e84211,caf9cc4e,3a897093,df7d31c3,754f84c2,68b0594e,85cfd4a7,c2731fce,e01cc3bc,2bbc383e) +,S(b95aa134,e48967f1,bd3a7a48,d89d550e,3c3c3c6b,3c73de48,f2e6fba1,81c93faa,b6f2d3b8,8d0821b0,6b1134d7,ddc898fc,e84898fc,4719f8aa,e3570daf,168b03e8) +,S(316564c4,ff00502c,f159db79,62984516,4d6d24c9,1f20ba73,66669808,95e58b92,6110a6d5,39ddc,d4185b72,cc576b7d,a4577e80,3dd47a92,d4bf346d,ec5905f5) +,S(222b179c,29ff89ee,d6f2a0d2,d38c6246,33237c5f,7803d8c5,e4315f93,6a9bd225,8bddc333,6eeb3cee,a0c3ac01,e7caf4f2,f44a50f2,587e3e31,7046c85b,a57cb6f4) +,S(10347fab,33fb4ee5,6cbf75ac,6cb768dd,f51f9b0c,5498466a,6679439a,32048904,a351f45e,30f32caf,e10d99f6,9e9e4be,c3f4aa22,5388984,bc45bb78,c0c5148e) +,S(7048c0f3,1e600042,4544e9f6,e26783d7,baeb411b,a2c29c7a,5aed8953,bf831629,4683ad69,1d9f14a3,f67131bc,55d28c61,1b78b2aa,329668fa,8fc41736,c95840f7) +,S(a65f7371,b35de05a,78b71171,40ea9e02,7b777785,13fdcc3b,484ad9a0,4bcfa1a3,41d1268a,77b31744,5a270f73,956bbcfa,3f291770,d248d1e3,364b4aba,72b961c6) +,S(dfa35552,8518d1da,ee31f04a,2673d053,11db4eee,cdc81f15,bce79c64,3267a315,19ed047b,4fa52430,18863cd1,d8ade0d7,dad60ecf,10b767ab,52f5cef5,9eea9a8a) +,S(663ec2a0,80564f1f,943a25b7,2f60d3a4,17b62130,3015e17c,85782460,c601a48d,fce25852,5a4dbb52,4057f8fb,a4393116,ebcffea5,5ae3459d,3d8b19b6,a0387232) +,S(cc714eaf,26f9960a,b2cb139b,d2fa0928,7a309c9b,ea852537,e10b6333,2912431a,c1c5c0c,48c98ea9,b57eb13c,fa64146e,67a95569,b45b8643,7752b037,9269d070) +,S(a6ceed6a,bb8f1ac6,13799527,38a19b35,ece44537,e74e9185,a4ce3939,c14f8e77,c0090ebe,d410f2e0,2e56b4b6,adfd5495,488c2930,2c5e7a61,3926880a,88beedae) +,S(e0b0c34c,8d4f77f1,dd09ea34,8b0ec683,cb5ca777,6720546e,27ffcf55,a5e5bded,ed4adb20,ab5da65c,2d1d5d68,9ad63ebd,90170904,65f7ad03,1bf3d811,f407d09f) +,S(2362ff0b,9a2d0f7f,d779a83,8b26da93,65730d32,bc4411ef,fc4fd182,d9e486a6,d3eb4ee4,42bd6157,557e4e9a,dcc9c103,892ca05f,5c5af804,f736ecee,d8a6fc3e) +,S(2f1636ab,a1516634,a00a464b,aa30c507,af25e83,3f7f6fdd,ccf0706e,d5fb57a0,a664c955,c24a54ee,d56f1aaf,fc853c2f,6a81c53f,1ee36c48,46b26452,fcbcc054) +,S(243a8fff,6230829c,73368eed,fb4d62f5,bf478b4f,1752400a,91ac177f,70c303ac,29318b21,9f371dfe,4048c4b1,2f5317cc,b2e9c44f,ed72b537,a2f56d06,65229235) +,S(b17182a8,5f442a65,839230ca,5cc8f67e,835e54b,e0296c6d,e2b62298,74ae804e,11a4fbcb,9f6f8775,9d131a81,939e0125,7f7b4fca,e36ea644,4da3cc16,a161e5aa) +,S(5c9abfd1,9cae1a47,bebdd324,d11cf758,a1224471,6e83795,e42ff5f5,a8142a6f,38a38db6,f64d9d07,4a08a909,28768bdd,adbdd858,79ec847d,28c94dd3,a122e0d) +,S(44a8a4f2,ec3dd02c,226ae0af,d73a12b9,726e03c3,2a41099e,b70cdce2,767e390c,b7e18dd1,e6adcd88,1d1dbead,e1c83ed0,1f1d7b2e,50246539,b6bc2811,d63233b7) +,S(8e477766,c4de01c2,5cc17218,7f3cb6f0,dbc8eb54,fa888911,33131b7c,219e6e13,ba8f447f,10286c86,d1330c82,2647a999,b4b60e56,4131986,a6c05128,a480d83) +,S(c5f862ce,dd253879,8100eea2,92e860fa,321d7709,596a4dfd,6faf6345,82fcd3ae,81ab1260,684fd3d8,1b47c5d4,d5f0c319,93ae7db4,fed1a781,61d9d9d4,50ff831e) +,S(a674db01,836fe328,8566d977,753ebc2e,d55f0116,5ebf0349,51caecd6,ebf899db,12ae8327,737f7db3,56e26fb4,1ab306e0,2294136d,71206f43,78300b95,60f580f) +,S(b0cf999e,f11dea14,3e6b6254,aedf30aa,ba1e8e92,96c500f5,481eee45,a0e15adb,e4970d00,e02adc6a,be5e2433,7e017dab,17a61de1,d977ee99,969591,91a5563a) +,S(b3615f09,29f9b2a0,6a4c68ef,c844ae5,54959ffd,f03f9266,f918f16a,b517380b,31a7675b,83677cc6,ca87c525,27fbba60,d06ea317,fefb17a4,7d7a4242,3595f1d4) +,S(7be97bde,b271f807,96930353,b82c3cd2,c6cce373,198572c7,d289511d,7cf262a1,1af89a8b,cfa4a399,bce5bc21,e26c97d0,5126f8c3,211d8051,626c4f2e,c6f128b6) +,S(692e447d,df9c77f0,2db1d34,101bc355,519d40b1,3fff2cbc,aafeb555,29473d4d,d21950ec,fb7bb723,4e4e48c1,98b93a10,8a85bb5d,5228116e,da6f5cfd,8968fda) +,S(61b6fe3a,21b0bbd2,dfccad11,5123db4a,98058bbd,4f8a61b5,e416b03d,b1414243,75d3dabe,f3e2266d,65d408ef,af6b32da,7591f08c,7fe7de,ff20b9d6,7a3a8a8c) +,S(fe0f9f,aa49555,2afe97f,8462869e,e8a584ac,3c3fba4b,886208f9,fe260ec2,a9c0fd7b,cfa239a0,299afa47,e2861197,f382a331,7607e129,967bb22d,3ae3077) +,S(7f7c9a0,a240180d,c645e77e,c628bff8,fff90d48,c85d2fdb,faaf76d9,b93e26a2,1b12ec74,f801fbe3,ddeeb37b,c940c605,84b0ef14,85e9d888,f3f81c05,607d1222) +,S(3e444ef3,b77b49c2,75aa0524,77da59d5,a68dc6ee,6288e2ce,140512,92e54c60,e211ba3c,6860a898,9e1ee04c,ea9ddf52,25ed88a2,c6ecd9fe,3c2fd500,88367b7) +,S(4844747d,d33f7e4d,39aee79f,138b5fd9,223b4e51,c86e4894,c917a3d4,746d824,68cd147,2560b5d1,6b9bf538,b7f1e193,d2f220f9,ed9f742b,d36a003c,28e2357c) +,S(f0211c1c,8f87fb96,bd591255,6a799865,7382380d,6b5b020d,9095f482,4e8f531d,649112c5,9cf7a8cf,7f9d920a,6b6d1102,d92441f1,1bde7561,9cda8eb4,a26d7493) +,S(918118dc,15520a31,c715adcb,3e02ca4a,4d77c92e,310ef057,3f9c7a4b,d5d2d54d,37d2c580,337de379,925506ab,c6b7d9,4a61584f,4cb2179d,560d3a7b,406873af) +,S(78734d5d,833a5d24,5546e5bf,9d3e5ce5,61f818c4,a4c90fab,c8ae4630,a2c307f4,4b150c27,e054fdad,73506310,80eb5308,b39c6861,23b851ba,aa2349f2,c26b5981) +,S(ee988075,34a02de2,9fda41b1,c821c41d,3ddd5d85,9c5a4ce0,d14ad743,25943f13,ba7fda65,4389494b,40a9fbbb,4b72739b,6f85effe,245b895c,b8c7e423,8d6b7973) +,S(4a821b26,bd1326a8,26be6fec,b90229f7,d7a37a2a,c49e97a2,5bddabef,e854e509,8a645892,37ea07bd,929aee46,6fad63bb,5612de36,cb951200,b9faaec8,b11bfdd8) +,S(23658500,fe8b5ddb,93dd3b50,a46f9914,d04af9d2,2a786be5,9c0fa7be,7c3eb2bf,840e5cca,f9f3e8bf,fe51c1d0,a84de234,d7122cef,fa1f0ed8,9f1701ff,d17c40d3) +,S(71dccb97,7f8e77f5,a03c8e8d,e4b2a30,11d01e13,549949ff,42af13cf,d7ecf208,b70ad10a,5f03ad94,dc8d91dc,e7797eca,6ace74cb,4715bf6d,2b628bd5,f767f34f) +,S(6174a230,e15b2584,7a25217b,9d95f07a,e183f2e4,74d0503e,108994cd,69d20e6b,18cda952,ccd68c2a,5d78060c,6849fb21,e2379b48,425003c0,6f148a34,bf876efb) +,S(57cfc1ee,38d905d2,897dee9d,105fd4b6,90967618,2a2a5d3d,70781cc,9134bb97,bf6b5fa6,4de9110e,7d26e67f,65cd2765,fcedc182,1627243d,a85bc955,2bdcac33) +,S(26605be2,e03705ea,7f199cad,d4d6e711,9805453d,6d4feea5,ec2ae3f8,cdba59c,ced5f775,b62bddf,5935acd,9786847c,e7825da1,5807b117,7459f51b,72fe5774) +,S(6d89e2a6,9ff96c49,a3110a5e,7e17421c,4ca2d07e,6e807a3a,98d79954,7fde2457,4835767c,8f96da1e,78f1788,d9764dea,e4e4a0cd,238ff35d,851ee7d0,9c915df3) +,S(e3607608,338f4229,d77c51fa,cef2697e,f6010c8d,5138e0c6,2b724ad5,6ae79a74,da6a0617,a42d6ccb,44155f27,7c56c58d,42d6037,d234f1ee,ffd720d9,2ae23373) +,S(c8cf20c0,c12de1ba,1ad4dd44,46f76533,7cfefc3e,213c426a,e0cd25ee,2e8bea9a,ef7285a5,ea7393a0,cd78ea31,ef74f605,3d3312ba,f17bf6a0,77b919b2,33227ed2) +,S(f3d2870,fe782b7e,26a1a4f,9c7c3ba9,30e0ee0d,df6593f1,9bf11509,1fa63477,e470c266,ae26fadc,b2780985,9b9d60ca,fcbd9de3,5dfdfb67,9f4fd450,4ed6f3fc) +,S(702a6778,bdf2f3bc,4ee954d8,1e7acda3,1df7127e,a7920e59,a7028253,9019c6c9,2ce19ef9,10d773d0,cc32a503,83f9e968,ca3e407c,c1dfb652,377d08dd,1dca5b96) +,S(ea85cddf,4f79d7d2,a5db06ff,f1c1e3b0,87b0242e,1d7de713,53d8f73e,1bc83888,3806d40d,ccd867c2,2d166056,77d2d64f,d7433dd4,b1e83e25,91860599,2e66fb83) +,S(d0631e8b,30ef6467,de6af60e,61b99112,1949b324,786ff1c2,fe633249,a832aec5,deaf31e3,703df166,b0f92ee4,eff1cc02,684911dd,40054bed,4b21d4da,17f4c05c) +,S(b56cb88b,617a7104,c205e089,b83320c0,5dce438a,50411e5a,d342e9,8b258a3b,757edc72,52d76fbc,24a5515e,417625ae,11db0072,24c02fe1,1e249064,b5127800) +,S(e91ffec8,bc3fa3bf,b0a600b6,89db92ea,7c3ad411,d025a42,95570596,f44702a8,38fd56ef,1f7b476b,2e36aff7,73e190b8,61ec6370,f6ae524,d5dea16d,cc4833c1) +,S(4cd9e6c4,8bf7b1ea,9f855b14,fc904ad4,5e3bb71a,5745b449,e1ab3165,3bdf53b1,b5a075d5,ca9060b7,7d3a00c3,6fbc6404,88df6846,c712afdb,1115798,f382b4e9) +,S(abb07ce9,c4b711ab,d01a12ba,45f0040d,e3ff5a4,e59140d7,cf4ff289,42b5cde0,a8a0e68,350a88b2,397a162f,fc969d28,ed937f60,4704dcf5,2faf755b,d21f503b) +,S(95fac51a,c9deb5d2,b794423,a71cd282,b0604afa,bb3160ec,557a8b74,c578f18b,da99fcf2,abf21cca,c6f4e89b,de2a6e9e,ad199cb4,b97f4122,4f68bb47,b7caead8) +,S(2ecd3e62,b79b4f69,609f33f,2042b56,bad2a9a4,18e2852e,9e8f0d41,1fba8ab9,ef964275,95061068,478e11ec,942b6a2d,99e5b557,830ed0c2,c4291305,41104046) +,S(437d446a,d139d88a,b5437f06,6fdf8acf,7b538798,6baf9551,a414fd4f,101a6440,6c532bb3,38a34a07,28d3273f,e9d9c70,aa79c484,6cb337ed,b41f6029,1e2caa13) +,S(63149b01,94b4955c,1b7e9d51,24e7e7d0,4bb5d902,7e86e63d,f3add9c4,8ab2a44c,c867234b,b4ed27d0,dcdf544,be060a12,4c460c5,7a9312b1,46d2bb61,fdfa6c1a) +,S(78e509ae,99e6469c,41c44dd6,868ee402,8fda1cf8,2c83be01,9fdf912c,1e638bfa,ff3b584c,5ffcb1af,4980e2b3,ace00f12,3be630be,cd91d045,4e031120,245e92be) +,S(cb1d5bf3,f97b2bc4,bb33babc,72de78ea,34cd0a0c,e841cb55,7b3dbc98,61e1544d,e619e8a,dac79a61,eb46add0,96bbcbe1,31d33a6e,b6c99220,123dc9ac,3f27df60) +,S(3247eaa3,9ed5b604,d3038127,9661f350,53ddad8b,8e29d0fa,5c27ed6f,551cf374,1d29a713,d7dd351d,87175b49,afab518b,f268ce81,7fa39ccb,f08b3c58,de1e5378) +,S(d493ab52,8d6c627e,aa3dbd3c,8d809ba9,8a9fe920,307a0c66,2917f8cf,f68b3941,30e4336d,e1621a2f,fe7247ff,157a37c1,edc4f83b,e9fbd84c,af4f96b,8997c7ad) +,S(8dfac8d8,b65eec8a,a65f96b5,571830d9,22469450,42fb2baa,f76a4db2,ea5258c6,c204e9c0,72f643a4,1f53d8fa,dc66e3f0,fdf0d000,c27a5492,6323b2bf,b376af16) +,S(331ca867,1098b530,1b1e6f12,d231793c,de67de5c,640f0a1b,fbfe485e,5d2adff9,b52fb89,3a466c80,ae61d534,cb03fde5,95078592,1da21e0a,a8103d7c,c2e3d8f3) +,S(f34a338b,bb05db6d,55beb49e,526c9d49,fd515792,f407397d,3b520fda,3ffcf26d,6a6921c,4831df6d,91e7cafe,869c4274,faada27d,9d09175e,e86a0b9e,dd70910a) +,S(56d97dfb,284c7417,2e08fb73,3a2d0305,ff774495,2f229bed,cd36e7ff,6058e75f,3775df48,aeaafe14,a8005434,dfc19538,2d10ca90,d05bcba8,edddb2ce,4a4ed7d1) +,S(70e668b0,e854cd8d,d3469cc8,ffab1709,2d9341c4,4831196d,72d9673b,43dfbf7a,60567ba3,5546cd93,f2c356e3,927dc89a,c43fdf50,5e33ca9b,52552587,c905e311) +,S(675af615,9fb50100,56ebed3f,4c9784d1,17538d58,afc5ec75,fa93ee00,29d32390,c501aa90,4f9dfa9d,78107844,983e17b4,34872e3a,a94e37fb,6677186c,fa1abdc1) +,S(99f8e5cc,86ac5587,81a67270,3059442,88b19703,551474a9,9d433d6f,37672d90,30148ff0,9fa33b4f,f4410d57,9afcb705,c1146663,bdbd0fbc,8434f4f8,eacb09dd) +,S(8dcec207,3d74b9bb,bd47f52c,394b263b,e71881af,363bcd80,9adc345c,b9ce9892,d8bc1ef1,79ac54e2,68a685a4,db35e3cb,5b6774d8,e8d6b738,f4d65b66,86782a5d) +,S(d83f7e8d,adf27d5a,f8502d8,3ab81001,a2219805,d942cbfc,76db2764,c4235773,48472e1b,2609f81a,8b12326b,1b19b422,763b0ed9,d6fd67,51751c2f,8aa46c2) +,S(e6f8bcf5,d9ea8209,cb46ab60,643555e0,d0569f3a,dc62ac76,edc09a43,e02fea2a,7974cbc9,5a9dde1f,34560043,7b628eac,3641498e,612c19d9,9a79ae4c,cbde2d82) +,S(d2f30716,e4c1a665,a37d0606,3987d0c5,c19c072b,f1cca8ae,cd0e4d8,6a9e8c1a,e26e6d9,9fbd198a,ab83d0aa,ea53228b,539efd37,ee7ee791,365cabd3,8850eb29) +,S(d7a891eb,551ae5ac,baf366b8,65755a56,edc7bbce,7304a806,2729655c,f7f61cf8,8a7d915c,3a9a1500,616205eb,798915fd,9b33d7b0,6671e9ab,cf02b5f4,667173ab) +,S(5bb03484,f02ef87a,fa07f852,e3ec0664,67ffdc38,39fa28db,de0da07,2fc31246,e0515751,c377266e,ff95f755,f9c9373d,226ccb66,3678e8ef,176af5f,2fea3504) +,S(58716b31,48a0cac1,2024760,60d3734d,ab066ad8,c0f8d8ed,cf5824ea,83e111ce,71ae487,701d16a9,c1d2ab68,1ec7c8b5,8367a4f0,179dfc18,6778579c,553cf608) +,S(4a344869,db8d561d,96944c8,94a3c196,8d649fcc,bd430c7d,4a66ef0c,dc2ca550,285433f1,ff0e7dba,5bae081c,1b33c762,6f30224e,dcacfe66,46787818,da0f810a) +,S(3ecc369b,e7789b92,cf1448ee,28c0448e,ebc5d277,a8bdb5bc,a22d3151,5e246f7c,a5532e1,47848951,cf11e065,3f0627c4,6530436a,5f3773f2,b02c9bea,c1b60e78) +,S(28a5d794,21db38dc,8d7c8cf9,d120a3c9,97eea170,f0f6ed43,7222bf4e,27f170be,88cb09e5,ea40b566,39cce9ee,1245384d,2f02f983,3b3116b7,31ae5576,daed7cf9) +,S(4f54f945,b3afc07,c38e1aae,16dafcbb,b7c5097e,4895a789,e70e0993,cbc905f,6ba69303,24adb3b1,2fc1f8c7,8ea58729,5115adad,52bb1138,7281b9b2,997fa597) +,S(f490bc4a,20f1ecbe,ac2dbbd0,56f2bb77,d258a914,9b0732a,f29aef2f,7ec25f47,aec6fa89,74c124a6,576475a,60931a6b,533d0da6,4e621fe9,c298f03c,a894336) +,S(73b2f8d0,e91e2516,d2ebe348,85b973d,4a0c7919,1ac857b4,74f713d7,348d343d,9dd7a951,efdaa060,19ec71b,e41b294,d047ab97,d0cef4ce,25a607f2,f11bbc26) +,S(d911f5e9,977489d2,847aeb66,8c3cdc6a,ffbc7b5f,4a5aff62,1761ee01,567c5647,3ee4afd0,2ef6d3af,a2b16d34,b59444a4,2c70b3d,4db17788,ad3aac57,184e02da) +,S(87b695f1,5c5d7852,88218586,96bca8c1,9c5a6f3e,f23786d1,fe010fd,d063b0c3,21b54a,46ee82fa,bb5ffe0,e3137892,31623502,9a8e0505,645a9ec9,29984895) +,S(39de5a90,4ba6989e,5d16dda,a7ff67b2,15e08f3f,b593d7a8,4b6babe2,ca25c658,eaf5e6c4,1e1e2fc8,c1581f6,211bc8cb,34dcc08d,58f570a6,e6719626,7124c019) +,S(18c1f673,ca19f805,c426162f,11e7eaea,c675974b,6b4cf0f5,9eb03288,83bb0af1,ad1690ae,65783091,27f602fb,93652b59,d7507414,e76d9b3a,82fef166,68b7aecc) +,S(8d70ebda,893e8d03,d15fbd68,67528b7d,308ddee7,b2620698,15e7f3c7,332d42a1,254be26e,d9e9009f,e4aceb8a,b97020a9,9196a9be,60777fb3,a9d1243c,66df1707) +,S(c32fa0af,ab39b1c0,c96852cf,8e2382e3,93e5b59d,2e83ed50,d5307a0,14ea330,381e8afb,85d47c3d,dc5da037,d6bd82aa,ff2324ef,ab0e48d5,5da3fcca,bf0ee3da) +,S(c350235f,e5ad084a,1aaaee80,d156b302,d34ec03,360a7c0d,3a883cb5,19f4b27,e5ea7c5d,3184e9,b62fc3a9,6c8bb9ae,72535fc0,497f1b58,67bbae06,672a1f79) +,S(8ef5e1f4,8d905df,dbbba4b2,d4b22162,db839ba5,b28c755d,46bc2f00,e25e20d5,6300d9e3,11971dd8,a3e88192,84f3a228,9986270,45b901a1,80f831b2,4153103b) +,S(9b3783f9,c638a6fc,166aa2a3,3a40d95f,35a4e7a6,c8cb5fb5,1e596b9a,f285279c,41b2dd9,c3abce77,fcf798a9,159f36c8,94b55ac6,7c917de8,75b18cd4,e70dcaf0) +,S(c262215d,720180ac,669f0bb6,885dceb1,2c941730,93e561c2,660a4a1e,cdf0a244,7fea7ed7,f654741c,c67b12b9,f115aa40,ce427cb3,8c6498f3,8bd291a9,d73e9f2d) +,S(bb72f723,f0a16f09,7716272b,b905a111,c290c229,94f400dc,c6a24814,f41816f9,52a83ee6,53214a5,3ed2b13b,a367324e,e5606fc2,e3dc05ae,7ce0cf05,ccd6c36a) +,S(d896a5a9,a833d66,51b17632,70b03d36,eb423236,bf3b2b7b,6b2ba97a,f9282a03,e8a823f,ecec375e,280e0d50,49625dc2,1f48e269,77ab5eb1,b467d3e8,a0fccb37) +,S(198cb33d,9a2b790a,2d6fa355,58733bc7,f64cad76,e3921dbd,8d7a1f,8db52b64,c7e93702,ad7df0a4,94fba344,8a8157bc,eba10ee2,8f66f000,7e7f7655,8e6b4a74) +,S(4354e475,81d63d84,df19bd08,d98b2315,67a55550,477e1c53,cd425854,e67cd81a,a612c262,74d79c46,89734884,5b87ef50,29d0f87b,4a06e4a2,8ab36bee,ae6a6ce3) +,S(d956c71c,1c8b110e,19f96ffe,5709f2b2,83a095ad,977878fe,287de35f,283b2606,7c029c97,9a807375,bb589c12,a20ce37c,75178be2,5d5af713,f4fd4070,429c8d0) +,S(2c04b299,1775ae36,ed7d5b1b,d9b0e65e,fb2bff8,ab99dc97,2cd860fe,19b1789d,8b3f3b00,6d63c81a,86f225a,bd8e658d,c9c4c1d6,6787678b,6fa4a692,bc7f78e6) +,S(c7897c17,debd6456,a81f8d96,c0ea580b,db5daffb,5fc8e8e8,9c8612b3,7b229f7,f7334550,d20a1a21,c422f73e,d23fb159,2fb3d9b7,ca61d7b,6e124fa4,147ea63c) +,S(3b37b93e,ff554f0c,7e7f36b1,864ecb73,7b33a93f,6ff31d6,9ea18aab,a9487505,50ccac12,e071f469,3f5e5339,462a0cb8,66bde539,5842ff44,620f7acc,aff09a71) +,S(4584ea49,8a2785e0,b12c86d5,2b839212,f21b2e33,3b64bb0a,9713674e,66216547,2851de5a,b67354cd,6610dd4c,91aaf4ce,b75c4c97,26d524d7,69ae0ba7,997ad79c) +,S(c0c971d9,f5644eac,851ae8a1,d87b4f09,a802ae07,32d9b5a2,d9589051,f139963c,83e07a5a,2b7614c,c064b4f,815b8a76,6df7eeb6,449fdd50,5c43faaf,b0bd075f) +,S(370e8fd0,858c6fbc,f479deed,f35fef5e,80cd2338,90d4b227,1867ea90,6cd15b5f,ef382477,fd2dec6a,fce439ba,9b341a21,17d6ecf4,c8c8d63f,b5775094,6cedcaab) +,S(ad402a30,99c5d5dd,c7ad9631,b592d5a5,862e8d3b,5ed47dfa,c288193d,69366625,b8c0d468,5d0eadfb,9f7e4ce6,11c61f5,7098da29,aaed61f7,6a50961b,b45b7c5) +,S(ec16de3f,32ea8f58,c1dc93da,d1cabecb,5ca33ce2,e3fa3bfe,bf0ee989,f3ebf5f9,aeec3532,c5391ce6,1e23bea4,5b7b5847,2c36cb8a,6c5f1363,e457c003,b18824fd) +,S(5a51f9d5,f5c48520,dbd826b9,49f62f67,c299a228,259e09cb,b3bd6fe2,f4b0e60b,3c498d86,225a83a1,f506eb62,a2e530f9,511c56a2,eeed77a4,18ddec4f,4404b9f5) +,S(9696865f,64523eaa,456ea65c,40ca6e0b,12c6e6a0,211cf8dc,20963f03,7980b684,8f879d8b,491f11a4,1f0025f7,b35dc8e7,c71e420d,7d7d9001,bf5da111,9ad6c2c7) +,S(80c43c8f,6325cc30,8065d4c8,6e3d7bec,6ed85f74,412f6127,4fc3ae49,b1e185a5,31dfbe69,a48f1f39,a0cca355,f3d68f28,cbf3e14f,7d47cfc7,d9e82e25,19bdc292) +,S(54940550,ec4a6ca7,561946c0,65769b4d,d4683f1d,11dfda66,4aa2f8cb,8f1ab2d7,f41aef26,64466fa,ecc076ed,cd383d3f,132b4fe7,5a17f16c,c394b6ec,e4079378) +,S(a9787f8,42b3549a,9a3bea8e,4182555,99addeb6,416118bd,901afd9d,ad4c134,4efc4f67,65c7cf5,ccc2336a,e9eb14c5,a1361067,7632ce89,68033d57,4bc686b0) +,S(fa83e81d,63b85491,92e800e9,7a422ca8,35c03bb1,5579bc05,bdb4367,198a24da,779940b2,ad301a1e,205c0504,fe3ea104,cef6a459,e0479849,90fc94a6,871954eb) +,S(ae9a8d1c,a8a560e4,8a373666,544a7b9c,84bf0475,9de1a63e,4a20b55a,2f25c132,b9b0bd66,4c46edfb,ca2a0bb3,a94ff043,c00c1441,9f1df886,2aaadc02,e25017dc) +,S(a2f47145,296dd22b,3a6f5185,47ab0957,4d3487b6,8786f87,6758cbe3,5f4c0ae7,2ec4171c,15e7a82a,f6ace24a,60176f7b,6239561f,3c369a9e,efc80d52,3fd216e8) +,S(1bdcf82f,a7dd441d,91332a11,35f50be8,4d275358,f95df300,25187f2f,9828f219,99f12980,92a3ecf3,dbeb290a,67d72aae,6acd9cfc,c21918e8,9ff7ad4e,548d3510) +,S(b73c91e1,ae33767b,11bd329,361fdae6,36642d92,6c2792b6,e19b6e3f,947a5be7,b6502357,11ffe955,be5798d1,467eafdc,94df5911,29d2aaa9,b747a2ec,befa9d7) +,S(b23b4ad2,e983e556,e941a35b,b84846a4,40cd25b6,82487f37,a3a30eef,eef15d1,7010f10,88749d1c,f95e52dc,eae1a379,b87762fb,55593d32,a65ef0fe,dcba2485) +,S(e3aad2b8,96fe34ef,9d775743,6e70a70c,b3bb0c6b,8436e616,a23174ca,985fd8f7,5d6edc38,89beb099,dd191564,eaa83c14,3ef0b74d,7789590c,d6083704,61c6676f) +,S(2f22a14a,f69e6257,b909e3a9,50aab3ea,dc09c281,ec52e47,8c578f49,68910244,4ab723e2,43c71baf,d2aead8a,479a1a2e,fd2aba74,800b5eff,56ca46d0,532e81d7) +,S(93806026,554bae5e,9b91f534,f864b79c,81e581f7,bdf3b2ef,dd007e32,dd6872f0,800f8162,a6836801,d052871f,6892ba33,b2f60f82,89a658ea,a8af25fb,f9d9c444) +,S(8895d121,ad1f2be2,3c578faa,1e33656a,e9403ddb,a1f1aea6,1cc7bd03,817a46d9,4f7577d2,420d0d0d,7cc9ed68,ba3645b8,f9466f59,570ddf16,ba168de2,8cfec881) +,S(1838c15c,3bcf1189,546a9e3d,69fb8a73,de39c862,f5faa4bf,423e1dbd,29046ccf,49484789,1b67ea0e,2e7f7eba,4a596d66,b3ed9842,3ffc54b0,e9292a4e,bd6f5487) +,S(111e8eab,d9cb3ec,5eb63cd4,14ecf59e,3a3f0d56,70959226,ec8f203d,947204b0,4ab9b3bf,dc853571,2c0db9e1,5fc2832e,4560e420,af96b043,e587d1de,d996e38a) +,S(80369d1,ec5ab7c6,492db1f9,aa2fa368,ec083d86,75d6c1,e7262e73,a612e493,cbb28c30,db7f7d03,6b8dda93,7e0c4e18,6a09f1ee,2407e55,7f6dc54b,a8c0553) +,S(903c9034,dbd3f982,41351979,c9306a8,42b88892,48cace15,c3e3a8bd,aafe1ffe,d55adb43,d0142740,71499c92,e9b851d8,e6ece26f,f82ceaff,ba63a003,14960f38) +,S(d6490ce4,74bd1e80,82c99e87,a0510a2a,c2c2772b,1921cda7,dcdcd5e8,6ecaacc,85a1fcc6,de4b93b8,b2314da6,31d7abdc,f96b22e4,b44a5545,f274f941,454e98f4) +,S(12f2342f,aa8b19a8,bb0e080b,2ed3487a,14ddd1b4,83e0bfa4,9ea08484,157d5306,eee0b9e4,30a51a3a,49da893e,50360ba1,21660b4d,ebd3ad8f,82805416,10f4b7fd) +,S(3bf1ef66,57bdcbba,a9d9a23,36c75429,c4df4a86,1d71a767,2d7e956a,deff234f,fa98467e,83dd1ca2,2a85cd8f,f12be1f,53d5a939,352f4046,a626a208,601ef6b5) +,S(d0d0f8b7,9a04ac8e,b6e010fa,3608926f,3b5bd886,f84c361a,8e1d9dd4,b01ff717,9bb32b90,6d7de56,c75bd202,b9353128,b6440fb,42dae8e6,74ade0c8,fc96386d) +,S(19507738,80caa9c1,f042f8b5,d1594a12,161635b5,a83e4675,35ffe51a,fcf631fc,9280d033,f7a8efc3,ff22f501,6a6ddfd2,f78159bf,1f13be4c,f758b150,21126ba) +,S(f4f50c3c,eb750a04,95973b47,53b96ec7,22cef833,f4ee7459,a51f884a,f9a251cd,41f554fa,d3d521e9,65fb0768,66caeac7,d9f5c6eb,ba7ba7b1,d1d38c9d,c06d85ce) +,S(8b669c9e,22452528,57e20692,2726f045,e97b8cf2,1154fb74,e199f49d,11951b84,550c47fa,c46060df,4d893afa,9f08713f,cc8e719f,a369e90b,fa8846df,7938c65b) +,S(941cb4b0,31edf346,64dd5e7,92ca135a,1d6cc6f,55341322,66644493,aee5fe32,cfa91a79,29b3b47,d73b8ae6,77cf64eb,cacf9408,3cdc76f1,79e25631,d574ff68) +,S(557e36b1,27307c56,72232c6f,6e92e4f6,f5da9d22,1567b464,8e79d97b,bf589023,ab741e35,109a0313,e7adee7a,2cec2c10,79d18d72,6c27f1c6,5a808b98,8e99b523) +,S(d8081ed,9e7cf1cb,e1ff07c0,b8ef3f9c,c7eac02f,1cf1c6ff,833f4b74,b2a33269,efa297d7,10fb71c8,43e358f3,e04ac308,53027f00,eea43d03,9b87c3ca,2b0fdd2e) +,S(b5e4fdf0,7a443146,263f6539,f6c8d991,7486da78,95b4ba7,fb4c9f14,78324d44,a3ef979f,93ec1a0d,602950d2,ddc400c8,4f018643,229cfba8,7cd9e3,f2fe657b) +,S(1a573259,a5100c21,d75efcc4,41d5a834,a81d96b,f4149731,eed4ae67,508bffcc,8a993dcf,49f94f6b,bc71766,9d37ebfc,614a45f4,1d70a1ca,f4da8834,5711223d) +,S(33d9f9f6,c9ba653b,f9c52166,c29e9c06,2cb7700d,ea2f40cd,28520bf,237b4558,dc80f57c,fc0e73ab,bdfc4a8f,f7e8f4c0,3353e889,bd54cf84,6361c1a9,af5a0a79) +,S(e4ca84fe,307c9237,4d39b2bb,b017b5b9,e478b26b,95236b8d,5bdfb220,f44419d,c0cfc7d8,92271c95,7ef235f7,2f310c7c,7d0ac297,97d42ffc,ba37061c,83775507) +,S(8f1f2d2d,cccb07a2,a8716b77,8c74f3fe,22990d36,b951e6dd,92f5896f,51bc23aa,9eaa792b,426d3400,31a862ca,b805d0da,ed9a4c73,591c204f,c5179b17,22aef870) +,S(20f7f6da,86eb5ae6,6a1b7818,43fd7fe4,c458e3e7,bfb590c8,76afb601,257b2ee0,6dfcd6c3,93e7e9dd,32c7e826,42f90d69,ff1e1ffe,18988de2,bc1cb9ba,db246fbc) +,S(3a3df365,8d65d718,2dc05797,8bc48fe1,5c5eb6d8,6bb241e5,9e178ca3,9cf9a010,ffb89791,7435e8c5,4865bc79,52fb7bd4,1578af51,6f611c54,1f7cfd5,651b293) +,S(7aa435,7dbbc5b9,a01b5f94,6b0240a1,36daa942,1ae1a1c9,b50f66c1,c7f15e48,4640c8d1,ace8f69,6b08ba1f,9a46d04c,f75c5914,3339d5a,181a7dfd,bfc92c0f) +,S(198e4848,8176664d,40fc14c7,acca9eb9,aa22599b,b2370959,5856d78b,5e932e94,fb9dcfdf,b27d062e,8ea5246,275d80e5,6fc7cb09,e81bf066,dfca888a,8df1de6e) +,S(25da8876,d3215051,75694915,c25056e1,2c1329b7,7795b7ec,1d4b66f9,3c911cc9,b8cf4a83,db78787a,6d80a157,c7c205f1,3664a71d,a6e78db5,b52b012b,930c4583) +,S(893d2e9c,3b6fa8e2,136994e6,14121842,bb82e577,89826ccb,eef51fab,de32c0b,d7cb9b4a,1d49b34b,f3f743f2,10c32a25,cf8df45d,8552a7f2,1268e270,ee6fed67) +,S(dde2e8a2,988fd777,4959457a,5c14384f,c8248d1b,a32c7ce1,a727af27,7b217688,1fe0272,137e4c9b,1c6df618,41a240a7,be3b5de1,7b93fa24,ccc055f0,e7972b1) +,S(52a73b51,5326198c,81bd988a,3033e1db,abd9ee27,9a91a434,e7a76813,453e27c1,c23130f,c4b8990f,ee308443,1cbeff25,62963b1a,4a5acb4b,a1319b4e,d77c745b) +,S(1b5afdc6,4598bfee,4ef31904,f0d06796,17b0149d,ac19e7ef,a1d4f19b,c5385490,fcc0c62,47d96d4e,430ae0e9,c19dfa2a,3719e66c,ce25a2cf,d7428d4,4c91e96e) +,S(76da558b,d533e761,4d97b7bc,5a1a98d2,af043ce1,1f2b323,185377da,c0e0abf0,f8dfecd7,94b3d069,ec22f01d,8418bd90,27c3dfc0,62beb597,e352bb70,271d3cee) +,S(17f01b77,d2417d97,e3c14f10,18af67be,c12c31a3,58521f6d,203f101b,227cdbc0,fe16d66f,a194c800,e3c14bb3,f1df0a2e,3ec319a8,66c69d9b,3b492d96,327bba04) +,S(6010e0a1,2cacb745,6e3a8f73,d5219a87,cc14dae6,54be7265,ffbf70c8,c4d5433,7d5f0faa,579afe50,2541a2de,4d525bd4,e7b950fe,2b2dcdb8,d67d3d9a,7b11da54) +,S(91e938c8,3f319320,baa876fa,23773a7b,d0fb81f9,bf9d99bd,13180560,bfdfad6f,b662f401,7a4fa2ff,6603864d,77a6b930,181372f2,5fd6a67e,12a1df6b,f75509ee) +,S(de2ef2ee,633355c3,3d259357,efca96a0,5b669f13,b515e64a,c0bc467f,2abd665e,d3d6254c,5c30daf4,64843190,7c2a8c1a,51baeb23,13605a58,b88c9a78,a6147c73) +,S(186151f0,dc37e48b,3178bdaf,900e7bab,49cf251f,7a2a1b8a,76da8750,2dee93a6,2a240dc,acbbfc19,876c3680,fdbd7ee5,d1085b79,96317e4f,db73d5a1,9f097f8b) +,S(6f19619f,ace0c63c,4dda566a,9a1e78f,a9568db1,1c46f3e9,771756b2,7d8a5c1e,4716efed,7166e06,722a744a,af89d145,4ddd2f0,f7652862,cc38eb3a,2beb1925) +,S(a0e450d6,fb8a6da7,e8f6e62f,7a08c2c7,277a832b,4e5c6dd1,8cbed37c,37db23a2,775223c6,3f81b2be,1bdf0831,9ed65ab8,d3e11c56,38830851,d55a8893,730979d0) +,S(6337ffcc,64f5d358,d90fad1b,646d8d7,46e2fb7a,2a6cc378,9b55ec52,3824de5,98286287,578bfd97,4c161a01,55f9a2ef,707858e2,a32c3fc7,319416f8,5a6e5427) +,S(914a8565,cc1bcf80,19e1eef2,87c9467c,bc42faaf,726d4399,6485efe4,6f99e32f,846c42d3,b1dd8c20,3a744f5,560396e9,b9a92380,2fccffba,c6ee22c7,65bffde8) +,S(dd79a02e,2460ef86,cb4e9ba8,3ee7e231,e99b3303,1d4a6d7d,6dee8b1f,b0050daf,d6a75b67,ba13f76b,172ae0e7,d63b20be,db3e54f9,e54a96d2,aebc326a,131ea271) +,S(b2776e23,3e759f8e,38c52394,d50fe177,c6842acb,f238eb56,c7638549,174c1b2,e18c89b2,dbb6453f,7b610518,8bf090c,f0410e51,97885d52,b50b2507,ffe4dc81) +,S(e0716c4c,c5e84f7c,282adc42,f294a36b,56bb26a2,58cef4bc,c36a7f53,1d5283e1,1442ddf1,57491db5,1692c7ea,5c92976b,8a2488b6,6aa6f38f,7a62946b,a24fc5bf) +,S(2b10c9f1,bfdd659d,9d1e073b,faaa4b13,2a08b678,f757ef39,a137e53c,b291df70,7d22ac48,f4e0aa71,8cf7882a,57a46746,94e0f456,f2cc3aca,30a3f8d4,2b240dab) +,S(fa762181,26a4813e,e97f2718,c6eed96d,da9f6912,ac463af6,a52f3aff,f80f07e4,8c8a04b,f38e2599,4cfd8905,27ace017,e57e3f8d,d378b205,2e767954,80dd5701) +,S(6b893eef,bea81e30,7ece156a,e79abc26,d9f0ca5a,b2dbde11,bb0d8192,f138e58e,510a308b,9fc47167,f72bbc4,f92eebb9,88a6512a,b1679102,f9016b0b,d82c559d) +,S(6cd9b970,332f421d,1aba33e4,75f26a4c,5c5fec37,4a549ac6,d8c10fa6,cbc40032,b57dbee6,68bef693,67188a54,52e3b4d2,cf1c8c25,8e36bc09,432b4f4a,dcc24086) +,S(ba4847ad,40530fc5,f77ea79e,ef7a1c0b,821b5285,5ec4ab8e,d238ce46,48a09fa6,627ba437,8e0f2044,20e61431,5f3d110f,2da9cbe6,2025bbdc,5bc1cede,c36030c9) +,S(92837329,4f59a142,ba84f897,7acf2e7d,9df06a27,74ad88d8,98689900,3750c513,4e938012,b6e55d6b,6f7871ae,c34511c8,40912a1,1a32012f,3bdd27b7,90f86c37) +,S(470c19f7,9aeb9a7a,142ba010,10ea4929,3de57e25,68658d45,42b5a626,864600b9,2e99dcc4,b93d9a36,7544b842,ef4a3085,2e24bc41,e98ff2a2,d9aede05,da703225) +,S(2496996d,c38e4bbc,851f9bd,f9a21b42,5fc7ce2f,2b06993e,97604a0e,db555c34,b6e02a63,f0e2e54e,1cfb9d28,7607bae5,8a68ae6e,a8300ee1,c1170f0a,9e567323) +,S(92366ed3,23ded4df,f4029e42,685c51fb,a8a916d1,999c42c0,a90feb44,4112a78c,88dc8af5,f6d1d14c,5c09d216,8af0e752,3fad76a1,5b311021,7ead491c,12b63191) +,S(ff9760e1,ce161564,22c4962,4962d89c,37e66e64,cd7f8e94,4125fd00,c05974a8,8ca4457c,a5e52e23,d70639c,a9a26f3,12312451,8f0de070,86d385b6,777a63a8) +,S(50d1d5ad,79f919b3,d23c16b1,428d971,5deb186f,2b92b64e,7a1ea1fa,dfca4981,ddb07de,d4ad81b3,cfefe726,33521be1,c7418c5e,aa759440,20dfe92e,b4653ce4) +,S(4934f753,29844b23,80183df0,254d1c7d,342d7d27,9fa5e804,68dbee16,4e9c2c6e,a4f8aba6,7a488a33,40e8e943,14f7eed,5cc25487,5acbaedb,afecfc2d,5405609a) +,S(d7b99142,9cbdf755,5cadf030,7b2c4a91,c0d25710,a0553511,eac6afb4,c544ff81,17bf03a6,83b1a0f2,366b944c,aabe57d4,eac4d86f,bfabd006,66693983,e0241420) +,S(834b036c,145d6905,58199bc1,e7a7bfc2,4831417b,530b0b97,8cc1fc16,af00b966,4a45a9eb,299f24a0,1c520751,2cbee44c,bfb75d0d,14a7eb08,b1668b58,ae58c47d) +,S(d7a8c30b,fd7b8aaa,78dd23bb,86ef2624,5a363857,6ed69739,25ea9d9,194bfb3a,7b5f9ce6,380abb91,6e041388,13baaf24,229ece27,3541f4ca,2883ad77,9e501fa6) +,S(ea4c7ed2,7ced8b13,4a2ccb2d,3828d29f,f80c4825,e5607597,3faace04,d2fb7763,a8454c9c,be19ec44,8a3234ea,97b8fbb,b256a148,11ba0ede,becd8641,42684d14) +,S(a2cb70b,f80d9f6d,696ceaf8,fe8052bd,e59215a9,99d4b92c,ce806a19,71d555c,997bd94c,e5cb12ff,886c1cac,bd21c3c,e2f144f0,3af644a2,c2aaba2c,825f75b5) +,S(89a91f5,c7c8700,175ab017,8c4104f4,a927af06,f269645,36cb78d0,19f8b8f2,6a2f8ebf,e1258b4e,31bf9a5c,31c51a3f,1cb58aea,6e18582,3e87920c,3df8e7e9) +,S(27d7191a,71ecb0eb,71b43dfd,5840079c,bd368c0,5ce62fdf,2967dcca,78f5a6d6,11120002,d0d0fa9c,8db51506,4e1fcc2d,92d8fa9c,8b454294,bfd01c64,889d5e6) +,S(cefb996a,d36deffd,c3c7ddbb,11a5013b,70baa98c,1057adfb,185b4c9,62f3384a,c93cab46,7523b0d2,baef4425,c9ff048e,c586d4c8,69c69cff,3ab9d72e,e3d80f83) +,S(5a8ff765,84746f42,4e06b557,77986825,e91312c4,4f79003d,c1b381cd,4fe5ce8f,3946177c,3c741458,7bdb9506,85ad0c73,b422a8b2,5cc3ad3a,cd486e76,99a42f08) +,S(b179dac7,d2f400bc,bb039f32,91e813e6,f9a7331,80a1a5d0,db51aafb,af582fb6,89a70304,d70cbb61,ffb3a499,1815cb17,458404fe,e43f09bf,d630d480,5978b1dc) +,S(6e14a53f,ab32be84,4eb14242,a3b6a86e,e178cd7,51dbb7a2,b14cb763,ab2fe6de,a22ed4a6,d98c59d8,ca85e111,cce0efcc,60e20df9,6b06615d,69aa3cfb,206494b9) +,S(12ac42bf,d8f919d8,c79a8138,1aae69ef,b789a573,f58332b5,79694dfd,27e7ff20,4ebc3b7e,4b0136f5,19a8df5f,baa7ff59,17f4f632,f85ab1f2,ce502fee,bb3a3a87) +,S(87bf22af,5e9dc061,eabb01c4,eaa867c3,4656599e,da744da7,774fa8d9,aad68630,d34f530d,fbea0d52,9d2d64d1,323fa44a,d80cbe01,fa2806e5,ff069e6b,fc0ce729) +,S(33fe6bd7,ebe2b197,76251415,8bf866cf,b6d3ad5e,1f68519b,edb513fe,6076c96f,c5970cce,f44aa84e,f95a56eb,588b7a8e,d9164f5f,5a2eed83,1f70d308,2320e9e1) +,S(5fc2f44c,fc0f7ab9,9f03b67a,d7b6ee99,d73fa9cb,d74eb5ba,6d931d9e,26ba51af,8b1fbfe9,de201400,6c07da02,4bbcf53e,13bb9f2b,ff3dd6d9,5135a6fd,5842f0f0) +,S(38fb5cfb,e4f348cc,1fa7f9b2,ea600daa,e8330aca,53308f09,b6a7d8b8,5ca8a88b,ac1bf91b,7f53e824,d845c0f6,9534291f,cec2808f,95acf58e,4afba07c,fc735be9) +,S(3ec2457f,fc848504,6f74912b,5c58b270,1be6eeb6,f88da0b3,c5cc4454,48750875,b63209d2,641c65bb,7105af10,61d7513e,409f153e,5d1b5073,232eacc6,f76dde22) +,S(32fe812,e67ef62c,687d248f,e75ef399,48fe8ae4,a01ae10d,ed65264b,fb5050fb,d9c85369,db22ddeb,baf7682f,53642962,cfec2afd,3a20de77,588e7473,18044f6f) +,S(97d7c69b,2aa0277d,2b20e9c7,bb1066a2,be6f443f,d0a428b6,d78c5fc9,6ff71b57,f84ba781,70837aa1,f75936cb,fc50c444,c40c9fa3,22d90088,ac6889f7,e7c0f861) +,S(3621a4af,4d698e4b,60e7f497,6e0c10e9,ca32834f,ff917aa0,6aff912b,2ad80cd,22cfd248,a17886a,ae5d0e11,53621e9b,46289acc,ecd8155,a1bd6372,120c4da7) +,S(ae79a6e5,848b1dc6,cb64958e,87b737c1,858442b3,63d4a2,e9f04561,7d73f8c7,c147238c,59885f2d,dc5dec63,de421eb1,c5a19438,ed0600b3,4c77f4e0,e7aa557a) +,S(36eec623,700e51e7,723a632a,55370ddd,d6b1f917,6ea39a2e,54a2a1ac,52598c35,29698c0e,56b4e2b5,5389dd9c,3d8b509a,e0b87f99,4872d865,3fc74269,7f5bb7c) +,S(50f70061,804dbeab,cf2ab4a9,87ebde2,1f5496c9,a4cd28a,4dd9da7b,310e876f,77e27710,edf831db,6ba9e64c,545f03a7,eb390842,8d9cfa3,3d0e1c87,61371706) +,S(c2ad6b2d,e84bf60,7fe77b8f,a9fe26ff,bb5e1906,1494e469,8da44a33,25843844,17b92ad9,d3938fef,7a17f543,a169a7ad,8ee65dce,36eeedfb,5e3c0c8e,a1b24fc3) +,S(a53504f,e2286269,427fd2fc,fcb64827,bdefdfd9,feb89d50,12651d2d,c60ae99e,6b9d73e7,e046d11d,ce78b114,8aea837f,770d8267,6642dac8,f3b3c035,c10eab45) +,S(5410db61,40a77b31,9f19c969,34a8364a,4881ab60,ad76a6c9,34f317c1,1b658dec,ac45bb6e,2e5fe23d,57297370,cd04ffdd,dd89a9,15a1e9ad,15d7f9e7,6d8b3aeb) +,S(882cbd69,fbd3e262,5093385e,ab82fc9e,7597c38e,45fa5df7,f98c94a9,3bf25467,2083f00,b25b28b7,f92b040d,a4294dc0,8a3d97e3,d951e724,da0e692a,5737a87a) +,S(3141e0d1,22b4c136,f7793c73,9c48f308,c5ec8043,19db116d,64eb14f5,aee83942,9c2d06cc,d765c6c3,cee09b70,247f8806,227d0178,13becbb6,96c5a344,b68b33b1) +,S(1b2f3686,5a63df41,65caea4e,305d8d66,1de6ccc1,c2a28cf5,2373527e,27cecfc,1239d8c8,d60fdcb9,fcf3f5d4,63305037,6d47fd97,4cd6bc64,99e0e951,2d2209a9) +,S(725168cd,9fd59e41,14918b77,6ea96fc9,6315b4f0,86672dac,1eaf59a5,e61ed25f,946f03a9,84a6735d,e72acb38,dfd21905,fa2358a5,e66e7b0a,2f8836a1,8f5d73c3) +,S(4207faa5,c9dc9622,9644df1b,a099a84d,654ec6d2,aa69efcd,a4a31a3c,61f2e1d4,ea72a92,836d1597,5275c18a,900c0e7d,d0502610,d97b6f79,1f6a28c2,1b3ddfd7) +,S(53900d38,a4740830,a6129a11,9dc90f6d,30f32847,708d48d4,20b77764,ff5b9cc7,66822ed6,ed941eb2,20aefe94,4325daa7,a619dfed,5c76bf75,1fc25342,8178d3fa) +,S(d0204932,a3fb8d33,9779851c,d69f6adf,be1a9957,59fcfce1,b8d4f047,e4e4dd2,8ded18ff,bbfc663a,9c658ae9,286f82e0,9ee9381b,3291f5c9,efabf0d5,fae318f) +,S(3ed8ce15,2d34e621,94b8b16d,55605766,41fb2673,14a08c91,d916fa14,f21abde5,48490fe3,9769c60e,38996370,27b57df7,230060c,2505f414,5a1c7f1,4ba16660) +,S(b7d57614,9b5e2ce5,edef3c30,b8cce79e,6f122c70,351d04fc,ed2b67c7,f5cb2aed,7c8fc1c6,bd42b25d,d45e37c2,5cb06f0a,bd2f7051,4a50e6e1,2a67c0b5,b199eda1) +,S(2489e06c,c8f0bfed,9d5ea722,c07ca795,e9ab40e5,45b036e0,f0c11f28,53c0d35c,dff65f5c,20766078,9737f1fd,5af05ce8,3b8edf91,37002b19,f5e1599a,e76c48bd) +,S(9b2140e5,509817c4,465e6689,a3bdadcf,b79c1803,883d493d,4e4e99cd,55f30a29,86374501,705fabc6,62e8c869,a22d2813,f7106006,ee09b6bd,b582b690,50186688) +,S(48d6fff7,d5c37c30,11c9e63b,b3342f92,776060a6,dd179e3a,e2c6bb69,d2f88b3e,783353e,adb6daec,536d37dc,76ae3ff,8dce14d9,65d21d3b,7a17515d,7d3341aa) +,S(35d46a17,b6951355,a055e253,42b4244d,8ce65459,c630f2fd,4708687b,e6a49e7d,13bc21d5,7cc2954,62927cf8,bc31ef3b,5407021a,62c5253a,5950e8eb,b9c735d7) +,S(17ba438,24168f06,274b2f5a,d080fda2,3c0dc34b,f77e81c3,c4413cd2,f594575d,e8b34f9,1ef09f92,7b5d9e51,662216a1,30ef8f87,2e0febe,3f5cce8b,318fc77a) +,S(2a09e4bb,4f6d6b40,f59d0299,93eeea7c,755eb00a,5eba0477,3fc0c796,aee68a07,cbe72772,e5d8b761,15ed6f92,f20028b7,cd06aea6,74e10667,899e78a3,df403f0e) +,S(f3838250,e9a03da6,87139583,2bc6b35c,2d427b60,4cb7d25b,149b6816,235b0e4c,164b211a,60562190,d415bcc3,87d1a147,66e45f14,dfbeda44,833ce45f,b815598e) +,S(b307de85,c4c2e8,4e076d19,647bd205,8c1a3c96,a0261481,322e73f9,f4a2e94f,5af7f6f5,ae7f65cb,b62f021,608473e6,b68d032b,4835540a,f265049a,e506b2d8) +,S(ff92c7ba,5f89d3d4,5273688,907ab9bb,794e5622,f8f300fc,804934d3,b5820e00,3b2e2d1c,420fafaa,e53c9da4,957e22b2,bc76ccbd,99f323d,763fa1ef,f92ea71a) +,S(49630ee0,a8ddf91d,ea678a3e,1dcb623c,688b7c3b,9e39196c,b578d30e,196e63b0,4c1aa073,d1375475,11d81776,e2e59404,aa08db5f,95b80e56,c39613be,fc2bfcdb) +,S(d41ead31,f2fac884,e762ccac,85825cbc,6ab2f8df,a069b2f3,fce3c6f3,d5c95a33,f15e3e23,e19baea5,677390cb,acf97180,86876f96,50fac740,9b6e62f8,b6d40652) +,S(5e62e95d,27ba07d0,781ad685,75a7a11d,df9f2776,48cb5cb0,869ba586,6ff02869,291717d,1a1ff532,e92f0311,881fe2d1,6c8844a,6f5ad7f3,b1ced51,e5088302) +,S(dbdd4f57,1f6d01cc,8a1025ce,3b92ce3d,56d396d9,635e456a,fd856239,27b53bd5,b929d5dd,816bcdd7,78abd34b,c15f2fb9,deff3f9a,aa44de0b,9f7b46d8,b8d70373) +,S(63a8349e,1a891037,9a74a60,babb68a1,4c8cda6f,c2cbc0e2,1a5aca7a,fff7a19f,38858c42,9cfb1e49,5fc90633,ce07abf5,b7c4a4fa,22b955ef,e2c137df,143ec70b) +,S(1805ec70,2cbb1e4a,5da4841e,9e24afcf,b84a3e45,dd6c39a6,cdb6a661,d5c6b1af,193be01d,d3f28479,e352f164,de99d21d,1b3fe8c4,b0af8587,8f21c858,cdadc27) +,S(2d6ff24d,fcfeddca,ab3b19ec,b6a79793,7f5c5d3d,373b9df0,6ec68549,6d075423,b8bbca01,551b2b3e,f6ec3a37,ff020f3b,6b1f5456,69bd8b90,f8a55216,3fb48fb6) +,S(3aa21099,52cf0464,4bfe5de8,69cb2d4a,19d3c4c4,661d6782,967dad8d,cc353a24,68252574,69dd7940,fbdd26a2,483149f0,f71a4b12,fd25b063,73765c52,d45985af) +,S(d6422a71,443b0e37,cbc06f90,4ba1f62d,c2351b7f,e4e0b909,5fc227a1,7f8f551c,ca92a69d,2c9aa758,46c2518,f1ae03c0,d7138170,447b55d9,37108604,f71b9feb) +,S(51e297bc,46529246,a36f8460,f285d713,b1e9ed67,2027eb35,37806d5c,7e756177,5d56e447,63bde166,b456531d,94be0d,7057081b,edbbb89c,be8c4732,13eb0ad9) +,S(378a552f,c061e796,c9ddd101,63851ea0,663f09f1,3fdf852b,ddbafd7b,bf6c258a,6396c9f2,a26b62c6,f33b73fd,98824958,8cbc7c10,e679bd4e,ea0f4ab1,dbf38f72) +,S(bd85fb00,1db4f2fe,95c61f1e,825e65c3,80761832,37be8a86,a20f6fc0,7a586daa,52c21cd8,9bf874ab,7d7e0f4c,d0095d7,6e8c737b,e2ba9d1,a21fb795,d968f61f) +,S(fe68e6ec,3c8e681c,e7230a92,e8762f0,af3e206f,f3b0afe8,be61ab34,2ca076b3,f8683d06,af0c88f6,93a0a6b3,1200cd25,ea2c7e9d,2b8048f8,b983474b,b1dc20fc) +,S(83e55d38,bbe2daa9,f8147b1e,674be115,d919445f,70d2b3a6,cb917a4a,9ff284d1,92d6c7f6,dbbcdee5,8b11a244,34cbe7c,9f9fc0dd,c93f6fa8,b6354a06,5bf90ab7) +,S(fc9cbaca,ed0e1df5,e9d8120,6bb55f26,67e114e7,30347030,8b333554,d0735ad0,d41fc02f,2d4b3f74,f4e9ec00,7a879540,54d12fc9,3d53242c,a45bd38,c75deb9e) +,S(7adf7a3d,12268d0f,c2568d5b,ee296d61,fafcd739,f081e5a1,7b39fe7,40132b05,51aeb855,a84f377b,899392c8,a7daa18b,70ec2a94,7929c93c,67ed8217,41adaa21) +,S(e9631ea3,7da9e68f,eb61f9c5,6eb91bb0,2fdb4d58,d99874c5,2c4af40f,9716aa0c,51d5a6ea,1a5fffae,39c598f0,158272ec,bdb48c4d,faa93da3,89af20a7,d2a1b8d3) +,S(e892f70d,15639bdd,ab70739e,83ab9f04,963e1dff,e2bd81eb,e075173e,6bdd116,2e78cfa8,30b68c55,68f635f1,b260d823,55f38ed3,f5f43c33,b178be12,dc006b4b) +,S(6f3ef2e1,e8d4fd02,2533f4d7,410778c6,ccd9a7be,aaeb3c8d,d9f01699,89598def,c4388130,c278f8f2,7a9947ed,4948f8e4,febe81bb,88bf873d,2d565b4e,3481b86) +,S(cf24cd64,84b07e12,37640f0e,da7e776e,3d4db192,6da8e929,19b36383,15de412e,a353cd6f,a796c46c,49f34c72,64d36df8,6d53f556,a36f430f,7f3f6ac9,3527ca7f) +,S(a2e98e5,36d517d6,528d6353,8bc2be93,4eeae1c1,cd8dcec7,a60080d3,fed33749,205e752c,49a09b0c,8804cba0,62b28ec4,6d9e8f37,9cbcacd4,a7cc9049,47bfb296) +,S(e5ef194,498445cb,718f7c77,6aecd0f1,949a4a49,73e32ce9,a4915867,dc27139c,fc774f2e,1e51f03e,71e637a4,158a0013,5aef5a18,b15ef0b1,e5696338,82bb513b) +,S(311b36a7,1a1d13b8,29c78813,6a989c0e,1b7039d4,d75110b6,ba97f000,749a8d06,2f441c15,6b80f4d,51dce0d9,bceca566,2f3fdd9b,965a0d84,55f6d154,3a661f7f) +,S(324420fb,a5076214,9be852e5,ffb88d20,d1120e1d,2eb9c132,32bdb13f,403a5f6c,378f5d4,1dc1011d,1fd338d,b6073df8,903fb4c1,e17b2122,bdad5eaa,496965b) +,S(7cf4998f,5095c102,a0db72bf,9935ca91,eac814c5,4120eb0a,262f246e,ede142d4,73e6db13,d6db9486,fc870698,7be096f7,7440dd35,67064888,e61dfeea,e98c1fb3) +,S(d6fe26df,d501241b,f8c5e74f,e30714da,7d690be0,91cfbe14,68ed6bbe,23598f7a,102c4907,60fed9b7,239c0443,d01f2742,fed0a0f0,fd7ab9f6,9fb1c58d,c5752fe7) +,S(805ee58e,66ab8020,c9c32afd,4c7fe30e,960f4c3,ae51e94e,da9e5242,af09c6f,19154d86,59fb1837,3d0adf6,daedcfc8,3937af0b,ef9a7d15,f990f60a,c5457617) +,S(491717b1,f22f93ba,d0a3cce4,fac57160,8ad7a746,ba73d375,1713e605,8bcd8450,9c38b3e5,4c89d38a,2307546f,f46bbd68,d4e4ad08,cc45bcc1,575fd120,acdacaff) +,S(a8e23e38,27c54d4f,ae2589ba,cf3c4e1,93f97e59,b11506f2,2016fbe,a9fc2522,ffaa8b74,22b50822,dadd781f,d31b5a31,eb384c50,2fa0bc2,89252665,1c602f3e) +,S(9fee31c6,88b0f7f7,78a39786,a84567e8,34da7ffb,350cfdc8,95d65c4a,afdf69c4,2116e31c,2a0d25f6,48a3ab47,1c897484,d71dd9e5,c678af4a,292a6388,d9e45e94) +,S(6256ced3,f08f1812,ca7ad618,383f1542,baa2bb06,332d4c1f,72d862c2,318854e9,9ae0bbd9,d2fd51d2,9347aa62,12b6c0ca,be530b55,bfd8cf18,6732be1e,c66ced06) +,S(ca1cf364,a5e1f47e,cd0ab0e7,52e785fd,935b8c37,964c5654,2cfb3249,a136d669,17412d75,2f350795,30db27a1,7394c362,45fa376e,d20583fe,b9f7a7a1,2c3b67f3) +,S(19004a4b,28e5352c,7c74def,cf129ca1,cb5c7dc1,5099b37f,dfdb0d5d,1e694400,68ed1edb,9ac650b2,965a4034,2812a692,23b3cf6f,24ab0a05,2e1b8eba,f7335020) +,S(c605e97f,27199d86,18fca176,54e44c9c,8dd20994,aeae0466,dbdbbf0e,ffd1b3c0,606955ce,33ff7b6a,cc6de4bc,4ef80b34,bdf6ea17,b29dada,4ee54f06,f61e9d22) +,S(3e89970f,15e79b51,fba90202,a16da6ec,3d3d5d53,72218d72,bbed0626,31affdfe,5d9e27d0,bdefa8cb,b02ca682,a062ccb0,61a8fc47,b2cc72a3,9b687b45,5f83182e) +,S(c077c861,575ab0d2,944f8e65,8a9dbe54,9fc1172c,6b7b7428,476af472,e625d5cc,c01e7abf,6a3abe2,d2aa2457,76068afd,9ba78fb4,fa39e531,8155597a,90fededc) +,S(8567c87d,c12f1479,5f351d1c,807604c6,636c670e,11edb286,92552ab,382ddaa5,71f0be2a,12ebee63,8a63e848,fc4d8116,e2dcbf99,ecdb15ae,1f4ec6c,84f359ff) +,S(9f1b7785,7421626f,87978ee1,780d0,1e9d0b28,df34c2ad,15332b8a,d94d8dfd,6df504cf,66144d9d,ed530e2a,1436a4df,634a84c6,9de752d8,42e076c9,1f129b8e) +,S(f45c2c6a,2754fc31,c5770e3d,38b19e72,47b0dd1e,5ade8ed1,f2f3219,ea7accce,ee07ec18,8190ce54,e736ffd2,912075a1,128d24d9,b178a7ee,72aeb6e4,9b593f4f) +,S(51a5441a,f3446fb4,cc63ece6,c9650a87,2fa5567c,4918f5b4,ec8668c4,ddba6e0d,413186bb,adad4943,6abc46e9,280a1571,a97ed7f2,a839cad2,935675b9,d167a54a) +,S(adee3e1a,e9a2ab1b,da34fd5f,afd3b3e4,f51255f3,5dbd94c6,8efb994d,afdeeade,f330f0cb,973fd732,eda39ebb,82dcc589,4c148165,428adfa3,b4ff865,1ea83e24) +,S(a8946504,eb084214,d0153ac9,c3437fe7,3ff5696,16e01672,413a62ee,27c97db5,3dd63d46,f74a56c7,2cd5d45b,5bd6f2a8,d913300f,b4ce536a,e504d4b1,b2e311dd) +,S(e34ce586,990e48c5,febc2fd4,70770cf0,4c81c782,3702cb5f,289be0,f4a97205,d1774a35,cdd7cc54,dc68827e,f7c723d7,5c167982,c314dc62,bba17478,611f5854) +,S(7a8a5d9c,edbc4c76,1fdf2958,8289bea,d524edd4,f7cad978,deaeca8a,de47435,559d473f,3a9a7ee3,4ee0a1d0,4acdffc5,6766db36,94fbf08a,5ccef8cf,31e25b6) +,S(848cd57c,62c67249,d84710a1,5c7abc27,93fcba46,497a6b0e,b23c2ad,f8acaac8,a48424a9,59922131,31a763ed,30369b72,b331d9f0,cf46f8a2,a15515ed,6726f735) +,S(6afbce14,1f2d2c4a,c52e7e05,5ba6ea60,7612a590,d74a2a5c,1344cd5b,c49f2f03,910d69d1,cce10aca,bb1593be,2d4d4d28,9745f97a,29fb78a2,a1eb6f0c,a58e4b61) +,S(61f3727d,718644a9,2e08ab04,814c4446,c5dfae82,c26540cb,44fbd310,a5315a11,56665e3,ee6edadd,a154563b,11fdc203,fe4365d1,ac36879f,a30778c2,badab836) +,S(582962de,e00668e7,91741dda,8e86ddb7,b8ff7773,ac1ea51d,1aac139e,a810f3d1,6bd353b2,e004d66,f1e90cc0,7c68a0a5,b532e709,75a7bc58,7a6b1c67,71022f6c) +,S(5c4426f9,f6071fde,a9304e6a,4a2374a2,e2591225,28b62d20,5fc3016,c471b636,ff6e8c71,fccddb0b,5cff9e2a,10c606e7,c571245e,8f5bb5f7,77e0a7d9,733560a1) +,S(ee02633f,c6b41f01,c3c24c33,719db7fd,a9e04b1f,7070395f,e8898a0,efbf6af3,ecdab00c,83cb3465,1d48a696,6a8e4f03,ebecfba6,f73a66ec,4d62a668,48f14798) +,S(fab36b80,e5bba465,63c9078b,b727e0af,c9bb0af0,2e394f9f,2c90e0e,fe9fb816,75173a7f,f5599da0,ab84519,c7c25be4,1f10172f,fea39762,8e7dab7a,3ecbcfa0) +,S(68a89a2,62015a57,5f882215,48c6d3a7,b4ad1f92,f5665980,367107a9,f4cc37a,43ea7b7a,79397dea,c8354436,d53e7731,c94773d1,5b067063,b108c559,c05bc46f) +,S(fa6e2972,87a0e97c,35454378,4a76fb0b,56106727,136474d8,a3edf8b4,e03f95f4,1e80751f,9c127fff,ce28ae,8ce5afe2,eea1c566,fb0a9f20,8030cc96,e1197725) +,S(25474cb7,805d22f8,7a641116,34628321,f086d1a0,40404c47,ebee913c,e9e02a6,2b3aac5c,85ce9672,61757c61,d6a72dd,4ff81e00,87f0b8a4,999b927c,7b069609) +,S(6f7051ef,10fc1485,a9c80ae0,8683397d,c0af15b5,f78fec70,fe4c456f,f33b6689,625086ce,ab7a9173,d9c370c4,c336c63d,2aa85ac7,b8391993,7ffe4118,f6a7b849) +,S(f4720462,2f579de0,701f1e90,558c74b4,ab634664,197c03ed,a85b0fe4,50df4951,c66a490f,f9b588c9,2d17b80f,7f66e5fc,a65187b6,d965e81b,c16039f4,477fdb54) +,S(3b718764,7e722fa3,de42fca9,e3ba181e,de982728,5b8b006b,d51a58e5,ed898c8,bf872047,f3cd3ea,d7a439b,9d4f7776,7175f940,d6cae8fc,930aece7,e1b92202) +,S(5255ba09,df1e199,4c99bf21,709849a1,cd7c4f58,a283e344,6b98872d,a416c40f,a1099eee,e3ad10d9,6bce7a58,839fcaee,1b43e0fb,29ac0300,4e0eb15c,63d1f604) +,S(f4b64a08,16dfe224,595d3a15,8173df8a,7d941a4e,968ac02c,bf87d9d8,93954805,e5585209,2d0cf600,b8b2342a,8371894,f422e3b6,68ec4078,2379a60e,944228ee) +,S(236d83ad,b789d7df,5fde69cd,86d45d5c,f20c5867,b7f2b398,b9373960,b22b29ab,9758044,d3e06295,e33bebaa,befc5776,5db45475,b3aab963,5fea8dd6,77a478cc) +,S(42071659,65af2fda,c9e87319,6dd0c723,e7d61bf9,b71e5e70,667c9858,6371f3f2,fddfce02,588c1d4a,b744301c,6117e504,8f9c2636,5ccafafc,8a6e19a1,ea7b1b37) +,S(7d0c2917,aaa71cf2,e79f1041,5e5e583,ff01b24b,65f1f409,240f794b,d346a452,1e857dbc,c86c1032,e748d8e7,c8f5839d,57295223,22e6bdb6,187b00f1,f489576e) +,S(f0609605,d6e39141,e5e4f0e0,6bb6d55b,9ea2ced0,6a58d4e1,7c46a3df,18257255,e6d54c0b,10826de6,e95553ac,e4689fa4,cb03b959,65472e65,a085988b,d045374c) +,S(86e66299,819b0807,af30c89,228447f3,71fad56b,75f238b5,30bef3e0,b1123204,de635715,b1997f4e,c367bcf2,8f8def4,7a7a2069,3a555b5,396dbe4d,19b1e8ff) +,S(7710dc40,f72bb934,f5f9328a,1284efbf,fad3518a,70c2a0a4,55028bb3,a8b87e9f,d2ebdd39,7dd91ec3,7ffcd8d1,a9b412c1,78f6d228,5e099162,76f9e8f4,a6a28e02) +,S(3d377c80,25607e9a,fd512e8e,7c37a2f2,8d316701,dff7f318,eb24cc34,348d935f,20d2d95a,e8503a11,de19db36,5bb62213,6cd55735,966c25cb,2a56f07f,19cb2664) +,S(812a4ad0,d004003e,fb190ea9,336659d9,7b7d6df0,29f29f97,bc9c8d68,f284a696,6303a024,b2d95d18,f04fa94c,df3d0749,60ed45df,584e16ca,d60a0843,551f94e7) +,S(51cb80b1,9c0dc55a,888421e2,411baddf,6c0eb167,ed9bd04d,294623ed,1bb61dad,d14f756b,f905da5b,6466bc6b,26685501,1fc90892,93633c74,479afacb,c34559f5) +,S(837afe72,82af09de,108711a7,998dd45c,36654f3d,a3cb392d,c46bee0a,b43cfc8d,88b0ae50,5c30a2b5,72c0f69,8e7ecfea,87ea1253,ec133fe7,fb2aff9f,6adc2e15) +,S(ab967cb8,463da438,ad360a8b,3e066669,231fbd,cd590904,fe827ecd,dff3cca7,d82043ef,c614db53,8d06f6d9,9fae98e1,ee6f5065,31b05822,ed2141f3,25ebe544) +,S(5a0b6177,50d828ab,fbf4fbca,576ff8ff,5bd19330,bd357eaa,a5564e39,c718eb49,f6e8d743,e528429c,2cacc478,368d7226,823a45bf,358d9128,9da334b8,5a1b4426) +,S(351b4334,83fae045,ce964f04,f63af62a,6964e0b3,5c1444ca,68d09edc,9a43c9ea,f571f442,7b308ac4,83bfdb4e,87987455,a2a49f77,73220511,dfedc616,20bc18a) +,S(fe64fb54,727ce446,c00dc3cd,5de496e8,2d5a0e9,b13a9a7b,99a96a49,2d0d288d,452c4c9,aee35675,33ce8cbb,e444590e,f5756f14,33865ba0,efd00c4d,26db7d8d) +,S(9403ea3c,773544f4,4eae3cb8,91457ac,89b11f1b,66c3c397,aea8d816,ccab1d49,46ec1e05,d13b40a5,f21ce743,b2ac9756,8ba98b2,8fe1443b,3b0c9cc8,f8a49deb) +,S(e81a5512,2af7093b,b361ab58,534df80f,dac734d,7a863ad1,b080d691,c4396dde,fb33ea54,fbc0c05,f9854f1c,49cfabe,90e259ce,9aafae3,353e8f51,594d49f8) +,S(b7511df8,fccf50c8,192dc3cc,bd79985b,15eaa528,89c4d01a,e21767e3,96bec9ee,c937db57,ddcf0997,aec742e9,cd522c10,d4a52b27,b50104e7,b59c441e,3d0fad4a) +,S(5625f122,4406dbff,a818d98,1e88b5d4,80039f5b,415acc20,ae69ae7a,704dead4,aed9f73d,5265c976,760d094c,e9ced8dd,722122cd,e0b1b34,a8296fb1,dccacfe7) +,S(1b89696,dca54369,27bcf0a5,de32ff1e,e79f6ea5,6f6ffeaa,bce6190e,bc7e2c8a,52bdf0c7,52cc3e5b,1cb9b5df,adccaca7,929046b,bb67daf3,10ba793d,9369b358) +,S(4ef2516d,796d1c0d,9baa8491,7c2db149,e7b89f61,a8f8fb95,e14116bd,473d730d,9a0063bd,6e25255a,70479159,d3089a75,834e54a0,a28c5c61,ea3c33a8,7ab20969) +,S(b36c8db5,51fabcb0,c3fa59e5,9faefed0,1a53b4d,5470ce15,937c889a,a93faecc,1be63f1e,7f85bfe7,2e782cd8,22776567,e3b58794,5f37e2d6,b137da7d,42c8cc10) +,S(7db8ebbb,88c7c04b,fed18b7e,1830df41,490028a2,2918098a,9b34b8eb,c110457b,73fe3f91,f0f8a43c,490f4cbb,f8bab5b,9a6569b9,22a69e9,93ab910c,520fa9c2) +,S(47bb0b5c,c207ef1a,db28a389,7606661d,e5d4c740,3b2858f3,209b7dc0,cac3021f,150e2ce4,44f541ff,7883cf31,79befa4f,5df9c3e1,9da098f2,1d73c37d,1ca48b16) +,S(7803d64,d7622ab8,70a8b927,74e012c6,ab97fad8,4bedfc7b,ac4ce6a,74dad4e6,2662e4c6,1a60a1ff,5485bb34,c1d41d29,3a09a69f,7fe8a049,95245e2a,7770cbe0) +,S(61a64d4a,9a801549,4fa670f8,85988b67,7d317b9f,9b75e3eb,fc6d734e,fe3d3aa4,ff25af65,16e639b2,b6b0dd56,c0e12610,b1b00c3f,d8c59cf1,3fbb1b74,6282d91d) +,S(f2de8cdc,ce609b80,af8b34ca,1c1166c4,3ae0ab9b,e742b510,2ecdd5d,2e94307,d624709e,79346b3e,81b98871,84bf252,949c7f21,861fe6f6,666ef5e7,18ec5be8) +,S(8c740f9f,386f5a0a,228ea52,18a43461,142f744f,87511e9,67080dd0,f3c65d2a,a641bdc8,ae1d4051,a15b1083,27581bf0,22e92b26,a5bbd186,86fb4e59,79f7cd06) +,S(b15a4b19,17448b75,df927416,2795eb5f,60cdb4f0,e657afa9,989e5133,646ae240,e09f731a,226189fb,429c76cf,1ed9fe62,2c148475,92f26b50,c2c45344,ddc73370) +,S(44a65d90,26bbdbc9,89b0dbfb,aa645860,d7a15652,5286a9e8,c67b5a3c,e2dde08b,f2f23e44,bb6cd570,f58ec2b7,b90eb66c,30775109,81279b50,2fc90762,3f12b52b) +,S(60bc8123,92f1e7a2,4eafd5f8,ca0d8fe9,c5880133,209f17d8,6b95cdb2,c9df479a,4d39ae61,cf4368ef,279231ca,b72be100,cfee501a,da7c873c,3db18742,82076a98) +,S(ad5b9079,36391697,61edf9bb,6ed7ae2b,f7e852d4,bae22afd,655a5ed5,754f3618,bdb40449,723089b,ad8221c0,8a46824e,cefe4899,d5e073fd,8606524e,a81a35e3) +,S(c8166cf6,97edc986,6f824df3,82a515f,68ef5d13,dba7e0d,43e85727,c6e15911,86804e47,8118c092,3ab97ef1,ab9dac34,2bc48c7e,3fe24e6c,fbaa74a2,2390f4d6) +,S(3e0a96a,94dcdcb2,9f0df535,970146d9,7f0fd71d,7a4196ed,cd626903,aafbff06,5f6eda99,4651016b,86d28c9b,39efdb4a,b8ad08a8,5c87b230,eaf7ba70,6062a8ce) +,S(e72351db,dcab669a,f1df2767,f5e5f05,ca80608e,29ac5f09,b1ff76ad,43178a5a,552249f7,7a8b3462,c9a268f0,fee20713,5cc14ccb,15914be9,2cd16d99,b2520af1) +,S(a08e1278,71ec784c,4f80099b,5d83c5f0,91978e38,7b0b37e1,f4622c15,80fea335,48e6aef5,349f25d7,40ffc9c2,5de543c8,7e0553f0,54990aef,fec51f0a,9b3585a) +,S(ffb6f3d7,92596141,f7d15157,d1a42bb9,e9b51e21,61a8e297,5aa05688,ba67617d,aea9ba56,8aa7cd30,b9940c75,29cc1e7a,dd60a5b6,8911498b,9fff470,bc4453df) +,S(9c644072,318933ab,4d1459b1,d684c066,d3df3371,d7659a82,fd396bdc,a9fc9e,a115ce28,5a7d9bed,a2a962db,5041cace,9d3b2a3a,12149b05,ddbae7de,30cf52c3) +,S(5cd3475d,8a6fee1f,a6e3800e,911de939,a7568762,4f0a6e69,26ea6160,4ddec04,20063702,1d1d525a,f250876c,c4b49590,9ca8b39d,74603979,8c8b4b39,fa07c17f) +,S(28519616,c035ec81,4e851ef5,191e0545,5f0bbb57,12fcacf3,2b36de77,1c351f88,42ed56f5,5c51953a,6a367398,815963e2,64363681,f9a0723e,7b622784,5cc939c1) +,S(a4e11f70,b3554f05,d0634209,86549001,53d2ed50,1f09f787,99b83308,4dc894b,331b9d59,4260e0fe,381176a7,1873a66d,a7a3b9f6,2a930e0f,696f68c8,6939755d) +,S(c5d7c22d,7a8cc553,7ed85a85,46adde7e,ebddff6a,f7e8a6c9,96f50df4,248a4ddc,dd4749ec,bc029503,1106812a,cee59bb7,acf41451,45883a71,3256912f,be9ee03b) +,S(b56ad6a5,c6fab0,934257f2,9e266052,cdba0bc7,8c8aded9,f0e08490,71770c7e,6aaa84a8,99a4f8f6,3ca8dee1,6df7c090,264e644,de1e58e0,49f99fb5,fd5f4c34) +,S(16c4dbde,37e8df54,aff88810,678f4707,310f29b6,e9e5fba0,384220b8,4f0dec11,4fbdd92d,56a3f76,56b4dcfd,8be6b589,eb02c645,8a247c9d,264fb65e,c84a3a3b) +,S(15e63d71,f02f6bd8,1899f95b,d3051871,84b68d93,6a2ac10a,d29c39c8,d5ba9748,20204657,ae0e1078,6df04a71,938055c,ae208105,ef1beeff,663d8cc1,df478ed8) +,S(3c07006b,8a1e1fe6,fcbc3a6f,fbb7a780,a0008ed,513c9967,853a8cd2,5827dcc9,b2fe428,d4c8d974,72066ca5,ddf32e99,18b5e79a,e81fd6c5,e00b8142,566ce904) +,S(c7f2ddf3,b3dcfa04,91846f0f,2341b1c7,81ce2cd6,ec20acf9,a304fa5a,7dbe73ab,595d0b02,faae2b6c,f6e84bfe,1183ab9e,dcfd6c9f,411d7125,dafbf9d6,60ec5d01) +,S(4c239c01,c05d9c11,3d25dad3,3d14ab28,f94742d6,8c9c748f,1773b739,445bb643,dab0d447,de6a8c79,6809563b,e77c89e7,956f6c9b,ed930d18,f25696a2,e436ebf4) +,S(9e1a100a,c44c33ca,543ae407,f98fef31,28bd1755,6a16e5a7,c1390792,187fac5,540a4e2e,9864a32e,3eedd96a,bb08fe11,b499a551,497e0c83,cdba140,3b303821) +,S(c3b4d7e1,bf3485e4,d028e424,81608ab3,9fe37649,de75733d,78f725f3,63255d18,8f8b00c0,2210cd63,c04f07a1,e10b1a50,f05ad863,b8cbbbff,9b73c84e,c59b5ab6) +,S(dc226233,8dadc96e,5feebb65,b290ceca,f8f5bcf3,e1794f9f,6ade4eaa,b89f3372,25bebe7,cae8b7e8,d755862f,dbb929cb,872e1c88,6b7d03cd,b32303a3,b8b5ab9d) +,S(184b11c0,a52865a4,5f530101,24cb353,dfbc2d26,bdb3af35,d5f029e,6bfe53c9,237a79d,b53bcada,f0a96804,d8072832,4f59f03e,1dc7a76d,ac5fcd01,3838b110) +,S(4ab97ea3,943e8157,3cfaa8ca,680252f6,577ff2a,a93178e1,e1a3dcee,242b460c,b8a6293d,f316c9f1,5fb8850a,87bce47,27d2a38a,d1f5f63a,39d08c92,e9d6d242) +,S(a531936,799791f3,cfc8bc58,4a2967ea,66328c66,98ebad8b,5e5fe8ad,8cd025b,c24d6ae0,8fac8028,a3e0e079,426b94ee,2ca7c845,8efffe1,58f6cb2b,c9046c3) +,S(37cc3686,a6e75e4c,8d7375a9,f291ccaa,937c833,f9e16d77,2591e74c,787b1f7f,557c1bb0,a7c7ed52,93e44cd3,75b04a47,7788181b,b20dc1f1,f6704c10,5a8e399c) +,S(94fab00a,9418e31a,9a229706,112f9386,d537bff,f5f0c4f1,b241c475,4d7336ed,14bced66,1c6eb134,7684cf29,6a303e0f,fa5abc38,ad569011,c09cbfb7,8316540d) +,S(14b851dc,c6b4382f,ca64791c,74a3faf5,adbefe65,b36de8f3,74e12e6f,e3c20e1a,6353d0b,ceff99b8,53557d0f,a893df50,598e0335,249f96c9,4e7a46a,3c02ce1f) +,S(d08e3027,5e05da0c,22991340,8317ec6c,e4362dfc,45b946aa,41e6b541,92c7589a,9553748,22228787,2ae61975,d0969373,8cb346dc,15d07e23,68800ab4,deb5d463) +,S(365ab0ab,6d51bff8,d4bb479e,a666e110,9a838863,2a5f8cbc,b3241bcd,5ba88ed8,bca2f90,ef0d4145,90cf9add,2a9d6793,d678f981,1ad851b,b2f54b6e,6bd093d3) +,S(879e8da9,e1363822,b113e70a,b8072081,f0e34a99,49d698ed,a0f55b4,6704089c,d5091dc9,d33861e3,daec5550,1f52378b,65b25a74,27a04483,c862e0a2,dc1038da) +,S(17006515,8ceda656,b23941e6,33ab3ab,ebb527f9,52ea9876,a8341600,24dc10e7,39a7ce82,b0afdbb6,33e74517,5fef5165,cd44c8c9,d81be0c4,56ee0251,6410b0f2) +,S(574a4f4f,67e294f8,2055793d,e94bf7f8,aa7106a7,f7ef0f2c,3bcbf2a7,352f017e,eac4aaa8,f4b4cf2d,ed8c328a,99b24623,16ee6e08,8805f1ce,783c4cc9,eff7baf3) +,S(4ab81205,32603ea2,9f88ba75,f2f0c4ae,330bb5e8,7f371806,de04f87e,69d5e6b2,1f1967ff,cd42f9fd,1f892f43,f8ea945c,c0135544,acb8010b,8285403d,5e621456) +,S(9a4e968,82b11f0b,5e431867,80dd208a,ba25b3fd,1350ed84,f34f2b43,f03c4378,15653ab8,375c04c3,d7488006,1acf13d9,c5d669f7,bbf70490,44dba189,372f611b) +,S(267b8cc8,3ed6ef12,47a93f07,b4f1195,2fb1de83,6c3e3fa6,782b5e82,80cbd8b8,19bc5752,1a41c7a2,a9dc29b5,cafc66d,7e2016a3,334b4be0,e2811ee2,4ab8599c) +,S(69a8b170,876bb1aa,2faca2d0,a4ed8c02,44d2cfee,a849293f,5ffb4243,256a1017,f77d7a37,5315381d,97ce835c,296593b2,80c395db,a5311a6c,c7fc2c98,d314b4fe) +,S(88aad570,3a4365c2,96946510,5a3a7fdb,6e1f3e66,618c3fbb,ee4c4e13,f333b1ca,10ded35e,c15df656,fabfc4df,a1140b09,196d10af,e1e99b3d,7e1113ef,f7fb8670) +,S(50709e9b,8620f7f7,7b8b6c69,1360062b,cfbea4ef,6448917b,471c3485,55691440,3946b364,3cb1bd81,818b828c,77c771d3,833a1d4d,dc509705,2e5db99d,6cde2137) +,S(1ac1d530,7cf465b9,8f1140ea,bbe7a939,df8155a1,bd656168,55ae128e,bd3959e1,eb02a759,82bfa30,1f9fe87c,75b9dd8b,fe2d64d,7ea89836,1aafe22d,448b418a) +,S(126a8f88,e7f59ac7,3226f2c8,8851773e,873f1ea7,a87342fe,da5d9795,3a1dc956,1ce623ab,abb29b2b,b9fcbde6,a6a6f12b,ec68bfd7,aa98733d,f2452860,bf9349f5) +,S(592fe608,92c09243,20fb32dc,39e7a42b,1f5125ad,1e7d6790,9978ac10,5471427,c4d6ed82,4d33f52c,d975d34f,b9df5698,968a0c57,9b44bd19,8a67fee,9935d310) +,S(babab912,e526a92e,d903a942,ae8ad2a8,930ea5c6,17cbf913,7c074060,6462f0bf,76c7ced2,4318c1b6,c7295d9b,52bbd4b6,707a4170,d020e7dc,1c7b5e92,aee7a4c8) +,S(6554e9c9,bdb1757b,408164ad,1cafdaff,4642a3bb,cf64e785,42c324c7,5be1a903,439d023e,f6c63252,311cf494,4c46a8cc,a3d684a1,d66556e5,dc488d40,e5ddd500) +,S(79ad3f8f,4c429721,281a9b17,d9c05463,f26bdfd9,c856451a,1050b3f7,805304f2,3be5cb37,da868333,4f9a4def,aa8711a0,adb6721f,d4b2bd38,5ad7298e,940dbb87) +,S(fe559c8b,e982e801,7427d308,e50e4d94,d7094aa0,635e553c,c591181b,c9593015,9186fd9,c9fbecaf,f4747597,22b34b72,49fb72f9,f02a67a7,627c1562,ae80485f) +,S(d2b4bcca,38d1b073,5248b4a,7f43f277,8e03f46c,8d7f3a33,ad2dd9f1,70ce0af0,fe0bfaed,846a3f80,10fca999,d0c3b0d0,b54e2fd8,7a0bf751,ba40f560,9eb952e6) +,S(f6b9ecb9,631c784c,8b33a7f6,3fd0903d,baec116b,3dfd2414,4865b297,5c290faa,95fa4ed7,e67b828b,4c685850,4b003928,8ad9e27c,da175b4c,81730fc4,5b063355) +,S(3e70eafc,653ef528,aa12ce46,59c90ee4,32979f0e,9df260fa,b9063f3,d30de2ee,e240ec33,b224a5db,3761796d,2c1285d8,9cff64b0,7c36a184,996cdca4,839a0a65) +,S(dcc76f61,46d05c0d,67bdb161,dc395c83,663ebd48,6bbc6e62,f3576335,a8ee0c59,e451ed85,a9ae624d,4617f11d,67552eef,7279811,6767a29,59708e,3c21208d) +,S(3bb03660,430c43f7,e3b68acf,fe692b,5ed6703c,c808d7a7,503b3536,381180fe,c84c669b,2822cee3,627d4ff7,7c01c9c2,57c57e80,f35e4fdd,eab84a6f,b96fa1ba) +,S(5e266477,dd106005,6a59fc2f,df36b540,e051bd56,c9120b9a,41471c46,b1c5c60f,d6a7ff4a,ee4abbe,cee617,1ad01373,d7916899,5370031f,66f62229,1057e6d9) +,S(c1c13157,56ce83c7,93ca3a0e,12dfc0f7,270a0bf5,38177522,8850863d,37e5e537,750dae34,9090f275,5487778,5a126ea0,f3c2c0b,e3cb9241,326a863d,3179af7e) +,S(c03ae6f7,96692287,4cd10b76,d06ac08b,578511cd,c502762a,cf14eac6,4f1a913a,b3ba2ed0,6cd40752,dc97e450,10699cba,90f38f65,cc4623b5,44d7a57f,250640e1) +,S(5938d249,4edde81e,6fd50332,d81342eb,6cd0d938,f1f02ae,562d1305,cc665bf7,b585c184,1e17e6fa,9924361f,8d06b172,89782433,1d65b3c1,7f2a109c,8be27b13) +,S(e3457146,bf3f5667,e0621ccb,500854b7,a08836b1,e4315d4e,6479b3af,e5c2bcac,3de9142a,5c495249,14d3619,a92dc5e9,f20e4603,5cbe2c8a,95a8e683,bb287fc3) +,S(4187afe,4918d05b,74a969cb,14f54b70,95387e68,c043a581,7dcd1d80,4ca917a3,4e1c19eb,b38f0a15,36afd31d,bda30d09,3c776545,f2d8d823,bc3eae61,35585569) +,S(60cabe6e,ffbc3735,dcc19c15,1abfdf4b,cf082768,e62a1176,f056c4be,ff04640c,7b0fbd84,ee9d787,2896b9c7,19448412,c68a1ef4,e5c6cff2,51ea823f,76d6c4df) +,S(fc6883a1,231355da,115af629,4f06eef7,83447185,edc19e62,b53f3156,e0f540a9,73805d95,d8b2e4f9,d0fb36c7,9f09780f,c315c1b,724c5de6,98d96861,c521a6c3) +,S(90f37677,e738ee2b,3abc9d42,54085349,9cd02836,474624c3,9513950b,c325b66b,90884e24,a4171613,da7fc192,f1bc913b,31bda925,38fc0501,6a55af21,e08bb960) +,S(55e68c72,f76925a8,52d38e8,172e6340,3966f148,2a212131,cfca2497,ca40db3,ebd22c3a,1388485a,e4b48b4a,8b69b98b,755084f7,9f9018e,36769d5,d78cb7e7) +,S(c74f257e,e1bd81f4,fcf512a1,f866b9a2,19586a3c,62e7abc1,112b5946,d5b90e8e,aebcdd14,5555164a,bfa127ab,9f352034,f31a19a1,d36cda02,f51a88ed,9662f44a) +,S(bc63fc6e,d1963da3,7fa064be,f7a371a1,fb471ded,5bf084d,946a6eac,fdbb8d36,a26a6ee7,e178a31c,4ca535e8,e3d1dfc4,dadee69e,140a37cc,a306546c,26a75a2c) +,S(9d11ff44,5e331ca6,fa99f29a,78f5e769,44fbebc3,e3b45a6c,d46ad17,74a1f29d,2f007088,110e9ec,66e5f134,b6d61ae8,70b14741,e9b6259b,ff06753b,fcbfbccb) +,S(5aa47205,f01abec,79b52b1a,2625b773,659166f8,1e300d9f,46bbcdd8,532599e2,f56747f5,c7431031,5b630fe9,b162fa49,26757780,c6ba59e3,6e91c29e,c82fd357) +,S(14dafde6,c8818744,2059563d,1e2cc095,f4bf18e0,f7e41b9e,97a74089,f678ce60,6f6a348a,a4f72e60,9a44b01c,b688ea96,cdc7c3f5,e559ed8e,d9a1c7bf,cf1747b9) +,S(ad8cb71f,1a3739c,afc8a4e1,ea77bd9,f5703d4a,55812381,c5a04847,e85e9950,238d1945,cd5d6094,dee8c6f9,41a19b48,d1eb88,5c986dc4,b8f3908a,508d46d1) +,S(e88df4e2,8b7cb050,aa81792e,ad108c4a,d95e735,6443dc11,f77d70b0,5968de2d,9541f907,f29d79ef,83c25f29,fdfbf819,49f21604,adb5a5df,e939afe7,511cdcd9) +,S(d6b6549e,1ef4fecc,ab56860f,c7a8a549,a354d9d5,63b9d459,9bbfcd71,7631c879,df7a0e04,18a86774,ba87bbe6,132028e2,927985e3,b1319c83,4e1a400c,7b0ae231) +,S(d919b999,faaa7cba,99c79a2c,febbdc68,6d94e07b,3edf8350,6e9a729b,f0f834d0,4b19faba,9b6041f3,9d33a21d,285fbf7d,638f3a07,63e2b15f,df102131,49b94ed4) +,S(b89d011c,57cfd499,fffd2d1,8734f604,83df00a5,6efd43a8,35d5983d,573d902,977f7c06,ed8cb0d4,980c3160,b152a7c9,294d489,2c90fb88,1da40f39,adfe2825) +,S(264873d1,cf9f6f35,d6bbff5,a2405f40,a5568a80,2fa35069,ce422c61,d9f2146f,36b8e9c6,e73044dd,b9b9753e,c682db1b,550d8310,7e3ea3ce,e8ecf031,500b3c31) +,S(d4c388e0,950d24ad,6cc1dfef,a3735bf,bd9e729d,99fafc8f,e4fb5266,8816dde1,3d583de6,27789a58,fd4afbe1,af3f931f,5d12df61,7f5adfaf,95830cb5,4b13743f) +,S(80a2e2fa,40aed900,a7a55f84,d4965f0,2c5632b6,1317e801,b83ec659,cc06e35,9e61e863,69c8a367,72d6a4b4,c2b7bd89,405fad38,4f99514b,485974d3,2342f47) +,S(e36eb1d7,72d5d498,3cdbeed9,b64fa04,1da7f1bd,e324f1b,a734323e,9f5331ea,6c879df5,4a96d33e,6a66895b,f4d12fef,46d5fe48,8d62f073,238fdbbf,d94126d) +,S(7852402c,a08ff966,44a4c1a9,3f7186c1,43f0e1fa,203b5991,d3fda009,1e0a4e76,a9b15d95,3ce9b219,a53da27e,e1b03aee,5431e26c,f035fa1a,a067f1d2,d36d5402) +,S(5d33032f,f7bf3996,14a08ee3,a5b67ff4,850f37b5,eed88e4a,675b020b,90cc4ac,ff5c96f0,5ba73287,f3279907,ebd0fb7,9e0b4866,c32f6fbc,2541d8c5,cbe12112) +,S(138d3701,98eb6569,17b3fc46,c2a3a1c2,3f7c95d9,d355d51e,c85b6062,4675030d,94314f22,6581dec4,3518cd7,61150f31,51ccd59f,3ecf79af,482c4dde,8a24e28e) +,S(3f6ddeb3,4756ed69,de83946,b48281b,c2b60882,d0cd721c,5d76c5d9,ff898c2,be8b8841,12fbfe09,9af67a96,8f4bdcdb,db4469d2,34d64d96,99c6a575,518c48d2) +,S(74676fc7,cf3bce8f,c0c4a774,89b320c4,736b9ed6,171d0b42,17a1433,eeee384e,fce3cf3f,630452a8,1cf3fb72,63c1b5,b66df4fb,864d4b39,7e66c69f,2913c54b) +,S(e0d1a049,cd49ee87,d71ebd9a,95eef0a0,b1d7450f,617841b,590eab0f,3e883eca,99e00118,42e6a78a,d4c3ec3f,f7a61bea,a1a5f8ec,2f4e9066,978e96d0,cbaef51c) +,S(531b455c,c6051fc5,f0f3190e,9523aa5d,2d0485d4,3901600a,aaf207dc,7d2b1fdd,b95f8c0a,b0e63acc,542df948,4b112b2e,e49f53fa,7f3e1dd0,307d9161,7c0f313d) +,S(b86ba09a,2b05e91c,c23db107,9b34807a,778749c0,ed725437,69ff6c17,316e7db7,a91c815f,f8a3b2b7,e5d44357,20e3a513,32cf405f,7501ca3f,b1aa1f59,a01c10ab) +,S(b63b43c5,af63f2ac,3f6e004b,c790404f,a146ea97,94b1bcfb,c4d0544f,2ca3f33,31654108,481b7dac,cbd48e8,c20de95,11406381,ef38fe6f,950b4321,4b145093) +,S(63a09087,e237e0c9,686f2a79,af977ba7,26d77118,29c8ed91,4f99dc43,90c64d08,7771ce4,a5e7d43f,e12b5e8b,c819506d,3b85f52c,fdb0f9c2,92c22bad,790d3b93) +,S(77640a42,4ebd84f7,71734050,7ba400cb,9e8462a6,a91fd0a0,b8f152a5,f5f7868b,516688cc,f422e660,15afa46,b5889331,3cf2d622,d6c829b5,f1d11386,bce41640) +,S(963ae02d,62739bbf,e75965f3,d1b7786d,c3dc6fc,5667aebb,5544db3,157f8a17,b3a109d1,b56570cf,2a5e5056,fd9feb05,2e8f53c4,f2ced1a5,27c3311d,87ba9dfb) +,S(81324144,454cb304,36f365d1,c78ea5f8,9a73706,acc49d5f,125fa282,c28ebad5,77a71aa5,54c0da45,31ed307c,c7fdad32,ab82a4b7,2f2a5154,cc405a2a,fddd3cc3) +,S(eadb105,e80bf2d7,cba9b468,6f6b88ad,34008c85,a7ac4d53,d3fff09d,dcd566b4,199ebce5,872f9dd7,6c14821,622fd343,2b7b6437,3484fe84,714b137e,fa782bee) +,S(a030450b,52b387b3,35cfe8ec,683fe625,884ad73e,2fc1c728,c8d9629e,dd2024ed,895545be,a9009f2c,dcb20441,33f2fad7,ac704ab4,9435fffe,b92836f5,57a114fd) +,S(57e8022d,816255a5,f4d577dd,867d65bc,7d7fdfe9,ed8f993e,d9b30531,fa36888b,9b0b7929,3cef66be,c21b6fca,140e6fe7,9b9f2e8d,48555338,868b6c78,d300233b) +,S(74c14e1a,d52761b1,7206b61f,fdff526,f7008f94,b3707a5d,e5c2a600,97b0dd29,18778822,89711679,c5e41fb0,41b1a806,9b12b709,b526ca1c,44839d5e,3b51e72f) +,S(5a9ce7b6,6368077a,73c09869,38656705,c017faa9,2d9cd40c,69d8e57e,63f8e229,f716b144,cebe2907,928f4618,821af2fc,9de249c9,5ac61bd5,99a550d5,e97363d0) +,S(f0fe4fd9,99c66687,3ab4c904,b9b7c0d2,7f033a0c,7e6c1b6b,4488097b,cdb49fdd,1f4643f0,6f700657,90aa51b5,911b4866,c286128e,de489dc2,8e2d6b64,972738e9) +,S(e86c729d,19347238,a9e97433,355530c8,7b701aa7,d710d26e,6b15ebe7,4796857c,d6260d35,466dbe3f,58d2aab2,1c8d76df,18271247,9715bacb,2a76bb95,5b75730d) +,S(b4e3a39c,8fcb0dc,5a23610b,bbb564a6,765c0135,1a8b666e,d680291f,c97df351,3f1eef0a,7a235130,b5236a2f,f8c9d2a5,fe5f1cbe,22326ba0,54557513,82ec651d) +,S(a9c8482d,a54adc98,4db2d7ac,8fac659b,51c1237,eaf1d524,9bcfcf1c,4ae601b5,446ec925,fc148900,8ef10348,ee167a63,a10686f0,7a01772c,10f7592a,2a544a9) +,S(17adc7b2,c36b1ff9,ea40b398,53246d71,bb973bd8,b469f7dd,fce49aa5,e77b8c06,fc3111a3,8dfc5219,7f41ea77,1682bd8,618f9825,bb2f842f,c7de15bb,224bf573) +,S(328e9a6c,e7397c45,617aefee,130cb6a1,dd26e727,efcfc30c,3d8f415c,187d23b5,6320e80f,e4a57574,1fed0624,c0f956f1,d5b30914,b5da88d5,f63e17e5,64f8b927) +,S(22188680,f765065c,360c52d2,851f3081,b2d3eb5,4201dad0,60b536bb,a45a1a41,b1107c8d,430df246,3b3e91fd,904883b5,cb877b,c10b6f,397a0cf1,c4b6eb35) +,S(620f5772,5919db4d,53b8f89c,5669fc6a,5a3f3846,c31faee1,1e93e53f,b1d20bc7,7d739bc7,4becbd29,2784be81,294185fe,db047d4c,c16c764b,678f9d00,6d27feb7) +,S(49b1228b,b624c71b,2c23e334,b098e84e,d3d8fca0,56303057,49dd39bf,5296666c,3f316e5e,ca9c1aa1,fd5d23a2,613e756f,8ac5e819,8ac16650,a9dff05a,1c8d76d7) +,S(97ca98cb,fedaeddd,b13b86cc,4b6bd7d2,140fde57,dae7b847,c51b118e,e3d6b8c5,1b95c9f,1d6c808f,397c3ec2,a2a2b220,6fdf546e,8c70ac2a,78086ad5,ebe28142) +,S(a679e7e7,3df904aa,e80270b4,d5d57dfe,f5ae819,f240369,b773fa3c,609a1ed0,93201a4b,17286928,be5fdfef,473565be,879cad73,24124c6d,6e1614b5,4e6e5124) +,S(fe17af2f,70887958,1b9b176,4d45d76a,afac0afc,ff50ac8d,9179e53a,c5e3777f,f59728d8,67c3ab63,6d387352,a029b5d2,e9f2d531,7a508894,9a0f6515,f5c4d61f) +,S(f7601ed6,fd64cf22,57cc15e8,e554dc8a,e19e4f4d,ad3fb3ea,a7e819f1,98b3946b,a3292ed3,ff088c22,e9a62b85,4ec87dbf,394cd681,501184c0,b841656b,def0fa3) +,S(3dec302f,cad72c11,54c6b588,ad7f77b2,9368557e,77a723ba,6dcf9357,82d9d917,6484365f,537611ff,49f157f8,6c4bbf07,25094f7f,d8db9a2c,b318ab4e,632cbfce) +,S(a847783e,46ccf334,d7b15f1e,d3aa7dd8,c5b3ef4a,7ced2501,c26d1dd9,4358adbd,7c56f1f3,323a500a,a3496729,ace84a91,6bc6dddf,90435a09,8ca51f6f,5fe5fa99) +,S(dac6ee37,3c655763,b01c5012,fe01518,c3ac1b52,543cfd7e,9ca24d6f,42e4bcfd,6b861af2,f2a82f0,d4c458f2,2d264d82,54f16f46,87e31cce,7199f061,bdbc941f) +,S(27dc7253,90de6a8f,6cd9918a,e3b2f33a,8ca4307d,ca4acf6b,f9c5d0ad,f01b4dd,2a0798f6,7e583426,cca8ab26,e6742692,ee790f21,cb3560af,271b0a09,eb9f8b31) +,S(1c6a5d3d,10983673,5e27dd2e,ff04adfc,7bae277a,870eb4c6,d6379467,de2dea5b,92658db0,8b6b85d8,5c20ad21,f3ad33c3,8de02319,4bc54d0e,4d758093,b4e8695a) +,S(cab35e2c,b731cb9a,402f77ee,44565fa,d6623951,be9deb97,88da09cd,d5abb56e,37bdbec3,638594e2,59018bb,9145c4e7,dfb55b13,bda50c6a,c33e975e,6c5d96a6) +,S(97e4cfd,9e1ec85e,cc49cb90,ada87b99,bcaf8160,9b04417a,70fa66e9,78bf54b6,7423ded2,bc56ddb8,11db577d,f1863fe7,dc758999,55989e4a,d344ed4d,5faa9ecf) +,S(f2a9b193,6f63e9f1,8333cc3b,745fb2ab,7b3749a6,c59e7d6c,42e387b1,1e9d86fe,3c6b6bf9,1c2c4a,859538c9,db95a2c7,abbf8c37,b6e172b3,cc2be8f0,f18b5484) +,S(49024a25,11bdedd9,6c84aeb0,707b5b70,f027afb5,8b4053e5,234a9312,771160c0,22ab8282,914e69ad,8d573e7b,841e9eec,2fdfb939,c76ff330,fb2fa5b4,14a4523d) +,S(781c9886,4bf29760,c44e1f98,9a5266e0,4d25c6c5,f941e2b5,cf92b958,99ecaebb,994a771d,eac92f91,58ecbeae,6227d2b0,4c09df72,c5ee4439,509f0b2f,6e361509) +,S(8f7e703a,ae008b13,590ccda8,b70d2cfc,4c98480e,2a5032bd,dbd5edeb,aefb7f8b,54226771,863fa87f,ec101303,9238da1c,74f9f446,26b50f07,f35d6b59,bb7ff81c) +,S(ecb2e955,ed796695,3eb1ee21,564125e6,e860935b,20fb99da,91556cd4,e5e57bbc,75103469,9b958e18,638f4e00,654d6eae,f5631483,4c890ebb,7e2d93e6,9f4d8bf7) +,S(971cd7f2,9ce90bff,4c46eef1,9df001f7,afa6af09,b553e8c7,792f4ae4,d3f82cf9,d4180c19,7a579e31,f27874df,f29cd17,98e5740a,36380349,6f7dd211,59bda50c) +,S(7ad3b286,3952489d,42cc8394,413545bd,90fc63d9,8558323c,feb8e7e2,50ececf5,5c566bdb,5efc3683,2b22aac3,c0b81f6f,bf656c11,5514ac9d,8a223071,168f139d) +,S(c905b52c,ad0e0281,a90eead3,3939c836,ebd591b9,26787a4,2983804d,e658aae8,df2a40ca,f95aa72a,181f3424,98024f72,f1b9e46a,3816cf3f,ba5ac699,5b996fc1) +,S(e2517e6c,84856497,8ac94585,5c80bdf5,7c074b6e,fdb01cea,e93c17dc,ae4b3ff3,373ede93,2f369807,56afd100,e2f65794,69247690,61a597da,80d10fb6,bbadd3a8) +,S(4d52a8e7,2f3cd95,2e25c8,175e82b2,726a8d95,b735c8d8,a0f1805b,6d94ad78,248b7174,61287611,8000136,dc92b841,de63ef18,a7c9ff71,a10a662,af76ff17) +,S(58e5b42b,e614cca8,e131cbea,24b3debc,9d9390af,469df700,35a01957,3307ac,be4a397e,7809c9eb,2113246e,7812c403,4d42d8d0,c353d841,3001280f,db6bc220) +,S(3f177142,a6e6eb65,ca5e1609,cc89df40,3e3c74c9,e5b291cc,49fb0d8f,eff86ecc,2a445bb0,b648487d,3b962bfe,dc74e4da,d1d98845,cea5f4c8,d2f0c4a8,2bc4c514) +,S(3389c700,9b2b0c6d,a25a9610,4660a164,172a4c21,d0ddaada,c6902bd1,c9f04b38,413344db,77b83806,80647da0,d6086ea4,d1b8394a,a0047a9d,4c21c667,ade67a90) +,S(ecf3f933,4b2b656e,24ea0b14,7528c490,c3a566ea,c4c51885,d3ee94f5,7370b218,3c07fab9,b6404f24,77e4605c,345d3a93,adf9a06b,649206a4,b369712f,7388af0) +,S(a090739b,58310867,5e460a26,7e29f228,effc954a,137f358c,12be2fb5,8011b2bf,a3563d1b,7f242a1f,7ff5e505,280c3898,13251c4a,d9392d82,d3b12a07,e22271f3) +,S(a251cd29,357e5300,5a8600e5,bb9d56f4,c8251f6b,46b4d99d,5dca7b2a,31f8201f,51764fe4,596059a3,b1eb06de,56c6e21b,d58e7194,8e1b6e65,51e53333,5f9721b9) +,S(1f9aafb3,eb39814d,7173b85e,b027ad41,be210c3e,e5c857d3,56288b3f,3b05c5a6,d34526cd,16e7d3fb,76ecdfc8,c11b1bc6,38762bda,b03c1c07,94565cbc,df41febb) +,S(dbd4b724,1fa4769b,db161472,1179fdf2,6df4372c,d3b0c4d5,1e37fce5,317e7e42,7e9bca3b,bda0e211,5f2e69cb,8fcfc333,fc1b8b85,70123816,802b22c4,9634219a) +,S(9ce40b75,7a257205,fdf51545,7d6f9691,7e54a5dd,32b76230,4ec50c2d,3b89b06b,9b128748,851dbe81,255f60f8,1e5e9de4,6b20a545,afec40f0,38beb9a3,f7402e88) +,S(12f6c70f,46efbb73,2150f775,5f597fd9,2a527a33,7039def6,5533cfb,18df3406,7d9f18a2,4988ea,a41a1364,2a1555c2,bc9bfce0,dd62ba87,fa628b84,850e92b5) +,S(3132e530,9b169a3b,7401651a,351f7fce,43471b19,7ded3d1d,cb0b768c,5620fc5d,3e377f54,92c99dab,84f4e45a,7cba38e,9b589fa7,77dfb334,849310ce,53294287) +,S(87160125,8a8a69ed,d52986a9,25a54ed9,51f561cb,8ee9e1ca,6c58b826,495b9977,c5484596,8ebfc8c9,5f317681,78379b88,2e9c2548,b3c579eb,f6da5186,5d51f36d) +,S(82674b33,ef500391,2dfd5f2c,26722df8,19fd265b,3b6e4cbe,239bea50,298653f6,bb6d0457,2adf23db,86b07019,f2f59fa7,ebb4fd2a,ac36ff16,6b22e92c,3952245) +,S(2efc1011,28432933,56af99ba,ea2628de,a518fe02,c5b0eddb,3688c8ea,39d4b07f,d54a087e,bef0923b,6244b21b,f260eccf,e7d590b5,cf98be51,856347da,ea48779a) +,S(f2164194,5de0542c,de90f1c,4be955cd,98f6bb14,ff30ab44,92691985,882fad46,a87f6725,e89afd3d,6f24db13,39fafd45,5f578c9a,104f6509,c83d0694,ef795dda) +,S(72d2c60c,6f9bf0e5,5de82932,88c298b1,4d851deb,6ba7421e,ad22f3d4,2b47ee88,29626eaa,712e6d76,d85b9f7a,7a17461e,132c9bb7,a2943379,c5eee30b,1ffeb1d8) +,S(9bc6dede,ebdbd843,a5534fc4,71212480,ecfba4d,96acfbf7,25e980bb,8a509773,24ef23cf,e1580173,797ffe5e,baff2fc4,3cbd3161,e87e32bf,463333db,5ca904ef) +,S(20ea6c3f,51565fc2,bd5b8eb8,1ccd20fc,f3aafee0,9ccc9de0,733df6ed,8caa9c81,78328fb1,ff260b0f,cea69e1b,77b35679,d6663513,8f136c59,127ec2c7,d9668dd4) +,S(aaa2ee28,a4724d91,f37044a1,3b264ea6,9dd9123a,b3b7b300,999564d1,73057339,250b6164,2ade568b,8a56ae82,4488e740,b9a65f7e,a2b95f3b,4b11b6a3,1b49fddd) +,S(a1e54535,4a8976a9,33c04366,f0361775,824383e5,2c3aff3a,357788b,38014ab1,a6a306b9,b0a7d3c3,d6530094,9efb038f,a0a2cd1d,79bd495a,487a95f6,ef2b7a7) +,S(f2b0c76,18fc3ca9,f9ec5d65,2f39b6e0,158598b3,cb90b20a,53fdaf97,ac58f7d7,1d63cacc,7e8e46ab,47ce7e4a,e5a3336b,8d3d9bf0,f05bc428,f6f769fc,7ec6cd15) +,S(91843b8a,90a43571,4d7500de,fff102a2,fdb53914,2bab12bd,d3f23576,75a9d62e,3cc6365c,7dc3813a,d51cb2a3,d86491c2,c4e38f2b,10a33181,374bb0c7,ac90237e) +,S(f73b2226,8122a4e1,a440f891,14fd8d72,19cad2c2,9798cd0e,3631eeaf,6b125a92,bc14e84f,ed149aa8,ac261f96,6b192168,26f5a2b7,cfb997c8,3f293101,e758aae5) +,S(e87cfcb7,1f2b1c9e,8506b0e5,c56b14a6,52201397,1bb4c53c,855f1de,45644ff3,7e3399af,b4a9f70e,63907a29,c02e4d58,b2e76517,e4f2f2cf,a3d2fdff,808d5b08) +,S(b9657f0a,6de5cebe,47151293,de1fc3fa,b2745a18,bd5c4b8c,62282f26,84404f21,89590e9f,a787f41f,79a0b8b7,682bc353,1ad60107,771233d9,ec303830,e8e8dad4) +,S(b62d02b9,de395843,eb69e8bb,4d974590,7c8d3b26,cd4b13a5,6b4a0e27,44f449d5,173cb5ee,60d3999c,7e795607,bdadf8b9,acf9282a,d7a68ee5,6a204eff,ce5fe751) +,S(b6998e3c,31e9cca5,251a9d89,1aeb5384,fa86c92,74a0f027,8f2dc67,34b5f8fe,7658a71a,fe032a5b,ccb80a1b,1516faf1,5a3e8638,6f74b2b9,b6d17cea,61c3ac5a) +,S(864d4c66,4db88b56,6471852b,eacce0b3,52b55404,46444cbc,afad4f17,204a0170,7001969a,fb35a3ad,fc553e00,b6faeb21,c8fb1af6,135e86c7,f7e91397,a826346a) +,S(22a0ec9d,ecf7a2d4,6f550ff1,5a8f1c8f,6efeff9d,3ff65d47,97512980,216c025e,9d60fca8,97662f4e,947c962b,48522cda,c7ba956c,5a23c3b9,67f46073,af0980cd) +,S(f1be1f63,769f74f2,947eb25a,194171a3,9f9096af,cd454ad3,35809b17,d7e69fb4,b78b3a16,b734c528,c1911d95,895bf873,fcab1440,b62211c,25aa247c,161af243) +,S(7b196c23,1a7650a5,9db8faa7,39b7ffea,488b3623,a0bd7a2c,48b51c97,f5726c76,56e84642,2c430157,bfab8821,9112124d,6bb968e9,3ddd6b77,7ee08228,9a7e1241) +,S(6ad31b05,5a1f1a65,f4e554eb,31e3e048,8570032f,8af2891f,4436c640,9420849a,edeafdae,d10bf173,5de8ffe8,41871909,79f6377b,4cb566f6,3386534d,89bde755) +,S(fb0f3da3,a44c0921,85d90b80,1e7f5aad,6c85d5d0,b9cc4575,ad95ab34,4fa6aa7f,7857f22d,6f9356b7,96020384,919e26de,b9abd707,4dc6db0d,768f630d,b3589b18) +,S(c39b4ddb,edb892fb,2394e875,7c7815e2,f487f81b,45c60749,1c314350,c929d96,530f0c07,68e66dde,e10f0d95,bd66c07c,ba10f165,dade7bb3,77f8b7ad,ac4a673f) +,S(c50b3ae6,d14b11b9,a74dffeb,a5bfb9cb,73652cdf,a2bee6dd,6cc3c912,c312e537,85ac82f8,e3aad88f,9af29cb7,ba66f8a5,8c340d03,a1f32654,2071ba6e,99705377) +,S(a763e636,4e4959d8,9d3047,bff6fbb1,912d99f0,c092fe62,947d4385,2427ec02,bd799a82,a3cea8e1,db94946d,c7d32236,13b008b,da8eca4,921c985c,c022622f) +,S(2abdc992,b2ba838,1da0c2c6,2b35d3c4,ec1a1e39,516b6afe,81fdc7c9,cf3715cd,8115c826,d214c36,36a8fcca,37e89dcf,4be7f34a,2cce7357,3a6c42ea,9e084a66) +,S(2cb83bfa,ef83ab3e,53216500,944b2db0,e8cdb6a2,caad1eec,c9e14c33,a16a07ec,5b1b1199,6e84c17c,13c070a7,99dc0975,3d374018,6f9ce89a,bced7539,7121ec7) +,S(2687de95,b368b07a,7808ebf,e45fde24,d66fdc81,a02744cb,3e96e847,f0a3ca41,5728b8fa,aec73963,236ca282,d1a8946a,b2a26f28,a239590f,9d38bdda,d05a846b) +,S(b143029e,7f32bd1b,a381fe82,8da79713,b53550e,f3cf867,4ef1e952,d4dff9d,a44a622f,ff5fb466,83b69803,c6ea5c16,8d86c673,3ed94058,81aff0a,2a8edfd2) +,S(4c54d53f,cefac583,6fcf3b87,7202afc4,ccd68695,567c7fe7,6068546f,aa9070d,7f42728d,78193fd5,946f3787,6ab124f9,b1045ae7,45df58bc,cab6f59f,a27c6f18) +,S(6b625111,556e3ff5,db9cd8f3,7f610b3d,718220b,e459f546,47314b04,b2ba9d6e,b3ae66ca,fc58bb7,5a057767,8bf806b2,c204b90a,fd114ae8,4ed51378,53b30a78) +,S(b3fb12d6,e16e4279,5fcdc1ee,23d42f9c,198debdd,56217af,5b76989a,271b473f,d38adb77,4ce1d0b5,e41b65c,7c973a27,10c5621f,594e6f70,d235f5e,a4a89386) +,S(9c7c908,6976b590,ba9c80a5,fe12c5f5,ce6bbdb3,5a4058a,3cc59899,82073ae4,399687af,4d3e221f,6168ded6,31644c35,e41606e,94583429,34693221,9322da67) +,S(28cdf6d0,f0374a35,4dd169a9,1c3beef,fc88dea0,4922f841,401ffe91,8bfbddb2,2b8f1d3a,6f001d60,95aab0d9,d6248cef,cf3b97a4,850d1a43,ad2bbb3e,d2ea4518) +,S(1974b196,a25b6446,2ceda38d,d99662b9,7dcd0d7d,15299c00,9b8d14b7,f139e7af,c685622d,96fd6379,6bfc3f2b,6eacd54f,83281ef3,50d2fed0,2507a157,9fcdb74d) +,S(3beafb9f,4cfa3570,3d3346f1,afa48acc,25909889,a60b76c0,6e7774c2,acfe4367,6c8c09ea,7233ce1f,cacc82fc,9c1dd357,51021b92,fb86d6ca,3749bd58,6dfa369f) +,S(b5cf157c,4586fdda,16a111c3,9a500daf,b1aefb07,e32f78ac,a23664b,cef3be73,1d273f45,58e7c2ed,6f46ed71,e297dac3,59489934,b10afdc2,cf0f4270,ad6fd53e) +,S(9bd52baf,cde37895,9439aba4,b1fd4080,84441657,c2b1b36,c45446a6,7b661d6c,87f63761,6dba07e5,96d71dba,f86df28d,c506a123,c6f082ff,47886a93,da068ebc) +,S(20cfa2f5,b391d687,b89cad8a,a4c5ac8d,8624d4c1,ebc94cd5,d47999fd,3278febc,1bd4a896,4641d8cc,f6926323,8b1be2c0,49141736,ee780ba,d2110f27,a674239f) +,S(e54db30e,e18cff40,56473478,2c61d437,c79adc23,51739315,ba845adf,c7e8b6cc,ef631e42,9529a407,cb3f832f,6aee6fb,a26b125c,fd28f43,b9f07e4e,ad8b0632) +,S(588f13bc,3dd3b927,7508ca9a,82ffe280,fabd5cb3,1413e848,2a2aa4d1,78a290b0,95c81a4a,44e6af9,5d8f6fbc,ba3884d6,d6a54057,1835184e,50f5db88,d08517b6) +,S(1927e8dc,ddc7ed4e,62a32a82,bd4a0977,fe24a571,10b1acf3,78826484,de7e757c,e0a9674f,76122a18,dc6004a7,ebd1da86,f98e896,5585518f,bc5f91d0,4699719b) +,S(7b82c663,aa2201cd,71dd7c2f,b8303264,8639f9a7,9de52706,e2deb38b,85dfcc36,90dd6e59,e98c611f,a7162fdf,8fc503ce,71b0ad1,dd150698,e4c21916,d5ded962) +,S(a15fa4f5,97086036,9e9c3946,5f16a458,c7b36ec6,6ad384db,7803ce19,555a2bbf,bd406283,43e40d18,25a02557,6bbf752,35e34400,dfabf022,6cf2bd3d,8fe80901) +,S(2b6199ea,7c42b7b4,2230da51,a895ba86,83950dd9,d4d5ecef,80512976,a0df55af,26ecea05,16faa497,3fb9865,16415ebd,93ce4a6b,e4b5cf11,3507d3e6,6a70b692) +,S(35ad2f4b,b5ba61de,364e8198,f5404ecd,4cdbf26d,7072dbed,d9892804,364b43f4,5481372f,7d1af5e,9e60cb02,ae8512a,6bcee9c,eab1344c,6ac5898b,aeca2e94) +,S(f4eeca4e,2ab38839,8a459df5,b94cf98c,68017522,353aaa98,bc606bf7,5008c6a3,e016b7f9,aba6e910,6a647662,1e7a5b7,12ab97f6,498f451c,21902e0b,9b6c2c2) +,S(6c488180,aeb6c444,78ef24de,21a47431,b2e26d9c,bbf059a5,84631829,a75ec053,ccf084c9,38491cfb,8cf0349f,fe80a315,71bbe0cd,dad9a7d,a4434cbc,9160baae) +,S(a52e5bcd,2214d405,57360828,10ebe384,a94f79a6,579a8966,6f1ef7cb,bc80f4e,c28bb594,e15c47df,9bf4ef2,2666fe4b,d916dd9b,e62b60d3,49c2ba11,506fb79d) +,S(c55414db,3374563e,5c79d9b1,1103ad01,7b7c04b2,3afb5431,8c469cb,49ea9754,f54a4335,7bbaf088,4d18bd70,6f3d69a2,44f4d527,a6dccf5e,52f468e3,752a998a) +,S(b7771609,335ac0ee,1e82539,3fd3cfad,5cb4b06c,98a05bb9,95966e4b,6fc1f24e,f305d878,33f95e99,ff6a3b4a,2b77ffa2,4cddd5af,d253ebcd,d60abb1,438e0745) +,S(da220e1c,c66af350,10736e75,483e2573,91f6d2e1,d355d05b,8471cbcd,4b64832d,bd028d5,5db1c54b,86f6f123,bbabe6b0,f3e52e02,ca48d92d,464311a,aaad3cea) +,S(1a680b36,991edb40,ada892dc,6c102b0b,186bce17,280f9850,ca50eae4,6951ac7a,f03d7958,b0789cc1,43d61fc5,11b70f9f,1ef17239,dd6a057f,ca394f62,225ed9f6) +,S(af8ca10d,b1975668,b8071ea9,5d474b5e,424d461a,e98b5ab7,bc240cc6,44da9c57,e74ea4ca,59f926fd,89bb2bac,61790ee0,50d746af,5e30019c,5b53130b,9cb9786) +,S(e6014570,2ad63dd0,5d98c576,c3d5158d,162857f,7b83045e,d91865ac,bb347922,52931836,4a9fe323,40f13c0e,15c955fe,f718ec64,4b45b141,33fa2edb,c65baa18) +,S(56eedaf1,fec96ae,c2f1e051,9a1c4a76,dc0ccd67,1781cfbf,7804d215,626dd5de,adb8a791,b73aebc9,c6406e2f,83e611b6,286761d1,a6c759c1,de85658c,a4788923) +,S(43c5272d,a3dc8c2f,edb0f5bc,a5bd5f40,fea4bd48,9f367675,cb578690,636fe0ad,52c62fe5,1cc2dbbc,57de501b,839cad13,b94c9e12,12b1cf30,d8463605,f7871c43) +,S(66e76528,7cd0ebb1,9d9e0d76,aaab4230,1aa87367,c3a3f96a,101c6125,84f816da,39a4d3ef,8d263217,3a3a8c3e,d9c1b460,e240cdc6,9c9cbaf9,89535604,a2f28edf) +,S(1a274502,31f1b5c4,9df7564b,f311a7bc,d5123e75,2471c243,65c0e142,d3b292d4,5eb550b4,ffcb5e1e,c695f22c,dafada32,1e967ac3,7e1fc20b,e0e695c4,324c1131) +,S(36f1c3b2,ab6a692b,5d3b1e10,f53b43c7,7d5c4e76,90e089dd,9e70eac2,773c3620,5553cea3,8e56a7dc,384458b,84c419cf,2d493246,1f75f16f,2d1a547a,6fdf0289) +,S(1ff0f8e7,8318fcb0,2b23a1d3,61015c67,6b1a446e,784bbdcb,c088b241,3da40369,39db7b2c,6923a1a2,379b58ee,cb91ad5,554ed5c5,60d5ddba,225c0074,e9fa4415) +,S(c5d7b7f5,d6dddac5,2a2021e8,437f771c,216bfb43,24c57ba2,e8b43a0b,dfd17e8a,78fc37a3,9586cdd2,f1145cd6,f9beda83,43e8ee5c,854c256d,419fdb4,1c9d2bc0) +,S(4af5a3c8,cf107e16,fdf3522c,47fff7c7,b2fcff70,c6c8b36b,7d66a3e0,4b107250,b1b6e5cf,de869937,45ae8632,c5e2860c,76a5f481,ac24ac86,ad135136,e9ff4e04) +,S(37b59d45,86fff48f,67ff3ea3,f31f721c,3daee8f1,a2ff960e,3f77b793,3e35b50,73a7d048,2c93b9eb,4c2f6359,3ce61497,ed455970,9b2ad7af,2d0f834d,6f070d98) +,S(6e9dd180,d5b7c41c,5f529439,e46534ae,b80bd802,b8b12e0c,ccf532ad,a99e663d,e981b043,e936da7f,ecef09e7,cd118646,5bdeaa64,45a00a59,1139eb9e,5628004e) +,S(fde9062a,9f7e13dd,39117543,be4aa5a8,fa10b810,a2285661,720b4586,68acc236,fa39cd20,a5eee1cf,3727df99,32c5289a,96b1409a,5721b9bf,1ad8f42,ee35069b) +,S(d7ef4012,ecc74538,16c392d0,4d1ffdac,76a30992,4c1489e5,159149bd,84847835,b2f5e699,9883a21b,edda98cc,ccc1ee2f,258fe291,c508cee7,39619659,e18c9720) +,S(8355ef2c,f0e37956,56b77adb,5f700f75,47e2c14a,b5f6a0ef,8716c99b,a919612d,d75dc6c2,fdf0260c,6682b16b,42d022f2,f03bdd1b,1e6ef520,da295465,a1988eee) +,S(cd49e795,63655bfc,87bbba18,b72a0c50,52568a26,53bc0543,3661f3e2,2ff159ba,ab28dd46,696b7414,1365316a,c8cd8dc3,2c2f5e01,6ae34302,d103cac7,4e37d25c) +,S(8f93b20f,28f89a46,1afdf08d,7550964a,dfa8df2e,ba2c00e9,9d351b84,2d6e0b78,92ffb09,812b6970,7746493c,c833bdde,6849a2b6,ee7778b8,2b41adcd,62b5620e) +,S(a7b3fa74,4621cc50,f6719d0d,5fd3f2d9,6acd3163,4dad1114,3ece3db5,d114ab2,948a3716,9394841d,68e5b64d,2f86798b,d78c8530,bb8d4d61,f82b5f51,ed0f56c6) +,S(e16b540,d62e3237,361ba514,a11f2ccd,ff72ae57,b37e58c5,d4c3c49d,2cc8fb7a,67efad5f,f02c36ac,a0c0ab0f,ee5e5135,fa7bb3c4,51472d37,abd0711c,6618a7ff) +,S(589645ff,5c3dc06,9d2ff542,5278a51c,c4fff14c,3de96036,b773497f,8940d240,1ea7ba1a,95f65259,c30ce21c,a95b3b95,83d11b10,3c8e633f,6dacbd2a,fbe44116) +,S(d551239e,3448ccc8,9a83b8dc,41396df7,d3db0c3d,f79490be,9d44636e,f5f0d0d6,779118ae,a8db8a05,f2231b5d,923e9d97,5e69a462,5c2ad551,5a8e7777,b1b25899) +,S(c45a22f2,9f611893,d8577d7c,9c1cd46d,82f41b48,a7c18628,ddb609e5,abc434f7,515624f8,5776268b,5a5c74fd,8d87f8e3,3e47648e,2eb8c2e8,27e9b57f,7fee5a1e) +,S(fb8e4546,639d9571,90f2840a,2f72ba12,a72063fd,85f160e7,18477d01,26132b85,3cead630,f8bf75c9,31d19cd9,6f4f3718,1efc4854,b937ac31,c3e0dc6e,363db575) +,S(ec51e7e1,6cbc70cf,1f053860,fd21e120,98b652f8,df9eb31c,74eb787e,87e5ca5d,bd5afa95,6996b7b,357b475,fa7a326c,d6c2505d,1ebf6fa6,feaa27c2,c2685867) +,S(52c497f6,f8b9189d,30fc42cd,9c2a44d4,2d4f70ad,815ccf05,4f50571b,2570bea5,35f54d97,a45e2712,b0f9a720,38adbb27,a31cfefe,c065ba9f,883d36c5,c070e9fa) +,S(2fd360f,1680b7b6,5f49f64d,5a648e8c,97e4a756,fe88e107,91fce9b6,295cfc85,7a88eccf,c4d82e86,72780c6,e1c34bb7,51a6029e,dea0cb77,3d29a85,b0689f5) +,S(7b328546,25307c85,f9ad0c31,ce2c31dc,e746931a,57ec42ac,16231165,74f63d78,6862d363,4360e404,9693dc03,772f4840,4a30f167,993c8f1b,56244c0b,af6e29ec) +,S(af76b473,4cf5f641,25124792,fb558b97,df60d67e,41c2b104,ba27ff35,e3be96c9,21e6d4c6,c1e0d3a0,69520227,c4cf2ef2,8204d85e,26f4ebc4,5d6f81cf,10defdb2) +,S(5b1237e0,54c98238,df2ccb28,a5b42b64,cabac24d,1126c8ea,5520ed69,ac449521,de7ac1bd,4994cce1,8800ac0d,5499c33d,bdde3096,1a0c9028,58de0325,b91ffc36) +,S(b81bcaa2,25743a49,36fec95e,13c8c20f,f818ee5a,6bda3850,870492bb,b3380d5b,6ea959d5,483ea6f3,5b3a4a5c,1987673,a8196c98,d8efe8d0,3aed5884,dcb391ce) +,S(5f6ca7cd,1054051d,3d8a2a83,73be30f3,516b305c,feffe073,ea2773fd,77eeab3c,89287d8,22775f53,61c931ab,bbc43d38,380488fe,5e35124c,2daa5441,3b36c126) +,S(ffb3a36a,8345f9ca,30369f21,f04dc9f9,d5f0282c,d0e1d68b,f5ca1cc0,c05ca91e,21ddcc3e,f5a94fd,8046295f,c0629b61,b1e8fea9,364fe659,d298e0b1,be5a6c17) +,S(51902e3d,8801dff4,e6bfe7e8,8f600d75,42eef0f6,e186df29,3a8c691c,d57b36f6,48a27fbb,cf9dd38,a1ab966b,f635fbeb,35bc3fcc,52f0cae0,d9a3ad5b,2654eaf1) +,S(f1a68a75,8f0f8937,e14a48d7,15c4cda0,679d9b9,12682182,493cd9df,c3ccb345,db85d3bb,b8431e56,d21e8c1f,4eb1c750,225bb222,38da0bc0,30e5da0b,d4f4a985) +,S(81d88ea8,4969d53e,fd0aaaf4,248a3558,f37059da,a4b3d32,da538bf,ca1a4d15,840fe961,11756d96,5c8cea1,32067ccf,31ac66bf,e1086f16,afccdbed,f16a2d33) +,S(b5830ab6,2e236cbe,345044f3,af940029,39b00b52,e779ce10,b02c446c,84163e95,d8720333,b210a391,499f38e,134a0989,254faef5,126750c0,ec2a5850,e1d4a821) +,S(b50caed0,a4e9e41f,f03d16ee,1848d92d,d4e6b76e,4e2332f3,793e8650,20bc4d11,39195ffa,13745502,eed8801c,40f36e2b,d9b636bd,11d62388,8ebab8a6,41d3ab54) +,S(6190402a,3750824a,149945fa,df942c0b,3622acc,b2ee1304,f260593,8de5c333,290f4d4a,73a1c72d,c5ff4228,41e2949d,966da112,5278d082,ab779e8e,d4cb179d) +,S(8966488f,4e991a1e,e34552be,c6246b2d,b9486442,47fda545,f8083b7a,b0435060,7daa3ab7,87283446,a7900c83,b726b849,f7dad684,6326148a,aeeb6233,738c6c4f) +,S(edfa388c,fd246c1c,6bb7bd9e,6d9d335f,9cbd7018,ff2a750b,4ba976d9,dd922b5,c74e1113,f10e6954,6848e4f4,11a5afe4,bbf7dfd5,d252e66,5789627b,b81ce714) +,S(304740e5,1b99d9ae,97de2a55,8dc1abc8,60bb8b5d,a52f6208,af8e28f8,89904951,8ab409b2,3606089e,4a111377,c8a94e5e,75ecf098,4f62aa63,faaf7e18,f93a37d0) +,S(d4ca8aa5,e31028a9,37d77295,96b011aa,c26f58cf,3a7ff671,99aa7f30,3e00bc30,5ecc09b3,bcd9afcd,309280d2,617cbfb7,e4a9350,b4ef79ee,1e46a8c9,56d46fb1) +,S(4110ec3b,ae03a061,59910aac,4f2e4bd7,77d5e055,4eb6920d,aac4b835,2d7cfb4b,c1237e1b,d6f1cf1b,6fb0f248,9e3712ae,5557e4b2,f72ea7a2,2c482e6c,d84eba92) +,S(f7c55f97,3e9df72c,c9da8e04,b1926688,1d53f810,fef5e664,99b81f44,e4f9800,4b45e93f,e02c3993,95238c80,b65533d5,ebd01cbf,1b3dbf44,4d550cbc,4652fa89) +,S(2de3290f,56fbee3e,15def5a0,5d65436b,9d722e3b,a435f269,a5ee0231,857cbd29,f80d2ac2,356ac72,f3106b47,5cbf3701,bf924e3,48e93c9b,f3327b55,bc203828) +,S(c8e3d2b8,21922146,1862d5d3,319c2abc,7dfd568c,59df4367,eb975a95,6e22fda8,d6ec1b9c,e758a8c6,47e718a3,ee8ae232,c416d763,d2887e19,54ae4c30,30404f20) +,S(50c1a147,c117d907,fc831f72,9be19b3e,e93733e3,aa3bf402,e64760e2,edc82065,acebcfda,80706b53,116e4cc8,cdede6d,5aa1b7ec,62ffc942,572f729d,47396289) +,S(7bab8930,55114c0b,e4f7637d,ad165491,c21d6970,c8036375,1387012a,7b785528,6e31589e,2ecb13e,b7bca5ee,39e3bca,fd2e5645,6574a642,131d6178,901c0d97) +,S(dc94ff4,44a12ca9,5180904e,58802ed3,d1be43d9,8396360,e9e932e5,622d257f,ee2a426e,4da3dbf9,8a6d5ad7,9a5437ce,6fdbede1,25e836fa,697685b5,989c86e2) +,S(efb2b411,e66f8e8e,186b0fd0,c572381b,e9edc68a,903e95f3,759a11b6,b6a9dc40,4a7af79c,a1383f20,55bd080b,f410f554,24a21ae5,7b57b63c,bea36486,6c30dbd1) +,S(2f37ff22,26a0c4b8,77a36da6,7a423f59,497a26ca,8066a651,c13573c4,4954599f,f9d1c91,2b74f663,56798637,9de41a46,683a7cb7,61ac6e3a,86d65094,d1e07a8e) +,S(63aa61b2,e24fa178,95688d95,442844cd,7e68edcd,98eb3496,ea071c4,5b46abd,9423daf8,b6c241f8,785b76ab,a40803b0,97da461d,cf6579d4,4308c9a5,e63ac4e5) +,S(58618e22,93c52375,4db8187,34e355ae,27b73f7c,4469d7d9,115623f4,e8e20900,8531be34,22616853,c2c095aa,138819b0,bccd7c51,1380c64c,65ba2500,ae18794f) +,S(61418bdd,2619e7c8,f32c8cc5,34097cc7,4a9a0c57,a38db50b,ad828a60,f658fc0b,acec4e21,774d283b,db9a8a11,115d20be,20cbfcd8,2d67b10a,3d57dba7,74537c48) +,S(79ed2dcc,dd4bd25,67dad04a,b32c9273,39a9c592,95a69b09,be7965a8,c779b2fd,64860dc5,d17e409b,4feef8ea,8a7d7350,3eec186,1fa15f8b,f5576a9a,39f65283) +,S(a20434a4,c51da2a4,c7a6699,84f41ef4,67bd4de8,a628fbe1,385693ee,6b6d8a52,e2a00a0,d00aa33b,b49743ae,6f9c824,d2fe1f7a,f51f7720,cfb0d800,94279dd1) +,S(d8c0bf47,530ff603,c140503e,84fdf6d1,ed908b98,cd27136d,ecb2c4ce,da9f8c61,19e02399,bd898b4c,b9987fb3,4262df35,9b32cca,430e7cd3,aea9d20f,530610aa) +,S(29f06c46,a20b5387,aeb6e490,ef9a7bda,ce16fc2c,8e8eb202,b75973ea,8dc4f3c0,2220f678,daae0cd6,8514c091,a1dd7060,b21c2630,138b5e32,e0d57d79,b7b06d7c) +,S(b18aa504,d9cda828,e67d4f55,ac51c961,e9603d0c,ff737c16,42f1c8b3,fe81aeb9,1f6d7e83,480a9290,789c016b,b62e6e8c,adb4b500,fac710a5,7969bff2,89578ba8) +,S(e7591f42,e649f9f4,477c9cb,bc8559e1,f01eeec5,e9f3a0b4,8d9bf515,6d2044c,24c8777d,5e7c3f7b,2c6fe0e1,cd0e3845,45bef898,aff192ab,c4e719d8,ae466385) +,S(65992fef,50a1983e,bca478b,419ae258,77ce7a52,339ecbe9,2394dba3,8ba72081,32491cbf,baa7f8a,9cf21a4e,57ffa85b,eaba7653,d46d9f95,b4e25212,7bf76781) +,S(771b52ad,e47d035c,ddbb1012,bb89aa9e,b774e0bc,aded4998,86e1e358,ccabf9be,1a897dc8,420a076c,5a723f30,206db0bc,e9760b09,1f698952,1c4edc17,f2dcb202) +,S(a7d5c532,bcaa8ed6,6846046f,d1570837,111281c0,795d3e50,16cd8108,5721d644,13ccc5ee,4bab0d34,2f740ae9,e538d50e,900aa30c,bfe99e54,c69cecc,f18f6aff) +,S(fb504652,a55d01c8,b45ed7,6c71d,9067026b,27e9ceed,c38bf6fc,aee16d50,a150b6fa,f37b0c6e,ce4ebbef,22277b37,44933fd7,a135a25e,5ba8f7b6,1a6b7d0c) +,S(6242a221,115863a3,73bea0fd,39974dea,3c33b794,68e04087,8ebd8bf3,ca09f1d,b806de29,fe9eddde,1556c09e,973fa20d,c4ff916a,3d22504e,19e33169,f6f903e2) +,S(30382a2a,fefc7110,77af1a7a,d8a4a4bd,622635bb,73e50bab,c58116af,d1e8f3cd,d7ff3c80,9845a620,d53299ee,98cf3c6c,499ade93,84791af5,d6618866,3b6b74c7) +,S(b69ab576,13d1527e,990d747a,bab1582c,af995b34,6b25ed1f,df3490c,5549f36c,cf896a0e,4878bc0b,ffa3a750,f83dd329,ade24fa3,76acdba6,dfdc294,768e3f41) +,S(36f6c331,4071ed0d,b2be8f45,fe19be0f,9b37a675,a56679e8,bb49925,9339257c,d593ff7e,a32103e4,9cbf2995,21ad3da1,5b1b44f9,d6c95418,9c794291,b1e6769e) +,S(fc312428,7a9ab1b8,3efce7f8,17f707ae,320dbe77,e5937cd1,513bbbbb,90ed9995,11bce5f0,91dd9ef8,42a46aee,6f3a1d,7cc23da7,381a5f24,4b5a2638,45b2bb0e) +,S(b0de8720,58e1165f,ae1207ec,5ec0888,5f14bab9,35a2fe6b,1f7212f9,7b4913b4,5bab24ac,2589b8a8,c5e102,26a907ac,391289ed,23fba10,b2acdb74,a142b395) +,S(699828ec,db84587e,31ad233d,c27ce5de,a8f4e03e,2c450706,d625e387,12c0af3d,db1d9e5b,9a74caf5,855aef9b,6b8d0c18,c98c27b8,ce0d8a67,c6bd8467,261f726b) +,S(8d133509,d51edc48,97830f8b,3f8a6688,7a16834a,5ea31602,1649e219,cf899cac,9d188e67,2940f23a,39ac6ae4,fa532478,9c5df2d6,57f8b4e4,eca17e02,5560f08e) +,S(9cd47f8c,a9d3ebaa,d94dab36,4412ac4,a74a4b18,bb516538,10da1df3,9efc7582,de1e5cb0,fd5e7a20,322636d1,883b1ce9,71bd0565,a0eb60fa,559b552b,34b42e1e) +,S(1fef4107,648ca126,d5c4fe59,1f190f21,57274d0d,d9c57fdb,ffe2c348,35d36ddd,abf8fe20,87ca3feb,d8150e5e,c66c5d07,45731121,9b759430,4465bb14,55f992ab) +,S(e372b12e,8d3fed92,9e01babb,1a6517c9,ecb54d3e,6d0524ec,42ce57f8,5351d32f,43e910a,32447627,e2d9d5ef,53cb9c0,ab60333c,b07e72d1,c2c6644c,83e7d8a2) +,S(37c346cd,ae9d76e5,7ba2f30f,d05c33fa,3e6058af,642fb1c1,1810b4a0,b97e2dd5,9939c619,e1e49ec2,bf716714,1c23e135,157c7e4e,949e4fcc,b7dac7b9,7d4932d0) +,S(14e337ac,39f0c1d,98333716,a6e596eb,ad0d0688,b815c7a0,c182b546,88e5c3bf,fe45e34e,bfb94cff,a72b6312,a422562c,8ba782e2,16d5ee31,6425651e,e67d3d22) +,S(25517b3f,79da61ae,dd50cd1b,69b1ee36,fce0a44b,feb572e2,e6eb4aef,d751146d,7d2a017d,b9089d13,9a072803,dba874f0,f169ea36,95786e13,7fcd36d8,8b28a213) +,S(7d5957a6,92fd532,b9991383,cae895f2,8554e6c7,83ae3bcf,e72e3678,3475cb46,fa4b9b07,2bc725f8,e35b01b9,590ec1d8,f205f6c1,3203e91d,d9d39954,922d7f65) +,S(77eaf892,c4f9e0d,f63e33a5,1c088e0b,323ddff5,6f854475,b143b458,3e0e2f81,93d0f67d,51abf50,ea260593,96effe6,7f985caa,a9d68250,224e7193,414105f5) +,S(812c11a8,2851f474,e5a0a175,fb527539,7f1c73e7,6a766cd8,7147c9c4,4c970ec4,f9e22a19,a35179de,30379c37,a616d226,666311d9,4ad62b6d,59fcc579,cd024af8) +,S(b633f071,57c5a0a3,7ff5f626,17de24dc,14b3bcd8,4eb43385,e5a59be5,1c371fab,93e70e48,68ab5a3b,e29dab87,8a50e2c3,a09edb5,bf5b47d9,a09ccf59,53b67673) +,S(5e78caf1,9a2031a2,d92e1ba2,1f43f91b,10a98672,5d23ef95,1bdc1f83,cbb16343,b438317e,9446b88e,92206d81,883d5f3f,6497ec33,d144cfc,3a7dba41,db53cbc1) +,S(dd082ebf,88d49fed,50b76df,8e64d3a4,a1d4bbb4,fcdee50,c3ad6d0d,a91e4ec3,9efe99d2,f15d46bd,26d67bc5,ccc122ed,bf5034d9,28bcc946,e4714784,5edb6244) +,S(1ba1e135,62c06b5,63db5c49,7ed46ee5,73399a56,15fbfb1,4e080815,874e6a2e,f298d5df,43111fb,2f050cfe,2a06b3,c9a1fa02,d409dbe8,a225a3e,bec2d8be) +,S(49477847,a590a536,e7370341,5380459,843da9c1,78a1d176,f2f0133d,6ca214f3,3d1b8f64,a1d94169,2baeef3,86b4740c,4eaa84a9,15e4d03,2bd7bfd9,f5d5bad7) +,S(c4336d6a,4fbc288a,2c69fcc9,691fc8e0,e277fb98,e34b7a66,bf29d2d4,32c1339d,64ca0adc,72f0ff2a,f150f01,ea243a51,e91da8e7,480e0553,c7bb268a,22fbd383) +,S(a510cb67,9970c117,49e4e9d9,acdb97c6,d8712ca2,7961ca45,ddc9669d,9e02207,afa065b1,46b75ee2,ff7ada4f,ac7b863c,f4937cfd,15ca2ff9,c0901c7d,faac88b7) +,S(7cd63347,59eec80b,666237b2,abdd6c6f,e5e24a60,9305165,b5bb1a04,2898cc7d,abf7dab,f9e12893,a8040b13,afe16524,1d749660,93cf2da,157e2a78,e89cc758) +,S(e0786f8d,6f339bdc,9ff47416,4070bc08,c864d0d2,6d507762,19c2ee87,61cb7959,892cfd2e,62f4c352,b296a508,455a1dee,8dcec943,f5109add,9d38437e,87ec58c9) +,S(d81f740b,123cfd5,9c5ceb73,47668148,995ca21c,a3ca1b8c,b48e20db,32f6b55d,3af34294,c83c41df,7d5f821c,68e71e34,4656be14,f09d1858,7a204244,6f74f4ff) +,S(f4ba0503,6ffc1812,efbf3fe3,81423d91,38eca2,a67a0f4c,2d0474e6,5db9efc0,b2b3c3b4,4af46f2d,e53b1e8a,adb83d59,4c32b6ca,a7113207,b7993bef,dcdf78b) +,S(aa6f03ed,50640e0d,72fa4de7,ec3964ab,82e7758f,5e6c16d7,78c18296,e3b1a527,322223f7,ecf473a8,36bcfd41,e50f871d,715f9729,93df1359,80794587,59d9767f) +,S(f2d48f68,23984bd9,e8e87772,28da095,4ca134a0,6b43e0d1,1a8843d7,1fd0232,f0b7d9e5,982d0f6f,5ef74bc6,78ee504b,6202f52e,8db7c069,e179d175,c6f760e4) +,S(70e9ec90,7db14601,9741a4d5,ba604187,a7313b7b,500864b5,383b3763,be303979,63f9ef9d,eea1d1bf,a84e0d19,c8f15665,60b4f1dc,839aef5d,b6fbef7e,d6dc6a4a) +,S(23cfe5f9,7198efd3,f34f657d,158f092a,29748586,29983927,9c58944b,146cdf1f,3b8fdd8d,cd82a757,44152552,35de0cae,c39e20e5,f82c7b6a,2afde8f4,1c6afab6) +,S(b1e2b9d6,4c14d667,4de9a6a7,5499cfb,12b30a92,b9bf7578,f8fdbb55,b6d414de,1e91c321,24341a58,e2c6d65f,18f3f735,f9f98d2d,76d124a2,c694db84,37cd0483) +,S(5092db9e,5bde4edd,f385c67f,357e43a7,c8fc8e09,f5c07c14,25bb405b,7e52d1fb,cbad30f3,f96767f9,553c9be3,2adbae2e,1f27f9d5,3fbb2f99,a361056c,ddf52858) +,S(59d39b90,2143ace3,3a1a1e54,a5cd83e9,6cf56f65,4a91f524,af8c8e19,e25496a5,be21c89c,baaffe09,4f2114be,72ff26a8,22db9a1b,818d96a2,bafa797a,3af056a5) +,S(89cc95d8,cf45bab6,9b3f86f7,9e1fa71a,34fa83cd,3d4dfd16,1c9df1b5,817edc16,5eff0aef,63fc853a,f8b65e8,8a45740e,6c78aade,4bcfeca4,c7413ad6,714eef84) +,S(6c2b8809,79398e60,87c756c,443fb032,f2adf5f0,17bf9ac6,44d164b7,d2aeed3f,bc46cb71,c9ed9f46,b34a22cc,e1332cbd,44318165,85407951,580e4b26,270f3eca) +,S(767ca583,c950f7bb,e3aaa6db,b6aa9867,792e0b8a,6930ab97,5c46d09c,30e14a6e,bb90634f,87133104,1c7eeaa0,4a09887a,e5946daf,e88828a,68761cb6,21412d49) +,S(cf742ea0,35c4d9a6,40fd0686,a460ab96,e2015d0,2c15fc8f,333ecb7b,8af4b08d,2bb94f28,bd69b51e,de993065,fd61fbad,c65fc62,90059a8b,d29948da,20b704e5) +,S(84d5d76a,a5a6ec84,52f024ea,b365290d,9c3b6be8,f33a93ea,b0a0ac4e,e6271059,f5565742,f1a47a88,8d0bb802,b002ea6e,f5acea8,864e2f64,fbfb1565,dda51952) +,S(8f5752a,8fbdc1a6,201b6e0b,7bd5db3,cda2eafe,f18cd6d,559724b9,56767d32,d63c0fad,de72b753,6d0c8606,88e0a82b,9f9e35d4,b1db7be7,efa9040b,82298008) +,S(20578def,f94e3597,50ee95bc,61ca76db,f8fc059f,e25e42c5,bf887d49,665d1871,5fc52a36,c95c6edf,46bd5c38,60e151b3,9f123e9b,c58b2079,cd165c1d,e6f33d3c) +,S(30f2b825,739fdf4c,5af0ca08,47f50323,d3ad6eb0,413b052e,525f837c,38f97a4e,d698b076,4417357c,a49b4306,7d73206f,d3d97036,26ab15d4,f11c56bf,4bc820ff) +,S(2350eca0,b4d0c460,12024be,6d458444,b37b20cb,50042830,182b5e41,d564e26b,5909eee2,e3999d60,4cbe0832,58012001,30a344c5,f72ff127,35e66cdd,904623d7) +,S(ea660cfc,45971e57,852d382a,ae8cc6b,1887fe68,dc1b84bf,31f4409,89f89b2d,52297e86,dfec0730,81a280f,9451623f,9e69c320,6f3c205a,f07e56a2,54cd5837) +,S(9cded450,2a7da3bb,77e06ae3,ef398954,7687d21e,df522f3a,cb6b4fd9,d3d49beb,7b13f455,35a49049,277abc34,3cc6f0a,336242e8,7b69815a,e27122e1,d7838a8) +,S(ac8705f2,cf8dcd77,d409a962,f98a6abf,198dd238,3a5eb258,6b0d2def,81daa172,892fa835,a0af6cf9,dd5bf158,44b22cf8,2af258c0,519955dc,ee4966c7,bd5e995c) +,S(fa026b1b,d5e5fe3c,d6b144bc,6b22f7e7,5512ca6c,9525f62,fc7fb4ea,bcd2282c,4ec40de5,7ffc1768,edb20cae,d3a11380,fcc32f45,f410ad9b,8b674d95,6614aad0) +,S(a928eb73,28285c79,5717dc0b,ccb5280f,92f9a4be,5a0b0ed7,67e58eaa,8b9097db,af57cb66,61a9d08f,4d4e9e25,aeeea2dd,e4c88767,c4e177b,37a87ba4,c2bb772e) +,S(bfc9197f,80fded3,c601a0f,1c8c1d77,cbccaab,1c8357ee,4fb67875,cbbc8261,6a721e0,241ef436,69cd386b,d07fa2e8,900527eb,ec3c0025,24bb457e,1bc7aa28) +,S(ae0de043,b332bb4f,91395870,4f3a41f,65e0f3d9,ff77c1d5,ec3add49,6087d5db,34d697f5,dbef1bdb,f581127d,bcd2755,a5a6b32d,73164665,ef84f6bc,145c2afd) +,S(4433db73,29d75b3f,7df0e229,538b7a33,e41183ad,7a7a5e3e,ccf5863d,6bbb692c,3a66630,a03ab85,9f23ae3c,a0a38717,d82ac240,d271b3a6,ccde1407,d0dfdfa1) +,S(bc7380bb,128a6f8a,5cdb971c,31ad12e2,ba593acf,718f5468,285b7f14,69ca2b90,a17af518,9a7b0445,cb37e797,a281c84f,8cf8ecaf,c661e601,4ecc2467,18f1d8e4) +,S(bb53297b,18d2d785,a4756437,70b387d3,ab0fe62,8b78c09f,bb6cc226,79bc92ab,5c34b9b7,cfb3737,bb515ee8,fcff8592,1fe8f30c,e97d21b7,47a4b084,22b0c811) +,S(1accc1c3,f46945d5,7b46b1fa,15291ae4,2f0a616f,df23f1c3,3f0dcf97,53851fb6,bb434ef5,e30fb9fc,15e99b15,b5f9d766,9681be30,d2b0a6f5,a4946268,158d5369) +,S(2fd61a9a,b99c588d,fbe74fde,e8ce95cd,1c0ee9a6,da7ae59,910485bb,9a3ed9c6,2c214840,e2bd764f,74f9db81,43ddd549,3d322dd2,b25790a8,a681c97e,2fda07c3) +,S(4bfd09f1,4cb492a5,7b9ef3ca,d245a0ab,c4bd97ef,12bf7204,8ecfee47,660f7161,57102f3a,ecc42742,657032bd,aa36526c,e67f2589,2775fdb7,e8b029d8,1cc7ec9c) +,S(f60eb13a,8e6485ea,fe653e74,de1c1376,2df7ba0d,4ff0c65f,24bb1c0d,74cea3fe,c5830078,5b74cc82,80128896,f275fca4,9be94edc,744f8535,128ba7a0,5c1d9552) +,S(2b4c7864,9f568eb1,ce1be63d,aaaba4e9,7c31349f,aa353371,6e9123f6,4c57b041,b5dc9c1a,b39325f9,3ead6f35,66c30479,5f817ca1,b3c37258,554c4fb2,51a0ca39) +,S(7fb057e8,5f559f11,b5d41538,6a25a77a,aeb904c5,d1be33cc,bece20ad,57715ac4,2352db61,6fad3f10,a2decdef,5a7fc369,5c8462b3,7d2d5ae8,bde17311,ac6b4ad3) +,S(a49bc870,bfd62cf1,1dd1bd23,9499157d,247ac61a,a9b34210,65d4d029,56f71a0a,b639709,88ffd1ae,ed1b62b1,83ba629e,f5f0c088,f8a9016,d7fa9157,46642e3b) +,S(30b1f21,f8dd3f40,697878a9,fa0b9fbe,d51c1ddd,165bd5f,ed9d1fd6,49ae913f,d5a55164,4628de36,25895fa1,471e0f5,77326d14,132d8ffa,37e0277d,a928532) +,S(8a015768,8aefcd7d,13cb3b98,373a8823,aad11a4e,718a6ea8,5fdb8a9a,6edfd879,e7d92909,4d198910,44c00be,69f267f3,84aca1db,642648d8,34a5b9fe,518e1363) +,S(810ee87e,45d8a5ee,c0e3fd0a,cb771d2a,5a3116be,f4dd7d5f,bfac0dd9,fe072778,92abfc24,9500ba0a,9cfa1725,da4c37b6,cf081f5c,c6cb6a8a,3af889ab,ddd2b7f1) +,S(a0eaaed7,687f6a94,398656fb,3fb15f49,6522c3dc,c19e04b4,598ac4a5,6408d1d9,792a67cd,677ae878,eae040e1,6378cc36,9583b513,bca68fa1,cca3cbdc,37aa61d5) +,S(ca438737,ffa6f30d,9aa695fb,3d29db01,f805f480,ddf1b227,3a0515b9,5f8eba97,56238731,ff5314f4,af6a3835,b30ae83d,feaf0db,dd4449e0,5736efba,8a8cd51b) +,S(b252f5e5,4b67554c,ba3d481d,66a2f223,a633b5e7,f335e357,29928264,e744279c,c6296730,5007e2c9,3fbdb25d,a33a9fa7,411218c2,87b937c5,dfcf8f45,472533a0) +,S(f61b57a2,237befe8,2f1545cb,131acb03,3015a5ee,f0cb77d8,c73add0d,ec0083f7,9b1e0c93,b326a265,eabf096f,f8eedfd5,9f765b51,5007c5cc,d0d3b569,838f381) +,S(30df434c,1051cc43,4ca35671,3957f964,af406830,38ce82a9,5cdf2c7b,55c50f7c,883bc89b,f54c490b,3be0d434,fffb6b,7e829be1,55280401,eaf44bee,7c445424) +,S(fd0ab148,727eb712,adee1fe3,4e3d0fe4,99bf8307,fbe18e4e,d20d5236,f7ac25e7,f5f38f32,df094903,b3219b02,8413cfc0,e56b8ed5,6ad80e5b,aaa00f4e,3877377c) +,S(b4a8fcf2,b406becb,6111c030,ae375f12,79de175,7f2f3b90,13e7ac55,12de747f,d1634039,59a6600a,fa05b1cc,3c459fa4,f1a5fd4c,27ee5382,c5f01e4d,a0259889) +,S(a6c10f0b,33d1518c,8f8b7055,b139aa84,c96e9998,8e8a256c,5883bb44,10687c72,8f0dfa52,ccaec6c6,789941f3,cab90b55,4d4f587f,bb6f3354,b5f7158c,758e4f9b) +,S(7188fb46,e244a5a2,6e38cf13,bf2e3c4a,ff5bdcdf,8d9f78ae,88213692,d866f5c9,8cb07d12,ec743540,3be09b19,4cfb2f1f,c4a85d27,d940cad1,15ab53ea,4b8e0d88) +,S(fe4f9f00,308670fc,48e45208,4063fa94,a6cf7141,d75a6ef7,4f92b474,5bf692ea,5dc45ab4,77f80594,4355698b,e95bc582,efea563a,970217e5,2bd28ede,d7e8cbdd) +,S(93f3b5df,43006183,47c7bc14,3eb046a3,842614ad,78d3cfc9,a6b665e,3327b7cd,ffe5f378,7ac36947,d635d156,7a28fa5d,45c0b5e1,70508cc2,7fd621b4,47b63b53) +,S(dd350a89,e0c3128b,54a3955c,bf802ff7,3a565760,39e1e068,e783ad00,36c13604,45cdb508,3c33c35,fcdbb29a,efddcc63,c4cf3fe7,c08a15d,8b2cd4ae,dec2c3e9) +,S(4f164801,17c1d338,7738b728,96442b9d,66f44eec,e3eeb7ff,ffa87d24,4226e44f,750e6348,490d0ecd,3be07303,5a8e653c,1b1f7c30,d89e27dd,4efedc9d,755b89ee) +,S(b7c62749,aaf79754,de281314,acb4b2f0,271f2a35,fd187307,20349a3c,a4b577a3,f100956e,ddc307db,56542c1,d2c8f176,e0636ba,b04877cf,3dad451d,edcb0df4) +,S(fa5c1278,1dd929a0,2ae4755c,70531cd6,c309261,36ef51ed,7d57c117,c1c8a885,57c5280a,5f08f0c9,393aab6f,eba0ae81,a32f8c6e,2a76c780,a871170a,e37856f4) +,S(e16d6dee,7830b0d,e6cc5e4,9b1cfb7b,50b7d89,57fa4e6c,42ce9925,2e6a29b6,cd5955da,4c1ba840,a3c98d1,7dea8d8c,f4a3d2c0,1e0cf164,2cdc35ee,87fea835) +,S(a2c659f0,a0ef6147,ba84ff26,ca81443c,ac8ba9be,f6fb32f9,50653384,a5db0213,5176a4b1,88b5bbef,3680e875,437e4209,d2fd2462,77c5f84,4a0eaf5,e9d8c129) +,S(7d507179,6a7c037,77fcf84e,62e6eac5,456f88fe,76e71301,76fe458c,8f301a68,14018264,59d3f62,6788fba3,1505c83a,4161674,21dbc2a9,dc14e1a,f45a50a8) +,S(642721c9,20d9b3e6,72db8d82,34ad60f4,bc74684f,11f2ef1a,8171d02e,b9eb1f12,fdde8b98,8aac7393,be2366b5,6e853c7,b72034fb,2cb176ee,770063dc,42b63d48) +,S(65fc35bd,2f604eae,48a1b68e,1d66cac3,627b1498,247d8376,47082702,293c97d,d01018e4,85dba402,17ac631a,9b88d240,de32ac74,79c7d47a,9f5d9188,f8ac01d7) +,S(65c8b230,2e51129a,b81de5f5,22217f06,f5d5360f,af191c56,8f395a33,2b04375a,3201b7ec,117fceb2,b225f137,877ef481,2be4d6b4,52d1c855,f73adb0f,e99b2943) +,S(b82aa6a2,86fb19a,d2eeaac8,9019782b,d0d6009a,8fa07a02,a0d6295,ea8637f0,426f8d72,ec1b08d2,72671566,1f77f470,f334e4fd,3370d96f,b0acca8f,2df99398) +,S(a3222e7a,c6c27f2,8b24b33d,ac78d873,1ce060dc,be055100,862e71dc,e6671a18,6f2c2a1c,4b80534f,8ea53777,71760fb6,5148d47e,ca097fd2,b7bb2f53,bb07ce22) +,S(281ffed7,48a4022b,c663d08b,5681733d,f1343818,d68c8b63,9f4405db,ad7020e2,b5f18609,d8e5c6e7,990808ec,b4fa7d2d,7d4cd106,b7ad2c6a,e356f239,9a535eea) +,S(c520ea8b,a481c220,bab0f218,72ccfcd5,d7014d8c,fe55d1ef,743af305,c0277d5e,e23eaf34,33e4369f,30d371d6,e396b0dc,a065506a,c4159967,fec4475c,8f6bf78b) +,S(54d39ed1,114ec02d,8005d7a0,82178175,78f827c2,1b55a95f,330a7af9,34727958,4aecf190,229c49c9,31862a57,9131aeed,5518cda9,4695e8ac,7ebf7667,d9afb23e) +,S(756b9bf0,1da8d7f6,1627cb2c,b3ce3ba,a9b6450,c2d24596,feae8bd1,2ef67a23,51c47b15,b2ac96c3,5d7b7dc2,d8c57ee9,69b347ff,dadb04e9,cbfd6889,a0ef70b7) +,S(eef22145,6ff200f,ddced960,f422588e,333a437e,d68c04ca,9758d77e,a938ac54,21587cf5,8b8ffb72,6d29aed2,3c984a4f,25df40b0,abc9b7cf,2c07436,ab7e09e2) +,S(b841b560,c0af44a,35bc9320,3c05cb32,250fcca3,f83dafe,30d1dbe6,61aa3d1,e7956525,3d07a89b,2335e2ce,9e11fb89,96e2c146,271189b3,461cf944,2ceee2d7) +,S(14b98f62,5849b798,48f32547,efccc33c,3ead6cd1,a290f03e,618acc2c,1ce065e1,4e7a2415,805a4486,476221f1,ce8234e6,5b81ee60,e1ce5eb6,b6a2e041,ac885e00) +,S(fb0aafb7,68c19f64,17e417b6,f1c22dfc,737bc74,fe8b23e7,4dc1c1c9,aad7ae44,88b83e23,631c58d,c3729cf5,54343721,e9ec7dc4,a889b85e,31cb302f,f2a6ab3b) +,S(885497ab,e34d1745,a3e2ff8b,bdea904,eeacf100,324540c6,86de137a,9756468f,63f3f160,bd266c3a,7c49b13f,c5b06403,d0614b79,17c49824,bf507257,2ac0b948) +,S(52dc7bd7,17e4d8e,8b634439,f6f06b8f,7121a0e0,cfebe7a8,3031668b,9832b8b3,90679373,9bbce1e3,5c48f41e,942d9f15,9161e9ab,273a2217,d9e857c4,3bdd0a5c) +,S(e6ed2c69,a8bd7af3,35591f4e,4dfba7ac,d82e87ee,e2b3bd28,f0f87f02,3abaa7c0,dfec6118,7396bd5,e32ba2e7,a8fc912f,1e54e078,6f2ee222,35adcd22,8feca28) +,S(9e2171a2,c1025129,1757e26f,ba979e5d,6b68c2b4,975c7289,2afe7898,cafc46eb,3dd062bd,210bfc4d,d30cf01c,51d810b5,cf1b8ee3,d3baf490,6bf5d381,b87c9a56) +,S(ef4b8648,3bf209f3,b1c2e166,a32cda39,44b21282,db8acb94,6ef62122,63bc44cb,903d1088,ccc0ba47,b5ac3252,33ef9b55,924e61ba,7ca80bd8,1925b269,3998920b) +,S(64ec6d14,7ecb6e44,6331907b,b9006b2b,fa4fb854,20ab9aba,f1026bc3,4ccdc4b5,583cad3,b41bbfec,d0817207,dd312cdc,26c0411,b888ce5c,c6ee025,baafac63) +,S(128cb9de,125429dd,e5b9b33c,ef9f34a3,e7b38ba,f22dbcdf,8e169894,3f7ae5a9,a10a01dd,33f5a4eb,66091348,c4dbd90d,62879ff6,7559229a,e53c2896,8fb4584b) +,S(a7d2e7db,ca0890ee,e07c4bbb,399a0120,e71fc284,c5b7c96b,4889fbb6,db95dbbe,b8caef70,404a19dc,15670cae,b19d5b74,e70b7492,50390b12,5f559b11,feda83de) +,S(298b17c0,b8beadff,5e6eafb8,ad7d756,8e961468,d8e12c94,78ff3870,dc722450,a0542f35,c7b95774,b1c10f75,3e9da029,ec7a54a6,97bcd9fb,46c98d26,91ac1a43) +,S(31a39bcd,a3d10e9f,6b46e1e,c30b41aa,557ba664,9b12db64,a13557d7,5c592d1d,814f1a6,15382d4,e57b8d41,351fd782,ce7dd9d7,86aa9bbb,c5b1ca7c,207ab12a) +,S(3317f1a8,1afa54f6,ffa6df13,bd3f818d,e9a29bed,7c119b6d,fa31560d,31d81cfd,aa8eb746,4e9acaea,19a42509,a23a281c,e9917788,6d3de977,4398c57,8867e095) +,S(85511857,80eab56a,274cad22,966ca547,3027d3c,3cdd7e25,7d8ed8cd,a5c6ef30,b13de0f4,45d71bd7,db0327eb,15438e9e,84fefc1a,464d8a2b,bcb8c6b9,f59f93dd) +,S(fdbffe5,7e378262,c32bc4b1,e84ae01e,a2d6dd76,28e10b04,9adf6a3e,ef106e83,f19d0c4f,1411ba50,35d3ef23,153f306f,d40b22c6,69e92ea9,22bd7d5f,83a98ad) +,S(7562779e,6aae0c51,52acc7ee,43bed76c,831ba008,82d89c9c,e29a5f3b,8855b3c8,c83c1662,7cb9e1d9,126e07b3,354b82a8,a0cd49eb,3936c14a,8d8d64ed,2a4e217e) +,S(130b292c,10d5b336,4635d43a,fb6d6e02,d5acec2,c2033525,ed72c030,e82a4a08,d534b710,e1201276,6f6fce50,ed6efec7,3e0c3e75,50e0bf36,d5d625b5,4855fed5) +,S(19fecb77,bcfc0db3,e595fe22,e96f1212,b3ed032d,183be5e0,ed4c1b8b,d0501bb3,6b0fafde,2f0636e7,2b4ea946,a0e5c27e,4e5dfb5f,e8b8a1d1,b36bdee5,75d02e81) +,S(ffc668c7,9c6f9fd2,f5c56104,aac1286f,cd4a390d,ca586af2,c348458f,b4130f77,fd7b748,d15f10b6,4eaf4946,34fb4ddc,439919a1,1155e40f,24f3eb2f,482445f4) +,S(b9797c7b,d4112aa4,8abbc827,fae12fd9,6d19f3c7,81626e21,5c26ddcd,b2d4552,ddb0aa81,3113288f,1fd2505b,d23f2a9b,a1ba0a38,dcbbfbcb,46da78bc,5c705327) +,S(9388c9f6,5bda9c81,62ec04ec,5345ff80,da4afe34,7281f768,4d8daa1c,cc3d7869,23ef5140,3f926d0a,d2ebbe6e,48858850,aa98149c,89490e99,65dafeb3,c82644fb) +,S(44cf864,6c2d2f30,e62f63fd,8e145786,80c1d5b8,a27fd496,a8e34bab,5f88270,9784e329,a434b442,fef9af66,b23ea917,edc0f6ca,8d28a992,bf6944ff,26c5cd25) +,S(ebe08589,930001bd,ecb83ca4,45c4a9fb,fd61b229,60ba9848,2e0fd520,1af5a0a9,ce2d31fe,3c271048,24ed891a,910e2cd2,2b99b39b,480ad812,a5d6860f,71ec817d) +,S(176d14f2,5491c2f1,c1d4fc87,a8f59aef,f560bbd9,41872f6f,2a1a6adc,7620f1d7,42c1e0f3,8a3379b0,c5de8a01,11b846b4,35db7783,caa9b84,8084a985,f0e123a4) +,S(74e96d3a,972baa6f,8ad6876c,9494f451,a12adecb,681c4d55,57eb58a6,de920fc0,c2cd92a9,994ad5,f4700561,e8b57201,96e61a5e,b4cb4a55,66959bf1,35a07f83) +,S(6751dff8,5079fa45,7f5df468,624c4a85,db970586,889cd80f,d4514bbe,8de42d60,7cc20505,deeb3f91,3d78a265,cb301c68,49cd5a53,dc029af5,c0db1876,762ba3b0) +,S(f9c63056,24d8cffd,abce64ac,65dd3a09,ebd344a5,5404d08f,b4140a4d,8489628d,49ad5449,bf1b6def,74283afd,2f315f77,3de82cda,d4c9de21,7995a2ff,c44e934e) +,S(69e8c4f7,3f757235,d0e4f39a,f2595cde,f7ff4d20,dd2d45c9,78f32d19,921b6672,8ffc35fd,8a6d541b,3f7135f5,643efb00,14f275ba,d929671,df610748,6f763f55) +,S(3d03e571,ed2aa88a,3309f014,caddfd64,26ae84a2,a526fe35,f30ceb8a,4e645e22,db95c744,6bf115d9,4af9074a,6b274725,adbcbfa6,77f9ce48,535003ad,dd37bc6a) +,S(e5b764c3,dc73619e,51a2afa,fb2292ad,db2b7b0d,e12b46b7,f14b66ce,f8aea7bc,41fbe1bf,41838b6e,9a57fcbc,b3e2e3c8,60170122,29a5ffe1,7394139f,5e8edf2c) +,S(adabcca6,b4252849,76596462,36b17b0f,9d1b4e9d,279af84a,c02f556c,e0f4c385,7d12959,9b411665,dd23b676,57229f84,ae46773a,e13284ab,1cb608d2,ccedafd5) +,S(963094f7,7ea2a7d0,3115e201,45dc6f6a,7318f8b9,a55ab015,8096db54,48e341e7,36bb26ce,3852ba30,dcb1a8ee,ec834f4d,d5342ab9,f7bbe85d,7addce07,62cb22aa) +,S(30ca6324,b4f34e1a,4c3dbe4f,d6dc6b13,a8ebcb35,2f632fc1,5725bff5,f260c508,5c04aa15,8dae7205,3bb608db,1b9245cf,fc57c10f,d015e80f,7afc7532,632eaaea) +,S(8356d1c4,55479df3,adbca347,fdb73f89,6027471b,e528da9a,d9e6a968,8dfba744,c325c86c,cd603953,8f610b03,21b6147a,371314cd,2c41df91,745e579d,8564a177) +,S(996ca7b0,4ae2dde7,b5ee87d8,c077bf97,9a63ce83,cfb84349,c7d231b3,1e15bfd7,cd1d1b48,d75f9736,68b91101,8ade913c,19a1d785,65adcf71,6c805863,fa6c1f17) +,S(8a15dbd3,5cc16f77,b22a87b4,4f93e5e5,810f0503,56e47d53,95769128,a2047738,cf96df1,9ecb6da4,3895641e,4fbd4f27,f00f58ce,cd34d38d,e258fa78,410a1f7f) +,S(23c1e11c,be9f6a93,6c5f8abb,6cc501a1,ca38b03e,f00b31a,79057156,f382ad7c,1d996782,9f709951,2c9728ac,68c16db,1889bcbb,7df432b7,f430ebe6,56b1e050) +,S(d937ad87,70737b1f,d52bfb7f,86e207f1,9e4bed5a,43b165fc,981685e6,24b8e84c,202913d6,1eb467fc,b488fffe,5914f853,73aaac20,1acde09,ef975cc8,a05fd181) +,S(6457f59b,2c744ca7,89537c59,6485abe9,7835368b,47de3667,4740ecff,72e5596d,30b6ffac,65e34e88,43681e64,f041870b,c85b3b5c,86c032b1,e290848d,ad2f7a5f) +,S(7101d56d,813abe1f,c50aa99a,bc7b2363,9532d79d,facc17c9,f2a5b4ff,50edf82e,62a0b9d2,bcab5b18,9075f6f0,e648a4f6,fa66741c,389a4196,a4c5ead7,cbef14d8) +,S(2dd4cb3e,5cb59cbe,b961af3f,d1154fbf,c21916f1,d5331ce4,c8a32ad6,8f7ad818,397dde68,7b1f6b5d,38e457ae,2f5fb332,74917949,801151e5,3d4fe19,9a34289a) +,S(509c6fcc,ae22b09d,b8f3d191,434e584c,9d17dc76,c268560f,2290d81d,53ed66dc,cbf69e20,c3f9ada3,41e32dfc,581f0ba,f015413d,735d33a6,260e0cfa,7a4e4d05) +,S(71babe5a,9458bf5e,112b23a5,3f87007b,66ddd721,32a45a34,2a9c521a,c2029aae,55ac872,a0275be6,4f569901,d84dca32,ea13e2c6,cbe608ac,de228185,34e031a3) +,S(f5f2144a,46fe975f,82b91da3,a98ebcb,6d5769a3,4e5fc50f,3ca640fa,152fa7fd,2fc05c22,2b20b895,5503f23e,627558b0,e023988d,f02993f8,f495750d,db6344fe) +,S(d6f274fa,fdfcf64,25972a56,640e53b8,15c4d213,8e4a72a0,b8ab3394,2955e416,738e267b,2fe0d9,5774afdc,7c4f559b,7bfe2824,ea6b7a49,d3220eb0,460ca733) +,S(81a12fcf,cc6acf7b,89f30fc,8f61e2cd,7882f664,55923993,2b8d3f9b,1d6e11e7,a8dbc6c1,cdca67b0,64b285c4,e2f69242,cf55f06c,5eb28868,d9bab656,eaf9116a) +,S(57019cc4,941f6f5,9ed34305,92f35a83,39a495ee,fb3e311a,fcef9001,cb8123be,a3d632f2,54083f5d,6f2857b6,9556205f,59578908,9d8d8fd2,4d10bf1a,e6d2e635) +,S(4205a71f,504362cd,576c4d84,c8b65252,3d4227fb,d6aa28a7,e7fc3d70,d1387c80,781bfe55,7fecdfff,c54d1c1f,e147792c,5ea7a1dd,7224954e,e110cc6e,18ab452b) +,S(1c42ad3d,f90472e9,e5c041da,52476ea0,896a2032,ccff22a8,fd7bbab1,4c3961f3,a5072a70,be702dae,f9f93148,4bdfaa00,1e2f2ad6,7d79f46b,5717147,17c5499c) +,S(6c9225cb,668ecf25,1f7862ad,999516c0,145faa37,b4fe92eb,561104f8,f1dc2997,8c12c1a1,e372baa1,4e9b1443,d3530548,d7d1d05b,fa354c7,f47ecab3,23993a31) +,S(72a0fd2d,eced2a05,6885dc81,fb61e881,b09104f3,8650a872,44e40471,ed189992,d9c67490,43bb493d,9aea4a1b,3a50da8a,e1c48523,9a480442,32406d2a,68487e6e) +,S(b1d531af,8faa24b5,69945ec8,1448915b,a46fd13a,f7fecb8e,16cd5d03,52c6c2ab,b3ba6cf4,bd36bee,66deba09,35bb85e0,b9ff7e62,5b5c793b,543a091b,dc83292b) +,S(da79daea,d6ea65e1,4e8747b8,b4546560,a9e5a99e,eb572f17,f2ebcf3f,9a307a86,f5b3c430,e0f2d4aa,2f278088,2d683c48,d84f926f,6feea88b,10e06930,6567a290) +,S(1fe7fb5c,99a69a75,a2276ca8,92f67da4,951bbb4f,ee4df8b4,a54c9833,d0d33869,e2b2b4c6,fe0f63f,d97fa179,331e9bb,55786006,f0425e98,71452aa8,361f177a) +,S(fcdb7de9,4eacf36a,9d662371,edd2b535,af33de37,1985ff49,6ce5293b,9d2ac00a,e332723c,7a9708e8,5172e56a,c4291760,baa17e83,72c98dbe,83a186de,77c2aa06) +,S(daeaa6cb,19bdb4b7,78077495,a9cead03,3be16d2,97e742c6,97ac18ed,e5b514c6,4545ca53,bde3c1ff,6bf32604,ef037da,93134215,7057ab10,cf6af451,e7753f4c) +,S(83e4b4c9,1a923321,196fffb9,b45d089e,7fa1f6b0,5ff45a6f,d099f7ec,fe283088,926c28cc,bf405fb1,17c5f168,20216226,4c89cda2,cf669937,2739ea05,279325d5) +,S(cd7f2b53,8363914b,2e83f1e1,c9bced9,32a682fa,cd72b46a,eda578c0,640089bc,ddf6dc27,a5281d3,b4d66af6,8fc80f5b,16cbb7b6,ebfff06c,6902d38d,4632ed2e) +,S(6f3f5a27,b19dbfc0,238bccc8,a9adcf95,adef6480,fd4ca405,454509b2,97a40928,36a56db2,abf62faa,b2255d86,d8a46aec,361fd2a8,65740c31,babb2e2,c551a85e) +,S(f7aa5c92,a523b86b,c27cf714,51f7dcec,fa18ef3a,9e839c41,a8b20c4e,30ba3b77,fa7d3ce9,8009f23d,f5eae927,acf1b28d,5ce4a2f8,8a92da7d,fcc1acb1,3c8cadc5) +,S(4adaaf95,5bfa6a94,b518f7f5,b0ebbdca,493798f6,72293e4b,36d5fdfd,5f641b89,ce675ffa,cff579c0,61e63c2f,36e77f3d,5c2571b3,aa99d3c2,74086b22,a862577c) +,S(2d737aac,3600371c,f6b638e8,8a29884c,978b783,cb843922,bd449e95,814db692,205d944f,13b21441,76d8276d,74da68e5,142aac9f,ba632568,79753c01,e54cf4f) +,S(7cb5d365,10ef241d,ac378fc,ebad8738,e8f2e6ad,6d838731,e5c82bb9,169d51d4,33b088df,5a572e8e,1010886a,d01b68f1,9f345770,81a465ba,8c5c784f,225f6510) +,S(9872a376,e530e254,8b623b6,b9dbd6d4,d123f2f7,30579f47,4d910831,7c714c0c,3de01ddb,3145d81e,6ac002ec,70086f74,6eb2bdee,6295f5fd,9d2cf8b6,1b222e58) +,S(8242fc8b,2e848618,ba0f26c2,6acac090,8eb21b4f,41a08b76,beb35f85,3b8c2f33,58b94d44,3379ea4f,829fc809,ce2632cd,78d8675e,45a29c58,e29fda43,785f2e9) +,S(88800a95,cf1682df,4e5bcde3,a96de21c,3353751c,5300dfa0,d0f802d6,7b60eeeb,2a35b86a,4e213e9e,c53dfe04,d91afb48,25bc8794,a1574ac4,53e81ff,4b6e453f) +,S(3c8e2755,9611937c,b7e2b2cf,8a87fa5a,e5c696df,fc6703b7,e1e65ac9,111ad87c,c54d129a,be0c774d,dd415707,bfabafdb,daedd1bb,2ea9cff3,bcfae0e4,55c916e) +,S(281af73e,a1f092c5,535557f9,3b5a8c94,54165f8,76650801,856bf926,19f06cb8,deaa8495,30951f12,ea157714,3caab5fd,d30f0dce,ac9b80bc,fcdb3477,dc01ea1c) +,S(6b353e5,3202f63c,69bce96,30535f11,1939fa,849dedb,92ad21c4,89f5174d,bd961eed,c16f285f,2eccfa69,d37a6d63,be5fad00,4858c80e,6fc4dfb4,2717acc4) +,S(68c184f1,a06685de,fd20f36c,82b5446,7446d5d9,8fc9c312,4852bb02,f35ed91d,486a3d16,7d94c66a,31f9e134,1652f658,76d49379,f3e3429f,e9f2da30,84f56532) +,S(5e884531,fa011576,3d36e1ad,74b22e23,f765db14,8b8e965c,80f7c9bb,7d561bf0,34c4f03,b137b627,3fbdb6e4,8ac09ef9,bc3c8ccc,e720853c,61ae58da,ee3a0cca) +,S(10c13f0f,313e5ad8,ea61ba5f,ed509aef,5ddd3016,c3339eec,ffc8f9d8,6f131118,28f6ad5b,324e44ae,64ee65c1,b86710a2,91a8e71,c33adbcf,a84f18fb,144dd67c) +,S(aa99d8b3,1031d4e9,8c078a68,7254968a,9141c710,cd8e4842,5a384bf8,414f5c5f,ee6eccfc,1dbdd5a,c9080cc1,ffedbc6e,64fcf847,ab9c0ce,fa91c4fe,d1de44bb) +,S(f087945d,a464e66c,4c73b58c,5034f057,b96ebe34,f5128397,cfadd7be,22ac6100,c5485411,d78fb395,3f229109,45aac354,1a2b2acd,1d95c38a,f51f36dc,d3cd92b2) +,S(2e4d8752,ac21c22f,e4d465e,3d2f5ad8,d9168080,b2b44930,e5de75a0,d9bf91ad,92f394c5,963c0817,500d5a98,2eccdeac,aa55043e,27a9a035,1a6ce245,a8d048bc) +,S(68b59755,44feb608,15a6ae70,9c2647b1,5c7fe073,aa6af8e6,202b9782,83daffac,b3745e92,fd86bfef,ab9d3d26,cdaf57c1,2a329889,cf646b6f,d6cf869a,f727e0c) +,S(15f5f668,e9576897,a287f131,bcbe1216,84f7a108,3c28c09a,afa77074,63dba49b,133e33fe,9bf06bb0,3b814938,7a099da4,77a3e758,82fb19a4,463ab360,bfbc368f) +,S(f2bf3cc8,ea752c11,db2ff594,d3f9b085,1dc9dff5,7999aa39,b5af2a29,30d50491,6e01a801,c5b7e4a0,a16af1ff,b011bc12,cb0483dc,6127fe52,c992b07a,9fd77635) +,S(1b04817f,46dae06c,120f8088,b3cd09e,267d4ed2,db14881b,bc32b5d6,345dc54e,31634e28,56f61b80,56a414e3,94a34ced,200bb172,e9a9aa07,6a224329,61f55ecb) +,S(693f066a,bca2046,ffb860f6,5e769ca2,3d1f46a3,6c09273d,afe5eb89,d2da45c5,719dd749,5c1b1938,a92af84d,eef309e6,ff1978b7,9adadb96,c58caf2b,625fff9) +,S(3fb25a02,bb232d18,c798f688,b16193ae,cb2eb9f5,e124c999,225f0720,63417008,90db4fbe,fdbf8a26,ff9b4afd,355e3771,c7c95891,68e2abea,264aba44,ad74707c) +,S(4c878f4e,6ebfed8e,e12a0a6d,17298b70,79d9de5c,6a7b5359,5ddefe4c,db82a79c,837592e6,927938cf,dc82252b,2cbfd9f6,cbff1950,405b4511,f2c21fdf,a3f72715) +,S(5ccaf6f4,2b75e152,49263cb9,5fdb34a8,770e8828,98a1a0cf,5407c48b,b9ad4ba0,70851419,379e5e2f,dca9613a,a9f92fd3,8ad8904e,e0ce0048,b2510943,ba8ee24) +,S(455907ae,713d1ec9,3792d488,7d9857bf,a02bc2ce,abd2c599,cf576f09,f7d09d11,1fc9ee5b,90a039f8,eb3200e2,ee58022,3f0cc699,60221d67,86f0deba,f13ca6b9) +,S(6f7acd2b,3683482f,c2c2d55b,eb62a89a,37578a34,417bcabf,ba7c56b7,bec14f0b,6049db59,b6210e55,b06401d6,17247d91,64e84e2c,b8de91a4,d4998c15,6f46d621) +,S(100f00d4,eee4ccf5,ea2558bf,92bec83b,223fa214,ad692ae3,b0a2ac93,c546d558,fe055fdc,1ae02552,1557db5b,96a62c92,556f918c,ca908708,46ffa1bc,8192b65e) +,S(8650c359,664c9f15,935dd776,bc6fbcc8,d9cb02d4,c22428e2,aee3e3cc,6e9d0c6f,89dba800,919866cc,8c16895f,785fc9cf,2661f78a,9e065472,652b71ce,d5ec8732) +,S(22b6df12,b3932756,713e2ee8,2d296c64,5d10364e,cca1ff94,9b477fa4,9a2ce5ac,ac58a22f,42769cf1,e2e23308,b3b5f139,4d329323,4bbd6053,4817ab5c,624e995b) +,S(ce87a23a,1554ae1b,9579ae96,863128cb,926a0cce,e51f17db,75155607,754b5992,4bb953b8,9d590216,e326f23e,66768c0d,17626262,6fecf80f,97435b50,d5dbd503) +,S(e3ae6365,29e3fe6b,cd1c89da,d72b17d4,8bf05c1b,c487ad41,d3b97838,a2cbc19c,4634e222,316b6ee6,c7616cfd,cddaee07,7fb2cd0d,26dd9f81,9d0ba43,313b3f4a) +,S(2825cc59,3a0678fb,e0d37d42,df72037b,6be96e98,18c52c49,fd426d75,324bd620,ea0079c9,2ef91a7c,dc587343,7b454763,f2fd4adf,561cd023,8379c8cb,1d281f6e) +,S(a6b2a170,fb23ae2f,943408ef,60fcb145,106735c,8876238,5d0fdd9b,75a2c46,1ab40091,e000012,60837529,58e3134,5f260890,d1bf6930,ddccedb1,dce20255) +,S(99a36f38,759f0a70,d719e3d5,b741a40f,daef1ad2,bcc65293,3992f134,39ff339f,a36d4b07,a7258053,61430332,6be79a,61ce8eb6,97944c6d,8bbf433b,a565bb66) +,S(e511e0a,de5a9be1,1b8513e,a5706258,6de09e2b,e2df739f,d647fd33,7890d36e,c6cc03ed,e9e50830,bd5579c5,9d6db693,dfa5c057,b1c214da,e4064804,9cd7ed70) +,S(bc9de66d,a406288c,a8a94e96,5ca16dfd,379608c2,63222a24,e3fc1116,20a82125,ac471a7e,579cfeaa,fdfdb5ff,9cccd4e,dca4202b,1b0149ab,ba9f6c09,aa219626) +,S(e98cd505,bd593bc1,496b1203,436f6038,ebe3fa4f,361ce4a6,54315402,a74c3a4c,1350c197,1c7979ca,7464c4d2,39b24a55,a1e50812,aaf92479,d566fffc,9135d977) +,S(47c0c788,2490ca53,37cc668c,5a549bde,dac661cc,f77534ed,d11f7ddc,9e0f37ac,ece10949,be122263,26819032,a2d952c3,60b27e2,7e527673,e8fcef08,a8f27685) +,S(e8224d65,35ea98a5,e3fd8833,7bd167cc,776f89ee,7d0ccfdc,46a15fed,93bee5e3,44b260e0,e0b1e1e9,383041cb,1dd985e7,22edb214,ae58f784,f174ba84,ef51c59e) +,S(c195ee35,5880dd77,67e01659,ecaf07b4,571e324b,b8a1a4fb,75265af2,3ea74709,95c39cef,f781f53f,31bde05b,d0289313,4e478b2d,1c55e741,aef8035a,2ecd4226) +,S(60d989b9,c9cccc66,139bbfed,e2902a95,fb9ca9a8,6c2e863f,21eceb4a,7d73c4bb,8295928a,a48ec1fb,775ea29d,9da1ceab,47e82f3f,1e6ef7fb,c7505e26,92bb51d8) +,S(a33b33f6,9db29609,8cfd34,caefd3e8,c3c0d863,ab695b06,b47d102e,f827fe62,669d9d96,842ee809,2cc31cfe,baab0f1f,4007c3e9,e7e41be3,7a08d221,1c6bc905) +,S(bd25afea,d9b8c833,fe332a2a,a9f3b627,d8328152,8fe1422c,5da90163,4b971584,35bb66e7,35326f67,63f4f38a,c18f8412,26da97da,c9e66902,f28904eb,b49cd78) +,S(4d7e208a,4264b565,d8563cd7,80773012,581e8688,a62921f2,d6a334c3,25625279,6729dd4f,b45ecd25,2c5cb033,dcb2f520,dc928ca5,71026fdd,b42943e8,b5da9988) +,S(5dd46f97,fc740db3,ec56348d,ed73fbbb,c440e3f3,33c64a3,a37b7a73,7f0e987d,7e2d9ee6,9e1c07a4,b764cac,aef3d780,e2d43288,2863f76e,35ff4cc6,832ede1a) +,S(4631992d,d72c02d2,2073222a,d35917f9,2770198b,ad13a847,8d3c9ca0,6ac91a3f,e28ce87b,35a69d5,c43b685a,e59d5f9d,ccf187bb,62517a92,90992b39,86d9e1eb) +,S(9153ad32,32177422,e03b6739,d5654209,7fe04d22,28b1e873,f02e990f,8936d1e3,8eb9f3c6,b54726b0,b4e867e0,5ab2fbca,e76d2e20,ce07983d,6edbd5fe,ebd0cc8d) +,S(939a7349,28fd798b,5036e2e9,60360387,b9c080a8,982d4530,78415667,848c2eff,30d2a2d9,5890d023,7d117cde,2eb3c532,e419770a,8783a7dc,dfe40674,709776f1) +,S(8e67bb29,9f11e01,b50be7fc,bee1f73a,5b335f5e,5ece8619,9a514156,dc243f3f,7fd0cb92,d6f9cd8f,bee9d337,90a4492,b8a7fa02,a0914c48,1f8becf5,d9a9d7da) +,S(a2cab3c2,537d8fec,b2f891a8,1da2f523,de4f869d,3c812806,855013c,73485ab2,765d058,c681af53,36ccbc8e,88d2391,b4894b18,15441342,4c4e73ac,1a8e0d71) +,S(9b39be40,976c13c2,faad320b,9338f985,b0718123,d2fb543,b65473a5,3bc2fce0,10f8234d,455fa7df,d09d76ee,41035515,9b560db,7b92988e,a42f9e9c,f7baabeb) +,S(35011530,6f2975be,73e7685,6456c94d,700e9b1c,4685639e,4f60efd6,9e513692,5992efac,9dcc0a58,ac6e00e3,54e44582,7e2d66c7,248e4b86,f8c216fe,a9e44270) +,S(df5a5b75,d96ddb93,9a341f58,7e1f997,3cb9e799,b04ccdfb,9907b57a,7b005d1f,3fd070b9,8d759043,8a53c302,b7ac0dbf,594094ad,7101d500,1cd5d687,e80066f0) +,S(91f14eda,a0db8441,10fa531e,eac54762,d52ca420,bb872327,6b383587,d40fa761,86cbd213,358d1f70,c3f6ac7e,913daf0a,47a80f8c,bd72445d,fcbef37,d0d71e69) +,S(53ddba06,46853311,9fe95719,15a9efe0,bc7fcf80,59d638e0,be6c1aa4,c5f21ffb,e59de2f6,5e583ab,5ac86c77,fc298301,70a18b68,a1979074,c21c0d8e,4a562567) +,S(56599462,4ad07c16,607afcd,76e86218,b15b50b6,9df84695,e60a15f0,3d7bcc49,c6d36636,ef58556f,9b688db0,eecb6bd4,5a7b30e,a9748732,68be57d8,542c966a) +,S(76fd5413,fdee9cb9,7f92fefa,5efa27e9,9aab744,e1e15029,241e35bc,743307a3,bb23f425,9aeb64e3,8ae82c3,b4691bf9,3b4a719e,a8ce076b,d0345a2f,d02a6d05) +,S(511ff159,2127502d,7499b988,7a3b3bd6,1242518,58bb62e3,dc1638be,89204a0e,21da5ba4,34b7819e,53b51e46,29f5fb77,e739ae5,97af55fa,aa09500a,dbd502f6) +,S(8e948cec,51635ab9,5a057404,39ff17c5,ae2e05e4,523bf2a2,3df28fd9,86de6b15,e16aa2ac,6c3d34b,9535b795,29a82b67,34bfa260,4a74ebe8,86110d73,ba564375) +,S(ebda8670,9277fecf,9b8507d9,40da6c74,210c8c7e,ddcff0d6,e3b52ded,3bf57095,3ca95e8e,112c8137,fe8c5088,bf041674,fb657260,70625890,1fdc5626,7655d654) +,S(eb5ffc4f,559f7ad,2469f48,1c730925,1ebf3863,357dbe72,41a29205,5b667682,b1f72d74,a5a16ef1,d55804ce,76974f46,61a49210,8a1a36f8,a59484da,a9bcbb96) +,S(2a93ae8a,d15c82ed,19fd2161,4a7a65a4,cf562432,623bf7f8,6c765548,b02fb57,5a949ca6,60a1430,9d4baa6,9be69b34,848e9874,62471d74,54c4817b,336584e0) +,S(9ac4a3e9,f1c8d115,138bd299,b6fb9132,c5d64a92,1fd94adc,82eb4ae,fa64704e,47f0eecf,a9eea827,ff339f3,48e67e8f,da9f00e1,a6b9086b,6f5441c3,7ae1d335) +,S(add254af,39d99d6d,355510d7,4f8985ad,3d8ec1e0,1e8d0210,27e80049,f555d65f,5b147a1,fc4bdeae,6c2f1224,b35e9e61,ad0799db,53597ef2,a23a8f3a,344dc526) +,S(d374d326,52cd006e,3b27b8d0,3ab47d44,bfb1c5d4,48adc992,5f63235b,c34aa128,3cc6ee09,c04d4cb0,aae8b1e6,6018640e,d0964fb7,148181ab,e9fd0374,642ad9a3) +,S(fa7632b4,78e5389d,cbf380d,8efbf6b1,d4bef9a1,70aa3517,e4c2005a,71a283f,ef2bcbb0,4b20be5,d4bda0f4,26dd0baa,31037d2b,7b530e2c,24105c3d,c817ce89) +,S(68ea197e,76af6950,1633ff9e,c825a817,80c7405d,ba513f96,6106632f,ea73ede5,633a5f90,587d4ea1,72129820,2ccefb30,c14a052a,6c0ca723,65e2f91d,53b3ff4) +,S(8874397e,1813f0c7,6946a84f,b05a0f44,224bb110,8aee722e,736e807b,b045b73a,ced2e2ec,85ae00e0,2952f55d,3626b613,641d2a8e,84b4c9ae,36177bb6,9ef80d9e) +,S(2ffe42bd,89256069,2e460701,b94208d4,85915043,8a85f71f,32246b8,5dea7594,6e7e4050,a35f1570,f8cc40c0,862e9cb5,59e744bd,8788646e,b0dad904,8b2777d8) +,S(c6d3a6f8,c997cfe,779a9a30,79de4bae,c19c226a,21f166d2,71ab4da2,cbc6defe,a8f8a618,3f6c8b2b,1486e60c,337725c9,9a9adc07,7dbb6581,e5b0119e,ca10f418) +,S(9b51cd75,442203ca,c4b8aba6,e9829b73,abe29f96,92b8dee8,500e1a90,9ea9767f,ed0ee32d,6042b6dc,dc818dbb,8cb00619,6bfeb2d7,88f35626,789cabb6,5d159c51) +,S(952c518f,775c8bc4,dbd7629c,8dcf8807,1b208033,8aee2ffa,eabad5d0,be6eaa1d,8a3b10a3,d9343d39,e7bef8c1,193f5cbd,717efa3b,cd2491f0,2dc1aa17,2e5b629b) +,S(49a18372,63eb11af,77a2e33d,2743393a,7eb269aa,f64589fc,8f81507e,55e8ee4d,7feb4a59,64b781b9,640d60f9,4e7eb291,6025d190,95945aab,7ac7771b,6f96cff8) +,S(cf974d8f,46aceb5e,a8c04e55,703b5a43,ac577e84,cb174abd,88198e1c,12708c9d,c50cf6d2,afd79d3,ce275e91,26bdaf0c,f46c1eb,536d0753,9dcf6c2d,d79afc35) +,S(461a831e,8beb6da0,df92dcdf,2697d551,cbf8f903,138d13f2,ec9b7fa,4a7d4763,e5fd85fa,c251705c,27d4e870,64af33b2,ff99b19,14496d96,6fa18916,faf40e01) +,S(eef4eb8a,9eba3f2e,b27e3aca,6cce7335,49f2656a,c26f1921,60c05bb8,fc3f1165,58f74526,a812ddc3,713217f6,fa4be02b,7062d042,2dbf5625,abd1f422,58b54c36) +,S(d674ace8,a7e8ae,d4bd4b7f,235b9f4c,41ef4355,4d9e6e44,3b6a26a2,d16f798f,8a25a7f7,896c0dd9,b1639f99,5c4b3df1,a3536104,b7d59348,c2c56c0b,bb21c609) +,S(a8eb465a,8a2869cd,6419af81,b3ad1355,1508727c,2b2494a4,93b5ffe6,2c1375,f1a27219,f59f29c,bb93bdc0,6157618a,454075ab,b95184e6,a4601ef4,eb2c8a1f) +,S(ccbd9d,6253fae3,f7d2527b,17854e21,58581e95,11a1f11b,55e3e04e,7326e6db,51ddefc2,5d9083ce,e9999e91,cde32f70,344ec937,d6aa5272,3dd62496,c4b0485b) +,S(524bc1e1,5c75e8f0,6718d2c6,da0f6599,c8b5d83,7cb9b48d,615e8e46,49dc6659,f6c91d14,72a50c31,1bb202b2,3d76f224,b55af957,c7bf3c19,69d175a1,d36ed19) +,S(520e667b,a0604a23,dde663ea,1a29971e,7e753301,9b2217af,296b02f9,de1df35d,9c2a2c52,290b6927,ac566ea8,4caa31ef,cb19b65f,29b4833f,5f4eae8c,f12b38bc) +,S(156282cc,65317dc1,3323aae2,d5d83e8,b7113d59,a50e54d8,dc54c1a1,ffc85d23,840cca23,67da3c5a,15b6427,72f0cd7e,675bb8b7,fb5dd58e,8cda9fcf,cb21606c) +,S(15afddb0,5adcc8d8,f1322a7d,f451fee9,873d4b8b,78b0c1e8,1d99f002,84aca6a,15465563,a129272c,b5e25bc3,ac76c52e,b4e115b7,b216a40c,145c4699,175cec9b) +,S(9eec4fea,e9508445,4bd275e3,f8d0d6d2,9040ae95,c8268f55,918d5f3f,2f75865,b7a873a7,5fb4ef70,3d62a723,81df2ded,102726ac,81685725,424a7733,8307ed7d) +,S(3aa7be96,660652c4,de1c82f5,cce1a864,c37725ba,848180bd,52e4dead,1a33f60c,f323d266,3e5d14dd,18fbab76,271f6c8f,32d7de0a,42724fde,f6ed3582,c886c402) +,S(bae9e070,a8e5a976,8347368,c32b4287,c7a6a20c,b491bebe,fc6acc71,aeeecea7,b5791703,f98ae848,1a9c66ab,878f7c27,d37a3e0,133b4e36,82ccb2af,b518e8ef) +,S(7b12ed88,be061d1f,7dab874b,17602bb0,ef5ccefa,54f9ff40,e753312c,335f5f86,70930f8,c465292b,b02ded5,59a235e6,c4c83480,9eb94442,8440846c,377498d6) +,S(9eb22b3c,8faad620,ba002990,4a5d8d1e,8cb4a054,179e3310,73f4ae8c,def4e1f7,6d58b972,ee88dfd9,4e429a2,4f6961a7,d085f00e,8d6f8d14,ff2066e7,df68bc34) +,S(bb2aa6cc,d8402cb8,e7a9a6f,a755c66,366c659e,bd2e5c85,7fa486e8,aa86b857,443e7128,674c040c,b0316d9b,3908bcd3,298e727c,c805bc8e,ca58894a,d51f4130) +,S(c58704a9,fc3bb18b,c460c4e1,f17a880e,c0dce45b,e2caf759,72f55061,d59d30d9,75216472,aee1c9c,280aef07,7208fe42,136b495c,f80d8a79,e8309272,c6c4d6a7) +,S(b7d7c721,70af0b6a,489c41a6,e817da2b,ce59f30c,88a82b11,83a6af8b,174c70eb,59481a78,52f3726,3a02ef48,dfb366ce,7bf170b0,af3c2000,2c1053e6,5dae1ef8) +,S(b1386833,25f5450b,f4f68dc9,93d72fa1,1e636a3e,8b881ac2,f237cc44,42f3461,64d18f1b,87f9463,10627931,cb003c09,981c4a94,969a0df1,7f717830,4daa6cd6) +,S(7e27af99,500cb8d4,d812a82d,29ca369,5e543231,8432c9f5,96f5b76d,c6d1bd8c,25a477c5,a275daad,10973822,40961da7,5385a367,6dff887c,42bd904d,8e885997) +,S(62e04f6f,d953c744,aeca984c,bad13c91,f4e09c1a,d5441603,2bfbc277,5ebbfdc1,4f1f609,db0befd5,fe781afa,d21efe44,846f9f78,9ed9f99d,f3291eec,3cf3fc89) +,S(b08a1f84,a3cbc3fa,f952247e,6d8d8d9b,1ba3ab01,ba03ef8e,836b94ec,dfe2e2c4,5027889d,e524fd0b,e6ada02f,4f33d1a3,4d0c3822,f0ca6651,ecebaa37,3c54692b) +,S(d417933b,c7a8f68a,9e023d64,498b5117,850f8885,b6254256,f9a12a47,538d5bd6,7ad14679,21349d14,10e35750,167282ee,9a6f6044,b6c9d71a,633f4475,5b432ed7) +,S(575909cb,46a36dc1,c270267a,10a62f31,941fb7bf,3699aa43,1a3bc770,fa37d5de,c12ef954,c937d6a7,bda47e2e,67d9ebb4,9434cda,e1571ec9,f604c41e,b49d6189) +,S(5298ad25,cc5a6bf4,d88b693a,d32a61fd,efd763f0,e4008ca0,eee17730,4365de57,75f821d1,66b60185,41cf7653,9a0cc41,597b8b78,10fd1fed,f0da4542,3a26d5f1) +,S(5aab0fa4,b0beb77,d5d2a8fc,c95d516f,1dfdc402,3e3d36b1,4ecb206e,ee8c5ff,7a4659c7,7c062356,c51ee28d,fd0cea92,a1f36d5a,b78a3ae,8b48ac9c,7fc63fca) +,S(482fc791,7c739041,41b69e59,62b0b815,8a9bfa00,f203afa6,46973980,9b250a23,89b117bd,485ac6ef,4de8eae0,a9e96edd,a3615285,e33282b7,bf0b6a6d,e11a9c8c) +,S(a576ceef,40728a7b,b63756f7,f3095c75,ff2ef84,c7933d02,b9dd8a0b,a6046653,b4c707dd,9d880fd6,d7a1ea13,87dcb6a8,e2433e21,8ef3a7f5,e348d2b3,f9a9796a) +,S(3c9d7c33,f8c62b35,fe977359,f290dbd,c2b46fb0,a2fe126c,d06e0422,54d1527f,cf92e34,3ce9f624,84c04c9f,d97569d6,67065598,9418d858,aa7368d5,bd6a2df1) +,S(56bdd1e7,363c52b8,2198cbe8,62ed6d7c,7b96d184,7da44036,2401368e,b2c83b89,505d385b,747c9545,6669809a,a9da035c,364e7971,7cd89417,3d1d5d58,298a1869) +,S(99e0696d,ed30d242,bff69e9f,ff4ec88d,eaa3de35,34dc7f35,c2bb7df5,9f8b440e,2cd1fc77,3bd071b4,c9695f25,458be688,f662f7ee,ae8f313d,9c373172,6185acc) +,S(7d5cb46c,81f4a4ec,f1e5d913,1772277e,d17de7ca,7a4b277a,d6b135e4,dbe61916,cc854503,d73e8034,7a6ad7e7,f5bab321,b130d7d,3d938423,9e0656d9,74971849) +,S(45cb6ec8,9dda9aed,5242347c,a20377bf,c93ebbd,50e48ea8,159530f4,4f4a250a,411a2ade,aaaf43e7,240bb0f7,cde77d8f,846bc700,4721f6c7,4d313fbe,70ae38c2) +,S(a7e23331,bf08ecce,2dda8c2e,a6c3c4ce,9bbbe3a4,4d40068c,e39e94fc,edea42e1,d1f12d24,144bb258,dcda7060,5348c1d,85faf076,fdd3611b,ecf6c34e,843788e6) +,S(d81aa0fc,b0a8fafe,d4c67dcd,6fddd414,80cd5548,cbd6aa58,35e5896b,18797f12,92af7ad3,89320814,bbe53ab,4cb5bd26,3df11ad1,8d2ef235,ae9ba163,74495d8a) +,S(8d379fd1,fc0acf5b,c96d03e,b73b340,5a7326ec,82882235,918a380b,c3e2669,f8c6063a,eefc0e2c,ef94d8d,ac4e5b5,ccccdf25,663d993f,7f9a0172,6756564e) +,S(5e20f600,6f533af1,8f3b6e65,17aaa2a1,2876b79,c99ca059,c2791a1f,1e466e8d,b23fb129,57640a9d,d38bf206,7649216f,50b913b8,69d241c2,baedd5e9,dafac9d3) +,S(1a0a4f54,80d37257,c62d1b0c,18dda7ce,f2b64754,be9a3d77,b3d80ca9,215026df,2f76696f,f4001927,9ceb5652,44d4a60a,9e353266,42bc2138,1191a51d,7661ca1) +,S(be868184,dc2e91fa,b11833b6,f78577f7,8c190220,a2b46551,53cc8ea0,73a217e7,6ffa3f78,1c64c118,68abc331,beb8c284,2919d593,12bad699,53edd222,2b98a31e) +,S(a4650db1,917b10d9,1fffa809,d9f9cdaf,f07faeb3,8f0ecda6,9c80e0e0,fd27592a,d5a6fdf5,647e26c4,1d7b9c78,7880c76a,b0cbc23f,d4c1840b,78717b34,2ade1422) +,S(944b1701,4a7c7eaa,754c9e03,d56516d9,e21e254e,9a871683,7a14f19a,9ebaa517,ee198337,3822c796,3ae7df88,24687dda,eea01e83,c82e66fd,baa746ac,485a6dae) +,S(f9e76020,cb9087d3,a60ba7ef,7ed5e552,3e5a3645,f61f9ee7,27d630b7,98d37ede,28371db3,433bcf49,f6d77711,d7ed9101,f5e478de,49c58103,b021457b,7a0b526f) +,S(7ac0c7bc,e17509a3,e4dc004b,d18c322e,600aeb83,8fe2045f,6da348ec,6f178b41,8b5cbafc,33469334,4886f60d,ee7626e5,36e9a977,60c0dd18,7bab7e45,6b264956) +,S(1d7ef98c,c8520f5,2310ad12,107351f1,966b651b,4b2a39a1,7a47f658,857e3bde,2b78bf4b,7be6f1ec,61cb235,a26e252c,71d91f58,69752719,8125712,55d47664) +,S(fc57f5ea,b40add8,f00c2da6,c7d24540,1b24b6b1,f102836b,4041009e,2fc6d5f0,11d3e017,bbc33913,b7861dff,bc11bca5,5aea26dd,f956d21d,2f333755,ef823af1) +,S(340c47cd,3b473c01,80526815,64e77f7,6ec29eb4,b6603985,d6ef7b64,29e2f289,994d98c9,c1de0dad,d27f3386,eeb7da40,6144e060,ef4db403,ee8a76cb,8e21bf89) +,S(81b13929,f08e9a23,69127ba,3e6e839b,8e9cd05,b19a9bf5,2292148a,26bc47f9,b6c33861,ae8fe20c,7a6c7830,4dd03fc1,a281ea57,59661e74,7f9e904,e4261226) +,S(6f325fba,15a463ba,4a61e2e6,82655af2,72603643,8a48075,f76c9882,4d7e252a,1f205971,70f9f48d,5421b73e,97bb6260,cd2235e4,6f611e9e,99b7f8ae,cf435d45) +,S(4b388e91,fadb26f9,b23b015b,398c8666,91521b8b,484d4510,b3c162d5,834bc109,ade8365,9d59c363,3191c3b,20e395cd,13ddabe7,1933045b,cfd8e941,f2096d25) +,S(2d1c0ab2,83c80dfb,ead3e083,76024910,c1d97acd,6c8be069,252bfd94,ac1a0db6,c4856c74,e5ef2e6a,17ebc35e,3b310f6f,8e7b043b,abfff733,aa3ed9f8,5a391c98) +,S(4c77a1b5,b88ffcce,1f98fd89,251c0085,9d4197f7,d45bee5d,edfded14,a97e5b87,48b8135c,b067c292,6c316424,2392fb06,f698b6d6,e98b2ffb,bcdb028f,4d85e5bb) +,S(d39cd5fb,11cabce0,e31f6fac,10cce3c1,c0bdbcc7,294119e0,2b433b0c,20e3a4ee,487695cc,8a8ec5fa,aafdfe25,3aa7d635,8d44e82f,77328e8e,4541f7bd,f629bb5d) +,S(238a05a5,519ff4e6,29b0f86f,9d259eee,4e620685,a2f0509a,cc9fb1eb,4e89f5f,6772f6b7,816018af,ec95717d,a6be2d05,3d740ec1,80241798,117ed39f,91f3beb1) +,S(c146bf13,72816407,b5f8532d,91210d59,a8a6db2e,54fcfd3b,fe910365,99f7ed14,e952ca9a,f88779c7,eea67194,3df6fad9,72b717a2,19d1eca6,33106f97,2dfdf0b3) +,S(4cd9154,861edfa7,c29378aa,1a7d6d0,83b134a7,572383ef,3010f34e,19d40bea,9ad99f1c,b5841c57,5f7db959,362542fc,40b0cbec,58d16ce1,54c5944b,6ff2aabb) +,S(d35f2995,2478be06,e86b342b,fd2d2cc5,ede30ee8,509da11,504bbffd,5884765d,a0f66db4,a21462ee,e51e226e,2d20d896,2c4edb1c,babc45b2,645968a9,c7810fb4) +,S(caf9c953,ef1f57e6,905bfec0,2fd64fca,92ded9a1,9435ad1c,a672cb7b,117c6fa,23b01776,7662984f,9c2f9e43,756ed3a1,3a574960,558db4ec,8670122f,472d0ccd) +,S(4bd40ba0,21d2beec,ec7317ac,df3b719c,179ec012,1274a198,d0ebd9d5,165bbfaf,9a5751dc,d7885a78,20d8ccfd,1db3e3d,aaa2bd65,b7b6fb2a,2d7b2902,c629d81f) +,S(560928ae,7b15a63,62eb4444,d4c0e069,1ae43323,77a2de40,83f056b2,70259814,ae97006e,fa5986b6,5da4ddbc,2bcea172,f12945d9,b6f5184,4027085d,bf644794) +,S(7870209,38e53dac,8e250835,9e41e313,25b09faf,95b03a8d,c25e83ae,f8255301,49fd164a,915e0af1,a66af8ff,d215d1d3,92e4996e,5ef68f8f,51a4b895,145bbd90) +,S(7aa3742a,6c82c90a,7e6c1b04,a41e4643,47925d,66f4e7c9,ab2aff17,e64932fb,7317dd5c,f2cb9b3d,5cc0fd6a,c433d1be,d1dc8345,b26e10b9,a2b8cbff,ce2db0c6) +,S(7a400535,be3b134d,4572e0bf,75c13098,dccc0f04,1c10068d,31e2b0b9,149c2e08,c00fd102,309626e5,a2f97ef4,e0374a3e,de0a7ed2,e45aede1,40521086,5d10466e) +,S(f4d71adb,438460c7,9003e150,c4143557,ffe30efa,eed122f,dd97c572,e1dbbbb7,e73588e0,fbae054c,140c0474,348b7ce2,92a1976f,e8c940e3,62c29ea4,31bb719a) +,S(34b68d32,810bd1ae,51d0c82e,ef99752e,8206aa04,8c5c30,7c3e9a25,b2d68b94,4d044a,6781875,6aeb8982,61bff840,a3ab951f,bf60cd03,e8befb25,97203c7e) +,S(f83b78af,f18f22b1,f2081707,3dbb9c79,4f180b0d,8871a1af,94d16d29,5c71f1c2,93ea5b8,c7847ea0,2cd5b783,5021d37c,dbbd395e,fe6eff8d,866e333e,9d6083e) +,S(a4a0d5aa,f521e1bc,2e818063,f4575f1c,99bd2447,80eaf848,2737b297,b2deaffb,3906373f,2111a47b,102b0294,f9fe1a4a,7ea9a9e9,aa2d9595,68ff8419,9f1abd97) +,S(cc211da7,28671182,1f0bca25,b9703fdd,ac9d0277,7e1829b4,4d9921d7,998011ff,c0cc46fd,5519211f,3a4cfa43,d84e5b3,bfb316de,39a5f4,9a50b1b1,8dcef321) +,S(8a07ed5b,e0397b5a,8c33ef,108df270,4b385c45,c8296593,1ed3e391,17b2f531,432023e3,8fc1a637,fb5a92f5,9911932d,f5cd23f7,eb16ef68,c8c573fc,86e8c1a9) +,S(8add7841,86b68399,b08a0bf2,a92a3987,36fca803,3807b564,f97af438,ac70d49b,758cdffc,ef477c3a,fb3028a3,2866d860,c65aa2ef,7b3d6261,9664ace3,658add34) +,S(ec6b9f2a,a1cdedaf,306b8a5a,b021e430,e9931126,a1c505d4,a10e71ec,3cc60453,8139b262,40e2c1ca,8ec89808,2a37d7a9,b78a7e5f,6330ed32,f9786cb3,c4ddb722) +,S(22ce2541,dd8a47ae,227dfec0,95ac361b,88ae2cd2,c9547344,f551f6bb,cada196a,68ce4f4,a677a0f9,60aaf4a4,f8e83a24,ebfbf46c,6ce2a470,255d2ede,c4431581) +,S(ee7dd0d0,3e5b6d8c,ae78caaf,27b902fe,4136201c,12a49551,bf208c0b,f88bd33b,aff0eef0,609bbf68,7ce2aef,9f48f87e,67aedd82,e5342159,3809cf5c,da4ac004) +,S(f7f9a352,ca18b2f4,3aa18c00,9ae0eb31,17679db8,d9fd7e5a,41b77e85,b54140cd,afd30383,ccd2a6f7,ae76b953,5bc80330,4419d38e,73e12d1e,dc407ef2,f3b50326) +,S(7793ef5f,8e57e872,ea9fbb18,bd710ab9,6ea4f646,134d3308,930cbf62,e73f0e1c,8d5b3b79,3573090f,a4a7e7e5,c38fd987,e889bc3e,720e05b2,43e856f6,32ae7cc5) +,S(db743211,ba814bf,e6371ddf,d03ba554,b558548a,a90e81b8,e1421321,656065a8,8236f24d,965a9003,84b382e8,d772d7e9,2dee2ce6,c3cb3388,3ea627d5,4a5170c4) +,S(48c22865,6e51199f,6306a6f1,7dac1e6a,13f82d42,61de0f0a,9158ba98,715d3cf9,19a2a4dc,35d2bd03,5bc5daa2,5526598a,8eac11c3,52e5fe70,be726531,747035ef) +,S(70a28bf4,ce75b491,582b48d5,9a5069b,9dcc1e49,c41702d,ee5d688e,6e643a59,7183750e,2995f655,1b58d5e7,21a5f33b,69045934,27f27f34,f8ebf62e,74e49de2) +,S(a90d8505,596b3d01,a5e8dad0,fe652cb2,4a008a7a,61ba3cc6,8401a7a5,5c2e3132,f98af047,6e925c87,e13bb7db,f992b81b,1be564b6,8589cf2d,b79e4800,3ff7b0d9) +,S(88647576,8e960c09,6aec9832,448decac,6574491c,df737dca,dcc42b84,d7e775bb,ad94bcbb,7309f483,19077724,2582f5cf,a39ead6,a4050afd,89a49ecd,7a9090bd) +,S(c505d2cc,ef72aef5,8130f472,2ab3b2c2,3a6864ab,bcfffd5a,725a6df3,fae02112,dcb82329,cc51dc7a,54e7104,462997c,3d135d6d,5c82463f,45e875f5,8738652c) +,S(24a1d4be,c709cf8c,8b8927a8,8bcfb11,43b0d579,10e0900d,68fbb682,d4e14df9,9ccab057,8440f6a2,edaa4f41,28f427de,adce5584,692b79d,624e8724,572a043f) +,S(48c44581,51df1654,fa78370a,6975e59e,576eeb40,e94f636e,72aeb37e,8fbdc4bb,f48e8dfa,bb085834,43a7b495,ced76171,348e92b0,41b9620d,3cd5a84a,d9ee4eb4) +,S(42a7838,d33d0b15,b4409534,18024a33,e93ccad,946b50cc,a543ea1f,1bb457da,dc2f0e6,4f30e973,cdf6289d,d8f6455d,51c2da02,bc4e1d13,6668250c,e4dd791f) +,S(80988f4f,c2dfcb86,f1f65196,d93a6cfd,5c664e1b,5f8875d2,61e7c4f7,b2ea31e4,1e695c33,b526d582,6210e694,49c4673a,8697e1d4,a197c9bc,a3ecafda,2a7d5b93) +,S(a0ae003c,65d7f1c7,b65cf828,124701b3,876d0be,587c2589,9a618c4e,7b79d480,d7f4ec52,bccd57a,9515e80f,2c79be42,a6b53ca6,253ee13d,ca33c917,ff2ddf02) +,S(392f57a0,ebe08b06,cb4946ff,3c1df6c7,c5cdd97,b9cb2190,dbd4678,cdbab3bb,1be9f9db,dfca7981,b3dbbb1f,daf50e6b,c5662fa8,1b8a58f9,9953eebc,d1d0be1c) +,S(cd904f6a,3d8c4ebf,6ebc4e5a,e536c544,4a04e278,389d771f,d5e7566a,fc592d3d,e3e10f61,a15399c5,f3f29626,af323cd4,3d6572fa,bad4f02d,cf76c0f4,9785919f) +,S(27a4480c,df5782bf,52ddb140,ea5a908b,38dc1ec6,a570580d,2b72b922,e7eb05d9,bd96ddd8,1a6aca1f,88847632,5a6d48c8,4d61e76e,b4d0d957,15d39f80,faa42f3f) +,S(6673ae66,3511292f,fa4dd8cd,bc0f7b36,613c56d2,775602ff,a8cf9302,4329f5a5,d8b4ffd8,8ca2530b,18d85762,529d056,e8048cdc,51730082,2d24cf42,e4053141) +,S(3dbae13,5c395043,d5b88161,4dde355d,5a669076,98c36b23,ac4c24bf,80e34f2a,b7d46059,3154fad7,eaa37c3a,291c6c67,16df2944,4274b044,a9f12ac0,a34139db) +,S(f2db4c09,1c3c606c,9784f6af,7dbfb26c,2b18bae3,b22f13bf,4188d761,6cbc1371,8c84e295,f57a9722,260d5816,c5d57719,c6fe2a12,3ba20a48,189bee04,ac252f32) +,S(918e415d,92e768e9,f5a875a8,aafa7dd6,66109cd5,8fb6044d,d2c8640b,f6b966a7,b53400ce,9ee9f6c6,9cc35b5a,3e04eec3,dc89c931,e545068e,ed4a818e,42073e71) +,S(5eda3e22,bbb8c368,82d46c19,2068b3e9,a65624ae,31feba8b,df725d5e,47bc3437,1cc2c541,ce3ad8fb,ac3cfabd,a492c03,87099e1,659cbf88,bc8f27aa,f9a2fe26) +,S(7df83e06,9cf09a92,5405e0d4,3cf0c7bf,52a8e603,d286e26f,f273e218,b455e61c,9eba2975,7f61b054,29c9e915,a092a591,ac1d6044,8a63a8f1,313f4018,1650f4bd) +,S(b778c25,d78b0bc4,68dcdc80,5de93809,1c3c36c5,b5f9569,a9394b5d,c7afb162,901a4ecf,b4821145,c7d9e56b,75e43ab9,cb7ef8fb,1d384a4e,27033e9f,90b80fc) +,S(dd4965e,f6fb759,104f7877,307e2a6a,f837ab6,8c19aa40,6fbea75e,e8a7adc3,45e92312,b5c77585,3c4b95ca,6b4c1740,a70b57c9,97124883,2c53d291,4b0fd4d8) +,S(ac971379,7dffd8d8,2e33f927,48ca063f,f1c8ccf2,5b88fb6,bf80946f,1dc9dede,6ce10e3b,ee4db87d,40e8de66,fb5263db,2a578d6d,be100fef,d6e28bd6,7377bc26) +,S(6046fab5,37fb413b,29797312,5a6049b5,5c085194,4b058264,c39926ed,fe85c6,dcdf9ed1,cbe469ca,2976333,5a159e42,af6f0d32,664ffb5e,173986d3,a277180d) +,S(a0d94b05,8e3b4d30,7f3b3f09,ba5ba28,d0285a3a,22cd577a,1d949b8,bd23f1ae,37fcdfb2,e3027e07,ea77f28,447ea13,93971c67,974304b5,dbb7621,2ad2db10) +,S(da15b971,1b974ef9,14433ca5,71175233,693cab3a,dfd5f6e8,cf034254,6f73dcd,e08c7f62,90f87fc5,4e2f4ec5,a5a93d19,f4484868,3b5ae9d3,810b0615,c7e79402) +,S(b26ba5d2,72a744f5,562cd745,a3f3f143,32980458,60be4e97,d737fbb9,b3b0d544,3d308307,9830f281,14de6c5a,f3876446,507558e2,95353dcb,738deabd,80c0ff20) +,S(fa26c55b,bac04fff,ccee0610,ba9b60a2,4042aa08,7a564ffa,b891e087,7a2185f5,5b5a62eb,1ff579b9,ee4c5d6e,ceeb57ec,e9881b47,dbfc24f4,3b9a18d1,4addf4ab) +,S(5ada4a59,4802163d,4da685d9,b7479f,4b70b489,5ddac7a3,b06a1c4e,a5f46dbe,2ec25e70,871b4958,81f6b0e5,cf821b91,febe83db,a52fe2f8,1a2e4e82,165c7e3f) +,S(a57751d8,895282ac,bef2384b,db4817a3,539899b1,e7fe7a62,2596ad18,5dcf878f,651eee9c,12ecaf92,9eccf109,635680a,a7499537,f2054e2f,69b0f062,c820784) +,S(3f17f0f2,6a540098,d18f8d88,ad323e9e,e300b029,b7b8101f,6aac6ece,8d375f3c,9c0713,79e35319,7f1e76d,55616e5f,8e573a54,b6bfa56e,5dacd2aa,e2e0deeb) +,S(eabdf476,d2e9d76d,775388c,da759930,4e1ebd59,b3a7e491,e3c9c62a,4d0968ba,f4bfd5b5,e0e8ab41,a04c4cd7,763b243f,b82f56ae,9f08a6ef,bc365f3d,fa227c4a) +,S(c5b76ae8,dabb664d,fc1fd499,36399e3f,c2b7366e,a21cba10,dc7eec7c,5c93e0d3,ef30249,19bc03a,c24c8544,9467728a,c160edc5,7c901e64,b947f298,9bb1242) +,S(7161dbf8,6b1080bd,bd6aa47e,dcdf8a77,17b7e281,c692e9d8,739caf9a,f3f23d6,3c30b786,b095288a,4522749a,3d32a35,5a8bc694,cc9cf7ea,989675e9,a1688e8c) +,S(ed6a7b49,9b74e62e,53183b15,6867d41,3ce902a8,4b71fdc4,fa414f8f,249c2ce,793d0356,1bca65c6,be127ce5,a1034b9,86285fe1,f52075e8,1a9b5bcf,87348c4f) +,S(fe6e1abc,3ee3be4e,22456f4c,bf93810f,282f0fec,96ab5cc8,2b35b08,54492bad,96c76282,3adc2cea,1563c02b,fdb25b55,eb15e769,90a7ed7a,3fb8aabe,47bb077c) +,S(979e7ab1,96e66818,b67bf79,31232826,713648bb,ad956add,ed594cdf,8aec2937,696a9505,e3c473a7,2df68f96,13c2e494,406782a4,f92a8fd6,5017a83e,d7912491) +,S(9f947cb3,f859f2af,11a63400,800c18de,b8813c1a,b396ebd6,a1d5bf4a,9689160a,8efeced6,7f1e6fdc,191ac965,5002a02c,386a20b3,eb2aa1be,7fc12008,b8bead1) +,S(71790030,58caecb5,dcd2e281,b42eb59e,57de1e6e,b77f6bad,7d2d1897,6a512eb4,abff54d0,e20347bb,c10a79c4,127f5f7d,868176a7,4e34077a,8ede1d54,7e339483) +,S(d786ec15,c140af46,841b7eb7,de5fc9a3,c0636885,3950bab9,ffdbad01,d836b8b0,fc90191b,63616a44,72efb2ac,a7d84da3,4892af1e,25bca0e2,d41268c8,d381d062) +,S(2d553f03,31eb83ca,edf2508e,67054734,d929367b,ffdd5b7c,dd78dc6a,2e724534,9abcef4e,ee588e28,fa1dee0a,b8c426c1,67d37cde,f8aae9e4,248c036b,baa1af70) +,S(326b36b,eab4d932,c40182f0,fde043ff,d6b84b16,72fe1dc7,3bc7e518,6ef68062,9d86868e,2e871ce5,c3488624,bec901d1,47b91bf9,a3154cab,6445870e,d51e8f1d) +,S(f7a6a26f,4e3583bb,9552556a,5d248735,7ea4af7d,ad1f7a92,717d19ab,34dfa11,7ace27e1,dab2dea9,704c01ce,50ea3de,529d864d,9a5fc11f,fbc0e8c7,81778b2b) +,S(8ca708ea,c072334,42eae4e9,eb0c7b45,83e153ba,84ed7a0d,3ece41f9,4df6cd15,33f41d25,e446c47b,68ce1bc7,d1dd1740,fc701538,3ac90e05,1dc89a9f,dc97592b) +,S(4fca2ed9,e1948697,208a87f9,a890cc96,4aa9e5e5,7c7274b1,deea92bb,586875da,98a9303,4df7185a,df9f557b,b7def9a6,41e7ad5,a3f24051,435673ff,12f0580) +,S(466da0aa,97c68c05,21b8fda0,fc70d74,bb586eb3,da7eab4b,6c104404,d9234d37,5834296,f94f5b54,beddc0e,3d7f6004,659952b7,90a6341a,de99ff26,5530e5e6) +,S(ae22ae10,d016b54c,c0b5423b,3f4c332e,3180ff23,c279078b,864e8e07,77e26a5e,ee21d64b,d6043d56,de3346dc,f93e2c40,b9a51498,b4d7df61,d476d339,7a2d0c8d) +,S(58cd60cc,ea7e0896,2f65a353,5d45ad06,517fea5,575c5511,277103d9,fabb5167,175a68a9,850b4026,bc7b5684,e859c808,edb5f3,7acebc47,9ba489d5,5aa9774b) +,S(4be8a0cf,7ce26101,49cd90a8,68f4049c,43054aa5,68bfb2a1,a5bfa386,9b2b485,a3bab0f3,e3bec469,f779404b,d025ca14,79e3f6ca,9d496f8f,8ce4aa6f,b7e3b404) +,S(428599d9,398a2c59,58c5f74f,1226605f,3c5c8e74,ad8f9bc2,d62b8d9f,d679d3c3,12a1b04f,add5d70,bdc5e15e,d6347f7f,6785c425,61f14ae6,275cf050,3a1bd5a7) +,S(134ba4d9,c35a6601,7e9d525a,879700a9,fb9209a3,f43a651f,daf71f3a,85a77d3,21c112f7,6a9b9b21,e2e3a7c2,8cf500bf,f48aaf1c,48e0e13e,f15618b9,ca62287d) +,S(d53cf8f3,cc5e5739,bc9f9144,a44e7b7b,bb8f2c36,b50844ca,9b2d9cf0,cd0616d2,799c23b6,c57aac44,90d71450,4388cfa4,689393ab,6f30a347,4503400e,c568e03f) +,S(a1e22cfd,e8d12f17,eea1eca4,ea0b520b,6598c036,d1bf837c,e23bc300,a384e5a7,cfbb52a6,e1fb6a65,77cd0377,8bc965b2,a3df592f,f73c34d2,a91b8008,74ecc295) +,S(72bf047f,a0da1391,78b97ea,7b317168,ed0576fd,71f49409,39a8daa5,2a02ead8,f8d93088,cb3457e,7828ba9c,bd8492e4,9eb2bf0e,2e23bfb0,4591e9bb,5273f462) +,S(c5373038,4b951abb,3bcd1f4f,98d983bd,cb5b36a,42719531,c4d1797,2e9bce59,d921f353,a9f7309,6dc16028,f9e1b562,f406bf49,54d78bf9,f2b4c8b9,2db26a82) +,S(ab20d194,e2a6c4a1,5705d3d,19300810,89342b15,e9fc6224,7be58805,a8682a5e,44dc39d1,8342502d,cafafe40,33c4893a,2dddddd8,ad7b238a,efca50cb,5d5b3186) +,S(1432ddfb,108ee664,2eaa9c97,585f47b7,b92a5ae5,39a38093,b8f3153c,e971c8f9,5362084b,5482e375,1662aa95,dd3b9a6e,65235aca,b0ce1eae,f5153951,1391e269) +,S(f199ede,4a69d778,61e107c9,376bdee5,6430e279,28c74d75,66e65589,be8c433c,5e7f6fb7,1b68a535,f407a9ee,2e42f65e,9835bcc7,2c34b8e7,712e5332,a65e9ac6) +,S(b24dbf51,47452b5b,ad94bf4b,493120ab,61ce2ea4,37b3ad71,62e568a,1d595d1a,b3af5a90,8d063602,6b3869d0,c9f31793,61afd6c3,8db99cb9,77b14e0c,46427518) +,S(d7fc29eb,b681e025,992d9ef7,295db58b,40f36c64,d1be10e8,c8e66858,d4d0d9aa,c587764,ca84b3c7,2f95eba,eb62494f,ce391202,90c42b9,e8b2105d,ed972595) +,S(5c71e90,d5f00ee6,e033f0c9,c1ac495b,d148a31d,143a8e3,59a99c7d,faa3cc9c,4acc164,ea3071b7,a4830a27,24135c49,562ea631,9dc4455f,808f1282,4e3bbbb3) +,S(4c9904a9,a33f7e33,9a3d85ac,c12b21ad,11f7d7f2,76e46620,76878593,9d11eaad,2387a445,b0967403,86ce4634,62dd101f,95650365,ac02ef1d,2417321a,3fac1df9) +,S(58f457ec,12d22482,2a91b78e,26f7d8f8,5fa39d90,59fb833c,bce4b1f2,5c9720f7,90808a70,79d4970c,b2b70ae,390844a,2f0283ee,eae05a5c,7d2dcdde,be656f2d) +,S(9867259c,51414805,3be6a73c,de522d91,a8d8b359,9f9bd39b,9c9575a3,90405bdd,9904fa47,34973bc1,77a3d0dd,f0edc0bd,cbd77b36,357734c0,a0feff89,4895f3b4) +,S(663b4991,913148b6,631d16fc,27471938,20a7ace4,ee85c335,17b60cca,b5e12b5b,1b295a18,16b92c5f,15b88bbc,177dc011,f6ef0760,15b5ea47,9bca876,ae072070) +,S(5e3ff13f,bed18235,fe28dcc0,72f09cfd,41394cd,a9a3aa52,5286cddc,e56acf05,c1488c2c,903bff6a,411697f4,44e6e72,6ed9c0bc,5aa87cbc,ed8f8802,6998fc7f) +,S(54c8aa07,2f1fc5a6,ade56cef,d7c6aae4,846f6855,34912868,b84b195f,78bbe14b,ef11dea9,5cb01680,e7205f1c,62aa5242,2c0db969,9b2a0202,966873f3,1fdff34c) +,S(bc69e00d,4118d8af,baef7647,6e435883,2ae9864b,f154368c,e2ccdadd,c58cfd9c,148aea9b,2babfe00,3a6592f4,d3788893,5be42ac8,a50f68d7,ba2cd738,47dd9c52) +,S(50ccf184,c0213602,af114924,fe100f78,1f0d0cc5,beafb671,9dedea00,c7c3ebe2,b6559878,d7c33082,4588dd33,3054c4d,2da86b8,ff23fa9d,6413920c,d876e36b) +,S(c24ca0a4,8ff2cbb6,235ef33b,42ba032,386d5475,3cdda799,e9bc53c0,5c299aee,8555bb16,bf4d7bb7,5c0cbdc8,8594454,bfc819de,72c3954e,76b65780,2be28fe4) +,S(cebc8853,3f6c3a01,58dfab12,ef52b4e0,a7473d1d,209b3585,bc94a13f,a83abf17,42fe28d4,136bf80a,7149a0b,7ddce53c,45e23c0e,7be85fcb,73c0e66,2c4c0593) +,S(7d921a84,65b11feb,7a755a4c,5d96b6b2,f17e07a,95db112d,48c03bd,18f61422,f0ccffdf,7f04e350,13ae7f36,411dea5a,ef69f220,790c37d7,354ce5e,83925f4) +,S(b6909fe5,6824e868,f4cbdd10,d3d00ca,23e5b4,35c78aac,c78a9a1e,321d178c,3d513c55,136d61b0,73dec253,e1ed817,782d9f76,b536d3f7,c3b0d1f,d06e6c1) +,S(5a3df3e8,c78c0fc,56971dea,ced3a6ea,e1ef668e,2485bcb3,d4e24408,7f53cb74,960721cf,316278bb,56bcdd1d,9f3385fe,8feea6b0,661bdf1d,44771067,661aaacf) +,S(dc65c06,50cc30e4,d759fa93,d4d0ab65,5cb16591,289206d8,7a988290,20c22f9e,be39ae0e,a41a81d0,2175dfd5,5e004e19,fa925a99,2166d862,296899e,2532b6a6) +,S(f659d02f,f2d4bc82,9f816525,24019a88,6ad539da,3f38b083,5d8265cc,bf3a67ed,bc9c7a81,9036c702,338c57ef,26134647,2a22b8cc,5543f4b4,4a4f7d4d,d4339573) +,S(b89bbc05,420a8a65,aa03441e,da1c54da,9ca84f11,e85a393d,47cc54b9,78b8e5c0,5f3a2fba,4cef4457,ca6befe2,7dd3f9af,a5d8b6de,86679eb4,c877c7b4,cf03c08f) +,S(b9bbb789,690b53e6,e42266bb,853cfbbb,f6788b58,b325097,a55470e3,d4cbc3cb,1f984f38,78427212,cbfc8a93,8c1f0d87,59792ca7,17635c75,71b2c4f1,29e35c2e) +,S(d8ae91cf,8a99ef6d,2ca0c15e,3132974,33cd46cc,55a2c8f3,9f909e96,7d50006c,4fe4696a,79d2f269,9fecfce8,8375cc1,81c2c2e5,4109f380,3377a020,5496d733) +,S(35dc2c70,181aea7b,ccf6a15d,1f708c87,cc8bc5af,93671c97,35867367,549357bf,6f27dad,a9abbe5e,5e59141b,78a2e6dc,5b5433ef,dc8c9cde,a6269c2a,68346f87) +,S(a4c042b7,d54b67b5,877442e7,2e81de4a,7edbe667,f00838f2,34f567a7,9e3791af,a8c6f7b1,6b23a9cb,64732852,c9734547,69ef3e4b,9165ebf0,a1dc4967,36c73242) +,S(d6ab68a3,353b6c65,3836b66a,8c39f2d0,892cf856,90ebb56,a5e8e4c9,e669da34,4847a9d5,f39ea1e0,ce392c8f,9e63e88f,8f3feb9d,7b2a05c7,23a932d4,b07e38e4) +,S(e161c34e,644eb3f4,fa217d33,dd3129b9,c2b85ad6,40b56e56,e20982d1,c5483d43,ceafd0af,a8cf7a25,be092307,22c30e41,b6ea151c,d027326e,5152aba8,d9a50749) +,S(94959a2,fdc2650c,b4b0219b,83ac99c7,c477132,eeab585c,1fb262e7,6cca22f,6243f3b6,ba7d4f26,24f7b7d5,8e070b2d,64610aad,b281b4a3,b93b030c,ac0cdab9) +,S(491129ee,3130c2,f262c4d0,a1bfede9,e470c3cc,8e80774f,52ea023,67e2e3de,c1e61e2,a5310866,762a0cf0,ebbae0f,738b9527,46001aed,a991e0ee,d95f0db5) +,S(e2967539,b0b29fee,a668eebe,eb8e47c0,1ed84950,a1f9fda8,6884a421,5ab6f265,c304cf3b,b7faabe8,cfeab6b1,b88d3d41,7e950963,745c2479,85211d50,888346dd) +,S(d90a380f,5283132e,7d6760ca,f2b28576,49445507,b8e8872b,d0e1d5f,56a15af5,97fa0739,7a1357c5,e24d0a3e,ce608332,107e41f7,c65d3c6b,98ed66c3,af30a4ed) +,S(cb917a08,153599e3,e531b1ac,63fb8e71,fcce572a,29fde1b,17de843,a99ea2b5,701c08a4,a9da11c9,3b4be64a,6832af1c,900ad3c1,4b4dbe70,c62e3e08,55b41b6d) +,S(e6ee53e2,98efb026,bce900e7,8d31f3c4,539108bf,d4c5139,16417f8b,49199a0a,bedd41e4,751f657e,8cfc65c3,2a82f981,4b223e62,95ec36cd,da5125eb,da736429) +,S(5f777c41,f4e3441e,75c66f28,852e7815,59d0f3fe,b8b05b59,b6966fa0,de84df2a,7fcc66b8,127d26d8,e0ff69cb,b80acf44,bae5546,edc9bc52,90b73da3,447a2731) +,S(16d312a8,1295ab9d,c08f9fbd,61e6445b,5ef1dc51,fce19d,89f04e7c,66ba2e07,6c555fca,2a1d2f4a,24ced2bb,c3966f4e,1bfc1912,eca39067,503df586,41a4d4b7) +,S(660d3993,fa887e1e,8b05d80b,96e2b70c,341c3ba3,351d2562,5792acaf,3859de3e,87b06f5d,4c6f854a,8714eb3a,55454346,f931c21c,c77da23c,2d069d33,7dcf12c) +,S(9d45e660,879b6e8,b8007a8,ba7fe7f0,c7090cf6,9c733bf9,43f4eef9,965a1ae2,c81a3fd3,9b6b83ba,d163c763,f3010359,ac050e64,a47167ae,2ca41345,c7d95662) +,S(c95d92f8,c81d15fa,a68f645a,c1ebebd8,57c6942e,e75ba13a,3a4eb9b0,c45c43ba,2f55ceb6,c1de7877,7b322a91,7f3e48d9,480b80f,d49e362c,99f8104b,a20f392d) +,S(95d445ff,e8cf8240,fc4e9856,9d41164f,50e9ebcc,1fd3bf19,d4309de3,34b4c97d,7c391704,a75cffe2,f8971ddb,417e8158,f97a3688,6841f02a,c4b699b8,df6c0d32) +,S(40f028ca,907d6521,df1f052f,ba5268f9,a60e705,bdf94328,17881b44,1500170d,4eae26bf,37d04379,126ce777,b1fe1115,8c34d077,bba66994,78ede7e,244cc8c6) +,S(dab90c34,e7460068,d80d2be2,2bd81f9e,667bbd96,4e30c23d,3eaa7a75,a61e862d,cafa2892,5128bf3a,4821cd14,8503ca8a,86f06388,d21df6ee,af92d514,e66c5f1b) +,S(6df2280e,381ef8cf,922505d6,7646c1af,105f91c0,59e8e7a5,3ae2e7e6,cb456be,d94a020e,7776b713,b31aff5f,d72be6c,f29bfe46,16ea3d29,95913db4,698ae3ca) +,S(a13d83d1,3c60cf65,cefc8692,f5d339b,2873582e,938373cf,3e4df54e,ae32ffd2,8f511212,a65d8963,d7df7ed6,27d87fd9,208f83a,1a4dd14f,ab0593d6,38ac71d0) +,S(ad4c1ec8,39950ae4,a7fa551e,5aa637a2,e5d40bfb,3fab696f,ea886a4f,40bf2167,fea9b599,202841d0,c3afbf08,ef1da210,cdd92a4e,6cad485c,a0d5275e,8a5dacfb) +,S(64188be9,af657254,1314e661,848970f6,35a0b6be,7e594824,6dcf36d8,66840610,58880b5f,c3df9b6a,45b49ee6,b90f6b5f,f3fc440c,37855167,a30ac013,3d48e53) +,S(9b71366d,a66b165c,6495e4fb,46ecdc15,84a193ca,d65ad8b3,7a01a3b2,cf2e43f6,d7315165,ecec296c,4b71a860,f3b2f0dd,fb9447ed,5c59a458,8b81f679,34321301) +,S(4a3c5006,74814f95,9e9c7726,595658ff,84e369af,636f6aff,ab579bb8,5583d395,1b495757,1a302c2a,d1cbe53c,2ba34a6d,66bf1aa1,8bddad33,679e4890,5f9a21e9) +,S(e4305e5f,72c43558,79a749b3,d563d4d1,dca704c9,c8867d01,ef503cf8,97c90034,ee563dca,4d31945f,c536a318,e9297d28,81954b37,af992722,9e4e0e9,1c16244d) +,S(3aea4eb8,ee74c9d5,2259f37a,da33ae1c,78f5dd8,d97511b0,2e0a0201,82840a8a,8fc58135,757b8c0a,a8f2c2ab,ec9d9152,20ad2046,f429667c,c4586765,10e93e87) +,S(58e8e94d,e9f85066,7cc4c61d,acd212f4,77454942,33deb015,858e500e,9530228d,5a95aa24,fb782a4a,e64e155e,eac2b644,cd614315,9b46db6c,9bb4fe4d,5e5fd690) +,S(6642c782,7e1df912,ff54dbf7,41fa1567,3e9c3aae,5e7630c6,9cb74b60,c070af1c,ff338a86,34a404c0,cb26af9f,59df36a9,e1a848e1,ac178b25,c37ac177,db45e947) +,S(c5c69fa4,65c79a50,a98996ee,cae55474,5c544a3,ea838a42,e12a733e,9181b30a,6e805461,5a424a9a,1fc229b8,7e594211,697386ef,189d1ad6,ac75eaa2,5727253b) +,S(f4faf099,fb16a1e0,e5f64428,5317d91,a31daeed,fbb6a6ec,b70a213f,ee95217d,ebfd8445,42fee872,1f721f3,11ef28e,30618bd1,bdd097c1,ca573285,5b079734) +,S(773b3e69,fda8abed,fa61ef37,57101756,9d9d2a6a,ebb4fc5d,d9049d56,9c66b76d,1bc051c6,302a383c,dc0756b5,114dfc69,1dd2180a,88a4ea9b,6acebccb,a59f27ca) +,S(bcf80e7,b2b54cf7,971fae7b,db8b3826,85acc1b0,f987f352,d9b0b3c7,ac78142a,20620b00,8c010ad9,7a98f093,8d9a381,f9935d5d,4bc9b060,af2ee6ab,151eedce) +,S(865dff30,47f31564,99337316,ea20223c,5967e034,83d92d54,7486fddb,fda959d2,2b655c9e,37697f10,5535b736,a31b2c6,7f5726eb,602f1438,f808ff62,4bac074f) +,S(e48e0e18,101eef40,f351067e,63d2b34f,4fce76c,d204d6f8,85d2664d,fbc92d3c,594061c,8d3f4748,bff85cdb,f70a4035,5ad826f3,46d44aa6,7eca3196,e433bb65) +,S(9807e350,7a03e134,9bca9101,cacca5d1,a564b647,8c906619,61ba503c,5ee15bdb,301e68f2,d584b1fe,3eeee7c5,85cb4d0a,26621bda,229589cc,2704dfa1,1cb8d927) +,S(95f94e75,8602dcd4,60ded52a,c9d7ab40,1fdf1405,b271acb4,133a86be,cfe5027,f08d34eb,e9cf1b1e,78e1fbec,452e1ab0,412c642e,1328b2dc,f5e08b08,38aa3c51) +,S(7ffa20eb,ebbad406,deec68c6,9bdb7b0c,7615a56f,5c62a646,a57535e1,68a82a31,230241b2,3dde374f,7a039eb5,5181e954,f0472056,79edc688,7c4ca6b8,778333e4) +,S(1dc7e0b1,487fbb33,d1ecdf84,7bdbfef5,f7df9f31,d07ff024,be063a51,53eb5498,ebcf72da,18a1a55a,5aec4aa8,efe09407,263a7b36,f09552e,22cbceba,7a3cf980) +,S(a8052e02,e39d9981,2ac6c4e6,2cef526b,9cb29843,127f2227,50a8021f,4ced5cd8,9011f0b4,9dad7e5c,11222b64,c5d8c9fb,297d9afd,c683f545,849d833d,f2697220) +,S(3edfc4d7,f1f699c2,8e8ac2e7,5cd93e69,fff31607,d99a8195,30603437,f9bfb20d,f901260d,68c6dfe8,aef8879f,ea1ba009,a1e1d931,fa6ba39a,ce1e2673,ba759311) +,S(fdf8ae7d,c10d01f1,c39bee75,3be3307b,53307caa,4af0e8dd,9de7d696,45ce527d,2425a3f8,c6147073,3a44d3eb,eff3d8cd,61e5fd55,ffdac357,d7906600,153a8dd6) +,S(1cd5ba80,fcfc77c3,d06e230e,e7ad8ff4,f9ee6c60,a71d37b8,3718d8b3,15d5a4e0,f90ac118,30b963ab,fc64492b,415db7e3,47eba55e,d2c5a64f,578a13ea,b4435cbd) +,S(3cdb43ea,b7afc936,24b80269,18319fa0,e75de21a,c0587af3,541e492f,1510257b,49b6411a,c74a2eb3,ac2ec784,feface51,8d5a5bd6,e76d694,bf5ab8f5,c146abfe) +,S(165b6f5b,9b1e6d08,14e44cc7,c7969297,bba7c659,58dc3274,7e7c4148,e30c09da,e715e411,2e6cb67d,cb8e1ad,997691f3,bd6119c1,3a5f0329,1ea3928e,35c04551) +,S(d4c1190d,da3c01cf,6c75fa06,7b4dd1ee,e645a0cc,8b034f4b,24580661,8ced0bff,c837ed81,b6a0e5fc,6a02d6d5,f837db1a,20eee55b,41ff531b,9bb642d0,52c42bfa) +,S(f7359bd0,bf7db6b0,48f1a040,a0163fba,285780e7,909ab689,7963168a,a7de9c29,8c71bc3e,95ac0f52,54db6192,5c6f9112,759635fa,95846be4,6a28573e,2b25562b) +,S(4d54a2f6,dbebbee3,8950627e,8cf2c65,6ca071e7,86687f3b,a0a77178,be14c55d,6579dace,73dd14c,695fd781,218786ee,bad84445,ccd910d3,43aa2478,f5576671) +,S(f70b532c,e7203208,d6440bf8,55e1293f,cc6ebd75,96a04b27,9fc056e1,2ee32dee,e9d18f7,126ed56b,4a657104,2441bc08,1778736b,91062c9f,cac28cbf,b8737be1) +,S(bbf3ff3e,577aaf34,157e32db,d977b907,e62b16b5,1ae0abd4,f14e71fc,cca11357,cc1a78ff,44af7c2c,cdf17423,9b3aec17,b660ff2e,fc07953,2d9f9d6e,55d3abf7) +,S(53bb2321,b36d1dad,226ceb24,f55d4292,be245444,b2349611,5a649560,59729700,a5db4fb9,46c4a2db,b1a634cc,4032b7b,cf23f7d1,717a0ee9,6c02f131,24f834b6) +,S(3b863ec5,5e4ff2cd,d32cbc81,6e645009,da444255,9bf96267,2cd012e1,15372186,914682bd,d6a052bc,25cb4d41,e9bb28ed,76e9aa55,3f5aaf25,75140e04,eca69fbd) +,S(4cc4a750,d641c574,5c68a48b,953ee51a,258239b2,fda1f5e0,80831417,7b837946,cd6f665f,6f32246a,1cc8fa61,4da2783c,f4c7b92c,4f42749e,b6bd4a8d,8a84c54c) +,S(6f9b5d79,efddf08d,26183b4c,d6001d6b,fdad9e8b,dc5853,f3a8c47e,7e6f9e96,b81148f2,1e50152a,8cd7727c,ad14862a,37fd186e,2054d4c4,a4cdbfba,a9076991) +,S(9e13d0e0,44cd3fc1,1c7596f2,9946313c,918caa65,bcb6387d,361eec31,ebaa6977,c5f55650,7691bc2a,b2c9e4b8,512f7282,24276490,257288c5,2387fee5,54bc9c0d) +,S(a4441292,d0e0db3,2a4f33c4,8bbc5fbf,d40650c8,d9f5c4f4,88a1f69a,d71571bb,a23ef48d,38abc24c,608dd688,d524bed,a3709a5b,3567ef3b,e43f0240,796b7ad2) +,S(def598a5,c4828c5,16b7076b,9682aa82,4e74ab42,e4d4692d,17c8f289,67f3cbb4,e6b4bf28,6dba0803,e4a97a1e,be55a088,10660466,3d7726e9,300109ee,a1d62a49) +,S(8e991fb7,372df8d7,7b09436d,c9424a8,5a14888b,583b3c58,6d2a19e6,91e75b67,1780f6e9,b8379b0f,ebb1328a,69a96878,9f58b45d,8e3ff218,81d753f4,8ed6c43d) +,S(bae09288,e10351fe,fa0b0971,400ac3d1,a7eed65a,b11d1ba4,8a1c6240,bb981114,dfc2979e,dcc2de8c,abd9abd2,27cf7351,4079d950,aba60e3b,f4c775ac,72c30f4f) +,S(4d09ae3c,b484fe27,411d4142,e41de523,2c54c42,b387c7c2,cf42a23e,e7611fc1,b94fa116,27eaf5ab,8fb7fc25,1345eb28,8f5249e5,ec65e056,d60e1294,41481375) +,S(32f57ac4,3cb29fb,f8a00d8b,c95da8e8,3f0f541d,ed6c9e78,8af17a14,6a2704bd,2a969b58,9a3f9a5c,26b51ab6,1a5717bf,c06eb438,85e4f4a,84b903b2,da5bbf09) +,S(595503ae,e179f59e,bfc581b8,7ac18e1d,55b4c794,87d90d62,492d3ea1,1ec57579,1f25d8ba,e38c0a69,50a17836,a842b418,b0f7c317,9486fb16,9ba11b23,d315ada0) +,S(5555fae0,6f1563af,238184c9,304e26ef,f8b2121,394e6856,79de6792,31a56842,6840bd14,faf202c6,60650541,48bb87,df27b979,4ec0da66,9ded6830,b0623980) +,S(714321ed,2f0432f8,a05c33d1,84619de5,f81fb8db,95ab3121,c24fc998,ab9b9ca4,4470cdaa,7067de2a,e6678cd0,6d613e19,35b1edf3,bf278205,da69ee8d,6086c2a7) +,S(d41b84d5,9a0085b,86e88fc1,499151da,b0b9a574,92b01df6,36cc2288,313bb592,c04c54d2,57fe5ba,691f6023,bd72edaa,20e71a97,1a760bf5,3c990a39,f5ef7c23) +,S(ae34a065,b42f5158,e6a5b91b,9ff54b0e,6d5bc906,f6d18c3a,82865e,e9038f34,bf2c90fa,30639811,2ca2ed02,b7ad090d,8bc990db,b6140a05,afcdc80c,bae8490f) +,S(9b3c2682,3050a52,a164ab,f4504ca4,7750c889,b6465f95,658ba7b1,c279139f,ee36938d,8a675824,51ca289a,55265606,cb9ae4fc,8cd274aa,41db5ea9,9a5b4299) +,S(27dc9a57,999ed109,c3dff978,58d44ab2,73987af7,b617e9c,766331d5,c06e0dfe,4e97536e,67e363c8,add07030,d59061,33a726de,38dec51,e5a20009,61ee0fe1) +,S(c3415e9f,9c823521,bad3090f,f2c48e8c,647e438d,70397e5f,7d7a6f2,b7d7d4b4,8c3bb607,c746147c,d5efbef,7e27d236,c4063116,89794cc0,40c235b4,6af412d4) +,S(f47e44dc,90c8bd2f,150d9dd3,e4922395,14990fa1,f2fc361b,a24f853a,b37e33a0,f4282a26,bf5f358f,2dac4956,8bcc15a0,d7eddeee,b43e6fd1,8f35d5e5,4295ddb4) +,S(3cfa945e,6044ec2a,da895f9,9dc24575,deee57e,df8b1a10,a2d2ece1,9951a812,d0ab59a7,46ac6edc,69307a0d,caa00aa9,e7b73897,b89185fa,9dc3899,ae7e3664) +,S(29df8149,9d83bd0f,8fe61eb8,8f0abe0,96a75e5a,2cb0a0a9,76d8d27b,6533a4ed,7eefc80b,63f927c2,e905a56,62d5ed34,e9bde8ff,4402491f,19215c9d,494e62d4) +,S(eea85a80,566d13bd,da65f553,b2077607,c8b9184,6a5a8096,f57afe93,ada9fff4,b5389645,fd772da0,1cf3b535,6ec40279,cf76d812,11f20a16,71d7ecfc,441d71bb) +,S(e1a69e64,4434db7a,54550d44,adc43682,c128c560,3907615b,9282cf1e,e2b74f82,bd434a85,984a6e64,441134e6,d1f1a3a3,adeab658,c29af8b5,4bc42667,634d20ee) +,S(2d99f69f,a7e9f3f6,6a53eea4,381a4c80,ed17e8e1,4507d7f,4a80ef25,293c5fba,25a6aff9,a23a406e,9e34ad6a,21627acc,96b6ceb0,7896382c,833b8fe4,8e603e0a) +,S(d2482127,1554a14a,7283dc8,1ca70179,42a35cfa,c8659b2b,db78c426,51807cb8,a252027f,3fac319f,c767652e,469421b8,d34decd9,c9042d58,f8aa0ab,70e5ceac) +,S(bd030cbc,5f0de61,ef194eac,38f7443c,d231f75a,a0b568c5,416c5b8b,917545d,ad0833b1,4f1e23a,76c0626e,71129455,60fe62d7,4b3a1b95,69e4887e,c0661e78) +,S(afb44b49,ef00b1e9,59c7a864,19d2a4db,71d9bb0c,4e1746,9550d39c,9a37a161,eee390a9,530a5baa,5350019a,f756185,8038c2ee,7f78c365,dd6f62f0,f0589aa5) +,S(9e584f1b,e06ea4d6,5f8dd96f,206cf4b,88aec6b9,ccf06779,53acf110,99e2165c,6994ce27,46b0eab3,d8608ef9,33b61339,d3e589b,c2acb7bd,aba3af72,b082fd24) +,S(d2265b51,b4f7f4ce,1d0966,e7012f9,76433dfe,444b2351,9505b29c,d520c6df,44414498,df439478,328b330b,6f8c6876,682b6a32,b5d01355,2494286e,a635bf65) +,S(fcd56867,58c797c8,3aa1f1d8,a9ba34a8,8e624aeb,666507fe,706b310f,3342264b,680e0b98,455f0e64,94622fa9,e36f76d4,863a9fdb,a3df93af,c688c45d,fdc5967b) +,S(bb9a0c0c,74db0525,370e8235,9d733aa3,494afc03,fb61a1b,7c2536ed,9903fd91,239929a2,1c0ed586,b9bc8f03,af422994,f81619c6,b2fe637f,a511c07c,d9421603) +,S(2358b078,1989d6b5,666d9158,2fc640b1,13d52e61,ea2147cf,a0a103e1,5ae80db0,73f84f47,a78fe2c6,e4d01d7e,3dc10065,e052401f,64796358,c784d59f,742cc8f5) +,S(ddb3409,ce3184a6,d9d7b4c2,cbdf32ef,ebea1be3,595ba19c,42933882,237dae94,59215423,3760cdf0,513442bd,2fbef84b,9c8c0fb1,ef16fa31,783d4dcc,50f24f24) +,S(f99aaa8d,8d42ce59,86ee8ffe,d4dc8a48,81810a6f,cdfa9519,46f00857,4ab1c3c7,d0f29fd1,32bd65d,7f9820b6,830a3e42,8664f9e3,9cb25729,1adc36db,4e177187) +,S(83042093,46ced0d5,a33f491a,c98aae7d,51f17ea4,1e5722cd,29c47f15,49c183aa,1f680d05,ca015018,664dc767,4de0305c,b09efffe,e7b164c8,c733d2ed,18c15d8f) +,S(339f95d5,5e293243,ec10b3a9,a8dba385,dcaa23a1,bb424bbe,4c894deb,c61cc881,3a4ec84d,67831768,e9ad1d9d,db82f785,52883fd8,b0b63bf8,38495a4c,455d5865) +,S(f94de87d,1123f6b6,97d4536f,7bb6a995,ff158060,b7d72b9b,3c9d74c7,1afb6009,f123cea4,bd4859b7,a852270a,17c76c06,e6050a26,9c035bf3,6c505fc4,1fa81066) +,S(71341ad1,bbebbb4a,17a7701d,30dcbf7e,2a70b901,5f6c5ff9,9fb614c3,d3338fca,6ab14a0,4b4956d4,a687acaf,30d7997e,cf70df2c,b49356fc,5b86453c,5ef6d55) +,S(79b8571c,f52ee30a,4d3632d4,965cc6f4,cc6ea06e,fe1cb7a6,ca2c9c9f,1e835bfd,259c0c7c,b8fa8345,57febdba,2183042e,78f8cfaf,24a41dd8,2eb0ad3c,426683fa) +,S(f3ce9dd9,d879e70e,21737426,dbbb27c9,7af84307,6855bf4d,af141d55,5006e402,302a436c,f8be3a4f,dd70a9d1,8f63cc8b,ecd3ba1,ddc9aa3d,f4285239,61e7553d) +,S(4a66f7b1,52a8cafa,78d0e98c,2f8361b9,a0bbad47,d23b24f,5184477b,e90d6318,7555fd5e,87676154,8f7afd4a,33511daf,b3bc4a72,c9f54d4,92327a2c,c235c02f) +,S(cd3b4037,5cd6da61,ff432898,fc592175,c90fbc31,373489ee,c988f280,3459bba7,b0e86ab2,2f2d8d97,5f68a7ab,3ffa3be,511390df,5b647608,fb07f29d,3155728d) +,S(e96f166b,84d98bc5,41ea8885,f7a48612,227bc907,795782c5,d0e2325d,96cb44a0,18ac220b,8def89a1,e3bcc2f2,7a66c0ab,3ee27c3b,882dc16f,e2571963,92b94310) +,S(87adb3fc,7fe6b07f,795bb7d7,ab8383eb,a3f91564,ce89d267,599657b6,9d793838,68677cee,bc7b3d09,efd0f118,d392d0f,d2d5f2bf,f2b50230,e127acb4,97f5c886) +,S(fdaa2f5c,ad1da3c6,3d241a7a,b4365e63,8eede0f8,229a8187,6cd76a3a,7020c545,5b04616a,a1a84785,c4315dda,da6289bf,e57cb9e2,80c42395,a1b13d69,dfb2ddfa) +,S(7eafa986,1a94fc8f,d263cd4a,1e481e1e,8bc1a385,c4ab748,8715b032,3bcdb52d,d655b395,607bb0b,c9092364,803798d8,17a4bdc9,a30e2844,94be8322,42af9949) +,S(a7fe4656,98538c32,844337ea,bc90fb49,feaf2fcb,8b86da6c,d173bf9a,70e62a53,cfb26d1d,689a9075,55057ab0,d4261e2c,acbf30d6,cdd09f95,9cf1830e,5d635aad) +,S(8bcc3b82,d543f365,478fe32f,dc9860f0,10d51f74,b7b5344b,ca6b868d,3c11264e,6bcc32,f7ff3e48,fa1775da,44b64848,f52329d7,d8bb0a09,338b799d,7452b4af) +,S(faf9aa3e,f1ccd659,2feb2add,a1b58ed8,27511b39,bb981cee,4fbe47db,5cab2917,854b88e3,50a6e9a1,fc7e9e45,f3fdb69c,8111c654,877cd620,fd82ee18,ba2d195b) +,S(77012901,ea269067,4d8b4397,e8976f98,e33b1709,c81624a0,7916da35,af75cb29,b63695d7,cc896358,e119eb98,7dc0f67e,22a70c5b,5e6e2072,e61ef62f,9333bc4) +,S(127602ed,f5f1a04b,813e2b6b,2c27f50f,803b70b7,b9525b7d,902f348e,c03591a4,1f791f7d,2427532,968f7a08,f94df6be,a95b7e6c,5490fa9b,5eb5a3f3,207455ce) +,S(17e82c3a,3731a73b,36501eaa,fde3ee8d,ca9f2573,dae1e4fc,594cee8f,e1d19179,8af04092,79792fe2,273e3f0c,e642f3fe,edf1ffed,59c5826d,a7fb2716,ed365a3e) +,S(3c34e27d,5d5737b7,78e968a7,fe8e4113,f802bd30,1e0367ea,41fc8b5c,14b0dd84,67bfc61f,bbd062ce,6224e883,9092a962,eb3105a3,b6634dcd,d59c61da,906a4ce0) +,S(b7a35907,c7b55a8f,7173a5da,c73e306d,64ee0f6c,cf4f5c76,3416bd4d,4f5359f4,d111c21c,e5a8674e,3a64adbd,4f56b1c3,61350b,44a552fb,19186b8f,95ed534e) +,S(6c7d4836,62c28812,7b904cc8,969ff925,8d9fafae,e88f6d3a,e6e4d771,bf8ee7b2,b593dc47,3c29694f,95b00169,286e3981,afeae4b3,dd7a788d,f32a14fb,6e6a8e7a) +,S(dfc406d7,625719b9,4f709993,c5d15062,4c725ef8,eecb2308,bfeec0b3,6050bf4b,651c0d2a,fb29eab7,d4740b5e,1fc2c069,54ced865,7a6bb980,2de8d873,a451717e) +,S(1631fa0f,4e9fdf51,a2842994,da94f835,52b5bb40,348c706b,54079073,665c6bfa,de202803,9a8d1ba5,b133ff28,6df237d2,a8d73343,a08e7c2c,4560954b,eda5ade7) +,S(fc52b3a8,8f712053,2c7667f9,62146d40,eaa3e924,327d959c,a49b74a7,da11e5f6,7bf890f3,daf8f9d9,49e800de,8a3f5458,5aa46e2f,25535cdc,18170391,e2101daf) +,S(6b7036e5,c5993c6,8a744776,abb0b5b3,31a886b4,93e49d41,bc7d93b2,28566fd8,6ebe771,42a56189,f272b6e8,8f70f356,ed1a3d37,ea2d5e4c,998e47c1,319e2ec7) +,S(b13dc5ee,7333e3a,4b13628f,7ef96ae9,4faee9d7,3a46297f,f1e357f6,e7a57a48,4f30b474,99c66456,33f5b521,9164a890,d8cdb78f,8c7ee211,1bd37fc2,d41bb2bc) +,S(1888e944,6fe20426,d4fd677c,5cf1043b,605e93a2,c8532623,b5951e22,34de08ae,9e1f4fed,d5de001b,378ad1a9,98e31f00,30af9674,c383f287,50172e03,ea0b0f81) +,S(2495b334,9884d5f3,166f8fdb,7ff81f9,9042383d,def7cab6,3c2775ad,226189f8,846850e0,2a1cc051,eea77bbf,d5f44499,673f40c1,190e379a,9b37201c,51a06e9c) +,S(e4c0e483,f21102eb,7dd2a78b,5a605b8c,410bafcd,f2ee4bdd,fcfe1374,ab32d17,b53fd34,568f5edd,a6a3abaa,2d2c3a4a,bbeef0f6,c37bec1b,fcf012c3,dac15b9f) +,S(8f9bfd7a,2e798812,eb927fc2,b0ad5eaf,81a0a30f,a97efbee,a1d6d9ef,19348a5f,1eec535d,1bee520a,13a8410b,6f3824cf,7c9d5a82,8208d2ae,dc89fa13,8bb7c2b3) +,S(1d95c760,f3fc2eff,df51cf0c,4b708538,efb56675,eba3ff8d,4f6abe65,d3f167cd,c2bb1122,5016ffe5,6859da47,4b777703,49e4e655,5da3d9a1,6dacbdd2,d42ab0a0) +,S(9df72bbf,fbcdc42e,adb71d69,1791f073,7f945e8,bfbf4e2c,68c5e3f6,5f6955f,24d47aa0,1626b0ac,ce77827,fd7daff,7424d4c7,1d76ad3e,b66d22b2,6f7d1df9) +,S(2df6ef96,7c0e1a41,2bb48bdb,f2cdf8f4,22829abb,4b7a42e,95bd9cbe,8b7f860f,bace2e70,7915069a,bdecd097,9d133cad,bcfedbaa,7eb9891e,ad3380e,9ca99401) +,S(82bf2a99,624e408,188da19,59599de0,eb947438,fcd45784,9a731a1c,15796028,3b6c668a,e5b8f394,3ee713c,a8fbfde5,8ef0f9fd,84054a9d,b626e274,37e611aa) +,S(60260fff,1f213916,9261edeb,5f1d2d0c,a143ca69,400b2776,21793c11,fbc89d41,bec4bfeb,9d09f8d5,cdb11e3c,f65c6a7f,d4aa87f4,e41d7f4,69717a64,5dcf9945) +,S(6391ed61,80cc46fc,33481faf,d21151f3,cb678e3f,4d667748,1a24a01a,c5e9180,c48495fb,fa5962b,44bfbc4f,491b89a6,a8588fe,91677b19,b872dea8,8e34026c) +,S(20807816,758361aa,d5f62d25,8e3951c6,da518890,26507a7b,c0ceb7f6,a96c141c,8526d53b,61c8427b,eb141d91,7a338fe9,f7789a60,cc1a4a62,15dc754d,a73903a9) +,S(1ecceb4f,8acc649a,dbff29c0,fbc62c11,498c7cb3,7d29ed15,e0ebdf03,e994b67d,1c701af7,a9f3d870,81efb798,2382bed6,5d56b8d3,f050bfdf,6da32b10,a18fd4d4) +,S(be82742,dcafe3ca,f646cf01,80202cb4,7086e0f8,ccea3c11,dcccd9de,aeae688d,236d1fd2,5e05d6ab,8651d88e,33d18a3b,caa66c06,b2b68c50,f6158717,f23b4866) +,S(15252ce2,3f666836,dfb47b60,1e642abf,6e56ff2b,5d58fc97,ab09f29e,1b4ec3a9,9a6b2658,f52dfc45,32e6b482,fe909e47,ba7b4442,3be4d474,a524df32,d6149913) +,S(b0429186,21f94599,f7032b4c,1b53bc6a,9259f142,27922f42,fe774772,4b0ee9e7,e73c32d9,ede68eab,e77d681,dd4cbd9e,a356fdf3,bf066f33,6ff2c367,59bed08b) +,S(5fe0917c,1bca66f6,f34bdf1f,dad88a5,b3dc5f5e,2e0227e6,5f3a79c8,9e66c888,885dfcee,c0947b01,71dbaf65,52c09ab4,c0414f8c,7373ec5,e3080521,401ed575) +,S(455985e6,44d9b679,85980752,a13a3f94,94bf9cc,8e6f10ba,fc7a70c2,27ce272c,47eadad3,15e88a99,9d3f5ce7,792ebc10,2fc68f18,cdfa1df,564601f6,3b26c22b) +,S(3562aca3,2291c1cc,ed072a4a,85fbf82f,6b3299f5,2626154,2b41840a,1c868d4,ab4b2656,11db5875,10c48284,7f72c75e,f822f46c,2e7760e2,a46070eb,5db7100c) +,S(54225801,f82406ae,74416373,7408e94d,d3990eb0,60581c47,b453a859,cf63042c,43834935,a6a7acb4,9c086391,24e80fde,72c0a876,1c13a340,1a4e48a5,d96f6a2e) +,S(3c253039,fc8278b2,8135e21,700af1e4,dad4263e,ddc867cf,a3532499,c2c1e48e,ee01ce7c,f8c56c74,1b4b3593,b0cb90a5,9c59cbe6,9a48235d,1295c1d1,ac657081) +,S(8adead1,d898eca7,9e043f71,aa735a32,763f75f6,f6ba7a70,bc47a284,4b580972,7a370bda,412df63d,5590b3da,80365c71,1267cf8c,e3fdd1bb,91ddb981,d213bcd) +,S(8361e522,34c8d62b,9051a95,8af3f090,4f15ee35,4b69a560,7155ea4f,69548037,ca2c329f,8cb65e8a,eb6488f2,2131c525,29df97bf,c8847c87,47145767,7f82c954) +,S(97d65162,d96feb36,e8f00221,52c92c06,2e97087c,703a9dd7,7241216,2262807,15a260bd,bd591c7b,91bdcbb4,c2684545,4706f039,8fef8de,9173e7fc,a3f31f44) +,S(2609f072,5ec8e5ed,4ec86128,dcee3baf,9e62bc24,aedbaaa1,eed8dd31,c9c07503,d51fcfc2,55398507,7e471397,ece94d92,e26375d2,2bddc0b0,d1b780a9,65caf2c3) +,S(820e8f01,5b4ec57,b884dd3e,6a740d90,68fce86a,a4833636,4b65ba6e,9e272357,bfc87d52,eaf352bb,91f071ab,6e986308,4f197671,298eb614,93152f54,345ce5db) +,S(2116bb76,7a56312e,71ac58f5,ff2a7071,1be5b711,1e9e56d9,aa098af4,b991db0,2ebd4fc2,abd618d7,d1798739,7e9b8eaf,8276086c,2a5066e7,e7525603,fbb4c40d) +,S(e43f029f,97f4370d,65826870,3f29422e,7b81112,61364972,e26892f,77eed56f,4e265fe8,40524def,23b7ed34,54c6618e,ec70d3b1,92ff30d2,1b9b6fc,b1f7164c) +,S(9b4ca445,5cada269,2a77f7e8,9aa3082f,ace4de3,898408eb,6a7d1cb0,8ac15c14,1446e397,d1c3fd20,283f43a8,4679e728,64f05251,b1d3bead,58aca70c,4c0fcc29) +,S(e2abb09,c0566f51,7947f99b,2d6fc4a5,637310b5,5f9d9014,84decbb6,ca22cbfe,f334bda6,a1d9cfa8,2b18f8f7,e90b8426,ae13e20d,8834e061,ab1671fe,4f4665f5) +,S(3579fd9,9cdf0be0,2d7e8587,2e213ed3,3a424650,5608d66f,8f936ae7,72511e28,dd93278e,ce754f69,4b3f09d5,a3c32c99,2c5f2e04,cce0a517,de750a38,ccd88abf) +,S(f307f2fd,d0f6c0b3,12841e5e,1e17b966,47d7ca93,4ba4970f,4f9c0c0c,3ccb071f,4829a5bd,40555e18,bf80ae66,b9742249,32679acc,d0e46003,e079e2e3,d20d248f) +,S(a462e750,9e718317,dbc72b9b,fce5909a,bdd2fc36,d56a525,97d3adc1,fe4b98a1,2f9ee390,13b70415,d2e6de97,7aba55ab,1f7c6f2a,a5a7b541,a7101a62,f2799836) +,S(72ec447a,245624d1,1e1270ea,1c4c060d,c6501f11,70c69f4b,229bb98c,e7c47804,cc32a876,33bb7d62,e660ba5,59cc1804,10a51cc9,dc48a9ae,fc3d8225,35fb8ff2) +,S(b97345ad,2a7eedfe,71ae185,11a3e2d6,c431e628,363e01a5,21622c17,e92b32a3,2ba1cb4a,abe0aaa9,619ea819,4e5c0222,3bea32fa,e4ee3378,ba798472,19670e2f) +,S(f417f269,de287f06,8a74a330,14331059,73ee691d,a7f5aa1c,4e00a100,1e2ccd56,1bb297c9,5b3fb72a,3263f126,719a82a6,b8bf415c,e81f6838,2c8b3a9b,42917584) +,S(cdf7b5d5,f7632248,be1bcc1b,61f56491,e81cfe76,1ee897c6,fcebdf4d,109b9b41,118f3f6,32f18892,d37a2aba,1c8d22f6,587ad56e,eacef09,b9202005,75ab7b11) +,S(3540c9dd,992dc4e4,9aba8fbe,8f28950a,24921b05,e1bae568,2714aa6a,95eebb81,841d9499,7bd92dff,53ee3c86,eafc5f6c,6ffac94d,3161ae86,2182caab,8d5a9268) +,S(70f4ef3,37a5519f,466cfdc7,ae779588,74c9a9a1,9a558550,8fafe6fb,d61decfc,413476f7,78f5d377,31aa712b,5c866100,bcc2a33f,ae1a078e,c5e5cbc8,4b7bac5e) +,S(d08990b7,ad5c205a,e5be2700,63bcf8a7,e13c2a,73dbf321,b0d9cdca,a1648da6,b0aa7acc,f47376c,115b0226,1494162e,d425b496,a2392aa9,254655f2,4db6cf48) +,S(53d6c5ec,f59c49fa,d970611d,b8423bbb,e2f5aecb,d4575877,d903d95e,eb88e455,6a954f77,532c80c4,42f2bf65,8e9d763,617c802f,369f1a62,34927e55,ac549f56) +,S(af7216c1,e51fbfb4,a5ec1e93,717f7375,9f15dbaa,86706ca3,6636f499,416ce194,4ed49771,f858a8c2,bad65a31,b5ea5a53,8841553b,accdced4,8c41e2f6,984470f0) +,S(c560d2be,2f2fa6d7,3f5224e,36acdead,8d91eee3,b99a219,a762adfd,a5e79d07,30d4b54d,a96fcda2,2187a2bc,1562d59e,24ab55ce,f9cbfbb8,570cd89,4d436343) +,S(30b40200,c728bdf7,510db9f7,a8a63792,ff70a9f8,c6262e89,4ef902b7,eb30fb8e,8c98fdbf,29f8c5c1,ce6d7d4d,61a2907,ab57b4ea,a25888b9,e8ce751d,6a19a88c) +,S(d7239e30,814ef236,5193c19e,dc91fcbc,955dab78,45b4f3ac,be994264,2a434e1a,454dc941,4500f4a0,f93f751e,1e2d4c25,8c0f10e1,b4f0c6f,3be39b0d,e5170dc9) +,S(60e511c7,7c8d7496,a3f262f6,376d3958,dfdc4645,73aa303e,a6e6672d,b1c21b36,bf86ea8b,c8a37a4e,c5ee1a60,d6e1888a,ac90530d,eadf40c5,b4f61a38,2ea1e340) +,S(55cd8f35,5a242219,3b64633c,27b1c1fb,7ffeed51,c815e1fb,ebaecfb2,3883d739,b6508643,7e53ad24,1fdf4dac,871e58f3,b5abb87a,d4920057,3c37a1c4,b2bd4b3) +,S(73cdc2fd,468a2d21,72a9e0ea,3d4a04c5,ab5fb13e,2e2ff2e0,8af5d70d,ac9bd41b,682e525d,1263abaf,f070bb47,6f754da9,f6c74d,27f319d8,5d9d2882,6d06fd04) +,S(e5f30676,3c9f620,1c5ada9f,6d01201f,97e36fc7,5bb10a12,4cf69cee,619f07fd,611f0f16,39aaac35,5e311a18,a5dd65ca,70e1a52f,452bed2b,3382ac03,dd50546b) +,S(c2c21323,b156ca78,5e53c41d,10bc235b,8e32e4c8,ee377fc2,42d089a0,d2a27d84,6b9f3faf,64331dda,d25d603c,d8f334c8,5cefcf5d,3bf640ac,96fe3bf7,71ce9cf5) +,S(599ff1d4,806aa8c8,b1440a92,9e2383bc,efd9b16d,899289a4,a335dd06,7e63d9,459cb346,7c387470,1b86aa34,c47b8214,7f48a0d2,7b9098bd,2b53d7d8,93e25316) +,S(f02ddd62,a2456e7,b4a90bca,1cb01e98,8b0a09e,62c90154,42db0f52,b635b006,a5666540,6cad4d01,aab99686,90a7ad3,b1ce936,957c317e,57bdb763,b8867583) +,S(f332b89d,1977afdc,aaf681a5,19bca58d,59852f74,78572346,b688d55a,d55a34b,988afdb1,afa1041a,51ed913d,d780b21,5dc90b8a,b5ae857d,73df2883,50ca78d4) +,S(d6fa4679,4fd4d05b,d6b7bdae,22a970f,8c3c3628,ea0d2656,55c0ebf5,2f1b3a73,d9f47018,86512ff9,9c4691a1,60b62e2,616f5c8a,fa2151cb,fd6dcd80,308ad947) +,S(2a13e09f,5c00016b,d974d62f,2c7c7ce9,e46fb142,e7334f5d,98fa2428,e26b25f7,f300cba4,26636d5f,7f8fcb2,bee6e5dd,b9697cdc,9f9c0636,b02b3fa1,820ca235) +,S(c6480cb1,c1bd41ff,e57cbf57,ea854158,36284048,e79dbb51,44a59027,13d13ba0,fee7ee1b,1652e63d,ad49736,8505b302,f135df7d,4a2f8720,74631646,244cd43b) +,S(559613e,8111c9e8,25fa96,aeaf4b2a,4019ea61,4c67446d,76305484,c9a4d7f7,cee9f4eb,2680a723,9187d407,f39390c6,4fed596c,b40f58a3,aa6c96e5,c8c9c2af) +,S(3a95b2b1,e6d19b19,84dcd59,501c33c2,4ca50b59,d410d99d,b9b6da4d,b1dc85ec,7a802198,a5c61542,81669bc0,640a8f40,6815d25,3bf8090,3ed894be,a1b09c72) +,S(5ae541d,b816522a,5d347339,713e92f5,25a637dc,75c9d9b1,5d834f2d,b265c2,e98a115e,f76b2102,7953f65f,cbbfd29b,a79b43ff,8cd6813a,b65174d2,b03027f7) +,S(88b5506a,72d681bc,d34eb118,3d6a7fc3,77e7f496,6d4c65da,bfa60a18,210ec487,c29638a6,ad91272b,3525ab40,1ebcd1ce,ecebab3e,b506cfec,e7e9df0c,6ee40501) +,S(2942de16,93ce8b88,e7e45657,daf88b2a,2118fb7c,9d2296ed,a1725ec2,f7d408a5,778f43,8b4f088e,fb99cb1,819fe6e7,55b0641,8d8679c9,d684faa5,16970f5) +,S(25c22037,e5a46e15,4adfcc47,480a088f,fb53410c,dea814f8,a4f2c387,df2afd0a,45cfaef5,5c5d3742,3380be3c,c38e6785,c85a90be,7cc92fa7,a390bca6,d3e7b3c1) +,S(b1f78a73,d0b935c9,80cd8ab3,b8237e3a,df5a40bd,5f875f7f,bcb13bfb,cc45839a,466141e5,d465c35e,37f80e57,c4923c84,c3a3ba98,549cc8f2,db6e0dde,915293b2) +,S(39405322,a57a4846,d8b42bb2,58f851cb,5570295d,71ded6cf,b803852a,8b4d8304,c81047d5,92fab5a8,c6139ffe,6887d966,e809d2bb,ee3a10b4,5adc5587,ffb302b3) +,S(7faa8352,31e2c6d5,b331ada,5e175954,6142b131,5347196f,bbc5759b,dd6add0,8779b0c9,2c176b72,5355bfb5,ccf5f739,5fc82ae9,48896a38,1be85e58,c48d154d) +,S(b786c145,61fe4677,54e14766,56d33daf,adddce8,e86e09db,ea93f1a,2ff0c3c0,4c5c35f5,99bb9637,75b4d61c,b30da8ad,a4e83a56,2cf7d2a1,fc22f06a,fff92aa8) +,S(ea4b9a9a,b4b509da,d1e70ebf,6604d624,8b6b63d1,c905720a,648e208a,993ce4fb,f5ab8356,ce28ccb,b6f1368b,344e15dd,372d732a,d8953864,21ce415e,6f0a92c) +,S(c986010d,6339bf7d,2ae3e9a6,c977b9a,a2033a42,14a1e9b3,e700abd5,428e2491,4156f13b,a68ebc80,ad12efe2,d5a0469a,a41adc0b,1dceb765,f651b4b6,b652a85d) +,S(385b81d6,e023164a,3662209f,5d694910,22e84b5b,7034e8ea,346941b9,c04df428,9d12b15e,c1868f27,362662eb,cb3c9bc3,1626ef22,36c2d75c,65e82c75,7ff81a2d) +,S(82196a55,104624b7,6710b4b,864b738d,36ebcfec,55226aca,990474b2,58aa978a,48dbd01c,5af9b1fa,21d88b0f,f6b994a8,38d47755,5ad85171,1a4e3d0e,14ab1914) +,S(dcfbdad8,34d74f6f,9df6c143,abd6dbd4,57f954ef,6323ff77,3a19c367,8d6616c,698d3051,8f8d7ffd,b45f0bde,c32c22e,2cb97acc,a8aa0aa9,c16d8789,a128c3a2) +,S(343b04d9,babecad6,5479cf60,e43350d9,8c56f989,69eeca59,5a5e97d,89c25489,14d00978,2729ea38,a79765af,ce78ff5f,16ada59e,9d275274,e6e0778a,facc25ee) +,S(25af2270,88a6beb,7c76a80c,9e87a412,ed166a71,d65d5722,a082e57f,8ac7bb77,d01d89c7,39508a84,5ea64e03,697c2867,5b14bbc,fb484f94,a530e57a,cf19ead9) +,S(1292823c,1759fa5e,48d0724e,67c93df2,c8ce9fef,9124c5b5,8a477aa4,dd3f5e32,c481025f,ece1bed9,26ecd3e0,3cd7eddc,3cc31836,a12855d1,639a85df,7c69650e) +,S(ed92246a,5a5cb2a2,1618deb0,794ea013,20560b42,273638cf,afc901d6,2ff0bb6e,3675cd33,825e611b,8802c746,9cd82b97,b659e31e,c31ceee6,681a35d6,2d5097c) +,S(8174e89c,9b3666aa,69ed3bda,6c73c572,ff3384e9,60863f87,b7d346bf,afd553df,154d75ce,caff95ba,4b5b7b7e,9ea99dff,499a522b,f0252691,cb6b92bd,98182d0) +,S(54169816,f0440f73,1f14de95,e7ab32b6,702c0183,62794ac9,e55ad632,95c4484d,12e541c3,ee64efd3,b5b8781e,bcc5e273,3544653c,d8b55d51,fa3f887e,d8f42465) +,S(c654d2a5,e31ce452,739f9cfd,b2784d37,eb974a4,fda45fad,13a2aea2,f7fdee9,47c9d2e5,24e7391a,f645ed92,cb04a0e5,6aef0362,9b57d593,edc85b1c,686ca4b1) +,S(59724a3f,eb1a1cd3,7fd6a5c,57c3d055,28fbdf48,5102e709,7ac35912,3f9c2ed0,4df18ea4,c0de4053,6bc5a6be,2df794c3,2db2a9cc,19367473,d88829a5,8603010) +,S(d99168a1,3fbeee0b,6aec4b7b,a0060649,1c4ca151,1cadddb9,2ab3478a,325b1073,c2b0876f,528de53a,f2f695b9,b8225fe0,4660a447,c9e9bd9e,4ba5e52f,beb0ce6) +,S(769c1b07,c26b402a,9a4b60f0,5bb0b318,d20f65b4,e941f525,43a15697,5a8f876b,7880299,413ffe86,94a76f26,9acde007,6e64a753,94701bf0,84324215,debf3d69) +,S(6074f53c,ddf9467e,b9fcc4f7,ea4b18ff,777abd8,895c9eb9,71195a9d,57b1b1ac,de1aa32d,e5488e69,eae8a06c,3c89422,a26f819f,af3cd9cc,762d7fa6,1fda3094) +,S(1f9ea27f,3e9e789b,1cde3fff,94bf8046,f5399046,867a2f37,2da59221,e85c655,c47b5ce1,3d399032,89449260,7db3f7a2,52a74084,20ab1846,fa378674,1e7bf7dc) +,S(c2551e26,5d3978ce,49cee376,ac86a0ec,7ba10804,bd894019,e79f51eb,ec9830c3,23dc4415,10635bfc,2af8f85e,c4cc59de,fed9e9e4,198304f7,e57a1f42,736871b1) +,S(2ca9e4d8,3358ea8a,40bc15b7,8c06169d,ed7a5abd,47147972,45078e94,75264d20,f3324029,9fe328b7,ecd14a11,6fd82bf6,5d925532,85a176a9,b7ec892c,ebb2cd94) +,S(e0db4052,8e3eafca,1f514b26,8b673afd,277c5aa9,e2dbe85a,74478d6a,f2ec1f94,f868c782,40dd75ba,3c6809d2,d62f80f1,ab7fead3,c67a90d4,ecc242c1,687376a7) +,S(d34cd3a5,764d99f3,16a260c,9659955,fda3cd9c,cb305f73,d3aaa61,27d422f3,3f3b80ef,74bec102,2e442e69,8fd4e28e,514d3a9,5011ba18,1299793b,b760da72) +,S(588dadd0,79d2280f,37dbeb16,3e05a70f,48ec61c7,cf3e6455,f3ea312d,4ab2e075,731a5c9d,f803c2fd,b22de461,d51af493,5874b745,4ab8417a,940383b2,9404fa3d) +,S(1731c955,931905d1,ccad3bc6,133f5d9c,9fbb10c5,e800be80,643ed02d,6d7477b1,d7e89b3,1f18ac07,6c7b7380,64d2449d,2d9beac,728ffa8f,6d8a7498,d43baf1d) +,S(e2f990d7,6818c0c6,e73d066d,5644e9e9,29f495ef,507b18cc,594871a5,88e45b46,2ca12307,fb565d0,7c60b6c3,e92c9757,45777970,1733c0ad,2b299a20,8f50925) +,S(b92872d8,a146c0da,ec576be7,35a22896,5242cc61,ba37313d,31b0e5ff,21a2273d,8178dd34,e552e97,fff949e9,b101b44d,35d6b57e,79c0e78d,f91ff3f,2817daf1) +,S(b980d2e,9440c3c3,be5cf393,6eb9634e,923cbde7,bbf2a07a,d7a97287,7d6caf11,51b7d51a,8685bf75,c6d376d,441dcd69,cac67b77,762f6ad6,97a9649b,f3d3719a) +,S(db2f9a64,3c3e3706,e9b5aaa4,5ebf08f2,ee9e967c,205a49c1,7dc76b8f,b20a3a52,d05d486f,ab967e27,b9e8c175,e93df203,4657dd,750ad788,1bcc4897,b80f3d40) +,S(5d5985f8,910be82c,538d70d8,9614ec3f,bc9e1f91,9a19950b,a8fc99d9,203d92b5,3efd77d1,27e43849,a710d1e5,7b18b681,c1acb293,2244fa7a,30f360ea,88565e35) +,S(fac2a758,a063dd8b,46b41933,9c70d2ed,3807bdca,69d3b36e,6369f8b1,23200866,8294340d,6154afa2,d7b730b1,301f5ab,322bdcf8,6fb5676,78fc47cf,b15809e3) +,S(cb92092a,d45ab0b8,559846b0,d02a0c67,a13edc86,7d0cce5d,fcc09e63,d1b6cbde,d2593de2,6371136e,390bd52a,c9811334,e13fddd4,e9b86b1d,942cacf5,4615b287) +,S(d715f4a3,66a4a770,bec8abea,49ab6c7e,99e9473b,73e479c,14011843,9470c844,40aff69e,ec6da9bc,38ec119,df4482ff,3bc2b67d,31284db1,8c757990,de6cd0d6) +,S(77312617,4a395a4e,f7bad07d,f2f38843,22293a17,ebf09f20,4732e7f1,2c418417,602cca39,d6352365,420c5ce2,9d2282cc,5d8919b5,e0669b09,22384b69,7bd48a7f) +,S(a7c4b675,f529ba17,e9f1ef44,c88a8066,66131845,dd98da82,4cb04f24,518a4071,d88edc2a,1eb0cd48,57283e3,f8658d8,2da27a7c,7ec16129,78fbb484,41ef438a) +,S(6362f575,d459b970,db814a7f,9a142605,f26f577c,4357be2d,dc16793,64906f7b,fbc9f007,bd08b6ef,cd49c1f7,1e199fa5,ede01123,5d9b015d,515bf7a9,7192f8c2) +,S(6cfc45f8,515813d5,dacdcbd4,c34c155d,c66b0298,8b8a8702,73a5342a,9249b623,f85c980e,394ea0b6,39bdc5a5,ed185de1,7b1f44cf,b7e51c7b,b9f56b3,496b5ee) +,S(d76c0b83,b8a8a8bc,4e8985da,f6f1535b,9ff3b4fe,e13eed4b,39d6426c,87cec8f6,15ddc103,bc6c4f43,6f7e23a4,78e47166,e3f4156b,5ad2d581,7f3a7ade,7d80dcae) +,S(c4f98324,57330d12,e4e26735,464f24a9,a93dcf75,9194fcd8,a2d12ce2,bce0449,e24a6f24,f9a09aa6,f58f29aa,f4e24a0f,864410cd,80280432,d82cd9a4,ea2ac2b9) +,S(e86da865,238fcc7e,d8c2721f,89c2619f,a2caccd8,5e05a21d,8d23a095,634cc439,85b35268,31871eb4,3323caec,13de6de7,94ddc6e7,5d44835b,7443137d,25ee2194) +,S(a6398918,6969469a,939e774,eabbce54,109b833e,b3dfb566,876cf50e,bdc7613a,c48430c3,fa8c730b,dccb53,451b5a38,888f2e85,1f510ca7,64360c9c,b72b6eb7) +,S(6986ef9e,88c8dae7,ecc8184e,a4c5d131,32a87ad2,ff8367ee,63fa0ecd,b7a2972e,e9acba9a,896e1eb5,bc1d2625,983fd2c4,13f54b47,3056a893,7197f940,92eb4bf0) +,S(47bf849a,e428bdde,ceb221ab,e0ea2fd8,c0a5bd39,175aab96,c2fddc57,e809527d,401a7ba5,1999aead,4dfa6ce0,fbad17e8,e7187e52,806a58a6,7033f653,67aa7c02) +,S(c0c02bb0,92af6a5b,73582a14,89067933,8b31318a,f2d5b142,d58833b4,cf07fbe4,a19dff69,5ead3a33,d8f9f7a6,76b8287f,4bf6b23c,6761b084,12d86508,830f8990) +,S(f2185913,405156ab,22baf16e,644063da,b3ba25ab,f191efee,1ff028e2,f7d175e0,814c64f9,9a4250be,2265cf8e,47c8276b,9e5245cf,16ec98b6,b789dd26,be894f3d) +,S(40d840cd,ac4060f6,de850df7,c37462b9,b4d5892f,a1e74f35,89ab3955,a5d941f8,9892b0dc,ff43a872,6974705e,3a3fd077,c2b91a2c,1c6ee153,7b728359,d3217833) +,S(6f6b79e7,9006fe48,5546c3e8,52a33dd1,cfb63f3,96b44d1b,af1fb112,93271b35,f5aa0beb,50a628b7,b5348817,8344527e,d1ecf0ff,a0766a78,faea2361,e8fde7eb) +,S(4edf60c8,c0488128,29e7bac8,7b03ce49,c8df0f1e,6a3e02e2,ea8ad097,f66163d7,9861393e,fcec430c,c00ccd49,3d4e2d2a,a45e1034,fd9a81e4,b015bd5d,56f16dea) +,S(7ab0c44f,f6a444c4,e0feb1c5,a6650c37,26249caa,38f53e62,b6bf225d,bb1e008,e7f9af86,a2839ce0,80c6ee9b,d86529eb,7a7abaed,aa6aa4e7,207e67e1,a500e5f0) +,S(6a90e50d,3dd2f382,1dde8714,57012b3,5c1103b5,80ee4982,f9bb78d4,2541f8b1,5ccd34c1,8455aa76,4cfe6c9d,61507ca3,cb613bf1,4b9eae3f,8391e1a0,9dec03f1) +,S(289d4d81,cccf40af,a61ef56c,242ef8a2,c9883267,54139e1,dbf018ad,5d251df8,f3fead19,c49de6b4,32869220,7e60408e,dcdfad25,26e6e555,9b022941,592081cc) +,S(a1ee466b,c561919,2e82a316,ac3b7514,b109f442,1c93fbeb,234f5862,d37ade3c,db12d4b7,246e8ee5,55dde2b4,9b5c42e7,408eff9c,a853af00,5a6f6c7,73aff21c) +,S(ab30a856,403a1622,14be9837,2ebaa8cf,8d946074,abfddf2b,8e65e2aa,51554329,7a6b7f22,a6ea52be,8c0c8002,29613020,a247c026,86eaf960,7fc56cb7,d696c56f) +,S(17c5f1b8,8e3e4e08,dab8a5c9,2a679c05,688a7437,df6336e8,7b0d22d5,ed56e5b1,96068e6c,72fa9b39,5993913b,305caf70,3186404e,17131b5a,24e14273,a83dccc9) +,S(a76fbdb4,b99e31ba,c434462a,64c0557,8e961a53,dacb9bd7,68400b8a,823190ec,52e31883,5ac1a3fa,e269a4d3,17bab065,7e890844,c3fe96c6,d04fa015,89c89f14) +,S(17b8bb76,10476261,ebd75e4f,299f1805,5c88f36d,ae65cc57,3c959820,25bb794d,1bd9a52e,585a40d2,67db59c6,b9b1bc59,4b8d5344,29b5d4b0,82bb1c9b,b54e0392) +,S(65aa92ba,8fa51cbb,3954b93d,68cfdfa4,d64d97c0,9e099b1b,d1ba853f,18500b37,a2b12d21,b7a1ffd1,af48e4fc,e80c6fc2,e624783c,7b0cdce7,9f01ae7a,5fc96e14) +,S(3450bcca,5d3b30b0,3743f0d2,6b61ea09,de6ef7cf,eed44b3a,c58641ab,93ad7867,4fca3307,5328a298,d310d447,4806f297,3b09d885,9fa3b949,11817d8,39be66ad) +,S(484bf072,134849c7,695ba73e,5f25a8bf,74a183f2,bfe35ad7,34b53878,a2036be4,a5295452,a0830b3e,3c45043,43fe950b,3289d402,73d3bd49,6c0cd7c9,156a1d6a) +,S(2cdf1bc8,920ec641,63fbc8fc,c72fe5c2,dfcb87ec,15725e04,163e1ac5,1b7ec763,c680476a,28f054a9,ac3a073d,67ceecf1,c8262ed,4bc462bc,44798e88,d0e48e54) +,S(59c3c60,8031a214,d3e6be15,c644265c,b6f3526c,2b37d840,86532c24,52359bf9,2248b28e,922e0468,fd98856a,41ca37ec,2fc7bcf9,a13c7a9d,b4135e28,5cdc1bf5) +,S(4f20493a,a7a1e558,a44e54e3,b81c889c,d45b6384,9b8448d4,20a34a31,c7c451ef,bc1adcc0,d00384e7,93c2433a,a76f1f08,b8b23170,6e285d56,3155dc19,d5aaaf1) +,S(bed9949f,dec9813,5bcf76b4,83117fca,57525221,8a6a52c6,a44f2468,29d48ff3,6ab35499,8cad07f6,250dec89,a0840192,b62777df,6e241a4e,325b0c4c,bdad23ba) +,S(1cf32b2c,46deaef6,fbc892ae,4f05653a,3cb69d0d,821a7125,3c2eed77,4e75f767,caa6268b,b8574b47,840b3626,74f4a479,5a2c6478,c440aa4c,36d88a2d,ebacc8f6) +,S(9d3f7c5e,5e0947cb,d9bef5a2,dc1a3e9e,ac22d6a5,9071d0eb,5f270100,3e66dc96,6ad63f62,22f3a787,9544c330,4c3ff653,2a00a764,5ddce01e,c4ac4933,a5c7aedf) +,S(a5a927a3,f03dcbc,b3d069ec,156a6ba3,474f48cd,4828f54c,a673e10d,265eab6c,b5e360fb,39f65847,b63eb29c,a8b403d3,272d9ef1,8127d3f3,7650d13a,9a5736c4) +,S(48de778b,7bf72a21,871ea379,c02b8887,5da9688,37faeaa9,93e68eea,926db673,d25cb808,7a7c13b,fbe8b1d5,522ac2db,6c44526c,13ca0327,586d6a5c,1aaca91a) +,S(a029831,66e7cb1a,fff65515,48faf447,cee8ab2c,d4937269,db002cbe,b8f1d1ef,ef34ec5,42554e97,1ae8897,b049097e,3430c99,d8e93c50,94377c07,a5619ea) +,S(6ed75b96,c4e976cf,75cf0b72,df83c043,5b15d2c3,f8fb319a,94a4d97f,1394df6f,9b63c36c,1e31becb,e1a5a6e2,1abb99d0,d5c39694,cec00fd0,ffc27b53,309bf6eb) +,S(9372c1d1,6997f792,688a55f0,8f246cb0,f206ce31,d0f416c0,c81cbdd6,8487aa5f,ff97433b,be60eaf6,6f19c182,e8a8bbbf,b24cdcb3,7aabf79d,5dac9f13,9c5033b4) +,S(269c7d84,b42f90b2,f39b4db7,5ea0724f,fa6a1a5,4c66a1bd,a21e4254,6f243bf6,8f6f8b36,e278c6fc,bb0c9918,7325f1b9,75ec3add,69708be1,b4703c5d,53d8f8f5) +,S(62f9cd79,921ec46c,d470a12a,9619ad4e,3bed051c,14f47071,e55f9da7,d103793c,92211db,6c846f43,2867b9ee,3b1ff00a,a08f179c,13aa7475,d64d0731,23a69e4a) +,S(fa432dcd,e7e3b40d,a127d65a,e0955d58,a009d81b,46b48745,c4e24597,4e589a8e,a8dee557,cae7e50,d96555ce,c56255b7,c64d10b7,6639b3bf,8d60aea7,58716f68) +,S(a424d18d,c6861074,40f42e5e,f2bfc95,f4c8f94f,e3adf681,79086502,fd9d80a4,cb30797e,1d973012,d488f54,6033b1b3,d6b50c8a,39badf8,fcc12bfb,3a1c4d0f) +,S(c2a6369e,e2857d2e,fce4ab31,49556c08,3ac6af90,6be418e5,ddc5677c,f2f7ec8d,a7097ae2,9fea3774,11ce3f81,b6b3356c,9a2abb69,986ad81f,46b13f8e,a0e7f0a1) +,S(f5eac1a2,3dc91862,d8022931,3be5c7a5,364c0880,23c0650e,53e22f42,2f91ec98,7dd62b80,3ad606c3,3d8d9bd6,cb397ab8,1cbd688f,8375a405,23bb7315,a3f48c24) +,S(dd94fec9,3bf7ec6f,cfa0ced9,df31a1c,131c39d7,e110b5e7,b988c59c,dd594fec,cb858565,6a32e03e,2eb84732,4bdfe2e6,3601148f,7bc7e56d,22cf9aa4,54f063f4) +,S(d3dfdaab,8f9db2d8,edd3db27,d96be5d0,678e9088,5448222c,5b3171da,38fbd501,6b210554,df32021f,35170e13,42e2da18,72be61e9,481a4b74,ec470ed0,b942025c) +,S(47ff9918,3e452838,f63eb828,a239d95b,62f8a746,8acd0b43,6c149985,2afbe3f9,33b7b8a3,a5b51b15,fadc1f9,baec6b8a,88e0da56,5a54bea6,1b2d728f,eabbdc48) +,S(1e99e92b,aae56401,1cc10c94,dac071a9,27565a17,2d6d4a20,224e1b49,c007660,914b98dd,a92484e5,a43d7de4,7bbf57cb,2346d852,f743fdd9,15dcedca,4393aa21) +,S(e150364e,d95ca778,70fb3367,64f9f417,70bb672f,7e794416,13678dd3,65d84fc8,23ddee4,30d7b565,cf545837,58df61fc,502bf86b,a4cbc1cb,7630a9bf,e339ab85) +,S(44dbe753,dd822ae3,f3ef4939,a1d96a98,33e697f8,c4979191,c64114f6,71e8fbb8,b909bdb1,e6e55571,b03517a3,c2345b49,a5793d21,15fac018,2afbefe9,c726c8d5) +,S(fb843458,725d557d,b1a17ad0,427b4d98,6ba189e5,d6da81a8,81867259,6d9d2858,18f1c9ea,bd8e71cc,72e2dcbc,532b101,c7381475,46c24790,d010a6cc,32d4eb10) +,S(48f43c41,b63bbb05,57f8d71,edc739a9,57baed23,efe9c814,848c54e8,54159144,cb75c418,d5557cfd,b29d4807,3f193343,6eafc209,bdf9685a,72f57b98,6676b193) +,S(5520f175,9be037bd,cb5af2a9,a0bdbcc4,9e1531c0,32fd17a4,66f59b38,6d7de1b7,45b933b4,cf256aa3,c6513a2,8a4eff90,7691a34d,e6cc2e7b,a0b83f8e,a7ed4e52) +,S(83936718,1d7d065d,5c15e246,ce8391a4,bec58c71,7f82419a,ed6547c,63f081a9,4c903568,f5ce9a54,65fa845f,985f24d7,6e66cbe,3ce256fc,1703b4b0,5dd636c3) +,S(66a82c47,e756062f,6586ece,86911ac2,23b84c9a,e53e5307,4e22bfbd,3eae6b30,ab2051c,4c231a40,c6da6ac3,5ee32b9,b7eeea17,222cb1d5,a3abf9b6,512db8ec) +,S(974ce35a,9d5a4ef5,84d252dc,3c365423,1335c52a,e3daefa2,8ff9b75,56bf3b46,8c82a9a9,c49d167,4f3995c2,90ae06ac,fa0a5c14,b1c5f41e,c1e449d9,5effb2c0) +,S(1139c2f3,60cb8a4c,e020bb8c,3d19bce2,1c6802e8,5fb3df33,8fc9ffbc,8fc231b6,59adaa14,7980b3e4,73801b73,9f1e0248,2e9f1e17,65df2ec0,183d9f81,d57723d6) +,S(c1bd3793,3b03d95a,b17c10bf,c5167556,4294e38c,740fa3ef,9b356be6,71883b94,c68bd82f,5e926a59,95a83f1f,1a8c2f4,5966b073,91ee74a5,ae2ed99,106c6dca) +,S(c7778ce7,ce668003,86db9263,cddcf608,aff769e4,b4755858,20554ecc,8407bbf2,92509200,9c57f224,8f9b4e71,532c698c,88b91b77,4a3907ae,1ef30c51,78d807c3) +,S(14aa1177,51626b67,8f32418f,1855cc34,52d4ec13,d2d900d8,8a22432c,70ec022b,6151fdf4,f4372a2f,e803478c,da340678,c18a0a7c,d540b0bb,4168f9da,2edd6d81) +,S(a53443b1,b6065cb6,abb18c84,1f171b3c,7152b34,293a7ad1,2e8599fd,f1398cd6,3a07e076,35254aaf,eb30efea,328e3ffc,3a7d280d,5cb2c8ae,9e70b412,38fe704d) +,S(35c3e3ab,ba4c946b,1eafff67,a9831588,d6631e18,9507eca7,66e22449,f8d294fa,94973920,58f6830a,4e5f413e,4d7d442c,4348328a,5115fdc6,ae5ab638,339e313e) +,S(1aabc2db,5ac6df10,1d097db6,26e3623b,295ae6c9,941e3717,ca849065,512a7fa,2d2781c7,b42ce42f,b40b737e,9100ea24,ecfdf58,fd724244,dd67d84f,4a6cb097) +,S(383103b5,5a4beefc,89dd082e,f0e14211,9bacc5fe,8acde710,7429241e,f16820ca,2000fe09,2b820a28,f065924,7823d6b5,18756044,1856dc46,a9fca8c3,84913398) +,S(2cb0f0b6,35022212,88b8be63,a0ed5d71,bc6b89ca,4b2fb4f8,54123124,49445107,9a9eacc7,cea4c27f,ac63a2be,96884112,435d5f6a,bf4ad519,461017c6,1c87b2a4) +,S(ae63a05f,306e6980,f439afe1,b34c7b3c,1453e110,e477e3a8,d7e9ad3e,fe2af088,6facce76,d579afb1,cded78a4,1dc64c79,b6e90279,d8c5f07b,fc2b0a3c,7243b309) +,S(83207533,78d5c6ee,ae26aee8,4f2a0833,a65a94ef,53a477b3,c8708ac0,35dbc24b,d8306300,4caf8d6,7fdcd09b,267a2d5c,b0e83e97,60fc4d8d,2b07e2bb,c2b387e5) +,S(f38888f9,3337633c,9873d501,8b9fbfc2,ad135870,89f6becb,528ea063,a817af28,8beb556d,8e5342f0,6fe06215,891e6091,f8b940fd,b6eebc08,ded95ea0,99985ecd) +,S(c15cc888,7d13a93e,c2411ea1,6f21e86e,4fabe47a,55a94a6c,aff7681e,d47ba1a4,5bcf5b68,91f3e01a,3754fed3,f70e5052,e33fb62a,753c5f3a,532112e7,f7f58f55) +,S(9b3b0b21,1befb305,b02d035a,1e7cad04,3b8ce04,f9315bd6,5067a73,f2680030,75828df7,eeec11f2,e0dab801,5c75213d,cd93cc84,7e873e19,b7d03eab,e368f989) +,S(2028eed7,50cc8973,c1ca2f28,a30c05c7,73e2a05a,db916825,60d41cea,3b4d0a18,40efea34,b941571e,98a0878f,a13c4efe,1e3410d8,d5166661,115cfd55,53bd0d71) +,S(ca697e9f,8954a7cf,593068b9,1078486b,c8c9a71c,d1f7a890,a0a3e0ec,e48521e0,4268dc9,28ef996b,56af66b4,2f410831,b9f8c6a0,2658d706,975a7624,6468322c) +,S(26c2615c,ebe251a8,a78b00d8,1733aa70,f8f9a97f,85c2fcb5,d9287e34,b786d4d3,ecd17c3b,e1b3232e,bde8f859,39f5158e,f8c340ef,cec4e758,cd54eb7b,df4df77f) +,S(7296b5f2,c4ac6a83,c180cc55,2fc79a84,691c50df,8e56ca8c,731ac368,2d8737f,d4410073,1f98235e,cd75e26f,d251bf8f,f93ee806,52446208,c9643fc1,1b938aaa) +,S(465d9610,c3f6c8e7,366ff5e3,9e1e17be,53ab9eb9,f299be2d,3d9dfe6f,fc601a5e,741cfef2,cd41ca9,4b847ad6,93f29e2c,7e0bef4e,6244dfd9,8e3bcbeb,8c3ae7c) +,S(d3264c87,fe435796,b4d40631,a0a64a34,570e36a9,809d98a3,7ffec23d,9f3775e4,e6fa5190,b66bd281,538787c7,40bdaad6,f37a1eb3,90b19c3d,cbc65b19,9bad22ad) +,S(d925ed62,5551d49d,151dd55b,2cde29d8,fa5dfa21,e9454c17,a834f090,7d744640,e398cb93,1c9fc1ea,d9912968,b335015d,162a04fd,c17049da,80a7ad4a,b3b21451) +,S(2f8339f7,72cc9ff5,c59ace6f,d38a13d8,21562e0,a0d5dc2b,a2c074a4,f697413f,d09d6be4,3678836c,e110e805,68a2fbb0,b87d1657,d568d7d0,e4ba1237,dbb4fa4a) +,S(f954909d,f67de727,72437ad4,d3229c50,8440d23a,9215394d,d09c7232,fa5cd0a3,6656c1b9,8e726305,644994b1,eed6234a,834f74d9,2fe16ec2,c945478e,ec53fafe) +,S(962b9348,d9b64aec,7ae38297,e80a5f80,ee1ea253,6964742,99c5196f,cd58f33e,10601c5,87838f02,5b6735c1,b711141d,ed04ed16,2fdc5bb,4dc593fe,9b804145) +,S(b70436c8,4c6e25a5,34c3c2c3,34f13350,7050b552,72a04032,84a744a8,6336efd7,1b9911f4,72a19b13,e8bad704,ad3fc911,3223a5ae,4939138f,86422536,da1009d2) +,S(8989063e,49605ac9,78705f40,cb916505,615cc465,40c4760c,db302cc7,a9e4c6a,e76c05d8,5b32b5f1,9913472a,be4322e9,ff5149e6,e224da9d,dde930c9,948ce8ca) +,S(9e2c1711,29d01e44,25e4d5a,802f4c5,84eaa0a2,2aa8fe0b,bc0aecc3,15fb00ae,b1385087,ff34618a,d126ce7c,dac3a7f5,1f0fec09,2a7a9601,b65c2786,c733fe81) +,S(3a88a3cc,164795e9,cc668d88,d9ffb6d3,52f90edd,c6b0aa5f,dba493e7,273731f7,95f0b3f8,2fc2c9c8,a25b337e,af92ca08,e17ef68,ae5abcd1,43bbf5ce,d68a1fd) +,S(f5cc5ffc,ad18ea3b,c15ed2e7,d0ee8e9a,141bcd7f,8e6fbe1f,81be287e,e7281725,ec049ad7,6ae3bc58,9a60bb7c,ae79dea4,b512fe66,bd812ec6,91fb3182,27d9a288) +,S(c8af958a,201a29a,bf880688,89c211c6,da59b59f,ffcc02db,9010bd6,65b7ccd7,3755194c,76d6e0a9,3f2424fa,f26a512d,f0605393,ecb5216e,d5f1d452,99b247f0) +,S(16f10ca2,a11493a1,361c80df,40066683,93c02635,78f11f2e,d9153dd5,5d6019d7,b9fbc23c,39323b8f,6fcfbfbc,b1a4b81f,e7eb1bb7,2855f46f,5379c68a,dc4680dc) +,S(1b218d55,81d012c,5e804030,ed342308,29675ecb,e1608d75,1ac1fb26,434744dc,8ac73d03,b888545d,dd8d0321,afe66cdb,e8dd7c6d,f1fbc3d,74f7ca46,c551f357) +,S(d16f309a,820828d5,e2d30f78,2f055428,fc8e7bb9,dcedf9c6,34640c19,7562685c,1f27768f,9a2c63f1,cda9f0f4,132f41ba,dd48a03e,5e387abc,d6c73a51,2d1b23f6) +,S(75c161ae,1b38e93e,b623305,e2566957,a277e515,44c2fe13,e02ccb8c,c72886bf,384bd613,39e4e389,5cddf7e5,b0cce8d5,35c03c29,118624c7,32b75a23,a9befea6) +,S(3327a22a,c4891ceb,2a704a19,e60d04fc,64cbb0d1,dbd5a1c7,bcb2f4b9,8a1d0d2e,1ef62067,d81d66,8a7960ea,a63832eb,5c6fe33d,3ed99147,b7f68eec,cf81551d) +,S(8c9fcb52,65eb08d2,90cc1f6c,9c3d70f0,2b553119,2a0c96c5,f59231ed,370c4036,c986a64d,f3972c00,5618cae9,4a84e74f,2566fe4f,aaa0e2b4,d3f52e9b,4acee6bb) +,S(2465bc60,19bab7e0,72bfeefd,9d974124,19bf47c3,24477dd6,c83ea3a9,e9a3f353,675034ba,c62cc0f3,afbf5027,23127859,280e2b2b,91aab521,9b48a36a,d5bcf77f) +,S(3eaaad77,af1f4e6d,a267bce9,e7095672,c44deecc,a71ec3b8,c86d8774,ad06805f,8a3277fa,6247ad67,e6aab22f,a7e7375c,b95eea16,a055ca8b,32615122,30033ff7) +,S(21734e43,c3ab4ef3,55d36330,adc76a02,7fef2658,92c29ea1,a26972fe,48c3528c,e1d82d68,612ba50f,cba70f17,3cfc17a5,d2d380c8,d62322d9,e6e93d3e,475ea6cd) +,S(d21f30a1,19f86a54,2c7d74ba,20796cbf,dc6644fa,2327392b,3aaf9f9f,ada97235,22d5e9ae,2940960f,6722d2bb,c077d9ac,aa12b5f8,b9a404a,e28224d5,5b604f6b) +,S(41741846,c3c6355b,69e39c98,fcedb9b0,73a5b526,75ef5388,819fbf5f,ce1d00aa,3978dffd,d11f4f53,ae1f5aa6,a04153d1,e9ad984d,8feffa10,9ab57fe3,f58145f7) +,S(cca0a89e,6612524b,a9c318a6,879ef971,d346e29a,4cca7c2b,4619a1a3,1f567163,8ed2e1b3,587cb5cf,37e18db,e9a95ca5,17795137,a8215cd5,293aefd4,92b74dd9) +,S(dd3be3cf,83feca12,c3dbd1af,a9166d2f,138efa4a,297c742b,778da203,dfff3728,b0a156d2,93dc289b,94abc958,f17cbdf5,94f04cba,15b71ed0,197e239,8b56976c) +,S(6c535f98,868e9ec3,31135e87,b80aae3b,a0609455,8d426bdd,3dd400e0,344a3cd2,30bb8bb3,4ccc439f,c343d3cd,ceabc511,3efd9087,d17004fc,37f70d98,94e3c04a) +,S(338bec98,1bbaacd8,8b82a53d,d1ec2ea8,aaf7fcfc,e5f502c0,a13b95b1,d5fd9000,dcda1b41,ca43c103,f0c6b294,5f9e94be,be971eb3,12f18197,b4dc795b,1efe13d3) +,S(b70accd5,24961804,40dbb087,c3c11f33,f27c7e9,886aed80,b89b121e,8d51ada9,6272d04b,3ec35419,be43ecf,72824127,ae4fd87b,640a5a4d,8e8ea05b,5f3f9b6b) +,S(dbf05f1e,b489594a,146de62e,c9423699,a7aa2d19,93919b0a,2a26b53a,a0f1a033,9608352e,a1abfc2f,47c42ed3,8f1d2ee4,bb28ce2,9dc98325,eb137acc,36924d65) +,S(56e94226,a4237f18,c57b5ffc,cfe795cc,b11522e1,fc47d549,6dfad2a7,2f109783,f137a96c,7965b8d3,2eaadbce,d86abe0c,5ee79de0,9b94bf60,8d72a4d8,8cafb542) +,S(c206cc19,28af717e,63e6e7be,781ea37e,d7dd1766,f1553fa3,2acc34ea,c1157bfe,78963ba6,f03b670d,602fdb93,d71dafc5,bb5ec7,b541b9e3,a84d42ac,1a444e29) +,S(8b579cc3,6455002c,372bf1c3,21012fbc,c29e102a,9acb149c,93c651ef,e3dee157,9fa8c521,e2f911c5,3438015f,48134dfd,66fa5acd,8916902,c1aadb94,7ceab096) +,S(3ddca5bc,e582f08f,ded36cce,395f4672,9fbfe7a2,c9aa3479,7fd2e332,352daca8,d77a9165,e281ae70,f1e9a265,630acb60,bcb79f30,9cd926b3,550b6c9b,644e5e6f) +,S(ee7c92d3,d81c3d1c,6384c771,b609de3e,e4979ce8,84df6312,fff84a0a,bd5e662,f131dacc,28415d22,729f2b70,934ef8e,3033b7b,c5a38dd9,1a11e4b1,7843978d) +,S(b9f80abf,ab86d596,521bf2d9,cf500e15,adedade8,b51fd8fe,c1d8de53,f4191222,82548186,bb97991f,1f28a93,804cd0a6,378355b,f7c7ed41,4cd585ed,af0afdab) +,S(1ab67bc1,50fcd943,f0b3419f,e5de2969,fb4428a0,b90981d1,a3893c6,18aeeae9,56fb8eb4,4671006a,805e161e,6e3f2bb6,b0199659,667967cd,3c72ba65,97a2c39c) +,S(17425974,39a91dd3,72c09b53,68d9821b,2acba5bf,a5440118,ddb5c494,fa0a807c,1b73d3d8,5eaad3c9,b37f7451,2a7ddd17,97e10e71,b640a820,181757a7,6cd27cba) +,S(72b080f4,d8e812fe,b485361b,59213f59,cd708e6f,d2f7ee71,27af3d87,971633a9,5b9c6a0f,d7a6f32e,f26a4584,542c15fd,e3504935,baa7b48a,d67848b9,67229f32) +,S(94e5f999,b8927b65,a2f4d318,8e3f2fee,229e42b9,ae3e6b46,9f7ea373,d3f11beb,aa27336,204738d3,b2a306d0,4c6efad0,2ae3a431,1da988f2,c6c1decd,b1fb3c92) +,S(2f59f652,2f045980,833c45cf,ef8f3a37,d65ed946,9b3beaac,2167eff8,142e8a03,9101d9d0,60f1a770,9f604eae,4b4243c4,6096c041,2f861106,dc4b8d1a,d9f9b170) +,S(95a443a7,22a88b70,32194130,a349a952,67b89786,e33938c9,7bef7fdb,d211b54d,3d80619,399c0975,a67ea7b8,9a5f6427,ac454278,42f54932,cbf71711,3ae460e7) +,S(c8430fa0,2ce95d35,96eef42c,69f68502,100a29c3,6ddd1f46,daca1e14,4f382d0c,f26c4baa,4a6d9711,e6de2b92,5042c6e3,cadfe60b,93d4f385,d759ec72,5a32bc21) +,S(67504cc3,ffaedcd0,c5a2704f,af1aa41b,4cf489d3,5c23b6d1,4529f4f5,c49afa03,b3858859,b3749792,4d01676c,d96fc03b,37be7b82,aee57e64,a44ba433,2fd936c0) +,S(d4fd8a19,6a56052f,71e8d51,ee3db667,7465ea90,f8449939,be9d1814,86fdb56f,7fc64dee,2bf744a5,ba155865,fb722e11,f3087010,b566182e,8a39f718,922953dc) +,S(12810eca,7f3087,944d3dd2,69baabcd,491113de,99b36ed0,65520569,484fa7ad,f25d02ff,81ed030f,1e67e002,6680a3df,3f27edd7,76155966,6f043f85,f82c9e4a) +,S(b347e0a4,102d2954,cf3ebf6b,7c0e5f88,6a823cb4,c134897e,3169b081,8bd35f4,78494910,3eaf28be,b2d9732,f88dc9a4,66a8b5fd,cc50a2b2,8b347436,2e05cc93) +,S(54cf5638,7972ba75,48dcd07f,1bcc47eb,5d99c95c,a18de15e,9d2ac40f,1435b27,48558294,410d7ed9,9fb008c2,32cc6ae5,33bffa9f,3f02f0a,76b1f390,9e78e507) +,S(e75c0831,ab9569f6,4844f67c,3650f691,e1f84366,46a28613,653c354a,3107cdb,5e2da0f6,83cf019a,82e44f84,e7bde49c,5d477f34,fcbfc3dc,2a9a2a4e,3ead0a1a) +,S(f9eb5462,7d88b64,5899b177,99257d92,4570b3fa,59fca83e,1e302f38,5c524693,dd187873,9fe69020,970609f8,ca1ff56c,3aeac075,43e6cc5b,bcf75af0,2932d516) +,S(8890093c,5bbdd22d,69c91acc,5608e19b,59d2b105,b75e054c,8e873b88,59865997,1f18b0bd,e9b546a,9a2955ad,492290c1,a041b61c,7b534e1a,bcd4db3,b5148bee) +,S(76b95d04,88c9bb7d,2cb28589,f7f3f9a,e2ae24a8,33adf557,76bc3132,4bb80168,590b3022,81649af7,bb2cb0cf,fa0e90ee,13b97294,1a7a8748,bc9ce53,88744e53) +,S(8b3526c5,afd2a987,f8a04949,bb5695da,8b073b93,d8418878,28f9d49,2a6ff4f7,387954e7,60f2ce55,17213bf9,c987a9e7,f9b9b05e,c4677911,22e7a28e,777ab4f4) +,S(2aaaa06e,244684c2,571785e,a77a76f4,2c4bca21,14fb290,ef66d150,74ff2658,49e3135a,3e8ed289,4dcf61d5,7e4cce47,815ecfe2,5bb1d8a,a5d067a7,924fcab4) +,S(981b94b,be061268,c02db0f1,fbc330b7,351d6229,b645e4d4,3d908be4,241795af,c294f8d0,7b6c70e1,c18ce6da,b3d28c4,d53f3e9d,1f8d9ba9,de18cd39,51c8ff5a) +,S(53721e95,737d09b1,49eb185a,1663999d,23429e88,51ab849,a671c0,d86efa3f,d36f27f1,9f812f8c,c9d770a4,1655ef57,efc1f126,ecfea4ac,dd5bd42b,13e20a54) +,S(9ab129e9,a4d92118,3ebf686d,97286d1c,9608003e,67b026ee,d3d06a8a,e21c0bcf,4683a375,85be92d3,331732b7,8e12f452,480a9569,4c309907,2bbb9427,602ab016) +,S(330ed8c3,3c3647e,7b80f38,f3a1865b,d33abc8,ecd4d0d3,390e8ab1,a20d1c0c,5e3db6e9,33d6d0d9,43cb4202,f9ed135d,ced588f,8742556c,2012989e,768dc770) +,S(66c40c78,6933308b,9397f3c0,3c4263f0,357d0f8,a6a9eb06,725c4c15,c226d57a,697777c7,13cf3b5a,ca964377,fb124f87,81743794,afbb1966,4b126a86,9f90bb6b) +,S(36ce2c7a,9c7f1267,c081c9b4,296a191b,951415eb,12e95b02,e5f5d3da,47809b23,ffad6d8a,e00096f3,878f54df,5cf714b9,6646b5f0,9cce0d06,ea38c558,140223e) +,S(73fefc2e,ca71a95d,2e9d5d0d,464a7408,e38e21e,4450ce8c,51ec9d6d,79b43f52,cd47ba4d,6c2ed98a,c369ccf1,a3d65e05,758f7f70,1ca87a02,5baab12f,5b765a20) +,S(cac2c00,567cd06b,d4e98e04,e911f7b8,7d4fcc19,28a5b58c,635fcb96,bc7c0a8f,fde04dd0,3235a3c8,9b304c97,7889506a,c3ef937a,e1dfce4d,ce29a1cd,dd385090) +,S(f6eb390,2654aad1,1c69c028,2d1449a3,4198e774,3d329c3e,85a8af7d,f6219e1,6403deda,8911378f,7defebc1,ef7a4ee6,20802e76,499993e,15b22e42,3e492d0c) +,S(91a7da19,ecc1a979,7d5dd222,edd74bd5,af76d200,30cc27bc,41304ef4,578d8773,a32b1a26,e77631f9,6a3dfe17,ddf16ef2,f38f4026,4d3fb882,dfb34cf1,5fee2f1f) +,S(90c3942a,7b00bc3f,7b852097,ed4590af,cef6fbe,783fd717,9fdc4955,a4fed12a,aa7d174a,8d9f1292,b8e4d9d0,19dd336e,da305ccc,ab69659f,90011b69,85366751) +,S(717c0973,ee959e19,6d11939c,acabeb83,607f9367,83dbfe6b,911f30a3,c51af3a4,f93d840e,88497801,223992dc,24dc976e,dc0b442f,2f845650,4903c9d8,338c1ce2) +,S(831f9794,c88cc5b2,2d202130,3731371d,a0ab67f3,c080cfa4,d958e6db,aa85d140,46ed65d2,1c8b5565,14e83074,e84267d8,43f47154,8774533d,cc11e6aa,cc655895) +,S(f6c7e9f6,69447c19,843ff209,72c47cf9,5341e0f9,d2d5f14e,c9b17abe,20eacdab,1bc7e847,a9f2824b,5895edf,cde746d4,5ef0fb67,b7db1637,e62d2d40,e046650f) +,S(c0ce97e7,ccec223f,947c0db2,62fe3865,9e06fc77,99a41581,7ec36642,9d5f120a,dc281020,af95986,dfafdfc6,ce122d33,e797197e,c24c09d,7e6f04a1,c5b4793f) +,S(e1eae094,f68fad14,5d905240,e79abdef,562ac20e,d52f3503,db34b2db,d2f72d43,71e4754f,ca9df5ef,b3b86c88,7e3eba7a,33554a83,cd68ebed,f6dcdea7,fecc6486) +,S(acaa286,bfd582c2,c938ee36,23a20db7,5cb6f0d4,95419f33,b0b35f2,59841bad,c3eea949,18a16138,caecb54e,ac03b6c9,282006f7,14d4b024,2bad29e9,aea7f667) +,S(87f6893c,137f60ad,22a5d543,2dc2cb2f,f2e05fac,d643839,38936c2d,206afc82,f6bc3092,1de852d4,4894d318,aaff9243,a9a521e,501ca78f,c7baa82a,3a118ed4) +,S(9df94d49,e9508ab5,63ea1,16cc4502,aeed0bb,7b1db426,c6695da5,8da1f59d,51786cb4,a7caea66,39546ae7,5826dcb2,5abc5a76,402fbe9,b32432cc,aa48adc1) +,S(8a902a14,5ecfe343,503f46c9,71693677,80499405,5df02c32,9446a373,e34777e3,fdfaf856,f777d30c,24ce54f9,d503fc8e,a3cdef47,e292ec5f,f98d6449,a69b3f0a) +,S(ad6128cb,8d9903d4,23558eb2,23fa580d,e31c454c,d3985ac2,6a2854c,733c05c9,9b69c9e1,564c9271,5562d4cb,fc040495,6ca539c9,691e2f02,5f41151,ca6667e2) +,S(c2a2ff9f,6d064f4d,551cae44,f726a8cc,b14c9742,96725620,1d01581e,5375e57b,ae53fd93,fc405150,3d6cf2bd,bb51720e,7555cb24,ef7ee8bb,9ca306a0,59563e65) +,S(56c0fc7d,c2ae8f26,4e132fb9,3a52a634,2b80dd38,f329fe9d,1871e2c1,6529996b,5d012467,cd511264,82399d80,c7f8d248,d9267251,e4d18aef,ccb9bbcf,d854414f) +,S(2131bb4c,5014e751,ef0690bd,562ad29c,d8db4461,9eda575c,9f0f75c2,da4e0f32,7bbb234f,7a83afa6,47599f05,8ab8f5d2,83f1f02a,8f8d5292,15c7cdc4,71f03fef) +,S(e8adbaa7,bb0542a9,9b9f7598,59637f1a,cb734257,13247db4,540b8268,6c14a462,72de1317,fb5ed43d,ec57ebf3,dbec4eca,bef50923,a4743359,72aee4b3,c03190f6) +,S(827e8e05,2a084162,19c8894f,ec6f31c1,a7902792,a1e8cdce,6fa5f732,4b428417,d46d9c86,4fb85e09,81bd40b,b6c3a563,8a74c222,6e7c8299,3ef6b408,4aaff5b1) +,S(3dca9759,338dc06a,8e81218,4c14c82d,3fffc50a,6d0125d1,1af0523e,d14fd699,e6c98f39,b3fd3ca7,66ac5444,be93fe24,3085a38,7b484f28,da571afd,4b8ba210) +,S(3ceb28d2,e59d3692,63432323,c36bd680,f531697d,c33245aa,bc7c5838,52bb8eb1,d2456980,96a36ca1,7301778f,d1661743,f43ed9af,3968e4e7,55ab39a1,6ded2d19) +,S(47ce6c4d,c1dc8371,de44ee1c,828e4b3c,6ba040c6,8dab0802,417ecc31,1e7d79ce,2d6b7bc7,c43e478,6df00ca9,75268b3,39f0ad68,7b0c70c9,ca857913,485419e6) +,S(ad6387f,fde71126,d0784af6,fc8a1ca7,be9bfee6,176245cc,2a18c9c1,c17f156,3233a7a,d7d50605,8cb63aa9,becf9eb2,bf8fa88c,afb2ce9d,a9236736,803dda1a) +,S(8c9c591f,e2afc468,4b64d9c,44b92627,e70babb1,aa9f7f00,6dd5b0ba,4ae794b5,5ed4c9cf,30c7d1f1,ab31e9eb,8d20fd16,77b67152,44db075c,c9de1b49,58607617) +,S(2d601d55,1b07c8bd,10db6a9f,516882b3,1525d819,9df56324,cef2c40c,6753ef7e,d32e4bd8,eeb642a4,38a9493a,87824f86,6731bcf1,6c4b1f42,240f07fe,ff075a2e) +,S(2c67af99,3de02d6c,b9db16fb,7fa99fd3,6e32aff3,69e855b3,581af5bd,cf8f81bc,69fc1c49,501a76b6,e21abdc0,27aca6b,a062dc11,5cfdeebc,b8b98ea,ae35d935) +,S(a904b9b9,a0abbb60,4be04958,74b0bf72,5c1c21fc,1ad75b9f,99ed5d60,d1beaa3c,a2cc5370,58dcb26,6677e730,449cafa1,c0135179,4afa24e5,47b10479,a2590069) +,S(bafa5456,e50152d3,115d40f,b9571d79,7ae8fa48,97801c13,238d8b98,b919302d,8108653b,f4f9bb59,de7e26d8,55f72103,20eeb48e,9f127778,5f05ed34,3e63ac43) +,S(fce057b,560e9da2,7621d46f,bb709275,4a6793fa,eedfcd7f,9a782aae,e35cd9a1,c59891ce,d7c19c5e,7a841620,8d0d819,c8634bbd,2466ed60,214a726a,dbefd784) +,S(b8dd80d9,6b6229fd,d13dac50,23f53657,3b353efa,45016033,58a9bcfc,ab61fe7f,a72beda2,c2cefe01,eaf1ceea,714b538d,8b052eac,3df84158,dd3530f8,9c5bcd25) +,S(c676cd53,7366b97d,9a056e87,2bec47df,e2c35e88,947275b0,53212536,a6231c5,9d5fe80f,763e202e,6919c760,bc96c732,844c4c18,7e7a3a1a,a0760c29,80c076af) +,S(626a28b9,c897481,19d6c4b0,bab6e81b,9d6747cc,6c49521e,6f032f50,36c9fa87,f8373b3c,748231d,b8aaf580,90898566,27b112f5,6697b18b,3cd6a73a,16f2203a) +,S(d72eae41,548363f7,cf5b5b9e,ab73076d,650f7616,e7584fd5,1a5ff2a8,ce1959b0,c867515b,46c9973b,53498582,5bebdc1a,54181a33,74b507e1,f0aa21d0,7a6c0e0f) +,S(6e34489a,4ca57c6b,f3b8a98,dac3b2a2,995a7bd,f90acac3,b81d56d7,678920ea,efe88e16,e60cdf2c,3e6a07a0,ad323e7,ff9e731b,26f9cac7,87bf43f1,b3401169) +,S(a8214f4f,1ab2d150,b66eb9e6,f64ea4f4,6ae08909,9248663d,8491cd2e,90a56ae7,5d252b54,ec694068,a0944f8d,4e0ead5f,2cf0982f,3981240,5165b5a7,e4d628fb) +,S(76591c34,fdb90d13,14c5f85,e76e18c3,aff3aceb,4385011c,d4a74bc,b36f775d,f13c54f9,c32fcde6,94d29b12,f2a8d8fb,a99d19be,80ed9a8f,903b33c1,ae52a447) +,S(17d22fdc,c48ec732,afd7bf62,7fd09d22,d2989437,a8ce1950,3acc99f,ef2c0cf9,3bf80fed,48c4c2f2,f25ccee1,9a717067,976f108e,b1cee625,1fb6257d,f1788384) +,S(2ffd19a7,f46bc028,9b11d7d8,2b391091,e08202cd,95c7a4f6,fc351713,d3645b6a,807ee1d7,af310183,fa9edc73,4c098b90,5b0d4021,6c19b797,1429725a,5763d01b) +,S(63496320,c4eaa16d,ca4ed516,bcbae7cb,124af3ef,10969329,dfec9a92,22f143ab,3ce522ae,ffd08245,d5dd6ce0,17ea6915,aece97b9,9314d61e,2d4e9fbb,5b92406e) +,S(685a372c,ead446b,6f8797c6,dbbdf442,e337a200,1b09c03a,84dd523f,674c1e31,9436ad88,7c61bd79,d0a73f9d,1d06ca0b,37f19511,9d9f9f10,6a6d6d97,f0654d96) +,S(d1aa115d,ebf2ca79,61b1a13a,9f8f2f38,8367280b,c36f6208,15cab71,d235b866,9dffc173,6590390d,a2f3239c,f85ac4c5,3e842aa1,3ba035ac,2824a269,c7c2a487) +,S(d12c58f,5e20c6de,34a27270,d29ed5ce,c2a36ac0,8f2a1b1b,69cdf2a5,d501bc3c,cf7bd006,5bedb875,bef1861a,335457ad,66042a17,c9249006,60ee461c,db0a7de6) +,S(95d9af48,d2d3ad22,4ca83889,2d7f14a0,bb4770d0,42ecca6c,d71b9278,79e69898,39e47d1f,6acf9570,5755c560,22a8faa2,e961b25e,c5ca36e6,3a5ecf5a,e54a48d1) +,S(6e4dac4d,1b6d8404,e84152ad,f5d49de8,d269c9dd,1c66136c,65b8f78,5cffec16,62d1dd58,22c06f57,f2ccefca,31c318d1,175a3c60,2aad99f2,e5c63e2b,c8e33afd) +,S(d026e847,3e8eec4b,d9ffb21d,49c13900,34a9a66d,5423174a,40c8b4b3,4f6fe060,682d65fa,cbb29cc0,91a290e9,305e4615,5a5d472e,16d37c7f,e06a9a40,f578c58d) +,S(683bda54,87643e28,8799658,baee6ec4,73ca9c66,4a76c302,b26f5762,877e766a,d3d80b37,117d6b18,c04174aa,2aba0774,43fbb78a,2649eb38,e5798935,75fa237a) +,S(fe2f54a1,830a0d86,ba30acd7,d208f6c7,22e2deda,4e554a5a,533e8e07,dfc76f8f,6a6f032b,5bb03b33,3b28e6bb,30b2b04,305e0aac,cf53e2b,53f215ac,2f1ab8e4) +,S(2486b52c,511b2499,7ef5d49f,8098f596,568fbd0b,756334b5,d5f01852,191d677,ef82dd7f,9dd36f42,55b75292,40af86da,bbbdeccd,6ebb3197,11fb3432,236f5ccc) +,S(c6fe27cc,79f316ca,5a59acd5,911963e3,bd6ae911,c50bfa1a,f2ae8b6d,69ff8ef,70aa6fed,ba4c57ee,5b3b33ec,ab6efe75,e05b8b43,41c3a7b9,bca5053c,40292101) +,S(be4ff208,8334d043,2d35c9f3,806e516d,fae58bbc,d2f69565,ec461006,99af2be2,49de6b17,c8636af4,25ed00f4,228ecdb0,4208d18e,b5c9c194,d6714550,26396adc) +,S(c5789253,316faa6c,2f87f888,a8fa4312,42618ebb,37a5184e,29abf20a,e6b4aea4,77647e3a,e083159f,afe92726,6c60f666,12e4fd31,ace116a5,11678291,185df958) +,S(27d2c091,b2212a3c,b2a827d0,951a4bea,6994953a,cbfff146,cac83f20,f164ffd7,e9fb5899,d757055d,f0e273ef,c38cf691,e958c6e3,943aae43,85f77a14,8189eb92) +,S(94d45ebc,ce92d10a,3294114d,743db35a,8e2ddb7b,7f5bee72,7d093c89,15d98d45,802386db,bd731bbc,5dc793e4,88fcf20d,15be9024,b94d1d19,edf99ea5,ab1c0acd) +,S(46739c9f,6245639b,692a68bb,8587499c,43184d0c,f0ff467,7b264985,b4a2fb62,9f8f41d7,2eb951ff,17d4517a,3fe7dfed,556aeec6,be518188,8c39a57c,913f7da4) +,S(5c9f57b1,baacb2f,67f1bdb5,3fc85aa9,3293a2d9,d85d1f2f,f62ff40e,d26e8c30,6bcc8a89,7ebf12b7,2f397b5d,ba072f3,c91f413f,df14e441,ae2a2155,887c1527) +,S(9ccb3d34,4c813c15,e9b85517,95ddb04d,c3c790a3,a1ae051b,60202b56,b735f3fe,e24525a9,fa784ee6,2576fa15,6ad350b9,eb5802f6,e5d5bbbc,7f4217e8,73f85916) +,S(f0826eec,162f343e,4cd7e135,df33219b,507876ef,5d29fa13,d8fa2326,3e7ca425,649c1d78,6547e29b,d266f2b3,cb70b823,8e71d198,6791fb09,a19ee4fc,1823e02f) +,S(8b672a2e,1b76096c,80b87519,10b6a09e,1b7204e3,ec139cea,46aabe84,4302dfa7,33f05056,ab88de5,e036f8e8,5bcd51f5,423ef431,4faa1015,7eacfd8a,9db71787) +,S(93cb8f71,9db52708,986fcbc6,26679315,fe79d119,9f8abffc,5c02f375,8e5a50e7,ed7d055d,91843e32,4193d866,81551f5b,36b484d1,f0dd5fec,f59e51ba,57b93bed) +,S(da506843,86019984,4004a723,7ae11605,a7132e86,55145d8f,ce9e0e50,feef7d5d,260f6393,886e1b8a,93986894,a4a98fa2,61771a46,16d04ebe,baf8bc48,29816d53) +,S(2d469c5f,26610e4,6bfe1cc9,43cfe267,e1bd0a22,e451aeb3,e74f17b,b9ec2f76,1d702b5f,fe2101cd,f5a5cdc8,4d4a4890,6720ac6f,ca626979,d942c218,163474a2) +,S(1ea83777,5513ab9,cadd3b53,66cd9049,da1b01c5,d347f7b1,c8a74f6f,658cd621,b9e312c7,2dee3d7e,d6e67596,70c65bca,5bec92a,2eb82fab,a122ffde,5b1c7f5) +,S(70bb25c7,9589530e,f129166f,cac1d5d0,81083ed5,fc5b9b99,e744585a,1d6b6816,63234c19,dad2914,3e99ff95,a906a5f7,a48b3e87,2d45436c,8d987baf,a93b3588) +,S(fd9989c8,f68537ea,a3248b19,ae721256,ba8d0d2f,d825980f,f89bb158,a700f0a0,eb76c5d4,93e52d45,5d27a12f,fcdba859,664bb048,1686e67c,e79e40c2,e0160c97) +,S(f1c32c17,8b36e837,b058e9ab,ed3c65a7,e2c76397,4936a524,838a480d,b3089b7,dd4101fb,92488a78,480cb6af,6cf73dec,5aac619f,3bb42594,c3dbef4e,cc7a3a11) +,S(842d8101,ed396c79,b5b19779,c245dbdb,b1ecba7,9ad77d5d,f78fedce,f9d83f33,6fe77300,9117a404,f4b28ef2,57441145,c5afc6ae,2e655ecd,2497486a,fb1ebc16) +,S(6e9e1a2e,847fb133,cc92a79f,767a5ea8,cac887a3,16903a98,f3ea86ad,3e8cf24f,d71de5dc,12be0a35,b996d880,8442fdfe,4a6cd810,7fe29548,da99783d,9049ce3d) +,S(f6739fd0,1f5429d,27c216fb,2b02d871,7028366f,7af115d1,e154f87a,812b5e37,15186c87,f776c8a0,3532b554,1e1b583a,5a465334,45bcf93e,dcf539b,21663c95) +,S(c3acbd98,43bbbd8c,68f153d8,553a81c2,81dbc584,42edb9d0,e51d056f,2b80e2f2,8397a220,483081ec,ea7de090,5d063412,1dfbcc32,ce70379e,c58b33b3,20f39d25) +,S(aa9c8bec,c845e691,a4c3daaf,a9bf687c,ad733290,32abe151,a26c2a27,1fb94732,b82dd0fb,c53fa616,74b684f9,7da10e8b,77f1f7e7,388007d6,656988df,e18df322) +,S(3df35629,654d5b70,db9547b7,5373efd7,e1781dac,da11607f,e4f3c903,5493c8a7,56fbe92b,25cbc615,96f2cd4,10290da,239ec3d9,360b3561,4199b006,c2d2b54) +,S(bcc8b337,98973e00,771a1955,89165cff,83870f8b,a897b8da,84c1bfb7,57168bb9,483a7f4a,70c8250f,62105842,310ed427,168382a2,5d687290,d54e65c4,c83bb16) +,S(bd2b07e,a84da89e,bf5e39f4,d3f03df8,658061fd,8f78d5cb,99f488a3,e992b870,33ae1dde,77c5b467,f5b3da9b,c71a6557,b0cb3aaf,892d0e76,d9f34b40,1fe60c9d) +,S(2c55718b,87b4facb,8ba9a6d,287a2db7,1b5a03cd,c7d933d4,c6014232,ec35d05a,aab1de0a,d8e3f72a,ae822bdf,f3ff70bc,bc5aa56f,1dcc5352,d1f81614,f3f461dd) +,S(eb837dc3,b2e346c2,aba58441,8c84b20,d144f93f,841180f0,860f7280,a7ee23e,6a342fb8,38a63813,e279d1c6,94b2ed26,79c3dd21,652d5678,5f47aed1,5230137b) +,S(827719c6,9b097381,da18519,ecefa193,9a9eba18,189092f4,89bde77d,33046fb1,e73b0e1d,76e40777,4ee6fbf5,2bafe516,f6d94fa7,c7dd1eee,b3c36f7f,6429dd3c) +,S(2b0da4f1,23095370,1f1b9444,847ab289,5cbfecb5,800f2d7c,16e7a56b,c6988d2,fa8b4d9e,5da41962,68e10a3a,7d69b1ca,7963d4d8,cbb5fc1c,83239212,dbc340fc) +,S(95744eb5,13436e2e,877fb0a5,20d5f1e2,1e1ab59e,15bbaf46,206550f8,d5563134,5fb1c034,62040ef2,28adad1e,83fc2c74,57344c38,7b7ffde0,d247bd35,6b5ed1af) +,S(4c805d94,78793e63,4833e90d,d19822a0,ab7d263b,1ab67654,bb77358c,67b327c8,863cb8f0,dbca3c1e,4011928,e5827619,53df792d,449f8980,bd10cef4,472ab56b) +,S(fac22633,3ffa2cc8,93c434b,46fb0cbb,cdee765f,e848cb50,b1dd6e70,272e0ec3,6a6b07ed,46f9b9c0,86e970b1,d2323fa5,d7d66b0e,3ad3077e,da5766ca,ac5b500e) +,S(b9d6af71,c59f0fed,ca4ecc31,25aaa645,f78d43eb,79c22034,1034de56,f59ec4ef,5509e17e,3d7ea457,c428e10a,7e9d8bd6,69f0fdc1,165169bd,a2101d8f,6f4041b8) +,S(57a68137,90725717,2dcd8c46,3a9b274,69c5ef27,2ae2be60,b10b96e8,e6b47537,9f294700,1c04c06f,2e2e5aca,cf4b9549,265db1f5,74b2328,ef4469b0,1c2bcb98) +,S(58ee2a22,9aba401a,637c4d34,2b1ef408,d9f47c53,6133fcc1,de030d00,25e2f53c,d361b0e5,5ef24c74,94cb9e65,cd273e9e,73da6e50,f452f8b1,7d30eadf,32ceabc0) +,S(b8bae4e3,f2e22824,7ac9cd8c,a68632e5,1bb13d4d,735fabb7,305a8169,31b0e34d,ae8ed4e8,6ac41b60,60ca3423,fe6bffcf,a7f215a4,7076f270,a2d1017b,887b8002) +,S(a81d5e0,f11549c1,373b4c02,a61c8908,dd109f4e,7f0cd250,a667508b,aea3feab,8c6cd790,4cb4bc0f,7e56d928,ac362cdb,f096841b,dee9dd48,429c52e7,23511bc1) +,S(fea67ebf,f8f092fb,dbb3110b,77d6981,efaab5c4,97a23c3,42afe294,64f4746b,1dc947e2,94b0fd7f,952a6d5e,1f5ddc9,5f0acde4,de8db93e,41f37ec8,965d4310) +,S(4db2ae8e,6b8e674f,2d79e1a2,fb7c7d10,921c9280,d8eff92a,df1a2b02,4485bffc,8ed56cea,38c2a96f,b6e0ecf3,22b745cb,35c43313,9b3dd28d,9189a9c8,9f2c055d) +,S(be31fba,3995348a,78303047,24ff117a,48dcf5e8,72356d03,ac059306,fc5ae314,8bf72f6b,70189284,ecf86059,affb4a2b,c7b5f923,d7c34429,6f401490,5a94061d) +,S(e8d716d7,18078be7,78535c20,4f9cb681,e8f49ac1,85baf865,e7b78e19,58ee703,2c79f768,c6d3bb3d,b0923d19,74647e8a,966ce69f,d06a8fe7,5601e51,61f49f85) +,S(20ae34f7,f3497d67,6b0496fa,1a68178a,f4fdd57d,15131ab9,c60bedbb,1a840070,21fc951a,2b29ab95,994cd404,b3da37f7,d21fa790,820339df,84f17b50,3572edce) +,S(73db09f1,3c8e7bb4,48629f24,83d56f1d,a207617f,454edbd9,d51ce59f,b7ae1ec9,1e4e5175,efe03956,cce3f160,791595ad,35bea993,139e8967,f591b973,785dc29f) +,S(14f5ae1f,af20b6af,9953fe91,db5bc192,31f302e6,cc03a265,d661b25f,8f07cb18,b790b1d5,ef6ad5ef,97ddb309,354756e,138f4db3,208a873c,17cff3a3,13a8be2) +,S(94ec6992,d2531cdf,3a1002cd,efb12e68,868be66,b704dc26,3dd422b9,9cc379ec,d2c4e479,ce9e0227,4d9175dd,e2a42639,67ed377e,d0416101,7e41e8bc,8adf7fe3) +,S(f73b5edf,c8d60e77,328279af,ae99381f,4ce0de22,17a4edd6,4c228b4,84816527,86758bc6,f44e1204,68555e71,786f7d50,da7f9c72,665a7475,e77c32e9,dcb13419) +,S(94b51939,45a518e7,c602570f,863902b4,385c8e76,c335cae1,b80e1d0c,e118b573,7c285008,4d398cfa,24c5cc6d,879868f4,d8eb60ad,ffc77214,5debabc1,3f154ea2) +,S(26acc055,2fa90d45,1dd6f2a3,9bd46a9b,99b0f66f,3e6e72c3,16e09e52,f7b265b,997dd988,5d5b15a1,957e1cc0,a45518fb,b0e1f847,d58fe9de,9be95273,cbe61b00) +,S(e8a5ac01,959edc90,2050922f,af51365b,ecebc967,8e57cac,420faa6d,4e2fce98,2c2998c6,fdd97d15,fd0cd228,e9975064,f2576316,6e61a45e,ae532481,9dd96227) +,S(d903bdf2,5ce9e855,60f63d72,512626fc,fee11c4e,304d249e,e13a4b69,60f95019,1027435d,bab3c246,c93897bf,ef7779f8,9a165595,a7c9c536,176fec9,1adb645f) +,S(eea105c6,fc04a751,cdc27e40,25f0d50a,99f8d01e,1110a505,eb95206,922e50fc,b93db8cd,b914dd8f,2b57b907,4df41e9a,d8cca9e9,c3a9754b,e8473446,ef3a6c68) +,S(801b116a,1fc2c2d8,fc8b412f,e9b1b7eb,b19f4e96,16b420ce,16f1b4b8,dad235f0,53def02d,8a6deddc,250de95b,d6a96d27,1c197d36,e137e640,4123501c,15fbaa9d) +,S(db3b8667,1b0dc54,436520fa,c8d437cb,7a4c23b6,bb3c3cbf,dd34b605,af406939,36562d35,a298b620,f7a06498,b1032f4a,33cc3b3,ee185e0e,aa8d3e4f,3ec5c481) +,S(7bb26eab,8b808402,a9fb3fa3,8f0e2f6a,7d82d7c7,eef75cec,43594713,dbc17706,f4f86283,713738b2,a9209e49,f82ae1bd,9bcc908e,b9565546,3a8a5dd,8d0c4b9c) +,S(e89fe301,8d87bdae,20fee2ec,7a3f6cd6,cf932d20,a026e1bb,a82f9c63,a47b97d9,92a7e80e,e7657ad7,c72f471,8f0c28ba,acfc748d,a6090026,1191912d,32b8a498) +,S(c9a10a8b,14d1845d,be6eb6a8,65cd2ce3,a4d0be4d,fad24912,b01fed8f,8bc812da,5852b791,85afd22a,6f9ab486,91340dfc,e9979cf0,bc9732b3,637434a2,25001d71) +,S(257df2e8,cf535cb7,954b52dd,7e3ee08d,24cd4415,d1075dba,8f75bf74,74afad3f,f72d1e76,d4da3c0e,5b41fa82,28639f2a,f4e114c4,94073ddc,3e8fb2aa,f82596b7) +,S(3c64aa9e,548a430f,a815bd54,713ed69d,bc32a0a1,30ae7ca4,1541ff21,a5314886,8377911,7426e30b,dbf84c57,b110cc3f,df5acc95,7313cbc9,86e895d,6e0ec912) +,S(cd509f83,3b65a7f9,671b9239,c5e71323,f183e956,2c2c115b,751046a2,33424ee9,579243a,c7fbd433,81cbd0fc,4330d087,cfe0c41e,449ef82e,12def6d9,8977338f) +,S(27adfe4,febc3501,3ca7a0a0,b5f7e525,3768df69,9d236f63,9ab80782,c2d426f0,97cb4e7b,73b065a2,499a1675,88fc116,be5606f,80619d42,ddb63286,4d20521) +,S(ef5bb148,98dd6802,6f25c363,16a34acc,c6131851,a4efe8af,219fcbf4,13a45207,49f30d08,291e60f0,975c93ec,403d2a14,3742aaeb,3777cb9,87ff38ec,b51967af) +,S(58df8d45,955e923,ec7fc327,f8081ec3,7ca6e261,7fdf7785,6eed51dc,95057034,cdfca79c,92dfe719,2ec63f6f,f93d211b,d7cdcf69,4deac173,d3393fd3,7655a310) +,S(2a4ca8d3,fff5ac47,bce452f0,1587a769,443e66c0,164558b6,301a6a2b,d3d44270,ed73b405,521274a2,3abe589d,c5ecd607,b6467691,f81e3c9e,6a2096db,2ec82366) +,S(84bafd1c,7c6072cd,fce244c4,8ea004ad,9b8e1765,bb8796d9,118d6dcf,a5ac58ce,86ab8db1,7c26c921,604a3c3f,f0155a2d,14bd7274,16f9e319,efb68f50,2b636c00) +,S(c3785528,7cc4e3df,f5e95951,c5bc5964,28705a12,10927e89,890726a2,ce00b657,302ebd13,5643eb43,f0bd14c3,65e385a4,c9a177f,96595054,3c3d96db,69ee8f79) +,S(c3aa3cae,efa22927,56c72aa,53507474,50f7a64e,2df1bf64,333d0402,5dc87d25,c2eed94d,ff9c5cac,248f77bb,9e189302,286db019,64214d71,1a199119,4818ac4e) +,S(443b9c8c,7d8c4f7e,de7168fd,666e79,5da85e6d,6c0f6448,7d349de7,66acff4f,2d4f1d59,b0d1688c,9fff0f2,f80e3434,db72d76e,6ff886f0,63dd63fd,e192b01e) +,S(2afe7ea8,688081e5,ee8a4ae9,87aec6d2,ede5ec3b,ca887bbc,1461709c,bfc6e5d9,5c319df3,2556ffc2,56194b7d,eb46dcee,59208922,1e55c384,2a03ddd8,78add90b) +,S(c62004ce,4d82ee0c,e2edb82c,e0ca98fd,413bbf44,55b96624,a86aa252,c346af6c,b68e4fb6,88f78331,bc2218f7,6a5e43f1,cac49fe3,4c5302cc,d6a3f1a3,258564c9) +,S(f759c4c6,e0bd72a3,9aabbb17,fc62c900,d57e0b5d,ab9ecce6,8f439025,2b17cd1e,6dfaaff8,5f9f3990,ad2c55f1,3c74e3aa,aa0f10b2,90def714,229cd3da,d5d18d98) +,S(b527a6a9,d27d0a1d,5adfc1c1,35308e5b,9a805638,132f61d7,e5e60709,ac3a9d9,e387f69,62ad151a,d920c82a,739af00f,628865ad,4ca42405,efd87e89,55b91042) +,S(ba23656a,6b0a5c57,f9a00d6a,9e6a331e,cf0c8097,3675f41c,7f0172fb,caf4eb02,b48ac68f,1a108de9,18d76d4,95a811c2,b7614aac,3c5f6b18,f28de8cc,7bd4041b) +,S(ad94ec6,fa161fd1,60dea3b3,2c63a480,9f7bbb91,49de206d,8cc61306,dbd18aa8,5240858c,ec88a022,7c1c12a7,862b4d18,e5acb344,5d66bf85,79ff9fc,ba866a4e) +,S(db4cc48a,de1bff28,c8987eb4,d00e1c5c,4c49fa6f,125d213d,bd814c34,cb81adc2,ffd64cf7,3c62c097,a2cb3b84,c62bc32c,54fce797,74479146,7b9a2cb5,d6f821dc) +,S(27d276df,fa39d889,beb0b586,42a0e479,8057760f,c392d052,6751f2bf,d0c75aed,c670b966,575401a2,a790440b,e5e5db56,2914aa74,e8304e3a,407aadc5,c87e12d8) +,S(7663e9d8,e34b525,b336587,eba3678f,488f95f0,6e9a6848,d2e72d35,36b569da,472b1c5a,8ee189f0,7a06addb,e6a8d90c,dcb87039,aa35e896,79143547,34e2786d) +,S(b2c81e15,1c1f8ef6,51ea1810,97eaa280,7e3b4a96,fbe210eb,b49554ad,6e0c0c4f,2d914319,f5ff59f8,c7038de7,dc70d1fd,6c4434ca,e5c0451f,4fa99f46,55bf8e4c) +,S(25131e21,9406fae4,ab96326c,7bf22892,1f18aa90,c52f46fe,b23f90ac,59ca570f,2e8a380c,eed5d51,6c190251,4937aa0b,1f39d3e4,42ab2537,e9e8d9aa,396e4c20) +,S(c2694b68,8e33025c,fb9929a8,c7ccd1c4,7fc5811b,9658ecb0,f41e4558,5d9a0c3e,c443d593,e68abc49,cbaa92b1,757ab2ab,c30671d8,4699a5c4,85a30b05,6b1ca701) +,S(8d528cc5,17a371bd,9605d8f4,e7f030fd,8a82d489,5bfffb9e,fb11f63b,be534fd7,ef2f16fe,64ceab2a,d261bd1b,314ebc65,4b4127b8,643466a8,f367b991,d28795d) +,S(409e69c7,62f856a2,4b6e80aa,baec8496,33b75c21,8943dbd,7280bf,e917b032,7f049016,62884d22,dfbda29a,c8061b7e,b82eb94c,13d7efcb,77531f8f,980babdd) +,S(82c9b4b0,2451fde7,8ae877c,ed9af540,1ec0e258,119e27d4,1938f71d,ebae71c8,212d6e4,99b91fd6,372db1a9,4ce3fd9,c5443b7e,fb37a566,f0c5f54b,fd8edae8) +,S(6c9fd728,37a39ae7,1f0b5852,e5c09d7d,cde66ec,e8f19a2f,2ab46fdd,d17c7f88,430d3385,aac729f3,8ef5ed,922e709a,73df8e70,eb9cdb83,c41e5958,272e8687) +,S(9f012582,e527901f,ad1f350e,1a64702a,ca1f6670,e693bea8,fc3eb0fc,2c090329,a2fa07ae,c15e9457,b2c75e50,56c79a6e,a4c8f431,d3c720f0,60f7b5b6,da6e3dfb) +,S(6a1a49b0,7b2b61d6,474834e9,953aa455,63910582,57db16bb,406b600d,bb01300,d4b93b7f,66ace505,cbbc2326,ddfa2fa4,ebab145c,ffbdc2c0,606e2ffa,9dbb41f4) +,S(981d0638,a585970d,4b12e577,95e76c26,1e10d65c,c5a94f19,43560d10,472fc9c8,f9babd66,3d02e030,1fc0ed0f,75cdaf94,fafc18ad,2cc68db6,d2eff7ca,e6c013bb) +,S(7edb47d2,b6295063,6cdda3d7,169b4296,3616b6dd,79e2e148,609a7722,476e564d,179a20ac,a6deb286,76fd466f,be78c3b6,789d31b4,91622149,4d7d634,d4bba999) +,S(51867800,4bd8a43,8f180cdd,efa17ff6,e16e29e1,e5fc4ef3,f5fb9bd0,bbf14ae3,306cc2c8,60ae5871,8632f94,c9acb7,321ba1f3,5092a0b5,72c33b9a,cd8cc956) +,S(bade1926,d8b0d136,69e056ea,ea46b2c8,2bf0f0f9,a25bc481,8a40ca8e,201500aa,7ac76679,c9807217,ee802773,dde582b5,6f0a4b66,f6e21764,285a6a81,4d584502) +,S(87815e7c,d1e5e88e,eb882b23,955de59a,d63f14fd,457a8d96,4b47a611,b3e40043,ca82343d,8e2eff9e,314ababb,15cd03c9,6fe6660c,421af876,63500d49,ade0d492) +,S(77d815d8,886cbf43,c1b4ce94,253e93ef,767bacc2,42ba7c1f,bfb7ffa5,308d855a,a8bb3f95,37ef9a77,ad7e282f,8ae05ab1,8131e59b,46a67c3c,7e85ca00,a2d3e1fb) +,S(49079a0a,7d9d9add,3a17ea11,747c8baa,d0922a7a,e766e5e3,fb80ae0f,dbb7d6a9,162b4e23,58f03e01,b4e09264,8cd0657c,f2cdcf54,f6c25d5e,fe7330ce,421e0093) +,S(28d61211,c183ccc,2b08168,5a4487d1,57bc0714,d3ffb16e,96bf0768,ff8075da,3da98c13,a2d1b07,2ddda165,3fb68a21,aad4a9fe,b4536dc1,507dd78b,7e51e0a8) +,S(40c34a4b,6196561a,d4591c49,df51b8e0,4106e545,26092e88,72018720,41448080,7a6e7478,3ce7d82f,b83f15e0,b74ae6d4,435593a1,67da6f57,f2e285fa,8c107e4f) +,S(88754e6a,e12e77ec,21e64cc2,bfaee207,f5c882df,80c8b15e,983fb1aa,99037c2d,5ae10e7c,e7464c69,472ce85f,5183a836,6ed5b3a9,1fb1af7b,d5078c76,2512f37c) +,S(8bb7094f,ed62beba,d628ef9a,84a0679d,7e0d2b45,a655fa3b,68d9a6ef,b24c738d,9f5370da,993a3811,48ad4da6,2d7845ce,4cc81766,f26189c,2199ca73,4312d102) +,S(79814e63,7bf2cfc1,cad39a36,b06fcc4,f7e865b3,ab2de1f0,c786f9f6,40dd80ea,aae4d149,f1e44635,e4800962,f533eb38,5f067114,3bfb87ab,9e68e27c,b56df226) +,S(d493573f,93018d4b,8599b54f,848c3afb,816e098b,8e218e2e,6304baf6,c726513,383c7ff9,b3ab6472,ecb80f72,2f70feac,7ff26989,bcb6f984,628d5eb6,a409b9ce) +,S(59599fc8,7d6a7532,5ce8f205,204596bc,4cec40b2,f3eae8ca,4530053d,1b7c731a,7a8d91e5,5d989961,889949a4,bbb71e17,84055697,b396a011,eeb33889,88f0681d) +,S(4a834ec4,da3c9afb,c62fec5e,898aa2c9,5a835e60,ef199346,3e4bf255,e32df096,8736155,ecbec836,e8d51d79,9176a5c6,6672aec9,548aa63,b7f5524f,6b20cf73) +,S(2142b447,f2707c37,45e877dc,8039161f,d222c69b,9236592c,c1b3f56a,d30a975,9a48f0a7,1ebfef1e,615a7d5,a9decf8c,70764133,88b0e57e,da554257,256e41d0) +,S(c3831b8a,eb58dd77,e21762c5,f2e88035,63fa8b1d,f5d1fab6,3d012fbd,27971797,45d1f565,6fb2779d,6ae18e96,4d9bdf61,924a5f6,a7dfbecd,2224f1c5,70292f46) +,S(a09dd11d,b78332da,663acf08,c08b3d45,d0f4740f,5df66e39,d53e6c68,f5654040,649959b5,bfdfe8cd,45fc89b1,ea21cff5,94f6083c,da8f5e5b,94525d22,3e326217) +,S(53b22e4e,8a17b408,6b119087,2b8fbc9d,fc667b40,223ecd99,27cdf4d2,a9dadaa,87eb7fdf,b9c6e23f,e7c6a8d7,fdf94438,88c6c0b7,1a3728ae,ca8e83a9,4403bbe9) +,S(7de0aeb6,d3c264c7,f39b087a,e62dfb5e,aac6fb17,483bf33e,bc34a903,f0ee35a7,43f92307,6a2205a7,f2f0737b,2167863c,43f3cec2,ea3abe32,9bba3792,6f070847) +,S(ef20ed3f,3dd1cd8e,a610f1da,64086eda,a4a646dd,e2bff224,ae72f069,f341b532,c3477d8a,a0acf4a1,e9c83a1b,c890e59a,1b739a82,55d3747c,7403034b,221fd0f3) +,S(ab21ffa5,3c7baab0,4ee74647,5a5e5800,dcae9a10,644c84f3,a108cd41,cb0dda53,3a02b102,dc21cf05,3abfe19a,2747f34a,35ec6347,ccf02880,5aa95119,110cdebb) +,S(ee8aa184,532ceae,2c3a5071,d905c6ba,bd1982cb,391151f0,fa869339,aaa50ad5,157d9d2,be135082,9eaf2c54,94e6f90f,8a82ad35,2d486dc,58068cf1,c96a421e) +,S(6b234cdf,fed7575a,f1cd7ac0,76c4251d,5853718e,2ea07c04,77959afe,5c0af2ce,223c14f3,d3d61375,d104402b,bbd51026,a7644b15,4f4d254,85e9a4a6,e8dc2831) +,S(e04eb879,7821557,a875d846,75496d9e,420ac3c8,6e718e45,347a616d,7616e836,59f33383,8354990f,2189cec8,af523611,df4a1b30,3001c5d,84e1de70,bbc6bf31) +,S(6b8c79d7,791e8423,788627b7,a99be936,145a8eed,178c8bec,affa8a2d,72965da8,28fece24,22f67bd8,a2d6ef6c,66fa51b4,c4abe766,7480b520,4f77bd8b,2e3863a8) +,S(d21a9136,eed220a9,708c09ed,2b22f5f2,87868a1d,493f6ed6,2fe9a455,42a4da0e,efb15959,cc7a2c02,7ab7f5f6,ec8a538e,91df38be,f78de45d,dabcd8ef,9b5394b) +,S(f950b37e,a5bfc8c7,a76ebefb,f228043f,de066a1b,cbb30688,b73e085b,cce1ce6a,6bc569cd,609a15d4,28ce7874,53ab8d02,fcdc9c50,c8c44016,ee2b62a4,16849616) +,S(75a72a76,520a5f29,368338ec,fba063c8,977548f3,7bbe08a2,e1ff5e97,88b9bd3e,41c140e4,5e2be58a,dee8af9b,e64cbe8b,4c82860b,98299d52,3b27737b,3266f978) +,S(4474da75,3d9c68d7,2218648,1ce835ca,4e17220d,533adb53,38a1978f,cdfbc3f9,b5e2d3c4,ae2c8276,5a337068,58e76666,52a81638,27cb6b94,3d2b80cb,36ad3eb2) +,S(e3f3e682,fd38b067,33f3e91d,585f3ef2,aaf90409,ca125479,b93039fe,3437388c,aaef5ad5,4bfddc87,f7c3f969,94564ac2,27e6d5ac,ffd345ab,246ee2d8,60d9242) +,S(140ea6a8,ad374157,396b064,f7a952f5,358ff408,951895ee,b66b6140,d969afc7,2f340af0,b08a67db,e6ea7535,581b763,24308f37,6015de2,a4dbfa64,1990cc78) +,S(257c1e04,2fcceda7,9aac2bb1,1f01bce9,37b445b2,bff51fcb,555395d2,d0c16b89,73b21d9,75bf23f1,aa0efe21,5141bad2,5a235978,e36645d,8503a60,3956cb5c) +,S(c243452d,664d2610,d1acedb6,ab5201b9,cddce44a,b9320806,4824329a,e0b7a550,50607848,db2b53c3,c9ed2b4,8c0adc28,322d2e,1b30c3df,1ab9dc37,64b001ab) +,S(a0ae37a7,3aee0e63,c0ca3e50,e59a660f,c7ced178,10dcab26,7f91ed44,3f265fe,8332d25b,94e0555c,21ebab61,cd209599,bab277d2,fb9660f1,f237c9bb,4327668c) +,S(2e4886d6,678f10a4,460b4ca4,a9964125,413a739c,64b6abb9,c44f675,1b47dca9,96f6bb70,9602de31,1be57a61,966c8227,e5dda6c0,48d3f1d3,bb5bafa,fc1e8de) +,S(855b47b,efa7dee6,2452a602,980258cc,de813ae4,6f580a46,d5d18432,5b423f8c,c27f5513,114fea7f,b5a405d2,3f25915d,775ac895,7fdc6e74,2d968bbd,d86c181f) +,S(dd360b5,38fe460f,9d6ba828,e2ac7973,46dc386,de05022f,fe48e891,a115e2ac,8fbc127c,d9e79454,a0be9bb9,cf25b7e2,71a02e7f,b4c68653,b7e08329,747cd68f) +,S(993aa6eb,47f7713e,e6c5bea8,1a65af55,1036d0eb,3df8a6f7,e091c16d,3f424c7,8478497f,a774307,d47ed357,35462f3f,a680eb87,fda11107,275c1883,e859ee34) +,S(4828feb,5e3abb4a,2df7b990,e20e1214,804ef219,4cb8acf6,cb8e85b7,5696d961,d8d2636a,6b664b15,f371ab5d,3ff31279,f7c42678,ff380ab0,9c220b02,6ecf3e3f) +,S(fb8dbd84,64bbb5b1,904a2610,2321e59d,ee9debbd,afdbe585,3e1af139,f8fc472a,54e088c0,91a67284,6c81d136,85b4edb4,391e5121,3c4d35e8,47863018,62844742) +,S(851fc8f,cf378f62,5edb262d,e0360d2c,2e9566c6,93abea8a,aec29e5c,275ecc9f,dbe6025,e9857155,83b38b7,a167adff,da70398e,85223a6d,9b940fa8,30a9d9e3) +,S(4d5d07c4,23079d83,d001c000,8c76fb6a,e03bb39e,325d5794,b5414b,46c6777c,d1c1e970,5b80fef0,8450f941,1b8d6197,21f1491e,87745c26,9458d58c,18d14678) +,S(8164d5c9,cc72b489,b6a54e8b,d9cfa9f,8496cd34,3a227a74,f37948f9,5d35340d,90b51776,9e8c68c9,20f5e948,755b4da3,2b294bd3,eae81da6,e0c0b287,ce229bf7) +,S(19a42dbb,9ff7a400,aa821c88,807281ee,ff7df5c6,e047cc51,9693c191,6145e00,369e9596,903c35a8,bb38f4d2,a74da3c3,cd2246d6,b0391e98,e01d6101,439a1018) +,S(7299deb0,a9b7bb52,76c8acae,37ba0ee7,bbfc9c0,7845765c,136cd762,7e7b7f09,8a0a1d16,f00e3090,cfd9cae,d8aed291,ebc56d97,66a131a1,4fdcf6c9,ce6b49bf) +,S(12c7adbf,112e7718,a3ec6774,1597d6e9,87763a10,4350650,e6a804fa,507ecf6c,b3eced7f,bc3073f2,5f86d647,6d822b8e,eeeab7f2,1b3263be,615517e8,ef3b1a64) +,S(b2d76aba,c1af6c6a,b4840b34,db1125c8,d5baf2be,90dc7e6a,52edbf79,a4b0ccf6,92938b87,641e0886,3f22f5ef,e27f9b18,80210d14,312ea6a1,eacaca0e,58c0f73f) +,S(818baa52,8251c326,bf545ff3,a56596b,58e0b525,64ae359a,c1d5199,4b52106a,9f8b4753,a7b56b26,e0e79b9e,9ae5f30a,64d0ce27,5898bf3,c0f12e43,faa69681) +,S(e08080aa,21c9b6ba,da128bad,62dddd3e,e8e5afc1,c460b997,45eab92b,b1663a80,2b878efb,95a19892,8b3609a1,cdbbc1ad,d1530a38,b528539a,c52fff24,2c382dcc) +,S(7a8486a4,1d774bb1,32321247,f7b87964,1b18a621,508f5376,99b31798,490f3e8,ee8c3085,5c3fa5c1,6f90b68c,89944aa8,dcb824c8,c37a0feb,ac796403,15ee96a0) +,S(f74e74f1,6e67c1f3,f5952373,e53cb85e,650a6dae,2302f495,f6882b9d,e2b71cea,1b15103c,8566d2ee,cf5c59c,dab607e4,97075e30,1443b75,42620545,2d1d2bd6) +,S(3ab4150c,cdfd6793,5f8718f0,dc961126,114a5d82,e1791c2d,48740425,8e8ba004,2a6c6f1b,9b546945,f9d327b1,437b1c58,30a48eef,6a14887a,9d8ca24c,c6bc446d) +,S(effec0ed,bb80580a,7bdf80cb,38cf775a,43b786e4,2c80d73e,7a254216,ed7ab7c9,c7c27bd9,57c996e0,42ceda33,2d0b5972,91ae5725,6df77e82,48bb1716,127c582b) +,S(5f49ea21,4d80f735,fd5f51d0,55d4e2f0,fbf0f804,a8f32615,340bd6c1,65a059e1,72e0f032,83684df0,98e740df,bcc39d61,6956ea36,275ca5ae,dbcbd682,510496ca) +,S(2327c29a,7f906,4cbfd900,43d28618,5c555bb9,bb64bab1,c3e20417,4cee2591,db1711bd,ac9095db,39e3d2f7,d2b84129,41dc6f8d,116115eb,7bfe710c,13b9e41b) +,S(12f2f427,64f77c84,f8a97c5d,7743bd8c,cd0c6132,5cd10fd1,76975a97,b447991,713e4660,cadecd98,3b0e4986,855c2c67,bc220334,2991a18c,bb0c08f6,8282b246) +,S(cdd3eb6c,b496141c,b99bde6e,a66b2c78,7264e6c8,74efdda,5ffc53e4,b3292a08,d1270d9,7cd63394,b385739f,4da31dc0,80f410a6,aee164fb,8147b7f6,9f2f1cc9) +,S(2ad4df2,1ff316e7,ce29d76c,da40cb7e,31aa7e14,49fce23b,93f0d096,e1b74159,972c77aa,160c98d6,c64dd9d1,4a0cedad,875e09b5,b0d5378a,7311cd2d,a7d641a5) +,S(df0ed61,d6c8f906,f970b9af,b4080368,2f12127b,e318575f,8e2111bd,514c33a8,e9e67c4d,7b1b0fb6,fa0a4640,62d72f,28180e6,bc8bef1d,60a1ea79,2c4f259) +,S(9572faff,160577b1,83aca01e,45d61647,7dfe147f,a6ae0b26,ca0264aa,23d2870f,b7de270f,572e1503,8a4a9b3b,706eb7c0,2a6e5e56,ab0f73ce,9f4b7926,a79c5c5) +,S(52583f1f,1d51acb9,bd25b63e,158f66b9,8e021432,427efd5b,bd629594,ba951264,3267373a,94719487,344c9af6,1eb4b726,a595bfd3,41d78b33,f3db8897,7e53427f) +,S(c95cd125,7e59d04a,6d953d7d,f68089c0,c29b6a28,90ff264f,74b469fc,af7cfa58,f5c7c7fd,82d3fd51,4031ce72,657cffd2,da83d072,9b0c4d6c,66180214,9e6ac90b) +,S(67a190df,e1a2bf4e,cc23972d,24d1f13b,803a9b4e,5c1f3cec,55d2c2c,3b0b2487,5c35f795,c29a2f07,74ded776,72ab5a7d,45902f50,e76a229a,212d580e,5edeb8c9) +,S(7ffcb588,ee9192c4,3bc5bd3b,4d0c8211,28c3f2a0,62ec3b0d,77752ada,69f468e3,9204301a,fd0ecd0d,26f49878,47c42c70,e526f7d2,e69a97f9,2aa1c357,d4140a8a) +,S(7d693f1e,6e35e062,83f35910,3c1cdd8a,5279c4ad,ed0edb63,d6c574ea,322e91d6,a9042da9,ff323c4d,46b2b40a,1d44970c,92e3cb32,ddd8dc21,d32d609,c5611888) +,S(7e0e5dae,8ce3c883,49a9ee95,fb357694,a7bd48d0,f27c2eba,49896e4a,3da04fab,a397f8ac,dd4077f9,58d7b5fe,28582bcd,4c46d1ea,fa253db5,18fe90c7,61dbaa4d) +,S(715ea4a8,e5eca76e,48605688,560872d6,7a499a8f,e84f715e,a91d724d,71fc0f06,9a7b3eda,8b82dcb4,60ba3515,18c22f59,fade0636,dc893050,a51530d,77ef8501) +,S(52a9c85c,ca99e4ca,50f5efb0,118fa604,b7e789b6,b589a5d6,368bab11,44c8dbda,3796e4e7,87dda75a,59c7503a,f27732d,8c973414,c1ea0a90,fb6cede8,a5066fa8) +,S(58f1c65d,ee5ea200,57644796,a94e4a42,771c827,fe032116,657e990,b56e42e,7f12725d,388a22e3,4f191714,9f48f0a3,3d537c8a,1037247c,de0594ea,a11de507) +,S(3ce165f6,5f569bb5,e594110e,ef91f30b,7e330586,e86cc4c8,347d1ab0,f4a8eda1,90480327,7376335,72d92c00,10313f96,9a0c5d18,c852597d,1f88020d,f69987bd) +,S(133bb4fb,1a16b008,80379e4d,ca980355,c747683,cbd7e81d,87a9eb8f,4219a314,30e76cea,f57f8b17,de6d31e,447fb926,b1678651,e980338e,8f8327d2,648ba25c) +,S(cb26bb35,4cf94461,c675838f,653c3844,24ce543f,32ed7b5,ec1b6b33,a2f7670a,d6f9be8c,7f3781ea,fc765264,c9e84f2f,6d2ff771,25b8d381,199b67c3,763c4f2e) +,S(7d944d40,58cb78ac,739b85e,6dd3be5,6b08fd56,767fd02b,1d5bba08,d4639f1a,79716d18,44baabcb,aded3600,ffa68989,45920de9,1f2cbc83,e6326ed4,e37c3fc0) +,S(c4577e42,ad4014,e1fbc0a4,d69fe37f,49937d1c,19a95765,74f2437a,d06e6589,4a76bf04,18a4a8b,af103581,b6c2cd16,727fccaf,12a3998d,52b1a095,c5388570) +,S(c71f462,9196f35a,cefd651b,3f6f2b30,485e35f3,305dc774,21535961,60a17a0f,9ff53452,52ccf823,b83d47a1,a6a70f84,b9c83142,db0136cf,d03b1f24,6ec57590) +,S(35acf6e1,cad058b6,7543eb60,ef4d896d,c21c4b5e,292c3885,d9e42cf3,85bd22be,cce4ea31,c9a37f8f,21e32a5f,6f272699,7cddc6fd,8ab11c0d,12c3ca4b,fe6b0e92) +,S(c2872bec,f2abb3fe,8a258b3c,51a2bf82,6712ec12,587cb33c,a6765a4f,e3a3e448,a739cf63,63cdf69c,15c9827a,f759e0fa,f7d92112,9bce9e18,bad86c0a,9f6bf467) +,S(50fa12eb,c3dfb3b,dbb61a16,a8b0cc6b,6e6882ae,f245360c,e5524143,27d2bfe3,873afcc9,5ed9a3e5,2f1b0515,d901e9c9,3677d49,4e64a478,11ec59c4,c3374393) +,S(3ff1688d,65cc134e,c6f27fa2,eba78abb,2cf22eeb,57163e2a,150ed7aa,17b3640b,ec8f8f66,11d8f7ed,a296e690,63b3393e,58d8bf85,90630da7,3dc2c0ec,e9e05870) +,S(91a113c6,c53b57dd,60b2eb66,e6bf7ab0,e721dac1,acd71d76,2c278b31,f2fc2701,beb6c79a,2fdba188,4f98ec5b,fd97626a,131f5c16,d4466a84,a9d77213,96220b17) +,S(732b5521,e6cb7053,ebfec5a9,c5b35f47,8da4a9b4,c4bc5708,42b0e8b1,242bad45,30afe970,fa640757,bff0e825,93a102b2,e1b619db,7f7d68f1,17f27543,e72f57c) +,S(94831570,f0146aaa,fd2a7ee5,11f992c,6e2999a4,b22cb176,dd516dbe,e0e16e5f,eb275132,385efd06,35baab61,5367697b,8bef0c93,14fac991,4a2c04e4,aaf785df) +,S(dadb60a9,3af95d47,cebb540a,1c0aecf2,839f1be3,e264a294,bbd03317,686726b0,8b4bb755,46efb999,cddcb88a,ad5b28dd,b73ae1c6,48c810bc,cd9d0f03,c200e27d) +,S(148ebf0f,7e23a220,52ca69ee,57237eca,6c1caf1f,faeb8718,b9b8dade,4b97be1e,32f68bfb,464ae6a2,b23ac7dc,3910a87b,89ff7042,e863ed3d,3038d75a,f3689f4e) +,S(ec532193,614ca440,33a8275,49c65e0a,af446a21,6705f9e0,68e448e8,6e882bd4,30277366,58dd1673,d9f701ab,50dc3238,f073a297,d579cb8e,2ee51dc8,bcd44b36) +,S(bcb65ed5,b16fe248,bddda875,28eab71f,d9357099,af71ddd2,9a8471e9,b2b26e99,ed827c35,e6e13dff,ade8b9c9,d2c620e6,576db5a7,1b1e22a0,3f943f96,cb7282c5) +,S(de30301f,18df0e9c,15d709a1,35c30473,28cd3356,95922ae0,f283290a,638d516e,89398261,37dbf197,53012e47,6846f6d5,45a48312,bd6f4c65,255b89f4,a1b9df99) +,S(2eb56a7d,b051e9a5,8a7c12a3,a92509d9,5da4956e,8b820006,aef166fa,92873644,30867ff9,7ef8d955,d497abfb,463838aa,c946d487,d702956f,b75ebf89,e25bddc2) +,S(88f839f6,d7f06f85,82ed781d,12062442,66d4f9a1,e2007a89,f660fd24,ebb30d5f,29ce1d8a,376a587e,96fae37b,521a8903,8f0bffd8,9f9934d4,eada0750,32faba00) +,S(112cb93a,8db33b09,641aef75,c3f9a399,b1808314,64e8a855,16e309c4,99ca9a16,66b1a1cc,f2255c2c,119bd0ed,a75dfe73,fe2e9d9f,3aa9a6d7,43a4b7aa,9d76bccc) +,S(407dbfb5,53b43e95,639aa6c8,d6868ef3,2f41f1ea,70cd4aa5,9d76ea2c,83b567d7,69fdde78,a4971ce0,b05a57a3,8891691c,d1fb371f,b1322a6f,97849c64,bd7b57ed) +,S(16fb5fd1,fb41d76d,ffc130d8,9549465f,7976a3b1,ecd6a4d,9e5d55e6,b2a6a40d,3d39a810,95904ac4,9559aa89,8fa087b9,b84ed72b,55f17fa3,aef8b32a,10e942c4) +,S(e40302f0,6c102ca6,e6be1370,2aa6e860,5e71fd37,6c87e2d0,c0ebbf49,6fcb31c1,d32d294e,1e80cd,94925538,2156db9f,2bea101a,f652309b,8943cdf,b82d75a3) +,S(8f2cfdc0,eadf8b00,f74305c7,892a4326,9cd31300,d716d652,92a7a36f,7f1b3b9d,6d0e726,838fd161,32d19538,147248a9,5d76c7c0,beadf1d0,b553e4b6,b7d1a2d1) +,S(c7a636f1,1a9cf793,4385daa0,5c3f3abc,8c3a1589,7be0b49d,ae789fbc,e07674cb,4f715f0e,e52c638c,954b8b3a,84baac76,6a891889,db0b7f82,6bdf81e8,4040cfbe) +,S(245aed6e,5fc309bd,980d5597,9380e8c5,79dcc887,a2758e13,24ed3a1,833dc820,a7980b85,c6ce770b,13a888ff,22a1356e,4136eaf2,49238c38,dd103016,f0d0b1da) +,S(91ea17c1,d90556a3,722dffa5,631286b7,f9db0f01,e411ac16,f5203c4,1a8a824d,281023e9,88fdcec1,94af265e,c407910c,54d25f20,69d64b13,c5261f0,c157a3c5) +,S(729db97d,35fdb72c,a354c65,a0e7f76,7bff9ade,964b1d57,a98f9bf0,39a74eb1,59596a57,7c888b01,6e00143c,63f0be1b,d86d95a1,ef59e075,fe7e377,1472bfc4) +,S(f8cb711d,f74a4961,1391871e,d8de6a63,89679130,b587beef,f4eebcbb,9daaae9b,4c52c310,c6f960c1,53f386ed,6842c4d5,bc1b4c99,433567b0,fbc40e93,91a5e022) +,S(1700d0a,32624d75,258d80a6,2228b6ec,91e66949,a463f3b5,16dffe43,b9fa08fd,a007bbb6,b687b509,f450908e,b617f176,92918f18,be3113d3,8a80ebe4,4ee750b5) +,S(9f2f3ee7,70a2c422,b9b92eb8,a52570ab,b5354154,57f0efec,fcd9141f,62894132,c977d79f,ef198135,b69aa3c3,77a7f251,3274911a,73736b2e,c543a721,17908588) +,S(86f75064,8a2b829a,91acae01,4beb20f9,aa9ee46e,8e513678,c4fbd7c3,2ac36e6e,1768035e,bd1fd375,7caacc22,495a5902,a3c616e6,49eeebe7,4377b996,30192b28) +,S(1d4207d7,adcbd969,d432bc74,35f2fb75,b35adbe3,fa1f45ab,bf3ee074,b8269f68,11d80191,accff888,928a0835,562186c7,30367e90,91d310d8,2a712a9b,13d70f99) +,S(f782bf74,2e55fd7c,ab249b2,1570d9e8,cfc12f46,5330d2e6,57eff609,7c3d19b6,896e9e48,a60ace9,84d07ebf,12449980,9d96639d,101b3405,59eed877,908394f) +,S(22bde2e7,e3c0d268,f556c478,82e50f2b,634c4235,9067cbd5,a0daed53,1a166a7f,5e03a94a,5baf0f92,7f4beaf7,6b7e9649,aa578eb3,f105515c,ed670c3e,e1fa3e72) +,S(85ef9f8e,b54cc4f3,aeed4769,85529945,7eb4f3bb,1e5849a2,aec7b257,a8a9a074,8a0245d2,f0c8eaf2,84a01114,d29c65f1,c146af0c,dedfb5ff,9e3be650,e2acaab5) +,S(3bd6c026,63620684,4d2e1a89,2378ec64,177145d6,24062d23,ce9c2d08,4f34ca81,7f0e5053,87fade82,8596f440,3687b04f,4a2693ee,25361442,1d9712e4,b3e0c179) +,S(364f9a2d,641173de,4a0cd185,972f6d78,75b06722,3cececc2,9bfb8aed,7ccd3311,39e7616,8d64795b,504de9d5,3049dcb3,a95fb3c9,affa2d5e,8e8cb35b,6cf71219) +,S(8833f933,f4049c7,98493b,e5f764bc,47dae8fa,baf420d8,44c7436c,eec27898,bf615e47,1b0f1029,1afb2839,2c874f83,ae55b15a,58e61be4,12b7c7a6,13868279) +,S(d4fa5129,4835f9dc,49cc3ad,b9401f61,97654f43,ec88f206,9a63852d,6abb4a6,f24535f9,9efed47e,80d66925,8924e56a,44530eba,a2aff32b,70f36551,1db59521) +,S(50eb8613,99f79883,58b9de58,88c47cd7,bd4547c3,96e1280f,2806efd0,d0715b89,dff64137,f74442a4,dd0c25ff,ea968845,f9e14ff4,d3e32010,f1c64ca7,6afb1be) +,S(f8608ede,92c5a1e0,5f61c065,606ae232,b0f81161,bcec88bf,b4f2b14e,22165cca,e583acc4,7ee133b4,8c3c1eb6,5d6f4891,b53c84ec,7bb352ec,3b6e41ff,aa6a2089) +,S(2c00145a,60e21169,98f23b9f,6f83223a,6ceafa12,c1b92844,2a17bd63,582a4fd,ed76c7cf,c65a881c,5dd270bc,7be9ce49,c2990e43,425ccb7a,eebe64e7,eff423d) +,S(b42d4c38,79f7aeec,96f212f,1d5bd79f,bdeb9850,ae44af81,b4b3467a,85c29c49,7bdbbcb6,1eb04e41,9ea8f9ba,dd5289e0,d718158b,63ef0599,55e3c5de,41f833f1) +,S(a5831ace,8c19fd68,85e9b9cf,34b9fa4a,9e80eb3c,36e256cc,44cbe521,6efb7128,e10dc489,3e0fa1ca,f2dee476,5b8ab518,c3778f4c,583abe27,af293869,c765955a) +,S(9fccb821,59ed46e9,18a407b4,967e1154,4566afbd,df6c0639,d5cced94,50e56b17,c430e829,438188ed,a443cb2c,aa7d6939,3004ed75,5ff61e57,8dbe9e64,251615ee) +,S(c1efe41f,44186467,cc0def62,eb0f0ac9,22b8bad8,e2347849,28b465e7,1789fcc5,5d73f89f,709d54d4,e167806f,a07ef07a,7fd1a7aa,dab9896d,33c290f1,dca74872) +,S(805c0816,43fffffa,c9055f2a,996fc2f,fa02b4ab,4fa440f4,8af5878b,4eadb694,b0a0dacf,d9b3b9d0,51ac59e2,b367a8f2,eaae274a,ee2b05d9,18cc0042,eb02b6c5) +,S(d5da1b82,9b477768,bec17829,1c9abc82,b595bab8,6240e705,715de420,fefe498,dba833fd,df1915b8,fb43b39d,dbbd635,8f12d4c4,31b547c4,af07a6fd,75fde785) +,S(73a2441b,1f1b9ddb,844caf0f,8cb0a91b,5adc6556,6f883026,8ceeca8f,7c6c5cae,512e46ba,b47e9eaf,64cfce27,24566bf9,ab43e32a,bb0e098a,5dac9687,11927437) +,S(dbff750d,bed18702,eacea6d8,b54540e6,ae60ea05,85d77db8,7a63a050,806037f4,1f349895,29ce56b8,bba91938,16e842fc,f74b56e1,568857d8,2f193e02,207f84db) +,S(57c73f7d,57c7882c,5d574f70,fa225552,ab3d7741,260bfd3c,b616d6be,9698dbb9,f633edba,f7f6cc7a,9d8ff9eb,75bced20,f865fc9e,81245e77,32db2ed3,876e42f) +,S(6663e27a,7760f187,a1f67823,64044da8,d4cb82ef,e296f5ca,90c5d81a,49b7cc5,4df0d12,4ca9c8db,4885f8d5,18cbf06d,f5b437e,242cf86b,b11df29c,5c83937c) +,S(83f688c5,88ee13f4,a9e72023,5169dbd7,ebbe0c68,4cb61827,3e2b75bf,5fc581d3,f76e519a,151ba05f,df5f11e6,f3cfbcdc,4b00776b,14c4598c,51783088,2b2bc6c5) +,S(2114e129,1f65c339,d1bd4373,6c677937,4385b3a5,5dafa8e4,a0515134,6b650389,88103399,1b682f7e,9ba02fd3,e104932f,802ed44e,b996beff,7fa9523e,c9c2a9d4) +,S(a68b163c,db6c8aae,8ddeda39,7d8031a1,394ddc36,48168f13,47774ad1,d569233a,eba470d3,4ae15c94,57ba1b3e,cf5f3d96,682b8095,f83eba7b,f319feee,6740d3e1) +,S(a44b2a55,baa92a2b,cdd2f06f,36874d69,40416b79,20bb6daa,2bd9edaf,c12bbaf6,d06941d8,28f1092d,3667ce3d,59d23159,54d6cbcf,c0da31d5,1663a6bb,ccedbb47) +,S(5630eea0,8fee5570,c094ded,917746e9,18535cb8,b0286bf6,f6cc294d,5714fd33,91f60b64,3457dd9a,b2a02ff,99f931e,b38b4d87,204de10,d8a30cea,ce576b62) +,S(cc8b173b,d3112432,cd0c741a,8620fc26,ac5c5d89,e05c18c3,13067fe0,a41b4b00,8cdcccfe,b4ca500c,34dc3087,58f424f8,52517974,9a33e27e,8859515a,2f7c0926) +,S(abe16c8b,e0c99bf4,d396e70d,af00af89,b5d95f5a,3ba33570,892d086c,4b807943,14b2bcfe,8cec39c5,826fad38,9b8f8788,51e1595b,b25b1a87,149d8627,1218e9b) +,S(62411146,ca2da616,b557f0af,ccc2cb75,d278b07a,4dacd864,7e449ddd,cf29c09b,d081b63d,67ff0c07,5f00a3c7,2771c581,87804c1e,4ec5bf68,faffbd21,4c71e15f) +,S(5952a672,a50af560,66de08ad,be4f5cc2,38f3e4ee,84160b1d,14534c4e,2b50ed52,f7d7e2e3,e9612005,a0344e93,454f112c,792f825,f3d3044a,28d9af6b,798da761) +,S(128fd5b5,420f68be,3742def,c987da3e,bca771d7,af1cf8e3,12d6b3ba,df162492,afe5edc4,80c1c8b9,5580dd8c,aef14338,c09fbf6a,a1c951d3,57ca4df2,9d3f183) +,S(6315678a,13cd71e4,6e283287,24499671,6adedf31,38841718,cf5168f,60d4bf8d,ff691d8d,c5f97f7a,f2ae319b,dc590518,6ec3bd36,7f492d0a,95cda47,9492ad3b) +,S(50277159,4169f70a,ab39095d,d8027324,e212f78b,c3b0865a,a57f1574,dba1bdb1,e31fb30a,58eca0da,aea93548,bdeb9f32,325d6c30,69b3d936,d610bbc0,de3b771f) +,S(51eac8f8,8cdd7ac2,46375960,c6b7d9fa,a89352a5,4024003b,cada1e6f,df389721,6d4c8750,95d6ccfd,9d8a5830,c4672c71,d414ffe7,cff2cc15,b7b5d169,48e872d9) +,S(bd39330,76c0f542,a0590ad6,398b2418,92b899ab,75ba0065,b1ace928,b8cc0ecc,12f4bfc4,57e97c3c,9d4a21c2,ad106ee8,8cb5bfc9,92d151ad,589e90f7,d65b5d8) +,S(bd0eb600,5ae967e,81214a1,889f7dc5,d1e765a1,af74a93,f5292758,539d6dd6,2e532797,9e6a22a2,63025e57,4805e4c2,6d62b33c,5269d35b,bdeee7f6,4a2cfc3) +,S(b7b068b6,8ed18d80,67a0c971,a06cb3f0,5c96ad7f,371dd849,aec3a2f9,bbe23eb6,89871d76,d3e6b605,9997345d,4246087a,a3fa61c7,dd75f5c7,b2ed1e20,59a00350) +,S(10cf2c38,643a08b,dd842c4b,79efa2c0,3b5dcd1b,c708b253,ef71fba7,f0e51f48,e37d14c,934524d2,dc562f6f,997227e1,44094184,e8342995,d5562000,34e71f4f) +,S(5936b571,20b6dc97,ee242ba2,deaafad4,5ab83795,9f1e7390,54a0c026,df7bb342,86bab74f,b9d4f776,e82831c6,3a409237,d5923c7e,7b3e0b18,12ae203b,64fc750) +,S(67bb9ab0,bc48fafa,3ca66493,995ea995,e36cdfb7,152d64ed,ddc56aa2,6c315896,cfa9a456,dc2c82b7,f87f4216,e365f5e7,97223d2a,820a127c,a5ee4ee9,281b71b5) +,S(5580326,678b44ad,fb313c23,dd8a7dbd,c6869fd,70cc58b6,d20f8b92,ecfd81b5,6d343687,f3dc733b,55dac794,c895e5d5,9032613,563f2d72,ee94afe4,c2a932d9) +,S(fe285767,53d2466d,d056d2bb,b7141cf7,e52c2b96,d0ef3c8,52c728a7,d2dd8126,f344b3ad,c8077e92,24c6c5a9,c9f40a1a,d8721351,6b8691ab,c5fa9f73,726a6c98) +,S(12f25359,638fe4f3,97a17f99,c233b2ea,7a794d71,e677b206,aa32f251,8cef615,dd0d68e5,324e4325,ebd45766,441677de,40c15792,19d65c6e,9c63f15a,91519c20) +,S(f1e45fa7,21bf7fb4,45fe3eed,29c26bb8,bf1ea59e,de7c1a6b,e652663d,233c3e9d,6d145f2a,c3a041a9,83b9865e,66775342,a0f8a435,de19626d,ca8b9328,797e20eb) +,S(6b6ab442,d2623235,dcc7e3e4,85d00ade,7f219576,509c528e,6a3417a4,fefd328d,5d627367,ed25278a,6d3505c8,e13970ba,253a26a3,c605d11f,1eb345c5,56584e95) +,S(83e10be5,d21bbef2,ed1ceb33,c4e558ce,bee76405,257d9573,51f5d90e,f219552,41f5e174,c6e9b283,2a39c2fc,4d4fc573,634a8975,e143243d,14dc4be3,434a4380) +,S(2ba25948,9571ce20,f792129d,f43caf4d,ddd93b76,e4c8cd63,7a9de68f,f8d615c5,9469a49a,d1a814d4,2f460325,b7d58f2b,eeb85183,a370cc3,69a685e6,a87f6e61) +,S(3fac1508,b669f540,77a840ca,3ddeec5b,b70c03b0,ff93a77d,f2f73df,2728f36b,dfbac9d5,ceb21745,a6d27c3f,d6fd96b,af708066,a3bf765a,208b7231,d5fb6955) +,S(8996dc02,9b79d0f9,d57b8c8,549fc01c,2e1c1c7c,2ce2cd70,7a0bba0a,b3420ee,d08fcc05,5194c4bb,2d17ad54,2f9ec734,f2a9ab8b,a7dd0467,c5d916aa,10f7b8b1) +,S(16af9433,c9baf15d,921f931e,f530a477,b04b2e03,e1e8cd05,42e20628,df7c3241,65863e2a,acae34b5,a1dd53e0,5efd423b,165235cc,69195973,2d0f64e,c5b56fac) +,S(d82af39e,eda70e3c,f28e2f6b,b8009131,59ada2d3,854e81a9,3798e402,43d1d3c,fe03de8a,326e8bd,b202b5b9,929c2f3d,4bc8abea,a2377cb2,29cd4630,d1dc1c50) +,S(a7ad92e4,7a442903,c2f91033,7e2ebc98,14bfc943,f2747eda,666bc813,c04382ef,ffba6b43,9379448b,56b20163,44439b9e,8d6da764,72f67a27,9c7f45a,359234c1) +,S(fe9b9274,7b27f7b0,6419dd38,53e61762,de7a5116,48008802,681bf521,21a1ff92,f32bec26,b9d24872,1a47c65b,48426130,b8e72e68,d231bd4d,82c5d4c2,bde605a9) +,S(8e22ed5b,e5d80940,970de428,5ca609af,872b8a4f,b8c93942,fa1e62d8,588ac5f5,6a461962,b74b0f93,afa72284,4837ec30,b99a65da,d688c161,cfa42028,2ed97167) +,S(bd3bb0ec,bfdcf06a,62f0046a,989b45e9,c76f4d9e,b3225495,eff70685,71518fb2,5592029f,3eed8e35,727865f1,dd2a6de5,e19e6eb5,f92e520,529fe97a,c296820a) +,S(50a9dbb0,b08a48b8,76dc65fe,6e2efd92,d44053ed,73b8c279,5b688e00,a1fd1455,4a1fae82,51581f9e,32fe8f67,5675ef70,dd7c2ec6,2a42eb3d,e1aa13e6,717df147) +,S(6c1dc44e,67b52cf5,a4564ce1,7a9da713,c718833a,6d527e7d,ebc6b1f2,c893693,89cb4508,681d58ad,77900c1c,aeb25c59,b307c9f6,c84f1329,35e32af1,e39f8f67) +,S(3ffc35e2,4d4fd0e6,ffe4d1c9,b5097d10,c12dc7c0,4356cea3,b5fad66b,5f3333c0,89718e69,78748c6d,e99d181f,137eb74,6e28b05f,27af7726,e86c5f5c,daa1eaea) +,S(fe2261bd,b40fc492,99be54de,1d0bf22a,634aec85,77b5e9e1,51626d67,3f4214eb,896bf33a,5c9b7537,b621a168,f3da1bd7,dbc94816,922350fb,38925c96,5e57c175) +,S(129b6e5,dba2fe86,749274a4,f1ec5999,b2fc682,933a1120,cb8e16bf,4f4131e8,24dce651,b0fe4770,6c78c217,f0d5ed4d,4bda4e7e,4e7ea6b9,ca59e751,b018b5bc) +,S(c2d1b280,79d91abe,1a44c4bb,96ab548,90323054,94075907,37b39ea5,e1fedd72,6974b9f3,80edabdf,e7961bbd,71ba1971,ded6ba24,90466d1d,a5715d9f,92472637) +,S(d8dc5f38,76f6049d,a9a5c9e9,396d287f,911e06cb,ac0148e8,26c8de93,15bd464c,701d0a43,ab41f321,b8f86ea1,b1f51d0e,c22a3399,c1d8534a,433ebc2,38ec248e) +,S(2b1a41d7,ebb9756a,b62a96fa,57909ad1,a747dad4,b3730da9,a12dac8e,3ee4c414,8ebb1512,3f43224a,c2cb4ba7,c90ed765,5470a968,454d0c81,1ebe3384,5c6ca968) +,S(724fe9d6,e7f75b52,7da19421,3c984e31,f63ac3f7,1b5819bc,344ea339,567bc179,a1db688d,ba1d85fb,20da08f,802b5195,163d179c,7bc60f70,4ade1f26,4e1552fc) +,S(835b30f2,8e54b834,5ce95569,7d71b7a5,7cd4b5b3,17ee3fce,57722454,7695c9bf,f431ded9,b7ba9477,ff041f83,22685e4a,8c88c91e,15a14f21,6b16e302,e243cd11) +,S(f5015d22,16cfe581,e650e579,c49b6ccf,b5ca4dc6,42561256,21df7fc8,19047ee6,45b099ab,febbb493,5356872c,40c06825,47aaef2a,72e80d27,521b65ea,e2d4918a) +,S(df0b1b57,f3a0dde8,b136a92f,bf953a83,694244bf,99782ea,98050bcb,3313f826,6f7c8326,bf5f6e15,538a1f31,273adea,6908e5db,7f38f395,49284822,12124d5) +,S(a5a24cab,96c04bdd,6e5180a5,8fe27f9e,51897bcc,3a0aa9d8,de06de65,53d2890d,4f6dfa5a,a6b44550,105fbf39,7dce3a4c,e0b42362,4a97d979,eb31324f,f1215525) +,S(849c2e79,422d1dce,4b568aa4,c80eba4d,d8acd237,71f6dff7,40849bd3,ecdb7e41,a465929c,a3ab167a,dcc4fcb1,7ef9102c,35d8937c,fa900ef1,8022918a,6ebfe79e) +,S(a9a98d5d,d8f8ce0e,ddcd90ae,671c52b7,1b09b75d,b4185d4a,1ff12c71,70b499a0,a4eb522e,3aad7f1,6aec7758,c9528bb0,c07184c9,e3c965f7,afefe128,bd285c5c) +,S(8d60786e,1ec1fbe9,61e056c2,d6a57c5d,813d7f20,bc8c68ac,eea0020a,2c4e57af,3a39b0f1,6cc346c7,fc695f45,91b557b6,3c1f8500,89d742b1,60c39744,90e55720) +,S(4710166e,65b9a2d,8de5b80c,192303ed,25cbb80c,b91c57a0,b31a38c5,5c218fd1,b4cd95bc,fa48a016,658382af,37ada302,26887fc9,8200f77c,2b8819a9,ef489206) +,S(e2183510,40815901,c78a304,e6f0d545,5a116560,a91d84fc,157f061,5ed6c0de,6c557bb5,358454d,419c5cb5,1bcb85e7,db3b04ba,d18e1a63,a75687c9,8fd6d973) +,S(807cf529,8730d656,e68b4aa6,5f8d8692,ce246c2c,4e668c69,b5e93e8c,d26b289c,b22a6fc3,d7ffdf57,f18e7447,38cb1bb0,aafb7b39,101d3259,d31574f9,fff644e2) +,S(568f659d,f19f14e1,5d91f3d6,bd2a4690,b274901e,cf6fc684,dd0d8615,8dfbdd85,28eaf8bc,1db68471,4ffbfbce,30977c7,606bc591,dfb8ec02,e43a31c2,ecafbde7) +,S(651dacbc,564fdedf,f4e5e3b6,f2dd366,37c69140,7fa923a8,ce88ed1c,a65e6b86,88dfdb8e,6f117a1e,b99ba423,89ed2db6,cd5608e7,8be2c896,334ffb8d,2e19aa79) +,S(5a17c956,8214ae71,80322b0e,574529c4,dd7d951c,872809c6,b33ece6d,8cd7cd0d,ba4b0cdc,7d7e6ff9,5a97e4c2,6af714ae,f00c9142,5e562ffa,16942fe2,852a3e06) +,S(6d564251,5edd2987,568dc237,b5b98fa8,de9b86a3,e7176e62,f2609dfd,65810920,a99a0d1a,12534f64,fa97227,825b094a,22fc16bb,e8777669,d5aedc4d,58e81ff0) +,S(e3687c75,c0689381,22f60fc5,b346e8dd,17b2dff9,cd7fbea,94aa54b7,ba1f6de9,4dfd4537,1f62a968,399cfaba,87a0b985,cb333510,dfca666d,395e0f8e,2b7f473e) +,S(1a3d974b,49e7e68e,bb2851c9,c9f22d9a,5929ccde,9d635e8d,61aff93f,f7244472,f6c9e7e2,98815ef8,d53a7bd7,9f10275e,ea74ccd8,5d97cc3d,cf2e97b1,83c97c86) +,S(8e0dba73,12c4afe,8e1bc508,4d9f4944,654a5693,81a64158,d7db7007,3f897cb3,560eb34a,2451905f,2e9daeaa,63dff50f,761bd68a,2321a714,45930de0,4801875) +,S(35d9eaef,6c4ded5c,371a655f,2fdbac45,35471c32,b369955b,f8f30d95,21cd5817,c5a084ba,7b2ea4c2,e3c3d081,dab53c9a,ee40e10f,607ce88c,899f0e3f,5850d75a) +,S(44d1dd16,c42b7811,f78374d8,2ec9983,7fbee7ce,4b3a8ccf,81ae4ed6,3b7d2c73,6a7bd35c,b10c4e89,60e3eba1,8f658510,3be8d1a8,5c4932f9,f1e48d81,e3f66604) +,S(4b7a54a5,e7153a54,e4400ef,9311d6,cc16f641,6695fd92,5fcae9ed,6b2738d5,43a7bf76,ac2f663e,daf0036d,b665291f,cf983c0,791a8a6d,a5ee1956,2dc18850) +,S(add4a15d,d1a350e9,e5df62b5,4a6638d,76ee221a,4007a19e,db5bc93e,87f11ea6,c26d6fcb,3a8bc1fc,f1474c26,31a4216d,740cf94b,a9d7e48c,8f01be28,885ee849) +,S(9b52aafa,119ada49,ce33340d,ff75ac50,ac9a9d87,775e8e45,da58452f,228a23c2,67640253,503b5e3f,89f7ad07,b9379bfe,77c946d,4f61b83d,3fced91b,69e850af) +,S(965916f5,89854944,26c1efb7,88079a55,f8109cb3,8bb111b8,42ef7f00,2d4a45d6,9aa2ef4e,61902148,aea7769c,a561d04b,cbe1b340,a231388,e332c30b,8488e531) +,S(3af7890d,c045aaa4,2f7a8f26,5fcff9ea,41bae0f9,87c75c22,3ae3311d,e09b6ffc,dbb31417,27adbd5d,e34202b7,f7a880a2,41f18b0f,b1a40a25,376c4a30,ec607152) +,S(dc465cfe,c07f839b,46406f4d,9a0078d7,57c2fe0,e62aa4e2,212ed33d,5b2026a,1ef12aff,a98868bf,b51901f7,2693f868,5ba2248d,91440380,53bd137b,24b3bc8) +,S(5ae92f8a,cb9e2ca7,2f4d4c70,f4daaf96,bd237bab,2a83c0de,693d8207,baaa0f4b,54e266f7,aa1b7f22,8f24ab91,5e3f0a92,3220512a,de239da3,1069bbf2,35d63049) +,S(38134bb,c6c5a4b3,63421af6,ae573ce7,20421d36,1f4966c7,b9e7f125,f49659ac,5d42678c,d6f89848,72399321,fa86318f,7a4e3f05,4c3bc6f4,6782be35,4ac4aa16) +,S(f2b807a1,3fd603b4,111ac02d,5c646f6e,eb819a4f,7893992b,9132aa83,76892a57,6f1beea1,a315c000,3c957d85,bc5171d0,ccb8ff06,446f4748,738a3c78,f9ec2d4b) +,S(debe05cb,b308a6da,a1eb55bd,73c3f1ba,248ade5d,ceb1ef3a,baecc6ac,7a5e8c09,f4fb16b1,44beb4ca,789aff58,9374e476,efccbbbe,b74e2693,a4d05ba5,a809ff05) +,S(15cbbf1,2720f8f5,fa1a7752,b6f33333,91cf3d0e,c40f0292,24f5b7b2,c966c099,be3b1573,fe38200,a464f4f0,c3c2f9d6,24bd59bc,9412110f,6eca2ada,2f1c7294) +,S(e4fadc29,2f0cf77e,4b14c3cd,b567f5c9,be82fc93,dcdf2b50,bdf20fdb,f5984769,2858e2ba,c9834a2f,942ea7fe,2e339d8e,b384c255,6a48d688,a9510808,a1235452) +,S(b0a8c6dd,f57434e8,a8bee1a9,530cb9d,3d4ac31e,edd17653,3d84780c,6909554a,4f7467d3,62185e38,f6d6c30,e4c3e4ac,e167f3cb,aefcab8b,eac754eb,c76ea3dd) +,S(a4398869,26e4d06e,5337e61d,dcc6f491,4f40c04,eb3ec752,47d9d058,9dfea2af,ac1fdc5b,70d792c0,7bf7e55a,b07ea1ce,477dcef6,cce8585,84cc90ad,c62b6272) +,S(84b362fb,1597f179,f30b7e2c,b516153,94619081,1131d45c,ba55fd1,94f369be,d2a80ffa,44965152,78c2ee3b,122b5c3f,e6d27f9d,6ed6ba94,9258c2fd,94eed4dc) +,S(75182684,96e2d037,24c11192,e9112501,f7c27d19,8b92fa1c,3b0e931c,2b85d32d,eddd2a6d,d884f93a,b6f8d225,c9fb32b8,9f396f1c,2613be98,d866b713,14a00d70) +,S(aa96e0b8,104884cd,7e22211b,8b12e6e6,b3f3e3f7,fdff04fc,202d8946,838fe15f,d8a9b5c3,9739fae3,dd3b2483,4fd2981d,ea529097,2ba52541,56905664,f289e3e0) +,S(ada17e80,82a0a15f,366f3d6e,6775b5ef,35c980f7,12ea7522,1aa796f4,2454988e,7df4c1e3,191214b6,e40fdae,2f5dabaa,b6e9837b,f53eb10b,dd28e674,230cf2e5) +,S(322a0524,20f0fdbc,85f35fc2,de6690e5,a61260f1,428ec662,4185ee8c,d2385caa,fc81cabd,789995e7,d36d3de0,1b650a20,1befd7a7,a415d250,697114e3,c95e096d) +,S(5082ad62,3bf2ff8c,b4571b9b,86303032,ecd137bc,86c5514d,b9f218c,4f091153,b29d3823,eb4ed958,9b0fbfc9,2d006fb3,698ab596,d3e60360,e435bbdc,be4c80b3) +,S(de9e45fc,e9e0dee8,3bc806ae,286a2c5a,a7978608,42ddf02,d29a6ac3,9fbec470,bcda9466,e7207f7b,7d986e,36b3f238,d5bc51ac,90ec35bc,56e0949b,334ef964) +,S(95cbc3a5,61ff4cfa,278678a5,6f8b16a1,efe2d286,c7103047,310454cf,a1a822bc,8720fda5,58d23b5f,af9b80eb,dd5fa625,93325a6b,eb5ffbd8,f333cd9d,9c8b7366) +,S(fa0e59a0,969de9fa,aaf7be60,24136559,cb384b76,26d3bdec,f70f3e55,696a0632,2e8af3c2,a54e837,ddbfc32d,8a47f342,a856b854,7ec4b0ea,6fb3695,48b8b588) +,S(56aadb8,2056ff5b,3337aa5d,337671a8,77168b0c,f0330e8d,8f5445a0,cdd75075,d6c4cd4f,db81d8df,b095f681,f7349fdc,c2cc80d9,399fc16e,5fa3e86,9460a5f0) +,S(7da6a13c,11d5b063,8132254f,c064ff3f,d3818a5c,e07168eb,ed5b7345,53bbc19,26e912dc,5b03258a,f914cd86,88d6a068,266eef77,7d4f45f1,7c47cf92,16d00c4c) +,S(4004b235,d58417a0,ea1a2071,32b30a8,1dcd6a9c,3ea875a3,6d2a6225,e2b56917,1c960bd1,443c22fa,96f06eff,a717b5bd,efe8b105,7402cd93,7a6aa909,5775e226) +,S(b12c4d85,6ec6c85f,3553bfee,c7dacbf4,f742ccc1,356fbc8d,ab3d25b7,52bdfe93,a8a11692,49dba87,8c8e6c56,ff8f699f,d7e69c6,f994d05d,4e0643a9,7f9cd2e2) +,S(a78d9a2e,8b74ccdf,97c6ea9c,aada2644,8b560287,7d7b0970,41d24840,91490970,39cdd434,1d1e16dc,b89fe937,665e70da,ea80a174,5ae7b99d,dcad04e0,efe841a6) +,S(c8233855,b1f242bf,59b86372,ae49a985,2528440f,e3aa8e,28d594e0,114d2ae8,6f515202,a14738ee,ae4b430c,27391144,c551bfe6,bf24005b,81f92900,bda92b7e) +,S(6da24dec,9d6ec569,8dda9655,672a9b79,4923f595,9157af10,64cb53cd,5e5e8f6d,59517e76,8b0d39d0,2c075a46,c1e653f8,cf876fe,cfb10746,a8c248cf,4c73fcba) +,S(d0c55dca,ab79e0c,84ef08ad,57ff67c5,b36336f,2f6231d6,8efa2fac,54558ef0,d4365ebb,4096888f,c9a74d90,1adc4c5e,16f5dddb,442cb5d6,b5e22b32,99aa573) +,S(d76f084f,237dedd,ac8a1936,319d198e,baef2c4a,3b81ae42,e92f96b6,e222490c,c7737563,9639b7c8,32e4bf45,efc8da2f,53e20e25,641ea714,85674a40,1dd5f14) +,S(6a1f6920,9196c02e,6919abd2,c8fc835b,889c5ca1,6bb25e3d,ef7d0c4b,9f4e6d29,84371bc2,7bd206d7,389534ef,f2a49c6,cca4c9c9,874f59b1,ccc8b5b,bd5df3b) +,S(693b3520,85e1ea36,bc5c8ce8,3fa49843,369300c5,170010c1,b11a3c76,4deb12d6,4441c9b7,808b1ba9,8199349b,243f08db,6cdf5326,d7453828,16f008d9,45c1251e) +,S(5ee94c65,70953f27,37c16a59,590f7cd7,87143bab,405f6b23,718878c7,bc95741d,8578d18e,a49f9204,e51513a8,94920224,70837b2b,ee480929,5e4c4f0d,cf5c7794) +,S(30c174b0,e7b0cec7,e6da6a5d,335f48ee,6f59ef16,984e8912,32e874d6,2e51fe2e,af7f955a,8807444,56b2c965,5c4ad915,e040a360,eef0c4c7,891db1ae,6983867) +,S(3121068a,eb60a54f,dd427eb0,92e30895,92335900,cf191eea,79118442,67b7f4ca,d9e8f74,b8d9c0e7,c35e314b,51a52a95,bf672adb,9c6fb104,8d2ea489,5135aeae) +,S(a06f5e3,6513a659,106ec301,cf1f2df5,6093e690,38daaebd,4b17ea45,9e977ff1,eb19f18a,15630965,d2bd8bce,9e350374,1ab35f0e,4b442f12,9c9d831f,3811cffa) +,S(a26af0b6,50967dba,1c4a33b5,5226c2a,ad445f43,84567580,3b97ca1c,f3d217c7,22c8aba1,604df49,abe16949,59b33cce,db11c241,cbdb171c,50b0fb0a,399e6839) +,S(8e4471cb,b650097e,d3b0e938,18b8ec5,a86fedd1,addc6ed1,8703a99b,4128919b,fa5457bb,4068bf35,7b050244,6eef2507,c1d87051,28770161,ef0e92dd,d2308495) +,S(e9116ea3,24650463,fde960ed,eb7d5dd1,f5754915,62c99f48,31b553ba,2663ba1b,e30d7323,fc29e388,94244422,63434cdb,aebc5895,8a6fb350,33141e96,46a4529b) +,S(8b2267e6,9052f769,833b0fb0,ffe413f3,4b8edb93,9a2009b8,59d6673c,4ca6ec37,97f80a18,ac047152,178432b3,d445a0e9,84755d9b,7ad43165,8707185d,d573fc5e) +,S(79947669,d7bc732d,ca08e0cd,d3e601f3,c4344532,e35aa7a7,12271b12,339ee2b8,55906cbf,e782d32c,13cea8e,83812e8c,84c76d38,472ee59e,134994b4,b7b92897) +,S(ca6d40aa,2f863c02,b6444727,d00f8923,ec58afec,3b4a4e51,f4be232a,4b4c7c7d,ec73856,b0529f1d,9d2ed892,b22b059f,35e4a91b,b60c6a6d,e919d611,d9bd90e9) +,S(7ba23bca,5f9a88c4,588dec37,3861b699,ddfc9f5a,5b277bc5,e6e16f54,513fe7d4,25f10a4f,eeb77ff0,e24170af,dea0e2d2,3f2289a3,e53ad674,1640cc69,c042dcf5) +,S(186da622,d0e8a9ba,e26d43e0,b601c68b,311d2046,4eb36f2b,f86c3e0e,5d4ffcf7,53a6f54a,dd11af9d,50bd82d9,70825bcc,fe9da1d5,ab55e64,bbe4582b,f5248d37) +,S(e9601474,568cfe7e,6cdbf488,63f11f12,fa17ecdd,c623cb11,4818eb52,cafcdf8b,96d51b2,7b5e4fd8,1bbb009a,2ce40fc7,65fc6a3,7f8bda60,c4927766,a52267da) +,S(811f5957,accf9f32,3e48ac3a,33fbaeaf,c5120858,943e7065,9c0da185,71d1d4e9,ede77562,cc8ac601,e3dbdfb5,944d59af,63bd1aa2,ffb35bf4,1f79c418,d1d67937) +,S(77424acd,1d55424a,edb0149,a6ea0349,afcf44c6,667caa51,302b70f9,5fddf277,70e15309,989a2512,422a5bff,221c0e0e,df608746,7a34dbaa,99e77f9c,62b56c3f) +,S(e9e139fd,73c2c50,375d3966,ed4af6f0,4bd80bb,439121,d61bfe66,99dabd2e,260efa55,805918e,d45c98b9,c119bed4,21483d88,722e0f87,de7a31b5,e4b541fb) +,S(a9a5313b,e634813a,b10b9b3,6b38ed1d,a971c0de,5b6d1010,4cbe426d,544e7d79,ef7b3203,2a95144f,17803f09,72f6e2cb,80e6a099,5196911f,916225ac,e97bbb97) +,S(ff29a1dc,b5c97b7c,c5e2b73d,7a0e0244,bef71dfc,765127f5,f7e0f38b,f8a099db,b8334f11,682b9a22,ec08a376,dc3994c2,7d235d5b,a4a77803,79ec0fb6,7b4d0345) +,S(c98df4d2,da452118,852c096c,bb34c8ca,a2ebcb6a,e9a3420d,a4fd3eec,cc31c5f,f4f9c618,44cea236,edef80a1,4634159c,72663322,7372cbda,4b2680,b5dbbf7) +,S(66126139,77cfafb2,283e1a92,d6327174,86add807,b88cf793,12fef2b7,197289a8,504387f6,70caf906,2db248c3,ed5faee0,269d0190,70dd5acc,c8599f5f,5e01d1d0) +,S(efa8bcf,281ad4da,2f083e6e,182765cd,ab0e66f2,e8411007,e216a69b,d73d5e94,6e1afd7,e97061c6,cd0794f6,837aefe6,e8ab76b6,53d80846,98747ed4,64e20a) +,S(d4f9fa7e,6e385f02,c068ce7a,1b80343c,20f85b7a,4699c126,575915bf,69a2f6e7,a6496004,f280f970,c1028b3c,e0d94187,34dece11,3f9fc737,2ef0125c,851de143) +,S(5980a5c,456f383e,f0add523,13fe37a8,874059ee,497b0a23,c6dfbb2f,9f6c494,e1da9a59,c8e3d482,ece7972,9c77308,e47bcd90,cdff915c,32a94908,db1651fd) +,S(988ca976,ac6bcfb0,b42125c0,939a1207,2eb13565,29da3e72,6b70b2b9,5c310e12,97368d81,79a76a51,7c7d063a,179a2941,fe5df19f,25f1e1fd,42a32af9,d5de16cc) +,S(ca49ad0d,25b36130,cc878861,f4b72a4f,e945bc0b,dbae2f4c,629a6375,3d88037f,5ad2c0b7,2e084bb3,95b9268f,22ba1774,c6e423e2,4a5191d5,b940c89c,e1039f0e) +,S(ec18622f,b9771723,c8e6e6e3,5096e517,38c4dafb,82b10317,1f55f900,b24c2e06,5803ea86,c839ddad,6c288b8c,b382a1cf,76a790c5,6f99df9a,fc9915e,eac6dd61) +,S(c53aa261,60e627bf,bd905ff2,6b50a171,18062ebc,af9d9ca0,c486430f,b6de5da2,7d6298e0,f17a515,529fc5bd,2b85c7ef,d183710b,96d62a2c,5a0195cd,ea561790) +,S(ad138110,f9161559,229ead18,4b7ea7b2,846384e9,dc22b8eb,5b771027,8c8913d2,135a3ed1,d9df6a0e,105c7c,e6ce625a,f8173d76,60ea04b4,9ee781a8,90595eef) +,S(46946390,304df8d9,15705fc4,5714f3b4,233a5ec1,afdaf145,b6c09717,9c6b43e1,aef28d5f,88f62a7,e3ecb978,6efc97f1,1123fde3,88ec1bed,bbeee3c9,f9d3e014) +,S(d464a300,df9de2ee,220ae23,4d59b6cf,44f4d280,7ab4f588,aa21dd1e,eeb80819,89988349,b1ab280,8b829754,7e5b36cb,e66ddd16,4d7542d1,cb0cf210,44a8be1c) +,S(5e99ed21,9ade424e,25b807c5,bcbeb05,9b638b7a,4ffb3c6d,d3bd054b,b7af9f41,a373ab0c,4aba0b04,878451bb,96807604,7d811fd3,605e8e46,57574efb,21681aae) +,S(805edc92,6fddf92,d33db068,9334d778,4b30f73e,4e65517,42e62c50,fc6ff2a3,170e4317,4361275c,2102aa8f,5406ff9e,7bd35523,8bf1e946,1a6f16c0,38beecdf) +,S(ec6e5a3f,bb96f0af,dd700701,8c046998,49a879ef,5ebcaf67,456748f2,f25fd18a,9401937d,cf584df5,a9399fa7,bb309ffe,80ae015,20933d63,df1fc181,b2979fbf) +,S(94f226aa,a25c710e,2cc68683,2b9124a,ffc5c3fa,179ace52,327c6866,cfdbb3ca,b92762c3,a4c56d95,1ef16db7,b3992e9e,aa2f9c3,ec48f077,62a379f8,408f94a) +,S(8d2668fd,5d46bd4a,fc84183f,4fc31dac,964358b,fa6b03f9,16ac5bec,a619a92d,c24f6815,3e49048c,a461eea8,ce50c8db,7794b10d,d8080236,2af6677b,45e45f95) +,S(2a3ef112,d00514a,c562de17,a68bc4de,c4c04db7,8266892d,b645ac6b,ced47f67,6e93173d,64333933,c3db0f56,8c05410d,8a1dc73b,6a30c6bc,2b0d5493,d9f9fe1f) +,S(6eaa1185,19d6cdff,45d88426,6fac5867,790faca7,4ccc31bd,6eb19551,8f752dd,affb02eb,277a2bdc,db79d91,10ee8a7d,58c9662,22cfac03,b3d26cd6,3c678f9b) +,S(d17802d0,1944d9f7,aab2b542,fac985ce,e56172b9,e5629e53,ddf57e0a,8dd07137,485ae7ac,34d13d9c,9ade04f2,b3fb8cc7,d0cff406,97abbe2d,4961e753,b8de013) +,S(9e4cd82,67c121f8,96c8104f,988a140a,71f0cf18,782b574d,407a4840,607f5804,c05f08,3fb5c4d3,9d3e237b,101f46e0,6547828e,f05040ae,87db7874,9d7f3bf6) +,S(927758c2,12452995,886e97b4,b1d16c53,4603c,7362e190,dd2558a8,43521a41,30e4d5df,c5ff4b5a,5f4f757a,3483234d,4c658b7b,f24dd509,7968627a,a2a86d56) +,S(589906c5,90a1e143,ae7ea4fb,84edc7a,9f00bc90,dfeadfba,1933c36b,57f2bd25,e9d1d51d,92223636,cab62cab,362b79b2,12a18be6,6468f06b,a543921e,bc9b1c61) +,S(5b516216,1171c076,b10d4987,3aff89b,c080d2b1,9e3ebc3a,de8dcfd1,734e2f35,572c40fd,24965ce8,a78c3402,381bfd90,b683d6a0,d379d7ca,21e66804,31b8ffe8) +,S(1603bb4b,3dced105,b1a2b748,21080a5c,2629a52b,56823524,84deb617,2f6a9694,2dc4243d,fd20fd16,d1f2798f,23e43be5,f88d5feb,1f6fecca,6fadd5c3,f5a63c37) +,S(7bbf7555,4c14bf1a,63ef9f38,ae3ec279,5cf0aa1f,335c816e,580bdeee,10d95ed2,d2e12648,81ba6d5e,e8cf389e,84e37ee1,7c178b8,dda63756,32d9330c,beeeac53) +,S(b2d3aee,d083787,eb4e14f,328640a3,c557724c,e06a62b9,17134ebd,fe576073,68cd90be,2df0fa6b,da2693b0,f18357b7,ea5205e,2a1f5fd7,5a1413b4,fd9ce0d6) +,S(dd10759e,86121fd,6296f171,57a90f2d,bc217238,53969f85,4a71461b,27df81be,713442b0,65546c39,1b71ef30,3bb6ebc0,659471be,fc165a3a,686aae0b,270f13c9) +,S(54229d57,3a274ca,6eda6194,62dce4b9,35ed143d,16e470f1,239f1045,6dd2de16,a6739dc0,916112b7,4fd6ab7a,134093fd,55541758,5b3fb39f,da647bc6,5cde08b3) +,S(141f3cdc,124460ea,15fc80fa,dbba8fce,d89c4426,74e9a3dc,55f74f69,66bb8ff,f0c8d7d4,dac4ba66,1768514f,c9bb9e2b,319645da,735e5be1,51790483,d7b477ac) +,S(e9795743,efacb0d0,4c91c74d,33837b61,8d08acc1,cc2603ac,f02b1610,16fd1363,9f1c8ab6,735f3161,811f710a,1b5703d6,aac65c23,35fc1af1,32ba005b,bbe66d7c) +,S(e5e8c40e,c6c9475d,d4cff6a9,c9db5cb3,f8202bf1,bf60ed41,3e213d6f,2c860797,a9570e76,482a8177,b1adf9a0,1a7716f4,b1e754ef,864d2deb,354a96dc,ea1decb) +,S(5292362e,a095d145,c002f027,be53a28e,d5244982,bcea97c,b56d9ab6,162f7ae9,e7f00a61,8e63664,4555170d,9f13420e,fe1849dd,641bbabd,d3648250,81934e37) +,S(a6772998,3d6a765a,67e9e3a7,45e7de0e,1e42f34e,46b7cdde,8ab20262,ad246822,53281b0b,bb45bbf6,a1e804e4,cb6f443,537b4bb,d551c9d0,8976d53c,1020407f) +,S(7ed3bc8a,8a68ea64,c9dd316f,48f012ed,229e5f9,1a294666,4546ef68,bb48959a,238e6696,a7cac135,ac551927,fef28c04,5bbf23ac,9d24ef52,3d69aaef,27312e69) +,S(96702aaf,a9e17a9b,5fa4fe87,d3b45633,b59779ba,57002679,63a3a5,7e7442c8,53deadec,73ed54d4,db98093f,9e269442,d9a4a955,f9b18878,8be2c410,84014a4) +,S(bd3e9c04,42a9b0db,b063aa20,b09e7e0b,4ea18ecc,55a5794e,14108aea,1ffdea66,b5e43b5,fd8cfa37,dfd49f34,1ef14b0b,c2cc571e,cf9f7511,f2f11e15,825a57dd) +,S(d95c6dde,f07abe75,215e14ba,e76a3be1,6dc514f1,7542a246,e48a2a35,108c0833,e49d6c1b,baaf6f7e,44fa7389,31767757,76d6756b,ce8353ab,ca6e648a,cd2e8fdd) +,S(86f7406,7a86227b,2f5414cd,12c9907a,b78464be,8a5c2284,7d8442ea,d585fe8a,53c73db9,3ebd3340,2856a7e3,d91d29e2,e9439a3d,c43120e2,1fb5bb04,384a8e68) +,S(bdd92d35,ba51344e,4deb6b77,126fde21,9e7030e6,5e1d96fd,4987f1ec,bc5ee6ca,a49915dc,b4f29eb5,af9d643a,6c6f581d,94a205de,3a8bf3f6,a26d294a,be11cebb) +,S(dd4b5fdd,3ca88300,74f19532,728a31e4,152be230,2e6ef55d,a6ef9209,fc70b65e,fed2978a,3cc4c8d8,2012cd5,7e06ea37,4ac5f802,a0032e8a,21d58239,860856bf) +,S(fab1a71f,f8a64cb5,14c94e0d,1c67fb23,70998570,ebbb0e25,9e09df03,e82aaeed,6208c3fe,3fd0d9c,640a1908,1a6a6db4,cdeaf7f2,ede4835d,46549734,c1c50035) +,S(4d6fcaff,167a869e,311e8dd3,f736230e,78634ebc,c4470b91,686018c,7b529509,e860a3b9,28927956,1cd132de,a1de3456,576c4cc6,138bb079,a166b9a6,79555751) +,S(502caf19,5004fb43,75b13ca5,16583e1b,3f60b32f,d9769832,b42423a,f7ec78c3,c0aadc3,36019a17,ebf7f2f0,5afc036e,e807df5a,c2869bcd,19c2aca0,1b5ccec8) +,S(35727dc6,ebe8f38b,1a84d201,2fb24a2e,9ddaedbc,63e8de82,e18de4a3,c4d021ac,8eaca26b,3b88adf2,d19d9d52,c84d83ff,89451750,6a77b4fa,fd717b07,4b414322) +,S(7ced897a,47b5b0d5,f4db6d9a,ecd9c5d,54b35789,91902324,6f270d7e,7ac4b377,1c6ce993,fb84c89,ef5dbdfe,68dad70d,ca39a4ce,29cbc658,d5332d1a,6f6cb157) +,S(60d1d74f,660a5f6d,bac1ee84,5aa43dd8,889c9e4c,6b0ce1ab,e9952ea2,7146972d,dc4291c7,db1dcde0,8e618261,b8d177aa,77b5b0f,2aa8f341,8afc9eeb,dbf068b) +,S(a8b28606,e7040dac,7941cca0,b9cec031,31db3f95,bee7d6a9,8d60c3a,1091f81c,3fbac401,61c81632,ac8655c6,d5c02744,7c836244,e228b9a5,bf5d799f,1fb810ea) +,S(6418a48d,2705a27d,5fe68188,58a61a21,4e4dd39e,4151aa89,9fc6d414,95f975c5,21abfd32,14d98ff7,5b154250,8480b32d,ea8c50c2,3400235b,be800520,1609c7a4) +,S(b46f6f80,c71cb354,3058372b,9ba5e6d5,2617728c,54c10cf6,6ead7ca1,de2700bc,2008111e,c4b86ec4,26bde7ab,f52bf201,10787bed,d7b2e922,e3a5a60f,e68a0f4e) +,S(a0ef81c3,c9f78950,41a8d90,69cc22e9,22bc0cac,4e61a030,495ddf48,eaa6dcfe,8617df0,b32975a1,6666f86c,136e8c99,c07ff948,ff8d8176,9d968544,aee5eaa8) +,S(c03cfcd4,21cc7095,5a3e10e4,a4ed12f0,d31abe86,506da6e9,c83cf6e1,df73d093,8d6f0b3,c0d6edf4,b5d2041a,94d13f89,91adbb03,22ba9f,a56b31cb,63d4400f) +,S(4085a2aa,95e4e8f,11b0e94e,2673c48d,d42506a1,81d93a19,b5f79d45,ab88b688,cc769f7d,f45cece,d3cddf9f,c75c4dc9,bb7018cb,f54986da,434128af,b26c0f28) +,S(a485186a,b21c4650,e9f7375b,2ba644db,f22e2db2,20933cce,2bf0ca7a,c404fcb,2d227efb,68649a60,bee9461e,9390ba02,eb133cb0,eb8388e3,f64d23a4,332ec903) +,S(9faa3103,b212c2ba,9a454898,fb451d17,c3632b7f,c9368452,3ddb88a,7599f14f,9dcb34ce,51f7c4db,7cebb0a2,1b50ff2a,d67c6bd9,7066e505,60132168,3be236e9) +,S(1bb9ddd4,804edffd,78d00b86,ccf6a07d,a798d91e,5d88f2a9,37575d96,81187564,6577e8f3,ac22184b,d46339b8,25a426cf,2dc8cc86,78767d87,999a8e63,5789dfe0) +,S(7b32d099,c78bb658,24868172,1b375689,b91f0650,45fe3d9c,8af4a331,2acfdf58,a4c92e67,f4bc53c5,aa84cef5,908a36ab,afcbaf66,a946d98d,4254271a,b4f088a5) +,S(daf23c72,c67145d5,e2a55109,f95742d9,28cc16da,e53d0453,c8dc22fe,a9eec7f4,205dac83,6614ebe2,a55c04a5,970d4ae5,a9ba4b1d,d8d51f52,50de622d,2a2a472a) +,S(6f156518,addc53c9,76a16944,a059a465,8c49f6c8,6ac4339e,82a2f69f,a3599279,50a277ea,6650fd6a,4a5e4c2e,fb09bfe2,78fe8fc2,c7a86089,b4bb0fa5,fa7262d8) +,S(17661b7b,5b5a8a4c,f700397a,1f819c7d,a6c44cf9,a7d28bcc,df6e7fdb,efe50580,dc1d7f84,841d8ee9,3a9f9fa4,6a03b2a8,365398c3,f94b57d6,77ad4238,264c7868) +,S(c3042389,1483fe1b,12f16146,f48ffe67,3a1ca3ea,ea330d54,db5f7c95,fbfce6b2,131d108c,14a4fda7,77d323f3,f717fdd0,a422acfd,20b9db1e,2b2c4039,a330ce3b) +,S(654da43f,70e4c85f,c9aa2d5a,12ce1a51,d4364a09,5c230ce4,f8ae61b3,cde7fa4c,f82cfe9d,bec57ffa,4109e9fc,c9d79135,49817f50,9fde8195,79190d38,f0afd2f2) +,S(c17be24f,d7d2535a,c4eb722b,bbc4444c,fa529cb1,283585c0,730813de,7219b481,2d35a0b2,27582d2c,b8155b92,680f6651,c0040508,ae3de034,cac87591,654c0c86) +,S(fcb1fa46,f8178153,fe5f10b8,a91a411b,cdc4cfc5,61b5eb2d,29446a79,8deb14fb,29c75252,4cb13054,2b9c1a26,b4606eae,6f40ba1a,f73d006f,5bac65e9,3821f0f0) +,S(7b86452,e43ae1f7,177c131d,999b5fa,a6e4a107,e0a06f6f,4305a8ff,d1ead9d8,3e7cd53b,b2f27d89,829e331b,8236f92e,fcf674c6,478846c2,c18307c6,e82691f5) +,S(8271b58b,67a27540,c1d8305a,840475f5,3faa0031,4e779e2b,9e578c07,366a342b,5cf145f8,6fdff832,6712be08,ca1f3a27,d3e58fc0,baee76f7,a052f7df,450dbed2) +,S(3f027c76,4ebf933b,50df3d37,77f60210,4b997d1d,4b9cee19,cb549c58,b5d0fa23,cb44dc89,c533abb3,e255fbd2,6dace4aa,836caf4b,2589113d,82ad1886,7eee5d80) +,S(fbeadb62,ffe218e5,d0245d5c,4bce7334,cbbfbe91,610e8bd9,f0b89953,bf472bf6,7e824c09,9727cd33,1aafba9c,6776815e,3f954fb,539efa5c,fc4ce034,db602c09) +,S(ee2807ab,44643652,12b021b0,dbe94618,5d22fae2,f500a740,127f4dfe,32ee4c60,d958ade6,cb9d3a,ae21602e,d25556b3,b3869202,beab0910,1a1d97e4,8e360e1) +,S(94ab159f,2448f865,f3d38d65,12273da4,ff476289,3dbf74d2,ef889807,81d6802,88739de5,da331b70,87c58bd0,d734bd75,5853753d,1d3b97fe,39c68555,8868576d) +,S(98d2e8e5,a41a0bf4,a880a510,b4b65321,7b9a62f0,c466c589,d0c1634f,557666cf,aa24bed8,a5cacda,bc3c950f,d56bbd1f,f156efb2,4632e073,ac822875,2617d1f6) +,S(64efcff2,76cda1b7,c8e5ab1b,12c73a48,5a25113,eb9a5be,496e52b,ec6ad16,ac29e2d7,18bbf63a,16003992,d85a9090,98ae89,834dcb24,293d6bf1,b8130e3a) +,S(6a8e3376,1636ef4d,b1116d54,34f3fa05,d8cde7e5,31b260ee,6aa9ee61,1c4cf5ed,5161b5ef,4bafa1af,f76a1e21,bdad444a,2526cf5c,c81636cc,36f18400,b8dc47af) +,S(4786ac32,6312dcbf,a1386ca8,c505715f,2bc7f85c,22b6049a,e4386bec,11648013,67c80d39,bfb6cc58,7fb312c3,8fea53cf,6937216e,8e7f3b3b,4a2290e,6d6df687) +,S(b87d2d08,ee206c18,646713e,8af6bfad,c77754c9,59158fac,c31928af,c66bf596,6110c98f,bafaffc1,7b40277c,17ad9650,15de7069,705019c6,b7a74cae,eec6e65b) +,S(ea9f310b,d088429e,236d565f,d1b3129c,7ca8573c,191d1893,c1c157d2,a0867d4e,61c95d39,c1893c68,fdc60ddb,27909f2d,98c9ddd6,efc67ccc,7482c0e4,3c05b144) +,S(abf862bb,9db85d51,5d6a37f5,5f3942b4,5404238e,d49d3124,59f9ab38,29b34ebb,30d13c26,2157c1f,1ffc8a46,a3679e65,35be1981,c202e7de,10de5386,db863739) +,S(80767b75,df3e69dd,757b4c4b,fd0b4be,f75fecc6,ae22b488,214b0035,a276e492,ceea1fbd,bcce30be,2d88b601,895a8fd2,92f61b61,cafa5589,f9eb9652,8a78fbce) +,S(8b7a47e7,dfc35093,77771622,482e45fc,84708ab2,c9c734d4,fdc9e8d2,499dd950,dbbd399a,d62e8309,14bd0ba6,19a9963b,8aacc765,cad19734,e2526af4,2b2013d4) +,S(13700ef1,f1e82cab,7e9a7d79,d76fcfb0,b6e8c18,ec1e6546,d27eb919,bd871f2c,97488000,f96aa2a1,5913502b,30ae5bd5,f9edc84a,f97dac7d,c79be68d,a0f7f4e0) +,S(51f9a79a,991fe61e,c5b7c93b,d0905bf1,2fea0e0c,bb9e9a7b,d5d99188,f42c8a16,a3ffae0e,92e3bde2,51c9a8c4,1a6d05ef,2fa2ed53,d7a21402,e759c102,65d96000) +,S(ec7c6892,644bcff,715b027b,be48936b,26dde18c,bb4a1a9c,fa1628ba,84d5b456,51bc2b69,b76646df,51644c28,8570bac5,d51a0e9d,6e9f0ce7,1abf6812,650b18bd) +,S(efb8249f,bed3299f,7715b3fa,b6a23bf2,86560ad3,72e31e6c,ec725522,227f5c2,f36f4624,59a7fced,3c621158,8eac73c5,54593c4e,b7c82fe7,b0951bfc,8987bba0) +,S(4efe94a4,c34083dd,49762ea,eaf58feb,72f6e283,9931b706,56427bc,e9913bd,5c8b9448,5c687c16,76605dc2,d414f37c,da98a562,c71e7e72,fb2b8d5e,6ae0d60d) +,S(ecc5864,fd6a1f7e,1e20a84c,d9dc7090,9775446e,2e0bf6cc,bf896bf7,97f9dd73,49eefb1,981d4b87,ff21ca80,5471770e,8e063541,58e9c1fd,9995718b,14d9d6f) +,S(133f3710,9bcf5655,48d03c90,b82d55de,cfae1cf8,a5f3e117,5eaee107,aa7eb121,1af96963,e9113d2c,588ab083,58df45cc,8b68312c,9aaed504,3f17fffa,b29bdd7f) +,S(a8b1b9f3,971667be,2a65072c,efe8b1e5,e64f652a,4758f51b,4888087d,4fb489b4,556d9a3e,c2861fdc,da258a1f,8c6e81d8,1df43669,e64e3daa,90dad4c7,c6c0b662) +,S(104889df,dd81534b,655921ae,5483db4b,b3227f2,fc3563fc,4af46e1e,2c4fa88c,a70984a0,ce97af36,b827aaa5,218a9743,73feb2e6,fd7e2a02,408590,47323ee1) +,S(4603c58f,4fbc4575,7e9e165f,8a4b41e6,b0d2bf09,b1124998,3881ecef,2a8852d7,37e5990d,7c50bed3,2b72a43a,2e6a0ac7,5676cc82,113e3196,d6569e21,3adcd0c) +,S(5d1ddb75,774d6419,9f07f578,21786ad5,9fd7119c,f895196f,deec090f,1240748e,519140b0,1b340a4,a7aa2864,fef5a066,feb59c6,3355c222,6805e45e,160bcd9d) +,S(c9f86104,62644b3a,ec4daa3e,bac656b9,de816cc3,a5aff1a3,633f90af,8401a97f,405087e8,80403629,516d485,f17590a,1277e058,840b3fac,4e02d3bd,7be92954) +,S(b774b1e9,9f42b296,dab0e371,6d4ee04f,8bd517e6,7846d0b3,3ef5e0f2,569e97ae,ec78bffc,d6cfa76e,513a2b4b,db638f73,519b570f,f423936,ec0a54f7,9ceb5f9) +,S(d7a1e238,ad3eddf5,29002e8e,87476ae3,6668d656,595b3d10,2486f1a5,c30e6a28,5c9fdd1b,e132b9eb,4d7b935c,2209e105,55cb28e6,c4616be5,7b248607,697ef6e2) +,S(832b146a,cd002cb8,199e800c,47b2a0cf,d96591e9,91f1b406,b2db981f,c0aff924,f64632de,b99254ce,9d60b026,76551e8c,e21b300c,86fa5bd,8bd7c32a,4a81a18c) +,S(635f97ae,1785487f,437fee89,9ec21d13,dd8228f2,e209637e,18c85ecd,6cf50202,b53fec65,1a1af1ab,2f51eaf8,8a3af104,e1a40e82,691da03d,9a179c6a,3b30e5f9) +,S(f882f983,99891dcb,d58b931d,c215ca05,a232aa9a,7cfe5acb,5226ebe4,f4a598de,3d0313cd,aa36668,5422a6e0,2dd7c560,11fef0ce,3c268a42,3509991a,97cb4562) +,S(5b16de8e,3f8b8670,a98099a5,250e3102,51107cd5,d04bb804,e2a7175f,48183594,33c3d3f9,e3099917,e707a429,3c83205,4a26cf2b,2a9e3118,fa64f441,e7561fcb) +,S(b47e085e,c7a7977c,be3e2329,ea417e98,e86eea9b,aa23fc93,67cc06db,cf06647,cf6f1cc3,f564879a,f9515604,4b7b856b,827335af,5cdcd55d,1f8bde5b,d3f539e5) +,S(afd1a856,e0624d6c,16a876c1,2f78ba44,303460f4,d3cc793b,5dd32a98,d8aca9b7,3b334e53,83301f24,df0e2df6,12cbd7fe,3e1b6650,d5355bb6,c9d3873d,f42d73b8) +,S(454316e,69f558ff,6e19057c,9c754b2b,83eab233,f696ccd1,b80bbb42,3c81ae57,ebbbce3d,4e1cf2a7,315a394d,d03bfb64,300ac0f5,11627d06,7a5341b,43a4ace9) +,S(a51f1d61,dc60b34c,65345205,b2a316ec,766560b9,c50d72a2,330f828a,ec978d05,d4e9b10a,58bfcc59,ef24379f,532784a7,ccc4c077,8aa11bd8,bf008846,44175faf) +,S(771329ba,3ae1f814,764fb9c9,ebe68f37,26b8260a,90d4e49a,7dfbfd30,80cc6128,128479e4,bbcd5b06,ddf46f49,614f6ae1,bb8914a0,7a0a5bc7,34b69b39,caf71b1d) +,S(aaa59016,d18bc9e6,2716bdb8,6d0c6bcc,e04f74f1,98f17675,c021d078,166c9fee,36cd35fd,53be5c17,84dec1ce,80ab3ff4,95abf363,17824a12,71cef620,2784f91) +,S(be187d0,b3afc0,bcbf98ec,bf31bfcb,f4121265,c4b815d6,9403d9ef,926cd254,a3aa0d55,8b5f6720,9d8a6514,fe22026e,66e8d972,912a6503,5faaecf0,ac2d9b25) +,S(228afb7,a9564fd3,641bd417,20a0e44c,d2ab5d99,844fa61e,264ca823,e197da88,24457982,391e3209,88cc44fe,e42cd242,80fe1f64,b87814b9,bb485d2,ed0d7757) +,S(dd55db40,e3333379,7b8ae013,297b477e,bca2f586,16f29300,6625aab4,6c367ee9,836a69d6,603fd34f,92d3f775,d25ce89d,b92d2803,48a21178,44f9273f,1a97a9e9) +,S(f82dfaf2,5113d8d5,728c53de,3b43f2b9,d2aac0f2,f90b762f,390f745d,7e10093a,1097ff80,ef2f5dc,88873c1,2b5acf0c,a8a803cb,ba3624d,4a036649,ffa07bcb) +,S(666f1006,57ebb1b,f05f50c1,2bfa7fe7,cdf11e0d,6cb37c99,4b39df89,acefc7e1,3e3ceb4b,eb292e5,3c00e4bf,ab6dd9d3,becfab1f,19880c3e,cbb93a4c,415f67a7) +,S(9769b542,3b098d8c,18871a53,8403ba2c,d32f556a,7ca7b089,ceb9134a,5d983e73,536e7c80,5c71c49,9d9acbed,c6fd94a8,3e64a2c,8afd8721,12f6848d,60851084) +,S(26081d34,3771ce6e,39ba59dd,2c2e15c2,4c6903eb,b6c157fd,b1c51dc4,e5343bf,82f6d675,39d04825,4cda5bea,e4879547,9570f86c,9ccbe1a2,ee0deb39,afcf5efb) +,S(feef7b4a,dbb0e287,a0762b14,445a8fae,b860e08b,19600b41,45d92447,be732bde,44e79fff,55aed478,f8d951e7,f4a327f6,c4a1e282,ce617a3b,5128f528,33851e5) +,S(ffb8488d,f26d306b,70b5b1be,4b6f2e4e,b6d6b2b,4cf05d3e,9502f3f9,81406cef,f2891496,f5affbf0,bc60c46e,b96baf7,2a4e6797,3297a28a,948adb92,1cfd5dcb) +,S(fd1ec3c1,736f2edc,5c080981,fdd99fca,9e301851,cbe8d840,38f03a6,59f21ea2,86755e0c,7b01dbff,ed701a7e,b5deb21,3566f331,d707888f,4f658879,cfbf3bf0) +,S(addbda08,d536486c,b9d0b4f1,5ffc9104,ff75be96,739d4641,a405e2c4,2775fa4d,4d70179e,1237e2d4,61a0a0b3,c3bf9df7,9483afcc,1b8fe02f,47d4b312,2f339089) +,S(fb5e9c09,8ff0520d,b162e57c,19b80ced,7c63ea68,1c7a2c56,2557a1ea,d2d7a46b,c4d9cfa0,d31cfac0,78c187dd,2ccea738,4667776f,b8eb0935,9330fab4,b9f67e2c) +,S(28c33364,1762626a,c9e4d2d0,53a1e5f0,771449e7,a0977f2c,63db4681,1c675b45,239e717,a76b80f6,2b11c6f2,c459d516,bf81dbb1,4dabba88,9f1e627b,5fcb525f) +,S(68e783ee,84fea44c,59c46bbb,170d7751,c47fb05b,602e9995,2469def7,cd061c9b,7054cae8,31dcb37f,ae8d8298,859b4e56,772f8a6c,d079918d,b49c7bbe,cde61f16) +,S(1526613f,fda17eea,1a7930da,c1be4a6a,4ddffaa5,1adf3c92,73f3a2da,44d69c46,c9c3d04c,996c4d40,b906bbcf,5b0ac89,f541ae4,81b7c5c1,8f9b5762,7b9796af) +,S(263e6099,2adc9f24,1cd1c16c,de291760,babe6e41,d4e3d064,f1a8768b,94bc5439,3ba3eade,1c5b8225,7559c0d7,97c47e5c,41f60195,463b2eac,74801439,b3a6663) +,S(ce58e7e0,73c871b2,673e18e3,c9ebbb8e,e6bbb2d6,226e3d0f,4bbe6bb6,73e72816,85ba41f4,13158058,fe8d068d,df70f97c,acd4f512,cdcded1d,ff9c3c39,eb1ce1f3) +,S(47c3eb29,1402d2d5,25a95ee6,fc54d1f9,721f29c1,e5b51cb9,d82d234b,94de6594,beb5da30,3383e0d1,4d13eca0,8eb8af0,14f8220a,81b1e8f6,47c1f95c,4503380e) +,S(8f5e8092,c71ce97e,af578fa,52f174d,ad0133f3,94bf2ca,ef5c7adb,bbb37ad,1ca41380,ac714a5d,3c6be070,117b10b2,dcff62d1,bef220e2,4063c971,89f6ffa5) +,S(edefd606,5883e90c,31215558,b05f297c,45c64fcd,34cb864e,3f0265e5,7e7d5a4,924502b3,1d0e8df2,2a54ae07,edf1b0bb,f2ad1df4,7de805a,43ae11ae,8cf3d628) +,S(31516a47,50773473,6e691ec4,e6891eef,601d6e42,d7f4ea4a,1e5008cb,b77e151e,7f243d73,6ec9d71c,d2047cd8,d97c159d,345003d8,72556e94,4f7aaa74,acb8562e) +,S(9049e0ee,149d379b,77963972,1027627f,5cf68f7a,9b1c81fd,189ae6dd,eea5c552,ecd60031,71dd9199,77ef92b4,262388a0,7aec9cbe,65d80882,1847a5b8,c758ad26) +,S(977ac11,98214ec6,eb92699c,aaa9c219,6246df82,85a6afd2,fbef31,a5a93b8c,27e01787,d6e29c74,f6308e21,24f9fa99,6a5b78b5,4f45fc95,b71430a,abfb964c) +,S(bd7ead55,e8ebc96e,5429f59,52427ca3,8dad3d9d,649676c5,52a01099,3c359a26,9e596b26,8c90186f,8cf38e1b,f47d6d6a,d7204c78,b557b3bd,25125bdd,c3ef7824) +,S(90c82d89,9178857e,e2563cdf,d822b872,2901b53d,3b93a6f9,2905a4a2,163be70d,3779d0d,c99fc814,8b2f58c9,fab70952,373365d6,1adb6e5d,9ac3f761,9f27e943) +,S(a312a0c,3111faf7,2cea8b7c,3e2786a1,65362caf,11919687,3368c77f,117ac42f,1b78c42e,af0e6219,ccbe3ed2,384f5529,dff57894,b9c82566,255fc4d0,fc798b6a) +,S(7cd723db,c046d3f4,5b0fa248,1197790f,688632b4,5f57569e,2f7754c0,9fc9bcca,eb5fb704,a5cc0b22,979bd64a,790f428,b57bcc79,3bde8fc4,4d6bc9b1,8c21e495) +,S(2b24dc16,59324483,3851815c,1675d051,60713a5,7dff00f8,9e72df7b,d1c7352a,ab05061c,224fb577,a72f5d2a,38a9e59d,18c52645,9564afca,a9539d0,c64c8fc6) +,S(955ffe05,c4653033,c2b4afa1,fcf8d3f1,3b55c9ba,80d67c37,598f6d87,97f11b1f,d3502a31,bb37b950,93b3e594,944d4bac,3314d490,d375e118,4e8c5a7f,2346e6a5) +,S(699392f3,c2baba31,1bfa8aea,180283c1,9036ea92,f287fc08,adb32fa,6785fefa,242acb3f,fcbce5f5,9dba097f,3d358862,11232403,2730700d,724488e,8bee3478) +,S(193d04f3,61eeec14,633c702a,f8e7eb22,2c3de17c,a0130e9b,cbfb3daa,94674d24,39b68a32,79e62533,8bfda780,9f0f8264,ed2ba2b5,ea9c0035,6459882a,a943e088) +,S(564387e1,ed778e70,baf9f876,2a0171a2,6a4e660a,bc6eaf1c,7ef9f00,69794817,7cf553f9,128d0716,e3c81039,3d1ca83f,1cf0fd82,7f7d4ff0,ff538637,34ef42cf) +,S(f0eba12f,b282171f,d20aef8e,192741e5,4469c5c7,bab13f51,7084d293,c3c294cf,bb0211fd,18229247,5238ba93,f6992d34,a41a7ae6,78750b04,fd34f3cf,af61f90e) +,S(a54b91bd,b09d29ab,f4cb354d,283c436f,a8e9cb20,d3a7fd35,e59f59c4,200bc13f,bae1af42,3459a852,bc16d45c,cd35fd94,c0033d89,5c86fdfc,f4fad5e6,3bec2cd2) +,S(a9294788,b2c9d997,18193553,403350af,78008113,ed58cbcf,a2816c80,46f75d47,dea8059,c24c1c84,44a0263b,2896725b,79c2c8a2,6d425479,f44bb7ad,225bed8f) +,S(b6967b59,ea3d2899,c14fd2,cdd4590e,68dce683,de275313,ec94d5d6,1bee65d1,67eb4a27,8813f69f,e5bbc586,15dcab17,11bca30,77327665,2fc333d0,52fd2a0c) +,S(ffc0f3fd,241ebede,ddb0c43a,a9b8fc2a,9e778963,1af8d54b,bf99c64,add0cd7f,203e3aec,321e40fd,414c002c,e5c6cf90,2f3e75d8,ae0e3604,61028ca1,e21da260) +,S(d8f86220,c390bcfc,f714ec3,8b6f6ee3,d37b41da,4026805d,6fbbc798,8a936ab2,7c0ad863,31ab776f,c22b2539,fd65086,2cfd2ba6,2f5d67a1,6eb957b1,8d812bf7) +,S(11295381,19a146b,442c3447,400b6e7e,9b2190ba,dd42a75e,24460ca6,45a3cb88,44950501,75662a98,793416c5,3c55febc,7babcd29,398d6099,23e1c3e1,92dac1ab) +,S(d7cf3779,236c4a8f,d592525,d30dc0a9,a3318c05,1823e0b0,43a6542d,f38d92d4,1f866df2,429a3dcf,1a285265,18308c25,262c1ea7,2aa2267c,39a315c6,dcd22ac) +,S(d2bb0f89,a0897404,25fc85ce,4d8fe24c,ca061808,e426b08f,ff49a525,137449b0,fc4c4bfa,2bf202e,4d10969a,2c4ca383,17a8c179,20dcb965,7de7aaa9,6b97ff33) +,S(832d95a0,cfb91494,5b43d9a0,4f037266,2ad4453d,353f6e78,47d4aefc,449dabc2,6ec0aabd,3c3981b,8ff55747,63bfb800,453f302a,8161de79,b6b623d9,253124f0) +,S(3a224983,f83c41d,33d3aa2a,c9d29ca9,55850388,a968407,664b2830,3967f25c,7cc31841,96c4fb8a,953602ec,3c79ac75,69e1fd1,7a263946,27826a88,7d651f79) +,S(81197d87,1452332c,2fb18793,c0ebeca,3d1b8a4c,f161b709,e3da21f,3917917b,56a0980d,1ed1f77,3c960de3,1ce4aad3,90a5ea76,fc410a2b,107ed82d,b8bdbef9) +,S(b8204012,c77ebdcf,790f48fc,4acb23f0,b03e5d40,6fdcf212,81545200,faa2b4b6,b8718e0,12ed5a02,16485561,8a5fb6c4,ee4db08e,dc9c1842,881287c7,dc7191b3) +,S(766d82f3,b602c418,d020596a,58e61400,20f58fd1,52448443,2f816dc4,8d437750,d17969a3,c71ea79a,3854b526,efd56f8,148b2d79,3ea3c76e,eb14ab7e,de5841d4) +,S(35fe7655,5d4c88c,dacad9ac,1bf125d9,924276c7,8c2ea9c6,a9c1ae87,52d1e323,9ce2d43,ef8fdb37,82a38d69,972229df,9b9c0c98,abad263,1df192f7,e0e75324) +,S(a29581d2,55efc206,4d1d1839,621daef2,ab049724,167ced73,7566f4cc,3a81f5ad,7eced1c0,575b6152,38db2928,964f7713,f65edd4d,c31a4b34,ae20b6a2,babaa8b1) +,S(d7787fd3,75473801,2370e71f,4d2575a5,b9f89e11,aee370ca,2660dff9,dc6a807e,2875ab09,bb1b2a7e,cc6cb39a,939e4a91,2ddb1dbd,5ca43ca7,93ac663,552ae91c) +,S(1fb6cf61,e8be4d39,b36578cd,8853da6a,1e62cc7f,29426838,a7ecf0a2,77395288,1c018114,2dfbefaa,6bfc2957,6b79c91b,16cf03d3,81285e2e,b74f71db,1986e43f) +,S(511e9c97,866e651b,c4e32e41,92f75019,42316ead,6a0cd78d,fa61e0f1,e76aab8a,2e8cd531,d55bb19e,886b86cf,f7105591,68a9507c,7d78d34b,9ec50f92,5485931f) +,S(74afe7a5,6387bd58,d5996a9a,9650730,44141ca3,1f5236e9,da977289,73c9d434,be7311c2,b4566819,12d13546,8ac1e26f,6113b960,3039c24c,5e77edcf,de452567) +,S(5b545191,25d5f8e,cfbae2ed,bb724782,d07ff380,10eca9a9,29ccc9c1,ecd0f04d,21126f87,a0cf1824,1040d707,4d37e8a3,f035471,63de669b,9f502fd8,92322dfb) +,S(5286fb50,1a525e82,a944d1ba,eab49572,52d86693,19af1367,eaa8d5c3,e73ee8d9,f2abec43,68f3800b,30da81de,5b81e564,f94942a4,9409bcb,a9faffd2,51daa0ec) +,S(7b01eb86,c102438,4be1f023,90a5ed7d,beea652d,cf3ef77f,92fd883c,f2993069,becae52a,915d1393,15435d9d,edb72e2f,fb9bda33,4b2e39ed,6e698344,9e1ef819) +,S(3955e911,874fd6d6,74e04ed1,ea3b43a4,486a0ac4,19735114,3b451c3b,3028a674,3552c619,a845adea,64951c82,75994959,8609a3d,2f691d0d,5947b474,4636e06c) +,S(b67801c6,d96aca89,22d8e7a9,96e95729,c4c29d63,180e4f73,dcf013f1,98eb0d29,876e3361,d82f58f3,f1292315,1509ed62,2e2a73af,d063466d,4af5cf1c,1ffbb150) +,S(1efe4004,1c7ddbba,1aade160,cec007e9,8abfcba4,7d292839,4598b5c0,1b763b10,d9f00f5e,d0868390,230168e7,c5839be7,cef8ebe0,b3b70d73,ebd92931,678dadac) +,S(2083d5be,389e4bd0,23f7685f,2ad00b23,e3687672,72183afb,d5d02384,36d122fb,11b6176c,c7554617,935304d0,4e12e8a7,4bacc20d,d438bf77,a1729a15,b424937e) +,S(8a9420d3,93ff0ba0,25bcbb4f,eaaf715e,4ab14281,85b2dd91,f8eede56,f3006e46,f48edd10,dff93031,2b5df63d,ec9c7a2f,ca373d77,65033de3,8a54d37b,2ab1f4c8) +,S(64a2e8bf,b42de0b4,b2dc8fd5,9e3b2c18,753b43dc,e914fbc1,478818f8,145891c5,82583660,40ebc62f,d5526ecb,9f952091,47f7048a,4004a0a4,7b08905,12ac098) +,S(eb97193,28c44e0a,3f2568bc,b99815a7,c5c2e7b7,90e7fcb5,a2e7f7d9,920404f,cfd26c16,e6709873,1219fdc4,aa8ed998,c6fef955,78d7908c,c72c04a4,4bcf305) +,S(cea3219c,e03a2433,f9ad8d93,73a6bef6,c1f30b3f,fa79d7cc,6ced36be,9abc9c03,13ea8ed6,a0dd8fa3,29bd7563,a34f3499,6442b03a,f2eb703b,d8d5228c,cc4c4e7f) +,S(2e1675b4,f00a0843,89b0587b,f0cb723e,1a833539,c024caed,36101cb2,bc4c4774,ec1d76d2,2752a662,5dafb3c9,6235eb94,f9af1286,2b3eff6d,2a0fb965,ad3173c7) +,S(b0eb3277,7c8d1b09,e22c9b9f,1c37e8ab,74be3c49,7ab6d73e,7fb6ec11,10602438,6d44054e,a4cb4a22,3811bbf6,6d2ab2dc,f7263d7d,6421a368,9a9ca4f,97fac66) +,S(48254bd4,8df24e2a,759c5ef9,cee30855,1c1f79da,b0a32695,2196ea1f,987b6d70,9c12a458,f39f4880,ff04b96c,72b8877f,b4ad0020,495626f4,1d9b32a8,84f7a36) +,S(e3c6dc6,cfd0b94a,57b3d34b,3a77f4f4,45328a12,764e4619,efef75f9,f49fe8e9,5c229420,500f2fa2,888d834f,a517188a,207d8f88,c98b7b7d,31484a49,c4d43a31) +,S(a8d9d9cd,def3362c,b260ad0f,3de51aa5,db066c74,4166d9ac,57db782f,656be9e4,bc696df,80eaccdd,bccc9ac8,960325bf,1a0e9aa6,100908b3,cd0c0ce3,43205db2) +,S(38281034,a34cd153,34b6fdc3,26fdc558,555147cb,534a1c31,89beec74,5a2eca97,f4371a13,a65de538,f31ecc5b,50ce4b92,d5dc5645,9e523851,f6fcdac6,5994b5e0) +,S(b745114b,449f0d7a,4521d7e6,728c89ff,54131e6d,8add9f3b,bf18edff,bebc6ca1,18678d66,781a9120,ba33e01c,ee3fb1ac,b7790f18,b30a651a,99b913c,e621eb67) +,S(5987629f,9c26a8cf,ff39955c,3144f4ab,c4d094c8,8270f3de,620fcdaf,93a7fa9,a554b7be,24e49819,f1256c9e,72b8d981,2aa5984a,c942bd81,5709ee03,67894a07) +,S(8fc9dd0,d84cf297,75ece75e,8cfd7c38,dc9f602a,b0152b01,982be4a1,f29b7290,ed128f96,2cf60503,7acdf6d5,9578ca8,9a232a0c,c432364f,2b661ac9,2a3176b5) +,S(c8b5b1af,90d64379,f2c02ac9,51ff715b,2978ef0,bc87f721,3edfd09,983ecc35,c295c0d4,964ef85a,59490ae,dbfd98bb,f6096217,d7633f0,44470b6c,a1816b55) +,S(b4eabf60,c44d78fc,298b2c90,6cdcf7da,a23d095d,867fd304,513f90c5,437775c5,82d906a1,2d10d6d8,e4c843b5,48ab645b,e6f32104,f07d8fd8,c128351b,e7b526bd) +,S(b19350e8,a5163076,bba0b2e7,2159865f,59a8ea83,b0154fb7,64308bc7,4d0bcfa5,29e4ca7a,554269c3,21a12b37,a4fd0fb2,d3e57ed5,4e79423d,f715bd2,ecda9907) +,S(fecf1ff7,663c7921,40f4deac,ced92484,8c66bec6,f4a5550d,c340896c,5543b886,b2621f65,d52538e2,92a50808,9008efa0,8f530fd9,a321bf30,6dd24f26,c9a1208d) +,S(ab70fe2,355d6d6b,5c5a3f5,a6bdb605,86372165,b8f07d2b,ccfd61f4,798dcb7,7adfb6d3,dd44adff,87ce3727,efef0bc,6fdf9f49,44fbd238,822974d6,dd57e8ea) +,S(5ae5d60a,efd1dae2,43df9d7e,ca025c3f,452b6c62,216c1c1e,8b315ceb,b94769b,d90d73b9,6e33cbe3,6d96c45,656f098b,e7e57d25,dd03be95,8b9cb2a1,d00bb434) +,S(fc51f965,bf8dbdde,b5fa4af8,7abee8a9,a380918d,4d8524e6,73f8501a,291eb96b,ceebbb7e,35eda612,47ad8c8a,5d54af8c,9dbc9bd1,194ba5a9,5844fae4,b658496f) +,S(368b51e3,778f8ddc,2d09e9b7,fc2808c8,ac793edd,244fe177,fe3229a,e5a6d919,5b3ff9e5,af4c6ae0,575b553c,c0ce17cf,fc4da66e,e19fc3f6,2d047007,e1716db) +,S(ac9c4efa,5f63ca07,35fafa0e,9612b459,1a764955,d435c14f,91717e42,26ec0186,5d4d3ebf,9a064670,e09f1d48,79bc9e3e,22198188,5610731,29aa403e,73d7777a) +,S(2b5f951f,360dbecb,9eb1d31d,b414aa13,9d9a7d7c,ed952a72,c0f93a61,d52fbc08,8d46f1b9,cb8883e4,cb504715,eae4326f,11187e46,de477dec,2108ccbc,2b3ddd35) +,S(25a3593f,9670924a,a35c2008,bd8278a9,a78d22d6,572841c,98c4399e,26f67cfa,3b07ab0b,8d400f1,af88561d,af8bb7c6,a6e4b7c6,a8bf5915,4cd85291,66ecd965) +,S(3f03be51,b35e265d,d9cb974c,4ac021d9,d22b2291,40016bb4,e9edf52,36193eca,a17df05d,e4b0e5bf,c7ecac3e,e3253017,cc1d47b2,9fd3d1d5,411f2660,c34932f9) +,S(edbe9bd6,f16782a5,a00d7003,488b9291,faa4f22c,9602c736,a3698587,995d25d,64569751,212d2f6e,c2e7a6a8,973be7fb,c49d7a0c,8857fe76,f9c48011,735179ed) +,S(1f477d33,1fb16ac7,45c29e48,77df8c17,83f69e85,5a111a30,e4717fc8,ebf85377,64c947cf,64a66ad,8a417bae,1bbc1cff,56826349,e024d3bc,bc4a9078,f4bda708) +,S(b8ef8b11,901602cc,9fd1559f,c4bd6bdb,22f9bb7d,8b289c6b,fdae85c3,e9aa3e9a,60b3594a,7349c920,33d816fb,295f41b2,7c4d1d86,b4c9d2e2,2cc3f4f,e603f582) +,S(eb6b31eb,198741f7,49e4b69f,85c23e4b,58e3223b,df8537cf,60a94411,f03e0071,576746c8,ba579896,969c228d,67a57cd8,8501e27c,773a3444,35b7e860,bec9f471) +,S(47193aec,aeae207,ad675228,f4506db8,40a316a2,6cdb328c,2af6c24b,bd5e9a8b,dc67bbbd,ca7cfeb5,981571f5,7022986a,4ec3e408,b641c34a,57b7cfd5,5139a1ad) +,S(ae3c9591,c2768a02,f99b0076,9c56fdf3,1a98fae,1eca3680,38698abb,1d44f961,ba9b0c42,90c2fb0a,ad84754b,e1c3fa0d,7e34f737,f3874af0,4ed2824a,46efdb24) +,S(178ef542,8c4cc38f,f088c383,71f0ead1,4e7b4423,6d90bc7e,9ffc3db2,fdbe9b9d,a5f01afd,74aa9324,b10f6041,27ccae19,24da7b23,72269ef,ab984fe6,ba1b347b) +,S(2df7e5c1,3d0fdda3,3ef8f69b,f0ebe1d0,8649b106,8c965d97,37a7e9b3,c13f4c92,eddbf5c6,324853c3,7d478864,a68d0b40,2c28ac46,295c00c2,2359e10d,d0693d94) +,S(671abd13,fe274da6,a5cb6119,f33fc88e,37ee1b75,59adf215,e08fcead,ee946b8,49d7cd3f,3b8162f4,a85787c6,91bc29fd,69eaccb7,1354bbc2,8ba17227,8a8689fc) +,S(b69fbdbe,e72b1418,6fab59f0,6b57d940,fb8cb5cd,92c53727,a0eed42e,532ed39e,71f488d2,e104d21f,c816631e,d774a714,94c0c609,c86ee052,210113d,672ea302) +,S(1d5b8e6a,25fcc50e,a4a5429b,6233e276,47d978d1,28a5f495,a66b1b12,d7cd8714,8428efab,d2bd23c3,2de8da3b,cb630ee7,ebe84541,ec3eff60,645ec4fa,3978a6) +,S(4844a10e,ffce265b,1a5338e6,a10c4f18,b95b0681,bd702e30,9a376e23,7fcefa22,9018e1d0,34bf225,b4826e42,565a76d9,43c868c3,168d74b9,33b34596,98b5192b) +,S(9567ea6e,231a9c9c,3ea5bd83,59b7340c,dff96e3f,d1fa7a5b,c56c88a1,a57d951c,507e21e0,cc59bab1,f2c38cb3,42b9f83b,f291992,a0c83edb,4cb62b49,6c54759b) +,S(60f2f714,71258f56,6de74774,eda196e,46a30d66,3dc0b308,1cbad662,72f07bcc,12588be3,f62dd2c8,1e485efc,76c754d2,de642f53,d3937c68,f058c61b,ed7b6c22) +,S(4276ad32,b33ba53,ef2ab0af,9fc42af6,6c7bb23d,c7b7a9df,9c00e1dd,f76b6283,9c477729,c61fccfc,a2dc0c3,c3a9ac89,b98f437f,bb221be1,268a6f17,5d0dcd9b) +,S(fa3ec157,f8186c36,3419d818,4473745b,fdd2d054,e0c16e1e,fccd514e,95c9336,a7864a68,91aec12c,fe8104a0,eb3cbec4,4a907380,11a3acbb,d5fb6680,289cfa2a) +,S(7d7d2728,b1db5fdc,38ad6a75,3f39df22,88d50838,b106475e,28ba6eb7,5248b600,25d9d454,8b505739,16ec7bcc,877f6aef,e2641eee,8f78f1b1,a7f74c11,40173e1c) +,S(8cb9cc7e,4ec87013,b6994670,aae06b1a,a4785c06,614ca24d,cb6534,6592ffb9,728c4a8b,36bf36b2,a0bedad9,144c3261,71df8448,e87d151b,d8bee067,769113c1) +,S(8a9aadfc,84aa81d4,46442635,d47a9a4e,988e64de,6fe8836,79b8de44,f57c0169,60f39bce,be18abd,10afefa0,2d076d49,73d9615,10017a1f,469eab8a,c15eab60) +,S(3ea01e46,3a9bfcfa,39125216,7b6ce771,5fb309e4,37d495b7,852be3c1,af2a0b5f,2b756a06,75da2633,b8d2650a,a2102738,d5918420,9c57dd64,7b4c6c2c,c5250252) +,S(a0683395,c5245bdc,24b2b275,e8c2a196,5068fda,253343f7,49ab56e8,93672c4c,7f25a7ca,92d25547,975ddfab,fe50c247,6b4855cb,8f9ec4b8,ecbe9271,779431ea) +,S(b8293e68,1e33b654,7d2902ce,3addaeed,2fcc021a,cf7ee396,9be12661,b2abfb5e,7943e6b6,fd0c90fa,824b1e8d,25d63a1b,c01f16fd,3c9e2254,e1dc35fe,416a5afb) +,S(412722e8,ba1809cf,2df25d5a,49c7648d,b19e42c7,3cc30b7e,1107ee4b,f0aabaa1,557a299c,38ef6a75,61d79a10,ea052a52,818ce67e,1b341c1a,d70b984e,8e41fa39) +,S(7a62c6fa,f4a1f6fe,4a45aa48,a854a16d,a2fb19a1,a5647e3e,28a35d0f,619b2844,dab31641,6241ad42,3e7ee774,acb52a96,26b4b6ca,a4ea0b4a,67a513fd,9637dfd4) +,S(c3915b0c,19df99,ac0ef05,3a07b36d,62643630,201073b2,8e3ec588,6714a695,8f21f136,3cb4ff42,a52f74b4,b10f1dc3,5bcd782d,b477ecf6,38866d79,541de3bf) +,S(2a373fd3,ddd12547,3e30efe2,5533316f,355dd52a,6854d7f4,8144d19b,648f4b59,2ba90aa0,b3de3887,c1ba231f,49b28294,677adcb3,2e81c2f0,3c563e0d,221260a3) +,S(d8729e12,46cc8a52,a5c9b7ee,dcdf4d3b,3d0ff8aa,6efafdc5,ddf37480,2dda4476,1966d7a8,7e527a7a,bc1e829f,90e4e3e8,f4a7df30,48f098c0,df8a5eb0,2a8bc40) +,S(ce45fb7d,2ccb83af,dd1662e0,3ffec83b,5173dd2e,448eaa87,edc980f9,10a20dfa,5973238,531c5a84,1388c656,3d4f3579,41283e31,44d84ea3,31374d8d,e2122244) +,S(230a69,f0fff585,a1163702,4b16481f,65bf27a5,3ad7992d,47ec8ff5,edfe073e,61ae3fbd,90f157ba,6332de25,a571777c,30d8144b,e12d9a25,42bc1877,1b6abedd) +,S(d7f91496,1bdd4fe4,7b538429,df2bee94,cc4d66d9,270da392,a85e8c62,1527700b,67ee9184,bb2cb2b,9671aaec,57634814,b6a1a9f5,dd0430e6,6c5e2774,5afa9f13) +,S(4f2e459b,3eac7349,41cb81d9,2d8b3942,ee7b0ded,b2d9d8c1,90ff390c,aff7e4c3,4188f6be,c3526afa,2b29f953,f5044bf3,8e583c27,395c9f8c,979ff539,b4198e17) +,S(8a4a7b6c,3b598e42,851b0913,209a95fd,edf0f8be,6f152b33,1dac61a2,6f9b6997,53c457a9,f8926415,63e85b0c,b39ac9ee,69b31c65,5e3bc200,d37d86ae,d4291997) +,S(a48f69c2,9dbbc922,78a5f8b4,4a2fa2be,f432a4d1,e6fcfda3,8ad60dbe,6d157990,f251873,152f4ec8,fe5fc88b,cdf28ae,acef895b,1d0f0ce4,44105e14,7ac0ff22) +,S(fc0ec54e,20f7e8b1,fed5cc89,d18a5004,b7aa55ba,7520dc18,c9cbd935,7c78eb4c,17374d8d,36116ef6,49b723af,62a48350,4bd47bd4,c17c7990,fb0e119b,47f21ba8) +,S(8203924d,9fc7e5d7,6e3c593,ad439d3f,8512c0fe,5c298163,c8caaa3d,f7b39755,a224b743,258a82ac,2dec871a,dca1dafc,2bfa8e33,b2217785,2b97e57,2e1344f7) +,S(c4f66a17,a595217d,58d5b5b5,da997a7c,79b870c0,f5fa9dde,e146fa5d,c21f9380,13bc8ef7,ce6f915e,fd7a3522,f5fb9c3b,ac603d0e,343d344c,74565eb9,e8e3c777) +,S(4341f0a1,efdd7d68,435f1998,559e43b7,7d1c6780,d3d3e7e6,212efd96,5b30cb47,1777a450,1a693970,c2bf759b,a253e716,b17cd5eb,9a247d47,cc382424,48ae90a1) +,S(865d8a50,ad3b8c21,f8ff0e92,8e853789,a607abe6,e155b04b,3b81d80,c97c29d4,2a506b83,c4166a1e,fdc32c2f,b2d027e4,837d9989,a82d08ee,a31a46c9,dc72b272) +,S(d8d95cfa,ac50b79f,3189093d,50b03b38,3b798532,3f01ba52,d76f033c,83c832e2,cddf6560,d9942228,5a3c0f18,58a7c27d,fd7b8bac,9b23477d,8677a1a3,c01a8454) +,S(d09fa59,8ae65883,60247b05,519aef86,2b1c5196,d38aed11,e8350fe0,5bfd6cd0,45f51a97,67445680,37b1bde9,86696834,6dfaca49,719c5174,a9f9eea2,92ed4ab8) +,S(e19796c9,c9b48a5e,59556b72,9d9b8073,71cd7267,812b044d,637aae50,d6d3d1d3,1b86acc0,d904a31a,b5637e18,872eb31d,32617930,2f3d6bb9,36016653,9f218d89) +,S(6a88899f,be0c3c82,96fd27f8,6f89283a,f83df13c,a273217c,fef69d8,b048afb8,68ba38cc,bee044a7,a026ada9,d51e8d49,97083dfd,f65bd483,a45eb58d,5cc774b0) +,S(e5082ab6,a112848f,9f5f3362,8be3e267,87e24cbc,6d5563c0,7addeed7,ee44662,c3c1727c,d0c09130,8323326d,210a68d3,68bcbf81,bb3814f,dcfe6631,d42968da) +,S(795f456f,5d0cc15c,72ea286c,c881d8a0,21294e05,cf80ef7f,4497caea,92235487,bea99154,424f54d1,e91322cc,c52d3a51,4627fb1b,2fe9062d,91d90177,20530ccd) +,S(6e9e5f96,8c233f4d,8d2bcb8f,25d9232b,f2230e9e,fbafc89c,dca17498,8d7909ba,3f8b0b0f,e60aaf3a,89f5de79,35f9979a,ac3f3fc6,fb161d3f,29ab2ad5,d50411e2) +,S(72ef1e88,89cb7f22,19b4a7ac,92d5678e,a04c898c,5b83128a,1f7fa8a1,63772f28,dffb88e5,3f348c29,411e4d47,3ccd7d41,8a0617a2,6a640a9c,4b03aa08,15ab10d3) +,S(4a91fcd4,32f6d2e7,ad20787d,749524b2,e3e1c348,31b41041,c311421c,1aff04f1,bcd9108e,6e8c6da6,a15156c5,5c9d60be,e4aecad3,b0756cd5,81b1eaf0,ad300b9f) +,S(5f5255d9,8c7465d6,63fb4507,ac985629,8a434d5d,429d5a9,7b645256,e2cebab2,7c38536c,31e7331a,362fc944,38510aed,bad4cf29,bf0e7cab,c995ac6c,1fec04ec) +,S(866b5f8d,a735ab93,e84f7811,a33f1604,40a51373,7b52e675,7f212c8d,65c9eb05,af2cfb6f,9e32f412,5f1c66ac,61393756,b8c6016f,9d45a58b,43e16c6,d3549be5) +,S(636aca,387e767f,4702fb3a,d7b50b4c,4b40fb78,2c8c44b3,a298051d,bb3a71f1,70e580b,6993c9ac,5a48c2f7,558773c1,f5c4ff3f,aa929635,a2a44e97,c0eacae9) +,S(31a28d8c,2205f5c5,75ddd861,df94bfa,5b6b6e38,83797d1c,7d4a487,5fcf7f7,a790eae6,788407dc,89df860a,9ac25011,8b8c65f5,f47e0bae,6aae95ee,2d733698) +,S(c7f36e08,bcea178f,7acb0b92,fdb32411,a028ce88,be5e3480,a47c2c88,891827c8,5c285010,4742764e,7df86e80,e6e2d975,472abeb1,bd5664d3,a994289c,112a9e9d) +,S(85a82c69,2a5e248f,42a3f0d3,e092840c,de52e31f,19a2a161,85b2ad10,6afd92d3,eeec281e,55c79f17,332c4ce0,7ac4edc4,a3d75e67,558b4b3e,4dc86532,6132155c) +,S(e67482d2,4c2e3a75,3c1fa968,bf120a4b,7883d00,950bb0bd,ef5472e0,dfb09287,f9801b51,e0450fcb,9e405ebf,cb535355,24db5e9,91bf1572,6f446aad,34ae4194) +,S(c6ba17e7,81fad656,c9c93e54,36409414,c58e2761,b0297b8e,1811dd68,62779f1a,92583a9c,515a096e,8691b384,4104419d,c4e86966,4da70b3,afcfceb4,b9d74c92) +,S(67cf02c5,a616b59a,ddd421dd,15812e95,f36e6d16,af501663,9e60dd4,a8706b24,7856ad72,8a5f0d18,bf4bc3d3,75468f85,7bbdb1c1,206fcbc4,7ae66b5,6ac91b29) +,S(72bb64a0,aa8c690e,7bb6ed99,847488b9,f37490cb,a7fa120e,a7b5df10,8fce4b61,6057c37e,198f6428,3b9481f0,53441e97,dc827cde,edc55410,5108fdba,a14d81bb) +,S(1b132067,209e179a,5b77977b,eb81aea1,c480b32d,81729c5,32d100df,310575cb,67482a94,b5ddae4f,cfd613eb,68680dd6,c8172553,18478bf,79b5c07a,b0cd817b) +,S(96f5f126,4b68fd7e,f633330e,f32951c0,3716b62,7d1f3368,703447,f9dbb5a0,941fa02f,c6262bcf,dccb1cdd,1637c9cf,719077c8,1b26e7e7,2bb8fe4a,4d530e19) +,S(95335907,9c15cfa4,a674ce65,113127f7,a1ce740a,ecceef55,da5ceed8,e4e56a51,57ee067a,5f506b14,5c1c20ad,6e5d6b11,9bca103a,60c2e6e3,b5049b4d,db15baa) +,S(72ca7d41,76c687b2,1125c290,e737075a,3281ce7d,76b72725,c7680956,e0463f23,1e6a85aa,246b0f1b,324365fd,19809840,242fef0e,6d658cae,cc84bbc3,ad56af44) +,S(d5376a10,2cf819b3,a439feec,b04d9fa3,c90d76c3,b9bb749f,bcd6aa26,39ed5ea,3659d2df,266a0ff9,c2854f6e,f5a5ab04,1c0d8547,77fd4f75,320e064c,c4d0a4da) +,S(664a26cf,a965b59d,10c05dd6,e0aa4e6b,c66f356f,bb61b699,113a83d9,e0a2b4ff,fd191a92,c88d577e,47e725eb,a3c38b3b,9074bc42,16c5d6b,4edb3af7,2c4e20f9) +,S(7729abf,ecf9ca40,e8867936,7fef2d84,29bd877a,56f36780,1e677273,8e8cba7e,d5cb7517,42c9de11,84dcbfb7,5c9e139b,a59b6255,b59c7ef8,539de55e,7a709d36) +,S(c6d5f50f,e8e784db,630bc589,b4df5810,a2001482,9ca1608a,cd3e3634,7bd8141,eedd06e7,95d063ef,3d1627f0,bc7ef37d,a4846580,61e472b8,89679d3c,d88ab294) +,S(baa60dda,3cf83a11,82dc2ef4,f79d362e,6b32304f,896d30a5,b363c639,56ec70d9,cc9e6274,75feba44,b93451d2,753e94a2,85315277,7674b3fb,c67490e5,3627043b) +,S(370243c0,92b957c4,74df755f,e0951321,49dd3669,1a89fa84,e43a0668,8ca235ff,bbe8ff04,6600f245,349fbf3b,1c8ff04d,8d51185,3e35d13b,30deba91,84383ba2) +,S(1182291,92b1e768,2abe56b1,9f90e41a,c882edb2,27b25559,ce4448cb,dd19ff06,24271274,e6156ea9,e3f82ba2,8a72d476,8509dfad,b985c200,244687d5,8a11c1be) +,S(dd134e0,e4fe672,38716aa4,edba1ad7,6879be6,8b5bb029,b32704b7,9edfb2b6,7aed5398,40cd7dd4,7e94c224,151627a5,62f6a519,2f0d0b5c,416e6f1e,5e8a337f) +,S(ab7e722f,458d3acb,ad28a29,2f90cd8f,c2e09b2a,2beff3f8,f08517dc,e673e3a3,522a2227,c724f9bf,1ae08d93,c4c6ffc2,f5434173,7fbfa502,d6e49b1c,2ec4c792) +,S(2fa16842,ebebd6fa,e6c67327,bd5fdcdc,7e985b1c,22dfe307,23a9e2dc,ec43df8d,f027937c,9624aaf1,ccbf7c58,66bdd9d4,5bbb279,c2a37813,9166b720,32030773) +,S(adf5e014,e1f18c38,fd1863cf,76a6dcfe,65827107,fbe0eb99,117a859b,c5268bc5,c9cb3584,728b1771,16985b28,f5259aa5,279e15a8,1c1cc6b9,2b9e5f8a,6206961d) +,S(d939322b,a9b2cff2,6a2b1c4b,5ffcd96d,822dde66,ad0aca31,c24a8260,c9fc6d26,162fb654,6118a68d,7fb88bdb,3e3ab784,9278a5eb,13a940e3,3228580a,2258c8f5) +,S(18fb2621,71f79e5f,46c0087c,ac8e55d9,66f29a49,e2c91363,58900787,3d7e3a6d,cac82dd2,4a0f75ee,adfe906e,ccafd36c,5b0c97d9,cb150fcf,d08b28cb,83787a05) +,S(b8c0ac07,5d009e4b,5ee44298,89864cf6,fd54092,df835ec2,6658c902,57b670d3,2dbc6d4b,6ff28cc1,14e1f34d,2b6cbe52,7ca7dead,24f84681,4407c4e,a0df0bdb) +,S(6477536e,43a3ba01,8988c8ba,753d732f,9ec061a3,49f7826e,2c426ae5,3f2232bc,ede465d7,6b7798de,832e5f68,512cf3c4,d17282a5,ef88725b,36d8af1,5c5d2679) +,S(55b052a6,52008a83,41135e55,ef7de2b1,33eca8b9,ea0c4800,ce4f8019,5f7067db,a823cc05,63efbc19,d47c2c92,5771497a,ef6377a3,8be0d23c,81a56f06,25c43e56) +,S(c86a02fd,becc3647,16c58f5d,f3f150da,6ba86499,2df09dd5,82b9653f,10cc9289,263d1b5e,eff52a27,1a9c46ee,8adf1194,1da9d3bf,e69f62b7,262099af,43d6fe3a) +,S(23c659e0,3a59381b,3d47beea,60f0244f,66af0596,5f903297,c53aeeaa,d143e90a,1c14650e,3aa16bd5,b67e6077,be307cac,2bd2cd39,b246d6f7,216d73dd,dacbaf64) +,S(2f6d8482,734aff4e,21094c39,2c5fe833,1c8f9756,4de252c2,f6f690ad,8b2fa9d,8b143db3,7e14b40,3fa98a74,a81adc79,c34f17ce,5c9e6f21,68770957,1eb639ae) +,S(6d58e265,d6b08c0b,b050ee18,5175a9ed,51b5c64f,d523ab35,ed1457d2,f9caf153,d0675517,eb71ed0c,4fbb86c6,a2b4a7df,259a7795,3293777f,3a87f620,2265edee) +,S(4af09f2f,69d7b9,6676f472,d7f2009b,f0bb6f0,986f502f,322afacb,7e9d570f,1f93342b,ddfe5896,52e8b64f,a348f333,5396012d,3e870885,5b31024,f14f8e7f) +,S(813e446f,87d923ab,ad8b8649,71b1ca39,d00217f8,232a71cc,d6346798,3a9a0e9b,8ce9f4b3,541a8029,f1476728,2bedf1f4,367ff257,60f2ccce,7b273b2c,dccff418) +,S(53c4fd57,ca0b1eb8,7f5ca0f2,3ecae8eb,c44e9c19,aee3477a,3ca8524e,dcdefaf7,b3db3613,aa916430,a3227d73,35bd0532,74c122b8,18e4ac52,858e513f,c3ab95a4) +,S(e0f8afc2,ec89411,d926355,2c33af71,328331dc,9c9452fe,b0665c39,ae90121f,13523c16,1a00784b,f15d2867,15cc05ec,227248ff,80082e73,ecb139e8,f229eb4e) +,S(fe3efcee,6b82a5b5,a8c9a51d,ccc11f01,ef8f1a7e,588ec4b,17ba1369,cc6bb80b,be17246a,4d8660ca,98d57f07,ddbabc29,650f9a89,9da60a53,d21c6c96,dfdee15e) +,S(8a5182ea,591089a5,11ba9f19,ce4fe062,31b7e2ce,ec4cf75e,5c11094b,9ddc8de5,73688cc,f13d97eb,19a86c2d,8b010406,1c69ca94,fc9ec90d,8ada10ae,37503600) +,S(2d95aecf,72501ef8,e20bc117,22dbcc09,38c552f8,f4e0596c,9974d62b,a99fc884,7d9c418e,e1745ac9,e8f5e4c3,9ada4400,e65acb22,d336bcc9,fa2cdbe9,97f7de88) +,S(7908e4f6,ede4d311,3dfaf114,9bf6e40f,e1f8e33d,72094448,5105a113,21d18b80,11d92d74,9b011e83,5e06d7e6,1103cbe,bf958d8d,bd47d0b0,1ac2ad22,2f7d275) +,S(2c6dc24a,687d8437,44baf725,c75e7524,c0e6571,f32817f4,40183b6e,cbaa9f95,9692bdaa,775b832b,48584ccf,713421e7,5074f1b7,ed5477f1,4335db2,7abf03bc) +,S(a019d9a0,4a9780c9,43dd65fb,b87534cb,ab7c0831,f845e724,d663578,ec7bc090,38de35e6,faa2a1ae,e0649333,898a6ca9,264dcdd5,ad9f289d,ef750110,9bc99d4d) +,S(ced934dd,5c15335d,d050af1d,a8295d2e,bd9b8272,58d689ac,f4a48d85,5ef8a0d9,bc0c9237,1ee7fe7b,69c7c100,e5258c9f,1d68b6dc,309736e4,e05718dc,3a49cb6b) +,S(65e5ffe6,502ea7a1,47fe9c98,9cc745a3,b5262bd3,888069c3,92d1d1f0,a08dbb83,c80ee080,84dc5e65,de8df271,c1132cab,b8be293b,69a390a6,fb3932e8,51ffaf38) +,S(f4466d47,2365b665,6d1947b0,6e67e393,4c0e4f3b,91d52ea6,1f4588f2,4d217655,17afe3f7,2d384ccd,9beb59c3,d64353ba,57713f03,b8644e3c,eef45db,c74ae84e) +,S(66a80bd8,ec0d08ef,3d9aca5b,4329198c,b949ce84,7933ef2a,10baac28,dc98cea,7457685c,600a707d,5fd5d527,9f309b9d,5b14e668,1656869d,b041ba77,97029537) +,S(240e3632,7c3f4c63,afdb84d0,8586bf90,9b359c8d,7012f9ff,aec81986,4c44d597,a701b76c,d369e013,7b627216,f75d0ec9,fa208b66,2d7075bb,a6b6d39c,6a1cbb30) +,S(137f0df8,cafdb26d,36e1b57,c427ce08,444516da,ad1ca806,9dd60e2f,2ee23f92,c2922219,9e4e7bf2,f9159138,13e68273,6bb6a998,6c015caa,43cd30a5,aec74a49) +,S(965fef87,a63bedda,fd4b2e35,e1d0baf9,17d5850b,1f6beca4,64d4fae1,997d3a41,5f19603,369108a5,c599ce56,22bbf02b,cf541867,a57cff2d,8b527148,30124ed3) +,S(1e2ca98e,519c60e6,cf7ff3c9,51cde109,209188e0,6c68f4dc,c22eacf1,34a4125e,edaf4c8d,8bb97032,89f9fb0d,d0023783,fd35e779,41773a00,fb4289cd,ef8411bb) +,S(e8e0e2f0,378595db,68f96e6a,882752bf,df039778,e261375a,47fd4394,2b68006d,75b42822,1e2fbf9c,42772d35,e4bf1e0e,8d3b1ad7,92ff25ca,a488b51d,4ef7186f) +,S(a405f5b0,28e89d81,79e7fa9c,2ebac86c,ffd20ade,60dc5226,cfe6c91e,5c2f6634,c27ea4e3,b0da9e58,665f507,84225107,7893d6cf,5225d5d8,a06f68d1,1d863550) +,S(bcbc3c77,7367168e,169f3fcf,dd2340cf,9e6af955,d6038a0c,b763f036,6f4564cd,c7c120cf,99138201,40ec5339,ec23f5c,1ac0dd73,c9f947dc,e19c034f,7e7a502d) +,S(fd569bd5,14843a2d,e86ab2c0,475e0c67,4959c04f,bc230255,db3e82d1,15cb0758,aea9c73b,bc083366,fc5e31fc,90a1da72,66a9c43a,9d16f6ee,67bd56b7,9c354cc9) +,S(c2fdf561,eca2e3b8,f1107475,9cc43b9a,789ef592,e8afe885,d88b9dba,469c616b,678f57fa,fb2e42fb,bd1482d1,e22c866e,683e4fa7,6c1d2714,6540610d,9cbd1360) +,S(50f20965,510b3317,2836e1e2,d6e00ea6,160a9c71,2aa30bbf,ac527c62,9d8f4088,fd9852c6,974c603,7e88bf45,a279b565,d1246924,e3c4a0f4,8877b716,69b68141) +,S(3f293417,1e371e20,cbe0a858,e5984175,ba465906,fd64a7e6,161c8a7e,5847764c,763e8dbe,571ac3dc,1e803f32,7e2f4585,7268774f,1a45c50,9390dfd4,96bd374b) +,S(8276d399,fe32e931,65f2ef6a,2f4198e7,944bb9a3,226b1a55,fff52dce,d2b92b3d,e955a591,5c496a1,623466be,88c45a3,f545527b,4edd4283,582c03f7,2a2d897b) +,S(e77e7701,f4b405c3,5c8b7a6e,ee7d0637,fa1f566f,d2a2cb64,531dbcc6,19a9e474,23fae4c0,7661e588,365ddde2,63b97f2,cca2023b,c70633a1,41a2eb,1d3799cb) +,S(7ae8112a,732d6428,25aa2eef,10298300,42939628,69e7eea9,3fbc7b3d,2c5210a2,3590e349,f878af6,7f7754f9,abf4ffdc,4d4f4442,48c1e039,6071271f,be5971ce) +,S(f9fe96f6,8e20a422,5b563483,c1d2389a,ebd8a97,b1b9accb,ed7bc51,8077771e,cf3802de,20b5ae42,b1d1db65,b033f2e3,602ca08d,972eed63,9234e1c1,48542478) +,S(b9c5eb6d,ab28292e,49a76b34,49cf903c,dfe79b85,f7797623,924a4295,bf0cd171,adc0a5d8,18926d8d,7bdc83fc,72b2ed11,19b87592,900ae961,ffbb165c,4d5bced1) +,S(b7e25a76,3f3b8bd6,1c57da51,bd90ab57,1df3fcab,28003104,30c44fa5,4a6a5765,c471aa74,babc3fb7,dc80138c,83940e42,961f80a3,23e9741c,c44607a8,258141d) +,S(5808e4f3,e01937f0,e9c887ff,8ea3a34f,8fca4d8d,7868a9d5,7de6d854,a0582d71,fee8cfef,2d78896,55dc9560,68e1a23a,4308a8f7,7962c2e4,f90b2ddc,87f011bd) +,S(92ac7dd9,8da0a4e6,60964943,3887974c,5ec4b32f,875c02ba,1163d06c,b424793b,857b2046,8104a986,a0030596,62039e60,71c8f950,b7f7f746,16fcacbf,b8eee5fa) +,S(5c38092f,fba5798d,48d0068d,d1037563,1f40a693,194584fb,7199c409,b85d52b2,32b1d628,15ee2555,f582f16d,d59c4658,67691e1b,389d1fe3,222b5444,294ab391) +,S(ad7beb62,cec8aa9a,b619693,eb40c477,53e22897,a0007693,f10ce664,21d5c15e,49f98176,a360297,42402a93,26e34a38,cc837278,3873b076,f887c811,e2bd98db) +,S(aa335f87,c61f2e9,7d87355e,2a1c2e51,f2a0ac92,1cfd3cb7,5189f256,386ef185,b39741b2,fc158d6a,435bfa8e,1a68eedf,13deab27,388032fa,fa22d649,c9c9a8a0) +,S(854ef509,24fa2fb4,9b7b7b49,44ad8c9d,87627883,60ce8bd9,36e64f12,a550356d,c5af2246,96c7e32c,a385f7e,3eb8326b,7d9e3537,43a95c8f,3010a160,1d6f534b) +,S(71b6366e,42a8e2c5,31ad6770,ff01481f,5a39c54a,d38e0ac6,f068117d,8d5c9d5c,d6684df5,92f085cd,aee8313,59bbceb0,1a357edf,b36f8e14,706245ef,f01db334) +,S(3a4d1869,55b698d0,5e760d75,8842bd1f,cd869e84,c2c29e44,4ca83bd0,3408d6b4,5ddd1b14,15ff793a,80fc196a,83ff2736,e9791418,fba7eb98,71b51269,170d9904) +,S(f596d242,7e51e5ce,403d673,cbdd71b,75550271,dd2d7d93,79883258,9d3c5739,cb9ef0ea,f6326a5f,83e40428,79675f81,188763b1,ec06fa26,3d7793cb,82b4ceb0) +,S(4bb25028,b5598090,15286b2a,a8858e52,227f3b11,9075077b,3083fe16,77efd4ef,cb63c7c6,9de6503a,d33ee35f,95d4533e,1e978f30,2762b478,e88d18aa,5c4ff098) +,S(77c7c018,4c89f974,df648e0d,ea57ee40,73acd63f,c6e22a55,6269ca4e,a5dc1c99,978faba,232a2b0a,9cf3de55,976ca950,ce3f197e,e774b2e0,d4bd1e5f,e65a1b41) +,S(834d038,926b1ff6,13dc9001,7d021bf4,5bae5,d3741460,db681332,61975c30,f5624b83,614f5e82,10190b34,9f966fde,cd4eeefb,e3e6b046,b3028d6c,3807b475) +,S(370045a0,a7f8a64c,a2b9c64e,85a07fe8,9a7f725b,2cdec8db,258b3d3a,d9cf379e,cbec192c,cfc58b62,6b89d6d3,9b4bf622,81e308b2,2aa3ddf,51d45561,37b1811c) +,S(61302164,6103ee82,1c3103d5,f4d2aba4,645816cf,a67d94d8,ca53dcc2,92ca36d2,935e3db4,5ee1789d,ed8f0ded,ac0430e2,7317f38a,87682850,9568feb8,15e20a1d) +,S(4d6868cd,4df5b1eb,8d27e045,ef04209b,c1dd0bd7,aa712937,8f7bc025,e569d90f,dc0079be,850ab0ab,ef9881ac,740140c6,958e12ad,edfcbd79,2f446d78,7157780a) +,S(43413ee9,a35c7c44,bc95369d,38d9e7ed,53175f17,6f9eb54,6df98540,93105549,9eaef64f,45b421d1,ee80731f,ed61f658,861feb4c,72b5b2b0,ec659825,63172f7b) +,S(1e728340,95183b28,a443e00d,6ae01921,75844d9b,ffb1c77f,c00d388a,80a6b76,d1931ac7,fccf26d3,a2ef72ec,e8c7ecef,c51ad264,ecca5748,7e9cfe5e,d798428c) +,S(7b5995f0,fb0b8246,c588e393,4499404,415f0d03,c9174317,6cb2f4ae,b06960c0,716c7849,b6963a9,f1a7998f,49cfac4c,42a329f8,77d7c8fe,5b35f958,ceb9c0eb) +,S(9c5efff9,2be6057,f5223d41,87fbafd1,a59c364b,239120a2,fa1c4596,4fdad960,4f4aa66e,e42548c9,4d6cafa0,81e59ce7,ab65cbd,78027de6,b1edaa,25e6d7dd) +,S(46970472,192c3690,3c7bd061,dee19fee,5f3a973b,a70430f3,581abae1,3a61e55d,4d232a77,62e04a36,c62894d3,41665ff9,ad8ba55d,353d52cf,44f392cf,7787922b) +,S(add62a3e,583dce2c,55e97257,b64c26bf,5e86b0db,92962b3f,a54e7c52,ac00b32e,3c700f82,c5365611,f3cb1ca9,cb6b0c2a,dda80e04,617142ef,eccfabee,178d3dae) +,S(526115b5,34b212fd,9836a98f,853ab3c1,24bb68bf,afc1d641,b47d08d7,39566c4f,29c983e6,2a4de19,4efa5b2a,9cd4971d,6a86c8e9,f535d599,ee04db95,120fdf4b) +,S(87908ac3,ee8f4e5f,31aa08c0,ac97e93b,81ac75fe,33bfbff0,7d4f0934,7c911f45,b756def7,52c71c51,3f69235e,8d515d6b,f3289a5,a579b999,a695a2f6,49586ba8) +,S(2d6e6ed5,d2ea8912,6797b396,8ac1b0e,f876904b,acbace80,a2613b35,d9bbdd11,b1b66613,50ee7a2b,d149ec9b,25a502a1,6ba6143d,32da00f3,13cc389a,7c0e480b) +,S(f076c9e9,8590180d,dfd36cd6,160c21fb,7dc60263,77435e74,30f74cc3,c46533f9,2c9decd8,6da7b49b,1b4d0a8f,47aaaf9d,19d9e8bb,365c383b,63726f64,ae56fb98) +,S(fb52489b,6d101de1,2f51720a,60a78207,84ab4dd8,39d05c9d,a3b9c349,a74452d6,f1a967ae,deb5329e,faac7381,f3f0bc76,9eb8cc89,78d5d56e,f382a17d,4c3baf4b) +,S(dc1e911,53b3435a,8f23cb08,a7b52f64,a5e2870c,9345896d,ab4ab880,9710241c,3468d632,eb3a51fe,133381ce,2b5dc9a1,64f8eb68,9c019b0b,7e48d504,50e85630) +,S(2d8c6759,6c7856d,b7062c63,2aaffbee,46779bdc,777f0be2,a64093d,41d4000,c7006eca,b64904f2,9cd7baaa,14a5226a,e5c4b3fa,68c62cf,65789066,2eb8e8ca) +,S(31e9d361,fa0041fd,bb73665f,2e1541f6,c41764e7,78023272,478cd7f8,e9552f06,62fdf441,4effb45c,5408a0dc,c0a78041,cbe39cac,562659c,ab8c2b89,1a940c11) +,S(792f50cf,66aa0f00,68bd7fb,ee12bf44,e1fa6662,41c43d2f,275a248c,29459830,633cf86a,a3f10f31,40aaffe1,d04ee094,aad1cc56,129149c3,2702c33e,40d91e3f) +,S(be4f52ab,8f6ad1f0,664e4d28,b67be76b,c3d318f3,9f455708,94060e03,2f8547b3,22ffbfca,44482e40,19ef36d9,209b4262,48d62591,7d81b6a1,57ad945a,1878b4c1) +,S(bb693c4f,7110d2d3,8bdcd6af,d8b29b43,80d003a7,d0f6420b,e8d02cec,3e7cf385,3bdfb9c3,948034e9,31904f13,e3dcfa78,13d3413f,5c2daebc,744bac87,9e4314b2) +,S(56e8a65d,2eb188d4,64dd4280,b987b5f9,8b92b8e4,34b6bb1d,1800fc4a,f8f2deb5,11cd5d14,63777189,f8c7d19a,c692461b,d21fa20f,f12d1976,3b915fa4,33006cbc) +,S(f09d5cee,514e56e5,9566953d,f8600a94,cbad003e,9c898261,5bb998f6,a5b8cbb4,d07ee0a9,a0e88f5d,6da3d918,d5f309ca,7234373b,d528cd8c,21ee9ca5,3d1bafd0) +,S(58671c9f,687ab0a6,f6b7c687,ef546765,7f29ecd9,d6250088,d588742,7b96fa6c,70355403,abefa39a,72d89348,9a8251c2,10d59df,80b4d284,f3f02246,f3f6c60c) +,S(e47ec1f,8a7169d2,9c05cb96,42ba126c,aff841f8,c2edfcf,25ec9303,772eea78,ce97644c,ca60474,4c269973,fdfbf169,bf0fa377,61737dd3,77692b9b,ac775c9d) +,S(ed95cc7a,a24a4ed4,1dfb019e,31abc80d,93ca5c04,b0515f2c,492e9105,efdb12cc,1602bbfa,f17e8c16,30ab470f,bd8fd829,b4a466ca,9f210904,7e0903c4,e012d789) +,S(b6fa3ced,a07a9cf4,db717242,9de8069,216a7b51,4145470d,d811755d,a2908a2e,64f695ae,7f19c7d1,17af6ca9,36dcb2df,8715ff96,c8134313,c111da69,7a354dfe) +,S(2e3047cb,4d210007,39b8d5ce,e4884a5f,ae969eef,898a600c,2202da33,239b3627,197f37c0,7c11943e,40328e3a,3c7c11d8,f2d77d16,84532631,53b7cbd7,69808c9d) +,S(df9c446,7a691cae,b8a01ec1,a4a8ebc,fbbacfc,ca347aa5,39d56853,c14ecd3e,acf9554e,906e86ba,8459c108,d49f7aa1,e1e4b2ee,9957101a,f16d43f7,7dfaf6aa) +,S(d452f3bb,2e3e54c4,1f68267b,f846f504,99f39f28,5edb86cd,68330ed9,510aff54,52c5d16b,41eb1029,7f86a30,28d1b610,6f6c5aad,61b63bd3,53d95b2b,fd214755) +,S(cc196d06,9ad60096,c4ba1057,fa4fcd9c,4a50b28b,78227074,22d531b9,dd85a5be,bd930456,14388283,b28cb67,758c792e,5f9d6c0e,2d4c1a86,745e0504,ec902b24) +,S(4974252f,9c246a45,1187b1b6,546cbf27,af9c21cc,d03c7b37,df7a604b,a3b6859c,276e69e5,b85e3241,33f9ec76,d628fee8,3af37bd8,def6e677,bba19739,9da14305) +,S(311548d2,d9f880a7,17e77608,8574ff4c,185cb0f0,a9660ef6,7a94d090,3e2fd845,82b2dfdc,2d0ca2e1,d403c1db,7593dc01,2043122a,fb50961e,d5f86174,18a3d7b7) +,S(30068568,4497d5d3,2c98d3ee,1136a9af,d5bbc79e,340528cc,4e0b3c55,74e867f4,57c141af,fd650050,79ea563b,e9ebb161,a7725bb8,e41e3c13,ec528b2d,df23430f) +,S(1c223f58,1f03b4fc,68024db4,876b743d,8a2b635f,988340f0,d22c389c,43d130a1,9d99aaea,d3bd3b1a,9891dae7,4a3dd857,3a86b643,6c623c00,6604b211,4e27c133) +,S(a675c89a,5d453821,429109cf,45cac77a,880e0e6,396a0b4f,16053ff9,eabfe4c1,8cda99bf,c3426739,c4888767,113b7f4c,9b321a61,1b63b4d1,2dd50a79,d80b90f7) +,S(e990a236,d7854ca7,3e40c661,93dbf3ab,74198351,1236988b,cca29eef,772b32c3,a3d42c05,851b8138,ef1bbb7b,41510bb6,fc893baa,928c98ac,91127b3b,a100aa12) +,S(2a6eff8,af4b8049,2c3d31ed,f6672d1c,d0b231d7,6deeb590,a8d0c4c0,83586027,2a7427d6,951e07fc,4d5cd4f3,ca8a3415,8f0d03c2,3cd2f250,541c0f11,8013a623) +,S(7dad1061,9c4a7bd5,1edf6813,daa8fb4a,2a9e494f,9835db8a,42f4b0ca,827df50a,3e3b2b7b,a44500d0,277b792c,8a529fb3,41667560,1c4e443c,9c2fb2e0,dd17f1ee) +,S(39d5b162,fd48725,1bb42303,b7b8887b,92180fa,ddbdfb7b,a14ef2e0,3cc32aa0,3a8bafa4,5645e4e9,bf4e0175,69fad346,210e65ea,fa92b971,413a2190,b64b6f09) +,S(f9ac329c,ba09d60b,5aa62bed,81e9ca15,7a3bc53d,acd83836,89742ef3,cfcfd795,9c2fff50,aad80c18,2e6593ae,796ada7a,e0a42a4,17ce77bd,ea7be927,161be4ff) +,S(19adeabb,6e9aea9c,fe245329,623b8bcb,3554eff3,999b0b0b,8e035450,14cd964f,c570d99,99e9c62d,a3321f3f,a548d43b,99f05df3,e17273cc,2a45a3d9,20654cf8) +,S(978f705b,eb9b6009,22285468,521eddae,e71f6521,1ae79567,3b122090,fd4eb3c3,8eaa7bc6,1ae92adb,506f9e32,c66f5457,4e1d929b,ef4953cd,a1cd3f8d,c98ff8ef) +,S(e29b57d8,f6808d6b,ec982a12,ac70afca,1c9c19ff,a7b0c724,4fcc5b0e,3dc2fcf1,f7d60b14,133721c3,471fd91a,864e576d,84e5a06e,b031b1fb,3a2947b2,a33b159b) +,S(72073aee,f39510e2,81cca2d9,7831e533,56cc6016,9462a9f0,45100dae,3443bca8,bd761539,177458ee,dff87628,d155c9a2,d6a00e26,51158def,6ce72c35,84d56a3c) +,S(c949cbc9,48db844d,2cd04810,433f982a,680b6a95,e3461ff2,108492f9,247fefb3,162ca70c,c9f19d2,d3da47b,3b0a2361,f5c21492,a12828ae,9c0ff9c7,ef1d7b20) +,S(9acb7d8c,e958f7e5,189676c3,e7248ad7,9f717a67,2d4c80b5,9c425663,e078810d,112dc86f,1b241c26,30d87412,2faad000,473bce32,95bd6989,8b6a4521,7773284a) +,S(e468a13c,e5ad4c24,4df9aaa5,e13987f9,50900b4,a32ba33f,430935c2,1250e4bf,61e3e755,a91bb2a5,3fc2ac70,bb232b2,c1aa356,d494656c,5df93232,a866e400) +,S(26779f67,75d8caf1,46638c17,71e33b02,b41c61df,325acbd4,506199c1,bea8310,e9de26b5,c076eb9,cf3436dd,d9bc7f8e,5772720a,a8401227,7af573eb,65a769) +,S(8c408da9,a816c7ba,ae2846ed,bd923211,926e5e5c,ad595a5f,f2dbb190,f48857a9,89373ca7,e9f3f96c,1f0bc7ee,d427dea,99808bd3,b943964e,f0db4bc4,a7a1256b) +,S(9ccbac95,d80bafbc,b7e699c,aaccea8d,3169db5e,328e1519,825a3f4b,adee6a19,5a8d6936,a2859d57,6accb35a,5e80c944,4d71612e,76cd755e,aa3465b0,dcad8aee) +,S(34c7b7ed,6fe88bc9,803567ae,71ea7c28,c9511f60,e2ebfa8,fffeba31,dbbce2d7,31a3bfbd,c42cffa6,c77aa417,955c671a,426497e6,b35efaee,83a58bf,84de02c8) +,S(7940ca7f,c3bc810b,5679456b,88a73cae,4b2abed8,47260052,ffcb33de,edef6155,8923cf73,9285b368,d32d690c,488d8b38,5d3285aa,c399fdf,6e263daf,4e0b35c5) +,S(2cb10da7,104ff7d8,d8f1742,43425c2c,5e8773ed,71e62e4d,cc0c2d4a,56ac3a08,610cb16f,53ddfd28,7a7ff301,a8047555,801c13b4,81033c94,4e145b1f,98458520) +,S(ced78e5e,58e2893c,3910bd9d,d43fc362,ce06dcea,f44c5aec,ca17eb0e,cdc1fc53,c66050cb,835c97cb,c08ae0df,242895c8,f0f0085d,85a020b5,122e041f,8fb09607) +,S(5e83a4f7,7c4cc672,a7a381f3,e527c8fb,331e8d75,f8578a85,39ae1007,51903f24,545cda31,25c29fca,b343c22f,824c86e8,89f2cd3c,d2a6f3b8,9e3308fa,2968b8fb) +,S(c62958f8,33217811,cb099492,fb8c13a4,5b3d04b8,12a0c1f1,68cac595,e7efcfa0,e179e98,1b92f1be,25c9f892,d6a71ef4,62f3e1c5,43127fe1,f2b711f1,bf61aba2) +,S(7eba00b2,f151282b,83c7fa9b,5df6d9a7,b764e7f,42ca29a9,2734859f,a2f0f016,9e3162bf,a619123e,8e87728d,da825814,4b760c2a,3d05fc2f,1fc25565,1d72a7cb) +,S(8f68c1f4,83ce557b,98d96444,4a2a7d2c,a05d8f25,e6f9f909,cb44b58d,a030cd90,30f15dc2,17e7ce70,a6fae901,70930130,8f7d8706,80c43840,765ee89a,5f864e00) +,S(393d5b72,a9bbc392,4f712195,51ba7f65,4bc4df18,cf93dcaa,bace126f,c4262b5b,976cf232,fe59a3eb,c6514242,19128395,2945cfb,f792e4c8,19248346,37f16e9c) +,S(e1648650,d0d4ca1e,8fac2522,cda2a042,bc93b879,2d6f870,29c6d888,a11312ca,4d919105,9ea261fd,9470ffb9,e60c703,6d21da3a,ce975880,39e1a820,c636f269) +,S(efc7519f,4676989a,a12d823e,8e49b1de,96397ca2,11fe729c,cf0fd67b,bc6d6a74,3f304e50,be6dc6f1,b7c79979,4ae38ff,ed11a253,778d0294,13a98547,e3b889d5) +,S(85726e3e,aa8f7016,1c3629c2,84b7a7ef,79259de0,84f8bbf6,9358f66d,a439133f,756800a8,11a472ec,a72604e4,c2eea080,94971b58,3738e7eb,2c817250,2595e47f) +,S(2e238f2f,de3c39e1,19119541,9eccde3e,b0c4fee3,e432fcf3,f58c4f77,4ba070b1,7ce6b671,c0c79a82,2fdcb88a,a10a8033,bcd0e1a9,2cfab941,d052c09e,4e5e4c81) +,S(9cdc6aac,41e79b7b,ae77a7b9,bc027e99,1d4f8c1c,2b4913a3,1eaaf1a0,9bd87f24,9d666c3c,3503b9ab,d609b074,743f8a3f,460c0122,ad7a0cc5,a22a6d17,9d948ba1) +,S(360dd1e4,608c9215,287936b6,97e5e819,390629a0,7bec616f,7fc713fc,5ef8ec3f,7628d770,5c744125,d5885c2b,de25693c,5afaf8a,3d5739a5,deeeb5e9,ac1c62da) +,S(8657fbee,524c5f5e,25aecdf7,7306c97b,78119e0a,5f155c4f,37918e87,12302d35,242172c2,f90ea26d,b1f7d2cd,dbb9af4d,78a1660a,32402a83,6e598c92,df9e999f) +,S(3ae7e8db,7b069528,acea3e0a,dc005d52,7ca9e1ca,dd3d3cdd,5564ea86,5e1a453,ac203ebf,98b7e46b,bcad5156,cc058857,f32a8d62,2114acc4,55cde626,591fc5c9) +,S(81bdb0a1,9852adc7,afcd9775,54143362,c7e724df,884f1a2e,796d88bd,1c1696cd,2f189af1,a90f9445,353d0549,4d5562f7,4a3d37cd,1362d92a,aefe0393,9b46ef0d) +,S(e95b0171,dd117d45,263141e6,95ceaa52,3498a8d9,d1f09ae6,855fad1c,5e5c3e8,dc8bf907,ea6a4824,9cf930b0,bad3e3a6,c179fda4,107a0812,76f02a9b,9c689416) +,S(20e3979,e95fb62e,ecfa4ff1,3aa3fd46,94b24ab,c814f560,ce180262,11b70bc8,c9d01545,50f1d7be,da471272,822522dd,d6924a98,f6331fd1,cf6837d7,db91da1f) +,S(e0405598,cda639ec,6c058a61,4c39d56,f71c43ea,693f9b86,c9df1bf9,e4fef30b,9df9b561,a7bb2ebd,67b67031,a92dda6e,12550d0f,5cc2368a,603ae64f,ed20ef3b) +,S(faea8667,646d1600,2dbbdfd8,74a59ddc,a2b8024,b2f7f6f0,f2c2036d,ab5c5e9,8112167f,ed386f1,d1e02307,de202bd2,363cd1f5,fefe0621,1c4564fe,eb0220a9) +,S(f01d6b90,18ab421d,d410404c,b8690720,65522bf8,5734008f,105cf385,a023a80f,eba29d0,f0c5408e,d681984d,c525982a,befccd9f,7ff01dd2,6da4999c,f3f6a295) +,S(5906b143,9b994465,c9f3d4fd,f7f09a4a,b9ae0864,262b0140,def21014,8b097533,2917b92b,d0368fff,6e6a98d9,18cfeda4,d039c73,a3cb865a,5d77abff,9fe7970b) +,S(d6443bcf,53ba252e,925f5ae3,5d508732,a3289059,308fa67c,7b051ed9,66b6cc92,e0155fa0,366a2d1c,af8d2c17,a4ad9cf7,f4fc0102,f1e1ec13,7f1b2b51,1af0e7dc) +,S(b95a72a1,dbcfa0eb,ed2200ad,b57d71f0,b96a9703,8bd3cba5,78eff5f4,e9454196,f89c7cd9,783a4c34,1bdd05d8,241ec4eb,d8815463,4d05cc84,d5601f4a,6f3fac0d) +,S(50b287b4,d8b41f03,88804ae2,2b56abc7,be632cb8,a20629b5,3a00fd3d,9a879b6,67c3bbfa,4d8307c0,bd32106,57f5c0b4,78bc070e,53a3024e,1ffe103e,e5397076) +,S(64feb83a,5a81f6d8,8218e2a0,4e6f97b4,6efb89a,6f394264,d905c93a,cb7e5493,3fa224d4,eda77580,4d6ba88e,63df4c3b,5d9fab1e,519eca92,1ada5f44,741d5035) +,S(9434b5f9,2d63c2c,c90ee2fd,b7f7289f,b6277c69,3076d73e,cc38b032,bc8b5cd6,c940b3a6,4c04e6b7,1de7f727,d6fc0883,29443276,6d2ccd51,6d24dc22,3998aee9) +,S(a6db8e98,6d1bda8,995015e4,807b900d,704f8d3b,8ac5fc49,aa16bc61,82390724,3277c29e,bf27d0b5,ef8af507,5e295f23,92c41f29,3803a8b1,6ce601eb,2abedbd0) +,S(50733cc1,dd80ddbd,b254ea6f,14d0679c,6839e6b1,73aa0bc0,9d0ef5bb,bc5c2f9c,246e1742,a5a9172a,ed4e1e0a,9c0d623a,5233334e,47bcb68a,aec41101,92771eaa) +,S(4252122d,5a89f621,2c7b0a99,5ebfa8c3,b980e142,f7a89e07,d4788d91,1163ad99,bc63b87b,fda041bd,f9ac11c,e2e8ab3d,a1368cd0,2e276b55,6419e0ee,3d7fe284) +,S(6bc86411,d3f9d25,bad0a922,21f0146c,9173cc99,d00470ba,a41897fe,b5678f5,e2c1cd75,3915e977,c20d4508,1af1946b,1d8c5926,cca74ab5,c4ec0bd7,921c4cda) +,S(c786df9c,6b2e656d,a30146cb,14da5372,64683e46,5569b1dc,df4c1541,580d40a0,b8d229d1,d8773d5,dedde14a,dcf8816e,acaa274a,1f4ebd01,2086159d,1feb32f) +,S(f42032f7,dc6dc676,5b1a9453,74ee45f8,486e9b94,6a57e651,7dfefb0f,9ea3164c,ad2d7a22,e477f5ce,ad0fbcc1,fe2c2533,78920fd6,cd9ed4ec,1095fa88,2189131a) +,S(3c2fb5d9,a5bc7ad2,d4ce0970,5090a5fe,ef54a6ad,7f8d827b,2ce51785,b29ba5f3,3ec2d878,a4d836c1,71f68648,f8cac869,472847f3,4c267139,5f38e0b5,e96b653d) +,S(b182d837,dbb1e47c,bdbe559e,f30f03e9,ae1efc93,a5a165e3,10285bfd,ffe47303,c40cbcdb,d7f1fd56,c486a35,67420a7f,e4ed4ca8,43a68bdc,81eccbdd,213d7c5e) +,S(75a35130,cea9cec2,81792d7f,ffe84375,a4aec378,57496122,9a77270b,43b12391,988821ee,b6ee31c9,8201a90e,853c4afb,8e330592,6349d405,90c4840d,fdf3fb5c) +,S(a97b9c85,97362b4b,aab43b64,db7e5e0f,3e4cfea9,82939289,e5266f09,87fc8503,ccb75937,fd647eb6,7da4aa7b,72e8d873,c9f035a5,cfa120fd,4d4881f0,9924d8a7) +,S(d129bad3,4c7674ca,60a6f07e,1c1ee8b4,aa3dce4e,82ff890b,cd256324,13a56fe7,b3c8d404,7c8610a1,a56af634,b54dc568,6af075a0,a0cdd4f1,75855fa6,d22cc4c5) +,S(d8a0aed3,cf37542e,a0f06c6e,754503d7,3bd693cf,c3c357f2,7445989e,137f73e2,ba4ad8e6,3bde0d8c,42172a7b,59e1f4f4,581f48e,cffc45bf,929d815f,8133e6a9) +,S(9f905bc0,a5f479ee,6162ed38,24d74a1b,d6d48bd5,4611f840,ab1ac55b,7b84be18,5babdad,4248a4ef,4606dd26,1807e25f,6206dc81,6eeaddda,fd8b5082,52e3da53) +,S(d51c86e1,4519166d,c2aa7604,c88670c6,ca44a84c,6735f692,ee4dda79,e957b85d,fcee6753,29130c5,f4b260a9,cc7efb73,d51e6ec,8ac1ec52,28ca0ab6,51d5b8f0) +,S(ce251fde,ffdf0f59,5a563941,179f6f8a,86402b2c,6bddd1ea,7b37b89,bc649102,5b26a79,882b2f89,79610eb6,5de06e02,b09412e8,bbff66c5,cc650211,d1b9ccf5) +,S(6168ac7b,101fbe73,5b9339b6,9c459fbe,44a6f6c7,bd488beb,bf0b81da,ce511f3,65b6e531,f350983a,2f55db14,588e92db,436f7068,5117255f,ed474aa2,53b5c9ff) +,S(aab2deff,7e77f414,59d4361d,2722b448,6530f0c9,46b93d96,975f5a96,eb4bd091,b416562d,c5b057ef,7e63ec2d,daf0aed4,84320d7b,60695809,c6fa3aa9,e4eaa431) +,S(9ebed934,ca30351f,44b7ea4f,f3f6453f,6d0c7f81,869226c,c8f4312f,d413056,766a6f8c,311550eb,a8500ab6,f3466ca3,52e6b960,c4a52cb9,870ff784,4191a2aa) +,S(a2e78d35,d51970c3,42d03a09,bf3286c2,3a131ff9,abc4bea6,352826d4,5fe2da0e,fcb844e0,4bdf8d96,f423c499,44275ac5,8885c384,57975268,ec8cd880,dc640346) +,S(1a49f352,5203a773,9c7cce34,d21c51fe,dfeba5e5,c0f21622,eb954621,e3d6726d,e617de36,7c6a9283,8d4bcfd2,bf2fd21c,15b53007,6a0367b7,6d6ea65b,af032213) +,S(be65c6c,de6c54b6,c0c4d306,65cb4d61,211c97dc,a74c57b7,13f41028,e2d14832,d7875fb7,5cce3a40,9a74e48a,9b577a04,4c09a43a,989606b5,b44fbade,89e6c03f) +,S(a1572e58,60b10c96,bb3e0b6,7a047d4a,9957448d,74bd8a44,823de9cb,50aaa639,5ae37e0d,88c9f6d,173026ac,58ef45fd,17b0a0da,40304954,f9671c56,93946d1b) +,S(21917639,b6ef2333,378bafe5,fed1780b,24d9fa82,17ee49e7,369099a8,8c9baee5,fee39df,4bcb318b,4dcc4017,7f1af605,f22c44ec,d67a15cc,297f02fc,dfec4eb7) +,S(ac2a8d9b,b2219c98,444d03a0,36db728,ffa182d0,d31831ad,f2643010,bb76d3fe,adec7f18,c9474dc8,d0741d1a,5e72e479,c8b86f74,327fc5d3,cd79cf86,869310ab) +,S(c540463f,bf62ef36,a972d797,2bf79054,80b8e67,415f1895,d42d261c,165d1b86,f04a3516,83039c2e,8bac5ef5,1db4e105,95a27e25,cfe680ed,e7d840e4,f70e50b3) +,S(df63238c,77e75ec9,b844431b,d15007d6,56b849eb,4b6902b4,e79f0d6e,9527ef29,63846f3f,dfba6f2e,46bc7f2f,17af5067,a953cd0b,dbe2fef2,52e73be,b8a17330) +,S(43dd347a,1bf0ee84,ad774d4f,70c67cea,594946c2,94cbc330,15bbdd21,890998d4,b6f58d9e,e6e13c1d,b65dbd28,2343d8d2,3f73035a,b4399672,15b0300c,f7dfb48) +,S(5af8e3e7,f5ba9d80,73c5c1e3,c8d0a62,52eef1c1,17043e01,7d610f20,8b5eff0,246c7a4c,6f6e75aa,eb60e256,756e6365,711e98a6,aefa5345,adefb632,321d39da) +,S(60e7513a,b327df8e,e3d44222,95e0d886,b264f560,1c4e3d03,3b405d37,f6ae8518,4c58466e,b68d6c81,33fa6639,12f4a12c,a4a9f3e3,d4e0f6e2,7e92a85d,4d541cd3) +,S(126520ec,f3129775,786c4d64,7226174d,3ccaf5ae,2e4a1514,90e1b691,f07946a0,d5a2544c,efec281f,e70f05e8,2494085c,133fb918,6416435d,4b7fe673,88c440) +,S(b588e8a0,2396bfa7,8512585b,cd0d0e4a,7d14b5e9,f76e1426,b4591c9,6602c585,d7c6a7d0,827852f9,e34942f9,a6f5eae9,cd0a7f29,d30057cc,46d13e2d,4ec04caa) +,S(257f90fe,4409ef7d,30730e37,d7358c7b,60a1cc21,57acedd0,355d7a1c,89b5750b,b14da56d,9aa8fb67,b2d1260d,8de63c00,a9a7aafc,6fb890a0,e1a148ed,5e371201) +,S(8072b8cb,47c71331,9ed2ce85,cf913c87,85955c84,d338daed,58528427,3f3a4a56,6d7a0ed6,bf3383be,8953b47,d93ac5a5,e40e8547,2fc6d3d7,71f89007,f8d20d39) +,S(fab935ba,b2b72a27,b338ef89,509b3830,b7800817,19c2b3ec,8467c736,24ca2640,2a610c06,77fb514c,b224dcd6,56c5136d,88f87f34,86fa0699,eb71319d,c85a103a) +,S(7c6364c6,4906b7c2,e1ca0d87,6c163932,7e8e146b,afd94c83,2330f990,f979ae52,c97e7b44,26b91f6c,88ab7e39,d57f7b7d,4a5e0f9c,ff15aad5,e58e7017,d51c5317) +,S(98beb1ac,48c8824d,447a10cb,43b51167,b8e7cf0d,9eb4e4c8,43403b03,2208570c,1712c13e,ddff8fd5,191fdb28,717efb74,ed52a9f,f47bc21e,b15838d,fcf00af3) +,S(f683a667,31241c44,4adcf062,156f3fdc,d04e8450,911908e1,95bbea2d,8c5bf918,460eac53,48ea1e78,9fdef168,a150065e,1e4a2f83,85268385,3355586c,87453f1c) +,S(38f065a7,165e5d4,d31238de,8dc38f43,4dd024c6,6eca026d,de03c4cb,b43639a5,790bacf5,d35a688b,130bdb38,29073b8,28837b9c,9357544e,1e44d766,e401d044) +,S(cee8a218,2b3004fe,76714511,30340b0,7cf10241,d9e933c4,97ae84e9,a1d525f2,ed373709,3e0eb05d,3ee2823b,25bbbb11,ec943848,1a3ceae1,89573d71,7a596b3d) +,S(164c6ee5,620f4582,35520eb0,8276d85,9a14e266,b7b59576,d0a3e0a6,636f124a,fe92dd66,139f02c8,76ff6e57,8c769a4d,63484072,5c909bc9,481d4a52,f60565f3) +,S(d4606a18,1a0bd363,eb63849a,81c77d0b,942ff14e,7f885c61,d1fb772,78ef1008,81e7b2cd,e7c5d9,29e1d69a,309a334f,2c01b084,e82b789a,835a7aa8,5d7e93a4) +,S(905f5725,1df1651e,83aa8b3,dec2d96b,a32155eb,90d7f985,b3ce5213,2092be07,e4acfdaa,9eb8995a,a4ac8a0a,2b66cbbb,d561fe6b,389c56f,3af6f62a,d7579632) +,S(2e3ab14c,d3aa69de,a35a8ad4,99130302,b16f3d3,9df06b77,7c30e404,91eb6dda,ade534e7,ea0217a8,88c50bf4,ef81dddb,a5c7ffbe,bc0f90e7,110df2a8,e0c7c2c0) +,S(98207cc0,23f50a6e,5f5ce8a5,68c5b24,d4f4f741,94de76a6,d1e6f44e,72619a9b,fdacc236,bd424684,52508806,b08fabdd,98a15b15,6751953b,95b4c5f1,40bce374) +,S(b6d8bc0f,894863a2,3a793c3d,a33a5e66,cafe8696,f9cdd40e,777a40ea,aa203ee2,7e9b246c,8555b4c2,47c45fe1,d1e5d160,c7ed7d37,a3a43440,deee6ef4,15dc092c) +,S(56d030ad,eb30f1b4,7f806276,651a56d8,7f0d8b6c,458092e1,61351368,8cd0f6c3,bb3a1513,155c5885,e084180a,1728902b,33e1a61b,364c8d9c,94d67ca2,e386f131) +,S(77251aac,f919aa4e,b7610a77,ba27d94,c85b973c,af04c885,92fe5272,a48ec088,ef27aac0,593cc67b,93535bc8,4d0a04a3,e8a865a1,bdf65312,85beac2b,d58e149d) +,S(cb7b6a,cbbbdc23,64711585,6d80df3,2ec9aaa0,c6380b3e,677299d0,13658121,1f818bab,72575e35,a60ce7f1,c720e9ce,2f59b086,63cba841,ea288920,16a40ca4) +,S(4a18eb1c,bddb4c3f,ed44fdfc,6242959c,29720142,21d10154,c7fbca68,5c475d0,bb24f347,ebdcdc0a,6429f8ba,49065475,9cce87aa,8df10496,6d2ebcf2,97e70ffd) +,S(384286ca,1003c97d,7d151032,97d81cfb,e3e3dbbe,5dc70c76,6a8dbdc7,c1e625c,7201deb,638d2ec,6cbb4db4,e955ee1b,a3015cee,e06d2ddc,f024daab,8f07a277) +,S(84b6d6ae,d5e00e31,73c24d39,80f05cd6,f61625e3,11439a55,e03ae2db,d79a7d55,a3da9e1a,4c32e4f9,a1d326c3,6670296d,3ffb18fc,266e169,a99d4d00,4de383b1) +,S(aa1cab51,f7239cd4,dd27a4c4,805e614c,5d972cbe,c2830ebe,7daf6ed8,781c37e9,cf8bb72e,535fc8cd,12a2f4d5,e8f9e257,38a28812,1c93b061,5fd4fdeb,368be5bf) +,S(7c4f183a,f4abbe43,d7fa6d5,77ea6c14,83302f2d,b863a7c5,bf072e51,3d2140bc,1eb08c67,a417c0b,58fe8185,1a66902a,a608db07,24dc38c6,706bb80f,5956c808) +,S(dc30f68e,58ff30a,4bf2f6b,28b30708,506140a2,1a971c04,8afe04cf,94b54d31,6f996e08,8d4f7f53,aa1c2050,644a47ed,bcc1da4f,519978f8,6496ba1e,c7be2688) +,S(145185a9,ac6b036f,d0d3cd25,c904b3b7,c8f1d0ad,3ffe5f89,d87b7804,8b017426,d7f6acb2,828ccbc0,db0708e2,79db6313,fc955d2b,cf9e2754,1a7535e3,24c7bc90) +,S(e8700138,5c0bc4e2,9911f29a,e39a5ae3,ad3cf1cf,d690cbef,32f313ea,fd190fa9,92d83708,2e5139d0,f8084001,8ebf8703,da250361,466caa96,717edd0d,6e7ada7f) +,S(5fac0aa7,9963a1e4,5e1c668e,3512d0fe,2eb2c709,d5ecc71f,c862d5dd,523f864,9c7bfd73,445e7d9e,139b96e6,813f1f80,2ec632c6,a76e9dfb,8afecf40,2788d16a) +,S(441f40f6,f3ca4dde,5a085c15,57f14cf3,fbb7c3f2,5716c33c,63ee90f,45c564e2,c81f1205,1179c2a6,f2614495,322cf40a,34cab55d,c90d61b4,d1bc3787,ec3baeae) +,S(a4068c1f,c63d8f21,203cb6af,b37d796a,3c454bd9,db974ab8,e8bfba8b,b832b043,b5318b99,e9354250,a837f8cb,cf8d9cde,d95bb1d,1db0becb,1e887535,4ddb96ca) +,S(44563417,c647b097,d0238a57,74998fce,3c809a26,f6d2b71c,eeecebfb,134cfad3,3f417680,1c15172c,ef636dc3,12742429,13d59079,73ad09c3,9fd8c6a,8a95104) +,S(d57a5fde,47873327,728f5706,d2f0d64,1c5386a9,fdc41808,a2c062ab,942acb6a,cffcd62a,c0c85aae,4a6b7881,f3be170d,9836abda,baa47d60,8c0b8f60,f9aaedd1) +,S(9dcedd10,d495fa2b,6f4b4387,57cb38be,d6f09949,17e7ece,7de913f7,8cb111fd,73196e22,59d4ae9,950725dc,77321567,167d7761,29cc32e9,d92b6dcd,4c056223) +,S(aea290f3,a4ffd405,d51f1c8b,5f11cf74,60f05300,a1e24c08,25b96910,b24e32c1,f6c100a6,3c918f8,f64ffe8a,95840421,dc384635,e3465cf8,65034bde,17ca8eb9) +,S(bbfcd317,5091846f,181696f6,35fb1b05,802e7659,7ca97040,31339381,46812234,2a0a4ebb,1f09b31e,aab4478,89000fbb,5876d7e2,dd889393,3001a977,8aaa3a15) +,S(2a3b4e65,d9d7661c,addfe1e1,96beaca3,ccbc0348,dff6d2df,21f394c7,a7f4594d,74c6f171,1a2c0c01,78bb6a27,b6e17cda,bddb7260,acbc533,a49b19d1,e0b4a110) +,S(d8e47e32,15cbe145,aaa9efec,ada428f1,8d8c599e,98d2f9d7,8177215c,c85dc2ae,5c4d4f6b,aadcd87c,7ac76eb8,22e1c0db,e7c81342,702bfd98,e193df1,809e0511) +,S(5b5954d1,25d77778,f3c1514c,6256ef65,2ae0ab97,151f8856,dda6c87d,1e04a34f,1c5ddf3f,b185edb9,14769482,95e687e4,40497582,26b36fc6,3502922f,cd1bf81e) +,S(e8097e1a,12c6837,a8f29eec,31ccbd16,f191e7a7,d7b199dd,d6d9ee6b,7f26ef55,84ab3d24,e2c4407b,53e4299b,8de1aa8a,22dab9be,b6efa5c,5715a943,65174b9a) +,S(495ebcc5,b0573416,f2b3e705,fe4a4bfe,1e756211,5f37b8a2,dec57688,dedbdd45,3bc65725,6783970c,7696d5a7,b04ac7ca,8c627ce9,251844e9,e7cca774,4d385be7) +,S(4ffc69af,b9cf40ff,c94beac6,aa020109,b7855e0,a993984f,5e59ba16,fc78ad51,6d43d1f9,e3283528,494c9783,cdcbe9ae,2ede071b,ed494c57,2539df6e,b185ca1) +,S(b047e0d1,afc92251,7eaf5c0d,a9d0a093,fde5e829,84c8483e,d256640d,166dfc75,dca2ef00,a282972,d4bf67cb,74fb31af,da5b8145,ec1b7a65,6636376c,504a70df) +,S(ea7b0b93,48e0b3e2,be800ed,6ca4b121,277a496e,84fb6680,2658f68,37b1d530,cef40d2c,e1cf820b,19b435bb,a541ff1c,fa572b1,ca3693d0,cfeb3b32,8ddf918) +,S(e35a53cc,ad74738a,bde75a3e,e2b10d7a,d4ec39bf,e32e1daa,ecc151f3,bef1966b,ad06a8ab,a0296c17,fb24eff3,b27284c5,6ebca204,f6eee752,276ab9a8,1f1bce13) +,S(70334511,ae9acd50,e094f034,17dbba05,4a104c84,ea8d6250,13e1f8f6,907261a3,d1f603e0,5af2e1df,4b8eac69,b4067810,c302ed18,f02bc964,2eba34f6,bc67e84a) +,S(13adc074,c3ea8877,4598d0b9,ad653285,c81bd98d,d4bb6a43,c126db2c,d1f08d7,4bbda9d3,c83c7a64,fcccf3e2,c4251a7e,f61966b7,ec48e89,c991d44b,9588c83b) +,S(4a2a5fa0,71702fa0,934b66ae,7c003f00,306f07b8,625d92f2,d2276efd,18ce5f0c,f9c2f4ac,d8c594a4,117a836,7fa5224e,a7ccbe60,2475e3ed,b19a317c,5feff6e9) +,S(e2f7dc14,79bac0e,a95b5a2,1a9f4e,8c42a23b,8f25d09d,dacb9a3b,fe3ec638,ae6c82,c4f3dc5,41f140a5,2eb6b0c,dd0e200e,d0d951b,e85efd4b,b9b086ad) +,S(eb1d6d07,8f6b74f5,c48a913b,8510e8d0,5cfeb954,856f3ebc,628d0db1,a253963a,f3d73300,e216d14f,d5a67c35,91b362be,f13818c3,36a93784,875ce9b4,2cd8c7d2) +,S(a22c6d71,1d79ae71,403feb96,6dc11adf,253a5d3f,d3d74b44,5f7268bd,84b52277,86d5212,bf37db8b,4e873476,36201714,3f295f0b,d69d2187,8904a53c,831af06) +,S(42f7eb02,797cc76a,21c95db3,c9a0014,4ec5defe,553a45b5,1b701c0b,6f5ed3d0,60525877,3cb86452,928e37d,ce19e2ad,4da63174,2f049214,9f9823ed,ba679519) +,S(f5250698,b37da26,20a3a80d,a9c1c88f,792677a2,6d2b9fa5,49065339,ecc1e95c,b0826c,66edbd2c,5562bb14,d6f8df25,a5d0bc54,7a1163dd,4a8dc1e3,7d140266) +,S(b845d4a0,bc8db805,43526506,cac5c093,cece5c5a,85750d54,317dd78c,7bc10832,e38db2fa,207ce0b4,d9b01ee8,b174a24e,f28591c9,9d203ca5,7d90080,807279b) +,S(4955d281,746b8c9,4bac7d53,26aa28c3,9fba7149,7b4fc036,f9971f7a,870a67cd,ce72ebf4,aef6e987,3fa5cf3,e66ec918,911885ae,848aff22,eeef12e2,e50e6cee) +,S(20fdca2a,a03d6105,50f2eab8,d5e7d47d,c4e2e9ed,e6d2c7b,bfe64d75,176b9e9,1e077683,2c44d6d2,19d19c7b,aec0a303,f4d3d5f6,4ad8ca35,83f87325,6aee262f) +,S(418de9de,9adbec78,769eba7a,cab3853a,9a85ebc7,2bdffc86,844d0f32,15de4877,20d521c9,974c8ee0,f817ee37,a0fa14d2,eefc4e05,2c93ce87,ee665898,9e49c5a2) +,S(135b5a4a,f64eaa0f,d288bee5,c1bb7846,30df305b,5d3b13da,d8c446d3,6e13485f,3e571c3c,fe32fe42,669b08c9,14a655ce,828f41cb,12b9c18,9234f4eb,1aac0437) +,S(7eb64f50,5e26671d,21d3b2c9,fefbe094,c6300348,25248e86,5daa8939,f8b7e90d,be847834,f303ae8a,55ebaf3b,5bccfd61,6284e836,13eecdae,ab43bb6e,d4d4e986) +,S(9ee1ccd1,670be914,f9b7a070,962f7ac5,ac9d069c,eb3e0748,72db2e08,32870fd6,7d3d9dd4,9e71fd68,4271b17a,a8203ab6,1d2bf6d5,2fe3773c,78a8ccbe,d256cfed) +,S(a256c06a,796cffbc,5c90d91e,93ded565,b9755b7c,b760794e,e3060b33,3c217e71,e9ef8b6d,7731c262,6c93fb06,b7a6b3fb,fbbe0e0f,54682409,e5218a74,d4a064fd) +,S(dde8a9af,ec32c1fb,be9470d7,a435b4fb,bd34416d,e703a1ab,5188fe8f,f721aa4b,e7dbb850,aced2fb2,fcf5ea6b,edeaf383,e4ae0fe7,98badf41,66e58f13,6a87f3ae) +,S(fe88599,811d0e9,f780d359,2e8106fa,2f54fb6b,ba1df66a,b7b0b41,2c130c49,b3b0b8b3,c7f36d43,86f30438,1989d885,31edab91,f25251b4,e01b3d3a,6ec97f2d) +,S(d7da001e,8a606b37,ca65ae71,32214cf8,258dd422,efe0fc83,adabd56d,64b0467d,97f503ce,8eee1873,7e422bdf,91f051f7,1025353b,3a1f6f1,545206f8,2a32aa5d) +,S(65237583,e3e1a640,c2c16a9,a49d09c1,f5dbe2d7,a3845989,574abe33,397e747b,2296a1b1,e2350d8f,14a6fed3,afe30fac,a45ff345,9f8f515a,370aa260,d435f7a3) +,S(419a8e03,2d8fd27c,afda6538,9b757550,313307d,12fd185b,4d2f026a,40ae1de4,2932ea7d,3421d738,d821030,f75b0343,fffe9d25,8b605cf7,e20f6b48,1a94db03) +,S(275d261f,7ae2ab4a,4ad1fd66,1621a223,7ce08787,beaeeee1,a537b103,c0a44023,da955bd0,4ef1256c,9e184615,c4ee666d,c9ae14c7,d95caa7a,5dca6f11,1e5e2e58) +,S(3ac6681b,d0a2968,290ad83,cb0e00c6,6a9962cb,4c082366,953e294,58ff3651,28348b56,38ede228,7578cf14,ae385066,4595a2a8,d86282a0,edcc389a,346066d4) +,S(d4ba242d,9407df55,a9f672b7,e01cb1db,99ef744b,d24d3e47,9b1841af,a6c1f05b,9e395144,aa1fe50,9d72d086,4a1673c5,24c4ad65,fb7bdb16,cbd52053,1c6f11a3) +,S(c6525077,2bd399d9,901d57b5,7e8fc941,cae0f5fb,6d47ee3f,5b3a7cea,3fba2e83,629f7edc,a6ec6b0c,579cf9f1,867e1bef,e3bf9bea,8d8e64b7,2b18532a,47280efa) +,S(f12daac6,d38fcb00,c2425a79,2c52225c,a94d544,c6b96076,7ec9c885,d6d4430d,1764feee,2584dee3,abf2aaf,737a068f,fa6aec89,6c02e2fd,cf0ccad2,7ee8f210) +,S(db858a7f,4a84d88f,236b5994,dca409b3,d7111df4,f7e5c009,4ad1decd,82036673,c0606c89,e4b13042,9e0a63f5,1c93b3bc,95d98832,d89a9515,2e5c1874,f2c94a53) +,S(237f5f80,4d4c50eb,d611b079,5cbc6567,101ec8ba,1a265976,64472f7f,5a725ed3,b558d31b,7647c4c9,1367d696,a67e5d88,76454900,f340cd,5b8d7490,9fdd5993) +,S(e962e9c7,7c97cf36,32bf0a88,d6e939d2,68af9fc6,ebca96b8,6fb679a2,d953eb20,931395ff,a50a854f,5aa29314,c50c253c,c3175739,9c2eb20,600b0217,9f8ef48a) +,S(890514d2,14b57796,85c0cd66,818d182c,3d285af9,771c7c48,92ea2ac,115a8c3a,47c5a,a4d76cfb,2a62993b,b499df86,fbe7e130,d4758235,7decd72c,b61aee8) +,S(8b058976,295a316e,65da1774,da78722d,8d729f88,5ea4402b,f20dc67b,bab7f815,aeed497f,57d52480,23b94a3b,5fbe9c0,e34d0039,2d57b34b,377bda9e,b8703335) +,S(6a4685e0,7d96a793,d55a6416,6b089623,755fe549,3a879fbf,fc9a5a74,7ee7991f,313ffb6d,98044c92,f37b3f66,81f1b4b2,d9b2e42a,7c34a5bb,db45b9c,6063aad) +,S(65157530,e4ffeeda,41064bf1,f8d85174,2b9b8f64,87a05ea9,3d2198bb,3f33fc26,5b0e76a,8f96facf,9d492232,95a0c6f5,6a609504,b9b07c38,a72b6f15,4cb8c9f1) +,S(9f6c288c,1e04703,46b02d47,8fc0a40c,dfc0229d,928639cc,6ad2ac88,96e55085,2af7c1c1,7210b552,4ae2083b,b45e9749,c9452776,c12d5e83,ce265c98,51266c8c) +,S(c566e04f,e5ef7062,7d914a04,40731caf,3a7dccbd,5415cc1a,a2d81328,b8a6da75,a3fa5263,a7f66158,67f43270,77d3b2df,8db1befd,552249ad,e96db0d2,8c439282) +,S(6963ffcc,cb62ca7e,565fa9e7,99db54c3,dcaad601,95bd2dd8,df70d447,c443ba4f,96c715c6,84bed531,27c28e3d,6d6a0214,883aa214,25c4626b,4c11cf17,36b8f134) +,S(50db27a9,8e0328e2,81139df7,5376bec6,37e9272c,f7c23333,510bf5cc,c4f46b2a,b5221242,9eec82,1e9143fe,af9e7813,c135be82,bfc153f5,2ac061dc,c1302d92) +,S(7badba3,8f23f854,2404e636,b96e86a6,84557310,2bceb7c0,241e71c6,1ae22ad5,bd000395,b5d0ba4c,a76ac682,5191fbeb,f6066d8a,81c4a210,56ceda82,e13459e4) +,S(147667bd,5591aa83,26fa497f,c60f8e7,a5ceda47,9d4b2f7d,93bdab3d,81bcbd0e,bcdbbbc4,2adac6ea,413fc3f3,6cd2089f,420a6183,a918bf98,fff27aee,3e870849) +,S(ee576c33,e8404e1e,11e1b5d8,6ea52335,cf8b0ade,49dd16a3,7a61fc10,cd3eefd8,722bad17,6e083868,b3c14ee9,a70013d9,89c01b5f,44188ec6,7db7bc3d,ca26fe2a) +,S(8e3cfa71,f3b7d3cb,fe59d8e0,b579b76e,d33d318f,41d3b76,121b02d3,2acdec80,86be156d,b76429cb,ff2c67e0,c015b7d,d0007c7e,33f6041a,d817ae9d,ed5dc4fc) +,S(e220362f,2df8a485,51dbb7ad,634c4bb3,714151fc,db3cc2f8,e483aac,50d88768,acb8e001,a81eb6ac,99dca40,2d62fed3,8891fe2,eeb8e023,6d8f2447,6f4fd200) +,S(1777fc26,9bd7aebf,c014c785,25dcd40c,f93db012,31541cac,448c44f4,89628808,641bdcb2,b87e04ad,3ea3a3d4,c364e93,1171b2a2,cf3c0205,77552b7e,594c649f) +,S(ddfb5269,fcbac0bf,a855d2c4,c9062c77,836af1d1,145b4df5,688b6a5,24c11651,c353674b,9520ea9e,eac3bbce,a85b0709,d0284c37,a7faf78,8b573ebd,67bac8be) +,S(b01ea11,d4704bb4,24cb11d8,b42411a7,3f227c84,dc10eb13,c2b20a1b,bf23258a,240844e,9ea3edfd,948d2df4,51538cba,ed57f157,d0632f2b,8b644570,5f9ccaf9) +,S(934e20a,46fbb416,fbe829d1,46bebdf5,2220c0db,958c2a9c,64244713,4509cabe,76b7a1cf,79ebb5fa,58f77e64,4395d33e,bf90c0be,37e7439,c3d29013,25b0dd76) +,S(15bfe959,5ee58bec,791981dd,13ac5380,6615ff53,95dd4661,28c23f59,ea236967,d6dd460b,d3d36bdc,eb7d3ba7,745fe6c2,fddba241,fadcbdee,44f377f,bd43ee00) +,S(5e1a723f,403cf56f,8019797e,6f8191f6,6e7ccf15,dfb1965e,590490b6,224ecd24,97342532,36c48696,645a7e60,552eeacd,4cba990d,6d73117a,dc6f2967,a6a0fdbd) +,S(1485fddc,85c819da,8894c1b3,8be82eee,762a052f,3830dbb3,7d21ee74,868c778c,d779373d,d503731b,52f9e689,805988c0,b916a579,921c637b,286c580,93343d5d) +,S(6f4b7385,f881c9ed,202ab6ec,c8302626,668a1296,36274f3c,ac4296e9,1eece7db,fcf6f192,c6c80fff,3f5c2bbc,7c6c4a5b,b0ea2e3,70799827,440e998,cf6b26a4) +,S(bc27cb29,33ca44d3,b56280d7,7d69af2f,9220f4aa,49aa0fab,685c7c54,62adaf76,7607d5b7,a02c5e21,cfafccc1,26e8439,88c10942,aae6333d,b5262426,1ed4da98) +,S(76f86b25,2530d87a,b04e64c2,4cd1e05a,e8324bf0,a717280d,3845cc5f,a6a1a733,2db7ce38,33bd24b3,55e95b89,ae6d8a54,19124761,e382745f,2a7347ce,fd7c382c) +,S(5615175d,2ec968f8,81dacc1d,1bd6c06b,df87c9ab,53fdef11,335818f,bafa918b,5f755638,6154cee9,e71d21b9,cee3971c,7d41e3e0,ca2c1ff,f66982b4,d66fec25) +,S(6390f1af,1ef24aae,20e2ce29,dfe9a0d1,50b2826b,f5cb3629,8e0dc16d,ee2bdf8,cf3e2b98,4f120d10,16f82e1b,8916928c,263f3323,52bbedb3,c29ae1ed,b0d49b1d) +,S(bc5df46b,4fbcb83f,7aa53579,ac8d5c3e,8482941e,cf848810,f9239138,b57e9378,d4d15516,624e72d5,9002dca8,3c8b5914,b224d4c3,75260dbd,cda88f9f,2f772627) +,S(3d069f1d,dd52177f,cd802195,c7c8b2ef,1e43c34,9bd88b41,43bc2d54,6a2b8d33,2ba48861,14218d67,1b1fa4e4,2aae97fd,66164e94,af1e3026,8dfe9dfa,7155fde9) +,S(fa657492,fab9ab23,f1a49c17,40079d7e,abafd7a8,72e481b8,7b30cb63,8a47757c,89d2c7b9,2f1ad11b,d2808968,c426a00f,e584ad83,86dff03c,1a047804,a2e98630) +,S(e2318d84,61f85ac0,7db07d08,aacbed7c,5f5a45fd,7e505bb7,ac86b417,6b3eb46d,3b0170a3,b6f466e0,380ab984,e5bf4b34,89cbc479,cb2808cb,445614bd,8ab012e9) +,S(defa0810,91d1db16,fc2f86b4,fa5a7331,f0c15682,55855134,10f41a6d,a2bfa6a5,8429db90,fbb25354,3e77ca0f,e7a731d7,f1a481bb,f1c997e8,5f585844,423ace4a) +,S(fbaa7567,7306fdd4,8c85514c,bcb78e04,ef4d693a,dc7c356e,f95ad34a,cc880db0,45c4972a,161f4a23,cd71f726,d5617b71,421b68f,b0611097,3edf11b3,8a391909) +,S(5dd76e24,e3cc0c5b,ded55d15,858f1c19,dfff7e43,b299df43,3ee77b46,2122205e,992de68b,87bd6760,a75d6b90,89dcb2b5,5f0b39c8,eb079ba0,e4451fb,dee8f2d3) +,S(890c7a78,d0f1db95,75b53e98,a0a63d81,8096422a,3ee18403,ede8ffa6,3af9418f,7a362df8,7430b479,11e8d310,558195c8,fed28b05,531b68a9,1631ef97,ac008085) +,S(4914f692,f88e858c,b6174e6a,9d4bbdf9,87f0e373,1bf6e69b,8bf3531a,43a37e6c,8751e02b,fa9e0384,2ff3e4aa,307cb7c5,fe4f7941,1069249b,c8b19866,b6169cf5) +,S(a5f97745,ddc25117,81693df1,bd15203f,9d23bc93,9015be73,c6b4e256,d1d05416,57a99674,cf64bff9,7b9ba0c5,69d253dd,52aa188e,3ec645aa,89466c79,5d0f0370) +,S(62da0bf9,8d32519c,16fac809,95869f09,b570b953,e0a7bd83,9623a9c9,bebbdf84,9b9ec74a,c960fa8c,2ea8bd4d,93ff624a,9ce54fa2,4a1822a2,37d962d5,64e9121d) +,S(5caa2062,c25d7df2,23718f9d,ff29a403,e5200d22,bf064b44,c9519742,4c448a5f,6063bf93,1f72f65b,9ca76400,de5f5204,a8a0bdc4,b19cfd12,2664aa33,79a7a27b) +,S(4a81083c,c18dcec3,5825415b,2854250c,9dc651fd,5d897610,2ceb7691,576ebe60,850f48f8,7ffbf26d,eaad1f8b,2c0b8272,5f7c313f,55811b98,408f8f34,8421f241) +,S(ba8baaf4,9d0c194a,4814070c,786fd9f,4b8dc012,c42ed667,7b39f67d,da7fccaa,ceaa8874,10bbe854,9e87af6a,15ae1326,d85bb995,19a8c4d5,dd3a8599,d9ae0ff2) +,S(4c780a0b,886fdcbc,a30f239e,21495f9d,5ea38a6a,7960ecb,ea4dd2dc,9266103e,7e50e1d5,b6c811fc,f86ee651,5d64e06b,26827ba3,656dfd51,1fa64124,224e182a) +,S(50a433f7,f528d66a,961d7123,7e6d2bf6,7321896,5ec6599d,b1f34b0c,db21524c,6bca1083,9ef8d450,e56829d0,cb23ddcc,81e2e7df,e79b53f3,2f8aab9f,e2eec15b) +,S(b98e05de,573e37c2,80d6de1b,fd479bf9,bd23a6b7,2f6ab34f,caf03236,c67ec5fa,d86f07e0,8a4c3d61,f087dff3,a2af2d98,58787d2f,424b49da,de0c584f,4d8756b3) +,S(d0cb5279,e76b8be9,b2e9cdd7,b0c841b3,33398541,24923224,db54a91a,81995433,11fcafb7,28a3307d,2d87f2ae,d107b0b,d64f6030,b89e845e,1f67ce7,4cf7ee6d) +,S(90c89963,619b1593,d5fd7eb6,751ef03f,aa7c6802,46e36bcc,5d0bbae6,25376720,6e8f2a86,75b803f2,c3c87f9a,98318cbc,61b99073,2b8b33b6,62197ec5,73f64a71) +,S(bd3ad39b,989c3a08,e45007d9,116b0d4e,2dcb6a14,26a44dc2,29bff230,6c1a89be,7e633d7,de03e6d6,a7a4a833,f338160b,cb914997,6dc2080,28c3e2dd,b1c1b11a) +,S(1da5082d,a6e4721a,499d5559,91e8d216,f26167b7,d6de224d,17bc4e5b,ac94093f,5f864511,4b89eb63,60ef865e,6eb887c2,bb0b97e3,9a0b7078,888049c1,29f63c77) +,S(19320416,52e24197,d195a0f1,99818925,35c19790,99d15992,8999073b,4028d659,2cf34c91,2d1666fe,1abd3b0,49b12dcc,f7086e58,b7700b23,497bec18,e057059) +,S(79a7b5e0,482523d0,85bf574f,2e6e431d,b3bd6ae8,4e921b87,fff0f683,976efa9a,f713e011,70de0de8,a8cbe229,b147cf82,efc7fe3c,d7171785,c85bbbfa,ef935755) +,S(c2e16cc6,8559a160,9fe16d0a,9be4016d,55fccdc4,2d22ee62,895e0eea,a64436b,22399345,d40c11bb,79b9f036,5fdf6a91,4e1f43e9,bb1f0a87,81d2ede9,fdb6a118) +,S(f76c0f67,bd3c648d,c320125,5c34a93f,ed656378,8c9f61a9,c60933d4,e2d55d7,205c9c5b,83bbf922,8e99191d,e7ef0feb,429050e8,2b9fb3ca,1dbd2d5,cd35f612) +,S(24a3af8f,ee393ae3,2c30230d,ed6f96f2,9797d591,1cc3d934,b16e1304,de7bd75a,302041e9,2ce00cb5,d4a667ea,90df7f90,576865d3,7f462126,400c77a0,a44259b9) +,S(2e8bf897,5f29a6ba,636e6ddc,1e2dcd6a,1373f123,c6f155b3,3c8f46dd,b18baf7c,a93f0c91,258ec64b,d6e18761,872cf0e2,c89f9713,b9008605,b0378f8,1ec867ae) +,S(a5d35483,7f90d71c,8d829897,b3a8fa36,7c38b41c,879a3e6d,52f2988a,b88dcf68,853a586d,9661cd50,12d1ae6a,1501b874,739405c4,c7e32bd4,23ab7c1,62382bfa) +,S(51a6c879,ba6f75d9,4480aa71,2a343be1,bb9aebc6,bb4ca3be,de16d33e,992659c7,3e636d28,f8b0a4fa,a3ecd585,80d46253,61f496b0,344382b3,5742fcc9,403e7e05) +,S(a1d34e3c,aba45bf,1772d2f4,bcacd17e,342b66ec,97dadbd6,780cc453,ff2449d5,6c3a3b8e,9717365d,2f21e2ef,d72308b3,45008ebb,10ca02e,969d14c9,1aaf677c) +,S(fce0f1a1,69034370,33de7e6d,b2364b0d,4441c5f2,ee405e68,5dc546c2,58adc2ad,c018bc7c,fc5ad1f1,36103729,e1a177a7,495a6195,915ca5ae,c086804b,8c107865) +,S(e380e6af,c47b11c5,1ba0c8c3,9b796179,39b07cd9,988896c,38011e73,a4f98a81,60ecd299,c22fbed9,132bd455,1f44022e,151927e4,7ad93f1a,a0539c37,2680f6cc) +,S(6382fb8b,2f4179e3,191d863b,a0ec4cd,94b52103,f24a0377,8f6ee27c,7c81115c,6e8d4181,9621bf4e,3c04c5ea,2b4c0146,dd0ed973,bd4530df,d671f39a,e54db63a) +,S(c488650f,d66629fb,927dcb0b,9f5d0771,1caaa3f7,ad6df39f,7d8f98d9,65490aed,b395d9fa,46475b16,6beed01e,90c9cfd7,83e6138a,7bbc6d5d,f919d41f,3e512c) +,S(5cf1f5be,462dd02d,f8d1e9b,6cb1540a,4f1e5be,de9c8d29,954bfb0,217e494a,5e8727bc,2429c9a7,5123967b,b598207c,46105822,5402bfcd,c3d070b9,b2351d20) +,S(9644ace5,9dba2b1a,f3f3a4af,da130ba3,c3bdefd9,39c3b52e,670ead47,54407b9c,5e83dd4a,e338fc0b,8c501f98,72295400,4b2465f8,4f1c272d,20e3a2bf,300b1b79) +,S(728e6659,f0bb9bd5,916846ff,3aab6f10,85517ad2,c17254e4,c2ce908,c7934781,7df32327,354020,319e9cb7,696b0384,6b8e5bf6,691cc73f,829abd42,44e674b9) +,S(80e68c8a,31d715a7,5068091a,142d4a60,ff30d2c9,42f625c5,d234bb78,31009e8e,fe04addb,b9c47f69,3dc0b126,67b0960c,1b4a7bf2,882aa626,b1888e5e,e4157fd9) +,S(e655129e,90211425,558cbf1a,4eeeb630,df68a4e4,f96a176b,db55500a,89f5af02,d5e3a471,ba698897,9f747ae1,a1aa87e,e10c5069,3df12ed3,ed52307d,a0202a69) +,S(30a9e289,f6ab5edd,7b56f48c,e7373a6a,97dc698b,511343c2,8c5c217c,de67ab6a,f4016c18,c4f75d52,927127bf,c403a83d,4ce304d2,8a4fb965,beef9fda,4e9d3f4e) +,S(8a6a4557,2c1dba29,c9499ecf,d59ea43b,1ebec5a8,78a3e2c5,2de09aa7,c0288e69,56e4aceb,e8315c2f,b9271519,80a21d,c17ebad,63223380,b24a7237,33ec205b) +,S(647d3443,9f4e4f50,cee5b0d6,8047f130,965f8abf,482f6166,8859c86e,326c1bd1,f951b5a0,b180894c,f74c5235,cc625888,fb830382,3b0d085a,dbe857ad,dbc22088) +,S(6d143d17,9f0a42a6,85031c6c,f62ef32,2cde46b3,978b04ba,bc37bd3,34de5634,925d0fd9,11c7863d,b0ed42c1,76591307,72b456fe,83bb8063,3afe5340,cab4b5a7) +,S(f21cebe2,7ce8dd3e,fa30428a,cee881b4,d65eddd1,30f9fa62,9d78291c,a4f4b0bb,4a6d7f43,f2ed3d26,330e58c9,27a87c55,850620fe,1edc6e36,c9a3978,8f922ea9) +,S(37883073,e227a5de,452c7706,a4a06594,5a891c06,e594fd4e,191d36f6,79117c35,224bb4cb,389f3d25,42411fd6,594ad6ec,bf4f1c9,11f723e1,53dbb420,33ae8ec5) +,S(c21520ae,e6cb0358,53a7da78,919c88ec,84c17d10,3ac13ec8,6a412d9b,96e57c54,56720dfc,f748ca09,e27e8aa6,e29ea591,c0205290,3d38bc35,3b0b0155,113a5bbd) +,S(96cdb3f0,d6194675,7346b2a2,1767fc01,2e6d1ca6,f5e788d2,f89e2fd8,89a57924,9f5fda94,e0455b28,1cccc531,7118b175,edd6c93,c212e7c9,fe845f0b,cac5a592) +,S(7b5e5f5e,564606bb,a71b8f1a,7d410b1e,6482f508,879e1bd,38ecdc44,b7effee4,92a3f1b4,84ddc60b,4b005282,89984241,4f91b9bb,fd55eaba,37dddd0a,dcc1c6e) +,S(541fd344,c9d553fa,1e87c9cb,72150cae,a69a9a10,771ac96d,fb292be7,261912f5,80ef7a00,6ce5ac0e,975ecd69,601fd059,a9860e07,77f6f31f,365187b1,f6c9b392) +,S(2b2b3929,50b07da0,5c20b72e,4101e36a,bdfccb6a,2a7f36d5,b57f58dd,1403035c,ff6c1ba2,caee889c,14414086,456f096f,d065a7d,d2f9c7b0,9ed4eb71,3b4002a0) +,S(23acbca3,4754dffa,8cb3176f,d1435d6,4cba0deb,84e12ae7,adaafe88,497aa60c,5d95b3de,26f703a4,fe9efaf,cb273921,56a49687,a71433f6,9a2813ab,5fdb35d2) +,S(5fdfa393,6bf9c699,24417a2a,f36054d5,95640b6e,3ae30807,5ef51bb6,cd63fe34,97fdd994,bf74c959,2a5435fe,f1baea41,8a79c5fc,1243fedb,5fbb0466,4d599d04) +,S(177cf8ef,58aeefed,2832d53,417c7025,c6b56fb3,8122ccc5,2b33f79c,71c461fd,d8a12d8c,69da9b31,832cc10b,55b57a60,b900c19d,f8f38c92,f6bccfdb,98d3acf8) +,S(14f90282,e5a3121d,a59e919a,fcd146a1,9251cf5d,c0d805bb,bd013eef,4ab78bd3,74a504be,928ed13c,f29032e,5c3dcb3,a18a1df8,aad26b4c,a64ebabe,6afb4244) +,S(31099221,e6ceaf16,81b22b33,dd8ee3ef,1c2a95f7,2bed88e8,15333f69,f91867b3,772e45f0,930ebd2b,7f5b7ca0,640467a0,8ab5472b,f6bd347c,903cfecf,8f80176a) +,S(a6a0a416,d8eb22be,49036f4b,dfc79c6e,6cd167cb,38813269,70e199ad,c5d1b5de,54d036de,6b84f308,84faebee,4f944375,fed61a6e,7763bb40,a283ab52,3335af10) +,S(c7c5cb1d,ce484477,82e7da9b,55bb8482,4f715a80,d844294a,3c05dfd8,3dde77cb,bb66731,1bb0c2a4,e493e257,d289a42d,3630ee24,ba0f7f86,134a7c6a,72dbc3df) +,S(f1bdcb49,43267948,fe1c9b7b,51afcc79,7027e4fe,f98c2175,d7d1b8de,770328b,271f57bb,a9249cd7,dc510173,5a2168c6,ea97b341,b44cacc5,4083d115,6de8e728) +,S(2815f09a,8f0e2dfa,d21f50c4,60b8f69d,3aad54d8,aaa99226,86050582,b954d912,dec68871,752d862b,32ffdf2b,482ce1c0,6a659bcd,3a2931a6,29bda260,cb3cbf35) +,S(622619ba,8b74a0d0,5f8fa899,aaddd272,c9e6d43c,49bb6708,367155ec,4907d0c2,aab9b1ad,10201d50,7bea05bb,5b20453b,6474154d,ebe3ddc0,c86535ad,7c701954) +,S(a0390604,a40611cc,6e36c81d,30cc3e75,d162e695,1ae0515c,75417744,c093b997,60428c28,8d32d229,7417b6d9,b1003522,8190a83,66cae58,f7887037,9fb99641) +,S(422c7d1e,b0498cc4,dbba0135,ababc80e,5416f187,11f0dff3,80f1e1dc,23bedf0e,9c47d3b7,eb5713dd,cbb9ec8a,60d931c6,3382452,a4c0861a,847a354b,ba57d9f1) +,S(982844d4,7f572298,afd6734d,98064369,d81e4851,4b176059,6d3a1e85,6f043f5d,9b86f6a7,678cb5f9,a4304f1a,a8d3458d,fa85e65a,ffc79da3,faa1fc8e,a2b7ed2f) +,S(993385f,a76b9373,422e4411,b9e8a85d,3ac0e09a,f9598570,63d50a8,67b67ed2,3e7a9a3b,222a66d2,f55fce7f,e33effbb,bce8cfc2,8991d8aa,376a8ce2,4e9b8460) +,S(15b05ee3,179d1dfa,61bfb0d0,1903f93a,f7e04f98,cfd40be3,2ec094d,1bd5a4a1,ed1301a1,451cecad,30e8fadd,801c9db5,5acfd697,3ed36621,2f2e3032,adddd939) +,S(6062aed5,7d6312fc,433bf7f4,9c10772e,829e294f,e7ad07db,177c75cc,9fe5e52e,96aa4495,c6eb07da,9a5a0e19,8771dce5,23a5d7ff,de314562,3b704f44,c4ffcd24) +,S(ef45dd6c,1213ac0a,5df18426,9491cb3e,bb5312d3,fb55c7f2,8351c98a,1ba564e3,91541d32,43e032f9,450f3605,cab238f2,703a9eea,8e429824,7a6ef63a,45540e68) +,S(a1e32dcb,c3a13a46,166ebb73,7e09041c,3487120a,6ec46987,6befb2a9,94f4a69a,ba88b7b1,8cdb2088,b5998b34,461b8a5,d85a57c6,31aa10f8,6ff808e6,3186e5ed) +,S(817e6c2,6d4ae706,f43ddd7a,cfa4eeed,6498ea2b,86aa661,9a0b45f4,e22ea33d,8aeed38,bccea5e3,4923b959,53bc9629,d9348442,4f33b06b,b02c882,25e47bf6) +,S(e734f5a7,736014fe,2ac9137f,d025f914,7574d67b,3696596,9f89b5e1,aeebf88d,74fe8220,7c83d3ba,ec54f032,f11f9309,9be25f53,565f0d60,5f2ab406,5287c997) +,S(87b2d307,414c314f,184eef07,6e9809d8,ea19c002,390b5047,38eb7bbe,ba1ae7d1,37f5c369,f5dd54b6,87729036,b88dbf37,45f33da,8c2bc9b2,64213468,f1c56841) +,S(dda23ad9,6c51604e,602b8e6e,9f08a67f,981bfa98,93194369,269c56b7,7986b772,6de02623,5ef973da,13e2577a,bbcde720,aa8fbe12,23c5288c,5724bbc0,228074a2) +,S(8d56a723,6adfe6a1,46182431,da5ff846,aee78931,27564954,f14172f5,27048fb9,c7cd6bfb,fdc51a83,252d4e4a,2c978cd7,67fa3809,521678cf,7dc5e514,eaf0b418) +,S(9da30ccf,f147de11,de528d14,863a4fcb,a2ef228f,506aee98,eabee895,eb618def,a4ab41eb,a16b4733,34a818f4,b6bd3c43,de4a9392,c15c9e19,a8b4e961,7fd4bbe7) +,S(e3339c3a,b14145e5,40c08544,eaa631c4,7be6c09e,563ec93d,2a05f219,f4afdf2e,fd586d7f,3a668a0b,f888083a,9ea84f48,43598417,f55dfea9,790ee89a,c412fed7) +,S(a6e08d07,66274337,f3b139a8,d6fc3f61,4dde9ef7,2e03fc52,45f7dfcb,1c0ab41e,d9620b61,116af945,708a610,c78f0b52,8abd97c0,21ee34fa,10d289cb,f3c06d4b) +,S(f8ffa0d3,b463fc5d,44e6eaad,caecaa85,8515bc4,69fe76e1,4ecdb25d,e503a040,e80dd6f8,934fa405,fd4d74b1,1da0d09c,dd723df,daf46221,796cd7e2,4bb83f1c) +,S(22a212a4,3c00408c,b4b94e2a,45a6a999,917c0fc7,aaa565d,c6f982cc,64b0d0d4,663d8ed0,3128ada4,f421853d,8ffd8201,1ec8b997,4b0d827c,ed318551,ff9930de) +,S(8d33b62,f162963a,f6636f18,ef33a83c,e9a17fb6,b1fc58de,6d271d,472a9d82,cf0092e6,60311726,e0856ff7,1893555b,625b117d,4698e4f3,f31611eb,21a56f5e) +,S(3bbf59cc,5b4aa31d,dfa0ce94,1853ff69,9db29cc7,a0021cb2,11ba28d0,1dc61d3f,d54beb79,d1edb94c,d6bd5ae7,11a3ff22,9f2baa0b,efaa139a,e4724ae3,624d8318) +,S(4cc9c2ed,3a7266ca,faffb517,d3ad4aea,2d4d877,d9b08433,a4fca2a3,691aec68,70ac52d6,1094da70,5f714f60,854a5656,8c25cb13,1502171,7bd4085e,8b489756) +,S(a997cf82,50d7ec36,125189bf,eb6b8631,cec31fdc,f156d34b,186f29c6,5d0b30cb,ca73d95b,93b43983,b2c51518,b4e419f8,415f14fc,b6356287,cd65d49f,92929d5e) +,S(5fae92aa,7c43ac60,b4bb4f90,ae2ac462,a6f88681,9aeada99,4d0694a3,a6dc1722,6b7ebdc4,dbe15dd0,9c1e4587,c7f297c1,74d5c933,6bc91511,f31e0cae,2e51b280) +,S(cf541216,323a9591,13561470,2b187b35,fffaff7d,8cf1993a,5144c7d4,aa836308,9d955733,feaab81c,4b03fe4d,25fe36a2,5075357c,b6ebf8dc,1822cee9,7f585f80) +,S(9c2c8607,bad80760,efb4ba73,442a359a,c1cb9b45,f81e54be,28f3f6f4,4699773e,7848812f,f3101eed,137b4dd1,4f503a42,9d71c157,449767c6,a5850204,869a9817) +,S(2f3bca36,fe504b7e,93f70176,2e65dacd,250b3789,541076ac,86352481,c896f77e,5af55f5d,b2e84dfa,38bdd0eb,fde7ea68,33471d28,add88dd2,a7e7c63f,dfe513a) +,S(f2fa3afc,fe4e5da,385f26d8,1848f801,7401b06f,652121d6,9468d2ee,f640260f,54cea12a,b4ced3ee,2ca351df,4310e96a,d3ca0d80,c3a3c86b,267c4620,bd0f69a2) +,S(82559cc9,ead1465f,a910b781,cb2a50ed,9a2b6ff5,d8513182,425b7dfd,983afa50,db5dd99e,242287bb,c94cd149,6e4aff70,7232e2bc,1fa4c440,5cd9f22c,fc4d239d) +,S(954079af,1494e324,fb707e7c,e9f1fc2a,751e194e,4ee058a6,e55e26d8,34901f5,2f90eb0f,43bfff96,c3b0f3a1,bd48c00b,85d3650e,52d1f614,ed65ce70,4fa19e99) +,S(c913a980,b1aca09a,19654428,6edfed20,dce31676,1fbff05c,1c05fa70,7eced342,17d2864e,df39460f,99b5b51c,91752c57,10ca7af9,a6e10c4e,c2b3e9e1,7317d3c1) +,S(815b7dfd,3b97ad9f,cf6ce427,ffe4a91f,2b541e2a,11305b17,2fd7b63b,82272db0,f0b958eb,fbf32959,dbc407bd,5290ca66,6f28bcec,2fa69d1a,7108a2ea,19d58484) +,S(7f7c1689,e93b7c77,20ab5b74,572e87b5,d2a907bd,624fb6ff,d5f29f3f,c64795d5,691a2f46,430c53c,375829fb,46ed4030,b5bc4dd6,70646523,45fbadbe,e6f8a1f9) +,S(8911275c,106e5a48,1c3dc5be,b9b42e2,3be8cf2a,5a76198e,e6749b24,bd9c5e7,747cb0c2,16d899c0,da5a8ed8,f279a650,f4537e7d,ca51bf56,b3ca8c43,92b5e99d) +,S(24cc2552,c907e757,9e85bf7f,fc868ebe,53e44b97,102b31f2,38678bb7,ad8c244a,a152808f,7d615060,ba9e78ae,720c2fca,a67772b7,fd99ed6,ffd5fbe7,ed114567) +,S(b0fc5873,68795dc8,55e3e19d,9236244a,812d1920,afef4c9,7f1e6c79,25395f85,f8b46879,5a0e40ba,2029538a,fb7cef58,9bde7b3f,fcc010a4,4ac962b0,afb8d7e4) +,S(cd6f4558,d634fc66,6dce566d,f94397f,d3ab9178,affd3707,55852679,adc6565f,d34d7be0,ea40586e,4f9007d7,4aca55e9,edb4b31d,8ad4f344,1f611a9d,94161b8a) +,S(fc5e8a3b,ada62f0b,6453f445,7dd4aff3,993005d6,de7b94d7,19fd487e,b88fb7a4,46041321,f7fed84d,2266d4a4,d86d836f,12f12296,d62ed296,6a7d5507,d6717130) +,S(dc72a540,55c962ed,90e64916,bdfaf6ea,aaaacbe2,fc180a7b,7e7bda98,f45488d9,e3b46394,41a2063e,59c4cb06,1a0a7e5a,a171fe39,812922f8,c04df63f,41c8e588) +,S(7d135c33,88bd5896,47f4cc3d,67fc7809,a7a4c530,e96fb5c,7dabe3b4,32a4fd90,79dd3834,8a61b642,9eb7a837,4e9c2176,95cff14a,3596a18d,19f84360,8aa0922e) +,S(7a56712d,b72ac3a6,353dad9a,15cfc9d2,7d9ff67e,9a35936a,e38e1c01,5980980f,6eda856b,9b5d4019,d0efb2f4,9f1af933,8969872c,126b2682,f56a8b08,97a83696) +,S(d3495480,25c7f966,d59b40d9,6ccb9831,2948c992,fcbe2f6a,7a7feb6a,946125a9,b346f8bb,532da4de,96908618,b0f85a10,fe9a87a8,d612e4ac,53bdbaf,9dfdb3c) +,S(22175a8b,d0a51763,d1818054,60951698,14af196c,df8648f5,29d27a9a,825e60b3,756d5713,81b5ce71,e23c0190,a1062fd0,645eddd1,7dc84c9b,b1c7d810,c13db63d) +,S(f557e381,37a1c6e0,a19e4207,4a1dd59b,17c5137d,97a58fc2,55d18be1,bf63877f,6cf0bb41,577ea3bb,7c9945d5,db33a862,3c1cce5e,d6fc13b9,b0739ea9,9af10381) +,S(8ddbadb9,c6a78978,2540d3e1,9ada3ed0,5ef35f45,a38be470,7083dff,75d501cd,75b4c5f9,51f70fb8,a89fec5,d52ea0b1,dbfc5709,3c1ee5eb,ff2ab0bf,f5854644) +,S(53f0892,cac79f48,2194dd9f,fc076305,a8ecdd4,977ac278,92e2d454,965a2aee,b39f7a9c,ecdd4ed6,d0fb1608,3b86d1af,68aa85b8,c733617e,5e97ecda,269ef97e) +,S(2712110,8fe28f55,c746525d,e7cfd4c2,f3a11b8e,3c6329db,a57a7b32,5a7edb40,cf6732b3,74681ab7,3448bad0,ae54b953,ee5d7bb2,5a8658d,3b366a78,dae5fa69) +,S(e7627b26,ff0448bb,cb45a538,2ff8ec9f,9376127a,b8425213,46e899b4,41cb5660,79842f72,e02820b1,e6b6843f,3a0c23dd,c0941ac2,b6fd30a4,39397ede,c78bc40d) +,S(37c48874,f07ce0c6,cf5be181,9ba31bfd,9a19aac,97b27011,1de59fcf,559fcb57,34dcc405,6a6dfa3f,283e0dd1,8b1722e,bad0e367,c30d9f1a,d552f394,9ce19b22) +,S(316868fa,33cf5544,1e9b9bc0,9f74fa1f,8daa9058,3fb8b3cf,8d339971,311eddfc,89fa5c82,9d9f4687,f7cb6b8f,ae20d6a8,cf872ded,a7f9b6e0,6e168c1e,45db4e73) +,S(d34a83f3,7b7673b9,6c8695f1,81ce837f,ce2f15b8,72664d3b,1a045c7a,bceadd44,e4f2cefd,59198808,15692950,19b9120b,495a44d3,4862323f,808b11b3,a8698081) +,S(687a48cd,4142060a,171a1efd,c41dce8f,8127ab64,ebd7c22c,7803f077,19168799,c082e152,41f7db05,ae6c1e15,e4ccc340,f4a7c8c2,cfd8bfb1,6bcd04e7,e635726f) +,S(70d2cd79,1d2d1418,9cf67e48,519aecb4,d58be2af,164d0605,2132517e,87f444e5,73b4c12b,edda1a70,89ae9db8,f105059a,2a558475,f5cf4796,1042c470,c9e190a7) +,S(2a6f2786,a191130e,dba1307f,f1bb0f61,c3d7b8ad,c02286f6,44871cc,170dd2c1,28a0c85b,8ec93fcd,1b9a0617,780da654,e5448475,a02a6222,f7065132,5ad64b2a) +,S(3f482e12,8d5bfc4e,d2f359cd,b4244db7,ec7edf92,f3fc5dde,5367d125,dde8c772,d41be140,cc4b4217,cdfaed4e,b09ce5d4,d3e95b52,ed6689ed,3f6dfde7,3c581cd4) +,S(96673e27,fb0f8a46,8fcc53de,12876045,77786d93,5afbca99,35545363,6614a5d7,f6a4eee7,1c4249c6,ef850090,59c12f6a,44adb8a9,cefe7a8c,ff2ac0a0,da28884f) +,S(be7a886e,499a9c2b,c05e5f64,77bcbb4f,26772e58,4565897,408eb36a,e25a9c58,24457eb7,802810f4,de64516f,24718b06,d61b0adc,bcf0d279,d883b2bb,45363a40) +,S(123985af,a3253837,e51f933f,8f25591a,823b2442,88cf8ba5,a0247ec9,94eecee4,a952096,21f7a9a4,3867c6a1,987dd661,1d3cf996,2aebfadc,dee0a5b8,6df196b7) +,S(75887ee8,c28bd2f6,a96086d9,aef71b7b,5a5fce50,80c3963,787089ed,e39b0214,47386fd9,a9216193,3bb500d9,a1ee8207,dc3edbc,17d3ccc7,32d9c90b,8995eb48) +,S(4f7642fb,12376864,29104657,2a2fffe5,2322df39,7cc097e8,f7a57816,e6709fe2,4f2ff70f,2e99354a,60cc5b2b,c3ef33d3,7e3e9ee7,16766018,e3a0ba39,efe5f971) +,S(8117d4fd,551c9ae7,4c9fdac4,5d87bb66,9bc7dc35,4b08de03,7c4a1378,734653b,13633743,d1ee8fd7,8482e3a,3a9e39e9,75b0f6a7,efb227fa,84feb897,693c5c10) +,S(8afabdf9,acd49723,d73c0deb,1016ddce,571abaa1,a71ae3e9,a20bcecc,ade5782b,2b66e3fa,ee86dde2,23dcc7b6,10ea8ed2,dd42124a,247b7937,9e190098,2580c71e) +,S(c4819cb5,b5af7aa2,895b81c9,31dc6bd1,de4a1434,daaa4fbf,abfca6d9,28c9965c,ae4db5d5,e1a09a03,67df8c9f,fbd2ae07,9f79ff96,410a9a5c,17229fe,5575c22d) +,S(dae02e8f,5d106f77,c8a1e1f5,4366daee,d312da83,1f86dce2,3e846e30,737d49f7,6d666cf4,73d269bf,aa7c3eac,4e5a2868,57df3661,c949b5ea,7f9a7a0d,11206bb8) +,S(a8762d34,c6f8317f,cba5c64c,68e25d55,e5bf7e4,5caadb65,a5468d2e,f9d779f0,6accd443,bb181cd4,591f8fca,210f9996,86460bcc,89dc06a0,2edd7bf3,d4b75533) +,S(bf503c35,fc8a0c55,fdf4149e,96b2d218,d29e4b9,1f703e7a,9b0d4da5,4153d99,7e1c09ab,a1437e02,a2be3d4d,7a444753,e475acd6,e3a42056,94e68ea3,cb7d1847) +,S(7898067,48cc0ad5,ab3a3449,ac82a1c7,d51f2b96,791ee038,dfceae58,ea0c5cc0,66b59634,96eb4a69,b87aa0da,2905449f,ffd2ae3f,ba1555f6,5c9b8ec0,e94ad922) +,S(25833d28,addd8172,c2a323d7,cf498482,732a0be2,c10d1895,8d097cc0,ff4c9a19,74708306,d01258d9,bac99a69,58ce1117,f245d6f1,4a1f92d0,dd48ca9a,4c7b7da6) +,S(6800bb8a,9dffe170,9ceac95d,7d066461,88c2cb65,6c09cd2e,717ec674,87ce1be3,d5228e3a,1e0bce22,c2d85b44,e670e78e,ffb1ec69,6c01a722,2cd1e62a,dc4e63d4) +,S(3d7d5874,42e4816d,af2f0897,dbb06cea,f6c673da,445a5342,c61a8793,420d107b,422b212c,93ab83e5,d6323ad0,35a55321,b7441835,b83f180f,c3803ccb,46fa4ba4) +,S(3c44084f,aeee2803,34db27ea,63339db3,9e8359b5,c9cc9a85,7ea49740,32d063e,36493b7f,1a732a87,db4703bd,61a76825,4a82846f,31d8920f,2cba7ecc,1d744491) +,S(cefffb5e,d26cf721,a51bfb2d,460b91a,a0b40a81,52073da2,cb75050d,f7fe8d19,b4e40e1,1eac7572,efefac2f,a97e5cb2,a0d042c,3ae8c541,e51be4bc,f6d33f4e) +,S(63add0db,5f5099a3,2f274c38,780cc215,bfd58753,144bd464,e75b0da2,dde97938,bbed5231,fe6d5b4,9679a13c,c3c6c382,5c300fb8,42b5eafb,13355c41,120c6848) +,S(ba5d1f34,63130cfc,425cc6db,dfe08329,4d7aef4d,cb207e3f,d1cefebc,7c791920,70df29d7,8030a8ba,56514df4,b6ecbd2f,ebfaa1b3,df7b23ea,ed16458f,f2f18786) +,S(7d363dd1,c6dd28da,94a7f850,ee753327,8b735dbf,f43e9456,67fc26e7,af75d6e1,46026468,2bd00bad,a5be02aa,5034e762,fe50d02d,6cb8dcdc,5aafa21e,423267da) +,S(e2dc0164,c9060646,904206ff,37f912ce,2d50458a,3ca186ee,fdda8d53,4184070,de5267d1,354d12ad,8d9a5930,443dbef9,65947f2a,5efa206e,146e7b1e,df7f5a2d) +,S(2e53caa6,35f0ad1c,7bc4c2ee,1e29b39a,972edbd0,7d3c0feb,9ab0d945,45815ee5,1bb33d23,ad85b572,130e93d2,fe366e49,8371c49b,b47ceebe,de3c1286,3f443b23) +,S(5dbefdc4,9fabdf50,e6bc1464,13d59940,cb36551e,5a6607bb,13213ffe,b56eba1,893fd0a6,1797abe2,fceb4054,c4f540a4,ed644ed3,13a2200b,e1d532db,44d0374) +,S(5ff207e4,2b52b004,6ab8bede,6bd26f5,316ae52c,d148965d,8a21e8d9,63374a2b,1b778704,daa94eb7,a36ad0e7,22a6d56e,c0b43c7b,a739985a,538bcc22,b9181bb4) +,S(da260b,ff4bfb8c,a54261ed,c96493d3,ffa90273,a55538dc,c9b237ec,e775fb3c,c7c744c1,dbe1cd19,20edbe3,c13f7631,1378140d,9ee64c3e,ab072401,86c067dc) +,S(b1985ca2,e7103268,f18d1063,c59d8d78,2250f450,c467d7b2,f4e41c28,1714421,16357630,3970159a,99a34c0f,e114b95c,f115d9d7,dc9b09ad,7c81d769,aeee6898) +,S(bf98e773,fe93f15a,ced03ac8,368e4276,1cf3821b,461cd844,2426d2d,cd1f7d7c,f4fe02f5,f45a9cbb,b67ef626,f239bad6,e5108c28,d783d70f,6bd889b8,6331185a) +,S(5dd657a9,ff99f15b,56c5143e,86ba7bc4,b0f34957,71d91805,71a3d003,ff008fa0,8da0b780,f644b14d,2b661b9b,484225b9,86b13038,a8a9bdf6,6999144a,4f5c212e) +,S(53b42d75,bccf2203,7252ee9d,25830f3e,36e7ed8d,8e1b6d4c,b2371e14,fd4fdba9,7791453a,dda2ca19,a775d684,9ee60c18,ca495808,6f8b56e5,1bf2b216,9690cd73) +,S(a3f43f6d,eb178109,cf4aaeed,a0666bec,481a1894,c25a4fc7,f4a87e82,680d63b2,d0331e73,7dc25db7,35625621,85073240,3a69a474,d5f78e54,93486b5b,3d1c067a) +,S(b83bea86,b07b217d,ddac9cde,9b42b334,c994cd1f,92beaa87,57a7e7cd,2c71b1eb,b68f8fd,66b45aa0,dd568458,73eb7829,1e613569,58345fba,7dcef9ee,dab957ca) +,S(16ce1de,573b3654,a0c00212,e878811b,25832f36,373da91b,9d9d7c89,d06ff12,264031fc,7441fe5d,4ce8ebc9,232d1837,7fa810a0,a0e19ba7,5ab16ae5,e93d5763) +,S(18fdf98e,93a7e380,8762e819,2b6e634e,f255b2ee,51c65b58,1ab799ff,52409e15,2d71afd6,c6298868,52ea0a1,943dbcc8,ff844ba3,80b177e8,62f92852,97868dad) +,S(3d5f8af4,ff89de18,866f596b,91300a32,a7aedcb2,a9423f4f,e364cf26,e2f53e96,46e9bc8c,ddc56d4d,ba25a2f6,e77a6a70,2e712496,7591725f,1d240e7f,9836548) +,S(69dc1e7c,11516a15,9edfa4bb,1afe0ea3,5be5b7a4,7695df8e,728a8dac,e2997db2,71be8ddd,d4c51c1d,5c13704,3aee6129,9541e929,799f7b6f,da08aee4,52f4ce8f) +,S(4e8278ed,ce9b340,1ddb1c3c,6b208539,164e1ad0,3adc082a,8d9dd5a0,9c63f737,13e6a5ea,7b2e8ebe,babd812f,2579b8bf,56b476ce,dd23c418,9be7b85a,940c5d79) +,S(91157569,7aed20df,771c1f4e,78b73dea,c89b2781,9b8a5ae7,5e2dc12,e6e7d148,e7114d60,218eb1bd,73074957,5b96fd10,50fa2d09,f37c0fe6,2887042,24ca1a28) +,S(3a37e743,bd2fe2f1,3f667abc,b6c03077,fd82722b,859d7bcc,ebeaf1bb,a6087d93,4192a6b7,eef51034,64040fb2,6b8d9928,21e321b8,cd611b4c,8d3e0186,646f5dad) +,S(d45b5787,9e4732ad,ad0996d5,879980a1,fc8dda5c,b628435e,96c3b44b,faf98f09,e4d4f5ac,1e4b039e,731143d7,60e2ef35,dc243097,d2e2382d,b86dc82d,eba0554a) +,S(ae5ff450,aa607a9b,bf4549d8,4d96adbc,6041cb0d,719793f9,2ec53b96,14522a00,2f061b20,e9bca3af,358e8d14,a04f1d82,97d1e1dd,a406d60a,d15727d0,66b2b8fa) +,S(befd3684,32785d19,7709ec42,e3b6407c,7a539b55,d97c8fe7,afd4f96f,d2050e17,65691153,b2efb4af,ebd2dae1,a50fd3aa,b648c7af,f5804d99,5270cc22,c02e151f) +,S(d13626c,bc00cd,36c776c7,8bdb17c8,18a2bc14,9612e0fc,7ab7888b,eab48382,ea4c3fc9,fe2238f7,1023ab83,fe0e09a1,dc167c0e,6aa05933,8f177cbd,241bdd45) +,S(9730fd42,fa9ffdc,414f90dd,ac790110,9619c01b,f43bfcab,e65a341e,2434435e,de61024,6152fb04,c997fd62,961209f9,44badeb3,6ea7774e,30e8c8a0,8bb0b496) +,S(a27e09ee,9dd4e34c,6a92f4be,aa1b8c0b,2a60b33e,99fa981d,afaac623,9f616d15,33e6d8f7,bcaaa24,b6008e44,377b4892,5f88e884,5fe73807,8cd4f405,a2747f14) +,S(d2dc7884,31e5a497,3905876d,bd6ceac7,8246c9d0,73d68872,f54685eb,5970763c,85f96d40,e31ecc47,dc07fea4,4856068d,15d2a999,735aeb3a,4959e4f9,9ecf5fc8) +,S(38fe7a6c,c401daba,981875a0,f0c2f528,30ce31d8,816a1d3d,47a3d96a,664a02f7,e9893a1a,87034596,9d95e644,313d9b65,5896a931,81d36f6c,7e390102,3cb28c74) +,S(f2d38ff,14100172,d117dff6,1522c61c,3a590eb7,ee9fca77,dc264e60,32291d78,94eb4046,ab31eda3,7593e705,792fdfee,2e3df8f3,655befa9,ef0ef392,4b02dab) +,S(9e23c290,94b6b8e7,4e112b11,69c5c775,4b690d0e,141b74f8,2988bd84,78d42021,e2528717,28fc8d9a,283bdfa3,a011d28a,f531f392,b5da6da7,1834a29f,203e747f) +,S(3054117c,33c15a,504156e0,97e9a0f7,d76be8a9,c1c88fba,e1d9d2c5,4e1c9185,998844e2,b73e9468,1b0b09d7,4ca48dc2,f29c6df5,f3c38f7f,b6846178,d4ccae2e) +,S(65d1e830,3421444b,7d5f8600,2c6de12c,16f15a88,504c6876,b9e94cfd,a206810a,71c9cca1,6e066545,b11e2c29,edbac8f4,8533ec41,1e331e15,a2bed53f,1b1878db) +,S(124b78b8,948c4374,7c5b32de,50cbb055,ebf9fe7c,56437573,40723180,131ec0dd,c0fefa74,970829d9,4c1314b3,64a8516e,3a3c7fb8,e1d49f06,b1b9c19f,be18ddd6) +,S(f6d81a5c,395ab698,28e5f1f7,84edc6f6,a55137b1,411baa28,615abdfc,36c18567,3a759acd,4b758dbe,79e22f4b,26774b88,40121768,f655664f,c3f9bbbc,91f564cd) +,S(e76bd19b,fe13dc17,ddd14619,da6b12c9,f2819514,6acef71a,9e7bc32a,7abbea8a,68cee27f,152177e3,afa7d47,903801cb,b20ba02a,c48fefd6,e23fc102,e6456bc7) +,S(7356c0d,ff8f26eb,7df215c5,cf923c4e,237fd7ad,cbe58f7f,d5c745be,6451a830,16a0d86f,ebcf42d2,e89e9a1a,803838d3,1dd7debc,527d49e4,a7db799c,e1eeee6) +,S(b8d00a56,43fdc57b,dd949193,c5df30d,8ecf5b4d,8b3a13e2,64ba3ef7,c8bfacc9,f05acf53,9343641c,3feac336,28e9b08b,9a4e3c49,94c029aa,470fde03,d5d8cf8a) +,S(f555e29d,4dd3e0eb,d03f3866,596be43c,677d1ac4,1a1a3a8,1936ca6c,89618880,171d88d1,9abf05a6,981eddea,8f030e53,117a2e2a,97c2f352,255038fe,32ecf95a) +,S(172e6167,6e7755f3,c6a59c40,97a8a870,cdc09d73,2064d24c,3db8ada,7ca50ec1,7c1e58e0,38f322d3,e147b3db,e9b8f311,135d688a,98458da9,f6f8a6d5,4d4e2a96) +,S(f8f5fee7,d4e8eb51,92cb7ad3,9ee26cfb,44724b7b,f9c74fcd,61f95e3a,f33fabc5,607fd05e,361bfd66,519b5e11,d00d22af,8b0ce85c,11b16cd8,eaba71e1,2ebe6bbf) +,S(6fc770f3,f6169350,bb8b247d,16088031,4348ef73,cee49e5,c0f56a19,d2d66491,47708f10,80e755f1,2b1b0d9b,3439312b,dca07d85,fe94fda8,414dbdc6,ed9353de) +,S(2b8c0b49,94d3f78,79776d8f,bd2980c8,7840df15,82427cc4,f07ac806,eb7f7e3,3a335552,ccb38312,92a71376,9c7994e4,19a7919d,b776ce6c,db2ff092,92e4d798) +,S(e473c99b,dffad22b,72b6fcb0,d741179e,afb78aa0,de4b5d42,dd19d7d7,72ab1a60,2d0f795c,61ed1ac8,b5691d5e,9d52b19a,42c368fb,9d0787fa,8ef3b275,2a3d9d26) +,S(f389dc2d,b76a47af,f9d7a380,ab49274b,1a40c4f7,970bfa63,729da698,b4afdc4e,b15c2100,b63f7f5f,911a6474,25866d79,5a57dd97,74c21cd1,4907dec0,f5f3afab) +,S(df6e3625,8fdefe31,dffa19ff,2416f8b4,9fbc09c5,e86569e4,4b7330fa,56cb6e1c,4b5fedbe,835f0b8f,ae20f627,c8e74f41,a5443c1,a764c466,65f673f9,f62dfc7b) +,S(27e40da1,a5d7cabc,ce586e33,a4ace757,da972cf6,17c3781f,3fb0e0c4,e6f0a70e,ffc4b339,9cc0b701,36d871ea,a7c3a6ab,fa9dc92,72625fb5,b8bde8d8,be55904d) +,S(71b6f20e,e558ac12,829362ae,53900a59,ae29cc70,77dd1001,d9d7eae,36b95618,e765adc,1125219b,316beb88,4c0434eb,a6f62499,b8b2d217,c209c7cd,622d0597) +,S(535d5fc6,1eef1a92,50ceaef8,e09e1797,8debd360,8b03f72c,bfe265a2,633332e2,f8453dc2,4ad1ef7b,819d4329,a4752757,26c98e55,65440cc5,b5d509d8,61c98a38) +,S(d1e2222a,26817cd3,a537f2fb,7437ce06,ce9fa651,9d3c1967,71aac21b,97de68bd,a131cb69,cbd27e62,cb65ab48,a7f3035f,a78ef40c,6c42e94e,da326ccd,2b452849) +,S(5aba9590,2b5062c1,59b9639e,f08912b8,e7eacda9,759a4e98,b0e21d96,c2597b07,896a74fe,e2596e46,1e93f4ca,178646cb,4bc5454,227a2dce,1bb5d532,e558b334) +,S(9f07bed8,4449bf9a,815a546e,ffcbd0af,f22c9016,188d15ad,7c353e2e,5cf84343,6fa14d01,ba16dd07,15f3e68,d57738f7,1cb56d95,61eb84cb,ad63bbd2,d9ba439b) +,S(80a1920b,14723439,cc2090b7,189f96e0,20e6eba5,5f8964ad,2f0be013,b4925515,fdb63791,6d074376,8e32f983,f2ff1754,43136323,50b98674,2ccb9ae2,7997a020) +,S(dbb37c16,202dfd9e,914f2922,e8930789,a70d4de0,9fe7eb25,7d9a2b83,aec2b4c2,f6bcb8a,2c7bfa20,491d3fff,bd36e82d,91a7228,9e4fb6f8,9e167803,a17b7932) +,S(f03035f2,f0ad31ac,59e64a6c,f46e9ee2,a2243ee7,de5ccb0d,cb30083,97551b4c,1a1aacb9,798651c1,2bdc6ce7,4146e259,4263474e,d7b9d42d,78026331,60748722) +,S(1886fa45,e7447e89,b0873362,605beafc,4e05965e,53fabe41,41e1c42,4fb234d5,eb5374d,a4a198cd,d9027311,6b4331ed,f0f6a7a2,fc52aa44,8a9274b9,ee3d7743) +,S(46a262a2,5b76baf8,ae0ba3f4,ed8f86fe,e4854272,cbd0ed41,313c4e0a,357471b4,c08ee494,66d7dc12,a8e08880,732574e6,f6fedbf2,71d6dc37,eec3c324,33d8070e) +,S(d521c6c4,a0db2668,5a36b61c,b73d3504,ea7b7488,e3d75410,494e89e4,ab404d37,41f334e7,6a28cb9e,25e6efd4,fe3eee2d,17480189,b921d726,aa027b3f,75f94bfa) +,S(8f44a4e3,c18d714f,62054e54,6e9ca86e,818fcf39,fdde40c8,3044ddbe,be3b951f,210ec152,f8c90d7b,8486d1f9,c5c49a48,68249795,bf7d0171,34d9573a,d5b22aaf) +,S(d5a74098,eb20aaf7,b664fb8d,63e08b0,430733f1,466552db,417ec648,36a31dd7,7eedeaca,97f1c85a,98496159,eb024564,1c8ef0dc,21c31ff8,9d06012a,3387647e) +,S(d2a63a99,d6ab9e57,a2a6d39d,3738c3bc,94505332,2a34d3d2,4cabd1ce,5111bb08,e50f6c3e,796ac830,e276fac9,ab6738b4,722fc87,880f2e93,7417198d,9c6c1323) +,S(a1b826f2,1f7abe38,c622b93a,7510114d,dffe4cb6,d3fe0627,dcc0465c,bc82ad9a,c9cec051,a253ab24,a8507b07,d24ee4a,c1d5a5c7,33f0c3d5,611e8a9a,e4aa9667) +,S(40719ba6,89926b5b,ca99a4,161a1bc,d999d40,2311fc5b,e2f5260a,c3c10208,9edb353a,73e9fdad,96ca6b77,b467dd9,c7d6d2c8,e041d8f2,19448ea4,c603bcd0) +,S(c0ee8dda,80fed10,721e7cd8,9dbd237,216f4e84,bb8f7138,11f8ca16,1e3a0977,6a786e53,2a0f49cf,eabf0338,1d64de56,1a1973c9,36d6a5ca,4aaf4cba,b8fbe1e7) +,S(9c75860,ecdeaf1b,c598a530,48a22158,8099afbe,20cb08c1,3afbb133,b2681809,d7019eb3,5fb61aee,56a1afaf,2675a028,b8735dcc,b3f5d93b,c3e29672,9203321b) +,S(e39df640,6dbf2584,fecea5bf,aae78b5d,bca93ef0,9054c754,d8b235c2,c5463657,33ab2fce,3a2e3e7d,e2ab230b,e69bcb7a,4d71c87e,a1ff5d34,eb8b78f2,d5c61a55) +,S(22480e,4a7cadd0,d17a418a,78074f80,c4731ce5,d9f1268b,1ca50204,1ff8282a,993591da,11ea4445,8ccf8bc,2cd55fae,69e96a77,89d006dd,b85a22ae,85316200) +,S(c613fa66,231d38b3,359c3b94,132a2337,e2a8c608,3c88b41e,58434c19,48a6a1ca,8087377b,c3d6ca30,8c932dc6,ff82430f,3fce21c0,1a8b8b6,c28bab78,d3ddab43) +,S(c0a94837,c2fb86ad,ca791fb,bb0838c1,3bb0c981,e23c4ff7,94dfa5fa,c663e5,1f81050e,d21fcd5a,471657fe,abe6fccf,a391f1e1,9a88c143,4644bfb5,e1f69f9e) +,S(87ab257a,cc2296af,d921bebc,8e52fab1,c9c21f08,ae09faab,18858a06,1e408ccf,d5dd88e7,89d2f2d4,3773854f,ca980411,3dd69475,a56864d,3c869205,d682027c) +,S(10d14c28,cf44c5d8,fbf5caf6,b307f6f,ee203968,eea1be3e,b3697515,2edf45b6,46926a1e,dd4f79ad,74fd506e,4443375,23ec76fa,e09d216d,3ef3ed4c,73b635fa) +,S(2eb78fea,63035a3d,db64149b,612832b0,c9ab7880,539c67e9,d722e252,f7e23166,4f72ef76,d3dd59c0,a4e1be46,6ca5a3ce,fd84d207,f7d671b,240fd41b,20e72af3) +,S(14d8ab11,402af341,dc5cbb6e,1116b858,14decf11,e6761b09,31b449b0,20b6f56d,91bf8c4d,cc717657,990e0e3b,3558a01f,ebbe534e,897d1204,8a7d978c,3a1d735d) +,S(cebc042f,2e7bd361,934da5e6,dd2dc8a,9e73537f,5d5e3e46,3a88bcc3,ff6d5552,baa841c8,5e0a7287,b1f16c89,8ef03f22,3097e92c,44952c32,45f2fab1,dcd988c5) +,S(4f28e546,ef539c7e,8f33191b,897b7245,8f48a01d,79f65b8e,a8f672f7,d0ab027b,b1c1db0d,55be796d,2b415259,89088a7,d86c41f8,cd8ccdff,2bda49f2,407123d5) +,S(c4d31178,b73cf9ab,b0e1d554,a2cf93db,ed9a7b9e,8f49c9c5,4d63c815,6e111505,f5e4528,7089cd96,3118e279,bfc6661c,1e77ab67,b81a71bb,4ec14fe0,ef0c13f) +,S(8e260505,bf045c43,f3fd972e,d4189725,a4db0774,52f94eb9,51e42eb5,6131f0f0,4afee681,c6069893,5ce21991,208457b0,11548d97,9a33a0be,cf3c840f,77c4b4d8) +,S(982818ce,69b4c6f7,631daac,868af6a6,79fa6631,8c9da2c4,bac994ba,f9c95d81,f5b13c91,e7593e5b,69c85957,6398b186,8c503b6c,25179270,7e602f52,f30ca9c9) +,S(75c2f2b3,53720779,8737488d,16a5ca6e,dade382b,78f2b2b3,226fd5ed,6ac6c855,95aec19f,b97734cb,8426e530,afda15e4,c3c3dedb,2aaa24f,7ca73b4b,af475960) +,S(606ed5f1,8e4dc1e5,3b6f5ae3,896433c2,a904cb84,1ea021fb,84537f6a,9dd45ed,3165cf75,26f277f5,b9bf22e7,36291513,10158d63,b97b7c88,d49d88d2,d63dde9d) +,S(a33e07b,c2934f5e,f02ae143,6b3ef5ae,d2d633f4,2b40e35b,d365fe2e,b8c72d51,e93cfd7d,f2e98817,9eb9c348,73530504,bc5cd8e5,d8393f0e,844073ee,df6d6511) +,S(5ee1d077,735a4a18,533b36d1,eb484f14,98b220eb,cd41746,b0947403,d6b4644e,7d4e1233,46a6ba1d,8a862514,4d4bc5be,72e23d,f25287c7,3512817d,e632f8ef) +,S(7485b837,2bb28a8f,d496f6a0,33b92ba5,96202661,8adef933,d6aadc56,55765b0b,a58defb8,deb4445d,80016c9f,5c04c2f6,e06986c8,cc95da94,3105d8fb,d0fe84ed) +,S(3a321f94,2268584c,cac5d8ce,5d6011b1,22169e73,d9093b2c,e3e0bdf9,ad680d21,99fb13c0,ecb6c544,1347a4fb,842a7269,1492219,f9bb7c25,1916f837,7d4e2cc0) +,S(c115a2e8,265c6005,10bf7108,ee30bee1,77893d5b,ab0a8cc1,754810c3,3f112585,e2f0a636,5836a24c,7a5c6997,45022e8d,e0b88afd,37737c84,d1ae437d,eaf0b3d3) +,S(d8895b97,cd390ca2,cf8e6093,c125541,1602fd98,97074a8e,aa11bd3f,5b0f3c1e,af8c594b,bd2308e0,12a632b3,1d05a25e,23247f75,6d7a22f6,fcf48cde,28056ad) +,S(4ad1fd48,e77fe497,b4a15e7e,869377bb,163d760e,105e46a9,a5477d69,fa944eb5,a96cce4f,e2f5f590,17af2359,1dcbda3,b640271e,95290d59,1fdcbd8d,ecf39703) +,S(62ffcf4b,7ce6c5cb,76766893,bd0a6f2a,e33ef821,d953c001,51c4be3e,67f2cd8b,d6b1d025,152db954,968ea1bb,dd2a89db,16103f55,47cdd042,9b7380f7,c4c985f8) +,S(e71343c3,cec5e49f,60fea970,6d256a4d,ba499e86,6f9ab33d,6045a1be,cac40a,ee1fe3df,df226027,78228bba,91afd126,a96fc10b,f5d2cad1,26091119,3a92b426) +,S(31766ef2,f3531ead,2c7bbaa3,279e81e0,b040182,ab78065f,d193061,2792035b,87b93ca8,91151ee9,775669bd,df2e5bf7,9f728d52,efabf584,352ba4db,8cd6f6d7) +,S(1e8764f1,982a97d8,b65a0a60,53a2fb5d,39f1221e,dd4f532,b007b2c8,c52d39f,8ed84e0a,de68b789,5ecf8c07,80ba0b26,53faeb49,3d23a453,cc23987a,b51f3a18) +,S(e249cd91,4e35f97c,92a07f4d,6f9961bc,d7f6efe5,ccdfd44,cb3bb698,9939fe43,9ee2fb47,4abdac66,5bb0178f,d8ff3bef,6381a90d,bfab05e4,a9e5566c,cc362ffd) +,S(b9a28764,d21db971,f7cb0c1e,87c9db64,ee6a1acc,485a02e4,5d5ffafe,ea1012b9,1b55a989,43ff4fb,25db6aa1,12d5c459,b1ee0fa3,aea913cc,54878682,f625e882) +,S(5d042803,9f8c37b4,dc92a9ba,43bf7213,971488df,be699901,31820bda,c0b6ff03,1ffd5348,88796c6c,a631c3a1,4773f89a,ea6d5914,bcdf27c,38949147,cab8ac1d) +,S(6d817b42,52986e2e,8ecb908e,99011265,df0cff59,61a82b2c,baf0c61e,c8b22326,55cac99a,d6a70b96,5559af1f,c5b67469,5d653662,bce7594e,22916fa,7c4b826c) +,S(9b069431,297f0d35,91451039,df8a382c,1eba93c2,f62f4e98,90e54559,39916ae8,70bf9138,a04b9f14,2841a2e6,bdc3600f,1b22c81c,ba27f308,19a763b6,e64fef71) +,S(dbf5e41e,d83110a0,5dce4bbb,bee05f0b,f9700ef8,273b4bbd,57099bc9,460050f6,e3e8f5e1,697b05b1,23632b89,28c3b34a,6cf1e6f,327b4252,c3009046,c8406404) +,S(c8ef12ee,22aaefbe,9ca024bf,ba8fd744,d7c5d489,663cde66,a3ff1dee,8174ef33,db64c2c,db21415,fc552968,fbef01ed,131db2f0,1ec8372d,1b9da98f,4792e8e5) +,S(3d086c8f,af84b66d,bb9212f1,512663b1,793d6893,23f45af,9bdd17ba,fed8d7ce,2d6a47fa,9e1b6c20,a2cf30dd,a99547a,b9ab9c13,5ef86ebc,280ac62d,2ce7847c) +,S(e173d58,714db8bc,cfff3ab8,8cf1be0b,547e03f9,db60c97b,ba5a6735,cbf4bf4a,44b8f991,6764f393,68b36b56,9033e304,7fe90662,70758fb6,85cc3f6d,e31b2fc) +,S(31ef8faa,90826125,66e739cf,299de928,68c6027,b882978e,f72d014c,4aa4f379,9fb9e021,fafac237,8d21caf,f1262d7f,bd9b8333,eaab0fe8,8a3499b3,d153a2e5) +,S(5e112e52,2997b67e,146b998d,781bc528,7e2ad47c,b2c5863d,eccb0873,fa088b3b,19cf3f06,d3c1fdeb,8147e074,a42fe178,c0b14135,2dda5274,50a7d5ce,58d3d441) +,S(9ba2cddb,c1a5153f,89e9144,e862acfe,c624095b,8cf8b7a5,b15edd6e,c7b70eae,f032469b,9527911a,92dcff6b,5847fbb8,9f4ce42e,f20b40d4,80b3a58c,72fc805b) +,S(9ff7e46a,4e2c6e9a,d5843f4c,c5e112c6,557babe,723203de,d75be7eb,179e814,ec890ac0,2ece651a,6614bab2,1e4944a7,d248c886,bebb33c3,36c28df0,69be91ff) +,S(2a13752a,1aea6e21,d25ea21d,b644ab0b,fb84b130,8d3f2083,be26098f,fc4978f1,18f4936e,65ab66db,11bf53e5,7eb7d114,4820fd01,ed827f96,652514fc,d8ce910f) +,S(a0647c4f,2b4f271,97f3fbc3,bd81bc7f,f72d964c,1bcc131e,caf9bda6,57b356d7,5e023102,9a8eafe8,9466181a,80440196,e850390f,d42dfbc2,1d3fbf6d,6373a56c) +,S(6abaff96,aeaca928,ba9a0503,dcff7a51,420feffc,33b1ad53,b1a7bd57,8c641ede,ab59c03f,7ad47f73,df424882,dc3ac2bc,dc2ae6ed,3eaae48e,b807b010,98dc0de2) +,S(442abb18,54161ba2,47fd366e,52d40d56,65293573,c339aa2b,28dc67c6,70f4ee99,c3f44b44,d13ee3ed,4ffe5188,c5134d60,a28806b0,687b5652,99924655,342a5898) +,S(9f54f23b,45f632c1,f05245f6,c5d1c433,9e18671f,da494eb4,3bd22be7,b1610096,25c9e532,1a5563f1,f47a1e4c,99d6def8,541601ac,8420de96,190157ed,aa17445) +,S(50296c0c,185abbc4,1c86f578,8dad10f2,7f76074e,24e931db,53944b48,6ddee613,81a02b32,b1edd539,37ee1c78,ab630f2b,45336e16,cfc560f0,adeb464,fb71b9f8) +,S(39c2d099,e2892b2b,1b6abb1,a198305c,ed59d94,f7022cd6,d521b510,9a8fc219,8900f6e6,3fe1a7c7,27728b92,aea4d70c,1316570f,edba940c,e626e94e,f03dddff) +,S(3581e30b,17067e44,664201ba,d7a6ada3,e0648516,260b760f,b406dc48,11c9d0f4,c8bac129,83e49250,bc478ee5,fcfc0660,37d68f9f,fda1acc3,6861743e,d7a794b5) +,S(72b67dfe,d341824b,9fa09057,b8362d1,38287eac,7292610,1169e41f,19dce477,be527b0e,52ce07a6,99065bbd,ebd360fb,72c6dc77,88030aa9,45fbbabe,7f038524) +,S(805af91,4a973e36,43712cd4,4a1da4a8,3d1237e5,9c991387,17175aec,4a8667c4,f6a7beea,205810ff,5d1f0e8f,5818854a,bb730c7a,1ddf02c0,968b1b05,92ed8a15) +,S(44794289,4b297a87,e3cdeead,5cb2dc42,9fdeeed5,b8038189,331874ac,88e67536,d67efdbe,8d7ab8e8,1e25c340,77687b94,d22d8ec,b89d7f5d,46ca212f,abcc1b5) +,S(b73347c9,baa967a7,5e5c8ac3,14f3c6cf,275cc893,72b26bee,731693cb,48d36df4,307f8fa1,98f1ba57,7ba330d5,816cb8de,d9ec3f13,6bec0586,53c64675,753f1542) +,S(ec6d499a,efd540e9,357f100,4a136049,d1f7df5a,d99c44c4,6e3ed416,9e40acb6,21e8082c,df4fa2a8,38327e80,aac15ee4,40549109,aaf6ea01,ccbfb95d,3f7a47c4) +,S(7607b7ff,8bd8369a,67d22545,c40a1588,aa2e50c1,b5be6115,cbb8794e,2085f283,6c4a9aa2,1dd2ad4f,7d6c4f02,23885803,ac3b06fa,6c0772ec,bfb610b6,a1d25ea5) +,S(dec4b7ae,42e8a25,2c414458,d4e0d7fa,4bf4d0,c9432754,13d3b770,8fb53839,bc54d902,6d035b64,35796475,67805bdf,af6cdda4,d0177206,384c4ca9,8edb5898) +,S(a7be1017,82bf48e8,37c2e0ec,821e37b3,eab03607,36561152,a95a06a7,fbb7ae2b,dfd27057,dfaae0a9,b9523a9e,dc28a2bc,3631e84d,5fdf8c3d,e1e65055,805fe0bb) +,S(cb398b20,29e55994,741b87cb,592e9099,926229a,aea60bd6,30eaa528,726a04a7,263e89b5,85475a8a,50723568,d3328fdf,56c8fa8a,f0f796fe,7b4d2fc5,9aafe2fc) +,S(438a6d60,c289dab2,e8933153,5c2e09b7,a677cfcc,a605ea72,8815bdd2,615c852c,109ed19a,51e01f3b,a9dbea30,f652676b,b3a8afd3,5ab5178c,ae557793,13025f) +,S(4db8d4d,882fe513,e5738be2,347a05e9,2c97816c,b39339b,adeef99,7b682e50,88280370,d13551a8,a9803a28,c56bac47,70feb07f,56a1a0eb,d132262a,f1ea574f) +,S(ae2ad953,bf13c4c5,c3fd5ba3,389323fc,260de722,e36bdbae,6db77b2,1c0df210,a5866bb1,7580958,df07c171,80606be1,25b3ee73,9c3bae92,8fa35e9d,8efc7364) +,S(85b3809a,3f772b20,dc526b9c,19de8cd4,9c2d9814,df4a9b60,1aadd016,64c66c9d,984a8061,2ce95364,b9819440,551ab1b6,4028f61d,3028b725,74dfbeb6,8c174cd8) +,S(7e2ddaec,e443396a,7c7eede7,ad96c2b,63faaef1,66109949,e7431b06,309259e4,3aeb7f92,14fd37f2,aeec1921,e64e7a8f,e6a4c704,dfbbaf62,779d0eb3,3b1e81cd) +,S(4565a74,6cd1d26e,962bebb4,447985ed,7b1622f3,faade84,1291d694,17c3ea84,7215d7b9,6362fcd2,320c0c29,e0b304d0,ab174103,bfd372ef,e6e4534c,64ff3708) +,S(aa8c5a11,712aa04d,a870bad1,606c9549,bbf676d3,1ea5b260,27bd1055,ed56f996,11ec28a5,a7435436,3d302f23,24eba092,68028664,f0ebd323,cb7bc587,7d92aac5) +,S(d9d5c0d4,b5a3305c,9f057dc9,8ecf7482,f6d825e4,69a641f2,caff9180,b7096e0d,d2b0eae9,cdf57651,bd296ad2,f4403e77,d643db66,fadd7a84,27a150f3,6d547ec) +,S(2ab2e7ea,1e1bf61a,8bedaa4f,e8fe3a7a,db66c25,ed1b7ebc,536efc7f,8de22777,28806bd1,fc7dca1b,a0cdc177,908e0588,605f8c1e,e76cbc07,ea05b6ad,80c0e5) +,S(2da97d42,a9d064d7,38bc7fc2,f2db39a9,588d57f9,8094903d,cdaf5054,238c1568,fff93946,e789b902,8694ff95,a68a7fb2,981ca0d5,d2b6de3a,b3112381,b8360f1a) +,S(470fd28f,31418f2d,70367e4e,961da5cd,af8d0255,5bf6bcf5,e7db8967,6dbb174c,59a49444,2a250264,3faf843a,cd242640,e3622df9,da6bb1cc,6c956b7e,5a26078b) +,S(e41bbdd4,6b78fad7,ee62c33,9664fbc7,e884f88e,dc184e12,715e8368,160a7ef5,40252593,7979b92c,f72bfa7d,b3f378f0,cb4de758,a6348c7b,2b1d73d,57ac9faf) +,S(9761799a,7e155a4d,e6aefab,963b23fb,1f17d8a9,cc5f8c64,6d4dd638,2359c553,493bb00,3ab38aab,cb4d4491,b6b9e8ec,ef068c9b,933fbb32,6e0e1c75,ca44bf1c) +,S(d59fe2e0,5fcdcc2f,dcc22cb,b9220051,2c936bc8,2205f5df,75267a5d,b45b1245,68af556f,949df3fb,d8299f05,f5e85874,57d109bb,4d395634,89d3ac77,3d4b7b2e) +,S(f8bf168b,8a03f3c4,8e7568ab,ef613bf9,3f515f71,93a24b62,827455a6,c6bdf142,434f350e,8eeba6f6,d8f07137,a33199b8,88907744,eb547384,dcd96aad,5f9f3911) +,S(65298ff,636b442c,8c45e748,c3cd2d1b,94770ff3,3bde1d63,21135bc0,89ed81d9,d0aca445,cf460f5f,47090202,2c84fe1d,ac0a81e0,b7458e4c,9b21dc34,8c223ceb) +,S(d1ac0d0e,25dd02d6,fea02b6c,c98f4b17,38046416,93258208,f04a81d3,450aeaa5,be491f07,1833efce,56197dd4,d3471eba,2a23491d,f09a87f9,8a4ff14,257d1dda) +,S(be876d37,fc1605e9,ea0923c4,e53497a3,8d51190b,dfc3dbbb,3191a483,949cae1a,8499ad31,ca5acd99,4a98caa9,2a91a321,5fd1f1f6,cd70c463,afb91673,ab6b3a72) +,S(7c440cd,aff0d8da,6fb5e2c8,fe27a301,c2906a63,7d9b1bf2,db848f13,175de738,7dd6cf71,cb013a8e,9494cca6,b418fb,e99e9c80,3ff34d35,e586fe25,8ffc4fef) +,S(25086ee5,f0037c83,e38abbaa,e0e3356f,c49020d6,f9006edb,235937a0,4d7074fb,35c21eff,8cb888db,d3271a5c,4dc1db52,2072e58d,974af68b,4d6cecd5,d13e582c) +,S(b1a7584b,6f43a593,363d6e62,edade636,55ffc711,48e6c9b4,2915d0d5,ebf4dbe9,f418f23a,9dde8808,59816098,ea06d2e4,dbad3385,737b7bff,a1b339e,1b556718) +,S(87e1c0c6,aece59e9,1118829d,ba8e0271,db30409f,c08dd790,4296b91,43fb7aae,4694c5cc,176cf309,e4bd4935,b88e2fb0,e7e1b63c,28c5bab5,d3356733,184ef152) +,S(19f43d3f,4bf0f2b1,9a157f3,3f71c5a1,20e2135,3187b508,e568dc5d,8131a2ea,3ff130d9,835e0eab,2744cdb2,5607fa63,edcf9648,d910e25f,b885ba9f,b340860) +,S(a7f9239c,5e8e371d,7d175c8b,120816be,2e543d89,a22c3f7c,5478ae1a,14f8f6b8,75711a87,561fc767,93510d5,47abe6f1,678d0cd7,335e7bbf,b87abbc2,cc3089e4) +,S(9f3598e8,f80cee53,969a79a6,d3b655c0,66283a8e,f2c5d7b7,8ce89e67,5d06446d,d9cbf50d,c2aedfd4,51fcd93a,46245bc0,772e9f9d,fe5a25f6,3af027b2,ea1edf79) +,S(cc00a72a,66dfc8a9,e7a4edd6,699764ca,c15140e7,216daa59,cadf0edf,d1e3267f,91b10836,c8ebd031,2f72cf2c,cf26c89e,1489c100,c72a04cc,71c0d08,45c27d94) +,S(2dc7758d,b176a48f,f3117aa9,671f7b6b,f0b2128a,4bc60fc3,f907eb1f,d20ff347,afc82704,39787ab6,bec0d4f0,7b7f2896,43073cfb,65e9d3ce,db8ff7b,791f2f59) +,S(2502f7f5,8dba7c37,bc4637ab,8c77c047,700dea3f,5b760ec7,9b68121,9cee008e,f6fea3d6,4e776f5c,cd293dd9,4109d6f0,1e55c039,c30438eb,6e626489,c92f078b) +,S(70d39d43,d7f2092,53967479,de620ed2,bcf963de,6ceae3b1,a80fd275,1801382,fef8bea6,45807be0,e1c5c3e2,4d391d8,bf0a6b33,564749d1,4fcf4ef,a8aa7133) +,S(a09962fe,9a934004,bc31f92f,526ff444,da7889e1,79453a55,b0fee769,34c69c2,dd717079,8cf19b8,1f55fdcf,86cd3755,f708256e,694fdaf5,f929c967,eff687b5) +,S(355a2555,4590dcf2,84ca548f,9ec9f40b,22482773,cf122a40,25ed416b,9d29c692,549ed0bf,db861bb7,4684ebef,7d70ac56,3703997e,c59b1f86,bb4b5dfa,574a2f64) +,S(4c145d2c,a165a5a8,b66f7fdf,c16bcd5c,c22304f2,f0ec3145,9b9fb0fc,797c9b9a,7cc14054,948caa4f,be04b54f,1b60a51d,cc482fc8,c94825f7,8de4f4a9,a63f8d5e) +,S(b5ac199b,fa591300,6d635fc6,86d87cb2,52c5c67a,6b897a09,bee578f4,9c57f8f3,41e164ae,d3d967da,94f0cd06,91cb3ec0,79c5712c,279b3a8a,d75affda,2879b234) +,S(ad619913,f00f620,38321c3,59931a9e,fd26941a,aa02a812,8d9f3bf4,8818185,d7b5a033,9e4b8ab2,bf66a597,45c0b496,b5825886,9084d306,660670f3,a9de912) +,S(f8314cdf,d7659ce,e8377abe,f4c80b00,9c99c1b2,f2bee37d,a726c6d2,a1deea90,db5a45c9,a7ea22cb,dba3a784,131ee81c,2a1d9edf,d9ccb9f9,6fc93d35,a332eab7) +,S(1fe1bfca,5df9c482,68e88661,89e238e3,7915957b,aa1e0c3e,af317d12,c2f84650,dbe03060,745396b6,fb22d6cf,a6b27329,3b3ed08b,a24ddb38,8942199a,765d7414) +,S(c414da43,814bcc43,31269d58,1c0a8c14,9e304f5c,13614a89,7f414725,9ed39070,ca194f90,dac3f722,6090b94a,3b94db7c,5c68931c,7461619d,8b912692,4fd90a4c) +,S(81a098c3,d1ebf427,69f621b8,3eced558,9dc0faaf,24206f97,480e40d1,25faba3,fffac91a,3b729698,b0b26f8a,e6391b1c,8f8327f4,7978b011,d7d1cebb,6b0ad8bc) +,S(f99441dd,e4332003,b6421463,b4bf6595,172121d3,f000763b,a40cccdf,be2cf439,231fa7c3,fffcee74,bd111f18,3689062e,1b6b366b,2fe14440,2061de8e,4688b3b9) +,S(2f930e6d,e3586768,792e565c,211e1abf,99e05fe8,79c01083,3e6e5121,7abdcc60,2d51a776,209a3da9,ca4dda22,7beea48,a93a90db,b51f0721,2864f36,a56424e7) +,S(9f8e96d5,99fd07bb,e9e0010f,90802d25,4b30e359,bf0fdcaa,d6e782da,62c6d25e,baeaf150,de04c7ae,d8d34278,aba6eeb4,9b3f213b,fd56585d,41890632,d6b64e02) +,S(57f186f6,fa5f4aee,e3e8b44a,d775301b,66d7fedc,4ab8c827,cb138386,64727f10,5a65c0c4,a69b16e0,1c32a95e,77c8f99d,53bc15af,5c168457,19bc8220,d7baf849) +,S(d344361d,dd0b764c,be16f46f,5efe166c,10a88bfe,bf532a8f,a1fe138a,78e0e1ac,dc1afcbb,a84b8524,a5d985ac,5506f15f,d0b92f85,875c2c4a,4144a93e,e455792) +,S(444163a7,58095900,e5c8fda4,88ecae95,24fe54ae,2592ae53,7e6db41e,8019344f,bae54b91,e69ddcb2,c1bf22e0,bdd721b7,a9ca51b,dad93b28,59e09509,77c26488) +,S(5ce2e1d6,46f10fe7,e4196414,37fa2c16,48029fd5,5340d2e5,f32d467,e0c4e222,6e95f538,cadb1b56,8313429f,35fb925d,d9ba571d,bc6f3970,ff4fd276,4228e82b) +,S(8b7226b0,8525a07a,84ee858b,58c418d3,1d4acdaf,9b081761,56b83a8b,fde6d773,684e6dc,37a2ba2d,89c0b01a,b4e1fcc2,1734eed8,2f7b1509,6724acab,5e276006) +,S(6aa5e306,36046384,ac8e3da3,f2f00713,8acaa0b,b0d12ee2,1e69018a,4d0554a3,2144dece,31a7453d,3a07e290,cef1b694,f393e5d3,f6cb07b7,a8c991b5,2a788029) +,S(8537990b,dd59836d,da16ccee,3871a232,3bd841f,6128f845,b0369857,66c7e0e2,beda6acb,946a6e08,43ec2890,c2d11ee2,404cadec,afb3f1c0,37cfb9fb,7b733fb) +,S(85a8cf21,1de57c,9c0ba88d,2f9a773,61041619,639c9de3,e2f2bc71,cb9e66d6,80d1d1cc,3c757338,f5a6aecd,815cd5aa,1d772aeb,5c1efa05,d5b64886,97ff6de7) +,S(d4f18d05,cb183c54,7594ea93,9330c174,37870e7d,48a613ab,4a920e38,18068267,32fc3b50,394bfc8f,a2a3f912,b35ebb69,d388c87b,198c3619,b04b6b3c,36b6190a) +,S(123e8bc5,65ceba86,d4c1726d,5e7e2a32,ac6f628e,940aa97f,c96ff72c,ec79e637,e54b4332,975774e0,748fb58f,3ddc5a14,8c3eb1cd,824bab83,edc76c28,e991d2c5) +,S(ac26cf03,c5a95e3,9a975746,a26bde6,8eddd6b1,6c83a3c5,81fa0a79,e93d0cdb,73ebeeed,b96d879b,882ae503,2f400142,5c8ed97f,98daa225,346a5b7e,9abc4832) +,S(54f2cc5a,a4d81daf,6600e921,441f5aa1,5bb51a42,7479e123,b9561d0c,8c71299e,3ff4e7f0,83f85039,d238a740,b7fd16a1,e313cbb5,b0c6fe5d,ad99f221,da4aa27e) +,S(8bd785,b73cca1c,6c405ca9,1ed4833b,428b7ddc,1a61c8d9,b0d1dea8,ef133586,fc5b65e2,5c3fde0f,3239ab96,625a9751,27a30cbe,f98e1ee,735c1f71,c86cf292) +,S(bd4218be,af5f92dd,f96a54fa,fe873cf2,171c2e30,a13c866e,e830bb2f,abb17d5c,86097c6f,91024f4f,8900853b,d17dbede,e2674e36,9f3fc69f,a2578eab,174e783f) +,S(a4006630,1b63e757,fce2f5d2,a7444e5a,1f2d7509,b7f59476,6951f38c,1755b497,87aef8b3,eb160df9,4b46a56a,337b6400,c724658c,a49c1162,c418e900,29efde08) +,S(5c5af5f5,65b2870d,6fd08f95,b4146379,15cda056,e18ef682,4cbbbcf8,adca740a,c7c6de6c,e679f0dc,84e28027,86ea726b,8eb0ad04,af4b9a5e,de516f11,583607cc) +,S(b5c3629,b86e9235,478aa7e2,d2e5b539,b70765b7,79ace9ec,ae76a659,80f0ebd3,86a8a06,84fef80,7a081bf0,3f08c078,7ad04420,76f5e1d4,521cffec,8b2dc96d) +,S(1784e06a,cd25fa66,72d1fc08,9219cdb4,54f5c711,4237bfe4,a2143eb3,20a0bedc,11feb0a7,d8d43ebd,5bfde5fd,21fcaa5e,7f091da6,3acf4b91,956172b3,5d3af378) +,S(39266c28,48f58af4,47e5ce4f,61cb3ba6,9c3bfdad,7e5742fb,ba4ca3f4,f175a291,7be4b872,9d1e2fa8,fee16e5,bfcdf4ef,2cff2872,e9c7fea2,c1997d6c,5d7f1efe) +,S(d6fbeb8c,72214cee,58b33bbf,2eb04924,f96e9b63,d104aa92,fbb09c8f,c2e4b1a7,cd9bbb10,a800d777,9d4cda4,561a2b8,759b4dde,45dfa2c,319e4e59,39938c8) +,S(42ee0a83,85464cbf,cd4a9ac2,73001bb8,592f2a74,b28ab7c,2eb7055e,2a471b76,d22c6ec9,d5499b4f,26947233,a5589aeb,14c94376,9a6166c2,af9d323b,e1adea4f) +,S(8cd2a043,4de9b238,f4dbc3b7,e56ffbea,4f025e2d,6c73a68a,4aa62bb4,9e9057f2,abfe69a9,986ae1db,52a807c7,c08a1116,a2dc7aa2,ec3b649d,7326bafa,ca74d21e) +,S(14c35089,62dcb50,d01750a9,441eda38,1a95abe3,72c501bf,a99f953a,5c94da3b,bc57dbf6,bba79503,f33be20f,96a54fda,fcd7be9,cb064410,3db11e3e,2034a3ae) +,S(2cddb646,76717e44,86fbe0a7,f7d6d853,7c3fff9,465e31e6,8da22abd,b3eedee6,136346f0,cf5fd102,932036c,3605179d,1429a706,71d95524,a14869a3,d1cae8f4) +,S(d806ee3,b5702d23,50c3f179,a00c3701,9c055103,318594c5,8f10b100,1b705aeb,79201749,21e69cac,a0ad5902,42e39a04,c3af8cc4,c665587c,cc0c89b8,3744b9b2) +,S(534f24,715e9b8f,f5f76a4f,a617839f,6fe2f175,3cbc3d80,331d12e,98f6ec30,e26ebaf6,4affe9d7,1ba2cd55,421c33ef,2e4a3fb1,c59a72b5,93ececec,65a40e1c) +,S(f33af0ef,4e23c8ae,7f3de5c5,22e8bd9a,6e527c8a,23242c8,4ae6677e,ec90b8f5,8510bee6,bacb5633,45f38075,8d85dd0f,493d179c,b9250d25,fe88134a,6055aefe) +,S(3cad4725,12a745f6,7e36fb92,1a2926d6,ae294278,8004386b,272d8520,f5a9aa61,d12ccf97,27da84fd,317116e4,9d45d741,776c8278,4b2c9f1a,e3773e1f,57b8934a) +,S(4b7fcc39,5f50944d,5cf3b3f6,2b1bbc3e,3aca20e6,d259e931,d3398d08,cb6fcdf1,99acfb05,a106a389,b7ad644c,cd275396,d76b046e,9992e573,dcead5c6,c7da96ff) +,S(9b5c0e8b,22a62110,bca2770b,778f0a2d,6d67e908,c57f806d,7470f5c6,abbb27f6,da52c77f,ea3056fa,dae674e6,daff8b21,100d007d,8bdefb12,fd56efe8,13c15a02) +,S(ee7b7f5c,e79eac13,d9b500ed,ee0b65d8,e7c93203,aaad71a6,9a935e31,20722361,e77ccd78,bf6380e0,5e5e8de2,20448a00,cabe7d6c,6b7ac318,56c09e2a,9a390778) +,S(b4774237,d36a59f0,e39a133,756bebf7,4df43365,2a4505c8,b0b84833,dd5b3a1d,5a5a03a9,e3463ce2,fa8bbfc9,afde9c25,763f4874,3cf0b2ca,7dc42c3f,9bf3accb) +,S(5827142,c69944cb,a066a985,79acdc02,83e1df82,8935ba82,5aa55047,5a52271e,c05c5805,6bcb6cce,ed5e8c8a,beac78b9,69c3b482,7ef5b402,c94fe1fa,f7fc81a7) +,S(ba06eb6c,a54fe697,9be9e9c0,f8c77be5,ada7bd7f,43894d1d,1705cc18,9c9b0ed,176be0,b5d3db28,7f235e2f,a9531fba,65f5ff8d,55b784dc,bf7271bf,864e3d5) +,S(ed84c2b6,11c1208f,2f059d37,a3d2ddc9,c3a0196c,9b9dee3a,5f2cbb0d,9383625b,6dff970e,b08bb4c5,4dd6ad6e,df25bbb4,621a5ea9,3869de76,2524dd2c,af1c6f6b) +,S(158b136,86fbdcb6,ad8ea001,9005405,b5cfc0d8,f614592b,d9e76b1d,8cdd568a,8c576136,634af4e7,9034b450,1bae33e0,9b5712f8,642cbeb,2b7c8910,cb28fab2) +,S(687d52da,10df8013,7232821a,990adf74,c54711de,110027f0,68464c9d,162c2410,dad3819f,8071b3cd,dd84fde,550d24e6,31785e1c,e137e115,8b5cc687,8cd09f10) +,S(32e8617d,de77c33c,a1d317db,d350298,50dc7fb7,148913cf,413e04b,384dc81c,9cb2fdd5,15231e83,21a3feef,804d62e9,2b8b9c0f,dfb73dd9,c3323017,b29f2ff7) +,S(a53a659f,7cc90421,21d96a90,3801753c,ea43b363,405af6,16d50b4e,fff054df,96734742,ecb3c201,742f8f64,df22569c,92212928,ca0d5453,d706f905,33c345cf) +,S(8a9e4990,b9b1a2b1,32ca9507,16170713,bcbf5719,2dca9d71,7fc9c4fd,a3993fe5,9542de81,f66098dc,c0bb0f89,44ea56bb,bb7e86f,9c63222c,80620c7d,2fac20d) +,S(5966c5b0,f92c5906,b9bda0a,7b3f10f8,e4c29796,f953db1d,35976175,dcb431d4,539665b9,90bc3810,4b7938f3,47ac94dd,7b9228cf,7270284,a825a202,ac62d135) +,S(2170d9c7,4ca5e85,305d159e,697fd1d,d32d6f6c,a2806992,f082ce00,9139c34f,b5cbf529,66f71e55,8015c90b,ea2b95fb,4a0fed8d,ef1f9af4,518789e7,39076c64) +,S(86e9b03c,2497519d,615f83f9,d0f9bf9b,101b75cc,50059e19,4d7b58a2,9b6fdf76,215d042d,7976ea35,a3dd586c,f5b286ba,a30e013,966fbb45,99845111,db8b56ed) +,S(2cc7d91,ba630172,ae56cf60,9a5d3c05,e27c2e68,7d607c85,ffcbf6c9,a467028a,9404f869,32385290,dff3f8fa,1a085661,5855aa6d,e9742d8f,9e2777f9,472ab5c5) +,S(84a89b0b,304ed760,c3a3d972,5a65764a,11d59ebc,25249f69,1d3b8711,10d3cc73,b7200408,fb1ab866,53b61321,10b6bc53,c8a0805e,568f8a0a,80a65678,c54c5829) +,S(4e9c88e,d10be9ea,a34bae15,8ba3093b,42ad9cb7,356f34dd,33eada3b,1937b4a7,b92050dc,95423a8c,759222ea,c09593c1,342c2dde,5fe382d5,6838df0a,877dbd9b) +,S(fe2883e6,6db454d5,31f2aaec,96f4f3f2,868ef393,d09c7685,a65d476,c6c97a99,1ac28015,1bc7c0a1,cc4be80,c6eff3a2,b8ae70b1,180c6a15,7c89dd3e,3e78568d) +,S(c74df902,ba66c205,e49f25c5,62d1eb18,f4f4f697,3cf49135,16000924,80f3f68f,17f7bbc9,59dd7e6d,9043ad38,7c0097b,403c8d1f,e676b8db,d7f2c63,1fa1ecd9) +,S(e7e7a72a,41f95b51,2ea42d1c,76da24bf,67059e,c27d5313,55c30e60,69a72c16,a20c2f51,6e9ba9b1,fd3a8473,8309ace7,da6006bf,bc42d4ed,657da6eb,6efedb8c) +,S(5a93859e,dc4ee632,9d36536e,1bf1008b,603d6a8,859ac6c3,a098748b,d6e18bce,489326a0,1e05c331,d97db6cb,68f098c4,6712d7d2,994e6c8d,be892138,e2106220) +,S(6161b293,71353343,a46d0917,4f6467ea,25a0f77f,fe614133,7fd3bf77,71062de8,57c439d9,a1682d4f,773c8260,6ee65e88,473544ae,64194ab2,3fafc2dd,7612b385) +,S(c35b16b5,a3808dd9,553c7644,400d6fe8,ca48c8ae,49c78018,4a71eff6,b94ce4ee,6c3baede,4925766f,3a7c59e6,d6c1c966,c3348b1f,5883498e,9956f3e6,6d8fe13f) +,S(fbdae49,32baf904,1d295378,2d227dce,4e2af60a,86796a69,86b777ce,bd2cd134,9ed9b5fb,df42e755,a9cd761a,cbb92877,6c66d299,35dc526c,f6950ba3,d63d0c95) +,S(6c5bdede,17ce2007,a3a1c18f,af8a846a,ce67b751,3d8bd602,23610ae4,b01b720b,6492873c,26a22bc6,39d4614a,202cbb2f,2a08b78a,753592f4,10c855d8,12f8d39b) +,S(eba4e62a,bf52b379,9305bfc0,eecc63c0,5221a0b8,b554432c,15e460d4,21dddeb1,e378e6c8,d6ddc7a6,72d83c3c,9d863c53,32b1cdc8,21ed458c,b817c49b,6dbdc519) +,S(5a79ee4a,1aab7803,23ced132,f9c67d5a,c740900e,cf71a0b0,ddb1d6c9,794cd90e,9f608713,47b3a231,548909fc,812624ef,26eb26fa,98630512,2b1466f2,665176cd) +,S(78b702ed,6d816c15,eb31dc27,619d5bf0,6ad646b2,e39750f0,73c31d03,e50d9385,9f1cc224,91befaa4,fbe9721f,86bebcb0,11024a08,4380e46d,bf11ce99,f157fd24) +,S(494e82e4,1c24936e,c291f69e,791979ae,3e2a6696,c8f68601,b57942a8,fe6b98eb,59f3af2f,c720847c,19fceea6,e3d2a918,ff7f564b,2e3bd7a4,beda6415,65bf5da2) +,S(28b3ff40,467a8c68,69fdc61c,33f002f9,73705468,4fc707ab,c0b495e9,a5c0b401,46a44995,b2c8f0ca,a32d8e33,b8e38465,4cfbaf1d,addc4d1b,852b2a04,58f84e34) +,S(4e4cf4f4,38a40e8a,aea162d9,dac13531,f5730d96,59c436fe,d8acd06a,c4c46be,4fb4bf3f,bc394c7b,57f81a59,2ea14c30,1e9ca990,5f48ee7a,5db08ea5,eb6dfd7f) +,S(e2ace7b1,3a9e3e22,a9b8ee66,c56e80f1,9370cda9,eb3ffb6,2d20805c,7f4c34be,1c8fcde1,2a653492,6428319a,981e1602,44aa823e,a82831b4,d0eafd9a,2159318e) +,S(e090ff27,e42469c9,91dab9a8,dc9a3a41,d46b7482,a59ba9b,cf4de93b,c95c7e78,d070752,c5679b45,4e1b617e,8315a48,1cd9d24e,eb53a671,3b4160ac,64e6f824) +,S(705c8790,7b976b8d,c8ccd3aa,897b2ed9,ea091c37,e285c37b,aa321c89,15ce089e,edccb405,5ae9f933,973950f7,eb79657f,e94fe286,27f6cb2e,784d667c,21ad4d3c) +,S(4aefb3df,663a28f0,d531e390,339a56a0,4da1c64d,4692c086,de232740,6d386d57,167af97b,43a36a18,1dfde22e,633547e0,eaac2f16,84e8753e,1ba1741c,2a18fe3f) +,S(ea909685,ee8c283d,1d7dfc4d,6ec8910b,b8d821d3,3997f24c,db5f5ee3,bb7bd72b,685b64,d3e02ee2,be2045ba,c3bb428,636258db,33177da3,17f6de0c,f8c805f8) +,S(5987cd59,ecccc129,b3498331,f20cbfef,f377cacb,dea76ce,2e221aac,d3aace8c,3427cdb4,f7367d60,44ba21b8,adfd5863,bae2981b,340c9bff,d0d7e93b,ee4d6140) +,S(e23845ea,e6ed3d33,14fd6f6e,f6d6e9f5,92f921c9,f448eb93,edb49869,97837eb2,89eb22d6,455cc139,e42c6263,b65233c5,4b1a3b45,34ec3d7e,58b788f2,6b11b131) +,S(cac7dbd9,7dcd353f,fbf60696,d325bf14,2e3920ce,26570b59,ff9aa65a,a6993199,6e81a4e,cd60a26c,90f890fd,6de2d159,50bb70e8,92eed375,25a48f3,31307620) +,S(9dab063,3a5952fe,f168d6c0,3ebb226d,de3e591b,4ad0c9dc,96b247ae,49a317de,846d87d2,7662b958,b3422b3c,a3410ffd,1ae549d0,c6757bf4,f96d2d77,168cc83c) +,S(bce277d9,ed439b09,f255cee8,af5363a1,12ec2d5e,b111196d,be30fb21,43f501e4,63f472d5,2649843c,42e8f900,5adfa50,d6a938ef,8f4f04d8,897a68d8,8c213328) +,S(df9f8700,2a6baec7,8b79cded,bb501dfd,54f376f4,5756501e,f03a6c7e,ac5f1d7a,2a1c5c92,789fa6e2,84105f4c,74b0a108,27ea2f05,5560bec0,5d0000be,b9b6c4d5) +,S(6211dcc9,307c40ac,3c7be1c7,5553511c,15d3224e,3b305c5a,39653d83,f134dbb8,545c00d9,f02b8833,5fe4851a,88cbde99,f7f02d05,ac674f5a,6559ac9b,d03febb1) +,S(d739aa30,8d9022fc,45735eed,3ff92f38,545f6c9b,b70994fc,b5f41f78,772c9378,d3c3fbb,9077dd53,d023838a,5c35b728,af4a90c0,1f16739d,12870e1b,15e1e6ac) +,S(9d85e55e,8ed84471,b6822c68,3268c35b,fa314b72,470e51c1,a7ac9f25,f8bceb6,8b01f444,e84436ee,ae159a32,7026d279,5c4116b3,b2e78f90,99f1035,1511ac88) +,S(25d39bff,22c40a94,b194c38f,be9d001d,4a632407,237fb091,163e463d,7c5c70f,33381905,11368982,445ad7f6,608be022,209466c7,98b9abe4,f4fca781,2c0c398e) +,S(67553ed3,f14c28d5,2abc36c9,3f15d0c4,7f6fe1dd,628b652f,36b29225,ef8ff51c,da5d42d5,9d41893d,10ed28d,569c0cbe,1cc077e3,36bb5c21,e3712bbb,9c480383) +,S(ea3e80f8,c800b52c,a6dcbd2d,399ec5db,65057477,8852a73d,68af22d6,f1abbefb,5cd8f55d,b5330919,377b48d0,8ac1bce0,dfb8c3ea,2b97a5bf,547b7beb,ea64a4d7) +,S(7cf11aaf,638b1fe7,3b8c5753,48bcc01a,41c61ee8,f2eb3279,c2ad21b7,f6f60780,579d9a90,3a8a77a5,6bef3ce0,53ffb3c3,cf969f9a,39ea5bad,b1fa075b,e918f048) +,S(ed377aa8,42897f6d,1092caa7,42c29285,2349bd7c,a0e0cbce,d3cda58e,ffaf5e17,44d1e8bb,6491dfea,cde53662,5e5d7b52,1ca11a47,1eeda260,647f6f0,f0c5f917) +,S(fcafcd82,dfc87415,6e0db5ab,2fa2b56f,dd488032,d22ddc1a,b7a0ec8,1f1c4083,42f88e6a,cf851a7e,ca3fc28d,f771cce2,bef0d7ce,3d26da45,a2d9c307,e9e7e040) +,S(4d372865,7fde43e1,68744112,26b34713,9c1ca4ac,c2905858,de50def8,5515339c,5dc438eb,7091eecd,a4868c48,a7933b71,7ba99d8d,5e8a946d,ca9605a4,7421df39) +,S(aea6fc55,47faaa10,a898f21a,f63249ac,a0625f78,ea819629,8bca035d,7639e470,4d20c80f,173980d2,8e6bf433,bd1dacc3,1882e283,35fa9993,ef808464,a5568d42) +,S(f0ce04bd,bfa81a58,e157a49a,11695baf,7233470e,baa7ae76,3a07f0b7,f71cfce,f9e4d701,baa6f197,d9912b57,d9df016a,b789e60e,d731afeb,f2e5f920,b4eef9f6) +,S(cc476e63,130a950b,b8284c8f,fe74a059,8d9d44d4,c0372a9d,bbabca17,517523f6,d2cd50d2,e2cae10c,df744ff6,61a6c96b,64e17368,5f81027a,c937a292,3ad7476e) +,S(90e0477e,8dacfda2,7a563448,65947a05,d0afe9ea,60634473,143508fc,6abfc73a,d117be49,166bb010,afb0b82,a46a4204,c18ffa42,e3c89764,3eaae0f3,303dcd9b) +,S(522a6781,951f6ec7,76b6cf9f,cd20287d,6977764c,517d4ebc,7209d55,5714b5d4,a109c149,6318d481,4cb22199,3567e5f4,5147b64f,c1f9216b,922d653,7637031) +,S(b0a4c701,725326e5,611ea140,325b0536,b22406bc,c1e7eef2,1f874ff4,e02ec2a5,df44faa2,8a60eb90,3974649d,54cf340e,293fbd09,66b7b7d8,884e4c8d,dc1ec875) +,S(9e874924,90f8d4ca,2ada28a3,2bccfdaa,51ead138,3421d7d3,482af7f6,6df47c51,a58a577d,d03a719d,1bbb64,6ec3051b,44535da0,5e7e04d9,fca42716,2e335fd7) +,S(650a307,7b8007ed,f0441dfb,b7103d3c,d5146ecb,b949f9d9,c8c99e2f,25df7324,aa05b394,8c909957,ed1bd7e2,720286d7,56c7e413,8aa202be,b08bc446,2fdf86e8) +,S(f891ea99,2a3ef255,6feafa4d,7dd3f7eb,1f1401c2,b75d65d2,6ab81372,bab6f932,f7002bf5,821837f5,8df109f7,2ba5eea5,6c767a4e,198a7594,21467d64,df5e0bd3) +,S(d35e82bd,44fe54b,5d2c0543,91260c04,a5ce8d47,32e83ccc,2f3035e5,56bf2626,b002495,99705173,e0a45f97,196b9e41,1a7f5d1e,33f1b39,100b511c,e51f6953) +,S(7dd56d5,1f139070,a382af7a,2db135bf,854c8e98,2a325b41,ed869de4,349323bc,a23a473a,7f263bf4,944ab5bc,2d82e20e,8e9256fd,b11dbc17,bc0e6bfa,a8325f5e) +,S(afed2eb3,c5a95b4a,e88ee73d,b57594c4,7013cf13,a48431b0,f7930d5c,27f5c3f2,26b49096,da666097,3403eabe,342ab065,78c8ec6f,bc3ac7d7,2b3c95dd,54735dbd) +,S(13826776,74f36fe9,ca81cea,ac65c2b5,51e9fad3,af246f,4cdc65cb,cec534c5,a78e8139,760a94e8,3b3f5ff2,a65fbc7b,6b2b55fc,978b8399,df2d7947,18b496f1) +,S(99200c12,6fed5ec,1fc849f7,e9769a7b,aae3178,bd6b59ef,2563ed6c,5342b80f,7a7f1713,38a84536,e73f358f,91eb80b1,6ce8ef27,ea55795d,16aa7f05,577fc13f) +,S(159febb0,27e04f1c,321b852a,3cca550,28aac30a,8bc27caa,73a069e4,df7029cb,6bc7e080,8834b880,c590d249,241b709,e7280a05,7cbc1897,9b8cc9b3,cc21a821) +,S(2add9be1,a3acdd9f,efebdc0d,a4272ca9,311567e4,61db8eb7,3519c1c,6473bbc4,357ae640,a88c55e0,d3559faf,b0775d78,2373537c,c25e595f,f21ed1ff,b36b5925) +,S(8e2d4265,1742837b,b077fcd1,488e73e,5dccd6ba,6401c6c1,8d36fe9c,c428b664,ee9a5b1e,a04ab8d9,6430ef39,7756f62f,a1dcf35f,fff314a1,c3eb1dc8,d9a1e5e7) +,S(79dd7d76,5b4d7db0,537a69d9,b94210e3,8e7a11f8,cb9cdd8c,6aed6a15,748a3ac9,868c4b6,26dc49fd,de693201,3b89cf13,e1dfa1eb,56fb4de8,9e5467dd,86e449d9) +,S(552c2ba9,96a13b57,32c3b2fe,d4e9579d,365e03d9,2bcb231d,f14d1d1c,288a963,118e11e8,646a0335,6834379c,bc93c1d1,3ad80145,dee0a51c,49b10e17,f6952f79) +,S(fcfde18,a53b5a68,9d33bbf7,79d0b699,2faee8ec,243e72c0,7ca09185,d997e581,6709b56f,43013d47,23f15969,ee01b0b9,ff0d9d95,5e2b98eb,7e78e1ad,49cf3d80) +,S(e9e9d7a7,4ad279c5,691367dc,9c4b7bda,b27451b6,a8e4ed47,afae1883,96d8d82c,292bca82,e6d1c88e,8869f33a,7a6df2ec,ad812dca,4c46eab4,5c56c3b9,15f79497) +,S(514054e1,3e7ceee9,3a19dce9,96256b5b,5876a55c,6cab3c48,74b15547,4a58f285,f9ea80de,80403f34,2866dc40,cb0a9aa,4b38b98c,c4c38e3b,37f4da03,fdf7f5e7) +,S(ac0364,eeb9e7b9,b9589051,635de6c9,447b327e,8c820272,f04a4750,6cc5f290,ccde3ebf,13b5a291,a73b0c8b,9a69414,4b217661,7b0a7735,3a6e92eb,4af21b7e) +,S(cf558594,4a73a11a,ef500dc9,60cde799,ffb4983d,ae76826a,a027fd77,811058ba,369ed987,eac1683c,4e8874c6,40049a33,867fa0a,520e5204,df328d89,f197bfaf) +,S(ee216b85,62eaf12c,9189055e,32cc55c8,2ba5e3e0,237de083,a6e77878,63c56291,2a8bad1d,a697832c,856f6adb,e1cc5058,77e543c3,429b090a,1d3cf0,d2df6248) +,S(89903a70,55dfcb5d,175acd28,f7161862,c9fe22dc,ab752d88,3f52801f,92ab1892,e89c4f9b,7954c2b8,cd3b4868,ae171331,871a2693,9355d88b,8d37d4f6,b90c143f) +,S(f9830f50,66b1b44a,89231a81,1192a284,9ac94ec4,13f5223d,51d1e75c,a7727884,18ac8170,58795d51,cb9a5c2c,c249bcfb,ff3fe3b9,1506b721,763c8fcd,7e2ae44c) +,S(326be8c8,55c9f742,19228eae,f7d79c48,b02c2e04,3bc28e9f,275d6754,31afa8e0,293647f8,76b00450,b223e715,5faefac5,990322bd,eef964c7,185acec7,7c105d06) +,S(6117b67e,bbd6b120,906da047,88ad7dde,3549bc5f,c4f80e04,9b216b00,d441dc9,7ebe5576,17dabcd9,f41aa34e,1443e12e,715c9a7,ccfa04f7,ec6a9a25,74c1e540) +,S(1fa858e8,aed841c0,902db240,aa0e4c80,f26927dd,94e0e404,7e8ecc25,216dafce,26566614,2111086b,caff0e12,e4b38940,ecf05a5b,58f3e705,d7011b49,413a52fc) +,S(dd77d202,96a15eda,eef79ced,9b59e77c,1483b936,6e9949ef,ba3298a0,efac4886,b1914a11,80cb154a,f791a934,8c89bb0,70f2ac9b,acc50c5,f75b425a,df6bf125) +,S(e5fdfb45,1e7ac947,ece4e8c0,5b87b9a9,2d49aae3,5ef95f5d,1d18bcad,18053337,841d054a,47b4998d,d3ad8dd3,d2a1ee68,406a195b,1975bd1a,d4194417,fdc4cad5) +,S(decd87d4,6fb74380,56a83258,bee67d9b,1a49e170,bb1b549a,a5c2a2bd,cbc050ef,5ccd1cd3,614fad6f,601a4ad6,e964ee69,a1e06e8a,2e29edbc,7d19562,a4aebc5a) +,S(1a94991b,939bf699,468c465b,5aa4b064,520222,c0ccfcff,193a8484,6f6e4b8e,31b1e6f5,30147e1d,ec0199ae,1830a36c,bcf4fce1,e1258441,3e89eed6,889b9d1) +,S(f32874f,d0f18d03,7f0ae5f2,ddba8f16,fe7ca4f0,d694e803,b9216c59,91bb456a,1dd8f5ed,f228a9d,53cf881e,a4e3c479,6017be43,e613ac30,690435b7,84a4056) +,S(1c5991ef,154b79dc,df2ceafb,3f7fd7b0,7ce7563a,9312ee6f,1dbe5960,f91ba569,497d2a93,28ddb18,221b0075,3432f510,bbf042a5,ebc44cf9,3090d064,4a252a7b) +,S(3c88095e,dce54996,3bf6e312,4dfc14ff,4ed4ecc9,d6240af1,6ecd5984,5f01de9a,499a17ee,e13c81b9,bb7b297,5e7ed1a1,231d9094,348d22fb,9254c520,a65be01c) +,S(c849c359,1c4fdb68,55971840,c0b78bd3,59d1de02,130adf7,e277cb4e,f6eb7748,6add7151,b2640b78,b8fcfdc,fee4310a,4c4c97c8,bb0cdc2e,3bbdf802,750a7920) +,S(f53017aa,a5f79e34,5a0cc318,f40115f2,52ba5401,614b9eb8,ac9245cb,3ca5680d,2d8b8c24,ad89361b,6d77bc72,fb0c7da8,ff129a5f,2bc00e26,e635483e,c4c44b1f) +,S(b3657e9f,84975313,640b7302,a288ef4d,2dbf36ca,651c0189,b39fc10c,87d7c4b0,d821329c,a2f18f94,7a71f5f7,51ac1b2c,e56aa432,992e7a3d,ef84e631,db61fa14) +,S(79942dcf,a834e013,77bb3eec,b7fa31a7,b4eabe7a,578c46d9,3a0dd17c,b3455f61,4c08fe53,c3afcbd1,7fa0a809,bd4ff7af,88713a3e,d7713f82,18c4d4d5,2a757ac4) +,S(e5f14474,619e3acf,905da5c2,3d1bc311,7f1d58ff,8c63ed2e,fe939a22,5fd9d9a8,d4d225b5,85b6b319,431939a6,8e2dafbf,8ed90a6e,5fb559c0,70779d67,7c03028c) +,S(bc5ca503,3ca4398d,5f3c6240,946c5d7,c9102996,e302b9bb,640aaeed,8c9b54d2,ac15cc8,51e59843,fbb2956f,55d39f24,200922bb,6cbf1e86,68ed3ecd,6fae2627) +,S(2f3abe56,d63a2afd,35e35d4f,9e964c61,a5483402,5fea8cfa,66ba9aed,f75947b2,7adeb38c,e14ebe73,8dceae8c,77903b24,3066aed9,bc4886c1,356449a8,27496d5b) +,S(704bf0b5,f564ccef,4d6e5f8b,eb2e90c2,4b46d581,93cc0a41,5bfee2fe,95021b75,44618d17,c54684e,ea1ffe1,53892090,b74201d8,ab3f9312,e7359ed6,5ca94c27) +,S(a898e473,d4b59956,2a905ede,afc78bcc,9f374c6a,f524fc37,86a91375,83d749dd,5452e556,7d72dab5,e085ab72,1168b5e9,c96a0fce,f9d5862b,9c5a0daf,3769395b) +,S(fc4f83da,817b1648,a8a1be3f,dbd0f309,db712225,2988ac8f,64774c29,e8d9d573,57876e31,6a0ea473,3e34f34c,b0df75b1,1194017b,258cc944,7ccded35,19edd00b) +,S(63e97e30,82c36634,d151f0a8,837956d6,16d5d4,64c66373,253bb94,ed5c8236,af68f06a,e045e5be,88242af4,2e314ecc,d719f76e,33092e6d,8b69f791,446c2acf) +,S(4c06073,5107e9fe,f27dfbfd,5df447c,16f1c911,8ec5cfc,8083a588,9dd82b23,8c8508d4,b86e9eac,2058a912,f2581ae7,fe81647e,e27075d4,fd01b051,fe0ab636) +,S(667f609,7b6af1f,cff58520,83175f85,ea01a1d3,c508b12d,a2e19281,c8d8e443,ff9fd0d7,4a0cb42f,3d7ff6d1,67e5a65f,23c34679,caf3f94,ed5d1073,af31607c) +,S(e01e169e,7fbb4365,58e07dce,8f8dead,e2ec2e27,7d18db95,ef8e9f85,ce800fd7,f49f594b,9d60c59d,155ea702,a1b5e3a7,b26aeec6,71b275e7,10d0139a,21041960) +,S(ed10fbe2,565f80a7,65fc90ef,224c7893,cf9df84d,aa0a3b2,9089f7f1,c014ca50,ffc44137,c3ac6fc6,fddc65d1,87d6b456,819ab1b2,df461ecf,899c70fc,59387031) +,S(4563833d,d0052218,5a43bfaf,7e55c2d2,62a12feb,b76254bb,e8310687,e917cc36,fac05d5c,c781d49e,9bab4cbe,384b511d,1c8c1fe4,f68cdc2c,8c576be0,1c669e63) +,S(bf5907b0,1d05136c,b0a1b514,73682fa1,68772a1b,9f5db994,3bdacb3a,1e8c47db,fc69b036,4559f5d3,f9e0c1a3,e4da38ac,b9968e9,17311288,69da2158,2e334cbd) +,S(be106d73,d01ce0fc,5555d4f2,379c906,1ea6ca18,ef8ef8fa,754ecf64,cfc27fd5,9c84f04,947687e,f7d641ba,ff89a11b,da9e9769,8f8ceefe,54e2a4ca,267956e) +,S(5bceda4,2e94ac3e,68f6a3e4,e4509872,4724e9fa,59ee0918,859884e2,2c98f15f,4b19d70b,e1471e3c,7907f797,7311f5e2,ba78c6f5,6f2ccaf1,cb6b684f,7d048934) +,S(e0831b14,fcb2d4f7,3d17b6c7,f6d16803,304cbb32,2bffcd6c,84bdb891,9d7cc9d7,b474a715,5520cd3f,65b64d08,b8e92ca2,4b2238b3,11d91db2,48720a6b,65444038) +,S(df818b88,2300aaf8,41cb3962,2f0c9166,468c1ad1,3aa284f5,89dc354f,bb1fe97f,40f571f9,f63fb358,231c204b,3b9707f0,c5cf11c6,d7ffb93c,991cf7f8,dcfb3895) +,S(d9fdd569,bfc89237,4bc56a1e,e56bab5a,cb5e0ce9,85939b85,c929fde5,82a07bcb,5c56b150,4cf3767e,e22e4b00,fa467b75,4c9ccec4,c1364216,d34ee210,aa5b56ce) +,S(11a93998,d031bc3e,d55e74c9,ab327945,cbace727,647bfcd7,5ed04a2e,4836ba,4aa311b0,6fc8fc86,b67e8eb2,1e75d997,189fa22b,a33e7e99,b07d610b,1c94655) +,S(7c4fc747,7319464e,2d71f787,80aeb385,aebb0420,d8f8588,9652d5de,aaad580d,31d6d7fc,2cae58f7,b3e4804e,f615d7e1,edf9d06c,d1035b30,ee9b55b,21b06eae) +,S(83704a5f,4ef8ca9e,a3d5b163,2bb3df98,acd774d6,4e07b219,b9a36a9,ce88e1b7,52976995,faeb6179,bde4c123,1437dc73,b53b6f90,f0cce08,1e3e1644,e1ebeb18) +,S(5de15f19,21258b5c,f12f240d,147bd38e,fd5e3c3d,dbf148c5,9e541c01,ca7d724c,c0e18562,1500f0,e3fbbdef,45550e16,5fc2a2a,adc72558,e49f1e3b,f5f4fb00) +,S(f5d1fb46,e3288419,1f2cc8e8,2ba81ba7,5bf1d7b5,97ff82ae,8731d27b,7f059938,3d5118fc,9acc8394,9e0bcba5,c7c79fec,2f0cad64,7910976b,e4e8f9ff,43d8baa0) +,S(8ebe619a,985f720b,9381a99c,db63d089,6a787dc9,7d007354,fa83b931,223f8596,7968b6fd,e3dcc13c,512aae25,d9a8127f,313e2d9c,34489606,864732b4,c3b5ca1f) +,S(5b14482d,f96cb0a0,5664bcdf,e4ff7b87,6169db9e,8107b0b5,78baba9e,b1f8fcdf,19a4ed84,cd2ab376,b1e0c2c9,ecded5cf,2e874f09,6d84d946,22be5806,9f8a5e82) +,S(ae810d52,7f94dad0,d140877e,ddc5e0e6,cb636325,f4c926a,1a263f1f,beb9a8b8,c94f0c8a,f5ceca8f,ea29dbe6,c8afa649,f813744d,39e303b,c1f0c135,867658f) +,S(38853d15,d74060fc,7eb2a56a,b8772f6,855aaf83,b731d853,b99fde93,e7c06e7b,ade73ce5,4f513c89,531b5bad,7f47ce7e,a498da85,e378639a,5ae0507e,1f27fa7d) +,S(837a1d99,db477c6c,da832700,37f111f6,eec5402,2bf74773,4c18bbab,35b503b4,ce852762,ed9b36cf,65602da,cfd3a250,c547a971,1977ba86,64f72cb3,89ac1f8c) +,S(3136acd5,a1460d8d,717e452b,a2069b88,4eb6fa81,756f8768,932c210e,df08173f,42ad61fb,edf97b53,f59ed7b6,afaeaadb,831134ef,73e33b1e,26b2816c,99fc330) +,S(a9609269,c3ef7f09,2a43e394,e482483c,119d94df,10bcce43,6691230b,b77d3201,8c580df7,9147069a,5c84abb,73e28b6b,ab9c7c0f,7803d6f1,d9fa86c8,f324be1f) +,S(3cb613e1,1ce19ea4,a8a90eda,cb182c09,5b1601c4,92822974,6ec22fda,be699e08,f05a18f5,aef9dfe6,87e69a9f,287fd868,4c975765,284f578,50294598,51538b05) +,S(b41c56a7,47232950,7777847e,178eaa67,ce61f561,21c7f21,2c525bd3,e7adc359,b0026b4b,6f9dc954,7efedd80,d914a3e9,65b17378,e6e0055e,527f6754,c5c589fb) +,S(f44724a1,3cdcf9fe,5b4a8f3a,f0d3e076,4559c14d,87eb8ea3,803a41da,9ee9dbf5,355b54b0,7ae7c477,9eaa314c,953340f2,f5583ae2,ba730339,acd231e5,c3d64627) +,S(6fceab96,d5851eea,b5db9caa,9a0ff7e7,86c2e097,5c2207d4,67f343e3,cb36ae1a,83f2b505,20094ad6,cb8443aa,69b8af96,b531341b,81cadc21,565d28e7,43bf2c47) +,S(2acc57ff,ff168361,9275273e,9a821fae,54947539,7f6b1936,13917af0,ef83cab5,764b04bc,9068fae5,75a202c1,f01f87,f3650358,3f5d3042,1e246020,9fdcb94e) +,S(2ac302a,28126e57,5e6aee8b,2b12ad19,f5e1c056,2d63d73e,80204b1d,1d4227d2,74494cb4,1e7828f6,9d2dd529,a70f2e52,9bf4b524,26a2a3c1,ab10a974,99624589) +,S(c5de085e,4dcf8dab,497a645,c7abd513,4054b225,32ce8405,f6e5134c,515d9de3,d1da7171,31a859a,a2c90816,386f0318,9759326a,6cef86f9,f3425bf2,390bed7c) +,S(54729254,5f78e410,db85d01a,182bc5ed,84bf6843,3b5784cc,852d3e70,acbfe90b,618b0f0a,7919751f,46c0ec61,1dddd798,cd89d4dd,32d1acac,31432308,d1a615ba) +,S(c6fc6b5d,84bd05b1,ec8b0ed8,fd4eceb9,dda38669,3859d90a,a0853c3,7f75e8a6,d3d8ffb4,988f7e47,2c26bd4b,83f95453,149d1534,bfafa97c,39a1e8d2,70c53582) +,S(1e325c1,ef4a976e,77d6e76e,f9a6dc51,175175bf,23e2c46d,7203d6bf,86ea05ca,60f35511,510aad52,e156e265,6e7ec137,c319b8c1,a1591258,590186ee,be4c1471) +,S(bbca6232,ed8c6093,7ad123f6,87af56db,28947e01,99340510,95850d38,6ac1d89c,d1257327,269836af,2793a1ea,21106bb7,ec95885f,de541f66,1a4a7c38,39c222a1) +,S(c55b1ec1,1f7aff98,710c3b80,34021aa1,83645a14,edfc6926,cd56eeb,f1e5332b,e541bc98,7ecd2b66,d10ce29c,572272ec,28c26b1b,1703919f,d0b28e76,4ffa87df) +,S(f026dc49,feb91051,68f76b0e,7abcb9a3,cc78a5c4,e520c2,57d2f688,5f1c49df,2471f06b,5dc74f9f,6cca7d72,86571c1b,54ab3813,d30bfb57,53274a4b,4257f5c5) +,S(3f721c3c,605169fe,dee4a4f2,b07865be,e1f0838c,669f7eb0,f234e700,f2fb16f5,9691b1cc,ead5fba9,9d9f1feb,fae6baa3,cc7717ab,c5a671f3,8def1556,e27e100) +,S(65bccdce,bb7e968c,a1c5dd12,2d5440b,97b07014,9838f4ae,95962044,b822b0ae,bd594952,f8424625,91375009,3cc6716f,cf7bae14,a6fbab35,4a962282,ee0b3fdb) +,S(6c155658,7020a902,c225d721,7b1a5f2c,5646179,df5613d9,b95826ff,7723f42f,1ee346cb,5841939e,42aa41ed,84bd24b,33075490,68de1809,8e5f8605,59e9c073) +,S(d2c9f745,8f75b42f,b48dea34,13b2bf9b,dc045542,8c5a3dd4,2a08716b,bfc4367,e2a9be5c,8f06a432,6f148cb3,cf58f767,cfc8f8ea,1ff4656e,ec19a64b,183f650e) +,S(aa11c12f,5fda607,aeb4af7f,8430cae,3f5df00a,43910ff9,e413b3a4,7295a38,e506e63c,a0568d96,2c489851,489fbb62,acb60d9a,fb9dcf1c,7aba7be2,2e02c67e) +,S(a355e383,43937932,21affe2f,843d2541,3faf807a,900bc299,5a201f10,d5748f99,22ce77fc,c627019a,ff0245d3,ef2b31fa,55687ea7,69a4948c,5656945c,c5a5c74) +,S(d8f4e393,8b93f252,da68e1b0,16fe8889,d837ea8e,61e5d3f6,8928bf32,22643cfc,c895006,61003ee5,3d8f739,d6fb7cea,e2f7b414,28a65ca1,bf493a3e,27a2e826) +,S(f69ff0ca,3aa0ee7,122a00a9,4b6ef41e,13945263,96e8c590,84ab2b37,6b899cfe,a5211104,271a3020,d65ca305,c0daa6ac,cbbdb1b1,d349a2ba,1abcfad9,bf63a7d5) +,S(64b1b59d,7415d433,7f5b9a3d,98f06d10,7e647525,b347011d,6d3b423b,2a377592,12ea2814,ac8e0bf,1da8e617,c8f051ad,5799593d,f9ffc1f8,abc963b0,faf202b9) +,S(bac6a7ef,9a3de971,51044204,ddc3a2d3,4668613e,101a7e6a,5f93c25a,6b20dca9,7bc2b764,57620478,321de1fd,8ae8afe9,6047d087,b95390c5,bc69b972,654b97cf) +,S(fddae798,dad79951,a4c3b803,11ced882,903c9dcb,4d4dacca,c4cadceb,b837f688,2e21e1f9,f45ded15,76829823,4892d2ca,26ad5b6d,7b0baa77,c2dfbba,b1dbca19) +,S(3dc783eb,40779120,25b7dfe8,f493f073,b3a8520c,ef0c94b2,1c3a5388,b0a9a01a,b3fd9020,2ca16d20,fbe8cee8,fb8bcbcc,821696c1,15545aae,84c414a0,c4401bb1) +,S(299a187d,299c61cb,6893a5d9,cc8831e3,df92ab13,da2f0048,555d1358,e55e866d,ef0b105e,52a18751,56080cc,cefde2b2,848d43ba,6d650068,2b71fb9d,d34b819c) +,S(b690b4aa,fde43b5b,362de597,100c74d8,97cbc750,688a46b4,cb1093e2,e253dc4b,affd5af2,30e4de4f,9971627e,dac86808,c97bfa30,f051af29,bf91e36f,c3edf0f7) +,S(7217384c,1596318c,2a98db2b,7264c909,f823de21,702f5e64,b19af937,cbb8be39,b125b7aa,1bd67232,df7d2d3a,a671bbc1,bace159f,3ba2fad5,e82a1ae3,965d5c01) +,S(4bb4a86a,58f1248a,11f61599,f420fc69,bc77b7c6,86d2838,f4368971,67013852,29909cb1,3bce655e,88300d78,4f7984de,352c9b9a,960148e,a5b65acd,99b62d4b) +,S(4b3a9328,2e473993,9d7722a,7de44fcd,51658eff,93c3711d,3a8f0193,9a54902f,9a012a8e,7df3b988,204fe0e0,50cc197f,a27618ca,d8d712cf,520c7be4,cf4e0f02) +,S(5488985b,26978af,50099d93,8d7b1aa,c545b07,44938190,e0b880c2,11782b26,b51f749a,76b998c0,83dcdbe7,21d5fa0b,c4aa4392,f81821ab,3436fbf0,37d37bd7) +,S(7f2ff4bc,f270b566,d2d26cbd,ffeb1b9b,31b2cd7,b647d89a,2c86c003,b23f644b,f48749b6,21ed28ae,47223281,fe8e633f,35580bf4,8eea29f,d6aae487,ee8e0588) +,S(3305c475,1d63baa8,18f5bdc0,b4e46c9,acdf1c42,9ff5d177,bdfc214a,b838efbc,491d45be,85ae9675,310ae85d,258c6c9c,8e28d7fc,5239363c,911eb12d,ee68a435) +,S(4195dd13,ae5ea569,179dc64,b07f66dd,4a2e2878,d81a222,304d6a88,4da60b86,bbdb99ff,8d4ce150,543b48ee,9109f127,5234c69e,52293758,d0171ae4,ecd49677) +,S(8dca7466,8ffbf319,7dd9826,cfc7f450,fed841ed,83e1d466,e92470ca,1e2582e0,2b19be5f,69fae0e2,563a3f80,2f9450e0,fc48ebe5,973f542c,4c12fbea,97627e57) +,S(e235d150,3449667e,ba23d119,9b5225db,1b26220f,60f84476,70c6bdaa,f5eb7aa9,3d4d8dd6,b0c8c2e0,c8f54f6b,dffe8dd0,5ae22d5a,b4f9dc18,f17acafb,3fab327) +,S(aa214fc7,23c3c458,8bbfd962,b86cc3b3,e1ed52a,25524ed3,ee5c4933,1748ddf3,9b54e29f,63240e30,434f7a22,2679006c,24593e26,90254978,35a60897,d3578e18) +,S(1c468a51,cc9d2632,860bffba,9ed83e5a,e97dc1d8,c8bc3577,5dfef223,b3ffcc73,fc569e94,8eb20528,98b4cea3,86486afa,cec0188e,44dc4e0,25b34855,f1466e87) +,S(c663e752,ce62dec0,81f8ad57,3e4f5c92,138e6a8b,e6c9d015,d5cd2cf0,ccd1a203,ffc249a7,554fb97,c0f9d96f,4cb06fb7,ce6e868c,e7e60b30,f67a2b9e,919f6f98) +,S(949a84d8,5f68f70,81f379e1,9d673748,bed14bb8,dba2458c,34327c03,c893067f,1b9fe41c,f474e57f,8497f6f6,76472a28,fba6b259,91297f81,474cb6fc,f31380f0) +,S(6a4bf3ea,5798c8ed,4b35c629,c6cfdc0e,d16d0d27,39f8bcb8,c34a9105,fa35fed,6f69e357,d5123305,56d04946,f9265353,77610fee,20667815,e7f3cb01,e8196b24) +,S(d0a99a37,d1df89c0,99b0dc1c,16f325b9,b2ec1455,21d4f80e,ad67da85,4f2a5357,7a75846d,eaf7c4e3,5c0c87f,83732eaa,7f6903f7,b721648a,87e1909b,53d3e9c) +,S(e2a45683,f3781309,b6223a7a,5ca64c4c,cbc5d45f,29c66082,912568ca,3ec6d3c0,8e386acb,63c52ed6,5aaacecb,fab458b9,9585951e,cd0e35a6,19c0fc39,5ce1c0ab) +,S(9527ae80,10ddd443,7237c2b3,c8f3afb9,f31e1696,5b10fedf,f150c296,3cb8e60,6e0843a,87833be4,f882c417,7198d985,5af8b144,11e78c3c,497fc49f,5f728479) +,S(15f15c4a,2cc43062,d4360a8d,dcf8e0f,52bc4741,c7924f6d,e1dd9f12,a06b9b2,66a127e7,2c55db1b,57050f4c,a0d8abf3,9ccb455d,9785a4f3,2ee67a42,3bfe5ccd) +,S(558648bd,64c52dd1,189912b2,67078e8b,d3c68952,47e6c848,d7dc529a,7a24a75b,12332187,62872867,5bc88378,88deadf9,3a8f2c5c,864371dc,6be0be63,44fad7ed) +,S(1e87928a,b6e10b59,6b231b08,87a823f1,3c6b3ff3,2852d35c,d1167244,fea2db0b,4cf60a4,767783a9,1c9747c5,7abbade1,3d7fc02f,e833b474,648038c3,b68d96f5) +,S(7a169de9,1d4c3f0,3296279,2c3ebfe7,f2eb4410,f7b9a34d,c402ae64,ce9231fb,1ca2e8be,7a7603b7,d95820ab,86487a04,c91ab3fc,c88e2f0b,514a1589,3908e463) +,S(65185a2f,bbc8c96f,cf9a6df4,d002128,afff1f2e,5c0182a2,95639a7d,9fecaafb,10b6902e,e83c5683,3d796b99,665cf365,c1e41d63,41fe72a4,72e5ea6e,39dbb9e9) +,S(61c45212,4664432e,8fbcd950,1dbbcd9f,1fb8842d,c8e61758,439b6d5d,bf8dd626,2a9c7c57,48509dca,ebfc5062,4d7a0b0c,21c9f5a,45733f8d,e0441bf7,f2b359c0) +,S(f61aff9e,267b6d91,e3d4ab45,73457987,b1af9a9,38b659f4,1ad5cf57,8cc59d62,51d0b694,e5692be0,6841a3eb,160474cc,6c248213,19c9648,c32ea444,29cb2553) +,S(29f27a51,8fd448cd,73aee0dd,7316e566,c655ca44,afc5daa3,ee359ae5,b96f8bef,d5957a97,a6447a8e,a8301219,5fd53c1b,63784a8,14c29c80,d0061fac,2c6cc1a7) +,S(c656d66a,efc3597a,2ee977ba,bdb7357,a2485a6c,d654c56c,bc58a0ad,598f202d,f8267b3f,f293a5b5,11f4e466,dce0fa88,a37f1603,a3a8092c,934fb824,1427a59f) +,S(f28477cb,efe654fb,ccd3a9ab,e816f950,3749e183,3b1faebd,418c85c9,112a5135,9df0f505,6ad98099,ac0dc723,34af973d,9c98c597,ee26e22f,a0a7a60a,a3b76cb7) +,S(d007a1a8,b6f05046,a66fe0f0,9a02e510,3ca7555a,277c40a8,87a71920,5a429e79,d9a68c51,f17fd51e,b19e4946,694e90f5,2cf664ed,d2d45489,5f880400,b410b8ce) +,S(348f74ca,7e5b1ff9,e6ccf3d9,90ff1f7a,4e3a8296,725075e0,33a85b2f,5ba0f338,1648c7f,32a206a4,e21e7309,f1a68a4b,b2ee116a,f3fab3f5,7455da93,7c4afc7a) +,S(1f54e125,7ff3dbcb,f1b55720,564f0c72,d322b8e1,f14d2b9c,1f6244d9,c85f6f70,34c23405,4a970b58,2cbbcdce,e67cc564,6e187cd9,b3024dfc,731a212c,c53c2861) +,S(a984f8be,33360b3f,a012cb99,65ed10e6,43a8df49,26f8188f,24719a7c,ad6b5238,79a592f9,15c925a0,98406d08,75ec4fa,90b5015c,4001addf,1cab32f7,9f06f213) +,S(2ca5e948,76db7877,fb828609,68e39c8e,a413a057,1a39e20e,a7b98f93,f4cc4887,b1ce7c2f,8380eaff,57299cef,e840c552,53629691,7ed367a3,5c59c3b4,ae67a02d) +,S(65bc7a84,15cdd07,5f493310,de05b7a4,ae7c874d,5b5221d4,7ee556ef,4c21a0d5,ad20620,aa39a14f,dfad58b7,9f1ad70d,2c637119,d9eb33b7,ca9abca1,f5dc3db3) +,S(a0b6b293,a9e25a97,18fa0db1,16362757,6e39cd43,83140822,b2241fa8,c432f276,2cbd88cf,29570778,8a6d79a3,b451833d,c596fa08,8b26c6a9,962880bc,48ae2b19) +,S(b4d308f5,74f7832f,4f402626,5ff81788,91a86724,9df74486,f241baf2,28e130a3,ca82dfe7,4c5768aa,46927fe,70c93227,cc7c30ed,c2fb6303,9a1552db,534019f9) +,S(819b48da,f19c48c1,f6efd0b7,c4ff5996,dcd5e99e,7146bde4,1c83e333,ac891b3e,c15bcf4,c9a630f4,e1489585,e5719746,ac55210e,c9b82da3,898a0d88,4eab0459) +,S(66528e21,adf8c447,526d616c,777dde1,fa7469ec,175facb8,3ac9ed2f,db773f85,c5d38c7,c13449ad,4f346498,1eb7abbc,a98de561,d94f46bc,edf3dd10,1839d91e) +,S(a26e3a0b,6f61c611,28c2afc9,15ddf48b,8d3d3901,df93a2b2,aeb82579,4822e605,41f4e806,787884ec,84716647,ed72f331,126f141,6837a232,e1612ac9,99c7d115) +,S(56623040,c198fbcb,9aa32066,90da22a5,a0496a82,5313a2fd,529fc9b,26647d22,35867158,42a4c950,e67b9033,23c55fe0,3ba3bf92,a9ecc5f8,39708869,7e2e1e98) +,S(acb401f1,b535f181,75ba9cc5,8e32514b,be8b6d19,1401248f,20500dba,23ad4f,f57fbfc7,9bb94ec0,eac7d09e,52b699e6,d8407335,e6dda706,f7d1bfe0,71e09f6a) +,S(9e02a873,fbbeddcf,b760d48e,6e29e6a0,b37cae8d,6348a8d5,bd651be0,59ecdb33,9f926735,cb2f46eb,71005600,56dea853,9fd6d140,2a57d05a,e884e8f1,1a293c39) +,S(3e88f76b,ec410c46,ca20cf7,2d90c66f,476fb966,c46f20b7,facb7f50,17f71617,bbbb4d9,a453277f,a7bb83d2,eb8b2950,3fe51b0,7d4b93fd,5e993663,9b78438b) +,S(cd39870c,a6484a9f,e6d30e47,3a6f5e8b,3ecc1702,e191b5de,3d4d6cbf,dade08e6,234240c2,8f2b2233,9369298e,f22cf32a,7663edb4,2524f02d,6879b4c9,f4ab442d) +,S(5de275c2,69799058,3348c4cf,8c039b44,daf431d2,b7b1f862,98ae24ea,271644c9,adcf9fc3,10ce2d93,8459db47,30273255,cc483342,3f6bd4dc,56593f06,6466b8a9) +,S(2d986200,bd9dbe51,eee40ec1,2730701c,621333db,9e71c232,1a6463e1,f26cb76b,57433deb,d9149b32,37315114,e9747758,228d17be,5f7c54d4,f552e730,1e094329) +,S(654868b7,39d6a72e,75223cac,d0f827d1,789c410c,fe0d5ab7,24cdbc2a,c9bcb269,3d8d4270,63cfe,6724f8d4,77ddbd10,b24899a,3ab0a33d,87683646,9f4806ba) +,S(d97cf5c8,e1a40197,75312099,fb1a2d62,2b3354d0,e524cbe7,bbd3187,506f909d,53c8784c,cfc900a2,f159e98b,76481239,3d883d70,8a953905,44691b5,1c8c97d4) +,S(697707cc,9c74c828,7396d53b,9e5d436b,24b861d0,2fcd9208,dcdcfb54,8ad2d903,bd203dfe,7f4a3484,1f5ec966,f063d251,a89b7da5,468bb2f7,32775a1f,3a62412f) +,S(36b4329f,fbbbd80b,e8eecd1,358c05f3,6616b456,588571da,c23d7752,edf15d65,7c42145,d8774827,a0cbe9e,b9ca7b90,2353c6f6,118291ad,950dfee6,124fef3f) +,S(dad2b02d,a182ef28,5b00b0e3,2af77064,ca6b3c1,41766194,7fae0663,4861e144,ece34dc3,704d0561,f6029366,aa45f192,a2b55b1a,1786e8a2,d84b2361,eb2c2af1) +,S(4cdf386a,c0e7cbc6,e19c5f85,1edb009a,59b0526,f3f4d0ca,3c82f280,dd33531,3f8b3eda,4ae484e0,95916efb,ecf1ed3a,27bcb034,6cbae87c,6a388488,331ddc86) +,S(d4b65a7a,eb74d38f,2a8e9383,2ad27a7,2e2a89f2,2ccb5c24,9e4e55d4,da7a0cf1,a080a32c,ac9e0ee2,6e6538a0,233f2374,3428b2ad,821852fa,bbad2a23,63a7d7e3) +,S(c41cd230,d99c5f66,7d7ea000,53945fed,70a2a807,4893d488,5be2ee46,cadff9bf,4a7faa2,3ea4ab42,5f21e12d,dea7530d,429c64fb,c05e1b24,adc24f55,c34af43) +,S(4c67ce59,9a841ecf,ec147a89,c120619a,227100ab,c5dd0ba8,da6d362,6f687824,aec0ecc4,b65694f,150da4f8,fd514d37,52f4c6d1,aebd8b5c,19846ea3,bf4b13b2) +,S(e765b00d,a067b5cf,a40bf02,dc8ed6bd,4e59b470,ed6eebad,c02b49eb,1d20c37,23e0df00,9ca95773,a91c0805,40b024b8,34c0d7de,4dfd6c43,76c979fd,2665f21d) +,S(18a8cbe4,d7585fe2,3b958cab,a9303d9,505928a5,7ca1058,21a9f192,ea52298c,9d00506,4a1f3406,c3864d73,2ebf3e1d,b6f20234,d0f5a99c,3623ed9d,530d71cc) +,S(2dc043ea,a53c27b6,50e2f1c,b6fb7fd8,26c4fccb,9572bbf5,103356bf,87b3d649,2695f192,af96b031,a41b72e7,a61593cc,30672c07,cedb0908,35ee207a,5cf18271) +,S(71699cbd,e1efa1f2,914bd0ac,9b28111,8053f7ec,d51e79c0,44daa4ac,ded9c9e9,8519592f,a3b05922,86797110,b222231c,fef973bc,d51ea4c4,34998999,63637c05) +,S(23f4668e,40761e37,235dc0cd,41b6c7ff,1951678f,131763b8,7d394e12,6be4c370,1cce4181,2279761d,8753dfaf,8bdd206c,e4cbfc5f,ccfa8826,9d2d4327,431fa689) +,S(92366959,9a173d3e,826233c7,8062863c,2a92b525,106c1159,4885058e,4a7797a6,d1c9f57c,d03c0042,38f898bd,cb45c064,a1f2cc56,222bbc03,a61997fc,877b23d4) +,S(a2dd03bb,a75a7bb2,fc9915df,c66c0b22,76a7347e,6cca3d32,e5727cb8,b54bfb70,9ba4266d,b9c66907,b25c41bd,b96f8a94,a9559830,f171a878,66372067,51c7126e) +,S(79174d3e,2379484a,d3191db7,26073dff,d69a31c8,94314f7b,282ed68e,71395ad7,1cada148,7ded181b,f1a9cd30,21afb04f,4402a171,3a570d7e,9f4928c1,2069315) +,S(874597e3,80449235,af5c10e3,7af7eeb8,f755ebe0,6784d5e3,ba0d1021,fcd3b12c,2ebbde83,58c4f2ed,cc89bd52,e6cd61df,a251bcf5,713858d3,d247750e,266a6129) +,S(f23c1888,c22a764,c85c6328,387e9126,f4ad772c,fa0a6034,914d5f60,7efcddc1,6cd5bdba,cb3c6cd7,4c2c5cf8,1c62e774,560211ac,f1ee8096,be9eee5e,b716881e) +,S(a4c2054c,3a834498,6dcca592,db5fbe06,7f80d32f,8c492436,79c57b76,ae13b96,7ca515b7,8a3d5590,6100ecda,f7066459,db9db34,3bec1b4c,6a57626e,1f560444) +,S(d6880dbf,50d3faa9,835404e5,d723eec8,b1bae88e,b8ca22bb,a85fda5b,6a8cb669,a08c4200,718edd4e,42b775b1,1beb9a6f,eb3bb93,b54c57af,5218eeb9,7aa9fa09) +,S(2dfcf2b0,babd3990,c305cdbf,cae7795b,a2fb3cb6,3ea0cd17,787122ab,5af86bb8,e0e921a,15882601,c151fd93,d7b7c607,cf033633,e6498004,d29cbf76,7fb19f6d) +,S(d1dc82f9,aae7e02e,177680f1,e5d73ae,8a178207,7b5ebddf,45b56bac,fa1f11b1,4c9294c,e3ad0fa0,ceefde5f,fd80905f,ce57a5c7,c6807da4,186c9b4a,7c9d6237) +,S(ff111200,4231109b,d131e7e0,f6434461,ff699d8c,6be3e6bb,7eb9e827,9af4a4b3,2c30b8db,bbe56de5,3d8b3307,9dd741ca,8c7aa596,78a3c75b,a781a77a,e470620e) +,S(9fc4e1ac,28a8f268,c66aec12,f5e795d,f3dc23d5,c515c5ec,11c394c5,e6f2a11b,188bc037,ab114fd6,508627db,12317d0d,fa194cd1,bfd56c1a,4ba25d07,206aaaa9) +,S(bdb0b373,5bcad639,386579e2,aa78fc40,ec2f4700,4b3174b0,1bc2cfdf,f4164f95,45d65b9f,eb1f60f3,29195060,78d5c016,8c6b1820,6afcab34,a2623e1,d2bc70a9) +,S(1386e11f,3950b8d9,e87589da,c3dc9a47,bf36b0c7,8378d2fb,253e5bdf,9f704465,415f50f4,f048413c,7eec4c2d,122a4f7d,f4908265,31f9dec2,313bc7b,b1b2402e) +,S(be246169,75b321a2,edd491be,90b7a11d,75a36c12,ca782d20,732a285c,5a6a1572,ca9109cd,5b041864,5026977,9490edc9,d239778c,25609736,7b52d82d,97f588cc) +,S(baa5965a,c352a017,80b1359,3945030b,32c83b60,805bff01,476ece26,cf89384,c83a9515,dee6663b,b5688d5b,483f0a2e,51c8180e,a130e0ea,48f7a4f0,1d3d06e6) +,S(17dfb33b,957bd5d3,36c918f2,cd1fdc95,23e2980f,20a7666d,bd5e8a7f,8ab1f83e,32ffdb3,db6dfa31,ca67a5b0,27a648be,b978b6cb,7ace4241,a0b6a7d8,ceedda20) +,S(ec5dce25,af07a01d,9dc43a9,e92dfb51,29146307,cef69b48,cb7b6e85,d9cbd91f,1018c449,949bb2cc,e185a9,4a9a06b6,bca4c8b0,1a3079b5,3f86d830,21d46de) +,S(a56b5b4e,d4e4e3a6,29701889,2cf21f67,868ebf37,b8696ebc,83afd980,b5c63817,1cea139e,521c70e3,f0a4b02c,c20345df,90a579b1,20ea5dce,bc1385bd,162adfd7) +,S(2ff27783,681d264b,540df574,b478e3a6,2b919e0a,c4ab1b66,3e279083,25a707b7,3a477dcf,65b233e1,34ddae74,7fa5971c,cc0498e7,be9f17cd,5e34bb0b,480d360) +,S(aea30054,dcc9f7eb,c9dbbd45,cbcea779,49780d59,11edcc19,937e6352,a726b3c0,7ba5e2d8,9f681e00,bb74a353,f37c36c0,7264acc,d6a31529,cd5275ee,94a631fc) +,S(78531772,142432df,81802ec2,84884682,5ae2bc93,aed1fa8b,e345c106,7e23d059,6b8d514c,a0a13c1c,6425e5ad,f808aeff,f88fc6be,dbdfc788,1d174868,efd21063) +,S(c104c683,8448cda2,c94b0303,7b5ddcca,d4db1d7e,e744e70f,5f9275b3,244b8405,ac9e6545,3722e2c9,d7d97aa,767c3235,2a213391,aa2e327d,3884ecd,93f4b133) +,S(d4b02b47,3f851a30,604356c0,e9310d4d,7bd0f9a1,17db0c90,54862b58,969887c5,d451896a,5eba571d,4e55d26,9c3c8082,55c5cf18,f1f2d033,b8d30788,224a68f2) +,S(93d2f6a3,e97e4aa4,10fb6006,f3853f85,65bd14d,aec87192,e61b8f97,93f29b32,a0a840bd,336034f1,a98a0239,c6ab3ecb,eadd69c2,51b2e03c,339dd4bd,7a407308) +,S(7d7d7357,467e23a1,2022788b,2ada9a69,1243fbae,486cc61b,56458e2e,781f8f3b,59d5566d,97bb583b,940de406,44e19530,b1bc7ced,2a00c50,f4bbef30,d899e7e6) +,S(ea80f7b5,8c72080f,f2d83604,ccf6e4d3,28a71914,abf94888,8dc87c42,15fd4a6b,ecc6e626,f01f73b2,5d7e92e0,d1220b82,8f2f8ef3,3cc92111,3d15bfab,179ad8ef) +,S(c74366e7,26949fa0,6913e6ab,c41cb17e,c3837659,253c3038,30d543f6,ac0e4aa6,671ed272,2626ab33,14b6b1d8,1cda24d2,9fc132f9,51f97b95,8e72bc0b,9eb42d3) +,S(6170ed4d,c3dc2218,2b45c1c6,fcee9fc1,3897ac17,fc32fb25,aaf2880f,4cbd11bd,cb40f34e,7e8faa89,71338834,21cb247a,4c938f3e,f6a97cd3,1711f803,c872621) +,S(7dacf9ad,5775fa62,f99e093,5e948526,b94ef22e,912ae95d,8dd4e5ba,744a2ebd,69931690,6c73045,d8530a39,d33a8e21,d5912969,9f7b721c,1dd0c616,725d5778) +,S(b1d113f2,ad7b487,908767a0,b6faa413,6a8b3dad,41dad9d2,b6e8e7c,9f294639,7c802cbf,4596903a,90b1de93,649bff3a,6f63ab85,a9e3529a,7d907b41,7f973b61) +,S(d09d8d8a,17a1b113,aaafc270,db36ed63,4342d90c,64b6ae97,9733391d,eb67f3a4,c48701eb,738c3edc,a4f78313,a56660f5,3da8ebaa,3cd8d469,d1dd5910,c618851f) +,S(c264ffb3,c223e60f,fa34faf9,feb18668,4b05b7e0,6770db1f,9561ed5a,ea0bfef7,e4df501e,ebb4cdce,bbe6c4cc,b966de77,18ac5479,3e403e79,389ac330,92928e6e) +,S(55505edd,605e4299,afb5a69d,81f6df34,c7c2133e,dbc7bb10,ebc49187,857d7c49,ddc19610,7c3bee6d,ad955e1f,76afb31e,93a88e4b,de8aee59,aba50864,c295a487) +,S(9d61801b,a07c358c,f9942bd,d3614d3f,74904b8f,8f8aa8e6,a957eadf,79fe99ef,7d9d5980,2fb5dacd,93ec6c6a,92ef86bb,6079076f,7d964d31,70e3ad3b,7bb854a5) +,S(f1a6cc66,3dbf46da,e1ca98af,610f09e5,e9251a44,f6b9915c,b68c58f7,9038645d,e41bc6cd,747c0390,6d29e29c,4cbc2dec,6817114e,daedf220,b7fce48e,7e6f42ee) +,S(df247558,50b523d6,de15c3ec,38537a3c,af9a90fa,2c0e8b25,35696289,10517a79,bb567ba,e42e9899,85fc478a,4303188c,2da741fe,bbd1742c,818ec1c,e64818a5) +,S(4dc30b60,b174bb78,1f339277,ce3996f7,fe102e13,d80873b1,1a9e6b4d,54247cef,b13e5069,a7344a99,4f9cd284,1d9291a6,50e1d969,dd3061dd,ef34f037,7c85e20f) +,S(e77aac3e,946f7715,b9d0999b,1f3aadfe,de9c31dc,f8eab336,c5bcaa51,f16f64a,9d78c14d,90cb37a3,835d23,fd8d5a37,2b8fa160,1274e5f0,2bae50f1,587a11bd) +,S(24f29bd3,51e62864,3a7d2fa,6c47ab78,a6d0c6f2,c73bdf00,864e8bd0,c3f038d7,4a34c7f0,22e43220,f6d30d59,3d3ccb54,dbbcc50b,4b845712,e72636da,d0cecff3) +,S(ad4b9b47,b9cac9f8,a5f3728c,ec610000,163ac2ec,6a18cc34,b630134d,9fcc9364,afc8726e,e0f988f0,53247177,1efb25ae,e07b70ba,ec7c425c,50e3eae,2898c077) +,S(1d38128d,ce7ede1b,265a4f8c,4e6b911c,d0e28087,87244a60,dd9f646e,2932e509,857a6c8,e4f43cfe,129e11bb,462101d7,9c76e99e,c7b4aed4,efc029be,b1b9c1c5) +,S(c171b7a2,7badcf84,c3c61afd,7568d23,5a14f0f4,fec777b,4a91c92c,61358677,cde264a0,48d2cd4c,d53d7371,8546802f,1ab67815,d6ebb3cf,b6415f4d,5cf4057e) +,S(d50f20d6,f4781aeb,eeca2a8e,85e8b75c,187c6d34,a940266,daa16876,96acef4d,9aa99671,35a1dae1,af29ed05,9fa15eb2,a4aaca27,99d98c64,5ae0ed03,7f08f37a) +,S(6b83cb07,7bfd33d,181fc835,63eabdd3,a50be6d4,abb64a16,e338f18c,c098a977,3e04e660,cfe61fd8,1ec67b2,d20ca9fb,f9c6e038,71f7c838,b261ade0,51b564e9) +,S(2e75abad,dc0501f3,afa90484,e85972b2,679d1d04,62d6d206,42c73830,15213b19,4754077d,a6868edb,a0943397,3c60a581,88a55ced,dee38351,93ce045a,e93517de) +,S(8a322085,d749f63a,38dcfbba,624d0c87,b9bcb66c,e4e3d84a,e97f7781,a0e9a164,3b592b8f,9d8cc10b,58fbdc7e,982e3fa6,7aa67c90,e9ca884a,e1f57291,6d7e2076) +,S(b890b7f9,ba1f1945,2a7bf1bf,944f9949,36bb4ad,5e2e0fb8,15ddc1e,2f30d72b,c1b652c4,f8e9d91c,8a92f76e,f6f72ed3,3357b35a,5ac00d63,a039df78,eb778a46) +,S(cdf33d41,c54c8a0e,dd3a0b1a,12290d13,715de82b,21af5306,a1197444,acfdd5c9,2d7b78f,31d08fc8,afdb3940,ef1afaf7,3bf37029,1e1be3c9,5acdc673,ee2150d) +,S(20b24e10,c0f85631,fb891ae5,c0b0b36e,5a3829df,a5018c1d,8b59fe87,b051b55d,b5b9204,67394039,df0a34f1,308b086a,454c7957,40d31fd8,960f6ab8,33998c41) +,S(61976c23,257b9adc,48147038,ff5ace08,39d88274,9810bc9,25590555,b0e1c709,2b31a4c2,a712edd6,6b2da3f,84447c73,f2e3f652,d22b81d,d37ac4a9,def5390f) +,S(4ff22758,a2a25681,d28fbf09,e4cea5f4,74c00681,d9b4fe6d,ef3227e3,6092b52e,4b0f517a,4e56697b,37341ab0,6fbc92e2,4ae3d03d,392c33d2,377f7432,10a89fdc) +,S(46145b19,639938dd,9c6c84eb,d4180cf9,752cf9fe,a77ca609,5ffc8f7e,ead61f35,5b7b55dd,8a5e7d00,a3b62d20,2f371d42,8eeacc9e,7556547d,f2396c85,9503f6f8) +,S(32464689,a5d7626c,fd02b5fc,da68a8f8,dec32eba,887b523c,4b4e379b,c40bee7,6e9cb8c5,3fb716c0,b583b356,7ba6890d,2cdccd39,646a49c8,132ef061,d89f6710) +,S(56438894,2c9aa575,efbaec77,cbecd9fe,79fb4461,c61ac051,195ae384,dc0d6ddb,663528a4,29696073,f9d47d2c,bb2c256,9bf6c452,c171ca43,7cd3480f,95b69a05) +,S(f04fa37b,9c510bea,ec12fd4a,307b1b0e,d934fa1c,78cbfbe3,bf904b00,91491e4e,19b7da4d,826a314f,586c4c78,457a3075,76f3ada4,61af5bb6,c374178c,23a79326) +,S(36fe4596,b1ca6409,741f7a0c,b899f89f,a727207e,eedc8e57,504847d,2cb304cd,78f11f,4473b124,5772a101,564b6468,43d4bf76,afacf02c,bc45ab39,c7221e19) +,S(ce3e6d1b,f2deae03,8ea4333b,ebfa026,d1954ffd,f50df66,e2899b05,1ee87b0d,4cd4635f,10af69c7,50e96ede,e4f38591,2a14b104,ed9023af,60ac6e93,a9b9bcb0) +,S(33ad3a7f,a6008875,74bd735c,b2ec5dff,10ffc5dc,3163d8d1,62644086,888d29,8c959ba3,2f7b8ed0,4cb2bf2f,2b5e5f56,b29a851c,8d1f6bff,b48fab31,5335b3e0) +,S(7101a2b2,36aa7903,6348006e,cb8f81f6,b481ed8d,8a3081aa,40ed475f,13fde43d,d0a93654,51471aca,80a05745,57b4a24,9d627dd5,c1428ed5,79feac02,5fd2cd2e) +,S(ba3b4042,9c0bb526,c31cf602,3f9abe17,69974344,5e66566c,851501a5,d125471f,7db3e93b,e9944c42,b1654407,6d5bc6a0,99806c67,6cc7415a,9d110661,f4c644e6) +,S(57c6a582,1da61528,dc291aec,3a9f1a86,229594a2,27767583,5687ee91,15acd72,a9e4750b,5580b225,7768ce7a,b2909466,589a1a12,2e54a7af,35f44de,44cd5dce) +,S(c8353ee5,847efb7a,ec03d949,633132b3,9ad898e8,9a367c77,7b4a2ece,c47a7c10,dfce02ff,71060be,5e12d377,524c798e,97bafd6,e9aa9e10,27c07a8d,6f7d3a55) +,S(b0f6ef67,78b4ee99,6d7807ca,9f1f6f44,203eed8e,62584c6d,666e3698,e41c0eb1,b92763af,c9ea095,f757e921,ac0bdb02,605eb66e,d3e1735f,f8f17e63,dee5460e) +,S(f2e4015e,62645bd4,6f858b25,cb636f97,6ccfe5f8,da065b65,1ec170c,ecf5c411,8c05cf9b,19459597,7c1d1b6,4ff6e902,5d78a175,6416e0a,ebf33c21,7b3d5dda) +,S(ffcde722,ad4a2223,24396b52,5a95a233,31dab41d,95a4e19c,1a23c2e3,db7460f9,f7282903,525ac2ed,3f9db21a,92d3fe4e,b1635a8c,77dcbf8b,e671146c,8eca8115) +,S(81dc05b5,2d9a210c,4904bbde,cfb4eacd,8b3f08f1,d667e9f0,a27345a9,7d37b37a,747aad1d,bd0003a6,d89f9d9c,fc00977,719d24a5,b8ebb807,98d1b644,87105f5e) +,S(5b92318d,a267772d,94656087,8d698aaa,7d2c721b,28dfbc20,9e4a3e8d,1b0eef1c,b2d274ec,7aade417,4f2f8766,1b5018bb,47c83d32,b2fed50c,437348a7,93041906) +,S(3eabd0fd,6b5bc51b,c187c943,511c3005,8f86e474,8547fcd6,49595070,26fb805c,95e8bbe,8187398f,fa4ea4cb,b50bae2,19f8cc5a,5f09503b,3edd115f,729b2101) +,S(a64d3807,f92b91e0,717c310,ec799908,43e3a394,a5ece3c8,7c3bb209,8d3123ae,50fbacf8,be11f6b0,ea0e36b3,5c46bdb0,8fb79064,59b52901,9af59b1c,e80ad239) +,S(db9ce153,7cd47f3f,4b08858a,d429c975,9ce6738c,2e6d56f0,fca12e70,777b83a1,a91b2016,9fb1e2f,fd52192d,7030d86f,66358516,5ba32829,6c9aac95,192afbf6) +,S(bea03f49,76ee0a86,e00bfddc,79e52173,1baef841,d751ee47,9a46cfe7,89de2399,feec1fb0,3901a923,862054e6,7024ba7f,4c485c70,2ff22aa4,1620e857,6dcc863) +,S(cda308eb,6975d59,db439e23,32505d29,4ebdca67,5373ed79,25b52a6e,f60a33c9,7d2053c5,414c9bcc,5155f17a,6fc206d8,81cc882,54341778,1c5db51f,3ce4c224) +,S(430bfb3a,802988b3,a68d5595,c989cbe,75d409d2,68dd84b9,a1d4a5b9,42360174,3de4c6e1,3c87338c,8bef6195,6559335e,bec503fa,2025529b,b015cac6,6d8060fc) +,S(b4b1b7c9,932be243,6b06b2c1,e5de312e,4a409498,11a226d8,5bb30c5e,1487c36d,e169a70c,b95988dd,fe1a98df,527fc172,61cc3103,88d41ed3,4bff23bb,31da4dc0) +,S(7a954055,d2d6acda,8dbe81e7,46310113,e26af09b,91cd59dc,92a479c6,d6079ef4,83c6d3ff,4582eea7,becaf8bd,422c0558,a2bc8d6f,cb615c59,7c46982,47cabcb3) +,S(aedd36fa,91a7f95f,2b2f32c9,1be77860,75783bcf,8fcb20b5,f20ca664,dd89474b,c747f32a,174ec6a7,936f13a4,7e80a2e9,324b7f5,e163b396,218c4c35,9391e565) +,S(41052d48,54402d09,a813492a,2f9362ee,9799ffa,3d270200,45a0ae07,9f913f60,97cbdcf9,b60518c9,74ddd987,386d60d1,3d10defc,8fe64511,843d5bf7,13774178) +,S(886e5afb,69f6debc,c1c5be6f,b636eea8,7314ff1f,975cd96,9070e376,c1a9973c,6a8ec4b7,98fec5e,b4a8d645,c7b72663,d6bf4aff,8f4f4b36,c064bf3b,f6e7b5ef) +,S(c93adec2,ddf85ba8,d9147c91,82ee4fbb,5727081e,a6938c0,a4bcfbfe,6856dee8,9ebef5d,2b2253e9,4474331d,b52739c1,71214093,aeab11e4,e51a2be4,e201dfb1) +,S(d97166fa,94a4e51b,ad21e1d9,76e01011,655ce24f,5f5afdb8,feda67bf,8ae65a83,815ac894,57f83bf9,5579fed2,ea470ad2,ac1c83f8,546ef3f8,bc383701,1bc62a48) +,S(c23cf94f,c5a93aa1,719ea8d4,4f426fd3,d48220cc,10ea558e,e4680c1a,dc91b18a,64a4dd89,8f36efa6,efce9354,fa30f506,be3766f3,a31839e,1fba56b,5a07ebd3) +,S(7236b1df,88751e98,2689e049,c5084b71,b8d7979c,2c412a3c,995e61c7,2440929a,67155955,bf5d1916,9e36636e,ba56fe44,f7cef6d1,b0afd3c2,beb59b26,5e3b67a6) +,S(d1890a85,df4f7933,ff72fad6,5b95f5c,c9fd8683,3c1bd2fd,56a3b7c3,e90428d,8b4896ad,4b4469d8,af074eba,ac38563a,ca68888c,5411ce0e,b3701ced,75d4f87e) +,S(41ae85b1,9fcaf4a6,13901d2,3cd2376,62f299c9,7cb306ee,8ffc13fc,75d54e15,20d99cc3,215f0b1c,20922ffc,6e111b53,e88ffb39,bb6e118c,61b9e3ee,fa7d01f1) +,S(d125489c,4e07c44f,35d39008,e4e29c43,c28ca505,3e370570,a7bd34c7,57e673f5,d142e19e,825ff93e,b6661160,b60d4e06,b4393388,43500b29,2d4d62a9,28db2bd2) +,S(8c50b3d3,667b2aa1,a60c425d,1128d389,f2786b3d,656ee126,dd57af96,ebaad9e4,55eb7ca1,c2bb5881,7f82cfc,c8b5ca35,5c2aec5c,68dd479d,948261ee,d342fce1) +,S(3e77a3c8,a2e995b7,582f502f,9bb1230b,35311af4,dc8d0744,9211a13b,444fd42,99c427f5,8365ca18,75272508,7dc57985,d59d72ed,aedfd5f9,d61517c6,1fb8679d) +,S(73ffde64,80547448,658ae5a8,529755d7,2ad7ec84,5711f0b3,20acc53e,a15ab609,786de118,c6a0daee,48f3f585,d10b9f2c,b4947661,b844ed51,59903ce5,f2cc8d18) +,S(8f2ec78b,47c61a39,c2ed1110,902b3a43,4fc9ddfc,b11b1ed,75c675be,5b8a172c,441686b5,c1327f0f,d2bee35c,5d8f2aef,b6da72ac,e6206982,6d6812a0,5b937346) +,S(c36c0376,d360f87d,28069511,7eddf1fc,75ae7118,5fd5a82b,b47d8fcf,519ae4d4,44c31c56,cda1c2d4,41161ab7,45f3c0,25c4ada3,72396658,cca23f93,bdb6a303) +,S(1ff06490,3cc7e686,d408909,4812a7d8,531c4188,ab8aaf4f,f10bcc5a,9cac352c,87af78d5,3122ad89,9a6a1297,fcc87c1f,252bbf9c,d8cfbf57,f64cb6ad,fe77f3a8) +,S(1265a4c0,6e1f1a6,126a9e37,a6e4f90b,290fe449,49b82bca,8ea73759,451e0b25,4f0f7a6c,9bc7f00c,42b90d95,95871176,d723941e,38335581,8f7aacdd,f830d15d) +,S(2595358e,4d45d362,5fa1c89d,a3249cc5,a26ec3d1,e554a863,51b79fc3,4eeea90c,d9b8d147,e4e00450,1baeeb5a,8440235a,88913063,bb1ffa78,9402c3f7,2c603899) +,S(3f432621,7a5f415c,b028f338,40fc226e,5906a09b,d054eb55,45609fee,90135082,a572303a,131fb0f9,c63d88e6,3520ef2e,77bcfea5,1031fa60,fada909,a610c830) +,S(9ee85c9b,4cb0102a,6b21c517,947d798c,c26dd853,7bb5d6c0,46a50144,f3297f45,85b798ab,c2e4fcdd,bf3e17b0,49e2a495,6239ba16,9917ed08,ebe109e0,4ac8016d) +,S(479c2ec0,28e8c83f,d7946f43,8d802ed3,d728179,55d94c55,bdef5050,67774934,c262229e,23eacd99,f9d3fb37,b7b9040,47cdd5a6,6872f4ac,800dbb3,d87fc3fd) +,S(2a52f4d,821cf4ae,758ed477,6cdc3bc9,2d3924fd,97a12f1f,4dd5a646,24766b4b,28500754,6ca8f142,6f3a4a6f,6ccf8437,37c6a917,7ab59b92,728f588,f11509aa) +,S(d89159a6,d5447ab1,bd03d44a,a8db3410,c86305b4,4f06b25,b88c4244,5ab1e929,c539ee3a,e7dec645,810c4a02,e1777977,d98ddba2,281d5701,2ed2a4c5,85057013) +,S(c91267af,4c303e07,ad82c1e6,2ddd45d7,cfae00a8,d27a34f1,eea8d15c,1add920,975967db,347415ae,24b427a1,b8f7229f,8db0758a,7ea13f7c,582c260,c0711b85) +,S(c860be31,18ce92b0,39b27a50,960d3caf,88e24bae,6e45fd8c,5a253a78,e3d0e6cb,15b8374,922f44f3,adaa213d,5e3facdc,9de1ff6,4da677d3,d89f6e0a,a800413f) +,S(eb8b5f4,2fa90313,b3dd7d4b,d47338e2,9861e52c,b492e9dd,6a1897ec,4da14c14,9d1c4d0d,56c570b,8835e188,517fe7db,490aa6b2,bfbe1564,579e7af9,c0647bf) +,S(151e39fd,d5961da7,44ac5871,94081a88,666d3dda,6507404b,607d449f,1b3c005f,476bca6d,6bfc5e2b,2c183794,563d35ac,aa8d38bd,ec3846b0,22831bda,38fa28fe) +,S(b46d2d52,b764f922,7ead0399,c391a9db,1737c9ab,35394951,5a57c76c,c3c05c59,8aeb78db,db1e52bb,a8030a3f,b9f57f6,644332be,6d0ad41c,34b7fbd3,13c6eccf) +,S(a89e77d,30c66dba,a27a2dfd,5af0684f,f6ccc84e,49b8d98,f402a88b,20e5bf7,af45b2da,cffcc49d,4f6db574,29f13971,c74f5321,4dd4af49,2bc7ae5f,10abe1ec) +,S(8d774c5b,eaa0cc57,7479873f,9bf5ff6c,808afa4c,6fa6f2be,88b53841,4cd088c5,6b9461a6,600ae1ac,4e617149,73fa8b48,74dd7cc,9f331eb6,e15bfcc3,2e26eb63) +,S(34ecc029,105f5e19,d6c2471e,eae5b671,cd55f2c1,bfba7dc8,46787c92,ba7de2a3,13e3cd9a,db98a6b2,433dba08,ec7d40c9,c2467878,f6f4bcd0,ce60397,67aca327) +,S(41b10298,6889f83e,e89d4f8,fa50898b,c9bbf650,2d153c36,85064c9b,f3dc3c43,559f5bf4,fb827bc,dd068d3,cdb14d6c,ace08a47,c30e3fc7,8972ceb8,9f88c21f) +,S(4a77505,79ae33e4,55228533,b8856da2,b14f70a8,d13ac2f4,f6bdc5eb,4f5c9aba,d365922e,3a487635,3ee1b02b,511c7926,e5c5edfd,aac153e2,686ce4df,b623ed2f) +,S(173af9bc,efacadf6,c107748f,278adc1b,d42174e6,8fe06d9d,f84fe278,70eda280,7b120d62,2463b94a,57303cfb,2c477f95,49d41a66,d6ee7c2a,4c652284,b8535faa) +,S(f0fa5cfd,ba53a5ba,df707983,134c3459,5ba232b5,7fb67a94,5c5c0f25,a0539be9,ccbc91a9,c0a2efd2,3bffec87,cadd16dd,f4292aef,a59b3d31,6e9c7082,742edb6d) +,S(a5df803f,751c4846,10799c60,db405439,9b1d5bfc,c99fb316,fe884468,e57ab77b,27f8f706,a2f08ff3,249f8b62,ac0d59b6,31fe52fa,7ab08ad5,2d09ff2c,4138dcd4) +,S(d165fa7e,d61c251e,a93e8f16,c9291b09,1f258720,fd303a3d,c573131b,d98628cb,cc0dee9,f01f0dce,aba94461,1e48f9c5,282a7480,eded0794,81d0b8c,a9819a73) +,S(87703371,46a8694e,2bb5cb10,76ffcd94,309c9c8c,be6e8cf6,5a77f4ca,4d31efb1,ee64af6a,5569a205,2cd98c10,871b2ba0,6addae3b,3cec463f,6002d86f,b16e5e9) +,S(1c7e4ec7,c090906,3d98f786,dc22db53,da4d258d,6e75cbae,30d4b99e,f4ca47d4,d2001df7,3297e6ec,c0aec17,e72cfa6f,5951e2bc,f03e4aa5,5f329c77,47cd92d8) +,S(d485ec0a,1a0abc6a,69f308d4,c7f8b8bd,fff5ac6f,ad4ae4ee,271a87ce,973b58fb,68f51764,ee705265,3e2f7d7b,7415847c,3856e3e,1a7f4932,6fbfc9c6,5976cd48) +,S(ae5ab510,48215ea2,3be55d93,4e0dce0b,ff88f09d,10c8953e,390b79f2,da26e43f,9f80d71,36a5da61,7f90d0c8,a6d9aecc,5cf88abf,6bbc2181,5f8320a7,2fa8929f) +,S(cb879f42,4613487c,24aa73a7,36f211ea,96541877,33fd7091,40a824ae,7641a708,d46a4ad5,7f607301,f61648b6,e99891d9,ccc41af1,af29a66b,b894575a,a6b3d5a2) +,S(1e8d6ac8,ec0b543d,1e0aea66,7f35b556,87e30be0,72841fc9,3f599c81,4a750d7e,8965853e,6519ae5d,c523cb52,720b004f,ad2f03e7,aa3e0413,bc68efcc,c0617f85) +,S(dd8e5b0f,be63df6f,5c20e447,778f923,2e1b2c22,4d28cfda,b2c672c6,66d05f8c,d8b75f77,f955219b,d4d95e65,35d5cb0d,23c2a1d1,671b9416,2da9c4c9,b2d4a94a) +,S(78ac44e9,938bf51a,691132e,93ec3a42,22be3185,35209054,a76b87c6,645e0252,c0f66cf0,3c20dd,d136d1f1,cc00eb17,efdf2a4e,72d373a3,99c20da8,8342299d) +,S(d53110b9,5034e916,d904cd15,1170761d,4f3492ad,9e257265,3ef37739,df9bb035,c091177d,796d199e,5c05abeb,29d0df1e,e5ceb93,d272e6d,d254b6d9,a6dd35d8) +,S(868b2732,3debb9aa,60942498,3be71d0b,451ea44b,64c22225,2af3d63c,3eb512b,8951dd34,39774c8d,89cf4b4c,485889e3,df2503a,102ae568,c63fa74d,23ee659f) +,S(672de7f2,34e96153,d83da7fe,3e199099,52ee988d,961620ac,b8c6b4c3,520fc50d,ab02a8b5,2a2d9306,8a83a0dd,23ff882f,b1052f22,72a0ad11,cea13c63,4efa33a2) +,S(4887f473,af8189d9,c46ae22f,c86ba6e0,f03c81a1,490de032,72b07937,b63a7527,a2e7a713,e1e6691a,ce09dc0f,a86c92b0,a0097899,7fbf26d3,98581731,a577a21d) +,S(f97ddd77,38bb419d,ec051fcf,a93753ff,e3dfa122,6cf02e1f,23afb376,7a121440,2fa9c241,f747fcda,dd09139,437eafc8,94e7a47c,ec8a7ffa,1e054ce9,21d73243) +,S(ebe75f9b,2c92af9f,11eefb04,3456f6ef,f616d1ec,b616a9e9,9e7f8960,4f1d62fe,40b5cf37,b06cfb0f,294da3c0,f756c867,af06449f,e2a916c8,a327117e,9a47f74b) +,S(10d376b1,1f0138b3,238deefa,b57dc84,7ef66849,14e9f120,1a9619c6,b1715a13,a216f393,1f398b67,d68a2888,1d5a31f6,e5e0da82,e2b5060,4c6f6322,9cd21748) +,S(69e26679,f103a1f9,8a9a4d88,c973874e,db7c82ca,99e48554,37284445,b55517b,cab62aa5,13b41a9c,56ea455b,6a880b95,9d400bba,74e7a203,7b60ccb0,4a89b8c8) +,S(3acf827d,81e39933,64e99fa5,63f89305,dcc60b62,44ecc0a6,b101c6f6,ecd5bdab,77e3bd28,40a99c45,30ab7767,4d8ed17f,f57a4f1a,4ab91732,7059114c,c06fadbf) +,S(2e5125bf,9e263d80,7a220063,434f928,d9a4907f,68601eb9,829041e1,a7b89bc4,9810022a,d7d3c333,d7c022d9,d0779b9b,5807226c,9fd41e65,c3fc8e1a,a7a0b51f) +,S(7b68da4a,197f6bdf,56948023,d2baf10c,9449d6b0,b18addc,49385125,e62506bf,85a92b7a,190aec87,bdcb9df8,42e93917,9254ae6f,b45e6764,32e410ef,4b28434b) +,S(60d4b53,9a29562e,8fab8ad7,3b699550,3fe7d6c1,e2ba1465,f05c92b1,61dc829f,2c2ad439,8fc7c836,5b2efcf2,1e4d0156,1a753f5e,d945ef2,bb1a3394,203d07eb) +,S(5a3a1bd,30250a40,d22aed42,71892b88,3c03ea0a,baba458,6b65cc50,4c79b107,9bf3bf42,df1637cb,80c1fef2,cb9bfaef,dcbff761,89cf9f5e,d00f0f4a,e551e31b) +,S(9b9d93b5,6b70fc67,5476c6a,6913e173,11ed1ade,6ea7b810,b9c90977,a8f3707,ae01a481,7a9ce6ef,5584a48b,fb96a9c5,21d5bb7e,2bf73cb4,b71f4dfe,aa7da298) +,S(44bdd82,86a3a4d7,6ecad7f1,ef66022c,6a49b916,3d7a8ccd,e54bb017,814d2f70,26d345a0,52034ce8,b4382905,f60f8885,e20afd4c,54df6d6c,ccd1c2e2,5d06adaa) +,S(8de8205f,e44946c8,582dcbe7,152dfe3a,7eb85e7,bc070282,3c972727,54463869,1dd97d78,e12a92f0,cc2145ee,db4ef561,af21d3db,f3cb123,ce0bb582,6791a30b) +,S(c36a713e,3d88ae25,f79b4add,90c6a724,77be7d9e,a62ca0ab,2d5a800d,41f9321f,2df57da8,6cf17f7d,63ebb85e,e7570869,cf90b462,e76af48b,641c07c9,45a638f6) +,S(1ce37a88,f25b8b81,941db8a6,13a7d952,c2cf868f,e979bc0d,5f35410d,147207a8,7fab8cb5,f6611850,e541dce2,315a7833,3999041,5c18ab0a,718dfe32,e0ebe992) +,S(e489744b,dbb11e61,a7e827ca,ad18ba55,41f4c02a,b50b75a0,353f4a2d,f504d575,6abad3b5,3ad97e6d,a301ff7c,7931f37e,55246ab4,c7560,363ff214,2cb9a398) +,S(cb4ec0aa,c1cddaf1,c7a67507,5cad6762,cf8ffce7,6a06f76a,38bb88c4,5181ca3d,7f15e726,5092287c,d2ccf3c5,dfcda0c1,8c63f2ad,21c06a2b,469bba5a,d9e4ff36) +,S(aee1c436,6b923847,40127dc4,150c6dcc,ea62864f,394415ed,9a39d539,adda44e6,2496d2da,97990e6f,5fb1526d,b93e92e7,8a011c33,61218d,c3d3c56d,952f8666) +,S(a099ce61,d950fa1f,19abc21a,79c74021,472c46ab,d8f67798,34df0429,ba785491,c4dc483d,6d61ab1d,7578ee0d,7548e0b4,295891a1,39774c22,7d073232,f2f3ecfb) +,S(6c57db5f,b194dde1,b8cc9686,fdb65fd4,eaf8aba1,84434344,7a770ddc,9055e429,2dfb48f1,58663b3,6a7747e8,4b5a9001,163ea20e,f07649f7,1d7a21b9,b11995e2) +,S(f6efa15f,20b2e91b,cd7323a6,51e5d7b7,a8b323df,924c616f,21a7d6e0,e34d3c76,ed48a439,2bb4d596,58e75757,95a0a4d2,7443415d,7662a535,a6246d64,3804fc1e) +,S(e7983476,a900be4,e9dfed94,42f575e6,4d9af362,1e874a99,576e06e5,d31485f8,9d21080a,8b0534f,6b2dfff8,9eb8172b,cb5388ff,d910fdfa,405ead29,9df779be) +,S(88ffff0d,7b415f76,8c515d80,9c607949,b469b07f,3978caff,1ba64e,ce198ba1,6816bd9,ab0e773c,38b73787,2881e96b,8eaf381,b155b205,79b24a39,eb9e0fad) +,S(e5fd2c3c,a1c85b03,3d93a3c1,261a5c1e,c4adb72a,7700405e,7c0bb108,d8fec1fe,97bdee17,50801ab9,a96e3fd4,ec4860,e59034e5,c02f9168,79b1e654,5a65fd39) +,S(fb010f18,146336ff,30dee37,b25aa384,9588b83a,bf943125,613d2c72,c2f5b3c4,4b0ff7e,7a6f15c4,d377e92e,73c8935b,d2d9f7b3,5be91cec,da891f61,c7631bbb) +,S(9be574b2,c28b1460,a1d67d94,43e4bb85,c46a6bcd,a957499a,179cc8f9,7ed33c2a,16aada1a,367def6e,d4f34409,e0ff5d41,f3854494,6a764040,e1109574,109bb310) +,S(358e97b7,6799ae09,b80b6797,90e34630,3d61fd4e,71733d6b,3c90fdee,2ad44bf1,3eeb5209,175677af,c16a3869,5ff7d3a5,e9704201,b802ec33,50c6c2ba,c0ff4144) +,S(2ed930c1,c941b209,b6e7cf3e,83971e70,9e36bed7,a4d3884a,faaf013d,5b589b59,ff6a5a5,e601377b,9dd974bc,4fe71e36,403f2cc,90b16834,b1beb6a0,98553d2e) +,S(d86110c0,6e8b20a2,e6da1930,fbda70a0,d8c022e3,e68255f7,262168f3,2bd58986,34dd600f,9b7157cb,2ec545e5,c50b3f95,2f422b21,4afe255,b7337815,1f3dc048) +,S(4e7ffdde,305ea002,f7dfadbe,e2da926e,6d2c2714,596fb052,7eddfe92,81d4fefd,fb4dc1e9,476cb2d1,3bef4548,9ab7ca86,6ccbdd71,4cfc937d,bf024f12,ff04eaef) +,S(6ba04fe,47c4c99f,849fbd68,7e1ca6f6,3e00179,3ba3a08f,86acf668,df0afdfd,dad78b7a,9d34c738,31112801,91476b92,b8eff31f,fcaa6d,a0704244,5fe32e37) +,S(bda2c2c4,86029d0d,b804f1ea,e9215150,34276a88,179154dd,d5d9f176,cd7f50ba,a70291fa,47b98bf1,fecd9aa5,b53ca619,41d62c9e,c8b02ea7,91c79482,afc8f1c3) +,S(99801a16,4da92a6b,de6ec7a2,69c2798c,b421af01,5ec24c8f,1b090169,18980a5,3af209c7,de27a683,56f242e3,8805a368,66cf7780,60c2806d,76f9eb06,35c18358) +,S(67fcf955,dba84390,638517f9,5792d09b,64c056aa,ba98acf9,d63c2bbd,df5f021f,96f96080,b5758233,ee15e8c4,7ffdc3ae,bbc752a9,d7697e1c,3ec3d479,4f912d2b) +,S(c15990a0,50c2946a,e4518cf6,84ac9cae,1022a29a,bd88f8c2,c8e724c2,89b954c6,611bc99c,e4ad5b6e,3ac932cd,e2e70e0b,a3690ec,758bdead,5edb3b0f,b5b95d56) +,S(c5261ad4,f6d03a0b,c15ace20,aaf95bf,94d9ab5a,95a45d90,6ce7a622,3b09d59c,5fd1b7a3,b22732c7,4b5dc1ca,fac99882,467bd80,53f2f606,ca5af8fe,6d741448) +,S(b0bec3f4,eae81fd7,2b70027,72a49acf,46a5c50f,48358df5,e174eea3,c0da88a4,13a4cf63,fca873d6,cf1b8c35,ce25f439,4d74a5d2,f2fa2799,d4c3bb98,8b50ab67) +,S(e3d32fa8,9218a38a,c503780b,dfe23dcc,e6a7138,d84668b3,f71b3fdf,891b6256,246ebe20,7ab7c536,8f7c9795,9e7aa1fe,69909d50,f4b24263,c32b8bea,7674b85b) +,S(ebf3bd9c,1920f625,4c32467b,7c1c138d,3ac6a4c2,b185557f,6eeffd7b,2aae0d63,58ed82e1,25fa2241,2bc4397e,73009692,af1d5feb,f640a422,f52e5cb6,42d4bccf) +,S(d062e72b,3dc76d03,5827786d,a9cbcb03,b710ea61,2eac883d,b7f50df3,fc6f7175,cc56b076,dc3cf1b9,e544858c,1a31723,33ba9848,a8c463f2,abf2c6d0,dae14a6a) +,S(97444eb2,32b09240,fc596530,374da035,f5ebd37e,6c74b51c,8158eab7,8ae30f89,fa06190f,d2072bad,b9144f72,db18819,ba76399e,d171d4bb,1293c20d,63d70391) +,S(3e1ffac8,15f68300,4714f302,1ed39460,d5e4d63f,6fdc445f,12d8470f,b273c242,86f0839f,8610c4cc,6ae85d54,b951441e,efb700e9,9ce54aa9,fb6c7988,a8494c25) +,S(deef1e7c,12e498e8,6542acf2,3782ece5,2a02b13e,3b4e71cf,e39e79ba,541d2679,e039a94,f4b169fe,a13ed182,f9e541e5,2b117536,d90024e6,9f3bcc7c,988714cb) +,S(69ed1a62,b3de5c,c5785b27,c7926e52,4ca1527,9f8a00eb,b5e1c247,a6c8265,9c5fce7a,84b79a67,9f3030cf,a179a682,cc22c464,11ffc8b1,ceb885ab,4753de36) +,S(cdb36419,5c3be45a,bc9f58e,e5a499a6,ef58ff2d,296ff989,9fa987c6,b753f82c,34a1a6fb,7a6b6c95,2a604340,d2c30c15,69c46123,e5cc5318,833f3a3f,bd622e47) +,S(c5749ad9,f4583689,f2ed313f,dc35fe8,acb6a85e,dd0498c5,ddcd4842,fa21c555,f22ea47,ba61044b,54ef967e,c8c82167,3d14d710,334784f6,a7b46d48,19f99cf6) +,S(fc86862f,b2fac126,e4341121,c82a9e14,d7a48b9c,988b8458,300dc3a,ba10dffb,42339dc,e304641,f814327b,ca5a611f,3638d610,85826d70,3d94c76b,8297f6f9) +,S(66dd174e,446ae0c6,3ded267a,faa20ba5,62131459,5576d10a,1e44ef18,69e36ca9,d93a449f,6d3fef4e,435f6fa6,3191b625,b883c303,1dd8b380,777b556e,acee9df0) +,S(736a80e9,15b39c21,5e5efbe8,ed6f8fff,7aa826f8,5c940110,3c7ce372,86d952c6,4a9e17d8,9066ecb2,95447058,4dc58c36,c84b3d9a,30ea45cc,e62454fc,ae13e0ba) +,S(7b747f80,f4a324af,e6042edb,d8682d3,1b8fe12e,19936844,b44c3e73,f6b7075e,c2684490,56e7633d,2add97c9,75079594,b9c0d038,cb864331,aebddd3e,18830199) +,S(dfd54b47,e4d2abe1,c40e0a1c,2916469a,7e8a61b3,bd95a1ea,8b5fa182,903d8339,c705b4a6,761d2ca,49f87fa9,ebb4b39,35c3c633,4f6b7643,417c8a29,95515fa9) +,S(992a5fca,521a86e3,dce0ccc5,fd1b2ca3,656f44c1,f4967931,8ccb7a28,7879213d,ca2836c0,496c75b6,4c56c13,641d0402,b64cb2c7,617c9fbb,d820245a,9321217) +,S(38e59093,acc26364,de9cc5f4,de398799,54774d68,ed4b0283,2bde2a26,cfcd23bd,953c21c3,14ad2e0d,511d3f05,678358f9,eec2fa48,d6b24c9,6ed4efd5,da3b3719) +,S(889ea31a,5969b77a,c41c0a39,cc5d006f,6ff14f22,a62a5f26,d736ebcc,f5df8239,4c2fa5d9,c2f0316f,dcd6462b,e7571f4,92aaee96,c3c888e5,21cfabcf,78d2c3f4) +,S(f70e24a1,6807d5a1,8e15fdd,f31301e8,bd210ed,5d3d614d,82eec8b0,326d7b6e,dcf95677,b29a5ae6,15e2e7b6,c370cf69,ccec71e8,9569f0b0,5715963e,da6a34a1) +,S(d6eb7425,9c17d292,e1b1a4ea,624e6876,a78aa774,2b14e94,d3fd1b43,562e35e3,c05013d1,4056ea0f,e5f1fca4,b0c74b5f,776a8564,ac973973,c929707b,b3f078bb) +,S(df7cc5f2,69faf306,6050409a,6a03cdeb,8f33d1d7,4e30658e,e871de09,59701b16,3d8a28f5,f604c765,5f28dcf,b2f3407e,6a63db14,6b879fb5,bc785e65,59319147) +,S(e6bfa4b5,85f6df59,7840119b,6a31d0e,4ad800ea,239bbbbf,a8a1a36c,f8c6b5ba,312b5d55,35402ae1,37f9a02c,aba859a0,22a8ba00,88a2305e,4afe8283,98844dba) +,S(3c762a8b,acb68a13,2e3e022b,35634b3c,1907cdbb,261ff639,9520352d,fd94b3af,2ad358b9,65555cf1,e0027e3a,b93d46c5,b104324e,2c62f9b2,5f70930e,bf49f385) +,S(b4d94e26,9955b213,8025e5a5,bc5ea171,c2ea6d04,9ab60184,88f47f7b,f6a6d3f5,197b20a8,81e9280d,96b5b4d6,ceb3fa20,38a28654,63e76ff,d0b5b401,8b59bd1c) +,S(406e616d,907a0f72,12acdf3f,d5341a79,9b482697,88976615,388064c9,74cd8ed1,72730d26,b2698e29,7f62fd98,2a3955c,da516ebd,642d6619,2d33a915,ba39d0ac) +,S(1762d974,37705ae8,37603446,c8c11568,c7878053,f25baf9e,e3bd23f9,5babdaf1,1db8fe6c,77f41656,1fa4df40,23662039,66a8f891,e5467b55,3a2593a5,1ba796ba) +,S(f5fbeece,aa5386c4,d75c92a9,88ce6bf0,f957a0ed,3f0caff9,42f1f29c,7ad765ee,fa45a4e5,85ccc691,88426bd2,8ae561d,a2f59d10,e4bd63b0,4f8a53ef,1cc3ab28) +,S(7e0d92d0,d12bb892,b87b8bc0,993b4422,cd1e9d95,144602c3,a2aa3c06,bc945ecd,601b1019,746bbd4,ba9bf00b,4ffaccd0,b1bf885f,57ec49d2,9fc91744,8aa70761) +,S(64cfeaa1,4d4e0cda,db6b6a2d,9e2c2820,d2fa7817,43d7b2c2,8211b1fa,e17ee0db,f0b199a6,1a1856b3,360f519a,a6056900,7a0ee623,69e0aece,ea64f0d0,8cf568f8) +,S(c90f70e6,99ba1a75,86073eb4,5540b3e0,80bc78a4,e811069e,f861b048,5f11c88b,7a8257ca,cdc7538a,13b55285,939c614c,14d6a94f,70509900,44b286d6,7eb84e14) +,S(3edbcf0e,b4e5e640,f40afec9,ad370813,426b5488,3090f8d4,b72b1680,6694f42a,ede4512c,c4baf93d,f05c14c9,cb17b1c1,fec46052,c7087306,2214a05d,60743c80) +,S(77adf15b,ae449ae2,f63d7c44,3c56cf9d,64d43840,5154473a,5948cc79,160b64d0,a1bf9aaf,7804cae2,c377f98e,86b75e90,6886d293,21aed43c,77e330dc,df9113d0) +,S(350632ea,23e39962,3daa53d6,5a916f31,b2ca02bb,5bca1f58,35b85165,b430398c,41931339,48282550,bfa1cef9,94c28873,ee0165e2,fa970d04,e51f8ee2,b87916be) +,S(3d6aae93,5ee4ab6d,46de768a,d6938703,262e9fdb,13bade31,d38fbd0,9fade000,62674fcc,6eaddef7,b971e964,5a688e32,14186605,615982ed,a09de4ba,13eaab23) +,S(856e564,a155a29d,c1b739e6,309fbbe8,5ea4debf,f12a9249,347a5435,af674993,a0e6deeb,3777ceb4,6017ee63,caeda231,e616fda3,c105f0cd,6174a3b3,dea84256) +,S(95f8b405,c1ca86fe,d56727ea,eecbcce7,32d2c521,415eff95,b3cb42dc,5230cc7f,c8c66b1f,2637df4e,2a5375e,3a2e5e4a,a89404c,810322ce,c4b2bd1c,91163ccf) +,S(b51de213,54aa18ed,6ea589fa,e3b92d90,732dad07,6c80b842,579d53bd,2089e0e0,8f069737,1baca2db,208f7db4,c43a85fe,d816eca4,e35fe95b,780e2c4,a3f1b948) +,S(e36ac0b2,c4b73783,7a96b372,b8d0779f,41605e99,2a0013a5,3c8eb4eb,d5e2a988,d41ea6e,ef06bd62,7c2acd2f,645acd8a,75e353c2,448aba53,452787b6,670eedd7) +,S(4d08e46b,a3e812b0,67071dc8,b76cbbb2,4effce48,cf4a4c5b,ff4e15af,71c51635,fcbf2dd0,ba055845,b70cc545,5a8658f,39ef2176,9802fbda,2d3ab98,ba914c3d) +,S(31421774,5b1a6b68,918e8afa,4e8dda05,6094a6cb,6024c3e0,c10fdd99,245d7501,7ea6ac19,1c0db139,eafdc961,e8f91f1d,79269f69,cce275f7,abe9598c,2916fbd5) +,S(79bb2dbd,f9afbc9c,f605ad45,4768cad3,14d48241,724b301,7b5abd51,6f8dd4f9,d1c9dce0,30fe1a72,5d100c07,d842a78d,beb689a,606a933,ca865051,49007b8c) +,S(112abf7c,1d5d66d0,cbcbf42,1874bcb8,88d3b3b7,5c2cf36b,1399fc04,17525731,b3d3ee4a,f44b5372,8d0957c5,e388094c,34fe0f5d,c51abfc5,76c1e18a,c920dabb) +,S(f6257a3a,60365e63,471fe77f,893a8ae3,174d30a1,9aa495cf,251812d4,c5040400,293ef8f3,91804cdb,34845404,996a24a9,5554fd24,185ec85c,3a27ef8e,c89938cf) +,S(ee7c8ee8,bec75bae,db32c6f4,3b3b7a04,2c10e903,de9e7d21,5ca08135,d59a682e,e68a0137,398fb186,d5168e64,4421ac4b,30a635fd,c15527f3,e56b21b7,c0008ff6) +,S(e292a4c0,fd63ec7c,c661ee1b,25c9d5c7,dd805a97,fc6e64ce,5d3d18ad,ddc92aa8,ede226,7fa6fe51,1d4a510f,e7f5e14d,8fa29fc8,b77d61ea,2e140739,29644745) +,S(cf914327,85a05d41,2ad06146,6df68649,d3b8e0f6,86a83a70,d32c2fdc,978d8124,f74c1223,7b0899dd,53afaad1,b9b06704,c61c6e97,81456e86,ea55e65,96b0b13d) +,S(88e58d21,95376fcf,b3f0c44c,b013f2ba,3379ba55,2ceca46c,38ed48f1,263fe894,f2f38177,bc673cbd,70853432,6ece1090,5863ad84,1140c59a,2a782af6,ccbaa71a) +,S(90ada18b,df0c62d9,dfa35e14,d1eab9b3,48d09579,f4e82cb3,65dad65b,62ba53c5,2c4e704b,fc9a9b97,7d330d92,602c677a,4a06040,74517775,d441d0ce,6c5e5e2f) +,S(3bdc1eca,a939c8b8,c164e5f3,1a02a6dc,9d2712a4,6c30f54f,e2159934,d492d1e5,e53acd26,28f6e646,edb5f53a,d9e1639e,43b4ee9d,b61a3545,a9874609,9b3c1e94) +,S(54a206cc,48c7d529,c8bd2ca3,91501ca2,1b58f5ed,921ba11a,95eadfab,3a720bf,83f4f820,b9a07ae2,8c5e137b,c9ec4f0c,31c875a4,b055b0bc,8de38f74,c54c06fd) +,S(2754b021,13885a9a,f44cacd5,2999a03b,d16c90e,4e3a7a67,cb3426df,f6abe546,f8969b1e,e955f8e2,dc0d3090,167c0116,66beaa68,35bbb1dd,e721c4cf,768ef566) +,S(ae438153,4cf07fc9,1dd14a40,68b3cfe3,aa6e608a,bf1d1c1e,13ac54ee,e0759165,e7807ae7,395fd072,6f77d7dc,956c58a0,69243b63,32bad336,72d2eebd,662fb99) +,S(cb9d28c5,357165dd,fe18010e,4263e626,65fd6315,60ec8126,c7b60603,6ec067a1,86879039,f3264d1e,511fa8e2,4298c9ed,6f1e7625,897666ad,4db9a43,3338f5c4) +,S(763534f5,52317771,551524ae,7ce33579,41da627f,db77d4e1,a9cf72a5,f182d276,9fd8ae13,b1706cce,ad190066,9b1d2f46,8c177970,b5173f76,55efee54,ec5cd8de) +,S(3990fb8b,39d529d0,b2d38432,3c60ffdd,3b32f7c,278aca88,f49f1523,fa16ded5,e43c977f,427570b5,b238edd0,77db9df8,b1b82e20,b73b8d56,84a2ee37,886edb83) +,S(6b76502a,22280e31,d0b6375d,6dffef0a,94dad7bf,e640a406,2ad696fc,2744cbf6,e667122c,b6861cfc,97fc4ed0,e6193883,c79a896c,79e31603,a67063d2,421845a) +,S(7ac03be7,2ff15233,5ec9c349,98e77ff7,79e5f348,a49fda4a,e52a69cc,6b02c02a,74a9e36f,af0ef72d,b493617e,9474b112,593de375,c845c8dc,c1028ee7,9fd17599) +,S(31a83e18,c03c570f,31c3ecbe,bb787aad,6492cdc2,3e07d3a4,43b56ca2,51c0888f,3cbddf10,9bf08e81,830dfa49,d4d3cf95,224b7a4e,fd6d9997,9dceb64a,a8d7e440) +,S(ec13f2e8,53935ae0,705c2644,d1512bfb,8ea4adaa,d45a454e,f761dc68,c7ace561,54139641,97e2f5c5,5b4b7f5c,5267ea7a,bf36b01d,cbd62765,ff56322,c107f321) +,S(ce8e23cc,aa38e603,70defdd9,37803f02,18ae79a7,3679c363,4dab15a,bd46004a,1ddcf92,92ea77c8,4a1346a,a5f79f13,26c9092f,14ce1992,dfc24bf1,a2da35d5) +,S(77d80689,f7cd8585,fc03cb20,14d02425,3b547962,a282beff,17aee956,88292ee2,466283f6,429248fe,301e18ef,61a89784,47142fd7,fcc5c496,36dca83e,ae8e9f03) +,S(6eed61a1,4e7e7ea5,54a4e43a,61fa7eb8,b8e7fd0d,52de9ad8,e4e9b130,78178a5f,2cb4d228,90af9cb,a3c050ab,12a3dd53,1f1c6d6a,e4393449,219d86e2,6465bbab) +,S(10cb3737,a7916d84,12c955d4,32d1ad46,d8c7a60a,e44936ba,f62b2a26,b698e4f9,35499a5d,11e356ad,fc5960d5,8d6842e8,ea0615f5,e8bf39c1,8c217cc6,7a1faa97) +,S(e866ac84,6040f92b,c52d298a,6ae110da,e16bc797,d6958858,1f63c00f,6cc68211,257803b9,ef7bb5ac,59c6b59d,32a49d53,64c6659a,cb0482e,a9d57530,4e244f90) +,S(5e75ce15,aeb91c2c,e4ce06a2,decf02a1,db85ef5a,a43c42b6,179d4a,2eb4e404,a08d537e,529a6654,a785969c,8d8b0b86,e3a76f91,d9b72d92,992f8215,1b61bd1f) +,S(fa80c52a,12afecf4,7a5b48fd,60fa35b5,ffd122d,8616ca07,1c1271a9,bfe2c104,74487acc,dbe2e60e,e054643c,37cd624b,3dbbc3d3,4aaa6009,9c97cfe4,b3d69e81) +,S(ddec1720,dca13ec0,2eee97cb,445044e8,d6b049f5,694ce5a,efa664d9,d89b6dc,122b68af,feed4f56,b72a4a7e,e20a7f8a,8daa9883,9a797857,8dac0851,1b843872) +,S(85116566,57b70a1a,96c5da0e,fa5d3025,49fcd00d,104a4989,442ff143,a08b5a59,2c96bdcc,430c6c28,9d54806c,9c17961a,8121b10c,5168cfed,a0dc8edc,9f4f551) +,S(7de80b36,89b9dfe9,de2f4fd3,4dbcedba,9ad39842,f105f580,d8d5f6f7,e1eb3e58,43d20653,db727ee6,155998cd,f7bb70b0,9be15c7c,a04593b1,398b4586,baa3c417) +,S(e66179d,366fa7af,a982c480,9578d469,91731ce8,c669d8e6,bdc399c2,a2484b1a,9371d856,28bfb10e,d07c3390,b9f08222,98793229,ca2cf2ee,75d53d2d,e8144441) +,S(1d435048,d7b8bfa9,6df91fa8,f8f6d1ce,913bce85,c2c7a1ad,cc199afa,5da012f1,4e389f26,4f36b2c9,296934ad,5f567eed,30f3179d,6538ef70,f4205219,2c3eac82) +,S(5866c91b,619f53ee,3256772f,56191a35,23696c7e,2b5140a2,89f36c11,c1cdfca7,67090999,33ade86e,3a5d384c,840fc584,2a85087,10085798,913c41b0,e2dc1737) +,S(1e06d768,20c95907,405fc2c6,bd75e1c2,fb9c4d0e,4cee156,94e1a4d7,65a77a01,4aea359f,bd26d7a6,1b1e9f75,5544363,27eea590,fd2d5eb8,87ece7c,41759674) +,S(ee415cb8,5e1ef106,28c149bb,2d2f9e1b,a8083367,60a7b56a,f122cf4c,9b59e5c9,f52aeac6,e2fb8a7e,ca5a629f,b431736a,eec16a06,c1a4b876,94ad8bed,bad88ae0) +,S(42da5368,b60f85c0,9f52c3c,9aa82e66,4868ee37,4b1851e,d37ba6a3,fb717e6d,71f65b16,a3937a5e,b535da,ec530798,d1c91c88,722fadb6,580510bc,5f27ed65) +,S(10bf9254,6300aa51,bb08f849,5acf4e53,b1eb4e57,1328fef8,304189e9,7acb81e5,9b519bf3,482c9516,132388c6,dabced4d,971d033c,52bb5607,6c788a37,a9eb49cb) +,S(2f6456be,5c2c184a,1dbdde7c,2dff16ac,8f504a9b,d86de1a7,d9d3c1b2,bb0688d8,6bcdc62d,4e6184ef,1fd39720,55da6e7d,45cca6d9,4d15c052,8ddfa715,4aa68336) +,S(e68e8fb9,beec6d15,207e7017,c3574e34,102fd7f6,5e5f18f1,e533487e,5bb2fae5,13624e45,da0b6edb,df4ae938,82148590,63094aac,1b25d534,f80ca56c,814a944c) +,S(7d314f19,4b776d00,b946825d,f634f763,e0b5aba9,b1fb0424,833301bc,ab1e94ab,80d402d6,a8dca19e,709b9e23,4dfda4f3,9f9f758c,928c1b9d,842b2d5d,aea3fa54) +,S(fd78e68e,5b5b9522,14b35d5f,a0951f4c,4d5df056,28c5bafb,9c6144e5,c2c33a49,49c62b9,aa91e5f4,be13a209,7f941c9d,cefbb300,eeed9fc4,a12c1e45,37f4535b) +,S(e751f61a,11e0b68b,205fd306,9f4abab0,b2576131,3ac3ffc4,eb4c525b,f2b28f6d,aafd8f6,2fe20722,b180ba67,f9c3b6e8,8ab2b96,956d603c,868a8867,c09ddba5) +,S(6db64d2e,281e6734,a8908de8,554dfa93,d7811f01,2852a306,9e6ed59e,c09a4823,1c1276a9,1de36164,309d83a2,d9fdd003,d889612c,6026817a,cda97f8b,d2cec77a) +,S(4de91b17,fc227eca,652f8d15,8cf0c696,c6902df2,b44a02ea,d05dfd68,e408a17e,130fd485,d59456b1,48458cba,922e9ce7,79968340,d7e5267,b2f288d9,6df924ae) +,S(356ceb61,8c57db92,931a305,4537ddbf,8bad8cb0,c13f447c,1c3fa485,5c0ba01b,6f52d242,ec189e41,d8ca010,cdd645a0,8889206a,4df81651,1e820e0a,74bbaa7) +,S(595d8d68,1ccf61b0,456ec03c,53cc5a93,85e553b5,36033f8,ec56c575,db49cce3,773b50cf,f0718e6a,6a269cd9,65b5001d,f5613238,3e279bc8,fb937580,a76ad035) +,S(b076afb7,234e8fd4,4107caee,8d31338a,5122d785,4707605c,2e5dae93,2e6c935b,a18e661,1921080a,84a74df6,15006847,981b552a,4c3021fc,93967c64,b19d629f) +,S(834a4467,bada612e,2705b0ae,19fc9f1c,b80e6e9f,7c4c01e,5899fba0,1f60cfe9,9215b4e5,f8194645,813f00a8,58f77324,e28522de,16e93e80,93197310,38ed9af8) +,S(d00093b1,ba6eea04,c08df30c,90b2f7de,6e13b42,c645b01b,127003b,5642aae0,87739294,746ec547,4497a951,c26ca6e9,12ae4638,1d0e2804,5363fe26,de088434) +,S(66a33bcc,71bb2f0b,ea36e5d0,f1446ce5,f3d44a9c,b715e6ba,ed00cf70,4c4ce1d1,27d0f5b1,a40fb2f1,e1f9440b,c1c15731,50efa23d,b8997928,cbdc0e03,2824a6bf) +,S(ac65226c,dc40fb92,a9632724,20983b87,9e1a7615,fcbde26,ac9754ab,408211f0,ae633790,687f1c02,52115aff,3a7d5c92,9064bbff,616b5eb0,87d5c1ee,57cf8d3f) +,S(a4b98213,78cb0e23,d46b1359,43b354da,3825e27a,76f33334,147a3ee7,52a81913,2786a9fc,3701a6e7,caf07030,3bb382b2,ce6d7c52,42496130,13382f59,92fec4e5) +,S(6f58e6ad,e4af1dcf,633d3f35,8ac86836,cf0968a,49c2e85f,2434ffb4,2475a34f,70e30dbc,dd567c8d,a6aa970d,3131009a,f5297a36,f80750be,6462f1e8,336db9ee) +,S(9bc7ace7,d55fcd7e,e67a0484,7d0d6902,f053288,ef17b3c2,64072dc0,ae0e94d9,7f57fd70,93f3d09e,53a82818,e5ca4cc1,6b46f73d,2c00e50c,698d5963,8a8f3f7) +,S(248195de,ab1ad52d,fe739d17,a03ab762,927e058b,ae666f2a,4f7e220,3173bb3d,1b1914d,3dc9f584,51ac3346,d05f033a,b793277b,dd80262e,d2ffc219,6965b96) +,S(f6008de0,21c5cd46,d9d811d4,4070a7d6,46105a55,2a68c389,a11161ce,9baaae2d,962cd254,e5b0d724,5ddf7114,32ab2800,16f5583a,b8fa02a8,61b7c3db,eed77d91) +,S(f6dce5a3,a919eab8,c1d850b,4643dd3b,96e5c7e2,a1923b58,202543e5,4ece5b40,c1b88f2a,b3d516e4,7d289ecb,b16b9bb7,4082a6c0,8ccafee1,21abf691,73734c08) +,S(23fd6281,b4958fb3,b94ac9cf,851561ca,415cc979,7ed619bd,15192ca7,7ad39a71,fac12f70,ee38f19,b2dbbd5c,3cfe02e1,b69bbde8,7a45f8d0,bdfc909b,640622b0) +,S(da3a5c2d,7cdeffda,1ff2085f,fcda0ded,eb40bd40,3591395,e4b20b0a,25a1aa8e,e0d90d58,a6cf6038,10bf2891,d51da508,77d3aaad,5ccc971d,944ea960,c943e3be) +,S(22113f6b,8015b2f5,bbff28c2,cd5dc253,c495cc62,88361c27,3e788aa3,58a94554,7b6f7bac,2db4f198,20ca6e90,bab75cd0,983536bb,80051e6b,7da589ee,dd2ae962) +,S(3b59d8f,d59a2e6,89c9d1c8,a4e2515e,73317cf7,41e38108,eaad880a,e3417c72,bb96d412,50614b50,8903c28e,b5d39ed5,b4e735f2,ea10bfa,1265aba7,f21ded5) +,S(3407e5a2,4831b572,abc0eeaf,c587b20e,9339dd05,ebf395d5,378cb151,e0dfe661,49214182,78e4d0be,31cd4e1b,9b0458b7,7e028840,4a0745f0,40960dff,b9022926) +,S(9540e180,47717f7e,f6ce71a8,31368919,e4acbfb6,18c1582d,cb4093f7,caad2601,6fe0f64c,3f0aec37,5b36758a,7f65c7b8,e9329f62,3309900,efdd58f0,7bcf244) +,S(bd02f758,513ef045,af72344,f5d790cc,1c549a23,147a2743,b8c0cb30,891af3a1,3c605d60,13a560d0,d792c5a7,6ae2f739,7631ea13,7347864,4c6bbae6,dde96079) +,S(43b10d72,cf1ba39b,17abb149,884fd00e,56525f61,594f9654,48683c16,88f3e69a,cbce7dc6,1bf71def,95bbf6f2,fb03751b,8a0c6350,9eb9ead3,713366d,8cb71407) +,S(32900344,9ad58567,6ba2607d,c873a8f0,80d9c0eb,2f7314cd,9746008f,3c569642,8c6560c6,9a037d5a,3b2e740c,270ff543,54abb60e,b32ce5cb,4d18ecf8,1ab7ef60) +,S(a1495bc1,91188644,4991e097,5334c9eb,cc277640,f568a7b3,1350e4b7,222b885a,c5a1c341,67cbed14,7f28f794,d9d2867a,9019948e,56f986a0,e69a0d6d,6ae7dd1d) +,S(4640f683,f180cf5a,4929c2ab,5169de2,85861055,67c7c2a3,3db2d01b,c3c8d726,7d6ced33,7e752ab5,c87fb211,903c3a91,e5b0ee78,361a43,b716754f,d0daad8) +,S(91b3f9d2,7e989062,ced571ec,f373ff66,f8775213,95e625db,7d1b5168,e895473a,d7efd35f,56ffd001,bcf0191e,2692e832,99d0715a,edc1a19,2b74d8f3,c0b23b80) +,S(437cb866,aedcc0d5,8d89a41f,95cf5dd0,b355f244,7885843f,c163d54,a3839db3,9d644bce,31992f2f,302c5daf,67e42b78,74bf3f9a,a944e936,aef17203,5ac2bddf) +,S(be8cd347,572e0e46,5c745c83,26799611,a5ebb1ad,3ae74bf,474a6386,6f4a3705,6d39457f,c1010837,eac6bd41,bd4a245e,10bb39f1,77c5633b,92bda8a2,c50a3dd0) +,S(f40a76f2,130d31a8,c560795b,54fa119b,153a9a75,bfe7dd28,cc00ca35,2d258331,f92558a4,69f1055e,251c8814,7a94dd08,44bb2048,31f2d33f,d95858bf,dffd695) +,S(17e01809,b94d294f,c4438a92,6f90a295,5a9ed96f,a201df62,b96b19d1,7e837876,c9506f35,8efcf330,35603c30,880d5625,db15175c,486f3c5,841a14e8,8e5f4e72) +,S(7dbc457d,7ad4752f,8d57a09e,d4e8cf02,6c8b009c,bc29afe5,ce2ee745,61630da9,fea9908b,ca852ed8,3c1b44da,207e8bc9,221b8dbc,7e8b61ba,ab8d29b4,14eb29de) +,S(89fe19a7,6fe2208b,ee4de72,249a503b,88c63e22,71794bcb,9484fd11,1df7902d,8a4853de,c3cd9e1a,713e46eb,e771ee97,b88defe4,7e1106d,88ccd713,4e3a6779) +,S(3753a13f,531da9e3,5569ba37,7db76dcf,b43b5483,1e65fd50,a6ea560e,e70f3eb2,ace8aec3,867312f4,f095b4e0,d08ad314,2005cae8,b1369223,d1301e29,8e51d4c8) +,S(30d76056,384beb45,4c54b4bc,fcdcfe0b,1c1ad5b0,5e693384,8794846e,c93a144f,f79b9c96,8eee678d,b899f349,412bf193,5da9cd,c0ccdec8,2e45ac5e,58f3d3f) +,S(d9864700,7c74872b,40ec273c,59aa149f,8059a1fa,95e5b47e,378ec515,841eb2fe,782a39eb,ea47da96,9361a337,c5e2cc0e,49d43dc,6c6f9da6,6952f347,ca51845d) +,S(40d665ec,53720f95,42ffaf3c,30e8602b,53c52123,e9dd3b91,6a2e9099,8d534fb2,cc420012,490afedb,d840a466,bc27f66d,526a965a,ee653886,8cee46a0,6a58ee30) +,S(f31507a5,516719bb,f07438a8,7b6d6d93,d02a88ca,c18fcec3,9190f358,bab3647c,dbec3898,f884cb2,aedfcdff,402ca4ec,8c8f8727,485060c6,8da0d2e4,edb46eec) +,S(62573f5f,75db743e,7939137b,6b5e1d0d,41468ed6,28d2b76f,ca26ed2d,22532c03,9a4da6c0,d7966d8b,138497df,af25b0cf,2d14215f,ff039065,d42897f0,df33be40) +,S(d0887924,f15c5069,1d7ace01,7eeda410,e8d0a10e,f6091a4a,7b5f368a,42c54416,b2fdc385,527d34f7,a434c1a4,1f9fddc5,add3b1e6,82b63838,da0ecd91,16571c4) +,S(f7585b4b,5f3318b2,2307f34d,3e4bcedb,d4b91eb,180be664,4d031bbe,9b5e3d90,b6fd16dd,49993bd6,4d05398b,9ba7714e,798d3ffe,95393231,395c03da,b3ccc819) +,S(c9cdc99c,198ca8e4,458e5473,3e5cc0c8,100fb0b2,2dc6f07e,b1e99d2f,6a4cdc42,c4c21204,67198733,a845efda,33a5c1b7,67cd8ad3,47a16b6d,4a6f5ac,995dd680) +,S(1a054d1,5c4fcf45,384ead78,e1260eda,dee9e907,84021c99,179a9fc1,c7a3ac49,50ac5d54,d4fad7d1,5fbff150,d70a3ce2,9825e79e,206f4279,f1030d29,2de89a07) +,S(b0796a05,52245e66,56b3ce51,99864d40,aed71b62,3b9d55b0,d99163de,7a8e3455,a061fa65,63a84b15,1ef666d,9f99abcb,f1611e3f,6e26dae2,6be7ac56,c0f2768d) +,S(66fcf877,6ea02717,aca92a3b,5c19387e,72658125,f6e20da0,7595e376,804c3738,9fc66cf2,3195dcdd,7a72225d,96a8c5fd,1c84287d,2189e414,d0deb6b6,76e67518) +,S(3c7caf6e,d3da8c0a,effd832c,f5356114,55646982,3a62dabf,2ccb3de3,452ac96c,e44ca4ee,bb290527,4cf8f554,40c92ad9,6055fe3e,1bb344ab,ace5c4f,80e3b984) +,S(edf616b1,1e7fd6c5,96df3fa,77bfbde,f3f82e7f,93e5addc,53c83933,844e4b73,879982bb,b89ac019,e83f5c,dac1fcf4,fb918e2f,53a5ec35,45be03e1,89bfe152) +,S(7e9af2ac,b3044985,181de5aa,9908259e,77207ab,933c7915,9dcd7aaa,90527eec,e4cc83d9,d29778a5,eb83936d,97bad637,370e74a4,3254eca7,65d0a449,106ee3a5) +,S(32dad830,3bfb015b,6aa6759b,c2e8d4bc,2a9e356d,92d97286,c92dbcf4,53591aaa,ee27e7fa,b36a2450,f04962f2,ee60f9bb,68a1147c,9ecabaf7,e0981564,c1164fd7) +,S(d20720f5,73f1db82,c1a2dc61,d3684d7b,a3943186,cd513a5a,9eecd59a,c9225a82,3b29448e,cac88f13,7dcddeb9,36f02724,6819c6c3,3f03a2f8,cc40a6e6,f8704570) +,S(3a732b5f,5dd6dc77,1d215321,f8cd223d,22f2195,9d45c708,5b000129,94d5f03b,3fc75d80,ea7a0663,c90cc7e8,8a6096f1,b3a8c9c6,9849ea50,f1fd5c94,7928cfc7) +,S(201961f5,412beebb,7e190d75,70977b50,230810bc,b3b69e3a,dfb5e259,d811333c,eaa3f681,3816ca15,dfb9aebc,8888b578,e592825,82dd1b47,ea50cad4,f59b0101) +,S(cb184267,70cd3b68,151f7d82,cf7a12c,dae332,868663ab,4546c119,fe26f4ad,6c395538,eb09c00a,3e973def,8272eed4,27a3ca9b,a77d1a0e,c4e206f8,74591a40) +,S(2c13d3ca,9ddbe3b4,2920f0ed,28622260,2cbc0493,e5156655,30b53da4,7ae3ba59,f7d9f8f0,c58510b2,db28e723,6d9af034,658599dc,fae14274,efbff408,dfd18613) +,S(3ae7b7a3,86ae6c41,90b3a0e1,e5f1a7ce,4fffc405,84f05043,93e716a4,3ec83d41,643043a8,55603964,c46e3ba0,999c73e6,ab34703d,9dcdf9de,1743db7,71591bd9) +,S(b5eea0d4,ac4d0a9d,e37fb1d9,6825233d,a00f26be,86c0b640,f0fb34c3,465bc441,9cb5b93a,56d35b2f,2d921c4f,15b4ce0f,fbb9240d,30754ace,f16b3019,54732e1c) +,S(dc049453,9b8d5200,d513eeed,edc2afdf,e76146db,358a86b4,12ae9e28,e8501e3,6f6f2d25,b331a4b3,686adc14,c26814,96004900,34524b53,8418fe5b,9776a3bd) +,S(821e937d,a6fae1b8,3b6aeb4c,f5315bcf,77b2fb07,d0081889,80478765,cea7f398,41405d00,33224982,2acfc2e7,87be9658,7baca419,14b70315,c1a57843,3f85aa21) +,S(818bdf95,f55a8e51,c0d08d83,29574c28,1ae3397c,f6ac5224,4d82ea53,8f465c60,bfd41438,46dbbf6e,3fc442c9,7a66e8b5,32445d39,fae8c473,51e0ee9,9a9a53f2) +,S(e3bb3262,36533fd7,84fc6d9,84cc5590,2b03a19b,6c956665,322c0bb6,264b6d72,bbe4844e,2be51197,d8d84c75,ab83cee7,c9ed7dff,51eac125,1161e09c,c1078aae) +,S(36e3355b,5f946199,b33dad7f,b42d1245,4cf8126c,568c4be2,cb2abe4a,b83decd2,fbd5d87a,8c012064,3ed3441e,ec686237,1759ccdb,4a7f564e,c2ff9584,ab824833) +,S(bd7fc193,1b06bd7b,c6c274ea,73d33e71,1e1e57f1,e6bf7a8b,6452d979,777aaccc,95b84e3,d8c41224,c74451c5,ec8a23ca,35436798,c2f34ad1,b1cd095,2f0170ce) +,S(b82cb5d5,2e026caf,bc075968,6e1c6c18,206231e4,f25c956c,b3a05690,8c72034a,6417cb0e,f1a18ad5,e1a2a5c6,945f3b38,b0522ac8,70188640,80a0f33e,2796b042) +,S(47d70d51,e97fad87,fa0b1c10,43246c0b,32108c36,734c90ec,9de5a2cb,c6227682,6d5a2cdf,e916fe8e,8b45d5fe,14ce650b,79a3cc9c,e4f99aae,2e9c449a,a79a4a08) +,S(13fa7ee2,5ea49c25,200b40e6,b9232eca,16dc6840,4f76b43f,510c8a12,d520dead,2722287e,35c9f98c,6041cb15,845d515c,870f5943,74a1ebd7,36f973e7,5e6dc30d) +,S(c1350b7b,2a4e73bb,90d36674,771d5537,1fa647ff,c490726a,3748e7d2,e72fa7a,2370f4c9,403ba7,8e950274,bc1f87a7,bc80c1fe,7618169d,5c9bc5c4,81e1badf) +,S(dfe512c6,f884588c,80c1ea7e,1da4b3db,ab8d4beb,60749f40,9402d583,ac793e05,b52da5f0,333b904f,678dcd37,a890b79,e985dbfa,b501e61c,89930678,5a0bdf09) +,S(88da191,2face14,e6f5ded2,30d303fc,867f9b26,d85c2299,bfac1a23,9757b34f,4236025e,f82eab9a,f1fd3c12,42a73cb7,fa305e5c,ef6ad5ab,ea62cb8a,4d74bbc8) +,S(b7a82133,7cad0eb6,c1de7b3c,8aff8798,8ee936c,921a7dee,1434156c,c6505abe,d03b693b,4fbce189,a552a66c,3ef4586,35454006,308c40ed,c7dbbc29,75361b7f) +,S(44971116,c14d80b2,f0f88829,fa1fac44,ac44124,11ac6e92,fb898e72,a0aa7ed3,9171395b,3cac83ba,18e9a0a0,34fec786,986534d,667b0521,801638ec,e19343f9) +,S(95f0aa61,8bd8573c,81b2dc3c,aa16b577,6ee636a5,a8f9c963,b8a45370,7f35a618,7f2300b3,90006ec7,37365423,2b488d72,edf5192a,a1d5afc9,4bc95a18,edf5532f) +,S(e1bd29e8,778a30e7,d9d91da2,3db2da23,bc2e8d08,d5cd170e,8e753e3e,917b728b,592894f2,114c11d5,f04dd5fa,6b341cb8,29c58e1d,a7adb51a,19d881be,39f87eee) +,S(f6110619,208f64ea,8f0e8164,1ffe9f32,8e706427,87dcf8c9,28ca8629,adb99e7e,cff63452,a596744a,75c5e8a6,badc4937,e6f2c860,39196251,30784499,733651a4) +,S(4a3f7c9f,1ba0f01f,d6d42aa4,284e4481,ed1433c9,3248da63,76f25b62,d2d2348b,dcd4c18b,2ee4e447,3515c727,1467e6b3,86b6e640,834598e1,4ce3f679,47f5c2b3) +,S(50fc4d78,3964a68,b26689df,34d996cb,6f229f2a,ed017a98,4e027407,5fd730a,7ccd6964,c6c5ab52,5d31db1b,ccefc3e2,7a0882b5,8586a955,b76c754,9698a7c6) +,S(1bdc6b52,e92022,229cb15a,6436d601,26c640da,bd9697d8,bd0c49bf,606ce554,1ddb0cbd,e7b0816e,b65d7ea3,98da0744,b3ff390b,d2e13cbb,f8fe85eb,8f3d2059) +,S(62dac912,48f29712,c3126524,90db807a,af92d3fb,36117f6a,f5c7f5f,de317000,b3bc4dd8,97a2fcf7,483c0015,f793810,29bf6f01,7be37530,18b55e35,4250ee0d) +,S(ea552caf,1d0810ff,24eb5089,31b57573,d8eca976,2b314f9f,38301b04,ff5e2193,68181368,108c4fab,7c58bcdb,5bac3a3e,31eb1b3b,837c7ead,e691b56d,e24aa832) +,S(7d6132a0,9e2c0af4,9e3359de,a750551d,7addad6,8bdc602f,e65af48b,bc1127a9,5bf6234c,903e5f72,45b46187,9d780ffd,72aef438,c0b65c7c,d0b5fc2f,8ad29d16) +,S(916d0d6a,3d08a94c,604c34ae,8c930edb,5ad57532,4dd5fadf,7a4841b2,775c475d,e3cd9b7f,79572001,a1ad9c19,2921ede7,64b12210,4d4d8796,a435eb13,d883d391) +,S(804128a3,d982b50d,203135a9,c59449ec,c43ebb35,b2bf5266,6b710975,a3847524,bcebda70,4364414,dc269d2,6214968b,95ad5bce,239ca9be,f10fa1bc,d6f31757) +,S(850314b3,4898b572,5898dd9e,f39196f3,cabf4026,8248844b,1c3d2f55,a3346f2a,eb8d17e0,3c9f7fde,29276d95,c20b657f,af2173e3,7c620a0f,d2ea7ab5,f4706b33) +,S(fd879539,8c0ee90b,89aa731,a4ab60b7,be0375fb,59932879,7bd73390,58d7aa8,3fd62aa8,35d10f6f,8bf3338b,b48bbc33,8f235cb0,6bcccbe7,56716129,abe8159e) +,S(51536953,12e7c4db,bb18ecbc,26c51224,8f841792,e557e879,8eda9db2,c9840357,175d3e7f,92ddce9e,5595fc26,725f2c09,fc322b2a,251fc323,ad3c6f08,44badaca) +,S(8783f2c5,f7325f73,e5941ec3,5f91449d,558e31f,28af335b,2fde477,86aa2808,dc602736,ccef3e41,60b100a5,ef99c6e5,c59734c3,42b545e6,21d9b03,daf0030c) +,S(c871ab31,4afd8d5a,a58a0cc1,a5eb49aa,f25b4b3b,6e0ffcf5,38851576,27c0726c,d06b7e2b,f44d36aa,fcf653df,a675bbe1,36a18938,60864df1,fbc9e604,5a3707fc) +,S(40efd5c2,1a9807bc,b34a44,71066619,a2bba48f,c306293a,ab59d9c4,b30e9b23,85cf348,4df34dad,2f7aaee7,b8a6f585,4de1314c,215f6f0f,e5a38e61,f3decad1) +,S(7feb5d9,844798a0,2413cae7,cd791e6f,54250fb8,12f998ed,e0ea6eb1,435986e,d90ca7e,8e800e6f,fd0ea027,b8a379b3,9030a03c,7b111d68,465d2a05,99dd358d) +,S(1b2da7e6,f9fa412c,ec873512,7328f1e9,ea77501,5741a284,12018290,76a79b2d,9fe58682,ba3f6ee0,c7b73933,d14ce656,e3fe49dc,ec7a5643,efa07a3f,1a4f26dc) +,S(d7fc1f3f,7a6bcaed,1a84e8e5,7127a6f6,37d935a4,35500e21,47e8fe03,d3b06066,29938603,d89fb118,14d33850,451f5c1e,bc5a5943,70c2d8f0,1d9bed34,f0b2e940) +,S(45352264,1cd962bf,35d8ab65,3d8d6c97,75782706,1454a2ba,3d957152,e177ee5f,de5005a4,3989b6c0,cee0a71,b534137f,b946b262,bfe353c9,6a217c06,c23826b9) +,S(91733546,dedbbc4,c7c32af7,760e0a4b,c2bdf82a,73e2a91c,2d70afb1,100b84ab,e60a6ca6,f2a5b12a,307cab41,8793b7c8,b7ecbaf5,18166c61,acee16ea,83071a3e) +,S(26bcacdb,bd4cabe3,e7adc5b9,27465ac9,616725b7,dce240e6,37117a5d,80a9ffb,172208e7,6603837e,4a9cfeea,22bc6002,a5cb8f1,36d73dd7,ce30d010,acc05db5) +,S(18b520f3,e997b5d5,46a1d81,ad95c52e,7841f14e,11e9d677,de918fae,7bd408d6,51b0cc1d,4c6ec5ba,e6c587d0,d6489663,dcf74bbc,d93c4839,6e4203a9,caae57e8) +,S(e25c2492,b7b25c37,cb88da91,cd76021f,de8511cd,27188b0f,d4bb4fe3,dd845e8a,244facfc,a7dfe65d,6e1d1d84,9bec4e4d,17af0d6d,2611333d,b8b7498f,96c88bd6) +,S(97e572bd,300d9649,b9de3c8a,b02d4ce3,bfbe4ba2,68844329,85971951,5c34a535,324a34a9,39004c24,b2e55e66,83577259,6c9cb2f,84378bd7,e77806eb,75e77070) +,S(4d31f4b9,42526f1e,769bcf70,85f39523,60475a0c,3f8d0a4b,c1fd6a07,a4765fda,27815d6f,3b083965,8fcbb540,925986d2,bc6d1fed,d1c254e2,9580f413,99afa899) +,S(7344c521,23968b7f,7ee12500,e9efa4c3,859a72fb,cb1cc9d7,af91e6b3,b5bbe152,4794848b,1541a965,b7527533,a6d43398,d619df34,2608021,a26bf9a8,76e0e4f7) +,S(1d099181,8da474dc,38e08fc6,d97ac823,be34f4a9,81adee58,806f68cc,ffe1ce9,f5449d7,c69816bb,6f04b32e,69e46771,c7910f3d,77cee6e2,af75c293,119f729d) +,S(84607e2f,ab205637,4b708486,51e84966,9c386eb7,c6855a1a,837c8fb8,5da8e7c3,975b5227,26e4625d,cc5c55ee,22dc667f,1fff7c4e,417cb4b1,45bcb3a4,53180ac3) +,S(3d2ad8dc,11a9103b,2f03b3b6,24d93114,83f4faee,c4b9bc22,f4f8f559,16bae70c,b4f79cd7,27d433f3,72735afd,64ef4da7,de9ac35f,98b3b6fe,5de956a5,ba93f9c0) +,S(2ad5f6ad,c7fe8540,8b9bf547,a564727f,374e9194,460f6037,ee299f83,e842c883,f613f0b0,8e306fa9,fbf39919,e3040d16,ede4dc6f,9d45ce42,e63f4b02,70832f9) +,S(ca92aa9b,e8893819,e49fc29c,dc98a42f,e23084d1,fcc37d19,3e9c638c,db0f2f93,462a5ee3,2bcdca5e,c6bf6819,32ded511,12ca847f,7e110405,a893b011,874a66a2) +,S(4e435dd7,ce2783f1,5bbded75,e832665,e4b4cb00,b50e2a69,d986d2b6,d3d893af,cddec0e7,1615f7f5,769cf2d,ee649723,1218fd34,91a07fa1,fe619f2,26673042) +,S(b1d936c3,fa1fea01,e3e28332,51f3790e,1e0f0038,58656e8,af53a72,118cbd6b,183b67ca,8039f372,d04b685e,c515466e,8ca1fdc3,7b2ae8c1,ebc5fb02,9c0ced9a) +,S(77282290,d994b6db,10b06247,1b86caab,e4524850,22a1e63a,682f92a1,ae65019c,feff37f,1fee0cb5,569db439,a7697327,1a2e4254,a9125ce6,cd94fbae,110f6363) +,S(acd0184f,18bb7c72,f7b6ac5a,7ba3f617,c40e159b,a3dea242,ed8e6c23,ad89dc82,3ab71793,fdf23305,aaebfb52,982a2603,e56d7b3a,5430585b,557d3406,42fddfba) +,S(933e3401,91e41c37,69b60410,fc0ac56a,e5bb44a2,413921b3,d25a52a7,14015cf4,ad3aa53c,76c106db,6187f4ab,755d077d,c6d878c7,64181565,77bd5d3b,affad12d) +,S(6e02abec,969eb027,bcea0303,6648fe60,da6a7c67,d1e4df3f,a34c1225,36de6fa1,c0283bd0,9fb2bc2b,19bf5086,f1eb5c6a,a7dfa258,64c73647,8a6cf0cf,c5ed5c14) +,S(2b86559e,82e5ea58,423163f4,448327f4,2429f1e3,14097621,667cf1d4,f0af1ab3,35bcfa55,9e2a71aa,4ea25edf,f9e1c59e,2274ade5,9354e22a,9cff37e9,31258107) +,S(1c02f45e,5787587b,9d68df04,66df67c9,552301d1,3b32f50b,8c9a2d5b,c826606,5f5de968,b39e9263,c1624aa8,811a4391,35150e,f8167698,c381388b,18a232f7) +,S(d0a65374,7fdd0c5a,68c55060,dd4ed734,34956730,1997d07e,d333b5a8,79894eed,272fe0be,bb192ec8,9f2f64fb,43f3a3,cceff7d2,cf92cbf4,4ddd8fd7,b104b85f) +,S(bb48b81e,e0dffe05,b6b8d05,5e1d2e5,37481eb8,2ce2bb81,cfdfbcef,bca5c6de,717b99e9,63d59e1d,66c0737b,9843231c,9c5e1bfb,26820215,78e8e01f,9e5a3fa8) +,S(23901c4c,625d77bb,1d390891,6befda04,522c698d,3c4e82c5,227c3975,3d7bc3fc,2434bb8d,98ebf9bc,42459328,464c4a7b,b26f258,2d454209,af66edb7,e84effbf) +,S(2a150d7a,961b5c28,434cf4de,ab9bdd3c,d019f667,67437158,c0f3fc38,54de70ab,e0d4756,9cec0eff,b155d7e7,37ee49d9,863374b2,4c6f4ac,876a80f3,7fdf7461) +,S(d42bd53b,1de9e41d,6e5ada81,3b86370f,ec1cfe1e,cc8ace39,edeccdf4,e16e7111,e3d4de0e,e5cb0d3,e3dcfed5,a09dcc,28c9f1ec,969d70cc,ffbd34fd,d0ceca5c) +,S(2a865f60,413764f0,d50adde6,4825eed1,134c6875,d1712396,938cd8ff,784b743d,4b628cf4,8e2a56b0,2142bf67,8829a388,929a0c0,5784426c,ed114f21,ca1dfade) +,S(2189b2e2,e88054d9,c71c2321,8cd229f8,a0165a15,650d1557,3eccc450,671817fe,6b9e0fbc,f9b9c354,644dd8df,3ec14217,66fb6783,5c698a1f,d598d074,aae79d19) +,S(e619a8d8,d63ce9ec,7f11cde4,1cfd33e9,62884fd7,471a4e67,9b9fd1c6,8537fefd,d819f364,5438c886,915f46b,40fb5c6d,6932077d,6f9726d8,86b9ddc4,d5a552a0) +,S(80c0df21,91bd2fa,2fa21bc7,ea86e54b,ee25d9ad,43893869,1cee20a9,b5792763,b27d7eed,198a695b,405cc3a,9204242e,45833ab,3b9feaea,f81db33f,938dde53) +,S(cb3926e1,eaee7f44,6f2eb390,64d4ced3,cf8bfda7,9c54a25f,6894be15,61c2d02e,35d9e098,6612e1c6,fbc2dcf3,54022f34,5b2220de,139d1ca7,5dbaabe,46c69160) +,S(82e82221,5dae0056,917680ba,a3233d29,23a2a0d0,68939d9d,2e2fe013,9aa2a8b8,f28975c9,b27352f3,f6a2972e,ef33eaf8,5d0d0a4c,673e3495,afb71869,e768bcfe) +,S(6b48f2d2,d756c7f1,77d5d33,cf62e419,b32fe0e7,352e84d4,d3056571,fd837e52,d4741717,3212dad1,b476b8fb,7ace774d,fdd545fd,6f99ba1e,b701018d,f762ad14) +,S(6cc8e1cf,ca1a026d,c1db0d97,46029b6e,b9895d7,61f66549,4f29cafc,1243b56,429b0dfb,ca05d077,d4d87022,d5ab333f,d9068170,5e246fbb,6861bd07,7e85b500) +,S(fe3de551,3fc157b6,e5bfbe45,d88f9339,71ff5ad3,c436ca5f,21794899,33f01e9,a50a9cfd,8aee1546,c422c5e1,494dd42a,db36bf43,89bac501,38211c21,803be0db) +,S(25140d46,a9453d6c,9370c901,6cb163a5,a5faf371,50152526,b01c7454,56b995de,6e00dcdd,82ee197a,3d5ca028,86fd7fee,56319f7a,925a39b3,becba6b4,c806cc11) +,S(4b9c4d2f,bbd879a7,de69e578,a2ffc7c8,8a0c700,f27f2e7d,cb8e0c2,ba3654f2,4c876a6d,7778cd81,bb02c450,5867d448,c78e9884,c72af037,55a66d7b,848e248e) +,S(2c11d305,3eb4b59d,8e8319bc,2b62624d,b97244a4,47326f24,6b83e959,5797c2f8,41e096b6,a1d2082b,c852782c,74a02cea,55dc09c,64b9d19,1265c1ed,9c306340) +,S(9a515c26,394d56b8,77c2b70e,ba1745df,9322810e,63c379ac,1c2320bc,48baa2f7,5ccafbd5,d1dc3d86,4a7ac805,adab05bb,5eb27f8f,dfabfb1f,708b2e8d,42e097e6) +,S(791a6bbc,91321ca3,6e017798,1a611cc0,db9d2600,34d2c26f,5f6fa5c2,ef7a5fcb,4cc5b203,db4459a1,f396cb31,acbaf9ca,18446f1c,cfded245,a3b32100,38417ba4) +,S(ad40b691,d3eb6ee0,e22e6d7e,1e0eafff,7afffe6f,2e0e3b75,103001f2,3a9653ec,ff7b436,47c2e09e,84f90910,2b2abe7c,2d38dc2b,151abd2,4f92d214,5607cdb0) +,S(52eeb361,405a56a1,331e3fa4,b854156e,7c403961,240f6003,135df1fa,8f3e475d,128e88f9,1f089e60,61d3df85,fcb2c50a,b0e67ec3,a7e5b3c7,ef1f53a4,5d5336c3) +,S(fe1a5ff5,162731b9,7b4423d,ad1b5169,5401920a,6b1a07c3,6ae9a25,ec3fbd18,22076aaf,ee720a1d,96e0987a,c7bb8756,b04a6b25,ddf5823e,e2f93ac9,e54c85f9) +,S(4c64f48c,fa628f72,b2897c28,4411684f,c0795753,60b8633a,188a89eb,e7fe1ea0,2eeabfd7,1b976ac2,acff49fb,bd029ef1,a42344c5,489334fd,393cc4a9,c305b4d2) +,S(44cbb9b1,7ec26b5e,3489c17d,1da0776d,539d2efe,d9317987,b86225e8,77b4eef4,f5c58a8,6ef02531,bcffcd80,ce788f5c,e43f5ad0,37e940bc,70d8411d,27c32673) +,S(2a5a456a,99eeea58,761c8802,7a86a310,63cdc681,b4ba0e2d,e5bce690,db8f3e84,7c3a47de,ce538330,49e998b3,4415c46a,2ac5c633,1a074e8a,8b4cdcc2,c6d1770e) +,S(566c0ddf,ebb82deb,bc867f76,e2fc5ce4,71249a9d,1b659237,2bfbf9ed,4eb6bf04,76989cd0,e0ffe1fb,a5992e71,50272abe,8a483f5c,6bff023f,3e5e1265,3598bc80) +,S(6fe42288,6d5d8232,4b229084,b1b2924c,b6c45080,3aa095ba,aeb2dd14,7b6def9f,b3b394b3,fc2682be,6e8c1e5a,ef2c87ce,eebe8d98,31aa60d9,ead74896,d7d9a38) +,S(35b45332,b65703f0,4feeaccc,39e04e1,72e58555,db94f04b,1899d835,9abf996b,6643809e,ae897680,5e37891d,3a241094,27ad6dc5,48429a07,64e2e12f,e89c2d29) +,S(17288055,5c0c5643,bd740b89,bbe47949,58ddb0e0,786cbc80,84d64ff7,838930da,f60cda88,7f85da2,3fa719f1,29aab43b,8b9a1578,53f91836,3fee5bd4,fcd16a2d) +,S(76eb57a6,c2d7bb45,d6faee14,4ba7c798,2d8ab2f0,5240862,aaf32c22,77dbc985,ef7cd7e4,d10ab9ce,4551c6ad,18a3ac6a,bc851768,18fe0805,f1c6107a,5c8c291e) +,S(1e5b38b5,81ad0859,454370f5,2f636726,5987e7b3,981af60f,a987c4c0,2440040b,d9817e99,89af200b,da690446,df5d8681,92808bee,91ba077b,400e8de2,712c2ade) +,S(ee17e98e,12a9efb7,c27aee78,15da31e2,af0d3294,40e7dbe1,9783caa0,2bef5709,a78346df,4d7dded6,9146931b,f1ce1b6f,1aae295,50a877ad,5f2dd0e6,adb43e42) +,S(bd574652,1eec6a76,7c2a14fb,15c85590,f7a19e12,342d09ec,67dd98e1,7739a81e,f26894f2,b8eca34,493bf871,dd6a133f,efbf1ab7,687a15fc,336d885c,6f5698a9) +,S(63852cb9,391a1ee0,23947718,b27c51a,8f9a5d0f,2f707f2,d0afd728,6596aaed,9d7c8c01,102abe28,43a7cff5,a1b15fa8,b56f50ed,1a034963,2e867007,e946d639) +,S(6c3fb2c2,d49c1c29,4eff674,158a3971,f65010ce,93f53614,ac9e0497,e31c296a,ffccd06b,98cb8021,589e833c,9c8b3b38,67850545,7e8dcf2e,775bbc71,47243707) +,S(3c29a60,103f7f0d,6f9a186e,48ac242e,46a525e2,380c4430,609f26d,6c3559bd,9381e8a8,7b4bdf9b,361acf28,cbe43e1f,a8fb073,ae2d1d09,6df7813f,bbdcfe22) +,S(bb76e94f,6f9ceffc,53ee4b3b,e27d99f2,d02eac69,8a4865f,9cb0293,a68c5eff,9ae48507,33f5984f,21726a37,c6ed7a79,e832af6,83afa20c,ee5d117f,63ab7472) +,S(422fcd5e,d2774d45,6d5f6312,c716582b,c142fcd1,342b71a3,d729643a,d022b492,b6d75412,aa9d6ff8,9be3a0b0,17384764,35ad4408,b544b53,acb5ba63,119b6bc1) +,S(328db4a7,9fff0037,c79aedeb,e8895e37,75b1e6b0,4d0a7a32,e2fcabe3,29c60dcb,40a2fab2,622ca3f6,39711923,812d8687,9d92b15e,ceaf5855,a970cf32,854b8970) +,S(4269bd58,abcd5e92,c62b0895,1c5ae948,efa1c96f,39ad3601,5e28d809,74da818f,1d1412ac,57ae762a,c3918ee5,33577c29,a8e63afd,c33b69d5,e6758416,6ff87b65) +,S(6c08ab40,97320e51,f3c571d,3beb7d7e,cfb690b7,af3d449b,50bfd276,290b8830,aba73078,edbed082,2164c850,5ca4bc58,6e53b174,83675f9a,26734cb0,747320b8) +,S(f4929f5d,99d0a5a7,3d20a223,c84261c4,6a085af9,fae2fa8c,4a025489,30174c22,afc89b10,7a38c32c,a24e947a,d8efd761,cad69f7f,ce0d595d,d4cfa849,201f6178) +,S(5cf5baf8,726c125b,e1499cb5,3f33bc0,37c983e3,b03f37af,6b48f3c6,29827611,17bd2e3c,d12adb0,5502a9a3,aa418cb8,c3809868,56d00c5f,319c34e,66074a65) +,S(c4639023,b41d90eb,2b275a2d,b7ab632a,592535ff,b439491c,f0e4e692,8188904f,738e1a93,5ace46a8,fb4f6ee0,b9026314,d8c243a,8fe08f37,b23b9c11,11763de2) +,S(a21b2fc0,2a60cae4,e694f206,1bd083be,5ed681e6,53af9271,a8ef9983,5807ce92,7f80d5e5,5a684392,939d6d1d,537919c4,99e799e3,4bdbb27c,b3d79587,f08d5194) +,S(7841ddd5,80f9f3a6,e702b0f9,3e1a4158,5e2c10c8,fcf98add,2a0f6286,c5d285b9,4dabb8e6,2aef6506,392b0fa9,4e2005a5,ad03c4fa,5fb6de65,2e89b31f,6e5ed166) +,S(5d5fccd1,378e4983,8d8b05d,41f89660,1b3a7c0,58df493e,2a74b55f,d8210356,a7e7b0ad,fba08a37,6fa3cbe2,75dcfbfa,bd0629ec,e8a5830d,d6993ec5,15ccf447) +,S(7fbd50a7,360c09ff,fe55369b,fbf6be2f,70cc5965,340e0489,77e01b68,2ab6c9ea,17ff93bf,4fc95325,26caac54,89e22f4b,265f63c2,cf46ff6e,a5b1d387,98a9b72) +,S(d6c8bc1a,479ea533,e99afda5,95365a32,cb1f3a26,72b4b960,2e039494,5b379061,cbfadb4,a6039cad,67ded1e8,8513dbf4,bb1dd3e3,437c989a,1df98b31,d234975f) +,S(5753de48,683d633c,11103cc5,b1bb04e1,5a935e99,297c73f2,fe851d91,5d5ea18a,10ef5d7e,ca1580b7,31ed4fc,3cd606b9,e6af8b1e,dea8bcc9,b91d522a,af361376) +,S(fc0d0265,b6cfdd0f,6b755a8a,c87f0ad5,b5a19894,5348c719,e9593566,91a638f,c5f31483,3c166c18,8c62415c,e58297fe,157fc6e4,b8e36b16,ce28cc75,df9017a6) +,S(61570f72,5b030b63,def84933,d0d25039,cabc8853,e5185302,d3f2d3ed,2611eea3,b3edb685,379c0068,9e9f522b,a5ad9163,9df723b6,b5c3eb96,3b05e5e0,a4b8fa15) +,S(5fa72a54,36b2b7eb,c15b34a,fefbb908,acbada63,23809159,66785d12,fbb3d6e1,1c027ed3,a34e892d,4b3d56fa,7a93ac18,663e475,a16a3d2,fac0ec90,be6d9ce6) +,S(5296b2b3,9f179516,d3cb85bd,5b86a9fb,274da608,be6e95b6,c44267af,187e2371,728aa26,e5869775,9c392558,34d00992,73eb6986,5db38b3e,df87f394,31c75dbb) +,S(b145c8b4,4c3f3c77,658899f0,a5b97c4b,dc2b195c,aa56b328,e0535217,88c80298,c73d96b6,47c2e764,e36caa26,8ebfe40e,c07b1e0,947e392f,be2a9095,7dba2595) +,S(bb585531,3cd05f8f,7fc96d8b,6e5194fd,2a70ff55,e7a7c067,cfbf54bd,129d87c1,31ed0367,8d24c231,8c4295a7,90d31094,e54ca68e,47b289c8,45b2f638,64812607) +,S(c6ebab34,ab48c0aa,aa5243f,c82a90e1,9a2012ad,74c925f2,e4e22f6b,13d6e5e,8107a95f,28e11f9b,bcef4b22,c4228c0,b530cdac,1c977640,53828b8a,15fc065) +,S(92e6ec9f,83dd27e6,3be3b0bc,8ebc521e,d84219bc,177f15cf,1bc840d5,be2b551b,931aabb1,5b3479bf,994f7eeb,34871b04,81efa404,c40e063e,59aabb77,2553ac79) +,S(280280ef,424f01ec,3fff321a,61dde1c3,4e674a56,7142c8e6,643ad1d7,e2dc4d51,84710eb4,5a88503f,b1078d06,26522501,fc165ebc,ce3b90a2,925ab1be,3c9ebfc4) +,S(6326eb72,abc4017e,c3f55af7,6a50a4a8,d0074ce1,2f5c9e08,146682ea,e406d047,53dcc346,7b61d08a,204a70b8,7658e4b3,c4016c8d,b5f03004,af6b545a,60223871) +,S(6e59dc29,408efda3,1bcd7b05,276b9f8e,bd9caffc,7f22c207,61f9fd0,a281ecb8,81c60999,45a6a834,8f2bc7e8,4ee002af,ec5729dc,aeed8187,87060c68,c2763887) +,S(100b0cdb,ed05ba0a,fcfbedde,94ecb25a,5cacfb8f,bbb80fed,1a9d87ab,72e4b5d6,1ac27087,8d6764fc,ce5f036e,ea50294,cf38c4c9,1ca9c3c2,87718505,491e11b5) +,S(95865771,e313a1f8,5629585,7ab9aafc,68a124b,6ae61f1a,a7b6e9f5,fb5de6b4,1c5195a4,338bfee5,9910935a,f5fa6091,7d06a2ff,3908fecb,b3fbe30,b0b52b41) +,S(6962774c,9035ce5e,fbb8e715,9d381004,a1828d67,38082e1f,45307ea2,d1d60eae,e9c36d21,6a2acbe5,5f9f9535,bea4b38e,7b75a149,3a281e4e,11b19064,e282c036) +,S(8e0fb8c0,d2d7977a,23f19efb,aec886a7,5170cd08,6a035386,ac784b15,9fec90eb,1218a3d7,6306cd4a,b3debd20,9bf9238d,ce00984f,47b5fafc,b715e2a,655ca9d9) +,S(6e9ea5e1,127c4c60,a01cd278,ac1dc091,e768acd,41566200,47aaa32a,e9cdf5e4,bfad3214,9a299887,c74bcb20,33e01704,ace7c646,eb00dc71,fd1e0461,af05a6eb) +,S(60fd55,9198e1ce,86c401a1,d31d04a8,fec7da21,4efd01d7,f2081027,1c49206,4f234575,cf4e6150,16ad551c,2f8e7635,3718b626,70ed30c4,a4c15e9a,488e0755) +,S(d413c7fd,2c2bd34e,2ab3e871,38495543,3b5ceb32,cba0e9e5,1e0ce198,cd5ea806,b585f836,7ef44ee1,8e32dd1d,95d6f8eb,555d8e53,451e5d8d,1cfca993,b23e344d) +,S(e26a95ac,c0855b56,b702cba4,abcf2004,c7343cf4,72056bb6,161ac077,bfecf98b,36a4834e,4532fa30,eb1caa21,c1572c46,efe4f7fa,e3c370b8,3f1fc221,632f8c2f) +,S(f0541a0a,d29e3806,e2812aab,ac185a55,687e058a,eb17157,e00a1cae,5a9e3d9a,40e81917,4473fbbb,58d40f1e,ccb2444e,ca4a616d,b0efc710,8f887c09,dd04b4b6) +,S(db1112bb,394f5e1a,b315f575,e2cfb59,4b83b599,6c82365c,c2095bdb,eb7c3cb9,c53249ca,30eb726e,6dfdb792,4db693da,f9740213,d87c410c,afa3ade9,51f9f688) +,S(2ad80407,9fdb7722,c96abd1d,35e76a7d,30948c7d,7b1e62a5,ab0994fd,667103df,5289aade,9f71ef7b,dce049cd,3052f8f9,653a9b69,3876b607,7bb42d77,9829bbe) +,S(7bcd5085,eea0f429,238c23bd,bc14017a,f8dde478,9d99b2de,325503f1,fcf7e738,7338f8a,7980de0f,404764be,7fb22578,ee820c6,16044f18,ad12d4bb,4b35a2a7) +,S(85819e4b,348ef2b6,d141baaf,3297248,7f34b594,8d834249,df465fc8,29a83188,306e8693,e7e718e6,39f48a1f,7dd5d977,9da12288,ef3edc6b,74afb1a2,906f931d) +,S(8d035a4b,5905b0ef,7fd9b050,ac1c006,2c0a8d84,1ab342bd,9444d101,92ed4a79,1ede85ef,aa83c466,4ac1cc71,c2d4027f,632f7ebe,2a097d98,9278cda6,35e2e0a0) +,S(bdc84795,289c3268,628904fd,5d8ef1f1,a89587f6,652aa883,5e83c47d,c008c282,1d8342d9,e330c123,7e232067,6fec10ab,b19c0d76,744915da,9980a9a5,6459a61b) +,S(75dab97b,d9e6571c,afbd8ce5,ffef58b5,72b6d8ff,838777f9,21834b61,1079c079,d048e6d1,fe2598db,31adfa59,8a445f0f,7f97ea3f,dbf8fe92,f2aa495,ff033bdf) +,S(b2a4a872,4a6ccfaa,8b8bde86,64fef1fd,bf9d4edc,215f9f71,738885a9,af19b42a,9bec2c2e,76be8104,f2dc4ea1,3e1ec916,23baf856,a92863cb,33b2781c,35f9060d) +,S(caeb3a77,35824f9a,b24e364c,789fe66,4729fe9,d6577ab3,f9cc857a,e1c2bdaa,ad96c92e,e91cf44e,8f0480cd,106b115f,23f0a30c,fd6ab638,86ee87cb,ba5ed885) +,S(a897e41a,75e6bfb0,78c272d,ec320e70,13afb506,478d984a,b5532b04,6da0502f,136cff6d,896be55b,5da484b6,15a09e9b,13453973,79fe4157,64ac5b5f,35aab82c) +,S(a33679a2,dc5080d4,a77fee11,5de9eab6,d32e6479,bd4fa795,3e4a03e5,3242412,1840b712,9ae52592,d99ee262,a56ae597,37e2202d,2871681c,6625de82,7c57819e) +,S(3183fdb2,b5ad14cf,9e2ee00c,e23b2c42,dfbeab54,acdbf786,622a83f,4db62fd5,7501e003,2ba129c8,50c0f17d,2a16b3c8,602c5225,1bac7212,6d454cd5,fc35a92c) +,S(aa6ac020,42f80323,ca77289c,88dc702e,e354fe1c,eedc720,2b87601d,7641b00,3ef01b0f,8b396221,8c5118e3,2168a6aa,ac901e02,5610db0e,8cf2ab21,bb06995c) +,S(f952d843,386eeef2,64b1078f,d835308d,514bc162,559c8f3f,c8ab5c18,378b40c7,fab32d0e,850f4bac,9a4af6d4,7d15976f,2e6fcf3c,958a72fa,b8db1817,cbc5dedd) +,S(75b58bbe,a5a1d00b,40f2fa2f,9e2ca8c2,eb568bc1,fe5559c,7e40ab8f,a0148e67,7fd6db81,7359736d,26883e35,727f4b21,2813be05,8d5d2477,1563696c,feaaf021) +,S(504e2324,34a12a8f,a96f64e,d39a7c0d,4168bd75,1e0b6249,6b24ab05,eff49d46,3bbb93f8,3d2ee95e,2e2821be,c3d98f4e,1f2def75,b5f8ed09,6947c4a9,1c933ea7) +,S(563fe95,1482826b,38f21deb,207a30d0,21271292,8b53c790,c33080b5,c4fc14d5,9b875a5c,70ee8bac,9e6d7225,1981c9f6,c3b5bb49,c8772243,14c4b287,e990bba3) +,S(91ed3211,161cf1ee,96499e83,7baa574d,1f56b244,56fc1589,d61f4d40,dea01443,f1664cf2,578f5ff5,8d81c281,514c7f95,56bce3d2,2150db9c,6f4a4df,db73d14) +,S(27526d79,f92c2519,838e3e0,a9ebfd02,f66e857b,d49cd8ec,6deaca31,d6064be2,9e4b644c,5bf8d50c,96892f3d,6843c94,f9afb359,55fb10b8,6a2f166a,6fa17f00) +,S(3ad1833b,df1ab0,effb0dd4,d535285a,900060e0,f38555c,ffd3e7f3,392b5c07,5c37afe3,89bf6e88,b88c9a34,6ab5a0f6,2128828a,22b6f907,8f9bc6ff,ed5c311a) +,S(eeda869c,21a8911b,cf713c68,caf261d1,e7fc58b4,3bdb0fb7,e83e677a,ec3ff6d9,f58e7eb2,3a62a9ec,f7a13dd7,b6b6809f,5cf8bc2d,22af477,e002f2c2,9c44d444) +,S(c7a806e7,bfc982b9,342fd4fc,e8f34786,9509fb0c,f0f54522,98162dc8,ec5a398,ba895fb4,f9084463,3eac5226,e91a08c9,e83281db,c406ab37,fa3ffc38,421a3647) +,S(c59586d4,ecde0cfc,3a1f5744,45cea454,aff18703,720fb4b5,1cd7b0de,de79c9b3,1eefa71d,5e67d9aa,15231b20,966951e1,93ebebf7,ae54ea34,e84b7c98,7cd853ff) +,S(3b259287,5283a78c,5aaebc13,5ba13c5d,4e5a1e6c,cfa196ed,6eb7960,e7eed0be,274d3739,5757e482,1ee56c6a,f56c8acd,f58cb337,6bb5096d,2cff4e33,ebb3c1c1) +,S(d062159c,e511418f,ea6e9853,b1e8d769,96c686dd,eef71443,e994a347,3b90e239,95ca957a,b37c702d,c707a3e6,388532cf,26cd3db2,64e4e9cd,fe0aec9e,77ab27e3) +,S(12a2bd87,c4ca2337,7915bd12,3f39e88a,fd7257e9,9587d70c,a9d8fcd3,2aa7ce4b,85562f37,ac813598,cf59e427,106fe886,aa1a078,1170f14d,3968119a,9ed65e61) +,S(9214dd98,2737e4b1,585b3363,c363ef83,b40a3228,53d6c54a,f5b1bf1a,a21e43a8,dbc49c6d,abe985f,d95202b6,bb925094,8b98bbcf,71486065,9b77c447,396e8eb1) +,S(34439200,b0dd5b6c,4fe8cb57,2e26de38,3311a479,ee39ea47,221a179f,fbbc124e,b8c15197,1ea4149c,d2a77b4d,14cea316,6d70b971,a7f9ef3d,315ce741,2ad43e1c) +,S(396b4410,cb5a59ac,15fa6034,7215ac8e,4ce3490,4dd15b41,46e90679,ca3128d3,ad4eef77,94f0c541,e70fd5be,45652364,c43e91aa,34683525,36c8b1ee,c061cba8) +,S(8ee1b02e,3ddb0913,dc781279,6316fde2,4489c98a,4e4c3cfa,c84b4350,34d53d65,9a4686,a5241a7a,cd4295b4,f1c9f094,3f8991a2,b7edeea8,bc4e9c4e,47956cfb) +,S(78f4da51,26b42e21,1d69c640,4e128c71,cd08fb67,efac0157,3bf128c6,73bbf6e2,b3836f1a,e397d7fd,c88d2089,6731fe04,6a46b2e0,b617117b,161e1e7b,3a2958e6) +,S(ff5c1378,c8a233e0,5edeed70,e23bb9e5,a5eb65a8,4b37c6c4,7c0a4952,e00169b8,a7275aab,c73eaa15,22328118,68de268f,189d3adb,da113553,e3f9d36,7664a0ea) +,S(18ff6e74,87ebb21,dc935fdc,265616bd,d3a4bc2b,35037483,c911f185,596a93c0,34209cf1,2710b007,821ba024,f0491e54,bade44da,5980f505,37869b8,b410d0dd) +,S(3ece6ecd,1c52894d,90043c67,59d158e7,143c9806,658e8e65,5fa53867,6609b0b,4b1bbdb7,7ed6deaf,fba0d241,5f2b7993,89cf7df0,7acfb8a7,a22f42d7,7858c622) +,S(a5356c78,6cdabe6,a1c379a4,ca18acb7,b41fd691,c32c31ac,6cd0edcb,9fdc5694,ab8f37e6,d19af264,58ec5b1c,78f54a4b,877c9416,ad76b646,7e91ca4b,24f988f2) +,S(e5dffa5d,961253d7,f99445b8,f8d8aadc,5415c550,da64a987,ee604278,12de570c,401ebeb4,afacec6e,d41006e9,c60e0ec7,a4840c2b,c64527ad,80d640e9,70800186) +,S(12c46133,4aa32bb8,465c1e9f,afe68d91,63a7a9df,e11940f4,593697d5,fd0c9140,e4fdfe80,be2fa183,4c32598c,a096a5d8,15386dc8,67588354,300018ff,5b2e0c2c) +,S(bd8d9bdc,ebfeaaae,691a19ec,c20cca22,af76db4c,ee54f904,ee5aa8cb,2010ccd8,cd5fca87,4a6cc17,79658266,158066a2,840e61d2,28bdd4cd,f2697e19,7c6c994d) +,S(597d8ca,babc10d5,5402f3a7,48987270,7cd5d3b8,3356ea13,6c8c5135,cefe3fc8,bb21d384,a48ed346,eadc6ba0,2b1e2231,7b2014bc,62d17fa0,9196213,6e12ab88) +,S(3d91a096,24fe1048,dafae0e8,c693226,cb89157b,980aa490,a9a12598,e77d7c19,738fee86,ab5a6e43,753b22cc,d6a28732,65db5dcd,38a6be5a,6635e8f0,136340e9) +,S(44addcb,55d0870a,19682075,116c8fc,d8f49b5c,5ef767ea,aec513fd,8d71841e,5e126cb7,8d13c0d0,55c3363,b236bba1,21cc5618,40e3cc3c,e100722f,1cdbdb63) +,S(5bc89297,4447eb3f,54b7b451,6da3d1a8,f23d6d42,54ec7268,f3e9b2f4,78d88467,6d708655,6b679453,253df54f,68541560,b5e542ea,8f1a4a9d,a023d8bc,6baf9559) +,S(8fe4f040,553ba9ef,47a0086f,a8dbb75a,2362af22,a58e0739,7c62d2dd,2ed2fc40,b2d0c561,5afbbe3a,18fcd594,a8e91e14,ac714af9,e34938f1,74a2c7d4,2028f65d) +,S(e36702c,2af02461,4d5f65a6,b855848c,6d72709b,b3ffbb5d,f08206c0,9c481a27,c5558c45,9cc186be,665a54f,19ed5f67,983bc004,c1c196fe,80c57310,59f1505) +,S(45b0165,d5eb9c2f,22c6f2e6,3f2cfaf3,5d4a71e7,460a41a3,9d3708c3,111387d4,8fcc8dcf,9eba288,f0183ba8,345c4b08,1b44cf73,b9df77cf,3e94c089,1e53cbae) +,S(ad02f6e7,378d62e8,87d651a4,eaec6d75,9b15f4f3,b7e999e5,2e9e493f,7d765205,25090d49,2164feca,33270e1b,3b08ead0,fcbc7ee4,aca4b4b7,7e06d865,5259a63f) +,S(5684c664,de3844ae,90bc93ad,426bbf69,5f349b97,6080193a,710b6125,3d6fcb0a,c5138172,4fb53812,9dffe038,7029b209,946a913a,cbc06370,b3b58f1c,759ac8f2) +,S(cb36c4a0,f6d2beae,282e9783,2148704f,aea7bea8,af0abca,ac670a7b,3808a2d6,d0aaf774,d65b7eff,589afcca,746b02e1,edbd10b3,e2938d6,5a0bd17c,f71de58d) +,S(143e1741,5da8c61a,959643cd,bcbd1eba,169257,2a935eda,1d0e2495,6eeee4d3,c12840f7,7a9fb642,50d19be1,5f35199e,1da83a3a,54fda063,99a6908f,87c9beb2) +,S(b6292192,d471ab52,fee206cb,64bb526e,6b8519c4,3df9ae85,4e4aafca,d53c1509,3d6fb4ea,45ae494a,e2c4c776,29047e33,f73af147,4fd217c9,856d5d1,3b837c56) +,S(d490352c,70551c78,9951a057,cb02eb17,fd710a0f,27dceaa2,ff224ec5,3c566a14,15ef3825,97ee6d88,ed8840f5,9d496c0e,6685349a,f1c62c27,a32eb7b2,bb193fb3) +,S(4797be1f,7ae122c9,2193fda3,80dac3a0,fdc14361,110a2a3f,5beb9f26,f3d85449,9377f25c,591ca377,2ffcdd37,65ef9a3d,6fa7c2,79d18e45,83901795,2e5fa3af) +,S(fcf5b790,98cff1c3,b02be521,76e1c6c6,ac10bcf,f083ae4,5d26ead9,82839614,8d94f596,5b075d45,a8f732dc,9fbe679,dffaf0ea,d2c90d16,b2c7ace1,1de05c45) +,S(d7153203,1e8cf246,683ee529,b2dcf54,1a731397,89e5c354,3bffbf4d,f95f76f6,740af4a,40e7849b,a41c4dea,4b7e0479,ca377fc2,69db00f8,b484c7a8,c4c745bc) +,S(27946ccb,dba75dc2,23663d14,2b56bd9f,2e63cbfa,73290de1,8fb7e988,31b44330,52c0ed5e,abb069e0,84fd8363,dcdb9134,364dcd4d,87b46025,9c3460ed,63e04ad5) +,S(cf3ea523,751eff3c,a785884a,b019199d,2a0ccf3c,1d90679e,141d4e75,74e41f61,b38d0808,936a2920,ece323ac,a707e1e4,914b51e5,e54843c9,63abf292,88ef17af) +,S(d06327e7,917280c5,d8a5c516,7e0d0c1b,e5fd9e25,4c877e9b,9ae6b264,6d9a126f,c3cc3913,9d0d7b8b,6a6b0c7d,b48213ba,eade2202,544dab7f,9ae42e08,fc7c781e) +,S(acfc233f,28f98688,2ac5ea8e,61478f62,505b8b48,38833499,1ccad1ed,5ee2a871,244a188a,64a6aa7,9f74e940,8681b45f,88b5c65f,d120650b,1a25674,eabb338a) +,S(87fb09cd,b3b8a514,f6a02da6,1d9b160b,798fdf7f,3f05d7ed,25c1c4fb,84b19253,586effe8,c4252dd8,c225a6a2,8e515abc,e36568af,107f8c75,a941e936,acbf6e38) +,S(77225acb,8ae251d2,be2f48a3,2eff0c6e,4f558287,13dc8a6d,3c6b7bb5,de9538de,d6eb8501,2aa7fe12,285e28dc,3efe9cf5,b0cc8e91,6fd63f71,c28c3f5,60259a8d) +,S(b03074f3,f2effb45,5caf88ef,e0eca7d5,6f901b18,fd04c01a,f0425c58,1f13dd5a,c8e8c915,fe06d774,8ceb2da1,78816b8a,4db1d082,6df3064e,7a4b0f85,ae99d461) +,S(992278e5,229bad50,82c8c346,8542f91,e2761305,62a34627,b1d97aad,a5999908,56ef7d8,d360d7b8,5af0f210,161ad17d,dacebd13,c45b9c8b,34f1ac73,bd6a1ac4) +,S(b73a2eca,5e9d0e4a,ef4f6ac3,e9610f3,1c72c67b,6a152fb4,82681595,8d44be12,c621ee64,b03b17e1,a25f27c2,aaea43d5,eb2918d9,e0949306,d2ef720e,8975c00c) +,S(7f6dba94,39e3302b,e4f7e0b6,f2363609,eedb30e,dc0b135d,e4aef970,cbcbc607,61685e2a,eb8e8244,175deb0,a006b5c,4292bd73,40b2fa1,3548262a,db98a3d7) +,S(88f0e36d,345aaeab,e8af18a2,d14191c3,8330c5e0,d4e32c5f,c562e549,cfad763c,bd062c83,13e44ed1,891b8048,2ee93ac9,95a0142e,e0543a92,a215735d,1e94ac05) +,S(da3a270a,bc03e6e5,3f572a27,94c8075c,8ce4590,b1b626e0,8a83fba,45dab386,884b46e2,3ab44b4b,c3d09f5b,97994e4b,109f2db4,ee04bc77,6752fdc2,6f0df055) +,S(df346699,77cd82a4,8e3e1b11,6311a5fa,3bb0389c,1c500fd5,15573af6,da0b2cfd,2ecf1fe5,fcec27d6,35c28d58,5fdb01ec,2192c8a3,848a72db,f53b7759,7b8b3735) +,S(11a4dfcb,3a443c3e,259c9f27,e24ca426,6e08fbdf,e4bbedb7,2e6cae42,569b0116,7fc6c4cd,1e3a0e22,d0ee0249,d6940def,bd776bac,9da5a98e,23cb6025,42b3de9e) +,S(2ffd148c,132eba23,e235bbd8,f09be1af,dcfd6e4e,5f2f0b49,6e4fadec,b931706e,6a4def93,52d875c8,31794fb3,6cf0448a,8c9ab5ae,804c6397,cec41b0f,24583acf) +,S(109e1f3b,7043c5d6,fb822b8a,54c6949f,117e829b,4065c0fc,c3ba5e05,a79142f5,c976793,6c034671,e5aceabb,9b08f5fb,af3cea75,a7a4485e,6c08f7fd,a63786fc) +,S(bfeea71a,8bb8ebbd,ca6135c6,26b739bc,eb2ada11,9babf946,6db9f4be,c7406610,44ff78f4,76074ac8,fd7ef860,cab01522,750fec0e,4b86f11d,831eecb5,22d1f682) +,S(4d2b7cd4,6cb01842,46e7846e,d0572489,e306b370,81515cb1,8a7c6cd1,dfb51665,60ebb290,44169e56,1d10a8e0,a2971b69,82276f1c,e7d11f60,ad7decde,e1fe9c95) +,S(66f2068c,e65a1a61,ad95ff3c,a2f15f55,29e0ce3,ad522dd3,d6083d5f,793e0e5c,a95bef60,2bb89e00,98723b4d,7fb421a5,b2cc751c,e8862ed7,439c1b36,efba2f72) +,S(4908ff3a,27aebeca,8697ec,dd7d5483,c6e56427,9d78f013,129e26ae,2ff459df,4c5def88,b60cbb2c,a3430911,aa84e4fa,1055fe1d,921c8fb7,bbb83b45,62b0f5bf) +,S(f9a90175,3a0f4236,dcb612cb,fcbf3f31,d97a8e6c,cc4f9e74,3887407,5d19ecb6,ed3c6f61,259943d0,310208e3,dd8d5a40,a5bfa646,35fca871,a6df79c0,830e24d6) +,S(ffed50c1,2434484c,ae866c1,86b88550,b0bd02e9,db04657f,aa81a672,8e321850,f16e95db,d44bc7c5,66fa0a23,669827e9,9cdf4372,6fba5f4e,a3e28d86,33edaa77) +,S(8061e9b6,2dd9d027,d43d13bd,1361ef3d,ccb4e599,c8d3d015,1d3adccb,6c762eb9,d33a9196,419b283e,5579ac98,8c1b6e60,378fc692,69b18da,a38f362d,20ca6895) +,S(ef8d0e44,e8b6b5e4,857d3d87,c82b01d8,f254c516,fdd139ff,7b8d034b,45698162,8325ccfb,6da9438c,e99dffa2,92afddbc,e1a101a1,20955945,cfb1608c,d35e7065) +,S(a31af5aa,cbc4f6ac,aebb9abf,de164046,1b631b2b,8cc43eb3,edb53185,fc1822e4,c3dbc990,691987e6,a8c318d,e381202e,a0c2297c,f06668c7,a2f027de,e5118a2c) +,S(81e47ea6,b60a3819,2094e7f8,f7bb3f48,a839b26e,d2aee7e3,e159bb39,798cac0f,186a8c4,97ba163e,aa779f6d,a8cf88a2,9f5d79f0,43e6f195,9c14bb1c,b1a53967) +,S(e15af4c7,8c61227,ffbf080b,a3b1dc2a,4966639b,926aae67,f2bec480,5c1fd232,76fd417f,4fe9e7c0,96db39ea,180d38ad,2360eac4,2730186a,b1da32c7,33fe9a63) +,S(3a185818,fb47ffff,640f265d,a2c712eb,f7e022ff,67a21a22,a2e941b3,f72639e0,8d370a41,1f133f43,34534b15,655717f0,73293f59,3bd34c5a,fc5c933e,120e76f1) +,S(18e05d9,2f227564,b572069e,18a271fe,2fef9d81,14eaee28,adb0fd2f,5f078e07,75f390d4,6e8f464a,d71067bf,b0d6596d,a945ea44,c7ede2f8,a9082440,d7e9a263) +,S(7bb788b2,4f4e5d49,2af81fdc,2f3a0274,6eedc5a8,cc74d1cf,ad17c2d5,4e80a107,5b28450f,1cd8c61,2b73038,ae889c39,d33cd64f,d452ab5d,28810177,dbb6a28f) +,S(a156cb3f,e4425b79,3334e19d,2b1208ce,5e6e8fdd,78748c0b,a1116031,912fb9d8,b2571319,c644e959,ba401415,3cba9b3f,cd8ac2f0,f8fa1f04,2d59ecce,5de14d21) +,S(eba23049,f5228794,1651a922,f60bc406,aa15acd1,59eaaf37,d1f2cb31,e7d20b25,ac8f8df,155b2703,d65b33a9,af581a02,8afdbe06,f4927a4c,5546ab7f,b5b2cada) +,S(8203a1da,56c4fb7c,96816507,f99a9a2c,950da51d,af7d21bf,123402ee,a95954a8,913884dc,10a53ab4,6b2be70f,5e2d201b,4c6bb23e,b6644817,b4257664,ed2ece88) +,S(d46609e3,ec19ef57,97ef86b4,ec51b8e5,7a854643,f817e7a0,9be4bc7f,291e7795,e1b3a8fe,3cda0479,11ab8c09,ecbbabd3,ce96974d,8a309ba8,c38c1d04,ef730830) +,S(1ffa9ab5,8eea8682,76849108,9e12ee5a,cb0ea4fc,e6f68436,377e985f,fabe919b,302140c0,7d64e9eb,6ea74636,f9efff59,e907a16f,5c876658,2f4a242c,b52f1236) +,S(4032bf99,32057d1c,8b6a8923,1575d3bc,6e2d6d0e,e1c7aaeb,ffa9ebe3,bcd9a0e3,13ae5217,f9f70a4,5f626295,b30eba49,e9f7c697,e3ed7cde,95442a03,b7916a54) +,S(8d32845a,14575fa5,ea061425,5e48e37b,f3a55bd3,9d924648,209d26ec,b94a20cd,e0416169,8f1e1626,18ce160b,e8b502b0,20a5ab81,7853903d,ad7ea792,b76266fd) +,S(403754d8,1a0dbcb7,1ff4893c,a5551387,b73d64b0,25bb612d,5e2187e6,7acea8b4,134dd3d6,8d642070,cb7778c,7ecc8d78,5c1deeb7,2582e25f,5c58b19f,2f59256e) +,S(4fbe9818,60e5f281,cb1afa62,8e8787d3,28afae7c,fcb73637,e2d64337,bd6e08fc,31a10b4d,78155900,7cec4099,3b5c59f,4bbb2328,b00a7550,572a604e,8d55b6c) +,S(a5414064,747b2ac1,e8868074,3a0fb05e,7ef510a2,a48d4f44,4647663c,b5c48b39,4b3ba626,56cbe6ce,2f228f8e,88d00ddf,135d6b81,979286d9,ec41c81a,f74ef7e2) +,S(5af99ffd,9f27830e,6ba54e05,63b3bd39,4bd8cdb5,764f78ea,b103405a,dbf3a01d,f625b35c,f510f9c3,db948363,3b2951e4,b2209b37,1397bdbf,1d899a7,400a4318) +,S(7fa7dbf0,1b2fd3f9,3ff4ee6e,17115c19,1f663d95,ff446dc9,d53d3e31,c98026af,f8aa82d5,28f2ee61,1a71b432,d553eff,ff1aafcd,8bf18e3d,b6dfa38a,f33eff7c) +,S(4838c0c,ed78e7c1,e3707d85,7311b2ca,6c7a4396,5be4e363,d4a374d3,36324640,4404fc6f,1a6c1751,3354d364,ffd43bdd,61b874f0,714430be,8a5571a1,808b5150) +,S(309c59a9,b42b374d,5c88136b,43310fbd,fccd6ba,e439e7ce,3a9c5e7c,ccb8dda1,fdded8d0,661b8ccd,834e4762,db87640a,fb1e9636,b894dd48,e1b4fb54,863863d0) +,S(1cc76e1d,788111f8,eeaed575,43afecba,548785d,3d66296,223142a4,7be59190,560e4685,6204121e,402c977e,76fac50c,446560a4,9bd2be30,c6dd2c58,62ef357a) +,S(188e8999,b9de6190,a65cc051,f936585,9e7a0498,4452402a,3093b4cb,24116589,aca3c642,dec398db,81fab67c,d05ffa2d,948a430a,e26eaa0b,ec3b2cd1,20b01666) +,S(2fca76ef,b917fadd,88e30d06,d6f398e7,6803307a,b8ac9817,4baa2945,9e9517eb,186c092d,95755005,4292ee1b,986b55c8,ee3be8ad,8adb6387,5e4dc582,c1105078) +,S(41d690fd,b5eb4f10,8aac4bff,26eaf866,86489227,b6e59b89,25440780,10e3eba3,251f34b9,d69611a4,31fe9605,7be2e10,6d058db4,34e0c814,b0b8a0c5,f2e50b63) +,S(282f850d,344e63bd,abc4542c,66cf1f1,a6807b1a,19ed7864,82d06a6f,a03b1e05,de82333,48fc960e,b22f5641,5265c1bc,be290ed9,4ee81fae,248d4676,d38a1a5c) +,S(bdba09b1,c1810ad1,ab2f5da1,b628a44d,a8512221,993c83ee,4a9484fe,6416bd2f,7049aac,e507d6f8,d77587eb,b8759f51,1b7bb2c6,5de0481b,d82a2f82,246d229b) +,S(e7e238af,4639d3f7,e5ec4298,65aade7c,c1127f41,d04b3d11,477456c2,b155a79f,da29edf,1e3c0ede,5546ed8d,cf8fa801,b5bf0dc,720a02dc,d9d706c9,a9c6bdaa) +,S(53a635cc,4e24790f,3ec5c17f,f854e2a1,800b2529,fa882945,50a0d237,58d622f7,ae6bf987,2d3a31a7,4f97299d,2fbae146,58c32dfe,418ee242,2029fff8,166656f8) +,S(10750fa7,df634f7c,7d8bd73d,e056e48a,50c413fc,282748d9,f4d0b485,d0ac5c1b,fdc72a01,883f0ed0,52d97ffa,8e6935b2,3f684f58,5e6c29b3,702f5147,dc645e04) +,S(881e3e57,ed8f639c,5023e45c,4e843bf8,e7d13323,289f92c7,143faf66,a65c3fcd,12f991ee,3390b45b,af1aefa9,dcd20da2,5891ac5d,35073750,47836a0d,e75b4dc) +,S(6be50501,4d72c612,bc0d5df9,13beac50,bafefaf0,aac7d032,824a102f,25bb45f7,4a0e0d0f,f2477287,ebd8c221,5bbf3e33,fff7194b,4217f9c7,57efa361,1b3abfb4) +,S(2ca955ef,23ed3481,615d1f0,db0fe5b3,f0fe5f1f,1b7f96a4,bdf814af,c4a58987,2dffc8f1,ce4ceeaa,d09a7610,7ee08e48,a591a177,9de2c84a,765a53e1,808e3c78) +,S(250da25d,5be3561a,92b56f0a,a7ab8fc2,bf7398d7,aca09237,3d416615,6991a05a,b0e62549,ca492ef2,9ab753e2,62b9c3a0,10df946a,99a982ab,812aa48c,def60c81) +,S(57606839,4791a29f,b66f2314,73b6bc6a,ff383623,c889695a,910c1894,d9c060b3,bac9ea05,7285d6b2,7911b19,b32fec7c,1b25db58,ae39eede,f4aedf09,46c04ae1) +,S(46edba34,d53e958c,c3e4e74a,ba8bb4d7,c6266aa8,9fe804fc,e27bb8eb,cba98e2e,2b85a88f,136b201f,9a1747a6,dc55f59b,41049a69,f824e3e2,f7f00787,f687aa6e) +,S(5d4f6ce9,ac52d698,1a13e73c,3156dff3,918d5b72,5e42f415,a503e573,bd6611fa,4c633703,1de35eed,dfc5e646,e8b0f812,2b951fa5,343d7b59,ceaa5c85,e1ce5c08) +,S(c77db927,f7e637fc,24a38bd7,f605383e,91e4e2de,8ba5b1ca,baed4376,8ae120c7,347f43e9,47b5d13c,c7cb6fc9,fc2caa5,2991a0e1,bbd4bc96,d98ff26f,1a81bc5e) +,S(ebe2ccfd,ce91b78c,95cdd34d,79fc9a28,478c406a,224bfb4,668dd5f,197934f4,d7866df2,d346bd30,84ad837a,a0c65cb4,62cad5cf,3d23b0e4,bd260fbb,f4666ec9) +,S(ddc5f94a,d4420ae4,8ece780e,26940338,294b8929,549fb897,82c06e09,b7935549,25802633,a0127d73,21ef0a12,a2a6eb32,692f2d30,5e7d3cee,f137eac5,8896ff71) +,S(e4000918,46f79f21,71b299ae,6b93108a,9377708a,e7d3f5c3,147a98d0,95e876b4,c8d8c4db,8fdecd8e,fda047f,b92bd9b7,3c999493,27c491ce,df19718b,6152c434) +,S(4add9850,90e16b5c,525de57f,a15a9c38,97527b6d,8683e407,3a978cac,53b29b66,dfa1601,d36d2021,6efe432f,8a335e2f,42b6d645,7fd6f032,d727157,32a85dbf) +,S(8a5536ff,ff9c9dc7,b2a67823,5d60ad10,834c9029,bc243f3a,f10d7e14,4ca0a78f,72ee78e4,e0a618ee,191dc7fb,c1143e4d,70ffd570,bd50ce8f,d368ec29,c22bc926) +,S(9f708795,6233a0e,59757e17,5a87e34b,f7a7485c,94f9aa62,61854b4b,3ac62c05,ff9902b9,6a6cd1cb,6ea35965,3527decc,7827318,97b5c8dc,c24e2399,fb16ff15) +,S(bfa5d3b,e3fd56da,d8e70330,dccb79f5,bcc46cfc,220f4921,4cb8e700,e7cd0ae1,2a4870ce,aa2e001f,567ac21a,79eefe94,8763b5ab,c63fa649,8a5cffa4,126c6d0f) +,S(a965e34,47f07286,b91df55,3e4211af,e6b837ad,a9cc10a4,905975f,1983f2fe,393bb45c,3f6eded,b6de1e70,5a706501,cdd10ea0,2548279e,c75e4dd9,15f0da2b) +,S(e5e38677,c1e4a659,51358301,dbba1e3f,c44f5c23,af7011aa,5ab93ad3,c69b18c1,7939d32,de371524,c3b14f5c,745ea023,34f9222,31f29d8f,c8294802,b334821d) +,S(473453e1,80511622,cbe6df28,46f479c3,b8638efe,9af11beb,21dd093a,24d243e,e2b897f,eec79c08,5afb9daf,c9ead107,57d21019,c655d0a4,e5254a69,941962c7) +,S(fff734d0,e5f24c63,dfbc061b,bac37c88,ec5aeead,4974cefd,77f1eb5e,86219772,ef8a22bb,c29c9756,37c600eb,82ee5873,778d539c,a289a50c,5490b5cd,8ee0bfe2) +,S(d0508f53,85f92779,809a1892,43a58f71,47f643a2,a8f5876,510b191a,fa292dd2,329eae7a,2ee52766,a5a2874c,3912d777,e23c4aee,390aca18,140ced23,b8e82d8) +,S(93170604,98fb43a8,41d09d53,98807f99,23405879,be9f224d,52d21971,130fba89,e84fe72a,cd89fd4f,f7db1817,edab493,5f809b78,6d0c3448,f19505e5,a79e60a) +,S(63634cc3,ce570dfd,8bf90307,f4543528,e26f2637,ce32d690,75cecd2f,63dd127,bd12dd78,7ebe0ea6,661dc389,5bdb6478,7435e9da,5812d06d,d9188b47,e1a31939) +,S(30c7e2a7,925a47a1,5aa1e2a9,6f564e31,61b7a559,3af1a696,91088dc8,9ee64431,5ef4f2d9,cfefe53f,3dc76f96,1a04bd71,6aeca894,73553a0,1faeed0f,6bd2d506) +,S(f7b7e1e,d56b7caa,74f60145,98c18126,d8bf10ad,c37cf1bb,5e6c88ae,f9f6e6e4,a019d45a,7e091a62,8b7125e,d3720f95,dcc091e4,2ff17050,da596fdb,58ebc508) +,S(c6e9b0e1,177dedb1,2e19a0c0,d1167a8d,8095b2fa,1f2dc402,adef0cad,d5d87d3a,64440da5,2b3ec64,4565ad12,6f732ae3,c81276df,5ef15605,3318693c,91c12b43) +,S(653d41dc,e6ae6cb9,45533e54,63b50a4b,72f6efa9,7884d878,52c32eac,fa0f59e,693da28a,13a9cde,d1b7597a,c199e00f,fac2f6f0,6bb0dc58,940b6734,e5eb934a) +,S(b9830b38,f0f8d466,e363fe57,38124fd1,8c9f60f8,cdaf6fd1,a10205b9,5c7212ce,a3e31906,6ace61e4,ad861027,7a12d498,5c35969f,62e5573a,5bf4ac35,277cf444) +,S(94d9457b,eb2bf3db,cd38a667,adda8750,f449af92,6a92fc93,f1dbaa85,457182ec,ff1bd647,8f00626d,1b203da6,992a2366,2ca2b39e,f6c1528e,956d7d81,7e2497c5) +,S(816f295f,1648ccd4,360b1807,bfb9b05e,4a84af7a,140a1f2a,f4016527,1d4b5b80,98217f98,6ff6f38,1987db34,7c6d432b,f24b664a,bb47124e,641d0677,aa4ec143) +,S(3b830637,17254b06,16c1a5e8,999dcdc7,cf3cac91,67284981,75c6909,92e2b2d8,dc2d471c,5323eeeb,611495f9,cb37840c,905eaeac,a85cca33,76d24705,d152acce) +,S(33282bad,f48bba9b,3db3cab7,c3b46c83,867a63b0,db393ed1,2a4bf49f,cd2f7c5b,78733604,b1c94144,ab9a4886,80bc53ec,9d67c4cf,dc77af88,40bc3535,f2c42309) +,S(58e15e93,95c3abc5,2ea68f94,6e3f7943,b1ddcc8c,e258aca2,8cd9f5e3,14b257ca,d7c15bc8,53dedf6b,f7dfad87,bcac6e37,f90d32b8,344d95a6,44879ba1,e170070f) +,S(c9ee3e27,54e8c8a1,e99c39ee,cd3d0b50,f8fcd03f,ccdab94b,e503a1ef,b66d900f,a1ce86c4,7f78400,51c87612,1b26922f,2b4b2358,e302c568,da6310f5,14909db5) +,S(6e2621bf,dcab8898,7d50b5ac,b27eb94,b52cfd9d,29e80d2e,befb5478,a3c5a514,ec4c657f,ebbe25bd,9d6b172,68ada77c,5bc4f32f,57f325db,9556ae0b,4d1adda2) +,S(6e3c35cb,9c86216a,250ae256,f8ab076a,76264ca9,97b1a6e7,41ee96da,5cfb2c9a,d45f0082,46489230,268e5c99,56dcff9c,bf6a0621,f8978be8,f2183920,eef3dd74) +,S(f3b5baab,882e20ae,b0f444e7,47688392,37acdb60,e7c878ff,40ec0aab,7896c278,7d8d6b2f,92addfcf,a248aa96,e8a87e92,4c821c1c,cf7b343a,f588ce7e,93e37260) +,S(b89b2380,110087ce,8c8bfded,7dc98e40,3cada8dd,77b2e68d,120361dd,6beeca0a,616e00aa,4aba8494,9835b0a1,eb0ff955,6268d2cc,91b7672,d0164693,848389b8) +,S(6659b720,1db6e160,58854e12,dd78b89d,40d91a17,b7eac836,ab8453c9,ee3cdbe5,5ac9db66,325b000,1983df0b,6693d93b,adf7e35a,a18f76ea,e2053e34,b1b8908d) +,S(6fc76d79,bf3c34ce,afe73e9d,7a77af23,bcc7e894,dc555f63,8b9d53c6,c15803e6,c366b411,1d6ee57d,52d3938e,f2c0c620,746d636c,92ab73f0,bc812888,ef0b5ac0) +,S(a06b2395,238326ea,20545cd7,b021a5cc,9e21e9e3,26c28b1c,fb50f3d5,e0f6e069,8560335b,a5c16aec,a297f185,ab09e976,dc57934,61170b35,b8a3b6b7,4ff622f1) +,S(bb8064ee,177f3753,19e45c8c,4dcdda70,ddc4bfff,1a866b7a,ae7922e3,13d1515a,a3be84ad,274704ff,e7a76277,d87687ff,572633b,2fb46b4b,40f601a1,ccb98d7d) +,S(27f08788,6a2c0ab,8f26ef15,a85d2d38,f6fb8cba,622acf21,77ac20f2,f05962be,ab2f2929,45163b83,c9cda8e9,4f6d8f7f,167bb589,50d4226f,827c5b5f,a782f8d2) +,S(f51d558,dfd8f74b,7c9c3768,65f82932,65c1bbad,d8aa64d1,ebbc09df,b7f6de1,101c88a8,170355ec,ac23ac98,ab5aac67,7a68a94b,9343012f,74cccfe9,308c039b) +,S(60fed665,a3ba26d,eccd1ab6,c956ed2f,e33da384,dc687f41,6c37b550,123a5441,4bc061b9,b2d7c63a,ac042a7c,efb2b5c6,a43260f6,8bd0f843,fa218cc2,3fc9b5bd) +,S(fdb04aaa,f2958755,ed65f968,ce4fbb81,ccd8c616,32e9494d,510a7b3e,d2564256,afbcece8,c52725ce,c0ef2aaf,a094385a,831afaa6,7c4cdc9,57712bf6,1073990a) +,S(7f45848c,cac04d5a,d9caa28c,e60628c,aea7f076,262689e8,27fc7084,d6b94dbb,51e16503,e044b101,a0d12d73,3a57d8c1,546c1f04,386721d7,a6f9fb15,458e41c9) +,S(b4091018,4c556249,30ec006,95f02723,b9cef749,57996d50,72642d8a,b492c9be,1b3c221c,235e721e,95bdd451,f0f0fc8d,a6a18260,d47b0c09,5f3e1b1e,f6135e5d) +,S(4d5b1a50,d00b83fa,93ab4746,3f84dc21,d0ff4559,2e697fa0,bf8e8f68,5e1c9713,da52a118,24ed00e,6a2434d1,ab383f0f,1e495dac,d08c6a2d,2425e25,a9434af2) +,S(63fb80ea,cb68910f,69700533,8a1769f0,69acdc85,da71618a,9cc03557,1d4bdca4,7f2e51d0,4446763,9cfdc2fb,247c7cb9,5b8e01a5,e27e6bda,6713ba98,1936573e) +,S(f5035d5e,12313edd,b0c31851,266f7e92,6f16c1f5,a6031aae,bd5e14fc,18304faf,7381fc4b,cfb33c18,7bb59e22,be95081c,6f52cefe,dd84bf6c,799f419,453f61fd) +,S(f62c9afc,5a2cfec6,afd54b78,375a73b0,eb5a73b6,f53e24a0,6d1e9111,ff20e835,a5d386e,211845ce,9ffeb917,76a8b91e,27f0773,b2d53fb8,2ac44b8e,83198350) +,S(7477749f,3824c134,c1a91bfd,d1b646f6,43f8109,c2f46887,22799f6d,53e6280b,232b459,2259cff5,99e4af77,1e03c94a,11fc32bf,f2598beb,2316d3aa,d9461c55) +,S(2e2c5f43,5fcf4fcf,72f9629e,ef6ac5f4,31073f2e,7034d6a7,64af194b,1e84b6ca,cb94ef61,c794f44f,2c7d1115,b9dca665,7fd1c184,e30f6f60,caf40e75,142ee26a) +,S(8f7e4f94,fd2a651,86e4624a,93272af8,2d362587,62a0baae,f65cf47a,bcd8214a,9edada6a,d23cf1c0,dd98b1f9,d9a83879,4c21654d,239f1a9b,dbace84e,de6145a2) +,S(e184ae37,4704eed2,b054b667,33544ff5,6ce55a9f,bcd7625f,740b6a67,621c75c3,ea773744,2212bf1d,a26fd119,248b0c51,c413ebaf,75e52de8,97b4383d,b397e144) +,S(fa47fd9b,94cb72b2,60fe208b,c022dfe3,a2bce2f9,ec776313,a497cd91,4166850e,9a12bbf8,a6e0ff91,5176f71b,16327c7,ea5d928d,816de31f,c8de2be4,57b0c185) +,S(98d36e76,a4caf051,e848fa49,64e09bfa,815109f8,63cd5e99,405e64ec,d526c67,706b36aa,6e67352,8fed83b6,8118ae7,1e043964,f444c477,590430aa,8d80d19e) +,S(e7722849,c00c5875,44751cf9,a88fe964,9efc05a3,4d810788,f30a2057,cc1d0c72,d64ba14f,978e1769,51951a4e,8431af78,57e6d97a,f5fc5ba0,2a1fd46b,1c215e7e) +,S(93374edb,547efea,e5f22d56,acd80ce2,80e4d47a,bddd1d9c,9d85afe4,aa6fe6e,e9c40824,cdedb038,9b42e2e7,2e15009b,215d9080,7df6316e,6e81ec9d,40dbbee3) +,S(840eb546,a076bf81,e4954196,6eb8c437,3a055239,8dc4deb8,62a73b76,8b380e28,97b69f93,ac1009d,a3f47ff1,b62ea845,f2900d7c,84e8a1d1,48d96f,df6fdb98) +,S(fdc4ef19,59ca06ae,92587249,e45d456f,d7484278,6f4b3381,676094ab,446c3e8d,c832aa3f,f0ce9f79,1fbe89e1,5511e25c,6a7a4230,401471c3,7b9ab011,1b304366) +,S(42abeb49,83403d4b,5f529eff,16bb991c,ce705250,5a61fd6b,96467a51,f661651e,d815247a,f807af52,b454e83c,9d46327e,d80d3ec3,60f84a3c,dc88b9b2,59efb05e) +,S(176fc9a0,b44a34ff,fd652cc8,bf715a12,a9d5f40b,adef6032,24536660,5e0680af,a47100b7,e0fc557a,f5ca7dc1,c220b5a,770231ac,184f14f,57e554d6,11daba35) +,S(11fa2a03,aba7cdd3,1940923d,37045c99,ff531ba2,a1266fc,26ce7a26,c6495e63,360c9e1c,54fbc823,9878d725,30254bfa,70453c3b,934915e9,5ea4542c,1026b272) +,S(b2dda11e,db5f668b,8d90dc96,9d10f8dc,6c95a16a,a3e2cbb,3a1ccc03,d9acec22,f4d44dc4,8c260995,4fc1316a,2d5bfe6a,18ea754,43bd9a55,2b2f2f08,aa0652c5) +,S(baa1d433,e2adc426,5f87f407,1c8c35ca,423f3e76,5a835a70,2d804593,cfc3f7b,3f55a493,9e25a77,8a9d3ad5,4ccccebb,8a45486e,1aeb817c,dc0a2799,6e70e996) +,S(478f18c,30470c65,b578a6c5,df9c71ac,934e3439,ee5d5d23,21f8f5a1,7adfe55c,c43dccf1,9a9e48c4,41ebdc12,f2876cbe,32852cbf,d3bebc9b,a3708fe,6cfa450e) +,S(18223de3,d62283f6,c440102a,a1769ac6,f5e1a0a2,cf3fdb2d,ec62563f,4e149ebb,57fc95f7,f8e3151b,80fa4e68,45349b7,961fe72a,d6ffe725,e3a0a0f8,907a4555) +,S(2c8c523e,179bc8d2,1e3faa5f,5db5dcf2,cfcb0426,3f138cde,428746d9,94a17276,c3f00baa,69061521,5d512c97,aac1b858,40842fa0,d8bdc773,4e11d370,4f6330f3) +,S(a001d77,af44959f,f1e83be7,d6b16496,2aecd428,3ae2c38d,f3ed3fb6,a0f12460,e68a305b,6f60a1ae,5a01eaa6,7675f0d9,8e6c5baf,75d725bd,d0e2a499,1c323770) +,S(cd8a1d36,2ba4a2db,255ebbab,ede7ab7d,383b641c,515250a5,cf52a2b6,a5b2ea9,20716fe1,e0857b5b,55b836d9,39d3175,188a6f9d,d3cc42ba,8c61a7a8,d9a7a1d1) +,S(7796adbb,7949b234,6df5147e,c3d7e0c2,5b61a874,43fea9f4,b4e742fe,6c1218a5,fc338089,c80c6415,dbb32994,cf5e2af6,b5c3342d,d291d7da,def80628,a904edfb) +,S(3c345146,27eb74dd,88ecbb47,aa9933ba,b7fc8cfc,bd80654a,f96aad51,5c1a7017,cf4318c4,1c021c06,5c100913,dc84259d,2b8d6ee8,102e5c6d,c3f809a5,7f8f2beb) +,S(cb4470d2,d5715339,acf391e6,5e3cc397,eac932d5,5acb65cd,a94538fd,eb684f67,15e6e6f3,383b8bd,1b674497,589ed7cb,e0f41394,e366ec61,b7e1b927,daa6434b) +,S(954a527f,c4496d02,915f0b11,4e95f104,c500be4d,a61eca3,2fadde9d,c1f118f2,96acbc82,d0dd6226,5652b61e,226b7ef1,c6f00491,91bce5cc,fecaebd2,fdd28ed0) +,S(d3be92f5,697a11cb,b8374938,9fe89418,97707ffb,24fcce90,16debabb,a4faa4ec,23d04ff1,e309abd2,aeac3159,ed9acffb,9c5336f5,517531c9,f1cc2dc3,a2d300bb) +,S(7490df7d,5a7ca290,fe029f3e,d92f530a,23a7c983,654bf19d,5c9ee21d,b4f57586,3d7f28c8,81b259e9,f5f735fb,be608154,b80f38d9,b5131bd9,a6e2dabe,7ab2bd54) +,S(992c4a82,1176a784,ba2767ae,a3d39697,5f96ea50,b7bc8947,2356fd98,20aa666f,519d9792,458fd39,7050829b,6ef7449d,da4ebdcf,fecb2a3,8ca0f057,f051c851) +,S(f070e211,b384b497,3d2dac23,3373a3cf,6dc7b9a6,2529af9c,b2fb1e84,b40901e1,175cb1d4,f8339521,26d4b41d,9fe27781,92a2a1d4,7d5faa48,1e96f51,beaad506) +,S(108f6113,c2795472,244db9f2,3a1002b9,349bd34e,f9c9d70f,e146df96,be198ede,2bb9d195,d087015c,253af145,bdd6134a,1845dd92,45d7d8c7,c12ad2ba,2027ba2f) +,S(ceea04f7,9b611056,ed7043f9,707bb318,e3ad332,1da82104,9790b1d9,d264349e,5380e9f,1c52e5ba,6ce18afd,289f49bb,c358bb90,9db23d67,a851dcf,9ba5ec9) +,S(1c74297b,14a4fbf1,2a6421f7,561c549,414147c3,2a683391,c4cc5eb0,79948cd7,581c9642,98232591,bd5469eb,c1a01be8,37bd5d43,cb63e21e,8540ee84,52b12ab1) +,S(14bd0f2a,ebcbdc9,22fe8660,c63bf12,c6db8521,f1f0acf,7406e982,a103bdc2,46c3002b,1e5f25e3,98c8d7af,e340aab7,23ccb0d2,7e24bec4,55d547ff,1df59c96) +,S(32c923a1,526240db,b234ab72,565d6880,b260e7a4,33747aa2,802da713,c30ae9a3,b79bdd74,6ce4ecd4,a554d66e,b787e312,a1ed53e9,c77cd6fc,73b1bfee,8b13d620) +,S(af0ec2cf,21ecdfc6,a71c6edf,6f73ab6c,76600a89,86b62006,8b246008,7cdf168,d229f3c2,7db8019a,eeb8f25d,84f3e341,681de4ab,2173f510,51ef4249,7a9dbb0a) +,S(1d648075,8273a9a9,c970cb10,5ae7e537,b017aadc,8be5044c,a1f6a3ef,4d2cb275,3faae8a6,87925e3c,e9c3482,d34c76a5,702c9bea,a307aa45,f2ee992,efd86387) +,S(160ebccd,b4d5561b,488037ac,25224afd,91c66584,660af970,672d9514,87376568,f510b49c,b58b09f9,46001627,2fe744cf,e6e7abef,8f54552b,41b6158c,2ce2db5) +,S(50867128,704795c1,a190077d,49a87c99,8b10601b,c37b7aa9,908ce92f,28969c30,106fc462,a115f2b3,e83a5474,7a692101,8c73853d,83d8a1b2,2b0bb785,bda8a896) +,S(3ff4aa26,7e8d344c,ac75a46f,7b17af59,5d056078,fe860a96,573a8f0c,164c54ee,2632d1fe,f4b34a45,ac38a80d,236b51ae,de92fb72,47b81f6d,af1b9554,4d426d82) +,S(739f875c,ebe795f0,cbb846c5,88c2b809,21468ec6,4ae09034,6032a97d,fda93c1c,8393a2b0,f3e68c9e,a57d5c8b,f8355d8e,71f4699f,201636da,5451023c,92a1b98f) +,S(717bbfa2,b2c4818,ae620498,3506a421,8cdff59f,2e040521,764b8013,b688c7bf,9229cafb,e99cbcd5,3f73938e,82723d2f,e7187ca2,13d9e0a,57c12254,dbc7f63d) +,S(cc0a808f,2618d1f4,27f165df,4412739a,6ad9af38,e8c25a2b,14bc97aa,1c5b0f7b,3b83d5f2,3d94aa87,9769c3d0,5e921731,17c345a1,3418ec3,2f92d4b2,c1e275ad) +,S(4a0b31f8,8f82abc5,6867fffa,6deb20d2,d8e7613b,aff466db,b260734d,1ef8b1c9,375d7c69,83784dbe,a5bfa8c5,8f8baead,8e94b9b5,95c01d9b,2747a06c,5fb7272e) +,S(52149a1c,53b2da97,f39e9dd0,23443cd2,b21e404e,b6235014,ce04c616,9f5d05a8,a9f9a4cb,ea6d202a,c6b5aa93,28f583c4,45db83e5,8ba7fc25,3eb43e3,df7f542a) +,S(6e294aff,9e9fe4df,9345bd93,1229a7b0,955a3b63,fd19d1ac,57bbed72,50262b8b,abe6fad1,5fe423b,e1a55867,137c485f,85639823,beb6ab2,81dd9e1e,ed447123) +,S(e9a70c2,c5e962c4,dffe24f3,8930e095,c2b0b540,cca2d891,2f6f5b4f,e4592e9d,8d115293,95051750,ccc9369,63b987f3,96bd05a1,49915103,1e47baab,358decf8) +,S(e6c934a,57ec5422,5d42dbca,715e62fc,ae957590,52849f8c,b5f282a9,b82d4adf,6da738b2,40ff3070,bf0c4ac5,50ca5a77,31db8a07,20312e2f,f9de572d,d7b67d4f) +,S(89b46ad7,cbcf1b6e,8741e422,ef53beea,49662d96,bcebbffb,57baf0d2,1441a0a2,b7205431,1798624a,3a1708a8,986ca47e,374d9704,22f7d3fc,57ca7f22,94913e8) +,S(5b949cc2,69f0b0a0,c3b85fd9,e45b737e,7379a4bc,70d926cd,cad87ae3,4e7a686,7e54863,c1efbe6a,a98d6466,65057347,7291b068,a43f1783,9d02b190,9327bac9) +,S(787baad8,9882587a,97b049cb,9906db7d,b909239,aa50e073,d476081b,4a73d7eb,8e3bb416,b57585f9,8b337b72,b23c2b9e,c790ef6c,bd397b18,ab78f8a8,cb662425) +,S(58460ae1,5a204740,c375cef9,e447edc1,2fb6a0ae,2f8d18b2,8e82ba60,729c82be,1bdf3641,d16d89ee,378e83ea,302efb0e,2a9471ac,9bcbf1e1,662885a0,81c7780d) +,S(c9bc29a2,1f7383a3,a90ed3d3,fc5f6d32,1697e354,a77173fd,77899d17,2012f63f,1c850132,db81d86e,e866d9e5,75c4f7b6,270599f,2e48c0b4,94317dc3,97d1043b) +,S(d4c54b66,a1ff77b7,63f8c520,181e4ae2,6b82643d,758f872d,cddef933,36cb84b7,23e64d9b,f7456086,ce8b778a,194f8b8d,f1f0bf38,fbb8454f,d271d062,ddb9b9ce) +,S(a947a90b,b27db3eb,8b4ca4cf,f947e587,54fe6a48,7b922ab1,9ef4b5c2,99eabe3f,c83e7022,f6e12dbb,f95e95dd,2bde8b94,3d72cbb6,5b222f57,94d13251,c9fb975c) +,S(d83f6040,4099d448,8ec6785,131524f7,3e58727e,829b3d58,7d445197,cb367746,6d50285f,831e1173,cfd7de2d,d1c7c29,532a391c,3f450b7,f25e758b,f412a8a7) +,S(75d60448,78902615,467175b4,bc16c936,14bb10e7,c9fae9f4,57d4a210,2d506a0b,885237bd,b4277447,fc7970c2,4e3e7240,ca24a07c,e46b8bac,7a91272c,ad9c5854) +,S(1f96a542,2ea4726b,b639d270,17eadf33,43bfbb7f,f1fe9307,a20fb5c5,f1c4fda,5f80a9b3,ec8b0fde,ee914ae8,e6052100,77701a73,57829803,4cac5d44,91ce0a22) +,S(3c0159ab,4f27b61e,c2599ad0,a79b4228,9000fcb9,ceadb440,24e1527a,f5ebb9b1,77df26bb,659283a,37abc70f,9f465794,ef3fb178,107ccaa7,a5404c88,a67a12ef) +,S(5b250c4a,25293ab5,34f287a4,9f9f966b,7ebc1c93,e5a774f7,c98980fa,7980e07a,dd6c9c36,5c015685,9e1ec5fd,3a740d1d,7eaea301,2311d1,a9cf505e,7e594613) +,S(20a6dfe3,71696c50,c3a7abf9,3341c41d,9d6d2111,729fe45e,f3ceb49e,977151fb,bfdd0224,fff97fd7,ac3eda1e,ec0e6f02,8bf7a3b8,23cb49d1,52d643e8,fef3d820) +,S(915fe7ad,5840d789,b94d1bd3,2d555340,3c9152f0,340a5f87,6862ca40,19825b79,8d2f0ba,3459615a,2036a07,ca57d0e0,f3a4290b,f5fa7327,2431e3e7,bc44255) +,S(d6aefb63,618e2fd0,34e4f17c,eb766d5b,c27c9a15,d796b16b,7cf8ba71,31a2404,f0abb6a6,e862f6e5,d71b062d,e9bb7b9,dfb2e4be,1c5a85a6,dd3e0c25,3617e8cb) +,S(4c804837,7d65a97e,f65a342b,c4549e16,5cb1c50c,cd1549e4,d2d2ec10,c663882d,e1ec6bee,d0bb7243,a384e6ef,100e5180,4de6ac00,b06d7b67,3e445dc5,7b76dc8c) +,S(3a6df802,f372b56,1ebed30b,a4740d03,d257d256,4931422d,fd3c8c1c,619847f4,d168c60c,9a86bea7,e48e58db,d8e6bdcf,b6ea7d31,d57f8996,70f79e33,241bc439) +,S(423eedec,169fa4aa,52588642,a62ec6d3,8acf2ca1,c8098c24,4a56a6b3,169f9932,4887b475,7f27b4b8,d288c05d,3efc1d98,c0428b5d,e4db1cb0,6dcc4782,10cc6f52) +,S(2864f372,8f8c5ca7,5dc56dad,8b968c71,a008eaab,80b8bf28,16bba436,78465be6,14f5e2c3,423f863,64962871,b786c840,50cc07b9,f537ddfe,cf81b95a,a276b813) +,S(cb96234a,985e2ada,d8130ec5,472efca5,7ed7331d,ae7570c4,dd42f4db,9597ff08,69722bbe,5b367ce,5868b30b,31f67c8c,a5f047ba,a7f65202,48041458,66b88d5a) +,S(3be93dc2,99b7e701,ddf0f239,286ac598,bea1ce42,f1e21ae8,1cdf07a7,66c1123e,8a9c69a6,6a475e40,d81b5d0c,7734cec3,40f528f7,4fa0073c,ee92161e,cbb92d00) +,S(9022c43c,7acf3552,fed52d25,d25dd3f2,9aa61fa6,988a5499,6aa3626b,88b7f497,c514a46,f328c9a5,dc2fd03f,81ce3fda,915f1e8f,127348ff,55e4b5ca,2eba0e53) +,S(a24cade9,47a24abc,a050ef7,f62d138b,85c96d0d,2edac403,dad58ff7,8a2a9d3b,a209613b,c05a7b52,41697e24,73fa7fc,412f8fc5,b2fd0447,a2e20e71,949cbc0a) +,S(cb2010d3,94259307,ee3491c5,58efbaaa,203cef1f,8a0a1e99,67a4ff3e,719495f7,1bc5b6a5,b5acd447,baec221,141b601b,a5a3a640,7f84b8f4,319a760a,6226b0c) +,S(fda8c6d7,c6302b27,76172f01,5e59da8e,4af1e96c,3bd05f2e,a393fb7e,1dc1ecbd,87ce1baa,ef24102e,19c7a17e,b267acee,7681d40d,705f7058,c514c9c1,f615e666) +,S(b459fb13,5b9902e1,760286b,b37ffa03,ceabb618,e684823c,17ba97dd,c95ebdf5,6e35fbaf,b5175d3f,ac60f2db,97447cc4,a79c212d,5496ab7e,4b536ddd,797a19d7) +,S(ec137d8f,23b119eb,a286af6c,583ef011,5d21f335,8a1a2df,ba6af216,d03b4a36,344fd1e4,869d68a0,87a1974a,d48b9704,5db2940c,dec017fd,a0605c11,8e5c7bfa) +,S(8e0be401,9f2d3cb8,9ab07348,cae295c5,eb7fd7aa,fa5a83e9,3d68d994,af68e14f,542fcba8,51510baf,6f962401,e8a607c5,34a32971,22645ebe,2dd51867,b26c80d4) +,S(b1dc8611,3cc18708,338542ac,698a6981,a6192261,7c1f71d4,3b72baeb,1c0dab03,f3e61423,5b683727,f1cca8e8,41de0474,e8c4da8,d2bef431,3cc2da5c,c4ccf38d) +,S(d438b027,7f6c7048,c1eba2c,c1d7e554,888a7025,3f513261,3bccad8f,a5a0a9d5,fc419852,23f6ba14,adda0abc,389d2d16,4a910dab,b464232,298c2a1c,50ae64be) +,S(5484d618,5cdf3f21,42235130,62ddf251,1f53a84f,f0fada86,602c4b13,3dea0f70,6e697180,33860802,6ff41cec,cea79f48,b695aa37,2a76d046,47603a67,ccf9615c) +,S(8c1856e7,6842f5ce,82c09a82,4d343a6b,ac67ff27,f6d32de6,7117dd75,59df362c,70f78515,c4bd17c,23f61049,d6ccded4,100234ba,b7e1b9be,16925635,a9aeea2a) +,S(48052022,94350095,8c904f57,54285e1a,7a17ff81,dde1f50,8586273e,40bd531d,7f1ad451,38b44b15,33327a0b,15fba2a3,f70989e9,1c271b1,461750c1,5044294b) +,S(4adae425,86b0b95c,fc26159c,6aa0b1ee,dbbc936c,83fd6056,f2b730ec,14dc17e1,e6d0f9d0,bfec452e,3f2f9b39,4d73c72b,990f9683,b6004fa3,8e8831b4,f46f9d4a) +,S(d296fd2,926eab53,db70b1d8,d5c999c5,7eb36987,62537954,2074ae12,af5844f0,b4472b2,c33661db,50b4370,e0ae7811,b16646bd,3f4771a3,113d677c,7f1c0cba) +,S(1dbf607f,5d1ea26f,ee8e416a,52c8475e,a8363cde,6bebcbb9,bc04cf4d,cd19cd54,832c9d78,60c8acc4,5d00a568,2d28fe07,4952a897,619f8b23,42dee2b5,e20482d3) +,S(12321adb,c5997830,8689b103,ec1552eb,679a3ae9,70ccfc46,f4adab54,f8dcb2b2,92dd316c,bef2864c,bd5002f5,ba052712,3f920db9,44992ccf,1b2f967c,b70d1f1f) +,S(fa2c024b,76460eb7,a64d1e1e,fe059496,c28372c8,1d00cbec,9ea76f62,546642ee,5a848904,a654469b,dc3eec26,736d7677,ceae5fd6,d6f30adf,8c3fb95a,bd8e5d8f) +,S(a77dcc2b,218b5acc,d6172931,91e8ac19,fa4d74ab,4c88b84,775d2f35,3f51e7c3,de872263,4e2ff356,7d9e419f,26ff51b6,c41fbbf,48124d01,8409a516,9735a950) +,S(83d740a8,52a6139b,53b60796,50d64902,9f9fe35f,87689977,66246b2f,91e038a,1c630f32,722468ea,72bfa970,8a27bd9a,97b68e53,12b7799f,66ede01e,9c75b57) +,S(d894b986,d74580bd,84b24a68,eba87e8,32a9c34d,bafc5717,cdc03c22,8758c9a9,f0d68c3d,284574ea,21a357e8,cad5fecb,90a57b52,62f6ee06,1868d970,458d502e) +,S(67b4473b,3f1420f5,a8fcb39c,8671d583,cc1b9f48,e288f311,2af6241c,4190a786,14fb410e,e33f4a6a,3fe5dd9d,c30d581d,a350c756,6b513290,7f3ce97,29310b55) +,S(24522965,87039934,a6927d0b,782988cc,b126b61e,db154f07,18d97d41,6dc8f0c6,538c4e2,a7e180e2,14e36785,6dedb105,f688f1c5,9bf81d30,d92a8a93,d3c5a82) +,S(b47bdb89,b087a27a,663f9884,7ab44a64,43fc43df,bb61f4d2,bc5b7ff9,234644cc,ee508868,402613fa,1a200032,afacf672,9bb3f7f2,43b2f415,383f4067,d0212483) +,S(92eb31b5,79ac7b0f,d32ac278,4f3d2c63,93c285c9,567bf912,584c00af,3be97d8e,518e379b,ea24a823,e725a3a3,3608e62a,7a40aaf5,318c2c,41afefe8,ec9afe7) +,S(a5e06969,cd69f1b6,c30ab55,1028e1c6,1bef12b4,b2c2d1a2,572c3648,fc739332,378a3b33,3db8d289,667bcd98,9a3cbe9f,f20b08de,4047164c,7f4bf9fe,5d1f93b5) +,S(604823c2,f1ab2bf7,eba63c65,610fe0eb,c958e8c4,7f509c8b,42b79934,7fbd9bc6,29f532aa,3707e872,2169a02c,210fa9f6,100c6b1f,926c73e,a3bd0427,a1844733) +,S(1b4bde22,bfef0cb0,ec9e84fc,e3af2e8d,ce7f381c,e9766b1c,c1d5fd90,9a1bc891,ab20347e,3f4c7ba3,2bb773d1,50868756,69865df1,18297657,6c283704,49451f49) +,S(10caa116,2067d668,5371a20d,97313b54,4d79b61b,3bb41a87,980eac97,e82f58ee,7ee4f118,32d9c2bb,1f849731,d779d12,571a084a,4ecbf4d6,128f33f,6af477e4) +,S(71e7f07,513d42fd,5c0ad06c,c96ce17d,3542a4a1,a101e581,f7394c2f,d89e2072,e214ca42,1033650f,e420e574,e2266c23,3897de25,90dc3999,bee41181,db0a81e4) +,S(fcbe392d,82b4859a,2424ecec,dd8cd47d,bbb89d15,ea2b9591,f5ff1067,e2cbadaf,9b06334f,6ae45d74,44b5f09a,995fd91c,a2712ac8,b159126c,8cc0120c,513f0bcf) +,S(a64d9e9b,5c583eed,aa7934ef,a7ad036,8dc9adc1,9437f97a,7a16b645,cf46e262,9357dd,1aef4b07,a376b468,99692754,9ddfe9d6,7982a7fc,d607f02b,8a7158a2) +,S(e634938c,aaa4010f,f4a1e760,d87a253d,5fd645a2,4f85a75f,8eba78bd,d876fcd7,84be5fa9,c11d8c3f,54f757ca,d9ec4e18,86e52db1,4b3cd0d3,97f666fb,f347d24e) +,S(4640245e,23e6eb75,6e0a44f7,698c1faa,33ea4f87,7a52f850,3477ad4f,8ae2f4bc,ca2a72a4,1348d3f2,6a7fb977,d3106710,2494a982,a96c6514,7fbfcca9,1ed2225b) +,S(c2a175c7,badbfe95,cf7db594,626a42d0,1247ebe5,160c58a0,2437fcd5,eed8c59,94814fe3,7692ad4f,ef0f4fd3,41c63f84,b282324e,faafbc59,38d88f4f,eff210e) +,S(b1296159,d0651519,d76bffdd,98013d1a,bb17de49,111e3035,5f7a1d40,3d8d9148,ae3c442c,2dac0847,bd8d9052,a5470b9c,80536fd9,feb5f820,e4c54ac1,78aa44e) +,S(d30b1976,b0867b57,84d5e619,a5a63516,13980093,5a9458ed,59e4a3bc,ab69b7c8,1b1eee49,690de06d,7a71c334,4fc0a647,133f2fc2,99a8f096,a8a445bb,27f5b96f) +,S(fb1c3b4,5b0f6f0e,35b93a5e,ea157e60,134b00c5,38a6e429,301962f8,38fe51ae,cf79bafa,d7c3fb08,49e1c2cd,48a90ae4,af507bba,5a42f3fe,bb1e101d,7f5fb818) +,S(2bc4d2c1,d34c6d41,9d97ddc0,c758fac9,ff6489fd,786065a3,61ef442a,fe37199f,5b932f15,9500ef4e,a331be88,7022f42f,5148c54b,30957b0,424a168c,c6d76ceb) +,S(2744c1af,3459c35f,d8e1b27,668ac653,eb25f017,e4fa15d,6c1a6bae,e50f3a99,12f95a25,a87e482c,c647b1a9,f22286cf,3297a354,4c7b575e,aa888dd5,6db2844a) +,S(6429f3ff,d37b53d4,88024393,d3a9dabf,f84e3974,ef60f446,61ec9da5,efc736ee,e8426421,f15768f0,caf020ec,3999e8d3,a1aafcb8,24339574,fe119500,a168016a) +,S(33c132ef,437e9dd8,ee9c028a,2ad51c9e,5a836eb5,4bd97e5b,4b5b6bcf,38d006af,e2527602,e5e74d67,5a5115cb,9a89c579,c760445c,c70d95f1,9ac82a1e,6ca16c6d) +,S(3ac856f3,7d53e19f,f6ab49e3,94da6ed2,c8645e65,f4666bfb,163778ce,7cd910ab,ef40efa2,55d7a5bf,99e39055,8993f753,8fd203d6,7fdfb7d0,81cf8bc7,947cafcc) +,S(5ec9648d,b58770e0,314217e,f090c776,396f47e8,ba27c30b,98c35955,aab03247,3d3402e0,8fb71448,6ee97fd9,15293e53,b095fe03,9cc3100f,21240a24,d70819b6) +,S(54955c84,3c5dddd,89f60de3,828fe4f7,1666d807,ff88006f,b46c37bb,8a822645,c8745950,64b3c721,9eda8253,633deb6e,ac9a7825,8d1de2e6,c2dee05c,f167405d) +,S(e0e845f9,35054008,fa44cc4f,1f2e538,33b58e73,bc6a04fd,c16faa76,c1a92d3c,edc48f46,2a41dc1b,bc604e0c,82d993,edc2990d,f59fa034,11702d86,46dd1c1d) +,S(bdb43235,19acce9f,b95f83fa,485940c5,69108af1,376cd09c,884e6419,718ed14e,5669c5ac,84bacd6,eb6fa8e7,39da1196,dde69170,f5d4e5c6,2ad528,66b6d9a5) +,S(d208bcfd,f9754dc6,7e099754,983fb58d,e5679bda,96d8404f,266c0e65,9e4160ee,c602f6f,f0f586f6,7ac771d1,fbf9d807,c2edf68c,a2c10cc2,314221fc,cd3007ee) +,S(3cce9096,450e620f,29b2446,39e8809a,39000315,a887d6e3,da141ed3,32b1c30a,db0ee91,b8447860,18289da1,11b3bb62,289d21f5,2aa8f436,562e982d,bab44be1) +,S(6c351350,d0fa9ae6,e1723848,33e654a3,f3f655de,7530d9bb,59d92462,1dcf8253,2e5ea374,7a368904,e4cc663,feaf6f92,4060dd70,95bab9,ca2e0773,4c48df83) +,S(c64b42da,d63cff5a,ec1eb168,41fead6b,3dbcbd97,f6786efb,5336da16,35950d2f,73706863,cfee031e,3fee4be1,9241cd94,42c24f4c,c57faf70,f8ff8b81,f094ccef) +,S(c4b99a41,7b2277a4,159bf3b4,859e36de,399862ac,70ffb482,b92e1b11,99c748d4,6ae88f86,1ff14b38,b545ec86,1a808c5c,cc78c3e7,91b48f29,350afe20,9efdc33f) +,S(eecf955a,f1ae50dc,44b57715,5ea67ae,1ae39cf3,fd306eee,e943908b,a7a42a56,26c9e114,22cd3db4,b278adb0,8e4c16a2,1a4c0a9f,b837a493,6cbdb70c,65fc5be) +,S(454ef33b,402e3a59,d437925a,b62100d0,974abf69,8803c04,436b4d2d,5a5819c9,4a38e398,1e1d9809,72df7faa,b06bfa96,20fe213a,36ca0227,c7c20ca9,2ef31563) +,S(50f11935,c20dd410,7a2cfb65,2c94e39,8df7d985,c183259a,70f6a04c,6ac5734a,aed27454,75f207b5,dc1e9f4d,f8cf1108,c9507b03,d0e889d8,d33ebfb7,3b10e670) +,S(43bfd366,10b67773,a254a077,624c2b53,4d8ded30,bf53e918,4f10e597,e2edd980,fd0655e2,e6b257ee,a249a5d4,299146d0,e08653cc,cef19a88,5ad6b33b,ae8f345c) +,S(7ad3c8d8,85cacf19,ed6aca96,4607f344,91448d7a,651fda8f,2446686e,bd06a9f3,9cb921c2,3e338436,73fa1bd,101dc4f8,a9a714ad,7fb91057,dd5af779,835000a0) +,S(874346d6,9cfbfc7a,2e13c605,baa69c5b,d39bd69,36841594,1cc50cd3,18c17380,35326549,dcc29eba,4d35a559,e8f2f0ba,b4d94ada,f2c18612,a45c9c90,8d99ef4f) +,S(1001f64a,23afdd92,789390a2,bfde9ae1,d85737ce,3e5087cb,b1bf2d5c,fd0e6943,184b6a2d,ebd086eb,6e04db54,2146059,e6ee341d,1c8854e6,e57345fd,5dd237dc) +,S(659607d2,68695906,fe752434,a229c309,60b33825,a41589d8,6e663230,c1610fe3,77005570,df191869,38a56d95,4643d991,486c70cd,3d5c336e,11a9667f,fd8f7862) +,S(eab041d9,67f89c37,c04bc6f5,a9059d69,a53e5fbf,ef7c17e1,7cef48d7,e346abbf,a2c2fe6e,63e38c06,477d2821,d22b4a0e,4edb0b55,cb1d9fdd,6cd01ebb,d8fa51c9) +,S(4ff2394f,377db94f,e96f51da,d7364407,ed6bc1d9,3822f24,91630c3c,abacda99,4a77b936,a42493f0,d739afce,b769c7ec,cc0d4720,93e839a4,330054db,22e93b31) +,S(7952c74d,e0576ddc,c85f42e6,24937e98,1502b2c0,105b69bd,4e35f59f,1c2847b2,e7159304,dbd9183f,3abde60d,5ad65ab,5a4972ba,19739b65,1d50c056,ebdd5b81) +,S(b3149a8e,f602b6a3,c69a769d,893d561d,c7438a74,bf47c2f8,ba07941d,b0dd214c,99e591f2,2f8da0b0,c9d7024b,c82d1031,cc4336e6,bf1436a6,bf03bc11,3f041979) +,S(ac3bf353,ce672165,a7e736dd,632c7018,1e454200,77471380,7b875d3e,c81ead58,78def098,2f0f5936,5f5c6519,1857463f,7376ec93,f15c01c6,980d352d,17a40195) +,S(9edcef9e,d26ac408,61d3fdc6,66e09068,a9b1e05,cc23202e,1d0b6e03,faf546de,b1387281,71de9aeb,b02b5290,d3d56d63,b6c98c00,cecf8b0b,540f70a3,79556f2b) +,S(fd48622e,8dc91972,7ac21c30,c0fb8d02,d8cfdead,dfc39a22,59853e17,5d8e6d26,4bf26514,204912d6,8bca0ce6,71a967d9,921bb882,33099b76,7a056967,3fd42bcf) +,S(e6dc2ea,c28def67,78b0a351,6ef0f4b,104fc7c9,88a03a46,8cfbf207,ca0ae0e8,d1892329,e77d5770,d9a84e59,c2d6f6e1,db6de419,87c5e3a0,ea508ea4,a11b3762) +,S(e8ff8f65,559128fb,3fc52650,ba063717,cd2d140e,ed3fc4a9,10dccaad,bb0503db,f35c361a,8ea9b3a8,139676c,3ff31cdf,5511535,7859e5f9,3590302d,bd21e21d) +,S(2e18c597,d4e4a4c7,d54bee9,215a1ee3,d149dce3,25cdc4af,f62a3da4,1922c2bd,3e8595ae,4b6cd57b,7f685a75,87ed61a3,281b52ff,d76e9126,4a0f0666,701c5354) +,S(589924a1,2cc867c5,b2289f81,2d93f324,c2df420e,3c948bbc,e74b6bb4,6d30f262,f9bf3d02,cd0001c0,bb9558ff,515ee86f,e39d4357,fd39cc3c,8ce73c75,72850466) +,S(ef55eae6,4a07199f,1a3f3463,5007a8c3,8d39db17,48af39cb,584d4471,d3d32e18,9d31abdf,91bfedda,3c1f02a,aceea672,21d347f3,5d355042,75452c91,5f4b19b5) +,S(7a8f75a9,41182eee,49883295,56588c86,3021548,70ecc05b,31b83a1d,74be05d0,bda4d0ac,ac72ccc0,c07270fd,1c2bba00,4cc9ce5a,fc927f,6d4bc0a9,b2fb4a64) +,S(330f8a4a,582dad9c,159e0f9,a47101b9,223fed7f,896679e4,f764b02b,3d1d9eeb,50695f30,f9dc9c75,6df20cd9,aca3a06d,7199c138,4b3ede52,2dfa0816,31fea2f7) +,S(6dd34c6f,b89722cd,182cd5b6,a0d83132,56814bc,7ff147ae,668618e0,30bceab1,8998216e,8ccfeee2,78e5c0dd,e206d3b,1fe058c3,990159f,be3beb2a,2a97cd60) +,S(66c2d4bc,1e1ee63e,75481e23,1e6df60b,82c73758,36d4c9e9,fa99a576,304ceb8,f6871e84,fcb56780,682bb226,a8d2f46b,89cf516a,7aa0de01,2ccd981a,b739244c) +,S(560ac5cb,b9a55c0c,8bd6a36e,9dc90922,9297a452,b83f8db0,e0899577,cdb8d04d,b6255568,4574e609,ab7dff47,938008da,5b11adaa,480becaf,287f2216,cf1c7891) +,S(a2377a79,e597dffe,224988b1,63ca63c6,67827b81,ccc645ea,cc23ddb2,2a1503e8,7663e301,4dcdbd71,620ff660,426581cb,7e723605,72b651a7,103faf36,480834bd) +,S(fac987,e1c77065,bb1eeec0,fdd92f05,5d10e738,befbb155,152bde06,87c7fadb,c6fd2faf,683f4e87,77a6166b,59c4b93,e7f5d803,fec49ef3,29e1a4ee,9981e803) +,S(81ee9f52,9b5c6b11,8fc6ec23,88f3ecac,4120f8ec,d134d22c,f809e2cb,1e88a490,e8bba906,3a3ba848,7bdf4a82,c5014493,40da0e2b,fda93fc2,b6440b50,d24536d8) +,S(e6439a0d,545c7431,cd8321ea,420dd3d1,9932427d,475df712,529bfd17,99dbc4aa,6c86e739,b6d0f23c,d3570bc1,86fd9e1f,e617fc41,8089f2a9,d42d0b91,2b4c8d39) +,S(e08169e0,f7362908,a0d207f,bf49a44f,5839c76e,7ad80a74,44e7d24b,573f0bfd,948ce18f,15ef6e2a,23f2db47,db402d38,2a4a3cc3,a8a3756a,c382cba4,f608283f) +,S(5687e6f5,7b2bacda,eafe58b5,94bc322e,70c7afe7,884ab4fa,4d46467a,7a8ba729,994d9091,e432b4f6,72e7f711,b5e0a7bc,b07fae10,b6e05ca0,5a088cc6,e4d89d80) +,S(6edfc830,82a178b0,d9160f4c,f0b01004,65c29bea,e3c29d31,e3826aad,9fa1979,5f4c6676,3df8ac3c,4490ec9,fcb0695b,e9c0532f,df526e83,f27ad47b,5bfff2d9) +,S(28128135,a17d0e29,4871831d,2542c2e6,80dcd79f,6b4fafc2,722b7db8,4ed449ef,4f7e515e,b5c7eb7e,415d7b02,83ff11c4,8f3f3ce,a724c476,71d02d1,50835702) +,S(f55371f4,2d617023,dd5542fa,db9a9127,c16ac04,1545db5a,a18cdbb0,cca4cacc,d0c795cc,54057d47,5bd080a1,acbe6881,3263f593,917e012c,dc1bdafb,920b3ec2) +,S(10fffd7d,a62de848,c07b50b9,64e3b31,ee6a13ed,996fe92d,565cc54,fd0b7c1b,92107e28,5319ce07,61e5f212,5724f2eb,fc61498,f838717c,6637c7af,8f0fee23) +,S(a6a73f68,bcb103c0,523c8f28,f14fafd7,5b8d399d,617a68d4,6ad0a1c8,6f12a46c,d18efb60,37f40a9f,6567c993,24f3a7a7,543fbc30,feec0252,87dc862e,b22fa279) +,S(2984cd0d,5c4f1c06,3854d233,6cdfdca6,50c94275,975feb2d,1b0b5bb1,c7dda582,41b0a86,8064dfd6,5c2653b0,27dfcc75,50eb755d,7c1f5c78,b0c13493,acbb4db0) +,S(a701d075,7be9ff89,67c67622,dfa8ff9f,82f7d5d7,814f6c29,c73045d0,c5b16589,ada61af5,62851c60,66501019,70953ba6,fca0d99f,42378b8a,5059ceb,2a0835f) +,S(f07463c9,c37479e8,259a32a,a2e8f283,27cc267b,95aec8c1,1242a092,bf4c47b,1ae9bb16,e538ff69,1e9ecc98,838e112a,a2016657,c7af104c,ee92471c,df6cc136) +,S(fdec5a71,21a17f37,b4c2e93f,56d910a1,788d2da,b3cce509,b3e2f3cc,f141043b,a62e32d9,336fcfd1,4bab6cf3,26ee3f31,4d2ad023,430768ae,8f093408,e3505d90) +,S(be32f9cf,9a13d4d2,652d122c,c0ce07b,da21fc85,52c0808c,23dd5a5a,9ffb3599,a4a73b26,2b5f8239,e8c279b,20f79c71,7ea47a64,1c084f52,f1bd4f3d,dbcf9c43) +,S(735a1f6e,2575ee01,9ebee00f,a4acbcc5,93bb6572,c4db9505,bf96a2f,43b66f38,20b1c7a,99c6cfcd,58b3f00a,3f9d7739,f9cb682d,946b33c8,879f041e,15e198f8) +,S(a0b840b0,6d92eb95,4f029204,1229b9f0,f1838bdb,4ead7432,4d8facd,70f7447c,383fa2d6,47d88c1b,fc9b04a8,a27b3a99,1a22b82f,7c99fce5,5cf30855,ed1c22f7) +,S(3e54abd4,36a9763e,52b7f46f,4955a339,6db284cc,77d0b351,fee7a568,e4774c83,1c64ede4,fb372325,dd435f71,e4dc914,391da9f1,ee08a21e,2eb5ec18,2834b0ab) +,S(24675c97,67f69242,4e1d43e3,1c46ecf,b418fe9b,3fcdb495,822362b,2a8adcb8,9e38e08a,4359dd9a,a9ddf4c0,8fd1147e,c70ccf1f,8855cdbd,49f44dcf,1edcc36d) +,S(16e0176,9f5a5c0d,f6db88e7,fc95419a,96f836f4,f8255a96,77ca2710,2330e317,30641d0b,fbf7a1de,24c72692,b7032355,6996bf3f,1b541dae,d6ccfe8a,f847646e) +,S(a2e30015,a53829d7,c95d5704,3d6dac44,bca932b7,d4115826,c602aae6,8e4ef847,33ecf0cf,e81236c4,e8fb76e9,645e4827,9bfa617,f6d2760f,dde2b110,77da6ce3) +,S(cd9bcbcf,b7338c75,3fe393d2,2e2e44f8,45a09b83,fe4ce29e,546f2a58,7d7422c5,7683e5e7,f990e09a,e15902e6,c57b8db9,e2b1d29e,8d3bf33a,f90291a4,616c794d) +,S(77a00bb9,6abb918d,774a2a4c,3828fa5d,c7d40bfe,aee10091,f5d92cae,cdfca277,f3febab5,3310a2dd,5dd7a5e1,33572b3a,83950172,be7e4cd6,fec7575d,10142e02) +,S(be2f342c,ca1f161,d15f98cb,ccac9f2c,806c6d1b,f34656b5,c671c730,bdae8efa,c9c7a70a,88ed250d,6a7c0307,d3dd907f,ca8b399d,4affce18,e988ce5c,e0efb5f8) +,S(538bc08,c3c775bd,33edee48,c96c6fbc,1435b0c,f00aa42c,6c15fc7c,5540dd6e,9d8b1293,14283325,8d676a92,d479ebb2,c5ca171d,a43fefae,74c7740c,7e956eb9) +,S(d5e26827,fc0d8283,ee020376,56e83ae1,3ee306db,92d0bba1,6b3fd99c,7af15578,b41d4816,b9643a41,c4f8c5f,4121cddd,d7eceeea,9f2156ff,676d6d2b,61b7d210) +,S(7d18f6e9,95f9077f,c371fcce,729835bf,aa903ef6,682404da,bff6949,4b7faac,1423bd83,89410b9,a2cd8c3d,3d13b495,d856b103,eeb96e64,8c258ecb,a77d581f) +,S(fcb3cd95,fb0b4940,bdf0bd4c,ff534d94,26489c72,953b19d0,cedce320,6e3eb556,46043aaa,3d1ce85a,3009911f,763f49bf,df3d7fa6,347f5573,4c6c36cf,97f15702) +,S(a0e8413e,f66527ff,12f6de1d,2ed4eca4,6617064c,afcc4e98,495f2694,34492800,ac640fc,e3d5d094,54ee6811,81f8bc5f,3fb1bac6,84c8c572,b24fe3a8,66234d13) +,S(fbf60da6,aecd3cd,9137b28c,f7bfe541,efe60e65,52470f80,e301884c,1e9cf33f,aab83f4c,9d9e5ed9,ee35be7d,33a02bec,ebca4f40,8f6fafaa,6f385a89,8f7d846b) +,S(771c8d7f,73bf2b9f,4f032e7c,daf17c39,c375ce1d,2e180758,fcffd82b,26fe206c,af22f29,ac2ec5a8,ecb69e2,aa954f49,63d4a58c,2d6769b2,5255e9ae,2689f756) +,S(a30bea5b,4a911ae8,de11a310,a9cd3616,54143337,30aa182e,ba75601,3d12ab80,aad0f1fb,a9a8d6a5,71301b1b,dd11f4bb,161c26e5,3d3d5143,ad2678f8,9960029a) +,S(55d93940,b0a76283,6aac5bb8,c04f490d,e7ad8da0,4cee090a,e1f381e8,5c13fd27,f1c58d73,b90819b,bab9933d,60f54370,7a625449,d637f01d,bfcd6b44,bb467937) +,S(f6b03260,ae0373f5,b2ebaf15,1a74369c,22535844,13ec4356,ea4a9a32,f2132f81,4fd750b3,7f377f97,7b62580b,6ee7176e,a720a434,8df7aa86,642cc6b3,963cfdaa) +,S(80f7aad4,3deccf41,cf4540db,97150bd4,f4665ef7,72bf6c4e,e551c566,d11d13c6,20a9b836,67cbb397,aadb1211,fbc9a78e,2e998354,6f263403,1d844e7,b95766aa) +,S(4f574d76,130e2eac,16bd6b64,85bdd2fc,88855c3c,68a232b0,29560509,463f0c90,33dabadd,62ec0192,9fc3ad15,587150e8,ead8518,327207c8,7ad2a7ec,c712d268) +,S(41b6872f,f47a0d4a,764ab086,764e1844,a8b4c775,cb2bb06f,643a7c13,5a633e6c,301d54fd,d55c9204,4d337b83,ff17dec3,3faf0bf7,ddf7b5c0,afc8934f,65d54e17) +,S(9f4c75f0,cb44460,ad14d416,76c8b60a,1ae979cb,1ff6a506,b5523cee,8acb1bbd,253d8a0e,985fdeba,1ced50c7,aa3724ae,79c75c6d,7c82315e,65f3b579,5fcf70ec) +,S(25db3430,667796c3,b5f3ec0a,cf4f11e4,73518ce9,aec19932,3f0c5adf,e32f4a87,cce88dd2,ec02e348,ba569fec,15a3c527,ba580e44,c32f25cb,fa6f99f2,a1eb4250) +,S(fa71cf1c,2312c228,c706a7a0,2dc3b9e2,f571b468,913b9cb,3fc5ed1a,faf730d,156df61d,752533f7,afee2781,30763eb9,45a6198b,a78a8288,f30e381b,a809eb61) +,S(f5d3adb5,6d77855d,8e0d0b71,300e620c,f96a45f1,5948d97b,affc3ad1,7226294b,95fea642,5be2e987,968f54f8,f3fb30f1,e9a1ba5f,57b68b1,101a5981,a47f9f4c) +,S(907eec9,6db952f0,eb7d0436,c47f5980,dcae53fa,ebabe57c,aaca00e0,10361460,1cefe286,19dfb7f1,969c7618,e0ed4775,e23527df,6a214754,4cd066a4,65f92468) +,S(abbcec4,36e06bc7,bb92dd79,bcc5d9d7,73189ef9,2546dfc4,54b5d624,2c96e8cc,d1546a38,1f335077,ef40473d,baac9c37,66aba23b,8040c4c0,60778d13,8603be7d) +,S(42f1594f,b1e088f7,2020b68f,7fdd9027,b9638619,b0320410,fc4bec4f,4fbdda76,9a417cab,515a08fc,9e7a7c64,7029a457,9881caeb,df8430bd,c1712af1,3c6b02ba) +,S(a4aace90,751aafa1,fe3e14b9,ead1d460,e1cf029d,cc82afb2,4d169248,e63f83f,97ec83af,dbf32c34,df9cafe7,de0b6ee0,67460d5a,607e1ea,fd13a96d,51999d54) +,S(ac7ef2c9,56b41a0d,824999ca,550ddce0,83f2aba4,8f2bce5,644a579,c0e248c4,d97b0ee7,f3a109bc,bd308b3,e4d4ce2e,c1a65878,e7e5939a,76c9d8f3,6959c5f1) +,S(e9cb9aa0,ab09aa1c,18daf08f,bbdd0457,25d4ac95,8607a88e,5bb14c1d,61d826bf,a1cc215a,124e31c2,e426e337,89736101,b739a56,b6d4e139,ce65272e,2e4b9a5b) +,S(885de0dc,e9700b34,9e10dc17,68f9fe12,2672ed3b,ff9d20cd,93b98e38,ebcc67a4,f66a36d3,75272f66,cad46fcc,cc5d3df6,8c023da0,1ebcf766,f4dfbb0,60e1eae) +,S(8ad4ade1,ff9a19a4,fdc89e75,e4cb61c9,1485ebce,b1e4c063,ccb8001a,c5038e6b,e02acfc2,19c22881,d38a8232,fae54d82,e0b1fb77,e6b8b741,890357c5,be261ff0) +,S(5b395388,a62979a2,b931f243,7f3f65ab,292ddad7,728e67c0,e753565e,578fa354,7e77c8a9,7c274030,b22e802f,f0fd69f4,da65e54a,b50953f8,b9945b79,20fb89f5) +,S(271982cf,eb593e95,17a8fa5,6f62fd23,ddb6c7b3,8e5a87e3,712793b6,f10a2d14,6661cb55,543414dc,e02b3650,8660d54f,48086cbe,4b517092,564188d,6815d2ad) +,S(458be91f,a61f477e,47d7b997,931c992,86281efd,21445349,af820000,58b2a93f,48725c06,e7dbe303,f0c73d43,a5a62b6e,131d004,2bae9dd9,ec12cf97,8df04c49) +,S(763f00b,348c1cb9,e922c6dd,a7e72e22,8624fae0,158af602,457fffc4,cb0bf3c1,8119c3e5,b190c6d8,7f3d7066,72136a3b,d0f1b198,d9f304b4,cfb383ec,e075de74) +,S(4fd5e9e2,77fdf260,8ece7268,10343542,312773f3,2518b363,7075fe98,dcf7719b,c234141c,d3e83309,539aa906,2b17ee35,99a3246d,86ad20d6,2885cfaf,5a88162f) +,S(c578afee,a7e492e6,30ca55ff,8923d9d0,9a6d0583,5553c61d,fe871b6d,e1d956b0,bc2d5e2f,add48f2a,50e357cd,4d4c120e,e744fd96,dca0a959,c0bf482e,2e081ce5) +,S(df02c64e,7a60b87f,73b4add8,5ad93b40,bb6a862c,18e0fba4,3f722650,c0439107,d86dc018,525efc69,cc7b282,cd145650,3b9bddae,607e14ef,3e380d53,c266fe29) +,S(9471f488,84cff3a9,2a99dcb9,4cad96f9,d4761570,c0bda860,bf1b0a9c,32a27a4f,23471d9f,6b3905a2,6d9481ec,dfa10a2e,7c7a5405,119dc8db,de839140,2e063e13) +,S(6d1b5784,f58e5393,fec90c50,a674278a,cb841f46,97da9eda,12013447,a2e0b863,d052c806,2905ef3f,6302892f,48b20497,e89cf7d4,3c345ba5,94233cf8,2586a17a) +,S(94eef4c8,be627560,17e13634,53f733a7,fd590dc1,294cefb6,4d8965fb,3d2ed1d7,49a1e5e4,d72e6a21,14e7c4d0,eb9c9240,b3b988d3,21d2999b,46a562e6,1a203c97) +,S(3206fc87,582d1267,92c2a68a,7400a29f,61097fdd,3ecf4cda,fe495993,87ec15f7,c9a7af2b,88df18f4,fe453bf,fc4af355,519fdf21,9251bf6f,879f291c,32a38eb2) +,S(fcd215,45f8a36a,a5a81bd7,1fb1804d,b3fa112c,c21542de,27cd34e7,f7309a1,191c6b9a,42b0adda,bacffaa9,89bbebea,3424a3bf,11345ff5,2af9c3fb,6193ad0) +,S(4432309a,40f6c71f,cb7fbe26,d6390778,fe6f816,3ee50264,2b2027c4,86fee04b,550981ec,b4d2f6df,fd25f26e,6d65f62e,5e549c1d,79639c4c,d07b6282,2d075847) +,S(7cbff748,7bed64ba,7d6346f9,18e23992,a3712ed,1b7cb89d,a6c0540a,335b582a,ddf155a2,bfb25607,ee6bca12,f6df2a01,98513445,9b353c60,9806fbbe,2d9fbdef) +,S(f8cd164b,ad3e65e3,63368645,76c49035,a95f7928,350c855c,fa534f6,80709789,25412c86,23bca2e9,8d5531ee,d57c80c3,ab01abfc,498cbef3,deeff0e,ae6912a) +,S(6fad2644,990a231f,c27bd678,36ff84c0,11fdec01,120dcc0d,f114b032,1d60648d,6beaeb65,ddd28481,a23af85a,5a417323,350d057e,1a574b0f,5d994816,844d9d98) +,S(1fbf086e,d0adb5d1,9c49947f,d019046b,289c5703,1cf876d0,8cba81e3,7739dd6,86e3511,3d4593f7,a8142c45,8e915fcd,685efd0b,f8cca8a0,79ebded3,f4453acc) +,S(30cc2cab,fd0a2244,2c8a6e00,5b6220e5,4061dbb1,4f8ceabb,6215bce,f5f5b312,b5a1f70d,4a05fff9,9716ac44,57a4342e,7d4bdef2,6cd07fee,b515c56b,19fe72b2) +,S(156efdd1,901ebad9,70eb167a,bb99ac5c,676a0929,3feb0b37,8ba19ba3,6c2a3935,b1d39453,32f0e107,15d06df2,d784b1c3,bac221a7,f184bfe2,4824a368,9ff33051) +,S(48682e29,6b8e8123,c9fb6246,5e8480d5,54350734,bc763757,f319d40c,87f2ddfe,efa5e5e2,be832508,e71b15f5,3a7f1d77,773129e2,e57b2b86,9545cfc9,a20360f8) +,S(d188747c,968b67b1,88958f37,b9990bb3,1a54e3d3,e3fe9e06,7aa9b1a0,87f5e3d4,6d60a049,25660c21,81efbbd5,6a436409,6bbdfa39,b37f70e6,4d13f3ca,6bc76b92) +,S(d7cfeac2,7fdb5922,a5f534c2,b1d325a2,903217f2,ff41b6ea,1e5d2264,8095d3,19618c66,87bb0d92,52133c84,26fb8aa6,4fe1355c,86aaea97,b9ef49fb,de063e75) +,S(dd795951,27b0ff86,94dd131c,e85ca6ef,9aa2022d,d0c7f053,4c83735c,43096c02,caeb4cdc,2633b01f,1b51f7e6,103ede8c,a0ccef5d,e33a8ece,5ddfba8f,736e2adc) +,S(5b2bf207,9a319707,52496851,68aad1e9,5582408,7e5d361e,87b77900,d9c63939,728df41e,69c939fb,59803668,6c44d0b6,f102dc4d,ee146a33,42e1c04c,9b6e30cc) +,S(d2a26a5c,313aef0d,6ac190cb,11c84771,abf97d8e,c7e6e67,bd3313fd,15c2ba11,84a1fb57,1507d012,9d98ebf5,34fac803,5de7a43e,48868269,d5e9656a,773fe768) +,S(22c22c99,860b2b6c,630487f2,5263e77,91b62450,290e9df,379efa7,1edadb52,d2b227b3,dc85c1a1,ed64a7e5,6dd6b178,ce0ab5e,68647b50,1178ac78,4b7d1c93) +,S(b811b039,d2cf5f3b,95e38e2e,14da67bc,417cfd8,623593ee,1f2e924f,d9a064c4,a860653d,ca39030e,8c1ed7bf,aeab73f2,a68cb8a0,5263aaf2,431b6f8d,9c862a28) +,S(59ee23c9,26b38d6d,d6c091a1,d4adcdca,b3a5a548,a5cdaf92,fee9fe2b,c3cc2957,67462318,96e4f107,67e39d47,b03268af,5b54edb0,9f9e8e0c,3f3f89a3,e9d204cb) +,S(cc79ede7,ca5a9ba5,a7c5114,d2039d5e,c6a54db8,fee6c102,577a20d9,18ba857a,2cdc980,3a06af30,c19b262a,12f54132,933ec985,afbd1598,45c40be3,2e24861a) +,S(1319c7a6,2f3959b4,cfee1223,b225c6e9,9070753,a1ad33d8,8f3a8b78,a886f7da,d8a75f9d,b8266ea4,2b650265,6b6c0ee9,7f3598d6,3d149ad6,9a5829e2,f3c29085) +,S(e4ae90fd,74e9b229,b9100cef,1da59b80,753b4f0a,cd7790f6,34930a92,b1eea356,370f1c98,f72e698,73c8d0a4,e76fb117,d7e5a9a4,d13521f,f4902a1b,ed3688e5) +,S(286a98b7,b0b0527c,310afd37,717b5061,467be24f,8a0b5528,52ff26ee,d658988b,d10699be,f79e800a,519b964a,163cffb9,722c3ba5,b56694a5,c1a9b883,b32965d8) +,S(9b8544b3,79746ba1,8524e084,2d15b853,46577b81,7084e02f,7d0cb820,7448f5d6,8421c0f2,ba15f648,246f9cb7,3b10d6db,1de7aefb,fafd9b52,16234dab,8e70218e) +,S(81efaf7b,50a18c8c,2481299,bbd2adba,f3510ac4,f871c3c0,f849ef6d,4550681a,ea8567a2,9c871ba7,fccea923,fe07e8ba,2071d7db,25d33ce7,a6125c0c,2afacd17) +,S(6680fbdb,dab850c9,115d5765,344f1466,b88cc5dc,8de9fab,549f281e,974ed7b,c7871b4b,2690f4ad,1e67ea81,e2cefac2,988082d8,b1c70e67,4389c79a,bc880368) +,S(4c38ce7b,a8e3d317,6f615d6e,27578edc,746fb80e,4701efae,f7557469,a1dce4bc,f6e73d73,61ff5407,10dc9f1a,4bd5c1eb,a7d8c8d6,45f0427e,a4fc3e45,3990b88c) +,S(767146d6,bc1d5bb9,c73321b5,4cf6bc61,e8209703,fe4dddf7,2b3d6089,da60be69,86df7dc3,6894f88e,fe24a4ee,fc78b1e0,fbb5138d,31666be7,97b8934f,d0b9a37) +,S(bffd4856,360c02ac,ee741a65,486d5a7,44e2a005,9db5ef5f,f87b9e18,3adbed20,c11246aa,3bd4ec3f,71f88d2,cb3b96ba,2c210f90,d8234c21,99bf7781,f96ad781) +,S(ad19b5f7,45556650,eb496450,968d056a,fbcb55b6,8852f510,ac96df54,cdc677c0,f78295f8,59445c52,d6ea285,3fe9baa0,5e453bf0,9b3f4d28,6c99e858,62a04bbe) +,S(5924f679,bb94f6db,d8232b01,ca8dfbcb,37a83d24,d0e7026f,b526162d,c82aaac7,baee6567,6a526460,ec7563ad,42482d37,a42f2fcb,f4a5eb7,b37b515,a6c3f1b9) +,S(dee240ea,c90d5399,89d3b3f5,764e0680,1e601a4d,f2c09226,21eb0b7f,bb53ccc6,12668b,72cc139f,5f9fff4c,ef24b5ec,d0648e03,64c17417,f35bfca8,b819316a) +,S(e288a00b,e18b0987,9765940e,caf8972b,1a09f67b,fdc455c8,8e3b4ed9,dc0dad81,fd81e0a5,adf1df8f,42c64825,6b107d44,887d01da,f4cf89c9,11529124,16a1e4e0) +,S(e747d6c7,3f5366ed,241501a0,ca6dc04a,aca7241d,8effbd94,7d3e6a0d,bb6eadb,9954822b,15f44840,9c9e73d,97722194,1eb6a218,ef1ef4f,269ab118,8f9c773c) +,S(57b8865b,ac78b9a1,c244452e,8057be3b,4fe2c6de,9ca9a295,ded04377,2b61aefa,d0e46172,8828f0d3,f1fa808b,1dea8516,b0c30d93,4acc8c90,6ca2bcad,303f06b7) +,S(abda4b38,47ed5cee,7ace4e8e,e652336f,26a8f4bf,4bd610f6,e87e5936,e4e8339b,8860963b,8f266c30,9c6a00c6,cdb44048,a9a1b76,d0a5a652,b4453f7d,330bdd9f) +,S(7f05f2d9,5621d06a,d344bf91,7fa8b9c4,2ceafa3d,fabd0620,bf7086fd,9539e089,298ec6d,8d5a9506,8d916678,dc8e62b3,7b6fb9ef,7967da8a,8419433f,79922f29) +,S(99e4f06b,b8845bb2,12638573,63f7cfbf,3f15570,44256dab,21b5a8fb,c43e2453,5d76066a,86d5c498,323af928,2c3bf419,acb758c9,7883d236,7d9d9e3f,eb8e1417) +,S(ff994b1f,6447ae81,8bbbf97b,8b6d655d,900b04ad,eec06fc1,f57eab70,95375094,6a63980d,3a9324a1,9339a073,977130de,d0fddc50,5b86ff73,464dae04,c3004a68) +,S(3e9af52,f00d1993,8b6205dc,7b6f8ef3,31d624da,fdb2f09d,fd701b78,ed8b0471,a674914c,ac86037b,f0585de4,d867424a,ab9841dc,83c0ca8f,347e16dc,1407167f) +,S(eece935a,e5cd4afe,baca6998,63c5e8c2,e0b338a3,e2ae7dbf,e34102f6,f1c613f8,be70c210,b925dc66,c8546e65,ae9600b4,21a9f668,c3a09a01,8bc5b88b,91cf4d74) +,S(bf10e625,7d336eb4,a962bd97,4cbd9f10,76787b8b,90575250,7f0cfc2e,121d1276,1d93f629,8ddbce2f,1f670326,265c4ba0,f7f5256b,472412f7,a24f999e,f4e1c64c) +,S(73e43284,c80d4ea5,c992ffe3,cdce4149,d490843e,bbac444b,f47ed5a7,3d6e0bb4,82cab83b,c7d2d2bc,87881618,54034d7b,dab132b0,6340b316,964854b7,f2ee3e6b) +,S(5410340,b614b676,63173725,ecf0b8e1,67c2de61,ce999639,be2051dd,2695f8b8,f191ec95,9d6fd8f4,eca243b7,8d844214,976d97e0,65218487,3366ca25,f2e6a091) +,S(203d28e2,694df7a5,6c9b9d42,907c1e02,192e8030,77ead781,8198b2a9,69b127fe,c06fb7ff,2361ac16,a884f0a7,9e4f5a19,b0b5b207,9901e634,be237643,ba93852c) +,S(f9a1e3dd,f53e41b8,84f7c4e2,c2b47131,8d14705a,4e7362df,3221222e,6b2c1921,cd150a1a,abe8ea0d,853245eb,509c1d4f,e26c54b6,98cf9aad,26849a7d,e63b6264) +,S(e82e5e89,db35aedd,4d63b7e,4d55d637,cd2eafcf,f16ffbd5,b0fd1444,c7c783bb,6701a95d,ccae2272,2a834957,e0a4a3b8,3cf5ff19,2f41e8,1619dc46,b8238cda) +,S(9fb768b7,16032fd7,f21315b7,79776882,767d5d64,c9141267,a0ddedb1,55d4ae08,13474a8f,90fda6a3,43e3ea23,e858327,dc2f4c40,4a445f77,4b39b92d,a312574a) +,S(d0793463,ca7cdb50,3622f1cf,90032794,52af3fa8,1866e536,159fd23e,ba6082d7,35301f6b,66858d6a,3e97480b,ec3d2a53,8dee8465,ff5c15a9,1144ede,a208bd0c) +,S(cf8fc191,273383df,f1d703f7,bb237ea6,cb5d1307,8ce8ae62,67fd20da,4d845e05,efce641,3bb676e5,5c0b64d7,589ad771,23900e0d,60feb71f,93270390,ec66ecc1) +,S(f12aeec7,55a7138e,4e91024a,3006db3d,eff6ec84,ffa73d96,da21ef00,452064fd,f0f0660a,22cd7cda,25206f10,e82ca005,3a40d539,472cf41c,b86ed8ae,3d29abd9) +,S(3c40f94d,ab460ccf,c8e47a1e,d015b074,ebfb6142,fbbf5b4d,e77aca70,be67c57a,998efa4e,26df60a6,f1a466f4,c27b2dce,ce6367b2,e05a5ad9,dd71ed8b,482ce55c) +,S(f2fc6db0,49861d3c,a01a6778,22299264,88184967,3802ed49,503857d2,2860f00c,79f0f245,1cc6c5b8,d36c330a,ef8c9c84,95619911,1d4a01a3,d9363382,8b609576) +,S(35d3e3c0,29fdc9e3,88da1cf3,d52df6bd,9ac0ec9e,e591adde,26d3e414,f4fb92f5,62ba2346,6d19ec1b,64d2a6cc,9822feb8,e03df178,3f526a9a,13d10fa7,72de3219) +,S(5628da0b,cc2c0583,902a56ae,711a2217,455bba47,c7222dfe,d60580c6,461b4ae3,1cee8f17,8ce4c7f9,8cf2a2c5,92bf39aa,57616913,bb66f232,a813904e,7b574ed9) +,S(7ec54044,c3fe53ac,8437a540,968e0b,b725e794,a8c240c6,5dac1f12,4ce21be3,f6290891,af9af62d,5dfe10b,d567af1d,6b4b22f2,8177a14b,44edcd0a,be811c0d) +,S(eef7a7c2,a0224a78,aff7c61b,d2379259,7b307a20,bf1c253d,6f03d57f,a8a84203,5c7d6875,905e26dd,2fa4e5,24f73b00,6f4bd0b6,786639af,5d222411,bf69daf0) +,S(3f1e6459,38e4285d,ed30be98,1d149f68,24b264c7,df920f0c,c5d776df,83228cc3,a6a5df53,856be619,25e42d58,2f705567,39c3d7c7,a6e222b8,6385b63e,d1316916) +,S(c83ff186,ae3f42d5,a2c39b43,7712d607,92b9006a,d8bbcc89,97bb9b35,d1663cc6,2380aa52,44674eae,3d96d65,f484e7a7,62cbfa10,96c5a313,9eb4628e,77b72d40) +,S(41d20761,160125ae,c669b1cd,395110bc,94e09a,ffb52ef4,e7e86e,1f4a61a5,c8509f71,2c20aeef,7399d11e,7c438d0e,9b4dec72,76c1013e,a4c76d31,ee5c7496) +,S(cd358f38,d2cb11f2,9fb54431,dc5adee6,721c444a,8fab5743,1149e504,8ba0dab8,e6ab91a8,79a51f40,cf75e405,e508cad8,ec54dbd8,2dd4115c,fda7b255,b1a6fe1b) +,S(d1e1851d,cc95a94c,a8c77b42,5b4430a1,ac10e526,e3f4c6dc,f20267dd,7c5f2d2f,889de789,43c6674e,6bf28470,3ef8867c,8d827681,d2df8bd7,75384ad7,15730ecb) +,S(89268637,1febc520,14709cc6,b2aeb5dc,32e06b14,cdae3d76,b1bc04e7,b9b5dc8e,46823e42,9c0815e,ff99cd7d,f3712f78,9c102af,173b61f,8de08519,f19a3783) +,S(c7341942,420d4e63,263a144c,8a10d22a,fecb1bef,64f926a7,83951e0f,6490caee,925019a5,212e9de7,2e6e2336,c3e2aa75,9511396c,a171412b,53c8f9ff,3743dcd2) +,S(fdede5f,38d83f9f,a4ba3218,fac6f0aa,7c9f464e,33dc59f9,8a3444e,fc0ed7e7,26e4cd3f,3dcd6a6a,211bc8d0,65564250,49c6db2e,9946dcaf,43cb42f3,b0f20449) +,S(b2b5364a,eaedfe4c,f046193a,6777ec1a,1841436e,3cb17c34,9d27f42e,8e536618,32b886d0,f0f94a7f,1b1d87e7,4f2c7dcb,ba4a5dde,8e93c864,f8f48bb4,50d08ffa) +,S(4c3693f4,52a95b79,85e39982,f316897,93d289cf,66b3326f,3e1cc77e,457f64bc,48d7bdfe,fb8c3abb,584db0b,d3407ed3,3e543715,d4ede995,df0e1b8,52e12123) +,S(5cea23d4,927f0385,57f6bc1f,b926e594,93250b3d,6d934a16,9265ae08,24a35e60,5806886e,b01ca724,8434f2f8,be33287b,d9cb3c3e,b927bc4b,4fefbd51,ba98b5b6) +,S(db0a50f6,b4558c10,61a3ff88,1da60f9a,77b42469,a9713c8f,8339b8a7,b903d93,7afa6cd2,57685f49,9635d15c,bb7defac,d6f94c17,aa19be02,4e2efc70,d84c45be) +,S(b0029adb,25f32b75,8023ced1,632de91b,3e3e55a6,1abb56d5,aada032,f2c01ab5,baadabe2,2ebc5fc6,d5dee65b,262e96c,63c99fc9,13277d84,9ec8a730,e1bbfe91) +,S(c336b077,3ce16975,bc204169,8126a40e,f8cad886,6a52c50,c5081ba7,b878af5,6f46f5e7,b82aec01,ad499340,fb90b81,420b4a3f,977579af,d3eb0ce9,c7d752bb) +,S(f403fde4,7232a9db,9efda1c0,5df49636,2137f5c,aacdfc93,1346dacb,d2501451,ba30815c,197de8f2,258c83d8,dd7ed0cc,cbb5388a,415b8629,2cf5c271,2e75bcbf) +,S(85cd71a2,20beec1,3edd0133,6b0b85e2,da056a1c,4a48a328,f371af1a,ee502c30,d6accbba,491c915e,88474b92,bf23c740,2648085f,88a62d51,b26954b3,f6d2d4c6) +,S(324d65,b3c2bc55,d3b2a125,1b673ecc,d26d2b23,e333849b,7549339f,7f0d9a42,83750e95,32086164,e9aaec58,b6a2f00a,3838d76a,208d6453,14042334,b4af616a) +,S(322033a5,c90d7a31,9439c368,b1301f1b,d088974c,91560063,793b6f2,63b401f3,a61f4714,488bd2bc,84bdb122,c615ac94,a7d05157,e6d961df,4f873f5a,582e4606) +,S(a414ed01,691f2a43,b18dbda2,2d3d009f,f94f8b02,672e49f0,58dc2323,b71f5836,2fa3e483,a12ba874,12d709a9,47cdf22,6b96523a,ac7f4afe,c93f98be,f97ec01f) +,S(774310b1,7752d7bc,d8b3059,f7f4cac9,e661a841,9dcb20fe,355bc7be,3a4c65e9,b3333227,469ec87f,19e14e3c,98148b4b,45bde0f6,698829e9,3bbc9313,56e18b12) +,S(fbb23c7b,5c15bbcc,7ad0118b,6826e385,3f6c4078,5342ea57,11109f8a,27dd11e,b12ec70c,9b4c908e,2538f4c6,b0b3d548,ee199f9a,1e9b6535,f1c78081,41664ad5) +,S(4bf836d7,fa7308c,89b075df,1f336fc8,af3c6ce0,64e1253f,853f623d,c483b400,8b368da,424e5a83,48cfe987,93d47a1e,1e9bbeb7,3f5282af,82e12e4a,c7eac850) +,S(91b4a84a,e521232d,71e3d202,49dd1b3b,d134509f,e3e1ed1a,4e8b5062,a438bdd2,52d30545,4972471e,6900afbe,2471e1c9,16c82ce2,2f955ab8,454f9b65,b70ed759) +,S(9751092f,62147caf,f3953cf4,8a117082,37a78b6,5bf0de56,848da1e9,4c3c3a42,57fd0a15,fe069db5,5fbe870c,96d4280,7b9ebd87,33908bd7,49ba0121,a900b08e) +,S(54439284,6f6d46c9,8ac10a58,ac3c34b7,3cc5569f,8f9fd7f3,4e7ad2be,826bf293,1aff0b85,e5b9033d,7c44e877,57513317,a17734d1,13043cd0,780da032,2dc3ed03) +,S(172704b7,fd7f5560,f17cb6ff,ff9a0144,9a05a113,29b86df0,a8d3256,ce139a9f,a416c0af,b170cdfe,169f894a,b10aa97f,2c068a4f,d70a6878,ec9f359f,e8323bea) +,S(2236799a,a82d4b64,5f412aef,eae944b5,e28e0996,8a243ce5,b8986861,6f735dfb,7f20b2cd,af70cbdc,6aeec02a,dae1535d,4bd919c2,631ed69b,9913771c,2f482c1) +,S(6a52af7f,c6f75636,c33047b8,9f0eec31,66bd97d,8df6579d,2e2a441,6ec4fe8b,4eb0813d,220849bc,e0b948e9,a6efcafd,339f8861,eacb9885,40ff8e1,acf1efdb) +,S(8696e0b4,ae3f0dea,bf6d8dd3,854faa5d,d07b01a8,86f710ad,9b4cd84a,5d61a617,67633be1,a662024f,77931f06,38884175,d2c234cd,c4709637,4711c79f,42a897df) +,S(6c4b727e,3bf9af50,fcae1ed6,163ecb00,142d9a53,b6d3f54c,8aa14064,1358ce7e,c4f2665f,395af262,b30561d1,48bb0f4a,f5603102,80f57f40,1506ec37,2bde79a3) +,S(a51d2296,98aa29a9,6d0e5510,f3e815f9,cfc6d492,dd0b2fd5,bbfdefa4,984a6934,861f0848,7658d4a0,141e9d38,aa0e268f,40bad7c9,e2615a37,7a4b974a,6828d525) +,S(1afc4051,cfb5635f,9c2a8477,7dee5fd9,fc4d40a8,3ebe2f89,f36c0b9d,8327288,61ac4eb9,16567e13,d1b03ae7,ad5c3d8d,ef85bf6f,387c511e,bba6c237,96e95f7a) +,S(f8fcde05,5a2a92de,c65d1842,6607a939,b3475abe,2437c5d3,88387fd4,afdff590,4cb8f7e2,825625ba,e3f179bd,4ef94560,6e186346,61edbf48,8ce51ea8,ce1b0404) +,S(cb3dad82,7e7464cb,5fed0cbf,7e29e6ec,112ea411,706d10e5,c1fe6fbc,a04a3e9,a33abaa7,6c9afe3a,a8ce5add,7a2a056,b396cdda,2a002161,3de97556,a0364629) +,S(39d2cea0,acf8059b,a6275a08,ec3a3664,f09cfc43,f143c317,bed8d751,33d4ba0d,6beb4ca3,d3009ac8,881d6d15,e0a29eda,6738befe,e31707d7,f95eacc5,fbd77192) +,S(90bfc4da,3be2d74b,ff9b5539,d0787bf4,4a1b6d7c,2baa14a1,dc6801d4,42187f96,1254e7ee,c8acb35c,fa0695c5,85f61b69,def3ef5a,6219fa97,e3b09dbb,351152f) +,S(50772bcd,454ae992,f97ac3f8,9d8193d5,ed50b69a,b2e62e04,d6a115a6,6e71c977,f199a8f6,f78b0b97,cc20316f,66177383,64338fda,3eee7f0,f36eb0ff,64accdcc) +,S(afe51284,f033bb43,ac6feaea,9520e83e,f2cc1e42,9e273bc3,1b087161,52ea6926,77ecc481,9ffe6efd,33592907,17d22ceb,f4344c9d,f5f5ea4e,4d443d17,25c8aa1e) +,S(b0caf67c,151b838e,4ecaee09,c7908c95,bd3ef86c,797a5a8d,71cb3bf0,3a46ab17,10ae756,7bd2f6d6,712c448b,34636eea,2c7c3d99,98901308,14825b4e,423284bb) +,S(f2766b4,8cac8f6a,80a7cf39,81725834,d186e88d,735fc7f1,5135ce58,43b3dc8c,63590fcc,a4e21c39,50cd78a0,569c0d2e,959254e1,4bf2ff22,8d8fb216,b53601bb) +,S(80d50e7d,28269280,f8ce0dd3,6afb9f43,38df0403,d5661c99,27ff817a,46f60a0e,22fec5b9,7472824a,9b2ff25a,68d947a5,a8952ae,b763f37f,70228978,514cd22) +,S(18114ad0,7097b5f3,7d788165,e45ca1b8,462da0b5,78e5d37b,e4be3632,35d2fff6,87c5f261,39cb366f,25fb6bfa,fcc8408b,f4c69c81,29df3cfe,9190213f,8c15a66f) +,S(3bd52737,68294cd8,73a48831,8f958e45,85871165,c82cf6a3,9126e378,43975444,8c92d391,e39a50f3,4388510f,c8dbb686,27f29251,92ae8edb,b2613948,c01281ac) +,S(acf47eeb,526208ba,8a3de26e,481a86ac,f17f5f88,81d89d43,ab8a5a26,e00db5a5,b451b4cc,8be8339e,4203c913,98b9a29e,de86a91d,3e347688,53442446,429ace0a) +,S(5e39f375,87a07536,86bcd792,1183c424,20159044,a9f66ecc,2bdd552c,62ec35c2,b7131328,297c6995,7df6783b,d9bc550d,5c553e1,30bfc3e,fffedbd7,5e2d003b) +,S(315b67c3,d980796f,6f0a5505,ff2d25f2,8551615d,12bd73de,b10c4897,580371ba,dfaf812f,7525e087,2d11e10d,612004cb,3e2a7024,8c40416e,5020ef66,82d3bc1f) +,S(8f6764f7,73810396,62537cc5,aed6a8e0,318654cd,a95d8695,89fa471a,edd0d7a0,b91cf5db,2994d207,135c1dbf,b9d9d78f,7e3adea5,8c176630,c09140b7,f5c67e84) +,S(e844b948,118fc6a7,eb2f96e,ece3cab6,def55940,75c7b7af,fcc5603,f2012f96,707668d8,89db03d1,561596da,a831aa92,771a0fb9,e1b21273,d11a3415,82292314) +,S(fbbedfd8,4144785e,fa71e234,59973cb8,87deda88,971eb62a,3007f350,1fd0ae4e,51d23941,cf84f121,ff1e6a5d,651d9528,6ef1d803,819ce64,3aba8d16,67ba7dd9) +,S(a0190399,c692b9a,db172ba8,348b404f,19442b60,4df60142,f6330f36,ca6bf352,6ce7491a,b5329042,25556069,71502da1,601e3a04,f38601d0,80b31654,20fb527f) +,S(d240e135,4b9e6dc9,110a6b25,afcf35e0,74fb02fa,f3dbe484,4f46818a,89bca65a,d704be81,e2597460,6014ffcc,ef12563b,45faf24,3e070727,64f341b8,7dfcdb7d) +,S(39bd84aa,f8a15b0b,809888f,133871f3,c40ea5f,2825582,a35fa9a7,808e5e87,94ad6588,658752fc,ce45ea1b,752ecd02,2b621c5a,26908cf8,69c44584,1d831d33) +,S(2172be6e,dccfd021,a56a553e,53f814c7,c7cdd55f,d181d671,4ade45e7,72093142,b21439df,f7229e61,cd3b7c77,fa750a94,660f60a7,53da96ba,d8ca7e20,69abf76d) +,S(57db209d,a40f30ae,fcca7838,2ba525e2,231343b9,250a1b1f,67fad7f4,d4079594,34ce9bac,bd35557e,2f73c718,4287b493,c524a3a,d6b403f4,ae777026,42fd37e3) +,S(845c2bc3,3683d899,8b90a2d5,ff299064,5214b272,a056ed93,63621406,d3a4cc11,44a46738,839b2e3e,1b6b19bc,56a1ff06,7754126b,20654e51,3a1f13c6,de4c6535) +,S(705af30b,251cc64a,26edc929,e3e9ff3e,32ada2e,69c2cfd0,eefce4cb,a37329d1,c0e693eb,94c0c1f6,cf1a0ed4,8c9b13ae,faba8ebd,926c4fe3,ec9071be,2db463dc) +,S(9b2a64df,9b1f8e,34844e3d,920b1af3,14df1b7,78b863d6,f1a6b94a,869061be,5ff03134,722faad1,ab6f87f0,bc3c6abe,8406bdf3,6506b662,6d066ce1,fa6a7cf7) +,S(494967f0,1a0c07df,e7d9a8b3,e6a4ad13,893a894c,2619238,7384e38a,d514cff3,11ba0803,72bc4ffe,b86e25c8,b69ea450,5f6e3b24,504b5e38,210beff8,5d82e1af) +,S(fd547247,812e4233,55c4a977,4dfecfae,812ef2f8,f3fd832d,a3af2ac9,257d50a4,65ed23c4,d776baf4,53faf3d,d3edf65a,5d9935ef,9e22fef2,53319e22,ca4bf51) +,S(4aa16ea2,68ae95b3,2f405fd9,6fa78478,7c369015,4c124733,a07c1953,874b6246,4306725f,579fc013,2cb8c640,7361c7c4,685a2a49,532fa14a,36010779,7f5104e7) +,S(de6fdd33,8b6eaad1,bec84d55,992a5b74,aaca294a,5f804015,796d69c2,cfe67add,e58564e9,d2cc10e7,184d65b1,7e0271a7,f2e59073,bdca05f,ae664f82,7e6fba13) +,S(c8290adf,d0da5f1d,1805f36f,288a8709,eae0b93b,1a7490da,5ffc29fa,bf834592,ff161787,f096e022,f513afc4,e0b84d73,e7f9f347,798e1026,931271b0,8f1c8a87) +,S(1b95c512,815603a6,19750d6e,463d5a33,55f09c22,56484ae9,8f1c76c1,10ed8fec,93b3750c,3a1272d8,bc9e4c52,b5efb6bc,d794c2c3,939bbea6,1848c9ed,423bb39b) +,S(ac0d9939,104bbaf4,d2eb2560,356f2bd4,f1037ee8,397f0703,b7ebfb49,463a22e3,c6e6d039,d6acc470,c1ee1013,722c4a00,4c59e472,11da3025,49351216,f0bb8985) +,S(d8c8f5b6,366c1d29,b2f28f53,8645b17c,f001ae59,71a19f05,fb6ebb3e,9e36faec,5db24ba,6d78e76b,42262b65,e631390c,8540b72a,746cc5b,edc1b681,8b1a113b) +,S(d42ccf6e,6d1c663a,3dd95fd3,c016d535,c50e394,fd63b353,2949716c,49315737,e7337f22,2d353eda,651087c5,13cfea43,96e9cc5e,22aefb4a,5fb3ea8b,579c7298) +,S(53417fa2,4871adb7,6d875c05,8c46065,681f6372,95d221db,ea76d88d,f687b817,d014e6ea,e00c4ec8,ad931dd2,5cf569d2,efb3068f,7aaf5ded,d4978546,1bb31d76) +,S(202e9476,53b677a8,d7aca305,c52af8b9,d33b1ec4,c5ec94fc,5f7775aa,c478f150,539a24fb,211da532,b378f3e5,cdd688b5,c57343c8,2e03a637,39544c8b,34e3087f) +,S(434f1027,72aa0555,8bcb2f6b,1f64bc8d,72bf1bb2,8b972c3a,f6f342d5,f1493f93,a9db9031,f2d1b8b0,e5840fc2,b4534970,90aee2af,5bbd83d2,c14a5ad8,acbf8c14) +,S(a0e54d1a,f09c47f7,cf56d3a5,725d6777,9fdf920d,3c1dd566,caf71666,de7d5d17,87f5db7f,197185b4,73cfc293,7b0661ce,1ea92099,fbb51151,63c2b26d,47ba97f0) +,S(2d43cada,5455b682,f6430cbd,96a2e316,ac0b4b27,e9fb8777,a94dd2c5,81c0e350,bcf18180,1c1722db,1bd9b8f1,8a1989c0,fb372acf,fecbf9f2,c37ec4b3,b459c916) +,S(e5e6dd12,6340f04,431552e9,903ff4a3,c309ea1e,c498e483,e19b9cc1,37418d4d,58d35fe2,24ab47d,93cd53,fba32252,4a3ea9b6,d96390df,25935a1b,dd12126e) +,S(b112e4d3,ff9adc6f,706cac35,195843cf,302324e5,6aa7305,9c8b5f3a,6dd7dbc7,4fe76714,6529e6f6,b0379366,334595be,efcc7cde,d3596ed8,83e3b30e,780dbccc) +,S(d26b5261,ca7be739,95de1bd4,d75991ef,4d357fa2,5c8d513a,e209ac6f,64ad960,5f3b8e07,74e78bb7,5c395bcc,8512f2c0,1b6c451b,5c8d6ff4,18f9ef35,30c6d046) +,S(e2e4f568,eaf2807e,cf43ce08,3146e961,92271bb8,d6d9b24c,e7315b00,1cf04fbd,56894a02,58093a34,22f2ad5e,548158fa,c55ce47e,d00ede05,40434424,4e204b93) +,S(a62080a4,2d8aa307,32255b2b,a239d9fc,19729eec,1dc24bca,67652705,9b1182cb,ae848a12,893c34f2,260c33c8,ce2bcb7,14fb1438,8e6f3f06,d9192549,6a9ec13e) +,S(684c1647,7771de55,ddea8aec,290c1e25,65120fcb,fc8ce0db,4316dde3,f5ae8d91,ba85dda9,44dcfdf8,cfca86dc,ba902675,3d99e751,5719e579,64e0fa0c,3be1f06) +,S(3d730e7a,afa97950,bee35934,da18648c,d0bd62e6,9a49281c,b63589d0,5efbd2fd,c28545f2,3dceca1a,ad0a9c77,23b61519,44a3c984,d57517a7,cf6702ce,f4dbb630) +,S(337c9584,b49a49d9,29b1212d,dee7ecb6,39f007b8,9ba94180,80c9548,459c48b7,b26db317,3e996893,fe1e0797,f75cd05a,2eb23ddf,4806407e,1f173d29,48bb0bde) +,S(7013edd6,f49ba92f,9c9757d1,d3374229,706078df,c2e9c64c,c2e84cbe,85c6b5b7,e850419,7975353b,2863b577,d6f0c392,a8c5bdb4,2dd557e9,8e971114,5e078de4) +,S(9877cf36,bde592fb,5f4a4581,2bc3c39c,68ac9956,23b3487a,635d6e45,89e26e1c,6207d384,737a0d80,fa9f42e0,57684584,efe1fa4,5cd73eb7,bd50abc3,2722f159) +,S(6d35bf57,1c91a2ca,1765ed62,f9b2f680,cbd0955,bc76332c,c83a7378,4bf3eafd,47a589b5,15a88d78,6f94f64d,674182e9,7dd12450,ab3ec375,a9a31d8a,375e5f7d) +,S(40ebcc0f,be3a2a59,968bf6cb,4d638873,5468b532,80a9c96b,639dede3,171db2b8,c2181816,ab9f0d72,c5cb167c,5bbf7e57,9f17d7f4,484996e6,3a23e0dd,5f5d3a8f) +,S(90de4b6c,3a5ccf14,cb033478,577461af,8c347731,9857ff40,79e6ef27,5e557b87,c534bd97,a4af903b,f307c1ce,549a9976,70edb69b,c76ae827,f277ffe8,133cf394) +,S(54e89b59,b73c76b6,cac52433,305c41b6,8f50df66,9b99f47f,599504ee,93ddb110,d307e828,1e89cba4,86ac125c,1c5d8bc,943394ee,222b4b06,f4e3e5c7,b5da176b) +,S(f2ef67ad,60858ac9,fb03ad25,f75f85e2,e5ac151d,95c96967,93ca080,58756f2d,92c0dfa9,372e1e7b,7363f229,2776c059,56742f60,7b100be3,56dffefb,bcda8cf0) +,S(65b7d935,a71dcb6d,eac70b64,91b9fc50,35c63f21,655a856b,afd00a33,52f5be39,b0773657,cbdd3813,8f50813c,558ddbe0,3f8ad81d,e420401f,175bade7,1429927d) +,S(6e950ff,6471cf66,72ea542f,cf05aea8,2c2e1e0a,c2247021,4c78895e,e291ee22,c0ca0886,f0ac828e,21a5c60,776494fc,a6f19e71,741de92e,c11323e7,e874788) +,S(c3b15f8f,96c0079b,471896ac,89d7d11,8ea1607f,18a20627,fb18e1ff,24ee3c1b,33a033c6,68e3cff6,414835a6,ae3f89f6,643326cc,7e149f56,99ab4fbe,d5f506b9) +,S(9e333d9e,1c022776,da901c19,e6936c3c,1af45491,a93e9405,ae343c54,775f36b4,c6734e91,3d013610,6c77a1d3,ced1babb,a6c6723e,316c3fd,34a7911f,cef616d7) +,S(398d2109,4c0baf8a,ab63ceb,6aa6b64c,678bbe86,f7e01b3b,8c0ff291,450244bd,f1ffb185,112ee219,24a1702,183f9075,471611e9,54fafb4e,36f6acc8,4ac7acf7) +,S(8f99c7d,553ca4ea,b160a07b,6d457058,90634086,80c709c,530aef44,bce43c8a,52a15e23,2e90d233,211d707d,5849e214,1f56e8d,189783ab,3c09f37d,b86a776c) +,S(8d798338,b2774399,487fe227,e2d883c8,39698301,5f01bf00,d154f4f9,296512a9,7cb53de9,9303a6be,9aee01eb,3a5c115d,eff0e18b,2cb5e852,75e9b7ff,53e35666) +,S(c9d6aa,ff6b648d,69416bfc,6e4688c5,cf13b711,29c785e7,3ad1e04f,ba02be57,9fa83446,5405aa0f,332a6aa1,922e3fcb,90de108a,db01796d,9045d423,ca0c2632) +,S(f736cad3,f1264671,f2e79bc9,c21551c4,aaa5da0f,befc1928,b9e7fab1,f59c8c5c,2ceae81d,16206893,b3810abf,d2aedb5c,55b79222,1346cc9c,908346e7,cd3e661a) +,S(7748965,7c1fcd79,60fa4a8,36feef6d,835f960a,d3b85009,7462a71e,8320a34d,cfacdaa1,2906f705,d83212b1,55475160,e02cde20,71fc5c9f,6a6d59b6,8e100aa7) +,S(279f43f1,e26ebe73,34924bae,563a0e34,e5cea7f0,eb9f5702,129fa6cf,8b0c67a5,eb5a49af,bada9830,7ceec508,70a49027,7d1cd6f7,6f332249,b2cf2505,82a2a828) +,S(cfccc083,9bbcc7d3,5ceca575,3b9148e4,ddd4464,5727cf47,26f33d62,d83a6a03,e3cc09a0,9d26889e,c969c734,a3361dcc,d638b17,608baa1c,e7cbb99d,a5bc23fa) +,S(e60b0451,70c2e3bf,dd627fcc,5eab1218,d3d598fe,a41f6bf1,dace1a35,2daeb287,a493847f,4f1d3bf,9e90c022,851bb652,691af15c,f4c6c539,bf2831ae,9cf8f2c8) +,S(11875535,ebc90fb1,eb7a08ce,90dede09,fa8cce24,b3911fa6,44ce8111,8848b433,2fb1757e,38148919,bb197d43,48aa536f,c0f06f9a,859ef737,ea50b1fd,82968ce) +,S(c69559b3,75cdaea3,56003ad2,3edd8ca6,c415868c,a13a30d9,11d4c694,f5822179,a39427b4,914b58fb,dd35408a,67ace872,8c299a39,278335da,9228ac85,24652906) +,S(625ff5c3,1e951b28,40f431fb,4842f510,8b738722,cdd52c4e,fdb495ed,e42f1f7d,a3eb00d7,4eb30337,d3f61ae9,d595996e,c7d5a3f0,ceb39f23,3df0f5ff,43298d7a) +,S(1e2fb107,bbb46666,8c31a927,3ce57baf,fd1df905,3fa28286,196a03da,74327c2a,aa882338,5261bb03,a8d8793b,c8f98330,97c22731,46ded3d8,966b16f,337643e2) +,S(d0cbd747,970ab0f6,19e5e4e7,78709399,b65b3f14,24a084b4,a2dca8ab,10d76ebe,7b5f778a,94d3d75d,57b5e979,3de246bb,f36669df,b7dcc24a,ecf4e153,2025b73b) +,S(ab4c057c,a40b84f9,10142209,a5c173ad,6ef31616,db80bd13,61959f11,b739f9e1,598f12b3,4695bb27,ccb2fdfe,a0911ece,eecf2cc3,1063a3bd,42dd100f,e93c7ced) +,S(f9c11e91,2cd3cf06,d31e4795,40c703a7,4f5f3100,8e6ce299,30d5f08c,7307276d,f873ce74,bab3785,156468ce,3d6a8cbf,c532db23,2a0a92fe,91d290cb,ad08713) +,S(2d95744a,79c4d234,40e2f40c,a83cd89e,61576686,21293dab,6716072b,2089b77f,a25249bb,9dd8a94d,44576a4d,7ed800c1,9adc5e26,82874ea0,5d63bcff,2f6b2704) +,S(3bec0dff,e9a96bb1,f3cbccf,cc96ecf7,e1335b1b,86f13b63,6467fd31,5cc33abb,e31f8a49,dee2408a,2d0b9288,4e381379,9fe404ec,1d8b96ab,39413f82,52b85b5f) +,S(23721d31,616051ab,60287a0c,2b4ada42,1bf00825,127ea0eb,1341beaf,a207fedb,8cafc4b5,40e6f3a8,6fd1b546,87ff8c3a,fa2372e0,24696071,59b6e195,f633b169) +,S(ff8e494c,446cf1c4,bd557615,a4a1ce2,b8d25bac,62a89929,993cfd,51d45099,113e1b98,555ddb77,b8680eff,c57eeb96,4038de04,26a05289,8744f8ca,4f3ef028) +,S(f170cfff,f158e368,81cb677a,c9d9512a,3c1705c4,850d5a85,17a66636,7b0cdf21,49512fe4,3ff0f91d,b9ab918b,808ac11c,b5eeb4cb,ed905f21,adce5e4e,42ebbf7f) +,S(e9ff6261,d6c05d6e,4e35e373,57fca0cd,3c3ea884,4a33e504,df3dfa2b,a524d158,4969683b,9af581c1,963525b4,e8244e5e,6b7e7e48,f85c1fe0,1e9f0dc2,c66e591d) +,S(c1c4879a,b614ad86,cb64d15e,cba92aaf,4d1c7742,76d6aec3,4b59f731,addf6cde,6d7185b8,fc1b99be,383a7a5f,385a08f,d7125e3e,f72f93c,25063bfa,d44abeda) +,S(deb0c036,3818a101,ba38a657,5c0db8e4,a65f824d,57014dea,1c110387,7bf2d9ca,f658d43b,f0c106cf,6b45e552,3fcd066e,e754180e,8510b58,43708b5a,b40dbe32) +,S(c4d4164d,f782df0,f5366667,83e5aa9a,8b05c7c6,91c47a49,14798685,1da3fc9b,6c394d4c,3118c8a1,30bea2,2396562f,6dfcf8cb,8f0a91ec,20a43bd1,548bd80b) +,S(94d83b82,f6db0da3,f1bc24ac,8c5bdd0f,6e89cf2a,bafd33bd,2fb3ca87,9cf63a84,9e8658be,59d64a28,7cc0cfde,1f35b83f,e3e098dc,6212afac,ea234437,2591f2ab) +,S(9de3fae6,e6934304,a84eed5a,58e077b3,76da7310,61859847,d6abecfa,fa650dba,7e4cd395,ea8b90fc,20850be2,4ba8dd83,e79dbc4f,4efed68f,504eec04,f9bcb212) +,S(251869eb,7a567c9b,7866c9b5,a829206d,867f2092,8f3a6fe6,46ebc3d5,2df7d99e,bd87d706,c49690a1,9e0ad77e,4b3b8f0c,5efba7e1,2a448a3b,b4ad3c7d,44e35e7e) +,S(e92e5aef,818b2980,43227673,3d15021d,c84192d5,bc9307fa,1add63a2,8e888a9d,ba3a3157,96ab2051,ad53b5e7,a058f700,ea90e40c,c5855712,d2176a0a,c232f94f) +,S(b6a8a783,c2716ba8,9a37c7,d7022dbc,25fef8f6,76c676b8,267038ae,64a33258,93b09e15,8bf081ab,9e69c4c1,6d965743,cc08ad85,a8eb248c,9c67043f,fe61ebbf) +,S(26e95f26,7ee205ad,50dcc75d,82330877,394c56c,84546cb3,72fecda5,f57c2556,dfc935f8,63840ca4,71bed804,d0c2d14e,c9528a9b,ba1e6f25,6545311e,5cae673a) +,S(6c1ed40d,dd086dd8,2fb00bbe,d969ea90,7e363496,97c350e3,2d0074d3,3e05bc13,854d177b,5593cdc3,ab1e4903,c64be02,2b6b0cb2,25c71390,9316faae,a235b269) +,S(a9286b2e,81030401,c37d69cb,1e73caa,b1c68a4f,4ce1538b,11ed850e,67d11aa9,64ab5453,c3a1166c,18f35115,1f031117,e29b1a31,237cb2ba,51f1a873,529766fb) +,S(a2b862c0,ca02cc3a,e1bc5737,876f81ed,83dffeb2,4e6954c7,3cf97bb2,50050cc0,db3a03cf,d704f98d,dad8f48c,5861988a,1caf439d,b50c196a,32289622,b9a49b3d) +,S(19259392,58c25edd,953117d1,fc7a74a4,a9cc2fad,892255e4,fefdac84,4784c917,b86a1bfc,b7bc8844,abe424a5,b7409abf,26e8ac62,7983fafa,70dbdaef,43a88eba) +,S(6b85864b,194b2cfd,af1d6550,4960370,306ee34f,87c91187,230de0de,42fafd09,54a9ddf3,87458a54,cd305e06,61dd43f8,3911e88a,db6f0256,d19cdf15,3ab4b388) +,S(407cbd77,b88e6d47,60b07e26,f53fefd1,2b98bed3,5403c693,e7ea33b4,cd74b5c3,eb73a1d0,fd122265,dc880cb2,920b4eda,610ff1cd,a74660aa,7cb91323,e5485d60) +,S(f65cd6a4,594299a9,6cdb5f9a,7889cf44,62cca331,d3329f7c,43ecb224,d58c817e,8c906c4a,11925697,9f84722a,9440d2e5,f97e483,542658b6,8538aa0b,f10f5572) +,S(2ea38854,775422db,43eb69b7,bbd47a6a,b8ef1694,28753fa8,8cf49c8,9efc6e63,bf6f2d21,2b21c740,a25dfacb,bd22436b,5118f1a,2004bb90,adf580c9,6f21f1e6) +,S(f2bb4814,94677f3b,6af8aa4e,6a3b1100,c2e0e99e,289536e7,eca3e61,a20793ee,a3a788bb,2a810267,9a3d98f2,6a9a2740,91a3a29c,234043ae,aacc488a,67849762) +,S(e157c4a2,84354b8a,73d5e5ec,58dc1ceb,2396ca3d,7f651e6f,4547aa78,99899daf,f06970d9,5bef3e0b,e2bd4597,a1c96581,275c9719,605f63f,9618816d,669a0bc3) +,S(25aa7f3a,3a14731f,c7fa3b81,31a569e7,4bf32c9e,3492e626,aa4a4316,f263cf12,e5c2e210,a8a75180,31778f4f,4737a0dc,bd1a1312,1dfb40c5,5c435803,4bc3eeee) +,S(73ba2208,8cb96b2a,c23dd2a9,7b3cfd2d,c504ccb6,24c2dfd,5062f8e2,bcc442ee,ab9d3a1c,6bd762b,9cd866ab,7abc0e1,f898c567,61c7071a,d64d5233,caec0ca5) +,S(da94893b,3e73246f,6a72041a,44c7279e,dd8d4d3d,55b20db2,eb0e08df,4c5837f2,6949f652,8601b0fe,e572ac03,89fc3530,6b2d09d7,b59c40c7,a1a607cf,51684006) +,S(908480b2,35c49b11,12bd6504,5b3a8e13,7cbd4820,3046880,1688ed05,abb8b7b5,8a887f57,622a7482,3fd40b26,f58ae7a0,18d2d03c,d5ed31cb,93a529e3,387fec7b) +,S(682d7401,31c966e3,2a8bd150,27fae9f5,4452fce1,360c449b,1b20bbd0,257d58d1,ee7f773f,9d074436,685a2fe7,21b1b829,9369f543,dcac37e9,9adf43b6,4525c19d) +,S(c8661c4e,dbe125bc,a31e7a22,83aa0f9a,b4b3ae70,8512fab2,21bdb4d3,8c89fcb,30299a17,16d3fe04,7f48627e,36195b21,e6800d4d,a26dcc63,96c7fea5,8f25a2a) +,S(5112b1c6,2b88e704,f0d2c731,834019aa,d51a108e,1a8614e4,f6e62f68,d7372b4,1bfb5f8a,ee37bb82,5494c9ce,651d32d8,d47e5a29,4b9123e0,7311f260,9ab4053d) +,S(b9214d34,49e16aac,b90f6704,ee589f6d,8fa45126,be235e9b,61ef3ebf,2150eb99,c9465341,2dbb107b,965d177f,de11bb25,53e8795,69814d17,34ee9ae5,267529bb) +,S(94185601,f959999,1999c919,82e5042f,b7e68583,533f9569,73788d5c,3a087b2,5e51c755,de79b6d2,eb640039,a4268d3a,601750c8,dcd0a942,eafc9339,6fa36c98) +,S(3cdad3b2,2a707d9,90fe4228,adefc934,e8c69a9a,8a4cbd87,844a6ff5,a073f511,f6de425f,26e17045,5589e22e,7824dacc,f8cb8561,832db31a,e8fd2910,558ff9f3) +,S(ca986704,a0d1935e,1611e176,60394d00,7d2aa1cd,cb17357b,b4a557e4,f5a4b5ce,8d527934,a73f2b0d,b3cf571a,ac53b3de,11ac99d4,6ba995b7,5a09195e,b10bb5f0) +,S(f5920156,3ec396c5,82fd6090,2d98ac,2d5dcf5a,cd37e651,a1e80c43,f76940e0,2e821f19,7e27f927,20230f1d,9e26dff,5b3d977e,4b4a6cc6,7482f3ec,3cd5523) +,S(9ced8c1e,bbe0d213,b396fb89,8aa8c24d,870d305d,e9346239,b2cb0315,c883cdc,79367b21,4a2e21fb,1e9b24d9,fed6a0dd,b377534d,4da2b501,6999ebf7,f77de2c) +,S(5b07b283,ca262751,4a10817e,bdfc8044,883f4de3,38cae14f,de955f18,9713dac5,b8d6d9f6,8a71aec6,3b7bb44d,1d2b678c,75ff83c6,37c254c8,3a14be30,a5957b5e) +,S(4c2eb4cf,3fe64ba0,21297b8e,321cac6f,a8245614,45848933,19453052,69337386,35ee2473,1830eea9,85171af0,96b6bc0a,72f3e83c,6d83f8af,8ef1131d,c8b52c8a) +,S(5f24fcf5,f8c0bf43,43b8a0e,b3b6d8f7,2901c902,6d43f8de,4bcb1cdc,6d5019cc,87c2a306,138265bb,59b2b2b5,6992fb37,b2ab28d4,ff4909f,d6067e2b,4f312cfa) +,S(e26bda51,c385a875,5083faf5,6a915944,3eb351a7,88e1597b,f17649b9,b23ab8f5,35d66322,ba25896d,2a14a0a8,a3f5bf91,d3ceab49,9b2683f5,db7f58e,68ca1071) +,S(ae5be06a,b1ae531e,5d2338ca,d929b5a3,4c283035,95282aee,7c3ac259,8196bbd6,f4f486a5,bf11191c,9134d522,62ccd692,cc5c6686,c9013ed2,782b816c,da416d25) +,S(d143463b,4661a173,6b88f274,6b3c20c6,d054683f,9efa5221,ffd3badd,14804880,77b28055,7aa11d3d,b2ac8de2,c9c5757e,d0c9b0cb,af6fca3d,bb295bcb,c3ff8afa) +,S(54804f7,28ff88d0,bf6fb4a8,a24d4eb0,ee5379f9,30f890f7,8e31afca,87f23343,79288e99,a38accc2,c59ff29d,9da5df60,42d63089,45900378,c40dd765,2f0f5626) +,S(68c21fc9,93d41345,484cf5c9,8aa93bf2,25fc2c01,922a0151,cb3e247c,f130ba0d,9c435478,23b9d8bc,2e8ba0f3,8f2b60dc,ca5b6030,eef51c6,cdd20eb5,a02c6245) +,S(2f50b870,96b2e15d,6fb35001,cd8f95a,875cea3a,edf21a2b,c433b254,7798b856,44b09dfc,e0b8d522,17e7d9f2,9bbc5d6f,9886fe9e,3e360688,8d7c4e70,c3bea0ff) +,S(9d054954,f0c22da5,f330b3e0,69310a93,66b72129,2fd3c98e,76464796,847d16df,45962d48,143c94b3,57400130,d3e050a7,4defd464,223ad599,e6aa947b,12679a85) +,S(31980b95,9f51a6bd,4cfd9a11,b86dda39,af66ffb,97713ba7,3edf8c10,462d5685,d0a02979,5b45ba4e,7464fc47,7a09a90b,7265f5aa,e6b0d655,fca5828a,837c2840) +,S(e9c0615,f0444234,1f522d93,eda33b29,8951a267,7c4cdcbd,fa02a63c,a57d1ed9,7aef87dc,7095f483,5ebf2b3,156be9dd,ecedf54b,1b0c739f,c513ebda,5c5f296e) +,S(fec8bd3f,f19fa6fd,17523af6,27384c7c,6c92fa85,aeab8bf,bbdd2178,89b12650,d01a278,6ee8f9db,38b1ceb1,a3fb1df6,b631c0a5,23e92460,d0d51367,1098e414) +,S(2094133c,86322dd7,ee1abf80,2ddde826,e6385342,6bc87510,dff63f3a,b94f54eb,efb2ecd2,13bfda3a,227b791e,c24ff627,17ab559f,1bd289b7,e7db992f,d941e850) +,S(a34c3385,ee882646,b44bfbf0,f64225b2,fb86df27,c616a2ba,6cabbb68,cf36f3bc,e99bbaac,57c9f5ef,c64c6687,6f2bd6d3,b9ec8eb5,f9227c98,9a768f34,23e91422) +,S(df09dbfb,564515f3,a6d86e05,dca59a0a,c353746f,b291d9c,add27d68,6810cbec,90738e50,6bead3aa,c0cc4a2d,cdfb8529,5e752945,a7b2e357,1a99c901,49017c47) +,S(df9817de,89526a26,c61e4294,f245ce06,8b5e22a6,4937d5f9,6898b9ed,74078989,c4db6e86,c6a44091,23227adc,345d6eb7,d2f12acb,f2df3b44,978a1743,94072f15) +,S(2e713387,7a0419b3,7bf77874,b24fd0,7cae9fb1,59919a50,223437b,7746eada,5dcc4c1e,12502573,b0520817,16fa46d1,8ba43ff7,e7f8c8cc,e3c141ca,6176f805) +,S(99826364,f0061305,1ba033ad,63b95884,97158ee6,9542d06a,f2f86d91,3bea7a2,5f109254,24af4592,2ab30701,464d504d,6006969e,30a9ce5d,76a3b115,6158c6ea) +,S(19222e8b,5fec6d2f,33a35fca,83c3b9eb,effb9e3a,f2ca9c53,929fe5f2,a43bcd32,c79b4f98,4621b2c1,d627dade,f01fe761,8af239e6,b80c23ed,a2dd1b1b,e750549b) +,S(e0ea59f8,6b7925d3,ad511280,8b1ae5ed,c0acf6c6,d4ed0846,d870a663,5573970,bbfa69be,ef531b32,5272ee02,8c880178,57ab2a32,65b5a6b5,38ef81db,83b5de52) +,S(7df189b7,c29c06ab,83f7d849,58ba3d1e,7da3ebcb,feeb9dc4,64b60420,3631fdd0,88e00d45,8287fbea,5c14ab33,6a879948,e81d9798,cb82054b,772728a4,4b69e56) +,S(42b9867e,7cde3ef2,b4b73f3,e4df7504,b7f06cb0,67aedabc,16edb276,97bbca58,eab47aa1,df9af0f5,40d1c6d2,ba7edcc6,fd436f56,c944c872,1e1f4958,63144390) +,S(9ffd9a,5535bd30,a697f61e,e6fd0ffe,b1a7e5c1,9459bd3a,708a7112,3a3da54b,79fb0a7a,8334fe38,64fef8a7,60610920,af2741c1,fa41f582,37e92acc,29644011) +,S(13ea8e5d,1116a57f,8b223a5f,79c750de,6c25511d,558e6878,e92cb134,7b81e393,a41efd37,d35ea994,8175d989,cde449d0,e325b40e,a247edd9,e8f4eef,e6a4d283) +,S(110d401b,d7a1a0c,e484fdb2,34cd839d,5e40cad8,6244cdee,5c4f2ba3,ee1e2eb3,cdd91a5,4dfa47d2,98a46f15,c021a38,975ca108,f2653019,46013427,349d7ca) +,S(48a9f1a2,fe29b05e,d419a14e,8ed288de,46affeb2,296522a1,f2407b76,a8f6b040,f1c5ea7,887c029f,67202b41,198113cc,f1ba5617,79afc1eb,db0e10ec,5b5d2a91) +,S(d8c4d9a,feb94e7f,7b831464,77aa4e3e,a48b6af5,35015941,4047cd01,1beb7176,fb8af9c8,da06c2b8,8787793,bd19d7ca,6cc64508,7713af45,4f5fb4c7,eeda3c2a) +,S(34e84417,2f5ff4a2,70a9064c,d5994327,9cefde94,d295f424,2c491efa,bc1f0839,2da40cc0,5120e4e7,8e84435f,895a3ed7,6c507bda,4d16cd14,b602deb4,366ff9fa) +,S(ea802ca2,f07a4348,791b7df8,99639d06,c6db8d15,74ae8441,b29c2d18,18b7a6c8,ea475a0f,5f74e6fe,ccfca44b,ddc38a4e,3cc19c49,e2bffa49,a9572146,6338a155) +,S(d80542c7,a7be8da6,6346d867,4154f65a,b1cc8e94,19299d81,6a5c874,f25ee43f,5e85d69f,b5c72ae,bd94b1e4,67e0d30,df8da61e,4150f4ec,e3efcfae,78d88d25) +,S(bdb35e49,7c1c0d1d,7a120366,cd259e7,31cb253,1bf804ae,f0c82cdf,561d0c0c,2675a4ee,aea6dfed,8597c08a,69fc251b,7bad9869,e83f485a,73dd1638,605915b0) +,S(5174e27a,9a1eeafe,6411f3af,b627223c,db0da11,6bca0ac3,d39a3080,815e22a7,b1223046,5f58d183,fcda2603,e4593398,51ed87ed,6839d9b0,4c845459,daa3d5bb) +,S(c8b1299b,1b2880ee,7720ad03,62588349,44bcc309,6a7081eb,ce10ef20,ab1f6731,ead86be8,85584e4d,34ac18e3,d787031f,72feb217,694a32a0,76f2f88d,a04f3440) +,S(fc1378cb,c42fd6e2,f61be9bf,e60bb2c9,b36c5a0a,4aefef09,c8287a3c,f1310d31,ae4ff85e,5b22d2fc,914192d6,28324d12,51ffc469,1ba8ca13,f8347edb,5a9170d4) +,S(7c0bf943,23e7c1a,8004b23c,c05327a4,d4127c7b,eed27e3c,4a825e41,5f3c3a6e,30ad62ec,bd19f80a,a9dcbb5b,145d18af,3248eeb2,1d14e648,d7fd7217,f1089728) +,S(2ede5f01,232c0409,34b07b70,1ccce3d8,27f85ad,2a56c987,e29cd6ee,d841efdf,24cbe9f4,69f036a7,6e140720,f65817dc,847e9562,4820dbdf,b8a1384c,8d39bdfc) +,S(decd1afe,faf01eee,d2dd89c,bdb9711c,4f1899fd,437d0371,55cf74f5,9aa0858,899d1c8c,49b0b3a1,89a24445,568becb1,d3b1420d,ffb2c7a3,8fc291b,258f8ebd) +,S(1dacbc1,4be6e70c,4043f5be,3f972d36,3cb456a2,2ee1b1ce,3e207578,8233a79f,fc3b04b4,f502affd,37d71c58,d6f1a61e,b126cda0,e156608d,7f2ab786,e0734486) +,S(12ad0f7f,cc7cddd7,60cc6a3,3fd3de83,b1ab0e3d,cca013a5,68d5978b,a176cc19,b4e0cf97,335b374,661f6c21,46aa7315,a2c4f6f2,b839f88d,5376e256,99e6e216) +,S(f649bb96,e477a5f3,f852dd31,65496c07,5e14a85d,fa929794,64f1297b,d56fd1c8,46e37dfa,7eddec82,88fd18eb,9ac200e6,79337296,e2cad24e,d67f98a6,2e61d040) +,S(34f5e5a1,248db96f,a165950,a9b74392,acda22d9,95496808,82760daf,642c600e,a8a6637d,a1cad7d2,f72fe1a,df67eff8,49d0ab0d,7e06d7c1,a11078aa,de9dc733) +,S(26e940f9,ab45caba,adb1e4d3,23e5cf70,575c44dc,dd2f0b10,1540174d,d2aa715a,972aeeed,c5fd01ff,3b04f236,c799039a,9c7fe6f0,db1348e8,b97dc885,c57926e5) +,S(e3ad36a5,132f1067,503498e1,ed4b315d,4d06cc67,3a9207ce,47224497,12f51a4a,4fa719b7,37cdf1b1,cd5b963f,16a47e6a,e43dbe0d,490423de,fe5deecc,5baf9e8f) +,S(413fd0f9,4e231525,1aae9d6f,61aa0038,dbf7cc03,10c81185,394c8cc4,8b721a9d,9281c5b9,ac1f56f1,50776cf4,69e57118,8cc6989c,ae41b950,f72ff25a,d6de836e) +,S(9841a473,600446a5,5ce0ab4f,2e77ad12,bc2abc34,1e607241,c0c61d2a,d87148d5,7d1bb113,ee4b5199,95231834,1e05d87f,f9bb355f,df958410,3d161969,1d786f4c) +,S(1d4cdc2e,3e06c052,69958eba,f00484b3,36594f9b,be1ea98c,2382e608,cf78272d,6cf5d126,766fe5f7,75bfef7d,c2d91b48,66d4bff4,c8eb4e59,230d1903,16ac6bb4) +,S(da74f388,86de9c6e,36227258,758111ce,3b9414cf,cf8d5cfa,bcd9b3f5,44f1262a,d9231255,e3d9189e,a8d0344a,80978b83,3f1afb57,5c799f01,8b4ab73e,866a38fa) +,S(b2ebf119,46e37704,e1e12345,c68f70a9,a031c0b1,e85c0833,c5df9c0a,b836a6b5,c1652832,1666f0f4,19806a4a,86f4aa8e,46fccb4c,f7b6493e,26d9699a,9db2bde4) +,S(9fd899da,eb6f84ac,edfdeb4b,fbf8b521,6b5757e8,10d22d4e,56afc38e,da747cf,2daef35c,7ef36b82,d6158256,2466895b,9656970b,4beee8cc,2e0b12da,83d7af64) +,S(eda65e56,9cf07e84,19d07f6f,4aedd24,3c46df2c,bac92772,bca7edc6,ba17b217,1f394c99,b48348e6,fb709e5c,b4b4b77d,41eac2aa,bea2a4a6,353d1982,a9985281) +,S(78567d1a,1a96df56,299c9ee2,1d916472,303178b6,9b18fb00,a23c10e4,f093ed5b,9321fb0,6c5d2dc8,52e64d6e,453d9923,bff7c626,efaf5e50,245b4ff2,64158f23) +,S(c639e1a5,90f5973f,148e96f9,15c94d8d,7c7ec36c,bdf15150,83503aa9,9ef5636e,3620e7fe,faaf7581,886776e4,f7391599,94ae3e2f,2a59d4e1,ad6d508e,f4db90d4) +,S(73ef6655,c49894,7aa2b63b,89b77f14,3ac096a1,d7333494,d9774ada,5467b60e,c04509b6,2135fa8f,816436e,6ec0f513,ccbb23f8,782e2ca4,d5eebfea,6a5297af) +,S(bfa4506c,dca70a3b,a8fb0188,e0384bf7,3afbaa0,6818d503,df5015f4,f10cf341,9fc7388e,2edd63f7,9a5d328d,1b949a2c,a364075e,f94c8de8,37cda5e1,7d566ba1) +,S(2d594b,814a9e92,7ac9433f,3400c5e5,1a58457,6f47b8f7,f5f0b8d8,f48b2cb2,fa9a1577,d565b906,1acb9efb,4bf415ed,f264b662,594d1ccd,51baf83f,101183a7) +,S(401ae44c,a4039850,4d57390f,66044f6b,cf1e9fe9,f0f2b057,3f64f026,b6d5f3e6,ed947768,36ce65ec,f947b81,9dedd31e,c8ceacd0,16a40a35,78a592bd,4c2626f) +,S(43f9397e,a000ac26,8a315406,fc9cd047,b9cad513,8a7dab38,2717744c,ec6662b9,14d5bbe0,52dd8635,29c0b0b2,dff7379f,3e2ae95,6c5b4920,b36452d9,56085bca) +,S(25036a12,d8eca48a,78cb5e84,41aa5a53,16376e83,e2ac7819,db9895c4,dcf2a22e,14c421de,dcfdf9ec,b69a24fe,1c289ec9,f4d0f9ca,6f4b9409,35ac9815,a4154209) +,S(30e9c9a0,d9b9c92e,65794acf,a2cea34a,8d56e3db,d5b56205,59100128,26bf8991,999e4729,bca75c06,18f0bf9e,8ebf0455,3460031d,4ae47623,5e4ba182,2f62550b) +,S(a8fb641,fe27c40a,8e410831,fe1b0d2,cf74072a,bb626b19,2fa51341,e99ca538,f3832ab3,4eb6917,3e2f3755,366b2bf2,4dbe257e,eb516e39,4bfb91e0,a63cff8) +,S(5e09a349,98a824e2,f3ffe2ae,34a835ce,6b2f0d28,43b765fa,8dee010,49df7867,20ea03e8,4817593b,931d1612,2d01dc7e,52715fb9,36838de9,d2c9fb69,9c935025) +,S(e62a12e9,d02a5a7a,475dbcc8,79850ba8,340b638b,a5c3b505,8d3c7ffb,c90f0dcd,b075d21b,40e5897c,7e849529,bceec7f1,d5f87881,318c115f,acc99680,31d17f55) +,S(45aa5534,530265c1,c358b529,de15487c,b6bef551,5125e835,e027b0a,a69a9cb6,2e56d88c,acb32ed3,24068fc4,4c03ed2b,1b70392c,9facfc2c,2fafc696,fdb8d397) +,S(7fe3d940,50e2f358,31b410ce,e54c7c82,5eb05553,981f1c45,b136a24c,4d4a4b57,97b0cfad,4f6669f6,efb2d35c,42302e10,87c03b3b,1c22e23,24050399,345165a5) +,S(110dd614,85bedfa6,c09a2e5d,5737ab2,4a5b35aa,40129388,c6ce876f,bd3d9c30,7972ec2c,621155fd,12f0a1cc,30927669,13a9165a,7dab66aa,8f8fca53,7411041c) +,S(7427d28c,aecb7a35,a4100c42,9fe03d5e,6d3f2f91,a5067866,c618c4c4,5fc53547,8cf74ed7,1324616d,bf4135d,8f442ba8,b473ebab,fe981f2,3a426eed,bbbcff16) +,S(9779c811,98040c8e,754b9df4,b547c09a,a9f25410,1ca1fdde,365b0ef8,34ed95aa,148280a1,d26c8ff9,51774c55,7590081e,36030a10,a148c322,3c7379,67d036b8) +,S(41113833,f7da3a4a,4736fa62,ad7b4508,74ef5b9a,b0961665,e30f3081,7d861adf,98399ec3,485a984,cdbb6817,783223b6,59884450,881d2f41,3f4e59f0,ed5fbc65) +,S(f9830e6b,9a9a9dbe,50abcae1,d44d4856,3a92e505,aefd52d9,2259c41f,4b8b14bd,12061be8,5a0042ea,1baa46df,b36d6f78,62c11cb5,b8a070aa,6c6a8991,62ea1c0d) +,S(71284b2b,36a4c8a4,40fdd713,6b647482,bc73d680,3d6d0142,833d34bd,f2794077,6e54f5fe,8cff4ebe,bad1b47c,4e5d6407,44be9171,b391492c,df48bc35,10e195df) +,S(524a04d,a1b7ba04,34377531,99178ceb,7ea18f36,2c300aeb,a8daefe8,6e63e9d5,1c71ed93,861c3b1e,41a7fcd6,57225e92,b2ddc507,4d54652e,3898683,8c3688a2) +,S(e1fb70fd,2057dc3b,c545fb80,eb91cc9,479cada2,93c3492e,260b0a75,4bdc0222,aa799a9c,25786c64,d5138ba2,1f801e5e,88fc2602,d4e288f1,96c8d79d,7bad2278) +,S(2bf126e9,8302e8cb,407570f8,c3fe1206,3c91bf2f,63cdd9a9,3dcc403f,a8592d29,6452a6a2,20edbb86,3ed65ed6,cddd70d2,6b4975bc,e843a41a,10ce6ed3,d38591f2) +,S(faa217d0,1d9f0517,5950dd5d,e02972c8,b2c88076,36090d2a,6f17e7df,2a70cd7,73077022,dc1cb45a,f9e9d056,19635760,66ba7b07,bc79601d,ae7dca1c,a6cdc033) +,S(3d184ee0,ed45af08,6a7263b3,47b12449,a7270887,b8460b7c,6428ae4a,6dc4836e,d002efdf,1259dd8e,ab5bb606,97e8892b,4cb5376d,c26d08e,52b7c9a,760e8826) +,S(3c825a76,29890675,bfdcdb73,27f37c4e,43214a91,c6ddb6d0,f31e13d3,cd26eae,45bc7ae4,649d70ba,99df5d07,92e89130,87a272b1,bb2d6043,dcad0fda,a5444fac) +,S(3a8822a8,d5b230ef,62f657ed,31a12d63,2862f22d,eff16f08,8065981b,ed3e926e,ddca63dc,57ea2ecd,b25e8d03,2868b12d,abe8f7ba,8125a4c5,56d80a9f,9784a04) +,S(d0042da6,aa3b60a2,463e3396,98229067,c80f8924,12d8323e,7482ff2c,53ad86cd,40697e7c,4c5d6c,4ba11cee,fdbae25c,e234ece6,7db446df,daa03025,5f8adf35) +,S(a06e32ee,93d70c5f,a45a68e,2a180be4,7d8a7f60,17d035e,47c94d47,6f3838df,f4bc98ee,c7f50d34,4e88ee87,d8705b50,4cc0fd5a,5bfba498,5fe0699a,df811bdd) +,S(21426073,9f41da95,2dcfa74e,dbcc2f22,375fb359,93f83842,99e3735b,e39b45a5,697f2d88,74cd2249,cb84c9aa,16d20956,f5c40358,9d5b7965,ea4b46d1,9cd61249) +,S(84d52769,327646d6,13e3e1ec,2d20791d,76b765eb,fc5fafaa,20097164,dc9cda7e,d87be4b4,2e2e55fc,8f87dab5,c8c64493,f09b3996,87ef8fb,3a6c653a,a2250cf8) +,S(ca48382b,49283747,d433be3f,ee0569ac,7d3714fc,bbd76157,ac5229ee,ee25dd0,b67165e7,60894bf4,3cc80df5,6e3109,db7e370a,bebc7503,ce20836d,3f0ebb2d) +,S(8a6a751f,816c206b,91e67608,58fd7def,8ea70956,598b1c61,8e0699ce,602cda79,617ea6f1,c7f98cb6,41f01862,1f4c8cf,12a556e5,675765cc,c7bb1283,cc8ed073) +,S(1d035f81,fdc7c483,9936692,9bebc7d9,15fd3c0a,380f3348,13c65527,d82344ba,e4203070,f687298d,af8f6d66,6030f3a0,5826b0b9,31f0e55,8c356aea,b3464712) +,S(d28f1a4a,1198f572,a8130bbe,3f25e585,348a33c,2d73053c,ee7e29de,8a3ce79c,2841bd4b,c46c772a,57018c74,8510fc7a,dab16644,f157d51,9c38391c,4470f7b7) +,S(41c71a1e,eafbd926,4a49ba4d,e976e72c,691026a2,b1773d4a,88670b4f,d9c57fb8,c2b198e2,e4557f8,41203c16,b511d61d,b39e8a67,21b776e9,865158e1,91ea5ac) +,S(3f0ae72f,b1a06f84,803ff8ad,fa0e5c76,ed0b596e,d52568e2,6fe4aacf,6cdbc3ee,69a9dc3e,a0ae93b0,6c4650f2,941ebe38,fcd4eba2,fc9cc0b2,3fb9c127,81c53f78) +,S(b59e2f6,2f01449e,d6728b04,e9334f10,54c4c8e2,db4a422f,70566b55,ba4d2e5f,1b6b73a5,7444de3f,4137f2ba,722e70ab,e6a785fe,1ead6fae,9f2ec5db,299f00d3) +,S(42145fda,e72df9aa,fa3c20ff,cbf11a0a,d357515e,ec6846a6,5cc6ffd8,8fcb1347,4a34e518,c9d39736,3c88ad65,62ecc0ea,2652c6f,48955852,99a0de0b,5f42b6) +,S(17a085d7,8d8013f9,b98a8509,aad7553f,bda81df3,33aa6f7b,2457a74b,c83f6739,412d9b80,efeca81b,29619ca6,8bdcdebf,7cd253c5,88793675,6a379655,d10df76a) +,S(f5ec770b,2e0d0afc,7b155abe,bf310270,28bdff39,a08d113f,b0409624,e7049ae0,e9ce5d46,2dc5d255,a09ba37a,a7a5edcc,aad29ce8,6f9c6b58,d2781e81,5d88a25) +,S(45e3976b,749279e4,486d2db6,fbbc7731,168e7311,d7d75f62,b754839c,2fd17918,84f0c079,c0127eb,d1064724,927459d9,ebc22eb3,502a99a0,e5876e15,29c8845e) +,S(df15c304,378da361,7f821d07,d00d1bdc,189fae9b,7aabbc71,51c2117d,8fe0c21a,589daf,cad5cc54,58544b9d,a69dc510,9f8f1e82,866b1076,ff1d229,5942deb4) +,S(c5798ca4,547cea39,75e3b613,911434e4,e5f503bc,9ec6e9a4,8dfe8386,a371494c,2864334d,fb2bff0a,4690f2ce,d1a1d1cd,e3b13f96,325f295a,8e0d4f1b,b13413f6) +,S(773ba0e2,e1a80ca3,9060556e,2fa8d15f,83dfd018,dd226751,9262651c,9765c915,e59a686a,3fd13284,e380188,fa40e9e8,28991374,20c906b9,ae882512,7dc7dc60) +,S(6ed4cac2,e1685eec,16c12251,db5dbceb,90f542e3,854177d9,83ee6024,d6e7a740,2558db33,ab598118,1802a84,e6ed32ae,49c25379,606a3150,6ad03ec0,a5308b98) +,S(21cfc6ee,d98915cc,1bb52da8,e83aae9a,64970d4f,ca3e70d1,6536043e,bfb60fef,8154f487,290cb8ea,73cd147,c1286d5b,9f471665,d9e0ee24,c2be264a,2f1fbc36) +,S(f8db764e,bb8f031c,88921a73,92f75323,7d1f4a22,7bc8cc64,8bcfa9a1,235cdc11,82c25fea,2d0a1e37,e16dc4e1,b6737ee2,281693b0,b48911db,c79c1470,c4e6274f) +,S(d52f4eef,14a5a860,983eb0ac,8e02def9,53f2ba03,604cc60c,80882e56,ad6b3dfa,b34b3131,7e37b973,9559bc2,6f85226e,705bf06e,1fcd818f,d5b88eed,f2d6b7ed) +,S(3e9d0688,b9b67517,e25b526a,4e46be08,4e95a171,f9d99ae3,4cca6c14,66adaf6e,e1938d76,c4349375,1d5b9876,4ada7b7f,57144878,78805540,70b7c27c,9bfa312e) +,S(c74f8dab,54590ad1,3fff217f,b62f43ed,7f42ffe3,6c8fddac,fb6315f4,a3406094,1bd8025b,57dd382,5b5b6896,e90ab5ba,fd31ef46,8f02846f,29f1b1fb,f90302d9) +,S(71f88089,a8e0ac,6cd92e03,b1dafb04,76c0f489,f16def9,75985abd,5dcd02ed,839bb263,7827e6ab,f6941b53,67c9aa09,835c588,3956733c,267fb52b,6be767a9) +,S(983d1cde,7d75d70e,328a8a62,ec82a96d,5174ed4f,c9676e10,7c282be7,bd408b07,5fe2426f,287fd353,173c699a,39407e36,c365c413,76986640,7c12f11c,5dc436a) +,S(3cf9defa,a067fd0f,6b012fe9,8b05aea2,83c332e8,cbc8296d,c777951,9e62519a,3fbffaec,c77d872,7dad2011,aa314690,2806f763,b73b780e,375508a5,c77a613) +,S(f415897a,cf653c3c,26fc5231,f375e28f,c23041aa,af05455a,e81419dc,25546e30,3a9d5669,806ef66b,7a0661dc,cb941531,d81fa7f3,e08fec72,ee6312fc,4a7f59e7) +,S(5bfb64c9,cacbd09,244f3a96,d5a6800c,4a012297,95962fbe,a429b659,8e085eb1,e5c4ecb4,9cc4c685,a4fadc80,77889298,b94e779f,7f462aa2,130ed8b,ccc0604) +,S(61a5a78,a086ccfe,9f54cb0b,750b7c8c,5c8dc4b0,e32f3c69,c65ce4f8,9009587d,50193c13,affe3a34,1ff3b8a0,b37f07f4,b49667c9,f8940311,9ecec58f,a76d8c60) +,S(e74514ed,93784bda,ca6e30e4,da2a9a78,c7c70bf9,42cb4ba6,afdc357a,c694ed1d,ee4a8c9f,7d55dd09,c51117d0,754d301a,cc36dcb,49d437dd,ca46a920,7088440b) +,S(dd9aa58,f2cc4c0a,8d2bf548,6dea653c,b4671e40,c0073df5,352b2a9b,95637617,6478aca1,582a65d2,6c0f42e7,48f06e60,ab85a494,d0129b85,5b5fdc62,7dc7f580) +,S(fd7f2622,665c2005,9bdb8c46,d140ecda,ce013ada,6dcf3503,e28eae44,c2dcd83e,536c948e,5db2978,31e2ab3f,fee860d5,dd7fdd16,645cf561,67e484,f4a62c61) +,S(9635dbcb,b2595930,835598a7,149a8a37,be64f607,c227b307,4d8e8184,c2ae99fe,1b2cb226,bd9f23d,bd243ea3,eb57baac,163f4e49,6272d220,9f436f81,7b6d9442) +,S(642ead1c,c24db5c4,77d798b1,515083a7,7777476b,c3a03993,883a7ab5,e1abf814,185293e9,6e6dcd7c,209fc891,d96f9dfc,4012f756,7d3e2f1c,282eb0fa,5de46eff) +,S(3c5be7eb,3aeab34a,2d982ebf,3a766962,822ddb39,527ab79b,83540818,a4e2931c,a161ecfd,2b906280,46d6d1de,c149eee0,e6c92ccc,5f936a51,e2a4c3,82e555a9) +,S(c1a90946,6c0ea8e4,b60611fd,6c61733a,6d90b0f9,d1b7a761,1f9e3e84,6423bde9,f928d301,b6c27eed,79b8a8a,ba388ef5,24b17220,7ebd7396,5623e60f,f650dcf4) +,S(e49d97c4,2ec1b8f1,43180188,382b265e,4216bdcd,82824a15,3a0e1d73,83207541,8e521e4b,95a1f4b0,934753af,cc3b73b4,7b3eb00a,a9c7af48,7e4e5a02,6234b733) +,S(49f71f26,f0d828be,9e9a70c1,bf768117,ad2fe6b4,6e9f416d,eaf17695,80dccfc1,86f4404a,67e8e6a6,9a901589,d7c09873,eb553aca,41b0cb66,f4767621,76ff0d79) +,S(487d806f,67635c8f,b66afe46,3754a5b4,64afa469,2ca69541,5d91a6cc,8903e853,b610d414,26951e55,46bb7216,6dfb62fc,602b3d20,18c38670,760f55a3,6c45c688) +,S(47af5aaf,cc7b69e4,bc536d57,8f57b958,3fa17666,2d302141,54e3e35b,57357e38,b79a4c34,709d215e,dd3b0fe4,9cde9de5,b45ccdd2,af25bbd3,602a0c90,9834dbee) +,S(84f79cc1,a257fcad,546d8218,5caae7da,8a8d523e,7feabb79,16ad7d8f,5421575,cc84b844,55ef03ea,59913a0a,1919c0d,cde9fd92,ce872029,57a998d3,80ec1986) +,S(550b7e54,3d3accfc,eb488b6f,b2b48d0b,9017a5d4,ed54d55a,72b8ac6f,c83f6d14,91f318dc,ea83ec1f,f43d5172,bba5ab1,7891f3e1,2e162b6b,8c512589,5f2e5141) +,S(f691885b,eece383f,1f56593f,92de0b34,1627cd58,389afaba,f442663,88aa6a32,1e6c920d,2eb76656,63c7586f,7907471f,79e5faa1,b6fbf4a6,c905e422,9a441638) +,S(4e3c52a,96889e52,b99aa469,b5f07dc6,f6720e49,29f3961,fae2c59a,98ffacf9,ba3a3d9f,dccc36fd,a8642167,7f54de2d,4c079b2c,e70b46cc,926d7d9c,ef1978c4) +,S(66720c8e,c638367c,c934347b,d8768338,54eaaa01,a81a876b,d2321e41,40381224,eb595ebd,67c5a08b,24482042,5035ecad,f20fcfe8,9a84a60a,93f3c05,a926d574) +,S(4b01f86f,f9feb661,eb33d7eb,87f940b,74302cb7,449b0bc4,84e97af9,56b79d2c,2680db84,1b233e25,16b74190,fd3a0cdd,b8fce285,ed8577b1,e13c2205,9af0d3a) +,S(dea430d,6a4b5e18,fa04f77b,55f85d36,df9dcd47,87ad5899,c16d2533,74494867,46b1f5b6,307a1c05,f3b42ff,e7a272c8,c63c2a92,19bd2971,d70ea793,5bd508e8) +,S(3f987bbe,d7fe896b,8a8067a6,de49de63,670e7260,f255e5c8,a926b9d1,fb62945b,373b2f51,86d7d9eb,18570d3b,9e48756b,663cab9,df388899,7af923c9,bd79971d) +,S(6cbcf291,56e3cb2a,b0d3c03f,3f4c0283,d953a641,9f7e8655,d8c46566,e3990b70,535c1017,fb40108b,24de2a8,358d217,8e47961,e5f4cca2,aa7e7c2b,339c60b0) +,S(d253488a,5610a541,e4ce2cac,bf4d4901,5fe63dec,94615e80,d795b39d,9885201c,c58ac205,e7323ca8,9bfd284e,9c9bcf3b,89b05571,b84d3afc,d69a9a96,9ae4fcc6) +,S(bb77470d,cf0851b2,29494d6b,e69aeabb,dac44010,17200af6,391cc2d2,352bd9e7,187cc653,b6b56e6e,6c9969ea,705a6d9d,3539d4b8,b8777371,b64deff6,45307942) +,S(438c5a3e,1ede7063,eb928da9,693d7602,809b9e08,e49951f2,6a600eb3,e929ad1d,d2918af0,67d34eeb,d8f179ad,2f3f105e,a5cbfc9e,1f416cec,372a5175,8ae797c5) +,S(35db0905,e28b3e7b,b8c4425b,8d9151b9,9cce6a6f,b6e11561,65b7e356,9191e69c,25963fe,ba45cdbf,5472c046,3da3279a,60a3f1ca,9ace47d0,4c4648b1,52d2e560) +,S(6ef2e9b,79e48447,7532ba0,1b658af1,e27b13b1,f1635317,6b47d994,cf3c6bce,88cc8a3a,aa1f2040,7976ae7a,9bfe09f4,f0c1fb85,72f122f8,4aeee871,b87867ca) +,S(c85fa35,4c064f00,ac39d455,7884f8a6,420c4192,aef2d6af,8afd61ec,2b977b33,3d17f849,6ba4a79a,bcdaf502,87fa5fe6,15d2c24e,58f27ab2,7cfb2d86,140a5f0c) +,S(7f47028b,4e84f4a4,21909f01,85df2743,e3f18f1e,134f90ae,166c4089,a9d44803,4c025c2a,c2a1af49,fd7e1e3d,2ddab9ed,104a0e21,17895fc,36eaf9c,7a550e9d) +,S(6ecc57c0,5c7f6b98,6cad9c94,435664e,3b036ae7,325ea091,234ac216,3dd0e324,67086288,9346eaf7,e6bc8ac5,25d60d5c,41bd9701,f5cc5f58,7995fd6,1c75def6) +,S(46a2a28f,61fbbf15,c3afacaa,e181f527,2b957bb4,7f127465,fc03d021,76359328,88b36e79,2c071ec4,adf1a34f,a3d1114f,f9813f91,39b2db5,83320bef,134ace83) +,S(a2072cb2,e25bc045,fe40dead,8fd3f7d6,449f8d7b,57adeb47,b6ae9b7f,7d6a5b2a,fc019c0d,8c9263f3,aed518e4,44c1e5bd,dcf796fc,7d16cc6e,c90c4c59,7877e36a) +,S(fec203e9,60d4ead8,e3510545,3e73999,1ce5234d,a92a20f1,813414a3,599874c5,118cddc6,fe918be8,d2c5483e,c985aaa8,4d708767,4af70444,1fc0692c,b92a540e) +,S(fd166bf7,a69d80da,6e03c740,2ce6f4,a05f1df4,e2a131a,daef89a5,86f73681,1e3f6791,7f2d3824,ba4ef07,86ed8f08,c65860a7,e6913fd6,8cdd759a,2deabf29) +,S(a479787d,b514baf5,b46a578,6a221d72,2eda896a,a7fc1e43,4817f125,2bbe0d6e,1a6f7857,92bc5fc4,e2fcd87c,6b82ed34,20426f30,a9c6fde,4f09ae14,9ce426a1) +,S(7aeeb17d,f75e9c7,2dab575d,2ad1cf87,b3eedf99,8df4b14d,d42b50e3,7895f041,8060c0fd,5f903934,d47f3886,8731ddfd,a6fc1355,53be9a44,38418174,37b79fbf) +,S(6dd254bb,63c3fcc9,a5d1758e,90c0c6d2,bb1a19ae,788b351d,191d0261,cba75c76,b00b5cff,f3246b0f,ec835345,1d9df948,4fd903d2,e278ac2e,59d8ca20,647c2ba0) +,S(d9e76bea,acd64f55,9a9c2888,5834317f,c24fe5f6,7e540d27,6233c187,76cd7323,4d079fbd,15ab81b4,f3aed0d,663178c4,33f976d4,f69af7dd,ecf61a13,b831e71c) +,S(b57b26a0,55c4f224,865b22,287c9402,4c801618,c50b00df,33ef8509,a94a0f57,5abc4303,1701260a,f41a0e49,a100da19,8d033179,8b26026d,6401654d,39d53670) +,S(e03c71c7,9d902be8,5caf4df6,aa401a6d,18592725,2f10be1,f4190b67,bee3bb8f,e5f16a54,ce5772cb,1390e49f,966a00e6,893ceadd,5a753c7f,9d82a2ec,7324240) +,S(b7f3b64c,980f19f4,e4f46083,c1ca758,70f4313b,3c23c5b8,6acd6c8d,57ec0de4,5fd0d8f9,2a70c9aa,bcc1523f,91fa603b,e17fa73,fce73899,2c1d720d,3d55b1ca) +,S(27b24868,2a1b9186,3a1f5c9b,a5010a37,2349c417,e1d7acaf,dd94c7db,c5e12733,c54c23bc,4e17418b,deface22,b8e527e1,4de1aee9,71c921fa,fb3f58eb,e4778125) +,S(5d4b2b1b,5f9e640,bad96aec,6d3bc5b6,169411e9,5dac7346,d1f7f885,50679a14,80f0e3e7,7e8d6c48,62b1f6c9,c0ee37a3,3b98be7d,e1c6c8ef,12759b73,c20a2e3) +,S(790a4189,63c3ec0,ba6babae,18ddb717,463d3ec7,c0b01852,5e760e96,ac5fc2c1,6db8b456,9f2fed97,783a6e80,413d8ff,cd0ac6e,d512f712,4cdacad4,4845242f) +,S(38dd209d,705c3f63,9cbdb0b4,22160943,4ddf1366,56f2cc8b,22d27479,c5446f99,8b793f60,3621f6b0,1b1ac36b,cfd92c3a,7dffb7fb,fcbf831b,1db077ff,7482c25a) +,S(29c6b2a9,24f95c8,58857951,cea2baa0,bee9866d,77b3f7d6,3cf9debb,16437528,c408f565,cc78e5,15a6e0c1,7a96392d,2952e25b,d067a0f2,d2f10eff,b54c2ce3) +,S(19f1ab5,b54c80a5,a32a1fb8,1bb3f389,ebb53641,6c8a844d,3846c2cd,350420c0,38a56a7d,7eaf4155,4f5b8090,c06fcbba,9f516a23,340617e4,dc5951d3,35b75de0) +,S(fa3d2b8e,d73ab5c3,24980054,27830806,3715c17c,7981db6a,61757048,1e1d780a,7bc0b4e2,4cde1ca3,e30ec601,73121a43,f87f679d,cfac6db5,a9bd132b,d6a89454) +,S(908e538d,730377d9,d3585ae,59b0949,c46a6aca,bb04e725,70b189d5,57c26825,5e108800,6cddf00,39f7d855,64252cfa,d4a37761,2b58b6b,5fb2942,7e274af6) +,S(e57bfd8,f38d456f,2e977090,d9f81673,db11a195,568a85b8,dd8bb725,a30d22ec,b5965e5e,e680e791,e87ceab7,156a3e03,dda7e9a0,9a9dcd76,a72dc838,d9e2e6a1) +,S(aabc514c,b88112c4,6e6e1221,d61e2abf,dd7a3bcb,a2c7cb78,4d57dea6,1950534b,96e3d9b3,f3a4c3cb,1473f61c,e224dcbc,a535c90a,7d993409,ed079d32,3c8facc1) +,S(94a800a4,6eb9506a,8f6dd737,ba45c697,4e563c3e,1df40959,4198d1fc,e499d2d8,627ef26,d39e19d,ab4d6bfd,9d4fcc0f,4bb4d97e,b0886426,40b7ca31,952be814) +,S(1022de3d,6caa7542,ddeb47d2,1f82cc3b,6e04170,ed9d4ef6,81b58b81,efd6c3a9,2317f1e8,37716a0d,842884b,cd77cc3e,d6235dae,9fad5661,9f20a883,af459b5e) +,S(9509f343,17c15f7,a54cafc7,3d4275bb,41e39183,53858724,4031baaf,97bed951,dec71a0c,7f8108b2,5e296361,2d6623b6,7b2f6d9c,69d7b6cf,d7273503,fc72b4ba) +,S(bbf19e9a,efbee2db,fbd5eef8,492f52be,2e2f9072,6b3adc1,d16f0627,f4f19d25,d9e691de,f2ce976,c66d806b,f35a2b13,56f9fc07,7b7bb239,30668081,4b52cea0) +,S(b5997bb4,5b359516,b28ee935,e4d59c39,ae434c29,5e5d98e2,2ddeff57,4afabc5,fb6d36a6,fb2e1aaf,d9b5d89c,39a854f4,a3cec053,1e1a776c,b4267b4a,d2bd8ac7) +,S(fb746cf0,cbf2f56f,6a3a99dc,71e84cc5,af76899a,3a7d02c,fb272391,b77c8384,d1e4cf27,57373ab2,d1ba3f2d,d5bf8b26,a74d6814,23a5b16c,9621b788,a51e9796) +,S(644facf9,e6290462,1a5278c3,f4a2ff8,84600c23,69eeb557,bc412540,c4966655,5b5ee0cb,449423fb,67c9b542,3868f7b7,6d1968b,9d7dd751,75e7dd5,2f994b88) +,S(c41931cc,9c311617,f77faa3f,6b6f6e21,bd030c2d,9778b065,6ca105f,cdb8658c,8a759dda,8db8a8ed,eb1f2972,12858879,bb2f49c7,468d8edd,14d28525,ca3c0746) +,S(e8dd9063,48691527,e4f4fc96,2a0e5608,d151aa68,53257e2b,1acb7d94,56f66aa2,470d108a,4180fbd1,2a377517,c5e472d4,680ec468,52bfa472,cc400ac6,91541a0) +,S(6b48efeb,ea6d0d17,346fba31,8fa33deb,301062d5,94b254bb,db6dd28b,ffe27451,94cbae78,eff2064c,cf91bcc4,fe1ae3ef,3d767b63,6eb4720e,35f2340e,87c1e941) +,S(1cb08c2b,72f069c0,e1e4afb,7ea73e48,a33f23cc,cb89fb08,db1b0919,62bedbd4,64d3500d,5b6577e6,6c18d3be,fc0a7dd0,833d3245,1ad03257,b45a31a4,67c14095) +,S(2150558d,bc64dcb6,55224045,6d943208,7b7c7182,3901f78d,9702a736,998906fc,b49e2adb,321e0c49,ddeaa152,9bf83095,762ea96f,6c1290dd,b6ad29b3,c84b8657) +,S(530f4c16,58284100,55a67001,eebf528c,f9286825,fbb60ae5,da4ecac,e3d7f4c1,80b147ab,1eae8922,50bf82fe,1a9263bb,41573617,7dc02e91,fe7e07b9,66bc6af9) +,S(c5158947,8266dd62,fa495d4f,94bbe77,b1230f44,685ac488,7642576c,8d92e6b8,1705fd37,da813af6,2a799e41,3a45a4e2,4d2121e0,1065e692,ec1de149,43be7c90) +,S(e6963d21,b576da6e,6aa6f1ec,da7674d0,d827d533,31e19d85,83ba0e7,31553f86,d3560d2f,91c7219c,76962a5e,3b2aea76,604b5ad4,82da8f8c,419cf2c8,7cfdbe61) +,S(c5aa5963,a249a9a5,9445c0da,9537d482,dcb6ed9c,8372e455,4d440931,3c3f169,7e7f9c15,4ba213f5,981389d6,22af8694,b1efbdc8,18998923,92c9d654,92ae7fa6) +,S(bd04e3d8,5bffa429,81310c7b,2c885f04,69f01977,2819f6f2,5e69621e,5a32752,dc4da51f,b88ed097,b17358d3,da4fb755,254d8ceb,5b0df72c,9dc995b3,8e145d9d) +,S(f8f55476,4418c668,3390b604,c9f9f452,1ef63756,58000c49,d3b8b1da,80261dd4,9943a6e4,1b928da0,fad12667,cc997ea5,6af49db3,8c499a2f,a2343efb,52ea450f) +,S(77dc24bc,5f32159e,7c3b6a3d,168a6698,43b12253,e403299f,b4fa3db8,74350dec,240d860e,c38457ab,6d741edc,e4fd8c51,67f75733,f12fe892,aece4f9a,578c6bd5) +,S(9f238f7d,588d53f1,52da8118,2691194d,99a56fee,373745c9,aa2a3a7e,58d18825,dff57ffa,c2012315,ad6516d6,92902476,cdb2d3f1,58803c3b,4dc854ed,8ba9b756) +,S(92f95782,a59da5c3,a12401da,34c24ae3,fbf3822e,355ec948,6020ac04,3ebbbb6e,1c6231c4,e09c414c,e0d3cbf,9be73bbe,4f1fdd,b3d65a41,c716f0f9,a39788db) +,S(1b40dd47,f435579,1474b560,66a0efb9,88271a1c,19186bcb,b1715196,84f3130d,9fe6dcec,991d652,c9e13099,66b0aeb3,53869f99,28ebc955,6c64973a,4d983b5a) +,S(473f6b8e,d4162f9a,2f40d48a,8827f79d,180052e1,7b28b240,4772fe1d,64cd82d3,743450df,fc8315db,72c01f1b,21c4eea,9c0a39e4,c1e92e33,91096576,37e97fe2) +,S(40cfc629,a550b437,9e0941ae,fa8abc6c,8b492155,2045a8b7,560262a4,86954932,e627bc57,9c84859b,d2a9ba9c,9d7a69d3,8c8f06d7,ccd9e68e,fbaa0697,655a8a23) +,S(4c91dbc9,8d2d2891,a7861599,ab55fce,943755f,5f7c909b,3f0f85a2,bbb75c95,5e541495,ecf13109,6ac3aeda,5324fe5f,6f761ddd,d2972fe,812242ce,12fd23d5) +,S(1fe3c538,ac185172,44461f82,63961364,c3c74bc6,9a91bb4e,69973971,8e729d23,974c02cb,d7d97c,65f89b90,327e6dde,e627d96a,275c869f,22c14bf9,6be05389) +,S(fbe873db,56579349,4ca9c844,8a6e0dde,226d4e4a,2f210661,30fe1075,bb11dd6e,c91061e4,d03f9f09,21f56d76,eb78640d,4df80464,459fc41d,4ca33d4b,5ebbc5fe) +,S(b58277a8,3a6d6779,82af314f,7a41a69a,b1744e6b,1cfb4803,f52a25e9,8200957d,c8faf7cd,59e43256,33403b90,7abb1b83,d3f0cade,ddb9884d,77c5fa54,b55fc51a) +,S(3949cb,e84b7790,a7a110d7,9c41d548,d379604f,ec567cea,e3aaf0f6,fd0d9b49,cdfe904e,56a9c88a,76292c3a,5da21100,57795a76,fddc1610,e7efab84,750617b3) +,S(6ee9ab2b,537aeea0,bd8332dc,d810cb7d,36f8f7f3,57be2bb3,5d84e236,f1bca531,d75518a4,d975b0d1,e2780851,4b726626,dfc182a3,885900f0,4d82c839,1c64722b) +,S(c2875d78,3e15f959,33bbbd48,8a24c65d,2001f599,3536fa83,8a3e056c,20a2eabe,e5870c25,20adee27,83cbeaaa,cfae7283,538446b,7cfd7ded,12e68ffd,3f66de8b) +,S(88e26c2f,dd475547,8795e8dd,8285f73a,dd9eb9b2,68c1b16,65f1a701,cb3c81e9,d00344e1,9d14b3f,1dcf5c7e,8269237d,a2af90ea,93a9e24f,3a0040ee,d3f171f1) +,S(ca8e8c50,660f3928,3cb72105,43710f56,9a1518a,7948689d,3d7e2ce7,a038bc55,57600213,1892bc9b,5afede4f,e71f5ec1,a1516039,ace088f,9a851278,c52b2487) +,S(ecb6e53f,4330d0ec,b9aa3605,b6e6d209,222d611,72858b1f,d2663797,772e0249,b054af9,4c535d01,bcbd955,61770aa2,e08678ea,cd8de0db,d8efcdf8,623e9eb7) +,S(a011ad64,55123316,9916b20d,ca6efe7d,e78c6b58,feee97fa,7f9e3fc6,8d889cfe,f53b71eb,63f87651,aa0de66e,a51f56f7,c5e3ee26,ed4ccdf4,1978d7b6,f0191277) +,S(3ddda2f4,ec1b4020,31a73abf,ae69dd42,8d801bb3,fc96fd1e,b06334d3,cd7dc27f,eb1b6b4,137ca36f,5b160112,38fba958,32c5e5b0,aa5560dc,39600db0,624b6c1a) +,S(13f5b643,e908dc91,415ff856,76357687,bb777f21,3972cebe,e8157828,53982d8e,bc2d3402,f3af6ee7,bef0ec2b,73c75e53,b70309ec,633d97af,b9ababc0,62d20ec2) +,S(ed9fc1ba,60a7db8,93db8148,48790e52,4a246279,d938a04b,cf168ae5,71618e2e,b241c920,ab995cf9,4ab1b707,b06bbc7e,1cf7b8eb,80325934,d57e9e9c,6ba08459) +,S(89d4f6fa,f6252ba0,d036dcb4,54fa5f1d,311ee828,1cc5ca85,73fe7607,7b16fadc,5f441ff0,56fd4e24,ca4c9efb,859d6e16,2e4daa4b,a7f3fc58,e42a540c,23b6075f) +,S(4acfd390,e8a1b430,9af0c28c,87f51308,a1ec5741,8c194072,45c96c79,30498a3b,dde7b57e,5e5ab271,78f2ae5b,6494a578,2757f508,bafbbe64,30b96bc4,96728d2f) +,S(8320cbe,fa7483dc,4f5e7b7d,d74cf492,b95b7e31,b1bdc651,6595b605,eecc134d,e766291d,13959f5,805de8f2,aa59cd66,8376d93e,4b775010,426db24e,b2ff41f7) +,S(fbfa6a21,deb1d183,cfeda065,687a0b96,6d8e50fe,475a17a,7b690019,6e7c696c,f5901c10,5f30f5d9,75530b0,3f943caf,8de85a12,f54e4c7e,5685184e,8bacae2f) +,S(70695a81,45fad213,aa14a7ec,d3c84f51,53e3906d,5483db4a,c6e8c229,ecd2c3b,febf34ba,9744a37d,6b91335f,c735c426,1208595b,3b8d048a,3fb48e76,2008e90) +,S(89fd8cfa,8fd5bdda,b6a80bb9,96ebcc92,6074a15d,371622b6,1cd772f6,ae1ae9a9,85c2cddd,a8065ee2,244b3d35,61e9e057,87c6cc4e,d4b1ff59,53b736cd,d3876108) +,S(342256b4,cfd8e5b7,5f5d088e,3a919d64,ccf38827,b18bdd00,2ee9a6c3,fb4dad,c54f4704,f3512e55,4d96ef92,3005e4aa,34a157e5,8bd6b180,e219dd51,16c4be16) +,S(cbd38ba1,189a152e,bf78ea23,3eccebc7,16f4c28f,273f5c8b,ec9df397,9dba0dd1,d3df2ea,fa52ad52,9089c7c9,581dccbd,a2a0e060,7fd4b902,cc915f28,717c2d29) +,S(5f7ae38f,61dcf176,45affec8,7514c624,85b880ad,38df28ae,b695cac0,fd1d27ed,9b955855,f74cc040,63cfa4d1,fd098e5d,e0030c4c,6dedee41,71bb5e7b,c0939226) +,S(72e72eac,aef492f2,f3c5ce7d,dca58f40,c919a194,3662adb4,3d855a8b,532d194c,d7b5e558,a0cb091d,3b39407e,1dd58b95,30fe7561,6b4c3352,bfa51320,509d2d7f) +,S(925a19f0,c73d85d5,674a62b0,2d5496c0,5868fd41,a43696ce,eb153a0a,2e5b8051,2ff229b9,6c23cdd8,2bdc56da,37495c29,78fae40c,b429a1c7,3111d392,3c5505c3) +,S(baca4fb3,33bf98dc,165cb9e4,3bec920e,bbded256,e990820c,8f902e84,3f6e02bf,af304697,137728d4,e96a38d7,95b830c0,6c18579a,9b46f4da,a94a13e8,d3a0341) +,S(fbe24c7a,53b90f3f,918b92ea,e58ef633,bb72e695,2eee6c6c,2210f6d8,8da9e0df,1febd844,af3d057e,c9ace544,d5fd57f0,c125c55f,1d9df636,4f6be8fc,bc53e3b5) +,S(73b7de54,ae8a38c6,ccde88c9,3f526c2e,54d823de,a6415488,5e864a2a,1b0574,2cfa6943,634201d8,9438e6c6,fbb7224b,c2e6b313,538523f3,305c75c3,5c68aeb0) +,S(84960402,31a60cd4,cd623d4b,80bc69aa,7cfa7c0b,52cd88b4,2f22ca66,17151dfa,b5324b5c,af293ece,38deafd0,76835e10,469997af,7dcccd89,172ad20e,f36162a6) +,S(b6de647b,98585f7c,61f0712,76c6f62a,40926888,b1bab4fe,ad64098b,4b9434dc,f7180d81,be634f34,d878a645,2c662e8a,db9c18e0,ca7e7c13,8bc51e1c,1a5f5c0a) +,S(e062011f,da416281,bc296718,300a080e,fe7ab89d,8469e8dc,fd50680e,12dd9348,c33030e4,1ec1b1c5,4ad01bb9,8c4e2c48,32b2516b,1b2b3b22,adc4add8,1b690787) +,S(f7c7545,649b4659,49618dea,7fd15a06,1e7ee032,5d955649,b76b66f0,534a5501,2a929b2,a68342c1,81b2f55d,6b543584,ac06077,d95ad8f3,fd3f3ffc,24f56a3f) +,S(bd90ad48,1694f297,6506e2bd,e97d5803,b810f9ab,7479ae90,dc1fb146,38d4d5a0,f973b70d,8dca64e0,c9384b7,ad3b4ab6,4d3c74aa,722f9284,a9da82d5,5c6d02e2) +,S(cb5d91e6,68b3c202,b72a38c2,f98e6e4f,14bb566c,f7996d9c,c08aaa07,210b1b1a,68e9920c,597b633f,9bfdfb8,72814422,4ad69ec1,e435e843,73a9255d,79f52d7e) +,S(d251bb9,83bf849b,8f058ff6,32ac6bea,bcd12649,4fea947a,f943cdf0,6e0c240b,5098026d,b8ec5c54,8e3ae103,f0d172bf,e84281aa,b8c0b131,16295587,ed4500f6) +,S(27235299,c1f8d73c,268bca88,62d2b6d6,635f151a,d148c662,6c0cd5cc,1a3d2234,5b03cc60,f9224aa3,10d8193c,74e70cbb,6513ba73,d6d98ad3,2e1c2d,7f78ad52) +,S(1cf437b,aef8e007,ba02a24e,735d8444,698e5edf,2f9d41d3,cdb28cf3,3dd53851,e3fcdb74,b580b8ce,8f6edfe1,c276c478,da11323b,4e96862b,926ca4c7,f739a8f3) +,S(aa872c85,70e9449d,6cf020c6,77669c8a,7ad408e4,d9fa194,99867c6a,f77df918,572579ea,3d1d9575,e60a79a0,7a2aed83,4fdfba52,61b962ed,42e59322,c02fd873) +,S(b3224a2e,80c3a174,66a9994b,717b4c37,424f12bf,9dd3f17f,6eecd801,3ee2ed56,7ad1cea1,128c1605,552b8be2,a5fb6bb8,907e4290,d76bc82b,e17ecc32,1be6fa43) +,S(29b8f73a,7fdf6032,4c907022,b0107c72,ed951eef,29687feb,a8de56ab,4e47b8ad,19b849d9,bbdcf00,a6bba6b2,f1058695,4f286884,9b5be608,d6b4c357,1098f54c) +,S(3c2f5680,96f946ce,3d048364,dce939b4,a05ae9a7,c31c2a11,d2a29a1a,3fed06e2,47701685,b698de43,ac515062,9c40a15a,bb952a0e,bdd96,ba4a1b4c,5f61658b) +,S(b80f00c1,ec0d059,c9f5c69c,10cfcb26,651716d9,3d0b5702,b2c7b295,85af4c55,aac197db,ab42141d,31f63425,5df908fd,fd026d10,1263aa7,63d99414,adfdd02e) +,S(66af071,6b78b854,16572c86,fb3e51e9,b6d66ebb,bfc2a2f8,dd664010,83c83435,b8b2ce8e,fe794b13,4fcaad05,821dd884,9a8318db,614d858,8dfd4d2d,65314398) +,S(7ce8342a,6ea85f61,559cd2ab,e82debe,82eb5aca,142153e5,320562a7,6ba155c0,8a930243,5ebf4269,d75363c6,93ad4255,59f493e0,e492150a,9f110c90,8d7750a) +,S(cfbc37c5,184fe206,7f7b5c6f,c5e50adc,ed58ae61,d9059bb,4ff64a19,f48483eb,bd567b19,3da2b2fd,bb8941a5,be9fc72f,11d9cfa2,4c4438c6,a4db3091,9c8867cb) +,S(728c27be,52c5a742,859774d3,2d33fbcd,41e997d7,25d6be46,af480c8c,f9ec436c,745c7738,2a833cee,19808d12,9768c213,7500e8b0,b140e3b2,94abe97c,9a7c73e7) +,S(4553277,fbb30c09,a1ac1ac6,cb7d38f5,88cd5f3e,d4ebf6bf,5b5f6e78,12d47d5c,3ea80bc9,dac9f1a5,ac4beedb,d254cc15,728f0ee1,6949ce97,53f1bfbf,98e3c78a) +,S(7b752d90,9fd6d063,f1ac0f22,d8cf1e6c,78a335c6,ed11743,88192fe3,cba54cef,a252376f,e482ec59,2ac440e7,3aac3b46,a149e35,1aac2d51,244ffd93,f51d11ec) +,S(e78e796c,eaba974d,89d344f2,b610e84b,d51443bd,1fea5422,d2bea957,5467a376,13f0797,376bcf76,b6ade4c6,a95b6446,962ff925,b32fef9f,4549aa1d,e64e2b1a) +,S(1b7a8c02,83f25ac4,cd4a841f,4045035f,92a39bd2,c1903c90,4071eb44,5e8d702a,1843f757,7d03b6b2,22bdc28f,47c7fe6,fb83b25a,8d795b46,9e3ee9d1,ccf7623d) +,S(90d06a90,6ad81dae,20cfc2fc,6be85b2d,41dd9f23,63e13423,c557c4c4,bf3e5e4f,6ba7a127,58861784,2d7ee3d5,9b71ec99,86a72ae7,71312739,79544095,1e5237dd) +,S(d7bb4e18,ab61d8f1,de4b5bd9,9a5d03ef,1e0185ca,55ef14fb,be1f0b5,4cc4803f,311f268f,a672975b,acabcab5,1dec5754,48eb5a82,685e339e,e23b41fa,112873e6) +,S(6d5f1808,f17e0b81,a6bf12aa,34032644,b708882,96e99861,fe5ba4c5,5546c44f,8eb304c8,92cee7e0,70649d73,ae76893a,610504d3,fdf37af0,92d5dc7a,9eab1094) +,S(e4f62cf,9835a4e5,c2b7a456,596498df,2973192a,af2c80ef,c036c71,69df470b,874a48df,1a25a003,ada8939b,f727e0ec,b7d2e4d7,1efbec4,7193a2b0,50ecaf28) +,S(7fb31d8d,b3d5b77d,4aa9d0d2,692013ce,c82ccd6,83a2235d,848037b,dc6bf8ee,ed5cfa61,9fb4e350,dc37e2a5,2bb67104,90dcb484,b19af368,8101e59c,f9f0f0a) +,S(8887db9c,e3eddeef,ece414a4,f46ff048,3c4fd25b,14354869,6c60871c,5eaa9ffc,50294d61,1040a466,f826e654,3fb93f94,b4d60b11,d589287d,6ea33df5,a8dcf366) +,S(9d9d224a,595c843d,1f70297c,13b3645f,385eec97,80fa9e50,d27115f7,b677aae7,af9d5f61,24594cd9,a3aa7100,ae6f169d,dfd467c4,1affefe9,c9a0a5e6,6d7f2802) +,S(743d9b40,527b23b8,f165b6fb,9d5c14ef,450e1ce6,94439354,ab501eaa,2f34137c,6aad08c3,bf602b52,7d43e3a6,e7efcfd7,f24222fb,f8213f25,e8195501,9c8f0885) +,S(63c850eb,f0cd6fc3,3bb7a4f5,6d43c389,1f93b11b,5f67e3d2,f9b7537e,e5a951e3,29d214e8,bde33612,a5a160e0,f7cf3acc,3b83686c,ec5d7c36,dd90ebb1,d97c7ab5) +,S(b95608d9,84c13243,63809f82,b42cd16,fcf3c6be,5b19ccd2,249016eb,d93b4a54,41aabb2f,e7083dfb,ea380fff,2467a843,c88ef6b4,9740e73b,33a088a7,649fb0ef) +,S(8361b632,5eeafafa,f01e3624,6bd8f0c7,aab461ef,cb3484e6,c1ee871a,7ef95114,34ac956e,791cef3a,baed24de,7f0f59e0,bf34dc75,edd10159,b3870248,255eef36) +,S(807109d4,931731c0,a94127af,cc98e487,1fb44467,6d8cde89,88dfc252,868bc853,363e3944,be00a734,4c50afc9,cbb43183,f56eb20a,c0a5d9ec,74857484,ed9e1d61) +,S(25ef876e,d1654570,244ec258,e7ebda09,35b11784,bf5afb7c,8652e5f8,bdb34055,63459bb2,b8affcb4,1f6ad9b,9437f3cc,6d8082ac,d400928a,29f11fdf,503c8553) +,S(1aa87b1b,54eb6bd3,5fefb162,b27ad9dc,eee4b5ec,c1b69d4f,1fc6e230,d1848435,e5cd86ac,23c3790,369d7fcb,2189098b,a4fb3007,f82e5090,22e8ce5a,b44258d) +,S(cce2b0fd,9497190d,e6ea068c,98bba7de,a8f50aa,12930b4d,9060808c,b1ce6152,b6aea7ee,becee274,2bd8146,d792a6e9,ae75fd79,bb8882c8,11135d0f,8d5e68c3) +,S(fe421506,955b7cf6,f6a84228,30f7c6fa,95e4576c,da711415,5a49c238,e0bdcd4b,4e4a31d5,9b70b51,f73a4d02,eb09e4cb,7dc74a29,bef959ed,86558fc7,d4c8f401) +,S(cb26c118,264778ef,512f8acc,385e5ac3,10b489dc,88991e15,13665b7d,587d713e,7375bbd0,9fb3051f,a907fee6,80683f93,54403c76,85ca7eb,97a711eb,69033c6) +,S(8f286ae6,171d88d7,d82cdc28,93960bcf,53768c8,9cf1f149,fba0acc6,f5056014,6852d427,a888c5a2,4e748f2e,e482d38a,42879db2,2e824d1c,d72cf15b,8e32b085) +,S(b7af4b6d,7eb4eb03,e827f099,fb8b02f4,bdca366d,811d031b,ae827941,235e75a0,7cea697a,e8cc53ff,8c738ec2,55a64e4,4a499643,4bc5e16,422ebb9c,17d3dd12) +,S(c8471c9f,53eaa3fc,850ae514,dfa1109d,e89c500b,e965682c,6a4bc2f9,105be1e0,7d5f32ce,d1e758ff,e38325b4,778e68b6,f0ae5509,fcf9195,c1a79d41,57dc519) +,S(8d192ced,73629184,ff6db80e,b2c6df4c,a8ad77f6,dfc98c52,9406e7aa,dd506536,3b01ec04,ff45b037,c1444255,9cb2f629,7b4f8cff,e4c3eed6,3b95d4a8,88fb2137) +,S(318aa156,f4912dd9,7b90ae6d,c29b0f8a,99f616a2,5b023f08,ad35a8c5,10ec5015,b398cdd9,5ad52ba1,d7038cdc,67e36c74,5625fb94,f62b1b9f,aa2eb5dd,be6ce52f) +,S(b7fcf98a,6a385f68,a1c6f9ee,c6f069c1,81b0c94,df8c05ef,b59b49e3,aad66f54,912e2098,e3b6e44f,46eb6382,83f554e1,80af035b,16c0eb3a,4e000cb8,d4ea2da7) +,S(599b7a5b,e7a7cc6,3e0b66,38171c0d,41d78efb,92a9bc8a,1d0e8f48,b2b92fc2,afb3343a,1983a867,c8e196c5,92f745c,9060330c,1f0c0509,375e30de,3a214b7c) +,S(c3578705,3221f18f,69dbc241,f927ec48,3280ade6,44c70129,2b30a11,a296db9c,2b75b2e9,fdef6060,29633ae,481ba7dc,8ca52847,d4d0a201,9da74056,40a980bd) +,S(9e2020e7,cc4153b0,adc2b1ef,3cf607ac,305a480e,3a8854d1,1907fca0,4811fa7,19153a83,2c9a0612,dfea182f,a855a039,33035757,2e98e5ef,43f26699,3b8bc6b5) +,S(1e0c4c68,6e5242c0,6645f4ac,6ccb18c0,82c5d5e6,c89322b4,3b6079b6,ccb5a5f3,63c4a4d7,3ab71f2f,c49f5532,b543e381,83ce223a,c8609be9,d6b72981,8b5dedfc) +,S(44b11683,5f922e88,e7ed4328,cfb212f6,aabb6199,64fbdf3a,87dc7f24,faa761e0,a9181b5c,341cd78f,2e1446a9,e99acfee,7aa5b91d,4c7da20a,b7cf200c,f470ffbd) +,S(940baf24,717ccfda,e539064d,f6e4b670,2ad8b60a,c32e38cb,fa4eadf2,6a7bd3e2,bf5ab95d,2cd9b68f,cdf535bb,84b967a3,f4fc44d5,5e6d21af,d09e1de,f5f13ad5) +,S(4191f800,85767106,a9e5fa6,8521366e,a3adbd79,ea315b93,9e63f797,2995e7c5,662791d3,407f50b3,7a68e15f,5ee71991,2e893099,a907db3e,f818938f,9c3bb2cb) +,S(1bb8ec05,76ce9cad,f96b418d,cb5c7d46,9b49ca81,7f253b15,8b26cca1,cb9a472d,762e9c6b,2335769,72396f,36a56efc,f2618754,f1c30923,30b521bc,4e0198b7) +,S(c5216da7,91635b83,e55ec5a2,d9f3f8be,b1a2b0e3,cb097528,654c50a3,315be78,2536358,e58ffcbe,789d34e3,25d9f59a,2210c247,a80eac8,c716214b,9d5e520e) +,S(7b1488fc,83a02d7f,d12b9eea,555922d3,f4a9c42b,52f030d9,bdd39156,f0890225,ae7dba7a,d95420f2,8c8185cf,5aa2b777,b9fc5c14,b6c47048,d8ef071a,77dd4397) +,S(e245e4f9,7d6c69d1,5e244f4a,668afd07,d57fea2c,f8401b2d,8cfb6ada,f01f8891,88a0648a,ed5eab1,d57226e4,63411b9d,3632537d,699f39dc,7d7cbe5,49e76900) +,S(ed78f3d5,1f4fb987,5918a0cd,d6e5280d,6516f5a5,acdee916,d3e7c7a1,d757d188,ebc569ce,9b3cd325,528cde0f,9a2ef54c,d9f920a2,1ba7785f,62ef40dd,9463a81d) +,S(3e8d870f,bf768b41,8264ad34,89026fe7,8870e201,3f98696c,81dc3b7b,c0b1b599,28558e59,a6a6c255,a352cc9a,c20508eb,55af7449,8d75ded,c94fe414,fadeb218) +,S(96125a01,8f901790,fe25b41c,f2f7fc98,50cd30c8,cbd05ffa,cb76bc45,94d5f5a8,125db476,3fc8ccde,492e7eb4,45873147,8f8ddc5b,6c2b588a,b72d9b1b,fcd4c43f) +,S(779d9847,6fd72d41,937fe1e3,e12d4076,5cc61f41,19b37437,3919782,18a3dfa0,75a3aa4b,95523e05,42f38dab,b2d80895,cf3738fe,5152c8d0,96a587d5,90442891) +,S(681e390c,323224a0,7ccd9520,23c9d89f,4069100d,3b275c5a,e773efe6,170f4e4a,a9bff0ee,334bcf6e,a3bc44e7,5d85acc,f753a471,ac008e0d,47b19ac2,476b184a) +,S(d7b8530f,3fa18d18,ed94b938,2906e431,44ad5994,5eef1d53,e3290887,5c4af2b9,2efa5bd1,acc4ce6,73b8c76a,beab2e87,6399f84,94a1450,bd6833dd,debddb92) +,S(1277ad75,6e965ecb,c8c04ea6,c22042a3,ca3e71e0,74334012,7063d3b2,da257a5f,38c90310,ee2bd94e,5f3149f1,f38d065f,8df110f,92f126f1,3a69013c,515134ed) +,S(4e66e596,94c521a1,8114b232,427dfb97,8c7f14ec,7b5e18e8,9c11cb5e,6a2f6f65,47d553c1,8a858b59,f1aa8f4a,51803450,c237fd2e,60b137f6,d13647e7,6683caea) +,S(1fc1e8bf,3eae2dfc,b141be5e,ea612378,209ab1bd,7ccce45c,d51fcfb2,8f192aa3,5632d350,ab6b3da1,599779c9,9cd8d7b5,e317ca9d,ef3245a8,eb62af95,ef769c20) +,S(7eca2d92,8d2bf4fc,ed1a8336,eb87c5f7,b864b842,b3057c8f,97d5dfd3,76771d7f,969da039,7a37997c,c243d448,90d8110e,fa580359,bfb35342,bf69dec7,4bb17337) +,S(2531025f,8fddd665,cf34baec,52733303,28e47e4d,827248ad,9a1e6c4e,be3db9a7,e404fbcd,d180b2ad,931b4f61,5902eb06,cf00f66,6f1323f7,bf0652fa,94dff64d) +,S(dd28941c,bf6e4bd,76b4bad7,88f700a6,8c4937a,67958c3,bf6388a9,d5e26441,cc45f07b,9ea1a81c,8d89af84,8883ad1d,53ee8a52,2416c445,c4963791,7a366909) +,S(d3af9e12,53ffb85,ec7829a,4d41d704,1ea62952,4d7452a8,d9ab6ef0,1d6b509b,9c115e78,91b383ec,20739d37,6450a2e6,6f332e5e,c7a3a97b,2eca8c0,ae819bbf) +,S(2c59063f,13ac98a5,ed196aec,4f2aba11,c8647d78,42be85f4,ab87a007,2b49001,e9738418,cbb6e31c,f588a3b8,70883f90,ed3bd8e4,86e3c77a,37fd1152,c22637d2) +,S(6c0ace3,f182c963,45deeee3,c41c5e51,8dce984d,3d409a89,6d766ea1,b1ff0e0c,17e09dc8,dfc53cb4,952fec4c,92782ce4,3515544f,6181bde1,1d1ba1a,bda709ee) +,S(f161125f,3a229d29,2053fb9f,6f118550,43d80e33,9bd45af8,c1519043,2098c9d9,1b442ffe,a186cf2c,343b90b3,66a3a008,e08edf90,f073e740,abd5d20b,8dfa1327) +,S(fd081f05,6b219fef,56a3fc1b,6dc7873a,fb054b40,1b3ae0b2,414384e0,9080827a,9711912f,4ceb0036,3e5921b5,c2f7ca49,387b78cb,f1b9c2ac,9c5d5d1d,1bd8203) +,S(72c6eecd,d87657bb,ffc3a03d,b6e1f9dc,83252d2d,c73ba3f5,8642fc08,fee42b4e,282180c1,7737b51e,d047cc50,64d53cec,6340eefb,d969525e,a1524f0c,a305e39f) +,S(10df231d,4582143f,1ad7fe00,508cbbfb,3d49775d,f13dcee1,d6155768,c14bfc51,b7743bf4,e7cd49f8,88cd0d26,ac196037,e1da9402,26518e07,407bad1d,20af7dc6) +,S(354e8b0b,e40bd9a0,36620006,744c0913,5bd4bcea,34ab3371,9561e36e,f465956a,3e7407a2,2b9d71eb,a04239fa,600834d7,5f643c8,b4ca6308,475f6c9e,8d35fd4) +,S(2ae99ec0,6a4e080e,879a60fd,a93a47df,9ba7e0f3,b87a6772,be6d7e23,9bf2c42f,3fdfcc57,6a36adbd,52467e01,f3490fbd,82766cea,b956f80c,7b1d29b0,5066c31d) +,S(f47ff09d,5e93e827,72e73c7a,8f1623e0,823b09bf,86ba3023,a223a680,fd849bd9,379ad3a3,cd546d27,35fbe5d9,97c3dd8b,8c45a3d1,ce8e9c49,b23bbaea,e93a08ea) +,S(252c0934,acc3a624,c4d8b409,111b248a,2242dba4,7d57718a,aedc9cfd,20647cdf,17b7a015,a2ea993e,89a7161b,f50124ad,561ef131,609981ac,fc92e495,cd2ec91d) +,S(b7f6bde6,7cdbbf9,4a3e06b7,dc1bea75,d6247197,a8617a6e,a1f65f44,a7459006,6d6ad596,fd3472c,9d7eff56,943c1a03,ee4f540f,29782f77,afe999e6,14f5e316) +,S(210c956f,e0a741a7,edad58d5,d47a2a34,fface7c,5edad0f8,f4953b72,28350b2f,4d4498a0,892a9a9a,4e87ccdf,424d7910,bf90fe95,186653c7,6e6966a5,e7a816b8) +,S(7ab93adf,b8f35eef,8af99e5e,e37aaeee,401a1936,fc0b8c88,518065d,12f4a74,4f0782b1,c78954b,8ce37bdc,7e533145,3b885d92,da1dbf85,940da9a9,b7f0dd8d) +,S(bbe1b787,9c93e7cd,891f628b,c9a655e6,f4c7cde4,fa799596,74978224,9fd05fd0,493d3dd7,be33acf1,38ef5b73,8eb8f8f5,4e270323,8f70078d,5f7bd9e7,177088cd) +,S(61f42d35,ffcc6151,f8c93b21,5df0cab1,fb804cde,f9c68818,fc4f413,b804942d,46ef1e12,d6add082,3917a758,9c137d45,c38ae2ed,eadd847d,aa574e06,18708396) +,S(18542b75,3e78191c,b3dabcf,f9c64a8e,5e4b075d,d600fe60,b830e732,7fbc7b40,6a816e01,41b17e86,65ec280b,f6da9dbf,8061329b,6db9d13a,a9f10c0f,41340118) +,S(ef83d0f4,a3d2963e,d14e708c,6d22017e,aa97b2f5,23b06f1,140b4105,459781d0,5587e5,13725819,1b10bd40,e616e06d,16a50392,7a8b1394,a562c396,973d98b6) +,S(fe73429a,7714dac6,ed0db327,d6949d43,30d2b16e,a513d3b6,7b74452d,78a82a4d,7079088d,fd828815,ae087319,c232817a,4928f743,843a4966,e9411276,d16214b3) +,S(2e8b8a63,3048900c,4b82ff8d,fd86a971,86e3f1b4,6206b252,f06a8c85,82555130,74e10dbd,ca84d796,b3b37ae,b32624c5,2c5aabc5,d4fcc5d2,21c1aed4,71c940b) +,S(9f4164f5,4f34a59f,3a7be378,66a8204,def2fd98,8dae6dbf,5d471e0b,caab7017,4b662fde,bf2615c0,8c7b53d0,b2915d50,ddd2916f,a3599d81,bb2eab6d,80e93f57) +,S(c6f3a26b,adab6699,aa08fcf4,6f0ac900,2be3597c,16133225,6a5e6577,11b16ecc,f29db1d6,579d0e36,4528c297,36019f4b,6e8f060d,ad5effe6,d357d709,882a250a) +,S(76a4f5ef,6a869e28,e6494cbc,ffc6eec0,1ca983ad,1b8219cb,d2b84eca,96f8b910,e1d8bd7d,55a297df,32ee185c,c60db286,a2e46cb1,312a8995,5923a068,cd3318d5) +,S(d2c25c8a,cfaa0b2b,3f6e70d5,bf095700,68102f8b,24867970,ef3c8000,b2212d1a,bc907681,9c7926dc,bf81278f,7dbfc4cb,292a8071,177ce8a4,f4e9346b,a2d9e500) +,S(b2fc2ca,5a30c678,61b0bd22,5bf2c088,26d799a5,9c9a680d,dabac9aa,5cb624fe,d4abc7c9,fab915d0,fe9dcf59,a0f83ca,56b27566,6e9841cb,b5b14874,5f6732ee) +,S(110ef531,7824c321,ba7fa94f,ac98de3a,c6cf44af,db92b7be,1006c751,9b425065,cc3daa20,6c0eb83,6bdfe640,1f7461a,261e7233,c70e615f,db82a991,77b83cb7) +,S(fe21a607,7419ed88,12d16ba7,5485e01e,3c063359,f95a8634,3aab1c84,ba6ac00b,d6e08c98,4b7a5282,bc1857b8,82604fc0,692a2fa6,40cf5a5c,d298f344,54c836b7) +,S(c5e45275,ec85bb32,edd32702,7d7d87ac,8394bc51,722e0bd9,58281e61,c454fcef,9b158ee7,89034035,fec4f06f,2fc81755,59f9f762,38742715,92ca6ff9,e5712a8e) +,S(cc68f3f3,36360481,5583d65,7da51968,a4a44a69,1264bac8,289791aa,dc7a4ef4,9db5cab6,7b31f3fb,f3b5fde2,273496f7,93316358,6d14e80b,bbca4613,319df9aa) +,S(6d33be85,5fe8bade,89109a3a,8c089aa3,f7e9d178,969100b7,eee7a693,d83ace80,9312736,87c08455,c43b4cab,afe71408,333fbd69,d330315a,3fc9a52,a9d473d0) +,S(5aa581c2,fc48c38c,2aa2e176,c12f12b5,4810ff6f,862fa17a,196c06e4,812b9e3c,adbff08b,bf6fd5e9,9f91f9f0,9881cfcc,a7b5c2af,709bf838,6055079b,2f75f3c4) +,S(12eb2dde,dc6ea4e8,dfd43067,9d45052b,b882e4d9,bf516b,61edaf00,305fe883,8f948ee0,f8514f9a,1bd8a71f,20c95e4d,e30caa5b,bb3a383d,9561f027,34bb1950) +,S(3d0d7eda,43045e9f,62dc680a,be47e933,74a95d59,945b69cc,60acb89d,e40dcded,45c53d35,e369af07,eb223a79,1d148ea7,1875cd1,8ca066d3,399743f9,65e184b4) +,S(173d4741,5803d266,7ea94abf,319c5917,204256ca,e217647c,c024a9b3,be7b70c,2da6c348,cb066462,eddfe8bd,3dd00ec1,34a0a060,4b7e17d7,e6cda389,dd4d5463) +,S(10659655,e2f1ba5d,db390184,c2d6d1df,1cd2abfa,4cd4bc18,c89decc9,5b5d6319,5ac2793,3a88870e,d5c7752a,459ced45,69435a09,7cd0e030,25608cf4,ac2ae267) +,S(e462be7c,380d7bbe,cd45c6dd,8fe60219,404b0c9,4c5e0e4d,31a23064,2b5b95,872183ec,1ba5b854,1f3f99d8,ceac80c5,9e84756d,cae2e694,9b43ea7e,d1cf752e) +,S(8200badd,1df41faa,19b20cb8,efa424d2,2f39b1f9,26b692c0,2a0ef6e7,feeed67c,8402fa9a,6ef0f83f,42546b05,360296d3,74db551a,d89be995,f2c11866,5be528b3) +,S(760e2457,a3e1a165,526e9fe2,2b5bcad6,e8107581,337cecac,e2ae87dd,a3c54592,c5111ec3,24158a21,56b897a3,82bee2bd,de0f9df,af9ef611,a7fd3fc7,4a635b3e) +,S(ba6229c7,1729cae6,5275719d,9a35691c,f260a003,168f1a25,1ac00b09,aa17872,7d281600,6bd8981,82126d21,70fa0ab1,d5dd0c7b,cc377a88,c43cbf61,70a633c3) +,S(a2cf1392,571e758e,6ec74708,dc45f70f,90833ad4,135e7a34,9d2810a1,8efb5216,2420ffdd,afe96506,9e6f9266,1e3839ba,c9b1db40,d40149ae,3acc8683,86acd175) +,S(f8822204,27cc4b37,33268925,fb80beb5,a52e5391,b2c8e31e,f6e99592,1775af20,6cf6eb9e,b19825da,6edc5d2a,66f3987d,c9eb01e2,b70e6395,abe2903,d43acee) +,S(eea28950,fb676991,444f96c5,7e42ea16,aa380e3d,dde63111,fac35708,a4e45b3c,34d30722,98a686ec,7e1a8095,4e2911f1,5e11d426,ecacf11c,eb45a5cc,7204114c) +,S(7f252a72,6f9a1497,f61bc4ee,7b194a31,432c0178,2f40b16f,2d36e9c3,7279e25f,8edf25c5,f50c6f0d,f8a17f92,611bf024,14e02d0c,de38aced,e96d5a7f,4467c449) +,S(d3da23fa,56881cf3,a42a50a7,42e1067f,54ec24f,2de19921,b1ae7f15,fb90509f,b4ec4b17,8e555cbd,15210f09,bd1ab43,28e458fe,6aa3e800,44bf0093,ea08f90c) +,S(6752f294,f8ad85e9,60b6a88c,cfca7874,5c80ed7a,93f6f05,3782ac73,d96e31e3,976222ef,a6f6358d,42d6289a,4f11bd65,e7e755a3,845ed49e,3c972b5c,5fdb2418) +,S(4fe21b06,24a45b69,7f42aa54,10e6385d,645a8d49,2a61ece5,c8d5b2ad,1fe74efe,f7666c09,7caeea3a,c4b2993b,64f4c0cb,ac934099,8870ade,b7cee48a,c3c2c413) +,S(15af87cf,6dc1acc5,1d323a36,b2daf163,ccc8c201,a92f0677,6a26ae27,7fb9d766,53fe4566,6a975d35,ed20a5a,7bbe63c4,4cf8011d,45e7a171,d5d72e57,5debefbb) +,S(656f3686,c60039b4,4c39a5d3,ee82cd08,e8aa8ed4,b9a943e,42281407,4ab6821b,9f277baf,2da730b3,dacc2a43,512411c0,13ffca65,7092e695,d49c8d24,a4cda4d5) +,S(a13286fd,2c17a61e,6bf8d209,d64f5879,2534b037,cd854470,a09c8c8d,95aa0564,9322d3d4,c735a485,5d76e5d1,fd27fdbb,a8af2fcf,ddf7bdf3,f9e26f97,7f4cd1df) +,S(3b355928,4ddb285a,1eab73c5,6e6bc554,9f40d83a,2b859e01,a30032f3,a5a7109c,780670b5,67755fa5,10fc3385,7745a69c,bbe73e3f,8186b3c1,8a82c51d,5e1d9be0) +,S(b0b15be8,6c2dbfed,6da6e7c6,51c10b17,504070,7d1747c3,ff2dddbb,256fa495,8d2c438a,42750adf,345332d,f02f6c4,55a07fce,afcc51e4,96c270ff,913546e3) +,S(c754130,bd717ab0,fc17e14d,dd6ec50d,aa6bc02c,a338cd9a,798ee62e,72d517db,fddf938b,28500dd,c9f1fe0e,70104564,d1bad398,d3bee900,ff609c8d,f56d3b26) +,S(ee615579,33d877e4,9bb228e3,62766ada,8615d382,c022c333,39f78ff6,5e5d9ceb,3dd38b15,5ce01a1b,3d5434f1,b2e04a26,6a830f47,fe89263c,b0ad1b5e,18bd8b76) +,S(f830dd7,4d03a39,d7f5ba3d,132b40bf,130b33cb,9b6609e2,12b6479a,14287c9,9183a68c,33c2d25d,c237f8ed,b5a63d44,4dd2bffb,d1255631,221edb9a,4c9e9e91) +,S(544fa477,3aa3589d,9e63c8f0,f30cd189,a8b6658e,ba908212,cb1b4caf,2d5f64ca,bbf57d7d,b5c6b8d6,e202a7b9,8ed06edf,e1a9ad01,381da1d8,11405fcb,8ef717c3) +,S(9b270440,132f5b09,6fec6214,dac1e27f,3e6c6f7f,25eb2cab,680c3fea,e0b41b1e,a7e591d,eb990617,6bc87263,6ea19e04,c9a3aea9,59059078,2a757fd7,6d99c6dd) +,S(bbd69c5f,f0b9228b,cfeebb97,32b3c316,c14d9a54,d5a3e9ca,ebb337e1,e33525e4,78cc6c84,8c246e7b,7636949d,4e3f2b94,989c77c4,812b8189,cb857cd1,3457c403) +,S(a920edc1,4ddf5bda,56719c0c,2adaea7c,9aa8aab9,25a5d6fd,41172725,bc65341d,b8afac74,46e560ce,bcaaf438,69dc9287,2805fc14,122cb19a,bb59e51,8336ff80) +,S(3c6e9595,a201956d,ed27e83,41b22822,445148e6,d00b2fcd,ecfdd66b,2405ae3f,2b8ef9a7,d7c37d0,d2b77309,74718eba,16f221a5,f3074678,317f0b6b,7b0841b1) +,S(c35c9e45,3f487f8f,ae5b6010,e1bc1ac3,ebe4ee53,33666876,13cc4b35,5162ed91,7a8e64d4,9f827631,579421ad,3c7f7ea1,f4866432,a341f955,6741c521,c757addf) +,S(8fc84331,a8f8dffe,28f30d71,b39c105a,ec09aca,4876241c,1c81a223,a1953637,aac7257a,cf0fce3a,ff8fc7ea,e8d19606,2ba6d7ad,9b7987bc,35a92b9b,20360174) +,S(b43ca4ce,f1b4a2d0,1a20fc64,2f3e1456,317f729a,1ee69798,4ed88bfc,1c5a2908,39ae1e51,f352c0c9,8a79cca6,cc859eb7,92bad6ae,5318f99d,d62d3bd4,d8fedad5) +,S(7f29c501,75628aee,5d39db41,cc4d89a6,9f133f00,47500f6b,aa7a90cf,e13159d2,cf17c6f2,f6072b50,257f722b,5756d8ad,c7486cc1,d21a925e,8cd29a15,4b514cd1) +,S(3fb93959,55ea2522,61b0488,212e15d6,b28e975e,5ff5f357,15337538,247f2a54,7c3db409,e06d6980,d834639f,e9e357bf,e6a4e9b0,ae6b4fb8,f5e45143,ff625ddd) +,S(945ad871,18199665,a4078a46,a9596bd3,ec7cab84,b6323a0,7ceefc63,d7ba29f5,6eb6d896,a24d02c0,630c909e,65d13d4c,2eacf1ef,6e600b89,1b171594,aa28c206) +,S(513bfa93,57c827c1,b3c0289b,c4ce443b,cadfbef0,b28b32a9,265ab85e,67efaf4d,645c5b3f,d45030f,1aaea514,baba7069,91def89b,56fdc292,4b684241,6653b6d2) +,S(d41b35bd,421ef7bb,1c03c794,477c2851,dee92831,714180e6,6e57e735,caba5835,beea2116,9cf971a5,ffd83596,d9bd77eb,cb1e0c7d,506a45ae,be8c0c7c,22738f9d) +,S(96006d64,fac8f0de,88f93e05,da8e76e6,40afc1b2,8ee09a7d,5633643b,d89ee3e7,1404173d,e96a97e9,ae9bcad5,c9093d05,cba96479,30bfe6d1,96664d0b,13c93618) +,S(ad528227,12257f87,d1ef82c4,4af5f046,648c2a72,497651bc,2327e971,1170e7bb,2ea908a6,c742f542,324be959,4746ee48,4229d497,cc8bd535,e7989525,aa36f079) +,S(b4709a81,c067f6e3,93ef97ab,c26675da,6f01a2f7,e3f35275,e7089ea8,ad5644d6,f655b89b,d6a83838,ffd23f6a,fb944bc0,57fe7f1,1d321a0d,2152568d,91751d99) +,S(3b7bc8ac,5ba85b40,2080612a,84e0226f,3b43c8de,c0e54b6e,ea779b76,812782f0,8405dedc,a02825f3,5a60333f,190001fd,98ea3995,9dc6e61,c28c5e2e,5213fb0e) +,S(2a76b67,d197e7a3,7a38bb10,d49d523d,c4beb4a3,75336974,7e22e407,9e0310ca,6331389f,b9cf9fee,e89b089a,95abca6b,a3c2aba5,38725c1a,6848fa46,997d0e87) +,S(9938e808,77c2baa4,53e0256e,d250439d,b11732de,1abf7555,949a7574,f62819e3,250b061f,bba0faa4,468a080a,fee8264c,f72fc12f,2ac91dd7,13ed92a1,1b281124) +,S(7366eac0,7e5e4573,333291,3e42f111,e53a78a6,5069898,52db2355,de089230,b8dfda33,31646343,bfb60b2f,25050967,e735861e,1654f0f7,ff42ed9,5dd652a4) +,S(10fc632c,cab22fd3,2583599a,a2efaaf1,6e1b01e6,a5a96c59,50871012,33022152,cf53d9ec,c74aef44,7019150f,ddfde494,c2382a07,b2dfd73b,c628478f,7fbae0eb) +,S(785f4da0,240de52d,bb50b1bd,bc53994a,47318076,9a1fe06e,5a40a8e,e677ce63,9b5759d2,f77bbb9,e40ac169,a01500ca,6dd6bd70,6a003871,e7ba4d66,b32db9e6) +,S(a7f20615,593850c3,27aaa06f,c03e0d46,d3ff20a0,9b849810,1759638d,8cbb9cf,13b9ebb6,f0cd59ef,54e0f935,87a14fe1,2337e346,94f5e8f1,10323fbd,d7604d3c) +,S(cf083e4f,e0ed4444,85dbd99d,8e6823bb,483eb749,b94ce672,8671de8,6105aa4a,36eaa305,37378494,17050dc2,8290c74d,d9ff9df4,cbfa434a,2b89de00,e06f3099) +,S(9fb8c370,bca485d1,dcadc251,ee906f58,6f3e177,be37baec,6912e535,f5c7ab0a,df5cf77e,1ad07532,7150136e,7ebe7f35,1db47415,74444b24,e9cf2f4f,31ec683c) +,S(ff9e2f29,170dd456,2c94d48c,eeb0390d,db783702,c13574f,8a8c6a79,6427adca,784c7b68,3c824603,5077911f,957aa33b,47534180,d70a60d9,f248b860,736a24c2) +,S(802343c3,e07f9f9d,ed2ca436,3e455427,367b852c,d23ffd7f,fd690eab,90b4162a,b093f0c5,f0fc7d01,a5dfb7f6,bf9d90d8,8595140a,a13fbfc0,f7d6af2f,54ff5ce0) +,S(ae74ed87,11a1a684,9e558e05,47ff8f9b,f6337551,1dd3d272,ee0597fb,fc2d6914,c304897c,e5afb5b9,a383cb64,e85071a3,cfbb2981,93ea6ce0,55257ba4,cf15bec6) +,S(c099b05a,dceb601a,14892c97,7b267152,f29ea71e,fed6090e,7267249e,43c2390f,6c3ad3eb,8c95c72f,e0030a7d,34329945,fd1eb71d,8c4aa1f9,8fa4656c,6bef067f) +,S(97c6949,a25581f5,945814e9,9d590dff,67d936d5,94101005,4cac33d0,fb3658a5,cdc7faad,5bc387ce,a16a10f6,6971e5ee,6d32756b,596adc14,57081ad4,1dbd2e4e) +,S(14a19edc,a1ec89e9,524548ef,cf3dbc03,81b14b46,928f7425,87b11df1,7aa721f7,f83c4d0,aca08709,63a017b6,8db3434d,6088015c,2ff1d8bd,739fd620,35d1a19e) +,S(4742af63,c7597424,e6d04a8c,cb43142c,54c5b350,175bf5a7,27c39820,2187d00e,bb1f2f72,b21e15f,ec17af32,2147edfe,1ca80ac8,3e030dfe,8be39fdd,f340c56d) +,S(a6597869,4d168b00,d9df41de,fcd48ae9,68313394,db683407,330ffae9,f2559a11,6bf65c42,13317a5d,fb561e8c,872c8b1b,90bdabc8,8a8d2e1e,39919f8e,5043ced3) +,S(9e62e468,1e22abde,4d7a2fa1,2f7867f6,ecbfc15b,54990e26,821a8cde,fed76985,4877297b,a64fa8ed,c039ab1a,e62ac72d,8d7bbf05,7513d1,4237b8dd,9f1acd37) +,S(d24333b4,de55b0f5,d47c72e4,88cee8ee,8c960903,260a64d2,c89bca45,db7352c5,688b20b0,617ac12d,5be38fa2,ab52e785,69766a83,2d8b44d5,cfd6ee88,b19b9f1d) +,S(d203301a,13d1c8ae,a8c79007,1ef8116c,3916bb3e,dd5110a7,304b9145,465be838,a606dcea,398dc313,e61c8fbb,dbbc3874,3c103297,164e66a1,4412a21a,5319e55c) +,S(d1aa98ab,7010146f,16a9d6b,cd911535,2595bfa5,1bccb3d5,5e3a4a87,62666610,3abafa4,999294e8,80e3fae9,edb4206b,9b9786d,bddcaec,e2d01cf1,ab715690) +,S(1e20823e,81051b2,65535085,9aefed6,3631dbe,23b4e79,abd3c17c,21f053f4,e238adb6,86592008,f7fe34b9,9b934060,15c323ec,55981b27,85feae6e,5fde7186) +,S(d878afb8,8527a861,4b4276d8,fba39300,72196017,6f437014,980b7bbf,d4320ffc,7403107b,fca8ac64,baa94a4b,128e72ee,4a015675,845519ba,d5609331,1680e7) +,S(53f86d45,61deef96,ca88a685,38fd35ec,a95740a4,3a52c601,faaad0c2,aa3ab72f,667a6ece,bfb97682,c5e9e5df,b6d29156,9604f63,d9ab00ab,8cb10000,e6b01514) +,S(20990660,f1055420,b885fb0a,38824740,3b141c37,5aa20dce,8a29191a,e77bbb16,7d434476,9e302e38,9e14c02e,f5fd8a5c,64cfcf3d,e9813f1c,f53bc6d3,4da93559) +,S(1e70619c,381a6adc,e5d925e0,c9c74f97,3c02ff64,ff2662d7,34efc485,d2bce895,c923f771,f543ffed,42935c28,8474aaaf,80a46ad4,3c579ce0,bb5e663d,668b24b3) +#endif +}; +const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = { + S(8f68b9d2,f63b5f33,9239c1ad,981f162e,e88c5678,723ea335,1b7b444c,9ec4c0da,662a9f2d,ba063986,de1d90c2,b6be215d,bbea2cfe,95510bfd,f23cbf79,501fff82) +#if WINDOW_G > 2 +,S(38381dbe,2e509f22,8ba93363,f2451f08,fd845cb3,51d954be,18e2b8ed,d23809fa,e4a32d0a,fb917dc,b09405a5,520eb1cc,3681fccb,32d8f24d,bd707518,331fed52) +#endif +#if WINDOW_G > 3 +,S(49262724,e4372ae6,f6921b82,aa4699a1,f186aea5,40122630,3ea42648,97c2a310,1337e773,bca7abf9,5a2cfa56,9714303b,6d163612,a75ff8ce,c41b681,5e27ded0) +,S(e306568c,1a240c90,d5e253b3,e477e2f8,4dcc1a56,ff06db8d,1384b079,cebd2d31,eac6fe3,78934260,888f2b10,7f7d0db6,ffbc8042,be373826,692b4083,92546e44) +#endif +#if WINDOW_G > 4 +,S(3b9e100e,2428cefc,271b0e76,23fbd633,74ebf8d9,aab41dd9,c530c39e,363136b0,fafb9815,2d16bb71,df1533eb,8f475b26,a2ae28a3,3ad31f81,953ec16f,6cdbbc8a) +,S(bb0aad49,712ac9a9,2b76ca80,f5dedef7,17ca0768,8107beee,9608f047,2f485d3f,ea699c53,c5835479,8ecd201f,7297da34,895a5afa,31670bff,e7939250,3ca2f975) +,S(79090ac8,e4eefcc0,d4e8eb19,7afe0113,e1e58b4d,b01123de,4aeed33a,36718dc9,eaab722b,91905b8f,13d816cb,cd9aaa56,dd36afb7,ba9008b,963322b1,1cfae7c5) +,S(e77c81ad,e9f97b55,1c03dbbc,e549ba66,8dd71de7,cd775ad2,a269694c,7f60c7d1,3acf1478,eef81321,c5fc3b32,3ea81543,631470f7,1c2986d3,4ec581f2,82d72449) +#endif +#if WINDOW_G > 5 +,S(de2b5ce9,dbce511c,f2d8878e,3ded87cc,3d633dae,a2d45341,501fb3a4,55ccf6b0,f10576f3,d3c3e0e1,bbf717e9,8b1a3744,65b8c45a,c66318bb,34829eb7,11100666) +,S(d07bddff,d491a2fe,1ea59fbd,7c121217,29659ca5,de46658b,26b1460b,13c03c56,b2ad4708,cd3c97dd,f9c40e2,a1de04d5,61d963ff,8cc2eea7,6be3f60c,2b405ce7) +,S(82403e7c,5d3016af,3765ec4c,396ce8e1,f8da45c,434b8257,10edab41,bb6a4d51,d09661b,e27cb767,4456badd,b3e84051,99ab6ccc,4ec67c1b,11e92ead,7b463b19) +,S(eadc3131,fbd626f5,263faa58,c4caf4d9,930f933d,9541c23f,438cb486,750680cf,d3c977b1,c9b4a897,5c64b36c,972d5d01,a388fb9d,c3791a74,36094ff1,2c87a914) +,S(3903ee5f,6758ff24,c518a4b3,86748f4f,36bdd65c,b77e78ac,609f2909,fc7987d9,e92194e,a15241d6,40915934,bd234749,8d222a18,4927a8da,b0cfe2ae,182be83b) +,S(d0803b78,39ab48a3,8475bdcc,f9a9f219,5759c343,dbbf8e93,23e1f882,5be6a5d9,2cc3b180,ff29c97e,a12ec15f,b38bafa4,4ecf06c,e51d1d24,2894a926,64582f0b) +,S(1f56f096,b18a7499,a153a5ae,acf8be05,8496dd23,da8e6c19,215628fb,c0567ed0,fef22b8a,3b52f490,83004436,b65cd69,c94189f4,1a93c0d5,1fc13cb4,379dff58) +,S(1d9f69b1,a4a47432,e386f525,234aa30,79e947cf,cf203297,4e0fc05b,638e213b,d898ec17,949c0761,b38500c3,a2b1da24,5438d5b3,d3f6f720,41f15d6e,e4d4ccbb) +#endif +#if WINDOW_G > 6 +,S(128c913,4d9dcb78,12fc4361,5c67ad0,55213354,dc8008b1,aeb5a9dc,fb629efd,fee3e54a,dd152610,d9725936,99d662,c160c8e4,ec6f76e4,5ff41818,be67c96) +,S(21ec012f,5a95b94d,244b8d51,9756075c,301f2854,8e2c51fc,49c0e3d9,d1a9685,2def2105,77af497f,4c7fef71,6949f28e,7418eda6,fd5fc162,d128de19,3cde08ae) +,S(688f5202,fb9d8bc0,9e480e89,4c7cfc74,761c3be7,7dafb11c,58422836,3e331cc5,96ba7d59,63b541a7,2ec7cabd,92403434,1a393eaf,89eebd94,62d9c218,c7302cd3) +,S(fb5c9eea,1cb9a8f5,b3314c30,a50d35,744d0ef,e8bbf68,2e4d3ab4,f7f02baf,29fb8844,e18fc551,fb28bd26,95c5e95c,6868e0cc,7e526af0,91157e9d,fb630418) +,S(f1479fe2,2eedae3f,2f5f6a5d,84a9de1d,593168ec,7b52380d,e0b3625c,cac03421,1a642d5d,2fd88b82,13b50d1a,3fbd3419,c0b4630c,48352131,cb856b2e,22764606) +,S(2273edc5,e8199774,93c5b0e0,9fe0effa,f60f7b,2898565a,69f5c7b2,bf1a7950,b3aea238,8fd978d9,29f1a1df,98495358,b64691fc,4f50b530,906ae39e,fe7bda12) +,S(99f8480c,30504378,b47d10b3,2b39da2f,496d59f4,b1462856,56f05ad7,9bef5214,e001d55d,8e224286,8d8bd397,cb5aa99e,d930e437,e4e62151,71da7ae6,6264b2cd) +,S(382257f,e4751280,d74fdfc6,993c8642,9c000d5,87f57635,bc9656ba,996a6f1f,c2dc733d,fe52cb06,265a4f1d,a6b5f1f9,30dbfa39,e3659973,1c672aad,8b51d474) +,S(23a13632,9125386b,e45f4e1b,2acc847a,62e5eaf9,f6d5f452,6e145e7f,ecefb24b,9129777e,643ba22,2bf817a1,af7155f1,413cc370,3734ba6f,49f15f55,996854c0) +,S(6659ea60,58a664de,a35791a1,8c5cded4,8cf5593d,c440b9d4,8a30ac35,79149f0b,2adcc705,a85d836f,b347b69e,742fc6c3,5300d1d8,e534f1ff,c820c6d6,b2f2199d) +,S(52dc3bc6,e1acd3b4,7ece6f6c,89fd2a3e,2899b487,41421033,37a67dd,1ba939fc,5daeb346,32ad107f,bd83d9da,15a3c4ff,1e8f6ba3,ee5193e2,709e89c3,43d05746) +,S(524b2a97,93cd5745,bd9189ba,9c947bc1,693fbceb,75f074f6,17376b10,d5573551,9e8099d6,cb23fb1a,9deb92cc,3e8a2fb5,a6865ac6,3dececa0,2d146e1b,bcc80b71) +,S(732181a8,e2bc5953,923e6c3d,d960e9b7,525b7b95,e5906997,6fe79156,1782756e,2516c6b3,5592eb0a,42ba193c,bae98ab,e3c42d96,148f1d84,edac621a,722e4823) +,S(b8e8942d,926e38fd,f338496a,c2ef6fca,49a7ae3d,f76eb15d,8d570111,e502664b,7990d56e,7dea588e,4d670ba2,2031e6c7,97248641,69e51d77,f792f5ed,befaf8eb) +,S(144e88f6,3e73abff,72cac11e,7ddccf79,19e744e6,278941ae,18d1b797,e098e4e5,63cdbf3,5df3c655,c58197f,ea54633d,158705cf,7dc2eb3b,4e09f83c,3021837c) +,S(9436e3dc,489ecd8e,2d16a739,c9c73e3d,60e5bc93,68157039,75b8efbd,5c3a9081,1460531f,50cb6ebf,d1aa7806,ea84e7f7,8e8d76b2,b3a66d5e,3a0bf60,39a7e59c) +#endif +#if WINDOW_G > 7 +,S(9d3c2561,7a56d10b,46d9b01a,1710d193,e840e005,df669e76,1936c275,20890db9,6bbdc0bc,4c4ae9bc,c2dfee9b,82da9b94,1f89ffcd,e8af2aca,4467ce3,78521ea3) +,S(29f98e50,f51b7f8b,e18c6ae0,b453c4f2,d0aca5a8,b0e61d2d,dda8506,3fdb76c8,daf3bcdc,ae8e031c,73eb8b21,14058063,58a6ec30,ad379186,df80e3c7,f0e5d28f) +,S(d67d30c2,c71daa36,1805e31,1dd6046e,17a89752,94d76e1a,538af074,4dc22c94,48b9b0e7,12c807b0,b92e690a,a2e068cc,e87ebbbe,aaf4bd96,9c1114bb,a54f670c) +,S(f22c2ba8,8ce9c2e4,b772c9b7,6d03a017,59aa7b9,97a78334,83566027,2fc81649,6aa9e710,f190be16,243a4e0f,1570270a,2d92dca9,8cf99a3,cbc06fdd,f9b7028f) +,S(c5e718d,6b94c83,e52533c9,ef3234f,36b722ed,cfc074a5,eff30969,9ac5f894,24961051,ccfc6619,dd64e810,fa9c504b,f7f8ce9e,cc445d7e,642b3166,eeef436) +,S(26a8bcaa,c836eb7d,57618999,ef87ee4e,c291dc8b,333554a2,c1f66f73,7944d611,c20051ba,7236663,ace2da29,c1e0763c,e57192d0,e199e7a6,c69cc65d,bbbcecc9) +,S(388e3570,fb7b1545,9bf01ba1,7a6496d5,6bcf65e1,764e7aa,2c083346,2dfa098c,2d4e0d22,a2eb0ff0,545561e5,ae344be6,99120d12,45db6bde,a9500f5f,a07be798) +,S(68189bab,b5a0783e,23227efa,4eab2c6e,da2d1c,2ea57fca,7a7f8f72,15250709,bd30bd83,3694d3fa,a14954f6,251445f8,3e42d517,30d30855,a0eb834d,3c7ae856) +,S(642e0823,bb347795,967b7aa9,418a25,ea6ff683,fd7b3d42,b88d90d1,292190ab,db73dea7,86ec052f,e3674892,639daf39,286b4690,63b68903,210639c7,f3b4000f) +,S(9ab6ea81,e5bf5505,751addea,5896afab,7f4eff2f,d3986027,f916835c,64924afa,38c06aa7,4870a5e7,2ff29efc,bce4b3e7,dd951c9b,29966f2b,47d007ac,7629bb6c) +,S(b09805ae,e567f69c,71a98248,e89e64f9,e059e015,bde01a62,dd18158e,e94a8ee7,62aea16a,7ec912ef,cc5382eb,6d220ac3,dea885a8,e3da12c,8147b28e,1983d221) +,S(3d632a5b,636ed5a3,4a58bfbc,9831691a,91d6b5f0,975bfb5a,82b4c1bc,107e6e5,577a449e,75bf16d9,2eb6ba0f,9cb4d496,7a7ee09c,f1605aef,682cfa31,cf395a1c) +,S(6b821bf0,f70d5ff2,ceedb69d,d96ac8bb,b51e3635,3a36d50a,b1c1a697,40cef707,5212ce11,993fc120,88028674,5cddea94,6371b4,dfa2f47a,6d83b789,c6d12e5d) +,S(bf294951,4496fe0b,8527740b,5cd9394e,dc33b330,c91d996c,789db854,45728b23,790aede5,da35ce7a,343f745c,410de38f,4c53bc2a,bb41bbae,c13272b1,e912782c) +,S(a55354c3,82bf968a,16668267,c4498946,4906f69b,114f6f06,742b6035,1d75ec80,2809bddc,45661a5f,28b31967,6cdfb5b1,9dd6d296,d1828c88,179a630c,ca5962e8) +,S(fc015036,abcd8311,bbfa1574,ffb4980f,b893f8af,84f519f4,5a6fa344,2649a693,d0f6a278,1946e04b,385cd004,29acd3f6,f5542aca,3e7c789,657f676b,f19db819) +,S(6164410d,9f81f352,272a799f,b4afbe24,59caea6c,511fa4ea,b7578980,d9a7aae,92bc1480,ba19fcd,3cbee69c,95b9396f,4982fff5,d4e7dac0,abec7153,ee5a7966) +,S(ef816535,8de9d737,728f9a9b,3182b4f5,6e25917d,1ec05fc6,faa0fc85,170b5f2d,dc372426,403ca9c4,50649df8,8b6fd32d,f1721dc,cb1c7d7e,155c83ea,747ec595) +,S(428c34b9,89a436dd,dc704e68,170d2c40,178c5646,841eb2e2,642c0a48,8987a8ed,b1f24158,251c4646,ca04dc3e,64369634,3836f97e,71945f4f,51237abd,3aeebe06) +,S(6eb61782,f909157b,415e6243,7bebbd6a,5f19da73,7eda64a5,5acfe206,65417a9e,4fe7c546,baedf2b1,6c92f168,f99c42f6,af03edee,290ecc39,4c4efff,e8577b72) +,S(992e0af1,466d4fec,5ef83009,98ce31df,cb6f9573,d14c1646,e4371c62,a376fa4b,d0e3da69,1e4396b2,eb01e3a9,964365,50d000e6,31d79ae3,871690d5,29f9e825) +,S(fedb564b,adc70078,f20f31d1,12496934,513e9903,8cf5b9c3,1084383,5721d11,1a8e2a49,57e8420c,f2d5d97d,657f1602,ba26ae87,d5408b2f,a9448def,38157a39) +,S(f3f2068a,4dd89d18,1247088d,3c424916,3e683226,6274b575,e54430d0,73b24bd,f2aabd19,a7f462,1426ab1e,e0aa7e33,12381c5f,e1f1cf0e,75c1a7f7,7ba2bfa8) +,S(32b9225e,4217a359,8de4e7c1,476a8581,5b6aa458,d93dae02,b3d30772,3ca13680,7466e804,26856340,12985683,dd071e97,53246214,733808a3,96f35d0e,5a133802) +,S(85008a87,385ed6b0,4ff89979,2eb592f9,ee6c6b93,fa24dc7f,c6299b9f,dae64a8a,1f279e2b,8a5b0576,fb9569f6,c0825876,84b5b38f,a250e362,2ae6e284,a504a2df) +,S(4dac7833,ebbeab0b,ec8edac7,cfc49bfd,b835362,f0130e9,e44452f6,a82effd4,fc9d1970,f230aa68,6114ada5,7b4237dd,133dc3b5,db8ec1f0,ccd09aa,23c740a) +,S(9b755881,45a65d54,19b40226,e535df5b,4b44de41,1b93d71,31f3102,d56dbeeb,d0324171,d6937b7,c9b38290,7dbba3b5,c1516061,59e7ce1f,a1ed9d11,e089a02d) +,S(1bd6ceb9,2592a7d7,a66e6e8b,1b645db5,98b525ca,461dc4bd,b583ed9a,cbbc8bdd,9a59de87,84a98bea,48b7abb1,a5b7055,5a1c4ce5,551cde4e,7b790ac4,9a29944c) +,S(25a8b2b9,3b360941,6525b08a,7fe786e9,6b3a3c7d,8b444637,268f5355,bd5b56ee,3c58ccf7,5734687f,dbd027e6,3ec6c550,45f131ae,df71a40a,c45e4e8b,965f22ec) +,S(244b2833,27c33efb,221a5767,15225d6a,bf5a1caa,8da543c0,d88b21e5,17ed9f5f,220036ac,e48d8953,5aecec92,a29f5012,37f83ce6,44380db,c229f0bf,c1f53d7d) +,S(6f97ac0f,27b87905,6b442d13,e566978e,91f0cc1d,d6ac1e64,e9764a35,325dd1b7,83c6e70c,fac6c707,226ce1cc,691b38a0,7e937f5a,5f2d9c81,4dd0d3ff,9f433d32) +,S(72c5d60f,eb014e2d,ba8265ca,d454f261,2d6abcd4,b2236bad,c94c4801,561dce1f,e3119a19,7ef91963,b3b28216,3c5d3acb,97b281b6,d246cbf0,690b40da,63978fe2) +#endif +#if WINDOW_G > 8 +,S(a96d2da0,1b10186,6998659d,f441a1b0,2af32b94,aae8c6ea,707d9ed0,d5f33825,660d7d83,5da5235,9f7cfd41,28c370aa,5659ea71,16a91690,6c0e8108,a513f9f) +,S(2cce6f63,4d815ecc,1981d200,87616677,d906aa27,990c4875,17314dc5,5be3c4fa,615210dd,bd599e91,1b6f997a,fd05475,b33cb274,c9ecb6e5,d3c23323,beba4b50) +,S(992b0084,525dd399,d98602d9,8b8d53b2,4558fafc,758a2f46,60e89bd6,a645f0c4,83ca98d2,26545a29,8c45f40b,11420602,f5a5c70e,595eea57,2da64d61,a4e2f98d) +,S(434efb45,3089619f,cc761ee4,3fd4b77d,c6b0c69e,b45eb88e,44ef766e,9beb7357,3dbb0d6d,1c0b92e,5965586d,236c0be9,26967ac8,830b9bd0,1e9efc2,2a9291ea) +,S(a2179ad,2c683306,a2e4735,93efe304,f0dbf589,c4dc2b88,4bddc5c7,e3fbc156,ee4539f8,ab980176,a18e6dfa,bdb609d2,88a2d223,86dcb20,19207afa,f6033c1f) +,S(3934081e,d3e1e147,aa52bb40,9221ef6,a445f22a,66718f14,6d63907a,ef05a2e1,bc370656,31391b7a,209e79d4,9f1e959b,d3a9fefe,752c6062,14fab290,a7b5b3c7) +,S(35bc24e1,59a2b939,438354,6cc4255f,f1f3f7e2,105293db,39688e07,df95d8ef,dee1fc70,a9698da,f7abeb5c,56724f8,e87e0cd3,8849edfa,246ed0a8,9223ae15) +,S(f5d1e75c,251fb473,8552a0ee,f05d8e65,63aa14e1,bcae5b48,bc7e5258,b948127d,e260c1bc,7044f058,8f409134,d298528b,e7d586f4,bf7b6fe,92d212a8,6f7170fe) +,S(ee0a76d3,6fed282f,a1170dd,6acb7743,f9617bb3,60350f54,d12da1cc,7eb121c,f8cfc2db,eba6960f,e6135ef,ff9e10ca,4e56c458,4b42b516,6dbf7af1,420ba5b1) +,S(9e3b87cf,78927664,15cab377,f1774d14,4d1879f6,16da5676,f94790d1,9ca689d8,f8f34522,da3ca2ac,b273b0c2,b1b1f26a,7a9c2d96,b4547482,266a8e6d,bab3b577) +,S(e26f256f,d08942c0,3c85b89e,e66b8b50,12f409c7,4f625152,86e5b310,eb4868f0,bab3c4d9,6fea49fa,6d08c656,23c9b127,3552c23f,3c4f12f2,1bbe8bfb,56f210f9) +,S(8ac74046,a5d38398,d14b6763,4f07abc5,a3f5c852,ea8c421c,f0858980,11da6c2,d20ca793,f56e3198,854f5916,35402c2,4a71af95,c84288e,1495d324,7229b3dc) +,S(5b00e095,634694cc,93d531a8,6e81d29e,753928bf,b2c83a83,ac677a92,55932bd0,8d2f267a,8071c48c,616daf18,d587e,42882b33,748032d6,4bc3efd6,60a99656) +,S(162190c4,80c5d7b,ab1ae4ba,7b2a6611,546ffb9a,b4e9ea87,2c0357f8,9e11f0b1,db5e2104,12448a9c,e586d7a3,13bdcc6d,bb84192e,98c5d9d8,79693030,92423525) +,S(f6a84421,b4b383e8,87f8520,e06017db,476774dc,9aff636c,f26a676c,85b145b1,504c71a9,596770ed,7a2da8,2e8fe1ee,93717215,bf88e0a8,ed74a80,4037a3b3) +,S(1f02d142,f0b5d3d8,b3f8937d,a49f908d,83e5919e,55c9c134,55a759a9,932dd6e5,e6a5cb33,8ed36df7,50ec2eae,5db7c9ff,fbc9035f,48ae0348,fb3882da,39863e65) +,S(61f4d96b,c326a1fe,a2ae38c3,73484a56,68e29bf5,12d77a04,34a62278,1d78899f,651ef738,62005ac7,64f97021,6180c25e,2172724b,375e6b38,b3a37ad2,d7b4aab8) +,S(ba7d5c0d,623c5e79,8088a71f,8d2916e0,7c7ebe2b,1afa19c1,9c917cae,31c11616,da409f40,d7e3f6c,78655153,fb35595,a1ec37e7,e66c8958,ce45b07d,bd5f5a51) +,S(8b90a590,eacb977c,6e6a68ee,6dbc3e19,fe5a92a6,9abc43c3,bb16f9d1,925bdb09,a5cf5f42,ef655d59,ae734e1,f746a752,4d01ef75,d829b9a9,2a035180,fb5df718) +,S(87f35227,21914cfd,ce8294a3,cb5ae171,f0d994ff,f55b25b7,1c9e9aac,dff4fe4c,cc71d1af,530bc5e1,eae7c1ef,b9a91335,f26b283f,222a3552,dda24c28,64babb2f) +,S(aeea4d36,ba405159,bf0ffe8a,5530f1f3,83d5509f,187ef58b,6c1eee4a,cb77a15d,db563dea,e7403df8,7d3031c2,48aaa28d,96958f7d,ce36b3f7,8d55607a,60d3ac1a) +,S(300fe98d,3996eb9d,57c6f1ef,8058d4c,d8dda436,5774edb6,2a338a59,afcc7111,d148a017,8fdda40c,f937913c,143b76fd,d2e6e226,d27225e3,a49658b2,38c40e77) +,S(c700af5e,22cd146e,839095a1,f6743e4f,d01e9c1b,76d4c73a,a5005f42,99c19fdb,3fe00181,b1f01c27,27ab1fc6,bb6ea569,d4a3092,c2d511d2,8b5546aa,194b32c0) +,S(d03d08e1,17b94138,1aa147af,38448539,94228f75,f96bb4a5,941c3748,cf50bc60,5b119ffd,2785bcc5,2e0bfbfd,c541da8d,f47dd076,e91440fa,10e071b3,a896e31d) +,S(6c6710fa,17a520b8,56d7fadb,6af81a5f,ea9c983e,c94cf832,b395da5f,c22bd361,db4efd26,36e281ea,419964f3,c0897b05,b036a408,25ed4ad6,4fba393c,f2804941) +,S(d7b5c239,9df47a16,6b6cb900,d08a5d9a,5ea3bfe9,94f861d9,b46f3fbd,a3d91bbb,ed791f4e,4ab1c25e,9c83494d,794ffe1f,a3c8a065,29c0b710,cffd597d,64efe8ba) +,S(90f699ab,8f728152,e2deb8dc,ceaaa3ee,53ff2f23,2341a952,4aba9e8e,50f66c06,8bd0c3a7,7f5312bb,9d46d80e,c36921f8,561558ce,6e63c08e,641c82c9,fefce91d) +,S(4d0eae3e,3d7adc34,c91eaead,6a16f569,37557dce,2b68744a,bb9ae71a,8dfb65cc,816b0806,a9508731,ba99a685,142aa52e,6e874c99,5096a53,52c6e2f2,1fa3de5a) +,S(46cd9edd,ec318498,dfb4f815,3315185d,689a2399,8e06b1d0,2438f7c9,b2f7de62,3b5bc1a3,d5fd7874,8c964a8b,b813388b,e0d5d168,1247d008,10a846fa,561f29bc) +,S(4aac80e,3f6eaab7,1c576297,b4552e40,653748fe,21a580ae,bce4eb83,2ff730b,3d42a00d,8014f46a,80fe2dd5,bac6a046,40d331a9,4f1de050,9e435398,6c8c7954) +,S(59bea661,b73d9c09,131e4573,199937a,e03e96f6,2ecbc637,4bb681a5,582a4114,31a20be8,ee8a2c2e,81d062e7,8e891504,d9f2639a,bd5db5c1,30be3d2c,b1afa47d) +,S(7fe9065,88b2ee92,2b19acd3,fec7a4de,9ee089a4,a4e1c338,5e293567,90ca6037,dfb45d90,c5f43eeb,1f5eb326,20763ff4,2659b763,e7ec72a7,c69e9369,35e5c128) +,S(db390f91,32277889,784b5e4a,cf8f2fd1,f3a5fc47,f2c8262f,19dc9518,95322157,4364955b,241c831f,94fd7d4,da63db19,2aaeb5c0,ade82dc8,d5d61cac,d690bbad) +,S(34dd0266,e5fc19ac,7daa9e61,5ae8f78c,985732a6,1c53c29,e6e5d407,7391ebc8,c2d6b6d8,e73fe00,e6013b1d,2c2b48bc,77a5db6b,45232ddb,6611ea1f,c5dd797b) +,S(eea7f72b,7e31608d,d3dfcfbe,16e2ef95,5e6c7ec5,328f84db,336e3df1,9d1772ab,9662c8f1,ccdea4c3,48ea6d94,739aa747,17219556,c6be9b8,8af0d30d,514004bb) +,S(c6af087a,f1b10eb3,93de412d,de856129,9376d264,36d907fc,10d3e7a3,88c391f2,ba99d62e,31856457,8ad8847a,9cfeaba5,e5ff9824,2c614959,eeeb4ae7,50825144) +,S(eae34735,6c715740,ab73d034,bcb43f5a,aff37fbf,50d35518,1364c999,feccefc7,b9386c75,da8bf645,cd6f15d8,4944f74b,9685256d,b61fda98,79fe6639,92fe4703) +,S(4a548c25,f4d8f54,4f9a27e,64e49626,41b68ae3,1d90461,65c24ff7,fb40438f,ae9f2c85,bd608cf6,ef5e40c6,7e29a63d,4c274215,c2a0d578,38252f28,196f6d10) +,S(61925679,dd6545a8,ea19743d,7cb3c0bf,453318d9,e180da53,f43af2b0,ecf744af,a0682d84,d7215372,e9ec062c,a3e4aa1f,47fc551d,44e3d1a1,7260a44f,2bcb184d) +,S(e1db5e86,9574ab76,d4fa260d,2425ef62,50267ab8,52ff04fe,25f0150,9137ea2a,c8eaf421,9fd5f7a3,6c1a99a1,d0c61250,836e204b,fb496774,83f43c81,511905a8) +,S(3062cbca,73923842,2a0ab862,a1fd84f0,38e63691,cfc204a7,d2f9d263,d248c7f1,8f112e57,e3f2970,dce89eee,1e184310,721a85f1,cc9feb2c,f55c047f,dd9670cb) +,S(67c393cc,aa9736fc,eec7a861,eb53f953,9da74ff2,97764f4e,4e814004,4da4f739,93d329d0,9a165fe2,2f8009d5,ad59b524,33ac24d5,46c7d394,2d147c4d,49853203) +,S(c67471f2,36cf2187,ee5a5619,d581cc7d,ac5bc5b1,e8bb728,f943084f,e899bf5e,75c90c9c,787b318d,943c3247,74cbbcc8,3a533f37,fb684aea,62fe6848,8de2c513) +,S(d5015fef,d5938dd9,2133d7a4,25aaac5,12143acc,6547070,1827e98a,e4ed1b52,44305616,a02236f1,5c37c0d6,51d845dc,e9966860,b798f79f,4006c25d,7cf03d6b) +,S(aebcbf65,aaa89615,cb48ed56,4002ed9a,243ca309,1cf503b7,3c1929ce,8164f2d2,4fc1f25d,1a30ee2b,4220305b,a9c88e86,a6ee7f55,55399ee5,35e7d37f,e4b3f019) +,S(15cb1617,1727deef,49ad6821,932a39d1,bb8f980b,b5370d81,5ef00ff,ebb30e43,64ad8c82,87fc053f,ee35c1b9,9734b34d,2dfff86b,490ead93,b1fe7d2b,f80ebd7a) +,S(d404209e,8530d674,9bb88a5,525211a1,19fb2523,63e2698,6bd03080,4c49c123,3763fd68,fdb85d19,e8506275,3b5a327c,46ef87be,8a3432c5,62bcc622,40a7a128) +,S(f3abf8cf,c232849,58e330c2,fc327ac4,d40b4497,f422aa4e,d42e9381,9bffdd96,50861f05,d831327c,3e907643,2d7a7621,d1dfe9ec,7ccf2061,cc720e9a,7d5b0d5d) +,S(78a32893,2ee9dbfb,3f84755a,651b9d23,5cf92b46,c3e4bb4e,57b7ff6f,7c874dea,925b783a,966721f4,6eaccd87,495eab7d,b3c36eb2,92c1a473,b4617f6b,9ac85895) +,S(8165210f,5d146f8a,1ac1ed91,a7bb678d,1b7d15e4,3ed55e8d,f6503209,a750cd4a,fe390d3a,19e22bdc,cbd45875,e396c713,85954166,a1edb63a,384b8587,915d260d) +,S(75269de8,6c2f3d34,cdf17dd6,f3efa198,ba73431f,cf0072f3,633ca57f,7dfe3f09,b162a65f,d19927ec,4446e3db,d5ee8850,f463f39f,1df1a0b1,f5ed153e,704805bd) +,S(5e36ce8f,b393949e,5b9c4e10,44fb2ff7,c520a7b7,a9016b38,9544e512,427145b0,7c40e718,292f96a4,1d9822ce,d54aea90,22074c5c,c82ba4e,7d39a4e0,7811614c) +,S(c8cc7129,fdebbfc6,c0576edc,6ac983e5,c249d360,76da7d25,b05c0b6b,16632746,48a8cce3,a77e4498,76d1a332,c539feba,2653fbaf,d4f75e68,e4674d78,4c716fb1) +,S(9fe1db36,f1ee3e6d,3924d2a9,ae3ddcaf,fa85798b,7383a1dd,2d6c0c14,ea46973f,1d35b6a8,607045c0,95ed9658,80212b63,43dc55ec,beb3223e,7c2e76ce,2c74f6d2) +,S(4ca8ab8a,3d45748e,6e19e02d,dc351279,e23648db,d26d2465,7c9713f5,bc8ecda2,8a5866ad,9bb7db92,273c9f23,c0876c95,13b8a68d,bb20c5cf,164a9a39,4e3e73da) +,S(c8d0150,e4a989ec,5cdbc724,21b1e29f,f1f30818,1d8117b1,3d376f58,b72060c0,5c5880a4,c5a73a4a,1ae42a9d,f0b20032,7ac4a732,cb26e717,49e63365,5082ebd1) +,S(3d61557b,b4b9fde6,49fe1b7c,981c9883,9e368444,f130bfea,fc1e1a0c,12f2aa31,933167ff,62cd840b,3a960c90,e6b1c37e,1e233695,318ea286,71c87992,de5b56f9) +,S(67569f9f,fd44ca09,5b478dbb,85c3ed1f,feaadaed,cb624552,ccfaf169,24e69e2a,62bb2948,230fbd2c,aa941288,7adfb24b,d16619de,af0fc102,feb1b26d,67eb7fcc) +,S(6313d13e,debf1401,eb7d279,2c66daa8,6e3075d0,3cf0daf7,f3b753e,7372fbc2,44621230,50eb1245,86975455,63e06e2f,2839f874,beaff9c2,f65e26ad,d3573ef3) +,S(6f8ec9e7,fbbe6e8c,d0e3085b,47c6cb2b,88529f2,15f13195,5e2fde24,4dd23968,1fb61e14,523b3fae,b543b215,dc46eeeb,23e1950a,8301c78d,c510c76f,ed36cd79) +,S(56613dac,3c5aa1ac,bd9a0ec2,f839b4c3,36f0ae58,e0e02244,7bec0546,710b7f6c,247263ab,86c398f9,3b23319,4434af8b,530908bd,dd7f716c,f7b73aa9,8eca79e8) +,S(c9de15e7,b4147219,137e6e57,c5a7ddd6,17e91ab0,6859dda0,41eb3c21,87aee08c,c7178ea5,cfa056d0,dfd11c87,715591cc,40cc5db1,f5e4b70f,d8ffbfc4,3f8b0009) +,S(7590a4a,627d5e90,e42a4b0f,6be6497a,f906182d,d2dddc48,a8b6bbfd,f56c0504,d611e21a,b5498760,5c97e878,baa464bc,cc5bd875,353b69f8,1f93ce2a,a6c38587) +,S(94b1da0f,b310e056,db0dff72,81db3362,1fcc555d,bf3c973b,76097908,7fe19d6a,8318893d,d5b41a56,33a2ab4,ae4b953c,45c42e9c,8f2fd159,85286de3,fd4fc217) +#endif +#if WINDOW_G > 9 +,S(b5af4299,bcdacef1,e07d081,daec2cfa,d6f8f821,38e151a5,f20e6d52,84c9a6cb,c0984407,8a7db82d,f572987e,b137dc09,c8cf65fd,aedcb20,43b2479b,ab95448d) +,S(5f49ad43,70744587,3980a153,c851829b,f8ef6141,9889bb0c,3c476847,6939c3e3,5c40d385,20f56c3d,ba08ae1,b40fc24a,2ae25c94,45cae0f7,d01d1800,747e04eb) +,S(f6bb067f,88ccc11c,64e30d1a,e6893942,16bea3bf,26ec9c64,5cde1b9e,487da385,315a476d,7268978c,d89d4ec8,adde4a83,28dbfdd9,f2bf44fe,ad4ed721,78288f55) +,S(7b47cede,c02b19e0,94daea0e,ec000455,52690b49,44b3f10,beeed2bf,f9df6950,1bcda5f4,9beedec9,6ebac5a7,957918af,53b7ccd8,c211330,280ed93b,e0efe9a6) +,S(ba69d7f3,82d56d9,eb65e1ab,2d70f52d,18223621,c0029035,a78fd660,9940876d,5f44a2fd,56d5289c,f36bdb9,50ee2538,1d1cf935,ae1aec76,84b6d11f,975d393d) +,S(a3b70894,d226c195,223713bf,330ba39,fe7ec35f,9743347d,1d3dfdb9,7bd929ff,5d0cbdf,42755aa0,355ad2e1,ab39479,495edf2c,20bb0aa5,93cc04cd,56ae6135) +,S(c756e23e,bb2d81fd,91fe73b5,20eb3bfe,5ae602b3,448cf84f,e8da37d,d1c804e5,89f4b6fa,edf86b9b,ca9f52b2,9864a982,5a4faba9,d4fb35f8,98511210,34df49ff) +,S(36327c35,6fab94ab,f791c234,1c39049c,65410e9c,6cdfaa00,d172ee84,3671822c,fe3588a4,698c8d35,f0e01f04,e94f7039,eb745631,8c25927a,be7d061f,d3ed4c6d) +,S(3b75bfbe,8e372311,f46ecf6f,c5eb2af4,b66051b1,874b0c32,1189e106,37dd21bb,b9144a33,e3c3339a,4595e248,87940b4e,2e42a095,d7d1bc2a,ea50aa29,5a879908) +,S(44a61f10,57b4e88a,c55ed72,1f8f4477,5f0272bd,379a97b4,d92a7f75,f0d3c5e2,e870ea65,80340464,9716ec1f,5b79392,18162306,6dc14086,111de144,82d80141) +,S(11ae02f7,663c0a,24c6fe5,ce9206ed,1ebed2e6,ca294acb,74c9a227,97fefd,635a1b87,a262fb30,5008737a,e70422cd,a4e35766,940e424b,d697d28c,dfd6a36f) +,S(c751310a,dfb25d14,2e6ab1b0,ab31b37c,8e987af2,105d5981,69085ca,3e142f5c,a4630dd8,ed2717a9,f10485a4,be685c5a,ccca851d,4bcc1886,4df943ba,ee821014) +,S(fff4d7c6,4009f39c,41757b17,3eb2536c,a639b72b,e15af7f,790bddb7,efefe46a,694a798e,ffe22d1,1030697a,292a4bb1,4c110d6b,dad7edc6,e8976450,ce5080e5) +,S(7d76ea39,c13c9f46,e931e8bc,1cd43707,932b1a,31254d18,c06c8ce9,c3f90534,bc14b2ba,8f0a3d8f,34c7cf7c,3458c916,4cee438b,39a5a6d4,d2a1c9fe,8be415bb) +,S(b6efcad2,ca6ae4f3,6f080707,530aaaad,625fda5e,825f77e8,a6730e95,5e07ad39,4868d8ef,8a56d327,3f6f4a3,23ea6ad6,4694301a,1f6405f0,a421939,bdb1c765) +,S(1a04b12a,a889c4a6,d37f9050,e75406d3,48e38faa,c0d2ab4a,e30caa6e,3633a32e,f43a0c8f,fad38d90,6c817dca,3e7ea09e,21e1c801,7af86966,c246ebc8,dc75911d) +,S(adaeb0fe,9b345190,4e878b12,6c8edddf,b70e2500,5a4d6a6c,531aad51,83209b7e,2b26b16d,dd4d29a7,8cc6b4e,198e6ede,b1c59def,c6793173,f33cb039,d37522ad) +,S(4e4ca02,3931f3fc,b512121d,2d632764,d97fa125,8460d0d1,9a94eb88,2c6e0679,ef90eecd,3704a6c9,454c6860,928eb332,cc917b17,f2594348,515822d1,84537467) +,S(a1f3c279,df69c13a,711b5ee4,139f603b,5654ecac,adc02a22,a7c2087,c11e2971,57249794,54cb78dd,7ef69916,5efe2326,e8aafad7,a8a923b2,252523ad,b6a16b2d) +,S(aac76f81,488668e6,f4b0c911,bc9675ff,c19424c8,6a6903c4,f4b13e0c,e798c7a5,24ae16df,dd6c7d6c,1391df3f,73609bc2,c3759536,eb277189,588a72d1,84dd2978) +,S(70e94171,e59622f0,de360166,6498e6c9,ddce496,a15e30a2,a5bdb701,d0034769,d6d7c0a7,75a9cbef,2b84d4e8,200f92a,446111bb,cf46a57d,5e244f1a,1103b757) +,S(ca17e5e7,11d44492,496fa849,c6ab1c47,3426706b,5b8282b6,f288e096,8ec78890,b3ddd632,2ce577b8,ddb9f501,e17d32cc,e6c58fee,4e283410,5e32f3e8,8bce2c2e) +,S(1a16a58d,8d70fde8,6ac0700f,3a43ab9c,255ec001,8d5fe571,7ec6324a,2faa62e8,f2cb2eba,65aff0a3,dd7bc137,257c1d30,dc2eb6f3,7a3897e1,16c0eb1d,1a710c27) +,S(8f2dde65,b4ddf10,c29c6bbf,5ab2ac11,2dd3357a,e3e5f01e,98190e32,94b9d4f2,e06b8cf7,e5a736d5,4e954bd,8591f7cd,79df335b,d3279f5d,80493fe4,df4c6afb) +,S(3314b553,f8213e7c,80d4562e,cdb3564,ba1d7285,6c88d8c5,a3adda90,4950162,33e83c5e,e3f7c550,f94a7434,e3cad51e,b4dab462,13acc24,fc4aa1f9,fc43532d) +,S(7c107ef4,65ac0c2f,fc5b11a3,348a7cc3,fa589554,ee2b6606,2af3c5ba,25304000,6b5bcf12,2e8a0705,d067fe78,e56d365e,89b5a979,c23f124b,508e63ee,975702c5) +,S(e918a829,325adf67,511e257e,e5596d08,19be4605,36b20cb9,7b76a54a,5be4f8b3,1a92f0a0,b46152c3,e1ba8f0d,12119c15,b70af0ef,3ff0ebf,a9dc9207,cf2fa8ae) +,S(8770da03,d4f3a4b9,e92dd2e3,be309c81,c346c97d,2cea15d1,e3fc94cf,41b9b079,d025430f,dc633d57,18d372fe,64f57fac,53f96461,1fb5005a,10ce842d,ab123c03) +,S(c6b4ad3f,ae2b2557,b126a57f,6bbc039d,a6589f70,c6283ea1,7314aad5,acd8b22b,e8523dcd,2c1c3412,35df278d,e49c38df,85f2888e,cf3c76fb,46e1affa,d8d29bfe) +,S(9aced948,6ffbf984,373b1e91,ff871619,4622741b,1e1ab968,9c88cb70,37a86e14,d7a7eb1f,1b4c31df,ff43cc32,1949c1d3,16e1e7ec,a6a852cd,e8e7f592,8b352d9) +,S(af51536f,fccbdab4,c4f4015b,1e8aca69,b08bf055,e038b22b,87c54f5f,1b9007f3,b6bb0b4c,95c07ecf,49ffa85b,abb1d308,cbe813b7,703a27ef,cf849e,47130f7b) +,S(ec5306df,3ea0abb5,6fc45a09,25c1c925,56e02ffa,18238c3e,5bb2ee53,11823ce2,c950cdb6,720f65f4,18065352,43c90bd8,9457f4bf,d941c9f6,89812840,2dee8b99) +,S(293b1ca5,be127bc6,1a397ecd,59164363,587f468c,81de4d87,6c5d9bae,e72d90a0,4821030f,238e9f05,e93d81d9,1764863d,443a524,3d18e61b,ae26f74d,ba35d821) +,S(ebe54b7d,99876ab6,b2951f0e,e68bfc10,16877142,22ef83c1,2561f486,b4865488,2a1d651c,caf8802f,fa699d91,b8242f51,58d84fa0,d481bc47,9246eeb8,6d381c07) +,S(88f77523,3ab0d69b,ed220109,b29d0d99,33ac8325,445d19df,d38d9c69,3464acdc,8f915122,fbd95ef5,31ea2cb1,aec6bebf,8e059059,459c0d5b,79a282cb,cb276702) +,S(1d84753d,e7210dd8,9db6cfa0,8ebe9797,c7fa40b9,6e94439f,b8117359,a2b3bb01,f7360b5a,ba53a4d8,7440b6d6,f540c485,2e889c33,9f34260d,abfa7861,f1f41ad6) +,S(19e69df5,176f82c1,cf1ae032,cb388ce3,36f82a3e,5e741e1c,27fcefb5,311d4b2f,c401f4fe,c9ba53be,be175dc8,cc1d17b3,9d9f7af,5bb496e0,b95bba6,cbd5c143) +,S(73189e02,c1f125ff,df1a9399,57f29aa5,19b04f5e,42adf729,53d354cc,aee3f2c9,52b8e88f,1c6e7cf8,621d21b0,1e406038,d901de1c,4101d424,1009c01,a537f704) +,S(c5ebe082,8ff1a8bf,52f8956c,b747f1d3,278fd2b0,a5ae7669,1c256e9,2887a74f,ce58cb9,62e74b98,1ab97dc9,2d736b5e,c506e51d,6c09b382,3eff1cdb,cd259860) +,S(783c1621,f7990e01,4296feed,3bc883d5,1a780867,c83b9166,435a0562,4982da76,370b3e2,c9a189c6,9c565c3e,8eb8019d,f5224574,bd5ffbc6,22f563e1,8a36da30) +,S(555e7e80,ccc6a46,1724cbd6,4a53721d,fad66fcb,2c71d579,4d799f6f,fa2b3305,67bbf165,d39d7042,18f5ed31,28fe6dd6,db9b4809,b3270eb,63bda603,f44e9e41) +,S(40c49a26,ab1cd46c,f1e8467e,242c6b1b,faa40a4,cacdd50a,4b0213a5,40243471,16950b00,5002622f,e0b9fdb4,c34224c3,ad55bf30,67f4e96b,dc2813f6,2fdadf86) +,S(eb6b3be5,db098d18,9c810b9f,5593c746,47306226,d77e162,7c8b62f6,d7935298,3d295584,3f356f5c,1f990231,701cedc8,c84e9671,44be22fb,9bd74f0e,15c5bc8) +,S(177218a4,2b55c169,1ca0a073,4d061b53,a0683536,9a04fca9,3f670ce,5eb26091,9611661a,a8a3bc73,7717f243,e5e8b97a,57e0e72,759f0f4e,cedb690c,b3a5fa42) +,S(841a8fa2,f3bc5f8a,7d028c00,127c267a,ea0c3762,8b07ebe3,7f883a67,8fd2a680,579bf37d,b2fd1f77,a0057712,88dbbdcc,526938c,4f9ba89d,9a42d4f3,452cf1b0) +,S(b01ef554,3a377f1e,c801421d,a399c53c,3585789c,d7e51691,32250a61,6edea82d,28f30ce9,7e29b2f1,3af626a0,7a37f14,82e119d1,e66389eb,9620200b,25a90265) +,S(b93e014e,8e3cc643,17ee70d6,ef8c9918,ba344e7c,b38a9a6,2591b0ac,4f38904,129c185,5bdfd10c,6bf152e1,2ff89b4,d0b368ed,c0ae01f7,2b29fc95,240ac3d3) +,S(e0f426f2,43c50d9a,ff80a1dd,26d63189,a00601c6,8ac81373,5fbf548,7acd522,96d883e0,951adc5e,304bd848,ccf39c2a,1c19382a,eb24ad1e,1430d705,deb749fc) +,S(e11e9c74,40e897ad,2870adda,63b9ec04,d5379f14,b3bbbdf2,147ea9b7,9c73233c,49d6e54b,29197532,3f5a9df0,3e22e359,e125e34d,d9f3d6c5,cb9f0a20,6dc1f019) +,S(91ad584b,c5ef145,3b796eca,20027cc2,ae38ee70,9ac4591c,79cf67a7,a96b2c2b,d815a213,a620c531,11f79e5d,b5d9c3c5,565dfd97,ece841e2,3e9c1ca7,530bac2a) +,S(3e725392,f4fa34f2,7413785,cb322f35,1032971c,2303f3da,bfe40461,4299f8d2,8e323a51,5addeed5,f3548217,6907efa5,4e0c1d57,7895839e,42223e5,7708ae85) +,S(5e87bec2,1a589ab,483c00ac,fa863f35,91d9119b,9a0239bc,ed4f5790,45a98160,a88a3b84,33c75c1f,9eaf3c15,9fc39a6e,33625f1c,d0cd7263,3a825023,f2e7d47b) +,S(c2e08613,2cb93b35,749cb652,44e99909,da18260e,d252cf32,489fa1c8,d268a0ed,d3f2e3ad,1f16365d,d47038d6,854e0773,28e02e64,d05a4764,4ed0d82a,8c8a5761) +,S(99304c5e,ccf09869,7b2193c2,31dd9432,3b9053d0,e4b9025c,74517ba7,f1978af,ee77fa7b,638b1396,6f7e77e3,43dd04a0,ba1324f3,1e0da111,338019aa,209dc789) +,S(40e108c5,59adfb37,651f04f7,9ef895b,25a79f03,650d6b1d,1c21322a,b4c5c6bc,8980ab38,5733e648,84aff3f8,c47fef44,210eb2ab,a094eb2c,fc5e464b,679db653) +,S(98c88257,2d190721,20c31528,7f508df4,6d6276dd,c9711aca,5bbeacdc,88c6a78a,5d1aa976,1f5c5ffb,d12ea7f4,8d0510e8,b57f270e,e08c3256,f3ee676b,41a61c92) +,S(389aba7a,25e92306,b84eeab7,a16c37ae,d7b4cf09,806ff740,c05dab20,60af6fed,673ab0c9,ec48e2e8,d372ed60,812b2990,49c32637,35f61c13,d6f0ecf6,d065707e) +,S(9dc64fba,832c956e,93efda62,e5f11b3a,6619eaaa,e539b732,61453eea,ca53df17,180b38a9,3c56ab10,a4267ad8,d8a358ef,8f60a9c9,9483889f,956bafda,f45990ac) +,S(71118e18,e758c2c3,6203528b,8b6707ab,b3439b0c,d3d508a9,6759c155,fe4921f7,c2f61af1,2c1b4e9c,64908719,f11b52d1,76bf22f7,e1cc800b,5ec79e28,aa104ee0) +,S(33bcedee,11b9f3c4,924e0990,9adc5fd1,46218b23,6531a242,45cbb2a8,3ec3d096,15f4bd1a,6c22c673,941a71f6,f4694fdb,f83b3e8d,5e317e9d,655057d5,acfbad0a) +,S(4b599b3a,4ab5e9f1,f161e5cb,62fdba55,fbd97740,759871d0,e66b20dc,4e13ccb4,635039da,9035272b,8b706411,9b342144,6c96019a,2c6df423,2b18299d,4a3fb523) +,S(19f6bf4c,dae54ca9,41b8f642,95136026,decb8dda,35e97627,5694365b,21269d10,a24c3fdc,90b06dc,264bca48,db27502f,6cb31a6b,609d3f3d,a1c92c64,cf3c95d7) +,S(bc3df1a9,8027d14d,f6629adf,7ad073ae,8a270875,b177a67a,58dd46f,1b01314c,99dd3a91,54f51d07,2ee33e89,8d915189,93445065,770745e7,b6bb30ab,808d1f76) +,S(977a4264,521a70e8,4d25f5b5,ac1ae5f6,c34ef31e,4dda285e,1ea4a9e5,7dd72c3c,f1bfc819,7aab2fe4,d0425b61,cd403fc9,dbf44f68,32e1c41b,4fce9b2a,2b9f5a0c) +,S(5fdfde4a,85fc5a53,3ed4a7f5,8adc7b1c,98b2611,3e271b47,a1c12c69,dc9b339,3edf2baf,bd6a94e6,951bad5,26fb24b9,731c2588,b2f6eb1c,e9744f49,16c6555a) +,S(b8bcd72c,dcd28afc,eeac1ac5,fb7db8c,afc17abf,82268747,6ce01423,e9e9c50d,82633c5,2e6dc057,9bbd9f2f,3771a53f,9cb6d0e3,6a7566db,f2c45125,3c55e9d) +,S(6c20b999,133bc37b,c65d01eb,6e946565,721bc2e9,da735c72,8be8a41c,1f75c77,20caf674,d8ad036e,25f42aa4,7a5ae475,5e36f44a,79c5ff20,cf6df115,40b71e53) +,S(bbe49141,69655f15,39ce4bf2,f2e919df,f877b5fa,163b7a70,22bc91ad,315adb07,8cd3e025,6618d23c,8c6b5ff6,db8fc0a5,a77b847f,ec876ac,40f76e9c,cbf93a1e) +,S(748b3078,e38374bf,7f39bbb,c5641737,bf6dfeae,d3980c75,2fe14c68,65e73786,7f7cae0a,61a7dfa2,471f2c80,217fc847,ea86b6b0,6297aeb2,8f3c94cf,2a811ac8) +,S(fa63c7c5,b89a2559,4615728b,2272cf68,5d39d592,d052c0bc,d23d48be,c764e4b4,c4c32ba2,77519d60,e1e38a50,6d67d041,804724d6,5cb4dd31,fbe7d692,8ca47c8a) +,S(c2ed7197,d014e380,a365ea13,d948314b,86978eb0,70e3e444,6c953871,cc56c91c,5c73c293,e7c1cd18,f7854a09,d8b9b0fb,3404bdb7,737dc070,9bdc01e,466a595e) +,S(2514c1ef,cb1e0bd6,c2f34e84,6f3e8006,6c2b3a04,c0d9fab5,f22fe872,6fc266be,c5a0ac6c,7bbb2d9,ff844346,65f66ec6,507c35dd,8ccf9ad0,69a1ec94,c64e3eb3) +,S(5bc07f05,11647f2,58dd2e28,9a3843c8,e2318bff,c96a081d,f6b6ef94,4e286350,213ce093,1217ef04,8d87d059,f2584db5,7a91c465,1e112c66,a41f85ab,ebbcbea4) +,S(edb56e42,97375b03,8a573f70,67ae464f,db9c1335,f6a03dd8,862a1731,9a3cad8a,d3e61a70,16f8d546,61b8414a,426f3be4,35862053,1d981f2e,5314447f,15ef5fa7) +,S(2925276e,78266481,3bf60998,2e028646,6849088e,d0d77d55,817c8d9d,73d5d732,f53157a6,a97c989d,ceb2d755,dbb0cee1,84b33213,20dd6d22,62213fe4,9a87296f) +,S(2c4579d5,bda0f4a5,37209882,937019e9,91286379,c3afb37f,a69e409e,9bc0a034,e150bf4d,d889c3c4,655acd23,f3107bca,a319d467,b1b9b15c,336ca934,5ef7d99c) +,S(5f260a00,11f9f876,bbe29587,af31832,25f21b8,42698b3d,5688545,a8808904,6ad33a32,164caa1f,7a4d2ce0,6eac3d,736fe64e,d83a3a9b,53473a89,8ee02350) +,S(3bbbbcc6,12095ec9,52fd430,f3fb3d6a,9d1f2c69,e8885100,d65a4d7,b6ac4b8b,328e828,3846a6e7,db5b090f,8c9581da,40d244cb,aa3b5948,7e4f9faa,550b0c97) +,S(4985027b,dd0fd8ef,fec9af44,bc222f4a,343e80ca,662d917d,affb9d81,956acff0,58befd2e,37a36e41,305561b5,f2716a23,460b1014,fe814654,68c16c22,3b8affda) +,S(214638e8,d2e30891,9b5eb2d8,803b3dcc,6e826ecc,e1b59b,92e263d5,67959de7,9dd8921,6f131e29,35ae0454,ff5fc9c2,dc22c0f1,c8de7dd8,ad9d41e4,f632342a) +,S(6ced8705,5ced2d6a,f3e3532b,965e0a31,6876c110,69155f35,444004f6,e6f3cfdd,f51b1aab,c86f556f,8b7ba4e2,a1e6aee7,55b17c01,997fcac0,a83eb56f,7e546ec6) +,S(95016e67,876c936c,e3263498,7859d1ad,7fc3ade4,4219727,30a1c886,c9dcfacb,299e78c4,e6e266ee,ffbd284b,81005bab,4df7232f,2e43f160,742bb392,6af4f660) +,S(4bec66c8,30401c39,c42a8019,fc398cf0,c513d20f,9db30763,51315319,ee6f23f2,f1dffe92,3b7d4609,871aa916,28929c63,4fcdeda1,543fc3d5,8e8b2735,938514fc) +,S(b50513d1,40513b97,6ac0015a,60e53ae5,9088c4ef,5ab27bb2,7f3eda78,848a59c7,9c41cae5,1182e6c2,3fb254c5,1b786ccd,dc9282c9,a6674841,29a5894b,99c36399) +,S(59792653,3989a039,6a69bda3,8a920ac9,d1df3845,26f30c00,3e415593,b690708d,7f2c14b7,274887db,5e656192,19ee6737,4c750cb3,97ae4bab,3a3ed02f,1aba648f) +,S(7ba2b766,cf012a93,478fe2b3,e916d665,251cc7f9,3ec16dd3,4db37056,5eacf6f,14fd8938,f950798c,37121286,5e8bdd14,3dcc7e8c,2c84aa69,3fb311f,856404bb) +,S(3062f2fe,f69c90d9,fc947af8,a9e839cd,7f9e9d22,566bd3a7,1c43df9b,eab5d6bb,db84a833,49916d41,7fde7b71,9d92a716,64047d4,21a36bba,ad9972bc,6f5ee58) +,S(ed5f949d,69dbec8a,77f4d695,cb446700,f6fae687,e327cce3,db38c2fd,54b06e74,4724e060,57b3d50,65104ff9,29950bcb,2a690723,f15999fd,ee62febf,2d8930e3) +,S(4d655e03,30028276,e9312fe,a6acc010,30e12950,9ebd68d3,d7170e46,61b7cdc,90efc122,6c27549b,2f5e046b,69e97f04,4399579e,278ccd0b,7bcee523,10ea99bb) +,S(f6b60756,b888efec,d0a9edf4,a483f95b,68e05e80,c351a60d,bf166faa,eaf1b35d,27bedebd,aa781584,6a896b24,1afaf9b0,cb57e98f,bce5df6,bfd22a3d,38a5fb8c) +,S(cc592caa,942ba237,72c4db94,58d6ea2c,8c81c538,8fc7cb51,6ca30188,a3c55748,36e2f7cd,358e5608,36e77d10,361c3f1,bb6bd2b1,d640307c,63d512d1,db288158) +,S(4acba9dc,a5bb4698,21a3acb8,cedf4304,f9372161,976d0edf,6a7a032f,f6c1a456,b36e983a,b2ab47f3,3e72287e,9bbd02cd,d6fb302a,972f2166,9cae4e85,52262437) +,S(c8a5185e,42279d3,2b50e0c6,1b9ce961,7be3633d,84a9472d,97446ddb,eb061f91,cfaa50b7,445e189b,d64c1df7,4668eeb1,cdb7cbd1,ff65c0ff,3c38d22d,9bd4b5cb) +,S(c1c95567,7f4f611,365168a8,89de10ad,535faf08,65f8d20e,52b1041,d35f18f9,34f7bc3b,3d956b63,9afc1eb,956f4472,543919e1,3708e7eb,537ab941,48033749) +,S(8df4fbb7,36f6736e,b4a09370,33f7d256,8bb68d68,f3e1f8fa,8df1ae51,d236a8f3,3d7185fe,8e9a2d00,3987af26,71bb3850,cef6b0f7,70770c81,22977764,5d02122c) +,S(d635f0,e35c7cb8,f2aa1ec0,c475011d,c2c8bbb6,d1ebc297,c2baa5e6,aa5af3dd,926ebd,c588e8b3,1a990a5b,5907ae5f,441e5a99,1853c643,5e36803c,7ba51a2f) +,S(b58d12d,5f865375,7514ea38,30e38e05,3678b525,e8b51923,d6460720,b46a4bc,f7b59183,5b0ebab3,935d587c,e995c0b4,d77c9e40,ea4837f6,48e44841,7438d3e) +,S(bb7c6a8c,b4013640,ecfad075,578e89ae,12cb4253,6461d780,11ba76db,da9a7f36,7dda0258,3774d618,68995b83,5708295a,3c2f5c40,306dcba7,d1c20da1,4836581c) +,S(14f6f0f7,92cc8cef,9dca990a,7fa5b7aa,f116b819,722cfb91,9683b298,3514a9e6,88c35f92,7a07598c,90ce5aac,4b3fecb5,2261045f,f23800b9,7b96f210,3a4d7055) +,S(4b95e5f4,eb9fa118,5ddad896,457f4aa2,fb60f4d0,496ad4fa,76bbbce7,77448541,1e100787,513acb31,ba087f51,201201ed,502d1f08,f7e2b8f4,d27706d1,45e71d21) +,S(e9a17fc0,67f75b2c,6384aa31,223355a3,a7af5366,4d85dc16,dcecbd2a,364b63ae,420a2388,ad674523,62f8fcf0,12b601ab,a40e06a,1b4dac1,9560c085,de7fb8c7) +,S(39d8536,f320ec81,4ee3d066,c0834130,96d4ff43,8331a81c,f10ad544,43ff9169,70ee89e9,1478e898,60d66a55,6054abd0,c7434535,13a7a3be,7a76e367,c28ca248) +,S(9779a0c8,8624d6c,beb1dc72,f371ce26,9f95b993,c49ff20e,1daa3c,2fb636e8,9d9e27cc,67296c50,9da4b0e,6037920b,369684d8,a64cb907,d230a4ab,a0c2712e) +,S(cd4c584a,1dabb48f,6a5c09de,9caf36ff,4a97471f,d1565586,bd78b4fb,66f0b401,95902ab1,eff06e2,b3e104b0,cad2c28e,6fd60eed,c47657f0,bc433411,273446f1) +,S(46ca11f4,142362cb,513164f7,c72b776e,dbeb3f65,5d527196,88eabeea,2561aba4,8060431a,2a2ad442,dfb6d3cb,d6263c61,bcfd0946,6204e2f5,e832c4d5,a434c9ff) +,S(dfebd179,1ffb63f5,76f5c753,50031a21,eb712de5,7e8f83b4,75ac39d8,8a94a513,cfed6c7e,39b2b54c,206aa0e2,2f7c610c,6576af2d,ecb28f0a,a71cb1ad,c7be75de) +,S(c71c6532,82fd063b,44633d9d,7a3b94f9,43daf1a1,7967ffcf,e1238087,5d2c2469,62fcd7cd,e006a10,38df8aed,33abe940,f18a4736,52adeba8,620d1b54,a9d376d0) +,S(63ebbb4d,dc8ba09a,4e2640fb,f48fb75a,6864ec41,fc1647b1,e9f8aef7,34c0ff37,e59d034b,fe16f4b,41d090e1,97360a85,dbf1ae4f,719c62df,77261205,da4ae28e) +,S(650498c8,cb51bf16,58eeac61,61b5892d,ce6e5c1c,271d40bc,eea14db9,dd12814a,e7e66785,5d8e0193,24457769,bbd5d14a,7a614585,70ad5e6e,e3058af9,64a65279) +,S(14a1e03f,14c00863,81599c07,b816df44,90815a8,40c1032a,c41225a8,47c43fd5,35244a59,bc6ba88f,46f66bac,9bb8ef5e,4926da38,50532ddb,6d6d606f,35fe98de) +,S(1c9f6eb2,f6a8a1e8,ecf9a16,71154fff,a4f14c15,bbd50f08,4d6f5315,b6903c5c,e6bde1f5,2ca4ff63,9b66fc51,150eee99,5386383e,402f987d,d266065d,2a03a033) +,S(1de71eed,bade7b1,7c80966,4b00d56c,b43ea61a,c0e069d9,7cff0218,f970f8bf,8d4b2de,77edeeae,d78d42e1,6b7bc94d,ea99d65d,985769bb,5c67da66,18ee1434) +,S(f4432a80,ce954d2e,baa2f062,8bbab706,479f7a39,838d8c3,5a96eaab,5f5cb4b1,96fe6ead,ac34a40c,3cabf47,6fc09382,91f3d077,29c6302c,c67a189c,bed579bf) +,S(8f71e74d,17964af4,5623d3f9,68424119,cb37e1ca,c1524737,4828b5e9,2519fb21,6990f0df,d7f0ca4b,a77678e4,ebf6946e,5b6b1720,869c91c,ec07854d,604865ab) +,S(22293157,5e3dc25e,665546e2,96a83a0,5a09d052,cef701a1,20019c49,c6a6745e,aad8f887,42585607,c792bfae,8a941caa,83d05082,defea859,86a6ead8,e5c423b0) +,S(6585fe63,2ef3a69a,816bf5f2,9e627650,cc57d069,3ac217c6,3b73aa2a,f248a096,820aa3bb,20281afe,edd9ca57,76ecc55f,dc8a893f,de11f7cc,12f37a83,6cfd9f8b) +,S(22232479,e55c51ca,b2963ce5,abf06281,aaec472f,e610d339,1951e41f,93f67bf8,e192fab,2cf22d7f,e67f0514,bc1329a2,888a8f18,a8d6504b,b7c5a9bb,ebf927d2) +,S(da8d02fd,e4ba069e,bccaba08,2b396c94,4460148b,70e98f60,8267db02,5ddf16fc,1a30213d,e337084a,47d67e7c,17a2e64d,579756d0,8b66f4d0,59a76021,e63fa2bb) +,S(a6f72c62,854e82de,f1b8a006,64c4e8b9,ad49e092,78ee6501,708a9f18,92202ebf,8355972d,b08c8104,b7c56ab,c2318ef4,2e270989,1a83837e,daa35537,1d03ba4) +,S(2074fd3a,309e85a0,5c6f8c35,937dcc56,a15077ce,99f5a8ee,8d55ae2a,3bc7db68,e11c02ea,cb783c94,2e58fc0e,806a9816,3061a4e9,aea9582e,53ae1f08,322cff97) +,S(49ed5666,fae9aa2b,70cd316f,62a4b62e,29aa59f3,1826106c,8611c50c,79a1a5e3,5ca7e49a,ae144e2b,dcc0b19e,f986967a,1477c209,2a8368b6,324bac6c,807643f7) +,S(2289ec7e,a1f7e4e9,85f472a7,13722335,b582c4e9,8e191468,3ed13622,5d6c1b1f,11e2b18,689a0867,517bff6,77fe40f7,b03f898b,d70e8902,61a512ca,d9cbbeb1) +,S(ea852ebe,23f1efbe,d8ac64a5,58ba2ba3,fbed0e52,ffc70635,bc49cebf,85859164,bde2ef41,fe97a639,73266583,c7a4e415,25ef1351,5b25cfaf,c97aa635,41cbb824) +,S(5718f0e6,7cc07d3f,1ac1a954,8fef08a4,290b257d,a4179855,3fe9be1b,d302f0ea,98ec4d5b,fca65d7e,e72d8bd5,3645a6af,a6ffcaa0,a2eb894f,d4e40ecf,69acbe90) +,S(df62a2ab,d77afd7d,ee8f0650,b75458e3,10269c3f,780cdad5,50bebd92,4b810a4c,6a6606cc,e384d27b,80bea2e9,588663e0,8d39fe8e,9fc8a8cd,54c4829b,8e4c034) +,S(1010fc45,4b0c268b,c339bd19,809508e7,9ea7dc0e,efd779aa,4974cdae,805e6668,686a3adb,681b597,2b30a323,c6b6bd2a,d51194f1,9d3d801c,3fa65fab,e96410e1) +,S(8ac79852,75673818,69fe93c9,6141493e,72ca344,790487b,d5425ed6,9f5c5c18,bb314248,fcbfc867,d1972e04,b9ef1f90,775375f9,9d25bcec,684c72c9,bdd1c08e) +,S(560e9711,88cbc7ce,c61f3bf4,45f0ade3,6f3f174d,219a160,5f9c8692,3f848b9d,9e92dace,6775cc67,bfbbf21f,c64e6e9d,4d133f8a,c18ee277,bc80ef6d,d1b9cd2f) +#endif +#if WINDOW_G > 10 +,S(8c464204,4b95eb66,cc95ca08,8469a1c0,28eea52f,7f709fe,e6d90700,f5fb943a,bf10fc5d,7bc25181,301b3a61,5ebea597,a3492025,953c3aa5,1a11271e,689d0223) +,S(d293f74c,f3578b71,35377247,cd8b0367,7f2245da,f87453cb,4d8d1cc2,871eb5aa,86c2013a,61dfea14,63e45931,eb09050f,f080e3d4,ae357423,ba7afbed,a64a6f26) +,S(35997241,ae757b1f,c767611e,c76eb935,fefbf7a7,33666aff,4c6bd744,d7687d35,bcbea61f,246bcd4c,c3c7fd35,7bd393da,2f36e0ef,cca0df9c,5994f96f,c44b2aa0) +,S(d096097,7467197d,3c1a0ef8,44ee35dc,285a17c5,bd9e06de,15f36025,7f6464eb,342af6a5,1e06db96,692954bf,c79cde9e,3dd44869,9e88b03c,d144b70b,bd3e8783) +,S(8e875c99,2aeaead6,7033a375,521b0f85,62125797,23a25470,bfdf898c,ae41a777,24aa273d,2d2f19bb,4e0ba10,13127ad2,a5ee6686,1ec6d0c6,53f7a989,73c0bbd8) +,S(bf51d1f3,2703a4c,d1ef47d8,172c5bb8,d622f444,8569aee,d2de1b9,ae541855,8f81e352,b6c60acf,bbde6e40,99353d84,5918d931,4433c30f,3c60a710,cf9aa484) +,S(6fb6b462,9726856,22b2d141,5083ef56,2ebb115b,6cdcbaee,696a8d4c,8341c26f,9c08f7bf,f9cfcd9e,cca923b3,e578ba45,468f2530,baca2032,a899f060,b83c9c73) +,S(75583ac1,8b5cf78d,7de6a21f,aff045fb,1db56f68,6b2cf89d,678df62,93617438,8259a22c,b08546ff,e237c7d,9c669c96,71fe014,9840082d,f6790340,9b69f900) +,S(42611933,a5d759ac,7ec51c89,31b76e95,4aabc2e3,61010f36,cdd8ff9c,b4c0ef92,88a644ce,6b8acbce,a378e2bd,54eb84c5,58cde403,f99cbf8,7830151d,21ed4261) +,S(243a6dbf,6128f964,bdae776c,a716bdc5,6dff9c95,24baf845,8a43ade3,af520d2,262a2e8a,5ce4db48,d0401850,e92bdfa8,3dda744d,a3d9025d,6091f1a5,201b7e50) +,S(212bd175,1548c6d2,41e9307,a7df15bc,46e57e2c,5a76bd58,82b1291,d8c2ac76,82461bc7,6cc97273,267cc788,a8b24082,57f04ef4,719a9aaa,e33cbd77,79391bc) +,S(da6d030b,21f96d2a,aa0b3e56,28ae0ec8,8ca8a546,10bd61fb,c1a7c16b,4d61177b,5b5bc635,8fab0287,4a2b8596,1b49ec29,2c2aad1e,1eabbd0a,1cebdbe7,2e3f9fde) +,S(dc230af0,55a243b0,a581e101,2eea48fc,4a43137e,cfe80108,86a9f4e3,f6c1f01f,d0f98e1b,e70a458d,7030120d,a3d7feed,2eb5a9ab,601718bb,950fa03e,568180ca) +,S(82c2de3a,c815c686,98ce73b5,6f232684,ee9c5972,f0a44797,dc16d04b,a9089df1,1c847065,c6c2cc18,fa253b8e,c1fe178d,c859844,2c1fa432,54462d97,93b8573d) +,S(6ca2de88,74e4916e,e1965dfa,200af89a,cc439f20,6a4077f6,67076149,e20494a3,6ef0b344,97aa85dd,a43cc1aa,57dc7ce6,cd51119d,ae96ae06,99b5119a,9a662043) +,S(ba2687c4,1728fb65,206448f8,28400efb,cf1149a4,90bd31f8,8d33af53,7851d97f,96ad25f1,1f2ce7aa,e8b803bb,3c62cd32,3345500,243b1de5,d61f20b1,86b4c0c4) +,S(ac362040,1947f301,16032d5a,4b303323,d311f533,e46a1aa5,f1ea2f08,d553e0d,53839f95,fef32884,5a75881f,a17db91c,1d5b981b,7b20937f,df171c3f,2cb30a22) +,S(6780ecee,5b1b7205,68d4241e,c3b35c9e,4158c7c2,2dbba46b,ac769476,79049e13,9ac115f0,31e0888d,2dc90b9f,3562e732,46520d57,ecc7ae4e,e0fbf401,3057d9a8) +,S(95377a47,e7a2d5d5,30510d62,2bbd3439,c0f7653a,ab294254,cbb10390,d2a63732,57ac9110,9dafb62c,fe1abe4d,fc64557b,bb01f76a,36649d7b,91be523,f8195fc0) +,S(4f195bf,98fd83bf,7ee97c5,9bd42af5,486a8507,3327415e,85f17a17,2add8921,a56acf25,1c82323,6327cae3,6b1cadd,1a057bbc,f1ef3b59,a865c7b8,691cf3dc) +,S(eccd1fd1,efe8299f,d2208240,adbaabce,cc0510d2,d7b6f6ba,d9e291b1,4cbcb53f,ec2ff321,6da50fa5,c57d6f2d,3198e019,2d9b6f8b,ffc26b29,13580447,a42c667c) +,S(c757444a,f2c3425c,728ff41c,f7cb7475,502e7601,c1edf2b,1eec84e1,b058f438,e1d481b6,169f143e,29701105,bc8b6657,9cd267fc,685014b8,fd070283,6362e53a) +,S(3562837e,bff50776,58d99662,7a11a367,aef3255c,1c470ac6,36f1b57c,645250b8,cdcf4a0d,97aa58fc,ccd374bb,bcafc00,fa1d0d35,ca7fab9c,f4d72d66,299234d8) +,S(ce9dc245,f62e97ab,d2d339e9,7fac18b7,b1f2f07d,fb654a82,b113317c,86d4a3a1,f6e1e325,342afb8e,64fe8520,ec37abde,fcaf1b30,be6ecb79,27ba5615,f0d184d5) +,S(ac022f3b,48ee44ba,9d271a8c,1a01fa8a,aab4818c,e5383dd5,42d9d6e3,2c858a12,1e0563a8,91f8d11f,1989e091,739611de,191e497e,66055aaf,45db9ba9,72ca76fa) +,S(d1d4f682,cb12cfaa,e46e57e6,db6f2277,55a83782,88cca4ff,3a999c0b,a651d9f9,1b19bfe5,870e1931,4c23b4b0,71672233,1607264,acb8e960,488598e7,85e71a36) +,S(79962171,bbddaf94,45fbacd4,f9bdd1a8,892e754b,2d0052ee,2435303a,ff1c8a37,b8ac27de,2a952beb,45472f77,b34c1354,fe47b6a9,daf0720a,b8515be3,7bb98a7d) +,S(445a17c4,2390b604,bb3c3573,b061a2f1,20ecb3b1,83a0f8d8,cf032e3c,509d3b90,b75f4088,7c07fe5c,a0c057c8,7eff4966,4ea76941,e409a6aa,e34ad4b1,693f97b1) +,S(6166b5f9,63a9f47f,5fac49a7,8d175763,8c39cd49,11d16ccf,b0e83bc3,f402051,ba337661,8302aeb2,213e4620,4ec57492,933d6df5,5a045d7e,5317ddf,e92430b3) +,S(cd666e74,28901afe,8e67d0e4,48de7e3,30b6dbdb,a34db4a9,29315d3f,8b251531,af72d8b9,43cba540,e42398e7,49fc89ed,44095951,f11b24f9,e5451810,3802304b) +,S(c833be8c,fd2f6fe0,46d837a1,1d990355,5a6285bc,d7b8814e,e839cbad,523ac72,7ea8f90b,44112363,a8d75153,150c3b4e,b6c07dbe,fe8c8ed6,3a24984,c9a6af03) +,S(cc185f36,3036d385,d890ac1f,5e95b162,e40e4a4f,e8427613,98200369,f25ea477,e0768836,146d9c98,98aaabe5,a9ce12ef,f66d0775,4055eb42,832eeb9f,26338236) +,S(148e0863,24bbc2ba,b67c9be7,407b809,77ccd910,e92c9770,47943c29,8c9c4a5a,89e83c85,9cddfc80,9de733ae,6138e97d,b706a331,fb7176ee,55dfe3ba,c2408797) +,S(9b1539c1,aba5b583,5c88d9b1,92a3761d,cf8b649,54f53a7e,460e0122,bec95c76,391cde19,45be59da,d4c895f1,3aa1ce24,d8472e42,a232795b,967f21d5,acefdc18) +,S(68a2e6c1,75fb01d3,76a9e575,ff427cc7,a6032774,a974bdd3,e93b8a73,e4495cd,153e6484,c8c30dde,5f269b74,1c599647,666cab7,e273d4f2,5573dc4e,78e00752) +,S(58fe9049,35a5a40c,3b633440,533cccac,7669e9a2,e7c2f0b6,6c36c228,61dfb04e,b36905c5,92450e2d,71d06e2a,2d599a77,71ac1e27,15a79555,b39fbb60,5536e1bb) +,S(823c0f96,dcbdd9b0,bfdaff2a,19b6027d,3e9bca6,e298378,3375a783,5eac7080,d91546cc,f34d9a58,babeb6d5,43bf3c36,8831a2b0,d9043db8,895cd208,f2ad0a7f) +,S(4a56aeae,e908f908,55fe7b71,3013ee7,d6812152,4f7decaf,533cde20,2d14537,7e60c801,33b718d2,119995b3,3790eb9a,3129a7fb,2e030d8d,21af90a,c622b7a2) +,S(6c0f3855,e6c4f5f2,c4594e2b,8881319b,82a64bf4,9fe0968a,71f6ca3b,f9240334,5c8e7b6d,a9efbba8,4b5ade85,305aead3,e1478ad1,b4d56fc8,3acfe34d,23b5203c) +,S(d9cf8f2c,d1704907,15dde21c,f172e648,496e40b,5248f331,3aeba2a3,35ac9632,92a7c251,b440f930,bd98299,c35d57f7,15867e90,b6c3e8e6,b073815c,3c934344) +,S(4bfcb492,8b91dc7b,afa7ed43,d9af3039,77592728,fcf1b9e2,7c6767f7,b993d898,276a06f1,ecd9d410,b34c9a4a,e3aa33b8,1854a4e9,e4703dda,7bfca949,a45d14f7) +,S(abde5893,ac4032f4,4ed93dee,8156596e,7cdcc575,e5928792,944019ac,23c434e8,5e03a809,2b9e6044,e942af7a,7cef02fb,46162cb2,4c19916e,d774b00d,8195c40a) +,S(205d6307,7a98a8e1,178814b4,9281da20,4482f1ae,31be20a8,3b3b0fd,526e1cc5,4bbe295c,943e3560,6b95cb19,d6e64bfc,b9cab7d2,55657614,7a79ff50,6fe4d780) +,S(cb4c4c83,e6673a1c,6c228a42,36e6031d,b59653bc,45bf4b7c,3b98cdd2,441c43bd,142e0276,39267ea0,f7c2ff39,75afe714,62f3340c,1fea5149,cfe4e8ea,e31a2769) +,S(25a1f4b2,7ff5bdfb,3af54a4b,493356ef,92ccf0b2,dec7c4f2,b5abb225,2cca77a7,1b25dd93,153f8341,7df3b3af,c991f2d9,36c32e8b,288a7fe9,3e9ff97b,335c3c38) +,S(222f76a7,7eeca0d6,a511c863,a657cb89,5a1b7154,957b66dc,89ca77aa,2a54237b,a8f2bee0,64eff88d,87a598a,8665d295,a205c884,2f6a3492,7945a02f,2cb0b289) +,S(32d4328d,1ad2651b,41bf1015,6936b406,379f5c26,99f1c2da,ec3cbd8f,3b4dfa8d,9f6de6e2,3294a33b,5f4d4062,3e478096,df5de0a1,d069290d,204ce930,7988261d) +,S(cde9ecbe,6948cd70,3b193790,6662b7d,187d0830,cf991b14,e8265691,81f1d3f0,84887b32,e1218d2,9b77794c,e8ad56c5,f5abe8ff,f95515e2,4bce81b1,5b029720) +,S(bc89f45c,3baaef4c,a8343582,6506a006,509f4e7e,265e4b80,11d53d3,a748947d,eff5bca2,714db146,7a272fce,534f79be,9fa15af,7226ba49,51129f5e,6fe68475) +,S(62678cc0,eea2bbfe,2bd4ba65,5434a9ad,39b409b9,b57efdf0,9940f748,ddb51d4f,8d384f4f,d234d31d,1c161245,ef6dd54e,a5cc8ab9,e7bcc883,6735051c,37a74acf) +,S(15e01d39,c63ae225,63dc89cf,3dfa2736,df7c0ac3,4fe86f8a,f219f933,86bc93d0,1f563532,314fcb81,c1725133,49769861,e7efb999,fa317ef4,2bc75588,10cfb13b) +,S(4f8d2b49,3493258d,ff98e813,e428ae9c,52c60103,1bdbeb9,7b8f364b,2bac10e3,8c7d117b,67955db6,c0058028,3f919f57,1e8c58e4,216ca1bc,d66b264e,169b73f) +,S(d14ce31a,2bf16b60,983e397c,ddd3588c,3b0d77b4,f449136f,1a789276,b56f1d61,9a15f620,b227d83a,3722ca6e,4dd7bbf5,833f24c7,2584f356,11ef3734,ad3f0910) +,S(35221b4e,b8c275f5,c10d656c,9bfea000,24c1aa2,c4130841,9c3e8e3b,66f780f1,3d1c5a02,5274afcb,bd78382b,b2778657,6888d46,1b5dffa0,f8ec5dd9,3bf8e808) +,S(82ed9c3b,1c76c1b8,f44ff6f2,9f443b20,83f68617,7270843c,49eac0aa,34a8ddc8,262e9e6b,c2ab2b86,7342081d,90002690,e81f5a58,aefd6fc4,8a91dc5d,e95f1340) +,S(919e960e,acf2aa36,e86342f7,742e97c4,9c49b64a,ff5c6118,d181e349,10717b85,4b51a94e,a3ea1679,cd9ce9d5,ac329f13,ef530d42,c7821650,5a082fe4,d945b861) +,S(cdacae3e,20ce551a,beeabcaf,a643ce62,edca3de1,acf06c30,65ffd366,5bc3ec7d,aaacbb13,872b076a,e28c326d,fa54f4ee,235df4a4,b341c9b1,3d322071,6eb95bb6) +,S(1a4508e2,1bd05c09,f3d80f82,90726f08,de3a62ff,b663d5a8,f949ce26,92a5f2ab,52780ff0,976eb033,760459bf,4bf02994,c6e672c5,6d25d3e6,2b320f5,2cca97ba) +,S(8e46eeed,5686f25c,a94f73d1,3f26c98a,e9129f77,f3aa18dd,d5214262,803a0b45,ee56d38d,7041eea7,c87f2ed1,e7f0aeb6,7253d556,610e8200,782c9af6,1dedcf4d) +,S(782910fa,3bd6b309,dad797c1,5b618469,68d6171e,7a0a0532,a92e5bb3,697e4031,ff178365,c44c2a28,e84c31b8,946b0a80,717914ff,6d3f22dd,ee409753,44e4b3f4) +,S(2ab9e8c,c5a710ca,6cc2d0a2,94f1d7d2,65c50aa8,f9069041,ae93c91a,e5f64e8a,aabcd0f,a2254bad,67cf23d,a5934947,18536602,a13d81c9,db23afc9,a7f89b9b) +,S(d04667c4,4fb25b22,8320cc53,35252937,a094ddc2,4610f7a5,fcb1bd1d,6382c9e5,7c93613f,8bf048e7,86af436e,cac4fa86,6e5a91d8,3064f5c,e31d4794,b2b0f74e) +,S(b9c476f9,799cdda4,a51b0476,c5838994,f157bd29,c9361252,def4af83,6e86b0f7,2ec7619b,c77948b3,f25cefbc,46845527,d1a9b0da,a90529ef,f13aab4a,ca97932e) +,S(90bdedab,f13f9940,4b9d8c56,5d64739c,4ef765ff,dee2ae0d,64fda841,286eee85,a9f47882,d9a848ab,4be812a4,8187d26c,7f8bb8dd,b94f7725,5c628104,e0cf21f5) +,S(a0c394a8,2177734,c02b8310,74ebf0ff,ff4208cd,dcd59e7b,a680737a,fc485c84,c4f08932,b4c62f34,fc82434f,e9b7b164,e835cab6,2f34ee3f,d640e5d7,d09364b) +,S(1b0319bd,3896e8c6,284f949c,72e7694e,bd3c2923,ebeac273,b9aab50d,30294c3e,15dbfe9b,17c3aae7,e5f8233d,703b7b1a,1401b672,1da32080,53ed52f,a2ee339d) +,S(9914ef0f,e92f16be,a27a9354,518dcc0,1210eb3b,3a90f2ab,6992e3d9,bfdc719d,7a0bd065,cea0b11,e8df3629,faf65a30,5e671d2c,86332b83,98ddc2b1,b3c4086b) +,S(3a68fa19,5599f638,7dafa176,395c9615,5798ad6d,7ace7810,daaedd13,ddf60d3e,74575ae6,a3f569db,394faef1,e0641ca4,de158eee,9a8ac54f,5cd96bf7,d4e71a89) +,S(b7361d41,e2b67e31,f1872b1,b1bffc8e,41193dec,7f012385,9da69c60,5e88b2e5,fa9d6b95,66d49bca,73cf5890,f6e7acd0,315347bd,f10be9c2,9b0907e1,80668344) +,S(341e9987,42d84640,f6fd1354,3fb7ff9e,aa93a4d3,12e99bc6,37b85b83,25174e81,c49608dc,91b46187,611cbcff,762e5fc1,e95b4e9a,6ed747ac,8612b6b9,8bfcfc95) +,S(83d6a445,de34f557,40084909,402c9718,1f6420bf,af87426e,2a0f75da,c483e6f6,7a86db44,58c8fb53,5edcf48a,14a94dd8,e9fbed09,277db0fd,787c121b,a1dddfc1) +,S(4f11235a,14bcae9,89082bf1,c6a499d8,32d17686,2fcffbdd,a40b84b9,26ac351b,b50624f1,5f31269d,168021c8,37739cb7,8678ded,be59e288,6e963339,e94a2acf) +,S(8fbb5727,21cfe12c,ea685907,ab6e3d7e,eafb3892,c082115b,998e9a9c,d3ee2b89,da7fe685,c8148e1a,5cd8de8e,2a5b5aa4,ef1bb1a5,af8bfab7,91c923a3,66906680) +,S(804ca63c,b9243f5f,76605b1f,53f5989d,286023f2,fc672bd6,3f52c59f,5496c3de,372912,eedb1156,a1b8300e,eaeb44f8,865fab6d,49c89fd8,43170b05,d75233c6) +,S(b2795c6a,b83ca824,6b18de87,5660d04,60b48ed7,1de9bbf5,8cc1769c,2d2a5365,66e8c2b5,88530b6b,4d351044,186a8a01,db684157,9bdc041f,90d0935b,2a49d820) +,S(deb5304d,b61bd914,c0939d07,c0464eb4,caf3903b,1903a588,208c5e05,36d9dad3,18d54f43,e7e45e76,6a4b6249,1e48073c,40507dd8,3f7b3b27,762a06a9,4d5777ca) +,S(111bf502,d177ec34,b6c61d69,63366901,eb79d5e8,5749f5cf,7743cebb,9b7714c4,be80d52a,bc38bbcc,f036bad4,5e47a0f1,7d8ba20,fe6780f0,bf8c38d7,696a4e54) +,S(94b10f43,c15c5393,e8d4e71f,db863865,daceeb50,674d0f8b,161bd38,dcbe92a5,ec55af2e,a34e8c36,5b3f7729,110586cf,8d087dd7,f1a0ae9b,d25b9b77,f5be6dc7) +,S(4627abf2,2d447784,c53e529d,62d3036b,fb4a422d,10146248,d3b1f188,e895593,8b341d8d,6110b59b,738172d5,6651e948,cbea9ef4,c7e8dc48,3a560503,eb7b3e4a) +,S(19ce15cf,dfeadcc3,7375727b,5ddc5327,585dcabe,c172aa80,757ba468,297c355f,506555e,ce829141,736ca9ec,886cef26,83853e7e,5b3ba66,25cccfd8,4de60f81) +,S(75a40681,8293fcae,8c3e97d8,e1f916d,2950682e,16d3def0,fe6dcfd0,ab9a3e55,5292d5c0,e02ee24d,8139f2af,495298b7,4526780b,f39f5f22,a047d155,dc8c7930) +,S(a7eb8b8d,ef4b7686,b47a341d,4f4a7333,88cb82ce,8f3226f1,cbbf79bc,1f3bf578,85d98b6f,e238577c,b3b52bf3,94a72589,24407ca2,86865cc8,80cfc93f,bd8199a1) +,S(a5b9a2b5,dcc91d67,b352d14,3c19f6b9,6c9f8344,50e16c00,e8a938de,b3da647a,f12d4fa8,39850471,64335aba,1260cff1,26fc4e13,fd16cc29,63472f28,7b9571c1) +,S(7999c6ed,48d589a6,5df7c20e,e9b0fdf5,df58d831,78ad6171,57c09cc8,44015cca,156f4505,3706c3a1,c54b3fec,bd3f12c8,aaacaf0d,22755014,5bcb731d,418f5756) +,S(88fe81f3,b708edd1,b85baade,6c05c7ad,42202058,6dc2e8e2,fead379,c49829b1,cd62e24f,b04b7d2b,b1434150,260a052c,8972a8cd,daa1db10,d99d40d7,df727c01) +,S(18fb0eb0,cfcc61fe,423192be,b694a77d,91216537,5087a64e,89557d5f,4fe86763,3cf72b1f,dfe8b565,38e88e23,ed871d7,80f4a6e3,55229922,b1f28c5d,38169785) +,S(dadb3231,2be4f2cc,cff171d5,baf18e76,4719fa,eb5fcb66,d919c86,efd38aac,8d67945d,752de8bc,b0740e42,3a154449,50e0b330,c6a73981,3d9f4b30,f98bc0d7) +,S(f0b9a086,7cdd9f64,6b3aa0cb,e3105ba,7b17e09c,6c1c39c2,d8057f06,a39e5c8e,2b534e56,25e6d180,40dcf431,9821cbbc,3ff91303,c14bdc81,370c9e0b,5ef4e9de) +,S(dc0e0b05,2e49c3a7,e5d0e253,672e8d43,1cd60166,648b932d,406fbe8a,c2fcb7e0,ff919786,8e1c651a,4cd39cb5,6921e24a,223e7af3,821fdbea,86547e1,c7b791d9) +,S(e71cae98,fb6ddd47,35ec365b,515c6ca5,6bea748f,11811038,b74aa5a2,32eb91ee,804ae818,396c20f6,e2fc5bc7,5dbd1f41,9ed731a7,2801d06c,e6682bcf,1d600266) +,S(50d1b5b2,f7b2e4ff,3b594d48,6bd2ca6c,6b5d5368,2079bb9f,88d792b4,d4ddfd8c,556cf4c5,18fa0660,86b9b03f,aba18fd,5c554fe7,4020df5b,6e34bc88,4d5cc744) +,S(c087403e,2bd5d954,df8c5e86,f23f6cb4,96634886,b4709747,325321d1,bc2701aa,9db44987,d8517300,6e3a549,eed87e06,258bc14b,e2b00679,3a2252,a3507712) +,S(dd1037f7,f3843422,de8988fd,3f68a299,4d45f8f5,1e9242b3,7980f517,bd6d6894,ca8e6d61,5cfbbcdd,8b1ab8b7,673093a6,1a9c08ce,56b065f2,54fc191b,25cccce1) +,S(de229a19,67ca589b,f4a840c3,d18f764c,424dfda5,6c099d14,73a9cc7d,34b5ceb4,958fc9ca,84ca63a9,4323f4b2,ae46ff44,a1d352c1,1b8e8be7,2d5115b,54122b22) +,S(15e33c9,cc9d84fa,16225f48,70a35a42,f8a3a3dd,67a8dcb1,143c7ee2,4cdfd9c,413c0206,ed48cd5d,36205836,45e4ad6a,a59a1e15,c7f36256,2f89b9b3,660e920) +,S(53422561,ca3c4d87,2d23e317,3b21ae38,dc010b99,b7ab0fc4,394e408c,a3d6bee0,6ee906e5,e505909f,6507c7ab,779ce1c8,20f2c50b,a71bb8b7,8debb3fb,6103048e) +,S(829dcf4f,9b2c4f37,32800bbf,f5cb71ca,308bf0a,9176a74,a031d972,9a82b2fc,47378200,429e538a,4e249d3c,9f58873d,abe3f8c4,615c5d3,3525f6ae,5f8483c4) +,S(ed6f6eb7,3769b1b1,4923b989,acff97f1,c05594ba,1d5f4ebe,450e65c3,8ae911e5,dfee6f2f,3fe584db,8b98cd0b,69ff6219,d52e2d9d,a372ffbe,18e500e1,a2c38eb4) +,S(47e52398,1fbe720e,8fcd1982,df2b184b,70616ebe,cb4913da,30327205,c0455503,afd2dceb,f360c30f,6820cfbd,1de0de70,179f4cb8,6c461251,5eae2f4a,baf3e900) +,S(b02937d5,8f48e1f8,6f3f1170,f5e880e4,ba10e2a8,c13dd0df,49fde04b,a9037312,dcb0a4f,3e4c8456,316de580,5355e17f,8a6c7924,f68cbcf5,347d3c7a,28c18c4f) +,S(9f993cf5,3578c1c3,25c10c2d,c3c066eb,878486eb,dfdc65d4,4ee57fd0,ec1989f8,c6b8d40b,16b8811c,d666ea43,2e61baf9,e83eda6f,d38275d,9143b386,31c8bb52) +,S(943b6cc2,5913ec1d,f12730ed,bdfd1d0d,ce431784,f36fe84e,599f9b89,dd3240e6,e58102ba,cac2ff3f,d42d7ca3,254ef55a,9604d3ff,fab5b16d,9f77482,4fed3fd0) +,S(9b0e67e,69b3f9ec,51843da4,f7b5a7c8,59511dcf,47f58a64,81fbf2f0,19acfe0a,6a4ddc28,17a4ecb4,ce2defc7,d7333ca6,2e074c36,f7e49c67,4a009d30,edc54dd8) +,S(97dcee33,9cd8d18f,8c3b0fd4,48ca6e6,c8246b4,136bacc,72ae8673,a4571651,6163a205,cc20bf39,8aae36cf,c8933dc6,29b95246,48823d51,d82d2d2,74e693e4) +,S(a1e5e6f9,26961eda,7cea9c26,f4a1ba02,798c940f,2b7dc267,2999ff7a,d2292ced,50274b7a,6a710f74,ed138401,36d97657,cfc07af9,3b9e8fbe,ca18d31b,8d5f19d) +,S(473d5fcf,7cdfddfc,52fc9bdc,6fc77180,1c21bdb1,840821a6,1a984296,5b4884a9,348836a7,ad7f550,feb2d703,6c82d95b,89ff95eb,e8be6250,eba7c5b1,8fd856af) +,S(9c187442,7384b2d0,4fcc33b2,70544c98,db8c1e13,2bb149d4,54c51ea1,4796f613,2d6dec43,970f0079,3da12e47,595cefba,c1825d36,c71342b,7913f7f5,fcae0a19) +,S(11ce1c46,9150e2bf,b742d826,be740e4a,ed63c729,5f5bf51e,c7a6aa6,562b154,6e86514f,ea38280f,abdf8b93,be8b7d92,67f3b28c,af2a4271,aa3b6a62,1f4fa33d) +,S(1e037904,e4e2de6f,2893c354,18d1cc6f,dde2fd71,4bacf1cd,4d9a5c56,79656042,1bb759c9,34c26821,2a28aa38,a0af035e,3c81ac85,3c01e21b,462e81a5,8c960755) +,S(52fc9467,23f899aa,f1a94f24,f9e3620,3f8d505e,31cc6800,9951d9a7,c0071d82,4632ae0,75430820,20cdfb99,270b47ba,39c909b3,4b3a39db,92419697,d0bd6dbb) +,S(cb585905,997519cb,214f9e39,496f5386,35069eb4,d78dea4f,f9e9d803,642b20d,1b9f42e7,a5b475a1,e42a0f51,ce2d5e50,7131b2f3,5990ed45,64e3913,ec93ac7d) +,S(382c2857,29f14558,75727584,5d37bcd3,e7ab180d,a11c7a12,cecae00f,b4a69c46,cb27e912,20a70fb3,6e258684,3f128693,495907a3,e0b248e4,a25c1e7e,54039440) +,S(16f086c6,c2850e49,8f8456dc,eb33a692,a4dd613f,b2f9564b,6dcecb88,4f4e5024,2e152a08,88eb1188,3448ca68,25a65034,73cb5ece,c9ad5df3,12deefed,279339a9) +,S(7589bd1b,1f0a3577,5530f0ae,446ea275,4439a51f,66730950,6eed6177,65a02492,2d840e12,d3758153,1e5771bb,14d3634,4076fc3,630c389c,ad9765,98462d49) +,S(18b9ae40,cce63a51,3dedbd2b,c3beee9,a80cc5f5,a26b4446,9a0326f4,d4930890,24b86a4d,d5ed3c44,71b57138,2b270d0b,9e0032d7,8e0ad18a,cf6a4eb9,2a2690e4) +,S(8c1f74a4,acc71ef6,4030db5d,14be8627,94e9ba6e,102c89a4,ccfe8a8a,d3ebb48a,a79f0119,98aea529,1e3ba735,c8b26c86,1af1c119,92d5dc94,16eb1673,9245677a) +,S(8d80592d,36bfaab7,5b557ba0,d03825b4,2c0fca08,8ec7d046,5cd2c203,c2bfb080,3e479487,91f27453,4d0a1bd4,a533fd2a,7671a654,10cd7a51,859b466f,151d3f6) +,S(eab0bb19,f477e158,ec85046c,f2c56777,f36782e9,e0452770,f7125765,670ca248,a27d7081,23093af4,daf55e56,32ba071f,37f0bd64,3f189ebd,70074cca,7cc04640) +,S(ff0d0dc9,ebbb2fa2,6d3b249f,d5260ce9,9647e1d,db48799b,db532c6a,2a0a039c,12b0f6ae,7f161f8b,ce1c3505,5d9b760a,fad5b0f5,66211023,3c43942e,fab31fa9) +,S(d89a3235,71dbbd1b,92340346,8b08b7be,be22b320,3ee6b0ad,42a2aa96,3837d526,25da35b9,9988f167,3108124d,9e6f6f1f,db4a0490,5546fb62,146b7421,36e3cf27) +,S(f150023b,f01cab2c,433cdafa,86751f7f,13ffb315,55d34a22,9ec4c012,925b43fd,31f70e21,26ac896,6dc966b7,17a9e483,8ac37a6a,2072bf34,1a9c3d33,3ac7a9cc) +,S(d98ebd3b,2d580978,72ae9d5e,2c2cd0da,76f442c8,e0b4470d,9b3ee019,c83af2a8,9ed5b6a9,5a061970,34bf9873,da6c7224,930fb358,a514c823,e0017c92,75dba3f5) +,S(55b62d85,833a94ee,dfc2e313,df3ca816,8173533b,fa77d827,583fb01b,cbc3c746,9f848773,cb95af7,b79eb921,19a7a139,1f4dbbbf,44c81897,15bad111,364a835c) +,S(dfaf4d76,fef73f93,44c69de8,f6c1c3fc,ec759c49,a2a7a1ca,2860fecf,bd718de6,ac4af693,44e2c287,f6409f94,c36d93ed,7fba1f15,195bd971,357d3142,725a0671) +,S(84a444a3,4510922c,37420f29,a16ee22b,a7839f3c,5810cbf8,38b7f3e6,c5fe29da,ef733d80,2215c12e,143f420a,673069fe,6e89af70,cbf975f,a1898550,1114f06a) +,S(d7c84e11,46c87008,3ddd24d1,296811a3,2c337749,83892548,6291aa53,a72212af,b187dd58,a3be40bd,c3d357e5,480aec61,9579b2d1,4d79745,f84cd406,10844ac4) +,S(fdedc27f,a70b08be,f3a9f616,575072cc,c7fca4b5,71a2ea06,9ffea788,5598051d,fe9b7b1a,1c575a9e,354d279,ba7f4de2,53f6f54d,e7245fbb,3ec462a4,7b9bf9a) +,S(cfa7478a,d3f2cc6a,92022b35,58437b3,f950c250,4d96b9a5,c2d5c795,ef122e77,f3e48dca,e9592835,354f8eed,ffdd4c2e,2ad7800a,3da863bb,158945b2,261409c4) +,S(8e9157f8,3fd62097,70fb7493,d7678fce,aa1584bc,d8dde3f8,337dce8,ee0eefe2,6079ba89,8c05dfb6,ba4f91df,4b4b229c,3322fb5a,44fa988a,c8d9f3a5,3a05351b) +,S(7bf5efb6,103eac30,ec4eb80a,9bddff22,9d422ffc,6a7f9dfd,bea8b928,3116f75,efadb659,64791edf,e5935554,25796141,2cba4b4c,3553fe67,ba138931,3ba71a3b) +,S(f5ba02b7,b7b2c2d2,68c7b1b5,37697079,690a13fc,89d1d53e,85f27820,d0e8df0b,66316d26,b2640e41,bfc329d1,9d2d4430,a9266b64,c3c793a3,af7f1515,9e465485) +,S(5eaacdf3,dceca4c9,6d2bc3db,f22951fb,87c10da5,f1250828,4a1498bf,ff004475,cb3db3ed,9721ce09,16985d40,4831cd04,b38f5810,977ea106,c8c25194,ad1f122d) +,S(1cfe5c17,b8d61134,53552ba6,ad513371,d1320abf,4fa2de41,77c5a768,a64a5554,53837cf6,4846b887,1e64a2f7,e319f740,99bcabe6,7a0894,5edea46b,fd1dcd5d) +,S(92a83f47,8a2ccc86,1cc91dd2,a1210fbe,286f47bb,44e46880,5e21f0a3,7a5dbc10,fee35c65,fcd45b02,f8270b8b,c198132c,cb69ff9,ec35043,684f54f5,c999de94) +,S(172285ba,d5d539b0,f4d86ea1,35bdb79,73c07ad3,24bf5dd1,4f74ab69,cad277d3,45abf940,cf356364,e0bafa15,cee3e20e,cfc29040,288b4897,314c22f9,aaada654) +,S(ad17bd53,92e19bab,8554ae08,bf9158dd,3126ce81,3a05c32c,7646eb73,cb164762,a129819,1c833987,775d4f47,d9d70d4f,8139c651,896f9b94,e164574c,8d065586) +,S(10421b3e,91a4a89c,1f6ce9e9,13e511c3,c23fd307,c4064a12,5267cb31,2ebdeb1c,539bb5d0,9e49f0fd,186b8a51,73118cb,98569700,dac77cc1,92359286,3aa53319) +,S(c43adcfa,78152a3d,dcb9eba6,6632aa5,2e8476de,1e0e30ff,39735b06,21de5bfd,6ddd09ca,2122580b,847d04f0,5dacbb7b,1e81c6e2,6057d880,1a911a55,9cabdebf) +,S(f7a99ccb,603a58d0,4c6b9e50,d9e7d988,14434f1d,92aae27b,5c8d9006,98bf9a73,ccdfc244,76f4393a,e96d6809,1cb74130,5e62934c,cafc9b08,78a3a77d,a71a7ccb) +,S(9abc95bf,70174ea3,5038f44f,4a2ca9be,f602d516,9d249aac,b3175be7,e09c826,6cfbceed,3e71f8a3,d427d2c9,f8cedaca,22a9fedb,4cab7763,617198e0,d7adfe0c) +,S(8a7ca893,35c84873,ecb4af06,b493c9cf,d7bd08fc,d0535c5,546bb9ed,5c5d8d82,2518b372,90d3c08,72f1ee4e,54144799,209ae3fe,d2f285ba,7b00193,21e31bc1) +,S(70c8e379,8649628d,969182fd,862f8d4a,b39fdbdc,c246fb79,ff1c9227,a64d1c3e,c7931ad5,27ec0a4a,ec297d9e,fd56208f,936c9517,4efdb86f,7f50e321,26ea3cbb) +,S(d783f6b4,6121ee0,71ddcf61,b4808fdc,934a82e7,c185876b,9572d628,31002121,aa92bd84,14226949,9e107c82,b9a64898,28b3ed15,694ba62c,a031d98b,5e9c7fbb) +,S(c1f06a1c,29abb43a,cadffbb8,7ea8e4c6,b7592795,9c778758,1a17e4e7,7b654c18,1dae0de2,f1db42cf,ab9b0e7d,9e5a098f,bc102470,aa418547,6cd1925e,fbecdbf0) +,S(2ad74e11,3205b587,866633ea,32172662,d1498a27,48698724,87b7e62e,52cc5105,c70c7507,de1fa1fd,625aa7d3,755c83a,b5c85159,4b8bee10,5de45da0,d0ff779e) +,S(f490e9ad,72246a46,6eb74e23,371c4f57,a9e32b57,74ac9ac0,3d800978,d77408b6,546f367f,8fdd1687,ba6ef15e,4c258507,77e49a1a,8b449fb9,63a0f01a,a9dc3d6a) +,S(20502891,8357b697,e05788b0,55f8bb32,80c431c5,de07055,3f123373,981d6149,39d040e5,44110e22,58da6771,2852fd09,9489ee5b,5c77a777,7966abd0,468e3b0d) +,S(6e91bb8e,c169a3a3,eef59915,d4de9296,e4b78299,e038c5d1,bb001eb2,f86eae95,d2c21297,88f8c9f1,16434063,a89fac48,45305dd5,d208c887,2ae94ba2,80877dfd) +,S(cf2947c8,39fc40b0,610d0d2f,e1e5ad53,39e7d0ba,9e7dea01,90678bb1,6ba34905,6145a70f,7f0cd53f,d758d7b,172e875d,8db00f6a,131ec7ba,aec140b2,3f37c63d) +,S(b48f8387,ea93a6c8,561c9b01,ad32a28,1c8db1c1,ac3c6864,ad161874,fa2366f1,2cab333,cbe07d3e,26fd971b,3609cfc8,9f46bd81,df84276b,375315d2,7a2d253c) +,S(7c4b5f66,c572d34,e4cae58c,696fd43c,30f6054e,8145715e,f391eda1,42a1f827,3cbe264f,be125af0,3907c5fc,c61232c2,d0adc2ad,5f94f3b0,8ed32ad,99843d97) +,S(e5796368,d4649e19,6227f989,78758d9,8fe24e51,f5d5a567,378a85e0,850d86e8,74bb343,c9a69128,9ae880bf,67a7e4be,1fa111b8,d6608896,46ec354e,f6c9d4a7) +,S(dfa1065e,fe83e9ef,c19b8841,6e3ca5d9,67c1ab26,53c7d650,9f02ba58,fc4d637f,22d2567,eb0237ba,2f270e04,685e5352,e23fdf19,a2aa0a23,1ec83eed,ca5042d) +,S(2ea1d8aa,d436c332,8fda3ce9,8e710b80,8f463844,cf131f2f,2347b37a,928af132,33fa7e0f,b8c2ad10,580a243c,ccf2a4b2,13ff9620,7a034432,8fc61f31,e98dc4c) +,S(12ae1451,8122af3f,a788298b,5e59df37,ab6a976c,f8a931b4,c0a51de5,d567253f,85ff1dd2,b3271d2e,de2fa732,9e3f0a5a,1c3ff9ae,b55555a7,926d31a0,1d24877d) +,S(fd46e1ef,b588af1a,a72b84fe,dec75a3b,125579ef,55ef8270,85645930,95bf6619,d06adc6e,ceb6292f,60c7d477,c73a2aff,342e6b7a,e81c5ea4,f57cb7b,27a51b17) +,S(ed7d93e,b8c0d1ef,1c49e1bf,be22acd,c7a17946,b172de61,4a290d0b,7f32b52b,4d0ec30a,39a81c3a,f8f6668a,742abeb2,b5552284,a68ce246,f368ce9f,b2303ada) +,S(fccfdfa5,cd0e79a6,9febda81,475c06dd,58517913,3624f4dc,9d808b6d,e9619ef,79b9040d,11d09bed,f89959c6,992a9636,ec62324a,d60e1513,9bd2441a,dd0adf49) +,S(831762b8,6a9b9afe,d2a044b2,7bc714cb,16823ea7,22b44010,a57a5564,4b4ac477,147ae062,8ef3bfdc,b48b3f77,f84dfd77,2754c1dc,a626aff6,9c3169e4,9956c708) +,S(33448c01,a84d5303,c8f70adb,bfe0ccf9,f92cc461,f2656fb3,4903b99d,4ef26b01,e5727349,ae8ba61,c8ca8284,ee7f906,208ad03c,6fd7c95f,736ea541,ffb8f2ff) +,S(920a1261,6ddf1304,157ccb8c,50ebaf6d,60df85da,b19b24a2,48254cc2,7d215a6,19e4d4f9,cc91e890,3d2f7a11,ca8c0b97,e2cb671c,e0c4ee36,9f1a3ca0,f28d0f4e) +,S(1df8ce1d,3e370b9c,3f60ac00,cbac25c5,458385a5,c4b926b7,fcafe62e,536b1ba4,344df46e,e3635079,52af6160,2729094e,3fcbabf5,bc701083,533e7e60,5099a92b) +,S(7227847d,abfe26b8,13c18175,c37a0526,2e2243c8,d80d93b6,286b2881,d38b7b72,bede79eb,33da3cdd,95289c93,46165cf9,28b5aa41,c46b364,622988b8,a7973d24) +,S(6defba59,e1218bfa,b02571f8,ccc84104,3caff74f,26d8d995,9729f292,eff7fb8d,4d7baa8e,21f2fbc5,96f770d1,bd34e77c,2c188c05,5921c1a6,95696a8d,4c340a04) +,S(c667f4ef,f5127d24,6b31ae02,2adf53be,8302e1a,b631a0f9,90b16051,e02d6bd3,3d7bce76,ce11bdf6,fd4d822c,37d746a2,f8de283d,527af237,3e80c4a4,dad34cf5) +,S(ed9e8674,441620f5,990dcc24,23a2ab00,6fbca1b8,e6bb3d23,55c2c72f,ce29d866,64d5ed9b,b4289a2b,b81a12f3,30930dee,7c03dd71,1ec361a5,8297c22a,5806ad30) +,S(86a36c57,e6b0cee0,33c00b25,66344ef3,219e57ee,ac452ea0,b0eeb98c,3176fb24,b4f4bdda,9e9b58df,4e103874,89592b5d,b147b43e,79a1af25,135e8db3,eafff739) +,S(2e237784,45396ff3,a0e105ac,ba151e3d,fd02bf82,1de70e04,33792280,bccca913,f736539c,e77983f0,610464c4,3bbcc904,f43726dc,a336304f,c0b716c3,67148551) +,S(ccd3c14c,7790d936,5226dbcf,11a0bc00,9db88a87,ac2d68c1,6899f980,20a1390f,b240b35f,bd6d5dd9,f33bac2a,f1899320,f8a9cba8,7df0a67,81cdfdd7,1c3af907) +,S(7713be7f,988d7b1,c6c6566d,558978d,eba4efad,b82761b2,17bccd87,3637b018,8fb91a6f,a9d62baa,2eeef7c5,15fab341,c75f31e6,64ee7d94,70db34c2,74475781) +,S(5485ad60,35dcb3b,9520acf5,6b0855cd,34c739ff,1c59798b,1ab667ba,a4982b02,1dec6c7b,1e7c14ca,743df62e,589311bf,57cbd455,e7dcbf9f,cd439962,96a90f0f) +,S(a597503a,d3be484d,1d2d1cf,c4e8540c,411bd201,f2b05ed1,89c9a3dc,b0ee0f19,35113e7d,38001199,1e2cee66,4dbcf490,becbd6f0,765c94dc,e2855ee3,b01113cd) +,S(ba91b251,abb130b1,57dd07a3,dbd2fd91,1ae24937,80c8046c,b94de57c,dd91ce4a,cfcc2080,500aedfb,a69f33f5,4f642fbb,8377420f,e1799dcf,e1dc3593,d3e1f6c) +,S(adfb5a58,83cc7bd3,c6fd3386,95c86414,5c6d2f4a,603fb2e2,fb045bff,86d324a4,16574ed7,45d82dfd,c50415a1,8c30da91,3a525753,cc975722,22a5553f,7b0cdc5c) +,S(1084914d,dd710a5c,ac854b14,10a4be35,ba1f13a4,e0fba3e2,9d94edcf,a3561ae8,653ca135,3240ef60,412ac6cd,adf8e664,2f2b74a3,5908c3ba,be6dcf85,49c25e99) +,S(704aa271,9af38b14,17a8abe5,b881efe,81876a6a,f807833f,133cdcd1,6e6c6f7b,a6fee2b3,48c1180b,c48726c9,be2a1a41,11395af4,42ba7b92,63f4f66e,a3769e54) +,S(46048cc1,7f5d2a86,5c1f5734,893c6683,fe68b0e8,942c9080,51d7b836,a8aec289,d85cec30,89a8f77d,75bf9c6,bb4f8a33,2e766834,3841b277,a461a079,8b288efd) +,S(50f1e6be,315d3b21,636781ba,8598d2f9,7d04ccf,5ba1fc76,3d547657,77fce9e9,f7dcb7d6,2057b9f2,8cc24975,9dc9621f,25dd0ce1,47e00db7,d095fb0e,ee6b0b00) +,S(7ed0a244,676655ad,10efa03,7748d49e,944df1f3,5bd46d0,3b7ffcd0,a63501c,a8377a65,593f227b,446eb266,936ae475,e9b3ae65,a60768d8,2690d0f0,5c18c21) +,S(d039ca1,ee66c2cf,611c431d,cff1bd2e,19b29d3b,b1f37342,c29f4632,4febccc7,db0d0d82,14f4c661,629ff5e2,4cfdcc84,6e652ee1,15a6476e,7813104e,696d736f) +,S(c27973a,cc55fe2f,4531768d,94cd4a0e,8e36729c,aad61579,7beed810,a7b092b7,cdc310ea,b36ab246,c56d5402,9b9e3314,878e1f1,ff7520db,43917a14,1f41c99) +,S(36842942,cce3c683,25472e48,de202d15,ac14a180,3a2dd84,6de0c33c,6446e945,5000b3f8,19ab82fa,3589db9a,eed5609b,208d95fa,ddaebcb,57403b96,7b8f2f7) +,S(d1aa195c,338621b5,19ff3a8c,55abb454,74865ef7,85e0fd7b,c4554ae,6d62d0e9,e7e1dd33,dc293859,b4634576,d8840630,832ce773,a9a8bc88,c2e67251,a7b737a8) +,S(4f423d32,607439ec,366bd6ff,5766ec13,a59b2c2a,8cd008b0,489f267f,5c0f00f0,5e3c7e9d,7ee94c15,c048e6aa,272c888c,b6c3e058,4b4f4ee,f5e1d787,a4e7a9f4) +,S(6bb1c680,ff8991ef,17c0859a,9b5fa9b8,ebcb9afa,af7398c5,9101bb99,7f06c4a,318da775,d912ae5,d17c242c,97615c5f,217005fd,535b839f,189ff3c9,8669a253) +,S(14095d4,4713a46a,5fcb251c,9fbee2e6,bc393423,30303b8b,d0f2d09a,c0096f68,647e000b,3660b374,959e632f,6867c7fc,1c7aa4fb,b7f5593,22dcb2ba,e604a2b1) +,S(12112bf0,346e9c6e,4a8f831f,6c087876,8943191a,38e9fb83,8b2613b0,4b5e5323,e4149ac2,cb4533d9,4c3abf62,aee4dab9,faf64065,89cfe10e,30a42986,9657a3d0) +,S(f99360cc,c2ed4810,d80974eb,eaeb59fd,4f2197a7,8459729b,4f75c370,8bca5738,b92a2f5e,86d77a8a,c2c1b9f8,6e1971db,a06e4414,935020db,c0c83a92,ee6f1cb3) +,S(2dee5a4e,c81a3ecc,4872112b,be53f51e,64ae779c,49488567,a654e806,ea809ca4,1e2ea8a1,df6d47c1,c0aa1bcf,ffcf260e,bdea4d63,934ff3b1,bcb1e1a4,c73d4954) +,S(5baa6a2,139eeb78,f49638f7,b0dc5010,b73688d1,bbd746a5,f2382c7b,d461229,72677383,10a352f1,9f2a75cd,9e42509f,b08a0ca9,60154693,cc01d595,25dca81d) +,S(a3f77eb9,321d44d6,b774a6f3,b1319e46,f60d102c,d96e983c,8f8568ba,82913d70,f93d75df,8271e70f,1aa38630,26bec1d0,f6db1765,77470077,d3fceb8e,5c613a05) +,S(46ba66b5,fa1f9274,378fd62d,68ae9f38,8588d766,ebbe97db,72f9084d,c7f2cf08,5473e24,3078b8df,92df9b55,52ad5055,f44bf7e2,ecf0785b,72db5dd9,238897f9) +,S(db6a0246,ddcf0260,b2b415b6,f551fad0,bccb15c,ca196af4,291d21ce,ce80dbf2,396cd446,f93e1c97,a56c6f6f,63cd0966,5f88856f,e75e1c7e,a985917f,28591d3c) +,S(b82f8d1c,a7a298da,3992c4e5,8dba1b0b,38187eb8,66992e8c,5cc1c834,6e6c8fa1,4b0ae76a,f9b0413c,8d4fdbdd,db767c6a,b1b80589,533e3ee0,28eb6036,1483461) +,S(446e96a4,dc9264af,e9cc6348,aa612861,d106fdd7,b7ff94a1,82e0f018,c485f000,32dfe3d9,21593a58,1fe23b7e,ccf677bd,2771c40c,a612a507,c8e33df0,99568440) +,S(c86f10,7bfa6dcc,b9f00ca3,465049de,5c8a3e6c,d85bdfb8,1ada1d39,303a2eb9,e64f7066,7e910814,ae18c6a,7323474d,9dcb277e,4aaa7ed8,88bae4ae,7c34219d) +,S(bed8eb6e,cda14299,e4cd94cc,202be7b8,9201c2ab,302a5cdb,83e5f5ba,db6cd710,15736cfa,b375a40c,5d367b9f,6692746d,16a1f03c,20941256,708489a5,5d411aac) +,S(b3efa7f2,c5f6ad10,41f171b5,ad759480,665e0a52,de82a881,da1926e0,8f64d38f,3df63c84,32761a24,eb0dfa,ae98e034,b6dbd361,3fb53ff,c535fac8,ff1677e6) +,S(b0460da0,cd869b5f,235213a8,49e143e1,8fa9afc1,651ee921,ebdc525d,2715cbd2,c11a3ead,f3541877,ce73b4c0,2b57b299,46b4725e,cf630eee,865bb121,b86fd7ee) +,S(28b09517,a17452be,b2c19187,12d600d4,b4969253,a7d0d52a,49cec3ce,d73bc071,36696708,73c7591,11163518,40af594,40ffd6d0,690ddae7,fce7a43d,e920a326) +,S(521becd8,192acb58,7ae76b7,c0bf17fa,ad86afc,47157753,6b1a51b0,fbb5f136,648a54c6,d7aff13e,cccf399e,15451682,ee06710e,edf32669,1dc7fe83,75ac858d) +,S(b7bc19d0,b7fd9d65,26c9bca1,b587d84e,a2a200a7,76d39082,368b761f,79f6cf67,5adefabe,e0d0c8cf,4252c29a,773163b6,4a1e9ffe,ffbfe3ad,2f796e8d,1f70acae) +,S(e48e3be5,1b6ca8d5,41f0c241,21558cec,66db7b6d,c13e7114,dc931f0d,397aea8b,dbcf6357,5b16e2f1,30f064bf,ff12b465,f6cb3a3e,f9f74431,4bf41e7e,e0f28ac) +,S(395c6de,71c692a6,a3247596,8e28f2a5,4ec795b5,9759e890,9b2f4290,9f455751,ee5e0118,3c7dda2,c1eed4e1,5aca155f,b5b097e4,499bc3c3,fcfc2c85,a669a13d) +,S(28938ac3,b1184399,22741291,9c0fec36,663da3aa,8a4e89a8,1a66052b,ca808d99,4ae8de7a,b1ebc1be,a2beadb1,80cb9a10,da168295,ba1ea5f8,310b986,4f174603) +,S(5c0f618,95e88cf6,205472f2,3e27e853,be3af64f,d0a376f5,8523ee6b,5248f5a4,be5c3c55,cd09ebb3,b7363456,9fdb50fb,8473e3d8,3441e624,b41b1c3d,e44be51b) +,S(abe1f7bf,22d0f733,ed28e125,f7ed05ba,ae2e8f13,1eba2ec0,1554dac,5e3ca1d7,2a5a48db,3de31f6a,9708ffe4,b996cb33,e553e6b5,95aeaffc,918aa2c4,1ab0ab4e) +,S(c3042427,57d370b3,79124907,ea609129,5f1dbeec,442a6349,bccfd29c,e08d8be7,67a64eb3,3e0fda75,535b35e2,f8c9811d,528959c1,29e8f144,8bb75ce9,b1868934) +,S(65a59946,356f219d,77af0b6a,9e1a43f7,3152746,6af41c21,c81532fe,3140f5b8,64e7128d,5d3e4fa0,518cf27f,c070da20,1692b059,3d84641f,2463a61c,74f35918) +,S(6c7d2e3b,48428c87,46b066f2,bb99da10,60311cb7,d4dae19e,de2eb53f,141413e2,4ba47bf,c2e7c670,4de2864d,1ed7b383,286292fe,28f7c7ab,2a664bcb,6cb7e3a1) +,S(dd257bd,b99c89c2,b47798a9,bc93ff57,5655797f,a45399cf,44acf58e,93fdcdd9,f8d670f2,a9207f25,52e7e0d7,14289103,e8194f81,6390e0cc,1af2ae36,c1b3e4af) +,S(2516034e,b8ddc424,42952b8f,acd47c62,6827b87c,b08f263e,b2aab5e6,ecc23d7b,ad8e8879,f5feea07,ad3545bc,a731dd30,bd2ab534,4b486826,433c75a1,6216ac9e) +,S(7e723054,27b591e3,b19834b3,8211edc1,7f075215,6591980b,907dce0a,d934246d,5c91fcbb,d8c184a6,f21671df,1098760b,2506bee8,e32474e3,3b080147,4e63a71f) +,S(1b1f9d16,ac3c0d2a,4b87691,45a6f6bb,8d4fb57f,ef9df33e,72b5d399,6dd1898b,6442bf1e,5ffe450,b14a399e,16241a5a,15e94453,94519bd1,24d0a67,f35b4b1b) +,S(e0f1fa4e,57cdea43,a3b9f671,596631c6,65b5144d,93c51a23,e9006c63,fc872f2b,c83851fb,54890d14,2f605617,151dd47,74f9a3de,cd27d864,a49c176,af32e564) +,S(ae95bbb,697c820a,251a3667,b2a0a078,40efa027,99d7bd0b,3e9a12eb,32728c3a,e0dbba97,280096d,86ea4da9,45ae509d,1140e0dc,7b803f10,e685b148,6baebb41) +,S(fc8ff885,d52ef42c,1fe529ea,853ff8c3,afad1ae,6e7f1b30,69e34076,682aed31,525eced4,b26e8f62,7b904f1b,3356d01a,9cced71,432ac850,433e0a64,b1e3dd14) +,S(6613136a,459d625a,d5b5753,3a965a0c,ed68d63c,13652546,c629cab3,32c8e810,10f29ff8,24d30a94,b3d732b4,3b168bd2,28b4cb01,4012469b,93a9853b,655b65cb) +,S(45085915,7b683327,c76309b3,96ef3b63,19dda841,f6be1b91,f24f5a37,ddea56f0,b9a71446,584a88e4,6a155e34,f08d5a6b,2b6ebbbf,b4b1aa12,bbc7654f,91956821) +,S(168ee3bd,8f10ba0d,deb82576,cd5e1711,be58af4c,2274464f,e32f3f7,7c8a4325,6eef1e63,43ff409f,a916891c,982f8700,465fb184,5820b886,68b74202,6678b34c) +,S(b3191d97,763c2341,4eb4db8d,eb2003e4,fdd86b4c,6077931b,71802714,ef048c04,b4ac3085,59f975ca,be7d53d8,3ebdd5c3,ce6860d6,37710a04,f885a83c,c2152edd) +,S(da9d5c5b,4e73dc93,3c72acca,1e93f4bb,3ec477fc,f201096a,51ea0226,3a2f8738,7fb6a2da,65f966c3,689b93b0,d6366511,400c05d0,8b0a15b5,b4b3209,a812f8aa) +,S(140e55d2,a7e75678,1791730f,c526470,8d1cfaa0,954e215c,1e65bcc,e4b0dd66,4bfba0d8,63ea2597,91e470d8,1ef36318,87e2d5e5,c897918b,ce805941,953d5b3) +,S(15a9ccf6,4b95cc87,cee9bd01,bfe3d8bf,b438f7f8,66344a1c,f3f399eb,16b173d8,4f5472cd,bdebed25,605e51fb,f0b31a93,5d0ba59a,6e51bf90,e0363c8d,9bb33f3b) +,S(e86e364b,6776701d,b7b3d3d8,fad181bc,c7ed5e91,2d0a888f,814caa3d,88af6e06,22fd460,afc040d,3e995518,c6286989,24a466d1,4a624915,f834c357,4dea7b92) +,S(3c261a98,cd25f0db,2321134c,723ebce7,b1f81d3a,a6d5bddb,fc282fae,5a500eb6,2b8d2145,5e498211,8a4b6ed7,ec96a66c,4027a506,deaeecbc,45535fc5,77abd9c1) +,S(49b6a811,4d55b517,6cbb1f6c,177aacfc,368c3b5d,238300b,d66546ed,f4c955c8,d1b124a4,9f0e7c1,6b8e001,7f1b0b2a,ab94411,391d5bca,5b535838,a3815b94) +,S(6ec8e861,f0d3e5a5,3f9deab6,20cc4b81,6cee801f,f7a43f71,f34a8dac,c525f6b9,42e0545b,68ccafbd,3309c4f7,eb6b5eb1,1e01a871,c63fae27,782001ab,a6dcfd6a) +,S(7342b341,59aca5cf,d305a2e2,a8f14da4,4b527e57,4640fe1a,c08898aa,4595b9ac,b5261302,d2442ee2,363969a8,4ea61281,27f64f40,ac86dac1,21b5d234,53160124) +,S(842b30bd,54ef350b,65dcfc43,df26967c,b2747757,a609c54b,687d30b9,5a686fa9,4ffa9e92,2caa36b9,ecf31f63,f5c8de57,7d594f7c,71e7e77b,9cc5ac83,f336ead6) +,S(2dba435d,d589a7c7,bc3fb60d,495757ef,7c13bb0a,3e91a153,9998e04c,19f94862,c440720e,a4b23cb9,d238eca1,6775e500,931d3e5c,7df853ed,55cd4889,5e8cc0a1) +,S(9d1e8524,8f84ce5b,2a75a457,819390be,bb284fbd,3d035c07,998d3c45,c73bec02,8e40ee4c,c935ccac,2338bbc8,4a8063f3,6d62e8cf,849bff40,1298074c,94eb9ead) +,S(d2735ac5,d1fba75b,d4f63912,11549755,9a7f8623,62e25cf4,e76562d2,d8b441af,bd2ac36,19f9f339,77b7cb0e,5b52f3e5,b4ade86d,1c9fb31d,a1d8fb36,7dafc081) +,S(b0130e38,f46cff13,ad1a68d3,15729395,b27c2b96,e0a957cd,ca63adfe,b0a3e11a,3d9f697f,4a61401c,87eff844,3b11776a,610f1516,4366c1b5,535599a0,c6aa3c4) +,S(e0aa062f,15306af8,b662d8f3,3b89de8b,f59faa78,29942caa,ed82e668,35ec76a7,c5f5f0b5,6ea77304,a5dffb8a,cac98387,34305b54,25fc6181,79a4457e,c15e6f2e) +,S(ed1626c1,a8e6d1c9,187e15c4,ae9875ec,2156d685,997ac413,9f44bd0b,19432d5,3c0d895b,9dcb4a3f,4235e531,ee7373f7,7f0fad46,c5938d7e,69d50429,71f0c35d) +,S(a2cccc31,4e18fdaf,8cc25aa6,74c974f8,664744db,86e04be8,431d2ed0,4dc92833,b6410e49,d6d471ee,d20e5536,8672f5e4,2e1b9c88,c06438a8,46c3a74f,c759d71c) +,S(5bfc192f,db15f62c,4af19119,b2d96a88,b3b24e06,3d54b31f,a35f675b,f668bf27,46774374,acb4e140,90431bb3,9f62a38a,dbe0dc62,4021e23b,6099e198,47d99051) +,S(66842a89,da73c404,32a2e553,97efb009,61eb2155,886ac90e,97dab5f0,9227028,3a8e83c3,511ea048,88d2dc9d,f1016cd9,da171bc6,89c9af4e,90b18091,21941de9) +,S(ec823197,3ba95f98,d45f8991,3ee8b92b,e3a4a84d,d59342f7,1810766b,cdc516e8,532d3c79,f5113f4e,1973bc88,2a953b5d,77d5476d,ac9972ae,f1d25e19,ab7206fe) +,S(3b65fe78,bc516176,aed5e5b6,5f3ed39f,e8e8e26c,32ab07ad,1ac2519f,72fc140b,856d36de,5190eb9c,13f7f976,466f95de,566a1fd7,314cec1b,9c9f7adf,98208d98) +,S(3ac0eca,51c43525,e9e545ce,83fdd009,f9f31eb3,2722b945,18262036,d18cc346,dc72018b,c3b2a4cf,3244ef31,340e0944,325bebc8,b134de50,9ab3024a,a213a7e6) +,S(4929658d,6c50ece6,edd2db7c,217d9be0,bcdac8b9,bb50972f,fbe53a04,2ee23508,e5aca42c,61aabbd4,b8555980,a2328741,6dfc8df8,d7a10c3a,7284bd90,aacec38a) +,S(9d16e76f,4bc96492,f0e9e748,8c6d2297,7830e2ab,491a6278,1d60ab5b,153a6291,e6590f00,e4ac6922,5436318,9770aef5,5eaa988a,44e9711,da7266ec,895ac7ab) +,S(b0649c4a,1327e7fa,f52a9bbe,7ff97add,4e13b36a,27c0676d,5e09b2b8,1f5df94d,991c8316,a6f36848,e072a080,77f75a8a,d79fdd08,1a606d12,b355c3c,3891acdd) +,S(8f194a21,f6303ded,2c9ce51c,44a4a16b,eb39a8f6,5fe13c9,6b7c12bd,b052235d,bd1df3d0,c9af82e2,545afb23,e9a2dc85,c635172c,edfe70f1,bd755d26,9730739e) +,S(1da71a97,cc7a4cd9,4777f6c3,dd5e665b,3872b721,c6c2262a,60e87f1d,efe21ff0,78d17a41,39036e1a,7af2b9af,1273afeb,a4d30e8,66a3e6e8,7f0d0468,4be6a06) +,S(9fed363,e493f262,d3efabb1,c52291c7,e04e401b,b181290f,7a37af3c,df86aa45,130cc33e,e164adff,bedb1827,6a5c965f,53a54f36,90dfcd6c,b3fff836,703de729) +,S(d166eccd,221c3568,199ebc38,fcbb6140,7a601a3a,c1e7a98f,ebb75a87,a7a1e49f,262222b7,e7c200bd,5dcec562,85485434,b0628c7c,ce5483c9,93795f76,a072f06e) +,S(e7853cbd,3e3770c1,cc307839,294a2e8e,b998a56e,393435cc,2930920a,8ddbe0fd,7977e6ca,67147cc4,965d2918,1023c3bc,cff1e7b1,8fa4850b,80a140f6,980e5a8e) +,S(a8b0feb1,cb140201,2bea3cd5,6b1a1f02,51036ce7,423f511,480f49c5,6227a17f,ccd7c3db,9d3ab6db,6cceedd6,d3801c8f,67afe792,c8977b5,27c9dcfa,75ba8a9f) +,S(57ce5dd9,e1815755,31e90579,74f58b98,6d02e406,a846ab26,c9059f6c,56d617d7,cc34c090,4399f316,269598f8,aa8f9aed,829f61f2,5efc9bc4,e851ac63,fe142d14) +,S(479790b6,4f60fc6d,befbbc1d,365ab3e4,8077b6c6,cbf02f2d,6c8b986f,d0f9c858,157f4982,e3581925,dd25382a,e3e48fa6,5cff911a,95e9bf43,5df8b356,87f2f9b) +,S(55abc7b,1ffe0a41,2181b622,813be76c,8ee0a0ca,3cb37b1c,cb265087,fff17c68,bd134249,23e94c53,4cf9e603,a1dd259,606272bf,8283f2d6,69bedd4c,a971d99f) +,S(6ca7e032,8bf5d67a,b804d431,e5f709e,bd3156e0,1d4511da,1dc67394,24d2e659,c428b133,f3683909,6551c2ff,2f870d80,d80aaaf4,6d2b1a69,7722057,5eca2647) +,S(60df19a9,83e9086c,4cbd20ba,fbafa8b2,346ae4ee,9c1ad4dd,99959e91,2df530d3,dae7b854,29f817a0,421c2f1e,e4e8e4d,a09d60f8,84701e31,d7a8b7b0,3b79cc48) +#endif +#if WINDOW_G > 11 +,S(6f18d50e,5ef5f2ad,bad80ea1,c59a3847,5c22ff05,6ba52c7,e1d26d6d,d3686bce,d1d7ea0f,a166efd0,facf5c83,bc3786e0,d3f3405d,5b4578f5,13a336ad,f0d7d7a9) +,S(b634dc9d,5ed51336,4ac9569a,8ddee9d9,fcdfe00e,65e59233,b3be8a07,2fd949e0,d72e46e2,c61b2575,f25a505c,bdc3456e,fdf18976,6562a1a7,e86d036,eb31db69) +,S(5a37fcf0,2dba24f3,e6646171,43dbc5ab,40d91983,69f5cf0e,4fc566fd,97445910,a960d1d8,13f8746c,662a9582,614c7847,33e6153d,2975cb59,c3342463,75c69d1e) +,S(8aaf16a7,37318032,c60201b6,b196d8fd,eeeb8f8f,1aab29e1,1b83ae1b,f112661c,c461ef56,66a3a4a7,563cc0c2,845f2ae7,6d03a795,987c615d,611bab48,ab9a8e44) +,S(456f0ba5,a05f15ef,d93e7f49,e0729c45,1d85a693,42c2804d,9cd4b8ae,af5e3434,a680f4a9,99221a8b,7f46da13,13041e16,13f616bd,84b9135b,b0f7ecb2,7311cad5) +,S(f89c36a1,f1905409,75f146d9,31e12cb9,4bfdc90f,a178970a,9d0b34cb,38f5c741,3b562c80,58e31b92,a1186615,9c1ffa3b,4b1df38c,eb4a1646,579d2375,9c44099b) +,S(7f6beb11,2e1d8600,d8c4903,1d69cfb4,e23876e6,32f30428,9d2c2503,41f3f76a,a1937407,c0925afc,be994b9b,51627b98,8d98ed6,e8cf6fb9,cddd0ec,a4ff4a9b) +,S(905ad0aa,b585c7c3,78b7130b,ad1d5001,1010f543,194f40d3,827dc606,12d73511,bfce0701,c3ddcd38,150130ae,e91d16d4,a447821f,a2e098aa,b83b8025,b7177a9) +,S(f559c2d1,62122fca,7201e7a3,b6033d1,e54187f4,7550b2ae,f1d51521,f7408b6c,3f10d622,dfd0eb14,7f6f04dc,43108273,7695ea42,38ced7f2,5dc167ad,34154481) +,S(19b328a4,fa17149c,ccae5830,a8bedf53,7c0d8a0c,c3616345,6f4d91d0,718ce8b4,9a1ce80a,2a4e6923,69925883,8e786779,fdff7f09,25d68855,12644d04,9e582f64) +,S(afab98a0,e75619bc,ebcabc24,c1a6a0bf,2743d4f4,921fe53,3a393648,adad5d4a,bf1b3a87,759139a5,79741c09,21f29a10,7c8e5be3,13d75715,7dfd39be,923a32db) +,S(c2cabf7c,1d86a34d,addb36d9,c83a8ec9,b2dfe0ee,8176d085,cca91fd8,ee4181c7,5b32b724,53f1335c,724465bd,6eceb8f5,159ab150,e9c972ac,f51d27aa,4d91070f) +,S(92839424,8e847452,29a8c534,7c14355e,4f6e9cfa,2c1a2be4,f5ad6cfc,2a89f593,bb9c585d,fd41ee06,99ca7b4a,146faf68,51983b15,d652e996,c888a171,7c8c3a07) +,S(bca2853c,389ef184,208cba64,617bd7ad,55e55f12,d0c9fb4a,a8dc30d0,c73c7dfa,9f41289e,f32aeded,3d45952b,16a62fba,fe9a142e,1e65340,17588d79,e1df63a2) +,S(a4ea7bda,94b565a8,ebf53aea,5ce672c5,5745b7bd,37ca77e4,a0e4f281,71bf56fd,c9507485,1405ea90,9b9e1a28,42c5953e,629eef31,37bb0fb0,970cce6a,893e96bc) +,S(71707d45,af71139e,d7327ced,89a2403,da57c18a,8527bb1f,f754e78c,60d5e169,24b36484,fe4da6f3,a3f4e4a8,55a477dd,3eef0f39,fd3c585f,1b9de9d7,10223ef9) +,S(52c1de03,296f591,9d5cf4d2,36b8ff51,5b1f5ff6,d73228c0,7dda706e,4fdf9a05,db958d59,d73d574,384066e4,fda3b785,c9509714,90607c51,35d1d34d,5a344d0e) +,S(7b09c0b0,7852e845,30691989,cbccbb87,74dc2007,df827120,d8b1ea62,2368912f,4edf5717,ffc52817,d0eebeeb,782ba4d,9545a5c,da979f3b,e9c09e84,f82bd263) +,S(e99870ce,b29ad1a8,2bc08834,3e3b3c03,f26e7062,66b32540,681c689d,ffb8300a,8080ad92,78947d65,bcc12dbc,614d17a8,8ff75e3d,91230fad,c6f785a8,cd597f9f) +,S(c7e2326d,e28b8139,85db4cb9,c3d4c5ee,2dd2c2bd,2b912444,adc3a434,205c38e7,5f2863f9,cdc0cc95,1fb87000,e177cf05,d31adf8c,543d899d,195bf5e1,c6ad940e) +,S(883596c8,4ca7bd78,3c586bfd,28fba489,4c6bb66b,5ea52244,1199b55d,3e3f7965,fb114be8,59255f10,c96d6d4a,72945ac9,47a22f5b,68ecfac6,9406bce2,58e9282e) +,S(7f73a648,a19640f5,b0f029dd,37e56e7f,5cf61659,5e532dcf,7ffeb7e3,f20b42cc,4f76248a,f931aedf,b4e2df3e,ba3b426d,8ef191b,9d59507f,4a94ffa8,80ff4feb) +,S(c89ee34a,3660d03a,ce3b5604,1196a66,18c8739a,8a4795bd,db9c382a,9741aa3d,a4a3b888,35fbac57,f2f79ac9,d0ef292f,6838764,b99de084,684d1b95,12923884) +,S(24be4c56,d7dc0c4e,50050cf4,31d81f19,b5c46889,430563fd,566c4ed3,d941d5bc,e07236e2,4936315d,5f0714a2,af1ad769,f6a5fc7,a56fc1cb,ac09a085,f29e6213) +,S(6c31e71e,caa91c00,798ee5c5,3237a9f2,2866a91d,26936852,c4eb9853,e40b94f5,65aff323,2b5f2cd1,c1117e0a,c30b94e9,34543cd9,480f7922,5723c385,9ccf8cd7) +,S(506c373c,8479693d,3e03c022,9b61839f,e470a574,d31fa286,92601ed7,8e58b7e7,4fd0bfd3,a57c5035,268bd4ae,707fee83,e24f8676,e0ffe3ca,1673faca,1b7ab65) +,S(59d9a5f3,ea1a6cc6,16e7b097,871f731c,57571a09,1dc01b45,b48c5332,c98349a4,94ad373e,ee6496c3,263c348f,a7ee539,53aadf13,1aa6b638,21c16e0d,55fc8447) +,S(a524cfab,65371648,f641912f,a6ce67d5,c6f8929d,366448d6,4de381b3,22d75c69,14c26ff9,87fbd599,17a6ae90,bbe7c79a,ac1b6158,c3f46144,71aa36a9,45782823) +,S(126c986d,f83c6307,87c0d3ac,6a6b2393,8aa3fdd4,df26e76a,92af2c65,7f48c550,808539d3,a56a6f8f,3bcf04dd,31ac72e0,3f74ab1d,5e9afd39,7741656b,3d4c5c5c) +,S(32cabbc0,1aebcfb2,7e1a9d5b,4cd05550,d8ff25d7,b9a81db7,3188474a,ce39722,7d30120a,e08031c4,12c070f,6ee6b52a,be191021,faa9f5bb,9576feb1,8fbccee9) +,S(3fde1cba,f7ded6f8,a98975ab,ad5a64aa,8a9f0864,16dc25da,359e1cf8,22f42c64,e50fcac,61f0f174,b0e93cae,691212cc,44ffeda1,60dcff91,9ee3c960,2f5ac075) +,S(9cad22ca,d6c3dfbc,1d3a08d,6b102727,36e9db37,1ee1f26b,3ce932c7,11462e24,5ae84f1d,873dcd04,7f0781cb,aabbb53,422bb119,4e59d5a6,8824db95,50461c6b) +,S(9859020,aa807626,3a8ca18e,91c1afad,c20cf9f,9249c24e,1c652d19,7d5872ef,454323fd,21ab7d6e,470976f7,2d85fa39,a084e7eb,b332edd6,a3d054b8,d3c73e69) +,S(821699a3,e7cbcbd5,a2b093b6,c825281c,ac00bc53,c44b33a9,d8c31b9e,464eb2b6,f7d59c17,2400cb9b,72b44cbc,bbbd3e9f,e62cf451,bce6f840,23b073ab,66a9726a) +,S(bd06d4be,36423862,795fc7a5,5c48ceb8,440b3cc3,fc286dad,74ea7f6d,8cd370d9,3b09193a,fdcec88,ca4a5663,2cbb808f,61bb1253,fbcd1714,accd508,6183b90e) +,S(2c7112e7,9c8cc78d,f04a4ed0,e043c6ca,bb4b734d,9177f822,5bc41123,4bbdf4d2,c81ccfad,d42e2248,7612884a,d900f61e,bae242ef,65ff1afd,6e03ec42,38391243) +,S(51ae390f,a65d772c,833cbb8c,9ddf9ceb,b7f5236e,f1983905,762b7aaf,e122131e,3d7c9f80,80406b06,282f2a88,87dbb714,f1e96e1,e84eaec8,1256e1a8,ed7ba06e) +,S(c59de112,de237a43,2bcb8348,e3b31356,c7432672,6b9167a1,77d826e4,dacc59ea,36109f16,7c5eb66d,9f09c1d9,c5e1676c,aa33e403,a8e052b0,2854c689,d5903b25) +,S(8b99f23a,b7509d9b,151cb89a,45753026,77ca1e6,9219f0f7,57d01823,f556d978,86a31aad,f53afe44,f533cf0e,4bcd573c,d338e12f,904c8e9a,dc8e0080,f56a4164) +,S(4c670f65,7d6fa9bd,9ee4135a,1ef1882e,61f5fb7c,9deea717,39fe0ab9,78369dd5,531a8226,5d4b5363,12875dc1,7f891d1,b54e0435,1b539dd3,c100b797,efe65417) +,S(8e967e35,9daa0f95,fd7024e6,e57f0640,36aed9dc,9df0f33d,9388cedd,e0f38548,36f8055b,a59afaa5,8c88b833,ba110381,45fa0ab1,f0160d2f,1f1a1765,190d218f) +,S(3f611915,9c45a37d,5c7bfaa,cf0cc959,5e2e097c,8c7bc3e,c82e9e33,3b2ac9e1,c80134a,b7437dc5,23e08757,8eeada53,b9d2f3ee,25b754fa,f6c32e02,d597d7d6) +,S(3f59e49d,7c61412b,53fb203a,c7ebdedf,66bba683,87070745,9f85000c,f47641d9,bedd7088,4243dfe3,d238f101,f5dcd4e,64cd0978,986d50f2,ae3e2563,a43ad5b0) +,S(5b7d84c9,b64836a4,3bc44b5b,fcf170e9,ad46d4da,197a2781,bbbcaf87,5bf6d490,9f55b29c,c7d6b0ff,facb8975,507df04b,f2164e5b,9928156a,7c73fec2,b34d984f) +,S(e1ee74ae,ac3e83c,f73bf1f9,aafc3eab,90decf0d,a071aa88,962c29ea,51744b1c,9e86d2f8,b34e8525,568d674,8839839d,eefbe15b,80c66216,50acc508,f1277030) +,S(fa1ccd5b,8d489448,e3196c22,e96ca7ed,eba721ac,beee200e,5ddc496b,6beea871,e2b7f59a,326a6654,9d6479a8,a7a56d7e,8513504b,fd2a9829,d25e5855,2f9bc469) +,S(ef1e1cd7,f386096,1f6199d1,ab4a63ac,b292316b,f7a5e7a0,4c9f0c3a,7bfdc8bf,4cef9c01,a9de111d,8395dadc,e411e532,5783d377,559dc687,eeb510c2,ba98ef05) +,S(db714eb9,3e75d3ba,dfa5cc4e,d4813db2,fe9c395,95a1ee8e,9b3e468a,e1326120,3dc49a86,b81278f4,84d30311,c8816840,fa3cb701,665c067f,618295aa,6a71a206) +,S(5997f38d,bd572858,43ccf936,ee9d3feb,d70c79c7,9ad1c452,29af32fe,e6ea5d5a,adf7d862,b716011b,b9749e08,965c1ddf,1f44b678,df548eec,45a69a56,97e4a224) +,S(97d6f6d9,a920577,7b08ae66,6fc1fc7a,956654bd,3bc6c039,ea4eec3d,3bbaeb92,b450dc47,ff219854,dad5ab97,13dfbbf7,ce03fedf,4e7ffc68,4bdf1b5c,c20068b9) +,S(4f453fcd,5ec8b72b,69549ae3,c4eef99c,40232bca,1d6b10ab,c4b00d00,5ad307c4,aaded568,c4a8bc87,c7a442ba,f960d9e8,bd61f15e,d668f816,b94c0d2,9683f7f) +,S(5a604a3,50593035,c71c0a6a,caa7946c,ae4c4490,c0db71e6,49972705,949424d6,e578fc12,6d8701a7,a02b92b9,3fa89d09,ae82779e,2f78586a,8937a6f4,133be9c8) +,S(3f3071a2,916832fa,1b64cf0d,5b5fe8ee,2de601c9,34e08498,5d118696,7c635b3,e915b199,baeb233,b22adb6f,69df0e43,8712f25c,1be4470d,53851e4,451ad3a3) +,S(745bc1cd,643189ac,fcf02c5f,bf99c222,f7476a6c,2ede6c45,31849491,901c06e5,59a2bb4a,8703775d,e9a519bd,10e59db4,be16cb41,d0275d21,87291d06,d29933f0) +,S(bbd06e8c,adb3959d,853591a6,6387989,ab396736,8a6c1942,17580911,2637238e,ae0d8296,b484e5f3,dc46cb88,90b86a4,d562b003,a65a0c2a,2b4eded,38b8b690) +,S(fcda8db6,128dbe66,850b7e43,63a3b35e,9f3eb569,b7af97a3,85e53fd4,922cf07c,4bf3ddfc,37a0f9a8,5f758ee1,4c4d5c86,cd11f1e8,2a05ff28,3a0f535f,f9551974) +,S(6325dfdf,fc9c7ded,2a65e113,27210ccb,4ed2da97,4eded570,5ca1add9,9873cce2,89fe4c99,683e6fc7,170ada7e,26179fea,7ce1930f,af1a7358,4d7d56d3,4cf222e1) +,S(77594ca6,93495096,3ab5797a,5279d0a7,513df59b,41a3ad59,245e3f29,22ee0d7f,195eb112,2ccfbfc8,fdeddf45,ef2ef58f,631df03b,ca66a4a9,5818dd83,e5faabb2) +,S(f356ec67,ea9b242f,4f1348fd,c6592772,e2defd7e,308427b,ede15452,baa67867,3d2265b9,e23f8ea4,9a2b7145,9c98b03f,d75a2110,8f104121,e4a2fb4c,d633ba7a) +,S(3360ae12,9b0fa48e,c78e3091,4f8ae6f9,86149f0b,590a12ff,cadfdd2e,f8a205c9,79b37bf2,720d5a11,3ec09872,22b0a626,3d7a2051,e337cb83,dcb52df7,25d18a34) +,S(44e90f52,c703c50d,b331a4c5,fc47613f,b29040ab,9ff0f282,3fc15ad3,f9e8727b,62dfb341,92653e6f,636d013a,718932f6,95c2491e,26d16feb,4009ec05,e2f55f17) +,S(e5d367a9,b5c3ae22,31195876,4b28fe35,c9e04289,518aa09e,e7902b5e,4ec84047,ea1b257f,1abf0fa,6fb37c06,907052e7,fbbc06b4,c2382ace,9c9a98bc,e6f8bcf0) +,S(8478e8fd,7910e5ad,3c061e6d,422806a5,559baab,e083b8de,1005413b,59a63957,4f793418,5193499,13fe38c4,514bd4bf,2f3f1ebd,b492fd5c,fd8c1bc3,1a2df1d6) +,S(c38327c8,587731b,f5b5e224,79c9274,10c97dff,f6855b6d,c2ccf761,2a84b42d,eb03afac,cb1b102e,7e87d61f,5012ce74,a213fefa,b855db22,bc657d52,4dabfcd6) +,S(b9e8a812,5348a672,558f48c9,d17f28a2,4662b4b6,604ee14c,d622b521,bc51f10a,35c2780d,65fcd07b,51ea0224,1771b523,3813e7bb,97a1744c,e3dad732,ef1ccfee) +,S(a54aa2d5,95cfa325,38d2e483,70ba86af,d3c3b53d,2141b0fd,cc68a729,a5cc713e,bf74dc6e,63c02b2e,e676b9c8,6a851a48,7e9552b9,a32eabaf,4e1faa06,8bb8a159) +,S(d45fec3c,e2df54eb,844ad145,fb8884ac,420ce2ad,85cd55ea,4cc19e1f,da9fd956,ed8cfb00,65aecb6a,6901dbf9,b8c208d4,bf6be1e7,ee74a08d,205c48bf,8cb81bd6) +,S(f4b44e98,f728b318,ca959829,d4cd836c,5c0136ab,8e433101,87be24ad,53192172,ef5521b3,960bce49,2c841fd8,8cf7b8dd,2febdd8b,f0aa64e8,4d3c2a09,89ea9fa1) +,S(dec6d1bf,d349110,faa0cd40,e8533078,dcec40c6,e260b9e2,8412d424,353d67e3,73a16f81,3609c901,71efe44f,32ffc90f,9451b0b3,24107b72,54ca2fba,65095895) +,S(1fbcdd53,72e17c41,9310b9c0,2273534d,265f1c24,bc1a6039,238b5b7a,b3ff013e,9cf4ff4a,6a61c11,20c4d14b,97275547,b2358f75,8a2a774f,22bc77f2,4c6c27ca) +,S(d9cef0cb,38c6ed7,7aed53f4,93a31daa,50ea1ff9,aa43b890,357012fb,e2ab593b,32a416fe,246872d5,b68e6654,7f19dda5,e161cd13,ed041486,4d0c2dd,3653303f) +,S(1641b549,88e22a18,45e2135f,88d2386,4e2f607c,ce33fcce,de174a60,1ff03da3,edd3f0d9,ed718767,3664e1b9,540f816d,48e3ffd1,cfad86fb,5b68dfbd,7d94922f) +,S(99ef6c16,3c5d9ffb,1b638b21,1ef60166,37938f83,ac4ef76f,96c5366c,3a243cdd,6b6eef62,d819581d,e1dd7796,df043e02,7f912bbe,6ba1e499,a311769e,d6f66b48) +,S(49358d2d,c24c2e8b,80ff79b0,9c53645e,b9c47fb2,8ea4ffe4,e2f4679f,5b5bc97c,512a14a7,f9df40eb,ef7c1cbf,f8e1caf7,b8c80748,7bee3865,37837cae,dc1cf5b4) +,S(e31d3fcf,b1dedfe9,2549c5e1,a14cf383,c8c122a5,a430aee8,a4eec846,7ceb206b,7bc3c266,69db747b,a3ad1dd,e83464f9,effc4dad,b4de7687,e6c3ae56,a5b0449b) +,S(a82ec9d6,ac86bcad,fbb32e66,a28d7483,ad1fbb7f,979a15d4,4e324ce1,86c64adf,bac87d2b,c28b1ae5,f4ab46f1,ac779936,3a9efad0,b1e1f81,6c4ec6d3,cf74c89) +,S(21650994,2bffa2e2,479ab82b,18cd3aba,dc370c3,18c71c08,1bfb53d4,c8ea113d,97a8246a,2f653450,be513b6f,62419f90,f84511be,9a5f3103,feca77c9,c81fcae3) +,S(735050d8,208d9688,7fdf8070,e448e905,e4e666c6,3db5857f,63ceb362,c6dd4b84,2e3fb438,563661c9,e6fe23f0,7c8219ac,216887cb,ad307918,90001f9e,2ab9e2e9) +,S(1d021861,e679dc08,a34c3813,bb9c3e9b,65f87800,79dc08ba,197b3b66,47afe506,14b6d0b9,2303483d,9e92cd0,e295bf08,4a10e84c,ee0b7c50,ceaa6ba7,a6fd2f1f) +,S(7a7eee74,86ca8973,a858c15d,54eff96f,f91c5577,4985433c,1bdc4f4e,46d7810b,5e83a07f,5d9150e7,a7f5057d,87d19ccf,448fed1b,bc1e2297,c7012cbb,77b498cf) +,S(811c3d4,fe0a3061,47c05cce,32a32ada,48593802,32f24b51,f50e726d,a702beec,a6fcc16c,6c9cf4f9,2e84214e,35ad8577,b28e599d,31ed74ef,c95eb2bc,833d9f0a) +,S(2ca14438,89ea9e8f,24d5b1e,e44b26d3,ca25ed08,30ae950b,4357da49,21ad606c,7b48a6c6,92d8a29f,a82c656,f8e6213,c5e90be3,7413d86f,42e10e63,1026c551) +,S(a980ffe1,61041afc,339566d2,957e6624,5dd3e0a5,deee9c80,d57b1f1f,390277bf,3276f7a2,a139343b,87744079,7a174d99,457d5005,71ce47fd,10e41456,7a1c64c) +,S(e570a3d9,cbccb74f,c582954a,998d9371,fde41d98,e65de6bc,9579d6ea,d9cd80bd,5a749177,6658398e,99d3035,12c168bd,9400bc6b,c7dea2df,2739abe4,16973a52) +,S(f5fbaab8,952cf50e,66d3d985,d9280bad,71bd25fb,662358c9,72b954c3,7bce1110,1aca2123,6179f821,a4f097c9,b48a744f,8010818d,3ac6cf,b02f57f3,1fd77175) +,S(2e5942fb,ecbda8ac,15408d25,84ab7c75,6f9e525e,c4dbf375,5559dea1,718d89be,95106507,d14199a7,8f123d6c,f6b59262,30e12a1a,8c0ca016,40c556fb,14bc790b) +,S(92bd04a9,891c99e9,6c9e7f64,a9bab705,a9fc83f,a739413e,a2f33001,b55dd296,cf5e3733,659bfd9c,e5417ba6,817b6a0f,5cc71c34,22165fbf,13019fad,822ba587) +,S(30ddfd9f,a50feae1,e3cc5426,93ddcd62,4d0e52b0,867248ff,ed7d3041,a7b11083,ff15d0fe,2bb183ef,a18389e3,df4269ca,21bdfa0f,87ac641b,14bdaf66,26eb42ed) +,S(8cf5150b,df8ac68a,af319688,245719e4,67ddddb,72fa7f1c,baf39de7,252a9f4f,4c28497b,a299bb19,83b18bc2,508c98d8,2a9963ab,4145807a,2130fc05,4186cfd5) +,S(1f4b4efd,50974efb,3caed9f7,4f6dd979,4a8e3c09,8decc1b8,69032885,265e46c7,6c4b04c7,f7c6b00d,c38adeb8,a47c7c0a,68535229,26500a76,ee1c6feb,179fd399) +,S(9d524900,979afa33,8d5883ba,d13d038c,3f915cb9,f2e29a2,2a028b18,d091af81,409a113c,37c732d,1104453d,6b161d33,db3257c7,b9a73e1,e0f1679e,6802be38) +,S(52e1e4e4,46eed9d0,fd5c124c,1c23403c,5b03984c,f3fb0734,c627f34b,dda2e3a1,674a5731,7375ffa8,c3c4104f,12c1713a,e2dbc630,75a77e5b,26243217,88320a7) +,S(827b171a,8eef4e31,e6ca8b6e,7c6e36fa,e0a6c93,a77d7148,fd22f7a8,16401c8b,65cd85fd,d9b801cf,7de6d14c,b78ba114,1d58c7fa,129f9f08,8275f364,f7ca7540) +,S(124f45e9,61d89642,6a1abc32,e582e246,3db29954,1bea0383,c83f917b,6cdf2f9b,734e2fe4,110b65b1,2d60626f,c6288f87,e48e7a7d,213a4f83,58bb4d67,978d6c62) +,S(3b71298,18b2b324,10e4f002,899204c4,fb4ac9d6,c08d1169,5f5f5699,9a4d67e2,28b6f78b,cee16b5,5c010c51,ec465609,54ba6368,a18ae218,5607c1fb,42c27c3b) +,S(d271d87e,63b5363,e664485,ea6ae70e,e2b887a1,c7f7e0a4,290033b5,512d8a00,65d43804,7865e280,273122ba,8d8644d0,60ef5927,34971c9b,510238eb,9d6da847) +,S(1d1c6d85,2db35160,67cd2d82,e2ba552c,c5ca0c9,ae7bd661,e8494b25,cdc03a7,bc31f4c5,673a0256,ee9b6840,7aeea035,e19f874c,bdd7aebb,ae8b1005,4e691f62) +,S(8fee89b4,71e394c0,ddad274c,ba07de4,bf8d2ecc,695647fb,1d3756fd,46efdd9d,5d5ed0c3,8479578f,d17ae148,7198f9cf,4ba5cf71,46a9b4c4,34ca62f1,bd2b8c6c) +,S(681fcf09,55a0f351,6026302c,ce3ee475,8ef9385f,908dace5,5d7fbd30,8810a6ec,4307f75b,99927624,80187edb,c4c6575c,aa54e8f4,190f0885,e6d358ca,2a38faa) +,S(717fd6dd,fcb5a71b,6a584a28,904cd3e2,6f6c1a89,7fc32918,90792937,b3a6971b,b403f829,71d4ad04,dfc40553,27f6da8e,1b05e25d,341d6511,d7cd859f,58b17992) +,S(f2b95723,94eb9786,dc1f4998,6ad8c340,d2983279,c7323a34,227013e3,754f2ff6,b553d381,3efdcd41,7189645d,d9917b73,bb69e705,e7a6520d,dbaa497e,7766763b) +,S(58714be6,d7d86c89,65309b52,414135c1,9ab6fed0,1d56d7b1,4237485d,96730b1b,c011f34,3e38ce5b,deab84ab,f0e31537,4039845c,5fa4fcf8,1c9a4d5e,6fe3e533) +,S(d33ce5ab,c828f8c1,b9700d49,e9c6b4fa,b30819fb,b2bea23b,7f915bc0,7a6ff3c3,739b6222,39134988,22372ba6,3fbe22f1,ef59394f,37db956f,caca4c11,75a23395) +,S(82202e32,fc7d0128,4eba58ff,42f7d94a,24904808,d6f920d5,9837b26d,63b55479,fde63954,50aa55e4,fcc4fb0a,8ac2c880,63aa3202,9a00004d,edd033b6,2563b4c8) +,S(25e63218,5bfb4c88,fa300e37,c8b64a,941e6487,a34262b7,419fa7c,2dea7781,ae696b07,4802ab99,4df2d84c,c6731d7e,9caa124,548c2a11,42ddcdbe,f716e640) +,S(e0336a05,b9254e1a,f2468a36,343a0dff,9da8773a,1efdc487,53c1a95b,605c114e,41d75960,377ada38,79b97455,346266c5,5ba05915,9ddad050,d96d9d8b,afb12a18) +,S(843363af,7fd2b1bd,1c9a9253,e42489c1,34ee875a,db57b752,62d731aa,9a996662,e5c0a2d1,dbfcf013,ed4bda36,f153a328,12c4b3b1,66e48473,4d2d3562,ffcde6ca) +,S(adb210b8,1cdb7f18,f97955cf,fa7e5763,abb9f202,9d283c05,26df6332,951178de,2c211f05,d8182aa6,d784721,948afa11,364db5ec,2e1becf0,68eb91a5,74ea0e35) +,S(ab0c5fa1,a2fe2235,ee93854f,59a13792,2034604e,5857b6e0,88b65142,f24d7491,e54725cb,e36d6e15,71ca7384,7383553,b9dc48cd,eca6932b,8d92f785,b80124d2) +,S(3e5de77a,1d2163e4,3f3124b0,af4f5473,868a080b,67acd835,f9cbc88d,7885deda,a6ed34a4,2f9503b8,b83e66b8,377f514a,e1927dc4,c37ef6d2,254e651d,15746630) +,S(60c68b3d,31d1d326,5b71f068,8f32f671,32184815,f83a9ceb,50eaef6,4991ec05,977acc61,9f7d24c8,a95aecbe,987737a5,89886729,500ed32a,9d760fca,7c00d498) +,S(21b052e9,b646e595,b6c04a76,a66b9b25,4a126729,6bea0654,64fc5b31,69c135e2,1029e4cd,2fb3d717,6e87292c,4b0dfd7f,18db6ed4,59aa9285,38f0989c,540779e7) +,S(700caf3,7c664515,ee501e66,60be4cca,f58bb2ef,5b11f79d,c027dba3,7b523d68,7df76b88,f1c346f3,2e32487c,19957830,8eac9b7e,56edc948,e1a934c7,6fa08be1) +,S(c5ab9bd2,1293844a,ce5f1c9f,56458315,5d34aeaf,565a5696,29969664,49541e8d,2d2081c6,c61d6c8a,a0df4467,8ec53a7e,9b34d90c,57db2f29,c0361304,1c422d8) +,S(8afc3f2e,d53f5dc1,448d15af,8c2151d8,5713fbdd,5d46a3df,a4ad376a,16d75a3,11f6393,2627bed9,e015a99d,42193e6b,6767c513,f3457897,64db1a4e,4b9b6c8f) +,S(414ad9c6,bb55eadf,cf4cf729,27ae3682,c0d69867,ede9f4d8,6e173c50,3a5007a7,c4bcd0f8,f4de9b1d,1102306b,667a546d,dafb6f82,edbdffd6,181dd3a0,8f603585) +,S(8211e240,b84b7317,a9c5ec88,fdc426d1,a163a2d3,aaf088fd,b2317247,cb04a1e3,ca76a59b,b268bf51,4f06c4f6,d5961b33,a74f10b1,70a9e9be,4e7942ad,26722bbb) +,S(a54dfd32,363add3c,75ec4452,3094b27a,3f7cd291,43ee52f4,29e59daf,22ba700e,f160822c,59510098,3e38d130,49b44865,3639ac74,6a93ebbb,2798d92e,bad4490b) +,S(77d64c55,52b29b18,a7aee1a8,1e1e9d3f,e2921a43,cfbd95e9,a3ba7cb9,498860f7,e97bb8f9,5ea9c8f4,440ad9d7,36d9a06a,fed03a9,cc96597,8d06f2e7,8208ff10) +,S(485b3814,b33e6bf1,1f47f0f5,f0e5a189,1b57c103,60f13d61,ba7947e1,6e309ca9,e6bb7870,ce607c62,a6050dd0,946c8653,f998576f,2c781b5f,15282632,4e38bdb) +,S(ed2381ec,a90a78c2,def618be,1b6edc51,7ced2ac8,14aee9aa,b0a7068f,72739c,cb0873e0,ba326452,380c291e,c635381e,d6859916,2ca10fbc,3de45ffb,134954c7) +,S(e444af79,d36bc127,c83c7369,a4cba0fe,955b6a66,8f2e7a68,4a65e57c,44ed971d,a7876f03,27b6823a,aaf55ca1,6a48998a,ec50e5de,10d34e9,dd7c554f,d3785fff) +,S(af8c34d7,7de75a2d,5f64296c,152fea4f,b8677b8a,96cb278c,7ed7e50,69fc1fca,48ce5862,ab6ec1a8,ce55899f,24632be2,f3c451cb,8b81ca8c,f9ecc3b2,2984d673) +,S(e999d466,243a44ed,18e745a8,1da04a31,8940c88,760480cb,e315436c,f67efe13,f41e8e70,63a84661,c22ac0e7,5cdedbd0,ef55d3eb,7fa813cb,2f7d1fb0,237fe36) +,S(c40ef8aa,7d8964ff,a3cc923a,2fdebec,72cafaa,58b72d66,e209b644,604bcc72,480e35b5,6549a1e6,f112c3b6,176f3378,ff50ebc7,b3efac2f,696f0627,b7238ce5) +,S(482ee41d,e3f9499c,8992b546,e209aa5d,679e4b8a,22a26615,1bd1b0f3,9a52982f,510e6070,d2131815,2d52eff0,ee10ec6f,563f92ec,a0e549f8,32efee81,dcd3de3d) +,S(4660a468,25a3c1d6,67003c69,4d36f8b7,6df7ca32,c6029fd4,65dcf726,5c58fae5,3d982bf5,46b5c296,988acc72,2dade97,791de8e0,150397c0,700bfa10,ba23f618) +,S(b85e624c,92ba6f0b,3fb1dc03,37589994,d8a3aee1,76314d87,3c8bef0,eb5d8f8d,88936b33,20a55ba4,24bff93e,fb36ef77,e2cdff26,866cd486,88c61868,ad75679b) +,S(a41920b5,361a26bf,bd187eba,b62fd5b3,b3be282f,94ef94c6,b1698754,902683e3,7790a196,29d93b4d,3d27201b,32053e02,c95a8f9f,260647d1,491c7a4a,e15ea98a) +,S(93882da9,79262a60,3252f01b,bf101d89,ed1abf8b,15507937,55979f4,fbb7f86e,938884d,f0a716ef,137450b,ad5f022e,23e570f1,3ed37e02,15a40910,189d7e44) +,S(a10509f8,3efeda60,acf56b64,3709b197,ebfa6a59,e66efb06,79c23225,c8952e00,759083d5,559bde4d,9a37df5,24cae4f8,5a06e7a4,7c0e15fe,830f0677,c24d1ddb) +,S(2b945a97,6ea74278,73d7e587,dfe75eaf,c55de704,8aa26d29,4b3b1ef9,2beb3651,423c07ed,27ccbf6b,9d1cbabc,e00b451b,cb488d10,e5daf752,6980ef65,6893c65) +,S(86f9d9bb,c1a0bfe4,e03da0,13f17017,f6951691,28d51073,b594457b,fcc37f0,f370c624,2e303796,9339cd4b,fbc19e8f,62388f02,649247ed,df3c6c67,464c32bb) +,S(f8995af9,555e044e,6238d895,8bdb4e13,3aa9cc0f,90a9206c,16d0f83,dd61ed91,b2d4d2e3,1bee12c8,3b8bdab8,34fa2e63,1febdb93,4af194bc,d0825921,4ea8a060) +,S(ab55a27f,69eb352d,455eee7f,bbc1c74,fdea2621,63d09dbe,2f554417,819394ce,8ac56493,60f585b5,72373bc1,74a930e2,4d54674a,89789353,4a166200,e31b801a) +,S(4fc106c9,c428df98,916a8906,95e98353,ce84723f,fadae522,e8a89916,a23b9fa6,90d732e8,3d54341e,4f4c88be,3392687d,fdebbc4a,7c19afd1,92531688,9eb92031) +,S(c9c85980,8831cccb,77d87b25,73a894e3,874080db,ba076b2,4626bc82,3149a91,9fad4ccd,8c0949f9,be9a6355,f0bdfa36,c5f99268,780566e6,a6f302e3,b2abf0b8) +,S(c0cb5b7e,2293abba,5ff78c46,aa1422fd,a278eb5c,ddd58cb7,aee77149,1c5209e7,8101f42e,36648586,5db6b572,d0df4ca3,cc72789e,ed8d27d5,1330d733,4e10c47b) +,S(dbfc03c5,16d93f83,472ab60c,391bdd69,af2f70ce,fbce15d,140f1590,d8ce0b63,f838e14d,7228ca2e,9a33ecfe,c376aa4e,4811fa24,debc7b46,4658c153,fed26564) +,S(de260685,eea13ff1,1571d891,35607b0,435140b9,62e3baf3,7cc3f3d3,484f59c8,6bc23ec9,a6025865,e6363335,bb66f1e6,c1f5f54c,aa5f8acc,d7f8fae5,99cfbbcc) +,S(58d78d60,b8cb8fdb,1b62f79,f6c17ebb,1ed8129f,c7819e5f,6f73c58b,faa2e71e,7cbbe4f2,fd6bc671,8c222f5e,16391133,b04d6d3d,bb263531,40aa4053,b44bccc1) +,S(e72b9b1a,eed54fe5,ab0869aa,90014e3b,fde1af12,f646d738,211ebefe,2c00a490,cd92d14d,8f595833,842af020,a2cc82a0,d9465969,77d25d34,8cff3273,90af4fc2) +,S(6a86bae,d55a5be1,8b7cef97,c9dff8a8,b725f614,2221485b,3b9f348d,570fd658,138a2691,4c3b3015,10312140,7709fa87,60a14f14,571a9d40,81c06d67,ef2c5724) +,S(1ca08cf6,7240f244,d6cb0059,564fc283,b17c39e1,b624716e,e100aee,bb0a1fc5,22e24fce,655d09ec,34a36317,c66adf07,deec2743,4e9c016a,b3447b3f,d9ef0a55) +,S(d95dc128,feba49c4,73e1ee30,bcddf76e,b7cf628a,fd495607,f558201,824a0b0f,d5340ab8,fea3e137,d1061274,c69b68e3,633e98ba,ae2fe116,1612c4d6,c7c7e8a1) +,S(2cccd22a,7f4fd501,6c3a14e4,769a88bb,ae7e288b,d9987afc,f3c073f8,ccd83857,ae82663c,aea89fdb,4710f849,ff8e4fb8,c2a7b614,4edb5f3b,f2564dd,73b389e) +,S(ec17b5f6,c8e4de23,937bc003,7dd7b411,ae4a9ac8,1f2801d1,c5eebeb0,95628a43,b3e0d51d,fd5815b0,631fa298,968fe70f,74319620,f0798734,8e66e6e9,6658df83) +,S(a63aaf7e,62907f4e,1a613cdd,ccf1cc98,cee8e12c,8bace3ca,95685889,6c96b6f4,2f953ebc,dc5f9973,e9d44ab1,23255a21,f8bc1a7d,e800cdee,ef754031,aee8216e) +,S(8eb4ff30,318d8d0d,6b45636c,b296d137,90e345af,19a14b2c,550e0169,87ad32f3,4b8e238b,ae7ffc7e,7d600151,c5ebb776,cc01d51,827f171,8f28af77,ca4a3e3) +,S(ab1420af,bb5b4397,f578a6e5,74a63aec,2432fa8c,b4712341,4374add9,8276b8d2,faf6328e,a197e927,65b9df90,a79558b,8f264acd,7f75b574,90ce657f,64b94396) +,S(daa1890e,6b2ba1aa,dcc16f1c,c1d5999d,5102aa6b,348ffb5e,9630bd1a,886f680a,19facb69,5a63eb94,cb6f6005,cf2bbd23,8744f05c,2c220510,94bf6dfa,352fa209) +,S(f199f52e,5398d1b4,899a1b0e,df8ebb73,cb08a642,bd22ff2e,b1cf647,7f2a28e3,3619abab,a5e2131d,4ccf8747,67ef9b75,2d5278ac,5549bd9e,5c1b5985,9ec2f454) +,S(182a9e93,a5f9ecf3,6bf20bc1,497a4a35,e75c86f5,9c9fc046,e2752223,f046ad6a,c6095e88,c39dee18,ca867693,d85617c7,e0924dca,34af07a5,c731e488,ffb8bcfd) +,S(8a4cb53,636fe66,b660a701,64277fb2,5ac21eae,906327c,3b509954,b214abbb,693e5851,4cc6a941,23ee707f,ad25d638,8efcb85c,25500bbf,63fd6926,a1d0ed1c) +,S(4901335a,3b40c33e,115e3105,82c55972,f495178c,813bd8d4,2b208916,5c8c1282,47007176,429d740d,dcac70eb,9e1f0e94,f26a9a0e,4dde0166,24d9d801,c84214e3) +,S(8180e5a7,7c34362b,461b49e1,4b9f2e05,df64f55d,cad82b8f,cdf41232,984e01dd,34cf5621,d4b9f100,aa34b0fd,5ac91d,cb3fb49c,130f3e65,de742ac,af05040b) +,S(8f71227e,fea9f4a8,9dcaeee3,58eaa5d5,a90e1d7c,b7d232a4,b1085fe1,3651ee9f,a4da15bc,bb2b79a0,a27c344f,3b914c7f,e272676a,bc045aae,e90daf4e,9655ad73) +,S(dbeb9b74,cdc9bacb,83592a2,a3cc19a6,d1ea8eaa,5a086a3c,cc68d423,cfb58264,be668792,83f07fb5,830732eb,79e16bbc,18e86df9,b659e2d4,5b1009a3,40576161) +,S(f328e93f,a7f52cac,3c7b09b6,280528dd,7d9ea4df,8317a958,ab0a617d,71591933,41ce8c59,811f9e90,61dc3f3a,7d85d394,2f780c17,d7ebca60,fc042311,f2405547) +,S(56d4f403,416b1bec,49c5f44c,2021c245,d4ca4181,5131cb7d,8fb0cf08,30fea051,cc5f6133,948f77be,934cb637,3d5dce8c,51ae1a92,e3d802a8,d35a3ad8,50bd39ce) +,S(8e22e04b,8bb476b7,ccebecf2,a327fd41,dd90c9fa,67cfc49,e9cf4ebc,39022b8,1f8fa60,3204696d,34d03c02,31d3ef58,934e7992,c2c81d6d,3e4193b3,8286b8b) +,S(341b22e4,b9a77c68,f93624dc,1cfefade,b396eb72,bf9fce1a,51867d7a,ef064f6,95839f87,7bc36957,59562bcf,dbcb2db,af11ed72,7d329e27,cfc8ff8b,92d9441a) +,S(3ea42e93,9f234112,879e8680,94fb8a8c,e6af9799,c000b050,45eb2026,3b324763,44324167,6f5aa6f0,4fe4d51f,fecfa8fd,60a5c0a3,38b16f59,1866bd51,2e01a623) +,S(afe7b44c,ef10514e,f94faa1b,b8cde91c,23d8a660,a71c3173,42f927bf,535d625b,7617a788,51218772,bcd51f18,54b063ee,6cfcdd77,7a388427,51cacfc8,7c8b4b65) +,S(a360ca02,abb40d4c,6e0ac725,f2c035d1,12f69ee3,ff0e1dda,37a3fbe6,a2034d2b,161e7178,3fec3ea0,305d0f72,3a60846f,9d8bbe79,255a3814,fab83269,916b646d) +,S(10484e5f,f7d9fe7d,9b65d989,9b2ecdf7,5984d18c,1199f61e,c914a85b,78011b88,12d1c0e2,1e137357,9ede1086,c2c1449f,e7f4e03d,78f7b184,4f744d6f,9576cdc9) +,S(76c2dce1,5dad9ddc,24276c40,eda7de6a,b11245e6,5b051ba6,7eec39c6,d256b138,d9a601ce,585b0839,92e3fca2,a43739a3,61a8967d,3eb0f605,ed4cef15,fc64ce82) +,S(671a31d4,c3e6ed98,b044fcb1,83dc60c7,d1d83988,3a547356,27b99b75,11d4d2e1,6fdd8d15,2005ef3f,7ef3762f,bf43a849,281f185e,a52c1b3b,8ad371b,e053088) +,S(91d0f565,a2da6903,12b72998,57f71c18,c2343a5b,a6f17243,1bee12b6,6e898a37,9d6a55b9,1956a288,d56906a1,1f7cddd,e6393ad6,7249147a,41eb209d,7f32f681) +,S(7e59f058,1953a2c3,cbd4d0cd,8f4df7b8,6b90d8de,7240b2d4,de9ee9ab,628512ba,33b0d1eb,74cc7d60,ef419b0a,f9f03714,35a012fe,328fbdbd,9d9b69bb,9a42a173) +,S(ef78b65e,b791bed,212652ad,ab1f32ff,f43a285b,e040380d,d3492afe,1f788a26,9f3e2538,96d5afc2,3ea7ab1b,24ffac00,f156cdef,93957910,abb1cd1b,6508d306) +,S(78b978d0,199833ee,417ce2f5,fc12cb01,cb0c0e4c,ee752e1f,50a8cdf9,b56b7cd3,9f9c7dd7,372606bb,e3170141,31591f5d,dd631f91,71460cd0,19709478,c1560dd9) +,S(5f785b31,f9945a20,e992f5e8,80abab72,906ffdfa,7ba7ccef,f1b3d26d,70bfe64a,a3cc480e,b3cb386a,ffa1988d,3227911d,57522413,e5d0846e,a6d0a17,2b881c1c) +,S(9fee7999,7cbb492f,cf3aadfa,44484d71,2c3887d,6375c887,fcee9fe5,7806b3d1,43114327,7e94c760,1fbef25a,60e7d448,c0f0b8f0,c6c64437,a129d4ac,fe24c22e) +,S(12f2dd5d,b75d8885,7b7befd7,cdbd7c76,c0f46213,f9a53102,acd8b0c,4ee3f30b,ce27598b,e85049e,9ad3ecfa,d3864070,f8510570,742dd021,6dcb4aa5,334097d8) +,S(c694aeb4,3ee2b51c,3f0e2bef,bc1b718e,fbb86f9f,1d2a84d5,3178325d,cf997bd7,41481f3e,b6a69311,2cac3e5c,c85a6f83,6fc5190e,ae71f226,bdc887a8,ed050977) +,S(fe266a46,14c5c05c,baf3493f,5af3befa,9bd16100,860bab1,49ad78ca,81047ae9,e2e18ebe,c44694d4,b7c614ad,a772a5c4,f58ca087,4957f20b,741b954,18471e6b) +,S(7975e1dc,926b07c,487abfa5,8c183f21,daff04e7,f67a7250,d2152699,356118dd,c984b97,44e2b300,ca54b779,da94dae5,f943468d,9b9729d8,21cfc709,351409e5) +,S(93573cdb,50183589,a0909e8e,6186d700,7022848e,b7fd6951,a137cf16,a18a2b06,7a957c66,3047f6ae,d9490039,d136cace,caae2d8a,fc9f50f0,26590e62,18bf47d5) +,S(c0bd2e1e,a60f6feb,8e0ac7dd,485fc4fe,6771d80e,e29b4829,fa37faf8,81d77660,685b74fd,9edde1c7,69076267,d9f247e8,2effc83f,e25af065,56aff25b,335bacd) +,S(56bac0e1,93192207,906e5f45,d55d5919,b676852b,daa43253,8c4c6984,9c24bf3e,e3bf51d3,9856b812,d2e480bb,c8b118e5,f5c0ae9,f8d09096,a5d207ec,2fc18ebf) +,S(53b41b13,fe7cddd2,ebfec7c4,a985fded,5225fa0a,b93dfc65,2314d1e5,9142c06c,90fcba57,44e4f565,5d2e038d,ccd2bdff,cb4ecf20,48f2ba98,8f62fe9b,c3b69c4b) +,S(bda05e31,ccc0edb9,47ebaa26,9182ca2c,b1136400,674ce5f5,d55fcf2a,c2e765bb,97275979,31236982,a7459356,7308ea97,2fff08da,ae8f707b,447dc6ba,7acf75e8) +,S(48dd1c6a,8a900820,1fe09f19,a8f65f82,91196e95,1c57189b,21aede15,4b0413d8,79a1f6fb,1a943dca,6517e287,80e72f85,c5a6150d,9c50d7af,78c8b63a,c09b98f5) +,S(75406269,e28c428c,8eeff4e2,ce9a2d48,2be8346f,3446451c,13c04380,292f1f2c,4743b097,211c807e,23cd13d7,692efc44,202c44cd,dde5a89f,9e7cdc5a,133e996a) +,S(1aa55d93,bf318133,8840ae4d,24314ffc,74420002,4ddab165,4018604a,2192ee5f,babac354,92d59e8a,656ac7e9,9b6cb2cd,7a6a8e88,1361118f,41e38f1,2cbc3bfc) +,S(75be4c50,db7bc629,e64fa850,3d89f722,34d214f4,9b176fba,b937120c,477dcf03,b5520bba,8c606db2,833d934,3c860c6b,ed2e5e2a,41a6c3fb,51154c82,94b6f819) +,S(f3604822,e4ed8b2d,d4b2486,b611b95b,6508e10a,1c725c7d,8d638254,3cef6c8c,f405ac79,7404bc6a,25a1a412,f2cbd698,75d99d2c,b23ac105,f9839446,222dc0f5) +,S(6c9216e6,6f9326be,fad5f727,ae0326b1,da54ee91,f041d2bf,2d87dff0,e77cefe8,64a021b9,bc1947c8,df546f92,2f5230df,1ec2f372,50651704,557377fb,39d16517) +,S(ae479bbd,ce97732d,df313861,76807678,988346e2,5b12bb09,e74d474d,6905668a,a3ce0355,c5996087,89936336,64d9bdd2,b33a6f97,ea3f28b3,f4b7c0ac,92612929) +,S(ce92c179,66f74db2,1c6abcf8,36cfd2f7,9ccc05a7,aaaf74ff,4807052d,754898be,f3e3d3f3,530f38c4,ca59cec9,c8e3456b,8adebf80,3f10927c,e44e79ca,601bb978) +,S(8a0bbeb6,815275c1,77553d87,5988c4dd,a96f5ae2,378a3b9f,477cf162,6e6ea68d,958b795e,46f45412,acaa9f77,85a4e34,a7c74eac,228476d6,4e917983,5fe77f4e) +,S(c2a44ee9,975400e2,912a033c,5ed1e7a,7f0e97a7,1c86e8d0,c2e5136,8dd9ccfe,956435ef,7d24a507,dbfb69b2,93b51369,70cd17b8,ebf6bc60,596a9545,3675668f) +,S(b615f625,534ddf61,47b78520,85443bcd,3d3bbe91,c815dc29,3cc891c7,199ea1e1,4cabf4f1,99e21468,62a90876,4f65c624,fe3afd1c,6b399e12,267dc1e7,6ec3ea25) +,S(afccaf8d,fdcf272a,cf56dde9,3980bee7,b64a0c54,f80830b5,5fd0b93b,e8a75a0e,8d8ec5d,6f6fc44f,89f6d9ba,735861af,e7d6a660,7d88e95,54f2c141,1042a63e) +,S(534176a8,c5311b26,1f39395d,f78300b,559d072a,cb3873ac,32833c8a,9b135f53,ab84f84b,5eeea7b4,87ef6639,8fa756fa,b16155d,683ef643,b03c7e6c,8ab2f436) +,S(5f7aa173,97ef95cd,f47651ac,6220f3f2,8705abbc,6531b93e,a65fcd14,1b91f34c,72f7d898,3cddb34f,2ed1b28f,17a77d8e,70cffcbb,e0462ab8,b7548561,9881da22) +,S(d4fb4a4c,c1e2fb72,b301db19,335afd7a,b6a95258,6903f449,aa287fc,ac8f3d3b,76b572e8,4a9db287,19d098cd,5dee787e,f213adf7,9ae90306,8881894,e14154f9) +,S(3dc67563,843b2f3a,f793116c,23db0dd0,dad0975c,5abbf569,4c6d454c,9f7a9ff4,ddf65b9e,4db83b7f,43d0ccf8,49a8088,4894b277,4fb39fbf,78076a60,356753ae) +,S(657912bd,1edd014d,1386c449,50cd1667,9c3e415d,b36e36b1,3bb5c82c,a2f897d6,4e2fdfdb,11bbe240,8fc7fb3a,acd06c69,78205e48,c9f5a143,4300927e,fed505cf) +,S(5a453b3,5f82585c,f64473e5,f3fe937,6f412b5f,c4ade7b8,f9081717,3fd69f99,fc169e11,3448b590,4e1dbf06,f4aba1a3,ec2fb4b5,7f2b4c4f,a29b6617,58097ebe) +,S(c3adf45a,c24b4bc8,69acfaf1,f96a3043,d01c68b2,f4ff689,93b37b3,a4052679,3d923207,cd374b49,d3a85bea,972b8d09,4f0d4b83,cedc97d7,edc2ac68,10ae19cc) +,S(1a32edc8,905ddc84,2756420b,e1f25daf,5bd84f0f,acb046b6,e9040fe9,5e1e1d56,7f467d9d,113a77f8,c1cb73ad,16a62f4f,f1612c80,f8cf859c,ca42164,68eb4c51) +,S(70957d98,62e5977a,c328b7da,2e51024a,f925c142,eb1a46b7,b4a3bf20,a74d6c98,269b56c6,2e42cc34,a0951348,881d2c53,7d7ee231,f2327ec1,441fd273,bef09381) +,S(7e69416,5a02b16c,107ae454,b6cdf25,bf6dd256,d976ab44,bd7edc84,837537a7,ffb874db,38360c11,e2d0d2ce,3e47363,a6aac21d,78a37b24,3e66826b,445185f5) +,S(4f81a159,7b42e9b5,24be7ac6,ff4405d8,3b9d8a75,e37b58b6,eed5525,6ddf9678,b2531207,18676065,ad8ffbe1,6d27df4c,3558eed7,992538b3,77cb8497,86e0a78c) +,S(fa250368,577065af,b60be8d,e9381334,9be6fdd6,1bdd02d9,3591a35d,294fd547,f0b991e3,96c77cbf,5d642f0d,cf5ee10b,3b0a1fa7,7b696f06,d4d61cd4,48e81625) +,S(f4df5ebd,67162bfc,3e3da517,23fec6d8,6b61d6c4,a5876c4d,440f025a,272ba3d4,b23ba279,8bc75fb2,d590e384,c9e375ce,6380dc5b,90970d44,674e32a1,8052212b) +,S(ab5b6b1d,b3725b65,fae28e5c,44460d6c,51b8971a,655541c,4a587076,a1ac5015,16d2c457,ecdc1718,3a9d7876,d785adf0,424f72b8,d08b6a9e,286ea5cb,26ee2ece) +,S(a8a4ec7e,13db5370,40680dcc,470d38a4,2e08d99,3c30f981,ac877ce0,28869e55,4a9ded9c,2cdfee53,897d0874,965878a4,754025d5,babb07bb,2d248151,cb7f60a4) +,S(337f54f6,33bd60db,583d08,165c1011,6f914dd9,e2abcfaa,5f1b1410,b8245f48,9453eb28,696afc4a,40b5452b,d3abae4c,679b58e5,408b0a94,ea456771,8176dbfc) +,S(eee337ab,dc2baaf1,544c767,41a5054c,f6db480d,529d20e4,33b642b5,fd901b53,65ad5b88,2cc00d4,a4ba1d8b,a6956f88,825616cd,5f6c9830,87264247,869c27be) +,S(59f52e18,57cb000a,ec1aaf0c,b43f1274,f56e477c,50f010b8,4513fcef,752f1548,d8ca8f6e,8db03b19,8047006b,a6ec1e29,4f225461,9978ce70,a1ae0f53,9e1698be) +,S(6c200a0c,25fadb2a,8a9618d9,d7d86287,8583ab10,f2b4915e,8f5a5f05,406f3e12,14d4984e,374cf35c,145af24d,a5c0d0f8,504649d,b1793a61,7d6ed949,ef8081f8) +,S(1654a2fd,51dfb6f8,71aad878,239b657a,f8144ff8,98a3c2a0,32cffbc3,8aac07fa,8b6965c3,82d98569,b6cdcc1d,4c71705c,e36d46c5,fa5df714,8d59043d,161a839c) +,S(c48033c9,ab473556,c9982f33,953f96d6,7b3ed1c0,7e98efae,8de232f7,67556d77,af3f48a8,7bc43168,10787382,8dcffaad,abe4a54,828b02b6,f897ab4b,ed968bac) +,S(5dd977fa,a9d228d5,1885aaea,557f7a98,1a95771f,212c3ba,f1a4e428,3c3c6032,3bff9d4a,ba178f6c,d2ce5204,3daa06f2,abb06acd,274beb45,49e72a37,8f2eb656) +,S(2a76dc25,dd9a96c4,bcff1d8a,f3c9aaeb,261e4c69,a5d832ab,efefdf29,ce42a564,493452f2,3ad5cae7,de8c972a,af5c3883,7ff82aa,df616c5b,2fcab4c6,d5244806) +,S(cdbd3506,4c0e7923,822bab4d,b4662b2a,fd3935a9,a49e6d6f,f2db8f4b,d29a4b8d,b2126f2e,b61e274f,a688a84c,386fdb5f,58b84515,1543f143,a8ce0a3c,557a90ce) +,S(983f249b,2cb9d4f6,34d82d6c,e713aff9,2bcfd525,a4146a75,bf6f725d,2b54d978,79cd2406,850be02d,a6c4d25f,5a02dabe,af302e77,238833ef,cff0484,8a8abca2) +,S(88c765b4,a5a0a9f3,a257e21e,ffb23e51,9e128489,fade1383,96fdc18c,38afd4f4,f4d5fe64,729995a3,e505cc03,1d144682,e3336ae3,45c28697,1cf89b7d,595f46c2) +,S(197505ec,d603cb0a,199c7d08,b88d0e63,1d147d07,45603692,6b67a582,b7d52c31,947ec13b,f2d75fc5,af5a746,15a75aec,2f212adf,a234e08f,49a561e4,f9057f15) +,S(98184232,e10f605f,58a700c3,d6bc04d,9a19790f,eb638fec,668320b6,3b2eaaf8,ef3076ed,c0e2fa39,30ea837e,36c31f59,dbbdf192,ab18bd7f,39578e1c,eff9c85b) +,S(50f0db35,cba0134b,790f47a9,e322670e,f0c7d081,6869a764,892f8e23,6c0bc002,1c1b1c13,d98a84ea,e77260f6,58e265d8,9e466b44,b8604edd,83233fcb,68396072) +,S(d9e340ea,1a44bb50,ae4bb19f,3f4228f4,99ac8516,cd6bfa86,80c1dc31,d5665502,128a1688,335a2193,ff805251,b83baee9,341445ad,25048f75,355f4b35,5b4aaad8) +,S(15aa8133,1b84ddd,8c706702,555dc19,d42cb4a9,a9474c9,1e33c0f8,e87c539c,79530720,96586400,ebdd2ba1,7762ccbb,2a14bf47,45f63b1d,e22c6a6d,8942dbc1) +,S(6f708704,53d19a4d,b910d169,c7c7be23,d489dd18,23604242,55e182f8,7a5d34d2,e0531956,7c1c2bf4,a0a9beab,d7c4dbb9,c15246fd,5a6de101,35578aa9,99c7dde2) +,S(31e920df,cddca3a4,89ad97e9,168854f7,867f9a58,fe2d3f1b,f33f8dc3,c7167674,52712c3d,b0c6e58,15157660,ebe27dd,244a9c5f,b12cb8af,dbe82900,c3706f39) +,S(93300e11,708ff01d,96f81fbc,f1888c96,8258dec2,86a315ab,8d01f2da,4c8d51d8,15368647,3b6a208c,8449b53,66584e4c,9e0b7636,1256df46,4c93d4a0,e26c33ff) +,S(8f7ad50b,f62d6702,616cfdf,3712c1ca,30c94632,720797dc,ecd54f65,cfacb3db,5ac10a32,7dc52bc,33290080,a1a194c8,1eff0d71,65f9d34,7e4f8b44,8443c637) +,S(6ed80b82,76270d71,c468f5d7,350c608a,7f5cf51d,c8efbdd7,4458a487,2989f4a9,bcba2bf7,38f6e77b,465e47b7,c687c7e1,77fd3cf2,163b5a2,e00923d0,89826dc0) +,S(ec6c1d7d,b94989e2,962cb85f,777eada2,7c2b11b2,86949e38,eea2bb4b,7ce3714c,add9f9af,6042dfdb,1d4089d0,ebca5755,8ef2664d,52fce516,df3b030e,9ed08fdd) +,S(8f77cb2c,24dcc192,593a82d3,510859b9,c59bca8e,9546735c,1fe26084,4a37f093,51674986,fbf499fa,70b55bd4,f0a70cb8,423fdebd,587e6213,83199b3c,4fe07202) +,S(e2addf74,dae831af,aa4c4ed6,2f4a32dd,1122f2d6,243d049f,1fd78c1d,64b6aca,7dbcb394,1eadfb0b,8a56022,4da66128,a73a1aa0,20a5b23c,cf58118f,e28b2297) +,S(bf7af1bc,614d31c9,9afb7c6d,b8cd45b3,3a22b151,34ba872d,3e845da3,2b1e3eb,108d39e7,8cf88a22,fba9e255,23267bd5,2b850464,f1277239,7542ec2c,50125a21) +,S(88ffc7f,cec6580,5b92c5f6,952d82cc,35b4f068,a7581987,93ebc59,8efe50e4,382dacca,94b42c3e,ba1558c7,82394564,d39193c9,8b9e8cf4,a63dc4da,4d378a4c) +,S(b114a384,ef4ae60,b9c569ae,636eab27,9070e207,34cf83b7,a28353d4,7d3a152b,37c7673c,e58ee20a,6076438c,9bbe5cbf,bb826076,6d8a7001,b188c477,cca3fa70) +,S(e411a42f,e20d23b6,fa768fa1,e310192c,cfb0cd2b,1914c9da,627db3a8,c7e801b0,feb1b79b,43ee7980,7252bbc1,728af29a,8f6828ae,8aec480b,91c85008,959b1039) +,S(191f687e,46432313,c085182a,ede5e7c4,31057ad7,f1f48fd7,cb814fcd,74ee1999,5464b42b,3f77ce2d,59a39efb,83808cd8,43e5c540,3d5081e6,e4f4b1e,926dbc72) +,S(63ff4977,33b0a49e,f618c4ff,1aef537,9837744c,85a19bf0,b2208eb9,506bcd81,6ccaef97,ca835931,f3a87b50,a93f2a4,39535d8c,87e26090,3ae49b29,30593d3b) +,S(efe36425,c37705ba,69e7056c,2e8c628,f4ce72a4,68d85556,4c57e5ad,b7fc2c1c,392b9b5f,27ac49c5,831a5f52,c2e55bb2,8faa7f12,521732d0,8dd910c0,51970ad5) +,S(6d2126b,dd7cc08d,1bd41910,21704a1e,841d4737,4e5a0a99,9880051d,f77e94a5,2e9b823a,cda1be97,52f72ce6,ead42127,efb4d4b0,8daf55f5,5da6f956,84e6e806) +,S(cf7b9ff4,297d9c5b,91c6340d,c1cca93c,6d453ac9,86b4ef54,b20df6a5,4933743,8a57808a,2c07002b,111efec2,bf751b54,c20613c4,1e3612ca,83baa3f,eb670c15) +,S(b160242b,4cbe5ee1,d1dba4e8,e2d55a0,27f5a5bf,f7c023f2,c1524fe6,5f7e83e,ac35795,6cf31260,7ee9f54,fb39d3e,4f1faaf4,67d09567,6075b8c6,3c0fb5a0) +,S(7a86452c,4f8918d9,86d3dce0,22619a72,1744d9c6,dfd0e733,c02cdaa5,6abb050e,30d30b1c,b558b0e0,fbaa1fb3,596cf454,542370fa,43d8a85a,91eb6bc5,c90de179) +,S(a279bc1a,64d206ef,ea96d3a,b97c6770,af41ba40,9381d372,408052ff,582323b9,aefc853d,701ebc44,679a7ab1,fe033182,e96199d3,4b58261d,a1b4a6bc,6ce42046) +,S(e8e28570,5c16016b,608c301a,ac59f868,f7691886,261eb01a,d4e6ec6,a9e5cc76,92350315,f8e78051,b703b7a,49427272,c988ea66,27699eb8,f2fb3eac,9f0a983d) +,S(1fd9cef8,14f3fc81,8055f300,a734f9cc,46da1585,b939be73,459e0ce1,8a9f2bbd,dcc4f73f,a50fcc8e,6ec3c71b,c045f020,c26d79a9,794a25cd,26488c0d,9cabb55e) +,S(321b1afc,70275fd6,fd48942,df364f6f,c609d4b6,cf4e574d,39f1ad06,927e5f11,1368ecef,808bb311,ae9ad36f,8a4c81fb,725a80f8,ed029680,c5b54463,80add33d) +,S(4dff29fb,6518e393,dad0cad9,b0dec257,f9ef1e9c,c50cd741,8108c42e,f6d7bf72,4159786e,5aa4e82,4d7bfd06,713ec8f6,d92f8f8b,48304efd,e4d0f4c2,30dfb70c) +,S(937a84a5,eb63055c,65b162e1,1a3f2e54,c839b972,95a8c1b9,274373f7,5ced1e0b,a03078bb,bf11a351,da7fb3b5,2e3e7a2d,62c122b1,a9cf9954,53294f37,7966afc6) +,S(be3faa9b,7cab73b8,4fb2a87d,25db7fc5,56f07393,b3177c7a,56df0447,e96cc3d2,e7051ee0,1ad794af,571dacc4,4e91f7f6,9276446a,7e348ee1,2998968,afaf77f) +,S(58b39c6b,cc36d506,765d24da,6058d7dd,36928d13,b0fbe1f8,c680df15,756c9b41,449e9691,aaa846d9,a7412b77,73f7ae6f,b4ecd99e,c57863d,ad72d3f0,423d74ff) +,S(9b18a971,b1660b46,720e2100,865742c,8c91282b,1c72c7b2,8192bdc9,c3765798,2812e522,98adf83d,2cc07a93,4a065016,2be28d37,59cfd50,6c792e31,32e0f49f) +,S(a6c44c03,dbacd4bd,7a35e207,61778826,c86740c5,c92942d7,f73a1b3e,dec0f59e,73316b7c,d1fa3410,76b73727,8aaf39c6,2258d29e,c40c80e0,797493cc,7056077f) +,S(1ffb2755,628ff8d5,d837495b,94ad78a0,5dde2043,bbfa3aaa,21a05a3a,21682ee3,4a0478f7,6b8b6c,ffd9962d,f70e2a83,a71243ad,c690efec,5f95aff8,dbb5943b) +,S(aea4c48e,36051144,482538a8,8ee5f72,b67a21e7,8fb425c,3d6f1e78,419b8283,4517cab8,c28eb397,bd89d216,ce711332,82c7b530,6b64499b,2216bba9,41a80b27) +,S(ddc59bfa,5f8c0270,8436fabb,5003ca6f,b972f14,382bf127,f5297990,b4ed3eb,f6c3f19f,b80e75b6,53c370b6,742045e9,ad7dbc80,3a996696,99605345,ebd995c1) +,S(22184fe7,ed301b2d,62bf83ed,64742f6f,94d204c9,866a637d,d03c58b,54c80a8,9db27528,1073a3d5,617a389d,c5698255,3bc55d6a,3b80cab4,ace36140,8179b442) +,S(e58617f4,74e46f4a,74b0ed2d,6bafc3cb,6488c510,87136eea,f98aa37a,f201cef3,cc6a7375,52dde05e,71bf2047,d49a41b0,c6b141e2,e4f519d4,36d4f8e4,f2aa2220) +,S(c1aa4092,5addc5fe,de6c2c5a,ba73b3f2,b7394a0f,1d02846c,5b2a0379,824f8e41,9713af07,e30af560,9516ce73,5083ab53,2475bf26,5713570,28b586c1,d28c687b) +,S(4aa5d88d,89576459,1ccd1c2,3064c718,69e4d320,b0b71b5a,c08d291a,c1df6168,1d0ea41b,915c288e,c6b36cc3,f7806481,c048482,dce586ad,f240d32a,c913e6e6) +,S(93876d91,73137834,dca5e192,fa869a02,738d4171,391df1e7,43296f93,a2a8a977,7948463d,32bad450,24e63d9d,d5786a69,73fc6c8f,9d2a7c3d,e2d6fbad,7a6457a4) +,S(c80cd7d,30268025,b233dd13,f2f2af37,5d18080a,54c406ee,50e4890f,a91b7b28,9f73b58b,1f1d11f9,1648f904,ac7ad275,9e6260c9,27870292,278fe521,b718bc4b) +,S(d2bd24d9,7dd4a345,a8c4e857,97a9c30c,e8e7e7c4,3ce79bb8,fba4250e,a0c8c5cb,330f0cb8,15cb1931,90331055,809eb028,55ae895,ee1a30f7,665166b9,6494b9e) +,S(d4e4a3a2,90deea18,fb15457d,f3ab9815,5d722589,515a38b8,dc303307,5dd7e9f4,4854493e,77d0ecb1,742bdc9b,a5219e9a,71c57a6c,b914b8a5,6eec21dd,3e7d1c21) +,S(f0fb6cf4,810de399,fcd1b246,f0e76e2b,d318f82e,1601ebea,2779be6d,47236d19,e3ad5160,66480239,88b0b197,3eb2a41e,2cc9b4d3,7cf58d07,ad0edde7,ac595daf) +,S(dd4ec58e,3f4c470c,36991740,d02b0fc7,9a4f9df1,98647ab0,ebf73b10,34858939,3a6ecc86,a3b058ce,f278c839,541e92dc,737a6390,94ffa464,17e37da0,40a3bfe3) +,S(ec56910,1d7ed954,d4efe1b6,c4f6ade1,2af12f3a,ddc75a8b,8fdaebb7,ab904e51,3497c67f,5d594f71,4327111a,e22f2621,be7cbaad,b2cc1d6d,be0b7e50,d450c54a) +,S(ca45def8,51856a5,7ca5e1aa,64edc3cb,e25ef6b0,e28abda5,a85d9a19,dbaa35c5,db8b2357,268db9eb,e013fcde,111372c2,6eb4ed69,5a7a3df0,4d1d33db,703521b2) +,S(b44c5388,964521e2,4803c8b6,3dc8a494,40f8ba15,435ce2e6,5f6dc092,15b49a47,2e16435d,f30c4b80,cf125e04,b85166b9,509890a9,c1daf1f5,72462a83,de198d00) +,S(95b735e2,35862f14,e9291a5,4b46d977,33a20ce1,b59b4603,666cfeca,e05dea26,e1281705,5f38be19,9bc9977,b6e80c08,f22ab5b1,286ea553,21145675,264aabb9) +,S(83d9d867,8fbb0263,6ce8d231,3ee4716c,a15c71b2,66c1a7ce,e56928ed,73e5bdfc,e0494c2f,be92f132,effe7d78,499f4a00,c35027c2,37df8545,9918687b,6b949246) +,S(98b7c85,a04c546d,bbf6bad1,6914e247,1d3b478c,f30dd6f0,2e843770,49bbae03,769c3b95,3733dfd9,d9027da4,a10b0a65,ce4106f9,44364972,e569c63d,3b2ace57) +,S(3ce7b677,852d34ec,34358a8f,4da66d53,75019cb1,19a45dfe,864062a,a3b12e45,68d2790f,1838b86,96272c4b,d5e418a9,2cf03d9,de44f218,e42562dd,467e409a) +,S(a0bc28f4,d4da7101,41d0dca7,51542b83,6b50bdc8,997b291,e074727e,cad7836f,6ed7e149,40f2781c,d685a581,99f163d2,177eecbc,ca785224,2a8b4859,97e47c88) +,S(9a571ba3,52b1d62b,4e5d290a,c0d41983,97750fc4,9a18cb68,18fe11ab,1e185c0b,1a91cccd,b2b1f58f,9cfc0dfe,d3464917,f6c2870,9bdd8128,ff61e295,ad1b4ad1) +,S(fba0544f,cdd2de73,d24eca5,55a8c4b0,4e135c1d,8348cb93,d53f647e,2a21b9bf,7a11a425,a4ec6acf,2eafe631,ff83be6a,f71a6cc9,f7dbe3f7,aed57e04,2774568a) +,S(d96b253a,1edbf662,3902bdd7,1c3bad9,ecad1115,45614b48,5f7abca1,d4920a9b,2bc88998,f951b831,2f27d09f,2f331536,1499f1c8,b172f1f2,408d0660,3b18c2e8) +,S(2b3c17ce,1081b825,94ae14da,f9723f9,9823382e,947c1356,13a4b8a6,42267c2b,a4cd589f,2ebd245b,88c7f1fa,427a2ab6,e77205c2,dbbcc74f,dbbdfa69,336048ff) +,S(69698780,fdac522c,69313ea7,2e244d9d,3f6bc5c9,4fd86125,2eb56369,c96439f9,e8eeb3c6,460f475f,c06b5943,9bb9994d,8d6e4314,fe20020e,c1001f77,25a03154) +,S(1e344611,4cb4dc4c,963e172c,9bfc2f4a,d1577efc,ae2ffc6d,76395ef9,a0848c68,debbb8df,e6f59a0e,b96d464,3bbc6d51,a2bb4621,6059de1d,a473436c,404f5b19) +,S(6ccfae02,4c7f2a9a,ba12c191,bd35e287,d5c30284,c4273a1f,c558ae8d,286aa61a,c2014174,c78a2fd,d342ade2,51e4588d,94c4cdf1,ee83ade8,54e48388,b6e7cb11) +,S(5b5fd487,69b6a133,35bf0a2e,61a2128a,db43153f,9497c77e,c32b16e2,ca4c728e,9954353f,b23c9d44,eadf2632,168711b5,5a54c9da,53c41088,50fe77cd,8cf00c77) +,S(6b74a257,27af44dd,61fbbbd2,7323b63f,e6a41d9d,f5412215,65a6a9bc,35d069e,a4e1214,f17b8dff,b5bd689c,5f004c98,39b87a46,15245756,48099d33,f432a34c) +,S(5171a187,8ae9ed01,837408b9,fe08f317,8688606c,aae04b2,63cbf143,b8e372a2,b5e7de6b,964153cc,fe64a472,8a8f20a4,44da8e15,c593b7e8,68140276,b172d06) +,S(df9e86a,4337ef32,3518568c,e52958ab,dbce8a92,840b756c,279e96cd,43c8e80d,5442baee,c72f460a,110e8ff6,1e938132,50b2ccdf,61941df,5f591bb4,73027da7) +,S(ce070279,eb6f1f71,af876ab7,3903fbd0,ac9e4693,7b6d3307,2ec80182,3887c850,de83ddf9,88f9d345,e1ff2a79,e91f8ad9,6ea23097,538dde84,aea0ded4,b6a155eb) +,S(a0255c62,44afc84c,58238936,c9d08d36,96e5789b,ed28c40,7667f348,4c2f35fd,93e2d407,aef898d7,c898293,fb20f222,6cd5017,cd62669f,17ee0d16,773c6f7a) +,S(ae82dead,b76d5dd7,140e1197,a383ff62,138e1840,dc2f7ef0,d5126dca,86f0d86b,239dbbac,caaa0779,a9f6d8ec,2e30b590,72d36b58,199a27c6,5f5815ca,5720964a) +,S(cf0999ce,bee65e2e,d596441e,a8942461,97c597ac,9c090884,44c375d9,16332393,6620cbd2,f074edd5,1ff8cd5,45adcdbd,1c664ad2,6935880,8655ffb3,3a748ca4) +,S(5fb4af74,6404b01b,63bff1b4,1bacbb36,9ec06801,9454622a,76258033,9cd3965c,464cb2e1,2cce697c,d409529f,a48e7448,916a51b4,5954fe93,d99ce392,272abff5) +,S(15af3c0a,46b5232e,55e5687,a2bc4acc,2c7a350f,8114e3a0,1993a0f3,1b3216b4,ae03739b,bdce38b0,4735acd9,3a3ff7ba,86c8189e,56a5bb37,ad83a574,cdd45a83) +,S(5d0035d1,d008ccc5,42cb8b5d,32d973da,32c88787,640d0465,76d33e36,540b4b10,448ed8e5,bffa9f28,6689061f,73a74f17,76db8a8c,d5b4af39,6173e7be,6e54c465) +,S(944c70f3,9cf749df,edb9d322,ffc69e3a,a40b0704,fc4af915,e027bad4,83c95527,b0ac0c3b,bf489332,64a6e95c,e49a669a,df10acfd,48e22fc2,1a18ecbd,b86c6f0c) +,S(fa5219f0,82ec3c4a,3f246754,dad62f04,52e26225,6cd28acc,846bc5d6,151e4402,32ce81e9,d6403132,d7b64f26,a0289031,eb730d2b,dcd0065a,1b4e3f3,a7afae9) +,S(c9f4aa30,ddbee263,509455a,858b8518,85c6a16b,5d9bf032,70443928,38a30ede,5ef717c6,b438af31,9a892799,46b73ba7,add20334,c2fa5bae,a15b7632,34ad7b7c) +,S(2f4cf3ca,7454d569,5b0dd2fd,c72d4ffe,529b362,deeea120,162c178e,7b770319,dcda920e,3566ebcb,f47c1ce0,fb767092,fe7c87c5,ff146042,d5a0ceb7,a2f7d487) +,S(2417f576,2e0cd74a,f07a41a7,51f91b3e,a1cb0a42,2246af4,a0a96b8,79969945,6a96d755,f8dc71bc,adad0a3f,61cb44ed,741ef96d,96baacee,fcfba7ff,1e7807d9) +,S(9ab22f84,305a1c1f,675ca5ba,cbb3dba4,d43d060a,ee9148c6,6cfb61b8,5888852c,af0506fa,a2588e3a,7aeac12a,7acdda3b,b51d996b,2cca9c18,e23b517a,a428a03e) +,S(f7421ea2,24627926,28ed878,7e154724,77a91726,5a162f52,87501d1d,24a72b6c,db0ae665,e4484132,f372ca6d,8cd46115,fe1c72cc,885804dd,8f508900,5743fcf6) +,S(35756af3,548d57e8,dc0bb791,8b9875b1,10a912bb,7d971e35,4963bd7f,df5c6f75,1ca46dba,78a2b4fd,d4ed69ae,bd4e1961,86d94b8e,20b1660f,c517098d,e077a1d2) +,S(a7e9f00c,494cfaf2,16449d9a,b0dae7ff,de4041c2,fe031bc0,241c44db,1bf01837,f2aeb9ae,480b406a,9753d009,3b5fbb74,5a7760de,4ef508b7,d10804c0,161a0280) +,S(69451dba,92872879,7737755b,7655ce70,64838aa2,9084fc1a,c88f5e6f,ca62adb7,26218fa8,d40df091,31d845d4,96c7b950,1071537b,51a3143c,c73f1a7,b02b1cd9) +,S(c49c3b30,15cca66f,ce986a37,588205fc,89a92d0f,c520ad3,5eb6cce3,daabf06d,7afde84a,665b02b0,e0cf9ca9,9d2ad097,6242919c,53e31b5e,6216d67c,580e354c) +,S(e9e181bf,b68e6e38,c5b6ee1d,f11c14f8,90c0c744,8570387,1ab6b624,6b9dae8f,b0bcfedb,3f394317,907e260,c3651aa4,d1af5ca5,e1a34018,fc6c26a2,d5ef5e1b) +,S(a428586f,adf41cf,5b5c9e52,e0d6e1f9,2e7d8738,ab99e8bc,4062e302,de392324,77c3e18b,fe71c937,65b911e8,b49cdee9,a2a7413f,4de831c8,3f967f08,95b8d142) +,S(f5c92ed2,de1d1a25,4b4a4830,b656a674,38077be7,5efed94d,785dd0d9,72712cf,7ee98d3a,497849cd,5b5f6dcb,c51c5119,228fdf7e,1a67f1e,d7c670b9,eb3958fb) +,S(a85707ca,6c85f506,311b6930,767b5571,e18c19c7,ff2574bd,f268f018,b49c3f66,1785fe71,cde2e189,aaf67d10,337b8fb8,53654335,4d5ba73d,77518d9f,5e8aa050) +,S(8866f5f,a4c8d1c2,f2de0cf8,19eca5c4,d084ab60,67a7d97c,f9942fe,c0a7468d,3014226c,3f33907a,2f6c49ad,c762be6e,2915288b,a7ec009a,fe693048,3588336e) +,S(425f6d73,37eb2d68,e17d07c8,bda406cf,92af22b5,6461bd66,f40be14c,ae0688cc,e865526f,7c42e391,1ab73290,e80bd9fc,9643659,5871bdbd,ef8d698f,d04d4980) +,S(f70fc3e,1de3b3fd,55418441,43deb4be,36bfe6af,3bc9d9a2,f8ac823d,68323139,8b47994b,1febb309,e3774a82,d6b18d78,8ae5750b,40aa944a,dae404b5,da24ce03) +,S(1be960f3,38d2a16,703b10c,eafa121c,d59c66d9,439e9067,58a0b363,2a83116c,7fa4ca2f,8218ee04,351549cc,3b0100fb,647e0db,19eea010,62f1c0de,3de29aef) +,S(1f2a2d3,bc5c80a3,8af51e9f,5694a80e,e915eb89,4831ea7e,48472d53,320cf385,a18cea39,174b4c49,f9bcb945,5e6701f9,47053f07,6782ce9f,f55653be,6d3940e5) +,S(61d6b60,70846b0b,b7790861,17cafaa9,9aa67c37,fe77140e,88477f3e,44d04766,c7f4be90,86849ae1,632eb59a,6c96de66,b7c4b1ea,caf724d2,5338c11c,a8f2688f) +,S(2b37093,2d9a6451,79508daf,4be7f26,6d540538,9276fc4f,e6ccf0d4,1ee6cc9c,c002a3f4,2c8998b1,ee16a1d8,616ce0b7,91cdfc44,61839d9f,5ca0090e,295beca7) +,S(48f5c3ef,aba8b7a3,4d7fd136,fe9b01f,64af0e95,6a27663a,87cd6629,f2708858,8cc38cb7,fce46724,7c2f09cc,52f973be,90aefdbf,b52895e2,b88f03e8,fe32b5ab) +,S(e609b597,2e5ec227,6f5cfc87,67b6d534,adfbe9e9,9e60b86,e981dd9b,3e62cfa0,6be9c1ee,ff205fd3,e3b85e3d,bbf1b27a,335ca616,9ae189f,a433e65,f2b43081) +,S(ebe40b1a,5b2c9cc8,46eb8ea8,1ef0c1ce,f19a767b,2da6ab0b,2ab66a01,b16d205a,2f7710e8,e4fecbd1,39899bd,1964f5da,967d6920,d70b2f4,45d6f914,97c67058) +,S(97cd4c39,9391b1ef,653d32f3,9b062bcf,dbc72d45,83d81b3f,f70df659,358e3abd,a05a6184,a175b6de,f9bc35c5,6e9f5cae,263acf2b,a233bfe8,13dd98a7,fbf37968) +,S(826928aa,1d3fd0e8,53cd99fc,e6559ae7,257343aa,378100e,bf64a3c1,bea7e6d9,bc4a2db1,a3bde90a,ff50fddf,cf6f844a,faad7f8a,89bb399b,20b07b2,2e1efc79) +,S(3e754097,f3ac95dd,176e9181,410bae7a,83bd1431,6772e398,967ed39a,5fa19121,746cce99,c6a2cff8,a0907ea1,5cf9436c,ca7f94,7f3f1801,135fe675,a518df32) +,S(ce539464,b49b9947,808a236c,8a027066,82991f46,3bb828fa,44653266,329365db,3578fec2,b8bc4763,61f2fda,29c2c38b,a7bda79c,11b2cd41,a74d4c02,762e5dd9) +,S(c933d9d9,cda615ad,7ac41d6a,e64a77cb,44a2b669,21533669,2c90c3af,53e349dd,4a6da24f,83baebbb,8f2a8ede,298babce,a25b054a,a1c4cc11,f9f0b0db,572c610) +,S(459aa43b,1aa9815f,e9116f2e,78ddddef,a78e2def,28027d51,d081a3bc,800b2ac4,1acedbc3,de995e30,b16900d4,cddcd1f2,5c615156,fc29645,4bd5fc7c,63e993d5) +,S(5f2aa5d9,9862ad29,eb467564,639abfc,ff445334,9a30c4d4,3b27f913,1e3b0a5e,571eca2b,e61d0bf2,d9def9ba,445b39b2,5acfbbb7,dd4e2cfb,ce7541c9,61ace144) +,S(ec66ea1d,e6cc5d59,8c393e68,1a338549,fc2c3d7f,947b40f5,75681e60,88f4fd1d,d711ffdb,3b378435,5894dfa9,d0a5497a,5540739f,a3706597,8ab7dbea,a9280f52) +,S(6ac53df5,aa097f2d,9983f018,bd1cab39,69ad0638,c78547ab,1a481682,ad6105fe,95f69a81,5127b919,9488f224,24307969,3cd51796,bba08578,abfc8e05,6618a598) +,S(a8a3e9e2,9e4ad0fd,c466dcb8,fdc02cf4,16fb5c85,b3454fd,3842f129,9f96c0b0,df86eb3e,3006dea5,ed6d152,f394d7c3,169b8d09,bde3685,ce138a55,6716dc2b) +,S(50a764d2,a0d8741b,95ec84a2,feda6df8,1dbe987c,31861166,32c40c3d,e6db251c,b28203f0,233917c1,4b67632a,ce9b512d,76a8c7e2,430a4a40,85cfb422,7fe41055) +,S(c2c314b1,5ef9df1d,26856c9f,a6e39cf9,1bd9c584,582893a9,423746ca,1ab51ef9,a27f9286,ff7fdfaa,b949a9f5,d59f08b7,a22f1e31,e7175d44,93e219a4,e92ab759) +,S(ba655585,da44c8d1,2b3ce375,e220a4c3,64868b75,370fdb75,dbf6bb92,76a9d3df,3d64e16b,a03a385b,def18f61,432cc49c,bdd9d04e,6fc8873b,e51663bb,ca4fda50) +,S(3d45fa49,d4d100a4,b5a76a65,1a5100e,56bd0d13,b9e141c2,aca5cfef,a46e600e,92953c,38eeee42,e1da93b1,3c75ba48,da30390e,731801ec,ed5bdc2b,58059176) +,S(e464c346,e40dc3e2,40e5d96e,1fb2c10c,83e5ab7f,45138596,4ca46b04,bc8c3a8c,63f14b46,182950bd,282205aa,374cd67b,ca364a4,f57cf90f,c7ac90d6,5204cf4e) +,S(14a7e12f,e235b307,5b0d218,bd3dc720,d56f3fe4,c73841e3,fdf42dd5,aea42688,d79683cc,40cef482,ed6a22d4,8137a4f0,eac31249,9ae13809,9eebf3a3,15e9ff2a) +,S(8e7afbb8,21af73b2,25b34ffa,bb2ff6f4,61d2ee2b,85d5d936,a99fb078,963828be,40e4776,5aa0768c,f055bea0,e20e1062,bb528470,dee61ec1,6cc9ce7d,ae6b1853) +,S(9d641162,3227bd0f,4bcf9f1f,9e5ce829,7d844055,8a7ec246,5b160690,53297573,ed0d83a0,68001be5,70067ea4,70fec09e,1f8630d5,da5d1b2c,126c35c3,9b07fc8d) +,S(6758c6d6,d9e49d9f,4125b52b,e3da74e7,52bb40f1,44af0f0b,7bd80589,7fc83eaf,749ae08e,c2aa2c36,3a229b3d,e9577dbc,5f7d22cc,d46826e3,6a1cb434,184fd292) +,S(b3e11a0,45702d8b,14d93df6,3a4c11f5,32632719,b4a33fb8,da11514,bc61d73a,c6e152b1,2b267ec2,1a23892,8cb97044,9660c6b6,34b22d18,9a86fbd9,a9d562eb) +,S(50a325ea,3b309911,1371b0f,e0b9e276,137bf43c,22108b98,994ed5a0,d66fa5f1,946dad1f,d39e0aa5,8a758527,9b39efea,8a99399c,2750739,dfeae140,3745b70e) +,S(f7c8f249,3befd5a2,cf6802ce,8ac508ee,a97341a6,a46efe39,1214e322,e95e9661,3c072cc4,eec1f370,144b7ec0,598e4d96,573883e7,fa3f3f5d,4e33d88d,1e34cb67) +,S(302d3b50,c1543386,f0b0b94e,9c49ea68,38c7e7bb,86c28cc3,c8965dc8,38f85b6,988047d0,d891aa2f,60b66e5a,96c78c91,3588d128,d4e7f720,5672d25a,88310744) +,S(cb8ee728,2e7e5ba6,d0e8ecb,55ede89a,1d3810fc,b0d8f86a,fbf53b7d,219f0eb7,4b04ea2b,efad1099,8c9a4e3,cf0491c2,bc10bd60,e06a118e,75fe0163,ab8094cd) +,S(645caa2b,4a0cd8aa,20cf6f04,ac73a49d,74f92035,f90bd31b,211572d4,cfd5fd42,793f2996,35f9d7c1,de62b64e,afe516db,b0aa1323,7f51d0a6,b6fc8346,fb7ef5b9) +,S(4d37342f,6148b5e7,5434b8df,6617d64c,f00b1c73,3d85f4f3,e69c4a27,395f5a21,6400092b,d4c5c705,464a7db2,b1b6b667,4eebd7dd,38d05cde,d1577d8a,53a708bd) +,S(e634bf17,24e07b2,b4ac6285,ed28bd39,747a2134,36e76d3d,bd693170,18c0e0d8,cccbcade,4b82a51e,c31d9d8b,6b113876,5e9660fb,e897ca53,20db873,13e9f021) +,S(80a00de6,2767c513,d5bff535,431d259a,64c49006,3bbf992b,a5111005,360cef38,7dceb589,e8d73366,c467e23e,8fdc7e03,b2620df7,9530b6b2,3fcdcdf,628ef572) +,S(606d50e6,d23d50ed,9a5b8409,f11d5c76,baf5b765,f719625d,5b2e2dd,9e8f8a0f,19f55492,45b1b4c2,fcc1d220,6c171200,4cbb41e8,bb7c8cc7,48c4366b,3fb32788) +,S(9c8a0a14,f7d44fb7,4a7ea835,ae4a1932,266c3083,38d66a24,7fdd60ee,e4f0420,42c6c237,31578dd2,31eda621,81aedd29,8737fc7a,3b2be8b4,d0187459,e5d9169a) +,S(d6ccfcf7,81a46f58,ff9a0154,8a417924,1a32d3ca,43a8e59,cebc33db,1e35da1c,a1caba1a,ce846b8d,8b4824e6,1570f832,87cbcfbc,910256d5,68bdf223,861d084f) +,S(8cfaf36a,3e4c3ff2,4556f446,4947c8d0,6eed5ded,a40ec0da,4596bad9,f5f2e3c4,991b6ecc,ede6cdfd,e33f81d1,c1067f22,6b72373b,96ca31fc,c80e0692,d8f3c4d7) +,S(d46c3d81,5b0f3a0b,bcef3973,8c6a6cc7,189849b0,4917fda0,541c08eb,b7e664a0,35524bb6,d053e5d7,c8adc9b6,3a84ed81,58cffe99,4041e642,1fece58a,d967c159) +,S(8ed7f0a5,2f1f8df7,f82dce33,e4637dcf,e2ce9b75,ca02ef04,90c23fec,2b7adfff,f02460c7,b418d10c,96c27225,3dee3b66,4be1aa5d,427759a5,5d90c223,628d99ff) +,S(433ecadc,a8a351bd,52ba4d6c,aeea3ff0,825c46b2,8abd2c2b,cea85f6,6daaa2ca,4d3b71bc,c2d7d177,45cd03c4,86f390e3,f6d396fc,83d0438f,983a142f,162977e5) +,S(c0771ef3,10377f83,7a9bbddd,81f6642b,ba04493b,15c054eb,249b3f9c,bba006e6,3e53ddab,1d816a02,7898c6b4,778d4c48,e1ea2b33,a63955b0,784df258,ef4ccb1e) +,S(dc2e0226,4a6a87b9,53997383,e35c6cd8,92072ba8,a741fd23,8ff3a9c5,f597ad7,898ee746,ecd5f723,440ac0ff,6f597e85,8ddbacc9,cbc80e01,d8a3c1f1,7c12c74d) +,S(16bace34,59ad8cbf,1fc5f781,19cd9e31,d043d7b2,ed839f03,11f0b760,6adab771,f64532,6ec697e3,2ca23979,72b2b834,66e835b1,2bd49243,e896c1df,c02fb1c4) +,S(afb94d8f,3a271439,146539a8,68f00346,9af55eda,839c81a4,725f3a5,7d69c736,afdb294e,a455031d,a70a4eff,7563d764,bdf37fa7,5594c9cb,386e8bd1,a35201d8) +,S(bb31d236,4a98f7f4,1edb0ae7,6018e5ea,ecc33780,5f781dc7,6b15aa9,d0e89b74,1a064c03,1f47cc7e,cd49f032,f4dcd95e,87cbc28a,7b9d9c27,a92a4c1f,da031a9a) +,S(f73b1b6c,4bbf5da8,16604ee3,ef6e83b,241e486e,d727e700,1c3c115,3c4318b8,9a28ad6e,9d64cba,b58ac225,5c01b261,25ba08ef,3b7d5ffb,476f9b8a,6c8c9b17) +,S(63bd212,d0ddc5a0,84f8ea7b,69605b2b,708c8766,f0d3313,b236e921,91afa8a4,ee6f4f6,4824032,afbb0b3,a9fd342d,2fbca8e,ccecdb0f,a3307698,c56c3e67) +,S(6367d22b,36068022,30a416b7,8c689078,967b955d,875fd629,9e007f86,1abc6465,c3ee1f2d,14e6793f,17e444ad,5595f9b3,118d1647,9259e0bb,ec628897,f69417eb) +,S(64e2e543,fbb7f52e,57901e3a,448394c6,c922d0f,b632c4f,2df061a8,9485976e,641a602e,336a68b4,fc447e5a,5863a4e6,89511942,4fce011b,48f937df,fcab2a4c) +,S(2e290afd,18c835c8,d1d79e86,9d9c1322,e16e824a,60e67f38,d6bf3031,e3fee1c6,7f85628e,d2fa6b67,7686b0e,c29a3626,da18ccac,5e373874,96266458,79390f21) +,S(859a208a,c475d3d,f5f22907,988c7774,c0ba2dcc,d80ba42b,17ef1087,2cee401e,a667c645,778e743a,d62ed1e3,cfa12fcf,84b982a5,f1349e31,33627ffa,8b19f5d8) +,S(c9291908,ccc50aae,f19c0b86,829fdc63,b608d3db,d92b0939,b92ac02a,2bcaf21,e5c52bb7,47111f58,c71352c8,b2fc89fb,20cb7d45,77c6231a,17d8d01b,6cd2599) +,S(50359490,42b5b620,78ab0141,1f1a1d4b,64bc42c7,58da4459,b04d41c4,61cefb26,f61e823d,b68c064f,f80d9bd8,f272f817,347c132a,f7bd2af1,e5b451ea,bf4f04e0) +,S(f9bcfae7,d6a3f1fb,612787c3,53b535ec,39f29a01,9f45f43e,7b61f2de,c4a4cbf3,f4d86e6f,273ad7e7,902d5a99,50fc846a,a6565862,aeddc67b,28fc07a1,232ff883) +,S(1c8fe8b,901cf050,e93c0004,b0764715,602ac4e9,7f8d5dba,d7747039,f14a9152,b25ddb34,7c452942,92143b6e,cb19f87f,b42a0f27,f6f74c4c,99d7b80c,7d36769c) +,S(3c50be1,4cbe3a87,d363947c,6eb534e0,8ff676cf,412bc4dd,2c543131,e786f552,4434471c,6e03f600,d8a27775,4466cad0,194f553d,bb653238,8a0ee4a3,9b05a74d) +,S(64cd71b2,555472f8,fa968b4b,4f8c8b24,5b3e91ad,6d9dd409,fe72702e,e70522f5,f1b1cb3a,e9fa4855,2d7fc2a,819bd629,40350a3c,168782af,5bff16d8,89ede304) +,S(c1c00444,c584c346,d1bdded8,7cc40b0e,fd8f69e7,d2ec8bcc,c41e3414,619af7a8,d631b494,3e3febb0,87bb391a,d5810b61,f78dbccc,bc6de38e,ee09005d,3d879436) +,S(fa509582,dad5bb61,c7450fd0,3b7e4ba0,2fbd0c28,af971b96,cbef5956,3fa2a7ed,267c2764,617c2f78,9ab890d7,56be2195,2acdff5b,3490b816,165e2b36,2f3df4e5) +,S(655dcee6,864e926a,a6e05f04,b604c2da,35175e0c,f7d264d0,50920d46,c1a2386c,da2eaeda,bf062cb4,ba2d36c4,67965baa,5f1dc238,b5e22df7,2bbe14f2,cfee19c8) +,S(6ab5e1ac,1b7be624,97c8fbe,fec5c4d4,2cc1de06,d15a1beb,7606fc0a,df3c213e,26f983b2,4eb319c,7443e024,7fecdf58,f833928d,4e809d3c,f36231e4,d0a9e466) +,S(c7da869e,39caa2d,e9f73a0d,3f73ab73,6224e2a5,47ef5af3,5f5a9d51,60c25f3b,e99b1102,49a607d8,12af4cf6,76ae4c4a,cfdc0f89,9ec2175d,c7942b6b,40da579f) +,S(2a6d4ca1,5f1c3ced,ed68091f,af4d149b,e20b6854,bf015084,a9cc0d21,4944ef99,dfc27688,ae9295e9,ab8d9284,6a703ac5,5cb44bf3,13f179b4,ced6b638,e661ae0) +,S(447260b3,bba7224c,aa659f70,b1e7716a,92600a27,18129575,2de62cda,11c846c0,f17e3e0f,33ef2ede,8980c922,613947,8331e2a6,40260996,db8da1e0,dc8999bf) +,S(8cdb1cad,baba7003,8d4b0f44,1b312aed,5a8788a6,6e5356fd,5b994f6f,a7781969,b3a5bd0e,262e673a,ae7be06d,8d56c203,b88750e4,ba06cfa1,7c177edd,74536977) +,S(28be0f50,fb60f905,18323e6c,dcf46c0c,bc3e2c0f,175d8576,85126a85,d5caaefe,5cc058f7,fb188c4c,e1140429,568c3176,8d986fc3,e96409de,21bf9ba3,862b7320) +,S(b6dccba,dd2dfad4,8b69218a,d9cf839e,837e2262,7f8f0eb6,f23484a2,994673a0,e1c14ad7,297f1626,774aa2ab,5dcc730a,49623176,ae81eeaf,b52cfd4c,fa2de992) +,S(e45ebec8,8813f7e5,7a7ecec9,dc5fcda6,b9cbb0f9,19bae1d,be770955,e3766a87,3a62a0bb,4ccf3783,7c384885,7acbaa9f,f915cc7f,13a46856,3ff135eb,dfbb07a) +,S(de926f77,26f39d48,1e0b9e55,22b7c874,59bc9d07,df65f0eb,7b21fea,e8dccdbb,9d190ea9,263d4240,24b618ce,1239f6e1,115be2b1,d721aba0,f5f367f6,304d8e05) +,S(f7621384,20669c04,6d794807,569722b4,91e4272e,1305f091,b54a03f2,ecf2caf0,d0c7e8e5,a227f2a9,5c594528,4590caa3,3088c92b,b4c7cf6d,12d293d0,4b9e4914) +,S(4e795e86,44ffee08,f44d472e,8ba0eb9d,a5cdb1d1,619a437d,4a8bbfe8,565ce751,5292d8e5,478268f9,bb9b4be3,c751e587,65038c55,99cb3549,749e83e2,1590a4e0) +,S(70e64689,a944b03f,32b33a97,c8923531,cdf75e8d,e8577559,bf45d2d2,ddb50b3d,a675c6e5,460b89a4,a2dae263,c71e6edf,925a93bb,a916951a,5364d252,c225586) +,S(21637c2e,682b247e,cb8c1c,8b4bff1f,477ef390,3b9da0fa,b08b5cf2,5b67056b,e357165e,3e761a55,7e4c2b6c,727d8387,c5716b58,e24d0e04,8314979e,4e92d882) +,S(7691a399,8dad2e1,741a4135,18bdbb,d8ae5bb1,7466a949,a40560a3,9c1d1964,225f1404,b05b5bb6,cdbf2296,f76e7f27,257280e,28c68be4,d8c81c47,a422b74) +,S(748316a4,f3950849,9af5a14b,27eabf9,b0d11e91,d8752cb,69899af8,79af9302,cdcdd4d2,6bd709d8,c3d87c12,6dd8a2c4,f600fc1b,9e84461f,1793047b,5bfd7ee2) +,S(af588dab,3839e85c,6dc84aa6,c1e05294,a9708951,8006b390,c2ffe0ef,f6ed9b8a,a3d37640,1b06548f,2794131f,5e002aad,616ca135,8cf0c81d,87015416,37d9cff0) +,S(bd91dc60,31706ffb,19b61796,995728b0,5f869d07,6f0b6537,35c46e10,d9b42dc6,3c963ed1,5a74f5c6,1a28db8e,de070870,cdce2cf7,d6525a6d,16171076,44f153b2) +,S(e625c4a5,91460bbf,5c3aa583,c4cb1e1f,c202c82,7debb82d,985732ed,ede98f47,d026c5f9,a9035396,1de50534,8e4ac600,812a461,c913299d,7fddccc1,5970b72e) +,S(6bd227fe,d4b193af,8b94553c,718357e4,122cd057,d95980e8,ebc8fe5c,7fdaac1c,5f3fe540,a98b044,9ed835d3,b0a2b4e7,c66a0549,e4b7b9f0,22fd5249,c621a9f9) +,S(27707a25,895e6f38,523d3872,c5b34e7c,68a0f0f9,54c6ef49,5cff6664,1edb2609,ef6f4f8e,c893d697,69f34b38,f7f677e9,e5de5c91,48cdf35e,3a18022,3ea777ee) +,S(6052248e,3359414e,bb7770ea,4284d7c,c01770c0,e75466db,1f314e0f,cff0ebde,3d982058,5cf0d546,9fa62e02,aa556fb,1a1ac7d3,1cb9712c,8b118e2a,e3f7b7a2) +,S(f2cb5b53,4fa6124c,16d82506,8b229857,4d4fc666,9b45550e,6c75a114,f370f6bc,5cde5707,63a5cad2,13952def,43cc779f,8ec9dcbb,c5fac8a0,ad7a740b,7241112e) +,S(77ad8215,9fe6b9f9,e08030c7,a75c601f,4989b51,4ef6db20,aa9d0b23,e87a502e,9268a1ee,ffc24932,74856add,2ba1ebb3,c13678,99af932a,87fc680d,ea9b703d) +,S(4418748d,171cf80f,a7f80fd7,c2d7891b,5cc9aa0f,ba4ec1c8,a114cbc9,4a8fd29d,5130285,79f01b7f,72f18322,b279fe7a,9916c16a,a67c6aee,babbb418,fb1835c1) +,S(16af4960,6f89c87b,7e5901b4,d11a826d,126e59ff,df7d0883,1d37d7e6,367355e0,1a138440,537223c9,e8e131ca,8ea3b38c,12c8e8ea,50ee09f6,8ca4c2cd,4cb04c2e) +,S(22d2043c,ab4431b8,cf77ba0c,ad81848c,730a730,35277e4a,ae6392f9,de120f9d,d7795dad,5e5b46d0,9c36520a,c7fccf9f,4d71f9c9,8ed3d45a,432f4f6b,991b8acd) +,S(57e7a8b,34826533,7e203360,2f6244b6,41d92c8b,b2419734,9fbb3271,57676302,4d9238e8,ba4bc8b2,c3abd2eb,6bb2e545,5146afdd,3d1ce2a0,9752803b,4599b83a) +,S(45567f60,2755027e,282b39ed,5ddab1c,7ea19a82,4c0609da,9ab455b0,26acc359,db55ed5a,77a354ed,e4229161,141392dd,82c1ccf7,f726fd4c,2fbba48d,4134f5a7) +,S(968fb554,91059347,9dc1933c,a8453201,798a0be7,1a284c38,3e8fa5a0,f67fa80f,63586b1,6853f550,c36d8394,56718376,1c14101,dd03cbd0,b164cca1,24e8920e) +,S(f366c649,f633cd7,f1fef522,b78f7b85,3d640acd,11af2d17,14c14251,ad873107,5fc396af,38f4988d,82c4cfab,59d415ef,25b260d5,711ad546,e56fb65c,7482c6b9) +,S(aa0c4fd4,4a2e68b2,edc16eff,d2b3c29c,59b19289,1503e6e9,ad068743,fc201d06,614e3224,2fba7977,464b5c4d,b76270d0,2763aca2,54aa5b82,b5ddf8b7,ff3fee33) +,S(6c886cee,19b2cec6,91c57abb,da830e45,da4cbdcf,37484ad2,b4444948,8f2cc876,24cfcbbd,9ab4df87,87eb384,fec64e5a,a197e897,bcf2dcf5,def4b94e,afc16a1) +,S(4d600199,c33f76ee,c81d1686,f116d4e0,fbe5e4ca,df3bee43,9a81785b,c6095554,cb414cce,b769f70e,115d4321,fdfed743,2bf73dc4,9f1628e9,dad15f32,5fae614a) +,S(377dd0a0,196d59e0,e71e8472,4b65bb16,12c3caca,d2f2230a,a675a25a,5d0a4fc2,2c727e7d,8c8e27dc,a5ff7c27,b349752,1159c736,2b51f38f,e0343c74,acfeb560) +,S(9cc0cf8a,23afdf35,1bb1826f,9d900b8a,e8aa5019,8cf36552,9ca928a5,9c820f46,b572f15b,e2aac0a8,c8142c53,ea43a0a0,693e5446,5d5531f8,b068d347,f7e67256) +,S(d55dfaa6,41492b1e,981d154a,2be0441e,4cfb01ea,a51c40ea,d4a4d8ed,19fa3050,2e795dcb,408c74c2,a9898f31,523a1e72,da0c4c77,c2dd2eb,45fb2e51,8f238ac1) +,S(46bf63d7,2d113152,3075efb3,fac6c07e,e3d27e87,279724e8,46306dee,7d210fd5,771bb1f2,30fa3253,7e3b433a,2905c4a2,dc4a2e37,32e282fc,94cb2c88,33b0b199) +,S(4bcfe388,3e77c17,8c6f1826,f75e7c3b,594ef8b8,c1a9ba3a,13f00597,50ca3aad,b5562e93,9dcf4672,fa283cf5,6c05389c,ec5ea923,b47d09b6,2ddaaf54,8a1b0129) +,S(c9681af4,715e69ee,d7cce802,34bb2a49,6255ba39,1df76c57,df9747b8,d8c241db,c8b175cb,b03d5206,5f2bb846,81bdcb04,da530e81,31082e08,28c21f7e,cde038e6) +,S(9fa0a1b,56bd6ac7,f4a2671a,3b4f6ae9,bd5538bd,4c6b4101,790b16bc,c2fbc33c,27f9eabe,785c7cce,e4859959,348b4864,16056f68,6d445fb5,c08fd8b6,33129e63) +,S(15042df0,ca178aac,723e7b9,8677964f,a87203f2,bc0e1fea,a56ec6a4,73d0381e,445db657,dac86b0d,f8f9eb59,a143e75f,e271cbfd,2420b3a1,677ee3bd,8d0183f2) +,S(354f6a63,d4c1877,e0e7da3e,921db767,6b7f9dd0,eaf8b65,eb08b032,1f37e9b2,1000503b,d65b80be,eb72f480,a43efd65,2eb784d8,650bffea,5559aa2b,104ea8fc) +,S(19c36161,7a448fc0,49cd8864,d2707d6e,9e4ba74e,79ae3d8a,c11430c5,99de065c,2f9f9fd8,fd38ee81,94ef570c,1b7299ee,75742d18,1610a2fa,4dd06f30,989acff5) +,S(3739848a,d2ab8a66,b6a7a2d4,add9fa8f,5a50807b,56ce5d39,5bd4c146,7083f88e,6fd71820,611102bd,f4dd2056,92e188cd,68a5409,1036951e,6f58162c,65f0cbc9) +,S(9c22cb45,261c2336,7dce66f1,f84b84f5,2dd7e58f,8ac6dba9,50062eac,1c10aa35,ff84e3f5,8c25be72,910bead6,286dcb9f,605b0017,1996d46e,c8dbee64,52a1116) +,S(572145c8,7f311732,7ecc71e4,5e37c555,5a412785,7513fabb,d0fbdd49,6db4538e,163e63ac,5e3908da,2aed5166,c121548d,5338c6ac,4ea7550,242c7910,8a38871f) +,S(c9f07954,d8413eb5,8ef213b7,51a966c7,4c641ed9,8db8685b,d931e321,2de940ef,9e6844bc,804f3d3c,a065fe7c,b09bcecc,92cf5af3,ede9041d,792c184c,cde17fd0) +,S(ac297f46,b07c8a29,d664a24d,f05a8a51,f7b0e4aa,e8800c3,f9291a13,787f25e3,686e3b7f,78e45ad7,fd1bbca0,54a5d182,b1556c77,753c1858,75e98de8,29b66cbb) +,S(6c5913b,b383a688,390274df,121eda23,5ad55dbc,80bc198c,21897934,98b3e1b5,54b9b83a,e3649f4a,2f11ebf4,61e24a55,53e5b4b5,d0f78dc9,1c35aade,2cb9ccf1) +,S(b9926fa3,f503356b,eca59ee9,2163cbf9,846e3efd,c793db45,cf7ea8e5,af2495df,d6dd7c5,33d4e7a1,ed415c51,1fc15e05,54fd00c,100bf047,bb8f9054,42e2a512) +,S(4325d1d2,f2cabe77,fe0ac758,f6073057,cb36adcc,5357fd5a,31473ad8,74e69556,de437968,2ff6315f,8870562d,560cc48f,818c8fb5,c45ad932,ed5a7d4f,534904a9) +,S(a183e95f,fd84bfca,4afc9059,ce1c40f4,eed924dc,d1bfff11,d38a8c1,14164917,11c57610,c7ffca41,145046d,4cdf95ec,c38d695d,bef057f0,a0d01bb4,41d64b95) +,S(b340d0d9,ad6528f0,d9651414,f48fdbc2,ab7af5fd,3baa8091,84d5e881,6cd07114,85ed726a,9ecd70f7,f964dfbd,40e4b68e,ae2671a9,b6c3d384,919d040e,120dd4e1) +,S(b52daa88,35992fa4,1595f0a4,c9e075d0,b146fae3,6c0d640e,8e6f34c2,16719377,6088b139,9fa83889,a6cb5cba,d478c7a0,4f42f082,24b169df,85ec90e4,7a4ad7ad) +,S(90bbd15,42b0f88e,9f7f82bf,7c2b5fe5,f269ba36,4523a18d,5dc5709a,2ab9d8ad,c9df924a,51af344c,a71461d2,bd3743df,c6958490,caa68743,e06e7348,528fc4e5) +,S(f72ee5a8,1696194f,ed3d4603,f2c9312e,b31c54fb,438b1fd2,2788ea6a,a3aa8365,e2963d82,b6ca0f4b,8ffc1078,cd45a72d,e66f4cc,78567074,4fc1721,e50fde8a) +,S(108aeb71,5bc70525,42f5d6dd,dfd348f3,66a10f4d,d99f9298,c35a8397,e6b9fb53,8388b339,75a58e04,a0f6df47,23728986,97b8d970,ac6bd45b,49b9cf1c,3209f1ce) +,S(dc3daeed,87345ebf,824a5ff5,c7f46268,fe54c901,493e2e86,e2667fb6,ff61d5b0,b5a7e641,238e0467,196c1683,bab47f66,cf001079,932a5d56,da20f89d,d93411e5) +,S(3f96a83c,2be16120,e81e1d26,e3853ef3,a82c5f32,f8d6bef9,38179c06,4265c589,eb8d370e,797cf20f,d5a0d734,9fbd027d,630ba1fa,bafffa0f,2ea37131,6b5c64b3) +,S(27caf86c,e1f423e3,1cca2278,244fbe3,b358357f,af1b7866,1de13b96,eff5c4f3,e8f77718,647acfb4,633ddbc2,3dbdca0f,2187e24a,2f2d45a1,39bfa1b8,a2b30f61) +,S(6681cfb9,e2418786,1887f964,84819645,2d301a1c,7c082361,67a24c62,f74d04f,9c89eb3a,7849e944,2f5f521c,c08d11ef,f1ce4869,98d4d760,8dcb4a37,b815151d) +,S(6711b849,48664639,67274709,2fbbafa2,fcac45dc,1795e80e,67fb0e65,beba8da5,ca12c83f,1c8f5ed9,222d8cec,bbcaa51d,6b986a5f,60d2c118,b812b259,294cfcc6) +,S(5ee9cfd0,b1102cdb,a0fd22b0,569875c5,fdd73467,22769011,8b7843e2,b71dd94d,1dd7a4e5,bd7f9ee4,e17c518f,19b60332,60d2294,8b5f463a,ed9021de,87d7d86) +,S(586dad7b,13390be6,f3d95a46,6d0224de,d7538f1d,2d2d9785,e38a3f2a,edf4924c,c777eda5,a68a3582,1735bd33,7119e58c,bc2fb101,c995f986,7bed9786,cc2b64d0) +,S(5a752d2,fa012c7,1924b6e6,90ddc7e8,867f6ee0,ad8eed91,84f7250d,665091ec,57f2232b,32b36429,f15029fc,5789042f,ec1ace97,5f2cad27,d53ae819,47b95db6) +,S(f93cbeff,22bc577e,a9cfef22,378eb142,426372e0,7be72e60,2a8064,a53e638e,abc772bb,401bdb42,94b16670,2279e4f3,eeadb75d,57190a59,96f34237,83afe50f) +,S(8c272c9e,96130972,2efc34c3,6a6e63ac,76052cfb,856eb8cc,3dd014e9,d2608a9,9987d168,13c57162,3ba7d226,18b54003,731e3716,3495edfd,e85e927e,6e4607b3) +,S(bf78a0d6,1519542b,a54226c4,d386ddc0,38dfd84c,9f9555e2,3e187c11,4d769bc5,6094e1e4,d84ec081,e6014b59,d42959ef,e160587d,315bcfe5,5d26ec98,b1910280) +,S(5b264b76,cd092a1b,ffbab651,f42485fd,497aaa5a,e311eb28,ee1fdb99,32e65caa,944f9359,4b68d7c3,6610fdc8,85695b06,1dd84722,d5d88c84,c8c188e0,69d70d78) +,S(a2473cea,8075a0d1,33bc1459,8d147705,322f7151,274e797,4b7e042a,c082b559,64de830d,b6557b06,d9b09d79,4f6c905b,5168d32a,708484b5,d2c68a89,a2575a18) +,S(3e18d5e6,3dd0c514,4341535d,a7087bd5,f14b0708,a292350,6528a803,609efa45,6e9c71b9,7d007382,f82fd041,be53470c,26dc2f16,26a9f0a6,e1d9ff67,54288c5b) +,S(d1acdbcd,b91e6315,bce96eb0,9c229086,6a767441,4285984b,71af6156,8294dfa5,5e3b0cd8,9872375c,3f807201,73e29d3c,accb67e9,66b99452,2c9e9f88,613da5a4) +,S(3aa999e2,9344e0f2,b5ecc880,a35ecf75,25ddb028,c40f64e1,a210aeb7,d850c90d,cd2ce25c,fbe3a1be,dd2e6fcb,e9e9a881,28c454e,c7ca1951,5673aabf,a543d42b) +,S(98a2361d,7c3815cb,ecd0aa4c,8a3a6ca3,3e1b31e2,5a4b6398,146d6709,61a5017d,1f33df78,3040c13,be821395,8d78e039,516df25d,4d9d0de6,d397128a,2c09f2bc) +,S(528b9d8c,907ab870,6d7a5562,84cb69cb,ff348247,be1b7cc4,61351674,53d98d0,f91eb836,d6665778,7c6d67bb,800a1141,29700d53,194c5051,aba7ead3,ddeafabd) +,S(f1adec8d,3d1b0d20,8bda6a86,6b8cf730,f4be50e6,2c8d993a,3556c1ce,625029b6,478324aa,c3bf6182,25bfb61b,32d47956,57e4a754,e08a978f,964741d4,1bcc7224) +,S(ab2ef133,b1b115ac,41870cf8,d75d4aa0,e0181ca,742a8f10,f74d50a7,3924a099,a2c47b74,c584eeab,57949ef5,38c0af69,2f2baace,70a730a5,75629ab0,566a91ea) +,S(9d4eb41a,f6da6f83,b1d20e2b,f9370868,539a617d,111b8c72,348c9b27,e0576f85,c1c7b00d,b786c84f,a8adfe90,4353305f,73acc3c7,1c2580d7,cc5d6b33,c5f30eea) +,S(30edd57f,7b1b2c81,c886dff0,5d9e9062,a553638f,9e4311d0,18a42229,3fb916d9,6bc4774b,16d257a1,81e20b48,5acfce30,f7449e64,aa288502,d44e7c4a,44842ee7) +,S(28e3eb9d,9a2b855,65c58fc6,a492014c,f10969a7,1e81066a,41d1c574,31306583,fc653b87,80734067,e5245c,8c150ae3,59ad3c15,df4a8e7d,dc93c8fc,dfbb6c2c) +,S(cb984735,e3c842d0,a2fbe578,ff3e8263,5a3a8bb6,749d8e08,c91571a6,9aac75a3,e935a0cc,7181c3f5,3013f977,ad714af4,4c3e257b,e9936268,b481429b,7bea8367) +,S(36186672,7e8307ea,eea83b10,6850dfcd,5e4d665b,20aa37a3,ed0051ab,a40fe170,ec626ed3,2ae6dc01,f5d4c710,ce2cb349,ab27cbf2,da5d924b,e32ef8d1,707a6d) +,S(b1a40d5e,69e9f365,c2accd54,dc8267bf,4260abb2,3b503136,2183b17,4c6b7d2e,76619dab,2a7b41f5,2401ff89,4ef632eb,31eddea8,bae27c3,af04730a,3dc186ec) +,S(abd488b3,dff60a23,e83f0ccb,22620064,b32a8f35,d4c22be2,42eb1427,15660825,6eff80f2,17dae3af,96c7a463,d1848bab,fa994904,91a1105b,e1bf7f23,840b3a89) +,S(ac0cfb04,d91ceb48,2f497974,321bdb10,2a572115,73fe4598,c92510ca,4dd6aa22,6b5f6d87,4a90d0a9,658ae4a,64474d9a,3950314a,4662ade8,1c5e5605,274582d5) +,S(1fa8be0f,476c4b67,697c7541,dd1d8be,a3a0bff3,552948e6,87baf11,afc34432,c6b25084,8038461e,32961d20,181c7187,fe79df6b,8f5c5e54,96ddb5ab,80788d33) +,S(281b43c0,d3738989,a817d313,755c5f0f,f336a4d5,7e8d0ec9,b29fb425,c1f90e56,26b5070e,ba1059c9,b73e3db3,1227a6a7,68683044,7f823689,921a9bbc,bc9f4a9c) +,S(d64e69e9,54e81ec4,7446327f,55e5a82d,3b83eb46,2a92af12,f0b2694,c88f4052,aa7ed55f,284c3ca2,9be75565,f66ea54a,27a8f00d,afaff0d5,8f394797,3dc8622a) +,S(f4cdbaee,d4b01db1,f0b5b0c9,d6178235,483fb58c,5421ba54,17bbcd4f,e4e7b028,66d590eb,29d69904,deb9f48c,5a250089,b2b92447,8ea91302,1ddbab19,6c63b47) +,S(8ff94c4a,6c46e334,feca809,79d21a0,761fc1cd,1de76f4c,7243ff0a,93c9dca5,123ddc33,5f3c68e3,29f42d4f,187fdcc5,77207134,a8bb777c,2ab08dae,f149abf7) +,S(c7869b4,6e34186b,7e87c0e6,23b64fb0,efed70c4,f2af5e70,5c33ecd1,27cf267a,66295439,36620898,2f17d95d,7da2bbd7,29aefa4,69005a59,629720a7,5c11f48a) +,S(e0ea76c6,ad5dc87,bd26ba9e,25b5041b,9836f72,6eec0142,351b2431,6cce1a4,8bd724e6,632049ab,88aa620a,78922b65,6a3a4417,c8af7dbc,5a03d4b8,d4eff928) +,S(2fa63ac7,3e5f8982,36419f1f,4883f52c,e8e357d2,4b4aa7bf,c6aa7eb1,29e30159,57e1601c,d6323fa8,f5a5b314,6b5e5c44,7251db28,6f79d51,7d0b038e,b8d8c3fd) +,S(74ed4038,d4c50930,22e783fc,1bb7a671,49d00877,b202012f,6aa6d32d,c73026a1,5b74f70e,65d009e8,8cc618e4,9f7bfda6,58234f80,38e9835e,80e6fd42,6b28476f) +,S(71d6aca6,e38713dc,6ded1f93,4fdf20fa,16ce75dc,dc405148,eaefd2d8,f0025f97,b4314533,7d48dfc8,278c6c4f,b19f69d2,72ffd4d,a22dabae,dd3e60a,7067e302) +,S(28fb47f1,6e91bc9f,9a2d5720,f40297ae,ee617ec6,897044ac,8a174acc,5bed5292,d04b6961,d72472be,4fcdb605,9943c1b4,ef8e7aed,2ba7859e,bdb84a4,f8b498f7) +,S(68fcd5f5,c8655b5f,5d2fca79,59a7b32f,828c5a5d,e262f6f8,94270ee2,1f47a37a,57480650,33627166,c372bc4e,7a710275,7aa1ad4e,5460dfb6,d3d27f32,778d74ab) +,S(c7617ad2,f423abc4,7ed7f8a8,a7a6002d,17c9744d,1ac6be97,49110685,95aa36da,d9904b67,fba96cad,cbe7a9db,833c614a,69e330fc,a06ca2e8,d597f657,b7121d9d) +,S(a7ef4e9d,2396fe16,c0e78b95,5259a9d1,474fdc21,8b24e8c8,df566192,a6487cdd,d6782604,91c6be04,98c3f7e6,86f22f37,23c1e4dd,4f7655c1,f40509ef,5af67f28) +,S(f3be7846,7ff03676,4ace420f,4091bb2e,71856d18,9e24890,eab6b436,771e2578,f3027657,45cbf3e5,67a6966f,6b27ffc1,dc58f3a,6811f309,434662bb,636dbc5) +,S(5c947f95,5bd9ce29,5214c30,3ebf4f35,6c8d9e2a,4e173470,94d3f755,36f0259c,b6b7b775,3e7552a3,d2dfdf49,8578a24d,90a81d8d,68ef39c4,68fb5ca7,b75e76cc) +,S(236fd960,c3b1735e,279b8702,8c25a074,2c54c189,d1040c13,4e4cf40b,bd8b2c68,9ab59bf7,c0bac18d,d0d9360e,b79a76cd,b44bacbe,b425d181,873ac390,59bbc279) +,S(c92c9f26,17cce2f7,659d0243,8b9d8932,c1a9e9ff,ca8b0d99,cd7613b3,b1947b31,abdea94c,1a5e64eb,65e70769,51ace2b2,e4804ddf,1539d50b,725fe09b,52357a51) +,S(986087c9,142c608c,33783a12,4d22c316,733c2ae8,bc1fbd2,8bd1a142,6da94fc0,14925bf5,fd76b51d,6be82c65,4562cdf,8f5baddc,eb26c68d,3fa6113f,ab42a1cd) +,S(465a1f74,f4f6baad,7c644e21,f35650b6,22424e93,83b421bf,91478621,a48a1d43,26da71e4,179430bd,ee6698d5,7db01553,d41c147b,83247157,9f2fe923,c788cb48) +,S(dd7658cd,f28117c4,4ff3c88b,d0b46adb,c7aa2ed9,1590ca00,785cee6f,5c9796b4,32317c0d,1e10e997,a74935a,8e45d27f,9a9b8ac0,f77dcf37,c1c7b340,cfcc43be) +,S(55bd2abf,a803c49e,b4548b17,725f52fc,6222af8b,dd9aed09,715ceb87,57c72033,34ad33b3,9d7344b3,abfc603b,8dcf2a54,96b991b8,41564e35,53adfc85,3d3ebf43) +,S(77be5c06,d10c226c,8c9616d3,15b1e1c,44006876,4189fc36,db0c88a5,ec12fcde,ccf2cbca,5c474949,88f27ae1,b0361b9c,20e8b55e,d965f7f6,ec8fd9e0,429ba01b) +,S(fab72486,3eb6f261,f0460b19,bea7dbdd,80b890ee,d4bb5618,30db6af2,f52ab21c,914f03a,408a2e2e,aa9174ed,99d80e68,b67f6afa,fa9da5c1,5d22f74c,8876d0b0) +,S(232a2039,fafe8b86,50a671f8,de78314a,510db65d,5cf105a1,9db8082,2a0b2f66,14ca7082,510bc1e3,533b9f6e,e216ec13,3be3a6e2,64d2e218,d95f3ec2,ad78576a) +,S(b3482839,612a4ff7,a0584752,68f13e69,deee9fa2,894a8cfc,64e39054,1b7f58e7,722d02ed,df30025b,eb4e9fa,f7c58de1,653b8224,66b1193a,3f2fb71d,5d26ae44) +,S(d3873a57,b0a7587,cab10871,a9322712,cf6fd84a,226dd7b2,85823996,5a353210,3b7e4984,59c7e706,cdff95b1,c55e3351,f1f45d0c,c9fc11d6,60e5e27f,5160f186) +,S(8828788c,eeebb2b7,71d289a7,147c0a7a,26a049c3,26dcb281,cc9b2dba,162c20ad,e428a195,87425f22,3d798922,b4971c26,5da2fa2d,de4a364c,c8c65f48,c07b71a7) +,S(79fee531,26d0c00d,74986c18,993fa174,52f8dbb4,ea75680a,37f0b3f4,ea7e537d,fde50071,3143f994,b4d7b4a1,43ab3abd,525cebbe,93d5b5a4,560017a8,802cb5b6) +,S(f44f39f0,5a9f5f79,5f0f3a8c,e03ab50b,3672429c,bf699cba,ac1e58a8,66814e91,b95e45d1,e4de1300,950a27af,bab28bba,ff75a246,46ac70e3,fc09da30,d59a1dd4) +,S(1138e209,baf9c9b7,b89d7d78,832cf7be,da45afbf,3c2fa148,50426ec2,80aa9abb,15c28d6a,3965b2af,ea28520,7e67f2eb,ac7d5917,9672f88,3727e499,f06a0d6e) +,S(29d3e942,1b98d78c,60e4685a,aa503130,dcd3d3f1,5afc2620,472b83f9,34912c1e,90a514f8,49680969,215d46b6,db7c53b9,411c8c67,c88bace1,a6d45ca7,d2bf5825) +,S(a8a36a94,5fdd33e0,4f0c18d7,cd2d4709,d6efeb88,a53fa0c7,e90969c0,84ff3eb8,1dd7f905,edbfcd70,26aedbe,1cee1cae,23db1fcd,479f02a9,bb596af5,f56ab564) +,S(5a048ece,b3fa944f,90eab4f0,c3e7a06c,3c35f2ca,81263fd5,3670c212,6a981eff,124f1bae,b70fed65,d53caf7b,5e88077c,8e330d05,692199b2,4b3d6d4b,b7c886ff) +,S(32070282,c5ebc2f,665ee579,c645fac4,fd1eb4ec,d7297158,3dc81288,e932b8ba,8f69662d,d68407e,dafb11ee,8842707,c17eda9a,803cbd55,b86dadf2,85bdaa17) +,S(7c22865b,fb04d3f1,bb988a7b,80a49fe,c7810593,27a19786,db77a47e,57afe787,a02eb26e,30f599d7,d5599734,439f19cb,d0830b72,60e70d53,97f06e1,d036bd3b) +,S(a0ba4c13,30ad8252,c06c8269,a3dcc365,fd842e5a,aaf33a4,22de13cb,b9385cc,58ecab13,5cfdbbf7,f3b6d2ce,277b9e84,940fdb14,47568960,c13290e5,fe239bc8) +,S(42ae6042,c8a2ba5d,c8fe47a4,12e2c0cf,7be448,70e67819,4e31ec23,44f76309,72ad69ac,7ba38bf2,2210a6e7,53c64467,36ea3a23,cbc96c3c,e19f209b,dea1542e) +,S(8f3ae8b6,45507a2e,3ea5d82f,47020739,d8f35f0c,8fc11e7c,6e802d5b,87b2a19,61bb8e35,3028ed54,1b83420e,fb9b1473,60425927,d3f9b6a5,d1d2b80,ee5b4cfe) +,S(3a84ba4b,1e151920,a0e4fb4f,30953e6c,a7ba1e12,62ae44c0,d91b437,b05df32f,b20f6579,43525f14,7b23abe9,c90e1d0f,32c44eeb,cddc6ce7,d3cbad9e,7d3e20c0) +,S(6481fe6c,b04a503,3ecc102b,de462e7d,7b87d181,2bc145b6,2d25fd88,e7316210,bcc1b4ab,f5f32ad0,9176538b,808c9187,d4e88b0e,aef31075,b26e5c27,8f64560d) +,S(5340174,722c583f,af9cd365,1f73b33f,8e4bd1ca,cac0af4d,1ff13064,d551a094,e4902135,448a616d,e435180,f85d0a95,d9e4655b,aebf8a41,a3bbe221,1a66a8e) +,S(c0693eae,a5825c4d,6c24e08b,d57bc32d,6c9ee2a7,90dcb49b,b350f852,4e2867cc,da20fb4b,7627411e,5c6c601,bcb8fb55,f4f6178a,efe0ca04,62c85d86,33b9a05e) +,S(c51d0f73,72fd0502,d17f9200,ca189821,e63e581a,8356735c,a91b6e93,3cfa0d9d,8d10e6b2,7ed7edd,2cfff134,54eb979b,a2797a7d,3c6fb413,b3bad4c2,2e352655) +,S(16811856,4d2d99ae,2daf79f8,58b52ad3,3666008e,d9d2c59d,be3160df,bec3290c,2bebf1a1,45617848,e81dbac3,4522cd0b,3e9cc84a,3c08602d,1adf77ba,292e7460) +,S(7ae94933,e1438525,b2ce1622,5654aa6f,b15999a,57835a09,20a49748,7c0afeb5,91912e37,830a645a,138282a5,1e3331bd,a13b87b6,f443f901,95a2f4f5,bd9b95f5) +,S(e7792d97,d02b76ab,2414c5f6,c2d1bf0a,c27709b0,55b2c36b,179599a1,3019d4ac,9f4d9689,5fceb179,b98c75a0,d5d56cc1,dd274614,a2816e8d,16fb98e0,ed3f2b52) +,S(2ffb9d4,946fd63f,24cff64f,4ac79450,3d97d706,eea6c4f7,69d9e34d,3247bde1,66b1c74,34881531,2bd6907a,6e3608ea,cff18832,5576fa9f,f32257b7,eb81014f) +,S(4544ba32,f90ceeb1,1412aafa,b4ae7351,aeb6f4ba,12e00ea3,c165c8b1,ecbb2fbe,62fbd7b4,c2a5cee8,bfb9ede9,b02dbbfc,2bba538b,834797b3,948887cb,9f8f6736) +,S(250b01b9,25d9ffa2,75eb156d,d8c67b5e,5d981ce7,8824d37a,1b09e3e6,7918980f,ba1d924a,32f0da86,e532bcbe,6766259f,eb185161,4042c0ff,261eb81d,3e09c481) +,S(9cf30e54,1f2a5fdc,73046807,32e962ee,62e0b813,901e30f5,ecbe1fee,abeb4d2,7cb246cd,1281b77b,19601d14,baa8ff62,848259eb,6567c741,c6bd54a3,b5510723) +,S(edc9e8b1,3c61a1e0,8f93be12,495084ed,b5a90eca,4f11dc03,cd217b1a,56285a2c,66c2fd21,7e8fac7,4fd43b1b,58c80661,b0e8df85,a2fddfda,5e9f99fe,621e1f3d) +,S(dd94295f,8388a498,2aaa4164,927b81ba,cae9d002,b3ddb6b6,97082c70,f1e2c66e,838c060c,d409c1ea,9bcd9e8c,b1fac83f,3b08c2b2,482d8f4b,fd3ebc18,8d6706b) +#endif +#if WINDOW_G > 12 +,S(7e068e04,dfc0be48,e5c0d3b3,5bfc734e,96e96ddd,d0ac4876,92f74535,685ab7e,df2cd146,90d225c8,d04052e6,93f14bf,69351e08,79883646,2c88401e,4ec70d0a) +,S(73a9bfcf,d10aac9b,46e659f,76c439a0,b7c2a073,7dec217a,21f43f39,949a5052,73b91529,3ea7b052,682062c,8f86cbf,bf379df6,91a9cec2,4a1424a9,b3be10dd) +,S(efddff84,c8cc754,e6e34678,d85809bc,55cc224b,f69be05a,daa847ec,d408c55b,65dd8f41,8264aab2,efe6ed7e,c45b4ac,8aac218a,3b6713fc,1736dd6d,c2f188d5) +,S(46a94585,7118384d,8d23d6df,a4386dd9,dc264f5b,171a7585,7565688e,bfd73f4b,3de5af7e,a298c18f,4dbf16cb,38e7f617,1e684ee8,e8df2a6d,256c011a,65aaf35c) +,S(6890edb2,7ce3c265,d4afa17b,be160a94,45dd0c8c,9ad6d93b,910ea47f,3821cd3f,60f47c5b,10ef5494,cd8810a7,ca8802ab,7c24beea,eb67b852,dd22040f,77cbd1ab) +,S(8cc9eb6,340b886a,81ec4b32,87a251c1,e33e68c2,1f55688d,952b44b4,2a05f5ae,9b176b0d,90e38290,8911981a,3bc475b9,f0323870,150c9c95,5078bf02,307fb6eb) +,S(2b062f5a,da48de0b,58001cca,e96cde15,227875e0,26c94e0b,fd1fe2a,495b0475,537f1c68,3d5170c5,8097bad,4bf17276,da9de994,8eff6805,48b35390,2f1eab0d) +,S(dd55b91a,84b3d53f,4332ff7f,224e9231,cd358bdf,27f30082,52d7a8be,bb7240f9,e6f1a08,a40b6530,ed737609,4deef969,774d2173,a3ac158b,ff681908,192a5c94) +,S(ac5127e0,40b1a979,46c0cdb1,c782ee2c,8986fd80,d7151e75,16fc964a,f2c59c70,849e2249,323d0539,dda0a82e,1764593f,8afbb097,880af8e2,4765ea01,b7ae5db9) +,S(df2f2eda,f0b6ce9a,3784e9ce,1e1ddae7,9d136423,153174bf,f72212c1,b5311123,b9d98f8e,bd78bc20,316da64f,46644026,fde6ab44,162027be,dbac75f6,e86d46a5) +,S(fd83b387,d630c176,23b85e21,62113610,97bdc5f4,3fb08027,73ff93d7,a373e5f9,9a962a7d,375f35a5,9d7503d4,c0fddc9,115805bc,38438837,d3c670b6,ab6cd2ee) +,S(d8fbdfc1,155ec8ac,8bead443,ba3feaa8,d565ca1e,4206cc95,5feb8e1,32cac4df,f9453c0f,ccb99cb4,739258ed,febf7642,7fb1fcd6,1f963abc,9cb9a5ea,751f73ac) +,S(9fc9749a,20a3e8c0,1b309389,16aaf3d6,85f00872,b82350dd,c89110c0,dfbf86d6,a2f679a7,6c92ac59,5773bca7,8f0f18,75c85cba,e75a6379,1094a799,863e19df) +,S(29e0e346,71a91ce9,bdabcf25,2e2196bb,65b81092,d3728f62,b58c815f,2f476a3f,2f440c61,b82e0ae4,798a35fb,9c66b261,1ce5ad0a,ab42f9d4,174bd698,d4d0c0e6) +,S(e4068a15,398955a0,f27e6f92,9cc3dfb8,7fbee70b,a86ee78f,8736b519,ae102d7,a3f2f2ad,550f9cb6,aed99943,84a9a350,16b91143,747d4e7,7d69e698,8f165086) +,S(51b45e10,bb1bd989,8bb156c8,a4c81978,10f16364,d3d55092,48a42028,726c9f2c,e30719d0,4225de63,281ac479,70010cc0,8a1f1d51,48c57f6b,b478c95b,e3e141a2) +,S(14cd5d29,7b8ab6b5,e92ec2e6,6c63b3d,64dc492f,d5a0c5ac,f6fcbfa5,aaa0b83e,5bf307d7,7cdcc866,349ad1fa,fb1c7d8d,5be19270,2a46b327,9b72bd77,e3666805) +,S(a5a8a711,49d6d146,c32254ca,e9c18288,45e9f2df,419ed68f,8c3cddb5,d22c4da6,9f47d887,7bc3d345,af4ae693,35639bf0,b003f481,7833d2d5,49c0bbad,3cbf6c2e) +,S(359328d2,4baa4232,d75b13dd,d146624d,bda494e5,2cc778f9,99a4c0c4,1f9372cb,d31556b5,71df399e,99084afe,c8f680b,b6695d7d,648f7050,a7881acf,241af6d8) +,S(c9eb81ca,4f2a5a2c,17268a12,d901edb0,c8fdacc1,d26c5e8,91af95bd,e673e7fd,6d4a512f,c974fe9a,711ae19c,d9bfbd6b,5acb73e5,355608df,76ed2942,488adb66) +,S(e26631f8,16f3b308,ac4c9f90,eb6102e2,48e55730,24f41f38,6ccde5e6,c2ca8e2c,16e0b2a8,823aecd0,165e2f10,20518253,96cfb814,bd5a248,58a4615f,eb9306bd) +,S(f76e9af3,65c0e7e5,7807b24,269c4732,677e5af9,a3b0a965,8db8355f,f99110ba,80cbf439,d26f164e,bdaf2542,385f9d65,9d174c62,96ada102,8a0cb013,bd2a9407) +,S(dcc8145b,af891483,295d6ab6,9a88fce0,bc333d54,841e7421,e65f6270,852c5dd9,cedb3770,a620e7a9,b40d6cd1,60363bb0,296155cd,80d89480,ccac619a,f31e2a19) +,S(7645e13a,6a1c4d0f,a56a199e,cae93018,7660bc40,41edb847,ed33fad3,c603e4fd,7c8ea51b,7990e5d5,125eccbb,5da890d2,bcfc5353,b22c42ee,1864c22c,be91e713) +,S(ba26886e,d74cbcef,afe7210d,51c1b6bd,4658225d,45e20fb4,4222b318,9e2f3f25,fd885db2,23ddadee,9e65fa64,abba326d,e0b89627,7359e893,bdf50db8,a2b3409d) +,S(68b500e1,31005b1d,901bda65,4a525e80,fc6578d,5e792c92,299d21a5,19050fe1,71ef7d7a,b3544a94,f3dac8e8,77da5468,8df9b127,83e02022,7e05d5f,6b12f96b) +,S(2ccca7e2,613fa83c,27ff851f,12c6fa07,5b19f7c9,940a5064,b84ea75a,da7996ff,5ab70a22,22dd1ad0,d0db2d2f,c59390b,cf37af8f,bed4570,3c6ae0f3,27d7707f) +,S(6d797d2d,6a56a45a,1b5af718,3cb6dca4,383ad9c0,4d5cf3c1,827f4da6,f7179f14,aae34e5,d203d18d,99e84bee,7d6f5a8b,b80e541e,15a3aea6,7b162551,e80edf3f) +,S(aa6997c4,37be76b1,9553751a,d96a53e4,9a565a58,55f67b5,11605996,e98ee327,6034a421,9cdb40e0,9698a15b,2e2f5f1b,6532e277,a1f4f4cb,3cc0c33c,c684835) +,S(56c40fbf,fb8e1d70,a345d53a,7b0f897e,1e62484,dc01adea,d010c5be,ba2a0f48,e9d06789,78b9290,27ba0e09,173d8ca9,676a4a78,7e299f4c,458cb6aa,46f0bd01) +,S(db6ff6a8,3963ed7b,dc262be0,8b52c3dd,b6e1e237,2a535073,1d2903d5,17918d89,a40112fa,f30fd44,b42f6b18,c144ca0f,e248d65,d757bdc,162b7118,7d951769) +,S(8df55701,a46f63e2,338e730d,4a543013,dcdd1173,754e5de4,151ca180,63402c6d,64cf9c9a,64b63ff0,1396ddaf,b48bbf62,b3e69665,bc89ea93,b9dd12dd,57a1c3df) +,S(9d10bc2c,51018f57,407ae460,e5dd5e45,66485cce,de258af3,7bef48a0,1b0588a1,38ea3c08,b2a4e171,7edebe44,e03dfeb9,726b302e,aea0eac2,fcbc9389,84922c04) +,S(13bbc875,cc07a787,2ca7a706,8201606e,560257b,fce7e66,2e4b9807,37640367,b4cd971a,47c82a66,d8ca19ac,baaf1b1e,d2ea3daf,d6f54769,7fe474b1,c0c2c708) +,S(8f26dc37,f617b174,dc947630,d0da6dc9,1857d9a3,13c92b1e,24ec9cc9,5ea9c3e1,3848a303,1d96bf27,4e423877,6591ac44,f887f2fc,1553375b,d5cf76e0,e73f15ef) +,S(2e1e543d,f290916f,5e55236f,660dbe13,a23e0d9b,11065041,ad3a579a,4c7b5f21,885a4527,626e05da,c10cd9f3,218434b7,947bc083,28fe2d28,ff10ad2f,b5473846) +,S(c83c2ca3,e0509ea8,bb876936,e3eb69b5,8bed8f50,e8c7b85a,1238082c,ee3f3109,2b9602a9,bc808de2,b0b0da10,d09da554,74b75e75,5301cc0e,3b07c331,329cd5ed) +,S(8c7903b4,e2c114e5,3264111a,ee385311,afee8b1c,90606700,578d1fab,d3d29607,dd02dad1,964df94d,1eebea9,a2a343e8,b488c401,88299b25,28d47376,f1025731) +,S(48263c54,dd7575cc,b30adfc6,c1c1eb85,b5185786,654c89bd,a38a681d,a6569cc0,e6f107bd,dacdb528,ed98e49,a25936ea,e223ac5f,55cb7955,ddadfb88,1213167f) +,S(6a7c050a,ea7d1f58,7918644c,63cd5d98,accb7127,c54ee274,95fb2546,16f0d2d3,fad19261,27a2646e,34bf733d,c089b0ca,f80f7383,b4bb1546,28d7256e,d821e232) +,S(e1f0a53f,62fbc202,df9147dc,c31cb09f,ece879a,ccc5a3b,f105a198,7f5c3ea8,3930a10f,b0eea30,c314848,94037780,3968f692,fe2e5d8d,cfe920f1,e5706277) +,S(bc0fb113,cb0fca5b,fd66efca,e8a4c866,da9b6a0a,6d4e613b,48526675,d3182b7b,e11e725e,6993eebb,d2715f69,13ecb148,37892c56,745c601,31ca1c6c,bf090f31) +,S(1fe5a0f4,9c438f48,64bf9ebf,89eae7b7,ee7e6a1,74a134fe,266b0329,cb42f908,db8095ce,f912641c,92b3878b,cfa7b596,694514a,ce9b779b,35ad6109,c675f66c) +,S(5de3fdb4,90167002,fa6e61aa,eabfa53c,ade6ea83,3f3c8f6d,e434320a,aa524cd4,b5cbde88,862ad6ce,b344a1dd,88d1ac29,6d7e9ca3,d5e98535,dc158579,147e08f0) +,S(93b06993,a913dfcf,5eeb0be6,294a645a,b2964f2d,e2196073,8adbfcbc,23da54f,7836782f,856cb744,4187330e,fca3c7e2,9c151ad8,1e8b507c,4d14a14d,492ae0b8) +,S(705740d,27d90741,e685a044,f6b055e0,9c33369b,e3815851,886b7860,8e762f0c,c4a58fbb,738d46c6,9e93193b,529f12ac,b2142b32,1fbb3c55,d3f5dc99,173d956e) +,S(7619a000,fc485526,734a20e1,99530f0a,e5b78767,61040864,34a1a1e1,1d93e296,7f300de1,182f479c,f6fa0afe,a2ceb59,ab26f675,77203201,4f8be86d,46ebed6) +,S(fe20f5a8,ddbb5201,6cd346fc,d281413d,cc0d54b6,384e460d,bec5428a,599ff2fe,5b488ae6,e2604871,afebe551,f9957b0d,338cf47a,b4ecae0b,ae7a92b9,242d879c) +,S(995b9727,ba7e52cf,2dd29e07,73c751f9,70768121,8ab88b84,422e1e3f,42ad11e2,c3c9ddc9,7ee41468,c4ec5e5f,529bd8b8,49c8d585,f905b91f,42af5c6d,2f7fd26e) +,S(cb4f2e4d,2ca94d7c,79e91344,55161921,636d26a0,d84c2a16,e267c242,702b3136,215eb6ee,394940e6,cf00df48,f7e2b8b,1b9b2f71,7a68ee49,bbb879de,803ddb9b) +,S(ca0a2a56,34b49144,bacb1070,7668eb67,4572b69d,5e1a4e6f,c722c256,b9fce397,92079cc9,74ab2fbb,a5098c85,56ada7a7,6e2562a7,bdb5ebf,e958eb82,d3f4841b) +,S(fb82332d,c6c0c0af,d72f27a0,24ef163a,bfadde89,67b4c246,acd6b922,e3af7c7b,1a697119,f21cb690,338beaa5,2e2c4b1b,2b90d399,19ccf0c6,60dc540c,a934c7ee) +,S(d056ee05,d9258def,20e184a4,e2018bd6,d07550a0,63db4673,6afec42a,5c513dd2,af2c1d0,d222e356,67e7cfb6,201ad727,89ab0fc4,d68dc31c,276d998c,bc15bffb) +,S(3fadb222,c6584262,c4046c59,4b778635,c6325712,cd828726,7aea1253,42526f12,da5c8c07,fa6811d3,84ca7e8b,fa1d74b7,d181e0e1,eab86717,58666b7e,c199ae96) +,S(d27ac969,e222e50e,5dd915dd,39d3e453,214b818e,fb67e924,8ab4263b,1d9c4b61,c4857694,baead305,6e6709a3,28403236,9a7c55a,d81e7f6f,9b494a76,cb3a1c7f) +,S(412379b4,8b981ae6,a5fa4324,bffd69ef,99cd3a01,91a1acbc,d9340f01,72c565d4,f24036c9,c7dbe4f0,2ca523e7,ebe6ab57,c755053b,245523ab,ac9d3e87,fcfa280e) +,S(d8234eaa,99374c13,b9a8cc3b,663d00a5,bef7909f,b21243ea,ce9dfaa3,f79336f1,b8cbe8e,c4b465f6,6a3c1593,292babc,1845d8f9,1dc80e5c,b1ff904b,6e755c27) +,S(426f8b35,68c5ff9c,bac0ea34,9d8d3c00,427d323a,3d877bbe,47fbaf2a,5d189009,4e02293d,ce023c7b,9c7a1ba7,27cb82b9,9931f57f,78853bfd,7dc4399c,bd00586d) +,S(f1c706f9,2ee5f2c,2412a7fd,c9ca5c79,8717576e,c780f6cc,fe72760a,4497dfa5,c5e1a1a1,727f55c3,b3978700,51a09b84,9ef75895,d6b9c4d5,9f85cfac,2bb7e740) +,S(84e2e07e,cc1d5987,968d6682,525356b7,5982001e,67598b67,608f1869,36c2c679,41ab1343,8ab5ecdc,cbe75a8a,b7fceba5,2903f4f6,f5b43bb6,28173866,cc28555a) +,S(7935eec2,fe535823,44abb699,3a18cf6a,3d599b0b,ef0f6068,9c689b40,76ad2b54,ce2fc96a,ab978861,b6ced574,fe4f5c85,b77480ad,e3d75b40,6c4cfbd5,53371910) +,S(d542a4a8,319f6d56,b79abbef,9da3a009,3e8c2689,421e3839,27aa6a30,b0dbd9a0,5104a162,65d285ea,8f99f3e9,c1951a06,6badb70c,985877a4,f1979aec,7b8173e1) +,S(bc692619,42289890,4af0157,a038126c,41011b9,26fb232f,d3c27d9a,44ece2be,8e2f10c1,47e2a630,9372e854,3cb8ef77,11e2022e,db25f4f9,847369c7,fe52f3bc) +,S(ebe1b736,bb178c08,deacdd80,b48ab650,ae079a90,95f43e51,f4521bf5,d18d8d03,ad81c659,b1450de,45f78ef4,b2a8126b,782f2061,fc8eebbb,cd0a2ea,cddb7514) +,S(e626b908,49e1853e,4fdf887a,df8f0562,666d094a,9382be7b,de152dec,b9e2774c,90cf55da,a4192a27,caab406,2cfb8758,2dca7342,c59627c3,f513ff9,f4834ea) +,S(fba7a057,ba048792,2313b8b5,ba4514f0,d8f571c,bc7ac0b6,1a987a9c,e4381e67,7d2af7fc,142653ba,bf05133f,958a57eb,7cea5b90,ee5300f7,18cee7ae,d69ec44a) +,S(144cdb95,5b5d5181,5a2e5bdb,57033e67,239735a,456579ea,1e3be689,94c35a54,e6016f89,e896fc59,80150d2b,8135b4b,ed0a108f,8a2eaa83,91987a77,6253f1e6) +,S(538de6e,e7ab1b5e,cd74a2d1,a9599d9f,ad28cd8f,2679eb6,81c25e1f,183c0a24,36c0a747,79611193,5bdbac87,d5b38a05,2482b973,213d0960,1de8404b,14121004) +,S(76914f0f,d28f73ee,20ca3806,263b757c,7876eba7,54bef2e,9d10615a,ebc03794,6de24dca,e16855bf,22355786,1626c62c,59330752,f9f8808e,c3930680,33a12642) +,S(69915e6c,f675f7da,f595f7f9,6c912f29,86bee7bb,aa5800af,27e32baa,9e34e4a7,6094fbc0,2f109855,77b53c91,8dd2da3b,139bc31b,77674d56,df72cf22,2b9513c9) +,S(4ee6c20c,900bd4fd,be942dcf,db91182e,2c37eb35,e4287e5d,859ba847,18ce3015,84ac9769,863d618c,4b738e66,8c293d3d,42e2a9c6,4c47cf75,90ed50f3,2062cd64) +,S(a6813c1a,f57bf8a3,8ece11dc,a4c8c581,aa598211,af9ec6a,d2271796,88a0fa61,6420274d,f4e29631,5b7e2335,e98fb2f1,d415c99b,dc5a00e1,d182e809,5b8edca4) +,S(421003b7,107b2897,24dfc743,275db49e,69d91077,6ee53d6c,fdfe6ab,710fce84,cb955569,7b924395,c31cb149,88bcfea6,a932087,3aa8002a,c218f33c,76f9427f) +,S(9cb19e1,cb8c4fc,4009d362,eacf5f25,5e125298,5b546df8,fc60bdc6,48c8c522,227015d3,2c6ee68b,63e20e70,ed7cff86,8fd286e0,905bf623,1b7e894e,c7d70d12) +,S(c7413596,8193588f,38f412d6,4e2ce0cb,c4a7ac4b,75e33704,36236362,8bb953c2,7bee7fa0,1b7cc4d6,d22dd4c9,ec16b39,5e1d70f2,52778801,9ae75ef4,a908aa6b) +,S(2a8e8a05,321f4926,e8fac1ea,8a97cc64,c2f8e398,e86ecec,9dc2e676,61df22cc,63b86507,4e4fdcaa,acb60c21,c0d3f813,64f0c007,faff7780,9c81fbdb,981fae42) +,S(f3ef2e60,7d403594,48aab014,ea8e2dca,5af1d8c7,c6f66127,3552293e,d497a14d,dfcd5334,214b9f44,bc5d1d96,49d78c15,9e9ce44b,f43dbca9,c637c31d,622cccc9) +,S(6d5c41c8,169c33a5,d1111b36,1b1d6713,a9396424,bbfeb65a,80e40280,4857ead9,4c1f74c4,9f01bc2a,27a91d37,f59bde,3d674220,a521aa4a,6a0c548a,a45e84c8) +,S(5d812a61,c6a986b0,2c5982ce,34fd478d,60822322,47e07c73,488c71f4,4afff85c,37083f7b,16018fc4,25749e16,4c923484,680b1517,8bed41fd,cc8da253,ffa960b8) +,S(e01596ec,b82ff347,c3ba9710,82911855,720177a6,9efa7d5,dc6aa847,7f73093e,a674c832,849884b9,d38da5d0,6a58e4a7,a17acac1,8776577e,5547eb3b,b45b3bdd) +,S(67a5a900,313ff2e,7b32fcae,adcff7f0,ad52b960,ef6b0d0b,8344402c,9980f55,e77efddb,5e8e8865,eb807575,37c41e0c,9fdf928a,eb8b6a03,f41703c9,3c0a4a02) +,S(2b96879c,2e2d0ad8,281c1647,7de62583,53d77db3,57281c21,7df0d7c3,6466b277,dd5d3473,43bac32b,cc72408f,64a9fe7e,7e0dae06,786a7074,3cbbc846,80b62b25) +,S(ab94378d,f08748d7,2df137ae,a3e3e123,639b054b,fb726bad,825e2d9c,310367e9,5785ce24,40037d11,a8331865,c78ddaf5,7f9d769f,f2df11c9,9e3dd018,43a10f03) +,S(70782556,fd6fde3,8c93def6,ca3b6c8d,2f3fd4db,87be4985,fa3aabb0,43e7439f,7913bf65,ecf15020,cc5274b4,8eaf5351,51894bd8,fa236af,8f9b3fb8,178ebec7) +,S(3025d070,138bd507,b338c72c,1024a3f7,6b4d5e80,efcedeb3,e71a018b,77ca3942,3a8a6a0e,175eff95,a94172b5,f2a4ee53,8d503295,63a5b096,f83dd669,eb470ae) +,S(3fe372d0,65e97abb,94069e7b,2a8d01e6,576081a1,93897ac9,901a3d18,b9ab0b6,2efcee92,3154dcc0,377315e4,1d67b5a4,82403552,ce260c2d,4c1020a2,54842e5a) +,S(368c7011,3a9133ee,97ebe8f9,cc06bd7f,c3c373e0,da810164,bce5a30,2af55636,21bd40fa,4870a8e1,6f28b942,bc169712,eb7d70e5,a6faa4a1,35bf86b4,d06162a8) +,S(7df8ffd8,beb45e4e,1e440f23,ad6d713,57682f68,e54fd003,87eeac94,ecd96810,3d9e4cf9,908e5a99,44857d78,94c7ffe7,dc55550d,a07d3c9,ec07667d,982ed0df) +,S(cb6ad939,eb49c7ee,7c216262,49b11276,88269172,504fb8bb,dd2dbf7d,b6f23d4,ec8604db,18f27367,de1319c4,59f8e668,5480ca65,e1789cb3,45e0018e,ea98346d) +,S(5ae526c8,fc319e6,e054e6a7,59d9b205,d8a1e042,e684c53d,39098f28,70f4265a,da1e1281,14c25a90,cff3ef80,d5577c08,a03f4bab,d9e33850,f2488617,d50a9a63) +,S(868d7889,6ddbacc,1bbf2b10,e00456d6,17477767,5b8d3323,a8786ae7,25ff7661,d92d681d,189b0df8,e2be57ba,a9c146f6,46e6ce48,342a0ef2,96fc3093,af22ca1d) +,S(9f2f0b4d,e7b881b6,c4dd1f8a,74106a32,23bd3298,47c325a4,8341d899,d7686b74,6fbbd511,c2974af8,b5729ca6,658562f2,deca285c,b47f16e3,c444fc14,8e440088) +,S(faaf222b,813b0df1,5bf2da98,3ee2eaf6,c522f5eb,e8c08b4f,e6950376,d3fca8a3,bec53e69,2e98dac6,8fb2ae87,44f12abd,47f00e6c,5d6d1469,b2cdb41d,440b472e) +,S(7f5209b9,db4ab9fa,2c2a7ab7,6c5c62f,13c81c80,a4ec4008,fd67d229,a7161475,a13d6cda,f9e8699f,f397d1c7,6f191cd8,a58be40b,9acc4ce4,e69cea0b,4a48d193) +,S(f436156e,80b136f0,8065f395,e2700a14,50c7b492,405a0c04,6834bf4f,12c84c2a,cdd957b0,5684e01b,a760807d,39b007e8,e7aa890b,8882a0e5,9118951b,bd7e60c4) +,S(ef9aa749,5c5b37d8,2060c2bb,3454fa6,88216754,2b24d69f,201f1c31,3cf1d06e,f840da4d,f1d84a8f,ac125750,e4e6cd7c,50003aef,d1f77666,54ccf4ca,f27a4aa9) +,S(7deedc43,e96eb93a,c290809c,25e7936d,6d9e1cc8,a5971d40,b122896f,fb5de05a,e65c0e66,27b9d0e4,e7b349,ccf12fcd,de6683fb,6bc93381,22026b65,26a8e0ce) +,S(5a1bef15,6669795c,47be7495,582f361d,878384f4,f283e15c,e74b3ce,986d6459,ce985a81,1fdb02b2,9e35b97e,1833e380,69b28b81,ddf8cd61,b1179c47,2ccf39ff) +,S(d8123d39,233413c2,b14eeb78,983c6fad,73b8fdcf,84b2383b,fa453558,e525fa8e,ea9d399d,905b7200,46ff78a5,abb3cb56,835e35a3,c958095,ea226947,a633ef73) +,S(62f43dd4,f1f4c310,266a36c3,909afb5f,10d3dfd1,a7a6b5d8,93931334,e4d4bc8,bb7589f1,f83bd647,2e34fe22,53a101f2,75525829,daa2e8ef,89fa1dcb,b590f3ed) +,S(6cf371,d549c045,1415aa75,cf594d4,865f6e44,6f62df4f,cc1cf312,f79d72f7,f8ceb62b,5c979bd2,82a4ce20,682b45a0,2b3dee9d,68ce16a,7581adc5,c9e93af4) +,S(5bd7b780,770be9c3,7c1638dd,8f954086,49aeb371,516bdf3c,509efc5b,47cdd7e7,d46c7848,8a87aaf4,f606b91c,5add0c25,efc12c03,2a28b1e3,2190999,286ba9ff) +,S(c7704954,a319bd4a,e5a08665,3b959ba0,81eecd7f,f15681af,e6bf3db,c36b6a62,373543e8,24784ba8,3f53b9c6,189ccc4b,e508f600,b15c7378,36e3b3ca,516d0fe7) +,S(2b4f27fe,41533a9d,d807da70,7102c1ee,d6e92c8d,9e43958,8fedc6d0,39b19cda,98ba8310,ecd7cb1,c11fadd4,a92f4c30,49ba2180,24015d6b,89e2ad9,f81fb424) +,S(e7d4171,b3dc34d,2600c581,d6931f97,2c920c81,3d7c3db5,4d306c8a,5d4c0e5a,b29581ba,9a825387,99fa97ef,b5a6c4a3,3e75b71,d1cd51f7,5b6144f2,4a9a409a) +,S(6203022c,18f43577,5270043,56a2717f,2face761,e806e84b,2b5660d5,f1c5d52e,18a52f25,5d0a4d75,275fe97,3542b97e,212efd6,aa8864eb,6c80865,136a720d) +,S(bd81c7a2,8c1c80db,a0e327fe,3b0fccca,48987679,791e6630,e81c004,92e43b2b,ca50a84e,a14bbc64,9e0831f0,7f185536,416064ac,e93a8678,db1e2347,803871c0) +,S(9bc43e1a,d3c1390f,cd1385cd,603a6e0d,b875eb4e,6f3a554a,626a9b4,fc4bc29a,857a58bf,9aa54bb5,7acd8b34,b79717b2,64ea39bd,d8f4a22e,6b12e6a6,e0cb2cd4) +,S(3055a68d,194de3c7,9ef0117c,b98bfaa1,5e5bfb71,c656f908,2c8769cc,a7e40f33,183f9ea,dfebe1c4,bf0218cf,b6aba6bc,acbd8c72,a7fe086c,1c9233e4,340d1d10) +,S(4adbf984,ad1fd188,c9271072,43583b80,2cec1231,7d8bbe1b,71079fbb,734b08ac,14527188,9560ceff,3d950766,8e3f2d0b,2192ceb6,f79f5954,7094d5e3,d7ec04e6) +,S(38f72dcc,27e041d9,c7bae397,9cf16d22,83fc6a36,aa074b0c,d00651d1,a56487cc,5238c97c,33a106e4,1370d6f2,c0b6ffc6,49610444,2e51988d,f7160c3a,933dc0bf) +,S(de55bd8a,7bdffde0,a1413828,ac59ff2a,4560647,47c27813,322188c6,655be2d3,477861b0,df5b25f3,75f9d097,97b0afb7,67bf9d51,b996d51c,e65d3bb5,42d9da) +,S(c617bcfd,c47e02b8,918edf98,666fe912,e19340ea,6c567893,8739b89e,5e3bcc66,b90ba117,357e7683,2ae0f4e8,644cfc49,a6e04672,b5b08d47,3b826349,65117072) +,S(4655c196,1b5188c9,4b6402f1,e92b3033,c455f4db,ecadb672,4adab3bb,f1c84758,3ceecc93,a2575f4d,fb202df4,f4bc05ea,714600c0,28389b5c,f2c1c6dd,e6fbc4a0) +,S(fca90f40,dc05e5ad,3693c503,fab43da0,110df431,9c1dc4c9,e5f86e8d,d7726a45,f5822f9b,be3e6604,1af37aeb,19a70a6e,7478edec,68c8e1c3,e6cc86fc,5f5771ca) +,S(3176cc4a,dcb28258,779e88d7,f90d3b3e,a31cbe05,78c7cafb,93ff151b,d27f61e9,3863f2cc,148a0a43,c94fe7bb,afb5ab5b,6a18ce44,56cd06aa,c340c231,42cc0135) +,S(63269f73,d124210a,d070db15,14f82cc5,51d9c19c,d31a54e0,b811e650,ebb2ddc4,bf8ec7dd,8a787a42,4a74114e,5088c387,38246bc,3f6aa188,e300f0db,d85cd0ac) +,S(679a1d32,8d0f53b6,1ae7f544,541326a8,f07e749,34dd2708,dee3ce54,632003d0,83ca1348,8f21eca3,11be341d,5f6ee9a6,c9b3eb55,c9a3485c,349018a,725c350f) +,S(69a27d79,29982c84,d3948568,a54d8933,f7172f9d,e80276fb,846cf399,e0b38b28,f411d207,cb0f0bb8,574b16a1,22f39bd7,69c8b7f5,ac2db4f3,a02163e4,3bb26168) +,S(298d5057,1af5042,cbff2171,6029cef2,abfdabb9,cafebc7f,9dd56c8f,6773ee4d,f4343e71,d271844e,412e62c4,bf6dc36f,4271bda7,9ccfa220,75e3c055,aa97e9e1) +,S(3f2983da,46d1f101,8f8eb984,5bcf3930,e26f66e4,544f63b1,f8f8b979,200c4653,4973a914,c4ad33b1,36d57e6,b326d380,9b2c38bc,79d81b53,b26c5995,9d71db0) +,S(f836fd3e,30ceb88d,942d64b2,a1d2a6f9,54412ea1,ef6a3b13,59faa8bf,7e3538dd,720a3081,53476b5a,709fd29c,158b7720,c8161ee3,6e6aba3,b28e0282,cf7f9367) +,S(dd72eb9b,911e38a2,63bc8ee4,fe991fe9,295ed522,1eef930,b3ae2df2,ef583a,2a0da5a5,688bf7f9,79285d9b,360b53b2,398e86b1,43f10b86,83cb40c9,40c1b2ef) +,S(551e2650,46ab255c,c427bd81,8258b7f3,7cef92a4,496119be,283f033,4968a619,317c33d2,4cd8f35b,9b86a5ec,d1582a6e,e5ca16aa,3c6ea23b,2007a370,e7bc4f30) +,S(6683de92,131de3d0,ecb19a30,47825591,ab28cb61,5c852a90,fbd3ad78,5336c650,49eea58e,7fd95523,f8a9556e,c46fa0e4,b3c3b2c2,8e5d9e4,d636d089,282225f8) +,S(b5b3b1e2,84a6ea65,e0a9b146,e8f895e9,ca99ef3d,31a4bac7,bea49c89,3cf97805,a1720a94,f333e296,f2b91437,e19500be,3cf16cc3,547e9667,ffa4ee88,e5b6fe0d) +,S(a5019773,7c337684,9709a232,9718d463,ccdde43a,55350d10,11a2401f,9bcbf466,9cc069e,ffefc5dd,e8fa5e09,b6b751eb,c51fff8c,fbf3c27e,12efad87,a9d429e2) +,S(9ec3498f,eb2bd861,42841b66,3b1c58b2,e5cb7224,88e2f849,e1236b63,ec1588fb,e00c91a5,e14b97f0,362719bf,2150dfed,e517e585,a807333b,a000652f,c1f5f12) +,S(e10b6cb4,adc13185,750b446b,ab926667,581f97bc,5aaf04b1,f4731abe,f3c5b6b2,53a51213,7db20477,f3a87bba,5b65c01d,d5520b42,f2660ed3,c1f5fdc8,35e2b9c3) +,S(b754a967,e36ea7c7,98c93691,e02b6043,60ace927,13b18d0d,3ff61ce1,dd3ae314,8a9a3963,9aa7ee59,a42f8d5f,bab01e50,d83ee029,933cf59b,e12b82a1,5473d849) +,S(5fefcb61,c10cbf3e,6f8a51e,6bea569d,7999a275,6340ac8d,489183ff,32efa869,d81b3093,cbc4835c,1d88c093,28c20541,dfe49950,9b349207,15bbe1bf,4e881e83) +,S(fe51a3bb,4ac97e1,96009400,b27c8995,f0ed6f0a,b29b9184,70aa442f,27d2d848,8568fce0,a4c39e44,f2b8aaf,59f0b183,d306254f,83cbd6,ec5fd568,c9116a58) +,S(6a3e4629,9fccfedb,c58d5d38,aeb2a420,391df798,e447558d,7b591466,fab7dee3,1bed8fba,7bb15d9d,d42e7b5a,3ab1d9e6,89873eb9,e3161a91,aec3ee9b,22a5e548) +,S(adfdbcb8,3c40d4de,f7d62d97,e16bd32,33db304d,392492ee,7457ffc8,97450c37,7b082bdc,163549ec,2a8b588c,4156ac23,8fde7819,2e1cc195,6be85b87,f4138e0e) +,S(96850fcc,9c202c54,83654c27,2d17011,509ed7dc,6ecc6cb9,f06c3dd5,113bf61,10404432,7f966c49,af0c05da,c7ec18b5,b9fcc455,a01c3b6c,88b8b0,d281aaf7) +,S(2ae21197,8f78ca14,d88151ca,804379a7,1703f514,166f3ac4,f09409b5,5c458bbe,4de74cd9,576035fd,451c7dde,387961ce,2328e327,fa0312c8,6cbc9dc4,19ecf7b1) +,S(edc18742,e1b5c996,2661a332,cd62c426,7e5816e3,c52746cf,b79eac61,1a987e57,80003b3f,c63309bf,3f9d484f,ebabe579,29ba9032,30311bc0,2c021a34,5b4fe5e) +,S(7802aa2a,5fa0f0ae,29a90885,c8b94368,38b35d49,843765aa,1d6a640c,fe43a244,c7f329f,e63239a8,407297e0,416c49bb,7e3dda11,85baa5dd,7f5f7167,9192a803) +,S(fb774579,b124475a,c36ede71,2e3fc5aa,c2173de8,35e72386,bf67bf7f,992335cf,c0e66b51,63c17b7a,e43ebf0e,31a8d641,b6f413a6,b763ac01,cdfcbcc0,47a82c94) +,S(d81a3d8a,43abae9e,32f94881,fe9c1200,695a9c24,a31b6982,51e21b3,fcbc2852,630dd811,e8a8cb6d,7bf51de7,e0a98db5,a693e3a3,c9064dbf,2ccf6bf2,dd12673f) +,S(519fcfd4,97cf068b,804da0c7,e2268cda,7cb2eda0,4be5249f,9bf776b7,c7324cf0,bec6f4cd,198ab3eb,be691646,6e44ab9f,a357c8ec,3bca391d,c20e6ffc,9562fdb9) +,S(cc4ad3e4,8fd66073,5e193853,e0f9b1c2,3e31b284,e2770441,24f9296f,868ef592,88af158d,8583c474,7d5520ac,67b03c75,9b12151e,db25af68,996263ad,d783c27b) +,S(add950ae,6f5186a5,98b40cf9,64ded6ea,c06a57a1,4b85de33,8f709034,78d24117,c59ca4bd,2d69285c,debd6853,a0bec744,111f6dfc,7004d65b,c047bff4,a3affa7d) +,S(dc071b45,43097de7,12bc8173,cb358af,ef0437c6,7c4927dd,d0bc3f6d,bab383e4,793912a9,899b304c,173b2adf,c187dfbf,f28fcaa0,fb11b85f,9e410b40,e95ca8ed) +,S(5dce337e,6db6099d,a9c37da4,a96b7a72,fd97a672,47f6babe,94d79b1a,aec167c7,1b887fba,1f098a43,b6482991,f16ba2a0,eb35bf56,64eb21d5,7549dbd6,4e4377f6) +,S(516eb6ad,8493a662,6d22ead8,e808982,900a83f4,3e6109e5,ba284cf9,e1f5cfcb,ca350ab3,8085b46f,48d8308c,61d3f947,ef6a8511,bb36fe3c,68d30fef,ece05042) +,S(f767c150,3b1f2da1,e5b91211,4e917c87,732b3fb8,8c860a3,35af1c7c,f00e5e75,3ecef8c8,cdbe467b,888f64a2,655ddab5,9ec610e,153cd657,95bb473d,a1aaab54) +,S(f9bbebcf,f792c8c,a7bbc5d8,f177de7a,4185582c,2cc65e75,31f3ac26,73c977f0,290ab4fe,fecb3a26,256f767b,8a19e55f,ac751eaa,acecaf52,b8c99657,49614a85) +,S(c6bfe1fe,be5c8db1,f55bff42,c123df61,6ec3adbc,c9b08e8e,af477fcf,4d47c45f,c595f882,d6d6a2ff,d832eddc,95679890,41c27101,347ea995,c79ad56c,d641792f) +,S(56c2d771,808fb4c8,71dcfe6e,5170bb47,c5559521,8b872425,761829e5,e0d3ce1e,6de72ae6,9e03e626,fdca9188,9e721c61,12ec15af,7163629f,5ee0fc9f,540468ba) +,S(20218b05,ec9f5be5,87a96d8c,3c0cf7d2,da9ecffe,fc604fb5,66a3667,5109d663,2ec1a7c2,c5357e24,1f2ea0aa,7a7321ae,b41bfa3a,88b42e12,e79283bb,ef8f6993) +,S(3a089269,a04ccd9,5bfdb4f3,3494aff3,519035f4,bb3a4f09,e2afcaff,976be4ee,e5680e25,fb0ba1fe,91693757,b3404b32,bbeba22,640da757,ed91ad85,6842d1a2) +,S(8f9f2296,a0cd8b53,db0fd0f2,59860496,dfd3e101,b2975a2e,2856523a,f45b3f07,61c15907,2cacdf31,b229015c,d6290d77,8ab3be0a,57785920,a3fb5722,b5c625b5) +,S(86580be8,695bda97,cd1599b2,6d4b6328,97e2b90,d1ea106c,836f474c,fe88070e,abae68a1,7e19ac58,38a2fe5f,d07dc7ee,2bb220a8,9dba4ac6,166c51ab,b17d7d21) +,S(fb6ac38a,1e69d589,ecc5b4f0,e57aa1e5,eb1f9458,fb187d04,8c72dd29,72b4d762,d601dd16,bec271f3,16f1d3af,2b82b5cb,e5d1806f,b960014,7fb01301,86ac32c1) +,S(7c10606b,c54fea81,bf3aec94,72bb09ce,c5bb7d31,b66e27b4,242b8dbe,7ea5f0b0,12215180,d3d65643,76583987,240b445a,2ea522a,3e0250ab,d14d77db,bdf68f54) +,S(85e6ab94,833593bc,74c7822b,1d9f384a,740a5be6,3b846495,2042b017,2d52a1a6,f5c160d0,68d6f86a,8f651213,8cd28e3e,d6e69fcc,dc75f0f9,81fe5ebe,1fae93a) +,S(7e1e2d3a,99cd1ba0,51b74ea0,e5471f50,5e626aec,b880ca27,f02113ad,ff51cfe5,413ec82e,e9d4fe37,8c232229,136f1ade,c1eb96ce,c0062997,81f4b009,9dad89bc) +,S(589640ad,813735f2,ed7a08dd,6931c61d,6a9490ac,729c2bd2,928ebd01,e87aaacd,89d6b10b,8a89fb4b,3679fc33,96c4cd5,7a1e909b,e343ee15,c1a02719,20485429) +,S(7080cf4f,f3465ce8,fdcdb0ef,3741c19d,91fdd7d4,44118c5d,7b7ad040,5e6e6608,7a3f0da9,5cbc0ba0,b5c6d334,f80db1d8,3033489d,34143d34,7dfad89f,8e2441fe) +,S(bb660b4c,2d400269,2dd96bd4,d7737c31,91deb03,6553e61c,7d59a876,26917c55,b494ad13,a7560497,6068403,15e8b86f,f150af11,18dc02fa,f5ea3f40,c0983c24) +,S(73a241ec,7ebf15b2,4408161c,5d8a5c65,3f4f4fb0,8cddae0c,1e2c506d,9f221b27,80534bb4,33a0a56f,78486504,1d480d8f,64bbe192,a2f6f32b,5e1bc175,d463640a) +,S(fdc1ef51,f08bfdbc,7705741e,6a2b58bf,9e45f202,c3d7ba6f,89a15aaa,78b1fe07,7e9d00dc,dc1e4878,647ac05b,55832540,1684f714,755f4e36,880e5a0e,996c0586) +,S(204842cb,a0ce5cc1,3ab44736,ea0e33df,a15ab13a,912687bc,6d6733d3,a33618af,afc92205,a6e991cb,fad18741,818a94f2,9e1804dc,2145ba35,896f92ac,c96bd521) +,S(a03a9c1e,f82c3151,81009567,50486869,a47c4a99,83bad93e,b2bcb60,157ed768,786f9164,fb9c6980,eef3e5ee,d9a43b8b,2158ba2,dcd8663c,a26cf19f,f6090a65) +,S(5e3b6e5f,6cb41784,6355e3ff,7da813cd,fa18bd42,c54565ba,ee19b15b,dbc00ed2,b9c3a553,42bf2284,fc3b8f9f,88d3e4d9,9d6f358a,c5c8acfd,e340036c,42ee603f) +,S(19b0960e,82d1280,663dfbd6,efb2746c,abf76476,1029a54b,154385c,a05dbc70,f8b28bbc,440112a9,45f35645,135d041d,33df5ded,92116f31,2d2664f4,e5504773) +,S(1061e900,7597bafe,74b9b274,ba1a22cc,6f1c2d1d,b6be8d05,9876638,dff3677e,707ac922,dec592ec,44d7b59e,be2518a5,fe2ffff1,4cf4cfa9,8155c781,21444b21) +,S(c9184c11,60fc7d87,5439b567,69a0e501,cc0e221e,3b155f57,9a71cf1,f3030b85,880b69e3,7c04689f,d6dd30f8,75f23899,8e83e594,9fb1ee30,8c43e982,28327805) +,S(a35fb304,4dec52cd,8fc5af94,e6c26ba5,c8fa2364,2622af12,d37559ff,4374cf04,1544f375,692a3584,ac7adcb9,a3422dcf,8b360471,85326840,42dd755c,112242fd) +,S(ba8c03ad,60ffebe8,118efbe8,eb130f6a,1b2c501a,78000a8c,1b204bd1,b90929ca,e95ef729,a3f341e0,faf73211,2f7800ab,65b88d47,ae137722,f11369c6,e3aaafbe) +,S(d2ba5755,842d8a77,97fba189,f49a010a,fe08495f,5e64bba3,361d1b52,8f30ddb9,da3669b1,cad179f,784bc5f0,c3a55d60,549e72c,390b7d47,7c030bfa,53df7957) +,S(3e04eb58,7ec988b2,77b1d2b2,880ccb24,d3de1579,54ac994a,86db9ae,6c8703f2,fa412113,e6dc85d9,eeb1d650,3df96fbc,ff3c6089,cc5c207b,b98cdb4f,e728dd9c) +,S(efe5bb42,4339a49d,3e19aefa,91f466d6,60c9f120,5df91ca1,7a0da3c2,699fe7f6,98c6212f,fb39640f,cea21db2,97cf9fc5,e3576b0a,7cb4408f,f9e3e9d3,245156e9) +,S(a29442e5,a330336d,eefe6591,5c4e0ea7,bca2dc2d,9816c8a3,2bd66a4,baa0908b,a1bf8829,7320e47c,3b774c13,a6fb9e6,dc5e7789,44666311,2db427a9,dea135e6) +,S(454bccba,6f7225f6,a91f632d,1c9218b1,48e877ff,8018d371,4da8aa88,f08bb792,da0ded31,2d532b01,60a1106b,202ea0dd,8157753d,a07e0e3e,918958b7,45ec713c) +,S(3163940,32c39566,cb0631af,a2505404,56573782,e940dfce,d425834c,dcc2dca9,18c561c4,1b15b0d1,a477464c,6f4d668a,67f0895f,a55544dd,8445a8db,cfcc5fc7) +,S(54b5375f,53f07bf2,f15fced,ffc7132d,5240893a,3bce0a92,bc4fc884,49b07703,31be2b3f,a944251,e52922bd,fa2ea218,f5df11c6,d67226a7,b3f163ee,61abdee7) +,S(283ce31b,745d067,2bfb9b6a,694da3cc,de9033c4,639e8328,d2b7ee6,58b3bd,f6a948a4,d452bb56,e3dc24e7,6e81d2c9,dc1c7aa,67aab38f,f9d20044,d8c0245a) +,S(b5e74f53,117ba754,7b12419c,b0494579,48d5f831,624bb3e3,a60167f2,10e99967,5f635754,d635f9aa,a83c22e8,9f383008,3da8331f,4cfd9b5e,10d48280,718dd3e6) +,S(681e5f32,cfa33183,170dc776,fcd51daf,d587c34b,1cd27134,a856713,cbf83080,c587d8b8,d144e4dd,7b4361d9,b7fcf30a,76f646ef,fbe390e3,8f241a54,7279266a) +,S(1fba5eb7,722f289f,2521ec17,d05df56d,2f28b221,5ff8c621,b925a5fd,29f1d0c,df1140d9,97ecfd4d,fa7f287e,fcacb66a,34283b63,5b2bb7d1,524dcbe9,eb11f5ba) +,S(38bff4b0,df378b3c,ed38fc5a,733f1bce,17855b64,cc254f20,31e1300a,6a0d0a77,6d4f065d,f634296e,64c485ee,32537475,f35a5065,e73bfdc2,520dca1c,6606b1be) +,S(707013c3,59a9264b,be4050d5,27b5798f,779df8a0,77cc95ed,6e08f74e,e57de831,b9f379a0,315386c0,657b8dcc,53cd98ae,582313a7,632e4c3f,da9c9157,14ea422) +,S(ad94ed2f,70282484,e490abbb,320905ab,fc5b8c91,9de6535a,73da3f63,45642ef5,9dbe8813,a554e1f3,e98011fe,c6545216,fdf4973,6d026bd6,cb8702c2,aac88457) +,S(47b56de4,e991876a,499ad155,2443785c,fd174f87,58b881f5,4f545584,ff62612e,a140537,5855beb7,7c56117a,5f753777,383d92d2,d4912d9,86bd59a5,7cefdced) +,S(51bce64e,9291a693,1f24643a,f11f2813,f31f624d,cf336af,3cf7b48,15f431e8,8f743d9,a3012247,ee35d28,4120394f,f40c4c69,d75ae13,a063aaba,ed07e552) +,S(76287cfe,b52c5a0f,ce98e56b,49b4ebe0,83b80bee,3bc7447,a0cb9542,e7f7a43f,736b7e56,fa5744bb,d39d064a,50d98b07,e9e3399b,dc5847ff,13e33eb6,e2a7a7bc) +,S(614d739e,ad01010c,3191aaa2,c1b7771a,8dc29bcc,f1c4d55b,181acdc0,df6b8f5e,5ccd4e8f,e8607b76,c8a96c2c,5000464e,5591efdb,aaf07166,4f4db66d,c2f8085) +,S(c38444c8,ee8c35f2,b339404d,90d53ed0,35cd5889,89663808,3e2d33a,5e4e1bc9,8b4408cd,de5452eb,dc222109,287fa9ab,1d2ffc9a,16b93f44,1f4bfaf6,6ccd29f2) +,S(8a9c9649,c6016f35,d819bf02,852bfde6,56a1c197,2dbb6539,2391df7a,99c8f349,b2dcd957,be2c19fa,6c4a6030,a4cb500b,1797aa5a,339181af,dc6e974a,d769a8ed) +,S(a0f5cd1b,e4449709,25d0ea0a,6cd28d75,96292ab4,1d1766bc,ed6b3311,bea04b03,d270b1e9,291b29b0,ce045d92,3e449ce0,c8d7931a,71cf4b8,68af9594,927ff95b) +,S(a78f2336,87675987,7afbdbaf,28e2590,25c64ddc,a9543a64,709d4c11,1f3a9614,1365fac9,a5d245be,e5245b3a,b1f50d19,de34aa9a,439f5e7e,e241f042,32a6de83) +,S(3bfd37e3,ae482c5b,92baa6f5,a413a46b,af6cae30,9ee3fb51,4a9fc3e4,b391769,2d930818,34bbb0dc,66e424a0,7778c9cf,d0219c06,4dc7fd66,bdc554f0,396d1c59) +,S(92cac49a,261d23,e776ea75,7e796d2a,d426f23d,602ece5d,1b41cd71,d13881a2,8c76ec13,8e0f37c8,99a3e587,a8c0c5d8,c7a43ac8,187a35da,c0d8492e,571521c1) +,S(cf6ede0,ea7dc67,cc370894,1493567b,718944de,7a96dd6b,82940214,b56842a,e2b717d4,e939608b,7ad6128a,53dc8c52,fa070d0,f591842c,a76e8d0f,54627cb7) +,S(6204c998,ba512945,26ecb92b,bb06e218,968c8c2d,11e0435e,3dd1bf82,fa91e17a,1690a15f,e78365c9,c8fae21e,f0af2a3f,d1ed18c7,9a607939,d12d8add,94cd0ea4) +,S(a2c4fb48,cc5500e1,35268d13,a6ac2e16,d8ce0707,f2237f12,ef71affd,e4c0aa5d,3809440e,e608c897,cb7e3906,c58123ab,c403f57a,8d59b950,9e5f2d70,3c0cd839) +,S(d8af7fa1,cc303e4b,c5da36a6,d3b2e309,6acc8ec4,d73224f2,b667873d,eb656f60,9fc7f2bd,5a5533c5,dfb39f9,7045b135,5e14d81b,9edeae5d,dd4ccfff,f0c6de0d) +,S(3e9a00ad,b873291a,604e6bbd,b05304bd,3770733e,d1daf90a,864d775d,76dea0e7,deb49457,f6d1c4ef,53524e37,f19a8d77,20a5cb9b,321b4630,51f8eeb4,901ded0) +,S(bef5ae9,30aa620c,9854346a,ac1b2ac9,760ca8d5,ea189368,9de50e5,d3dc3d7f,20d80c42,f7925f59,b0212b9c,7d4041e5,5da98a3,fe373034,c0fdfad6,48dfc48f) +,S(cd4169fb,cd28d507,6c174308,8f39645a,38215e47,888a435d,5922b490,9155f67f,f9e62ca2,1a5564dd,5a65f274,356c1b68,a170dac6,4b5aa5b9,29dc2f26,35e74674) +,S(a4b691ed,34cd2624,fdda2504,797d76cd,4bd37b11,4cf3921d,538b6042,ed5a4c28,c65374b1,6781b4c1,15b0ca54,53e3397e,5c045a77,72c72d12,c336c205,d26af55d) +,S(5e4e916a,4226b788,197cf68f,aa850c0f,c2b3a615,bf9e847d,c9e55dbd,88aa4818,3f3c3ea2,5c7c52e6,5c9991f4,464f4b2f,f3395d05,3eb27280,8d984a31,b798951e) +,S(dd4782cf,f4d69f72,c951b3ad,50a4c601,f743e6ba,2194d05c,a5f474d5,eba74a7c,97a7bb5e,9854ef7c,5e3dd6ae,8628d8c2,5d3a791,d4db281d,6c7c12c8,20977b99) +,S(a1530ad5,4ab405ec,ddb94543,2669c07c,281aff41,13e4af0b,cc080991,c1adce36,f3d25221,2732842e,5dddda78,1e2aa5b1,b18550b6,fdbb968a,77444958,4a63e756) +,S(51924ef7,b438e4f0,614523f,232af64b,e5ee0d32,ad8e5323,4a2d76ca,9e64719e,87c115a8,11f94db5,6736e82b,8b644bfb,fdfe6e55,8ead8176,26858c77,128a8095) +,S(82cfee68,e137d874,107625d4,be20a9d5,6ac9055c,6b97563,a3bd5568,53fada6e,dd49aa46,c4f5c51c,72a804d7,6dee7a72,a363e978,502a5cc0,6ff67987,30733b7a) +,S(a3738876,f7f1ef44,5a1346b,36c7da6,e02b70fd,2cc3bf30,a4efd7e5,5f2e623a,6596f52b,e1ea52b2,be0de46e,5480331a,b1204d36,581b2edf,368d9611,ea04a21a) +,S(ae4ca6f7,aec818d7,2b624fd6,b2ccf24a,8d8382b3,18b0dd7d,d8203658,2980bcbc,6a7ada03,35ad7c57,1e2a29f2,554c8bbb,34815596,1aa995e9,2378c97a,dd8f1ae2) +,S(be33562b,fa9d762f,9b18d198,4dccb9c8,4d6ac894,2d8506b1,400f7dfa,89d12732,b666efb8,77c666a7,35336cc2,14e874a5,c4f5c956,903048fe,473d2e6e,9475d442) +,S(9d7da6d9,815b3db8,5303b288,8a60ff6b,f382891f,5c45ecf,37cb92da,37c74446,46c29073,72ba5be0,e61f6d73,4b413d44,df3d2c0d,89fcc1ac,5402b68d,755b12a8) +,S(39233d31,e513c2bc,c0602849,8bb7b3be,b5981fb,7c99a366,b62a9df4,3b0d4d3f,fc27857c,92e0ea4f,4d8ac3e1,c61fa774,1ec57d4b,a9affc81,f97e384d,b916dfd8) +,S(5be2d788,db90e83b,58d02dd,7b58f41a,547e6f33,a82d84b4,3f7e5143,db7768be,b8cf3a8,6b2dfa33,e71396f2,e84fb6cf,d585ef6b,9a264875,b9049f2f,6fde1026) +,S(3ff59de8,191fa14c,113600c2,5700479e,7c53a0be,3827f5d6,82f7861e,88f162ac,25d5985,6ab0b6e7,beff5f60,c279a11f,8ae4efb,5e79a4df,d815cc32,2dc77ba2) +,S(81dc9c30,ed221920,98ad9475,da00f8d8,b61a18a4,e6305b58,75f75bc1,719a256f,2970745e,5c44cb27,a6f8f9c9,869a25f8,9cccf024,4d40ffc2,ca678d28,f54682a5) +,S(ba3e1e16,579c4099,1576121b,230d4f4a,ea386943,bb7a8d24,f765a3e4,9b70e9da,a1eeb808,653cfa67,8a9d8d9c,7d30e7b1,54eb0de6,55abaabd,90f82236,32c72f4e) +,S(35ffe81,50faaee1,141f52a5,1b7d1e69,fa66d194,3b96f04c,a0589c48,83f854f8,d66a1822,e83f6dd3,fcca09b8,df1105c0,b4ad3163,6070dc9,b7dd08a8,c9366fad) +,S(398ebc22,1435f972,1c214c0a,e38b9817,8b50d7e3,e5724f68,5b950aad,60763a06,c2df549d,eb2eeab,fccd4da7,fc2c8ce9,15551b87,14903011,6825b804,80811f46) +,S(2ba27f33,e7d0a75d,1d18511a,84e5ce1e,9ca89d2a,a44fda0d,6cfa1389,ca89e96,5023684b,832375c3,8f39946c,c91ca114,45c2ac54,fcc12f19,c6bb662b,997cf4a7) +,S(4257063e,7ec51f92,4ecd485,653182eb,2b54973e,f1ff0144,65cd151e,f9e829c2,47351f41,16dd0515,11c9ab35,b69abd64,919f8b5a,812ad5d7,47c5462b,55232c1c) +,S(77e737c1,b5b3ca56,94218582,d2e56519,80da8195,88ee46eb,e3ef172d,9b4063a8,f7cb5c5,e66798ad,37c8d4f1,1cb912e3,651ae45c,4d7b07b5,e0e20c2,89d1587a) +,S(dfcbb64d,71018c1f,bdef98b8,2a4cb72d,ef027d43,47b5e8c4,4a7eec7a,34cdaa4f,dfc0cdbc,c7207588,8af379e8,751c4f09,bc2f4e58,2dca9a92,e06b0c5d,97972a23) +,S(8eb686d0,a7871e8e,32ac5256,746d7189,b39855e2,2a52ce78,ce6962ad,d355ae8a,ea8b3611,fec52837,fd0afe42,4c22c5fb,89b79675,6e8a6a5e,d71e331c,6ccc30da) +,S(d17d8713,ebaf2dd5,316703ff,8c4c20fe,ee56522d,bea7a987,85a96b1d,36cc3e54,ea70b7e8,6d7862ca,9d8a7a19,bf8941d0,e9fdb872,4e46f8b5,6821d85b,ddc10b54) +,S(7d4c77e6,ae050a1c,856d28e9,c14eeeda,249c471f,82cd6cfa,3ec037e2,30b3ba89,c5a07653,f5fbdf93,4b180f39,327d102d,900ad68c,3da1a7bc,ea773fe9,6265fe60) +,S(96381109,451e5e68,6d61d51b,3b250bcc,3721dced,de39e88,f4f50c1b,4d7ebcab,9498f419,b088ca54,7f17642b,dd6f0fe6,9f06514,a7228394,7f15d165,64801b64) +,S(7400b550,54353d73,7b2da8f2,f8f4e953,85f44e83,fda3cd20,8afaf90a,978c2ba7,9933aca3,ce503677,e2129a04,cdb1d5ca,e36bed1d,275048be,6b18aae7,fd909771) +,S(3d7b0c11,fff7bfd2,71ef5963,9f12ebd8,d1de3bc3,93b51be2,a011e4ba,66ff59f,b6397431,7f7ca0e5,ce1eb57d,8a886144,8bddb12f,75469789,b791c4cf,32404b9d) +,S(4944d9ec,7ebf7f14,e6312545,5c8dd996,45d128dd,fde3743c,5cd6f4cf,65c46328,f0655193,13baaec1,329cd408,c9d1f3d3,5c2f49d9,126c9d1f,5bf271bf,2c589996) +,S(9960432b,4764b1d0,f8607d6d,8e08b355,f37b9933,69bacb51,89ce46d2,79bdecf1,304ebb4c,921d2fa,29200ea9,ec214adf,f7e1a9f8,debab865,78fac56b,7160ebd) +,S(313f535b,63585658,fc3409cc,726b5ded,9196cc59,6331681d,9ca84aa3,bb3de4d6,e511a48a,a55fe0eb,e5f23bcf,d01ff4f1,59b049ff,8c999fb3,dd64092f,c56c9716) +,S(85c4586f,8cd36dc0,db9b9006,ef4f74ec,c99478d6,5c011bcb,3ccf034c,548ed7b2,cd6953c0,45c5d6c,dc632592,3f9b0070,144c8c5a,67639f63,a75b4d10,5b434229) +,S(968bc6d2,a7fc359d,1edb668d,b7381eeb,d489b20d,b64d651d,21d706f9,94671af8,fb0ad6a3,723d707c,c1b2f330,4e59517e,de88bcfc,b52f3ce,d0bdb7c,f0725186) +,S(2325aceb,3722e77e,e6cc7221,56ec0e87,26d343cd,cff6a420,e58c563,bf28985d,601a7f53,dd295978,f2a59683,6f3e9eec,19b9c8fd,e81bba5a,21acc8ff,878d39e1) +,S(8c6e76ab,b2313bea,dc0667f9,9aaf32a6,4b1c9335,87c31dff,5407fdd,67acd2e4,27e3b850,d422df3a,c93b19c4,229b390e,3d7bb92e,340daa46,b3f5112d,1e50c29f) +,S(e31fee31,34468d3e,4d6b2c72,8d538a14,352e7a8,8b9837af,9d8d4230,5e36748f,89b096e1,2d1948f8,937974f6,b9a101e2,37f9df3e,280d4139,e419a1b2,6d0309be) +,S(596b586e,849404b4,7c742fdd,7e5a3398,da748ab0,67ae2981,215c17d4,b0d306c6,e96c5cc5,912cc711,5e4e8f05,edadf219,2ac27760,2b2ce791,ff6e18f8,62a0a16b) +,S(200cc167,5cf500cb,a199c3b7,1195270f,d453d5a3,8313f317,71452486,15aa1c02,780aacc8,7e34429c,6445c133,49240371,1905e24e,c17de4b3,e7b79cf5,5320ce61) +,S(7e4d5432,e10e1fcf,79148f8,77a4f247,70f0e96f,81331767,dfb5e9dd,2ad2025f,35f5dbb2,6ee82837,f37f5fd7,d417982d,fa58f0ca,13e3136e,ec0b5251,63aa432a) +,S(c1ed7508,95c97e6e,7a78f800,b6eb10c0,ff21f879,352870e5,776bb7bd,9c0237b7,90723a06,8dc0aa89,9c31e89f,64ffec3d,a38e8095,97209bb1,d8b1418f,eb418fb9) +,S(e5fd4ae8,a53e809f,83d239b2,a15d8dd2,9948ddae,6956d8de,fc7ce730,b2104770,6a4dda9c,1cbb2197,208e8ebd,35f2ddbb,50c7bf5,df4c112a,de02f0de,f71c4fa2) +,S(3ce75f2,4b33bd88,7ab72cdb,42f9ecef,e3534d15,f6733f2c,a766eac1,da51f30d,9eda315b,51ddf6e2,62ce9b5d,828af559,a43eb48c,d3dc9362,13bf04cb,1a838ed7) +,S(98c04c4f,33a81cd4,e498d8ce,77edf598,527fb9a8,8e19dd34,ad8cef3b,8813391c,fd2bcd67,f853f812,f4371c46,30560957,acb4843a,8ff21949,483f0617,ff8bd3a7) +,S(50e9c88,2906cb06,cbce62f8,413ce43d,f3d2dd28,6e4141b1,fc8a079,a87bbf3,43c713b,b05f19cc,685eefb2,ba04474a,479a615a,2f3c8a9c,e1c6243c,d82fa3fb) +,S(5d41ba17,458bde3b,64faa66,3f21b76c,71bf6564,2a73e3a2,eeb8a831,43adf2d1,17effc54,b3082159,2b39e22b,7ab058f,7f64fa7,68763510,87cc445f,57b6cec5) +,S(4e332cbf,2a02593b,6e83cb8e,1d92e076,6b34cc60,646fe3e7,89d38f9d,24d5b077,50fa745c,3ab90300,95b69985,99e391e1,744ad03d,decd1955,c8b2a859,e79e1f95) +,S(1bbf629d,27ee825b,c357455e,43410507,c83cd47c,dddb53f8,34716ebb,35a2dae9,850908c1,e99753f3,4f40070e,c0bd85e5,8fb72f69,601d3709,71ffa894,3de9bb75) +,S(a9149aef,f9dbd62a,ad0cb3f5,debdcb30,23aece9b,dc79db8a,a25f5b01,b5cde650,a7bb4b46,e6db111f,468a16d0,9df686d6,ae3c148c,61d99a79,918f0bf6,b4c327bd) +,S(801009ee,185d642,19de5b40,6b21dacb,b98ffcdd,75f8037f,fc5e1721,fdb72a71,e8216609,57c7ad6c,1dd36f70,645c75f1,38283905,898650f2,daf2e5af,37e26441) +,S(c070e99a,e466f67a,b234584,66f132ec,96c6b639,3900468a,b7368a86,c0ba6770,883fabdf,358d4f56,8a56f77e,3b291fff,be6f8fcf,9c0f0171,a1544905,50769362) +,S(6be89639,cf612761,78f549a9,ed0af9a1,8e028c53,1dff79da,71c5ff1f,54109f4a,777aed7d,dd379131,26a92f38,ff9970c4,ff2dd6f1,6d766e0d,8215fdb3,9604cf14) +,S(d1fd31d4,1ce71e4a,2052e79,6dbb1a87,10094505,2b9d9009,2195afc5,9624ce8c,f357abd3,78a8029d,eaa796d9,1860e5e5,36d5f2aa,ea2bda2e,5b510949,253fded1) +,S(1c85382d,66fef14b,3cde617c,a426996,614c328a,ead99033,7aed469b,8f169d9e,58c5af1c,b236e6a8,4f58702a,b727d9a5,e2406a4d,fdbf9113,a8f895ef,78a71f9) +,S(2d8fa9ed,d5fd67b7,42e5c2d5,2c128ad7,eec32cb7,43cba12e,272edfd9,4f36b5ea,71b15f39,37eb2a09,f9533af4,bc964c32,23145340,f780731f,aa9ad1d,8f2cd37d) +,S(4f512379,d9f6c596,700d0a15,950ddbde,40ceb1c6,87030c95,4c538122,dfdbafb4,54de9b05,f864df5e,ba861980,c7dd8567,4b320afb,e0381674,c7f7c083,4248c05b) +,S(9a5f91ba,48562c21,58e60949,6d43eca3,2c69995c,ed93e834,168d22c,d9e7cd1c,9f88a3ec,c668eca2,e01b1126,6acf9c51,29bfa33e,e12f7638,2d40d2a9,b722b86b) +,S(dda30502,d417fae4,c4ccc6b6,e833b75d,d84d54fe,d58fcf0,6577dd8,52f9f610,fbb88dda,2e74641e,6d45468d,102e2a42,7f4c57a,6c7fab0c,5d4dbfc1,155469a1) +,S(8d2e9e7c,5586141d,848e50cc,631ce5f4,600dfb1,618d65e8,e88da5e3,5b26e117,989c39ec,ffd67c54,d55ab4fd,85c2a556,bb72fabc,f763c9a9,e33a1ed0,251aa86) +,S(726ce076,e6b63d6c,b071cee1,c3d1da27,10c911cd,e3d59bbf,a27a7ca9,641ee995,4e41baa0,c91ff8d3,4d2d01ac,d8ab5d36,6ea5e81,d489595,1c49622e,610af9c1) +,S(debc91fe,183503d3,fbac0dc2,c9e95062,c0d42971,ed2e97aa,27dbacd9,bea43a1a,13593715,a6fece9a,f33b2ab8,d5f26e6,6dfdf3c8,ff688100,d497a333,be2d2f11) +,S(c5394335,77914ab8,e7039e8c,472ff2d7,237c794e,d6b24f41,c585e910,f0fd4b28,2805359b,628a9c4e,7b304112,80aa2a0d,23c26cf6,9a47310d,1fc6fc54,cd26087b) +,S(9a2892fd,33c300b1,802bf1fb,e8a8faec,72b81810,4bee4e1c,c998bc79,a05f29b0,997f123b,c2fa0136,522ebc53,a5ce37cc,5538705d,f5fa8beb,b37f72ff,45186e33) +,S(d4a22cac,f224fbe,7ed7fe42,7e0bf180,1503bbbf,d73de79f,4c558104,b39a351a,8268fe97,30622e09,2a513349,548e346a,23260311,a82e860,341af6b8,3ace752b) +,S(4a4cf3ff,15adbe1d,8d414963,b0709fb9,11d4d2bb,877dc03c,77594fb,9cc11659,57c84583,36f1952a,62441810,ea64ad3,5109a30b,c39ff36f,c2dedd60,d1ced874) +,S(58c0eced,b580d530,98c836cf,42ca2648,e85e0b27,871caf44,1f69bf0c,158e1539,eaf79c49,1e1dbc94,4e63d36f,702cd1aa,92e54a09,fa11ffd7,4121783e,e842467e) +,S(b0fbd0e8,a60c7e89,38a154f3,fe586a33,26132302,7f4e416f,634cc9bf,724892bb,27ba767d,6757f40d,b54fcb50,104998ba,6dd83f93,137c3d07,9a0330c7,82a5d3c3) +,S(a94ae12a,26b0135a,125fecf3,e4ef1844,ee90c2f7,84cb4081,8c16d8c2,d992f0d3,a37447ba,665e7595,c7816a6f,6bba3d66,e046fe36,8877c95,bbc8a760,218e2050) +,S(8cbdb09b,3eb74fae,32b6c484,4f9004dc,c540e240,7305aeeb,9adbe308,8cdcbeb5,2b863a2,91f6efd2,ff07b14c,4e5a169,912df745,1b49e3b8,10e72cf4,34c932fe) +,S(3f8cfac3,b70eebe1,cc6fb89e,d508abd6,ab3131c,40d37afc,f8f39c87,34cdbac8,dbb02111,846ef25d,794b303d,b48964b5,a68b5287,b443d321,f0c493a1,2d58b65e) +,S(b61604e0,cea3e79c,12fe9a4e,1816630b,31fcaf4d,b3ac84b9,547da60,df15b25b,1aaf2bad,9ea0fa5b,56665b41,ba1264a4,317d5388,9018d92c,ea2e9d98,29b86801) +,S(eb0e7410,3c6ab38e,1c26630a,7fbd68b6,fa51bda3,64d1ca3f,7c86ef0,451327d7,dfa05017,57c4d3c1,9541c9c7,d9fb7438,45cdaacc,2ee377d4,4c36817a,e0e82e6d) +,S(636fc503,be51e64c,8dad2812,6762c04b,1fcfdd7e,4b25544f,4bcd0bf6,9556b7ae,57b83b0f,5104149,7f3b6735,a8b7d886,b633c8dd,5052d1e6,9848703a,313f4cd9) +,S(2fcc70ec,4d42235b,c6afc2bd,1d2de19c,5ba9dc65,4ea3e288,c15c7358,e52ab330,6a356018,2bfa1e22,db64cbbf,cfcdb695,35bde8e8,2519d069,39577e65,256a30ce) +,S(115006c3,8d38b8bf,db8969eb,d6bdfa95,83be81e5,bd5bc7d0,1cd10e22,7ea94b54,6043ad02,16eb3fba,9af39e83,f6e87802,3fac023a,73252780,2505a1d4,2080e08a) +,S(17d565a,15ba1c29,60ca2da8,2fe54d01,3756a986,bedc19a5,15b653f2,8db3aaf1,917bb9dd,42040b3e,4b8b05aa,b9d31252,7a633819,33534c35,f274ad7a,9bc7399c) +,S(dcc2489,88501c43,b8598f7,a3e5bca9,d2af8f8a,b482f30e,2374c6d,6b221ef2,a859757d,d5c6ec52,b1f102a6,ef00decd,cd14cab5,99f1eb0a,57864646,4f652e72) +,S(a68b2b9f,4e9430b7,cdd919db,6e808a7f,ae2cf9c3,d43cba03,d833fa8f,351f0b0f,e34b8a32,d3ad0923,8e95614d,92778021,7f45b14d,f2f9cf66,d6525bc5,94786526) +,S(2f954e5a,938e1198,5d2d11e9,ead59539,fe7e2f17,4702d3c2,3190829f,8d0e9462,b2f0d379,fa8b7d40,ba2fecc8,46a393b1,f6aaa6bf,9f89ac61,e55ab1e7,bb16dd5) +,S(223d3d13,6bca94fd,9b3fcae8,6bb2ddeb,3cc33c8d,3ccff556,fa93938c,c27ffe4f,87e19106,e317187c,c256f7dd,83d6219b,27f6cd8a,53bb4ec0,9c74ca58,51512915) +,S(5591d8e8,291161b6,43df3a3e,9b4f495c,b614432b,a0f3aabf,517834ad,9e8f7b2e,2c8d6a35,f6267e52,d9bd454e,bba4719d,30b3fe7e,c40a1416,673ef594,e6d07c2) +,S(eb2cfc4a,d15d1a14,870cbaec,b9e9aefb,4141841e,2e4e6252,c8e93751,45f29a29,78810e08,10bfd992,b7b04c79,c7650150,e62f210d,6d35cb95,5bc67215,7052e5ac) +,S(6112c3f9,9680a818,5e4b1d4c,539cc436,bdcdb47d,2e19f927,d5e3e9,eca3f74b,6e860bdd,723c8bfa,dac8f9d4,1f6ca96,f2579d7c,6a690f35,3b8da52b,98da4403) +,S(892120d8,d552d8b0,c42c23b,9a0f17c3,b9b32914,d7e31579,7b391ded,a7d58b16,e62523ac,b3c778f2,f913d1f6,4bdcc037,cc4a9496,ccf0cedf,32502316,b5cf7941) +,S(4758b456,217ad3be,2315583,963044a5,60ca4a49,168461c5,ecbd1e91,1fc4dc0a,99e4255a,4f30973f,57eec6a,1c30ac1b,bb74987b,3c4e7ca,9604878c,d9d83679) +,S(ab0422a6,9a207a87,97fa0c6f,978479af,a3b95dc7,d962e143,b99e7575,6e5629da,73398d0f,fc9855,ebc280c3,2b8dfced,84af0f70,4994edfa,7a06e2e3,a5823faa) +,S(1980432a,ccb90b14,30d0b4c3,5829374d,852cc7a6,f0a1b20,ee7ad43e,d545d227,96ae3873,bfbc8ceb,2afd1a0e,9b989bd0,69c919f8,f2e51546,7668da5b,9e8b05c5) +,S(4288137f,e2dffb3a,ca761cd7,2abbef3e,93b8a015,743d50cb,b716252e,e0e93f17,c02c333e,36c366a6,fbd855f1,4da07672,71fddb04,59e788de,456086dd,aa238825) +,S(d65fac35,5bc89da,afc4d16,aa73fa4,77e43276,e0db7d3d,52d9281a,d6b6cde2,e6f4c975,a6c5f337,b4500545,603862d3,8e9f07e4,18886b13,5678478,c007383f) +,S(7029b144,e62dd27a,4fe8b54e,1607936e,beb96784,d1b294c8,43e19978,b98f6ca7,8b10ed66,58056c6e,3a2c90c3,855444f0,9a1af347,c73dd14a,a478f2a6,7140a25c) +,S(4f2760eb,df969bdd,e80ea87d,a5da0c19,fa343e4e,44715711,50295b43,cf9183b2,43e2c17b,f3f9ef09,fa01fd39,b8f69b69,15d8625d,4daff425,d4629677,83e28167) +,S(cc996aad,db4196a8,f355f0d2,3b9fa539,b573ce57,f2ee7e52,14372615,c374a5fc,b1c40f87,18782935,c30a9cb6,6be85d11,44284bad,bfff9923,2c09f2dc,a8ef28d) +,S(c7818ad3,f3e3d905,b2c38e39,85eb7091,cd9b3285,3bdac631,3cf4303c,d54ade42,55574c4e,7bbb850b,14763fb0,fc1443cf,3403a5fd,d1f5f937,8007fbaf,94339eee) +,S(6cd03584,6fc48ebf,f4344d1f,3ae5d258,9a45dbc9,e779445e,52a75737,2589c4fd,afa85b7d,b165e371,adf67dda,8e4d1fa9,4d811c92,8d2d4723,a44e8cc8,93416e34) +,S(d652a205,f716b1ae,632b7235,91152314,f5231b92,474dcfd3,bfaebbad,443b2e0a,d2198421,b78240f7,cb82f0d8,5de03d8b,c4646a23,ec166dcb,a458a06e,b93bedcf) +,S(a807d04d,cd844a1d,f99b3e4f,e3400459,81ff0baf,d34e2d73,a6a052f8,dc52fb54,188a78bb,73939835,3bbe5079,ffbbc718,c385c047,97cfb151,af019e9f,a2e2cd9a) +,S(1efd2bbc,a5d6ad48,e93acd07,d6cb02b9,b8c3a95e,9f4cede2,24efdc17,cc9eb2d7,f951ef88,1b98c7e6,357e191a,1bb357f6,2a9d2ae,a8af1a05,63eef3,66745b39) +,S(5bc5e54,d27d3db5,982bcd04,497fe0e3,ea0ff13,7c3bdf66,6ed0b9ac,2395888f,3555bc02,d4f4eb01,661f8b85,7321b36a,5a22d975,2710375,4c1f65f2,82ec3300) +,S(f600a8d,26b08d0d,e6e2ea80,e8ad2f1e,158cca71,15be6f22,1f5b808f,746a3247,23be5a13,4bc3fa5b,7ef75b2,8e8e11ba,76c753dc,6516caa3,cd5b3ab1,8bca3523) +,S(fc7e4e0b,2ab2a7a8,cc5712a8,be590135,f5119bc3,6c73f81a,e492bd26,90ccaf7b,4bd612f4,1f274690,6ea862b8,a6dd8929,e4a682b5,aa835cdd,b31c90ab,381151a6) +,S(51325f3c,fe12d00b,fac67eec,d79b63e5,41641d58,76aa9700,bcd3b3b5,89442655,d6a894ad,7b7ae6e2,db962360,579657ff,982a35e5,91cc3b33,a4437a4b,133ef941) +,S(2db3ac27,1a55f608,f21bd2d5,717f003a,afee9a6d,b8fefa09,5626e4a,c598aec8,c526bf33,a0c01c60,c1cc9fc9,d66609f5,3c662168,b47cfdd6,e0de0c88,9bf9533e) +,S(9080772,93deb01b,5eb05b55,47de4321,35fbd436,3f391be5,1e1d205d,d5fa7dcc,fe961eac,17243337,653d318,b5c62159,64dfef84,d3cc3f23,e034c102,5ed7442d) +,S(32a1ad81,e5a64fc8,2dacc03f,6790cd4f,45557ad,3d3597cc,e2ec37bc,339ed9d6,87abcf68,18010022,7097b63c,a795cec6,79cb55d8,a9076cf3,d1371062,627cb07a) +,S(f7cba8fa,58689c51,a0f1af76,971d17a9,8c5849d,55b396e9,a3771d17,4d9246f9,f4b0aba9,10d5a9ca,eda4d733,35d5fba2,5309a0c2,fc83072c,ac37b9ec,af58ee45) +,S(cda6b104,2da900d9,d65b6b7b,c0579a43,fab75f55,ae7046d5,8ee8af82,6329ea81,70c86c0e,acfd7401,5a1171a0,1e3601e1,a8ee20e2,9ac7c0fc,68d6f1d,cd1771e3) +,S(a261a640,9c5b1ce,9a8f27b0,544ca0bd,a46f3bca,d11b7f2a,c23878f0,f67ee58b,1e26a71d,4c42dae9,b4e9cda8,fd33d725,85da67c3,52594d4,20c6e410,f3fff888) +,S(bc70c967,fd7f842c,c018e885,d1fb6d34,9435aa3c,682f6884,6b394ba6,e9f4db57,a2b99d3a,837e2de0,c40a0784,7865701,3032efc4,87c47e57,18d84e11,f8bd9468) +,S(364152f7,5dc43149,4cbfdba3,ffaf234a,c7a26567,1051e026,9c60389a,7077a9c2,ac3ac7d9,f4ec8543,3038ae4e,7ede71d5,3c8fa601,25bcd8ea,e084c32a,546d265) +,S(52fea4be,d57d5d58,d92f59d8,e4022df9,d82ca42f,a3d89326,cc36dd96,94f3043d,b7e98328,52f98a75,f738f9a3,7a67b39c,5708c608,5b6ab99b,2495428d,8c4feb35) +,S(34fee910,c8bac880,f973a096,a9ac3ffb,9c32ecc3,d2be3ce6,7b8d6822,863e1a4d,e0f01096,9c9dc446,c78db14b,122e260c,2231f6a0,971d08b2,24355620,689c7a17) +,S(6af28f0b,f4c9d2e8,192424ec,31f076f7,f815ab0,f476a4bb,4019b3bd,db49c65b,2c55fd2f,bb663b31,1a9aa346,917f6064,17d1e1dd,bca45d3,f1350feb,6ea45248) +,S(70b6ab66,39e4b41e,644f1b6b,6ac5d36f,a4f0ad81,3baec03b,8bc74f72,4d320e46,ec6409e6,4f4e4fcc,d19ad5f1,96d3cdd,96f7135d,dfcdec95,23780c18,8a913a14) +,S(ede56f11,4ab975ae,adbeff50,ff7dbbe3,614196a,138600bc,15508481,a065724d,9e7fae4c,11f421ed,26d839dc,61b0684b,71906b63,6efdd73c,2dadfc9,5336ecfc) +,S(83c34c3c,840a7310,3d819d74,66648e66,5de54af9,f1683efe,6857c3e4,556df8a4,9c238041,562dd6c0,eecd9771,9ce2eed2,90aa8137,f109840f,d31d70a4,c86cd10) +,S(aa2afc90,89b98,5228cf03,205e87a4,52cbdb45,24d377c6,1020593d,a0c7e200,3443196a,b382ac19,d3d3bc4f,a3161e00,6d812924,fe8a86af,2db5cc27,96be01f4) +,S(e42ec7dc,4310cc8b,9f2956a0,2fbf71fb,e90eb045,881ee800,e3a75730,4155c520,939ef2cf,a52d91cf,e20324bb,e8da54d9,2d000945,58abe7b6,93c2679c,d625336c) +,S(bc0bdd24,937c5200,fd78de47,a16ef9f4,181617ab,267fddd3,4a3c5606,32ec2b21,db04a4c,10b521f7,986e0e1d,192b0870,4a028511,205d7ac9,df6d2993,52b63035) +,S(dc88c9d8,9b43a733,5e310fbb,3f760e29,55c005d5,640df990,6264ebae,adadc8e7,2d6779e4,6f984794,65bfc677,79feb7b7,3fe37b32,cc23d636,25a3c8d4,fc2fc512) +,S(a7ae0d8a,b0c932c6,2ff0cb02,e05d76ef,5ee3dc50,88c57210,f6f9e349,80111c20,d632abc9,d63162c8,98b9dc94,b321de53,ac8dcc45,69233cdd,1d07da1,92e553a2) +,S(4620236e,e7bd6eab,db203af7,57da6e64,d631ac9b,a5f03215,592a5acc,5cbe0e97,5df4244f,69ea1e2a,6cf30cdf,4fae66a2,707851bc,7028d8c8,8fa97690,28627c6e) +,S(804cb84a,3d8bd930,51f50aca,cf932301,5cd35fff,349d8a38,e2bd991f,27d8f671,4b77b812,9ee0f835,96ab2f19,20362128,cdc39552,bbe6d267,bbc9a8e4,70ae8d3f) +,S(6ea5370f,5382b2f2,b801fcbe,ea463912,b4f6fb4d,e207237,8e71dbbd,b91c2ed2,f5b4f909,d8f3ff08,e92a002,f1959ec8,8b513ee2,d37825b1,a8bbe141,90b2d88d) +,S(d8b8de1c,4ff575a6,12bb0852,af61c6b3,1ee56403,b81a1f47,69fc7072,a2e22ba6,1e3c4190,d37abf89,896ad827,55c256e5,f23cbdcb,47b05892,8d538df4,e2468c0e) +,S(66ac1864,4a0bd6e,9c73f3ea,43a00d6c,80d38467,5080a9e,475c8291,edf1b147,2cc60c0f,7db6b776,1503efd,551e1a36,9be98ec6,73dab0bc,a61881cc,4fa1260e) +,S(47194a2c,4ed223e7,7663cf9,38b5a5ca,f2784435,c375d85b,ef3c5367,3f60e942,b0fde11b,5a64dc13,7bf2738c,75027d57,5808a3ce,abdf7604,5f4484c0,d3d9f920) +,S(1141c859,9af2d983,88274742,88dd768c,9fdc1b2a,c5f06290,10bd3cf4,2a4c6186,7a431f47,61ed4613,3317555a,578d0f8,5392a0d2,ba98ec64,d6b508d3,eef0226e) +,S(f905bdd8,e80afe9e,d65da689,bfb8e549,487002d6,16ac651d,4ba300c3,e7496f79,1ec7bf7f,3bfb5045,d461b03b,a7f05067,cd999fc5,f9f4c0fc,a7e25b19,77bfcf72) +,S(ba54b8e4,fba452ee,23c4faad,b14f0af8,232ea62f,ad47a3e5,573f1fac,f15e9e0,af556ae9,efcabbcd,bbd8e48a,82ac80f3,9c96ed0b,11370b8,da1bd4a0,60f5cda8) +,S(59d3ead0,561f6e24,30e87f3c,f361a2b4,1494a042,3a775e9,c08a28b4,a26a80a,9b26f47d,50a92229,c28a278d,427c7306,f5f1ba1c,c2d095d9,ac51e18f,a9bc8) +,S(2a6e5a3f,ab4cc928,d8f2924b,e7e0f3cf,15520a08,eff00f5b,129d9070,ac865d47,55c0350,58727023,86d19196,4609351c,919913de,e93800cb,38b77183,5ca625a5) +,S(898f50d1,c35bee41,88afe91,842cf659,fff1fb1c,5aaebd84,6828b002,a8907502,b2456e80,c97c2ad0,1ae02e49,6b84c02,fc9bd0f1,4a3c5a79,dafb8f8f,62f3267b) +,S(d1854ab8,b49bf0cb,1f9d2304,e9ee3cd3,bda5d9a3,ac04fbf9,422c4671,d6af85a,4e2500f,d8818180,5b16a077,53eddf34,d7bdeb02,17df7fab,d0afebe9,47155c20) +,S(861fb88a,394db6e1,f03cc787,d968ef7e,4a417df4,2856d303,a3edfc66,feaf4680,a5d7e95a,ab8b0b13,23fff11b,638ce02,68f3c38b,dbc018dd,dc6d2362,a135da6) +,S(9544f768,b9fa9dfc,3c155d90,b676cf7b,ea526903,39fc111c,b0ccf62d,813403d6,cec4d92f,cbde624f,851ed21,bbf5154b,28d87603,5e302902,53e99670,806e047f) +,S(f7722192,f80a66be,23cd7b07,89f3a094,311cd40d,3b7149ba,2cb5cca8,d1db68e9,41d0e9f,3243d64e,73e26661,cc851e5,1c11e320,277f7462,d40efe0f,c81b516e) +,S(876b4c65,1f88729b,dbf9927a,89ea91e5,b6bd8e7c,439f6fc1,be9e07ad,698e14ed,ece2cf81,1cfae9dd,cf787b88,f62f2537,7b573084,86ab84e1,6ad31ddc,f96b49da) +,S(706af4e4,62249924,88461427,8917899c,721be8f6,3353b127,b7ecb03c,7b70b10,c972d972,c65d200,e695bd29,813deb74,cdcf0ee0,e612f545,aa6506e9,7e58a5f3) +,S(ce97a218,c9c96262,b5cdde0b,a27e5f4a,4301b75b,f9d17dde,f1495b7d,f3863c9d,e9398e1a,20fd1426,88916410,d7af68f1,c12900b2,72b03f55,c5770aca,2a1ef10) +,S(fb625b9a,92cf831e,886a1f01,c2db3d53,7dae0feb,e9b3d9bc,a8fe6449,d53be895,7f783e74,ba569e04,a29d68f,12cbf223,1fe2a8c2,a651f44c,30d597de,82599a37) +,S(81ffbf69,7a7d2b8b,51b9098a,8833244a,f126c6f3,b42ddbc4,c6b2c5a5,55f4747d,e2c67357,982eefb7,3213d530,3cdaaa7d,90b4d1de,d0fd4d0a,8b002162,f006f33b) +,S(6dfc0f66,8c30ab7c,af16d4d6,309ca2ec,679d6b77,be695ba8,c0f31b55,6a0ed0e4,a41ed2b2,d9a0aa74,3182d7a6,20f441e9,36772438,bfddfd90,5aeff87d,b15a563c) +,S(1c6bf1d1,b953e56a,ba1a9b5b,f63c328,1d5715df,88bcc23f,e77f94c3,3b501b44,c667214d,b4ca1782,19c368b,f7c2ba4a,8f1e74df,91a22d3a,8e336089,e4e4b0ef) +,S(b69112df,5d999f60,5d863231,9144d2f3,5417b9f0,d1e78ba6,b2afd247,fadef93d,2163cf7a,302886e7,70fafeb,8acaddec,464f115c,89422020,9a034c63,558df4ae) +,S(bc038732,ac230f91,7c58b8fc,f9ef9229,9737a2cd,8e01687c,da464bde,4976b6e0,3392601,796373,603e1d6f,a7e23385,7a7ae0d9,df638ab3,7191c046,a25682d2) +,S(96df3295,1047ad4c,6aa4d9ae,e59bb00c,7489c20c,aded01ae,1833ebda,27e32d81,418f6c2d,74ce633c,181189bb,f5d0f066,cbfaa8f1,e6674466,1818c4ef,e2e2034b) +,S(a3c53227,939e40ab,ec6fcefd,eb6193c1,cb6a65fa,2f3501d2,63cd2fa7,6a304979,4468caa5,498ea6f1,92f29ade,37a2f667,f46798,dc600de8,cafa0ed6,dbc967e4) +,S(93fd8207,3dfd6d6d,6b85f1b3,1e172a61,7a0c7d46,b91d5a92,601af0ad,2545eb2b,601beac1,9a8a6a41,1bfca863,6acfebb3,9611559b,20b8a866,88397d47,d563a847) +,S(ba7d3bef,2e2c63f1,f9acef,2468ad8d,a5bc0d9e,b45e4a2b,ee87a0ba,86ca0a9b,ffff9a67,33129e31,10d33ed0,bc5b6bd9,376f29b1,7bbf2e09,7c6efcae,a55114dc) +,S(e0d955c0,2a4f9c35,97597258,2964d7a4,218898fa,97ef3011,1904290e,36157a8,176fdf9e,7d420bb9,a0782f0f,8707789,b28cbc69,d5a6797b,aa2bb985,1c617f0) +,S(16b2c140,8320cb93,112da689,c690d6ab,6cd226fd,e1752dcd,a444750,bff81ac9,2a2face3,dafd3932,f2617488,faa17b6e,5025f792,53c65edd,2abc7790,d98bb281) +,S(2fbb0432,9c823e2f,645d61d9,795a414e,220db4b8,eb4da0c7,fc22bc10,e9f502da,1adf66ef,15eaddfb,abbcda69,c87068ab,cbfe58e,69f58246,a0ab6259,90ebc31d) +,S(649134da,f42d2579,b6522f2f,404d6456,c178f883,8c9ddccc,e132de4e,67208ebf,4385abfc,3c554bc2,78844648,a9aea5a,e7709b51,fe644e8a,4a756c3b,f09355cb) +,S(b95544b1,dcb81f9c,e0601c17,29c7fdef,128d62b0,b8993023,dbcedf8b,d3991cd,f871fadc,ffbade32,d51f2d45,4f0726f,206fe760,93c0ab0f,33cc5383,769dcd2a) +,S(cd403bfc,5034d0c0,a23df5b4,a4b1b096,ace1ea17,288f89f,d063c0ca,5dd668de,c1296131,aa56f46f,49f59760,f3a4bf0e,3fa92e54,a5d8f160,54ee1fd4,26717742) +,S(a4aaf37,d7f8bdc9,90202074,c9a8f56a,1ad78479,95b4d939,4b6ea478,3c04c581,4cb3b6f2,dc02b61c,fd2594f9,40773d73,1a8cb9cb,388af431,9348d3,af16da34) +,S(57102ff8,ebdf048e,85edd3c5,2864578e,97478fc7,e6e7b937,6c37d9f8,abf438c4,c84f1d9b,eaec1962,e79e308c,251b2f31,915aef16,90ab5128,8331d8ee,f8161d4f) +,S(68fe98e9,3c5c5fe1,108c08c5,8fafc701,12424685,c6809774,832b0623,a3c9b6ac,492ac417,fd956b87,ee8078e2,c46f21e3,cb5ea58f,ee871cd4,d6480cf9,c4df273) +,S(d4bbb6,9ef5e23a,f0932cef,2eea4ab,d97baea2,b0893d43,ec08be40,2a4a5e77,5818e60,9c1dae31,f65e223d,f0cd08c5,9e74884c,2a2dc166,607fa4b0,47c7c98c) +,S(e9627957,a8eb8b72,7f0d872f,28acc53d,23a5c53f,763f3b5d,68c7e690,5a83192a,5da92e7d,dc291696,d9466d17,d4ca6cca,cab1057,4f5a08d6,6e59ba87,cd0d984f) +,S(49f21cad,a83bf2eb,7c508ffb,70f9fe84,1cd547f2,df83f207,6dd2382f,1711caf5,1c7d8129,5f834ed8,ac73afc2,132e6eff,2447611a,4fed8114,ca18925f,39f31931) +,S(58282a9,40f61d53,14d1d110,c18713d4,6c34a8cc,5b0ff64d,fd6d0182,aefa792e,7855e155,d3e0f2d6,52370145,7d83d7dd,2a96ee49,3eac091e,677f0a34,105f6ddd) +,S(c9c97a32,4b07027a,7b5db6f1,d70bec7d,dc3a14e7,43d492e5,f5569199,23ca118d,f601f40f,894ddfe1,c2b1e99f,7c2032b9,15d600d9,e2ec41e8,747d74c6,9664b56d) +,S(9e4dc175,8ed67501,86a7bcdd,a11f9273,9bbbe0cc,aa72862b,8d515a21,159e054e,ca11c31f,2bb866a6,be0736aa,19b7e36f,fcf4f4c8,715ebd3b,c9483813,cfc6c0ab) +,S(fd063789,3ae0030b,cd8c1e5e,8cdfbdda,8e5af1f6,268f552f,7a7f0c6b,8aafe2b5,8c09eb0b,68d744a,20468a8e,4326dc01,f4a35480,df1a0435,b2e21d53,dc8e676) +,S(37dfdf3b,6e7a7651,9ba81c3e,c1377580,d85360b5,b466ec77,b3f2b272,fd4af04e,cc3137b2,7b732758,d78f3b07,80129e77,43dcd85,ec941727,ea3b4f8e,d0fb7844) +,S(45ef106a,c8a5e25c,771a2f18,bd2be0c9,a69cb49,de16d8ff,e72a45d4,6c1dbae0,cad6a40d,742e7db7,3f7bd452,e0163490,60e5681b,31918bf4,2ad8b698,220ab158) +,S(258c52f9,88b9ca25,73cde71,472bf58,99648cdb,c1052d14,be02469c,11fdfca4,4491d88b,26ea5c7c,6a66c9e2,1a5b61fd,d60d0b8,8394cffc,359af9ec,29939dfd) +,S(bc92a4a7,727c4e36,3d0cddb9,d36f1d9,d4f8ff67,de881c83,afd51193,a9e65217,76ff6a08,b18b4795,61f1c024,d464e9c0,331c22b4,ba3779d7,9dd13122,63aa5120) +,S(8a19e8c2,58c04834,f11ceccb,c156bbe,23e4e837,fb5ff353,249c12d1,45f27ebd,15fc6c70,bff48ea2,c56caca,2b978fe1,8f50a9cb,85c19f06,5d65e507,e4e92c1a) +,S(e59b82d1,ab90344d,b113c1a7,a39968bf,e6f3b1a8,c2a44572,d9c911ea,55d70eea,91e33d8c,d58c6a1b,e4df0bc0,12eaa76f,6634d699,814b80,cc22afc8,313b91be) +,S(f994ee5f,88244cd6,bc417db7,2f941bea,f3456d2,5dacbeb3,330031b0,7371a7d0,9026f86a,ecc299b3,302bf9c0,4dae48f7,d9c2689f,42707d40,51440a05,ad9c62e2) +,S(776c4c8c,32d1fa50,27b17bc7,9803d121,8000cccd,565d95ff,c1e3a693,7800410f,40ece234,438e7738,6f8201d5,3d3e319,988b64a6,38413803,165f0bad,c8785149) +,S(393675a5,dd13b001,29caea3d,8bf25730,32c1968a,c5e02620,fb83fae5,b5c9b12e,39e6cfc4,793400ce,ed16ff4c,4b0ebcc3,2e5dc773,4e742dc0,3784a69f,a75a8b00) +,S(d7cdd294,22edc7b6,2cdde130,e90be1fc,94dcfdf2,7bf38eaa,2f09fa8c,c3401b1e,dcb9cb28,73cd18b,b74ca141,dc79eb46,728bdd9,8bf14302,db5c7065,354fb184) +,S(bce3f127,2726449e,3f620671,824949d1,9026137a,9885a1b9,9cb67dcf,6e56bc2c,a4240017,ee92dda4,be968f04,ad125ad,1c33c37d,c52a497d,296d5786,e6e76217) +,S(9df098cd,efe9414a,79adaf44,21e35faf,70080137,74399eb9,738dee97,ff5d6e52,6bf9235f,ef36943c,d0f17bb8,6917d97,f04bf4b4,dbef1baf,3b788eba,51abf15d) +,S(9958582e,e561dc4a,9a4839a8,292713a0,5d88ab4,72222522,e8dcc94d,b3d43f1d,e0958a14,b63af23e,86f76cba,2259b37a,850008b3,43b5cdc7,8cb4e88d,68c226a) +,S(3e7fb8ce,7d7b279e,7d292447,c88c048b,dda93379,5233a871,bad933c4,6dfb1f24,4279d08,34108239,2d77a5dd,906ba4be,e36352aa,9385f09d,2296ecad,648806e5) +,S(1cd05450,f7405a11,cec93ee,7826cf58,97611d69,fcb681c7,1ac0a6a3,75e8ed62,85bbc70,bb52b040,d620a3a0,20b9536e,5759d35c,9931936b,39a203b,af34d0f0) +,S(5047776,32145a3d,f6f87bd0,1b81032f,93cf99ca,fb4c5e3,c76adbf7,d7c9b367,7badf59,919c7771,89eb97ce,6c71f418,a0e67b23,884cbf7a,1ed60804,57d1d2c3) +,S(1ea912c9,49b64ba8,309b8d87,ad1498a4,79fd302b,f7f2f0be,636f7a0e,4470e6ea,9b279fdd,b1211de,8414896d,3502ae8d,4acd50e6,cf83ab4d,29086d0,42b7650) +,S(9b3fa0d3,4b00076b,cb4e0b00,95f03c4b,e1ed329b,d2f4c7ac,8c03028f,21a01628,10117a48,b2a58ef5,2e0d5dcb,cec661f,8cef81d5,50efdf41,26e9527c,27d420de) +,S(65fef7b8,1d39dc4,b3ac7f7f,5bad14c3,6200d128,d0257e22,ff678fc0,824c94eb,3021e1ee,680c4fc2,77373b9b,36ef276e,f2dcd6ec,7a02b2fe,3259af09,2708b475) +,S(3e77b23d,af763a55,644c0f2,bc2bef5b,6ef5e1bb,8fdedd46,c5a2917f,7f85f9ac,55b278e9,5bb07879,9c7f0766,a2db2c79,8d4fc785,10fd021b,415b3e4c,2a4f3221) +,S(2ac4dabf,2fec922b,c4615fba,1cd7d352,f69fbf8a,3232f783,96f8c08b,21de2965,2c33c017,76238bcf,2be6018,7f85a518,6d649425,1eff089f,a92f3eac,219f957f) +,S(febc8a20,67da04d0,855feac7,42b2cebb,748be0f1,97e31558,68d54285,4d62c66e,aee39287,3d45e63f,f9a9d583,e105a771,e3db346d,a262bbd1,a554a8d2,65d96083) +,S(a41174b4,a5982751,88c2d10b,d5c5401a,2654202,dc4e38d3,e1c8f689,8606a569,bc6b63b9,9fb5f85e,c6965337,e82b651d,6b589f2f,8d90f67e,3bd087e9,1f732a00) +,S(122196cf,5d10a3af,3bb97fc6,d12f749a,907c194d,3caf2ae9,3a3a464e,5a5de220,ccf46742,b3c1e213,544e2c7c,59423fdd,674697c5,d405b2b9,46fc6e92,9304e533) +,S(77bad5cb,104dcc7,e1bbfaa0,8c867761,9f93ec61,e8e3b73e,799b663c,2e1eadb3,889747b,ad8bdebf,48e1214d,df2522ed,4023fd8c,5ef08fea,411e8609,c6faa3cc) +,S(1e8d4075,8c49ad41,6ad45163,73327e6e,4e3f6c89,695e15d2,f5517ebb,12d4a98b,1d16ccb0,730a619,c9b379a2,ecbc4f83,98d94aa0,d4881cd1,25968160,e3b09f4) +,S(c87a656b,240241dd,487f4974,afb5b535,880ff2ac,f4a028cf,b1869fa5,fbb6ee42,16f25f3a,6fac9538,5ed74412,fa6a790d,34f694a8,b4c99cb7,6ddaff70,be757beb) +,S(96b01407,482c6e88,e9769944,cce16a05,669adf16,e7393b80,71fbd9c2,4b55a4eb,b32be280,590384f0,e08d68de,38688018,82b786e2,1b178ca4,578bfffb,94b50223) +,S(120f68b3,739562e6,81e72fb7,46d08f54,b2afd162,22e1deff,7ebbf884,6dae4af5,7c1eb74f,2b2376f9,477c6729,bd90e14d,2f4da9d,615c8743,dedc1690,300bcc78) +,S(e64871ec,2789b455,8608be2c,f248fee8,49a4411d,285c0989,b65c5dc4,e6acd157,2980b2d1,4999b0b5,5b79ecfd,45ef91fe,f3dbfae5,d02f2145,84376294,58e10a6b) +,S(608b1009,b20d3e2c,25324adf,c73e5c86,43daa1e2,53a8d266,eb43863,aa6798ad,826e5a03,ad5c9638,51181c68,ccf0e663,a021e13c,43efc38b,3b4fe2c8,8d0e13df) +,S(8c978155,8132e45f,60a61435,36f4c1a8,cdc4dc2b,6850b3e5,7c65f841,a1bb152a,ca3b3098,394d81ef,a9083c5,3864f194,1875d63d,9c31b44b,a6ed4137,db7460fc) +,S(353591a,c73b2482,f8d8e731,ce01106f,10072757,68210627,f80c8f8d,9821bf3e,2b7e3d1a,501ab469,ac6306db,2608eab,70ae8981,9f44bdf9,d4f84c7b,41eb6412) +,S(eb2dfd88,ec357372,ece70a67,178fce32,3c387b7d,a9fda978,b05dc430,9cd78857,6f2bb6ed,1f5a5521,c441a8cf,9852372e,3dd7aa7f,ca0a1f63,18c47ef1,a24b88e4) +,S(a41c0ff6,5db1050e,6998f3b0,7eef66dc,abd72ae9,8672c487,b29ff732,992eb279,56fa7b20,5616ff9d,f619d7f8,65210727,bc0786eb,2aacc244,ba951eee,99a99cd4) +,S(8e748b35,f5bb1500,ca45917e,859df11,615d85ad,861bcea9,41524e57,a2217e92,55413362,e226c6a7,84bf8604,3973bc50,825515a3,96cfe66c,9ef75e3e,fca46df8) +,S(a83f6007,cce70624,73cde2c7,b7ec7bfe,bc506c27,ccce7708,f35d535c,26dd17e5,ad48ed2d,af217e5c,63397a37,9d50d615,5a1ac614,887a2cbb,26c3b74c,626af268) +,S(f9ed7a92,7e451624,9380246e,bfd397de,5cf208dd,b47573ab,f118afed,1282d2fa,c1574616,3a6f30f7,80d5d53c,670d380f,3c84407,b4be5324,87bbb5d,c8237b70) +,S(a2bde78a,175ec7a6,7f917707,8f09f812,c7dff012,46c5255a,f0112672,6c2ea08e,7125698f,e82d4c0b,6d7d2741,9dc503f9,3b5dd62a,a5db9e20,b091323d,c9ca12ec) +,S(ca8c05c7,9dcb6736,905cc0e9,b8e1690b,6b37da42,e1875a56,e62ea231,bf9899b2,504c51ca,1e19adba,66560c1e,3fba9caa,2751259d,71389996,41d0f19f,1487534c) +,S(fc106ee,61db13ac,4137ecdd,15018278,61fd4b21,c979f97c,617b019,54eca02b,6da55a28,4fc26638,2afce9c2,cdc5a2c2,971f2cd7,5c92f08e,6bc99651,7bbef3ee) +,S(2cb21839,f47b1068,5419a321,7ac7846f,504f674b,c2f8f90b,8b9b6a71,6492a8ab,1feb332,ae75044a,f6807feb,965e2fc8,63726433,45a97af,f1081249,f00a5757) +,S(678c50ee,759c07ff,fca2963e,fba55edb,56ba3572,79970224,5d4f2132,5561f3ae,1989b787,abbf04c1,c93afce2,d389e1f9,780b1657,daa77107,c498bff4,aecba96c) +,S(ab63e1ce,aa2e34d9,8215097c,30b2f266,75d407ca,41f3dda4,1b21c910,824aa6fc,ec65be61,1150ff72,715d3da2,c4541ad6,812eb7e5,7d0f4d82,4792556a,6ce2363c) +,S(a69399ca,fad59597,5a639a2a,31264983,ec567279,b052bcc9,f5c919ae,903dd336,b525c659,c30c4f4d,a7a799dc,c93ae348,1c87304b,63491755,1c43e12e,c50d97dd) +,S(32bf4282,507bcf5e,d25cb5ed,439c82d0,bd7a48af,d3be41fb,e13753bd,c0fc5f3e,cd92017a,4336d9fd,25682f90,be347e82,6773c598,1e80033a,c20bb695,e79bff2) +,S(a1fcc983,7e720096,7203e2cc,11450716,f2fe6484,f681b2ec,5e42e9b8,eedfd862,1e16b6c3,17e8fb89,dfffa79a,722de49e,a419affd,bc17fa96,4ba6c316,31ed4fd7) +,S(d85a314e,69617e50,18df1e82,21cf738b,60485cf3,3abe8838,472b32b9,7f0ceae0,71a0ffee,e92dde66,66b4b5da,a88a79b4,fbbcbacf,3308a4e8,6e1aa367,e00a52f1) +,S(189226fa,688c70e,415b20e3,1356eac2,f5a53b5e,8043e24e,4ee03faa,f20c2e7f,d9bfd8ac,40c7d53e,7bcbe575,9987cf62,fc2bba7f,952e4eaa,1a0587de,f1515aa6) +,S(d3542e0a,6a931162,d7a674e6,32cf2135,32465d25,4d2d9241,ff70709a,559e1330,25e8f51,e2664234,34cbe5ed,622bbd83,dbd42784,4b1f2bf,9a079917,4b57c369) +,S(514d9e86,fb2cb612,1ec6e6b8,25875856,9a8c0a5a,adbfe45f,2876be18,753a2bc5,700785c0,be5423c4,341918d9,f65f79d9,1b8370bc,48629791,45ca8418,57cbf261) +,S(c9f86365,12e9ccfd,e1f1db33,5ae606ab,43f9aa62,2d723f6a,d6d684a3,ea3fe495,30d3a631,3868a01c,9c6f9445,ddcbe3b9,b7b80b58,f31f5db3,a3b3beb2,1e30270b) +,S(226f20c,8fc089fe,98cd96f,a0e790db,641200a7,2b908768,357d206f,2c2deb93,2e23fbb8,b43e9362,3fc2045,24b43da9,5e02626f,ee180cff,1c03b95a,aa5f66bc) +,S(a1b27668,d4b6f94c,7219bd9e,6b8a7e61,9fcc79fd,88d9bda0,a38496b6,cb5350bd,3933fc5e,b6e1c16a,e7aa62bc,200fd5ed,c96b5f74,b72b9b94,2644f32d,d6d68a32) +,S(10f13657,87bc31,1f7b7181,808eb0b,57dd4e7c,1590dae3,8c40df7e,afe28887,1a52e487,9bdc3ec,4e6c570d,91d4edce,291338b4,a79a3995,db5c6e85,e3366d84) +,S(8aaa0ee,a984f583,f9f55861,500dc5ec,f613b3d8,8f989884,273e1841,cdbe3e46,52962154,f85277d,c02c46ab,c2f246ed,24e56458,156d97b8,f2197d22,a133260d) +,S(32805a71,24b1b7,9b397ce5,f7a93a98,dc9472ef,a5b8b4aa,3bd66643,f1c89d2e,af525c7c,b7c139f5,cd2d0a85,82a74ed6,9e23c765,c0b882a2,f4d50e58,843091fd) +,S(c72f10cc,909ad449,7fb0835a,4f3a520d,58db0c2,87f6ab12,cde50749,a9412451,a5666e7d,5197ba9e,37d5ff15,b64b60f3,b8e2ca32,67467030,a7a112d6,f2c7c65a) +,S(6ec768c7,870211b2,415a74ad,c203bd41,72c0fa5a,d4c05f97,c0320865,77fc9a22,18394a22,aa2dbb0,925e6710,549c8c3,b51cbf42,db67d4d0,5da260ce,af1b09fb) +,S(de36ebbb,ff99d156,60506143,87045fc9,a846e7d0,eef91e61,dc93a71c,3ca64737,c5817652,f3cb37b,40647dcd,a476c4e0,488208ba,1b4d0c81,b39a4b4e,900f4270) +,S(f534155d,5538fc68,5ac01ef8,6dea6f4c,19bea322,b46a297b,c0c20ab7,3cc00218,4fab7df2,979d98ca,eeeb38a4,9a2d1253,f9d0924f,14dda603,d3d5706,b3b9523c) +,S(dafdb850,a1db9025,80ecb98,155b687c,8db2088,d0dab521,bc7fcc1,3286129d,43965ffc,ae6c4c14,3febe601,f36fbbd2,7d0ccc50,60ffe91a,a2ef2fef,651ada22) +,S(ac9a0a21,1dcabc85,1816893e,e33cc69e,2efe4069,680b3721,2999f9f4,99d705ce,16ebfe40,8281d2bd,ffa1e02c,2d00d712,acf64eb1,39f5ba88,79c3971b,98c728ef) +,S(afb37bd4,b48cf6e9,5e1eb8b9,7b8b69fd,31ae2b2f,3b7aceb,1db81ebe,98503030,1ccf4164,f8d9cdd9,6f231af3,e5fa313d,c47c06de,ce44dd84,d3fcdb16,a4b7929) +,S(1b902ef,6c294f83,3ab18e7f,21d6470c,f553682d,2b1287d5,1a602925,3888e709,7735c5ff,34e49fe7,2385e9fe,40d66a4c,66ca0102,46c724aa,24b1ecee,dcd69ebe) +,S(6e4e5b10,8a202958,7c5b4ec3,d33220fc,c61b41c5,8c3bf0b2,55e4e28b,7c3133e2,b49f8306,5c336af4,469ea410,648915a4,b55ae504,6cea2f66,f14a99d,9a8fcfbe) +,S(87ce3f7f,bb800210,5b853276,d3931b9,12342d98,7d81e80b,5b10d1ab,1b7e9714,56c6a847,79dbf39c,293c386,9c1e3385,6b71f898,dd46ee16,d1e1973a,4cf0f635) +,S(8724329b,b7ba649c,9c019372,45a1e946,d368a706,2747dd0c,9f6667aa,c9426b2d,44458e8,48d7d44b,5dba5a5b,b0fa0479,55b109d5,21d170d8,9d090ff9,650b944c) +,S(b91befa6,4735c091,32db18b6,b64d7d7f,c6338dcc,cec845cd,7d297f62,b6a1a4bc,bb3961ef,8c9b79c6,867f16cf,be4766a0,15e7ca0c,8f9f340b,14cf701d,e0138467) +,S(4321ea59,332e97ea,3bcfbb89,68a3489c,9ae3d6b8,65cd696c,d8de3200,a0d08fb3,97e480d4,c5149d53,d7582e3f,c73a35ea,3f6b023,5ce50f7e,e31cb600,84cd538e) +,S(dc4597b2,836d96a6,431051d2,5a98d421,7950a8d5,e88c2069,5752ca6,f6f1bc6d,c712580d,16ba30ca,e84e3a83,c35725ab,362bb4fd,e4c42ad7,f9b73f20,53df36ff) +,S(ce644ef0,85ff3009,b261750,8de007bb,c5f19b65,2d9f8992,9972d39c,159a009,9a41d0,58db965,baa00c92,f6409ab0,c9402a69,20a66c17,c089906c,d73cc8f7) +,S(ba1e35c,b6c7b4ba,b81aecf4,23cb8ef2,2ec439c2,584682b4,a7444e01,9936b41a,f3b3c652,f91dde46,3e178d6f,7b17a8e4,1098b33,1cead28a,d2fce694,35bd6272) +,S(b9f98ea0,2a331bbf,60fc839f,845d8f43,6988c747,1902d14d,72ed1360,86e3920f,9fb2501,a7f23a16,cc726f5e,8aa17c12,35d9b04f,a94bb27d,318fb292,c60d03d3) +,S(70591746,55f90f81,c478e702,ffcff930,dc10dbf1,16d7aed0,9c1a369e,807e886f,8267a0a4,b9d6f0ca,db1ec92,560aa3f7,d09d3f41,d1f36f8c,64b95509,b205a59f) +,S(efda78bc,c43ca063,a7b6469f,84012161,14cc5a6e,4e31c31e,b76221ac,c25434bf,7b436ff6,ef45f859,f521b13,53193d13,4cdf8064,449af2aa,92be3781,56fa2864) +,S(229a4b0f,5d62095e,3a630988,c8056aeb,35fbd874,1a2f0e,4306d094,750ccc34,170d33af,491c74e3,d2d694ce,f6a519bc,741acf35,dc6a3428,8bea3595,e234de6e) +,S(c770f48e,20a02132,189d44b8,3acd0f93,f91860b4,81a6bf17,8ce8e2ec,6af07100,7e1120d3,8b2a191f,9ed43aca,3325beae,a6d8dbbf,7e0bf8c9,610f8621,c0266eec) +,S(a5db2c63,3901d52,8b60a48a,20b189a9,e89b8998,3d424af8,eb74869a,f286aa44,b9961d92,6a93d5b0,1a74e6ab,d9c37eed,384e186,8051ba14,46fe1a37,24af1e50) +,S(3ec7169e,9f330288,a63897b7,c168db9a,4b447e48,cea2c5ff,77b298,da41db1c,c3fe974d,e8e60eda,22c4aa34,ed34a4d6,4b3be268,12e58b49,e4477835,f3053fb2) +,S(b733de17,c3311184,d5860d4c,f99e48eb,810cff93,ce92eb77,7cf2c114,fb5bf3b3,c75c0dd,72d20ad3,2000d537,ebb61571,57fc4ace,9f26a90a,d28e1a43,480c11b0) +,S(57d6e208,d555bc24,15d49616,a158075c,27db59e,6a821df2,8b450161,6c2ed278,83870a79,d130da5a,3528e353,353b34f6,74b5d02e,6cf2a891,fca34c6c,3746eba8) +,S(9e4234f1,aa69a478,3bf2fd3c,3208f6f2,6f069409,fdb2faab,8d792588,6ad3673d,5f9a4773,7725ebc3,e8bed041,47a05841,9fb42b16,affec29b,d753733e,288d0653) +,S(6fe3ff9d,9fc12ffd,bd896202,fd8ae913,22e4fcf2,76068a75,8d43f1f3,9ab68173,ef0d5682,1d414608,273ef7b5,9208c59,bf94e16,3e7faa3c,2ab3da22,b509fa1b) +,S(2c31e54f,d2586e19,efaaf415,e54cb499,54c49257,d11b161f,ff2aae5b,b45c4631,bedfb0ad,364ab1ac,e7f53c0e,9fb825e4,39af354,1a70708b,d8e0e423,13311346) +,S(51597259,23148c3,478ea2ee,2b933858,4d745b89,49c2f782,3b8660e7,78203ebc,329f6153,f688b743,c4fdc470,25790503,b81ff7ce,5a613362,8a59f3bf,66aa1817) +,S(188b3324,58f7d7b8,dc3fcb3b,df51ccfa,d394684,cde37d60,70d41e46,cdcaf1f4,4dea5da,d112a117,31a6b04c,8999c6a8,3d1ddec2,88aaf104,487656a4,d3901910) +,S(92058e41,a503e158,8ceb0a49,4e13e2a8,85f804cf,7f13c90e,307372d5,cfa6e471,368072d4,449e685e,17e5a230,e2177e95,bcbe177a,58cd1510,68e7255d,84ec108a) +,S(6a6f5226,44e06506,f27a7a68,96eac537,726f79e8,9e83105d,5b647883,34099fcf,cf3edd4b,d8ced83e,b167b664,574987f4,7104e79a,5a4b5321,4c19a32d,a5132586) +,S(7e37cea2,340cdcc3,ca21e0ef,ad548f37,c77c3402,34f0148d,89cf926f,2376bb26,c72d829b,42a27c6a,b062938e,5cbc22a,6b02d42b,d31cb69e,83b9fe9a,cc9a0016) +,S(1e6c2069,cf0f6cf7,bafb4b39,bc97a8dc,800d010c,52c3cc5a,d848fb82,d8d0b667,c181e76,bca1d846,86ecfbdc,2cb09140,49146986,630b7e81,f86c7238,8e60196f) +,S(5566600f,e6eea4f7,b06336c8,d44b47ea,f36ba43c,ca404be3,5203ef5b,6f07e16,6bbbd08f,7adc1979,ed4acb4c,f372eda,69e5ac16,7695a78b,5a7fe1ce,1057e55) +,S(435c634d,ed451c5c,21c5adc,45d94f8c,69045016,d529ba76,19ad07d9,31c791b5,31ede268,8252a50f,2206f959,1953c0ca,85009876,2b6f6c14,24b4e47,c74d4cc1) +,S(6fb8cce6,a66071c7,395c0612,252a414d,f19a0d2a,f855e7e9,dd142342,9c57b9b6,17f94249,e2f05314,d4799fad,68232626,183dadee,abfe34c5,b845d89f,2768e052) +,S(866f3e99,20fc4c94,9496a695,f54ee634,11c711a2,99e05890,148de8c3,2980dc36,e99c55af,4f8dc3c1,d38a11e1,3c00db2d,f5211f02,e837909b,ae188786,d18d62f7) +,S(80c13410,b7164899,a724f723,4dcbe505,62404f0b,7c027c79,4616f618,80468b2d,8631942e,71a01e4f,d7b59281,9dc69d39,bd6e8fe1,c2c41621,6d8df895,280b91aa) +,S(8f85bdee,ef4e9d88,53e68be,c17bb6ec,bf37abc0,a4ba44d2,c3815dc1,82a7da99,36d45bdf,8ae9342c,af0f8ce7,8aa591a3,4e8c295e,49ee6962,b4a9fb7e,ee17897e) +,S(9cd0b2b,3393cda6,ccedaf4,ee8a1b3,17a920e4,826da2aa,4404a01a,49600749,3831d35a,51d9650b,b1901e34,8f0c4ea2,3a00c492,a4960463,414e5dee,aafc5a5a) +,S(a0c5f1af,15c67ffd,6cf832f7,974c779a,3ab7ca,dffa32d3,bdbe0377,d49f33b0,6aff40d4,42de6262,41734412,3620c5ff,3079392b,b8843c57,80029682,1cb91ec) +,S(af396f40,13e217,d7e1fbf0,2ec4039e,c0111370,37cb2d78,90a82313,58edfacc,aaaeb9e2,5a57534a,2dc35d16,705f0e5d,6754c599,e85864bc,936e94f5,9acfb936) +,S(fdac3c57,e3e0ecc5,e7e871b0,dbe35979,87d4c071,f2f89307,cf1e71c4,91ed0eaf,3028a0cc,a22ef096,73c877af,ccfe36d6,b14d14ff,5da10b18,cdee6068,3ca09fc7) +,S(e3aeb123,9587baa9,ae12b6a9,68efaedc,ae745fd6,aac5103e,14a471d9,eefa88b7,7ed1c786,52c1544b,e306833f,bb27d1a5,7b460305,a2c8f6cd,2d33397b,caf769c7) +,S(96baede3,382a0162,fb4cc663,c91acb94,eb83d7a,3e3e0a0e,6055a50c,d78352f5,78722e97,b3ad2824,388c3a80,fb930089,5100d61,ea58f997,2adae059,f0c50cbc) +,S(68704956,16975637,f35e1eda,4db546fe,ba93d122,446a3e40,8de04ee2,3bf5f5b5,6247d2fe,b5f7471f,3a06c7a2,4c4261a,934bd226,772405ff,46e361bc,614ca494) +,S(71ccc634,2ca1f858,8ae02d72,eb0dd2b3,62eaf652,f83edad7,94095e09,f4bcf749,487aebd9,23b10e69,4a8e3f22,2703e5a1,aee17794,42a96c68,6cb9f983,dd2a45fb) +,S(6fda1dd0,65739a2e,58cd183,1e8b6109,713844ba,f249cdff,25d0b3ec,f635d3f0,a2ee44f4,8afccb72,4f8a96c3,2a88a8b0,a232a93d,553713df,60a965f5,2078108e) +,S(d3f27709,ff5ad97b,45e395c1,39947115,65cbfcf5,838e7b64,b1016cc6,d5147f45,f96cac16,cdc8e1c3,54e026ed,29bbd6c7,29006ee1,51d9d61a,4391567,93265077) +,S(27ba1944,a28a6eff,78a7d064,bb8292c6,68f82793,8e2be786,41ee366e,a4a011d6,24bef875,9d216430,e7312fc9,458f0571,fcbe305d,574694d0,a77f7a98,4e7bdcab) +,S(fd37b812,5fd87ce0,82fca9ff,7872e0e9,772f4c44,1870748e,e35e7d00,944fe190,9450a525,d9ae198d,db9b8c43,fb337df2,8ec68a44,60106951,1847b9a0,27dcb453) +,S(d9967cca,92583e0d,8d329b63,f32a8017,4467518a,595d8b80,2c24cc8a,8e071c69,6aca3673,c2c39d69,3bc86dbb,92e5af27,28361cd3,2179eab9,7ed64ae9,73376c25) +,S(8ee82d3c,4a8d01eb,d22798ef,bfd95c16,f53cd45f,e3d044f,3d89ed40,b94a0a3c,e0b0ab7e,9167fb9d,aa71c3a,fd5b9c0b,79c8b6a0,db3ae2f0,36c626c7,791e1b2d) +,S(461ec3d2,817cb549,a9fea029,15029707,8d709ccd,bf22da47,64c8a1db,c562caa,661b2d7d,cb5c9790,510f6e12,61650401,26cdf80d,b086259a,db16af47,af6684af) +,S(3155885c,935caed,469bb7ff,b53ff6b8,59f51780,f3de5890,673101bd,41f5793e,603594e9,92ac108f,8d0bbdfe,8f3576e,8ca6ac2,69c7581e,28d434ce,2043dfff) +,S(5cac93b0,ad9199c1,906267a0,ad8874e4,c68a60a2,b9fb6f0d,cad42758,2a4ea9eb,836faa98,a116dab9,69961fe7,ad7483eb,c48e7295,37e6634a,e423b99,880032ab) +,S(1fb6d3ac,afd5ef56,8b6eea1c,4a8f0179,54d05274,24487904,7e3ec56c,6956cc42,b8ef91f2,f89e4f32,6df6a1ec,50c6362,7431be48,1183c839,3133dafd,2e2a5411) +,S(89c0b1e1,52f0005d,a30618f0,3fdee7b3,69fe0157,f7bb2bdc,d31fc773,d82b28f2,61d6e357,bbf46816,acd05de5,b26c67b5,ad8223d8,ac9f47a9,25477ab1,80b4a507) +,S(7c5d18b,1f9d1afc,d663875a,34193240,7cb968be,f31d751,807ab1b,b79a211e,45fbe7ea,985bda0f,bd23b449,5d4e945f,d136b5b1,296c9b3b,6dcb9c37,bc779ff3) +,S(c827cfc1,9393108c,9b31ad4d,b5eb7f00,b6afdad3,a5e2f792,ddf9dd13,f159f85c,5c5fe07a,3025d401,54fe12fc,89951e2c,330ea3d,d6335a12,9c31aaa6,753a1cb1) +,S(6347b32b,b1f3f7dc,205e2fa4,6b201b4,2e0dfa80,550741e6,57117875,57cc5d9b,f8f30e10,9508d34c,a48d7255,3f8ac26d,a455d3f3,b170ee52,e22be1a3,ae8c3ba4) +,S(d489f595,e822101d,5f6e4283,7db032ae,bfa21f3a,94998130,e0a1d226,67ae1014,8b112e89,8dc4a146,8f64c33d,1261f8c9,2bfa98df,eb9500c2,2b4a66d9,227c66a9) +,S(90cb9c63,fb4afbce,adeb8f98,1e76a645,2200d73,d43bc0b2,76f058dc,b8a6205b,894afdc0,f6ba7f1a,bbaf19c6,720471d8,2611a77b,5bf6c87b,a02324ce,52e74645) +,S(40df68bb,19f121f4,e566a1be,f0a98954,869bb06c,699e818b,bc49a795,6a196af4,70d41c09,d0cedf7c,31c9f830,3610f6b6,e1d6b3d2,a431e69d,a31bdd63,220cec90) +,S(4936fd74,a0ac5045,91c962b6,431f17fc,a888dd1e,b35549d7,8b41143a,83da41bf,33a8e1dd,6517853e,d7c68af8,5b3f1cf5,3fc5b72b,5dd0f392,c5ab5ff2,1bff4f92) +,S(17177538,811075b8,f5baad59,2958074b,c1a0c6e7,b2a3f594,288e82b0,da023557,583d4814,efd8742c,5e7a0c5f,734da121,b163fe28,239a8775,4bbb072,c1b10837) +,S(a126b7fb,ad5145ea,b414c82d,8f21f208,6abb118f,3692bab,586770cf,4f8926a2,79b4ac17,b601b4d2,1a2bfdb7,41f7fec2,88d46594,699e4394,452cb2d1,ae2ec669) +,S(51eccf61,f8840048,d55de51,2d865a52,3485db8d,869844fd,ba30e703,ae871163,594ad253,97bdc9f0,82c0c46a,200ea090,12b2c6a9,57a53dce,e87caed3,2ea8d50f) +,S(97781633,6f1b7131,f55966cc,a79d25af,d8984438,8b516882,5105ba3b,52b3c7a9,f1df3541,b4b08fe0,cdda20e0,a5275eb6,105011ee,f4355516,e47e89d9,3b214c60) +,S(8bc2c43a,72248dab,5c78cf6d,b1a35ce8,64231d3a,870bbf01,cdb79be0,2c93f7fe,d52e2e76,173e9dbf,9e8106a,ffbb9d7b,b428f07,80269b2e,f49bbe0e,14cbb425) +,S(59ed7881,6c7ca119,ca4abd03,34b90b37,6c327175,e899e0d9,79953e50,5aef9f2,239fc027,690c0feb,e4db46a2,8669e0da,20779425,34bc6133,152a606a,16449910) +,S(bec399d1,76714995,d4192390,72a39c59,c8ff8e9a,2cab6520,ca0cf6b,2fc70fda,ffc4bdef,7831208d,23a1ab9a,de52f346,a21a2f7c,efc5ffbf,70999bfd,ad0ba8e7) +,S(93bd25ee,f4cef1db,fa5f9951,7d22a77d,9e3b87db,58c63076,68a35885,9e0c4d34,dd7d1412,97361b84,9a43b1f9,4f89970d,fa008c63,3404860,794e6a87,6735b4dc) +,S(b203a657,fb4e4d51,158b2e70,87455a57,44e553c5,bdea4f8e,3e8e6c92,eddcf58,98246948,c63e182,61f0d4c7,543fba7b,c74ae5a7,a19afa6a,ac80359,5ed8c99c) +,S(af38b53c,1dcd27b5,8118a28d,11da8aaa,3e20ac21,4cff64e7,be683dd,80f6aa69,91e38936,ba8b2b42,34686723,7a3fac67,5ec10179,18f8ac16,acdc32f9,c09fd919) +,S(55e4da5b,b99d805,e2058756,20ab4cee,988ed472,f5e6d86,2f8c574f,6bfd8518,7368be34,75179d32,e15b5976,c34c366e,e7713b25,8f179309,40b16117,aceb2d35) +,S(d3d7e82a,a7ebe240,83e1a15e,1d817d91,a3608c6d,d25a35d9,183a0c9,e42133f5,a5245cc,e89537c6,895080b0,6c5e2772,4b1c6d69,6c1bfbfc,ae6a7d4f,fc5d504b) +,S(8bd14f9f,e6b54437,9761e803,1dcc9907,beab754b,68c6c3f2,2316e5a6,6ee48799,f89dc150,a3876257,51663a26,35633868,ecb2eb27,d9ae8603,89110aff,6978fe19) +,S(b1d72365,3f50f42,cfeb0858,972a5a22,3a7dbc5b,4823704,85c88fd2,22212932,40597dd8,594ee1f9,b74aa52d,385b21dd,6279864e,7d0255,70bf3716,c02143ac) +,S(44d303e1,cb7d920c,93be4763,e045cef5,c22d4403,5e1d3a8,593f4f72,679d3023,79fbdefc,5ed6da2f,31bd6fcc,83e3cee3,e77e41a9,a01b2004,76912c61,5d89a70c) +,S(1655ec54,447aaae9,727bd4b9,60eb996,92558999,83d4aed,3dfeba12,3b0cb3ca,8e90e578,cf96e31e,988f7bf8,a9f06d94,6ea28fa6,b1dad744,3b9ece50,6626fcdf) +,S(b02ae3fb,530566bf,66da9326,18990e6e,7902b104,800cacb1,9717bb7d,845a3f29,c6d32713,f4483031,19bb7631,1b397427,d8c05232,92a61051,62a0307d,7b006876) +,S(13c46566,53a0042b,8755e140,2b12b9ad,8e123699,a153ec6e,65e49cd,10c5bcd3,70f3672c,498f603e,3c521ea4,3ac3e10,7bab13a8,a78b02b0,4492c51,6d41c88) +,S(e7c7562b,3fc37b8,2e9c7bff,c5e97b39,dee483f,141df4c6,361eda3c,4f1483f,734be460,3d2b4958,c1bdce51,57a16d0e,1a937298,1dbb2833,1b12f8e6,c3454066) +,S(de025d2d,fb6cfc75,342b2ed1,ebc7b27f,332f3921,c304b8d2,263d7fba,2959aefc,46c609b1,6ab6f3d0,2dafc30c,38373a30,1a88c31d,80694598,5e676f1c,16de12f1) +,S(21e788b4,fed21c3d,25ddb714,abaf053a,25730af4,83817e4b,6a42c323,3eca2e01,1fe716bd,a7d07592,b66af9b8,32335b9d,b249b1f9,cd413c58,ddd29fda,e5c1c4c6) +,S(a7db27ca,7481d50b,1a458242,c75b28e6,d1bc1a9d,da97e012,e39cff10,7b79edce,c4f8686d,cabde6c1,d3455fc1,795a7cc4,84239d2,3b11e238,85136183,71ae73f8) +,S(c5205173,2790f240,e2161627,5ac894cd,e80a7d3e,bd5bdccf,41af64b6,5e2d477a,e9aa295b,209410a0,f0e8e216,deadc3f8,af87700a,40543cda,463beac9,f62ba20b) +,S(487bf4ad,99d1daeb,f1c25f86,d36ebcd9,38e91eea,8b855d05,ea9371c4,9572846d,81293837,ac6ded0b,ee04b2a5,4c18ae0,32f72970,86de0ffe,791a9576,fcfebf70) +,S(bd6dd2a6,bfcf688b,84fe2d55,e56025d,35567574,20edfcb1,3cd14483,e08cb53,c86a45e1,84c83806,38ea1501,c2851598,c467d2c,d1c51a1a,1b037324,d9ab0fca) +,S(94a417fd,fbf188e4,1334fbb6,906731e8,70dbca18,672bece7,936a4c7c,235ec737,e03c6607,343ec14b,81eaf124,bcfb7d97,564e9f93,546f9765,299a443,d9be8e34) +,S(2a57dac3,3fa13b26,a96d9b73,317a23c0,2ac597d3,3968fa8,ebd88d94,66aba51e,1754a6bf,1db7b3b8,b0a7996e,545f34fd,3b05bfd9,805b50c4,42db2937,e8c13b61) +,S(ba2e4a71,6142a39,67c613e9,93083c32,d1c66da1,2c3f9742,eae90b7f,ec7e7df7,c262f8c1,a704e83b,ed7fc60c,5adec65e,ccc6de35,8247cc97,1da7acfe,834cb07e) +,S(4ae19743,b3132512,d28a12b1,1fbbd824,8bb0b9a,730d5a8,a862ea8,ecc01ca9,67b7267d,48fa3ea1,d1c1ec7c,a6a8ba0,cb27883c,3f9d4193,20b4c8f7,a017eece) +,S(4cfb26c,81823667,f18e6ff9,d77493f0,778b9bf,411b63ad,61850ea7,7706ddcb,10df8a3e,3a8c93df,4d7af16,407c3211,aa6c6079,c397a0c7,78e1e277,24e4028e) +,S(9f9e4566,a014b66f,fbc3ef,b3c05062,15f06102,6f7d6561,934d3db9,5d130349,492b8ba2,76d526bb,98fd0c17,c5a0aa7a,ab6e5c9c,ccfc95fb,a417d25,9396c592) +,S(e36c0513,3641b560,b0f9f4c1,1159e2f,ecf2a194,b55f3bd2,22ecf1f6,ac239301,c7fd74ba,48e7fc40,6322aea8,e9f83a82,20e9a113,ac2b0c7f,aa05a5bd,bb43736c) +,S(91b013b3,b94d4568,78737580,bf1da11a,a0bbe0a4,e3ec65e0,33e9f824,d8480bbf,15e3707e,c13a2529,22a6f824,7e33d172,948790ac,3d367f00,6472f22b,e8cfc5b4) +,S(254d1788,8c676d52,236782de,2d539f2c,41f667a,d75d0107,7d43f723,ea324f56,f7042d2e,93511e5d,13f4aa03,bb2dbf27,9bec3df,97974275,5fd25133,c814094a) +,S(452d6b7e,ce7d0f71,2afabe44,2a22f16e,34fce662,99697f0b,6135678,56f4079a,21ea8ba1,28cffe76,3400d69f,ce13204a,9a39ee59,14132d23,db3315a4,7f776998) +,S(1580b9d0,7888f41,81b86b85,5b1982d2,81783185,5f28c163,1f151479,cc4c508b,4d9d1e9c,78b3d9dd,73bcbbe8,778c1b7f,65b69faa,f6576dc3,ecdd00f5,c1d689ff) +,S(79dda23f,c31694c9,16d1470c,2cf25a02,45a5aa7e,d084af45,c43960a,a2fc2700,7c7bc169,a58b8d8c,c4b74e0f,377031ce,d7158c21,a156fc03,c34f25a3,a95bc978) +,S(866da7a9,bf80cdc2,13e500af,b5f4aa0e,419b4905,44653b5,fe2daee1,4149dbe6,626d38b3,b45d57aa,6867e8ad,c8abc49a,81a282c,6bc6524b,239819ab,766bd3d7) +,S(432bc912,bed271e9,321624cf,c6062b96,cdb502ca,f990522e,1024b80b,9411d374,e645449e,dd7c4d41,41e0cc5a,cc5d45df,4cb341c2,baebb605,bcdce6a3,e411cf01) +,S(ac3d3b28,83ec385a,15297086,525d937b,717f5ad,83a5be4,77b3ec9f,6410518e,d126eefc,c9ed7e75,c10d01b2,c0e790e7,3dde58f9,54185a12,5fcc7268,84b9b8db) +,S(4570834b,2a82385d,6db696d9,56ae2667,fa18b262,960d064a,5c13da43,abd6aa37,1e9d6083,b249bf88,7ba2eb59,a9597e7,d9d15c64,ad6404e1,21a486b0,61236f28) +,S(88e9a6a0,ffaae05b,7c29bf79,2163c0aa,b224b051,36aeffd3,341355d4,b983ef15,ed382a2d,fa2f63ea,dc4ef5e2,579abafc,fe457475,d1ae8413,99969324,718d1e5a) +,S(16810eb4,de3327c6,a505663d,cad7f618,b397033b,d6c9748c,4fd8748,2adff706,352ebd8,91326c9d,2159e84c,40dc29ae,30693b7e,5a9ed119,1fbc2cf,ee60c81b) +,S(d7d03a92,95064981,1cdc6503,588d157c,88c861d9,43fa9cab,e2e3301c,994adf7b,8c820bfe,933c2c05,1ca3042d,5ce781e6,ef419c8f,d9aa83ef,2a915e36,382f9627) +,S(5b2c9504,f645ac90,24e25c4a,e880ce7b,f33aed77,e45518d9,1c6f2652,ddcf11b0,500b37bc,c7764f32,11d681e5,e8f2a407,440e1da2,4eaec354,fc289c76,29296449) +,S(d18e26ed,d2c78bbc,16982675,9d59eec3,bab1a64d,f579dc2d,6284add4,8cfbd761,1a1e0172,37a3e78,8fd8989a,a5bb6944,685915da,1392e1a4,4a6c7ef5,7d0770c3) +,S(5f6b1416,9bbc4795,8ca21e88,df252546,dc9e46bf,85d64772,24f34ce2,8e58222d,3a8e7616,4d5a0501,947ca0bf,163e64bb,3a57918d,e383c600,e02aadaf,4b7fd6a6) +,S(d7a625e8,dcc8b456,69802023,8fe85691,ec2e050a,25421373,39bdbc8a,f762bc7,8f1f9935,a8ce2607,abd7bcbc,fec1fd8c,208683a7,10000806,20d50b4d,36ea3643) +,S(a56a5419,72aea4f7,15dc891b,3a1b34ea,7ffb42c9,5e02a771,6bac663e,8d5913f9,47b3dfcd,ad785671,180f7bbf,a7f5d144,59b44023,640e3897,1e999a90,6bc3f994) +,S(1ebb89d7,e574cbd1,37b1a3cd,4a306b44,30325db3,361b7c3a,d93116fa,baade31e,2039c52f,bd30c84b,4983d118,5422a342,ee020c33,49357e6c,b9550075,38da4dc7) +,S(b5728208,931a6575,4b5cd1a4,a798ec27,e189effa,3ee4f0c5,ad493a88,b49706fe,aba785e7,2da3d301,cb29a612,562078bf,358f680a,317836c2,14ddf7d1,bede9ee) +,S(56661670,d67b7c77,202a00de,effc1ef6,f5162a97,47afca8a,a2457c85,85a775c9,adaf08e1,83bc5107,4c55122,e549d3c1,4de8a893,39f199b2,ea044a25,4d8803f6) +,S(18ab56e1,ecc86b69,77104243,35737b51,c18521d1,b170a3e9,3fd068f0,ddf2704d,cc694c3f,43bfba9b,20c9572c,acb30563,df74f07e,ce1ff9ff,46b8259e,8119e94a) +,S(bc12a874,48270360,1575b754,bf5f50f,6e945185,2e0f27c3,c1aa0f1b,ce0ac357,9c6ce76b,ac175ce1,d667783c,7204ace4,abe12d56,62ff36f9,5de017c6,f1ada1e9) +,S(a4494c26,99b2da0d,6edbdd6c,353eca75,75666796,5ddbe457,2c27ac46,79054584,589804d5,8360a9e7,d3e2a914,61d5b1ba,e8f0cf69,3b8add03,5545386d,5082d621) +,S(1ff846f9,6d0e4e7e,be34de77,d036a027,f1baf9e2,ea3dc9c7,8284ac9c,ce3aea6e,8a0a336b,9f0e3560,31f5d1a8,d4c0b68f,27ba3eef,a52dc974,8d03e12d,653f2e6a) +,S(33181aae,91573ddc,728839e0,4173f716,6862d724,35744099,ffa8f1f2,9510b374,1252129a,8cc8ce4c,1ddaaa0a,4c34c4a5,f01ee26b,3e1a9fed,ad0e7c9f,bdc5223e) +,S(e687832d,fb0e44ae,f7649b15,77274a94,64426406,fe4c6ae5,7028c9e5,78012594,5e5b3897,6b734b7,b65caec8,c77ffb7f,c5de4f98,eecab002,8cac0c4a,a513b7c0) +,S(354bbec3,ed9371ac,58174b5e,2a18c76c,c22951,df4c6aa5,755c72f3,94b37def,40d0c876,9283416d,b3613ac7,37401e3f,3c2f4def,bbaba44b,5c2a83fc,c614c29a) +,S(fa46ac6c,5205b81f,3c9e8aa6,8b5ebef2,a8bc68bf,fa2f4511,ae36f726,7439a485,3853f9fa,b016d2a8,1cde37b,13f91185,f25657a,765fbb35,c1b4d52e,3f67d180) +,S(426f86b7,33255f8e,1e85263c,923fb86e,56a53d77,945f6688,5627ed43,3024328c,92ec0168,5301c89d,5b13c4c4,57b22215,658c6341,6aff9148,6a2efbcd,4e8c4fdf) +,S(5c8d1c9b,f9a72d5b,e15d3f4d,17d48cf1,7685dfec,b5ae1ede,b47b6e58,80bc64e3,6205550e,286c8150,6638a3c7,c20f0dec,b6a26b53,641d2dc4,e18d0f7d,29bda7e5) +,S(3a021dbc,7bf7501f,ddc31356,26a9cb01,be673373,fa663c80,271be570,e683422f,64a89c54,ddadf89,784e508a,d056492a,a7a71b82,485fcfff,54c7b0de,4c1bfa53) +,S(8d82aa49,1d719f00,75d2dd9,11d642da,7c0062c3,2296726f,a504af27,23ba47bb,6e616f50,6d1478f6,988ed10d,6c6d3c50,7c2ae8fd,4760e699,1ff5c62c,53e4e77a) +,S(6feee988,fe488e1a,6ed5d60e,fcac1b3d,bbe5451,6e7aad76,f27f08e5,3c5e536f,ee9f95,839fba96,ba26d848,425b68a0,73dcee61,35e04eef,f482998c,b044c502) +,S(36240979,bafd0d57,72fcadc7,314093c6,3063880a,e895ce5d,2df3e59c,ae16fa1b,619d0376,58ef3d85,a7afe326,77d832cb,eb20820c,157836ca,65f17c61,c84afa48) +,S(4c2b8847,4da56053,c2046fc3,26e81704,b78494c,91877407,9984a3,cf6a18f4,459a9e69,17a4d711,61ca0b43,85c5a823,734286e3,d0cab398,dda1fe21,3b28142c) +,S(f921c9fd,b33211ba,a7f4eb5b,15044c34,d6e54f50,af7b85ad,84e12fba,b1478ac2,38cda9b8,c850483e,48c6b16a,5f7f624d,e31faec3,5cd594f9,3171490f,ae016973) +,S(a57769e6,6181d68f,8f86c8a7,da3fdb4e,850ae07d,f41e78b8,ca42c6b2,1b19b257,212a987e,644bc42b,f757e00d,54d50ba5,1dcd6698,280f8eb3,8829dafc,527cfc6c) +,S(95a78104,37abecad,888c9195,23c9b16b,375a2714,fed74639,1ea949a,c7c11d9f,eae320aa,227b056b,242723df,1f11a494,ec642876,91a22748,9e1ecb2,a2cfdf78) +,S(76a7e034,f7ef3e14,2ada9a64,ce984008,9f034a5d,8584a0e5,d10d40f4,29cfa42a,999dc0e4,9633eb3c,2a4ec931,9bc2eedc,9f2e4996,522a2404,79c9468b,668effa2) +,S(eba89396,47567805,1cddf92,9db7d82d,427010cd,5e94247d,f4e8cd1e,eb5881d6,b92ae1f1,c0885214,3f37d8be,616cc5d6,aebe0f8a,8da4a6a5,d4a69b5,dbbca552) +,S(a53a48d8,59fe156,6b14f872,aaed4735,dc0b7589,713ce8ad,dd3f3e1d,6e23704f,9e738da0,7e85a3a8,3d89403f,3ee7d564,4f463418,676cea0d,2528b515,39387fb6) +,S(8326590b,dba5f8a3,8e39b148,105063a7,c831cdf9,98288fb5,95b4fd5e,a707d3bc,a02cf88,a2a1dd25,2d00d62e,cfa4130f,61b7d4f,297e6b06,cb4661b2,bf768982) +,S(6a492194,ba8c10bf,e31714a2,17a74b2f,b0c35a2c,95c54af,7f5ac6df,17916030,3cf0bd65,8e0c937a,92ee8b4f,b4fc3289,c531cd00,6febd45d,b6b6c522,f8c77b87) +,S(b4ac71c6,ff797eed,61d80c49,46ba221d,3b34bf13,7e3d4747,4b6c3068,33c52a57,46acb2e4,8fb6fefc,ef3d2343,4624e0d6,14ae3b60,df813683,90503506,d52820f7) +,S(8b11711e,65bc29e1,16bf05b4,e557b211,13b19b93,a1c1ef9b,49a19316,c286e0bc,79c4a0cf,cf383538,21ad695e,5b4eae58,faa6bcf5,c722c491,aa9078c,8aaf0dde) +,S(b8b00a76,e86b2579,97603f82,e80fde24,727362c3,6d7a192d,8974f643,4408e098,5db6718,8b0108dd,7cc6fa1c,fead7b2e,9e0b9ffb,78487d0a,5851b6f8,513e56c6) +,S(884c3a1b,fb6b968,53427a8f,69b713c6,bebe8f69,645fa52,c50e2d2d,61d89814,3dacc0e5,94b2eef,3743f821,df54dec0,ff28aa4b,d5b18fbf,14d658ce,7a3749b) +,S(3d0c1dcf,51aabf63,fe04100d,cbf6fb4e,c004dd1a,aeb637d8,496a47f9,74637596,576ebd5b,bf5de2cd,6242a6f,599fbb76,883fe013,3e2ef05d,22843236,a5df51c) +,S(56b3edaf,61539adc,25c57f79,a277b77f,1ae972b6,3d5328fa,13f811e2,dabca8c7,40383900,a2b28602,71ab3d2b,5dda95d0,96bdfbab,79b3d347,dbdac66d,2d2e98eb) +,S(426e29bf,50f34c9f,434eacfa,845485fe,5fbb11d8,319f9457,a74905fb,2bcfd3e2,eac47ee8,eeff40c6,20d10cd5,c3b9df40,eeb60b88,df1f57a3,5d613198,33cb117a) +,S(955737c9,f4e24d7e,c6d419ba,a2edadc7,cf0a91fb,5bfc3ea6,7ed50ca7,62929308,97bbb89d,18fd9040,2fa78314,359bf645,b69831b5,bb82e608,67baceed,481675dd) +,S(5d72a813,d46427a0,f2fae639,fd7e7ad0,704e74a0,83a784a1,cbb85017,e8b3e514,c92f410e,851fc61e,559d6839,a99eb95a,2e8c2636,f51c45ac,9bc1613c,b8876c2f) +,S(c7da9359,e9bc8890,620876d9,170495c2,dda7849c,7852406,22551bb7,e769b6ef,12278168,71508d00,95d90787,24a0c7d9,cb41f6c5,c82f01c6,d3794c3f,5c10653) +,S(67080a26,da7af0fd,c2fee080,f20ae516,d92697d7,fe1adace,9729064f,569ac683,ad9a07ac,fbba708b,30944107,e510eac0,8e08c5ce,79f77714,aa522cfc,2316d94d) +,S(210d5a39,d61428f7,6f878ddc,82e3afae,3947f404,5c155a41,63a00570,5bfc0dbf,617effe6,b0a629c2,ed14cc29,67a9193c,2efa804c,93e997ac,96eb1803,2e8c511c) +,S(5d0b9231,ca1511b9,c53242a9,37837d13,9b8e461c,6ff655fc,59021d04,e334ed98,c8070121,f4794eda,713ae1b4,a9e7d6cd,1a2f58e1,f6f8d986,bd13a66e,bfe924ae) +,S(687bb82c,42101161,ed0c8bd5,44c71975,9013fe2a,fcd10ab6,3ce78a67,e2907392,eaf63d87,758aa3a6,f9fc721c,f8b209cf,fafc8e81,10397e35,6a25e966,100d9eeb) +,S(51a44157,b8048f68,1a2a3931,ff2c3ff3,c63527d1,f4bccda4,e66a6d1,ce5f5d33,6ffc8d16,519c2940,14fdc8f4,20a63890,1a0c5667,2db96a1e,c5127fe9,e847dd31) +,S(b2862572,56f515a3,5624786a,478c3bfc,a5dd7b2,2bd7c4f9,89f70bcb,8de86775,22ae96a8,a5a5396,b7a67e5b,29292ffa,f53b64de,45f17692,abcd932d,4664f3a3) +,S(f76b721e,201078ca,eebd89a5,ca396213,6ee129bc,fb92ae58,4bb7c7fe,835bc30,599c6ab1,8419840,3d6b3d17,161bafff,223a0a38,1e96a4b7,a062f4a3,e6c0d561) +,S(12f24a87,63adee06,e82f20b7,ff696911,d9a3b88b,36dedb97,93b07c34,c4178e9c,59fd7b9f,96f01fb7,b265d939,c2827bee,b2427fdf,8c2e1b7,35178d58,e055b7f1) +,S(9e1b8a90,de6c6bbe,2a082d47,758240cc,bcfaf145,c48f90ce,fa0221e3,1922260a,fa5e86a5,b54bd9e0,7c2a54f4,2097d004,8a71eb06,ea48c0a6,6cda5981,b390f9f) +,S(63f05903,20b73eb7,b01b6a66,e28e31f1,ed4117f8,6c4d20a3,8c109031,2fbaae9c,24b8c9d2,d34a81fe,6e88967,a263877f,40bd68ca,baf5d9c5,1abe8b1,9501d8eb) +,S(447c7409,f5402a3d,a1f569a4,ad9f2855,e5c89b6e,99c743a8,e27992b4,63905999,d44e8d9a,b34a7a09,47b37723,be016c08,716baa73,8b93f28d,b7790370,ad49e154) +,S(74a4406a,4c5340af,ab77db0c,20f197e4,25eed4f4,a3f986df,1bbf10df,f8793254,162d678a,a4ba712,8cd36907,d1c46b16,dc4c07bc,52aa46d,fc7987eb,67742023) +,S(f7035171,31872637,ab0e63cc,841cc7b6,2bf5e076,53a45ff,3a23246e,8f047c07,c84b0391,4f470785,721762a9,5972b59e,b3c7eec9,66718936,ccaee9c1,da1097ef) +,S(ec4fe07b,57b23581,2c53bd70,6b0a4152,2194d168,dca164f6,ac999f6f,72eaae11,af495291,45deb29f,ec2b1dc4,e9b76051,b2f9df79,bdd304d7,b5528f5c,a2fff789) +,S(17156de8,fa96174f,7a0b274e,e168d0d4,c6552047,11f36388,78dbab4a,cdd2ac9d,2ae3ec4,36c1460c,984e37ed,6decb88f,c2de9f84,79e28ca6,abb6803f,1461ac3d) +,S(e41b462b,9c5a47fa,cfa9be56,dd30cd,549cb90c,9506a6a,abfca643,9a1b2ede,108fe2b2,8bda60a,c68fb353,38c6958f,e1b3604c,22215311,d380bf11,17507f4) +,S(ba6c0159,20a67883,d7880051,51a8038f,f357ea78,c53b95ad,38a6f437,7744aa38,da49c0e7,deb6b746,83366aa5,c0e8e710,cda1441b,6686f680,4579b5b9,16002cf3) +,S(26f0186e,883a4b6b,87102ab7,f7e52663,7e8a9553,8495729c,ee283568,f8cbf1ae,eef8de4,bb2197ca,ccd51980,995a18a0,aad78823,75702d3,98b245db,4bd5f47a) +,S(10b93de,80c0cebc,351b5588,c0140e63,e00874e3,da663cdd,c4501ca5,b40c9d22,ed3189dd,f79af36b,e7986b02,7ec495ec,1dfef7f2,14358284,2d05b4d0,1c903258) +,S(e79ec274,337a82c1,c97b32da,8f5a8c55,7a159540,76c94255,8bc9a3e7,77769dde,9f23079f,22dafce3,54a4c175,d888f3ee,e527dad9,83d7502c,3b651762,628a06f6) +,S(fda0175,44f0ad3f,c8b9915e,8e0caa4,2be265e3,92041b0c,1b7d31a1,6fc101e1,424e9a27,85d59c6e,bc7e4298,1a505733,e4d49a76,b84a030d,db2f5c1,858902e7) +,S(681f01cb,5380cdfe,50314725,88bc830c,8327d8a3,cbcfdb31,577cf11b,a099c6bd,bf9d2117,cdc3b05f,9c06cef0,6fc9e757,59c02fad,8e0d98cd,794918,f39002bc) +,S(70c25a6b,2a2d2cc2,dd251f4d,9aca250b,d6fe9f8d,b9eff961,1de2095c,dbd12f64,c9eeaf68,a386b2cf,cd3f4dc4,793ea5b7,134dae04,d991e1f4,6b299a4d,1a966126) +,S(464ded2c,404b42d4,fefd5ea4,5f408666,3b466e85,fe8b304,3b121b8,9fccbb9d,ac113904,6ad3f613,23ad2d3e,a568367,610bb47b,e41a5260,79627122,105d910b) +,S(a32df72,48e79198,5d455109,a8c5dfc1,b7ff8990,1b5ce3f0,2ad2f134,13a103db,6db4b4fb,9d60906b,9aea2d1d,876296c8,cf9ad8d3,3e65e173,8212134a,4b160e72) +,S(fe49ce9e,7ee5d39c,97029774,64ec4fea,3ccb74dc,2a5c7fe9,a7695315,165179a,653f553a,35925dce,eefb9866,26155723,90bfc582,b4c426e6,3f58f0fa,3fe7dc8c) +,S(868b2e55,c895a7c2,4da24750,c126b10d,ec0f1a0a,2add5a87,8aa7316e,3354ad14,5e288874,cdea19fd,71f03594,1a6291bc,776bca82,804324,efb61f24,7ac299ef) +,S(fe0790da,90ae34c6,8ad3f1c0,80fc486b,d267ff7e,fd32d1ae,c63d08b5,8c098461,ad331393,de35b283,471a0244,422c6132,cdbfccdb,5680bef4,16f3beaf,6cf7177e) +,S(fe7fca8d,f4f80dc,ff16b196,167dfefa,42a23bf5,66837955,7f6d8279,6fe9a9d9,bbc4dd8a,d7ee925,dd80cb3d,681476ae,ccc5729c,e1815b1e,71c8f1cb,d8d4eb3b) +,S(a236c270,aa4ce20b,eb51b11b,511cf4c2,ae797598,e9f03fd2,3aa36858,9434cfc2,593599c5,75a7bdeb,41cc3ade,608c210a,8bc37040,fbb69f4c,4863a31f,2088f29b) +,S(d6a04a5,6bb8c27f,3ea8a348,538364e2,800472ea,d9e1f644,565546fc,76519248,4b95470d,50b97629,22b099ab,a5761243,dbf6e466,226e8e07,9b77069a,b542deb9) +,S(d09cd377,d64a045f,e1a76f97,4d701a24,67773228,3261aebd,74e02ea9,f8a30e7f,9f0942b9,2b702b3,d76ed2d0,1caf880d,41a342ea,ee06ae57,b90a924d,6588ffea) +,S(d030c72c,16d01a9d,6c2e5245,61c7e3fd,b9a469bf,ff0a7440,b2ed0ea5,e5d98b8b,c6b1308,dba4f981,16b60ff,eaea843b,fddeee67,ca1406f4,73db0f11,531f2c8e) +,S(56c456c6,d2c04a7d,441980ab,80b39a55,57d0c6f7,5706bba1,100c6bb1,e345a691,c5b2f0dc,2c735d0f,8ef10ffc,2753c2b2,825ce525,a0613189,fae34e64,26084819) +,S(aad5adf3,b505d1f4,28dd1b98,dd4fdde5,29d911c5,e0e5837d,75e51bac,308400c9,e94adf67,4f7994c1,123aa411,b790c68c,f55569f2,1df4b6e4,6afab87e,2777acc6) +,S(ac8cd8b4,dd76b8f7,4886b089,f18b4cb2,ca1d43c3,19ed034e,6b1867a3,5e6a0ef0,71d70894,f9daab15,7413f827,1cadcfc6,ffb71db4,9bcc81f3,c759d4f7,137ba96d) +,S(3964006d,f110fa45,409d7a4d,5996b44,93eb4150,f4217948,41dc5357,dc47ca1d,e4a11c6d,21dbd93a,f922a2bd,23f057e5,c2f24474,36bfd7d6,395a46df,3ad7cdb) +,S(2be10b92,27a312bb,74ea37b1,9bf910b7,723a1ff8,fb43dd55,1a0e1fbf,7bca56ec,19aa8afb,c886dc0d,293ecca9,4290b24b,755b2de8,c1dbc0b4,59052edc,c6df59ab) +,S(379c1556,2bd7f51a,1d219a34,dffc8bab,ccc1e417,1c38d342,a4c6325a,b5ad6ae5,47a1f28f,a6371df5,66dce223,10d95535,e7bc6734,7f20748d,b983fac4,2f185907) +,S(24054dc2,e675459d,2af584a7,d78110e9,30a36290,989dd4f9,914cf7a8,1fce9809,6572ae1d,3e05d0a7,1c4e06d4,820d16a5,3553dc81,a6f02a76,12b584a2,e57ae592) +,S(9812e6a6,f46ca979,ab7cd879,1023b32,913199c8,b850298f,a3bb4c8e,a02420a4,468f6eed,dfecd933,1bbaf36,986f9377,3afd963b,56577151,52a12b9c,88f937f0) +,S(46058644,6a348e4a,d84c928d,62c9de2a,9f6c14fb,95c83c6b,96203a5a,7f13c700,9dc55ee3,ca3d701f,e6759c20,3b18840f,4c873f11,57b21ad,90401e77,622d57ab) +,S(a8dbaf97,2d5b05f2,a44d862f,e4aed0fa,1cb877ca,639ac322,8e909507,bcbe006a,3db54c03,f082937c,4f98d38c,6141d0bf,253e6729,b83c9902,51ac4a97,176fa4bc) +,S(9b8b39f4,f4d91329,13324bcd,44f9846a,8b3019,22671128,74178009,391149fe,490a4f72,1c452e5a,88ffc693,90b0d1b3,4e4cae7f,2e0ab097,6b396a99,1c52c1d3) +,S(334681d5,a7ef46db,196404ea,9e501f81,7406ce24,27a64597,73e358ad,314615eb,2b23603a,1d80cd6a,9f04775a,52069de2,e328fdc1,37fe6b6d,6c165442,53ad6c0d) +,S(feb8e310,c63c6ebd,294639b4,a4cce42,bd5f8372,b7aeaa88,2023f0e0,29c32529,cb6e42ad,6a0cd780,aa57934b,86682260,68a45c63,bdf7e617,fbcbf86d,db43b213) +,S(f4a17bc2,c7334e92,dc923252,3b910a55,a8ca5cb6,2b3d93b3,fcf0f2c4,8705cb67,812b7795,1b24729,4b3d55a3,2f45260a,e7f4a9e3,d5cfa304,bb0471b0,da6e9dfe) +,S(8ec2ac7b,85514349,5496d596,bfcaaf4e,c330a995,f082324c,7e0479ba,4ccf181b,ea1588b6,7811d263,7bcb3bcf,5cf0bc4,70e92797,27b1c258,ce4ebba9,35250130) +,S(b1e6d61b,d9aecdbc,c9c4b6d,9e48c6f7,e4aa9eb7,7da3c001,53443c6c,4d4f19a,43af7b55,b2e2c976,8b1fdb34,48d6f339,1bc20aba,eccb1a3,5aef98af,b28cbf4a) +,S(4d83d2e8,210d47dc,3904a397,4db35433,1a951963,2d4e2e4,b69f2049,2f13b2be,7ed96c83,3874fc97,be45f1d8,8e5842b9,7a0e3bac,95400036,7c8572f6,3d37a6b3) +,S(1448e92e,fd51fc21,d6f5d514,35527f67,25f5c5ee,b0b29c36,b9eb6e8f,2bb20b84,248a338f,c8be83e5,2f91c9a2,2868973c,13336aa6,39e43f4c,ea8bbeb9,96e344ad) +,S(e23b03d6,9c2250ac,2cdcc7d4,4e0a218e,39d928b0,2585fe63,dcf7093c,924e4ead,8c69c7d0,4a8a4b7c,1997ef44,fadb04d7,80b91cba,13dee454,2effb8ae,71b9aea9) +,S(4cd1e35e,21fb3fe6,946a929b,9aebfcb8,78663224,da66af94,e5a722b4,32b5d7cb,6b1112dc,5e865b10,ba688780,32978617,eca892d7,730ca984,cba4ab07,7167e3bc) +,S(f3cebaf8,92cb7b0,27cb9212,3596ece0,bf25c6b0,d8f1dbf5,f2efb204,a0db647,a615ddeb,8418c013,91923b88,7bba5d3e,cf3c7172,efbdaab7,e12582bc,a968d8e0) +,S(4b8acd28,500bf88d,68337c81,33181706,dfe341de,7b1d5736,a3a85a2c,6d655e7d,be93d7c3,825a5675,84d6f76e,52f3b06,7b7c5d87,844f18ac,624c96e0,fd219bb8) +,S(8dc7ac81,d04e0088,63fd2de6,2cc68de8,3e567d81,85aebc53,d4771950,7ceb9fe1,62d4b64a,be7b1931,c47a7755,e25c803f,1eb14c89,2ee100e8,214b94c4,11fa9198) +,S(aa63343f,3078d48d,942bbbe,df4f2c19,a817c257,9003e033,7104e530,c1b89a23,277ef834,23a6730f,69c20f4d,9de94758,13befc6d,ce389bb,fe7958e6,6ea7b524) +,S(37de5061,9f301e40,8e9096c0,1a37df3a,dadeba61,bc217495,5d04d7a1,5121d30,7e7049c,33e40b96,6a136516,c005597c,e3f6aa70,d6db4c31,f5732fbc,e538898a) +,S(6d18cf55,506d59ce,a3e8cd44,f347481c,3ebc6682,2f91445e,11f2c0c,45eb83b4,e930e9c1,ca8b73f4,7fb9818e,5d4db8a0,220f7ba7,ce807854,c8ff2af7,b8a30cdb) +,S(77d85660,6de7dac5,dd4203c4,90152c9c,f70243fd,eb1d1c0a,5b797b39,16caa7d3,3f57cfe3,9b37d550,c9db0fc9,4b3c0e3b,1a2722ec,f920522e,8c37d848,f59c659c) +,S(5c2e69a6,c44b5bb1,c55dc8b5,6f9d4fb8,ea56450b,d69d9229,670e635d,f381bf16,f082049d,a69ca9b,a2821a8c,2fd0b46c,9ec21af2,4c34b049,f1af0126,75a3588d) +,S(329c01f5,66d925b,251944ed,92beb06a,aaacb98d,89b2aaf1,918ce400,b5a393ee,7e29b038,427a0fff,6ad50c80,aa49252e,e21c2c45,3d4c72a9,c3cafedf,faa57abf) +,S(4c2e631f,18b2dfa4,37ebb401,771d7b0e,b670f4fc,8546499d,f62b49bd,629525da,8a95ebfb,87423fa7,4c131432,6b7c204c,50c6c2d,a3f85370,1f789ca3,fe9a6f9c) +,S(fe121b93,d254465,c7f7cf02,ce10a575,d3808ce4,c6133fd9,392fbf1b,63d03fdc,33129b07,7bda4311,94fb997f,9443a0ff,e59438a,f360b635,2a80cbce,a49e0ba6) +,S(323ac27b,1cdbf3f7,74e20521,f1e74b8b,2cf8c5d4,4180316d,a532ae61,2f784c23,21156108,f6a64ac5,888b15fe,834ba9a5,c8489e16,f3d31197,bb296b2d,3fc3dc18) +,S(11478f62,fdfcb1e0,3a5f0ed5,ce0c2101,eeccbca1,e5934d37,41b1a8d2,7afd533,a9f79673,2fd3ecc8,644cdb17,6d11527a,aba8d0b2,985366c,70d96999,ec5f9153) +,S(9f0909b6,c9d3c1ad,eb88acf3,aed5a6a6,bc8d82b5,767896e5,e7343efc,20b9d078,a5161ca6,c5c95d64,dc663e30,b8f65234,61a579d7,b99a57a3,3b0f3844,e47db181) +,S(4960a875,32a9ad42,299d8cd1,a1af80bb,2807acb1,b52f1711,c55957d3,a64a6b9f,578e51f3,f1b46628,eb48f54a,93f82f24,4bfc6202,eef77dd8,cd6b90b4,de976625) +,S(675c29c2,bdb265e,5490951c,bfc9f73d,d142d536,4b5ec668,54b582f0,5d310354,22d1f669,3d82de84,202d4f9a,186bdfa6,8cb8ad5f,9da33a49,83229047,e08bd6d1) +,S(c4096cbd,6c75d0e2,6a922177,916ab32d,a9053adf,ad849d36,f65b7c9b,107ce605,5f236b47,2ae3c979,2ff61efb,9633ae05,b2b97e51,24a3c2,94f5be53,95c2bb46) +,S(9ea0d341,ddd05401,f01205ec,2c754155,b1ddd460,1fa3789,dd80ed9b,3b905c0b,3bf48101,97e7d0b7,6f3692cd,53989764,ba7ce059,953f7b71,7e615c20,da28e69) +,S(bdebbed7,84bffa33,72a6edd2,d9815b0d,637367f2,de3768c7,b4dd75a4,32fb92e4,5ff3437e,b62a9b62,dc16af10,5a20941d,297a431f,ca2b0067,6734c4ae,cfd0b9c0) +,S(a486fa44,b46e34f2,d6aea82c,8f554210,cbfe674,77b560c7,bccc7d6c,9e910932,1ab7e36d,8a15e284,8a3caa0,f54ca555,b8ad5baa,7f47fca8,3231c822,f897317a) +,S(4463a0a5,57a1a926,aa204376,a20aefab,66e7abb1,18c7f769,fbd1a7d1,29cbba08,4a823aa5,ab7b602a,704f01de,20b8ad13,7dc08152,25ceace0,9a9dab69,d034af0d) +,S(d6e08a4f,c45e8dfb,ab713584,d91d1d8,8cbdc5c6,2eeb4b4c,593a73df,4d825ac4,2cd85bb4,77c0d0ba,5071ae8f,c0fe2dbd,8be98d9,622fe506,c59800cb,5eb4c55f) +,S(b626e299,2a69cad4,e6076dca,29bbfd34,546d31d0,a4f8b656,a433fa87,f96a10f1,6df4b2ce,ba919268,f1568632,55e27655,62ac5e3b,8cbbfd34,3c656f43,43cb5bc6) +,S(3619e6eb,74645070,b2dd6196,5fe4d25e,25a4dfeb,619e3b05,7e31a566,947139a5,964b8989,ba156121,9d36c3bf,29467c7b,79940c3b,450c1431,1aa0d725,1282093f) +,S(b3529b5e,6718843f,c40a3a65,d5330961,41e5c060,e326aa84,7ccf7f48,c8bda2e9,49e6e0d3,7ce1bf45,e9c595ac,285becf5,31111e7b,2a67b057,9d4fc552,59c1b935) +,S(e51f0067,5bd7145f,fcf85527,dd247ef1,4a199778,94035598,d42afdf1,2fa9e05b,d59aaf3e,8b4d72eb,4523d5fc,69b047e5,7d2e175f,30eb6c8c,ec729cbf,c1b32666) +,S(8a87f066,f64bc468,d225eb6a,a0a7b54b,91c930e4,d4520720,1705f831,cde79b28,a92a5d95,e0057c95,b219062f,5d0e4277,249c34ac,6255113b,b5eebea5,66800227) +,S(d8a81015,aa878ee5,a69c771e,bf88daad,3a926afb,b8aef632,cd7d987b,fd310b06,af77f7e8,f276ed4e,963e3a26,2f3942c7,ec9f31da,199ead3,d5e5f53f,e20acbdd) +,S(88c3fa19,4f3f4d93,dbe5b8a3,865d28d6,51c9abd,519af4d2,c3b743d4,ce0cc5c1,104caf7a,f9ff822,7b3671a7,e7745bee,26b5deda,9982b9d9,a9d648e0,760d8f8d) +,S(f8738725,3434b0ea,f05e9c76,3c729a8a,22070111,6c5a6484,5532c3d9,41ce648e,a8b5eed1,d4ee7913,b69f6627,d3e1be,4c286e15,78167201,46fc9e21,786f7bd7) +,S(5fca538,5afb021f,94cca826,5689605c,7b35c8f8,dc8fef60,6b18420f,909acc33,1d7fa51,f5c28bfd,eb1f9a52,723eb2bf,c5b4d078,d5d4b639,2b791af5,d5b2b3a8) +,S(47484548,d4b2acd9,bb2cef9d,bed4bd0f,f1fddf4f,e1561a51,891464fa,cf5007db,ea61e154,f011cfe0,46fa2539,7fb444fd,b93e223b,ed882bce,3ddaa3c8,328f44a8) +,S(331f17ff,b14d43f,ec69a6fe,b41a8021,dd8ef56d,2540234a,b466ac85,e6c5b8f1,ba20bca1,8d774731,b6b64a0f,8a19311f,37d8c061,2c4699b,68d569b0,9eec144b) +,S(6aba62d,200bd2c6,4ab88ce9,80530e56,5b8971c,f9c427f1,5e9b146d,dbdc68ce,e4c936d7,d7d98919,acd26ef0,cf071f2d,fcf65fd7,78dd8c5b,ab026,e9f832c8) +,S(c76b6c35,e72fa19c,621022b4,d9ec6ece,c0cfee8d,c73254fc,f52cbc69,23ca19c5,5b0ffa7a,48ee8599,9b74d0a1,d7fa0289,b49dba85,7b21bfb1,ee7ba831,fa13efc9) +,S(c056ea3d,43f33df9,b01590fa,429c14a6,aaa438ed,415254a4,4a93854,ce1bb77a,d5b194e3,2337a9f1,2c479e7f,576a571e,58355a02,8ba0231c,c337b8c7,eb3dca7b) +,S(3d688ccc,8ddf0ef1,137bb897,e6b8f3b7,7df652c4,93cac3a1,47e88b3d,bdac938a,bca94788,4d7ae1f2,960daead,111558f7,38b6bc9a,7af3bb4a,691af22d,4d5879fc) +,S(4faffaf6,69ce1180,a9145f0c,df6fd834,c59d7a9f,5f2e49cf,3924b33d,8d2a179,9e015cac,2dca2ec2,5580c16d,8c910abb,dcbad872,79bcc485,b71f5561,60159d2) +,S(47578e3,7cd7eb78,6b5a44eb,3decc7c4,e8ba0c7,6699ef61,4ac30579,a24aecad,948ac817,a6bee00b,c68eab83,fb628ed8,7fd0066,9ebc8569,45eef8c9,67171651) +,S(9aade275,3eaa646a,24ad1a35,f74887db,8c72fda7,77b59dfe,103e1a95,dc618836,b651c384,8f4625f3,1e6e7a05,c94483f,ceef2fed,dc7f4256,87e0373d,d02bca96) +,S(b1b0c3e0,b1c6ee75,55a81852,447b2bcf,321c9b9f,629caf9a,276133af,63aed711,493accfd,624e6ff3,dfc1d0dc,aff17113,c2c74167,58cbd571,759bab24,300ac827) +,S(c557758f,b192a058,eaec5762,ff391a9b,2a0ce462,7bae01c,156dcf3e,bf2a8ff6,1d4e9b53,d209e195,9f10f671,b1bfdbf3,98921284,a7719766,39947969,6d1bd3b3) +,S(73522ded,5120ed4f,b49b5ca6,df5a1b6,edcc082,48df4d07,cc9ad923,2b7b5e0e,47569d94,cdf0270e,d1d299ce,d5bcfdc7,7f87db4e,c9725a96,e4388cf8,dee08d74) +,S(16571c0d,66328963,2a3ae46d,fc2bee50,fd6fbc0c,f0ec73ad,72bb9c2f,e8323506,65de9d86,9e001c60,ced57911,e7ce6150,a936d684,d759f143,d2224448,4bec68d2) +,S(f5fe5564,2eb630a5,c860cc8d,7c9ef86d,b1810d2e,82dcb343,dbe2f958,98e5f090,5cee7a1f,bea9dac8,ca340dcd,fc2c097a,eec2dabd,d7ed7504,e20c913e,9fadfa87) +,S(ef09db32,70d09043,43da67a0,b87cedaf,7aece017,58d78591,8814e3ca,396e3592,5934f805,ba246e9f,965c6161,cefdf4e3,7df14271,2ae076ea,80ca239a,c7b675e2) +,S(886cdbd7,27d2f099,9852b102,bf201ed2,5e174622,f974b0c1,3edfe523,373a54e8,dacb328a,7fca6e5a,e5351825,564e4f89,5166f687,38d46142,6eea7b09,dca73b2b) +,S(b7d86bcd,4f7bf2e,8529ff84,88d9f3e9,62071308,9bbd91f9,6e4ef82a,ce237cff,c6b0aff1,375a547e,a8546dc4,17574843,3acf2a4,ed0d002e,e32fd88f,3b702f0e) +,S(d7d171b2,8132ab85,30791059,395d94ec,c24f5e99,67bc6c4d,22bfc7ba,eb388c67,983770e0,2421e2b6,55d3c135,1bb39bf7,7be39682,97088744,bfe6543d,dc1a8f8f) +,S(ffe712fd,881e30e7,229f1cfa,833dad90,f01a1896,c1493502,a429080a,9794b1be,f4299e03,75d084a,10b11ae5,8f2fa2be,f18b037a,53165586,cdac18ce,54505da8) +,S(563c92b3,3226951,cd7cd10b,3f7b35c7,dff3f0de,6f03d66,9f664655,57d13f7c,dca1eef4,85bef41,80b0ccfb,2416f948,9bb21094,854f5f43,769cb37,33ec10c1) +,S(b8e76ef3,3a2cebce,614056d7,efa47c15,dd52c228,66a7d3dd,9a0d4d64,68f32437,4454e79d,652ae799,a3a539ba,42619a1c,3076ccc2,109dc4a2,2aa41a28,918651c2) +,S(36db2304,f60703a2,df2e429f,b8a99841,7b4a4f3,e08642d2,c5675460,aaf1c971,a0536e8f,5cc30aa3,d9b859ee,e26b1abb,ecf7f9b4,8c8fef0,f842fc9,a684b5cc) +,S(c5d4e51d,1521637d,d6055e52,9e9ae2a4,d537c1a1,b7c6a314,f9d558a1,88d9a5ab,c841eeed,6a13782c,fbf65b08,c6570994,aaf7093c,4b48ad0d,e04db33a,6feb3f6d) +,S(50c03dc2,985859f6,ffb79ed3,f635e17a,b0561533,600af220,7cd98f9f,71c4bffd,c68c8797,a7ed229a,f59a2bd7,451c748a,5c425528,8b7f6b0a,68b64a77,d7137720) +,S(2806d84b,3e7e6f9a,2e10ae5d,cf5bb0ef,98560691,71d31ff3,3645346d,f08333ef,72f23443,38e34ccb,4ccb5ddb,f3a6b58e,bf8cac26,4243785e,54a9b503,c87b429d) +,S(40d70ddd,1ef58dd1,c43e33c6,29840fee,82bb9c1b,e222b53f,4c58952f,388790a4,9b1a9d57,b6dd9932,338d170e,af4d5516,e654685b,3946930d,96b2a1d7,f70770d5) +,S(8850ec32,4e71213,bd77b193,ee21da82,ac733804,17983905,d0156608,8b87c835,aafff7a0,a9160b91,92df58ea,a65c0716,961480b2,b0cb4d3a,7d637f77,ddc160e2) +,S(54951509,bd7c29f4,77f9d4a6,2be485d7,de291b0,6a308bd2,c240cf71,50ad6e8b,2fdb347c,f2a23b9f,a3a45c76,44cc1fa9,b1d8f79f,77c02f27,f136c732,c7ef2722) +,S(9f5ec355,cdf8eb4f,be5af1ab,70a3e7cf,1c3d3e8b,e643621f,acf8f69f,af7e5d44,b203dd5f,b38b3550,6fda94b,ada1c4bb,12b8be5e,92bf8a58,4886f2ca,19d3c06e) +,S(812e4a9c,1bba9e52,8cb5e606,c8b326fe,8b72f0e2,3062837b,bdf371a2,2ec0b3da,2ba7b5b7,14a9ae39,c62bd36a,1ed00135,5d98d248,9ef90f24,d10d1c58,f8e222a1) +,S(e6035ddb,cf85616b,540ff16e,cd5d7fd1,af52c6f6,2a26ec36,c5c1ca86,2a3d924a,a945389e,f687fcac,6f918718,a1f29f19,173d3104,69c5954a,d6cfb5df,f01eec86) +,S(d788a5e8,d771d471,b7169693,86b2339b,2643f129,a6b50bc6,321e752,6a033446,34b85622,5095783e,fa57fe32,a7d91b65,c0c9a42f,79fe30e0,b2598967,e97d3990) +,S(663733dd,29374f9f,1db5f11,6fa32b56,c8ef42f,706bd802,5615d4a4,77c6f41c,3585d45,3ae0fc59,1614e916,a1400b79,85b59f66,6e2553c1,394ec26d,d078676d) +,S(5ec6e728,5ae1413,3dbad4d1,19e2e3d5,6affda33,356146f0,a5ab023d,b323ad9c,3dd241bf,cfc6301e,29cd609d,b1a0fca3,d35a56e4,668de9f6,45c23d69,c0884579) +,S(f18fc442,1fbb66a2,7c336647,1459717c,87532be5,d4416cee,e4a674a3,2cf8f3c5,d38a0881,3bb33df1,fb4e83,6686c0fa,26847e5,f7580de9,b4f229cd,1359d560) +,S(818f549e,d9bcfe1f,14b8d336,21342465,7dec0dfe,eea1d719,20622442,39f5345a,37ce611f,cca2f448,45c1a8e9,d7f4b3f8,48b24c5c,2b9f5368,8cc2c8df,846602ee) +,S(75e2c791,f98e3fc8,2a74ee3a,532e8796,4f4fa8eb,6421aa64,17a5b00b,1edce942,51f2373b,a4951362,68aa17c7,1160855c,7c4d37be,222a8b26,f2d54cd5,37e5f6b4) +,S(711f8db6,4c0c6d31,daff4ec1,f9937730,9b44de59,9f4b9119,9ca6836e,1e3954d3,485955e3,432ac82d,f3170fcf,7b3519cd,6e4dfbba,170bbeb5,b75e3e7d,70caa4b0) +,S(63502c26,6f755a1e,4be2104e,e8ca9c2c,5216f50f,e21e003a,16071bc8,df3ba6a5,a5c829ea,c8fc3f4a,f55075bd,afe77977,3954b88a,f1f3a8dd,90f28aa1,49bea942) +,S(2a677459,ccfee070,1b5bb82f,d2ee57e6,13f5c3d6,3927938c,92d10954,c2850872,ff6e9798,5c5db0b2,9bb1cbf5,db03e0c4,401bbf28,eb39e053,714d5c5e,ef7cbe71) +,S(68f85f87,78fb69a7,9db4a22,3d7faf95,5b359bd0,5f230d99,f051b008,32b52cbe,dc2e949,f4fc3189,c4f7b771,f8b56b85,ef9c65c0,b3bdf89e,405d5f3c,ffc92210) +,S(7ee9d5b7,3b542036,509fd8b9,de590b24,9e9883c8,6f4bb90e,1c2904f3,d2b74ade,98122413,241cf205,f91c6977,b925ddd7,4920636a,9dd67828,2c30ee,7343e62) +,S(8447b090,cdb19271,db368023,d40e520,102b5d04,79eaf391,3817553e,24aa33c8,25b43fe6,55a27481,c02eacd3,9025a523,121d02a5,78896a78,c14a8c21,f7c0bdb0) +,S(316073b9,3f1550e6,b7951f56,90c5ef3b,e45e622e,77a2f268,c89a8b6f,2816a645,557ca5ac,2fc70ce8,c3e54a4d,b31d5c77,a1a6f38a,18646caf,6a52db31,947624be) +,S(fae2312d,a2ef21ac,358fe949,5c011b18,cdaa8ef1,c99284ae,5fc1eb77,4836d6ac,6f3bf661,2d99e865,b0cee7cb,3fccbf08,aa948ad9,b29b4b8b,54eee8d6,3f0b76c7) +,S(280a2bef,5588dfdf,b1e1691,d3162aec,7d4c72c6,b76f23e1,2f8ce783,54f839f0,f00ded1b,c31a9774,a87be1bc,7e440585,b2b4ba63,cc0d9c73,ca7b4873,93683f72) +,S(3f43e77a,9b7b92c9,92b7e5d2,8c75444f,4d0609b2,8aa65ddb,86bf6ac0,bf7ccdcd,b3117903,bb3c0a54,bc6bd6b9,92b8d85c,ac267855,50075536,6cf70a84,4c72a2d6) +,S(3a0afdc8,387bfa34,87e53929,51e10e,9535500d,f6506296,46df162d,d469f2de,75697cc8,f0f690b6,e1f86dff,3f2ca53d,907d4cf7,c8ff324e,8b4187ac,c9bb29c7) +,S(3f972b64,209f723e,99a6e7c4,a72072a0,2789d0aa,8a23d660,949ea417,7970809d,7a482428,7a6bfd9c,afc26e76,9a02c5ff,9a7c053e,e63f2342,b32a4f84,42e47d15) +,S(938ac0f,fa73364d,11df1ecc,4b84e3d1,c21ff85d,71f404df,e8daf4a,e889b0b4,a1c09ed3,d526b381,dee8820,eda06a10,4e968995,64cb604a,63fba74a,45be0312) +,S(5355ed75,2993e74c,275eb9ed,3bd34575,631c9b5a,64e99071,8311ec8a,f4e28bcc,bbb1007d,680aaaaf,ac10279f,600f1851,c8188547,3091a40,ddf7253c,fc6ad088) +,S(880e32ed,70ba76c6,d83bda46,a4340646,b033696f,ab62e457,4e989311,fbf277b7,76feb2cb,8d10d5e2,406763d5,f696686b,8dcdaaf,330e4e7c,623cc7a8,6e0410bc) +,S(daf778aa,a2755b68,2b8beec1,14b74e9c,857ac503,daad6bee,bd6d11f1,8f11aa8,1ed85c62,710d3017,9f0f3024,d6ed6976,e38325f0,92981c88,fd4982ee,98a5f428) +,S(e5c3e9dd,75633d27,d19c0a4f,f3533f80,9a60b95a,42c8c464,1cf7abda,8fb474dd,f1e56af6,5c5c087e,ab303699,f20bc9f,71b7b7fe,16332afb,2517b241,8dfb0dfc) +,S(5d403f34,ecdfd870,977f3bf2,ffb274c9,6859c20c,8cd7afd,d116837d,c0096973,6b5be47c,70246cc5,9781ba70,71764d72,2335c334,443e5e4a,5ba12939,d8f6aeb1) +,S(50ba053c,25511f71,b35b4caa,c990ab27,993bf034,f3e55f81,3896458b,5e246b12,d2f12a45,5c7a3170,67690399,3063dab0,9f8a22a2,bccbe5b8,8a7fa2bb,f2291f63) +,S(385a80ea,ee4aa9ed,a07898f0,b3658793,9f290622,399c3a00,b38bf02f,66629cf,a543320b,d72ac6e5,d750806d,6a90b8fb,8cfe4902,46991eb1,6e6a6974,befcd50) +,S(98610b9,904ede38,6e69de6c,3d40518e,eaa669a5,23620a9b,276e1996,bdedbef,907755a2,eb6aa100,d0cd0c89,25b5ff8f,b88f72d3,61d83315,c4ad0d94,8a06b581) +,S(83f10807,d9640d3b,dfc7b0d3,dcbbf6a2,19ae215b,4754f29e,d4579999,191b7fe8,f62d231b,5095d6d2,860e69c6,509de2c6,fe719e4a,fbc2b5e2,489a6f9a,7293364) +,S(51d22293,927335c6,2f82b028,52759fd1,e3f287fc,67b6059e,74dca17d,4fb1c246,b9b803a3,b5145b4d,188b909e,23c8b126,f7184bdd,3128587b,e05219df,6c63745d) +,S(be6a6342,e5783d43,d2691945,826c37c5,3e8cc24c,5a09fc70,3689def1,4f465006,81c718fc,3fc15279,da9efc14,13ddcfd8,81bc7e4b,1c36004c,2008dba2,7c7ef000) +,S(f21334d9,c36f1284,a4bb043b,58b27070,448681d9,a82ae81,a4e8f226,bc790036,44851,3a87fd30,453dcc1e,b21e293b,f5f143b8,2cf2708e,428f4753,8816eff2) +,S(1558c4e5,af34463f,810cb1f1,b73bd27e,932a832,9253e2cb,419eafae,c9730508,cb8cc650,25e14132,3663f3f4,1a75085c,6921bb37,428a149e,2dd1fe4d,f4c917f8) +,S(1853a32c,8f6020d8,1b4d3596,a88e3825,b575a4ed,99072e64,2e090a1a,665d71e2,8da2a31c,b170aea0,e364aedb,b1861fff,eaf26888,b29405e8,9e3ed982,a03a8367) +,S(ef585cbc,f0f03bec,21df60db,cfafa9dc,2da679b4,3bdd590b,8cc35388,cccd323d,2bc5e203,aff91f6d,38592e07,82813905,f02ccdba,1daa7acd,d93f6d80,78c2f296) +,S(3f4c2e37,e1ea68eb,befd9971,424b3c7b,1fa45864,e0d13471,a1476808,b2fd5c14,678990ab,3d283b3f,aaa665b0,4f329b51,51b9c17,b54385e3,30de36a4,7e0eeaa5) +,S(2b001b8a,d557ac4a,181cb20a,b3abf2ac,59fc9b10,20b5533b,621b4119,e421693a,fe4f3007,f8d19db1,73e6f8c3,48b2525,310a328d,2ca0c675,8fa29723,82c6b94a) +,S(6589d1dd,7d5a08ff,8deb5b10,81fb0ca3,79c90d27,58ba0bb9,8d05bc93,452cd229,82409480,ed842993,4bc8dca2,3e000fd5,fdc5475b,192bc7fa,a1179165,9a8149a9) +,S(c233fd0d,f5d3835b,8e855316,276a6a90,ab4377e5,a3a3a8a8,3b89f0f5,27950a6f,8fe8e0a8,775a9a43,12cfbac0,85aefe06,31468031,86b8b84f,49422392,b4f6f3e5) +,S(82731a85,2a848eee,d6ee3f57,f75a2bfa,a388670f,89f6f6e7,b65b13d0,764cc14a,c00c509a,307c5e87,f1b09688,38e34a21,1ffacc9b,8a7504c1,2080e4cd,39117583) +,S(e21387a0,e5d8b1b0,5aab5150,122942ec,c9dde3b5,53eac977,7eea6d0,6d2a50d,c74de509,f90d93df,343c920,74ac5dfc,fc81c95,93354349,bb2810dc,8db0ff83) +,S(180871fd,2fa443d9,c5b5c341,4d635b8e,92780fc1,6d04236a,bc9a49,5b9fdc25,d66c7150,6008f37a,422014a8,bbe025c6,1f25e636,e2778146,59985003,ab14d120) +,S(b5a9de7e,ed112a8c,1ef29a9a,31156b90,d64dff93,4d83fab4,b44d30e0,bccb2c42,5675a679,a81c3faf,3b0afe66,ef85721,30315ccf,b0ff3d91,130a71f4,3539db38) +,S(9acce472,61f1e50d,f42baf78,c05a5cf9,8ce5697b,6d3b4d,40d011fd,aee7d6,fb7c413c,40464c69,e5756830,4325a79e,6cb8c831,add29e0a,95f88feb,9507a05c) +,S(f80ab79b,7bef7712,2fc7bc4b,2193deed,916796e3,67d85100,a02cc0eb,8b574e5c,c6d4d03f,842d8b25,d9b5834e,d6cdcacc,3a1ac585,b5d22aa,c77019dd,1d5c8f6d) +,S(b431e355,cdb4230c,3005845e,f60b24d9,25d81448,ec161aee,faea1f98,bc9366a1,ebcdadd4,ae230a14,382ddb98,552b6e69,7aebace5,5cc7390e,f69d176d,da41c1cd) +,S(7aa60b9e,8c93a63,51eb686f,46dca4e6,efe56edf,6c2089c5,93f6a800,6e75f1fb,4c81e2,17700bf1,e053e389,2e33e694,cb424800,b4068c98,328f00ef,5fa0347e) +,S(1a88250b,3aa4243b,7441d1a4,da37b4dd,d304fbef,272f696b,a72e7610,1286b30b,625b9d05,57d3815b,e06d8cb0,f056a9be,b33b7658,f459b3fe,3fd5f4ab,69b7a473) +,S(5b524eeb,49fd558a,2d34db23,cb5dcee1,6c4ca47,1eba9966,82dedbc3,aa2b57c7,2d5c2c97,56b7ce1d,735b163d,56e02bdc,25c8b227,2bca2d5d,bf252906,bb8ae50c) +,S(ae60e777,6d9fbe79,d24cb8b1,f756098,191550d8,433bc7d1,8920584e,7676d2c5,430fe986,73772472,ca11be83,eefc8678,b76c36d4,346a311f,fff926fb,158d7784) +,S(323a5e3d,9e29f668,d460b081,a5dce6fb,53e8d730,9c42f105,ab4f812a,62efb9d4,2b457b3,69483f42,16656d1d,3b5ea9af,da6216a4,47661830,c2fa56be,804776ba) +,S(acd1b5b6,591e552c,864e612e,d69429b2,b3db8649,7a5962a2,a8b36511,7a7c1c9d,e6131fff,56f6900,926ba772,92526348,67544d25,1ebc4fa3,26562190,8e6e88b8) +,S(6b4e2c70,817852e0,2a26286b,155248ce,f2e61e60,84742cf1,14ebf74f,785b36f4,8400951c,7a529bdc,2007130d,1cd5297,7398f7fc,d66e5a1a,1e4e8644,e5b893df) +,S(c2b2c727,d67b0a46,1f44cb91,48c34744,4e6a375f,80a817c7,1517969f,8084ab31,fed64359,ebf37b0f,998aa251,9c281fd3,a52c47d4,89051718,82481fae,c36d5f59) +,S(3385eeda,b5b2bd94,f794609d,978a0f86,2ec56422,bda0ef71,2207ca1b,904f202e,2808d2af,1db8a8ee,1c5975aa,31a9f441,b12a2186,746a474b,4907bb71,603d7443) +,S(800f6cd7,f2cb437b,8470c292,3fab4786,edce15f0,d4c52a82,33519c07,14c803f2,981f2e3e,ad11aa0,cde06ca5,9b26028c,23e0a88c,ec397734,ad0152f4,d4a37056) +,S(73aa0bff,3a9f286f,d3c103c6,d7bb6caa,ebfaf4b7,614f1f32,4741c600,eeaa2f3b,30bc529,81a9b983,cb89fe73,dd8bce7c,a54a2e3c,4fde6846,701f1da5,18c6f7b1) +,S(9280fce6,ab5c13f6,80bfdfa9,c3e7612f,86380498,9bcf29ca,789169fb,a178f2c5,4b96ecdb,c5bc2e3b,2fd397b1,8f7f5bb7,aeb6a7bd,db0a3b23,3e74f7cd,ccae36bb) +,S(72055c4b,bb50b9ef,b589be16,270d12ea,9f438780,e5eccb81,7dbf69d,a66e03ca,aeaa257f,c30a77dd,58574693,51a8623d,985ad705,aa1fe3f9,8915a6c6,f6bc7cb) +,S(f0f3cfd0,c771e3db,ba687c7,3fa827cc,eb8e887,12abc8e0,3085d047,e7c1879f,2c612e7d,8b744c54,f8f0c4b3,24fe10a3,1bf8aa5c,cf6e2e20,eff98153,a4936b33) +,S(97c78b65,70b0f270,6fac8fe8,ea4376ac,d8b61177,e5e7b3ec,67a7af9b,e6f5dcfa,1aeb950c,fea95858,a97da765,c3cb5941,1334609b,347c4daa,1279d58f,3be14c3d) +,S(b39ffa1e,967e6fc3,d493a9de,48b077fa,4b7908f5,33a73eeb,9b5c9efe,9e509e69,f55f3951,81ac72cc,d618c00,71c817e3,f76c302f,11219e16,f3b40e75,ea65dc05) +,S(159b9f7a,5ff27c4d,1bc3fcc4,d91b160a,1b2a972,8b1e7fd1,7351c663,f8e7baaa,b0aaef6d,86c54ffa,eba1c7f0,d789fd41,5e60127d,578c2697,a8380b7c,a4c4360a) +,S(25810fd1,8d4a4b16,c7d07a62,b626fc45,6dff76ab,3c1361aa,f729f7f3,cb85e44c,f4052325,7794bd70,ad295526,ab4b8b00,9252dd5f,33578f44,2fcd2219,2f6a2d9b) +,S(27e20a4d,5860a328,c499864c,9075881,eab36291,2da641d,6e1934df,473352b2,c1598cf2,888cb50f,e270b490,503c11a7,d3822a2b,e86a7b,65cb9499,3b45203) +,S(483f454e,517a94e6,6708f465,ba217b60,6d684a68,4d9472dd,685f3bd1,341448a2,c13b3144,d93b9a36,45d23346,c5dcb70d,d338d06f,f0c22aa8,2ba68a61,7f9ad11f) +,S(4f47b2f6,31734aa6,5ae4c9a3,c1532f2,67ef068f,83a4f266,e2e0a59e,be04e6e5,58f1f34c,f11b9cb4,5d33ebcf,3edab72d,6824392e,dd547a27,c0134d66,58d99d2b) +,S(aa78eb7e,835ce84,b4f1774b,8e810cc0,d6f42a2b,11c44574,24fb6341,7797db07,39640139,d957d47c,59cc7541,d3f72e33,ee9ffc63,2171311b,cf493ea,185e743b) +,S(d2598985,83c00560,11dc0655,c37580b0,31a3f1f6,aa5f5944,17eb0ce8,cc033d49,3641c973,9de8818b,a294622a,2dabe172,b6a83006,4d04ed8b,c841d11a,5af24b08) +,S(4f023ba0,ecccb74f,56f38f87,7435907b,f2d2b85d,59f7ffaf,48712a48,55e26634,f80efc33,b15d5125,b8fc35ea,393b6c3f,a2ae094b,86a6ee95,bc48295f,b1a6a456) +,S(9c86f08b,48abd097,7bfe13cf,a15ac8be,8eda4128,f7c143c6,70bf7e2d,763a1589,11511f87,e9ecb88,39eff453,d29d4db2,17d4eeda,c8468a17,1a152f15,c1c2df45) +,S(3808718b,72e95cc8,fffaffce,cd57e269,c5e6ac77,69d8120e,9130d8b8,ee137576,f40ebdeb,cf5eb722,4dae59cf,8ed8b58c,c614a61a,9e3ccee,db789fd3,31ea728f) +,S(8defd15e,399492fc,71c80a1,3eda0244,eb349f47,5c7dfb42,55dfa752,fcdda3df,d3dfa778,4353d0c3,b4fb0d19,ffc39631,a01786ce,e3b0cc7f,f9e4c5f6,7bf7a771) +,S(8dac8d42,5ac1d07e,3a805248,798fade7,ed99f9d0,c324bd46,6eaa8aa7,dc24bf8a,f232fdb7,923234fc,d8b2f097,d354dd6e,62e9d926,20d28087,d6410b72,aaef8009) +,S(87ce6443,f3bce6de,287178b4,f243c3d1,e45d26fc,6f811d35,329cac04,7fa90e1b,a897019,62414ab4,c582799d,77edf3b0,f0af0d4,8f70e875,fac33e95,39724138) +,S(bfee6bfa,bf2fb77e,9eb60a3f,8b7775ba,124a21c8,741af3b,d22966c6,a8fdf08d,29a1c399,541e4bf1,7f26f215,52fc83ee,cd2e8be8,40b83fa6,9c9ce5d5,fbba76f7) +,S(c30fda5,b553a800,2341a553,7aff72b8,44873af,8381b41d,aef9af2b,5cc2bec1,5ce3eab5,87d88d64,fa44aafc,ce34046,8a96f76e,65dd562e,7d14153b,439fea6d) +,S(5e7151fd,594119d7,c5cadbf,e1b419f5,4d922607,728d70a0,f1dd4d8,3400ed97,6a8ceea5,a51d4a7d,62d36816,ecbdd75e,4c64ec96,6257820d,73bd9531,ea108917) +,S(a9c8eb0a,66cdd5f0,f33d0620,8525225b,e974af7a,b50d47c0,4bf690db,ec86100b,f209cb9d,898731f3,b02a24bc,ff290cd4,a4371e88,f3aeebf3,29c9e1b2,8b8e0a2f) +,S(955e79d7,93669150,471b1659,52ee6121,cb804373,5faf653b,9468511a,e3a2e439,68daafe3,2a9b67a0,10ea3bcc,1a56c7a0,2e82cddf,120e6826,1de0700b,cf930d06) +,S(22d27293,d3c1cc6a,5b3bc70b,3bb7e1d6,bef24805,3c8685fa,c192b409,dfa34a71,c74b44e3,4fc855b7,53c4e42e,740dbb59,4d5d76df,b39de389,e6c837e9,639b3357) +,S(d19fcece,f74b1767,4e9d91f7,1487aebf,70d46e8b,3a037835,b2f4e8c4,2137abc0,5b904bf6,3dca0981,3ce224f7,494ccba3,34ca9fe5,f365ca83,fe9adb6c,4adb7580) +,S(6aa1722a,adc74800,d6609113,6bc6a6a8,1723c36c,524a873f,2a437c91,c2336899,8e97004c,df495b4c,63164058,321549ce,3d9709d9,ed667c36,9a4f1ee2,d6319d07) +,S(79bd3623,9ee4d220,12d07262,e96ad186,b16e3b6b,720d052,f736f851,b0ea6efc,a4b43db,f568cda4,1dc972c7,1c7c92c3,6a31166f,ad6b80fe,42df4dbe,f418729e) +,S(b5e02b2c,146337b5,67583c5d,3690c0cf,a1c7d820,14028c9,e401182f,a4f16ce9,4b9646be,3facaa70,e793516f,a0e8a142,1a819ba6,718d26fb,938e2795,9069e0b2) +,S(8e33e271,de2deb4d,6444d64,387f790d,14a2b685,4b38857d,d05af743,7a7403a,d82d53a2,c7a4a1dc,eaa011bd,3e0ceb0,e08d73ff,fe9d71f,c913d6e2,89ecc429) +,S(4ffef442,77dba780,e5769b73,feda37da,abd6ef6,2f5ec36b,74593ca,4c19c2f5,a8a30857,378aa0ab,9ef6162c,fbb09f9b,796755bd,67bf9d69,4a186813,d392f854) +,S(878b652a,633805c8,b58b0334,11b9246f,f7e42ed6,ecba6bde,fb737126,e9f57368,2cfbeb3e,d691158c,d3ae71f0,b4a41d42,148c9711,d274820a,d541efc1,56e85836) +,S(ef9a6a15,aee2aed1,46b99ee6,a79acbc1,a0aa0b2f,1b082783,dd40b106,9deb415e,9909e2ba,7fd53514,a1bc4480,2b49c7aa,cffd9cda,19c54d82,6d9bd08e,49e97240) +,S(d5c248d4,1551d230,60835885,5db0ce8e,c7814e18,c2d548fc,dcfac207,645bf37,f9c5e89e,7bf01f82,8cb78c88,c14d50a8,3cbeb45f,82daa9cb,3c3804c1,15a36f05) +,S(edbf25cc,a526157d,9676e614,81455682,6dad0c0f,96ec289,f2c5919f,57227d1a,6845488,be056456,4a833c13,70d37257,d0ca701e,f6e574c7,409dae02,ee99554d) +,S(5c12026f,b8e2e376,2521f5cf,cf9286b,14be80b0,dfcf378b,26fe6ea5,4ccf09bb,3d98d5ef,4e2c6528,7950154d,581c9a44,94b81a50,40ba9047,1bfa6758,66daa596) +,S(c27e6243,5056055d,614dcef7,f67338c7,ae728bd4,af3970a0,b434d2b2,e48e2d6d,76b586fc,703cf946,f78839ee,bcd28c56,9e372d97,856333ce,d82137aa,7d8525c9) +,S(a298c704,5140186b,4d424e8a,f98ee604,7f67603f,ab14a7e7,b96daede,51abaf8a,4dc70158,2a1b1935,4a5b430d,b4a0c21c,463b779e,ba72f108,5d7d638d,68108fda) +,S(e1fce957,82dae8b0,ac204fb1,9496a332,fdf40014,88ebaf07,3a62f33e,192013c4,40517ee5,d5786ff9,6aa2b50a,a77a10d5,e8ca6439,44f308a4,a264e367,1dd02f4c) +,S(e719bfc,5f06b800,8e4e49bb,62361544,1a2b0c42,f673f3a2,1e139bfc,b097d6d,f0c83c1d,83f5df68,a1e3929e,e18c4945,d14e2c33,4f9165e1,a0a462c2,aefd7822) +,S(77766a4b,a9d8cd62,840b1127,7e697dba,dc27c50,d9c8e35a,17f4c38b,50ad0a4e,5b3a2ad9,448327ba,22265ac,86c190bc,60b7586e,6f6e4554,b26b69ce,231b6431) +,S(f237721f,6215a855,f8660cda,41063690,4f2c9dd7,6260c292,597f28e4,348514b4,109daa1c,187b9f9c,ecae8c6d,33cb9874,46f5ade9,d57b3651,bccb9338,1acfef79) +,S(9408f5d5,e8891a1a,3cc4e7a8,a50dbc83,97517b3b,6c5af3b9,f1953f09,a67b3e08,1b82f212,c32f1ae9,c95f87b9,5ced2bf,ca33e954,32d9eb90,ed879ec9,59691555) +,S(b2837655,11707926,c9f214f1,3e90675f,883649c8,8f1660f4,c4f9f6c1,552a7064,89553739,15be8962,e6f03ca4,5bd5516f,be6ee041,519c4131,829b0bd7,6d1abfcf) +,S(ce42ba00,36b5f54a,4ed2a9dc,b6c0d29f,98641331,3f92f9c1,43767bfe,4b78f4a9,6e547292,cf35baf4,768c9179,2f706bfb,39b757d8,2452a802,4a7bd37f,e3a048d4) +,S(bb3da2a0,1a063a97,bec9325e,8d4af420,d3942bbc,42f0807d,8e9edce2,eccbcc59,7c28e7aa,bc39907f,ef58a19e,cd6e7b2e,25d6eb00,8da14d9d,5c559d27,fad70b32) +,S(25d84c3c,97f3a074,ea76f620,d6fb2691,dd3e9592,6de8eacf,5392c0de,36c490ee,5e0e7745,88c8a9b,7b91ffb,1ad6fd27,4153096c,9e759540,16f73a43,16d5651a) +,S(8baa3bfd,ca256f18,2e55a01d,e7c07617,7649942b,e743b4a7,faf39446,84d5765b,6147e897,8bbe15cd,171c2816,4730b211,587bfe7d,7e71811,7f308b80,d30506f1) +,S(507d6a0a,5b7d0385,11c65dea,8024c706,4d3b6b9b,287c7192,74c69797,21f5170,139f1647,70ea83ae,47ea8141,c3ae32bb,c12793d0,ce30fb37,2dc3adad,7f7bf5d3) +,S(7c6992f7,a059c5be,99b7354b,10fc217c,e45620f3,b4ff30d6,d640fd3a,26bbd846,964474e,fa63641c,2aa7062d,7139fbec,1f64e6a0,50e1ad03,2741a0e3,b5e0bee6) +,S(f62832b0,1bf65b23,14850750,9ea6f157,3d5e3317,bfb0a38f,cfcc6692,5a698bf2,d7314810,7b8fdfd7,3f6ce6f8,84f375bd,1546cc3b,8c75840d,3668c2a6,54c1ec95) +,S(aed21cab,d8e4ec36,2b4e2091,3be3c7c4,594f628a,cccca657,deff8b9c,6bd1ebad,f800ef3,833c7ffa,a8ade1ac,2f41d456,b880a205,60251dfd,f9cdd672,bb7100f8) +,S(46de78a9,343a9a41,6b91b877,aca94252,9e0ba8ca,b6c01f66,e06cdfdb,714bf405,c1441252,4f3ea73e,e12809c6,f19184d9,81f30b87,8df8bf6,6c89d98b,ed4d2bcf) +,S(2832b5b8,ab82e932,5356ae19,3043b99b,ba4b6591,a750a174,a6303b42,54bd41bc,c0363e03,3f59f149,23ece534,cb86695,a8275224,f490849e,249e63f7,34aba440) +,S(5b57c502,4b5c1621,77cabe39,b0f36bda,d85bd04,2736c7ff,9395f0c4,cbe43f9a,842407,dc0c0473,f1bf57b9,b35d84ed,b282ed4c,7adccc06,8530c8c1,bd20766c) +,S(b384da70,8063758e,825bf821,196b4a9c,814c45d8,a9edbaf3,dac4cbb1,c1c2f103,e6e661df,6400cc40,74f92596,590757fb,aa237515,af30ed13,df30befd,230fa325) +,S(36ffd991,563e448a,9b0a5da9,61a5466a,82a5b672,15ec8f8,e7300c5a,5c9a5e1c,97067835,fc17d47e,94187320,84d722a5,d1a742d6,4b513b6,32aed897,bb961c) +,S(8367e452,57c20367,e4e60ecc,33adcd87,5b9d142c,c6c362e7,f5aab34a,ed89d81c,fc7ff090,3ffab26e,d38a9e74,df43819e,b77ce2d5,bbdcf27a,f1f3bf19,948be121) +,S(f6ce47e4,8cc9296f,b40539e2,41238658,4719fb9,d4b1ad17,9c98969b,dcb0564b,368d2b6a,fe5edc0b,6f159017,5cd192ae,b524a606,7800aa60,930d60a,87950e1c) +,S(438c23e4,bedaa6f0,527d20c2,a725645f,194ccb58,8ff9eac6,5091da7b,f9fae013,54620a30,c7218257,dbdc93b5,3722a213,e28dc6bf,f8e48b45,6d6131a6,92ff15f8) +,S(1cf672f8,cc9434bf,7ace8722,85db5017,a1fa1dab,2eec7fbc,84e8492d,86bee7bd,d6d23d25,8bb4dda5,957ccced,b767cefd,8932b511,e2479018,16734bc5,b5cafdcc) +,S(47464e12,b0fa29b6,6e9cd157,4f7155fd,94305263,8af28bf4,4325a23f,3e1a2861,209a41d5,3bcdf566,5e966e80,84869a83,a2468db0,a1b19e56,81da864d,a2b05141) +,S(a9adfba1,c884c904,e6877a0a,9bbf73ae,c4ee223,e0b02ff8,717c075a,6aa964a2,ab16101d,a7bbc35c,73f371d4,afeb527e,b2d809a0,8487a36c,6cfae404,83f79dc8) +,S(2932fede,10600312,5ac983dd,659ecf95,824421ae,5f417f97,d3101c6f,c58edcbe,8601a743,e666156b,f2e34fe0,6f4cb11d,2df54fe1,7c842176,e1075423,dd76d727) +,S(5abf5528,2f87ad04,202b62c5,8ff7440c,64f9ba39,e6989df3,e31d665a,73a50216,71dc4e16,f2705c02,2ca1b37,edc5ce0,610b6f57,282d69c5,d3269295,5a1fabf) +,S(419495d,9a121cad,41887fc2,82c2c7e6,f359cbc,d24093ec,57399c3d,a801843a,9152e197,40295094,11afefc7,83990732,d5e6617c,d8e389d2,956536aa,f73e0c31) +,S(fca38482,ea2be49c,3c27979a,5acbb158,a902bc4e,226f7eae,4165ab7a,29fbc3af,1260735e,5fe62664,90a35f0f,7296c441,bad50a75,7b00e34f,28856a6d,cf46f5f4) +,S(bcf05123,3d703688,aca13530,55bc3ab2,d499ec40,8c912de1,4b7af456,55065d06,f32657f1,645690b,35484e63,a3456885,c16d4991,7670e71d,5f5cb65f,a5fb8bf9) +,S(4e70b42f,6391b424,b159cf74,f5fc96b2,68bf6cc4,a26658e4,79dd8a4b,8e9a92e1,8ba78811,1af31c06,77b24fa4,4b6ce356,66c595b2,3fe4fd36,591b33f7,e53b3688) +,S(3fe853d7,331c60ea,c213703c,a7628dc1,3cdb83f3,8a25ce6e,1be84f0d,e921742f,fb7dbb77,c2c6dc13,6f596154,f24dd3,f27b5125,9b60476d,b230e54,5ddca5d6) +,S(d803377f,c3cadd40,a2aa810,4e97641,ca42f582,3ccf65f5,ee726349,f648b9be,6357b84f,281a8080,54ab7fbf,ecc113ac,aeed2118,5ad12900,d824719e,8fd6fe99) +,S(5ddfdc7b,6f9e7162,2b7128e,77fa5ed2,90a05114,d6d8b845,94106c9e,70bebe1,23eaced,fa1efb7b,fae74c81,a1d03066,714b6dd9,1198e1e9,d5911ee4,55cd76ad) +,S(5f150243,c964948f,e91353e6,b12d167b,7913b46e,d7214411,5bc4cd4a,3b6f7dd5,36abc949,7a2fd102,14991e2,ba360c4f,b18147cd,955754e5,71cdd4d9,b8af903f) +,S(24b4fc97,5b0c4429,7a4cc89c,2b127eff,539f022,61544e14,97969bac,50942647,bd3e3fdf,54ad07d8,5faf8881,9014a77b,11d96f2c,a43cb8f9,a823a257,9eb5c207) +,S(a020eb35,5556b5fc,9de5ba2b,28644187,f11b18ea,ae61e76e,1dcce55c,e42202e0,9a71ea1f,1ad2f457,16b667c0,a982e2b,9765ac6e,beb71880,27e9059d,56639f66) +,S(60b79064,f7173eac,60531de5,961c47e3,8a224c78,ccd58a0f,86f6afda,e1f37a68,3ad04a4c,f6a47d6d,6b9fcbae,d8b54a7d,3437e4e3,627256de,cfc5f38c,490786b7) +,S(835cf1f2,b57d1e54,4ac2d915,9c8e3fdf,d2e233b5,d1109685,1777b7e3,a1f7d60b,7d23dd35,136edb13,bbbf7733,9e1c6e01,a7112f95,7c929b16,1665ca07,f6a84b31) +,S(faf74350,1f1edad5,2e1d8ad,e4b319bb,606cd7de,3188cdca,b1d39527,837f77ed,dbbeb0bd,bffb580e,4b9a0fa0,39b7acff,fbcd11b,85751476,16d48bdf,fe21bcc9) +,S(396c6fc3,e072bae2,3e7f74ed,f121271d,44091a5,d3b2a762,a39dd77,d2de938,5de522f8,a143544d,4cc49940,96b8f54e,9fb0e076,f46aa479,9e99d162,de6fa667) +,S(f8ab940,ad8df2ca,1c2622f8,8ef20c,ae8e605c,1e43b684,881147ee,d44ab88,99fe5281,fa7427ce,c9857163,f804c429,630d28f5,33e451e0,4063e70d,ddbff796) +,S(a6202868,af6dfffc,1e122219,a829c374,8197af4c,95901e78,75841b46,bd772367,2607a20b,c82a49c1,4fa54eb6,f8d68d8f,fe9c4fc4,d83b0e4d,145f7bab,5e9af45) +,S(b12d1993,49a98616,90764484,23a4fdbc,cfbced5f,b76a2bda,4d7c4984,7067e41e,a66dd4e0,ac6c8ab1,29045eb3,f7014449,73fdd555,7001b7ec,99c33d,c2d75e31) +,S(8dc8f55,b4990f5f,c6cd3b97,b1938304,9ae0a734,5cdc9c64,fd67bdc2,15eb3b3c,1be7457d,62cc8fd8,cbb54cc3,88722f4a,c00ce438,c847c2f5,fa641119,e5435d83) +,S(2c0b7337,d1db9e4b,7d521c4b,36351f65,f128643d,add0c53b,1b6d619c,658feeec,e8a27b9c,eb7c1219,f7bdae62,c07cee15,ef765b95,3b082d9a,d8b99005,b39bad53) +,S(891fb4a2,50100689,a48a0d1d,22414ab2,b17ad692,bb5f0d0e,66d06c93,da3bab35,41685166,cdf43c13,be93c03b,c347f3c8,52d06c0e,661a4c7c,cb53e24,e7260f6) +,S(4a3379b1,511c440f,e7213729,de668ef0,39fbccb6,1f5bbb02,ed99ed3b,69ca1738,b12a3dc2,39e9191d,a89d75ee,db44ce88,17b3bf03,d4172b00,39d1464c,a3ac0f6f) +,S(b8b2a7c8,54936bfa,62da360f,83bf8d65,7dc58e3c,1c8780d2,2c57061a,5f815521,8f922c0b,7719362c,25ac7001,252a89ba,1cd4412c,17c2b021,c065a3d5,f6a2d15b) +,S(9066fe19,d1dc52e5,7a9341d,77cf73a8,2fb56ab6,22c97a7f,f0191247,db9624fb,23d19b43,6ec3c575,d81d4929,eb6789a4,c281f5bc,abbc896,2153e7d4,711baf50) +,S(bf9ab14c,19c9e9b1,ca1a18df,2b5b73ee,9f32c8de,6e6757c2,b0534d75,7cced8ec,ec969923,3c3863de,aa72418,837a3af7,85e79375,d5bc91dd,481f174,e9759c0) +,S(68b2686f,9c654a02,225b7e69,e8eb7aaf,78c65686,e885b1af,10265e35,316f9d03,1d3d7504,846826bd,8554d407,739272ab,b6b407d6,f4f7419a,f284b81b,f14da9c3) +,S(20dc8385,5251a21a,94c989d3,92c9f36a,4d3bf669,bf7bda7d,e0427003,710bf2fa,217e6ff,713d329f,f9080c4b,3608c877,975d6794,5623e24a,974760f0,95aa749f) +,S(f0beb49b,555d998c,a39859bf,8c59184a,5c822e46,9a875568,1689a1ca,b5fd0368,e0e05ae7,a23ddf94,ba33b3b3,a75d3f4,2e8ff7f0,160c32f5,3bd08b40,72664aac) +,S(8f1aa973,270ff9eb,a41ad8ab,f2cf4c44,16ddc1ca,dc8a62cf,aab5a3b5,c4a0ee57,90aa3660,e3409b17,a86a441f,12f6eba0,2bd6bbb0,2b9a0a87,71e30a6,595920ec) +,S(c71eb135,278dc147,60cca847,a934b880,fecd43a8,aac9dfb,41382283,928a9957,41667f77,485281b8,89307ad5,decc7f6f,51d23ac4,7e71b299,a9d8715e,b6003804) +,S(e900726e,50d66a9f,37d09df5,d4965c5c,fa904d3d,bba1d180,b0fed651,bec41c81,d520aa30,db036f40,b34e64f2,cdc7d7b3,117f7725,786e9d5a,921c5f09,71028e39) +,S(940ab0af,a15952a9,ba3a6d6b,720efde6,58d72dd7,4f87cc3b,edae63b6,123fa38,fec6d747,6f788a19,b85e443a,a49116e1,6e5061f0,53c40702,f8765d28,f3bfcbcd) +,S(9a13de6d,643e9866,4df3ce9d,412c5ce6,a03ba46a,640c6be,8b62e70c,a8c76949,42388296,6c2d8d87,8b4c914d,4bc6cbc9,a4601630,2eb6a499,6103953,6926dd5e) +,S(438429b2,b31129d9,4a86b149,7d7f62e9,b76d6a5d,bde09d6f,256f83ba,76c577c3,e4360186,a7229be1,b33cb600,be833bdb,b2398e98,7c46c6cd,2e44f932,5cbc1c7d) +,S(3f63b25d,66d472e5,fac7b524,e4300e97,c8bb59be,6a3a8f01,8c1677c6,96d02605,4587042,158e19cd,2bd19bbe,5cff2c69,d9971aa2,fbdf649d,44c95f3b,50ca39dd) +,S(661fdf7,17c7a520,a384f90b,98dedef0,47afe5ed,6cfbd3d1,e1735c5a,bb8cc0c5,bd9a243a,b43c0e19,2592f00e,a52f820f,b83b7433,b1d96549,d9970293,7da80abd) +,S(b9c1bb26,daac6af7,d0c27c36,7ea2d4c8,c999d7c,75bc58cc,279c5f89,5ab951ce,a4d3bf34,a42b86bb,7c66c5b2,eed5d7f1,b872d17b,b58c938a,767b87eb,1b140f4e) +,S(cf5ac4a0,fc6cd9d8,50aaf473,3efd2bb9,7d9b23ec,bab5d099,9b11bfa0,b127e8fa,44926316,c37f1718,7fa94fe,3a62caf5,ad465d77,fd484287,2caf1605,f91070ec) +,S(aa52b2af,ab8da32e,3b882c57,ff0a34fb,749caaa1,b3c29b74,13c86471,beb3b422,4b562728,4a7be595,1b68df35,e320252f,73326084,16c60244,ac4e3ecc,c0859ce) +,S(610f22e,b3aa08d0,a9f2909c,546896bc,7926b056,7987217a,d347f183,4868211c,a52e190d,f35fc4b3,19571e6,166eda3d,2dbf637b,c27746ea,c1bc3552,4819e6bb) +,S(13f24c3,a485a58c,e0454330,79512796,db65f15a,4265fb44,acc3cc34,c51a6d80,faccbfb6,18e069ff,3cdc4327,cf807366,f07976d,b24fae61,1a44984c,7f3bc3b) +,S(15cebea8,49279b29,cb0112e6,922c5a85,caeb18fb,415cebdf,92285d9d,a0eb0f24,23e5ccfe,158ff9bd,539af7ed,4a77b1c1,c36de452,8d0c0d05,821e89f8,e075d833) +,S(2605a18e,fb7d5367,dc41b1d6,24d61a,cf92e0c8,96f29a5,5f2dc872,4d97f6,8d6a6774,f111b7ec,dc58ae51,80ad6fd2,fd6d674c,9402e2f4,f944bd50,5e66d375) +,S(e588e272,a78756f3,7fa36d6c,d35b362a,25cd0513,3694b56c,33d0b05c,be7f9995,15beb983,20b84296,9b8715c6,ad23e53e,115425c0,d071917,66e56ffd,5472fbb7) +,S(80032522,f264edc5,b6b205cb,f79ca7e,3a02cc39,93458b6c,cb98750d,bd6b46a5,479c92f4,fb4e1eba,98f8b93b,5aa9725e,fd2c1de8,d4c3d043,bca7183b,851a44a8) +,S(950c040e,e1a581b5,70501308,a21287e7,94b525b7,f916d3f3,c166a2fa,1641bfaa,e43db20e,911d70a6,9f171725,f760237a,52441a88,dc385a75,1e4ef1db,b738ea71) +,S(e899ff36,423fee57,2e70aeca,db8bc0f5,a634b9be,282dd875,9db5500a,86c391fa,7ed97b0,dfe6b68e,26710034,f87a911,88611790,cb18a790,a0248c7c,b43a9bd1) +,S(d236688f,c206b7a,8f8ce5c7,66e19fb8,6a7415d2,9c57845e,8332a5cb,d8a1a51b,72f2359f,e3d54a60,fe6d6508,5183b525,d5098887,55219113,adb05c2c,656ec9e9) +,S(8d3d7c71,da4b2f24,83e458c5,85c628f5,430eab5f,ddc2686a,e94e2fa2,119dcccf,33c408e4,7948f20c,bd79c22a,d647ff1e,1f435c03,1f594aad,82778f58,1f61178f) +,S(320f137a,e8918199,51a542ba,5d1063fb,fee4b750,c56168a5,87d7fd9b,b72651a9,b840546a,e2a4e1ef,32275a02,153bf003,8603698,a8c5e232,6795bcf3,d3c402c5) +,S(9e8affce,3fe6b259,f4568607,2d46401a,7546c87f,9a46e0de,8ce577c6,1881352c,d0ea3701,d83b7f7c,260ebaf0,97236525,af8639f6,c53ecf92,73e77bd4,c29d02f1) +,S(5df465b8,1d353987,293c915e,a24121ef,daf70ff4,9ad2bf53,9bec72b3,c61eef64,3206c5ff,5ae4c841,58c193d,40f46083,8b38047c,f33c3e54,38541a7a,d5e690af) +,S(999cd326,a18c350b,f4861d48,826ba3c5,1312bb39,886a064d,6e1465d4,8ef28f58,2cd8ab2a,d6dccdf6,7998e662,99551608,a9fceab0,be884b0d,d418911d,c8b1ef12) +,S(81d8fcee,574d9c16,fa2ba6f4,255460cb,479a6667,4761b4cd,c0ab026b,6bce163b,594e7f34,f5897a6a,3b79a996,f5a69814,3cf771fc,fd7c8ca6,38cb4ac5,98673143) +,S(9e83d724,b9ac73be,f932b42d,56f85183,85839c9e,c953a0dd,4da6d843,d096d278,f8b18cab,fed05acb,89efda95,ac496ec5,d3a000,a4a1d689,a1afa5e2,30c88869) +,S(98ba38b1,3b443c08,aecb1f64,73b2d901,908ec33b,35baa317,64f91144,3e320c3e,343f61d7,fc2232fb,f5d2f972,e6f42f49,d8ece7a,3434a0a4,4df1d602,3f99b84f) +,S(2092ea5a,9adf5eec,5a7a9488,6e970671,5822447b,e6c35285,de704806,52a875ec,ddfb72eb,52382722,a94facc8,e2616661,95a7e9c2,6594228f,304adc7b,ea4e7f35) +,S(f9ee909a,8f45a05e,ac77bce4,ebc97410,c044c639,95a1de76,5b9304c2,2eeb75a,861031ba,93f5b0e8,473e50fd,2aeaf13e,3a39c0d3,7e2e9d51,a41ee4ee,1301705e) +,S(a13878e2,3bc55a7c,b342dfc0,2eea9bf9,5c676e27,c63e8603,c75ce535,35df101c,35c0ced0,3d4bb091,92a2f83,3d3e81a6,233f0585,35c72ed0,1bfff82,7b108427) +,S(e8ec81d2,cd32dfd8,7284cfce,9d80c03,b863a7f8,3a71865d,34f7b1b6,104ae3e4,60be9b77,ee12dd7f,f532b01a,f517bfc9,1d24c4e8,b0865e4e,c6e27227,417648b) +,S(f07cb255,1529dc43,3470b18f,e4e06390,d48e9898,69b9a97a,6642b898,1cf35b60,bd46eb77,6d74633d,59b4b941,2e2355a9,c6e85421,59513d2b,a3297a6,c223b550) +,S(39bce423,dcfda9f0,a97189ae,a60b836,42becd3f,2f2cbdd8,2fe884da,baaf7f51,c1599caa,4b71219e,74490312,7303ec91,f3430be8,14979a2b,2db78bb1,ea2c8e4d) +,S(6da611cb,d51db814,18b3de21,13c9e4ff,58142f67,cf2d8ec8,e0fd105,1e9ad3cf,6bc2f048,3250f654,fdbd494e,bb03480e,f7fe5568,2e19cd70,4d178ff3,41e4b7f) +,S(cedb9c03,8d2e0eb9,8a4a5e8e,fa7e95c5,f4e13a47,79470c70,9a3c9fbb,8b1aed8a,79a2fd31,de42d8bc,820d5d56,ed084f40,46804fa5,f7f5e8fe,eca6ad4d,859a0d40) +,S(7d895498,6d1106d0,32f40458,f6c41fe6,25393f84,d00ded8a,3a07a107,5ae3d82,5d4fb6cf,ff1c594b,61aa117c,7e0ade0e,edd110c2,b2109866,ee7d586a,1a6d0d61) +,S(a4f29162,737043af,eb1e2606,93ecec60,851aaf50,c6e9daa1,75a00c43,d7dc3df7,beebfce8,fb764b15,38fabb20,145aaed3,92265d7b,c4628846,b2726821,c79c4439) +,S(b6389dac,de4ccb08,ec0ce964,e689bbcc,e0ba8165,6680c0a2,9cb162f0,f91cab70,e440c7db,d2d02474,17b2826d,28e31fe7,9ee836a7,eefdacdd,679040ca,50303529) +,S(72ce6dac,f752c36f,978ac2d6,aeef37f5,ae4ee016,999ccb49,27b8aefb,f6fcc9e1,ce6ac297,bbd84403,d60c6d4d,5e01027e,8ad38acd,e796bbc6,7c45f10e,d79bbca6) +,S(efad336f,cfd7425a,560931fa,a6bc50c1,4fb3e5ad,f8ad175,31b13846,6c255f06,3fb784f4,eeee9a11,ec7ab559,71817163,9eab6b09,4419203b,2b422e8f,e15a82c0) +,S(f98d4a2d,4bb2166,7c74bd1f,393ca57,22002c6c,f382b49c,2895deb2,34eaae3,9875e31f,7e3778a8,34750763,c6147cba,97a26f8a,d669e69e,9a36cc45,b56fd148) +,S(768cf74d,2bc24233,2c5eea34,2ac13ed1,2b0d161e,65876eaf,b0b89460,c934183f,42337607,e55b4d7a,f86e9f72,dd06a550,af083b32,e72e6265,4ba00fa7,a2fdc34e) +,S(ac26f3b0,1d05c625,915ec27a,aa916d0b,d34cfb6e,22525c59,9791274d,a5beff7b,e36760ac,89b61681,d1913bcb,1ebeb199,a3005576,b8bde54a,42fcecdf,e94f4045) +,S(dadd4181,741a0be2,e665045e,1b042a3a,fcd9050d,a08d1285,21298f34,1f74fbac,6cf46fe0,da78dd04,261e4e2c,863574e7,f6a4dfbc,a902802b,8056e00c,e4a84f60) +,S(e5651598,ce9e6a86,2fb139ed,143f1b73,2c4e1eea,91a88f82,a6fd0ab9,1a86a3e8,bb421336,69ab6c10,109723d8,ff4d5f6c,770fd8a,d569a21a,9e9efdb2,f1f1ac98) +,S(f26f791e,b4f0f596,39b69b45,401ee3ab,2d47a2b7,dcc3d65,de9e48ae,270f5b02,22e34b25,1fe5bbf2,5183c799,ea45757c,2693c591,633a84fb,e1a9bc0b,470ff699) +,S(a50cf10b,95612f48,7a9da048,a84dd8a3,2d2bec22,9db798ac,19fde68e,623da9da,50107d3c,a76a264,1db9f9f4,7cd60598,ce345ecd,cc3de888,e4c81553,bdcc576) +,S(c74883b9,2736c1b,d5f86a5c,1817cb9b,e917e5ab,5ec5cc10,a1197f1b,a45c13c,e03c43bc,e7393cff,d53e73b,60ea2759,d1c2bd55,c2b7a32e,847f4bd1,2c8beedc) +,S(a3437d0,1741ed88,bb05c00f,629e2c8d,be7cada4,dd002e9c,98dfe800,9e1c0a9,a627e05a,257fcabd,1fda00af,a4d3eef8,fc268e67,bb41c9e,d2160713,847bca1c) +,S(a6cc4988,f179bab4,de98ba89,530686fc,90c00c77,4a3f093,8329ffa3,99c8312d,f4eba473,7db25a79,29bcad7c,11346471,bbba86ef,4f611643,712ff475,9d70b6df) +,S(efb34a8,f5778d57,b3333077,6ca1abe3,9e680183,13936f24,da1aca,70b84dbe,a94a216e,fc53fa98,320878e7,167536ba,7e5845ca,149b2595,2f029878,7f428d3f) +,S(139846d8,1385e6bb,b23914ef,487fb9ab,19ec3b8,3fa45a94,a2c5fbd6,fcdfa07a,4fb7cc4e,599b10cd,a99f6aff,28dfda0b,62d837b2,e8c96aa0,9c34cbc6,acfce099) +,S(660923af,98ba567a,83df6eea,ab53ea62,49c2090a,13299bb7,a538cdab,adf2a101,b28b58ca,b1a29722,699890ee,8462f440,ee2a5686,9715478d,4460f0d8,8c94bd29) +,S(f18092b5,8c7517de,3f389d31,f1a6345a,d2f86bd0,c1dc9a98,d3e03890,f52be3c6,a83e7a04,91484ed7,7dc27697,33b0321c,440ca763,2a36c6d0,7e8a9afd,68f9c906) +,S(e8d0aa0d,1b2c540f,38a6a6e2,5b5fb295,7b700467,648cff74,bfee1138,81d0ab75,a324cbbe,52055c1b,d0cc3f29,88a80982,1abfc0cb,feaa56e9,f19bb8ab,d314409a) +,S(2586a0c0,230a0743,f9613df,51f7463e,1d8ee72d,503025f3,c7d2dfc4,6f526c1a,a29735a1,eb2b470d,43c7b469,4850f5c3,ce9b1da2,47406653,ede03ac0,9816c735) +,S(217cfa88,8d826d46,c31bb2d2,7dd7c8f5,8be68254,e42c0e48,a12cab42,677dffd8,4d5d9c30,9ff0fb15,35f9901,7af91ded,34179c57,f48131a0,6d96b6a,c01ac4ce) +,S(4ea65934,71aa3b8f,9e6791eb,37cfd67,943af3bf,100d7ccb,8203a353,1aedf4c0,feed9ac4,19a302c4,58047403,5c95e997,ded8a534,c88beca5,6c12984,84b7b4c5) +,S(974b828b,51026aa4,ea522150,c497eebc,7d7759f2,c9645f7d,4faac24d,18c7a1ae,c0369da0,c3087336,2566f344,ca81f261,65cd3a03,fcb05093,96d18fed,4faa9d26) +,S(d9815b79,d572d7a5,94dc8ae9,3cf45b36,99bb4544,ed1d5755,529c7096,9c978186,9fcebacb,f5181954,ac4ea24f,508fa67f,5b6c20d8,64ed53ec,d67e358a,e75241fa) +,S(52712597,d9bd4e7,cbd506f1,bd65aac1,478208bd,f9f6ad38,efd2b30e,b8368837,52eca996,ba2cecad,9a0911d1,65368010,68bbf0c1,12d6dd79,aef23262,8b3df18e) +,S(d94231af,d890f5f9,ef70e1fe,e8a8f4e8,ed2fc89e,2b8ad818,6af55de8,449abf6d,26592b6e,f0789218,273475fc,32d865cb,84278ff4,8cd0de27,e0646549,219e2867) +,S(64851231,7a69d956,3b3a99d4,b763eb9a,b661f7b0,714d34bf,e5328a4d,a99a7d90,eb20ba33,515040e3,6fa02088,5a3e9216,1e06d3b3,78122fb4,af4920a3,51d46bc6) +,S(d0a4b11b,ef662f17,98f413a5,fc062567,70df9edf,ee00217,79c3811c,ef68a8c2,d4bb9920,18e16945,c38ef5c9,9ab39eeb,748b7a63,ff4d4dbe,8b329f74,dd70b9cd) +,S(1d89b886,c4b24868,1fbe103d,dcbd76b1,dcf54637,3bd5249b,3dc07f06,985802b6,a339ccca,aacd1057,7e36056b,a3a70c6a,a7885a45,42bcdf30,30d0939f,6369535d) +,S(b6cecdd5,936343e4,f136682f,a38ba0b7,9dbab223,beb0b6c0,10a75cb2,e4dbb256,2935c053,87dd87a2,5bda5d1f,ed813666,5f77d12e,8242dd58,3ba8db03,319f7be8) +,S(84b244e0,a0cad847,35ca6c5f,bf581968,40f1b491,98682c4,28aea50,bf2f8e24,62e28e8c,7df80b11,f5713087,ee3a5d8b,148c8e62,da0de1ba,d8289ac8,861f7a71) +,S(f95eccff,2a908028,39436610,2e377c41,66433159,43feb6d1,638df6c1,cc114d1a,bd0da83f,1a0306cd,b8d1b9ea,189856ee,56d16e81,61b4f9ca,7de38d2b,e558eba2) +,S(7c4cedfd,3e613de4,af2ccd25,59a2dd23,2a1e6335,e410d327,27fef321,d10f55e3,331f2109,d9d00536,c697f73c,672a660d,11d4827d,41306696,cdc224e,e7d7b1d8) +,S(235c2e38,1bf2d829,e4b918de,435e276,f88af866,ed31a1c3,4b9f7203,a0269713,50dcdf0d,2671092e,e7c48614,44eaa87a,815a1304,4600fd0f,f26104ef,a0dbf929) +,S(2a961cd0,9ab50975,517ca488,d1c4a388,cdf2686,c5dde923,76128683,ea07cd73,85c91076,b2ee46d2,3cc0f7fd,e7654723,84f84c2e,edd7552d,d7f93be9,2398abe5) +,S(958bc491,f0afb8cb,8713f591,cbf183f7,e00c4775,93d1b51,a628f40e,4cfd65e3,bf5be339,fa21dfe4,9113e026,af0761a1,b2a09a7a,ae8f6eef,92d50c6a,89a65b3) +,S(6e960ca3,7b5b7abf,99f5a2e2,46b7e953,adb5ccc,6424e73d,dc865bd8,3d5f78fd,53ad4bd1,d5e9643f,5f2df612,ea16fcc8,dcc4b0b2,9ccd5a81,bd659b35,6a5e0e15) +,S(d7ac920e,9272bbad,dd961525,7d91d6a8,e747e4a7,a48ddb5a,8b2ece8f,f0d05871,6381b9fc,c37bac5e,38c0b96d,c098c3,9d4c7620,61b66dc4,adf66e24,f0c87ed8) +,S(9b0803b,1de95706,ec123844,ebe941ae,4481c12f,f2d9c078,b6c1d531,5d730609,160cf3d6,b12afdfc,eac2ed01,fe4b801c,5a5ff0bb,13cc3ef2,f5f0962a,6b3fb1b0) +,S(5d50746e,5b74e006,9dce0153,aba04216,991bd509,f022f3ea,689553bc,a4c5fd88,b0fb205f,d2a14b1b,bdba2e48,429c92b6,99cb336f,b6ed0e58,9276118b,6b259ff8) +,S(e1bc2669,fa27629c,92708748,36008c2d,ef4483ab,c686ecf0,bbd01a85,fcbe04ff,6fb7af71,83c0921,6173fd43,5b3a1b2b,15e8b149,3bed8e78,3d409f84,cd95ecf) +,S(cfc9081c,b5a893db,40c51979,7dfa1cf2,f36ac620,cf7017a2,fe90dccb,d8bfbf78,26a88d23,50772fc3,2f377c15,a9bf817c,db321d1a,55c4d659,b5f29ae6,96a69ac2) +,S(54d9c9d,12bd68c5,3961246d,b98ed78c,cd8ad158,8458d4b0,5fad9df4,2229bb9b,b5e97c31,1dec2259,630c961f,ed33d7d4,f8de7005,849225eb,a4549d76,de1cbff6) +,S(8a914ee6,7e9d6619,d00f1924,8735c7b2,bfb11eaa,61fe42e8,d64b5a53,43057dff,1b91e1da,3374da49,8bc2f018,c2a6fa51,99c51846,a2f59b05,a6311ac3,1bc040c8) +,S(d4b81177,3e641dfa,bc704fba,e9ab0ab8,aa2f4c42,fbd23b03,a0b0595,d0240522,ac30ce3b,1ea4a14f,4a5bd364,20f40df1,cf2e6835,17107b8,ce6fbe05,665cc726) +,S(b810a787,c232357c,bb82c351,79249f64,6f3c8e2e,72e0e004,36226670,14780ba9,54364034,1078dd7f,216bac76,9abc8467,4a422c30,f1b7aef7,6a2dbbb0,158319e2) +,S(d71f88ce,dc738ef7,1ad691ea,c3a7eed0,d29c762d,8ec7ce0d,14e72d3f,f534a833,a47cce83,1aaaadae,a2d9d0d9,7b4c5125,97c1b514,95c3aad9,d2a4e716,cfbcd99) +,S(170cf31d,4cac628e,139d9d89,662ce5e1,1ad7092c,cc6c6174,96c99d71,6d147ffc,4fb3b894,e741f585,15e8baa,addf3119,adf6c4ee,695253a4,6f9a5dec,cc6d77a5) +,S(8e7692a3,b1275e80,38ccbf66,ecb9a55b,bfd48d49,4085b4d3,1729a644,ecc316af,f339c1d3,af07525e,26500665,ce66a91a,4fc0bfa6,b8a46411,34cab540,68c504b1) +,S(5d1ff338,3dda5e81,32c47002,ee2b77ce,a07ca869,e7684d40,a27cbefa,4aca891a,a1adb3ac,b0decbdb,6772e62e,74c4516,fe843833,8d2979cf,5b091801,bf5fbb8b) +,S(d566cfdd,a9dec66b,ddf2605d,a37ad558,375e1c5c,5f5403eb,d75ff6ec,3c2b8af7,13e2232c,78586da9,741598bb,25a90a4c,b6007bd7,bfb75ed8,427ade0e,619c78bd) +,S(cbd708f9,cc713af1,999ae74b,2b9ee3db,3cb85bce,249c5e33,764997d0,2fd96493,f01a1d6c,4ca9b0b8,9337a5af,889d8dd9,c264f4bf,6b8066d6,55cf9267,684cb2cb) +,S(fee1ece4,e8ac73c8,8220bea2,153800f3,e8d049b8,f89053f4,ad4b5ba3,f0ed0fe2,9b5208ea,17c80640,a353354a,7cf42f6a,f9763db0,bdb0954f,53d56a2e,ea26aad6) +,S(c6aafa66,de444853,8d7a6c3a,fcb35a8d,db916f33,14c15dbb,79c48bc9,46ee843f,8a61c6b6,7fc590f7,bc81a761,7a6e24fa,34c49d5b,30b11bfa,7cdaf3bf,d1505b1b) +,S(ac351560,83d91f7e,4898551e,7666bbce,dcbe21ff,f005ef1e,a866781,d9aac415,d709d997,77109afb,28c318fb,eeec8a27,5577410b,8da6d494,5ac7d660,1334483d) +,S(a89c2616,7c024b4,81455d19,93ac04f5,7a212e34,95066ef9,21968f72,3d914e18,551d3662,6127866c,77df092f,5ed8dab1,3569ce68,433de199,ac8219ee,13a3c1ab) +,S(9951831d,b58499ea,e94018d8,cfb9162e,b045b95a,fd246a3a,72e8808d,1ca3f06b,fe39aeec,b9d5f867,d5827146,fb384808,81696ea1,5b0d1188,fd487b16,d9e4d773) +,S(b71e17e0,14564447,955be26c,61ecebc6,8615e2e9,37cd2e8c,78d26922,681fba96,ebcfddf1,d21fb8cb,95e3cb22,4efe9553,1a6c5ba9,cc92bdc4,3011c50b,63b9e3bf) +,S(e170f81e,dbdd9ff6,d460ef6a,1c3d4bcb,2fe89fed,4e6742c2,2d683ffc,15914074,93f110ee,f971cac,8ce206e3,df6f9caf,801fa7eb,c7df14d2,d6f67e72,ab96e67f) +,S(431a5547,318d05c5,5ecf04ed,a1fabbb1,4ac8dd8a,47dccc73,acf5d5c8,1ec104f2,3e3188ad,7b141a36,4afa1f04,57ca827a,7a6f1f70,75ca8dea,6fd08bf2,bfa779e7) +,S(299e6a6e,e1587523,2be26ce4,a1f80cb1,3c193291,7f0b5eec,383ffec2,62203394,5dbb70ed,3ad4a211,bfc8e8bf,4c00a055,23c9b3e4,ed3790c9,3bb2a77e,5301575) +,S(66db488f,f2b58869,b2fb5f78,61e7f825,bc99ac9b,ea02b59f,b0978c9c,c38934ee,ab76e112,b51c83f8,b10e997,2f6866c4,6040e7ef,eb14817f,c316b730,e8ae6a5f) +,S(1259856b,66980d52,bb3912fa,78ad5b70,54c812b5,fed402a1,c3c8247d,26274932,ead7cd3f,c67895c7,edfe0557,d5681bc2,d70dfcbb,870c7c22,45d458c7,f2150383) +,S(502c8614,cbb0f3dc,caa5272f,dd252c7c,4269968c,efebecf9,102745df,fdf1cf00,711bc688,7e4e356,fd9d0695,16af0a38,1b2f4500,6487154c,10b90995,3bc53c3c) +,S(e051193c,323c6403,f69c1073,47b6b7fd,f43acdec,17c00c42,2249bcc7,aa8d6fd,2102c271,ce428ee6,16d1d2bc,f25c2630,c574d9b1,87f8b9f1,c85117e3,8a560455) +,S(c9187bae,d768d32,29fd90c1,5c3b37e7,abe7c58,b538acb8,ae0741,adf1dc46,c130d443,f9ebba4a,197d0252,615ac29d,5714f6dc,f27fc0f8,d7547335,89c92c0f) +,S(5b08567e,b9f3b3ee,2b03ae70,1c6b0641,23dba4d4,4e8297a1,6eaec8d9,e90fe897,ad071c15,9079c91a,cdee9c72,6e8f4cbb,914d4b4b,750fed92,aa81a987,a90c27c9) +,S(ae5b648a,c5817a86,cfb06a26,d62691ab,8a9cc554,d0eaa86f,839f4a51,b43ba7cc,1ea8ab0d,b665c22a,ab0a48c3,da17c629,66e75212,a6fe9983,d5e925d,432db311) +,S(3b9bacd0,a092856d,98eea5c5,ccc9abb4,4f2e0d3,27e578f0,42102148,890178d2,b681e88e,9603ba1c,57b628d7,ea929ae0,b79be447,f181a491,1fdc65df,21bd5052) +,S(524df580,a74066da,d522f827,95e415ab,f6dfbf7a,1c1f6d7e,e8d05a86,4659fef4,3facc728,5f3bc1e1,a6fc210c,ad302ef1,9c72c5f9,e358b9c1,ed2aba72,2652f35f) +,S(2e0637f2,ade775c4,87d09a38,3480b875,599a34ea,7fc70cdf,f6ed309d,450e4363,e3c6eb08,7e2f4e49,cc78e903,e4ad55f3,1aafb7ee,ada72369,6da8a0a0,f764738e) +,S(f7fec6ad,beafbd3b,e413fb33,587b9427,3f785728,226f764b,bda6130b,aa0cc483,f646a974,8eb457c7,fc8fefd8,e63b976b,dd42093a,e59fc544,c66531d1,9e08c68d) +,S(75f94ce0,6af64c43,2b6c95b1,2d892dc8,cd6eb497,4d826299,29ad8bc4,8a3608be,bf8716d0,a2b7906a,69316b32,21ec0076,6e32c892,2c7be6c6,22e8877b,b1958a29) +,S(410296de,5e0df3cb,c4f039f,cc869090,f62e07e9,d3011043,31bdc1cc,f3fb4d85,e963579c,a314dd96,ab8a82ff,86265b82,da74267f,c861f96c,383f72ed,3ffecfa0) +,S(cc824eac,5f5e16b,2f7c1703,d97da67b,69b4e698,16cfbb23,cbdffe00,d73ffa05,17c30da6,9f414ef9,5dc58218,27c7880f,d18c6f1a,92afaaa8,b8cf10a3,353e8abb) +,S(acc1b0ef,4d26ed8c,d78f0b3f,d3177077,d2074b64,fa5d8d03,a83ff113,7e6e5820,6cce44c6,8519ae91,19d2a80,4f8fbeed,4fcd24d0,169e8000,11150c8d,9c3ce6c3) +,S(1cb3076e,ccce1d9b,774bccf,4318b921,583c2b8e,31877825,7c2fc71b,c90da922,2a2f276c,58605d,82a17dfd,3ecdc5a2,a6372df2,cf25bb04,c628748e,4ce373cc) +,S(6efda451,2a22f49b,587ea975,9e5a400,8cb3a521,7a38b978,3d5825aa,84fe13f2,11d44ed5,6dfe2fb3,1ff2a54a,de5a2a5b,60673cfd,bcbd5039,54a94758,878e2424) +,S(47f69619,aef4378b,5cc931ec,da8fd996,e2fcf1cd,17de9cdd,51b30c3b,9dbf0858,149556a1,9776e3d6,da457af2,a4d70d66,d2fa28a,c33abee9,bf890894,7cb5e274) +,S(dd3d8e1e,97f5dd5d,539032c5,13a19e26,8b2eaebc,1a47cd4f,8ff48335,794d28e4,7968bc00,20f6a83a,a973d7c6,382ebf33,c2233bb,34be9c2,e0ff9eaa,4c467e83) +,S(2bd454ee,a19d3a6c,be55737c,d3cb4b32,7de2a2b7,50535f91,97aad2b6,65e16d78,a70b0f0,d6a1278f,34271c66,7ad40feb,9f7c6248,87e3f754,9c1f1fd7,129f7f99) +,S(8cfe75bb,2a7e74b4,4da035b2,9a26784e,915462df,13ac2d91,4733184,f5a5ee0,648ffc56,bdc87c86,4f6226d1,c9245540,b65c2da2,7fdadca1,eeff2e24,6607cd1e) +,S(5eff34ae,19c462cb,c2fdab83,24b57bc8,5e046a68,4f112f6e,665b3255,65724d36,198fb5bb,9db347ff,c2289629,d2e01051,d551e783,47c6a6b0,ccfcde15,a04d68a4) +,S(9aa54554,f3673b08,e36db624,47631774,a2c51bb8,1f18aa61,426639f,78f92b91,8744a3ad,4a6e7404,d2e22faf,272b50bd,1d167b46,5f25c6fa,75c8d784,61384806) +,S(4b837575,8cfb5970,dfc7463c,5df06ebb,ac88a2a8,b9d7565a,955cb53a,5ff259e0,87591d42,bda1a53f,6e474396,a1233df6,ed70766a,ab7aaaa9,9171a5e4,fa8df208) +,S(5d4b74b4,9134f90e,673f5a57,959c7cef,c49dc725,85734428,226cc5ef,76428bb1,1b2a8910,f466ee15,abe6bf3f,100d8da0,cdc2f6bb,f19c3cfc,b1f07a0f,bed89425) +,S(d50be2c0,c55244ce,5d93bafa,f62ee680,2d475441,7ab9c2b2,5702a309,57cad2d8,fe207e4c,6ee89ab4,c0185461,7eaa5de4,5a4dc0f2,bc4544e,f241cd5c,162f583) +,S(bb0f61ce,8e0eb384,64e48915,5bf985aa,f93bbff1,416db2e9,a1da034e,1075bff1,13a715,2ddeb0df,30ea1032,cbcb08ef,4e6b0561,31474412,631f6fbf,b77b7d0e) +,S(3ce6bddb,473f01b2,170e41fb,22fc5efa,63e22b9e,14d9bd22,88632bf8,4ddcb2f5,43086057,f4b3d298,426b7308,110dc52e,9a84268f,1bac1edf,8d47a25a,ace0922e) +,S(ffdfb14a,ef1680bf,c90b5490,c7cdb75e,bbcbbff1,a56c3c85,bd37460b,b38f81aa,f3045def,2f1f19e,232880d0,e948b5a5,24f228c7,3bbcca54,7c216ee1,eaf4af0d) +,S(1ac1f2e1,9ab4264d,8126eb60,a2330aee,c828d102,507ac0fe,d59fa4c0,8223172c,7636a4bc,339486af,b215e3a8,f80d8f02,d8fae063,81b4e7f5,133f1936,d8e18bf8) +,S(52534c0a,740e89a,6142957a,a20f2339,b1a1a6b6,7e6c9a2,2d6661ec,adec228e,cf6fe6ba,fe41c899,c1ec2fa5,d884fafa,e1a31835,ae8022f1,365a97cf,3261b669) +,S(40920285,7cd22c2c,78cdccd6,137d32b1,4f36f2c,964a278d,5b9f6462,651b09eb,40c94f73,a5fa3e3f,66dfef1b,f4f16091,f6b7738e,b8a865fb,3bde12c4,2d031d0d) +,S(8b1d3c33,4b9ccdb2,14aac6e4,e840db16,25a5b3c2,4069d9e7,c81bc51,9b029289,114fd464,2acf9599,1fd65898,57822277,8020769f,145f86e5,630d6db5,5e23e853) +,S(ae14de60,b7c7f13a,59c6bbd2,64162dc,8a6055e6,3d3460fd,7e473979,f4a242bd,8b960489,cdb5a333,a14463e2,376f2113,afccfee7,afa00ef1,6cac6a3e,4c65a412) +,S(f37f1c40,99c95e6c,3e8d4e39,5dfddbaf,b8153948,137de420,788386e,2ed3e3d7,1f290080,82f6ad34,b2283d76,9cf76577,a564411,4f51af5,310c0a3,b3fb3606) +,S(1fb07fae,9636889c,34441c1d,3c2496bc,a13f8933,666d05df,aa53ecb2,1b017a84,128189c9,3931576a,c4b68934,b7ff551b,58e6da46,fa97fcb7,b8df8c9c,637753c0) +,S(44d1eb54,e112eced,480ea2b5,1ba22ba7,2afb0721,d0d6b715,52bd9728,4815f3cc,a6e13b28,574c526f,12673e02,ed7938fa,ea1855b7,d5b6e88e,3c3a9b2a,f2ed7043) +,S(6980972e,5732b875,7fe311cf,f76f7bd0,ec94918a,759a7cbf,d8795645,a103238e,7b9fe832,4ffb9143,21776b24,a92fa09a,ac4a0fce,17c5bc26,2d2eb063,63d26a5e) +,S(6a3a8c9d,405bb1e4,de59639c,4f6406a1,a8566a62,9d85b79b,f7030255,50efcfa3,3e8abae8,7b22d04a,8fdb408e,196b47a4,2e0f59eb,9d51924d,ce8de66,cac038ba) +,S(ff3108d4,1598464a,bf5844c4,dbcfa027,bfb6804c,946c3683,1da390b6,4412ed0b,805565e3,3ee1d089,5c6abe85,77054648,11f5d4d0,219cb515,dc072d47,cbd0a317) +,S(ee4f76a,2f8549ab,3e134e53,335dcb5b,49972750,180491bb,2139324e,95517d73,6bd90167,22402b11,68989e89,9c942767,8307367d,8a29075c,8fe81049,7071a838) +,S(c21175cb,1ca958f,36a3b720,5cbaba0c,230c6305,8cd09966,ac78f51a,4c53e0a4,6a2183a6,7a7ca0af,256a0ecd,2c085d81,585a02eb,2ffe6a44,ea923688,cc72bf51) +,S(2d5e4c08,a9a0b398,34e46e39,e7cab91b,953141c0,c5f2d292,c409cbf3,9a37d6fc,7569b9c5,31a331fa,9234f37,e9005ed0,4b512721,dc4ef054,d6164046,75c6e15e) +,S(6302c041,bfbbeb09,cc012408,631765f1,5d573b67,6b799c26,ef7c2a4c,bb15dbc3,32a8f20f,67ce42a0,ac4ddfed,d0932e91,a1e51488,49e463f,915af082,7e491025) +,S(3e61516c,198de08e,f7dc9cfb,89ef9b94,5b6c67cc,2deab08c,a3b36612,5934e2d,1effbbf7,4d5f7104,f7784944,70f62784,5692f3ed,48e4dbe,e956e063,37520d9) +,S(a008bab2,d7d134d2,4ab35a37,8f330521,b8989219,108b05a7,d8aab0ae,e542fe30,77a377ec,903c06e5,463ec2fd,e9c05e43,f5454aea,82b99de6,be09fe31,51b475a1) +,S(7782ff48,b928962a,950f42b8,2e43017,28711467,16f22f8,c389917e,a624005f,78f23e49,38a03e0c,ad1b766d,b9bafe30,8ad2c9cf,5b22bc18,c78c8898,fb608c15) +,S(233b4a78,c26ab44f,d1019d64,8c896eea,f6ffbe71,8cfefc20,d20676f8,27ddcfc2,de508436,d976df90,487ddc1a,48284cdd,f553d342,2e0977b5,18f3ed9e,773b655c) +,S(8014aa29,6eed493,d83d7aed,2d8c3342,3f7a4623,295f2a07,92c5de0c,71b8bb0e,dc58729d,f2a5bc9b,1170caba,4856a9e1,695f39af,81a1e444,f3e6dd55,f583d92) +,S(8a01e90b,233dae71,1bb050ce,7bc61c8c,60882aea,ebead2c1,b284ab59,dbbbdfce,580ed75d,c9e3693e,92c06cc4,12c95a77,b34b72ef,20a51cf4,ca4ddbad,7691fdb0) +,S(50a2349,24a29158,70e6372d,582ba07f,a569881a,96bd1a4,4cbb9232,1ae733b4,6943c220,a97701d4,a79d8e2f,a446fce3,aa21d00e,2f7cfcde,3edabcda,678cc751) +,S(cbf49357,8c96744f,7ab07bd6,182640ee,b51b8f6d,a8a655ce,cbd2087a,eeaa6d83,4d1b36f4,5711db99,404bdb1c,9484a04d,29e79fa5,87d051d5,e75f07c4,ee8d4bf0) +,S(737b3d75,e4edd296,8d164c2e,828b31d8,7c63d11f,fa4ebd89,ad59ba71,8a8efe38,ec7f7ba1,8aa8e515,4527002e,edc8327a,ff1ebf6d,d3877439,e7c822b5,35f31586) +,S(29cb2864,251939ae,6c7f46d6,9d7b4416,8812e0c5,9aaa8b8b,b7e22ffc,ac38813a,c3ec280a,7c98b2c5,c96d77c8,b410ff1b,b5f1eb51,debd38f0,528bb73a,eca2e6e9) +,S(1858aa59,24282fbd,e44dffa2,5197954f,f3668954,24741d35,2c9594fc,5e28fe9d,a0e891d6,b0a69fab,9fbd7879,a7e4ab4c,be50b1d4,4be76485,96fec281,d2d4bd46) +,S(72ce7c58,1a420eaf,67bae314,d4f092e9,26c1378d,d346c4fc,2a9e9174,862eb75c,2329177a,5a9c3e0c,f6e236ae,30858bbb,8bec121f,f7462ff3,6284bde0,7425ae1c) +,S(34c1ca5e,5dfab6d2,4abdcb83,9ddf9bc,a3f9dcbd,ecc315,17bd6f46,af112903,93f6fead,c931cc97,37c4aeb,6a589a08,80088a24,eb49a1a2,7aca807b,22a1f803) +,S(7c5ac4a,80af5867,f803100c,cee3d676,8d348b47,9ce26243,284ba0dd,24392c36,a4468964,903be647,31fdb23d,1ea7ab84,a3201cfe,ab585b86,3370ea40,66fc5828) +,S(be2ddd13,244c7728,1a98851c,8683ff7d,b57bf3d,a746cb35,e594b7ec,4c363caa,adb75939,af8a2cf,1cf3e5b9,2bc7ba65,8c45ea85,47959f25,7dfe4592,3e580fa2) +,S(6dee1f6a,3b3befdc,a4a44471,d62dfd9f,7f7b222b,819979b7,901a1764,8ec13806,5d9e8501,4fa5f6ff,f8c0356c,eb013122,1a9a2431,f641ab6,af3b9999,5c2e5651) +,S(15f264e6,8e2c8ed7,490355b3,a7b11512,c764386b,f6bafc31,80ee6fe7,9de68c19,90574d26,302bf408,ef9053fd,371cf29b,c41291ab,a76ab74e,ddf3f8c5,70326aba) +,S(3d7cfbe9,4cc890c,ea592b78,5a984c8d,6d4c5e7b,183f3666,df9805e,be9c91a3,d55cddd4,4ea7065a,f8f51694,f6a84b54,42b30b48,e7630d12,6827dfe5,e7f62808) +,S(54388bf9,d7951dc,7ab5dd3e,74971201,1ec637b0,7febee14,45b1dcd9,9c3a1e10,17b36788,25dc2a5f,9bc8738b,3f560d23,725f99c2,a9d6fdcf,6fbeff54,aae5a547) +,S(3c229fdd,9a0080a5,948a0d2c,c9281794,425327c5,52063c94,be5f0197,6a088313,34975795,a84f83b4,af883d12,c373c505,192f0a71,fed21d8d,3a8dbb8d,2a926a1c) +,S(b612bb6e,e14659a6,e1691f36,8fa8d7ad,1974e74e,36f1ab26,15705271,27c63210,5948c5d,d623cdf1,f9477733,dd5e8eaa,5240817,e8e08dc2,23b6072c,a4df5bb4) +,S(ec56e585,54470dd4,28341ed3,e900e33b,ae381e42,2efb3d64,d2edefe0,7b05f613,ecd7f151,acf6e308,dfff2bd6,578bce76,86effe0c,d3b156d9,df8f66cf,295e9c45) +,S(1bef3ca,85ff9f8f,bd80c8ba,f9b15b26,a565eeaf,e4d4f08f,25ee748f,fc11f80d,fe0260bb,96be497b,a194b892,71bc8989,125f0a0a,de88b135,272c5683,f92914f9) +,S(1bdbac4a,522b68e2,a90e4265,3d2bf4ad,c4b27e5,a2ebb251,68a083e9,9107c20a,4d60e326,3ede7fa0,10467b9,d2e8b206,5b09e127,e3fba9e,3d228439,50d63ea1) +,S(e9032f9a,567ab8da,551e0f8b,1b2c2c22,6e601b7f,e894b837,3bd9eaad,3662fc84,ac563e6,47d21af3,a0cb0a49,dafde0f1,21d60994,8dcb4000,64e3196d,74eb70e9) +,S(7ce746ec,44af748,a5508044,f56e3467,a9608deb,c8a051a9,fda907c3,8f79c75,8f0879c1,3166bd23,be7bbb80,46f3c019,2c6b8fa0,52fad7d5,40640fd5,f2aa426a) +,S(e0d52789,19de6d1c,a0929856,46cd3200,68862dcb,ef369fc9,6c6de4c,bb59eecd,98f4679d,b4ebdd90,a4f54791,29740bd5,f46680f1,d3ebfce8,8cda2946,ca886b20) +,S(c6a245cd,cf1f4b58,4400a4d4,82f2088a,e28c8b85,641c1f18,a13aea,7e5ca397,d306dbb9,f8f2cb09,958ff944,ab5e6cd1,cb11eca8,33021bf3,605e6768,961ac628) +,S(ff956c8d,9710f082,a1f34ace,c99987fe,4ade0919,4a0deb06,a1b286e7,3695c910,fabec52b,866afd46,1fe8c832,fd8a9375,a8086aec,b0e2e39f,b7823d0c,13bcc306) +,S(549dda4a,d1a604c,d0f44fe4,5c4dc276,5cef1216,6f4accc7,3ff12152,26c23af0,71e3afc0,e05ba826,36eede7,1abb15e1,166fec97,562c4381,a33f765e,100631ef) +,S(66d3df09,21573cc,e69896e8,2dd91f9d,6070dd12,edc21723,2077e644,9e776416,93981613,59a2ee3e,d9429dd3,eaf058bc,9ccf1c9,ea931ebc,25ff279d,5bb46d5d) +,S(61bd3dbc,9426386,9c53ede5,a5c4f186,8a4d7a82,fae1be7,37b702f5,79e4f8d9,8c056a47,e27d4f49,2c9ddf54,efa48c,502aee81,e2336caa,c1b572b4,e082e780) +,S(8a6d8b46,4a31daa9,2702f784,9148db1c,4ef20c76,fa67c006,47d6f3b9,80178f4e,d1f406ee,e4087601,96c82f9e,38205147,c31309a1,93bf4d8c,cfe67cba,ae8f7407) +,S(e2b60e93,99eb8441,700aa90e,1edd981d,37893b42,5acf6b1b,809fd1e4,922e573e,68a0c65f,e394829a,8ae09dc0,ab35d789,bd8a38ad,800e9ed,63513eea,4e19f7af) +,S(4c76030f,e85cfc92,64f16cad,99dfa541,fc23b8c7,2b558574,ac6f26cd,d4f55e85,44b352d8,9a31b4ec,c8f9a574,51855f7d,e98c63aa,98aec090,2d959be8,a12b7bb6) +,S(dcba96a3,59103c35,aa2c9ef,538c13a0,2f0e8997,3663d580,fe454762,5fbaed2e,af9e205f,727698a,de9f91bd,75bfb73c,b13053d2,db2d83de,b4d14c22,176544b4) +,S(fd0497d8,f75fff9a,cbfd3e03,6c9edc73,e682427f,e9b66a43,3b5b2b31,f2dc7883,d23014b6,9c8d1077,ab3fb2fc,c0bde4a9,44576289,896de135,9c0badf,fcd62d79) +,S(5bb401bb,b38e413,43d1b779,54e83f9e,e705009a,eb8a1077,542cc6ed,edf48040,431fccb5,acec1391,f68ea628,378e3ca8,ca944573,ecfa5e,e7a82bf8,82599c14) +,S(1fa96a4f,f9cc6ad8,b2741c38,b35c377e,3acac704,67bc5d20,a262db5b,48bb283b,c3448c92,ffc65afc,f1eaf7f1,f9e149eb,6404dcc7,1b222ac8,bfca8feb,eeb963eb) +,S(9534aef,1ae551d2,7a638453,171d0a63,697ea420,2fec8f11,61432291,642f86ea,cd0c943c,38837dd0,33d48a4e,ca13e96f,46fd7f21,2cbff849,6432aac0,ef729591) +,S(ea72afed,43300390,bc98d08f,3aa4ff4a,138440bf,1a8f8b7,b57032a0,d01b8ceb,e174dd8c,3692cc2,9ca856a4,4eae7e94,af4fb044,85172809,25ea66d2,d858663e) +,S(39593534,3ca2523a,603801f2,abf3a1d3,16569862,35d79fa9,a01f86d4,5c122327,acd3e3e9,2cd76dc8,551cbf0e,94475125,42a5b887,ac6c975b,45d2e000,9953dd96) +,S(d57b2a2a,375e5a0f,6967d4b5,3dfbb51b,4f74eff8,72742a3f,246e3e09,ec2d4721,12d6f848,4d107ec3,10ec2091,3482781d,c0aeeb94,71ae7b03,6a0d2551,d3ed306) +,S(9f2dfc5,1b99eef6,e6c781a,fe0135d5,fc5def8f,d520119c,59bbaaf6,442f1082,8f20962,fd273048,fa3ca6e4,ea96fe0d,7ad1d758,99e25a77,e0f8b56e,5b20dd0d) +,S(b1c30bc6,f030c913,49b36c1f,9c0deda1,afa58776,58c5a16a,9352c1bd,229884b,f8a459d1,6e77888d,cd5c63d1,8a2e28cb,5563e727,f53b59f1,939390c6,1eecdfc3) +,S(8a663e7,40cad237,12dad6f9,1bb5d266,30542933,853e02eb,665e198d,a5c6e53b,e206b399,51175d15,daccf711,63b0918c,786a8fe,2077812,c0c1eee6,bb5c1e99) +,S(2b0956cb,e0ca91ee,f44fac0a,76cc0b00,5f4a5ae8,27e63696,fe1aa8b4,a92941a1,a31e6fa8,dd454eaf,90ee1e11,62627e3,3f84b8fe,da29f423,ddf2a962,386cffd2) +#endif +#if WINDOW_G > 13 +,S(e0144151,7a2ac970,eb6381df,7e11fb4c,6b009980,6a0d330,d5429126,e662c3bd,8238ce9b,a691c80c,21563934,18d18dbf,aeb3fd34,48863a1e,7f4ba360,408dfcee) +,S(be3213e,8b062f33,caf16c53,5a0b666,f344e0d7,1e28aeba,8b215a3,7ec86c37,552523ba,eaca38a4,cd795683,852a2643,550fb83d,d1db0adc,3f1b29c9,7d51cd1f) +,S(623393b6,bfbfbd64,fc7aa1db,9e58e274,1c18eb6d,b5eb30ab,c4fe167e,a9e8ff2b,7c0e4174,f0fd5bf2,a025e316,fa3b7b97,1339a197,b52b0b50,bad4dfe,34e42cd3) +,S(dadeb702,dd0a1e87,669eb624,b8cf40bd,bffe79aa,8afab26f,bc6215b1,dcfe97a,930890ca,e4c29b41,2d660acb,8cd39bca,5a522599,a1ab0bb4,8fbc38df,17253490) +,S(b528d3b7,83179b82,bb1c9cfe,c26184cd,ac98ad5,8e253c13,627fb778,c9362f33,63203af,1545122a,63940676,ba2d18a1,aa902e08,8bb444f0,47c2bd52,db8babab) +,S(2ff223e0,93986e22,ae418a5c,804e9e81,7a4de562,d7436e0,2d025ab7,11734a6a,c5c8d188,ec9cda19,bda76d8a,97838ab7,81990475,d4a00096,5efa79c0,100f678) +,S(cb8731d5,e1e53c9,ccfd2c39,3c8ab1cd,9ec839f6,99fc712f,f946e74b,99eb1742,cd2f9df8,38fec504,dd125248,92ce8a4,a9db3848,e487076d,8c18e53c,f6d19fb5) +,S(1377ac45,d776a5cb,76ec265a,af272690,6dac52dd,be987799,36bb1271,e0cf4a2e,a4b3330d,d5954932,d5a7a818,7d9493e0,25ab2155,d4ed0d4,f34aca1e,c2a04670) +,S(984b9bf2,f140c75f,e03489bd,88041faa,22fbac73,7ac10724,c511a253,641a5ccd,c474f639,9733eecb,a750ea82,ac4d50c7,d82f4c9a,942a2bb5,90b1431f,c8d14ff3) +,S(4b8fd000,74f57cf9,b135d56a,c002bf99,d5050479,7298cc4d,857960d7,afbdcb06,7c044103,1df0f6f7,721937a4,c60cf76c,95560ee9,4c6d097e,3a8e37b3,34c703bf) +,S(57316653,68412236,619250b2,58bd0ddb,34fc751b,8becc0b6,60585683,218b6079,d1d56e4f,30f01c57,48972898,b8afe142,16f8335b,e3a39c37,da64050a,91652904) +,S(c9a00197,a0ff16f2,fbf2cb59,767d43f4,60737367,4f2bbbd2,53c7220f,db69f286,177c6783,d739117c,1de074b0,c605cabc,6b4dfeed,a531f4e4,9cc518,40142f0b) +,S(e23416da,91df23a9,db161618,579b3008,c87b9ef5,7ee8bff6,45eec6ce,a61881cb,e556417f,84043691,e7ec82e0,e1718296,e42fbcf4,a3de0137,93772dd5,70b8da15) +,S(9f96e3f2,369d753c,e6614c45,77bd8a9,14293eee,8a749c1e,3b0e2955,638e9ad8,f5cd1268,1567a4da,71650d10,e413e0eb,9b01a396,e529cff6,b494b2b9,a21ddff1) +,S(48657ba4,b24b0123,cd54317d,de6a3245,ae488078,5ba43d5f,1d94fbf6,b9301fd2,191edec0,6eb53781,2813ca77,2a9021b0,dd4931a0,929a7e36,a4a8c66f,aef0d890) +,S(67b322e9,d72e864d,8cb15511,11402d7a,c6c858c8,77832d4,e0187e69,6a36f24a,cfeb12c2,6b58bf15,3bf87058,c5faa833,e15c867,f7f9a208,c3dfdf3a,c7f6946a) +,S(42e8068,542bdd98,fcce8925,52002db,c545ba13,16082d40,fbde06f0,a44ded15,1645a9b0,c1f33cf5,27364915,8c4f261d,a1ee814e,7aac6402,bf644451,bfc4aae5) +,S(8ee7dce1,339c50dd,55619107,9bbac005,3e058cca,4a8c8e30,7588c105,fe90d4c7,ffbd0af4,8aae8279,2cda6170,5dbe9fe0,98e102ff,1e6f11d2,b4669a81,6ec9d5f9) +,S(abc9504,12926a71,6adaaaf2,7b209216,37a935f0,cc66eece,55978a82,1fa368e6,72e54935,e9b461e7,9fb75c48,4dae4fd2,155a4a48,d3a467c9,624c5f6d,71f79203) +,S(bd250320,81e0e9b5,3b00568d,40378fcc,fd72fd8d,783f2f3b,433ddd1b,a6976ba8,5adb8bdb,85855498,994ca879,717b8e7a,e8c4c87d,29d6a1ca,4c3c6395,6c3ceb39) +,S(b4fb262,fbeee84e,49ae9167,7e7aa0d9,b31609e0,48888740,c018a5e1,8ae25f71,34df3f86,dc091d51,258ecbe4,1cf616e9,265d6d36,8ea3403b,24cdc7f8,e176e5fc) +,S(d1ba9b67,66f24eaf,a145ad14,81063b5f,e4604817,21b361c4,95de8833,be605c01,e80a6cbd,17de2f1e,663ce7f2,8917e3a9,f871943c,a4b8ba75,68476a3,d9e8400a) +,S(5c98b6b0,3a3e5a8d,2d46652d,5eb9e2ab,a80c6702,69dc5ba8,3732752e,922639b3,81e2f9a,145d0cdd,2c51bd13,f907a04,6a35efb6,635292e0,6b8f6513,c50f4a54) +,S(c1fa4688,f75fb86f,61cb7071,4fb25f2e,908abf0e,a87aa84,c8db5dd,4c2618f8,f2f74a77,6d4bf26d,17f55d0b,1aa913cb,9db869e5,ab861d9b,2043ccb9,ca8397ea) +,S(5e806e9,a644eb32,51c62ba2,f09a755b,96aec78b,4405df76,dc22ba6c,f7227ec1,b88c8118,d6a1829d,6aecaa05,742313a,b871d2eb,72f31bba,9d84f87f,a73e614b) +,S(f9d147fe,fd2c1a7b,f623d965,bc7901bd,968636d,eff54c14,34c6326e,d1c8de9a,c7887249,ab6686c7,93a7d77b,41173060,40de44cb,84980c33,cd548d72,d29caffd) +,S(b98cc956,85f6d11f,a8d38a1f,50799634,9f7020ae,3171023b,abea0fec,a70ccdb1,f074d495,3689e8a3,6ee4cc0d,40882355,3dcbdf21,70975f49,5712adbc,990ecf7f) +,S(f161cad4,100817e7,ef9091dc,714bcba6,18458493,f3bb6b03,4648a649,6817b474,b7eeb92e,396af324,22881eb2,45e62515,c4410af9,fa519ffd,61289f6d,9b740904) +,S(7c8b6c89,2ea4061e,456abac2,46edc08b,fb54aff6,8a2678fb,1b7b1c08,5326914c,f08e1f02,6ea1d2c2,9ff5eacd,2b34682,2960723d,cdab95b1,d69c8af,5e7692f8) +,S(4668c29f,16334658,195b93cc,ef71a076,3ab2078f,724dde5d,d3635eed,24f28729,5db228c9,8391ec52,324c723,39d99514,8fd22f7c,2f61cfa1,29ee7400,4c74ea4f) +,S(3443d0f3,9dd6da5c,19a39f8c,f3672652,96fa6729,c9290b2f,4e21c7e5,3167b65e,4b6db639,9a088ddc,8ed31f63,7c079451,14a4320b,b7ed94b9,2a87c0e8,97429234) +,S(d3ce3501,17a04e95,58281e69,3574c096,458dbca9,afac20e8,ffec7810,8bc227b4,bcfc77b9,628f6ee9,3541e47f,8d556ad0,7696ee6e,90ce384a,9b77fcab,6eb9908) +,S(c4dc6363,ad772452,221f8640,9de0ac28,f2bc1ac3,cd2d7080,23301560,f95127af,78c9ba8f,d789a666,439bc949,38345250,38a79c5,97f6a71b,3e096b8b,752d3038) +,S(c771f94,221a47ec,fcd78d7a,123d2e0e,1bf4edcc,bbcdd075,b23d29da,6d5ac666,4dee3b30,829f5aae,f4471924,65de293f,f2ec526f,5b8f5980,d9155f71,ce8d6091) +,S(bff88ce8,96338f7f,5b6516e6,448a1f25,36a1e9bf,275afd50,e772517d,79e6e199,8f3f0c7a,b62010a4,eb0b6211,7e9761d9,c1552629,bb55bf68,53274158,a788c206) +,S(4afcd064,5d57bf24,e850e7e3,d3cac224,6a467e76,f2ce02da,576a1f2a,9a3b97a,52f23b54,87b2af93,6cc692f6,1876d6c0,ef927d3b,c06df93,984ae54e,4d10f0a2) +,S(c4bc122c,b9bf0e20,57f933fd,94db012f,79e23c34,98007abd,f8a8ba21,32b1f373,e40c47b6,a31d81ba,dc45d427,3ab56d77,1daf76bd,7bdf8ce1,a8778861,66fbfc6d) +,S(487a19b1,d1be7791,ce223d27,bab11aca,837a0f8a,7d34d9dd,b4c68f18,51f821a8,a53cd743,16ff4e9a,8633554,45415e35,5552403,7b25ce67,f519e44a,a5d0ef98) +,S(274a3cec,be06c748,7603516,588ccffd,f4ac3d80,a85887f8,10632cb8,4301a326,17a24be3,afbe8b45,9a72cab,5385cb6f,5d06b47a,63bf19b3,ff4989c4,85a9f40d) +,S(32b31f77,b0cb99ac,3bfc34e,7558f9d3,e82fcc6a,be2c4,8a079106,33afa9e8,447b44c,c191da90,e0eb3f71,db804472,17f422d5,b1732a8,993da80a,5d8f5bc2) +,S(78c39dcf,55561468,dc339d46,ffd09337,945285e1,66dfc29,25d8b85a,24a2752,d691ddbb,4e41ca85,eebdf63d,5c023e62,a6680931,5e1109ce,89088467,7ba3ac55) +,S(5fc75911,a4920777,c4502116,deefc1a8,d2f21f96,2b1e725f,a04d6fb3,9d34e5ff,59f36f7f,18f5885c,dff524d9,6a139749,f3fe1662,4b728d50,7a460bb3,497512f2) +,S(ca1590e9,152b2b49,a6d0fc16,4e346e19,fa62dde6,f693b64a,e38289d3,ad08fa8a,46a7bb3c,1174ed75,49e9eae1,7126455d,535ea744,936947f5,24a8ff30,8046dc8c) +,S(5c715266,5ff0bdeb,a7f6e660,bf66d5ad,743d94bc,5ba9b7f1,f3d50cdd,9d11fa1f,7da23b15,56e995c3,a21ee9dd,ccccca4e,e41684a4,f208c39,b31ffe9c,4473a03e) +,S(c7f71da3,6361c73a,55dc3877,4f2e1033,fa0c09ce,1c9557c8,c29d8684,e4177e82,8d6e47f,b5c2cecb,6b22dbf9,a04f0f95,77c6f50c,3429df92,78edb85,59b0dedf) +,S(6e91bd9f,5264a13e,37925900,433b7536,4130b4cc,e6fa716d,a0101c2b,dc58afab,5baa7455,506b5849,5d820c20,f209aa01,ee0f9434,1df5b449,9ed23a51,b4fd604f) +,S(1f00de8e,69ac7cbf,c75c789d,f3322813,244907a0,35059072,68954196,baef0d33,2892ce90,f32330ed,2800c30b,4482b67a,e65fec8,41ba6fea,b2e83703,dae040f4) +,S(c00133f5,7d8c469,421efa3a,d71d8d58,1a2f5b0b,2324de10,91d79145,7d300638,3c76b182,d3ee982,dd05ea45,59e8e31b,1aa3dc12,fdcb67a2,f86434ed,34f18895) +,S(af639e7,95e8269e,12cef756,4bbf970,cdb172e0,66cde48d,2f8e9083,c60cad49,1bd01179,ce92cd29,e414a74,cd1de487,c479daf,5673b219,8082c474,2426abb4) +,S(e6dbd27d,5447c044,18fa0a5d,8ebcb9a0,ede58538,8b73ccd5,4e979797,31f4c9a1,77f38524,f2848320,3d4c780a,d0cf8bd,a2071037,348db8ee,6b5deabe,3d242153) +,S(f51a8d23,c95b70aa,d02dd904,ec1119ea,8d1e3e1e,e405646e,4c2dfd53,336360a9,426b2c9a,1d703cf6,c748871,4959acda,ae5c6aab,51945138,73e4b1a9,c0f46c57) +,S(e12f11e7,fe437b7,f578d8fc,33ae3a60,a3e4d1ac,1ee45863,42944d50,a0f6bcf4,22d15d58,35fa89ab,563985b1,4114c957,8bfec8d6,6dc7d253,e3c6d900,4419f5d2) +,S(e758860a,8ec762d0,62f51e56,d958a14f,ac619351,9d890bb9,bf6bc7ed,e988e14c,42d9e0ef,10b84f21,fb88d780,dcb4750f,10438682,3b003b23,ec41c297,567ecd78) +,S(3c933fe9,ba21e5c,d822b90c,78a54b7a,d4799882,ef91f2c6,e423a398,20a64163,c1eaa0be,531922e2,ca5b23a5,6700b4ad,7fc16135,11f67f96,b672ddf3,669080df) +,S(4757576a,83c9066c,b6eb6b08,e91a79c0,df828e4e,3c09ce07,939134fd,ea857872,13f22fb2,62d77f15,de3e8388,d9270c45,818248e8,9d950d7b,58ee6c6,c38277a) +,S(35e63903,70085140,b19df903,9a7fff42,46570fcd,8661caa8,129d6b4,31045c7,2855f4bc,aa9d60d8,6a44f022,b3c28e84,53cfb3ad,a58691f1,b0e9cae5,e5b9e8c9) +,S(ad446e01,1f118207,3565f031,6d0a85f6,ad5330dc,6a8be4fe,82be632e,32126fa3,240382f1,decee3f5,1f92b2f8,1ac0eab2,1d8acb54,2f730d43,15c194f6,28e83e39) +,S(fbe9e5f3,eb01ac37,820af752,9c4221a6,89164ef5,d77461af,f82d6400,6a718815,f2f30cad,d85a2e0c,e393e088,44de9215,ba207384,2abf8468,47801ff6,12d088e6) +,S(1e675320,d81ff430,a0549aa2,482cc9b8,b9b585f6,32d32916,6e952f13,3b038135,ca0f1276,c9c7d995,c5baa5de,2988700c,2e2c7f1b,366d1db1,b44b4c9a,f0686aa7) +,S(176dc770,23b0e52b,a2eb8068,ad27924b,94c3f643,30e0e85f,3593917c,d6267718,d2dc6288,5df75b06,f450686f,df660950,548d4f6,85d1a701,d510f3fe,297ca8d9) +,S(3c8536c2,32a02b3a,b36feadb,ccc7c541,7ac83942,3cfc8dd,99a0ab7b,41a9c938,a684eea5,249cd12a,d24dd8f1,73022ad1,8ebc7f52,d6a3bd9d,bdc4f5ad,a25186b5) +,S(48aa73ab,c61a76b6,e54c446f,ba881198,ec6ea003,d63c54a5,605a322d,7af006fd,c28c5eee,ffb20882,2d018506,5cf3ddf3,5977fab7,40e4d4e,cdbf9935,8e403a64) +,S(74732762,abd028d,9dad2b36,38baca49,263d170d,ca7e4cb,d83a3a1b,53cc1ff1,ca11df0c,e8dfa09f,3f2ef7ce,5c023b6c,927a88fe,60ba9f3f,ddfbd69d,e536c2c6) +,S(eb71ab09,520e5915,767dc918,814fe901,a4042725,5247c02e,b8672c5b,95698522,5fad7ae0,a7e95b38,ed6563cb,2023b2a9,c81b6901,c007f1eb,8749f56e,4d48e995) +,S(2b101bf6,c7763df5,72eda74b,eb179cc6,af5e8695,965761ce,f8444371,7ef5be6b,dd16c8,4e1d1a3d,98d4629f,ef258421,f6664719,7f89a597,67fe862,3f140ce9) +,S(a099efa8,2c666552,5332b050,75e81ba2,13976afe,8f3b33eb,ca2a4436,6e0ab337,f883957b,5cf1f2b3,ddd6c7fa,a126acca,fb1e33b5,e5b753ab,dc0a99bd,5d136c07) +,S(f4396856,a61c3ce9,5b72ab6d,470bdeb1,8cc107cd,2f58f15,3d5bc99f,8b62f95d,6e3ead1a,a8edb0b0,67240ca,f3274e63,df81c2f3,e1ef2567,cfd76c01,6e9f6133) +,S(da732197,26aabb18,4a0aabe5,a493d803,53215b7c,49458008,bd5e387f,694050c0,e2075f29,17724b68,6bcb987d,85e954e1,9489af4c,e7ce1b46,66d84604,e9a0eeb6) +,S(99171a1e,19596183,f4259c09,b9740201,8fea44d1,4d0f2f45,75f4d66f,980a6792,522b840c,83e1eef3,e7f2cbd8,e07bec0,66af1bd2,e7607f5f,27a767b1,75219a4f) +,S(353cc684,8a2c4f36,2408997a,944889c0,1db7bcf6,9133323f,8f0d34ca,758cf08f,1865bc62,f4ba0156,7e2b283f,702834c8,97effd14,6e0e5fa2,43d5124d,ce67ebe7) +,S(8b16983b,5053d3d3,8e67c77f,1b6fccc1,b9ed7e3d,d57a26d5,deb242dd,7a312852,49d93f92,fa5953d3,956adeed,cff49ac8,caeb3d2,c72a0e9f,be6c85e5,d85d47c6) +,S(68f5dea4,b1da327c,227b2513,83fb622,efb36730,3bac02ef,505e0464,750cabc9,26740f9c,5d624b9d,aed25774,46c9c742,146de045,71cb750c,4356898f,e3d31782) +,S(faf5207f,c3acf00c,8e19d8bd,7208a6d7,cc936706,98559244,e74bb202,20c699b,fab7b446,17a6c6e5,4ac186ac,971de8e7,d42c1527,8607eeb,b8564a0b,cef1936c) +,S(c3d1aa91,b541c5bd,3da04707,a2f01a16,917c0420,a26604a2,bcaf5081,e8d17630,5b57806e,5a58c405,f06d7ce5,4e5f7b45,c9b631a3,44c92c64,d8cd1435,fcd9ce27) +,S(23278a35,4970b920,450db526,391b30f2,54b5bd5a,fe2c1455,623c99fa,8ac8db1a,864d81da,e8df9e47,1ac93b74,660542df,3570486b,fbd2144d,55dee441,90e1888b) +,S(602f6181,33cbca4d,74de4ab,29dcd101,7c12c32b,ecaed25,485765c5,478c0146,4c42cfb7,279ec350,f77d2da1,599b9c85,9ffab342,ce6a011f,91c9e460,27aa968) +,S(1dc4cfc7,126c3b2,5d82aa46,3dcc8cff,c545a9ab,c7962690,fd4ecaad,25843ee9,a9357982,5c535575,35820f17,25e32afb,6002b1b9,2a9b43ac,18225e61,b3fedeb1) +,S(3694f121,6e02bc79,14cd9910,8ca65fc5,887a95df,8b04e8b3,3c6c23cf,ce35af82,7ffee291,b1c41976,d456e742,88458b9e,c61e57c9,9b8386d1,d1c36424,38555d88) +,S(743ada75,a9596420,7b8725e0,af06c2c4,457be03b,a920c3b4,b65922b2,1df1bdbf,8f2b81b5,5ac4c5d8,9cc23ed,92dc1b43,5629b9f9,39b126c0,b16d7175,b40f908f) +,S(bfe9397,7c7cc4a7,b9cc42d7,f044607c,f047cf2e,26d6f94f,210af7d6,b2420085,625d6059,8f80dc83,1ef2b51,dee53d5,666c3dfe,a02e1d92,1260906f,ae373d64) +,S(86c02c0e,6ee646c8,c2944751,7867a1ad,cb56ebff,c8795f83,73e6d8b9,1b240eb9,659b5e9f,da4b1098,b76af529,f56f809,59d39f48,916477b4,bfa66af,c85be4ed) +,S(3b281045,857f01e9,995d357d,3a84dc66,6c4f8f47,4b82ace5,dc3a9229,387bfda4,9948272c,cdeb1542,47c7c42f,1f313dad,e318a123,3f500f8a,16d80e7f,d2e9fd4c) +,S(cddccdd3,b54d979f,504d33d,46d6ff2a,9859038e,33087a3,da9a5284,fefc47c0,9f2a13f8,369313c1,44d91e5e,807ddd65,196c991,a7eb0321,2133aeb4,56683b8) +,S(98100502,a6023ddb,ee34462b,d0232711,8f8c2e9f,33708088,40352c34,870e16d7,1a8bfef4,69af3b3b,1e381c08,454a84ec,4cf8fb66,bd721a57,47a94e90,1969b9cb) +,S(8b76757d,b2fc179b,9016847,9b82124b,c26092f9,b35de316,c90223e5,99978179,47eaf326,588e65ca,541a71a6,a1167321,ee449268,1e8b4960,6eaf9a73,42b16f05) +,S(e66ed7dc,897fb660,6f44d8a9,46b0c61c,45c83bb2,d55ea771,bf2571d6,7ecc3b43,e536f79b,8a9fff4e,c6cffd47,8a405bd,7f1745f9,9374d810,518cc5a9,f214dcbe) +,S(e0600592,cc9c0b9f,8cead065,6ef5c115,ead63209,6a0a0d2,2626aa13,f2bc88c8,628d94b7,8b84705d,16e55254,4c138416,e7f75f5d,d549ff7c,a42d51cb,7f5a584f) +,S(1b0af242,a9cf25d4,cc758fc7,63e2f07,62db2e42,6b392cfb,ffc5ac07,19152767,33ae8aa5,eec30559,a3d28da0,d9354048,592dec4b,2f975a0,ed043fc3,c856a2be) +,S(e204ffaa,142f7eda,3505beee,db0bcbf0,9b80924d,ce5bced5,439aa4d6,af5acb7d,a2804d9b,98e11519,adeacdd1,aef2c22b,a02f7030,479a559e,6924db49,5d8911ba) +,S(afc40e67,651531f6,214ea1fc,13173d4c,a35431f4,92845964,69cf099a,d515ed5,57ef6ef4,1e480de9,32ae8596,3b04d267,5e629707,cd3bdb45,5896d333,b648e0) +,S(59538bdb,747b641f,800588b0,45c0e3c9,72963406,5ca601d4,690dee22,abe8d226,a030486a,3c919c35,4c26547a,3de96087,52ca7a28,5d378e28,652031dc,ec2d4438) +,S(6a91d34f,c38d4028,c63a4bae,bedb2a76,b964cf44,1804a6f1,efbe469a,a997ed8f,32500c83,6a0b5274,2d2ead10,dd3e73eb,45f221d1,ca6cea00,f2b01d75,d9073260) +,S(9260e687,b11d97ee,97dc44ab,5e21c344,332ae39d,c85c2ee8,f7760cc2,8ac28d61,f47df3d,e8d8654e,f7124f5e,2dcd9d95,c1d13e83,a33e5d18,92ce9851,6223e778) +,S(3490e733,7a66f301,f678561f,4b4f293c,b48820c3,dcf9be06,a0390410,4cfe3cba,49591a79,7edb70ac,74f87636,85bbb22a,a8169536,532886f1,f5ea0721,5e9bbd6c) +,S(9729f4fe,1f47a9a3,7dcb1311,ea9548a1,d71d7a81,27803c8a,6ec0a680,8d07b4a0,56e1a5d4,ccc4fd72,8e7ee569,e22efea5,c8f03d73,fe8ab86c,2dcb8af2,eb17f896) +,S(af63904f,6da423d,1cfd77a6,680044f0,5e694eb5,80ccb4b1,c37d8d83,ae9c2caf,6decdc2f,5fe2b948,d15fa10b,4a7e2edd,b8cfaf0f,7c79a400,f3d1b306,dbe110b2) +,S(96271f72,1f4a1f21,a2274edf,87c294fd,403cf0e0,c7b7b1f2,f242fb7a,2c435a3f,66ffbc73,1fc8cfd5,2bfbbca4,49f6e949,71940077,ae411e79,51cf5b21,ca5adddf) +,S(a42ef215,1f0cc5a0,15827287,eba8d960,dfa5a13c,7ee2696a,95bd94ca,4b717c7d,5e491ed4,5f08a591,32296929,814608ee,e3243172,b523a1b,a4995f6b,50046fec) +,S(193b3ded,e770a757,d859c289,68be2780,fc4ce92b,135bec2f,7fb2c07b,b862e5e4,192c4bba,ffbb735e,a1d5cd1,38de0556,d1d1d740,980c5775,22d19b5e,f0716700) +,S(ccf9a075,da6f44e4,cf3b4fc8,26feef3e,54084b0,e2756029,e290a7ce,8c1e8dc7,d04f844d,ccf2c30b,9020f0a2,308219c6,a5d6491,ffe9ba67,bf6b20c9,9a35a183) +,S(7ebea25b,1d69bb43,f60ec69e,f9853208,d3ddffb9,6dce3831,873e7ace,d25b0140,24540af2,bf87d637,54f1a47d,13a3f38,2a948375,8955dd16,fc83e7ca,4fec3d65) +,S(c9eb4a,c2a31b01,9b7ff038,38478b6,674662eb,d729ae45,d7111c63,84a038c7,7e2ee03d,caed3f4e,bef2b90d,78ae42a3,12f6e8b3,29d116a6,e26bdded,81f32240) +,S(45756e0c,8c84e9e,a7b23bc5,cfd87b4a,cd0b290d,86c547b8,7fed946f,e5acd867,9c3f522c,e577c017,3db984c3,d7dc16d0,31326e2c,85b08413,4611da0a,70acdd1f) +,S(1bd58481,21bed636,c789e341,665e2477,59438de7,10aba25c,e4a0db7f,13e3c0da,cb0eb897,bf905399,63a8a7b7,50787430,ffd4d6d7,9589c8e7,350f4351,4786a90b) +,S(f199562c,8e809ba5,84aa80a9,759dd98c,32eda7e0,ccfa4063,547ced9,d9324dc1,33519ead,76430498,949dbcea,33ca14de,2f7ea811,cdd38f32,4009ac7a,b408d1d0) +,S(7fb4e910,e9e37dc1,f56ac3b9,30589522,975319e9,84dcb559,6743d3d4,f45ea6f3,b74a03c,cdbec9c,f36b59bc,fe3203dc,5f4d94a7,bd54c628,55f0d04e,67c960ee) +,S(503d08fd,19839d78,aca27020,54dc4233,c1a1d68a,6b9cf136,4c6fa9fe,886c03e6,84ad95db,9a09eb87,bd905914,473d23de,698459d5,7c436f77,58937bd1,8e448e0a) +,S(335c9204,e6aed3e2,586d0dea,c87158a5,62db42c8,e42fac25,9196083,51e1e713,15b359bb,10f8bee2,940849d4,212fdd84,49b60c5d,d3419ce6,7ef9f0ac,f4c1c2f9) +,S(ba68e5cb,e0f7159f,821117df,5720bb50,f218c298,9ce3f5ef,30c44134,2834bc9f,2ac25bdd,ff34e4e,ff45a2ed,e412a287,101ff447,71c657f3,ca57bc94,40fa5d72) +,S(93d6e713,311d8430,58c20267,739c494b,446d7943,a1e47a0a,2a5ec20,877e6baa,5997c19c,99a2fd26,e0471041,b509f9d,e10bf615,7cd4f24f,db56a65d,201a0856) +,S(7a94bc1b,5cc77a0a,73a84fbe,c5d1b68,cb8f4626,8ecc0c22,ac7500a4,5f397172,128c1c6e,82a4d52a,719c9f8c,1bedf4d3,32d3695,16decef,34af6b4d,578bee5) +,S(f031eda7,c19383d9,45eec848,f6f3e467,75fbe52d,bacadc54,c213c596,c821b809,b4a78d0d,1c17e4f5,db81e2e7,ee52f195,eb505d36,af96bea0,5170c776,23cabab8) +,S(86ef1acd,5c17e082,dd4cb924,c74d0195,ce9f375,60ca7608,57ab7d70,2039907c,6936a13d,b9078ac9,426d34b1,6d5673d,36c3105c,9cc77de1,ef7e0ec2,4cf3bb3c) +,S(fceb6cc9,c78e3a35,49b9beeb,62a14fb8,6d24eec9,2ab10bc9,40916b60,cbc6d720,7c2e6024,5f561f1c,e0b69701,512ca518,9f8d9292,ea75562f,8b17873,77f7583d) +,S(a1d9ed1b,9a36f348,1f426d83,7afe4380,d475e36e,3b140f3e,cd2213ee,dfa5c8d9,3683c97c,a1273f29,ec02cb77,9e9fee54,78bd5e3b,e6aa8c60,7d5bf07,dfa558a0) +,S(d98de999,6311ed55,e15bbdc7,58affca1,3e8f629a,77eb3dac,844f90e0,51bd48c7,8af6194d,272a8ee7,68d1b810,778e5c2,69394353,969d77cc,7d449f2,84ef2a22) +,S(78328db7,cd6afa29,b6f1ba80,d65f8050,60f4d223,9897a3ed,bee8ba3f,a99aac60,e37e496f,1d143ac4,8123377f,4e136442,629f8185,c4996f01,f35c7a9e,76fc5efb) +,S(3d701493,31faf0b,37c1e1c0,9b29ee6c,5d9747cf,cdb26117,223d1f66,e336db5e,d1e99140,8311b20a,b41f7ce0,ab4e9dd3,414ea3c5,b87f88b6,648cd1d3,e77600ad) +,S(f619acc0,47e0d4ab,bd5ba2a,7a54ecc4,e223986,fdbf7a96,e90f762a,ce288628,f24eb63d,e1545d10,fc96a2d7,e7fc1ee,f9cdccdf,cefb5de6,8fa4eca6,781564f2) +,S(cca809b8,71f85a00,3344a851,4f797fce,91718f35,9e2fb489,a1dbc00c,aad0a2d2,b7d72ab8,35658359,2258f260,a1f62f97,aba642ed,382992f7,12b11c11,6890b037) +,S(bbd19766,ee2c501d,afb4eec0,30fba145,aa26c45b,51c1beea,a7d3ed1c,7dc81958,bc709dff,70b3612e,7cf0a6e8,9724442f,6c1ca4ce,c32efcfe,b98f1acf,df5250fa) +,S(6a6fdca0,e4a58197,267a1938,1644aa5d,ca8f0ee8,3a68a5f7,9c34d4af,ec1cfadd,c28b1236,76585255,cc104298,d7ebcc70,6bd7ff6f,cadb0c36,db6517e9,d2732081) +,S(922e3e1,fd401a22,ee7d693d,e52f0ade,9564af2b,17a4be0,2c553216,df5c8006,a7b10065,aae84a40,b9b79e0c,5ca8a227,696841c7,20b944aa,3b9eb1c4,1488f757) +,S(53ffae4e,3526b553,f92bd9da,e3cbd3b3,aff8a6da,d4895490,38382f98,a676eac8,81fdfcae,b51f80b8,3d23b222,8ce860ec,9e665292,c456fd23,cfde43ed,99f8c6f4) +,S(14a0ddd1,c94596f8,9d3e7028,929cfe4a,7c2e70c6,b55d15b9,77227862,a03d119,2b5dc071,febe1d1d,df7e1483,53dc6558,7e8c94ef,b598ee83,806f1fbe,976dd086) +,S(7368240d,fb74f35b,b51a2542,8caf3b90,bebe272a,739eff4d,c0150f1e,3d68c8f9,95392b17,b4c5e82e,774c7168,7c46469f,21801a72,b15cbcd8,fd2ba85e,e4a7a27f) +,S(4c0a8053,b9eab39d,2396825e,2416885f,5fbdb610,7bc5879e,6400d010,20580560,85a222a7,5b7de532,617f67f6,5408b79e,f5a6f826,6083c2f0,93ca97bb,d7db5427) +,S(ca0d66a8,24a6f8ac,bd9447a4,933dfcb2,6751a26a,f5790a1e,91e32053,3ddabbf8,61cc898d,4f8b0845,11dfd49b,1ef01e43,6f7d7d08,a5f16b2,1550f329,79395d18) +,S(646a0775,e62f6a36,d520763b,5c7fb081,a3d7dff7,355cba76,45358a6e,55e9d6d4,3e528844,e7380a64,20db2366,22c936f9,b1100046,e17c66f7,a6b1a01f,7b7dbbb4) +,S(435565ec,f3d6bc13,142d7c47,7052da71,bac77f3e,a81107f2,30303003,6455d070,4ed52896,89fd8d96,343ca8f6,c9f0c70f,ab6edc5c,1ae96d15,9fa9eb4d,a4c9f6bd) +,S(d8b01896,9328544a,a51f22fe,e605c693,7a014aac,37316f,ff6a760b,93aa8f9d,63f51e42,a2b879ad,36d60719,c1497b5d,dd412782,e4c620ac,989f7259,3a06edf3) +,S(b9475950,4a9b3052,94af6f2b,6f182e5e,83a30abb,47f0799a,21ee5446,d63d48d7,cbc74028,37a899bf,79b89676,9e358ad6,9680277c,34ae1018,4290689,c2fb876) +,S(14317c49,2972d5be,c5a01b5e,34842135,d521b467,1b262f0f,c3e6a271,ff8e0ef0,cc4d2235,d1cecfd2,2e6a476a,7db483d9,d318e79e,4c3486cf,c2090651,c2069381) +,S(8bbdb357,5b6867f4,4f062ab3,38928d52,78c4b873,4e95c5f8,fcddd4e4,9d44f8c6,d38d3387,32424054,c05a67e6,c7d00e62,1279eca2,ee1b59fa,90b97f33,5173a778) +,S(7775fef6,36ecbe78,9279786,b996c0d7,380b00f9,4ed31476,1ab0b190,f06aa685,53152a73,162dd659,dce57d7f,cb885c44,34243153,2b8318e1,2a996ebf,2f55407b) +,S(db5e4bd1,39ebe377,bca64950,29e72fb4,bc9b0ea5,5450e42b,a7491c0c,39693b48,a6468113,a5a796ad,a337ceb3,238827dc,416ca20e,bdbe4e62,14194e66,cc5a0abb) +,S(eab057b0,578c52e2,eac11be3,9f069cf1,6ebf0631,709560fc,2414ff11,f1c619eb,5422023f,cf69b8a8,458e5c6c,3c2de1e8,6fb041ce,180c5f75,cddb455,fd023f67) +,S(e955fcd3,c657d611,7e2d43cc,651333e,c231ba32,418e6fe6,d021183f,e7f5e44c,dd0eaa98,65212f27,2454953a,4c46c5da,973095fc,b2e36369,cfe228f4,828e6b57) +,S(5f5a3c57,58eafc30,c311f596,f1d6605f,ec0f5e73,eafb27a7,2a07f91b,42d8e49,3cde0b0,1d1fb2ea,9d85a04a,aa4536f5,c6d9f55e,84ca890c,cf23ef2a,5dcdf1a2) +,S(fb7a31e4,ff0ade1e,efffbd7a,e04af118,49c82cf2,6ad219c0,7572d924,8b99bab,ea87aa0c,431de57e,ee86bc4e,be463dd7,9e69102c,736345a,5502b07f,31df2a01) +,S(d57b1d6a,a3e87779,4b9f8dd9,683ee3a3,6ec18d3d,e6b22b44,e411778d,4bae1ffb,256ffed6,b85bd2f0,9dadb0e,43ccbaec,9fcb9e57,22d88ca,73f8d7ab,dc0f62ee) +,S(9326fc52,d3a24a38,ef0fc90e,183697b3,f726832,c70bcb5b,bd6ac548,cbb8f1a6,863bd0a0,cc0a2bea,d21176f5,c99fafa8,d7d766c2,54d57308,1a58a6f,12889a2b) +,S(58362cd5,abc9b5e6,57617e94,620ad087,d5d310d5,313ef6da,36079b4b,a93caff4,5f2a404,4e86bbd8,d73b673b,2e432233,d6923cf3,c262e1c0,3da48d42,f62fc52f) +,S(7dd488b9,cd06e68f,e01b3a9,6b318d61,b8c4cd69,7e9a3681,34fce81,841738ee,fd7aa5ed,130c897,cb770846,a3d292a0,d963ded0,5266010f,34a99ebe,99782cf) +,S(9bf78b02,524c1554,3330d7d5,a3b41705,1fa2a324,6ba8061d,f9550640,cfe798d,59f537d6,6d2e6801,d79d5a70,3b4219c8,c7d06385,76964f48,10427548,90cf5a83) +,S(4c816973,743ad5cb,18db4f87,9bbd6d1c,e71513b6,eeebfc39,3663dc36,eb3fc47,1a6dc6ac,37a49400,a933ef09,f00f9650,44c6f8eb,d516c254,ab142d89,d41105f) +,S(60636bc2,c7486343,59296408,40f3a26c,173f4e5a,7f0031a8,35c1c56f,b1051be8,71a78b7a,3aed2bc9,70a4190b,d1be56e,efc2e6b,7cf81b08,541dd8f5,dbe88e8b) +,S(a6e772de,c3e90a70,6c663fc9,4d5e6cd3,12211537,40ca4a2f,47a0f3,7f8d00a,25b8fe1f,5c727181,4a6175a8,10aa1eb8,f1090221,d78c32ca,76ba7c73,d0eed520) +,S(79c6653c,60307215,fabb4b2a,aa8bfbaf,1692f613,166326c9,19324e5,7cb89262,36d5ed12,b6b49496,e2b79da4,c0955cbf,c24eaa3c,204e28f8,db1e7ed2,d2197bcb) +,S(3f556b2e,4530fade,d4774af4,ce4454c3,4a2bdb4e,aabda42e,d4ba8546,d646cb15,bab1d321,1ce84331,3be8996b,f5c5c4e2,c9fd04d8,397cf9e0,58829389,ecfd25c3) +,S(eda59a23,8d21fe9f,5e11e0fb,cf731fe2,d645c52a,f3861dd9,3b7273b7,3fb3913a,22f22f01,4ba320b8,2cc29c06,d75b7eb7,d3cea857,40cb2562,4161b9a8,712bbfee) +,S(7a1ea77,323dfb4f,36958adf,71d4f03,3a2bea2b,a9f62cb0,c42a4279,7de68548,4866ce25,ee7a2b5d,a276f976,6261318c,6d2d7f51,f6f41940,f6cc3f20,264825a0) +,S(ad0fd3a,ad62d6d0,bae7c51d,590876e2,33d1028f,62d5c178,a0ecc3d9,a323de2a,88e024c5,9672469f,54f4a64e,b792d761,d163157d,ed982787,a6d40382,83b0601) +,S(1bf19432,f4044a69,56d6e9d6,7b84a5cb,ee743771,8d634dfd,20706cdc,602729d3,4114b200,afb6a1a0,75ef8e3e,16c37e9b,4f2acd9c,3fa4e922,63d1c430,7c516aa9) +,S(514616f2,bc2dafa,8be3b9d5,f00b694d,2fb1af5f,12d219af,c35204c2,e121d93b,3e5cee53,608a299c,f67f4e0a,65adbf2,57ba8eb7,450058a,74072ccf,922eaf9c) +,S(8e3fece8,5338ed31,93721d18,1cff34e4,fdc42330,6a8e1a1c,3b002da1,6921a661,b095734b,6736486b,676dfc24,9cac99ef,12894543,8b333b2e,77664f8,20f686d2) +,S(46c06820,df101ed2,92842186,7d8a20b9,6db1eb9a,ae63355,916d438c,a7ad4447,fcaa10f4,59bb73f,c48d1b60,1147b535,d7d43958,1bcfe26f,9acc7abc,ddf1afa9) +,S(56334df7,1f9b390a,b001e406,ab37af71,3fff8c69,1c2eedba,fec7dbf,cf722b31,22d04394,340dc156,a73c4b0b,f49a6c93,8a3724c4,4744690,4af420bc,726707dc) +,S(202e246d,89045f8c,9dbe3dc9,7ca52d23,b2fd55fa,1b5cbd37,cb882663,ca67c4f,f4e6cf89,9cbcf023,1c9f661e,c4588072,7a6241e3,513cb365,9a2e7b20,a17a62b1) +,S(c8054ee0,10211c2c,f5bc1ff9,43552eff,c56b635c,d65e409d,ece239e9,9b9bd4d5,f50515af,51601cbc,de6842b4,3b854b05,bf1ebd1,678c46b,46e22827,17f31057) +,S(9c29788c,235eb61a,d2940708,4b9701a1,ea3282ae,56f984f5,1c625538,14438383,32111eec,efdbef2e,236469db,4ad30e30,c116cc42,99dd286c,2ab70ec4,9759ead7) +,S(5854e8d0,b7b5c394,7068dc86,f94c3cd6,72999023,367509e7,4bc1745c,19a57ef8,7e7901c5,c06bebd,f50c4113,4e0d2d3c,edafec05,417d45f2,345b75cb,a63eb98d) +,S(8bb2fff0,f45be789,a0446e1b,3547a44d,1104a5dd,9c8e50b7,4d0fa891,f73fef34,c332e365,a5343ea6,6c571e2b,c383f767,efb979df,72223a79,35da9173,73b182c1) +,S(ea88e65,6ee43387,86165faa,cb643cda,992f5656,dc7333c4,5ad8b561,8ccd209e,c38c40d6,4a7a343d,8ab357ab,845725c6,af9f4f4a,843986f1,fd0a83a2,5df0f909) +,S(c0c1d4df,857c7ea6,13322c2e,e0d4ba2a,a3b18ab8,cc8d9ff6,6981d6a9,4446295a,247115d1,e8eef7d,ce6ae1db,ec9be46a,9a284648,ebb7a918,7292fe05,9d72b981) +,S(91e6a4f9,407d3600,2a808d8d,db723ebe,67b8e212,792ba65d,b74e028c,ec7fc72a,ef6ae560,35f58be8,5cce98f7,b2106e0a,12e57dbb,47a4a995,47ee5041,3c5721ba) +,S(cd749618,9a8c1d36,2995cf61,380d0366,24a7a402,e21cf078,18794e08,fb208454,f3dc92be,15d1f912,a03debcf,88afcaba,2dd6ba2f,1d23a4f6,ba02c5ed,cbaf0bd9) +,S(99f5d12d,2e68e70,c304f3fa,44eb24a6,85f63065,af0a15e9,f71752ff,82943018,59f1921a,d97ebf33,68f962c9,c0a297b1,dff9f980,246d5dfb,61168dc1,da0b8a9e) +,S(63c6cd30,f9c07096,11656b1e,75d17191,a6ac02db,9debadff,7b3d602b,b9f7608f,2e439373,70a41b8f,e0e5f0a1,3ab75be4,6d8b1ef4,ee71723,4b73ffef,461083f0) +,S(b7407f26,90a5ea11,d9b7fc55,17e6c1b1,9e04fe47,180b1e93,94cd5e97,5d184d92,1b953929,47dd4984,7420f727,7a6796e7,828ee880,3e0d701a,aed8cf7d,513de6d2) +,S(24979116,161c8840,dd521e96,27a50056,c29ce48d,d86b6297,150f9a6d,e383fd40,13dd71ab,3dd69cf7,384d3e32,5fb16648,76f99943,f7ba3649,143e09b0,28b9a70b) +,S(f109b6c3,6122b278,8656c80d,4c84150b,b419919,4650d96b,6f2de8f7,f9d73b7d,a7457369,bef8d54d,c0a91d4a,39496f85,bd971f6b,4a13dc77,142ead5d,791c96ac) +,S(59b303f8,2031c370,4f17c929,7fbd0ac1,36be9900,a26b5516,9f0e3b6d,69118df9,30c7c4dd,1666271a,5ac8c936,b35068c1,9830306d,c695e02,d6c65ebd,2fd678c3) +,S(6627359a,eb3910db,4ba54cd6,c7b884c9,a678621a,d2bf6cd6,6023974e,d462fc52,bcbcf9ba,d2de78a7,fc486734,79379778,feab83dd,ea4f50b2,555c457d,a910d487) +,S(76fdad80,c4fb507d,9c9d2305,90e818ac,ec7efc04,1184281e,9212785a,dd51f1d9,cc8cc7cc,349cf307,f34adf7e,f559855b,9b88b70d,7fc9d19a,18cecddb,e3a8c346) +,S(7893406,e99cf08a,52fed9cc,4a9bafdb,7f2380cb,4c288700,b983b791,e1313330,ec15e1af,39eafe5a,d081acd3,fbd5b8ec,d0c2e83b,7a5b8074,50ef80af,2511d149) +,S(c454aa4,e8d02133,ed93c229,d0e834f5,18f16cd,d83a39ff,3bbb1ba0,838c81d5,a98e61cf,89c43808,f4895924,529a6e59,8af57be6,debf5f5c,67e3b3a8,73ac51f0) +,S(ff0bc21c,cbe5c122,dd2ba8f9,64239010,97dac1ee,3957b27b,f990b611,72402880,c34089c,1f7b2181,8d4cd5f8,9e445ec1,ecd634f7,52960da,467cd396,77bca50a) +,S(8e4486e3,49a0423b,d8132e39,812b78f0,3a0e61e9,d3eb7735,86f1e279,3731c301,897b24d4,37cc260d,52e1866f,4dc222ed,16bbf084,c108883c,a8a7b9b2,9a2053a1) +,S(4f4ca57f,84f5c4e9,6aca30d2,351089d,bf7fb6c,13847584,590fa5b4,4dc579e7,d53db7dc,1691556c,36f1cb1f,8a1b91a9,33634975,8f28d0b7,17d250e5,ade63a9e) +,S(fe8924b2,d7a24fa9,5c61830,aed8fba,79b3a96f,388b6423,c760aad8,cfc203bb,bd303e29,c6be0e2b,f4e79df9,cb17e0ac,e92c3784,fa8c26a,7e3cea32,d0b056be) +,S(3099f75c,f6a8c72f,8685895d,152f99b3,8ed9a3,320f3fc5,dcdcd14d,c46abb5d,fbb5bc2a,ec76529c,a278a32c,2a95c2a7,4a939c4,c0e2c0bc,6fc20b2e,a6fc9f1c) +,S(3b939f83,f84126f3,b6632de0,12405c9f,43244322,2ac384b9,7415c2dc,9c72f4eb,843b7353,ec454380,e9d4cc20,23a138b5,c1e602c7,f48be55f,1d3c24ca,f05607fb) +,S(dc7506a0,4a50e345,dca3cbc0,6384a938,b032f197,d6d30932,7b0c646d,95c732d8,fe942311,f6d49d62,99f9559a,3d16f43d,c6a39d4e,b623f9ea,e8dde39d,9bd5772a) +,S(f5a4c596,c6996a35,172fb61a,2721d75c,3dde68fb,4abc9433,f84d20ea,83b30dea,175e0d3e,77d2f63b,bb2ef922,d30b962,fd783b30,ef7dd8e3,5ae680bb,9df26572) +,S(7e9038e2,c499049f,e5d222ae,2f79f4b1,373c2f69,262f133,cb9be1f9,aea4029e,3592e66a,343335bc,e016276,6550a363,1d7dc64b,2223bdfe,7146f32b,8025c737) +,S(80f22f73,d16bfc6f,d6c28e92,1f990eb6,e2cb803a,d920850f,af489ff,f62b1f41,8714a5d3,c5ee74bc,73e11fd,24cf4d80,370e6711,c726ed68,2a1bd77e,7b503b28) +,S(498a2ae9,e86e53f8,43470a58,ab96fe0b,7c3107,d9d23535,fc887bfa,fe0c897d,47aa9eb3,73e841e1,4260f651,6ab3081f,7ec3809d,53b8609e,c3cced3a,6401bb53) +,S(601af64c,d14ce184,ca52c542,7a78c746,8e9c069a,80277b6d,960ddf30,3b3a010e,2900a63f,ca1576d0,11b46c27,32c87ef5,56dd579b,3bcd59,9161cd20,ceb7fa18) +,S(8fb2e0f0,7b2fcbc4,ad58b06a,822aa683,957f2bf1,5dd5984,9a1341ff,2d74a25b,7cd82350,8d797cea,b1b386f7,5d5f82af,6137dc9,9f6d6e6,b7c68cb4,75070aa4) +,S(336d780f,e9d14e8,b55a1498,72c63bc,83468eaf,aef3282c,9bde58af,6a69e6d9,92d1004,be45bee6,b8eeba68,2fd0187a,ac3e6679,f4fe0fb0,b723214,a63d2261) +,S(507ac721,f4d58704,4f8d2cbd,fb03aaea,b0634303,cce3e1a5,e4e47efa,90d5872d,b89a398d,e7db05d7,a1055093,97b83d60,9ba53c95,39caaf0e,917d22c8,307174d9) +,S(c42dcec1,3bb7fb96,29171026,f66c012c,992c604c,bea189a0,46e688a9,473ba343,8aa37731,a53223cb,a021bfe7,c413929d,8fe010ec,353464b,e3c1ce02,e54f7bb5) +,S(b02ea2c3,cd6093a2,d5e6b5c4,5c510848,857eedb6,50038417,d9f41947,2a866a2c,ebb45c50,cea190c6,1bb7c505,8cfce93b,ed1d8ee8,c75a5bf8,6ac9c127,2fa5527c) +,S(f4d193d2,9f20ed2e,273dc2c9,aeab43b5,85626d41,59b7edb1,51e17c45,ef70d84b,bda19a3f,19b834a2,c493f8cf,14cda579,1f84b6c,611ea7e7,280e5196,1add0974) +,S(8740dceb,19eac62b,75859b9e,4d0302d3,cc84f6bb,1ff6e857,eef7fb64,fe5f1bb0,a729eb0,4783205a,2e1d5b6e,7eb5221c,f2f151de,1489d1b,1d98fb68,e7c6ca49) +,S(79ec464a,85a29f08,d910bc94,449ce88,50aec626,3693bf6c,92a5ea23,84fcc9cb,77d170f5,b52ed0b2,9aad476d,85abae3,a1d7c544,c625ba2c,d44e68ea,ef53b39) +,S(4f1018d5,dd54bb71,cbc0e29b,255eafd4,6fb3aac0,45889238,847efcce,3207fceb,c32819e5,d23cc2e7,73a9b2a0,271fbe3b,a64531d9,2d416222,24a2f64e,3be7dd8c) +,S(dfbf1936,2094e85f,ddb25de6,8ca85cd4,e53768ab,81780eb0,33a0607b,fbe969a1,25ea0b59,33ba21c3,2ac5f1e4,932debcc,a2ca3aba,e0b7c671,93d97e1d,af80e563) +,S(184c66c0,2c36883,2c1805f1,8612449,940a371,54174f9,b67bb41a,85226649,cf63fe44,f1cfc791,4fb4a24c,1e0530e4,736f815b,a910b487,ec4fec7d,f8662eaf) +,S(daab3606,d386e04e,e39a8a51,a8242e43,ede9de09,f50322e0,a0f410f5,f043686,c0ba1c4,ebe7d10,6fd0d0ac,4e8f6e8c,fbb2b682,3a9d55e5,dbb7718e,d411718a) +,S(a887f4a,5311f252,ba0219d1,f848d334,26b0e216,69020dfa,89dcc339,7e04530f,5a7c04bd,8c37cac7,f429bff3,eda01dce,a5b4fa44,eecd3195,2878f79f,9dedeace) +,S(1b315664,409feb98,4d5dbfd5,a85afbce,4784a907,b284958d,28123f61,7c2d207a,2972cc7c,ba348e5a,5841d463,4597fd57,84eea5ba,a99bdc37,3119c568,aba205bb) +,S(154adfd4,27c30d56,d5f5de1e,d7e8465,6727e69f,82ec8d1f,19dfa16,b1fe4984,5ba86e8a,324b279e,449f61ba,a7fa52df,882477c4,921f2079,98199775,3e0372b5) +,S(debd69f9,fe55c15f,b28df417,cb1af3c2,585fe89f,b44c2f94,94fec0d5,88480994,98d8cc21,55c16d24,e8b1b831,4fcf616b,2564ff,9345fc9e,f8de3cd7,57f9708d) +,S(9fcd3490,2a7c54f9,a2855fc0,9558c465,e0316c3d,fb72c66b,71d4492a,efbaf9e2,a7ac4160,c2b5a416,89e9e49c,8d5aba67,143e1de8,26498be5,1cfa691f,f7f5d14c) +,S(59cacdf6,fe4bae99,598fb1d2,df3de7e0,586fd792,eee0d9bb,fdabfa73,8943a727,7c730d34,7b3ead16,c74d349e,191c484f,624599c4,d379acf,ed8d2341,160df035) +,S(5ce1abd1,fb291d97,db3a4c9e,c4ace635,34e6b373,407a8821,67722fec,7fb226c,abc24634,b4070973,d8a11025,5ff7a81b,a39de6f,5275d3f,7630de8f,23b7526) +,S(f783628e,bd0a0b4f,6ee68001,5b3014f5,e70897d0,ce159baf,3afb1aa3,207d73c,d06438fd,5b96b88c,94a68b6b,1f5fbf32,6c7a6efb,83b2f1df,2849f809,cc39af61) +,S(7830fabc,cc7229ee,35c7130d,b441d2ea,9a988d47,72ebe00c,6c159658,537ba0b3,49e8b58e,6aead178,3d5c901f,a803a33f,78acc889,27f2d57e,835aaadd,1b38fa7f) +,S(c930390e,a8901286,21a4399e,ef9a4a90,6416d549,c28f7482,6252fab,5fcba9e1,fa0e87da,3d0618a2,f54e8fd,d14b4bca,3bab137c,daab0197,4d110ef,b161cfce) +,S(2a0da867,20571917,6f949876,e5f00ec3,ba0924b0,a5e758b5,2d0c23dc,da9cd28a,96cd04fc,69350959,595b4870,f6df11cf,90c1fa1e,cd04ddd1,975d0bf0,fc907647) +,S(f64881e2,60da581a,f576e92e,3968e24,f2bef6ce,dc1c9c53,256374b3,1446fc83,808a945b,660b67f4,43ef833d,b491fa56,2aacec09,f8a2dac3,863b72f5,e7c8b94) +,S(970bd933,5ec5f0d,3a3cea17,5090f1e0,c181e488,1565c341,33017f99,ab81f052,4d1d270f,e284774a,fcbb6658,d5cc019b,8eb30bdb,39464a79,6ca1431f,1ae9fc48) +,S(dfe94b31,24c4541f,4723ecfd,f5cad197,2c058886,9283073,35415bce,e4c4779a,cc283e53,d547606a,a0fafe8c,5d3403c5,d047aff6,8b194b1c,1855a7ab,9540e23b) +,S(f1813379,673ae594,8fa30bba,d8737d0e,16c8bdd1,8f7047e3,bf33a39e,858df800,acd2c1a6,139a2dbf,9b008d50,d0b4d1f2,20bfb9d8,c24855d2,ed54440b,a87d294a) +,S(f4fbc156,fb631bdf,51192bd0,1c0db0c8,8890ec97,9c368f3e,c2106043,6d423740,3cb08cf0,5b07deff,431faf91,e1874baf,9cfccd3a,df487f4b,4f69026c,1fb7149f) +,S(124da4e8,3b079d72,4a7a9a72,5cc45a80,cb92675e,74541bbe,d36224e2,793de6d,3e700066,f12e0cba,4d6200fb,2a759554,1cd15531,ea065e6a,c6fb4986,548d00e7) +,S(749721c4,455e7826,1de045bb,266b97d0,6c2b112d,2ac8c16,428ce961,b78d1138,27556aed,a3f331c2,fe95b237,d3326c7d,25bc594,39f07b87,86559fc,55a3032) +,S(62cdca49,aa58f55,21c60dfe,d50fbf58,257676bb,fd4c4e97,9ff9a91b,7627a028,8bd49b9a,e16d10ea,7ee1528b,4f433e8f,17c4a688,a7148fbb,d10ed605,595c37b5) +,S(64d650f1,39b13aa6,16d3b227,5ed164b1,1cd3a1e8,81759344,7895a4b1,686478c6,deeee52a,f7572d87,f339595b,79643402,f9e2a065,7b95ba42,6b83d87f,3dfa5c2f) +,S(d08a3e55,66c636ef,3417c17c,d8526df6,37cb935e,db00365a,1ecc9f4a,9ab59d,2db049c7,5eec5176,3fcd3502,33856da4,e9ed32fd,24242a54,9c9ae462,240dc103) +,S(8761705a,35bcc509,afa20d62,61859afe,97154782,49ba47c6,f4b8df0e,30f20d89,b34c25ee,bb029622,4929c3db,78046a09,fd43b9e,be2dbf64,436f0df8,adb726ae) +,S(e3453933,4ce22643,146cc317,54e7ce7e,e35f6f4,e37b307,c4e9e11a,3c6f5f85,73ee178a,941e81be,ff22a0ca,f2bb4513,1afdde7b,336a3bf7,ad05c1,d465d97d) +,S(8389192f,6609a7e7,ac8e6f11,fdbfce8e,246b36bf,95e0de27,fac36082,34ca917b,957073a5,50448ec2,9d67b681,ccfb1aa2,67c88976,e53c99be,c68c378c,f2b636f) +,S(8bfd70c0,67bbe25f,e2e78029,a97fd863,bbf287fb,7a24eb8d,e5a61af4,c3c5d387,e2e6c66b,ee7265cd,5a1b1d64,4f24fcec,8b59001a,6edbb1fd,5d894740,f3f952fb) +,S(a5d0999a,a05f0bb1,59a1582d,980520b,eaf82015,68875269,fdb593e0,e6ff257d,e34d9720,5ef05766,676b62b3,62ce4b34,fe6d27d1,a644050b,f9c65c34,c423635a) +,S(44e479a7,b949c88b,507942e3,89ffd9d,17645185,92d4e544,2827baaa,5d5043ce,7b86ec29,8c12c463,b0a71416,24a570a9,e2b10d77,ae12ee23,2504edde,213a7087) +,S(60dec943,d9797811,706e0251,e0abd0ca,3f37bb8d,559213c4,3176ce3d,352aa558,2dc689f4,a31c59f6,c5181448,d6985335,16b0764f,8cc6fd50,8bc109d6,69fcaab8) +,S(5ca58d38,5b5449e6,f93c5c5,35941b98,d4ab829f,e641bb24,af7e7ab3,1f7709b,82afbae9,80eb6f48,110ce090,7f39ddc0,945d9d84,d5d3feb7,4d96581e,c17cd575) +,S(b775967e,a1b1d60c,9b09cf3d,26bf86c3,b9a23ea3,cd329138,10099048,d18b031c,d2b7169b,ecfa1055,70d885c4,adcfadcf,66f12d8c,846f70b7,12004097,840fef1) +,S(73d8d4f6,7d602f61,3c3595f,b9aa05d7,a23bc4c1,1fa3844e,92e6b79a,40220f96,bf722a2c,ffc9fee5,bea18530,9ecd14ec,bb17fb3d,568fe85d,331575ab,2400ce3d) +,S(e8104df9,f49f7822,152bcce,79c6df0c,8b1e9745,448c24d6,1e7b1180,99182b75,dc2fd539,ec8f2beb,1bfb0da0,f95ffbb,e14fc682,1c5d5023,96b1253f,82137361) +,S(ecd72ef6,fb6c432c,8e1449b6,d16180e5,777e5524,4900f209,e89e995c,546113e6,1671715d,358fba99,e509f518,e1458600,4252913e,268129e0,e00d64eb,7a26e135) +,S(e3b5ccf,be829bd1,6f1fc6b5,fa6d7c35,464bfe10,7e9d6455,a9907050,a4fc36cc,ce7abb9b,5471c1a6,de17a2c9,b5b671fa,296cb5f0,10b5ffa3,31e7a87d,a28044f5) +,S(f3591a02,ffab91d4,eb7e75f7,f4ebd22c,41c1ce4f,25f89c5e,d7ca1ab9,27107734,345a2e11,f2bab926,487c7d53,5314bb6b,cf04746b,be2edc10,18a57c2b,94fa4b3d) +,S(ca4b045d,ae54ad95,37d184f9,ffc5a950,c61349d3,227ae1a6,5501ad97,cb45c635,e30f817e,255ebeca,352f3c24,b441d1e9,4660f456,ce9cd72,eaf4c4b4,d7c1d806) +,S(f7cc5740,54a9ea61,fb8ebdb7,b76a95f6,30094720,675aaa72,dfd29bb6,6f41ea38,9d463d8c,fc5424d9,18275aeb,5e046be3,b3dd7fbf,f52d4990,f52c81e,b2556eac) +,S(f75b5f79,fba3e3f8,ee64bbef,7d34e8a9,b4fd9bfb,27b6a9f8,76b1f70d,ed6ab6c2,220b40bc,5c3fb77b,72c48e36,2745677f,7e0e8266,f85a14f9,d4030ec0,90a43faa) +,S(2aa367a2,5060236a,627d89b6,ad0a398d,89d86a64,735a4fde,e88fa4c,d9cd450,2bfedc25,2491b1a,764a802b,c4d77d86,1e70ecff,e9aef8f6,e829448c,d841fc32) +,S(ff7f49c1,11783cf6,e7da1e22,15827b07,a1078ce7,b1eb3257,65a37c38,a141620f,69550c9e,2e48058e,8bca8b54,21251dfb,a488c585,df644bea,d519acf6,54702733) +,S(31d05358,1164f4ec,a45f7338,fd3ebb90,76d017b4,5e679601,6fd02a0b,5c3aa39f,f124319b,72177ff1,462fa90,2e1f8d23,d4d3ea0e,d1bfd1fe,6bccd92a,9963f575) +,S(6eaf0edd,fd1203ec,a2cbf2f3,8da9ad7,c8a02d3,ab5f06ea,bb88fb5f,7e6a3bd0,d9336d01,2d8e9474,49c521a0,10a3b83a,815a0c74,a31596d4,125909c6,73e05f08) +,S(b95a519,77991ab7,fc3943ac,ff5fa88d,68e759c8,11cb91b9,e9a1ba97,7eda1347,8cf2cb5f,446d448a,5a98fbfe,75a2ffa6,9758bd15,36eea87f,533822bf,6de5ef23) +,S(4e5301d4,8ce5edba,f3b1dc21,2e804e7e,1fd386f5,6b90eeea,da6c1c58,211427e8,f503ec3d,e169fdb2,e1d2f684,cbd9d684,dcabaad9,376d96b7,6fb0a5d8,1b9f0d9) +,S(4fd1a9e5,9031b43,715f92cd,c49c2d0a,fc7c2b54,ad61d7a3,9142033c,3cf49cd2,73ecc11c,ab0165bc,3ab4b43b,ecef694a,2d99c9be,8b877160,2c885a6c,8e4adcb1) +,S(67406865,b32543fa,5ebb7fa4,a8d8fae0,384070fa,2e8a4b86,74510099,20257c84,3fdd9ad5,93a4e0b9,4f4451f5,e3227df7,e829a45c,ef3d271f,3fd85896,bbc62008) +,S(14b955ae,d6ee5be6,ef3dcfa0,edac40a0,de7e8dad,19c8dcb4,e4992a65,a46569e4,d4b920b6,21823f81,8481eff6,761353dd,7d9f31c2,5ea9d1eb,ed60a5d5,fa2daf70) +,S(14f283bc,5608d5fa,dfaa0b3f,fe42fd20,4003e434,c6368fb7,22ca5f22,3339ae42,b2415e3d,c3aefd3f,c644d4bf,b0a87fdb,bf0ba630,2baec8c3,7c1577cf,9edb7397) +,S(3fde0c67,7d3ce20a,6e6275be,7f80cba8,5416a55c,58dfd09,8e733a32,1fbd1c1f,9b8ef7dc,610f192c,949782f9,bea1f08,9a018b25,8926389a,1155d033,1adaaac6) +,S(9c55e669,df5f3d05,f15c74dd,48290540,b993bf4c,41dd9247,df5332a2,ed17267,23db0ce9,30c97665,175abd78,33757fc1,8c6b3c32,2d260810,981b4e0a,6e4979f6) +,S(bde84df,ae141a0d,6e8da60b,30f41746,506d2dc3,4b3faee7,d8568a92,82968d16,45a33214,ff1361df,e724697b,99a6c686,900374f3,6c298dcb,468f38c4,4ab3ab04) +,S(1940294a,b8f3c2ca,45f68f45,4c60c5a1,e5d97085,946b30c0,54939daf,c99546a3,563f9f99,3816c62a,1e60ea76,77c61bb0,848bc926,e76e386a,bfd96bb4,ece3b81e) +,S(dd6ea1e0,d0d13e64,7d0df92e,ad22d838,757a34d6,373ccad6,6829fcce,b32ccb0e,c018e9e3,6bf4e8b2,853ad27f,a49e5ff8,384b557c,aa00c3fd,d8e008f0,ee33c313) +,S(657847e8,c7844562,e26656bf,bbc27b84,dc104737,42c4dffe,c0669616,5b7fbf19,dd90dfb6,58b6ada4,901beda9,a4a9a8a,42c6a4b4,e79a4172,5b5f6ab6,95f2df5) +,S(fb7024b1,40be9d6e,906d26c2,edb781cb,49b8e71f,396b8d3e,352506b3,fe3dea6b,de408ff4,e50b2328,b6ad961e,9dd1ac46,fea36005,1407061c,5a0fca32,eb08efdf) +,S(2d29c008,65a7b279,d3a80179,cd3e8944,b3f2be0c,49a398df,a3113350,d1d8e8e0,caaf127a,ee704a4a,65802748,59649e45,25c55005,f2667ee9,92e9d923,daefcf8f) +,S(db690474,79b7209f,4de8d42b,a3c1be3a,39268589,a7c33632,6d87c1cc,53d5662d,90de8be9,5c3587aa,9913ce32,c83e24e9,c58c1fbf,2922c63c,b777770,fbaa4e00) +,S(9806506f,922e4f0a,afd54ada,258d6fc1,723c7087,ccf13f0a,df247356,c941697b,c07e2018,a8cab841,37698eef,3d31cad1,b97fef49,77b277c9,25245a12,f16bc237) +,S(ab868d3f,e6da25ac,5b11c112,28f14621,661e0ad1,e33d25ea,ae1ae7af,60e40187,ee24b698,334a9850,1a1195ce,d57a7fd9,4b12c488,bbd301d4,d47d5f13,3cbf54ad) +,S(e7561fd1,9d2a8ee6,12056be1,74257e28,3122f366,abf66f49,31fd89ee,fd8dcda2,2052e74b,e3656d8c,5c766ea7,3227b75b,85bcb190,8a07918d,5a8a8001,3e166d5a) +,S(5f19523d,84bbe307,be08f2e0,fa844cb5,7b562f01,39950820,83c2df9b,e8f21e6c,3a96fe16,28ee1dde,2ae9ad2d,5724eb88,33377542,6e91acb9,d9be7c41,7265d7fa) +,S(c9fa4668,3c949b11,2422ec1d,c5501efe,2fdcad17,255c7545,69837d28,d9c10cc1,1aaae75d,18f691ac,cf1dcd8d,9e8e0348,b5b6e734,76d47ffa,b5e23ede,c7fd1dba) +,S(1da90e2a,1e911879,ccfa084c,393b8205,1799b2d4,15a42401,b1ab793b,beb8d8c,3d3f7c69,11ea4d38,46cbae1,53b3fc32,74994ee5,1f4fd332,2da4d28e,c89c0b63) +,S(c6fcf10f,3851e432,6bd504ac,b7847f7,707e4098,d8f66915,3e6f89ad,f31b3186,d0ac89f4,a16a63d8,6e36ea10,8f8ee5c8,4e2705af,a9c98bff,3ea5253,93a50ced) +,S(15057a2e,eb56c67c,cb6b7d4e,f1257ae0,3cf236b,1f9b6588,fefd7f46,16378233,4dbab530,ccc0a7a8,c49543ef,5e3d0a87,6f205a62,2f9a90cf,5054bb0f,dc4930c2) +,S(37eeaee0,72d5ac0e,6ea7337d,d07ddebd,45bf1cb7,8c02dea3,6d1e419f,308b20f,b0c64496,5d9daa14,19c77249,a32a3a3d,a5761adb,bf5a394a,ebb91630,f361d506) +,S(5a207bf8,3a8bd0e3,3c71cd96,d928bdc5,f1d6ce8b,7ad9bebe,61a27362,2557745c,3accb757,fadc9b6a,2a5494ac,15744113,ee878901,323c6138,19ec7666,60f54dad) +,S(fe08dd6a,1a0fe325,5a391271,e81befb6,f6b2ee08,907046d1,642d39bb,fbabd139,42c342d1,3535822e,e4ef1b43,69f204c3,665d2326,1379b0d6,c520c64e,181ef40) +,S(b7d262c9,8dfdbba,6483edcf,38044cad,d6d859e0,3d1ddac,a39cb999,78706111,96086350,b308b397,6a007c8f,5181b5ba,30bb4c11,653d689,ee97fe47,28d798e8) +,S(4b12ab2e,23b33775,3ae97d7c,1badf5a,7d354b57,dcc78407,6836f802,34130c86,419d4e0d,9f708bf4,2e11e73,a1fe5815,2bc9649e,49ae45c0,f52fb8f0,f23c450e) +,S(fc6d2682,a96eccda,4b49570c,8bcc47c4,8f362bab,7750b4f7,4f30cc49,d8e88dac,1af56527,30264580,d1f22612,e90597f8,694aff0,96001848,8a243ac,f4beedbb) +,S(c7b9c3a9,a189eba0,54742c40,c4afdb9e,5e07e6c0,e442cf06,32e9b655,1e607e8b,5bb68277,2f2274db,62084cd1,9c1a545e,ef6b38b8,d4dc3e30,fc58042f,f01822ab) +,S(eba4d342,30dea72f,29a339ba,3ed4306a,46c761c2,bb6f9627,3406fa7,50dfd956,fce17a0f,23d278b2,e3da1747,706840cd,d31b01a5,8793506d,2155cbf5,dd237cee) +,S(a4d36dc0,2177d471,35b4358c,6adec285,3595dcf4,6cd9acc8,944a569f,82c04155,85a29779,63b21be0,e378fd0f,b5a60fdf,65f3e45d,c2fafad1,13377f4,81f00281) +,S(4916d063,ae8bfae1,e3f5bf56,fde73abe,eb165aa,4e6d4a16,9060ac95,76109ba2,a7916703,4a0b40d5,f967a08c,a536b7c8,3b2b0b4f,67638656,4ae9b4d4,4c3f22ee) +,S(761cdb10,b2966030,85f8f1a4,404ab49c,9de01979,85817f97,ecbae412,eedd046,1d115720,763b545d,72f8f50a,b881e7fb,9789f0ea,8dda90cf,2ee2f3a2,2e193db7) +,S(d7a2570,4d718e92,df85541b,490bc13e,860ff014,e8ddd9d2,e05fca2b,8dc64ea3,5e454b9d,21346767,71c0e035,fbc6dec6,6102db85,aa3acace,c158f43e,534cb90c) +,S(2af6004f,7962c52b,b0ebcb06,32975bbe,a6b9a99,4dc5a5df,eb20a43f,3d4d3bbe,6988a9e2,26f8cc15,3108e3e,b68c6596,be1deb1b,53239933,d91e095d,d81a14f3) +,S(a92740e5,3bab0860,1127571e,5d33184f,ca805d18,75ba4959,74108f5c,a9504255,5df2851e,2eb76b71,66dfd3d,3aa84e0,96d49c85,10daa44e,68d75eb6,c6d2379f) +,S(90d0961a,510d8a6b,60a87c44,93a0c8ea,76856770,d076057,bcc7ed98,70014939,74d4753c,1c913088,66b6f7a8,a2386ad3,7c13dc2f,85b4b4d3,f8d7603a,ccaa545d) +,S(ce52bea,f2e3a604,f62bfa27,92e2e083,8dd5c6a6,ca338ded,249be611,4ecd12b,a869de73,738a0a2b,7c824444,925f3d9b,dd08670a,8f8ff1a1,a4988008,2d5c009e) +,S(99ca8dc2,10e3fcd2,ffd39a47,1c06ac49,12215441,6cf16be7,cc3e350f,f3cd05c4,78087a80,131adc2c,6bf26309,ef0720be,4550fbe8,912142,c6ce496a,dfbc4982) +,S(be442cc2,9f5a7bc5,d07ff1f4,322deb90,cbfcc6a1,7d8e611a,b51ec610,a202d1c6,6250f2c4,680ed934,8028f2c0,1acf5bb2,fb3d1f25,e324b734,11156128,77c084a2) +,S(53f5f5f4,37fb010,2130346b,51b9b14f,e75d95f9,b8f80e65,67770bb0,e4ec8e54,8da52b53,c6e733f5,aa0dd2ef,23e2e1c2,24c0d571,328520f9,cb1cc5dd,28c62f93) +,S(206ce565,e90ebff,7d799e3a,134a56f6,a897ab19,186ed554,11de6daa,5c8c4d6c,c2ff5d22,bd2d6dca,7dfec5c2,e79d5320,aaa5d53c,38ae0675,4dca9a96,a6a75804) +,S(d1dcbfbb,87ee2fab,811f2a72,726d2b54,2176a20,cc23f744,faadf2ac,7073f443,106c63b,41800bb7,bcafc27e,3f8337d8,f71da693,e1f4350c,1870f968,edd17db9) +,S(d21aa173,c54376ba,1eb9d432,26ffc9aa,a516fd0,ef17c2f6,db117392,db749328,37cf5df4,e01480d6,604f79de,63a754b0,2326f9a4,ed74cb24,7724c682,6074e424) +,S(5234b024,a22c69a7,569acfbe,2cbb7ce6,15e56383,f8e1e031,78dd54eb,7d1fa3d2,2beae442,651db52d,62bc3b04,2a5b343a,b4103895,a74aeddf,d9189630,8bc000cb) +,S(9e6e9875,9f3e21ab,a709dbd4,f598894d,f5048dcf,5ee6cb6a,50941c13,35412f64,2b670199,e38f2e5a,424dd169,e047189,43111033,a7e4f4a9,2dc26906,31757ea8) +,S(8416e295,9a718e77,10df8a92,72de9bb2,89232c73,832597fc,d28f2928,8743ae45,6ef49202,801b408e,d81b7904,4cd1f002,20732a74,764c44db,4c77121f,1133d139) +,S(852a4fa7,48a3b2f,326405eb,e52ba2dc,27462f72,9ea3429b,b108e27a,d53561a5,3e06c046,ee0d9c23,6b0e441c,2cbeb672,770b99f7,8d062ac6,a81ccaf3,a562e520) +,S(2880dda0,2dbe56be,b93b876d,3c083ced,fa6097a1,cb234c5d,3282942e,3677c1bd,dfb0ea10,92d46bb4,fe95366c,5370dfa2,3d371f78,40e919d3,bf966441,f1bb43a) +,S(cb6fbc02,9e828d7f,c03dbf86,99a676a0,37b53555,275c86f0,7709ff89,f1819c65,fc4e2d9a,36577399,15cd50c2,1cb76cb8,ce9161fb,e05babda,ce516277,86062266) +,S(8087bb5b,ff1c3438,21b24106,606d0ec3,abc76a5,8a872161,5f6305ef,f81afebe,578b287a,a602c02b,8f5c5030,ae9a4632,23a4437e,944f4353,d9bc17de,824ce72) +,S(369f54bc,30f2c90c,7af0def2,2517ce58,f8ae829d,124ed482,653a28fb,42d3a33a,b8f2eaf,35af7e32,c63c97ae,f5962143,61f466bd,c4cc1b40,b4526157,b9b59d16) +,S(87be4c4c,1b2f871,53bb2448,ffdb6ac7,df294628,6be49eb6,edd3d533,1fed8f7d,7ef474bc,6386c0a1,5771ec54,21cd51d0,71fdc8a8,66938a2a,dfe6f4eb,ca0effb1) +,S(7b6678f0,18f75bea,8c310eeb,239edbe2,80393f1b,5eed1cfb,c013fa29,1e14715e,78e8b8e2,666ee69a,7388275d,2aa1bbf3,d985707d,3dd52890,c181a8b1,f38b1fd7) +,S(89ec641,96729e02,2a9608f5,8fba388b,77d2826d,6b37caed,90d27394,b160f135,5def3142,108d7387,19ee5a7,41deb18b,5e3030b0,d919a1f8,ac94157,a4786cff) +,S(2cbec739,3769090b,3aa2acba,b78c3fac,8b83749d,641f9311,99e6d1a6,36177f8e,3140e7e7,57ad2d5a,cb34a007,f4f26aeb,67fea7cc,8902fbad,2ca92083,f3de8886) +,S(678cefcd,a2d6a58b,53e7b116,d03020ae,29d57618,4663fcf0,2ebcf0ee,bd946908,f45be9e0,13298eef,37cd56ee,932b2a41,18834fc4,331c1713,314bd028,e6a3491) +,S(8ddbd278,adcc4ece,4692305a,94cdf011,5073b073,c88c1524,b43f1822,c73ec0df,90510edd,3c52c6c8,caa9ea1a,e28c7784,5d2d675b,a33b1022,68b93b17,80fc5b0c) +,S(972bf082,b162ff5f,22b149c3,d238260c,607d7878,aaf70739,3041d0c0,d7e36626,4685b3ba,f61cbca4,d99b64f4,3e90a442,6eca9f24,d0235324,797505be,4c4087dc) +,S(42af0d52,66604ab9,7de801c5,4514d23d,d8c1f760,bc86280f,540cc57c,bbc97230,5649b89,8c9029ed,4357160b,d97b74d5,cced0668,98de3b89,77192bec,1699ce38) +,S(5366770,87296d1d,8e6138bc,a9a1a0c6,1748ee21,4c0410a9,fa5623f3,d7bb30ac,e0683a60,786ba0d3,52619c2f,672d90ba,7c297b2e,59591f31,fd8b8fd8,ed5cac1d) +,S(a03b6284,e85bb18f,f04308d6,bce40e91,7d5d0ab4,ad920255,9de55c11,fc39fc24,2739ee82,c22cc348,23fe7235,a939db8a,c073005b,e014b564,4cad5a8f,e8785493) +,S(2f394ed7,268a53a6,bd998055,a24f7dd,f5801dc9,99233a63,7332e24f,26b66e97,b52b4f3e,e934ba86,d1bd3714,a26bc4c0,e49d09f,5d055413,8d3f4cdd,73918d88) +,S(91773010,527e537e,3aa0c19a,611ef5fe,7740bced,ef0b56eb,bb35a12c,7133f8ab,68f937b3,28b64809,27e1063a,bbbc36b5,b097ae5c,ccfbb965,f4814f,ea9fb5b6) +,S(2851bdbf,cddc8af1,7a20c1ae,bd529df8,28486bc5,50a00b24,763600d6,904af615,d302c085,702e27ae,6d46c376,33035d6,ee2acfc8,3b731758,5d15b2d6,dccb1356) +,S(e986d7ab,25499f1,45610b79,1b47e597,8be3d363,5a6f29c2,5d34da48,314bdfef,d0638dc,b3b3c150,10ec63b9,61104559,cfdb182d,31c41c16,5f53c331,47de35a6) +,S(bc45db2c,5639b2ce,2dc1c5e0,d831d824,ab44daca,7fbfa099,b2e648e2,8f8bfc03,5d93e3b1,27b289ad,49e32740,a9f53e0,79a80580,82606884,2c1f9b52,7213e32) +,S(4145d67e,47d9809d,2c67b4c8,49e29101,849ef512,ca7b22fd,667498cb,50a8dfc6,5471f4fc,2a4f7f82,9708604d,90daa64f,daeae007,d6290ea9,4922173a,4513f096) +,S(2d157f7f,4cd79d56,bb15314a,a738dcea,427e3087,e7a5afa3,c1ad740f,5c686825,c1c4f318,76159f4b,94e7a29a,2766bb46,962a4e21,3a7234ee,eabf1ea2,296fc1c0) +,S(2f426284,77040b2e,5dfe26cb,c12a81b,d6e89696,8226987b,361d7961,a73933ba,bdce120d,141b2879,effcf601,c75532f8,a384f942,879f24bd,45e483d1,8144075e) +,S(b419cfa7,6cc91302,e683c9c0,eef23ca7,bf9f8f44,37ea220a,b8a1fffc,3bbf527f,bb86954e,290f2419,e4891a70,b48806b4,a911e956,e3cd0dba,3c0251e3,aa929517) +,S(cdb4a6f9,5734164f,c7af2913,f0ded939,2b17bd09,b03698dc,ae2d72d5,4925236f,89eda236,bd1bf5d7,e500c47,d4996451,d57c963c,4f125ff,c9ef8e6e,aac8e7aa) +,S(ed4ea751,a45ac44a,33e6f2f1,50392fe8,a12a8c4c,2233ca3e,321800f8,f408c256,9c7e9da7,4a2b349a,ae9266d4,d04d912c,a296d963,98ace64a,caf40634,970ffef2) +,S(9b06fd38,fdf963c2,830ccfcd,1df08a73,84a74b02,fa79a87c,96decd0a,3cfbf5d3,e866461c,e7ee6015,aec4ba3c,d4dad0d6,3ede27a3,2a1b20d4,eb29de00,6b226416) +,S(df11481e,efc1cd1a,97e13f1e,e0702056,c2b7894b,de1a44ef,5166e521,1af6ae75,a1411856,35429fa1,ebee1f24,de73e2cd,80c76661,717abad4,a8354aba,222b7e02) +,S(f8328d01,b1f3fb3,d07bc65d,e807c619,46e4df15,a518a845,23da032d,12a3c9f5,e29dbc74,7746d478,9ca9a6d0,b5ca9ebe,de174914,7a709df0,d5e0f100,cdca6216) +,S(3e3afbbd,b7fc2301,f9a13590,acc4fb5,ff0d1b7a,92e09e46,1fcf0093,4d3fff86,1fc7f7e1,b9821c51,bd44c8d0,9619aeb1,a5c2af1c,3ec28742,f0f26212,6eb6ebd6) +,S(aad17eea,68348015,9cc19f8b,69d335b4,78ede880,4c551660,74d1ea9b,1ff86125,e993b8c,ebab89db,67cf45bf,361ee910,dfa1749d,26b488e8,ed6cf9df,9d365257) +,S(213fbb19,5515c686,fe8f44b6,fe26a178,286442b8,fb29072d,37ce9972,910ea35c,497b951b,7df7f4bb,2d0ee577,19876203,d68c3958,62781362,18e8969d,ed52a852) +,S(cd7f72a5,78ed819e,3e93321e,c8b8c7a7,eba91b47,92821a2,92493790,370120fa,a3d51c70,99280f2c,fa2c537d,991077e6,ca4eac91,93e97ce2,d8ff5fcf,45bb1148) +,S(70a360c7,a72939,f9a3a28a,1d1a9e2f,8240aec1,878f9b19,68923e0,b67f3b51,a1d9c07c,53a4dbc2,2ea4ae36,b6a0bb8c,dd175c98,6d2f61b7,3eb05552,ad710259) +,S(544a615b,4f8e2b66,fa1dd19,99e9cbe8,eedc35fd,9cda6d33,7a076601,21b5e46a,75d26db9,c76fc8d7,65286aec,bdd9c97f,d7ba7a9a,7308e6bd,75150b35,ba8451e5) +,S(4218d79f,76a073d7,af19b265,74d5b5c8,f8b779f7,36c23c92,a01c3467,8ec8acef,6815f631,49073b5f,e65c05f4,eb284c21,87e60355,e207cbda,f2c01eb9,becfb28e) +,S(6ada2638,1a82acc1,8a333759,8635f83a,3deca5d,9e4dbb38,64a2fc1a,a269b0a9,53d356da,65f7526f,60d05f32,b8b5f841,2e77b6f8,b61533ba,e8ace229,5eda6c08) +,S(5fa99d01,d9f29a59,75518e98,7c382c41,8bd46ea4,7813f579,3366618e,e118b09e,d479d238,38deb81b,dcce381e,4982652a,f15f6602,dbec34cd,6fc0e82d,33fc3b39) +,S(cc6ed576,34cc67ab,6ea676d7,2fef96d3,bbb4a00,b2b7fe44,12d0195b,44d76f6e,b15c15a,d50999b8,aac9e5f1,5834284f,dd801b0d,da4be94,f520af62,f7bf820c) +,S(e4cd0fdb,5323ea3f,deb10bf8,90a26b6d,ff447679,d8747ddf,ec32ee3a,40ffceb4,fbdb40c8,ba9c4357,656af718,db7629c4,e2caa87b,c41f8c48,d5d86a30,d220e1f5) +,S(77fd722a,ba51b929,ac1e3ca1,a2346833,b3c1fec,7a756568,6afb7cd8,9b88a8af,d02519e1,d6e9cb4,4f378ee3,1df4bb74,6b5b555b,2c7b0691,9cf90f5b,3de98505) +,S(4d391fbc,a9dd3d47,9d3ca57b,f299f4f8,6291e523,95ff73e2,665418c2,997a41dd,5cc9ec33,b55e606b,b93e9fcd,593c43f3,e5d4a3c4,39ec6fce,1a9ebf25,b4ce899e) +,S(740bc446,17b17b84,5c99d63c,3c92fe81,2eb232f8,8161a2ba,83f3a445,73084e97,fd8ec6e,f4f9567,71717481,c1301876,fb10742a,99028c67,883254af,f8355464) +,S(c258c21c,adeb8453,5bafd8d6,cced030,c6aa12c4,1bdcc392,b2d10e5b,1b46e5c,7cdf037f,3e60bd7f,739d08af,a20de5f2,b73646a7,5ec3e2af,4cb62139,5d171d31) +,S(8fc88069,733a4d5d,bd80261,c0c9ca65,fac22a5e,1bffd768,dbeffb83,24130d44,4211a94b,f88c042d,54d37ba7,41cee691,ca5bf0a6,38f0ca95,954f4aa0,12fc946e) +,S(cf62ee1a,40321c24,7fbb0e0b,ce99d314,f5152de3,32b827c9,67a61bb9,c96ad4c,6284a7c3,e64f7b8d,4bd978c0,77dc0d22,e6137336,6410d5a1,9bb09640,1ec4a3fe) +,S(53351f8a,4d447760,ecc88443,19b69160,bb00b3ca,fd6fd9d0,6c0040e4,549f8ba7,83844e19,3816e6ff,755c148f,d5488384,c7c1cbb8,8c09ea0a,16352bcd,377f6b83) +,S(a02063ce,ee2c9191,94d0e1f6,2ace9407,baa0157a,d2e600a9,e173348a,1d06bb0c,630461b5,55653f52,da9b40ea,c4448a8b,fcdaeeb0,1007dde0,5f4c186e,9f146339) +,S(70bcbdcc,25de1764,7274f374,196608aa,c360c09e,951b9857,5a6a8d6f,e1d35660,3bd5bd9b,716359ce,80b5e719,a9e1d40c,3a1c7430,94a82b64,a90b0f4e,db461d74) +,S(df8ad77,83cfc3c0,6fbb1fe5,2a515de0,54b161c1,74155fff,13812d19,aadc8fc7,bc4090f6,416db0c,9d34ff20,a0b7cb99,af38401b,1264c604,44ec7355,80069569) +,S(2504e9cc,ef399800,5f2fff3a,452d56fa,8788df47,e05509ed,777a9d9e,540a30f9,73c506a2,c6f48efc,af7f4d2f,ab5fd833,6fd6514e,db690a51,11659f79,7390612a) +,S(24ee0724,1c920334,3a6a6fa5,68979fce,18a9ce0c,819e3a61,571141a3,5bc97025,ee642ecd,605b84e8,68940a6c,cd7f4d93,fa0c6816,2fb0bf6,ba22427c,b0bd7b8a) +,S(1ead5ce0,9665c05a,63cd8eb1,12097f35,4961e12b,25c03093,e1c6d919,7a3929b3,1800df5d,a1abcc4a,b1c32fbe,a1dd7120,7284b5f5,dee7009d,c2f4aa6d,7156fcc3) +,S(47cf2ff2,26084f8f,9020b0dc,d838874b,160c69ef,2e5e89c0,98e7891e,ea05d507,a340d3df,129b5aff,1044d0d2,c3866464,bca2efdc,8dc83516,cf8fa522,d5fb7d48) +,S(4064fb9b,8e2e4952,a218bb8e,ae474a4d,51f050a8,3f9a7e75,b04e39c4,42d6cbdd,ff9cec76,a2ef1930,92d3e02a,aaddce0d,18ea66c0,d1adfd2b,9c7886dd,b835de10) +,S(838ac8d6,e58e20d0,321e0150,ba6d401f,cdd70a40,69d980d2,22de3353,fb81904e,993a6f96,2909dbbf,7b9a6b25,14153331,b1ef24fc,e892710,5f95488a,d95614bc) +,S(a5e36d83,189d43d2,34d6ea26,ecf5943,5aedee72,bc4cf385,5fce9b5e,e202f266,78c02c3c,e25c3b89,9dd2933f,b41435a3,dfc2d1b5,e7ebbfe9,54ab1c94,79822c55) +,S(9e240cbc,d357c4d3,34f31209,58d6f7ad,5ad79e7a,9b51f572,5ddc8fe3,1fba72c9,aea34a5c,ed60a062,ec1476b7,b30b3c9c,68cc5e10,cef9f0eb,6729172c,b511f06b) +,S(ad1b0646,bb315380,5c22b91d,1ee47a26,dfc9ed5a,860be0c7,ea8daec3,b6dc2eba,ee2e61a2,2499c509,1a910c9e,ea159873,a298daa9,6d30d8bb,353b9955,92dd2083) +,S(4f7b0889,11d8f64a,550d8c7f,11ecaa6,fa12df74,a236a0cc,35cb7cd3,5b0ec72b,931af41b,d297e18,b4d9ffac,8d7f8691,29b94250,f1de1d99,3e31525c,4dbb21e3) +,S(dab2a170,e4108f3c,93fba874,70bb12fa,834d0274,35695870,d2d3523c,93d3a7ee,262f12cf,c37f010d,deaecb24,d08ce35b,293a5c6f,1746651e,7bb0b2fb,c573ae4d) +,S(3437257b,784d72e,419e5082,311b5a66,d499f687,c4bcfa31,7abc7484,a3cf39be,ce9bcf07,6da8d68e,8453fa14,3b06d7c0,f38bd0a0,76f1cec2,a49b3bda,a5c398d3) +,S(4594ae9f,82611ab5,bf786024,ea34bfaf,939fa409,34ad155c,9ed5e736,899545b,2255c7f5,62b0ba72,c3834e72,e77b478d,77d3f15e,3ee0cda4,c4b3f928,ead450f5) +,S(7337705,12db06fb,36b21fc,26b585a7,3aebb036,b22743c2,f13212dd,81898819,753ddd59,725fb0a2,561cf461,c587f3f8,848b53ed,29283c51,5150c57f,50307887) +,S(bd915814,4d2afef3,552d81c1,8c74abc,a53f4c2d,654f72e,3f2abd66,d69790ff,1af609e1,7f27e373,b42b3277,a5f4714a,31ae895b,95e0c5b9,50e7ed2d,1827763e) +,S(8a11cd4c,a51dfee2,28c1ced9,3d8870d,67782740,1f49fc73,d4ce56db,915bb557,d30ad7ee,ae9f9c97,eec27822,9f47100e,ca46b3f0,27339719,6be0f619,56f5cf23) +,S(7929b5f0,2802bfeb,6c23e688,65b0272b,cfd058d7,c0a45ea6,97d46d87,6f9bfcfd,f9bbae21,8039783d,4d1e366c,32a890c1,8c80d1cd,41f08b36,963b54e2,f4fde5d0) +,S(80df06ac,1d5b966f,8080e79b,cc9f01f3,d55f8855,9b1a58bc,f6e4c526,3bd89d01,346926d0,a59558a9,2625614b,ac010f36,5ee5c337,2660fa7e,be41aac9,41136538) +,S(74545478,9ff383f2,94a92b4f,383ad13b,ed4b01da,d20f4ac6,efc83315,be5ccaf7,36489f77,81ee77fe,de1ea165,c4cf2b5f,e8217361,9e760cea,fae777cb,c160fa9a) +,S(2c6d35bc,b213a8a0,95c11fdb,42828b7f,e562d7d5,f877de67,de62bcd8,5af8fe85,aa30e370,34094f9f,4502206c,72f23298,2e60139c,2bd35631,44dbe6b4,c75d159b) +,S(8353657a,9d934eb0,2811846a,728cd162,4b43429d,304aa7be,830da70f,b2e338db,f8400438,20ffa5d,28189c5a,ce693c8a,ed4a4caf,3aff83b1,66a9d404,74e02b71) +,S(f216e93a,54ce56d7,c7ef6336,561a6299,2177ca73,4ed867f6,fd1a8ddf,aaae494f,db0a3f13,a0fe8326,63e8fad,b11cc347,8da296b7,2f1b672,3f43ea44,acd30611) +,S(dc371221,9b5d73f9,6562baa4,b62ec7f4,2e9a3dc8,de6e7112,273d0811,a88324fb,eecc9092,ec563e96,3720acaa,929f9bb4,4f1c9015,7c3300fb,581b154f,df57c004) +,S(db34736d,48f0b4f1,e1ef6029,6ef534ae,b10f047b,4256516d,d4499072,36649475,1c29cd6c,f7a8c786,952c34c9,b93e5188,1a116966,164d0fdf,dd1be4fe,ac586bec) +,S(a248457,20468424,f355af53,62f5bcf1,971b09ed,358fbd33,78fc7297,3696add,9a703d61,37d0deb8,a3767a56,c2e24573,baffc931,bc850694,8c9a3776,26547803) +,S(bb7b806f,a9ac753b,bbeaa429,31f68d0e,3eba38b9,8fb25ed2,40c9faa3,124436c4,8f812574,a1a8bbe8,d9b682e3,d9b0150c,7c6ff9c,f9b42e64,6e5836a8,12426752) +,S(6703c476,fc28a023,f6619427,69e0f068,489344aa,74495ad5,17b096ff,c3ecf446,8b48023f,7c9ba723,584211a3,6731e14,f42699ab,522e15e,ed3ac43d,28e2a38c) +,S(bcaaf8e4,bfa4b448,fe23f3b7,a1612b82,14cf0daa,349463b7,1b44ba61,e2dbf6a2,bdff2700,e2cc953f,e9d08835,dbe8793b,b07498ec,68ccd736,d6fe1710,d5c7f404) +,S(906b4fda,1a557220,1dd5b446,d726a2e6,21794517,3ec74f7b,bfed8791,9310cd49,d753f2db,cd2c4d28,d0304bc4,78690871,cf838490,2d7f4f93,441a74d3,892653cf) +,S(9ec09783,18f7d95,5180e326,e1bfdd04,fa5369cd,eb786905,8d726ac3,9ef93d5f,9cca8057,994b1641,53ac6842,4d28fb70,ad22baf8,f894a049,d1794add,97acf205) +,S(53347906,b75440ec,28362a96,f114d2f3,6f823cbe,bb0030f3,a2de3314,12b8209d,ccb38e0,471f8abe,47ef1bf0,29550ec5,59581680,3e19034a,b06a090c,ca0c1fa0) +,S(27c53f29,61b70480,93e24b18,eb357807,a33e5411,15215337,3b98f80d,d23a4870,5a3f4b8d,e18636c4,71fc70e5,9ac8c4e4,703fa5fa,880594fe,2f3b888b,da575601) +,S(921927e3,7a115f9a,12a616ba,cf55c213,22ed51a3,e75caa5b,eaccce75,de59b68f,bdbb7c7b,c1e4c5b5,98afb590,6263d779,55884e71,17864ae2,e0636c4d,727b141) +,S(973b3c87,6eb81ffd,677c692c,ae432751,efb3e609,1a3d9ff8,dde6cd6e,3f3ea8c2,340df771,129510b,a4112c5d,adbcecd9,8684b0ae,3a9683b8,49aebdd9,57bbb0ed) +,S(20b817de,f0533767,9938ef03,69204700,d5972c32,12656ce4,205e9ac,1b01ae71,2ae2fe5b,e2daa016,1c65c352,94a0abc5,c6b1ce64,fea99c12,34a14aa8,8395707b) +,S(63e2544f,7a7e9181,7a347b7,f72eb43d,ca9fc5d6,3d931402,aa6889b4,a3c95876,e62aee6d,20d714ce,5cdf52,18bd5fae,adf49cb1,f86e6067,f91cad71,280d16b1) +,S(2f90de10,71508cf0,b3583abf,c47d762d,a69dd472,e4183421,6ac39753,e22aeb80,5371bebd,4a0dd74,625e5e02,5a118488,880f3a89,9c23e64b,76193abd,3788d389) +,S(8d423ce8,faa96d05,45e37cf7,5e5a351f,7c9359c2,ad1c48a,c9adcf29,d2ed7209,83d4b83d,ee76c401,a18df794,8a655c3f,bb9d23c1,5ac0bd6e,ea80b129,bab40f2) +,S(12fa4142,5e028aff,303111a2,9144242,a9c91d88,55b3ea3,becec110,80181efd,6b95b370,8e792b57,ac10eddb,362eb10f,ee5c3b8b,b2e37d4d,c4d2ac40,e8c3ff57) +,S(923ca95f,2a3bdd7c,d7505632,cdf2d730,ce70196f,ffd0cbbf,f8347882,8a706f73,f8b0c866,c39b6173,b383983b,210cfff3,1903afdb,cad0b2ee,3e541ab5,2746abbc) +,S(844d53e,878240e9,63e3875a,728f4704,64ce3aa1,66209776,a869e0d6,88893646,c60cd291,92bf8c1f,f98c47b4,a713bc75,1c84f9c7,19fb32b2,5b855e73,6feeaca1) +,S(43786fd6,c5471d8d,ba277ff8,1005a5df,8838299f,66636b2a,65e7ac21,f1dc98f3,d292a2c2,9c8c5bbd,8f79109c,b8706fe7,745ff884,f98c127e,9142f52a,56091b51) +,S(6d1bc28d,f5e9ecf6,38783f71,fb98a2ce,bb0dec45,8b07257c,e94736dc,ae36d7ab,c2c54192,bfd80447,86214cf7,a28108dc,e2c00e07,2321638d,a0e80023,88d549e6) +,S(6bc4ef34,7f74ce3c,2ef3f89c,c3e265d5,6c742ccc,ff84b7f6,7d8956ab,16ff86c6,eb4492fd,2d2ab04f,1276be35,c6bde62d,be26a6e4,60c5c66b,8b0fccf1,79b46408) +,S(6af45075,fa810958,d42a756e,d08d5c,d67a62b,cb33cddc,b35fa99b,bf678b38,adbb724f,e7682c7,678ffc3f,ab738666,886c7b93,84960bfd,5287ff9f,ba5bff28) +,S(eb6d8b76,36466365,a96f49cd,355b7460,939f340f,905ad1ca,a24e8a66,55260600,7ae9cefd,aea30b7f,b9ba11ff,603b81d2,41f7a187,33f95c3,3c2bae0d,7fbea3cd) +,S(bd0c2233,2fef7d3f,6738dd69,4bad4cf5,b34d5e9b,ad6500af,11e4575e,91795068,eaba5c4e,2ea0b21c,a9100b6,66823a4d,a91c4d57,3aa9d136,76f820be,9d1cf209) +,S(c5e23bbd,c5049419,3483e083,14249157,aa56d9f,804374b5,54c9879,52b7e392,4caeb8cb,f684217f,bfdcd02e,3aa4ae3f,8d59e766,855b4fe2,f7f8ac7a,6e597751) +,S(b5717631,f3aaab92,5543edb0,3d21c604,17178e98,dea38bd8,10867cf8,8c92d2a3,e74209cc,324a9ef,8b31874,524dbab4,d2bdf0ac,8f1d20e9,a02409f2,8c11942a) +,S(9c456099,49601f2f,caca85d5,d30a07e1,b2d17b7c,7b838cca,2fe4b5e9,5d305cfc,2d7a1540,a109dc7c,2841b61b,31e50078,329273c3,585c8674,674ee06e,ff04eeb6) +,S(52ae2946,c0ab36c4,a8aff175,73b6604b,e33dae5d,73228d54,98c77a51,8aa82f63,70e23d9b,66061bfa,4d03f899,b65493f2,7179f5c8,643104d,24dd771c,777f87b4) +,S(149ec3bf,55ff462d,dc1ba490,384081e9,89db9ac2,c29efdce,f4f59f0a,394adf9,59c1d88,b91db942,2ae8fbc7,ce6951c8,49642b5c,df949801,341a8bc1,789dd7af) +,S(aff89294,d66718dd,dff83534,61538da3,dbe14a09,c14d0576,8a163810,42ea7ea7,71866beb,cf5887a5,ece2352a,cbc256dd,7b615491,2a88cbb9,a924e764,4d59266b) +,S(de44f478,2cb30472,ad52d49b,303e2b41,33887eb7,38e2ab78,da7bb406,584e76fc,6798ad84,25bad82c,57c630d,7ac8ef21,b13d08fa,830edda7,eefd23c9,ce482bf5) +,S(e0bc7a73,76169366,c172c88b,e1ba02c6,b3a380eb,2894e134,5a2ec7c7,39682e61,bd147eb9,33b18fe8,dfe7c0ec,87b7e993,5ad1f7c2,ab5424d3,c23609cd,262cb2e0) +,S(5ee34f27,2af69f33,9473cfa7,a536a22,ed26139a,71cfbb11,27c7a476,32f0e6d,c1df8f96,dc0d4d82,f6a57de9,85cdefc5,1316bf10,dc3636cb,80762aea,c4e13b8c) +,S(4dca3bc1,77fa7a63,9b8c274d,fe157d7b,67296846,bed5d13a,68bce5e8,e599a0f2,8fd2b558,36fb9aeb,fdb3fd75,8f59c8e9,2fb48968,52eff2a1,d01c32c5,3d2fb234) +,S(554f6bc1,87560290,ef9ed937,3e993f54,98081034,a03484f5,bc184751,f8b48136,b454c3b7,dfd512a3,517834e0,e8c3f50f,de80863a,1e0fd83d,2064a6bc,a172b26a) +,S(95a81b50,a6c0879a,6a960b2e,162dc22e,955af87c,b539eba4,20b2825b,85592ad4,be55e8c4,dfed22d1,f5872b75,f38aa4c3,7c7d5086,f67011af,2651ab0c,c45c712) +,S(907b6d53,9e496a6e,358d14d3,431f5582,44a4f8a6,ea291d85,3efb8be7,477e5c4d,b3d3d694,3853bb9e,67817fad,1dd7d1d5,ff33e200,2922665c,21f872e4,77150eac) +,S(8527c81b,b342576f,67737cb2,33f17605,c8884c64,6b7a8357,5bcccc09,c7f17aa6,b6d675bc,db8d5249,57b1930c,145777f1,9cd47b9d,33386a64,5044e8f5,877604fc) +,S(8e1e3a7d,5a320d92,4ea13436,f906e9b,bc6bf83a,819b9e9f,913974e2,efab00ec,d94f03ab,d81d5f25,c323dad4,ea792c50,792e07c,79281c4a,ff309e0f,f418b0b9) +,S(fc02cb93,be1f4a8d,9acbd86,bc68cb6a,dd0d8949,5cc7434d,72cfab75,519a8d76,71f7a532,729e45c5,98a6ceb0,885a256e,ffe4cd9d,967e84e9,8b73c772,7e4d4f94) +,S(cf981302,a2858b3b,b1ac44db,4f08751b,ff76357f,9b2ed6af,80a289b1,be6e7b8,237e9c12,bf23666e,70205ded,984f1fa2,f1a8a828,b88847ba,d3a1be88,8d73b528) +,S(3e6e45dc,1408e920,3ca74cbb,ac8d50cf,71718d1e,ba05228,e97dc4be,a8231d82,2e3fdb7a,2ad3a805,f61f287c,a7ef86df,961009e1,f0fde8a3,14774677,3b1ef848) +,S(bb047795,7581471,10809088,8119b0ee,3ce1b2b2,df2668b3,749b9eda,707d4657,95a72efc,5f95f3a6,af5ad697,7dbeeed2,8d6ee55b,fce29bc1,bc439168,75c67587) +,S(4e015d42,b8b02c10,4aba3103,4ce04cb5,fe356d,81291939,3f5d1b9,68dd6748,d35b20e7,dfbf6d71,d94e96ff,c9267422,746419fa,811bcd2d,d3942c00,70a870fb) +,S(609a4c57,90c8b210,9eb7391b,d68d078e,70850af2,410df867,92a37459,24a49daf,f3a6327e,bc965b3a,581cb14e,78a4390b,14431d00,d1f34e8a,e35502f,bad0e98) +,S(ccbe0736,2a955ac0,4e1b7558,a17ecf61,b9ea0761,305b4aa1,780bab03,a27fc730,f96b7c16,6c98a1d7,65ea4e3a,e0643298,d981a012,4b03fabb,71fc47f9,1c92cd3b) +,S(6ed60282,85df2302,e6b10ef9,4e11ce0,9143c569,a84388c3,e2151c69,c3ccab14,675ea3b0,687a3b78,e8fc7564,9665e62f,e91b0d1c,25852608,1d591ea5,1ad196e7) +,S(bdff05cb,a0cca14c,55b3c592,38515532,70806177,443dac7f,4c07ff69,cb49d8e0,5149ad04,86cd8ecc,274a7239,833910f,f09d7993,aaac9798,1dd106da,6e0c9a50) +,S(64ce68c,cc70325b,22be5366,579aed1c,af9f6e55,c2c7386e,ed579911,4e2bff2b,f8c89976,3c8a9a0e,ef61dddf,2213cbba,11bf1be1,eab6506b,d596aede,45cb1cae) +,S(cd42e46f,489150f3,dcaf3c06,e1e5a2f6,b40de7f5,626107ed,af01471a,18ab2bb4,d9c250dd,d2026967,b4c921f4,e253a788,17f11f89,f679e5c2,81dfda0a,1bcfada) +,S(f7c29007,e7145420,e2da7e8e,3ec4daea,5e9b121c,35c4ec43,59d55f01,89619735,66451406,4dbda93e,637735a5,6a06ddb0,5ea2933a,36cfa3dd,c2ba4a33,eba493b7) +,S(6339c1e1,d4944856,26b3d221,4715835c,377609d2,d91150f2,c5c1f679,5e93b7a8,efbe6dea,47b4e8f7,ff55a20d,8facb8c1,d10a6960,98762d95,be532fc7,3ed59b28) +,S(fccf1f13,7d22ed06,5f4bbcf1,e9f4eb86,7b1ec78,d9f40b4d,aaf58014,3489b37b,a5a77c2f,ef9facc3,881559d9,d9756b4d,6646beaf,21b3511c,64a4b550,7dbf3e10) +,S(da97ff10,6f284ec,f123b554,17bc8f5c,a13434d7,58ec3d4e,935c1600,404f62d2,65c2168b,4900330d,f2f1e3d1,cd8ab92a,4f308222,881a493c,f8b301cc,af12e168) +,S(6676a4d5,428a24ce,36957556,c1b44593,343ab6f2,21f0b9bd,dce2d898,333e4fd8,45bddca4,b9332535,f5f4d3d8,3467d793,b7afcf58,45a19593,854b1c5f,3daf7b41) +,S(791e6abe,cf461420,54a22c7c,d3deea41,53396e7a,2a619ccd,30212ade,e57859ca,e4033bfd,8c55c93,ec732990,aff43ecb,e2ec07dd,17e4ec92,68a7b0cc,fadd37a1) +,S(3a7c7bb2,d5181550,a78985aa,84b73246,c66dd947,4bd7c5ae,20717f31,af0018a1,2388eb17,e03cedc5,221f732a,9e505f8a,b6c38d25,184afd14,37b52d45,fe9d7459) +,S(249a2db1,86801c96,9cbca936,101381d7,62b74017,2b454c3b,ef720bc5,413ab999,da185c41,76aa91c1,d783cf9c,85c075e8,2f708e0b,3ec930cf,f71897b5,d1d5e7a3) +,S(b25e0d53,b4e0cc09,985f21e0,c5655e50,32c0857f,b792b136,14da6885,6b4f8590,1bd17aff,6b02e3ac,e1cfac42,fd28d837,ac5b80,42b21055,d6b84e67,5d83eda2) +,S(73605368,5e83388,a869db66,f110243e,bd1073f7,517fecd6,1359eab,b7862fcd,e9c7a02d,842c5a6,fa43407,6e12d41f,e767fae5,5a75e913,d6912536,6d5f9d56) +,S(ec2f0b56,71392e97,2f8a0c9,af259d42,2e5b52da,5e70fff9,7115c961,77f1b1d9,47f7debb,adb24e59,c8571fa,98e56d32,d49e072b,4fdec818,2049498e,908183a8) +,S(7d7a8fa8,250dd43a,91a2ad2f,320e18c1,afc25595,213778e5,cbfa6058,4be2d378,6b48ba16,89627d77,2700f11,9bc20d7,fab2b07a,413f752e,2a0e111,60fea3be) +,S(316a71ce,52b6723b,a67aaa01,e26082c6,6e59490,cff4c366,5c695ff,aa6713d7,d6f1c1a0,7820c612,12b44cf6,e5cb4ba1,84152ffd,cf82e020,e95af73,d6a1cb9e) +,S(21a2a8c3,ade89ff,c32e5fc6,ead97a22,9ace8857,11db9930,ade6e72a,9fbcca51,95fd3d04,38df517b,5712aafb,432f0e35,726fd438,a2b941e4,7c56c1cf,8882e4d0) +,S(3f181867,b3ad8170,dad46c68,f513afae,1ad2cfde,41f9535,5369ec32,728f94d5,a51a94e4,e9f20eaa,d5599189,4222ab0d,91abb8f0,38b00a91,5f09df10,a2317527) +,S(e9673139,11be39bc,f11ecb9b,690ecfb4,79b7aa6d,3158ea69,7d0092f0,89f63c1e,c100cf7,6f8783e3,8de2017c,32022a0,da9be6e8,61307546,1f98142d,4903abc2) +,S(61ea8ab0,1407ac7e,547e94b0,6f3986c6,d3096d43,2bd08cd8,61554fae,9008b122,e5c55bcc,a6f713a9,76490488,e69f1cee,150167f4,283fa792,a105ca8f,f4a4d182) +,S(ad8fae7a,17457fdd,9f481554,239a3f1,49673df7,24a3a402,cab73f93,bedab9fe,53b7dfef,7966c4bb,bb01d037,12563f79,48517220,2ed375d4,d777efef,50f03e6b) +,S(8605cedf,bf7f2e54,d3f62f27,44d9d436,cdf9974c,9190020c,586818bd,b6a829d3,5af8a55f,ca2aa1a,6209fe4d,a7708ff6,e7a33df0,50e85873,d786f54b,7d946b88) +,S(bdde544b,465b9ef4,7dcffa5a,7a794cf9,6e83253c,2f22d4c8,cbcd2d6c,ff921d87,6c536367,7ba867f5,5ede7d23,81be6ab,ec97c8d,f88a1a74,14ebd6ce,8caadcd8) +,S(31882a45,e8b088dc,93eeafca,85c28f49,62a47bd2,bad916bb,fe3e7346,32fe1cdb,b3588f0,c5f86e1e,12ab833c,12e028c9,d03557a9,e02aa1a6,fd11d693,55f176bc) +,S(17be4052,45800f9e,db510b72,603b8e63,e49fadbd,62798883,b0d04f76,a5dc1202,1504ebdc,df8855ae,28f7738,e40d277d,81667e55,a2f59cb9,c9e33a4b,8d32fc54) +,S(c633bd33,bb697d2a,6bfc4e69,707f2b28,a303eaf5,a601b08c,9c56afad,cb629537,86e3f413,2d4764db,f2c06b92,461c64f3,9f3b7f78,33139eac,2b4d59b7,ba18cef8) +,S(f39e208a,f4c710f7,190881f3,a33cd113,633434b6,3b460062,570618f5,8260bb52,90c56805,807b36f6,272e2b66,4d943c33,d0171ee3,ba5cd4e2,bfe52f92,b9e2e62c) +,S(ce984394,14f29607,a54ddfb3,72db8ac5,ee4e5414,f59f73db,89360232,f4a16c54,1a4805a1,d1057b13,c72f7835,4d40ae96,bb80542d,ba5ebd7f,627cfbae,ba24e0d7) +,S(5266e79f,81107a4f,ba571af1,885d755,94504122,a15a673f,6257f633,bb0a76bb,5e7ad1d0,9cc85f12,8773ca39,77b55c20,18d38200,fb739918,d3b0fc96,a559b457) +,S(4fb9de57,63f47aa3,bf370113,c778bc8b,67507009,5126f5c1,1ffc158f,be7056e4,2cd97636,8eec7197,aa2fa34,72fe3db2,29fe0275,8789d75e,52e87553,1486f7ef) +,S(ad168024,a99eddbd,aabec840,6158cb92,4ae7bba8,d233745b,ded57407,23cc0226,ab105015,968e4673,8ede230b,b53c4be6,9ee6386b,91ee03cb,a21f7040,f14095de) +,S(55829ae,f7cc555b,733aea9b,f1b307d,277923ba,148ba8d5,5a7c8001,73719cb8,6a7b2eeb,9c9b9585,5403b47d,6c60a7bc,382969f6,c4bafd3d,3c4f09a7,63267689) +,S(2119f233,8efa4e0e,2204bbe7,84e9de01,4e1e4c43,450d5910,960ebc38,6b78b935,9f219dc3,778544c6,7a337db9,69c513a8,52acb469,34a71957,11c36913,b8e7bb93) +,S(11fc3e1b,9491be9,d7de53ca,a2558d36,c5227498,3f5c1b32,57f0fe5b,b1295bbf,1c69c74d,ecb05933,6aa6b6c0,231105ad,91b8d188,d109ced6,2198c528,844cf6f5) +,S(34b6ce6f,ea6e5143,95091bc8,67e606ad,5e64ac18,99085821,80abea3b,dfd2a908,5b6ed904,f4bdbef3,fb9e41a1,32c7a313,28e9f325,4e180694,40339642,2bccad6d) +,S(6c225dca,b45768ab,3b1c567d,97ef745c,b326fb3d,2db6ae2e,22a40c95,3610a84c,7e85d752,2180fd8c,ebf63cfa,2d843bdb,912fe042,d00ea29c,d72fd768,402a1084) +,S(5f776e5,41dd95c2,32fcc757,14b688b,cbfd2020,e7043e3,2a707144,5a8ed7d7,3b8c5c78,fc6d8354,3a09de0b,9a64d529,c116803f,6d0d62ff,2a20b369,8b273c0e) +,S(3e26d8c5,531543ef,5e37eaa6,3244faa5,d5371e48,16fd94e9,b8bbd932,891b0e23,30a09d1b,43a4a036,ee1a53bc,6c3ebfa,a5b4c70f,6d8af4b0,c70faabe,b79bb65) +,S(c634da91,c17a3568,aaa970ac,26553e45,6be8afb,ee12c85e,8aa0dabf,f18b36af,649681ba,e1390a7b,d05d34bd,98d87ed4,16eb9f99,c606f46d,6d522019,f1a925f0) +,S(ed335d6,4d643598,c6eab4ee,fc242d7e,4e8361b3,ac8d747e,806962df,2e7e9a7e,f0e7c3f8,19c97aa7,2334b690,98f28bee,5a1552e7,f578d5e1,eb56b600,fcaa93e7) +,S(d37f8c09,411359ec,3f63dbf2,2870ffb2,d10be164,7553f28e,d72d9585,1e521581,da7cbfb9,ba3ae036,72df1be7,56d3c9be,831467d4,cbe3b51a,191e47b4,9d1f1922) +,S(ee3121d7,c7475ea5,c3aae061,1c6b420c,5b32d05,ee86f919,43c3352e,7515c360,703b2e37,97bea3b2,9b447411,dad7b12d,b809164a,ba690281,b76f245b,bc4c1686) +,S(7b42c9bc,5c2b114e,a0ef1a59,492a6132,3f5c7290,427572b5,fe98017d,1987a12b,5cf76382,e85b73d7,56fcd92a,99242725,dd57edde,30ddd9c9,dc7dcd95,260d27da) +,S(9af5ff9a,b13a53a2,93f9b6b9,a0f925d4,7c8f9b84,5f6933c3,ad074922,f8a555cd,ca93ec9e,89954734,c8aa13fa,5908c855,13a9ec19,6417042c,a336f1fa,be5d4153) +,S(5ebfa274,1a2df1b,6e1d591,f70983dc,a5b2b850,856bd7b6,59c81877,3848dacc,b0adcb9,6ac23c64,2904c15e,822991e8,46e12411,f472e0fb,5fb69fc7,a0f7ad18) +,S(93b58d08,465e5eac,8518382b,c1f6ebec,daeb486,7da65dd3,ac125e6c,fcc81e6f,54be8918,c3151784,d74646c3,6bca9c8b,8a50b8d8,63b1db8c,f4fb3ef5,1a24a4e7) +,S(a28a579b,4fb46474,ffc943ee,32b40664,5bc86899,edaa111,32c31f1d,cb6fc858,40ee91a3,d5625648,196605f,f650ab7,6e059b14,faef6396,46c9b153,9af14670) +,S(e987909,781d5560,b71f6029,750d20ad,add7cb47,f90c9078,e8de5f2a,d606ac9a,7121ac3,4c80dacf,ffea346f,5c5e1a81,c55318de,ac8cc907,10274f18,ee9819d2) +,S(291ff11d,158780d7,fc0ee62a,e395456e,5c84bca9,75e84a7d,1d2b3bf4,61e1dcef,c5c8cbe8,abb6854b,670298ce,8224a945,71e79e37,9335acee,c230366a,53ec2616) +,S(4adb1390,9e28b53e,42bd9e57,373aa131,efda7d16,b8dffd82,4bd9803e,fde315b4,66d7eb12,b827ae7c,970a796e,5c0c99b1,24eb7002,4fde63ee,fa2ecdf5,d4b1456a) +,S(55e25f4b,95240efd,e3c7e47b,2254601e,1707a8d,e40bf384,6b3a7024,da7591ff,e9f2257b,7e98606d,743000b3,72dcba0b,e4491ae3,22e82266,4b1e2b5b,78c830c2) +,S(d24e7493,e41dfc32,be7a6d8d,5463834d,388ddd01,8c213ad8,aa3005b1,fa747eca,e73a8216,a51b4b16,5e9f5bee,414d49f2,6d7feba0,982b9e93,fc4b7863,48c26cac) +,S(a510b252,6d015da1,cb7e4600,3ed61af7,caa03562,19227d9,ed8eea13,61fd8b2a,3d98c2a0,9015abba,63eb15a2,80c6c479,eaa2b2f2,a0011adc,3565d2f3,8abea726) +,S(c04d04d8,127500d5,15bac63c,ca55d76c,8404cde6,fe681f4f,6b8c2b48,87b314d1,cb3fcd77,9fb80eba,99cdb937,31bc0814,e3cd4458,df7fee1b,98c4067a,db4d9d59) +,S(ec9c7c61,146c3357,ba851e69,18ffa770,6b5dd2ff,a789245a,df70f90,eebd0649,9a09d3fa,10ca6923,c068803c,e9161923,b36b0709,b12627a4,dafd0e3d,3d186170) +,S(95df5d3f,5d504c45,f4b7e840,3805e54b,b2598511,2ca53064,baced58,a0488cc,a43dcd85,89b85305,b07d1ef3,23eb305a,35079cdb,743dcc21,c7a6ad48,4ce8ecc6) +,S(b8eece0c,3a950c52,c46bf50e,b5dff949,2fc8ec4d,6bb19651,c72d447,633a6d16,74e942a3,1b75bf83,ba6c1460,d969a2c2,4a49572c,ce3fb84b,8d0b08e1,d5583744) +,S(599ece16,d1f69c22,53deac51,75121ea0,c82b2d77,c5e2c0ba,520bf1c,72dd09a2,48a10c3,25af3d0e,dd8e2e72,5d7baf48,b2fdb672,66423859,9f73d231,a911bb14) +,S(b8ba5a06,8d9458a9,6e2fdfaa,961acd80,7fff711b,bba4e3bc,35a5fcdf,e82ab32,8035ab22,7554582e,9038845,44d32a53,c2d82417,6e2ac7e3,2d44cecc,50f8ca4d) +,S(b6846be1,cccd7865,e56d3d7a,f1b1fff3,ae806380,a1710c50,8c35ed17,227e65f4,bbdb7025,32945f0a,de989f52,68230763,361ee9d4,9c0e6241,77fe6fb6,cbc7201) +,S(a1ffa65e,69872bbe,ed009e53,f846d9a1,20dd31b3,a11932ff,c3580ff8,da9d0fe,aa32e580,d5763bf9,dd054999,68b6ba6f,31c210cb,26846ada,dc0045b1,ceda215b) +,S(9b38176d,67091c7d,f448c707,80cef08a,2ad73cd6,de65eebe,b62f9a21,730d0fa2,22d26b19,c0fb9b7e,f1ec86ba,b8ff5996,64a45988,190499d2,6fb604ed,4e609207) +,S(bc2ccc9e,209dc64f,bb132553,bdab43fa,accfa4d,7deac898,1fa52841,35fd0cd2,47366adc,176ff88b,674931b,a8791fbd,350d1d13,71891343,74d58305,a55ccfa6) +,S(1ce000a1,65452a62,5d036f51,4fbd2a30,d4518734,e7f93aa6,7a7dcd7,24730f6a,e09103fb,5b90f0ed,54b34baf,3e3d5bf3,fa288acd,b6b0ec19,ee3062af,35c6811d) +,S(846c569a,887d64c0,e8fb26a4,49c62447,4cc63d68,4da0909d,63dcdd0a,dde0031f,547753,132c9f98,d7facf93,d4ff0416,7dcbe44,77987be4,7df408c6,fa12c07e) +,S(6365a8f7,28656e23,c9c58518,5686b18b,701f7b1f,54c84b82,f5c81ea5,74fe59e9,8c7cddfd,a992e53c,ad151d46,c9cc0b3d,be27f2b5,7ffcc23f,c8d9cfa7,eb16c2eb) +,S(ed425f3c,89ae3d8d,97972619,55e8fd17,71dc5bf5,98c5237f,bd267f80,7d2939fc,4226abdc,14f7a03b,cd7285fd,19cf14f6,10c98d0,6e3c1d74,daffd602,99a3590b) +,S(cda5246c,2e1b7ebe,8d667f23,5ae310be,85a0bd7a,2e903d56,62a6dca3,971f00c7,791ef296,f8ea9b34,918534ba,318a4fea,96b2c2f3,21e6d3f6,eae8248b,29247f56) +,S(b42a84e6,ce79c1e1,bedd3f7e,596a11c2,f5017d98,386fbbc6,f906a5a3,f4eaf99e,2e722cc9,60bb3718,36ffad5e,47509fcb,cc3d0601,70b02610,823fa40e,d407e6ac) +,S(358be08c,e1e2e9bc,b43149df,58700875,be23cad0,ba8524c1,6d46a01f,ab0bcf90,f3c4fef0,70baaccc,f64fe0bf,b6f5111b,462318a6,36489b00,34f05c44,1f9d1308) +,S(3ed6435d,e5b36230,a4f74bfe,14571041,427d16f7,9b433ac9,85c9e135,6016a789,1c72e85e,142743d2,b5be677e,352d3dff,ee0dc56d,5aa56d91,4b92762,6041f26f) +,S(249f7e2c,580dda16,5940d7e2,672176f4,afb7e50,b9250610,c253d9e6,2416c910,6fc16311,49e9916e,8f70f842,3dcde36e,1630f508,79e96e48,6dc7147b,3aa9f50c) +,S(97b3b144,e8381ba3,1b0f6f0b,752cbf91,14d2d76f,6f418d4c,69f0de6e,c89cc85f,67c9bd30,33b720b5,a1449257,f1de85a,f7282260,9e411dc2,1820444d,c988724f) +,S(3b38459e,868b2912,51d0fcaf,41245087,80610d0e,395e9b68,cfaeef1a,5f395a85,be5180dd,bfde7454,c78cc9a2,b77245eb,91ec4c25,9b17e3a3,c3e8e49d,5225ce6f) +,S(4e6f1790,9f33e08a,914974f4,48270fd0,74fb35d1,49a017ef,c54fe21c,96163ebf,fb6578f,6f47156,5b6d3491,3a9a5e4c,dcd2880,d774e6a3,e56cd078,e6b9fd15) +,S(9141ac63,e408937d,7425a0a6,70a2565e,72e301b7,75619a92,18db32da,315c75de,d35e290a,56bec3e1,3cc55e72,12aa57db,f10b34df,bd7847a0,5de91c4f,546d6045) +,S(ec9b73ca,72052751,565c006a,4fc9200,337c9162,88eddaa2,e212c611,395ba578,37e11ef5,c1a22e77,38d5f3ab,4c74e2f1,ba192275,4c4e7905,1b15010f,3c224887) +,S(56fc14e1,bb1a928f,ce0430b1,feaac6e5,aef50208,be530d7e,f0f5b81a,ce1caca3,4e5a6084,f8680d07,9f3101d7,a80f452b,b05e597c,c38d9865,f6d7f307,90209001) +,S(aecd741b,2cebd4cc,82b7c362,70f1b22c,cf3997c3,f180e1e4,d803a89a,657df731,5f104b33,6c0df49f,1936356e,8dd09b3a,256bd317,afffd4d,8ccc57e7,f90b8e21) +,S(516267af,54f83d52,24a22b9a,4309e9eb,9b5c1072,31c3ff0d,418f8bd4,15836cb8,4187389b,9e062c17,f4ef6fd5,4b6b8439,cc37e85d,8a3db13d,86592098,7befe4fc) +,S(68bbeabd,8d3f4473,f91a0368,7383b4ff,6e7658dc,6e1287e7,f258f543,2466fed5,cf63c80f,ac99b42,7a67f7cc,d978d5b0,8656fc7a,6a383c27,48635ad,97049c4) +,S(20ce22ab,69467e9b,51d59213,8b52c1e4,49ba908f,909658a2,b90dbeb5,b07b67be,562da07d,9f4173f6,9232d17e,49ba2c17,31649ff1,4a9dcc3e,68f20dd2,de97ce2d) +,S(b5b7d6d3,a4a59f92,3afc1ec6,a5215786,9c86cd90,7ff80b07,d04160ac,6e798f78,d05e32c9,775125ee,dbc55af,4d1597b4,45b5eaba,5bbf6dbc,b8ef3e79,fe9d4de5) +,S(955bd489,7a4280b1,5de3d848,f75f6dba,3967e593,6b077d4a,cdc4c30a,33f45df4,4bbeb7cb,5d79991c,ef3b9fe5,bf25dc01,6448261b,44b156b2,749f9734,3d453a02) +,S(9f60e951,98e7370e,b3c02fe1,8bf53735,50fee849,9c876211,201e47f8,660a5a98,6395aaa9,33ed5866,a02570f5,9321d89b,c865a7ea,62e28a6a,3a62c4fa,4cd613cc) +,S(7081f0b6,f5923d0d,be4041c7,589a7100,9c2b6459,a255468,999f2d4e,50d776ee,9cb60005,e067019e,93aec9c9,1bf28b4a,27f3f0a3,288c4758,403a9ff7,668f9fc9) +,S(e290180,d8d5a7c,1b0e982a,bd63ef9a,b607d605,87e663ff,fb0ab73b,3369561f,462776fe,77289a4a,250ccc7b,80b54e51,993f741a,14299f78,61b88d17,d4d924f7) +,S(8aea7328,b285bd5e,b63d5939,c5c8c6b4,ac254cdf,c42cb521,2f4c5643,58598bfb,b682da77,4fc55ff,eb4a559a,8ca0580a,13819aee,990d9e22,8e0192f8,9568ec2) +,S(e9d108b2,54b24b57,ea4fc287,45865c09,27302d7a,1dfe90fe,b3a17906,2f256e68,fff7274,6d29d9fb,e7b0fc0b,1af14365,8d0ba69a,a45a72c4,8f67b939,aba128d0) +,S(9ea1f93d,1f317e61,786d4909,429e4cc8,b81f2914,17d28913,6d886b86,adba0fee,231dff25,d3b0b351,6e63ba6a,afdee115,975a5c00,b95936e4,5d9c6be4,6980ec0a) +,S(e8a12415,e5fabbee,7cd687c,43e8d530,80701d01,85980c5,7181b68d,481e7a0d,dd69839c,e263e746,99462f18,58745727,ac53bf2,2e0385dd,5d6a21b9,922f62c6) +,S(e287f3ba,ae486145,a1c2e04c,7f6d5c1a,ed796840,27636cef,b4446ff3,6c3def2,9395413d,401db0ea,4ec53bae,ff215580,5bcfbe8e,7e6bd6e0,be5b9fbf,9d968c63) +,S(8f99f79c,ec205ab0,e79756eb,a14c8e7d,fb23e232,6ab77927,641960dd,56ad1194,805953e6,15cc6d6e,2f61b0da,389b24bc,89cbff4f,c9f0ade8,c5dfff99,e819a2ea) +,S(9c87ab6a,8cb723ad,e0cdfb59,e5a1ff6d,3adb63d4,7af33e14,c6b3c343,c766f586,74798122,7a3bd79e,7d5012cb,bc81cff7,6a3699e3,75874200,4edc6e8a,a965bb19) +,S(c52c8608,6f245de4,baa10b7,fb512ff4,6042bd50,7d88bc3e,c75ec75d,d99dc00a,e8c2813a,bae8b8bd,9c8762d8,37d42fe7,85e5406c,5fb5cc2,ca05ae03,4ab3f844) +,S(b6286e1,94d5be25,9a79c6b6,ebd634ac,dd7c3fd,66ab0de,d8d4e160,ba8dafb2,72ec2ea8,ee0bf270,777cdf7b,47991524,27eaaf24,2a0334aa,18b3855a,899b5910) +,S(df7b56f2,45f2f8b,7213792e,370ca89a,45c648a0,5f3ba4ce,8c5d6d34,2f086b6d,386ced48,3850bdcf,4209fc90,d039b96d,39ac18cd,5d97ecb4,3df23ae6,1596ab4a) +,S(2acf4c9f,4cd42357,3e9110ed,a64aab59,459b2b37,675e420f,48ecd068,6d46a3b0,5f1a5829,aea5e981,cddff9bb,d2521ffa,58b37baf,9544c0cf,f622857d,5a2cc579) +,S(bfaf95aa,eb55d378,7210033d,b352372e,6a64f2ac,a420b877,586b684d,9cd33f36,7d22bb13,bb61e76e,2a2fce3e,bbf9ec83,78c6ed2e,5f143b0f,60803cda,b0a3d8ee) +,S(78cdd360,4eaba890,da793c9e,f589cca5,faacb6a9,14a29f6a,6add7d8d,74f31421,ed7e9c15,5b801d1,16b0d60e,fb5e2287,76430c8e,faff4bfd,327a38dc,ea19a634) +,S(f2ebcfa9,50a391f,c7b97713,566a8a49,edb8944c,7cb68276,d9843c22,8d213d2e,b1c7630d,956f9f13,c53a180c,569d534f,9513d16,4f3dd900,bb3f5e78,b0fa4eaa) +,S(43cd60d,83bfcbb3,cc19943,34552eae,857593aa,e8158adf,ca850b9d,1aaf4c2b,ff64bab1,f721e6b,9e23d659,224998cd,996230c,4fccd302,47e9e110,607e8f62) +,S(41925ce0,c01a49dd,7be0c3a3,b27f13b7,2168dfb,a5ec1315,f9209709,f38ea93c,5346b95e,cb6a0ac0,1a2f49c1,9b02d9a9,a725a676,f144ac26,c48f0d8b,94f77110) +,S(fd231210,6c762d82,a851bb47,8d0ee0d8,fa1c232,7e26feee,3e6ece5b,323877e5,39763316,f5a6ece8,baf748d6,234eedc1,4bf33462,1db3e66e,7eb2fab4,f96fa162) +,S(fe3e8a98,cb7abe1f,36a54a0a,a3d1c9f,d5ad684b,d05cb2,6ea683d8,f4556ef7,7d220677,34d80663,a1ff05f9,bad5a648,837b4475,abe0746c,3fa940e0,4f1223bf) +,S(e602a764,ae0d1bdc,4c3f9b1d,271717db,38232e94,84181ab6,9ba8eb73,4f5ec5dd,ad1038aa,26268b10,2c1f951,6b77b482,8bc9bef5,7b483450,d513e6f4,cc916fdb) +,S(871895cf,adb772d9,719cd962,5dd391b,3218142d,364764e,eb0bc6ff,8d81e086,49a09064,f7916421,9d51ef56,53861350,dda2c9ca,4e6269cb,555c8996,3f37722e) +,S(e229d56e,77755805,c814ec27,8fbcc56e,8ed4ec4f,4c14907f,4ab3a0e0,fa7e4b4,cdb07ebd,66d68055,bed7a46b,63ceaea8,6d42659a,ae6ebce5,795ab908,14e48af2) +,S(5535f85,6b0cc868,88ca3118,9d20d68f,471066af,a70addd6,ca4a9537,68bbf8ed,8655c844,245e2a67,8427822f,ac3206a1,f6f52953,4f7a6c29,6ab4825b,770ac5e8) +,S(10657905,4b37ce9a,e240d78e,3d1066c,b8c48d21,fb7130c,dd35ee5,24767ad6,52836b7a,508a6190,28eb013,c0355f70,4393a5af,e133abe2,e8b81dd4,83e125f5) +,S(3366b207,2521d023,99e9e311,e3447778,e796db0c,87428e4b,e6cb0ff3,a0868c1c,ec6022a4,22a8dd2c,a7f2f15d,39998394,ac908c64,74a34ae3,255a28d,4311638c) +,S(4a789396,339be46c,73608a1f,59fdb41e,91bd0164,66417332,f2630316,b5953ca,d45b657f,615aad45,809c0af8,4eb8618d,6e6e0346,ae1ba101,3a123ef9,e5b4c442) +,S(ee4f2484,fa0a9c7f,dc0c64ad,4df60628,d75d70b5,7ab9f0fc,ae9947e3,89f1302e,52b77894,d8ecdfae,df7dbd0d,8dab74a3,4092073f,47a543cb,f81ebf42,9f80170) +,S(4095b5c,163f226f,7937fbb8,3035f6a3,9aa9b760,c5086c0e,af9959fe,25f30a1f,1ce180ac,65554d4e,e9b14595,5c059f64,6a15d032,930465aa,d5547c58,5dff7ab) +,S(6a225d3b,67d9d42,412ecd9f,d018bfbb,7a36a59c,d2f53494,3d6cc62f,b0436a24,29b3d63d,9352e0cc,aa23c91e,e300592f,cc698abc,c73c48da,6a384f99,ea7c2701) +,S(23efaa41,e5f9bbbf,c6953a89,e3f4b9e2,17249254,94f2057c,f46d0c8d,71d36e93,cc89a7b1,7f2671a2,5bd7075,5c269219,88cea3a1,1315b86e,f218e493,7cdb789d) +,S(e30c1e97,f7f99957,58d591bd,6a398ae2,f972176b,f637c66a,258ad081,4f3ae55f,1a70cb6c,3e66e925,fc1edb4d,4d29bd89,3e8e4755,6825adf3,11e64b01,866509e1) +,S(be64d782,acd416d5,2b612f53,4601876b,f05aac91,13c8d265,b57ca32b,df08e278,7a41844a,43e837c9,293b81c8,73cb504f,bff8f15f,5a21e61f,9466acd5,87ad7e4a) +,S(caac02be,e009a61c,672b4c2,82fdf2f0,73ada0b8,d4a45450,76d24c38,f23822ac,7d93e4bb,a040f4e2,23acc304,75c455e5,72a9a64f,c66108ea,500ca62f,9216d06f) +,S(da2539b0,3a9cea94,9209321e,e1719b3,c1ef94c3,86d0e860,fc0ec1e7,684ccb3,e85e1387,bef1cea2,f67f601f,8067e9a4,b1b1306f,6a33df15,7795cbf0,ce3cf9f5) +,S(bf2ee552,5c965248,64bc1bd8,ad9cfba6,4ce4fe7b,be9b4198,700e936,799cc0ed,889dea1d,b1298a24,a11529bc,30339023,5df42398,e760304f,6343030f,6058f18e) +,S(b3e4b141,cb0c3662,9fe4c9ff,7a42c635,1130602a,720a6264,30b967c1,47abde36,f66f7118,c38a828d,8c4ff83e,5d6affeb,eff979bb,2e419f56,a7d7c8aa,52865657) +,S(451eb24f,fad7211a,11181826,5a223649,24a25593,63c33e01,4967ba58,889475a0,889ff940,49f6c3b7,9a5bd321,1c1f33e3,4cb2f8c5,bfb141a2,1fe5419e,44565bb6) +,S(29ab658a,6a754778,2127a0b0,c4b8af33,ea4c77bf,4bc0fc53,b5d65d07,38f5984c,aab1bda9,c77d6c3b,a619f9a,d72da6be,947b8baa,b80b16d2,15ee01a2,2bf0f2c1) +,S(9b59c407,d815c224,5bbb9c0,d1da59fa,9d9e79a,c654f8ac,2879b341,297b24db,a4ef058e,e6300114,c2827811,abacc0a9,17bb35b6,9b1b76c,c761ab8d,a676d190) +,S(87d27b65,82aeb975,8b9b970e,76c3ac77,78e69a7a,52ac620f,c0564e74,a66c9fd0,3ff3b3cc,dec29337,41c83e08,7f07f41c,4d03d5c,93268b1a,cd9098e1,37511e03) +,S(fbb3684d,38958163,bd85a0ab,36bcb93b,7fe7d383,b70d8bf4,d9bba6f6,989701dc,7b07cdc6,c9c5b3bd,10dc2af7,b2606d09,759e7342,68a5d4c0,301be264,87b94504) +,S(82022ae4,ccc42b9d,223cc1ad,c66e58a5,a3ef1494,f95274ea,cc7cf79f,4314a453,c2424250,9ddf2da1,6f0481a,9b4db450,3a81805e,9a8eaa3b,b5cd0e49,15d2b695) +,S(ba5cd45b,3b51cb37,bbe475fa,e91f8975,ab4e657c,57d0801a,1dbec958,be20f901,2e0d4fc0,f8b4f58e,3407eddd,298e5324,7f9eb718,b58e4164,78e3a01e,cf41b0a5) +,S(1293c841,b316d1be,76ab4444,53e72e22,cfadaf3a,df5b6d27,7cd065d8,8a6703fc,98f58f37,40fb4988,b844ab52,3420709c,2488f144,88049749,28670aad,5133c2fa) +,S(ec9fb3c8,54bf1bda,dbb972de,a8ae36c7,987d77e4,8d8437c2,c69aa988,df5015c6,d5f35bfd,ea59e974,961d6b36,4832560c,fe425d54,bfceccb2,a00411cc,a5486d3f) +,S(2ecce03f,4cd55b69,6cbd4466,2e7d58f6,9950eaec,6adaed62,7694dcb7,2544d244,e14f30a9,7f1cf747,ead403ca,e07cc4cc,d1bc793f,de79082f,f6615e6,43f68879) +,S(ed8faf7f,a63ee520,7825e7fa,56f6ef8a,4c6ad5af,ac507c3d,77c025cf,566fb3b2,df120b8,ab21273c,a5af1040,7fa3e54d,cb0636c4,8ebf88f2,c9b046d8,83684a9d) +,S(47cb8f7b,8780dabe,c1eb456a,f30fd917,bf3fd9f,3c28e3a5,bc078c5f,14c32e4f,f8607a02,1f16aa6c,68afc948,f21ee2de,c5889d19,8978e9af,66931bd2,b4aa3203) +,S(76365ac1,fdebab2f,3b9a3ee3,8afe70ea,d4962047,1e38e5ca,cf215a24,3d762009,2cacaf72,8b4bb600,ff37d3a6,97d45fa6,5d778334,dcddc32,11fc51b5,4e53aa28) +,S(c9dd5de0,99b8e552,e1375055,63edc83,9f09311e,11868ea8,a233309e,cc27b424,4bd4d238,4e615eb7,c68f134a,eafe7d2b,38163a8b,b117f9e4,b382236b,b8f8c1a5) +,S(22b6a7ad,cc406ebc,7c1c2f96,bdbcb6f8,9a1e08b7,822e2a31,83e5932d,ad0a8961,52b86095,bab24f4f,d2ebe9a9,534af2d6,87b553e2,77536d5e,22c1eddf,ae0ac794) +,S(3ee808a7,49fa7df0,c37017fe,cab7f6be,96f06c04,b2de4d24,a331a9b6,9f81a158,3e954cef,5f1f9ecd,887dc174,64cffe5,6c4b1356,d841ed0c,1e23c1c,2439cb4a) +,S(2938bc58,8a7a4896,de69ae4,4d5b6240,aa97d302,7043048f,3e49c222,b8fa4803,f26bbe61,9fad1d90,de74826c,ba6ea02a,9ea59a1c,59cb4818,65b054d0,39ada03e) +,S(682558ec,da97c9de,4cbea34f,5df43274,94962e5c,df72b0b1,2c3977d7,e1a15451,c4c2e765,22c7013f,19253c4c,c8ec6cc6,8b543f33,5aeb620f,112824af,b9fedc6c) +,S(720d8c68,fc520da5,79f6811d,e1f3d045,cbfad09,f7f7d8c,8ba68b4,6493620,b867e8a8,4f7a6d06,4d6fd4b,8bbb31a9,7ac26c3,b485fa29,50d545e2,644c9cb5) +,S(967fb3e7,1bdf658d,356153ec,6c0463a9,994d5d2f,b3aca9e0,2a17f5eb,f4b84831,b84a65f8,861724b3,2f5d0926,ec9a84d7,e3cddfc9,c7887a09,4cf602eb,547cc83f) +,S(1d9fd25c,77ecc3de,8e5fc113,1ef13b9a,73f6ebd4,f8673d88,db2a418e,8bcedc85,54a06301,d9720fb4,bfbff58e,23fa4233,33012c9,d62bf0e4,aa6adc4e,ae8035d7) +,S(80138158,a1137af3,114dff5f,f346e339,7d04029f,9050d44a,b4cea89,f1311c0a,16f49876,e6d6ac18,c25cc712,da3afe70,bc5e9489,7c55dc36,bdd234aa,6a5a6b31) +,S(aab4021,77fa5844,e3a0906b,3107df22,33aa3418,222a8c55,c6c37933,c3ef632f,9b83208f,3ad0dced,382d5876,7a089711,9a062039,9a7fbd2f,7034e5e0,3201b092) +,S(8f3fd14c,7670f464,d9ef1e3b,3014be60,3c523237,14bbcfe1,d3612f13,fa72f10,84cb7094,7901b81f,bbfa593a,5c8606e6,862ef6f5,d71c30a4,b546c08b,4205c68e) +,S(3ba9c91c,aba3c06f,eaf4578a,33979ec8,df69a903,30cb8d88,e49953ed,4a1f4933,7048a74c,3eb66231,e8410799,591bbef,fdcff65b,b492072f,582ee47b,7a6f99cc) +,S(2f8c37d2,89dab0e0,d5152468,29f6cb61,7344c21c,29860645,5f29a9ca,dcf36a7c,37f0b64e,9d02710e,bfaf145e,69c79965,4223d093,7a834201,d2f74d40,eb508396) +,S(45e6eba2,c9209d27,b87f6050,a656fca,73fc6128,cedbd2d3,ec40ca,76dce846,a3e28d44,ba1882bb,d1c91a63,5ee22582,81386757,4e66ad90,8a062c93,bf5f16ec) +,S(a5ecd3d3,80a0cd23,4467143f,fe842d9d,82ea7d9b,98ad23cc,605fb8b1,fd767ee9,22f12823,8ea84269,3fb31fe5,b5015cf2,c9ab6c33,d167b95,9bea6e29,4f11353f) +,S(f7f45a71,c4663b0d,44369821,318374e8,51eb66a9,ff6034a2,c943ff90,174be83,3a1633cd,4ec197f5,40d3d6e3,e6a3281d,7fbdf46c,f52fa149,8acc0a43,5b46fce5) +,S(92582286,6b314e17,5af03b7d,e5717d42,52bf6c82,a55ec832,a94b274b,487cb59c,fc8e9584,736809dc,65234670,495bbc7,d6c2f52c,ddfac4a0,369f0527,4a811f68) +,S(c15fe8d3,2303d00a,2f57fa96,6ae6fa75,1d7849f1,4dd527b5,a7f76cc1,e556da4,36242bd0,4685dff1,a67c445c,50cd348b,e017c06a,e2e9cd06,7b5fb109,81c10951) +,S(49ef826,152aa60e,310372df,270bf0d4,4dd7136,7d72be5c,a464f60c,c62b5355,7cad4ba2,dc4480b,9a1c572c,77a93e16,4e369d2e,b565e165,d9c7d2cb,20c4c645) +,S(c8de09e7,538ff94f,e12c876e,163133e3,629b2f1d,ed6686a3,be4ae122,9c91a18f,b5a7f31f,e58088a1,5f1e872,d6096562,9cecb10f,1a76fe88,5eab6247,697c2635) +,S(d86b9fa4,616f43af,1d7a6cd3,d9af24a9,6b3d5119,ef764576,9461ffeb,96abb344,2388ce8e,8f931b49,9481b660,669cb8e8,56df6f72,e392e539,fb570e19,f003bcab) +,S(a2cd7618,f351e2b1,3fa7b719,30fe3041,87c4969e,4f3ec55a,7433ae6f,cbbf88,3e0b5c62,436a363b,1455711a,b310c362,29870cd3,a096324a,a8dd36b8,5d19d685) +,S(b7283cd4,e33e4a3d,7836e4f6,b39b5576,ad6c213b,ed2fab87,ab442ccf,2100b2ca,918006fb,111c2307,1821e7e3,25eab3f,9083367,4248fc,2733173e,8eabc9ea) +,S(b1fd2d2e,9707ba8d,355b123b,9ca31365,d12e1854,6efccd9a,cda40f6,48b9a769,8c37cc16,8e8f55de,f604d222,62818bfe,1a40cf3d,62dae94b,58c9ef4b,475e674e) +,S(1b0997e7,4242e85e,cb4be32,885b5a6e,cf6077c7,395d262e,7c0d2b82,cb39f90f,55b4d8d4,27cd1d8a,ed8c2d2e,f3bcf8ce,f30fae12,4c14b183,fa36923b,6b7760b3) +,S(56ffbbbc,94ac925,dea36258,8ee1bbb3,4f1dc9b8,1bfd7e69,8e055065,d98d78d6,aa73208f,46d72bdb,20af3d23,5bf1c5ba,a517fcb7,b8036208,9f8a7198,d8edc65f) +,S(c125fab0,7ddb7f9c,3426cd36,812611e8,e3908191,67228685,3533e0d9,3ad7045e,2806703f,622f654a,f6c3f118,92c1dd8f,1f235ef4,4df464b6,8ccd30f1,9b77e5ae) +,S(e7ede9db,e4e0c742,8b1fa6fe,e81e80ae,dbab85fb,f259d301,48a51423,fe7f4a17,93ab277a,9fbc5cfa,c80d32e6,5e7f50d2,e236925b,c2dba1d9,e635a56c,d33fc066) +,S(74701763,f1bd6bcc,470c3da9,d853f967,a47c1628,c48b1cc8,aa395159,a3df93f3,ab2c011c,7c5510f3,37393dd5,f05547ba,bd22237b,28ed69d8,a4b6ed55,5d8ac8ee) +,S(8d27f97,55baf71f,e6dffb8d,83784e32,a65541c,5c656576,89c900d5,e79081b8,a7176e68,f3c89601,4ae1f837,bd52227f,f308f90c,b7f31386,e5586125,1ac250ad) +,S(63e06afe,9040b531,eac0a0ff,1ac25db8,71b0db01,8e9828c1,f5798ce,accc7ea9,aaa11884,a9dbe08b,6b28fcf9,9049aed5,c911244,b2a6a13e,9300c007,f4910aa5) +,S(dc7ea1ae,27d60ab3,42d617d0,23e4b1ab,6bf8bac2,882084b5,dde95894,744ed50f,93c7d76e,2b95c5ff,bbfe97fc,754a5a3,b63efba6,e111540e,48a0291c,142f3dfa) +,S(f3aeef6d,f69cef4a,3d7dfb69,1b5aea99,36e40239,bb976253,2213bd1b,f6e842d7,57d37dda,339c7002,f87bde03,1cb1e8ae,72e5628e,f493c115,1fbd0f46,2208de5d) +,S(825bd84c,316e64ea,dc5d081,23d7272e,ae87dafb,28f69b67,816a1aca,76bec3ce,61e3a7fd,ba8370a8,6a3e3f69,d62f87f0,849a2564,97658f1d,c28ca11b,40a52251) +,S(310a2cd7,4dec5370,9d2988cd,8e9426a1,edb63b9,50ec3ba7,f5aeeb93,3856a75d,5d6d86ae,dfa5e57f,7f2b4ab3,aaec250f,e3ff1eee,3e18c228,ffa3b81f,17122c97) +,S(3471d475,62a512d0,f82355ae,f9bb6c3f,69f04db8,977ee4d8,5af582c0,1b425217,272dde70,2fe0b69e,bf86d68c,5037b425,ccd444a9,45040358,aed399eb,db9cba31) +,S(bf6d2563,bfafaf24,20a2b3df,6829aec8,aaeee4eb,ea3538d,8086807e,4c257a2f,e8077a00,7e7b496b,64f847ef,755746b8,3a2738c4,4c1e7ff1,a920ee2b,40b496d) +,S(eb33cfc4,a883f718,52fce42a,3cd876e6,d6f4d465,327223fb,bdbed425,2b9cddc4,9bd400f2,ef608bd9,904f03ad,27de86fb,eaab9137,e25de8d2,a7dada4a,1ee5453f) +,S(1317e0d0,3e5df319,fb383edf,7de95645,6b123d3d,a0d8d08f,82091d6,ecc2279f,3bf6424d,5c6f9d56,b0d22b60,706a5471,dcae4297,eacbbb25,2a9d2292,33675afd) +,S(81b09f2e,99dc43eb,eb22cf5d,c5406f30,7e00f8e4,267bb456,c99bce34,309aeb56,9d4856a4,b300eeac,42f5b1df,92e331c9,1dbc5f6f,ebf17d7c,49580180,a3361932) +,S(deea567,a32ff19,722626ba,ef4ea337,ed436d32,d23df7e,98b86644,855a62f2,5c912697,c61a17fe,8557ab3,40c1792d,4a8310ef,69e785a6,dfe66d43,9d032414) +,S(ce4ad083,5746d173,e68e02ff,a7a91b9e,96ae44b1,cee86755,ef3dc4e8,e43444e5,be86cb89,5238d4c,b553e289,1545fe84,df7cc81a,ff1c2a20,d3406f62,7f03aa7d) +,S(9a993e3d,407f1af6,e4d19a32,48a0715f,2e36384c,7d8184f6,fbbce7ff,530b077e,e4a1a52d,80a267ee,99a7bd6f,e6d96ffa,3f87001d,c543f4e2,481eebc1,301a7a7e) +,S(1f10df2d,7c7f05c8,1717ae6a,1267aa63,9247c7ef,45aafccc,6b36112,fb9bd0d8,670b265b,71e2db79,32683b0e,c967e9af,e45ad643,27cc29af,fdc783b0,58760410) +,S(70ceb257,29c9208e,7db17633,dca775de,d57d1dff,9d914a5a,bad5711,86621b53,6e016e91,b3f3455f,cde11c0e,c6d1d75a,ee9825,7e9d64a,ce7c1ec3,49df0c62) +,S(ba46e5e6,4dc882fb,dfb7921,a106060,43fb6c4,ace9f2fd,46693f7e,74694e3e,eaeb4eb9,79381ff,8a208e06,9d8c436,5868d0b3,dea049cb,f342c88,7d789acc) +,S(bba3badf,d233ad20,e4179bea,f3d7579e,a631d154,e048f7a9,e320d388,3c30d81,f2a7c70d,69dd44c7,317a0af4,e765693e,2bdfce6,8c8a51e8,ed3f9003,5e1b961d) +,S(6df6a769,d300ba68,17e58f1b,f41e0af2,57d491f,38e59283,d93750a6,36d3f8a5,63a083b3,aa51c772,eced1f31,ccdfb4d9,ebf0f492,99f12819,dad9a36d,a73a4a6f) +,S(affd131a,4b8c9a98,294c7ea8,793a5173,4fab1953,1d2fe236,1ada5c14,a7174f2c,4c3de4d9,ec4a3698,3feed322,58a9788f,3426891c,b3205708,73429671,3411be6d) +,S(2da31049,9e54b18a,19dba546,4c7c708d,d42ccac6,bc2196ee,2f0a2453,3b0abc7e,f354ac91,20ad9f8b,56d09d44,51c4d052,426981a3,f7b47e7c,bb0549f,232db974) +,S(3882ec53,cbe3f82a,e8d381ac,edb4c31f,23cdd201,eec93f2b,2dc7d0cf,92e57b61,1d34315d,e5a7bfcc,492b0046,b2740646,71a43d53,2fb0bc78,6cd80ca2,ef7e6e3a) +,S(fcab8635,6a08ab26,39bc8e4e,8e9eb5e3,46825975,2a65cb28,d559bb48,7387f9f1,af4080c0,d08f07ce,57566753,ad3570ae,bef2b38d,4ef7f54,d7b2054,c8fceb94) +,S(fd34b8bb,8a897e12,a29dcd5a,57540caa,847bf011,a1548ebe,5dd89d75,9d36bb58,51a66aac,6a56f973,57927eaa,723106e4,d19b603b,d0d5a55b,76235197,7910b2f3) +,S(1e89bd20,c0d85bf0,76c7a167,87c7d817,1d3b4660,99dd82fa,808d98e5,87d4b469,4f53a9d0,e5768760,3dedae47,574f3123,dcf11590,fbc4d353,eafc8629,3cfe882a) +,S(8a1ea7ba,66909ace,c8d7d93e,aefbaba9,ddca459e,548c448f,5fb3a957,7baa05ed,d58059e9,64f37a4,39f7655b,25018031,ea1f1fcf,8ae94f99,e09c04cf,8b7be091) +,S(e0c3780d,80d59926,96886549,af2961b0,e4451f0c,b5572cb,d7ff76d6,1d1331a1,99d9d2e6,a13c3b1f,c2fe6f1e,5230e7b3,d827ddae,bbeda551,1144f68e,4945491d) +,S(964354e6,ed23ac12,16fed36c,5daade18,8e7e1bdc,ff20e60e,f7cc8c0e,83996035,f748a09a,8563c6e8,72253198,4e6f0622,7e6a408d,3c633529,aa8bd326,3428998a) +,S(9809e729,5125c862,fc6eab86,969861c7,8bd09415,585414a1,ab1dbc3c,1afe3f9b,d8536228,bbf55568,833909f8,6fc84a58,ab0fc089,432fc05e,fd46bf63,987b9fd7) +,S(195f4d02,305e4c42,28d83bf9,4025fc7d,1d8d7173,da14bb34,abbecc4c,f01ed17,957db8b4,97aeb9cd,68152e0,23cb4467,ce13845d,3a88e21a,726b8766,7c9d637f) +,S(562eaaba,3eb17e34,efa3f726,d02490ea,b0203fe6,81b836c0,f2c44a1a,854e0b37,b4e658ed,9e664804,2d3a35a7,70ef4253,97093ba4,d7b32da5,9db56a6f,8552b1e8) +,S(84a2c5eb,1efa9014,80c57969,3302f34f,de3a602c,a4b1a28,8d79f28e,dec8282,700d51c7,88cfeed8,587261db,adcac440,c703313f,5f0da023,bdf62b36,46207cf1) +,S(83d12fcf,dbd9168,b14e7340,6c6d4b84,b93e2bad,77d4c7fb,8ba00973,3672f913,e4856e26,bf2532cf,bafc78b3,682ce9cd,3c0dd6fd,861218ee,523b5131,e7279a7b) +,S(9988a6f2,bbd9320e,fbe73bf,9f4643f8,ce64d522,9ed4b701,dfb0068e,dd5451cf,bbb352fc,bd3b2650,d0a44e4d,892f89a4,d2f431c,2abe9910,ed5a1dd1,8a882d5b) +,S(3e92183d,cfa615c7,33721d1e,2a2116b1,83017d4c,2aa70b51,60f80a10,afff0724,c7888251,f4bcbe62,f0dfe545,3a73f3d2,76a2f30a,74e57d7f,22680b39,142dc945) +,S(d9175300,fa1efe3a,4cb130fa,fb36275d,e5f85b98,5ea5844d,e7763d6c,8239492f,59138c65,e9592a0c,d9a36220,d165abfc,fbad38d8,85eeb521,c5566702,9b678b4b) +,S(6ff5825f,7c12bee6,5d17f7b4,7766e677,ed74c5b8,7a95d068,b4741129,36d933dc,661ae9de,cb7974b,2304be0a,4d2b8d80,2ea9aca8,40cd0381,31f80248,378c0edf) +,S(ed6cbeb1,a8761064,3067d580,ac024f7b,9560366b,bae875ab,15ceb116,50b1c31d,9140687e,d1e889c1,6564d60f,702e8101,1269c476,f19d7f45,19a7d66a,8617dede) +,S(5f8ccceb,32d75aa3,1096b56e,eb132230,853bf6e,38643ca4,43abe04a,8bd69164,35f72a3d,d0c81dcd,4b6ac0fa,c4f5834a,4f6576f0,21cd2d45,6bde5b7e,7995da3a) +,S(81e02085,9bb23977,522896c6,7ed66751,86580d62,f015af7c,479a6e23,982d0b7b,8791fa9e,4159f26f,b42ddc08,85d562b2,fce84a38,e2af9960,1ebdbc64,cffbb669) +,S(de226b16,dd1e5c26,9b7aba7a,d63badc8,3eb435e8,7aca85f1,41d42712,95d8f721,62e1e399,fa2ce72c,3f87dc03,de33d913,3428416a,434dd20d,84156470,1ac8423a) +,S(60a46f42,3fcd3295,fd058be5,9f7ff164,fa762995,27cbd2b1,d13a0bdd,97d140f3,9571d2de,7b789e17,e46bc31e,1a03cab5,912cb854,ccbfec8e,f4d370e,df7f1219) +,S(6fef445c,e1b4bd91,fbd3a77a,ba0c6364,80d7e759,3f42a7f5,ce089fcb,c32249c0,115fbcfb,af46db83,9ae0cae2,e581796f,f82e73d8,7d4c9a91,9a92c5e9,f4fa347d) +,S(6d37f51c,a8d2f953,a3e5f7c2,6edebe57,a9892b5c,14482a30,9145c4fe,4293522f,78e511f3,6718ad4b,de6897c,d3b90e08,cbefae5b,a884bdda,a967eee,9d5309af) +,S(c0ac827,5bd0701f,5c9cb035,eddbe996,738aa155,fe74acea,a76b0351,3d46098,e7f32a2a,188fa708,6a968490,60ce26a4,31d76231,6a65a6b7,c3e83201,f8e3cd97) +,S(9000dba8,6f895023,1fc507df,85faf4a5,822a648d,4b256b93,5d012060,50cde108,9b7ae836,94b2e9e8,bbd8383c,2f4cb03c,32331902,2f58cff1,779d83cb,654ef3fd) +,S(f1109bf4,b490bc8f,36609253,26c2a2e1,ad58dea7,463e1d9a,28aae8d6,969098a1,87431a27,9fd72c10,80230514,1c8c17ec,6e3382c2,c325b022,4a800509,5d2f24c5) +,S(4de871e5,4e9cc6f0,8c4aac8b,36d61f83,929adc8d,4f6e18b0,d1dc855,6e8c0003,25d1fcf,ca98e8a6,c75d7fce,19de16bc,805f4b70,e9a3e9c7,f0187f07,70d3bba6) +,S(a23cb675,92972bbf,3a6e89ff,94e10010,8f47b467,fd8cdc80,b66013d8,62766ece,8dc70b84,ed05c23d,a1681126,77d23aaf,cbca18cd,fd7e9dc7,cae5aeb9,47cf548c) +,S(295d0cc,e96d8f8b,91ba9dd3,be703cc5,79d3c938,a5b7c32b,bb40e149,584244e5,a6d45a06,a16bd4bf,6af0fe37,6538ae5e,745e078c,12b3ac95,2653f7ee,2b03c926) +,S(7c683b91,9d5376a6,d40b13d5,1fbd1769,6cb40bcd,efd248af,9e102545,eebabbce,3da0c9d0,3608856f,5b7ed30b,8d9f1562,197d4e5a,7c23ae39,332b1ada,adfd3f73) +,S(30f7512e,d27e249a,95e0e21f,fe22ec23,8505f39e,ca9f41ec,df5e7ef8,1c0c3189,2f5fd85e,25ea3a3a,15928651,4b473197,9e4e7f7f,56e88164,fad41e42,e4875169) +,S(9377bf8c,96584f27,324a41c7,78d6314b,1c7f842f,b4910835,431f5a76,2f514961,d599cc55,e2eddb2a,e7d7fe92,aed830d0,3cfa892d,3a89cad4,335fd6d3,69f3df42) +,S(3589ffe8,67f20495,ba3530b8,5f83c54f,28a66e78,6bff4ca2,40ac1d55,6d31e9be,98822eaa,983089e1,209c22a8,9f133dc4,d75b3a67,8fdd385b,581a9274,6d224c89) +,S(39c1b1f4,b2dadc4c,e58e0f84,1fcc6415,2c79e77a,11d53f85,999db8b8,ea3f8935,e785271a,d9df3389,6436991d,eff9da83,50bb8a42,616cd31e,6289aa84,d4256721) +,S(dcf11459,d3d62c2d,99879918,8b6118e4,b103f9da,2453b72c,ad5eeded,8b25be33,dc038c1a,af3270e6,345a92fe,7c7dddc6,a190f6c8,fae7d7b3,30a3cc72,f3a1c074) +,S(614929dd,1de9b0c1,3f4d82e5,5bd8050c,a32ff05e,6eb238f0,eed635b3,6623d1d,afe8517,c9e7f17b,a1918e3b,e8180ff6,afafdc43,1acbea6a,e0eaa75e,c4d8a24b) +,S(f4a72228,cff6da94,a45da35,5ec8adbf,ef81609f,b59a8296,a7c1b0ba,9cce743c,545200fb,de095aae,9de90f1,569c6bf,2fbcd025,3cca1e50,a25a81a6,e19cdacb) +,S(63cc4273,6caa5597,46e62fbf,5371b943,57aa164c,fc8fda65,b5fe524,e2c93f7,1fa4ef5b,c5a6413a,d6ad2bd3,4b08289,e7d8224e,450f04fe,9b47c45,ace5665) +,S(ac175228,118438a6,d2cf761,f345cb04,aa7f9d89,9678c201,6b84be2b,8eab8889,dd7df8ae,29643dac,27ab01b6,8f7765f0,832f2989,99a13396,3435a982,aa4af2b1) +,S(9dbb5eb9,637c41b6,c4a5a2f,72efab5b,823e8622,f3786c03,dd678816,9e4a0e48,772d6ba,13f4d61f,42dcde34,b8519aa2,5aa0107,89c625cd,1ba38d4e,b7d924d7) +,S(717095dd,d2fcc135,1015b586,65a0d888,4e88f599,b8c32fb1,73d5731c,3ef7dddc,1de0f109,52b470bc,fb23da3f,3c7b640b,4727326d,d18e020e,581fdfb4,7e4fa5af) +,S(5ac230d3,f829b1ea,401e3964,c14d6e9d,a32c4a68,2ccdbb2a,3319a964,265872ca,fac3c3a6,27dbf66b,c5e06a16,e319ca43,423484d8,3217ae81,51bdcfae,c9c026ea) +,S(d27ecaaf,a39f415,e1457537,b2e74e1d,382496a1,51118100,8577cf76,1f8752ef,fc480b67,46cbaf31,e3d8eb6,4121b68,d5938e84,face3ae3,5dcdfa4b,71c6c037) +,S(e08777dd,f92d99b7,c4f87d53,9fada8c5,703af619,3a088d64,77f1e20b,f2ac6cda,25a4f88a,c6cb18b2,4e96ecc5,3ce10d5c,1202c4e7,edbdc070,db05a501,29ccfee9) +,S(45ae7ab7,afe9580f,bdaa6a13,8896ab97,63bbe3e6,88054776,753a1180,699ba6ed,dc1af7ed,127fe06e,4d0d440e,3d96f43e,502f797e,fefcff1,6c46e6a8,a156f322) +,S(fd28cb3b,71c50453,b3cf509a,768b4c2e,6ae26514,5801c045,327e1de4,f4d00590,b0684f95,4867cf44,aa5081f0,8174ce21,3298a22b,257f6159,c2666518,5ce5a7b6) +,S(f226f00d,ac6f49f0,a195e4e2,23b9fba7,95681660,7671dd7a,158b3260,1c1d850d,d05d5c4f,6925a38a,b4630e53,a1986fe9,d823a08a,3691550f,10f71b65,a109ef86) +,S(3cc61613,a80d8480,87ce23a7,2b3038f8,a994e1ec,8c9c740f,f0e44e90,760337a3,8474f96a,24093af6,94221f09,f95ab010,90377f1e,52a173f5,a11dbbda,cd3c3956) +,S(721ecd89,8cd99712,bd3be233,1f57d686,633e7ed5,c95e6223,e5cdace0,ce60e736,31d294d8,6fc07cae,4a09450b,fec633c6,21dc34a1,2f7668ad,532ac192,a0ffd69f) +,S(dcbaca58,1144d9b1,b2586271,5e58fa8c,d5b1062c,ffabaa2,db0fcffa,2b2eff38,9cf7621d,339f6cff,90efbdca,5e2c6174,708b53e6,1af38689,e6d3b257,21ec7d34) +,S(ed314e93,e649ec2b,ce491938,2c7a256e,b9300b99,528e7878,3421f337,15db8b92,70a81aaf,229fef2,e2922355,e1f15465,53ea0348,e3dfb18b,c837ddcb,60744a11) +,S(6bd19bcc,7a59bc8d,19a97768,95f65ae8,5f5c7eb6,429c09,85436b8c,29c036ee,51346de3,f4f65681,bdbea28f,843545c5,31803ce7,78b5732c,bc42735,4999090) +,S(99ed1e15,6ac8dfb3,604228bb,5be088bf,ebe9d640,7ad8be15,5777c6b1,d43d9acf,75b159a4,478b4cca,80ea0ac0,472436ad,69f57e9a,f447c61,e19910c3,9d2c922) +,S(3e166f8c,793addf0,84add90f,b0303970,f00c3c3,8c36171c,c7fe0877,b2452045,91659a63,b0cd2a65,ec330f6f,b9a6cc6d,9f07e382,7b94b8da,1921aa8,1d5bbd59) +,S(fc1baad9,a3a95f38,bf8d1b16,8409d09d,a1b1a432,841f656,cf8559cd,176df936,31f7a7bd,b013b89d,5045f248,982b5b4e,e0b4d791,21b6e638,84a563f1,a08af43d) +,S(80b3ad3d,4b371eca,c903c879,53386884,3bd31843,4e9c925a,5a5a819c,ff7ff340,fb1ca8bd,aac96d84,c67ed7d3,b65fac69,108ae28,7347abfd,f20e4cdb,712af178) +,S(bad065ab,2901f727,d76c6c36,48ef7aac,2a87d49,4d727422,73cc2e4,68616af2,ae9d7902,16b5eead,4d9d69a2,eaba2972,fa925a34,13f15d92,236329c7,470a37ed) +,S(d1f046d9,d700ca6,995beb99,cc8781e5,a2fb758f,7e81bca9,bce435f9,129b8fe7,274ede5d,7eb815af,34716718,84433143,3090fc0a,46575017,fca345a0,6111a4df) +,S(db535f55,e3dca80a,58dea798,d29aa0f6,99df1761,473a7ea1,751a047a,33f0a22f,e7d8015d,931a3bca,1193df83,7e7a8f10,e61b86d6,477a37cc,ed3528ad,5b53ff6b) +,S(da1dba06,a56ef0fa,f98b1a5d,b9a4f184,8635b7e5,164cdd50,82ae691c,ca42a80,86a8383c,a69531bc,ae806621,5cb4160,8807e265,b2179508,d928e312,a210c9ff) +,S(267c26b4,d980066c,7dd4f29f,f7b54277,3b176b77,9b3588ae,ff5ec28a,9aa42e7,3390c03f,f4e5eb25,5d230053,1d93daa9,d584658c,7fa30341,42ff20b5,827161d) +,S(8024af52,4e6ff955,70c7915,208491aa,5561fd29,fee5c27d,4f3483ff,c1cbea0a,c9de78d0,498f726b,8078fc0,609e76ff,df7a8a71,7345ca16,dff1d7cb,b2dd86b0) +,S(11ff6a68,c7beb420,7f0d7dd,371cf000,dc3dc195,406882d2,77953445,6208f661,b53ddc25,254781b5,1e7cac25,c1f7e3bb,916b75ba,97a7fdbd,658c1bc0,a7b4092c) +,S(562a1083,f9a68203,39a32d7a,65a9982a,b70d6596,b00751f6,aa88c27f,36e3c929,a380ec7f,908808b,950e7d25,510ad741,66ec2061,50923364,55744acb,b1e05b51) +,S(4efe70fd,327c0d69,3b6a7598,7ef64573,e097fa5,4cf077af,68d082e2,d341c413,10d47b9b,92e0b62d,d9a8befc,3ed1db10,b110a346,8cec98a,4a1abef2,c8ce3dc7) +,S(4d64bf0c,d7f128b0,bde8576c,91e01c27,354abd98,2cd3034f,f9ac09c0,23a51f20,159e741a,f57fdffa,de7bf38e,14dbc2e7,da3c524f,3d4c8d6e,b9dc32e5,e8a5e28f) +,S(9a41634a,ff3278d6,b3ef0064,ceecefa3,466de2ac,372ae43b,c48f30b3,a187bf,eefee0f,aebb77cf,8447a193,6564498a,f409e530,f4053c3c,ad27eaa0,6aeae2a) +,S(e1804fb,9a6bde08,8efec2d,2e5c6774,2c85cffc,e78254cf,4fc4ded2,9102418b,e9cab1eb,9c1664b9,c2fe81c9,b3a665ae,447e93c3,183cde7b,fa56ab34,7efcdda7) +,S(a603744,c0247b0a,f821705a,dff44d2c,717611f0,25f9fb41,1eb62f1b,64593212,820fd32b,706a7638,4335c0a2,f8713b9a,6dcdb015,a96b9d9c,e5c19bb8,ca603b61) +,S(a6f1d9b6,895b1298,34972a2f,ec96064d,a1108ab3,aae276cb,66a620a0,495f20b0,9a95dcd0,df038394,63ff5ea8,18679c3f,7f55d7ef,83a06880,29c9d543,6dc8273c) +,S(4ac51432,7822b63,e00f920a,b6e22142,f09f30f3,bd74c418,93baf126,cdac5c48,abf5d148,77e94a46,45fda161,f9455bbe,7ff037c7,720f6a6a,d7e4bc31,a0f43885) +,S(5a2cf55b,a16eeae5,8cec588e,1b339422,48af6e37,33604475,2dabbf7a,f7fb7a9b,8f27be2d,32ff72cd,4fc5aeca,5de55a21,a45f8c1f,7163dc4f,e0c00040,e69322e2) +,S(e16f23e8,ef48c1f6,4012b27,f6ffc4ea,a876053e,30cb388f,232b3d8,db011c99,195ffd52,b0afa5c0,da95bb97,7bda614e,47ffe3ea,13429799,e8c390f,b8c56000) +,S(65367cd7,ac109f71,50de4263,cdd86e92,28923a3a,ff96945a,c1bbd163,8524fb84,cf0b9ba,fbe67dd6,4d09b170,beb4fe8c,a7687c95,68086b6,8a7834a5,1747d731) +,S(10a8840b,39443b8d,7a44228b,d6dd6647,89da489c,8e954d54,a5cef366,6bd6eac2,7ff6f76e,e4aa58de,185163ac,ef7fad13,d8cc4055,c4e10a60,7987e034,c661eff2) +,S(3fefe8a6,c01b8202,6bb9d038,c577f16,7b1db5ea,91ae80e1,528b1327,5245d6a0,a96df5a9,37e6144a,454a0b54,39d68eff,a959757a,7c9fe350,ecd58c3a,3b637b5e) +,S(ef93d77c,e4162e9,6bffb908,f5d654d0,58595fd5,d6184c62,6c3ada21,c4052b61,40088b85,c18d97c7,f59a23a8,645d169d,e535261f,6cd7466b,953fbf40,f3cd0bf5) +,S(f1789f5e,b0fa903d,485edbd8,4971b7c4,9224a2cd,519bdd40,d627bd82,b0ca4872,4c845cb6,5456457e,a2f570f4,77b1f4a1,9e36a905,adaaa8d0,5830161f,a71a9da8) +,S(d0bb1788,ebc75ed0,20c0d15b,ad4e41f0,64193890,8a8704bf,ff3cc4d4,cc9b3274,3a120ca8,95a327f4,8e687796,30666649,43ac5524,44df62fd,8f7abbff,222a7641) +,S(164ac10c,f7807ae6,4d6aab31,72110d42,2f05e461,aeb9805a,3facdf7c,dce8963a,8dcf43d4,40f1dd90,bb1d6f85,849f98fc,9b6dca26,301f3209,3f9dc59b,ea549b69) +,S(f122d965,a8e40e3a,b857dcf8,92949f0c,cd3aca29,739a2e40,9876c782,941fbe5f,4d994b9d,d9e68983,78a192d0,bb670bec,71504372,d218e257,f50d0268,da447371) +,S(ac971547,75e5a92a,1aee2cdb,f5781f09,b975fc21,816e7829,662143a4,e48870a8,249fb603,795b1742,96767945,278dc97a,53fff7dc,f8bbfede,f8943c71,43c9aa71) +,S(959ec91e,dfd5fdcc,90d3da81,1e9c870e,fb0add6e,144b3fbd,d48c485d,2d330906,1e5ec128,b707c761,b61b5ebc,653585fe,29f48975,2a4724b9,aed90a22,6257ebdc) +,S(590795d8,ed7d8232,457b2d15,76de09f0,3c6abd8d,23b5e8b7,c59ba93e,1e982859,5edd2a86,1a3c8841,60e16490,a47ab42d,a09cb123,a9d803c0,6835292d,34576708) +,S(c5372b98,7439245b,eda17853,5cad1507,48fdb4b5,bf1ba555,89714dce,a3e0ba9e,f82d0b61,7159db8c,35536a5,46b69c4c,767afc43,8b64b5a0,22e899b1,60bca987) +,S(f3d959fc,ae39cbd3,ec8d2016,630f26c4,10161128,7dcbdee1,6534f96c,e35bd38a,e1e55c1f,87602783,2ffe2f97,fe460147,da586a03,ed0ad6ce,1603cf17,ac9b3562) +,S(922bc76e,be3ce378,2be3d960,489b064d,3a28043,f8a92cf8,dd5cec2e,537fad9e,b3199f57,2508efda,230b9ea7,51a95218,116fbf5b,d1032b54,352c0995,f5122109) +,S(45c7dd40,7a6d2c95,79ab1df0,8070acca,2c461d88,dbafca12,99a38734,d9f0360d,3a17855b,5f2469ed,6c3c4e04,4dd96f4a,d076eba6,a60e8e73,2644d25e,67e85ac4) +,S(6422d998,dfd4b00a,3dd89a5b,74411c14,f1af5c9e,1f090d5b,33ee2d26,b959bacb,e2358f34,491218a1,93d8cea2,276f5b3f,376f597c,703bba09,cdbf7d89,37a3bb7) +,S(8e048214,33196d15,1a4640f1,92bf6c1e,95b1a3cf,5365a6f,535cc82c,c4b0535c,d6d2d73a,1f652c55,9f282ee5,35f55aa0,20012195,8ba94e65,b638ef50,e97c34ce) +,S(5a2b3da8,b5eb2f0f,8a25a9e1,7124613e,b8a7080f,15d42c4,a1c0709f,e64a79bb,607c791d,eb54aafe,6dc95f8e,6ef9d8a5,a6435a82,6b824cd4,4227f188,bf86e6b7) +,S(143587a3,5cf614a6,750f3d91,220abf49,c5296e00,3cdde13e,1a0d2d5c,f6cd7ccf,49bc85fa,de6cc58b,49a1af49,46c340b,15f14c7c,2e83201b,6622f2df,86a653d7) +,S(f0080c42,8737c74c,c3b2c4bc,8c9d8505,ba357b7c,ff25f1a3,ac81be19,dca93193,bdd0119f,57bf674a,7d7cfe32,7027d1c,1c6a5ad9,2ca24f0b,5cf25c39,4a3951f1) +,S(877ae373,e547fef4,36cb26b6,85d7c426,3e162dae,ac0749ce,f7748d50,5d130034,1f0af495,b750341a,ed956b79,8f268ca3,e1275e15,32e5ca8e,6f9f6df0,d4dfcf5f) +,S(a418df6f,635ca18,8c96a071,469a272f,e55f0706,b73aca0,d1df2ab9,e8e619d4,2b06cdbb,5bbb24e0,fe499a6d,aa5b5918,2e755818,694b8856,525ffcd6,2f754e3) +,S(4c213d74,bb6fbad2,25edce4f,44c7b0b4,114dc209,4eb52967,1d907637,a2aedeab,741bef9f,ccc65c15,8752c7d5,48afa9cd,402fb5fb,206c5be0,e480df73,9f970b51) +,S(a37906fd,3f94e51b,1a94073a,4890011b,2cf93bc3,bb4cdbff,ecb7056,20deb13a,60a3b0b1,c14a9b1d,184d5e55,23314b70,8a8a16dd,894aa332,f01a2d67,30e6030a) +,S(2c677eed,639eaf51,64b8ae3d,26bb4652,d6d6528e,5b85225c,5e84ec4d,4de72301,df0e4c06,d3bb7cba,17f89240,cac07c24,5fe63931,3b2d278e,aca1b9ff,7ee832a) +,S(b551f814,eaf86be5,335fc3c,5b01c001,684473b0,cfac8939,5d61df4b,7d7e77b4,a979cb35,469715f8,3033f923,b4ae1e7f,8818b6e6,11a4cbb1,bd399ce3,646664b) +,S(98d5c718,5c8c04ea,f1f61ad7,40df0c71,2cd47ba8,74141984,fba82146,ddf45c39,aca52591,5c737ec2,eaf88f5,f1ecea89,9d4a8e2c,428f5bc5,b36d2ba4,a0050804) +,S(5148e2f7,260fb652,68c10485,4fbccc62,ed1e1386,ef8fbbc9,a0b23e2b,94cefc7c,65b4dd2d,35dea498,5ba4a8cc,b1a08dbb,9e9b2716,7ca96e1,29672452,a7ca2500) +,S(12c699c1,8c9d6410,b8a40ccc,bbeb4514,a08a227c,982df1da,f91e9131,8e0e753c,7d953c42,89b55724,3469a197,50516e88,4889e968,4ed28e1,20b022d8,932fcc99) +,S(4d0d3ff8,a7734011,b36ecac3,7ebb2312,134e4479,5039ac63,9dc69d48,120a8d20,edff17a0,e0984c60,3c4f1026,197f2173,960b3d29,f0b0ea57,3979605b,144ec64) +,S(ce90b70b,3e7b7a71,a91b0985,a668336f,ccb069d9,81a9fbe4,48125061,5536e42d,8cde9d56,12e0ead7,524640ca,2875606c,d3af73e6,457f1e67,2e63a478,5569000) +,S(28c79327,6bb1879d,99fbf985,1d2fa908,d197f5b1,c83be97e,360c4931,563b2d8,c8944e40,e2f3b546,5b6b10f6,ae1cfbe3,bc0f3e3e,33d91085,7c18496c,ec643868) +,S(6e2dad06,2413cf01,804c2b6b,55b707f3,864d4c80,87d30665,9a1fea53,e7442443,9da67c64,e05d6914,30a1fba7,39c0fe36,497bc028,43441f7d,c4c035df,129430e0) +,S(bef94ac8,3763946,c434d03d,fdf437f9,19f26188,923ee54f,8715b724,8c4d20fe,d2324534,e5fc1dc6,6fb83032,e95e1406,38648473,ce7e38aa,ee7ced54,31e25db) +,S(53548c57,f1b45c1d,ca166a24,c0f2653c,9b7d8664,3d45d125,918cedaf,818e433d,cc138946,f0c207f9,c01307e2,130b13a9,c36aec5c,3cad640d,b6a93804,f10c3b44) +,S(7fd4f784,7f25c901,4e8b0e09,b8b723ed,2aee71ab,fd47988f,90f6ff40,4b5867e,ffc3debc,901f12ad,96605805,a6e402db,58eec18d,375fa976,41b5d226,74a1bd9d) +,S(444a8a7e,499789b4,49c62424,cd9046ef,d4251bf6,9cb883cc,d3dc79fa,5397dd9d,4cc5f03b,3356b020,ae8c775,708d56c2,abbe8e41,bf393f82,8f7c45e4,dbb30282) +,S(ff05c877,84d8625d,20694bd0,a4201c7a,4af8c67,8e62abc1,acb0cea8,5726bd8b,7cf0e8ce,6ac26253,cfa5a0b5,cbe984ee,a55f6aa0,90bd12d9,6ec2de7,a996eeb3) +,S(80fdf7ae,6aa31b6b,ce894ea0,83fd9274,e29f4704,88f0a7c6,79b8339a,de7e4ab2,77d7317b,16962745,6344a8b4,bd4575c6,49e066bf,bde58fe,3f46e60a,3952fb75) +,S(ac38a4c8,10a307ca,d5020a74,f0d772d7,ef20227b,7eb49589,6854aead,41879214,f49c4cbb,3c675858,416f77ca,92e229ec,d75ecfab,3415d213,c7cd2cea,1f5016ac) +,S(b3736edd,f1e10262,fd562f40,8f502f19,3a90a435,de9ab6fe,a2691155,a5f75514,818cefaf,f190353c,f01cb068,2a15ae0c,eafa2de,657ee751,4e483057,ad5a78c8) +,S(66d67e12,d100c116,b57f6d5e,ac6a85ab,e698d4f5,cfb6722f,12738b33,310dc295,9cd4fa1f,3a0c9a54,d3608ea7,9bbd7ea,685faec6,32d7318f,d4e25fea,14a05dd3) +,S(b856d75f,756ae484,daaeb651,46ecdf0a,9fa10c6b,a7b9b33e,b8747135,3eb59043,5c8542b1,2f249649,4b3a177d,8df246f2,6566da1c,727284c4,b041a7db,89cb0a38) +,S(683bfd7,3202393,458142cd,661a6543,c736b15f,b8595548,afc52951,f9c47d27,90f52a40,f5dbdf26,51429f84,ac68cd96,420387a2,8d94d746,b4163411,762de166) +,S(2feaa1f0,811e1e65,13895934,533a0941,41ee1ca7,f8975896,3d00f8a9,1b2923c2,9dd80976,b884d401,5d7701c3,5e6ee1ef,690d1820,7d06e02b,7e3a10f,e8499c1a) +,S(ce652937,da9ebaf3,a9765e9b,144d20cd,847070aa,1a968097,80e07b58,2287cda4,c1c46ffb,5f152dbe,5a9db378,29e7ae84,454e1736,eb7a1b68,1b321f3,41ba572) +,S(9a695bd9,170163fb,ab3f9738,a750bdc7,556087b6,b04dc8a8,4fd5fe68,78976211,f40ba1f3,75accd85,9a42b133,294868d9,dce262d,64d6bc27,d0c9cf12,3eaeda70) +,S(e5baaf68,bd3083ec,bfabd6d0,9e4da4c3,5908b0a1,75578ec7,18cd3223,bd34ba44,ab8651f0,365cc3c1,50476d0e,da3e86a4,5feb7f46,a9afbdbc,e4defb99,ba27fb0) +,S(2c26cea2,b507fe77,d8f3530b,b7ee1fdb,dd0c4dc8,bf63d04c,e06376d8,ffe59cc7,b344e3af,54d03eb9,b0b67151,b5845297,c30a1cc2,aefc004a,5000b69c,e630d62e) +,S(7c67539f,52af2e61,88deadaf,a07e2f6,4d53339c,7fca7a3,c2a149f5,c8bbd0e4,3f9872fd,c21c9136,4ecc97e,c6642ec7,df7a2cbe,61b63038,96ee73e9,309067c9) +,S(4b5ddd2d,ec8b0bc9,3b6e1d17,e1da71c8,3345614f,74201aab,8f9adc1c,aa249574,cfada2dc,aa3eefdb,434d18b3,d2a85a6b,6e16e755,6e55fd69,5dfb36a8,aac5460b) +,S(d775c2f2,ccfc0835,86a66edb,873a84f8,f9bb6680,2eb0be59,216bc35b,88d80e4b,c8a5d9b7,988a2231,cf6aa0b5,19640a86,ff56ca57,91135ea7,65bb3a4c,fc53ddbb) +,S(71a3cf09,78810f9d,fb01d984,43dc3da4,b005b07b,36ad4dc,d3c803a9,bcd9bb4a,8aab8e21,7db0301b,9640c5b3,9617ad55,bd4a3c4d,8989df5a,1ad31040,bcaaa55) +,S(bc71a054,92b696e6,200596ae,3d778ef6,a82f7d05,825c0d3f,ddee8bb1,991f53b7,15789125,9785b8d8,b1374460,630a908,d1125536,e400fb52,b07aaf61,729a2a15) +,S(9d220919,ee62bfdc,fb81ec81,a0f60e58,e8d94a0b,c6b79f7e,6f891c64,fc847abc,8149379b,1a073c2b,8dffe886,59f4e94c,75f3a7c8,a7bfaa16,e530bc7e,6ed0c675) +,S(8369766a,d30bbd19,a330c6c9,1e3ad594,d25bff1b,4e3696e5,c6c8c33e,eff33425,a27f7682,b3f4575c,858970d9,c21f7daf,8ed5d28d,8617c8a4,e4b958b9,72e79e4a) +,S(4e277875,e4aeaab,1a8d36b5,487fb427,e91f51ad,1cd68ab4,57db2e8b,20542451,9faeff65,5bbc4b81,f96bf1d2,a152a546,d138fd0d,a8f8523e,7635020e,a26566e0) +,S(27f907b5,1b9dfff3,e68d202,72605a91,825046c1,b21336b1,8b91023,39a0d2fa,115225c3,6cb13600,fb84bf3d,dd5c49d8,105f459,ffc4068a,cbfd7b0c,52d4a15) +,S(6d9feb20,650674c4,437ff42d,41f70f16,ef0900a8,f3cd6818,64b42eed,56b1fab3,82cd97ec,d82b3c1c,2e55f857,653afa3b,5f7f7b00,39aa2444,f60574d2,fa02065) +,S(8981d2b4,3360669d,361c3b36,3284e729,ecb9cdbd,3bd17558,857f2cf7,8187bfac,1b9aeb27,d5a45d1d,8791bf73,614ecd5c,216b263,6c27645a,3e86d781,70d48498) +,S(84a67518,e47ad1fe,25540d55,77e92ce2,81691685,6d3c10ce,9c5985a7,72e1d72e,d3cb93af,85072ea2,e6c11777,dc4b73fc,16ca04e2,699ef356,dc8a5341,c5631658) +,S(d3e66756,72a6cf77,50c111e2,e65a6674,7e666cdd,b9dfb418,d50c28d1,aca8c683,7776ecaa,20e5c6c3,ab5aca5d,8233f83f,4e2e815b,dd1063f0,7359fae3,a6556dd9) +,S(ede97def,71d45688,804e76b5,ee52563e,522a7684,a68efc4a,f7b2db38,65b87bba,92e6d20f,c9c1276,60a279f9,5758a80e,85bafb8f,b77eb513,7587b692,fdae702f) +,S(3359e142,e000484e,cf7db3ae,2312e703,6ff84f84,247ac6ed,25a0b9c,eae8c871,67b039f2,3c845fa1,f38c98bf,739dbf28,d00ada09,36e4377f,87166f9d,d21cdf66) +,S(e0d0a972,c0502fd,2654a3dc,40914a55,96f675ae,cf0d958f,8fd62c7e,122b44a1,cbeb1bc,34575a7e,bbfd916e,dda11903,3ee29e2d,1b11e171,8db7bd47,576b0312) +,S(50b89367,5df77704,64031ac7,a1f5d5ed,4f332019,d8da2605,9397df84,21a4c987,c4765d81,63ec9eb,a089c3f2,56d6ff0c,45c38253,3d9ed204,cb04f335,d292f293) +,S(5dc62d8e,66775ff6,f21e84e9,9f8e8123,7f80cbbe,41ce9113,91ce432d,cd6d446a,4b456b7d,e27045a5,76e1cdab,d163fd5c,b020cabf,125f5de9,9ad6eb14,1773a9c2) +,S(8ba1778f,c7921845,d2632e18,790710c0,4e56ba7e,22efdb03,fdcd4f96,22c10971,1f31fcff,a8359658,b39d7d43,59707268,48ffebe8,8f418c53,f48c7af6,3e3e3854) +,S(a3a14614,7d1ea7bb,e6bad055,fbf9810a,999a102b,ba8b694b,e7ada06c,8db9fc7,c9827731,d4321284,23ccd6eb,3531ffcc,b3bdb87b,1a727d57,a697eac3,886ac294) +,S(9620a847,bd2034aa,c81671c4,436682c5,f6ce1af6,4d8df286,21ad1c2f,e0af78f5,e1cc6d08,a7e3dffe,cb47e32f,4156bcdd,6168a205,e8959f0d,99e1555,4d826e1c) +,S(a40d86a0,aa7d6a79,3f0b6a52,c48d8fff,95b2c57c,584996ee,bc9d98e2,796016b,ec54d4e2,d453466d,ad94c15c,b25617d8,62911b4,45f44873,96ecd950,a351ea2) +,S(71ef35a6,f60f268a,57573b97,9745986f,7e5f40e9,ba64efbd,5ed90779,b3484c80,eabffa1e,9ff3590a,c577df7f,6f5a6ad7,2f3693c,be475669,8751637b,4f8a1ba3) +,S(bbd1a408,c742a3b2,e49ffa52,c5f70958,5308abbe,f103e674,86fc76d7,a4f68de2,4941cbec,9d07e13e,1b0fbe96,c7328459,ee6e1e08,50a079a,2ac0b61b,5d246124) +,S(169ebf38,92116df5,3e09c92f,f2c89b5f,805d60ff,9584d2ab,433f1f9e,84ba9009,914f5516,e7667ee1,581b5ade,bf1de48e,c997d149,1e932a08,34503599,7c6228d) +,S(8f157abc,4c5ec7fb,eac11545,890d8e45,d71c8d9e,4c4e0c3,9777502c,f8666acc,d41576ba,1e78a9fb,9899883e,602bca3e,14dc709,8982e1bc,63518138,3ff25908) +,S(8cd338d5,694b26dc,4ebcd8,39a006da,84f71f93,94fb8100,91ed8cb5,b4ec9a8d,40318c10,dd88d9b9,c080eccf,45464ab3,3fd9d34,c2027d95,2a6ecd21,d0002578) +,S(20e47e2f,7ba5ba2a,d36171a,f4ad760f,a64748c2,94892e28,cbf0ee3e,293272f2,b96ad220,56b89821,5c052089,789e4267,162ddaf8,76c720a7,71b15254,67be00bd) +,S(582ed8a5,a0d38f9c,db6942d1,1f411dee,f9c31ec6,ffbf1579,3f3cb5e6,5f2bb880,14928c0e,4f9d9d3d,ec4a8db5,64baf4be,948ea892,34a6e352,a4cf4796,3ce8486) +,S(f4a6f59a,6e76c11d,3f7daf30,b8c046eb,b0bcab8b,6d1f5e7b,877eb1b4,601896d6,2d39c6e9,e5d84156,559a05d4,a9b148e5,ccde4641,e4bd9abe,edc8a648,b070f0c5) +,S(2a820c60,f71cf5c,76e66bf0,15623f06,fc8564b8,c763a52d,2c329af7,30957b24,b38c0703,d7429599,e6c9a1d7,250078d2,36f77f7d,b9956d37,fd92646,77ff3ca2) +,S(8c2d9c4e,9fccbf64,871d4802,6077a3a9,33634d3b,c1d7f872,6231848a,6e2799e,ad50136c,ae43bb6d,8c49c534,e80985a7,cf032d77,604c2db8,d5954858,a9351a7d) +,S(b18e9053,744b3ed,d0b991e,23927593,db00d25f,adfc4490,6eb0d19b,74061ba9,90f5ca44,c829b2c6,771edd73,998034d3,43a8aab8,442302a7,2ffea92,b33877b6) +,S(cf1d157,b03d435b,905d2f58,cc53bfa1,d124827c,f4ac4109,33078dcd,1ce835be,ee37a9a1,5547c7d6,a0f03cf2,1451cd64,3ea656df,61eeb1ab,1196d959,ca71b459) +,S(8d8b9d8a,d91a840f,b2e1ac36,a6f4bce5,92fc13d1,f22f84c1,562d63f7,e54a6007,9528be43,93ca8f2d,dae0ec3b,439abded,ee9227e1,47ce6ee1,ce61b46b,b842dba) +,S(604b5544,bbc5b0d5,716c936f,b0d9a4cb,7ae04a8e,1d5a506f,2561b463,fc87fc9d,33fe2f9c,4fb9a6e7,41c1e5fe,7d3d9edb,ee3cde6f,106a1d8a,3e319254,a16ac8eb) +,S(3e7ed414,1058ee44,6057aab7,49e14ca2,cbd16d64,7959fe53,9ec0916,fbaed8f6,fc15b241,86dec6e0,c91aa697,10a015d1,f66bd361,82f18648,1ba24568,952e79fc) +,S(d0ae7435,86c17b8a,c9524243,16cf42c1,26e46f69,4c69cf26,736d0527,7943f326,bfa02de8,b983588a,d4398865,abf6b69c,fbaca03e,8bb5f04b,1971ce1b,f74e0757) +,S(ef5c05ce,d4f343e3,7e6ce2f9,45380ade,3627bc65,9de0597f,5f851eb9,88be1441,d9267c1b,379f19e,3bd0e40b,74ae8e21,b681e7c2,ad49f635,d60c619,74103281) +,S(176b8feb,d7998ad9,7c73015c,3b09d9af,d7cf4265,c7bf38ca,8dc64864,5c8f49ae,b4bcbe4c,8267c9a3,69cda190,5676c781,7f989f5e,a014f4c5,fc821bf3,8522f1b4) +,S(88f683db,cffc14e,ebb58d69,fb6da62f,b20771dd,3542fa00,97b3a766,bd16c3dd,bbef3de,812103c5,266797ca,252b74e,cf80096,b2972219,f52ff718,1d1cb601) +,S(a65d438f,1247ec44,fd8c4121,c3d67891,8488b082,31fb8854,dd1fe238,f637abaa,5f65c84e,551e433f,dfdcf3c0,b4c771af,4bb50095,dd0fc986,d4d4a82f,9a3f36a3) +,S(bd6a7e51,c5028bb7,f7e08148,af1597c6,d5cddab9,2883d2f,f6a5a6a0,6398cc27,3f2280c4,247cdd22,e8b97979,b9a98e93,c1ed14d,a76eef66,2ddb70f6,7859d885) +,S(a672436c,2896a0dc,54b1814e,c5101451,58f7a3ac,74d36202,34411a09,909d274b,dee80293,468178d9,5baacea6,badade56,c81f3d9d,95642654,a7afcece,bfc2ec3f) +,S(ca0f55fe,d3a1d0cf,1adb0993,d7d2daa6,57891ba9,321104bc,8d9e0379,f03a24b0,dc01b8e4,63462dcd,91588baa,ebca8d8f,52016683,9671399d,b5843e6f,5a3cfa6f) +,S(610ff08d,9bff021e,544961c,cb2fe206,211843a7,ea0c74cc,b91f0b5c,4b6429a1,4be3b18a,ee965fa7,dabdaee6,8fcd3e12,d889bc36,35f160dd,9b0760be,d1d46b28) +,S(2d6f8215,a70aeccb,78cf006c,ddf8f0a2,6b695dc9,622dd622,38fcc638,a9ff42ae,3fc0964,e2a3c969,e61e4e5b,ed50f337,7a988715,246c50fd,3ff45487,95a3927b) +,S(8c0e99b4,868e39c5,b610b132,de1a09ae,99846826,a42fc4ea,70b0c630,88a1bbbb,32b8ca42,f0a240cb,2876a41d,e61b8c17,6ead992e,8785a95,c384c536,141cfe52) +,S(1528ba8f,9000ae75,e3062af7,74ef6d77,658c6d1d,65af87e4,dbac9eb0,8fc96ed7,a8dd278d,54df6bcd,1d4e6466,5753a8bd,2d39f0d5,c731a53a,5faeca64,513b5651) +,S(c1466ea5,d9256a7a,f13a040e,29515858,104394bf,ac5af7d2,fa09ef7a,6ff1d7a4,cab6fec6,fb36d879,f0684b54,6003df0,d8bab3b6,e1050a7b,c05457b2,75be5128) +,S(ce8bb093,e051089d,2a297a87,cf90c3f2,2ea946ba,87a76b56,c6f11812,d3e98cb4,a8dbba51,b90684fe,6dff556f,bd13baac,679cbcad,90b0e888,4c252e95,e03a0470) +,S(76f3a73,7c9d9e15,8d30ddd9,b092a94e,7c426763,a17a96ea,9186582e,6d6c86d,5b1ca07c,4f345e6c,778ad27e,c6ce3f05,b29c5302,7e40443,b202a022,e5d16c8a) +,S(f757dfc3,23390b21,3bb98872,5dd7d43b,2ed997fe,af7681ad,ee1572d5,a74017f,d0b4e4ea,67879726,700b63d9,88eb3957,74ec4f67,ca1a56be,461d244b,a5b83cf8) +,S(59bf6198,a2deaefd,f080ec6d,3dbe3c67,f27a5769,d91c65c,1492a87b,9ed5f5d0,bf5eb739,9011a282,3110834c,4d467855,6e02f2ed,601529b3,a2f3d70f,8410f8b2) +,S(b1d02f0a,c86d8757,48685790,918c15a,a7b0e85c,f12cc52d,53f8082e,549ba16,7925e03,99926b76,8a80878d,63f3852b,97297725,96d3d06b,85437fc2,571a19d0) +,S(cd27372f,41d8e07d,1bd2ec1,27c41842,7e5267ef,14f3a908,882e4e40,381d1905,cce36e03,661b9f33,ccf810f3,b411693a,c3329617,52098352,2acaf0c9,5dc77297) +,S(112644fc,26ca3599,3b68940f,5cbaab4f,ad2258cf,bbc81695,b01db49b,dd9d3ace,8685f790,7b5ab95f,2f7a49b3,bedf09d3,7ff1362b,3be9db90,4e74d049,15c0bf60) +,S(918df72a,99ce4a16,20e84575,935a24d9,92fb51ea,eb8498bb,3a606e2c,d97d3ce,88b339ec,9ebbff98,f1b8b0d3,c90794cd,bb6b7918,2749b065,602ccfeb,bb8a8461) +,S(9b8f18f2,65c4ffc8,9425b51,d48a770,436e6424,f34a1af2,a7dacfab,fcdcb83b,bfb95e8,6a34c1b9,2c2e8c78,fc628139,476c4af9,3f63d247,5e454a9a,d585ba55) +,S(ad02f2a,c396626a,42abe52a,1ea1d8e4,3821740a,d8b3340c,9b9c78ff,bc24ce30,fcc738e6,cf8a7872,ff827cfe,1b7bb7a7,ad8c97e5,e254db13,255c2d07,1a2885fb) +,S(359afbd,5e60dac9,8ee34f3,f5acf126,9b5c1928,8f54476c,84bfb9ad,da6d85a3,2e2aa1e7,9cd3fed6,71272155,3932ce17,44358636,a18230f6,b501579f,b3088955) +,S(5639aa3e,230f2f1c,e10c34a3,1cd6c78f,c3c15c0,2061f3ca,55e09116,68b279de,e0b6e95c,15de7f0b,3fa3bc26,3a9e83e2,e3a7d51b,462709c,8ed6c2ec,b511bef1) +,S(309cffcc,7b2f959d,85cef3d0,c25efc2b,693d3b6c,ad42ce4a,d8657652,d669ec44,3abb62f,d917b1be,79809520,5521d522,32f949a6,7999274b,cf7965b7,a950077d) +,S(51f3d5d2,dac1d7c0,5d8eab6,375c9130,534a026a,48abf3ca,ce91af6e,533b86b2,50b1e7f,4bdc48ed,9f3fbfe0,ca05bdda,34f9cf2a,ad04235a,ba998a8f,a3e99f74) +,S(8f563d18,33aa4100,4540e0c6,f501d327,4158b0f4,8b3e4e1f,a3fe25ee,29c2a244,4d4c71d8,5ea93371,57f5248d,a012898f,3d031d93,c1803589,2875928,67432193) +,S(36ca2909,d68fdab9,cbc45b39,fb096ac0,3c4853c0,ee19d108,ee7c72c8,a77177a9,64f511df,c8252960,85f3d095,31467a5e,69cbf363,30e12ed6,90516208,d24301b0) +,S(f628b8d5,1f3ffd7c,c201ca9c,2db26f0f,58dd918e,b4d70cf3,80734702,8ea306e0,bd6bce3,ea82e9b,2e8d6eb2,7f557def,b7c2d77d,e6329c1e,a2d360bb,d8adc2e8) +,S(fb7a6bf5,a82c0b,b2a6922c,9d0350d4,b44fa7d7,61002f25,86e41152,c0e67c1b,c12a1c37,f1c9c703,1f1406,efe8f3e9,8419fc33,23a1acfb,345ebc9,4a702da1) +,S(c2091919,5c3e2af2,df14080f,400c8c75,bf8ec01d,a5a5b033,75391fbc,fe78d092,b307b56f,cbebdab3,ef87b1fa,3b66c327,7aec3818,e880de6c,3826b632,f1adeb9) +,S(7627df89,3d1727fa,a06d7c4c,93774a48,73c3bef8,d19bc03e,8d1bfe28,bd3192d1,35d2676c,36f455f7,aa29a865,f0776c52,1326dacf,f6d366f4,bcf726b1,b68451e1) +,S(26666518,7c9f33a9,6d25df65,8dbfe035,a3df6f73,cb059639,ed54b296,2c62b9,bb30bbc7,d34a5e26,a51fe757,8356a876,c4e7fcae,f4a977ba,468025a4,c686630d) +,S(3bda9185,fa9802e7,7dc6a02e,8f2a6f71,ef8b125f,735837bb,c7abf3a4,c62ac683,202edf2b,c11d1784,d7875a76,a0ae3876,22b3475c,bb0ca118,9362e54c,a05b85) +,S(2f90bcde,4486b64d,81313c71,48570d22,3be1978b,7f524097,bc033d6b,983a5c4d,63864e50,2028d475,365b2e30,cca99dd,f4aa759e,cf579705,270fda71,bc384f89) +,S(8893f5d4,e9b00e6f,dc992e65,4c9fcd71,7083edc2,6c71dcb6,562bcfbc,c8becda7,1faf3055,8e631074,fbf566a1,fe0c6996,a80fa29d,6cdb8313,fe313b4,b238ab50) +,S(acfc4ae6,86be5de3,eda4fac5,4da73510,f482fc25,309ce37d,855c3495,8555abba,387f4597,d1878a6a,d37a5b99,8ec291fd,76423f7c,f9bd037c,b9071685,2ac52d87) +,S(2ac4d090,a4c5e988,2f3a892d,d28a5229,d5fe35e7,d74660d9,5422a55e,2017a469,6058f516,2aa3ec45,47a79f2d,7d8e6869,927d172b,f75a1914,a3bc2571,df086c09) +,S(137ed3f,fc13c572,e5bc235c,d13c7e62,c9b1a3ec,250c024e,9c9f584e,7ebadef6,c1e95cc,3a464f51,b43ff02,3c8758f1,319e7c2,f37acbf5,4fb8536a,8ec6fdba) +,S(28a037a,72148a3d,2c89f606,81152b94,6cfb16f7,58f0a002,cfe4d857,ef3ecfe4,b2f4b650,135cde9b,8d3788cf,5c6e7a3a,ec595124,fa903aa8,a1bb3984,fb973d10) +,S(303ecd51,8fd433fb,ce9d426e,8761a76f,78ad6d5b,5f34c0ae,3bbaddcf,1a7a9eeb,6a7fca44,3b73b19e,967a5945,a275cb1a,cbd8dc3e,89d85385,ea956339,99abb602) +,S(cf63042e,5680f71c,2641defd,9d6569ba,c69cf079,f8b3140a,50923381,51533202,39f3376d,fb17fdc5,719a2052,5e1aa428,176643a,f50dc9b6,b8ff3401,eceb4ee8) +,S(cfafbbef,b4a49fb6,96923b6f,2e56c493,34a229a8,e2dd968c,44f485aa,1b1fe4b7,dc0ed34d,4a9c17e1,ddfafbc7,1d76f71,f3950698,18faa16,b5d25c6,c69f9007) +,S(ec0e3de1,fd527ea,6a493ff7,b3c6aa40,4a7ffd76,9a08953d,785d2f31,390d363e,94bddbce,300234c1,82f3c6a,717f53cd,723129c0,ff353528,fc5c285f,8e7a35dd) +,S(f07d3cd1,914422cf,d4b9c0c3,501916bd,13e9cd84,eeb02f7f,84be2a40,4b2bcde0,cf0c96fa,cb393218,37c48de5,16d1f705,dfe91206,8ae2d810,3aa1f77b,39637320) +,S(4a4254b6,6f2e204e,a3264ac0,a00c7a63,8a6fab7d,e67643e2,8217099f,3648db10,b5510e29,be93fc35,efbef989,15f7df95,b187ab08,bacb157a,c26ab2bc,2ee5023a) +,S(56f3bcf,b38eaf1d,35e62582,9f671945,910285b0,3805c1d9,b5d813e8,e87774a8,a9b964e7,9e6b7f9a,9abf3e5c,a147f773,8a1e8270,8677e6ba,c57a7a75,d1ed9dc6) +,S(f89d8f4e,7d32efc,c2d95bf,feaa4c9d,af6b9886,51fe7561,ace561be,6b081295,ca632f11,56a33867,63a65691,17186994,75e48f51,48d770d1,449ec719,8dbacc69) +,S(5c3791a0,d3e716eb,50ae4a07,51f0e92b,2890b933,c6d9aaf9,758e5205,27c5530b,44ef13c6,2de52d11,179e30da,e913493a,c4ed861c,336ae1c3,ca1f8bab,a7b34616) +,S(2748765a,e6baa4ae,62cd7156,16ceeadb,8ab94ef0,d3a3e331,15fa0132,aba38d27,71d4d19d,de63770d,60905bbe,6c7ae92a,f36b333,a93b3359,cd4f1f2,77f69e48) +,S(964f1fa2,f8ecd778,2ce4ee1,87ee3e78,d2c41a0,45a95453,22e363be,bb27e254,c744bbca,de64a883,e9481a5f,a2cce621,5137ae00,3b9822e6,77556610,d4138a2e) +,S(8bf60775,8709c9fa,8503594f,e6c379ff,ee758a07,4f7d2754,58985e25,79b8ca3b,3e638992,b3fbdd72,ff51cfb8,fb118d0e,ccc193ee,718a7715,a5ae6188,87dd51a4) +,S(fa107123,9189239c,2bfc5a95,ddbee798,fbff70d8,ee0d5f5,a10238dd,2e06612,a60cd5e,1a4eb0a5,99133ea9,71d5ec1e,315a6be1,2d773936,c624c468,f968e7f1) +,S(5b48c6d0,9f6f97fe,c03d145e,f797cb8b,3fe2503c,a8944593,83c34e51,44975588,44d86689,352611bc,c1291762,d965f136,173d6c45,28077d83,c6410a3a,906e30a0) +,S(80e153e8,23aa7e3f,2865d1ea,1e5e88c8,5d4e854d,54bca5b1,ae8f1bca,eeb94fd0,edd3fe29,e435c11b,13a92d8,ef4f6413,c888a12c,71823f7,3ed7acba,a06b9728) +,S(16e225d9,77bf7526,c2590dd1,54c6e29b,cd25de9f,7b1fe3d,ae7ed6d0,79ccf2c4,e0702d23,b9b57554,6d2db82a,fcdc22b7,40088c52,e0fa274d,d85188ac,9710768) +,S(a3440a09,15a16c74,8221b65e,7825ebcb,bb064aa2,de3593ce,7a691efc,d9456938,ff7c7aa0,3a582a8f,5641323a,7f437a67,26fa5d3d,e7c3e26b,be970904,d4dbf40c) +,S(8154334c,d1164468,2320401,f2d59385,8d607203,93eb37c8,a030e9e,cdaa2b5f,bba4094e,5f97e4e7,b0f0676f,4a091488,17126ad8,7d573d54,150139c6,5b3d5142) +,S(c91348ca,27b5baca,3a092b25,74e09976,fe7f2361,3e1b5efa,329e6a5b,7d4d93c7,ce241d0d,e9d49a2c,5060e62d,c01bfb59,e7c37295,4095f639,9d61e73c,5976b44a) +,S(9408b5a8,79fead4c,67635e99,59ca9649,cf362940,737e6436,3ba2590d,7e6e8a88,e10b410a,26c710c1,bf2858d5,21f46094,e02155d2,84b80fdf,cbf17645,7f994fc0) +,S(b851d158,eefe48b8,79e0a159,f8582aae,f80ddf90,d9021b92,e7d3e7e5,d6bdd2ce,290ae2e1,792421,c1cc53f0,69c7d580,60facfd8,e537693f,6abdaa2,c6d85469) +,S(9d320b4d,4a7c2007,65805387,5e536093,87e7a5b8,54da05eb,1b971e6a,e53d10fc,4f6acb7b,6a1f190d,6822acd0,ae783b0c,9b44be61,ca68306e,79603f47,25d7c456) +,S(6d779810,7b29ec65,4ff86799,d13a4d89,eaa49114,d00744c2,2f30a9e8,358ac7b5,8f7993cc,105c4f09,ba060a2a,ce265cc5,77b2a334,3ba3880,4b0a3e79,db823950) +,S(bdb2d2ce,77e14fb6,9a17d62d,b8056d28,680c9607,2c82772d,73c91da9,e57f6489,9c1ef71,d43c3b19,dcf8eced,ea6f5648,8a3bc00,3c2b0c1,84c9e42e,5baa590d) +,S(49a8ccff,1ab70a94,a4db4787,f73bb058,1f7897a8,d0a1adea,741ffda3,f055b3fa,f298d277,2049ebd1,ee2414ed,6e053ad5,15b41914,fd6de43d,8bd9282a,6c6ba426) +,S(ca3f7d6e,b466b7b0,56267992,d317a02d,3b3d5652,b51040d7,7f1a173a,769d2236,a26d6225,9dcf384e,f78e6f2b,f96a42c0,90e538ad,f6f1a1b7,126dbb12,8e145c37) +,S(b672ce14,f1c427d1,b478469c,b0d08b99,902e9cad,a66bc65b,c00ea630,737176e4,176c0979,15176,27a0f1af,f3c5a56c,83f4f550,b8c4bedd,b818bf76,68865efa) +,S(bdcbab57,d4ad621b,d6db6866,f43778aa,2c26dd78,8f2e968b,da3c913a,ee9c199b,79868872,9bb213fd,7dc75a33,8d6168da,1cee9f0e,e93c7c11,24055232,88df5603) +,S(f5f25f1e,382f7df5,74c64aa9,6c5f423e,d5beed0,82d80196,1cb384b2,55fbf9f5,ea388f0b,5ad84aae,58393a82,9a50a4d0,f54bc068,e12724c,98a9dffa,f2210221) +,S(d2a10464,f9f8ab9e,1ca6e280,b2747c43,60372049,85a2650a,a730b894,16ae09c3,d19719ed,8dc46533,28d8dca,f2c902eb,2c9ffaeb,ab3c1b45,4fdcf68b,229c5962) +,S(89255afe,e6724c4b,c5522105,8e5e6aa5,19058c3f,f3ff691c,c5f81a00,20410038,8d626dd,9560c934,11b52ffb,4fbab500,c833c3b4,671713e6,9147d8af,da3fd7bd) +,S(5e468c42,c5eceeee,2a67d73f,57819019,5165e3f2,6c93f94,beb12fbd,556e3952,af83e24a,467f97e2,ab8b6a8c,330e400f,ce1fdeb4,3a4ae1ed,c3866505,8cf240de) +,S(70c2bfac,9eca198b,f606850e,8f54ab03,552caf8e,9385036d,7fc85007,e99c4e2c,60322365,f5ffae57,8a8f9939,20cf7596,3b45d940,d2c4591,544feb6c,d6ea37b3) +,S(16143d3a,fe5f6b42,500f86d,7c79630f,bbb0db1a,64e901ee,ab641c0c,b9c2f318,7078e814,bdf81d7,81109561,d4d1cd37,9fd74924,4cad1e68,ecb8b247,d76a8a94) +,S(8ac33ead,a9341644,db25b034,60ff60ab,e1326eb7,73a0bed0,29f6e966,16b62bce,6d07756,6f0d18b4,bd055d0e,d99bda50,ea9e614f,da46005d,c6d9dbfd,18945cc6) +,S(476b98b8,9e7feafd,ef5f0116,6e0c8eb6,70cb3325,a10fbce7,71ea9e06,2566e22a,38dbf041,83252b86,7e138483,c012f220,fd98bd5d,b80fbf47,30c5d0e1,d6a91050) +,S(e4d262ef,f0de9c3b,d3302bcb,321adfa1,2e383a7f,19297058,a19c453e,ed497f11,a1f830fc,2fd6120c,8f2070c4,9ea49001,953d0c0e,7c02050b,fec7cf7b,ce2781d3) +,S(67bc850f,48f0be1b,f18c4f7c,b9f33e67,e2e09279,4196d987,1bec0fcf,f0b5cbf8,e2560470,334aa4b2,4693e927,1d2f20cd,b7e6fabc,92e0f355,526dceb8,5f52d32f) +,S(50dcb2c5,c0dd4942,2564be02,2ab274e,b543a6a5,a2712b8d,58f51e12,72ef2797,2cbc036f,b69c44c9,9c7854b4,9705dccb,8d2ea16a,2f77fca8,90d89f8d,4ab04ea0) +,S(6c9bc577,784948f4,1e8a57f1,bddf5665,f860804e,89912876,44ba7b76,8758bd6e,84a671f3,5998ac84,7329ca3d,1224901b,a0401915,c9280260,cd40ff44,ebb3ff20) +,S(5dac93c6,d27cdbb9,9f3ceb36,18c6ead1,28b44146,467390e7,a337c494,8382a868,86572065,1805082e,7434578a,a97704f8,8a91c2b8,e315ed8b,ac2a4bf8,342df508) +,S(207903c6,5e1c57e1,7a6e9484,d3073c0f,f6b2a40a,b46b996f,2ed2be7c,85780c86,4b568420,29564d4f,6c4fd310,73e17a3a,bbdcb83,115a6c14,854633c1,47f6a442) +,S(da025f63,9be593e9,3ab35113,2282830f,6ffbc94c,829111dd,29546221,2edc2f90,9b4acdeb,a315e576,f0edea9a,21acf5c8,714defa6,5ef4ae68,eb694383,916d2723) +,S(327faacd,1b489c8,2126ffa6,b87cf191,2a0f61f5,ee47eea5,6030a50f,b2823cb1,634ab42,6010009b,144c5ac2,903e5f8b,14c00c67,5fe177e9,6b7f9ecf,b8c33b9b) +,S(44cffbee,1619f462,a853478b,a9d1bfd8,6c9cab17,f5bf50cc,79a24558,7b7e8084,b08983db,62079e28,7eaf8c28,81079a45,76442a1c,fe35f64,563c714d,748e7da4) +,S(c5d945ca,13730ce2,c21ad679,734d1deb,beb19785,480f93a,43a2170e,6a6fe3a,a3aad11b,4d8b7140,f67f7287,8e4a9c1f,74102ffb,ca3ef04,46e7bf1b,ae3cc3fb) +,S(f4d8cf19,84dfe935,11b2694b,8f1db0ed,d4bf22f6,435359e6,77fa42e1,cec0de31,83d02d22,3aae1880,d7ad76bf,c7e3bfde,5b48a55d,4fb0ff02,c0ba5c2,e9a30845) +,S(13d721dc,8e7b0bc8,90c6380a,fd0f0ee7,a2dce0e1,e153ae70,7e50ae7c,1837c4cd,c620c1e3,e1575411,7eb9e823,70af7e4c,9309c412,d5f14d28,e976112e,88841116) +,S(e5f9efa6,66fc6728,207eb55d,94c1daf2,5164824c,e27b1783,92862e11,6265ce1a,500a9fe6,18302bb5,22ff9fbe,163d79b6,1f0f3cf,53e0956c,7712f23d,5c0d9975) +,S(73ee3cbd,a1ba137,65a7abb9,827b1b32,a48ea0e0,a43e9698,8da603c3,c319c9ec,d96832b4,4093689c,beb30bd5,a2e825f3,bbc909d0,20e18744,b0a5ec5a,1ab38f58) +,S(11b92fc5,94046bba,813285e7,122750a4,3e028501,f027a55d,f101d2cf,c5990d91,e08b24aa,e20fcfe0,40dcfcd0,80949b2c,23b324cd,1d957b63,9fe91716,a4315417) +,S(3043669c,22be0dc5,fb14442a,22a625d,b1bf4952,362f06a0,623ca1cf,9160935b,dc4834b0,4bf47d6b,fad52c04,4d26e21b,41eeadb2,5c92ca22,6dd2c86,24d0eb34) +,S(490c39aa,c290d144,2b87622b,407dec4a,3f92aa61,6a9c8495,132b5c7d,3d5f9a0c,5c76c0fd,57844aa6,fe81361f,73cb314f,110f87de,6773e1ea,b3d68dae,2926b986) +,S(fdd5cd58,67d73347,ca1fb212,772b0c42,3a4caf94,a3e0bf2d,2bbb2433,9002d03a,efe5f407,d1d26bc7,6fb5bcc0,6bc94da5,bd69f84c,85d89172,10bce909,77411995) +,S(bc8a108e,b0830fce,443db272,2655ac40,e2d35989,cad192d8,e9c5017,14435ea9,924c0258,cbc3da74,a708a384,b93196c0,76bd3af6,2907cd60,22069b93,e3c979f9) +,S(ed809b6e,41f4b6cb,86ae431a,718989cd,2c706ea4,10152e3b,3395df8e,ae924b44,135d805c,3295589,c8d42ffe,947aa88,bede6699,b119f8a,324c9bfc,64315da8) +,S(26b50ac7,5d871e07,4147d2f1,ee42d6a8,4398212,79e9f276,7978517b,6cda52d8,26599c63,86426e9f,b14b4c8,3494f0e,1fe37ea0,521e4518,e346bad,545dd7a0) +,S(cced88de,27d0ccd4,7f6746ff,98907cf1,679f6b10,67785dd7,25ab94d4,9fe8efd5,5b0ede2b,4dc584ac,b4b6adc2,c52b4268,e2beba67,ce2bc825,fe4df43d,73f5729a) +,S(d8edf7cb,31526332,308429c1,178886ce,bf3fe516,18f472bb,52da883d,d83c6249,a56abc3e,c155eef6,d43427e1,30ba691d,8c71b80,6b487343,7f3daf07,17984be3) +,S(ed82c3c9,f8db3a75,50949aa5,cd6c8fce,52648910,53f66515,c2fb71c4,7564b4ef,5ee59949,6d9d3c7,6fa9a26d,444a8a45,41ddccc8,15fccf39,b06e8fe5,20b2f0b4) +,S(6e277bae,e89aeb54,5e6249d,56d50848,5853b3ad,8588365e,7771ae44,3d8ca937,84871bd8,1e6f94a7,c31a3fe6,63a1170c,8f9623dc,4e24a5b4,3f7d023,39f40b8e) +,S(aeb67ac8,67271210,565b1229,3e1aff94,f2f8b642,76768967,f702afb9,48a03c5c,fcd3c588,94259c03,cde7898d,66ff5ac6,747100bf,3707e89b,6980d21d,78bde197) +,S(9f07ff4b,aa20c756,94c967d1,e38633e8,45753193,a3d13af2,3ba7ce07,5283da79,ae8b7f0d,49d06168,ff3c5f36,3aca3f1c,14308cf7,d95bae36,3975fb7f,dff2136b) +,S(24046d14,a026daba,1644b3ef,7bc5501b,baafc3c9,76acfdf7,243395d5,713e1520,265485e9,6dc40a31,ba93cd0e,915534b4,37fd5380,bad0d6cd,321867cc,aa029ae6) +,S(1376926,bf8fd1c6,80344f48,c574e6b9,1806afab,7d9f90c9,13bbc233,2b3de519,a34100e7,c4b44c26,f86b2198,e7d3696d,8aed9051,a1755ced,56ebe795,d5a61fa5) +,S(943c6d4e,816cc668,87f7c85f,b116ae82,5e4c38a6,581cd5f1,1efc9a3e,ef5a7d6e,d93d4724,f618da6d,29e35165,7883ee36,74112f70,3169eb24,eca31a60,969de8dd) +,S(adf8041f,159260fd,cafc9e61,d5f4f867,a0f46e94,af60bf53,d4d0808e,885432d9,c4eaba1f,99d2516f,3d731bcb,be90b36e,c055ff5d,e6c3ed95,2b95a037,1be57958) +,S(15c17f13,a404ae08,11ddefb6,707a9fa9,dc0d9461,8325dbb8,d96537c0,6e2dcc44,8740d352,21f95e8,36842b48,2512d79d,a799fb62,60d65dc6,79df383a,e9b6898e) +,S(87bbbb35,62365994,9c5bd575,86b813dc,966ec9ed,1ac31435,80e4b7cd,cacb8ca0,159992c,1bfc3c24,95e14ccb,e24a3e9b,6a28f4e9,1cad4a36,f83624a8,d22a48c4) +,S(39bd144a,96454c1,9e082b5f,eae9c433,564da73c,d5d44daf,95b5c176,9f1fa105,ffe8a11c,30b5d4d6,f8abe12e,bc3ca8f9,dadf1527,330319bc,fa277b13,256fb207) +,S(c4e4eb4f,9bcec0c6,6bb99bfe,265b2f4c,a1c69676,ee970a9a,b78bbd64,a83bca7a,1bb9a83d,316cb9c4,bce4be2b,d05e6381,2ecb493b,44fc3cb7,c2248a0e,96bfc286) +,S(c183e783,1c80d334,f0ddd2f4,48ca035e,8b53f034,f53c1801,488e4aec,cf244e69,f998feaa,c7711d48,3db38926,8ddc080c,8ff2f95,494e60b1,59330d8b,d9a8b820) +,S(99d1d7ac,22a2e108,f093b630,f374d9d2,7ac812d9,5753bb6b,e39c2401,fed47d92,a67d334,88dfe341,e8dca3d9,b36a897b,52080a3d,c366c8b0,39cc4de3,52d0e1f0) +,S(e0700a5d,16cd89bb,df3d6f18,76909082,985a3339,e7f5d2bc,5c3ccc92,ad61cea5,26f6abe0,38953fa8,40bec97b,c9d59c20,1fd08b29,4a7ac7e3,bd994e07,70a0b0f0) +,S(b19bf6e8,cf36d839,51b813b5,67aa62ee,5415e95c,d3fa668b,55487a3e,61a7e721,630e78a5,ca2cdb2e,cd59f59e,4c10e712,46c1055b,a7709041,fc03f8b9,1970153d) +,S(25009156,e90dd0ec,88027adf,a9701474,39abaf72,1c333943,c6fe6e19,ff71fd44,324047dc,86477632,214ed74,cb933859,b7825500,2d19ddec,9c466e1f,573afc11) +,S(45ce7fe8,bbe45d1c,9bb0c98d,9d4c500a,aca5011f,96238681,771b8538,61a3e4ae,8cbc22af,d1adb8f0,a5c72570,2b6c7f65,9e41b176,1358832,7ca062e4,2392f8b1) +,S(73255507,d3a27fc6,7df39212,a286ef6,b939e953,43b12683,4e38148b,619ec059,3174d6e6,e941020d,db0f3ded,aed6c8cc,eeba10ed,1344ce28,a354e93f,25c35b63) +,S(96b2cef4,1c6c39e,1ec9a945,8c8bad3f,6025962d,a1207885,97800ac1,c811df58,ed85ed21,41b4ad26,3adab5b7,c0c0035c,2fbf9e2d,983dda99,7d1249c6,8a7fa8cd) +,S(5764181c,d941b28a,a307bebb,912ae87,57f23cc9,37025cdb,b983eef6,42b9c772,e8173b4f,c23908fd,db5f6437,5457a43d,2495764c,f3ce1b8e,f2c8d2be,e0ac7089) +,S(38ec7839,7e965056,2b1e314,5d5b2782,ea16d2a3,82bb6c60,efd29edc,93ec1b61,acf04ea4,c7b6de2c,d8acaa3b,a4bf2656,f522e631,1cd51089,744d4c96,318ec96e) +,S(6ec4ef80,13d01c79,758439fa,108f7389,79c9f7dd,4b285eb8,b7aed579,76c0b572,2328e642,e618ecdf,3a6560e,3eb6af15,7a6d266a,f5f3c4df,13c1ba2f,c898be7e) +,S(d8da1f6b,30c27b1a,5000fc14,9a768683,4c40a581,c4ed67ba,88492449,ef77141b,ccb532d6,876d92cf,2b8c310,839387d7,d92464e1,727287dc,49a3b348,2d519758) +,S(84f25402,734970ec,966aa9f0,eca84a1e,6894d36c,338722b4,e5fed162,8f7ccb90,f2d7fd09,f07e6302,cc97d283,d9f06319,9bee137,d3946944,983c2192,335cd8b1) +,S(ef6752b5,58874372,859190e3,91d86ff7,3adabfd4,cf008038,136b2ddc,ce9503e3,23d9ca50,13cb479,7cbaa675,98dcf98e,73f949e4,614390f9,1e6159f8,3f131c56) +,S(f88d8590,3b3fe67c,f641cb52,c77c776c,fe198f74,fe116a46,602bc2c0,1085ba6d,58af2abd,2178ad68,a3695714,cddf14e4,21da61a2,8e6cfa32,40507fb7,ba08742d) +,S(93b739bd,d366b95c,675daaa8,c5ded7d2,18cde6d5,d15d354,645de017,2db9c01c,24ec535,6c2f8eaa,633af5b9,6ae0fcad,7afea5f1,c7c8bfb4,748b886e,25cf3627) +,S(732cf404,da7ae5a2,dbf82f80,180c1154,6d313b92,58980f95,d413f223,443394,858e8326,1758b90a,902eae58,a777b56,e8820084,153c9e43,1b7d789a,4d596953) +,S(efde0719,2c40002d,3f82e024,a1b838b7,65fc252b,5be4af75,28ea5994,a9d36e32,89c1d35c,3a153576,91a63585,183a9c6f,b8dd69b5,69d45483,f0c2f80d,3dd9107b) +,S(9eedaaf2,80693392,76420c1c,da950bda,f114e65b,9b1a74a6,b42dd90c,1ec90a15,1399dc24,7fa540cd,3249728d,a4d28c52,26f427e1,ac3b533,7de8ba07,d3164de2) +,S(23f0ea8d,342aa214,69deaafe,bfdb487f,775805c7,d4ba17dd,877d3d42,19433195,dc1c6e31,d2a481c5,5d32ca8d,1d4e93c1,63444dc3,9e7c306e,4fcbfd46,b699b03) +,S(d85270,58c29e60,58916e82,76a305d2,edd47c0d,4e33052c,74cb76e,abe060b8,9a0e4904,e4db5bb9,5ec9e510,59ea4993,84d497b6,8a9a1258,3a2fda6e,bd0cfac8) +,S(1b34c95c,ccf91204,5922d2d1,90cf3860,f7d36b37,7ab3763a,d4d9046b,309f28b8,70c2320c,3f5f663a,b6478c3d,2607cb4d,f7080de8,91bb408f,6f14a11e,2951e948) +,S(32558fdc,ae5e596b,b756d4e2,f10707a,89569d36,1498e8c2,8fca1473,4b84b228,7759a8b7,5ed5755b,b23978c4,3d5aacd9,fe63f6e0,74b6fcfb,501906f9,16038d86) +,S(64818b77,37d2bc57,391a3820,2040afbd,6028d7ec,fa3d6bf8,577cb1e8,dd984acd,4202b934,6ae9554d,9227b891,4a6304bd,2cf895ec,a7267e1f,5707489,71ca2300) +,S(c6af6ac6,ec2d7cb5,42aa2373,24288551,bcd33705,72e1b1aa,e572d87d,8d77b333,fffe5559,4ae5a2df,18e393bc,dd0407ed,5d5af18b,85f22352,9fcc7cda,dec78cbf) +,S(91f8fc44,94ea12f8,fa2a031,7f32fd44,2e7a9e34,28eeaf13,a8b0de3e,a4f1cef0,6a0a47a,d81b1dd0,ac6765f4,20cc26af,e76c2225,eaea2a42,481ff6d9,a554dbcf) +,S(e159a9fa,6a26f57c,879b081f,dc015b46,dc610a61,2335d6c8,e353fc42,11c1c017,c209c643,445ae009,a75656f9,b4c1df2c,f1c181c7,ca81d0b6,bd749afa,a93e8851) +,S(e8e9fc5e,135f3a97,1211eda9,c06d283f,1a705169,b89e5813,556beba5,a4f9330b,e8b96899,3b73e8f6,da7b55fa,6f1e44b5,c88423b9,f3fc5553,fe0b10d4,e9af0abb) +,S(f3f5d529,d5ae4c0c,36c27ec6,e13384b4,11aa9818,aeefb5b1,64243536,bc0edead,3f740643,1e40c133,345abbc4,ffc5e0aa,1dd1e306,43e52ae5,33f5be61,db3d34b9) +,S(c5b76430,f5d8bb9a,e1b9acff,e1417602,9eae65c,9ca1606a,2e4f6d7,6cc81159,80689f09,70089098,24087591,6915380a,29b13933,9a000fad,d2e86d8,cf2429c1) +,S(954aa4b4,34f59074,45ed30c9,ffb6db50,ff275564,72894261,e935109c,cfc09b0,8023ca86,1bad2c25,6cd76be8,b3b0fcf3,a808b76e,80a70ec5,83265d3d,c72e4ad) +,S(55da8bb,4803f339,3020c9b9,30a58336,1b96ee54,4bbb4249,d6df1335,6e3d528b,dcc44b88,ca06e9f5,86cbb209,da01785c,2387b14d,355a2575,799ef7ce,32e5b64c) +,S(8084e800,d810b286,b0ad48d3,d262b4e1,db59eabc,a176e368,77a7f8bb,a3a1de03,d8beea9c,59f865fc,9f09c12,9bc5f980,70d60ac,eb348f85,e0888fc4,63dc1480) +,S(1b2bb6c2,36c87f88,748f49a1,5094949b,d15e0217,b00c0bd7,f85efa41,5718b39b,2164b195,6aadf36e,f1bdd018,6bea9a4,c3c3f42c,b0e81dc,8101ad14,51029c74) +,S(fba687e8,125c372,e5dc65ce,3d2febed,de9139d9,9f91ad84,830da38a,54377872,8944eadf,3b89501,ca4a7a82,105a9613,55aabbf2,57f4bf83,923ec12,73e001c9) +,S(b58e4b80,a5f1c16f,f9c699c9,75a2d797,d21e155e,f091ba94,8aae2dfe,fd560cbd,fff9caba,1429bfa6,e571b7f4,12d6aa97,4606f7d,9025efec,937a5128,52cec3ec) +,S(8ebcd526,da5d6c21,55222551,e05c6be4,114fdb7c,9d43a900,fcef17ce,3426edec,73a0fb98,6b3301b6,3eaaafb1,a52f5467,de11f034,aeedc21e,ae97df86,513aa97e) +,S(fba4540b,924b29e8,95dc68f0,e971549b,7354e032,64040d2d,bb956691,1bee325e,529088c3,3b9857ae,342ba0d0,7e52843a,290f3c27,16f5321b,6c9e6f51,9105ebd4) +,S(8b7a2c97,a1a7d324,6d86301a,66f3f36e,ce014c97,651b25b0,ab46f4e8,bf9c44de,b82854ab,699c35b3,6786c1f4,71ea7536,7c88a684,43fc17b9,4cb328fd,a4b247c7) +,S(9df11743,5adb7154,e7003bbb,a1ba909b,93179677,4bd4896f,40c39a58,ead2279c,a7922f4e,a216cc36,c36913d6,e7ab8641,d4566865,dd7a2f83,77ef219b,41b884eb) +,S(5beb7b84,749b444c,82efd9e7,37314892,c54b6215,c1f9be9e,414adae4,56931cd8,99e425f,32cd0a7c,28e753fb,ff308eff,d32b7cfc,eb51df7b,64f60e9b,2fabb95c) +,S(2a830c43,ff204ba,78c5114f,4a3b7b09,c9f8ed5d,17066f43,cd512af6,4c788879,12523a4,e8882652,eba827fe,d207eb00,9ba14809,9ee42865,61e96230,62666c84) +,S(f71a46a1,4c1f2a18,e68ab408,bcd32d94,985a7714,19c1419,1b69221e,1c57486a,adf19b3e,ea79a53,4fcce36a,4cc0da0f,4fac443f,b281cf0a,ef675b6d,20534875) +,S(2b80e5dc,aa9c2b31,678ae2d9,f7d63c0c,bbad2bab,36c412fd,cb6436a7,c69d193b,bc83e04d,71d60062,b681ce04,c506247b,8f3c9019,c2084ffa,27621d62,22119c3) +,S(49cbdf3b,e521c69c,68375b58,d4449341,75424a0c,3c45d5b,b975235c,e63beece,c5615eea,f0f0e7ce,c3756aba,d3a136ae,91d260f1,563997a8,ae46c49a,5c62de7f) +,S(ce4d4714,b643b115,9215e2f3,1dd070f7,f7aa663b,9ca87739,79c5ba31,ce9ee1b2,b1f35e12,a2fc3aa3,4db59ef5,e62101b0,479745b4,200c10aa,d6f8fddb,1efeac38) +,S(6827b6bb,aa905ac3,8d93bb1b,5d5881c5,a8bef479,ed7c5656,2992d4dd,61e10054,8524a87b,af65020d,91848427,95801f54,30fc3727,7ef2064d,1aff8ecf,296bb869) +,S(e71e4d5,48f4d7f1,c7b7879a,a03a49a0,ab947218,b16dd125,19af4ad1,8056535f,495d0461,7098b3d5,6f7e3bed,ab5c9813,83aeed68,3ded1c09,f0d1bb38,e32f574) +,S(ef683979,7429fb30,d436aa32,a3feda68,65652816,873ce542,6320667c,6e00e032,b4b18e4a,de1197e8,fded846,f188a6ac,2f17506,f014d404,fbba3635,534613f) +,S(c729c1,ac8f7eff,e908007d,e2eed85,dd557e36,c1ec645b,a258b03,e52deaf4,2e83edc2,18584f6a,c5ebade6,a335a087,12365b89,ac38f27,bde7ebf4,561305fe) +,S(cf1d3919,9a716d53,b2bc4f2c,3ae2ded1,290d54c2,2124a88c,c1cc7fb1,115735ab,6b44f5a5,7ec3402c,e8980970,dab8538b,c276c8ee,ad542908,9b7b70d6,dd00e6e6) +,S(6cf3d7a0,266dee28,f3f1f772,ee90da75,a9c28a7f,f82d8f55,cf9d609a,5becdeae,3bd18fb3,1e11cce2,895b9cf9,4d9a99bc,ede5c9b4,c44ede14,34dfaeb,49bc58d4) +,S(da4b5832,ae63eeca,cdee4274,f00ae932,56393cae,cf830be7,f28749c0,7d395542,9aabb23b,d2d940f1,815610c9,d1684b92,106374e3,1b694ec4,dfec8818,cdb2210b) +,S(f91ae350,23210e16,f9c63270,cea2c6bf,407d1a10,e95842d4,b8ad5f7a,4330bbfa,1f161ac,d1962573,5969290e,fd3d1af1,53c91543,33651e16,1b51086b,913aa1c2) +,S(7e1bef52,cc3e2a2e,45847c86,27328526,c72e3f73,fa1c8255,56c874b2,f2f1ac02,be4b0f7e,a706e4d3,db7048fe,e0fc4f50,8ff0523,ee8d97f4,4c4c1787,e6f6e6fe) +,S(dd2695d,30dc2ee3,748e256c,cdc92eb7,706567f1,4be720ec,b27fc814,d3d5998f,9af32c44,f1028013,9ee7cf99,845472b5,bba67740,f0f0b96e,d1a02a19,a32dc74f) +,S(78fae650,714763c8,d12602ba,33b76da,125fe395,43fb37c9,4d20b337,649dbb9d,78dc4c0f,726cce6,f65826e5,eaf1fea4,c3b92532,6624ce1,21de2351,bfce3aa6) +,S(1edf521e,510e5dd2,aa1d3e00,d016b45a,1b6fb6b8,591d98df,ee692636,8dc98bd5,9a0d016d,9b2e9b8d,d0f4d817,71ab0f20,aeafd8d3,63beca59,7518da41,55311f86) +,S(8607bb0c,5e53b60b,9153272c,d3cc69e4,7ce28694,37562b4d,4166aa18,d0101d39,15ca31e,c83c6541,c35a294a,a648af8e,38870757,27924e98,8ef566d5,96eb519a) +,S(65fd300d,9a0503f7,80a0dbb7,c42b9bb7,37f9607,912f2df7,166b65f7,96d02eab,fcd15350,84fba852,11f94540,1f6b43f2,904caf3c,d44ecaa5,335b928b,4ea3b5d9) +,S(c918b3a7,6c847ce1,c01845f0,34ccbeb3,36d031a9,33093f99,f47f1458,cc02d148,f256f403,dbbb2e20,32063270,faa6e62,596329d,ca85ceb5,3ed3adfd,7f728568) +,S(6ac2dcfd,2f0b879,becb429,140ade0d,532e54d4,dbe0bbb5,c1352ace,5661817,f2c26310,abf9396c,5af23074,e72251c3,784c3de3,60466dc,ccaaae16,4a9e2af3) +,S(8d71c4ee,54acf389,cee39b94,b5feb2b2,cf8e95a9,45f6a95f,5b0bff2f,cb3f3d19,98442bd9,5a02c1c4,7dba3881,e3306103,f0bc8857,89b44cb6,54875847,f1ce52d) +,S(e8bd24d3,fd557118,f2c0abbb,511e874,2e5d7368,a909b072,b3dee124,44f583c3,e1d2c3a6,e28622db,65b7992a,6716459e,2380285a,cd60f43a,4d4b1bf0,8bce6538) +,S(62dc2975,5cf8cf89,e52c8c51,6311e3b4,483fd024,ab2ad1d0,32d7255a,3c075217,799e132,d80d22d8,e0f01e9c,d0954251,8b3fc8a9,48f3cd1e,9391f8f,5c0e2a97) +,S(37db13aa,cf727e60,cb102709,9c7d3c07,4d43234f,66a95451,a6050496,d44c0e13,c383bbe,8f8ceff4,196e9d07,e013a1f6,d3402a6d,d09eafb0,62924334,89b1066b) +,S(37bfb765,eab7719e,c858e17c,472f1df1,be9dc439,2699ec39,37875b30,fb2aa1ab,72718bfd,865dc4c2,2f935f4a,98af8ca6,5cf44b5d,66f0eea,23b53563,d1721364) +,S(433ede12,87fcbf35,5fdba6e1,f62148a7,b5d5c3fe,1200d456,6dba452,a85d1b94,b27c5613,ad6e513b,28f0c959,13e30781,137a4c7c,7aee6e60,5715d949,4757c20e) +,S(e582d9f,c5ed0de6,92a1917f,6ab7df46,d23295d0,b8b27b32,3af9a57d,3e671861,33ee8785,e2770dbd,7e79faa5,386cb1d1,d1c9e073,1b78dfd6,5571deff,d4073154) +,S(60b07b40,634bf15a,ee2d4e5d,a0f06ade,ad8fae01,4a6a189e,4e428600,4c9442c2,fee087cd,d243ba,62b4a2cd,68d8aac5,1557f29c,a57b0f37,ff2119b1,bc4b283d) +,S(4b167276,1e5f7ca,a245e35,b6e88fba,643e6da7,ce37c9b2,10265ad3,2de03100,28aacf02,476887e0,90d26c1d,cd51f15c,cacda02b,b6314a64,73b90c93,8ed8dde7) +,S(5d56113d,6d797e74,c4803e63,89a88ae2,fab97de1,4ed4e7b2,a17d2f9b,fe2a3170,94ef2a8,534eb2ff,a62462a2,7eb2b34e,c6545992,e2d973ab,ee42b1e7,d9cd524f) +,S(a9d03fae,75111ad8,d1157287,8e8c4473,f4a06cd8,f28dfa7e,e38b3d06,3a34f828,a0733c0c,c7319879,2ca665bf,968f6bab,480c94b4,c7de262e,4d9d1f95,33517881) +,S(74199571,2b1abce9,ed4db3b7,7b3f7e70,58fd3716,1666c522,50855f25,660e1ad5,710caea8,97f7589,cfd3b8a3,22726000,ea3ef2c1,8aac385f,22b67bd9,da44db) +,S(9f16990d,bf81a5b1,eeba1e5f,84673e16,ba0106f2,acc68be7,cabab96f,25918dc8,d60d47b,62321042,34c49d39,c03806ab,135d46f1,ea6587b0,f0b6588c,50ebc4f5) +,S(3b5069bc,4f12b9a7,ad91c888,fabd15a7,c99143f4,c8b24619,b6a70c0b,2301d57d,dbb87cd1,ae59a944,9fb4ddfd,4b311d66,da18544e,a86b75a8,979828c7,fddd1a79) +,S(9dea63a9,67b5a36e,bbbf9af,bbfabb31,f90cdcd7,9b75c68,1159b946,dabaee90,b14bb99b,11bab958,e31a605c,e9180d23,5c7d2eb,94d4de57,b8a66627,c6bc4464) +,S(d0b23392,1da36a84,2565cb53,4411e63b,9fe6b2c,c33c3a18,8e0ddf48,55e31e79,80ed93ea,1a6e5492,dcaf1cfc,c350b3b2,99117276,43fb6a75,15c435e5,f74f8e9) +,S(8adc9655,966ae44b,f98ca7a4,9f441e3f,acfe85c8,f7111da5,52c817b3,90b03d20,ce72054c,6a717312,180c80ea,c9337430,5dd1d566,db1b93b4,eed1a6e5,73c0021e) +,S(f6f76bf8,cf6d3dea,700800b6,d018a4a6,141ac610,e2e13fe3,ec916726,1e0c1670,b2c478af,e1ea9876,98e5e2a1,6b3a4ee,63e4fef7,6644da54,e241c395,3f9a302c) +,S(d58287d,38b7da30,174fcaa1,873b3913,2176388d,6da5f9fe,f873d86f,14b898e5,785fc934,69ed085e,8318f91d,114ad0cf,da2b8ff5,61e545ee,3502e9ec,6afc8402) +,S(7772ca23,50c5ee55,9ffe810c,6d8702c1,538e3d11,f4b8354a,2fbf674,39a3661c,5a2312de,853fda14,5b48aefb,23e98e4f,fe3dd3a8,ae1fcd6c,931d9a43,686edddb) +,S(a2f9f976,c50f3687,7ad0ac95,efc3fb2c,46ce903e,76ade308,69705df,72433ad5,ec565311,24c289f3,35e11b22,bf8d7445,f201dd4b,68afa38a,22af056f,c8c285f3) +,S(e20d9765,85f8c567,24d0ec16,5ad27110,26fce166,b52ce11b,cb221a80,4f8b15f7,63b61933,c7f63862,98b48b6b,c6abb54b,21d08220,166ac31b,fc85d4dd,998f33fb) +,S(8868b59d,ca1de2,cbe98d94,506db1df,659ba688,b2bf093e,42dcec03,acecc18f,f02e9b07,96b98fb0,51dbfe8,4524ebf6,dea56251,afb53076,e2215d83,fcf2fd0e) +,S(1099f82f,e32a1361,a785f86b,9a6bf0e8,99c83226,7826d6a4,3f8581b8,89fb677c,13fb83f2,9aa7f271,2cf20cd3,592eac46,cd2d3e61,9afe9aac,7fcf37d8,9c44445b) +,S(4af9c696,77ece7b3,5f732969,8ca61b27,deae65ca,ee2d5d31,ef2051f4,35cbeea3,73004596,429da976,f96e3f80,a24408a4,ebfe5196,8130c698,5b210826,664e5ef0) +,S(9680cbca,5aceb8b0,322d4bbe,8713cab9,300d1f8b,d16ee045,edb3b543,57dab473,2f293940,e36ba1d2,ecd4697c,209b33e0,6904ee3c,2798aaec,e9131d9,bd0df58a) +,S(7eefcc3a,fd6347f0,5f9c3720,31480c92,3eb7fdfa,d6e9c0ee,4e2db9ae,27fae7c2,b8e9fa75,4f745c14,7c753e42,17bb1e28,7bb93d9d,aceb357e,9738f10b,fb5cac06) +,S(b823156,d63612b9,3461fbf3,6894955d,65e7805,b025c2fe,98fd46fb,2f9fddb4,f23c517a,826fe91e,45f453eb,fe9eadd4,d0b537c6,a2a8f60c,dab9dc58,10bd465a) +,S(e801d35e,2035fd54,92178706,e2e765a9,bfb9de22,e89182b4,4d5144d3,85bdadd9,1e648234,27dee6f0,818868e0,419e37a9,1c216200,3faa7478,48161809,76956a26) +,S(e9d4610e,56b70969,284369e5,7e890fc,29fd7750,8489a1c,2f348f52,cdc67f3a,8e5f6aac,357d82e1,a29bfca2,2ee4516b,1df0e622,257c2c1f,7d9a84fe,b48e4a0e) +,S(71c3ef0f,51d6ddee,340d7aa0,94680084,b94bae8a,e3528ff,4a481af6,d3b7609b,4d1674dc,2f8087a3,138adc4d,aa615a43,db6886d4,4992fca4,544f57e3,315f6b96) +,S(9567ce43,b6f5c41f,bdf8b44d,938abac5,21aadb83,c157b947,ce999c0e,4a7d6f20,c9d86317,31ea318,a647ca8,945cc35d,1fd0fdd9,b2de4fe3,5e2a0021,33a35538) +,S(34523d4f,81166739,10b3de85,11a1921,4f8fc753,fd8963e,b9db22b7,8edcc141,900fb9fe,268b88eb,9b80a066,277d8ba,877bca84,f277dce7,7d6e90c4,8422ac90) +,S(69fd1f7e,5f1c43df,a2f4350b,f20e5cf1,f2993dfc,b61cd278,1590ad79,e3c1bd98,633726cf,8b74940a,edb15665,a9277973,2d1c923,2bcdd615,43c9f3a,6c51d7f3) +,S(e4c2c424,c28cb8f7,3146dd39,bb4fc6ba,411b2a75,e317faa2,a9f878af,e94a3de7,fb8d17e8,468e5ef4,8da60739,820515cd,c861378e,9b3bd158,fcc8a949,eb21ae89) +,S(5710459a,ada916d0,9c1feb59,c494863,98e44513,226119ea,5ded7b1e,34e32773,89deedbd,d2d83a58,4e56b409,30a8355e,905bf12a,f33f49f0,7383eca4,e63871bc) +,S(d56faed4,171806e1,5ac17f91,37101b27,59a3a8a9,90b85bc1,89b217a0,6fd48e64,fa871ec3,3408b2d6,b62f513f,5368f549,ebe43977,a48878c7,6d731447,34a6652c) +,S(e7416eeb,da0b805b,68b47be8,eedf87dc,cb5cf012,804d03f7,604525db,aa599bcb,8610c716,f11c567e,313eb4c7,77e4314b,533d4f37,13b11185,bbcf5442,b9a56c19) +,S(8ca59862,ebdd3541,91c60608,13ce32c6,a4fbfca4,6e60175f,2d5f1607,d5fa997d,aaf3480f,b132feac,6adfa195,2b14d0d,113dd6ee,faad71c6,980d5691,e37698f4) +,S(6496dc45,1fd43b45,1915867a,846c5203,6eb8e4c5,28c2a181,72fba6f1,53243413,44fdc41,f85c99e,e00a281b,9292cf1b,153fe2bc,5c8b3e87,cb14d624,e44da1f2) +,S(3a424463,aa82416a,a38bc248,dcaccd20,67d20721,d4e8f5d6,c9cb2fc1,91925a5d,71a56463,fd242591,434775cb,37c81bf0,d4857abf,aa527ca9,c3178464,8afa9fb2) +,S(6a945b05,51d55194,adbf7985,aa85fe63,8811781e,6b787418,1574d109,d178c2e6,186d343a,1ca3a836,32421fc0,d267ca27,80df9af1,32625d31,924e99ff,9b57ac44) +,S(13f1f692,682d04b7,b7e7381b,8c5c13b1,32baac0e,b7f8c8aa,c9ffd77d,3aadd47f,572a05e4,9622ff88,9eb36852,85d1400f,43372533,38c47ce2,282aa4ae,26e99f0e) +,S(79fa13c1,4f0de8f1,ed98e7b3,3edb77f5,a2700707,e54ce522,a516634b,d2804330,a094bd88,e1969ee7,3f41e40e,60f7198,cf2484ab,5a2efaa0,96e891ee,d5987c65) +,S(df1ff6db,d2c61bf7,c273f735,a6dab3d,fb08377d,bb3dd101,9dda03db,e533130a,2fdc7ad,68c58689,92e5c969,d8d14a94,cd0061b7,5ab0e824,fd19611d,923f886e) +,S(a8223280,5f5939ad,180f8776,810cc7,2bff651b,df7972ec,51d86d2a,1acbdb34,9681590f,982e3632,b2aea9b4,40043751,e7f2ef2a,d4b125b5,2784ffb5,17c2dfba) +,S(406b0cc0,49f3bf58,6990ada1,63c73932,b3780fad,ba3fd8c4,79e6a688,484c7c44,aab0e4ef,f2508e54,79074fe0,218aee7e,8b217932,859192d3,1bb20817,df6503dd) +,S(4e7043fd,5783479c,3dcf103d,bc4d9bc8,523c81ea,dbac9460,4b73300c,f516bcc8,196287b7,3ed01bd1,e8033ead,66c92625,77c859b6,73a4c7fa,98f53d69,e82d1133) +,S(a9a2a671,b945a5f5,90575c0e,ff24c070,a7d55b00,e9d2f44e,43cf400f,f3e9997,6cd45e20,cb972715,167f957c,af7f2f3f,9ae22ce4,3b46d4ca,5b2a58db,6e0c6285) +,S(692a1394,75ffae88,fba47ae4,9315a6ef,12b2dec7,6807608b,22ba3ff2,883d3c01,74dd5b4a,3c14a54,c0a86732,1a77d160,c834773f,8063406,85a65c16,6ee46131) +,S(36f915fd,156638bb,88928e75,d00fcd49,63b44ac,f5d016e7,7ddaa8c8,d62cfad8,3f11eb6,16edd183,5ee17d02,722cf6de,991dc8b0,e37fce3c,cf7f5db,e842e8b9) +,S(3f0f6f5a,7e010c0b,5442fd6c,a3643144,a5202a7e,bb3ac8f,99aa38d6,4aa53b31,2574d7cf,5b33e29a,950099ac,db0ffb3f,ab1f9ed7,f13cdf9a,b869fd07,c35c649) +,S(8c58faff,71f661d,b21d4258,a92aacf3,508a9dfa,bfc00cfd,26b95673,34b7b067,38d1e894,ea1d4ee2,843d08a8,80b11325,e9a8a171,59de273f,694a14d6,73944893) +,S(d806731a,205c1a3c,64b91864,2d940b12,dbc6f580,866b7a9c,f287fb7,c5da78de,870a8430,2ff57abb,735da831,863525fb,70402006,d241eba8,d43c2ebe,cdd0e05e) +,S(7dbda37e,195db041,a4dde151,5a7cdd97,416bde09,12b9a741,3354204e,2b848648,83c89c7a,649d0070,fde84ff1,32f60a9f,e1c0261a,87a8ccf3,885fb003,10715e71) +,S(8afc23c0,c803b0e2,df78ee09,3b776cfb,410a1b52,7d4be45e,58d8cee8,3ec54a1,b041eece,4f4ae4a0,b3a52957,386394be,bf7b11a2,b97eb5b4,1bee89c,352ed76b) +,S(4e4424c2,2c50fab0,4b803b4b,2562114b,1b2531cd,e5786112,36fb9021,3652207b,22bf6181,51187118,fb421e4,fb0cf243,889d5ca5,c14b6ab5,7e7b9d0d,d9796e6f) +,S(60717788,5a230af8,a0816e39,6e39b34a,f394b1ee,c93ba382,8fd92941,e6e97453,a84df29f,2b9da41b,14f24762,df6db87,fc0a18fa,89e356f4,9f8f4017,dc4b26) +,S(c9f73d20,384e56ec,37bbbf8d,facf70e4,a271f169,2dc22ac,6022ca24,8caf3213,7622e7f4,c77f9581,b73c9bfa,8bf5b578,89070e8b,97616cb0,69ea25f8,8dfb5437) +,S(19ea5dfb,dab5a1d8,c3c2bfea,48d90f6d,a8b9959c,630bfc4a,81ea5eda,ee45282a,c21295d,14bd81,7c3a4d62,af307b1c,870a959d,7d6e4c65,3b8e1db8,fdf1d56c) +,S(564bdb1e,30b2c292,cdf0e149,1bfaf852,4780669,717de5cb,290cbe6d,c1617b58,9a8da0e8,6fc0b95f,99e84198,877f5ddf,32508c1c,ebb9984,7004f74e,7e2b1c0d) +,S(5bc93842,4f3ee6cc,926e2123,7bee0739,50b39d1d,e0dda3b5,4bf46953,7d0bf06b,d4e9c3a9,662778be,b7f4bd4,cd753a50,32bb003b,694b5e00,c119dea4,20fd5d16) +,S(19432e94,829e8b57,3eef4fc3,181c7d7f,5b2a5f2d,a2e8c3a5,980db9bc,5cb7f99e,f75c999e,5c5b8567,bb69316e,36e01acf,2e860781,5c5703f7,a4be72bb,9abcbcf6) +,S(d399281e,6196ce19,a2ec28bf,a0b1b559,8d0235d1,beef1672,ea230cda,20080b8d,1ca7e95,20ffce3a,33e94931,95eba433,d5483cf0,d5edbea9,6384d1a3,5168aa19) +,S(6c84e35a,b220fa9f,e55f5556,302f6656,39372e97,74ae26ba,e21ca8ea,10e73c88,98a11b2a,bd442c0a,d49711e7,b0e8b234,1d15465,71efe9b8,9a7b4c02,1101b2b3) +,S(7814cd8a,b1b045ed,a1797f14,c83bd807,ec4205b6,e9df4858,1180a80c,a0f8f64b,ef917e40,edc8b8ff,f53e8b70,4de63adf,6bf17730,41af2284,1b0e38d3,b515f33b) +,S(7a01ee83,1c8e22a8,bfdaccfc,cd280532,444e710d,7138b993,fb6e06c7,996235f,fe512d09,19895c2a,b62f2e08,90dc3bc,dcb06198,18160b41,4527e7c,281a5519) +,S(655ee6bc,df86c9ef,918b5660,5d4a5e79,512e856f,3f3e40ce,72e8217c,1f9bd908,cbeba5a6,91fcb085,f3996ea9,d081e69b,7dfdefba,123df5c3,cae94f63,12f87721) +,S(ebd68009,fb0372e8,eb0be25f,e78117bc,3d4d4814,440f1d9e,1cf87ee1,de5219bb,8efb7e38,6f96df55,bf0aed2d,6bdb75f3,6da8cdb6,ff65868b,d8ce79b4,ba58814b) +,S(87340818,9ac05bbb,4e95cf05,17d5abd6,8b7834cf,1168f914,9f1d63b6,19a3865b,9f1fbd27,72ce0f15,c91d4dee,dfae0a3e,7d24c0fa,de2bf30c,df63f93c,14480838) +,S(61b6b213,b02a7bb6,4de62e92,515690cb,5dda3a4d,aa96f68b,a12f2c63,30513df3,cc71b8f2,4eff8194,36ad2962,8c003a1b,c42c6ce6,53997d56,e0162508,c2b6144e) +,S(4ba7ee4d,77171ae2,e39c9e66,3849841a,ed5e02e2,182d872,98f0a064,3925fe37,5cdbd5b4,fe344f16,cb72158e,b80a726f,5a9da4f7,751bc4f1,f1fdc734,8e96dbd6) +,S(d5827a7f,c4d5dcd4,32e5bd7a,46892e9a,6f9a3971,fe9e1de2,ee247e6f,208badb7,f790fadc,9d0e0c0f,61efb534,9b23c60c,2fb667a6,3b52cf18,ca0f0a1a,6098e1fa) +,S(81ec5159,7f950cf7,c0d44d4a,89efe691,e9086dfd,ca25098d,411cb9b2,93237633,885b27dd,c9eff80c,94653f28,ef92fb60,741d3a2,aa828065,5fa60b73,eb9a1e7f) +,S(28fc41f4,a0d4f680,76a3e06d,af0aff1e,6e723779,d8ac9e68,4cdba2fd,6ab6dec7,b1e4af1e,7affabc7,3eab9415,c15b235d,482c6a8d,b1085f54,d80ca2aa,49657225) +,S(d5e753d6,c5bf75c7,cb0f5b85,77fa2f9f,6341a88c,cf59b5d1,328b18a8,f7ae4f47,d1aa54b9,8eb01a32,2518a10c,8ecd66ea,fc2f380d,d97953cf,faf0c540,c4a1bff9) +,S(1682355d,89094297,e6008fac,44c1a5a9,257d413d,7589286d,8bad4873,1476e836,4f69c762,e29f90c9,e8459b11,d53ed84c,2ec9c2d9,ded4ea02,78d04546,eb09cf4a) +,S(9ef7a85f,b64cae73,2eb511a7,8cc46e22,73425d1a,d02272e7,9030240b,23e781db,c720356f,6f6a5a47,3f35e2f2,f5e92e2,78a50c9f,3a77604b,3eb3987,d09ce73c) +,S(4f6aebb3,6f429a52,4cb1be48,ace0eaaa,2174468,c13cf612,dd7ec70a,2e915ec,406ad94d,a8b6d790,2ad3609,252b6d7d,b28f96e,8780f6c6,39620b11,cf8e761f) +,S(4cac3e0e,5ac02e66,66710dae,5c0d7e8b,7a3cc7f1,a42b1f8e,88561bb,223f8232,9396ec8e,aa96f3c8,4ffcb9c,d8c29d27,dca65556,c2fe5931,8ba05a08,cb263142) +,S(86f3463,3aa2ac57,2c4a9dd1,64c7606,f61fbd78,60619937,38ea0a60,f79367b8,c802fd2b,556179db,5f776c9f,db4c38af,cfb2aa61,b5ae712a,3f23e58,25824f05) +,S(5323f145,7685e123,e7a5ab4c,d1d39fa8,bcd6f23,ba3d74da,318dc7ff,347103a9,3092fb79,ecccb29e,12dec977,cbbf38fb,94cab4f1,fee1e8e3,46100378,88691cb1) +,S(d3d70d46,402711c7,e680c45f,3a0e1c5f,f85cf106,983ff0e7,1a7f1178,33ea74fd,91982773,d9b90f31,16893b9e,eea7fb9,e4f722f3,e1577c90,fdf6f625,a8d07bad) +,S(df63b937,28a87613,2db5d71,43654f1d,f8cb89fa,1ab1efdd,6fd864bb,db6d4359,7c3b79ca,191a3ecb,6c793034,832fa5b2,a2c7bed4,84b792a7,91bacb7b,62ce5510) +,S(f1fe9222,8cc334d9,5f303169,a4e2262b,28a99494,c7decec3,9746be85,71b70c74,5588f3b8,4e65fa72,a2b35f60,f99af1e3,b67a2435,8c28eb64,69de5136,e751a10e) +,S(9030e75d,918ca330,edfec14c,b7cb28d0,fb0adbb5,fa29fc7d,cb3c9085,ba74fd95,7df20128,61698984,c0928821,c38aded7,5a452dc1,3bbdc4cd,71ad99da,38b427f7) +,S(1cd3c139,35bedb87,134f628c,576411eb,dfa0b586,2c3cc7d5,ddd7cf5c,77ad641a,2154edf2,5488bb08,be93a25e,c847b26,88264cf1,30fabcf,5924bde1,4b9a324d) +,S(e56e0737,a19fec7c,78b9f462,1ec0d5bb,bd6e91ff,ac995f42,202f127d,2446ad96,8830d827,368c760e,40a8d9bb,b693e975,1e69b42f,4b2ed776,98f889aa,3ead70d7) +,S(1b0a6ac4,58abe5e3,63e6dfeb,24ad8a65,56fd0886,78086657,587e63f1,5fb6b101,d2df6fda,e64263a2,b133e5aa,bdddee5e,9a35e86b,2e1f5926,5212cc49,335178dc) +,S(10a14146,8fd4618e,4ed390a6,246d62a2,50bb1676,45cfc679,d80cc081,23ea22b2,3383b868,dd7fdd1a,ecd13dba,f4ebb151,f9a09b25,bc30d562,b033800,fd40185f) +,S(9dc4ab51,5378a351,6db8837e,568afa72,e357f7d7,95d546ac,8a73e419,a43f5e35,8ddaa2d,9bcecfbf,e2a422be,1ea591ee,bfe05ce1,90439a06,cc68d66a,6305903f) +,S(aa93175d,e7a4e1fb,c538eb19,e89b20ae,13539e99,362035de,1aaac76e,3ced7196,edcedb04,c1344568,9f3e133a,d8b48004,c18d146b,ece3da05,effb864a,c2afb9ad) +,S(8171f3e4,da02d9a0,8bb6fcbc,6d06cc6a,94f3cefa,35d4ce27,536ef6fb,222edf7b,e91be5fc,aae62aba,1c02b55f,73c1a64f,c32038b7,a658f189,7499ca59,e9e6dd6b) +,S(51f6c983,c89128e6,635888c9,3c79cf79,893fe627,437464b1,11b08058,1aa19ab6,5d2ef149,4bb164f5,20c1dbf4,1a8dc650,b8cee400,d5e37252,a3d0f059,3881a067) +,S(c0a1f184,766b30dc,6a5016b1,9db0dda7,ec34dfac,74dae580,9500ab8f,6fd7d38a,bdcad3e2,c7b6daf1,29d41051,624d26a9,b27f361e,c695d490,52893597,eac6f01e) +,S(b1bb1ecc,95435169,4ab6fe0,7341efba,fde9279b,1eb78a22,2bd39472,33a0eb02,df523f5b,525342e2,c0cdbf0a,671e0c95,4b149084,de4c61d5,5a7bfab4,c81689a8) +,S(b5cc30dc,86d04ed5,88539ea7,e437391e,3b0891e7,6818c470,1803de7d,640b5dce,f1cc0d9e,45c7312d,fb1acc8,62e03f28,9f55561d,fffe4b65,ca41e62f,42d4ec14) +,S(de2b49e7,5f0ff53,28155b3e,a7d5941c,8cacee23,51fe16b0,da8e940f,9263ccb6,34d7a42b,b12dbf67,b0d7c680,178154ae,4690d5b5,703572ab,74b4d93f,6d8de99f) +,S(8b18f4e7,c129e117,e898404,65870cfc,ea5c0db0,ef2ef11a,d515f3ce,57cc8f51,6b1e1086,c8674a59,3c5e9d6c,dc8edcb3,72281c27,e6b2caff,8842e008,99111f37) +,S(727162c0,27cec888,918e46f1,762a830d,822ce424,b6a5f442,f9e582a9,17c86889,de348296,1452a369,e6fde5af,693e5247,24865623,cc516e7e,8416f38a,2f223cca) +,S(4ae4fc1f,4dd17483,f18eb9b8,1c2494be,946a75fc,a3f61605,6d0856b7,3505058c,d87e7812,e901fc17,da034a05,bd878470,59999b36,e2c617a3,9ae46921,f2563e7) +,S(e90bc9ac,e9e873fe,91826f01,66affd02,72007bba,4ef690bf,58b15206,5ff43cd1,4bc820e8,9acdfd68,51c9b0c0,65b9bfd,47f0762c,d2e56d58,905e6360,4564fa87) +,S(ba0ae6a1,db432fb6,f7a1af7d,be9ef746,8340ba5,7f051020,9af4076d,f51450ba,7d3839b7,4099420,1729894c,51a2c30b,51019a18,c32ea908,c58876af,912be720) +,S(70661b81,f77c6531,1d98b403,ed7b97d7,4f9682bf,2981fa5d,ef882022,d3accfeb,9022b973,18959f4d,9ae4bd7c,fb06fe66,7b4eeb92,5f46677e,4e5bc4e4,a6d06387) +,S(fe1b7b47,28153a07,11f81b81,7d9efa38,29451c15,8d1f11d8,e5e0922,fc471a47,95a075cc,68cfb7c6,fb115dc0,5e4c31aa,85725bdf,2344dfae,19c8a034,d2b0f847) +,S(8acf3bd2,bbaea907,28013d04,f27e3a5c,4325bc37,ec5c5e86,7ab70f7e,9acc3fc0,f679e3fb,36da27d4,f29c06a2,5951f80d,b3e2e0ef,dbe2c785,ee148657,3e16174e) +,S(1d855d37,25fc7d5a,b87fc6a4,7f62ba7c,d2db01d1,ca0f14f,1d6ea8d8,4af395e3,ceb263ac,2d41654e,d4655183,f6268c5d,9b0a9ad0,da8912a5,815dc8d3,20b1b90) +,S(1abb4566,30c24ce3,c84ec6f6,d70d19b7,9934498,451f02e,3926d70,592a8cba,4627706f,6be87fd7,2ad886ef,73245452,1778d293,41229863,f4b19548,74525885) +,S(1b4790fa,672c12ff,965d07e2,44a3f30b,620e5c66,4b098a5f,b86f2bea,93eac036,eed050fc,a758e2d1,f2a2a9a0,6b8391fd,7c19fd70,9c9e1283,1b018d24,e7edd60e) +,S(61878b2c,fc9cc88e,30ddcfc1,2561367b,44c106a0,21e3dc98,a433732a,68de3dd3,fa2454a0,37c279d,1b54cb77,498a9efb,afc6f50b,6b13e29d,436000df,1eaed78e) +,S(84048df0,2767feb1,7bcc21ec,f4502238,abc7271a,931730b5,3354010a,8544308a,d4712e4a,39d608e5,d47f9814,f7763c6e,605accaa,438ccb7c,838c88ec,e993c32f) +,S(eb7ac4e5,6c96175e,c3f1e1e0,37e89f95,8ba37d0c,a88055cf,889bac33,ca683a94,4941e267,2ccc274b,6d8bfe07,513dfc6f,cc07de33,2f517011,2bc3814f,5e85da8a) +,S(48a36ac9,877d4bda,b62b452b,a0288dc1,4cbce455,3809feaf,1a4195af,22142e91,e24d6d8a,1256288,6c6d8848,4e950d8,27eec39c,21ff6e25,30b50eba,ab69659c) +,S(9c3125d0,5a62a25d,7869af3b,1b23a00e,e817b664,c82d2a91,2522ce16,bccca1b5,c6ecda10,f6707168,18417277,4e18f5f5,38566361,5506b450,a189b6bd,39e5afe3) +,S(33e81e21,dcc7670d,c7f3843e,4dc8be3,10160bed,49af116b,c2ca91e6,90f80d68,e694c1d3,a0a386d2,e6df1167,8efe38ae,9ce417e8,9ec05bb8,fa592906,55757b6f) +,S(9543769e,6899e0fc,bfeb1071,50959827,5c1d0d4b,8a68f056,d8e1c033,1b6a1ad2,700cffd,afc58410,7635f148,72ed1ceb,1c4be86,7f668b16,3f579534,ec9b0d7e) +,S(4f41ccd1,73e62c39,7d481e6e,5b50807e,6ca9b163,e21dc203,a0d50826,7439efdd,fbf98ed6,aa8c1972,d88b309b,ab74b254,8fea56bf,e158d1ee,d95ce77d,c69f79d) +,S(4cfb6a45,584b00df,be3fd9c3,38eebc28,a667fe6a,4651bbd9,64ecf20,645f12e5,2ddfd6da,fe080e87,470fac21,d1255900,219638a2,5e72b12f,5501921a,f614652b) +,S(9efb8bec,d9bba33c,a5e217fd,8fae533b,8e536bc9,1817f982,1f217757,75eca626,661300ed,529c15e,918bcf9c,1a7dc3e1,8e3573b8,875e2f8e,9440f8ca,b0658fb4) +,S(c2a80952,5be7e4c0,b85ab953,ebe6dc5c,f5d3c0f9,e850e470,da9e61da,e430e441,1d2ded36,bf72697c,7da6fa22,1c09fbc3,6be06363,d50d277b,4839c4ef,809d9d2d) +,S(42b7ff6b,49ae822b,f5a16276,4c620b13,2501a1b4,1bf830c,a242e02,3bbaa707,e41fe058,83f0e400,712de994,dcbb5a38,fb2c7595,c5ea8b0,eb90a236,82b71d6d) +,S(ee0bb129,d420b641,2afe1518,55f78f39,e00cc3b4,7ee3b86d,f781f0e2,abb0c28c,f71d0e7a,5a4fa247,67c6204b,60edbadd,54b17455,54ae9099,258343eb,5c0aec61) +,S(1f1d8a07,9cdb3bf5,2671a716,26c503c,77546ce4,2a252e78,9ca487c0,78ef5b9a,c611130d,ae55512d,ea8e8e6f,9fbc3064,180ee4aa,63d1dcfc,5c39e98d,97571c20) +,S(5bda9c06,ecdc9fb1,5926ebe5,5418f016,7cf3136e,5f9317b8,b2912fec,d2a31310,358ffb70,dce1260c,80fe59d5,b72eadb3,89076168,a17b63fe,b728dc28,45ecad7) +,S(c32f6503,fa50c727,66652dec,8e0efd71,edc78973,48061be9,1f847544,df866ea6,67234a26,7a13ecc4,7b09443,cb13f0b0,6a34af2a,20b80856,6deb9c71,1fca304) +,S(5483d511,b71feb06,70e6c83f,29612640,849bb673,896bb069,941cdea8,9489caed,a09a6a62,d3233437,40adf0f3,8d8e4ddf,494d5350,7944becf,bfed3927,d88c1148) +,S(3d2d8f5f,f0b86ab9,1ecb9342,5a6cc246,d5e3c6fd,9c43d047,2d518958,ea322ef,4188348b,4c23ce05,1879cf4a,b71d7608,fb45d7ec,a184c509,c739f364,d955b830) +,S(3a5c3939,29ed6780,5fdffa9d,f936bc10,735ddf03,aab89e9e,40b47574,21af1dc0,760b1919,76752916,2ac2a7de,22975c83,44dcd52b,a945c81e,dd8224e,90a7011d) +,S(a3d522ac,7c1cc06e,3ef9272e,fe24a0c4,64886948,3373df90,e5dd484,d4590a83,6f53ba2c,b739dd61,219684a9,50001697,28c377c6,93054795,5661f2c4,ee1b6be2) +,S(ef0b0931,13992018,44078583,62e02aa2,c1437d87,ddb95d42,36751445,d8e8180f,73bf4407,8d184772,7d0408cd,f92f8c74,2173520a,41971980,1b9c842b,9486c634) +,S(8140effd,1498ceb9,c2211899,161185a6,a770c15c,d1ca3e07,f26f40c,6d15da20,4c74f3df,6d2db7f1,be68b7eb,2162f03b,51c1102,a02dc3e,45fc49fd,22a474) +,S(3445d720,8e089f1f,93d32f4d,3fd6fd65,7603c7f0,378b74,215a59bc,ebe122f0,6d41035d,fdb54ef3,3a3c8626,f036e1a2,38da0637,e82953d4,b63e23a4,890b6d5b) +,S(e14b2a5a,6235369a,1d1933f9,7124cde1,789a366d,5fe6ab9c,72c23d43,f7a58cd7,216f06c5,2f63c102,6cafe6ce,58a9e589,d67a9f19,625513be,44744d0b,f9fc8f8f) +,S(6d9640bc,8389bc44,fcd0b2b3,3c088170,eec93106,67348dea,f8f3e92e,2d891e0b,4b4c1485,6e42db64,9fe04759,a42887fc,68b5d4b0,8161b31a,1852bc41,7d04227d) +,S(272f1404,5c461f21,3f67cc60,d7c03b1a,fd028a05,b9e06324,62a0afce,fd9b2916,98635ec0,c0b23538,a63f78d,800eb0df,fd6b7583,31544b1e,825cd433,ab348416) +,S(a8c99928,eaae745a,631250be,cde1ce3a,4152334e,de916257,681329ee,1f3a07e3,4ccc2686,abdf3f7e,8e1149aa,5162fed1,24ae51b7,e61e4620,9f5cd611,e7351621) +,S(fb024b33,7f93d565,899be686,9b1e4c81,1c660d50,fd31cef,fac38600,d8975409,339686b7,8bff3c95,7e8730c3,9457966f,ee562ca2,89d95e12,7b072f47,c4c7fdb0) +,S(e0e48547,aa613549,773ecfe3,691b84d8,a6cb14f9,ce4494f2,35187057,28562f9c,60b6dd8b,56407580,52748838,9396004b,778609f8,69f47676,a6d76eef,dca7b307) +,S(bd6c402d,378b8b53,9f74d29,605e1b0b,ed9e383d,dfec2169,8076ef59,125d16e4,cc3aac9c,bb761c7b,3776a8f8,c8c6d2c2,a276924b,79510f10,75521b22,9def2c24) +,S(e6919231,eefa4f44,144e2c55,4f695235,d5886cbd,e150aef5,52c217ac,5e451c0,e74ee1d4,d34d2612,b4d56133,c2a11cfd,73a888fc,6927f46b,c2aba9ac,63b1cfa7) +,S(1a9acc6a,2a487874,4d92ac5a,328bdb3a,37c481e,8c145463,f9cba4a1,a03d03a9,ba6069a,960e3628,fae22422,47fe1f5e,df71fc81,b49b5ca1,26a7c588,fdf0fb10) +,S(5b52bba9,2e5fe301,f2c4e939,52db99b8,abfde642,3d6da8d5,36ccef4c,f6070910,108ddbdd,901e227d,118c68f0,a71aed0a,cc1ab1b0,2fe74341,f546ede5,b747ae29) +,S(92c13760,1ebe82a9,b11a096a,73be2828,652b0a4,d7f9a286,f1338a6,b1e05242,2e3e93fc,2dc6d0fa,5d43dd57,85f6c79a,51a588bd,d14e0275,59fac06c,a449d7c) +,S(3cb6f8da,79964c17,73a1c749,e89542bd,94b4f279,f6061d69,a12ffe5f,1d4318b8,42e4c1a8,b4f45ad8,dc23bd02,faee2956,72a77771,6e6b1d54,d05ba413,6bb11956) +,S(5fed6351,d34518dd,111a5110,50de0e5,3c60a14d,9a80183f,66415145,bdbf5db,6b79a590,de07a4d0,b74140b6,49bd8375,da77e01e,295e76cd,56a6588,7746c3f6) +,S(2b88f0a7,81c384f2,b7c245d3,7c061128,a14132b1,fd0fb56e,7d3193e3,d95d3840,f787e7a2,11446e57,655b6453,c2244974,c1632bb9,4838a981,bd4873f8,89a0e10e) +,S(c7b247b1,5a3f55fb,35b0ac3e,1da36719,18507f94,e988efd2,47bb02d,83293443,4ab40437,34c65f11,236b6ac8,bc27c548,c3c8e5f6,64f3cb67,99e7b8d0,6db85f4f) +,S(ddfda933,8dcb9481,5c08a20,12c137e0,45df69d2,121d4534,4c6aa375,e03647dd,e95f79fd,2a116bd,5972e93a,c58712d0,95f91255,a46fa091,bbcbfe5b,57795c1b) +,S(dc8da645,5084b105,6c7e3233,b247e87e,9d15a6b6,677f8c29,da04fefa,371da7db,c2a345a3,fde3ec03,f6a07fea,8bd7685,8fc66d28,6637cc80,93132048,441449c3) +,S(f53f3b26,c5fcbfa8,f1263bc7,a6439056,a31547cd,3a8c795b,8e355482,d1ff5333,113ed45,e664b9ca,98d88f1d,a17c4fb3,5249e8ec,7998b1d1,bb68be3a,e6716ac1) +,S(648d23fd,c6d4b5e,4ee1d647,7d969df6,7d754686,9313ba39,1d89dc90,b646cae9,d035e045,239375d3,4e6ffbd4,bae0697c,9b336454,3261206,15edbe28,3e18962) +,S(92e6dd0e,5dbe580f,82473f3e,dd70b343,bd51bde2,b1ca91cb,1c483fe1,3c2839b1,cd90dffe,a5447bd2,592d1a46,5a09b391,a61ab01e,a10c9201,5ddf4614,b39577a5) +,S(1315e58a,7616ef8c,fa223ec5,49415a54,eaec0583,6be6fb78,2ef4ee60,f49f687b,729e0100,802d60cb,c54e1fa4,5dac72aa,2fe189a1,737da80f,1ac58426,d7e1431c) +,S(3c8a9799,9857fd85,e26b284,a3554e51,86b001d5,e6d075e6,c9402148,ab3e2a3b,464f0492,acc720a5,59487b3d,60502067,a722e915,a10412f6,d6c69dbd,28b25977) +,S(ea417230,cc94c5f4,436b37ca,e6f9ace1,bf3c7f8,ff03c879,f862ea76,127591c8,961238cb,8d95d50b,baccff1e,b9cd4040,6f4ec475,7026b3ac,e916067d,85a4faa6) +,S(8eccf9c2,f271079e,329a8c81,62c98af7,48496ca0,b0c5840b,ceb19a15,361a6ceb,3e259a19,165bf1aa,630c6a7c,52f37b29,c8ac5239,a132fa5b,b53d687,b17e56af) +,S(32f0fc83,b3503a59,6873c34a,b54eb6aa,7bf3d913,842cd556,77f398b5,4f0f296d,d4736d6d,906f78b7,62eda345,9cb5f7b0,311e50ad,cc9a1e26,655d8ca2,fb81a811) +,S(b7d63110,77c83a8f,a43c171d,a5ff7cbf,e4fa4cc0,b2614a9f,63831836,e0d7accb,6095f640,56441552,a8ac27ef,9c1eba64,8b84a5b4,c19237e3,5e285ebd,796be9de) +,S(ef6c904a,22df4fb0,a54cd74e,6563e56d,588b4f8,d6926cf0,6c32694b,be536954,308910b1,44cd37a4,e5bc3d3c,43715e22,aa7a0492,e0e9cc9f,b1095a22,65dbaa4) +,S(19e13bd1,e62825bc,acceebf0,b48e17fa,b375241e,6eaaae27,672d7ab,22941a6a,6f22c329,7005bc1,155308bb,dbf8ccc2,855ea666,e4af3bba,d3e3c90c,28b613f7) +,S(57d9bcdf,10f2f790,79349ae4,5f2980b0,9a98a7ab,d792a5df,967b7b11,1c8e9420,bf665de1,d9b7854d,e34f8fcb,1cc027b1,42ebfdf5,bfe4e65e,b5068a72,9a65db68) +,S(37bc6685,7c3d5e8b,878874e,3d1ed324,bb530a21,f13d057e,4e3f87ae,3df06b2,15419bdc,764db12e,f12abbea,c1ca7bbf,77764abd,72f5eec8,3a81c452,4c998539) +,S(1b30c5d9,9ca59151,33c012a5,96512c2c,b572e11c,a7412138,7f3658a9,f9c2cec2,dc42320b,2a0dcd0c,5404d842,17af8c9c,2344f751,16a5d691,1db0590,5ef39697) +,S(96831db3,50c55ac,dd329f4f,85ad4a5d,e6e89f21,85b93e65,b8db4e1f,c1efd0bf,86f43820,fd03739a,d0e75ef6,17072f2c,82bd8bd5,a1f8cef4,55e49ed9,cfd47837) +,S(cd2f500f,aa1f04a5,58309f28,f7acd542,bce7999e,a17cb792,66b93cd5,44e3d8ad,1f977080,ddfceaee,97c743c2,4815f795,27dd1b05,63ef009e,92994046,c8dd8c29) +,S(60392a7d,237c9e27,a3318ff9,d3552928,802f519e,60a783e8,ff32be17,4ad01c08,eb7f3147,df91d1f8,d8994afb,df297c44,88ae9e1c,539fe065,663d856f,22893fdc) +,S(f6cf77ad,df6ab73,20d68fb,9f3c1d7f,d8abb679,179b3c90,d22574f9,fa37ee9a,8ffa29c3,3587f716,6b5d513b,7df76311,77e81eba,1cf9b523,280c778e,2442ebd1) +,S(57c8015a,f542c514,595b6f4f,f11be2fc,944a0ea2,d26a4abd,1807142,da7cdf7a,b67c779e,80153cc6,7b6b32d3,3a3754dc,ffb692bf,d16e3e3d,2c9c673e,a8b05da9) +,S(7fe08558,ac184922,ffbd5c51,cae99931,2f58a17f,bb643b4b,fa1e419e,6b4fe462,7600477d,8bbf58b5,36e7e9e4,cd595858,7268cb5d,1599079e,5a88fbe7,79ec7a11) +,S(2e16f56e,73b39bef,d729b87a,d8e7c4e,3d81095f,58be177d,87bd9f3e,dd517b75,4ce271ef,57323a0d,baac950e,668f0981,77aa59f0,7a0211aa,69b4e417,375fcb7a) +,S(a4d4700d,456d7802,be781f66,6ca24a68,49200de6,d2dd97ad,9c2eba62,18ffdedb,c099f86d,e7ba0f0,e8f0e762,8864a4d8,9124aae5,af12f6d5,efed434f,c66e4532) +,S(b4764d50,50119e3e,a316c921,91e57ae5,a2327e74,187f145d,c048c199,db3fc059,e39be8fa,cc272129,3ec831ea,9016a188,4f127070,4fa4f092,c2f3d56a,4798653c) +,S(3e686ba7,d291abf0,ced5b352,e84fc696,c29fe123,aeb82235,5031931b,e7e2d0e,7b35c0ab,7cc3c81,f84caf9a,9321b9b8,5a889b07,98c9eed8,8c7dc4c1,a985d266) +,S(d7886f2d,1d4c70ef,c5961e56,33122317,58400105,288d3346,db49407c,d24a46c5,da645ee6,17c46024,b2e25903,d4d7dbab,c5ef503f,814f23e8,68d94040,54b9efc8) +,S(7cbd23cc,14654d40,5aa57ba7,1e375466,cea867f2,325db475,d3a7990f,ba17a40,eddce5af,96e66db4,b985541e,e638ecea,88d327f,edd37039,54ce5a1d,d2dcbc51) +,S(54626185,c279e1da,eecea0a9,233c2190,18f877bb,58614a5c,5d2b9894,17eb9795,6507ed85,dc8f8454,bf96730,5cfc7919,8cfb07f0,dc64b09d,928d0b86,e0e9b6f8) +,S(edcef155,1cbec2ce,2bedb20a,addabefa,59e68cf9,e2200681,89285c08,563c32e,ade67143,75592853,c296ab45,bcba4db4,bed59efd,9df68b33,b03d7a3d,be753179) +,S(e027a953,ff6ef9d3,f27db47f,bc91db7f,6a30bc5c,c834add9,9cf8d086,23380cd,1e6b244d,e18ad020,8e52d174,ac452a81,62eafc80,975872e4,c7e362da,cba2bbf5) +,S(a5006d68,db980242,54ea251f,3787d527,66dbf92c,aaa20960,e17ac6f6,72e2b2d6,a1938d7e,e854dd5b,94d98766,c0f74723,72d1e6df,5cd6dbab,fa63faa5,6a0b2b9c) +,S(327075f2,1a1fd9e,da69adf7,32995919,dbb02025,ee504e0,3d5e5066,8427be11,41a57eb1,c32e94be,948ee335,6d855fa2,611a4ec2,4519adf3,2deab381,6bc4debb) +,S(97e1a5c6,66694bca,6b10cdcd,7e95f74b,9c87ac04,c623799a,c1c7959f,1a3363bd,3cc9a48d,1ec7561f,7b72bb6c,4d671c25,56689498,42546a9,48c9b927,c8ab5e88) +,S(450fa640,a7374dcf,d11bce28,f4eeb4ab,d0a2d868,9f6fda04,7f790ed3,b0c07a21,eae88298,dc17cfa1,d482a1f1,70d1dd36,aa35a56c,7eba231f,c4c3ca16,efa77f4c) +,S(f5aa7340,fc0a66a1,2da09ce1,f4094240,d81fad91,87e944a8,a7a1ef4b,9e0a6559,1255b3da,9ef992f1,c62e7738,f84dba74,899d9810,9db522e3,49ad9fc7,2aeb5017) +,S(6900965,55197836,a7b23841,97fbd75a,8857ac8a,8b14ec8c,90cc94a7,8a42f1b4,1dc181d2,38c976d,b4627107,2be05f32,952b5bb4,e9ef4710,f1bad8c,171b027b) +,S(2b1aa05a,8ed84136,7e99f377,bc3760c3,bc3bc0f9,33555e87,8ff2d212,c3f290aa,47b25af4,1eb8bc31,915f5896,57f368bc,35e9000a,cbdd0a41,7b267e58,cc13e280) +,S(a4e6b69c,a18caff0,d993540d,3005e5db,454b1e39,d4c81a7a,b5315d25,d052d81d,7cff2103,9c587904,a42d78ed,c2e7a6ec,df4fcb21,17c89822,36fadc01,a15d70c5) +,S(ab44bb96,c74fca4b,3e39cb0f,d9dd7795,f728e1b2,80a7cfa1,7a36916f,b39d2582,59d91ba4,519fd8cc,9e773feb,b1915967,7edc0dd8,1e794ab7,cb76a2c9,9026f5c1) +,S(83b0c85b,3fb9c3e4,8c96a31a,e5442288,93472975,5d42fcc7,715dbc23,9de332a2,e99f5640,fb620582,fd833b34,2f086800,9652d739,2d6494b1,e36d301f,35a6da6e) +,S(ea45313e,624ecf81,676911ee,500bc0f,e5bd10f4,40e04788,41eb215a,ef954537,4ab8b00c,e2359cce,203bde53,b9e8a454,d5afb101,4d0b644e,81ad1313,6c0c7445) +,S(936c10e4,af823d96,9f7f7a26,98c927cb,ae6d609e,37cf7412,ac4cea1f,51a3cd4e,46f72bbb,10dd850b,8e3e5bdc,f0359f6c,bdb9067b,13d2b60,bf68e8df,b17b7213) +,S(436a5d5b,61aac3db,392cf8e1,36a89b31,847bb116,5d1e296b,36c90d60,f2e9bfd6,f81e688d,ae67cacc,1978559c,84a90f9e,5e342bda,e73647e,33655686,2f36d68d) +,S(9eb95427,3fe789a1,3a24b929,e54f6262,d931a442,2e61867a,abd84d80,4b315d7,38bfc192,7dc0e640,820fba,8effa82a,d29f204a,fec11c60,768f95a3,4d2ef59c) +,S(3826b6f5,87ecbc0a,cb6b4328,a00f63e6,ecb77a0d,bda277a,e6e4ba5,fa280d8a,d9435fe,dcefe69b,213e6eb9,eda63f89,db35e293,8ba3d6e6,57232f04,3a4ef271) +,S(a348cd11,ae0ea1be,3f993bcf,84364b9f,4dbdceb8,db3cca21,cd0f8ca2,4a1dde2,1fc851d3,db7cfaf2,6a1c202f,212b68a0,7078ebd,6d6441e9,1d1d4884,1c83ac75) +,S(bfe15639,237fdf3c,4e67646d,724264e,5d70d1e7,3cb70205,3c4e9a06,2bdf7329,902b37e6,1ec5e59d,9c3c09d1,d7767483,345488dc,e492e3ad,f12d04f9,2b5f88e4) +,S(e2fdb14d,81a1b6f4,d40dc346,c8205821,2642ae10,2707e944,14413cc9,c03dfa5e,3011b4ed,846e8551,6c66925a,e7676062,131e37b2,9b00c408,6c60a3e2,3488fceb) +,S(bbc659c6,8f476748,17663659,db4ac222,cf1119ea,ac27c38a,535ddec6,913c7416,1f8b6ee1,9fdb512a,fadd13a4,d3fe13b,48250120,975b8c7a,47798267,7954b954) +,S(65bd2a02,9866789b,dbd6f743,af85a789,ce9141b6,fd9ee056,cef4de8d,a2505521,32dbe030,1cedb7bc,5d5644a4,6c3e6c56,990b184d,710a770c,6b65f39d,3378f5a5) +,S(1ec0e6eb,8382cf7c,24d75a49,fc0f3724,e9318d90,9e05d406,a3a5916b,2278e981,e50e129c,f486bcaf,eac8ade3,c697debe,fa56ac06,337cd9fc,240cfc2b,3415715a) +,S(7390af1c,203da259,9e1aceb1,ea101628,c0399c92,33658d9e,10dcb10f,63a9eb8c,7d215496,60647b09,d7bcad45,1debe7d8,2c3adaa3,39bce418,320e0df0,977c3126) +,S(99a51469,7b913764,783ff7f3,7099cc45,127baf1e,d8931eb2,b4b71e81,6a1a533b,49c0d00f,7503770b,fcbd57d,bae748fc,809d21f0,8ea38d4c,8a288063,a7e2e8e4) +,S(e4d6e0b1,3b4804cc,cfb7dbf,ef1b7026,320ba2e0,c4fc98cb,f12fad65,8a0394d9,aeb0eaac,73a006fe,6a75ad20,3e0ce2fb,af5fc3dd,81865b6c,75075d36,3b761a41) +,S(488a4141,507ce5ea,cb3e1c46,ad23fb3,1a9aa058,2b553459,d885116e,19fe1a04,6bd6a066,43ef8c7,4cdeaf25,ead63b04,e82f8073,a5fd904f,d7235f4,cf12bd12) +,S(c0ffe24d,ed0ca54d,1ad1c2ff,d2dfbac3,2cb65b47,ca42e8c5,5d2c15d9,67babaa1,d15cc63e,29c54df2,19dbbba8,8df7011f,a0a330e1,6756567d,bbd7c6bd,7e7e9289) +,S(336d9b02,6c74e25,9dedd36d,d8fc5ef1,31b05d0f,f20bffb2,8305b923,767780b1,4aedacfc,9886a707,aebe6510,e1bdb89f,2b1b6461,1471fd1,b055e7f5,414e647b) +,S(98353eb9,8973a7be,3bbd3711,dc7b86e5,9a2a175a,ce8464a7,6aece2d,2dcf04f,a877fac2,27894a43,8f610887,305e1c12,8e45b4a4,3d6c596d,9807461f,aaee767e) +,S(cdc35e1c,17fff9df,63639cec,b54fcb0a,71c9fb77,c38eb905,3f37c361,36da1e61,a8ccb6ba,fe917260,df64f0cc,dc14a31c,87ae559d,7d629778,2bb9b668,9494eace) +,S(912cb1c1,5e62a0b2,71458409,39c06f15,5d7407ec,526ccb5b,5159a934,80111941,584c14d5,1723c447,cd0cecc8,6c70dda6,6fa710b9,8348f5ad,bf4bd587,37049bf6) +,S(2145e17c,7c1e8acd,99eec6e2,cff40341,ffd19568,9800d02b,5ddce693,c3bff3bc,3b8a71a2,40c0b373,95b0e9b8,99da7bac,a5c43088,afafeb68,957bff4f,7636356f) +,S(e4a09336,7e877272,2224f7e1,d03cdc85,43e0910a,20bfc4ab,3aa2ea94,8d0b1692,24b1ec31,33146f03,8eeb8315,afa6e34e,898f48dc,af7d0f80,328155f7,c2cf846e) +,S(fdf526dd,464d459b,b7f387ed,46402875,2b1390d8,4006ebb0,f579b36c,d328db40,4396e6c5,679216d1,dd2cf776,61264ec5,9373269a,a745c963,14147c0d,e53a3987) +,S(6c85c16,b3e37273,509933eb,caddf3f6,52ca4c49,4e751da4,fab5bfb4,ec729f86,9b530fc8,520b807e,5e649040,c5de9929,e592ce99,1b9f9cf1,21766f16,eb648d6f) +,S(4fbfae3c,596ee2c0,10c98ce4,bde00cfb,4e7da2,25583203,feb0c791,2ce28e58,fecf0504,ed0e364b,95edcfdc,ff50fd62,396b8a65,83bc1799,3e73557e,da6ff099) +,S(9bcc3b7b,80171179,3b683c2b,90e9c811,97c1d7bb,d3c281fa,1503513e,778bab8,da2eac92,878289d1,a77825ab,9859baa3,a887f009,d7d8be9f,3b70b917,41bc0b43) +,S(4415a040,e70a3b3,f5a95695,672d1314,855b0a26,3b5e3c8a,24e02f9,6b635150,d8a53b8f,cf55a9f8,81e92160,39afd5c5,6843ac56,39f3223e,c58a5869,da3986be) +,S(55e759c9,cdafbcf6,a38988fc,be203839,b617b536,2d537092,4d2084b7,45b2341,4a6933ea,ca8a3405,89952483,4a231585,3c1191b7,42eb95d6,cf7f82af,f931bb95) +,S(21021f25,bee23c2f,a57e072,bdc83e09,8d115358,adbde4b9,5ed63488,5540e9c8,9aef5691,a7700eff,7c42c98f,8dc822a3,4ed8dc1c,7f8648d4,ba5bfe3c,f9a8c9f0) +,S(2b4e311d,dded1aff,edff3c64,10d40b8a,c70771ef,6904e4a7,a28c7982,cdb376d1,145c30f5,6b255b07,fe43d02,b57f5b96,48b25dd3,6c792575,d20ac730,d709b05c) +,S(5190b21c,7e166100,c90dbf5b,e05d25b6,5ed91caf,904b091e,8b98b5f4,5d5378b3,b83cce47,1fbdc5f8,b50e3f26,8be0d540,1826d26c,216c1166,4040b4a,66650073) +,S(2b211ee4,54c65c0b,3647c7b2,87608189,88b1b009,77d016ae,1f2f4030,545b56f7,75743959,17d8fc1d,571a2563,47a61288,fff74669,b863f506,f9b70ddd,76854f5c) +,S(cb30b792,b41e0aee,3478ec37,ff6ba66f,a3caad4f,9418e1b6,d4af94ae,e64134d4,45571ade,cb3a9f4b,922b8b4e,c0d1c728,9142300d,daeb08e7,71b1aab6,228358c5) +,S(f4e437a8,d2fcaa16,e9749d07,89be67b,e4687952,7eea12e0,65c3cd32,4e4bc6c,fb52d28b,7e81ae8a,30de51f8,bdd0ee28,8e7647fc,392249c4,3449e62a,77ba97c4) +,S(a0a676a7,6a2a19b3,bc6a726c,193d9df8,ce85a94b,cb678080,545c53cd,df50d518,647fe8c6,b1b7bd21,b9816e5,aae40700,41198d68,a3c9bfe8,e437fc88,8f190a71) +,S(15dd4fea,d7f39428,b10544ab,1eaeeee6,75a2ba5d,15d8971c,7860a5bb,b7ff7b7e,89955ddd,5fab2154,7d231342,7b7322d2,53924d1f,1b1fa961,8442c471,9c38445c) +,S(a806b800,40294b47,6377a188,ed74dd9f,1bdea3f,f72cd2b8,850f6321,930ca522,4b6d7365,47d8b1af,697f1fa8,8ab3bb86,53e49aac,618910db,9c6bedc8,deb97213) +,S(41e3723d,ca086487,7a36ff9f,6e431ee8,58a983ac,f3c0406d,f31ba3f5,5b9f148b,8ab166ca,f27c58d0,1a80b136,dcda9fd2,2753c52c,dffca007,9139481d,3b8392e4) +,S(63b8a0f2,78558c89,d9ba3e58,70946615,690e0447,f8d4c953,ea00bca8,65bba3c4,321fad41,85eb8b49,53a95da0,f004c80,8c09b297,728b3a0c,2d300b32,43cf787f) +,S(62beb7f,aa8b0d3a,505cae5f,82229a61,c1ca21da,d2ff856c,8942a9a9,b14c5963,e3d9ccbd,7120c27f,5cd88655,3efdc43,a36f4c02,2f5b7045,1ac9d7bf,56bffd4b) +,S(b75a180a,f524b89e,be5d79a9,92f0313,bc133fcf,3cb2d99a,c65f2c48,1bd20427,e7855056,b11cc054,d37094b4,1e62dfb3,6a636964,633eed48,c39b8225,4a1d2747) +,S(4e5bbf6d,5298fc76,97ddf9cb,e4d063a2,6b51f93c,eef032f2,f03eed04,fe4949e9,66702686,40cd9dc6,54514a19,ecd80930,1871fbbe,99254119,476e44e3,16a12188) +,S(a9778c3d,f509f069,3baedb83,6d5fc1aa,b78e86e6,dcb61200,74bf6df,c173237,33a9c991,86a0f913,95fa0e50,d0a85d65,9230a41c,8c0be5d6,90ee013f,29d0cab7) +,S(f1b3aaad,16664be7,c49790bc,1a4936ac,757fb3e7,26ccbc95,cdb7bb63,cf570449,d1dfe1cb,ea3fdc10,3f218398,14626782,643426,24d3aa3a,52b0fb4a,7460cc06) +,S(d66cfd3a,b015d585,6a436ad6,a4cf019d,9563a05c,af4ce2d0,6988a4c5,9349f598,5ea179b6,d097d9e5,663b7305,6db940f8,d8e91bbe,fb951756,d07cf6fa,111efc4d) +,S(a532a4f0,924b5d13,bef0f30a,a982edfd,5d87a16f,2c0d8470,ea8fe3bc,da0ac7aa,c2e62c19,ca4e3481,3cc3de70,df50386f,74fd1efa,ac6217ad,5532a927,1f8d5c1c) +,S(a9ab2512,5df78835,ffbd08c0,9e07b632,81ebb3ef,3fe2fb3e,1a3e5ff6,28439b70,58ff55db,56e015aa,9aa43698,acc35df1,f2eaadb5,4e950c7e,3501a5b5,85877514) +,S(ce058f84,90813dec,c184dc83,915c6d5,53df174a,41ed6521,f2cff46c,460466eb,414d38c4,58817414,5a815a7,84f4f71a,28237741,97b37d47,c7646b2f,8af3e745) +,S(991fab65,30a2b36a,ab385c7f,31644de9,afe9253,9db603d4,737beaa9,6e406299,2a29b576,77c544bd,f156dc9d,44da01f4,5d373cfb,7ada94df,4f82af9f,e48eed57) +,S(ee6cc81f,bd034eef,cc841921,5ab4817,ddfdd2fe,a13f1e14,e91c3f2,af715d8e,b8f79ffe,48c3e965,21c7358d,ea5cbc04,df8fd847,a97bca3f,6893af3b,e386d8be) +,S(66a2d372,d6426657,5544d5cc,5141e003,16ff2dfe,813357a3,c5daccda,159c5ef2,b57d2d6b,91c6f48d,389423b9,af265e04,780000d0,598229a0,5bada3f7,4dbc8929) +,S(20a02dd9,f697de3a,15043e73,fc7f36a3,7a5798e5,acb4cac6,adf5c692,3a421da9,1e6cf100,d162361e,aa6ab61e,711e3dca,ab4ab538,2fcc6b97,33836c30,cdcf34f7) +,S(a0b8ca43,7818e81f,6be98b15,4da25331,9f92b79a,1ceda110,92e3a303,cb7bf552,4541bceb,f447d990,50663ca1,10fce2da,218508e6,48b630ba,a1abfe5a,3d543be6) +,S(51714ead,73dce488,7e15c25f,b5cba9cd,e0d5018d,db5b8ffb,9e4174eb,1cc52c63,47b794a0,82ef561e,bee272c7,d66dd201,a5c24831,99977097,73b9141f,9bdafebf) +,S(18ea2763,1fe4de07,3e7c41f6,a9921e91,7da195a4,13417a2f,ba74ac2c,d6451c59,398a0b37,fff6bba9,b33e0428,e8d7c93c,d31b3b83,2e955e6b,106aed50,d64df316) +,S(e1d58b4f,c39e1834,a61ec341,acfa985f,c3d1f0a5,9d75b0cd,e8b9057,17c5a87e,1b62ee02,15fdfdec,8fe2ab3,bb7f99e5,af82153a,dd9c7f2,cf299225,9c6b3c47) +,S(39793917,30d3c125,7dff5ba8,eb253dc3,2c19c541,11caf9a3,bbdec3e7,716c5a84,d6a1fb50,189f36d9,912cded5,c851da8c,be7b4ebd,41c2a1e3,f78aa4a7,f5f18015) +,S(49b49e4b,601b406d,381dbf88,8b02519e,1d57360b,24f2aef1,5520f17a,5b9ede88,fcf3ccca,2f2d7b3,59be7be1,9e45a3,a0a76e9c,d0e42fad,bff7e772,f969a38d) +,S(2cd8190d,b42f36d,20d88548,f0ff17a1,d64e38a1,2241e4c3,2bfdaa45,28bdc871,f729c369,22141490,5aa84f45,e5abfeae,2fff780d,2b16aa67,77ef59b,68259187) +,S(1fd0fb11,17385466,2da0cb2c,f1f9d19a,b5b642ea,45d1c6f2,30808626,5e41025a,3136a688,fec60820,abc7825d,1c7012da,94e928d6,dbb6c08f,5579181e,469ce0e9) +,S(f08e5f3b,d7f8b5be,18648a0f,5bd844e5,3f93d5bb,120d3871,7de4440,7af3a141,3dd8725,9067eae1,cab61bdf,cc94ea0d,bf7ea1bc,df1d289f,a526e15f,d9902fac) +,S(a0033c06,b997fa96,373a046b,b5c6bdd7,a0584cf0,1feec5a5,d3f5cab1,1c5ee207,eed61931,aacbe48d,708c2cda,2208d40c,39a577b6,3d4ffa17,d140a8db,c3e268b4) +,S(77deec08,68bea671,df01e29b,117791a7,83ff8f70,4426bb1e,1ea91b6e,62c16f70,b908a228,ba8f73f6,3271ae7e,f19f5e9c,fcc1fe27,c0a224b5,dc426b00,5664861d) +,S(2fcded5f,fe2f10ed,1a1bd1fc,63506b3d,994a03c1,c188d960,5fd5aa1b,a9aa72b1,155bebf6,f09a86f1,d20d7c12,e12a0c66,b4df6628,4ff9d45d,3fab7d76,6bbefc6) +,S(1f0c0b2d,fdf5e733,abcadd0c,9ba47c09,8667f8ff,77c2a904,c8310e20,995db3ad,bd711bcb,84cef55f,5139b761,33268489,70f63182,85ccbe44,8efd7566,577bc566) +,S(b4a8544,e7162c44,60dbdf3c,5edf654f,d603fded,4aa885ef,465ea8f9,a37dcf8,53c04391,b3f2e8b2,32e27b,2292cb38,99f8ee6e,dbe1934c,13938a3d,203c62b2) +,S(df0ebe82,dcdeb735,38f31cdf,ad307397,d00a900c,6d94221a,9d85b0b7,2c5bd68c,c3299639,e7fe2e55,447b869e,3a4acf8b,4a1d07e2,d70701c6,8a2f4733,88decfb2) +,S(eb04b9b6,d1c38c33,e1eed101,fd28f89e,f9fd460e,5b379576,ec7c9267,3c101c0,4f26d849,7f47f80b,2b13cab5,782d6e9c,f5ee7548,3974a4eb,8d8f28,f5e0ee9e) +,S(7bc9efe2,197cc6ce,ac94b37a,cab9865,7db4ee0e,ea7b9cfa,ce9f71d0,d3294334,d931bcad,52347906,a941d940,4b5b3a51,ad8fb493,1c15c9a3,7ff782da,5da72013) +,S(8922d1e8,f4bd52c6,4bd0c10c,66a3e8b5,1321dc50,cc964844,e0a41ad3,b73e0b9b,2b643cf1,7d23e21e,890e6abd,3337dc52,61c9eec6,d5edc318,2a02a7a3,4a41c431) +,S(42f2ede3,3a9312ed,22f6a842,ac46801f,139a7da,77ca0e9a,cdc19d1a,f2ddcc72,50d45bcc,715c985c,b9260edf,fdc8e342,ab95360a,6bf4776a,c70e3497,8f26f99b) +,S(7bdc9720,1862feb2,58879255,726b5e4,627549f2,92936ea2,f3b27051,4d6761c6,700eb72b,7f77adf5,76e6f0bf,f8406660,65372ee4,cad6269,f1ca8541,2da4fa8c) +,S(b9b527a6,4bf549bf,5e42e0d8,5228e1a1,f79d1647,d2102306,6b6d98e1,746314cf,8e1b3464,2a721f5b,93a2a2b6,802f30ea,e35b1539,c6d84eb7,499299aa,aa0cd93f) +,S(b65de529,34d47765,1c29192,93ce1177,d187c431,590d2d74,978ac258,75e3dda9,3d6fcbe7,4ab9b5ce,b3dc5535,13036457,f0755509,ecbd94ef,324e36d6,3b9fa529) +,S(18eab632,608751e,af3604f6,a3f05611,a036edea,3405aca5,c2e66366,ce7c6b9b,89eb07bc,94671119,36e11f05,79f91e6e,ccd42605,24bd7dd9,9d12b365,95074c5) +,S(a2611066,4a479643,909a1606,596d39db,c7502ee4,a31b40a1,4eb1c60e,ead360d5,4b45d74d,88025576,3262d9fe,38198218,85721a81,f323cac0,3e87f372,75814901) +,S(789454b1,2c108537,a019dc58,35cf25c9,c77ac0fa,f6001959,5da9435d,3c08a8a7,6bee870f,ed2859fa,cbef4337,8a0bd760,8841da03,252687a2,591ae0bf,a5829041) +,S(1977c206,fb4f65f0,11b683b9,c2e0df95,f81570f9,9f6625ed,7cfe25d4,62791569,de76dda3,c22244aa,95ff435e,5a9ebe05,9dcc79b2,75d91de4,67de435e,bf698479) +,S(891f0df7,d259d536,40fe6086,1ac34d7d,ec3b5ad4,c3f56585,189ac986,65f4222f,cc5e0bf7,f538a2a,1ee7a601,1a8d6b0e,5c3293ab,404257d,ba90ace9,1b2de219) +,S(8534bbf6,9262f81e,5926c282,f412b4d3,50169de0,3c5c5e55,c80a937f,fe99530b,fbeee581,e0bb0737,4cc764c0,be5451e8,f70cfc23,4d415cf9,fb08e7eb,557423b5) +,S(e5b6f564,4e7dfd3,7474eafa,354598d5,58fc897c,f010198,9e44b07e,a86f63fb,4368bbfa,68a83a8b,76de7d98,38880780,8e153cc9,b380ad0,b2c6ce26,6118da79) +,S(f9591a8b,11c3e514,983dc5e5,507247a8,46e184f3,b4e38e29,f7c90d4e,6c2b850,423b6436,2361f587,cca4e288,a366a1fa,957de393,f29ff2a5,1fe97a0a,d90ff6b5) +,S(3d3239d3,252f5e9e,ef9eb617,c9646379,7c995059,57bc7821,2f2920b1,ff72384c,1c3e37b6,fc9bafac,e52798bf,695eb4df,ef860ee1,a9b67df7,8f73ca9f,22a2e57a) +,S(f4089723,4b574d76,86a8e855,408612a2,a0a6ce61,c1f14055,201d5c67,bcc5dfb8,6b8283b7,3e72fc91,54aaac24,2565a79f,c3a62fa7,602ffaf3,c7453209,c0a56a4) +,S(ace076e7,aea9c745,f6be65f8,498b8b47,95058ed2,b0413c64,bc4b462a,2e267ccf,b62c617,377fa9e5,eb72ed6d,caf353e2,dc58f362,a994520a,31f1dac1,168b97aa) +,S(70ccbba7,408c6069,3230b848,eadb1d1d,b4e63f33,b4fdf6af,29d6fc2d,86ec161,fc1cd9a0,46d30e60,e4128677,654a80ca,5a4ff338,71fbeec4,d7782c7b,a14d6959) +,S(e6e7a8b2,7c0c5b0c,5ea58d3a,4af5abc5,9e33fcb3,9970aede,7a8de629,e758d372,d40d11c4,c8c8b5a5,d511fee2,793e2c80,b40df3a5,c98bd704,9a8db3e5,c98d4a66) +,S(cdc495c7,e0a02e68,329495b7,19e2c424,7910e9a5,cff6739,117b1d32,23216346,63664f6e,9306c0ac,f29ddcd8,7f1df6ec,d51fc68c,6ce422f0,559205b1,681583d4) +,S(2784e89c,2fd721e0,22ba33,3b824dc1,d0142f19,6f35ae4f,825975d0,7a2fc5cc,ba58a66,bdfa661d,354232b9,b25628f4,5a391e44,f00841db,4a335ef5,dc3b8cf4) +,S(ab699b62,ebcb7380,2141dd83,184ba3cf,c9118bf,f6af4d0b,9b98ecde,b01a2390,5e548210,b71420f6,c5d42547,da0a7ccd,2be0117b,79847e71,a609044a,34766905) +,S(781ea7a5,67ffe37f,b9fad6d3,96dd1eaa,2209552a,9835ae08,dda3d4f0,b75756ec,3c76d87b,46f63b97,e4d48174,4ba664cc,403a507,dacdf4b2,af9505a0,f2637256) +,S(75ab2d30,dd5c0a82,a5c04fe7,b8fee5a3,a0e1ce3b,e20b33fd,39d0bd3e,e374029c,aaf46147,fdb1393a,cb9f6f8e,694c1cf7,30536efa,b5ef921a,72215fcd,61914b9f) +,S(294b6698,37303da9,6d23caea,684ad56d,8712433,d11c7ced,85a10cf8,8ddaff0,af4d7ef7,feb73e9e,a9407fa8,b38bf1f1,e5861bfc,82f9cb42,22fa64f7,9b46f7c1) +,S(d8676aea,7433639f,c9683b06,bd97011c,f290f6e9,1257ee55,ea3c6e34,b824c8d6,710c8876,35d45545,90f3e5c,d6aac133,9017b2a,de71e30f,d71a5b71,97a7456d) +,S(5e4eca03,d90f5ac,2ac4771c,6d86a458,cee71427,a93b7544,fd8bee74,161270d0,44fe0143,b970f8bf,affa6d60,33628894,a93bb4a4,f48cbb9f,f0caf0b7,40c97351) +,S(90152daa,244fba85,fe708f21,29b55485,265c3a3d,dbc761dc,157f125b,8580097e,1694d2c9,200b8690,b59fa3b2,8fc2f613,8a6d392c,48357939,b9435739,83ef2cf7) +,S(67dd54f0,96f171c4,53fa953f,acbf2b9c,daf3a199,732ee0f1,91fb3c90,8c5637b5,5327db43,2d33676d,b6502236,5a338c62,5fd6c4a4,536645df,b36279bc,4ede74b1) +,S(c6496a83,8f95ff57,65a0f144,774cf543,2e770457,55efc790,142df95f,bb6c51ef,cc0041d6,604d1d73,92d86f01,d8b05b53,1177b51e,3a38b8ca,ca8267fc,bcd752a2) +,S(e799d70a,b163bfd,475e5e72,8810d9d2,425bf4b6,9461e9ad,933fdb6f,68c11c74,fc34745f,e29bee3f,9b0adcf7,618c6d13,e504449d,1a272b21,5ef9fc18,14ba8600) +,S(6d3c97b0,151a9011,5032aa22,1183d82e,820c8774,c3dcd75a,cb6f45ac,5d489d3b,5aa8bd01,3d15d77b,8f695d43,8cedd9ee,64b57df9,ee672323,1c2cd5b1,4ad4749f) +,S(86e53e00,1c1e7de7,419cf2d9,a4c6a39b,b96d8ec3,a82f8165,45658eff,a517539,f8b1b70,f2efecc0,cedda525,40663114,c24491ac,266df520,ce475f01,ab44ad86) +,S(a641c339,ac877cce,a9a3267a,78edb33f,75feb90,1f3c8743,47c50843,52a9880a,d88a6d4f,2b275ac5,3c7dc101,b7d69ca5,407559c6,afdc9009,c34a7256,f982163) +,S(3a0153a9,4d778266,4581de43,6de31754,b925aad5,4090cceb,9e013568,9d82511a,f6ca48,94935c5a,7a252df3,8ff06cf9,11154b0d,9f0ac55e,35a33d7a,58802035) +,S(e78024e8,a131c9ad,40526b31,d26e96f4,2c2e51d7,c63f458c,5bec2a61,472b170f,c540a14b,1ad7b18f,23493d9c,edab65dd,3a309204,976408ea,f197f658,9f4e0c53) +,S(7858c85e,8351f0ec,740380fa,b1924668,7f05a161,e2dd87cc,7df13338,a86a5162,cad94344,5313a06f,c382897e,7c444d53,7e658e06,5a76fa60,928d7e5b,36a6de1c) +,S(6643e28,1c5bddd4,35a68c17,965f334,ddb9da8a,d599a4b8,2f37147a,cb0263e6,9f9f9e36,8ee1aa0b,9bc8f928,284968d5,6f2dc8fa,6944ff36,d728b588,a72cf79b) +,S(5f36d937,a286f14c,96fe8f9e,3ea05d56,b3132523,e9a135e7,106592d6,b9b2254b,9d6377c,c8265ff2,dcebf753,ef2fd39e,9adb0e1c,3c86acfc,2e54648b,9056310a) +,S(212eb1ae,93d3a0aa,5f626f26,628503c2,310f9172,461ea12f,b17677eb,c63e2224,51e57e33,347f2016,c738d9fd,cf528269,bbc5afe,f5fd6fa5,1dfcfd47,4b6d1b67) +,S(526952c8,940b9f93,32848145,203ce261,dedb3b93,8e6f277e,9a085d77,35e9eab4,8657cfa3,56df2aaf,9a5fe2fd,81804ff8,1d9aa58f,143fa109,52e4f4cd,85a42918) +,S(20f1750d,ef762812,e4ba8778,c953a2d1,419e03c,b97ab6ef,8f98be07,4c1c099f,d2fee9f5,3e7ad2a7,fb20bf88,16a1ec2d,d9d97b4e,5abc060e,bd8e8b31,bacd485e) +,S(e231a3b7,ea011665,c223cfb2,7f13c691,dbc7f5e5,efb66496,da2936dc,d67ee910,aa645c2e,18e80131,f3e7316c,7cdf13c3,45e4f602,fb17011e,96eb5fc0,bf65d53f) +,S(7a4e4d52,3fd026dd,3daeac35,552e7299,d01aa962,687a02cb,ed7174b6,25c702ad,351c12b6,a7398e5a,ae461204,4cd7c9a4,7d2bdfc1,1f941b84,71eb3304,fdb965d2) +,S(fd2f707e,76965670,67ca5a5c,a0010c6f,9a858967,19a23eaa,5574d1c6,cbf137b3,c45a430c,1d5ae473,31f9b161,3ce55f41,df00e287,9e3bf8aa,1ae75306,f8315408) +,S(c506cc1e,8cf2f4fc,e1011d67,6d304877,6c2ac2a4,f8265373,f3f918d3,f02bb89d,8de6dfb3,74029b33,2fa47f6d,1f7ba609,bea33d6e,2f58ef0c,58641e2,a5d3118a) +,S(2ea7ad02,d9ccf999,86ab901c,3c9cde3c,ad1bb4d7,d8f86eeb,b8204b75,ab5c0369,11222bd8,6d750302,763648dd,daf76947,2b213562,494ebd9c,e4003195,eb0ed43) +,S(ab8cc192,3555e4e5,2f84b421,ba2ac134,f8a6023a,ddfd279c,4c338cd1,9e159c5c,2c48e3bd,cc268dbc,23e9fbda,6c449079,5a5b54eb,54ca8ff5,14d1f23f,8751ee1a) +,S(88fec2e1,cc7c41fa,91b48e1c,2a71089a,c3778427,f2cfce04,c54af407,f5dad430,ccb45e88,4f696653,5fac7a04,9b035c26,dce30666,fb1fecf3,c4b89deb,8b3ed80b) +,S(404587ae,1accf5d1,b42c3127,2ea0cdce,940522a1,c64c757c,11d3768,774b0d41,e8dcf25b,25ae61f4,f7368c36,9b351b35,63401deb,5ce55325,f96a393f,ef0e787b) +,S(8a701eb9,cadea8c0,8d0cf20a,b2750915,28a377cf,bc4d9097,51424d1a,7eb9ab8a,4f045982,82a9ac70,c3c76154,ca98b5a1,265e8b26,6f02d43a,4549cdea,caba5a45) +,S(c2812978,194009c2,9da141fd,b108a4c3,66e15e06,c6d64389,29b2aa78,89ad3d56,85409efb,6670e969,a2834255,dcb1e546,9c9db5e7,6e549afa,974936c8,b75db5c1) +,S(9ffd36b1,c810c843,92bb04d1,90b82be0,f8ea4b4,2baa6129,8adfb810,71d72fe,11e27a4b,a11f0df8,1a42955e,bfa9fe1a,6f25fda4,6a65fd87,1caa3d6a,1b38404) +,S(ac4a0044,bc92b017,cad07210,750eec62,39e43147,5e3feb25,f1c6f879,a5a48b96,41c7c3ba,731002ff,1e749853,344c1f53,f526bbbf,457b4de2,d5f88ce4,1eb8dfe1) +,S(c78b101c,157ca920,b3fc83bf,9d19fc5f,57d8086c,d764e142,24ae4e86,7b06eb5a,1a8d2c6e,b9d660ff,faf5f533,b4f3b8f6,b7a9d7cd,5c29c1d2,deb37f19,d38126c8) +,S(d08f3b05,572348b9,5f3c1c2d,baedd921,c24b0db8,e6d17d13,578e5ae8,77147cd2,27b5fcde,a067cbb,69ae3e5c,90a38523,4987dfaf,5dfe0696,91461e26,762713cb) +,S(40ea043d,5060f61e,b81db0f1,c7c5c1fc,1091cf51,c5808489,3d08c4b2,a5e312f5,2ef11ec4,2836302f,e02719be,259f1536,43de03e,fb659f30,84816a00,1d66b27c) +,S(f9d104bd,fb53b4b2,de8827b2,8242eba5,17db73f9,dbb1eec3,9f184ad0,358362e7,d7644a0b,6c371948,2ba707b6,d7d43f0a,68f4e3c1,4af6f167,296ccfcf,4d771877) +,S(dd276aee,14c34829,4e6132b4,9a05dac,fea5f8a2,41712b6e,5703778d,eff9ddc9,9be617d,5d92eec9,7fede655,39690d0f,4803f760,2b74066b,7bc98353,a5ceb7ef) +,S(8522f839,d5f17bd0,c67d11f6,7f0db8db,2104c7d7,785252f4,c8be84f0,61155d49,b7791951,80311f8f,6cb9b59c,ddca495a,e01f3edd,599f8777,3163a959,5a3ea117) +,S(732802bd,b4f3be8e,fc02a62,4614672a,51049166,4bfce64e,eb2030d8,b6b5b225,7bd4cdec,590eee1d,6a505f92,cc0b25ce,d9022d62,d1155f8e,35a6ab72,46a3c6f3) +,S(1b3641a1,2c4f8352,21e73276,b8bdea15,f5ffcbaf,c4496fc6,8fbdb573,5967af62,3d50ef5,e5d12b7b,2ede2e15,3b4cd01c,b824c32,7a8e1ce,f0fd866d,99664048) +,S(d3034e18,4e9284ff,ed6cbe60,cb66d391,c897255,dbbe8c85,2c79525d,3f01a7ce,b3666304,16ce50e7,2a094b05,85f1ba1e,11172ac4,eeb353b4,8c4f8f99,f9d09e25) +,S(8e7a8200,bd22f5ef,18fa4461,8f4f85a,61c6db2a,a3ad176d,65c3270a,69e9db17,841fe3b5,1cce979b,f87536ed,c71f4cd4,4a482475,19b35cea,dee4b3b1,f3530568) +,S(fcccb4de,29ab8671,d1aaee27,61421ae1,c4af5f3d,839009e8,5dab0b65,60a4027e,a0229f96,203b89af,d5b6ac06,9e977e71,a6e269d4,bea29c47,fa6bd22e,f7f993b) +,S(e68cfd7f,44358180,84c681f0,aead705b,9e74a476,1f99e576,9252c007,e44c8a26,add9c0a7,b027e2ae,42378d50,cf0d0eb5,71229161,8e9f13c1,672121d2,c428f82e) +,S(8b6f0fad,4b81ac16,4866de7a,c8bb3252,63bfe008,b726e9a8,72e0461,2c6891d8,9bad6508,a8d604b7,8088d328,ca27fe13,91493e8f,a6291288,92ecaed,ba4bc9ca) +,S(108a0caa,37ac1949,263178cf,20674616,470f8943,57a921ba,8cfd499b,d64b78b1,68150d22,be05956f,5ffe302e,1ff517c7,9b7c0452,6112b2a7,5a819f64,a265bf55) +,S(fed18333,844c579,1d32daa8,3370a5c6,ab8e13a5,2654a08b,42940284,c757edb6,67bddf37,8395ab84,eba29a97,6952ea34,b1b52c2e,d3f89e56,f839c1ff,36457fe5) +,S(bebfb39d,ecd56cf3,50bed6f1,a4cde24,848b2d84,23518da5,1fb2ee0,90170a2f,1302b4b3,bb86f887,22925fbd,e146d948,5acb2db,a61e751,97dd40ca,2ba11573) +,S(67135aed,2a58d3b6,c4470d3c,b4da7b56,af8d5057,9d28a86c,9c67bf28,181e716c,97c40aab,2786e3ef,dc8f3e77,8f374446,30fa2f3f,53eb040,194809c,8efd6664) +,S(b00eb5bb,9566e5a,cdb09dd4,14d6df03,c3dea75e,90db8b9,c37928e5,4354f90e,6d120e41,1cb6e674,e9973aa6,3999af3a,ccc3a670,112c964d,8aef93ee,e69b1258) +,S(721b1a6c,be8d2022,f09db6d3,ba39b2d9,23337592,de9efbb8,ecdf3384,f3bb73a2,cdb41b62,631a0f47,75ae1f2,e94f8075,4acbb2e7,950be088,5b458c8e,4a05310f) +,S(cb8d78ac,15b12ca,8dd47cde,190c420e,42f60b83,9489bf71,d10a3e44,df8a0837,80be31a6,a3073675,e8cd3846,cfb2e127,abdc448b,1081a2d5,d177ccff,ea96dbc2) +,S(491cfbcc,4919d884,d23f3ae6,6d143e55,37e5c52b,e2ee017a,afec785,6c781920,76321b50,26283088,d344d29c,c4d9f2ec,3ba714d4,edc1bee6,2b17ecc4,56874189) +,S(6ca33ee,48992263,23aed33f,f822303b,8145d146,c5ebc27c,fb3700b3,6ab507b4,96b73ab3,f189d064,1e0a20cf,d09522bf,8c376a0a,9c9dbec6,44e3e8d2,c0b11f4e) +,S(6d1fdfcb,3c48971b,6cbb33d3,822cfa68,1bf602ef,98160f89,97ccaba8,19d842c7,82bc7c4,66088262,5e57c062,96f04600,fa555ac4,5596b769,9c6d29c,cfb1713f) +,S(a3e81699,1cf92f41,a69ac3ca,36b23796,daeb533c,3e1e9b55,c88e3fa9,fea43554,87a6fa9b,e802467a,fa206b0b,45405309,65887df5,786d20fa,25e94832,312855f1) +,S(fc84abd7,cbfbc329,4eac7438,d50b2ac4,7f570e4f,3f2ffab5,71f8c3e8,7dd49c70,7447b269,71356b0,e59b3f1c,d99b6416,589f6c3b,a566c75b,6915377d,a7f73dd9) +,S(beb23240,8fc515ba,180d3125,2eca15a1,af4e02a8,73a69769,53982408,355d1b1,12fa480d,6fa8a9aa,e5edeba9,31b765b0,e2bbe6a1,5eb9e95,b3bc343f,519c1459) +,S(68891679,cffad7f3,19a373d7,1bc02529,a50e63b,60434f13,df760364,1a99eb8e,40b78db7,6db28aa2,d2ea7c1e,86d01503,7736ce96,2e6ec524,da07b5b7,68772fb8) +,S(ec435ad,1ae7b040,fde2f736,e8fb5d53,b42e2fcd,428e9d96,ed401ee2,bb068817,13bf6eaa,1283436f,4a828a85,66c4f795,26860c45,d650fac2,44bbec8e,fc96f9f2) +,S(679f5afa,661f688b,7dfb7ce7,f77e4904,60a75576,6216a49f,374594d0,6bde3033,ff3fffd9,6c254fda,558f14bf,c8f05cb6,c8fddd32,a487d709,4e0fb35c,6f617ca1) +,S(9febd8ea,5dc26e5,fbf48aeb,64307b21,1aa2fc02,eceb6d68,ba89a652,f633be60,c0867bc8,ea6238a4,ad36e785,cbbc5e0f,3fc5dc96,2c778992,72be4364,8e630bee) +,S(d886b154,c534b48e,db6bed69,6dd8f9f1,347b381,1b593acf,e2137c93,600b1601,a4cbc118,aa07daa9,c4e246c8,efeaa3f7,d74028c1,93b77964,f10ab5d6,f75d077) +,S(af8ccbef,c187a3a6,83f6ab6,8ff217a1,96a8e27,dcec349a,b7ee4a74,446e9d47,3ee327c,99880392,7da5b625,be554e6f,1662a733,25a8fdc4,990716cf,6be941f0) +,S(1e9800e7,891c261c,a3451d22,44a54a84,42d33458,a997b365,4a6198bc,7dd3d26a,322f8986,bd6ffc0d,d5f1a321,e82e6b37,978120c9,66f753bd,5669a773,65a215d4) +,S(115c62f2,9f008009,5840dc97,f3a90f30,aae4ffa6,a2d255e4,15d64c56,77150044,4d944c12,7e37ce5e,fd7660e0,1e4fdf0a,cceef95e,8bc2da14,a8fd7e32,ead8af95) +,S(1cb9f9d,acbf79dd,b34d78e8,e51225bf,3f53135f,f4282ba2,59855968,2ae0178c,8fd3a4e3,7a189bc9,421595e2,bcf234e,d7b602d0,855de84a,50c68cb4,bd1a4bf5) +,S(62a15bc4,63e4c167,4bab2a5b,dc991e39,af86d258,f9de770b,66a79ee5,2fbfe357,b78fca9f,d9467e4d,256da69f,e6f45522,d376fe41,e8ebde9f,9f7bde4b,8ef52749) +,S(574a6ce0,5bf31948,31570914,a73bed4e,6bd2c82a,5a19dec4,54b11e93,5edf505e,e7b2e2cc,cbd6f338,f0ea60b8,c1a9a50e,d02dbaba,8bb5cb78,9b281a97,5c001318) +,S(64c6e37c,a3a361a4,baf9ceda,d9ed24fd,c5ae50ab,560e6319,4adad1d5,264084e6,e6e42f56,56b6b404,9c54a07,f35ae004,4b7a480e,8271c8d3,f7996ed6,68116067) +,S(49065a97,fe4a8de5,55646af8,475607a7,71b27746,ade7fb11,55308107,4900fd84,985d689f,67a03090,9ab8eb43,c7b2767c,94e7217b,fbb43342,83485f1a,d70ae7f9) +,S(6fd32ff9,185790bd,876a683b,7e889f32,8500c91e,7e5889aa,44394caa,330999cb,12fe8084,1be1f25d,7a93784e,9c6bf01f,efbd6493,8f1a678c,64497a8c,85d2de3b) +,S(4a67d4da,48189c8e,6ff34812,f7094851,2d6d12c4,26fdc98b,4ebb92b6,62d3b185,58ea83f2,f6e5b777,737c668e,efa71b8f,3e50095d,389a5540,684cea51,7f8150f5) +,S(ea1ac931,d3a92770,5d20752d,b9adb464,edecf4c5,95c20c85,987e1beb,a79f4db2,cf689b0e,25aa9601,58316597,b06fd91b,426b5753,68a48830,9c3f6f34,6722b83e) +,S(8cb35a6e,248a0527,d572663f,7abc99c9,61414af3,f196c6c1,2de4dbf4,dd545c5a,4b32e4e0,47dacdb6,66edfc3f,a08d500d,c43292ab,f36bea7c,4a3952e6,680cfcfa) +,S(247c800c,f150c2ea,cfef8dfb,f4581560,84318684,430788e3,c8d99ccd,d27f21c8,cc82d8dc,914fd1a0,e626462f,5829eb7e,a708395d,db3e70a7,57942e85,1f1d0db6) +,S(6543d7c4,7fb927c,fb0f3566,6103708e,5e0c4a4c,ed202482,c9f48647,33426673,e252b29a,69b4ad75,814ae0d1,631ea750,20bbc730,3fdc819c,88d8f3d4,7907ebab) +,S(c4f11255,d81c33a3,a09da4ac,af5c9c16,d5e759db,2e1026c2,9b3556b4,c388e6d0,e2e636a8,1af48ab5,814e5530,e48f518d,95379eab,186e94f2,4fcd551d,1ca8fd49) +,S(cf186a9d,85da39b7,7383d92a,14af5a05,b082848f,c9e60598,1bad8ef9,5e019eec,cabde21b,c860fe08,53f672a1,64010968,6b49f37e,61cc642,48f455bc,b079e3fc) +,S(e6c3e353,ad69dc13,e595c2c7,a88b8fd8,fe9d1d47,3d45f7f6,be9ae903,86ea8f32,7d3becc9,4f455c1d,21610800,a4985f33,96b5a920,d5ed2b57,2107c28,ac690be1) +,S(3827b3a3,9bcb554d,54695dc6,bcf22911,d1ba01f7,730d1c11,8d6a7b2e,c1060988,32413047,b7c9ee83,1b818c5e,cb8f4c10,c800a362,419227bf,d9f50aff,ca25a211) +,S(ff37cd38,a4b149a2,a8dae898,c40b0ef4,4b55ed85,11b5413e,48f5608d,cc6d24fa,5e615919,8f4c6665,7c3a489b,86853a95,f3ffded5,20433cec,a6832cac,b9c49048) +,S(bdff4fc8,5b492fae,1ef34322,9e83b751,d9e53c7c,455141de,f68d3e8a,320ce509,e8fd9864,9dcc5d66,6155088a,29959e12,531f8a4e,44d70b8a,43b4fde0,8bde3d20) +,S(fd6ba29c,313ff2d5,7fb2719d,9018f11e,e59d1007,24146aef,625a9b3c,3bbcd0af,187daf5e,913310c3,39f584b,3196b877,5018dbe8,1e38abf6,de73e97d,f5c1079d) +,S(ea3c422d,48606929,ef72ae1c,3ac30178,4f4b5f78,1405fb62,1d0b710b,1b29ff28,e458b41e,c6c3e06f,c37637ee,fa316e25,d10b377c,ef99aaf2,564fef92,33bf208c) +,S(46537133,4982d600,8d228924,f0b459b8,2ba8aed,af1b3c39,f87053f,487b3978,fd96654c,13189def,f243bb29,e2143dca,3b2444da,3b895700,6bbff65a,4dadf3f0) +,S(fda493f1,1226069a,ac76227f,9f9e1051,b5b29b4a,a2d0f9dd,99637176,8578d8f5,5a33593d,f63d136d,f43a69d7,f9803a62,e04e55b5,25633a06,d8eb86c7,e1d46fa3) +,S(74fc42e9,5f09018a,5468e283,5ea36fee,7ab5e757,c57200cd,966174f,39f3e4c1,ccaa0b40,3802c914,da86204d,45f8f5d7,3760e075,4bfd10b3,2a8b2521,218751f0) +,S(98cec4a,997b79a0,cb818da,58e41477,d98f8c2,8140fbaf,e118bb1a,ec483d53,2fce6058,14945cd5,367b82fa,182c19d5,ac422d9e,840eae4,d81689ac,7eaaec08) +,S(c742ddb7,89a5cbdc,2055119f,697fd575,e4fa1540,96cc5fc1,3c2374b1,13cba60f,9b0af128,34a990e1,d402474b,e6dbaa1,b32248f4,f2b1d680,13fa0dea,46f3532d) +,S(dba2e04f,9a78066a,b5897913,6c4c7224,5b5973d8,3b13819b,77409fd1,e791eb89,34747922,3adb5f74,168240e7,a178f077,aa0e90e0,870456a8,89dfca5e,633baf43) +,S(6d4cae43,6e598586,7b984dcb,6c2472b3,6cc47f86,eb69fbf,1069c69c,42f6538d,c7fb58c9,f0c4f7ce,dd7c53c9,23efef52,125c2b67,d67f1ced,b98bb1f3,d5e0a84f) +,S(4c457f6e,d5b67b05,5b664ed4,3bae291a,9ec5b539,6fd9410f,9cc16a03,b8523982,492e5bcf,20aae19f,5462ed68,2a72827a,a435f47f,ec218e98,8e91fed5,20f344eb) +,S(4d7e6922,34778a41,f58033bf,d74ae295,51fd8ac3,64cd817,9cdd441f,cb46eb3f,756ec0b2,ff5c9d3c,3a453879,17ada4bb,6d7bbd07,bd10d2ad,c092f46,c62750c4) +,S(ac5be50,69bad479,bdbc565e,ab74ef94,4a48399c,a4af915c,fefc47e3,7d464b2,3246e3fe,bc5810b6,37722c54,69ce45d8,8bad5daa,529b4381,928b7cfa,de874345) +,S(7da88d41,21350139,bfe6a481,2534b25a,24f91e0b,60d882d1,5d69e840,da3995b2,9888ad8d,62a7a4f8,700bb03c,f9772432,6a2d092c,98adbeb5,cbc20348,f0b9c587) +,S(389ff31a,63239cd2,c9aa3eb,4d3ceb49,5d3b922b,5d137d4,e8b52be3,b1abf149,b0ec2a2f,bc840bf2,8a22ab22,b76c1485,42f54ed,5515ad49,dc3c0037,38fe7c93) +,S(c1cc1acf,5fb128b2,ea65afaa,d3d5736e,a94b2e49,8dfcf6f8,f3c27a5d,596c4370,8903584d,1986ad31,6cc6bf2d,aa2a88cc,ecfec6a9,c1bb153b,27c409e6,934fd38e) +,S(399ace52,b92768a2,a4740be8,31c96c04,2a34957,e91b3cf9,8b3518b4,87c9aa3a,908314b3,abdb3a47,a117ae09,5d609fa3,fd9af1c3,ca04a869,786eb134,48800005) +,S(69543fd,8c9d01a5,702d9859,305a52b9,470cc606,cadbc76,d8283ba9,ba06d0a2,e998e7d9,a0c372ed,2e348f17,b3298088,404eb1c3,8a901d1b,6f4e8a19,731057df) +,S(2172d54f,df9b46ef,5a1289a5,872b1c28,911e553c,f703ad6d,400b6af7,e8200100,2121b47d,3b790e5e,c820df5,772b226f,34a8fbeb,f642b5d9,edc18971,1b687a8c) +,S(f606f7e9,37a324e5,d0e6847d,24923965,cc27133a,10e43f28,6d45f7ce,9d5e8b56,70013fb8,622a21e2,d1337944,89324a13,f5b80580,91ca08d1,a30e7571,3e86e007) +,S(9f1520c2,dfceb64f,1f432013,b342af35,2f95bb2d,d49aa14e,dfcd8e71,7c40fc99,fe8fca26,feb1f004,8fa4fe3b,764a907e,d53dc0a4,10308199,6cad54d3,898e863d) +,S(6670af6e,7587676e,eb0674eb,75c93d38,53afe418,cb286605,18a9370a,231eaa08,2993ff5e,dfc071ee,97fcee9,bb12fd8,fb59fccc,524d8537,34ba4cee,4beb09f3) +,S(15c6d58b,345cc4bc,e4ca4e2f,ee954c2d,40650a4e,ad6c9524,65f59a94,b3898350,894c53b,47276196,65a0f3ad,f528d2c0,221b12aa,73df2cf3,a6b0e778,a363cf7f) +,S(bc4ab6bf,60dbde42,c9293ef1,75f85a48,adfffd20,960ea454,8a919977,75389621,fe28a0ba,24a4e2a7,45d6bb68,ecd71931,791a74,61549a44,37a34761,19040e42) +,S(5dab1c63,23fbf2f6,e495e87f,1c703ba8,64f20e50,4007dc80,54ef2e11,d3e6ee1,1fd0c200,cf4460bd,ae6db415,3e68bffb,e7f01ac1,6b30e305,9c134a86,52695d93) +,S(2df1cd57,d7357c44,7be28dbb,c339ceb,5cea0bd8,e702fbb2,dd67c189,6a8acaf2,33192011,20cbc8dd,c3b9c031,94db76f5,86eefa67,995dd741,492f425a,e2f1b9e8) +,S(2d81ac17,5a02daf0,83100585,ed2bac76,3d9f9764,15356fea,584ddef,60d9ab8d,f9908cd1,3b1785f5,78297fd4,9e6c26e4,21052e2c,ce9f51c9,cd58183c,2df9a0b7) +,S(1fac1762,fb732e73,2440d40a,5356eb17,67b1e7f4,f11e8db5,9ef90702,437dc097,edd5784c,a58d5113,2b5a08ac,e75cb30d,5ae7ce27,129f158,4df5d95a,7b9b8729) +,S(d06a0b5b,4694b90b,80c93e88,4aa9086e,45cabaf0,99b15a9c,c2b19678,38c8c98e,4cad46ba,2777b24f,cd1b391b,222ff3b1,14fcc814,5f78ada4,98620cfb,747def3) +,S(4928df27,590240f9,9907928a,cb6f0695,870059b8,cf9b1d69,29b152b1,7de53445,b773e60b,655b3b0e,7b0c400f,37dc336b,240b83cc,286a8ac5,3dba751a,3300d505) +,S(a61bcdf9,cd541811,4ba99261,7322827f,f945d450,c331ddda,1b8639ff,87a28390,3e771d6e,d6fdbb28,54601b2b,301fe24d,8e61b370,ebe644ee,b40fe807,b8d93a68) +,S(535a35b9,3e2fb7ab,b3b1228c,a7977,fb8e34c,107f093f,9f641b15,11db93c3,2471ff35,5657cc8c,38283fba,27c2e3c2,92475fb5,e8d52e96,f0c9fec9,b2ff786f) +,S(e68a652c,e0140f29,376888bb,c6be70b0,f23ad42a,d10b8acf,a5f790b4,c465d4d0,49d7390c,c368060b,3134ecb6,dad52120,ea6e0c7e,b80c94c,6cf9c1d,3c1a7385) +,S(e8939e7a,74dfccd3,ef17855c,3ea20e99,f5f05f6f,8bc4757,6b4f0e99,37e25fde,6b48a73e,ff0a55b7,c90c0fdf,449e4c2b,3773f454,da2c388b,3e0829fe,d661b173) +,S(496013ea,31451874,800fa293,a6e70ac,7fe16483,67c31daf,bfd00faf,48751543,b5715111,8790556f,8a4001cb,282b3312,581e1508,b8537f65,6142e2f1,ab8c4332) +,S(444735b0,b1efc3a9,56957a07,22604d09,bb0f6c29,e7cc5201,89ddc3c1,804b1e1f,b1ad60a3,461abab3,a9732a85,7050e55a,3e9a439f,32a5b21f,9aebe400,7e59d6e4) +,S(5805ac8,4acd7d5c,f85a40e7,4b9c98c3,e8882189,e77aa475,da3b4114,3ed9511c,c952923b,fbba533f,7bd70dcf,d8d69b7,1ad0022,aeae363,bb48134d,6a3c07c0) +,S(8ac11518,30fdc785,5e1c53a1,2b690202,c12619bd,c46c8898,dcab77ef,279a9501,29fb7cc7,7867fa0e,1d015493,abfb2000,d5deb3b8,ddb3053a,d59d6bd4,a9ca4fa4) +,S(aa47add3,e42998cb,68fc8a7c,cc7beff5,4d70226f,22f41ec6,7bd7b444,aab542d,cfa5ac81,ebefd98c,3af4f9e3,9f67459a,4f8e8c4f,c1959816,f771e75c,c43683e9) +,S(f3504127,a27a14ee,92133f75,364922b7,d3984898,e433d3e4,19e88db9,e599889a,f1af9f7e,9f9868a8,84ecdae5,47bcb886,56c6d423,a066b313,c09cdf08,601b45df) +,S(33730f44,edcbe688,35887076,69a989a6,272d3ce5,9261b98c,7c6eeeed,7e4bf08d,3f833928,d87716d0,4b2a32d8,1bd2d789,b6fd057e,cc70a794,8605cbe6,e1775a6b) +,S(3169d6dc,8c06f584,880bdf07,29249cc0,a33d417f,be137478,6e5b4a14,3edfb0d6,2a91422d,ec563839,ba782f59,68f428c8,a311600c,2fde77fc,ea163e86,aa74ce5f) +,S(d8c64582,13aae81,cf07a64d,5a8dc3eb,c8c43a45,d57f8e9,80cfccc6,f02a4429,c4873051,968863ae,8c92f195,c9fee3b2,c4773d47,849e2a7b,e7f48c4a,93822aea) +,S(ae67101f,307e246f,7a7cb039,5631b884,7e59c8d8,14da3fe1,7891f69f,402b6f11,f5f2dbb4,a90b09e8,b96767af,a9f9a357,1d3900f9,7d8222ae,e411e1c7,11ed44cb) +,S(a9e87231,83ba8338,1c0a7dc9,c81a2c5e,b4e2ff43,82574f0c,1a9d7f14,81dbd8ee,96810599,90d90e33,d69fb59b,ee68344f,333153ac,3d73a335,4b7ddd6f,96468901) +,S(616bf21c,ae7d40b,2694f43a,a33e6848,694ef879,fb5bc48d,ad141e34,e43376f2,bec18f83,2aa8a0ba,83245115,3b5a4faa,4a27725a,a3d9e799,bf05dc94,9880fc77) +,S(121e8335,601f33d4,78a882b1,1e9be5bb,39be47cf,701a695f,8d351098,af3f5c2d,2e992933,18faa04b,7d85fdfd,cbff81df,10dd070,4485b659,f682a298,4d03b3e8) +,S(bf884797,3025ca9c,9413948,fef1bc48,3a83cc28,4a909933,25c8c97b,b89f1141,1953a326,3d26d1d7,86d5ae3a,7838d125,6de24760,6abbb63f,e2713f6,6ead5386) +,S(7c28e9a6,5a3800f9,85c94d4b,ddc75cd0,dd1b06e0,faeda124,e86600a5,6af811f0,a3756db7,e339f03,8914cedb,707427b4,2d396fd6,f2e43863,13414863,8ab5c277) +,S(e5ec6dad,45e62cf4,1ccb5bf6,bb92387b,46be0791,31bc2b9c,b8b93172,90a66077,d0045ecc,775703ca,f854bea4,be3c4d21,2bacd092,7a210eda,67c33aba,e44f0e19) +,S(6fc96e1f,92039545,fb31c352,544a5974,7036263e,34d8e043,58336169,118c2fed,d032de87,1571de8d,96b0457f,84397e36,6ca7c20d,d90d089d,9aababad,8b8a9a51) +,S(89e3d351,1c219c47,42e79091,47c13146,1db684dc,e1a1149a,cf49115a,746e4133,aabd970b,672e8468,d84b53d2,303d57eb,52fde5bb,bb224b03,6892f918,630c5c90) +,S(2b7a60f0,b2136453,878eb44e,aadb8da8,c043e947,fea1de8,4ff8e18a,390d5890,c829b16,1ba032a0,59340709,1b38b365,b806686d,19026f41,ae8bbab7,d04a5244) +,S(464b9f1a,c2c2aeb7,e6f7c3df,a8782f25,bfeb9879,9263a9d4,2f872050,6c9627e7,7c260bf4,59e1bcc2,df7761db,e503875e,9a23a107,6cb4e8aa,dac5b5b5,b6f7502c) +,S(79253e30,43572295,e331519,e774b670,b25141a,986eb5ab,268b63a2,759b616f,da609d7a,55b62353,b20f5bae,969665ca,ed6467d8,f2577f69,10003586,e27f88e1) +,S(634287c,e900c0cb,5c2b1967,f55190f9,19acb09,43596773,87642d16,dd399556,a2350909,8cf01601,30c042db,253334ac,223c473e,312bf758,1fcb461a,c3debead) +,S(761121c3,8bd8fd5b,a3418557,f6a75b42,d7ef2f4b,91620223,3587f183,421bee98,4014715f,f9c17435,10e6eeb0,1bb29226,76070e34,afaed040,5c897bdb,52dd530f) +,S(2618c8c1,bfbbdf1a,59da3078,4e50a2b0,77990fe1,c2f074b3,e63dd606,6d453413,ae539a37,20f5b6ab,18ce584b,4ea3cb46,4bdaf35b,326fdab0,58169ddd,32a48da0) +,S(d429fccc,10c6f025,fc7a57e1,949308b4,d91699b5,c099820d,ab31f1db,90f685be,da2b15c1,ee155918,de414ae9,1be51688,a8df043d,6fb6c964,689bd4e,5a648edf) +,S(6a18e3c7,c04b8cab,36e07625,ba2daef5,bf1e0cf6,36650b6d,56dda8d8,e4e8b705,66291330,f56c96db,ebf9c9da,bc52541d,20e75440,25fcc49e,d6215d8b,c0e94eec) +,S(985b6846,d1c9f3fe,5482fa4a,d6a21c65,d7ca3eff,a41b3220,1a37a122,f8f97756,3e2b23dd,98e18f6b,76a2f2c3,2c543f8b,bb9dc6c0,8d391592,d56f5440,aaa3a92e) +,S(948a0ddb,4ba838a4,63eee339,5c7bcca2,96fba8e3,9654ff33,55c9296c,9e6e8c64,c5b34c25,da19d83e,b67b69b5,2e527d61,c7b705ed,2f794d2e,12c4325c,3cf5c1d8) +,S(4332d02c,1332b03f,b7a46b47,e82617b2,a437fcc,694868c4,256aa40f,a8a3de2e,e6e5196f,5f9c555d,a94327c0,39411780,393985ab,73765de1,e1bbe0dd,8aaa5289) +,S(5dc977b9,2ac1c895,f3150b68,a8d69f5a,f7e12dc5,6b211d96,a67cc3fb,722a5aa3,45888ca7,4bf1ec9e,1cbd2912,3cc696d2,83260c34,3410217d,10fd0b95,9d07844d) +,S(5b9cf534,ca94f124,a9b0cb6d,f0003924,605e3bd7,d063a2b0,c6e1a81e,3fd15764,44e98dc3,b0516ffe,bda48790,fa0b2d67,f9ba4b48,ce88bd1d,d81bcbc,897458) +,S(84353007,3a63714c,7d26baf9,c895e2de,e2feb4b0,9746ca66,97fd7ff6,d5372f59,d7373a9e,37689d44,4cb7361c,f2f3452,3f9fea7c,826c62d2,3ca3f368,b0c7a175) +,S(9cf0000e,3eef9b5c,89c8485f,c3826e78,8f1a28df,f8746619,804030fa,c21e6a61,2b3dab3a,d8684b2d,94e1fcb9,d2005c20,2764cd76,aa91e0a1,ac2612f7,fe91543c) +,S(bb292fdc,5897188e,20888b25,b491a757,8ee02853,83aff5e9,99671a24,4ce1235c,d3b6d9c3,50ba7863,bcb83990,95893348,d5e9b8f7,27e1a3a0,9e02f59a,412507e5) +,S(95d6d1d8,9835ed57,6a88a21a,9239b623,efd2bc6b,f27c5ced,2d770c90,fb7d680,c91535ad,39db65ce,fab4ac72,f3ce692,b39e7d38,c72ce468,e40bfe06,6cbaf048) +,S(cbe3f20f,a164ec20,9c2c3fb9,ca0c95,15ea3787,df2c8fd8,9ee50a3a,85d09a46,cd589754,faea165b,48e22280,4186e6ed,734a2251,a6588e8b,8252eff9,afb9a28) +,S(3e01dab9,9bab9e20,59b1c215,ba67835d,2942f4,487d6094,9f39e293,b39f604a,b41ddc32,33365044,ee350979,8679f3e8,d0a556f9,e8b6a267,bd5cb5e,8523d481) +,S(298974c0,5543dc96,15f1cfd2,e4b6331d,c798918e,15e3f350,97213a64,76afef7a,68018355,55f2d8be,a74e02e8,747f60b2,22354ea3,62cb657d,e5cd8060,7a99da2f) +,S(1d70674d,54763c96,ea3764b4,641d14f6,2aabdec8,a093c3df,d936793f,511fdc61,9674adac,697e44bc,6324378e,59753f42,80774101,623dc10e,8a4eb4be,38dfe632) +,S(75220107,b43ab970,6261875e,c8f8b634,73c47aaf,91ab8fba,cc0b1c4f,40fece1,29094e7c,c3bf07d0,12aebf2b,fb56efb8,41bc9ad5,9c350b7b,885602e,a3aeaffb) +,S(cfadb9ff,b3474ec3,27db31aa,b0bac83b,fc7f4c8c,9d0efdf9,2e7e7922,f779720,ffb7657f,6f7b8cc5,78f22ee0,2a99fa82,97d41806,cd2b8b06,ab00b993,d9a09b2) +,S(24ee919d,be3c5603,adee46e1,39d84aa7,34a7b4f4,d7b826f4,64ce8b6b,c1cb828f,44039756,794b547b,9360b63c,539b8f11,ca651476,ed57fe29,364a5ea6,17ff1506) +,S(5995e246,2f755256,f4d6b8f1,a5cbeec,c50ddb53,fe4391dd,1b530eff,7cb100f4,77ddda7c,7da05165,c46d7ba2,40127ff,fe78ac6b,13dd018c,e08161d6,d81dc64a) +,S(a7233471,97dd478c,e129fa20,59193c08,364f8c37,1cf19c2,9dbd51c3,234d54dd,2c4e695e,ec2b2ccb,384a5fa0,1a96664,9cfdd711,1a1d19ad,92f2eef1,31796e68) +,S(6466ab69,d5bc21cb,d7e75fcc,5de9fcc9,a57cfed6,3ad41c23,4ed56cd0,132251a5,76e7ef89,6dbb16e2,76622f5e,5f6894da,2e0c1ecf,c4af05d2,990dfa4,25294dc9) +,S(a5eccf90,a29388ab,de6fca2a,bf216145,7fab8bb5,1e33d4f1,1e0acda0,f5c28e60,afd75916,468a11f3,73e88643,56690adf,110e74e2,c81123f7,94805b26,620e3ac3) +,S(4251bac7,493678d9,f26e52a1,e3ed9e58,6a15d060,4d1c9cdf,89ed5871,ee9c2779,fa1df27d,92e6716d,76990c60,e6603bcc,4bb023c9,dff938d,3a15ad8b,72987aa5) +,S(77a9e6a2,4c3cc21,d8cf6b97,3181e283,f40c9d0,26a81903,f042c2a4,fa7db30e,a1ef4fef,23f25ccd,bddb757f,aaa10ccf,e78d9310,72b2f180,b6e3de5d,18155aa0) +,S(33fe7961,c40d4e1b,d6ade73c,4577fa2a,e9c0d20e,999f639d,e751ff7e,6fdbea6b,91b7d5cd,4eeb306c,e316e7c1,464c30cf,9a591994,7232b037,5840b8c,d5f54849) +,S(43f66b2e,5ee52211,a48a9d8c,b485be2f,39535701,5249c311,5b682170,f9179564,5370b55f,fec0ebbf,c30cf7f,e63f5f88,3555e699,728e6892,1dbf7874,2da46a45) +,S(4ad36614,97ef1074,d74c7f6b,446ea87b,ad2f0b43,73f5970e,1a745b6c,38b19bfc,8f5dc1aa,769a5bfd,5eada00d,436f850f,6f9eea82,329ebcfc,cd4cd60,ef5cfd39) +,S(9799aaef,95204d54,f3734dc9,75b797aa,59336866,9b2f12f9,155d5953,c012754d,4b6b34a3,d31fe173,43a5b476,bc492d54,44c256ec,ea236f3a,9845a875,e83e6353) +,S(c86249f0,c6e4635c,2d03f252,50e35d67,c1c22f51,6f9a8dca,fbc9d41,15d828a1,e21770b,37dc4d0d,f4d260b3,94bc76b2,46523893,469f1025,86d99467,ee95ec73) +,S(b48dfcc5,de5687ef,c7282c84,cfd4060,61a08fe6,92fb08e,3accdc2e,6656038d,87e3e1f,ec737074,71f69c7b,c7147665,c0b52a95,37ad1566,277b6d19,fce8927c) +,S(77dceb6c,62d65535,c625c828,fd3836a3,146fa4d,36732da8,58503fcd,4e357fca,d90b2600,5a139713,525b8521,3d370ed2,4304d08c,87fbb67e,9091d1ea,18228bd4) +,S(5b588cbf,4cda0184,4df1cd63,3cc70ac8,69cfb43e,332f5c17,47364750,3175e49a,1424ef1d,f9e69e4a,84eb823b,2f95fce5,a52e1978,474697c0,d47890ca,fad55cda) +,S(c087bbba,83934208,8c70251,8c9ddd93,2c50cea5,11c443b7,da107685,9320b20c,77f26a69,717555f3,b030e345,5cbe4b30,f637c23c,7ebc2b5f,6ec2207b,9209d996) +,S(8d16057b,68f8082f,208b3e7c,fc2c558c,247d7d60,f117ea38,35e474c1,3031ed94,3d305db6,62ffd13d,100f6e0d,19c439e1,6ffb8ca7,eec6b14f,ed551091,d6ba3eca) +,S(75fee0a6,3d32bc8,6dbb8cd2,af7371ff,d17daa96,5e1e19d9,242fdcc1,edc184b7,c2b1e4ff,abc30975,fe7c992f,f8091795,61ee89e,9a040409,10bfdc64,93efa2f8) +,S(65da3959,b147093d,7d092599,a87710a,dda7fb75,460e47a1,10d5c1e2,6f368abb,3599e1f5,2f92c1e1,c3f2b463,d9a30230,a6f53028,cbae3ce9,5e345c5c,21862844) +,S(6561fc10,94632125,d76b787c,a7539870,5dda1a5b,f4407f4d,f8140b6f,967933d3,4c06c7ce,a88e2828,3eae8aec,ab6e8802,cd083f37,6d3a1ad6,29fa9b55,920e6337) +,S(c9a53f80,70493701,523041a6,a9d23343,c8a3fc30,6664d6a3,5ca5e7a4,80c4410e,9af470a9,b2e30d45,ebef0dd0,6e3b2736,369d4dad,f2366d42,18344a22,b6e4bf9) +,S(5c1525f3,443e56dd,b009f45a,1b9a1540,c203f814,5f577bf0,3c734597,33b89158,967892f7,cb3ed91a,d74f4c80,3625707b,58574afa,cb99fc02,f5b3493b,eb4f4f18) +,S(3565ccda,d87c171d,714add6d,472fee0e,69bb6ea9,75f61454,d69939cb,541648ed,9f7d87bf,ec2b6358,5dbd09bb,d008b197,4f02d3a7,6a8d54ed,d486a2d9,3462aef8) +,S(4003ef34,c0f5e1a8,afcf29df,42a14a5c,4527d3dd,138b5eda,bd8de661,1d0e3614,67203152,44894cef,cc3d0a76,35839c2f,3d3ead0f,36174e2c,d3c4c682,4c3048c4) +,S(f5c4df5b,37965079,3ebb7de3,9b495a26,13886422,929393,1cf55d1d,912ea57f,b0b93c3e,12a98cc4,5a3672c7,4a3610d3,aca34da5,7d31a968,f30047e0,31eb3482) +,S(bc9f680,e21186cd,ba63533e,56580289,cbe8698e,69ced0b3,97a96793,569e213a,6adffb78,8105518d,50fb94ff,87d204dd,7bf54ef5,b91e1d72,713fa9,b32e06a3) +,S(4dce8797,f1fe8811,453e15d3,7a3e2621,549e8347,4e7046b0,55f99d2a,c9a16516,ea5e6b77,cca7825a,95cfd9f3,7d70d7b2,fc8a8aa8,749a9d43,37ce0754,abe668ea) +,S(f49b5aba,1ef29570,bd72a0c0,40009382,af2a4f85,26511eee,81869d3e,9759b16b,2e496c20,b996e8d2,696125d0,f60abc66,1d2fb343,b4181202,8658e459,d57c7f7d) +,S(26917d4a,7573e1d2,7b4ff765,72bf2948,a2d02f07,7d7d7fa0,54a1bee4,92125ec,b8e47f7e,ae9b66a0,2a6fd2d5,cf8c88c9,7eb12530,a618678,ad5ce83,7cd8f97) +,S(1b4948d6,285a2fe4,98cb71da,f213e6d8,7ead776e,cf8bbcb1,cd7faad4,fb29d290,1ab4081f,ef87d925,9b736c7a,92d28618,d5698499,b2f5e27b,dd30c1b4,24d3bfd8) +,S(14d712ec,8179baf7,9dd9676f,ea99653c,f8b20ff4,a69636c7,7258bd57,99ae64ca,2e1db8a6,424650ec,32b0df37,d72ce3f0,6f38538,53bafad6,d366e04e,380c6ef0) +,S(7675220,41747684,9e1967e1,54445913,a1111988,8b5bf813,4d9694f7,1d952453,3f1fb063,6457b603,3a155a90,6a722de4,de1a5c3d,6094dc27,37776843,ed82fd31) +,S(5ac8e534,a8828b67,40016d02,80bb1e26,a53b16f,96b24937,fc84ba34,dd49fc63,193d020f,68f5fb7b,2c5a429,6909ce70,18568f87,115e987a,8e5e206e,b8a1fc73) +,S(87cddeb2,8d16562a,531135b6,193449e,d618d0dd,6acf6c43,3372202d,ab4a14b8,5c1de4e6,7c958ad9,9411cecb,de2e9129,cd709425,135c84b,e5b68cba,9f846e2e) +,S(c431ca25,a39ed09f,48e1dfe7,afa86630,4d4f8b7e,eb0d6d52,f8c778a0,424c84cb,450607d6,5b2dbe6d,35cf2d11,d3129330,489eded6,e35ff429,2aeae0a6,7100d825) +,S(8c5021e8,985c1a11,4bfaf3d4,40edc4a3,aaa93002,5c2f2c1d,9bff3683,88a3dd5a,4cb960aa,8077d81c,71d74ba,6096fd4a,b6d31fae,d4315db1,3c2b60aa,a12fb108) +,S(338e54bb,38a47485,8c17c07a,177dbbf,6e7a14b,26781bb7,70b9d717,6e513d4f,7876ae4e,84cacfa4,c22951de,f67763ba,c44a6ad5,52ee66fb,92f80cf8,c4420548) +,S(87f3bca9,efbb3a21,917683ab,ccd268ad,3c7b0fe1,7eff461b,cb06ee8d,2f1f3c5a,a7a1d2a1,ecccfc54,4881f258,bf5fbbc8,e5cc11f1,a117c7f8,361a33ed,55229b61) +,S(f4023df,8ed85856,8e56a037,5a6a78d8,de27d6dc,467c9aad,52407bbb,413a3e40,b2499142,fc4e3a11,6aa30e56,6d59f373,d5e2c545,3e274161,5cff9f6a,1fe178cc) +,S(cfb51fc6,d55a8a22,89d3d282,fe63b2fb,453b512f,374e01a8,92708564,9c6f2d3a,8d4a3223,283811ed,28ec120e,761e61d3,e4ea5312,1cb2525e,b567fe9c,9070635d) +,S(59a44a0b,97050147,bf219db,22249635,c45f77b1,1721c486,8937ce96,ae43c539,31e1ca6,6a910f3f,1de1d0cd,55eb8a76,400ff959,13774efd,158d931e,7039afa) +,S(b99cee56,ec277a37,6ab44628,14bb3a8e,d8e26d73,19fd786f,8ab570e8,28a8ee7e,46e0f398,dda05d4d,7253b16a,b4d80c14,b6be5d31,d1dce230,62ab1135,c23494ad) +,S(a39e136a,d96b1a22,6d039872,5da75b38,1c6b922b,52947cb0,25207ad0,d172bd2,7a5c0758,74de08e,3d10fa71,79639fa8,7dbfc977,8af1a3a2,d910a1b9,b6abb532) +,S(6e8b4125,3b2eb74e,626dd59a,eb7512b7,2c23c855,35f479c4,5b48d974,57001976,cc0b88e1,e4e8c045,5e804068,acb85aa2,715bc82,ec21ab47,873999e8,cd2bc88e) +,S(9942b6f5,681e4189,f7fb6ec9,8efd1343,cd1fb66f,1c98e418,9c44478a,51abb019,200d0ebb,b0cc2d52,90a11e6f,f7c989e2,d2fe986b,ed266430,56951679,8d0e7a9a) +,S(7fdce1ec,3c57b949,44166918,b48af352,759636f7,2eafd744,642e0f7e,5078506d,d7785aef,c19a76e3,73b786ed,1e2d2ce7,53cfb979,65d3d46b,2e313a2a,d7baabd2) +,S(6f13205c,958c83f0,f170906b,d77c79ab,5f51654d,88195b04,a70d4e06,93989c57,8ac9a15b,b386e483,b268d4f1,c950da35,33deb99,55116c99,e848f13f,73b55ee0) +,S(f955a35d,833c69be,40a0665,f4dffbfb,cd405f4f,5b4247e6,e50cd4b6,3697654e,89f4fbce,22468389,a4af2407,f40eb37c,f56e80b2,7da1983e,4e2fa4f5,8bc9ee44) +,S(dbcd152d,619646a3,42a25743,1df9798d,b58b8d1,89238132,e65fb6a8,26248007,66fa717e,c1948fd3,e4425e9e,2e6830f7,e57d23e3,d0219dce,c02b4d36,32d8ba13) +,S(f1aebce8,e57b909f,ac7cfb1d,a2e7953f,99f713b2,8b35b5cc,71eea153,5e6e8ebe,8755fa14,3e77e5aa,60dc24d0,92e4f596,88b0bfd9,585fd57e,1b7f610b,e5d9d516) +,S(a202164d,941f84bf,f89ceab6,50430905,c002bc5a,91d505f5,fef6e48a,4b55187,e0cd42b,2bc268b5,1e572e97,8b9bce4d,b6d4b095,ade884c1,bee3b3ef,c2f057ff) +,S(572e1657,46fde7de,9480eccb,287f8b9a,b1344da5,8026861,e784047d,ca8ad68a,c174ade7,c066abb4,8d2461eb,405dad76,39eda86b,a0fd170,47569617,31eb5d9f) +,S(f4e2c907,fee4d753,c08b1052,b76e49e8,b67047ca,59e64dac,58acce9a,5beba31d,b243aa2d,3a92d2d8,e48b8317,ca8fe897,2b76d134,248070f8,f41bf871,2c11c42c) +,S(a6fc6aec,84455716,f5e9e436,d68809b2,26698781,f24d0463,e06963a5,fdf06dd5,dc16a615,548efd18,6ae135fd,f748d9f0,a96b7f4e,918513d5,73900aa,1818ca8d) +,S(bd806817,5fde9652,9ccd8eec,937f2cbc,c4ffb3ce,b04cc9bc,c00cde2d,5a05f026,b2791ae7,8e014328,e0fe6a0d,8b6bddc6,db048274,5152bf76,bce228b3,64a26216) +,S(f888a667,6f8c3d20,b9d5e810,ecae6b8f,2d8614b2,3a5da51e,16386dd3,5c0165ef,fb4ee20f,e429f3ef,f6f4bfe3,9c7b1ebb,57978853,de242239,42a3dadc,909a0dea) +,S(7c8d1e05,5adbaa71,ec9023a4,8d160fcd,c3182ee6,bda76fd2,3ba8877c,409ae200,8a3b820b,ec595523,d96936f4,fce83779,c489b9a4,f2c8c524,f16e1764,2f907ceb) +,S(18886ea4,ee257120,610ccd71,593baf72,6dfd9bb7,5229959e,75c14e76,26fabf43,266c6695,f57068b0,d33ad3f,7d02b7a,898a3184,39889d31,ffa98d86,e091531d) +,S(fea70f1e,38c8adbd,3339a44e,cac036b9,1242a099,506789a3,184fdcfa,6be52cba,9dff6df5,ba8c1b9,ddc58093,a36670b6,76c0e2f,d745c468,83e26ece,3b9e7915) +,S(4de30389,45aa3c90,76d99142,e79451b8,96dcbf7b,fe420c8f,b1dc4b4a,37d1365a,cee74e9e,9093de64,2401eb2e,201b3377,992ac0a2,13b0b16d,35664ed8,d287cba4) +,S(67d09dd1,eafebe79,c63b8c40,1c102982,8c2d8e6b,6b5bfda3,73988830,1a104fb2,10eb5508,92660d96,8e99049e,818f89bc,d4f03558,d9f2c984,61ffaa5b,bd360a5f) +,S(2b5d70fe,186834f8,d3b96d4d,87031cd5,325e55f4,ffa31b5a,f5ffdd77,40e6010f,646e7e53,24d2a373,78b1f54a,fb2081b4,38439890,c9f53b1f,7bda4d47,a895ed02) +,S(cb426299,8700a76c,193f7b44,6aed0ad0,5d600b23,ca24c752,527f62df,e17be248,92843e13,f3eb60c9,e5f74eb,8cce7723,bcfd0250,4c2186e6,5ace939e,5819a8b6) +,S(3ee6ab7e,75b3bc57,135ce4d0,1e6de88b,22d0b853,fd8e0d25,f9b04ded,418e4e41,69c83f98,4e52519b,931b6afc,933f5200,6ee1ab4f,680ee768,9ad55368,1b4bcc87) +,S(583774b0,a95a5143,17232df6,f17d7829,6918acc7,2a385724,e904085f,c887e7be,33bb1e66,ee8cf17,30c16073,665c353,f8f047fe,150b4424,18c736aa,807c3e7b) +,S(af32e1ef,39822787,79082b77,2a643ea3,eb334100,9a7a3fcb,a8838e0,7079dc1a,6985e28d,c660123e,73aee7e8,b1a36697,16938331,e4b4b7f8,b3a9ecb7,4e95fd4c) +,S(6edf7fdd,32667551,9b60c740,efbfc3f5,50f7249c,deabbf9c,948adc16,e034d0c4,f08c5f18,5def7da7,5fb0de81,9b092691,64534f14,bb7d96d4,bb4513d,ef46026) +,S(3b9a6645,5d268f94,6c504120,a81821e0,86257a60,b3fd9f00,72a0a4f0,1f16b42,ed583a15,aedf6c08,95f60109,6190106,3dfa4848,645b0117,e2e1bddd,2cfe7c7b) +,S(bd035b08,2b5cfdb0,d17cbdf0,7a012b0f,27b52943,82e8bae2,d683ad0a,747a1039,6494cbad,ed4ad7e8,aa135ccd,572be8ac,e94a23c3,8b62bf61,ac90f9a3,9bb2ef54) +,S(e25fbf19,fe9b180b,ae3316ef,89e0382,4dda59b1,41022ff7,a8c45b44,f15338c3,45ec47f6,58a3dd52,34241258,1852e694,7c2c8f11,9f4fbbe6,f6388363,174c7cf2) +,S(2e231dcd,8ffd7356,52ebea7,50165405,f30f595c,3871ee6c,7c072e64,ecffc9b8,c664452,db15961a,8458f3d3,ea24ade2,7610d4cb,ee51927d,29cf3164,6f59317a) +,S(7c0f29c9,3932626f,1b1ae126,316e065,fd2a4174,a66a6048,3b458660,f5338fc8,27ef533d,df6f1272,c0255d7a,587f56a9,b76e2c4f,98c98162,350233ba,75cd037f) +,S(e702d61c,ea6f9fef,fb43e014,4794fb32,7bd857e1,70b299ba,31bb6a20,7cd5bb48,c4779881,c924ee3e,7f228a71,2de07794,ee5e28af,ca63af21,12ec0eb3,1a3f79c9) +,S(f1780044,eadec1b5,bee75c11,7c1647ed,c362c5a4,35a66d8,cc2ae45f,66c852a8,5f8de9f0,f31b3977,f08f63ac,1380310d,e8b7a304,85c9f0f8,9a677572,329e1c18) +,S(7a41b37f,22ec5b18,d4952c53,326665a5,1582ccb7,1660b69f,8d00701f,1f12dd9e,8440be13,2c57259c,80cd74d,2a2f1e01,3448865e,2429c2b9,db750951,607fc443) +,S(94fff7d,8111d69e,198f4e16,57c3551a,a5ce6316,d520a779,ca4ccd0b,282db6d3,482c9270,26c0c05,578a75d3,8b1d8ed7,42541d30,3a933b90,9c3249bc,505c6fc0) +,S(d6a6d44e,8a3ffaa9,56cac76b,1b05a4a9,d3775989,2f70e560,a54c9440,17f4e9d1,48c3bb22,cd839de9,9163bbe7,83a8a057,c7b93e87,de388b6c,dc57ba57,90863e1a) +,S(cc0e25c0,8e24edab,1e5dbc47,71543b92,c47c0993,95b93d53,699204f9,50cdf80c,20c52cb3,7170cf3b,43b76ab2,cb4a6d3b,1a977c55,2431b8c8,220282bd,51fb70b) +,S(cf953f68,4cb7d1af,a125d0ea,cfb83cb5,efddb6a4,41079be3,db72573b,62f8d827,70cfe366,a45d11b1,a847a7b,8e3e50a3,b302fa50,1afc89ac,b89eddb,30909c0c) +,S(1ac22694,2b34ab06,ff16b711,2fd5fe1e,f8ce6975,143ab19f,bff99d6b,6815d5dc,a43d457e,2bd0975,4e9b7fdd,d9e16681,1bd823d2,24476615,4d9e3fa5,ab6539fc) +,S(d02bbbff,1153804a,f0f800bc,f959406c,496349fd,a412e9e9,bb9f7232,7a4d65c2,5de80de,3c7832f7,1d744f9b,9ef08b56,d41e682,1c20f2f2,876fffc1,b802a384) +,S(3ccad347,8d464ee3,42e1cc2b,2c4b299e,d8a60326,4837a040,25a8525b,4f1831a7,35ea2a4c,a59b55b6,ac79fdd,6ceb3401,47492f3e,49b5035a,f3036b26,d02691c0) +,S(eac4d554,3e69c3ce,d762e90,fff84b3d,b3a758b3,419d06b,80f7ba43,36033688,aa64fada,c54374bb,fe8449aa,ddaa1846,ee00fb6d,c9435dbf,17f6a12f,d1deacbe) +,S(3d2879b1,73d93713,3326d0c6,133be281,5df62465,d3dbb7a9,a028e0cc,63fca753,576ce64d,9b95b5e8,a9e3eecb,34cdb8d1,fd38a08c,2a6a8c9d,8f0a7296,68d6e0f0) +,S(3f58ab8d,56a9682e,25137087,ca8c766a,72358a56,99ebbce4,cf74e304,6976ca14,3c9053d1,1a2a8a8,1f30f45f,1b480dad,f6bce20a,15e6315,447405a7,b321f29d) +,S(4f5f1289,b48dcd19,41a9df0f,a8aeae42,2a4ed913,58845b97,3145ddb1,13fd12d7,b5c3e32c,c40c6577,3e286ed,a233ddf,42ac0d6a,5a3c3e86,77f8256,e0dc1428) +,S(a64a29e2,a52dc6fa,e74e70dd,da2bacd7,e4a9dcdf,f9a0bbc5,ad0392dc,93376a76,ade2f8a6,faa9b1b7,7baefe87,33ddc365,c776f2cc,b0e5730b,4ebb0166,418a9590) +,S(dee0aa12,c4d6f88a,d1d86f94,e1c6d3e0,b8694b5d,e234af04,994cc7db,c8480e70,cbd9acd4,cbda37cf,a9d368df,332d30d6,413ae1c8,d34219,3d4651f8,62f93389) +,S(8677a99d,c8af6ae1,b653e294,80eb66fd,772b958c,882fdf74,f9c0f188,d5796936,31c668c8,348c048e,4b7a24f3,a0861f44,6621151d,4dc3e9ed,783eaf81,ff97232e) +,S(51117d3a,cb73a7cc,2b229bc7,9bd6d676,f44aedc1,98cedd80,ed6178c1,f6997fee,5c638c04,123f85c1,170651d3,96343e,78f20770,321c1a4d,174e568c,39110cd6) +,S(48d8af64,69670828,e87585d4,52ea1242,a83c9f8,3423e468,9c3ca383,9f141b12,558a8f42,f07f49a7,c6a9843b,9055ed05,1998dd35,9406c40f,dc4d64e3,5265e9de) +,S(62ba9ac1,b0fdeee5,a143d457,41c164e9,d4060c7d,f5bbb17a,ecbe80f5,f6966d2c,65a0653f,2290416c,5fc96494,b70b3b99,9329fe67,3b034aed,b586c118,a5d9450e) +,S(be4df7cb,679da56b,50ef2058,3c79310,508d8d05,43f3004a,486297d2,49febf0d,c1b56550,dd5f3e7f,a898cb8b,aca88694,ca697306,fa3fa09,95f6c43c,d5f3ba9b) +,S(e49c036c,cca2e068,5acdf34,8473316,424eac2c,ce1550c3,a0c435b0,28c9979d,456bc25f,457fde5,5bd0d273,d27afd7d,21186b1e,3b42bbca,b168992e,61609a1b) +,S(ae6845ed,a81d5d90,b3fddfc9,cb774c2a,976e701a,ad5c1302,7057a6f8,97fd58e3,529bdc55,322ae360,9926194e,7539a534,4501126c,e69b3e11,e5b4a675,7ed755bc) +,S(52a1d8f8,bcc2b772,cc8717e0,2d713864,4c274499,4a19ed18,3fbeae31,bea25e1b,da2a0b2c,e7f17894,848e77f9,53b7528b,dbb80673,c70341dc,9f869e60,4189011b) +,S(ffbbaf01,d89a7804,d5d19f50,dd5a4abd,cc5529c,90d26918,425f3805,96a2250a,88b5f7b7,eba662bd,5aa47a6,c2fe1322,2d514410,f6b1a145,38c37ed9,9a9880e2) +,S(6ffa438,aab51ab7,afb840ee,d4f85725,4473fbc8,ff6051f9,fe5a59e0,ff8c942e,5b0226dd,3b88a040,33860dc6,69613bcb,50a0e95a,2ac7df8e,66884c7e,e602d775) +,S(9c895658,f92ac47e,318f5ee5,91d26a10,c2c1b77a,c8bc8cb5,a45ec943,8a2fc79,708071e2,665ebd58,f1655e28,6de258ec,ef4b0115,ff74f38e,516acfa9,c01045f9) +,S(532e6e0d,efba35b1,3fdbffd1,8ebece3c,a59bb3ce,54fbbb5,df1df6f0,c1861ba9,1af465da,d326bbc3,eb732ed2,464b4593,36f04b0f,a50ba7ff,f61b4754,aebc5b8a) +,S(99043321,e3f27092,cdadd8e,b50cb4a3,8a8a9f80,83da6ea1,38d48e00,e5fff425,11be8597,325c250f,d9e5b1be,8e52c336,404ecb24,df29b786,ef9d1f04,8f77ea6f) +,S(96799080,3a0934a9,9a0a6e94,120c22bd,7d79085c,c331e172,87daf46e,7c4084f9,ea90012d,18450bfe,6a520b5c,9b2812ee,574903aa,d75cad04,7dc46c23,98005e03) +,S(74f5d157,915ffe10,cf6d9981,9eff7616,1f36f71b,4fc386ab,729dac37,3aa0afb9,54a0011e,4922bc74,12ae225c,dcc8c447,4e4ce4a4,941a409a,2c49d421,19b29eb5) +,S(12692753,81c63955,5700ddeb,e8d7a751,76e5f3d7,2aa0631e,3e7cd6a8,c1927855,b92e5076,811002d5,44c554fa,1e1d10b5,3eefeffd,513357b7,2c5e0cc1,c39ae553) +,S(84ef81b,1d143d73,5b6ae35,af18d810,d7cdcfbc,dd1c68e1,1aae007b,80230d9f,cae39a2b,a5b4ce8c,21ebcf2c,638a2e5a,b0dda645,aa6e01bb,33ca2edc,b575d42a) +,S(1c22ada3,326e6ac7,6647af5e,ed62a85,c8d89dd2,a733ea62,dd71512d,ec9037ad,f5a598c6,1562a6af,99e46283,cb9e6fd4,bc4142dc,99600b64,49304981,16aab458) +,S(6b91ca4f,f08eabd5,aa8dd35f,e9e04153,c00f1f48,50bbff4e,73671cc1,fa4aff1b,c6cdaada,85adf99d,4fb68c8b,6104f585,b3c9551a,7dd87977,8cb250fe,11b31852) +,S(57bb4999,6d2e2157,613f0321,983736e4,b99063b5,e8f7188e,23f7895c,edf85bc2,93b46dc6,72eea90e,9274b9b2,73a82da,d0a1ad14,55b550e,94fdb765,ab47cc3a) +,S(75790b27,ebf7566e,1056d9f2,463df8f0,67c38a2,78af81cc,b40f5260,4a33ba6e,ca0c8ca3,d99b0a5d,b065decf,346eadd4,3af8dc71,9ae06dac,6be99c5c,b89d2045) +,S(ca7a75ad,a90b590b,f7f2b6fd,e3004a1e,8dd7b4f4,db8c4a08,f32bcbb5,474ebe9,b3278d9c,5c9351e7,bf4e8a08,e0f1dc00,51273259,acdcba1e,fd3ded69,83fc69e3) +,S(a6dc68b9,3bf46294,392a8d6c,e52bb365,47b65a5a,f318765c,7a4f5d5,2f90b2d1,41ba6d67,fc575561,a0f3c76a,931dc62,37f53348,819224de,8f925c33,c90ad45b) +,S(da2139f3,f8afc90a,c7f5a025,20c36110,2fafed4c,3f525b56,fe0dae1f,6bb6bba5,95adcb88,fa9ed816,dc097522,de5a394e,71e92c1d,f5bac36,9ad06067,1b6349db) +,S(a32b2475,2f0e0063,acb4194b,faba9ccb,2159357b,23f102cd,f5a8e24f,413da4de,57df4143,bd8b27fc,c7716ffd,8c35036f,e09f8ab8,34f053db,ddfd3161,ebe576cc) +,S(aaea491d,33824ff3,c7319d8b,a233c73a,741718f2,2c921392,b893894c,ade9bedc,c8fb291,84d8dc65,93a6a3a1,a5aed86d,a7ad0d32,d5f8ef4e,b24b6154,4c1a63a7) +,S(2935e0e2,7a8409bd,224f3eb3,d68fb639,ccf75d5c,2b0315e7,ed388acd,887f2c75,c4ff024,62c9ae40,983d0068,180d4c3,977e900d,fdfc164,c89a938,c568aad4) +,S(7fca8076,9e2e7253,5834259a,abeafecb,fdebbd96,5066181,63d244ce,770fe577,a3ddc3ab,f04ded22,cac3b8f6,ffd14ffb,69cfecba,7dbaeed9,9a5e13cd,55bc3542) +,S(c5b5f1c6,1caa4a7,fa5e665f,57b45134,bc129ec7,131642a9,6a7b4191,5e8b483e,26cb3816,da5098cd,a3f58fd0,c26011e9,73dabb66,d8cc9257,6d37bfbb,9cb538e4) +,S(410da274,df436772,64decf4b,f790035b,d7d5b174,cffdb4f4,56bfb6a0,79a50d88,71c81968,188eb349,6dc2ef1e,841979fa,254a1939,5bbf00f8,8e1d2bbe,ef9a6abf) +,S(3d9928ca,334dc800,abb6c5a2,43a2681b,6f7976ac,50abf387,54058d05,cf67769a,cfcc01dc,766127b5,3e183cdd,80a0a1e4,62bf01ab,c4df115b,2e4f4e9f,95d2beb7) +,S(65a8f57b,c523e4a6,4777ce31,2000a322,d858eae1,f39fc919,2e4f436c,c8040cec,820698b9,9e953745,b1ff7b0e,a22bff63,c9df69fc,c2055b32,3f07fdc0,61192432) +,S(84e553cc,baa606c2,ffd3ef5c,a62725a4,55f4e539,24a72153,1ac3e66a,5d92fb79,34d6af32,4aaa9465,fd9f7731,1c01221d,4c0b5f75,1902dd67,4dcfd0f3,9cc5f8d2) +,S(3b4a7558,28851226,f391578d,5d146da8,dda3d4b4,f79a36ac,5f8c166a,773783,e37c96fc,4de83cc2,eb4b3cc1,44d4d35a,1a4249bc,4cd7c787,7d0084c1,b1a59e53) +,S(6dbbd12c,6164e8d,ea489819,37fcd12e,74339578,6482ddb8,9226d9ef,50cee6b0,89e48f49,b3f5d154,52e47c17,e1a68ace,eed3bc6,8002a70,33316116,ade02b90) +,S(b64180db,3b926481,74a0b743,1f84af72,f1118e9d,6cc1fe02,f0bfb72a,97c441f6,b394f997,83d00816,267a4f0f,8c95a05c,33cc19db,eac704d9,9e1f0c2c,4fb25fd2) +,S(2173f38c,f47377c6,d977fc08,ebb67f29,37e96fbe,e46b2115,b260f03a,216b5c56,bda02a78,3c4a684,fd88c6df,6becf56,3bd1c61a,4e79d36f,27e22fb3,212e2abf) +,S(9dd9ecd3,f3732f0d,864df0b4,8cf35e06,ea2b89a6,a8a2756a,222e266d,fc5c5214,e42ea720,2ba68242,ed9256a,57db94ad,dcf00b1a,50ded06f,a70d712e,17e921fe) +,S(705f9fed,5cebb62e,dd549526,af729092,9d398232,604d136b,2a121cf,e7af71a9,bb2cb17d,aa4472eb,12e69a4a,57a7863c,2b32c100,a1601d2c,44d2cc29,750d30a8) +,S(f1a908d1,807169bd,7fb7fcd8,cfc125e3,3fbcdaa2,8d8df1a2,17b84c2b,2cef4410,e86cc08,804c6c56,f81cfa75,9851e67a,d792bbe4,87812cc1,4e7aa468,b8911512) +,S(db0c5d79,35693c81,c752a475,6852260c,5625e434,5247cd5f,d40cdc24,e1355f57,120c5ea9,fb00d295,37721f1e,2539ebe5,d0de9bd5,354ce629,fe8c2e8d,c683daea) +,S(5e4d5935,56007e92,1b158f68,3411da86,e979d470,7b53caf5,9a2ed94b,ca423ec6,71588b00,1a0e9ed3,27862fb3,7ce6aefd,c309b5a0,928766dc,b947773e,6272d935) +,S(1a79ee08,3c46e5f8,8e97fd11,22410164,a264d106,7ecfabcd,4872af40,4fec7f28,f928dbfc,49bf7553,356289f,18a5c8fe,54f94bd4,d9406aac,c4df7a32,32349111) +,S(e693f88b,e3be0c46,cb40a2d7,f3a0a435,1c3a1812,4abdf46e,86ca3af3,6cac5468,55f0b7bd,24ab2a2b,aef2db89,d8aec5af,fb4f354d,66538efb,78f094a6,dd649cc4) +,S(6d15e8b1,f3c73c76,f73b12d4,f5e96e28,43bb6157,5d0d7e58,1bcc551a,329ca25e,f21f3ee1,84386427,32a0d05e,419f8a59,173f8cbe,6e3b765d,b74c3588,2d7f069d) +,S(48b8f127,b2614af4,95303f2c,f6593d16,20dd98e4,ff536dc4,8a5aaade,3bddb682,8ed1b9ba,ec137c62,5127e23b,55d786d6,a50d191d,6803ae99,c7ce57ec,2f546e3f) +,S(2ec32631,ee1e4510,94719f29,1a32e0a0,8c29fca4,8ff7e3d4,170a7c0f,5aeb3028,eb861d9f,e21b536e,58d0ee35,9d07d09a,491572f8,13eaac91,6105785c,697aa945) +,S(f7960ecb,3b35d4d4,5ed2c13f,b55cc944,7ab72f66,ea1857b6,776b8ca0,afafce3a,90245cf8,d40f6b40,a7140334,da02eea7,d8f7cf7f,8f87c510,cd6d9ae7,8410f08a) +,S(a94ccc3e,4b22f7e6,ef385780,d58f3aa1,139b29b8,c7dcb5af,7a247125,ba613ef2,ef7050fa,dd6afa0d,a89ba357,cbc6a66e,782471cb,8cfa4c45,e94d445d,a2845409) +,S(e48a7ff8,6d80f430,d3d6f57,6c56f026,8c42b3ed,17086399,56d62f54,f5485c38,b1695229,b4997b0b,229e3c65,c7ffb42b,e429b702,47b98231,a7bd9c5a,5e9a6255) +,S(df99488c,7d46c83a,cda5b422,b2a633d6,cccb09bb,f6488d9e,ac79e9bb,7f22d2ae,89490280,a958f45c,4501a878,5bd3c72b,aaf83a45,d91a38c3,e0477562,f345fd4b) +,S(a150c6c0,4db9f9f4,6f64368e,bba348ef,2bb4e43e,29b27770,553ef41a,ef22fd84,61dfaa74,e1525a0e,c3922ab0,20e3ed1c,e7b5397e,b4485cca,e1f64f29,19c5c201) +,S(9b7dedfa,452ec6f9,1d7aa167,bc984a28,937392ff,d856a4e1,204aa541,f3aa0edd,a37f4912,2f5168de,bca0ef6e,7c9b31ab,b4262c09,ccadbe79,10a4a844,ffd2c74a) +,S(c7d77a51,6d0cea71,b20f75c5,579a5906,ad1aa83c,f9209a23,d38ed358,28e51904,399a7c7d,9e1a3660,37b0a7a1,49b0dae9,8e5a288,cb5ece7f,56de4d8d,74a44bd6) +,S(d313a829,8e4de5b9,a166c2b0,f73d9812,fc1d1dec,b3b86e8d,8062794b,42fe73fd,6524463a,7eb26e2f,34f062c5,dbbdb4db,b8514c9c,60af6a16,fc9a8830,1f708fe4) +,S(4bbf363b,4d66c693,6469b8e,83c4f18e,2e6c2a3c,498f38b9,787607fe,97211e5f,9e42ca4b,243872c5,8c7b6406,11817743,82bb0d59,c0de2562,9f26e8c0,7d1c60c3) +,S(3be10ed,23a0ce86,d4a7ef1d,a667cf19,d471bd37,7467403e,3f84535f,861e9cf2,bb959378,cb317c0,6534748a,49bb9fa8,edfd2090,cb95cead,a7b0433,4066d885) +,S(fcd1f880,d723963c,829074ec,8d29eec4,946da198,cba5d96,b169cdb3,5dca2076,9489bce3,a31362e1,c4cec275,5fa5fc86,d3b105ae,25bed346,7c0919d3,b2a87b01) +,S(b5fb358,993d3662,b2e7114c,d8ea0681,f39f42a8,52cd217c,f0ec0271,fab317c4,89d88eb8,ee622cbc,f0dbfa82,8b04ac27,80a653c5,14df734a,791b3bbb,f3a71b43) +,S(a86bbad1,7fca460a,4740f39b,d2ccab3f,30fc137f,604e77c9,55624815,8fe274e7,c26dc259,ba969ac7,8d294742,e4962c07,1394ede1,263f5a0b,d03b7a29,30753fb) +,S(ed321f6,348cbc96,7e34bc7b,a935d129,9dd3fc94,c27d1467,88792180,1330d29a,b4078360,832b5de1,c77b44d6,cf07bdc8,fc5ac402,15002135,1be0d251,a3f7c3b5) +,S(c8179275,e4ddb936,f55e827e,de094c4a,2c425b8f,701fc979,96ed7194,1ad17dea,d99f1c5b,e241d165,eb49b0b9,266486af,8ce5e963,c52b2622,e16fe99d,d82553a7) +,S(6f08f13f,23b9b383,49274328,3f8dfcfd,eeaf82e,1d91e512,df1d0d7d,de84985b,96b6a829,f99f433a,6ee58ed0,50370ac5,665d61e,22cc7a76,a4a3fe08,d8bd602e) +,S(9443a7e3,410f9d4f,ef9f8b60,9eac3e5f,e6ab5d6c,a11855e4,b35c7e67,9f8f9d5a,d8c507b5,78188c3f,31bc93e6,d07abc22,ac9b2add,5a11db53,8fa0f936,3beded57) +,S(432ea8ca,3ed53681,ad3afcfa,570056ed,d38c8848,de293d2,e37703f5,133c502b,9e19228b,70b52bb0,8e66a49,826c6a61,965dddc,64270118,faa3389b,6bedff71) +,S(1d17589a,42f82fd8,c5a584a3,d3c7fed8,5e22ffea,e2b732c1,30f903fe,1982403b,9f7bcfb4,8f18d208,26564ed0,245f0c6b,511e965f,be5e0e94,54e0f552,78c39ceb) +,S(743399b5,cd84846f,65e778ea,790412ce,1ab8c6ff,ff7c098b,55436a1f,e18e6b2d,dc6cc81a,82801be4,e6566d3f,c01b6fd1,e84ff49c,e5ccf145,fb1f49b2,d8b784bc) +,S(e4378558,3b2848c1,62e3e80e,3b5f9242,5330d232,e05f871f,a279a0da,7910b2a7,c39b4171,aa4978e4,7d3f99f,8179a189,d5e2421b,d076d1f0,80cdacf0,55624b68) +,S(eef768a3,1b1639a8,f1755ac7,d00a6463,8f8a99ce,f5f42a10,664026b3,6718d63e,2a9005f4,cbf4e686,1af0043a,fe97b065,878efcb8,8be29ef5,ee648473,1e24a991) +,S(15db4687,2504612c,f40dce1b,d601d848,fc2afa9f,b2de929a,1800caa1,bfc91fe9,a8b6e64e,7d508d9a,504678a9,d90b82ca,1e72360b,95a2f515,f17710cc,4e7eded8) +,S(ad45facf,2529a84b,9d28d2a0,456a1fd9,d2b3303e,fc78e2a0,c7757bad,d4a4191,1a78e571,473f3035,16ef5867,8f77d5ad,bf8d8109,60f9925a,a6b4a88d,3654f069) +,S(41a03744,6e027b4c,eb775050,a892bac2,1c19b66d,5fa4bd89,58c949b5,b9151824,ed7c44dd,fa96d38f,a721cb2e,60e07a84,27c53acd,6435ce74,bb45437d,95140317) +,S(2020b869,58f4c7ff,2cf64589,f1043d,5a70d5d6,dac0740,56011f1d,27217ae0,111959e2,2757f431,a1994617,124eb1c6,b37f5961,29d52aa0,490b94f7,e1c4e9e0) +,S(2f99a946,859f74a9,50f3bf27,d85a169e,99c5f755,71000180,283383a9,eabd4ba1,3e622182,d3caeaa2,47a4ed24,6e608592,36ecba9d,4e8d79fa,12854f74,df89fc53) +,S(ddcf3989,57f65390,167688c,2e227f29,e7c5675,d4a83a3c,177ebdd1,9e369baa,a700c63f,3fa6833a,239a3cee,a90dd021,c3c3428c,f2b11b87,3da1dc01,2f4b2431) +,S(9863a7b8,4252360,19e2fa9e,9319c5b,92df1eaa,c0b7917b,73bd1042,191ace4f,49a499a9,281dab21,4bf09f8c,724a30ea,3a1fa7ae,4f1b2b6a,3bf63562,e03b69e6) +,S(6c5465f9,ea2c5917,34f3734a,64b8e92e,19cb4def,3c6d5d2e,467a8f58,dd733886,57e22bb,179df35d,15aa2616,a319ab3f,768f655f,a1c07c8d,3dafc443,1e0e733c) +,S(ab375513,360b3beb,d0ca387,fb0a0690,25acb654,fe703a53,d979f14,50f3fcdd,3e9c1149,309a2de5,9b7842f4,3c37a6ed,57f0f776,503ee7c5,46445c2f,6c41be4a) +,S(6acbe3ea,4a72eeb0,b23aee37,905b22d0,ff0878a9,e9143111,4821207a,4c2603d2,1ecefcbe,9f934d96,a163dad7,6c9e6806,ac2ca23c,e495a533,aecb19d,df0d32b9) +,S(7955d144,58fd0c42,5e4ac9f9,c766e253,b4ddd105,7f1c8c3,653f95cf,3a955d89,8fa9c5f7,304387f0,94a89225,53a20287,40c6723b,d25245,84735024,9650aaca) +,S(b5972d0c,90433f,702b25a2,27bd3da2,d488ebbf,e5782132,28c20cd8,35ace377,235470bf,7fbc3de5,279ef70e,d174b2fc,e3e78f84,5c0bf212,cf3d64cb,42b008fa) +,S(a266a810,33a5fa68,a204f3bc,53cc79f2,cc500d7e,4495f8e4,d9cb48bd,4bcf886,31a8b1a1,958d15b7,664390e6,a46f3e37,d52b7824,5b0b91ec,b568b7bd,f55949f8) +,S(1c6412ec,21191b63,5bbbf47e,1e9f7c21,ae7f4f3f,7de0f123,451e421d,44b9cc15,833f8a2c,1111c4f3,f5973851,d0256309,6e7fec1b,22ff969d,82a62284,c42c685a) +,S(ee3532ab,17575b57,9c19ccbe,fee04ff3,8172677c,5e625f68,a7462107,3e0b7fda,f12e10c8,b2fd987b,83dff04d,cc48ee6b,4b704b4,92b2cf53,a9af4510,6e7e8718) +,S(5ed10fbe,4867937,2ed2765a,f7f5b4bd,52ef9a4b,96515e,37944d9f,ee1a3838,8c4eb9b4,9fbe3924,ed8ac641,25dce66,a4bffd7b,5fbc68af,2a8c8633,b4080573) +,S(fe348a21,f04c85ed,69fe7345,f73420e1,b8f6b859,7082152a,15a1d535,d01378d2,d595d446,c9f5abf4,5ceabd10,3e38e423,5069473d,7d9f9911,7fa9cfd2,81f2b7b) +,S(7140369,b7b64ba0,861e1c0,b5ba3973,b573679c,c091623d,bda88745,fb2436e9,f0770350,f9799607,28c3bc21,30c6672e,40dfa362,72c50ad,1f376a54,83a12b2d) +,S(6d14c4e4,8e981e3a,e7ec6b88,b1230096,a7465db,a8381bd4,aa82157,99dad15d,25e4d2aa,a5f2d12a,a4e635f6,db58b2d5,f9c6de8f,b5bea99a,50e5b050,e7e0f841) +,S(7931b9c6,a6837d7e,3620558e,55504e88,4dffe071,5bb628d,aeadb0af,1a4383da,586caa73,320925c6,ddd12128,bf3bd38d,96b7a904,8422c2ff,9d5cfede,fbe80c0b) +,S(149e9d6a,35b83d43,5d0e1f32,a1518b88,88dca145,27aabd95,b0ecd0bd,e5cec834,45807aa0,fd84cc65,e91d49b3,8d012fb4,f87b11cc,1906e1f,cbc74c5a,ce5606f5) +,S(656b276e,b4b351a8,222f2b59,d7949010,1bf6e696,c284bff7,b809bd27,d297394,8746f309,5f8643a2,928ebfa9,a0e35893,905eb6d3,faa8e5d9,b6473281,7b763291) +,S(60f399e3,68086989,ee62370f,f7852e1f,3656a720,539880a6,9884b60a,f1eb02e,55dca05a,8347be14,85251676,d20b779f,9e5976fe,4def267d,4f1b2b6f,d444ad59) +,S(4dbda303,a67a41c,a46f417,54f9bdc1,34a305a,9018ce0d,4cadbe41,886eaa13,e062cc86,fec33458,d65f9fe6,4433b745,909a3b95,d23d7970,c22585b8,3fa48fc4) +,S(816ae1ca,4d64cd9,5a7c10c8,fd819c6b,f8c8b1c4,f495b111,d07f2a00,dd0363d1,c09f6bff,c331d6fd,56ea5520,8e9d13bb,b12187f2,d49366b,2dfd1c18,66526009) +,S(45839f9c,983fe0a7,c3b2b8fa,e5e288ad,87202944,c16d60a0,f18daf,8081b52b,f9368e07,4de74eb4,81f7beb,30835af9,aff40c6b,cda104db,2a785a6a,65dd8649) +,S(50ae4a48,3a49203b,1e04c482,ec2454f2,44b7157d,b23bcd50,63c5b6e8,d4223465,e1b8cfd7,e7bd2e1f,65f2031e,c583bda5,a619028,5c364658,36dba37e,7d5f3008) +,S(df77b231,60395089,19edde15,b21ace77,c5d8bb4b,ed26f9db,a694195f,616b72d6,8a4b75b8,9ecebfbc,b88cff82,2673665c,c99e270c,b33f3037,17dc9500,8b4344a4) +,S(ec6c0e19,978e2f18,b74419d6,70274075,6d2d238d,63b2d345,437b0b8e,f233277d,f90dab9a,2dfc4cc,62571dc1,35bfe4c6,96bd3968,173ced8a,4a363f0b,2636e553) +,S(2874416f,90e7fce6,a6d4eacb,1a722218,5af2a885,4852be4b,a4619011,617b3329,1f323675,f6ced648,8ba4c9af,7912ad7f,b8628e49,5b4da82d,448dd265,eaeb4466) +,S(872367ec,586d3a80,f8c87f3e,83b6dd3e,529de5cb,897c9d2b,e7dacf3f,70ff4247,e5bb60e0,fae0843b,788768b5,e427e009,7dc571c9,e4b62eec,eef68512,a6a3ac05) +,S(c10f2004,baffd30,ae430ce9,40c4af34,4339a0b2,13bf1f48,91b8da3e,424713cc,37be9055,f0c39b36,c546d456,b3f82edf,bf6dbf79,6343ee59,c3537fbd,8631c7ee) +,S(14e12ee1,91c83005,6bdf0f15,3353e80,733b8bb3,b93bc59d,a356dd94,70d847c5,e9707b8e,e00c1781,12b32ba7,c66d7245,d487f032,18d20da,b1550e3a,9befcfb2) +,S(dac3ba1e,2583379d,d2cdd89f,6ad053e4,3687eab4,ae804305,7b57823c,489fdf36,45e3da4d,88211519,55c20ab1,c2d1ec4e,d13a6942,fd37bfa4,7614745c,6fe2c041) +,S(d9bfc67a,5c086ae,e879fad0,b03a5a2c,a2507dbb,fdd2cb09,2644dcfe,bf800bf4,e015dd4b,5a39f5bd,1536c8b1,e93490f6,e5bb4e8e,b23e6ca4,2fc14634,2be6730) +,S(de6f3452,2f04ca0f,170d7e58,1d965982,8a9a981c,12f6cb34,761873,7996c61e,1ec16f3b,28d74fa9,43b9c251,f34e15f1,2fc6d814,ff024e3b,826054ee,f72a0cc7) +,S(432ab355,338e17ae,884a15ef,2365283e,b35c873a,4fa8cd93,f54bf828,a0e30eaf,f56066a1,a130dbf,8a3e076,720c10a5,caa9af0a,7d86ab9e,8703dcff,95447807) +,S(3d2e3507,cfa127c3,a350f546,d95431d4,8d96990d,b5beaafa,35a408d6,7f7c4d99,da60885f,4711c373,145655ca,b0f8a235,7f4a0508,2bbd2ac5,1b255593,a35f74f2) +,S(7f3f431c,b96dd4be,37c29d0a,55f3b71b,f781c7c6,191983f,4576e518,24ba87ef,f4df4345,7649d24c,2b8259f6,f7177b96,cfdb08c2,4e9161c1,695c8027,60b8bfec) +,S(4270bf59,f2c87000,44b6cdd8,b558e6ae,6f7b67d5,f128d716,3d604a7f,2f687143,358cef,2e8ff919,6865da0a,15a25524,6c4a180d,ed25174e,d08eabd,4da99315) +,S(c663722b,9930fc9c,4113af50,d51c6539,b7931cab,ebe2ebd,46f95b8c,7f603852,3443e7b3,2cdf23d8,eb532a31,3ca4b32,b94768c,78f8f13f,719708de,925ecfab) +,S(b0014864,e396ff02,cebd7b65,c66e57f9,168e8f3,95ae614f,21f8f162,c86c4e14,3f6c99d7,e2ec94de,ee65007d,ab501230,4a80b69e,a0e0672c,b7dbe4ba,182eb876) +,S(64c9cd93,98fe9da3,5d3329f0,ccb82e43,c2d735f4,d2467b2e,1250c410,da84f41a,d24b50ab,71bf6033,d1206fc6,dcedf049,c223ef78,ff50905e,c8d95115,564372e9) +,S(ffe0841d,51e05862,4f759706,b27db960,ecccfad4,cf326736,ab4fd9db,cd29b766,6bd1f1c9,f6956c10,4fec1595,24d8064f,eb5d8391,30b34a01,77c07491,79cf2562) +,S(d1dd0c0,c053c597,bad38d63,c40d1e7,4b451861,cf0f20fa,47fd3f9b,77ca7432,34aa97fd,b59495f0,3e7b5628,daaaf66b,5c9dc5ad,de929032,80ba23bd,6c55976f) +,S(87a436e8,c76bd7f7,a3e49cd3,af996f1d,9153f704,1b185d8d,f99eca45,ae8e5779,8d2cf3f9,12553844,ff905d5b,2a1fc36c,369ea4ff,6a89a027,b6dbe797,f11006f3) +,S(a54500a7,4899c440,384f707d,a8fd62ec,1801070d,cd5396e4,b59e9ecb,df27c379,e216444a,6d26bb06,5f5b5e5,72c927f8,5315df3f,11da2cae,2f334572,33cb2668) +,S(937ea4f7,99a319f4,fa5aea85,b48d6323,28417b72,8690a9c,88fbd51a,57d8187b,2931a7fc,9eb4aec8,b7dce4af,d14438ac,fce77bda,a7d3e7bb,b24c8554,cfdc9d9d) +,S(50793c76,bb914aa3,204dbc96,cb04ff26,c7060863,8d497f07,a1eb05d9,b7fa6f36,afaafc78,b5e30762,b68940df,41de9ef3,d963b603,6bb1ffbd,eb13fd67,4bdab21) +,S(8b600630,fadae564,dcd1c570,9bfe1ad5,f8da5282,61d55bf1,3a29766f,6d8bb5a,fbf1b64a,3bd0439a,ec45a7c3,859374c1,7885389b,9c201f4d,c1142120,6b2916c0) +,S(14e88bdd,e32a15ed,a37c9650,ffc18ba,1fc61643,22b167bd,635f3d02,e78c8458,82c1ec3,dc240faa,5fa355be,475afe0b,5cc2cd13,760249d7,35b1efe9,976f02c9) +,S(c201e4a3,24b68472,d453dc02,8c78b1c9,975a3d92,efc70a30,a912fcf4,991c6c06,263a954,41d6e6d0,8ebf3e50,3c786a12,a82f5f8c,d38dccee,62469e9c,f0e2dc08) +,S(4097665d,319a8b3f,71861b1e,11828cdd,4094182e,611b35a7,c5d10ee,99db4fb,ca0d7ceb,b1aa2b1f,576cd1ef,56ffee03,322a6b95,505727a8,5dcb70d5,27a506a) +,S(173d287a,eff9293e,dd2021a0,4be052d9,3713341f,37a537a4,45deab49,b427fa92,9626e204,f03a1886,c27c37b5,85725465,72e5302c,d3f9ce54,1fb9a46c,16432548) +,S(c923c74d,1b114f,30d4b03c,92629bed,227fb8c6,8f1f39e7,8f121a44,694d4256,58edbe6b,dd5e1382,108d1e08,ef022788,c9f91803,d8ad4273,1f413440,5b5b3d29) +,S(92fa7906,b1f25c2d,3df104c5,196cf31f,93e5a8be,6cb91a57,c88c4ab7,cb92d46,a12162bd,79341fc1,e8f74db6,2bcd0d9c,ae99a75e,ce588240,79a61751,c887f307) +,S(37477df6,f51f78c2,a9c8e17e,ed30d5dc,976a9c91,4aea94c,691aa3c4,8639140,6a6a26ed,1955656c,64c6e15d,e50b0453,25c3da66,f5067730,5c2d5fc2,a8089872) +,S(b5b2ee59,1195f38d,f834e78c,abf00fb0,be9bc724,36c5f596,67ada098,dd0521bf,10f36f85,95233008,46dd276d,951abe90,cc8baeb0,fd8fe44,314706e2,15ddd5e4) +,S(b05d1b8,443f1bfe,36a415e9,d307ef2,56039335,76ecb630,94845b03,d2a137bb,99fece4b,ddc90b2c,3102a415,6f0d63f8,54098f48,3e1ef64,65f7f062,7d066cbc) +,S(e5d711bd,4778b3bd,cefd703a,e9525b5b,d69dcd86,6cd0b179,de76ae8a,5d918fd,8af014ae,5aa05b29,a0c88e21,8fef5690,9685d524,af389327,5421c35c,6db64248) +,S(7703cab8,377a482f,bff1c6fc,4d137998,1e217d11,c424beb9,f4f1f786,86fa4f30,3323edd1,3c80b8db,c7e247fd,5568c92c,8dd329ed,420aff0a,8e903571,e884e8b3) +,S(f834af29,c18bb488,366cf722,52a44357,2c87da2c,c14b3085,5405f626,154f3cc8,2e8a0ac0,ca5441a2,9e27dc09,4fd5bcc6,6bd8f7c4,717615c2,82001fed,bd5ab248) +,S(783b093d,13f33c60,1db8ee84,51d852ae,13bbcd7d,1d03a79,4c774336,4bbbaee0,3aa90b01,cfde6abf,fa0dbfb2,ec2cefb,6b5fb1c7,41701ab,66ec9b82,74ae5b4b) +,S(be3cc435,a7770b99,14a243b,48ef2c77,3ae42e82,1c18a266,6f92da59,e01c3391,f0d3bd36,9118c58,159fc64d,e61ebc58,a560f705,25dd5c51,e6aa9276,5ce4a701) +,S(3e980bd7,adac00ee,fe362454,5fee1ae1,97f89e06,84576a08,fc909285,783b606d,c64f0e41,204c946c,74473434,7e091147,67455e30,53fd488a,34d333fe,e9c5c228) +,S(a6b04140,aa621e6a,34959bf1,29790d72,91d7b154,5dd5e7c3,35bbcfb2,d582b442,9cb2c9e2,9cc05d97,5e61d151,542af6e7,a997f8d8,ee42d37f,8041c169,abcce830) +,S(7ea7a83f,c133cb8f,a109a6a1,b29b2c3a,29ba432d,222382e5,1f280e2a,47fa721b,cbe384e8,8a1f7731,831c5562,ad1430a6,6aabab75,e38571b4,5bb7dd8e,a699d7e8) +,S(8525e87e,f0fc9ebe,2f82b5fc,a14ba6d4,8189f9ee,146aa6c8,d842aca6,90fa042,dcf06a78,f311a3cf,182a393f,2a34a570,192873e4,7dfe22a6,86c70e75,6b32c86f) +,S(7555a77d,fa050d0c,9ed29c33,765fe967,16dafff3,6cd04fc0,952a7b41,bbd56dce,9854c0a7,bdc3dc1f,d8bd0a3c,d0af3111,18dc0392,830699cf,d0bd6f1f,17152177) +,S(9ed142a0,7a0adac1,14e6f026,133e00c4,972bb3bb,bee62f71,beee17d,9aac296b,c1eaea41,6c80e3b8,d24d4006,f699af50,9f4dca85,11c27f6a,39150a20,2b7a9d08) +,S(a3fd2cb3,784d8ccd,92aa67c,8118fd80,c9d761c4,2e0169d5,fb13b1a8,dfc56b79,75cd9b41,55db55f2,d50fcdd4,8c90d58a,b0d13df2,7a09df6d,659bfd5b,f87ea755) +,S(2ab4be05,217e7ee8,799ad598,b9c0e211,554338e2,8bb6d42f,c9952203,605ad251,2c2a1a58,da066d4a,7fc8c8eb,83e1e47b,b788afc8,3c8c5dc7,bd8f5e31,5476d853) +,S(4afc2888,af8c1aae,af6268af,566ecceb,8c95d09,d9f8f359,2cea567b,8648c383,b1e26b82,5e69e92e,98902579,7e99c7eb,cf3f284b,e1de0054,58b59336,a84f5eed) +,S(dd800faa,92319d2e,48038600,aac1c2b2,f63f5fc8,dafd988b,548021b5,2d8e3d4a,59fe8092,6e0468d7,60e522ac,91881bc6,8fdbc192,14fee60c,e8e1a977,1039ed7d) +,S(84aaf3d1,daa3a5f8,89b9bc88,6afb07f8,7bb5182,6df9d7e2,af5a74f3,c6378c91,f4164ab8,45eadd6f,a6331218,ecce096b,a04fd586,b744f0e8,11c2a48,3835afbb) +,S(b770f2c3,9e2e9db8,a92823a3,adffb690,d2ffc180,b88cefcc,8adad3a8,27c40b2b,f7c6e37,9894982,5d1b90a9,16ea564f,7e319c97,ca6cd22e,79f599e8,dc0d53e6) +,S(a60608a8,a0d7e556,70145c5f,bb107cc9,7cb1db87,2493f362,36a21e7e,babb2b29,3423527c,4e414548,96406f90,6d4a3819,a289c91f,b580ce19,6f25c57,7b54de5f) +,S(80f470f9,7b7f393a,5e6b4e5f,cc911a73,20f6b0f7,fcabf2d9,21eb649b,53414597,57d8f2f9,6e1bbe0c,1328faea,13c3f3b,770fa14e,4b5b7e4a,fa1ce9f1,2bd9adc3) +,S(4e111bf2,1f5248cc,3fff0096,c64a7b58,72e4a3db,1f0397fe,261cb33a,995abb6f,249cf172,102733bf,5fd829c1,d60bd930,c03af614,31faab0f,6d5acbb6,96eb2b9f) +,S(7d8c0cb5,47fa1d8c,10254660,a20e85ec,960cb70d,f38174f0,18416c68,c62844c9,2d79ba3c,17ec4d79,a157335b,f358cfd3,72b0f1ae,1f6bc743,40400571,5f35a271) +,S(bc0d4cbd,95896fa5,7f23d48f,a5a9f2f0,14b132b8,9b5953aa,f612aaf9,670a0034,f5d06670,7b316581,7a641091,55773bcb,83d7563f,28ea5412,480a0f48,2fa0639d) +,S(b154a2ae,ad08d115,95eddbe1,c9321e15,5cceb0a6,a7e646a4,2dfe70fa,b6d37a40,9abade21,674dd12b,d01e586f,695c896e,17120d8d,9e3c756,c8b5e53f,3216592a) +,S(ef603c34,a6ce06f2,7053bc31,841fe2e6,314be756,7e477325,5315ca58,5772e1e4,f9ebbe28,96f3b0b9,923a7e57,d819a231,15fced8f,3bd08f51,64aae597,c49ad65d) +,S(a601176c,2d901ebb,ad5a05d9,1adc0041,14f7bda,1ac496e2,c04e23fe,82aa9376,f781ddb7,ce0c3649,3772fb84,9e914377,d59abcf9,d3eade68,f8c8a303,1ae88ab8) +,S(abc41547,b6b3104e,fab968b9,bc9a9027,b1c7f0ef,3a9007eb,27331539,5c31e6a7,cd2858a2,28f21d6f,bd8a1821,dd78399b,594c5bd3,52628dce,879b8a37,d97e0ec9) +,S(92890105,c9c721,b2e4c430,26e413a,941d1da6,d31b48a9,2f2e3a17,d87f5a81,c1cad87b,1bdcc617,1e432e17,c13b47c4,15c22399,a7cbfe2b,b3097f28,da60a5da) +,S(e520dea6,9f4b35ca,46a46831,9947690c,500a7a80,6bd64228,ad144a7d,c37ca563,6c00d766,ac3f8fbe,90355dda,6ad4e2d9,e2330b0a,db673356,638b0d3c,8a2a82de) +,S(c0337a23,e2b3f1c1,a57d18a7,9d6ef25e,ac2e9315,18f0ffb3,7502d701,c57fadc4,fcea5873,be22df35,4729c3d5,b07a570d,7ac7e938,f6e061ec,cdd2146f,50808b65) +,S(276c62bb,6a8e2c07,6b5fb6a7,62d1d624,1b796a96,f53593c,fa51279b,2981ac04,5c53a65a,bb6b01d2,3f9284d7,d696236c,773c4578,88f692b,73e2ea17,6e1afc1a) +,S(604e1e87,e5051dc2,b837ccdd,43c8ede4,c4a2de6f,67a9ded4,fad1ddbe,8d663a5f,f70fe23c,e3511973,93d4d675,9444d862,ace4c473,99c34349,2d06b27,8b429bee) +,S(f2fc151f,470d9106,d58b05be,5e9039ba,4aba16ae,f7e4ceec,3b6e3887,5c41f6fa,f49bd2c7,f1aa5c63,d8e7f2d6,e0769060,d4188773,9c16b7b8,5d2a9216,e1e98de0) +,S(8e031346,e9f34ef,3b81d9cd,7e46eaf8,7ee0b294,d66b0315,eb0902e1,340ac560,ff00c8bd,a5e21e1e,5127441c,ee0504e,6b028c,ec0dca,450145f9,2c394296) +,S(a8680f1a,d75b7a8f,27a1cbe1,6b2954eb,d4e26eb3,a85d1a0a,96b9f40f,e5a9970b,60c6bf41,a55bdac6,397281f2,a3f52896,757401be,c42964c6,9501973b,e6070c01) +,S(4d68a1e7,f4d82ea1,e03407c0,5085c794,9ca8c00,d4dcb066,128e6c6f,504f4658,85c5de11,e5dd3691,2aa95d2d,2c36dd6d,26a9550a,3a672af4,d36265e7,a3d1ce76) +,S(3708f41c,2a6d8649,6983cdc1,bd02eb39,758c0bc1,ae78a483,5c6a20ae,e6c2a7a9,310b799e,79b7f5dd,5c9ae0f8,7aab193e,3c2b7145,c70af9e7,f4553faf,abec4152) +,S(8c883967,45028c5f,f1b96181,43f5b732,2d1a18cd,3abb1a92,f6ce3780,a115f059,24563ad4,e4ae484a,c344180,66dd87c,40128e42,5c8dce10,b008a7e7,620725d8) +,S(c445e76a,7b377ad3,bdd0fd1a,94a7263b,652e83c2,6342305d,a0e1ff4,d58f8fb0,4bf5a41a,dc8ddb61,3afbd3c1,3aae7dc6,f60ae2e7,23065b8c,c94cea61,cbacc89d) +,S(4ad9613a,c8a635fe,60d9f1d4,da4b9fd6,36949d5e,edaaee8d,eb2f65da,8e419f16,5a9b5b06,8f67a0fb,3ad31c83,ef1a81b9,38745877,b4b07316,42ab4951,68b0d7c4) +,S(beb3eae1,958bfe6c,9ab49d77,648aa662,2e397fbc,58e90b0c,20d1d29f,d760c943,d5a46be,f43a67ab,225dc6fb,5aa68daf,ec76f27b,6a822678,dbaeff88,382c9b8) +,S(aaa9aab0,bc0e21b3,be3f1dae,ad4a076b,4623fc14,3e4018ab,9df9a19f,d134f397,9c7d7fe,19642c65,a6c0347e,8fa2dad2,d3f094f8,bab6790e,d3c703ba,2742e40b) +,S(553c1e89,99d6f446,2fd7ae86,f1cf9d24,d4011661,9fd5d356,604d6a55,ad99dc52,26b40d3b,8410ac5a,6d2ba776,f7c5f0dd,3f6fe494,379344ed,6460bd6f,f550cebc) +,S(859885ea,36378fc4,398dae9c,4725d604,c3d56509,559ed28a,b47ebf82,5aa1abc8,fae5aef9,fc74b963,bb2fd930,5b2d1636,e6e84aa,3c40b066,9e531fcf,f00687f5) +,S(5fb0a118,679c5d48,554bf7c1,63b82345,89e51032,a70c654b,bb7bdee,f0368f93,e8d55f0,4a6816d3,bfb02111,eb95d868,5b9966ad,39d94c70,a4cf1826,6bc2a2d0) +,S(a424347,86f3e153,c9bd737c,1e70ddae,bbed445d,77ce4302,194be82f,94f91dfa,27d1cad6,abd9fda9,c1012f4d,f25b69fd,a54239a5,8ec12496,97116548,e7beed46) +,S(5d007508,63fc1f3,a0a8213c,c729310c,39465945,72b083ee,f571b921,a0c821e7,e538fbc4,5d055a07,b2cc2605,61238486,d99bfa81,6b0407f9,39f1d12b,feb2df3a) +,S(a3eb25b8,97b10286,934605a0,e4aab7f9,e80393aa,71cdea52,5f82f5e8,282e3db9,e2b354c2,1430c45e,4236baaf,128967c7,6beec58b,e6890c6f,303d83f,71e7f77d) +,S(73f63220,8a133202,96f3f51c,d10fc816,1e309e51,4ea35f0f,ff72e09c,e897981b,43412959,cc78f32e,d6df7ded,349a92e,3095257a,8a1b5dc4,15ed073,a420179) +,S(5f0825d1,c21bb5f3,8f0d9628,f6046648,7760512d,f1bb9bb4,ccfec4e7,48da41af,debac60e,1d95de12,84f1abd5,b6592824,5becd4b8,c2496323,b374239f,fe5c0cab) +,S(cc42a6f2,b4a8e9f2,fae3f624,cd12294e,efc26dff,c66a676d,a10259fe,f7d84294,91b26600,f92cbd05,f13f2adb,2b5fb17f,118a7ae1,6a011690,1bae0e14,3b230519) +,S(7d56e0e7,6eebe13f,a81aad7a,8efbcd1f,4de0c5d6,bd136261,8f0002ec,6f635a17,95462707,fe55e3ee,46dca813,eea5de7c,51122ec,55a91754,f7d13ac2,43fa1ad8) +,S(7972b61b,4fe85ec4,acb605b3,aadb56b6,fba8cafc,3c764dc6,add54c06,f7a5be61,ba186670,2642fa6e,a7cf5141,c46a8e92,4764c4ce,5d45b7dd,9f4926c9,32b8c7d8) +,S(edc8a440,347b1500,899f6360,9674f90f,4344c87f,361fae0b,bbe08235,d5025626,2f812740,9a14bf59,9bcc7cef,ad140b2e,7bf84122,9a51e486,4d01ecc4,1b2cd77b) +,S(bd9b65c9,7a8cf4d5,2f727b57,19f99986,d4ea1bbf,25560d18,d54877cd,3db16899,59cf7127,4e878e5c,bf0d9e02,dbe4f861,ee9c8e3f,5dfe99af,7db98e2b,c2fe432d) +,S(27635e38,1191e8cc,d3756571,44c5d8d8,72d91802,5950a106,f3be2acc,4cf35782,c359fe47,a019870d,7fa3ab,8f51bad4,eb5251f3,7e6b8ce9,db1992f7,cd1ab7d) +,S(4492dcc3,16c05300,a96abad1,47a00e98,359950f4,95dfb261,839ddf21,3b21d85c,edf1371,a4e81e42,dbfb8c6d,2229dc5a,fafed4bf,a58c2433,18934ed7,dc9ad35b) +,S(f8124c40,409e01e5,8de8a386,9bd88f8f,25887fc6,cee37c01,279aa606,edf9c67b,e0071d1,d31fee0a,8a26269c,3d7f3662,2b0f008c,61e749d3,65560c21,a26fa6bc) +,S(cc597b67,66eccdf,a2c0716a,dd5e51c2,ce3aed33,8f55f0b1,b5784bc3,9a05a2b5,12e031fc,394a43d9,d1e12ec4,28e8d5b,df52dae7,f3ef22da,74e3a418,74682865) +,S(142cb2c6,bc1283fb,d2164270,7d525dd3,d4a45f71,a63222ee,6fdd31e2,805aeda2,bde93d12,c72c422a,e4d5b718,bec6458,ed082bfb,7c686f2e,1815edc6,5ed66ec0) +,S(fbf4d602,dd765941,e696704a,1a8977a9,c3327375,e3f0d479,68fd9148,c01d2d3c,65b46df7,99e895cb,a6d4cae8,e68f6160,c7b96fb2,9af7d18d,8ef1396,c06afa8d) +,S(efdb5d57,bb5fe8cc,e3300ea5,ec14c50d,f09436db,cc58ebc7,c6d123e1,8dbfffb8,39083803,d9a90c62,6219ce93,167bd89d,d444e47e,ddd60deb,8137b8a4,203fe01) +,S(5bca4334,faa30557,b4b36c7,b6bf2d4e,921f59f1,eadebd74,97690742,2c3f35ad,3c97baf5,e2a663f7,22bf2fe1,cd646db5,79d27f2a,353fb45b,520d227b,c21206e3) +,S(9aa1dbbc,e90cf065,5ad8a0f0,54aa6641,ff921e97,1a73cd4c,49ff1f3f,51156632,cbf6e81d,8bf1e85d,b802f0f0,b9cc1125,6e0b337a,b90112dc,6513bf40,2b1a4200) +,S(2fc7fc55,86ed7676,4d5d4d23,ddec2827,5386e56f,eff09b86,abaa9562,73c2d408,5bff69e4,773c34ae,e477b0c3,e0310b5d,6019a558,5d4c7f3f,54f9c810,69d6285f) +,S(8e5fb941,c3ac6f2c,43195487,80df9116,6ef3b210,f2fff34,764cccbb,d34190b2,dbe2c1b1,5f6668e2,c63ae154,ce7b32fb,8d29b59e,dcf1ac4b,1df9baeb,fd10d2ce) +,S(7a5bbb59,4748d6dd,5d3389e,aa95b8da,dcfbaa07,26693690,4801bd61,b3d49c6,c2d28043,a42d33fe,8d8b755c,61235c68,ca13861c,effe3fa7,fd4edeac,634187c0) +,S(95beb8bd,f0d33531,ae73ee1b,1b631ce0,917c2fe2,22534745,f87faf21,bfb76753,c6cf8600,d9c55975,527e269f,b4a0e4cb,b9bd7783,7e230ada,a43c2f0b,c64f45b) +,S(f44ecb0c,6d896dc0,304fa480,90ab9793,576dbbd9,76b8fa32,540d223,e2968d25,545064f6,29644329,76a03c62,9ea46a09,e20e470f,5e76f810,6d19f353,c321086a) +,S(8dd32cd3,3dbd936e,6173ef50,1f7ae265,d56d68c2,77061e07,4fb49d26,538109de,6b110e5c,80daac20,5392bca8,a47d6d90,ad158c25,a10c0851,ae985f70,7c3f1e76) +,S(3d4afd30,b0f0f578,d4fbc77e,b71d044d,fea5e3f,891cda8c,77075dda,6237997b,2816e0e8,664df57e,c793d442,49c23ada,ae286ab,e748493e,d83b1efc,58bb9ce) +,S(a9b739fb,beaa3b22,b295ccac,86a07ba2,b8391381,2d2be020,a5ffd17,85d95ff1,a1d08230,21d6a6ae,f696d732,3c43fdec,d3c5d42b,8ed7ad40,1a55b2ae,bfb84f1b) +,S(e9009747,e51251eb,fc2cc754,c8b55966,a37e5d67,acf9b632,bb77f46b,9203715a,34258967,9ea8f997,71b476f2,5571a588,756f277a,17ce3907,d9cdab4f,cef9df72) +,S(7e348ced,55aad74c,c24d09b9,6b332084,2108fe41,3ac4c6c1,a8376439,f21a3f2e,39874f8e,3b722795,d8465706,e439e4f5,c884c22f,365920f9,1edcf83e,b4389e5f) +,S(a7dd6676,5ec2d701,56f2cdab,f0fcc0d3,8e340fb6,8a0d1220,aaaa74a6,fbb1f55a,b7091bf2,ab00a63d,292ded9b,b00bbe9,63710dad,445e8e73,760a50e7,2f41d447) +,S(b8579e9,a8cccbf6,dc033a48,16d8934c,51c0852a,4693fa96,a308d51e,3f57025,e696ecdf,4d79151b,2f164a3e,bc11bda,42c88833,6f694fec,a8bcdcc5,7158268b) +,S(7b29c45,9de0080c,bf8c8c72,e7b59a7f,184f1c1d,46787cda,bb42c2c2,bff57af8,40de8367,a6d8d13d,41901ca5,60376ede,817578b7,25f4e614,5dc46de9,5f801799) +,S(1ceffa6f,89b65be7,ac20e34e,f0b7e503,d1c4e9d,b31fe14e,179afdd,96f00f9f,6fdee8b1,f05930bd,ed98feb7,81790e10,8e293bf5,bcaa6da9,79a744b5,1a76f179) +,S(556e2d6d,11d301c5,6d5d01e9,a01e2ec6,80ba4efe,a004de47,ca2d97d9,15e58d74,9588349b,b633d7b6,b55ba7ee,feecbc99,3d66e6c7,2f4d8961,8a2c5d7,5249d711) +,S(b3506e88,73b93dc8,ed04518a,42b62ea2,b97f7bf4,e9ee66ba,6a7ab917,a1e51586,474f8100,1230fb7b,fe693eaa,7acba9fe,5917a39f,1f8fe3fd,5823ae4c,1b0b3b38) +,S(d186af3,89cb4908,db01a886,f90ee67c,1ba7ff3a,19994504,e9774b96,bd2628f3,3b39782b,c6567cdc,b4778c75,a862ef36,d01d746f,f411821d,5d08f7ee,f2a2db7d) +,S(f951d921,e1065ac2,3fa45900,62c639d,19e8c980,2a810d55,d6788572,ef987bb7,dcf7ddc1,87f8e975,27db239,7f553b5d,51abc66,bb76a070,55d098c7,f9d7d950) +,S(8dce8e16,7bac6fc6,1161b75e,8f340e0b,8bcbf874,fc82a42e,bb53e0fa,c4d915ec,11ce53a1,4a9eb9eb,9b4b410b,34d26ed7,9c0287f4,9032f674,e3212617,e0ae940c) +,S(ce2d3cdd,a0a7a43c,d7d2ed15,ec12a7aa,755dfc5a,ed5a85dc,4f439eea,99e26e2b,6a96aebb,e7ca2740,ea75c285,3c582207,6a8eed64,7e21809a,36b97c60,7a5a145) +,S(239466fc,dcc2d4c5,cef367b5,c8339248,f712c40a,558c80f0,5ea390a8,dcd92092,f2ce223c,9b49f08b,ca63ee8e,98e01736,65d176ab,3b7d718e,d0e855e1,27ac28cd) +,S(220ab764,341d3c08,d9c5bcd8,331e0973,4a2f3c64,4766395e,f6a5fee,3563e033,ec1e4460,d03013ef,467dc7a4,2b520c03,aa51a9a3,57817f4c,f9dcd837,56df5011) +,S(d5455dfe,16406a6c,70fffd81,f3c4d10e,845f0b12,64fa3db4,ee2ffe80,904b469f,a0f93b8d,257e1e62,265d99e4,9158497c,28a20af0,2af959b8,8810a10,60aaf9a) +,S(52367377,a1f6f05d,df054665,f1eec0d2,f5039cf9,cfc1d37e,1a9d89f2,88e60c72,78d89f55,430fc352,cd58d047,7e2497a0,d05e7016,d98f9ce6,a039fc5d,9ec7bb41) +,S(5b996113,3ecc9422,26a3dc88,67364125,a8e569f1,912e6c9f,4c1ddea5,5fa56395,bb9d7764,12076caf,5d8ac902,c5e2fd5f,f7a0bcaa,10898f19,10142af6,4e436d0a) +,S(f36cdb5c,bfbed1e6,58c7fa4a,5b0a6773,eecd1a43,db96cf0c,7c24f32,b84c13fb,cfc6d2f0,5ee9eda2,9156aee1,81b62659,993744e7,479379b9,3156c919,18c394c) +,S(3208c58e,25107af2,b38bf754,5531266f,842ce33,10ec7e44,595fb91,7079a6f9,831c6dcb,5346b6c9,605d9ab5,895ec2cb,653599a2,c88801c6,45a4758a,32c785d7) +,S(a6073a07,1f8510e2,aa021b80,b44d7356,47253583,6501a5a8,c5253918,8c9927a7,e93a9fc5,4c8a7612,16718baa,dcfc9bba,6fefa30d,85803246,2a9f191a,6364fec9) +,S(3350b462,f79eaf7f,260f2147,6aa7ffe8,f7ec1a7e,35624713,9e335758,b9f0d913,1f0c9db3,fce1222,fbd2341,ba8d2e93,20f33bc,482a4d4e,80571711,21152959) +,S(a54d5b17,bc82a74,9207c89b,5a1650cc,1773913e,66f6744c,11de24ce,3073caa,3c243b5f,5309dd11,83c7fe5d,49222c9e,fe62dc26,f92b73af,cdb7b7b1,32cd3ab8) +,S(6590a2bf,676a7a34,a1424f7b,1c7863e,8a7d346d,563905ca,e5b25729,4a04cf8,11cd5803,490419a,1382d972,eea2f734,4f8d0cec,42466f09,39650645,a3e781e7) +,S(b3c09c02,9fc7f48b,d2f461a2,a367011,8840393d,fb5b9592,89e96ec9,c7cba9a5,e7b5aa6c,bce54ad0,197bb1bd,ac030e2b,c6a3e6d6,af2db60a,b97cc92b,8f5cfa9) +,S(d7968ea8,44a137a,7db36143,98b29972,cdc693ef,8fdd4644,26a92939,18fa8bc0,82402589,2901434e,cbd5dd29,410ce2e5,56dd3174,d74da2e5,28326ec8,ad57a75c) +,S(8a57bc6c,2f98f9cc,d1eca172,b2501adb,3bf6edc8,4b50365c,6d959f00,838e8304,2ebf5462,a0f85985,438b5d67,cbfa68c1,bf643410,11a478b9,92cbfe0d,87e7d14e) +,S(51886841,684750a8,c3cb8d18,f8c863cd,1a555dd3,7f0efb4e,551d0d50,6073fd22,eb05f09b,9e983ad0,a6ce0f15,c2f5983c,6a28ce50,6c52f691,f30517da,36f3f192) +,S(fea8c35f,27e4794f,a9eab856,615d55d2,d3a02616,9b9eb7b0,ab7493e2,f67c85f3,c325de97,cf0a6e3a,98512337,8e2ba2e7,b24158f5,4aa97c88,9b54a502,8c148864) +,S(6612cccc,d7bc25e4,f8dab955,d5c1c9cd,d79d5f69,7ef4c510,77b5027e,d1f94b63,615512c1,7a378d0a,e7aabab5,253df9b,e9bd6a80,15c3b200,73d0499a,3457860b) +,S(c998b07a,499859db,4c9d698,33199956,f8a7ae67,9f6d3533,cbdbd7e4,a86f8746,d9e01371,919e775e,a259d81b,ea206e9,37d6b5e9,113838a6,6af11bf0,8dd17138) +,S(5c0e4669,c5cf965f,854fcd0d,23ddc908,c32e3915,460d44bb,520fb0c,3a4cc1e1,cfebbbaf,7e0f9a4d,d2f35ffa,cd0f7cb9,89f5eded,c8b3c554,64fb909c,675adea1) +,S(d0fdda48,30385167,f06f6433,c6a9d3fb,7ab3195b,64f9750b,cb2dbfb8,7158c0fd,4cde5577,6a34f61f,c82e1359,8618336b,f5c6e315,d10abc35,8f0020e3,ef1283db) +,S(d925570f,d78bf65a,5d48265,6222ce74,7cab153,19b7cfd3,7bc70185,fa4a3e81,3c399a,c448ba90,1a21a5c7,b9c6575c,ff52bee0,cdd00ed1,673d8370,87c1924c) +,S(45d2b56d,81f84bac,2077d607,484b0802,a023cc4f,d7818f9a,1d0c2187,98374c8,14ca18d1,92a9cb37,ad50506e,a25663cc,9829411f,848bbf05,3064041,9c19d7f4) +,S(6ad3ac11,8588c5bb,f793404d,df9a10f8,ea8c1978,65a93fba,5b7170b7,9e157400,fa25741f,1e242c3f,edf20e80,fd76292e,382eb5ad,1731ec97,a8762fe9,811686d7) +,S(5da24445,b6dc960b,793ea9e3,912b21d8,26d54028,2ddcbf5,28e1ebf,4a595f52,bc18ff8c,ad1dca69,8995063b,ff8a2fd2,fabfa0d9,8c8885fd,1b401f3,65f16026) +,S(ddff6468,11ce86d2,93b9b89f,865d7133,d7b5f189,d527cf44,5ed15efc,ad32b346,7260e994,cb22016b,e121ae08,12b01c3,4d236b67,13ea510d,b1aeda36,567f5737) +,S(a8b9ff3b,9120fa93,d0f2f50b,c8509846,c1030d98,dc9d5243,6ad9e69b,f897f029,5cbfa29a,4e516872,ea9a4c2c,a3b07efc,c866aaf6,a2b1d7b2,3597e332,8466c591) +,S(943077fb,177b9d6a,723c18ed,ac8deeb2,62875264,868eceb7,a2fc1a01,51dbcafd,219a9fc,41ad01c9,d3f8248d,ac7df93f,ccb6e312,fa5fd442,e48f4a95,18e7eb4e) +,S(75cb61ac,9c9817a,fdf60f52,4245ebbf,82b33f9f,a564d74e,9e810f36,6458b8e3,aa8135bd,f66f0843,460986f5,f9df6b9e,66441477,cd58cfb8,5b3adfde,ec03c367) +,S(1bb7e177,27cb8e85,4bc922b2,28e7f0ae,c34fdcf3,b9bad0c5,750e7c8,e31fccc8,bbf72886,1e855e70,bd9e322c,2425cdea,855ea00c,3e6263d5,2380da38,c0221ca3) +,S(9443384b,39e9dbf3,45b4b41c,f05d0f30,87f02471,c98e2a2e,afda94da,8bee8dbb,e3661173,ccb211f6,38cac6e2,27e3939f,e7b5da57,f6f7a504,abae01c8,fa4544ee) +,S(49bd15d5,a68caab2,8d51638a,ec1a9668,b4c27d54,d6691839,61ccea76,ef740d25,648fc868,8760e47d,fbfb9f0b,d995cc0f,77563b60,eacf3e57,9db7439f,3f18c6e4) +,S(d0c7b78e,92809e85,84d3684f,d078907e,c151aa78,a5832f90,73f945fd,7acfed85,a80d95ab,be494a40,b5529046,bc305203,be91e475,22ad9d57,7ab95b3,8e970c89) +,S(debddd46,3ab22232,5e98f9c7,5cdc875a,7c4c28d8,79748bf0,cd96bd7d,92eb9275,d103c8d,c3fffcb0,5ffb3dd7,a73af259,50d02064,b12bc754,ab594c71,ab9799c) +,S(1d79b17c,192a6a15,8ea2d6bf,9da07312,9f9ebcc8,54b02dc3,3f64012f,1bf33998,6ec818b3,dbc1c8a8,50d986f,bcf64702,c787682,bc408a12,b78e6bbd,4cf60a5e) +,S(2feb76eb,ff5d47e9,7289c38d,7d1535ff,74bd3b57,264c5844,9edfd36e,6e81ca1b,45bd4e4a,7ba3a823,dfae18cd,1f9cabc7,bb462e74,7899c899,378c33bb,89f93b74) +,S(14d0d90,faef5a77,f5372ab6,2e4f5efd,3eef1b39,498653c0,6372fe0b,b0682573,517f169a,80c2b930,4f530f51,d7196831,601a4f97,72ba1312,36ba92f6,36081453) +,S(7306a7a5,728f91c3,384bd9ad,b10b39b2,ad094137,46d2e476,fd522abf,ff313321,215e22ab,a5b9e87b,35bbd4e4,f492f1b1,574c85ed,25b86a85,a8e17b81,b7a915a2) +,S(85d8454f,870cdb8c,b32873a0,698fad20,fd24067a,737e9719,76d4d362,6a55aaba,36d63c0a,36eb9e8e,522a0715,cb9c833d,539fe7ee,111af3bb,951434b1,3117e4a2) +,S(c68f6782,993067aa,259b21cb,9e19122a,2f7da28,96d8cdff,c1f0a0e4,b34384c3,74e6e96a,331c3387,7ea484fb,995d6a3,8dcd493,6b2994ff,b7dc3681,3079e94a) +,S(81448111,5dbe5550,846d5c9c,3351c89c,e29cd850,6870a17f,e18e3184,4214dd3,75dd78b3,d4cdc29c,697740e9,2478689d,540ea33e,5edd6785,210abcb1,670d19a8) +,S(310b5b19,de24d478,f5f25993,a706f21f,24214e1b,cf614ebe,3e204815,60c945f5,8b42c6ca,f01d205,e09d017e,2e840d69,bf04cc53,f02a21a1,47e33327,104318c3) +,S(cf8a47c7,4bafe2b9,dfde4bca,b2e6d451,b0a90f11,c684f420,a025974c,bc4bc3e,f0b7b264,a9a78699,786331e6,2c8762be,614d8a69,c0a65c3d,1cc201bb,e34c97d1) +,S(a0fc6226,7352a904,e70100ec,63d3b60b,ab553388,69b982d3,3d747af9,8b8650fc,ba3ae54c,16b24eb4,64b88c39,521c399f,cd162eb3,f9966497,e2b96f2b,3b85865f) +,S(aae5dbe2,535edd47,584d0339,10045333,2e1eaf70,a4e1bfb0,7dce3c23,6cff8bdb,7cfaa5f6,e55806b7,bb278439,2473732e,a51d85c5,a3558321,91195533,b7a7449b) +,S(8a4e9311,5b897550,cbc0792f,b2705a18,5c317226,86da5bd5,b0a17bb,503ae39,1623adc8,d85f08c7,1751b976,daa17572,2442aed8,a9dc1692,2dde923f,d17cef2c) +,S(cbcef535,eca90b09,5295cd61,29010259,8627e1a0,ccbaff26,a51cdac6,87066463,651c2ffa,c2a2c99c,3fcafb9d,8c50e156,498fee17,3462fc25,9d87fb8d,6b212653) +,S(25971559,4d6a2bd0,baa90e33,62b9c841,bf12c4d,2ead6609,9902eac1,ed0f8c99,49e01b6c,c97404cb,8144002b,c33e8bb1,d974dd22,d0a26a82,94bad2bf,2875245e) +,S(b754d68b,2b049f6b,ac389044,c80f0364,a707da5e,3e9b2a89,1672cd29,b6f1445f,1b29b7c1,625e23d1,67092e3e,ddfc2f4e,bea55542,5f4ddda1,af2aec5e,4de37740) +,S(20aea5a8,50e68f39,cc7ca2fe,2f38b5a2,ab0bde8b,d294c40a,afa898ff,4f084037,6ed576b3,f17666ec,6aafd6a5,6fe7e1b2,ab1e883c,31230fc2,b4e1df0,3f59444) +,S(26844e02,919de923,3288cba9,a97eb85a,c6939fd0,7cd01e7b,f0d5e1db,e31979a6,3df9a8e,6a5fad1,266f11e,518fb60c,cc08a6c2,832c45a1,6c7c5520,5c9ebfb7) +,S(11afca61,33a69aaf,27cfd413,becdf9b4,efbe0a6b,1a38b3fd,485dca95,dc28414c,8d4b8790,c0677fe6,22847bbf,8e47b400,9e4678fb,b9c9377b,2121f5ac,db737b1c) +,S(c46e14d5,2fe25d65,37bbe09f,ce7e95e0,4986337b,23e7160d,f3d78dee,e8d2a63d,a200fdab,4211e9ff,2d008dd7,7ddf67a5,1f04b9ef,43fa1758,9b175d58,f5d2b723) +,S(570aa12,578afe09,245f598a,210a7605,2412283c,8f58e142,d31d9cd8,d455e4d7,c28fdb61,f8e90de1,d43439e0,8512a071,9a2beda9,5d0a5601,b932c0f1,2cc50ac8) +,S(d312db5e,88a1cea0,28570b84,e3854e16,224236ac,cfb14d1f,3a33108d,fbc3fa64,ca5e5089,1843383c,845fa0aa,e031c930,64b8aa4a,ea72e6cf,36b617f6,cf97e1de) +,S(1ee22876,9981670e,14da9ad9,9279f001,c9be5eb8,76224a6d,1fe4ec08,aa398087,62101114,e303e37a,dfe42c65,54460d4,b68603a2,580c7452,6ff31c9f,2176dcc) +,S(1fb9cacd,e2b84ff4,ae85511d,1c964c01,1700b9d7,2d4c04a9,4afa9aa5,57bb81a0,430b73c1,295c5f2a,d6d9f59f,74201435,15b1306b,2cfa038e,e0456752,5f737fcb) +,S(d04cc82d,d3cf0b47,69f2934a,9807e743,4a998bb9,fa44d145,f191a176,611c085b,7a4a19af,b551d151,c42cf1ca,ce123dd5,8f4c13ac,c865b92d,15520169,1be5fa9) +,S(1e4cb208,8bd4d17e,ba8b97f1,f27df5df,8a544f8a,548dde15,712e8704,cfe7e9aa,d805c61e,b72933ec,e799930a,53fda3f3,db066396,5c71be51,5e8466cd,a59b828d) +,S(520aa398,707a85a0,d672bbad,cbcfdab8,8f769f69,df3de8c7,17d6dc48,de51590d,98d344e4,a9047a22,c09095e2,f1176aad,3b54724d,998e052a,39a52554,4ee9ada8) +,S(3a9fe3de,165bef7c,2e0d45a0,60693fc8,e7080ac9,783245d7,a79cac6e,7b614ef2,5189446,fd3fdeef,c7a895f5,2934c888,7607ff49,aa6d798e,151061ef,73038d4a) +,S(9c929dcc,c26c0805,e4289a3a,2b6d68c9,8e3c6806,43730056,65fd8e73,da149938,6dfbf940,b4c29209,3f39f6ba,b102762a,c81edf2d,aabe6dd9,56aa140d,396ba2fa) +,S(355b60ce,f4e00651,8baf1e42,e5e7a08b,ee22abab,c69aca66,d117748,89be419e,7f478a3b,b277fb10,5fe0eaaa,cb53fb73,4fc01dae,bb44f1b1,ed146f43,fc2604e2) +,S(76bff14f,21ad4442,e1389bb1,c11e1019,ea966091,97439705,dfce7bf3,d7f7f37f,3855a291,376aab12,b5f4da79,bc22e02e,a29a34f8,9223f07b,93f6a549,8289d763) +,S(91bb9cdf,10577a3b,7e23aadd,35883396,becbb4bf,fa22e1db,fa08f8b6,6156b454,ec6cbb9e,4f149b02,edc53e93,d1611b5c,a3edea42,c2ca9a1e,f2cccf25,94edbb91) +,S(9e56b10f,1ba86c61,36481204,f0c45b09,a7dde7d,4eb32537,f8c310a5,a8865562,cdf3453d,8552c16c,48bca1a3,79ee8c6a,8b523eaf,d1862d5,16f4361d,5a8aa67c) +,S(4e73594d,af286bde,4e6e28a4,23930571,a11dcb75,4e9a4af5,f4972d,988e09f9,a59f23d0,70c042ce,e03d812c,ea274035,af0d8e53,828a418,cd1b51f1,458a36e7) +,S(156fdff7,e92d89fa,691d3c55,7cb2c614,393d5973,4b11bb1,d9fac953,3923d860,4e929637,c448760c,ef6d68f6,4665023c,bff653cd,23132ee,620eb013,a13b360b) +,S(f2c8b650,eb372336,5d57a905,23d34db9,70d23d52,1f893569,98b0a7e7,ae8ea03b,bd9ac314,84a99bf6,973422a5,60bde9ec,204488c7,6739583d,72f42a87,181a7881) +,S(27dd3657,a4a37430,5f5bb92b,a7b816ba,88d526a2,1ae316c,d4a4e67a,c751e3e3,880cfdd3,c0faa401,6afaf3d5,1fe975e4,8a756b01,f7d84e8c,226424b,83be3304) +,S(f6c25e0c,19f7357c,3cc5d98,1cc4b7f3,7a390182,c2673708,1c64f163,ebb3d1bc,8c80b3ea,7c05a0fa,39944d66,643b3589,29534152,adf37c2f,183e7c22,8e89e43d) +,S(c678800c,71305ea1,ea2847d3,d9d37612,ae280305,716fdf45,87ac7f4,a7fad4f2,be5ffbf4,d2fb2b8c,ddb49a42,ebeaf5d8,55d41d39,f106e2d9,2d549e88,6c0b63c6) +,S(f35db221,e66b7786,fc9e2d3a,68f0d1ee,f98177df,a05bfd30,931912e8,13f6d560,4899a808,e6b2040,89974ca5,afe5b1ee,9eccb15,36a7c7c7,bb67cc00,e43f29fa) +,S(2b93e37d,a915633e,db9843d6,c33888c3,5d944f04,c4074deb,d021c639,93e69de9,eb3d1095,c6ffa1cc,93f72a58,f1320efc,366a4a5f,c7250167,6f2d3c88,4b0522f0) +,S(818023b3,90454716,f51913ab,fdb406f,e4875b94,8b14a331,60901d1,66091b69,b3f5718a,6224e447,c5af3e1,f38e4f2e,6fe57389,fa7d8990,f98e44c2,a227e7b1) +,S(1676e6a,244ca69d,b2ae565b,65b14184,6645ce7b,213dc1fb,db33f060,3c662bb6,88e1cba5,deef5dc1,26b20830,d2c7c0d,aeb9689b,16ecc1fd,cf4a31fb,822e0298) +,S(2f931a1,a92537de,38629d73,323e2f21,d9657454,30e32e4c,3912cb21,dc298d54,93292d08,6fbc0fae,eef6fe43,f5626711,95a5b2d1,bdbbccc,52e1dfae,a30ef904) +,S(7ba4d612,f0dd8c91,8bff20d9,b4f45486,be8a32c1,122601af,2d1730c8,a1fd0279,8aad7aa1,f5b06431,b2e20af1,4cd049a9,3018e3c5,bc4c4310,d0234e78,d2401179) +,S(e8dfab0,85fe7a91,6a79b2be,723799eb,d6974d08,ccc8e3d8,5286aa61,c81d5e78,2121686e,519db556,e6595d47,a5487bb8,fbaec53b,b18dd81c,37dbab40,6e3f926c) +,S(7d5623bc,b1597ef4,6f907106,17eb2e6a,ea751da1,5535a64c,94c90c9a,d84ab5e,dc639f31,8004c4de,31514b70,ebe2e8b8,701b3cd4,5d66b7e4,204dcbc2,a0ce7575) +,S(8e81e2d,8074bec7,48986194,b3b82200,1e4ac916,81de2ad5,7b9c84fb,88a7ffa3,4085ec1d,f4a34480,ca56f6cf,762d03f7,fa94e99a,8df93e6c,dc781499,59fa890) +,S(a9d5a3e7,8290ee62,5e18affd,169c6829,73e68ac9,2b5cc52b,53ed3a85,a68da71e,1cdaad6b,f9eb5889,5189ed29,8793388a,8d6e8b20,ce47a536,ee89be4a,b4805fd6) +,S(4f7c17ca,bcda7c0e,3b7b313,4312f848,984985f0,e46ac497,d64b123c,57dba2cd,860bedd9,5f470c3b,f3cbe7c1,d5070cd0,c877010a,9baa8f5e,888b5ca8,a34a25d7) +,S(478f3d7a,9234a7b9,a91bd016,f55734e9,34b8c3c,535725b5,b4fbb0ab,9fa64921,db97a394,f0253f18,ebabca05,c21e9a7c,3f253054,d83f2101,8ffb4d93,b8051a1f) +,S(ace7e57b,73d96e7d,69f7cc01,b1da179f,3ea09aab,61fba329,f2552eb1,fe7963d5,fa0251f2,2b8dbd04,ec90f824,be025965,7e213e4c,1030611b,796efb89,26908918) +,S(cd56e867,f5170771,8253757c,dcdf69ef,4401f2f8,a1d09f24,374040e7,a908d1fd,878f7216,52cf0ba4,8a2b29a2,2f620c7d,83e4bd84,277b7b90,a454bba2,7f338fe1) +,S(3042ea5c,d9d2f89a,64b1dd24,97b25da5,67a2c672,ba067e33,e923e513,ee4d5329,b84c144,d5516a14,a0a8568c,d6d227a2,d3fa1d86,8e05e612,81ce41b3,5a68b1fb) +,S(55e088d,a094ed26,82b042,bafa0345,30a2e291,57af3f1b,ce351ec9,9e712ec0,c6f8297f,64398324,96091933,514c5449,cacadf,82d51db2,82c9d2e,c66b33e9) +,S(bd2db13a,69dd93bc,a37f9181,7a57a0e9,e0e0da83,eed71875,e63b4691,3b408966,46e12660,f9489b2f,123c25d5,f65b3f8,187ee6e4,405b8cf8,78a742a,9b0da2d) +,S(7d7a5110,cdbb8455,5fe678e6,24ed1a14,ef7c5219,986a7ecb,6095624e,7fd3d210,c9578dc3,c0a54524,f819d640,abaa4a04,626a46da,15c0867,72774a59,6ac6c37c) +,S(1ef12317,513b1354,2a966981,34edabc6,fd4e6d29,1bd7782,5bdb30f4,937a3fa7,e0e4b688,a391bcef,3451cee3,36a8c9f9,12319899,4684c039,909489db,590e1ade) +,S(effa0403,766fb976,dbec6b2e,332d2f12,9f463b86,8b9a1d19,49d30cea,1606baaf,8c5b1c83,bf3efa64,f38de1b7,da03de17,b0cbfdc4,5b4a03c2,38221def,bc1cb082) +,S(dc9271e9,76a9a042,5565f022,273af5bf,20bf780d,9d0ea069,df5da661,a087d65c,dc8679e7,b27bd30c,986f11bb,1c73b76c,55ce7c7c,621490c1,944cd8a6,b63d6063) +,S(ae1ddc2,1e1ca02c,218f6946,275501d6,a1336b93,ddb1b2,384ab414,b8264619,21c6b39d,300b6125,ec8e5c8e,19aa76d7,32466bc9,a887c76a,1ff4220,7db26b48) +,S(aea2c00f,fe5835c9,2b892480,83164635,33dd989,bf2bca12,85cce3ab,4e82cf5b,615598cf,935a6112,42a20e84,e670b193,9aad65d4,53ba967,3e8193b2,93dfa97f) +,S(913d0e16,34b43a86,f55b30b6,5914767d,b45e723e,636c866b,6348c8be,8aebf696,e14c6607,51affda4,d02574af,43172024,39c0c10e,148d4305,1487a5c4,39971a9f) +,S(4f0b3354,775cac80,5e0d6191,6bb0f9c8,40fc163d,8878c05a,301252d2,3bc26fc0,b0b75486,3aedfdf8,3a6eb2eb,5f3e25f1,6c0e1077,6e855ac3,fa8fc3f3,bd8da1e3) +,S(5da21940,73cc978f,7673b2f7,5c65ead2,f469df96,f80883b,181a3da4,f316129d,8516d080,743d2670,570d5e63,e19eb543,237b1db,6285b1ea,f3bc1459,8ef311d1) +,S(76e1b330,ca7af91a,ccb92fb4,63404d36,a8fbc132,269d163d,d4beeed1,6be74a23,10b39253,c5908ef3,ec96f2e5,2ffa0dec,2ce2800,31a791,bcaae0de,1e7f5326) +,S(c4be488b,2f210ee1,82f91263,78b23359,8fe8ef57,671ddbe1,d76975d0,ee393e64,bec1392f,a1a09a54,480beadc,b98ca024,b910cd70,f807eaee,10d4e270,bb79f449) +,S(546918d,a141fd77,b1f13848,d3d608c0,25f5be00,4d36a542,56d0ca5c,9333ff37,3d1d4c7,9684be1a,5cf00d71,6cf9a1bf,b0b82ba4,833704dc,c2d23e8f,39914ac7) +,S(cdfc6fc4,2c51ebd1,2df368b0,98ff0f40,2e6df8b1,9c67e10a,320c1960,266078de,5985e930,23e5264,547bef45,6b3774f3,c2689411,b6f7463c,febe068,fd89cb0d) +,S(7730fccc,593b14a0,6f21eec6,2408e146,36f0e4b,4718ce99,c99bbcd4,7cf03034,f17d0fe2,736e412,9c30151b,492aa7dd,52beacb7,6f97001c,cc28f991,3e016b72) +,S(8aa4c3de,474308f4,674c7b55,9f895bc5,d855a7a9,5fadf090,5726b354,7941b3ea,f275c44a,eac2623,1bf4ae9a,df403ed2,78d9da42,150c98be,7dbd5f71,5cb8594d) +,S(15926da4,97a4e925,8976f87f,f8940491,f32d33dd,7855b13c,5cb54d10,d325bb79,15ac60b6,5b1ebfa8,e871bf67,ec9f71b8,1aa03c78,82880d40,d35f16d8,67ce8e51) +,S(55f4f4fe,aa741574,79462b48,f001688e,fc99f545,d4566dbb,35faeebd,f5b1710d,80ecea05,63f24c84,e767b894,57dfec3f,d015a4ad,b6e3fe0f,662521b4,bb02d15f) +,S(95a56acf,402f7f33,fa357c4a,e4db337e,f04a60d7,4cd73e0d,b137f68,e0e93cee,e72405bf,b7c9ba84,16f4e72c,a30c9ae5,d21c89e3,ce5b61a4,bd521b4e,ef6501fd) +,S(28a9fd90,b50d7bb5,13034b7b,c36a9783,c2a697ff,499bcd6,4b0881ab,8e7a2aac,76b151b0,5c7e7ea4,9a263596,81508781,a37cfaf5,14fa1720,b583faa3,9794a788) +,S(6406dd13,6d6462c4,a4116b93,eac38eb0,b571e2d3,99cdb93c,b36fa5ef,e734b672,3974c77b,92b8e614,52d9c9d0,62a911f9,b4c5eb1a,977da74f,defea358,5dd7fd1e) +,S(8fd8f23f,cb62e1c0,94fef903,9933747e,9d40c37e,376703c2,e3c8b666,f1066529,8b0b2f9b,de10175b,24a9099d,75926296,5ba19013,20912baf,3100e057,d4ab5c8) +,S(51780a2b,d4457368,9c0679bd,c8c94e43,cc525bab,2d2723cc,3e0eec15,63a1a24b,5026cd9b,e178d1e,c746e5db,29391c4d,4b6b08a3,3530fdfc,64603fa2,f0e01109) +,S(aa01493a,e7c7f9bf,3d2fe0e5,cd939dca,5b8c8534,6848d065,beeb42a9,8c283561,a08f0b45,4a42b5e3,6463af14,5199e3d2,1783e908,b3e130f0,e9e88110,2cda82d9) +,S(cd8ab749,d4bbe6d4,ac81c4fa,d6a27e81,e4456d4,196cdafb,97fa6053,18ceab70,fc6c7739,bd68a20d,e01c9249,3cd7ad3d,ca862183,b0529231,da32f1b8,5df59beb) +,S(e69cdef2,e824501e,7a17bc54,e0bb4886,379f56e1,c59a27ea,e00bec5c,55fe3e32,e80ed10,40375398,10125338,f6291791,3fe735e6,6c4bbc80,f41d858d,8ac41cd4) +,S(5a4e72d9,182b1f7e,efb08af1,9e3181cc,f6b6ec69,790184a2,f30188e0,dbff8a8d,2d1cf7f2,d5a857d8,3b31c588,5ac33a5d,7b62ea62,7b5b0bee,2d912502,2df9da19) +,S(9e000859,48bd6211,410416b0,3920a79e,d78f154,5dd0330a,8f23d1b5,f8984a2b,36ae0492,412fd3c,c230680c,edb0dfc8,151ab85a,d8d3338,db611674,c88e3f62) +,S(7e0b97ae,b12f8734,ecad35d4,aea425c7,99f88485,f0bc8d18,ac82f213,258bc6ff,e76f4e24,a4be1b6a,3fc3b0b8,ab0b6e5c,ab95fb40,4692f7bc,cc7640b5,69f3946e) +,S(62285cfb,1bdfc495,dca81ff2,867878fa,b3064be5,81be0c86,b9336c6,61038ee2,f2c91be4,5f60e9f3,40e85ea0,8f159dd4,a65ca8d8,21a6e4f7,71f4dc17,849b5161) +,S(70234e81,2de1b5cd,699870ef,36d5c19c,cd3765ba,eef3cac8,6f126a,a6c82623,e2dbf98a,f92a777,263f3c92,4738ba,3140f769,fd5b6ec,7139bb8,41330736) +,S(ae770461,8f31d5cd,ede40ef5,7434b0a7,7dc1b4e6,7f4be5e6,136978a7,cd5db07d,a4e25164,65584487,3f520e42,74037c,5cc43e12,95167b01,d46d3e93,d93d16e6) +,S(cec7ac8f,e4db698b,48ab78fb,768e6094,5b7f4ce7,d643e604,9e349b6f,246f2b53,491bb545,99330685,433bfd2a,7a4fc831,7f5d3cf4,80d6bc32,5ca6d001,b65fadfb) +,S(80276e96,b051e49f,276f62e8,35622979,852e4cf,c2ff8665,8db4d5c7,7c5eee36,f510d696,882589f0,870d526e,5f7513a4,2c4d4ce5,3733f30c,f06d7dc4,f42f6d9d) +,S(b19027d4,9d85faae,e6163934,ef8f8508,e9e351c9,82bda741,b8944317,da85b27d,c12461cb,dec0e69c,b378443a,f2eb042e,8439f812,205742d6,dbabb372,ea47a824) +,S(57599d4a,cf07c001,d3d3fc78,aae147f1,989c656f,fbc43081,29e05b2e,570c792c,5cf8ea0f,d215ba16,e18e181b,1b4b4f02,38b57af3,9010ba23,f13bbdc4,18514b0) +,S(5ab76283,fa110b82,c53cda2b,653e763c,306e6f8c,2b07d490,bad6e418,eae6e4b7,ff365643,cc2a78e3,17a7de95,b37b3f83,aebe8272,4162a884,f7437aba,b55e8ea0) +,S(7b93d9e4,8602d7b7,ea52dd45,fa0031de,66ea88e4,4250c898,64903e7f,959d4a85,99ccb201,5ab91fe9,a0f50a0,fefad6c3,f6dd060,1f2ad099,a4851d33,162b52f0) +,S(2d5878b6,af642776,41101be7,2b37b4b9,1de26a21,b9610a93,7ee2ce2,fb96ebb6,138ee05d,df4b004e,820815d9,46716f2c,a887066b,b7d218f5,47d4bd7c,2cfc585a) +,S(1d637068,eebc32da,24a0fce,2e41cb57,cf1ccff5,38cfbee7,ac13269c,e560af3b,6a932df1,a2896445,6685ce54,2ddca0ae,d589a494,b7ee89ed,1b4ccfd2,c276eadb) +,S(82babb6b,460e1235,f3187a68,912b057,26a478ef,fe929a3,9479cf04,467de64c,3a9f4e33,603d0396,1f3c11c,94d58369,5de43a1a,327d0faf,f51089e1,1345647f) +,S(ae86e71d,4e715bfd,b63d899a,9daae610,a01c5134,c1b9262,c276dcaa,b48090a,3564b48b,fcae8f7b,feac4df9,3b458678,6f2f9e83,9c9c6126,a301de29,4cfd7b5e) +,S(35a5c2fb,fc661cc3,f4ad6ad7,f1eb32b6,664c50bc,91f4000e,f5774364,2aa413e8,4a5029a2,887f6648,3e072782,68a81c85,b901d692,4ccae752,7111bcce,cad6cde5) +,S(386bf07f,4dd508a7,61e7c29a,dd251d9a,76324496,6c6d760e,cbc6b069,5858e2dc,d4f14804,9c1fa038,1d903e19,68fb14b2,f07c621,15a81f41,e811ad87,de407696) +,S(dfec381c,9bd6f4cc,50ac445e,c9104d87,f9c46c7c,cbd70909,fabde6aa,12346f96,5e7344f8,25b6649b,df97ef3c,ad96945b,d1ce0f76,a48b7f2e,56ccff02,797553f1) +,S(3606dcc6,4db04cc,5493e0b7,ea0bfc2,f8a24302,d2358aaa,7c1ea8,842abbb2,a5c8743b,bc4d9b74,b8b569a1,feca3667,87eea8b1,be2025ed,bb547e78,5687301d) +,S(3441e737,e19096e2,9a3025a4,71d192a0,db086038,8e76d2b6,66052825,e52b0016,6d10f7e,d5cac98e,73c3ad47,bb233a39,5eb9bd4a,37861b9e,7dbb60f7,81eef0da) +,S(b2c97c2e,460d8c34,b5ea7a5c,456a9cf5,b1d902bf,5cdff030,25410a,26636e99,67133081,a8c8ef30,753417b3,cf6973e2,1f151e1f,27216bfa,23289ea3,e2057b7c) +,S(2740bd8b,59a43ddc,f8ffdabe,d59027b5,9b7932b7,3db45a97,3b6c1501,57e045fd,8106f2b1,f69f1ad4,2c0787a6,3c137753,987aabe4,b0ebd82e,1ea7807e,93b02a85) +,S(a1acffd9,bdbd94c0,cd8b4f40,3f9400c,aaa8e269,d8cc49a6,9ee565e1,89b6c21b,62ec7d7e,733f518,974acfbd,4f4e4f44,bcc4fdc0,cc194a66,df7c16bf,ef0235b) +,S(50bb2ad6,8d524acc,3278c0f1,538d3aad,9a5357bd,332d8cff,6301f417,57349847,d8fc33d5,d03526bd,b97d4c6e,1ef018ba,ba4b33e5,9677f139,b8535287,fbacfb32) +,S(ebdfb262,6031b4fa,d675e860,12288101,948e2ca0,8fd0d32e,c9fd0838,5f4fdbd9,b078066c,c470113d,db3964e4,466e319b,4f853a42,8951aa0a,9c4629a7,a40dbf2e) +,S(5daf4c23,65482a2f,bdef73e8,208fd5ad,d57b35f8,171541f6,3a48b742,e98bd48f,9015bfcb,5a248607,f5ffc83c,4fa3dfec,9cbd11f2,c230327d,b2cc75ca,2e87f967) +,S(22db9c20,5d840814,67a19517,f353f825,238c905f,2b12a77d,8551cc9,ee8533c7,680a1bc8,23432a28,a3219264,2b9acf0e,7ad19e29,b604631c,85f7038b,386dcdf8) +,S(404435d1,2e8be51,92625213,f86e6e55,9fb44658,44694d4f,4db72861,d4c100ae,9ebce682,b321eefa,e8e04776,8f82491a,dd734e5a,941e0f4c,b6284f78,6d1586d1) +,S(dcc36eff,6831a6de,f8600d1a,3d52da1c,fe59df9c,cc69c9f2,60c26532,e837530f,8609b5e6,7360b7e8,5ea98c13,578566cb,b6b385ff,6990d4d6,5b795acc,1f77a893) +,S(ef0dfb94,1a1c383e,4a44cd04,c633cb48,e991d140,45e2d9a8,b4353e79,9c1a1514,2685d60,f2a75f9a,b733afab,f693ed05,9c3599bc,3e3346d4,61e5c86a,f76f6f42) +,S(24884cfd,c6c3efd6,b4d2e18f,2ca66b06,410b0fba,d9d61b30,86abc537,1d766b8a,7c1cdc85,66f3daa7,d395c6bf,8a23c130,86a78619,aa68519e,4657b17d,ace2267d) +,S(541b6691,c7004a81,2c607756,f81b6f05,485b0708,678d9a45,de74632,4acd8b20,ddccf301,8a3c429a,c749f0d5,2383784,cefff3df,ae98517f,c7f5e764,c5c609d7) +,S(5462969,67262e3f,a905eea4,ff1f8296,f792cfc8,bb9dbb9a,138242c1,a1fa822d,63012289,8e429527,4fad8e7f,9865659f,da2d7370,10f6e185,70dadbfe,21e6880e) +,S(94070912,ad2c129d,cd122bb9,704616fc,3ddfd949,6ce9f6d,6e18020c,ec848e83,9c3b38e5,6bfac9f1,4c901080,b5a732a4,b5d261f8,2b571536,d1fba6d9,b714017b) +,S(2a6330e4,8ddc661e,afa8071c,c1417b92,ea73b060,3653a017,3edb92a5,6e3ca6c9,b38c51cd,4754c350,ba2d56a3,fbf4fbab,5802bc38,6b5516f3,d8b761e3,c3fc7d79) +,S(9142e493,5f95da17,5ae4078c,5ff5a7d4,8c418c1c,7efc4c39,dacb047a,d3f7424d,15165a74,c47133a9,e8684ea5,5a4f9b05,37d0ab7f,58a7eac0,e698adcc,b885cd05) +,S(2e198b29,5cac4dee,dba63ef3,888d47e8,bb8d372,7fcbd69a,d1eea656,ca85804d,31dd5db3,e700ffbe,152b0c2b,1b1c830c,eb382468,1c522e63,676b9ec,6780fd52) +,S(b44e35e9,b2efcafa,f123e52a,7bd9bc58,32a671e2,fbf0264e,5a3f536c,b272f72d,8d489fe3,beb39fd4,2632b80a,1243e64b,f80c81c1,3934ab08,71c16a5a,5aa43423) +,S(dc99ad01,e1324b6d,b5ecf365,c22463dc,f609f0b5,90e6d3be,efddf641,b83a3cd7,b4b422ca,2feabeaf,32c0ad6a,4393c087,a4c1e794,47a043de,cf0fe486,ed11b682) +,S(e174838e,eb991c2d,cfaaf89b,b868f4d1,3029ebfc,ea286376,48fc51ba,f962a359,f80dcc87,27cbddc9,4bb1b60a,4da5cc74,238dc62,821a9ec7,c05ad2a,a0e7570b) +,S(6240358b,15bd16d4,42cd79a4,8b739eb5,4ca21d18,b568b497,65d10dfa,a2583d33,6acb054d,724e7550,ed6d8caa,8a436d51,fc546116,df33413f,a613c0dd,6cec2d00) +,S(a757fceb,560aac0f,543a29b4,bfd3af7a,aa61c077,2c1c07b2,e6268342,e63f8d70,5864ceef,f80855d0,fb4713e2,7bc6a202,b4c07450,6749457b,39d0c40c,f40352da) +,S(f36e1073,57c3149d,902e9416,2e509d19,d4b1f498,bc74814,3a1bd411,2f777bba,d4d7da2d,139a2693,e40d9396,76c3fe48,45f74b43,78f0c25a,14aa9698,3bd1993e) +,S(84ef2d4b,55a40ff7,13934949,b6277ef0,4a8e0e4f,cc49420b,17269d59,224af99a,57affcb7,1d3508f,65ff2b7a,b1d6752,60465e76,754efe3e,80a83085,85ea73a6) +,S(138ef64b,274ff66d,7defdca3,4ff28bae,96b046df,408b3f47,84f78784,3fce7576,ebdd04e5,1d517c43,412010cb,d03252ed,dfe97912,ee4a7d4c,ed98551b,fee1ecf1) +,S(e82f03fe,2ba05692,212a1145,550d3057,2d454728,e2d9989b,435cb608,3d1f318c,191a6fb9,cf295881,92aaaf1f,ea34e123,613b2937,602650f2,eb25f63f,5812dcda) +,S(56a68400,676b6fac,7eb5fb3e,7f345ec0,11117098,c0d1a96b,e44bd2c8,5aafb317,3a41827a,5ae1b7cc,5b9dcb5f,4c41819e,f49ff2fc,ce1d1949,3f5b0ce,9f1dfb4e) +,S(82f5747f,1b353db4,f3afe255,e1f2660a,8f735b1a,e40a5008,4efa3c32,dc1309f5,42e8b903,f4ccc01f,7e87e4c6,71572bbf,d05f9ea2,a4cf7bd7,dd5a9906,264a0144) +,S(89136941,c47b1964,a4b89e0c,376b3136,80ef76c1,19e02bb,bfad594,76e0cf13,b3a2a785,fb6a564f,f4871bc5,177c635,d722e659,77f621a,f6f7537a,2c2d3f3b) +,S(c6250969,c98f580,f0cb51,eef5d8a1,b9954bb,97671df1,c7360a5f,42238909,83d0324c,915eab22,4dd3f935,e197e0e9,552d5bd9,6b1ed240,219731a3,58ec724) +,S(e2399ad9,63913200,4664a481,dea99d85,ad12938d,dd0e4184,27411fd4,8fc7e3b4,60fffd62,beae28e2,2ea836ed,389c3780,5d8b1fa1,fb10f298,f9b54df8,93c69d47) +,S(4d00b1c,186d2992,9a6383d6,cc8d1796,a59c5d7,c27cc7b8,5a1f821c,3003d959,8752ead1,27eb19fd,49dc8ce7,a0b7c7e6,dd4aa3dd,6f8d5cb4,687327e8,ce487640) +,S(344663ef,8b8b52c9,1aa548e9,8b7e1c48,933cbfa3,59644318,5ea973ae,9264121e,73d099c8,9dfe8eec,b957e624,bea5673,6fe3885c,e2f72b06,dfeae760,514d9efd) +,S(37ff484d,dbd22a98,333038d4,7949b4f3,f411d3d5,7ca319e3,5c788bbd,a0de6648,55a5394b,51618d81,d2fb1455,4ec1f5c3,a45cf086,83980bfb,a9aae6a9,20547c67) +,S(cf3bcddb,ad5066b,909339d1,4cc6d3d8,d2881f70,d6f12e1,4d795b06,17556ca5,e12686bc,99e20f20,6220e402,32760224,b61b90f7,8b7c0b1,3c197a47,11678a6f) +,S(961d02e7,db8fd1ac,a38f9110,13d74a5b,d35e47a1,49818d41,49f02edf,5da73402,9462eaac,d698677c,16515ca3,bff217e9,765dc8ad,fe2fa82a,1db05158,71fed240) +,S(43e62f90,574a8e65,25d22ca6,74814ac,3648b724,45a535c,5e1af97e,b46ec3ba,6def0a56,17c1cf17,9369e20e,d18d0503,e141dc85,9edc73a3,18f6a35b,3d939734) +,S(9e682b79,9d79018c,352113cf,98de93b2,457e4b4,a962d160,33b80052,57dfdbb7,9db9cb16,bfe96343,89c468a0,6405d2c9,b2983354,ee9a81e9,3f23ec5d,5049041b) +,S(790187a7,8079b59d,b2a22d13,8520192,5f9682e4,2002c8f9,436876e4,d5a1a8b4,25c8b46,f6b43180,3654381e,56a8e6aa,ade0ad1d,6a6044de,17e9592c,affa456e) +,S(928a4b8a,6c744b33,99e22d65,35a21ffd,662a0dc3,d5bab1d,683ce555,f0169583,251fee84,c064954b,e1daf7e4,5fec87a9,323d55f6,687fdeaa,75d4dcc9,9a6fcf84) +,S(684e88da,81182f2c,ac5872de,8c98b86f,a6ffb73e,9bbfbfde,6c16c3f4,be084c88,c451b59d,fc9d62d5,921ae45f,f7557ab3,ad179eb0,6afb8bb6,bff0645,cf6383fe) +,S(8bdd2e83,a403b0cd,7a8ebfa8,6949bceb,8e8166ce,a8596f6c,ad7b7bfa,4d174343,7220c147,97f682f8,7d707b2f,eec937c3,643bb359,e9effbad,8ccbc8f,a8177172) +,S(c3fa3b21,6e3c4952,85258ef5,f921f6b,b7195869,e46948b4,b69e80b0,be399543,eb2044ed,21f6661b,77088050,3026c2c5,5a519ca1,748bb10f,c8eed953,aec1796) +,S(1c063c13,eb2d2959,379fb0df,50d355e0,195a8bd2,e642ab48,3d6a38a7,af7e63a2,20538f92,d14c9ff6,75c5005c,8c40e26a,b0042874,7f446ed7,cfcf5737,870f2abb) +,S(60c06797,7b54c18f,4b82dc6c,227cb539,6c6086de,107e3999,81f1b5e0,f31d452,3f4546c4,e3eb11b2,e6fd2617,b57afafe,ce0e8bf7,aacc13b,73755f18,dc98429a) +,S(fc15cf99,92256abb,cf263e90,a231ed8b,421cdc2b,af185722,c0af008b,fea36856,abe346a4,d9c40853,70dd46e4,69a3bf24,c01dbd97,f5c9997e,27634dd7,c635d798) +,S(f6143985,18173bb7,428da262,bc8b07b8,1a92b06a,65926ee5,c6f8fe44,27249454,b1d832ef,3a2558c2,d41f06d,4be8e5e8,11fcede9,9cc784aa,b9f3ce18,d701cf84) +,S(579458a9,62218442,41e68542,76a116ab,1f1fd43,109d4bc7,ad5216dc,ee61b5f5,33e56474,68c157c6,15cc8d46,135bed70,21274a4d,97aebe23,7bdd4b61,2c53e2d8) +,S(637553dc,b0804132,b5562678,fe355e8f,abab2001,fe17e3bd,8c051e7a,41fe62e7,28bd6dc2,ad586e1f,f1a6a951,167ba14f,6989b296,874e0d3c,31917920,9b572dce) +,S(eebbd70f,6f64855b,e21ddb92,3e54f583,89e99ddb,577fa8ea,66c3722e,b580e2a3,3d1b6979,15b50345,4a55606e,ad3ac4ec,7c986698,3df60e1d,edc86827,32fd4d68) +,S(4ec8931f,2bf4b1da,6891135f,37791236,3a6e888b,6e5c5ddb,2e350e42,686c56fb,d5f6c679,4c2b306a,bd719770,48dc3e28,3cbab910,556919f5,35d9df23,56a09bc7) +,S(6b5a7f76,fc5b7e7a,7d84deab,ffd5c771,8f49375c,cc4dc1a7,35bc253a,bee9730e,1e935dc4,8fbf5f6a,b12562df,11855757,9eafb8ab,f6ad8b7a,f8f9df64,43b58af8) +,S(2a3129eb,f620958a,4264c759,17e29807,5f5b73f3,21d4cf27,683e4788,9e013af7,e940f5e4,c1fc28b3,fa346ede,fdb8ac7d,4066732f,a6c276c6,89a1921f,d1020da4) +,S(8bfe81c,e99443f3,78307f34,5bf2c826,94a8723b,c4836d12,4c6f5163,bb28b5ab,58b16e27,857e5faf,9768edc2,68d177a4,87de9848,52e65348,36464ac3,f0433c71) +,S(47820cd9,449c566d,2d39daf3,ee4d356d,5423692,a050cf83,1c1337bd,8ad61be3,48dfae03,2988a810,6d069d,22f27445,1200d38c,a9aea665,5e5f28cc,9952c2e4) +,S(2d7f03ce,8e453605,89d221d8,84c3ef28,f9e8dc52,41d7d131,cd6e9921,55a934e2,8414b3c0,5a39f38e,1f14d55f,e28604f9,16a7b272,6d6c843a,57a2b8e8,b7ee9c72) +,S(83887cec,919d8c86,c2150d4e,e842505b,f5a5b06c,b3b1e67f,1dc2005e,99d8e37b,fee0e7cd,f5c9eae2,78d34fe1,2e58845f,fcea9de7,f9883e32,7a2ee941,92b4c009) +,S(519f9e1,138bbab2,da8c03f,565c3fe0,a9d0b709,1a7b50f3,f37df191,d1ca1e24,64d6bafe,2bffce42,3f631ec0,781e96f5,ef6cd6a9,ade6b9eb,367c036a,1a61ba9e) +,S(8091f78f,84bc74ea,69ade208,72936152,3946ffe6,63edee6c,3cd13ad9,4a8fba61,1aaaa58b,af84688c,1cba5d90,d700aaac,6765faec,39e7a212,fe13a9cc,b0bda1a) +,S(50abc00d,d3fe5748,7c2a5343,4a36f10d,8388cf20,56cd5bb4,9ed8e97e,7adb3afc,ab8bad95,14b6040f,b1c64f01,d9b9db01,670fe0d9,3add5d6,8a80e41d,8ded25ea) +,S(a9a428bb,4da01f44,a9e4f253,3436ae75,20e5722e,6d12931f,aa20772b,36a2f110,ba05121a,1830a26c,440f0582,2fa53e20,f8c226b6,85b437a0,557c8450,774c0c5f) +,S(d7afdbd4,6314d677,727fa11b,211e99a1,d6948be5,b7753ecf,64e9b1d0,ac7a83ce,b5f8b97d,748a4abb,a390ed9d,79cb42ab,ee9b2afa,517288f4,80aba1cd,34e21d04) +,S(2b6b834c,d2b54466,a3d41694,b672ac55,13f19b1a,d518ba99,d622abfd,9b6de265,c1922593,ddc0b3d2,ad1d52dd,3cbeb2e5,74ec849b,892d9df1,87ccec50,7ab9b0d6) +,S(f393cf4a,d7f93fa3,1596ef7e,a9c11c6b,f29c344b,1a3ee5a2,51360c9b,a96ac8ea,4e811ca9,de67ceeb,90e48016,30d06125,fbfa56b3,35e3a7ec,41bedbaa,7a8133e1) +,S(bc3b9250,4e14b254,81305012,a248a94,bbc546bd,52fbc0e,848d75e,9c8f418a,9a52afc6,f8b487c1,7653a911,f11d78c5,8f5eca38,c761a2d9,af0ef2ad,a6a58223) +,S(2f5a0e73,c601fb8a,7321e170,4abdb1f7,e71edc3b,bf6f9cec,1467eef,5f6055c0,d8bb1d0d,142d2cb1,3b9d97f8,f807212,b79a5ecf,13d942eb,c5a3f442,6553e393) +,S(b83e51e3,b4fc69ac,84d099b9,6916c11d,8d8d8d7b,f5cbe15b,fa13ed34,73a54ae8,232e0f88,69c53923,22018084,a7cfc251,daeb7a84,6e1daa78,f3f365c2,ca3dda5b) +,S(10971993,50b07a32,1532b372,2ee89058,e90e9b30,5908cc0e,a249a40b,e7516377,69a0b67c,7bd55a04,9be3a3a5,e308799d,dc735f9,ce56ff67,2c796b6e,5a0a1055) +,S(dbeef75c,c95d5018,f3d71711,a0299dd2,62250814,c1469647,dcb27a23,e85c4477,dd4712cd,16334f8e,2baa6855,6c5237f8,676311e3,1cbf9753,35e76a90,88d66413) +,S(28cb1199,2bdee938,f3405f18,537b410f,2723888,e4ca0d29,d96806d5,6baf114f,e6c63fe,bbe5dd81,e3a9fc1e,83a5ce89,c3e2ef6c,c2a57fea,695ebf4b,e81e5aa3) +,S(f0a2df0b,64efe99e,65f9f176,89cec5d4,caef385a,e4676da3,790ba2e2,4e9f60ba,a7ca7e08,b900f664,14498c12,aaa77da6,6a2520,7bcf1a85,713b6d14,807a4519) +,S(d1bb46eb,38e54396,dc7ad4f,f553106,54fe9b06,9abb1410,799dffe5,482b0f00,14b0619f,e08830b6,d0a5463,96826b6c,d03bbd2c,af930b14,a031f922,eace4214) +,S(27bd2e56,7705126a,5f5adb31,ac1d09a1,85d74002,c548a84,15c0cce0,1f69517a,12203dd7,cba21cd2,9f9ce428,a8e302d7,202690ad,8734c08f,65578192,a5468d32) +,S(6b2cc790,1c72e201,693835ef,ab2b676f,55441add,b8dbc3bd,61d43201,bfd21d9c,59973406,deea0493,daff90d4,b62ff38c,6690bb05,18e75560,1bd5b3a3,4add7456) +,S(75e85c3f,32f953c5,632a8bcc,6bfcd6bd,c63c3b93,e607d267,5df09fb5,94840f27,22f5a376,8714c73d,96b24f89,d426e668,bc59aca0,cbf6dc47,723f01ed,6cb6bb30) +,S(d37a3071,969e152f,41c0eed5,7e28f1e6,f26e1752,68c52683,15e200b9,f86d56a7,53d52726,98751d1d,dc4b1fb,d9621723,70e296c3,b47436d3,abe20356,76ff6181) +,S(715bb304,7f5e6973,36546640,8e183bb2,67693198,94e341d2,37d5101e,a7414976,28440f5f,9e82d5e8,7a5ea672,2e95c3d6,a89dd1fb,3d81aca,db7609ee,a1a968d4) +,S(f72d142a,4f5b1595,a7b4a52c,c6812ec2,8e4924a4,b087c139,68d8adf7,dac642e8,501b644b,b5d8042c,f16ab9bb,bc594c14,585486cc,fbaf47a6,5378f97a,761087dd) +,S(7b3eff8f,b4ca8a3a,5fc3cf02,a469f34,3840846c,d8fbff28,243b962,c1284c26,be2588fd,15396124,a50bc555,5e161022,3bbf636e,fd510522,811902b5,b1748579) +,S(2441a8d7,71fbeca4,cd365225,7974e4cf,ec2a7035,ff2b58a5,9cc5fc37,8a113dd8,54b0776,5316713,4bbf4c6a,7ac23360,fe035d2d,9316cfcc,ae129893,82447981) +,S(c20fca4e,d05467ca,45b916b1,2f4ab386,512d097c,b64b824d,e78e9a2a,14cf7591,8851acc7,96ee4072,ca87e69c,9dad10e7,931fc48b,238cf46d,2b2355a,e4eecc02) +,S(2f6b0e8d,1375263,b74e415a,ad758130,a34c6c6d,690ddd67,63492eb2,49ec8e7,d0c791f8,438cf6a7,976cb614,eb4c9f67,adc72275,f6236868,e384d538,daf25dbe) +,S(bfafc1be,426f056d,6f8b3ed1,1374db21,fc00373c,3dd8c762,c5ab91ea,b2b665dd,9c41f297,a4cf003a,7fa7b071,a106da4,6dc40050,a7592781,68436115,150e2a2e) +,S(cf711dca,90560273,6d776f86,759d74f,895980f7,1728dbe9,1df93076,cdcb4e8c,52a379bf,cf25216f,e3556def,e889b23e,2f91f6a6,47ece012,4e140aa7,734f1cd5) +,S(b9be95de,27aaf198,c3001ec3,63690f2f,f516fd75,1864794e,1c3516c9,abd1384b,68875f97,dca1dc43,3e9b614,488cb8f8,1ff85d58,2ba853cd,27fc1227,1ecb6df9) +,S(f5f257e6,c4d06a8c,53442b37,7519ae03,7bec31b3,c257f331,fff53773,28e9638e,b6a1e60a,48d03213,bb52e3ed,d20d0ca,c80beaec,bb40169e,35ff2165,43e986dd) +,S(fc35c74c,17e38aa1,15803603,30af7d92,a5281b3f,cb6cebf9,df09155e,64099ce7,6e2f984d,41e8693b,5a4c1c58,9814d131,8421fbbb,2ec8e45a,c6239227,db79dae9) +,S(ac135e01,49d97363,7405e5a9,89ffaca6,c1bc18a1,cce34aa7,4732fcce,d54384dd,3e38bacd,ff954a6b,6bccbf63,9f8d709,d8e54553,6113dcb1,c471a162,af021d0) +,S(60a013fb,efb3c5d7,802400c4,e791a019,bfd8f657,cf4c1055,f3674f92,9f5f345d,7e763c48,3582fe3e,2a0a5138,ca320013,9907e364,5468b5b2,711205f3,38a1bd97) +,S(a099a106,5d907a8a,43a3d027,3a789992,ceb3a659,bef93a9,6ed04ea,19a7acb7,137f9ffb,fe3c22ff,c034bb57,9f0c9b46,d5cca4a0,db1407db,6fe3d7b,5bc5a50d) +,S(9ba1da86,d02763d6,187ea5a8,c11c9da4,c6ed7017,49f107b,9ef4fc3,94a792ab,f19e22d8,ab5459c5,5bf87908,90175184,8716aea6,5c2d9650,c472199d,6663f741) +,S(3642701a,d30d1430,17be9363,3f28d90d,b5e02238,9350934f,63a514c9,a677ada0,2fdf9ab9,c76bbbc6,c4e79f48,7a8bbd86,6432fa98,4f52ac22,3440de3b,f1aba970) +,S(29f79c58,100b7098,d8d53012,e2c98719,6188c0d,54712c31,c2ea3a52,d6941a5b,be9edd99,eaa6c1bb,d0ec42f,586a2806,919d5bb7,2d59bf3,7216890b,9a2d524b) +,S(e011aadf,bc35284f,816d5030,c3feb350,4b470d0,ac5ff061,e5ed2c47,89ba1bbc,e0a98f93,4f2b591a,36e6ee97,f293be77,21ec1a9,3f4b2700,55a0c01a,3cc979e3) +,S(a5c8816b,fe5356,37eb54b6,88b9221e,ee06a51,77ffedfb,3ed96ee0,4fd78585,3f86637b,9d63b11a,44897792,9a6aed53,7d65951d,aed6476f,e03be5a8,c9ff8e5f) +,S(3b952c23,7e44350b,95207798,e708fce6,2e6d49e0,912a2509,abef16c9,c7194707,f784972a,419e6c8a,c9d41a38,57912e66,e6ebb631,6bcdeeda,ff49b59d,bdd68936) +,S(8d9796bc,d9d8152f,80c7982a,187a4aaa,b75dd611,99f86632,72502f24,dd57af04,fb9b1479,94a31e3a,28d5695f,2d689951,317c2d2,98e9892c,89c98202,b7e611bd) +,S(96b2bc5b,2f4e6445,1536f689,d7889669,4fde4d7b,6ef67f51,84517704,6a02de40,e0b76d26,f91982db,5039bbf5,afbfce51,914eee9b,352c178b,2145c4a1,88e89cc) +,S(9bf7cf10,9f129500,e2a8f54d,8658ef97,4e3ab2b9,a1d0d036,25bef5d2,432e2d79,2113d7ad,f77e1c68,51b8bb52,188ebd25,dd6373a7,19bcc20b,e32d0f19,496e8582) +,S(10d3ffa7,20375b5e,afc8c741,4d9aa184,c91f853d,61d58dab,11a04f2a,f3d4f1b7,c1f84b2a,8a96f43c,b215c704,6f8c3f02,c98d750a,d416f21b,eeb10ea3,f298c90) +,S(56d64e2b,6adf1cd0,b8d63692,1b68733,e92577e,88430e92,c0e679e8,c3a5eb6,1370e,fbe98423,488427c2,57d5e696,5d4b687e,25e4b824,51b1a6eb,7ef71cc0) +,S(fec4a03e,1c83cf20,75deddbd,20c24792,6dbfa352,a56821c1,420adc34,731528bf,a152b2af,8670e42d,3bba0d98,f387e9bf,1bab788e,fd7bcb2a,a787e6a8,bda95509) +,S(580a7517,7ccea9ac,63ae6d53,e8959e5a,a45d11db,29a87001,779c260f,2fee92eb,793be69c,3263c64c,2048c411,115375a4,86733a90,d8c12a06,862f4f9c,669e8edd) +,S(9286bfe5,78931ceb,cf97431b,1137a19a,991b46d2,d0dec2f5,35edb976,6af636f6,7e774d30,e962db42,83cb38fb,2053b780,c55fce80,51edd5f2,8cb1b5a5,d329f516) +,S(606d55a9,9b702652,5382cf2e,efeec65a,803e3851,da60a041,e5422c1c,28f4890d,dc895a6e,3c2a6411,4a04a1e0,31b8aa17,79ae2c05,1f4eee54,7f8a54c,50eda1a7) +,S(a16a0d40,833ec1cd,dcfef2f1,bcfc8ed8,11387dc6,4e4df6,6e807f69,75f917f5,548d7d37,13de4a53,151f32a8,2880d087,7fd281d9,63011388,ba991bd8,822d6527) +,S(5949d2,be574e23,9a3e3e7a,ae20726c,4a687a15,edf5a62d,9b79d0c3,4f9f0329,71dcb25a,167ab25c,311d9427,5480f78e,f7f4e777,9723e55f,337a9121,445288a9) +,S(841f4e4a,e67b0d8a,49d7b295,e9c1ff4f,cd1e2333,82101536,cef7c4e5,7968b240,1f561c9c,265fa4ed,eb979f4,a8dbe679,ccae8037,8cd73d5c,b56671d0,571f4161) +,S(22623df8,c748e9f2,88bd4b83,142788ff,faf680ae,a2640d,dd43ec1e,46cb7442,380ef00a,dfcdb592,f964381d,ce9f787,17d5827b,494c3aaa,d5e0e013,87a55da3) +,S(816067c3,6438301e,ea07a21e,5a60a17e,670083de,b3b66e2d,c88c6548,f45185e4,5afa391b,9a95e9f1,f04f4e8e,236fc39e,2eeedcec,45026647,f5982aee,4dc4cbf7) +,S(f8f23561,4745c531,24cf6431,ba8e0425,7d28af9c,2c196485,3546924d,ce085c1f,a165067,e3b41b55,a548c1b9,3fe76b5e,bd101275,acd29e75,7f66753,6734750f) +,S(21325584,e25b8a15,c25537d4,c70853b2,28bb933d,de04be23,7753b933,57f2f57a,3708c8f2,78ef57a,f0b00d5,17f21a6f,f627b5f1,59ba1f53,d20b727e,86e873e2) +,S(b2f4f7fc,f48519b2,c00c24b3,7ffac141,88b0baac,bc915c77,845cd6a6,3fdfbda3,b206b59e,6f52ee43,3e7196bb,5f4cf3c7,de49723a,cd5b3e17,1c94e28c,7d57702c) +,S(b4e65d4f,5f420006,b7c70fd3,9564f9c3,fd9c5c53,dacae064,62212fec,ee7f7265,307b6ef2,e3993bf4,f321723e,a83ec2c4,3c75b8d,684665bf,fc073506,816bee0e) +,S(4f104593,9ac609ad,25f3968e,9798ee93,aa58b61d,43f4bab,8f2352b1,de3a7240,ed8d87bb,988395fa,581698f5,c55a5093,ed9d0a89,3f4aa4bd,186221ee,7f8179ae) +,S(9b5e74ff,e515c125,9fa6b215,7ad7529f,90018e85,23888738,d097c016,494672fa,1d55866d,4c4e30f1,7bdbf70c,43fb3b9d,fe2df3d8,41e18bc4,3db3e3af,21fece15) +,S(77bf442,ac8cf718,102968f9,49387b1b,30f8a2c2,c76a2cf5,10d817c8,7e17e302,bb0f9e61,1d90046b,1d3b1103,e6c5beed,e4daaa85,f6eee1f2,6a6e756f,d32af0e9) +,S(27de125c,d49f2049,6b028f34,b16a9988,d2f5d469,30834fe6,4bcd388b,78c266a9,f43d86a8,483dd92a,2ad9dac2,4039ad2b,84cdfc2d,323979b8,1a43de25,5cb54bfe) +,S(21bf7178,ae1d75e1,94bb60ae,4efb5a8d,7f026cf0,7f845b13,2250989c,4be9c116,6caa82ad,bc21df15,d1744466,4d3b58f8,76b52291,5696481d,434f689a,edb8d967) +,S(249254,93ee016a,41b65b7f,8289da2,dc9100f9,9c0b956a,66d245b0,5632e234,bca30194,1cb51f98,1504c536,74f6f904,d883ff46,7aec96f8,2ed5ce31,7c45b3d) +,S(d3f644c1,e3472b4c,d46d89b9,6b937108,ea8e9425,9b9316fe,230e7538,7d867f76,91822603,fd629732,a3b37ed9,d5227942,2e536ae,316bfb7c,597b2da5,d8cade5) +,S(c8461f4b,e2bd14c,a88e52c8,147f656d,915c2675,46252e89,d0337f7a,e325b261,18971181,97ac799b,3f632868,e46efdf5,fa3b615a,a8a92b07,e38ceeda,9cdcae06) +,S(b6e5f590,be20a938,c1b57e1d,a7bd6ca1,a8c2406a,aaa5860d,9133342a,c395a380,a6728cf1,a2f2eade,72d86569,ab5a6305,cf01eec9,6a6de026,ff146d5e,7d8a04b6) +,S(3d25179d,465701d0,56d4eee4,b5efc10d,8b70138d,f14908c0,3124ec95,59eb5d6c,158b7bfe,1d61d7ec,633e88e2,b3c5897a,3b56f7a2,ac557c8c,3e032a71,8b95980f) +,S(abb5d54,c7171499,95893425,bda27f10,d8515511,91af6927,9849a597,46f94d99,4cdfebcc,3be28dea,36d06e89,ff4209c3,7012ff0a,d93d7ad0,4e454422,c4b9a161) +,S(989d0125,115ef9c2,900dedaa,4e31cbb2,f1243386,73ec624c,52352907,b325c73e,d3699ee9,945de070,fc1f28ee,14279750,d553df63,2951c0c8,d1668206,94c6c8df) +,S(c4c5c5d4,29c3d1ff,628bbe4,e0edf70d,5e05db13,c82a2af8,2d0e4232,5e013884,c2c76cb8,825188a9,fc29da67,70ade97f,ccee860d,d13323ee,f2ba7e7,efa97eea) +,S(8662d850,c623b65c,8db5080a,900a0e83,be947111,ec0b9edc,3601549e,26c0807b,a71d677,b0c9e7a4,f66a72b3,2918083b,f91e724c,cf8c9526,b3df0f6e,35da840b) +,S(d26fef49,7e7359bf,33309b38,93abbd44,543b73c7,c0eaa626,cbdedb17,1d083898,ad8bb209,bb090b39,6b501ea9,a5070f90,4d60c20,d48dbad9,56d381f8,79851395) +,S(569be8e8,c584b682,7d742ff8,5b402112,5b4560f5,f173c983,8f8785d2,229d8d0f,b09381a4,a8267d89,a8d5581b,f9a49d52,47c74a33,5e26e354,22edafc7,d41b8559) +,S(de34acc3,4d1787b3,ad967bc6,c5960d8f,842f82bb,c95aa255,d4a9e794,d30116d4,2fddb41c,b2b6b52a,57cf4d3e,883d3f0,e322c40b,ef098505,b6cb3d77,a21816d3) +,S(47430b48,76dbb762,6d549b86,1742a5b3,ece85240,70dd83d5,5446fa99,c5c50b1a,d75592ba,7004573,58c6a0ea,dc8ec772,b53dd048,8ddc73e8,5af357a4,af200df1) +,S(f1d1e0b5,e6687266,ba17339b,76bbab6d,8c2d8ffe,f6c68fd7,9140b097,d6e9fe13,a56226,fc233385,349c5483,72300f97,a1d8494d,a67db50b,b96543d5,bf24e580) +,S(22a6f735,76140de1,3dbd0426,e968c374,6e8132ff,383f44c3,c0515b85,f8cdbcd0,9dcd89a8,90270e3e,358dc004,40f95a43,430a399d,a8405e48,e69e3455,fbd18d1b) +,S(2ddc5328,afd8ca0,9bb0c6ea,57442e8e,fc307b30,c29e2867,b46654e3,e1a9ada2,7b9ab90e,db84e6e8,7bedaa6e,31ff90e1,7d07793,19977393,b5fedb73,c087dee4) +,S(60b6f839,a60af52f,d30a116c,58b9c65,1fb1c854,546dfa9e,1a01e2c1,95da0fca,5e77f69f,28cfb414,be39b07b,2469d8eb,b0e74306,1d856bcd,ca54d83e,20d26c40) +,S(2790c19b,9b9092e5,cb06bfd,f95ea43a,2a1f1f28,57c96776,dcd26a7d,ddee53c,fdeea120,d04e6e78,b2387afa,a0217098,de067759,8b2bc5b6,2715d9f3,714e7f64) +,S(e544320f,9ee8bc8b,127576b1,6fad9782,db181ea3,4514f7c0,d7f9046,f5113803,b93762fb,2397444,e69b63dd,9dce7c32,3c73680a,e26b5e0f,95211972,c0510741) +,S(6c7f25ae,73e428d5,860a236c,bbd4d74f,8e2706cd,69bb4cee,39f9aafa,db55313d,d8cb8fc9,e33f9b18,e9a0864b,e58ea921,8db30c4b,d5dff091,6a44b581,c8dc9121) +,S(289d1b28,ff710d47,4d1e9c3e,f3952f27,dd9eaa3a,30ef499,121ea53b,9b93d4e8,f36af80a,aebc2dd5,84cd5cb7,245b3cba,64285932,77f4be38,cf2b394a,ee880667) +,S(f02ab597,ac01d673,25e87b5d,29130c18,2a6f4a8a,d4ef7476,b9fa1da7,5910b5aa,ae88c02a,ecf28b13,ecec5379,5c34633d,d3262d1f,65ecaaaf,fe89640a,cce38adb) +,S(a9a974f,71a73106,57732c82,d6d4c6d3,c625bb70,4ec29a8d,2d8f8418,dca0413a,c20df433,eed4e96f,d5ef222d,860ed1e5,af1497ac,cfd1dc21,26e8925b,91b372cd) +,S(7866190d,728e0899,19a0e9c7,8634b55e,4cc392fc,1650471f,4a21cf40,8d77496a,1bb5fcd9,540422ef,caa8e265,c35a1e64,29d79de3,a9369b23,3512913,ac0cfd22) +,S(c8a7ffe3,44b3b8eb,f00e4e2d,e63d9540,25cc115a,9110c773,ce0130a4,8333b9ac,ca1ffbe5,10c63b5,674141aa,5ca87246,ca91280f,da97e806,dbb523fe,5ee67b6e) +,S(6cbba6a,13ff6ad3,83419e84,e4da6871,9cc1e633,d4cf3347,2f9966e6,4f9208ba,35122582,c7893f4,4fb92c34,e9c4661b,5859889,62a7d1fc,fac8a2a6,1e214a88) +,S(e07f8dbd,691a8611,eb5450c8,2c765dac,ffe9aa81,49ba3e12,87b23fda,fd9de719,932e35f0,8e39cfee,a2593799,4e118c7f,acb0c6e,5b526bc6,894b061d,8cfbc509) +,S(62136898,32fd9416,cde720d7,1d3f32a5,51f3e238,fccc69b8,4b633a2c,86d2c5d4,137e9a4d,62247e41,8596d968,4f1e5997,791f3b6d,69cbcf68,206f715d,be784e9c) +,S(8b010588,62de6706,253eb2fa,f710c086,40a318ba,1ea684f2,3254e8c0,dba7b9eb,54a48862,dcc56c60,cccbf43c,f7a33f9f,dbd7e419,56f0f48e,2ce2371a,19bfd264) +,S(12d3908c,14af8346,fefbbbac,e56bcbae,3fb2ed87,dc30b1cf,eaf480ea,84f213d8,fc4615fb,df7ddac9,84676421,2c4d99,fef41e5d,98e5824e,cb181a98,9a67fdfc) +,S(219d05e2,a7b8cc69,bbce368f,62922992,b474286e,df7c13dc,8da62d16,1784cd2,8aee4f73,9a7eb1e7,8ee9407d,e8fbabef,dd28203b,2161ede2,761e178,a34c7f5c) +,S(d7d0cd59,9a76fa91,f691fa23,ad265f38,49bbe213,c38cb074,852005b6,d85de204,f349572c,60750dae,5ea1330e,a725a0ef,ed1a2d60,c0b1d9d1,e938f37c,911ce2fa) +,S(a3fa69f5,a04c9023,83d8b73f,6fa8fc3c,9f65cfbb,2c77042d,635f4978,eabb6c6d,c798a1a8,9994aab1,4af95b18,f389a2f1,accb2d7,59997e83,72c1ca5a,c3cbdea2) +,S(902e881a,825c457d,79872e71,e5d94170,8540734c,1d050e92,cc5c8afb,13b2d1cd,ff075132,63bf6fb9,11163ccc,f62f8246,de099f07,ccfc9e33,8ac82c30,bda62d43) +,S(325f19f2,4c470556,d361aa2b,3f1932bb,6923ea5a,4f8cb505,d34cc60e,729c144,f4a9aeb8,29a6a0cd,f981bf35,81bf8f9d,73e3e9c1,61996178,2a57c46b,9e94bec4) +,S(6994f3b4,41a6cf61,53e5f860,2cfffacc,bcdf7245,eb078247,63bb07c6,fa1edd4b,ec2fc5ea,892da897,29c479d8,d3f05152,198ee4ab,87f73bd9,27db1160,1874c174) +,S(21855832,4e8f796f,fd51bd2f,8c1f6a8,5663c5a2,b5bad37f,3b7a1052,6acb2d8c,e77ceecb,33cec03c,99690d44,b0343bf7,3b93c495,5d67ccf4,b15d1d76,a1897c2d) +,S(905daa3b,32f2a801,a567c312,ae4772b5,3196933f,487aa1b0,a5674cef,9d91b099,bf6285c3,6d271719,13691d5d,767fdc17,82b64843,5b2ca1d6,b695871c,41cf369f) +,S(df7cfb84,34b60524,75fe5161,3dbe0e7f,1b685376,315397f5,b39951ee,b65339f1,e725cd7e,1419e455,2d953393,dd9651d5,d1beb3c,6fa44d2e,e580ccf4,df558243) +,S(e7fb9efc,88842b9e,a3c96b82,1ca5eb80,385cb276,28cf64a5,d8c5bcb4,e5732491,c8e676bc,a6f9e037,263b8390,675a97d,1353c128,eddabd5f,5e928153,78f22331) +,S(d1a35f0c,272ba216,1e6ac0a3,fa440f59,bad79715,d71e12fe,cc12369d,cd24285b,1f5b70f7,d475716d,b9ef2f21,bb156400,49dc41a8,96102ec1,806b949a,c99edb3c) +,S(66ba6e8,5f2ddd4c,e17da74a,c7ae629e,4f420429,45932e5,55110da5,2a7b079f,d8ebac5b,eb8eefbc,f4da4f35,79bdffbe,21967904,36487733,263089e7,7a80402) +,S(6f7f69a,26881052,b4bcf806,575f9c50,c36e7c22,1d7b9e48,7b1f39a0,b4f3a749,11d06b84,d89b88b,ab4352b9,cc160e50,bc183558,25bb602b,89235aa6,7331a4c4) +,S(4be16d6f,571d503b,26169906,75c01ac5,41c23afe,84cadb90,eda0c21c,926c0abd,1041ede,ffa1f2dd,61a116c5,dd33fed5,d9ede78f,cb2311bd,9a04fe32,9ec1d06d) +,S(8af88c5d,b55eacb6,760abbc7,91698c57,19160335,ee569bd,33bf050b,1e75886,4f5b850b,6bf2b4b1,29aeeb24,596007c0,ca203eb5,d2be6bea,f61bf596,bdd9a12c) +,S(ad33e77a,3316eb8e,1a764849,13915e81,edb69518,8ce7507,2e2b6088,4cef8672,fa0161e2,4bc88bd9,da5157fd,7950c75d,6c4f4f89,f158f13f,16ca5e4c,af7ccd90) +,S(444a81ef,7620afe7,91d96ee9,b1c34485,cdb51e1f,6022dba0,69ededb3,d9d8c2bc,a88017d9,a49d4ebc,2ee811c7,fbfcd9ba,bd268329,1efafb52,e641af11,9751e2fd) +,S(5b1e07a3,9530f8b6,60787e9d,26503a2f,77af3ad1,24ac8aa5,1d10b663,ceaa5c51,fcf4c0d3,8d55e0fb,2731e59b,686a806a,e87cb510,7bbd013b,4500c470,f77810b9) +,S(3e8c17,e986a86,57b4c403,3317e67d,b1d42e7d,9625e844,ba533ed1,73174309,deebe49f,f4f5a35d,ea0f396,63c7ee0e,9db702f5,c296fe5e,696f5521,94cd6caa) +,S(a1096eee,5e9bcb19,9d24c078,b46b528b,6432af90,7f1d57e5,746e83d8,4d112c1,bd13e4fe,699436cd,656b3019,acd54f3c,97eace0f,e1e92fc1,3f08a76d,d2e7c6d4) +,S(54758579,4218de75,db40a6d8,99a75ee8,36861093,4ded0938,98c071b7,6665f2f0,3bd55642,13e1c689,70cdcd94,fddaa656,6bcde4bf,29c761b5,c6f613f6,19174548) +,S(4eb32992,fcb4bc44,bff56604,f4f74586,a4400290,5ee9adf7,f7bbc694,45456057,8576a8f2,68349f01,ada77c67,30510030,e0cc9d8c,66db7609,21661540,ba45c3d4) +,S(350b2def,d2eb4933,c6d428e7,5990a7c0,f291723,f92e5ea2,69c5b4c8,b661a22d,61acdb00,add9f34b,f36126d8,380ef5bf,70f807a8,1320d59e,3540fb25,90de6943) +,S(914ea8be,ce7f8819,5337341e,eaca129b,2da3c96b,306b017,d7afd984,75f360ff,f2aa5548,ebef80a7,483b563c,e8b242db,582948a4,9fc89213,d3272a63,85ac7b53) +,S(f656e17,38da25f4,baabaa6b,d70e3f5f,ad6d44f9,c53e1a33,4596246b,83dc0fef,40a8b4fc,34639287,a65f53c,f373c2c4,c742ce4b,2040fef5,4c31dac9,59fd83e5) +,S(a47c8af0,334e3ce5,464310bb,c0daa66e,d77e1023,a2124182,ce3e6771,933b618f,6dbccb54,56d7c75,5e71694f,fa1713eb,41ec5b2e,8715ec4b,52295294,c0c7f08) +,S(a8a2862,5e1d04a6,5e87947f,82178d5c,3b5fde31,1eace688,6766ee9d,30753ca9,f82ad6f7,483aa04d,a79e672,efa109be,1feb3f75,d73923ba,94df2f1e,2efc2169) +,S(a01ff54e,5f8eb855,e26903cb,3fd407ef,4fe06208,a0f44837,1e83f7a2,31c017d0,2247c6f9,c52f1a68,84c18ec0,77d92514,9fd59b00,27663ee2,f0e5b2ee,f2edf92) +,S(e7b09c3a,626ac89b,6caadbd6,99a320c,61ef4fe3,11f6072f,f2d7c875,8d42aed1,8ed996d3,6a7b50e9,8e5c64ac,e03cf5a3,37c3c5a1,6d99c5e2,c0faefcd,38609fdc) +,S(3bba9e53,336c2a06,ca89fb52,26059041,96307c97,373cb717,3f35a45b,863ff2fd,f2fe9109,3faffc16,f58ccaea,e54e9b8c,419d5123,6e8c960d,52ae8341,27d24799) +,S(f61d22b6,dc660174,22122400,432f533,11950c21,b6240acd,af905f9c,2c0630a3,6068f5a,1a662cf3,93eb7e86,5cdfab18,692bb250,15de2796,1421a457,49ffb05) +,S(a5b60855,6b1aa22d,8bc38dcb,8f3e4721,891c45dc,2702d13,7189be70,e8a80962,f77f97d2,d5e305ca,1fe20b99,e6c182ae,3a9b3a69,7cd5e34a,7199032,fcb1ddb6) +,S(70c3aaf9,eb2562,e1cae950,be6f592e,f030301b,28022ac1,be3aa5cc,85e1a259,3f2cb7cd,31a8d319,ce4626f7,213aaef7,3f91f2be,a17addeb,61f217b9,4c587583) +,S(1dd70dd1,a8755e37,28dfcc4d,443e7911,a67952af,c33e7d93,b975d136,8d06e530,4684f974,1f5894ef,47040074,2ecc73dd,240e677d,b6bee0c4,141dfcd,7190d084) +,S(d44d698d,7aacfd4b,d8d6bb7f,2d0dca82,f5d37c3d,46573546,2b180cc6,9083fcf9,a18d803d,4232965,90c67b0f,4c415bd7,c0c94041,3740be47,2819bebf,87dc407) +,S(84589ae1,30455742,85522ae7,4b1bc3bb,62730a10,6154d9f9,4109c010,51b34ca5,b979fa08,f2a9af49,bf6ffce9,b2fa707f,de14fcba,3de3b903,49bf6235,37b3606c) +,S(3dec8fdf,89118996,bed31c1e,fff15c1d,bc84e73f,fd04c6f5,469d2f04,8b724627,b143f3c1,e20b5bcf,dbd1a555,f4ff88df,dacccaa2,bfd4310c,16ae2710,1beb2c8c) +,S(367a5fc5,519dd9c6,39bfcc4,70ce465e,23a8dde,417b412a,2f0f4374,8cac160,5b8fbddf,2c6bfc90,e291b136,a3e0f8f3,faa64820,97a87cc2,dbafac5c,a7f021a6) +,S(8aa5587e,7777606f,60bab6b3,20d88e93,1e7c2efd,487bacce,5fa3256c,9463efc1,ac5ba76c,98bfa132,c16921ec,aad52289,50beeb34,449e0c18,88a39fd1,d3f091dc) +,S(79c00191,ea632ea,30fd9e6,b0d11c6,af3aa441,eca678c6,a5d6bf3c,c84cae0,3f954070,3b19a941,3ac0b659,bf401c2d,ae9c8bdf,2bd19e85,472d2d82,39dfb899) +,S(c84de722,21a59e95,cdd951a0,66483255,e858aaa4,ada498b3,d4ef8bf8,3a04fc8,73cde958,915abf28,df811109,a6220449,548fcd2,f639102,158bc451,4af0a0b9) +,S(8b1b8c50,c6327d61,e063822b,2a3c5935,fedf74dc,fe694c21,8cb6b892,b5a9d6d5,57bd8952,9f156435,f47dfc47,9ff4314c,c036766b,5b6437f8,41f60cb0,fe168b) +,S(b45eec8a,3ae82f98,52117c4a,341f8184,33a31ce7,719d7650,e783fd52,48702c52,cd556b5c,c7086b4f,905d0fc,ea01497b,25411785,9ee9046d,c988c6e1,9e0a1c90) +,S(a2d5dcdd,d93096c7,67b4871e,ac58cbdc,31ab7013,865a9cd2,c5707780,80757319,7aa406b4,b130b23e,9e6b5978,b1e0b537,227ae8a8,af62c1a6,a6d46ae,4b064547) +,S(712036b4,e5d35950,a7564ea8,3509bec1,e97f6e19,121ae5db,3d314c19,b9ed2b2d,25862ff3,8d0560ea,4698d2f,425342c8,e3a32232,f9c9572a,21d7414e,68a93616) +,S(5bcef0f7,d70fc8ba,942e58c8,79afc973,dca402e3,795adf0,7562b552,f0bfb392,7cc85a4c,ecb45bfd,3efb0e12,c3fc56f9,1a444ce4,73256474,54ccb089,baf1919) +,S(dcd5702f,9d508a1b,afab9f0,af5c4e9b,b9e5f214,6767ab0e,b5c1d859,39741ada,f7eaead7,ea13e3a7,dccd40a4,a2526501,3369d558,6f0c735f,9eb2518e,ddcc1fa1) +,S(2cea30d0,4f629d,91c3ade,1b8b7176,363bfcb9,25d3eec9,784b91bb,e6cb3d9f,a83f4bad,a42ab171,cfc9a76a,a3dde34b,c48ba454,b39b36e3,44446096,a1c42624) +,S(858b41f2,767b191e,df15fcb1,3a41934d,150e54d6,e95035d0,9c6dfc7,a1c32f8d,8180b97d,76dc95f5,478a2172,4802a28,b34d6e9d,f65d5358,e5cecf82,9fde8dd7) +,S(d5c98002,bee359b8,6d1234e5,ff418013,3eb9a668,d2f1294,245fd020,c30e81e6,4e522da4,cd2bc795,65a2e4e,e2fc87b7,a0b6bce9,1d90376d,921aeb49,5b3781de) +,S(ebf9162,2f9cea9f,71a4ddf5,82de4b7d,34002d76,da8acfb7,d4d6c3a1,9520c1ed,eee75322,3e1881cd,8e3fb483,318c75aa,7e43d049,8e53536e,cd481d18,5ad4e251) +,S(387f6a9,d08756f3,8c2e111,af74be68,acc4504,f3669ff6,b406f6c8,a61db4d,288595e3,ea1d5f91,26df96d5,dcf5cf4d,ed0bbba2,37224c4a,59db552e,87cdd28a) +,S(897ff1c0,989bd52e,383a2d70,e5920144,9aa409ee,9fef20df,25574956,dfe46714,3cfbc0c9,308c6529,f4e95a00,644a873a,de094069,36dfd6c6,bcaecfa5,9adc9d1) +,S(3255a8a6,8e0484c7,8b6bbf21,e2d8b24f,bbcbb337,6f84feef,b577ef1a,b5a395af,17b3c002,f9204b84,e6d4a426,d2d74893,79ef3fd5,441e881f,d8122599,2912aeb3) +,S(2ca4a952,d4d9e3b0,3943eeaf,b1eda225,1fbca101,e7071b7e,2778ef9f,ddc55599,bfacbacc,df02c18d,f81a2c04,4748c7eb,e98631cb,63ca16a0,9892e163,23891e00) +,S(c3e8289c,478c334a,d47b0410,e1b6ecb6,46ee5f8,8a4dbd73,10505f6a,de4383b2,5792c2ce,b0431238,647f4309,cb4ed220,229bee,2727b91f,de4004b3,d6f0f55f) +,S(2bbdef60,a86292f9,867ad63c,67f98420,86fb968d,db92d9d5,f87bd27c,15119049,f4a4b762,3536c479,29099d19,3d40d4d0,e75dac62,54f03538,80286756,60a85362) +,S(77a6e090,9e7dacb3,18014ef0,714a4be1,fe3ee47b,e22afd2c,e0333cb9,ff2496a9,54f0e475,b1a9906c,d1d915d1,81961492,34461e27,e163c384,3815d188,5251065f) +,S(d195b96b,78719254,deb4f413,979f64d6,ea94d5e4,79350b11,45c39e45,4f40ff0,238a4440,613c846e,9472d4b2,b0a4db88,b8245cad,a5af469b,d997f729,c0e0636c) +,S(c39aa336,208aa65d,75bb0f3c,6084f414,ad024d15,48da5bd8,113a619b,4815b1f,7aab1ed6,32741dd4,14883b22,6e573ee6,455a0053,6d849200,4b233153,1126fe5a) +,S(e80ca23d,baf82db,cbe0bd27,24cbb31,d6661c70,44d8a8d2,2967764c,fde31f30,a153d1d4,73187878,bcbc583a,5eb83e5d,f67e8142,46861713,8c484744,a597451b) +,S(395b7a65,a5a9c23a,f96723a,4d595a7d,f3e8505f,e0aec120,711cb7b0,2e72ef80,59020dd5,47476e39,36ad37b1,9a256543,4f88cf03,2992f4b6,2229a5e7,e49bf990) +,S(67a7e8db,b83e23d2,7b57e2cd,9512a5da,e8b49545,ffef2577,644fda22,910b495c,7c01499d,3791b9f4,100a89f7,8e80b410,41fd34ee,68ce1f15,a8753032,e8965735) +,S(c4038e7d,76d2e7b0,86986395,2cbe2ce3,ecfb4b08,61423892,ac9b3b3f,6dc0c7e7,61bb3b0d,9efc9b7d,326a27ba,bc34fc8c,129460b,6642629e,72f20351,65e92f3b) +,S(7705a5e2,26a4d3ae,ee69c52f,5fc3f575,b9bdda08,70a19a8,8a525698,c3a25e5a,458e096c,f01906f8,c2426973,d1c6a982,44be9e02,22406207,3a883e4b,53c7a1b7) +,S(f3ce8bc3,5958b5e6,16a994fa,1fe0396,68a5f1f1,b464a875,9e4a3475,34ff48d0,9c1a1d97,be3ad78b,67c8c441,f7aec63f,8dc92aa0,87500d6b,222a2473,e10058fe) +,S(905ec120,de69cebd,b9a9d764,e01d6f88,3ebb94ad,af5685d7,6649e7f3,3ca4e0d0,92617e99,3974aa3a,774a75d0,e51c00ca,4d42ce12,d79ab0e9,3a8e449e,9ca9d554) +,S(b1b74a4d,a3578592,8bad0edc,3664c1e1,27d48447,fdb246b4,f15ada68,9b21eeed,c43539c7,b84e9e8f,bcce3823,1322c4d0,c14e4d90,33c4e132,8f5b26ea,9d3206d9) +,S(d440165,c72f2d46,b2fa2a47,1c42dab7,8dcf814d,12d3e025,bd5fb963,3d8bcf1,e0864950,261f5fac,e97ad9cf,fc7683df,fabab749,1d80422a,ca30721f,5343ae3e) +,S(ff524c4f,722c19a7,f7b910be,6d573a57,fe08d5af,6580aed1,de03f939,5053b303,fd4ad4e,5b25aa8b,b3a62dea,b320335b,5b9f1368,62f5f913,98fbaa61,b753bf0d) +,S(effde0bd,331c1083,e4b76a53,c9c1ba43,8fafa2b0,4a3ada44,63a5fef1,8d8d091e,fd7a869c,a75c2105,e6ae5420,86ccef0e,c563bddf,2b41ab9a,ee0e14c3,1d947dde) +,S(a5e746c0,99c8d99e,88b4a304,b583adb2,1115ba64,e409d278,f40c4571,2f3875e8,7f832112,c2af3830,2c4baba4,f073f7fe,84170939,dd9e8c07,fd2c1eef,2e934a68) +,S(6accb9e3,af5ee943,9d8daa72,2fe45513,7df5e274,486fa46a,1ba19093,32ccb431,f0de1658,842d6580,507c26a9,b68f92e5,a3faf108,1fcf5dc4,43ec9dc9,9f28dcae) +,S(c813d055,e40d32bf,6e8e782b,3ef85698,b86f3d31,ed0123ba,1ec8978a,f72b6d4e,baf6d757,2f924631,c4da4191,684c6280,47d37ef4,2bb9528d,28879011,8d068ca6) +,S(8b882012,2cadfc62,44fbaeb5,8be4d587,599dfae6,2fdf1177,38a844d,97c7aaf9,f5e6c293,ac900da4,f716e131,fa5a42a8,7867c8a4,76808b2d,4093c782,6f7a6a3) +,S(8f075050,f49ee582,b54a5acc,4ffaa443,bc02ff34,4d0717ee,9619d83,2d7310ce,13b901bb,7ab3d325,7cc555c4,a4799a3d,f1f9f9a8,6b7021c9,b99a1315,b5f7fd3f) +,S(bbc852f8,cf643e54,3c65fb1d,f2f72c97,89a456db,21c81f66,921d411f,b0374c58,ad08eabb,e7971e24,b2bf604c,fb248b3f,a27e1c2f,84f032cd,4c14bba2,b30a590b) +,S(2d77dc4b,662b55bb,878edddc,9dcc2dcb,9278cc,b1e549fc,ffa02503,1a80bc1f,288bb0d8,4fa7f2a1,ad1a62f4,e76ef6ee,c07707a0,89caffff,a8e60870,ecd058eb) +,S(bc83f71f,f3b4a86e,b9bdf7d9,94bba6c8,c90945ab,9c858b4f,fea7914a,d7ccbdf9,18e91fa3,e5067d23,809ed455,3d119541,fa3c2563,a3a88b30,20d3e49,4b232292) +,S(7d5b9657,8d9b88e6,b0e11b7e,847d0524,45f83dc0,e2bbb7b3,205db435,2c200919,1e689f7b,8830c10d,c28e7230,c569af4c,b1c9e672,aafd4d35,2faf5570,90515ba9) +,S(85e9f364,8d758688,31dbab4c,7460c4a1,51614887,cacc3cdc,1baf928a,200cdc80,7f4ce518,d5045c0,5865224c,c07f7fc3,5ba5e4f4,c03ffa9d,d5f8e5a1,31369e27) +,S(425390df,c1c094bd,34855816,dbbdb71a,9c1d1077,45b1c65d,a101d974,4acd0810,81f14cdd,20ef4d70,69026d94,f09de176,707547c3,8c0f27d,1e277d4f,1f49bc00) +,S(3cc7cf0b,66dc93ac,250d00bb,60a0f75a,dbb59409,bf8e4419,4cedaff6,413549d7,331a632f,c3dbd899,f688bcf9,9df70e66,ae17b719,a2beb23a,664c1d6,1c204a58) +,S(fd72088d,b8a1bce8,8fc923b,cff789d8,9d6a0b2a,2be3b5f0,17e2b3eb,ce70853d,d3912b23,a38819ba,eb329638,3a0bedd9,93db4aa8,df647494,b710dec9,9eaf599) +,S(3e558f8d,5e0096da,4d8940af,9a0baa25,c7318541,c7dd37a6,b72b35ac,efe65060,2a403d7f,69d805c,3ebbdb3e,fba95b87,f78f6431,7fec0dde,7695053d,e660e90a) +,S(70708223,f525f222,16c00149,fe1b745f,721ec32a,90fd0b01,e82f2049,d8694a7e,ef4ad787,f0b4d53f,19d86ac9,7d943b5,fce17d88,6366354b,410dabeb,efd12569) +,S(eca29625,eabffaf6,4b9eddb7,11e4cf76,440b4a94,def9d2fc,6e5e9114,aa414f72,f1f83875,75afa3fc,b23bb227,4c5890cf,f798b628,283989dc,d3538b8e,237618cf) +,S(5b7c4750,542df0c4,c4606db4,6861925a,da7423a5,66d6c108,77838f65,5f297695,42811366,1ae1889f,d6ccb1a2,4cd9a6a5,de04db2d,5e805079,5f922b9e,791f784d) +,S(af1a5fe6,40cb2ee,d36982dd,1e7882fe,bd5da168,8005838c,b379b986,bc012042,71570549,305fa634,a63d12ed,1fcfc8a2,5768778e,863d4778,b7e41215,ad3e6224) +,S(727443a2,46787e70,f83cd1be,1189c4cb,e1e8b18b,1d57746a,9a319676,9366b1d2,86357826,9bba3e89,a12565cc,1770e5b4,4df56ac0,d202fdea,d68928d0,6d16d540) +,S(967979,db34e62a,149e6835,213991f7,ae2c9a2b,abf3d255,6c5e198a,12640b8d,99dc1d4f,52445651,3cee44ac,3bf3d1ed,8547b827,251b3b01,4d4c07c1,fd9edcc4) +,S(794009db,8f3491e,27ef9d73,905b06b2,be56937d,be57b8b1,59dfb883,af203601,5a6ad64d,7ee5e1cd,15a6a087,289d86b4,5db60d24,243917e7,50eca61e,2ada40b6) +,S(87cbda39,6f90b090,dba9470b,1fb0dd1f,d2f4212f,50791511,cf6297e,a9851d10,9ca095f3,a2a4f997,501fb4c9,c5585dc7,fb8f7952,e11cd3e7,73a92005,a41b63f0) +,S(9ed57b03,475d1752,58c20772,1be7e12,cd1f3ada,5b95fa8,4b245b01,b910acb0,d58c9054,43bd81af,85c58a7b,d0cb3e8b,25310644,8e1ef50d,324eea36,a3bbd5eb) +,S(ef8d0a22,92ca4498,2dac77e3,86113363,35fd9521,fd266be7,d5d354e6,28b0402,e55c4b4a,556881da,a4a23e5b,677c1a9a,f2f547d2,e2b4037f,f9758a74,3cb58b6b) +,S(6dba21f5,a274f29d,502d369f,4fa4ed08,2299f6e1,b12f5c75,abd01597,d127e3d9,7685f72f,a0ce41e,8c4302de,3e6e9c81,c951f69d,d2df0f3c,930da77a,51a39d94) +,S(8cdc3ebf,857ac235,7dc1b0f4,26ed2a52,7841969b,7fb332aa,6ba44693,c8399a3d,234dd3c1,915ac714,6cd8724,9b68a19b,df250dab,2748abec,a5876fa4,b7f910ca) +,S(a00829c8,caa665b4,7e3391b6,f4706898,a9c790fa,29344de1,e88fd882,b5eca2da,b7c8fcc7,1e834cce,5884b129,98318254,23486297,6068eaa8,184b5316,fd424110) +,S(5872f762,446cbeb1,4a12e54a,1d4f22d6,a4ed5b2e,eff7c5f5,640801dd,2149ce6a,8aacd3e8,862757fa,1a70c29e,24364ef4,4c37d5d8,bbacde2a,c75a0c1c,28ee198f) +,S(b70ed5c0,5dba7150,f22677d2,e5b532cf,53e7ceac,919d3b2f,ec4cc6b9,2fd34f2,7e43f528,cc74171c,a2fcee0b,18d8fcb,76c3d69b,8560831,26bd4482,a4d28cf8) +,S(4beaa213,3c6161d0,c7da6d7c,8eb09cba,2e8ca505,a790a7d3,9d0c16b,8053bb52,da4c083c,9cbdda2e,c6626d8f,e4b2ee13,703496c,c298bffa,db1acdca,99d4f6d5) +,S(a8d31ec6,53ce1834,a78de363,c5f9abd1,8594b34,4d34f5fc,8a10e81f,5804831,b4d6a9cd,7cbbe370,9edb5601,cb7c8ba4,8c462c18,594a4bce,64f4c286,dac5cff9) +#endif +#if WINDOW_G > 14 +,S(a8f11586,f3df4945,a753c485,ee0fd4d,e410771d,bddc1b26,c9ff10e0,77b915b7,a4ad6f16,dbd741bc,71b2dfd2,dd2d340,3816bf73,4e73cc10,abcfa6bb,d0f161a4) +,S(13e697c3,812d4772,254082da,372892d4,e1a66e1a,eb16bbd4,f7a0d531,c979cb2,87fa7baa,f6def12d,31e42c14,f672c0d6,a9d0e2e1,ceee2546,d65bd01,c18df57f) +,S(3cae4590,821a9697,a5963269,b44f0222,98f60021,b6048b3f,49c6fd4d,8650c7a,e03f8745,60418449,ad97f28,41664745,349329d,268c1c43,86e25147,6e44b234) +,S(6f42f6b1,cb6bbe13,cf081480,766cbb36,9d2e63e,e691722e,ac81621c,66e0fccd,e5d8f8eb,67702ed9,e33c7b71,e3cd7b25,cc9cd315,314815b0,b67e8622,fd35f022) +,S(43d24bc8,469cfdb7,51ca7d82,98727059,6fac14e2,11d37041,370f3bec,90e411eb,2129d618,fb1f7030,9715b2ee,d8e70aad,8b172b74,425cd747,3a3fe40,d7c50dea) +,S(259170e1,d48b6f36,6a281592,3474f0de,434f4ccc,e45126e4,a15c503e,c1f8b97c,9ec06188,dc194bc2,be131217,bb862943,aa9cf36b,7703c45f,b1ffd282,c3c12549) +,S(18d547f0,426139b8,7837d1c1,ca0f5f06,883581b2,c001e8e7,2565c9fa,80fa2719,26d9dc3,e1145ccd,23ae36e0,5c133f6a,5ebaa9fd,bb954792,3e762a8a,9fb60260) +,S(23b5936b,f2dc961f,2fc93814,4c96232d,dceb477a,753253cf,1bf05713,1b9e58be,9b678070,1bd976e8,8d66e740,2c00bec8,e4fbdaf4,976289a4,4391b28d,45519b07) +,S(46de46fc,209df62,6185387,938724fb,702c3239,fcf29113,2807894d,148774f6,cdc2ab0b,5d37a348,1b44de55,d9c50ce5,4322f8be,6f284ed1,ee49f04b,65f4f2d8) +,S(d3b78208,f5876faa,c4d3c970,3a87a586,e897c11f,dca9a7cb,6573c814,9b5d5da9,112ecaec,9f4451ae,23485579,9cfee804,ee053df7,8e65713a,cd43d953,471863e1) +,S(52d51f46,ac86afc,391e6a1d,8ab1d862,2492ae18,b3e86f13,5e42fed9,4cf3735a,cea47627,e7eefa90,7b8bfd12,f3212ea0,bfaf9e00,f407bb4,8a86039,d4815215) +,S(46451d04,48f3f959,e133723e,b9138a73,b3877adf,f294ff15,6303a845,65a4c39a,fd1c4a00,bbd9aa06,afb14790,5c0530e1,d6c3b5da,c9001b9f,8ec76df3,c7bf3c76) +,S(a12f5e51,16c58aae,90d8532a,9182d54,f539014e,e2d8357e,5ac7854,26fc78db,3db94e9b,f37ced91,7c9f466d,2a6db2bb,21725e9f,f4dc1482,3e6e384a,265e0cad) +,S(98999fcc,38fe6b71,97ebfd61,fe8942dd,be944f98,6f139c4b,7bd6bd3,28ac250c,48adfd3e,c348281d,c23335bb,8702cf8,acbdfc84,9ee34a3a,14bf36ca,b7dd0ae8) +,S(8ac22420,f9b9aa05,705b31f,bdb57d05,218ad72,ce09b489,4d0b515b,4e5940a8,37a7e2ca,442b2446,686f91da,db6975b,6f63454,e3a96df3,de8c62a4,364d30b6) +,S(cd0804d7,b1aea00c,13a94a33,f75f3736,f5759080,96a2d418,c5b54c72,de31d619,c7f68576,10df1c38,6e677bb,dd6dc121,b3d9e9ca,e54d22bc,a5ce1184,a3dc755e) +,S(b6c4bde6,2f8fe3d7,3c32e641,573098c1,c559e847,e40f60c3,49d40050,49b0411e,62c691f1,68510458,3e5191b3,758d7e3a,b2cfa31e,7a00d7b,97e39f88,786f9a9) +,S(c20990dc,70134917,42cec766,8b725c26,d918cb46,6cbf7ebe,7b1f37bd,63d5df0a,6f1e71ce,e28b22e1,9c6f180a,4d7a48be,9fc696f4,cfd418d,134c1196,6e285d1f) +,S(ebf16672,45ac7f7,385b5356,dbf977a9,3602a11,40ada114,7face805,dd93f9fb,24b15faf,3b3ac9f9,833882db,b81a976,9a37e6ad,1a4228c9,a4f7d0aa,fcca3400) +,S(1c620657,63199ded,804deefc,c33822f9,cf3d4c1d,72f2022f,37714eb3,83367621,600221e1,48f74ad4,dae118d1,b6dca782,e00117d4,92881c23,b5a7cb79,86cad767) +,S(40b300d0,7520348,38bec63e,f155b527,286db841,754702d1,d512c183,345d492d,29305ffb,31b1feba,abe027b5,f432679c,e265a57a,3960b8b5,a66e6de9,5a10fee6) +,S(5768ffa5,c8b56772,1e10c2ea,421afef5,dfd84120,a8d40e13,751a895a,fccc6c2d,52ee5ce2,e60ec485,7ee62e81,fb2eb118,4d2a6ecf,d8ab7e09,dd728d16,5e508d30) +,S(d906e71a,b6bc6697,858c66d6,82aa85af,e8d80ced,5f470ac0,8158b5aa,4cabf2b7,c75ace8c,74552756,8cad89e2,201dd954,bc6f4ae6,3a671d2c,9bed78f6,40b7e70c) +,S(f71937e,f4553a81,a3f155d2,2f81a5b6,e080c0ac,bd8f5c5a,fd437960,6a63460d,d6e4e57d,422df901,e6292a28,c83c3bf4,136b5700,f6da0351,33099feb,f1228b19) +,S(3c498699,da858f1b,364c464f,b4317b76,5d085393,1c187888,83072cfb,39ee337c,cf3033a8,9749a0f4,fd4ad867,67b919a1,c946dc67,cc46524b,af5c0015,833daa0c) +,S(c416570e,cbc5576f,97090660,438dc3ec,be269c42,6e36fc44,45d53b0e,b2c5d54d,cf6380d8,76e20ce9,5cc181ac,4fdd42c4,b91d7132,d1b1c19c,4cc0db01,4ed782e) +,S(8f97b2bd,344fcf8,62846992,e826f5b6,7fe7d103,28f34231,8a9f7de9,20d71110,e296632,fe41f8cd,b3da3ca,e1c356a4,2424c649,11dcdd58,c21aa1f8,cee07ec0) +,S(6283f371,8e67e917,5b8c1bf,28e66bd8,470dc4a0,1a720a8f,cf1df325,fcb0e10f,5e80225c,87c0d6fe,3432db79,e8cf4365,8640ad4f,21efd2a4,333eb6ae,a46bd342) +,S(9e29c616,ba66db06,bb71e56a,2a049727,e07b83ff,cdbff6ab,898a73a2,f2f9d58d,b5997133,bdfa29e4,431494f2,8444f186,7c4fcc1a,516ca195,c8eb6fe3,f5693dfe) +,S(7a0db51d,56a2b33b,8ab33329,a1d41454,a30e34fc,3db33a31,45c1cb07,923ca061,334164ab,64cdd8fe,9a106f2d,156a16c1,5da4f07,fbd7f1ad,e9d1fd7a,c8630fc9) +,S(5994233c,13ba74ea,19c1c65a,c3653a6c,cef79c4b,bbb827af,e736545e,33ac05bc,2175bea7,3090437c,9af6f994,b33023b0,5fdb278d,c0c59063,eb3b805e,a6b6bf6e) +,S(11e13de,2371f715,4d15373d,d52c504e,50811146,10ebeaa3,f47a3335,ee4e17de,fa961827,b81e71fd,60696d97,17820d67,ec86e8b8,74d3d4bb,f50df644,61f738b2) +,S(787099cb,c8896328,dad08bca,4e682e4,90461574,29aaf740,23795a1a,47f25ccc,1ea5755,bd653ed,ee7ee8d0,b3e6214,df2e31d7,731a1c9e,47a95f46,8c3c8ac5) +,S(c644c88a,fa42bae1,27bec7d9,528cd695,7bf7d906,942baa4e,14ac960f,46469cd,9d7b39cd,22007b04,f61c5905,3a3b7614,81dd45a1,532d395c,baf8ffff,377f2644) +,S(79d1abe5,cdc8b0c4,9508a87d,46163d1f,ada43640,ca1a5e89,d2f1c07,8c58820a,465cdc,983f1be8,948b850e,a9a4d9bd,f3898781,65d5a186,1c94d3d0,3fb9289) +,S(e8d4fa8e,db2a1612,d7b6fd8c,d6aa2f1e,a8f1ba1,32572d6e,dd0826ce,bede0e55,92aa3d43,e944d032,afa03d23,dcdb4604,56cb0363,1fe2f8fc,404dd9f3,f832f065) +,S(6f99e75e,b09110e7,3d5e304f,bf569037,577554e,51861356,40c2c69f,4a92e88e,63b5bf05,c554aa30,261a09e3,b292b9ce,8f1bcbf0,ad91c35,aed04f31,567757bd) +,S(cb089170,d912f8ae,e59f21f8,8860c0d5,182b5252,3b2cdf35,b9795d1f,20c37815,4e8c5b3b,6d79cab9,9f2570de,8c58c34e,6b6f5e19,b285bc4,6988ac86,20e645b5) +,S(5c248793,51e487d3,af1b6d16,b25367fd,9d2b7185,9923c565,5d7567d8,ffd621f0,5806bb36,ac3cca86,2624401c,27c54e90,c76fc747,7e83e6f6,ac89f22,35c84211) +,S(f1d10f0a,2ce54b90,ee71ccdd,eb6dc4d4,c2cab0c9,5cd35bea,5f20d3b3,51d15896,7bdb4d3e,cc613f8a,8fb84d25,541970e0,6c7385e3,c04022d5,82e2efb8,4998eead) +,S(536a5966,b594b460,bd27b4f3,aea6b555,95940f06,311970a0,36dc0fa2,3a274519,c44b0b54,cdb4eb87,b5d70d9f,bfd4a601,5c34182a,31a882c9,880878,7910b3d4) +,S(7f8165ce,ed27a393,41d542e,ba9ae875,554265f6,1c1c56c6,cc0bbd66,2ff88686,6993724,26c18b1b,dd1207e2,f3bcc0b8,673f481a,e7638d5d,45134835,9b4e39d6) +,S(12214607,c610d012,83b9265b,c59d7458,4e0a79f6,8a4e8535,a72809fe,11a6830a,5d26e498,9e3edaf6,27cc4bb7,105ba8de,3344e506,d9bc9a33,8f1e3219,e473e609) +,S(9ad7f097,1d1c326b,8ac468ef,706f53a1,9e8fff25,8e599f0e,72acaa15,b21d58b7,2e7c0921,2199c7cb,da8d3645,a5bd7831,4234849c,41a238c2,a3dd7fad,fdb8a880) +,S(355f75a4,9df69d8e,4ccad41b,1fb0445f,619ba9aa,34697a5e,24ec8e92,f5b41068,9ca83421,4622ad8a,90a09469,d155bbec,717295ec,c071894c,6c91a7b6,e1345f24) +,S(bcde8058,8336a996,fa6af5bb,5ebf7441,ed3bda73,4802ab5d,5c0eac31,8017c706,49e40844,78cf1912,ddc91907,26411814,a782a3c1,452117c9,993d9e8b,4f247563) +,S(372cbbc3,3417320e,21ac4ba5,f286a9ed,273b6425,e8ed22b,d0c352e4,f0a97270,6e05363c,bbbd6b11,8f2842d1,243dd629,247e90d8,4e7c9d02,c3677f93,d1d01bca) +,S(a1122e8d,82e97ff6,ce9fd024,cd4ff32c,76aa0aac,c1c2849d,887240a7,a368e064,ef97035e,1df99943,1139cb88,e03bdcf0,8b3a5c86,d4b42d13,9b3acb12,e4e7df3d) +,S(e0101d71,1d0c7b60,8093997c,229bbbed,63b05224,53c0079a,518826f9,2bfc9d5c,a0acb9e9,c3d3d384,7f2d38bf,29da71c9,af0b5a41,5bf738fc,be24e367,c8db2fcc) +,S(6df3e575,4b62c92,41f8aba4,12804ddb,74ec864a,12076a34,d9cdf9ce,be336e8b,bc66fbf9,276fc85d,767c2155,ef481295,963cf371,c0ae6b48,50140d1f,c32f80d6) +,S(e5794801,97d3eb68,fd859d0a,a616912f,a2a52a7e,f3881969,626f43ae,4ac20586,3ba69743,176c43f7,7afdabef,a06eabf5,94b9d0ec,1114f352,7cb1b127,697a5275) +,S(7b290c31,de7dcb10,177313f1,9a15d751,c128bb2,16823c5d,69298baf,575657cc,9d3b3707,d8f2b17f,e14b6b1d,80c55d14,b747c3e0,8c79d55a,bb54809f,d96f99e2) +,S(4ce2adcc,18632b7a,560bfc3,beb7138d,8ab210de,6cc7f6cb,47171b45,13e991f7,5aa8204b,689d8f69,a42e08d,5ecd2936,97ba76b2,1bae122c,e9252659,25a874c6) +,S(dd798cc9,97530af7,d7ce96d1,a6a93aba,c29056db,c69eebca,f4649648,4942bcb3,e9ed537e,7b679852,88bab4bf,36ed677c,14db982c,9ce8714,b4f3a32b,56609e49) +,S(356f4414,9da655e7,c9ea5a3a,5c4847a9,f908c608,aa492efb,15e7cb00,8b065b79,2adb4e6a,ce059ee,44ee8962,b4aa8c71,a4d55f31,28f56877,6c70c4c7,19fa6332) +,S(b607a62b,c16ffea9,b30f61d9,c711adb1,d110c6b4,357b1e0b,dc71c8e9,4d06668b,6b933ae0,9ba42b06,96419a90,e6579a33,8f4f3fc7,5134abf,70cef838,efc5270e) +,S(77fec5c9,24fbca2f,f564bdc2,96ebbfdf,d52f0dfb,15011792,57085c0a,8fec498e,6c7729a7,c92a6626,44f33fd3,1ad5142a,b521720,24e31308,dc11f73c,ee29d69) +,S(804dd2d9,8014a95b,7fea2651,195585ac,acab7a46,1f3134ad,f2c4403b,3de98461,66fae33c,458de63b,dd04fce8,1c425938,dc13194c,ac03205a,c278c071,918a4e84) +,S(65866c36,90a6cf74,95ef54a4,f222bf9e,3ea803a8,98eebc19,2214133e,9ad35234,8cecbc5f,4a152864,6350711f,c4df57dd,8a2c44d,4133ec17,c95e00d8,b49c62ee) +,S(eaccdd6f,2a9616ed,d6f8c3dd,bcb04c1d,49f0c4c3,8d8c34fa,d47174f9,2323e15d,15a435eb,e23d23fb,2313b59e,2fe0367b,17b6d9d,e900332a,20430362,19c4591b) +,S(860d382,8ccde42e,b27652d1,c159d9db,57a0b9de,f290b071,e93e36aa,f730c53e,b4688879,ef616c7b,87bd37,641e0381,115dd487,d5fa3e87,99257afd,26906aeb) +,S(fe446d0e,8d174570,49394416,9c332f9c,eb47e70,3f214d83,2777398a,dc8189bd,d4cb299e,760ef11e,a3846dd3,9a49f34,4d5c10bd,ba66dccb,a43e3647,718e598d) +,S(e4e6bb03,528f913b,bc34d910,2a96385e,bd0b8aa9,a07a863f,9d5d41c7,b982a578,46ed6885,471b4806,aca3c265,d0c40535,4bd63da0,af35a89c,e8a86896,63d38690) +,S(12d69042,a5515732,8b729ba4,4fc3bac1,95c419f8,ec71f438,e676d722,4509bc56,80d7fe55,52e88a74,a87d24ea,de1f9e35,de5c45ea,b8a9b48,9e7a0b8,4bcf5d31) +,S(d3ab5380,1f1b1f7,36d94c4f,2af00a81,ee1815ed,83dafdb6,ef5189e4,13a32552,691c4594,657c0809,f7b6335f,fa4fddcf,8af5b729,9262f790,2ef12e34,f546a401) +,S(50bd549c,2da95051,7b4c72c3,67074539,412f3b4c,45ec69cf,aed0fe61,25b50d7b,147f8768,515cb545,2f291d4f,5a627f36,5c88b2b,d19457b0,1fa711fd,9bc71abc) +,S(a36bfbc2,330165f0,728578b9,ec81d14,c0417090,e6ed73ac,e8aff550,2ad9c62a,acb88155,5251f37e,f31aca05,bf33f9af,9ccc57df,58692067,4f11b787,8c7c9337) +,S(2be3d416,ca9627d5,9f259f0,1d52d915,9972f50,c391531d,70a6b79f,735e7413,fc0eaf6c,97d11c6b,cbc2722a,b3e821d5,363b04b8,edf2b700,15ce37f2,f70e51de) +,S(7ca7ca3,393a9884,c94430ad,20731f2b,7629203e,9c892d22,ae6df1a9,fa66aecb,c05c74b4,e2580f6b,9be6efd4,379b7631,26d3e0d6,d4e9925d,1a7f874c,fd620f43) +,S(6805c83e,19aad851,6fd7a4b7,e9bbb82e,28d9d0e7,1f1608f9,dcf37a71,3b434893,2313791b,8fc68b18,444cb309,9cadde98,f4c95ff9,88ddf601,ea3eacb5,bf1c1512) +,S(7540f073,3a358a90,80d2b07b,64e179e6,5de8010,6eed4eec,dc8c879a,87d007ab,998820f3,bac2fc51,48b0823a,d20b5f77,eb7acf5c,fb2e968d,2b98711,a1ef778c) +,S(6eb9b5ac,960407e4,465a0c22,8d98621e,47f169f7,61189aa8,f7de2fe8,79daa64a,6a32f21d,379c38e9,229ed85b,33b4a35b,9999281d,f96ecdc0,18d2482,67d85d54) +,S(e32d3fe0,80d92f46,58331d25,f0f2bbdd,43671885,98cda416,7b4920ff,afd5cae,61a8a2bc,aa82c5d1,1e958a0,fbfae374,138efec6,84ea8e18,af1d13b3,5388d1b4) +,S(22a0ca56,c95c5221,3ca126b,65e5382a,5f14d17c,976da5df,54942495,43415d70,4cfbb8e7,7e3f04c8,69e62ffc,edf23907,e8b17a48,13fc95d3,cf307e2,978e0bc6) +,S(ce3aeaf2,dceb0f4c,50120b90,fc027d7b,b5a4cddd,fc337e05,14298873,e3ff340d,49f2b65c,4a64b462,69e93c2f,ca6cdf14,b0c81982,b095ed3,644805c,cf333021) +,S(75251b27,b360b236,9c0cb297,c5f6ec1b,70f6afbd,5af1acfc,38bd765,9c9dd5bf,cd0559b3,9b18ceef,85c15189,fbd7a93,1ceb3f7f,6cc93d3d,7681f564,17f3f891) +,S(9d69e5b3,32a9ba92,17884f7,e7a1ab91,53a65c43,917c566a,c2969f50,accb9047,9ef330b9,d347c93d,a9441706,a502491d,55d27723,ac1138dc,751308ee,fc6b3a37) +,S(783ccac1,f7d6409f,758849b6,f7c4d5bc,401fe5f5,b08c2f84,1e0e3fc4,fd7a2d47,e5c62a27,65ea5ab8,6aa1aa00,9297d1fe,57248127,a5d3c36b,9665cd93,b6255f83) +,S(dffbb444,8362773f,e45e0272,eeb1a8b8,fefee1b5,59a6bb12,b9613fe,55ef33d4,fd539cf6,aacc930d,2ff104dd,405ae6e,e4a6b3ad,1a9a0038,f4ffb4ba,1a6115c5) +,S(cfc948b3,c14d06fd,c528d299,22305663,d7a427f2,fe08cc2d,942528d,dac8ebe3,81ab799e,5be02ce0,d3aece6f,c6ce84ae,988872fc,ab640d96,c3ea6bbf,3e2e709f) +,S(d9fb08fe,6a7cb6ce,721cca32,74e20732,5933595a,e59aa0d8,811008f5,cee83490,2c7f1287,8b6ad3b5,88cf9483,f08a92ca,ccc18ece,93c68297,4d80ae31,54965938) +,S(4272b558,49ef0ac2,d7830183,bfe3cc45,65ab9d9f,32b9366a,b0477d1,ae93956a,caa07aa5,d117616d,9f442b94,40549bd0,f64e7a2e,3cf96053,bd14faab,196c698b) +,S(69536c0d,6404b026,61598e11,1e6eaa4f,e50a2cfe,879b0f74,4df57727,933367fa,aeed065,2c5325a9,53913334,7e4fc4b0,d2583608,c2086bf6,b2e5ad25,261c6bfd) +,S(199589ed,5c4b6e24,65d58257,2fcfc194,60f7643b,7019c02f,8038d04,70368268,c48567e7,ef7a3507,14d1b479,1a70e7b0,5c1d4351,eabacd9a,1d8d5e80,a5da23c2) +,S(ef3c4f9e,53ed652b,f48feb4d,1c7b711f,5526dfaf,6588007d,1c2ce942,86db1f4,128e416d,570c3eff,d45da6a6,31246fc4,f45b7d2b,8cf85a93,22b5f65b,361fc49) +,S(6bc3a126,11ca8f8,3d0790c9,f5b5b137,4ce51c66,6c553ff6,9d103370,3b3d7cc4,b20988c9,4b882f50,a0ba8194,2f168bc8,f29eb6fc,4c3d0149,571968e4,81b76ab4) +,S(a0100574,5ea131b1,4c180c17,f26bd3c8,1bd48ca6,c2310c4d,34bad277,6bf51aab,177551d,299c9ca9,92de3e9e,f82b115e,6d3cea9c,fb276955,5509795e,4870a546) +,S(15553b6b,defdbee,532599ab,9e0de23,b4954dcc,912f49e8,f084ebb5,af6088e5,8dbaf203,351a1119,16e8aeb2,e5eeaade,b0ee5d9c,2f5020,62b9f5cc,ca40cbf5) +,S(ae84a42d,49dad25f,f0a5320b,d6a33f8d,79c9f2c7,2871f41e,f8b02cc2,c469452a,9b23f07a,234782b1,cfa524b1,ad539ea7,b8fac3cd,e9bfb867,799a2587,95a48ca8) +,S(eecfc7a7,a41f75dd,43e2b85b,5af33ad5,f017e0d4,8f9b9bee,f0cf4499,a3c762d0,65526d26,73b9e5b2,8ec256bc,a524e376,f0e40f62,2db6e492,e1c53820,a3180d11) +,S(35e427f6,d0be7dc1,2d306ecf,be9fd8ff,107a044f,81c38c8c,5b9a181d,f4565440,de09eefd,27a8bb89,d8fc38e8,d32aecb,5c6bdc57,5cdf91ca,2f18e926,dfcca94e) +,S(d27cec9d,11130024,7f71fadd,4ace1139,cbe3168d,174c93d,4c756c42,89cef3da,5747fbf8,c4040895,fded5c00,e1f60403,40e07827,8924ca96,2f83b3f,e50ed701) +,S(df30926a,ca9062bd,7bbef9d9,e312a1cc,49442ebd,9994b93f,dc652e68,25efe5b9,8d1ffe08,de113447,5c3adb77,ee5aa58c,593486f9,c3f27c32,a20df570,6ff5c572) +,S(c2602b7b,56d0eb73,ab494934,5eb77e88,787e54c,79558ea3,dafe3f50,84f96682,45885bf4,65996b12,993efe44,39861b39,84d5fa89,5242ea8,f498695,57391182) +,S(d6d606e6,28c55e08,61a37803,72c17b1,6a2fe04b,e68a0cc7,60fdf7ac,85c8816c,76b299fb,9e716eed,7f09c879,ea8256bc,a953454e,490edabe,c90a2e5,dd277da8) +,S(d1bb671a,10ec5393,9d67f6aa,3b09e1b,57bb6414,e1f707dc,b832ff67,56289c4,47559cac,311f20cd,55760ed6,2e1e87ac,cd8603e4,c1d0cba9,79f6802,6b294a94) +,S(739ab4bd,6b6132f8,2659fcda,5fb46836,7adf2133,37f897fd,70a9552e,786dcb91,b562e798,2bf508ba,1b932deb,3e6b5962,70bba4ca,402db3b4,d806da6e,fbf6670d) +,S(a710cd7b,2aef0005,6d8db954,2abe6cba,2a6c6132,a48a6670,c501fa82,4de21388,4a6037d1,87db5b52,2996bb0e,47fc7509,1ad9eda8,e15faa56,92d56006,e72fb220) +,S(cdbf59c9,b38e40cc,d63b36b8,b6e894a,c6baa22a,93a1382c,9d7fd070,58e69b7a,3e836c78,6954d509,348314df,8ce08f0b,59967aef,4e5f0136,9c6d0f91,2682d77) +,S(1d32e93f,6e5e138e,7d226636,d5e0946b,8117f91b,bd59efb1,560a15bc,657e4dd3,82c43daa,bc1e23d6,3632e167,6bbca422,9797551f,f729cb5d,c8ac1460,46ed20f7) +,S(2730066,cbe72e84,304e3563,a18689a1,c6c8e0b3,e92d8c0,d63a96c7,e3573337,8804d5cc,6aa91f7a,1ae23506,17332f7d,811b8f1f,752fea43,5208b770,94e67c58) +,S(44624ea,a1607e8b,8bcbd736,9c67f86e,6b0f5226,4cc76a3d,b40042cd,bc621cc4,95867a99,d110d9d9,e382d3d8,a5dff78c,d8db012a,972a87ae,ac24bb42,b2747e53) +,S(8ba805d8,864774eb,4494ca5b,f8257bf6,980466f8,30028340,8aafd665,7f1ef49e,1a1164e3,c1b243cc,57507b07,7348495,95cf67c7,b40d4d7f,f10ad096,3f3866b4) +,S(297d0130,25bdd2d3,f4a008d0,d8c51a3c,7b16b605,95958d9e,93e427a8,7ac01267,56361e70,22138026,683b7bf3,1a6bedaa,988d3938,cf8399aa,e0f3175e,61d7e2bc) +,S(d866bb84,23527208,e66f34bc,e26a8ac0,daea9d03,27c9dccd,cdf0e2a,a0588f52,5e16262d,3564736f,30f26e68,1c5ea26a,b6f63429,f0b25dd2,5635a3b3,1fc45584) +,S(ab1e9d8e,b18e016c,d8ec49e4,388636d,5dd2ab55,8b3dbaa2,433a976a,ad8f16a2,a14cbcec,6327451b,15d47fac,d3f3cbbe,fef828c4,6ea07a0a,9c7155cf,2a2f291c) +,S(cf2f035a,ce393efd,3032cb05,4f4e92ae,5311dd04,bf8f3653,5197748d,2094cd30,75b38e90,9f02bb50,7a778243,35a1c97d,75bc653f,c6dcffcf,615bf8c2,ec6b32f5) +,S(a801d552,e63ba120,2c3ff08b,8fb025df,e545ed35,1adf00f,1a08d982,9e8bf9bd,bdb9274d,f5298fab,4cb08f42,252639e4,a24b553e,892cd1ea,93499c47,41c23165) +,S(f7ad7a63,eb2b90aa,3de2a80e,844e5336,287984f0,6075d9f1,26f24108,f4a5869b,d151e7d,9766a81,1db45134,7cc22654,c5ac40f4,a1d82d96,2de02c58,d46cda31) +,S(732541be,26fcc3e8,c824d539,28282611,96717b00,2af01bc8,57f4c0ac,cded0a80,d44adf28,f26370a8,3c9eee24,4e870dab,3fa7a508,bd14a56f,17c9a845,5055f8ca) +,S(892ee5f2,a5266a86,5bf2094,14c31225,b92dffda,7a3ec00c,ee53dc0b,8c3a3367,21bef833,3b983665,7d243b85,c0b4e2cb,6a5b339d,831cfef5,d676c7d8,87a0df00) +,S(1a4597e3,77569fed,70875f9b,f8f5531e,6bd3b363,b96a345f,9275365c,e1e64424,bd142738,fff7b9db,cbff1c,93a968b1,f0ef3e63,8279b745,21aa53a7,6e611f7a) +,S(c7306404,4b4a3a00,6a584193,e915a174,631c8a2e,ac45973,c815a2f4,aee82f35,280adb29,4f9642cc,cbcc4345,28bacdc5,74beb7df,84c06216,68cc2fec,d78731e6) +,S(c06d9d55,264e4ea9,e160fa78,78a7e4fb,1757f6db,fe610966,503ae03,b4723f2e,c0d82600,ae072564,b29373a1,ff1a036a,45f68f22,9d1594ad,1c9584cc,ab732743) +,S(e878c0b2,bf38b500,6c309a51,5121a3c4,5b13440d,74ff6e27,5bfe71d7,51e6926a,4b8be149,11432ff6,c5169731,1c1db30b,c921cc95,b4c98a39,5228c08,b99b6229) +,S(d1f6621f,89ac4421,7ef39a54,a0922c77,dc7addf4,78b15796,2558ae72,9b24f65e,657556fe,e02492c1,97c9c97a,67bf1d6c,eb689415,80a1ce6a,1e4d98d9,64902f3a) +,S(f9199ae3,ae51f441,d88d72d6,9c1ff64a,d5cc4979,b5fd6a23,c514e9b4,66ea0a5a,3c484bb8,8ec31009,eb95b971,69c7ae03,c20dd833,eef53cdf,fdab8cd5,7a82d4eb) +,S(4e059bc7,516d5fe2,510ae63,d3ca9543,f840bff5,710a744d,62de3965,af6a657f,222309ca,242c5858,5a792045,bbfb3e55,b61daa3c,afedd1ee,19a0038b,f929a47) +,S(c1673413,d8b4298f,6ca04741,a3c32d79,83a7d5b3,dd39ada2,53013d3c,9750ac65,333b1660,7b998e8e,fa7e3d32,2d51c052,1de24d3e,9e389f0e,9a00015d,6cc32d54) +,S(c078723d,8abfbc03,63f12766,cf9c6261,9c31173,5f3a9654,b653a8f9,e08f551c,880fa5d0,be09cb8e,4f145749,1c7f54e1,ceef1477,ba7ed718,5f27013e,32deb89) +,S(c77f3351,4b44b047,3835e3ff,abe8d7ac,ae5f912d,aea4e6e0,4ed03feb,78c41ce5,cade531d,26ee668b,5e2fc7ce,421de2f,d9a14dfb,87286c22,c840144e,1290e910) +,S(90e14c45,4ba40739,10f800b2,bcc7b017,ec58660,bdb1e72,16cc6afb,8320277e,61dfb75d,f5f74d47,287f828d,c4ff46d7,fa351a1f,66da99da,40e74758,2c6bd4b2) +,S(34c65f34,23f2474b,7e2f294c,ed4da6b5,c05d19ef,a2b5a792,103b681a,be1a3f68,d0fba1a3,b12d3e4e,f18acdbc,68fc285b,8ef1365,6aa900c8,c86ae191,37f028ee) +,S(fdcc63b0,aa5017ce,215735cc,2802eee6,aa5ef53a,6a258dd7,a128ed7b,9a5df38d,217ea863,5c9b88c7,19c7f9e6,1286844b,d4f4b758,81535954,7b24df0,1fd5bf03) +,S(10624260,58bb9f6,a5e83740,59595ffd,e7013492,4392c753,15853f4d,9c7070f8,84a92509,e809874f,86e60bdf,85c75d7f,2cab596e,185f56d9,262b06e4,e79cf785) +,S(e21f29c6,70cfc7e8,b104921,bb524414,53acdc5f,fd80066,39417966,ca235c9d,41be8bdb,969ddd53,9093dc1,b85dbd4c,a78f7e68,67fa916c,36136d24,b6c4af38) +,S(154a85d8,60b532cc,16bfdcb2,2769fdf3,6dac818e,cd2daeb0,864eee0c,f78a5c5b,15f2dfc9,3e5e08e4,9d340ce4,bb805afd,79f940dc,f97456eb,c73b47ea,5098e429) +,S(ae6f8b12,d52907e8,4ae5063a,114f8e2a,da7be317,454ea505,571f2132,5e4acf42,49376af9,1aea68bd,cdcd637a,909b639c,b7ff8800,ee003fe6,540d6797,f0f296d1) +,S(de42ac26,47fece4e,69341582,7e3c5b7d,a19dbbd6,22da9fa8,58a3be29,8a47bb65,e57cc346,b83b33cb,79a34805,42811d1d,4178805b,a2b88de6,d90b4fe0,ba8e2740) +,S(14c2030a,c65ec50d,2f7c176c,45256587,59cae0d6,55cd57c2,c1ea970a,11d7bd0e,f3d9f0f0,5881801c,ad4df439,3a3bd2df,1a045181,af7ccbe6,7500b02f,61edece) +,S(32fd40e4,7a94e51b,e4c927c1,42b5f470,b2abd2b6,2773934c,a29a5ff3,924b2e8,b3d74958,f17147e5,1da38ded,7cb09c27,96b7e36c,3149d85c,9b7db85,acd89bbd) +,S(fe468154,cef5383,4820fa33,ad85c158,33635cb4,a55870fb,f588fa6,b8d2918c,96650453,25235606,7bcbc214,b0a04021,bc71c9c5,4617b1be,dd34e6cb,dbb42873) +,S(57826e08,8fa3841d,a33ad3da,e5b5a688,1ee770f5,bfe45f5d,507b4801,ccb511d9,6cb01dc4,4da6d83e,77a6e8a6,d374275e,7d8484fb,4863b5f0,34bace60,30e6b3be) +,S(61bf08fb,1d537624,1dca6119,19f0dfcc,7787090a,6747cdff,8757b037,a1ea59ae,6bcc632a,f4f43c6b,547e6ac5,5854268f,1801614e,5bc9d4e6,ea0dcd99,79f7adb1) +,S(f54dac2c,b5521855,9dc274ce,25b77645,6525e91b,c011e657,5ff812e8,d846ea4a,bb9734dd,af492c18,82bd36fb,f365009,bd41b8bf,1378d9c6,a18477b9,ed627f11) +,S(640ebea4,b85cafa0,d5a38ef7,7373bfb1,36b9571e,f9694724,680e92fb,13efe03e,eaf8d756,c70ee813,60dadfd9,702f660b,516fe3a8,4c50043,92bc7c1b,892a43ac) +,S(6c4814d1,e3c6c16c,366c8776,93b6df4e,266b45a4,26fb0d5b,a2d8fef3,a3803975,d9330c82,8fb02d63,33d57990,b6bedffa,ccd143d5,1b5eac78,5c7f058f,af77a37c) +,S(8dca10b5,95293452,7bd2f264,6170b6fd,59d636a1,f2578b6f,cbde0c96,8bb2db30,50da6972,64516689,57d2273e,55dd046f,1c398d1b,9e5e864d,ae6746fb,825ec83b) +,S(1409a8cc,34389c3d,3fd1b482,fefdb25,3ba32070,dc23b7ae,6b8a1ac0,2f00b776,f8f1c0c6,3bef8011,27ffaf53,d5c07c,430566e2,6c4a9591,f92d694f,82d7a9f8) +,S(1c752b3a,2cdb6257,fe8bb102,e7560b8c,6f86e7ca,e809892f,58df3b16,728c0999,9f1341ca,61dd07f8,882596fd,12531577,d09fcb45,5a086b0d,17dd5390,8326c741) +,S(ae8bb1ae,a4341bf4,c18e7d99,7bc3ebb3,d9f13c9f,b01b93f,885cb32f,d0586999,92eaf2f5,c4e231a6,851a2324,a146400e,6b9eaf63,8eca473b,9d4e034a,3d5c8e6b) +,S(e3d2429b,ba45b4c,2d9c9bed,45f384a1,7d74cd28,56c87772,6d9a98fe,67bcef97,729da85f,927c04e2,c1db1458,c133bdef,c454d0d8,88262b1f,418f3420,5df2380c) +,S(858ee23b,e5166c66,b99d48dd,24cb5078,fcad23e4,a8df9f91,53ffcab2,fb8624b,c5088132,3af436fd,c4f63b91,b06061d4,b26fadd4,6bccaf05,bbe1a8b7,338cdbf2) +,S(7c6a82f0,e2d6c5a3,ba37be0e,1bd8d4e1,99f9aa3e,c9c58054,a66099b9,a4938a1e,881916a7,87e1f9eb,b38906c,c101b0e6,5d245fb7,733c0093,9a7f6f0a,9b4d113b) +,S(4c980c4c,144e4060,c00e7476,c782cb83,42ae8b02,6a155904,c29837b9,fc25caf2,fbc05490,a62e49f5,6daac9dd,5c730f90,183ee565,d62ca949,5bc7ce56,61c2e8c1) +,S(c9b66a8f,6c163037,f5bbc7b9,889e9cfc,357ed5f7,a5e5cd17,6a7e4bd9,6da2fa3e,4eb8b3e5,365a6a44,4b277b4e,89b89e04,2b44cbf8,b73f183d,42c9d8b6,547885d3) +,S(f2f8fb3e,2bd3cd82,99e2faaf,bd5809f0,12190658,b1efa389,f58ffe28,c1056fbe,db6c4cac,35f78015,8fed40c4,341a93b5,64af02c0,12d4817c,478726fe,4faf8273) +,S(8556c286,895b9402,283b5d2,f90ec950,f91c4dbd,6c1c8a0d,6b27cb8c,3bc7da8f,f2979daa,81ea79b5,d3ee2718,4aff3805,9bce1bb7,fa36cc53,4d139b83,c176badf) +,S(4d4c2752,5f24a7d3,ec3673b6,a3c8f110,f5bb5c5f,e9675776,3d81620,90579267,43ee95e6,5834075a,83675a88,15babeb9,824d4703,763b5b09,8c3aff78,837b353c) +,S(cc6689e1,d08b243c,a49a7020,b08ce4ad,817193c7,456fbf4a,97fca02d,f1ccf2e6,32f00dd1,1080c238,cc7e8c9c,60a1b7dd,7503417,2ad0464e,1cdb4221,35ea580d) +,S(966525b9,26bb6596,584cbb3,5da78cec,e6709134,700974f2,6cff34a1,23fba535,b5c18347,52894cc5,26336590,d961af5d,2453d9a3,40bd151d,259d8be7,6c5a9e7b) +,S(df7f51dd,b0e6c595,c8f3de9e,eed5da8b,b05e2cf9,a4555001,760888e6,38b11c4d,19759148,2d4f49cc,b69ef50c,a1cbf3a2,49e3958a,aa9af2c7,becbb72b,8d53f8e2) +,S(4ac2754f,93745ef5,61d6d213,7fd2339a,81cfb619,b6699d9d,21897ed,975f6fea,854a0153,17ce38ef,e070da13,c07829f2,1bf50d40,6458109b,5700a3ec,ed6a3c0) +,S(7c062917,2c59c7ef,2d488806,b1131193,b6b6f0e0,221d1ebb,eca1b358,43959694,77f73272,291c1c73,5d79857d,60cc9db6,b6128e86,4613b3dc,92dd970b,d5dba9ff) +,S(c80152f1,e4b7728a,36acf22,158b8215,d8ce0ea7,401274a2,1139dd71,59d81557,dc0c54e9,88d2ee7d,e8a56eab,d358aaaa,4542a45b,a170db58,89d634f1,dae95df5) +,S(9c2352fa,464cd430,31f43578,53feaddf,7d5ee143,c6403ea1,230961b6,377423c6,841277d5,71934bc0,20f71b36,dcba2293,65819fa2,b20c57b3,5774a524,d6fb20d8) +,S(8d7554f5,fcaae19,cbc20c7b,73f22f30,aaeba42c,a22bef77,3d780205,8c8e1d7a,c6bfa4c0,9db5101,d33abd0c,47c4e125,bffab86b,8ed50864,6b6a2b0f,322bf5d2) +,S(1e75acec,28a2ce98,5929479d,e0842826,bd556b56,6961d5db,66c9055f,b1b63635,89cbe475,d2036dbc,9227db67,d62b51e9,ac669a22,76e4e1e9,d0be9b1d,58efca6d) +,S(9fe62187,5e71bed4,7d9f2d79,91e76b41,88b2c8c6,10f20d33,5b38bed2,eee85236,9a3fe290,fbe192f4,8a44177d,4f0d037a,fc6fa8b1,c57032b1,3d5894c8,c426e8cd) +,S(8dc1710f,8869b645,73c361d1,ada3f43f,6f2c175e,b24ec358,6409e978,1687a220,37a4187e,bce1845,9520b60,9317a4b8,f33b371f,1a768ba1,96316f41,373ec87) +,S(71ad2a17,5cf75568,214fbbd1,f177c1a9,9eb75486,c161f1b7,ad47063b,567e8d6a,29d80786,e42d9cd,4c801f96,a7d44eda,5e325acf,3b82e143,2576ff1a,3b334850) +,S(be0eeb11,aaab9c1c,ef7a2004,731e3ca4,435c8a02,548b36b2,db68ea4a,56ad8b7a,cd7ad5,c9b7e867,1a1c7889,95e45cff,bcdfe42a,6222e0c4,1054a0f5,f7bb00f2) +,S(364154a1,3a7d9fa0,b349c6fb,ec36ea43,51cdabfd,8fa79318,88e4ea5d,ee8bf6d4,40b40474,7e8b30a6,a3cdf3ba,f0956fa5,f7f61a2f,89a8cf7e,c2ed7445,504a71ee) +,S(19daf7ab,c9f69a31,8b2cf1c6,69d50532,5bbd7254,c4bc4126,d7b31dcd,e7b558f1,6b740772,606874b6,5b1f819c,c7339ffa,30669d9b,2770506c,e9fb243c,6ee4a925) +,S(5ab3dc00,eb9efd03,ae491844,50fe176b,765c5da7,d95ffe6f,61aa0073,7366c918,2603f1ce,935e3af8,af5e3d1d,d76a3410,200037c,87c17a93,96e11018,260dfa01) +,S(47bc2c3,d7dd83f7,2b2160a2,64640e58,16316d0e,3272abf4,b859dd0f,6c7b27d7,1ceefb1f,23dc61a7,2f663d9c,3cdd6625,441a63f9,f9cae8d5,c4ba88d1,22c0f258) +,S(b220435e,e903f1bf,3b443549,5a01e739,250be2c1,43f08a73,bd7dbfcd,75602ab5,3659abe7,953b817f,fd1c02b6,56da4e54,9df250c0,7c28e9c0,89ba315a,bf25e1dd) +,S(be25604b,d52413fe,2c743031,98aeff78,c27086f8,97fe4b20,be5b6251,52fa2fb4,ebdb5191,aa195ee,a0a39bf7,399bbe03,76073830,fe54cccb,a3194eef,d8d34fe5) +,S(5cedfbfd,633f16f7,8ec8a8f,3dad7cc6,edc1a6bf,3e37cf3,65069731,64aad0e3,70e687a9,d8632ed6,ab188f14,229f2e31,34a3adc8,727c7b0a,d7a16d78,1244c9a9) +,S(8109ca32,a86fa712,690315e2,1bc09265,bb6bae74,588f507,1bedebac,47e54a3a,e7f3342c,6e8a3dd1,f6d02bf7,5610511f,d0cffb40,1c14633d,308c2939,e662cbf2) +,S(54264a0f,dfb894a6,b29b6922,b859d715,77f0359,ef825442,e0d43b79,a0fbfe3e,4884ea59,cb88fb60,4ca4de66,110b7d98,ec529112,d4574cbe,9a2e7a5e,5a4a644d) +,S(f2756955,a6a7db9f,eabaae3c,844cfd37,5d86ad8b,a84dcfba,582bf4a,273f90f3,b24c14a7,d2f60103,a7d3652b,c5e68988,390c0e67,1f0fca22,ed927f6c,96302239) +,S(351da3e,6ab7c98e,840d0dfa,e7231d21,ca21a81b,3638371a,673892d2,e40498b9,e1c5ffeb,43b443ab,d31332bf,aa314fda,5a4c2634,afccfb97,67bceb3f,13ed5f3b) +,S(b5107be0,90f84c7a,e3d89782,c6751b22,f9a7ad2b,3a1810c8,f14bdd8d,52344357,3d5571d2,36a7c9a1,aada0835,9e8ce162,c46a878,5e09265d,bb52b419,144c5b6c) +,S(b6172135,85b2557b,7c253189,9422c428,79825eaf,64d8e8e2,2f7bae12,31c9ceb7,dbfe3609,df9b8e1a,33629219,eb241daf,42d8be2d,b9a20a0d,942f24ce,1a117e3b) +,S(5eeaaf7f,577b445d,13481c9d,a567825c,f0a5a3f2,9ccfbbbf,d8f1f8a5,36e712cf,84a4352d,c40556ff,ab75acd3,b5a63366,39119d8b,f814feb,e55fb3ee,dea5821c) +,S(c1bec874,44b0210a,9a86ca9b,94891d00,e1fee6c7,490aa26a,1af61f97,5dad1c13,14a61af6,d280ea47,433d795,1786351d,f90a9879,7678781a,8d9d8884,e88a06a6) +,S(34787098,f0df9eb5,e4825449,6fd7eb6b,3118dd45,aa0ccea9,f606a794,126b9ad3,85c30ff1,c8dbde4b,88fe9ff8,20c9908a,a35a0c53,c2175524,a5929eea,5566a564) +,S(6599b07b,5d31c19e,ee02e287,ccaa1bce,70890b14,cd739140,6f1d862b,39d794a6,d18396df,27e6fc21,a9c36a1,8d8f73ef,c79e6c56,ecbab9cb,83750220,a816d5ff) +,S(27b50b15,c92c010c,60feb473,5ac6ae0f,9ec0af75,81e41a90,a3a940da,a1a44ee8,31c2c3c8,61e64b7c,df8ca011,bd4002ba,5373ec63,8c3608fd,619bbb92,3552c1b5) +,S(40aed5af,953f3a17,f5d17622,127f16bd,4cc5bbee,33e23443,5f49c4f7,a48e7a32,e47011ee,96c702a1,1eff428d,2940ddab,d17b76cc,6765d5ed,126876ea,2496f6e6) +,S(cb56977d,f69956da,cf40909b,69fe44c1,4a5ad547,a025d684,c9815ff1,b5331ef8,9904de7a,9ce0f656,2f025ffc,79e8b3a4,2e410ad5,8f0caa16,62a13e29,f0b00bd2) +,S(11dc40ff,58c6d2a5,9768a776,41c611a8,2aa651c1,15e9d36c,bd3ebd18,ac7a0df9,4717af95,274f820,28592bc7,4a84e467,9d168dfd,826a292,5789edf4,a06e0e50) +,S(3baee366,3d2033a7,f3170bd0,723eded9,2fad6501,702fa530,b26d0623,8d6992d0,47497025,41900ac2,fb236a26,81ca09db,c34fc84e,98ca289b,63e07d17,2e4671f4) +,S(6b19e8e6,b2010e2b,d461d716,66f691ea,88417df6,773208f5,18ee5fb1,e0fb580,a7c74380,9f744b05,99b6c4e4,709a8b86,241ef931,effd9323,9a6d15f7,22916578) +,S(412a2c52,b31158ab,9d3bf4ac,d4355eab,b7e94e98,6a839681,94a7322c,72bff9f8,c649c493,b19b713c,8b3c4e30,567547ab,472a599e,cc95ea73,d36d373d,c92f7dc1) +,S(b5e0c57b,f79e7364,9826a818,bed1d521,3cb88645,5dc91a4,eb157a8a,dd869c24,752feebe,9614e233,3b876297,175078fe,f9adc63a,4639242,6c88ca94,aa06163c) +,S(f656ab08,9e75bac0,6eaaf373,1cc71347,6a636347,35ab57c0,222589b1,e914160f,d05493f3,52b660e0,4cb00b45,5cede6b1,74337488,dd1650c8,4ddeaeee,b73a6ae9) +,S(bd2465b7,b5af290d,2d1157fa,76e4550c,ddb23922,7d30c255,8f0cc3ad,ae5fa32a,63a28c79,cbb50c21,bcfc85ab,269090ea,d56f6396,1fcc6dfe,b3a000e7,3284880a) +,S(d53c8d24,3dc3600d,fa32dd69,11a87ab,722108e4,192d7cec,4d84eefb,9b32a351,af681092,6afda47a,ef5bc91d,97f273ab,78a21c01,64f59c0c,ed66f7a8,5b67ca24) +,S(4a83ae18,ab379d5e,f6cd2be,77f64abf,8fc23b52,edc88ea4,9efc1bfe,402432c9,e9eef9c2,f595ad74,abf10b85,6ea24ac5,603d46de,c320f9d9,a1453be3,92eef8a6) +,S(d1df4416,dc7cce22,832f9ab0,b74d927b,f9b3b7a5,c52acee5,3d95812f,f26caa82,9e5242f1,163a863a,a5702af3,cdc21f7d,b0143f76,ddba2a2d,d592676c,57c310f) +,S(e6add3b2,583addd7,8299fd2b,14b87155,93dcb13a,3231458d,ab6864ce,87b9c890,384bb2a0,930ecde4,1e9945f6,247a6530,58e26bf6,88226c8f,ee9a1ec7,a1f07740) +,S(7d3f5447,295479f2,96c11443,eb9b1fbd,fe81a258,78ce4195,d2f01290,7e01fe4,d12e4fd7,9f66a45b,2596b066,651d8686,83e62627,95f681a4,12022046,86304ac5) +,S(60636a7,45442fa9,57f21668,7fbdde4e,a6f7f80a,ea2832e,ca3ea43b,d1f12709,395ef963,bec2979c,fb211f8b,da3f4b4f,2c129ce8,26a35367,d955243d,3b40022b) +,S(6bce0837,39c04ba6,b9dbba8c,f668bea9,4d8a8b38,2ac17b87,765bdd52,86e47963,a2d85e2a,7ff5c433,c2c77363,666ddd3f,3fb28f8b,2b7aea1,16a8f42e,384a0a37) +,S(59e4e7e4,438ceba0,cad5f146,bc127c1c,df7f4735,f581a0b2,6a53a366,947d4581,32207f5,95fa4cee,3c8ea269,9578b28b,6290a3c,33eb0ba7,e3bf7267,b2bfa445) +,S(ae2cb18,d4f053ea,3a497162,8c822f6d,72806f79,bd39dcc8,364ea256,f30af032,583d363,b9ac6fd2,709985c5,79603d9,4902da04,1563879d,245433d0,abc54a1) +,S(bc4eb578,630ebde0,f58fb192,377de006,c630e96a,51219379,43e9a240,c30951f7,5224d403,4635eea8,e63b9e87,2875f27a,129de5ff,fae3d516,5f6d0cfb,5bb7e584) +,S(848ca19,6fc34e2c,e484f74d,79565ba6,9685128e,7b2d21fe,8cf0e4b1,edbd3b13,2a41e449,532de110,e0d3fbb2,bb794fb9,32d0d4a6,33aa65f8,ec40e5bd,5c4314ef) +,S(b90e1d09,4257ae30,bd12debe,648d4cea,3f063740,e3f9d3fb,c67eaa4b,8ca0bcfd,5604b475,31b28a6a,e0a13a47,31f6cc70,de91d429,d2204d45,e1423652,5a5a4b43) +,S(430752bc,3e1191f3,6e084f2,5f3e30b,da104f40,1de93267,62dde1cc,39d94c71,e707daaf,841f62f5,6d9c45fd,3304e616,30b6af9c,d6871476,6132e127,40f5ebe0) +,S(ab919243,c8bd75b9,d67f331a,b3b487e0,ee9c97ff,c35de7b7,e29827c1,ab643be2,d4d853b0,3971f1fc,ce365db9,cf4a54f1,5e6059c3,c5138b2e,dd765c51,7bdc0c7d) +,S(adce8be3,2277c2cf,6ee46781,f1da8f30,1cafea51,18322adf,8d6b8a29,fc3f7551,e819a7fe,73311592,bcfda0a7,3609748e,6c039066,feba9e5d,41e125e8,4dd67fb4) +,S(d6ed759d,15f5d47a,146e7fac,423b952a,5eabc238,a0382762,c2a226ed,8c24b601,456caee9,1c212f19,acdc2d82,9eb90cb0,2ac35a72,8d280042,658af65a,b6840e9e) +,S(3583bc8f,c3ca09c6,6f2f4fe2,83da5c47,f0ebcccb,fe8e94ab,fd315173,fd3fb2be,ed60aa95,504e9b31,81225d0c,a4f513c6,6da2a2ce,9fa9afaa,c12e04c4,f1d50b59) +,S(223879b3,b9e50b46,7bceda29,905d09be,1acb206,5b23a61c,686d6f39,bf0744d4,a8326789,ddb38afb,e993ae33,6cb1c3f7,92addfad,e47663fa,52a296a3,b6f2873f) +,S(29329ca6,c6ce20b5,81a90873,7206d44a,95ff382f,1ca0cafb,755a3450,83883d69,5d2aa87f,59e13a0a,8ca4460a,4db7f496,1c118ab9,b4f783e1,305bf43,ed44fe71) +,S(e1c6a08c,593454a4,aa116edf,13345cdf,6dc00b1d,b63b7a64,fdea70bf,e4301ea,e0f9f3ff,5e7e395c,42b3e093,87b8363d,37092335,31c317d8,1b61f20d,f06fa662) +,S(da098bf4,133f656d,f2a09d95,c437eaed,fadb4c9,8102d164,b355c7c9,38e65216,74ffa0cb,1fe7f0af,8a1a6ed0,531f8e98,28be1110,90b580dc,5ff55fe7,b75d9b66) +,S(f3f7c27d,54b40dec,5b3da04,b2cd222e,1507aa70,bd236bd4,2d6e5c95,c71bbe7c,80e17f04,7cb2203c,cc58c364,d77e9a1c,8abb092c,18a9f8d5,6edcdaf6,1ae4062e) +,S(466b6e28,8981fbcc,92c4ec3a,a54eae4e,2e266041,5b242aa5,523579b5,b1fe93cd,9ae0e0c7,d056b2a6,831f540c,67a97732,33bedbd5,bdcd2904,2a8987cf,1fe2a86f) +,S(e15af336,8c59576c,a36b5f80,d9ec0458,d9534bf2,6650cc26,a2079946,ac43fcce,7fe0423,e6ea121b,308d167b,ada98cc2,fb8cd772,f95c3d84,c1907f09,66e5b46c) +,S(c20ab235,6dfe247,d8fd1a9c,d8c4f16d,a5322bff,692efe70,f256521d,1b990492,3c0aae11,e4cf9a17,57feb324,e1a2f0e,48d5978c,6007429d,ab32b2f4,d1a19bd6) +,S(d8b6c190,4a848755,f126651c,d96fe374,7c5a3744,1fb7048f,e15c4693,dbe35e17,fefb5310,e7e38891,70fca6ec,61ffd2d3,72605ed3,4bd22d73,c852a92c,c22fd30f) +,S(a8256823,5416b18,a01d2df0,f47757d0,f42c26e3,780ae6ca,2421aef3,83361f8f,c9192412,77948b80,87d1c45c,a95224ae,5f9c294d,fa74c1ec,42ad6004,f8646540) +,S(28f2a237,c4dcad5b,a6bb5274,1e743020,e90bc598,8ee66b39,cd672ed2,ccac6c33,416c59f6,c1b1b036,f38a5523,722954eb,52848872,8ecfac4b,5bfea63d,cfc181db) +,S(e65d92b7,571bbea5,4e541862,a4dc7890,bd546782,bca886e8,3c1ea17f,33d77e9f,6116ed43,13def2ae,6b0f2b92,ff07c91f,4139982c,12511152,fd7ed5fd,851bd04e) +,S(68adb9ee,c7ae0198,726b0457,91fe52b4,b5c2ec78,4199da86,bceac230,d55914b5,41ff0808,e42aae9f,e93ff704,6f08fb38,4b72d7ec,7b1dc6cc,e0071306,7ee5cfd5) +,S(3f0f7d6e,6bf73a97,40eb842a,35cd4800,85db4766,f7fc374e,1ac915c0,e4d6d6e2,392aecf6,8b446346,2d37a106,8299a09d,9ceeeaa4,f0ee3397,dc54b2f3,5e3332d6) +,S(929aa2ad,48a1f3a0,24fbf7ea,70e9d57b,9ef65afc,b3cb2d8c,5041a933,321359e4,b1cfddf3,4693ff40,ef16d8f1,8fd1fec,a71252cf,ee13ba4a,16094c20,3b4fbf66) +,S(9a9ff691,f16ad46a,d0b753a6,fb09025b,e196b443,839114a9,580eb2e7,fc494e2a,3776f83b,92c135f2,5e5e600,27f65e2d,582fa2cb,f5842c3f,644fb726,1e305c67) +,S(6752b226,65900d81,47ea6b73,25b212a2,9bcf899,799eb1e6,213857dc,6959d32e,fafdac9c,2f791be2,d1cfa78b,7530859d,c1b2ff43,e546de14,1f42baef,3d6d1371) +,S(9fc32749,e636e845,aebfede1,1d0928a1,8cc6e3b4,5a2536a9,fae0a2b,ddca6434,9021fa85,56c5fe05,cc87bdd3,6cebb00b,3d1b77e1,80ce2f8a,a4d155ee,84fc6441) +,S(60ca338f,ff905b58,b1d70d5f,89c4cebe,15600495,698b262b,e1550da,a0054a99,6d57de6b,f7c637ac,4d728090,9960ce01,4b541abe,754b82c,bfc87ad1,5fbe6ede) +,S(91537a09,697d3658,b06b853,d07153da,6d0228f0,786f334,db6151c,64becb87,231a2595,e04dadb4,b3900188,9f34b43d,5cd3c81c,f13fc6df,d432a377,4f53015) +,S(93a85356,195e109d,ddf054d1,2a901d83,b10db692,fc17e878,849eecfa,5d387fff,58f2112c,3df38b46,51e347fa,5aa8569a,48d95b2,670c76ba,d0de339a,72ee87e4) +,S(d5d1f0ac,bc2ed1f,50490030,43fd020d,7a1afb27,cdf78318,6a043bef,b0708eec,c473908d,4d754fe2,a5fdea86,62a6ad21,b711560d,77ad85d4,e3b47175,b5133e52) +,S(afe283d3,8cbb257c,5f2c0dde,2fd1bd56,2ad33e5e,605c24be,51a255ec,5725b1bf,a050f64e,978a9ee6,36885656,5cf22d7c,2fb45e64,6df60abf,6d3f9e1,132311a7) +,S(179667a1,72e0c37f,33e835d9,b5b6f326,3d963f37,1dd07d40,e790e0a1,21765180,da24290e,e38be6aa,ea2e86e6,bf683d4d,90f5fb79,20257b78,aee8be4d,fa65eea1) +,S(9add8ed2,6feeca4c,d3e583f0,88398ecc,f245d30,a888e135,b86ac1a7,e4e42d05,41bcd07c,c2e6acef,bc488d74,8a338c9a,7e6d3e43,85029f75,63dcde83,54d82232) +,S(25ba1237,6bb6e146,5195db78,29f51889,869dc972,fc9805d3,f6dfabb8,bce6b,a39dfc2d,223f924,47ae617d,537a4877,e6a47e0a,7e5fdbca,e1bfc956,a9c42571) +,S(dbd78620,c9a75ddf,55d0a41f,7312ec44,d3108f34,e7d75195,d3d49f99,88e2f986,c184bf06,ef304b72,1637bd3a,d511dc92,297dd5f5,9bd8d053,a048702a,fdbc400f) +,S(f8d67c64,b71d14af,b07d734f,675913cb,7a1bebc1,6e4a4916,44f8f4f4,6eb69448,eda6023e,5df40a5c,89a7bd26,94767a5,f17042ad,f95fb4f2,88bf9fdd,b91b280f) +,S(fd5f3228,56c096d3,4683a672,64ca5d76,58a8b7bd,41c3c1f2,5de2e6b3,164c8fd7,8372223f,3fab4f89,f6604b16,dd66db85,b8a27eaf,30741bd3,7a90d34f,f19c154a) +,S(ea49d0e1,b07948b8,b122d52f,8e8be6c6,e470262e,1c239b40,483a6076,c005136,b81ee77d,3d8489ab,42a9978,48a2ba17,273517fa,604a2abd,f2e1e726,d71b7e19) +,S(bab8ce82,c1210a99,ce17a5cf,3636baa5,52cc793f,89fd25bc,b6f078c3,92149bc8,afe2bb15,d00cf460,953a7c1c,8e466556,136ebd4e,ebf61a76,e49726f1,e3aa8e73) +,S(98802d2e,225bcdc7,9e02e746,891cdf12,ac434154,c08c2e78,589dd9f4,27277e1e,1eeb6b5c,4fc6459f,28e3fc49,6d9a6baa,fc66ede0,85d4b0d7,4af45840,78987514) +,S(5dc54290,2b33fd65,7d37a22c,c669ff21,75825cad,b34e1ee4,7aecf7f5,2dfd750a,41fb96de,60564df7,6c8ec857,d7f0ceda,2b330fda,2195a70f,5f64f973,ec0144d6) +,S(c4894f07,f6679f4e,13369495,dd501aca,73666303,2f7bab6c,fb81d7a9,1495834e,31b7e5c0,35e81c22,89de373f,85d648dd,892021e7,1d120742,e4ac6af2,44a25a8d) +,S(f7c9950c,d5c5afda,ede613bb,8f21ca15,b0e92487,81e16378,c7f2cba,5efd7beb,1746037c,b9211262,f0867180,16f0fed1,75debbb9,5eca39df,ed757c02,aa282d24) +,S(96703c67,4e7e2859,f274e7f8,d458f7a2,89ac0bc4,961ef3bb,b120a1e1,b82b5a55,1f92a15e,5e8b476a,a165823e,59da387f,2f9ab551,4703010c,c35b787d,c7d8ddf) +,S(4e8fa83b,bf9307a7,8b997e49,68227e86,a0402f0,643f98bc,5d13837b,63d08156,81f1f92,bb2a9ca8,14cd212a,6de7ebe9,7a04ed97,4de89e5f,cd8749ba,6e49e4ea) +,S(bc239122,b0cb1f43,2ed0c2ec,bb3c5391,b7eebafc,c4190fc5,8f62d313,7f58c1bd,5b3cd401,d9cf971a,6b9f70a4,b6f8fab2,d0026a24,cceaea2,a86aef0e,a76a3cfa) +,S(90b7138d,5d2e11b2,86d29b78,521aa9d7,205351f0,1451e134,8a593b78,fe5f1778,77cdd3f5,829af7d9,937ac98f,58a78b99,b54fe9d7,593415f2,18dde880,b5039a37) +,S(631d0fd3,27bca9a6,aec8eff9,54fdde7b,df43edb7,b1f602fd,a32c387,825841e7,d6e8af71,273702a3,a2a767af,3c438f0,b07ab614,2fd0f67d,74a9d358,74e29100) +,S(ac2a0153,d810371c,6a7bc097,ed09be67,14ae9935,4a9910ad,7ee77ab9,6d54cdee,39707795,a4f01640,87b5e149,2ec8fc1e,be11d8f7,c43555ac,6f4674fb,8ac5b0b0) +,S(89751ec3,c1aeb288,1953d3d3,f2f634ee,29b1aa18,c55b8b62,59d5cadc,852ac99e,8b4d51e7,e2f94c0e,ef80e16d,9b62c935,2ac91394,902537f1,e93744c9,72f22a3) +,S(849a6cfe,5e3ab81,2e21f491,4c6ec76b,5233d405,7ffd487d,bb0b5091,2ac499d2,a490b967,b5fcea21,d0135ec3,c9ed5efe,ecf7ac6c,81e06648,66cdca60,a1be7037) +,S(4ffa897e,17d760e,41859b6f,3bfc421c,55fa1e31,eb999f72,5028be41,cc8a9ed,87b4b5ab,d9875b94,aebe632c,64ea441c,71ec7353,5ae2c83b,d9b904e3,7f7fd326) +,S(4fda56c3,8bae34b6,55b2ed2b,621e5074,7ed1dc13,a1a815f3,be12a223,d54c43c6,6cfaa7b3,c53a1dfd,a03dc7eb,6086b84f,ac280c55,7c357d16,fa5938cb,c149bb10) +,S(b572c9c7,9ecd2ee3,154c56a5,9c81444c,b16df1f7,82198eb7,40a78c7a,e5c7e99,a1fb603e,42d3ac6a,5ef5dd74,ced31624,fed9086,5b02b3c9,2bbad21a,ffb1b79a) +,S(a77038c8,e79abb7e,a94b4e17,35f0f5fd,a07b1ad3,367047f4,895a5104,dfeaa623,e9bef9db,e8e2078b,346aee0e,efd64657,2210e451,ed2e8d63,b92be059,c577e1ea) +,S(3158f3e1,14de5071,c6e2bff9,32f1c269,7e374dbb,786e960b,7e59df03,26e2e1c9,1d0e950a,9124a626,4553d29,3e5cb8cd,9dd05d45,f48de2ba,f60fcee0,ca42a9cd) +,S(b0825b69,373aad7b,53c256c3,1258169f,2736d47b,1c4aa22d,a052b864,375a807c,23ec01bf,a857fa0,1f3d46f8,7857b211,f7533c9d,611dfdf7,951d190a,d7ef07db) +,S(9578e6e2,1a738f66,ca04647c,ec610da1,afc5602b,1e72e096,bd192ed,f19c205c,fb5da982,7208b4e6,11e6dc5d,dd8b8ba3,569a578f,275e6d21,3417fd37,7e4e6cae) +,S(a0aa979a,5448d27b,e42c39e2,2fcdf40e,d3b64c74,42b2c40e,2f6d38ff,fabb196d,7a9bf938,937be244,38aa212,d9342f98,ab9a35dc,1361fe56,5d04a25f,176eaff9) +,S(f535145c,d57aca97,f4cef68b,ce57d5fb,62ccd1ea,b48dfeb1,3b87b188,4614be82,1323e73b,dcd6b16f,bc481294,198d8235,b98a1885,a336f07d,593e5df3,a21ccc11) +,S(587d9e30,2f9d60ed,eeae03ca,252cd2fb,117bd07,fc91163d,a7cc569d,1b023f9f,7bcd5b42,3c01038c,9d3f7f71,ef52b4ef,a99e4be1,6de4314a,5e517e8a,950f45d5) +,S(b1e3b70,aa8a0cab,ec119ca0,5430396,ed98d9f9,e3e1a650,94cbd985,4cbd0932,3c9496c8,eec7dd92,9718f7a2,8c7a67b5,c4e55d88,5a943cc0,deefbd11,fa4f8de2) +,S(8f7001e2,928200d3,a478ba12,424be4c8,de8f16f2,a932e000,1468d9a0,48d57816,bedb10ab,72eacf26,1ab17a34,e5138686,34995952,545129b5,5bd6c7cd,84b50bc3) +,S(c95bae04,e1c397e1,498822f7,b7910019,922e43de,83119ae4,8517aa9b,aad6f35,8fff7d3c,d8bd7af6,c8523375,84f73aa,2f1b03c6,585c7987,98fe29aa,36620486) +,S(228fba21,ffe5ff9c,e04017e2,7cf04c71,1cc454d4,534d3404,49bbd1c9,60d2230d,825fb4a9,169ab67f,50963731,10f27fc3,f6f7acbc,9f2918f,c6f0bfa8,a92ed32e) +,S(89610875,bb7c5f53,87929e38,22249817,ade80ed2,a69dec5a,eee31a48,6c40e316,f7d5f522,92a53c2a,447f9b6a,5c06b3f,22fcf19e,4a200ef0,9167c255,a373ef47) +,S(9f0542f8,8e7e2b4b,c20b7b5b,87f051af,6650f605,67dca77b,7b6e1ccb,2a63f6e8,2b80c840,5e343203,d0a7d1cc,ccc99736,4bfa10d0,6e71880d,a0955cc0,b1ff98a2) +,S(859b6ce5,b7075222,549b59bd,3de967b2,1655d163,c57e94e5,6c9fae12,e5222061,25b8cf47,baba2000,19036027,fc86f03a,1c4c406c,4a8b2c34,398b7191,bf125aa6) +,S(e6febd1c,91e66c5d,afc7caaf,4150a023,b70c57fe,2a14f84b,c9fabce7,34982ddd,b606e3cc,f997b627,a0a02c4c,e147e0ef,58e3e275,a43940a3,4ac74760,457ae5e5) +,S(956912,ea1f5ce3,52d39e67,a41e4ec5,8c7106e0,4552e1a0,48e4b195,6cff8c01,b1eb2fe7,7419c11f,f7e34dfd,a752064,39fa2e6f,b9f9d34f,70740950,cee3cf40) +,S(1bca39eb,9b6a5d5,3575e510,6955143d,a6fe9d67,999500,ea78cb89,d832ddba,47afc8c9,358e5f7f,e92370d1,44ecac99,4e69afbf,6ef1af7a,32adefb7,9331173f) +,S(2c6db85b,c4d84c2e,61f0b5c6,2f330e7e,dd672c63,a14fa534,9e4d1488,ad462c5c,bb488565,cb2da5c9,be8a6f6,c71555bd,d85b4a53,20ca0753,f524f91e,ca1e331d) +,S(f66c0d73,36bc153c,5f85e297,52845255,8957a08b,603e4d3c,746e0c19,572c9f82,74e32022,faa58df3,1e4a02b6,f00ce5e7,bf34ec93,d8095144,a1af69f9,c637f01a) +,S(774959ef,cf8c126a,67012f8,10217d39,298040ad,da97e973,9fe13f9,927f6757,c33b9f7d,b2901f8f,171151c5,dd8b890b,62c0a1c1,9b7d4691,9c23ed00,cdafdc24) +,S(f59361a5,74b69786,f7ea4abe,44eef2f4,5000328d,dd72c2d5,a6dce9b8,63037ca6,34deb360,ece45bb0,a2417618,e7c67adb,24b33620,80d464af,a1ef20ae,35da05b) +,S(add233d,31c179da,bb62567d,2a5bc5d7,9afc5c50,93dc8196,9767ccf3,8790a5a0,b9ecd675,13d81a64,8ca5ab2f,4d580d3f,7f7d4def,909f2759,af61e497,b6d8f82e) +,S(3d204b0d,1d705714,7e947150,52a531bd,b6749743,5c94c7f4,8f821579,170fa257,ae20862b,cd86c6d8,e8344911,cfa8914d,f40e3a80,b762fd54,e2b7f704,4de86017) +,S(22be58e5,1c5ce0e2,f1ee6f5e,523a2f55,e110c4b7,bd3135d2,39d7a25a,94bc13f4,56894dfd,6c2cb7c8,5689cf04,3c1d4221,a7dfdfd0,d05b1e68,cc41afb6,6a472a04) +,S(6e015fb9,af8ec2d3,9f101509,f8f1fc9c,d95dcf57,82621ead,4651a062,c8487687,c7dc43db,d1d068da,9cb751fe,51251bf8,7f6edbc0,bb19925a,bc02cf6d,d7cf8554) +,S(3baa1e60,42844786,fb720758,67ec526c,2e0f7fab,ac8e0eca,24eff876,47720c2b,909d202a,b40037e,9afe2ef4,d8638491,d27a1d24,98e497a0,908a52c1,52b1f8da) +,S(689cad9b,2cde514,b9c25275,d6a3e68a,ce0b2216,4418a403,a83d88a2,4dc4d25f,53e7be29,165632b1,d1887aab,94c525f7,86166f32,56b44e70,295dd0d8,bb9f22f0) +,S(c1cc4802,101d0251,76875033,ed9fcc07,a66a57a7,71ca1374,63d7c7b,655fdb6b,ffa73601,5dc4ccf6,8afc41e8,e254501,93eb75a1,60a98c,6c800d0c,f10edc5a) +,S(888e34,86eef22e,2a25aaec,9bc9b84d,fec14fa4,c98e9d57,4fb9e08f,5b0f76a2,d2e3ec7c,db9286b4,b0223762,234d771f,a5b6f378,b6a4bbb3,c94c3303,af49b4f3) +,S(ab355f17,a43d24dc,330a9195,c2be2ed5,6894a9d,e38d3dd1,1a31f9d9,9ca88a93,7f8f8748,c07cfbd9,fce0ac7c,b624f2d0,f83bc49a,53276b00,7ca2b821,51988e6e) +,S(12a65c28,aa2020c6,5f56d916,c1cd111,10f68f0c,2408863e,635e05f0,1dbe4141,48bbab23,1be95484,7a0b61d3,89913ff4,5a8b97bc,384a0780,294d97d3,9c9b9be2) +,S(9d96107c,591d95b,42854234,1371290b,370640d4,2dca9fb,5f5397db,84a5577e,183fa1b1,cda4719e,869c8b44,e97cf214,3ab14e5d,e5d0d781,78e5d68a,923b3dc4) +,S(7ad05c26,aa642731,9eded7a,43f72faa,d344ef4,d413d884,67bea154,4459ad55,cbd8ea0,7fcbda54,813f990b,a6eb8450,6faa12d1,cd478a9b,cc278a32,511ba8f9) +,S(d1ddaa71,27959090,65ea358f,b887e4b0,894028bd,457d217,f1ef6b9d,143a2292,e040cd3,d5f19cc0,456a55a3,88ccb81f,787eae9e,51a289cd,d8ae8d25,881e6ed2) +,S(ec3bc5da,a9be90e5,1af0c1a2,bf41adfa,8023bb4f,b40e65d1,c65a0e82,6bdc30a6,d4962737,f9df1f4,48df1d9c,d8f7a140,233c2175,5d8ef5c6,f292b230,9ce263cf) +,S(f3135904,dc55f515,806dbecf,d51b8106,f13dd1d3,43f78e05,91de1f94,86fd193f,c5bd3002,24934faa,2f9ec634,53fcb3a8,20af608e,efbeb963,23871faf,a7dc2246) +,S(fb5f135f,b2f7638f,36bc332e,1bbd2050,335c141a,49db5452,619f266f,df85e8f9,6e71e045,827c3ef1,b75bffd2,ce54361,1e627cca,c94c8d3c,9b8fff62,e10b7b6) +,S(d1dc12ca,62a1285,e3c16125,f71a1b7,7b0dfa9d,c1068ba,2bb56bf3,98864620,6a6bda82,232af467,afaecb2d,f4a7ca53,3c41d63,d8f7093b,f04a2964,6e4f19e0) +,S(52354730,2d24cb39,f9117e35,d9374ea8,f3b6d027,cfcc23b7,58f0bc1d,b4f2d94,83719f2a,ccc79c43,1d8ea584,c78dd5f8,daded80a,fc3341f2,5789181b,7671d586) +,S(fd8247d,d208839f,93eebd97,f6068ae8,a6640a46,48c16b65,6bc992b8,4ccfc5e6,dd560b93,9b530bf9,a8bfd5f9,614f498a,49e54061,61107517,e9ab0104,54e42f3f) +,S(da5d83a1,9254990a,c14af844,df04bcb9,accab655,5468d08f,3b5d93b8,425f1600,9e5a8786,6be51fe3,54bc2412,6315f8de,1f0113cb,ae78b72f,cc70572c,d978fba6) +,S(1205f8ce,b9c89dce,13cd44f4,9ef0b2e8,14b78639,372d9f61,f0543f54,bdef2944,6c820a46,afd1dd98,499636d6,afc34ae8,218a8c90,2f210dbe,530a9211,a058768a) +,S(7d94e0f3,3969c5c8,164739ff,11aa05e0,45b32702,333d947f,8a0cbb80,57dd3c71,7b92b367,33f92723,17948d9c,a1875303,865fd47c,ecab145d,9ac9d4c9,864d62bb) +,S(d7f22ab5,6840dd87,d939c4ae,3bbe5a67,75e7a2c6,dc969a96,40c7f65a,9c18b2d8,bfc60718,2509244,b0ab951b,f4032852,54b73d5,a23a19a0,b4ccff25,1e5dbd6a) +,S(cee33146,2accf4fa,5f06b598,cc36aa06,342c83d7,eb4b6aff,90b9555b,fee0fe61,97583a48,5c323459,ef4b3f02,bf63f9bc,315cdf91,6163b389,c6a48cf7,4127da34) +,S(1e1d2d64,1ec550e1,1a29bc03,cf7c8442,ca13f10a,182783c0,d4ee9bf0,8c3c8a18,1ca8ff62,fadef98a,4d1faa0d,28e75e5f,117cd890,2934e457,e042b870,d8cff1bb) +,S(f10b2e75,8246bf49,1b70ff95,a385e701,dbbf333a,95a94652,6df7cb7a,4b4cb68f,ad15b5a4,d39d6458,55e57a65,3872d9d7,5f4b3168,182bdee0,a6289f68,aa7f99af) +,S(4674dd8f,16ba5557,c9ef32a0,2b6be805,e864e7af,c4fecb8f,666cf396,f7e6b0a7,c9d95120,73388bfd,45f702ac,8851b5b7,531edbe2,df04d855,7bd58b3a,c889f48a) +,S(95bb741,da472f82,8ff1f98,970c2bc9,1dcaa4ff,2cd4c0eb,195978b3,242c66f5,1e097cf6,d47dbdfa,8c3f2ce,90e0ccde,5adbd581,9a6e135,957ce258,805a4130) +,S(9a892056,42e03fae,b24d8fe0,f94e4f6f,d0c72581,604a65a0,99e3d28b,b103d2dd,c6bee4e0,d94c30de,b99c1a21,5d28d7cf,930e89a0,7865ea1c,87d39f5e,1522b33a) +,S(ade39c3b,e9540095,2176bb09,20541076,dac8e68a,a20903b1,5048e5db,4b0539a4,6bec56bd,60596cb5,7ea8a355,a6a6a4bc,90871c81,de902b0,6abd45d2,5802b204) +,S(45f808f8,bab4b4e1,a790240a,f036516f,bf0a8eaa,dd4dede1,eb88bbb0,7d248b8b,3f3bc72f,bced768d,bb90ddef,5b1baa9d,aba634f,1fe19842,c3b4b456,8957794a) +,S(ac9aa903,845a3081,2ddb195d,d79ffb6b,6e8ef0e,d34d0a5,49b577ca,35a116be,368ddbfd,1c5a2fa,c2c183af,19097414,82b272d0,52cec4b1,745fba6b,4a9fba89) +,S(ba26fbbc,ffa6b9f3,584f18ca,255da089,f561c30f,29a378e2,9f76de9e,b756f6b6,55f67b11,81ff78c4,c4c86e7c,931e314f,dffb392,306a7145,5efe875b,fbe39ba3) +,S(d4b1a902,1ebed925,9d53f2e,d76b03d,25b4241e,d5f932d3,a54c4dd5,db3f0423,6f1dda69,9e60053f,d9d2f5c2,c2ed2ae8,ba3337a9,b669ba05,3e48d974,f73c2d93) +,S(bc91d139,b2de4778,458ca891,fa97e413,d4cf32b1,753cfe0e,540726c4,648b451e,b56454c3,4f568840,6d198248,19ba350e,aa291f16,5676d7c9,fbd2d592,169c76e7) +,S(cd7a85ca,a0f408b,26b29306,3e01dee4,acaa8f9b,36f7273f,378290f,a4435450,1d9508b,1e7f5bc7,ba8fe5d4,f44e8c57,2bff9a98,887c3ef5,2fba3057,f132c5c5) +,S(497f8e85,57880856,32edbfec,b0065ac8,3267fc31,da9100ce,c2b99cba,1e7c26f6,e6729428,fabdec3,2db731bb,f33cf45e,5001b890,8f82c0fe,f7cac291,f89c6a46) +,S(68a607,4065c372,a95e7a6d,fc742e32,e59cc4b,86f9127f,f9465474,a466e30d,58e59131,1cfff974,e9e9162e,e974de92,904ffaf6,ffb97f49,d9dbb1ca,bfd927e) +,S(79d2dba1,67b93ef8,82ca60ef,b803e1a6,eadb4406,7f9dbf6b,d45c1a72,23da19b2,a59f1b7d,9ca146bf,93b89f2e,244dbf00,31511075,4dc03050,aabf0d20,41230ad0) +,S(5a2802ac,f4b92bf8,1c98c431,28ebe1f9,c921c211,910b6648,6391ca0e,4c6ad50c,5a0d7b4d,49b5835,dc3b395c,83031571,f7c361dd,840b41d0,405db351,14d86eaf) +,S(87b8a84b,1fd98b53,e9eede06,74555c7d,806d5b7f,4cfced4,16203280,18c398d5,a38c4d7d,549bd5d,28fa39e9,805e81bb,975cd137,64aa8c7c,a7055d63,bdec52f2) +,S(f7ca4d9e,7ac11395,70db6a06,c7f00833,5982181a,6642846,5d0899bd,907a9223,2c3dcce1,2ee6a995,52a72eee,e0968552,919502,5d4962c5,a33035d4,501badda) +,S(15bf39e1,5a11ff6d,216c0f7f,f1597f7e,ceeeda34,91b450f5,3b3407cc,bf7ced7d,325462a,6591d018,e495c6f3,fedb3abd,75d5a4f,15e9ab99,e43418cc,4aef1ecc) +,S(2755adc5,e69db19d,7caf52ee,117c6907,5b342e73,4af64a4c,77b369d4,269897b0,199bbefa,7e0b1c10,bd78ead,d5e65a6c,e6b3ee49,1400b9ad,a7874bcc,be580d5) +,S(96ef0e89,80602225,1d8bb9e8,693f3a67,5152cc9c,43c4e953,5310b9f2,c4338d8c,2414e799,14b2d6e2,a9be60ed,52508d6c,15765c0a,7fc1746,8820fb99,a9feed56) +,S(c1642d67,339fa6dc,c553233f,116f8086,2edbfc24,5512c2bd,4ca6e348,712bfb19,65e9a14,c7356a36,625022cf,873161b8,ef43ce38,a99586cb,9b5c8189,f3c6b8dc) +,S(54c536b3,cb6da9b7,e4483ce5,d5c9e660,bf973ef2,f4c8b095,f52dfbed,ffdfb96e,777f16c3,b026b75a,c9863f38,d015f04d,e1067f2a,a9cbd15f,9ee27475,ad46a4b7) +,S(706041aa,63dee9bd,4a441238,bd343bef,14e69929,f6e007e,181ae87a,68362926,36e15a44,57b86f4d,9b233dd3,37fab91f,67e4c811,3340463f,8f443b50,5a0ba014) +,S(49acc336,116c91af,6f90a6a2,d69b9d3d,c0be0a16,afb9830c,c1af0b5e,749b2765,bc4f0f6c,725e037e,3a9d9be6,c5d4f80d,67b3e28b,92e5d3a8,fbc8f66d,9ba97d52) +,S(96bee384,ff79ed86,a9d816cf,32f3d569,d1ae03d2,d154c65e,a117707d,74c818b1,848e45ec,27eaad75,5328d84c,a76a7235,ef4f492f,5be115a,a622171b,51dbac8e) +,S(3b2b0810,c4052498,189affe0,4c7ebce7,b9050aa4,6a702d0f,72c5c770,4ca4d1ec,f782ad72,1bec3b8d,b1df97d4,7765434f,e4145c81,27acece3,9dbba604,4df2543f) +,S(54c3547d,e4873721,df6215fb,ef7bcb4b,b9acce42,7b0381cf,2de151f7,c69549f0,5e7ce1b,728a3b21,e94b4138,bcb89af,f4778dba,fd21aa42,16d83a7c,9ae81edf) +,S(9f1c53de,e7b895d5,6081585a,c6eb3821,d89bfa61,55cc96d2,e8b6c472,edf57f27,38369e41,ecbdb4ac,619aa7da,4d708eaf,4c572ed7,f1f2c079,d0bd17d5,3ba8288f) +,S(e79f75e,a1a224c2,a0533936,65a5f705,15eb0aed,b924dfbb,5aa30055,d1d82b44,30c65139,f6a6f5ee,5304334b,1ce9dd15,27ba6031,2ff2697b,8eebbdab,67105171) +,S(7f95a1da,79cf25cd,a904b7f3,11674d02,fc6dbf34,ae8ba7e0,b179b48a,d942821a,3d3dd8bb,31cbafd3,247ddceb,be1faeb3,b00e057f,7e90bd1a,78f89cdf,648f7c8e) +,S(41e83f7b,9067a7c3,bb6ec755,ab7c83a4,dff74c93,65d0259c,cb635bea,718a416a,a54a4b56,5286b26b,d0bec338,2053c349,2de80062,2189e2ef,ed10e3c3,9457a34e) +,S(81599046,453d0a96,2c5f3b47,fee7f827,537c7c58,c5ad068e,e5e6ae6f,7a9c6848,4a83b015,3abd0df4,177ef3f7,e8a7ac7a,e8e5afb9,a2fd359a,c30c13ee,321f943e) +,S(11c9aa76,2cc334f0,a3274f15,17a1e60f,2f0eedfa,d603f83a,120162bb,e87830d0,7c73d374,26f648fe,4a9978c6,a64ac787,f4c8d79f,b58b0f58,70e373e4,d317136a) +,S(2b41f344,b285a31a,ebf4f4fd,f0c4f463,49fcc794,e5762e04,dc39efc7,936735c9,7eb708ba,3f683d48,db8e0c25,ac8a6f8e,5c340749,c3d6ed5,7936d515,7f3d6e73) +,S(dc063e66,38098e8c,6ff55a73,faa20099,c10d0258,87c19850,43d1011b,ab211b93,4abdfdf1,285ca3ad,65bb9a85,7ad647f7,fff01782,79fc6df1,6179e1d,495d212e) +,S(7cb514e8,b6361aee,f9a7abe1,f84ef401,97de721b,609c3151,a056eb03,71ccdf8a,4d359c89,7d03a633,369bddcd,52a67387,fd4d1e00,2406502d,9ba6e967,a5efd0be) +,S(103a6644,ee731721,bf29f036,97641fde,9775a642,9ce8d97a,26b06ceb,f9066693,127b6948,6db0d230,6ae90a1,4b8c8ecc,1e9293eb,cf154371,5ecbd579,e886a726) +,S(adfaf771,662d719e,532ba045,72a50f12,144fafd0,afe93f7e,1d8edd13,99226e5b,b4d0335e,8b600a4f,84d919ba,514bc249,e3048cc4,281db3b9,a7c62013,4f00bbca) +,S(609d9352,30f3c6ed,bf64275a,23a9a258,33fb8c3,9c25dde,4b55ce22,e64b50fc,cbbae84,c57f9c52,67278c4d,3b0cc5de,9327ba71,ca1a1bb1,8aec9a97,bce17552) +,S(5ea5aa20,1b958a8c,49b70510,b7c6a29f,86d9ded9,afd4e833,77c433d0,f62d9ba3,27c5cee9,fe3c0bc2,af8d0e30,57eb0f57,b1fa4237,996bf8ef,5ccd25ef,e1b87ecc) +,S(3f801115,3603a689,800c04d2,7d3b3fe5,c415ea29,c6e11951,7a56712f,20a10862,4280ab61,65b122e1,85abf4d4,14a31961,ac6595cb,421db183,34e57cb3,fe9bd059) +,S(54981ffb,5b692a3d,6d9ec7ce,f027fff3,c5dafb7,aaab4c5,572522b9,e1157e5,893183bb,554c81de,21c84134,e395b9fe,80a5a9ee,b06e3b67,2ef93127,24c307ae) +,S(97c18e92,d499ef62,77585c6b,28f36fec,e9a2e118,f793260e,32750861,a1aeb813,38a87702,7f1df077,d6e734fe,18a023ff,36c6d4bf,4a7bc7ce,d3a3d38f,c3069f33) +,S(d5507aae,97bb6e17,410bc87,26d9eb2d,11558b96,6c54e7db,6428b44a,3589820c,e78d0140,7468dda6,d9c4af92,1984460c,e523c101,79f7aa15,d8004dbc,4fc63cb9) +,S(220d6c2e,6c500d01,9ded2aa6,5de205f7,cdbb15e5,69038ba5,7bb1700f,1182ca70,64b68506,c387ec2d,9e030448,797bc28c,5e09e8a9,95f5f80e,70c26a1f,db7b16b3) +,S(c1a45038,da6aaf1e,396595c,3ab404eb,9584ec90,d894985c,18e779de,793d20ba,1a443da1,72e281aa,4393e1de,6f1e4f89,4fe5d1ec,b3a5bcd9,607cf79e,6f20b533) +,S(9a66f9b7,f3162c80,d22d0b5d,2a659bc6,ac01c74f,46804ca6,c908b6cb,90fb419e,3465357b,209c2b,a7c766b6,9c17abdc,69205c54,787fd0ef,e744692,16b95d3) +,S(8b305bcb,1b6156e3,22f0f0df,28265cc8,79e5909b,b76b546e,eb223d62,89883387,929a341d,dfa05574,90cf5293,244d924e,852b00d0,d328ffe4,7ccc5356,623a31a) +,S(4e36ee67,b5398169,8ed9460a,cb450023,7198f249,4dded391,4c2ae003,63c8f664,a784c95a,9a443b7,5859a1b1,373eb750,150e1ac9,d5120ca6,f273578b,73df77a6) +,S(167275aa,38d02988,c53e0a36,d8312e32,369bc677,6d7143df,cc6fb83f,66747caf,f26b9851,31abcf45,369a6415,84b2956a,7122a51f,b909bc30,5427eff3,916e6e14) +,S(c93d0b74,c99358d6,d24a962a,15adafcc,8a4e57ca,df0afeb4,9d46ef53,cf619797,f93b324a,b144c6df,336e34e1,beef5c13,b931668e,54ab34a5,eee1a8f5,5acc683) +,S(8bcaa2d4,671bb4b6,8a924109,32128551,fbf6d924,d4854027,7c1dff57,3bc4a8f2,88be3e1,6482477e,62255fb3,24512abe,27cbb4df,f63c6d8c,93c3c923,47e519d2) +,S(69d186de,f9a7ea8,be833f2,590db08c,fc0acfbc,64a648b6,8ca412d6,88c03808,d776b644,43a81bf7,527f658e,45d92324,a08ce171,6db6c259,898fab8c,f3d6d41d) +,S(4a53595e,e2cfbc6,7bdc9f07,308f57c,7ff5afc8,a35e27ec,e22cdbdf,6efde161,e55913de,d481077a,ea36c502,494b3b72,b1691786,335b0278,3e40fa1c,1a1b0876) +,S(90856f9a,5f7bbc14,557dd419,516ce5c9,aa451bce,13c17ce5,5c9fb1b5,f2924995,ca083d2e,b5a3a067,4a2534fe,e03bd99d,976b96bd,2b7f16ea,6e2552ee,648e934) +,S(2eed315e,d863298c,aa374ca6,2bf8313a,a0ca07e1,9a94bc4d,58eb235b,52d36dfc,afa36c8f,90d7adeb,ec33ec69,40c08d1f,2a4044a7,477da88a,6653d90b,880856a7) +,S(345a7f2,ab7ef064,eccdbe6a,ebd11dc5,3c7ede0,b62ab3de,f8e409e6,258e84c4,c0c0dce5,93cf9647,c0228d8,42b78407,c3b634ac,799fae19,f0e685d2,6ccfd73f) +,S(5a669d7c,209b222b,47843a5a,633e0b53,48442258,663a58cf,f6ee3cc4,1b893d3d,7067be1d,8636698,2b808b16,3c05e57f,2be77d1a,2bf78038,e242f5c7,4ce10523) +,S(3f0b0f3c,30a991bf,927f5119,6fb4af39,e1399d01,1766b1ff,1f0ff2d8,a3199062,be8f8781,4c06a75,97d062c2,196a49ef,93a73923,f43d16ac,82c015e5,4789d472) +,S(9d7f2c64,98e78982,8492b8c9,2e0e49c7,b5c9d4b2,e86c409d,828447fe,4a2f5a13,95ccc540,8202dbc,78323d6c,7ef66d51,4af34699,5be50b56,98a5864d,34e502cb) +,S(9f741436,55b3cfb5,39917a7c,79fec4ba,164503a8,3958aee1,f6e8f34b,3d861b78,345738c,ff8f3c64,f5cda5a7,a1aee7ef,f3a33141,a5139c3f,b891c6aa,feaaa7ad) +,S(cf1d3136,11d4b6ec,5f1a9728,c9a7b037,d9c16d65,bad95fdb,17376200,b1a9a97a,51c72169,bf7b1ad8,6159fead,dcf3b0a7,a7fc13cf,7bf12d98,409dc3b4,fcb7a9a2) +,S(7e04c6ef,c7433f3f,fa2bb827,3fdcb3a,e0470abb,cc7c8dc5,414ff77a,915868a0,781125c,2184eeb8,87019f7,c5c0b4a5,5922d11c,53c3db9e,d5795bc6,e7539cc4) +,S(fa6d73b2,a97801b6,16697ac6,102b002a,53d29d33,289708f,d8f73964,77f3d57d,543db558,25c67a4,cd010413,9f8a093,7ea7ac2a,380799f1,c2249569,3cf557dd) +,S(d0fd7208,8deac18b,1bd63fbd,6d5be0ef,5dcd34c5,538a9b64,ef43daea,ef321d1a,7a5e1e2a,1b290241,7879f223,8cc8928a,65988130,81366846,69841b93,3ddc3a1f) +,S(e856e67,e28330ed,77d8687f,bfdce5d9,ab8ec155,4c94ae0f,491d1e43,9361cb99,c51585a1,9da7c26a,4fc07c6a,f17999e7,a6718542,d7500fc9,1bd21fc3,8acf1eea) +,S(389cff20,9f240748,b9beba0,34d36e0,5e758a3c,95c678e,28cb0226,37db1bcc,ae5a0d3,db1e17fe,30daa2af,1e1f759e,905811c8,95706b7c,e5741051,2b7ac4c) +,S(28ce0f42,ff79cdb8,16b666c9,c3014051,d46e8df2,e2377c3b,619b78d5,17f21a1a,4e835188,f742237c,fe581edd,937b7294,e06fb295,d401b35f,5b7b293c,6d57f875) +,S(3d955bfe,5b27beb9,68400d11,3b8666e2,63af6ef0,ac6ee49e,8628e2ac,4d1e69c4,1fbd2aa9,b6913655,e00f1384,a15a7215,d3d7340e,4891893a,a67faf39,88801e9e) +,S(53dcaec1,2dd48ad9,ee65c9bb,ef21a023,58cac930,316263d4,9d607074,8aceb771,5b976f2,411d7624,d4659e5f,db624b05,9dd5e06e,f0b65740,ba394020,b9d279dd) +,S(ab822568,f7e9da8d,3f1220e3,24752c80,b02a8124,f8d9f65b,3b4532ec,3740881f,6c2152dd,34451ab5,b47b7c08,f7edada8,f9f230c3,5fdc2de3,35cd93d9,e25703c8) +,S(36719c40,1518b976,684bbdd9,2a923485,ce92b321,dc26534f,e5a0473e,4c873c30,d5d58a83,d493cccd,d16a1282,68aa93e4,94ce8638,41ec947,930c72e0,887564fd) +,S(360c82e6,18a53dc2,e44677b,a0bb8c2e,2a08f6d7,afbe8498,15d605c3,2e5fa969,ade18222,e1d0fbe0,a31c55b8,d2cb947f,1fcdea11,1e08ea3a,4f3dff02,e3e38b70) +,S(ecf50737,25a3b81e,dc621bb8,6c98fd59,f805d6b8,2f08380c,64270df1,95f020f,f5431b8a,cba5d27,1da26cb4,fc3412fb,14ccf088,6e0e08ff,ce0db47c,6b4172aa) +,S(3b751389,e6dadda0,85298860,76076249,46df960c,e030caf6,91f91d46,4d05be4c,244b0028,48829fc3,824dbe61,168fa1c0,3e4d3835,d3da3a53,7c729f0a,191e92cf) +,S(ee6e33ee,7fd9d7e7,aab10b8d,2c58f2a7,26ed307b,6fa69b3b,f1f08a74,f1b04ffc,4233398d,6ee49505,c5f21525,6cc9112f,5dfdd7e3,4bfb9a8a,476b3cd8,2645b8f) +,S(e7141075,e20f0903,f39fa384,f91a5708,7f24deb3,68571939,572caf0d,2fb543f9,5265e24a,bd84336c,26b4607,dfa25c44,49c4693f,adddca01,599d814c,21787cee) +,S(d401b043,ee9944dd,7429a8c9,4291dc55,7e5827d5,88375dbf,1513d394,a778826a,9ac9b417,5ac3666d,7c6327be,5172b8b8,1dbdc96,eaf8578b,76f01128,4434b937) +,S(d48c45cb,b90394d8,23574c16,60898091,75d503b5,99d936f1,f60246d1,79c9cac5,4e6b2bad,a67ebb7,a0743cf8,52298052,2737f0fc,6cbef646,fb270d44,2a285612) +,S(19f6446,fabe1899,ec4476ee,1d61f6af,961da42,ada1f77d,2ab41cc4,28791c64,7a0aa5b1,4f2ae030,d9711963,bbe160ca,424f6a7f,17b6a709,b5f0dce5,af1d0498) +,S(2db1d56e,f19a848f,6d6747c6,82cffa47,78444b3a,d57ea076,b0560b1c,4e57c83e,701a5894,d1d6b6cc,b421b168,fd34aeaa,e85262ab,44f70c65,b8ff61ac,ee541cf6) +,S(443597f7,9da3df8b,5fddf8da,3bc3a060,83f2cb6f,affbb1ab,8a12db92,59661adf,31b2c4ea,362a0cbe,2b9b1488,719b7ac4,3a85f7c9,63e9f2bb,b46ee055,71e2ac2) +,S(e49ad52a,7bd2db07,d972def2,d1ce6684,69aaf6b3,3c7e3427,52d79fa0,9d2ee9ad,b2063f37,1ca78c2c,429cd401,c5e46d6b,dcbf373f,5cb77163,5760b8ed,b4f91ba4) +,S(35b3fc0a,4a625cb5,808e1c2e,73c962e1,6ab7dfb3,28109ef,fdf81e15,841afd0a,cb558481,145a5f75,b639133e,8ce42c98,d36d8fa8,5348c815,d92a2c8e,cc993bf1) +,S(1cc238ef,3f64a075,c3c264b,ed42a0ac,aa6de195,7db40d52,86c482cf,f62f609a,d3382317,2b8a1450,bd918aa3,c57de76d,3012887c,98452f7b,c1f58610,f528af49) +,S(f5a2794f,48885192,dec727c7,79fb0b6f,c2cdf399,4ef7cacb,94e35791,4d138e84,d3ed4f1b,912f5dce,5a0842ff,d2e671b7,f3ae8448,b1a73f43,249a1acf,915b9c34) +,S(21b41016,a8eb7c6f,5519d94b,adb0fa7a,5c553822,6d080e3d,6d2956e8,8a9579e9,802570ed,b9b01b1b,ed79b6f1,76ebc4b3,55dfdd16,f43e82e8,7803be0,fc44e274) +,S(9b5611b7,938ac10a,9818bda7,3615a47c,1ee0f072,75aa3dcc,b8919dcb,e7beaa88,89787c01,d088e3b3,603047e5,2f49981f,7f007c8c,a88502c1,3bd9c327,e3c83b1f) +,S(28163c9a,b08a7ad9,38c7b35d,b19e700b,3865800a,55a3103d,2213c9ef,2c03858e,661e089b,df356bd4,afcd0604,fa5ade9e,74c5663e,77ab57f4,a069bcf9,b8b227bb) +,S(bae9ac08,aecc06f0,ffb8540e,2589a32f,11d34a7f,6d899e5,7a8f5ec5,bcf1601d,f5b63d9f,8a5d23b0,80d0bb6c,b8df736,aa660a3b,6f987683,cc0a54e5,f7d1b4d4) +,S(19f48542,3ed01783,cb83d2d,2efd7264,a10c6c23,1f5a242b,112eccb6,31d91f7c,79ce383f,f011ce13,87537ebc,3e70dcfe,2cc2764a,3d180fdc,74a21062,a5b75f50) +,S(cf459ca,cc8c4ff2,c827efae,6593a52,838658ec,bf1a78f4,75965856,a8ad007e,786781e3,3279964e,da5e6374,bf088fa7,5d18901,df01a0d3,6c87b62e,b419dca0) +,S(c19f2008,e6945d10,471a13e4,8b16f3a6,381de3e2,b24f2d92,77ded25e,930d1f47,7179aaf5,322b51d8,7758219f,cc30e9d6,bad5ab77,b35dced9,6a513b4b,fa7fe147) +,S(3fc1b649,3b6cbecb,1767d0e5,6202c84f,39a6b41,87f05ac0,d659c45f,5bc565c,db9edee1,1768e29b,4eefddae,c6234622,55d06e0f,1979c8c3,5c366f4b,5cac3f5b) +,S(502f74d2,cf589c65,e63b2a8d,b2b1e4ba,5f851715,bbb27e8f,165fd34f,29cff7c5,755e6c73,765bc45a,c2f9d4da,499198a7,36743571,40846ce3,6afd8063,981a96b1) +,S(b9eb62bd,7cc6d1d8,b0eca2b6,1920be5,9227534e,7fb581c6,4dcec4f8,f1ec44ca,ac567b37,891e3eb5,c40fecf2,d4c5b106,bd222c8b,64ab491,f7a028a6,a02178e8) +,S(2e5280c7,25a109f4,c5de565b,9fe5357c,718f9fea,db53aa9c,b07a0f3b,f5030ad4,4783be94,1b162e,79f58d4c,448756bb,8ea18c4b,692e236b,4b2b3ffa,24a456b4) +,S(bd59ac74,92d24606,9b233efc,1b68c371,bbc9713a,284f7f0c,699a1d5e,ccca64ab,1abc71f2,fce8372a,a4a16abf,1d75bd87,a19c08ed,c8843f44,fb71bb7c,a74ec4e3) +,S(6acd12ba,4623582d,d769c8bd,3d6adabc,2c13ba3f,bb67e4ed,ee0fe70f,3f832f5b,982fce11,ff1e6c05,2f21be20,ce1710d8,5cc043dd,eca9bc43,5d100d9f,f68150b0) +,S(601f4285,8b0cbce9,90500c64,e7eb77e1,c433dca5,55e6ddff,5264a98,d688decc,56110eb5,d5b3ed79,5906ed4,c0275827,79cff7fc,ddecbe7c,74d2bf10,ab2beac1) +,S(4d393daf,c47391d8,8208263d,b8132156,e9fdd6b7,da48eae,90489d12,832dc938,a14180a7,898a5a00,6ae5b8c8,d034c233,93ed2ef4,faf6a4fc,42c4df1d,62d0ea7a) +,S(4ea24f6c,3c164285,166bf2b,1918b3ee,3cac8631,5a1175d5,4104506f,68b7171c,87da621a,a18d1a18,b1dc222f,af37b5a0,b149714a,e755ef03,ee3af91d,f2a7f5ea) +,S(eb1f6f2c,a5749f80,b88a7359,f0b67230,b2b844a2,4fb72dba,f5604e73,cf6e432a,6f58093a,55e3cb37,8830e58b,10612b24,56a35fa4,b8e60409,1b9b8304,e35cca51) +,S(2361315a,3d41b82,98a9d768,be7f453c,eeb977af,bf1db132,c831bc00,aaccc9ab,90df3be,336d4b58,7965778d,9300cc1b,3d954d4a,89abf173,a9bfd99c,29492dbd) +,S(44f432f0,46c7defb,630d5535,aca1bd47,10505012,6b2454b3,4591b079,7f8e9b28,1fdfbcb7,25eee981,c19f98b4,6bd19dff,9b6cbdab,730ca2e3,1643d415,e3aa0489) +,S(d563a475,4c130e71,81b60a66,a3f2ee52,7040f9f0,eac9e26,866fb226,f93db593,49fab7c9,6e320961,6f4610dd,532d547b,b24f7f07,f916f10e,630ae5cd,b40d3b11) +,S(2fac4651,f0ac9876,4a74c066,16e7f1b8,aa6d2bb2,99ef9f0f,43bca81e,7a4089b,fff2b7b8,c330e1f9,86a69324,a63bf10f,fad8e78f,dfbb6062,c96a67b9,eb460d5f) +,S(b6f60ef3,d8b793d6,5794aa89,12e8e1a0,9ef7604a,5c9eaede,b803fb31,e8195dde,b9b17946,4057de11,9d0bf993,fe92e4a8,bb633d6c,d03ce8b5,10200166,467e6bbb) +,S(d08c6813,65789ba2,4e580637,2eaa1ad1,d1254462,3cb64d83,5ea2e1d8,6e34620a,e94875ca,f5beb1cc,f88fa3c5,e2776203,a6f3cb5e,b4803618,52215d0a,9076b185) +,S(dfda222c,a4ddbb56,661f56d,b9b74be9,a5a7c34c,4761483d,928aa48,9bfabf52,bfac4ed0,ab4ccfa7,caf64d8e,ffdf1aab,6aad50db,595b1b49,7fab28d7,656b6475) +,S(3f72e4cc,ef5c0bbf,995bf8b4,67759969,d93ee40,8e738a4d,41f55896,17f241ed,c42ad857,3f49218,c7daca7c,8ebc5c7d,f9ac301a,2aea209c,ab6648f9,8ea16f5c) +,S(6e85f587,91431b6e,dd848aa4,70101103,f63881d3,2fa9f1c1,e3324161,e9ebf0ee,f7f00d38,31e09d69,f0684107,59dcf837,eed3d833,90efa352,40a9ac5b,2daa2a57) +,S(d991fc76,2f75235a,5268a32d,64dbeac1,b2333a34,5ad552a4,cedfab54,9b191f14,90d58a07,794983c8,3f6edcfb,55ed0b68,8f43d9f5,eb7678d,29610299,63c748b) +,S(24cd7922,7ef207bb,6b9b678,5156bf76,77101e6d,f5fb5576,a9d33175,8d7c00,44608974,5f1157bf,9c9f45a1,b212a978,8e348a0f,55129935,b038940f,84e91468) +,S(5c053ed8,367806a2,8545692a,47314850,66910d02,3373b89d,e85bc7a1,c25bf6bd,9a3e45f8,dde03e64,6d6c546c,cf4c8fc5,a253054a,2d6df43b,d8c4afb6,385311f2) +,S(4f3a4d88,20948d82,b598f118,1d350719,ffaa5ce9,9a7f614f,a9e3cbf2,e021a6cc,149c6722,980a41ce,5d4c0b94,9cbe3c99,4d94f2d7,c36ef45c,d7b9084a,79f451a2) +,S(badbce64,8d55b579,cf8d6dc6,74580211,a6bff856,10804077,adad7c2d,238aa542,567d75d7,1570d7e5,184f4f4c,a53fb92d,654b67d1,f6bff96d,fddcc3fb,932f0343) +,S(d2eda540,5f828eb2,cd3bc5c1,9f9a838f,8a1186eb,3cbe9575,5cf6642d,13601942,ecb3dc3,655ea992,4773cb57,f4f4c8d,6380c665,69f08184,f521dada,c108629c) +,S(191ad0df,31c7f3b8,258e5bfc,ec9d1ff1,2143b240,24d93a17,893da095,97304f1e,4ad4991,6bd2296f,b6f6b697,dbf5a3cc,4c3ba136,6f6dfade,c859b190,48fb523f) +,S(f108bdc4,a1874aff,35bd1fac,b8e81b5a,7a7ed597,e78dfb7a,c716bf7e,7a15dcac,b88e4fa0,d630d92d,bbdeda05,4a6c6624,e7bb1d5b,a6dded85,347e5275,e08c9414) +,S(30fb7605,7e2a6cdd,59fa279b,3574cf1f,db9d5159,19d0e523,990be02b,da1ebffc,5743e72a,752b558,1b959a48,3b6229c2,33fa1b3b,2719ad3c,a6f8f9ba,1702ae8d) +,S(6facb1e2,6d1ef1cf,ff413145,63f3b2e4,e762ee7,3e43378,c4c15f85,1cd948bb,92000e2e,a3a5bdbf,f6eaeae8,8619f2a,48b649c9,19972b7b,83cbac22,62d484d7) +,S(741c1253,8cd4da52,c596840f,e6233558,fb480a0d,43232756,2a9e2d59,a574b981,79040b6d,580efd7d,84356117,a93941f6,b47e6c0,a53627e1,bb80fcbc,e6881234) +,S(2b86fe60,2b2e87f,59498185,4af1f986,7debc5a,84ae8bf,94d2d6ab,4ec8be09,cf4c27b6,b33b5020,2ff7fdd3,ff0743e8,f28ca75d,3cb74d21,ecf21dab,ad7ae079) +,S(e6e7b676,9f0ed86,4163e9d3,66a7e2a7,a37f1ee8,e827b105,3119b8b,d36e7b84,c2e5b9b1,d82b4504,454062de,6c24430f,53e03bd1,819eb1e3,2f24606,73d09be2) +,S(86600d22,e7bec946,5b0afe05,5e733b03,f8f81548,ea44e008,740ac807,ddaabbe1,abb367d0,5e6d2fd0,a2c1d0af,5ded2926,1439c985,ef287860,80614bf9,8e8e33fd) +,S(67171261,5cbe2da0,b252a773,58f81c91,fbe561f4,87df09e6,cedc8a55,cb3414b2,bf02d81d,10a47287,2b7e65e4,93d97cfa,e3bd6ba4,39977371,8cde674,8829b0a3) +,S(97db468e,7f3a25c4,c34e202c,e9b2480a,4344aa9e,1b76a147,4bd375ed,6d10c0da,c58ceb67,a28f99de,2eca70ef,e8af24fb,c381f962,d5db6e3c,481cca6c,469c4c27) +,S(f3ac0267,ef19ae9d,762c4023,4ba97e33,a88b015a,e6a83f4b,cf730d84,405aed26,cfb8a3ef,d1104873,8c1c927,b31ebd05,105bcce1,d57c7fad,1972a5fd,cbe84f06) +,S(fa44128c,3c258fd,5a99c85e,775fca63,4e8a46d8,4fbff953,b373bd50,6ca3da85,f3196e74,d17cae3e,3c9958cd,ca591374,ddffcba,7e362139,17e5e119,a5538b1b) +,S(95244462,1eda0b2d,acdc1a57,41725c1f,a7c5660c,feafe18d,ff8789ba,164e7dbd,214bb6b1,7af1584b,76a9e5a6,2c32eefb,497324d5,7b00f71d,24b162e7,1383bff9) +,S(11de80de,c03a732d,6769e52c,64be4d2f,fb1d696b,2a658721,94aa384,392dff9,e1eb8932,42386216,f543eea,a2d1099e,531d8e27,7cb2e5fd,ec76b3af,e9e9c7b7) +,S(7318b452,a2b9d23a,eeb30bb1,3883dff5,b373be94,94451c72,36a2c02d,a4c37e75,26713361,aaedfdbc,2966fd1a,ecfa705,772012b2,564b077c,7f5db7b2,c810861c) +,S(82d189ad,e5610825,1b03cae0,4b138ff7,dd2d77b5,422535c5,cef9be7c,1899cf75,3634267b,1b3c3de0,d2dba36b,924949b6,7e05d790,29c9452c,18c6ff70,252d570) +,S(e5dfe995,82152513,80859865,f16fd0fd,e4d60c7,be0e2aa2,d9b2af4f,3714f8f7,e1d87d8f,fdef889,52918e62,5bcc90e6,83f337a6,103cd000,1cfd01d1,eef4b9bf) +,S(54cdeddc,9fda5fe4,63240f72,8ca66c34,ec05394,899f2472,d82d2378,28a06a3f,27bab620,cc373ed,10a71c18,345ff27f,e20e1bef,98052afc,bb45fdc1,80b94a2c) +,S(7f96757b,8b473785,e1359cd5,bffbce2d,5b26e8fb,579d7833,607d58,a791c47a,d4b43e2e,20a43fa3,5a96d1ff,93109aa0,b7831fdc,2d2c016e,181beac9,c1cfcc4c) +,S(eaed18ba,83238fa5,ce4e6c82,edaef108,b1f9929,3148f3e6,2d3a6d4b,535455be,1017291b,805f7b1f,eba367f5,9a1a10b5,f7501355,7e6eb921,20838c50,aab0d29b) +,S(91ca8ba1,da9c2625,66a135ad,81f57b10,65ea4632,5bec5d42,ca1ad62b,826f04fe,2d5d619e,84fac3c1,e30343ba,a61d3bfd,e1c4a9a9,8d968e92,d560956e,138e34ee) +,S(8a31ad57,12e32254,7d0d2907,cb73adfb,a9a1ed5f,fe98d645,22082e6f,1cdb1364,8ecc3fea,886737f4,f94a2984,e6edba9,8128b2d3,bc79a209,2b2c1a7b,b00bff9e) +,S(123acd6b,ca673170,400bfc4,1071cca5,4e03daf3,e1ed1458,88470c04,568b972e,e97dc60e,cfec2d5,c6a6e173,9d60f7e9,838ad723,ad0fc7e1,52b5e70d,b48b853b) +,S(d7c26043,ad647f0f,d056c10b,4ac932c6,6a545bbd,509de346,d4391942,52e69df7,6bbf2f04,2c9459cb,c72e6aa3,1b6d8e4d,e606eeb7,d5d4e0f5,7f729a79,ab1f5e9e) +,S(67c4eb00,548e2cdf,50626c8b,1aec1173,8370da41,9280c9b0,1c8783ee,91e018d0,ffff201d,91a4538a,97ceb50b,c9aac646,bedda1d1,3f22a121,c709bfc6,23b95a67) +,S(7e1264ea,486b08c5,19c7d8e6,5c80e1d4,d2994bb0,95185c72,63172b1f,ead93428,1797609c,816f10a2,e82afe39,65112a35,798b0874,b24503a8,f57c983b,6ce7ef6) +,S(912cb164,5f7d7bfe,5ea443af,1c458500,a95ff22b,212109e3,12fa01c2,41319cf8,bab658b0,111c48f8,a2f620a5,fd130d53,e1c70e7,62e33dec,55b22d87,38c1eb99) +,S(1d60694f,15577a40,f6b9f299,be5eb62d,251f62ad,b9b5651b,df804e27,df919fb7,3799eb92,e18335b0,bdbc29b1,f8ce07ff,3f7cd409,5840d5,dd324bca,2f902a14) +,S(8166b50b,811bf53,4a94af10,7672ab4a,4e154b2f,c5cebee8,c8ef6ebc,18cc3906,6dea31f3,343e5b45,f5b08c19,903491f9,349f4ca,29818fa9,16f5e02d,85e09ef) +,S(4852393,e42d0821,6664d5d,f85eee60,9660eaf9,54ccc6d,fb1c31f9,da17923,e4cadcab,17f64d8a,f5dd74da,eaeab7f7,13be1de9,600d4db1,a781a058,5ed2006d) +,S(f40031b5,ba171b26,e02be47a,c34173aa,18ec3850,1b1d529e,a922a418,1c3491c9,c2db89ec,2e50105c,4074b729,a53ef0a7,9371d2a3,354d2d23,8fb39bf4,bcb5028c) +,S(8926d233,3c842f9a,213547a2,fc5ef5de,b0bbd603,e69d347c,41e7d49e,aad76cb2,8459bc10,b7b2617,b0d434f4,b62a4452,80bb951e,b4ada14c,fa800389,b3ff892a) +,S(7f362b16,9d9bd514,c6289cdc,70ea4b77,e7518db6,a2e33acb,8acefcd3,d8c5726d,dd1e69e2,71d77dc,2629cded,72869345,7c63ea2c,8636c712,e45e6af0,4c15fe35) +,S(261b8037,98ca780b,50dfd56b,81093f32,d7c348e,4f093b91,70470ccc,4516ee1c,5723ca6a,5959d589,4a24c12b,5f87a5af,84c7a6a6,89304000,344bf5d4,81c7752a) +,S(d137c0e8,edb32642,12f6b958,8b527a9e,9e57f3fc,c358a2b,68f11d61,89fbd900,d1043b3,5f809513,ae370588,4adf90c1,9d164c3a,8166027,92ca55df,74c71466) +,S(96274665,253f4479,753de9ff,ac94af8c,943b8231,b1302040,bee72240,2ccc445e,8ef6f9fc,f1e21c2b,d913f877,98c6ac9,f53445f3,6c2b1678,a301a4f5,17867296) +,S(bc6ea584,b1f9b006,5f39226b,e31a866f,7f90229e,12aade70,136dc747,b37f70c3,23f1c4eb,99dfbcbf,66539762,4d20c4dd,4414bf15,172cde8a,b7652ad6,f87ad5e0) +,S(3e968741,5adc1afa,696e3222,fe351939,1d3f19b6,feb1de83,f4f1aec4,d25e2387,e3f4de27,67ea84c7,8baf1382,4b7917dd,477bdc87,cf1cf38f,8f5da1,e096b528) +,S(b5973535,c9c9d4bc,2b2a9b38,57cdfdf6,4995d64b,81c9e68b,abe4d4eb,4eee489,907cff05,5eded925,770280de,9ee0e025,1629796e,8ca05a47,48ddaf98,72bfdc3e) +,S(d616bde5,acd5f345,d23e8b2c,3d95038a,a1d7a354,a7613cac,57ec3e8,d07e11e,6c88c62d,d795d065,c6c86478,cacc56d3,2282b4f8,f3059aaa,55388555,449879df) +,S(540345c6,5869d3cb,cf4099c3,ba7422e0,feb87ab6,ad67429c,2dc99840,e6bc82b5,9f424998,bceafc7a,a58e5f16,c58e53d0,c0d51f64,285b0afe,e84ec9f6,b22f8f37) +,S(6b7893d0,ace6ab5f,c93f2e80,65a3de57,4d552e5d,4a89d660,714113c5,a256fb84,81a016a3,7be94a2a,f45b1bfb,968006a3,58398fab,424811f7,6765fe75,d4438d5d) +,S(357ea5e1,7a0795ca,cac47e4b,9e59bb24,45d1b6cb,2c713f04,7029f6de,2be57734,571de7e0,27dd05af,90bebc29,bd7345f5,77babdd2,75498bb6,d43a668a,8a4d6206) +,S(7baa2739,1db2a0c8,679d0e18,8c5e7c8f,ef1814ad,b7bb3955,519b4189,1a63ec40,73b56168,20fddbb5,749e8dfe,ef834f74,e266df86,e34510cc,f5eebb52,a868075d) +,S(4d5251b2,dd778e04,5143169f,8cab93b,9a9b7428,1a0ea1ff,964225a8,10b1e578,9376395f,2091036,16a95686,4e55382b,39b6934,2b2964d8,34face11,a44f491b) +,S(3c0fb553,b0c6330a,3cc06bb3,b0cb4c2b,31be7582,55264b29,98bbc166,ff57f7ca,f1f409bb,cc227824,4dc08c3,147e0b83,3522da9,6859400f,8d19272,e33ef1d9) +,S(6dcc7ba0,4b3b0827,e3bcb462,7b7db8d0,e0997c7f,900d27de,2d6c87d9,aab2c373,f2dcb11e,87b69faf,4400e9b3,186eeb6a,4fb48cad,6d6da276,3943772a,4e18b450) +,S(a5271e35,ca54c1b3,16d6113a,40414ed9,26bade46,c3258c6e,82a63e9d,927ce06a,e2d6051f,bf42dd9b,e0b2e00,1fa4e308,66232871,313f9c82,fd6ee37,6e8e376b) +,S(574d1ffd,976c616f,60371e4a,76b2712e,84b0a231,8d3c1c0d,3b56b10c,e978efe0,13733a17,a956efa5,733dc130,cc4c848c,19ecd6f8,7fae5ac,82c73de5,6d1bfa0) +,S(ccccd911,ea84c247,262a6e61,8028b2c1,a147f55f,b637b570,97400472,fe3e27ce,cc4b4f5e,491f32aa,eb6d0404,5965a97a,e4119e73,33bd1b39,f2ea9873,7d8f33ef) +,S(df1e4fa5,410f1438,9a41ca26,31eef376,4f10868a,f2f05a1,50e2c7f,8c3a2643,8185a88e,2b4a343d,bbc10da,723d4cb8,29014cd6,3211a82a,63627b01,1b01e87e) +,S(2ca6b7e,b53516c7,2e216263,213c0d60,fbb02f,dd227f6e,4ad29069,f6da9213,81d5fd1f,5264af85,53a8227b,7500d82b,84ca1fb5,87ea30bd,ab30fae3,67e3d7b3) +,S(df4bd790,ac3a0db,1663a8fd,aec1cdb5,b30c7a67,99ac2ed0,16f1c474,5bea92a1,db80f378,f33f920a,d0f4fad9,595b0655,44ffc36c,ebc3f294,3db13b83,cf609bbe) +,S(792648ad,8c053cb2,102dde19,789d3782,79e0e0ca,c500f40,d46bce68,97af2839,3c2d9b55,afa4ba1e,1ce3d142,5e17ca6f,191b4013,7b7d8162,8de35e8,801f72f4) +,S(dc6fe845,50fc1136,1f648fab,deb9b3f8,e7fee837,ea545228,b2fbd432,e53a8aab,2a4bf746,19fc5408,b4bc6421,f4a570aa,172ef263,c890c853,d7b06aa0,65960dff) +,S(10d4786e,9151f2de,89f368f8,9502b7fa,74b7b9e5,7a1485ab,f82fccb4,9c2deca8,15da7f51,bc84f421,2ce948cc,28663e57,8b4b851a,f6533f2a,b548adc8,147d2243) +,S(67c7c0ae,e82dfd5a,194dc416,41388947,29ddce96,5b7d50f3,ca922512,6e730bf7,a8bf40e5,666ba93f,1f723414,24d8a7a1,6a2aed83,7f022fdf,557e6082,e3e515fe) +,S(602c3df0,8a2efee8,329f391c,a9f7b2f8,31ab6d32,bc4623c8,92272934,1f78a4f0,ac17903b,3808ee52,c0208342,696f5250,5789206d,515421ee,8b1ab28f,5b788eb6) +,S(c3441893,e78910c8,3cf917fa,44b608b6,671ea508,37a7d2e0,4e658c6e,2a91d9ef,b5388381,17c264a4,a0876c3b,7c261bea,229c2d0,a7399c05,114f520b,a7185dcd) +,S(49dc68b4,412dc3c6,c4b265c1,6cde755d,bcd49f0f,394d552b,76f67ff5,33c7fddf,f1490a38,cdbb6c88,d270eead,7de6358,132cff29,ddb6aea0,1baf6353,4c0cdae9) +,S(89080197,4a95beff,da864c28,36997c7d,3a4c0fc0,4ad59eb1,5ba4b124,702d0f04,86e165a3,b2e7eb55,9a7996ad,63265609,aa6e3385,30b51d22,4ae4f16c,d30be3c0) +,S(c9671a3d,f749d635,5684de03,1764cf19,1087bd06,965d23bc,4ea0555d,29ee0804,33cc0d4b,b02a1910,c158db0,ad2bcd66,c9585f9b,456321d7,61752148,e6224d85) +,S(f3c1a76e,1d4ce622,dc667a4f,dc0f4ede,7eb7111a,1539ce5a,bca71d2,fd48a26c,269ebdcc,e1d42428,4e0dd49a,73968676,58857998,7a062320,6251ccd0,ac9f9e2) +,S(12002fa6,68924900,8a540429,50ba2901,5870a70f,7fe95ae3,dba4b07f,aa6fa460,51543d34,34568008,74e8b1f6,6ec72fd8,e2936b6a,6022401c,d13d50bf,931996ca) +,S(a59df11d,d3fcf1d3,264028b3,6e26dcf5,62083e44,f91f3d27,f23acdcd,f1392a9a,4dbda718,48afff3f,faffaab0,5c5376e5,5fd15ced,68b03ac4,4785ab2,201cbe70) +,S(89f738a9,802d7deb,5cf2895f,60916fa3,d6d74bf9,e740d2f3,13ba393a,b40cacaf,293aa5b5,b03557a,e7288066,5ade09cd,ae6abc71,8e7d445b,cbf85a44,a035b73e) +,S(d0414978,6252c5fb,b4bfdae7,56092769,dfcdc2f2,ae38a5fd,92ba9df7,f91a9ee,53ffb526,a24df2b3,d6e8faf5,ee6d79dd,7f427d8c,14d2d5b5,880f1863,e7e95099) +,S(4a638f24,ef3528c7,a4e42cdf,3f5bb946,81e6435,ba2c99d3,625dc282,b941d341,39d97ef2,949f1338,bc016ec7,89585780,832043ff,33777357,9ecefac0,ae40620b) +,S(70a25f6,34d32af1,47c4dd06,5d52f78c,eb5e53fd,64bb151e,18e8629d,f4f8088,1b6bf3d8,15a0f919,68dfd6a7,7ab7417,d4aed9c0,a52b93b9,9b4e4e41,d1e558ce) +,S(59ea9490,24765add,f99fd908,11c65ac1,d0f9ee8f,27e459b9,b5851765,8e96ab82,b38bc716,896e0337,e4a1b361,59e2b5b4,e1680ef5,beb4b47e,c5934b4c,8d6675b3) +,S(874a6ecc,6260617f,c6d4f334,40c8e080,3d993594,ccc2d47f,be2869af,9881f2aa,f509e39,6663327f,7d768a67,aa0d2ef8,353b613c,bb60fc38,bd2715cf,349c02e) +,S(dbf3567a,1e634ab1,63a860ce,244f3046,c7f486f1,b12928d3,1c44624a,48bb5df0,bcdfcc85,38e4702f,99f9d55,eaa55216,730405a5,66fbdf07,4c62b310,6baf7ce3) +,S(a534bcb1,96450965,5593155d,2da069a8,4800e4e,eca0b504,62d844eb,d179ac50,f86b38e2,d558be4a,b0a216a2,906099dd,f3cc16b7,30e784e8,745f39cd,829ff3de) +,S(e11bb840,b79063c,4e090771,29d2bce7,8f291f10,120b57ad,63594780,fd1f1905,7fcac0ad,e0cdc573,67c9d74,1c6c9928,15b7c3fd,3a19b266,e0e5a94a,9a9ef9cd) +,S(d79bf0b6,552c5b6e,6e9192b9,765e687f,ea226d86,c6f83f44,b427ea9f,14b404f8,54e7a54d,583e2cac,129f17d6,835597aa,934b4732,6ffecbfe,deb503a9,eb9660ad) +,S(103396a6,57856b61,5507969d,958de14c,251bbbd4,9851a5e9,ea64e0ae,2f87817a,9d66ba9f,cf3e0c2a,655cabd3,91774015,e209d771,334c461e,d27b683f,f83b477b) +,S(9b49e25d,ae607f4f,fd876837,5ffc0fb7,142f9cb1,f0fde836,537f320a,9636723a,3472462f,f9fa5ab3,1fd1279e,7953dbc9,85623021,e714c951,bf00f632,e9f72ab9) +,S(a017d4f8,8e757556,b7ee81b9,d4c9b260,ba2f7f8e,5e1b69a5,fff94abf,723cc438,5f2819cf,53fcc35f,c9c15d2f,13dd51e5,2c8dd134,f012aed3,4b4383f4,8b00cd95) +,S(aff1db2d,3fa6dc00,51a8723a,7127d57f,69ed0b49,ccbca35e,25f72bd,19076211,7cccb7c9,4adc2075,9c7f8870,fbeb1a1e,9f95d644,af0411be,a6b12d3,aa6f00d7) +,S(21ccbdab,f6902a86,d368d445,dfba0f31,4f80de6c,c7f05f7e,9fd1818d,45a174de,eda0b8b8,7f40e9f1,bdd5ded,e65bd72b,6cbe049c,bb1575c9,6b779818,a67251d4) +,S(c3e58ebe,36b0e5b7,2e15c47e,cca6848f,5312369d,d270dd18,93b589b0,56ff7082,212df92d,d724e3aa,c42e293e,b1a2de5,3c340712,7ebba791,b901ff87,9fce94b0) +,S(f19961ea,da1d77df,2fb9d976,7d7c664e,c0400066,2ecd7244,12b72dc3,c5de133,74f321a9,1287943a,8ed14d02,96b05ada,1a24fb52,a09d467e,8e8175ef,8f04a2c9) +,S(3aecbe48,2aaf6af,35e63dda,fc919adc,f974e89e,765c4437,69ef535e,cdc05e01,92b854b9,30e5e4a8,85397592,fde89d3a,601c733f,d88d230a,a1168e36,ee367273) +,S(55fdd639,b87de7c3,9acb95ed,33222935,bd4217c0,fee1c924,f1060f54,fd22055d,81f4ef46,16e51585,cb443bce,f16ad8e2,36d6ed18,87ddfa60,ddba2089,f4b83c4d) +,S(b86a6272,ceed47d2,4ef4cc67,bef0b117,c0761b4c,29548c8a,367d8ae7,bf90744,1c0de18c,d5f3331c,5439020c,5964404,be582f88,3feec4c3,112396ce,b657c4fb) +,S(d141a7ae,9d92913a,26deec5d,d2acb7a2,115da997,63be5a76,c5a0a415,8b5b8170,de784bc0,9308c82b,ce3be9c8,9abe41a,4ed9b9dd,cf817aab,3785e344,dfdc2ecb) +,S(af340559,6902ad5b,e3fe3464,3cbcea5e,7a478a32,b1bde828,ec966837,192344e4,3f65050d,13e7b969,35b013c4,9df385d9,833d419f,aac0f754,5c395ca3,75ae8965) +,S(b70dce7f,870d3d12,472d3c5,481a1288,cccfdb2d,12637a9d,973f4e12,b026cefd,4b4f733d,e5322132,80a36e4,ba57894d,a563f4ac,b1a55a9c,83c8dbc8,96a7fdf5) +,S(14fa3dee,44dafb00,d2e71c91,5d217cd7,a9c2b50a,edcd0025,f7aad2d5,b6c62ce4,67c3a65a,65bd4292,d271fe13,9a8c9d84,343bdf68,cc16e50,11676580,6bc30d8f) +,S(baaa6a53,26a7fc36,e519d9a2,48dd3aca,f6f0628d,a6406021,caf0d0c,56d9bb3d,4fe9c638,c1078c39,a0fc39cb,e9a81bc3,4b15840b,631ecbd3,8ca4a2bc,10d1f3a) +,S(ffe2afcb,5e53bc7c,9ee07740,c8094a3a,32c8e627,4dec7eb8,cf2673a2,dba11725,68d7092e,2b8b56da,1653dfd6,42de49c1,799d3ad5,864e052c,8829815d,b2ad952a) +,S(a25d7c5e,83a0659c,5ead610e,b581d325,adfdd3b8,af03736e,4039ee1f,40612431,679ecfcb,b1da355f,d73d02e8,b79fc949,bdc9fb5b,5a4e28c,5e68c244,7599c806) +,S(1c5d082b,3121c4b0,2e0e2244,88e53b8d,ac5500b,bd396f0,7fc218dc,7311b299,a2a435af,4838d028,4d459216,f3955427,59f1f51e,9a15f28e,d83987dd,9c09e5b1) +,S(d8d21200,2cf2ceae,6c9a90db,89f7d0e0,42a2e97c,458b9024,437e28ed,a775ca26,b31b6a02,4b01de6b,6159ee51,3c118921,f61a536d,c1442525,cc613290,364b9df2) +,S(b5265664,54579771,947bee20,f8e7bc48,1e15d07,2adcecbc,3aae7b91,a4d0f3b3,170ad05a,da7f67ee,f7c611ba,3c55c835,f054bcf2,445e5c1a,399c7cd7,640ba8be) +,S(1d0d2a24,8982635,18a28203,b78cb089,acd5c296,98b86b7b,48fd4db6,3c80812b,71e2e1a2,7bff3bf,9e85e145,7f446048,7643f154,1cbe829e,f600d861,dbb752e0) +,S(cbbd4ee6,33aec586,ec7ba712,c3bbffaf,a0e6497b,8618a476,b64c660b,340046da,d792753f,c73090ac,177ec5e3,40aada73,3b31dcf3,a1f46952,625dab85,7e9b9795) +,S(73cfd295,99b6b80f,8757a873,ce9166ea,b1960f77,fa8f1d55,ddcdc90,9420a751,17832aab,c1083af5,683f8cf7,23c6376c,c22d190,241e1e37,6b1ef2dd,7333c873) +,S(20572bf8,abe50ea,37a064f,738dd18f,531b9404,e7e4d693,2c9fc34f,ef6dede3,41c79375,11c8911f,3146bb2f,63750887,c663d2d6,f3fbbd53,30af73c6,9d332191) +,S(ac208805,2f1518d0,3724ef5d,cb149f36,1ed713a2,94392f92,96173857,94794f70,1820bb1a,e843255,9025f3ac,977d9f59,4e812cd7,e27c390b,b2fe905,ae2b7017) +,S(33467f95,60aae872,b5c9afba,7f29cd32,a213f197,47745d8d,945d06e8,295afacc,44040bd8,138d9bb2,76181e21,79f4bf98,4b0c3965,a1a47d34,be8f47ff,52e15af2) +,S(1a77910,75bd3497,cbaff6a5,d5e8bd6f,439f5bb7,ca3eeb98,68033ef1,cff83f03,d8af802b,daeebe88,f823c43d,447f1906,63ede3d4,c4b9dbd5,caf82a3d,b4f1cecd) +,S(dfacafe8,cc15f85c,f829d913,9a9aedc8,6a98e010,5308572f,94c183b5,533f5bb2,c076b6d,c708d2c9,895e22f3,2fe42f4,33ad38c2,a0c55aaa,5c551fac,fe601916) +,S(3cd975ea,9eb5fc56,14955bb2,db0ca413,b83116e3,a09454dc,7fe973c5,7ee33ca9,261e5544,2fd503f7,7c1c7a49,de96fb50,aeac56d,30cf3ae4,8b2e1cb,1b9fcb6a) +,S(168ac250,d23cbce6,23eeda,1389e261,c56bc718,4234bebe,6b108bf9,bf6df45d,add3ffb7,b0a9a860,21ca51e2,f3d2e2d,19206bad,78d9e661,2895068a,c880fcb2) +,S(47bae439,31966a6,984241b2,2afc8b86,94db88c2,abdb1737,728e1af0,e3803968,a7cb7d64,a07cd13d,2c8dc7a7,8a7a144f,8edf915,5cec20af,102260b2,d8d355a4) +,S(76e73a26,730b1444,8ecf60e1,3489de8b,93355af6,8943669f,20da17f1,4f04d517,41f75520,3d5d528b,2e81bab1,9ce8031b,bf9c73ce,7927645d,178ad762,669b99fb) +,S(f6f08712,d85f9219,bb475122,1ada6a75,1a8d4758,f5dd72f,f5231d56,e634c19f,d72cfc42,379ee25,eb0fb134,338109a3,7752fdd8,1ad102b5,f365a276,1d133f10) +,S(1acac95c,9b0ed53b,e6c37d03,7d47a796,2a371caf,cede380e,799d0110,b0634f7f,658c6b74,44846952,93a5215c,63695a5b,fc80f818,633e0278,f6a1193c,6d441bc1) +,S(5759a8bd,ab76d449,13258631,68773369,5b76d2da,7b1e4004,1bf99198,bb25f218,15c1dc97,8ecff6f7,cfd6cbb5,44ab8666,83127b41,951c247a,f44855,4afde3cd) +,S(cae1e14e,c28ddbfa,379a97d0,5f3ae379,3771750f,5a9ec743,2039f440,8fa7e2f5,eef09268,6b9f60da,8e64ae64,8f13d01,8f7b9ef6,aa849f9f,72df2f9d,f80fbf60) +,S(9aaa132b,947da7e4,9c1de2ea,ce06e3f9,ccdc37d7,6d2081a,be5f3a37,9b0756a0,e9bee628,d6a64476,af601737,68829c11,e8646e8c,beba7174,806d800f,56f6676) +,S(f38b62b2,4d609d01,70cbe85a,cedde44b,7a766a30,d8b7db6,8d935735,c4d0d204,8b61a5f7,e3caa2f9,17ef9ce2,c5eb2499,667558f,7c4e4e84,2644dfdf,a57666c6) +,S(25c121fc,74c57f34,29a143,8ff3c47a,f06b5de,74c4dcef,4a093b87,3a2ab659,69254121,eadbd76f,289cbf3c,21cc96fc,436ea33,9ccbc8e6,fed92b2d,331246da) +,S(95666156,c40929aa,8a7ca656,c42c64d0,e24c3789,ad3781f7,9e2a0ea,c7ab9c1f,116f5750,7e07b846,92106d6b,6976741a,59bf7cf7,909733e8,e1fb54f3,fd77e783) +,S(cdf1c010,7c48b65b,c001df94,c6e84d12,d8885af5,820d65a5,4e7ffea1,ef4c0081,8f3d1be4,206bd313,e1c498e1,5e7a9995,64312ea5,aa823196,9c566349,9c3a04b8) +,S(955269f2,be0d5e20,5382b2c1,959103fd,fa2c2386,9bbdd33e,a09487fc,294dd1ee,6306a092,4ce63798,41b2d773,8cf97ca5,e259f81a,a67b1e0d,42903f1,beaa869e) +,S(78013976,449ddc23,4d32a793,5ea525d5,9afa3617,47e0a8f9,c8141d9c,2b231e14,8d28142c,bb88abfa,1a72d583,4e509d69,3cdee0d3,bdff4788,83618ce7,a1ace69a) +,S(e6d67a8d,fe513147,2c0fe227,bb08a8f2,25e2606e,23672969,ad3acd65,155f972e,46c8695a,5b9d931b,adb568c9,d3f9a8a9,dd501508,fed00c99,5159f518,bed035ae) +,S(1779ea78,eed6d974,563cac2b,b3e72693,409b7029,789e0188,d061a182,3f18e962,82590ffa,6b9e0e13,d2e72bef,96d555df,9f7d2b81,2423a947,e0dae41f,91cc0722) +,S(28ec208a,6e929f8e,a815fc17,7b927086,7d482547,53ba5c3d,3b56fa91,acbb67d8,b4453dd2,eb964348,94cc1cfa,25616a2e,2f0803e2,8796206c,dd31ccd2,dc69aff6) +,S(c218c6d8,3124c9d1,c988981f,d4614af7,ff85a572,dd89e4ef,6209e80f,e7656de6,7259c421,45ef6e9b,d4f451e6,e2dd226c,2e750650,8ff7834a,965cca4b,cdbfede8) +,S(4c9891e7,5559d993,536fec73,a433006f,4f0e4e42,f427300f,7a3825df,85b1cd9a,7463ce1b,9f9af66d,7a5d2d43,ab0f09ed,186eb132,92c75431,ccee645,4280aa2a) +,S(a89fcff0,694ed82d,537399eb,b59a054f,f43eb74,f8e1fc95,be0c03c5,9889c931,3ef3c627,e3011b2f,38d596d0,8ded759f,fe30ffc,9db95356,4e01c8a0,2949ea1e) +,S(a8b3ef89,561da88d,ca9890d,5e4be652,8a4b8fe1,691d1522,54bcca43,1e7ed801,c86fc397,52254f61,88ee5128,4bd54347,8abe7390,c784b51b,6d41c947,5d58bada) +,S(6a2fd5db,da9093c,2f7c7ed8,9eeeec7e,efd7e759,eacc2946,5d1f3a15,baef5b42,8b71ac63,3886ef25,74e212a0,499c34e2,d2c7c1ee,b70fe7f4,38e08914,48395e4a) +,S(d33dada5,30626dc9,1d23d16a,620dbeb4,7551ca44,dd6c8b18,fbf136b3,1ae4e097,4161f6a7,8273ead4,443c93f8,2562a29,1dea1adc,d84ba0d3,75a997d0,f07d4ed0) +,S(53ef9ef5,731d9473,93e70829,bcac3396,3b7a597a,1f3c861d,401af7f7,fb1559d0,8654f8c3,63e53905,a4d1e2c4,378fc3b5,fd5ffe3d,e7fd3adb,399d883b,3d1b9e18) +,S(5f13c5af,6979832,da1bd43c,4b7df970,1ad837cd,e59d8f15,381858d6,6263b0a1,3b0ec16a,1024c902,666dca02,4649c110,2a17205b,e4ac20df,26ce5b92,8baf90c1) +,S(674b7de7,f65337a8,c66dbf23,2f86d6a4,feedabbb,b71e83dc,fd854926,86f14f86,e3961790,a662c6b3,70460e16,18ce3b6f,42bb6142,78fe9e2e,6bb1179,181c2aae) +,S(291eed8b,47c3e2f6,b3c2967f,76743708,5c460c5d,d5d6a75c,489dcf0b,4d98b8eb,e19a9d2a,72fa37e1,e10ec853,9c772565,c53e36e9,160651d,c2a8e364,db408ef8) +,S(e737a407,1bf4fbe4,ee5d3d74,7df3cb54,cca38f48,f977919,907deccc,7090ee3a,ac66d422,79441d73,2809d695,68f700a0,3fc5be05,8241da39,9be3a63f,8da16069) +,S(67fb1676,7c3231f3,428b39e7,bdd082d7,1194a51,ae99a7a3,6c113e87,d338712f,38c2ae1f,afb521f2,279aae5b,6ba5c636,1dd34c04,a7726555,acb9265d,31c996f8) +,S(9b3b4788,71bc413c,5e9d2b9c,61e0cda7,f2fb908b,805f3370,45639,5e616230,7028b68e,af818a13,d32f116a,aeb0bf45,215d3d62,6e6ccb1e,a901544,ef1d696e) +,S(63193415,59bb1e39,2f92dad6,e5f8576f,7f49d4d1,439f4f61,a6f2be0a,75bd99e6,259d5f21,6c780a8a,5bba3e66,2242f26f,1adef829,a535dd97,11f8e786,f0a57932) +,S(4163082b,7720a8eb,e881b398,dae059ff,3a65fba7,f8b93afb,6e3dae6,6a969c79,3939395,d8e90807,e77e87a7,4c89f861,c689de81,a37caf6c,32788310,7b938ffc) +,S(b3a6baad,15c01677,ad6bed54,ea5ee415,4d0bcb7e,3ce59f17,7ff26,ef23b82b,711681f2,1e56c0c5,f2c2b011,dcf75716,300e305c,110f4071,51d11890,25bc4760) +,S(9011d111,fcebd882,b9a228da,fdee1297,824c4007,7d5ad9bd,59e603b,43be8f52,d530102e,aac87930,eff135f2,e55b5310,7c03ea00,d24f1201,6653e3e5,5d778246) +,S(d50adaf1,d68a746,e1e7b148,7660e90c,f268450b,df460d52,e1f761ab,bf7018df,4ed454c4,5eb30cd6,e6f1c4d8,7df2bea,e15ebc07,e4b4e50a,e4dea733,93abcf8c) +,S(cf886295,d1eca9f3,4c95acfa,b86a5061,82e30324,ba5b495c,70318cf7,9e910d70,59bb7068,3147c26e,45c76e7b,e018e467,1f47f488,2c044484,62a5053,8023fd40) +,S(38723d58,ffc69ea0,9a8f2655,dc225da3,78b9224a,f8d81e7a,bf8f29ee,292ddd24,c41d67bb,e699e504,3198adb8,ff11a5e0,4033b66,98d3ec18,256bab11,93db5535) +,S(5b41d5f8,1e2443db,d13521e6,e7b7fdf2,4a226cce,f369279,22b353ef,107f759d,8ad730a5,bfb9f01f,853434b,24866a49,912fc756,e086e781,a07ff2e7,98ef38d3) +,S(4f3c24c7,7dc7e41,843e9018,400d3bc7,5d1dc958,2895c11d,3855b80f,4709373d,a485adae,c547a5a4,e0695913,c4c2fb5d,ea16a3c,5211c162,4819d704,5b0d8d6b) +,S(f603e0c2,8b40c8b0,a6a0378b,c4289059,5a4cf4c9,2b74199a,4e4e231f,a6b458a6,a73aa12d,76572041,f01c33ea,ca599a30,9ba0b838,f1d67fd,67a9a5d5,c3ffe258) +,S(84ef0a98,229c7af,9404cc25,60f4fe1b,4c8e66d9,1aa96118,53fc4f25,eac78a61,653df937,3640b28b,2c2c4061,1d79c4c4,a2d577af,b572f5f9,e50cf3fd,2b2dbcd5) +,S(e675d998,6b3bcd4a,d098ec95,c386be9b,4e96e448,ce6c9371,15c5dedf,d97941b6,4319799e,c4103d65,18590091,e333cd58,9901b020,a62565b7,f84219bf,e31003fb) +,S(10837b33,3603f33b,e528f391,b5b672c8,93fbd813,99c5dfea,f6126e6c,2a2633bd,cdee9534,2456a261,8a293b49,d91ed2ae,f620addb,ed83fc66,d169da3a,a6d0e2f6) +,S(4cb1ddb6,6ad3c1b,68c6b0ea,408ba1d7,dafdbe54,89c22d47,7a62dcc7,77a8e013,6c005484,29e789f7,5c34d8cd,a473270d,75eb8f1c,3db13856,87cda96e,5cd4b370) +,S(db72d671,ff3e0102,b1c52481,b66ea142,8f7fcd43,15e2792,eae234a9,291926e6,71c55f71,68a088f9,980bdb6b,79b052d7,4edd0b19,7ab7359d,9a2f9ce9,f1c5a5d9) +,S(99f92125,162e18ce,7493e6e3,91cd06fd,681371b3,2e147ab0,24ae3799,15f586d3,9286b3de,12ee578a,12ded341,75bdb712,d939b7e5,555e86c1,82f1f283,3c1b1d40) +,S(a2b8b63c,8984a666,2b09c18d,b2d7f263,796660f4,3bd78eaf,af7c63b,986c92d9,2003787b,2af02879,642b0a2c,f716f4d5,85dac86f,e58c7d41,d1959763,310a67e1) +,S(9292ef40,21bbe6dd,8c26a862,c9b7da54,4292e479,a9e89264,618e3a47,9773b446,d1a51e4,65bdfdd0,dbf74fd3,d449f6bb,ea862eed,ad369c52,34a695f1,d229ead2) +,S(8d5a920,547d838f,90159068,ceeab220,cc6aa126,d423edd0,2e136cc0,1cea5d4,c3334a69,f7694e9,43ccb182,2caf2abf,f4caa83b,a006af52,bc010bab,8710b596) +,S(7b62f4a7,7b79c09,76dd2f74,4ec08d9d,21511fa0,7891ff5c,52b8dfa4,ae3430bc,70ab2073,cb3a7cea,350e6aed,dd0c9a6c,ff8e02eb,69bd14a8,a09e9b8e,ccb19a0a) +,S(e18a9c02,5829b47,cdd34c11,f97f2edb,a7fb9b9d,fa4777a6,dbdc3cd6,85245b12,7386fa51,881fe49a,adbd5327,8d6cd0c5,1063dab0,aa830b7d,88c7a4d7,9998e743) +,S(ab9b68ad,61335dea,767c763e,3294d414,74a6d4ce,e4ab9a78,7fbb6749,7307d0f,aab48fbf,53f365ad,bf94f823,ea973a25,f6ddd341,397bd81f,8e825f60,c916852b) +,S(3412531e,8423364d,8d8bac05,bb78973d,7b8d375a,9e52d81e,bed48027,2abdedeb,1b9f5eae,59ea3385,9de752f5,db973a86,4c735e69,fa8882ec,e1ca8350,77d77335) +,S(6a6425bb,af9f65d0,1a401330,46053b50,7ac8d45a,77b30de3,69eed52b,956799b0,7cbdae81,93368c60,5671ae9,faead4ca,2887bd8,9a06062b,dd6707fe,f849f484) +,S(87860648,af9ffbad,50871c2f,29bce0b3,e9693835,8a067201,5c1156a6,7107a4cf,8660c0d8,f1aea58a,c613f6f3,5b27baf5,53835493,fbc0e203,f024ee36,27bbfa53) +,S(35733610,71c55057,5392c685,3cadd8a3,6618443,d80b3625,53a2f197,d3c52539,42935628,adab5bd4,e0aad0dd,a7d76627,58524b2f,34ded1cc,d3e44249,6d213630) +,S(82d21949,2fd48d51,65bea027,6ac15cf5,d27bd736,45e03271,24f0f689,8a2f4a25,f13d9063,fed02a94,1b4e5c67,f42d82b5,33f18ff9,ca4622f6,79c6aabc,ea454c21) +,S(ba1a1c83,6dabf825,e517ff65,549e1d5e,729944a1,d7b17cea,aa4466ac,39ec6eba,f01dd751,3557249,d2fb224b,40ea19e0,57b4052e,adc46787,af587ad1,458bb4f0) +,S(5cfeac36,6bbe58d8,45a36cd7,de6601c8,56943a07,522364b2,23a1718e,6df8de8a,6651d3d5,a344af69,25fe039,c9e29e77,95544b96,66752f4f,c9a3bb57,c5586c32) +,S(e5e314fa,2b2bb630,6fc235ba,cfd356ae,bbf88bf7,eb039629,165d8651,c0c7d5ff,c727787b,a63cddb8,feef96b0,2f6c8d0c,9722fb4c,6274e034,32d9016a,eb4d8cb6) +,S(f84d8db4,b745cfc0,bad4844c,b5a28d7c,80ed5fe,6baefda4,1d9cff9c,158c7ec1,cbd7bc36,a8313a27,feb8121f,a8847bd7,937fa53a,d56ea066,fa600411,d87dcad8) +,S(8806a2b9,d72156ee,4948e86d,43f4b3ac,cb24529,e1b2b2f3,5ff94812,e9534513,aff42464,773f6535,b4dc5eb7,76a3f7b0,7bc2fdce,1413587f,c4055cc5,afca3129) +,S(e2ddb5ef,a1105437,b6107305,bc715e55,ec0266aa,a0ba1bcb,e12ab801,45af7eb2,43e4d056,804f229d,da5a1efa,8f429189,6c9edf94,fba505f3,1f22b511,8b698ba9) +,S(636f32ff,ea843368,c0cb708f,43841ef3,20151e39,35683ebd,bb1f476e,af2f8c5d,7fd53321,517616c,c7d24b51,851bede6,b03c50d9,82269773,dd51e36e,4898365d) +,S(bbcdd1f,f5b47391,d728a06d,b4783d5,b0e279cc,107488de,44446e36,9d11d143,6ac7dfc6,47d25b37,7281abf9,ead782a,b5f815d7,b2c9f6a8,46a8a6eb,9253014e) +,S(af0ed949,22336a4,337e9c01,53357e02,3d7a2728,3c649712,3e117944,39ed3577,d8b9c03,958dfa89,ec2105b1,d9542d68,568797e3,7df16cc3,861627f,298f3359) +,S(11ba75ae,db365552,b0d3f938,f03a115a,544a0818,b2f54f50,e4873249,6f80ce13,a6bce69c,f9838317,70f70370,7073e87c,61b65c0d,bfd34dea,f0c549f9,c58e674f) +,S(e7560fb5,79e5ca35,f45881db,b05383f4,c5536ea8,a5438fc4,ec198ba8,7543d3ad,ff48d1a0,bcf5e510,9ed50a0d,ed8e5b83,44da25e1,a1a3d421,606d19f7,e28fe79d) +,S(ce7e289c,61f4927b,bf933eb5,193fd2a3,a0686435,80c8f235,1e24c33c,e418cf10,17246f2e,e87f4ce,f1c8596a,ecdb1f95,1407ee7f,dd839c9,98102344,2f4dcbcf) +,S(b765b875,32ba4db1,211c612f,b833ba18,156fa0dd,875aa61,df1ba9b4,7d1c405d,862d28f6,d85a8ad3,bebfbc82,437404f5,2e0abafc,64369b51,b75364cd,c827171) +,S(400560bf,d2863ea,2bd9e246,4eafab2c,2798cb95,b976c022,bb9d2153,e5978a0,92ca9a1d,938e53d5,d10f1f83,54abe43c,e8f8c2c9,d4e851cc,a363ed59,71e2681) +,S(7859190b,8e619c98,76af21e9,64f323e5,221cd4c8,83dd9fdd,e56f691e,e3db91a2,e1874376,755165c3,72abd73e,415f2663,bd94cf28,2009492a,e5034da8,6f6bf949) +,S(87d90b40,bb472b84,f9d24caf,ba7a6437,b50febd9,5c643aaf,e235750b,12527ef6,e6a0f719,25697425,23517ea6,183faa64,dee1ef63,183ab1e8,3a603b98,20452c4e) +,S(dbae1ee5,a1b6affd,f9b41ff6,a1f2790b,2c492ce7,53865690,ce5281e,a7f08177,f6a2be90,a2c3cf,8e14bf87,4cc52648,b60f9a26,2930f6e8,9fadad4f,d919db6a) +,S(943a79cd,88260475,10d90c99,4d8d784b,32c03d1f,f2478102,96125f7b,28576216,48d4477b,f97c4cbb,4a96ff99,e427d4ac,cda9a367,2ae66930,88d8cc57,3bb3e2d7) +,S(699911a3,81ff3c7e,71b3a458,14133f3c,ec644378,e3244d66,6d083c55,7b6c040d,3eec684,3190d6f3,85b07116,b3b1ba15,4ecacec0,63e02933,9b83afad,5598b22a) +,S(f002302d,9ca256e5,cc38ad82,e529b461,efb69f28,52513917,fb8d63df,45e602ba,78455ea9,708b750a,97dc74ec,8939c880,d76010d6,78e0c112,3616483b,fb4e788f) +,S(5f701ce9,65a20314,a65e2c0e,1cedded9,ddfc9d01,41a1e095,e91b9915,cd2aacb1,57e770f2,d04e33ff,c9d7807a,4c3d1edb,3f9ed2,c19771d6,7e85e618,463e5663) +,S(454c122a,ff85c42e,49d6f033,87744462,8acfed31,90c41255,e8e34928,83563fcb,9d9d6435,a2c4d146,d92f34cc,40b32a7e,55584885,82143a,52039fd7,129fa6f6) +,S(d9abd19e,81f392d8,8382918d,6ab3f89d,de793abe,2eba3aa7,b58b0ca3,1e614e51,8c1a0f80,8b8d9e56,aed21554,95f8902e,a390505e,15cafbc4,2b71cf05,575abeec) +,S(20779414,b5ed6524,b3613a8c,d0b6e9c7,ff3b14b7,7264cb8d,7b266ef6,cbb224b7,c1437dd7,71f46cc4,3cfa52a1,28c70367,d6682553,28942150,c4068da4,8c218d66) +,S(141739f8,503f2225,feb40431,21f0a5ee,844a7707,62e37d1b,3e43fad0,81c631b5,a0a13de0,10566ee3,1af8c6e6,38411c1c,a3df6c63,c8ed67da,8ef7bd19,5fd6e73f) +,S(de8d06ae,dd58b148,91184145,fcd9bc64,634725fa,e4a77d35,70b21c8b,77890b60,a046f351,eccab99,1d3d1c1c,642387fb,fb9c3f92,75bbb93,46688555,f7aa9460) +,S(312c7ad1,53f7280a,9bb86cab,ac573944,d271bcfe,bd59291,c6f05fe4,ec003a81,3c6428db,5eea01c6,9fa2556b,1f20c1e7,4e65629b,e73e6429,81b971ad,b6563866) +,S(2eafa04b,d276e33,75b8d063,b206785c,ed69f763,7a5a8b99,ef7d3de,bbcaf35,f06464db,56ccf55a,1f41efe4,d1cedf81,a642da24,3cbe68eb,6ddf7ace,f67dac6d) +,S(5873091e,dcc8a95c,a0a47570,3d88e54e,1381c2d7,a418f615,eea97470,2c2c6fbb,5a91598,b8e47d2b,2fee6cdd,c33aa309,9bec74dd,21688542,9b76543a,c233ccdd) +,S(e713bd84,ca505ad3,aa022577,c0cac85b,466874a5,b02f4ab9,e8d14bba,c131fcb,706dab4a,fe49e6cf,d290913d,358d1dcf,62e8b4d5,b9bd46a,322ee4a6,5a660849) +,S(aacaa633,6fe86d41,b05feec1,cf4866d,340fd268,f869f912,c2373e10,bcd95340,d933cc5e,c01024e4,77d1e80e,c533f293,e4fd0dcf,7d1156b8,7ebd9d81,b9489c57) +,S(cbe0ce9c,77649031,25590e03,cd9643b5,dc45ecc,604d51fa,d5bcb8a,196b65ee,d2287fed,c331d56f,e585bf1b,793fd957,ce425492,df41d8b3,4faf84de,c3bf4d22) +,S(f9990413,deca9f9a,4ea6bb8,fd9b8732,57d15ec3,70e98187,f4170103,dd416dfd,b0593cb5,dbce573c,f0a75f2b,c4756781,963e1a2,e988e0cb,23efbe26,c215ace7) +,S(2ee21d79,bf3e82a6,2bcaef34,85276c5,3b51bfd8,becbc9f9,6ad316f3,a74bab97,fe3cfa79,b208de80,f659cb81,bed12184,ea2c2524,dc0bea05,64acefab,1acd8fb1) +,S(af5c91d3,44fccd3e,70345a2b,ccefc0d2,19287be5,aca40ebe,e61de522,56f25faf,bb58da83,bebb5346,9b345695,fc23853c,bc0221b8,5d75ced1,da107fc5,c106875c) +,S(2cf50e13,477d628d,117e0bb6,1d3ff9e6,1108fe94,bf82a6f2,4138a743,6077e0af,339c0a53,77a6465b,29377e1b,dc344464,875687e2,7d1f392d,96a275a,e259793) +,S(c51a6610,1522f63b,84b98ec9,31984865,eb3b29ce,fe115b28,a87f89c,67bc441a,240b9717,6af4beb4,b0502d7b,2caf8c7f,47a9362a,9f9610c5,826762ea,a2a2be5a) +,S(27478eb7,b14d84b0,80426792,b0e2e510,8a4804e3,a15e80b9,c430d7df,c09d05ce,eaa0a072,86fac65f,3da47bfb,4010fed0,fe8a9e17,ae42ece9,233c2078,2b36422d) +,S(531ca72,e21caa6a,43f0c9bd,d0bbca5a,b12dfb9f,70f045b2,f0865077,cdde192b,c4ed88b1,a0397589,3cb14ed0,897d6bd,783dc647,ba59772,16c308eb,630367e8) +,S(295775cd,950d0702,602278a1,56c1d05e,4343474,a4b3ae87,63f4a686,7688e42a,907fa8f,1269cb64,41591004,f6e62b14,4d6c54b5,f6ed424d,6b0a8a75,26935b49) +,S(e88425d8,bf6e89a0,f9c9f525,97eedd4,3c331058,10936c23,4da69d21,9a86f82,56d53ae5,e7a354d0,d65cf742,d603da2b,199e147,3d42bf3d,639f83a1,cf91cb65) +,S(4d20a032,c44453f7,b374a82f,f088d816,c1fa9338,57dae8c7,b87295,98869a4e,a7ad5993,af43cd42,1620e87f,fa0b3305,e7da39ef,704319f,ae5faf55,5e3fb43c) +,S(b98e9728,e9288607,a7ed6df9,c68749e3,9d7efa1c,4f964428,5e9bf81e,246e936c,1620c470,19b9cef8,7c1f0612,cf56adfb,5526573c,f7746a98,48a0d40a,3a92d38b) +,S(562eb109,e9988778,bed85413,2fa7d086,6b37a8b,fa2d2d3,470da3c6,97996a48,29f4f0da,e554e380,613156e3,34207e6,fe8fa678,4c08f2be,c3d6a59d,1e0941b1) +,S(b189e864,8dc0ee06,b09ea73a,9a01cc06,b6fa74dc,f626e41c,ffad926b,901ae851,4e42c870,3d04838a,9f0682fb,bc13c849,a515d5f2,f248f393,e2caf0c9,b6d61d0c) +,S(3894cdb3,425c618e,1da2c6bd,1177109c,49362a4f,24030692,244bb0dc,27af6913,20c0021,5c72939a,497f08f0,bdb03493,8d04ddd4,8fe8f3f2,97dfef19,ec9397c5) +,S(394f74a9,9146fd43,9035266b,ccee72e9,36ff59cb,eab7bf4e,925c6363,fce7d4f0,c7da15cb,c1cdcea3,21efb1fd,c56bb3d1,2bda06b9,993b6f5b,2293e493,5aa3e5a) +,S(44fc66ca,d6e67f97,be95c667,fd5dfdfe,dc82edba,35df2bf4,737b3ba4,14d15c27,9c485757,351825c5,245c6eab,2ec4a51b,ac60c9db,fff7988d,8f28728e,60f418d) +,S(fa355672,d9f7a1c5,30b2d76,f183720f,e50840cc,ba413b64,e449d2a,8b0518dc,63fe1c7f,d97c7744,35613167,660926e3,b7377fd5,97dad03c,9debba5c,a5a76e3) +,S(7325c269,50f1bf21,2f9cff6a,170e446e,62dafc2c,f05dc50,4eb951d2,6322eefa,5f8fa04f,ac5232b6,cb553630,5a48b647,263a57d1,2c485363,6c930497,40965f00) +,S(7004b364,c89ac7ba,ad03a03c,c8e2972,e5fc52c,bc656165,e08df216,afa216c2,971a4284,80556797,ebea8b1c,b37de6a4,1aa11abb,df9b28b7,7f50d362,b6554974) +,S(ffd45087,eb9308fe,e7644817,9406d9b9,77364732,f3570ebe,80a4710d,a0ef292f,f4ae4df0,5af2ee0e,456cca0a,2c9d93a0,83eb9751,a09c2f8c,5bcefce7,83764ffc) +,S(ad899e8b,a7aedce9,e53d86ac,5ca15408,140c8f1d,be3ba4a4,275f332f,b0320572,a914795,bfd9850a,24251eda,98bced8f,2e2627a7,8a1bf6e2,8b66ffec,f11ea105) +,S(7078718d,e94255ff,ae25f1df,4ec2ad90,e59ada11,5ad4a6dc,8b305fcb,93a1114f,ee736453,927cd7ed,bbb55010,32eaa95c,c0f6bb42,be77db4d,62b77650,1094ed7e) +,S(30858d4c,d39abb36,51b8d35,58800c18,5495bf31,bf23dc8b,176c1f04,171e304b,3aad8415,4abdfbb9,1eb95aa8,4e7ace9b,fd8b5f6f,cc8ea08b,e094c874,8161f85a) +,S(c06d6d2e,5637fd7f,df08b41e,5573478b,eb19dded,ead1f0c2,9309fd59,9551c787,78cb2514,37d4ca62,91bab9c5,57e88244,edb9dc30,fb1a6441,78217039,628e1db2) +,S(2f93455c,279d2a26,1cc7b724,4c32c853,459e5d99,32692fd8,ffe7ff0e,493de2c2,455f0902,74d698dd,a1a97586,fe797421,899f7c22,60ef0bbd,db0664e8,fa323156) +,S(c00c345b,b4b53f5a,5dbc5f10,597dbad0,75e28162,32e0ac71,fd778c50,20b663d5,c30f78de,3495063e,ab98f001,83fb4bb5,5cbf58a9,1bd52ea5,36a4a38b,cfe89288) +,S(7d78faed,f86a22b0,14e86e1d,f98d425c,fe2e65a2,39b37994,ab25a4d8,5c863382,cffee7d6,5181e361,679c9d51,feff7a36,e8d84282,32e752b9,62689eef,a1ff4441) +,S(ab492cb1,da2e8463,a2536c0c,7cf143a5,9626497b,23d4c179,d2af4cc7,f2b55866,11d4205f,55a74f81,e9512496,becb61c4,6363c42d,baede671,9bd7f2f9,3e00b14d) +,S(d1a3790b,478bf657,360b3459,555e1dc4,ece339d6,93b5b51c,8a3af121,79578688,c6028991,3ea89956,802f8b8e,cfc8dbc3,5894846d,1aba2074,9a55153b,83f73891) +,S(6fbb6fcd,36fe0388,a4bb648c,95a004f9,b3c64e4f,9aad672b,42d8c649,4c0cef3e,51be473c,74ca2609,c3b79c60,2fe90d98,62eb9d14,b9e52aa9,ddde106f,7253da44) +,S(db150ec5,680299af,681ce826,794e4818,c0d6d3d,e3c392e2,79ecb408,aa55b243,15e47cbc,1c4689bf,fbf7ef42,1db5c57b,e3ab8c16,b69bb518,2ed8a1cf,20067de8) +,S(51ffc919,afe67f04,a889323f,4438da99,72d2161a,54f9e81f,aa1e644e,9cb88835,e4b579b7,e985b38b,486d0600,3f825538,52a9d0ca,b8fc98d4,371a7e33,4898742b) +,S(fc86c54a,48052ae8,d8480235,e4dbb7a7,6d05d830,d2c0cbbd,297db0cf,3d2d6cea,6cd61522,e2a22531,da973c1f,9cca1c77,98ff66a3,8f568935,e378f73f,9f1d7dc3) +,S(d440de01,20d619b0,6add3d7f,66184864,5754a424,f4b1054,479a83bc,7df0db9f,3f09b245,91124175,89071c56,84215cde,a6999b17,b5b2f664,6074e7ec,6c38f68e) +,S(f3310bc7,7556f234,e7c83d69,48afebf0,5bd91a25,b516cf84,60d0d6ec,8483fefa,1d10f23c,e0e35816,6b817553,cbae0792,9e34a4a6,ebb47186,1d401341,1ef2b3e3) +,S(ea770a59,e88b598d,493fc75c,62eaa9e7,485271dc,edeb1d2f,76df983e,54cbb51c,e2a4baac,96c9538,3de82a29,5bf3cf30,53bfe2e4,6fb0861d,65c60c09,978601f) +,S(2cd56ef8,6fd04d2a,ed4f8520,951798c2,a1c69a38,8e635a2c,edfeb08e,3af0e331,3b9de80c,42e0c683,85aecbd9,c8775b56,86a8a376,39efd24,98664343,e712707f) +,S(37bb0206,c96d8fd4,5315e96c,860f5cb4,a44d2b6d,af17844,bc305b4b,59c9c649,c77b4a05,373c42e4,e071090a,7da9b4a,4ccbbeaf,d399496d,f6fea1d1,a83808b1) +,S(55fa08fb,f83dab6e,ea7034db,15bbddc0,ab2b472a,b29861af,543a970e,d16033ff,73c8a90a,86542970,4e25f370,a5caa4b,a68eb72f,5ede1edc,5549105,491858cd) +,S(4eafb675,281fe8,98593817,efe128ca,c59fb775,82f6006f,339631e0,6e7d2e7f,41347946,6630f6d3,9fa97404,6808067f,fca9e173,3e1c4149,6567eacf,47a0bc6) +,S(857587d8,375f24f9,e6924793,9ddf3a60,e0469f38,6ea0d55,d990f1e8,4ba5c7dc,1913a118,9d867c57,2f947fb4,3e062cec,3df37c3,e8edc6b1,927b315d,10c37be3) +,S(89e334d8,f71eacef,6cdd0a87,b1e03352,1078a1fd,20dff144,10108202,93143815,8b49aebe,71b60eca,c9256794,3cf74d9,80bb3e47,161b2730,9ed15872,7accd84c) +,S(69902d6,8a4ecb1,d2e496e8,cdaebf94,58f71f94,8f4341a4,11fbc2dc,524f694a,19a54c4a,5de17930,6c50cc03,b0b8d54,b613cd31,6f0bdfb7,851db63a,9c605896) +,S(52f8a3bc,832de46,1bade518,50121b5d,2ff475ab,5eb0e71b,a91ede46,41f0d576,2a946bc4,42f24ef9,469b8121,31021186,5b22f3a,adf93648,5b7907e4,791236e2) +,S(146fc341,4ca1c497,73734f6b,b78ee9e,56fedee4,9487ea01,a31d3e4c,152d2bf,82e21722,e415f5a4,1a076f3a,8324db4,2436da5,a3776f60,b5c6f53d,e5419db9) +,S(9e22043,8458e463,b1bd7399,28200a4f,ef7c3760,a7b6357e,afc29a5e,36b37b94,b1f5fd78,c1c40d98,1473fd4e,72af64d7,7387c28b,7f005191,5ed2587,674877a1) +,S(53190555,8d044a1b,94caad4,526cbed1,82807c65,2bb053d7,61301ab9,68323f1d,ce591f2d,61b47a8f,8707d8f6,a8717139,68ad569a,667ba4ac,f2d18356,ab6d326f) +,S(b93b3c6b,1297cadc,392b0cab,da322a1f,69791a68,b48b0ce7,4352ba3b,b8a3cc8a,136ec007,24fd378a,53d5c573,3aa7a06,15b2fb20,4fed82ea,934bc131,7e7ef22) +,S(62e6af72,a9fcc956,4367d44,c0699897,3674e3f7,b79c67bb,8c8a82f1,6d3f744,4c1e4e33,2e9316ff,24a7b38d,13ed1b30,a659a8d1,8423db6a,af6324f9,4e7d5475) +,S(c91facca,aad0790,886c4421,d28f3ce2,9d64dc87,514f06d,cfe95063,dbbd0e3b,e7a0c83a,18d655d9,7a555c51,4bf8f117,59ce866c,61a7e691,8ecb1cfc,21e50710) +,S(72b3b928,bea91ce8,767b2f22,958458ab,9a23f455,78f836,65c89717,7e7ea502,773235f2,c9ec74e1,c06e4e7f,89247d12,27f303ff,2fa354a0,663791a6,ffa40e49) +,S(1b014ac2,7213cc08,67877fa3,c0c07b1d,91529259,23f6164c,f1d204d9,addd6f4e,e0dec57a,c1ec264e,380909bc,c62394c7,413cfd48,180b1de1,c5440d08,99c67f2a) +,S(985e8845,97e2831c,4ba48888,ac219b3,bd891dc,63176953,4e0d8a96,bf583a83,5afa0b31,1e909594,4b228958,657b95dc,a198b008,6f5cda96,c3e4da6d,e444a418) +,S(ec3cded4,cce88ac6,22315a51,ec99ed3,1ba2cb32,74a43e6,98e033d4,e490bae,1c2a2236,2704a765,252b2a57,aa1ecaa5,4f1c21a5,2d67c050,a3dba077,ad4b4480) +,S(f882c4f7,7911f6e8,64da7cb0,5f09da54,b9cf7125,21504815,e6fdb9e5,73f24be6,9ba4a4c1,e0978ee6,1f83aedc,b1db2193,518a71d0,c112b9fa,59c629cd,d42ebde2) +,S(f06c3a4a,9f1e80cb,e2ee6b06,ff50e447,13d8eddd,5857243c,cd02c5d2,7996eac6,6b38bddf,fccb4888,da20ce7e,495bac5e,7b9ec8b2,2feaff5,8d4329c3,c546e839) +,S(dd1c366d,5be82883,eb86da5a,f9b5d5b2,89de6b9,1165b91c,20ad7b73,ab4de421,19bb181,70b31a81,45232ba7,ed0fa93c,273751f4,838633f3,c94647e2,93c06bc0) +,S(e965c1f6,89b3f7a5,97d23d9f,378b9a03,fb1c6e29,5354de36,a5bfed53,3338372e,d98295bd,890e98a5,9d6dd93d,bd9eca2e,e79b028c,7459779c,2d3fb665,46692242) +,S(4f9148b6,cf21f5fd,911d7fb0,e92132d4,686d3e10,12da444b,f5a89040,69c2fcaf,60e9b3ef,924260db,ad68793a,d684c316,6889b0d0,38474c70,6ca25978,4a5fa7ad) +,S(d9341e6,6a505a91,dcefd533,90abbd54,52b39594,42deef9f,6a67dac0,8faa5e00,eafdc1b5,2b7c97f2,21ba8635,d49b58fd,17cc7983,92e09d13,8c41d679,6fda331) +,S(b948bed3,4f426bbc,e6c24ca1,fd1d2092,1de6f9d9,f561c32f,877a4104,afe81ae6,5cacf40d,f6fba19d,763c5b0b,83aaa284,f9485705,ad882b6,d51bd46c,b12f532e) +,S(73011c97,dbeaca8,8bf04371,311e9558,1e494b73,1778a2f3,cf906cdf,64dd781f,2b5abb31,c2e56a38,e8236f7f,b28a93ec,1bef3fc8,9be46d92,c2fc476c,42ff4ed9) +,S(44835043,1373a095,13f756e7,36ac8342,24004348,70d6cc5d,ba31f74d,565056ce,748f041b,a9c990b7,70c36f68,5667109a,acdad74d,4569b107,c968e517,143499ae) +,S(3463b029,e9dd1907,bba2b582,c9ff1f26,4a5ef4b,40f5af28,2a00c136,97145ffd,9e30bb18,fbb70c1c,edff9065,968523af,33665b53,3f14457f,59d7edbb,d5f07c5c) +,S(5d95aa9a,9fc67758,56e6655c,21a62670,c567e236,a41b523,436beb2c,18e69c97,d6478496,4220cd62,b4651e8f,965bbde3,22aeacc0,6557f656,764a20e4,649a1ea5) +,S(48c65239,d03715f5,71ddc36f,313a1723,b014c047,30cbbd8b,ca9617b,e8fb3874,c0bd39e6,3cc00fde,74c991cf,f6d711a5,356b029,5b6eb9c0,28c445af,21de8325) +,S(67c610ba,fda8dfe6,c7977b70,7a57ebd3,9654c7d4,c9a0aef2,80c896e0,e8134078,c3d4ffc9,891a4267,612aefe9,6c1093f7,8e1b9a58,746c986,c5265381,3ba46aef) +,S(4605213f,4a21b15a,4a471e10,f157d0c3,faebaf0a,f7cd05c5,a27c56c1,88b6d430,9853c145,5f4b3d38,200aebf9,d08f87ae,3eb4a99a,3d94ace2,94331e03,876fb751) +,S(6277ce5d,fb56c2fe,3d32e28d,6754255a,ecece179,70615443,b0d33e5e,b54e475e,dc7a742f,92160074,69d53e73,da526102,a6f68e7f,e8f07a91,98614f0c,17daf06c) +,S(7fbc032a,fdb50898,3270249c,f1a7d27d,198432c1,6815b7d2,c929ab67,bf12f01a,dd696a37,fbc4f852,6db85ca0,6bb498e8,6dc33480,a4f84564,2b49775a,6bf20ae) +,S(dc849e28,6b934ef,30cab6c0,1cffaed6,9ea74ab6,96578027,4c88845b,8ac264f1,6d7f94ae,347b9b18,bfa2bc0,d6b9a8c1,13ef1a70,78e317a2,53f21590,667f4b5d) +,S(e29e0833,7da9e0b6,b6886c7d,fe010cd8,9a4b10c6,f5e4ce38,c1202f59,94a69016,de082f4a,9c21db41,573dde9,d2ca8256,57c67173,207d43ef,e42ea81a,ee812dbe) +,S(407f0e21,ddccb63b,5672eb15,742d72f7,1a522f9,20d04247,3e1438e3,7bdecd35,28d6f39c,cfb36986,351d7619,daba6271,3c9869f9,357504d3,25c112bb,74ad336e) +,S(653286df,15bf657f,1685bb92,98462383,7c819def,84a8c332,77f6d777,4c8dd3b3,c5ea3d4e,f7a97324,96a98abe,f999c25,9a8c622c,944a099e,146c0708,d108eb34) +,S(47613ca6,ca7fe67f,4286f12d,3b4bcd86,455a1863,94f2bfca,e162f991,3bf6ed7f,4409a6d6,2ae63ffa,a3f2be74,e7d0e5e,3ad4704,76282b0,c2919750,72be1386) +,S(8f9bf4cb,4aee791,12162fff,75f35393,509fcd4d,aada298,655bd58f,567e6adb,893c8b52,c2c25f8,e2ea051e,d1a35a3c,8ddbbf9f,508ac792,37e53143,7818102f) +,S(4b25cee4,2f955db9,79c85682,4dae806a,3925a33c,42e6b6b6,ab4997bd,884f68b1,ebe38f45,bfa13922,d3112af9,1324f526,1c5f864f,4d722576,24e3afe9,9eb7ebd8) +,S(a8a40ca0,56b5e168,6fb9a348,ae26b15b,cf812cc5,b9c5abc3,44f86021,69958e81,a55421a9,4f1fda1a,f6d64e48,cee3e4a3,434293d6,b49a5cde,b521c0de,49cd31cd) +,S(1f7e97c6,198e27a6,48ffebcb,55c001f0,d591ca64,77c7c1f9,43928e2d,dbe88dfe,2dcf0a3f,f296f744,dd402fd2,48d29c96,49abc871,28624da,28134cf7,6242e6d2) +,S(7ee29348,7d6520a5,586b3ba7,4a0a660a,a4392e5b,77529e8b,890a5927,aad1ab71,a51d3123,b4faabc3,77bcc565,d66d4c40,92decf92,f63f81d2,ba3a4f06,2a8ea485) +,S(30880ed,b4f5f2d0,95c34730,d10a8c02,11efdf6,784b4886,4afff98c,407734fe,4ca4b106,41c55905,9a19cbaa,bc6837b5,300aa1c9,8b0d8a72,8caf5cd2,914ea693) +,S(bb7ffe45,b725038b,597a20e2,6f67d0fa,c77730e2,4ebadc9a,23afb4ef,a01b4d50,dcfbe717,46bc35b9,81357857,c0b4564e,1ea11852,c5ed8372,bd15eb69,e674e14c) +,S(ddb59e25,20a5842c,62607d4c,6d31f2ed,b79e387,527dc12,5d96fd04,84385e51,2a167f97,85ee4285,7471c45,2c38639e,69940f77,b3fcd54c,a697100,37d9e5ef) +,S(3c405df1,36b1c6f9,99fe0b3,2bf5a305,bfa4a6e9,994a1506,9c5fcfc,318dacf5,ba5ab8fe,43a2de82,ce289cf1,2255f554,d7881d08,a54017f1,e0c84180,d963f3b8) +,S(ed4e6e9c,9d4eb277,73f2249c,13d6fc1c,5c07be47,9ca48e35,913f6fe8,384851a0,8242617e,36b3c298,b13aab58,b7383dd7,93ccd4d1,7787062e,195456ec,43fc4198) +,S(60fb7766,9f312be7,11d0a269,a72a85c,e06b70c8,1d34416f,f7ce1a28,41d50895,ba2097a9,772a616c,82cd5dc7,84585c80,65206913,474f49ef,71579a3f,8cc0d825) +,S(1336a678,bd5d495d,c688c039,a60b256,a09fc946,830fa80b,1622890d,9c8f0881,6d57f261,b1073b42,f6eeb514,58e994f4,7406b3f2,7245a0e7,528748fc,65a4937a) +,S(adb7e6dc,72c02a9,fb99645e,a3695824,db60df0b,f9b6a420,75cf02bc,4e3713ee,96325866,2a062180,20f8b86e,e5ef1e04,57d01fd6,d5541d6e,50798883,9f03b3c) +,S(674d6e5a,6501f824,87efd592,c37bc14a,ab596cec,4d5e6e8f,1be44a53,a9d2f416,e6ca1153,76af0429,1bc2dd6b,2c47a816,c17e0ad7,c252c1d,cdede8fd,c9a18630) +,S(ed73f5a7,93dd0359,68e253ba,8ac4f2ae,83d7f290,24eea4b6,fdc418e2,4b63dcdb,bf687b81,4691d48,5296e192,d5371ec,daa831ea,ce2d502,4e321add,b62f0faa) +,S(601cc630,47ce63d2,c1fbbc49,f3b2dbe5,6edbca09,17ac41e9,b39f7ac4,5cb3ff8,a773b9b6,d98c7b35,458e956f,f9cc71e0,8539b118,bc2529f0,fd496e54,efc2b25f) +,S(a31ffa07,e16fd36d,2edd7d1d,944ecdb8,2da2641e,cc6ccfe1,88219a67,5677a740,c2253d87,59ab43d7,c919710,9e9ecdae,df1f2d82,d34c5d82,d113958f,cbf1e3f) +,S(131060f1,ba302b06,b26360ad,de531db3,422a6bc9,6ca1e9e2,98b5da8a,c8266249,4dae16bd,40159bfc,96665d6a,d64c6c5,3ce90fe0,95a316a6,8cb84844,ec183cf9) +,S(f4a29fd7,90f35f1f,4b3240d6,c0cbb983,3e701a9b,ca241d3d,bb7630a6,da2de25a,b2ecd794,5afa51e0,1caf3c21,5334dd9e,9f9a1f6b,8f35767f,c63a3f00,69c01382) +,S(b30546b7,c5e45266,854b90ba,53afdab8,be115251,5ae22f1b,5e0c472b,a3b44042,f14a6b95,178fbb85,5aeb9cc3,a4e65560,3e8b18c1,4cc54cb4,1e059597,faad7f4) +,S(b6cbbb5,10ff83fb,c8bdd55f,cfafeae8,947f5e83,4cc78eef,dce45dee,2014e175,d0e29276,a14cfda6,f518a3fc,ac6a4163,13666f91,ce5ae7b9,7abfdff4,925cd415) +,S(fcaed74f,19199482,564ce4b0,7e1033ca,c17bd418,769981bb,6547caa3,b61bafd1,c52c08e,f5331ccc,271b108d,5e7794ee,4a493ce4,f6665773,f97902d6,ffcf40e0) +,S(c6d1ed83,29ade032,c9924945,189ff23a,c6b1befa,e4a33794,8b1b4126,6d14344d,6e1c0e9f,da231092,86d23cb3,2c588799,507368b8,8c0628de,b8a79c97,e4ba817f) +,S(ac7d0edb,a7b86cdb,60963277,cac43674,19dc5d85,ccd9a649,bd335dd5,eea2a9e2,61790bc0,ddf5307a,56fce870,4bc9eed6,9d61d857,f4d2fd34,d53a97f2,4c4c5206) +,S(fd9d0c61,d3dc3ff9,bc3735e2,b2c7d5d6,4a740b1a,bcd46a7a,860816f9,84a64645,701e082a,5e59460e,c3e3ca21,40137ed1,5bf9ef49,fb4673c7,4fa5e4ff,2ad8b762) +,S(82ce7d44,87101716,6ee31310,11bdf02b,336fe44c,88572c37,930a33ff,5bdcea1c,d7fdb097,3961ba18,b53b421b,21c0424b,7b4807ae,256d883e,2e3a6f22,9bc44c0b) +,S(5e507c80,bbd32e0b,efc31714,b464580e,f7ccd3a5,2004f2f1,9fb9b0f6,dad1a767,95facec8,a3b5c996,e0bc9d5f,77c11407,d0ee071b,9cf5af3d,ed625025,3aecc75b) +,S(640d6b4e,bde998b4,1bc86fbe,2e981683,a83789d,b71bca76,76715a29,143c9b55,61334a92,358ae235,104aca00,a632006b,769475a,da8789f4,f7047f75,c8b1123d) +,S(e951dbb6,96ceb16b,6059c3ec,9d84ae8b,8adb146f,fd79a323,59c04677,aff990e,d3443900,ea47be0e,10ea9178,3f1524f2,fe5c5720,efb62458,987dea0f,1612a94e) +,S(2b601b8a,550333aa,2a0e98c9,a4b28043,aa38f20b,13a72cb8,7a8cf212,cd0a2a92,2f3f309d,ff296fbb,a406602b,be72d9bb,c3ca0578,f5f0aa08,6e65ec0d,8989f670) +,S(d803b242,9814658d,df7cb378,bb9708ed,6ddd7270,ffb7d712,b64da007,158a5add,bffd353d,c4d664f8,6d7f150f,30e44ace,fef4abe3,83f30369,15b4d5d6,5ba3c131) +,S(d5a8348d,fbb9f9d5,798d7d1f,3515e25d,a2707869,26ae598d,500186d8,a284e86d,ce31c62,19960c61,f2a8a7bd,2fa36f0d,6ea7838,5463a4a2,9dc1ee95,2f37905) +,S(250dd67,781128de,65f0529a,11b2405b,eab01ee0,1e9bde06,cabcc9d3,cd038065,524aa9b5,8dc5c6e0,6951aa70,c4bfdffe,a2dae26c,1e8341a,5f63a180,731a0dcb) +,S(f3a2017,127dab2a,917dfaba,5eabb6da,bebe554,431e796f,a3b09771,66cd47ec,69d1de46,2e63beeb,1334fccd,ce147b96,23683942,21c4865c,d50fd687,5c1659c1) +,S(15626140,5aabf9f9,d7dfd218,3d7fe2c,900ee051,784537b8,deab8bcf,20c6e0e,863d8797,f2b20b0d,ca1a5b93,9a43a712,4d5f0725,8ae7caba,4b76d308,65f9a98b) +,S(3261fb49,2c55978b,20d2c5b2,3e6ac5d9,5e3b40a9,462b748c,62a41035,2efd227a,4072fb7e,3fafe456,8f62f4c9,83d0988f,f9096c14,317f7584,9fc1aed1,3e39708) +,S(2a5e6aaa,f5cd7466,b851802b,80305dff,3e655dd9,b0456bfc,fe704eb8,582836ac,2e3894d5,762e70d0,4d3595f8,83dbec7d,5125720,c516fcd,f361ab22,be67062d) +,S(e85a7e08,13294e2a,6eebb00d,2e3fdd76,318b7520,dc682705,8b3e923d,536cbcd7,5a936569,77b89e9f,10c4ba6b,7dcb1611,fb6a85fd,af0b1136,b19cf222,a2e9a477) +,S(8e36e66d,2f080ff2,a24ac836,25e50148,e5017b7d,b898820c,b7d73214,bc198b28,edf99b43,c2b976b1,6eda2a75,f8bf5aa8,6945d700,9965b528,bb78b530,3888c56d) +,S(99ead62e,11cc613f,2290332b,9d5ddb98,8c121ed5,21a0147,589bbfe7,db0f0c58,6e73b10d,bac646ab,44f3a062,4150da41,d666523f,ed55fdc1,c1146fd8,c4a262a7) +,S(15e233c4,783fced,39c74cb4,34e77491,83004e2,6e16c372,f6f7f23a,e30efb13,c9954660,eedf09c3,af8e1642,3b57af59,227ee5fc,e1885837,5ac5a853,34fc5034) +,S(3b33e0a1,254d310d,efbf526a,8a2314bf,18bb490f,e592e36f,6763b174,96f57a67,e4e6a395,a6e6c730,67fb2e0e,2c5dba63,9745daf8,bb9d6aab,e96f59d0,8eb24c83) +,S(8be3fe6,f3e45fda,270d54e6,49bdfa8c,1dbbecf6,35922aca,2d605c66,6933e859,10269847,15eddf7f,b7b23972,ed1f6fd6,882481f9,1a12eed7,f8fabad9,da0917c5) +,S(ae6863ae,e6d519be,773edc77,8e7bb5a7,5e0d30ca,91dd0920,aee35870,5bf6effc,d104a8c4,1939444a,1a5843e3,92fc5fa8,d2dd864c,1f442938,9dbdb543,415988b3) +,S(58a06c8c,658430fe,e03b3218,6ec051c1,18174f8b,ade2cfb4,a98cd9b8,13e12dc,17f4ba3,da575c84,81cdfc04,25567dd5,1d2b5e94,a8ed37cb,346f88fa,e6f3553c) +,S(cf0b0d64,3843e10f,142ff912,720373e7,2fd18576,1f3d6c71,52647afc,1ebae367,8ed9b909,f4b622d8,cf5d5b19,fa2e1cc4,bdb42133,c4dd9261,22f1aef4,6dac4c64) +,S(8be41872,15902e71,8f75558e,4b1a5ba7,9d3522d2,e8a2d723,f863c4d7,3c4713a9,f7be5a77,6b8ea8ee,1e59765,ecc6a33d,210eadbf,df4c5586,aba43710,49c715f3) +,S(1e6bfed2,36a10a7d,9efd88cf,af1dc1f1,9649197f,785b4a30,c68391ce,808f713b,90200be1,29c5235f,41a79bc7,2d4d10a9,b00911cf,b9f27b5c,6d47eba9,8530b8c8) +,S(97176463,8cee344b,1ebd8c67,17782c6a,86108b76,e1eb74a0,791527dd,b18e14b5,ccb128c5,2295ca09,626ccd39,b5827ffc,3168a628,6633843e,96184aed,d7bf898a) +,S(2fd20134,8e0b7259,415cc828,c3e4f79b,2184735c,86e534dd,77f1c5b9,9d8cbacb,41fc80ab,25ca1a82,67582a89,f901ac61,2237b942,9d724a2b,8cd0dcb6,c96564b2) +,S(5ba918f6,6bdb4013,481bee06,6aaafa88,f741c362,b5c3a4b,28dd4555,3c1aadd2,230f6c20,f39849af,9de4ff99,8e39ebcd,818d5d6d,8056ca67,8f04f33a,c4fafac5) +,S(69b5676d,b8e2a30a,8870c86c,4a859ba0,dee25a28,493909f0,9ac88af6,83b70797,4831db1,98c1b4dc,69942571,e4908ac7,41ec4568,e27089a4,682ee7c9,8ee905d2) +,S(faab5a9a,c530f7a3,343c71a4,273935bc,fb280258,24a63ce,8f8da6e2,a60135ab,1443a23e,793eb339,f4353371,34937bbe,9bda0eb8,c2fa2d50,bcd85757,68c79797) +,S(c70ef20,284cf135,7c437093,10451a9e,5ef10cbf,e3a0ebfd,b90f487b,8ca62d9,8970b394,35c29dc2,b8254d1c,9c4cdf54,6df52979,ca70ff8a,7dc171b0,5a176ce5) +,S(acb67abb,fa6dfd52,e9829767,182f5ea5,66dfaeab,42a2f2d9,aaa545e1,2e9cc68e,4a097150,2367b1eb,4f51f6c,a394d351,b734b30f,fb36dba0,9e53b4ea,3f6ebfce) +,S(2da4ea09,315e0f1b,f5b6e0c5,b8af330c,c27e26ef,54affcdb,fda834bc,cd578a3,a4097d6,ad006cca,53ea3391,10c80150,f63694be,c5ea7728,f77ebb83,2362a407) +,S(70cb8427,c2d5ee86,59b26055,3499374,63e7a482,6731ab85,6b3fd10b,be3dfdf,f44efbf9,a914063,3e8378c2,6e9676b8,351b6ddb,33f7e7db,5ced5208,e4cb7361) +,S(b14283ed,9a9db980,b135557d,97190066,60e4e1cd,3ab288b8,dccba9b1,67b24934,95fccfb3,827332be,a813457f,28a63a89,87dc8c5,340a8f88,76788a60,f03b2606) +,S(ca24774c,727efbb1,15bbbf12,20e59841,891dfd50,9f99744d,de815f5f,3e959555,2519e414,a9703de5,60f7412d,8949f509,bacecf3a,e672e7ac,58c5f98e,33162bb2) +,S(5a5c1b07,ddc79f18,26bb95e1,13e03894,bb63b593,c808f0a4,2bd44b36,dcd3765,a11bc328,2f672c9e,60846826,521add7,57b891b5,d4fde6c9,738c6329,dabf67b4) +,S(2caf2545,1daf272a,b169a4e9,b0eb9c0d,26888c18,ae7d8357,b8dd2b73,ee4cf7dc,bf8d8ea3,afc9d56d,9799083,c79f1289,7d20d7ed,48ba38a1,acb039b1,2292a3c5) +,S(af6650e3,4022d1a1,b66e8c3a,b0a8e9f3,baf44318,f6946a0d,d1c65aa1,e3f9c9ce,1b0222c1,a5f3691a,248154bc,bc9e5e9c,6b2c96c1,ca468feb,97a7c01f,2551bd5) +,S(3653e476,4215cd7f,83ff59e2,f63f2a7f,dafb8795,95006bd0,182e0e,78ca5b39,c91824ee,6ba3c685,2eeb2695,9a3b136b,371c9bac,4141344c,a1eb3051,8a6d8ab9) +,S(7826902b,cdd4e7b5,463452e1,baf88534,64723588,86643d49,cc89b482,298f6394,dd5bf1b6,91647ce2,86c745eb,6246d087,35cba4f,22c26a37,e326ed1c,4e3d968b) +,S(da19c301,4e77f987,abb00fda,8a5dc50f,d2777850,1fafec84,2ce7e5f6,37edd1da,757fb28b,366104a8,cbd26981,3697d8c7,821c6e0e,5acbb33f,9cbd5fed,25cce98a) +,S(efa31697,2a61cf86,df713186,c1bc61f7,63a618a0,8034c26c,462a2457,907436a,74f17527,e6c7aebb,ed6b28c6,26e612bf,82d61ad,cd1ca156,b971abfe,2efb15b0) +,S(379d460d,12fc351,4c46567a,ce05cc9e,ded3cc35,fb416bfb,76d14f3b,c5164f7e,f548ad44,a5c87313,aeb52885,5ac3a955,178c8992,2415837d,d3e7863d,685004fe) +,S(e3b66f05,ed217cb1,6e3f8ce3,56704f70,36b97104,e39c5649,83bc204c,5b112de4,8a2ee4f3,ab9c8cd4,40afe707,d8dc0fed,473db9c1,be2b6e7,b14de905,ba38ae2c) +,S(b14ebf0c,ccda021d,8db1fd20,a4108c48,750d10a7,e0659cca,64f3e6e,ee8c7a4d,c94ca9d4,d2ba47e4,576aaaad,c15f9019,f335ebc5,763cf232,f56f215d,ce42322c) +,S(1ffb2e29,df24e7a7,6a71f607,f4f03608,43d3715f,c3e468b9,92b684bf,1352cee1,27c39636,54ab505,4b008e5b,4598d025,ade823c3,df6aa7c8,18d4d4fd,32442990) +,S(63e13d75,38f1eb8b,7276ee28,acd56bf0,448f026c,f98c77fd,5f743c24,4872b70,845d65c0,a6cd8b30,7c7ea0c9,8024773,86a50daa,3f361539,57cf995a,43705cc4) +,S(169b95d5,417e6173,b4393c6f,1ace153c,21c83c7c,971418f1,27e1abf3,aaa04673,bcd1ca4e,5618a183,420b34d2,52d25dfa,e7cf699,3e936450,ffaae3d4,61a50c50) +,S(3dde4fce,54d27794,29b6f6a5,8c5315ff,ed682bc4,5601c623,948faa88,2309ed27,2b22dfb2,dc46f625,4f29f8a1,4cb4496a,dcef6806,26d4f499,a17c4751,4a1802a7) +,S(6b37e3b0,2422eccb,299e2b89,1a150be9,ef56a628,62c78c11,f3411614,9fd37802,a9c347b1,fa5105c5,a27ed367,261a1cb5,5d54a071,7a2927ed,29d45eca,6e958295) +,S(7bd6703,ef78de4d,f32f0d7a,274f6371,2d0b0a9b,fafc3f6f,5c03b654,599b443a,33d9b1b6,8254d47d,6aabb7b0,b66298d,8447d674,4612a2b1,f722a597,857fda25) +,S(2e9a4aa0,a2062c8a,63b18e5f,91a609de,b60ae126,93d7f1fa,757057e9,ce169b61,65a55523,e34dd131,3597b250,acd11763,62df6b07,1d3f0f1b,907fe0bf,46ff87ca) +,S(f90c1316,9524f407,8c7328a7,126c48f4,eadd6d65,8baccd1f,403a0b68,ff95e7e1,1d6a1b16,6f5b4f7,9f51b046,42306f53,bd84b7df,2a58457a,a8869,4111cb38) +,S(a7bf0bfa,8e84b448,9ee29801,a5d06f66,a17d2374,a6d91eea,2482354d,66c60074,e60ec871,7ea63cc0,e4785455,478848c0,363f6717,748ee9f0,1c928370,df0905cf) +,S(dc4ac1a8,372c06fb,ea7a44cd,11a352e6,fd204df6,f6868cb,5230e420,9ab36e6b,f3a1b986,c86134c7,40c0ac9f,8f2780,82a103aa,e9e4269,84516e09,659f668f) +,S(538086b2,aba47aa4,dae137a4,4bc747ce,9bb225c0,6aa015db,e9478da5,e2c9cdb3,42ed59f0,fb831257,84b15d27,b9086eff,9aec4d8b,9ac36f87,10f93bbb,2ce7d971) +,S(92eaffc8,955f3f06,fca49a8b,881734e8,6750b34a,d253f160,183388af,f18b8106,fdb45519,36f9e291,9e2e2e6f,7e5d2dfe,16e14da8,b5704a27,60e92d9b,13758767) +,S(63fda46a,4350fdea,fba277c6,190c5f37,c4cf7c88,ab1b2ba5,7b0ce9ec,60249c46,b64fd169,403a2687,f5f41038,91e811f,6dbb344f,2c3a1492,fc408f74,433c65e) +,S(f9f7568f,d2e444a8,14b03934,72564798,5c16424c,1ea6fa80,79f96df2,ff3c73d2,8ae3a292,fbef875d,ca18fd89,8e1e882c,ef7bc222,64140183,1cc2d268,ae795a14) +,S(43a5ef22,93462849,ecbf95d,3c92eb63,ea89a39d,2e0a5da1,eb261c1f,2e8e4bdd,896699cd,e580ef97,64ef1860,19317dd,f7c17ccc,34d340dd,b23621fd,5e5ea5aa) +,S(1fc26dd2,9a3ad966,4206f233,82e54363,6ec9d95e,a58e7af8,a4bc8aa4,736c155d,5f6c4cd9,c6f954f3,f0f3bc54,8bfc4a58,faec99d5,9f3466c0,bcc09053,346bab63) +,S(9a63cdf2,84ee5189,34b73c8f,d8b3932a,50ff88f4,8ecf9b40,c0056c7e,168956b3,e52e7902,6a8d4e95,c7f473e3,84b9966e,16347c60,8b6fb6c8,484335c1,2872f4ad) +,S(aabebcd6,8050337b,1e6caa45,13d279cc,ddc4c218,f849afff,8c1302f7,6b728bb5,808fc6b3,90c2f001,bff1993,346a55b3,663378d6,e457c639,b9a73f87,78ee8d09) +,S(9fd45b70,59cd26ff,2188167,ecdb5ad7,62efbcf2,b668830b,475df22a,bf526239,6fa79fa0,e76536af,92eeace7,77c1b735,19092fd4,870ead71,35634768,700a1241) +,S(ebb3ab2d,e71844f5,b6ff8baf,637cc0f1,1004611a,e8a3b570,1afeebb,a7a4edd4,30eed3cb,55a23613,3bec9aec,b620f1fb,1f39a067,8a44622e,84b9e271,62294133) +,S(a0e7ea2e,33c6a3eb,423138d,8faa7ae1,d7802a73,507d48c1,bb38bcf9,29838ca,31158fe7,ad132036,62449ed9,76c4aa17,67382a8a,cd42c842,ed22badc,882d7d26) +,S(a19abdde,db579ade,1dba9264,9d58b15f,1bf3b87c,eae44520,2ae758ad,f2164fa0,508d24fe,f53d8f4c,77e43ef8,eabecf3a,ddd04eeb,acfbecc9,637b6c07,fbe88084) +,S(e86ea1ab,95e23d10,f086c91f,289e2f80,d10ea0bc,aa9d8f3,1873158d,19166976,4b6bc016,6d95e08a,72c4dac7,9eeb405f,938346d2,ae45ea0f,ab76ba98,8c4e910a) +,S(aad23857,d8d835f8,e3785020,6b1cf1f1,efd9c9b6,ea15d42c,70444ed8,81710ca1,a550855c,381bca50,44be47c1,42d52259,af2f486f,622699a6,aec28138,554cf06f) +,S(b5c04c3b,436fe2ac,97bf2043,3ba57e9b,897816bc,eb8383be,ab11d488,e231a138,b1ee4b56,1965ab9a,9fa87112,57a250a2,d7f06de,7759918d,85c69d66,5546a6c0) +,S(eccf5dee,9b6e3fe3,1e3c02da,a2d5c408,72766edc,29da88b0,cdb01f4f,524b0ad9,7770c7e5,d97ca3f8,cdda858f,ed5976f7,cf2bdcc6,60459a64,daa495e9,28f4cc37) +,S(4c34b06f,6d02467c,9e667ceb,630d9865,4540b18b,8832292a,17e74d9a,d0cabc6f,57671dd,326be6d0,2ada8789,b5f27c7,b052c8c7,d300b2b3,ef881a24,53bcb6eb) +,S(9493f9c3,98a50b50,4db602ed,ef50cfce,5bcc9f0a,bcf80ffd,d5f65d1b,d030ca31,fb45d7d3,fc1216ac,6b948369,42d33e82,932af020,fd7c29f0,f2959eef,a41dcb30) +,S(b0ca3c54,153d435,b9d6427a,20ac3456,ab980b67,ee92c6f7,605aa934,faee0bbc,2e2a47e6,8b959402,1ed83edb,d4968c6a,2eb4be8e,2cf128e3,a3778751,20f27b16) +,S(d5632117,2e9b97eb,4b282d8e,1e303cbc,d50656df,2a99c9fa,bad35909,1888ee8b,30157215,efad51b8,20c03f8,2e4c9c93,11d15bc8,3dc75bea,e44e1e52,7f1ced7) +,S(3a0f593b,69396a6f,62655efd,97c103db,5e209554,8d45de93,8963cbc0,dbb9b4b4,66ba5298,590d027b,30dcd9cc,e4605e4a,73adf43c,6e377bcc,cd19eb92,e20e267c) +,S(10eb3c4c,cccd5ca,e15bcc7b,92d108a6,de762cec,b71bd78e,1917752f,b3f6cf86,6d65809c,79395f45,153d7650,74597eb7,edff81bb,598ed20b,f9780e45,bd91d077) +,S(40ae7e81,e33791bd,154945a5,105be36f,57477fdd,d36f83f9,d31fb73c,862e70f9,71511daf,e9cde10a,7e4ff05b,948a0454,e5fcd355,d98b2bf5,8ed3ca54,1a568498) +,S(994dac9f,3c047519,823c007c,57851a8d,41be0480,7c6ba5bb,88e19869,261fd12f,913e96eb,bcef4fe7,60469aed,51e9dbd8,4910d606,9646255c,9cb3bb7d,55b1e342) +,S(f0d9a17d,112e91df,63c106a0,5df3cf41,6471b817,a3ede631,d845b604,3c04d0a5,3ab43cb6,cefaa228,5c84a42a,8f1f41b5,631bab61,1d006946,f06be576,bc817f18) +,S(20919d38,ef12de1a,64992396,f7b265e6,2ab8267c,bb988d1a,6771d8e,724d16e5,81f79a2d,b629ce97,47f146d6,b16cdfd7,90fccd71,c46cf9f7,fe526e58,8ed1eafd) +,S(b92087f7,43494d53,71978b1d,63e11ae8,a1595239,33b87a44,704ca3d5,30f1b02e,6eef6ac2,49a8550a,135072c2,9bb42afe,5d27c8f7,fec88aee,6274d27,36dc0eb) +,S(f69dedef,a8894583,c05b1fd1,ae205fba,3ba11a37,ca628150,d287f01b,b6920b34,a9f88097,66d18287,9d97e01,5544c7ab,ca0cf8fc,c5b9f305,ed0bd9e5,58fa5b3d) +,S(8bd8b3c9,a301a76,9f5607a3,e116edab,9d87bf79,e4099d62,a328c1c5,f8b8b6a2,167117ec,ce6e15da,47a076c7,afd851a0,cb5dd1c3,d8187f4a,9aa5abca,cc0af771) +,S(d7e2bc76,e2a87357,82611,2619513c,10a2eff8,857fe6fe,a2e2c00,c6f8ef4f,85e1891f,531481a5,e935e80c,448d8da7,32f1d061,dd234e39,d55c58ee,df8cb93b) +,S(2701bed3,778a857e,c7a9095e,6487e92c,555ecc9,32ad8e0f,b1b59d70,4131768c,9e38370a,8d30d5d6,6aac5302,efc7496b,cd5734a2,20d2e76e,fae3c72c,a793d991) +,S(3662a916,a05a80a9,13bfc287,c81cf28b,7295dd1f,aa52badf,5ea1c83f,e1b90521,b14bfdf0,a33c9286,eac83a4c,b982f5ae,82e2e134,99bcb3de,3ac930e,2ed0c931) +,S(d778d74e,317063ff,6556388e,2d2ac429,410337a,d62917,a94b7654,6a04787e,f7d92bac,c92600b4,e8184960,11f135fd,adef70da,b72c1bb,9da26bcf,7a81f0b7) +,S(7b05001e,388f9197,ff3504fd,402a5291,d1257621,c8eb202b,dba87b56,154b6f8c,8c9c1c9b,5c151be,39f2875d,b31797ba,2899047b,6e5c491a,56f7fd28,1b897f2f) +,S(603546d0,1fe46be0,eaa9f00f,c99b2a6f,c38fc225,6553bef4,9ff47442,ffa7626c,1f86de50,aa457ed,ea306251,91cc3cd5,61cbd8f3,2b41145f,139110f0,64b73b74) +,S(80f9318a,ca3b7aca,65cb1965,a36981d0,86ba3b32,8a95cfd2,9c6bb718,1f662c25,c195025d,794abeff,49c9b1f6,724f72ca,70b0bada,d06ae11f,2101b49d,d592ae18) +,S(61182e02,a877565b,1209dce1,bc84c62,f3b1007c,48332c56,6dd6e697,664cf64f,d21f9259,517656d1,3efe7166,750c341b,6c064b0,542bac29,28424a30,ce4b51cd) +,S(21cd4f22,ec3a190c,77073680,58562d82,36d463c,f7706f10,fbebd283,c19378ff,2e04af2b,155beea,daace708,c510555b,467727fd,145426bd,680d1964,fc1b03b) +,S(2d3f6c78,94479443,6004ac92,554a507c,5a20ffd6,f275f715,b176b3b5,3d2000b0,72e0e16f,9d9ceaa,2ffc5a1a,71a2cee0,eea64e44,7f716eec,1fc417be,ba16d92f) +,S(a9f45433,c040d1ac,6bd36d33,5b655fd8,eec0d3a0,bf1a622b,df422039,239de17b,264ed6a,8da4a169,a66869a0,a846229e,81d52517,22fa4c2a,4c775322,f33b1184) +,S(16e052be,bb65657c,309d67e5,b68ea441,4179ace2,38315c8f,df88626e,84281ce,91c007f,fcd4633e,597ee06e,b28296bf,2837f30,8cb21be5,9bdbd52c,4ae0422a) +,S(ff6d96b6,f633beea,1de80512,73c06bbe,181a1098,db737c8,41a48d44,cfab935a,cd9a8011,c22b643a,90f1d9c7,54b7330f,1c1970fe,5ac04225,c2720054,fa6a8444) +,S(dc2e3b1b,a2619ea0,6e4ddc09,99630c4a,7b94c78e,ed13517,25b8b8da,137fa467,f97e82ad,49bb4389,84cd752e,820aac43,bc6cccc3,f9b0d9eb,c2337bdd,89cb83c9) +,S(d53b97c5,8e23d50a,3b855c43,80a992c9,2d667afa,bba4b028,20031f58,dd6947c9,24a54f43,bb9a04c0,8b93aa96,9dc61f92,e705ab75,e644a3c4,40391af0,ad375ec7) +,S(b598a510,beb9cbc1,c4e03f8c,1ff610e0,7123ddc6,ad69dd6e,f9fa7810,f79c8ab3,b895b8f7,68a80a37,77d8b5bd,46754473,c9590768,a56fd586,58ce97a,366dcdbd) +,S(cc2296b9,ee9726c,17b0cc6c,745a038e,38b5535,5e19fc49,e89f4ec3,372b2a05,6a466fd2,f9a5c412,9f5b2faf,aaef4cf2,be71406,590ae031,d68c482c,86e3f89f) +,S(3e84c76e,254c858d,51cd4e74,72c22ec8,364ce28c,de81ddbf,b7b3093f,44acf9b3,ce8ae6f5,f56814dd,21186370,f70196d1,339c264b,c78a502e,bd57e771,b438c81c) +,S(976f67c8,c626f8de,9de43f8b,39585d7f,d4cb35c6,e4211b00,68669ca9,a973a3db,ae4f1f6d,25af4ccc,3eca4b2f,6cda0cad,dcc28f78,c8e881c4,eaca9131,2f0b057) +,S(b28bc958,aed50ba7,6d5293f9,7c419828,53bec43c,21ef0f96,9000fa47,49ee95e3,e7d4cc8f,f68182a2,15dee707,4df2c351,906d5527,c47e6414,54e97c1c,b57525b2) +,S(e54dd5d2,16bc5903,9a630bba,7cccf5e6,81a3113e,be8fb57f,83c51209,965cddb2,86352dbf,5f2b2327,ca50458,b3b2cdd1,8d403629,a2e2a776,5562240,7e8407db) +,S(58379e1a,10115a0,8f2b93a2,3f2f4e2,2787aaec,cf129910,b1911b43,c209f379,a526d32c,abd99175,e63b4493,7a0dc96e,e0feb99c,bbe00c91,1e606537,c871c6c2) +,S(7858a68a,cb34cfd3,e1cb7d06,4d233322,ceefa6a7,a2de391f,991b94b4,da9ebd63,f54490f5,ede73b49,946ffe19,d5ae6f7f,ca026c07,b1dc0138,b1409383,5d792db0) +,S(3c8c636e,d20d58c2,f03c26ac,bbbfed70,3fd13304,74bdf2ea,7526f16b,4fe6f7f7,550069d1,cec630e8,6d22af01,5981f20c,ffdfabdd,6416bb00,f2dd8c6a,44401759) +,S(d03fc7ec,eb716cbd,f033d76e,8db97314,858674db,48ede8dc,1a1c7dd0,82ae7f93,16f7a717,a197fede,dadc25b5,a20b0486,1cfaa63e,3157e5ce,ff0c81d5,bba296d) +,S(6d6c983e,d60601d4,ae5ef4ab,c73914c9,b9546964,a6a3d2a5,681b09cd,afee2c22,13f5af59,61877fb9,2a2ac09a,eb691a88,20ca8361,81359fa3,96558ad7,b2276b1b) +,S(92a9f5d2,149065eb,1241d82,fd06ac2e,f1f48003,3bffb06b,ae3a9d88,a1d05f8d,f0be5964,75545835,f64b0985,1baad682,b84d7a39,91ea0f49,1d19b7ad,77ad75d) +,S(222aac7d,c2fe0066,b766e1fd,179c1912,5f289e98,33cce93c,749afe38,ce0aa74a,bd6c4d8,43e5923,b5944e40,8d309733,dc0cd9c8,b266dd8c,2df162de,fde5bf5b) +,S(28b14628,3a7e1785,1c9d1831,6c9ca17e,56a13c25,7877ca3f,49ab2369,ba2898f1,292856f9,572ecc,5f000e0b,1e7ebdc2,3ec72619,3c97542e,8ccb10cf,d608eac7) +,S(5031a6da,df8d12ff,9eb19209,b844b008,dcc26de,c4a6f9f1,bbaa45c9,daf909a6,ef31dc6e,3cb5c39c,64011c0c,89cddb04,7ef9a881,a0d718b,5057bdfa,3f5e1ffa) +,S(925835ee,ce55e98f,130008fd,a6f01768,aee2de4a,f96f239a,430d408d,95a627c3,cac1b7d7,60362e24,f3b5f277,e370ba70,f0f8c6dc,a338c15c,46c5fb4d,f9f1bba7) +,S(6b7253e6,9dd65781,8214fa04,67f59e92,a4671648,c465ea4,9c993bf3,de8d9e77,50a9cbd6,b429c3d7,32bc3b68,ec581327,1c2b0a4d,2d8587bc,7b26ee15,ee7e92bd) +,S(f82d660d,cd5df408,714213aa,a7406a3c,868a8d05,1fe37689,baa6fbc3,effb3e33,affe0a1a,b7bf498c,83b331fa,f3e7b723,2a2dbc66,3b93da28,e12f0d56,260677a1) +,S(990954a5,5342c5a8,db3fef46,da2b31ba,32b6d597,c67c8290,9329a90,c39845fc,d764f091,40891e20,a2130696,216049d0,b3eb1af5,7dcb9797,4c42abc,5298977b) +,S(d6b8e341,f8bad7e6,ff434dce,fcae81a4,1946f237,e0467120,fd420,f3dafc0b,a8ba941e,81677d32,140666f3,55fc28fa,7233ec8f,8fd13703,214bdf8f,340c389d) +,S(fabf5120,8632ee4c,81ad6d34,cc84de68,728197b0,fcf07971,326d4c04,7017b905,79c781e4,aa7dedda,4663b8db,7fff3cad,f9752cf7,ef2382e4,b0254489,7c6c04b2) +,S(e62db266,d9b208b1,83016754,c871a8d7,4b6848de,e496ee7,e2c1447c,2029b0d5,ef7410f4,f44d7414,53b9c1c6,8e5689bf,37cfecf2,1602e95f,3fe053dd,aea24f77) +,S(4bd90928,14753ea0,1025ea2b,86ae2310,5b581b48,98359481,d1b7d36e,d9c6e478,2f12e4b7,b825c858,5362afc0,d0029880,45fd638d,65f1865c,6ffb31ce,424baa50) +,S(3e397cfb,cc5c9fa1,15519366,c6e814ec,4b0ed9a5,dd093321,219e4028,28126ed,31c352e4,d4bef7cd,8929d3f0,aeecf9b,1d577ef0,e8086a28,40d46966,c1165dd) +,S(24bee951,54baf31c,f5322941,ffc01fda,9e89f80f,2662f5b4,edb751b6,5b372ee8,9568a4f3,a34ce5c9,8f591550,b75f2a84,be5cf67f,1fd7713c,c126f46e,1c3b6883) +,S(d242bb09,e4c6cc42,a2bdcdc6,bad2a02,8e4bfa21,430acb16,3f0e8a70,2ad2f0d7,2eede9ec,799b9e5a,13c40c4e,4cf6dc0f,9640d472,2a78cc6c,d920b610,d070b913) +,S(a57b5fd8,f50de3c5,98073cef,bc6fa56d,20dbacc0,72dbbf81,347bf0e8,7a36ebef,a99f123f,a45b1369,32394f8c,1f7ea2b3,563ca2ba,9c0da00d,b2c9c119,e6f20287) +,S(82a43e2c,a533abf0,6aab1199,fdd20f9e,1616a79b,859c6467,ffca4b27,989da9e,19cd40f5,8c997e78,311e5ba0,c0b3328a,b845da97,91dc8ef,a6cf7e5d,efd51c6a) +,S(95bf3b4d,b8ef8bab,16e00f5e,7dbe9907,b54c98f9,2331c4fe,688129a5,5cd021c9,2b4c46e5,b9d9f915,d5d020a3,3c63fe49,eeb32931,9373bf9b,e359c40a,19328c47) +,S(791ea768,4d333125,893356ed,dd8ebe4b,3f98ae58,e25a2d1,67006a4b,5c05799,5f72cf68,7827ce2,6bebd598,539f6519,a50788c,9d093766,9c3b9c29,f52d5422) +,S(85138ec5,a5d3f0a7,881ce23f,e34fbb80,fc3038e,bb7c2a24,ccfeb2ae,5ef07b74,8eb9e80d,8664b11b,d24753f2,bba612bf,c06bf9f2,a4bd061e,82ce9037,895b084d) +,S(3b4c1149,1d98483,9ff33b31,39da0341,f430175a,d5800c80,37e35dfc,a68ec256,574d6475,e225aeea,dace647a,1489b2ea,873175ea,83cff3f2,fec684b3,fd2ad143) +,S(2ea17f9c,6a028f58,396474bc,8c51b1e3,cb9ac0c6,7f73f533,a428281d,4101b04a,19d1f021,8b8cc0c1,bbfb8925,77087caf,ebed3409,69dc2281,3a1dd6eb,9d6c44a8) +,S(67839592,2fcc7ffb,627486db,e1d98a45,25ee1338,5a98c01d,6b3b9f8d,f64c507a,e13ed82a,a1adf761,7cce3760,d049a72c,7572508a,5ecd0615,1bbb589f,17e10a7a) +,S(54a0d2ed,39778fc1,237ab423,1e4384e6,4156c1a7,d5e999fb,2c92ba42,ef40ee68,3d879ff8,c25ed54d,df848e5c,49e3eeda,e8eccf33,afcb1c1f,67440810,8b3e979f) +,S(263ff1ed,726d0ff3,8b47b398,c713455a,3030d499,3a77ffac,b9cde4a2,cd12f7f3,a5f3fb33,c739bc93,c442508b,ac70884f,d90c3699,34fd698d,bc27f7b0,ca48a0ac) +,S(43b2fae4,a431beba,598b3ccf,caa1e034,91e7099f,c19508fb,8e693de6,dac9e3ac,3e43aac3,379c321a,5313b412,7cd00a1,bd8cd177,49c4240e,7cef1e97,22daf69c) +,S(a73e77a8,5733a296,2da7fe71,f4c16166,cf0acdc8,2d5288e1,8cb9479a,2a31f362,12b393ee,e323fd52,e44a9ab4,3e19256a,f7a8c892,76e9e6c,9052f0c5,3ecbc031) +,S(f007a9f2,bd3d0d4a,8cb9524e,a5aae499,6b2d0c1a,19aaa56d,4038a21c,6dabdd86,517a6a7b,3b75b68,bb04bdbd,d6f93414,c97b4b75,47b8fe22,c5828d6,7e979702) +,S(bc64853f,111371d0,8381c082,c72d2528,823e18b9,99de4303,c85a5fb8,87250833,51e7dc7,23256176,62a9f6f2,70bde4c7,94020e61,9e8dd87f,3f266d99,71596146) +,S(144dc699,a063c473,4b9bfd22,845317da,c4b55e92,a232b92f,965967b9,9c8e21df,a20a7982,7d57de38,85e573d1,6d22025b,648b9ed4,c656c48,bd6f4902,2b67c681) +,S(834ed659,98d49650,c3f525a1,b34ec725,89d167f7,a3cc6e86,8e4cdce3,b2ba3179,6bc2b06,c97a108b,2abf4033,152065f5,24c1bf1a,e79adbd,b7eeec17,9e598cbd) +,S(efce9278,6052c989,adc9595d,cd1500c9,8e93eaa,dafe2b87,2011bf55,3488b1a6,48ca33d4,cc89b486,c0930829,91f23147,d910f29c,c9d13787,7bebf55e,a3dc92ab) +,S(5b150240,c3563354,39adc9fd,23e9f94c,369a6cf9,27f45bc0,f0110cc1,b7321f4c,629b62e3,1a85fb44,b448c7e8,e76ec423,b059c9a,c8144c64,9d77a4da,eb044aa0) +,S(cec8cb02,3f166bd6,9fe04f66,8ef6e949,f4e3d26b,d748c30f,53a9b9f0,9ef3f9dd,997f9fdf,dee5cd89,352e6c38,760c4cd0,a7bb2696,94916567,ae60fc5f,39b908ca) +,S(58427cc8,edc5e616,3726519c,7522980c,30443eef,7e72cdbf,b135ede1,f15c0890,d2fe9c00,7eeb0be0,8295e972,e69db2e6,a54193d3,ec9a3ea9,f2515e59,6e34e88) +,S(a7d0a30c,1787b9f5,8ebff502,e4e38000,6bf896a,2feaefa4,4670dee,6030bee1,774eabaa,863fd4f1,63b3a16e,4c17078a,fd0a1761,b39ab05c,3c6b1d36,5e487592) +,S(9d70eeed,55ed86a1,ba17e817,4c400cb8,ab0e5da7,b568ca81,60b585df,d1a84e74,f4c1fde7,f11b329e,f3c3e196,bd78a3ce,5387662b,859f99c7,d61f7962,2d01b5f2) +,S(e0715fee,9639817b,8501de6d,6561e623,628de7ff,d0dbab5f,bf58ee4b,a365f684,e34ba5d,acb60318,c6ccc001,871800cd,98ba2c5d,529a42a2,fd145336,7b10c22c) +,S(594612b2,4cb1912f,fb945465,1036bfb2,79be0bbd,e718ca26,973552b6,93d20143,d68338a0,d03fd0c5,e6c6e3dc,7070c78,72eef1c8,cc2154db,cbdad699,b594b142) +,S(f4f41087,1dbd5394,5fde2583,b08ba65c,6729dc05,b32ec3ac,c8aadb72,a6c0d8,8e948ba3,853102b2,99f2f558,ffc66181,310bc676,4b82dd7e,85fd075d,70d92fbb) +,S(efca7b38,69f2981c,406cfa40,2bbdd00f,15ba56cf,c6e9f66,9bdfa14e,aa9b1d21,458fd928,a07fa32f,d6a0ffd6,64a16b21,2bdad00f,a4534093,209b0c92,f507f9b9) +,S(25b2384e,d9e09e70,8098971f,153ada48,f3f4a9ae,fb946225,77d213c1,e7b326cd,ef50fa0b,4cd30cdf,2667bb4f,7dff0287,df666dfe,7d5bdb9c,ab567af4,b77c7535) +,S(ed12b20f,aa2ac5de,db62c17,2bc67cdc,f0534a33,6a32e26d,b325321b,81901268,9245a6b1,35df02d9,3db1224b,1a0a296b,a39f07ac,ce7ccee8,eda552b9,748eca9a) +,S(5dfa472b,b84ada50,da3ede14,8bca4f0d,aa885d2c,61633336,b2a8a37c,76b200b0,f314a5d8,2d20fd30,ee237487,7a922a54,f4c10468,656a5432,974cc3db,c2c7fbb7) +,S(a9e5c0d6,5b6d4b4d,b242d87f,57c805a0,5b7f243a,515c5d34,a037ddc6,afcc4470,d90fa599,de5fc8d9,87a42c57,f3bf9dac,d5b766a,fdfa07db,4c56c0d,3d71324f) +,S(4e9ccc2,c0e3169e,e25a6701,a82e7f02,2663d3cf,585100c8,2c6b5da4,d8c42e8c,854aa160,d48d241d,f5bdd1da,2c74d1f9,a00fad7,effa29fa,5ecedc42,3ba43484) +,S(2122c569,da05f145,48d42f9c,5ce3a96b,bbcb49fd,fc5b2dbe,1a7ab0ea,84aec76d,40796418,39879018,93bac618,8a7286a8,f8745709,58a40cd6,1202ac,dba5867b) +,S(aa8cceb3,415df249,b5069899,75a853fd,eb409729,331b1807,ca2242e2,a75c6968,5362b581,fb676eb9,9f125fd,5f674be4,800d89f0,133a1363,f1cb30ec,1fa5b468) +,S(beaea0f0,1fd8e442,50648032,530e41cd,7e3c8271,f4b83b4f,7493039b,b21013aa,e21f7d1,53e5d411,794fb472,47897cc6,a86396f6,3f6291bb,fe2f0f12,a263873f) +,S(8eaa225e,63bb92b9,666bb318,22c166e0,3843ff47,482cec9a,69ff584e,d1a750a6,aa26b32,e69c5d94,19663e05,6c9f7a60,1274aec8,a839ff1b,b5a43c2e,136ffd64) +,S(bcba926,40d8b4cf,8d73c0e3,7867f5dd,81eef91,70e904e0,15f05f3e,1bf6591c,8913cf87,e33aae4c,a16b1391,75d60106,ac7d05f4,a8f10db2,b0569a6d,ec121260) +,S(2d03aaf,6828d0c5,9ffadaa7,5d1b7e95,b3c4a547,46faa5c7,c428fe45,1a9f3332,68468e2d,597e7534,37b3865b,3c968e4f,e3f6b1fa,996e6b4d,296a12c3,20e61cf2) +,S(b39b4de5,be757b36,b1e88773,47cdaebf,11f8d6b8,ab59d780,fbaf2263,a823b5b9,1b609d2,de73faa,d613c4dc,6ac3c2e0,33d62b22,7ca3fcfe,4aca992f,eef90b0e) +,S(eabf705f,b6996148,170b23,9fb8998e,df6214a2,cac27fe2,9c66a899,63d2f0f0,e1867fd2,98603f79,d17876f4,7aea4437,cb39ee7c,bdafbf0c,5452b024,86182d17) +,S(a4ce885a,ebe9335c,6ef43c6f,395e1644,201f578a,4caa7f74,65c12542,27f1bc80,bde32eef,f7fdc498,1c50942c,efb38cf3,c3f1a8a7,5addfe7c,f11723e,f57d0c41) +,S(db8ee69,b0e00ff7,6a094251,a53c61f2,e00dc65e,a11e00e5,553ae121,ea958361,24e7ba71,22742e09,728108ac,59c5f46,aba40d1d,c308edd2,9d494f39,d156b2d) +,S(88a5b43a,d6cdc2b5,aa3d58db,9a9a2e1a,6d70afa,808efa87,1d7f3cbc,509cf064,88483c55,e5bc8717,8114605c,1febc726,e14345d4,4211b670,6b6185d8,ea6b7103) +,S(e3cb24cd,2130716f,10a4d432,d4e59229,2e11a2f5,3190bffd,4f478da2,216b0051,df432db5,caabb417,f8252d37,dd23e117,38066c87,101cd934,73a2883a,1b7e8de0) +,S(d9c88471,97f04254,731794ec,e29ba74,f1d72f19,8f424beb,8309e1ad,e09e1301,12037266,331f04b6,db4264df,ae6d934e,5f753828,35a646f5,cfa4ae8b,e6defdc1) +,S(80bb740c,3510574d,10afa586,a6e541fe,b5368797,231d2a63,67d2890,8877dc58,24ff9389,bb268f42,b04f1702,f7b7c6c1,7194c7bc,c5f4f63,bbcd4ca8,ef476fca) +,S(4b8b08fc,400d6fae,da05cb19,65c9c785,8f93452e,d1be8f29,28c83e6b,4d2b06a1,70a6612,2549ec2f,6cde4a4a,9ae8a528,1b05081f,8eefa8cc,1111271a,1beea7ae) +,S(d2708078,4d19552b,13ec8473,1eafd2fb,bc0ae927,dd746a8c,be1dfced,d072ddc8,9362bac1,806dfc84,e736758f,22f1039d,57a5e44c,40586195,885abb78,f75d3355) +,S(7345d1a2,7b9dc922,f51a0466,57721451,cea5b54f,6673a484,7a210b4c,339d1f18,293053e8,c180ee0e,d81bc4fd,658fcd5b,99bad329,9e62aedb,74867df2,3ac12d98) +,S(3b3a6baf,b9003f37,ca27889b,a2be2a02,748ba52f,6c6dc719,afcacf2d,578b1e24,a9749973,c49ebecc,501f9b18,83056505,6c88c286,2747496b,951892c6,8a61ffcb) +,S(b2d1a3e9,15b02709,1abb5ba7,206ad2c,5d07079f,8d4c7fae,3e6837be,34159226,90007202,78d4188e,b8efc45e,8306ab47,be1c6a16,d88284dd,fb627b1d,92959898) +,S(298019e5,5de972ae,fd0b4c94,9a9d4eb,46689fa0,9b849947,bbe27805,4a3f4a5d,5b7a4298,be50ee99,363b5f9a,1ef103fe,4fec0034,fd8a2364,5cc76f8,ff804f0) +,S(604f1e62,7f503661,5b55fe74,4540df91,67fe575,e0aafe13,8af68214,f1080eb0,2ade0355,b373ddce,2353669e,6af5575,cc6b01ae,58b2a581,2b63f36b,58effd5d) +,S(ddcbadcf,63ffbfce,326db7e9,2df0d483,9a35df32,fc576070,9f2422f2,6e07b8be,7a3bf369,dc3a5ae0,c91cea62,1429ad45,76f7110,b654a1f0,edda9b57,3076306e) +,S(160a19ff,10c8b334,7683b21a,880226da,81d536de,d9dcd357,d4f2283,18853837,91dd522c,f6d1d276,e88287a5,a37afefc,ba010111,82b93eee,1483cf56,e6cf5218) +,S(bc1210ff,d75737ab,51300e3e,ea48a264,5f363793,1f4c6f95,e8507034,84834819,8acc70b,363e52ee,779e6342,ea7dcb7c,d7d09946,5b0eec60,e35ea360,e8baa7f4) +,S(c2da24ff,f2c5419c,3d2ab241,e55cbfff,5f41b9ae,4efaa105,abcc173,a81fa1ec,3fed4d9a,4eabf3bf,6b913bd9,279761f1,4aa630c,e278d0d8,4bc82ac7,af77481) +,S(73a495ab,b26d4be1,bd99935f,f5583f6c,f5c86d79,fc86a4ed,48f87f40,2f12259b,f4126c3b,3e9b8dc7,ead63ddb,5749fe5e,6cb0bb3e,3c85426b,950326ef,9092301a) +,S(599baa65,fdf949f0,f54c44bd,ff605ef6,2f53d68a,f0e44bf8,274f0b73,ce251227,297ec5da,e3893363,617f5448,b13237a1,91fb5a20,e52ee897,a124daff,9f1eaa9c) +,S(9a032a50,6786e1fb,bd9ecd25,243ea67d,57f9e4bf,d1083879,e309690c,fb07f253,a073c6cd,15445d6d,80c51af2,4e7f540d,f1b75fa0,89a4849a,ea14ea09,ededdb0a) +,S(792160b3,eda1d493,48ea96d6,857d8631,ed1e198c,c7f185d6,b9d2c75,2b1cae63,d12d76d8,bd73a681,9bc581e,cdb84efa,dd143704,5554984f,8f1132c5,fe1bc32f) +,S(8fc4cc45,9639aec0,2c337f16,589c09a7,e787dffb,69ba2907,37b6c465,db9f12d8,b2ce3c2,57ccb79a,b677a9d8,49ce1833,2a97fca7,28bf3e83,6dbefd23,5343571a) +,S(fc06a9a,5857b2e6,df0c3be8,b5840946,ca77f690,4ba114be,44e760e,267eeda5,6662078e,f0de0e94,5a770654,b8281a74,f82168c4,49455250,136dadf6,87c5bd2a) +,S(345da4f,e1e89eb,eb27b353,4352c5a8,32d3c9ee,456c01e4,657c2885,733b5f69,3db274c6,b89e7400,cec4e2a9,9995ece7,be8b98da,39a2fa48,bbec96a4,5c758be0) +,S(309850db,36d8c2b4,610cf8f3,e1c12431,3931aebe,1a9d2099,cb3bc7b6,fbd8b0d8,edb3a9bc,f6417672,af9b1379,9bd8aab2,f953391b,27cdf167,320005d2,3fb3e4d1) +,S(8a35d7fd,8c7c75f4,e22ba36d,d84533cb,f314a54a,2e17a43f,d250c941,5025b0aa,97ae015f,fe364a2b,6c12cc42,fe526eea,7250092f,79c40b94,ab7102c6,fde1fa5a) +,S(982d9d66,dbe6b738,2cf2820f,c72ed491,d6a61add,f1eff091,de30fcb4,96ca4161,b4a7b42,2e9cd74b,10cde7a8,12b4d88,f3621324,acdcc911,b39d0952,3cb3b501) +,S(1396bea9,57d47670,735ed20d,e2ecaf6b,afe7e067,5f2a0f65,6b3e719f,573cd1a2,cd77e7d5,a49d6cac,5e7871c4,58df45d8,821d77e,f3ad3db3,56989c1c,700cf484) +,S(6857993,76774ed8,a08da47a,9323241,71d09fdc,964893d2,aa5e783c,b8030057,640c986e,befd426,8fbfb131,8133ca77,879ced3b,56ec251d,12d0a650,28bc9d3e) +,S(801865ee,fecd50cf,4970e6ba,efeed453,769b3e14,c103db6b,2632e241,2405fb6c,b3d3f23c,8f75e475,8784016e,6ce1b91,8db76599,8c7c1aaf,af047282,ebd7a636) +,S(f09220f7,27e57fd3,209eac13,1ed93f96,142c400d,b9bd708c,9e716686,20f1793f,8280375b,1b2f4915,cb82a72c,43c9fc6c,f3a7de5c,32762b69,43187a8f,13510eb) +,S(1c814b69,6104f8e6,5a51b98c,e6b0c4f9,f26b5633,8ce24ee2,24a5aef2,967ab705,1cc2bbfc,61828277,90502c11,bcaa1f1c,b5e34891,a7c669f4,dbbb604b,df2e3135) +,S(22fff436,de961dd2,a7ef38c2,f588aade,320d0721,55e6a42a,3201bc6b,c7d24a54,6f13476a,f48f9197,3845af18,ce68ba91,77a0d4f,a835acce,76814c81,f6aaef5a) +,S(39da98ed,a85b2de3,c9bba6d4,1af633d5,5a392dac,b1d4fb9b,654fde5,a3343a65,29b89534,35045e6f,2ca41bfc,9e5c9ad2,55eee426,95f41744,b239659d,460fbdaf) +,S(6fb84bb6,3be24203,6e067346,1a5c1fbf,7d89a0b0,c98ad2e2,25cc3ba2,191fbdc8,21a37f8f,e4f821bb,3886333,c554d6ea,cb8924f2,bfdd7ebb,339a8a92,ec9bf8a4) +,S(29080a3b,7c1d33e1,13b70c4c,8388fd4f,59118a86,c4bf697d,25346368,8f60766b,745ea31c,e9731f68,89aaf5d8,1e7f686f,b345c9d7,3af728c6,7ac2b8d7,60ffecc9) +,S(a54f7e6b,aaf05de6,77e7d98c,5385d9e9,57f68d2a,8e3c24bc,11b6435f,50cfd955,f7d61099,22ff9c98,ee79998a,597dac10,be885d5a,e7b53158,fb671e38,470b5c69) +,S(7472bc8d,c109d56d,a5f150c0,bc41edce,7e378335,15e17267,62f50cce,e8845b2e,3c59290,820b997d,3c9109ac,7319048d,e37c5280,88e671fe,9cd85487,f03917fe) +,S(6f4cbc32,ff1805da,6cf83c64,b0d0eefc,6bae76d0,586de80f,df2317b0,a4e68267,29e492e5,89bf6c68,17731a29,76577124,c9bc9e32,1856bba8,8a060bf1,6a6c9d9a) +,S(1a93eff,b60cb865,8538cb27,d8c12490,483e6886,16320327,e2689591,c5413a12,d96609d9,251ea2b8,f150664f,efe6bf6f,a0634cda,b8846cc6,5f6c8a8e,97697c9a) +,S(79b82e7b,bb23efda,cd79c0f0,87f2d0d0,e2d3db59,97696c73,e6e94ea,8cb72027,bf16f23,47e594ec,fd087189,48d531e9,8306b173,88546493,d620218,b25e3758) +,S(a806c902,28fbc405,ef56057a,aee9993,ef177261,74d456fd,818126f2,5d93f986,fc49c640,7d9d26f2,f043a1b8,8a41bbc1,3ced6a93,bd669ddf,3186dab2,9eb7c5b7) +,S(227d9094,69eab478,9da2fe7b,3cb618d8,d781e0a6,54892f86,92eccce2,f41e9a1,81cbb45e,adfe3fc1,de5975cc,f87f0a44,e9f106d7,19cfe796,f33b5c60,85de7691) +,S(14f8dba6,4bb5aba,f7f58e8,1c9b0425,2c357f3f,fa8aeee9,fa4ac700,8c49e060,26b19dbc,52b913f3,b6aafb31,3de99445,b49d64ef,2503d28b,bf2f9e2f,4614609c) +,S(f22646cd,553c5008,aa3c4cf9,f01a05e9,20cee021,4e9b0553,38a75c1f,23e5fe4e,52cdb77b,7502794,ac16003e,2f4e859d,ec3e68d8,c84a3365,a307ac80,53d3089e) +,S(515de841,814df2fc,7cd43689,8780686e,f3bdbfff,83eed568,af19c75b,5ee93a2a,7395bd22,705a81a3,d62f3130,ad416db9,268961c7,facf81a4,44d2ed8f,e4df1282) +,S(59f4aed0,38f07bd7,b2aa6600,ca6f15f2,d396310f,e4ffe212,4e6a89e7,3c7c4ed3,47a048be,5d6e45c2,4fa69329,86b86edb,b990eae2,f5dc6524,3eadc69f,43b2ed3a) +,S(7492492a,d7f94293,4287c7e2,5f05203c,4c70b8f5,80d23b4d,4822d1ce,fa3450a1,19523158,2fa06da5,eb5368b0,f0b19971,1a1bdba7,50d1c6ab,80ca1b4a,d649eed6) +,S(2e12d13d,464de937,372a69be,b90dac92,78977cd0,e7b954d9,30360d6e,a51cc3af,35a900c5,6c7cf352,8c4a5af1,56dbebaf,32d2a128,8a58e3a2,ddbcb539,bc3e3505) +,S(ce4c4b4f,a52f06d1,11dce52c,402395e2,a6ffd186,dd8275b7,bd43e941,9f4699d7,957b994d,c5bcd5e0,179a67e7,94a40ac5,cfc074da,6d23d178,969fd8b9,2d9dc455) +,S(c51f1cac,5814de82,f4bd5d2f,a8e4950c,38fe78bb,8d09bc5b,628ec13c,e3084592,721122a2,f63289c1,f8925ce,34d810a4,be52f7bc,9bfea232,47617f95,8db1cc95) +,S(9fbaba14,8293f578,46c717c8,718a3a9b,9dc5df98,63ae10e8,9c3649a3,e26128b1,4917061f,ad964c4c,5d79780,7914b2f4,b6c3e730,3bd440b7,bc00f15d,bed2b497) +,S(a4a9ed86,ed3a1dce,5423c1e,1bb5fb9d,fbc1ed41,66a4219e,bd343922,8542ea9d,fcf6821e,46386764,c61eec0,19b1a84f,6a8851d,89be0da7,9d2768af,6eba9d89) +,S(a6fa3567,9a732aa7,b58582b1,5f3e95d1,f702ab16,5249e9e7,89a5a395,3858d1eb,3bd67e4e,c0dae02c,43d32cfb,d25a4b2a,75898bad,eb2986b5,6cf61825,ed2830ab) +,S(3b3992be,8f7e1b1b,4d8f5710,6feb825d,568b176d,af0e41f5,9c2fd59f,f67d9b7b,ddf2751,ac17a413,2ffc5c08,3e8c7728,a74b0063,377228bb,a60cdb87,1b5db297) +,S(e9ccb20a,8a279cef,2a15de57,e34f510a,f60ce397,3fa2ec2c,4558f166,7ad20201,b70a11bd,1b5414de,1b30035b,3dcd1dbf,17558bad,368c78c9,d5d9136,6592fa53) +,S(98083dcb,1356be80,b324aa95,e38217f7,c437f421,5a7f3e98,ba803fd8,5cc04445,bc0d9ebf,ade6c640,d16b82b6,5328a2,d5ef2f8b,52aeadca,e406ae25,e9b756a1) +,S(6a0f3b01,9e3b2a9d,453cc36d,275fae6a,b94aa14e,e0b4103d,137b1741,8b446ba3,ef30c9f8,46c54902,c10a4ee6,c5013b25,709e7109,fc004b6,e291b1bb,a021facb) +,S(83a62b60,6a1c4d03,1a18473c,f2230088,1d767332,9513b7a9,4f402f18,3dd45640,b23888d9,98bb9c00,ea57c9b9,517080de,95e6491d,cba0d7a5,729ab99b,12a1246) +,S(d2781bf,4b059449,75da8b83,cf82b43d,b5ee89fc,168186bf,74f22321,30678492,76fe1955,18e00974,6af44fb3,6e623f4b,fd0ae8b6,474e91c3,3ec364a3,91049d49) +,S(e4445252,dc6acf3c,f7b3663d,7176d46b,efe95e40,3063d1a1,68fd5742,2795a986,cf7613dd,eb200aef,e665bcf5,a6dd7fe3,cab6ede3,877f6a28,22b6620d,1eecda68) +,S(51f59cbe,68f523b5,b435633c,ce11c0fc,ed2129f3,5d392b2a,ef789044,722052f5,10403f8b,e7024887,3c4f0aa8,e4d0fe46,11f34268,178368e9,1eba8e4,d9b318fa) +,S(7d1806b8,410ec629,77fc41c8,8c86ebcc,74da9a0f,c6b35c43,68d069a2,c8c974f4,efe8890f,5aab9bd,c2da2956,bc089b58,b67ffd79,957ce0dd,9a53da,b26d68b5) +,S(add67544,8cb87ade,f8cfc3c1,85fbce12,e0d1525b,812fb5d0,2420481d,b22dde96,bb80527c,b22a3bf6,728ba305,bbff36ce,c6b44d68,9cf79ffb,93030460,8f461174) +,S(ceb2968f,fcc7b751,f9b560bf,b08f40ec,ba1395fc,9a3f63f1,65a1be1,862c3dae,67727357,b2dc8d2a,47606c1e,ae61ce54,1d79ba49,4a5e310,e0435b,383cf088) +,S(3a03b03a,1348cf01,afc60665,26b6fcbf,b3977ecd,7c17725c,a9f0c494,f31223d0,3e71c6e0,7da0635a,a2fadf5b,78c892c4,c95f794a,88594b72,183a9b2,4dcbe7a6) +,S(42ac22a8,c708f2fa,559f9b9c,fc7ca705,4356d18a,95a69137,bb8b8b17,2b771aa,ef669b2,85ed6abb,477dc2f2,bf0517f3,794c371,9ba5b267,8f622eb4,aa4ce374) +,S(b2ded9ee,ab06f1c4,19c5aad,5163e731,722c4923,818185c0,d99d6a8b,dc603a06,23bb4786,f84e6c75,e565a708,2600585c,e1fd60a2,b935bb4d,390d07ee,5e8646dc) +,S(ea1faab0,bb0b6cd5,454e63a3,d1546747,cf9707c6,48e4837f,3fc6b6f,a2ccc5a8,8fddc23e,651ebf4d,89e54148,2043fc34,7c26b66c,b2ad2d80,1fd2fb7e,77d4058a) +,S(487920c5,14ce0cf0,9d939ce5,81e13409,9fcad06d,3e6974f0,a3b5925,12cb3a86,db447217,8a92e9f8,65a1b022,3baf54fd,618a87a1,15887f02,11cddbe,986dc927) +,S(6828652,aa506655,60349cdf,132ca8c8,9a6f9a1b,8baf6adf,64e10c12,9ef9217b,9615f563,20db2860,9062aca4,7a136e85,40ebab0,3f8b5f7f,93bf0369,aeee0cc4) +,S(b66a66f1,9c94868b,3e45139c,ef9d7586,1a6d06db,72027814,83dbefbb,41157a94,5c0763d8,8aa90b2f,ee04eab9,fb96b280,5ed5f7e0,58c9805b,64fb17ae,9a0f224a) +,S(5318b13d,a824dbec,eced642d,d742ca30,479874e,31c1013d,d770a0b3,c9ddcd9f,13093540,9b5d56cc,d5004948,3bb22009,4b7d5a5b,6d357bd,76b79117,458e60ef) +,S(6bef6b9,555f29fe,b5277c48,863c990,8f1fe2c9,5b4b45fd,8c5eb482,3ef4964c,36c00eed,4281871,fc5e15d7,7acfe3c2,6793455b,9b3607dd,71f3e163,9790e3a2) +,S(7972f783,4c010837,89b92a08,d21b8b8a,b6ff6ef7,92eabe32,fa1c92da,4c8dba6,2c3352f3,f458a538,f8228321,c1adf9d4,a2df9f8b,bd22675b,afbdf041,9538f1e3) +,S(7f03f087,168dfd24,7a93e840,e68cd221,f02735d3,93d777ec,c9d2c9ff,7cb0228b,663dc741,fc8e98d0,7dece960,5b506b5d,a5f2af72,1b64480b,1dd3e7b5,a0ee7743) +,S(d06ffe61,973be011,2f17bec,6f4dedda,9b9d310,4ed22ed7,d4c2f009,843d2c04,175f409c,edd739dc,5adea16a,187a951b,d42df4de,d1aabfae,d56f1ac5,26cda8f5) +,S(9094c510,3a34209b,45f7062e,ec65c14b,72fa75f4,13999117,38cd7f6b,792119d9,da541e51,7211db66,272f8f8d,413e1717,13cf499f,6c9cd694,b98e53df,45e9d501) +,S(22bb9cee,8a5b99fc,d7ac7d67,5e232183,3f71a72d,55248a3b,451699f4,9f151eba,841e7292,93a98297,ebf2e9a6,6d106325,265d91cf,f7e4c245,845e5a40,522b94a0) +,S(b548dacc,944b013e,d50dfadd,fb84173f,d5829b2f,69a2242b,c152684e,7b8a3ecc,6c7b70fb,9ae39eaf,583939b1,1a1b8aad,673b18f2,7568d2e0,b2680b19,8c662a88) +,S(34f46e0a,2281a617,f8ab78c3,86ed27d,f837f23e,e986dd20,bf705daa,1b0188d,4ce4df08,bbf1e735,6760e05c,6e5d3f3e,664fea9d,6e1a72d6,bad17bbf,a3c089d5) +,S(3063ee2e,3834842b,84cce6d9,50ba5afa,1d645c5d,e0acdb00,a9cc4ce3,c266f649,b08247a,f79c2be1,a0e4e021,7c9ccfde,f76d1e46,65f3185e,8623aaf8,63bff9b6) +,S(d7232b55,3740b2fd,464a919c,46168ef1,e09638e3,65117eea,979130aa,d40fc525,f842c256,38e84dbf,47d13fcb,1763a296,47b72908,1b522623,4e307dc0,1a402ded) +,S(6025e82,e2ec643c,32b10e6f,62ef310a,11576957,96c10f8d,b099dd4e,1dd8cffa,1e5aeacc,cc5c5455,d542dc51,b12fa3d1,75061cc9,45931548,7467d09d,945e2596) +,S(141d07cc,256c4d2d,44ddb7f2,ef720aa8,4bef767b,3597ba32,3b39915c,1d84f175,a842c439,9366b2a1,91e850df,333c9cd4,6c983158,cc57989c,588c305a,1144ead8) +,S(ba4ad105,6ffde493,54d13149,31846519,86fee8b2,728917ec,bd1a0112,b986a90c,97ac53,f84e995d,92d14f0b,b156f47c,be5b6506,2a7f8b07,7e235da2,808029c2) +,S(5a6ed894,bae212f3,eb44304d,98ffba1,4a944cde,ccd12517,5e52d767,5e0f801d,d8160707,d43053e1,e5806d7a,bc17563c,ffaf92da,ac51deee,55fcb9ef,715b08d5) +,S(d82a95b6,56bd1abe,50933a3f,e291526,7b257807,c8cde42e,5f1648c7,f35c4f3b,748904dd,3157bb07,65f94c18,7528c74f,ac639a36,be18963b,676b9b37,83f54591) +,S(df7c9de9,8e748188,74967340,34a76645,c1e55bed,3a90fc65,dfd9726d,a2bd3826,3bd77fe3,e4a4018f,3e256ca0,2bb5c8b1,b783b729,21ce329a,fff9caf6,b530b1fe) +,S(d3ae1d68,4614c95b,51cc4af5,a9e8a05c,4ad7eb4a,ba4a65a8,9a151b96,d91bdb68,a24cad0c,ad0ba98a,860d6d74,aea57c3c,23780812,5fb2356b,b55f0bf2,95e25e67) +,S(ca85c924,48a27be2,2b68c9f4,c9ab431a,b380ccec,2439c8b1,944c234c,bd865758,c53177d7,6b16e0a4,389e1e32,7072d460,90e92f48,22043bea,924fab46,4023038c) +,S(34309aa3,4e9fda0d,f2c20da2,3e8f8f31,eebea096,1e769c92,6c4251bd,5440bb04,f655cca9,d5811164,577d0525,2f5605d1,b4e2b6d4,dd5540be,aefed4ae,84caf3e6) +,S(40212a4a,b2bfcac9,62442617,8a807655,185d2929,7e26c437,4879903b,41307b3e,26624605,6ceb4509,b481df00,79e25f1c,4dfea60e,a3768e8a,462ac273,8166dfea) +,S(2e441425,d121e140,5d2118b9,7a3b305a,fcf62e91,4f24bb72,46e92aeb,1ebdd152,c3c7a567,51dfd709,9535d07,23778131,692dfdb1,7584d9c0,c8fed42e,5eb662f5) +,S(31539912,9e59668b,49d9b8bd,5ee66b2f,aa727ff4,96f457f4,33400a20,b5242b5d,5e90a20f,a700c59c,fb0ca2cc,ae3f2837,6dde44c0,3cf6af64,365e4cd3,b4e6b3b4) +,S(3b586e2c,30eb1959,bd5171ad,54b81c36,cda6b0ec,b5e77b41,a7b5b0d3,e1ed4ba,6782aac,d675436c,d969f413,c471edf8,ff7d89a,d0a07575,16bb695b,19ef40fc) +,S(f4299c9e,b3453201,375b972f,fa39f01d,90c68625,a63c9d06,513fc9c,8c623fc3,cbd0e2a5,f9b9ebd3,f482a5bb,c5c17894,d5c320e0,28744292,31c94fe5,55ecf68e) +,S(96d35f35,eee50b32,32371acd,99d6db8e,d2b7c4df,1f62b867,5559543b,785503d2,c0e8bce8,8bee1e02,29a5d2ac,113c9f2a,feff0260,869ecde0,7cdb1cd6,3ba5f73f) +,S(ad3036ec,e2bcae0d,1fc34680,be2d293f,b40d9133,905af375,1ff89c49,2c183e1f,2c0773d4,bd30833d,24222f89,3a4f5e8,cd6abe06,63d66d63,2d5fb832,4c260f9a) +,S(a0f340da,909f04df,70a33195,1c48901f,6353f4d0,4d22f99b,3763f567,be9d207f,caa1d9de,46a37b8d,623a39a8,6792475c,bf3bd694,597351ac,515ecc8b,1a4fe78b) +,S(2adc4874,af0b3cfc,675421b9,5369ff0f,950b55d1,331aeee3,dcd0adf3,859be4d,6ad9fd3f,a840d02e,ddc01fa0,ffa61bf9,4dc1db4c,ec733976,bf1c6e41,bdd92b79) +,S(b9222a37,c14456a1,cf931410,d96cd84f,a304b9f8,5a96edd7,6e67f928,43536175,4002a875,dfc4cda7,4ff7145e,ca46aab8,8cfc5ed4,53b34eeb,1e5dd859,4793e3d3) +,S(530bae23,c3796fbf,76f86f6a,d8b59b80,801328b7,6c46e8f1,cba6398b,270919f8,d4d3b34c,6701f07,bc47d1d9,fb868fc2,b46ff397,2086cbc0,517e29d7,38a7964c) +,S(a7f2ce51,5b932506,c740e84,40f254d0,e0da57bf,4d9199f6,acfa3664,ec36b823,4946fc41,3ef1b3f1,86a2c781,6c05ce7f,803fc7de,a00fbc2e,7e6c4688,fcddadae) +,S(8ff613d1,1c81c4c9,fda3860,cbaa71f8,eab9ac3,d9263f31,55317949,d6ac1d53,54f683d1,fd382305,b2193554,bd3a6d2f,bfbb99b7,5d9c3e1b,876c3b52,e2bdef49) +,S(6fb3bc81,5ef3d8a0,d7aa1a69,dccbdfb6,3f7a58c4,7a12f98d,b09cc105,4306422d,798fe7ac,f9fc71ad,7023a8e2,c9376161,8fa632cb,ec607109,d28a63a3,9a4228f9) +,S(c32aa38e,bf0ec0ac,61bbea0d,d2c633c0,bd49ddae,d77179c7,e0098c8f,4488da8,e01f5fa5,1107cf4c,f4c1c5,78c3b5f3,a1baa059,90bb8913,9e2abb30,2e9e5042) +,S(ef5e1efb,d1ede40a,810434ae,833a4681,5f9021f9,41ef1d82,fb8c1399,16c4933a,94a9cd1,5f160c1f,4f85ec8,16fd1834,9cf19f8e,96ac9247,695ca37a,dbad6ef9) +,S(8e3aacf5,33de35e,4f923b36,c378ea57,7a5aa477,70ea6390,f92c73d,50fbfed2,49cd7650,ff69ed12,b96ac3b1,420784,7677497,b731e0ce,316eed65,7e40e014) +,S(84521790,204c64cb,e026152f,474b0da2,25703d93,25943821,7d66f20e,3ae0d06b,c1da2f7e,438fbc82,57f7fe4c,7e49b73a,446a19ae,775e7f1d,20c8fa31,edc3bd20) +,S(604cd900,97fe538d,b27cc2cf,a2d6c79,692f2e2b,c1b1b23d,1f0eb949,df560697,914d5288,29765538,d77f8e04,182c50f,d2d9aecb,2412ce47,4888d666,46b2258d) +,S(c13a44bc,9a27e241,5531b8a0,5570e66e,6763b3bf,a80e00bf,59d6124f,b3d67858,71557bf0,4c261c2b,19196d0e,9e459f3a,f629cc19,84f411,31835400,3a60563) +,S(14f3399d,5204b654,23d9318e,4adf3af9,dc43099,eb43b719,eb4644a4,897ffbff,13808cff,4c096655,72329034,d489433d,976cc011,d1a30ced,702ba4e9,5892422f) +,S(bd84d712,43dea7fe,f151b207,5d03d2ee,170488ab,4175965b,128411f3,f592a36d,57a1c602,408fee75,ea9e683f,43d7a984,bf03d66a,37513c50,5cbec6c6,6d286fc0) +,S(f319d4a9,b5f2c1ed,6619ae06,e5aaabe4,13a2a6c,265283fe,4a51c7ca,6c49b572,bd9d90c2,2a369a03,1856ca9c,86426171,db952537,edbb1ba8,93fd16ea,35aa43b2) +,S(9649689a,424ec9f5,aa4cf4c8,45588046,c50eaf9f,700d0412,83e02640,9a80c61b,5cba8567,95b0440a,53e0d1c6,ad560364,c398dda9,d2e97a1f,e594355e,1402fa33) +,S(45fe12f5,a340034f,381764b0,adf503b3,8026374,70eb26e9,6f4181e2,68cb1c38,ab7715c4,52e5581b,ed940fde,355892f2,b8a16a3d,d322123c,14a57a23,72f244c1) +,S(98bcd24c,3222401a,a4683213,5a378790,6c9812ee,989b8f52,fa522c6,d39adcc7,14c7cd38,7bb5191c,f49bbe41,52af8b03,7135a6a,bc7e4697,35a72a38,e44433a4) +,S(6dc56512,7be34196,a7653057,f79b854,83c275ab,65c05c2e,83663da5,31d7d652,a6839fcc,ba7db053,affaf5a9,4c95676f,9d65eba3,c2474472,cadbde17,2ad3692b) +,S(7a7f66de,b04492d1,fd2fb9aa,dbc4ea7b,ae5a3801,47b86e68,60513a57,eb6b10f,fffe4f2,b722f36f,1e555671,90d84d9a,39b819e,fb7a3436,29b83adf,f6045f05) +,S(aaf9f188,3865e2f7,eb500794,3888b4ec,9013371,f7162947,2cf52adb,bb5288b,df634c19,e7496b4b,478bc10c,7c00ac8a,5fc24dc6,4f193355,f0fa9c62,d6048243) +,S(6304b1d7,56988ca,25afb747,476acbad,2e5afa5c,86136090,c86a82a0,e52b373,91a2d829,2357463b,ea1a7cb,33d79be5,8bd71732,f1d2cc13,ba4b2724,6fd39f05) +,S(6eb17147,2aad3d80,bf4e06af,a36e37ee,5fed9c18,bbaa1c4f,5cd871dd,c526515b,6afd615a,e2512fa,70c9670f,b56682d0,f304a0c,6a23d1ab,fe958a7c,2a4173fe) +,S(9113b613,ba815ca6,43e7bf66,27dd71d9,8c2d40ea,5c00f596,6216ca9d,d32b1ce,8790dac5,bce4100b,8e4e5135,66a104ac,b55d3ca4,3d6ea083,d23716d3,3894de2d) +,S(5c7af38b,8821169e,2f7e2edf,18b3b73a,c387e6f6,a8ef1cdb,441f5a13,284f365,e12619ea,e2a07b8b,c55dda85,6cd37927,d448cf2a,f4028459,2e371e10,8e91358b) +,S(becf19e9,23fa7b4,1cca0efd,937a5b11,e227fd18,22743fff,80c42692,6631ff0,3213c209,9f255eee,93323d1f,b440cbb7,64e86f1a,4021b82c,485517b0,3440ed4a) +,S(f22954be,430be6ab,34dfbf92,264c2406,df3a0a7,19ed45a4,d09cb161,4abb0c91,a60397e3,ef3308a8,12527744,311673b,c18bc43f,352dba00,4dcd4124,7b84846b) +,S(c50ae8c7,61776e62,5156f9fe,7d863c36,79d43126,c5f3fee7,5b837a38,1803e770,be966bf5,99b67e6c,ffc421a5,e6d12e4b,cbde12,4d84eadd,781ea562,aac7dab8) +,S(c6c3a65f,e849c113,748e9b6b,c9a9419c,1a919503,36201a6d,f989bc5f,fa07d359,fc205b83,b4764ded,ef58082,11eef120,4d9fa959,4d5b7086,c2fa2c9c,3e56f37c) +,S(73466c27,8d5f13ad,dc7da10c,b8855c23,9069f57b,6ec5e80c,e27191aa,196d9b1a,7e7e2cfb,533cfe9b,24243cd6,7680ade2,35abddc3,d8d00faa,c8adae39,15ddf6a1) +,S(6315ac10,35eaf884,8919bb56,759f7013,92fe442c,771a42dd,dab88ab0,2fb0df2d,a49d2c97,6e0e66b4,50e8511f,d91f0f71,a7516fcb,70ca61a0,98f135ed,8e19396e) +,S(fa195146,fdb7ddbc,41daa8c5,98e1f811,36c33eb3,bf23dfc2,3f20b36,1c8e7bca,324fc70b,38677dd4,c8e7bd82,4b836d0e,738dd757,ca408ff1,95bdd5d2,bcb5cef2) +,S(ac40bc48,87734bd,51247e91,43e5e188,e5e2bfa4,2e3c0392,f39755f7,3952bcad,75904ebf,603c2f07,4eef9435,eb22c663,1279afad,94d3bbca,d5349881,7dc4a472) +,S(15b7e1ef,d2c41c6d,416b6dd2,5f6612f0,a0acb0c4,1b161b4b,c40c5ad2,a92604f6,a13da672,17501833,4c4057d8,85be8fb1,ceb6d369,6979b805,83fc0a37,bc27a0a3) +,S(adf4aea6,6fff705d,5bff3c84,93e8870,4ba4019f,ed550cc5,c203439,ff8512b3,53ad2122,30528e1f,4fee43c9,2369dca4,3d071177,3ccfff26,aa16a502,6206a3d1) +,S(da0d5286,fc261d92,1095e1cf,f67e729b,501f21b9,45e8d84f,ba2c80ff,335d105c,5632ce7a,e8d9ce7d,aac7cf26,2ef7bd27,418613c,b288e793,35a0a638,6abd83f9) +,S(463fd081,daf02794,18b95a48,bcb8dfef,3a9dc67c,2237d2e5,dded1522,f714d6d6,3de4cc76,7126750e,4038fb43,bf6af17,63ebc0b2,ef56ba8c,a376b815,6b4f28b0) +,S(15e37b9f,58ce033c,2f0ad85,d49f8207,2e2547c7,91c7ec52,a8606974,2badb586,38816873,5e37c957,c18ad56a,e7ab81ca,8fd89a2d,1710f032,54393ae,a046490d) +,S(2e121b79,765d4f4e,8e7aa8df,5c70e4b3,716b976e,9e4536c0,dbb3b5ae,4044a345,d7ba59fa,97a7d6b3,8b70ecea,312cb01,f9016b33,c160d1e8,d33fdf50,ffcbc928) +,S(8d4c7aa3,c59dede0,bc146b10,38f8f823,8182c4a7,1cde0d8,e17005e8,64ad29db,78aa343a,28a48f90,edd7fb31,849d6d3e,fdb4c677,73cb523f,82c0b187,2c17f44e) +,S(1ae12a1a,19a5d1,3b2f42c,35a385,f8302721,86a67801,c22818f6,94438c58,5cb421bd,990ddad8,16de9439,87bd96e7,e1ddfc5a,8d0f86f7,b30e3483,897815d3) +,S(7a6f584c,d48be9eb,a7b73377,8a173d5e,3833aed3,a603a226,741ae1f3,a1c21bbc,5995b840,bcb6cc28,b83d048,9faa5dbb,c6a93190,46b11b13,59827266,d177d27c) +,S(2dc5ccb4,8f78db2,3f0933f0,ef67bea0,53c7f2a6,80788bad,b71526a4,f55fe758,9e4743f9,ddb46206,a5449942,71743b44,713522f5,f358ab47,34c9afcd,f528736a) +,S(f7a5219c,75dc4426,4322bda0,3b603127,26a6b3aa,c471f42,a33072b4,a44fc9bb,9e1feda5,5da277d5,cb18381a,4471939b,a12c692a,a40ab965,fd03949c,d8124ab3) +,S(a1bc311a,e29e3926,5b3be1ba,23d7ffee,df0de8af,fba089c,fad797dd,8ba67c68,9abf66cf,109821c7,68c98164,b0598068,c4590d7e,b9ff4c53,594af6e8,e0ceee8d) +,S(2c2ffba2,e3fee930,fe8d061,3bbb8290,22429fc6,654d5ed3,61eb8720,1d6b92c5,ed89f28a,bad9510b,acf44d05,5934cc64,ac7e94e,2dbc72c9,986e6cb0,acbe434c) +,S(1d157e13,97f6b22c,3dbfe448,d1168b8a,bc963f86,bb3bda29,a8011f07,d2cb5ffc,bc7cfad1,e1633419,cef1c82c,d0249ee8,4c38b3f9,d628337b,aacf0449,4ca3c0b4) +,S(1b9e9723,56f8a4c9,5e8d5fbc,8e2f2cc5,99c77d62,90b2fd44,2e63b3e6,dc33c772,59db6eef,988386fd,21e8a772,a1fe048f,95ba77a5,122253c0,1a52830a,bc12742d) +,S(80460a3b,16fd9ba2,250b07fb,c77f2526,bec5488d,7d6c45ae,6f480308,39af48fb,bc7648a7,744347ad,e17da2e0,5cd1ce10,db704acd,26c81fbc,db251ff1,c6fdef10) +,S(c6903a0d,d8c26c8c,cdb675b5,972aa050,6912813c,a6f0c8b0,2edfb792,491222e7,f4759b4e,9db7f1db,7c8a1c38,dfe76d86,c3778423,29280aec,12b1be06,d8e49ff5) +,S(95ab78f3,a4268c8b,39aa645d,8c9fbfd5,b92469b0,809d6369,3d57d076,d305a241,8df4c8d0,ed85ac01,a965d580,78f36a5f,690aaa03,d9479d1,b2297a52,53e3c6) +,S(bafa3ae4,40a4b8d0,40862a18,cca806fe,8cd0e780,483b396e,41543486,db91d9b0,d7bd60f0,c0ee0b8e,38aad85a,85a42ede,56c66d54,ee73d371,a3bb7cdc,e93df670) +,S(7cbf5d9e,3bea6864,b87ae157,fee6e352,dc3a4f32,6e7cfc68,377fd199,a6dbb71b,42d082e7,5d55eb62,9771942e,3801c27a,fd80b502,a997ccc5,9f5a95f7,d7276999) +,S(9108db63,751b8a9e,f4870524,f1198f9c,c4d3ec20,af54119d,6011f263,de13ca29,8c1d6c47,aa016e83,d2c8f76c,df0935b1,b6f909b5,bf5c90b,f4203493,22c711d2) +,S(a705e7eb,c20ac4d7,43be660b,d61340c,9629d069,1b08ef01,a59e78e8,2d8b347,1ed8ec9,13ba19e0,851e1912,5854433a,f71b21d8,df64958c,afc5d7aa,cd29ccfd) +,S(b75483e1,60144d46,78af9a3d,701c4ffc,fe154c49,fad45ece,3945289d,c3781c3,a4c0455,13cfa7fb,ba2931cf,722fc569,87b2c79b,c4b94313,5a458d34,5ee16d57) +,S(c59aff26,bd4fdb9,37468989,763376f5,64abce,c00d54e3,45b6dc2d,9b7b76cc,c396c194,db446fb1,a22e86e1,8a37981e,d114e675,474bd24c,784daa96,91eb15b3) +,S(2f6701c7,815ba7c6,37c4649d,d05a31d3,e909d8ba,931e7136,1e0662e,3ea19985,937eb67b,9262bbd5,12a21e90,393f9e65,66e03203,55c77afe,cb98d1f4,bd25e8c3) +,S(170d1e33,f0862542,dc72b1b2,950ba98a,812cc308,74ecd0c9,23ce7c09,ed7cf9b3,5148ef39,68ff35f7,a633ba7,dab6cdaa,54dc6f87,510f1a7d,4d3019f0,c00bc868) +,S(743b02d,b02301db,453f8694,cd517c6b,433f609c,e205cac,6f6a49b8,55890708,7af6350d,56130339,1a9ddce5,f566b9b2,9c10d535,d5183850,6c743124,8dcd707c) +,S(ba6df8bc,f9d83988,4991c6fb,e0aa0ecb,d0b6cdc4,d0e372f,3ec83b51,da6a6570,7d27b176,3a01a8bd,fb7dbe0b,226dee6a,73ed3644,807ca33a,1f5fee61,cc161fcd) +,S(d3b63a2c,364f01db,6a6f384b,4bba6c2f,2fa46a02,86090826,1c8b5045,4b92ca5f,df6b9cfe,fe7aa14,a38bc44b,8b627b6,e0ceeca7,1720d630,cc4fabf7,b020e2d2) +,S(638c2b1e,374ff6b3,4b634f4c,bf363bbb,55d66c8c,af4d3fed,43cf436f,a1daa7f3,9854873,37b48f32,4feda3a1,f267dbe8,8831aedf,d7bd36c8,6cc8d862,857507d7) +,S(1749ab4a,607d7864,5c4cbd30,4bc34c06,94a7a636,9c8c8f8c,be4602bd,16c4b4b8,465bff86,b459b842,dfae4df6,99be1758,8b1aca97,f9802827,d892792c,748ac92) +,S(9071a5ff,ff23ae4e,2d81b2fe,d341bfa9,cb86ae6e,ed32870d,d549af20,a1db9feb,c0e3d4e8,277d099a,9801ddf3,3e513aa9,1761218a,967d530f,21d70894,ea7d38ab) +,S(91bc0055,28e2d857,ac644954,242fe0c7,bbaadcf2,9119837e,898244e3,a305ba6b,6fb63758,3aefe600,46997b88,315d5a24,683ff955,94398694,be304dcd,8b588f67) +,S(9663913d,48c865f6,312fe51b,6e8e7c6c,51ae397b,d141aaf2,df3d6ff5,edebbe68,c6251751,7e24d81d,b1da26a6,6627fbb3,28b5f818,6477b34,43f7c36a,918753bb) +,S(e88e8702,7843b941,61c71887,9a0a9a90,cc0d6dd3,8329c73c,a18acca4,3f44ffc9,7e31ae63,1f9c927f,bd055f58,e682935a,3225e778,9221a062,f2826d86,97143a61) +,S(5ad27e79,dee03a52,be771a9c,ded6a02a,58337f26,be810d1a,f8cc5ddc,f1eeb917,91c18e01,dbee34e9,a5302a7a,11bfa7fd,ac852e1b,e53aec2d,3138259b,bb53e6f6) +,S(bb7f4694,f2935a5e,8711ae47,bccca550,cee2632,4347b468,58e40c74,49111ead,d9f94c14,332ad75a,59dc784b,d674e8c6,146a2b74,566ac1ef,b0ca17dd,9eec8d74) +,S(ff77ae06,e0624cf5,f6d905ee,b0de7281,5377c026,3e01b6c4,a139e4ac,3304e82c,1f27acf9,933d08b6,f0892199,11805d14,1611a318,661e6e7c,75014b84,cc35c395) +,S(84c15fb3,1b12d39e,ae59ce0d,f8646e9a,67c85492,8d6c498b,4b836be2,eb19a060,6aea0d78,d54090e3,2216c3ae,be0dd433,a666e67b,44cce0ec,1609804f,fde33f74) +,S(9a48086a,8bb9b150,a6ccb966,3978f555,41db6d48,1b3d5266,e958cbd1,df9ee7ab,d8eb2109,13ee09e7,b5767661,13157905,b3cafed4,641903b6,da0ebc08,a0ce2f58) +,S(db32ad19,45f4d9f7,eff1b8c6,b87c5545,1b0c74d9,8f1c4ffc,6ff1e79a,21ab3033,2d56e65,a70bd23,617f85f1,4a5d0e4d,52aa6a70,a8594158,36d02d87,309c7eef) +,S(4de7f1aa,b9089f5c,b6d95b67,a04952b0,d4696cc7,6640e1da,f4bbf7d1,da985851,96f2cd33,5fd4b816,4499ddc5,5e81f5ea,c4db4399,17f2151a,f86d2ad1,e2b3612e) +,S(74bcfa73,5aa43e8d,1fb540fc,9922362b,21878c09,666bf541,a0e739b9,dcbfd1c6,84b26fc2,8b2a05cf,a67947d4,99c1ffe8,f94ed4d4,6ba583b7,cf26ee81,ba60bca8) +,S(9c596bcf,3d2e1df7,cd8d9807,8a1ab17b,f16e27d8,66cfb582,ef58498c,48ee0fc4,14dfc093,48f72d7d,af9e3599,8b5f18bd,5287f3f8,381308e0,63666028,dda9ac56) +,S(8e2a0d3b,2786d67c,a15cd72,4f2cc6dc,16cddfc5,cf080ef8,d9576b2e,e5b0ff9b,2ec4853c,c83c72d2,68555129,ce30c836,50fb59b5,17a3d61d,56ac4273,1b33ad06) +,S(25ec4838,5fee8fab,773498dd,85a23ec9,46c59839,14fb8a9f,d1239ea8,ade3f829,123bded6,5312030e,d4760047,1867aa4d,b7537239,b363b6cb,58ecff98,44986924) +,S(a1cd9872,7551e2c5,db7985b5,1698b97a,31b90d3d,32fbe60d,34f8ec2e,398df76e,40858565,aef814bb,8bd91fe1,410f9556,3f1bd170,345c1283,b64fb268,4980c398) +,S(bf7d3973,aae2cc31,e831390e,f2458e85,9ed6847,e9eefd6e,cf91e3d,4a4924ef,c2829fc4,2127b303,c801605f,35602b84,32357564,d9333ba8,d962d639,ef21a6aa) +,S(5a5299ef,e0d394ef,f4b510ba,96dc73bf,6332d358,e276bddd,527ec290,b73e3313,9ccd4618,17e18104,d324e1e8,1597eba0,cb6b475f,4e5f2e02,9cfd2d63,b613de79) +,S(90efd8cf,9e4b9d74,5cb36c13,20a8bb0d,b5cb8578,de49ea4b,c289043,e2ea810e,87a73339,aaceed7b,af47b5eb,97aca554,d80a4f78,d439ee14,4b2d0fb,f8df5e3a) +,S(3a174294,4db7f2d7,2498e2fc,e2026d5c,f8600ed6,958db97c,24ac0de8,417f6def,e60c566f,f3fd14e2,2ff0cdee,45c4c2,31797951,7aa0ca68,af91a5,e41f5f60) +,S(2213e0a1,3679040f,73e4d68c,bb941665,6ea83640,33abac76,2e64d509,48a97656,2b87ce0f,bec2728a,aae6586c,bc6d9a89,a4bb9b9a,211de522,cc909279,5860791c) +,S(382412a1,ab396523,8b06c47b,e941051a,bef3b215,16efb280,3ff3a1c4,824954c6,7cae8b14,1938312f,ba482980,ab9073da,c66c1ca0,d746231e,8ba8f331,4cfd9cea) +,S(3d76bfac,89c1269d,53b6ded6,bf13c785,361b1fa3,84be2ea0,d582ee43,f06de56a,18b1cdbf,96d12859,bf887b89,6d8f2e6c,a08b892c,688c7687,8294974d,af6d6284) +,S(715b18a0,68d86cfd,41a20541,847b36a3,ed7814c1,b5528751,927042f9,85e7ab70,2b8e0228,b0a4d39b,85a3d3d4,f5a10db,c6825659,f4b967c,361da449,24890911) +,S(a31a10ea,e38e6a00,ab4f004,8b45c8a3,e57856b0,d0b64e47,fa90e234,e17943f9,5d4b5150,7d3f3a82,87e5d51,6bb30b89,2cb9f540,96aa5962,1f77e028,480e594c) +,S(b397f965,1dbbb6db,34708066,a1743511,d4a16332,95f53772,72c43a6d,6e21c1b0,673fd327,45bea2fa,748a759,30679b2,471e4004,423dc8b3,f34f95ea,f88d2b8d) +,S(2e4b865b,c1245325,1ad8b3f6,e8776bd1,a5878b93,ec34f847,65af84fc,146d9919,b3e7d479,661ce034,50094432,c538f55f,c8be8693,73a91fd0,350c8089,ec0d76f) +,S(9a38c790,7c0751e5,ba750bc3,485b4a4,72b661c3,63a3980e,feeaf59a,98533a43,dad7ac3b,65493036,d30068ca,72808000,d54b42d6,d263de93,a1ced3d8,fa82ca71) +,S(5599d251,3b3c8337,f8af18ea,70d1d2e1,83f3e363,63735357,2b3f6cba,b4371d9b,7ccddd9e,ec8dd507,69f6f633,cc70488a,6a4e322a,2716218c,5de50fae,45398dfe) +,S(610c472,4699e983,530e8a32,b60a8077,c7f60226,cc3b2010,e49b2e30,f2a98afd,42157de0,fe6d8e53,17a3f565,fa450f53,8755679,6d98c7a7,ba0b3e96,844d25d1) +,S(89c082e1,1438e2b5,6a4bff8b,cadaf804,c9d11fa0,8f47bff5,39cd0741,76f24a59,f4dad4e6,19ccf814,e899d48a,904a88e0,6db6ab19,a2e06a7,b359db9d,cd0ad0ec) +,S(bf810fed,48d1ec4f,77cf3732,ed10a44,df9e4540,2e1a1e1,6b29c82e,32537565,6657fd30,337e294,fa31d30b,409b426d,546a47cf,38cd23bd,75aafaa0,b527d66e) +,S(3b1317b8,bb3c9c91,964ce369,19ea811b,90a8bd5d,ebadabc2,d4d1af3b,50fb3524,cfd5988a,78aa555b,595531f6,5fb0e3f1,e756c1db,626cf60b,4c1b17e6,caca603f) +,S(88520439,61558d79,7b084143,783d9e03,791e6183,c932d1a4,17eed30e,bcec0bd1,271097c4,4c723ab1,c1d13e9,93b7b232,235957ef,f21a13b1,2b3d302a,ed46ba1) +,S(6c420c30,343cf500,da0cc05a,c315d3c3,45ed8aa8,e0551bb0,32fbc512,732f34d7,fb3c0808,698d38f8,3310fe1c,15587cf7,137bef2f,666229b5,bc789dff,1b2f1111) +,S(b12e21e0,e060fcf5,580d75ef,22e8a800,33e2c19,487d0660,615f7d51,ba00e430,3931d63c,a6e0da2b,295bd22a,4bbb27f5,fb52d89a,3c4ac36,795747e6,e74ce944) +,S(4fb9da54,4da95655,c0de5654,f68c2820,a7884734,4bad6934,e671d56,e7bfe11d,16586701,9bc89b28,f5fdec40,78a2a51d,6e087ee4,29456cf3,d258b97c,209d44fb) +,S(18f8f6ef,23bf75f7,a7811025,5babbeff,792109ca,5a550472,245552e4,35588e7a,e712abe6,3a374f25,fd4b18c8,4b1451fd,ea77692a,43c0e3b1,cf3469fc,7a9acd55) +,S(d730d167,1bd36654,7188c07f,78d82ed5,6775d7ef,bd0967e6,1abddea7,7ed6073f,49c4bf3c,ad4e31df,d840045c,aa8be15e,43f90196,818e30f7,b379c74c,1d866553) +,S(a915fb72,7d4c40ab,547c983c,24a4f019,1f5e89ce,7ab0636,e903aef7,592208a6,fdf28a3f,6557f27,5813ccf3,8d927f40,b910c5a9,ddc3c83,14a48eb1,6a5956be) +,S(7abe8d85,bd95c580,cd9d532c,c1295f5a,ad5168d7,e3dcaf28,b19f5e76,95107157,694abe9b,9a6ed0d4,c3ac1db8,79b49038,6fe9d2f9,443647d1,91533fbf,aae3cc02) +,S(93a83e8d,c80c7cd7,64dafff1,edd148b2,9c336de7,cf978b14,fbd35625,c965942c,91ea5775,db50bf9,a7e3f79b,77c7d5b2,deddfa08,a1d455e6,fa21e547,356c634f) +,S(a334ed87,dfd2e1fd,699ce1f0,a09fe921,487f8321,6cdcf75d,fc8d7e5d,cbbd1af1,42a0874e,d148bd32,ba4324ae,eb0260cc,7f0d20f3,f43388,38904640,8f7ae7e0) +,S(811597c1,f4129ac6,e150c231,bfd8e4b8,4ad441ff,2c698486,affd4ac4,c22bf8b5,5f7fb47e,11b476cd,acce1552,95ac000f,3acb3838,6ad3c6ac,191dc12b,d1e299dd) +,S(cf754ed2,47c9eb9d,24f3129c,4b4cefd8,94b5e58b,832f158e,2e82911f,8177a59f,262cb605,2991777e,bd1ed5de,d2e7ce29,76dd73d1,ad4c9bf4,9fb5d8fc,fbe4a3d1) +,S(290ec604,a6b4d816,ffe75715,419b0f33,452530dc,94f48883,b3982161,4592b229,4235db3d,df2e3f1,b61ac412,a90f543a,ddcd519,4f4a315e,d889b32c,ec8f76e9) +,S(1f90b869,4bf9ffb5,7697ee72,29b7f20b,52b47997,35efd7f4,1e4b9102,51886858,dc3d7a68,abaf70b3,c84d533c,48ef38f6,9be7d26a,13c4e834,5a7bee29,c6d3c87a) +,S(ea20b4c0,931087a3,44d5642f,6c018aea,2da5f189,a4d25968,96110281,88545ba4,f9adfa96,f945e745,88d2ab41,e32ef5a,76b370f3,f0362e57,129c3998,e32cedeb) +,S(b8bb88c1,21948c01,3a61fbf9,bae86eda,28e9bb4c,6870b0f1,7109e5b7,c00cf4e1,90685129,8607d7ac,25bea5c6,1cc4feb4,4148e07d,2fc3fe7e,449773d4,8245019b) +,S(2345a244,f2f411f6,ef091904,81eb9083,9128f7d8,67e8b910,124d1e44,d116c529,a8eaf530,c69b792,d6595967,4f23b9,b0e239bf,b69d05ea,466f9f5c,9bd5affb) +,S(75aee824,38bb03a9,b9208cc9,b725f13a,1fee1aca,20ed129e,e5ecdd59,b92c89cc,fb6bc8a0,fe0f684,ce4cf153,2053312f,b97aa1e3,739fab8d,407db39a,737ee51b) +,S(ca46d3eb,c981eb1,764867,c99cd7fc,c414bdf0,31b2c9a8,96829ebd,f31e2e36,46363355,862a64b,97541f7a,7fc5cf51,ecae168c,c6530db5,3a7f2394,2e87bb96) +,S(4fc1d258,575c4830,bc7a03d7,6a259406,7d633e9a,da2c734f,45ce4d9f,99dbbe7d,cbdc616d,fed2bae2,9312c5bd,9e195bb,bfdeb2c8,d1aee5e8,cae8fa0a,4bd6baac) +,S(4edae08,54365c27,f6ad83b3,8f68c7d3,f0a09c6c,153791d4,df98b5f2,3f0b9be,cdc8a485,8ac66847,c732a2c5,4be86af8,d6d637f3,e62c7802,dd71c7b9,2eb18223) +,S(624dca60,16beeb28,c35bef5f,e97d320d,d725619c,3faa7ca5,ed79d491,72b12469,cc33b49a,6fd125cc,98d65a81,c0713cb6,2a7e687,276e7fb,641e2f76,52646c12) +,S(6738d38e,760e50b0,6a18a9eb,b5e3676e,c38f3487,e34461bf,2e5d52c,5bbd6b4a,c3ce0343,9b9f624e,a92eecc9,860ff680,63a907d5,b57c43e,465ead5b,bef5e709) +,S(bcb74527,10f178a9,eb48655e,3d373b56,8f02036c,e9ab826,4ab7cee8,52f7f9c0,4e928392,52ee05de,587c91d4,6eaf5e5,92ef41e1,5a2b5c2a,da9e4512,c25bb416) +,S(3375c7ca,1b5fb4d1,1838ffa0,c21d7e03,de34ca5c,92bb4592,bb4598c4,2b490382,8c284e32,f016d186,875b87d3,3bf4271,4b9013c8,ea159634,b39b7365,7ea837de) +,S(c347409b,336c0677,ee95de61,f93fad4,738268d5,6f31059d,380af075,60e496bf,37dfcc4c,c13bbfa,5df43c69,eda0c68c,42de70d6,ab64a9db,a4ed22f,1cec3fdb) +,S(305ea4ad,ce72ff73,976544d7,a3deb346,452ce997,f280c3a6,106b7c9e,3b9bf5d6,bfc33cd4,ca178310,c4caba86,5b87477,77e9e572,4278f0f0,56b86a86,1f2e13ea) +,S(8333f714,70e57841,312a33bb,4f1463df,d237651d,fec4e1a6,40c0ebe7,478c5a9f,d39b03d1,11657c2e,19ad78d,3e1208ef,f8505695,1bc67098,b891c42e,b427bcb7) +,S(500af03f,eba3b646,a0690f0,74cac255,804c8f8a,c2aaa1f5,f5cee4e4,41913e7,5e67542e,8a8f0411,e9985a76,97ba626d,f0607f66,9f9c11da,826b47b1,a52cfa2a) +,S(e440385,c16bc15,5023cab3,3e7a48e0,5e7e0c57,e0fe848f,26b146ab,237a3abc,dd8ac982,b627fc9d,7fd83c26,51805de3,d7369ef9,2e970664,8744badb,3db060aa) +,S(83eef59b,b0a26fa6,60c94e45,c097bcfc,8622c37,cec46eb3,f9e93aab,d0fa6438,8b51311c,ff685570,d9aa7a66,5b47a3ee,b6bc05c0,a2709421,c73ef814,bc703723) +,S(930cdc95,f723a7eb,d698c1f2,46692f39,e1add95e,f8cf84ca,171d4700,2a7d759c,ff161a2,31fc8964,5bb6dbb9,f595daf4,86cf01c1,944021ff,6ff793fc,2613bfbc) +,S(4f7cb0a2,c83f3520,86085b19,a6e5ccc1,b0beb700,35637e6e,8a79fd78,337c2616,aa32192b,82831ed,a9412f4d,6ac6d148,f68ef492,5b438cca,12a73b37,b4a8fb74) +,S(c7238fe3,17590be1,d8403ca1,3eacc6b1,71cccad7,e2e8f659,f11357ad,70590424,59079180,90bdc74d,aa06abf0,940519fd,d09f24b8,88c15cb,a632b814,fe8910fd) +,S(c6afda17,e6582427,95cd34e3,edefb600,70c73737,2d99f6c,b2b8dd3c,99874880,d805463d,15d185e8,b4461293,b6e6f6fc,a0b58a49,eefd4d0,11ef48cd,da5a16a4) +,S(4a14de26,ab18ebc3,414c6856,1a77d62a,221821f,8c216496,700bfbb8,21d95b2d,8037ef39,d1f190b1,a24078e3,e554ad1,cf86d5cd,f731478d,b34b6b5c,c9174314) +,S(cbcd6696,547fc496,8c6e7ba5,4caa4a74,43764852,43f479e5,558e2be5,77bbad00,8e0099e8,559db5d,dba0bc72,21505ef,d42c1c95,876539ab,19dedb2e,8e561482) +,S(37f168b6,4b41be85,46abd1a4,7a5ab3dd,5d690661,e41b16c3,87025106,caa4e2ae,8c617348,bce3bbf7,54a121b2,37342794,5d734e38,da08066,c4de59da,e60b5c89) +,S(3aba7081,55dbb35e,dc4a3fd1,1db54446,a7501ea0,d67b0286,8d2e6d77,99162697,829502c4,2dd1105c,cb046e0d,41cd68b4,38437394,b93f17de,5534f014,270c4602) +,S(84842bf5,fb46c697,44eca720,7b1be6b4,e5f5809e,eaeb3e9b,1058eca5,4498cafe,f4210fd5,b49f6484,2efe3089,327673d7,95642ad1,73b6840c,7323b7c4,16d61fb5) +,S(88383dee,ee0db44b,959edd96,feec2bdc,73d6ab56,9333fb3c,d4b18b01,ed8af5e3,8f0cf362,7aba3ceb,104009f0,baf84ed1,f57402e,fc330f10,6e0b45d5,4e0626ed) +,S(ad0a97ec,2197c5d7,4e8eb9bf,9798a24b,aa7c3e5f,ad6ac263,1acd5109,5178d8f9,72b352a7,445db832,2e33c93,5619e7d3,266f254,64a4c4f0,96051ebb,1e37065d) +,S(9b727e4,d107418e,1f62b499,99c1f8ec,55ef6e91,a9a10ec6,8905be97,33903d85,27d6456f,5ea52aee,a190dc81,647ce31d,15ac8c36,de65685b,4784eaad,4a32e41c) +,S(e86bbef3,8d740a44,7a2cb0d0,a89c2106,d85299f8,c38fa540,a7075efd,3a02fe03,8a9f75db,ecc532b6,ade5be9,b855a27e,185895ca,d6a6549c,f6c4c1c8,7e151b04) +,S(b72ac468,9209207,2cb735e7,d424a18c,5ef097c9,3a9b96ef,1ab7e29,d0f379de,89111544,27b03712,e2236fcb,12cbad35,98ef794f,b8141913,aeb1ad8d,ed9e6467) +,S(f8f5819b,203b2bfd,ab2dc532,53277103,2f9caf34,c53c7ec7,7253b314,2e731ff3,433fa831,3dab76fc,aad5bafb,12362126,6d8d7c09,9d513ee1,1b633c6e,e3f1e96e) +,S(8e4a8890,a15da3a6,c14d2df7,d09f6157,d5dc95f7,4f9518de,8aecbc4d,c0ac62f2,c06063ce,1d3c2a24,2494a1cd,db381513,6200827b,78ed080e,3cd14f4,5e545acc) +,S(b1f6dbe2,ad730748,f906d1d9,160996cb,f3f2450c,fa656856,e34f2481,40767081,ee6b2a03,3f3245da,76d01bc6,e61afcc6,94a9e64a,4f0256bc,62acb5a1,82e45ae2) +,S(2139dedc,a8365bce,b9c49cfe,3834e4d4,27a46750,d6b0f0a3,4e7672d3,d12509b4,b20a9101,b63be2a5,28340f58,893e10d9,d3c63fc7,77b5c6e6,a9a67193,13725870) +,S(da3aa585,85c00e41,87db0240,547ff665,376aebea,a24e5aa6,67d477f3,c4d3b914,92405390,d255ccfe,a6bc7dad,4fb1676b,c741e530,cebdaa3e,669bfabb,d1c4780a) +,S(89c47de6,84e1eb9f,a5828e65,dd17a852,7226a75c,3d113102,97f5b8f6,5b67b210,970bc229,d60b00a7,c2354a70,37ec8569,ad615faf,ac77ddab,f5847a09,c1fa57ea) +,S(48ea67ce,db716a95,6b6066f1,e5e9fc71,5994a4f6,f4e2cedc,bf0c09f,4f8c8fd1,513e45eb,159f9865,5ac821d0,e18f4e06,fa3cb8e0,9d45c3f1,8d3c3bfc,48ff3da) +,S(c5ae6651,ded2c479,a3b6c9b2,44fa35f1,2eb1eaf3,fc78b529,42ffab7c,4e33a1cf,ae6ad807,435d4a9e,e8bedcb6,3bc804e7,e67e9418,6494bc8e,384b29e2,31af8cb2) +,S(24e485b6,7c27af76,e18ed116,90c9dc90,73a0b80b,e93f5381,6669e2d,3c0175c9,fcb183d8,696805be,789d83fb,a197fd24,996d3542,b2f3eb5a,c207950d,f9c079b9) +,S(3bb47a19,b1bdc527,a8fe262a,ecee2d7f,1e772627,e5cd5c70,eb2a8c39,f1977628,2d10cdb9,91fb5042,905f822b,68846390,a922da1a,7b7b313c,47edaa6,85217fa3) +,S(7d4233a,cd50bad8,71e0587d,e10204f,6013a784,b65b6540,73307364,5f3078da,be85d3d,dca838a,1f35ca11,1333e943,fb498cdc,63258ac8,74bd6acf,2b934b77) +,S(b5d0d791,b6685aee,2f599505,d01bd3,d41d5a1c,531ac7fc,e6c33b97,cb0ed264,47fbad30,413084aa,619d7bd6,62502cfb,9648e64b,e757d6eb,24fd5e2d,6b1629) +,S(bd6178e5,b1c558ba,432a89a0,482585ca,40ea9922,7c94ce,23f5b081,d606c7f0,9cdc6bf9,d32b98cc,73bad7b8,c08914dd,b9a8c937,913eecca,fff72dae,fa1cddfd) +,S(13ca834c,a5f41671,ce7f0978,a310420d,6a82fc38,fcbd997b,88a1e79a,66fb9375,ae32a0df,7f269c8a,744e409e,16ec3d8a,eaac151a,99bdd7e5,50287005,6f48f6a) +,S(96322802,87add117,4040f802,ed5c71c8,5f924398,1ee78480,4f16e5f3,f0fecb6,2d07de71,cfda4b43,44baf838,7933a372,3d536411,7e4c0f84,2b402156,3f9b56b4) +,S(961efbd2,ae687808,bbe275a2,cba1769d,4ca537a4,1046c5ab,ff269c1f,b9477ad1,9d22a10a,294666eb,5c816bf3,c4dc2c44,d7836202,e15bed89,e3d28822,8193251d) +,S(4e7162a3,8926282,84ac4df5,88301fc8,5d91c869,2fb854a3,ffd34c47,7b216ee4,6e6dc88c,d91b9702,cb38cff,2d6a38e1,d512fb46,9d85f878,8510ed97,2704ba27) +,S(6c1f707d,51b1d3f4,ab261d52,6d0106e4,3e0a5b1c,76a59868,62e9d97c,bc6e3e38,d2563843,5dc8b102,c8f4ee13,dc345f85,289cd499,9bcfbe20,1bf3dc7e,7dae1f74) +,S(3ae98e6f,49d363fc,8416f965,4e57beeb,d37635fa,4e78d8eb,77caae69,f2201798,ffd95480,fcef0686,9ee781ef,cd2693f0,e6be2e27,f207411c,39a18465,4d2c3cae) +,S(eaf94fc3,fae4eed6,35382e91,2cf963da,2bbbda8c,3197e522,e0054cf1,4cdcd5b8,a838a439,3d4bc0ee,a57e6235,5dacfd97,7d70afc9,16e61209,10d13fad,7c1c22ec) +,S(7eb52e7f,bbf5b802,2fa6d66d,801c3632,2c195b6a,4eae4050,5295612a,a184e2c8,b4591e5c,c66521e1,b991f63f,456306ce,cb2e7568,5227788f,a2e9a75a,bd58b384) +,S(d22b8dd1,6ff24603,5fda0ebc,77129644,a8469373,99b2c7a2,60d8e985,d4f2a215,7a9a092,e8473776,6718e2b3,9068e163,e06618b1,6d6bc046,b33e2d76,8649611f) +,S(8f6eff05,f746f0e0,9b328ec6,2089e575,ca64175e,d950ae4b,c67a7ce0,83afa1b2,3681243f,ca51a6c5,7ac92100,f38df1fa,d1093fd6,ecd53231,bfc01fcd,89e38305) +,S(ac9ba060,ff520335,9a4111f5,c8c536e8,e27a79d7,33528ef7,9da7b21,36243a07,2ba3cf76,3dfc0860,ff3333bc,e660ebb9,c4e5a988,909880d4,1ceb4219,89f23f77) +,S(d6896830,62bec27c,815191b3,4addf36a,cee1cfe7,475d34d4,d83a83cc,6dce3992,9f038f46,529fb86,30b19b08,db11c630,70593570,70bc51b4,fc1ced24,f43fbd00) +,S(e815019a,f6923eb7,cf963f2d,476db569,bb04cc7d,78a0bbf5,3b422ee1,caf43b70,984c16e6,593d8642,de871694,800ca14a,a103a61,20b91c17,4e06567e,2d731c95) +,S(3670e86e,e9aaf036,40136ef7,3344152d,c792c8d5,a064c0eb,a511e14c,41d6f7b9,eac7dcca,e491cbb7,cd27a522,a7db96cc,7d300c35,7413433b,9bcd430e,152fcd33) +,S(3d03f819,4802149c,c6428aad,851bd879,189c557d,cc9ab1ff,8e2e4cdf,f0723308,1f56ada,27f2c923,27ed2fe5,88d8736a,fb68259a,1d5b1c14,7f85669a,eaf47615) +,S(bf2ac011,9e417837,d4815c9f,74351869,4395ae40,77429013,7aab32,980748c2,17a47bb9,6f7a3e7a,31b5a8ab,2adc9fe8,98fd94d2,9ea7c3b4,a5b6c5bc,9c367ac9) +,S(c72eea14,ef09c0e6,85c22840,95e73e0b,6aafa64f,9410eb0c,97f6ad4f,64dea571,9a46c93d,256f67e4,1ccb609f,4ce94390,df9c91,38a9d2db,9b993456,13346031) +,S(25463446,6da168cb,2d816bdc,3c9e3a43,7ca3463f,8c8a83f3,62704996,c1bb5b96,3d69cc73,6aca2a,cb97e1a3,cd57be14,243e458b,305df59b,3e3a67f9,fdc17b6e) +,S(b40dbb03,7062dfd7,7151013e,7cb0a921,15096a04,c39f7e2e,66188aaa,368c49d2,34923fac,7b1a4570,e824c3c5,2edd775f,ecf93573,2198f801,8f617db6,474fb519) +,S(23fba417,ff084b42,afddc4a5,c070305b,553ed7ce,1311e5d,4c1f7b2,772c9aeb,af4a342d,f9346ead,89b3caa6,6a3f187f,96f2b470,c0a26752,76e895cf,c5ca4adf) +,S(2e7ecc9e,d4f21af3,a3118799,d1ab7e3,a30d011,edc53e33,93dfdc5,6e80927b,b3959fdc,f6c4876,36813bfd,e7e83b4d,7ee0f1c3,54e0491,f3195b4b,aee2df72) +,S(1c68c34c,8aaa0f2c,7daa215a,961180af,3938599b,1324aec8,7a1b3841,710db2c3,d8d68179,71acb598,5cceec45,fc5a754e,e002d09d,4be45e8,ab6d2bdc,ad094b24) +,S(fc0ff082,ca6708eb,9b9294b6,fabef142,2cddb5a3,611dffc6,f06c05e7,f1eeddea,6f67fbb2,df22611,e2f1e796,c67d6c3d,14b17f98,2b6e39a2,7bef257a,d6955b70) +,S(fa683ae5,8dc85748,1a7ddedc,708865b9,267e9fe2,e485a243,1d005e47,64130548,4d9759ed,76326207,891a9649,3b4d2307,b9694fe2,c15e2327,a79dced,db8277e2) +,S(3cfead76,5ef36c3,fa9903bf,6d43cdd2,46ee8fb3,3fa1d13e,42fe9818,6d299009,85a18ee1,da24ebc7,3dd87aa3,ca1e7565,8f868fec,50225e2c,d5b5480e,cc0ccd49) +,S(487fdda7,364dacf6,2fcec7a3,fc2dfd27,c38079ef,d48f0220,85c62746,2c8ac658,1479200c,b06440a7,186a946f,83a137d5,7e5368b6,7ef8a66a,9540a131,e4bc9b8f) +,S(f3e413ff,81c2ee72,74c079f3,451f41c4,bbe1e51,f7c3a1ca,513392bc,9edd487e,46544553,79a33a0b,13fe5904,504cce47,55d0e2e5,6c4163a5,14d5b902,906caf54) +,S(d93692b0,53ad5194,b70b9be0,1dd80926,e2c93754,328b3eb7,bc85c45c,63e6258d,7221837b,3276bd2f,9ba0981d,883cd68d,97d2ce77,9dfa1073,d59b01e5,df311ec6) +,S(e85af4c7,42fd5424,2964eb26,b531fd77,550d3b2,eb64bf39,e97de203,ffe2d28,8368322c,e40ffca8,49ff1a53,76cacb17,98fbf4c4,38f58690,54dcb642,f805b9f1) +,S(9e4e35a2,d913a2e3,bc931c59,298ef6bb,3893b086,ee40c978,430d8436,49fce4ab,ccca94d7,d4bad95e,c15b19e7,9555579,d47cec60,9f42112d,3d992035,6d31f7be) +,S(7d72793c,dcd78afe,23e8b000,41d67983,6528b1f2,6978e262,4d022416,7ac5e741,1189804a,2558ff1f,63a7ee1f,ad5f8ab7,50a412ff,a83a9783,996e5f26,637100fa) +,S(3b8dd2bd,ca65d7de,6a7c55bb,47dd5ef1,5ce6f533,96ab335c,84c0c9a8,4e06a7da,2ac78cf3,e1baa981,b27dfc9f,e1b7cd2b,1ed4c37,f96af6e4,99bca1f7,de62aac) +,S(128aa84b,3d920356,db64bc95,b52d4c8f,3054cf5b,22c6039d,1d21ea60,b2274c44,5a748932,6a98019,824a9d7e,cbea2de0,5ccbd9f5,a2cc5dd3,ee0b3dcd,a92e8758) +,S(de8b04b2,c761b876,4c41c344,a3b57c41,fb62e6b4,ba25ab19,8fc2ded,2849e151,134ae148,d89de734,a222b656,b878fedc,886d5928,fc6bfab7,bdd0a74c,3753f1fb) +,S(f643025f,9fc3824a,6ce70677,23475aa8,8df24d59,682f5a6c,b18b1c0d,5e297017,24eb7c8b,e3de50a7,cb473bd8,81db3515,8f9484f8,8574af66,a15f920d,3986a049) +,S(ce2e5454,54324680,7035594b,1aabcf6d,5d8cad20,729a787a,7e01b61b,106fdf19,f3a2e5d6,745a16cb,3403a768,c1de8f73,cd26388c,c10f296f,f00cf9f5,209b1555) +,S(bafd293,dd071a21,25a857c2,6acc5afb,ea79093d,3c9771c3,cd3b875e,b74b3280,2d56802f,b8f26950,4adb2a51,a91ad2f0,d1ee6501,f8c9279c,6a27b524,8dddbb70) +,S(10e0909,5dc7ae30,9f1afbfe,1bcad71f,c66489ad,56256c3c,a648f554,c000f414,47a5a7cd,968dd158,e8436399,40c30179,3807713b,c09fb0d,83c6c851,c6b7029e) +,S(73b8149b,4e7bf050,b6cd9f4,93c85bf7,61062329,2dec76a3,2d7054b0,c794da63,44cb915f,b6813c71,dff3830b,ebb6888b,76312778,897903f1,d9988c37,3c362bbb) +,S(59a5dcce,9773617e,73c529da,6015ca40,bf923956,339a366b,84a69e23,d0a60fd4,4c62abdb,c4453ffa,2b806a6b,b823fbc1,59f57f23,2234c8da,a471192d,3081ee09) +,S(1ad2aae4,f571c6ff,1abfd45,35b3d085,1aeabdbd,b3a340f6,7bef0f78,f7afa954,1c394b1c,94539758,85d72498,b36a3be5,56b4f80,8ef52977,b0157885,649549b5) +,S(2403f66c,a6a7d60a,254b51e9,32cfb700,f37ad73d,193f25df,6024bd51,d0f337dc,9ad75d44,24517aff,7df86c,9bf4f315,cb40199d,4a2a5d5c,74044c4a,9c79d69f) +,S(f3139f4b,50dba596,87080a7e,b19bcc34,2b58df9,26576bc7,dfdc8e7,4914c524,fbaad228,ab34e8d4,451cf6b3,a1dbe225,bd46bfc4,8a4bcd94,10c150fc,5636c687) +,S(ec415f8a,81b1f559,714dd790,2441eb9b,c5c7bcbd,e87190c,c109ca04,a3e75149,9b90bd46,d27e0200,369fe995,3b50752f,d00b9472,fd31383a,f01bb88f,b223bc5b) +,S(5e336f17,264a53e7,d0899bf8,6f676440,f83ea0c1,5d53fae4,f19a71f6,998a3ca2,2a86fd9a,8358daac,828d42e7,9c6c0fa5,bf977cfe,56899db3,27ea32fc,cdf09f3d) +,S(98828ad8,a6ee9ca1,37f9ec2f,f0cac84f,5e664e09,68455773,3f8fbab3,ec38570d,ae7ba3cb,e617e9e1,6e67f34,f69af863,3e69c9ca,1f46b182,88126f16,9ec0e548) +,S(d6a7c2b7,169348d5,1d98f0c,27464732,6f67ad5a,4809acb1,6b783fae,36808436,698cc098,ebe430c5,4dc74fe5,dc300f64,7ac0a002,4109e002,d2f43ffe,89fb3d9e) +,S(9bf4a4ea,9fc4b731,5391b653,1d3ad3a0,f0fbaf59,b99f76aa,7807c442,f8e63bef,e69fbade,598fccf4,6f1f0931,f37f3c55,900dcc50,64e54ae4,191796fd,a4791068) +,S(38a09628,aa450076,3826f384,7e63121c,d6e9e645,f6ee0f,8cffb106,508021d0,5460c8df,56fb7d67,992928c7,fd969f95,d29de37c,14c2c9a2,2d60d661,6cb7c383) +,S(dcf8afd8,ee73bc87,c17c97c6,807fd3e6,a9c57a1b,cca058a1,ad24f2c2,6e9728b6,efee9612,3e78cfcf,1dc3132a,fa4ef492,12369710,fa2e8e99,3cc70b48,6ba78709) +,S(9c26ca1b,f52eb7b3,a0d40dc,cf546591,38945c43,41e0c611,40d0662d,1033420f,4bfbeeaa,ece9f2e,e11cc672,24749c3d,e12be6d2,d3d21d90,ed742d,31541ec) +,S(2209194a,6a3b76,1ddb2b1,3bca12f,3697248,4bec29e3,102d7734,9832ae6d,7dde1b85,cd00a08d,38035f68,fdef0576,c458476a,93eddfc7,4bd4927a,cbd64543) +,S(6ffff27c,ce5efa3,a8f8af72,edcfc736,9874abf8,5d15e1f4,cc71a186,9a7a29f0,27a0e163,bcdea297,1abe52a8,a92fe191,38b839f4,67772c83,81504328,fad94a2e) +,S(34e91b7e,e2c5f407,8a9d1ec4,7e2b2c02,1b8480b4,8c3b6e47,19cacc36,b4250382,8d60947a,42d88728,e0d67d2e,e9a0559b,55cf3887,92c733a7,3ed50b25,3706cb82) +,S(3b2de979,9106333a,98504128,e2135e5d,7deae86c,1c921c58,529aff14,12fd62ff,4ba40985,8c5b594d,f4bf5e81,bd7eccc6,3a7bbbaf,fb32926e,9a47503e,f031d86a) +,S(fbd44acd,9e593ac2,12fd1f64,c4e18d31,c3b3fffc,ae1c88c4,6aebaddd,ac608b2e,9f4feb5f,2ab68e29,ca64f458,d162dabc,7a05c552,2e38c4cb,74529e3e,41d805d6) +,S(d21c3321,bb6ba2aa,dc0cd52b,be68716,864ce1f8,44107374,c0481c0d,91729b39,2b51f18,7b95725e,f74deb00,8e9d96f4,f1a9bbb5,c9aa28fb,275f700,bd964566) +,S(ea2d29,f85855db,78808749,9ed79c2c,614406c3,adf64663,8756b60f,16166578,33971e79,39faeedf,15b14607,37fa7112,7d6f49aa,fc423f63,783e808d,1fd10369) +,S(2a1a6709,301ba6ef,c5ad4958,e29fa73a,caea18e9,e01214a9,65291977,8bec874a,56c971a8,d29d6641,9c05fcc9,9b0c0156,a1ebd007,bcab2cec,b9e920fc,85f796b9) +,S(aa870dd5,6434af8a,36d89551,a7109cac,c1d0c5d9,f5e43630,17bba88c,579cfb6e,d1fab1ea,cb5652f3,cd4d5836,82aa3cb,3b1b6854,c0ec1157,41486028,73625dd5) +,S(e979a3bc,5165f53a,60042a1b,11e8062a,e74f35f2,d70aab2d,b250a4a6,f4507507,4de86928,b9dfb2d,8509a1c4,8efdfbd7,a5432933,e3f12789,cd51ed9b,b967ff1e) +,S(620979cd,f37b1cc5,b1fe3e01,f761b8a2,ffc29bfe,8afb7cae,7ded5cd,37f97829,cc971616,6ad43824,4c6be489,7cac0b95,afe5ed6b,11be673f,d67f0d89,6d58f7d8) +,S(90a5df7d,986198fa,70152c7f,e2abc14e,2c80d5e,6b75b42b,aa042e03,f3aa00b3,77e0ad,2f3e1eed,472a543c,3852925b,327e3eb7,aae59030,2d7e12a7,862201d5) +,S(17802140,e10d8be8,ef210b69,b7fa1afc,16a85d9f,ea1150a6,c6e5e234,cf638446,af4982e7,fdcb7a8d,b6478b54,a5bd7cd7,c48d1dbd,2a3d003,7e87b6e,13c9f5f0) +,S(1f2b8d3c,fe515cd2,53f66b0d,59fc51b1,4cf75ac5,d9c7b4e8,4c75d92b,f0380b8,6902b31,19f38b04,5b979f94,cc140481,d4e6f7ad,b3efe12c,4de1bfcb,c71be72a) +,S(aee51eec,9e5dedb2,1619d114,5594ea7b,27e21a4b,37accccc,d5fe522d,2e46d29e,d40ef18,86725504,fca42c2c,b8b80dc2,c1028b71,1bf3fb18,9fdd579d,f7f0ff8a) +,S(40fe6d08,b714ee12,a4d643b4,21b331c4,fbac165b,d7cd88fc,ce35a873,875bed7f,c32f1cd6,3083a976,6b7dd8ee,7133831e,6796709a,c67953d3,4ec5d8df,922d2743) +,S(5425c555,8a6f9068,946f7075,db347e6f,cb6c723f,c5ff829f,1634fb2,13b3c820,c218eba,db4e3ad6,e41a28ba,129a0c1f,8626ae2a,16839b77,ba12257a,a6aa2318) +,S(83663c0e,f024207e,16ba74c5,ca534d37,733a7dfb,b2d531bb,21a28243,d0572a00,2699a48b,dc9af87e,bf18c9a0,929359cc,d7f2f137,b84fc070,2bc3497f,69c83d45) +,S(bf931d61,e72f217c,3b820b63,282f5d4a,c929134,efa06fff,88480f86,f6247535,82553f48,bc40dc42,cb8bb098,ecb84af6,5cb3956a,aae7bd0d,cfa9e181,de93c21a) +,S(1c6e0876,3c2242ab,17e2885a,537bef4,e8920782,9c573d00,64fb83e4,910312c1,b7c14efb,bea753a7,5d8a597c,7f1a716f,8ae4298f,379aa64e,d070ab2c,28e66185) +,S(9a2cf62f,9eb5810c,31b27614,1592f201,1eb158a,3d123610,99af0b77,7d42c0be,159ac59d,728d1c49,843a0207,e47d1cec,7b9b773a,9ddaad52,3ff27d78,d2767d7f) +,S(3596950b,393a56e3,443c3b93,b4f8288a,7faa8d74,d26cab37,dd7a5b4f,6a9e183f,f99bbbdf,c566b3e4,e65dd63a,15c727a4,403cbe85,202e45f4,ea5d3462,173babb7) +,S(6f556cb8,f5da812,24bbe628,6dc4e379,ec12b124,21cd95d8,6bb1d580,a13b5c37,eeee3133,1dc89e57,48fdf10a,1fc0693f,1c09aece,aa27dcbe,919d60f7,4748d656) +,S(b952070f,fe60bf94,cb14bf68,b37e3e54,bd9b8063,ec8cd4d5,20408756,bd318bff,57996eae,d38f066e,cb3f8113,aab5e4f7,f322859e,1fc075cd,69ad1ce4,3fc48ff9) +,S(dbecc166,8d94b2b4,8d37c133,26fffedc,3e690b76,87563677,23352ac,1eddc2a5,f124cd3c,e42f56d8,a7588145,e53931e0,ce8b79ca,ba5195f7,3dd861c9,9027823f) +,S(12dde570,aae95b1c,7cbc2da1,ed2d936,bbfa5155,f61979b3,e67057d1,5fe5e1db,328711a9,c30e4455,e2716e60,3bedf4d9,8fa25d1b,e892ac72,b5be17c2,b25ca311) +,S(64bb18aa,fbfb7def,3493c920,d2faf9c5,57856710,766b0bea,275db46f,f8271282,717c28ed,e9284844,49cd0db7,180cc5fa,20ba79d9,41c979bc,c0a8f274,3e2e2901) +,S(a798f4c9,bb3ef48f,d2ba3df5,2b040611,d0d5532b,9134aa28,e21b5585,d2379ee7,91f903cc,a4628477,b208bdba,59ecd246,bc50be70,34620a44,b55e0e9c,214e563e) +,S(7bfc5902,58f80000,82946c5f,a1a352aa,808fcc59,4bf2ed7f,bc6dad3d,8bab00ed,17b5dde0,3bf6a814,efbed180,3e5f1950,59499cc7,13e727d2,405e0748,2f912cd6) +,S(23bda819,978c9a09,864a1c53,e0fed0d6,ddc18777,b614780a,1c6fe08e,c35a820f,aab57ee2,7d226f88,6e55b8a7,c4d7a245,7badf96c,9312906a,3efe2cfd,3292af5a) +,S(8f649152,54696b2c,1804467b,e1619ba9,d37cd874,88db9418,1259d69e,135e1455,b05b39ff,241ac0c7,7e2fad78,da585b09,c98815e1,b368040,d493bbf1,3ab54ac1) +,S(177ea3c9,e592ca96,e9e0ef4c,87cb00a1,cd51ccb5,1e1d5252,963cfd08,77f30331,57185496,db5e5971,24e55b14,1042ef30,99f206c4,67415318,ccbefdb3,b18cacff) +,S(16042996,9ab25db9,e4ee4231,7497a607,360bb1fc,96e74d66,cd87cab4,44af2f36,1dfa3d9c,c3acaa95,1f7ad037,bea4c20f,c311a8d4,c2ce9cb0,bd30078e,b0ff64ce) +,S(10210453,fae1b545,de76c4a1,4b6ea5b6,172ea157,1fa60696,25753231,2630e29b,ec8aa759,5708f292,72e2ebab,eed6bdaf,955b27a1,469758b8,44c2668c,42acf576) +,S(659574c0,39345165,e35deee5,ab4c3b09,5b873ba1,fa14283d,6bced3f1,d562abc1,1a8b17c1,bbd83037,b20fc2ed,495b890e,526461c7,8f0b8d61,e94a35f1,bac9520a) +,S(a8a00f02,174b4d3b,9ca825ba,88cb1ec4,4c188343,5478a00f,38025a5d,1e2cfe58,fdac00d9,55b6872a,62a36dd1,cf666423,b4dd6b4f,4558cca6,d931a1b1,1e42561f) +,S(3ac8f67e,b4c2c3a8,2cd90fce,f612ec7e,7daa1072,6e4f8da3,57d9ab96,56e30414,15f71b5d,17dece22,a696fbf0,2e360226,bfaf685b,6575fb99,68fd52fe,7a21f9d1) +,S(87c56593,9fc7bce5,650e57d9,37e77642,a1ef510b,dadd117d,2732d316,a4f2ce19,59908fd3,e2b646ea,210606ed,c790851b,4077142c,ec7733f8,613dce61,3431570b) +,S(6531e4a7,f9cbc2f3,f0448bb7,f8250542,6cf5c7b9,d4a1ec6b,80098799,d5e43101,622a2201,f3c0f766,6e81be2f,22f1ea60,f4b45807,6800fae8,c353c283,b6ec3d7) +,S(e2a5b419,cf421b15,859aac6c,9beaefe6,29293f39,3beada53,e1ae567d,9bb7cb34,3c20b681,578aef9b,138f852e,90f49526,6d30f3b1,6ea2d3,23dee943,1af4a5af) +,S(d4e9df61,35cf81d2,21003bc6,e167b6c7,9e912c1c,2dcac775,f7d9d07d,6ffc7334,c8f54f41,513d7bde,3055c7f4,8436c812,898770da,754ed9ac,d6eaddfc,a80f6c21) +,S(b83ec734,10a1989b,988e2bfb,a46a421e,dc3ce47,d549d838,8970102f,98a6943e,4e0274ef,ea2966c5,200dbca,d8090f78,98bc66ea,44164fdb,31507ad1,e7e5db5b) +,S(ec57668e,12f0590b,e99a75c7,82c02ef9,88354f47,522c114d,bbdb9591,238d3eb,2b3c508e,81a3f696,389a5ec5,974a1bac,a572c042,69ad2709,25001215,c58ad7e) +,S(5acca76e,7578cb86,8d5ba7f0,aadfcf40,5ca4cc52,d778712b,785764a1,2afa01bc,7b0df4a4,5e048fae,65a0683b,ea0185ff,f867a578,179c153,5fa93561,54138359) +,S(bd563b91,6419f92c,43a7c2d,7042fa71,fd88e5e8,41115b5e,36a673cc,93de4e07,4ec5b773,b35a5579,b7c729fe,1cde3b32,ca34f97e,83b989b6,fe756597,3cbc840a) +,S(a070b055,839d93be,2b4ca25c,a4a6b133,5ef7d09a,268bce7a,fb1406c6,42e71025,d3fe8577,1241be24,ed209e8e,b5b28ec6,e3a0bf52,27f36036,3a7325c7,5eeb6acb) +,S(9c50323f,c758663,346c5863,84d5aef1,123b4338,eae00ec6,86aef2ed,e19b2f06,1dd9442c,cf588e6,6a3cebd7,bc68f2f,6f270d7b,bf01f088,a49a5ce6,da430229) +,S(90fe8f78,166e3f6c,2d19bd0,685fa2d0,1f6bf06c,2ea220ba,cedb0f22,ac9c558f,826d63c1,60fe3875,9c5887c9,cabe8f93,905fa1a5,e87a8272,670cfaa,ffeeba13) +,S(d2f44fa9,b704491e,69488c44,cc8ccd29,6addc630,2fc66900,1703afd9,a2a5d3b9,2de1d619,247636dd,ce1de2d9,75f14515,ec5807a8,c7c0cc4,5c7d03dc,fc19352b) +,S(17f1ce71,9e3dfefa,106e95e6,62786729,e8ec2bf6,25421ede,ca71ca14,5782b23a,62b51d27,74f514cb,5452cd29,e3907a7b,b39a9b9e,400d78bb,f6cca78f,cfc1aec5) +,S(5b1dcad4,5974f0ee,e99d7c23,83e48585,24428c94,daa956af,cd692c4d,5a63213f,6958ca48,f13e31aa,c52e531f,886da246,5ba713d4,748cf6e0,de16e0a0,366c33b4) +,S(ad284e67,a0682585,f18be40f,e966b605,8cc787be,c265606d,12f202e9,31f3067,c86ce860,ac3c3955,28321cb2,508e4da5,abb4dcfd,2844b95b,ede54ce5,b17e45b9) +,S(9ba71712,dee6f0a2,7352cfad,59f6c0ff,f133b1b1,37ecf884,a537b672,a4275c8c,b910f036,f906a419,21fd37d5,5e0273a5,332edf68,43dd8afa,43070542,b0849b40) +,S(cfc757d5,171c9d6a,ed8b9123,a2b4a405,d8e869fe,e80aebd,75255454,ee6319a9,43dc370a,c2eb8fb4,4f78940e,b41d614c,ccf2cd7c,c4f324a7,92ba37bc,7e48144e) +,S(b8288ec3,2e4a569,27df3e13,6e81498a,bc9a52af,80d74bd6,c85eead8,85e0c49d,a0d53c40,d2234ac2,df69605b,1422bd1e,4a1d77db,fb0d7269,13de63bc,a2f96078) +,S(3808cddc,497c9065,cec574e1,3b82a10,fe2323f6,65a99df,1e724221,3eeb9a79,eb046225,eb6aeac9,5659f1fe,75dd910d,2932cab6,9b4b810a,d83e7681,e0c32907) +,S(a1620bae,cc99c311,7b3889b4,7bca5d99,4ecfd67a,ad5a7461,646e55a5,19e46398,5cc4b5d2,edc5f7a8,54cc3054,1c7ec571,e06c59a,52bb5cf5,5882ec18,1b07c0fb) +,S(f1d942b2,bff74aa7,1f9cb411,91ee85d,e6955802,4a4d46eb,53ac02d1,32fefa93,19a564a2,8352bc48,a8dcd932,50274bd6,496cf488,6e2ba390,3cf91631,453f57ad) +,S(1a4e3836,a34db30,c47ed6d5,9ba6fb83,fcb7a5a,a0002ad7,1b93b154,a5fd566a,745e195f,d94900b0,aee3c5f8,7c6fc62e,fdddc18b,984900c4,20c77ba5,4141cff5) +,S(af37075f,866d7aa5,9371a08c,1e7aaa5c,d23ee51f,54428fb6,84c09b27,93b432,b32e8869,fbe53ef5,4250e803,facc93ff,a100a84,becaec5f,872d0548,da4ae9ad) +,S(88e39f4e,83a0d6ad,a1e01ac8,9021a7ea,fdd1f9de,740bc10a,4d572c46,fcee9330,a14f6881,bfaf691a,96249322,37522174,e8a17920,52da7bec,a6c77d4,5fb8a357) +,S(8e406574,a46e56a4,dd4cf780,9a7ecfad,5a1f9225,cfc2fbdc,6b7bfc84,a5ce14fd,bdc6108e,d5616bc2,3ed52c5a,1314642e,a9272707,20f00be9,62e48945,3550f167) +,S(aa31e1fc,118e00c9,8b8f561c,479c6427,8676356c,640304a8,3b687a34,8ad4a091,31c95ccc,2520c0db,7c7440ac,7e640be0,7a8ba4d9,c0020b1b,f31418fc,98d9eb17) +,S(ef278702,becf4f15,ad43fb6d,d0114077,d9f8bdc3,ff4c78d4,d0a2a15,8fe160b,377dcf3a,774d6416,58bf108a,87201b9f,f28c70fd,449a77f3,966ff441,d89246e1) +,S(d1903199,b9762761,fd02c4da,99fcc503,ac15332f,4be01b7f,a044c5c3,f848ebc7,662ca3d5,9ff2c5fe,a75ad64a,f7044a94,60abbc25,defd0a3,aa6b0e6f,2c7f4f3d) +,S(682ffe36,6838fa12,827ae055,7e38c12a,bb571227,47643f26,9cb9f1bc,922b07ab,4531b021,2e8fad86,6f6f67fb,d8eac752,1a360968,f0f9792c,a4568a55,855924e5) +,S(418fa3e0,b32be1f3,f06b96d9,728aeac3,3294ea8f,16f35139,76b30801,81d8e833,dc1608cb,68bb0480,859c9983,6baf9558,9451022e,b9966e0f,498245e0,2183ef80) +,S(e3f7dd7c,f5898b7b,174cd809,5c7c0890,4188bce4,bd6487ab,e974d979,f7faec2d,5ed5ca26,77d3bce0,15a6cb51,9922a55e,9ac1adc0,61bb0774,503e79e8,204441ea) +,S(810ecbe4,f43814f4,e658ce9,234d71b3,946556d7,2ba9c3f3,6fa04b,4dc25cf9,5fc57520,81397434,e395a752,560a8a76,252e6eea,b9ec067a,16befea4,1f1bf1ca) +,S(6dc50f0d,7e9518ab,fa16bcdb,f43ecb2f,745d5ee4,7269225e,36811819,6542b7ff,dfd5e416,2d596f5,b44602c2,20a87cfb,77473aca,6dde260f,856b3295,527bb87c) +,S(d39c9858,5ba2a859,a6fac25d,602c8e1,fb205a88,7aaf628f,5d1a210c,bc648fb5,6e92fc09,9df932e5,1bd0c6bd,5b6567f,61a1e8c3,93d69add,111e9320,661ad724) +,S(d7fd8580,59c1a86f,1b81c05b,ee63bda6,8ca93c4e,5ec99e91,88d9c7f4,40dae0d6,ceb56f19,cb3e5b39,64a4c58b,5cbeddcb,c736f0a1,8a2f8f3f,f0758f24,525a8fda) +,S(2e269d93,319a79d7,513e6f73,918638df,c4b1b7c,4c19ec83,b254d780,dc45fa87,650b6846,4e8537af,583cfc48,caaff61d,f31dc67d,5a48e0f8,5857d7c0,9bea1d70) +,S(19f396b7,7147937e,bef30096,44b292,2a6e3c6,5b5c929,22b73118,7120224,4751cc23,4d49e926,651a9a29,c2a8a55a,f7e4b25d,9cb4bd2e,83530bdf,494b131d) +,S(88f843b4,e3689ba0,264d6d4b,4d310afd,85de2ea1,6fbe7275,d33112c7,538a7c02,5dba8024,30d31ef1,412f2310,3ccc5951,66e903a,55bb2a35,8be1e715,27fe45eb) +,S(b1301f5e,1474083b,3828ba5a,837efc44,21990da5,275f6b1e,bf773b52,5ef14a1b,f9fa0af5,b508ca7f,2345d06c,5bf1e5e4,907f2b19,3681837a,802259e7,7208864) +,S(63bb3295,b57ac21c,4b96ae49,92b8f3c5,7c23743d,44ba348,195c5f58,d5360bb,f5e6a98,96683293,af6b9ded,f9f163ba,15b44017,120c61bc,c9e51758,f3ac793) +,S(dac9f722,7d0c012e,e3e1ce88,a99cddeb,9a23979,14c1cf1b,915dd41b,8ce27687,85bd90b7,7f2917c,cf1ebd47,3bfe04df,386d37de,69893cc6,9e79ae89,2255a12b) +,S(ef05aacc,a8082759,33719aa0,e0958f6e,4be6a6c2,3458f243,f84fb2df,9aa422f0,d6006b80,bf611afd,3f1ed523,988b3e8f,d44be73d,565ef30d,911e4226,ae546f23) +,S(dedf3d9c,807065ad,8c1e3714,f682ff86,4f6ae08d,6bc15773,53f27677,ff86783e,7bf6f3f,1f3e5ca6,6902639c,a0b071d9,50dc2331,52f1b47b,5c68923,9a1cccc6) +,S(3db2b461,d18b367f,cbb4aae0,7a73ce9,6c1b4cbe,140099f4,d3839764,7414b5da,515ead0b,a8ba7b25,9f04484a,4c55b532,5686b882,77562b99,21827706,a036a142) +,S(5516a03,3da8cc3e,28170010,5e8d57f3,daf5bb84,596c040a,3d2ae05b,41cecd4f,6bf44084,f0ad14a1,f129a267,e13b0717,6d2fd274,2d4be75a,2192e54,7ceeebf1) +,S(7bd86d5e,6fa18e26,cb0b6e2d,faf2a4d6,a784e831,2e7168bb,8e660e3d,92514c3c,9b634094,dcf4ade0,eb49eabb,a26334f1,7770bb94,b776de46,64d847fb,f1574afc) +,S(a24a8b40,d63c242c,3dcd2513,fbee3c04,cc15117e,aae40daf,b39c3373,e6a83bfe,49163714,afef59b3,b503eb73,ee091349,e599c563,bac4aa37,13d7a6d,6f5271a3) +,S(acbbcaec,8b01374b,454208a5,19051ebd,e5128307,785f7b25,706642f7,775a932b,102283d8,1bd1e9e7,e5641b8d,a701f51c,86047d76,51becfba,89478d8a,a6226572) +,S(efd5654,8f37a094,19f9fc11,6d818523,c4d76725,860d49e4,a1cd8434,a0cfda99,d521204b,da2b9db5,dfa47c9c,6ae4616a,52b3caad,5440e4f7,f1fc41ba,766bc180) +,S(15600bf3,ffca622,a163244f,47789725,b4c112b6,e3d7652f,fe829a6e,386041c3,b1a1ddb3,e2b0e170,8cc9e6db,13c7b111,93673cc8,397d9b59,5b4e8dea,a39cc5f7) +,S(8d14cebc,62f7cd8d,fc60c2de,d144c76f,55d9d6c1,be5380ca,1e1ecefc,5a85cc53,244bdd67,5030a8ec,af876f5,99a34f98,ef7388cf,e6705734,540f57f4,15641220) +,S(e3cb7328,29c6d09f,6ee940f6,5f30c79,7dbbc26b,b8b8c860,f6e07088,a485ebaa,e50a5c44,6768827d,ddd57e18,68064e61,51c8951e,e4cd920b,b664ca06,67096db0) +,S(9a18212d,45364ebd,3ecf4d76,3efcc66a,ebd17b7a,f8bb7f65,8679664e,16888e47,85297209,abebf492,ebeca275,bdf3bd2f,987d1e29,6bcee299,9d6e59e9,2b890143) +,S(43366191,859bb3bd,5b44f1a,d9076ec9,5439c082,c99f70d7,4363117,8020521b,9dd6d890,16666a3e,6bd8c0dc,76cc9792,fe5d76b8,c49cf9e3,75e031c8,bfae2556) +,S(1b77e51c,b11e514,29955ae2,37659c5b,a8050ddb,685e5b74,fbe38506,33bf9358,f198ea65,1ed4a743,a14fb905,fd0bfefd,843bb00d,675781de,5fb1552f,c7f382cd) +,S(7cc6fcc5,558cc6b7,156a62d2,e823631,e0b95e8c,57fb79d4,b4aa3ff4,9cb718cb,526becaa,c0c84e61,b3fc85f,60d028d2,eb90567e,319bd6f4,12b1a36b,9c3d30ac) +,S(18c1e51e,10a7dc5f,7783d511,6f2c0457,664ca2eb,6541ff29,591ee59b,f9f958cf,7f72840e,ccf4d03f,29ce01a6,725f6b97,d701a935,bce8a2b3,c1e44904,cf98a300) +,S(3b9335fb,b191bc9d,17efec98,77960307,2648edb7,2c427521,34df37b6,f1620c44,ca526125,8239c80a,1ae893b,268d28c2,b02d56f2,d03a8a7b,aca25def,34d6d694) +,S(f39660c7,ba9bf800,381b8ddb,d783dc6,acf8c183,95f9144f,688db8c9,9d7874f4,c5037625,2abd41f9,7574a607,52be4c31,e5ef2874,5e9d80a8,da2ea676,4bce5cc1) +,S(d1d2872e,ee06a4b5,d3b735b1,9db1d88e,cbbf9ba6,89c1e411,980b2cde,e70c86d6,dc5cd630,859fb9da,70f25023,5b7bf2ec,938743a,a9f04fd0,69d1d746,e3ebbfef) +,S(aa4f71b4,720f6470,8b6261a1,ee97bc7c,ee85b274,caf71a6a,1f7f000a,b06a1948,958fa65f,acdc42f6,c1e98f7d,f67ccc39,14dc5e8,d20bb872,32d7c898,e451c017) +,S(85904824,63e69633,42cd1e89,1502c309,d74e1722,fad8e766,d7e8f566,49bb732e,585601b5,5bfef406,ec5f71bb,6bae29c3,c5e77247,befbb549,5ced52a6,da64a2ff) +,S(dc899ab4,89a12ac4,4152ef63,48553ed8,29855449,c204327b,bcce6418,75df4d99,6791a965,65fb749c,44f133d4,b3c9c8d,77488831,c8a99ba9,7b66c9b,43deddb2) +,S(2fd60f25,3e0b4f1f,2589a096,f9a0ea3a,405906c2,690fce35,ac7df791,f106020d,872d52b0,1cbfc3b0,ff699950,5be33b51,64a7054,bb00e22a,9a87d692,dccbe782) +,S(a74830c1,7f816549,cd617b94,c0360b8c,9206b382,6782d351,650785a7,fb71212c,fa928b97,75b28be7,4e340cac,eabf866c,fe7e7299,7c276680,2d211b1b,389823b6) +,S(fd450d48,c59fc4db,e3366b43,caf00c2,bd10045,842311ab,20f8e24c,6a9a10b7,512d1998,db747e39,34451fb7,9aaed7af,48801051,dc8ef756,4580db9f,bad910d) +,S(c0816923,9f2bbbc7,34d52def,79f9f4bd,cbd8c435,d90561ba,fb23f12,f1fcb93,fa00c81a,8c5cc2d3,5f78f0d2,475851f1,73f70cbd,cf2c950,8ebad1ee,b35a0852) +,S(247c4075,9a0e7098,25a5d82,4fad3419,55103052,bcdc7ab,fe4e559c,84a04a66,8da3cae,878260ba,1b95a5db,900f20c,12597187,4191cf21,939ab6c7,fe378067) +,S(fbe3b30f,16ad6608,32f9ab15,2f44076,2f7a09a0,b07b12b6,4375db15,37fa4e9c,98cf3e68,2a914c3a,ee54971a,793a9309,8c63aab3,e2db5389,3f05450d,61b9080f) +,S(79f71ab6,fce86bad,be02055c,520e6e0d,88e98def,d37f2d2e,488bf455,3ec954c1,b5de6a0b,c1745341,3023a650,45262a31,e9264089,8a655b0a,4b3042d3,a9114330) +,S(d190f258,e2e3bb8e,6812da59,606f2f4a,2e3fd1ec,34e2c80a,9d280f5e,e1e9c4cd,98997b49,afec7d7a,26a26f50,9d4948c9,b5ebf9b4,3eef6183,3a2b7a8,6183c267) +,S(24f355ca,c0e00545,f47e199c,3818e76f,534eca88,776ff6e2,54244686,24f0b636,37710da6,5c3afbb4,308d5906,7ad044c1,4df30f03,427ac83c,23b829f5,37d25b46) +,S(bd221461,ae73e639,6b23d269,ed6907ba,330544d0,80342de3,a769d3cc,668cf536,a5f25513,3967fa67,b22dbe31,917d9d1b,ae11a1d9,dc7ab731,26e8f0e2,fbc8deda) +,S(cf0371e7,22194be7,ac5d19c1,1b447978,b38f8a1b,dc1dc3e2,471d4a8f,75d0f6cc,4bb04426,94fe9d55,cc49dd4e,e168b604,564b95f5,99566f3b,9fe2fbdf,16084f1e) +,S(ce21a5e0,287752fd,ae9e04ef,363540ed,f6736c34,266a2d77,b5d0bb5a,868fc32a,fc30fc93,bb5a2731,8b81b942,4fe330d1,878e710e,b9a44fa7,bf4cbfa7,10c7d0dd) +,S(7c95d800,da4720a4,102de774,d585f230,2b849e57,1f42982f,a785bd15,334b4b12,244deb56,fa5b4367,2c39693a,f09083f9,2b153a9d,4829ab3f,ff604fed,a266b30b) +,S(40bce9b4,f90489f4,d392fb93,3f98f71,e9281976,164b25b1,982cfcfe,338d93e2,5a9ebed7,25bf11e8,7bbec766,c52309d5,e2036454,447b8ee6,6dc79dac,7a2a9b4) +,S(f15a2ed8,2c00ed9b,9e7a9338,2d08c46b,33ba7bb5,dc15f5df,4cc29fdb,86f11c4,d02ce92c,e556287a,3acd4115,a190767d,2979dd7c,7a32dfde,cb6d806f,6e7d0205) +,S(4230d263,d5a4a915,cfd863c0,e38514b0,4929d352,d8a90c00,c0ab9ee0,56215932,4b134eb0,51699656,dedc7704,3d5586c1,62134b00,e21ab1b4,517bdc54,5f05fd69) +,S(6cd6bfa9,63327c26,fd2c7aa4,fc159956,1a90096c,dc88f414,d5cc9c64,df6a9e84,6741fda,1f774c52,20b04db1,9ba7402d,9b77eaf5,b8e9e2bd,a000d2df,2022565c) +,S(118face3,ab188a36,581cd1ce,1d434fdb,f1d06f50,1e9a64be,bfed2ea4,ba3f477b,a263037e,c8d5a9eb,be1a8b97,bbfa5624,ce00e852,44828af9,8cfc38ee,1da9052d) +,S(6be81e79,5fd1229a,fb24a395,fccfbb6a,ae4fda11,28f93faa,f74bfb76,c39794c7,edd0f9ae,bbbaaf0e,bd1c01f,af128730,5d5fc5c7,732885eb,327eb21f,62683869) +,S(b9a0023b,6fcbb06a,48f97684,509b8b9b,937f990e,4c3f1a61,27a8a18e,e4c96c3c,e1b25ce1,3e8bda89,ea631d71,4c07adfd,c31fc6b7,304b5e7f,1cc7221a,6597df11) +,S(4e384b06,ee4007e8,e70f22a6,1af73d89,4528722b,cf58bfa1,16eaa29f,e1f2334,2fa6ecd4,8faeedb6,240db0d1,14c7eb3e,63540e31,788f5f55,98c52fd2,538ef0d) +,S(14c8267b,6462dbfb,6ad45cff,58d9b22e,9fb0de4e,3d026999,9357acc4,d254ea52,86da149d,e14de86a,20becddd,e33e99cd,158a50aa,2da085c3,cf675eac,224b292d) +,S(42d0c7fc,21129bdc,97318985,73b6d111,568005b5,cbabcf55,c1828340,4f5de376,8f83e957,92880a3f,dac6738,e65ea463,1399daa4,defbd1e9,41a5586d,a42f652) +,S(bfb03349,2fc28eb,82bdf9b4,417f02ee,f93588a5,b138c520,77250499,23e9f135,c65872e6,9438a5e7,a20c3492,7a3de7d,85c690c8,b20af801,915585d1,98184882) +,S(1a111410,d04e1278,3cded32,b3602007,7dedfa32,6493a780,999c68a1,dee18047,874723b4,c6b63cbd,d957098f,d0124779,3e711a56,fcb8499a,a046f5d6,36d5be75) +,S(a47aa995,3a999aed,9c1d1f77,da8b736e,6d045d3f,9b32ecf,a6bd4225,fa66cf6e,c4ae1566,4da8e033,f0e3fbbe,472e16e0,70a4b40d,f210f7,3d0a6fe9,3dea782d) +,S(52ccf2b4,5cd2555d,5a0a5765,92b5ca7f,5ffa329b,6bb12979,8f2c10d4,bdf34119,92abe502,d23e9f61,f420e4a5,44e42864,2462dd0d,7d7f386f,ff7cf2b0,b3a5043f) +,S(5d2af6df,77107953,e6221a4a,ccecadf0,a2a8d676,5e720d2e,38c386a3,e6b48e4,5a44be69,80ca8082,5d435aec,34db8be4,6dbd05e9,9b8fa176,45888bd4,2260cfaf) +,S(e0f9b457,e184de7e,7484ad3a,dff4adbc,4dfe508c,14341dd9,d2c4f584,807e3fba,d0712e1,3bc4ad8c,78261681,3d00a237,36383731,7de79d0b,1f52264b,6ea70526) +,S(6797c2e1,d1c2643e,a831372a,abf36ab5,fcf88804,9a01f052,a7930792,acd2a7a4,ab005587,69f8d361,2217de,8d4bf205,925bde,7817e97f,d155fa94,8f084167) +,S(f302e07a,8953b3c1,f0b3796b,55bdf133,84809aa7,3dfe4fde,a0d56ae7,a04c2f26,339d529a,a6c0c8cf,f46682b9,c36390d8,28b2861d,6f4906dd,a4659028,c74d780a) +,S(aa4c04f,b8261965,9254f9b3,2d9e25cc,ec651f79,b710cc3d,bdf4eb3c,dfc207d8,520cbe2a,c8c0382a,23d2cea6,6a18f28,bb87e773,6bf60dc1,cca64a58,9fda48db) +,S(cc4bbc6e,7f4b02b5,ebe6d484,6352257,4651fed1,a079767b,fa53d1e0,dc38b5b9,4be5e0de,c97683d4,939077d1,edffcf6,dee4a88f,f7d42894,c3d50302,70c9554d) +,S(6a4bada6,cf1e123c,e80fcf83,2d6934f5,486901f1,6d3ad0af,cc87a4cf,205e6e79,b79145f8,24d693b3,832c6676,e35a73b8,d8acddf1,681720f6,c8d5ac7,f81752fb) +,S(d7ec4d61,1d440625,367b2c5f,a6904b8d,cf6f0d81,39cc3bc,16299e1b,50c9024c,18c402c6,3e818647,7f15bdb6,d514373c,89d1f7ff,bdab83f3,5267eb0c,daa81d89) +,S(4e21e4fa,4b570b40,77ff6876,a1d7e4ed,1db77d0f,56d35445,178ff508,5b197169,dab19080,ef84f0c8,4919d869,36de1aa3,ca85a1c0,aabe902f,b46b31b0,3b350580) +,S(32aa04b7,53752059,7b4ef603,62aa208a,effa6891,240a587c,e1cae58a,28a6075f,ca649475,a58a3ebd,c3802c13,f006335a,7d203ddc,88bbd1fa,f0787839,b9452f21) +,S(306bcc2b,94214994,e86c7fed,4f510ad7,9eb1af11,b824d897,a1cc7564,9eaafe0a,820c2986,abf17343,35089773,5b4da35c,56265a25,4a768346,5dc084aa,ecae6dcf) +,S(37d23716,1044ed4,b395b044,5d70eaaf,864530a7,fd8490c1,fe2d1ea5,61f4bc19,a6a9da7,70558993,ce38d5d4,c69dd25d,134f37f,c28dddef,2e7edc0,c1343b83) +,S(1f324230,886ef594,aecbe39,2d7a4434,fbb9fefa,fe22573e,9302d3c,dc81490f,39e53099,f25e977e,da860f77,6d09338b,c0e75446,42557c8c,b05761a7,c18537fa) +,S(256d4808,d6dd018d,392a6716,2af909f0,51bfeea0,9ce22a76,6266d1e7,61df967,d5643938,c7d9163b,38fc8072,609bcbf3,e063d78e,10cb8cee,32c00abc,6341df09) +,S(58a91493,ff7aa600,1f457506,624ce8ee,b1c142ca,d49f67f1,be099c58,3a7934f4,bde8d64a,79d5722d,6a96d240,897a7f48,8e20f19f,259fc6a1,e4483448,e0290122) +,S(b4a80e5d,eec9391,8f146b4b,e0d91630,606a5c79,1c4b7b87,d4363de6,e2691f2e,a6ff5b82,ca6fa424,efc00b20,7ea90991,fcd5af15,d23140ab,96da6cc8,bd532297) +,S(41bca570,b4981232,f21ba765,4f23d5ac,27f3bab6,9f1107fa,e3dc5102,6f8dafad,5a14cd59,9b0bb884,10ed1475,8445c49,3af3b7c9,2cc6435c,5560c4d7,832709db) +,S(bcff28e0,bb6c242,a7805142,122848ec,6951940,bb4a2e3,5ae05137,d73e8264,43661933,9ff9f2e8,278ef17,329682be,aa801596,e4a12b0e,615fbe7c,4b24e1aa) +,S(8aaa2f44,fbf66c2b,809180,bdbb4e2b,e25ff178,232fcaa5,a2c2563,8c1ea21c,69dc0de1,686bffad,1ceee711,95831b90,c57900a9,baf57ac,2f34a917,6d33e21f) +,S(930a3df,f84583b0,ced82f81,96c39c16,b9318f2b,44efde12,2be6c10c,70b33dc9,ae802936,2b10029a,a81cfc47,1251ffc3,6b586de,da9c1754,4a96d3ec,31cd6d1) +,S(907cf1c1,92b98461,8021a33d,1c9d22e3,22dc83ab,d5768a39,c04444bf,d4e76df2,527eb629,ca0a2e2,7da96361,ee727a34,97edb8c8,68694871,6a7dcca5,13b6f901) +,S(f9d32e84,76c666f6,29e86e0d,3804eee8,abe1ab26,ae2eb396,b584e4eb,e9583b88,d92f265e,9e6c5f4b,bc05a432,6fafeace,1516f823,3e5dc280,b239fa6c,bfdf7fb3) +,S(94772c31,70e50bbd,9952bc69,44e0007f,3d928cb2,c86465b2,ef040fa1,b4f90d9e,29021913,9326e358,2cfa8ee2,428c4307,86cb2de9,6ea2c735,eb71f759,bab14637) +,S(2b49cd38,8cec8c30,e984d40b,f1409c6f,df7ff24d,36cb699b,5f3c24c0,fcc872fb,9e65a427,ce5dcc20,db83a96b,720f29fd,df643b2b,a192736d,c621d92a,6583aa8) +,S(454f174d,8c344479,9bb58158,423d8413,ce87def9,2fd25a,5164807c,982d84e9,bf324810,d9894da6,1eb1e3cc,5a30639d,6500a453,c14e8a40,78353d42,7ac67ee1) +,S(df4461b1,dc1eaae8,5b4b7fe1,e87753e5,546f48f,e39da02f,f1012b24,58a79a17,ac4fd2bc,af8e0b51,48cc5a5d,46373209,8993b377,becd718,732fa339,95412fda) +,S(32973a0,d1be4c6d,1342b2a1,44ffb36b,81fd9b38,c0b7411a,65b8ccab,cda9161e,558b278b,f507b498,fdbbf73b,524c4652,53a0082b,d53a87f0,75d5a49a,bb601b69) +,S(cb63a9f2,d1a11b08,479275f,e1f0bc64,37126875,5bcd8a21,3481be2c,818c69d6,4949d3dd,a31db2b7,b2d42f2b,4709a870,5094d470,1e742f07,b6f86670,e43bfaa6) +,S(543ae5d,a0c271b8,41542353,277e1651,b7b0f587,a5b18ed2,856c2e07,41c28914,22c999cf,aba6f95b,21f87ab8,2f2edb2a,3dbb43a6,ff413506,83835ae8,bbcd0a37) +,S(7ffa63ef,3df62097,a3b61289,4eaf5bf7,4fb39ad2,9ec63f02,2c6f310e,77268c7e,9d26cd72,8fcbfe54,6eda19db,5ac61978,1b456699,59a21628,2500e5f5,389679ca) +,S(72cc6606,b02943a,5bd9a476,6b24caf4,e0e2f83,ae3bf671,5f05b39e,3436122b,f35ff21a,39503637,d05b744a,77797565,1e290541,326d920b,d5bcbb6f,74b56c3a) +,S(f5f21182,de67c3eb,396055f6,a8f64f5e,54043376,cdce9f3,3cffa601,4a348a4c,1fe09f94,8ad7f78,6c184675,3105cbd2,e0f8be91,f9160e81,9201fed3,3208b3c7) +,S(1cfa62a3,70171431,52fe149f,db07b00f,bd8d7928,e5eb145f,b4ff7343,fc19437a,1efe47f,e41677bd,337451b3,92eddb1c,ecfc19d0,5ef04727,12927d4c,77b2a742) +,S(b6609139,733864d3,abbdb6bf,4b827b2,3861594f,ce9e996,4637ef14,f0fb6849,2e807df,ae409cef,b7be1ffe,63351d8d,8de5fbeb,f3be5069,31befc97,d479c006) +,S(13da7c85,bffeaa0f,e295ce6,38de0b05,1b4aaf9b,d888f5c5,5f74fd60,abf2e016,c0477fad,fba6531e,f6369aaa,be663871,3bd6954a,d1e5df1,269c7208,31cd5bc3) +,S(2abda634,547a86aa,1188b2f6,ffaffb36,dd126d94,18406bed,8b81e29f,596526a0,7db36141,2e96e02c,34c0e39e,1201617f,2a7f2100,d5c3cf64,6dcbe563,e2b25524) +,S(4e161f2e,22785c68,3ac494d4,1e3cc2d5,e6b87be8,bd65e85,8aa255dc,8ea8fba0,c81a4e95,a2cfa101,fcb62112,b60c574c,623b5df6,5a1fcf51,e6bf2936,45d5f17b) +,S(586be2b5,740b0ed6,6f5c5f6f,217766e,ad5543eb,8e983ffb,928fc40,9e76397a,f11acf99,8bc2a25,48799529,9c7ea84d,98c6a525,6cbca56c,3ad534f9,26c6b08e) +,S(a76c4f91,1d0dcd58,9417fca9,8124f62e,789e0b05,91a9ce94,13e0b7d,e7d5986f,6a49fc1e,2b32aeaf,6f291184,3995602f,903c8d76,858fc532,9b3a1940,2c2c9b7c) +,S(15f11b1e,804a6d54,a496ee4f,f3030167,eb510e9c,c1754bd1,d46beace,1211a68c,f8968e8d,21bca271,75a79de5,31ad810,ca6c6da7,72ff96f9,937ef824,8f4fec45) +,S(f0169f41,685292bb,4ac93afd,58b2c239,3985ecd5,16acc8ed,ce46f325,74b23e58,6fbd711e,fd488c8b,43151ecf,e3a42ad6,25697053,1df18afb,68f8ad71,ee9aa05e) +,S(2f241e5e,2596d32b,6cdedc4e,b6de5f53,374e358d,8db78d08,9e772b37,6250f359,fe79f0a0,cdbef5a2,7287c86f,afbe917e,4c26c4ca,512f57e4,ab066d3a,3f7e71e) +,S(a14743eb,92bdf49f,5f404a54,ab193100,26f3fbd6,fc5a06e3,e8c6b33d,1f7a5559,3e7e5f24,b1d0425c,ee5c56ef,1e38098c,6d67fd12,f57eb2fb,1ee76c57,41d57c54) +,S(2d097299,db14ce88,8da72472,da686f67,b8ecc812,5618cae9,2acf6457,a011d2a9,e9cf7195,32f71130,5bae1fd4,ce3a8fb3,c8d71088,df35b49d,ad9c16d8,8734a187) +,S(aa7257f0,d7e51fbd,95598b7a,a994fac1,41aa76d6,318cbda0,20d30471,73317f2b,15e00f94,f684c30b,5cd4a978,c6245e6,8d30d959,a30fb39,834e97d8,c18a1b27) +,S(ae524618,e2877095,d52ffc94,67a081e8,b27b4b16,566e2b37,c8d11b1f,1f7416a0,d0275a1f,6d49a7c9,44a07a6b,ee5d5095,7b22b698,2607f822,8ad15253,a546ccb0) +,S(e7a85ef,ff8b0443,eace4cd8,81d768ba,ef4d93b9,79b17eae,fd237e8f,c9ff660f,4b53f073,8f07b360,7d66f720,b1222df3,eee2dd,ca20f40c,630a7911,a0fe2aa6) +,S(daa96eca,b8412eba,75f58a80,84a302a6,37dd7d77,bc9dc502,a2d083f8,35bf242e,ac3dfd97,a01557af,cd1f199c,afea0cce,52c8e3d4,975cb331,1c817c9,fab0815e) +,S(34dec21a,965b7027,44fa7881,4ff6f782,790d551b,7be35c,18cd46b2,e189baca,ee825c5c,5cc289b9,d7f01b43,4de4f0d,d250f9cf,c22c8d20,a1e54175,9bd23694) +,S(dc2178c1,2f015bee,829f0567,566d15e4,d607c8f3,b2992801,4b6a5607,1e61247d,fc86780e,31cf57ff,5da387d1,c3c25a19,36a263d,f6571de1,d098153a,97b41db8) +,S(6faee1e9,354bebe0,1fb65158,20b7e992,9517cb40,23d49d19,2a002ba4,e4782db4,a4915992,ceb8813b,e2778fb0,47766b6d,3958cf72,f18e9172,3f74cef0,1d2276a) +,S(a2bcc012,4112818e,571c8dae,3cb724dd,e45faa06,d04d7128,e9a71dce,22f300ca,1f594d9c,64ba43bc,11764c61,f8fef5c2,e31fda39,3447e7d2,10d004bb,c13870e3) +,S(b8cba39a,c51d86e7,d83a50af,f5370240,cd7b20f3,81aeb799,f951883f,458ead76,3a8bfe74,2b3a05be,27ef835,39988cc4,616365fa,e3738d35,29f9c0d1,c15eed4d) +,S(56e532dd,295792a6,ede9da94,c43e0b2a,25b72725,333f8205,7a352ac9,7a1991d3,a617e234,18bfdccc,8facb454,649a4914,2f0abd64,f30e06dc,838fd80d,7a141fc3) +,S(4803406c,e4049575,19f5306d,6daa003d,2aeddc1f,c1d4db45,15a8d35c,aa39a6d6,7add51c,12412f4,49d7b0e4,b7df38a4,83151126,49660956,da6273ef,8b53fd5e) +,S(bd61ad65,2e44effe,ca39620,2f84e4d5,d81cc579,2a2390f6,52409852,cd79c459,f3d7071b,8bb18928,1d827f71,ab7b1ad,5289033,ab28d20d,643af02f,a2b4ce5c) +,S(1b543175,e5b6f271,58048588,f81a791b,3ab216a3,578e2f79,354da6c9,5e4ea9bf,67a2e60b,321456b8,5b5499ca,e33cd6a6,d5445f91,eadb5a5a,f0e2164f,324417d7) +,S(2035953d,3319ffa5,9f75d86a,8d1b52c0,e8118ec4,d830d15e,d9616215,cb82d080,302cc0d8,5bc1e667,b160bc8a,64cc3bb8,763066a5,6652a0bc,c12691be,c41d7aa4) +,S(bf2a9739,f65bebbc,90413f9f,d311e095,e7628dcd,d76d25a9,8a4681ef,f4f84035,83932d07,cc950bcf,380cea81,28e04322,26b9a4bf,8536122b,fd055517,e7d3b2b7) +,S(a915504a,bb62b4a8,1288c44,8605e8ab,bafb4f0a,ffbcef23,c9c2a26f,f2cb8557,2fec1059,abe1bf4a,834c50c1,edf0e5a6,2ef21f7f,9da2fda,20989bae,18acd442) +,S(318939cc,fb2eb0f8,30d30079,7e209f00,6e28b899,8aecb548,b3b97900,9ea6aab7,5308df9d,10c7d151,cbcbe417,91b627ad,b7204db4,dbd2e01a,c5698e61,bdbda375) +,S(ff9e4851,d3f0c9cd,818facdf,4781cecb,7f622039,c23e236a,7e4b7368,64af1a8b,fd69f241,bac1c6b9,a52d6dd0,6e7bcb6,4a4a79a4,9c3881be,6ef8c83d,3f462c5e) +,S(497113be,a88d4888,2c98ac14,ba0bd0aa,d3c75c8a,58d374cc,86a7fd0a,b6259cc5,9029d9bb,96076d69,a264d2bb,6fea2f15,423ff1f6,ff47c02d,4699943c,9cbfbc4e) +,S(2b2cdb82,6653f563,dfc4c373,96d738e8,3d1dcc5a,ec5f4886,2d5ca84c,2ba69103,d1985cb3,f42e4ae1,7da33b7f,f8563236,6e2f5c89,b849ac73,afc3a7a8,1d049d85) +,S(c55dcffe,fe63e948,af77c8c8,32cb6c0a,c09ced5,5c338bef,fc27fd21,dc63b6c8,edef9fcd,b00fb62f,cc97c03f,26253069,de6f8d00,c13a5666,f574feb7,fabcab6a) +,S(52a0dbfd,371bcfb0,e2c69160,c9cca2b7,8dcf679f,774c9d99,706d306a,30eeb3c8,fe01f320,781275bc,a6c031e6,d71df467,56473d8a,780fc54a,6f8eb1ae,5640bf65) +,S(75c4ace7,6bb32bac,13141f21,615e11e,7608fda9,cba4723,9eb6bb8f,349a9e86,7fde50af,19506ba4,65d49b93,c8884478,dc34ec91,9bd5e22,be6bf71e,a870a70d) +,S(69cd7caa,b220abe7,8f36f7a4,633802b4,85969ac0,e9c4a244,7b0ad782,29daa879,4140a70b,8d9025d1,7fb3402d,2ac8afda,e9e0269,b29d488c,2266c1bd,58accc9c) +,S(b6cf3a30,d2a5ec8f,4480df82,ec6104ff,37355ca6,f16418ab,cad3a0c7,bd8da064,f12006b7,5d406558,51fde0eb,c68113b8,50441595,dfe3dddf,86c17a58,94a6d923) +,S(65c4a081,656d9b7,445131e8,6081b306,3a6e0c02,5339d3d7,cf48e9ec,4e8f59f1,51f4ebb9,4b63b8bd,2cdd6d88,c84c8608,bb380e38,c748ddb3,3dabff1c,f50197fa) +,S(c243570d,e74a1fbd,938bea43,bb17a929,47a228b1,ae47a6cd,15c37524,b034fa66,ca57366f,e3886510,903970ca,e1a5dc5e,bb857007,cd4cc56a,37fc7fca,8aa4cd40) +,S(9290d033,1c95584d,e2bace9b,788c50b2,f7fda83d,d45a4a37,5705400,45339d7,dcb3032f,c4a41076,8b8e442e,46cfb16,b63f4fe9,f94a7d5c,f932d0d4,95e699e6) +,S(295df203,cfa65b66,6afc86cf,b879d650,7075f75b,d1eb753c,709760f6,6ae9df94,2cf505f8,8220196f,9e5478e3,6bd82501,671c6b91,a404fc0f,b1fe00cf,12085934) +,S(1631e6c1,3f89dbca,36cc78db,1056a461,6ce2dcca,62f90b0c,5fcbf232,77d41116,acbcbff3,2c164cd3,b57d3b89,a326b5d1,46b5769f,a5b35df1,efabd2f4,407759d0) +,S(db0b2c5b,eec9f06a,9bb43e4c,de18d978,59a95cfb,9b319d2,5ba619ee,abc49e31,d647147,d8330315,1446a405,85bd857f,ebaef109,22ddd0e,3ac62231,8ee91abc) +,S(2581d046,9e8bdc16,e535c787,1a37c48f,929aad34,b694ac2e,5d264962,11e744f,7bf3c684,3d953b2,dbead1f4,8614646c,d18b0f2a,cdf6fbfd,3ca68498,ab25bc60) +,S(9db20da4,d576f564,372aeed6,eae78eed,f512d13b,13d4e9e2,eb3133c6,13c4b80,40f8b2b6,fa113b6,ed7dce55,d62587d8,a86a43b2,9e0b4876,c5607b89,9144d255) +,S(849b193c,e490990b,e2aa48a1,c31694c5,fa041f96,7086a83,cd1a06b6,ccb5e3c3,353b49a,6ac0c705,5a3d5e31,fa428e2,391165ce,402291b2,5e1b0496,bda41e1e) +,S(ae83dff6,5a1dcb6f,e2f56437,c83162a4,a388a16a,bd387d62,f5c5ed,ea08fe7e,e9c57ed6,5404a00b,a2f87613,4d484842,4a32246b,def6c31b,533caafe,7029eabb) +,S(24724eab,46175b4,efe51d4a,5993da48,1a62c6e5,f05d16a5,17e4f58c,fdb103,94465b42,e4e378c2,1ef0d1d1,8b25d140,fb17f51a,fa4d37e3,450c73c4,72468b04) +,S(c2e2f802,4ac48fcf,41a3790e,e9916018,5a301859,3cc3fa77,c7840552,6700659e,b1fe219,1563f9bd,20c4dd45,615d8cc,400911d,20cfb7a7,672b06,20d32a2a) +,S(8b889bc3,e2d1405b,47a1a05f,fd0c17ef,9b4dd75,4d30640b,e3c4de7d,f70791b5,e2d459ca,285629d,956d5046,54c5e907,6ed92fa6,c95643c0,7d746b78,e03a044a) +,S(336c0585,8f5ee169,fc09fc73,a181ccd1,3bd62dd7,c454837e,1a5d7676,abb2faad,5c29a656,e8f2804,6e855c6c,eb0d908e,38f1b62a,69c67948,9d6a03ce,64a90c2e) +,S(c9a6df46,411961d,a703ab23,605fce16,fe021576,92a9b80e,65b9e4ab,efc9ee93,8b63a4ab,18a4b190,89dbc53c,ef0b0dc9,20c0ec7e,1fcf075,195bccca,58d52f6d) +,S(6cea35c8,b3a45063,75456a6,2c95e8b3,7d84a6d3,e909c5a9,cc4800eb,5bc28367,68198888,eacc9380,4a3233ba,1801d586,e5bcb2b2,78efc7da,6764bd5d,edf0ee90) +,S(f13d4ee6,ea31999d,d8d1220a,13ce8353,7c350046,319a0412,e562cb8d,3e4ee80a,5f7afcc4,37f0b29a,ab11a8b2,14947521,bbb4b622,261b9e73,f989e1b9,9f43a2ab) +,S(7385b644,2e43ab56,f4df8a57,4d0a32ac,6e5a7114,13dcd992,f86c0b04,33830307,6d27608a,8ea00e22,4197c8a1,9e9511fc,7ecd7083,86e94ab9,6aaa35f9,c8c2c5f3) +,S(35923ab9,a784ee6e,5a4e911a,7552fa16,ccefa4e3,dcc90083,e5a19e31,4dfeb06b,c900475d,980cce8,7d5cc091,7a28c18c,2fecd5f5,cd2cc66d,e26deb61,1c5acb89) +,S(d88b6907,3abccb02,2e235e60,4f3e2877,ef979f85,a5b3fc4d,fdb166b,a4155d9a,1eefcc82,69a20b9b,48d30d00,e7ffd021,a9b7b171,378a299,819d6e6d,19d5ed24) +,S(b86c5281,cfb9f3b8,4be4f83b,1d13d33e,1f6abfae,bbedcac1,63362a76,8740a827,c1a32696,fc1f8507,289f111c,85483b23,23d228a6,773030e8,dcf30f6d,57857dbe) +,S(d2813221,9c730094,97c257f3,1fbeaee9,1afa001c,84e4b813,d7ee2452,6454c640,75d722d9,7111d6f6,b6324598,8720ce39,66fae55c,3d81e4d,ade294d6,1269b4ea) +,S(445addf5,bf941ace,6b4aad55,6f174ba1,ee793bf4,9f46295d,19fd8e2,807f8d3b,cbe249f1,fbb39f8d,93079d0c,e84302dd,52212b6c,828a1626,b7446f91,4f6727e) +,S(a2dfad8d,66fd47fd,38fbcb74,b983d366,c57a08d1,b04f1acc,4fdf047f,100552f2,63011d2b,43822a21,ddcdb1cf,ef4dc0d2,5f595165,26939761,953fb738,77586c9d) +,S(c4e84511,6644466b,8c573b8e,c7dd3788,ecf1c6c2,63ecfabb,b13f697f,2b2e3a7c,1e53964,a70e83a8,ed9f5d8a,b6b79629,7368bc5d,cc90e6fc,4e8e6f65,542f63cc) +,S(63c4cc2c,cf368a04,be814d1d,81fa4250,653ebeea,ccdffea7,944801f6,efee1aba,6fc2bb4,a7e573f5,fea2cabf,abd9a145,c7c36d62,11928341,e9553e8a,b38fa90c) +,S(51eb8782,a3445ef6,43f01634,caad6fe2,29585dd6,f073acf3,62cfaa3e,960343c6,56d20f56,8e6d868f,491b6e1b,80fa5c88,e3d625d1,433b1dcb,5ae2d35e,b40a960) +,S(d89ea50d,ab6c4c39,48fc4d4b,73d7812e,a6b59ee1,8e7a51e8,bf6249bd,ec59d068,a1ae85aa,e842ab60,95feec2a,d6ce6206,e81a6e76,7f0b7b20,d41cd28b,6a8c4258) +,S(d121a6a8,11f953fe,a90d8f3,4487d654,fb48e4f3,c5ac4db8,3e3f50a5,cd7bac9c,a5b9e6da,e07a0f1,dc1695e0,20502f55,5fbf136,df11b586,4ecc63e2,d7061a6e) +,S(83f7dd06,e5afbb3f,7b2563f0,faaaa3cb,20c32db1,5bdaeeb5,73219814,caa599b3,c452f49,1e4482c1,1cd4bf46,75d74c5,ab054163,b6d1b837,dd10aa05,950f199a) +,S(1f5ba7d9,b4cf8684,cd073892,4aee1919,1f611d78,a943d49b,509fffa6,31fdc19,a017e13,7a8a199a,e361dc18,527f0135,51e48b02,c2f7f102,a6c1ffbf,fdd0baba) +,S(d25fc73e,a83ef669,c2e127c7,1615df3b,ec61ee38,4a6b3396,cc52066e,c3b70b8b,9b5d55e1,c8dd008f,19b5e989,363ea737,e7d7bea3,f7b0d9b6,951dd78e,be6c5334) +,S(d749373f,ecc1814b,dc8ce6d1,2ebe84c9,e09806a,9cbec814,cf98dab2,4a46ddba,88012af1,84f7ce23,730a3a4f,191d0687,4d5652dc,2be47214,61e9ad31,b39ee397) +,S(8e05da99,971ba37d,38e7642b,693856af,a90d7206,2d360611,6c8f506d,68316b9c,73de534b,5833287b,61f3f985,2723be7b,e8e22ee3,1d1ef4fb,f45d6931,827e3db4) +,S(e71e83f1,c6b285db,157f4a14,825aa540,558a7853,673900aa,fdb84556,ae5c891e,e37980b,c180e3d2,481d81f,f9aa33cb,b297ffad,7dbbdd79,4fc013f5,43ee679f) +,S(bc66d644,dd9da12a,299557d5,fd35993b,ab05ac8f,bdd3802f,25b35890,db8eed32,5fa8c7e4,bdf77750,573a4ac7,1681ca04,4f796ea9,c8379cd5,ed55631d,bfdb8405) +,S(537f194e,efe43985,8dbe1eb2,8e27448f,fd1b554a,b12805fb,3af43538,fe0e37f7,e82018eb,447b2810,1502453f,db4c1cb6,666330d2,ea3331d3,f79d5652,a1d64af7) +,S(4af2a9f2,e0fa4b4e,731dbdf3,676538c1,110f3232,7b8f2766,a7f489de,8aec3b4b,cf2e0ca5,dc830856,43151cc0,1507af4f,d84b253,2f0fdcc9,6489baf8,6c1bd4ab) +,S(81369bd2,a4945c15,389aaea3,4e2855d7,6343e200,a9407ed,d05ae927,5585aa36,a9b4ba4,e26950c6,6648b573,3c0c4e5a,22a51e71,ac3c1fdc,78403eaf,3a667161) +,S(1c0813d7,ef91aa73,9e3a6f0c,e3c7edcb,2b7117a7,2ad1dad6,1aed130f,c61cdd3d,e3390572,67ac3bfc,5373cc31,14bd0f38,ccc3cb25,862b243c,912c0aeb,98bb86d) +,S(8a651923,d2622057,59e5f82,b38bfae7,dc922488,749ea56c,e43f0f74,c268f9ab,f3df434f,14f67030,fe888ba0,185e25c8,38520bf6,6e85023e,e4ff4f04,b056d9d1) +,S(89d98548,30a2a572,dbe91018,e66b28de,835dd02b,f793a469,2de3d720,d70e8f43,629868e9,9e434bfa,98bdc4,f191a08,72e68768,ce8a918c,7c2da42,75e5cb3b) +,S(97d0c83c,5d83d399,c567ecd5,dae551fb,7f1838de,1967e84c,7f053b81,62965fe9,fd716683,73347fbc,407543b6,70734733,df623a55,1766f55,8a02059a,2ce55c4d) +,S(20807bb2,650e6d82,38a66ed4,b239b59e,fe073a5f,9cbf0d6f,e3d9a16d,be427de9,46a25a29,c104c56d,8436155f,64b1d84a,b974fb2c,951470df,17a9704a,be5c1f9a) +,S(3cbb1786,945bedb5,c8ac2d0e,dda1b0bd,ee314cc5,faf56b75,fa878bf2,b218a8f3,ddcec0c0,d05403e8,ea2094a9,6ea910f8,3261932d,701a2ea0,4e93581a,7e4b7275) +,S(961d0144,1618561d,c3504859,7e1701bf,35412ad3,2d04486,704e5593,b9278734,7e137d6b,2060572b,ef66f8b4,c8ce535f,b858327e,26dc2295,6ee6c94b,c83859bc) +,S(2814918a,115ca2d,688f4358,9010b7d8,b8b560f3,6a3dce2,8fcc05e,acd4845b,dbf76f51,ef80e324,8820cb0b,bc7f0c2,4d50ae48,6387d206,c333222c,5f959dd1) +,S(6c4f3b00,c02c19a5,ddb0ab1,798929ca,222a9538,b641cb3c,dd61736,77c9442f,a91d13a4,d3888b82,eaba5173,3ff76fae,8022fb10,8cd61f19,206cd78,7d1c99cc) +,S(8f48304c,ac37d495,fa1a2ff,2178f442,4af030ca,70662a1d,c750bde,5f5d8dd4,ccff6176,47cbdd3c,3d0c588c,37a98723,c2b2aa14,fd17cf9f,ea45ec72,eda8defc) +,S(ae3da559,e3fce253,f5c1fbad,8e12a52d,ee989ea9,8552a192,93f7db0,eaac9405,430b86d1,322191c7,9aed5bb,12eece79,f2519bab,17998346,8aa13b69,94032ef7) +,S(90c093fc,27fa9de,66a1fdc4,d921e5da,6f4fa049,81cb6f91,e085f89b,96561471,1bdad536,4ae198ae,bbb0ff88,3bd5dd2e,dfbaa91d,e989f45a,30befa9,76f2db23) +,S(730a1c4,5ca28743,538fcacf,a65b7d90,b6f4b1c6,87364cd,53dd6585,328eeabf,7420e8da,cc64a059,b46221c6,c3adb4b3,46eceeb8,8effca39,1a1e36d0,d665257b) +,S(341eabe9,66c6ce4,65ac18ca,1eda053d,4e6137c,ac0796d4,36a942e5,99ef897a,d298003d,fdd9f529,c1977b3,ed426d6e,2df46098,539b20ef,c2011a36,b7e49cde) +,S(8f7e1b2,cb538fce,4b4a7775,90d1a95c,2fef8331,9792c0bc,8d5f3493,54fca169,71b0849f,cec62c5d,fdce37a2,61e2a06d,32b3e0de,87c8aebe,e5a95cab,b0c3f798) +,S(c7ea6b43,ce1e0e83,819bee9a,31fba823,3ee03a6b,14988b98,e80aef2c,eace0d56,e9f1328f,dca6a16e,2100ad40,138f9810,17e37e67,7e7ca747,c3a57ad9,24b8891e) +,S(b16ea5c3,b03e58b0,e6c217c8,6eacc4b9,3032add4,30acfd7a,e50eacb9,a0e5c1fb,507fed89,5c14380f,ab9e54e,a3624e5a,cbdaed64,b2712f88,48018b90,7a88f47c) +,S(15cf4de,7c36b72d,f7b98c00,8e928731,a2e71c69,cd0427f3,ac9264b1,5a5fd487,3865beea,6b36732c,5ecfd21a,562ab253,d618546b,c1c24745,8881b20,643d7e2f) +,S(394efbbb,fa740f53,b1e70eb9,d12671e7,55337357,b982921a,79915acf,ddd18218,8f1f1980,6c1d631e,6519aadf,b563dbf2,37b75db1,54baf06f,6fde8218,a0a68eb6) +,S(e6c3e152,27453d32,cd4449d1,ab1ad78d,58f5bb6,f29d813c,e7c6628f,b284399f,89d87957,4b640a87,abadc6a,dc51a533,838a83f,53de4448,9ff0640a,502dfd62) +,S(e2480000,9db30642,f925ef8a,e31c54fb,1340e88d,b6d633f8,415ceac5,f583cee6,39ad28f1,8ecd8302,5cc1fa72,ae4e9897,81318cea,9e506794,50abd959,f19758c0) +,S(1c134120,3c2e7e21,f5b10ecf,402d52bd,33db3278,70cdfcec,f3b277e4,78f551f4,cd6907b,57cfad79,5e14e7c3,b2a06a46,17b7a27c,ff476bfe,38d68d0f,1470dd63) +,S(6b2fbbb1,515c261c,954a0be0,279a1cd6,63daa083,1fbd58c9,c69b7be7,ee6047dd,d38ef59b,4e276fe5,6f1f2fe6,74b6974d,5ef1f4a9,74abb2c8,17cc68cf,78a164a) +,S(dd5098a,8cc8ccf0,7db82096,b52d1fa3,6bd26c2b,57f0cbe6,2ca954db,1773e9cb,a198ec1e,71c53d73,5b50000d,678d7d93,de64a72e,8ad018bb,f515b4f7,c7840aff) +,S(f6dc494,3baa0613,59a000ab,3aed3456,1972d425,34df86ae,3eb8e5e7,cae23c22,7150f3bc,5a968248,d8c71ea8,32b116db,73ff34e5,35ec1e6b,64b2474a,947174dc) +,S(e115c926,5930dc67,bda89798,e267dc48,12c06d2a,a05e4a5a,6519b0bc,7096f84c,5b0dc7bc,82b61275,2aa17a13,c1fe2cd7,6b60cf1e,ceaa9234,ec9e6609,7b2018ec) +,S(6687f498,98a22d8,58ef88a1,bcbfb446,33d51eb2,6c1fc2ec,eeecdae9,e0960c56,fd1912f5,7c0e691,23977524,ba58ea04,5757aeb9,309a5b94,3c993a93,57475ff) +,S(366ea4df,3aa8da52,763b513c,efa3c739,af61a497,6fc4c884,e879ddde,c2457d1c,876520ba,cca3c711,a23186a4,833db7c7,14b3de4f,fc86e8e4,cbd61cc0,9a48f34a) +,S(f7951805,eaddd012,2bed030,f3013e15,6fec02b9,80ba8ff1,1a0b978a,3fbeb778,b255b5c2,db866de0,a145a108,f5eb4b69,271c13aa,8db75fc9,f467843e,a59549a3) +,S(c77d217c,5c7b8fbb,f7bfd244,b2a2d2db,372d8217,43eb4a03,47858678,471d8be7,55573d9b,dd16bda6,ad916820,874d64d4,b260a489,b4a93427,1ea5a79a,f57dc5c3) +,S(c803dc3c,1432ac63,19db169,6d7ebc16,58021b8c,b92c0fe6,a59d1efc,2056f214,ce8e23ae,c7dca471,8666f61d,d7d8bc10,e06d1929,7fbad7b7,7d45526,9e207b09) +,S(676c4c3f,617b661,2affb2c2,dcfe072c,ddee8b29,dad9124f,dd24e87b,52a80b88,6df8c35,be6818bc,b6857f47,f59a11b,e05cebae,ee6c53ed,8cb93fa8,92c49e0b) +,S(8b0c8502,bfa15f59,335cc34,20e04c90,becc2308,12d40c7e,14fc3e30,88b8f34f,eb75d312,9fa4db28,27d20c93,de9c5cc9,4237bbb,a9710371,f767f686,e5ddbbbd) +,S(6ea07148,5594bd90,2d0d526c,28918bb2,cdb7d1cf,d3ad0b1f,a3ebbf76,71a2f90f,3a30be2c,2058cb43,c6f8ae98,1ce8986a,8b4dfc3d,81d49a80,4e40c81a,a1fc8220) +,S(aba7df4,8bcaa5e4,c4fd9a21,6b4bb3fa,27617205,693763bf,71b8b214,9cf83c48,6b1b719f,a0633abc,4580a9cb,9c1210a3,3e5e3bfd,177bc76,39057e51,3fb9b3fe) +,S(ccfca0f,ee5bebb7,26e2062e,2b5cd592,847031d3,5edc7767,414af809,9b3ac2ad,135b2ff2,4e40f04c,adef93ed,ceededb7,380c1e9a,e32a499,467af149,cfb532cd) +,S(9af36417,f3076595,42ea54b1,4671d2db,f7582ff9,b7e659a7,6465ee2d,4db88e35,dc5d07a2,58d4f5d5,ae649b63,e07a4253,8b8b3e42,c65a6ba3,44900028,c84df8aa) +,S(e2f308a3,e4d99e74,d6b37cc1,dc41d9fd,e98a4472,6e86764a,89fe959d,deaf922e,f5f8eebc,d9252e1c,74b9040b,569c9d5e,11d12af5,aa21051,8f87043c,7b47776a) +,S(a62581d4,c4fa4b6,aee11db0,8fb698e9,864cf336,578c536a,8a887d1a,89b43e35,b530ce21,bba9d034,d53dd490,2b76f25b,4391914a,5f87b6d8,2526f7c3,def132ba) +,S(11ef91f5,dfeaceaa,52f434b0,fc03085f,60c12ea9,4441574a,5725aa86,52c259b4,a2c81e79,cb5ad0d6,bf0221ce,2bf7497e,bc91026e,adf0e25e,6269dfb3,2ced7d8) +,S(ff549a04,fb86bf9e,3ad207b5,ac76a74c,2532a1a6,516327bc,96353baf,66269c5c,ef1088fc,633f639f,f0061bef,4c1e4c64,5897ba0e,ee229d44,d99be0d6,95e494db) +,S(11c9ebb8,893b6534,b323909,961bb8ee,4c90c854,52848ad4,18fcf223,c80eb7cd,4b0229e0,89a1ca0e,d60ce2ee,590211a8,769d333,5f4ef32f,4460f8be,940a0dd2) +,S(e86b3691,f8ae48c4,12a6216c,c3633f1e,88667299,f828d6b1,3ff88cac,e060b1b4,14d95d6a,61298fb5,1fc974fe,f3957db5,4aac7130,a1ed873c,cee5cada,6eec0c6a) +,S(e5b9550e,eaeed973,5d8068b2,2c48bd2a,608fdabb,563c0e5a,6c2b2351,24c49d19,506ff679,2ea7c37c,254d7ae1,a459204d,aca23016,93c1598c,2449b758,6d437de4) +,S(cab58ba0,cb15982a,c1ae5b51,ad65574f,55540820,d057e11e,f9c9f9b,a1388335,ae1c9e22,74958b33,2ce0f0c7,ef621a49,4d5b0ad,b04748c6,539dbcbd,6bd4ef18) +,S(715e4568,9bd663cf,e9c83006,82fc7c90,5ae2da55,42b0ea4,40cbe2eb,36e1eb43,58ccbf96,8860cbfc,6b03f6a,a5fe05db,14be0e3f,3d6224bd,ce6eb54f,7372aa85) +,S(df92f1de,4d22a7ee,6d2bf45a,5728745e,f59b6b07,29614536,722c7de7,965c0648,59e18767,f0e22894,be586a13,46950c03,88f1529e,cced0d18,50439dd0,e5bc192c) +,S(885dd01e,dc8ffd40,c5f26d7a,bcecf670,7a791ac0,b2332b91,12e69cbf,11399808,4ad58276,6e763ddc,3f4ff718,569f600d,8c764659,87ebd74c,d506c2,ba886ac8) +,S(d3fe74b6,17a1ab46,c773549b,af4bcbff,917c7ecc,fbedb63b,651b2197,89c1ff9e,b2e6bbee,5d84b593,d43467db,7372f40e,493a9fdc,c5d88fe6,5767f45c,837acf93) +,S(c3730836,74b97237,66d9e9de,f7b6d792,93faedf4,f7d86301,75862d62,d4ada8e3,b669a24,a31f8277,8686d2aa,6b2acd16,872570f0,8e6f06b8,394f498a,dc29f099) +,S(4ef2ace6,422be7ab,6a614e2b,fc43360a,2fdfcdde,51cb31c8,372a2042,2df4e100,86868f5c,37366665,fc6121e0,a8ab3a86,92ba1714,f5b8506f,9a3c3025,eaf9fbc8) +,S(51110764,573c540b,813ebc4e,79d59277,67e30ed1,70a951b7,9ed05f8e,8d142cf8,ea8890e4,9668304b,63fad75e,33f42601,f2649a21,724fc495,61110e79,d7ddfea9) +,S(36cbfeec,5491e1c2,f853d98a,275c1baa,e74dca33,2bd3d830,7d896607,c1cb651c,f67659d6,677b44cd,33c0cedc,d5886ad8,90eac77f,5b393fa5,6cba1077,54489eee) +,S(d1ff3875,e1ef071,eef96f05,bc19d356,aeee18a7,f3892f02,4d07b43b,c3f09dbf,ba825e43,72b4a644,dcb5b8f9,62e0099,ab75ef9e,34b54874,f8d68197,ecbe8eae) +,S(45e7bec6,75819b22,f2761ae7,575ebd6a,8ef87727,a15e33b,3edc7ead,dd58fa5d,3482080e,edaea330,240b539c,7d6a2e42,2cbaf40,f93d5052,28c773f9,6d80fa86) +,S(8ca5d7e4,bd79aebb,bcc5250d,c86ac1df,373eb3ce,1d8cf2ff,5916cbb7,1e8e016e,1af3f417,afa5c1c7,89474f79,a0755bbf,a9a268bf,fdf242f4,c1599dc7,b7cb1e55) +,S(56bf2e78,e7c43841,6324e581,7330a6bd,208edee5,3b5d61cb,a141f9cd,ec3002c4,e8f91e4b,3984a946,29060a4e,534bb4de,f345879a,f4884214,63dd0299,2bfc2fde) +,S(af152859,31540f00,1be4a65c,e3f55437,f7be7d8c,db0283bd,f96bf71c,c004455f,1f58be49,77cf9b8,b7ddf7fb,74f7356d,62b8fe33,6bf798de,3dc435b1,86122b40) +,S(2f4d8117,bcffeaab,7db186c5,7aedcb00,80d67b36,a560c5f0,e323b7d7,79be70b3,e0a50d82,80677ee9,caafcaaa,5d7d1747,3f4980c,73831f43,29b15452,94e71829) +,S(bd96815c,e6b6d0f1,3688cc26,777c42bf,50fb8cd8,cd0aa082,94ad498c,620d1f4d,8a882c49,6592b186,b0a4071c,b0b556ca,27f2ec82,417aa246,ae24b415,8e4ddb23) +,S(cf64bc04,e0796fec,112a5ef1,2c3cd909,5c041b46,2772b818,3e6656ea,16d046c2,6a05162a,eddf163a,fc2cc91f,34f7a7bf,41660b4e,a8ac7af5,698c259e,47e77e86) +,S(3dc4bd0c,2c8253e2,4ed942a0,7f45c2c6,390a9d07,6d334f3c,80dfabe9,282a86c5,e0994668,7cc9c6ca,e2d04840,6d34cd22,e4f8e271,da4416dc,77e59af1,7057c43c) +,S(40e00b89,e58de390,6a56133a,fa574019,51be1efa,6156baa0,45fb3987,94f7ff11,cf675e01,1f20efc4,24b882fb,b7bb9ca5,5a1134f,b628acd9,a597fb97,968641d) +,S(5626496f,1f49190a,48929e9a,403f9f54,319a6b1d,301cf256,692cadc0,41301c27,21702c7e,2ee0efcd,b98a0170,110e3495,e23fe5b9,9674bb0d,b6f09e57,fe76b48f) +,S(2a2c8f2c,a580145b,a9593b87,dfe0dcdd,caa3cef0,20b0e2cd,3792da6e,2f989a11,4aad23a0,e01f6a43,3f6c2a40,f480f945,d45c89b1,df42d67,acf6b0da,3f46bba8) +,S(68a74703,a88b88d9,ee974a87,af7b1be5,1866ea40,ab4cae9d,90a4c2c6,5822b565,3660b266,c74b8be1,c98d3b9,b1f14dd7,e039b476,733a1322,d2d7d2f6,9ad59700) +,S(b50ad45a,4898f77b,c5544dc1,cc8c0515,3b1e15ae,d043d2f1,40bf9dd7,29e413e1,1a59e623,3bea3be0,5713c06,b18badce,69b84693,636be68a,532adfa8,c617a773) +,S(df18b016,3ad015e8,da7e87ad,e6fa5312,d665c696,e42e466a,5e9e2a82,18337c22,532d4260,eb34306e,691f42dd,c215ffdf,2f6cf3ef,cc1363c6,46b8921,ccf277b0) +,S(405bc408,446bb0bd,49eb838a,eaadbf1b,27d413ef,a15fdec9,86e8446e,2c799391,1293bad0,ca26e4a9,f80dcdbf,1ccb70d3,fca9f403,89bcc331,e917d7e4,bf4d0b3e) +,S(1230acd2,75b02450,255fdebe,dd0ae807,38f081bc,5faaeebe,4542664b,ec57bb15,aa31d16d,b145414d,acc29c11,b0ed9fe4,fad90a7c,451d9706,4f7ec6fd,75b15703) +,S(4015c1de,3e09dc64,9ec990be,898db42d,1193e1cb,81956b08,da1c15c4,b9a2af48,d58b2d0e,553dd49f,ab07e5dc,c5a66543,b7dd351d,84375fdf,2ce1db56,8185e67c) +,S(f3da2f12,7f47b9a7,192f00a1,5576aeb0,8bc36211,7fe1246,ad61421,85f79009,865ca0c6,4aa629af,b84f761f,b26a4938,25fdec29,ce1d4a66,9dbcd138,41bb83c0) +,S(15f1e896,f7c7cc94,f3f921e7,6dc1e30c,f0d43b99,e9fa0352,42e56a4b,90a0c736,61e85195,215b864a,6f5bd52c,d0274526,dbc80af1,4395f9d1,ce60bf74,27c18b33) +,S(3fad3455,470a113a,2f773ed1,258111ff,da926643,f7fa1cf2,8e9f7b80,dfd2105b,ffbe436f,a1975bcd,ee4eae51,8dece7d0,cb34aeae,ade738d1,b39f4c6,6ca08a05) +,S(62bbb9ac,282329dc,65ae7b4f,bd15dca7,70a088e5,45850aaa,bc4c09b1,9177f92a,9c000eb7,f6104a03,51e1bd8c,d8ac8f7e,22578291,35eea222,866f2632,2573dd72) +,S(9bf86604,a46cf253,324f5608,b7bc87c8,72939597,131f12c0,eae3482d,b1935e42,e9f2dd2,15cce5af,16149d3f,1116d79,74f51267,1b575f8f,6ab6367f,fede26e9) +,S(a2ec976c,b5e72001,a25f95e,bbd7d354,2f3b174e,80ee3752,db082910,895f723,dbcb75c8,365c6248,c52a83fe,1c79d7b9,37ca8ebc,5060ba7f,60b881c1,964105d5) +,S(dd91f713,ee212c4d,f94b1fc8,c7f246b9,cd162a9c,5c8988b8,f3adff0f,fdb0aded,efd02821,c4eb853b,bb8c55ef,c54f41fe,7e53582a,230d52fa,c18a53d,ca40c81e) +,S(504e6ab0,c47fb3f4,b6b74a7b,1e67f6e8,c28ef108,62c2db35,f16f31cc,e71d423f,e7adb424,3200ab4e,71e50453,2d849315,1eae2247,50ff7a59,52bf2656,65b3ded1) +,S(1a9ea377,27fad3be,155f7ed5,1caf01ab,1d30195b,1629fba0,dd04b7af,c1a4b8c7,2140f144,e531dab2,22cd4137,921b4720,59efbd57,c1c97367,ff2083e7,f2420206) +,S(d76324e8,31920463,d5d94ce8,f9654393,89d97ba3,eb5e91ac,56898343,397b34aa,189631be,df5d46fe,fa8f60fc,a8b22fc2,ac987d23,b21ff267,560863,6ef3a72b) +,S(de87a315,50a6f146,e681d8a9,f9a7cde6,7da28c7b,5cd7a70b,703695e2,3c9bddfe,d33a05e7,77ba9e6d,155bab56,2b8cbba,27f4cc63,13038f7e,d193137f,d14b8502) +,S(d8381e05,7fbc1910,8b5a304,fa984e0c,cc0f5d27,303a3287,9ecfbf73,a1137974,1dc513d5,ac96238c,79d8f740,c855e045,e5c4e567,b3f44744,abb3bf46,ca394e8) +,S(69da86a3,b355f0d8,138bf809,79e314ab,7cb34e64,45be4842,b8f3810f,11b2ad66,7db776df,68bcac9a,c0f8f55e,8ee6a7e1,771d4148,1f5d953e,e9bf832e,cf713cfe) +,S(698f8b8,66093996,e2e357d4,d671d21b,230726a8,84079fbd,766332a5,4e7f18f3,5925e34e,dcab3458,ed959aa,3903ac93,c39b8ebd,1c562691,3f844150,d41ad20a) +,S(37274120,6cb7047d,aa97274f,b12aa8d2,e5eabca2,f3ab0b4b,daa3fd7a,3d5365df,30d36bad,c10337a2,db6c30f2,e8876226,5ccc6a1,7104460a,6f438a9d,3e8e9300) +,S(4e8c8df7,30f5d411,77355739,84a530b3,8ca4012d,3d4c8ce,f6c00689,b72cbeca,d9f7aed1,f5073c8a,8d2948d7,86ce6dd9,93a405ba,393a20a8,719287ce,797163) +,S(1e62c3c7,16e4f6ca,77b6d325,8553a5fd,59f67df,f7295115,cd6e4bb1,19b4b5aa,7df17da2,a7397df9,e314fd8b,6b03d293,ff7325e2,769d7477,ef3802a5,7d8010a0) +,S(c79f25a4,664cae13,d0971832,fee6a316,ff338f15,40c1f3e5,95c707c0,6e035db,5ef7879,c46266ca,7b342264,1594e81,5c189ce0,f057f187,e43a61fa,10330e85) +,S(768a47a3,64304a44,34b097e5,132edab2,b9840584,6e543dd4,755038da,e355cb9b,79896b63,eed7e866,32c33041,d037554e,551f2a73,6f2c2b35,4dd7d7ac,f6562344) +,S(131671bc,604d622,eea3f06f,200edb52,f7223fc9,3fc8d4e5,496af1a9,f72e56ad,5a6614d3,7985f503,2ad7a53,9b99ccf7,7c453f94,3975983c,e948dfe0,ffb867d6) +,S(3b9e6127,48d11bfc,1d2d0081,24e79a9c,604bdb94,6b25ac57,749ca6ef,302d7a58,379e7cc0,fecf689c,f905e84e,8516388f,3e1cb41a,aab8192c,aa024de0,3332cc70) +,S(4f9fdecd,10e8cc9c,f5f2573e,a2fd231f,b1313468,f516af1f,76af0eac,9ad51d76,26d6731b,71382ea2,1172142,fc5e0f5d,d0f7afa2,c9409fdc,62bced0,e8246194) +,S(25f313d7,ad044b0,538bff0a,ead5c689,d5fafec5,7cddd9a8,31b9a4f,59b848e0,f28c77b8,9202c9fe,e64c0580,75a972cc,a4839e20,42c32b66,517605d5,87470c40) +,S(24a8d407,e26c4cae,3f8dbc18,5820f345,81bf4355,7ad036a5,200b9b7a,68bb8d6e,fc6a5d96,4fa25fad,4d09a1a1,a4ec4697,818e46b0,8cdefb70,367e3453,2462b98b) +,S(ef4df8ea,5cc100e2,768aeab3,112c7ac0,6077b754,75cab97,df3ed51d,6d397f4c,fbf555d2,27eb051f,54965eb3,fac1e21b,82d216ec,807b1485,5074ee56,1ad70883) +,S(80c9d594,578f6e4,e8462dc,228cd99,9b621493,63d55cf5,80e1c524,627055e3,b6d5bf04,97b44e3a,7c7e52e3,28f63214,5eedda41,f80c44df,c7ca6d29,77436f24) +,S(35961cd1,57bc1774,40dd41b1,6a81e31e,52349a5a,e4085248,fe4a19c3,b10f7a45,b999ee7c,b32f1051,f0e7eb9d,4689fb30,fac7af1e,b410d53a,85e79f27,70a7566f) +,S(4aa49d09,32a7fc5f,77a76a12,16240142,596a82d6,6b59d388,24f0d8a1,e7f70948,adb5b12a,5e40bd0,a14c174b,13c242c6,c5f25ee0,642a6265,60d293d9,64a4671) +,S(110d8320,a9b2c5d6,5b06f0c0,cddd243d,c83cfe4f,671db970,d1213780,f6c4af0a,a97c2689,5c7b6ea,a3a02564,d2af735c,9b43ceeb,9f6f2557,3fc468b0,467b12a1) +,S(d01abcdc,a363b065,d8d2bb08,388c3545,cc8ee88e,90ee66c6,18a9f575,c0caee80,317409e4,a70e30ac,279914d7,135f9d18,e19fe520,4c83c472,5f86a614,1863b4f9) +,S(cea07679,64506753,8dc24b68,19bc3332,f320f16c,61eb7b85,96e25bc4,1499796,edff87c0,67e97ced,4aa330e4,e9dbec3f,d800934e,ed7238f7,8e97a46c,a6ad9ea3) +,S(d4463e7e,da5fcdd8,8ef0133,a5dbb1ca,c93aefb5,64581da1,3acc3871,cb94ce1f,bb1efe84,81dff341,dc7a4a8a,c2e44ca1,b071bcc3,81e7ecdf,4d4b7edb,6996547d) +,S(89cb575,5e9323b8,7771a2d0,58dc78ab,ad8bba70,6f845a8e,9a45a16e,ffbca1bb,f7e743f8,a24a6728,f8e8051f,5a303d71,f1f47059,202d332c,23f3a293,9ab97e37) +,S(d035032f,8e8e955b,b71006d5,a3b098eb,7dedb73f,a15d0d36,7e01c390,18bdea9e,1ba4cc2,fbabc437,a9d17ff4,f3c59f2d,2a416d2d,c07c0c81,c3e0a180,260bf2c0) +,S(fec9ff6a,e37da167,208b1247,b754cda4,6c6eb072,8d6b65b1,3754e2e2,eb3223e9,305400aa,27a59f3f,f19d1513,3c5a3655,464c6f39,94fd2ec4,562d7f85,fccfb767) +,S(3cfcc8e6,9e872314,5853743f,efd2802d,bb1a05dd,a03a731,77daf6ca,da9007f0,42b23158,fd0c1401,29298d42,a6cce708,743924fa,624d582a,94b682ee,4beb222e) +,S(4438bc8c,f41b7916,dfcdb038,5c9b6806,83362409,69ce8869,c294990b,cd340e45,78154e51,a4dd31d5,14885a18,b7185fc6,84c699de,2862f480,937c9697,6155ae70) +,S(d2ed1c29,faf76c12,fb15b514,6a31cc2f,c799cc4,5fb226bd,c38d3b0,176b5d52,54984b30,96d0c2ac,763654b3,32666baf,82c04562,d0d92310,cce6db16,216bb219) +,S(37f21b9c,d1243170,5ee6db23,eb578ab7,31f4b470,ab07e1e1,68e69bc7,539b170a,afdc95dd,6379244d,cbcfc811,54aa1917,e8093b54,8ec2be73,5867574a,82ec10cd) +,S(73fe29dd,54c8584a,7d25641e,c62f257d,400f013f,763c504f,f0a3ec02,d1ff8067,197ada53,ba57463e,e23b9831,ef8a6a69,d3d2f84d,c7225782,e7569603,fe1d1c83) +,S(56b27676,f0e88c45,3db1819d,36a73d03,95aae3b6,59f612a9,fe28714e,20bc73c,98660256,fb7d7d75,aca14107,dda9e7c2,28459341,ee6de72,9e463c2c,17367149) +,S(fd898f2f,e7f918a4,61f8e57e,f30380bc,5192a5d5,e1f520a0,5b99658e,8d57bf54,6861b3c3,5b0f6943,48bc4886,15eab8b1,36630618,592ac04f,80cdd632,aa63b620) +,S(fef75974,2911ca2,8057e141,6288b351,ccf2d490,634c98a7,a80153f,e9dc6929,9d5d0031,3417797a,83f0ecb5,d4f45890,d679660f,b1b19686,ffbe060e,deff716e) +,S(2c4338aa,18a27e60,d1daaa92,3d837414,f199f585,8d33b66b,471c6562,c8324bb1,13c28f36,25f2b273,8646cda3,5a2a729e,eac1de3d,80430cd1,6a0f3a9a,cb859fe7) +,S(53d6a5c1,deb56159,34416235,c2f597af,f6e10ed5,444d15c5,d9ca82b6,8e1820f8,26a5d559,cc04f53c,6b1ff3c2,4d5a62f,f63e50f4,b7c1b619,9dc54c20,628364c9) +,S(e432c338,e550a635,79ab747c,f01f3ffa,9e39d066,b221fbae,8b44429b,873decd5,297e10af,3006b09b,80d0ae19,3c1e8b3a,80ae9051,3dd9944a,444b2c7c,42d0066e) +,S(a61c5ac2,60f3048f,92cc2af1,6d3f6a4d,696e543,f044192c,9d840962,493f5cf0,1249d4f,83608025,331ab42f,bbe338b4,727a5ccf,b086aeb3,f1d9c96,26859158) +,S(a2f37e43,c3c2ffba,c45ad316,e912b586,2a9ac698,f85ea54e,98fe9a6c,e1fa038d,93e95000,6bd329ea,57397b25,71dc2e78,a466c4f8,76ace5fb,36d945b,a6e9b24c) +,S(b2257994,44c2ed2f,3114185a,6ca617b4,bf4ac15d,40d73b62,44a81852,d30260c2,fd4607f1,3d4ee761,adb6b139,3f87bf8,e8b05156,5561d650,a47efacc,5ef01dc3) +,S(d0607bf1,5317662c,73df9f40,805c9b3e,208ca1e,7fa8c04d,ab9766a2,8c7fffc2,f06da1e2,7e721799,41a9e128,e25ec992,eb030ca3,6161c02c,8ee31d22,388bd999) +,S(596aa391,d4831e61,55d80829,35a85bed,a1ea3190,a499ec6,374389cd,1bd5d8c9,cd9240b2,9845911e,47ad147e,ae55a0c8,639b994,8d72d2f2,72e89fdb,578d4ff9) +,S(433ec09e,19b7543f,a460de41,e70f63f6,6097ece6,dad9b5fb,f4030c7d,bc5862c1,dce03718,a609efe7,1858f6db,184708d1,9275ec2c,76d5e93f,e55a13e7,927cba95) +,S(343b03eb,c285418f,ad522784,f91707e4,f268451a,394e85d2,824c837e,fa48900a,e6595234,643667c4,bb885b8c,1ced3a29,a71212df,f2904549,ccb91de3,75787922) +,S(6094f67e,41b23bbf,4d594e97,b53391af,f10a978a,19244189,63814efa,ccd6488e,ea874289,dd943f7a,c109939b,8fa545f7,58359804,ad81ed0d,26cef135,cd138954) +,S(cd0c3700,bba5677c,8d0ff53d,f06bdbf5,c5adce29,64921c9f,a1591c91,ae0a375,720b88ac,9374709d,1919854c,237cc5fe,dc53b14,3897e4f,3502af17,d7510c63) +,S(d37ef1ea,95ae727e,5166dd59,bf5ee82a,1f215720,79d82e25,6369464e,d9a87cc4,59c5321a,d700c559,c92e500b,1d3a4589,15a88575,df9774e1,4bee7b2,ccf9bad8) +,S(238158e3,d58e7618,95f7a0b1,cdad8a24,3e19bfb6,d1c65929,ff08e766,e62c2b12,1f65fb1,8969ccb7,27d50e46,39c7551c,5b3f25b4,90b7d70c,cc5e4ae8,7293a0b) +,S(260a340a,5245a039,4a2f0908,f2912f0f,63ed68b6,aba66fb1,951128dc,9d542512,d30646c1,e8aabbd5,e758adbe,980715d2,b0b169c6,fe09c502,3beb7f5d,b5422be) +,S(7333eee5,d111e138,f6e0b2d,4b476ba3,6df4357d,b28c8e7d,f232c61a,6c3067bb,d427a423,a4de1e60,e8b73098,b8582e0e,18923637,c44c52ef,fa0b5f90,8ccf786) +,S(c411f215,dfb87aed,be2530c2,82a61e5b,37a0df71,1ce61fd1,25624b44,b7673eaf,ca3d89d7,d136fd46,161fdbd6,3f0b075a,afe90855,51e075d2,391f3206,382a0441) +,S(275b0a1,9dd09495,78298aa1,c4b62ff,11e715fb,6c3740fd,80d7ec03,3f2635e,30dcc412,faf3f389,c9919b6a,f96e7b28,6eb8abb4,ada80525,b9ca7eaa,f3ce74cc) +,S(9f774052,6818bbcc,b068c11c,b400eecb,da60c4dd,22353a3,4145e3e0,91f81617,a9e56fde,8f38116f,cafaea93,35ee3688,50da5107,a5a3e571,92f2bfc,1b55033) +,S(60fb35ce,f1098201,353bd7e6,89b42fc8,9f86dbc6,3eb400e5,3b8d16f6,28ae4358,1906ce1c,6a6f41a5,2f45424e,86633937,8ed56578,ffd94174,f4f85960,ee0f5c2e) +,S(1390a621,f999c3d,1737c72c,3235e542,99ce4796,d65c1ec5,45f60b71,19f4ae25,8f1cb557,b101a876,296881fd,5d30556f,55d739da,5122c0f1,43c04add,3485dbad) +,S(15f372d6,9be09ee3,8d43c453,dc488f17,42f53a7a,7923439,66946210,60f1cfcf,c6007ae8,a836a1fc,b5a4c0f5,e584fb7f,2c468d68,9efaefa,6303bd6a,beecb05d) +,S(bc2b2a76,17dd9fb9,e6d60931,ef2d5755,d33c7174,7e8e28e4,be151b42,14537d80,2582a69,b6e9b9a8,c5bb4b79,4ffeef15,96f383cd,670eb524,2ac4e7ba,375509e0) +,S(5f8e0957,61e307de,26ffd39c,92410937,5dac9c83,7a5a62f0,b0864cd8,a37f2686,d342dc84,7657354d,d524b32b,62f5f69d,5b2ecbfd,71831b28,565cae36,777534c7) +,S(5451cd91,9e849ec,b8dc75c7,9f939957,69a39234,38159544,dea98155,cb001c5c,e00d1d37,f9ec02d1,ae8222a,744381,4f16220,800f84d9,17cea9d5,9a9e2a60) +,S(737b89ae,9d9951da,d2b489bf,f2ff714b,dcfb13fd,829160c1,39875a46,7343adc7,ef9e2ab9,f8246113,726e5be3,5c1da48c,a6b4fca0,53d35dfa,8a3d826e,e48c5ced) +,S(8b80e227,9b0a91a7,6af48413,71ae6fdc,68f4f5c5,58de6efc,1ec0034a,36a26000,909b49ed,6ce8c555,25441ab5,1046956f,d25a3a73,5d979c04,43ae5891,fbd43df6) +,S(9db9379d,346cd59e,ed0c0d44,abc4baa0,3b7af170,9f034c79,151aaf54,69296780,1a8f9e50,918a104d,b525c439,bb16b64,179dc4d5,452c86fa,e87ab757,61e97031) +,S(3aa7c8c0,293e5c9d,fb50b63d,340851a3,88b1641b,b864cc08,fe776500,76f98d0d,974dde95,7c958b70,e0ff9399,d2d03da3,4dfc3437,c1ca557d,2240fdfb,3ae1d36d) +,S(26cfaf0,d27825e1,6014e5c3,19bb907b,7a3b777f,b6bc69da,5cdafec8,40d5b4b5,2eabc11,73729fdb,d31fabb1,722be99,e5b4355e,8c2b4bef,53b21dd8,3cf81ba4) +,S(ee495a13,bd1ba28d,cc26fc72,4cbfee95,3aa72852,57c883f0,cb09d6aa,1bdf3b20,b340a2f9,978013ce,8275b7cb,3fad5c1e,857bc6d7,b43782f2,1e737681,fb39b619) +,S(2e04dc81,831bb465,59d6c354,c4c6105f,41f61301,769d6006,adb290b1,b75540c9,d086cbba,4d221cc3,7cc96b20,adceff4a,6157ab9e,686d1fd5,5d56801,dcf4b779) +,S(557b5b41,b2b2e4c,598460ef,22888da0,aa2ec3a4,7980247a,e8bd465,a6e64c22,6a3c62ef,f80832ab,83a9d698,f61f7903,b7f64e41,9900950e,c7d79369,403d57b5) +,S(a0ea9ddc,e2797fbb,d384a1a,2c26d7c0,edf866f6,9addb763,dc44b112,dc1cdc5d,a085725e,d947778b,40a2baee,98dcf460,b084aeb0,3f96658,4f990895,b15e3378) +,S(1acb89f7,55a51f58,d8ed16e6,99a2294a,b2f4bff9,2fa2d1de,9883061,4ed4b5fd,5b243df,31c3bc95,79c47c57,58eef875,c18bb896,424c0293,22d729a8,b7a32a21) +,S(fbced690,b601e450,2f0ed32b,8c2ac448,31c4b327,a4ad9648,97637b08,2f3d3912,3235804b,549210ae,8cb5bdae,e5b1b474,e682cdfd,8af4acd9,8c0c19a3,cb5c017a) +,S(75339a0,82043d85,54ff592c,186a183e,4597a0ff,48f280ac,d760d134,9dfc6b5a,dbf1aaa6,8bd5db0d,8cdcfee7,de6635b4,b7877ef3,6da38418,5688fefd,dc584b18) +,S(d83caf1b,2994a763,77a2bba0,3c644e48,7f170d5,845a7d17,b8be8bb9,84b0ad1e,e479d930,6d2a405e,f0be6418,3d4f8dfc,a7f58ff,95df1e38,c92a401f,17745879) +,S(35fbbe06,39d9fc5e,d595a67f,442d7adc,ae6f566f,4641fb76,149d6c74,42af49b8,5eaf204e,6217ef53,1ab09a53,e6cf4d12,46d6c4e6,b71ae8ca,4e821474,94d00680) +,S(d5563a62,bd6fba1d,e252a5a5,9c03bdfa,6c570145,71cd1a31,bba43700,2a7c1251,2f83b6a3,2efa7b70,1b4d3262,9e7a8755,8b681d17,cff0b992,1e102b7b,f851f543) +,S(8f2a2a14,a834ec79,107153f0,ffaf8f46,5cbb46c7,5a72d07d,5e2af498,f3359b75,30ac7456,5c946e21,73bdbd9c,dc999e79,260445e0,dad2467b,97234789,a4de1184) +,S(74482ce2,45605bdf,f7d586f3,c2e343a6,8f9579f2,543f7f7,22a4b352,24361b4,b2cae040,9ae59ee9,bff4fb5a,34847ca3,e4acd880,e35e4d5a,5ce47b70,3cc565c7) +,S(ca0c158f,cd362d9e,3724bb2b,f07305a9,5adc440d,24270efa,2a8f208d,7f540295,9e1daa26,5a5effd1,29f62b,bf0f5c60,c0c2b73a,58ddbd99,c832125d,3927e7ae) +,S(9c5403ab,b8b04f47,5eb4ac6b,5d526784,8d237d3d,c36e8698,d59172c8,a9d9e268,89cd0c87,9d02beeb,ffb6dc2f,fa882711,2071cde4,ee71a7cf,669f64b7,d463fbb5) +,S(e6bf2997,5eb88332,64169169,62f49080,29072e85,797fb699,df05526c,c4ee0de9,ae9de06a,1952c472,6047bbb7,b967e6ac,db33a123,24b07165,d2caf737,3fa40068) +,S(ae4ec439,e4392deb,5e1b1565,2310d52e,9cf5c0a9,8753a95d,d7b51e1e,5889a744,1400b900,c3159d2,25ceac35,4e566d2f,4e6f057,f4664b8f,465b0f53,2ba61852) +,S(c6351ec4,27d1d6c5,64c05959,ccee21d9,b086a881,3f4d7406,ba8d0ec8,6cd139dd,b7173d5f,bec7a8da,89c4cfbc,a911687,f39a455a,9293640e,7d473bd3,ef2dba3f) +,S(fe44937d,a54556c2,a50d9538,98abc367,6da5d722,6c6d3379,20092042,d1219cbf,c5c2e2a3,174fffd5,74761486,669b0cab,5a5eb21e,be669ba,c5c5a4a8,46e404b) +,S(760dd500,b18aa066,19a3977f,ddbb6947,d092649e,485f2b6a,d26d01aa,445588a8,cac47a22,6da2825,38126d62,38751a4d,93daf13a,420be353,3fc0f7ad,db68c20) +,S(8bdd111c,e271b136,45d00636,f2d33c92,df570cbf,6bbdc156,69fd898c,ce4ea8ff,884f1430,8ab84796,d61e557d,abb4d15d,15114c0b,dc770388,6ca3ff0c,622df5ad) +,S(406c6d3e,79fed3cd,16d616e6,7fe004b5,4f589594,ecb7ca6e,24210224,c5b4eeb5,aeffee19,f82cee83,d4796668,8545f0b3,b8e61877,f34a0b23,c6a3573c,b98c9b7f) +,S(c645ea95,2580652e,8e05df6a,3d208df0,83e00eaf,50cd7d4d,a7c51458,324a41f5,8b4b9cc9,d3f00fce,332d2ae8,2874787a,6c6c8008,a7577ad5,d225f951,82e8bd2d) +,S(813cd833,dacd8916,76af5b48,5d1ff72b,df03f2ce,c7657e6d,249e79f1,f12d7476,c364d1ee,f5f1d5f2,7438c58d,ee926c18,7162cc71,383e9524,b80cbae,3cea17bb) +,S(fef2337,cf88e025,d70e2a44,e68c22bd,95371f36,14ad9009,f2ba8286,290495e5,b622fc00,8b0a0f9a,9a36ddeb,ce10d871,e5dcd7e9,65ed2784,21ab5891,6aaf7404) +,S(8505810f,22888b4c,c8bb708e,c1e4df79,a3a361d9,5ac2b259,acc18d68,e50b0952,b80fd8aa,b7196e00,3fe24ed,fc6742d3,e7b2115,967f838a,2ef00345,3dbd4d34) +,S(b7aa2500,c77effba,3e93a4e0,b09a0f52,92240fef,4282cae,4c8036ab,2e5e5561,85823a06,1fcf2328,2dde378,8802d211,fb2cda3c,316dbba,d30048be,1295d7c) +,S(6dd2e626,181f0007,6ed53763,32493f55,a77cf5,3c268524,2be805c,7da06c95,2a6a76bc,b3f96c70,78920303,c5ed3208,170fa969,6c5264e0,c3cb1d86,ca322da6) +,S(ff5181a7,ab79b40,a3afdd39,8ac729cf,e49a6a23,2fa46f9e,4ce0bdc4,696e9f9a,99c20ca8,b11b6a05,47e81074,8a34c606,140eb3c7,ce5c843f,60203249,62858aad) +,S(a5dfb758,32aa580a,790fce58,b4c04a1c,9de648f4,19ec33f0,92491275,e8243ef7,78a66f07,e1466785,edfd5523,99fd1935,1ccadfb,e3b25bf9,d5279ed4,1d0b9fd9) +,S(fc5e4688,ea6e4553,610946a7,dd3497e,a5f698b,82d6b439,6a21e082,a9aabfce,d1b32a9d,761df7cc,3d69b5be,d7aac28f,976be204,7b2aff0f,2824fb00,b70d697a) +,S(179fb126,a3cd9931,f135cbe7,2604d3bc,83cd986a,d9a7900a,17436875,d3507016,dc2a57cb,b0bd39d6,c8daf32f,fbe6b300,52cff0d3,12e84b72,c00a7d76,3578e749) +,S(af37b7e,cac16810,9c51fe45,b2d6da7c,3505c1e4,bdc88b10,5a386fcf,e30a6c1b,4dc24b79,61e9199a,f594974,32c9352b,4ca9d735,84dde4f7,a4c0127f,21cc17f1) +,S(62d66f6,b534cd8a,66236ca8,5b7de9a1,eeb49aa6,e65e5298,edd1a99f,96fb1dd1,83c05dc7,e69dcb3c,86dab3d1,153d93f3,e9861239,6e25d3f6,6a6d637,248f237e) +,S(6a40f396,1587f5ab,89280eb0,568ec9c8,6958b4d,d8a379ec,2ce2489b,7e451ece,74972d6,1100a0bf,7751a400,6093ec78,1fe16c82,baaf0e69,74142a17,a405da36) +,S(2838f8f0,c6e20e53,9ecba383,c6e07a2c,5022d436,904e5da2,b91b283e,40bbd6bb,6bbbf962,2b7c41a4,6b72a30b,d051551e,e1227362,b0664dc2,509404c2,819b4985) +,S(21e927c2,82cd4635,c9c19c00,f18ddfe8,7aa9b60f,f3000c3,2019871e,a443333a,6834c23d,e05bc59c,f6fc64a3,af61f049,7c247d86,f76a2ada,7771dd16,999febb1) +,S(d9817ec8,f88f9cef,92cf9df,1fa9099f,3f10ac61,fa641ddc,9f36e854,98822571,53c57b49,4d84c3e7,679060b7,c7c8d4ad,f2db0c4c,c8cabbc7,5c29ad15,80b963a9) +,S(6eaafe92,1994519c,1c61bbcc,c1ad6ac6,f7a2d0b4,fd88354a,6a458e78,17503e30,f29a7f85,f3e91dbd,9e011055,f47a5079,7f55fa4f,46a6b188,ea4694d5,a086b4e) +,S(2bd45f,1625722,2352b5bd,a6d8e2b8,e7d23c3,f8497fe7,348a8d59,97b5cdbb,b5f9d5a9,8ed6387a,41397e54,6b3ff76c,4c6bf446,3093e979,2d1f8748,7a6b725c) +,S(176d02ae,cf4f1313,ff804e22,326d6a13,8b641e2a,3fd4d38f,2c27a35d,f1f456de,5ac13641,d2f19a4f,77097a4e,8eaae498,2afe9494,9188e411,e03ed031,fed9eb40) +,S(6d0bfca8,6296255c,1fede5ef,8d04dad1,2b6625fd,7bc127f7,3f6b82d8,b257ca8c,3c5c434a,81c17be8,8101b5d5,ead55e60,9d9dfb10,c5ce1b41,484d5fb6,f595fc10) +,S(b67d3fe4,f6617324,a4063fc1,142ff4b8,2a605a26,8b8fa146,697db96d,570ecca8,63ed93c7,a95a0ea2,9ffd6090,5c096042,b5562997,4957f47b,2d5bca14,21114bf5) +,S(c171bc24,fd80dd05,4eb3e3a5,5f50c9a5,322989c8,a1213179,be4503d0,2fb1f1a,2ead84ac,7528e7ba,982fec81,4b68ff7f,7aecf675,281f126f,9f41a7c4,5deb2e03) +,S(c4936f57,96269373,21abb8d7,df07e4ae,48ae1f18,ee1f8ea5,6682ced8,ec911593,52ea3664,1b441154,81805582,dcddf216,80eaadf7,4fb324dc,67439b03,aaccb1b2) +,S(b0a38c84,ecc73fce,200658d3,cb43cbe8,e0df114b,5d1ff7a9,33158b67,b404dae0,40c381dd,fc600cfd,ff24d7c,988cd4ce,74c18fe,130f4248,2ced320b,b91f2ff3) +,S(1610da0b,300d59db,d8746d5e,17d71ab2,e7e9c1d,2b43b688,656e570c,60336b82,8923b2e2,fa3fd34f,eff4ad22,7f9f6638,745167d8,fb769b34,b3bb7e12,a25c892e) +,S(f6723495,10cb5b38,da909d65,333adb86,d22ed855,74d24d35,83294cfe,ac88d999,6f726bb3,891187c6,3c74392f,a1e3ffea,f55b9a9e,76e3b584,1d13dac2,e888c740) +,S(3cda21c1,d048a768,d2863f4d,1028459b,c9a91c31,2626940c,f762c074,23d85796,af05c34b,4a15e00b,b6ce97cf,501316a0,85ba33f2,cb536f27,324f5506,54a8f03a) +,S(24e1bc3c,84c0ce3f,52e1443c,1ce82330,b69bbdca,f09724d7,e616135a,d9f99c0c,61385dc5,e5c36e42,275109b7,9c22940d,3ee4c679,99802e67,3163c114,be7fb0eb) +,S(f9511548,94dc4fd6,e2907990,7da4a5d8,ffff1eba,4fe12201,a3ebadec,2620a2fa,d40669dc,1eee21c8,ee1110c2,def5de0d,4f08ca55,6758dd14,b6f3ce64,46b2fe4d) +,S(c6808c80,61dd7e3,f6fb5ae,d129d23b,c2101af0,130537d5,4bb99baa,1b8ee268,2fb3e2aa,77870909,993f9d91,bf670cf9,bcd5abfe,26ea9990,6f9805db,c8707f84) +,S(ca952b81,f79a35bc,effc9f2c,c66c9928,cda6330f,42c44f9a,3191ea5b,5f473c3c,aa53ac41,f34fee5a,2028f7fa,8ffd5106,23cf2dfc,cfe5a1f0,99ef3649,d7f846b2) +,S(679c1de6,521c29f1,9b0d3c5b,ab481e53,d01ec2ca,c719fbe4,16054bae,593c0592,61d4eff3,5daff1f,a4bb875e,bbb42722,9cc996d0,405c74d0,6ef0c6f1,31ade7c7) +,S(6f7ac468,d33618d7,8c59d849,3571abb5,1d445a43,af10fbed,c5d25b13,58170f76,9187ac46,f2076aa,5cf4de93,52e5437c,4cbd2220,d8070172,807e84c8,10a05090) +,S(f4ce85c0,86a4fb78,2f081fe5,379984c3,1a7aab89,ef9ac386,a6a8a11e,596278dc,89d6d9d6,75983a6a,d1dc4c45,a3dafc1,cf951dde,2a038261,b414eb94,f9899886) +,S(18b0b225,19360c19,739623b1,7850107c,32ca6b61,295a8161,fc9d2e1f,8d1e60d4,4d8fd58,98ba10a7,3aadae04,a9ed18a7,8c48bd94,7825f0f3,b42685e0,d391dcc9) +,S(3f75a866,537d022,f6b9b18a,330ecdc0,bdfa7aa7,5b213f4a,876705ff,635d8eb,fd0f335b,b4f41615,7ea2752,2b8aa40e,dcdcacbc,1beaa051,97ecc250,f0782cc9) +,S(58310e74,290245af,c4acb91f,20b29db0,f5195039,24b9f76f,16952141,875d0124,f79e457c,364b374b,43c8d6ee,b408cbb2,94fe8eae,c6914427,83a74757,c8b27f98) +,S(90692ace,4fd3c4cd,3c808952,d5ea0781,2192c807,4619b8aa,4274426e,feb7f6d4,3147db2e,7b3b8506,625c21bf,8803c5f4,e7250b0c,4523a6bd,e25e874f,76d1622e) +,S(154d18c9,6c441173,ba7b8d2b,5099d9d3,92ae7e0d,57419144,229697cf,52384f73,1c196518,31ad8cc5,ea72b3f0,ed0d30f,78b773a8,1f54fc37,29043a35,6fde93a5) +,S(311ebd4f,5f52bdca,290dc97c,73102ecc,421a0b07,4c53c052,11b0bfc7,3b6eb94,69d17492,eca55f53,72b04c34,6696454,ab779ddd,8c7eb07a,34c8971c,b5168019) +,S(f62a716,6976f82d,b62e48a5,1eb45464,c845b71,6436d639,a9aa0a06,feb57d0c,a0b93a32,3fc8c047,3c4198e1,5ab5f499,e3eaa4f1,4bf99a69,b3ac303f,34839448) +,S(c23e0cdd,5e39eec0,d3ad195b,19294bc4,30c11eaa,67c6e87b,a4e2a88c,60998ed4,a521d1b8,8e409682,bf7b7daf,ff45f697,6254b8ce,6c63985a,ad2d9cfa,559208fe) +,S(742ca7fa,a8b4a98f,28040f8c,15db6045,70353954,b862c223,c7979eb2,3443ebfc,251ef412,e97a7e21,292ad45e,46c40774,b97a78cc,7826e45b,cef481f4,a64b838f) +,S(622a14f9,7bd04407,63eb5d52,c15c2f3a,ae185395,e89dda70,3cc4c439,60c5592f,e5db52cb,ee60b303,69da59eb,87ce10c3,c485c239,ae1c849d,32e0e239,df4d4d46) +,S(3d6dbb7b,4e28da6,b731c67a,6c038107,c2ec325b,6b10e31d,9b7d5273,fc81e20c,f0b619ac,febe82f,8c4caf5e,6d4710b0,caf89c2e,1a20073d,66ebe05d,69fc2c9f) +,S(d450d441,bc191b5f,9c2556c5,a56d20c4,48afec13,c7e4b9be,320ddb90,b4c320e3,661be65,b5eef8e8,b357f521,d360379c,585c9e71,c27bd038,f2d63f72,6f6606af) +,S(82f9dd1e,ddb046b2,2b33bf19,2970e41f,c5df0c45,f1375133,fe1b365c,eed1ec7d,cde9eef5,554476e5,e775e829,1d38a572,1d055c4a,925c7eff,35a389b1,388e18d8) +,S(5227c547,834a79e2,d6fb0c3d,720661ac,7ec0e2ff,5a1480bf,3f31ac82,cfb2650d,dca309ff,621c591d,aea1bada,bb1be76d,2fa30a0e,1c83b4b0,d66cbd59,7612c0b2) +,S(3eac813d,7e2c8034,f1d58217,c4372a96,a1fcae27,b29ed9c4,e870034f,7faa78ba,94005cc8,7b8f7aba,143573b1,79aef942,c1519b3,9c63b58e,e420a875,7c4c8b80) +,S(9373c48b,50fb0243,b42762e,3b80994d,61986116,ba2c0677,a38daeac,7dcb21a0,295f694d,70f8295b,cddc1c76,141ed17c,a1cd8e5b,79909785,dad43e92,9247b250) +,S(14019457,db851046,42b51ece,50fb8d94,201a80e0,f29d52ad,1af35e4b,8d82c6a8,c47d97c9,df2816f9,a6147dba,7658b5da,f9588dab,8aa393c5,8f721786,9c498b9a) +,S(1b01f484,ce98064b,f60333d3,65d926fe,ee1cead3,c1497c88,8409be91,2288890b,6817b86a,86c98512,634bf90b,564ce109,13fad61f,c807a256,36a5d6aa,7f2d6005) +,S(5bad5660,f615f36f,6bb9ff3b,19c07b74,9519caab,fffd98a0,4520802c,88b6cb22,4d23780a,e6796a72,324c369b,1987465d,90540788,88987513,13dea0cd,69f90dc0) +,S(7044bd7c,29d2a7cd,d662bf16,90d5ee35,93175691,b62d68e3,f8f66ef5,8304c509,f263888d,52d74133,60ddfb1e,d1c09b9c,871d6eb1,e5833949,13d24973,5721ce12) +,S(186411c2,f90c5402,6445dc4a,4764318a,f3acd811,52c01dcf,d11d13cb,a8c02afc,462b9556,1bf18dff,8a4211d8,89f420d2,122c4ea9,7fda3d23,558b6304,d25b7aa3) +,S(62ea528c,185fd551,745c1512,6fbfd40e,5f8933de,4e1e3b95,92a93b0e,b762ec3f,276da42e,3c8a6755,6be49e00,2e42522b,e3b95e15,39a4708b,36429a94,4f6a86a3) +,S(5e2c7621,efa587ce,bd3c2392,a1b8dac8,50b15316,c42570ed,7270b521,a65d6cc0,c7faa23f,eafb70ce,723338d8,19bebd10,bae703d2,1e72b496,f2f95b6,e83d7d22) +,S(7e27207a,b80f124f,9694f143,9a0cd895,2c67a719,4075d9fd,1bb7c9a5,a22dfa3e,322b729f,e19d75f5,89749527,80f3ae04,48edf9e9,d8c24267,18812dc6,bef5e602) +,S(3de40b43,9077ea26,8d94aaa1,93e9c7cb,98e362a1,551b9b10,d5eb64ce,15986582,ba36c2b,e0f9d3d0,220eb47f,4ea66d01,59236411,2898172,8582d3c5,19576943) +,S(81e94808,7e89846e,6c7e5ad4,feb8f0da,4553e460,e3e4620,d87a206c,9d3232b,e216962e,4068a44,920e6a9e,f03efd5e,afae7f96,3497ecaa,dc741c37,7e9943b3) +,S(3056eeb0,d00a19cc,bb5a2ac7,f320ea3a,4e931187,498ec23c,daadc820,a34f49da,c6803559,271b265c,80257585,1f0a78e9,85abeaac,cb570787,f398766c,aa21fed3) +,S(9b1e1189,752fb763,7a61246a,7134145f,86b973b1,e44a5e7c,c59998a2,77ed64f2,f6793c87,9b8c380e,65296c8b,eaeb98c6,b66e0be0,d03865f4,4a09177,5dcfd332) +,S(807179ba,f1f9ce93,bb1f7dc6,e3c6514,99077419,472b9d8a,1a8cc4e2,f35c0c5,36807219,9966eb7,f18dfb7,d3316eec,54f1a9a7,7214d4fb,ce3f3ff2,935e2ced) +,S(60dd7b7c,cbcab93b,8ecfa461,8cfef03f,a74bdd47,9cb90a1,5e46ca7c,c1bafcc8,1d24d147,9987f779,90cde228,22ea0cd4,4236875d,943eda25,fa25096f,e16b8ce6) +,S(19daeb26,23383b3d,d15b0075,77a5e92f,bcedee4c,91996546,a7c52200,d243e4f8,12676559,3d67fe33,b1c01d4f,cae44be1,5f5f5e31,1149962,83ce8f92,f6485e3) +,S(f42f8e1a,813dab8,9e4db5de,27445215,c0fb1cd8,91c9a96f,ca5ae1db,14e84361,3cd2ab35,55705994,42070743,d01fe633,34819d2a,af95e97,6dbee1b4,e3ba2ae) +,S(1a2b774c,d5d0dd28,26d7c4e5,886d65b7,a219952c,6a413d2b,9fa8f765,73fe2bd8,4e2d0ecb,384a158e,e57da809,d065e39a,27830625,400c51ab,dffdf615,5d88032b) +,S(e9fa7d8,94c55075,1ad1731a,b322fab,fd3d17a,889bccc3,73e44e37,b4c2c880,67528cad,e7987d12,77c61681,36abe0c9,5dfdce09,33129f27,ffa9d36,53673bd2) +,S(5347f349,e54c301b,abcfd8cc,50f6fe6d,8fcd0fcd,861757d2,bc36cbfe,39f460b8,16d08dcc,2a837b6d,f9fe2ffa,b188a966,9d39181a,ed2aeb40,9ed17c8b,554e9d27) +,S(e20e8cea,4450bf03,149bdf60,e95f7ed3,69fd79d0,aff9fb8c,ef6e5287,830c7ddc,fa256932,fcc569e2,613bc0af,5f90263e,6db633f3,6cebfe46,4c70b043,9b886058) +,S(69132e10,b3a919a9,8a7188b3,e9afcf6c,2d53859a,5955e114,a41f1310,55ef8441,e6c339cd,ddd26e0e,ac4a1c2b,39e19cae,2d67e726,8518b750,94cb5e6c,b525d84e) +,S(11e10c95,9bd9f79d,c2819a87,e17c0de7,9105af6b,fdbe3f6b,88d198a7,106c861a,27c29227,4a3677c7,381fe385,8382ca38,d4b98e91,af4250fb,40540828,246d5c16) +,S(785934d,e1b1827a,471d7e59,2a4c3475,3054b00e,b8aae372,444b00ef,d9d894b1,7b8dd2e,a038d4e6,23f0f1f5,a2f75dbc,fe34e76f,6d106ae3,33d88a07,56dc4a60) +,S(f09c814e,8e66b8e5,930f1a49,28b28e5d,206f4108,fe99f04f,ead93096,1cc17605,469bf934,b4059a4c,4a942214,e1d86fe6,dc900426,c7d0eddb,8d89e86c,76d60f24) +,S(732901c7,26982b9f,ff1229a,3e8f7c83,2f47437b,412d0b3e,274a73e4,24ad6480,6b82e2d3,d4830086,cf416593,9b04ff7a,89b835bd,81cccd8a,7a29d47c,b70da3b7) +,S(b8369a11,d23ff4ea,99ce166a,4708ab9d,389cefed,b1843d8d,6fc11f74,7260faea,f4346c36,63ec0ab3,6ac12df7,f61e9848,c2d4cbd8,c8e6e173,2d104d0b,aecaef9) +,S(5499e409,3df85dad,411b8675,c09d0c0,c25bfefc,2afb0228,b94f834a,34ff83bb,7775c877,8fc6e8d0,519a67a8,7d2fe9c,c8abd8c1,15ae1700,ad1a94d7,925a4c78) +,S(fa5bc8e6,fa9b13e8,ad787ba1,497a6b97,5c34a64d,a0da5b5c,836bd71b,88c93740,8c919cc1,19e45de1,54f6d0d8,f22d3b51,169b3f0d,181bb40a,56213cfa,a4ad126a) +,S(a871c481,a84f4447,89ecded1,e2b1f525,9b1d4ab2,b16af331,199f89f5,730cdb0b,c478609,30b85d69,1ae45d62,7aeca01c,9f10a119,500d5d4a,c317fdd7,8de4a103) +,S(63d3be27,66d33bf8,f7a38577,aadd5d9b,234bb77f,d206b056,379e023b,ecd25616,9509d09a,6dad6da0,aa5151f8,dd8e3800,abd8a799,2276ea0d,3c958f76,498ada87) +,S(413ab945,c133d383,d70e81d3,34bda75c,be7bacf5,e49d71e3,7a884489,adb28f8f,c3d49f0d,6c65f6d6,1c9c84df,a4138076,f0168916,393612b,837c1df5,df710a02) +,S(df070f4d,ce208b39,e2614db,77c6aa32,2a50bb26,2ec7141a,b1e26835,911b85c2,b0b1f453,1815fa7,83609235,da7bec0e,f1bf2ad3,b0e71f7f,7c18de8f,83f55a3b) +,S(1ae86e7f,300efbe7,8cc933c,f8cda339,1f0f8f7e,411e6c0e,708a0d75,a1ab1815,d322b34f,f0778782,7d2da5c1,4d6bf17f,f8afc8a1,8051a724,bbd719e0,f5f31a2b) +,S(2a38dfd9,ee86308b,e0c76ca1,8dbb9ec0,fbaa86fb,51075887,9283c070,f31106a9,69cb45,94832b55,abd6b686,f67d493a,9d45e4a1,7d557723,fbbbbb83,7ab30cc4) +,S(2c6d7ab3,b1c3301d,65c79d40,583b566d,3ee39014,345a4553,a77a8e46,95b779e7,eb392dde,318bd27d,446b814e,73f9cbf1,380e38d,fed25022,bd584585,d671e470) +,S(758739e8,2aa2e25e,b1d8e027,b0e401b2,c82db2ca,549ebb9c,e1560ec4,531c5c77,d3792987,c3da2cc5,5760e52d,a19ea1b4,d0c2cce5,3f612760,bfc28414,37090975) +,S(7ef11cac,99fcb66b,71de229b,b34e82c6,e3149f85,ba1ffbf7,f22db64c,6a72588,64d7c546,28892864,1db41465,a134cfab,bc08ea64,11f5b2d5,b1de9f0f,7ae41b70) +,S(a7d48b06,c66ef847,f17b4b3d,ec40eb97,cb91bff1,c06d4a6,5d6db8c3,5561e4c7,2f22aae0,d6eb7221,d24f025d,ad1768a9,a619d6d9,db9d8e74,948d9263,3d0676f) +,S(a14d7354,1bb93625,d80f654,af67febb,d774e42b,6430a14,a907c52f,84088ab,adfe7f75,2b080bb6,89c031fe,c7c11d1a,bc303b7,f1a0ec2,38a4dac4,afbb2a5c) +,S(16076f95,b067381c,9d71cc9a,928f10db,43553332,f747e33e,367cc7ca,ffaa5f88,5c05040e,43a29d4d,e16b5e42,b672f618,8f0496ef,f307c269,13df7c79,ed2b1502) +,S(973450b4,a297ebf1,70630a71,fa74ae2c,8f3f8e39,18ff73fe,41bd1e65,5a5acd29,ba04c21f,e92ea639,99d5d87f,77181c31,ae8ed87c,dca22054,2f746fad,61f63039) +,S(aa29f7ef,d2f06187,108ab23e,16d9f3a7,74ab6459,550048cf,e1ddca0b,45fb5942,431cbd5,c77b31b4,d8b8568d,b092e721,a02d643a,75621eb6,1016b363,9728e2e8) +,S(bba61b3e,52614d14,358dc227,e0c0ea6c,e4dce364,fb7e0500,72d0d780,14642d9b,f33e0e72,6e9ee195,21fb4a80,2510156b,230a7ff,6e4c6b38,cd38b3cf,65336d6e) +,S(911443a6,979ede22,f23df6af,e4b1ab,73867dd7,a47e9268,3e3cd83f,241bfacc,a0e5be1d,31cf0375,7c2469a5,90f3493b,e43a99e2,755af9d8,b87aac67,ee7a6488) +,S(ff2126cc,5698e6c1,2da7f441,b031ea11,30132b86,23994f64,b332a8f2,87616d41,c616d5b3,79eeaae2,b072a11a,d2b316c1,b2c851d6,698b6d86,7fae1abd,482e91d4) +,S(2fe68e9d,de151731,12e1653a,2a6abce7,615062fe,a5375ef6,a2be1e90,67f6d8ed,c24e4b95,718acdfc,cd9f39f5,eca6f2fe,74fe7f9b,ae6aa352,e3651bbe,5d81c9b8) +,S(dc08aedd,90e96f6b,892ee62b,1f719619,6af44975,58d70f1e,7294863e,271ddb99,7250d846,1ac24a2e,e74bc5a7,53d5361f,8c012986,4364d84b,4afe22cf,c94aad99) +,S(e9c21052,f350d1dd,caa82594,b6eefb5b,f8f01ec4,1a423278,fba3951b,fcc7eaae,6992071d,becaac44,a5cd1537,cd066f2f,c432f198,621b70ab,360ea8d1,35757784) +,S(110d5ac,7838b9d2,d33bdf5e,8aed8529,5ceb9334,d409074f,3c26e3f9,f0cb792c,7879e339,427d8425,b597e76f,56a7a6e7,9a8cb00b,374ac98e,9cbec66a,47bceac) +,S(92982af9,ab48532f,cf2ece2c,9ab346b8,7def4729,8ebcc03b,91869aeb,42f9bbda,c6bf5a6e,8639e2ca,772e8c36,5f824b3d,331c7be9,4c13e9a8,ae6a779e,208a157e) +,S(c3f07f47,5246bb78,eac4e9cb,263c774a,3447acce,993a9707,14c1b553,79013162,f1931fa5,d6fd228a,a5781b35,45faba70,df6bca90,936acf17,f81a1155,4897e136) +,S(37640e7a,eef3f326,a598d363,df4f46dc,8f0decb5,ab6e368e,62294c53,ef3fdc7b,54fe19ac,83a074aa,796feef6,8d39358c,7be8848b,433e1eaf,9982701a,3eca7a1d) +,S(e903363b,6c90bd,2bf15176,83411d1d,aa9f0bb4,a22b5f03,ebbee32f,8f0cdf4d,fa44eae0,78b4b6fc,63abac71,ce0a6e0e,16b42d80,94179f1a,657eabd8,7eea2480) +,S(3c5fdd9f,7313d178,312bc477,849ce90b,a22770df,867a815b,2bd1d1a0,600afb6f,d81524ea,9e23ad00,bd7197ee,63457745,75795d6a,63b8b15,a19cf606,72159a05) +,S(40b540cf,43713ef5,c8b919af,6c991632,e60c198a,677887be,33b84c70,7c0ede8f,54586256,7352c824,49ebecbe,6eaae259,8e1cf694,fbb296bd,c14a4a59,240798e9) +,S(67da332f,f57adb72,8eb8b926,5a650e23,bfe5271e,396d74f3,9878c22,6f912053,91aec263,d7b74e09,b4fcffa4,87cee236,356e4fdd,bfa257c8,f3e3be0f,3a3b16f6) +,S(40cea066,fa029178,5662c994,db5b1153,e59fa121,c9133c2,fe1c6d20,91d44d98,61475cc7,a5ff5251,558f258b,aa8d69a1,52ed6497,c8ffcd55,2b66baa5,534df64c) +,S(c4eed050,248cf186,f8e2c079,870a139,c0cb13ed,2cec040e,d98a18ca,9b67ed0,50e9997e,263c7441,9e71405a,4d342e76,c5548e,1a7a3cc2,97e4e00e,91fc476c) +,S(676b60d2,14e309a6,8c7a8470,70d5bf62,8b71b7fd,322ae568,56fd4a48,9f3ee83,16e0d8d,371ad6bd,4713e9f,7c5f329c,72215cf,21a48527,35a0df71,205b733d) +,S(af268996,29117c69,c3beb193,c5cf7944,d4d3cc1b,7481d08e,1a190b45,c03e1fe7,359e17db,bdbe179c,8cb75ead,8a0e3c86,2b1d7d28,4aafd2c,e45e7b9a,c6d83ebf) +,S(62e15cc5,a30d1388,2b3c3890,4e1d11db,97b0a7b7,c658175d,e438591c,ed1ed489,fab66c6b,ab7b5ed5,815c81ca,65cf364,f4cdf8fd,7a6fe8e9,ab5ceb66,3bb4e8f5) +,S(6c7af496,df980977,41f5068,f34d0477,7da16397,c853539c,1b0c1263,2e43ccaa,4eb0e461,5c6fd9e4,3aa05201,88bb264b,d55b3a05,fc2b6c8c,d6f5f51a,4314f6b) +,S(757a45d4,4c662311,289be914,4ccca5ae,6ef89f51,52d66a2a,9be64096,cfc8fba3,732ddb9f,62fdf217,44d7daa6,35ca3e92,c3b84061,bae4c220,13339e06,fd4da7e0) +,S(14978c08,f1eaa9ce,4d878db3,29f0bc94,43301fdb,24b525e5,4f9f27d5,7fc114f9,439d8e8b,2b9fdc0d,85914620,9c883731,26e961f6,82407867,b10bdbb5,3074e5b7) +,S(3f231c93,760d9198,4a8d9adc,abd83c1b,3ae620c8,f067d103,6d1e5e02,2a72bcf9,95eb3a0,a928d3f7,dff3426d,763cf6ed,20d6df2f,c5dfb17f,c53ce0b7,90412bc7) +,S(c90268ee,75cc8e7d,7fe84298,b1db4255,80c3c45d,3643a280,e3d5d0f6,7337b616,f566fecd,1158d49c,c12a1dde,d0b1fd7d,d9cafbb6,ed3fd09b,48689f85,b01cb59) +,S(6457f0e,4d6cee8,7a2c0aee,85306a02,42c017d0,1e1c9807,5b460732,5c1d82f,67bf2f81,262de4e2,ba9d29aa,aecc9315,ff56c3ed,2e73d438,2a3dfa6d,faf9808a) +,S(ddc591c,1abcbb88,3de13bc4,490a66d5,3d96c97c,f1e21ae5,6a430c1c,39b1b629,80ae50f7,b81f043b,e3db78c4,da7b6078,871d4813,1e076ad9,de59fb27,b942f72e) +,S(22b19bf0,5efeb280,44b43bfd,7a6b3427,2e8b3c1,f6635ec8,d6966a,6667a252,47f504fb,ef2cea6c,8692d0a5,1f492560,151429c3,e522c23,2914e880,9ffdeef0) +,S(d5883bd6,c13d93ed,50371ce6,ed6c4263,395c5d28,f589c84e,cb9fbfc7,2dca651e,acde7b79,969cde2b,9a580387,d8e3a99c,45f15d38,f2e2ed79,688a3a95,80aa9a8) +,S(9615736a,4804dbbe,439f2f82,ca576623,a1675aeb,9ea9e40e,f6b910b6,c84af16b,a0967216,67f9dce6,25ce5331,62d45477,31be7427,74eeb1ec,4e3fe578,343c46be) +,S(ea36bced,959bc461,95e730f2,26224652,2472c2c5,70df6832,befa40aa,5c600e32,dd52094,82eb08c4,bc233090,2aa54333,87d06885,a9357f40,246348b4,4d97fbf9) +,S(44814860,ddf61b77,77cad443,18c21686,85c614b0,1c5830b3,b178a52,d85539bd,f3e5fd15,c71050b7,280643dd,bd2c55b9,219e7fda,cfbd2e98,94fa9a4c,9cc7ce75) +,S(814454b,cde86996,56b44c39,69bf0a8f,31b6d032,40098f9e,43d52266,cee66f49,8baf21a6,601db25f,2e18f5fe,decb9f31,9a6378c,adbff6ec,99bfa2ff,8d26b5c6) +,S(1ff790f1,11f7bbb5,b6db5f83,22ac696,c1de09d8,e50b9f4d,641ef76f,d43fdad,145d0aae,c218d6c0,219ce9c2,313b4962,f873c5c3,5424d3b,536c1543,14fa9517) +,S(275cc6c0,6a8faf20,c3c1e88b,87e27d35,661c6401,d3a1bd96,18cb016e,fdbc6d0c,45cc522e,97b61ff7,696c5780,638054cb,5e97b6d4,fc4a813c,7000a633,4e9f2a7c) +,S(9c78cd3f,874afab,23874f0c,8a84fbc8,a6d032f6,7fef6c6f,29c90513,bb6926f5,818a2406,fedf958f,7efafc4a,584ff6b1,7bae1b65,f67d1086,c3e013a2,cc318b1a) +,S(3c060395,f2cfe9f9,e3bb4afd,e5a71e03,c5a92491,323de27a,c9ebe1b,170153eb,344d08c3,8021fea2,6f55c0c5,85af3954,afda21e7,14cdedc6,8ffa3771,43b2ca27) +,S(f4b61a9f,7392b960,752bafd0,ce521e4e,57bcb898,cbb2dd9a,b0f7db8a,3ce5ff2,e412589e,f3678029,15813dfd,acfe8056,2a59894a,4c87640c,6044656c,e8998d5c) +,S(418698b7,6230acce,49505275,a0c01e9,4eb356a2,e3c242ff,470b64be,fa91ce85,cb9dfaf5,4c64ed94,f97e03d,30e07876,9e5a3142,d0fc0902,5e20ac2c,21c4ceeb) +,S(809cbcd7,91c1f309,864ca343,3d9a11dd,2db3d9c4,5350ac5b,27f55e19,b9941ce2,8161347a,92fdcb92,263f1d85,2923a15c,42d5012e,30d7d9b8,623cefa6,12b06bf2) +,S(6410c007,1d1025c9,8ce11509,5359e962,58c6a115,5e7f34fd,30fc8862,f9569f23,d76363d6,2661a55c,6c211bc4,13b1dd7d,63fa6964,a03d9461,e2870f2e,54c87d29) +,S(8394abff,22ef6051,7517e352,12a4dca,103a19ab,4091c7e8,fd417a,98fcfd2e,20d0ad3e,bc0fd15,fd405417,61bfbb40,8e37975d,a77321b,67ad2ef8,35732c0b) +,S(dbec1fa7,765386c1,d7558cb1,f19788bf,ce0e9fa4,7ca41edb,2f2d9ef8,78a04308,5d7c918e,335ef23a,934fc963,48dfd8f6,ce79885e,f4c463c5,b2645c8,48083ea0) +,S(9d6fe17f,3f53f921,893a422f,c9f675d4,652cbe09,a870c81d,8b2ab9ee,9a591cc2,fae9ccd0,b22ce7e3,2e7faed,2d47c36f,25cac5b1,e56ea936,a6811f11,8d485a2f) +,S(622f8f9d,65c4270b,1479e4d6,6f82fbc4,cf23e7a4,d09a6537,22c20d9c,54f85f55,e87650ee,4d9ac5d9,c6566612,1b6e4679,7e52953a,d7c3aedc,2fecc22f,d5b76591) +,S(cb1db853,b615cb5a,b92d0340,7ad7891f,2e7b1d4d,120ba3fd,a1ea3565,ee1d1436,8362a39d,9d425eae,2451d601,27ac2824,3819da08,856d0db2,18f339d6,fcf20046) +,S(5cbc6a63,59d9d6a5,2ceb093f,ca5cae,f32892f7,c9d04091,8245d9ae,ebd594cc,c4c18f88,7436575f,39b9e7c3,27c4efdc,6a3490b1,23c50743,ef03b1bc,34566bd) +,S(37a3cb08,3a9b7561,78ff2128,e7a5b9a1,19577af6,61974869,cf555dac,ee2d11d8,1a8a6297,4d0b6c5a,12155e9b,cf895c95,f6fd2b1a,2776c32,8d6b56f8,32f114ca) +,S(8e0611d7,ae0914d0,5d39e06b,9b359400,f21c3beb,73ede92d,bcaa6d96,8a995b95,ddd4f522,d1c6a964,9629727f,52740664,43e80ac9,7d9c68e1,225e60c5,38129bd7) +,S(9ec34a91,8a4648f0,1c1d726b,51b1874f,36410180,8bee25ac,c159c251,fc115510,bc5ee2aa,b5e43b01,b34ab989,5f4cd71d,1d3e6288,915daa2,e84ed531,4b388b8b) +,S(dc19ec7d,c901e215,f6d0603e,bb5494ca,aa9b5409,67a5a339,8915c4cd,84000654,b55b9596,a18e53c4,b82c669b,92defe5f,bb5e58b0,3cd227b6,3b439b4,ef13bf13) +,S(51bc3804,bda51e9,d0c45087,13599f12,b9a8599d,6e8ce949,5a3f595c,7d38dc8f,ef0d6522,fe3a3c4e,64ee1092,b9ae8f6,e7847bf,94044693,a3ac7cf0,f4aaa37) +,S(13463b32,dff393d3,dfddbe3f,bd6ade13,190e0bb5,83e3f126,344b5e01,3105e234,ea3df1a8,b8270f70,24910f40,a519cef4,9944fcf4,d82a4300,63b2026f,8f019ff4) +,S(c49b3261,2e286adb,606686d9,67dbf4b9,57819dac,9bb75f44,8f1416ec,f900fa5f,ffe45b6d,8c19de7e,c08256e2,96667d89,f290f5d2,25a9e380,c106972d,79551b1f) +,S(10fe7aff,36a8f4ef,79e2d8ac,619ca92e,2300878b,cf21a271,70f89e9b,6294dd28,f67da70f,623fe6fd,2c7113eb,fe6458fe,d0d5471,ab639050,685a0fd9,33afd6a3) +,S(d56037f6,efec78c6,e31e633c,570c5e46,96f96276,b86d0b89,e993f014,53592300,2499b92,4fd99b5e,a56f9649,bd8e53c4,b2f20dab,9fc1de83,dc199022,1f606b37) +,S(a3c1d0c8,939cd4b7,5cce8ce2,f3be0226,589fb32e,55457fde,8e700a77,a60bf003,b9054f66,bf6450cb,782533b9,33190084,d6f5944f,cf4e06f,de72d483,144c941c) +,S(85d0c4ca,e1cfe42f,b5f73f31,42e97e6b,c0855f7e,db19b1a,8668e413,4efcd249,d8372bc,937fa754,b81631aa,33a8ecbc,a25f86d3,d5e0627,d6964ee,5c08e68b) +,S(3092eae2,d41d0ccc,10af9d81,fe8c80f6,9ae27c9b,74b5f696,7a69211c,8d6ef98d,153f0882,f6b720bc,c8ae5924,923e0608,d6163c3b,32d36742,68718d6,e7f35e4e) +,S(4aeb6ac7,fe874a0f,1c24681d,884d5497,3486f293,9b01ebd9,74c34ea3,429be71a,d7077161,c508a7d9,63006566,f652afdd,df87d0b2,8daf3fba,21f206ec,a94a53e4) +,S(729bb0f7,be975284,a68363ef,40003a6e,c770fa4,5d6e6b99,ab0c5042,da563f32,1e0435d5,ea85370a,7293396b,407da43f,c540aa04,24f21de6,a002f555,cccd4480) +,S(96164ac5,1074dce1,3b973689,a39b6795,9bf5756c,4de94829,66bbdd23,9e665fdd,5cf13b8f,56c92710,80c11626,6bce26c2,14a39237,9aa68ed1,b7a05d65,7bbea247) +,S(6d45d79d,9297bb8a,326b889,391b18d7,31af609c,67b31be2,9b4273cc,405e3f80,2d99909a,7b6969fe,148111fa,1b9c9326,97fb465,494a8225,a6c62626,555ed02a) +,S(70091076,b5b81f33,42170f0,4881c941,d2fe9b0e,ff156221,66f0c92b,4f9e8e71,7c676c5a,4fb434d6,58f635a8,20b454f3,4c9795e8,b8860d27,4d0fecbc,ce7824a2) +,S(d04c0a77,c8fcb3a9,a02df253,b8819853,63dd47ca,bc1f8881,88d5e56,87a7db74,302d1b96,c35a2626,22bbc6e2,841b5cf8,efb77374,af428f9e,d69b2b11,6993db09) +,S(3c93d2bb,7ee61cb0,7a25ef65,f3e22eda,6e74d2eb,f3ede111,b4f2a28b,b4f206d5,65ed4db2,b1647b39,7225741e,241310e6,7a47f0ee,4f459ab1,3d29689e,cef70336) +,S(8efd5a54,a4c7567f,bd971cce,7ce3006e,3524dd81,cb7a4ee1,a5c61e12,62961037,6b9688d7,d73db9a7,1410738c,80430ea6,ea329145,718b4177,71e542a6,93b494cf) +,S(e7aa8121,34fa1f75,efd2ff02,273becca,18bf1ea9,47d168e2,cadd6e9a,6c448425,8af9da38,e2471dad,128e004a,d7b03932,2976d84b,7c91f91a,af471546,dfa5acdd) +,S(e4335a32,b3218d51,9c582689,85138591,4b7cd8fd,4e4eabc3,b5f9e0b4,c3212b84,ccfbfb0f,50b5fe75,c849510a,bac5870c,3016573a,53eda160,b14602c2,42ca02c7) +,S(aeff696f,e98cf587,745adbee,3fca1ae2,3f8f5768,56bbad5f,4a26afe,904a17c4,55dfb880,250b9a5b,7eb971a1,fbe0cd6,ad2d375b,88d98e52,6afcafeb,7ac4617) +,S(72fdb2e1,eb120244,4b20bd6d,afd6d49f,46a56f25,63e679c5,2de98cb5,bc999a5d,e29f2,34ad3637,7b4a9cf6,c307b8a3,a779cc22,ae472aac,eb519c10,8137162d) +,S(eda9f183,f89750bc,8c12ce7a,fee54145,42edd9ae,9fefd508,ab588bf1,6c7da0e3,a9328c5,81b0cb02,fc09fcfd,2a57d2c8,fa313ef5,4e205902,9bcf0846,811be308) +,S(744683c1,2a0150be,1d7655e,b5a3fc97,4303387b,e53bcdeb,41dfbabf,ff12f6be,df2e4b05,a459aa27,2d3de8dd,4c211dec,e35eb798,67050285,57dd53e3,5b9950f6) +,S(9f3cabfc,106653e4,65daeb52,e86d99ff,e3ac6df4,5eef90f0,61226cc0,32592ef5,9f20d174,1b78c8ec,d7425d19,fc0a4c9b,1f1cac8d,b97514b5,8585901c,41862306) +,S(9dc15715,9f59c338,d56a7d9d,ce63579e,9e95424d,e6db6927,418b024f,ed95936a,278716bc,3e131fa4,75b9ac00,6e868d88,ce17e6ba,d7ecd202,33fbba0,fb34f0f9) +,S(b6c91d90,b1bf3b46,4b526186,69e1c118,dcf8d6f9,c8ca7473,5a361a28,b53c823e,15132dc7,69e59b7b,fe5e9966,1b82e47b,3ee4c3a3,1c0b8201,517dc567,ff90a4bd) +,S(56604a9,ab972c4a,f18d5f8a,d5cb6d94,2d0c841,a7defd1b,b94b1b25,c31df400,3d2f2a27,adc2eb70,336b4b44,5229e09,a74a113a,f1b21bd9,1e3f2351,15f6a85b) +,S(a5b57de,fcd3016a,917ababf,171c3c31,ed62957b,cd8d233d,c551fd9,6216561d,3731d822,155afc28,8bd7ed39,30c3f6e3,58a46f5d,d9fb3ed2,6d7c7da2,a8dee233) +,S(da179b61,81e27ecd,d545af40,ea5967ab,7c0ea9ad,68a8c0e7,4d4a5aa6,4aafa549,c5c97c42,e74f4991,55191cf3,3118f05e,19633eac,ff5ea8d0,7ec866a0,7fbbef29) +,S(42a0e864,1b594b98,90357e4e,8c32422a,19f368e8,190170bc,6d8abca0,9cf12f1a,bac7a5e5,baf7ddf4,934aaeb9,5078e0a9,c449de0a,cb932b13,a93f474f,52312d85) +,S(74c85e38,944e2003,435bf345,124656a,6a409bdf,fd1c16dd,d1246d45,cadcaaf7,bc18315d,caf237d5,5f3ffc65,41bf9ca9,f39af9f0,cdde6fab,f602e888,dc51bb7a) +,S(8ceda42,698e5995,40091f6d,f50388e2,7c229b0e,76e7def7,f8986b6c,af975e79,a8915f5e,7c524e55,5743c392,2b31c426,71fa938f,84679389,b3738af7,59e92430) +,S(f44e2dd3,ffe43da2,66fbaa1,990d1f19,e3e4e44f,bb565ec8,74af57d5,feeac498,c307b3c1,a90da318,bf928a1a,a8894080,29874639,ece7700d,4f4dddee,d005f6f3) +,S(7e30bb38,c9a603f9,d5fe78f7,453d1c81,30c140b2,1ad7706b,601d6b77,5c703984,49c9de1d,1d057fa6,b135ebdc,f0b5ad9c,f9f51cef,95a4cb7c,ebc11d4a,3bbb6d7) +,S(8e1e53c7,4f7e1595,b4a5f7b4,5b324f0e,435473b0,a61cdd8b,1607c0ef,d11e3c56,584781c3,ae8a8beb,f6cd1bb8,3938fb12,35994977,2d1facc4,240fa43,7bc241d2) +,S(63d0cd75,54bf2c18,bb180269,2d19b4a5,4acd77c,d2eccb7f,360dfd5d,4b9f22bf,ed79b7af,eee5ec82,e498ebeb,467380b6,1369802c,3dd995ae,c844d3d2,91ddd756) +,S(feef5d30,b382dd6d,a0ec0d81,b18ae45a,8dbe12c3,dd695648,cea27c14,7d075be4,20ba81b6,b48ebc6,b185d6bb,2fb7bea5,c6a011a1,24f49550,415eb8f3,cde3aec7) +,S(21bb575f,db07a152,5ff0a7a2,d7d2c189,742df276,9306bc7a,30c2c3df,f2125b94,8ea74299,b7b40a93,3db6afb6,6577ce64,dd10d24a,89bc3907,1f656f5a,71c56953) +,S(dee2dc59,44cad7f2,310afc94,403067fc,5c1c3d2a,dde88ae0,55ba8bec,d6e5cb08,606542e4,5301a6ac,86c5c794,444006d,d77e2bda,11c8af0,6d883689,7a6b6dd7) +,S(4768a2e4,98ca0472,67cef4b4,18e6398c,81289c73,e617a2e8,ca021155,edc576d8,f8c574bf,beea93d7,1a2a331b,cee494f3,75c1bc3b,ea918106,9f1665fd,589dbc3a) +,S(53d75697,160c2b90,aaea7f95,ad24bd66,b5fd7681,f2802bef,d2a71080,74f66b40,4b04d94b,416a8098,144dccab,bcd8742b,13e85b2e,ec8817c7,7b7e5821,2e8b56c0) +,S(832f268d,ad5f3bb2,dafef9dc,f94bacec,b369d4a1,ffbdfdae,bd4fcd9d,77404e99,a6495805,c9b439b3,26e32f78,8b06765e,8d984a37,86ccb4e3,88b36bfc,c9cc582b) +,S(657acf28,15b8cc12,83265bcc,9c826be2,c6d5329b,351fee50,2ed96f4f,bb1a6333,171a0a97,b2809b24,a89b337a,cbeab96e,7d1d850d,dc577a10,ef3c757a,9c2da9fe) +,S(7e3d0a00,a3378c89,741184cf,36e41bb8,758fd131,eccd970,638f60d9,31b8348b,6f2cc777,31f94565,2a3b4c42,29be627e,bb70d5c1,67648d16,df5470f8,90512517) +,S(649a30aa,4fac6630,c7bab764,19f9a34c,6676f17c,2458fb1d,ac48317a,734242cc,fb3f07e2,5737d6b1,a28b074b,880db818,c9623c12,80268b02,dad90da1,c8d246d6) +,S(6b65264f,537fa580,9d21588,fd86b246,4d1818e1,ec7fc43b,94af9211,c835c986,537365ae,4e61460d,c2c1caf0,62ac870c,9536d8b9,436a0ef0,dee6a39e,2bf25611) +,S(dc1a5599,d5cba6b0,2d692eac,304b642b,db32afe2,59e2e8fc,3bdc8f40,72c66c51,bd18d5f8,f99c6070,b7bc06af,568e28b9,2b39dc31,ad49a842,4bff4a55,d11ff42e) +,S(726d39f4,86401794,9e7a5498,8e8fbb53,b8b13ec1,cd5fa3c2,88f0d933,894e193d,f23e9fc5,68c9e0cc,b258dba2,58b3dd65,7aca0dd4,d9cb7bf,274e62d4,f65543aa) +,S(78d76006,857c2f36,889c14ee,90a8f993,79c1e0f3,5344cafd,592c961f,75aa8ee5,76dfacfc,994f3985,70d98c75,4f5e6d00,ae3ed428,49cb75d1,580e81f6,3d7427c5) +,S(d69c3e30,88874c19,71bf077c,89247826,b464d3d5,292196f,88ef0ad6,892375a9,cc7b98de,86017934,c472bc7b,e56f14c8,7494cf5,6f2791a5,d3132c07,26315cf5) +,S(9b4022c,907c4aa6,87631f88,8d47bde4,faeab1c9,23e125f9,280dcf86,7920ae84,c7468b6a,a7b4db67,a9b4a23e,11fff527,19e454ee,65ed88eb,3b89e231,e504c284) +,S(e638a253,84e985a3,5dca200a,5e3786de,52617824,7ed0c9f7,9c9d598b,8bcf8447,69211bb0,be8fcd6f,cdb0ebdd,a8782be8,a47d8852,7bb74b3c,d98c41d5,6aae614c) +,S(e46758ab,e2838a28,42baafad,12376137,7cf72960,d6a93105,3270330a,203134b4,2c7ca1c4,73d0c3a5,8f5ca754,6604ac75,8c460c82,64f5ab4c,832893c3,fcbfc7c) +,S(c8c8083c,3bd1b84,9b9402bb,9472263e,37e7d2bc,53a77b4c,b0ae9b3d,bc70342a,9085d285,6ebd4446,127ab4c2,fe7e5c84,d5c5c114,8ee76a1c,da72933,9ca6578d) +,S(cf21f3d5,75f194a9,3133a382,514ca64a,f998b099,eb307655,46e2959,f71f838f,657df357,aa09b443,c4763432,45d3eae,4fedfd1f,7fa86896,2ffc5174,d61fc4ba) +,S(684aa68,d4aabda9,65adbb29,1056db00,1c41b900,e11770c,c77c687d,ebcd702f,c6f0c4bd,f6f6193b,928d81b2,812c4303,7e6b90eb,598a50b0,742dd21,8d82bf3f) +,S(b9a4ef5c,acf4a82,77b81803,6bcf2672,945ec080,e16e8c3b,19f7b612,c408c884,1cca67ba,9e98e737,b4a55145,7f254d4f,5d793d4e,22b81b35,f4ab48d0,92b9c745) +,S(ff439f87,d4784ca7,490da489,e2757106,e59836ba,f3fe2e4d,f6700c08,cdb2327e,98f21471,84659fa9,a9cdb709,f894236f,18ecdac9,f5834dfb,b91a7bf5,8edb7461) +,S(e1750c62,9c35027a,c1a5f1aa,c9fb4076,badfd925,f5915b11,a267672b,15945ed8,e17610ce,a0b8d837,d1038d7d,9300edfb,5832fa84,c4e630f5,2b01ac64,a79c0e17) +,S(2b7057dc,1c59ed7c,e64f187b,86adbbb7,1d228e5d,eea347c,75c73d2d,a73b3a28,1b136a49,713b433f,f2c79de6,77b5a322,8003e614,2b845470,5ce884ba,329e20a1) +,S(711c07c9,ae61ac68,f1041b58,c0f2ed07,1106c2b1,bc47e104,527d540f,53690434,e9199a45,2f5a3a39,9506473f,be6bb46a,3105aa70,2c517051,7b4c810a,5a194c09) +,S(54dc7650,68c38627,4619d9a1,faadc0d8,c2ad8296,dd0d00a5,f2e53c5e,1a1a0d4b,2e9b7179,3af2f752,c111fd71,c00f4bd0,8fa646a0,428b4813,885d4e4b,ab7e3fd9) +,S(9c5aecba,3f8b3507,db7cc231,c2c1674c,3c3e27be,84757fc,4f1521f9,c3d9323c,413455e1,9e157ddd,17f51c1e,618dd726,d300c023,f0a0d24b,518c93e3,f2954630) +,S(c22c2972,5f67e23,a369342d,9a4e1f6d,dc1bf6f5,88d63086,fb54718c,f5183a,a26ca30b,d97ad611,4ea4ece4,cfdd3928,23c02838,9723d53f,32c3b38,320eb707) +,S(8f87ac94,a29dd2c9,d37aa130,f8b4e67b,da595ca,a22803d1,e2d8cd4,eba4f2a9,551fab10,16227763,6d7ad2c,bb055439,21743078,e02fe0e1,b7dffd6,8749b04) +,S(bc486976,76c0f638,a116bcce,d7ee781b,876ef486,111fd680,4dedb176,8125dbd8,16d7c6d0,19708ccb,510ddcd8,698c2fdd,bc6978b8,7b0639d7,a1c8ec6e,ee3ea383) +,S(5a6ec844,201d9b39,92a46fe5,ad4f05db,6e8cf251,24ecae84,3a28da7d,46e75ae8,9e1538ef,f27344ae,8f9c1049,c12b49d0,2d5341fe,33652a8a,9f2eb512,c74ef224) +,S(dd6b2c56,b13f0ab5,4e0a75ec,526219e1,4d43ad78,7deae379,1dfb387c,541f4d6f,7379c5ff,1b793b97,c8acba99,97a3c079,f1c147a0,f63a2ec1,a0a771fb,fc3d432b) +,S(74e27534,4d7d8cab,e4995d08,6d61d27e,942e2514,3c6e3040,8402a5ff,12a63ebb,7b5914d7,fefb7b3d,2fdeb49,359878b8,355eecef,65ac589b,dd31ccb4,c270d854) +,S(bea186e4,b5e3663a,fb00230e,d8ac1660,67b022b9,b69b6e75,aaecb88d,d750d34d,35807d83,5b75e3ae,670bf35c,d415d37,6fbc86e7,4f99afbd,c60a0b46,9153a0d7) +,S(17ef6838,a07f65f7,cc094d4f,77743c6b,fde12ae3,b81625c,23826228,650f30f0,9d696a8e,a5356fa2,10626ce2,cf312fb7,198b0937,493c9dde,4711dba1,333eba5c) +,S(b3e1bfcb,38c89fac,af3baf39,80b21e47,d6465473,c9d6a29c,8a08cb18,141bcb7,aa33ab83,2a9d4eaf,2a50f9f8,ba55d0b,c5e3b120,69a8330c,23120928,5f6b90) +,S(689d499d,c754f099,83372631,2f307f0d,807f4f6,d67df40a,318aadd5,5776202a,dd4f9012,8a19dfbd,8f5f96ed,df2c7d79,d451f84f,fa63b1a5,9845f84b,759377eb) +,S(4bfb2e58,9df27505,91a73a4c,6ce88397,a2f68f6e,8f866e1f,d3af9df1,f3c96bb4,a9d94c07,29948553,6433f0ec,b4618d1d,af309c6b,73fa4749,aa949e6f,bdc24b7e) +,S(97a3004f,26461f5f,b6cbbca3,13eecbe7,5026d958,a1804c34,6e661722,c67f91d,8b8e9bfd,b9b704d5,13bb5a50,a725bae2,c7ceb6a1,ffcf8c49,8e87c6ff,e47844f9) +,S(a07a76e2,48f87384,48e607ee,7f30aed1,db657b9f,b159ac9a,3d38e8ad,61050c28,7d7749b7,aa6dcd68,931c3c0d,87eb73d9,6bd5f43a,b633e7b8,f77406a9,23fdd47f) +,S(9bcff8ce,3a280998,fe077cb7,a6e4854c,4c834e21,af118937,9a133986,320b8e25,da3214eb,5cfea248,f1f8a1c1,e8b0dfcd,4171b2fa,5e133854,84a6c99b,215c9ca1) +,S(8f1d2dd,bab8242e,fad20574,c0f28976,d2e98df7,5e144d27,e558b9c9,8d75a036,8bd117cb,294db5d6,a39097e9,8976c701,1042af41,680637d1,c8a1d56d,30800bab) +,S(73910449,2a44d0fa,d27b97b8,df75ca46,caefe44c,741848f5,36b78986,8d966991,d31b7807,e1db48a3,380608e6,5f865cf4,445c47f6,c6094819,da2450e4,6b78168b) +,S(e024cfa9,4fdae1e1,2895590f,222b167d,8a41a87b,b1a6b720,61ccac0c,4caf9dc3,d62f1129,cd84f36f,bd1b9439,6f5ce037,fbdafe41,fbebd066,110549fd,50b0ba05) +,S(a378ab39,e3dce826,6805bcec,b8123eb3,ffada53d,883c752e,e46ce4f8,5bf093fc,db9d124e,4c0a8d1f,14cfc406,4498a7a6,513261f3,300ebe0,99a41f0e,701c7dd3) +,S(bea5d211,b808e4bc,5aaa1a96,a9b8842b,5204b60e,24d5e518,4b4e92aa,12d8968f,caf8b92f,87c4b8fc,4692efb,889ccd64,610fd604,b9669dca,e033ae9a,453bbca9) +,S(7ec06291,40b6bfd4,cc4bd45a,8a953fc2,653d73c5,d366b5e8,b732dd8d,5d30428,e96c9515,da8dfae3,a2f4e01f,f02ebad9,8499a318,8905c0f0,fcbcbb58,87360a08) +,S(9f9698a7,6ccc8bc5,31dcd9a5,b21f3f8c,9d143194,8902312c,b5ab89cf,554c3b26,e08acb08,2ab0eb8f,fc31f7a3,1ca8dfbc,d4b1863e,29d155f,4abb8d54,6c59a221) +,S(ad955b37,a45f2bfa,1779e8c8,e6786af0,dc49dc9d,d692281c,da10b25d,7517214f,b2048fe,ee71c152,8c8f5dbb,cedde340,284af2c7,71ee86e1,8d565285,5de918ef) +,S(e7e56815,9412123b,7737b006,f48b3f32,513023a0,70c595af,1749c564,8222e21d,3ff11313,c2992218,cb3a1c01,e333f2fc,ffdd05a0,c381f8e2,24de6add,85aad0b0) +,S(4afab50b,55c2a433,488adadc,ca5d11,2e225f81,9ab817bb,116109ce,71dd3439,d232e25a,e5a8f6d5,68344997,442bdc54,de9cc597,cc474555,5f64cbfb,6252808d) +,S(2f739ca4,6559727c,cc2fdfa7,c66da19,b7d2660f,cc63b74,155057be,d55313bb,c39350a6,10a844f,80730a2f,27bbee8b,a06d7ae4,a279f490,f975111f,eada018b) +,S(f3596671,79f9343b,bc3238e7,bab4686b,5b7aaadc,95848f20,52adfc33,d15a990e,8b5759c8,e0bfb8ff,e298e824,ef18c042,65d7f886,1af8252f,8c9d9d92,c1822849) +,S(b3feced5,8d79e1c4,d0a210dc,de416353,404f533f,5a1295bd,4d888d59,b43725c3,b07089f0,871f7c3e,440ea905,cebf66de,52c68c04,a6aae237,e23c3272,26d99826) +,S(85c74622,bb28186b,5f1bffc9,74d51ea1,8bd7e6f9,1a02285d,6d8818df,b87d2f9c,8eb95100,63263c67,976fda8a,18910459,df0755b9,307e4145,cfdd18,15b799ff) +,S(3823d01,5b240e83,44c936a,c9a5f7f0,fbbe3059,1c20f39c,ca51be63,98ea7a7d,6e6539ac,8f404753,dda08dbe,f7ad4e48,69a67a93,f0efd0b7,b53c14be,130435fb) +,S(b73ae7aa,e97af1d1,b6ae34b5,f6188c62,33ac811e,1ce5818a,a9f20703,7deaa539,cf495bd7,2df163b1,f6908b7,23042bf1,137a325f,bb8ebd78,c0d12086,e7b76005) +,S(f6724463,faac9880,41dc4918,5e27b97e,889e74a1,81f35b58,2601328b,143688a2,a290e04a,f10a477c,7c0269ff,42285277,f1faced9,eff88272,45f2d069,58897554) +,S(9eff8038,1d546c1e,e1b5d562,947f3a98,427e17f4,84d0007f,eebd71c8,261ff959,bca422aa,d4748c66,6aa99f76,eb8e75b8,55f89b0,917ec29f,401caf83,cec111c9) +,S(93384df7,33d0fece,931587d3,410f2520,6bbbd992,5e6fce75,5432252d,8bc1bcc5,9e3beba1,b9a92647,c630cb4a,69b9bbf,80e301be,fb8eeafb,fa0021c3,ae27c38c) +,S(ca85653f,975882c3,71902d14,6f50b3ec,1dfe1fa4,b6e6d5a9,e5068685,632074bf,a64fdea1,66808f60,736f7d84,850f841f,588879e9,5d1adcf8,ccbdc4bf,4f743bba) +,S(b80a52f6,951aa063,1094a2e2,866d3a29,b95cf239,48753ea5,cf5c6f6e,8e082f90,61c95d57,d6fc3ca7,5f92771,61ca211,1f20a67,9a85f4f,2b8b0fbd,494d55dd) +,S(3d5d3037,3a97dd67,fe45b273,2ce21e19,b29587dd,301f3dd9,b411432,e642a3c8,5c8e3a19,896ef1a5,5cf1ad28,41e47dd6,1f207f22,3388c6aa,23d011d8,fd95929a) +,S(b274f8ca,29a87fe2,8a3a1269,ddc35b5b,44f9e4d9,81af118a,187a7c36,acb155c8,410d9c55,bc34c21a,6368155b,c282e449,c5317dd1,c4e5758e,27493fa0,4f028ea8) +,S(f1557792,8cac518,66f59814,a70c14f7,beeaefa5,33d3b535,df19d31b,ce1cacf8,113dbebd,9fef9cfb,93b5606a,e4b48aca,5b7e9c00,a59832e9,6865282b,80b70ecb) +,S(fbe3f262,18fcb657,7fe56753,42765926,59ff099e,f36252f4,cb5af101,622c40e6,4c872d52,f1202df3,e905a0d,3f9c89f,b347723c,99b1b273,8f3dbb83,df0fa2ff) +,S(ef7d13fa,25ef46c5,3663eddb,a677f6e3,b24c7e7c,6895e731,2c6993da,3d9384a2,cda2cc35,114e06d2,1f6f896,666c16db,78fef74d,2ee46fb6,a4a34e09,8546935f) +,S(8e34b93e,afb7f027,cdf8696f,a6407213,91860119,cae17b66,d4f16175,fd7118cc,3198f5fa,27ee190a,e56813e,910d917e,3bb8c850,20b0376e,e55e88ea,eb56397b) +,S(a8291a3b,8e544240,a0f0dcaf,6369b2cd,98ab80bb,4dcbc2fe,e023cea,1fa52399,f8305f7a,b1993569,82b8f2a0,1fa0e808,46a506ef,5cf0ccf4,fcba9211,284e15a8) +,S(bd36854f,90dcd6ac,978c2545,7800489,35c4cca,79a0f9b6,bf461da6,5522c88e,631a3d75,7c05e2cf,4a445b22,73cf663a,332610a,8461dff3,846503fa,12e65ae2) +,S(fad86e9a,ae78aaf2,e55e93f4,b8ee049c,73b86524,e942ab93,f05f9dfe,8b783116,657988b7,20822c63,dadaef15,a5cb3d49,be52946d,53b62ed7,1814ef3,b5aa4b7c) +,S(e1235938,db5e1f30,fa0b4d4e,a9e1925c,6308917c,23efde51,47fb818d,a39bcb2,7641244a,7384acbc,3133fd6e,9ed61af4,40e3b951,751f9d0c,384328f8,b87bb2bb) +,S(40ec62d9,7c9bf05a,cd4bd50c,137cb143,8e3331ac,554098a,57b6a92,afb137da,8b1468fa,f334dcfd,5a43d405,aacd3f31,7e868af2,73498367,4d0e5a06,67186a8e) +,S(f6f5c9e1,c7d94d25,9abe1a8d,745dabfe,59e911cb,c917fefc,e2da2a63,cacb26a0,7f2454a8,550b385e,f49c26fe,bac89a9f,33bd6c9c,2eb05c98,4303a16,6b21ca51) +,S(be4dd8db,c703a834,9e24773a,acefd0d5,70dd2ec,ef07bc70,ea8156bf,d5b516ab,46a7dc2,5d56052e,dc64a0d1,b414da49,dbb218d8,5abe14e5,c753eaa3,84b958ee) +,S(273f227d,448ee218,1e43f2a9,89d5942,54eae85e,54fe9f76,fb67ce81,169e1be1,67cf461a,a6c7ac60,1ccdd925,b4b38e84,1998cbec,cabf9441,29575438,a855f35f) +,S(94ca507b,96c063b5,bfa9d74a,c92e656e,db186570,9a017ce0,e5270bfc,8a8c4a31,98daf409,49c48c6b,8c674361,17aeb669,36c76169,8d39eda8,9922490b,4fa6a25d) +,S(91d6f747,c1c7e012,440cf7c1,34ba64f8,9ef67b54,3a57196b,d2bd6ae8,f5ef4356,e2117eb0,4d4d29cb,5b13f628,f69e5eaf,2befc095,2fa361fd,f757430a,168c19d6) +,S(8e2c4d62,c17835b0,97ba07b,37947515,e710e825,15bc614,75a66ed2,ac4cfb84,7b7f7e56,22bbca12,2863c230,5271ad25,b5e4195f,cb96e5a5,ca2be6d5,273d6532) +,S(55738482,d32c4248,1bb0d1a0,666adab3,8bd2441b,9436b690,77925e15,2d8c948f,93a6705b,ba5e5b36,e73fe83e,e0ea81b,3de393c9,1c209e89,af90c539,9af5ea2d) +,S(7faa0f45,45fa7d68,918396f6,19beb941,43d93f86,c5a26539,5016d386,b3e85086,cde251a8,3de1f2c7,11b884b5,33226baf,cac36791,1ff30e19,25f499ef,27da9c06) +,S(61a766a7,7bf6f8f3,a07ed7c6,6648606c,69134c6f,4ab4931,9033f9a5,e8b7d191,7dfa24d7,6bba181d,7bd77606,c73b1635,46f41751,c05c34dc,ec54773b,4abea4a) +,S(64317c72,4f1207dd,a6009736,6fd2d189,8b702e71,8cf84fcc,7c50427d,7eee171,d1d45f23,b1c5fda9,ee05652,dd30f898,c75923d8,293d6eb4,18a0e892,ae400895) +,S(4c328f82,5923e7c8,9cdba421,92ad7a37,5a328323,71242c4a,e108c771,fc47356,322421bd,92c5142c,efe89e59,4689a7a2,e8e5507e,be8b505f,9dc9ac86,9d90ae6) +,S(360aa32,797804f1,649c44aa,b38fa9a3,5d011301,708ac7cd,a4135c13,d86a6758,ce4b0fb6,6957f5a9,7e4ef923,ebfbd850,4f6d4098,5333db66,1152f1c,ac6eef59) +,S(5b110b6b,d9ec8ab6,635f5016,bc597f31,ec22f7ce,cfeedd2a,1e6f883f,64d2379e,9472fb70,87ab6f91,2b10c6ff,995af03c,5086c034,f0f69860,ead562a6,e409c8b2) +,S(be27b39b,5a657cf2,9ebb1b16,45f5e1d6,bb6e32bf,e3a52b08,a916e091,9affa38c,93fbf27b,5d03ae3f,471ad91b,69e74fd5,a8f2f925,2bd16473,62c4feac,d26d1dbe) +,S(8ec65d75,c599fe6a,bddf3cf4,6ddcb4d6,1e9b5324,f5e7b7be,5407cb5e,28a9df4f,3d52541d,49ce4b07,7b543747,3a219db8,29680777,3cdcbf80,e4b47786,14ca082) +,S(6edaa1b9,ea8c2c45,6c6a34c6,13ad60ed,aed18a04,32e96030,ac7305e3,2dbbf94c,c64c8279,40cbabbf,fcdd4db5,1719c53e,f8cdb650,67af48cf,fd731618,bd304b7f) +,S(3fb5f6f6,60847e2d,e04341e0,26c4102,68d5a1fe,880602de,c07be98c,22640a29,ddd43c38,95e663fa,5aee3227,4a16f532,df3a180f,e0813ba,3ba113d6,f6fbd9cb) +,S(2d740009,d7addda8,a802b263,35d41284,b3b9a119,248a3b,29b4a532,654e84c2,cbe1e2d8,1bffb79a,6f9292f,43ccbbad,e763a56d,6d68cde3,d946f310,dba8f28b) +,S(15bf4abc,1d74793f,6ebf2fe4,aa74e34d,b359ad76,3d5624d8,19c24386,33517436,5efdcb90,259b5dca,39054a98,b6702fb0,f2fd4b61,235ed4c6,cd1f3dc,dab15f12) +,S(feabbebf,3c6ee0ab,9b584162,476c8e9b,f7523c30,7874d7cc,c4a6b88c,b118d423,c51d328d,276502fa,f79168f7,3787d3ac,b4e7bb7e,1f65b421,64ad6e93,2d405460) +,S(e3664247,929c3455,e98de868,60010f47,4258c686,4d4a7c0e,4bec28d1,b9af3188,7cb4bd55,ba579fd4,a3066f83,710a3606,2fab9f87,34311609,902407a5,88454bc7) +,S(d570f391,365aa2ee,29b6db92,2a96e4db,f1820a6b,94fbb8b4,aec45ae2,f97a21af,c0841986,2b0adbae,1cb192e7,bf80dba1,830520e2,35e1e42,569a31cd,fc0c1543) +,S(9cfce5e3,5119ca91,38629b08,c75b77f0,db3cb593,b2bf8dbc,b7d6fe3c,d1eba55c,33891655,226885b4,cd74d8ce,f29e978f,60a71bea,be99a2c1,d2071f53,239e4f12) +,S(186ecba5,f9998439,f6a3dca1,a179608d,a15adf9f,f4d2f386,4268ec3e,e62e4407,ec7eaeb9,bec50709,e8dd761f,6253248b,2fd064a9,638953bf,510a38ae,c7c6d24) +,S(ad52f2f6,fe0b72ed,2a0fe483,3745aebf,acfae58b,b40a0291,21767058,3e0f6b83,b0cae754,85e3038e,c6dc8d,b4e95a54,63e3e6f2,1b99d991,2f521663,3eea0c71) +,S(aa06c0be,7d72350e,6b094df,f4dec4c1,993588f8,5bd82a1c,158f92cd,eaa7804,ed628163,29369fde,f0fac2f1,a533fcb2,a704637d,6dfaa1f4,a3b57f74,4efbeed0) +,S(5f7a102c,4bdf0a06,76e1bd4a,4df17a3f,124bfee6,312b3972,c49fc4a7,14b3e867,85f153db,ab40a9e6,747aef3b,cd30f59b,fc9776c3,93293222,8f534d59,9efb30e3) +,S(31a4309a,64c225e,69567574,e05bb994,3fe01679,6f09a0d9,358b1237,96cd3fe0,9b9862a2,916ad0f4,1e588567,5a1e357d,19ec82c5,b31f967d,af058e1f,da7cab16) +,S(8906bd2a,505e8401,cc89e32,b9362c84,3443b4f,bafb7c03,75bc883b,11c3e59c,ca78e1d0,1c6fca33,b39d690f,5b85c764,ee15dbfc,48fa78dc,5f7d7cc4,5323d187) +,S(41cc93ec,a27c4fc4,19fc1c8e,4568744e,dab8917d,d8936cd3,2723737e,e36b4f9d,c11d8299,bdb95efb,842ba828,a6d89120,1fc6a28a,9f208430,c1e2f748,bce6b7ae) +,S(6e1b0bc6,199c3b84,220020e1,1a698e74,2fa97a58,c498254c,d3af1e8b,23ef34ed,25e0ed2,3f04706,6b4134fa,b6252276,b2205bd0,7a8ba64c,636e0b0e,1d6d0217) +,S(b55741a5,7b9c7270,dd1e5115,995ecc55,8729832d,9f4e373a,6824d9a2,83490eb5,9adffc91,52eca815,465e533e,c82bab6,ec6b37a0,dbb3b6f8,c3bce25f,6e3d944b) +,S(fde1167e,67f0e084,251d50fb,bafb538c,9c375e4c,fd6441bc,59a46104,46ff090f,662451a2,444d39f9,58593a02,e9a94ec1,4a1b8d87,9dc80c5a,73bb68fb,67af754) +,S(bbc374da,188df5c6,5a633621,804d22e6,f7474faa,47a8af94,a1cb78fa,aee68040,c2c74abd,17acf272,6c03bbfe,7dbe06ce,eb1f4e0a,b0001aef,4d8f342c,aef50186) +,S(bde6680d,414ce86,548504d5,55c83b18,c9f94e45,f55b6683,166cc81f,583e0eb,2b1ce8a2,6b5bbd8e,a087eaa6,49f49a48,2891abda,87c2037b,a73409a2,69b5e2e8) +,S(8f9ba006,faf50323,b9637f00,9aa29cb,8e402219,75d329ce,15796428,6c84e5d3,1bb33199,e5c30e98,5f5eaa1d,65879a3c,703b7a50,cb04c7ec,a6c7512c,94180e16) +,S(19f03bc4,3b61d7a8,62bb81e0,f4b84c02,7359a170,de0108f0,e9b9e9d3,95b63481,8239d21c,f31ae885,ae10c62b,8be0ee1a,95db0368,cf0488c9,4a110a76,7d22dd73) +,S(667c2647,2261828f,2653a0eb,e70de204,29231fd8,132961dd,ba3e4853,8e332941,5758f0ef,4362f75b,c4356b3f,cd8cb2fc,3c77cb25,b7d65c8f,ef7b4096,e0d879e5) +,S(86558922,618551b5,c4c66c50,9b81a622,ca376318,7c558ca1,8a00a00d,cf100ca8,32cd16ef,955deafe,e3fedb4e,55fd2071,8e35c1e3,736dba12,9f3c3287,4413739b) +,S(255bfbc3,d0b0d6e,385905b4,af6ee2a0,18109db9,73788509,2d5fb275,f1b47b45,b1b1078c,afe453b9,a35fbc30,c5f605b8,4a1f61a3,1104b406,ab5014db,57e2b167) +,S(acdae8d2,eeab48c1,9507f575,6ba60b0c,e9d5b6be,e2ac15ba,d31e8932,fe89e14e,b077540,ab137999,7e7dfb3f,5e3fff46,10c2a369,2306ac0a,418d5026,caee0466) +,S(162b0c77,da3bc13,a62753f2,b7b19c9f,3fe864ca,66fa2dc4,226c5cd8,ca8d7112,377956e8,59c2f465,2af5159,bdfc86c,df0bd742,ec7f9eca,6e675b47,d6eda222) +,S(9453c9ef,afb6a23e,40ebf013,eb1af363,c28a6e14,d235c7eb,58d3d142,9d1b498b,65e3b737,ede3c00b,5f322b38,9b09a68c,2ff44f30,c7737932,4e9c94c9,bdd78961) +,S(8409ed29,38a5bec8,669e24db,88c1b6fb,b397ba64,d8a1826a,f011e2e8,32e85506,573a0053,1be979e,1ae3f2bc,3d28ce2b,7a3ec55a,bd00242e,73523a2,582500dd) +,S(3b55d457,b1878898,9c486cf5,3d1ef107,4161330c,64fc2250,81096148,f5ea2c45,24780fa0,3e83919b,9e3491a3,7e0b76d0,f5d32f2f,a4c3aaf0,d2e7c955,43476fc6) +,S(d3fa2c21,f4704812,a5b582f,9096fc87,422af325,5c5d006c,63e1e9ff,d2d9ebb6,a6aea607,9ed334eb,9355fbff,27650096,ab19a007,191d57b9,4a8fc12b,a59bbab9) +,S(a938a4a9,9dc45307,29f3bac7,4fcd7241,1e56cff6,f53fb048,c727087a,9a54b374,3949a589,388ef8cf,aee8fe82,fde39e01,41ad91c5,2beaea97,e7494659,1a27bebd) +,S(792220fa,aaf49862,479a6de2,35be75f7,e811d12,7420571f,e46213a3,e77aa93b,5f274791,31fc7585,8b82b021,52057cc4,f1686543,3eb6e22a,de56bea5,8823131e) +,S(b1bcdf1d,57699459,1bc11d27,cb15d84e,75eb3d5b,2f9b4bd6,1c848c14,e9b7f2d1,a2115735,8c35ac77,e116a0c7,77917e62,e1d09897,316e2389,41d8e170,90adeb50) +,S(735b746f,6f0d453a,c45cf288,6941cf13,c63899e1,a41ff3b4,cc1a4c74,a9b2ab33,e4c2b67a,c6bd7917,2478b52a,994dccd4,475cb24a,ed1eb77d,a39fbcda,9c02771f) +,S(1640d0cd,ba4b4fc2,f3670b35,2485027e,2724ea4f,df918b72,3807870a,39fbcb2e,a0a71f4b,ba481eab,999dfc3c,15275eb6,b06d129,48000828,be4754b2,749bae30) +,S(bff651f6,df72e476,a097231e,b496618f,6e7d621d,dfd45356,8b08c718,e138caa7,10dbc188,2d0a17fa,d769f855,bffe1a81,14183445,5d9ae144,e6eddb52,8e67f99d) +,S(1a3fa743,cf429f3b,49e122c4,5c6d6e8d,18c633b3,f9903c9a,207f0405,c2716b69,b1b2c87e,65faf1fd,debc377a,bd9648cc,24abeb81,75efa4cb,5f0a0b06,aa2f1e28) +,S(bc2a9ae8,e2940908,ef5420f0,9ef4c2b2,ae988e5c,c0c0efbf,62f8ea05,b305ff21,ede884fe,6b48627c,84c96869,5944be60,df3ed5c6,66f16b56,188a9e1d,6bb14e80) +,S(8cee080c,e9204a62,502ae88a,15965abc,b2a8f18c,80f6c461,24b073f7,63990ec,3adc08be,7bea0bc2,b2c5ca91,963f4c40,bdfdaaea,318fe3c7,866229e7,e53c8fb5) +,S(2fc784eb,f79f916f,7f57dfc5,8fc62e8d,bcdc7dc6,70d6a444,c13f397d,ed528e2c,56c58a3c,be510393,90a88028,12d03658,20141c,b1b24241,241dba91,74d6eb37) +,S(fcd83e1c,ec7a6db9,1aef0894,cfb2e1e0,d2b48fe4,88bcd28d,c14e7824,6e62aa35,a86c9321,e2dd113b,cb7ec78a,d9d4db68,b5ddbc01,70aa0f67,5586b08d,6694a853) +,S(4e4fc5b9,65584dc3,40cec4ed,87ab0610,d674e1c0,9d76d28f,e28888df,7f9be562,d2344fc3,de7e5372,ebd87d0c,ea5431d3,2364249c,f6c350b6,d7aa2353,26e9ac7c) +,S(8df04fe6,e93afc18,b0508831,320caa9d,b21fb740,46bcc364,aa80564f,6f89270e,d7b37dd,2463e50c,583dde08,ca253215,4d52bd8e,a4252c55,6749641b,71ee7214) +,S(f662c961,3102fc4f,afd27352,cc697de0,1f4a1406,f99a0656,6597b96e,cbc99305,43783b26,759a2eb7,8ba4a3cd,e865d1f6,474b1ae,26eeaa2a,15220353,f6bce384) +,S(2052f92,b66998ca,f172ce,15cde941,484c5ee3,d3ae329c,87f339df,ae944439,3e9ef8c,9f4257d,c563509,6df8efa8,ffd2312f,60684baf,bb9e5d,9cbd04ff) +,S(730aa6c8,9c8abb08,855bc551,1938bc46,bad8f226,4b162386,9f2e1ef7,c2d6702c,7523fb97,799e2bb2,f13ba1d,59510bec,ee682f98,f865aefa,cf3c6578,bf46ff81) +,S(c5773c0,7d65fcb9,7cefd396,b07dfa58,1ab237a6,be3c1739,e6db944b,c7991c59,6afea6af,44ea94f3,ee67d071,4b6654a2,b1b335cb,e914d305,1a60b05d,44cb81dc) +,S(3ef9a63a,5bdce751,48d7c585,5e3e9aec,90d34ca5,1a3b4829,979d67be,c38c83f9,145e65ae,635acb42,e7949060,fe44f350,3535d417,f3ce9e42,4c3b3d0f,b02f2c73) +,S(a1632263,42b900e5,887b1800,196c5be5,11065746,52cb88c1,4011aae4,6d14bdc8,beda5e2c,40b55506,31aae0d3,fd9997c5,3f37169a,2d810b5b,402210c7,7c372cdc) +,S(8c1f5646,65449ca1,f16015a,a876bdb4,4d965993,7ca6936d,5bbb80cd,c2da6d65,7cbacf98,f6f7363,8940358b,60beec4d,1cd1a1d,900f84bb,9db5f784,d4565034) +,S(693ab562,230c1ddd,3493c64c,6f7e2bb3,a74cc535,2b7efc30,630904,e700d5a6,db890cb6,d09d5be7,ddaeec01,299880a1,2e68acdb,dd950118,3c8f5f07,739cf4e7) +,S(d761653f,f7979336,f833ca51,b91b9e90,b2bc04f,7b7407eb,be3c8462,95d59df2,f81a5d74,446e9762,2391368a,ed154b2f,4faefa60,af93d727,8357fd6d,98537c5f) +,S(41de1c8c,a1315711,3435ce37,c2d2efef,24a67cae,59347d23,44bebf0f,a2cecc64,77629b7f,46cf1d57,a03898fb,cc9f4e37,937daac8,12c4df74,846d8c89,60095559) +,S(21f89207,4e3a00c9,39862265,c1762dd3,2662d182,397c1127,4ef144b7,f598b650,e6e30e11,566dce23,b7a6a06a,11e12bcc,978a6b7a,fa9297b,8c978f56,40f8b4f1) +,S(6163f61c,628b4800,dc49f30d,dff19310,369012a8,f31d4a25,27c790b4,c51f068,4e87a17a,12055cd0,4c4bc19,e052a219,7dc5847f,ec984f9c,aebb8f81,7a7065ec) +,S(a68ebde6,43ab3c15,72f5ab0f,85d683f2,6e6eaada,550e2425,f85596db,8e2dd200,487604ed,38a6387a,27e3cf1e,9d4865fe,7fc67e7c,ee26ab11,1f03e0a3,316de778) +,S(9b9fd342,4e065ff5,b82e0c4e,e5efa0b1,d0991087,684c46d5,7885adcf,b352d06c,b11a7a46,570eb251,6562ae87,90eeb403,823380c4,aae99329,de713677,5b5b5e0d) +,S(d80c347e,98db32bd,57ba5728,20c7322b,b295cc3a,ae326fd9,49e7557d,27151fc2,6d734eee,40229d19,465a0e2b,a011af39,62ff5c08,f83a2e17,e5c2783c,a430cb42) +,S(c8aba983,5f814895,e5d05139,7b23988c,7fefedd9,aed9dd20,a9bb0025,49e63ef9,3811b18d,8310a67,532be770,45461d82,e40c0ff5,6af355a7,55ed4708,f1ab0c7b) +,S(1bad15b0,a186d418,bb84a87d,d2fb9255,9aa64547,cf47a93f,73d23953,86b0e301,e432c50f,662da1ae,f7ee8f73,f5d7ac4b,4a4da6ee,5a943d17,7557e64b,5eeab1d2) +,S(720fcf05,dcbb0609,e5b45efa,e055811a,66d84384,16f7091d,7ea66186,922779e1,9045fb14,37d2086e,ae7d6a64,49effaae,17e3a559,571ab010,57f20fb5,80535906) +,S(96a540e7,8fb6647,63d3b129,8a1100b0,88a2c3e6,6da3ebe1,4b6c39cb,f01c4b2c,4debf0e5,c95b9a1,7bec7f14,12018197,88a1c325,12f60973,9d5b4909,f087b5c2) +,S(2f068148,d385bcd9,6fd6b60,b7e331b5,88419085,3942ed16,18b464c5,9c3ca99,596f3fb9,2fa7377f,a9ecc8e5,be8421cc,77339f62,c6f1572d,7a28266e,eb66ae52) +,S(466e9733,ef167fb7,233363de,16d9d510,74d8e5b6,35abaf38,20a037d4,f6b3e45c,73548e1b,d4d5b78b,821fb1d9,94919bac,a9d4e12e,13a2e8f2,d4c5df51,2553410e) +,S(f8a841c,a8c7fb3c,9a334f12,cfed9ddd,d1dbd4ac,764f3c6e,87a42818,a4a8e51d,57ebf669,72e9c4fc,7c435cc6,a85d381,6a969d02,ea85edc5,63c57187,34f0f926) +,S(d8c7d6e9,6719e74b,2eb7ae7,59d82ad2,6aa003ee,58e3d9df,ea97c7ae,6d6b887f,f1bec88e,5e8df038,b069692b,9e44245d,a11feed4,c89ff45f,17bbb6b7,1b0e43ca) +,S(afd5e13c,cabf51c,19c39c4c,815618a6,14a4f461,5fbaab69,624d366d,540ab638,9ced3d42,5c361706,16cdba99,653c00aa,e5212c5e,939c0bf0,3e5a1cb0,a05cba0a) +,S(fb491696,a4a1b9a1,572b462,f4628f66,b8368f3,3ce957d0,6ec29a0,92ce994e,b4fa2340,68741570,a56182bf,4f1c71eb,2c80d6a0,cffa42bf,a3e9d9da,59507de8) +,S(96e301b1,efd54091,80e3aa96,6652a253,78ce4bd3,cf587238,f0367a58,ec45b0a2,1bcdfe49,5ed90efe,7eb853f6,6fdc5b7,365ca2ae,e1d9b787,afd41b9f,c00f308c) +,S(9cde8d26,40d197a0,9ac00167,9b93092a,6eb8b13a,4d9fe2e5,6365a5f5,fc38d809,c86345d9,cf95fe50,40b2b2e6,2bf4267,72cdf10c,988a4f27,836d960d,c98501ef) +,S(d33ba473,10c37342,43e2219c,f3cf4881,2e843be3,c7492d01,80239bdd,20a829f2,ac774707,a5b0888e,b2ffa131,b71035d3,6af5be1d,3a3605b5,751b92c9,9e58a45a) +,S(6b364a32,1b5aecb3,749093e0,1b5229d6,5576e65e,1bea096c,29065a87,e26cf2aa,668ba97c,a01f659b,3687cef8,ac87dcd4,7a8a4f85,4a434f49,9592151c,b8f96152) +,S(d0009cbf,1d0d0673,5f66c16f,5cd4747b,d33b3347,fd7f096b,a026355e,917b5015,41c10a72,3e82bec0,b92e560d,3bf1e0d8,15a22e50,3afb4bf0,e5caf771,569a7dee) +,S(e618f641,a5b443d0,704102b5,40efb697,c470955a,8375e6b,a845c299,f377b701,9d615e53,edf6463,9b3b2d0,8684f8b6,15cf4122,33991a4b,5b7c85bb,f5698825) +,S(789c55f5,d393228c,7cc7920,9e705d29,867a6e9e,750d05da,9bc6dbdd,36c5c49f,8f38e43a,338058c9,39c64ff4,3f8f7bf0,2a7910bf,dc2f9af9,532049df,ca236de3) +,S(8fa4f0a8,1bacc20c,5d402478,23401597,bfbcc47b,973abe3d,71d33e2d,fee792f7,7dfac188,c5d6c17d,9e7430ed,d9038a6,e6fcf47,174347d8,7a68aa4e,cdc46c38) +,S(4d5f8d87,9377b056,afd4c8df,54a48673,c43fce03,d05be56a,56c42a23,356c34a2,b064c60f,a3b75513,577c171d,68acbef6,ac96f665,6a899d54,29e9427b,533b8e36) +,S(7a409ee0,8299930e,ddfc1e4a,db8475ca,9a99bf4f,a178a8b2,540ae448,ac87a3b1,5c4873af,c8a079cc,38a385c7,2ca039d4,4363425e,67de4cd4,15d09514,50f28e17) +,S(ac327275,44973221,dfb8b577,e33bd935,76407a86,77a1a842,bb04832f,68304e88,2b41b0de,3e406560,a57978e6,3359a911,6f993a91,9dd1c2ca,79c9a36a,6a4c8815) +,S(f9168fd3,973728c1,30dda9c6,907912d3,2e6cf9fb,b1c7a058,cc948f4e,44282720,c7005217,a6adf515,d8d3a87a,7e8a8b2e,aeb470db,c014824c,733e9a7b,17111677) +,S(818b9a9a,295d8f3b,bfb69d3a,f4749e5e,b0c1c04c,838ac3b2,9e74f6e5,dc73281a,141d14ab,64b3023c,597e96c9,3fdf393a,b53f45f4,56cd17a2,bd917af5,dca8daa5) +,S(90a416a9,dba0e05,bdd25dce,2dd0b0d9,200aef56,a6c0e16c,748e7d4c,99cac1c7,bc871391,95a2f128,af9c9c0d,56bff605,2fc27648,4e0a855d,6472f0e9,a779e0dc) +,S(678248e3,d48fd800,8e694890,b442d3f8,4f851881,9b152a6f,da24a6ea,88e1efb8,fa5d302b,51a36e66,98477693,9ba9c095,eb8b78c1,8f1bb114,53cb1904,a2185615) +,S(c2333e2e,742bb244,b7dac90a,18c2e65c,1dcb447b,11f8596c,bb3fcb8a,1e85195d,d15b8771,a31ff36d,b46b9ca1,b0ba345e,b877b7ec,48de20d4,71792a78,17ee826f) +,S(7d562291,9c72b694,d81bd71a,e78204af,995a63bf,3e8ecb3c,d9b3f589,c75ef8d8,aa554f52,67d9d3a5,8d35cf02,191b533d,ffdebf32,dde4cf07,252ef949,ec7df6bc) +,S(c5023f92,698f955e,4fcf7fda,288673ba,97b4d275,7f26f25a,c63ccfe9,155232b4,7d9dbce,def550c4,ee4c881f,69010859,c7a09ea5,36a8f7d7,17c1573d,c5db971f) +,S(37dc82b2,ed0e9533,e87867e8,d1eb57ed,707f335,b081d14d,d57aa247,645bb1d5,ff0c021a,d28649f2,3f000ae1,123f909f,1c803a9d,98ef2487,bf5d44bd,a97068dc) +,S(ceae1d30,2bf19dd3,a952ac79,8426244d,968b6ddf,25e16357,553f7636,a15ba8ed,c5a3a752,4d05206,39ecb8b,bf4282df,a1f9c795,ea74410a,ed9e064f,9a37c70) +,S(8bd49da5,fdb92648,f321bfc4,2d8145c5,20b8dea1,46b2bbf2,7a054698,3eb6574f,f769854c,5b5c9058,20ab0765,1a6d8104,e9a9dd1c,7aea6425,2b239ba9,7b0609f4) +,S(d1553a8,9a5226bb,3c5b6296,9f094b2c,1b761aaf,2605b0ba,b3793f6c,6959e8ee,d7b11159,d289d42b,8ca2889,f3dd7d01,e1cd0e5a,745b9f12,ab98b823,3211b2fc) +,S(ccf443ea,d8b17d4c,ec59ab81,74f1b1b4,8b6c3934,6e64a955,c617336e,aa6abc89,5cdbd39c,64510cdd,b342cce2,62915966,f8cef7cd,e10f4a7c,41a0d495,e5bdccd1) +,S(f95ad24e,7accb2b4,ce6afabf,6c1a40a0,172cf2b8,42871778,881ad94e,3f3d8d57,f3142d40,377543bb,6dee8195,743a6d73,e60dc5ef,4b0756eb,68d09d6e,326caf5b) +,S(464189d2,52726cd2,6f5ad4ca,5184503f,c546144a,ce7dfb32,5e4b4147,da7bcaba,910f8aae,acb04646,3c6a2a31,6174510d,8ae8bac9,fdce4c47,6c0dbd62,5196e2f9) +,S(637d373a,7355334e,70c78ae4,99429ce2,a5d97257,c0f407b8,1cb41704,d1b36415,469fa81,5dba5bd8,bb53a5fa,aff5b4c,18e7c28e,fbf21f52,14359d6c,7114c818) +,S(b489330e,2a4a4a6e,a99091aa,4e5aebaf,dcdba016,2bbc38df,5a49d4e4,7eee62b6,e1fed3d,93333ae9,2198b85e,fd68582a,35f6fb60,ff268494,720b387b,b3a7543e) +,S(8f2fbd92,70781f54,9d32ca01,2479438,2ec9cad6,2151c893,64c1cefb,c2324b12,2ae5c8a,2e018e74,2089a4b,12082c73,ba466dc9,851e7226,388e48ae,b1270a1d) +,S(a1871963,e9a65bd3,9e1b667c,55fe1a1b,87fe4215,8273c6c,f338f8cd,9119a828,e6881210,adb32d79,10dfe9da,c46c728b,20bca3e1,ad130240,f0dac7b9,fcc0cbc7) +,S(4a91a506,e8b32ca,13cbad7,99c4e3d7,21fd64c9,7ac8a1b1,e7f4bf2b,cc0ffa1e,66a0ee78,cb63bdd0,1a7c4bed,7bb565fe,832eb2c2,d32f2032,c33935f3,f16a40ca) +,S(61c960ef,2a3a6244,e8e7ee79,d4f5e98,88539c0a,b8be739b,314a100a,375ab66f,13b94913,ac8f600d,dbe9a82e,8987a061,3672629e,45d7d887,822faff2,60fb9e08) +,S(10325f87,f797c48b,2e6c7acb,bc0aa0e0,537fbf59,e74ab9fa,a7630c9b,86a04b72,685c5ae7,26b5c9c0,31e1c830,d513db4,b7fda140,14f8b723,7a9da11c,7ce9403f) +,S(71bca019,c15a3534,e5bebab0,6e1e1821,1ab01e97,dd24e1f9,1f89f834,b2cfde74,102387d9,a8da4f7,a483480b,316f0303,2178e668,f7a72719,2ae7e54d,27314a8e) +,S(7d30b01c,5c93abf8,9a50d14a,535a88f6,7cbb535b,1e58540,661fd7ea,91121fe6,e3d7e70d,1a9c0cb,2d6c86df,53709d18,b1989a5e,7642c1b7,cb381338,f6ed96d2) +,S(6f0322af,c1617968,b2550b3a,cafc8d0b,9445fd7a,4692f933,3ae9146e,fa2d3a4e,ba402962,d311b424,d05f199d,31b07d5c,8534ccff,caf817e0,88712bb4,2e6496a1) +,S(a3719ef2,c34bce90,feb7668c,a07d1a23,e2e6fbbe,1eef4b93,d2995e84,2c74688a,7164f77b,67aadb53,30b4127c,2d71dc46,f11e2d72,25968163,f4ea7358,134e5d56) +,S(3cb0007a,d2f1fb76,aa9752f0,218e2498,b7217440,a6a5136,68e95a78,4f71a72a,2f498749,12e8f469,e511b7a2,5a5046c0,f922a343,24620b51,3f6f0160,6de6b308) +,S(26b62d6e,f46929fd,88f00ca5,a699ac87,e090ab0c,8f02a078,393fb3ad,ae18c055,4fb9e53d,1b643585,4f2c78ed,1a818a80,1ff62c5d,fb0f8f23,d5e4d096,29886fbc) +,S(55e9ad69,68f490d,bfc2e17b,4e90dc5f,58335c1f,7bf30d44,79679acc,8cd7442,d4df7806,f74d9701,378ea804,be9a6270,85767e0f,b816ddec,4dbe34d9,536fe892) +,S(74a87add,723320ea,f1f9c35c,eddb6133,79835723,c74bc043,af36aeb3,5e1cbc45,5c79ef91,bccc2579,eea907ac,85ff8c0a,57fb48a7,c273f9af,6113e177,588fb14c) +,S(d4f1a9e0,2189ffca,c1d9914c,177fd66e,9d9a41cf,d1a38626,aed6b82f,70ba8020,923bf669,6ad7c71b,bfd696f3,9d68373b,9d0cbad,f367363e,2f70a93,4a0dea5c) +,S(593c7afd,c178edf,9eae7fb0,85984992,bc5ca489,16a2a155,a0314d8e,31fed706,ef39bd47,64f3b3b4,cd505e3a,628197df,5aa74126,30d83793,75c85b27,76a1073f) +,S(6622a6ea,569856b4,23e9f68a,78bab680,759a0089,3f6f645b,2e0eae60,f5dc9d3d,e84d491a,1bfc3127,3cfecf5b,65621213,bdf1af30,2dd39d26,d756e75c,4f9bc86d) +,S(d7149e32,fb34d956,1f5b25d0,650409f7,33a28fc1,fa4ad822,eabe98c6,74b3437d,2c249dbd,a1a82561,51b6eca8,a44bad96,1b2d37c6,d01c1d1a,70ceaa38,9b3f5039) +,S(64058ff8,7cadb65,8efbf32f,a68b7b3f,19915f5f,b434830f,bd5ca208,a4a09b6f,71c6dc90,f4d8082f,72d5697e,48fb380b,8486d7e1,5f113cc,28365ef7,d3ee9999) +,S(6a6cbb7f,5b571865,d70ec29d,425115a0,82d1fc1,f0209e8d,47be5e5,a9965dfc,fbf10d18,f3af0e94,b2a6ed30,c610f17f,5bd571a,41d073c4,2b2a4dce,c98ced0c) +,S(3f9a9be2,9a72cf7f,2086d9ff,3701a35a,94a7f81d,4b34a364,916a45e0,d8652874,25b12751,5ff033c1,70feaee1,189a35d8,ba0b8da,d4c943bb,4fdcebd2,1cffdf72) +,S(873be5e6,3cca17ca,95980167,9568b487,3d34c5ce,68abd305,169917a7,30dd206f,6ea148c1,280396a0,7ea5c5a4,17980f48,d076bdab,3df1628a,89da4c45,14bcbd4c) +,S(17865995,f28d4c4f,43e2e9f5,9b20edfd,830a65cd,d632cf9e,e7024ec3,4da0834a,6773c06d,478f3822,86c749c,5ae09a10,910b5666,5b6df747,36d20adc,af58332c) +,S(efe91146,fdfc4865,bd784853,ef79b8d4,d2f020db,3f901f45,38c74c80,bef49172,a81a34a8,dc3a229,8348109c,14d367c8,7abe6753,50d013e0,b1b0372d,2a6ee5ba) +,S(7ee6fb63,36cc88be,3b3c2821,1552031f,d017aa4e,192bc122,973545bf,924e8a8e,a656fe25,846e8173,327ab11b,d8ecd2ed,9d1909fb,21d1f789,588b4fa9,a8ceb0d8) +,S(449677d0,2a78e9a,ce374016,e19e7fae,46efd0d6,4839680a,4d5cbc42,173ee605,47017ad3,3357db8b,ceca2513,c6477157,f3daa99,9cccb709,bbe88340,29312013) +,S(ca9db080,12b9fd4a,cef2255e,83f35fb,e20806f3,9839c2a1,699c5d03,d7b5762,6a1a811,3d82a60b,677d61c0,af995bd1,4c598f08,f915e971,d9f17e9f,8398487b) +,S(d040da2,8dd69fc,67414651,714d4e94,5a27c4a2,9fb44612,d6d87f4f,80e2ba45,a1a72b85,957a01ca,8c8d5d3e,d9ae6a2b,31d2d192,cd7afb9a,4d1e4629,aeb9f356) +,S(27eeb708,fd2d52a9,47f69872,3706b67a,1d6e29b,cc0c86e8,111057f1,e3655608,7f67db4c,25b2e88e,275e53c1,c52eb47f,c2dab668,64b91236,522229d3,1b001a94) +,S(3bc82484,daab22ad,6d887a01,ea9f4b38,bb70e272,b15a7b4d,ab5e3dd2,439d8a23,b0ec952e,6cfe3ed7,4e6246d1,46c9c354,dacbc024,45b1af9a,6a668f0f,6fd52ded) +,S(d0a00060,13596198,8f070ff6,7ea731be,7f97a51f,68faf899,95eb0ad7,746aec69,dde4a735,d6dd08cc,28e2588e,9af0eed7,57017b38,790d381f,74748f30,9717a989) +,S(8efc4c50,51c65e10,56f5ccb6,a4be2014,a9dd043b,641b7690,cae2a8a9,1bbe8ecb,26fd1112,e1aa603c,91eb458e,3874d010,db58efab,5dd9ed20,e156886a,5f39fc13) +,S(e42271b8,881306ce,f166138,7d14f0d,da0db64a,b379368e,fa79969f,432cb110,d2b9999e,7ce9f3c5,8a6c77ce,a97cb9f5,f2860299,9bacdc7a,b7d5487f,8b5c9f5b) +,S(bb068f3f,8e7fbd31,f3aaf96f,de53d134,e3a88b63,9d8d43c6,4b3a2b45,2f8de740,574410bf,903c173e,23d88fb1,3f7534fe,566984c1,aa10f0d0,f54eea9e,7883819b) +,S(a3d0bd11,d728bee1,b166dff1,81c3682d,650fde7f,285ed5ef,b2ac5033,5327cd66,3e290c2a,42afb4ed,8b5a685f,69bdd379,44215104,17a61d8d,bd3dd2cd,805c5f44) +,S(9884434,78bfd51b,d27d04df,b5282a72,4cf82c21,487a8733,9347e560,994debdc,545574c4,359fe553,b694803c,5a07339b,b4e1a95a,b9fc72c7,5d4c8f5d,ec6ce4df) +,S(8cc104b8,58b8fb94,e0771dce,a103a7ae,adfc50cc,dea4cf93,a5f4ec9a,d44acea3,ad73883f,57c470cf,d477f228,8badab99,938b6723,516f97b1,22578f2b,f893b506) +,S(1627b08d,a822d244,ec8fe2e7,d8f02094,111fa362,e7190cbc,11229a4c,2043c626,41f6b029,80fb1370,c1e800de,39507798,8b6f315c,f910bb00,423ff727,e9e04c3c) +,S(459d4b1f,2676fd9c,2aac5937,376dc18c,fd4244d9,dc937407,d641e0d5,491df69,6f22ce37,a6ea6a5d,c9823197,22b0085a,e61fe140,d7558fcf,7bb44976,5de7aae6) +,S(cdfc7b3a,b755c181,72882e37,ff641e2a,94fa4e5e,b1b2947,4cd6e94b,f4f9387,76a11dae,107bced7,c2870dd,62ef183a,d45a02be,ce33e333,dea061fb,d89e787) +,S(5e189861,b1615f76,ac416236,e84b0961,b2639845,ecf6e6ee,8dc33217,b0952afd,1cfc65b0,9194d1f9,71136626,a2c3896f,5a3de705,ff1009c3,49a09c86,ea1667e1) +,S(84749eb3,3d482c59,81cc08bb,8634bad,fe0572d1,42367cc8,57a3c399,821d12c2,146a4fa2,3fecc0de,f00299c0,19406b30,dfbd9ad0,975717c8,7630e726,6e96022e) +,S(d73a1958,e9aa29b3,b1ab0807,2165c06f,daa38c90,6be76384,1851fa3,5711bca3,41f13f90,20633dac,3a5eca40,229ad15b,434c4f8d,4ea570ff,76d15d4f,63ea882e) +,S(1d77616e,1ccf1fda,bd4f0beb,6b60c1b3,1b94576e,bf2b61b8,c085cd79,caf8c018,7d3e5813,cf181271,55f3d084,df1077eb,2bfc9d6b,8ab6ede9,55c0ba4e,5095f23c) +,S(bac936d3,63887dab,cf27ea02,50bd550e,db37cb71,a2a8351f,fabcb1d,6d6ca9c4,7aa18819,8bd513e6,b45d6633,aa1af70d,b1a9c432,ffd1be4f,8a8fda79,a38c47f6) +,S(4d18c5c7,51639d86,b21712fe,b2333d52,5f4bd3f5,e4208537,e1e3bf7a,9343475f,2a60352a,4d8fcb6a,ce01b4c7,63619f16,156868c2,d85e882e,a2e7fb80,16f6500d) +,S(8e473caf,d74fb06,b483d955,ba3e35ec,e99a4495,cc86485,a8b6fada,c4c8eb4f,41708d5d,ae2de832,c7c38389,d0ae46c4,46add069,8e83990f,52a545f1,6ef66942) +,S(91366f76,48ed27a9,2ab639ac,f37aba5c,44e8bea6,1402e075,e307c4fa,ea2cefa8,868fe0f5,565092ad,677eb1c9,8959affe,5af504a0,c929bb58,2f5c3263,114ea371) +,S(1eab20f6,51b63685,a0a8489b,4e3247ce,1592c4db,40cb2f90,7eff1c58,c6609915,c142e9f4,b9522b0f,1db90912,c6f83c7,add6d8a,6bd1de4b,c54503a8,4e873cce) +,S(a0652eda,796ff72a,eaf8cfef,371594fb,e1528929,1320230d,2bc6a252,f4b49484,67e6d40e,8aaca7cd,f2a2047a,fc0a59c3,b4eddc1f,d3d5662f,45bb5792,9e4dc9a3) +,S(83915afa,c936886a,3deb8958,70e926e9,7ee4a6ab,1a5c66b,a1602c13,c4f959f1,86dad7b8,e870dba7,712ca968,1ab63524,2ed55fe0,36a148fe,25b1bdb1,1ac2da4c) +,S(293f0bc4,11454959,86b19fb8,6f738d2c,814523f8,b75b280,f8f9021f,95db6061,12d98ddc,894669fa,86b073e6,5855a0a1,760733e7,b7c5bf5e,868dd977,82a4fdbd) +,S(a829b1c0,d6780732,321871da,2d28ecdf,18d13311,4ddc33aa,8ff4caf0,58fb9ad,c5375298,501294a4,dc38d1f,a8c96f9b,789ee9c5,6533643e,1142bce6,8e1ef5ef) +,S(48133900,866a344,68309fc0,cc18f79,c0e01dfd,ed6a7bb4,e5a4697d,4b9cd462,335b7188,ccea4687,50fcdfc9,a6fa6034,c239fe6b,27cf6460,182a1c0e,c8c96707) +,S(7fe9d310,bbdaa101,b28c1e71,137a7ab5,181dca39,aa8d1469,bc06c856,3c7a70ec,18006c39,538a5141,2371828a,4d73e02,f4699a34,7e887318,57e394fe,75c8143c) +,S(1bd5b65b,d79c3eb,54ee1ce2,541b4352,1b7d6c37,d2a26f21,b51d2c07,103ebd8b,66e81b90,a120f04b,340adb2c,42143ef6,5d8a383,46e71856,673090ef,291d7bf2) +,S(237374f9,306e5714,5f37a1ce,1418a238,73938fa0,8bb971ab,fb5d97e8,e5fa0206,d8410815,fd4049e,f54abd51,fe2053a9,bde7f3ac,f40fe992,21bdcf8d,4b9808a5) +,S(33279bf7,38cee621,c17f75eb,bf78af4,58c1a4ef,a7197456,b57ee679,f2c34bfd,d63a21a4,5dc29462,13750d88,ea5e12c9,900b110b,ce433c49,e9b37382,7de327ac) +,S(ee274c71,b1f43251,11d7e100,5d7e9d97,ed1590d8,95cc6504,3e8ed6e5,5a87a9b,73f2bd1b,1baec4f8,ebbcf9ec,f613869e,8a262d99,7bdee49c,c5b2fb22,b652e9dd) +,S(2f49f0ab,c24536a3,3bb0857d,57c35846,f5dd036,3851a873,46f93d0e,53ab8de2,66b3dcd,d367958f,fd06121b,e42237a0,a9edaf21,79ded853,e8bf3c5,b6a81531) +,S(2d122193,ea92ca9b,46eec10e,2a23268e,9b1f972e,91821489,8cb12571,1e3094b0,b54d4b85,a02f77a1,57a48001,c0a26c81,2c038d0f,b91575cd,835183ff,3a0b7071) +,S(9047eae7,841f346a,374131ee,d4299fef,970f59e1,e4bb1fab,819fecb7,3026ad44,d59d3d2a,2c03be1e,553a54d,2dc61c9,e13364bf,3338a5f0,4e4466b6,86695061) +,S(f689480d,c0ddd61c,ba5aefe3,17aaa497,c2d051ed,d527cc20,16877986,12d7c8f,dac41495,a948d83e,37a9f310,18cd7abf,6d5608f2,213aa5cb,7a2b8f90,98d87398) +,S(4a01c418,be472958,5bae0c45,cf6233b9,ad0177ef,eb6e74ca,ded59788,a22d083a,8a12550b,83de095f,22411b16,937a85b5,a079c7ea,b8137f80,ffa66116,2a1c53f2) +,S(a6c715ad,cdc77020,febc4ae7,c6caf5a1,107147ba,8281d3f3,7945682e,72a5da7c,69b4ce46,2634da4e,e9e43559,72f87511,a26718d1,d232f726,577f0d5,6b49d165) +,S(68ecec4f,aa19ac3d,dd8b928e,8362ed53,3be24406,15f5ddf8,4b8a376b,cbff327d,f725f978,f6129f28,49a1c9fe,b07bc2b2,78fcdf4a,96a6ee3a,70b9b943,98a89f3d) +,S(5701d52e,2cdb7b71,a9ed91e6,95396487,1b656bc7,271299b,62ce0a4a,78dc69ee,eb8e4539,be99cbf0,ad730fa5,fcf785b7,35e1f5ad,f5c16c72,85abfeee,4058ea1b) +,S(8210f0c4,4b2f54b0,62de4761,b7c62d87,42424b13,ca0448ca,ba999eab,73b26409,1c79890,e4149248,b9dcd496,1da563f1,517275ec,79949969,8c6b864f,c03f8050) +,S(64cb2793,4ee57641,9d52cd81,6144543b,e4319d16,fdde7a7e,2bd824c,113bc1b7,c3144c3a,518eaebb,51a55bba,d87aba0a,12d3add5,5a5e00c0,94a2e227,80f0f6b7) +,S(37eb1010,acb889,932d8536,b86648e7,c124a8f5,1005c0fd,ec40bdd3,6369c82a,1da5703f,b387312d,d5a337c2,2d3f63b7,33dcc994,38109c2c,4807fc45,c02a2717) +,S(84bbf980,b54d60e1,ccfacb7,e861aded,b4cd7bd1,a3f7baa0,9a1898fa,22144d2b,3820c4d2,7b545166,2515064c,dc9b4cf,813c2435,e84a4a67,ba47068a,e6e09045) +,S(29e74c02,6b43add6,939ae05b,dc0a5e0e,b7d05bc0,536e23f8,8fed55e0,6f93f1f7,f0f6fd9c,798732c2,1dd1c550,3cfbd56a,71b335d3,aaa22359,75fb0c89,9571186c) +,S(98f3530,1122a0d2,629e5adc,579acbf8,8c0aea34,c3450ea6,784c4ce5,ee9672f1,427b899e,74e0c5cc,c289304d,b9a874c5,1d401369,55dc5b03,e0713bb0,8c3ec3ac) +,S(16af2e5c,4c37b50f,5f2b6e94,341e365c,90548d68,2bd9af37,1d849126,ad2c6c6b,1dd1bef1,6077f6df,32054335,c81ff474,33b7c1c7,d6243dbf,6a2564b0,fb9d8502) +,S(56998d6,bd5b2da6,d452f951,5343a9bc,4ed64463,adc6b8df,6a3edd64,e75e1cbc,ccd52462,9dbda3a3,10f3beac,7b3d3bb2,f1bf3c50,bca242ba,224861c8,1f26760f) +,S(f1732044,ec59c52,86273115,3c53aee0,c7f1c4df,e6e55cb0,e7e37d62,c1e11ce9,16a547b9,a61a6c10,4df1d1a9,de99f54,b42e0ad1,104a6a30,e6e616c4,dcadb2a2) +,S(6e5fcc4d,2df77b2b,8d2f95dc,56ddb885,6030f9af,aa51d86e,20d4772a,854b8973,11939a24,26c1ad6f,90db151e,c5c9f036,3f0b6898,2464fe92,c33aeedc,77347660) +,S(74a3ff02,98c3cbde,ba066554,d6def895,3954af05,f0f72365,680b7b8,4c5e9fc8,23b5f011,e50dd2b6,9c5ad6b,b4dad524,9c6a8abd,f134c232,f40de18c,3c65206) +,S(12a40a59,169e806c,8e32deb6,7e64c615,77a3ca,5c56723d,ebc98ed3,11983bc1,9d97709a,9c644c8e,3fcda745,a70cf3be,c405d230,eedac78f,96792bce,a715c081) +,S(f25dcc0c,5ae3f442,7fc17acc,bf888557,82673050,c3594abf,cea7eec4,7a4662af,52a20297,bb9213cd,d1b4f1f3,4f6168aa,ec2d326e,d9aef5eb,5b497c39,e0642e0b) +,S(fe31ac7b,ee45ffd7,99941eb1,8d65cb2d,38618938,c6278e45,4b87e41b,3c7d0f90,a5f1be4b,e0373cea,c3137a8a,c5b5cab3,e1209c93,2d49b245,eb99a0c6,469c3e64) +,S(c0ff7403,2855c2c9,ba06c2fc,6c9a8fc5,29b4ebb8,be082951,475196b0,ca4d6711,2c53707e,540e0c03,27a10980,25e356a,81fa6a48,b1e41d78,b7fe5515,b022173f) +,S(5b23e392,b87bef43,a3c08956,e1930c10,cae049e,b6f041f9,1bf3de75,4a84f984,c905adbc,337b9e7d,78781dd5,deaa0590,18e20bc3,5114c842,72343879,7a37987e) +,S(2100a6c5,b6b96ad6,37e13430,8d92e2cd,97902da4,7a016ccb,63a20cef,e72ac25c,a1fdc3cf,93560ea3,8418c470,679c5389,7bcc12aa,dc87ee6c,3429c68f,690f7e11) +,S(d57874e,509198e9,ee6ac826,b101ab2d,fe952895,ab288184,55f2d85a,eb2c3387,1c7bd36b,cdb6debc,99228b,341abac2,94dfd768,c9e00e73,8aa58339,d226f940) +,S(ff91057a,e46817be,b3874656,42afecb2,a290d31d,f18c6da3,d6313565,99657c4d,a2bb1559,96ca6cbe,2a375c3e,510df603,69d0cd8,f66d6571,9f9866f8,c6e73675) +,S(7f20e461,ee306c4d,7db625b3,49906235,14aca551,21b7890f,244de5e5,84d18b7f,8cc69fd1,eeb89a9f,9e126be4,3983ed45,d88c22fc,89f47006,ecd57431,4eb5d1c5) +,S(7ca32320,75421226,257670e4,5f277f4d,32cc7b4d,53588423,317203cc,b787696d,1e940799,8f25b7da,8f1212ba,c1dceaae,ca7b1952,26655535,6a98769c,b3da15fd) +,S(bcb7060e,f2685623,9aac249b,f603ce7d,b5c253a2,c998e44c,4ed60db6,730d5450,e03df58e,6d279025,955a4f52,dfce586b,f427e376,ca72e09,36d80c31,5047f57) +,S(30387990,3157ac6,fed71113,a004284e,82fdfc7a,3e08c0ff,1ff5dbc5,dbdfc04c,7132084a,1230ecfb,200ded60,9a1666a5,2f574014,d29475e1,6a061330,fbe19e7f) +,S(c5b3aa24,1147d664,a0b80f33,5dfefed8,a8f7c1a8,c6258686,cb16e3c,c365a249,fb5143c6,5f8907b9,a44f10fd,27064fef,93ee588f,9e77a65b,1cf64e4f,956f5748) +,S(11d8bdb2,b750146d,d78d8c55,6e6ac45f,4d34f1ae,35332673,cbb46bdd,3d76d9a9,1ea28ad5,c92761b8,7df96987,6e5b3243,9fe97661,87b5978a,d7515288,f30a5970) +,S(d6db4c60,742914e,c9c7bfa0,7a716cc5,f5492aa8,5381f31d,f76d089d,a773814d,b0ca5fa3,bc5d1fa2,c62a657d,77215b49,82f72e89,b138552d,bdea8166,359b6a3d) +,S(d28a6123,3b4e073,f1242c4f,5292bf70,9e1ae15e,a654bae3,4239133c,4a8a86e8,89519c8,8c44b38e,ad5c2ace,52d01de1,d74ee0de,5365567,cf1b21b2,2ae8b95) +,S(ff32042b,6a943772,d6f5794a,43420218,2ed31ac2,447167ba,ec82d19b,86751337,16961368,e20f2550,383268c3,9fe62fe3,b5d356b1,da2e4231,8f9618e3,13655f0e) +,S(f2c6177,30a9870f,5999ed69,d34efac3,589baa96,9f660cf9,ac839eee,a10e374c,fbb2480e,b6f191c0,15ee3c58,bd950aa6,7bee7569,1681250e,502e1d76,b6ec9069) +,S(dca14b70,e5e33022,eb559d90,1cf3a533,4c40bd45,7a43df85,e87acd40,90342539,7d038e51,30f77ec7,efe84748,4145594f,87d613bb,85a95715,9d0cd377,91678d23) +,S(9cbb407d,acc7c2df,2d86a258,d5ab2b43,31a18bea,9480f5e3,7727aef7,644416cf,7ab9fb64,c5f54f6a,4e5529b6,eb7834ae,80283bcd,c3f6b24a,8a014d02,b3b80b57) +,S(4552c48f,2d96ef41,aaf27778,a7ab4945,e4f2a2fb,7e1564ee,c375e50e,3516f555,e63da54e,28b70a99,5f35a1ac,3a25e384,6efafcbe,4e8f73fe,d84e095f,126524ac) +,S(b4cfb90b,168dc850,36559181,3d2d084a,9866512,9396f76,879280f2,38e4af3f,cfca0080,37006c35,7fd89c5a,1d444d19,5fd72d4,17fee670,e7b743dd,57f71b00) +,S(8e264996,e4e8a858,a1e32c59,b0df640b,a096401c,e77be6ee,ccd228d9,67dada3f,c9f5a8bc,89322e56,838c6d8d,859b6012,6d0a40f2,8377aca6,b5b4dacd,44931d0e) +,S(34ea02ae,49a4dce2,5ec2dc07,c71e3400,390b3054,83b8f30a,81021eef,5761ed99,b4b6f9ea,c41bf9d6,c4bba143,4639fd57,b8735d27,6098e374,d9324319,3def4c1b) +,S(bd391dfd,8569db85,b567c36c,5e6c378c,c152ad9b,c23d1fd6,1af23e29,543a56c3,9bf167d2,77e6bca3,a8d3cce7,61a36abe,91fcad0d,8d6ce398,877d3748,f2f13bf) +,S(67b295aa,440c8807,95369b98,c67d1d5c,1b9265af,58541994,7353c0bc,47ea8483,c94a0287,f2983c42,14f3baca,a8c5c791,d4d88fb7,30eb5b3d,4309f5cb,c5706f23) +,S(755e0335,284ad31b,5afb5775,fa191486,32b29c84,f38cafce,1b0b3b4f,2d073a42,c4506c75,c8cc96d2,8e31766e,a8ed1cf,531f4f7c,73888831,65f825d2,2b74c47d) +,S(e4b0cb3a,6149079b,5e47ccd,bfc4f351,64e575c9,20cd9827,a83f68e7,e5ba0a97,1604e71a,afa83b19,61bdd7ae,e4be5113,f9e9ddc2,29957255,cfe40341,9e45e5f4) +,S(3baca490,3147531c,905a2c73,277e4f39,c27ff6e5,41f6f2b,3ed4f730,3af0c4a,cf36fade,87b988ee,a4ccec6b,6b70ec01,d057173f,3aa7c79a,6bf4d412,d4bcafbc) +,S(229519e8,2a4dd2e7,ae77d713,21fbc8ff,f31191ee,6604823c,a6aefe03,19d06d95,a70e9af6,14ae7a1f,76224b8f,f5d1fc26,2e34707e,a6bfd26c,8cd7fba9,b5627e15) +,S(59364b22,4a53ed1a,7dbe9753,1b3a2600,2f700045,b0576733,8ee87a9b,2559dd54,b08cc507,70f5f3d8,851908b7,ed79ebc9,7f4f7342,61de0752,e14237c2,14eb5f6) +,S(d4b681cb,f3467c9c,649fd640,7991b4ac,3c66bf50,26ae93f9,e9b0238,9fb7c1f2,6df1a03f,4a5f00f4,8356bb9f,b23f1621,bb18a8e0,5afac4ce,4edbb01f,99a27000) +,S(49784426,fb036d23,a289f4f5,b3b92535,76da111f,ffcefdb6,a4163e55,2c56bc,76de5ca2,16ce703c,6a62ea4c,a0f7ae07,3744a22d,6db67389,9b0e93ff,a69e2a9d) +,S(5b213b9d,3f84b248,a98fe326,929b94a9,7bf78521,106a83fc,adba632c,8ed4892f,8eec203e,d015749f,f01402b1,722214d2,b58af86a,95384ae1,c39a7780,5623a204) +,S(6bb55cfb,9e3f5468,966711b8,6343e4af,6e372d04,ee7f570c,6a12005d,628959ef,75359562,9d633c9f,c2432ad5,f4c9f00,c6f54005,cdffaf82,438e3e58,70d14154) +,S(d760efaa,3a9e5c7a,967174a4,705baafe,d230dadb,4c957cf0,c025ab55,4b2eeb43,c019790a,b1b8498b,58daa784,ba02b7b5,672ba2b6,13d312cb,b4563f6f,e26675ce) +,S(f8be7bdc,6a361b6b,2844c60b,af7fec1a,216d75f6,96418734,337f9ccb,ae9117c0,df66b665,21e3dccf,639a323,5d773c21,4364062f,c5260323,3007e2cc,457d783a) +,S(6bbd2558,84348c88,c75a8155,6529be50,93b06a4,885c5d32,f1047357,a11434a2,842f9093,fd41b2a2,153727ae,ca2cbf00,a494ab7b,eef1a2cd,8696e972,1b1491d1) +,S(e692623d,cda3bde3,d32c1c01,f480d21f,9ccf8f1c,416a7dd5,17f1aab,12d3f3c6,30dab6f8,71f4c6d4,c768e086,fd6287ad,3fd000bc,3b04aa70,35cf441d,44764b89) +,S(86dbfe13,5db905ef,9ec7483c,6f51b75a,71726a1a,27e5131,8d6c0b2c,b0b933c2,371f23bd,45c46d3c,18056f23,10ab6afa,1259c88a,3a55f8ce,cecaea71,aea083a0) +,S(7ee329d5,9c625a92,594e620,4cb48559,458514f8,da94c7ba,fd2a4835,a4f10239,80a58a2b,e17f32da,dc80038c,7ed57de4,c7c561c6,69bf6786,57ee72ae,dd2bef04) +,S(e16a922b,8255d2c9,6ef776b,cda14285,b1cad5f7,62f75465,f8c82695,cf1065c2,44bc0e38,df9ce403,a927cb01,849e166e,6838468d,c7a8bd4e,6ec3fbbe,1f3465bd) +,S(5311e064,23145aa3,10737d38,41adf49b,988c1ca8,e8a705ca,3ca67f98,f756fe87,21c3f6ef,73bcca3a,c376e20f,dec775af,e22e708e,7e500c24,23114b4c,84ea8678) +,S(c09eda1d,dad1ca36,a9aaefdc,d16c5f50,a6ae499f,2cdefa2e,e5b61ab6,69f7fdf3,2220cbe5,bc279362,c5506404,e1df076,52624966,e3222b7a,62a1f7f9,faf00500) +,S(8888e104,18fb5c16,fc3c2b7e,b9ea06c1,da2df71f,c647b989,e71662a1,731628b4,7444a776,fa8f5bbe,959c71e2,c919b761,a2f8fdcb,5e66e5b,65e561e0,928e2d73) +,S(5c70258c,c1030231,d897c9a9,199df5b5,8b920f59,18141ece,d84bbcb8,b9b6c243,d627669a,e3dbb6d8,57d65a42,ec53bdf1,c688492,d189b1b8,67b75c3,f003ff90) +,S(eb58739b,878ea9f6,74b16f8a,406e7135,1ef06377,d9be32e8,1f657648,60e35ae0,752ae84c,8db0c7f4,e956d2b4,5750a5ef,24ea454,8f275f0,52b59ead,e3405ab5) +,S(996af698,ef26245e,35e4845a,dbf9c45d,6fec5564,c1fab0b4,8ac78f06,9840e0e7,a86b564c,ee788008,bbb04ee,e3d66c42,3c6910e7,7988aa54,420f3a4d,3137a1b5) +,S(bd03f2ea,a505bdd,7f39915,23f3e3b7,7532335a,6b5a11a1,4e303542,c00b8f4b,345f765f,cad902c4,56211eb0,bbe520ad,aacf55d9,e9c67787,aa02f18d,ccd88ea) +,S(8480c77f,3dd59c6,329c221b,ed8473e0,8cb0446d,c4e9bc6b,f2dba8d1,7ddf0f1,b055a135,a224f1e2,965cff1,a4f0d764,fed265bb,c644c696,2f3570d1,9af6e360) +,S(f46b6845,fa45e8c4,576b7c74,cd1a308e,f1adeedc,1d539d21,1158318c,ebd4c584,e1ed2665,34a14e05,c1ecf136,8cf8d6bf,fd9f0258,a5f0a53c,8abb00a3,e953c425) +,S(596db19c,38d91452,ad238f05,453bb773,9883df63,a0b545e2,47e5947c,d9f52a2e,bcb35b45,b172aeff,9e4fc5d1,714d6743,46bb4073,6f571291,e6575d34,f8b0d929) +,S(4cfbd998,f9544921,e5f1658d,e3ee95c6,df85b3f9,309f7a85,f6e4d08b,7e535497,ccd54e2e,437e4aff,7fc2ef17,3bed5ce6,b7d6e34b,4b386f36,72140d72,ceba71e9) +,S(c3f056d2,357ffe13,4403cee,6dd5ba3a,a7d2d0d,91d83f25,cb2cee44,34e57f94,1c31cee1,595b3682,fd85840e,312a61e4,40cbbf68,55418b30,bd6b24ab,c3bbf7b) +,S(e272370b,82ffc486,53274eb8,898409c5,9338e024,202be5e7,e18b5ec5,7f824680,d2f4e6c,619f0d,71e38990,f4e52881,de27ee31,ff7ad9cb,11a7f735,80a546cb) +,S(81537bc4,6a5fe897,827aeba4,6e66d576,6cd05a35,cd3fe1cd,9c06a2d7,194be59f,6706c254,125be3c5,92ad55f,6fa6289,4298f23f,2f4f397f,3bc84f50,79bcb8ae) +,S(50e161e2,6bc89d2c,816030ad,45559a3b,49c8606c,b28b6307,6ac6a051,9226d2b2,98478bc6,3f8d6be9,2db02910,67e5e359,e865d046,a5dd853a,fccb7592,72985c67) +,S(76ce7272,fcff02f9,bfc355eb,24286fab,88ba08ad,b15e099,22724b84,315be56,88f98fe7,62e37fdb,b72c1526,59528cdc,fc4c4203,c465e772,38dc8618,78a5a14d) +,S(77935c1,3a7f5f47,13098101,6e10ec54,d5ca10eb,492d4025,365d5265,ef7e6ebb,3c15e7c9,cf230340,44444126,5e74456a,4f3b72dd,7aff0ac1,aa76c67b,ce77dd0a) +,S(b37c3ecf,5813b5a5,2f52fc4,42049653,991d9241,e8677d2a,f9779fda,43c7a255,4361c742,c070b356,387fef42,1a9cdf79,58dd47a0,b7c9add5,57fa425d,626a1786) +,S(90c64aa3,28d43534,271e2f37,3460d8af,d17f9585,516b909a,6a7bea2f,1b12d2c6,79394191,a37a451f,e439e538,5e876c2d,ac397f0e,dfe29114,d14b9363,b76823c4) +,S(a314f637,2362ef7c,c4bec6a7,9396c318,568d9f05,9b6ca38a,c5f56090,c33e5e5e,b3a401e4,f7741130,95179507,2ce3b0e,53d2f51,f2dcaa9,d289e25a,8de01519) +,S(15c25f4e,80a7de90,8c49fc7b,649afd24,4c5a4be4,dfc15b2f,70099f73,6ad003dd,4aa9fb98,39855fca,c9f8bb26,b6322c95,e5d7adae,5aca9bf6,de073949,eb364b86) +,S(445e6d34,90f4ce76,22b6713f,584065a5,e0998d47,1b7603a,2527ed85,5d67d7a0,ecf994bd,7ba618b7,6eaca0,b852e0eb,be7b7f86,70d028e3,f52d1b2,846ce8ea) +,S(def695be,1ded66c3,650e9918,e942e4c,bd420fab,3a4708c2,43ab4e3f,1bf2101b,63569e13,532bf299,a0cf8d0e,a4addffc,1e782a1e,98a2f7c0,e0b64f50,8dd0afc5) +,S(eb364f1b,89938b48,2d32271,551f9529,5013185e,3ee2b534,bf01a288,9a534a88,9e6091fa,b439d0,d96f6a9d,cb468573,881234c1,6bbf4c50,32a6c950,99e6e6c1) +,S(7efe80e6,5c0134b8,49b6c58c,7d4c54ac,873e43fd,1b47f9d2,1d33f9c8,ee26646b,5411262d,63357cc8,d8280243,41d1767f,c25a0972,4c8c7a7f,3ddf6701,d12478e7) +,S(5f412876,3774e9de,6f2f25d8,a1d98de9,edf959a8,884643a4,fa18ac7a,26d6308,d3f6eff0,e336d4f5,abe35a12,4c47ed4e,5dbbe6d8,7d563fb1,8ea89242,aa454411) +,S(c63e3b34,bafd384b,2b60328b,4aee3a28,c7b3ed30,8a7cde45,87aa6c75,6c49c2dd,724fbbfc,11d05623,a5cb449e,a2f9f65f,a3de167a,a811bfcb,6ab9c3d0,3b123ad7) +,S(cc92795e,a0be5e79,757a86b5,147429eb,ca7a7c7,d51a8d3c,93650178,4f6ba5b5,92053ab3,db369d61,3af1093f,50829c73,4618c1d3,610ebc57,420d2824,935ae2ff) +,S(ba73d310,b5170cd1,57bbe14,63bfe944,ce7cea80,1b1aedb2,da3db11d,f1c7ea5c,4114e55,15806ca6,323edc6b,f0b75a92,834c72ff,9579d1f2,27fdadd6,d773c577) +,S(6047bc6,fd3a1c7b,9a36036a,94139542,7d9432c0,f652259e,1d52d709,e5088b22,bb119bf4,5d8ac9bb,1a918e6b,95cdf23e,6e2ccbf0,171579f7,ecfc9126,6d236b8a) +,S(a0600a7c,484559cf,3da255c4,935d2d97,1628fe8a,e75b88bb,1f3c4eca,e68f78d5,a439ac58,42bc6ee8,be096973,d87598da,349929c6,92e37852,e57ac941,eedef402) +,S(bfdef77e,1efac235,c1c49f04,290f89aa,c2e4632c,85f40481,dfdf5c59,d479e2a,e6e451cf,d76433ab,1de85511,9abaa5c7,8e4499a5,fa48ad77,f098c825,206dc7fc) +,S(81ff778c,373975,fb041cf8,ce4bc337,e2c977f2,d6bb2c61,9122240b,8e21234,3fc02add,e92c9928,e94e0ad0,218d6204,d71ef295,4c32227d,96b30b5b,9a61f640) +,S(84c4653c,d1af54f3,fc59dab4,5bbaa470,48f8eb13,4afc8f5b,60a8974a,cbf03933,98ff808b,a4682139,68b73ecb,67fe0a32,85dace7a,19d300dd,bd517e3d,b04ddba9) +,S(9d7ed86a,69f39ecd,c448de07,55197029,f81ee886,7c9a61b9,62d13eb3,abce35ae,d9b27e39,41c6bd1d,87dab6e9,32e8ba57,1dcd2c58,67177ce8,e7ef0fdc,668fccd5) +,S(540b7fe1,ee263e6b,3610bb96,1071d817,b9cdd162,77aaf28,87e7e5fd,df72f5b5,6638015d,591305c1,7a598a44,ba6871cb,8d1d3628,3ea25c5c,846bab2b,cd910340) +,S(4799c5ec,f1fb2112,f479abab,217ed0dc,e936c6bd,14215eee,6bbbb34f,d811cbe0,abfdeb73,4ac738cf,f607cd93,de58c0a3,237201eb,da47a808,3bccaee6,da5c5515) +,S(70a58362,b360c280,626a8698,7e4f2aa1,abdc9f8,f0b8c216,bb88b0cc,133c2312,5e37daac,d2835f3,1d924659,e51858ab,68e3a874,947360eb,c520767f,ce4d46d0) +,S(f345a0ca,857102b6,e225dfc5,7ae1cbbb,dd5c2737,116adbd8,b3d1f74,7d132b01,16fcaf2d,f7cae2ec,ef25da7b,3d51afd2,8f5e9920,5c0d74f0,c1e7b360,17eb2fb8) +,S(c3f4282b,29f253d4,fae8deb7,941484e3,9d2555d3,f96889b4,551e7d8a,eb2a209d,fc562248,8a6e83f1,6bb1530d,2a35629d,b25c8280,2eea7057,d22d109d,4263ac1d) +,S(5542218f,606e7cf3,9cf646a1,3677b0ed,4ff61aac,1dcea3f5,da5dd4a7,d4727632,f1bce63e,a11d5176,f47e576f,66817f45,4b51840b,a79695e6,3a1a1cd6,fb6c07db) +,S(65e81ed2,7d6b839e,5c3829f9,24beda63,98264de8,b2223479,43881582,4d124b35,2a17e09c,e4dd6e6a,32d85d71,e6cc2378,b53b9356,ee5790f6,8fc3040c,e6b6179f) +,S(5066b022,e43a5f83,c4290064,9e2b611e,750a5ded,7c89b8f5,892126f1,e112edf3,381e92b3,e407c6c5,d1c0ab40,6fbe1a99,57643da9,a2118cb1,3b8c6243,9328d9e8) +,S(8df78ac5,c5ff1cc6,df1e1dfb,58ef889b,17886433,61bea73b,a805c3ae,a83e2373,dc668ddf,5b39eebb,7b692997,f22140c1,8347e14b,edb6d343,96017e09,d37ca4d3) +,S(426c1767,7d7b00ee,af8d7d30,1bb11cea,1cebfa58,fc54bf0f,de719937,fb484434,4fcfa5b9,ab7b71fd,560ecbfa,985db2f2,418465f7,cbf8acd8,fdd71af5,7f9519d8) +,S(4f7f0061,bb90e396,63f6aadf,70dd3312,f781b2a2,3867df3c,566e18f9,b4a511e1,1308473f,d8956b35,97d904c6,cf73c05c,fd7c075d,c83848b8,571e666d,dc44b3df) +,S(7393002d,d2e85925,3b23db69,6c140258,8272a847,d834982c,5b22b256,93685107,b61532fc,6706f7ad,78548e76,b334a836,c899a49c,46afa1f4,4579b5cd,27641528) +,S(c41dff65,e88e5c6f,fca3b0ab,6ae69bde,c4215194,cd7c57ca,fd9b38be,feab4e05,9a9ba27b,a0b7e217,d31340cb,acd8506,7f052d8b,f92a4be1,cf59fbe9,6241f0c9) +,S(f619dc9d,6dcc39d2,9155082c,2ed9bc60,99296a8a,982327f0,60fd6208,5765c518,87961d3e,2b850ffd,f080d5aa,cceb1912,120ddbc3,4e0ad8d7,96e74afc,aa4871fd) +,S(6becdd5d,f02a7bbc,d1cc811c,fb83901c,46aa8207,35e81c29,a8d4d4ea,3f5cf99a,a45f7dd1,8949f842,2905c7ee,836004a6,5bb106d2,fb623d68,c2690c14,f181e39d) +,S(cd2390,db1e7c53,f424a893,c2529660,69a8f151,6f60722a,524578e3,e5427196,b817ed1b,457633ab,a3439454,6ed410cd,8140b9d2,e7e679b6,3322eae5,f3466e90) +,S(96cd9608,7f1cbcc9,b017ad2b,c7824285,4047dc3d,f98e7951,2fc09fbc,e3cd33ab,dfdb9090,c8a5ae70,13b8faa7,855554d7,3df381d7,88fd5569,d3a7ef7d,f729d625) +,S(99cd7508,5e9abb74,5fe03e3d,b100f2c5,2a43acc9,4127bd8f,83314baf,70d72b4a,19c98b68,2ded269a,57e50e3a,28b9cd65,344000ed,dac3d3e9,d8ac8370,662ee1c5) +,S(f53c2afa,647496ac,6c1220e1,4627c138,186286ef,b776449,55f9238,b39fdb20,1598c495,106a200,58fe1fd9,b29140e6,a93996d4,fe4cfdd6,74ec10f0,75badbd7) +,S(c127dad5,3b9457b9,51192c79,5793734a,d0a84671,ff6ba236,f53cc40f,769585f8,688e6264,da4e9d1e,fe66f5ab,db078c56,98696ef3,53b3bd42,c344b3f1,3d3ad744) +,S(882bc4c7,651634c,930384d1,3daf5a91,41508df3,2d34818a,e61bb65,40e92bd7,5c456414,430eece2,fc6e95b3,7700f608,9815f5cb,12a09ebe,8a87e370,85bcf255) +,S(4cbc141f,55c0b001,cfd934e4,dbbc2c2e,4b5dc68,72e3388f,2388e54c,e2a91bdc,1749d8e5,30d21894,221b2aa5,96541f1d,e700bbc1,e3427140,24f0a9b,9c0766b0) +,S(bff11777,6bb9bfd6,bbf872a1,3b9b6a62,32465cef,f16c3c75,441c39ea,2975528e,9e7412e4,ae492669,58dbdc9a,b7b1f4c5,d2edefc1,d4f0a483,27f2fd69,f992eb34) +,S(251416e5,887b294f,c752a1e2,3e991f11,90b8798b,11c97033,e470bfb5,b7bb6328,e6981dcf,15fb4b98,ac8a37da,267f1bb6,a2a38b86,3919d20f,72b5bc34,cb5e329c) +,S(bfe2490,569255d1,a62318c8,416e9114,66dec59a,bcca5df4,e64c2f48,46d050d4,f78bf673,577508c8,3846bd5a,312b9284,2283c47e,d4df7651,79d33bda,ba6eea54) +,S(e50d0a78,40779b75,fa121def,456865d9,3d7bb9c5,d566d790,58b1b56c,3fdc7b15,2f2688ee,4abcfa52,9424d4cd,eb40a8a6,a6de26b1,be8946f1,e4ba429b,5b32a6c8) +,S(b3a9d7ef,235df6ef,262e177d,19bd1121,167a056a,3957ffd2,a3d22d00,9a199bab,6a7adf,40ab5386,21fc3447,77c97416,66537460,44113c5b,a73455bd,9658753b) +,S(ec7277c4,20af5d48,3a6247d3,a1da7243,31c0e35a,85d6057d,55a3e526,bb7962bf,2dcd3e8c,5abc47d4,56bfcf,a8086889,89354e10,8188fb0b,c9ab4578,7b91d69d) +,S(91ed86a2,330ef91d,e085df2,a947c9de,f1284b8c,76d9b432,8dee9342,bd297242,fcc226b0,c46a36f3,ac56ec31,bf297360,8375f0f7,d248c669,f8b31ca9,9d056713) +,S(e2a09eb6,6e76b287,36c5824f,829afff2,439d973,e5be5b4,f3c55cea,e83bbda4,6b217b22,123bf3db,f35845f,315264c0,4a67ba18,a49abd08,6b2acc76,ca0b016c) +,S(78501449,bd76ff1f,58cdf2e9,203de4d8,3325ce45,1bf106d,2f474e91,bb0545c1,d0bbcc92,acbad044,218a830d,5f461332,cf488003,4b6f89fa,2f58b708,63aaa585) +,S(c3626c48,95df6e93,2c79d5d7,eac22ef1,d7679f1b,1df1259d,2be08aaf,7217d978,a594df74,250bcb50,dd9f294d,e7322b25,f72cf935,700856e3,c31fc5f,35e61b67) +,S(5c6e0358,ec09945a,c4240f22,3304f744,63269e16,c4f87dbb,99bbb744,bb4afaae,e6168dcb,d719ad05,8020dbc5,b03ec4c0,61b1fd87,d530913e,6df5d498,69e340d7) +,S(4a3fb364,d55fca64,4cc03c40,5a5956b8,a794718a,5b43cdf0,795e5d5d,5f79e7c0,4c4e0c11,5f88ef66,545280de,af9e3d59,3ff7d1ec,d8f20779,70ed84a7,8d660d28) +,S(fdc828e6,e305427d,c925de61,6f57e13f,5d0c8777,b538e511,49cdd304,1b022d05,de9e1037,db0dab0c,fedd8544,3b926a36,88e8bc54,7420b27,c65e2c6d,bbcb1662) +,S(6d70ad6c,375119a0,f4b2b65f,51257d7b,e73a8d69,ab944dcb,232b8fd8,e7021583,31de3a22,c8e8034a,d812a7d,234cad97,978aba7a,1c2996b7,aee623f5,858770e2) +,S(faa9dff,68f63fc5,7ca8c39d,8d950c2f,78a90317,c5d7f787,6ffa035,fcce8dc6,419b6c29,a7c02ea6,c8b7ed5b,c9b6fe89,1b3c705d,2ed2707f,3ef7995c,81a17de0) +,S(53d9472b,eb9a020,e84e10f0,1d5ed8c2,6219d24a,eab4cd67,dd30f084,8fa47a12,624f4c9c,64c02832,9b6ad395,dd51f960,41eeffc3,b3238764,57d21f85,d8c674ce) +,S(598daff5,aac2831e,21d5eff6,319e563e,3208ea18,e3ffe13f,2bf19444,73292f02,3db66869,2538e2f,e3689a41,dbb05504,bc86dabd,e033fc1,dbd7f84d,dd4e18c2) +,S(eefd321d,41fe6468,a7766276,5932449a,d3956209,c4c6a548,c4392db9,b4e39a97,6a372971,d5515d4e,c691b3cf,e73ccf6a,a491df97,570c9f52,3f29b108,c626c319) +,S(5a3a8225,49ecb78a,9c5aa12a,49e02c5e,10c168ac,e053f85f,30921724,96177ee4,482a65fb,f57726d9,d4ad61c6,75cdd5c3,25b6e59,899366a5,3ddbf239,96016448) +,S(e21f7d4d,b2343eb0,1d23d1c,5ec76db9,89e1e624,2fabc41a,b204ca64,838eee55,fb64c87d,ba7c54d6,f1d68d7f,a49d50b1,bd79d699,a75227e8,4363ed30,430b2605) +,S(fd688c92,f80983b8,496689ae,fcc0820a,a3d91fb2,aa4d599a,f48de5b0,98ed491e,7f946da9,9169612b,dd6ad8b5,d686052e,3ff25610,21432c92,f8728187,29e860e7) +,S(65594f34,947f182,bcd1703c,dbb1ddbc,4bf59b2c,2fff23f4,1d363631,cd54af0f,e8d98a21,a603eabe,cc1a1b83,9ccd553e,fa5eb258,5dc94e10,d134f40a,63b5bb1b) +,S(4f47f3b9,8bcf3917,464c07cd,be99d2ea,adf31166,8199324e,d120d087,e14789e7,279da4fb,d718e578,b10d316b,9994dd05,ffa05431,f85db2d6,6d4327d3,68aac28a) +,S(32cd8b52,c027e4e1,7991ccf5,6460f3dc,1d2ea6be,6434067d,bd4880e8,a09196dc,1d3066d3,76961716,1882da20,e467e035,4ad84041,f391a042,9e94d8a0,5d89807) +,S(c454b400,c7946f9b,b35003a7,c51e8037,dd03fcda,f0a19b14,f8daba3f,310d8bec,dffa7652,86aecb65,2744c9e0,21229687,1c305d91,6279b414,1ca1a,acc021b9) +,S(151b7159,4fce7438,2ac83025,6d98abbb,6ca309b6,64ef0235,53495342,34f7b4f6,fc61aa0d,722156b5,dd7ffd24,38100589,1bf236be,a41aff71,c3f1964f,15978a28) +,S(59716aa9,6b60ebde,848783d1,a686e9ab,e337b87d,104aa3f9,4cc7af46,ea8b8710,2e3062d4,a7d290eb,f53ee38,31dd8e80,354fd671,62a01a4c,73c57fb3,4582d345) +,S(81838542,7622429e,832e7a50,b1a670e1,2a626c3b,97cc69f7,abaadd5a,44a92395,18a88d63,d42e3ba2,af4b6c36,9971b0b9,35363e5d,77417116,b58de2d,8b6afb42) +,S(c95cf1fc,af7dd741,479a54ca,40a0e264,a989f0da,99b3a1b3,c4f67284,c8ae2a75,14b32e26,99662bee,51ad8534,fca1925a,a835cd00,9807c5e6,915b8283,63872f76) +,S(9362c939,ea51a649,2ae2dc2b,c01e4bfe,ccdf4612,97932668,2435bf44,e9c6ea1f,944684c5,2cb534b1,4871f48,41e889ad,933299bb,474b392d,a1e85ffc,5763c2e0) +,S(8852fd51,1e5f5e93,c6c3c7b8,4b0544af,cdb7d0b8,497d4bfc,90cb7322,28a77040,b0ea5bb9,c338a6de,7333b4cf,ac29f997,36c248,405facd2,6ce438f9,cb997a13) +,S(59105318,491ab266,2fb94506,532ed81b,74c8f82e,a13581ae,ef6c0c77,80696ad9,3417191f,bdcc1dd1,43323ff8,6e6051ee,921dcf69,9eda369a,afec66ff,a06097ec) +,S(c78bb701,8e96901d,3f13681e,ecc24df9,65b124b6,43f72904,ddf00ce8,bd28a127,ef07ce8,1b9c1cf1,26108dd5,f96ff86b,e0d49bdf,ad3d518b,1004b3c9,4c79bea0) +,S(9eb55ba5,2b5d11ae,3d419ede,5a5480b6,c2aee921,6f9aeb4a,96e51934,e8e4c11d,50094963,25b94022,2642ca89,1fb4697a,5e643e50,19670934,99937432,f020eaf) +,S(79ca2986,d40c7786,dc4d3e6f,71c33233,ad0b65fd,96822383,c11949f1,ab42f5e1,275db48a,fab1f0db,188e1298,4b391ce,6872e216,85c04c0f,3e62d42,d1e6d310) +,S(a2cf65b8,fe2a7abc,b78bcdf3,f25db18f,432ada89,1ce7d99f,faccefe8,e9673463,418e9be4,f6a1dce9,200823f0,5a6a0e31,7924a0f9,44c18b2f,1602a2fd,1ef13a72) +,S(7f6881f3,9f4684d1,3bf35c4b,c350b64b,28eb3c44,2adf05fb,c5e2c1e0,307fef19,20ac47a9,b954e805,db6fa20a,4e1a04f3,36c058f2,1946aff4,d4c24f63,6bd608eb) +,S(abf40dcf,72704e6c,65502d17,aa384bca,5834a802,62ec0de2,fd46ca2d,d9316ef,f605192b,a8c56b66,f3bdb35d,9c1df4d9,94662b41,85e8e6c2,9fde9d27,45039564) +,S(4fbb0d5d,ada948f6,2877c665,9cfe5f57,c6d5be5c,8cbb7bee,71cc575a,f4c92989,d2290911,45c718d5,243e1ef,92b8f7bc,2c77af95,f8666ce8,15900462,35d1c3dc) +,S(6c62710d,d0ce5eb9,5196e202,7c3c1d12,5a25f329,878b1f38,fa883146,b8ccffa4,c70ee1dc,174a6a4b,908b54d,69c94ec,5829bc8d,f6513c82,b0fc02ce,f566dde7) +,S(83ac175e,e220c800,a40adc87,df8450a3,963015a5,2c9e9497,602f930c,37af6c12,b7dd932,95601178,f6db7b5f,98046d7c,e42371ab,34e5f11,31f7cb31,59d37994) +,S(c823727a,348e275b,f52c18b1,43653c3f,8f0a5c0a,e8f48432,a8016486,ec3674b5,760237f6,2222cf21,fe1813da,4cfe9437,edca8286,cf3be63b,5bb05560,fa71235f) +,S(388b02ce,506784cf,2ec7e315,e765f126,23f764a,7d1ef836,fe360355,cb3f0517,ed0998cc,9069f336,c938b639,d6a74e8c,672a7d2a,2fe1dd1a,d7e18969,e7e57b20) +,S(517d4da9,e25f9eaf,7ab6efcd,cc00e6eb,7bfb975d,e8287b2b,d7d15914,4b7a981a,2ea7196b,a14b60c0,78444a20,2b67fedf,8fd119a3,7a39738b,17694cd4,c0dd712d) +,S(eac89275,54b0f71c,4ee80260,4945c5e5,578fdd50,ad68c478,2295670a,80432bee,dfe1ac53,dc9e18b7,1a930e73,cea36af2,8c8326eb,3615f858,fa1d3884,446f59a7) +,S(f77a0877,e7ffd2e2,cba9f0b2,c419e886,73711125,7cee4480,f6529851,6013ce29,fb245db7,29acfa2c,145011f4,42c9d480,5fcb0be8,b7f27dd9,9d532f6,aee2d57c) +,S(747d96af,94b3ae3,4cbcec25,fe8f4b74,e8d2077e,31ca2520,6d0d5613,3a49bd7e,b13d780b,8d322848,de5463af,3dc9c6fb,4cee70e0,da4731db,14916252,4b6d8d4f) +,S(9bf6ae06,6db68c50,4ef9b1cd,6d123efe,1d8608c5,3a1a2276,f1f44493,f8b5bf72,5b0080f7,694a756,f306714,aa9d8b3e,617b7c5e,84d862be,470df13d,e79c8994) +,S(91d0275d,e99c876b,d6de2cd,481fd72,13d9f6f0,e41c3444,4790dd80,cbc72e72,b12126c0,8b03b040,60183216,d1468246,d32d834c,57e687a8,9ce42ae4,d4eba7bf) +,S(e3cd4480,6585252c,8eb09442,74222016,5ed4cb90,462310ac,ddc769b1,b9a111e2,d8821a94,f1bd3def,434557ed,4739d625,c74548d1,44bd370b,180fc4d9,ee04afa0) +,S(3502269d,e8a68ef1,a0d74a0e,1423de94,99b84d3a,1fbef636,d9f38801,ed00d64a,9c4b6259,8c1bb627,449800ba,d9e93c7a,265114eb,99f9b5,7bca5b6a,72e3b302) +,S(244e51da,a54a3c4d,69f7a53e,6ef82bf4,7907bb40,7713f5e8,84694641,1daa2ec3,61f00ad5,f30928ca,a20cd38e,5af6b3fa,afd4def2,f8433609,4f08e82e,d46a0320) +,S(4f00186b,5d6f0420,88ac1221,a13e3aa4,60190cc2,2bd8fc26,4df81f44,1e508032,fcf58149,b8ab94c,67b30d73,4a3a5835,c7f89e44,6aa8d2d9,291c5f6c,b17b2e3c) +,S(10a96b69,2f522f4d,58d1c9af,463ae371,73e6f72d,93aaf756,6267b3d2,f62a579c,fcc7a656,c9d72dcc,1540a638,51328135,85d3a0f4,ba5242f,f5aa35f1,88ee3070) +,S(ee66c2c,2b19242f,4d7a22bd,df36fe3b,b5c151f7,1dd8a81,93fbae52,23064f9d,9d4f46db,7639d9a6,95d51d1e,44b08d6b,4791e443,744042e5,cc9e3933,cc27aefc) +,S(872337c4,e34a3dbb,f4b274a0,a47c1398,f78ce842,fb11b994,b550e9e2,e21934c2,aa8a7840,d6dbf62e,d91577c8,8b8c6baa,69e01f94,5294e3fe,4ccbadc4,c1d64221) +,S(e2eeb213,bfeb413a,9093be1a,7f75820e,5f48dc34,8515ef1c,7026c116,9be224a6,a49c846e,9132e039,9d66da39,57cebf1d,2e673831,3a89f682,c28d2230,1792d36b) +,S(fe264a9d,63932bdd,5cd4629a,dda37c9a,8d2bbb1f,5809d333,80a561a9,66af74c5,543b7920,c6c27b7a,515b5adc,b8d144ae,64118447,dee21638,1d6cfd2,5627fc53) +,S(13806a30,2d32152f,a1ee8060,67b07cc8,fce1e67f,16f21859,e7b60de1,37debf51,ffcb1150,7be5c2a8,90237923,5335b575,4cd83460,423ece53,18be988,c7e5e978) +,S(db67e361,f9d91cff,1d8b99db,b7bd2584,f1b91cfd,77473802,f603ee9d,debf4dfe,6407a133,7604fbb9,35fbdfb,9fd6e2a4,ea6d8f28,a63e7524,172cebfb,d3331e2b) +,S(606d4f47,d1d4cc58,3e30f214,e1ee57cd,98e68b6c,91c852ad,cad6c32c,989d8bd8,81911891,8a074dd6,ebfb95b1,c97b4fd8,5ef0887c,15ae5e88,ec6e97b9,cba46701) +,S(51d60942,e93fbcf7,e230c116,77528c0d,3b63f7ee,64c1da65,692aa91c,af482ecc,b82db22e,3f104807,6338369f,d77dc11,d982296d,e630af66,36cedecc,108ae433) +,S(2553006b,b8474473,29108ffd,cfd9c91d,22b7ad2b,6a0e7bb,72f98a0b,770da376,6173583e,e6b238be,ff3cd6cc,9bba1b48,d89dcf6f,481868dc,773eea98,59ffa871) +,S(52e35836,fb0a1c1f,efb092fe,3c95bb32,26bf9aaf,50fd1ddf,29ef0ba4,b100d794,639078d1,cd1cd22d,135ca950,d754d3f2,bcd11a95,91abb6c7,c4a5177c,53adc86f) +,S(332e02e5,5c089c84,82ee9863,a4dff4ea,b01a7a83,4747ff83,512fb567,7a8ee3d,b1edbfc7,9c1f9264,10c4cc3b,8919b4f4,e89bad8f,34135af5,f16b1c66,9a28b87c) +,S(d9113a61,7d5a4a75,2080931a,84bad37b,ce2f907b,cc05adf4,16750e8b,8951435a,ab1f66e5,8d3a5887,2f664b56,6c2468df,f9d4d76c,a73111bf,57e10d1a,f7df7aab) +,S(71999a85,799030fa,b0575523,fab4111c,f4b60351,e03f8703,1f5a852a,f22131d4,eb171070,d6124255,9adbd82,565c8dd3,fba6b8c0,df939501,4223b2e6,148668f4) +,S(a9436d74,a39c7193,368ccbf0,d24098be,41309668,87ebb8d8,4213e0b3,436c23cd,7b93ba5d,e2ad9b42,ac45ac85,43f7ae59,7300e745,8fed1359,bc392015,540e7a19) +,S(f6288f25,57359179,cd3659c6,1cceef6d,c5a69631,174f63ac,115633ef,83530b2a,d54f0f2d,51a365dc,7ba8b630,67ae663c,b4102e2c,59cfa616,b8878483,6ca85722) +,S(bf685c41,863c9fee,dff2fdd4,ba47d726,1f0fcb6f,b0432a8a,3b7c4f6d,6f52700a,336b1d57,43ca6d28,52e20cd6,ab276441,61b2ed82,ea4e0051,d88ea92c,a66828c7) +,S(672eaa18,1009f3da,3fe74eb7,4b479e24,4b73b54f,206f7342,eaa865ee,f945b00f,3f056594,a113cd63,810adf1d,b76efaad,babcd42d,347282c2,ce66ad54,37cb89d0) +,S(299deede,3b6722e4,4c5e7600,214f85aa,4dd70ac9,df75cfe6,c020ec58,c8c859f7,92ecac9e,4a6326a7,8b01c0e3,6466723a,3d00593b,6597b6e,e16924cd,60105137) +,S(e4268a68,7e6c26b9,2cec18b6,8581bf03,8b25544e,2040d505,7b9823b8,b01c07d5,35d1c370,95a9cc03,f91db5d4,250a904f,b9f9daed,9388b5cf,d6a3699e,b03c30c0) +,S(7164e9,bfa6ade,a42bcc27,db38cf5b,fe908499,eda21bf2,27063fb6,369b1ae1,a2367f94,fe683e07,1cb7ffb4,287cce88,b11336ca,98e6769e,178262a6,b9103d9b) +,S(84d8d32,a4be7402,1c94d3b6,c0913724,b2db26fd,862fee1b,c467a37f,b98b299a,46b92145,46603cb9,1cd5ff05,a237079a,e290b496,19d868f2,8143d660,94e61cf5) +,S(60372ba5,d0eaaacb,c1445979,74a0553a,6e63c4d,15001b76,39f5d05b,84f626dc,8fab368b,c4d011aa,edf7d98c,2e6a0359,5737b1bd,bf701060,d30c9f0e,a8e8c847) +,S(453c17f5,5364f812,a7da0f75,d7c074b1,81cc0901,a938447c,df55ba92,a7decfbc,53d465f2,6d3d86b,a76ad1e6,f147d208,474989a4,64ceb6ce,79972cc9,3e2e2c3a) +,S(cf1f79b4,b765a894,2b967271,85d0c2c4,dfab4c4d,d679693a,4b35b343,c411cdc4,b89916ea,4ea34abc,63b9292f,591b5c8d,5ce802df,9d4f2be,275ef244,7e1ece) +,S(74b07994,b7fe3a27,eff2f9aa,72f8746a,299f9eb7,81f09b15,f3ae595f,a0c4ac37,5464345,f4873a6e,9f4cb569,acc8e551,a9acff9e,5e0a92ca,e2c3f800,1888f905) +,S(f9de1544,89b7a06e,40115de1,fcb7897a,f6bb2b91,5a1e8971,8d5d5ee8,68bab74f,789b5c55,2b352db3,4345ca30,328f542b,ad22e5cf,d98e5b8b,e85646e8,d47a0e6e) +,S(c011f3d1,897cfe72,827d2cc7,c04a6aa7,496273ac,11f0df10,27ab4d69,aa745c5d,8e31fcd8,3842ffbb,a600e771,feb0cc2b,1fb257ab,7c19fe61,67802313,cdec95ad) +,S(16567ed2,4ea1cb2a,15418d66,c70c0587,386201f3,4dde86b,82f687f,95229648,fc4aa805,45366f8c,3ee336f4,51397e3,7f78188e,b6dff3ce,d80c7e7b,6d9f7cf2) +,S(8cd33d56,9acd3607,979b3c17,bf5c2175,ef6f3428,7a25c18e,89a1b38c,979d50e,f4312cbf,f301e942,61b7da0e,cadda424,336b1201,82ad73c3,c907be8d,840f8614) +,S(646e22ba,39927176,4e1df1b3,16767718,79d7a16d,587b9e47,5232a331,42ea2a5b,55378afe,5f29feb3,2a3152ba,4d83305e,f77b1025,79154aeb,7bb8b8ef,c5958576) +,S(2699d984,462e1fa6,208a833b,8af2e32f,58cf2d02,7707b4fe,2e3f888d,82e8e2fc,ed6c46a5,c5121755,5f236a62,21399a40,5fd87fca,f4cc6603,fffb440d,54b888c4) +,S(be4b6631,3fd3e2fb,163530c7,2fa4a41b,e36d04b2,638315a9,1d07de72,c4ced54c,a7a71077,83a27b72,4e8aa644,a88ea181,c770cf6f,489829c6,6660c44d,45fd2ddd) +,S(1f2ad69d,a1438b51,fe02a47,9cf865e2,18bdbe01,259b0818,1b6e7e6b,762a72a6,d4da99d5,7aa328df,3c587a45,2d756bae,fc09c626,fa5516bb,c8781b9e,b1ecd29b) +,S(b1adb241,721a1622,35540622,f8e84291,e1c2702d,70cad33a,89aa3806,f7acda43,e6cd9324,12542fc4,25b8f16c,c0820f3f,26f33e11,1ba0ca5c,b5aeaa54,1fa3d9c6) +,S(b604e2,f06df266,1163604f,2a1941ff,4d6991fb,84796c40,ccbe74e4,cf68e207,306936ee,e887427e,6005b265,38af5ca,53b3cab9,a3aebaaa,96556c46,7db96e88) +,S(afb935fa,1e30617a,9b4f26b2,e7b150b3,f33566b4,7b5b4a7d,6223fe3,9c0dced3,83624f43,66dd82d6,8e4cd94c,464b6322,db0b1774,9fa1125b,a0944503,b0c63b0d) +,S(5cc7252,a08ff892,93022eca,b56a5e9,cd2eb4c4,71161d15,5d7a5113,8b12d235,c272b6cc,42461529,b31943fd,fcb51a59,7a0eaec3,ae8c2754,827fe1ce,a182bcc4) +,S(6c17c206,7c72a1ba,410f2446,e4ae2012,6eccaf88,aa4fea14,522a7607,143c1122,7172af08,2adea428,2f6a3cbc,7946db2f,c0170975,50ad8ee7,f4c514e5,dcd693eb) +,S(fd82afcf,c55d226b,274cd62f,385e882f,716210f,9a14be15,baf7e17f,b98eac8,4aeac666,a1153851,6d66390e,6c7818d3,3de26fa9,3d234eb8,5e4d5b42,853eb3c1) +,S(3d0121e6,c758dc91,8ce3bbca,1884788b,20717075,1e80742c,49b202d3,c2f8012,ca6b47b,306cef6c,662bf21f,77bae3c4,25009561,72dfc630,e2b1a6da,e733413d) +,S(488306dd,3bf3bb5f,24160f1d,67b8c053,bceb8da3,cb1b05ae,7ae9fa72,e3f7b0c2,2356a8e6,77ae2f06,6135bd88,1fab5b4c,6aa57769,3e2d476a,1b2a4db7,fc6c27be) +,S(d7e91488,6786009e,eaf12d14,a23ec6e7,fe41e406,aca5a5b7,47010ee8,8441d6b2,c98389a9,143a8fdb,df4a67d8,7c05cd4d,fff75fae,845e7e62,26c61aff,3dc4dde6) +,S(873a2877,9c45280e,3d196725,5da756d0,ee5ac567,cbb7c52d,c8e654a7,ed78a1f1,9bb9672e,e76feae0,d52be301,983c182d,ec99b228,defb2166,5310a4ab,cae7eccd) +,S(410a7c98,85f531ab,46d99b26,b6fc2576,97723e4,bd9b9c57,6544542e,2cad2ff3,8bc9e686,4f139032,cd443148,4f43abd4,3234e3eb,3825a0a7,db8176be,cb5207cb) +,S(39269060,b38b4abb,107c2230,6f541353,d8627901,3399b368,8b650ad4,cbf99144,780c2949,aad303f,ef8fff3f,b9734551,b45639cd,9437935f,d93f7d95,bc9d1aa4) +,S(bbc52d26,36e861e1,3e593d4a,c6ad573a,e4698ede,f101b3e4,f4aaca54,325b9f9,4a311f44,9f16bfb6,4c5c554,5ba373e9,fc41b7ec,beadb4a6,a929cf0d,79f37922) +,S(bb74e659,5ed6d931,9716b8e6,120f0cb0,57dcce87,150cb26a,cbdf5b1c,f86ccbcd,9f2385a3,bb460cdb,8ec74db8,fc5e6014,f4310665,c5b69e1b,cf96203,1afe085) +,S(67e64d65,badbd97a,bb204cb4,f9009f5a,973700d8,34f6b6a5,395fbaa2,c6eb0672,a122f76f,67490b28,9977d730,df544f7,d725dfd0,93135ba4,4bb348c9,e4dd3303) +,S(dbd294ba,e0775f43,f58ee677,6240eaca,b9b29ceb,92fc3354,de4d4b20,95548050,1987ba2e,3ce8ef0,5b71acbf,4c69f706,e2cb4d6f,354f0fc6,53a7a9b9,5dd1c115) +,S(45c946f3,93658d3e,589fdcbb,6895c97d,60352342,3a3676df,680b6bad,de6cee1,629c7238,db2cd3e5,29e5b30f,a8ba5629,dcbb1ccd,ada1eb22,294f98f0,4cae2d00) +,S(33ad0e14,44be7775,3fe47875,2688e384,2be7ae06,7d38e473,6b7dbe0,c106e14f,d89eed78,6518775e,1ef441d5,fe51c4d4,b2a1e377,eb1a4cf7,804c2e1a,5175f1ef) +,S(f9f78161,4cd463fb,6e358274,e47812b,4b0113d8,3f426aa7,85756c6f,b9e7853e,5eab09ab,51247c76,f87f592d,77e0870,238877dc,a47cf6b4,5fe72940,ff4469a2) +,S(ed3015a6,7a1fec82,f0f7a59c,45cb69ae,8aa0d883,39a05f,f6959c9d,b7d761c5,b7c94a9c,28389f1d,17bdf197,307411fe,1c395483,da8a097a,fb3b5fcf,e392e16d) +,S(301af330,de741ec5,61b71fc3,c5048a42,6b55d58f,800934f5,c59aa8f5,914092b0,3d3137d8,262d196d,872fe1f9,8acba295,fec02ee1,30aa4c19,8c888765,357b67e4) +,S(21e197a7,85953a90,226c1ab0,441e35a0,fd8444b1,92c0e5ad,65588063,f40c1246,6811f94f,18a9569d,4df2ffa7,fd8d75ff,5f66ad1e,62da2a60,c0084381,4437a2c4) +,S(325fcc8c,27804b7c,e73d6687,70db7f5d,3ed4b6ac,feb04003,c61d6262,8fd41385,2110e8d7,24e3a03f,46183536,b208b595,8586202c,2e69b954,d3ee1500,21ae7d8) +,S(48597aad,ec69eb0d,90f60e6a,abf83129,1de759f5,eff24d81,8677248b,ea87d945,c3fd850a,cddc2597,499be90c,24e249b,328728ff,5443fbd5,6515a6f0,4111775d) +,S(58648599,8ac44bd2,e937796,d8dfa60d,c56d94c4,d53cfb77,ee0e380a,53ab8f76,2f47f278,6a285454,475d3c9,ffe9ee44,a93e15cb,b17772ec,67a30943,d3191110) +,S(bd2761c5,722288b1,409aa7a5,db7690d2,73dba76a,3847d94e,3e3d3a7f,2dd35597,42590713,6ab6745d,5e573412,663d4d93,29c4a284,4430899d,68e5ca64,197ad58c) +,S(fac9260f,1d420371,7e468769,1af8c270,dada5d97,5ceda9f2,64deffe9,1990b52a,9ce17c1,ebe94ab4,e092d1d5,850194d1,7e6a87e6,7b283ba4,ba68640a,94193314) +,S(82e65b52,e4585ec1,4bc03591,249cf4bc,e5391f69,7b813175,b3b4b403,a64d6877,627e196b,42d327be,48526318,2dafb37e,222578a0,ccbcc443,7941eca4,a82ac893) +,S(b9473eb4,dd002597,464965e5,b6d95842,543cd14,e18c95d5,b45bb654,eedbd6da,1b6e80a5,377ba31f,b5b6fcac,633321e7,f6e5b2ad,4a54e143,e963effd,6805c3a9) +,S(31113d99,4caaea40,40182db0,17a880ac,64b183af,1746b820,2f29fbd0,d1bcc524,3f476b72,eeeb7c27,f9d04cba,71ac54aa,a25ec4ab,15d77e17,8544a651,653e83ad) +,S(d04adc5e,73e21fc1,8c3b047,4d34f8cd,1cdcebdc,5dabf14,e258af57,860157d2,b21150d6,ad94a07c,d11a81c7,b741a2f0,5cd6a5ae,aba194ce,b874bd19,bfb6c326) +,S(4e59356b,f280f51a,c492751b,3b3c7c2e,3fd1fb58,679b4fb5,5abeeb65,bce6ac2b,33a1ed9a,1d9c34c8,42b95e4a,108c7985,21c1f06,91f3716a,68fa5c5f,89dc73f) +,S(53d76685,9ddc778c,b4f719ad,55f74fa7,85d52300,65c06b1e,4aafc560,39ba3547,168b5451,29170fe2,c53abea9,68a25dcc,2719090b,e0814635,a1dc7267,9d83a01) +,S(7e9107a0,d2830362,7e53e06d,6e82efa8,b1a037a,328df546,a6d04c30,3037a63f,222d8b81,2a99860e,87fa24e0,aaab4ddb,b903cbe6,39c46cb6,3f2144d7,f8856489) +,S(6775b2d7,b23dc08a,beb65049,db8552f5,565a092f,fad86865,c17347e2,a744106b,a6f23c87,fb639e66,9830ed33,34c6dd42,6bab0e23,aadcff44,1d4c73d1,a61efda4) +,S(559885b6,a54fd0dc,b275662e,f66566e2,87a17219,e9c02f68,8c1cc8f5,6f814673,2ec87df5,917d1d7b,f217621e,82c31844,d2bb138b,bc35f873,5b52309a,baf800c1) +,S(6fb774c1,2971b620,b40a17ec,74dccc9a,8c711844,a44a3e4d,e8310d47,bda53428,68ab0f6,57b823e3,35df55d1,ed9632a8,f0825f67,8a05d5eb,9e115ea3,e43ff4e7) +,S(623c155c,2787396e,815ecfdb,5f03bdb7,282aeda1,7c9298f5,306ec10e,21dd0b90,69382be3,a4bae926,47252910,ad1ec62f,f4bbcde5,6259cb52,10ff2158,6b111f66) +,S(c42c41e0,63db66dc,9b32c21c,af94a158,14fd5b62,aaa975d,3ca06925,f6d6ecc2,e93159a3,ad6fbcb6,38f7b09d,40601189,cafbe5c5,90c70103,b56fbd47,d36ccfba) +,S(1103e98e,7a1659ac,4242ad9d,6fb012d9,21696e58,413f71aa,e19fff6f,1c334fbf,9f2d4667,ce5a1285,524d91d9,265d7a5d,da9f5ea6,cd508cf5,90076d57,a975bc62) +,S(9e113cd5,9e7177b9,19859144,598263cd,630f72f3,4da00ad9,84964043,9d82878a,6a09bef7,6e13f2f6,8e70ee90,5dd5af4,495f5965,859a1d83,ef149f67,2643c9f7) +,S(c03c5ebc,d04bf8a2,bdec182b,32d8c726,1913652f,e4547892,3e0dba7a,1fe275ed,f567e3c7,4b9313c4,11ac9411,11f1b62f,74a16f79,e7d90817,b54d4d89,958cda21) +,S(205c4c44,7b84661b,cc8ab2f0,221343f4,140c9efa,fb107c59,b5095b48,25f54b24,94a10344,ac5f48a3,600309ef,8ef94242,a8050865,3c018e57,a7734d9e,da18a55c) +,S(e393821a,58c9e9c5,6e45cda0,334bebc4,359c4b3c,1a60bab3,f1dce2de,c1da81b6,5469cb0f,4edf5f47,80df616,3a6b7074,7b26291e,8830d113,79d84aa2,15814e6d) +,S(38fc36b7,a866b165,a424f0ae,74ca34bb,b05922d5,1bdb447,5df797ad,4d042e2f,a9279b50,4b3edff3,aa8fe039,3415c21a,556609bd,8089383f,f5dbdd34,291c2b1b) +,S(1c4a1aac,3c3247d9,99a75a00,305a4092,c5f34321,d3a33b66,b110331c,85be2b27,a10e4138,a835e3b1,16d7b6c9,46c752e8,dc92c39b,21677968,147a3cab,fa5a0f1) +,S(41d70c82,207c3b7b,716a2d4f,784302e7,e1034555,a79965b7,d951ebac,b7d62c00,7311a36f,cf3de546,37013e84,b2162c35,3eba0387,3d296427,220e5942,af398bac) +,S(bada1cce,dcf3dfc,9834c49d,200fb82a,b13cf2d3,d5da9ee5,c9c35448,352e660a,42e0d3db,511cbbe7,942ff10d,a7d49805,972d9373,a75545b7,206e490b,6c5dc651) +,S(233c0c68,4b9fe726,baf2b7d7,a0503e,7aba8b1b,3c4f28d9,4d095107,2b429bc2,a7bc02a6,2b81154,c3bce0f1,6a0b4f77,d412d162,1c713372,11ac1c61,a8f6fa28) +,S(a675f208,a0d48078,93525e1f,b6c5be3b,ef24ade,8d3ab14a,afac550a,fa6362cd,3f8edef0,3e0600fd,1fdf10e7,c5562ddb,af4266a1,52bfca59,e1235d7,c9e8223b) +,S(4adffdf7,a8a92b65,6f747fe2,92e6bfd2,cd3deb68,ccfd7f01,5639d779,7d21e48c,4d4079af,a6e89720,3a50015,92e43c6e,97fa1e57,11f21bef,c9df4274,441f7d81) +,S(c786e96d,fa658a6f,a9f1fbfb,a15d7021,835ebbb4,3e8ed3a2,c7ea573,c2944dba,81130470,7a10b1e8,74f5b856,b7223346,eef3c562,8959454b,d536494a,f075393f) +,S(10df82c4,64b75895,2c54f335,6fc24ace,cda4578f,d34ae82a,8a1eb2e3,d0830349,dae5c271,86031dd9,2eff2db9,6af91689,2a131cb2,44534495,c3562cb8,dbc5e1b0) +,S(55532c4c,c4483aac,26b049d7,df1da7e1,ef281c7d,bd99d21b,72626443,f2c748c4,58160e32,da446337,d0642596,c17500f7,b0e64081,1017bc1f,4452c4a6,8d2c2789) +,S(fe994c5,d63149e0,d31f6cdb,fce5b941,239c4adf,8d5e6866,37990411,d88ead47,200532b8,4ee2fb18,b2c27cd5,9c3940f3,58c6a2c1,d96ad043,4d826c8c,2a457dce) +,S(c7690e5b,3148cd96,592e654f,2eed33ec,a69d2684,545cbadc,6f6a7e8b,4d3cfbdd,98e0018c,36423854,6dede771,b78bc4b8,c52d956,d26eb22a,e447ab3a,144cd394) +,S(8f1f0986,d273476f,b31d4ddc,de949584,97ec74c9,acc6b0eb,900e4cc,2c98cac3,31f420f4,b9209657,1656b663,a06804fb,78597071,6c138106,119ef214,43aa765b) +,S(bee7ab1c,c23f6101,be01ed60,574ffca,70fbd4db,ae73e5c7,128da065,9f78c042,9187835b,66b2b634,a87d1baa,7666bda3,57dd5ed,26871976,8e0ea9a6,ed102dfc) +,S(8093581b,3205e5c1,90e9d38b,5b5ac422,b48fb5a3,9176bf7a,e1d377,c764f74b,3a3e06f3,4c00cf7f,6216e67f,2d55236d,db54640f,192897e9,fd68820c,bf22ce39) +,S(fc018da2,70dfe007,871e0be7,f5edfb7c,30fc30bd,bed5052c,78a7701b,50954702,9483d20d,af9fb123,723e0c17,7905e94f,462831c7,dceb0238,6f0942e5,188f3a90) +,S(f4ca4cab,7f06af06,d0a8bd7,97d6eed1,7354420d,65d80e09,32f6eaf1,ca12a39c,76770553,69f93094,3f368b0f,2ef6e412,f3d5a45d,1c7edc3d,9550fdd4,ebb1950) +,S(fb03fae2,87265749,787de276,84bfdab0,4993112d,f384113f,1dd41e6a,b438bdf3,ae8829f6,80f60534,50113930,2aba238b,1dc295bc,6598c060,af8393b5,1153b479) +,S(b76268c2,6b873f54,9438a569,5e2d5e24,a92da29a,ca26f18e,143e3290,69638a45,bb4543e,23abdead,c3377320,dd8ffb6a,d38967ac,ea90afd3,23c69373,eeb2407a) +,S(87db8064,9a5a31a6,487893d,f487c00b,143c09c8,fe878621,b592b25a,6c3d8646,872a4547,520d307b,a537d42a,ffbc8e9c,e04d1c80,e3d4f506,b001b0f4,79e92972) +,S(fb9295d5,a7c2cda9,c08b0f39,1fcdea4d,b768b086,dd170d8a,966516fa,bd476acf,62496929,a85c828c,cda71d2a,ae70c429,7bc12f03,5a99ff75,7d66cedd,5fb5135c) +,S(96f749ed,a1fc306,6d133829,774293e0,b179a598,327c3bde,a2e94200,770d12ba,c16915b8,af011de7,614fda0,ce1c4a3,ffeabc2e,2e891e4c,a2d9c277,247fc8c1) +,S(1564d7b9,77b4eb29,e7b23ba7,a3cf453a,8eaadc50,87448fb0,fd9310ec,653ec66d,6d8935ef,a7d41cb7,5c5dad07,c3ca864d,89e49f0,f74d4474,af85bf05,c408009f) +,S(93db7b68,9ed6ad1d,bb61223c,dcca7280,9cf4a49a,14d94f3c,7cae77c8,f1298340,9088165,7b4979f7,26b156c9,d34430db,4a0af993,eeb996c,d091ad5c,4f7bbaff) +,S(2656af7e,d8a2e718,9849cd,793baf93,1c23374a,a3b5596,d8c88841,2b6a5ad9,dca0f4b2,7c7f1547,97346dfc,589374d6,1224048b,ead5442c,9f8950f1,63689045) +,S(a7dc8558,c0adf996,b7f57c08,1b509e34,7819131d,42647498,ba819c12,6d26cbf7,9fc494c9,2bbf6116,14527eaa,1de4708a,f0c19847,3ee9164b,6b9de909,f6a4497d) +,S(1f6d4701,effe09fa,17ecc014,a7a0033c,143fbfac,ba4861cd,d7ee2fe8,5bcb2cda,84c5b017,81ce481a,e610bd8d,96ffa58b,b7a158e9,d1cccd79,7e6436dd,b1308de6) +,S(eefe37c5,d547b779,d905849e,dd1038b,f054233b,f2d1af47,1684e5c2,a1e69960,b5f00dee,8b1f7960,aa068db6,78b6b41a,f8c3e14b,27c1f43e,10f9b34c,fe8eefef) +,S(8d4e8462,60602ee0,948ba5c9,f0506046,d0bf3fd5,e1b93073,bcc8a6b3,30af96d0,62d0e36b,bdffa309,f1257b23,6327506,5a5e7999,b314dbed,edc75800,b35534ba) +,S(da22079d,352d3527,5a80b95f,80b7b8b1,3eb6d178,310b6889,a94cbf2b,9a9a898,e8b06b65,37aae428,cd25fae,d0c1af8b,827b296,886de122,c6690a19,fe94df72) +,S(beb411ff,67cdbb5e,58da6353,172813fe,e386c7,95550395,1ddc23e5,d4e579a4,919f8f30,41f53020,4d3620bc,b0bc80e5,9d87ab73,355c046d,cf2986d3,c9995866) +,S(b0a5732b,76b4a526,e8e742e3,528891c7,3a1febb5,c7657a90,4bc1f95b,96d462b6,a5e4f134,93f9e24,75ac7dab,479092cd,fb59a931,dd042add,3d875e22,76965a9e) +,S(5a87a782,13130193,4ce6180b,714e99c7,30fc8bd4,fd4e8c59,f5b6aae2,f3622561,a929d120,68a64953,90c559f1,ed8ff0d9,bef92431,66117208,d0e8a08a,de9a3d0b) +,S(777ba7aa,6c50e927,64ed93ac,daf56285,9909b031,7c3c5ab9,38ebba26,d62a1748,27a32172,d7ff61ea,b6a26d00,c252f99d,bcf614af,1434641d,cdbd5f4,10e6657e) +,S(ad0f9f5c,8fa07d5d,3c18d4a5,7dc16bc6,3be0dac5,9166fb42,6bfca8f0,65b76720,d2c9c778,3caf2c93,f6c3980e,60ec0fcc,1f1d5ffc,8aff6af,a912b5b4,245fff6e) +,S(8904706f,17aad850,7a71f0c0,94f981e0,5e5a42d9,7f5cc3ed,99b4f635,660e227b,371d5f58,a63ec71b,3cd8b601,ee622586,2822a2c3,99ac70f4,ce293846,23ad8198) +,S(ce32a220,2e276dd,3f5b0a10,38a70f21,3c29ba7,58934c4e,27ee3bc3,c9dadf00,e43d28,80486b34,65e545d7,6618d80c,56874afd,29ea4ee1,e79dbe5b,b1ec1f42) +,S(3d031c0,c717447e,19266420,cef08d59,ac2aed4,7e84c84f,8980e8ed,ee59c7c1,86eabd82,d6a5f57e,cdb52825,a8c726d5,4a9faba3,6272441b,22627c4,a321d970) +,S(9dc08947,fa3983dd,44730aee,d8e94f2c,78eefc44,df126804,281f740,53330b43,58838246,3fc02261,c0e771b9,3d4a90cc,c2c19f7,2893ffb,22fe6d5c,6c5e7318) +,S(f9fb51c7,29646acf,94ecadf7,30989d5e,669ca4fb,3b4dfe66,dad9838a,f239057e,d4a04a8f,f44b86c3,17ce4350,1a2325cd,46b5decf,5f4e14e7,17ef0c30,1241f8e7) +,S(d4a1c33e,816419c1,884c2838,abc7148,c61f05d3,71fe3de8,9f06b580,cc5e1006,28d8f049,a1daec88,5d186da2,66e2b8a2,893d90c8,5e720d03,a53cf8bb,f8a10208) +,S(1b1c6ab,74436126,203ffd8d,8e9c58b3,e18f2767,3a72bfb4,ae4c9e25,ce17c570,ad28aaa5,eaf7128,fc8788e5,b9c1c13b,a4cf16e9,f651da6b,6c43fd1c,cf2903dd) +,S(3d791154,b2c062fb,e1f6ad1,dcc17bd6,b648aadf,cd573cb7,d29f9e47,e089e1c3,a578f2d,a568b0b,e95e1189,7bad8311,3098787d,62756c99,7dc3d795,8e0d3745) +,S(6daac2f7,ee7b01f7,277b3a5a,44785b4b,c6b6adf9,b112dd6a,227b2b46,9f27940a,fe9504e5,75c1e349,9267624f,2b2e603,55befd7e,d66d7fe6,80c521b5,e0d1aaab) +,S(ea6343d7,73a5966c,2ea66806,ed59079e,8b216a9f,4fcbe8f6,744808b3,2478c26f,15ab94e5,cc1f4a5a,33955f6,b30571e2,5104c615,c0aef2bb,6a8f6693,64639f6f) +,S(ffc78cf2,4b436203,dcc1b892,c77fb336,463a8070,332783c6,1824acb3,f5cc55c,7bfe0b6d,666b28c2,3259c4f9,2c0dd94b,fcf53921,91d617df,da1c651a,1fcb81a3) +,S(db38103d,5156c309,67189677,9317879a,f31dcfa6,b8d0f5d9,1f2bc691,6090e6b0,b5b3e45d,9bd41082,cd3115ff,824ff027,78f47a0b,d0ae71fb,32c48d72,cd540c84) +,S(4c08de87,abf89885,2791b455,126e7e54,d660903,f407b842,7b4fcd70,53e0b000,a9f83c6f,dcea99fb,fcb301dd,c31146c8,25c02978,6e5019e8,7b1b6db0,731b9189) +,S(53b6a23b,8f93200c,41bf45b2,bd0d20a5,e6fa5db9,a3686a7c,c669815b,993604c5,31bbf5d2,21023fc2,fef75f0a,f7bc33e,b4d38323,cbdaa511,c40ed497,ef42a850) +,S(b39ebc0e,53b74f35,6f573a40,1df6f8d6,fe1e4fed,f7d976e9,949da398,a45b464e,9d58ae16,a633ea0e,86bad1de,39f0b203,c1ef0b24,b5f1d824,48fc606c,a97fdff1) +,S(182e4920,f4a60741,a0b94fc3,e3ff0bef,39f1d459,cd6c18fb,16b072b8,de0bd416,6d048d0e,ffdf4e60,8097cde5,46955541,491721a3,d6e051ef,9279c5c0,22a32fb6) +,S(7a39e7e0,a2da209d,8cbb9797,5f12acce,840dddfc,5acf7c5f,fe778a11,829e1003,c24aa083,20e6a786,1c161e14,f37b1c7a,65117e55,2c252445,20b47b67,66fb410) +,S(62e6173f,2273d3ff,d9fb640b,87d5cb15,e5df841e,3462bc5f,6b94575d,a110914e,7c424010,4ba37e57,48404997,689a01cb,851343f1,ea8259fc,938483dd,d70ac5d4) +,S(3960d4cd,96de0126,5c2cc88,68e94d66,40e9ac8a,f2abfcd5,2108848c,ad62467,fb24aaf2,85a54405,f75db03a,464bbc29,3bcaaa44,344c763f,dd44656,fa7faeed) +,S(463437ab,42694ce9,98ac17cb,fd4d9034,ab7a1be0,35f911e2,864441a3,eff8e3e3,370056b1,24b894d6,3631087b,194dc5ca,fcd2475c,a95402b8,418f1954,d47599e8) +,S(65774018,fd428788,e4234b36,42c8a354,770ddafa,c38040e9,49f194c0,ff7ad3a4,462ec7,cb17abb6,5200a88a,9874c7f9,40469a51,c48c1fdd,6c0938ad,67741467) +,S(738feb40,e7f16fc3,19ed6649,2507406e,6501308e,5112dff4,db9d12d3,bc0f138a,4923678b,84470212,cc5472de,2163ac3c,51ed5419,dc1b1b1e,78dc0946,f730b940) +,S(546841d9,7985d80b,a0eb3448,3dc5d17c,441b7eb5,d24b6216,ab98b14a,d320cf36,719434a4,952ee43a,b1006ce6,f7785734,c6a5c018,383eed75,e3660f7,94eb6829) +,S(31143de9,1a4b96eb,be7efe31,56b6688,5d39ce66,fa8ce202,57626dc2,8557ece4,d8d8bd18,36e5c388,2273143e,721bef10,b0c3005a,5e2fd3a7,e471ae33,ebfae7be) +,S(ad8076d9,29feddae,a52c009,f3967726,a0a94301,dfc12d59,da63c47b,df6566fe,39eedd66,13fb7141,796c0353,9f560ac,e23f844f,d0d3d422,5b36e41c,b6707602) +,S(b01a4f7b,9362be2e,78f5cdbf,d9e35f21,4d296a3f,3e233d93,2cf33c71,95b3f613,245b535f,c58254a2,157caa87,38e855ac,fa9b071c,1b6182f3,5260da9d,18e385cb) +,S(50f3458f,a781d513,a944ebd9,6f6b791a,1b0cedec,1421f9a0,16a7f350,2d5f429a,4972c646,c1ad5958,18766bab,5bf54ea9,e9c4fe3,19810f1e,27f46cc9,41594045) +,S(d7b606ed,e8fc3a20,925e0a59,5c9462d9,4e04b5c2,b64d13cc,19a47d1,ccf10b0a,dc39a8dd,d5447a0,f07b5e7a,6ea35286,ec01679d,ffb6eaea,6222711a,d57cc63f) +,S(7629836b,73691a1f,f3ae6c99,7e06f597,34fb9625,2d1ec88f,2af15cac,f89c199d,296e4034,8eacfd71,7f64beb5,76736398,62114296,5fc818a3,ea89ba52,1b1a410e) +,S(88f6a162,7baea31,3a2a5d5d,aeda2f4b,4becca6b,812a60d,4b5b4e22,6c822a1,d3acfe53,d6189ab8,31106ca8,2388fc28,5b567e5a,5172050,5be56bf0,187534eb) +,S(8e93c72f,cfeae58d,fecd373e,dd3f31ab,3e4fa28c,f53681dd,ebf37d40,c376283a,e9c82571,2d0cfc0d,e6f35e77,acf0597a,7e2437e2,80f13420,cbe93acc,52901d71) +,S(b2264df0,eaead63,111d7b05,8767cc7f,36d386c7,edf21792,e8aef135,9958b2bd,242eccda,5e4324a5,40e8d098,cad23ecb,88955bb1,126314fc,5da973ee,b1d83313) +,S(9f1c9e90,7a6813a8,da520f6,84553a4c,aab650a6,7eaf4f0e,f2ec212b,95a885ce,416dae6b,f47aeb47,453fd0f4,7da6375d,136b6ee2,450bfe75,504f3619,84a0f355) +,S(dcc7585,132b95e6,a4a24450,d557a0fc,3ec93506,fd1f5f02,17bd0f7a,6943fa1a,c62ca95e,d976d1e7,2bcad48d,9bc3a316,a35e8496,952a7a07,760ecbc,8903b001) +,S(e3c674b1,eeb380f3,f08b1d64,32f4dddd,77cb602,915ac478,4f3c769b,dc2a30fd,fa8b08f9,8bb4a1b7,568c9d3e,754292b,1a35cede,1b377c99,fcee16ff,a16bbe5e) +,S(1152da70,2b7341ff,69e8d3e,6f5ce8d3,bdcd3a03,27df54bb,846ac8e9,18e747e4,7c139d20,1e2ea5db,984cc8d2,4b66c64,8adbd0c9,48c0b085,d59c73ce,b4d7a91c) +,S(3449cac4,abbd3ae5,e09dbc5,c89258a3,df93ead9,1f62fad3,a5add3f9,87a2f83b,197ecb64,5dc998fe,eb9b0df8,6267c7c3,f7af9647,96fea60d,88d4de89,33171e2a) +,S(ad5cc468,11c5a39d,ffa313f6,578acb00,cdda9ce,b3757479,9f2afd6,a8df7a19,4fdf6093,c8fa8a92,7702982e,7e638948,b982d84f,ae6658bc,fa839354,6d6e76f4) +,S(acad447c,b57e8f5,f528a113,347e5c5a,2097766a,b4664c70,3f496635,759a7799,88e1b4ff,3bf3e967,d2c6c948,4bb727a1,d13231cd,acc45463,d421431a,b6fc46ad) +,S(b74286a7,e2699aee,9802563a,4da514cc,d74fd001,48534822,9214395b,36158246,8df2b491,ea018b23,b1c7fb54,b7a05db,b79b0ebe,1117e2d9,ce73e70d,d097bf90) +,S(86bc560b,19d583b3,62c1634a,139bbd16,db30e823,75913cd6,fb688e4,38ceca4d,2126168,75c3adc6,c2aa16ee,2e028052,9e6612b,fe4b1aee,e4961feb,f530423c) +,S(dd79faa2,e97dd468,5f3ff8b8,d5e55598,73736de3,9493a3f4,4e679516,f51e495a,2cb89020,efaae785,5376d901,4d1bc374,21e0ee60,29ac70c4,65bdcdd1,e240e57d) +,S(caf6318f,825ca3d8,6d45c681,60d897d1,76875e26,bcd36d00,82b4e1ea,e6d8c142,d7abc304,55a5fab2,68c6d17c,3763e586,5b9e266b,bc6211b9,48b5ff3d,19f5df72) +,S(c49dbf15,70c3f0ce,1afd166f,884af550,1f26905d,2f726185,7c026405,efe9a2bd,54eaad70,8bdb7b2a,59f5d69b,5be6ed79,5fbc4137,945be17c,b4736649,6e8cd3c0) +,S(a87447c2,83c88db2,15630cb7,6029132f,47dadd57,5ea6f9bc,10d1b36d,8317a91a,8489d3c3,6334d65,fdf6089b,df19c3e8,7eecd697,516e1756,5fc8b977,be82c363) +,S(7467b357,7d21b028,dab4100b,ebfa734d,36345024,adc60b1,10b8f96b,c7860889,b2978a5d,dea9765f,7877d8ff,fba66c0d,f7c8e0f4,6854deab,21908da2,8572b1a2) +,S(6af133d9,bf2d6b57,ef69e0e7,5eef6d,eeb82d31,af490320,bd26f4eb,38ffd42d,7f4a4d1e,ce23c530,a4d64c23,cf0bf8,a46c59aa,74dce4ac,2854c6bc,d11b056f) +,S(faff2662,5a41c80f,c0889d4,83eaf50c,596aa571,6807b118,1e94ce5b,581b0a7d,32f3d057,4afc487b,43b129af,f83bc7dc,d495402b,90fdc30f,736e87a5,469e8aa7) +,S(8c6983d2,8047ab24,ddb292cf,1c8836af,7e75d7ad,6ce23149,e022cb64,2439ba2f,70a10314,447eaa73,924fdf58,fc607dc1,c69a69aa,29b84dcb,156fec4e,568a5bb9) +,S(bfdda2aa,b23a37b2,bc129bb4,280a2c45,926360c3,2719872c,ae1e81b3,1b3d3d02,e8319769,b7d21199,9e5de1ae,76021c9c,2bf75ed0,4c9a6c2a,c52da4f4,88cf78a5) +,S(80e24bf,3559d05e,c0cf63af,21dd7adf,237c23e7,ed930c7b,4124ad4,46871de8,c8f37f91,4e78e037,602ae9bf,ea3511fd,2c95ad3,6665117d,64f9fbbd,ecd10063) +,S(7f56d940,2d6fb0ed,dc6ce187,2ee8e55f,45db30f9,8defa84d,f7fb76ca,aef5e800,f43b9e6c,b60ce85f,44682fae,d8a48e5,3d9599d4,f19b4757,74f7d6fe,2fb2554f) +,S(437e7a08,900216be,72f14ef7,33127d49,9130309a,73369d2e,58fd4187,a0d428bd,3f0a4c4c,63dc779d,69dea37f,786a5157,43b3560c,16d4e56,4a1d7802,f40dec83) +,S(3bb8db7e,8cb3534e,d7f70f8d,eeb1cb81,3ab21dd0,552c311f,f98f38e3,63321626,9ffd98a4,b3c271e7,83ec8c72,98ba83b3,f4f0a5bb,61ce1edd,5c1095ee,eb16a569) +,S(21de8ce4,6aaebc6b,4ac51620,e2c78904,f9300b6,2930fa79,699e2782,5dc2310f,12a21855,50ceab27,aeb2e7cf,14710b7c,b3dc837a,67a1e4d2,6950e2b8,1942b9c1) +,S(6c24ddca,e4245972,5268369c,545bc910,ffddd8c3,49d83fec,eb27af78,cfc0c965,6263abc6,ed17a124,c8b2c5e5,c91b157a,13512939,b9d60e48,929fe6d8,4d032338) +,S(ddc0dfb0,49f7b6b1,cd1d03c6,f8ed2470,ae320f04,bc6f13f5,a88ba89,7bd1f91b,57c5f0df,8fd22e02,da44cd05,74fbcc1,c8b4a903,a909f906,aef9208a,89eeddd0) +,S(6fc5fdee,78c61421,a3ec2284,583a56d8,f18e66eb,58c54dde,e5209a1f,3e3761eb,baeac905,6101b825,8b08a807,72c977bf,60956b2e,d98d1b25,e71b2b7e,10e3d25e) +,S(1b6735d6,ac2f1db0,1c3781d0,cf42e2cc,3ccbf030,2e3837f1,3f6655eb,ba0dfddc,621e8c0e,cedfd28e,608c3da5,135d63fb,93dc6919,2a6154db,bf63e315,2f7db68e) +,S(1a875a0c,ecae6600,4bbdab3c,4fa5a57f,925079dd,671bd9aa,e8ac5c51,b21a5e83,2317a0c0,81c17bed,a15e1148,f44beb72,d4e65fa4,96408778,8216f85b,3924d9fa) +,S(51635c0c,ce51684d,9631c8ad,f6c9c4a9,3f66dc04,a372c891,294c42ae,2be5a84a,5c6cf3f1,bbc9c21b,124e78cf,ba05b2f9,ffb9c902,3895d584,48622259,635d3517) +,S(c548df8c,b685f457,71c564ed,a3e9a77e,a4734faf,7cef877c,b0bd70ff,d85afe0d,2675d6b8,24f99a19,8825f1ee,19510492,9eb633d0,367c01f7,c922bfb,c2cd6dc2) +,S(121592ee,aa39a310,3c02502d,72e360f,f500853a,7d2e449b,16c5e443,f42e700c,b5cfa2c6,689d973d,e0578a39,bc687720,acc0305d,6ad3b678,1d36941b,7f0cb435) +,S(c2a8b66d,cce99233,b3d83699,b20f0c60,21a83ee1,46a491e6,d7e69ebc,92a3e046,e0f3aff2,64228803,d8c1d729,88780d2,ae8c344d,f9571b86,70254b39,b4243705) +,S(b2b703c2,56ab219e,528886a4,4d6b7772,5cfc1b5b,17865ecc,c46e2d2,2420514d,b6aff027,b4408177,8c6cfaa0,123590a7,4615394c,aa83891d,15013d35,ba28a9cf) +,S(d249c36d,fbf43f5d,c6ddb196,d78d4cbe,898d64cb,3b239f89,1e8c4245,ab647afb,4e58c9e9,4199e0b7,8a0179c2,c82b6442,17eb3fe7,80c687e5,166f56ec,f812d092) +,S(ac3519a1,92c13eb,b37cd560,1a141d80,817226ec,7c86a053,42655c1,997b476,3522765a,afc2fe6b,2af9b489,27809189,74c2bc57,58ce502,13777ce1,a569083a) +,S(e9b69596,8983ee86,340b96ce,efd9a289,e86e6d49,3f057962,77b99067,af83c70f,983a83a,e1b08fee,392e51e1,c12a8e12,731d592a,517e240e,ea508f7d,b34059d5) +,S(e11e982f,10e65120,ca357106,f273c5b2,9900454f,81456781,36f52871,65444dd6,e961b155,81ee9ec5,1e6a3616,b7182da1,bd2e5db4,d08dad5b,46bee558,44c21850) +,S(f63f4434,bb0c17dd,3a17c75d,2d179e41,10a7810a,847bd4eb,37827997,94e57b14,480bff24,69c2e268,a51b2147,3d6cf7ca,23a3c61b,513267f7,c25b092,7dcc8549) +,S(cd8ea46,d65bb2da,7234508e,fb48a828,27678525,d102fa85,6d26475,eadc1a69,ba231ab9,513f90be,9cc8260a,fc63b05a,5aaa0280,3502532c,916d99c7,20b7b2d0) +,S(af9a2285,ec491b56,9cf53c8,6ec188e0,5bf18d1c,d7e898c3,c881a5de,481bbe13,a0cca7f3,714f8d00,92af7bc9,62abbe5,4d106dd0,a6089607,35a6d9b4,73d9ed58) +,S(5d90a039,55f41998,62e5cef1,af05ddd9,114632eb,abf27d6b,9b92c6c8,cfed8640,a0025e54,482aa49,11d8a556,2205c2ae,2a0f75c4,6f3faa55,7463b9b9,cce1281d) +,S(aa1f071c,55541753,3a47b490,c5603aed,2d2e5282,71acd0de,6ea7c5bc,ba66a348,195d8b00,fd90b1ad,15e46620,22d78bb2,47c1e9a7,8ab3b018,f341a005,6267a167) +,S(4225728f,ee2495d6,73f64d5a,cee2e372,2713a3b4,dbd2f098,d20415c9,3ab513e6,76c17939,461504e6,e1420d53,c5b07449,ce7f976a,e4de178e,69552755,75e92412) +,S(669c2815,b3357084,bf7bab89,63753b0b,ebbea33e,5b731ba7,5f969890,3a281eb6,23fcf486,e2839e2d,91fb08a9,11ed7361,81de7406,54c13306,575c576c,d0459c28) +,S(7b29749b,393869b0,d7f0977b,340b9e07,76a20ceb,ff6d8f8e,f4650348,11773665,a0d68cd,b6033ab6,a4c92113,c8f07110,aa5cf600,2335d2d5,2ca9b956,2874832b) +,S(6f096ed8,56f38c26,25200774,77ef7ade,19fb616e,30ed560a,ee5b4987,5f43a7db,54453b3d,1ffce3dc,b5e72ec9,3f15b57c,6725b271,d3fe8b7d,8f30ed00,35845b6d) +,S(1354de01,ef040d5c,681318c5,a6dfe7af,5c8eba0e,ab581674,acd5335c,e33c4bec,4002513f,ed215b53,c65686ad,8366cd7e,38711fd9,98d27750,bfce7e24,dda317b) +,S(5f8afb0b,3800c535,47cdf93d,61ae5c67,233c2525,17f99c38,8e97e7a9,1a90b561,7dad53b7,a64b7068,967fc0dc,e7178a43,a06e56f7,7aab1a57,7910d69c,23540671) +,S(901d50d9,b88abad7,84e75135,85a81d9f,a1d167f2,c9f377b8,e2598cc7,b249f3d8,7242b591,3c80db3e,748c63ae,47ee6229,2c1bc29b,e9f9088d,fb882259,405c3dd0) +,S(2f5e27e7,4c7e00df,c0bf6401,7d1a888d,1bb7a3e1,3ff66874,f7c306e3,806a48d3,87c3836c,7fb767f,4b5ac585,62c48a18,21dede2a,7d63fe40,da75d967,cf9191d0) +,S(8b2277a1,70464589,1d8b54cc,43bd5fc3,54fa6f1b,e7a062e6,6349ac2c,40eef8ba,ac3a007d,6b3278de,1c043476,13b254b7,69c9e14a,c2f50f9d,5f025046,2e06081f) +,S(4a6273a4,e9f28e9a,82c3532b,e7d3e564,66e87273,ad1fcfad,7ecace6c,54890de4,6d8b0fb2,1cb99844,610a325c,9592cc1d,e7c8b32,d70e19b7,ab7df472,8fd1aa51) +,S(f07330e,2ab0150e,435fe3a6,8c703091,4e7fb52c,307d58d5,d5120bfb,891f84d5,25b6ebc5,4c1d9206,33891a8e,39aadebd,988bd98b,a29b04bf,6ce4c283,a4438b08) +,S(3a30b22d,6f1fd617,9fff2c21,d6635828,8c21fc8a,94a274b8,993ecb91,eaab54eb,94035c56,6ea179ac,d58eb0f9,d12cb41d,67b94fbe,65805110,61cd9806,890946b) +,S(8c4f39cb,87d7f65e,591fa10,fd46d08f,fc5de3e1,67d8c9e7,718bff95,b112a5a,726c4bb,c64373f1,9399db5,18b5f823,3faef361,3e363b84,ad76052e,bdc8f2e6) +,S(38118e59,63a1929e,74f6d98b,c80e44e4,d9b32254,6a0f1eb0,690d08f4,f3bd4bfb,23167e00,c29513e8,e82d1140,bef5bf02,b806a652,fee592fb,1dad0811,604a4b50) +,S(75d4246b,db065274,550e9784,f3c9437f,3aec0fde,3af54a0d,61430cdf,2626bf5c,8392f570,3896cc62,d66f8485,9f7f269,4ff40a81,9505447b,7d79bb98,77f8f621) +,S(ab3bb0ae,c1f390fc,8d1bd3fb,4ecdad66,3db868c7,c4fbe0ba,6cf4f059,7068ed87,91382a2,2dcfe0c1,b51148ce,bad2140,24cf099c,e3e5e3ee,867db7ee,d070c7e4) +,S(d1453e12,383386e1,1976cae0,8a448b63,1a977dd8,47c19d2f,d7a212ad,5090011c,7044d595,fa8f68c2,4a972658,590d7ab6,dfc06ace,b9d8c320,205b858e,ba0402c5) +,S(894afc53,b0d1bb6a,c948d117,7e83c42,b4ff4084,e593fd75,d0660680,2ec0a4eb,5c5eb493,73e74fc7,39ca4fba,1940ebe4,6912d896,7cfc036c,7402e8ff,8d071d7c) +,S(f1dbde60,43133dc6,8699e89a,3dcaddc1,fbcb5ea5,e815eb5,8e7a6b87,dea2e6e9,96a7b1a5,b36e5492,68168c91,775d6107,a3f87f9,fb5c8b3b,acb75dad,5dc544fa) +,S(ad9126ba,6d35d780,dd2841cb,2ba817d3,f0267446,ee99abd0,ee2b4910,a4450b8,f77eaa1e,66c0f3f6,88268d92,9c9ee53d,6dbbc20b,b74e864f,d7831cc9,3d47280b) +,S(70d5638f,d3fc43c7,6f321157,f9715994,3b797d8b,1d366f5a,a5a7f1e4,ff1ff7a6,ba6f2d41,7af1c5af,dac8c83d,dcc289e9,4d6f29b4,2631ab7b,19414bdf,4a9ece36) +,S(e43bb23e,1f82bcd2,708730b9,3d3ed915,e3b4ad84,61904f68,bd91a065,80a4ebad,3b528db,117cfb49,99941b0b,63f51ef3,926a6230,dadbfcdc,28fd1d6,2bd44147) +,S(9daf3516,a015bd59,b648fd9d,9143e76,46b2e3fa,df5e446a,c57f2e22,9c9dad2b,5ec57ad4,808a9f2,ed297f7d,a7794e2e,3f1bb14c,bef58047,e0bddfb1,8ff11149) +,S(13970b21,3115c499,5eac245b,33633c53,1f2be101,a541f129,7403a727,67df16f8,3444e05a,7e49ecfd,633c96dc,99a0fc81,cc3c359c,440ffcd4,5df61c5d,1c027e97) +,S(12aef793,66978a2d,3fab4377,7debd5c7,4315b002,829b49df,c80fbb4e,1eaa94c1,e3f5c1d6,ea42b741,8bb3446d,c23679f8,e7a96d02,a8f33d1d,3404126f,71f9e37b) +,S(62a2ef7b,887de5a6,19c753f5,e0bc45c1,7c47b8b1,ffbb0ce4,8b93caf1,d1ab4b02,7d00482c,20c25e01,ad6ae35e,89c263a3,5d0fc816,952a96b0,9f2c2fa4,b8fc9bef) +,S(e6b03f17,92df5b1e,ec324ea0,898c031b,d745221e,791d2406,2aa917fc,de6f8a87,b4ee4b18,41153d22,1ec0ffe0,ec05b1c3,408d71c,5d2ca29e,a01ef9c2,e669b205) +,S(dbc02e03,e03e2f14,53ff6410,afded1f4,f0d51461,5ee00f75,d041ee33,c4f35783,6a3a332d,99ffedd3,d442b44b,aaa9d12,9d93e372,36dad034,9c47bd83,7c7674c4) +,S(f11be23d,6884890f,78ccbcaa,c1759ceb,5e7f98dc,19073aba,b85d85be,5fd23ef0,9afda170,dc1270d2,e43989a,4ffac7d5,c848ee6a,51e03b58,c308a4fa,39e2cfe9) +,S(16ec4f95,1db5c83a,c2c26209,f2577650,e9c48c44,224b221c,c09b0b19,1c1c5552,e3959034,d259e9f5,163a44da,f87ffd63,7a35b220,eea5ec6c,301cba9,eb09dc54) +,S(3cf701ab,8991e981,e9cbfe30,7b3aced5,c2147c47,f76bc2a0,733ee544,3fef3f99,1e6a3069,d4d0bb8c,def98d49,cd303b51,c45617ed,833be051,ca2836bc,13b8e1ac) +,S(96d093fd,1a8efbe,4f93c544,d0d7e397,cc83e114,4574f804,570fa3ec,58d5669c,2a29438,a05fe50d,b980db60,b343b06d,1796ddff,7e7fe39a,356cb608,69c05ccb) +,S(a6457eee,21025732,5094d3eb,6f6ea05,21e83184,adeabfa7,b6cd1206,9a9dc9ee,83e436a8,b234778d,9c7be0db,88639177,1d7a48df,19c29c47,682fa2cc,30b5e7f2) +,S(1b366505,1c858ac3,5890e7dd,b4aa7984,dbacbbe1,f4d2b62d,5337b530,da9bb0a5,39d3c2d,42c5d292,40b474e7,2e94a64b,4612f5a,68f5b6bc,586db5fc,a41025d6) +,S(e09325f4,68328cc4,d69a71d4,8ffe0380,bc118cb3,b8f390d3,4c1e56fd,27f4589a,16cbf183,78946fb8,3ff9763,41c6cc3c,9b2f451d,7661bc3f,3811f7e6,ea89a01d) +,S(6a8ea7a9,ddfa0f9a,8473c47e,f5667e44,3e64e03c,5c33a9a1,f0f1257b,40e1ce62,92806471,56238c84,9c0aadaa,2e9efdcd,e5a56a2a,78803f7b,66210333,88f525db) +,S(480b30ca,dc57c623,9ede2763,f113b46e,599076ae,ec687c49,33343178,c620f2ea,b5aa1b8a,ffe1e5ed,de587df3,341dd34b,57180cd4,752a1bfd,d06e4c2c,273b4433) +,S(134be6f0,b03ea43e,98e2364,46a45253,ea5822df,46227aec,b24b09d5,b25a34b2,d4841d54,b7fc1736,b293d314,bf6352f2,c7b08ece,4de652d4,2c1b985,d1fd78a2) +,S(7c84e23f,c54de6a1,a4ff6c9a,c7c88ac6,732d019f,a53b1a9a,a6eb2ab8,a3101f31,4ca88bac,a8d52f44,3703fa27,4fddde3d,ae5924bd,26b062fc,6f7fd0d,43ddf5ad) +,S(7c6a349,911f02ef,2aebdc3b,ce77f639,94c4dd2a,a842b567,141c023f,4f2d92d5,9b4f2f9,6215aad9,c7e0edf6,efcd8aa8,74dbbcd5,4a0c4cd0,fb7e01e8,ba6884af) +,S(9e1dffc6,a919c9b8,4e3d818a,5ec60f7d,5a2baba7,7d2dd65e,137d2ba2,8a1aed3c,cf26f543,bb7ef705,e2d3c429,f0ea27,84e3daae,1a71c556,38d281fd,aa3b1738) +,S(e24f6abe,c7519588,33ecc765,7446ae36,937e310b,d5754c5c,343eecf3,6e140079,c38a0399,774ff24d,90f8e599,183bf011,aa3c911b,9a66bbe6,920522a6,7510e62d) +,S(517505bd,1fe28c21,2fa85f18,99823b01,bcc3bd1,748169ea,54f35ab7,870f0e1,da94c34a,5de41881,1184fe92,8b3f08fc,362aadf7,5e7abf87,5d5f4798,fa712f3f) +,S(a02217de,4ec87155,b877b7c9,dd7425b9,79627f0d,ebe127ce,27d4f3ef,fb988a8b,cdbd866d,7b69e8eb,9321d07b,2a5c9d8f,6eec8be2,f19a6d24,4c987a1a,12752bd5) +,S(2e595023,c8ba084c,1cbb2d05,96ec35b2,7c366bf,cde27666,35d7e820,8fcb0ae9,ac08f27e,b8fd5ac4,deb1f46b,23ac1c5c,aa02b918,9761e6ba,d7c1ebb0,f412d8cf) +,S(ce384358,fba557d3,b76df62e,d2264cf1,e0bc7b43,bab30be9,fd6f65e1,a82b6c2f,a3cbf24a,e067ad85,ae0fc86a,6e8490b6,1e44980b,52f29a75,4a3f7e4b,a79ad119) +,S(9715da8,79cb1c5c,4291e00,fd2822c1,7f067d63,664170fe,463e86c2,d25e46,c5179ad3,a4846fb8,d6f290b9,2f957e14,cee99f6e,12fc8066,ec4dfaaf,a82edb99) +,S(19f61bb0,4cedc19e,5054eeff,8d0bc384,6cf62a35,1af3ab95,e03e45d0,786e1aef,aaacadc8,db84d7c3,f869931b,bd33d295,45fb37cb,7e474fa1,7a69a683,117c05b8) +,S(2a69afa7,581777ad,38882f6c,659ff4da,33fcdc17,a5355583,d1f8ae37,7dcbd718,878eb429,d42dfbd6,435df1fa,c5e0ed4d,e7b2fe4b,6f7dd6f2,6a3150a2,213fa619) +,S(c3da1980,cdf257db,f2a5eda9,94267691,a5e1e679,79d4908c,c06f7f50,71002381,2cdf4de1,90ef2995,a984d882,598c24cc,35cfc47e,5cbd2d53,53d880cf,a944bfb6) +,S(19294eba,9d19c03b,497551cb,7cc8d8ad,3e3b2130,942ca8dd,c8bc5579,1c424d2d,5523ac45,e676daaf,2d8da00f,5e9240e3,d904a536,63e6b704,735889c8,84b680ba) +,S(d8b3b307,41ee7ff8,2306615f,2d2ef361,6e3f7cd7,fed9d56e,69300fb8,5eab6bd3,7d57c12d,e65bf3c4,6dde736c,373ea374,d090b240,6e1d97fc,8049e21e,aa10a74c) +,S(e2fa1bd1,164528f0,87c72a18,e9843e33,43b376d9,b5616b74,5e0c3d00,eca95fd1,72dcfc41,15b35406,8bd3057d,506d592,771eb3fd,60a2c2c3,dfc2645f,86b249e5) +,S(ac587775,bee32b7e,90031ad8,9786df14,8e4d9662,55a06399,e43c0441,883fd6b4,b6d3eaa,6f6a50ef,ef84c578,b241591e,4a718eef,fb40ac34,99a57079,b74c45b5) +,S(74976cb0,77d481f5,e6bf700,6db57a13,f362fabb,e6aaf25b,6e956452,9d093bbc,bb40be31,7cfde25c,75ea859,2b622e4,9d42a0a7,30602241,219bcc84,a5f4bfe1) +,S(e71e6967,f32647fc,2c13a94d,4bc1410a,cf9728d,1f543522,681d750b,2620a668,33c4fc82,720210d9,afb9f1fd,51671f3f,fc1df5f4,e61a5ff8,c59f06c7,91f996de) +,S(bc36cf57,161e9b17,828e4557,74204c59,805b35e3,abdf4ba4,daa9a5c3,fafad6d2,df34c492,3018e879,bf459cb4,528d2983,340444f5,a85efc29,71e260df,29cd8a20) +,S(925251b3,9c8761c6,317c2066,717a2e8d,89e43acf,89527ddd,6078fa49,2d040f95,aa7a084,8b2633ac,ce551f1c,63312895,f4ece420,5e224665,2c486beb,b8131f10) +,S(4ec27df3,d9e755ce,b93e1ca0,e0e43f44,595b529f,6460e3f4,e6cc3765,3a2bbb21,eee3fa06,257b4be9,387ac7b6,6cc1ff6a,aae43583,37d6ed71,27ff753f,1fb179ac) +,S(6f733e9c,b86c3a17,5bce6c08,347577eb,69cceb17,3c19abef,c94b6646,92859812,2a895d6f,c4a7a4f5,610e8b8,9e7361bb,db728625,ee31e6de,927c74f5,b10ad0fe) +,S(c631f4ec,f205b1b4,6bc0fce5,3680e6a7,27f9e64d,5ee3fe24,c37291a0,d3a69b53,f0de9e0,e4bea2a9,cc01a5d,2f9aad2,f5af7ab2,7c2ff98b,983dd2b6,38dced4c) +,S(9326d449,4fcb3049,8badc221,6d3a90c0,131d31ea,cbca74f7,2cf7da2c,bb6aa2b8,c3f268ba,4c9a9ab5,3f9edb88,74f2786c,4e4c065e,ab58487e,cd8101b,7132511a) +,S(538c52d4,8fa6c492,cf546349,675d6a2c,ba10b442,8827dfeb,69f0303b,1b7ac774,c1b1db4f,d6d50f3e,c7533ee8,cea0d47,883b57e6,dd4137c8,2132f846,3b81ebf9) +,S(5e58a25d,8bdb4576,d178fdb7,23158d26,508f8486,ad553727,907901d5,6e99a680,97c1f814,8880cd85,51c9f1e4,9ae58eb3,d1a01285,bfa8248,44bc794b,7d83ec62) +,S(1e9830ee,b75d6c6d,c70989d4,ccb85e62,34a1cf94,ef81b65a,49b25753,cb6c13c4,d0780a7f,9607e530,d80dc068,68b1cf8b,82d3047a,2977d204,65187ed3,13c94c6c) +,S(c5b441c2,f7e97a03,dd690cbc,ac605772,1524dce7,fb5068f3,f73d78ce,50fe0183,9bdd2242,e35008dd,4f907600,64eb3b29,427ee9b2,bbeeb548,62878688,a916e410) +,S(a5f56816,e9448d0a,aaa7593e,e3145678,3be74521,b129f13,f11a276e,d419492,8406662,65c275da,76ef289e,d36f8af6,6ead9be1,48cec081,7c21987e,9e8af9fc) +,S(c5e85955,44004f79,2260d553,25628858,ce50c95b,9362d868,90fdfd20,8d1184c4,6a5f02f1,c0f381f2,7fd21eb,928f03a,c8524dc5,9bfc1bca,d025a290,7cf939d1) +,S(d5dd810,9e691f5,8694c87b,59907ed5,ec18d37b,8554799c,1481ac78,825d2ac9,ffbaef54,fd35a5ab,a49f9946,a11e5ad9,2f29590c,20e28180,9f4be3e4,b156dbe0) +,S(ee5d42a2,c705bae5,a11d9d09,af3217d,2ec16588,64a60a56,d8e4a1cd,4313c61a,f08f0e08,89c015f5,3a2a74c3,3dd625da,b329181d,1d0f4fa0,14ec01e9,5d113d88) +,S(90409eee,78af819e,249f62ea,3b4d70a9,26557f8,731e50ed,532c0b30,4616d615,17aa7a7f,8af635c9,c4ea353c,56c89da9,70df599a,faec8916,ec9a6d82,cdf957f2) +,S(5f19aa1b,a6d6e3d6,9445e8cd,71c95e3f,a3ddad6c,ff0dcb4b,9e09aee5,65ba9e46,f82b3170,c2953397,a6cd29fc,9c87bcd9,76cbc7a3,5f953948,5a57a940,63c2e570) +,S(5081942d,1039bfcd,96a759c9,27583c45,8dd4c013,59fea307,40c65fad,d020606b,f6f204b0,97cecd15,b2619aef,c25328fb,226598b,fc9384bd,de99a4d6,567e9b8e) +,S(43eea7b1,acbb1450,c9a49b0,262639f,702612b1,b184ac15,675d6496,a5df1a54,a31636f9,ceecfbd6,60f78f,f6cee471,d4366d7f,ff863db4,9a5a652c,71b94797) +,S(f2860e98,a30a6c14,ac43dc9c,61bbae1f,b8ecb294,36c8f0a8,7dd43c01,486706a1,bc83bfae,9a4effeb,4beb025e,962108e5,4a5d7979,cb9ae2a0,7cc44295,b95a2e9c) +,S(6b4a8c28,7d5c7af,63ad8d34,9751116b,60a613f5,7cac229d,7ef0d9c8,166d69c9,48333ada,f4810cf,94c78d43,88f8842d,fdc9fd7c,d391e456,a592d8b2,200bd6c6) +,S(d1efc808,393d4498,50837323,9b791d0d,a814e888,2958da8a,87136d93,bf1b7d74,13247e74,3c323631,9e557476,4ad4dcf2,b91dbc97,2d38244a,fabe4c0c,d08a9924) +,S(d3fd7a6a,fdc7332d,f2e6683b,a05ab787,e3c48262,ce837f9e,d8ebae1c,1f6f7ab2,3d25bfd3,3ddd4f74,8ff395a6,b4f36d19,cdb28ce6,b56aaa55,e783e9f6,3d74e70b) +,S(816f76c4,c5ea8733,5f220c54,28b31cc6,89fd8267,e498a675,4d6a2620,a60d9aac,d3b61647,f6624bb3,9fbb672f,51b66613,8a441a3b,660bb223,1f255488,806e79c4) +,S(1e344f97,98f11372,aef5c6c7,6b32a568,16198e18,8e611026,1dc78338,2522cc93,474e0218,9bb4f698,15e304d4,8166e3cd,717a1bc,a9cf4e4b,1f5c5d23,a36e392c) +,S(4d3bcef1,d6cff8a0,e0582d11,22eb7f67,28aae077,e9ef7960,ec519ab,3c6d6087,29f1b01f,a70e6812,3dade19e,d5d2689,6c491389,27a2da1d,3c628965,bccf38e2) +,S(6d5d596e,467ce203,297cdfe6,ce4807a6,1686a8c9,f0606283,5e0fa194,894d027b,ef58ab41,1d6a002c,12f469e2,e9f8ded4,4291b270,4b7490c5,4c70c94a,cb7b24ce) +,S(86f6ebc9,c25f6866,f14af018,88486b22,6c89b1fb,64bd9ac6,e523e503,e2cc6819,b0725647,4d9f3b4,8d4055d,e15b72ca,ce599bfc,247ce9a8,4b0d5e72,16b30425) +,S(520115e9,a47c3df7,23f81df,8422c03a,ea0084df,b122759a,8d6a5b75,540cb339,93b12878,44f22aec,9d5ed727,3d204d85,d3bddaad,73e3545e,5725b171,2b8417bb) +,S(19c9ff30,e5e50038,7e9be1f8,efddca98,7a72de0,d5cad8ce,7569d4e7,2b381009,b43b0c98,402748ee,16b362c8,b68ae79f,8493a7ef,293b149e,3faf7247,83ee3661) +,S(bdf0f8e0,928affd0,87566820,3c95b3b4,e6baf9f,f1da25c9,73814c27,551ee4cf,5f701bb6,8f8a6795,cb4ad675,e00b5ca5,761a0eff,32a1d3f0,9b29d5b6,3885163b) +,S(5c609591,37a48dfb,f778dc26,80de17ce,390ec33c,ea5cace2,a969ee9d,1e413e1a,670078d8,59a2c138,93946d1,e052552d,fd92c380,5fdf2ed7,8aad7795,2ddf2f51) +,S(98f22ef1,b9a62928,c5ddbdf,70e71c08,eb910ff1,fa6a6a1,56de82bf,bed8fae,f87be19a,9433d279,8088c882,5d122e31,bec9102f,c580fa2,24d40f32,2a2db1e8) +,S(d1842bec,2bfc5fc0,d5208d7b,40e04b76,88f26add,9b5a6845,751147e4,b4524615,f0405ace,6dc9bc88,48d18586,b713ca3d,67b76ad9,4f2f794c,55fbe9d2,f72ffa56) +,S(732cdad8,e0aaee77,473c0140,9702cb39,555ee048,357fa448,33ce8e3f,5f50bea5,24666502,448268a5,bc2c693a,85a1fea9,dc5f010b,a9b0cce7,6bf36124,bca1cb0c) +,S(6c86ebd9,5200b1ff,dbb6ce6d,e9fff6b7,1b6100e3,1144239d,77e7201c,db2e8af5,39c687d5,abff0ec7,c7ccc4b2,a4a16ba9,7324430e,a1ef209d,5ea126d2,cef24539) +,S(655d63c1,47441ab0,e65f9893,dd38f3ca,22d2d8a,a7b2c87d,a2e2293,b24ffe0f,d19ede94,b25ca138,94ab8fe9,842cae29,92cb58da,592c19eb,57c4184a,45121281) +,S(8efc14d2,d5845886,ec264438,449ff49d,ddab7567,69999bd3,6191cb39,efa41b72,9f1052fc,416db1a1,d9797e66,a816b403,475ac091,ded4dfdc,e262d527,3c535293) +,S(31a7f0b9,1686e695,d1fae6e3,8fcdbeb4,246fde30,f2ca80d5,2d9fde31,c728eef2,9661bf7d,46248da3,8ae1ff52,a8aba654,351a1f58,4f3bbfed,1875d439,3612190d) +,S(9dd959a9,8c862275,93dcc957,e369b2fe,5cc1d411,d4162ad7,d8e22b64,633adb85,78ccfeef,d95ede71,32a7011d,bd39b9dd,f77e38bc,47f7432c,f90d6583,5a6e665e) +,S(51beca99,fe3cedfd,1ba44caf,58c0fe4,8b926e72,50e5ca7f,8a187cb4,909b1892,12f38fea,aa9de148,7c14eff6,96cccf6b,c7cb23a8,6f2b7e0f,10fa971f,3545a0b2) +,S(1a84e21,3ef9b9a5,9f9a4fe9,d21dc1cd,1ec7a102,fd91557c,1129921b,781e6b51,aefbd931,17a42a75,13db0e04,782aa0b6,9cd79d36,b074f026,bfb7682,983cb78c) +,S(9eb14847,2d02a287,cd197450,78d81963,3943183d,c76c2090,9f68b94,12eeeaf0,6fe4b5a2,d18ac52f,9a129a91,1db00993,ca505380,2054b5c5,48f35380,c6d467d) +,S(9ecb3cc8,da5475cf,1a66ef54,3286f07,f5be1196,2c1fd420,b394bae6,c4256441,b4160952,e3701895,6f25c5d2,98ce90c7,91797571,267902a0,bba53d2c,2fe2cde0) +,S(541e567c,b2160fe2,75e9d479,1beb8ad0,4c07d6b5,12b43d49,888963f6,d6696ce,2444e184,66cc09d6,aec704c6,ea8ef670,4ed1488c,dd8107c0,c625b783,6534d6f4) +,S(8c061b6b,b6b55fc3,7c6d805c,808fb3e1,6fab1cb3,a05853bc,4933eb39,ca7b3580,20430304,c3780c29,a977f8d3,54424539,67924c2e,9a1353da,40716ef5,a0a0d37d) +,S(b9b674ab,361aacec,bbb69f7f,7c2b1361,44e55da6,1566ed97,cd58152e,4d391033,7841975a,4cf0b5b7,3bad8433,10866275,8a8eb93c,3b5db58a,15346083,f3d10386) +,S(3453c3d2,27e1dfc5,3e22487d,cce25ab6,18a3d25a,48f6fd1f,8e9158ec,dd6bfa1c,aeef1768,b754d998,fcbc75db,7a750396,930d169d,bf933c1c,15dfe041,17ff63a6) +,S(f36f7b9f,28463b22,d2c43e29,6b96846c,1b3401ec,53490c5c,35ec197d,da2c440c,9876476d,e2cfb7eb,b1cdef50,a39d20c,4ba5000f,9edef22e,ab7b96c6,f0cb5585) +,S(77c64190,33ef6119,ac3edefe,ed519632,ef5dccf3,f46066bf,6d5204e0,66788b5d,980cc59e,a103486a,9c8cfccd,213cf704,3e6aacb6,abbf3293,c833de88,e5b2716a) +,S(818911ee,6051c4d5,19fb7785,d52c6f27,a82e2d79,a4a55623,909f3d6d,b549e851,5d031307,a2d66fce,4b607967,f662bc0,af855112,11cc209d,8457ff41,968fa325) +,S(bf5900a7,7c211a75,231ebe58,17f91748,c957f5ae,b98377b1,9554fe90,d11ff226,d910b2e3,1760f0de,838cc159,7da6915d,e6c3fb29,80bc0e65,e87a36f7,749cf1f1) +,S(654b4257,11f03dbe,75b7d60d,b578d75,20e7cb6f,4923f774,8799b4a1,df06fa16,12c9e75e,26d3ab4d,645a7a64,d97eb6c9,356a3951,fb715bbe,9e42f500,a0c3f44c) +,S(33a425f4,f178af6d,48b78873,cf70da66,aabbaae0,397e6673,7a9c6074,15ab3d2f,1e5aaddf,d76b1b91,14016f02,1812d7ea,18decd14,ca3e3925,89894151,92178cb0) +,S(cb088930,f0ba3995,d7542d48,92ce47e5,5d8fdd5d,7adf60d9,3651240b,12fe7636,aad156ed,f0b25c30,e4bc3784,a1b7857a,4cabbe5d,2ceffb72,ca2cb7d2,45ddb024) +,S(ffa9e81,afb11dee,83a54b47,fe9b41e4,de4191af,7279d19e,90e1c528,7c205c0a,405233e,b20a4848,80210b40,18c098ff,a0a17203,c5a13b12,5ec0fe92,1d1c3567) +,S(92ce3be2,8fd6c9db,be1cd4d1,f58005b2,af0c136,89181c52,a67c2503,2649a6af,cdb840c7,7e0fc2c3,d4b959c6,6fc47142,622c8a16,b30168a6,c62ef368,8bd23932) +,S(46079065,b64bd3d3,3e3efc91,374e1443,69adc4a,bcd2bcfd,91ebed0d,88f78cff,b09653a1,1c0710bd,b737611d,e5d80e67,a516dcc6,2ee3f63f,e2d6a7,f3794ae4) +,S(5cf961ff,de982138,958471b4,b316cae3,6246ae74,e1309400,a3acd118,e4736c93,ab8e425c,ab39325b,b94925d7,795d8799,18efc449,e44c9f03,cf7bf487,fc32b185) +,S(d8fa1009,27c1aad9,6ba97005,f8a24013,bc29082b,ada0e0ae,f6f72ec9,8abc4240,38d88e5e,183b2c09,d53adeb5,8d0525f0,83a512c4,1dd64cfb,a7024602,f39e484c) +,S(cdabe8c6,7d0d4627,54cdd3d5,3e70bcba,e2ec925c,a46a4536,ce3be9ae,e0b37d8a,c2032dac,527cd15a,a1d812ce,1c658cc2,a0a379a5,2a30613c,2737812a,6e683830) +,S(394fbdd6,7069d83,8fdf87f7,b1c179d9,1a491ca3,680f0103,46de3299,37e92374,5ceac741,828ac862,7fcd587c,69844243,d17ba038,901cdb42,36e96685,d589a693) +,S(84eb6d44,103f56df,96617c7,11c79dc7,a6161189,157a0782,74fec78,fc5f6c37,57a16660,4d1fa3d9,cb358e7a,47984a00,6926df57,4e06a313,7b3c7524,2e882320) +,S(295dca03,657995e5,d432a606,da12bec9,fec8f4f0,582d75dc,5657c973,7cf1051d,c4df0ad7,b1e3a29d,cca4120f,478a2d63,f18fc67f,bd201be4,e2fe05fa,66ba2d6c) +,S(b42d81ca,1bdfb038,d7888194,deda7307,eb846bf1,738d8b7d,89663bf2,8097fa7e,2d913cc9,93ed5118,1082c85a,42495133,ace8bc6c,65530309,c6e15794,aceff5cb) +,S(48484186,4f6d8f3e,94977a3,45bc0db3,1330910d,4c555032,da8da876,912df644,7a78a8c7,68c9d9ef,b5242fcd,b7a5946c,5fbedaa3,2914339f,8b0c0ac0,eb8a3146) +,S(7376d600,9a7a76b9,dfb7e225,c0da3186,b683fb13,daed9fdd,25ac6606,7e7d2316,fc733036,dabd7c4a,719b699c,da115547,8a52d69f,387b8a94,28744144,c0502901) +,S(9be9c65f,5fd6cbcf,522eb9ca,91427f68,152caa2,a152051e,3919307c,9f67ab8f,14da6642,fa3b7c37,41c9f953,d1e01c09,267b7885,8414fe5f,24f47c4,b44c413e) +,S(7d0ae5e4,18d6c6df,45f6d26a,7e962335,4ea1a338,8f183eac,85b1eac9,1962e2f5,551eedf8,264763b0,913307a5,14fd9e09,479f43a8,87983e69,509a3688,d09308be) +,S(d91d965,d3b85441,acec7257,d444cba0,6243b34,14e9665,9cc62cbd,89aea7db,54e26ec,9c82bf54,f06cce16,2cfb924,2a354c83,fe530937,9bbe733,6010f44f) +,S(44c8fe95,aa48f8bb,6c0a32da,a6263918,fc2df7b8,6a68f277,6bf405cd,1bf0566,87fc1fa8,3fea76e5,52f53612,45e54053,9d70377,cd865915,b6b78acb,f73a6e03) +,S(c7f8e825,5f2ddcfd,f21c5c20,ea84a527,2c5f6c59,99843c96,3bce9e0a,aa7ef398,5c1cf086,9c1f790,b1e59ddd,ec9729dc,2b73c6a1,be333dd7,e423a0be,bee50243) +,S(3a8ebd85,a2bd4b1e,6c0373d0,63e9cc4e,a91e6e1a,f4bd7af7,501e0160,8fb7f4d9,4d9351b7,3c3f6f36,1b86ae94,ccecd211,ee3b13a9,522d799f,e78c87df,4473f23c) +,S(bb01a4cf,ad90f3e0,5355f0b6,d1b28420,786c09dd,9db0ed5b,38a56506,4da77e9e,1742b511,43bc276f,273afb9,8473ee3d,3d962050,7462cdf4,a8c641ef,cc2eccde) +,S(eee45594,1a04bdec,a02e7e85,1d86987e,798c27df,adcfd5d8,2979960a,3983f4b9,3cc75025,9b5d215,37570928,1abca8a9,29f5609d,56c8dca4,3d1b23d3,cba3fedc) +,S(d2290ec5,120d591d,2e4e2796,3d10c0eb,e73d8b9b,600587ca,84173ce6,796123ba,c4760011,4c68baf7,b4053666,d8bd494b,b4ef1786,c1d7a522,8a656f10,1c837304) +,S(21e359ca,9b7326bc,b3380282,b9017df0,3cdfb7f0,af95f326,701bd8bc,25dc047a,68b866e8,555206e9,7cc43461,34c3a61d,ce2a50ab,2b514935,a05f8e04,ad95c70) +,S(b4acba20,94b35033,5095b208,9126367f,3203efc3,35caa43a,51857181,f4987e42,fa0f72af,77db4913,af6d906e,54eb33f0,a3e9d20e,d366c878,3b7c513c,c5165c16) +,S(5ac6b2da,18754c30,9ae46ac8,3592d673,ac5d61e0,35ef1b4,512aa9b6,7551862a,e93b1d74,105974cd,b6bd3043,c0dedb8c,b4eaa81,c949999e,68d52fa,4a3941a8) +,S(4a53cad7,2a894cab,9b761157,38ae78b0,c36c293,c60ba8d4,b87300aa,792c51d,4b0dcfda,b890ffdd,cf8a6a71,15f2a351,d883ad8,fe3d15a9,118f3e04,43f840c8) +,S(5edec48f,7d8c9b4c,af3371c2,6e054392,755fa073,1e660971,4e089b04,2a07bc36,b18b8d1e,407f934f,e526fa5d,38b3a408,76bab92a,2bc192b5,b7c206ed,6ad040e0) +,S(986b120,618abe6a,d965f9d9,6f50fd7b,29f1b897,8276ff8c,562519ab,8aa92890,2c981569,23ff1809,1cb47459,bafc0d51,699740b,ca59dd7a,14e07b9,432f681) +,S(8c61517b,397d36a6,b30f0894,98d525ff,bbda959a,126e9be6,f67b9831,e5e7372d,f7608f24,130ce8cf,56a7b762,c1563519,a4be88d1,79e90f1d,b432ac05,d472d771) +,S(d6abe67b,e30fa40,b642483e,a6ae7ecb,4217a07f,35546f97,469aae99,a286b8f6,b5e5675c,91aa08d2,a5a77349,5f1ad2bf,8b6b9dbf,4b35dc6c,262ff1c9,8eaa70ed) +,S(26be12b4,ae013009,45c8ec05,58edb030,e19a75ad,872104f6,8642bf5b,c30f7934,1086891b,3ce97d8a,a568fe54,28164f25,4fc15953,c97d40c8,f32c0ec6,6ed49fcd) +,S(3f0281e9,1dfdf1de,69d5112b,1b86ea89,d737d09f,1cbbb71d,2400d0e7,719987fb,27451e9a,2cb336a2,998d55ca,ac12e16c,fffbbd5a,3703897d,5f566ab4,5f4aba92) +,S(b84b8115,7b1782f1,e4b641c2,a001daac,ac7d53f5,b3f1fa5a,d0a5ddc2,732af3b0,a0c8f7f4,8fa49815,7a6c590d,4ea18dc2,c586b332,2a303061,c19edf44,ebba25ae) +,S(790ab328,fd86da4c,66f0989,296229fb,609de28e,cc8cc6d3,d315eb89,fdf31e38,cdcf83d3,44d103c6,736bb5c6,375bc2e4,3fcc46f5,777d6e5d,cc407700,15d29aa0) +,S(d5fee68a,f9b7f182,20ea6525,6a591f24,da79a642,4ea0fe31,88a0b658,9006fb51,87b8608b,1b3d0d71,c9ab2c9b,84654408,af6551c7,1e8ec6d4,a8f26cb3,20577532) +,S(410f9801,4622b22d,62eef39a,3c5350c9,e80ecef8,48c4295e,2ae279ca,9623e777,53dbc737,5766d9f8,ac0b8b7c,e4b852b0,3214fe70,24386915,84856c83,6376821) +,S(6cbfa6f,fc7a6009,75edf0b,c260bd2d,258bec55,382e650d,c464b16b,e5f34eeb,de72cf4e,33028bb9,18ed3a41,d885faf7,e1d5901a,ad7a325f,801f1a36,2ed730c5) +,S(2a75c02b,3325ec0b,ffa42250,e224a4de,e0c4c12b,69b71039,4bc3040d,80e5c504,ed666a2d,8ac26964,f0445d21,f1719ada,fbaea128,9edd69bc,94ae6123,a2e6b8bd) +,S(ea2bbd57,6c0b20a5,6790ad87,29f25475,24ef97e3,86663aba,2473994e,fab39134,d98d55fc,daedbd5c,3aab619e,1288634d,f526f873,19743f81,149ba85d,535172cc) +,S(893e9899,fe1b8a92,439c1594,634ff44d,1d0a60cd,8a0183a,b810bb8e,f80d0400,c680717b,aa2ff029,7baa807,d7290190,37c73d0f,a5a1878c,9fa2a6dd,d17cbb2) +,S(d75b0a2d,1e9a20c5,398fc3b7,cacc79ba,fd53a14e,ebe23196,e4bdf4a8,f8546422,37a8efdd,618bcff3,3dd0c0c9,f8bf3192,2e131764,305b610c,88a2b74e,44672981) +,S(96c84a8d,5dd2ec9b,44a5b2d7,af3b65e4,9c07786,969452c2,5e8e64f5,a264c14e,292ed1a0,dafdf3f4,83a2a419,6e1b6b28,4d7daf88,88c7268d,70e556fc,ff55341e) +,S(458895f0,77782a62,43abb775,7daec5af,264a71e0,6eccaf8a,3b23baaf,fbd7343f,809eb056,87bb618c,a0f4e1e7,c8d05e48,95f18393,2e949b22,b9ddab0c,55d32b93) +,S(59dba4a8,d7956515,7f639316,75fecc7b,58a0e91a,6c9c3d50,7dae6d40,2e67a9df,718094a2,83e47265,1ef52d4e,db25b0b9,9e069f,391ddfb0,d9987596,d2f4ee2f) +,S(a5f92445,b7cd75c3,9a4b3199,96baf00b,f855ebf,ab3e19bb,71d4346c,878b4bb,9bb1bdb5,baa4d56,5b9c5077,c86b96ec,953ce381,17cc8d53,7b0f3957,a582ddd1) +,S(f146eff,2a9ee1ec,a3f26d6a,17c89639,a1f3505d,2776e340,ae89449b,99c8b198,1674daf7,a66f1a89,7b5378e4,add6a196,fd27795d,88969a07,a210d33d,646a64af) +,S(e67f5967,d98c1d3c,3a4d0872,12d3b65f,6f8df216,5dc14633,92c23ecf,ffc17e1a,c3dbbcab,d1971eb7,a8b088c8,c71d65a8,328bf43,58f0e685,9025db87,e5391380) +,S(32e8d775,ed37cc2e,6f69c85b,71c95d65,d40f033c,b5eef362,86d4499b,fb6071f,2fbb1d8d,a6364791,95ecaf52,6f63eb86,e4604973,edebbf82,b796d88b,ade44334) +,S(b98c7cac,475ee4a3,c1815a17,e460f76e,e5334b27,e1ea319f,be8924b5,819a473b,d7f1bb1b,a653f20d,c7145420,d8c8c339,d789ee60,9b9ff0ce,73910b94,3deeb88f) +,S(d9830fd5,545dcca0,ee37a402,9f406cd7,50bbaf59,e60a80fe,da976bbe,ff9027d0,cffab795,e45a2833,75e7b861,728421d5,f9e22d03,44f835a4,c55207bc,6479b947) +,S(399fbb5e,4f4e27ab,3e737a32,d065fdd8,c003ecaa,84a2430b,4a7b4d40,eb6f9721,93f7b541,121d13bc,480f7868,ac9d8302,6706d2f1,a46955de,a3912735,c5f02e0) +,S(64e067fe,17127128,6ef0fbdb,b2adb4c3,395a1ddd,f2ea76e6,261e6cf0,112c6227,8ef6afa,4bac7536,a23f08ed,eb8656a3,47a13f25,2dfacd37,c215cc3f,473687ab) +,S(57eb5e46,40721263,97f0d12a,532aa1ff,abef7822,4ba9b10e,8fc95e1c,6075a071,8a700d2c,80df0b4d,7e2c9291,9a51e565,8dd6ba5e,59bf1503,7bf7a0a,935545a1) +,S(b83adb94,ba1874cc,c61ade9e,43093cae,5bd86ff5,e86615c1,abca14cb,6eb81877,93417bf2,1fe01c85,5c6f7782,a5cdb01,a5d13b29,d719f0fb,a2373ba2,8bada7f1) +,S(7a90bb49,71537bc9,3d1bed23,891e43c6,e350fa37,ef8e73a2,3fd46ea0,5ed879bf,ee0fa59,3a51af19,70a6d8b7,22d42bf7,ca253572,2324a59f,c041b3e7,305fa3d4) +,S(195e4afc,6c95d6d0,8273cb0d,fda2da3b,4696b569,650d4230,4d396fb,3e653831,44075199,9dce9d6c,f10ef736,7888dbb3,a713423c,f2881cd5,76a681ce,6fee2c8a) +,S(28ec566a,29bd09e2,f8b228b9,ff2ec49d,56660734,f2ea864e,856be0ff,fd6f60ee,5b4f5e34,298b5a56,a6826143,8ba59d25,4922078d,f36dfdfe,d4e8972c,647fe031) +,S(1e005b72,be1bb77c,80512581,58e940e3,544cc133,a76ddf49,b681422c,1815cd22,9c523adf,4ba5d753,93e432f,491f8e1a,435165b2,f40f79d7,889d82f,8951a25a) +,S(f54ae39a,d074c399,636f0dca,682b9405,4e87e87,3132d7f9,cf79ad85,fa82586d,db2e9b90,1c2dac2f,f4851f4e,b0ffae7a,4c0cdf34,ae9f964b,841c539d,84040667) +,S(bc8dafb8,d385cfca,3643fc75,c8a93f9,792d2b9a,715269b9,ec40d581,5936e6c6,dda791ce,186ff28c,db7636ad,4073996c,4a2e2875,80da17e8,90d03886,1c14aec9) +,S(60b6ea82,627f5414,16978e7d,3c9d911d,9d9de92f,3baa0975,a8de4e3a,8ed3ab2d,64693ab,fd095109,ef89cdb2,204f2f3,89dae035,f451fe28,58fcb4f1,dbff0999) +,S(9c8d1352,bf291803,22730718,5ee65aa8,b14211de,6d73dace,2937cc6a,1ee79134,3c6e0d66,48f3fb6b,23e6e4a9,5df3911d,797afb50,9c12e90d,7eac557c,7b1a9505) +,S(bb2a8344,2997babd,6cefa2a6,1c9d2fc7,a42db3d5,bfb19334,c7535bd5,630be896,20de4f27,69596e65,edcd695f,61a0d8,3c8925a0,c8707470,9e84bd41,615ac75f) +,S(ba8bca72,496a6046,7b250a38,cbb536e3,b9fb9ab1,43800655,8ef5d186,2b66d50a,81abbb33,e835f864,3d075858,7ce59671,7e08c1c6,75af4c5b,eb80429b,9dc46aec) +,S(529fde52,c4d1c978,405107ba,d862f7b1,995ffef5,cba4a59e,d71b366c,28e17b46,ed7c2521,d39ca130,60d5f509,d0a8b0b3,40eb78c0,d4e4fc4a,83318a65,226d0a05) +,S(6e0e10c9,4445acc3,593efdd5,4b8ea1f9,db4a45a1,4c2d0a27,edbe3fc0,7a90e91d,43537a25,d2091290,f753460d,8816ff10,41308e6d,fb04b7e7,d5a108d5,c9fd83a) +,S(b2f90714,6fe2e01c,5cbd73a0,c0a358c7,a05aa730,97a060ff,491f362c,75c20e90,d0e680d8,f7d27eff,12b332e8,a92b846b,4242a746,9b31740,6c6af499,5ab51948) +,S(bf631a7,933e3ab5,f7b8707e,55a9498e,31ec3f36,970bb9b3,f5efa0cd,1a3f28a0,1030146a,387d4fd9,c12544dd,a448133a,49b5a6e0,d4b96933,cd47e89c,608dd5b9) +,S(cd7ec470,e9d5e3fc,a4c344f2,9edcd4df,4b7033ef,5e7ee99,de0db6a8,43926034,3aae77b3,bb008cb6,e982d673,7f78e920,f3a587db,bc106c0a,25c65d45,df8a4f6e) +,S(b3ac95ff,8365813e,e71199eb,946b5888,7dd34b1b,a214482f,dd3b7fa,3b8ce6b6,e3d9bbdd,aaba7887,8e763360,abd370d5,9d941ba5,cf758d33,3b480f00,ec5e7672) +,S(7fdc21a8,6962fced,e6f9fbdd,77e60a00,4dfb858b,5b149362,dae4b4f4,a4ab84fa,2ec0f21c,3d1c6d60,f22817a2,954d02b1,ce6e8b91,d826c11e,f8e542c4,245a55f3) +,S(6f019691,def39afb,103e524a,598bca92,3c99e44d,7dff4866,f76fb6c4,47e6b40b,a225b484,cda916f3,559e5782,3b519827,8bd7300a,c31dace1,7a797582,2a2e2a31) +,S(cb9a979f,c4e8d887,f03956da,9ac89637,579813b0,c3b27874,82961a54,e54c16a5,ff42bdc3,1a837d98,b7d37a38,db3c74ee,6109e868,d06b7c33,d5562dde,b11ddbf2) +,S(2ccee51c,9d63f3e8,99314da1,656cbb15,635928bd,309cde4b,69699860,c0e28736,dff9a128,adb8b4fb,7bcaa1eb,6bfb390f,226b2ae0,b72443a4,5982d559,91d379eb) +,S(a239538c,c5e0b53e,879f484f,7202a52b,d52d1cdb,ca7ae7d0,639850cd,b2c4f57c,6cc1f9c5,8998e347,2c6db0b8,6b1d9092,d3cd0c84,267bea8e,75a172f9,f4879dd5) +,S(e77796ac,c619974a,63476635,7bb3bf33,6ad1e87e,8496bac3,378db956,7beaee90,3eaa4b63,c9679050,59d8cf67,7b7095c,4064534,1c592ccd,ddb7626c,10586f0c) +,S(52c332cf,37646a2c,c1bf25cd,26a6131c,3b243688,9c44d804,d2762fae,618250c6,2279bab0,a19f78a9,43e695d4,53a0dc3c,f0312987,6ccbb70b,1d61eae1,e92729bb) +,S(d68826da,36e5f78c,bf728468,88bbe6ef,f6e55d34,f8b47a9c,8296e662,4ad9bbb3,88ffeb15,5f70c6ab,9b83f9e8,30e2f449,7e661dac,889803a6,d30a2ddb,11bfca5f) +,S(af92bd8a,2a5b0034,ea074d69,a1d7a517,808d3b61,58f5f7c2,d3fb08e4,eb825cf3,2fef9245,a4a3c837,d4b57ded,52009294,c077a801,8f633313,b6db2d4c,92c724b7) +,S(df86fa3f,63d95d73,98872011,c7545b3c,f5f27fc6,a821bff4,5d09afd1,bf76d895,a891e766,12953c4f,37a463c5,ce449574,b1906c63,c192ba4c,67f4f78e,d1e93a35) +,S(a049a35e,119e27f6,e272774e,f470ca50,9a1da9b,a4e5d781,6e64a13a,23383c24,4cb8e2eb,ccc82ca4,a2f82841,34eb3a05,f85a2335,a3b1b117,92886029,96fb3fd6) +,S(b3193cbb,f3c8f3b0,b47b1a0e,5689461a,8da801c2,4c8d51ab,fc2e67b,1d9f331,2032a290,3f0cf0ad,21b63de1,565170a3,edee470,a4dbbabe,5f3d459a,3827c7ff) +,S(ff3cd387,554fb3a,89142ff7,3e97fdd8,884964bb,2f457283,e45b79ae,ef1d692d,24475b62,c1ade1b0,ba36ad93,27916e19,637071c,22fb59c1,f2108fb8,d38ec5b2) +,S(c095d24c,42b058d,c2f77e2b,463a2c7f,1ddd1dae,dd6f6111,11ba78db,26763204,d2648519,61974d6c,cf66de22,1028943d,ceccb181,ae03cd38,fb5aec22,fae54326) +,S(5365a446,389f54dd,8279826c,e1e7df5d,b4a9699d,b8208efe,80d1eff,2d883ee,74625a49,b7856c1f,b157776c,cd79270a,2957862c,a437bfc8,75986264,401e714e) +,S(4c073085,e96134b7,75248412,c36b3b33,77afa275,24c83369,a2477b8c,9851f15e,fed40d9f,a44f64b1,59c290e1,63d1add6,92bd2782,51de71db,e302cbfa,67e508aa) +,S(71edf152,ab95a066,2b7e5190,8af75f38,d03d27b0,3a3b6607,4f472b7,706a3114,c3e6f145,dad44a3d,5cf056d6,2c608b4c,840608bb,aa15a101,c9bd3cca,e511741d) +,S(1b7d41a9,3cba67ad,c145962e,7b2c6353,bfbc8eed,34eb4c27,bfc181d3,1ac0a802,b165e7e4,23c5d880,1bc3bd1,ee0c6778,50986b35,9dbad06e,aabb2f57,dbe553bf) +,S(ce997626,cb41ab98,58f591ca,c89f7a73,bc1df836,716ee74c,45c4c781,eef4ccc9,be76f81f,a8e4b053,f63be23a,5e72ad6b,7aa359aa,e9407b60,98bdcadf,e367b167) +,S(5b0cfa70,aeccd544,cd634d10,a2bcccdc,33e4525a,54259ba9,92c40100,698bbf2f,ec50e655,163994b6,4881369f,b3aab550,65df1a5b,dc6037a7,7c01e56,904660d2) +,S(a03873b9,695ee82a,42b72a90,f0cdfc68,dbedbd2e,a50d5ff6,e120d811,ce8d5097,5947c8e3,56e64f71,b36fc1f7,7ecfc7f,a31c0f0b,cbb05102,775648bc,f2435758) +,S(d6b91cc6,6dd31e59,f04c6e58,bd7448a3,fac62753,68579214,11a8b42c,66084ed1,fec7fa6d,778c0b63,e7dfd72f,ef044da0,903ccfc7,1786f0b1,85edd004,f691a0f2) +,S(896a1313,e0a96d35,9fd42841,7790baef,a40647cc,f56264a,ac3b72df,687b5692,831d4b47,52f5fcd3,4942ca26,95dc86d8,4fe49c73,7c2bd46a,f892eef7,304117e6) +,S(779e7400,8e564167,56795a4f,9d2021b7,94aa9689,eaa37780,8e3a6061,b5bdc04d,739ea3a9,4f38be31,df57c052,362d1872,333d2777,c6704c2,e69271d2,9f78b123) +,S(f20988a9,3a3273a5,a015866a,c4de930e,507b5b48,cc5ba52f,a2a71e9c,185e72a5,6c861907,8fadd612,c0535163,dfa4cf39,648b59d6,4b056bb0,4e06695b,3b51213a) +,S(7e8520e2,3151c78f,9f48bd10,5aff4087,dd90f9c4,2ac33e24,19499ab6,8e5b21aa,995827b,43c276dd,61403388,27b2fb9d,9fbb46ec,af10b65a,a8025971,608f38c9) +,S(98f0e4b0,9e72ec0f,d80ce3ad,eab29913,fe84fa49,1786bd79,7f6832cd,7ed4dc6a,58c42bdd,7ca43579,b45df8f,b2a6db3d,41f75a3f,89dfab0e,e000383,c9e04a54) +,S(31cf38d3,dec1162e,e75eeac6,6827aad,22fa3ee9,abfda002,b6956600,6fe596ec,ceeb4b04,fc6c31e7,7a6260ca,bdf92569,92841221,d60ca74e,81309472,8fff4442) +,S(60766d5b,50cc4d46,a1234c92,1baeec1d,29900e23,4493f174,2f06ada3,ed9dfbea,13debdc8,9795f421,6f796a53,74b87aaa,bca4f73e,fda2ecf,7d151870,179c44a4) +,S(1b0ebbaa,35e8ecb5,a76d9c30,ab88fa10,b211ce3,e7c2b428,117645b6,881c7895,a312adef,e1c4a34c,d9f75d10,bb2ceb29,79184181,bb955439,e317cde4,202b2f83) +,S(b3129f71,c3438602,c9c24ee3,2f997341,26dbd76c,60cf7713,200ffb68,e486fc94,bd4beeb5,70966285,6a15002e,a25046cf,996a1cc7,9a570549,467ac179,b4f145f9) +,S(2925ca17,662f0905,3927d40f,f2fdee1f,66cc5c13,c3587e78,fe48effc,40cc075f,6d37757e,64f0438d,7bfb3658,5df58dbe,a8ab8cdd,89871670,dbe221b7,4c416185) +,S(8d55b85e,fce6bd0f,ff8906d1,6497d35e,4b69e701,da7845f2,aec1a049,39c9ec45,435416dd,9fd01388,b717ba9a,df574456,785cc0fa,c115dc04,8a242270,4a3c77dd) +,S(a660a9bd,b419e19b,16823b5e,dc3fcdde,f1021b71,4debd17a,9a0619a3,aabc23c9,ef6d4bbc,e963bb1c,2edffa5c,ef70e330,bddccb4b,3b23cc52,d5b4a689,e0c76c32) +,S(8896427f,c92bdbfd,59699ec2,222b4bbf,2198e160,94945c77,1f207bbe,4cdd6b0b,bc6081fa,abd2df26,b087acbd,a2c67c63,2eb1a8dd,6fae2454,6f3fa747,be421ff1) +,S(47e5d9b2,3f429484,61ee9f38,2f14babb,f93e318f,32d67063,ed20e917,9894e993,bd17f8b5,6084d3f6,62facae,24e93e3e,29205f21,68903ade,4f7476a5,ba70e8b1) +,S(ff352870,69bf1131,6f530ef3,b32a487b,3f11e6e,f8518c15,b1202a18,4e6d5487,8920338,ccf36f98,4afde38e,1a74528e,86fddfc7,8d7a6964,cad4286f,231462b6) +,S(2a713d8f,eca88def,b5fda7f8,8de9c6cf,9b97bf0,4c070173,aa47dad9,197bae2d,5ba64900,f7d18ec6,8887bd26,d2769b0,26d22e96,1007b58c,b27b8983,a6e7adb1) +,S(853d813d,3133ed7d,a85065a5,755ca0c5,3f32f156,154853b4,b018668c,b165ace1,fda8017,e00d20db,6ee4c0a,5790b504,afc376ad,170d74bc,1261abcd,241ea020) +,S(d5c4dc15,37b048d8,6631fd14,7d896d6f,f403ceda,7de8883e,1ccc006b,6d9ccd99,9ea2829e,64213434,eaaaadc,43ed10b8,28e706a,5001d7df,47146058,8bffa8ed) +,S(f649da82,d50fa298,8c598d5c,caf1e7f7,2bde2cc8,8073b55a,2e43b6e7,5d44905e,4438f4cb,d3e1938a,b461b1e7,b94d8f6a,c6e287e2,50d06da6,67aacb1c,609bb6cb) +,S(adc3515d,4fb7776c,b1592a69,13254d7,afcb20bb,8a4d7529,7ddba70a,c35a7193,49b7bd8b,3f280916,c6024e46,afb45e6b,da5bb15f,3d3497fc,85e9a46f,acec3d80) +,S(d78c46f2,603a00df,b45d0495,c1e2b346,963dc82b,84512cac,c2227acb,383f75ed,76b02870,2e7a3da8,79272759,1e2f81ef,54ed02f,debde8b5,38f9ff1e,4335a21f) +,S(13904467,59e0fc27,c2a499ec,384e0906,bf293dcf,c13ca16f,d3bedfda,d320e466,5320c884,b74bcdc8,6e0c21f5,40c16f65,dc61f19e,9ec5e8ef,385fb637,ee7a3701) +,S(93700603,543ec558,3fe48141,310b184,aa8faa04,6058467c,389c10f3,b98c42f0,8e0acf55,bfc585df,4f8cae06,131753ee,23a7bcca,68354182,61450e1d,e1a136ce) +,S(bdcb9434,4c556618,a247c935,29c5ba74,c68f25ea,a5507148,1431c00d,6926884a,c29b7b1b,ad6ec5a8,e7046cf7,cdf99c6d,15f6a3e4,3c6ce718,9ae2acbd,531bb17a) +,S(839ac85a,9337818a,b07b5611,f67eb16,6342c1a6,8aadefc4,6842c023,336afb08,88b30690,e79fb157,3e424c80,75bc810a,fb9d57de,5464a253,79d06222,ea656b69) +,S(2959c32e,bcf9289,9882e003,c8a28e71,3d491402,4d24d878,ba8bed88,47b949c,ba2643c3,95fac027,6b7c55e4,d2d2f530,318665f1,ec2065d6,eaf32c05,123e04c0) +,S(71116eb0,5def3abf,bff3e235,a0306605,b18aa465,b47167b4,4eb3486c,c442f3aa,2652a726,35be8336,f9b51607,76b3af84,26a23716,871fa64c,898520eb,822d6630) +,S(400c1bcb,dd3e3978,3ca06e8c,2abe8b0b,9b45034,a1655cde,fb748ef4,e2a477af,1471c86c,c8b27b48,1d4c45d,1533a5c8,d46fc3cb,ea4788d6,c48dbdad,6f7670e3) +,S(2a4abe28,5cf94456,aa92216a,dd635abe,71a7e825,712d775f,d61fdc98,a4c9288f,3c0d2483,3d6ffcad,b471d234,4ee8d07,15c09719,b18cb581,8ede730,35d89c15) +,S(cb0113a,1832843f,7e32ce25,15b0b23b,8a803ea7,50b500b6,3fb89f6c,e7bb1884,816d7056,b775f13a,cfd94a6,f88f442e,519495a3,6e74d8f8,359a8a03,16583bda) +,S(4d7be7ae,5c4271f6,c1adeb7f,4dac8467,beae22bf,ae00a71b,1e353be9,5ef172fd,cd7e6415,5708d20a,9fc3b0e9,85517642,f677281b,49490439,14845a36,afe6ca56) +,S(bc285658,fd724cfe,e6ec8c7e,35f9197f,e9083bf5,439fd4d8,d9b262bb,7a3c38f3,80c10d7,59f76b4b,8330c96f,38a1af24,1d1b0db7,4e6befc4,4f085180,c1c33729) +,S(a829ee43,221d9955,38ce8aef,e80c49ac,dc6e71f,4293750c,c0585c1,69963f04,144f6b56,d996a18e,627d4c8b,8115b2c1,c369f410,ec8924c1,73382302,3758ba33) +,S(f5450bd7,efc9227b,e7d28ad6,75ee14df,1b2378fd,308e6c5e,68945ac9,a47d82d1,20201fb5,cba463af,cc46e6da,caac8417,63356e64,5cdcfa5d,9d810c48,272cf8e0) +,S(7dcbdb69,46dcf79d,454095f0,b61341e4,45243bc7,c2262817,374fea5,37a1a380,bd2cee4d,c7ebc71b,46fb3bec,bd6787eb,d2be3128,703a884d,98359c5a,43875977) +,S(f1d6c1db,cc8d33d4,fbff821c,8c788d06,2d906063,2f44a786,585a6526,20ce776a,3696a3c,8473bd07,76e77226,8cd09f96,3dbcd143,e631c32d,5d83254,ada29340) +,S(f1688e1a,d5df4e43,67caec6,f2234534,f65aac3b,79a94ca,32a786bb,9fee4b49,efb620b8,e2164685,c9a3837b,5c6eb70d,759824,96541aed,eb80aebe,4193e34e) +,S(b639e284,3ecffb0f,66c2819c,73787e10,527086e2,5d3da074,e037a915,e06a5813,61fde1dd,63ff304c,8ec99d4,67ffbabe,e8000637,b9856bec,344b825a,4f198407) +,S(11a4ff89,d5e63ec8,6a0ce18,53dde8a8,875c9a9b,f563526b,cd25b830,6e3314dd,9f7ec77c,9a3f88cb,7f64b1ca,f2b5c7cb,d70d65d6,dbdba9f2,68832f3c,a2e117a4) +,S(323db783,26abfd99,2b13d67f,3ae01415,77162203,c50322c5,7a7933e8,7d687d3a,92c8a67c,ec834653,d0f1a50e,a7834b38,631d83ec,ee38e50,33ed927f,7e14253b) +,S(2907fe12,c8433ab6,2e6283ec,85b93342,ecf14d16,6d90cfdb,b4061968,aa35170f,160f9790,66291ed7,d51962d4,be02d85f,8a92ff8f,258a9a07,55b8a5ca,2d57c2c2) +,S(67cef7bf,a58519cb,6c5cd8c4,f69d5cc8,84cc77a7,854a4b6e,a40b6dcd,57e43e17,e8071d35,e590dfa7,4d4f4da2,82033f1b,590b9383,70b571ac,ebb3bc11,b9b48482) +,S(81e40522,3d4ccb14,3952867b,b8398cd4,e5ab92c5,d91066b0,d1fdeaed,55c71165,55357564,af12b507,b2e24e6a,425697b9,e6356b82,da36f111,2b5c3394,183c0d10) +,S(4880abe1,47bcc598,a3743304,1afabe99,fc525daa,564c4b05,2aa60c27,5c814628,b6d944f1,ec69e7bf,2cdcf621,864ba76,a1df44a7,ed15ae15,290ddbe5,b91f585e) +,S(fa2bc507,19131dc7,d5f23fe6,9e60377e,2d2f11c7,f8a9a451,82aa69e6,24eef53e,9b0f2017,5f287d80,41f58930,6cd022e5,9b63bab5,1b26abfe,1a295ac3,46a2e9fc) +,S(aaa18fe1,25e781a8,901ac351,38d2bffa,83cf7c3c,99a4c7d8,54b30c70,c9483e8,6a8f9e1e,a40f6383,6008a0c3,596f7644,c044bf53,9700a142,ec5e3c97,c075b03c) +,S(d473950a,8465711f,71a6f1f0,22f4bfcc,1c16ba81,4bf73c2c,1eca244a,da4999f1,6eccf9c8,aafb09df,46ca0d36,ee9028dd,1df62bdc,6ade77b8,a5d410d1,a69bc775) +,S(d7e2b17c,f8e1895c,cf9fb163,b56f4be8,ad99133f,f00f2ce8,8ce911d3,3b8e36cc,b6557693,ed9371ce,69bf397b,56c0b566,21d38958,ebdc0b96,ca488570,1561e6bc) +,S(3f1e73f2,a2192809,acaecea1,71d175af,683e796,e1337db2,11b6b34,8ba924f0,a593bd94,96308bb7,76b63703,bc11e30a,c5f01c17,c8b9748f,3b4c8015,56f52c3f) +,S(4cbb68b4,27899c92,1c0d969c,94341d8d,161f3b8b,3fe4cfca,2b583a63,eb383dd2,6b3c29d4,97728ac7,b45cf7ce,ea618789,95935bea,bd146e1,6c756f61,4698b6cb) +,S(e41b826b,52ff9bea,354af41e,3004b7b9,edd01e13,8cbd466c,ba4cbab8,d63bb4e0,75d5b642,47388aa1,51d4d8d5,2568a804,fcf66ba2,42b3f56b,dee0350d,ecc527ed) +,S(d508dd5b,ec38f795,88bf2d9b,6bcc9f8,aa96689e,91c1381b,9f8db311,94588028,9b144547,1a2ee559,6584c927,2d779fa3,123a760d,9faeabfa,1e39d3b5,ddac2595) +,S(5276f127,3bad7149,ee5ff022,2769dd6e,973018e2,a9948070,fdc82148,ac637de0,73f778c2,caca737f,acd3d877,14e24d83,964dd47c,f22915e6,212bfb09,8e804e21) +,S(2e9ebbb5,9d108d21,71d28c27,32af659d,b534cd10,c5457cea,3fcef018,4d7761a,229d20ba,cbf0104f,b385621f,dfe386df,4f40c287,7d956046,60eb3923,46937bff) +,S(ad819539,5b94bb1f,4e60ab85,932b24cf,2c79c20b,79a972bd,db1e4201,c49f1d1f,9226c45f,4d161a1a,c5c3c0ba,a0536def,3c6052a4,ec22dd24,cc803a37,b5d791a1) +,S(f5d4baf3,93ee5acd,944b2057,78cae170,8a622f30,68d3147b,62bef05a,88fee96f,9a187396,9eeba528,9f5fca32,6bbb922,2eeff5f6,6d205a0c,78b77dea,ee17d1db) +,S(1a0f5e1c,b2fb5d01,fbcbfddc,da83693e,9619ea60,1669c728,7710733a,e6ca778c,3074e8d1,61da2d34,38be57cc,fc437742,533e415e,5f0c888b,834a2fe5,5d4fd6bf) +,S(a78f6f7a,110cd16f,99fbe16e,9be52782,248518d8,e621f478,2167887,f81cdae4,e3f78e8d,507f9af8,11653ab8,5f2286dc,db34d9dc,8106e9f0,57610267,411f02a0) +,S(c53e4243,b26ebe40,a1abffdf,3c42129c,47e92cb7,32bd66c0,3d4a4290,c68b1c25,bd4f96b5,b5801b82,300b2132,2c39fd2c,65b2d084,e657ba2d,7db5df15,ab960e34) +,S(a3a1ddc8,56957468,2e9b8a57,50bbc738,f72dc60a,1c09c95e,a6559b2a,736535e4,94887a0f,a4381d9e,dcc4faf5,1525ce98,fc43116d,29f68c99,236e4940,6842a741) +,S(8160ee7f,738f54a0,698806b8,35ed4bca,113bb568,b23fd331,ef5f1cc2,80b6230b,382ff30b,f13f1e28,95a38af3,13fcd099,249b195c,96ac42a6,6a929fc7,a907b17b) +,S(68552ddc,6b219554,dbabd276,e0f2a5d9,33610bcc,776bc40d,7efdc1c6,1b992e35,eee09352,c82dec92,b0ae8d8c,d6918ac6,df620dec,86a0e4ac,abfe8025,52a0ab5e) +,S(919fef57,f308255e,1fa802cb,5acab880,c1810c62,c79a4ecf,c2251a2c,cc02ef51,cb38c163,8f7890f2,d61a980f,ed712ca8,97296366,7927ff7,39de824d,207ab566) +,S(d7e493e2,e2c33635,ee8d22f3,76fa8de9,31ed4eb0,9771746,f48ba201,307e04d1,ff7e6d72,f712f2be,2bfffb43,2764f677,62917509,3cf6bfe5,eb5cb7ce,85c1a8b4) +,S(6b642a8d,f4131ddf,7f31dc9f,c26b96c9,286e1f80,de2af96d,52e383d1,ffa73a41,1e55e667,5f0ee13e,76818c02,ce424d8f,48cbc930,b25dbb85,363019c5,fc29979c) +,S(7ade4aa8,1df8c53d,1e64655f,bfe0ad88,4057e0b2,1c88bb2b,4c8d5f19,34794191,cb0e9802,fbf1027f,dd8d2167,f4f4680c,f9e18f4c,3bc3310a,c4014745,530a06fa) +,S(6d578cb3,1ed19dc,812fd360,a367996b,91a7ba42,7d2f74c3,e2a1721d,72e1c017,4ac9ad2e,aab987ed,dc994b74,733fc710,19301e1,f691999a,5161cb15,ba11e9b4) +,S(df95b5f7,7bb79814,21892d72,743abe4e,828a7544,f9f0e774,b1f5ada7,a054978c,4ccd011f,a966604b,32f70bd6,1a3c1515,c6d1b261,a3ce5c59,7078cc9d,f723b32) +,S(d7392d17,1cf72e1e,ddea0be,a242edf2,c418e9eb,7dcbe1b3,aa07ae7f,a11ef08c,4b696f78,20a9ac8d,a5f52c8e,9d1a39c,bbb3d3d9,fb5b38c8,1b46bcfc,90e869ef) +,S(f01f2aa9,82842315,98f414ae,f87c9d7b,6eaf21be,c4dddea1,3967677f,5d2a1032,7c3be753,da5485bf,13a4cd72,a9e3fbe2,28fc6a22,c502b19d,1b6f7d18,149e2b3e) +,S(cee8f811,4b1829ab,a47e9b5a,54de7cba,1f6edfe,624f30e2,ec4979f8,3145242,4b01f7b4,cebb1125,20bb0ab2,7013af9d,11d19c4,14b0c29f,d142c7d,5b903a09) +,S(f4215bb,973b5586,ec051388,203b1c99,cf69b555,6b1591d2,fc711842,759e9832,5d8a5ce3,ece7c011,df3d414d,e62ecd4,2d716c73,cab02e29,621307e0,658e6fe5) +,S(e39ecc23,90c9464c,32b08759,9a44586a,da09af95,ba8a3b30,2cb015d6,4a49b18a,bd7957c6,e1feb061,f5535231,4f5c5e5e,c8245f28,4638f390,d079cffe,bd2eea7a) +,S(9e6bbe6c,402e4cbf,e298e789,458fa9a6,68cfd407,3efb3d25,4699278,760589,989bf3f,9912b242,419c848c,f82c3fb6,8690ab05,bc23372f,eb9a850f,b4dd44ba) +,S(67504bbf,57824cd9,8a3bd82d,34fcb59,3d20590c,a90b5652,c4ea4695,1f48905d,93ebbf6d,3678478f,56ae5a54,cde812f7,d8d40928,21b5e82,71669dd3,f0b263d3) +,S(4e5bee50,503a28c0,9252b9bc,dd28af66,cecae127,2128e88e,fc431373,7324657d,d6891df5,3a4cdf2a,d7ab610b,817b0f60,c2502442,2e40269,a7da80dd,2672e0f3) +,S(7481d5a5,bef3d397,533e01b3,640b0809,fd6ff946,a1e5481f,b90f0b3d,dd05c21,21ee7275,9c217d19,3999df2b,e3cddff6,fe59f26a,e570e0ae,58868441,f37e342b) +,S(778dff4f,dd32f251,6defcd94,188f2440,26b9f789,ff4eefd5,84b2c15e,2bc61d62,996a2edc,f43e10d6,93cc2d00,c6f4506f,45be1dc5,c762fe20,3dce124e,34e320e0) +,S(cf531271,3d65d471,929f6108,12db63ac,b1c52203,eb879ca4,a8ee9f62,774580,38e28f4c,881391e0,55fa7148,2c92c097,c842dd,74a58d6b,5753d6e1,2f773b19) +,S(99702d01,9420286e,723625b5,62c8152a,2dd5f1a,eff84437,8a182cf4,25582031,8c4af2b2,654f1631,82d08bd6,e7f8b6ca,d5365b9e,d869fd23,e5a3c4d4,7e5990c) +,S(68614c88,6b59c198,c49245c1,e1232cb0,b4bcce31,e0a2aa15,9fb2331d,ff1dce66,49c870bc,7ac78e22,b635c2b9,6f29e641,3b29c6ec,8ec8b3b7,8fa6597c,90180c7c) +,S(f497a9bd,3c0e923d,5d622ab6,afeb9e8b,d9b4525f,d1f30cae,2a44d0b3,e13256b,af4d0273,e088adb0,b7de7724,b387e109,d49d20a6,eceb369c,12e07eb4,f28c9e32) +,S(50a764f7,f5e29cba,ed7ababb,97d2ace2,22c06c65,9ce87d77,83b5bd0d,d165bbbe,4abc57c5,4c9f1d54,e73cead6,d77a85a0,8be25ba0,ce96b8a5,1b4fb460,5eca949c) +,S(fccfe1e1,e312e6a3,92e83cb,97274576,56415255,b127ad54,432c93cf,4ae29bc0,e681795f,d8340902,da0018a0,dbb8a424,870e4bcf,7de95f0e,b2c7e98f,47a46c58) +,S(66de61ba,48c1fd44,80d5c78c,3f06c3e2,45ecaf25,e4c235d5,8408c90,e1b62ed1,bf3bf086,a90221ba,711819d0,d80194c,c45177e2,57b1408e,7e649f15,a92aa6a0) +,S(4373e7ff,e3995503,97bdee9,c9d3d70b,39f47446,45fd3c1,63b2012d,58e148d6,440a1376,16096124,42c7bff3,5f9a8aa2,3ac64e8b,bd5ca839,bd3c5a39,b84e6fe) +,S(c8774ea8,7d83fc06,c36ab357,b1fa76b6,c647cc9c,ebf2e125,3f3ab303,9306ef1d,ccbde78d,2e4d2c0d,ace4e660,efdc4d1f,a1dbb44,4cabb0df,bfe29de3,191f7249) +,S(748cb6cd,c57d89d7,8842fae7,731b1e28,3b59ed18,cb761c88,a4d2e4c1,c3359fc,62f5b6c4,11c72d4d,a412148b,8b038b32,8f98aecf,ece537a7,237b9f87,646b9a3b) +,S(35a83610,30fc10f3,53d2fdbf,cf67655a,91b86555,cd3d9eba,f962996f,ecf55cbf,6fd79f5c,df8093db,5c7f3a94,52dbe6ae,bd1ca410,199deb04,761ffffc,77ec9fec) +,S(cd05bbae,d0a8d78a,2e05e9d4,1c33cbd2,8d59d384,75d42389,43f9cc35,3a8a30c7,9f7336dc,8e77fd55,bd0b9e8f,23eedf1f,d0c16f69,921994db,dc2588dd,ff72a8f8) +,S(da3fe760,2547c831,2d4f515a,5ce530be,29379325,41c6de36,364c4b5d,ead43c58,df60bf1a,d2fca43f,6d482df4,61fc180e,5e3cf692,995bf8c1,7d32a263,730b3ddd) +,S(bbd7996c,25ba5c24,a14742a9,745dd2,8c18a598,8279581b,f8b5958a,8092051,896a39f1,310be1c5,9de4b86c,7c9bde10,8035ba39,864a45a3,69573d16,4afbc2d) +,S(bf7c20dd,64ba5f51,2a996bd,428fdbaa,df1853fb,f814e6c7,a415fb64,a3e16070,afaec0ea,2b18c89,93a1a565,f3db58dc,748df9eb,4d9f8a04,a18cdc23,4710d5c6) +,S(919d0aa8,10928129,cad99b09,85bea5ba,7d4ea114,9f9d5d65,2b6c0e0f,20464903,60d9352b,d053b01c,83caa121,63e8c9a3,e2153cbf,b335943,7a2a788b,7aa02dfb) +,S(5e7c2a9c,6ab8251e,8f8749ab,e0ef107c,aee593e1,a8632a6e,1e0e0c4e,d3cace9c,74186f42,6f03f806,36d79258,e9edf593,6e21e0b2,d882d933,c0241ae9,7d761582) +,S(2815184d,af1e838d,86d63a32,db5339df,24731743,aee62add,21e3a6c2,c2b0b1c0,2a4951e8,3ec61221,b1035c6b,a7d2cbed,f6ef93e8,cff69e01,295e09d9,20e05919) +,S(e3086e36,91432a20,8602458a,87d82955,c8920ca9,fe1c079,3eed878b,282ec3b0,13db4219,50388269,cd591c5c,d8e07ba1,b532f01b,485829e2,aae4f55,f05e4362) +,S(8264d534,2db3261c,d7e294c6,eba040f5,461fb9e7,e3fa13fb,dfcec29c,20f2fa7c,4dc311ff,10b92db6,b67f47ad,86fd74be,24e3dbe0,d6a74842,92f35d9f,6e95eac1) +,S(9edbed6,3c4b66ce,a8c2ac6d,89622d61,6838e1fd,9b93c363,c2bde528,a629b2b,c1706a02,5b1f9419,2dde89fc,63ae2a81,ea015ab2,964beb58,d5ca7ffe,84266644) +,S(68006753,672e9ae0,79fcdee7,9814a904,80c170a7,f644302d,e5de7982,7684e12e,fc1209de,88ca7c3a,614252e3,153df13a,6d0e8db0,4c82d83a,fcbe65a4,5c084398) +,S(acdfba80,9e519114,a11b1634,1835d7e4,5b93a6d4,7094846a,e1ecec00,18bc4531,a4e57d02,51511f2e,5b729309,4c50ebef,4f8d3cb0,d930ff48,20d0623b,d636d664) +,S(978304cb,e99eb899,5ef43f94,c67c471d,6d42ec19,f83f9323,3a44b9f2,e2d13896,ab26782d,2c1dbf16,a90f90c,8e49d250,1e1172f1,3e1bd20a,3deba979,85fcb110) +,S(8c8b561f,9c857510,88478c03,866b347f,46e06801,82482343,923d7e27,c67f49e8,e7908d27,79cc5b19,dd00f2f0,4ab52474,5c35cd6d,4b85932c,99614fdc,64048cc4) +,S(d4a7ebc1,987b1abb,5d02e598,190f73a,36d1176,d7f0f7a8,cb9491c1,b86a3c55,4f321dec,9c1832e3,b0d0c974,798834ab,6a410bfb,398ebf58,177f39a2,22ba4e50) +,S(14bbfb6e,7910f522,69e544f2,ad3175d2,3a36b7aa,8209c24d,1fbb4277,9508c261,d16147c7,2a1df442,d8e831e5,aecd54c6,8bc3f225,4d0f51e4,9a375053,73e8d08) +,S(d24ad71e,81de43d5,7096e101,6bd4d99a,46e52e56,782d99ee,838217a0,dfea0f5b,ae574f68,e07cead9,f214976,67a18c2f,fbbcbddf,2bfb6c95,3b7ba673,7d08903b) +,S(ac96889a,f0b46b7,6896b763,8e1a13f3,dd24459e,8601f892,c3367c7d,8b52931a,b68a8631,8672c378,d99b0b20,44b06190,876ab100,eadb12e6,c16c2ba7,c1ee2381) +,S(36b4da52,4f4ad5da,d7396445,1e79bd5c,20973df0,efa82bb2,da4bf68c,55afeeb7,5cb7598b,576f0db4,db1093c5,e6c9723c,f493bb91,450a0017,9f79c8e9,92dd6136) +,S(98c7bc3a,7054f928,d062ae65,b4140dd6,293f2fb6,cd16fbba,e00d39d0,b11befd2,82d134ac,3e614e4e,62b1b475,bbb5fd80,a5e6d3e4,e4572dc5,7cb666c9,a06131da) +,S(95bb18cd,991cdf72,1c627d62,aee4655e,aaed0b6a,b1e64537,fa89a0ac,f60b4a52,c37e37ee,5b87a5a5,e61824a3,ea519f03,dc3b0a77,f755485,d337ac79,f1f9e8c9) +,S(41e45ca2,b88a4679,6df9106b,c8b53180,81674c34,9646545,e5ad9b6d,e5f6cdc6,151ba666,b0fdb7a7,18110f62,83e3cb8f,8e40fb8b,fa1a1854,75958a3b,ec10d197) +,S(32d15f16,dda923d5,c2f1bf5c,9c003b33,b77d2450,95e5ad31,2ec62956,ae092805,58d64c72,96ba6976,d61774f,f37a0d1,6316cd19,3ac0d930,bdcf4924,cd904095) +,S(41c4e029,817de516,8dc04b2a,9bde3850,7ae48602,dab08db0,3272c694,6cf099eb,303738a8,7b888601,2f9e2053,5bb8b610,92f6222e,96968afb,98940d85,98181458) +,S(28b8ccf,b14a86a1,84fc3c3c,a6a7dbd8,763415,91a67d8b,4109f026,1dd964c5,899f98fd,5e0c59be,69150d8c,5a917d6e,864fa15c,63072769,f75f6353,8cb276e6) +,S(f2776367,762d6468,c9cc0e38,d3921813,16dd394c,eaeea519,786b4353,a5044586,1a873421,1b170183,a117e420,c41d2725,84acf219,a415778a,4eb0c34b,bee9a68c) +,S(7a1a5f53,6e96ee57,e10b1337,6bb1e895,d0703c6,5adfa4bd,f24937da,98bd7144,860921d5,67fbdd1f,11aa3ced,103453af,8c3f5037,b92ff10b,ee70ddc,e4323206) +,S(d82e79dc,5d6de6ff,8863e039,60401bd1,610500f1,57fd2b41,f20689d8,889c3f76,f1947dc2,ca625df3,af5ec97d,32e62c09,2139d108,948795ba,5cb164b9,351bbf34) +,S(6a2e184c,23ee235f,93f036c4,3875db01,22416a6d,26ef54bd,85a2b465,defd0351,6c44297d,724eb2d3,2f58a197,2f52a7ad,ee49d30f,c8f8424d,7f26c6e1,73dc6868) +,S(ea0d5281,bd1417c7,2dbedbd4,dff37c49,bf143417,d2a40dc2,573b4b3b,2ccddcde,c7a35ef7,685faf18,c5a4cfcb,2eca27b7,46b21d65,ed71aae9,15185cc2,6d2b54ee) +,S(d5432c9f,8ed0b4a4,c3e42dd8,28098f13,11d2cdf3,94672331,51fddbf2,a6030974,1e047ebb,8cc01a43,2ee6ea04,578513aa,17ad2bef,7fc7653e,936b2d22,bf8f6c3) +,S(c99ee053,655c9d53,1fdd2298,3cc03756,299f47bb,5e1d0a4,d7b861fa,c3b5e332,61c5187e,4e12f8fc,617a5fd8,746f28c3,368b6b19,1189172d,8c56e2f4,679f6b7a) +,S(f41ee431,170be2d7,fd488c3f,5f21f3f3,aae5e501,1a3c4ebe,d0f6db8f,63e11e55,f767b289,118391c4,4d27c1e0,76b7c70d,110cb938,e9c5c62f,1af200ad,7e9976cc) +,S(505a65a4,29388bcd,55561f5e,fb3090b2,2d553bac,2f8cee97,6c0685da,40006e41,c72c9e86,6b4de925,3d463d7,e7bcf7d3,d6e7c351,1fe6c4b4,7fc436f6,d793e0ae) +,S(ca34be0d,48bc2931,cabe71d7,9cd2bc23,ffa928a,5d532d67,f3665acd,375dc01e,263008ca,4dcf002f,8fdd580a,65701694,bf4530e8,baa0438d,b77ec246,75b4db4c) +,S(d52c25e,9a76600,b16dfa93,cf9c2df3,38c806a7,d8cff18a,1d262662,5fed6d7,80309810,6055c3a4,d5d1e5ac,945aa815,bb8ca3ed,10b5d54f,156e4336,39c4ac77) +,S(83e43bbe,62e39426,a0a74ef3,831cb4d2,776d7ea3,d5a2d57a,8280bae6,ae0bd8d7,a1c2c1b4,ea1140c8,cb4ec2a6,98a3f727,666eaf81,1ae9b89a,9e09db39,d1a257d2) +,S(12326a97,ad8dbe89,3181d76c,6d7507e2,74d62cca,1f0a3517,30e9ddd7,198de84f,79e7823e,7cfa4df0,bb31e9dc,ac415297,edc6e061,4fca2f18,1ac7fd46,d27fff48) +,S(1ad3375e,c82aa917,88bd787c,37fef8a0,2071509e,aef62e8a,dd5c5e4c,b096cea4,f9f9f10e,efc1fa79,454c6d55,b894d65b,ff5c445b,f4e9e46b,8c82a4c2,a94d5a71) +,S(8e8bf9c3,a752258c,278bcf19,7aeebae6,fcc198f9,2310b13d,d114c1a7,d10fd2c9,e2f41d5,b7d94378,5f61f572,763e6e88,df7b321e,9baf0445,482d9f22,f1160195) +,S(c44cd33a,144e2926,b7ebfb93,d7d9ab56,3888f6d2,694efbbf,a5665e10,f6e0ff7,c2d4afed,f77c22d2,88f9cf38,a057df90,baae8ca3,1e074d11,bb8f89f4,e9e1bf4b) +,S(b960f0cc,bd7d3431,4138accd,aadc2efa,d5b60071,7c69f1c5,92900a40,ea68b24a,e0cbda14,801794eb,19401935,3f8ab80a,f525b8eb,2b64e5ee,62afaa5a,da4680b1) +,S(f7104560,8fe120fe,5d036260,5bc3de58,76e24b6,20684413,a440cf56,6520559b,2e208f0f,fea3f78a,9a283f9a,6b601ce,db4df4b,b2fda7b6,306dc8b7,3d514f35) +,S(83a31aa2,cb10fc14,ca8cb2cd,b21cc163,41cc9862,67a5f1cb,75f3f90e,1a92c59c,be18918,400a6e32,b4fa3469,ea6e3613,251ceabb,29cbbfaf,2c16af07,fc5413ac) +,S(3d7b800f,8802e521,341f83a5,efdb355a,b60669d0,adc15531,72c62097,8106f6ab,f9b179a3,aae98427,e02d4f00,89b3cee7,250e30e5,ed72d59c,198d5ae7,1d639e72) +,S(7bef4e27,9064400c,48d4f27a,a754deaf,8af74b10,d5dfc497,98c38216,803f50e8,bab0d71f,d02c67e6,b274693f,6a4f402f,831aa4a3,5fd83014,fb07c5c2,e62d561d) +,S(e6b2c83f,2a6ea201,f56a024c,482a8076,2f2053b4,2e4d2901,4a5178d2,3361bcc,75d1f7b5,209cafe4,f98e63ac,53052a40,487b3390,e4ab73eb,ac63acb5,cc3dc88c) +,S(eb4f3b95,f12c16a8,e5e13446,d9b8ca7e,92c56bdf,565aa4f9,bc1c47af,914fd145,e3776908,e3f3228b,ba0c0523,7eaf1963,1835797b,ffd7eff5,61b30fb3,9fcd6307) +,S(e2926c0a,858b9732,82c1b34b,4c4ba4b0,3099797d,d29c5fb7,4447f28d,ff962e54,2730f3b,e59e862b,37ac50,e5375d08,920cb62,b87ee8d2,f5ec9975,886359e2) +,S(8bf4efd6,c11d7251,b06a5e08,99ff2e80,349f8022,387f3200,6d9de060,bfbf3f68,5514395,457848e,a79ba6c4,39a7c151,b497751a,a4a1784f,114686e,e351afb) +,S(4ea97c1b,920d2ec5,3c2182ce,d6f7e1f3,df49e673,1a14ecd7,d67aba0f,fd077c78,5e958afc,d3894757,e0aa89ed,ba26e035,f06e4b11,da13675e,29d382c7,b21fa9e) +,S(1b12e81,3c7c19be,18576f93,69d938f4,bc81bbfd,7e9eb443,cf2b9ef2,b8e2b3ee,47220a1f,718c36af,8eb8a9e7,ecee3899,ddc49127,91421438,8760d6d1,699a5ad1) +,S(7c6c144d,4f839904,ba40fba8,b739913,54f40bf0,f5810329,c4e3b961,ee6a101d,6eebb976,3ceae5f4,f1ef2473,a50b795b,a6375c72,df67d15,f310b5ca,9706b5eb) +,S(ec637ec6,bea71803,2bae7631,9227ae58,9581d3a7,131323ec,35f0d9fa,27a3e285,c12b830f,f273fe72,4f3bfdef,4279ce4a,8ae6d0a6,de5cbad2,b0a7b4ab,643c79db) +,S(33c8d6fc,d2b37103,2426910,6b9adb76,6c76f788,9627a9,6807572,e0af0f84,b3f01db0,7ed82f45,252d494c,af735e6d,3934ff78,c89c57e6,6c42a663,e5d6a792) +,S(35ee32e5,f7204f0d,d48b1a80,cea0a418,1d71205e,c373827b,2afc25eb,e3ab7e20,d57bc815,684adacc,756291c6,87d0eab1,63067976,a7c3a2a1,98ef1d8e,bad12935) +,S(97362395,200d807e,623826e8,3bdfa37b,e98793fd,6d7057f6,370720b7,5089a6b0,c908e9de,cd1d54a,172973b9,1706fc6c,c3367d75,2216c2d8,1085dffa,5627f06b) +,S(bd37e01,eb944b32,b379b195,16256ad7,4061760e,4eb44361,5e8e5fd4,34eae9a,dadb545e,2a613e54,6067cb6c,179fbdf6,d94b7b8b,c799adef,391b2957,2b93f57) +,S(b332b57a,4f070d20,35986e86,d82cc6f,7180293,53bd360f,d3f883fd,68e9c5ef,eebd3a05,933526ab,ba9e63f8,cb24ffa8,550e50d6,8cc7a4bf,215c551,ab89832b) +,S(fe5bf27f,9459e640,7489bb32,2012d1d1,69d40635,74dc05bd,6bdd6c38,cffb61a3,72c6318a,3775b8fc,5c790d6e,31bbfddc,7ee07a71,93e5f137,e3c3e098,bae25da9) +,S(f27cbe27,ed93086b,517c5859,9929310c,ad0e05fd,482f1fc3,af041852,c63a2a23,cce74e61,4e14bd09,8f11674f,23e30be6,b3276f4c,f85dedea,b3cbfa49,5a716e4) +,S(b328535,1a1b22cc,51b7a590,3582dc27,518bbfa7,410dc36c,e63da663,daa8693a,1b260487,7d3ad6e3,db75a0b5,d6b89df,df507809,c203b4c4,cb0712ce,ae2e3d2f) +,S(bb47bb09,70dae4b5,4f4c3aa4,ad4c966,ae42691e,ae39f1ea,a6711d6b,5f04b81e,2edfe19b,b52b7b0,2b4cf349,aeeeab0f,67524375,7f35b6e7,b920d343,e71561d2) +,S(fb6c9735,d4333142,5bf6ad83,50c7241d,b82e35a2,9cbafa37,d822304a,c1b3dd02,946a7ec7,1e0e6dd,a4d4c315,2a6e7489,8671dcb6,8a219781,3e2491a7,6a9e3357) +,S(c711f224,40cb6e74,a60509ec,23daa2d9,fbca44aa,9a093475,524e31e5,43575a3b,e4259800,352d0d79,c5e22d3e,2bf59f4d,e4b42484,68d15c47,ab5a9cbf,854294b2) +,S(9c748d45,a07e141c,639ef97e,8887ccc3,aa84bdc,ca0618ff,4f43b1bc,7496c83f,3dab775e,c3001657,e71a9d46,bf8cc120,9a74b9d5,1025f97c,4dbb9457,e932b5b4) +,S(9d5e6479,81a9278,6db60938,4a462621,23070938,d50e1bed,8df9474d,bee12513,30f2517c,6ff04eb8,9a6ef21,e4ff33b8,e14567ef,e38871bd,b9dba6d5,584c8b0e) +,S(4d24d6e8,91644ed6,ac1aa619,f9eb2b58,a9932c5b,3e8c82cb,589694d0,14b1d472,6ef1a21a,9dcba4d6,850e6593,3ee1a259,91203278,2f478fae,cda13d6e,5abdab22) +,S(fa226c4,36b389c4,50143041,da93c906,70258092,a8de85cb,d20a0dcd,284bc808,524d2992,655c77bb,59258257,5db3d663,a7a345f2,85572ff6,e9dd1dd3,21bebf6a) +,S(d4e659a8,e9af7543,2d9ec769,5dda0371,7bd79ff1,9ba73f07,ad0bf13a,45ad3b77,5f4a2de8,db9814f9,9b73ec63,6c77f847,1aa48356,ff67c8f8,3f0b6aaf,1ac7ba9e) +,S(157b6da0,1556b23f,b6c46b06,145727c0,21d6e573,782bcded,d594ae46,598df23,737d45d5,2c247718,e720fed0,56352768,ce111425,72730890,5a84a545,c53c560b) +,S(d97a6087,19c8572a,78b73938,5479e4bc,9a2b249e,a4f064a4,550ce127,fc4bd45e,f11e233a,75ba1651,144368b9,55bd4dd0,f4ba1f2d,2d0bce2e,82315e5b,78e0d76e) +,S(d2fdeec2,9b5f4ef6,374c2b87,57e4867b,d2e036d8,964a2ae,15632ca4,6f79a294,2d2c1502,a935f5b5,960abbda,9db42aea,c9f58f0a,bfaccddd,f8d59881,e2953872) +,S(fbc6a2b1,b3c0b039,9abc55fa,835bbeba,b0461db,41f3d9f3,160d0154,72861b3d,54421d5e,a5ed3abe,f22df1a7,26a2b5da,a298b41d,acc69bd5,a72952a2,13f928c4) +,S(7ac224b,cf7efd8d,b214db6,a6601d51,829d57d7,28c4c3cc,398ba57f,5079bf99,edf12fdb,9e281367,5e7df40b,1cc3c572,55213edd,c0fa07b,4e3f750d,36d6935c) +,S(f2bc4452,4af39f04,afb480c2,f7addb57,a04db86c,3691b26a,922b6ce5,a1724d9a,63c372e2,4a659127,ace8f54d,33969635,1d890dbf,7e2a31c4,251352ed,53c3a0bf) +,S(5ac48ffd,bf27b452,ca37dbdf,83f2fe6d,8f704040,f3a56180,ab991228,a74ad88c,c7ccab4a,e859e7bd,982310ff,2b7eb408,678b6f21,f52ebea8,41021f3d,fab6f091) +,S(daa3d659,a4defb4d,3291a16a,d669a43e,b6891e90,dbe0147f,568b650d,1213750a,af36d811,9ce1ff85,5dd637a3,bd475b10,50216093,aedf5f24,2c98da62,29bdcdbf) +,S(381d3791,30d4beaf,aed138d1,4072bbcc,b8d71d6,dbb13226,ff168f64,3bf55d37,fbfd7b62,3ce32a4,4335682c,d5ccd4b4,aa4430ce,d8fe3d34,9582bba,fedaf9c) +,S(757c7b23,ff6b6ecc,51ac84e6,26ed70ce,4e6e5c74,e4b65f3c,fd17c304,e19b1ccf,56848ab7,f814a047,2bd286f6,b386af5d,34b27c4e,a3ece71,f153dcc7,7e4027b8) +,S(ea498d11,ca35804c,d6103185,ae468bec,6c2f4f6d,ab025173,b1de4027,551b913,ee939bff,8d30bf5,9e3341e3,58acab90,17de8fb6,e2977847,f4322ceb,ca8829c6) +,S(9d119046,59ea5dff,30105d1b,5ee3f8dc,c9291bef,15844057,66e07d44,72ee1418,45355041,4d4715d,e8c087d4,f05b5f8b,a424ac3,49b3ceba,bf806b9c,e657e139) +,S(97bce776,90f8790f,e7d82b68,b7dc1b38,f3baded3,f7c67655,1894fc4f,cd1b834c,fcc18c2b,4f743ca0,e00f4b34,509fc237,c030b689,83451d5f,e7407b81,ed04f7b4) +,S(5339f056,abeb4926,f46dce7,3b8d735e,c6e9c4a3,2057c22a,49c86400,f15d7ed9,5be6a0b9,37e26dd9,f6db74a7,74fa10d2,cbae30f6,bdfafb,dfa2922c,58a8e37e) +,S(27f22d01,4dc5d3,162f39c9,7de3ef62,f2275b5e,cc6d5e32,6ab064ad,f762a446,6aebbb58,d671179,d712926e,d56cd280,8393fd85,dee45558,300a0f8a,a2986f09) +,S(94af2fff,61741cf2,5f158308,e7333e3f,678bf1ee,56adfd56,629312e2,2435f9d5,33a7269,79df6672,b13198f7,e674330,806f2dd9,50d8e82,1774f381,d9ec24d6) +,S(ae8a0e18,486273a5,36d526ee,3974031f,f3ed095b,a6835a68,2e943dc7,e19b37e4,23c2a51f,69df1826,9b21413e,505c0785,576fbc84,acbb2812,fda146c7,f5e8c8c) +,S(623fb527,d3d99023,9f720cbc,64478e0e,a0b84b28,5c0c3f3f,107b3e3d,7dc430aa,3ea48a8,d1eb543e,ade4a55a,f909e62,14e47d26,9471dbf,4d98a3e0,39c72849) +,S(1ff42295,d85e98be,f7aa84c4,47ec854,29f01b4e,c87371f2,b2025ba7,1e31a768,d9500f9,6e8c697,7ecc51d5,7bb7dfd1,3cc97017,58bb4196,b0b11d1b,3c24937) +,S(bc808b0b,67bedaeb,2e7a4bc0,d328efbb,1ff35dc3,b51716ca,3e501a3f,956aed3f,6af0de4b,26a6378e,6d86378d,d1f54243,929c37ee,d5c10bbd,86b80c3,ead8e545) +,S(e8ab92e0,17a9e956,354203b5,5d7b15fb,acc666a2,79158016,33a91865,b5bb46af,3e87cbd3,66926e66,3f5faa,426a9db9,811a9db4,de3a4bd8,79073b7f,c1e3197e) +,S(a7bf0061,c383040b,6e89ba95,b57102ae,2c291ad6,72bb5027,3c27fcd,f90613ec,79ac8ff2,4bbc79cf,431f4159,4852ad4e,2f0a9f86,ca72ac11,9d4e3fdf,8746539f) +,S(6841bcc7,22b705e2,759ba4e6,83d6b83a,19ee6b4a,375b38f4,5722474c,ca41894c,dac48021,5c168519,d147a966,451dd63,7838d236,5667803e,80658faa,1029a7e) +,S(d67e6d5e,a1606817,512f1db3,9033829f,69089a41,902189c5,20f0202b,8511479f,9cb4a747,7224f93a,8447152a,9fdf4c92,36a1b698,2e9196b5,a4355715,b9fd6ee9) +,S(61b3a7a0,53c477b9,e03d0a96,8ba38de0,b02e42df,162a13fe,8ad538c2,840eed06,94c07520,fa19457e,8d98fdbd,d9a9bf93,57642cd8,1b48fa58,b4b14a89,cb6142ce) +,S(cbd26d0e,a00f7dd,4b0b26ba,7358b2c7,587e233c,3a13c86a,d5022ecf,c865e4ba,50e832c9,d84ff55b,7ba79035,559bc889,53700622,59dec3cd,8b3eda3f,1f2b6e67) +,S(17a6f57e,d3d729d,c5ec018e,1b69394b,1ed78e59,6c7a5e31,bfe5dc43,f9c67e56,34f16cdb,c23e46e6,bda5db2,7538d7e1,f66751de,3eb05543,96c66a54,36bdfe9b) +,S(eaeb62d,93fa13e1,ff9d9a4,9eda40ce,dd6006e5,ced0e8b8,c02f7ab9,98f06f31,7e225365,b4b39204,b01c8890,4476c141,1c166f75,1726718,8ad917c4,9a719ad3) +,S(d09999b6,c35d5d71,55605a17,e7f82d3c,dd8d68ce,9be252e6,99493031,a39c7487,9165e761,8dfec3d7,cf733fcc,e548f672,362637aa,8687f47c,b1eb5c18,a780b061) +,S(2b4cf8d,7fb166d8,a6931bce,e67f580,870c61c7,635632a0,71166fc7,4a11c693,7974e2d5,164771d5,2e7a121e,2b015565,b3a874d,d6313721,ec69d3aa,b44d473e) +,S(d5c0045f,a411cdea,470d85a6,66aafa44,49ed682f,a1903c5b,c9594d19,5cbebea6,ffeebc77,5b24e736,8a8b46a3,64837466,85df5c8c,b40bf2b2,291f370e,94c6eca4) +,S(1eed08dd,d9569712,6c0a4039,c6aa5ae5,ab4aabe2,a88142c3,b9aa763c,632b5e38,d180547e,c4640035,ca86d925,10a70a64,5dcbf2b5,d3add4b0,3f6d0fba,5f4c2f8a) +,S(9591b04e,3de43104,3a6982e1,29a2e79f,f525888,846689f0,44e50b18,ce14413e,cc4cc696,73279c51,4aa4fd63,474050b1,930e627e,a1bd9d55,867d700e,fccf8155) +,S(690a17c1,b2dc7ff,520aecaa,380b19d6,d4a28669,83dc5df5,8ab2bfb7,6c7b0600,bdd8a858,e2d989ca,79fbfd46,60ffdc0b,e0231562,711b13d3,47cfd37b,7a53d4d) +,S(800a632a,aca6b505,f9fddc22,14efe53e,4e7ad2a1,a6c07d90,27d8109d,893851ab,bf242683,e0c1a24c,9220c6ab,deae8baa,12c6203c,a0080856,bc5e5ac6,4c3f3261) +,S(6dceb824,c48159d8,57a92334,4c72b4e5,cf2005c5,6fadedaf,5a8b780d,3ceeeab7,e6b961f7,6162ab6a,d44f3fb4,5aefc843,bf0d714a,1a50d13c,68036f42,30a1ad10) +,S(3ef5eff1,2b76432c,4bdfd1f8,23bdfdee,f60b5d0f,5d0b4051,2a3e22bb,665f2b0,8741e43e,9f78066e,d57d317b,d50252fb,11d8d235,3c2c9d57,76191c21,8798b0a9) +,S(4ce8d33f,a53f2e1d,aa696e9a,2de694df,2192db5f,8e6fe88e,b5d51340,83791c17,a023ff40,69587bb9,49ebb15a,d047f141,9985b521,ae915f54,bebc6dbf,5320558f) +,S(da030efd,a3f6363,926abcd1,182f183c,43c4a6d8,8bcb0519,3d137827,6dcd3fbe,c14f27d3,2251248e,3c4b8613,481e7c48,f1493b6e,5c0716c7,8d5e3b97,876ccf3a) +,S(65c12034,87b729bc,3709759d,beabdd79,ee7f50d7,ed2cedfe,f2c2b7e9,97192f40,f440d69e,d59adfbe,e087ef70,9b75b95d,13ac0e06,20683d3c,6d49762,ddf68a01) +,S(82c052fd,dbd6aceb,1f429f21,eed34d44,f915e949,7190aa6a,d487bd6e,68d91c20,90dfd944,c7c20ec4,4ce35d2d,4036eb97,894f5b93,6bef62a0,f2f65082,b0f7678d) +,S(43e2f20e,cab47065,e71abd8b,aa511163,e17e3efd,3e17d790,ba924de5,ab402255,6b1ff657,e37b65e0,bf2c5e60,47e36c8a,2b7ba965,79bdc711,c3ae5935,d2c47bed) +,S(27f177cc,6722e5a7,70200e2e,a9198bb9,f227251f,8efa1988,ac7342b0,985a3853,a76767a5,3743db5a,1062ccb,64452ad1,606d5065,9df8c0a5,64f049f7,764d6a99) +,S(6b1ce20d,d15cf54e,59aec7fa,c1919791,c40c926c,55801801,75c09ac0,dcce0147,c90a76f2,627d2510,8b89df25,23f069a3,6b102201,c2fb7e9c,932ae678,4a28906e) +,S(f9df6109,27b5ff70,d55d2a46,ebc6a734,df165bab,3bd27398,24a60727,5410f62b,4da454cf,7a381056,581fb115,d36161a4,4beeef74,26724d,d4f5500e,3a6c1612) +,S(16bd87e8,2f87778f,6b3f1da1,a04bc048,3b14de01,420b0074,81aab07d,ad806cbd,13a50b5d,886047b7,6898c85d,cd1bfb4,87642f95,8304d54d,12c03b25,9429170b) +,S(f35175c6,2244f0c3,ddb104fa,3ae57557,424c2d0e,f6a20246,e2fa48d3,2908dae7,dfc1ef36,8ffb4e8,2076ae27,7788444b,33880ee5,b6d5bd0a,9a2ba79f,30c4d1ff) +,S(6a9ec43a,b8a6cb70,465ba668,5e15fe2f,9e714c0e,31f1f227,186d95ad,2faeccd9,ee752bfe,4da4feb5,e4c0266a,57929e56,62483704,6e927e73,9edec593,8d538708) +,S(92e3b0b1,3df6dea4,371601ba,3d92f507,2388a23e,302edf74,6da973b4,613d2292,9442966a,dff82878,f6e0b8d9,9e56f99c,68eea310,79b365cc,5f057eed,ec38496a) +,S(35427db7,f03afb34,d13ba232,420e5d9c,4a8d16c7,924a2043,a217fbd,4287230b,5cfc1908,108fa47c,95ff8e02,634351d2,523dd990,aee1700c,7cda57c1,b106a734) +,S(a61be939,d3a33b8f,65b8fb5b,897598a7,ee25e71,5797014c,fdb087f1,90895825,fa6b7a9c,f2a4a71a,857bb126,44fdcf27,c9f4ccf2,7bf6a0d0,dcf8179e,af1a3396) +,S(659868b8,2a9422d7,1da877f8,59956376,b999dc3a,657278f0,89e1b7,a3014222,9ecb6c01,fb97f691,92402a76,13f7932,d8a82252,e6b97c66,5c727d9d,e6b2ac62) +,S(12667366,d27bbe3a,d75f00c7,b28fcf20,86e9caf6,e8b5615e,eee0f180,526602ec,dbac0f9c,32895bf1,21c90190,d5bfea81,84289a4f,a0cf498c,3a3084db,1d4a833f) +,S(74af3ac2,60461898,c502229a,8eb4e7e0,962c8f38,897e43f,6e421ca3,3240b7db,72f03ec2,7c24abdc,dbc9af25,41e10f51,5e470c7b,19ab9680,24eecce0,954c84cb) +,S(9aefb23,d88c5745,da9606e7,ce81d4f4,5a39c8d0,4f5d5f07,62653594,d3a34732,f797796e,5c1c0a56,9886af6b,fd8a8299,29ae1897,ca43314b,cb6ed78c,6c0b0572) +,S(93a3da8b,1f686078,1849ca06,24c2cec1,796f64f8,4f46da9d,34766eb9,dfc1f018,e5c50d0,fe7d2f53,fe1053a2,985d2b15,25125981,30de3cce,2ebf300b,4b944c0) +,S(ed8d18c3,f526a062,bd6095cc,249b7d10,ee09d072,435218be,f2ab6c90,c5ed66a6,42084909,f4bfb751,5ec6da47,2cba7678,7870a9fd,bfc86fba,669e31ff,233a72a2) +,S(dba838ae,b57fad4a,6ecfae8a,acb50123,decb0f60,b702a9cf,7af7d78d,814845df,c3213cec,8f225c2b,fc02519f,dce1f67,5ad59b20,91381f0e,feef93b0,3c6536b8) +,S(f1a033b8,64caff57,21857094,e1038fec,7879765a,3b977a4a,c6616162,68c1d8fe,9592b8b9,e4cc6a7,ce173980,e68fc405,88a83788,a08c6027,8623ef61,ba81b652) +,S(53967cf9,7058ad43,1222e9a,46caeba2,d8fa447a,6b0295ab,f40b8aa1,af1f0097,e8e86c76,cef5299a,9a15a6d3,4985411f,2a71b8fc,99f7a76e,230052fd,d5309017) +,S(2477d3a2,bc3fd88,6daba319,8c6fa6c9,5206bc1e,955e9d4a,c6e5916e,5883eae3,baddd174,ce70e285,1e7ed788,cb81cb7a,3402276e,d94e6e72,f20789bc,ec4f5257) +,S(b483c05e,22824454,52f44e9e,287bc21a,634018f7,babd1252,d7a6bc70,24d9cdc7,5d7cd9d,23ad04b3,3a7ccdb0,38676342,5b2338bf,48d4f910,853e9100,e1f3aadb) +,S(6ae41ce9,6c8f0a33,674f74a4,587db25f,7c368fa8,83abe414,a3f6bf13,840d46f8,d52f2dc7,d4fd791b,36e6448f,5c8f746,759e68ba,994de0d6,48c6b1b3,58476871) +,S(bd1a4d61,e19ce013,d06740be,c3ad88fe,bc3b2391,45563be4,41768f01,45f4c450,88c18e77,c5313848,bd19bf27,374deffb,1f40e479,9687791e,675af225,1141fda3) +,S(1d2fccee,47b4553b,215dd2c7,f26412d6,73319507,43136454,35426bc1,29b358d9,93e52147,c13791f6,aceab8dc,95df1637,83d985d8,3a4426d2,f825d126,e7b8c564) +,S(322cdebe,5aef9ab,957b355c,e8e4fa7c,23f23f83,bf311785,f0fc40be,ce0f7453,2eb8c80e,561e419e,f898f772,12eccab6,e6c4ffc2,5daf67b8,47853a6e,8475d19e) +,S(c7474f42,5491fe9d,51a9e00a,a42bc188,50d4fa36,c19d9869,6939962e,61e207b9,8dd394ad,799a69c8,da550741,7352e7a7,92a941d2,10b3f65,f634cdc3,1f42a875) +,S(ac33b339,1bddf66a,6d04c62c,da618bc4,1ad217c9,7d37526a,38d12e28,9fa9dd30,602267b,59fe438f,ac57d14a,8dd3451f,acde0be6,c8984b58,7cfc1b24,34ed9688) +,S(158e2a30,36f71701,2020f476,dd869974,66f157a2,27548e65,688ae79c,3bf8141b,fac16be0,6033e61,d34d1d,1e2c95ca,6109dfa3,64232edc,8f8f62d7,19ce6303) +,S(88384c6,42d1bdcb,af5b472c,2f7a54b2,11ae4947,433d5d35,add21374,33ec4b78,3c3a484d,5b8793ae,203f90bb,476b4d25,773b268a,8e89dfb5,dcb17576,166b3e7c) +,S(21e3c1d6,32299e3b,5da75f6e,7ffa2958,e5d686c2,6322491a,2597dd44,f6973d2,9866fc69,7b9399b7,4307b1ea,e27eda12,590bb138,b22485b9,de6e3cfd,8d23bba6) +,S(abc484c2,6d06ec8,bba4170f,727055de,2611145c,8e4a33ea,56507b60,3477d319,4f92a638,17f6b635,d2d944a6,b9194d40,c4ab7041,2a8ccf05,e2fee038,58d87592) +,S(7a98d6d0,202f4230,a12fe9d2,e321bc55,a3a3b35c,92443731,cc8112f0,579b08fa,bad00e1b,578de9d3,73f0ea77,85502035,c5a04c98,8904923b,f0059a3a,a8ec6285) +,S(282e401f,57ef51d9,f9a10959,d272e0da,e9898f32,d2a3e059,4a65c901,cb1ae10f,6bb8b543,e9461164,a66d55a0,adc51ada,4ac311be,c6625276,5b2c1639,c803037b) +,S(5259a2f8,3dacac1c,18a9a64,d9834c82,e862169c,7a1b9d24,2b521c4a,d7ec4274,196fac52,458c7d1c,3dfae8b8,2ca130ba,99d43a40,8bf41da7,d2e9d040,291f165b) +,S(cbc4fdda,9ed1707b,59db2171,38e9bc1d,eda499c0,78f22c28,4309e184,cf906081,2929df0b,24074d42,75e8a861,8f064466,9ca0dd45,752c7d5c,8cf32661,f8a1ebe8) +,S(96b294a7,ed8d4916,21f10b04,3c2a5ad8,88b92c76,3c93f375,f6d31d99,64b9cef8,f5502e33,f1416c8,77cf06f2,5752998d,a30ed434,365b8239,617ab3e1,65948a96) +,S(287e1f0a,a510c8f6,273362d4,ff8d96f0,dd27113a,b45d0bf0,1af5d5,d24f5038,4b5ddc1d,6c9c4423,1f27ebde,bf4afa29,df337f18,2a2559b0,33de6830,cd7b9e14) +,S(eed0e323,3475ee14,5160d4b7,49210f47,c58d7b26,95dc8036,eae0e6aa,91a545bf,22407aa5,430aa089,3f7218f,2ca23ea0,16eb04b3,3c967e6c,d4c68ad7,16e4378d) +,S(1417cc87,72c932b2,b1116b4d,ab4e86a6,e7d80fc7,b082a6a4,c148c50d,177299a8,66e06245,56f09207,ab2d944d,167590d6,44afa2b7,b598b92b,65706271,d7b50e30) +,S(af6029b8,93c549c7,3544e849,b01ff2d7,b991fb98,2b8a3baa,b9f1b8ed,2b24bc89,dea2973,a97bcb6d,3257121e,9233956d,43ea9155,8105559d,ae17db09,fbc14f2c) +,S(dbb5544d,d28d2510,8a91dfa5,9a3c7538,1b4173c5,eecc8e18,b5065fd8,fa4fa570,4f84feee,755215e9,15abfbb,ff46eb6,9148efbf,39f69261,a9d2a345,3aaf1bd2) +,S(50ee1b42,be9598e6,3fcb0467,18786633,18b550ec,16645f70,869b231b,674a1878,8f4fafc5,fb1e714d,f52c0a10,55016f9f,3ca57c9e,48fca138,68464b8d,70c6b2fe) +,S(77b88613,4bcf9ab7,a1a6e0bc,82137906,d5c24607,a6d4812b,a6d1c8d9,55a5669f,2ffe0de,c5426e8c,92fc247f,a024f1e2,86668e7e,d7a24c33,2c887946,4cbfdfcf) +,S(2bf83837,74a9b9d4,7acfa6cb,6010fd44,620f51c4,e076d5b7,e798edba,21d7fb8c,5f336533,5203a820,a9f75f58,61bf2d0b,fcad07ae,9e297433,79d22b3e,a75f40f7) +,S(207b1218,9a6c2653,69f66738,b2e61d3a,c43cfedc,bd9f94fa,2b883991,ae69e350,c43b15c8,c08b4592,70c887dd,d15cbe95,d627a3e1,4a1c7afe,da2b0c69,549649c2) +,S(5ec76ba9,63dbe3e1,d054ad89,7a00c7af,f60f043,d2472f6,d3369e87,45637cbc,b66d383a,c4ec579b,8350a1b0,b2751fdb,f2ab329f,6ad63cdb,d6d46e35,944009ae) +,S(d010ddeb,3959bd8a,7a7ac178,ca573326,b578e940,b46f5808,12398722,42e13c94,2343d4e2,a380fc9f,617ed8b0,903b722b,a707c53d,a2140021,403ac617,d71fe37) +,S(49157637,c5c47498,5590bdd0,e4a8cdb9,f9bfd422,99812950,9ed25a49,7dad0da4,2564d49e,5c53c818,770c8a36,6e92532b,59fb90ef,e7ff4dba,c6baedc6,b792eefa) +,S(7e39cef2,8285f9ad,1b5ea712,78720954,f4933546,bcd7f9a,b4c7bcc7,54147a9f,6bbe4096,c4c107d1,c361befc,75916c87,6a2a0da5,dde637bf,1f125a21,5408e78c) +,S(45457311,84171f1d,aa931d3c,16b9926,59357877,84a4655c,54822c9c,1685761a,a13710ad,597e7da8,f4b12c17,9afdc5e0,5fc4f05b,e204567,9314149,9c73b421) +,S(c0bbe693,23e256fc,f69f0427,74c59091,287bc0be,82f0b5a5,6ea8f957,6d647d8b,c22981fc,6a015c20,525a0bf7,a9ba8ac6,dab6256d,d763614e,ea250c5f,fb433231) +,S(bb224b39,ff7d27f8,1ba86e92,a6897776,85074fa5,48b5515,987df332,15dca58,73bbcc60,c3adbb94,651cb73b,b311f690,6995d611,8b8238c0,4519de94,1e6bd47a) +,S(94489617,8b4bb641,9cdce312,5d863395,7ea54002,7f759e39,a8846551,ad3a0866,4a16c110,7730cd80,55fb7ddf,93c6deab,e3282da3,8a35d79b,3238e8ee,c8917935) +,S(9ae267ae,a964e8de,68398609,d6351619,4019778b,2341696c,7e44f446,dc8fac38,6d175dce,e589920,851c02cc,3fe3dfee,e9398e53,7742249c,e2745713,fee236c3) +,S(9e237d91,a6ce503a,65dafb47,a9fc5f7f,6dbcf3cb,b026ea73,68403187,d95afd67,64d491e6,df79eadc,74c198f6,fbdc481a,330d030f,851c7fc9,63974047,ddf2d12f) +,S(a5c5e0d4,920f0e3e,6973b71b,90ae2b32,2fa16960,b850eefa,1e516b4c,37252d6c,1aae183c,52ba9f14,f06c57dc,b483ba6f,740d6bfa,7ebb72e8,266895c,e6f6551a) +,S(a6d0cd83,9a198c9f,62bbefc4,b91b696b,57a4f876,6a16fac8,1131a9b1,1b36bc1,97981c96,9f758543,2c810b09,460c07f8,8520e78,3e68529d,7a1d3bbb,e0a86e85) +,S(4a75be64,16d6a87d,49e393c8,a26467b9,4b43887f,a90a25bb,139626e8,58b122b6,ace3e92a,c693f8d4,210932c3,89d4275d,75a46c6f,a9a76358,39c26288,1b8f83ba) +,S(1559a339,7f1e1195,51d8b4c0,fa53b5af,c693ab8f,843e6b00,1a6fc435,5967d7ee,8e450f28,78b2e9ea,829e965f,ae4484be,7e98f39b,a7b0ecaa,b777914b,848f324c) +,S(33565a48,44d9b25c,109eb9f2,9546fd5a,3f30d4a6,157a4e69,6029420a,21f74deb,c8141513,c4e370e2,400b8df6,208e867d,9bcacdd,d87cd975,8f91913,3ba97557) +,S(b49c8814,23db909f,c68c73ee,3995a7db,89e919f2,dc697855,357053a5,7ce26988,65878108,879801bb,ae412aff,72e83051,bb13c415,60499b94,8e0831d3,c2e11fb7) +,S(3a9635b0,b82585d2,37068f72,76291c4f,cd3d211f,80fa7256,58e28dfe,920e3e93,a1711c74,ba4e1a1f,f0a9b6d2,12c8b995,7b6c8c0f,f71da246,154f13f4,719f0713) +,S(ce6dc56e,6f5c496,71a900c1,ae531e3a,a8246877,98044497,fc543013,2572bff8,1af98ef7,3703f8a7,6fc88e9e,c9134aa8,a9e24326,314bf4d8,2a0c85ed,af291f8) +,S(a945a79f,1627e79c,9ce1cc5b,79ad02b7,c07263a9,11f8f78a,ad314049,92bea11f,f41aa315,4049b7c8,c9bef95,f3462336,ccfadf0e,16c1f129,26a5c9e3,7a443bf5) +,S(3debe447,a5f793d4,210e1607,642abdc3,c3a8d446,5a553a45,d73ede0f,20819dae,c0463782,dc5383b4,f7f81557,efddb90d,30138670,858c4197,5a47e6a5,565e339e) +,S(dba32245,a37b5636,9d7120fb,a99880a5,aa590299,601c6cff,d33238a0,395f8d2f,d589ab1f,ff442551,12aa1283,3deb9b43,947059bc,9cb1b32,4d8a29e1,1da82b2e) +,S(9c52bd5d,1fd61e1f,8e75f23f,f6e8baea,a8b7b5e1,5e780870,ea9f8dea,2b75cd56,e6d0a620,c116a7d2,2b2f797c,da7119be,556cd834,caab1007,e9a8ac02,f8945859) +,S(1ec28a0f,24e8910a,85f3c583,415afad6,dc5e10be,cc600749,8cbb40ea,b80fd383,c616ac82,bc6aa8b7,1a3cf89c,b82d7e9d,ddb95761,dd64da13,290f6bd4,a4ff907c) +,S(4fcefc4f,134d223b,994c1274,bd39655,878a35fc,2f50e405,ce11c76,1a6833c6,50c62036,fa49f62f,ab1781da,ec17a13c,4ec82c57,7993fd20,c5c56d70,145c2a54) +,S(85481ce2,90bb32a6,eb0fcfb6,9cb9fdda,580d8808,5b09611d,2d8747f6,ca657e20,ad2f9361,85aea05b,6a0d20db,9b6620ee,ee718c2b,2ae1810,42f6bf1a,2b3a2453) +,S(11aa1424,7760f682,3c11cf4f,12c013f9,7b1c9eaa,5ff0a504,334d2e41,f5daebfe,dda53fb7,b09620c8,fbcdf9b6,605710aa,f807dad5,c7ee4e9f,c7dc9c34,23302646) +,S(908c4f4a,15c58b2c,a0512eb6,b07c67d6,5b08b99c,13d22358,a52bbe24,b3823dc0,5e708db1,d4b37459,e33db06e,21a45fa6,5dd5c959,22ee7b60,7162a77,d89fc674) +,S(d192c334,65630164,2edf01aa,d974fb7d,af13ce8b,2c8975c6,ddde145c,1a2784c9,96bf7877,ced69d6a,a71f0908,86e2dc9c,f6529297,a2f8500d,d683b6a7,ebe4504b) +,S(8fa0f7a1,97138152,75f6c886,245902ef,b6e57515,c28732fe,d49b7f57,c19c6b9b,c09b428a,e54fe6d2,effdb332,4b1fc1e1,11aaf4dc,965b72c5,f97a8a97,18fdb68b) +,S(e53249d5,4d80b59,31d40779,4c550ff5,d6cfc715,13e0f419,4d7b42e0,62e74681,face5c4e,f404cf9a,275846b,11339448,d9133e1b,470489ba,64c190e0,b73735d0) +,S(9a1c943e,7f7178dc,c4453de2,d578c4e6,ab80d538,70c03001,a7a4d5d6,fabca0de,6ae6fb2a,1a7d87c0,99f6eef5,dea6a53b,a296824b,d8ab0848,b44d1216,c2c0789c) +,S(f0d4f862,dd60a74b,e06c9478,1b14b9db,af5847f0,baa00a97,72b61857,bb3d192c,6f48f413,90f9cdb9,edffe12,859b82ff,f8f7b32b,b655f3af,f28175c0,8f1efd8) +,S(1f474fc9,f6ee1699,7a7f37b5,f10f1d02,f2445e12,ae17324e,cd31d2e,c6fb8e1,cbb396e0,4b754b24,3dd587a7,d71eb673,c41b0fb6,329013a6,8ba95dff,c6d0d9c4) +,S(dd871b68,1cf78706,61bd7b4d,1ab259a2,1636cedf,39ac276d,5d36f086,fc9ad4d3,89456fa8,cce6295e,56cd18f5,9d9b83a6,4e4983ca,b55d425d,730726ef,12958dc7) +,S(5223e3b8,3809df92,8db9f4fc,d2b81408,96ed1b89,e6480fe,8a6b815b,bc7a33a5,95b640ed,128da08e,a10148d9,6f53b9ce,e53077e4,64eac3ca,1f604225,75b12182) +,S(65f26a8c,88fed562,12f11676,e2f237d,b4c500e4,a4ee0715,ae2e332d,74c48815,78df921c,545b864d,7ebe6c36,bedd987c,51ac9d73,795b3c8a,2a125d2f,2fb8cbd2) +,S(b62376f4,bd858d69,dc5d2757,b67dfdf7,1d3f770f,e96a0dce,3c61fcad,ed0e2586,c58d5af0,d669832f,4ff38f98,298e9240,c6202343,4e829b0d,145093ac,9ef023f0) +,S(a3297102,6001a6ab,ac0e84c,bfc4c829,4c607d6,c2147833,b1f726,752ddf07,b2c820e9,35502858,eea3b657,c15173fb,d1d45454,3c4fa17a,591a20d1,802d5c65) +,S(3b6e214,94773849,46056d4a,dec9e5c6,86f9873d,76accfa4,b4a377ec,84081339,d7e8ac79,e03d1cd1,7c83ad60,6a140b26,dfe67ed1,254a46e2,9c527cfd,c6afdf96) +,S(ff6aaeab,11cd4c80,ea69c3c,a10f09a4,40b3e51,abf56ee7,2c337414,f3220264,71d8a65d,7a605e01,852f0fcb,1bccfef9,147007f5,7c6b83d0,ef127b0,fe90bdf9) +,S(a0800062,64942e,b9bc70b9,f40bcc7b,52c0d43a,b4d608b4,ff65d73d,75fd4a87,de229122,80c1d66b,364fc250,5b3020dd,1482e43e,c4e67ad2,f2e49ca7,df9659b7) +,S(7c2b8b50,7e2256da,15afa166,84dcb7e7,c437b925,6c042450,6025a551,cafadd5c,747b5b36,e247939,7dd719aa,6ce55c0a,218312b7,26adf84d,eb7e3820,57a0b375) +,S(631c89e9,ee8a3192,c593ef08,baf3fc1e,d30a47e4,6a4ee9b7,e1c6cf5f,d6a0a5e5,8d49bbec,85a5245d,ecf6b084,fc65c697,190f8220,cbb84525,a344263c,ae10e2af) +,S(7ee8cbf4,a0b12cb9,d598835,bcf13fed,72e441e3,bc4b515,4182cf7b,f2818b3c,ea3b208e,70542467,a1e09248,8f34b44c,3821f0dc,169c6037,a7299ecb,25111774) +,S(69c085e5,86fad3f9,7b94d5b4,63b937be,5a95905f,eb33b25e,11ef9e8d,6b5ab3c4,5badd881,779a04f1,3a7333f2,dfcd0fb3,6167d168,b86b6a8,27640559,6e5510c2) +,S(951c09f5,563288a4,e25284a5,a694aebc,1e4b3d45,db0f0ef5,86ce87ed,237aa05f,a0c2704a,fddd2f24,462ac715,cca72894,7de034a9,2c958111,3f55c58d,83911c24) +,S(ba30b632,6ad18aae,6d87a1c4,6af749e4,6d639106,76f25f8a,673d0249,5e813e42,6c174bd6,3def5d0,2ce342b,1d0f895c,ef792f42,a97768b0,d091a92b,14f5192e) +,S(53a1d69b,77ee7d09,f9530860,31e93038,42daf062,c1ec9f19,1328b346,559fea0a,baea6eed,b33605f7,4bdac4e,749650a7,256bf215,737d1081,2c805ed5,298de5b6) +,S(23635f56,f8122da0,91d7e3a0,1125b99,a0f87e98,a717dbe1,f6d9528e,82f9b4ae,1a326517,904ad591,43f0f733,8c98f11c,2ea0fc9b,54e9eeb8,c41e469d,ef0ed297) +,S(c574946f,79574d9a,32c588bb,5cd8fee5,dc60403e,81b11fad,cafc69be,67c0eb67,d2598c1a,8568e4bd,a477d9d9,909b083d,f55a7116,9ccddf5b,666a8b61,96e2f73c) +,S(966bbcbb,4d397eb1,91ea2ecc,871fb24,b0c47469,33df7b03,124408fe,583db143,11741ab6,a38217c,e55bba9f,1c7f41cb,2e69e54a,8a78cf02,60bd3d2b,22789b8f) +,S(6ebbecdf,69e6e137,ad70cfd1,741ed404,4b3620ff,15a07f8a,769edc8f,c1aa1667,2ac87d70,baa106f3,a3490b56,a5960f64,fd80160,d0c14d64,ca1d40c5,f244c57) +,S(815f7f47,8609fee,733b7239,b272afdd,89c2f984,5da8186f,4ee8be2e,2e3e73ce,a972e06b,388be908,86c1e324,e1d2b953,18e99ef3,c0120906,f1508732,f839b930) +,S(24799358,53364be7,4675e3e3,c141e64a,febf3a04,386fa41e,f19696bc,5d3253bb,d6233c69,ec254367,673bedc7,1319641b,f9c029a7,2523f8ba,39dc375a,df245804) +,S(db32eded,dc803ff,d5a1f3e7,8437efbe,8c098fd8,e05f2aaa,c03e0daf,50ba5622,30ceea35,22ba7bbf,2ffe9c1f,bb817fb0,748efb45,65d01eda,9f9cff5c,b5afdad9) +,S(c5fb9cd8,8408cd00,33592de0,e602ad35,74e873be,4442d0bf,fe3bf837,51601689,a604a6eb,85233ccf,ef57fe7f,8a73c430,10e94b8c,4189225b,fe97637f,e7b97935) +,S(7b74932c,71dc26be,429f53eb,efc29905,bdf088a3,f2cf4bb,9828b701,11798163,e07d2133,c628a82e,4a22f58d,710b7d88,892cb999,2f921c28,3772310,c72909ff) +,S(308a025,4f659ac0,7c4e0850,738a2f16,df89ca15,fd64b35f,8f968413,b00b2819,4b4758f4,1eab6491,a8b50fc5,17f7767b,add40ea1,7b1272d4,1b1adf09,5d33c990) +,S(26a78f16,95b595f,63ff1b4c,cd52acca,ae650319,e67554b3,a77f85a3,637fa8ec,3db38db3,ca1e5dec,c73f2192,c4af6ff,66842a38,c76d62c7,c5958fcb,67f38097) +,S(bc440e62,f8524e7b,aef0af7,38e60a61,b213ab6e,713fa9c7,66d28785,891f15ec,175add64,8d472b3f,1547970c,6de2a57b,d2d3833d,2e847810,253b3712,f4d740aa) +,S(f2300d54,ebe04aeb,7a9409c4,3f2bce46,72d9937f,cf7e2a86,c4e8d55a,ef2a4e26,59e0e44b,9e090bfa,5291f436,6a3a5336,a56dbc96,65ae1aeb,e71e8c7d,e2fe77af) +,S(4d34ac85,c64d7d4d,97d40331,3fc19118,ddf31d19,18196594,d2f7a08c,db951c1a,551b6581,e6a1949a,4bbc211f,30fea215,5da8389,a69f6681,96f947e5,f7c8f7f4) +,S(53c3e6f0,fa2795a,adab1a45,a0f30f00,5f0310e,2f989d7f,d1f4504e,d03f2c04,12b382e9,b867a8c8,55461627,b7996676,880784b0,19690280,e709e91d,93048a32) +,S(259b0a65,79eced6b,2dcaa804,ff86122a,24e0d338,165a3013,9ab59f30,343f9ed3,27195af8,5217e109,33e2e1ba,57ebe333,9910c84,9b4e8f6c,af5700bf,93395635) +,S(fe07b39c,f9bd07e7,980b45c2,bd791e14,450eadeb,f6c41206,e73c101d,51093908,a8e47a5e,7a7cd98f,4f5e0afb,b5cccf03,56491271,c85fe0e4,9ae12558,721c66fe) +,S(7f38bd5a,87e2ebd4,526bfa35,5ced4f47,87c404c7,74e7f481,85d40d10,894210a1,4073ed4,cc499ca9,e7f8ff5c,f91f9c4b,782b6e6,f6225edd,d6f69fa8,b0b7fdd2) +,S(8796a424,9a251ecc,c0b3b44a,37edd05b,e34efda1,20926343,3a099e7b,8ae40a82,c02ec680,2860ecd8,f6ac62e2,845e8470,ff60cc55,10bf0179,49f95517,c1dd25d5) +,S(cadcc04b,9d4d8e5,22c153bb,def50fc,941cf94f,bb3b254,61422a4,7e6891b0,4ea1718a,53d64935,ff01f0c3,37ed2b7e,a31f9a15,edbbb60d,bceef362,8a454259) +,S(f2e614fb,346faa61,ec752d79,f9518b1f,e8f535bf,75496cca,513fa961,77f571a,1831227a,b5a35867,82b30fe4,720d2467,68f8c358,8472100e,fea76526,4581fa0) +,S(cb10686b,d361e64e,d82d6aab,5373f62d,b165d3a1,30bc7022,cb3f37ab,d61adff1,d969d37d,9c3db8d4,2c60a24f,44abdf65,b19fc6f7,56a452d6,7ef1b099,613019d2) +,S(6e903855,15042422,cc16d104,962352f0,2e86847c,f816468d,5754c70,fbc6d3cd,cc49058,80b7c15f,6f718ff0,ec2d8544,62e636c4,3e20dbdc,ee318a87,2d52d9fa) +,S(2c0a40ca,38f70b73,ef4da636,185c8260,e50643ff,9b5a83a0,fc0d410f,5526ac12,3f1eda92,8c32cf57,64d768ef,7a6afeb3,89dba8b7,e5bbe5d5,29b50410,5136ab15) +,S(1da89240,4f7ac31,136097f3,ed3055ea,d700aac5,a87ad4c5,49246211,f0ab0d0,e20f1baa,6ca3514e,ed4ceba5,9afe1875,c717597a,b7dd3c37,6d638e,287bd49b) +,S(5113fec4,172e4cf4,4eb27c6e,932e1d93,f16b3ec5,8c791b3c,e22c9bb3,634f76cb,7b129bae,b450023b,ebf81c01,bcd486a9,b494884f,b005b2e9,48eceeae,a6911760) +,S(a8d236b7,bff53a1a,fe23dd5d,63433dd0,a267e05f,31bce172,f80c5d65,c24373c9,698101e4,33e8a6cc,45b07552,7aa4ffae,3714324f,d5da0ff6,4fbc7123,773dde9a) +,S(79b2cfbc,b5a0c79b,d2ba680f,a1b594ef,4b2869da,3baba7b2,81cbf373,9d6f89b6,df45bb38,a21f4838,9e9eec46,f34eb6a9,78967e6c,5d181a92,e989e275,45756aaf) +,S(ba0eca95,4ec16133,1245f4fe,d70b725b,97393053,84fb962b,d1a0a014,cc403bdd,7bb5626d,2ee7d5fb,ad9cb623,7ba48440,26cbc27d,651811f3,92682622,2df76258) +,S(69609368,c01a1920,d3f62310,311cb7d9,cfca4659,1f53fc31,1f0af251,1f99fcc7,73ee24fd,32b08bef,f4254bf4,51614b2b,c0269e59,c6caf25b,bedb4919,76186894) +,S(c0b4d69b,d2344ab6,df9d8427,d480296f,5e0333a6,4a447302,5eac698b,e916f676,817dd44c,49b24aaf,627620c,ff7bb843,a84c7eb3,cc7e77c4,f65ba06a,14abd07a) +,S(80fd0f21,435d42a3,5ba377d4,e2401afc,dec30b21,db770277,fbab9841,3c5d9f9c,502f305a,a3350f4f,50d418d4,9b19768e,4976020d,8e158f2a,6cada628,e79953fe) +,S(7e18fb26,a881189,e3862fe0,9006e1a5,e07aa6b,39a5ef66,4f52547e,1cd7ebcb,dac9006,d99e8a47,d3c5539a,508f3f41,c649d475,4a53fc43,2dace107,1112ca84) +,S(a5b8503e,3f7c067e,628e5f5a,e65a0891,74385de9,69049aaa,26df1906,ed85af56,e9cc7458,fda4c95b,6b502498,95f4bdfa,ad9ed7d,346609ee,6549fa16,6e98dd1d) +,S(d93f866,450f7693,689bc9a4,644b52a9,3d006ced,d284332,4dac7fc0,bc6ff548,94c7526a,14b30b8c,b5a2b33f,c6508dce,fa141bbe,b6aba116,cf3cc169,ca094b08) +,S(424ec330,e4f5dff4,31ce33ed,1ef8293b,b8cdd070,12b5b18a,67b3f4c4,d766bae3,94f29933,fc5ee8a9,27295c4d,8d28b3a3,67c60d6b,13247616,a4dcab96,b58e2dd0) +,S(58805d5b,10cf81e5,b114c372,69dd5dff,cdf7e153,370a8305,bcba9d3b,7ebfe952,15e7b2cc,3ab0ab9e,a36df4bd,ce66bf72,8c45e295,d4a6c94b,e366f86a,ab39df5d) +,S(dab0c80c,a05aa70a,e626f81d,7b85c06f,b418ca69,8067b9fd,83cc6e0d,6a3949e8,4c728c4c,527f9839,498aff2b,91e8a344,aedb11cf,7ab9486c,ab3c7014,3dde8e3) +,S(55b54261,cd493b2d,7dd46f1c,78ad3983,787a37e7,ad8092d8,5cd6a55a,b870e09b,bee36be0,1168a67c,13faf147,6a614b6c,bf7b149b,4600c93e,4ec199e0,9f7fc63a) +,S(ea4f2da7,a5cec770,d986ba06,b1f18ec2,b3907a44,47fa2bef,4c9fbc10,d92afd4e,18f9565a,41583c3b,f7015986,ca7c94c7,4222b81a,9ac26eec,e7954baf,cb470072) +,S(5d835d26,54e489c3,d726f54,bc06ee3f,11390023,fb8d16,61f76b2c,29cfb78e,83091f39,9d1bfaf5,216add5f,dd4d60a3,469aaf82,ca6bf5e4,1070c03f,792dcb2e) +,S(f5efa7d1,484fe717,428e9f54,88c12db1,c775073e,27d028bf,7def8c3f,4f558774,1139d67,793faccc,70c7f48f,c892c314,988d4767,b9d6fa2a,2f2b632f,367453a4) +,S(132950c1,6f24a24f,a907cc59,704692ed,b6a5d254,79dea8d8,38cd71a2,a5d6542,b1e87d14,db8725f4,e2c2522e,c2f3f290,11d664f0,e6e85368,6c142c3,bfefd736) +,S(e479b0c5,478e49b2,c9e211c0,8e610b4c,1f1917fe,5960ba35,9af83c9b,4640d05,66ab2af5,3ffbfca,fdceb064,452c8ab4,b112ed68,8bafed44,1b66ef80,92144364) +,S(41b8288c,19cfecba,562b30e4,93a4c796,dc3adc21,f0c7a9d1,6adf4459,375edc5a,3ae93bec,6f438174,b3956d57,d9dcf29a,d673f6ca,74c96120,c22018dc,fea42398) +,S(56696d31,6d3cb87a,a3eb312e,1cd41768,2d0183f3,b91f605b,c4389d59,d86052e4,5e96c041,e495e90e,2812c09e,81d431c4,eca7274a,2204cb,a3a00a0e,650c6b21) +,S(ea36425,bc35c1f2,f0d96a49,ce4f4446,72848c5b,ec47d7df,6ac5cc30,d3968ebe,b00381cd,a5c19740,a266e63d,d59fea3b,d3d3d3eb,c899bf9b,da8090f8,1eea766b) +,S(84463c57,b01dd5c9,b98e0e4,38e221af,58c0e077,20540b7,da0b4d89,b3c36502,deb3a24f,9a0305e8,812fbaa2,f81e5fea,fd24c375,6a529c3c,c2bf4aa3,8d87b30b) +,S(13a22d5b,d23428f9,912df3be,eb82f4b,411fb39b,1604fa7d,4db63862,293c2310,99a19b77,c6912900,1047d70b,7e84ed80,a8427709,67384cbc,da11a158,6ddfb1db) +,S(de017231,52bdadcd,21b028b,4cee806b,89e98728,f91784d3,a418d7b1,22aaeb57,7ac0a9cf,15ff4a0c,f7d8a0e9,d2fe1469,1a08d526,7e3475a9,381be3bc,64771e65) +,S(1fc63549,9d462327,afc13d0c,eecddb5e,e99bb085,55076d88,7c064e8e,e40396dc,841f309,e47fb335,d2691c43,248a151b,426f1205,ccba170c,7c644615,54258a55) +,S(323e5acb,f821f131,7ff1dddc,822cba0a,b3b2380c,ea2f1b76,903c906d,83491dda,afc25ce3,b13cad24,6f6d9a2f,71e97d1e,e31bd2b5,967a5cc,7ebb1caa,adc7ee) +,S(57b1ec40,5c187317,ff9e70dd,2fd0eca8,cf2b94f0,3d7737ac,dd1468ab,61f56384,247e6f6d,7e2e70ab,a8d50794,36b1f6a0,b27865ac,791d1f08,c334013a,4ed93386) +,S(d6f5f80f,80586e87,5cd322f5,7836327e,225aceb5,6d8a83b1,6cf3bd12,4b581074,e925199,b597c7a0,a179cf34,519ca3a,f35b6d9b,24d76118,ace70f99,f1932979) +,S(2fef6c2f,eaa8c4e7,1cd31179,e0bfaa85,95d26471,fc36be60,1df75cd9,94e73c02,eedc9575,5bbca159,2271bcdf,d9ec8bc3,466e4b77,36d28abd,e6d68ff9,b9820f06) +,S(e488b041,358bf98b,11cd31a7,77a7d93d,e9cb3a61,c49fbafc,4bdabcdd,7b895964,755147d5,ba8f1d44,4ed0eeb3,d072c11d,f448c132,b9e06f4d,66f92ab3,7114a1b5) +,S(ba2591b6,35e7747f,20553589,11e7165e,618b6150,222a70ab,afa66bc9,a931cf35,3c60585,b16b1d07,7bd578f1,85e76bf6,77ff788e,a9b2caee,6c64ef9b,143ef487) +,S(ff4b12b3,cb0ec8d1,750c648d,6ed2695e,a41f8e82,58ba8b16,bf4727ba,6120ccf4,e2cbc52b,f0031efa,74c1fc8d,25466888,41c695d2,393e19e7,287bb34f,afb689ec) +,S(9e42cb1c,aaac0c62,47e4028d,501c84ba,6279b39,dd17424,73c2cd10,9bd36063,c9d0d646,381ca19d,528d2ff2,b04aaefd,9f8095a6,8811f8e3,e85b5ff1,5a5da5f) +,S(610e8f14,eb180ac5,ee435530,812941e3,ed7dad5f,c7bc7924,10f296e2,449fe005,67632a19,b1567d77,b0b5f013,77c6a05e,9261ca50,d010340a,968d8507,877fd4d4) +,S(f4c4230,443f3324,7be7bd5e,5cc12a0,27f0ff23,ede41c44,beacedd0,64789752,55d7b7c6,866f6bfd,29a410e9,8d5d5e21,98719d60,93e2bafa,697a8b5f,734d8bb8) +,S(66221ff,1d344797,1f308b89,a26e993e,c4d1c05,2c2fa436,3729c9aa,8c9dad0d,c0c78a0f,be8ba04a,932a7d6d,604f0fac,4ee01452,fef801d,36d3ca93,c4b7a965) +,S(c3ea4e8d,f8ac15b2,1dd251f9,7ebd19ca,2d91e97e,b13085fa,98294915,6aecfcf3,a7286bd0,3bc8f0bb,a3d28b6f,c513c4d0,472225b7,aab70f62,18c1a735,b31f1547) +,S(762ea616,8b3056b9,da318ef7,53ae0cb4,ff57eb15,8fff58ec,7e4fca2c,b605a49b,195d761e,9caa7603,ff1b5ab9,85d4bdb9,d128ba93,33d6cd94,f00c62f2,9452c8e1) +,S(50cbfa1c,f9a07c2b,42783077,8cae738c,3c3a0d5,617b8c6b,81914720,25712a6a,40a37fbf,97da5646,a653ec25,eeef583e,15cc1db,5bee9ebf,addf9706,cb14e689) +,S(bb8b806c,6798d001,f0d6b31d,1b17cf31,7a5422d4,3a906a79,294e5243,e22ae3ba,46b763df,b7a7a967,75b6b3ac,d21cfa37,a187ab39,99506c99,31623acb,841d4dd3) +,S(5491fa1d,88f0c9f3,6d87e074,331a9a6d,c17a5f63,b1e3326,500a5bba,1f0081d2,6c206f15,7dfb29d0,3b0e5878,57d1abb3,5f18b260,45c58da,87e8c128,7f699a83) +,S(c9d2726f,5ce823a3,ce02ee66,262d0673,f04ee272,dbc16cc0,27068850,cf472435,ff62f77e,60de3ae1,52c7f943,c3eed295,fe6a7aaf,8e6ba0df,bb60a8a2,b6081aef) +,S(a0d934b4,68151a34,cea5ff0b,d002dfb0,ec51df7d,ca5afd35,56acd53e,39515256,2f27f413,ac43711d,fc69e5af,785546f5,8e0da1d0,c9e89271,c8933b21,5998a999) +,S(342b7a8d,a3664884,1d8198dc,5e17b669,f4e602bd,5dfb28f0,95b7751e,4ce8602e,cf36fd4b,24cc36a2,806f8926,db6951a4,4c545076,70ee674e,8e2fefb7,e33f65f0) +,S(1f0993db,1084fdf2,6b2fbcfc,cf0c8a24,124c2474,92acbcb9,6221b788,18542b87,fbf92437,f6feeffa,4e04267a,c8dc57d0,c6d521b0,2631c20f,876fd9f8,b00793a8) +,S(f8f4034b,d7fcc9b4,256433c0,c2b40f9b,2f45a117,3a371707,f6ebec47,e456d9aa,cd895dbe,d97ab95c,b3c98512,c46a538f,385b9b26,5438f7f9,8caa245c,c680f54c) +,S(c80f3bbe,d409d5a8,cc0502ec,d7d95d8b,69067e61,fd2e8f7d,45f06ba6,7db89a33,dc803aae,6b936dc7,27773f6e,17c5d2d,af87a1f9,464320c5,4fd0cdd6,d7ef5c1a) +,S(b19fd3c7,8ee32a3e,dba4d6db,bb1464b8,5b3a5865,7f0c06f8,31af5b39,de2b436e,242c72a1,91f17614,8ee25c12,e0f8d4ad,52837a48,9b94b16c,27c8053,43c8e523) +,S(c88c52f8,a658160c,b10614bf,c744f668,ac46b774,94592a1b,99562a6e,e6482099,5a2f3a43,bfa6873e,7a198ad5,4fc0e3cb,2047f268,6396231a,a158a5eb,f4aa3170) +,S(b5f02d99,8f8f1c5a,8c8995b0,6879c804,1dca6f17,3666613c,efdbb82,820164e0,8e39ef2,207f5d31,2e36ce55,3569d2d7,2204a9f2,500ccff8,50bb67d0,a0802585) +,S(4d4e728b,5da0713c,18fdde5b,290d24ae,f7150d42,f9e5625e,f05bd17b,124f8302,be413caa,b7c77064,91110243,a20545f,8f9e5357,49ce98d5,cdb41384,5471bb5c) +,S(92832d03,32f7ebae,b67701ca,a0a2bfc2,b36b6403,742f4ff5,ff2ae46c,242dd226,cacf8955,92bc2fd2,90dc5b5f,b4185159,3e4a6a0,709a51f7,32e8c554,7cdc4114) +,S(2807a982,b4fc3fc0,8908fc5e,b3734a83,b2e2035d,8c6e3036,b16f9ac2,f4bba58a,ddbb9f8,3a9a6ae0,9945ebfa,9cb7e7a8,49798032,8545379,df582f93,72cea69e) +,S(e755ddd9,139cc4fc,b266477d,645b4efb,b02088da,752d10af,aee5ff8a,45df9659,fa7daf3d,605846c6,35e2534d,746873ab,cd502ee4,27e62c45,ef66401b,c816beae) +,S(93c33dc5,152a9bf8,31c3cf7e,ea833ca0,485f852d,52a255f3,55d7e67a,c1964f63,4a3a36df,13c8cde5,48feb589,dc56eb16,9a874272,c7a0becd,d40554db,cf4d8f63) +,S(70d66e2c,8f934356,d6720d9b,4809b984,6c7ab1c4,f8604990,d2e559b,a59c24c,24e8b668,4ee0062d,7bd79b3a,c96e0316,dcbffe2c,973ff6bf,e99590fa,192603e8) +,S(c165111e,fc2d7105,f6bde2aa,7f682287,2d803fa2,ec904380,27195cc,147904d2,c71184de,f408b79f,32086d4b,2b0e5d95,aed6e0e0,eb99bfb2,7e917abb,8d3a5e74) +,S(12144a63,67cc355e,e75486,357b0c79,ff4fe703,57a2f89f,3e3f7e20,ef860204,f2bb05e6,e55171c,c839fdb1,2b49ba11,dfd53af1,7a5a9602,4327f93,940b8491) +,S(3179dfa8,bd134725,3988033e,70f806de,fde9c6c7,aa4a6d43,a5cf110d,6fda7828,3133a35a,ffd1f24f,579fb314,2e72590e,475e931e,8766d462,842972cd,70e49344) +,S(51fe3e70,e5f21b21,1cae97a9,d0fa49bd,166dac17,5087ac3,a93219bf,d17ee77f,f3309396,5677c691,2a987cc1,5b3f016a,4efc22c7,6f89f562,4830f09,a9fc9bd1) +,S(2d7c98c,a224ebd2,a5167017,88018bf2,1f29444b,6eeead79,dfd9e503,7fe1680e,abae552b,bbeb09d9,dc82af6a,eea16d50,613c9314,be23eb79,d254a16c,decdc02a) +,S(c356823a,37dc335a,918a5553,bb0dfb9c,704ba647,d6cef22c,1e71bc9b,dedaa333,d8715c0c,a0ebf8be,d79ea3d0,85a70d2b,b40efecf,7dded60e,13d0577,cc235e3d) +,S(91af0d4a,56a75616,e9cb1351,4ca27ef1,b7411d9e,5991be14,c1658450,71a2b3f,f6c845a8,76657a63,ed37bd33,46ca60ef,a8fd151c,24a9e35c,5893f969,2580e26) +,S(6a32ae36,93ee1e7b,77fe8418,7266e6c5,cb41c7a7,37351cea,987a4f80,a26cbe00,2816504,dc3efca2,67e4bba2,5d4c9dbb,e8622e99,4a712503,37320925,ed5f64db) +,S(5851d327,5e18fb4d,79292e6e,2bd6bf29,b6301e08,7b418ac1,43afdf70,7d880901,1e17a9c3,dfa8c3e6,c1e9c34a,785321a6,e58d8024,c61cd02a,8b83ad41,a2366211) +,S(233a98a8,9fec3399,f74d99cd,7d5d4894,1be8274d,8f7ecfa1,f4f3693a,fb26a654,e9d5a9ee,fa5e6cbf,9ffe590,bd7db1fb,b9e08125,93f42238,65298e9,eb42d7fd) +,S(8751bf4a,9676e2f9,5036c3ea,97749d8f,f452f67,cd1259c8,4c39e8de,9cf796c9,4f883d63,bfb0a01b,e26b31b,53ed8755,49ed6c1e,4b67a160,7fb68be1,7b845711) +,S(a21126cf,7330697d,714be16f,27986c9d,cca4a0f0,3a12b6a,d54ef31e,d7a3dc23,3b0e984f,565d9825,2c523913,d2d90a26,b47973a4,8d8669f6,8dcb4c62,399b9777) +,S(8123c441,96d17ce1,c6c1a9d6,2aaecfb4,cdde74b9,317599ea,450e62f1,5639c3e4,406c585b,c9aee3ea,dedc117e,d58b1c3f,db5846cb,6eb11592,469bf958,6872b068) +,S(6f8e1a45,c18ebfce,2e2f710,efbd7ffb,4f107a32,fa6a46a4,caaf8ee2,37a22805,b36a7c4d,712315f9,a89b5ac2,8d1a5d55,5ead81,d7c43b8,8155eee2,b3ba8d3b) +,S(51db2e94,7e75c9d5,4ea07ea3,35942071,5987090d,b81c3b99,40b85684,2b0f0bcf,70967acd,454e6ce8,ad015f45,e0578940,c95dd818,70268d0b,9782a595,9a1462d4) +,S(84e0b0fd,b16e03c2,cae53675,4eb3452c,86ef07a1,ba278abc,d5aeed37,f71a3151,5a8e435b,53a4b51e,2ea659b7,1f3371ea,dfb28579,8b062eda,300664b6,d3b204ed) +,S(ced68408,c2b01401,fc039059,5140ab68,14142b7e,d69a64bb,e6ec5d85,be833367,76ce2478,28d2581f,6779f723,327fa83c,8cb10451,fc34625b,d069885,3cfad07c) +,S(8d860f70,a32921af,6376c310,bf5093e0,572e9b3,44e9204d,f5487554,c4f5484c,6dd24078,cb9f8ea0,eeaa4f4,3f3cf99c,890966c,5da8fe0f,9cd70128,88907cfa) +,S(6223ee77,4e56b9e3,da6e008a,eb3ba06a,40bafa1e,84383ff5,65f98286,d9095d37,ebcec7ec,f750cde,9ff7972d,172340c3,c324d843,f31db269,c3f35a62,c5f15f36) +,S(326943a5,5e7b7ca6,951a78dd,afd00e10,c8ca7f6f,3c01a038,ffc4fc7a,20fe2c63,b3bc5a8b,c87ba023,ee22bb4d,3f92cd81,787f2af2,4ecaebe2,67fe0a86,21c5d201) +,S(f69b5d4a,eb0b550f,851f601c,92698789,f9f94821,47a0ade8,c8a19fe0,5742731f,2a74e8bd,94469645,c8a328b4,24975dc4,93adc83e,4baddba,6c41e10,3b58e90c) +,S(f69a5a88,b8f13bef,a987194d,6b00a79b,7e576749,3d180fe0,32a1868a,26d853ba,6eb589cc,9c51900e,a6d99110,206f60dd,cbdc368f,5ae8dd48,cea05218,3415cbfa) +,S(c00ffc9d,8a57cfcf,9624a812,dd2181f7,195f459c,3b858a19,864393bb,1d1a269e,3097e10a,d6abfc0a,3fa039fe,952034e7,6bda4fc5,c5614092,b124ee3b,bf55f87a) +,S(9c021c49,d6671103,cfddcc3d,8e54848a,addfefa1,d6de5455,e5bda021,126dd183,4fa1ad18,81362f4f,f0587b52,a7a1b483,2d14127,434ffc4,c5bb90e4,fce50834) +,S(59aae0a2,923bc36a,1a5bc8cb,ea5bbbc8,b34f1504,4671a046,eb3090ed,3f4b2345,659f89e0,faa1577e,36175240,2cf1736,a18b070e,4d4b0fae,9ed08297,362cb246) +,S(7ba1ee89,479254b1,f32e9e53,517f0961,79d17d81,3ab3666a,75d4bf7a,7113f252,6530b40b,7e66a084,97168778,61cffd96,5eb71e62,e8b0ad7a,7d2851aa,15a26dd8) +,S(4b0e5728,cef356a5,c6d08616,47c268b4,c8a12d8e,89c2da7b,78e9db87,7b606719,235ec085,a1f3651,11b5f03d,7237dcf3,d70e851a,6dab8446,10e45fa,2996e70b) +,S(54285c70,315d9a43,a040bd05,70c35713,68504efb,fd103098,79067d6e,765d4499,de233654,e7c05b5e,f144d25c,6d539157,51e83ce,7f88dedb,7fbef11f,d6acadb6) +,S(ae15f764,d20e4eba,b95c3463,402a2159,fcd21daf,fa831b3c,e47bec08,9bd43da8,474c4cb8,afeba9e9,3f1d0a2d,28911298,939328e,5e364d44,68c64cf4,fec2eb78) +,S(6de8743a,68c42b81,ad069f2b,fc1482c0,204640f7,74515924,441ebd9c,917e3974,7ed602bc,16300648,86ed0369,21936ee,aa5e9764,ffc294e0,bcded12b,bcc86f55) +,S(7e4951,dfae38e6,f9869fd9,6d058847,2a38b0f8,827052f8,46195e21,44213a47,6fa2e60,a3883383,f8efebc,f39329a3,b7aec31,2958510a,dbdd5a72,728035be) +,S(1f7eb5f4,47d53537,3d29770d,5d7ed36c,a2d2a4a5,550d8bc1,849382fe,3847df55,5a351374,da5a804e,3b995d43,49560f7b,aa417dab,936860c5,baa252cd,1753f25f) +,S(dcc1272e,b31560ec,64a4b53d,24727bde,da576493,b571b5b0,aa7bb3ad,e8b2dfb6,567e08e7,bde60e6d,a614f321,a9cdba4,1b860f41,387b3854,603a8740,f93039b2) +,S(3f4e219f,5014414a,3c757cd,28120043,3cc199b,fa843c40,b344b32b,87dc7e7d,dba2099d,839e9c1e,7d7d4c0c,9444e956,168e2bc,4df1dc52,bb5b4da6,3a8283f3) +,S(dac6f264,74fee94e,dfbe9de,cb2fb88e,272ff622,4d7f8b86,3076337d,6d37acec,1bfc6855,5af0f07a,c9329ed7,5549560e,8d3e0c67,753d2673,eb6706b5,27a0de10) +,S(82ac1fb6,5a975de1,4bbd5709,9e2574d5,ebf883c7,7f38a7c6,1408dd3d,5e49b33f,a6d9dde8,b43656b7,9ab5d880,f3af2ca8,b70da7dc,251a7299,cb236b1d,75276c56) +,S(19a59ade,a0da1340,36a533b8,ff202a58,55b9182f,b19e8b79,caea80b0,9e263706,58030bb4,b17da5b,1879a67a,12c1d929,88cee2a5,d8915528,26456762,9a99f256) +,S(27e8a699,eca192b6,7a66b1be,5b290e37,1ff8e912,5afef253,134af2c6,ae709ead,54ae6059,58d036ec,959d789a,5316c70,a09db19b,179e5d4f,effbb057,abd87eeb) +,S(25e6cdbe,5d7e37ea,fe2b51f1,95cac707,a2aaa70,57fd5e78,8fc926cf,76e65466,576b2c22,cb6ba6bd,6acf0eae,79b52ee4,1fdebde,25293347,d624973f,e9b66be1) +,S(a0114d87,cc03bfbf,97e96821,2b69c53f,12910c87,e87e7a79,109f83c1,7042dbb0,869c33a4,cec89d65,633bbe60,a3cf7b70,1e5c83eb,e7c229ea,a4c279a0,4203a9f9) +,S(e8017e4e,806557e4,61ea997,f5f46e72,9fb668d1,e8faacc3,5804c35b,6d5cbad3,eca21b4c,294ed18d,d0df0dbb,771b8bd1,659c2eae,2c843f8b,bfd3aad,f44a4070) +,S(948209cc,528dc6d6,a406c996,7d7c191,19ab5142,679ac9ba,afc5098d,4d7c83e,3cc37cf5,1aecaa1d,ef14a2bd,cb6a1084,f3e60da0,9fdc4fb0,9d9f9fa5,84f14b25) +,S(10f4d6e1,c141dc53,fecfbdb0,b39e3e8a,c7f5279b,dc231fdb,55b25551,df90fca0,4f5e59b0,9c6daad5,80adde67,1583af4d,fae40d52,6017d5b2,9e798bca,6917e735) +,S(66361888,5d081bf3,9e9fc4bf,a0922dbf,dbbe8eae,451c199f,9aa844f0,71a327e4,f31b98f6,48940706,13a086df,12416779,a5c16ad3,7cf885d,5f1fdb37,1be94cce) +,S(6afc24bb,ae6cbfca,b34fd467,f23a31a4,4a0a35f6,203dd7b,a881ee5f,f340db5,7ab76f2f,6bada7ac,1e01c930,90c741fd,90c41fa6,943aa804,ff960b17,3ab28cf8) +,S(3551d572,2eff7d0b,275e310,d1466a70,2b828d8c,fec039aa,ce629aa1,2a9c23a6,b58af71e,1f9a6cce,c48d1fd1,18902a21,7f1e7f35,3ee9d794,8133d41e,2d2d61eb) +,S(e6e8cceb,f6096fcb,8f21cd9e,d428260d,c3867e0d,9c4cacc8,b8900fff,7bd4c1aa,d1b12068,f85e64f,7c9de5ca,f1490bd8,6dabf18b,4b78b5ef,531d9bd4,f64f13a8) +,S(5cd74a22,d46e7769,15b3c84a,492fad11,8790534e,dd8d9bb1,e117a6a4,78f566cf,397b2429,f978404f,ec96a4b5,28db5a,1b0cf9ef,61f2fc7d,470f0554,eed5ee08) +,S(abeeb656,151fc7a0,fdfca1ae,fef91a7,ae156db7,c433178f,be6fd700,991e8b86,e2a10216,3a8bdd0e,8254f459,b573d182,eb55f2b4,969a3f80,267b060,9f208a8c) +,S(3f9702ac,3b96bd4,ac9412cc,8f40d5c,99f4d7b9,d211bd2b,18c6f96a,ebd2f958,e077e28d,8f171626,9488fa5b,67a54800,b0d5a519,4384ca56,f9836b87,68e9821a) +,S(3c8e54a0,d094d63d,60aecab8,675349e9,f229995,587cefde,eaa1f50,cf3032ac,e77aa94a,fd252860,3bff0189,f11d5f93,fb26fe86,dec719b2,f8bbeda1,3cf38ba8) +,S(8e88168f,41689167,ead8ca79,5ef784c9,8e3cdc41,43a0ff8f,2891d476,98387bee,fb4bf21f,343fc6eb,ca6fe337,ccb7a7a6,899bb29f,d2f60480,ffb0a187,e875b00e) +,S(d5c7df6f,31001177,f2e88aa5,43276617,7a598335,cf6ad98a,e55a6d66,1b75c1ec,2edb20b5,47b7c233,23d5c4e3,295921d9,9d98854,e780f4c1,e756021d,79f1b035) +,S(262b9c58,75087b02,490291f3,963160c,c1f5a3a0,fd6a905c,25c98bb9,f7268514,c7e4ec06,b4cc77a4,ee551a5f,6c90ea97,4a0b2193,f1b828c,305a0fc1,b22e5b81) +,S(cc94ea5b,a4fdaa06,93976b76,893dbd92,f602bcce,822ff271,7a13bc2d,6b31e7b1,c81c01ad,f2ff92f,5c2cc42,f3c30091,11e6901d,36c9af9f,9b408df5,c7460932) +,S(b21abb23,4c435197,5b73471e,3b2eebe2,f537b95d,1b19361d,31ca4e63,336b0066,db02da36,73e62e9d,e96e1bc7,b79ef4ed,49449c29,7f4f0330,227dabdd,5b4c3d9e) +,S(ed130512,5af211bb,28118dc7,fc22b8e,8bc2554a,a19a8c7,16f69f11,a03df1dc,fa146b3c,2e0eab2,bba69407,9ee3b6f4,2f64dab3,9c19a1a4,256283f5,bc0886ec) +,S(42b829a,96989e82,37e48200,20eca1d9,61e487d,a9c6f9ee,60586dad,8fcd0e83,c2e164f0,29b6cab0,14fe76ad,82ee7372,78124cf6,5f251c9c,c260e239,50ec4014) +,S(15f09133,fe57a223,e2c9ad79,dadbbc7d,ec85df2c,6af39fd4,a156eb0b,1602a13c,563206ac,b190bc2f,c7b24d2e,ff376cb8,64102efa,12f7be73,84898cc8,76a12b4f) +,S(b8446387,4262c03e,312eb0cb,e562f460,8cd4f65a,75b099d0,b45e0de7,8398f5bd,da730acf,79fa0282,a3a29b99,7a0ea45,ba0896e7,598be8fb,8298aaa6,9b94640d) +,S(6a926c00,ef4c32f2,52d9f336,29fc708b,e21f571f,1f320ae8,6e6c46b8,9511fb95,9106e0c8,2eb9fa36,4a84a2e8,8eadc9aa,9113927d,99adeb61,a2ac5527,5de9a9ba) +,S(fd6cbc21,9ec03dff,8f3d00e6,9de0c64d,30ca5a04,1556d0d5,87339b0a,925d064,742ce8ed,a4b47eeb,29883606,211c453f,fc15ae40,174e1c74,656a78ee,f3a85e8e) +,S(6fd7f1b7,61116218,68e1038f,d5f341,c43b61bc,b6ec53c2,8d84b321,ac664274,f681256b,947a9492,5b8402e4,26ef8d53,20c65a29,3a7758de,6b7e3f45,a9d635d) +,S(c28c2cff,1e418550,f73fb839,58c1ca6d,4c616c3,74a67978,d4dd71c1,99902ac5,537d774,83b1ca9,50174ee,381023de,cc210f91,3a66615c,b242829e,e3285285) +,S(89458f8b,7db2cd74,346b8b74,759088,fbf81591,5fa1e97a,3f34ff90,908cb183,9b5b21fa,378f50f,dff2e276,353179a,c0a6e43b,13373940,c2e73e20,106742bd) +,S(e66ed94e,793b894,552b3893,88a657d8,55ccbb2d,f9ae0061,2cc07244,bfd434a3,8a6a17f5,da95e907,7b03192b,eba324a3,5595b6d0,660851a4,7f9e2259,2f093275) +,S(8c7e4ce,f5dca16e,620d6690,ce3b9337,8000ca08,e73eac21,deb5deae,83359160,29a44639,f9a6b8ef,990b72c1,5ca6f539,31000a2f,394b59c9,36542992,7959c505) +,S(a0d1611a,807750a2,45c68132,2f7adab1,9b81de3,809a9a82,ac038294,b732bdb,c6e35357,246fec0b,ed0c5491,24f13f9a,a01fe39d,e7630357,110a5878,90d9bf95) +,S(c1cc130c,474f5ebf,e4b4e26e,a444d5d,7542609a,2bc65a68,9c6453f7,9dcf6ab5,deaba85c,f0f7f9fa,cac2415d,9f82c62e,9caf457e,fc9149fb,55bd8f2c,32f74ea7) +,S(d6dd2a36,763b77e6,259f1305,43c8262b,784a268e,45adcd26,efbd6d54,afeabd68,2987d048,9549cdd4,c27222a1,9562b005,56dac521,1e67d052,e76266bf,fbab179e) +,S(885cf8e4,ee0498f,29a7f3b6,a2420629,4a941f56,f3d852e1,4641a604,9415c49d,b1002bf3,e34ce44d,54a18be5,3737d98b,28ad0f7f,31d9bc37,ce31279c,fc039b2e) +,S(8f0c57d0,3d85979d,5bec7346,1f0a3b7d,540fd0c3,8d8a6775,fde2ca35,ec8a0d91,86a3f4a2,cc4f934a,8b833cfb,dbb96eaf,159e00ee,caa0871b,235e4f8f,97bf542f) +,S(53456583,d2f4d9fe,1a13883b,7775b363,3993c9ce,6bca547f,d05021a2,d36cd366,92cec0bd,cd83312c,455690fd,d715e41e,c29cc70d,f5eb2e17,7f0b2caa,f3b5214b) +,S(a5ac3a28,fad5cda9,200bef2c,454b73ee,bbc853a3,e8b2ff77,111ea25e,5f3b5359,4951843f,6e23b9ba,60f326b5,d6b13f1f,f49e8b3d,b399be4e,46cb3ab5,7ef10bec) +,S(2ec53b3e,6d91835e,d236898f,8857a64c,4d898098,9af6ba60,392b774,6eb99ad,e21f4abb,cc05952,eec3fe34,f02d64c7,e7a91658,b0b11b14,118c3489,7bb2a2ee) +,S(5863f940,a45c8f5e,a2361500,84c588b8,a7f99d26,84121d59,9073d38a,69494c38,db422c38,41a2bb32,98390c98,e9f4c23c,da27c64e,a5481870,b33a2bc,866852d4) +,S(3492772e,d177b4a2,5f9eed03,7094c52b,aa72dee7,1c5b315b,70948dcf,a1975a64,c5a26844,26418929,b5bd1488,49b3241b,d0eb5243,61106911,1fc32cdb,29087562) +,S(4405762c,ba4f63b4,2401ce63,120c3599,af37ac13,d2de044d,26d830,9e664a4b,3143da5a,8319bf4d,32941d58,e9bda807,74c58380,ad8c3a33,460bfd85,36d9baa2) +,S(4894f457,1324691,8f32111,e1bd94e8,a43f717d,74e8c465,550e864b,9ae2a16e,9c89818e,6f09fdc5,b36c65f7,12f26be6,40598712,fefda799,4b200967,7de01ea) +,S(620abc9d,4efdc1a,55edc41f,29834528,ca87b333,a8678e8c,205d6817,cb7ec3cc,f524cc99,9e911d67,dd0e8bd4,3e003ec1,64af288f,3135a602,4e93856f,8110f867) +,S(d69800fb,9fe61ca8,97f77226,d42749b,1274176e,29bb85a6,226f91e1,e8c62a7b,43baaafa,e10649d3,ff2bb0cf,10c58f8a,f01fd3f4,1a06c245,a4e9c483,8bc9e3f9) +,S(87a12e5e,d002cacf,f55901c0,d374f81c,ab9da24b,80e1fa9b,25f038c9,38344721,36586bc,17aaf4d2,5d02ae3e,e9f98235,f9d605fc,1954be4a,dbbcb917,1f736d72) +,S(a0af0d5c,bfdf4fa8,98a3ea89,867721d6,3178341b,ba30f091,946c72af,bb876624,100f795b,18dd85c0,9af012b2,5d7d5d1d,5118d02d,3837fabc,ceab0b23,99f98e43) +,S(ac94b41,7becf857,1248a94b,a853f4f9,dd47284,157e0b9b,1756c4ca,fb692086,cf94bf3d,d0c7f89f,acf8f678,5508e510,faf27a33,10915d99,80b9fcd9,62f75c92) +,S(649654d2,966fb2df,b917367,e2ea4034,fe886725,3711bc40,8520d5d,579a3481,bf8ac886,a7550c40,29279fe1,c4d330d0,89935427,d6967412,66d79ec1,f1a2780a) +,S(5f7776ad,e73826a7,fd2969a6,1511141f,cbf6fb11,23c8d7e4,3ff93835,ee1860c1,9faebe0,2140468d,4564f7c8,bfe4f097,dd0ba493,8656fb43,ea9f0f8d,22089e5b) +,S(f5dfe7be,f4c42f3c,e91fea18,10402c5a,9072b001,286a7371,35a00fd5,c71e2f17,9787d33f,821a0633,ab1982a4,f9240b53,38a5644a,4d203b1c,6b5cb212,3c837b4e) +,S(d3432817,3f33fa7,e517d6f0,408bfcb4,6a882c0a,6d78feb7,44f731e7,4e1bed23,bc1e6ff7,cbdaca91,edd419f2,e80e617c,506bf8d,8acd4548,8267938b,813a281) +,S(a4420846,95528dde,2c52d5ec,cd67266e,3024865a,c6e812e5,1ef4f780,505f9d2c,4a721caa,63d62b9a,c23ac717,7a27e27d,d54e5ff8,aa204bf7,3e6b3131,3ccc31ac) +,S(b3818c70,ea29c1bf,db63ba44,1e42c842,11f9d2e,4d6b07cc,79938d3b,b294b820,1b2c711,a98418b8,15abf79,44fa75b7,db8965c3,1779d4e6,1967f009,8c005d2a) +,S(3854b55b,def18a47,7e87f62e,d7607c6d,aa2e737d,e0e5015d,de7e90e4,570d43b0,2865de79,552c68ce,4841ad3a,69376a36,9d74d55a,98c35d25,f97656a2,fa22785b) +,S(e2e805af,78bf592,6a37fac8,7429f6ad,88c45522,e58e5c9,2bd2ddcb,e893ff33,c2206185,254ef80d,fcc7365f,1dee9a3a,cdac0ebc,d0ce6a5d,4c9a6875,f07a46f6) +,S(37510b86,df2d8e60,10d40e34,77af6c24,47f2a41a,fa35ed49,224bee62,eb0b77c1,1f30b802,6a3288d9,714b33b3,7a09cf20,cb754f09,730888d2,a09869ac,1e905ff9) +,S(14b2bcad,c5272c56,8930c574,d745fac7,d190558c,121df6e0,c2c9cc1a,f9662fcf,baaf56ed,f70fc0b6,520bea13,a8794d2,d397e935,65841028,6dafdc16,96caaf3b) +,S(2984937c,e3d6a155,7a185609,9e44bfb7,28dc6d0c,bd8c628a,23a383ca,3f38082a,637c5e4,9b0943d8,e09960fd,2375770c,5d4dc4b1,3ea460df,d5ba49ab,fd6339c0) +,S(e64f46da,d9b9e7ee,3d2878b2,f604728b,8203a591,d02e7d23,721fc436,165918c0,f7ec0e4c,9f66adf9,98b8e41a,d43fe3e4,7cb9be23,6356ef3,ef5b9a23,711e1140) +,S(3f587c9f,1b88ca37,d32af584,e510609e,ae113e54,8d07fbb9,a34f6916,908f686e,37712f53,41485518,241835aa,d31c45b6,f5370c6b,238380a4,ddbd0755,17b2b49e) +,S(8218250d,b1ee7484,b8892bdd,53be130b,4b5d3db4,7c3c9e36,5a17baac,5d8654a1,f19955f2,a7f954b9,2050c815,8e801222,8e77405f,c9f0676e,76ec2e4a,f3b93bf) +,S(5ad1a4a2,55a22c56,e8d17acf,786356f1,7ffd6453,262980f7,188879be,9e1eaac5,fec5d2b7,ac26e4f2,d4d7af01,b49295c3,3a969fd5,28e3aeb3,90e86bf9,85093dc4) +,S(1c92ee58,c5226166,c313b491,5c731d34,739068b8,f0241dde,6f0d7151,243304c6,5dff29a6,1197aab4,c1ac8a25,625c4dc1,11729680,15f03488,b8a4baaa,d541bcdc) +,S(fe622f9b,fd9c4ba8,8c80c82d,d1a07bac,c1c9da11,996240cd,50b6a41,f6f8fdc3,666e59ff,e9efb9a9,b7c36539,ac948438,f1de235b,fbed8fe7,3a084076,39f6b901) +,S(b2e8301d,ee34bbc1,d95de73f,b947bbd0,325bb3a5,555738a2,8fbe4e3,601a40d,6953963d,3496413c,53ef500,6def0fda,607d9a9a,319cb93e,22d9bfd,a0c3b807) +,S(1505e140,4644537b,56da7114,5efd34d1,566ee38f,dd4b549a,34ff312d,5cb9bb96,d6440284,c1ba972b,413f8c12,7fc40001,39954fd2,5ee4993b,4dcd5e3d,a2b843a6) +,S(7a524409,67918e47,53b451e5,243c6996,c053fce6,1f492995,8d2bb84,9fd1088d,e089d973,19788a2e,853a3067,c4a98c59,4fce75cc,a9ba908f,37780e1e,567b7284) +,S(69eff8da,8efa75c2,ab052d71,e898a975,c58a074f,c46fc6b3,2b385e41,93ff5a30,30e873ca,f0e7d1d,e58713db,cf7c5c9,6c9f068f,e0e3d1d6,c3863d44,8da1cca4) +,S(64a5649f,2da4eb15,1be3fb68,b1210cca,61b32492,60225ef5,53426992,95e6d6ec,8c53c12e,8fbd7c,811d4a85,95787105,4719c5fc,368845a1,6b8babba,1d92f56f) +,S(ca23d56f,46ce3458,d95a5b24,d998188c,3ed76a22,2034b337,933c4298,8c804fdb,efcbc7c7,d0d11fd3,ef1c0ac1,38d1a2bf,d0e94a85,a0700cb3,630dcaa,426a4c43) +,S(630b2d2e,62bf28a2,ebf80f2c,2bb10cc,82014ee0,c48e15a6,4cd691db,dfe8a55,908eba92,e889f132,4da0f927,9c1bfb74,dcf16786,c9c8a964,79757a79,d4443e9e) +,S(29b5c3d2,57674ebb,1cab5fd0,56534261,5a059eae,c8002aac,c000a857,a7b7c840,e7724381,f3f1cd4d,198a06aa,33b7fb9a,d6acdd57,45f900d7,be34fd6d,4f6dd41) +,S(f4b9c47a,2454985f,e732c151,3216f7aa,f5de769,89344c0a,4b8b46fa,c15508db,9c51095a,c45d901a,75229288,fe5d49c1,1dbab43b,9a030cc7,c6d71bf8,9d097378) +,S(af0dbf55,79f3f1f,91847143,99a09fff,75c8922b,48213c42,1cb17d11,2ab7ac71,56e7f5fe,e96e464d,50cf98b2,90a04917,430bc2c6,72a411a0,842149,8fe23d66) +,S(7ddbb2fb,f5d68e,6acf574f,1fba12e8,323e06d0,b6b80cb,1b9e9368,5543a8e9,888670aa,f057a051,1738d69d,162ac2f4,9074e5ae,d28cd6a3,a30edf9e,e874d7ed) +,S(43612257,2c25dce0,4a0273a8,a83890da,3a27848b,103d5ee9,1f0bb33f,ddf131c8,62cf1a80,7bdfeaf7,7b9d90bb,ec403b7a,b4261e04,62a16c58,5c9e1435,afb177d3) +,S(8691a20c,6df3b947,e173735b,d764ce9a,4a7773b4,4b4276a,873786a8,8e5b0932,974ca0c,6644983f,e4288afa,a80c03de,fa8d9215,c148a63f,1a1d4aef,365d9e81) +,S(7376518a,528c1190,b95cfdb,ef241485,c84f34d9,3690041a,e4d8b0db,295fe87,fcd56fa1,3d5cbebc,ea197636,ad3e7bb,f9faf472,56d44489,e7cbad3b,ca9e01e) +,S(9c5e491d,838b7471,87eeff8,f44ebd70,d6087ba7,fa2b957,8302e46a,3e5113ed,3e312d47,a32d61ee,f88dcb4d,aa38b024,7327ba59,cd377798,75bcd10c,2aa13278) +,S(884a9710,9e6e95f9,9a52d8a3,7b91480,d33a95e2,4f385ecb,d9666bc7,ddad4c63,f39408bf,4f27f2f4,ef65b195,b231edc6,c50aca83,60d23ff3,32b1c3e7,149e8d6a) +,S(f99760ec,771c8f39,d349fa7d,19b0ff65,bfd2ebbb,f2d4707,eff2d646,4beca7fd,d71a5750,a9737ff1,bdd2fbbb,9674905,5508c2eb,ad22aee3,6aff09f0,1fc9d996) +,S(f2cbe279,98214502,77f79ae5,22da1671,452c8e94,27562e3e,5d78fc17,9e758e2f,8334d592,9b50b714,a50ead97,6fcd8b12,f9806edc,5da45d8a,a226e029,282ab52d) +,S(df67bf7b,861d0636,30784bd7,f9a472df,6c2db3a4,7af8d06b,ea3665,cceb76fb,e939458e,513e55b8,7e4b90d4,3f073e47,620dbdec,d7ceb425,1353f4cf,a8d20624) +,S(e3f17365,4bb1e860,d1812852,c67c0095,50f82b34,434c5326,d5864884,bf64fcb3,9f07baf6,ae6b8c28,e8a9d807,22a97f51,bae7c98b,daa09578,5a2d1fae,3f359139) +,S(1cc073f8,ac4fe62f,e311eb31,42bd357e,45ea17,a844528c,7261bfb8,24642103,52303125,ef059a48,142036a1,ab6353cf,90a021d8,30a936ee,9cc4ffc4,91f514b3) +,S(f6b037a4,9394a077,7483c0ef,326e694c,be4b01a0,3accec75,629f7224,7fce7f0,f922659,25e4434c,4054ea7d,d4bdebc4,d3801c39,be53b037,715c6ec2,88b34db3) +,S(74966556,eb8c4cbf,cd364f06,b2bac116,5b914c76,d394dbf6,b5f2f7ca,14e54207,8ef7ffba,85b9eb27,f444de36,86ce67f7,75c8eb30,5bae0c6d,1962c839,a88202df) +,S(44d3ad0a,bd1f2ad,30957e00,594f7738,18777896,9b4237cc,d61931fb,a564ff35,af69cb36,28168fbc,f194f55e,e7691ca1,88178a44,2e6bb250,84c6ea77,a3d41748) +,S(7914f911,b6956f5,cb0fa063,bff4ee00,cb67058,3dd4eeba,daa4e445,97f816a1,a61b333a,e72fef24,a47f48df,90b5384b,b25b5f8d,5530a5e5,117a4f45,43948fc6) +,S(65a31bd4,10c97121,dcf656fb,55a71c94,ac34ba43,c3219b1c,86f19f56,bda94c38,a2e19092,35d8b549,b1926524,132a5220,e26beb5,f9d87ddf,f46aae23,dd8834c7) +,S(eb092b73,5b789bfe,9a466b70,3801b511,aaa33260,407b8750,cad24563,abaafb49,ee29705,18088ea6,132314c,999ee924,5f50346f,c7a9d114,169fc20a,2e6b6a42) +,S(38574e2e,8ff7d427,3870b6ae,d859322b,deac3824,bdc1ec6c,e34d88a,8d92196f,a0a6691b,4d770ffe,e5503d61,bba954d2,f4185860,19aa60f9,303f3e89,9fbbc7d6) +,S(5e555cf3,adf0d5fb,530f87cb,6fa753bd,e2c09d9b,d703a899,f27d2a15,a051a64e,41bba138,c76d1e98,90ce88be,1053bd3d,559e7118,4b900aff,c048a494,24e1768a) +,S(4e8518ed,92b06242,b0f28b2c,e02582db,e5f908ee,fc660c63,964f5e88,95b509c9,9e4fa860,cd07c071,fea0ccdb,f1b9284e,393d9f61,45dc4ddd,5ded2dad,b8897e5d) +,S(507d72be,dc881de7,86103317,77afe208,69619a4c,f6cd3012,e04c8cfd,2029b562,2f2ec47a,963f7c86,de8497cf,fa70846f,22dad843,29643444,706487c7,d9999da6) +,S(c87295fb,299c6f1b,410f1128,a1c88eb3,4467d0b3,df7dfcd2,18fa3819,58df06f6,2977ab13,5abd0c43,ef791c18,5cdd6b59,443ad076,5f190cd4,77620227,dc38c479) +,S(5f9fcb3e,2d7f842e,22ae0d41,48d74b9b,66b8d98f,fb66e4d0,1f279be7,931731ef,523a9ed2,a911ec36,87619cd3,f7ffde07,e11a2b19,e90771b1,8ff86170,a747193) +,S(42d833f2,b9717832,1b9174ee,75127d0f,5a850a63,52452942,56fc4257,f90f74e7,fcb299b0,ed234bb5,4f0c9ad3,e9a87bd3,f4a83bd9,916c9050,6952df68,f39dbda8) +,S(cb173f92,400cf477,47ec4ce9,f9852778,6636247d,55279b52,81388daf,99b77f68,f9d99960,1e669f8a,d8283ced,317eb2bd,5aebe201,ab97a62c,25573a0,65b28e0e) +,S(a73a52d,a2ed0dad,e9943a25,8e6a1f0b,6b2b772,b010d81f,a6eb3bb1,61877d65,71f7bd19,7e9d575e,25a4c592,92061743,2cf69ecd,f33705d0,3429966d,956a2e58) +,S(209411b0,398bdbaa,7412b2b1,49162c31,da88d9c1,5dd4db67,8ab3e19,c90a8415,1392e77d,af6bcf59,3162c8b2,2e8ff0b5,4ecf8e01,4fbdc743,dda8f061,ba8a24dd) +,S(74590b3f,a03daf8d,537d9eaf,410b6c61,780f4146,44e7360f,7d6e2710,3c8a6d9,d40fb53a,1f6768ae,b4918201,54dc85bb,62c5ee68,614f1e3,b6cbbdb8,e182d9f4) +,S(cd77abfc,78148f0e,1acc919d,e3afeee4,2cb6fae7,5f68ce7e,cd31e3a5,adb54544,a46dc809,f9b1d0ee,4ba8872f,95166de3,87b08a0,b68b3622,492841d3,f4c8ee1e) +,S(ad1d5f2f,ef4890e3,5d1916e5,2dc6bac2,612d3b0a,3511ce92,94262b7e,fe2da353,6aff87fe,3778f197,ca8b1a67,9b8f89f9,69fcaaa5,4ef0a629,c0dd311e,ae83e568) +,S(de45c27f,fdb38ff3,a91c0dda,56076875,be8a1369,23ef5a55,26af8829,c9a17705,43564551,83b6ec8c,48c1daf8,d8f0cacc,4b3bd932,5d6e5adb,89368834,2ef5ee90) +,S(deba00f,aa0d172,a6bf1c71,6d280e2c,8c8e35ba,9c15f0cb,ae574940,fb78883f,2b282f66,c9499232,dbe30521,da2298c9,7a0dfc43,562ac5af,74eaac20,1d2dd708) +,S(eb95be9c,eb4d5db2,ae9071,e5dca616,dcdffd15,762dda32,5d13b576,f6532b04,c21f9054,d504aeac,79f98cda,de167ee,60e8cd52,bd44e4e9,3d66e2be,a9867741) +,S(a2849585,a2d9d78b,9da83fa0,cdf7f4b2,62c29e29,876297fe,3ad3bcc3,691acca9,a165f5d5,9ba43cba,816ddc9c,b33c4d14,e4f768e7,96ce32ed,2f711127,7bb8c720) +,S(e5a492b8,cae88b61,c72c8772,eaf6de20,40c89597,a4c4c703,85c28e29,62bdad2d,a0dd4bd,8cc5136a,184fd7e3,aa025c2f,62f85693,163e726f,cffa37e,2be168c) +,S(875a7369,7940cc11,b00b28fd,c72b31f6,19c9018d,5c261af,f1cef7e1,be71b61f,93718642,241a4e8b,22760240,2b4f4e1,fef54882,1af31e69,cd45afb3,427fe8c0) +,S(39a301a4,69836ad3,f98d7086,24b53106,fd9d9aff,c1253059,f45617d6,ad6c0c7b,a7f00229,6ff382a6,4bcb9dd1,fd9afb5,4dfdc2f2,bac962c4,8bb76603,7224a403) +,S(8bc53451,922203aa,caae8513,4deadac8,10c585c9,f9fb44bc,57113a31,9474f090,a4bc9c8e,8ea5d122,54a6cecf,aa46b791,68cf14a2,280c86ca,177871b2,abc65b8e) +,S(796eec76,37c1fc11,3ef31366,dbefc78d,ba5735a9,28505c0c,e2a22ad8,b447b1bc,11e91c1c,f9195b5c,b8948081,7d8ca5e5,f0fa07e0,19ee5566,90da195b,c5eda060) +,S(ac52e134,1b531c25,60efb2eb,1c0c79b0,d1f3cb6f,3bcc4b16,6a7f701d,7d5a60df,4415cb74,b2ca5b76,985a1386,2278f53f,871813e2,905e51a,cad23265,64efe34f) +,S(c231c57a,ed49f271,2d571e10,fea59a11,c4d427c3,4a67b024,b4713af7,d2288d69,bb4ec242,6026ce7b,56471afb,c4ed8772,dada1335,61d07981,907d278e,70ce398c) +,S(1a1b76ef,11f3d64c,5670f06e,7354fc4f,a7c17129,75ed3094,b5a6df78,c8c03034,600c7a61,188e6ea8,76b8bd21,b121ee22,ba9d50a2,d2ce0fc3,d836829d,1f829430) +,S(82b4e49f,2d9d49c0,f409ce6,13b65a5,c031c8fa,adb3ec8b,62885760,c69bc5,67b8cfb9,56b80bee,5e5ec168,6cbad5f5,cddf38ae,3bffcafe,17d242ab,896fca10) +,S(bb3201,8ebb358d,575cf58f,c08426b2,f7116d40,8cd2d779,9acf6c4f,55de461f,8f3205b3,291c8da0,b0ebab8b,ac292913,12ad3a7a,83ac3ff1,6e6f55d0,445f22bf) +,S(fab9cc75,60a02c8a,e916bfe9,b61cce9e,32d37203,8b42b58,150db6a7,cdd40a22,132d8021,8e3b6ced,e6b3060,5812823b,80c7f0ba,f369caab,8b3b7cb3,bbb477d1) +,S(9f96535a,bbd2b21a,bf41bd19,549528fe,1353724e,cfa3870b,3df3256a,661c7e47,b8acdf49,a9783748,40232e1c,9a0f6854,1547b7d6,d1d60bb5,c321a1b6,54fee431) +,S(60d4754a,2faf463d,f7b0cc2b,ff4e6495,ea9fa9a2,e2f1f4e9,d773301b,3d406daa,43d16225,6bddfb93,3588da3e,511ec648,5599ef3f,7db3a1c8,899c21ea,2fc91936) +,S(3850610e,10a40ef1,5aaf4d4d,b11ce214,7c82a449,ea965cb2,5cd9ad46,45ba44d8,c10cdca4,aa919f20,4ab148c7,2eb4e6f3,7939c70e,e134433f,5c70e45c,fc30be3f) +,S(68089586,3a26181,a6966055,1ad8d431,9d325533,70839d17,fbfed5c7,fb84fab4,a0cee277,97d2ff7f,fb7a6805,604eec37,4ef4182f,a163c53,15924f,287bd447) +,S(aee5d57d,17a9167e,9d3ff19e,8eebeb09,7675b709,e3a89ec7,38280b9f,ab9404da,154f14e4,f991644d,aad6d858,940a6126,5a8f1c1e,ef6071db,5e138081,5e2d34c9) +,S(8f3362fa,659c58a8,2376ab88,c921fe2f,2c8a5de,f36c057b,3154800f,bd493950,5f88cf3c,3fc1ccc2,2105f3c7,8ba7b8c5,525a9fa7,7cf76976,8906d0e8,caa64408) +,S(2bfce32a,9d69a14d,324d8f94,15b42f87,25f5befd,ba3cc6ac,2a5c7778,f23187a3,f7e88e0,51ea8e3b,788e18e2,95355b2a,75fc3c3c,e62f7797,d3b02681,d6a3b63c) +,S(62984e62,5d026f0a,c6c82fb0,46dbc152,a05c9a22,3f55663d,5cdd87d1,e094022,1dadce42,d6b68fcb,19439f33,2d2db167,f33861de,28cd303d,c94e3a8b,7b26964e) +,S(15502824,b02a10a1,d7b13137,4175bcb5,6b70f796,fd3d0713,42bdee98,99d8a057,b391d51b,b2b89dae,5b687e16,e2ce1351,2296130b,e86a7d21,c1d3c6b7,b20eca57) +,S(1bd98bca,c0ed80d4,f23422d,c32c5922,1164ce33,55526ea1,61e1d9fe,518c0e11,6b0afff3,b0fa5029,b4e9731a,fe772fca,c0e30c84,e6c652e0,f22dfd41,a9cf0ef1) +,S(4dc9e532,d5543e88,a59872fa,24b99968,e649977f,9ea08bdc,303ca4e,d574bf40,7c05536,d9181687,133dec90,43cae3af,42dbe034,af05a746,b6de1ef8,20276947) +,S(3e92be78,d7c7db61,1049827,42b0d213,672cc543,c27628cb,f5985c19,5685f3a0,171c3afe,c69648e,7bb7912f,fdfecc4a,78cfd246,a4fe1263,e5eeedd5,330f84e4) +,S(57b6c4f4,75bb9ce2,2086a5a,dfd5473b,73b0f6d6,278afc7a,16854e6d,32b2842b,4fb4aff6,738f05ce,31ec7934,3638a717,3806d347,c11afd82,e39d492f,b961ebb) +,S(571ac8b,835c5f4,a7457344,e8572910,309f0d3a,26b849a1,b3bca2a5,28771b59,317ae805,3aa2855d,aaec95e1,72f4005a,6ae09ce7,187590b6,41fc13c4,9bb4cf9f) +,S(534c2141,e38a7880,bf146951,7d4ceaf2,a25bf5d6,a538eb1d,4161461a,ae54c4be,59dc6548,1b056dc8,c3e98364,ce6b76f5,fbc9c501,6ff24e0c,1c7bfffd,306b03e2) +,S(7a761f86,3a224193,157d3d1f,8fff8a05,e5c13366,a7ef7604,2cd3a1ff,f9f936f1,20e86bd4,5e93b784,f433d6a5,8056c6a8,e8d1fdd9,e924fd37,bd5b16fe,fa0ece9) +,S(c1497e7a,b9373d32,4bfe813c,32aa9dae,f1351351,c86ed382,fd3d4c5,6a54f62c,9401bb9,1621a327,2c233004,d6a8093d,f812ee66,604eaa86,32085b3e,482eccf8) +,S(2dd83787,e31e7237,f49a570d,adc675bb,147b91e1,3910f22c,e41bd16f,edf3c0bc,10148349,6883687b,7ee844f6,d60ee90f,1aeb7c0e,c9f83293,be96eb2f,5cd6d46d) +,S(938f1399,18a0fe3,55ddc7ec,d6dacd34,1d9c996f,9ab4f9d6,90b435e1,fbc0a74c,30c71b30,e7a3c244,b8a576be,9e527e6e,60d1834d,17908e6f,a0771daa,92517448) +,S(e71217df,430dc65a,d188f6,861b00ac,c3214125,447d8e26,7dc5473f,9471284,8f27019f,114e542e,96eb5121,b7c3e094,f18f6287,f2cefd9e,6a5290e8,5e1dbac0) +,S(ca2ba87e,4c040efd,c06ca7fa,50cbe414,b66bb2e3,ded2bfdf,7744f81a,91b102fc,24848b82,4019bae7,4cd7604d,101c4819,6e808ecc,79db885d,bed68354,ba3f138) +,S(c267ce5b,633c7f12,1be2b297,45b4dab9,ff488b02,c8f89ba8,706948c7,77167ad7,16184a77,954c3b5,f9e330f8,e8b308de,1489ebe4,bb9ffe59,f3f545da,e7748385) +,S(1eb9af42,96d58c44,bd6fe2ff,db05e0ba,1b5f581a,af842883,5a47a050,468f8b33,f88a1128,95e53e05,4e514117,5a2c54f6,a235cb08,3494a7a,991fba4a,fd0bf1b2) +,S(ba342b95,76bea3ec,15aafca6,2f4f505f,c35bc2ca,64e2f4be,22323b77,1ee39062,91be5746,1fb662f6,cc28e73d,f0dd61e5,20cd3ceb,2d6fb0b3,39a76e5a,39114afb) +,S(f3a8c7f2,86117118,a4714895,eda7f401,dce4be87,dab4d4fe,b55068b4,c948b350,1dd2f669,31f0ec62,74e8bd53,97cb6b50,d97f9238,2603c931,8ff1c6b,18893bf0) +,S(3cc304a4,3aa02bc5,25437a89,977b8ee0,9f5f997c,56c4d317,f531d22e,4a60f55b,9ce121dc,cce749b4,78823adc,c9a613b8,aaf78f5d,c2eac8b5,d5ee6aed,592c7bae) +,S(e5be6c29,b9f12f42,1f499fb1,13e54bf4,4a577fa6,d8d0dcd9,6b509f65,1b4f3587,7755eecc,721bdd9c,c6e8c96a,d398cff3,c03e77c7,9e34dfac,1f51873b,8771d40) +,S(f933bc46,f296b780,4284de6d,5bbeed68,bae90bdc,cd731cb,b9519c05,278c7fda,b6ec0484,faa4c53a,630a8b81,e1d567dd,543a4e55,4e89ba69,d72646c5,7ab3740) +,S(e1d21ebc,297edcc2,b595976b,2f65b2a7,a6797cfa,3e5abd27,34e2a962,dd2f507e,aa31ee8f,4c80a0fb,9e52f32,c7e4a69d,fbe8d297,8dba479e,b0c9f4d0,350012fc) +,S(c3cf0aa1,8e45aa16,343c52ac,41b12278,f9899b46,e17ef9d,d7b67f75,84a1eb72,174cf510,70418a6e,f83b3db4,97102ca9,5bd99985,f2554e5f,c32cdefb,ba47e411) +,S(e6a90aff,6e1f0311,34f93967,538040e4,92120644,8e8e2106,dbba6f5c,701700f8,93895ef2,1d3513e1,19228b95,86289c26,f3efc024,1ac2c963,b394cb87,265638c8) +,S(e2b695fc,8f1856c6,131761e0,9f75a558,94a4a7a9,2837dd29,f8e8a474,7a9b9a91,b580a2e4,240724d,5c5381df,27a17d72,4d0e638d,958c0a27,fcbba34d,da5361d5) +,S(c0660a14,6db599e2,15f813a9,f267d6db,4542a696,9b08334c,6bc80558,ae518063,6a901346,6bee774a,54ec2f97,47d7a8d1,e157a13,fcde8cfc,57e48d84,374f6ee6) +,S(5c41cced,2b091fa7,df1f8e30,3d78b117,842511eb,41717b59,f776e05e,39daacb5,dc4964e4,d91fc985,af5c0f87,f0026a6e,290bb1c0,b181fe2e,195f7001,52509f40) +,S(6abe8574,49cbaffd,d0e5fdf6,450484b5,f8636170,5c8a0a59,a3612dc0,8d0d52a1,c669b059,3a5fadf6,86b9583e,7fecc9f2,c5eae967,9eb5be36,a2018337,64703bdb) +,S(560dc0ec,2d407e81,4b5034cb,8d26c8bc,7c86487c,f74e2fb9,a076b521,f68e22de,faa241bb,ee929015,b4183152,5aff5062,3a270bfd,6026d94e,c3106e3a,e66783da) +,S(f3ef40ed,8d82c02,a141b953,e8593f06,b74b244a,b85edb18,eda3e693,3c8f90a3,aeaa6d25,6db6b12c,d9a6ab5b,d6d0d854,62c0d298,5a772648,db46b614,3050d4a7) +,S(413e0fff,df030385,248b1fe2,1bebedfa,77c1cb86,d665790b,21914da4,58a4479d,6fa9db70,80ab1736,ee7d54dc,e5d1b545,dd17e3f4,45b39038,595fe180,2b34b443) +,S(ed3c5ee2,cf1cc502,37e5c654,d903083c,6fce29e,cda0f427,5a6e1ac,940b8183,fd265e79,3474e60,109cac37,a867bb81,d8ba8b8f,94b13f08,c409ad5,362492c2) +,S(b98e14a2,3f5e5d6a,a99ef39f,18e234d4,42102e72,2e330d2e,e654b328,e2f3bf49,67a60b41,8e820ef9,72ffa57b,1e383b1,35585920,20434df0,e443c318,89734499) +,S(31bb6293,28793b42,dbafdc9e,6a418c51,f0a0cf0b,6da96879,11a6f046,c146e475,1bdb131c,b14a3d85,a532f889,89112231,341a546e,c88e8a25,8bfa341a,7826512) +,S(79b2d26f,f257ac80,ca67a639,9be17ff1,8ea0c10d,739b106a,a9310754,6ba0b252,e09448ea,b102b1cd,654b6e1e,7e4754e1,c63d11c2,46b0ec5d,31df6175,6b99e850) +,S(ab8cbf8b,c3a740d3,c1bfc2e6,efbd7ac,f444795,cf2e311d,edfd5c2d,124d55da,70599847,895f335,a8bd33f1,811201b9,a860555e,8cde2a27,4c348b4c,6ab449a5) +,S(f6b7c2ba,c5468fb1,cfa0785b,f67972b5,36367ef4,d3dc9ef9,25e88085,524e6021,663be56d,c325b2e2,3753bf15,1f6b2ea0,2d6a62b3,a3d5f565,2734401c,9c661a3b) +,S(f5d2e04d,229d784,d7af5af3,ea65a35d,637b404b,c431ca72,1bec7fa4,dee33377,16c90231,e570a434,700fd8e7,cae5f8d,fbfda2e0,e234ef23,a2e82452,a0a953e9) +,S(4df218c9,4277fac3,3ca43921,e29fba57,6f183e74,fa2ea781,88b8245d,eddab853,7828e929,6c589e6b,1d80b036,2c9bb0c9,2c2a79aa,378c597e,8a64b985,d6e4d108) +,S(60e1cbc2,78d4a0bc,b7973b32,964da674,9e2b3448,24075a0d,1db93e51,ee3414c1,cec0aaa2,3db6c3e,6d6c3189,c2dca9de,8ce91791,a721ecbc,1f09c7ad,468ee8c5) +,S(1e7a9c4d,a6cf3251,559d58eb,bafd1a7f,ee686c72,8b1ad271,d9da3b15,6a9f3737,794e3bbb,cdc0df3e,b9669a10,8a32d7b7,f3fb843,388cc55e,aa893f64,ce1b960e) +,S(e8984316,5ee703c2,a413c7,2a5c8ea0,605c0b4b,58cb844c,85df83c3,b4f361b0,69fd6f89,5cac1716,f0bacdd,5a880f12,f964c2cf,1cd402ee,27ccdbcb,5188b64d) +,S(2b95eaae,6078c535,32a23225,eb9821e,c7bf864b,75187df3,2736459,f0797e4e,aa6e1922,b5102e69,77fc23b5,ac2de98c,edcbcc37,54c928b1,7c7d124d,980666fa) +,S(403c1933,59ac9027,7aed0f41,f9ebc507,8ae553a6,ec3b8a4e,4f66a841,19d221a0,edab5156,8667e7b2,c7793e17,839e46e7,85a6c7a0,8af7295,8e25b411,13829ead) +,S(fb182399,c535de2d,556ecded,54e42f29,8c0054a6,1dea2477,9df1c7c3,bb53e1b5,7bed179b,73268027,4fa6b227,d13763d8,4fa62dec,2e351ac2,d3aa59d8,8961123e) +,S(9375a9d3,ecfff9b4,b672d90,45aa4872,f61c3b8e,4cf86a38,7daf74cd,affaa521,41c7bd13,f4e491db,77a42bb8,d65ba77d,f03acc6c,cd789759,37486d08,c3f691d4) +,S(abb9554e,1b68c554,81d8bb12,5b426a4c,433cb110,79254f8a,ddebabd2,9f5be0c7,946d680c,1bee958d,32089353,41ceb7ee,5b4a8168,a6368a92,e1ab1050,5d0e76d4) +,S(56e8aa51,37b38531,1e5e1097,4df857ea,b4f7e53d,11102021,6463c212,ebcb873d,b51e1981,7eee5672,afe7f688,92ada73,abe703ac,4e19770c,97dac300,8d3b1632) +,S(ac487fb0,4b00962b,2b098cfc,4f22eb28,e5007944,d32a136e,5988dd47,d9828f5,bd305529,5f4200e4,c5e6238d,76665bc7,67ad8402,dad13d8,6c6fb316,a214a5c) +,S(1dd7562a,ce273114,660ccde4,5f41d690,11621bce,56cc49c7,2016f19f,d94e9e9f,a8e8861b,ef664d9a,c19391b6,5bb97715,ef677199,a373f255,876aa9ad,d8a93043) +,S(c198019,cda66df3,2bd528db,e3869d6,1d1d7cae,57ced846,eebcf1e2,bcceb8c3,b0a9c52,ae2b2625,7699d3b1,861474,4d66f09f,8b795d81,acd3e5de,902b247a) +,S(950f54ab,3cb421f6,95404b53,9b1f4936,6b43e61c,7bf9879d,168027bd,f58dc386,e14586c,7f005f5f,21e122bb,2a35194,d5d2fcfa,4898c279,cc296b24,9b48b2a0) +,S(5b20af8a,31b69820,a56d0df0,c7374c8a,37805449,81e0c017,83b4ff49,2bd40ed9,7e49b8a1,c5bbcb32,55991da7,99613598,cfab9d8a,82f731d0,dc2f52a1,6ec39c55) +,S(eb8a10cf,3777670f,437f55c0,2ddd8681,63da41ff,5b534c63,ffb23ac9,ecaed755,99c18d8d,df2ba9a0,d46ea92d,8b983f50,c16f14b9,86b955e,a577f332,10533df2) +,S(12977f3f,d75a9c72,81358c91,73664b40,8d8ccc98,45153ede,c62b189d,8da2b176,f24f505a,75761c5d,939ad836,17f0c6,dfd8a2f3,fea04721,9078e2b2,1aa317df) +,S(7a591539,ff4fc3ce,2e2936d3,50d753fb,131ba419,8d034c65,e76d0744,c159096f,c95d8b25,52b3920a,6341ef43,5e3e797a,446565e0,652a562,7e0674c7,ca243425) +,S(993061ac,819fde52,ae500ed7,3277ecb,fd08930f,8adff61d,f5f9c3c6,cca49640,d54444ce,748f6058,10051838,52f0073a,8a5d1477,f14111d8,b623a316,a67ad06a) +,S(cf807c40,c5664fbd,910a31bd,ed3cb7e5,2e54d1ff,ffcb4b43,7a579931,6f803051,a5d21e6,6fa9b5a8,2882b77b,ca66b6e4,163d579a,d245d089,602274f4,55ca9881) +,S(9c0119ba,461d003e,c6fb7a01,2bb32b89,ec819d97,8fd4cfec,ca1c3b2f,1f14f0bd,b41c5fa,ce1e9a54,f1605e84,61b5e6ce,c11590c0,15838059,2c511360,f4b537c4) +,S(56ea3259,5d97b7f,d7f9fe51,b525d73d,3d77d483,f8bf8460,8d874d25,aed3da3c,f764a40,2cc9278a,f1ae6369,e1723bf5,f0a15a47,992f8fe0,ae33752e,473c3058) +,S(2851e3cc,2fa6752b,e0a65326,46aa8445,71736e1,d22a01fa,ab3becd6,50460acd,ad778ace,631584df,da4730a4,20242779,81f9f829,cb18b20f,8ce727b,d62d64b7) +,S(1674559a,528fe0ad,5aef46f4,eeb4e0ba,34b8c2e8,c23d3409,5ec1e164,ef351979,afaef98e,ee2ada75,7be9675f,fee76686,d2350972,a9d1b80d,fbbc0719,efc38b21) +,S(ede70ea,37946f71,848d72fc,7a98102b,f9c167f3,219671ab,1843b5c6,8ba6d7c0,920d6108,6929e5b8,30c4fa,eb155b4c,9d324001,59d748c9,d7e74980,d54be37b) +,S(fd3b1f22,c5037c8e,93f4f9ac,27ea723b,3d3e861e,694ef349,dfe51f83,50ac9fa5,cc02effd,6df18244,fcbd177,a624ff17,b0da8fec,fd60624a,3d066be0,9c466cdd) +,S(72f037df,352a4619,729bc550,e23ad7bb,6f2d7977,7d39ae36,bbc1d45d,d6e864c2,c4bfcde1,2a5cbba3,7e047070,5937c2c9,da702040,bf49cb5,c762d60c,e8fca76b) +,S(f17a9b25,601ec8ef,e320558f,ca832789,d0d8d558,76ff7c53,6af0f84b,ca799a61,c90583c6,2a863567,b0d8c41,e47c3d8e,7e196a1,9269a409,31a1eab8,f1402837) +,S(ea9fdf49,9e201fbb,b0a26a71,1b9be2e,dd17b2d2,b96390c,4aa2dd00,83bf5c86,64322654,c8bb2a83,ab0cb743,eb907234,d8503fe7,381e17be,c17bad5c,d33b4df1) +,S(8d5d6f0a,24aa6e3e,7dff42b9,ab8b22aa,656788b1,1e18ca35,b4ca6f19,1d50de6f,d509a2b5,e9800d5e,a7d1d047,e945e693,2e5c1923,59bb0210,1b2e8956,33e0257e) +,S(25ebcc4e,84a1d22c,f0a8c980,8252d96,7dfd528a,808ea293,ffcae94,50e1fb81,3127af8e,4012c2b5,c92e428,2e455de1,681eb1b2,6e660fcb,4ee8fef,b64c6101) +,S(c55c17a1,dcd8fa41,8a3edb5e,c2da8678,4fed0840,522b24ed,4bcb85c8,cc45c20,7d583091,25261489,44b56fed,17b07906,9d156fa2,e9bdc462,eeddeebe,68f4f463) +,S(f4297f59,21a867ba,1e58b9e1,def633f1,18e058d5,a7889721,f0e4814b,7e63ecce,a39f359e,35a123eb,5fb66d81,f117858f,b0557859,aa2c388,a8298d11,43f137a9) +,S(626b0a,cbcf0e7,daf7d218,74f72be4,8d824883,4428bfee,f82ea516,c2d1b98d,74ae6867,1c27a642,53a36590,5247cbeb,77173b07,2e429de2,3ce09730,8d54ddc4) +,S(8ed35ae3,c02c5db2,acb516f3,2d3b6823,61964f2e,701889ab,26dc9512,ed40172c,aef4c71c,e241be56,63a091d9,e230463d,237e0717,78b7b6ba,a63688c7,6c694334) +,S(5b082ab8,963510de,46c1cc49,4ac6d3c6,dfa25f33,8627a00b,6b98121c,73899ed,cd8b2b84,185718a1,c4686ff0,7cad18ec,fe3d4532,9537910d,31458086,c280bb32) +,S(deaf505d,b08a282,d5274de6,43c5719d,f51827e6,2ed035f1,74279276,7ddc1158,c1067344,5e4c0d47,ad1ca85d,713a5916,1aef6960,9294d738,103b945a,e99ed1c4) +,S(eb62dcb4,d820019c,e865129a,28b98b5,be6c6e16,a80f7c76,db3a15ee,31ba7aea,e78eec22,be4a175d,68b2257f,c5adbe46,fbe31c3d,5eed4736,2304d80,ae4577a7) +,S(6c58cb64,7ef638c2,337cfedc,a9345837,597ac142,577b2230,7d231a3e,5a9173cf,563008a5,7b823427,60bbc400,5f604024,92f0bcf0,2ab2430a,5d7accc5,b62cbcd0) +,S(106d8978,775a41b1,12f07815,7c3bc5db,bb5980d9,c745806a,b5ca5ede,8b468d51,bf960349,6f7d7906,141bc4fd,b1766102,e8dfc49f,decb469f,75dbc54f,21338dab) +,S(cd6124f4,21796b6f,44c34982,8b536ef7,44bf30ec,6ca08142,e959a35e,c0385224,97ccdb9f,2896e815,19bbe9e8,ae491e61,e83417c6,6db8b6a9,bede3aca,f6608ea1) +,S(2918b2d9,b0bd83d0,b8d02815,59219c6,bd708e2f,2139ad0,1815b1ff,ae60e03c,f322596e,c56984f2,e7b2ee90,8a9bb56a,d213f84f,3088aefa,67e372f2,76e2a233) +,S(6d3fe999,acc04d13,2462f14d,680a90c9,89b747f2,3601d779,507c36ce,8c2acc7d,a6c7413c,36b6399e,58a443b4,e4137ea5,551a9fb2,c5397457,9d55b48a,5df0e30b) +,S(bd74ac79,8b320aaa,e3e51123,9354d4e9,280c99c2,809e88be,13980305,62d55d52,73477881,9b9067a0,78b40108,38ae59f4,3f6ef064,ba35209c,46a9f0dc,aaf84597) +,S(303f6cbe,866b4574,9d282217,1f571fb7,8c54dbfe,901f2f43,681ebb0c,76e5700d,b13dea58,d3dfe08,7fd043f5,8e63e565,85230d6c,273d9e3b,22145e39,afd6ea4a) +,S(3bbb0824,16a2e3bd,e328f013,154f18c1,4e840030,707057cc,f4fa0fae,ff56181a,18b214c7,823b0cb1,76375146,7a49bb8c,ddc0047f,782e3802,be12a119,ecf0f480) +,S(ef42fcbc,999a3f80,b4e7319b,1fafc2c6,49f51845,2e423c55,f57e5e1e,f8338adf,786ffec5,60464882,48641155,38aac187,ced4678a,cee2b9ea,eab6ae61,ab2061e2) +,S(afb21e4e,c095bf31,34276f4e,f1a4883a,6038c20b,a7a87ba6,6288bb1a,3bc6f77d,cda832eb,73dc0e9,fb77315f,6cc7a313,2212c7b4,1075a290,d5917033,62363bad) +,S(329c98f5,a40b81bd,7d121b90,78b50bdf,bc16c9bc,47744368,34a5208f,20d39d63,dc14f7d8,4bd2a1e7,22fdab98,1044d6e5,98515df1,7da5536c,7a95866d,8231a) +,S(6929a4df,e15e7ac8,e8a9c5a9,3544af51,aa621dc7,ca04038a,9c9cf603,8e081754,e1333f46,e4d1f151,f2b8c341,d2ff90cf,d36cf73c,6f441b4c,5c0ae272,6a25fbf3) +,S(f40fbdb4,27d2d68e,854cbae0,24ea5980,5948ca40,e3fdfcc3,3efea0a4,57e2bc24,33493d84,41c8945a,9e291b58,5e12789,34990a7f,5fec49db,ea1ff605,4bf8802f) +,S(220417f3,d9fb32c0,1439e473,d638856,9b169c38,1057b780,c9de7d12,e30dd796,4cd93672,5abc4e39,89e64ed1,af9b3d4b,4732ac76,a6c34d6b,23274573,8d765a1a) +,S(dcbe3dcf,2c0120d9,4d6336ae,6158bdf2,859cdb03,8c76ea3c,cad4ed11,13ed216,670823d9,73c74725,ce929318,c2ea650d,5feb7c33,c7831a26,821f2614,7db7d8ee) +,S(34de2319,c535a31a,87fd9b70,33c363e2,8ff754a7,9e0669a6,37230443,228ab3b8,31c65b12,cf6f4a9d,662238b0,c8ab52af,ceb96acc,35b90b2b,81667b8b,dfd9895) +,S(f0fb2bde,a5626729,720ff432,542c5e41,abf0fbc4,772aca7a,75b882ab,2b364acd,114edca0,74520c9e,2f5b8059,59c5273f,25d2e621,d2f55709,b42d187c,932b6bb8) +,S(90477ee6,a00742ef,78662c11,f3725077,dbda7b66,7ba55b55,f9bfea1a,4c5f88f4,467196ec,638d137,33971505,f291dc0b,b9d0f3f8,f284e3ab,9035273e,d9c7279) +,S(ecdef6b7,455a41ff,96cf943d,e489858c,b7abc526,e41d463e,b24a56f9,dd6eeada,1116e7da,9fe72782,e8c656b,9f677c5b,dfb3e46c,98957c30,49efd7b6,dd5d3258) +,S(58d30bd1,d8dbf5d9,885b6e7e,4f28ceb8,381415eb,3851a031,ef04b073,65e16598,9cb96ab0,546246fb,f5cc0878,7b99db0b,26c9bdac,184a0eba,728fbd26,bc880480) +,S(a7587d1b,771b3421,f56e342a,cca98e7d,82c7dc60,765a481,53379da6,fd341c10,9499ba9d,8351d194,213ba35,43d4b563,aa28bba0,9a6d8811,9b55f389,90742929) +,S(8aa99d89,9db3c7e1,c28bfc5e,18abdfbf,d9fff46c,560e4fc0,cff65ce,e18ae6b0,aacbc2e2,bcc4ae58,1e354d29,617afee6,20ced0ff,859c62b6,669cf2e0,565a9bef) +,S(45d81a7a,b4158aa4,2e45fe7f,3966d278,75f8647b,defa0c8d,a761ebef,894ef249,93a76905,d0e785f2,db52e09f,573a461f,183f672f,9f10005a,eeed6e0c,d40d31bb) +,S(de19bc8,6fddeed9,909eff4,80b0760b,66a1a245,82e33be4,55b8061b,225942f9,6eb2c1f6,f604056a,15c4e91f,789e35b6,1a787abe,aded0f30,abd2525f,19486d00) +,S(653f0e92,977b7830,fa546dec,44f05549,80f5a9f6,61f2cecb,7e360efc,41d16380,58ba9f7a,eb7aed3,40b54d7a,6f9ad0aa,ab5eaa2a,7715d8ec,48d52ce3,c3c57128) +,S(c05441fe,2ddce6b6,64270b69,83d44d5c,251efcb8,2bce4665,9388eb3d,537acce,cc82b7bd,3a049b06,98feacc8,11a1c9fa,e95181d2,50c0c62a,55a6d662,c9587d65) +,S(7a569bc6,a089fbd,294dd5d0,b10f3a7a,31b6bf39,42ee97d0,49c9259f,7adf0a7c,2399d17a,f6207b8f,20286fc4,78896af4,5e596b09,7c97f662,9bff0c03,14db53e5) +,S(1c4c7a37,4e14ce88,fd08a1b3,10025711,7d96878a,e173d29e,1ac53a57,1d6ae794,919f9b82,e827cc78,7352f0ab,99770e97,bfe1b600,40cf1034,b7c5178b,3d1104fe) +,S(fd4ef218,a09baf70,57b53f65,2ca1e2e5,8386e867,521dc998,1bd4ffdc,619c31c1,b1d2e716,64fec6d9,3cc8bf17,70ab5f10,a49a0497,ac1586b7,b8299f7a,e92354ce) +,S(f012612c,1f933373,ac30413c,1b2cfb9b,6582957e,b74e4de5,29c0267a,e7552a41,a38b2418,e7ace7bf,3a15035b,a68f82b5,aae49192,5783066e,f0f0263d,6dbc86fa) +,S(8e0d504b,c9598eac,c4bd2726,8b3ad24c,a22b7534,32eb5a31,d7e54948,d57f6f4b,50f59455,ba1ea3a,5a0cc105,a1c40c4b,72637a31,44bf9438,a1e3b52e,7b30911c) +,S(8635604b,89ec22bb,b3b4e266,3e76cb3a,976cc78e,c7db31fa,fc16a7ab,411ed197,8a11d457,6868d117,4f41c40d,4c82d27e,3960d57d,4b1309a4,531c2617,b1d90227) +,S(aba7addf,c234513b,3e861382,654959c9,984628df,72b2a896,6459b3c4,86bdbd2b,ddec8263,bfdb4c38,c9e7fd,e4693b0d,962c6e5f,a4f9eb9b,9561e81a,f04a3a3c) +,S(12aa3b40,44f699c4,8ff6c598,8a7ce56,84f3af85,8cf2ebd3,4d59ca7f,f7501895,76397e3f,3c42935b,6a42222a,525c2166,7662ea00,25decbf7,8a856391,a03956db) +,S(e01136bd,da38f328,af447a50,43a48606,a1ab5a86,b3bca98,b13a53aa,c7061098,76f3fc47,c1aedcc,bf26ec5c,4f6ad56c,fa163212,65844d01,8d2f2b94,184b64df) +,S(639cb266,f0672340,d0a789a9,41eec287,b60c380e,f2f62f96,41b67137,4b286aa6,d381e7bd,5dffffd9,50645e56,63c36d91,83210038,d70df2ca,4bbb837,e8eff508) +,S(eb12f18c,89a6d2e6,5e07d20f,f7acf3c4,b903ce68,5237aca4,5692c0e0,f6a4714e,d26860ab,e6f97745,dfb01cf,f6225c36,bf62e5dd,24088d54,a6d44384,e9eed86a) +,S(8fd06b6d,96f37c6f,8e69187c,c5ace64a,6e36b45e,37de82b4,872731cc,8781e941,e7dc3254,3b40599e,852d4e9b,e35a5438,ff68b063,3b81c8ff,c2f3298e,931e9e98) +,S(d248d914,4ca3f10b,3fb66763,713492ac,93b42781,9119683f,bf0e0f08,1bb6b241,e7a2ca15,f1867e9a,d19ffdc3,f1a20d0f,2469fa6a,e04f94be,a289d019,c405318c) +,S(ba2150dc,15884df0,34a64e20,89023947,687d1322,475dd073,b163a4bf,4eaf0740,af92a566,a320a0a4,34095dc6,f02f765f,4ebdf04e,d81e2c04,a86e6e2e,923f8c94) +,S(fb12f487,225a6843,31e39309,3133f377,a6f841a7,f6f90ccb,18ce3d39,48b56616,e3358a9,406905b7,d6bc0a33,6a89a5ba,e62136a5,4135c3db,bb32f115,4cf92b9b) +,S(bdeb3e38,7c36bb9f,c45a1d62,f7128217,20e6d0a6,55a285dc,da87af3f,f59e0644,b23fb389,3cdb280a,43275691,3c1de5fa,6da12909,c0ebece7,12c9da49,69a3cb29) +,S(32f3ecc5,925ccc13,f753ddc7,d5c0c576,e80402ab,ffc6dc77,6e8bf74d,15735007,f950add3,926bebef,9d8e3a5e,4d0ebbef,24b54dfa,ae7c12d1,32b6e973,dc67c050) +,S(94a725ed,e57e5162,817d95b0,54e2732a,3afe3a6,52ae3e46,ca9de38f,6dd9f0fd,805f6634,f8ba79b8,ecd5f46f,74b741a4,de5f9678,d29b8cba,eec73498,27671f67) +,S(22197e54,d35357fc,ad20f6cc,986216e7,f24e6dc,cfd9bb89,3e5c1ee6,9a99b3a4,b3e7edac,abb1d1c3,d375f361,1f04a927,9d2995c9,3e18e7fc,4b5dca90,a2a3fe17) +,S(a51664bc,5a1edd2a,ee8ea644,7beef61b,419e20ad,ac955847,5e1d666b,1b4dd52d,b8a95fad,e3aeac7,bcd8670c,78dd1b2b,5dcad225,277ca5bd,f24f131d,94405c39) +,S(896a11cf,f9db64a1,aef51088,baec42d0,16dd5a99,98262f71,a005cdc8,ae8cab78,7a7263ea,f64962db,bd09a278,a3958708,e61c4858,1a759726,c225471b,20899077) +,S(fff75552,46da269a,2124d03c,677f59a2,18b995aa,e69c85d4,3b29f81e,5201a757,dbbf1558,6670c191,9da69467,6e8906f3,80eb3f61,34c656b2,a4ccfc71,4b3223f5) +,S(89da8ea8,56f946d6,6e9b07f3,db5b2ca7,49a07e5f,78b59c1b,e52e46b1,3793e198,25889e02,d2013d86,6653cad2,7b3504d1,4e547343,fec74a37,a3ee71a9,646a36b3) +,S(d260dbae,6ac3e8a1,442ca7b9,8f7118c5,7d6cef72,783c0b3b,a4ad7e7c,3a8836fc,e00ee7fc,95131cd3,3a446bf4,c21b85cc,45a8e61d,2d9d6cf0,287996e2,77b7cb6b) +,S(bacf90ec,75ff002b,c1a259f0,3c9debe5,9f5f9dd5,cc0cbff9,e8fd62c,4552a619,82dedfc1,42202786,a7a12a29,22937c0c,23bddd23,4418979e,78723ff2,6a9e5e88) +,S(1ec23fa,995a22ef,53a91dec,a5b2faa9,514b5ef8,faf125ad,da61f84c,5b13a397,1568ab1c,cf75692c,b7d41c53,2dd4427b,ed7043e6,cdfe01a6,2f9bcb47,cb97435d) +,S(48225de5,eb07155b,dd86e759,9e172a3b,b96f69bc,ddcdb051,af2dc769,f4450b1b,856ce939,b81d976c,6a6c249e,c0d087cb,36e28082,ba050de5,4948d92c,6693f4aa) +,S(e9ac9963,16c1ff8e,a7d2bbf4,148c8b3b,a869d4e1,290168f,b3831d6b,3bbd164f,2b1ac664,6bc87e3a,e6914792,d0bda8b8,5cfe1107,3d0447ef,e4a65acc,6eac846d) +,S(8af31ba2,af0b68c9,4290bdf4,b4b80345,5f52ee89,f4ddbc98,1dda1000,9c1a2ec2,a37cbc1,a5efc09c,dc944f42,a66a305a,b302eb17,21ce5088,a7deb46b,5223f0b6) +,S(1d211de7,d75d4389,4cdac235,9b0b9e40,6b71b8a9,8c0583a5,7d7a0056,b3e88779,3ae4bfde,20f83eae,96982e96,62d65443,4359b2bb,df249a85,c7c83b95,56d7679f) +,S(be6beeec,18aa8d77,20100975,3dc7d745,c6d49f9b,ea64ebfe,b8fdafc0,92a192ac,c9e14aa2,4bce41e0,f444030f,ddbf035d,e808bc06,63a70593,454ba287,4ebe50cf) +,S(b126dd86,555491a8,e2ab601b,2f133960,d86a7f9e,bcd2c88a,cbd8764f,1afee3b2,d22ae79f,fbc60439,baaf2c2e,418edb77,70837ebc,a909322d,b5580c30,27c20821) +,S(857b9c62,6350cec4,f928d456,7d30cad8,6f1cfad,142b338d,74a137ec,61212791,9d3dc070,f610ee55,cfb9cfaa,4bffda56,52f7fd18,7f354634,ed4f2dc,d27f9ca9) +,S(bbb7d30,3e242e87,2e94e3b4,27197ebe,5252c363,43ccd8ca,940202ff,fe5775ac,f2b5b210,a40d1fe,140f8638,3a5b0f9c,96885d5,d9857a3d,252523b8,d59d33f5) +,S(3e90a10c,a61bd12a,fe9caabd,e52dafbe,8bcd332c,e0ff8bd,7718b685,89e26447,3a4d342d,fec7d557,7cc52bf9,d7c60537,50150146,8a05baae,7b562dbe,b626a952) +,S(dc1f2531,d5e0d304,66b963fd,7d09524f,c66e6bbb,3855b6d3,141db170,9af05fa4,f1644b11,e8291f92,4884d3ea,cf89d857,fc98c3fc,e49fef96,7986e8e9,d67c9b0e) +,S(2b4714d1,fd0aa7ab,6c103ae9,19493efe,4a503d5f,beba56dd,93a8c9db,485c1dad,d65bb3c,47efcc3f,7484b6c0,9f51605,30542a81,d1192ac7,5d506c43,16f41220) +,S(c9ffd7fa,3b14fce7,c2946111,e940ab3c,8199b7e5,40bface2,5d8e3b0d,d1a15176,3b40f328,a4539bd0,a0b71e5d,91a881c8,87b31e11,453d9f5,82066f44,ead8d5a5) +,S(e019b0e7,6b4221c0,e45d387c,6ee41bdd,ed5e219e,a7069b73,d92b6339,2657c39e,792691b8,4c8748a,cf10475a,955aaf5a,77328c6,f03c2e9,bd1b20be,f363f368) +,S(51e07e1f,e8dc56df,f6394d1d,7f2887a6,9e7959bc,ca10af77,cd54064e,6268929d,c09b86d6,6837e473,574bdf95,f0b99851,b97a821f,6dc622d0,d9c6cfbf,18db5ceb) +,S(c5c19c69,9a9b6908,4625d3b9,5c6598cc,d1bba7a0,6d4e2f91,88f1b77b,4aab720c,af066357,107d856a,51c699a0,259774b3,352f8c12,988201c3,c71bfe3,c5d8280c) +,S(7b51ad32,ec4a6df5,f9c952e1,fcabd8dc,7661ec53,2631331e,5205d231,533f8d77,e1346672,ebbb97d8,569a12ae,fea65246,e1902f0a,9cd91894,a4b8aaad,5a02ad35) +,S(be3059dc,3789e16f,5ca8f6e2,c469f834,bcd3fa73,5d84377f,907cadf9,e7eb895a,5a6fa277,ee5d317e,81819fb1,11f40778,ab437567,48eed726,238ab5c7,f4d6ef06) +,S(cf75a66e,810f3284,52e09fa3,15451289,e1cba33a,ff1013e,4702ff1b,92b6f587,a0658bc5,8c894702,ec5c366c,5a6b18fd,86269d5a,8117dee3,601dc40f,cc93f17b) +,S(6356c70,7d5228e5,253f09b7,7020681a,fa64dfd6,3cabd703,91d7f892,d9822737,3241ba88,cef88e8f,98d19fda,717904fc,737a9c77,45838ba3,97141964,946ac7b5) +,S(90d7f09f,7b8a1232,236b4459,b031b0ff,d5e8c903,36a35f04,94a89615,3baf8554,4a612ce4,b18b7ae0,f3c557e,c4ab51bd,7c32f26b,24d0ca7e,accc748b,3a2db676) +,S(170265f9,5a3f936f,57a39e1f,a748a3cc,902b1b20,4306c49e,f4485b15,555f80af,d2f813a4,b329da9c,68104a55,301eb7cd,3ff6dfe1,c4aab9d9,d2fb03c9,e7ae2bee) +,S(2d6316de,37598fe8,ed6e3e52,ac47d442,636a6553,64274692,5a468f08,54377eea,e064f40,7f0d3b32,74a61d82,d7b75006,6fab6447,e66f2d4b,20439303,cdb3a107) +,S(8c89678e,73f3085a,1a766e14,f566625a,cc29dda6,3eb7bc9,ed1195b8,5fa2092b,12b42d8e,91c9640,b8b2509a,157229bc,a66d28b8,157742fb,a12d7405,eec110f5) +,S(ddeb928,de6424c,f9ea76f0,abc208f1,16731596,29fdb2c5,c8e6ed28,38ab6b11,b1395663,9ac1f06d,74aef36b,61916872,7709eaff,d8b76905,ca404459,325839b6) +,S(485e8e44,35c46bd1,d0c2a4db,19b958fd,4a87511a,ad4aae65,eec4f5ba,6cbb38ee,e621d1d8,736d6df5,df2db5d2,36887c27,a93733b8,d3232dc0,95d0b2b6,d0be516a) +,S(bc527a44,2547aca1,91fd98fb,84ce7b3b,6210378c,46f7bd46,bfd375a8,1b61206f,ff7c1cfb,d3c21616,85d6f405,e6f19e8b,6785e6be,ce374189,4efc149b,b3031707) +,S(9911194a,7a75269d,2fda7214,21e4e2e,3666ad12,3e76004,b5ff9afe,9ea89ca8,b1f350ab,c87d942f,1aea75fe,6c144ea2,fc5cc040,5c38af56,9ee45413,17c7bd4) +,S(93226535,e27a96af,7953597,4db1d4d5,4a65d3e9,331fc3df,7f028fa3,288c3189,98007e66,60fc6673,5e8e864,afc62171,49168a86,27d35eea,2d02d90f,e6f15331) +,S(752d352b,2459fb97,d5a0c46c,3fef99ed,50ebfe74,ceb1c881,b2fa518f,3aa59e72,c6e99a8a,2544731e,d932a164,ca0def49,dbfe4f29,f8894133,ff5eed02,66c4fbcc) +,S(ab37b416,dd06a595,ae6fe33d,717c4ff1,fd1de2ad,6c58e2b3,660e872e,aac7aeb5,182f07fd,61ba43fd,e11ad355,d7027fef,714c9b9c,364c0b72,1838ef7c,f85d9470) +,S(d2fa3ff2,b1cd8909,52a0d380,d43233f4,1eef04da,aeac6ceb,46dd8e6e,572d170a,85f3c854,3eea0e64,b8a9eabf,18057700,6c3a6f70,a790cce6,24a3f6f9,5752c520) +,S(e256c5b7,99ae8dd7,1daf5a7f,3dd94f94,e040d98,291a2d6e,9b398889,addff0c,b4375e5c,82517974,b515ae28,54c83443,e8bf9391,cb975013,727e3bc5,1473b133) +,S(30056648,afaac00,c1d77bd7,e7b5235a,fea2bc8c,58b34a6b,83e630d2,3036db6c,16ca5c30,6882fd6e,f7d82f93,b25ad01c,eb3e9e69,b8280f0e,e3aef2e2,ec204600) +,S(8602fe69,d4b1f32b,e8422928,200c5b5,816f08a1,759faafb,29861e07,c5dccef7,59a5ca0,35490ebe,66409e2a,283f4c9d,f6e0a7c7,9dd1ec82,f31f7e4e,4f23ead0) +,S(53bb9f5,ed205516,36ab170,7afc75b5,6ab3a75a,3d360c15,3b0b74c2,d7d9d64c,77c6c1ec,c7a53dc3,8c6540d4,461757c8,b5f7b66,654fd754,fbfa8f5a,a5318cbf) +,S(badc41b2,86982d76,e5374b3,dcf023e,dbc1e187,70cd0b2e,2916d436,108a4328,b1694651,a69dfad,924fdf5d,526c25cd,e732078f,31128556,3d42ebb1,3329e2fb) +,S(15b43026,f5134b3f,1ad441c9,793b9e57,a318d455,fd6f10df,e4b0bb91,5d36600,43568550,60fb010a,6d5123e8,b446a1e7,cfddb6b4,6e4fb18f,245a4be8,f9975a1a) +,S(fcc64bd,c9df17,4d4e8e18,cad6116,a48780e2,a180f7c7,57f532da,9fdf37e7,ede37062,35eca482,b935410b,6d495f53,12b133cc,238eeb6d,e8e5f593,65601951) +,S(f394fa79,254fbdc2,7a1b31df,1f868c48,9b48b6d5,1afab542,2da5b1f5,6f3edd2,f56d3d7d,e7f8d9c5,fd45d6e2,cc2af300,fdce3293,9be5b975,9d81cead,e2000e7d) +,S(3c7c8c54,6b853a5f,97f92932,5558793a,e1debf3,3f680d5,6a79bdef,d4608d91,575771ed,f26feb38,a728354a,6c4023ce,4b019d65,3f7adfee,3c83cf58,9f2fe4a) +,S(51d61258,43b8d621,6c0880d,f04f34d,9acbb422,d8194a98,dcbf222d,cf27fc4e,2d0bbd0e,ae299afb,93980894,bfbdbdf8,6edf9509,ea408286,ef602cf0,ce3b33a2) +,S(14b17f35,ac7b5914,e08babb5,5aa37cb4,26d3bd34,f2c1e4f,45acb493,8ef51a65,a9090e25,cd8273c3,15d1ac6a,15eab027,7981f6bd,a85fb082,2b24cc88,2918fbc1) +,S(59ceb93d,d78ff6a5,f45c5fcc,7c1ecd72,43f2d8af,e4ba8a3f,fb03053a,586387dc,28f4d0e6,38ac1fc1,98aa3548,56f4f19b,479ff825,5d150f29,871630ae,f82f3777) +,S(273bc0a7,eb63a0df,eea8d7ac,b24a5200,a7086078,388db5bb,37a0a6f6,acf24656,a971405,a287160e,22909b67,357fb63d,10af5ece,f725b1ac,17c7fc73,759936f3) +,S(c9b5b690,3a45e529,3a298219,cca93e0,52a2719c,25cf2f50,c4ff7f04,5f346475,7db09fca,ff1aeae2,dad001ff,1fca6166,a607eaff,bdfc327b,9b63e2a8,e97f665d) +,S(90044bea,c8459e34,edf7c40d,2a101b45,66728d1,cf0055ee,c8ea07ab,e39307f0,19763d08,77d4a896,ca9ecbc0,56d405b,5a6ebcce,1ff6ac26,593be19f,63caf6cf) +,S(6e0898bf,bf6baaef,d085f41,da7e6b93,6b6c9a88,3728dc7,8ce7d71b,fd597b2e,4769146,91587abc,c2f21e05,4542280a,f7260a72,e822edca,1e63c089,3bea3dd9) +,S(e94563b6,e0156145,ca2ef19f,58de9727,7b806969,a249629a,4f024b99,8a023792,c8949547,a4d25901,f3980cf5,333f65f5,8e1797d8,b42ccb0b,dc550e6d,5909927f) +,S(3cd027e4,173c2799,54d06c3c,ec05c0d,161a3b73,348db0d2,8c03d097,8921ad64,f0d1a3e8,ebc3387a,ee88189e,c1fb3f6d,5e73e895,7b7372d9,32b00144,248271ba) +,S(4c96f51,2b4f0758,38335d47,db599db9,a2dfe1d1,6783960c,dedda119,693d5686,4e840262,66897288,9bd54c15,219ae871,e6426b67,afa949c5,e1226bbe,1204dfe1) +,S(8b03ca2e,458873,963ba6b,c9dcd14b,59fb1190,3f2330cb,22997bb4,19002fbd,2b29df15,c6be49d8,27981ffe,aae26930,7a461f84,8285f561,4f910508,eb865781) +,S(f4217bd2,32229dbf,39066a17,c2524838,38098362,f0891b69,275dede1,f8e64f0d,7581350a,957f480c,89246800,86c2803e,99ff5037,938e3e19,22e3f9a6,7098dd96) +,S(247274b3,d5095d63,132673ca,dbf0e418,898468d3,9a30c538,a46a2719,42846bf8,4265a1b7,7d1dbc68,956f2441,9c4d2b70,9e71f6e4,6e24e336,a978285c,22a84f11) +,S(19c25b9b,97f40ddb,23d62bd4,a298ce7d,befdf39d,327aca47,61815c0a,28d4af9a,a9b5d705,94cf4c7e,b7e947e2,101b18bd,fb9f2f20,fc3bf89d,f5a63d7f,61d3b3ce) +,S(fb69718c,baf0c5f8,e20ca98c,2fdb18f5,59df27e5,c2a064fd,ffb2785a,73bf27d6,b612c590,4e0b1c54,7beb4e07,82ad2716,84eb895a,862b59b5,cf6476b8,af52107) +,S(9fe6e793,ed1aed77,d8381352,84890e3c,f817ca60,37ee80df,6646a3ce,9c316a6b,17214a01,99370a88,41717ae5,41d35a4,96f8f041,e9b3fe88,4188f6f4,e30b0657) +,S(c7f7ab4a,394f17c8,47010e9d,f1764fae,6159b140,578f70c9,fe50703b,aaba62cc,9673f262,c875953b,90558dec,8ddd04a2,6d7d3ac1,672e3d10,c1a3c656,b0318c19) +,S(cc2e30bc,2d63d4d2,bd0f5bca,8faac99e,a6add123,82d014c1,d7bf8285,7ede2297,cb87a21c,f1efe789,fe72ca0a,b6acc5be,63e6973,2ca899e6,fb9e05e7,72d4d8ff) +,S(6fe7070d,a78205ea,1f12661b,4538d0fb,60b4bb4,9c1709c9,e53de22e,c3c25d6b,2f6b773d,492a82f7,f9609cbe,e1af3c9b,2940a12f,1e615c1a,801c02b2,19e6b8cb) +,S(1affe9da,ab5da7f7,9b331e9f,91eb7e3a,f4dad51,2317ca45,3ae13528,66013d8f,2706d2aa,d3139097,73a0febc,191c7a0a,df2ef168,c65e759d,7c034bac,49c8a92b) +,S(13566dd2,d0cdb199,20a1f9c,aa1ab581,c3ddf2ad,125c13cf,efe199d5,bc338231,b403ab04,67df46b,2efa5b8a,4cfa8bd,97399c87,12ffc55e,cae8de52,d4f6f28c) +,S(135fae69,9a969720,b1c0d899,790afa99,83074584,da3ee8dc,1e011336,dbc3636e,b4260b88,2dc79423,8cd0fbe6,5242147b,c96690da,c391e3cf,d2c53ecd,2ae9fc60) +,S(1b05d97,744364a0,5432de4a,7d840834,3ffe211f,a543bccf,71a4b210,3bb0489,d61c9d21,671b2481,a4ca0c36,7d4ea919,ae1b331f,a8aedb87,cc6b6c98,85616a9a) +,S(2877f026,83a5cb48,7014da9d,419c8b9c,920f0940,718e2d06,d7252de5,5b46a84d,849f9fb2,88d3fdee,de734aaa,3592ef01,483e02d3,62a5e0ca,2ead6c0b,42628038) +,S(8a7024ce,6348d2fd,84be71ab,b363bdb8,86ec6a2d,29d9cd83,1934270a,27370b78,8f520972,9e4e1325,363a68f4,ad53737b,688c63dd,1cec6cfc,bf9da51a,98b29c0) +,S(5ac77dfb,2309c061,8f68b320,bb6eb76e,4b0f0792,b8235fc1,ed768c57,44fabb1d,fab11baa,beafeab4,43a14a82,f8d70228,57752732,7b016b56,32b924c9,ccbb1db6) +,S(aee4edc,93413d53,45e0df68,99fc2193,be318a8b,f279f0e0,56c0ac42,a60759d2,b6a87499,1dc68ba8,503beb0b,8a6c09df,cb9efb6b,a44aed6a,3cd7befb,506119b9) +,S(b51d3e77,741d0d44,3a67b5d5,67b45aac,2008c25b,5196c71d,c9f910e7,b76dd2d1,431c7ea3,cf4f577f,950d22e2,74b7cfdf,2c2934af,ad0215f9,459a802d,ddee4c21) +,S(803a2ff,c59c2a94,65d8935d,93d47653,86e5beb9,c8e702a,12d7b565,98a09841,d5e02ccc,5d8ccf6e,29a7fb76,5a257dc9,c902c900,a4fe16d1,53e9187e,290efbeb) +,S(22c17725,44eef514,5636a398,23e874df,8c58bc6b,6935598a,440ae9aa,5b738075,a67f431c,688d4426,c519be32,9b1dd50e,2c64648,4d3612f,35d52089,c98dbcfd) +,S(ed67feaa,5aab50a9,ff71c434,855bafb0,5191f036,c691c8c5,563395,d2053b86,b2d13cb6,4ea821df,f8829ee1,57cb5fca,81dcce75,d091719c,73cc7785,c443f0a8) +,S(f8cc5ae2,3b670b9,3c27ab8c,7d8055c8,4654b427,f9e6b733,b083fecc,c7c5d375,7dc996,7bc3a2f2,f516aab3,682f8d6a,32b84173,ecc742d5,c8a15c5c,72eb261b) +,S(838891a1,fa46e300,a20fa3d0,cba3fe09,3326ffd7,bcdd7c73,b6e96da,5d591332,d46717e2,c5fa3d52,a268aa9e,a85922e4,547ea986,3a37b1ae,b77d75b0,d48a8fea) +,S(92caeb7f,400d043,6d36656c,8ffd5d45,6097cb51,cf512c00,1fe613e,8dcd5544,2096bc03,dfd088ca,25cb9aef,29b24b99,2b2aa00b,e3429cad,c0976a3f,162e47e2) +,S(2e6226d,574b687c,20dcd4a9,683697f8,afce56a3,1e11cdef,6f0ba6f0,dceb89b4,d9c4d7a7,a7aa5f8b,f30f0930,d6c3bbdd,b1963b1d,8cf24796,141bddb1,ba8ffcf7) +,S(33aab3f4,17d936de,b8c1f07a,9fd5024f,802fa5b3,a593a6b0,9fd42871,75f4ee7d,4c13dc95,7311dfaf,932eb4cc,68c82550,3b185530,13008dc6,878dd092,50edca9c) +,S(ed74a882,a947da53,fd62a242,b67dcd04,50896a43,e9882c84,ada77f47,691e5fc4,454a18f,a8fc6d8d,e673410b,213875ee,35190221,9f7cf88e,f363be08,571030d8) +,S(b5d3f084,56db930a,7d8b4728,2caaf807,be7bca65,bddf9738,67a56224,39723ebd,a89e0271,28d5872a,f4d6b4d7,c5a7efa3,b0339a32,89a12918,2077c106,b61e1a4a) +,S(5e40e90c,1a01e008,d5fde186,283919ef,eb859921,c3ec07ca,e34eedc2,b0f07967,e4be6ef4,26eb3c94,70ee3084,f431c68d,82b2df48,5286082,82cc1b2,afe77900) +,S(2f4b14a2,674a743a,4aefd302,7fb214f1,a7678336,3c9e7c8f,8ebee72d,c5f29830,11b13461,b6578710,677bc733,3d28e773,c78dec4b,27ca1aac,a17121b0,de9868e6) +,S(71909162,35890bbe,6a8f7a99,5b1cc3c,a68b3ff9,7551d09,59a97835,5b7167c7,f9aa3992,124fa99f,167f1351,865a1456,8ed9eb92,c7cb2050,5082d228,4b1532de) +,S(8df50d7d,d3cf99c7,b0269ae5,e70546dd,bed3f05e,260bd834,575d56e,e65f7f0,835405bf,3c84c4ba,5e253fef,f306bd1c,c1240ef,8c03da98,d7c092db,3a2606ca) +,S(5e0f6a7c,e9bc1e0b,852841ad,52045b02,b4fd545b,f2106c82,17a76bcb,441173ee,ad70f065,4d75c88f,e6654d56,fe65c57b,857d91e8,ad6abff,148f3577,7afa2aa7) +,S(27880a25,21a78fca,cddd0b78,5a3da1f4,2e832641,8f804a2a,42fe3c0e,f69b43b7,ac9f930,12402aad,c55c12bd,e450700,b0208d85,a6784586,d04f1c93,6df8624a) +,S(6578c4a0,dc25e068,97482b12,3f38cef3,39cfd6b2,ec06c89f,6da11449,de5e43a5,8a249d9a,41e6c1a5,a776ddfd,ea93ba78,15f2e908,b8b854c2,3a7e3ff2,cb99bf70) +,S(c66e890a,71beeba3,301bbbef,2bcf4cdd,def8e634,dcf476f4,6907a280,df3dd33e,f3510cb8,9c46f493,c4a70976,34a9c502,11532929,ddc883ab,d43bd360,1b028dff) +,S(393f0dcd,24b47380,81197794,58bf5e48,b2f5479c,8efd8925,a1f78bef,6bd16665,9898d880,e24f840f,f1b4662d,3d444c29,6527cfe8,2b6f1b4f,f0e5928c,bf09de74) +,S(337fc469,43942ce4,f1601877,874366fc,a8726f4a,65a9bb26,c6c2c013,856a3adb,95df242b,2d4446de,b2b792a4,d36ceb2c,add17365,380d78c6,3b23b101,1b3be914) +,S(b7edfbe5,118cfeb1,4c1aa23b,48154dbf,5e9d1057,a2758516,7fb8e030,a457874,bc1e5723,dabdfd76,c98f408d,1d8c13e5,7dee979e,83b3d610,981b9718,f1e61eee) +,S(4dffac46,6a507b9c,2e2ee5b6,ca81d8e4,d4e8aae6,3d73395b,dd29b024,51436e5b,af227fa8,ad0bfc48,eb469596,b9b2759b,f41eb169,1fb8f896,1451489,ebe0d5d7) +,S(553ab99,3448879,b42c72e2,97d597a6,24416a45,f5b35720,c93a303b,c6dd0e88,5ba2d3fb,222a03dd,c5302b25,ffce74cd,58de90a1,c3e41928,19717c28,52ad1b39) +,S(b351ad4e,4c7df853,8f6f71d4,e95554ca,3a07932e,7a812309,b0b0c807,f98cea2a,5ba5141d,b3d4ff44,7efe06c9,c624d447,1aa3c3f3,47af74af,49929de8,d8558c70) +,S(844b9bc0,ae73df50,e5b80fe2,fb24a35c,dbc91c61,2753c2e8,e3765e1e,47d44263,80eeb80c,414c3a08,a42d89b8,87883d48,84b38bd1,40f7bbee,e977ff5b,c4c57b90) +,S(f64b4ef6,8fc11491,38ed4ef8,ab2d6336,3d494b37,b56649d1,5d15777a,8cd315b9,c6f83010,5dcfc707,f435cfa0,b6bc32f3,8ce8afb6,28bf9139,dfc47279,cafa56a5) +,S(969ceb7a,441f8be1,f16c94bd,6065db17,942f88dd,e5506742,7cd30dee,cf9ba5c7,a08ae62c,6e6758f9,724baa45,d35f3e03,9f636265,d841b61a,f1b31d20,bc8d65f8) +,S(e465041b,78c33311,d4397b30,9a8ba4de,6b312d10,ec2ca906,583162ca,1c599102,3f21d5d4,416f1954,7af1ba97,6687dea3,520ac304,43c180ad,3892d46b,86618f55) +,S(87b2e548,b019224f,b1021a06,522073dd,46892847,308fffe9,910ffc28,5b702bd0,6e641d35,9e7ff12b,20123b72,f0e5ef16,338c3418,38c2f4ea,6dab94c,7c3f6940) +,S(f832d7b3,590a59d7,ec2c8a63,7ca0347a,893fe461,4eaba097,8707c353,8296ee14,3fb3e845,4624fe35,326062b4,a6f30c85,f0e7101b,a3532455,7687812e,412233e) +,S(52aff21f,fe1819f3,7f8b5974,ab004393,6e99d8cd,8342b7bb,ad46d333,5e4d6651,66d79c3c,c7d08828,d8646e4a,6a44853b,4a49df0e,85c95e85,d9c995c2,d09b427c) +,S(aae62778,8069bcdb,fc91bdb,946cedab,63896817,575a471b,315a47cc,d567c678,f254c548,832d95f8,4f20215e,ca4805fe,356e806d,9b3b7338,7dd36e85,214b70bf) +,S(fc15d494,b6580e0d,e1c769c3,95c5d047,202e8d07,ba94bd1a,f96064b8,d1a4bfbc,f9327fc8,75319184,c1876748,bdbbdd3c,bfee8baa,bf46844b,af465d4e,ec6be335) +,S(aacec857,fa4bc62c,82790a4a,496206f2,d79a31dd,e17d2db2,d99619d8,c3d880d9,6dd1e4a2,adc502ac,150293d9,42a1cbea,a576da49,f1de7d15,de70bd67,d95c5fb0) +,S(c7ba3155,bf34fb9a,2441832a,27396e1,99836557,9c9cb856,44c3ace2,9ade66bc,afccc814,243a1fa1,345fa46f,ee4b4190,96082bf0,7866fa8a,2a887d84,d24192f4) +,S(cf7a0525,3bee95d7,711d8f7b,7b301f5d,8036a487,a55e3860,900b25b1,c4599115,e8a4a9be,7b9cf99a,5dcb4db9,8249350b,7a71a12f,a9582620,eda5a35,1a8a0484) +,S(a4336248,6b999ad1,623c4c32,c79ebb3,4e8d6f3b,484f808b,9a088ffb,2885a11c,edf8eef9,74d3e5ae,2a991a28,6323597f,5a161f9,2eeb9256,207be548,f9b38d16) +,S(d3cdbd98,8cefc8ac,3a442587,51364a10,649243a8,d4a63927,f4fa3b10,b1a7dcc8,f10e26c8,11f4a219,da4f948c,51e8b81b,d8bb357a,182031cf,40ffb464,d5f7d7c4) +,S(fa038534,28dd093,26977fc,da344269,fe3c61fe,35567eef,909a2309,8e5ceaef,f31c5d30,9e234b3b,44fbb52b,8c973aa2,bd85afac,1f9f0798,788721a9,380d8606) +,S(718c2bc,26dba98f,24f34af5,18e18870,5510c0a7,4e228cd6,9d4e39d8,a00a577f,c41d0d81,8f093246,5675a326,d0615b0,294b8455,909af0c6,e557195e,11225152) +,S(378ade51,463d949,1ed22ab1,834cd7bd,6b99a123,52c65e18,70d5005b,e97e602f,76469796,d7758ba4,b3bed1eb,9f8389a2,2a421f5a,20f8df71,72821a8a,508e4c1) +,S(40bd7df9,1255680b,b50d3d08,fead6815,5f807759,87e9cf80,15e6359c,9f138b8b,ec0417d4,2eb1ab50,a469a853,edc65ca5,2fe00ee7,53b30b9c,56536e3b,36144e8a) +,S(f01b30bb,985dfb53,7bb4cd62,18cb0b00,b5a77215,4d84a802,8a4969,260d963f,3cf483df,d6b58eb8,46e8ba1b,f06c2e9,6784255a,2afbf68,613f39f1,44b82417) +,S(2392d86a,1bf9a636,38eaa5dd,76a7c6c9,eabd50bc,9f2ba568,183c0995,f7195900,806cd3da,ac23f93c,3b11cb5d,a3bc4482,ea49ed2b,444c6e64,dc557e46,3ac5c033) +,S(b0e7747e,c9496fb,aa7798db,e616fa42,5b0b7acb,7409265d,c989b8e9,20087133,32fa615e,d0286b64,b83cbfb1,e8c4b133,85527655,48071401,89f42d93,330491e1) +,S(8d1342f4,2f6dc3cb,9bb741c1,9daa08e2,25fb6352,ffe860e1,76a26fda,a0601624,82d35ed7,57cb6f31,81af31fc,36f53817,191fb9b,c9338a97,3757d0bf,50d79d77) +,S(ad09dcc1,d3ebbbd6,ecd7be45,d7eec5d,ef743851,2ce076e3,cfc4f8b9,449a2ccb,26bde63a,e1f7fbd8,46260a64,372d015a,fd3d5ae6,125dd801,523a94fc,8421d04f) +,S(128f5199,68435c1,a27591a3,2edff533,d12a725c,f0c7e121,260cbb74,925cc292,dd8945f7,42f1d013,92152dbd,1e4dd8e,53277da8,bab68acd,8c7c8d73,f5d7e190) +,S(118c1167,196e8eac,e519d83d,17ae1e30,36ff1fa0,cbd1cc58,41cc3763,d1d8a931,eea348d,54f47b73,d5d8c2b4,7eb7604b,53beb345,c82ceca3,4f8639e1,45b2e3b2) +,S(81ee4bba,6a84edb1,83e1f2b7,5103e453,2ba9613a,b612e0f1,27009ee7,7be3bfb,e58208,d152059b,73b42039,e0a7a489,df067fab,1d1c2c04,9eed00cf,893f4847) +,S(494a821f,6f4b2d75,7e5be34c,fb9949ba,b9271599,3e060005,dd807c83,adf04f2f,8ab15e9f,c37d368,3586a1ed,ef9ddc59,8dae7206,499cf53c,31a8cc4b,d7ba15e0) +,S(3749271a,da87c292,724a3ad7,2861a7df,9757e31,3c3156a1,72cd3735,608c865a,aeffa1c1,aaab4f09,3998b4ef,e046aaf0,f404d590,d6cd762f,1aea5745,1f8aa3f5) +,S(103c8721,321ddfbb,cc3ef6af,1ea8fab1,3de6aca0,294dbc79,3a1e4216,563dc1f5,fe9cca2d,b459596c,462fb268,a8115880,16c2894e,e8839caa,362e4813,e8e827cf) +,S(ddbfc655,6aff403b,10a643fb,f62a60ed,656baa39,b10febe6,92140bf6,7b78f2f9,1cd4169e,cc8ac589,de57b5a6,e02e03df,f3cc7bb3,45993d5c,4ea57e6,852a8417) +,S(d025f583,3e51a9c6,a1c785c5,1c579528,5af0c297,c0fdb82a,f555fd59,88dbb28,5170614c,110f31ce,d88424fc,7d569f4e,e93d5017,a8c676af,c7261ed3,340d22ef) +,S(3f349cbd,8ebac9e,c570b0ca,29fe8ce8,dbd98eba,103f131d,daaf193,4fe78516,ec9641f3,175ed56b,d0c8f26d,188f9fa4,b38d069f,cef8f4cf,71acf66f,1ef7dbc9) +,S(7624599c,61e71532,a177e838,ba92d789,5160e43f,ad798fe2,9170e6d9,bbcc11ce,409e8cbf,6aea70b8,21533902,150148a0,7e45dd61,c9c8d24a,b8cc3da3,f03c49a3) +,S(8a1e1b47,3c699dea,aa418ba2,85cf7467,51af1cb0,8bf78228,932dc6ed,845403f4,1bd300a2,7a9a8da,6ed2bce3,73b9d39e,661d02bd,b4605740,20934bc6,abf3c483) +,S(eeb91c38,d9b834bb,5e3945b0,9655b988,96efff11,1e3dde8c,add6ef2c,7ea51b09,899d841,24f83b43,e2ea82ad,3a6411dc,cf36fa86,17bdab35,d83829d1,456c56be) +,S(46360f69,8c023375,6f050969,60b3defc,2a1897dd,44e0e159,d185ac04,3315ef47,ec02a6d3,828246f0,684d2059,23ba73ee,10cc88c9,575fc005,6271fb8c,67e0a4a0) +,S(16e5cccc,b410c20,e8de1e76,e5b71649,64cabc26,f629f8be,36eff4da,66f875ad,a80de48e,e079d9c,bac312e0,70f10d6f,fb875c90,7dd3ff57,c9bf8b90,57c0fe14) +,S(c315230,c8ee5bd8,429efc06,bd089f49,5f4c5a1e,2fb188bd,81e41bfb,85acc82f,71430e8e,cee9c6f5,eb91eb14,20e07b5d,736217e5,4bc25cac,9767a749,a477599b) +,S(7731eb8c,1684092a,d9d2ec69,b50339d1,19436082,c9116366,8ce2bda7,ee80c0c9,e6636efc,22a4c339,f8100dea,a227a709,42d18222,8cc58d13,ae4d613c,a879de8d) +,S(2c1c200,4a5f04c0,b83d143f,ede81755,67d4d54e,b2dc4ffb,de00b83d,9802f68b,b3e2e434,30d409cd,26490c1c,818b24ed,b10e835b,d6292422,45c529f7,6e299159) +,S(2d984bc6,a2031a3e,1a57d0ae,d13d798d,fab45382,d29e4a7e,8e029fdf,2a9d95c2,b169c34f,9932f02,8320ed87,4a74da8b,5cba81bd,f20a9454,feb315fe,a0926720) +,S(f61abdda,f7673f75,74e966f1,c77096c7,7f6e8659,746e45e7,2c9fbf3d,5e0db5f6,1717dd6f,86edd17a,b242212c,91878ef8,9ff1b865,b32b4e24,cfdab7de,de90937f) +,S(a68f5871,bfbce14a,1097eced,68a9e906,c94599cf,b6dd4176,ef946fa7,e5552c3b,cbc07a08,86faa82e,d231eb0,739a24e3,62a717ed,30b0512b,96ac8dd9,56ab05f2) +,S(52035076,1af3e786,60906105,36a2ced0,6f8209d,a0eb2757,cba705a3,467ccbe2,7a01b944,5f63cf43,cba95191,262b1b53,ae0af9b,4b172b78,3970f84f,f8ace09c) +,S(bc8f1537,9d4b9862,a437065b,10f6c28c,8c3323f4,51f2847c,63251000,c9f144d6,894826ed,61d85e90,58c00e74,cc731dd5,d62684b1,ca996c07,ab176df0,e4b951f7) +,S(536b0cb5,81dbf6,ab3b364,64a397d7,2a513bd8,a7bb8237,70a725d2,2eb12a98,85bc95fe,6e160f2f,9a31b7e2,74c09023,90cb2f4a,22f15ac3,3d0ee72a,b666f5bb) +,S(267840ea,e2f49fe8,9889bac7,40eb797e,ee42f922,c1821a66,163a580a,a0df90d0,2c7fe7f3,4e06f09d,a1a2b1ba,79906a0b,18c3d51b,d9b6011f,688c69e9,1504a00) +,S(d8fd79cf,6beed8c,fa57bedc,ca06e367,e0667e9d,a057222b,81fba682,979808ec,39cab613,9cef186a,248562d4,fa581384,d6c81de5,a0f7fc49,140d0822,f6126034) +,S(601407de,9ecff4d0,836228ae,eeaa612f,2b659706,576e2eca,74bc32a1,d3d14b7b,6cfde694,2f0e4495,1e25ff02,2f2501e6,7101828f,8088c7bc,a8ef2b25,88036ec3) +,S(f518c5c,bc0e8541,48c40253,d500a2d,1ea1d25f,d31dc777,d6365050,d3b26804,8dcc6378,2b1ce6fb,9f15c8fb,70c5fd0e,53e4d80a,cf38261f,49a558e1,de637b6b) +,S(16a9bcff,334e35e2,80258378,b2e25d9a,56ce58c3,b3426bd,da82978f,370ee6f6,c7b6656e,baa3864e,bfbc5294,fe9836a9,9fdc095d,ceac6a32,642e6911,a6f3e933) +,S(9d940fce,5ecdc659,30c945db,7151b823,a9259d0a,2a3991c9,f71e8b00,bbe35d4f,32f533d,d96f2e36,2109a96,48ef55b2,e5887387,80de97ad,c7974227,b23fe647) +,S(19e973f7,69c2d02a,8bed34dd,b7591828,5036e724,22227a81,850f3d31,3128f586,f2f5fab4,fe79c177,752ee60b,cd85e625,e459b50e,de702010,a6fbcbd1,5b31aa6f) +,S(ff07bc26,473271d6,4371a4d2,9b59f858,b1b01e40,472bb4e0,1769ed2b,66bf2097,3eba7660,492ae09a,abd7bb8,c5d80e33,44539ab7,276edfd5,b599b02f,27c60b8a) +,S(175f786c,93d89acb,8959d846,934c21df,5dc4f6ce,a9c1e150,e7da469f,d27e6605,343366e5,7f5b9f7a,407cd566,7ef1f0f2,464a2938,61fd5f2c,b0363a5b,dc9c2f9a) +,S(a9027f8e,b662a7e0,d70aa1ac,d3e0b9ba,d4e0bd8e,8bf8c883,40b0a7f5,cbafa761,2f9dfc2c,57bfd835,271d63ac,e3cecdfe,f6e19e0a,fc19ac0f,71ef9ec2,7fac30e3) +,S(677daf40,728a6c89,6804ed3,1df283e,19b41c2d,36a3a98f,d4b93fa6,6699983,1affbe57,3a561872,37dafd1b,bdcaa594,e499b298,6af58591,c6a2ea3a,2016c039) +,S(1105aff4,d4761b77,68db74ca,d9fc7aa0,14c5417e,de1fe641,ed1dcdb1,9b5a3690,bfabb1aa,76906ada,4a1ea463,f4caff44,a41afa71,b82e2457,d2756ddd,14946ff0) +,S(3221d033,16be670b,e05ecc94,8b537ca7,9318eb65,f93ab3e7,32e7f36c,bd06249e,92f7b5c9,85ba3fd8,7b14a093,74d4ef94,130d3642,517369e8,f9de5fa1,c8ccb803) +,S(ac10037c,ad83fcbe,6aa7acf2,3043ed,831dcdb5,66921f16,246488de,6e012fc8,d530eaa8,ebe1d47c,8e57892d,9301c4a4,29b223b2,7344e433,f2bf77c4,d9a20205) +,S(b3ea670b,f5001657,913d5d49,ee1005ea,65fdd33c,4ad81987,efa0331d,b29a3e53,6d758db6,df4275e3,fc148e91,6536505f,6393daef,c54c3b0,b708e0d0,8de3f1bc) +,S(42762d8,bf60f678,924b7cf5,4e8c513b,fa1223cf,98e124d9,18d88cd9,a54c8d88,96be42a1,f0dadc84,64a3522d,e13f10ad,3272505,f7d4fc54,e6dc1a33,ef6b2eed) +,S(7b9e0117,167c51ed,c3f7d2b9,edb61bde,aa90efd,88349510,c4b1cf1a,43a9ae6e,c43e0d1d,8a673cb,1f0d9d1a,e3a4c985,bd47a068,2528ac36,67976881,1ead7a4) +,S(1e807fdf,c69acac4,ba5be7fc,9c617005,d34ff896,dae25e6c,428abb96,3255239e,7db7d6e8,8bf3b869,fe4f7c11,65d85faf,55bd677f,5670ebdd,89d2f8b9,f56d972c) +,S(9aadd434,75384190,a6289f2,32cc6491,fb7a1d86,b04ae9ea,94dfd2c8,449c3961,fc491907,3ffe94d1,3b945382,eb9c482e,9da5a764,d9aa1d1c,c3d973aa,5edcd3f3) +,S(5012b7eb,481fd85,db13c3fe,2bdd06a,720eb4c9,805193d2,b99b4bf7,39802f26,db8555d7,e2cc1858,21920564,3c288352,22086161,1a611dcb,d1e98518,7c46cd4e) +,S(78f9e5ba,91071270,5160654e,2393cd73,85e4681f,8efb4a45,cdce5ef3,d73cc7b1,24c4512f,911101c9,d3a4fe24,b7468585,36cb8137,e13578e,95cc016b,96d86e20) +,S(91a7d927,4de2c0fe,84a10579,891ea8cd,f87fcf5b,a0939411,4612efd4,6bb9f826,d5a5e511,b85436b,89e46211,ec69dfa8,d0ee87eb,7c0daa31,24d48462,10e6c40) +,S(3940b4e9,4626f86d,4592b96a,4f27f874,c37c6cbf,5940de,37dafc1f,35350b69,ecc6176c,43df3f71,cbf0160b,697afeec,1a9a0d69,5ebdb7bc,34b0efe9,2cc68b39) +,S(76e7ab54,8c63ee6a,30c736ff,11762061,283af0e5,5a7dbbd2,23fe467b,85c95d8b,f266c922,fc5e61f1,65bf521c,33235e05,113140db,6a481b0e,e4f9976d,2696fdce) +,S(ecdc2e5d,b8d5028f,f93de09,c9f5a6b3,c439b14b,3c322c53,94622a13,80f04067,e70df9f2,bf6b0a35,9029f937,5e74aa93,4014e78d,e68e29b0,9d96da05,5ff78112) +,S(ccfe93db,de087d5b,9194b286,99243dc0,91d4dd5d,7189a5e0,d5621311,9dee9a94,2b0a0a20,cfef0c73,addd2dc6,d9b4ba7e,9e44f2c,26d9f376,8ef90241,b2d907da) +,S(4e82a200,e64f5b3b,3e473e50,e437c224,e21bf875,ec8ac364,7e599ba7,84f02574,ff696d9d,2d9cc0be,217394da,85201a1d,208e4309,9b9372a8,19f6d069,88839c0a) +,S(a292e105,6f3f0a1e,e816a49f,51ef4760,91324316,2e3c734e,802550a2,c942c17f,479a2e95,7a05a686,1c08df3b,221ed2c6,5d192181,189269fe,9ef42a8,e71fd6ef) +,S(47279a82,8311aa4c,b6138c9b,ccb2fc6c,c4b9d27e,2e334389,1773fc1,ee09f461,2480a805,496e8bd3,e02b36cf,8081bc5a,254f821e,40c0ef48,291ecbb1,6699a10b) +,S(b903b4f8,2126fe06,44976643,5f0982f4,394db57f,b473401,9536740,595562fc,e8c97572,bfae91fd,61cfee63,3b24bece,e587291,f65a6aff,b71c6a3b,615e1361) +,S(16b9e3eb,eadf469d,368ab368,431afb79,214bc728,5df165e6,c3bf98a4,701b05a5,26e74ff0,64cf5de9,ec3d084f,8d75337d,c04918eb,f783c65e,b50b948,d48eb003) +,S(4927fd2b,c17b92c1,3b3ca52e,759324ca,353e8505,ba81ac24,9003091d,1cebbfa9,95a6c16d,c4c52f20,2883ab4c,a9c050c2,b2849b71,e19e0df0,8ff495cd,f62d73ac) +,S(70c503d,93e47,10262c8b,b1630409,979b673a,79ad08b0,8d855365,f51ffcaa,2ad207aa,5fe05144,95baf3f0,b0807efb,d10265f0,5de63d6b,563e4ac5,e70b89ff) +,S(cf598642,32972f7a,a6cf12ce,dc5aa95d,4bd0f96e,38ed546b,9ba9040a,e25a9e6e,67f2f83a,f5f09b52,caed83e5,1b9d6de0,58a6e9b8,96d1a6ed,d7ab4d40,30332027) +,S(617b9e5a,8d0b8c44,b186aa60,281cee51,dc224e2c,9e1c4f07,c342a180,78b271f0,b64d8d5f,bc2907bf,90b983cb,a6126586,dbb7e7f3,d95993e2,2bc1b8f8,9472d200) +,S(d52e0c90,c69cd94e,3c849ea7,dbba79d8,6aed2a3e,3ca3f106,d2baa2a3,59c50534,36c8390c,fe400769,fd2b2fe1,b6af62d0,7fbd667c,fcd9a132,b6b4f974,a7e454f7) +,S(ea474a01,36d16bc2,fae6f568,9ea3c035,aa6580e6,b238f42f,e2ab19be,81330a46,d09379ce,aa89765e,1164f438,4b0f0ef0,c1e8be86,427c7455,af98e4b5,c9ee8145) +,S(4e5dd22c,b6a4b205,97f71375,cc49ee10,ba6b82cf,b06cf065,a25d6f5d,bef51d70,d4ff2ed0,55279f96,344dc16e,10e3410,40378ff8,b8d912c3,a17cbfb0,70588cf0) +,S(774587f7,f3038136,131fa9eb,f1eb3407,9e473a8b,b9e5acb7,317ff5c9,17447878,9d3779a1,2b067b00,d2a2a3e9,46f8078b,e6279024,d94c83c6,28c0cd48,145f510e) +,S(ab807569,d1a3630c,e5447b36,c205e601,a6a23069,4d2c0786,19d9b97c,e0ba6611,2b9eef15,54f8ee6,e11af6b,6a1d045e,d47b8821,ec84c6da,366afe10,2730e9ea) +,S(91602928,ed3cea8f,4faa254c,28962b68,c4437602,3076a5c1,6bad95ff,3e328049,764faf56,2d8de3c1,89b7fef7,62012be8,112db4,a3201692,95c7e76d,fcbd3be9) +,S(6c1ecc2a,258a44b1,ca1d964b,c86c5600,49fdfd98,b362c35e,3c830647,cf1266d4,1f9d3edf,5e0e8550,e4534c33,5b07d731,19c49c4e,29397f6a,938fc1bc,ebf8fcef) +,S(e176821a,4006b429,999de3a4,ada09166,f6e5deaa,f09fa613,60bd3733,db8f7084,7d07d1a5,390325d0,27b05e18,d90e7659,f5ef4c8c,c7c6dd00,c7a5427,4ec91842) +,S(2ae0b70e,53536893,8a7faee5,da5d8dbe,e5af4873,32caf51d,7bade79f,eea50df2,e70be2e8,c8110c31,c3eb5e36,31dc5a5e,bebeb184,e6b9f081,bb7236dd,cadf903a) +,S(1c686fcb,d7a751b2,d420614b,56d37c8f,81c9a600,335fa911,835dad8a,7c663e24,4fe2eeae,7ea1b639,b893329a,a65bbfcd,b9fd8f07,28614bd8,6a5b0712,76aa576) +,S(b369d7e5,c31c9a56,f480b44b,3354be,84145c3,97dbed68,d87a4c3c,be31dc5c,50800690,17d31473,59bab021,c876d152,c17ea8e8,b5c95762,c749a2d,29adf3cb) +,S(522d3ac3,42a0ff3a,9a8eb5ba,54ed4dad,fbcdb48f,53bc6e1f,2c0c12a5,61cb1e9d,32ed883e,1e1f562a,50063d79,45075ad9,9ab55ba6,fdff0d58,9d2b4a4d,10df82fa) +,S(7d001803,7b722a7a,17d74696,fed9bbb5,f3d5f6f,d8a623b6,9c952a2e,f2be939e,81ee30e6,deb893d6,274c4b46,8533d845,38073ed8,68b101e9,515ebd84,70e368de) +,S(c76b074b,b228dc24,1dc9b704,75fcc52d,a3f3bf0a,5e954910,ca4e6f16,bd298983,4fcc807b,4c1e37cb,6a6c42da,b939a7bb,6f1d5ec2,87320d74,408622fa,ece22266) +,S(8497f267,99c7f525,83c620f6,e284a9a0,35130556,9d7caa1a,f118de41,c51b05e4,3b861f56,16ce6234,c6847273,16bfa629,7cac9d5,ff562f66,24b26146,413960d1) +,S(ddb10ff0,a217ab76,241a587a,8cd46cdf,ce6ff5ae,e39d2d85,7f47eeb7,6d2bd68e,a4110196,fd0813ea,19846dc7,12537cb6,8faf9333,19e7e49b,553e523,f07b004f) +,S(47eb53da,455cbbb9,a8330e12,e0210d80,cc565661,348fec72,529390e2,a75b812e,88e4a8f,91ada187,5734dcf,42b85e2b,a3f5fc53,1f1c1df1,300c3d5f,e811fb70) +,S(eb0a8903,42fb2710,a4c7811e,78384918,dedea7a2,9bb7da46,e49211bd,95da1426,8d69a88b,d85e2edc,d8264cb6,c44d8218,1ce8946a,818c9d4a,6f757bf8,5817ac56) +,S(d400c8ef,655d3879,579ba4c,488c3e55,af5af95c,1701bd15,482b2549,9334906d,e7bafda2,5a9f5934,6bcec6e2,a9408f18,e81b76f9,7a9d721f,496c27f6,42725e5c) +,S(bae81c3d,1772e104,66625f8b,ef08d4a4,5f07892c,72bcee05,fb7e382,2a6261fc,4ce5f14f,74f28b03,34a6cc64,452b9049,8460493f,3b64d9be,2ecf98ef,c0270377) +,S(7f575b9b,46bd0f74,555a363c,d80122c4,232e8c53,a6624d8,e13b5c06,74986961,edc0ffd5,952fd4d3,c752271b,7cdfaf3b,4cefecd8,66d722f0,3da1e97b,49aaa080) +,S(a86871d9,1292f7fa,7249ee8,e77270fe,e7bddafd,3255b4a1,16851ae,411293f3,c21ee1b0,bd897d82,566cf438,b66439dd,3dfab0ec,3c7a4038,ccb86039,3b0b0187) +,S(c320cfdf,9cbc6579,8c8b33c4,d97d690b,64ae90c9,a5070646,8c8791e9,1ee4a18a,8f1a295b,2a3c8c5c,3b5bd8ce,e3380a04,819a8658,7be87d03,73a0194c,fafac74b) +,S(169a1b4,f2eacec2,be263ced,2e0576b4,ae77b863,525b2a9f,f8fc05d2,c07a8fc4,7703f867,d997a2ca,bb697f87,b97ea986,78777183,aea81417,1dd0946,309e77c) +,S(218de686,ed51588b,1307d8b6,54f7f6fc,e673cb37,553d23b7,b8c23367,d6b7ea5f,98325ea9,150a100a,f5d2af49,2fa25b23,40ee9606,1553c3d7,edd4d870,5288b8cf) +,S(c7c2e438,d12ed0ac,6363564,8bf2899c,d1b39b1b,200ffeda,4ef9cae0,5d8381e5,93a36085,fd09ff08,b83d50a8,3b140edd,a63e2442,e4bf4d1c,7ae44963,c1646f41) +,S(12e834e5,605d6e35,8cbe550b,8fe917dc,59194c58,66b265cc,50b00e3c,38fe058,6a44ecf2,17f46865,2e0991b7,df3b3565,444e7e29,3a2d6378,61a8f3c1,5ec1517e) +,S(30f33c39,af77735c,749fd328,e228ed67,bd625fbe,79dc3b06,ab31c173,777308fe,29a00f66,1c998f1d,bd62935e,44794420,7ffd7b1c,24f63806,9206e1c6,5c3d9604) +,S(c5bd81b6,b057e687,19c47964,d55ab996,6c7ea99b,25c1c2cb,50d6fbd9,af492358,5a6b1332,8562599e,e87c475a,1e7da886,c460ec67,ad81372a,7ed39498,41225e5e) +,S(1fd0e769,9d931a80,dbcfaf1,46b5cebe,b9547f8b,b7fa75fd,b2bac9ae,ac0908ab,11770838,88607506,bad87ba6,38c9ae26,d79a9dc6,6746816a,b9f21d64,fea96838) +,S(ac14a06a,4679e9af,b2ea3aea,15f2749,bc9610a3,b576a506,f96fa41a,76df97de,5aafae08,5b8a0a7d,48ebb339,f4dec906,21bf5162,dcb85100,eb7524e3,a65a4921) +,S(ad583074,13a4eba9,ceecba6b,c568bfbb,30566031,c06e8e97,5c5d992b,9ed2bd33,c2bda9bf,d5c87e0f,a663a40,34162dcb,29b61800,c1dcf02a,c11fe615,25af1772) +,S(5a8bd45d,6fd468dd,41ecafd5,aa072227,6d0176be,81b92061,b4277b73,32acfaab,878a8d5f,7903d74f,6abe28f8,d94d4767,3698a7e,6acbc32d,9e92f9d6,bf31c2d5) +,S(6690c9c1,d9666be4,56d3540a,6a2ed706,372256f0,43265478,7b54e56d,f28df74a,7ad591,23772578,999c90c1,24704c13,a7193b62,64240aef,63d12d88,340762a4) +,S(3c051303,c113779a,6137f46d,38d1175d,8778e460,8b1e0abb,f7bc4539,80b64a2f,9d4d04a0,ad1c772b,4a523c13,cc48990,c733eb87,993cdbaf,61dbf75c,4f6a96c2) +,S(1fdff503,66319168,f5dc381e,8085ac92,223b3484,a8b362c2,26d1f00c,976f9f04,5cb328e8,56aaeb05,62017bf8,65da44b2,94ad7404,df11e117,8426b79c,25a49fa6) +,S(ae83da7,52ec2098,a4a593ac,f99dd58f,cd48da14,361feaae,6cd992d3,96b4fd8e,6dc92b41,f1e1474b,a259e96b,ec4f2b36,108745b9,a500ad9a,edd9c828,4c3d129) +,S(c0f86e7a,6c0fee54,a2f73d2b,6b1522e9,fee994cb,d5089886,d98a4748,161811d7,4aea0b92,2441c184,c347243e,5ad5a064,ba00768d,52d7153,a5e0838f,f497d551) +,S(8e5b23f8,85d683a6,a6a657ed,d2d2f403,1320d4c0,83340126,d2958dc2,4c5f5ba2,25c6db80,477f37e0,ed6c6e3c,c96b1c7c,f9a7ca16,8e940e8a,b2946c7,3b02e61f) +,S(6672b4db,bb16c672,2592c239,7d9ea25e,f081d48d,aeb42c89,ae6c42bb,af09b3c5,a3778252,e239ae3a,f64e3b91,7f646181,4f71753d,b4bc958c,b5bcd3ba,4b5b1a4e) +,S(e0af1a1,b894fe1c,1f7de814,1a393a3f,b12b31e3,fcbd8f2a,356e61f4,402d76d0,37913973,b4de9a2d,23f4f795,709fe825,8eee8aee,afec5996,83d924e7,21fb9b0e) +,S(94355dd3,c3d73a1,cabdbe7a,295cb015,335a5a79,dd301718,10c245b3,d0c840ff,31e96abe,22a16033,56681d3e,e5e8330,628d8090,bfdcb925,79074cb6,82845475) +,S(fc340c8d,1e3e2849,1470dfcd,1c2fb445,e807e1a3,9c525ce8,141b292c,596d2e36,738e7213,7580eb3,615b7eed,9b069cd2,b644447,24696a,7cd22257,7a931464) +,S(c0c4e03,61d923fc,4201872a,1ff525c6,1697412a,7bea7a92,596e9204,f1af90c3,25c4c2c6,d6593ef7,ff5f04fa,a5b0b2ca,736ab33f,5c36dc63,433821b5,382fc4b2) +,S(81afd4a8,d666b97f,dcdf0d36,f2dfdf7,bf7c3322,69728b75,26dcb3aa,df51365c,b7fe7653,2c8d48fb,8c3d0382,68d57839,edf80cb5,732ae303,5c1b51c7,cb8d3efc) +,S(140b32fa,62130c2f,1d49e39d,15f9c64,f5cf54af,3a0c65b2,298e91e3,21b9477d,bac2aca4,c96d4363,12107b2d,8996875e,6772eaea,a5070920,39c1171b,9113b516) +,S(17418ab7,46f1eaa,b98725ab,68bca72b,9cc645d9,cbe0835e,4bf91a7b,3939b98,dc964031,b1292c03,117a4ce8,b3341e23,da54a539,fdd31126,41706257,bb9382b6) +,S(8f0222bb,6cbf7491,12b95780,b50077d7,be2a27ec,5bb6dab4,923cf36d,3a12618d,8df2a149,abcd276,31265890,471094cc,fe6bd8ed,b75c2364,6f7dd54e,30c4c6fb) +,S(e3965324,1c543ba1,2d0ca0ad,88c26ace,b265f42a,41d2b7d4,f252ecf0,a48275d0,44467da5,18786654,5722fa15,36c187a6,f67f2e8f,5a424336,b0ce8bbc,a53f9cca) +,S(6a4b614c,41c4a6d4,ba4f8c53,26d08bc4,ecbc72cd,d2415c3,b7caf285,acb07c1d,76dd92d6,3f85e1dd,685faa18,283f8667,2425de4c,95a7985a,cf5f74c2,7b29b576) +,S(afdd252f,82037cb1,928d896,74f3ff64,57b94412,b563a4e0,1a32cf2d,1afde412,d57f319,300a6f93,1155b0b8,3dd83a5e,d2b4a0e0,5d58786a,8024627b,73360609) +,S(65adaf88,4297a087,6ec0d433,ea82bd5b,9e3590c4,921fc2ef,d1fa0b12,cc74808,30e4ac9,77b97c4d,b7516c02,df65de7c,d9e8ae08,6146716c,3da4b26b,eca17033) +,S(eba0dd63,c667395a,9e9eda33,93f5d101,8462b957,1aae6a09,cb723e2d,a45704e7,8314c569,49b7c144,197647da,baf857ca,a164e385,62c1cf46,bb30e7a7,a8dcb680) +,S(d3341d4f,1f967f0f,10932a5,351c6c22,c748af09,1d547cc,2766e6f3,1a9f570c,9694fb83,74177e3d,80775206,f58b2e12,32b87c9a,21f060ec,19152435,45502951) +,S(1596b717,8dceadc1,b5f3a5de,89c089f,62506b6e,2c6e49a7,f1cc4a11,e502079f,97157538,2aba92cd,8f009de0,9dc143c8,51b0f3ea,31067381,48a34858,8c085d49) +,S(138c0bbb,17c63e1a,a3e65b50,3c602d8c,74178847,8dc2bcce,65b84cfc,95f7ec41,bdf88a3e,34f69fc2,415fb95d,a4b6d3b7,f159651f,36a2d4e0,b4063e5a,ee86dafb) +,S(7d99662d,e6274432,65ab2842,a4ed79be,480404b3,1b847c01,c557e277,ee073176,e2ceda2,bada9b63,ff2fc022,44124d3c,b83dd4d1,f3fb74b2,763ecdb6,46e4cbf0) +,S(9e3f4ea0,b1db451b,7d713eae,35802229,fabbbfd,6f6d0ab0,af7483a5,37992e63,a9023199,10301c3d,78933143,b54e87d4,1419524b,16c9c00c,5550fafd,e6be8b65) +,S(108c8cda,d6ffc3d,b0863a66,43f0a4bb,efcdb0c7,202f630f,f971a7ce,dec3ff40,847ab4b9,8fba1fbd,15f6bb2a,4b3d1038,726c2d66,f4c9b8e9,f740af8f,5067d95f) +,S(5258a6a,7c85d4f3,e650f0e5,1e2d8254,8a768c0a,f276da55,27ab0050,6e97e710,e5605a14,31c8941e,4f2aa3c7,9ab94e3f,ffed156e,ba2159a2,7fde377e,e2ea7e26) +,S(c40820e7,f46acad2,5bffcc6d,745aa3e,7036f26a,b6ae85f0,e3cb8f95,29dc3f3,6ddf9217,88249ac7,6ac2c9b0,1dc5e62e,91587d15,c76fe26d,6d7c3b2f,feaf3b12) +,S(c135f50f,160d04e,43148abf,d76509c4,e55aadc7,554e4ea5,823e013e,a0490da7,f9a597a6,b26a1485,a7d5a91d,486d099,93de9a4,6484edc8,72f8f4be,710aae69) +,S(484be5a8,b903230f,991c3118,38d3ebdd,9759f5cb,11a9d0a3,9bb11a03,3924268,18f80a36,a3d70e67,5bd65253,a1ad3e40,a3152d55,c1141315,57f8585e,315ba462) +,S(af67562e,998a4032,77fc5827,cf222258,cb2f23a7,4dcd0d4b,315564c3,eb66da9c,dd5bf89e,c9b2c178,6adea201,d36f3a66,771b3f2a,cfba7acb,c30024fb,fa73ad6f) +,S(38dd6cd,475f2808,938d92be,8ad0c955,f3cf3255,c3602b99,3c177e06,553af411,1abcdd02,d82cc0f,680362bb,f898bf89,304d681b,6bc3afcf,65454ab5,8d44214) +,S(ee37f0ab,3db1f9b3,2da0031d,48f53766,3530b68a,f47bdb87,679426b0,d90fa6d5,6cdb12d8,136fc9c6,2f025cbe,7882ef21,4e7b09dc,9fba4cf9,b021c4d6,610c0397) +,S(670684dd,fcc19f15,ba9201b,ca36d143,47dabf31,bad07e8d,68e6fbd,2630a98e,77bd28ff,a9699737,1304d4f,82d29d1f,a69dc123,6f2ee438,cffe3f2d,358ce706) +,S(1654f447,78026345,64e3608,bb29004a,812ea39a,d96830f6,66a92f5c,54a79359,11d91962,3f81fda0,36d2d021,607d9750,cbd30359,83c4f47a,21fc8116,ca93cbe9) +,S(ccf0a89d,429a0274,7b000326,d62825ec,6978f24e,8f92f490,e8cab1ab,ff90bfa1,5f047694,375222bd,5968dbcf,a331db73,85b4df25,72791c53,42c62467,3d84400d) +,S(45d36700,32ab11c7,897d648,67ed4d03,a9504461,bf28f3df,7e1276fe,4c1de796,67e837fb,37108844,2338086a,d1506453,ed1f37ff,fb14e617,93908c7c,2a8e6fa4) +,S(9af3b6e1,627a2ffb,c18ddf33,b668fece,bbe7ebe0,986df82f,1b2fdfa1,56dbc565,55e9562e,882f6629,1eae4c6b,5a82b0b,b5bc2cb2,b8f884bc,3e45a126,aeee19e0) +,S(ecfe9d5e,d3b9fe15,c320e05c,7e0f3b1a,5fdaa9a4,5e1ff99f,88012642,a5d45b4a,b3cc1fd0,e0117151,f53ec671,e1d207ad,5f2b0755,8956cf28,2be5228d,db5d48bb) +,S(6e482326,6ef64478,dfa30c6c,f3a951f9,fd9d3108,dbe80e5d,3cbf2217,70f5483c,97a3f599,906b1738,372212da,80309562,d3d4179d,d2abc2b6,41ef2f4e,6561095e) +,S(3de89149,f6c939cc,5419e4ba,f0baa93a,390f01a3,4ad0fb7b,3b170e54,60a380c6,e9a07758,1c4617c0,b996ae3f,b250b723,874b66fb,85a6d826,cc5826e5,bb068491) +,S(d4d28193,53ace188,703643d3,84622505,d9020459,a600349e,69016cf4,faed2e25,9a494bcf,e15bcdb0,97513682,ef09890a,424d6bc1,ad1cb73f,123ff5f6,7babb364) +,S(f694a819,fd0f2035,1193eba,c18e2db7,9c219191,2bfcc60b,6af8e067,2281095a,d80d1652,1e43f18a,4b663898,bfce8670,6f53436d,a30b0663,9ee6f99e,ca87e667) +,S(af8595b8,8de7c459,7d1d1614,ac897de0,5dd9ff05,593dd3b6,cbac81ce,d2e1fb5d,e9dcce9c,83ee7217,857d5ca5,a48bb720,69d467b6,897be9ac,658dfbef,3d40e6a6) +,S(df4177f6,a7deccf4,c618b710,b096bb4e,b310e4e2,470b5795,6a633154,8ae8123e,3638e20,5d42ebc6,7184e567,c177f479,18fbf9b1,e4f69435,487b289c,35c7fedf) +,S(ef50482,30344ae2,70ec6d30,f4daea23,ce7b64bd,b101638b,5a52ff3d,6dbe9fd3,a6eec78f,e38a2e6f,a9c062bd,4ce28dde,c5d25fd5,a3204281,ae30d79d,99a2658d) +,S(f89596f3,ddd57abe,3344080c,a6d85d0c,492261ca,7a698aad,ae00dafc,75615954,f8d7d2eb,45dc2b9e,a0979550,8d9da016,20b53a19,b4150ca6,36bf0f86,ab3dc0dc) +,S(a7c2ffd7,50b99bde,92ac1419,57d8df27,56ca49e9,3f4c7434,b24899f1,24fb3cc5,a1672ade,aadad853,47d1440,d573e3dc,91987d6d,ce55c67d,665402d5,f8b0a289) +,S(463f5f25,b5c63801,a6b1accc,6662e6de,5256fce7,37e1c875,2b4a880f,19ceeee9,858e4a68,39eb48a7,f1a7de4a,514fdb46,1f4203c2,65e308d0,3fb9770f,1f8f13e5) +,S(4c99bf38,57073c27,cc92314c,f260dbf9,e0f4bb7d,1a5ffadd,a808783d,3bb602b8,aa107422,ac7dbbbb,f6e8064d,22a5526a,96bcbca9,9dbaac33,c1f35b2f,c6df568f) +,S(f6825035,64438569,aa7a5a4b,e2468453,8d8bfb88,f7e983d5,33ebffd,f8588520,84ff2ce6,9d79938d,fd910bf0,25a2fe8d,e6013c03,74784b73,168049e,c851cf15) +,S(62ea02ca,16b2e528,2f913a,36463494,c00df046,8afbeb34,cc9573,b71577fc,36be98bd,2c83e8cf,e0fc1aad,98ffd9e2,9ccbad8f,a25b9da8,23de20fb,fe91f9b3) +,S(ea34600a,7591c57,d0a7e351,de6e6d0,2c72220,62a980cb,bcca8966,356a2984,25eccb4f,a6102f52,60e4c545,57aa960,b427e372,80c35b8,edad5f52,1bc93aca) +,S(5b76cc79,2e78f177,97b32c46,92f2eca5,1ae1b7be,a753ce98,8e58d116,6c667027,2e01ba25,799e1b9b,3416493e,3fe9fff2,c46e634c,2175a30d,7458429,3ea60b5e) +,S(62a52d8c,d8562624,48ce5315,28213ad0,6fc6044,b6490db7,c27b7662,bb66f07a,e8f5e0aa,aaa65c7c,2b51e665,f3b31157,54ab9fdc,ac492ee5,fbb2a348,d8db5376) +,S(a276992a,5e155e29,d091e05a,5f0513d9,ff2dfbee,6ec1ffd9,a91f55b9,bca641c1,3d480cf4,e3f6efda,6cea9cc9,d769b16c,d1f3d64,23aefe9a,abc82b8a,adb22e48) +,S(bdb5fd64,bc99741f,a2a672d4,a895aacc,98886a9a,d38a486d,ed1915f1,f0b515f,d9744595,ae6e1e64,5a52a094,75ad094e,6af9655f,185ab90c,c03262cd,39794f93) +,S(cd6c6829,9d2b1c60,9b70153a,45259261,91b9b9d0,9fcf0682,d312960c,c3f82c04,2178ce3f,1bc21a2f,7accf32a,b20d625a,182f8733,e460e8b9,e1067dfa,fee6b36e) +,S(ebbfa9e1,8a4107a2,475f99a8,672be581,f32426a2,d774acdb,efe501c0,af97a0f1,1845a5fd,d71baefa,64cdf1f,d1211158,a70752b3,adf2b4d4,b5bf1a9f,7a0abdf2) +,S(bd5af866,4d51256f,455a3190,88b754a5,34223135,1491f763,3e090bd6,27414fa6,f8b5bbf2,2154786a,29f75090,fca2e149,60f84b47,50ea8db,d7c309d2,52233f3c) +,S(34ef0b38,5a8e776f,1c3ff95a,5852027a,ab6d1da6,6201548a,fa181c81,55382b77,958180ce,9afc501f,84d4d3b4,eb556ca2,e6ae8dcf,f949db6d,33cca5e2,9b357280) +,S(4b5f6663,11e6956c,e186fe3,85f00069,c0dfbf67,e4130bda,64e0e89,1abaf471,a6cf3571,2e790d5f,b500324,773281c,43f6d008,f6578ab2,b48fe4de,1ce3b545) +,S(cb22489c,bac94ccc,887a1d7d,8836cacc,c8c2a43a,4e09595c,39cc6064,ca04546c,dced75ef,d7ee6ae0,18ff2690,9abc82ca,da74e823,c62e4268,606334e2,969bf94d) +,S(b069007c,4a50bfdd,38ebcbe4,31a952ae,5356d63a,9f4179b5,e4cda3f,fe478c42,bb90d5c8,e66c6194,ec55acc7,6dae5f0b,dd498746,2d24a265,c314e93d,618a0f41) +,S(18722ce2,9d9cf99a,e52a420e,23fd8e85,e1582d02,c520d4f1,16c2b82d,464091f9,9fe257cb,5cd5f644,183c8461,ac2dde6,94cc84e9,76d9c623,584a9d94,40fea12b) +,S(a1562afa,6420b34f,92a78951,e9ab1bd7,8772a181,636df487,81bf4bc0,b16bd196,ce4435e8,7105d006,9b353734,5a503e0f,a692d3a,8571e8b9,66d9a722,48ecbd1c) +,S(f466db58,b4fae060,b12b780b,df11c7e5,ea8a08cb,cc50e68b,f5d85926,b9f3cd97,c86b4153,45515a2c,9a831ff1,d6377a8c,cae62456,f207df2c,42a1e767,684f79a4) +,S(686a1d05,e2bfaac8,e31def35,21876c81,27be2ad1,34ef3dcf,6de52778,37278176,111404e1,7f2153ab,43597a74,be1c278a,998b3695,5f4d465f,271a6a46,8e9e8986) +,S(d8433d5f,e5ec423c,73bb3ec4,95d06cf5,3a77474c,1e97ff97,f72a0457,9a60926e,3df114d5,a9d63dad,ada0ec4f,f98cb8ce,f1ad419a,e2c23dc2,f24048b4,1879be1c) +,S(3a9a0aed,4581937f,fe49392f,488053df,349f0194,9666418f,16e56fcc,533cb73d,c9db1a90,2d6676da,251b4be9,7a643bdd,d55c2b15,95ce51b3,281cc866,40cb558b) +,S(b35a204a,28884708,3091abf3,34be3696,b1082fc7,5caeb0d5,18387aa2,711a4190,65f951c8,78232345,487e8e,376c22bc,e5d9e8d5,ac04ecbf,24cafef9,4de358f8) +,S(81ff6fec,11f48691,59a2d23b,b2a6a9b5,68c5e6c5,1df9905f,ff73e1cc,a2c505a,c7b794cc,d657519,fc05632e,33e23474,654f1344,6ef1b4aa,1a1da18b,4acc1e49) +,S(3588a9e1,7feaaca1,a62fcdfb,3952beba,43116167,bb238f10,a41fdfa9,bc316d8e,acb24c1a,58ed9a0b,9744fa76,df4742b9,b440347a,4b4446ea,a3e55234,102d4789) +,S(7eb13fdc,9588b724,e7cddaec,498a8b15,d57a49af,529c8c9a,cc8303b5,e81f91c0,89f6ac3f,38351f7d,e4b293e,f61a87a4,2c08a4a3,ae0c160a,563ae5af,dc62dcfd) +,S(2f68c6ef,b363459d,862710a0,a0ae67c4,afc3cc00,85f02e21,ca4d764,3c76e00a,f8be9930,3a3aa7e5,c50d3f53,9b88bfad,60f05fb0,1461d7a8,225bb21e,18616cd7) +,S(b11994c2,5e3e6d0d,917ac215,c5f39606,3b9b764b,5dfc2b75,ab8ef880,3b57d802,4a7ae35e,c525d91a,cfbdea6f,cc9afbc9,b3cc8c80,acb4385e,9bc20158,f9f2b55b) +,S(d8e7881a,e1e4d04,c431c097,25dd4cd9,b4efb4c4,3896256b,7de47fc6,3a3b30bf,ccaf57cc,138f0bbf,5dbde16e,91d14875,c0d5ea,3842e864,be8de17d,875fef67) +,S(88e02abf,a37ffc97,f564cf3d,8a086c8a,62c05720,802319a2,30ab7cfe,b448c478,ec2d3169,15dd0c2d,6f03f851,97a96f9a,6c0772ee,df0c4524,c08f58f2,39c89308) +,S(ad258ffc,b42d127e,a875b181,a8883a5,31d18a64,d75a6dd4,4111d697,90752d1f,acf5ad9d,bfbb14cf,a25748e3,96bf23eb,aefe1bab,6550b234,90ca0278,276e9db4) +,S(a2bfdb6d,38159e5a,604d564e,af29cf09,a0a8ac79,1aef5a63,e1c05da5,fb6a0c39,7a45c5c,a7ee7c0e,74c05914,1e3d898f,ab62a8c7,31ca0a66,a937ef55,51dad999) +,S(7c7bb792,ef21cd53,bcf33a82,6a1ebf93,e9f3357,51131f9b,36f659d9,594f453d,bc98b32e,173d91ca,e9daf137,766c5ca5,6e0434f0,e2eb070e,7fa7f8b2,e7364635) +,S(40a2000c,dc0c1c9e,4c037a3f,3113b48,f26730ce,11e03b1f,d2f07217,7816f8c,1966929f,210afe16,9daee99b,7b48eef7,27e1703c,8bf42006,922c0496,888b690a) +,S(6427d169,cfd7e9fc,1b310f68,e3371720,28c17dd7,c6d5a32f,4ed6d41a,634f3f1a,b1a71fdc,6a030ca3,d7dc2ce9,27f3b9b3,ca7169f5,2fdecba4,bc85e32b,4e52fcff) +,S(723013b4,24fd897a,1e8ab77d,ee0618c9,3c832117,94f8b822,9d7a9e51,1ba8194f,21acfca6,e5e0cc31,479e5a97,17494692,9134f2e6,ebcef2ac,c7779251,77e9aede) +,S(1b18b7a5,43a284b8,3165bfa8,ca947da5,376fc4a0,8f4070e0,a20aa905,fd12acb9,713ab4fd,9e0bab6c,aadd2f63,1c5ef168,7cae85e7,6146319e,94b19a1e,f6a69584) +,S(65e5990a,4700f51c,9d3e61d4,26c92f88,5506817c,d4d60c66,a341cf91,9646ff81,2ebdd818,f69cd135,1405c63a,7160ad4f,17a6a7e2,d4353db4,7b554eab,3e737819) +,S(eb131661,eb78d37f,92a851f4,506c43b5,1f2a155a,dac7eee3,b1dbc29b,a0b1250c,45f2f418,47d2258c,3f78a2d2,794e9aa8,eab23395,8debf0e,b1440d56,dc728334) +,S(f20aaa98,ab7d6f1,d6de080e,ecd0bd28,b14cac9e,7f27c931,5576b039,16777203,4d0dc753,84c942d9,347f007d,543bc399,ee01e84,7625d5c1,a57cdd67,79c4dfba) +,S(9994a963,ef21ca0e,9657cbcb,3da57f3f,e28ce389,60ae5dd2,812b2949,96969047,93d75bb1,35572b75,4801509e,5b64858b,c0a415e0,3968efe4,2e558786,8516362b) +,S(4696ef39,95e5818f,d7894737,2fd68c75,fbb455fa,8477863b,b2687fca,fc2cd7b5,1cdc53f1,c52112c2,f58a6f66,7c1e4e9,110d83f9,d765347e,a724c784,d8e5d5ee) +,S(7b052f1f,e1af9537,ab3bb5b,3e0e510f,7e101cf0,999f85be,c403fd62,53e68d25,69c7f158,4b6c36c,f7d881a0,fe4efa5e,3f879753,641ce0f4,3e73bfe4,77ada27c) +,S(99452d74,3311867d,1d86c1b1,decd1ed4,7192a58b,3ef98cf0,7816823b,1296abc9,ffabb50d,a1a0451d,36b811ab,5e0c55d8,751a1851,3ca8bca4,8d6b3ffc,61ba36e3) +,S(40c6fec6,5ded4688,31f72de9,fb5c7f67,32c0c170,29f19b36,b00b0f85,5a12424,17aa0baa,eb3ba6e4,c5000e96,c3eb620d,ce1dfd81,b35a3e79,b27b8246,97dd5741) +,S(b9bd9e2f,c55ce5dc,2e46e4df,56401c72,e01ac3d5,201a3cbb,a6609fef,ae2e827b,f545efa7,f95e9a60,72cb9750,6f929b4c,e0360c8f,a9f058e6,5b87bd55,a72abdcf) +,S(867c0fa9,f885935,2756e3e4,11666eb2,59a416b4,7c2ff329,e30be462,13178921,6fdda875,4a50ee1b,d229d4f0,fa8876d6,9caf9fb2,a6af19fd,dbbd2a81,5dae486b) +,S(1cbe8374,3decb58f,f2fc9668,7ba45c64,812507ec,c2f71d66,eeea12c2,92fb36cb,799533ce,46d2c091,d0b553b5,18c82317,f516049c,4a4f50be,909c875e,b0bbe21b) +,S(c654f9fd,57426248,fd24097,2595d71d,80cf771f,cabed8d4,63bdda8,340398cd,898e9224,c9e94d14,3f96702d,c296bf50,9b2cee37,da574bdf,b7c09cec,18357a18) +,S(49e8e14f,4a9f09ff,48725eda,85ecae6c,3fe3400b,81012e00,991f6a69,2d75083c,815230c9,284924fa,57d08944,2a30c61b,8ce19644,7a8ede69,fb645864,ef35977f) +,S(43a6ecde,ed83f1fa,8946f584,99e71bd9,978f610,b46d1613,c15fcfc0,f79786af,2995f70c,11eb448e,68eb3d10,c84b36cd,7605e249,1df1fa3,2e9eb57c,c0275482) +,S(96861d91,678e5004,7c491950,5bbfc70a,e342c3d2,361729b0,4d0bcb5f,d93eceb5,6845ec37,9bee6269,464b2e9f,156ada30,c518bb8e,105153e1,c3b3e948,d38078e7) +,S(3c72be53,c9f82add,504a1955,7b9560a1,7716a058,26063408,79aca60,c55f4515,e584b72b,cc4744d9,6a00dc44,b03ba61f,c2269731,a8178755,2a6dce8,25d31b81) +,S(ce4b4d07,deae1fad,33192544,448ac5b2,77ba7f70,27b81a08,d7f84581,4f71787b,2013299b,2c744a5b,da6b243c,d04d95b7,e515848c,dc28eb40,892d40c,210bd1b2) +,S(37984db1,6d288866,78e1804b,9c94ebf8,f3a1aa05,1fefce96,65712f98,198ce116,20a25966,8ed14a5f,fc3aac09,6d2351b3,b6cb19ab,da4c833b,4a2a6c68,55d1ce59) +,S(ac01bcf,b02e627b,2c9c931c,f87beb2f,4ad7328f,9caf627a,2bf695ed,b7353acd,be4eacf4,77c92348,fe0feea9,3075e93b,bb3633c6,6747524a,e387df82,176ea252) +,S(d5d6af83,1a81b72f,efe5618b,ea43335,bf1d81f1,73382fda,860260d8,1a2faa83,865f9ee,4ff0fc6b,fa761709,db4c67ee,a3213d07,21b2facd,fcd8fa57,8945698f) +,S(3c16399c,4de35853,a6dac8c1,8eea4b40,92a22a8d,5e69ed0c,13b9c2a4,3cd5e39c,bffc2dbc,c2ce4b04,6a92b967,d1a04b5a,2fe40be0,fd9930d0,45b51f8a,15b53edf) +,S(f67018d4,23e50a73,68045b0d,ce803e35,a26d9a5c,288a3a7c,c25ebb03,dad28187,ee97afc5,1c73531f,ea8998f5,53086a59,cc67c6db,955baaa2,8f57153c,afc4e772) +,S(de67026,529a32fa,b3ae79f6,ef9ece7a,47008d2,46f89053,e9339c4c,dcb53b30,4daa8700,4b055005,90611f78,43d258b9,516f1fe8,61cf06ca,d466e000,d3a68783) +,S(a133627c,af6e7ed3,1d267608,19b69f45,7e75ecae,2169284a,48b5ee16,b5dc8c10,755ef12a,fb5c071a,36dbe2b7,1f858d87,31c93d81,d271c999,8f632982,d7e97ce6) +,S(d255c1e7,ccc88c4c,33887c9c,b9c15a2b,52b08471,90ad1420,5fb5909e,d36059c7,83e3e1ca,99a386e8,4b119391,2c20593c,f9573996,d42cb7c3,6db24a5f,cb413080) +,S(aca6c69b,9ea17803,4af67422,71b8062f,77e6dbe5,1df2a570,65fb3057,ea185b8c,40cb09f2,adef414c,816b22f8,d7a59f8c,f5197fb9,b31614d9,ac172436,d0f54647) +,S(fbc19c84,3b656fcb,21d99c3,5289d3e3,2bc58fdb,c2404309,a9d8831a,f2365f24,cc22999f,203a4fae,59eac72c,28bed6d9,291f204b,707bffca,ca6d8359,1e403124) +,S(ea308c88,c366677d,5e575457,cd3e6978,5b491a02,c542dcae,5dd5e149,2f839c55,c1916831,2f48555f,b80845fc,ba89eee7,a7967807,d10c6f6e,b516c190,94873ac9) +,S(d911d62a,33fefc48,abfd4409,7ee2d32f,d7e85816,2a8a00d1,342646ac,8dda6cd2,f160f933,41bb32f7,401d153b,4a2decf8,b793fb5b,4d5520c9,6d9cb9e,cba79aa3) +,S(bb75b497,7d698442,7b0ac74b,939ab3d8,fb94304f,602e102,1fdacf22,27022a4e,ee3cc171,8e9190df,9b45d8d3,54eeef9e,11639423,dc091128,ae09e451,fffe3468) +,S(358e7639,b4031852,b983b5c0,a6c0d33e,5edb304a,3f8bd15c,35d35794,e2cf90f0,5da4be1d,2e9bef5a,5da3cc59,d3c4f85a,e6624ee3,17395ded,398147a,12c56c45) +,S(e098fb9c,440a5cdf,bc570270,4b57eeed,f3087b7,e196522e,4b194618,f3c874fe,9a5b80e6,b22dd111,1f824c36,4830e723,18b7825e,2f2a0fe6,ca460d0c,34eb6449) +,S(62d607b2,d1a16e4e,cf60480a,be9416a7,15d70b5c,56734e7d,1498ba03,7632528e,70c5f4d1,4f5c900a,96291a4e,d0f795ff,439d35d5,2c5bddd2,ab10571b,1d7d20c2) +,S(a630c5bd,ec76f869,40842391,cba6cdfb,9109940b,e1034b87,c6a1e5e6,9662857c,8f28f55e,beed2883,3e347b10,92ff8938,133558c3,bfeac1f6,a6b938a4,c783b79f) +,S(e2a164aa,e17cb0c4,8ae1e911,872b0b3a,7c0e29f0,93cd5e18,97145211,d72147b,23ccf99c,afd1bfbf,a7ce1ade,1590d609,9a423b26,381f5761,c19ba373,290e6193) +,S(742526f5,b95051cf,2ee34994,8385fa72,425162e7,e46bba3,febe5ce8,595a8b37,d106a3be,638987e6,870acf8d,26d4f78c,e6aa0e9b,63e4eb19,114fc97a,eee3a12f) +,S(ce7837c2,31c32444,10dcc790,de465c06,75404a1a,68d23fe9,b69d887e,9c554f45,2c0be877,20cfdb11,8c37b2de,a2c355,d12279da,58f75570,7e738867,146257a3) +,S(601167cb,2788b146,998f0b91,d582cbd,27f4b866,40db7ec6,7f26214,f3ab9212,50525e40,1f2da57d,1f605f34,8f11ac23,19ad1827,41d7be11,46538e83,e03bbeeb) +,S(d2f657da,6f7c3c80,9d2e5291,b1161d46,393009ce,e2bd22ae,32b99eed,f76fc728,6df58d32,b795b6de,2a9c3b15,f4f1dc9,4e6e6757,e01891e2,79acde59,d13e3e75) +,S(e9fb6b74,f84973fe,7c7a0924,e0133b92,bb4c22d1,ce9a94e8,433f0c72,2a86e00d,f4df6cd9,df43c150,9d4b9580,eb87298a,551ebec5,2438597c,87dd46d4,3a8ad2c4) +,S(98d46b64,e43f3384,88c63da7,35af1141,e8b41981,b706ac5a,7aa1c0ac,1182afd3,3b2ce203,5bbd2cd7,f3206b3b,6aa3ac68,31e0e2d4,7f9e57f2,3e780fc9,cf865733) +,S(b358a4c6,a7130b1e,a00d8403,7164e68f,c6cf96ea,c6580ee5,b1cc2452,8d2bb5b,3bbb25ab,4645c17a,2a6f7e64,2d3952eb,86462564,5647abd1,2e80d965,be1d3854) +,S(fd8807c6,c456582e,d03114a6,2b42316d,4d0d386a,aee634cb,d33f2f82,c1ad6f05,c67d1611,e45c57d6,1f8d721c,437c0ca2,189488bc,f363aab0,f25314b0,14f896) +,S(cd53fa2a,1f989ceb,cedfb7a5,237addfb,2efe9469,8b6509c9,3081710b,f0db107a,47e61ca6,4d26e617,8192e993,411d0540,bf7c37b5,8c748eb7,6c1c197c,87c3f844) +,S(38cfdfb1,1b60a68,b00a4096,8f2da5e4,4a60868b,de9af728,fcb888b4,9a098c66,65f4f191,92667492,d717cfbe,bafd0076,59c08392,30da216c,315f2cfa,d3725184) +,S(cc179d39,1c6ab4f3,7fae6380,35886e2f,82038bca,768d1396,13fae8df,a167a271,a12fff68,84a967e6,fdd6e14b,4083c0ba,2a2bcf49,f7d6071,d708c70,4722b1f3) +,S(cdd61b33,75fd00cf,430d9d54,202c5c61,465fe7c0,a3f51660,ce74a3bc,58068c,46e5334d,bad31f3f,6c4bccbc,6812cf13,5c039a5d,d51c6516,49564f45,45f13ab) +,S(39ecf8d5,4fa9c3b,fef7d5e6,5ebe0e75,288f17df,4b33036b,d034be2d,fa5bdbbd,21991cec,ef62e421,70da3bf9,8b2f9bd7,790d5b13,25159727,a53a0735,5ab1d956) +,S(7b621836,342994d2,3185da8,67d47e69,a67a184e,925a3,524f5e7b,63639263,b08c269a,1569538e,653f2beb,3e6e348d,c61537e0,7afc5f13,f56d1f74,518b274e) +,S(28e5ec3b,38359a6e,7d3a965a,c5713365,44b91ea5,c9f350bc,d315c153,72133bb4,5bdc5a02,62734535,8621c1f1,492b3434,be731c25,f82c1a32,163ecf09,7f548465) +,S(37ac3ac2,3f6b2a1,a3a5a99c,707a6b8d,5f05e80,133ac522,107ae5ff,414d33c7,460c947e,76eb51c8,42bd0e6,6ccc0937,87f61bc3,554538b4,c2065fdd,533d0b02) +,S(275f5ef2,a4546ba0,9d96d138,e698cd32,78cf27f3,2ee297a,5ff8dd41,f16bea50,ca91a3b8,5b0a493c,a49168b4,c9eff873,66212b9a,c030fe6d,a9be42b7,45d0464a) +,S(6c43669f,a69bd51c,63cacca0,21c69bf7,9a94ef9f,3f0aaa92,3be9380,a6702e34,b416195a,1419771d,82fc2ce0,637cf5a,29823d1a,95614642,c93a979e,5ff3fff6) +,S(606c37c1,271b90f7,482e6ba1,81bd956c,44a44189,346bfa67,539c188,ac0eb61f,f6af64cd,3f04e30a,1b4a7019,c6caa84b,823bd00f,587eb58a,e190ef25,322154b0) +,S(e4136e15,b3fdf609,f779d0c0,70ce8521,2babc592,577361d0,47ada13a,2a83d43b,1af23c84,ad240ac8,50da2ec2,721531e7,18776ddc,5847edd8,b4d79d52,26e53125) +,S(a338d942,6ce420ac,c9fb29b9,cf572af0,72c0d144,46dc2c3c,75b9d5c9,f7fa244d,c88a4399,f6e3ef2c,619cd9d5,215d305d,3715b962,b02b29ca,231446cf,8ea7c40f) +,S(c454d1b2,6f77ad27,a168d032,fc1a73ca,fcc4aac3,ace31f2a,433599fa,7107fd9a,989b6091,ba2ec267,380c5139,47059d4d,2773109c,1afc21a4,3be54ea6,ad8f19ee) +,S(bbd7bd30,5bf1b36c,6cd6c180,2f41525d,75675d76,9352dc60,1a727931,ee6f40c8,3c3263d3,75bd412e,1d7f7d5e,72f73216,6415ef3,52d1fcf0,58e6c046,ceb46abc) +,S(acae6fb0,5d5b503,c2e3d841,19ce3bbe,1380b363,e5d8763d,3c520a3f,df39f7b6,6fab4b7b,daba880c,9167ae8f,5feb9aa4,3ca19f28,77ec5021,df36ac7,db730293) +,S(c9d4ba53,a44403b3,b06915f,6bd9bb43,4716a639,453797e,5b8e1cf5,abc78425,749cca45,6abc2a6a,4e970589,42cefba5,a2f55dc5,39bf4f3e,be5088cf,ae04bf70) +,S(3408e460,99f1a176,fd52f70,3c772f1d,a46d32db,246815d,a21826dc,c5657e61,a93bd344,de7afec8,2ea2842d,40fbcf60,732be8fb,738b8364,aaaab796,1e20974f) +,S(5e061a06,f97f61d1,5f66c3ff,17372621,4ce82af,3e606b4e,9f64fb62,e405376c,609064a0,bde996b2,d909e50f,726ed7be,b4698ec7,5e1c9c45,3ba18815,2915cb1) +,S(b12ca67f,739f4284,3a7adf58,d2a2f3ca,fd245ff4,b766793c,c79cdc31,45579113,b1deaf81,34f017f,b2bc20cd,da75fcb7,d219c353,98ee9887,612781a0,e3f3d1e5) +,S(95a72f2f,c241fa0f,c9500cea,d9cf069d,23820078,f61d4492,508d0fc4,34fc223d,832a3979,1fb9fd9b,79748643,64643bcb,b227c39a,e4cb5051,d136263c,293cd453) +,S(ab8ccb01,4b672660,b867e544,926749bf,7172a0f5,ac6317a1,a18b7d15,f039fc94,28471e3c,a2d6e77c,87700c4c,7e3df1e9,2fe91efc,77724df3,2f20ceba,1ba000fb) +,S(26131926,5261c0d3,ecfa9769,fa987347,1b9d4ebf,dccd830a,96bd160b,61b47278,dc9ab665,485133d2,59d7b604,5e1e4f52,9b099e9d,4db9f680,4935e712,f8402f66) +,S(87a4227d,2303f5c0,acace6e,96586b68,178dc65e,2c99583e,8e427ad7,167c4a65,5bce1d82,216355d6,5bbf33e0,9b3baec4,fb0befff,ebe48b24,cc01d454,7de6db8) +,S(a6576ea2,39af87ba,392a225f,bcec3045,8920b494,68c54ff3,92d2f509,519ce85f,deb950bb,6a767b3f,3603be36,6234480c,1851ae4b,1980f902,f8fec863,dfabde82) +,S(ed41c3a2,48285ad9,1642f6a6,feccdbb1,66298fa6,467b0ec0,ea256570,c8e343a4,dccf3578,aafbc34d,8ca5fb6c,b4482de,4b94dc65,a8d22845,ac800947,dc1042b8) +,S(227756ff,800f7eb5,4493181f,83050c32,9f27a35b,891a3bc1,ea5f0e89,b94e77e7,571c9345,e9e95238,3c7dc62,69aa0fff,46455825,a032f33a,154a89ed,e13a09c3) +,S(6f3b91f1,3f2d6787,454bc7a2,2ee780a2,3c251697,c0da9aa0,a03dcc3f,f03e2b0b,432b396f,c2dcd370,647eadde,ec058a01,53fac955,4b3f400c,435c4755,5e61afe8) +,S(aa79f0aa,767db1c0,3893c9c9,c75a1f8f,457f3f36,72c1d2ae,31e58658,a916fe5,4da2c69c,8033860c,14f8438b,8edf290c,5d02f2e5,153b68ed,fb945043,a6e6a1f0) +,S(48f2a720,df970127,2385a961,5a215d8a,3d41e0b4,ad439c9a,9d0453af,4699e527,11669dc9,26f70e96,e76178c5,7d698d05,b6e7da96,eef4e986,c4f6e6be,216f22ac) +,S(561d8010,a81a1191,3bb2436b,bbe0ce2a,6f1af6c6,fc064d57,59edaf45,c7f9781a,bfb2561,bc311bdd,707aee1f,af3d5c9d,faa9d564,a5121bae,9989665,b074f026) +,S(64d0fc68,6369a225,a15cefec,431fe434,1a424c78,85bdf2,51cccf53,9e266fed,694fdfa8,12f6bffd,b1c75baa,3b843d02,1b69cc8,20f2a8b8,69908a00,ac7050c) +,S(2b80d86d,341d05f6,218eb96c,fb227834,44bdcdf3,65cc3809,1bcba0ff,fa8b3d09,152eb516,2794fd0f,abf030cb,1968faa4,100e803c,e5b8046d,c36e3075,9317a8a5) +,S(62652ecc,daf7ff5,3aa255de,ecbcd653,7840a657,dd53e58a,514ec1bb,98c9b5b9,2a05c8cd,bcc4a512,1d2cafd0,6289fa14,178e91b5,b1fa1190,e4c7c13f,f50cef6c) +,S(f23fe7d7,69cd7bd8,5e0d97c5,4163e596,a0e7dce9,488e8f67,4ca6f3ca,81726ce0,eb1355f6,ecac2b79,dd299fce,c0f273fc,3767a3f3,3ac4777d,cb871a1a,99077e1c) +,S(91d4028d,1ea8e320,5718f427,b427c21f,4780a944,55ec46c4,2fb2a1cf,4772af1e,7a529f92,3756a212,24dcd316,bf9a3f6b,f5bfbc54,3b015c10,54ae51be,5c2e6b2) +,S(7a8fdf03,175969a7,80475dad,c43e5c66,fa75846f,63cfd62c,e82d2153,d185670a,b7b58ebb,fa0b2d3e,ec2a2638,accd965e,c7db6cf1,c847c1b7,9f0714b0,c1cf7dde) +,S(9d72d9af,5e9d7b2,7fb2b867,4aa35438,946c4ca9,eb9ea4be,e20b2eb5,e7cdb89,324214ce,d556acd2,3ac00175,f120d59e,50dc95e,96e6fda6,ff348bde,5ff7e701) +,S(33556ab7,bbf601ba,44dcb33e,aa5efc82,3c70f507,550f3d7c,22f4ba62,cf2d6bcb,a21d830f,233a657f,7857560f,c030f053,656aada8,f7812424,f38a2409,7505dcb7) +,S(e002b33d,93022868,60b39e1c,2820ad06,2063c344,bef98dec,3a9a0428,20e2c507,3133f829,1c81d970,f879d1eb,2128f21d,7540e135,31189576,191ac4bb,d83d99b7) +,S(9eeb1972,ffd49c0c,7982671c,59071b20,422a053e,394db993,8ef7b3d8,98795c5d,ae48763e,5ad78988,7a756827,4a17708f,8afdd8be,bc9c0316,73dd9485,319763c0) +,S(60f821e8,ad12907f,e445f049,141c7718,a495d1ea,3e39edbf,f0a73018,a5cb6854,6dc1b291,62b025ae,2a285e59,a5b406dd,927a743b,483454d1,20426fa7,50adb95d) +,S(5b8bcdc6,40f4a217,6d0661ef,f2ca5d6c,b1aa2069,557274c8,287fba59,dfd65a41,42ab52dc,cad04998,63e634db,ebaae018,c999eee4,c3cf05b9,4f18d218,23f21256) +,S(e764e748,4ec2a2ac,6843df3b,5316f29b,701b5191,778bda16,a23a20c1,69ed0cff,e1007e10,4ae52971,875bf7ea,9e9d9431,94c2e9b5,bc7b2844,7b7acded,de7d633c) +,S(37f0d948,48742a8b,e73ef1ad,495b88f,ea95f7ee,b343152d,85f9e442,771a22b4,bf960e80,467cf81,bc22380b,af2f57f0,d5938ccc,55f3f691,784be72,7fa68bb2) +,S(7ff13c43,7f2189a1,18b2095f,be268dfe,e1375a3d,f71e88b8,4cf5b94d,63b04612,10295e49,1f0a882e,aba663f8,f0202223,21c422d5,f3b453f4,8ab8bf4b,4319930a) +,S(74f6853e,f4a2d018,4cf113d7,62fe2a1c,33ff2729,5414a5bf,3f381989,551ceaa4,629f116d,37c1809,f4e2ecfd,19c2627,6deff6c5,b89249fc,d4f6e82e,58eeea79) +,S(d54d88bd,abdcc56,c1bb8cbf,cdaea6ac,16949256,f78d70f6,7a1416ba,b4cf51ce,996afb9c,5b36f588,812bad4,95c368da,f9e96035,4bb7dd1,3f457a6e,da159b59) +,S(a860ca66,3ecd7482,c862fac6,f6a7b0e5,f5f6bb26,ee950db9,2942fc89,4b6fbacf,3445137d,2047c17d,65b3cb88,8e1b0ba3,619db023,bd989a0d,c585a59d,14da7b9f) +,S(d794dc2,4e0e36fc,6f38fc8e,7a68cdac,8d547d42,b9667241,2dd7e6d2,c40f9aab,22a9a90c,10f1b80,e277be34,3c492125,24ebc477,1f838070,f2be48bf,cec3a7f5) +,S(7e28db65,2115f5a4,575293c3,d792fc97,9ea1adf9,dac468e4,60e5526f,6847657f,8025944c,16a12ac9,25e67f13,3835bcbd,33c338d6,824b1fba,f0ee001b,d79d66e3) +,S(d3f13a27,524f0c87,266f3d46,14d965a1,3865df67,5d20c5aa,99a4411c,dc6322c4,9a1025a3,845a4bc1,9f7cc175,9377c7f,e46ecaf7,53263433,baed836f,77e2e841) +,S(d845636c,db18d557,1759f6e6,bad6a60d,3a67141c,b0bfbbf9,33e628bf,8344c053,40de706,ef833427,80e5120b,ec6e61e4,a8a3726,32818317,729e60e6,3ab7e37) +,S(f40f53fb,c672573b,c020338b,71589d53,3709ac32,b38cb943,ed0ca0ff,e592e552,502145c,5f542a77,652a4ab7,d79791cd,e92f3e72,d9d0190a,2724e114,49b64e0b) +,S(2065a8bb,a0d61dde,921a0741,21ae5678,8766f712,bdb3c4c5,776af9e3,774120be,fd12cac4,a85d5b10,1aec6ee1,4b1301a9,950df948,86427be5,55f52736,3ca29847) +,S(a9b1e8dc,410466c2,df97cc8e,bbcb3af5,f981726c,2daf8803,ef06ef2f,a23e038f,b09dd4e2,77b1d10d,bfdd06e1,a42baf37,a5157017,ba026d98,fee13512,4a1c5419) +,S(c54ce1c,bbfa3808,f24b2661,793c3574,a58aeb0d,128d7e5b,ddcd98a6,d8c9bdbb,c1a2291f,d20e8a23,f4012b54,37a7be7d,5af5f776,bdbb2c53,193e07da,bc1a3212) +,S(41ad2d03,31f57724,6476d0c1,f2c46216,6156296d,8bfd3783,eec40892,835ec421,62777bb0,f8b6ba08,128f1727,9c237286,abc5d2a6,492b9c67,3edc903,7b0e3008) +,S(a8eddde5,eb761183,ef5cce4d,a7958b92,db28bab8,2f695fbb,597f6535,50048a3c,7f60d3aa,95ee5ae5,f4cf10ac,cb5729e2,bf89ba7,6b1070c7,ba385f41,a5912685) +,S(f1db8d40,2cee572f,6eb250bd,ed3d978b,1346ce96,d65291da,977b42cd,36bd073,9bdd0bdb,a546ce4d,774f79fc,3561c0fb,ca11f4fd,b46ab1c2,a7520913,f254f42c) +,S(55cee66c,7ebecc0c,7f22d03f,8f166b99,ec7b5e6a,776bcf60,b5f4d452,c861bfc7,772832cd,55b13c5,890c7ba9,96731ced,5a2e1c05,6ac89469,320c08f4,43159d36) +,S(61427868,7710db98,d4347d4c,aae4c70a,9efbfc7e,bfeffe5d,6f33bddc,4aeddedf,fe8b32bb,d0c7a7d3,b93dcb7a,99a5cc8f,e386bf89,7dcdcd86,ffaac641,6550dd85) +,S(9f3ac763,c117bd99,658f5379,119aa025,cd422ed3,b1ed2cc8,545d6f93,39baf4e4,41b2409d,cec0065e,4026e879,84b99d35,4eed93f4,15e8981,256e8734,a4eebb74) +,S(8510bf74,8f5255a1,8f408565,30f909d5,2bed111d,6347da9d,8da2a97e,751d4a98,2ed99b6,6af7cc01,37329730,1b0b63c9,f8732547,fc632eb7,7357e67e,df5549b4) +,S(d81fc0cf,9646ae0,7dad1a3f,e06adaf,1d3508fb,13af2309,c200c2e1,2228807d,cac833d6,c94e674c,a7aec186,42e4a501,ffbfed92,dfa1015e,c95740e6,57978cd5) +,S(32aa5e70,972a899e,e25e6c6d,e43c32ee,df07ebcf,e5ed13e,5095f7c3,3bb55622,38e82901,990a1e42,38639a42,66a93fb5,4368df0e,befe19a,7f234424,a8b68c0d) +,S(68f7b02a,ea38424f,decbbfc3,498c6193,62c732a3,b048800c,85d042d,b284f7d4,bef680d,90660176,f3d3d009,9f6cf818,a277dc5b,3d765a30,b7a4ee9,cf3bd46e) +,S(9437ace9,6b566bb3,87d9946c,2c32c06,f62f1158,dead214f,2b721c6f,b6e8888d,f8dee3bb,331e1e33,9963b7e2,316a1352,c8748f42,44463ffa,5bf2265a,243941d5) +,S(af40e909,85e5ce8d,dfb6d6d4,222d3c57,2ffae40f,53de2c06,55a74cc,70cd8dc5,2f08f237,3fc922c3,f0ed9d6c,b9538f4b,c661ccac,ea2f4975,50f47ec2,17d8e626) +,S(382e5a07,f1f236d5,ca1a68b8,f0fdb813,29e0393c,c375df88,f87914d,cef93c64,e24ee731,c0a88fa4,8ed3dba,87e97096,e0ec2224,dd657411,a9f1a9e8,2b0a6657) +,S(99fce288,c6ef1d18,ad630097,8ba82dd9,1c779a60,b266109a,288d51c3,978fe565,294c9e53,3cd42a54,c071d5c8,acb7edc9,897db8ac,d25b5f10,9eaf8dac,42a9c1a4) +,S(748326fa,c8dc9394,2de0a000,933dc18c,6ef92cfe,bac46939,f4405d89,627e6727,adf2d561,45581b4f,e706ccd1,8dc82bcc,14d76fa6,94ac8a61,a51d06d2,89e36d2d) +,S(43fef689,67074a2b,7e944b76,8f8b1ca,15aac3ad,c8f5590c,f1f02c3,afa82e90,5313cf6e,8cac44d9,fd548032,dbef3ab2,26e93880,644468bc,799c87d4,c0bc3f99) +,S(404341da,829f537a,c425050c,5ba93ca7,baa8a8b7,dfe51c23,e2ce7c72,43d57659,f91a0d59,bc35db64,b250adca,3cfbbaa2,2a82cfb4,d684e8db,e17484ee,af009f9b) +,S(3af33cb6,b4d553e5,50fd35f1,a0b569e3,eae05737,7b7d735b,545f7f00,d9acae67,42aa2cea,deab8cb3,b1ee9c3f,4b1b06df,e157cbfa,3912035b,585980bb,cf85279c) +,S(2e96133a,1da8a378,c138583e,86ba1de2,3450b285,3559a431,654ec9aa,1f64229f,3bfa5a75,4dbd936d,7a41266c,97472020,6cac86b4,d684bc1a,59dd1f27,fed8e76b) +,S(adc4b672,69fbb719,92bd6e22,3cf9ecb3,c9388b5c,45ffadd6,94c72e2f,b355984a,ed177eea,ac8cbd88,2a368fcc,4908ba4c,8a4054ae,3411d75d,b06939cc,72522a7c) +,S(68ac3244,49eac1e3,df997282,65a0176,bf6c6115,64daf79a,135bc6c2,5578b6e,407a75bd,b1e5e6be,a9b815ce,833c0be5,f5c462c5,c71a086e,59fbe0d2,5e7b1fca) +,S(5176afcc,b4235cc5,ea41eaa7,b657a77,bb383bf,aa71ef8d,d33990b9,9c2acab9,cb363792,9d249fc6,1c0931c,8a27dbfa,c8371574,52c40306,c3291814,ceb57095) +,S(4938cd76,e1aa6b28,2a89e883,8671e4e3,82d7bc76,dc4653c8,46323cd2,51d18542,2bfe67eb,6da39ea8,c8160621,158eac48,550c3730,664590a2,731247a1,399bc42f) +,S(9364dddd,dadbaa52,efc81971,75041c81,e7ebb48a,f20b82a8,125d85fb,a7fea192,ee568f7,53513b91,cf6720,2c45c4f4,181e855,ac0300bf,d7de30e7,317a6ea2) +,S(bdbc0937,243e563,df29781d,3b14d4ac,a664fe5e,70f3d9c1,39ef1b69,57839156,2a84b153,549c6d1b,27dd9ac2,1ab86f34,9ace0619,6f814d08,92a1958e,d137bb1f) +,S(b616b635,adf7b64c,43c4e242,32ffb330,bf28ee8e,48918093,9584c26c,28c82f2c,27fddaab,e5d47957,6344022b,68744822,eec7b937,d29b0631,93c7f6c5,6385cc89) +,S(55a3849a,50606886,abf6f0a6,5a3e657f,46031cd7,4c117365,8f27aa14,326f3cac,4a522c0c,17fcd116,61b11d9b,f88e0ad,c7a5761e,e01db8ff,a7bc3dca,a7976b00) +,S(d53e472b,439dc5dd,c8ff479,ff678dae,95b2e207,c92df108,728c3783,f8818542,3f3f7ecf,df3d9f89,1c723161,75a57931,4ebeadab,3c4dccf,bf2b834,c762d30f) +,S(1e27dd,f6186285,41f5d8a1,6587e2a4,ed38c34d,8393a6c9,fc0a7c07,cef36053,280b223,da8d01c2,12315c1c,ba7dd8d9,96d3202b,91c4eb4b,26c802e5,9494378c) +,S(b6d9d97c,64ba01fb,e8a9d29b,806d8a6,42413bfb,fbb4ae0c,10d8fe52,7ae6415d,894875dc,d912f8fa,845614de,a2b09796,d402c738,ca1f3c9e,c0796681,d715b8e4) +,S(2c97985a,5fd96164,e4c4d9d5,9a9fb52e,b2e6df0c,77ed4745,3dca6eac,7550756a,b3526bbd,de58e408,a4f45dd6,61f5c6fe,2f82d2ca,3e2172e1,c99d1fee,93d44c98) +,S(405deafb,528991e2,ef924f55,b7e4dfd8,185de449,cd6f71d6,53025999,4caae657,e959b7e,59cecac7,f813dc1b,a15bb71b,4a4832ca,54026e18,479d71d9,191c3ad) +,S(4276ecae,a14c0556,b4573b26,dba35226,4fff2c3d,12a3e4a8,4937ae9a,3f687bfa,7611836b,d3b335b,66f92914,6df0de13,d6ecd47e,88369ffb,6c9aa493,e67dfc71) +,S(6fd734de,6d2fadcb,d7b19f56,ae7153d9,1ddb396f,e0c1e51f,ae31fe80,8a35556f,aaa647c6,92865772,f31fdc1a,3b430ffb,e1647e10,3cd5d629,19bcc905,ff7ae633) +,S(4f0a22c,ad6ebf6c,e6b444dd,35a54e38,70017bae,eecc14cc,3f7ca88a,6d4e27c9,1386d40e,680060ca,bb46d678,d2be7209,418d6f72,d3917e20,7b833124,29b43ea1) +,S(873ccb39,5c10f57,6e50093d,44879f2,687d9321,441d8a7c,993c1083,ae7b0570,b261d029,dbe15f4,ff70ee42,36f558de,8984db4a,608d45f,4e28d001,e564c5e2) +,S(75d41857,5be3709c,5c732073,24f36089,b87532b8,e91db4e6,70a89117,9d25e039,47e29f54,fa62ef44,139db763,4b1c8f5c,e027b0d7,e1ec3501,1e8c6dfc,dcd1a105) +,S(b325bb90,cea81ac1,c79df75b,e7615ace,9b2a9d4c,fa1f4504,1a47e0d4,30b64b67,3024fffc,2005699d,5351615f,baee8c49,8a319a19,45ac8132,71c609b4,23e14a07) +,S(436c92bd,a6c7e124,5aaffb22,62036329,ddb8984e,624f2469,b30fab18,1269ee2b,2a9f90d,88c01880,62a152d7,e7ed5acd,33f12e92,4e36fab0,bc0602f8,2c1d3e3) +,S(c0105c64,4e51d333,c7ed28b3,c94292d8,6d9c04f1,89b3827d,f1f22fe2,99bc51cc,d73bc012,1233ba52,ef9727b3,1249babe,bf63cacf,ece0192b,5cbd618c,c48a9d35) +,S(9f2a01b0,46109d49,d11ee109,5e94c281,1cb6f76f,7773141f,632b3a9,2e5f3f7e,1e260290,fda1caa9,ea315380,781f633f,21c90765,2815251a,fa70b169,e4b0da58) +,S(4d805499,23dc1905,7fd35a86,feefb937,f2b621e,48946e53,b8390620,3aef584a,2cd9c060,d66be813,12a3fd9f,ebb9cbdd,4fc23431,c3e0531f,4a026f50,a40dbd30) +,S(76779423,b5e5acad,55137380,276c4033,a865ae5f,9ada0f30,dee6986c,444a1526,9a6fc30b,4f4c5093,b343c8d1,aa204ef3,7c2a6d5c,712ad8d6,39ef545d,1eaa9a57) +,S(1ccc1998,33f8b121,18227adc,2f27c3fe,8d328b26,684b271e,85f8368f,40378881,da21e917,94851bd5,a8859173,830ddd0e,988ffdd7,f7dcb263,f6755eab,e2d73f75) +,S(afeea14,e8306fb8,e69689a3,d12efe72,949b6b03,8c4cdc70,c95bc351,1f999030,cafbd04e,79378032,47c71954,d8c2a11f,d60e53b,35045507,8f6701ed,621e7d53) +,S(cefa268b,e551b02f,8fe50481,44920abc,3d03d97,3f2e9368,d2d2cb42,341c11b3,77440b02,64eb1972,91d2d69,ebdc1de7,66915a09,aa01615,e4001d62,7219d491) +,S(8625c163,58bfd43d,888ef97b,e33d9938,c62f7f2a,9e7b9735,26dd14ca,7c76abda,e1351920,59085272,3ea7b55f,49c4045e,2dda5ceb,c1a31841,1b6cf38b,b97bc1c4) +,S(43b8caf5,d4f72519,19630044,13bd459c,f5913edc,ff303fa6,dceb3412,f8e39c2,7d1df179,13186dc0,d7957f61,a3232180,21a6b50f,61a86f33,72623f5f,d3dc1a11) +,S(163fff8b,cece557b,6fd77b4d,e22e5b55,a6ed51a7,76dfe2b1,362040bc,9e7e4fe0,29204f73,5bc6428,3c510e0e,2192299a,7f6ed56d,1a0cc569,aa37f895,c0c4646a) +,S(fdbfcee1,69aa6724,b5839a55,e1c54856,86503b51,d1320b37,dcd9af73,2bfc4497,d7f75ba3,7af2f969,31bfa612,446c6807,a207af9a,17269199,77b0d71b,16b11a0e) +,S(f1981a9,aa585d6d,102b3a7d,e3509e0f,ed002866,b379b4e2,ba9d175e,4e5879ee,bad6366b,71a7cd67,163b6313,3dcd442d,24f9362e,5d0b9791,2a8dbfb5,74517e1a) +,S(dd3de17f,1cffd240,424096cf,effcaeee,87bf4d30,4a41d5db,6953102e,4b91853d,d136d363,1b9b968b,7c7d1a16,b2302e7a,7770e5ce,afae92a3,8f6e1299,6fb04718) +,S(b1d95fc1,4e35f77f,7e64f62a,36b26218,86397228,35ccadb8,39fa7d7b,834bdae3,75611c0a,920e0251,345b3213,422959c5,ac66c495,14d50c35,de7aa7ff,2a330a6d) +,S(1eeb92e6,75f25a35,9ed16c8b,51a5c130,d2210c35,ce954430,ea2cb3a3,af69187,cdefc1ac,40fac4de,e42ba496,eae80bc7,680e214,4117e6da,c2b3bc12,ce9eec45) +,S(aeef491a,fabae168,9d58c2f3,808b22fd,760e8607,c043c10b,591ed211,6fbb5ce9,b0db916,30810159,7e79aa7,82d8b643,d739bda2,c45ee36c,4f328108,4e55a33c) +,S(c56793cf,822c3da5,300c6c2d,cf06b5ef,413d51af,633ec77b,b9b68b1d,d3079e43,a423e25e,1eb466da,f521c5d1,6395805f,a2da609,fcfcffc1,2728add5,61eb2c0c) +,S(2aa824c3,c8c648eb,8bd0c151,b30452aa,c097bc38,a9f53db5,a8375631,25887e06,20aa4bfa,3bb2c5c2,a113299f,6c61102c,7586deaa,938a33f8,4a517dc,4ba78ada) +,S(4a0bb8dc,1833942d,9ae3428,ed493cb9,5716b50e,c96db120,c18ccac4,f6c3dd9,d0526d5d,c8be139f,53427e50,17cedc51,6b4960ac,bb98d4a4,f07fdf26,640b78b) +,S(2808616a,309b64c1,5d061adf,ae65711a,a561f8b8,345132e1,5b18fdb0,e476240,d2190f22,7320a55e,ad2dc736,671db631,9b18d6cc,b4d2bffc,cb4b692f,74eeaa0c) +,S(3dc4b015,13a95221,32ef431d,33435ba,8cf9e6d7,7e4ac75e,54848916,c23e7959,eaa2516b,9b13bc99,1f4b8ba,fa90a6dc,a007fea4,e26fbe0b,a2c5d1da,326d5420) +,S(473f6157,50af1d8,839ee0d0,eb5dfb8a,c14ce65a,e04e5f03,99fe8666,bd6efe6a,18d204d9,1b27527c,50823d5,222c4e4c,e3e193e5,a282daf2,75d2b43c,ffba8412) +,S(3b87175a,db1a5e49,1cf7565a,28250c51,9fff906b,ef0eecb,e8e50a7d,a190aaaf,fc58f2dd,16f1a5cf,1a728117,dcac3dbf,3a3963f,3859a518,2616b65b,59e96403) +,S(12f1b577,1410d1be,d0c4863,360e37d9,d36643d8,e0cf6fe7,8770478f,504174fa,be29140e,6b5a40a1,40559972,7e0727f8,ca904c3b,813897d2,bea089d9,dc2f17bf) +,S(8658f35b,902afb85,408280f2,3b4d98db,9526b57a,1e6cd0e9,37d8c629,ae174a3c,4060349d,47a69720,7d29e83,d8c82197,d32bb9ae,8c83879,1e09a7b7,1c3e7c28) +,S(530545eb,526d9657,2ce6cfc0,22e1c0a7,b9965c7c,3c2db355,cb5d414a,6c1ac8c7,99243b26,17f8ae2,898b96b4,1c3c65f9,96a9f8f5,83ff341c,671a2d5f,2d04637a) +,S(2e808732,bbab8507,e65936f3,fd1d1662,91ada8c4,c0d865ff,849e0a1a,5f08d265,96dbf7c9,53fd2f61,7e149f06,89b66431,88b091d5,1227a1a6,a0ebb140,18a79fdb) +,S(4272e028,b4b9c90c,44526fe6,b25f5e84,4b8baba6,34ceb2e2,33447413,cd5bb35a,6b4a4076,fc3706a7,50b0c824,93f5e363,f9fcdb64,9d683a1f,fc3bc2af,fc8470cc) +,S(78712f3,449d07ac,7f8171d5,42ee8b40,4761e054,eb0362c8,3b30cd52,6f9ac60,c7ff5ae1,38ca3c13,79457619,dd76061b,7b59778,79db2ca1,5b07ea47,7aa41bf7) +,S(be40166a,60ad9af2,d1bdfc7d,9b0660c1,640d4f1b,640ce485,8fb22eda,65d15a21,78c49afc,1d992bdc,b70bab6d,d3619305,a32fe90a,2f2b1b43,38d77c80,b208cf90) +,S(2fd5831e,6bd5a79b,35bc75f,92e83e4c,9acff5cf,61eaad7e,dacb149f,46d5e6ba,82f38222,4b6be0f2,f52318eb,7fe44a4c,4c0c1b05,8eae3f99,dbdea80f,a70ca3d0) +,S(32fc3fb,4574a651,472e0328,c15a71e3,bf9a12ca,15d341f1,c5102dab,2ee6de37,7e9398a6,760df809,e5eb57df,8fdc3234,eff1aa8,bef57008,c36e7953,c07f25a9) +,S(b9018d98,3452e952,7944f2fb,3354487d,a82f8b90,e52689d4,edb2a91a,a8faa6bf,e9cd0e4e,5c5efcbf,9ca25cf9,f271f9c,a2c46b4a,322cc966,e7789607,324348f8) +,S(a74ba662,32afb3e7,d5c22ad1,c792da33,f1bc88dc,244a8ce5,d4d582bb,b16ced23,444c5512,b49c2c63,4cc0328d,20f7321,a88db1ee,6d525be3,5d2de8df,cc381377) +,S(40f6df83,6d5e8c94,332174e2,2fa43556,d06f48a8,f52cfd69,820c2c46,2185dbfa,852c5b98,15ba4ef0,18f5d231,195fb62d,afafc08d,fbf9159a,eb5f6063,ca353424) +,S(e05800f5,f7890a14,6bbbabb1,27924cc2,e33e3c81,eb88606c,6bca020f,1aa71747,ab905ec6,3af1bc11,897a44e,89c2c2a8,3fb7255,c0e7f76e,8378e59f,1289158f) +,S(7b75dff9,a1afde87,be8528f7,9df63d70,e4df925b,a15f01c5,8e8162f0,89ee735e,bc057ff8,bdc9e37f,554a6ad2,3ebfeb2e,5d64adb7,69fa0082,2b2d4902,ca82712e) +,S(2fb81423,3b01e312,7ceabacd,a9ca518e,b1ab993c,bff855a5,20a96f52,afa9a0cf,a7e4bc57,b53affba,9e2cd875,9665cd9,215fcc,35dfc402,4d1db86b,9605c4b7) +,S(f346a33a,ec17d0e6,a10262f2,fd8185eb,f80a1fbb,f6b06bdd,1c04f18d,75dbaaeb,c09caf81,120586fc,33a6a0bc,97ef5cc7,56d20806,1340a1cc,ba9955ff,4a059e10) +,S(d128544f,a5f5dd38,62a759b8,5d538baa,63d112e2,f8c343a1,1bb3b8bb,ebf548d8,7893faee,b73f0530,bb67e8e0,9c2806d7,e303dd5e,116bc9e5,93e4dcdb,eac031f8) +,S(69046ec4,11c06822,59de20b1,a2a8f84c,58c56765,2ddcae,a26f533a,9c706f43,c17a9640,adc07d61,4a264ffe,272cd686,99b9af9f,6890cdc9,ac02c761,637dda8a) +,S(7b50f61a,9dd7b9ef,7af49c17,6ca5555f,eadf3b67,90998365,29379dd2,a3c85b5b,c08e18cc,bea8a970,7bc34d98,6b36475e,e2e24695,2304df65,bbe0347b,6052416) +,S(794a9728,9db4743e,c91a8560,e430b14,a7d8312,1fbacfbd,ec37a9bd,c749d3f,c57a5b9a,f0cb23a3,5e9b092f,386cda86,bf2f8242,1f74dfa1,837047c3,ae0d2235) +,S(fd8d1485,3d2fa064,c4682ee1,1c319a8a,49fa9b23,2cde9d1a,9a44b2dc,93b2f251,8cc4001d,f0121ce0,ad2119d1,7aaba363,ec45345,87fa10b0,c94a5043,f641b91c) +,S(7b5fbad,8105fe06,6962a72e,39a9a4fb,fac27794,e39a6660,133b9ef4,a6877557,b9b4c9c,1200d879,6013de67,958176c6,dc04ef69,b1021057,de8d3b40,564fbcd3) +,S(2c7ffc8b,40eda03e,20e64678,68959141,c66f01e8,b7cb050d,c60e8a48,6f6e41f9,cd0f3510,39aed3c1,56ae2b0d,1a117f78,c14a704c,992db297,60d22700,2f71ff7a) +,S(79533078,d81a6fe8,446dd750,b59ecdc3,8904f13b,48ca89a6,5644aa8c,9233de64,d7c95bff,77fa9894,74634aab,3b8f86e0,ee0bf92a,3a338fb7,f89881e6,e1fd746) +,S(be96c5e5,d3fb03a1,801a7760,16bf3824,aa16010e,884359b,d5e30658,5f13b214,d156346f,158d0c1a,2ef96a95,86cd8d34,20be21de,6997904,8299744,b40a5d9f) +,S(165c53d1,9ac89139,c063d934,511478aa,bc296cb9,7201dbca,fea4cac2,16a4fa02,4b002644,1e6f1369,d3603780,f33f74e8,42a812d8,b4e7ff7a,4aeea468,35f67348) +,S(508fb72e,dbd27f3c,76569185,a18af8bc,6242462e,cecffffb,3352ce3a,da5ccb5e,e1698f3e,1d53607b,4911423a,f2167466,a473a8f1,e2226849,f4dfe551,dfd14652) +,S(e9be0fb7,8e95dc1c,7b79f4eb,7976367,a85034ce,ea8642cb,19106dad,1d10ab92,b7867a7f,51f89779,a32a7b8e,273f9659,fb84367b,90ca3ff6,afc2cdac,7f596c45) +,S(e6ea5e6f,97116554,2de16c66,1f15d6e1,24aa449e,960b393a,e5337de1,d4c94800,bbd62bdd,1c235fea,60be143a,9155ffa8,204a1b11,3f19f4e,6e683c7d,e234ca7e) +,S(ab3f8a0,5b8f5ff4,149a390b,a858092c,9653d159,6d84bbc5,6e4a7a8b,fd54d7e8,41008916,311de07b,16c1b49,c6aa65ed,8baaebf,7a4e75b1,5aa1bc34,37483e53) +,S(515e02e9,52a347e7,5e0c5eed,a266242b,48bf33d0,70048562,c20feb3f,aecea1e3,797b3ef8,f6c35440,58517c56,ef5c6464,fcb63353,2bd6d2e2,83487a1d,8a403b36) +,S(520cf598,b3552fe0,a42438f8,55aefb81,3a3e10d3,f4985998,492e06a2,42ef46e1,c69de50e,2bf9658a,40567066,3cac0e4,8c8d2920,95ac1df9,9535b7e9,209324ed) +,S(26a8e655,4a2cf338,33bd27fb,d96880bc,bcc1d360,2a76920c,37075790,1c50bb1a,ba5a4560,5ddc5236,2a0d09df,3247a507,9c87e652,c4ad35f2,e19ecc9e,ec1ae4af) +,S(bd06a5a2,362ac91a,759dc578,dff3a3f8,102a67ae,a86ac11,5ddb2e14,bc1bba44,e8751dd7,153537ca,eec17988,98337cd2,abdfa554,52c597c8,61c364dd,78f49de7) +,S(ddbccf92,da0ab07a,e99fda1f,4a03927d,66b0bd21,f1e54fc5,afd4f329,35a0cae7,26fc18c1,52755bc5,d68dc3f2,461d60ee,b4dfc02e,a04b4779,6e7725b1,23acf8dd) +,S(650e1616,2a30c776,b54130c9,9fbbf567,32e2bdde,f667b09b,9b7f25ed,93c0737c,14972f76,429eb04c,1ebb0583,8c54fa0d,309fee09,a55d7eb3,700be0df,5801d56b) +,S(2920d280,41ad7365,cc0e33c8,7a3d6f3c,f9fec9c3,b00e5698,55ae3b83,8cf08973,3821fdd1,fe36668e,16f2cbd0,b2704fbc,24354f08,8ba19a1f,67d1b11a,b70c2d68) +,S(3842fb0f,1b26bfe7,535c9f5b,cee85ba5,6cd491dc,346829a2,a178ce65,c5294302,8e02342a,cb0e1233,9e638018,a125622c,5f66ab52,86a74a6c,28982c25,aa3a0fe2) +,S(9c21d89f,f1142d24,1e62c1df,e0481268,c2273d01,f153af5c,d31b3514,5b9b41ac,f5a924a1,d60e1eb3,72837535,4e252740,593c96f4,87328e9e,2a80cae,15fabdb) +,S(59320ba5,c9088701,f354a3a1,93391880,2829ce91,be9b4c14,c9018fe6,4fcd387a,610e48aa,705e2e7f,86a6a12a,817984a1,7bc60f9b,abc0ba9f,775f3446,8e3f3815) +,S(d800691f,83c2903b,1add209b,35d796e7,15b805a0,9bbe6120,3bf68a08,a13c46e5,21d194ed,bcb8bea0,cc35a9f2,328f1689,cdacc58a,73f65a28,56811e54,d96e5576) +,S(865588f8,66c21986,7b9643d8,7f1215fb,90fe186f,46478e8,522a5da6,724f6e8f,91a6b315,a7ddca8d,b4ecfbc3,9b55eb81,393f4c51,f573fef6,e7ca0c9f,cc551e7d) +,S(9dfe808f,cf7f574a,ddb251c0,4b053d00,8915f8fa,5a975479,d43c719a,b67aa4ad,4d40cc00,ea6720db,ff1f1339,7bb26c0b,281686a8,726fa430,bce0e4e4,ff01b01d) +,S(835f1c87,cf8d4420,72728601,ab6aef63,cd0d73a1,80f81f07,f5d57cc6,bab5ef7d,bac2d1d4,c8541f65,be0644c9,40a18f7f,d2c30360,2083a455,4d70a111,1c5bf07c) +,S(9562dc71,33c48e4,b46d73ac,d42db4c0,b0222c4d,dcd0e76e,b4f84969,348fa46,7cfe0965,96f691de,d358c00e,a292975d,465ef064,e9437556,40bc1d4a,33c3a0ba) +,S(21afe3b9,4237473c,dc032588,c4c1b7ec,853987a6,dcd1fce,2c48bbb6,8d0f3b45,1751c5ef,674cf88d,d5385943,b40a20a4,6d20cbd3,9876adc4,a4a4bba8,477e78a7) +,S(89809c4d,572cc5c1,2241ae09,fe1cee6f,71aaa292,c9ff8d6e,c3eb8a92,8e144ebe,462c023e,1710ed77,bd47d22e,e222598c,68cd7c56,b004369,a9356a47,ed6809d) +,S(b7c6164f,b9e175c1,5724c596,988b496a,b9f5b0d9,dd45d0e6,4a0f08cb,9000e3e9,1a13e8e5,a23b40a0,aba44bad,5cb4d37d,a6061157,aeab7a0d,d327242,ab2ad11f) +,S(62088450,24eef2d5,4fabe8f8,fabf519,72908ac3,23596378,c377c458,9719bba8,26216b3,a353295e,a1877547,b826c240,351f227e,293abcd2,e3967fed,30391f5a) +,S(dcfe82df,4049089,f3f8b275,120ca438,998fb22f,e11d40e0,9d09c4ce,c12ad036,388f9754,39b1c412,10014136,c3b58fe3,30ab524d,d7d3524b,37ce9133,5cb3bf3f) +,S(324ec5b0,12de7919,2f27f5ed,166344e1,934b3527,2b197d28,9a634044,5dd4bce1,99944794,579e12e8,c5c0c11f,a8eae5f1,88cb8cdb,3ce7814c,f89e3f1b,a3d3de0f) +,S(b3a70b0f,af96cb56,7dc4245,13ab7c6c,bbc38634,faa0c286,b81b9754,454a363f,b507745b,97843c90,170f8ffb,f731fee8,4532c1d0,6b2a077c,2aba5d94,189545ed) +,S(c4a6ff4e,cdf1ac83,29a1cdb7,b5165d88,2a1f1823,d1c38006,1b53144f,56187fce,6f7d0fc0,c08a7ba7,386f9a59,330996ef,b13d9d21,eb6d3915,4c8fb919,66159919) +,S(61d9ee96,1ef58634,5c4a73e3,a8c3df82,eac28aa2,6d740cbe,1833dcf6,d5a38811,c9e4a482,1700107,f2d4af3d,fcc74e68,8123b589,4da1c2bb,1fd926e7,11330892) +,S(67ecbd0f,4f4fe300,49390655,5289c9a5,cb6ac090,dff38502,75f7751,a84c6172,e422b1dd,10467cfe,921028f6,71cc75cd,2be5d8a6,75e644e5,d3c40b3a,2836726d) +,S(47f6c669,70898fa8,16dcd4d7,4553c644,2ce5bbe9,cda91324,ee3bba7a,868ff0f7,e56b9590,838bdfaa,1c2be1c3,95f775e,4a0b3982,d0eee531,26ec7ddc,9e3f77a6) +,S(e39ec9af,c8dccd35,4b155dd3,1d1750cb,ab096807,f89c8afc,fe61e6d2,e348cfa3,df3fca08,99158319,b93bac62,88c302f8,a59e175a,cda9fbf7,6f0ad59,8358088) +,S(2d1aae84,500037f9,8e08b64,341eab03,e0f1cdf,5a079b46,738746c2,18b3a0,747c23ac,8173adcc,de767d2e,ac156384,ddf40797,131a6167,c32486aa,12c89e5) +,S(ed1bbf04,d4570df5,8f584158,1fc47665,481ebf59,cb88322e,497d128a,678c2b4a,a40765c4,772da2d0,b3c8f107,22ec3931,ab3fc8cc,c28b003c,bea1a0da,279c8fba) +,S(bcbc3e1f,7bd268,93f377b2,eee4b76d,49a8a603,738de309,5353fde3,deff55f3,fb697a08,2f2570bd,1e9e4ec3,fc7f6c81,2a27a7e6,f7b8a53e,30da80ef,f59ef2cc) +,S(4f98b2d9,2f0b5d57,51ed3784,94cf4de5,65cf632e,982a270b,2746a5a6,3da9333d,43857e7e,437bdc02,6dcdbe68,d056784f,a2cc606d,7ce09a0e,12978a74,5b67e529) +,S(9dbb995d,d0667371,1731bf05,c8029035,2b413411,45318de8,2236df5c,87728582,c0a90e4c,6fbc0665,f8cd9fb6,fd59ea3e,f6ae9a16,9bfb71fc,5fe73e3a,4d665f86) +,S(83692208,1d099344,3d979c51,90940e44,5465624d,3a945c05,7885cd00,5ce6b9e,49be5e40,2e1d05e0,67b16279,855bc1de,6f0d8aad,35d8bc0e,142dbe87,33a14e13) +,S(f713e920,7a699b68,12d7ee9d,1475ba85,a6c56af2,8b73ace7,cf67ed6b,55650e97,a5a2c2a2,45012812,29fc71c8,103db717,48a51f88,61765d31,4b9ec278,3d0aab45) +,S(919f2d21,256ae682,fe514320,9c6551cc,d165de1b,8213fdbc,9c06578d,ef705725,9780da96,eeaac4e8,c96f3d6a,89c222cc,9b16b4c2,e8faad4d,96cc0136,a06021d3) +,S(1738093d,197a5ce3,cedd1fa7,e2a8ff03,959a6c23,6aedf9e8,9dbd74c4,d82b6e48,74363112,d16b829,d371a0c,f1b3bcd1,2000254e,196ea80c,a9d46570,1ee4a1d8) +,S(41242a81,35811b36,d4ffc40d,bbdeaa7c,cce36135,3c4fc9f2,d23191a4,b9c46379,7b894326,789f3071,374983c7,cd2a5ad7,e0ee49c5,e740dd33,817a31ed,97c388ba) +,S(894a6ec9,96eaa8e0,9e077b34,7b4f1e1,a387c930,a526c469,aaefc0c5,402d41a,a080fb43,c8e6bc6,388fa766,2398e096,b65032e8,26c5a9a8,47793924,4dcf5e41) +,S(a71f2131,3599cb7e,8c1058c9,52a5712e,661d01d5,79bc7ee5,1fbadd35,34ca5d5f,afe5b47b,663ebd7c,d788e0f,d3f963c9,ac63ee24,b7495e61,4a5d6553,9ab5f859) +,S(8f330e77,82eb62e4,4ea389f5,7f1c58b3,458c5d39,127d9783,6ddef8c1,27fa390,894e6142,bc359171,999489e1,1ed08224,4a46100,136fba84,e2b8732,e9bb626b) +,S(d2f0700d,6124c46f,a64cb709,1181cea9,34fe0406,21267334,c85ace2,cf05fc6f,8dc30ae9,83a95b74,2d800490,fbd8c290,bda64daf,f82d8fa5,e69243cb,43556cb4) +,S(c2478e89,8c075873,ba6cd5e9,6a730e1c,b6ade2f2,7fc9e7ec,93da9201,63f88c32,c6412063,9921d8c5,77dced53,76907410,82b1b4ff,4434bd,9a55b6c,8591ed5c) +,S(c5bc11a3,4e701898,d22e2920,2806c880,b24efa7a,c6d4ee36,cd440386,e4463a10,2c2b89fc,48feefc8,8bcb4ef5,68d16b44,cd7d8c7,485c9703,91c1c243,bf91003e) +,S(4b3930e8,21827b05,8c487bd2,fefa3458,6d0d3f20,5a2236fe,1b735d63,ac8c62b9,c38a29ad,c9b9941d,dc92a0db,4ce5f312,252c1f66,8a3189c5,595a371,19321c6c) +,S(fc646e3c,eab5a96a,1785da2b,1b50f7db,f0515091,76ade1f5,b057d05f,3aa3b8fe,b2014537,dbf2637d,6f683fb1,6af00d5c,63d4ed59,cdbdd673,f3bfbd48,e9574dd5) +,S(c0ada793,e2d9ed24,8ce307da,a6bebffd,3df2fc27,d2001dd4,5dbfc7be,c5f97a0c,6f3a787e,d40f073b,4965d52c,40700f66,da93f679,9efceafb,4894788f,5c9c7fd7) +,S(b1a45f3d,f2d45a,e21e43af,31ec3159,e8877d91,3efea814,b66f6fa4,a227b157,d988122a,eab167b9,f89db9e2,dd82405b,404f8a9c,a9acfd29,81c8bfa4,273ed248) +,S(4e19b9e2,ad3e421f,1b85a174,89d8841f,65672fad,9a17ad70,ca4fcd12,716d7a68,51412d39,59b158c1,3905c267,f5cf66e2,364aeb8,b66a11fd,ea6f5471,35db6dcd) +,S(533ee2b1,379a6ddd,6e5b3a2a,3a197e7d,eece722d,5c5b9c66,5e1be9dc,f3c09adc,b91b7477,fd0eabdd,bdc4047a,9bcad0cd,9da8496e,bd328fa3,a3ab20a6,3508a6d9) +,S(ccdb933a,816ac55e,c8fb97e1,504fa512,67408fd3,77aeeaf9,565ab66c,a2033b94,20299202,99b57beb,5d3817f5,26aae569,b490b24c,d5834dbe,8b05e8bc,89110de4) +,S(770d7c72,2e9964d6,84dead28,328b1925,287184b4,6e3abbdc,534eb87a,e0ea5873,363c73a7,bbb9b2af,3c0cc91,6f8ffe74,837a99c8,58b5464c,9f253764,e4a78285) +,S(d440dafd,b1d0609f,7c2354d5,74e711cf,852b364,8827fd41,88228ca5,98f71eeb,57d438de,c1aa78b7,c872cae5,c6803a2c,9844809c,7bca1a54,1be6f779,893aae1c) +,S(1602ca76,35ee0795,4fed3cea,d3e54470,41033887,2f060e19,990f3cd3,501b951e,1ea7f857,4b72d3e2,bd45fe36,c828f18a,5810812f,ab6e0936,a7854ed7,c7e32bd6) +,S(9c4d2a24,e4ad8226,bccab8ee,96960b1b,4722d2a1,88a429ad,f9d194ca,6687f7e,e3a4b46f,bf608018,ada2383,5c70fbe,cabbd446,78c9e5c1,35e91138,7d66b01b) +,S(fdf84be7,5be40fa8,db537848,e2fc8ba2,b7c58824,74fea4f4,6e8fcf15,f33d3bb1,2af57dd8,60ba0e6c,dc3606a9,a902420f,9283f721,70ce32d8,39c33673,5da1ff77) +,S(6083a785,fc5aedbf,34615139,e2f97964,ef65d254,a38251ba,203f8ea0,26eea81c,bb4d48bb,894fb656,22cd752f,6e3bc6e7,670a4bd2,64675035,8481561a,2c18439c) +,S(e03ad08e,5b4b9e01,5103e028,668f09a2,7d2f5254,25c6a985,6dfee667,6afffb82,f53acd57,1ab23ddb,d6b104b8,71d957c7,f254bc40,c37017ad,f7350e18,7a24865b) +,S(5a382a4c,88656b6c,4a5c8dcf,4052b1c0,a0bbc0a9,86289ab0,e212fd24,b852d9bc,912caa9a,32a10900,eadd0eec,423f710a,3e0aaba7,93f7b675,912223a2,6ba14f31) +,S(37dd526a,b5392b03,3bd05ff9,3952a31c,7bb0cfe1,f02f4f57,3b8d0ef0,c05d5ec0,7e329228,36595c82,14c8a776,15482931,98966225,4d4315c,c236c156,254e3249) +,S(ed4460f9,cd4bc957,aca653a0,5b6f0935,9dd7e19a,6d65c60,ca585ea3,ee2e1263,733af30e,6edd2b2b,317bba97,d24abd7e,41af6256,e7353f97,58357ff4,1a701829) +,S(eb01d9cd,50ff4778,b29e5096,93388ee1,12cfbc35,2c1b1382,7b3195e7,44a3bc3a,24877b7,99b7a2c9,db23047a,3194e9cb,91e5dd50,d0f1e252,b6f3bfca,f5b4d110) +,S(9fcfa274,b180ce82,14624545,22bb9289,1e0a8fc9,71949bfe,7aa25cde,88dc284e,6787be0a,12a76a23,9de45596,2a11f7f4,4051fbbe,3ba7dfb3,996c60ee,97be2b9) +,S(c8a0d869,87a7a854,99bd5663,30a07d67,70265d8e,cd3286ce,c9be4d02,6145bff,a65c64e4,af865e6a,893e902b,b06c43d6,10bb8181,134a4b71,d136b5fd,f42982a8) +,S(374294c5,932d3f74,a6e74731,6c6a84ca,dfda6d35,5edd914b,70b26161,50628735,ac47d048,8433754e,3deb79c6,7056d828,14d73bdd,703b299a,ca910180,e518672f) +,S(baa9e3a6,2b4bf099,1fde27ad,4b043b50,76a184cb,4f9a6da9,9b56ecd8,a2ab99e5,1e8e836f,f76043c2,c187ee45,7138cf3a,270e2d3f,5bc96979,c12c34cf,9205c63d) +,S(72f85ea,784a5ef1,71516c22,13abca1e,8017db34,4e00b397,4cb509de,a124654d,c78ec627,6cbc5072,fd75b28c,8d395f13,e84731f,7767448d,e06c0ba7,372968bf) +,S(c9173d63,bf5a1301,f0750b1d,e3cb7061,bb9cdd36,296b422e,d23513d1,10b61903,d26a6a0a,ceef78eb,73c6b031,a5455d2c,2a9c4ab2,d916b7cc,7b8e627d,6d581c56) +,S(865ff86a,58fb9853,8bcfe496,26c0bb89,e419083d,f21dbf6a,7b13e041,d5cd3f18,f0bdbabb,8118de57,bce9a90c,83529ec8,abfc2812,ab0088d2,8ade4bde,fe2fa383) +,S(3f14eb28,fd9852c9,bf4d4497,4d794ef1,c30f7748,5c7bb766,6f32fcb5,5ad910ad,e982971b,ec014b05,a9b5657c,a0bf45b7,adfe19d,22726075,80b42eeb,f4dc5217) +,S(61dfae27,c7bb68a1,ede331c,6499a7ed,e242457a,77e5a606,cea82e9f,f450d01,973739f2,c1e4ff77,cae3cf59,b481e730,5ebe8196,c5b62b9d,a96ae224,9b83efa7) +,S(e319690b,e944c297,f7c1bc1f,462ac3b5,a74fb913,9b963384,ac11046f,4343883a,3dadda66,538ea9c8,8fa0c02f,208ede8a,b6ef3480,82af4a32,2ddb5fa7,96d2efeb) +,S(f3db5e84,a9015b39,1617fea6,58143553,baa1975a,ab017012,38dc4243,7d9eaf9b,df55628b,c50cb2b1,be2ef193,a7f90e6a,3bfef4ac,63aaeee7,39907906,547d8b62) +,S(83a7dcd,58742725,8d17657c,ec64b5ed,4807db49,911296e7,10cf349d,c4fc709f,e5201a27,c37cca5f,2f7bf74a,fe000c0f,56643a5b,bafe9f91,de181e74,1d327183) +,S(b95b2363,28f51c80,b376dd90,a0f92d08,1e395132,deaa8f42,c142f642,3d3611ee,c111a08c,a6dbccad,2e784498,22816422,f2200f6b,bb24842a,f96c9502,13479f55) +,S(c3ff0645,921f2b9b,2fe590de,dfbac094,8475bdfc,a6446177,6422cca5,6fb549e6,e07ecccb,4b972834,e2711423,c0fba72f,eb0bb7c0,71a9e206,bf7715f2,62f03e04) +,S(96a37acc,527ee163,51c46241,1df94da3,eb0229b8,6a6e34d0,234bdcb0,5313c10a,46014617,fdd3f8b3,a67269d5,20a49680,9e5fa57e,76da4ef3,59ef862a,c58380c) +,S(b135c091,ec98a07d,b624fe48,4d336b85,62facf81,50346e2d,d0be2cfd,618d1e0e,24456a71,e8f2cc29,19f53594,6d7aaa79,e3d7dcfb,4af2feb5,ea0a2ad3,a23ec070) +,S(71d26219,81730b69,b0411b1c,f553c81,6f35a34,e09d086c,4500ba,94812017,c25357f,8915a2c4,98fe0d60,ca979ecd,8bd7ded3,ad6bc9ad,262db1d2,c3d3e7be) +,S(363cae8,ab2259ee,80283084,d98dc16,60c0934,68e3e854,dd86d9a0,d5e97884,756c60f7,c5e113d8,6e92df38,8fc9a1a2,a4889d86,f7440429,7661365a,ccfd8ce8) +,S(aef32d83,81ba3804,6e5b3305,6764ce3a,726c4e5a,ea61a17,ac114599,63f36247,dea5fc8b,d4ceba19,89640462,a957a8a7,44ca42d6,901716a,734e6f87,9aadb81e) +,S(65980f89,e4d0e342,d4b4fcf5,228692c0,9ebfed11,52d951c7,80963e6f,8c81313e,2d594cdd,a1f0b9db,7b3d8815,f7402a06,acba8e31,b9b0c597,8d2118a2,5dffa75d) +,S(f979280d,7e35cce4,8b9372cd,f73cebe3,6ee08388,e98e6069,fbeeb168,52329433,9181e8aa,bf7d4723,4cb851d5,6b3ca08,8c5f31eb,6b1b8c7,2879cfba,bd3ecaac) +,S(3b9f877e,286a13c8,a8463db3,b306ad62,c152065c,6708bbd5,68931e8a,2deb537f,12436d2a,5269a319,4f605dc1,4a34fcb0,445be9f4,5cc2bdc1,b96d86e8,b9465631) +,S(f2c70f82,1f86e94d,426e3e3c,e31ee519,f5a95b6f,38b663ae,9a6aba4e,f7bdc4e5,dc534655,4ba850bc,fef2485d,51e08c3,ab546119,49fee2ee,4dfc4e09,ff203b44) +,S(e2fb481,da8a356a,ab82098a,f622db2d,6f0a4349,2428dbac,922c1350,65801f15,5f735bb5,bb805980,d85ae242,4855bb2d,5de0c1f5,5018e1a8,e65bc2f4,86b7742d) +,S(d99e2001,33cf632a,f66c382e,d8ef862a,17092764,2c8f1f40,bd3d7bb,f0070324,d00f12db,cdb6e1ed,d190a754,fcbefdec,ee459be4,d5a46d69,afd6cbf8,4a4d30f2) +,S(dd765b6b,cb2dd356,f5965a8d,38108610,688fedc,eb3ce48d,352b095b,aa0daa,636258b3,58171669,f0dc68bd,3dff2a6d,bea6e5ef,20060913,66b12082,e2d7fa0) +,S(f549c14,f099a957,6267ad6c,3adc985b,9d0626b7,3f1f5849,d64ceb1e,266a9965,45a17dc8,30118c3d,28e73a3c,3d026afc,29ecf50d,198afdab,dff3d130,8f32f3d3) +,S(20d80860,90a192a8,51eddb84,f79b2882,ad7ce8a0,9374b365,61e6da34,427e8226,b48b6947,9febc104,880aa8b4,17344206,b19dd720,e5392bac,7e1954d4,a341eec1) +,S(b9f383ff,2545078a,c22cf9cb,83fc9e00,e9d37f30,e03ae03,5694437d,c1de2d7f,14f8b209,d1128a44,20dfb93d,3dc9ccb6,7ed22dd8,ecf5a181,2cd687dd,64a2996b) +,S(2ba0e5b,cec7a4f0,c6edd1c8,2d2f5927,2d97f4dc,3ecf353,8d3854aa,8d6f9739,8b10b217,6e56e5e6,5b99a617,eb71da00,3bee99af,b80e5ef7,3ca8c332,5ca7a75f) +,S(28b45a0f,3c308ecd,c0f7beca,c62d42b,500b704a,9af39931,b8debae8,8cef27e,9a3f95d,a17888ac,19eb4e42,1279c400,a60a0c1f,70a11696,4cf6943,9fc858d8) +,S(d8993bb9,e73af10,26ab41f0,9ec5adf7,437ce08a,4295cccb,283657f0,7a9ca3a5,ad8930d7,817534b1,e8407db9,bb8a608f,538b2bc6,9827a234,743382b,4381ddbd) +,S(77cd88a1,76bfed38,d7dd7152,32e2554a,a644b79,64a85f86,dbd6ffc6,69714e82,4394630a,42436802,1d0a0681,87fcc6a4,dfee0fe3,6db3bf2f,dac6fc58,34faccf5) +,S(d2557185,9c0fd15f,1f3666d3,aba99e38,89419099,ce7be94a,81ca1045,158f2da,90e94d6e,ac00b9d9,19832505,75bb2f,3242e7fb,fb429dc2,a90e3231,d1316cf0) +,S(f4e26c7d,4ccd80e2,7620917a,fdc72ca,94178f54,547eb330,77baa95c,d92a981e,c8ff014d,a388dcc4,c634304b,5973b274,785927e6,190c5557,773b9a0b,ef7d7005) +,S(da3a12a9,50dcd18d,c9e531e9,86f85a53,e7ce5bb8,8f2b5869,9830c44d,8c14cba2,7380a5b8,c01209e9,a1face4d,798f63ad,519a67fe,6bc90511,c763ca19,7e8c6b41) +,S(3565fae0,daf5d9ef,609bede3,6e19417a,45d8c0d2,59163f71,b4d9f54,43756fcc,7b1ef713,a88f9965,be7e6799,4a936960,c44f67ae,d5fa798d,8c0286af,e9da257c) +,S(6683fb20,d435a139,bd08c35f,adcc8aa5,8fbb8f48,7f9576d1,6f41d086,3a4e3de6,6a969f08,566afbc,dc98abfc,9d0d4d54,5076ebaf,efc9b966,957f1dd0,af52209f) +,S(900de92c,cbd4528f,57a72013,77dc1b51,7af67416,1013a20b,30d356bf,e011f508,cb55db4c,8799575d,9dea64f6,9fdf081d,959210eb,32b26bee,b903bf0b,80264c60) +,S(9d84bcf8,1be8a9f5,df2c6e80,d1b5679e,476e39c5,c30351ef,dc6b0b41,61c17613,8aa0cee8,cbc22e60,b71d9066,3533c2cc,a02b8d7f,7df93ce9,897154c6,2fae046d) +,S(6280fba,a3c3c8e4,4fcf0f55,a783eea5,2ebda748,501730de,71781be,abadd211,45b74c18,f0c18d3b,76cc1089,8741246b,202fd272,cdd0e866,5e1f4d86,ee527f5a) +,S(a4237add,7a5ea66e,8a1b48cd,7d5ef195,d90f25e5,6f2f148c,6d4eeff1,805b9024,d512d16e,632bd9ce,6cb1b2d4,c9a41e36,f9a2b83e,73fc8631,1e4dfa1b,b3bf71af) +,S(51effda3,e84027a,47afb48e,dc5c1937,9a685a24,ded2998c,c2b74422,b1e83fe0,346a9d82,cc07ed91,d785c6f0,dc32b62,e3e7877a,892007cf,c015444e,46001f19) +,S(87ac6bee,837ce155,29c005a1,6eca4540,de1480b6,91bc0ed8,90fed51c,de923cf4,9a9aa52f,1be1a33b,7280d0a7,3c19f5c5,b6baf99d,3c9c2d54,4a8d9bf3,a25ceb71) +,S(c8fd7df6,a3b0c0e2,5a931146,72a0af1a,1f995f1a,109e6e83,10b0d433,5dc2666a,3a853149,9b28c2ee,b5baf17d,dba8f57,3cf6e269,f4f21b1e,72bf38c7,79d239c4) +,S(890f9eed,47126c8a,355658f9,296b1845,b142eb17,4c2270d2,46293ba2,997e5a54,f294c38a,1c929ba4,2a9234d3,249690d,10a990cc,137bda3c,ada4b3,6d4e4b79) +,S(9329dbcf,45adc298,65c8defb,ee2ce008,a3474855,39826516,b417b9d3,bd47c86a,469a6cde,e1b0c094,f536fa91,f7617890,1be77c00,896efc5d,94cdd787,3886959f) +,S(6fa5326e,7b161c7c,742188d1,c47263a8,2f1f78e1,df83c664,2546c9ac,70660332,86cfd636,11879179,16d51a4,eac57709,5b8abab2,1024ce5d,35d64b4c,78559375) +,S(ece2e23b,8915d7fd,dab9e58e,8489b053,24eb1485,d18eff66,ee5013ac,3e0556aa,6a94e4cb,8fdf0549,a01621b4,438c2757,fe9753f5,13dcbb35,5655f9b2,ebaaea58) +,S(891b613f,413c155b,c2dedaf9,96d75e1b,54a5b5b0,3705808c,f575cdf,1f697ced,11904787,c1fb6f8f,8856aeb9,f7e05bae,f599b78,d7450bf9,13288e95,ff3f871f) +,S(b7bd8742,d1af6cb2,9b55a307,dd3c0318,6923be55,78ba2797,e38a7154,8d6231c,269e9a33,7a9421b4,cf45938,1961b0a4,3e4a0f6d,f6a0f10,2d7a831a,d7d4cd3f) +,S(7c6f5db3,1620a83f,b4388d45,75d244ca,1f38a0d8,611300b5,d4fdc9c4,34a16432,d5c0c35b,e4a371b0,3b85214e,f5e472de,e6c8175a,b140f05f,6e52b766,aa313f7a) +,S(18d303bb,d2bd6f9b,7f941879,6a23fbea,5ece2078,982064a5,e040c95f,c534ad3b,76eaa3dd,b73af546,7b1862c5,7b4385a0,28b03b7c,66729fce,94885373,1ed2dccc) +,S(31c0daf3,31aa6248,de302243,a768aac0,c8e27da2,c2b8e0a8,7fd74214,b49bb78b,e8e1f975,b77550bc,d404631b,12c72d38,55d3bf5e,5477a588,b11d57e0,e71e1ebe) +,S(940aa62e,e3bff798,327e806,b644a972,fae9eb09,dffb8394,61b080b4,708f715d,2853e12d,56898ea6,8e922497,3660a29b,37ec41c7,339b8059,2075c7a,2836c1b5) +,S(66db37fe,3bcfd7bb,25ca4a1f,b2ee09df,5c748061,1c3a6fb5,d0f079dc,cdbf9b7a,a6d37fd5,5d58ee33,73898711,69794421,9cd7a2f3,3becc8fe,761bcf83,ce603b9a) +,S(fb04bfa,a93a8ed9,3d755351,98895c8f,e90d545f,8eb1d08d,5d7282bc,66026e3f,e77f3c9e,afa2791,b4db2f69,3438d051,47edc983,a8541296,a8cae9a2,174e03b5) +,S(5792afeb,7766365d,e6b36beb,aafde8a1,8e556ac4,207965d7,9e18c1ca,63465c78,95fa5ad4,4b93db96,2c26d740,d2aa582d,da19b761,480ecadc,212452ef,cd885b40) +,S(186c0feb,6298e341,a2440465,547ad137,c56593dd,ed82b608,6824097f,3f83f8eb,b3b58cce,8375d9a1,e63500d,141feec1,38778c46,4be1ad43,7c63256f,33ac3b6c) +,S(d39114b7,c22beefe,6796b6ae,3f740a39,9e3c98bb,58b647b3,5cb23ead,76eae678,1df690f,383db8a3,86284ff8,f914925d,c688aa2a,8ec01a2a,243eb309,e79aa071) +,S(6195550f,8ce49ce2,f762011b,2e8feaca,509a0628,eaa93151,4c779616,721b39b3,ee8208f1,6d86fdbb,c75c265e,88090c85,aec112be,a1d803e1,211151d6,cfbeee2c) +,S(a8429368,37b33a8,2d41a26d,e11a0fd8,7af231bf,30d3eb64,73aa064e,acfd8740,18524c63,c0cf7a0a,b4e0eddd,61717f6c,3221a8f6,3522d35d,4eccee30,b6ed82c9) +,S(bf42ae9e,a64351a6,eb1d2c64,57d23481,f913de5a,d3c00359,5eb8cb5b,228fb202,2d74804a,34413cef,8d2cb488,ea780cf5,15faabd2,36a9db67,91549577,70a09d3) +,S(f1bdfcaa,ba133809,f915e0af,1a08fcbb,dd9cb3d8,b62f545d,6b71b44b,b130daa2,51fba8b2,2c7935ed,4241a87,f19c2b76,bc1a288e,3e53855d,c532f0b8,1ee382c9) +,S(7bf32637,4c6adf01,c7796f59,a11e5c3d,103e3339,a0cfbdc2,dec57f5e,69429bcd,8926e57c,a1fe7442,72d2e89,d037feff,df263831,e40e9ac4,8473336a,269f6b44) +,S(95b652a7,7f58b0d3,4c6d25e1,c294907f,a22a3ee0,f8e8478a,4e8458ef,37791c2f,e7dc4caf,b5bfdaa4,7185f0f3,a98876d,b50bfb35,a77a8b11,b522196,9f255845) +,S(d9f8c6ab,693f8058,8c73e2a,9221b0c4,c8f1d1bb,5e1e58e1,8468a48b,c74f50e1,cb38d48a,89c1ecc0,23e0e248,e1991ac4,3fbddf5a,9beeb53b,4cd9c49d,41f42a9e) +,S(43f62528,287e50,5cbf1a68,18bd99c,9f307e95,84e0d73a,f9fefeb5,b6dff92b,85fbb4ea,b81a0409,ce43ca05,3dfd5a1,9756fb5,f4b964bd,dc409efe,696c2ace) +,S(425a68ad,6d20661c,ca7d688,6bb9c66,f37dbf29,30ccc6da,dc34025a,5ea7a92b,7853cbea,d7816579,fa98ab94,875ebd2e,7ceebf73,20b50b2f,7e67f0f6,dfd2eeee) +,S(929ea74f,9b75c327,a7b26127,8828feca,821e4d8f,c5e308da,f3169a2,bf84bc2,f19e6cfe,d8c966da,bb070d42,3c62289a,5193146e,4297fa35,5908663a,a36b0578) +,S(83da36be,b1bedbd9,4a4ee417,f9faae5c,c20a9763,2c1ab2c8,b997f23c,f6d35c31,a91def1d,7871aa84,eed0bce6,12bf6a82,98d0ebb7,536d8e49,f6757a48,aa7a86ea) +,S(a2e1da8b,4318fad3,52c8c285,46f0a8d7,8fd6cf6a,b506f8ac,2d6746c6,75f2d5b3,584489cc,58b08663,1cdd1b7,3e61cac3,52c16814,493b611e,ffb94a71,2cd85699) +,S(5d7c1af4,4b257440,22b82ca2,9923a180,bb7fc209,1990b111,12061b6e,9eec4d2b,b40ca0f6,a974223,3a7b0253,9f0dbefa,1bf23dfc,cd59c747,c290f525,2e960b02) +,S(e2831933,6cce89c9,993bf230,ae4311ed,48fb0913,b5f35da2,b7531418,b512a75b,660aa9d9,8bfea898,810107e1,711abff0,88881efa,4d9d586d,91381b11,67cc2a21) +,S(722c1e28,498d3c21,66fb0f1b,f44e3ba6,181a962,75efeed6,ac83b36c,12426031,27a135d2,3b70006b,480c3aa3,bd057bfa,200da08f,f9eff741,6bff7885,4a1795be) +,S(f3cd71bd,f93f2100,c534d788,ba607845,cf6049e3,b94f5274,84e8608d,7adb9c99,b8e86285,db09b729,dd789293,7cf5fdc,989e00f4,5dcd4916,a6860bf8,d77cecfe) +,S(36341496,d986b096,5e5ad593,de6f88,67aa6130,889714a3,dc0d58b7,19647318,8cbeb984,a3f4bb5d,75cf77ce,14368b9b,77dfeeeb,7313f16a,8a573953,c7b898a2) +,S(a48443b4,3783c58d,4b7b2f27,17efafc5,e5ffe24c,a7c26bbc,dcbf93,538fbdea,33bd2bf6,9a64a742,c461fc0b,b0ae8770,9ed47a9a,26e7d05c,b71e4e2d,20c894a5) +,S(d4364d96,71f2876e,843dede5,9991ac5a,cf92268a,73212ab0,54b1c196,197dddfa,b30038a0,fd9f7387,fb37754e,9b050e80,e0f28596,d251d627,5149149e,880f6d43) +,S(ce277a60,19ac0b41,16b9d863,5a2555cf,30ef3408,f1a15769,91e48d0f,9465d250,5260ee90,65c89bc,7ef55fa3,d5f5d7ed,594fe592,322b7e97,965a9a3a,5654e710) +,S(587ecb1e,7c08da9d,75ed0249,b1281762,e9c71475,5c3ae35a,f7d66b53,c83117be,c6dda341,cb7d88b,b4a1b13e,313dcca6,1ec58063,376d38f2,cf142b56,87bafcb6) +,S(d87f96bc,69510c4f,19b84e93,cd8dd0b2,eee4fbb6,c5c5074f,ea76a209,e059f41d,a9b83947,b557c4b6,41952117,45ddff77,b5952798,66fb4095,6b5361a1,b463d326) +,S(af9f0d3a,78454c,eb8ca65,c9d7a324,312c01e9,dd8a00ad,e923f2f6,41fb0ead,2d3003ef,29a5ad10,20262c54,936411f0,f8082630,8506fcb1,e1848a7d,5d16482b) +,S(efd0467e,f4791ec9,9380be87,394225ac,637f906f,800abf9b,1ad5b3cd,cf9f017f,a14308d6,b0d16772,f36da4fd,d77945f7,1d82276c,ec4dddc0,de24f760,965c5725) +,S(e892235e,39ac312c,f30dfb97,97e88446,dc1f9a3,ef5cb8eb,d540c64f,d6b6129a,5b291ebe,7c055f52,b9fae6ff,cdcc6dbf,f84cad1e,4d35f495,4dc19b3d,64eb992) +,S(3158351a,542ff38b,12f82b5a,daa6ea3e,6e75caae,600b76a1,16ebe3f8,c9bcd66b,2006c258,a327481c,d324acda,b8ddc331,6a6aca81,44dba340,527babd6,4de975e2) +,S(f17bbac8,c0a7d2da,bbc5a5cd,4b28e96,4ab88639,546b2b22,d40c519d,25dd448d,14f9f488,e76663bc,278961f4,a33e11f6,98d6f5fc,1eaac638,79ccfae,1ab40991) +,S(9aa24eb3,44e010c2,d6fd3efd,fa03cfe6,aff67d25,2f08dda9,3d00332d,3065defb,494cc5bf,e60fff11,f42190dc,e607f067,7e355a9,52ccc682,6845111,322ac050) +,S(3f36168d,77711926,deb0f7ea,b6dad80b,8c755db8,e6705fa5,61285f96,84f1ccc5,486d57cd,b46d998a,5d064f9c,68511657,e53e544a,ee8c5521,87a1cd16,4121c6c2) +,S(fad18e89,219c449,87551525,6150e4a8,1fa2b23a,b6c1d90c,2e2f9152,ee8cd054,b285a7de,4eeff44b,751b4e51,f252f3a0,be7c2223,a715e301,23e9bcea,495ea7d4) +,S(1cb1cf55,9c56951f,58147d84,9482dba7,8dc6050e,2897f389,e30aa87c,519dadb6,b9e54a71,93a8e255,b2cf1fe1,80fe54e4,3e17470,60e9128d,bd527abd,c7ef1f97) +,S(74e68afc,9c8d9e0c,92675d9e,2fc12803,4ce7d22f,e31f5332,d18817a3,bd05efda,1d8442db,d55e8abd,aa1de803,f4fb1295,e9a8710a,3f25a242,7af9a5d0,b5dd786a) +,S(10336317,ec82d4e8,543dea2e,b8cb597d,3aec0e70,fb2971f8,7c5699aa,e4576de2,f2ccc066,5eae8645,54816dfe,c82e1162,226ab207,a17c6085,62925f5a,ffa3e021) +,S(b06a55cf,57668bef,6c51bb66,ac8ba7fc,6714c864,ca788279,2c8b759d,290342b3,7311f47e,5b526dbc,e4bc7833,e4c5b45a,190e3517,8f0f8137,2857e7b0,fb1f0528) +,S(af83a357,81306453,e576c819,d1cdacd2,689b0198,4f108d2b,6d9b8148,50df5523,14f52176,909c7e1b,eda13d20,7567f31e,161a80ac,112e4016,ad7c0bd6,20ec0ce7) +,S(f8f56d54,54faf536,3ea98913,827d57f2,a8983112,aa8809dc,594af919,3ae5c4a5,cc9d4182,bcb00a0d,cb73bfcf,da46ced0,ecee167d,fd7bbfc8,899063e8,d659dd7d) +,S(61aa26b7,415001ae,d6b3afd0,1c690177,879cac31,93a2e410,4148df74,733c4ea1,972510d3,697b93ad,60711b39,f3b3c237,771bfbc0,b7238e78,5a786707,dd179f23) +,S(781f196c,dde0f8e5,10ad45ad,6be062aa,1315c246,85d0ca97,21533c65,627ad492,6f3248fb,e7e61cad,1b01ec2c,fdb4dc4a,b1f4dc25,3666d4ae,27a88d50,c566d18b) +,S(c422d61,7f84ddc6,4483d55e,19228dcb,395f16fc,e85b5c93,2778f62f,ce8917bd,2f3cef48,deefbbe9,9477389b,be898cb3,90499b2a,615899bf,1e910e21,ed337652) +,S(c325955e,b56fe9a6,66555a32,72f637d7,3351b65,da5059f2,17748e68,4da4a4a8,3fa27721,2364140b,5b71526f,ad963d01,16889555,bdd42fda,c2b451c,fb70a3b4) +,S(e393cca1,49142f01,9be9a4a6,e8597354,e34f8d55,b400fa4e,8ac6b4c5,7e50b7a7,e40b69bb,44d6d1e5,22cc30f7,26b7a262,3e5b76e,7b190f07,10325a08,a99fb2ac) +,S(33e3686a,e4e69510,e939e53d,62fd669f,f1a39af5,c6179364,a190ae11,a600cbf1,cfe60b76,1306222e,7052b0f1,f799380,ed564d47,1f6418ac,dfb13d98,47f9154) +,S(1c35b5c8,3072f4d9,b3b294b7,69ff1b8a,ed2954cd,a013c7b3,34b53fec,c783b251,1c691a2e,782ff393,b0ac6c31,94f4cb0,f8dbd5f5,b51654a4,3914a458,6b855318) +,S(147255dd,3af5eb26,119e3d86,b2bed206,43579099,742c4ccd,974f7fb9,83e47fa6,5e0a0a01,7497235b,4766b44e,2fbe8902,aa0e2c2,69e7ceb1,d65c0f34,7a97f7f9) +,S(9524d71,57304a13,71d46aa,bc43986a,5ece70cb,ea008aa2,b8c97070,6436b237,a343af97,db30b94b,3d8d366f,1cd27b11,83c9f44a,75f0aa93,8c4932aa,52f6ea55) +,S(8502e7c6,e498d868,3816188,a99d0f35,79058887,dbeb3c03,676b3b91,be7481a0,28bda608,a9e18df9,c304896f,94efa7d2,46ee528a,6061afd0,322e052b,e7cfb68e) +,S(b89a0d8d,a21903a7,200b196e,46714aa5,ad49c5b0,8fc84de7,8e72ca76,2a3ad3b9,953bac1f,85e4f631,38464a99,e6e3be5c,328b3c10,7523701,c60aad8f,d57e6bee) +,S(49bf791d,67125d38,496d8ae6,ffcc25f8,97114f6f,9fcd5541,9876dca,4bdd7d7,6c2976ec,1b9078a9,a90f55eb,510b1a89,17efca39,d2fda494,b9013d07,7f4e05d8) +,S(c63bdf20,1ff5b07c,14273727,ff174322,422b6497,9ca8427f,5f868eb3,a1fcf05e,c4135e1f,1b5a09b5,d628674c,21ead97a,42957d31,92a44de9,b785986b,3269a782) +,S(58c8c84b,e9f85bd,b26f99b3,cde715b4,4b057d56,d5411c3e,a3000829,27ebe551,15de85b2,1467bee0,d4e0b0c5,114fdafd,410cf720,3ab35f1c,1eb2577e,fc2ec620) +,S(5fc5177f,e0b59e31,e4abef85,31f3fbaa,e3399bb4,126b6119,c7acac83,777cf9b8,adf27f69,b613fa62,3cff72eb,4dd10b4c,4009b73f,2cb885d9,57ab8c78,ca705fe9) +,S(1017a9b8,5116ce3a,498888e5,7a408f36,12a6e8b,b18f6961,d6e1d2e4,38902928,24f7a939,5e0d3c0a,579ae611,b733248a,640db7da,929edb24,b11bd617,3e573658) +,S(d9ea68f4,6af94d9d,232a5deb,c485fed0,d719cdf3,2afe4617,524958ed,751a2c47,e59f2193,db2fe578,642a774f,c4fc8515,f638d1c0,ae8081f,e9d3c0a0,fe20410e) +,S(61d975c7,e4d7e262,5b067179,667325c9,a32ef6de,a0bcbcb4,a825d89f,de6f7aea,b7a00d17,5a2d827d,37de6c8,667bebd9,da92ba42,4efeb706,54092342,e644db9f) +,S(2ff5cd3a,fefe0c25,bbb8245f,c9399409,83194bf8,f4599f3,101e9d79,b7f8d9bf,3c154b7,11964aee,5fe9175d,deacabe2,400350e5,3ea1ba23,20d9e675,5aac8fa9) +,S(cb438486,954c4a00,effd88db,29e0e61c,f9fef94,5b42bbca,8939e0a5,dad9e8a9,920ab096,41789957,6c3a8ab7,249ae2cc,73d1443f,5c650ad3,4da9d473,221de71e) +,S(9aaeefc2,889948cd,f59fa4ac,352204ab,46aa399a,c848041f,446be421,e0e4758d,8e4d2899,2ef8d6d7,3e1e118c,70729e7b,4e0bc28,69d7b5da,72df7b46,38fb3365) +,S(ace47cbf,846e95f2,9387dab7,6ed4e272,e32f6215,1e745738,69f2bfbc,720c78f0,a35997f8,37b8e86c,3b4377d9,71098d88,95d6a008,b9d65552,b1385a4b,49c53d3d) +,S(66610f69,1558942f,8285a9df,3081e431,5c946a5f,df958f03,b2ffde5b,c591850f,be93a2f6,53a8630a,ef037f8a,624e623,8f2ad9d7,3ff4624e,f4240cd8,d3495a98) +,S(db4bc8b3,7a12e950,706e148e,4f07df0d,a24d1de,615f2201,2dc1befe,f1ad95ca,fd55628c,c260a1a2,e56b95ee,ebd6b854,f548dfcf,2c4a6b99,7c4e95a0,a52c4894) +,S(a2a7aebf,58a9a58,24d5a996,da18958a,5fc23d8,452d13b2,b254d4fa,bcba959,d6440764,1642f25c,8b31a117,cd7ed90b,dcdc54f9,e652a818,bfa032cd,18bf085) +,S(2d8846f3,fa8a7a0d,408f8ad,bd76366f,4bf7bade,c255e2fe,595e4e22,cfe86684,2c0a7297,228185cd,3b21c10b,5c7f490c,b01bfe72,9513eb63,f2716d27,f0fbe19) +,S(27ba20c,a44821f,d6ebd41c,8c29e584,6237d8bd,13b3eedb,a82b5aaf,10a6fc59,68d65fe7,b511bde2,f922f119,4bb411a0,c000b4fd,268c2c86,e14279ae,9a63e42) +,S(c2f0b611,3a6bebe9,22ec3e97,a3962280,ab0b6713,76a6778e,5ceb73a7,e2894b13,27b90863,65989917,68a9635a,7f6c674e,71a433fa,ce4f1ec4,cd775277,89d290e8) +,S(8420787c,a0df74fc,5c81d19e,64f7ed56,8bc54a2c,ce1c5714,1706888d,bdf377f8,4a94b5e6,ad402877,dc4c0df,38c7fe41,d9e6e085,e70431e9,99c2b724,e35ae74) +,S(29d08c36,c45715f4,1eb5138f,a5c2f002,b4021f2b,bdc49592,e4242443,1f4d5f0a,903d61f7,2e81352f,582ce3b3,ead7b6f9,e0340c23,6925882a,cdb41285,6181bcab) +,S(ccabdbe,f68b6549,2158d4c8,91dd1d23,efa7a84a,a985252a,3a1e895d,757e5f10,94a0e915,2addabd3,d5adad5e,cbcb38ec,e1a045bb,9d7295a,92aee49c,a6f0bb4b) +,S(5bd30e4,9e2ba9a,745c6ebe,434b8d4f,1d8c34c7,6d478ffb,2871466f,a3ff23e4,1067b3c8,5980ea3a,dd1a4cba,8bb0b96f,ad988cc6,4dd8e206,51eee853,915226ae) +,S(eb233898,8f12eb2b,5369d9f1,56d754dc,8733745b,30967e17,697e69c4,4fa80493,c258bfa5,e993ff4a,4a9eb82f,ce1530ce,25fe74ec,2bbc2639,683464f8,a26b5667) +,S(4b44ca53,f01e1c64,8ff41a97,721e7c3,b0d62b4d,b15c5bd5,d84621c,4dee2ace,d8999254,b359a155,99e59761,f42639ad,27caf87f,ed4ddc57,203fef6c,9abbd922) +,S(ce5ef1f7,6cef6b65,35631e65,d4f47924,df370ac8,ce059a19,5455fb1e,b156f7a1,74c9b95a,a4dc212c,3e127bdd,266aa89c,a4773f27,5607dff1,4438647c,f0e50a60) +,S(563d7e99,40325aee,e66557d0,b14b7e8e,14f86643,de2b3300,1bcfa7f1,ef788bc4,ceebeb99,9878a305,1a8bd9df,bd6cb2f7,3ac18776,ea78df41,30fd22d1,bcdf37da) +,S(ff929868,fb5a1b53,6acca702,f5dc1e83,efe8cb96,f96a3303,d107c6ae,ab5dbee3,82828150,fe8fbb3c,f2bfe03a,3c79fa5e,67c33d97,b12faaaf,572b1455,6ae11969) +,S(1004e4ad,487212a5,8af57ea1,49e0edae,6aac884,2f488bc7,1b75a5bd,4a20204c,88d9a6,164cbc21,52d60ee2,7c8003b6,b39e418,b10c72d0,504a6a7,d7b8b2db) +,S(28dca143,206abca7,1c48137f,8bebd26e,6c79666d,ce164fdd,ef1ca7ac,8e141a25,ec1dea42,575e5cc0,804f6da3,21ee739b,ff244cb,2b299413,4b8539e3,88f7ccf7) +,S(cbb81690,a7e511bf,56a5190,b9366345,7b4fe8f9,64b8fb57,36df2067,28e1ca48,c2d2686a,5405dc79,d6a2deef,95a97813,4a434309,c5303985,f173015c,6571e5e) +,S(3e1e663e,378c44b9,b61d04ca,bfaf65fc,df6b59a2,ae83719,11b11582,978f008f,bbf4b3b8,2010fd76,70d23b07,6018708e,8f78d70a,b2da1efd,be668c9b,d5125d51) +,S(7f742a3b,9edf6aaf,cfbbf368,5a622318,1a6c3b5d,588b74de,c2d2c049,91d97784,238a4f6b,5cd25489,1b8aa5bb,41f253e6,6a59a020,bce98966,ed4c75f6,b7939e32) +,S(f073f95f,f6c809d0,9271eb48,abf35b2d,b760caa6,54b0969c,e327cff,f806f43b,6a7eb8ce,9e297e07,170e9287,7fc737cf,bf923cc1,e72efdbe,5b3b4920,6602746f) +,S(855f6451,5b98ef1,67d3a11a,fec74625,cf3801ab,75df42d2,1b33b1bc,c36d074b,6e8cb794,9990748b,f54a9291,e7ec374f,a31bef56,ed1812a7,7b2ef00,149938fd) +,S(f0164c7,f204384,d3235b4c,cc1bb9ed,bb10cabc,d69c02b5,aed1dfa9,56c74d49,872fc547,78580a6f,38792974,a64399b8,cd674622,659ec31a,91fea06e,77e27118) +,S(16e83238,e95e29f3,ebc297e7,fbbc77eb,53431f3,cc90f809,1e540457,aced09bf,3f3c0ff5,754630bf,88ec765c,a5c04da3,f6ca4ad5,e72d9c0,715cddeb,69c4f8b0) +,S(70d371c8,8b2e47a,47982dfc,bc98ffd8,324b6bcf,2f1aa2a8,f4815f66,dd4949bc,34cc039d,3c69b4d1,833375db,e7315340,bab2452b,2a82163b,dec73702,f4c8da46) +,S(db250b65,6848570b,5a59a2f2,d45e99d2,de1b3813,46fa6ce0,8bca0b34,57c27d10,2859a0e8,fde87061,d7ea8786,f5667aa8,b2633e8c,c52926cc,c5427d64,a7ae6494) +,S(d6ecb3fd,b1ef3350,a2a831e2,321cfe9b,6b22b38d,6a8fa0f,b256f2cd,ac73f949,ad83f35b,c1b5c568,49436cfe,6ff17f5e,8abc6891,e24f4a0c,524d88e0,6c3f1028) +,S(ee2059ff,a2c5bef0,646bf76d,ae2f3bfa,193b8a28,bc74cfbf,a96b48,921ac0d0,3a8f4693,15d6a6e2,2e57edb7,e5897039,153e0283,8d66b1ce,1814ac74,195379e7) +,S(6dd7fa4d,bc12da89,1669cf7f,b1024a8a,551d7281,83fd221a,7a0ca9a0,8c2aa725,67fe6a0d,fca0fc02,1fc89584,d2a5f19e,ce890708,490c79e3,cdbd43ad,ad24493e) +,S(15102e09,b966ca25,1a9b4609,16ca1229,7ab64d60,980582e5,e8bf0e4d,80fd6c35,7ebf61f9,956109c4,3cfb52ad,4e60078c,93ea653f,17c7328f,9120545c,48988be7) +,S(a0697816,4a0511f9,3a48efe1,de74992b,54679aaa,a99e328d,6771386,945e9039,92d24154,971add65,c40e6f32,a5b5fd01,765effb6,c8d64676,c9d97e8b,f857d630) +,S(ac925df1,3a9ce7db,18b9071f,f81880f7,c8ec97be,4b78a31b,27917d71,da0defce,6347aee9,abe9b2bb,1f1e3ab2,2792009e,ee009011,337fb184,d3f637f3,186acba6) +,S(74f3c7b4,e237db77,9229cb2f,1fdba8be,9ba990ed,6dd84976,ff92646e,55e21091,44e9a71e,a07e4254,c4dae620,9c437873,7c56187f,23f42224,3167e45,932055f2) +,S(78f0a024,f6a5bbaa,f3eaf550,92a55bae,73348e82,37dc095c,11ee34fc,3194eb00,54e9b6e7,2f758d54,23d5b9bd,b329262d,6745eb32,c93c2571,f86f40cd,1cfeeef9) +,S(83d3e538,92d722a4,8c006910,a22f32ad,2ff7bdec,d9bd2ea3,a2f315cb,550a1bc3,a4f3c8e1,732b23ae,9f21c03e,7c3711ff,687683c3,13455107,68278332,8d9a25d0) +,S(15deb2e3,f0f0b65,5edbb7d1,8d86cedd,7242a693,a271d853,b468d57a,52cbd647,7cb1ea70,8e12acb,6bbec5ac,26f513cf,51460493,9db6cccb,ba712561,a6f4a80) +,S(6fc5c841,eca2637b,67fcd5f1,3c3dbf40,3b00e4e5,270bccad,617c58af,ac54195,afc3fc7d,92f0f77c,5147ae41,8aae93d3,b5cb7785,d65e623a,391d6a06,1f82d4c4) +,S(154dad09,40e83686,d0942998,e220f8b6,bd106af7,beb9bc66,1f1a4c2e,29300f20,9a47095d,b2a357c7,16d8773f,18c953ff,dc0ed270,1cd881f,f3088176,e61bd7ed) +,S(a498ef6b,89b882d6,81ea0647,f83d53aa,d1fb39e6,6bba74bf,8008a43f,197b301a,cf4d289e,f723ad46,27edc96f,5315bc2c,9ad39c04,4e8e6785,222ca142,3f5c2881) +,S(5c01e4d2,465c3326,1828c45d,bbb2cf69,79cdb746,fb659a6b,44f0f2f1,f388fa3f,315dab9e,5e53f939,610e9729,3e686d33,e46f8262,5ddfb45d,46728b03,927a6837) +,S(d058f58b,255164fe,e6d60472,2b32624b,2eb523b,6c7f1e6a,7ce1c248,fb3fe505,21761abe,1bcb844,39645310,7a3dd2ee,28bb4dc9,93dcd0e9,a09d5176,1a7c6536) +,S(eb89dbf1,67c1352e,5ea2a7fb,206663b1,8c95d962,cec35412,3b9be6b3,51799ca1,9566074f,ec54174,a49a5f84,e4965174,408425f2,22d485ec,b020740c,74b08ef1) +,S(19ddc300,84dda580,db5b1230,b0fe9508,5575eb10,680181ff,ee0d521b,17b39cb8,6af18172,bfb03b77,db456836,b9617f05,bbcbcccf,f6ed2dd9,a9c6e734,c01d188f) +,S(66240aec,7e72f707,69560e0c,f31f2c41,9a8efab5,32f17f59,19bd56e2,93c41e13,16fad661,3600a79d,41eba5b2,d0721dca,afe94372,fe83b20f,7178e29a,e6f3949a) +,S(3c75905b,9e7c7751,9f4617ff,6010c39f,92b852fe,89dca73d,cb3f15b0,23f7a286,799ffb8,94fd4f3e,61dd489f,da746fdb,ae955eb1,3c8dd9eb,e4f73d41,2feccec1) +,S(9719a34,e765cca4,68ab3453,3c6dedd1,6247af51,b73295f6,8b936914,c03f10a,4d4e7f2d,ee3aec6f,71fa8978,f532e995,c0e1533c,5cf0b51d,f35d8f8,29cc10ec) +,S(e32f292e,e5c41e2d,bd262c45,eda0b729,713207c0,17fa5c47,c8bce6,ca550814,105716c5,5995ffa6,998f2209,b9d0b37e,f2bd78be,527f6c12,b707faf1,ef73a82c) +,S(76aa4115,595e7656,c76300d0,a60e3316,fd9c1b60,d41920a6,128e59f0,74a9dde5,f3ca0754,b6daef99,acd6e1e6,fb36d93d,e068c0d2,26b843a6,e7e111e9,9df8efda) +,S(215186c7,ba018fee,747c2030,f57d1105,945b021b,be053da,efb1136b,f0d6f0a9,62994ba3,62d9a8a0,7432690,689a859e,ef498860,3d52352b,e27e0f8f,d02e4de7) +,S(62e16f47,4d3ce3d8,a21f0c8c,875eea7c,111ca0f5,21c03f54,6bcee790,ab0d48bd,e9a8c8ab,99f916e0,4141972c,f60843be,72088d1,e044b27c,6a84b21f,c787abb) +,S(372b5966,8b00faaa,a714e7a8,2462107a,3d1226e4,988faafe,988d06a7,dfb27bd0,c6d349aa,6a53d0bb,185e0f59,a4c137c,8622be1c,5df205f3,4b090080,bf4e4035) +,S(b11cb8c,cd6302bd,e6005ed0,9a1ce2ea,a81fbd9c,748a17e8,fe1c139e,d2d1d725,29ec84f8,e8d627a6,b81a63c,4b13abb,88d7e5d4,9e56e2b9,a3f15f40,60efa38d) +,S(652b5427,f45d07c1,8cd9afb6,7d5743e2,836d78f3,528917fe,2a5c6162,c4a06d81,3e93d9e4,25ce53a4,915bd12d,4327e89b,64a7112c,5ebae53,35c631c9,64b2e5f9) +,S(c0812ee9,89fa0531,56847bb4,edd517a6,7d6d7d0,90ab5955,a59326da,f3833afa,2450338a,cf923109,d67b0501,3557f310,f3b0550a,8f1cd5fc,6eef8a5c,14b46c27) +,S(627c169e,fb0d9937,8ddd0fd5,a73afa16,2a317e56,7d957695,41e52a41,d9344ca1,11dfbcf7,75bafe74,eec503cc,22c8b485,90a17079,580acdd9,c51158f5,efd0cd7b) +,S(69916299,5b83ba3a,6bf16e2,97e8031e,74dc9054,c05ab59a,335a8296,d876a66d,39e45d50,aeceb888,8a1d64ab,9cb3b127,71269b7e,a14ca4eb,2179d762,4ee6d953) +,S(6693adae,579636f3,2332a418,7e49ffc6,921aa51d,852b5136,9a96c8e1,cc268574,ae73a660,580a4005,50865060,dbe9ab43,2f304ca5,506b9f4e,7e17f602,1456bfa2) +,S(ccc178c,7168ac1e,e7bf7579,f75f9645,5ca2b558,e1e709ee,de86df5e,5ebe0789,81c63b04,eddbf33b,432e00c5,9e76d319,fd1ef2f7,d6f31349,1f7a3c10,431a2130) +,S(d80687f6,89f7c28b,d6ef1531,c4e4b283,c6e5dabc,ad7fe5d9,6dc20d30,e1feeea2,71e24cd5,9d2c33ac,2b298946,bc90e0fe,d229720d,539a6f5,e128973a,9bd6c5fb) +,S(c53a6911,88f785f3,efc1fe1a,d142a259,cbda5f76,f3e9de36,895a8963,ffefcb74,7a225bd6,a348728d,951d55bf,257de9e9,d88df32c,ff2c4391,f4ddae8e,ce357cc0) +,S(d512c64d,1932fd1,5636740e,2bbd302c,3d8674e6,52a30d40,ce879145,bed9b7c1,52e336dd,3c7eca6a,469bc97e,a330fa03,1cda3c57,a4354ba2,acffcaa,a1e6b424) +,S(384aed3b,531098a1,99312d65,74b1b1bb,e79a8bf0,ea4f7a86,c5114fda,b32061d6,f2290bbb,2bbe7b53,eb64f87b,17a9e1,49356d9e,de3c0be4,28a4dc7d,709ef731) +,S(6db6c2c1,1fbd5b92,31a4e34f,ca2f9315,dde00dee,909b8d53,d7149c6,6e3cecb7,cb2f0b26,e1bcd464,6e6ee074,8efcd499,f535a8ba,f61a38e7,aaae3511,3c781649) +,S(6bc2f0f2,c0118efd,4fbab4c4,90c56da9,d66c0b1b,ec88e8b1,c8da967a,95d1ae60,a568f40f,24e51e2a,d01c13de,4490f34d,5a5bff13,e005ce77,94a59128,f6d871ae) +,S(12e4e92a,aa40ad9d,281316d2,358bb0d6,c69fac28,24114a9b,a96091d1,64ee29cb,f717cba6,b07eac9c,34b8821c,218519c3,886d5cc7,a22be959,54b3eefa,8819bb51) +,S(6dee0e1c,b887e155,e67996de,92716821,7874f9f0,a14c8673,1eaba91d,272705b3,28d9f2fc,23807874,9dd40ac1,8ace2dce,18ac3980,7c7a5922,43260eb8,ededbc3) +,S(f9d667ac,ec2120aa,50c2b6f0,810b54eb,4928bef4,779e2311,7d12eebf,d093f139,29c9458a,52d4cde7,e32a265b,fb537b3e,5a5fdbe6,ece9be95,b645b960,c160166d) +,S(e026f66,af79f4ea,52955b52,bca16e2f,b2cb05f4,38568c06,ebf1ca25,e6f5be17,6319ae60,3e733681,c8caa501,d7715b98,6244c1df,1c42d391,d8d47e15,a681df15) +,S(1ddd29c9,bbb37823,1185f631,23d5e1fd,39c6b03e,44e2a542,318a6f3e,8325ffb8,22d80158,cee2f596,8da360e5,93a2a86,ce1ed25b,77a4d66,a2e7592d,26c2c715) +,S(138f88f5,f9f46b81,2246373,6034958a,d5476a0f,348ca538,f789e980,9c799f30,adac6393,3e2de680,6fc38d7b,23ec5214,43ab505d,cedde216,6accf786,bd4e4aa5) +,S(b516a9a9,4630ba5e,7469d5e5,ee0f03c2,51bfdf5,a1983506,79baca9a,e6de6f6,9c184956,7ad68859,89a2013a,5a902fea,66d398e4,6c85203e,b7a143b1,90b42587) +,S(bb1a5499,548ee5a6,79500a49,a1466b11,9d56918a,735c3e07,31939ed0,2d47abe7,b946e966,2df43be8,6478e809,ac690d4d,b25398e8,de13731e,89867f80,63f8021e) +,S(394ee032,8be13bc3,ffa069f3,92b72070,36ad018b,503ed70b,9bbf9f7b,458faf36,3a494b0,dd538dc,fa3a10e9,70b83c4a,2169b4ea,255c6423,50f7ee72,40a6910d) +,S(a021889,ce4c941,606c0226,2b423afd,f44d6a75,483dc4cf,797b3512,8131e2ed,d8ae98ca,572b5a38,cd10a763,6638c47f,2867710,373af6a1,bfab6cc9,64b26547) +,S(99e2cf77,a0f389ad,a6c21457,d3e94ee5,95fded19,89c326da,3cef2e49,670f2572,8144a88c,a90a7685,ea3155ea,8fa5cc0c,c282fc25,532aa5da,84494b97,94810a1c) +,S(23c4b50e,80278869,b77c62de,8232a70e,52a4a3c3,4d0d6114,78989746,54e57aca,afba6a63,e35b15f3,1a24e5b5,844b0f3c,b247deee,a764b3c1,1075c3b8,c65bb0b5) +,S(ae0ea240,91b09648,6bd06ec7,ab6dd488,fd6669be,58c2e91d,e8486560,47493d5a,63e13a3,aed0b2f5,a11e914a,3617b1ea,abf62d4d,731eda05,62251d76,939aea63) +,S(d9e9ec51,cea0b150,28199afd,70fa86ef,dfa90e86,b9613831,9cbbeb86,c6573bf2,561519ee,f8dd5ebb,df47d4bb,c24eed76,5ff4342d,6ec6e537,8a2952e7,db51354a) +,S(e674a628,7472d602,8c0b888e,1dc86429,207e38a,af61d3e7,60708721,c1a44613,c145e723,eabf1dc7,f09fcef,1ae24116,1bc0d24e,9c96c83a,3c4e76e4,cdbb9e7f) +,S(19a43fb6,ad571142,42644af6,a3ce367d,329f44e5,bbbd8948,9368620d,944f413f,29a616f2,5f2bf4df,e516574,cbe840de,9ab9ee1d,abc86ca9,6413bfb7,a79a4095) +,S(cf1e1a87,32eb52e1,660186f9,6aaa4cac,9248d8ef,2738a6e7,5e600f3b,658825d,379dd9a1,8355ac8a,94a9614b,e3f51540,c17a76dd,3e3f853f,252f0e75,164dd346) +,S(342147b5,8a1ac1d0,baba5b2d,e924e7a3,b804e385,466f5ae8,29138bc9,5c5b386b,9d8792c5,8917aabb,6a3cbc4c,7f18d2b0,768dc9b9,1871bf48,8ebabc00,f51e2bf0) +,S(1c24e35d,35fda6b1,f6ae6b43,7599ec91,dff1fbb3,b3d26976,783c0539,a8b9af32,58591ff1,68290b12,d729c621,f5da504c,e697ef00,7d922152,9a76ed61,2dbe8608) +,S(d0fa4d47,a29e1101,baa08cca,73e00d6,660e2588,6e1b5cce,ee1c2a5a,9fb806da,918cb1f1,608f9e40,37a59bb7,28c25708,db898d76,a8b2e2b9,d4450c64,3606212) +,S(f3307ffb,7e72413e,2207918d,8a93f183,d5cc93e6,ae07d196,a6d22f1d,253b2499,8a44b83b,feffd6d2,78442d72,51929c0d,42b690a2,d91d99bc,c5dff056,d37e1f27) +,S(77e612a6,9931080c,7d910573,fe55582b,30eb4ca4,c7b7fe1c,9c4eeda9,b296097f,cc85c0f3,6f020bb0,9705f560,15268e36,bf61db2b,814b51b7,c958f1a8,656c26b8) +,S(fe3b3fdb,b8d768aa,8248087b,70e6d0de,592c95e9,b9816996,a3b4c88b,21bb9605,23b44ffb,20e8fc95,ad3c035e,3f52fb53,51291965,95fbda63,8a192cf9,43630e0d) +,S(e664460b,253050fd,8814a2,b2bc0f56,fd8d2f53,94673a67,9a76eee3,f7ae6e80,bf0a7515,a0a6351e,62bdf527,6974f06d,be87345b,438eea33,ddb980fc,788eec9c) +,S(b8ab3266,6cb96b06,a363d87a,a7ee820c,695b7372,b9341b,292dd0f1,d5fc35af,720da144,39ac9d53,c480dc25,5eb45ffa,b9e406c,8eb5bae5,1c145058,8120f900) +,S(38242b07,d1904c72,6da9ca1c,f1acced2,96c1c4a9,b37671c2,e529917d,a651b99,3d47a793,688603a,d6ddfc87,1c85a19f,e21ac51b,d292ff7c,69e219e5,7a0dea85) +,S(ae9a9354,e451abb4,454766fb,7eaac21,5982008e,6cd47d7d,df5a6289,27131913,46d291c0,7f99fbb8,2eea4c66,525602f9,6af5c16b,f4c31ca8,c26a831e,a39985f) +,S(59e59fd6,6a89a538,f5277950,816bf2dc,7070f70e,98d04b95,1b83a80,dab205a9,cec6b519,4d1122d6,dd3e39a6,fb3e80a3,2d829158,3ddf0bb0,3dc3086e,9f247f4d) +,S(59aade5,c117e92a,97ba1785,2e1ef3f6,d000620d,a8f82e2c,1d4272d5,34a8e22a,6a6b6ab1,bd9a69cb,181538fd,eb4a9e8b,eda75811,96ef31ae,deaf516c,e68c1cc3) +,S(f29d02a4,5b464774,fbc1bcd7,7caaf22f,12efcc9b,2a569966,b9adbe8,18a9f075,ba87956,5e0a1c56,fd03175b,a781110,3f34542,ded10367,6d44f706,7b8ab418) +,S(db630bf6,e9ff0092,eebeebb8,a5bc72e8,774c083,cafa0519,2e6d913d,112181a8,6b764e8,2213a7de,79b9abe7,edabf502,d9ac04c8,eb5a4b26,9d22b666,f657685f) +,S(45b9f064,71b848c7,48ff3fc3,34d425d2,57a71abc,27c4d002,dda26c77,1b09d2b8,c2119389,e4cecaa9,c0c72e4e,20c95cf9,1acb1698,c0bcfd42,f8620a9e,d781de57) +,S(d4919e2,3a278aa4,25bb55cd,2fdcde2,eac63058,4cd57f50,390c6a9a,1015ffb4,f8e9f461,950f17fd,8c46f04a,3a6f3c52,5318cb93,a0aa3f3b,bf90c02e,80e1c288) +,S(ea000677,2a76378f,3faa3c1f,c7227cc1,4b11764a,3c9a14c0,4f18f9d0,b55acc46,eafaf0b5,9a308182,8e6d07d7,2e16509f,bbf76fbd,e1473e35,18b795c8,87ff84c6) +,S(70b539a4,4750f763,be1145b5,b433e98,847545a2,8fc72986,2c17045b,f0d6fded,d5438ebf,18eb5feb,fcf6edae,4fcca2af,e0208adb,2f82648,2af428cf,bc761464) +,S(10b4a235,99ce2be7,fdc31f8e,ad91523a,47b8162f,8ed9387b,4c208657,25fa88d3,773ef1b3,a3943b86,729661bb,6ce6f4ae,e8c7165f,97a88fe9,5df9e027,3ccf9e7c) +,S(67d7633,499323f7,cc378c5e,2e62cc6a,5f34ff83,e11e6061,9ec4a404,d63b91b7,cd7616ed,60f0f4fc,1006dcf1,850807d7,e8730954,30db6761,cb7cbbf1,a9c2958b) +,S(5744c77f,8371057,61b1c782,1c9d9c53,14219e1c,5df80ea0,f772f9fe,ff157aa7,578459ee,13ec92b8,b685acda,a153f24,f6c844bb,e195ad58,77364ead,7a9e5fa4) +,S(7c6b254d,39ab709c,cd63c2c2,56c43483,183b1b27,25ca49a3,19753f68,6486ad02,5669fcba,d8662be6,2d12cde6,30102f3,70f7ef0b,5b347fa8,6000ae6f,497fad6a) +,S(8ec715c5,bda8f9ef,3b197ea8,a182a3dd,6cbc3b8a,88227e00,53a2eefb,b2006676,e59ecc81,aea7f2a5,4f659bdf,a317bada,3f8ccb7b,134ae21d,733b9710,335e73c9) +,S(fc7eb1e0,95d0b18,58d22b5,ad36fbac,1b513d86,2bb3c9ef,61b55a5e,985e642,7206dfc3,10056bcc,4071e9f8,13495744,dd903d72,2aa0d6b7,3fb35394,4b66c85d) +,S(4954bd00,308e205b,90c191ab,6262daf2,81939084,668c43dd,1ae998d7,e143f1dc,8efa802f,e67f72c2,5b4fc279,4e23608,7b343d89,255272d6,3c3968c8,7f26237) +,S(f27e6c84,f60858e8,2eb53d39,a6860189,9172da9c,e0d1ea70,9225effe,23de6969,90533e33,321f1f5a,ce37ff79,b4dd3109,a1efd6eb,de502aae,4dbdf58b,1e697c74) +,S(166f3aeb,cd040509,85df120,edf55c1b,f6eed0b5,fb24173d,c7d9125d,d0b2b29a,54b8de8b,c24b1cd7,8f89820f,d4b3ac6a,1588ebce,6de99b9c,81773ba,5fd32e20) +,S(c0797522,8dabaaf2,b46108c4,88ebd104,b0bfafdb,b344b5e0,72f6cb6a,f58f3cdb,bae0280c,704e0d0d,2d421b20,828cd00d,9b388f32,fcadb511,ad5ed4de,281a4a29) +,S(81366739,2085f96c,6a39c9f5,ff60b51c,4991a289,7106619d,1ab0de9b,34de87bc,d7b8599,2464c1e1,9f575cfd,de5bec08,987230b6,20d911d5,d6dd3d2a,8a75e079) +,S(335f446a,f3964e77,8f4985a5,c19e371b,876494b4,467ed700,482558a,9cab3c65,6e8fa42,24cd52c4,b78af9de,b7efa726,c26a94d,85bfeb6,fdb1b97f,674cbe6f) +,S(b6a34b42,bf5e4781,df14fe99,1a14b414,fa488da2,29314366,1ba83ecf,1e80d60c,b633c9f3,cd359dc9,2ce47287,670fe80e,131315c1,6fc17811,f319f1e9,82f88e8b) +,S(95f99013,a9e937e6,ff784b07,23287960,5eb4532,e16b3881,25a18012,17ee3ed9,2d0fa2ee,1aed211d,5b90915f,f3b2116a,28528f19,35b6b534,333b102d,177c08a2) +,S(9c0923b0,d681933,bc8bca36,a6af4058,682c5661,95d3a211,a1ef602f,933f6a45,a72df8d,a9a951e5,a63f0424,4974d74f,fb809cf5,fe7c24da,9c1715a7,2419c7d5) +,S(931cd34d,e32188a8,802b6a73,204eadf2,cae8d8de,52423daf,daf042cb,946657f1,d84b7406,d58c53e2,89ca15bb,4ba78db3,6bd2947,34d0ee9d,6939c15a,9f28b856) +,S(24be4b41,f845c43b,5fc594ac,90e3e4aa,b49cc79c,4b0a8408,1803c27b,ce62fe4,c6651cb0,be54ab0,78940179,6dca2448,3d9349ee,e45e78d4,b786a636,83861bb9) +,S(f19a51c2,ebff1d77,81b0e7f0,a29947fc,3ed9dd26,9f0b9cb9,c27c3f7,500c81eb,8e1cfe92,6128173f,556b3ec4,4c394ba0,201a4f93,e2db054f,63934b2,479a301e) +,S(f6e7e4cb,88ecad49,a1653a6c,5510571a,cec77024,2f490774,a8dd62a7,d6e16363,60842dbb,b8ae082e,9538e725,1a3dd7f9,8edabff3,5444719d,97134a3c,236c97b3) +,S(e7b7bb26,af8cc2ba,6e54e923,6af55e40,7ff704c6,1264cacc,74d96b52,81f2ca00,3abaca16,e20f74d7,6d11a942,415987c7,8da5ed50,3d781f2f,aba6d534,cbc3908c) +,S(c8acf1d3,89a29f6d,e0d03835,80a0a562,aefcaa99,b184f910,f2c8370b,bedc4c16,924e9b2e,2c6463ac,e8eda0b3,feee2f82,73050559,1071bbcd,72f6e323,31617359) +,S(1453b2dc,efe09d16,373ffc2d,42e79805,64ff194c,6b9944dc,b12d3cdb,1d84ae27,2d7faeab,7ca11212,a1aa9d4,a462c04b,8dacd2ec,1f7ee3b8,28fcdfb7,a26c6e17) +,S(ae92e090,b3c6ae15,68a12c6c,882d1b4,345f0a46,b9ad364,bc869243,dd533f4d,3f50449b,a3865900,ff63d944,27a0b967,56c00179,5b5f51cd,5996ca2b,19ad7abf) +,S(3086b503,ad8afe4d,fe2795f7,c972e0c0,8d6f040a,1be88309,606fee4d,b977fd06,b8631fad,80689ce2,7f62db6c,d6a56f3c,d3dc110e,610f3732,fa3f2c10,a4d588d9) +,S(7a17def1,e9c2b175,9944eb35,262908e7,c988f602,2440e151,928dfb44,462e2856,42171a1,f71ae8ea,d437a1d7,e1ec3394,b713044d,6c895930,2535787f,b93fcec3) +,S(97975e9e,b0698b70,995411da,37ce2a87,91198b90,e104476e,b86c4a77,dc5ce9c4,b8999b7,69b36de2,13cfc81c,c2fffc3,65e5dbc2,db1906a0,ec88e931,cd05bd41) +,S(839fa603,a2ec722d,1253328e,15f7d187,55ee80fd,b6bc7b09,bbca59eb,316d1b22,4e43cdca,1b33fa9,cf397ef3,91d40b46,fdcfa770,b2be8e9d,9ee22d61,6e12dfa9) +,S(ccfb8b97,7baefebd,eaf49c7c,1ddada20,861e1c0d,acba4caf,4e30f21b,ca3a05a7,7d9bced6,62579237,a348071,3ea1af91,256e9d62,1265fa27,cc80b5fc,cf75f99f) +,S(8f9f369b,3f325823,e8c9b16f,f2142ebc,3c6f7bfd,dd28c4df,63d1c92b,581cb6a4,114c7d9b,3f54f549,c10d93d4,25843477,9c297e2f,3f07b208,12335fe2,5771b12c) +,S(f0deaa5c,324c5ead,6f6f1cb0,cf1dac32,ef342606,3533435,65ac164a,ac8f0f2f,ef6dd597,e2203c03,f0ce0780,bcc1e963,621c1460,7a4ce616,9ee6624d,11c2eee9) +,S(638362c2,18c9e0e8,72dafa8e,fed86fcf,da5641eb,7cd01725,9a85103,f04fa408,3c224389,bff1fd09,338ea28c,7c9a93cb,32be8749,4a6c629,5c1c3dff,2b582877) +,S(49acbef2,64708117,a53f52e6,84bc15e9,d004baa2,fb3a756a,72d35efb,49871759,b678efb3,fa09158f,d1f9c74a,87a56883,b0a8316c,d4848ef5,1f35dfc0,8c4b5bb) +,S(a79d63c7,b718cd66,b393e70a,f032bc50,a90209b2,5f5b07c0,303eb8d1,1db618ee,ecf7f89d,2f6f9877,9b5e390,ce5837ba,49bc2b3,a7c1047b,7a7c852e,89f1551a) +,S(c922d543,f192f654,3445d3f9,c36a05b8,2ee043e0,92afcae5,a21b9b17,a06d0257,cf77d5eb,9e555f90,34ce1c14,63f7c4b6,a0202d81,deb02ddf,a26113ba,292554e7) +,S(dbb8f077,b411a4dd,faf0d89c,3dc20375,1bb20e16,2052ee46,915528e5,1f461387,a6c0d5df,91cc4ba1,cbba0588,774d47e1,7ef97281,63ca8950,7ff6b779,da12b7e6) +,S(dea21a21,eccb1733,14d76fcf,dc6109c8,88a633d9,645f86e4,e59ee237,cb18110c,a1390d1d,69a304f1,a2792ecc,675f2dae,1ddef908,2f2e3929,6b9a068,3933264b) +,S(f088ed85,750dd58a,cd03b3a9,93d88760,34f50780,985f7552,114f957a,ce8904a1,b810e652,f596cb80,c369f61,f632bc3c,fcfb1fc2,b906acaf,c1f39d02,5e3ae4dd) +,S(b85da431,16b898e8,56fe3c18,b60a48b7,c2ba9f65,ad67c31d,f45ac3b6,608b774c,357d324b,474ae7d8,97398506,c550a1fc,5353a00c,7e6f1245,c0f57683,80ac9e1f) +,S(eaac0d2c,276000ff,899dcb3c,61b41663,70123276,14585bea,7704de95,d164514b,aad696cb,1af2f12,1ba915ec,64afc13d,a689719d,8f188424,25d6068e,9bb06a69) +,S(7a3cfc7e,51fbf61f,ffed3430,eb9ac40,c84164b0,21220837,29f20ed8,eacac977,9015b896,e034d2b6,684f7352,185e959b,5c45406f,6187930f,82f62269,d3ba70a) +,S(5a9def12,e06f0ac4,35b9f853,53db864c,77288050,c349ed7d,a8d8b109,b001a856,b63b74ba,868df816,9dfc30a9,1efbae1e,1617a230,3ed20239,9ecf5798,5ec4bf99) +,S(b937b3d4,287a0f84,5323cc72,95b4a201,ba9d97d3,2d968533,7caccbdc,ce8623ed,6f2ad251,a66e5ff0,77384746,9208db13,2a396dc2,17e781ce,9aa8d556,83fc0adf) +,S(d44f7fc2,97e079f4,ba0689de,dc743d4,964c6ab,8c34bbff,f95bf22e,b7d3c209,b5afe3b6,f6b02542,e6ce811b,f4bb0fce,79e7916f,fed8845c,dd83de07,9320a2d1) +,S(5980fbb1,6be7acd7,b090a2b1,c83dbf80,9148cc99,75fd6834,edf564c5,50fdf2f2,72340269,8d22b946,ae51c9c8,33d5b44d,ee884a02,e12a6d4a,23957483,32abac9) +,S(94733aeb,dce57a89,16961f41,3eb04b5e,71f0a055,29baf93f,c9ff178a,12b70511,e591b62e,5cf92ea4,306e16b5,acc5958d,a5516bf5,7a7dab17,db38cef7,44302c47) +,S(1172b006,3d5ae491,8699e756,75ddfc59,fb5cc8d4,f332ce72,f99fb99e,c9799d96,2845be1b,b97830d3,f9d04a3b,36ae381c,5654bef3,f3fa8516,3a7b9738,ed867ef4) +,S(29afd093,5ca567f7,eb4cc1c2,ebbbe8c3,bc405691,b41a2812,b7c71312,81bbe691,77e9a59d,c0aebd28,9cf2960,ebe7fc19,236c1859,7882df0f,a8e6e0c2,6d92b821) +,S(191c7ed6,72c36def,576f645c,6b475eff,793119e3,ebc3a29b,71eee3c9,cb52a635,642bdce7,90d0f104,54ebe4d6,282be83a,443f8f44,faba4c76,5b1491d8,c7c83210) +,S(237eff13,89c20d30,9902a738,fa4543d4,c2a4a2b5,6731603,500997bb,40d28eda,973d696c,ba9eb05d,73ce7491,5c1cf3d1,5a998608,7ce85854,f7bdd157,3d5c7394) +,S(d4985b52,80673074,53ec0edb,f02b9d4c,b1ce9a0c,af7c675,77789b8d,f3b5ab33,ad8fa145,94c510a7,830f3353,4ac94a98,cdf2fa30,b6a1b572,9222969c,e27eb1ea) +,S(3baed9b6,25f5d0b2,6e0132c6,20a9015,f79bd8d1,8e5ce170,20fed78f,a2238914,6ef88df1,3c7a9023,1180edfd,d041b950,3ad89f22,4ec557b7,2e900bc8,fa820c19) +,S(6e2298a6,102268b1,af97bf67,c0aba783,5463165b,1df8a824,530ee5d7,86d354f6,c3864e52,3bb5cfe0,a64adc7a,9e8926e9,876516a4,c83767d8,5ebfc39a,e125c19c) +,S(f9e87f15,2412598d,775131de,abbd0272,4d658849,cb60aac6,92280e63,8e835e9,e843a3dd,a30f9685,70fcf86e,86206c22,af40ce3a,3beb620c,aa167e63,271d95ac) +,S(f9925b7e,80b8b8b1,b5c56f80,74bd5ee1,9e419678,2c5a33d2,c59f8016,56cfbb0e,3e2fda0b,50aa7671,5810a8d2,2d4778d8,66bd2515,acf87b79,7b2f4875,dee19719) +,S(6fc34efd,5d11ae35,2cca3254,c5f8a040,52b81357,556d4d67,dd02d22b,91f4e49b,f14c656,ec946492,a6af3f2f,56fd7355,71e41b2c,dfeda7bd,ed567658,202f6e61) +,S(ab504458,4b38c846,e282038,1f02f55d,fc6a5648,1b1830c1,e4ee94e3,d11af8d9,4ad3821e,3f5219c,aab31787,2a8fbccf,f0f445d7,da30f05,d9523f55,13eee3cc) +,S(2378d11,716b6d34,85bd5a57,5c5b2c96,1b817abe,e21b7d45,38244933,5ce7e7a5,33682ec0,8c59a158,c4ef12bd,d2a46d31,6abc6dd4,f81a1fef,5be40577,4e03fab) +,S(cfa95425,8608d9b,9c568a1c,fd4b078a,ff49150f,a4681324,ac56b6e9,df569016,e200a3ae,2d82ddc2,a71767e5,842ef30c,86cd9b17,7c5e52ff,ac316e50,1e7e2) +,S(585542d9,a3a582a3,bde74fef,112924c4,e00cafcb,61518d87,f524211d,bdb98993,2035bc19,7a0ea2b6,8d1d1333,b5aadc61,c9dff2c1,e4dea034,1539ff15,9a3d18ae) +,S(fab2433,905e5b46,e47103f2,e353d503,cb141e61,2167cb4d,34946a5c,cd2c06b,7097d735,3f17734f,8eff6993,77916c9,884f67db,42f7a96c,62a3e1d9,9e341547) +,S(b10db548,be55521c,de5edc26,fa39716,154e365d,a1b8b7fd,2265d78b,7edee8cd,389efe1c,7fd8b4a5,4a42e651,b5c06a2a,7c153664,e52ef30d,90eda2d5,9e9690c8) +,S(218a8a36,273fce14,85c0c9f7,1910aff8,64910b00,6254af75,e78a6b5b,3d5c7bad,4a1984e8,e90d4695,b8dcf34a,55eaf568,b0fd34cc,528a147c,bebb052a,8e6e8604) +,S(e120bc22,fe6dc81f,dc3ead1b,a2ca966f,c5702ec,479793b1,5f0ebae3,b6c5ff3,9f92f1d5,e8aa56f6,1ef20685,c8085782,be180658,c7d2b9e1,c020c98e,cd425835) +,S(e83373fa,f80d4560,5ac4da9a,c9cfa220,b2a2c656,26857035,a228c93b,495be15f,8b915652,3c532657,7945021,d6c1b1a6,3b1fd090,ea93cf01,90fdb791,a2397a91) +,S(b7d8a393,a10590ea,91d2a495,eec07262,f2f43e29,74f7b2e5,6ba6ee92,17e3e74b,4ad6671f,58b2c7ba,7a940613,5986b3f3,5a6e13ec,8243b0fa,c8bec130,2afbad0f) +,S(75a5ed46,b2103f5f,ee808803,bf419bbc,232cf5d3,84955e8b,1d4fbaee,5194ca35,51b9670c,4c65e969,a5a56e15,25f33dc9,458c317b,6faf484a,186d52d1,757e4857) +,S(6d8d67cb,c309ecc6,3c1e04d4,aa42683c,775d5f32,51111500,9166c574,b0dd66e4,e127b8c,f431cf09,5939712e,c96d299e,efbdeeec,eeca3d2a,9f6b3d29,fb8f72fd) +,S(51a6d684,463065c8,7d3e0fee,ac72e4f3,1e5187a4,dd14d59e,ef71ee93,85fb229,7fe1069c,b384178c,ae2241fa,6886e1fd,286347f0,d2488dbb,f4d6eaf9,e722a90a) +,S(5140ba9e,81a272eb,30ac5c96,a8b054bf,870b1785,9519cdbe,4f2e6573,2a56e6bc,3b6a2c0e,7421eab3,147b61e6,d8252326,1819bd94,9e3c805f,ca2a12b2,e13599a9) +,S(b94c7812,6c60a3b0,48b28f47,f9d461e,27ffdf7f,93a3ee70,ae38962c,8060166b,bab7aa94,385d224f,f20e7c60,7f7f11db,4ec3526a,c226cc96,16af6a3e,356208f2) +,S(45046678,76689fbc,a94ebd79,ddb5d655,3e97b788,c91cee0,b62ee75c,1fd12ff3,635b3d75,a0621530,d7ff26cf,f1878d52,2864a6ea,e82b220f,d35ae5a5,edea1895) +,S(45f4025d,7b88ddaf,e39566e4,27060b7c,ac59ae7a,526ac3d6,a489424b,d677a54d,d732c322,18307ace,354ade14,21d13438,ac4c7c3,5b5021a6,cea9137f,b47465ac) +,S(c6e8912e,a197f39b,817b743d,deb3320b,558af332,96e0ebb,58c258c4,f1c0d5cc,6e0c8ecc,fd97ccd,deb08f4a,73ca13cf,b48d3633,afb74139,36550e0a,c23f04ea) +,S(6fd13fd,c7d3809d,6a3bf6b1,5208d0ab,5f476e99,57a7fc71,29824154,a05a7dd7,95922641,32be31b4,c34ea614,df6b8c1a,c386dbc2,1318868d,b40869a7,17b862e6) +,S(54fc0732,968b0207,ea2c115f,d11bfb57,b9b28164,4644d82f,45af1100,6b63d0b3,318ef9f3,b2173c45,bba5ffdd,8345a08b,66c8e183,80ef9dbb,b5d80c1f,15f95bfb) +,S(9e6ac718,6e09f698,1f384cdc,b9c48703,a8ee3726,d6d8ee3,a10499f5,e17731e1,95b4c37f,ec98faca,b41c628e,12f606e,79829b17,96ef3dce,c4aba99e,3882bc0a) +,S(85732ae2,eb7944a2,6609aea5,607604c4,c30f25ca,c365fb2,e01c7600,40542ce6,928a62fb,b082f38b,52890540,7b20f9ac,1f4841a7,bde217ab,828780ee,94749fcb) +,S(3ea5ace,80e47aa1,9bf1406d,3559fd50,85d38504,93752563,de66393e,37a652a1,db5fa543,142d36f,fcc1cc8,36b1f4c4,e82d4980,f2b277c7,bec53324,fbdc8e9c) +,S(7d40f7ae,f1866d87,8e42f9c8,d5afff76,2a427567,f6130f73,439d67fe,d14be54d,5bee61f5,31e4ebb5,679c9645,d565cf,f93b391b,f48c5504,a5054e23,47859d2a) +,S(3dc80cd7,478ea38e,1d6e1e41,aca892c4,4e4d8339,acd72fb4,c27861ae,3e7d8664,98412c68,68af56ab,19876499,fa30c8b7,8bfa5d19,b4aea0ec,52787690,49b7e3b5) +,S(2dd92155,81bbbcdb,a1900b90,9018ea30,2e40ec35,cf2b74ca,6065e8d1,f3bcc057,cdb59ee3,b5d42deb,5a4ffe56,46f5dec0,218d0e9d,569cdd3f,b96610ab,2a382fe5) +,S(a245a185,ed85c547,3b78a561,76a32e45,b35efe75,36a1fef4,569f4f70,ba510ae5,93b66fc4,6fde9554,1363bdbd,117073cd,f3f5e406,75fc5710,34bef6bc,47a2519c) +,S(5136c2e4,5004c1fd,615fe81a,e4463153,2753c670,86d1bba0,b3a35bdc,47bbec08,1743f5c5,d0a58608,a66fab55,69f7134d,7fe49fad,2dbee1a6,d01c581d,18f97d85) +,S(b65423af,618b242b,71e258e8,c22f975b,621a8900,9e0a3f12,f9f8314d,7fc6ca54,e3c39b1c,e4159306,e730c2ad,4ecec430,720355a2,815b63a9,720ef623,b91e4230) +,S(d6db49c6,4e68f6b0,59cb5a00,3c826a59,bea1e9fe,7622a2e,6eca605e,fe9a7858,cff0a39d,83e246f1,efad5dad,72c5df0d,62e9cf8f,4da81a5a,68586187,3dfd758f) +,S(ff2f739,8e5aaae1,a589a82f,e8e36315,34ff1081,cb083933,d08473b4,bd16f03,1559e2e8,49dc5aeb,50f3de13,1478fa99,e110627,aa0c4c5d,43ab03ca,dde04cdf) +,S(a7c9398c,5938a3ca,e32d2947,d8e3a58,e42a669f,5cd9b219,d8acf6f5,2cef9bf5,43e2a5a3,dae4bd5e,64a30861,7056fce6,cf86d0b7,41c3b46e,3a3ed90e,98d2d51c) +,S(faa9724a,357b6806,7be30aa6,4fb85879,1969191e,346d5d73,8a85b27c,c6608d72,ea1f8a66,8f4881fc,4ecfc556,f0ca4e12,b93ee6ca,54a0fce,b2fe51ca,174d9713) +,S(28301cf8,328171c0,74cd585f,6e5870ac,815de8d0,9c9f15c5,5540de9a,b29a86df,31910e6f,15585710,74e87f41,e5cc9c73,4ddd15bd,4436ec8f,59806afe,7901c2e4) +,S(fbde2380,ae0cccc4,e044ca59,92be8e11,5fc782eb,c1d49bc2,cbdfd1f5,d7908a09,c1a1d67c,74777fbb,68919bf4,11de555a,6485436f,7e34062b,480b7516,8a03dba3) +,S(59d8ba7b,6c71ba1d,191d4cce,662483cb,9784ede7,d1908e81,815d0a48,4b84bf7d,8a32e968,1ecf963b,3c23a9d2,f382490b,1f229b27,a663f4a6,9819c33b,f9f6149f) +,S(6c3a61d7,1c999ae0,604511ad,23f620ad,dfc7891b,925eeed1,6a5ca37f,b41b47e4,f7115905,c696c2b3,e2b82a30,9728923e,b8eba53c,363f1456,73676b59,9b50b9c0) +,S(8e53027f,8e137bfa,73eb5167,35382ba,ab3468f4,d4227b19,a45c51c2,ef3d8339,2b5ea7b9,542a83ab,a1c402ba,be13437b,c621067,96e83b7a,2cdedc52,2bcff513) +,S(113aa35e,6a5f794b,a56b04a3,d639b1e1,64a790b5,8ff310c,fbeab588,f93be16a,227c17e7,d95583e7,fdcc3ab7,b24182b5,1bb11e91,9f28fd7b,3a2e4a40,4f333962) +,S(16c4c83f,5a06b2de,9d3f39e7,1f0a9de7,af6e8fc7,b0fecaf3,2c82f1aa,fe9a5343,b4cbf71e,c3ad4635,87f8e355,cffe1ea,a19f1dc6,d233d617,776a69a9,ab8dec03) +,S(4d97594c,613de84a,e685199a,ac639a56,e5c15a0e,c00282bd,197cc2ea,90f8fbb0,b9f39a44,24cbb1e2,f77afa90,ec7266ee,64ea987e,80a32b34,483c1f2e,b0c5844a) +,S(b679693a,b3fecff0,819e0598,65406521,1f9aee9c,1ea44417,eba86ebe,32414ac3,7bfe1ba0,ae17291d,71aac3f3,66e5a927,56b53312,a6818d1a,78b7d9ef,a5d01b2f) +,S(a97c2730,a78cf5b7,538b518a,87d8fc55,f95a367d,4e021d23,79f163eb,98b6eb9a,aed1e51c,3ce7e35e,ff69e7d9,3fd3a968,59f0f23a,7bbab9d8,3b60cabd,cebd34f) +,S(7dd4e84f,6547d66b,5e0b204c,ed1d0258,4fe92ed8,6b504ae4,eb1b3d66,d43b8ca4,7956148f,de975510,5e3b5126,f82e77b,89a77b68,ffc9364c,bfb596e8,187ea99b) +,S(5e856d1d,25acc306,e8c1a2b7,a9ccea1,beb3008d,c224b38f,e2f31376,65d07c3f,7dccf6b1,55c12be2,f3da02c9,12fdb666,816558b1,11a2a545,9d88aa28,9d855e6c) +,S(c85ee868,7f507429,e5c26af6,7ef1fe45,e782661c,1fa03aa3,ee9dd9c9,302e5dd6,732de6ad,d071a0b0,b77e47bc,14e176d4,c85b39d0,ac5b4ea,55f0d367,dba5ab5d) +,S(a97ba3a5,cc2d5148,3f65c65b,483b3e4f,3c68e46b,87bcfea1,42aeed8,4c42e42b,f4eb1271,3024d1,b7612c3b,7dc3dff8,ca336f23,5db7daf9,3ca6f7d8,1bdb4a21) +,S(3c0396cf,6ff0150c,5b6bd528,4a05d0a5,3b735c85,125bd49c,f62db5b6,4eaf54a7,6ec02c02,f8d9a61e,6bb38d64,daaf3c7d,5324462e,37e2df8d,4ad12bfa,37fd2cb2) +,S(e36fcebc,e406d2a4,ad42f7dd,cc00e9f3,cf385eeb,58cee004,5aab8174,82d84f94,7c98182f,6edd358e,76d66de0,b45ae9f6,5eb75092,1a25194c,1911bd9f,7e6a1a6b) +,S(171ea2ec,b9641746,7d7edcf,ecd6d85,8577322e,6ebac130,5ab57f33,62eb4e37,9f219970,753a7b9c,e4f0c6ce,5745596a,aca8df7b,a73660c9,10add5a9,d4c533d6) +,S(a81d720a,bf8047e8,ef67bf2,71a13f47,636a5f9e,ec425649,1cbdad65,39652f9e,92b37c88,275c022b,635df6b,9246edf5,5dbd8284,eaa4cd7e,3d3ce6d1,c0ca3794) +,S(fa3b6973,4bd9939c,4b6ecd3b,f0a06327,2dc11345,1bddd717,f17b745f,bc3f99b3,f38a4e1a,bcb35097,7acc8f2e,d7f73f75,d406e7f2,22fdeea,2c12d8b9,e5d2dfa3) +,S(c117681e,b55bff67,feda80a1,ae88b4be,c806860d,91916619,288a7ce0,b31aef86,d94ae124,dca678bc,b69c9421,f295405f,d6dc1fac,de96a2b4,58ba5fd2,72faa032) +,S(5ccc0619,92744a57,683de5e3,7b956fa1,fd7287d,b4516fc9,f8635be3,a0db42a9,af1f73ea,4d2168e1,7550ffc,dcbba288,8397931b,e457ce6f,3f8082f8,cfc5f03) +,S(858b019c,8a93524f,6c86738d,cb10b534,d8caaf86,5c0ce75c,fc9b83b0,7c661a0a,7c59d30,1ffc8ce1,b0767ed7,91f84bc5,60bb8ca,ed3464ba,698a53c,160ec570) +,S(e1e217b8,69da0c77,599dcc38,40fa1d8c,a8d08b1a,4ac9a882,da1476cc,cc76fa56,83f0ef77,905f0801,4bd86e17,63b8c2cf,ab2018a8,9586620a,b49a15,9fc314ca) +,S(15a1ae40,b4fc51dc,554b75d4,db0c2bfd,62dfbbfc,dede18e1,4edbb689,91525cff,4f0453b7,e4e0e99d,9663e5c6,bb018007,b52c8e14,d78a28d,c4a888e4,8c4326c2) +,S(1b9a142f,fc4d03ea,4b079f2d,b05fad98,8ddb2d32,b359967f,c173801f,63320825,59bda7ed,5b691c20,4fc8f8ac,f53be298,ae628954,a8134d0f,dd097e67,be9ff9b6) +#endif +}; +#undef S diff --git a/src/secp256k1/src/precomputed_ecmult.h b/src/secp256k1/src/precomputed_ecmult.h new file mode 100644 index 000000000..17df10296 --- /dev/null +++ b/src/secp256k1/src/precomputed_ecmult.h @@ -0,0 +1,38 @@ +/***************************************************************************************************** + * Copyright (c) 2013, 2014, 2017, 2021 Pieter Wuille, Andrew Poelstra, Jonas Nick, Russell O'Connor * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php. * + *****************************************************************************************************/ + +#ifndef SECP256K1_PRECOMPUTED_ECMULT_H +#define SECP256K1_PRECOMPUTED_ECMULT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ecmult.h" +#include "group.h" +#if defined(EXHAUSTIVE_TEST_ORDER) +# if EXHAUSTIVE_TEST_ORDER == 7 +# define WINDOW_G 3 +# elif EXHAUSTIVE_TEST_ORDER == 13 +# define WINDOW_G 4 +# elif EXHAUSTIVE_TEST_ORDER == 199 +# define WINDOW_G 8 +# else +# error No known generator for the specified exhaustive test group order. +# endif +static secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)]; +static secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)]; +#else /* !defined(EXHAUSTIVE_TEST_ORDER) */ +# define WINDOW_G ECMULT_WINDOW_SIZE +extern const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)]; +extern const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)]; +#endif /* defined(EXHAUSTIVE_TEST_ORDER) */ + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_PRECOMPUTED_ECMULT_H */ diff --git a/src/secp256k1/src/precomputed_ecmult_gen.c b/src/secp256k1/src/precomputed_ecmult_gen.c new file mode 100644 index 000000000..248fb077e --- /dev/null +++ b/src/secp256k1/src/precomputed_ecmult_gen.c @@ -0,0 +1,1779 @@ +/* This file was automatically generated by precompute_ecmult_gen. */ +/* See ecmult_gen_impl.h for details about the contents of this file. */ +#include "group.h" +#include "ecmult_gen.h" +#include "precomputed_ecmult_gen.h" +#ifdef EXHAUSTIVE_TEST_ORDER +# error Cannot compile precomputed_ecmult_gen.c in exhaustive test mode +#endif /* EXHAUSTIVE_TEST_ORDER */ +#define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u) +const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS] = { +#if 0 +#elif (COMB_BLOCKS == 2) && (COMB_TEETH == 5) && (COMB_SPACING == 26) +{S(7081b567,8cb87d01,99c9c76e,d1e0a5e0,1d784be9,27f6b135,161e0fd0,3f39b473,ad5222ac,f062cb39,21b234a7,15b626ae,f780b307,9b5122d1,53210f42,d9369242), +S(228af17e,df90d1cc,a40173e9,478fa445,9780dacd,c3f15b90,fda5d00e,1faa1b51,8ff47c4d,a4ba636a,f656da9,12a81f79,6252496d,1e519886,b2c2b073,25be2b4a), +S(b515ebe0,f48fb34e,9e01824c,d90553af,db116579,96667847,5ebaa700,242fd722,4cf08191,510fbf0,51f9e19a,198f11f6,ea31268c,2a6d384c,60557250,f0553c50), +S(c37581f1,35446ff3,e80cde04,ec987f08,5af3af9f,71d87494,99b03ba,dcb8f78c,9e46324a,8d8754fe,fbda7c34,2446cf72,b7472708,25cfb92f,a22a4e73,4f1e9bc7), +S(768392e3,b2ccbbc,21792f2d,6f43c63d,efcc249,bcd549cb,96abd8ef,4ae59e90,5d061a40,ac3f93d1,82f0f7a9,914fff5d,90bbacb9,1965882c,591e89bf,c49da994), +S(a9db5de9,2d96c715,8ce5ee00,3d186cf1,976f6a76,f1be0714,726594fb,6c1ba564,743df2a8,ee73fb4a,47b3d0f,2f2b8cab,5af5227,f232946a,82676e4e,8bb70c8b), +S(58921d8d,3712f640,657e762a,737ccd0a,cece1459,a2ba8323,20b43903,d4953f93,78c26959,7e422c02,c01fcbef,484a9d44,9f635c82,849bc2a3,2a9bf6ef,3e6f1e7e), +S(bae41760,ade541bb,60028015,f6906b78,aa41515a,d995dc40,afa091da,77ae8b9a,716ff74d,ad6dc24c,77ebe31f,abe19792,41d18b0f,43ff7598,c6ea04c6,6899219f), +S(e836ff6c,6f9e02e2,4e6f6bcc,2f2460b4,9de88730,6d69241f,b22fa574,7e100f80,77c41df1,4d87b6d3,de74deca,ad426635,f85daca0,ab966a4,5a26a972,32105c5), +S(c7f17d19,fbbf28e2,543fceb2,4268ff6f,1b85e189,79b09991,7457fca0,9ba1b5a4,23a6d4aa,83ee15bf,f1655575,3e4162c5,8acedadb,4c4abfc2,54b7018,f477514b), +S(79c337ad,8835046e,572eea35,df76276b,84e99172,7bbc68a6,8c0b743,28619a,af6cf2b3,9c6b51c9,f1f92e42,6598dcbe,39da6196,2abae0da,d5fc8d0a,aa16a875), +S(1e77739b,12d20887,2bfe200b,d202972a,544804f5,2b0969c5,c22fbe1c,b8b23837,880c7490,c0455322,a0f1e67d,34b3fd1e,33c1e7da,83b9b047,5aef7bc1,332ccee5), +S(b1d314c1,74d493f6,c36a893a,830d90b,f1e86d0c,6deceee0,edb77ff4,31e9607f,8a4ba010,ec3e47cb,29d8f056,e056eb73,3b6b35f6,4c742d06,9ab832f4,68bcd3be), +S(2498aea6,471f748a,f14e50e6,bc17e0a3,ee09514d,fccbd174,f8a21d02,dd0fae48,55a3949f,2e4188e2,b3d344ce,6f0276b5,99b121bc,358e8fe6,bc4f03f5,a8219d08), +S(42d0778e,27e87710,dd19285e,d531af1e,7064386d,11b1b3c9,fb788bf2,b0c112dc,37888909,d943825a,9610e37a,362549f,547b7c7d,ae64a27e,c34290f0,b4fc5fcc), +S(af09a9f8,3a21b2fd,69ce14c5,495c680e,df18e426,c1ca5a1c,af56246f,d6ea5bbd,8eae43ca,8e591017,62c4cb02,9e5cacd2,58f0fdb1,ddbef7e2,c5765208,107c96af)}, +{S(4a7f72ed,ebd8df1f,13db15be,3ab296a8,d67edee4,37965f03,eb501205,3518ddea,1f8c4dc1,2d0e59e7,7d30a326,fd4ecbf8,aacd5fb6,7dc7169,898a9708,ac46b972), +S(c4d4f8d4,949d1ff6,559925db,f4d34972,af16062b,d59493b8,b1e0d546,c2870874,cd7f93e6,c0ee3fdc,4267625a,a6620540,4f6d80c3,d0f7210a,ce6b52e,c350706e), +S(773627f7,dcc56184,7eabafa3,42fffade,325b8ee2,a96f2f77,cbe675a6,2942277d,fb0ad731,9bff4c9c,270d93d1,89a4c380,218ec9df,1f564228,641ab35c,46526bd6), +S(ff443101,befd0bd9,e1914864,3eceea06,b9cf711b,1405e3d5,3883a29f,906063ed,aa1b7d9f,59cbddf7,bc1ad03c,44c4295e,efb406ca,7960c683,eca508d1,b2a9a2dd), +S(cbfb6813,a6874b65,9234e866,c2875f70,e45f9f76,3d634752,3f86040c,880a2e56,ed85d1a,6620c1a4,85ba3038,d2ee6590,4e27206b,eaa531ec,1eb5b886,8232cbca), +S(4e85c77b,27788563,aaeba139,975f7125,f3c933b4,8d67ba6a,2e964243,bdd7bb51,200a9d9b,3400c87b,1fb98422,3200aeb8,e6a5af1d,58c061f0,d059c0b8,d431c08a), +S(d58178ed,f0ce32e3,39a524f7,ad389f7d,8817f41f,ed782612,45218816,2a67d4f0,4840df81,5dab9596,93f9284c,54bdee72,f4861d39,3944c648,ecd76ec5,6dd3225e), +S(670f8f7,9b66496d,13692558,d56d4cd7,a1eeb0d6,9e5133ab,4f0a064b,7152fce4,1a72206,6f530586,927c2e39,370d11f3,e251bd9e,eee10303,406b7592,716333d6), +S(10b41abd,ed4de2fe,b7a40050,e61e5982,89a8cf3,6eaa608c,f2a82c63,5788f1d0,532d168d,680e9c74,4d5111da,6aab902a,9a0f5283,7836b9d9,585dbc8b,1a407de4), +S(76abd21b,103aea9a,f04b4040,297894ce,a501474d,9d38d30,8ed02cc7,137bc7c9,b5a432ad,9e92935f,7040897e,3b04152f,693c5f2,cc1e1d53,2c33f70d,185318e1), +S(4335e10d,5e4f02e6,f1b58a0d,ffb473ec,15f735a3,bf96c140,50bae78c,37061db2,1301143b,1f29f122,afcd0230,d46b6b38,b154e589,92b7eeb4,16088968,a7d57485), +S(2b3966f0,7e37e771,ac4c68ad,b8eef3c0,977cb895,2f47676b,e658aa86,76057f67,5382a6c5,bcdd1a01,6d79d817,80faf650,c0a486bc,ea8fb592,1cd3492c,42c91c3e), +S(f7738800,b7fdf237,dc7f03fc,43724011,8b53aea4,4d7953d4,276f3b4d,bf5c0ff8,c1de7924,ef8dfeed,c4fb409,f1a15d4c,5e8ab42c,90ea9c92,867b5b5c,f2921605), +S(5cb07874,ac4ffb86,2da619be,4c8fa38b,e8b261ec,3ec73a12,cd4cf8fc,4f8d5dae,549d1896,4931dabf,ba4553a2,461f2660,87733454,ea8eec6b,f671e3de,60c70340), +S(5cc85d41,7b2ae9ac,dedb1b44,ce78d8a7,8f56b878,1a4b3af6,635a55c1,fead3be7,66a48c79,301e57d4,54cc8644,d2e778a8,45d85762,2c10eb98,d77eb873,e58bbeca), +S(f27211c9,a067b01c,7fe7fbf7,7b5d9b0,eb0f2475,9d541457,6eb24ba4,19fd3db8,e26d28eb,89f7b518,e9ae0b88,fadbfb9b,641d3a44,c59c6f93,ec28a541,486e041e)} +#elif (COMB_BLOCKS == 11) && (COMB_TEETH == 6) && (COMB_SPACING == 4) +{S(629bee58,a391595f,eb20c534,4933937a,cdb2eba1,86d49f8b,845c1b5f,4ca87182,8dae4162,73c6c068,2e2aede4,76efa86b,7612c07e,f72070d0,dc4486f1,47e95085), +S(6fd5e13c,a94b874b,28cd574b,726efdbf,143ab108,1089b846,7b5b2ebe,6c6a3c8f,4a4db306,52c9772a,868b2859,57c5a005,d83f6afa,6e65d87c,700da998,ce651396), +S(609b6576,191514f4,83f5b428,500cfbaf,96871b8b,3348fe5c,1a131768,bb266b6f,90abb9c1,1bf184d2,8dbc424a,bbf74eb8,de4e0582,2ea5dd93,7d1e8b30,e5f695e7), +S(b5ec0263,27cfd922,23a46abf,a755e6fc,547806ea,333666cf,a43865a3,f4ae647,143353c,cc733d02,617f7764,7dc29c65,67a2e245,aed27120,e5fc11b3,95ffb9c9), +S(ac1fff6,cd869ed5,ca920544,1ceb990c,ce304998,10e27587,6536dcd8,1d4cd15,67a7a012,3e5ea292,85dedf0a,9f138b80,58a1d23c,2a233c5d,783bec39,b6482126), +S(c1807552,644130a3,f9b8aea0,29fdccdd,59e03e27,ff2cb1f9,9d13cf0d,42da7b13,85e99587,f77a25b0,bc64b882,bd279fa3,5be0262e,c2f5253c,70a43261,bd1fe361), +S(42919187,2b3c6877,c3b91266,79266e49,9fa37a8d,74d094a8,8c4a3638,89c2717c,1e4bd2df,a9757d42,285cca1d,856b49b0,ad481ad,828d826e,8921f744,7e04d6ad), +S(d4b10124,283afe03,4b278e5a,91daf61b,be608417,fc845e1f,9b9f52a5,43659f2,fd070859,75e2bad6,b34e0396,47b81a87,cab9fb,fdba802f,6ae3cc8b,67cc98cb), +S(ec4c2c4c,722a54ae,edc37acd,cb6ac490,3077bdf6,3f2c145c,7eb25e74,87c046aa,327bfe37,d745283b,a385dfd0,8e30b61e,2b4e5499,4432be41,fd3f0d31,c00e9ed7), +S(559e40a7,e151eeeb,46442b86,aaf4922c,12030adf,7279bb2,2bca24c6,67566a05,5483756f,60d26b97,82033592,cec21681,c5b5496a,c47f600,2013e5e5,f2ea82df), +S(c1d2e92,19c0138a,9f0a34fd,acc92f4,eafe13cf,1c004b6a,3175133d,6cc7bf12,8fa653f2,a20c1262,be59c7db,20f12579,b2adb1ff,54d84ebc,e5ad4116,3d2ebc23), +S(1becc6c0,e4861825,576624e4,fa622c01,139d9cb0,50c12062,9b87ae6b,9622245,d8516472,656ea10f,5b23839d,43f64e,85fc727b,faafcb6c,fc9c6e89,bf9c038d), +S(c483c2b3,8ceefd35,a9fa9e7a,da242c,d618cd6c,8b3bcb62,e39d68e,c1effab2,71964aca,9101863a,d7c865f2,41e814ef,3c20face,f46246b6,4a742026,471b2495), +S(3487162e,40c7efa6,92bc827f,9af9121e,af5ac549,b4ab6585,6353d699,a94a3783,f5b29042,28833bd6,e3ce0deb,dc0396ca,42701752,f930f437,d042cab2,8f135c1a), +S(eb3b21fa,f733f15a,b94ee0e4,3378e9b6,47571aff,d69292ad,a1b107c8,ad511d5f,cd95b4ad,b8768858,cb750cbb,4fa64523,6b6fde59,ceb71dd4,73a7fe48,9e5a2304), +S(21c8dd7c,34d3f333,758d1d1b,72748fe0,8695af46,3d0bd9ad,e4a6498d,1d01e9df,634d8671,7b885f36,a54b6691,35c3c026,667f3cb6,f2577701,beb0d3bb,84f07940), +S(608e33b8,14998177,5d78e286,d5c6436f,99623417,4e887da0,a95274a0,d120b57d,5d094ca1,181f8dca,87d1e043,f798bfaa,f1004edf,a470fce0,7ff8b124,328397a5), +S(f77710b4,dbc5f7fb,ac6b015e,956ed26a,6bbfb540,6a931a30,d4215aed,de610c7,7508a762,a7333ca8,ac244d6,47e47647,c62c82d,387d8df6,175fb563,1de83d54), +S(f37040aa,b0347fe,976d5980,cbd83db5,4d8cb90e,7f2d2118,85159911,af769d61,df228dd7,67fa0d5c,d44c3f6d,b09e662b,99faa5c6,688b088d,949ddab1,6f11e42), +S(7bf929f3,25374a1b,a0cbaa50,db47fea2,a5d1e3f9,3c34bddc,ea5c2250,d8252071,8fe2d2bf,b23a6755,50f16760,a6511ae8,4e4d316c,45d7cd62,4411ad3f,c72805c), +S(261bc0f3,a69d636a,b82248,6132bf91,2ab7ca95,e9a4925,b3f68bb4,745ee63a,68f52f7,df1ec349,77856c74,7b280907,cb3d456f,988ff7a7,225c4ace,8457a804), +S(60ef138d,e7086d09,711defff,68dad211,88d7f878,d616efb6,dbd3d498,5b730243,ca7c3297,3c56b178,b0cfb8a,da98748a,c3d06cc0,7fe050d7,3a6a9695,52500f8e), +S(8bfef3d6,4be90cac,8b83bf20,906b4277,75a6d982,23d5141f,b74e3851,50bc5eec,ea8b6b4c,c7c8d9ba,809ca811,3621c72c,b82006d7,81ff5f25,32010fc9,57858324), +S(9ca85f3d,4ba47ab0,e9fc0a28,74cd10a4,a8ba9aeb,ff319405,6140fea9,6e395ff3,41182ce8,96554f45,93768fc3,a656981e,69719f05,5469f9e4,ba79de22,bcfe8a8e), +S(38cd0c79,437b1299,9567999a,f54d4f8e,82a6354c,8a93a5ef,56ca2ff5,8a1ce180,c96f5ed,d78de5,9f560716,82b60b13,f06de5f9,49206258,80544bfc,73dfa76a), +S(be37e05b,9369b159,6b390290,3eb8fa0f,f221f599,1270c5f0,47edf62e,a1d0e6e2,4fc860a7,1fa69a27,fbefd8c0,906fc68f,4713fa2a,b37aaa2a,7edd3c8c,d100963c), +S(aa640e99,b091512f,85faed73,a3ac8ede,ee120513,b661f812,9a046a74,d624aaaf,dfb48ad1,a01fd508,e5652406,b681cc9e,13d0d600,68411dae,72d68061,6f6b94ff), +S(d08bb931,f008a1a7,38a16200,9fbf1ab3,d3d455dd,47f45cd6,617e81,8b8e0245,18d92218,21c9a2ed,bcaa5d26,2b7aba18,1f7f1e,7f2007a9,ec6f7d18,f038d723), +S(4e6ed1f6,c39c894f,eaa53e1c,e115fda,75fecf8a,55794628,d9a2202a,9dd5c719,1bb97d73,2e0e137c,e9c94d4c,763f9a6d,e1496548,904460d,df1c3a2f,73b66c1e), +S(867e83ae,610a24bf,401119d,8e1af6dd,414a7161,52b2ffdc,50f71f77,2fa8409c,d5d21ad5,ef365994,b3ad0533,92df82a5,6f677cb3,8500781b,a769dd50,1f6d235), +S(6031361c,6dffc505,dc7e1887,bc75c1d9,5a9db023,8b7240e4,c254f855,b35686fa,b9a9a979,c6b8c576,11eeb783,8b4ac2ae,66506bb0,a203fdf6,3efa9b59,98c7643d), +S(8502d0f,34684503,68784822,f4510c5c,562ad30f,be29a792,f0667e83,b6f95773,cd0e6511,763674b5,57fac9f1,bb62f9e2,3163a0a4,cf08cd52,a6d10064,d8b02ade)}, +{S(840e41bf,13203a96,f41ea041,1ec034fd,db0dac3d,53b6bbc9,4f8a3a78,d0371235,71b51550,a79af319,56c9b3de,fcb167d0,c3e318eb,a852eedc,78490dc5,fdaaca85), +S(5678a034,a24a1607,d78036b2,23eaa3c3,845c9f9e,7293f656,7390354,2f714d5f,57f624be,ff5d1774,c6962dd4,d14cc565,acae170c,7aaf8c6d,b0552daf,e3bdf48c), +S(722012a5,57ed9c9f,9d737a87,39954469,ca12ab29,9dedbaca,507c1259,7a27c6f1,6c28881e,a0e62453,80d73a7f,499f05a4,d9a9c04f,c2ba11e6,9fdd0b6,7c5d5ee4), +S(70c638d9,3eeab892,8bccf1e4,82368eac,a03ca8f1,b30f7b2d,34258539,8c95c014,f8635082,3dc2a200,67dfb293,891929cf,18a810f2,5c85c169,159a4b44,b190a029), +S(2ce4a530,b5f6c77d,c0c4d891,6e89b24b,c25c04c2,723842d4,29300f82,5e260458,6f306154,4d1195ea,98b972e,91a962e2,95a2a4eb,9be03949,a789b01c,f61dd705), +S(89e71182,851bb441,c0af4e31,cad89dc7,3947369b,fef12eb6,b1dfa71a,72498c51,20b7ffd5,878718c2,2b5bfdc,ac422794,63aac0da,4bcdec2a,8f3b538e,3a9f574d), +S(f01f06b8,da0ba34c,e1045e83,adb95fbe,b76f134b,35235539,6b07f8de,9dd25869,afa0fb5b,45ad1f24,73460f17,5ff4bfd8,f162262,65c423fd,ebd92bc1,72f3aa7b), +S(f0c217c8,1dda2201,1f46648a,e096906d,71fba006,1b4dfdb9,85dfe59c,9d8f1894,d5ddd6b7,bdf2a6f6,cba166ae,4c01c60,b012c9ee,ad954a46,6f631f9f,928b3fc), +S(bd9acff2,87cc2a2b,396aef61,b0937a49,c43a2366,a2c3c461,c1283987,a7b6f53,828de0ad,b9067b2d,8f8fefe6,ce3a5c94,e7d63ea1,7c891d2c,5f266e8c,a5fac116), +S(ee314d97,2b9c10a4,fec9f8d,ccab0015,d4923e52,d915eae8,b319911c,7a8fe379,82057aef,ff05c496,ba8b4753,c4e57832,aba9a724,adf70a9f,1b3765ef,952ab52), +S(d39fa7b,9b87ee2a,aa32454a,13470406,c8024c39,cf387143,3364de62,cb94c103,5cf1309,530f9e09,c9b38ad9,3a778ec5,533f60f6,4a42e31,1cd97c2f,c984b9cc), +S(5ab1a07e,5f6aff68,16780e5a,23c9faff,b29cd7d7,5bfb8984,4834fb6b,c6b34a14,deac47fd,6567023e,b5ec7a94,a0133bbf,158c0425,c0a6d288,23558986,314d54bd), +S(b1a4ecc6,94bb5212,98e80464,cffacd32,331ebc7c,4d545141,a129b522,a4818830,9772044f,3722ec57,d76a049b,2afce2b6,319f0bbc,b17c7f06,175a288f,3ce3534d), +S(56015e42,81aed744,a0859e29,eb913a7c,b44ebd39,2ed8c0b7,1eb42f9c,9c93c8ba,8f973650,45f7d8a9,24a276ac,20056895,fb2b0aaa,4b35468a,a51f41f,722b9d33), +S(7d99a5c5,21543229,c8f9c7b,50dbaa64,b200bf86,47c71b35,a0105dde,2b3a8e0c,eae09ef,3ffdec8b,9a30291c,244c0a72,6466c26d,fe2c8d30,174f57fd,ec6aba60), +S(ab7d21f9,1e7dac68,afd6e4e4,794551d5,55a9f42,59825109,5b25652e,946ebdae,8805a806,710ed7b0,fb99ff30,130c074c,6a0be3d2,abe6e155,f9c07496,f8b92454), +S(b31a5f85,d65e5200,8f5261cd,fd6d36f6,4b2a1f3a,6f08d7b8,defc7e84,21d0f7db,2e6d46c3,3733b3b5,597a133f,7459ee2b,570ba333,4d1e7648,1980dda,1aa8d2d3), +S(30af789e,850c8ef8,9eeb226b,27786679,442a9a0c,35cb7337,98345400,29220fc5,f6651170,c83e24e1,eb6e8da,ffa0b0bb,a6c53676,72558d44,c5b77176,3d9b5d7f), +S(8d87f808,98fb8007,ffd5093a,bc6bad2d,23e09e90,e6967031,48d5418,4c60e0ae,3d14332c,21df527f,a2e98243,d3ed4bd0,6634d799,9f62efd,e8d6c919,8ed716a3), +S(a961c219,eb9ea127,205af30d,909ef732,8cc599a,b2080966,f615796e,1126dd98,3e235ae0,37b10074,32e5cee5,56778856,1d552e3e,bf40bb82,5b527cac,8e1e5b9c), +S(b9c2de21,40cea728,2aec5293,bb77dc7b,550a72c8,84389c05,310651bc,7570f2ea,74171cd9,4a5683b7,5bc7c5d9,b4c8fc10,1fecc3b1,b7349ce,840b6ca4,877fed21), +S(cd041ff2,e8191946,8fd7ef0,c33021d2,9533cd75,64772252,c686f718,7e33166b,3f54a9af,e8597bb,faccbbd2,9280e60,93d31bc5,b366aff2,2c95e21b,fbca6041), +S(adfce0b5,8a9cef85,e690b84a,c7ccc6,8f5aa4de,ada5e265,3720412b,e1f19ae,8ae2dcdb,c6ce83be,825593d6,87f9afbe,a88af079,564c60d5,c9b88898,897515ea), +S(833a50c2,cb5507c6,60bd450e,cde341c9,64f2809a,e400215,c52dfaa0,9825df96,de85e216,493cdb4c,4541f720,201f4c67,1472b6a,d61cf8ea,3cf54ccd,a4584b77), +S(3475027a,628ea423,2d689d70,d45e975d,39d62303,282b16b,1f94a427,3d7ea746,97c3052d,9270df78,6efbd2e7,beaa58cd,f864e28,905e3ab5,507cb9aa,e3c69160), +S(cbda2707,e61d9e40,124668ef,7ed83973,dd293dfd,221396a8,d27e7aa1,392edcb3,fd5023a8,af8eff08,f0e3c8b9,b6a22a99,b3ec3aeb,7e0104dc,1f8409cc,d83d8a02), +S(963722ec,aced2105,2bf447fe,619a8532,dc78816e,2e17a111,3ffc601d,9bec0381,f31feba,259f7d0e,3c326c69,53aae60c,1e0479da,5ca839ea,77c6f1a4,9419172c), +S(fc531e1d,49d2a984,83c861b2,b299b8fd,b26831e0,3bced6da,5c4449b0,18363be7,9f9cc9b0,8d72ced1,998c6329,a17695d9,c94cfc76,ba9f6943,1c5dab83,60f2942), +S(dab255f5,b47d96d8,ec5272b,5cde4ca4,33070920,84aa4866,d53b674a,330aea2,feeda13b,a544f090,b790514a,42064cea,509bb3f3,9dbd4567,6bd75414,37579d7c), +S(ac5b161c,fcc42998,71b49d1f,8cf35bd4,dd6bf98e,5f84f2f6,420bc363,b9cac257,4511d4d3,1fa26d7f,e56f671d,7467bbd0,41358b36,b60775f8,65f7d491,3d17b685), +S(b62fbfcc,2aca0706,b0fe67f4,ed5d96b,4a65b048,f73e8f4c,9782e1ee,36189e31,6549e8c2,f77bc824,4e1a90b4,e42ca64a,5ade1768,a0996d69,8d7b047,7bfbf862), +S(56938443,5e509000,5cf3dd18,ea629078,e15097e9,da7bb5c5,737cfa0a,a40e8818,f86a1815,4f379054,11016994,9b2eba6b,477c32b8,b68091a4,4b14990a,5e8eaf92)}, +{S(7c028bd1,75367939,74871a6b,cc957d95,3813331c,87ae606c,17aecbdf,a704ebb4,4fea040b,a0ef0319,91ff9b8b,7829c0c3,93895439,d506c5c3,1a1a3fd9,e32e7e59), +S(24e966d0,bf1227ca,11eb8fd7,8dc5369d,2bb9b681,927990b6,dd02b948,fc18a169,72b7fa7c,f929a6b1,c556a372,4149a279,8d57f504,4b50a1a7,922cfddc,17308ca7), +S(a2b7db3a,265b30aa,94823577,6d5ea839,c4fe564b,97a5e3da,313f7fc4,5de617c5,5e895546,a2778162,4a91ea4e,c104c911,85f3e954,f8378fc2,bea41a3,f1cc9e15), +S(e365c695,449c9976,cb67ede3,325d9229,ee526349,e6b255f7,f4e72184,6722580b,eff99cdf,a0920505,ac37e46,b812d47b,31a29113,e491208,a19e3edb,557ac2e), +S(c3d90905,297c3a65,2c6c3d5d,915cd9c2,d741f472,4a47c8cf,35e5cf3,cac9dd11,8e9ebbd,34a7dd02,24fa6e33,193cdd72,4906dc81,a5aa41ee,4a34d3d0,4b2ee5b3), +S(6e4e23cd,ee4388d6,e8b61f8a,af7678d1,a62b3e1a,3385d278,895a02a,de4c9f00,f69b843b,ae39cf41,11fe9b99,a0287057,5695421e,e1ea690e,2e76d937,e7e32c01), +S(92eb38a6,3743088a,39b58cad,4eb4a27e,a8b08c27,444d704,6d2aa5f4,89713217,22af8d6c,d3567a6d,cf7403ef,e0afea02,48ea4266,1577005a,9afb5f2a,19611864), +S(b3c84323,e2d022fd,d2a80d07,203f4a26,f5d32dae,62842072,9663a994,3a829d08,9a899de,82329d94,83b88e1e,1bce53ed,8714cad4,c68cd491,1b71bc0a,e3839821), +S(daae5199,57a6002,8958b22e,6dbbee20,c4bbd8e1,7ed589e,b8528f35,7a4dbb40,afec8a03,321586ec,ac38e941,7fa8e342,f0542193,59decf5e,56479a39,c32f92af), +S(7afb9a56,89fd1344,cdbc5c72,62dff7b,718c2e47,a6e19ec,665af795,86ed0161,d1c2c9cb,60a191a9,a3db7e20,2ebc2eff,444bbaa8,d81ba086,6f8825f3,8c765f53), +S(8d0c4a2a,e72b54c6,236d997d,61a2a24f,ddbd39c7,ee16f1e7,d7c45b22,b4bfdac8,15a58f4f,fdad9e3f,b4d33c7e,170747cc,6a5abfdd,bfbe5814,ef0c3611,6a0a9e91), +S(24d8c0ca,53400126,347e67f3,72f19298,595b6d33,76ba2f19,2fba6dd1,7b57bda1,7c01404f,bd316814,f10734a1,559049ca,d6f20e90,d6b13ed5,f9542630,db77f9ec), +S(4c2b7a58,87a49df4,f98cfc72,22fdd832,362b8cc4,c5f4fff4,d0a20674,c9749bde,c2a1cfcd,e8636288,31a5c450,fb5cf552,c76e581f,9274e5d9,888a3a68,c37ca349), +S(2028e381,f42cc2f7,a8829308,bb72881e,94e5b54e,3331d848,898ccabc,bf971cb,16debea0,203f0709,d9b55b0d,2b8651b5,cb085d8c,8f56708d,c1fe4d33,a616dfab), +S(d11e0753,f72b7c4e,641ed50f,84dfa24d,a0baea77,98fcf062,8b2696ba,a7ec2529,55af37fe,88324646,777387e5,a9b18059,ace20e9,becd7488,5e43fee5,fa6809e2), +S(72309865,b8a1a6f3,9d6d561f,43ddf5b7,f2d022ed,95df130b,2563eaff,9008a95,f3fca0be,3ce7d9eb,e1648964,58eba87e,934d000b,a535f223,868dae11,dd5230e), +S(adca4785,cbf38a0f,28de7b36,daccc761,89b8b918,f3fafa7b,abbbd9b1,4ed5485f,880d76a6,fbcce864,657edb9b,349f124d,1b22c7af,2b0ff833,40d7a0c4,84b49de0), +S(bb43e0c0,93d58c6f,3946e58e,4727d7e2,d223c84d,513c4e2,222406bc,aafb03c2,c00f7dc8,fcbd55b3,6a95f2f4,a3a07b7f,9599736f,af099a69,e2fc2630,a2aef1d5), +S(179eb588,d1c7aa5a,24e51f5e,6d86e4ed,2e6f0f98,bf956b71,ec1caca1,b7f775cf,42f461c8,a784f643,d5a8682a,6ec9b470,455f0ab9,f269140,b1ad2376,95f64afd), +S(9144fc75,b00f8608,714f027,16b30575,be19dc01,d3689e8d,2665d967,6a077d11,b168c324,7dc6c31e,8302b6df,a2e7c053,dd849538,f9d45b87,14d889ec,cc789704), +S(d6cd2a7a,b7253b86,34231eb3,2793f062,bdf103b2,4fade912,6206e745,a3b429a7,9903e507,dc5e3a61,d73fc83c,5c4ba476,6ae92655,9b208369,bee55948,4bfe444a), +S(264845ed,623bd3f3,808ca51e,517def45,1fbc237b,e47e3ded,70c3769b,391a9f83,cb5022fe,3d535f4d,192e73d8,5d9ede23,78e0824c,e560a97,d53aabdd,f8717377), +S(2df9621c,cf3e67da,54c47ddf,81e3ba7e,7b50e713,cc7d08ac,d64f47e9,85c4f334,8058f6e8,139d8e87,cce4a389,c470e3d8,45da3537,ccead4f3,abbccf42,a5fcc49f), +S(ccfd6c4,c5966144,32d2efc4,1ccfa0eb,3e0b44ed,a4558128,c3e4d68d,949a5cac,bc77b87b,5f50d3a9,6b28d5cf,fb371b4a,36cc1360,15c548b3,f98d661a,beda8acf), +S(4e2e6052,cd35d585,6531fcf1,73bf124d,3c569bf6,6cc54ed6,9a520d42,cb836296,6381d100,e5d9ae50,f1821122,b8e91a5e,f365edee,556fee1c,2fdb6d17,3fd29c65), +S(6603d715,ed0f8725,7d9c3f29,cf86ad4c,aa0500ca,fa16a708,3168e265,b87d0255,9d2eda4a,6195e2c4,a4576e4d,5d02383,a3a01b2c,f94ab8fd,fef2f338,15807d31), +S(f09ef30d,fade81df,2fcafe31,387ed34,5a8029cb,e93fec3b,eaaeb8ac,8adc9803,f9f3039e,5b73c7cc,cf841eb8,1c15203f,ba192e,9eb39aeb,36f56c29,44b286bb), +S(ebf4611f,cb3a44a9,f9d5b00a,c559dbb5,394840c8,41a06496,bbd2011a,2508cc34,1e183324,9a610584,5d62ef4b,685152e5,c75c7525,c703f81,51bdd87c,6fda0d43), +S(31fc3226,c19aea7b,4845ff2a,ddeca6a9,36505a49,92102cb3,499d66f8,a6e1a3b5,49babd41,b1e8a6c,a728f971,d8d8cb76,47cdb94b,89767a67,fd975ac6,93f144d1), +S(be1e37d9,dadc4b4b,7a5ba74d,fb7eb55c,2b0a4fdd,81564f8d,51038afe,f716e3d2,de6f0c3d,b6efc974,975a7d6d,3c6e9d44,a712879e,4e95c612,164273bc,5de1f4b), +S(f8034e14,2425cdce,31e82b49,cde33ba8,3630d1ae,283b8b47,73960539,c67f1652,a302e3b1,860252a5,e7114568,748746ad,1e02c2e4,280bc913,dd4b4d2f,92a561af), +S(21e709bf,369cdb2d,b7f51b10,f0ff56c1,2c1938d6,18419d00,34e57d94,7b83d5e2,d7e09ab1,2e7d04f5,aab3a75f,8cf75876,ddc3cf0d,cb4b9526,ea7f755a,dfe1e495)}, +{S(638b6618,c1498946,75a1a943,8755a68,30d7dd3c,9d8a9f20,d65a7161,9c8b477f,e4e66035,e947c73a,c34a48b,b43a3762,d1038e6,893dee23,cf658a12,190d2a56), +S(4b6ce93a,acd4954e,cf0febd1,fc9a965e,ae7fbc1e,e24a12d3,a4b86573,62eb255d,8cea4838,1bdf4e5d,79e25ed0,1aab94b3,5f00267b,3ec7eeb6,63833507,583407b3), +S(e2a42110,3e8e2a80,a6451f71,b348434d,7d508023,beeed16a,d87417b8,5c7b7913,5fc15b62,124014e7,fa8f95cf,78ce8415,d46a1f97,d88be55a,76431786,8de3d605), +S(6949956,91582c2c,2e14576f,5ec0dd37,65133237,a4e43369,633edf69,933d1f75,5ca8c2a6,329882f5,2daf48e2,e291c48d,febbbd29,9ccc9ff,3ad54ed,33ccef43), +S(55d836af,2dd4b8a5,7f20eb57,6886790e,a3ccfcf9,2a2940b3,646f263d,685f62cd,7cc1ffe6,37781c72,d3cc26dd,9d4147c2,183c8e0a,95b87742,52ac47ac,19db266), +S(442a3090,46bbf3ef,503a138,b60ddfa0,ea4a5901,ae801003,f3f18ad9,9703a44e,129d9913,eff78c49,16e67c4c,cb658784,fa47da35,92ea1c98,4054b371,a275639b), +S(76981efa,29944c2d,9463fc94,67a92e1f,9403c287,dd595292,4860aee4,b78626ca,c87cb5c9,ce93ff3c,23189404,cabf8e09,996c4cde,e67935d5,874c705d,6a7f95ae), +S(42ad08fc,2a1eafc3,d793f04e,304831e,c1bae89,c7d00b24,7785d870,904bef5c,c3913fd3,6b8c08c2,553b9c48,5b90221e,ecdc8d02,6d558bc2,5fdd6ba3,df78ed63), +S(4d70ffbe,efc6254d,cee74a89,70244bd3,384b3af5,8510ca61,258fcbf8,f0c82575,ba3e19d,b022a75b,c75cbaea,84ff912b,867124d,a20e0d28,a007fb74,9a9d1c7b), +S(4ec8dca8,7079487c,adfd516,da2df139,5477b524,df4aa6d6,4db5e206,cfb14b9f,e88e5bad,c8654d60,fe1f8f6a,73328209,63bb10cd,31aae381,ea1b4d9c,eaf9cb27), +S(2e5a47c9,a47cbbb5,ef701784,34798a6c,48244df9,dab1e5d8,83c7f732,b89c7d79,7c8202dd,cf7fb6ec,471856d5,830d59e6,99c04377,c553d835,f64cb333,cc405c35), +S(3ca98150,bc5f116a,e7034a07,628d698f,67cbb8d3,74869ab1,534464f5,5e8ed24f,fad702ec,c6b738de,5b653f78,f51c98d2,1e4bad57,4db97730,58324d1e,12b89f9), +S(f414495d,9093ad22,11e001f8,b07b38bd,f5430a11,72899bcc,2c553b55,a9d4a225,bc130e93,998db229,6aef128a,5b8e5b9,9cf67eb7,713aa7d9,9fc4c902,af660f4b), +S(44e794c9,5a28ee80,f9b64193,9c626a78,4a851a4f,84f28887,9eaec508,1b60e508,b219e000,1b07a275,f63dfb3b,e977a40a,f505be1e,d3eb8194,b919b9ea,70d7198a), +S(35024622,bc17bec1,9c352280,297ddf7f,41654c08,61c3744b,13c74ed5,6673b98,64aece4c,3ec0b19c,cdbc06b7,4eb15704,50ff689e,6c5bafb7,6ae99396,12f4cca0), +S(45c4f36c,ac4c4738,ab216363,e12b2475,50b8ef09,7a986ce1,60b0462f,f8725a58,fb944806,dee59834,7d256885,3761e3a,9ae666b,9b0ee095,fea33ec7,bbce9a5f), +S(20a0b4f7,cfb58bd8,d09d008d,70e7f807,b4fa61ff,65f2e15,f00bddeb,55df1124,7961d1de,e98ca40f,4d09e5fc,52ae4916,cafdd6e,2bdb9f9d,2776239c,a3480157), +S(fa10af8c,eb2de4c9,3c0c2cb0,95f95911,755b19d9,90f17e33,69930d6,6002fcd3,68a3bfb4,533bb5f0,54fbf91c,b771b08e,e388a61a,9727bb7f,a3a1f67e,60b2eba7), +S(d417d7dd,30ebe9f1,7d52edd6,c82d91f9,25d5987f,a6f53b0e,d769a34f,e557c11e,ea89c810,619e32f2,369258ae,8ad8a456,cc36d87e,b45a42d3,83956039,62e437c8), +S(349b002d,ecee0e4,1788a3c0,cabb9ff7,c17848e2,78ef8905,63924c55,52d59853,47583a2e,7ff61b9,6fa168b5,1191b363,efa3e0a1,ce825a3c,5bb04e5b,e403b4b0), +S(4b8efc8d,7eeb3554,b5128e8d,ce20c29,697f79cb,29f61b4e,a2f7ac28,57910bb,e3cef657,7b5c0d2f,dee52c96,b6353073,b099fc9c,65688969,24cd744b,981c5ff0), +S(f356e8cf,5785b05e,e99a2b4,d202b64,2b1e98c2,57741e60,7241fb9f,d3f5ea2f,9079fd65,59d5a93f,91370d16,20ba487a,a8c6ad3d,e67f2c83,a1c335,fb5398a2), +S(bfb74aa2,15e5ce36,97cb6156,c2666ef7,1b579a50,56ff60ba,d3d5e10a,7eb2d31f,3def8a80,ddc3f407,e30d11fb,232f0e31,191bba1,d51a7eaf,b86d7f97,45d78dce), +S(658748bc,bbc75ba,30c969fa,371fc1d7,76c32a5c,7e632201,264deb36,b296ad2a,c173fe4b,14ead07c,2c6574c,8887500b,53f6c02e,dbd4048b,e69aab41,9267ef9f), +S(f51a5ca6,fd6dde25,447fa46f,c0984d26,66004c02,8d402624,8886d0d4,604f6ad8,fd4290e1,7b2928aa,f2e03b98,97252b82,d481c4c5,ad4edda6,16dc1cd8,e7ee83b2), +S(20ef1ab3,491b8d0d,a1565e2,8dd92dff,efcb88f,e88c6c2f,6b100b30,b58249b6,6814d32,7b5b94c4,12fb770e,2fc4077f,fa9a336c,9e7db2a4,bc7dffa4,286eee0), +S(d98785a,82dfe5de,343bea0,d91cd8c4,93344b86,7fffe1e6,efd7758a,90cdd899,37ad7f7c,5783d262,5204bac3,4405c207,972b7fb4,cccc3585,ee152f0a,48ee16c4), +S(3885e910,18a66e64,2d3f0c76,bca6dc82,a6916cb,1e46a858,7aff5ea2,d9390689,20765590,83ba89c2,14be9178,46e40553,ea195c49,de203c9,d175296e,a4430819), +S(c88cf650,3b50f5eb,7bac15fe,2097ff76,9c57b983,1b5d05c9,fbd8dc58,d4a9a746,b6443b76,64984192,e7056478,e9f372cb,21a0c7c0,cfcb0e84,850443a0,25ff42b6), +S(2831fddb,2ba2d301,2dedd58,a3f47434,3450985e,80e4e4c1,646745cb,1abc69d4,1b04e1b7,db84b6c4,882f264c,7bba91d5,2256dc97,fdd22e58,483dec32,5020a257), +S(5e492bf,c9d19dcb,c3bcb9a8,b7bec84c,23c359de,a78aa0a1,e0522232,df037abc,614fe5b7,cffedd76,35d371b5,2d89baf0,55af687d,e6c12d80,b221d0b3,9d03d0ac), +S(bbc64d06,681957e9,685c9864,c5f9bfda,34b4dd02,6b749399,a12bd8de,87f311e7,3c525096,dd6b7d11,26c6ca50,5064da63,f7f6a203,87594f4f,e4ae0403,fb4d66b6)}, +{S(2b00c7db,16887372,753ab219,17092563,8c603992,f501b07c,9d8442b3,721addf9,8e7c5f66,4c2493ec,77cc74ef,d9221235,f26bd3bd,6f505347,39f0fe19,c41b9fad), +S(56b9a3eb,d9360224,56267f52,21ade9f9,674863ee,4bacb7c9,ef0155ca,b36b336e,2e2b1a4d,f6c150c8,6dcfe9d2,e2836579,f82ac4d4,2f8fa4d5,d814b952,e92da69e), +S(ff52bbc2,83bd15ad,523e6d08,873a8896,9449a1c1,c5ec8570,ed532000,5a92aa3f,dd9c534b,e59d3845,a1943435,1a20513d,1a829424,e65ed8ad,60342b38,6578b21), +S(84f79356,86b0d45a,13a1ce22,7e7c925e,d80be5d9,9fd7e671,270c4c35,7254e9ce,3dfe2058,1e91dc25,12c16951,c667fbe6,83192bf3,3fc0bdec,c760dff6,730f82be), +S(db14da0a,86bcd275,71cb9348,36268bb,85c306c8,9ee1b78b,83c247a1,71c7355d,66ba535e,81e877b4,71b0b75a,6545970d,6e33cbe0,ff011e3f,d653d026,f4fff395), +S(28bfbd9e,44554371,74a4c24a,cc65ef45,5fd4d39e,b466e94,358cef4b,b0eeaa5,5f5ce6de,2925acc9,e503d731,d46601aa,7e668001,669819cf,fa54b3e3,dbb98f32), +S(a7b21a3f,a07b4049,7f9f5087,c763b431,d5f04cd0,e7b7e0dc,f9c6c664,df7c6ac7,daf23f35,b903ecc,af4f50cf,459deac9,85d0d008,3b5afa73,7131a61d,8591bf9c), +S(12fac42a,40935aa,e881894d,245b9691,cd1f6ad0,e0481e1f,444e0ccb,6ee88781,c519e0b9,e3c19fc8,f67c07e4,f357f43d,589ec23c,7714f7a0,4347be96,4ca572fb), +S(5c2e28bc,94f8639e,50133dba,9b6c8a35,348eefdd,2fdbc18f,2abb792,eb8807dd,c546b3c2,5f2809e2,7977a8e2,3a1c3ddf,36229f31,434c90e9,1386ee84,b64e8fdb), +S(e8e73382,fbe8b94e,58bef90b,aeb28305,d27ea48d,9c5f6e08,ef47e4dd,5fad6b78,d3529a2e,86240f06,a63ba03b,ce419c05,24d22622,fab9107d,aaf3016c,6ff7fe3e), +S(26b93629,ebe68d1e,878e94b7,5babd663,6fe1f6f8,659b155a,e10801eb,c2cc51d9,f4630056,b3e532f7,32238911,555c0ffd,f4951c9a,b2f45119,cb12e774,aaa2666), +S(57b3b642,431f793f,24862328,dbd271ae,19d343eb,c3b0ca7c,affe4fcb,319d8d94,1d7ed35a,5abc7e39,1858a9fd,b90f907e,6a065648,c00f94a7,75f06d2b,1cacef1a), +S(c2123794,d98f0311,c1327522,1767236a,6f45cb14,8fcd65d7,e0be8d13,2a650953,d5a382f,fcc2ffba,3917763d,ebe0549d,66f358b,d727c281,288551f8,4ecd193b), +S(16789662,9208b510,7528efd1,36036980,994d6c3e,5b34c69f,ef3b86a4,3db9e8f2,217105f8,39b0ae0e,932b5207,e8e7c190,3ea463c5,40c043aa,e4f8b106,97ccc84b), +S(3a0b8ffc,83a1702e,214adfc3,79bec5a9,753274ef,9e3271ca,8ec5b067,a5f3b6cf,2c3de01b,f27dd574,4496c2a4,75a07e60,71ee60ef,7eaf1c94,c257418b,744d1338), +S(482e5ac2,6477feb7,6c848d58,40173d87,1687c5b7,3b2970ec,b8dd69e9,3393f923,dfb3995a,ca5b54a3,b48e1dc,107023d4,dbd7a918,6adf2af0,61f92160,76e7f78c), +S(3869373d,638a30cb,63177204,bfb194f0,2c6ff503,3cb4ef1f,b146741a,f3b495ed,f2ac9f3a,ca159c7,c2ce9f48,e895c543,d3f19066,74f335b9,efdc4b47,4ccc23c3), +S(98c16ec8,54b34ea2,f08aa4fd,3ce1877d,4a36f2a6,4e673191,17f74e5d,86e57f4e,3448646f,c9aaab0f,4d9ec298,8f7c07ef,448888e7,6318e0ac,552d7cc,b59820a3), +S(1b3a4ebc,8ad1c200,2847e182,de7c9eba,f0f7a408,e2aa172d,484bb9d3,ab897b73,b93decef,69cf2da4,5c3a2373,ac2abd77,311fc1f9,21e7df8,5f5b3504,a5ecbdb4), +S(57b5b2d2,87bec103,e4aa7b96,16129718,64780441,fb5ef847,19f16d,a1c909fe,2c2d983c,d8c37a7d,653716e6,15756121,9e5ad5ab,25664967,67a0fbd8,e8180c71), +S(cd7e277b,ae1498a1,8e7bc00c,2c299a49,69f6862b,bf96e6d9,f5143db8,7b582115,f9548ddf,c27108bb,38f80025,6ffdc25e,28621c17,c370a40e,5009b968,d40660bc), +S(80615f12,82ed7a8f,bdd70890,77c24f18,c03fa395,3cbea7bd,32a26c7c,260102e7,8d54a3a2,83383d6c,a633d17d,e2f4f756,3bcc3826,10cc1012,3f2f192b,e654c2cb), +S(9b16f5dd,c8c8c8d,53786b49,e2d025a6,838f6322,700fe105,7de6d78e,92281a9e,a36aa3c2,f2034285,76f5c570,63bb1bd7,e6e0410,2e1cd2dd,f100a54e,431ca08f), +S(f1d04075,35ffd40f,2a0b59f9,14ecf368,67700c5,b33e15c5,153fa9b1,c724d36c,22a8b48e,4db1d3d1,28cc735f,99db74d9,ba7457cc,67b8fb39,7b38fcbb,153f7c0a), +S(c252ae6b,cb711704,659c37a6,f425b215,1ac8a4c6,63420eba,899127e8,1d8df3b7,8e9a75ab,c5a2cd8c,d44331ea,b07e5d97,a30c6619,8e382261,642c0e10,8f6d46b3), +S(59f6fadb,4a8a3389,999b93d4,cdf1f8f0,4735b397,9fa5397e,64df420b,85876ed5,c6e8baae,fdeb7019,23a68b71,9c88596f,60742a38,db04d7e5,76bd1b8d,1ae25a6b), +S(3c2e03c9,3f610ffd,db519c7,80c201ce,b58ffed8,ee10e21f,d1610e99,33fd08c7,d339d979,38e1c5de,7492e7d2,d64fe34d,ed889d48,54670345,1c51432f,ec6c829), +S(59d16070,f9147ce5,5bac599e,aaace0ef,24c02ea6,85249cc6,ccfbeecb,a4bba83f,8105bf41,47f22f7b,18c73941,3a85ef06,40bf6805,51c68a10,e16be920,193977f), +S(3aca1182,3f31b2b,a12e628b,604b1010,62c45fce,22e15dd0,8b9b5ef9,a53dc02,bdca8ab2,d2dcab6e,3a98e7f8,7e192c19,cae60952,fc6f54bb,d46acb49,60d51c0a), +S(261e9b93,3648c7d8,d5255219,fd0634eb,112e1dd3,fcce0a04,135b2784,e67b5e32,e0eb5ec4,30b1f9ef,2bd10916,b21f4fe1,4cfc80c,2bc179e0,fceb8678,ff933068), +S(75918e0c,a0f03b34,40cd6239,c108bb72,f293a881,a1f2e9a,73c604b8,8c451c39,7f02a925,80e50aa2,5b786d10,422f1d80,2c42cb37,27f09ff1,1f5e0dd9,bf6a6c1c), +S(42025e76,3c05c64c,311a275d,d6f1fb3f,2da2108a,39ed24be,3de83123,b1c1d1e9,dba2eec,7405b67c,a1915b13,45702062,7bfeebb8,6b90dbd4,87849e5a,756921c6)}, +{S(ef2c5fa8,b23285e3,50d7f05d,bd8c96bc,6e662824,5b10e1cb,4b659bc5,ded5836f,1b37679e,1177b653,8975790a,4d5d2abe,3626d636,1d863e0e,1db5881e,8e0c54a8), +S(a1d5a92c,93df3c75,a06f9c2b,a604789d,fd487513,62d957be,68c033e8,3e202fe7,64c1c00a,3964fd5,b35fb52,b423b285,77c8d269,bb1ffd16,325c5e0f,7be08bdb), +S(ef13ec66,d46813ce,68038fcd,541e5e8,bdbc970d,337d7b5b,9099d0e4,3fa4288,7ad7f4f0,c8345229,96341248,b1455ebb,89aaeb6e,1ae9db25,1b86930c,9f779fd9), +S(aea7cf4c,afc1079e,e61396b6,2df9bac3,bf334745,54af4678,8621e337,4c7486c7,c0538605,5911c893,459b98b0,4be449f1,2e9f98cf,ef4cc292,9975d42a,f460d65), +S(8f082b3b,6476b4e9,ffb317e6,1fec0cbe,8e389041,1fdc87d6,8d4d26f5,44c2e6e3,eafc2bd0,d6570a7a,cf8bb5d1,6cdc1020,456473c9,e7fec1b7,fc6217cd,89d0a2a1), +S(e0d6310d,e5e664b3,164faddf,39c93676,4ddd3f0d,f5e007ef,5293ee35,980ae0cc,de093cfa,ca1033bf,47e88723,4978758a,74b93cc5,2cef48a3,daf4dda9,adc79ccb), +S(2518110d,5d064422,5ead0e2a,5ac8b8b6,b1fb7fe1,55e351c5,1d39673d,dab0f6a7,84f0d6e4,b497043a,95b6af9d,e72cd502,a7b6a68d,ecaa01b0,9e0d8ff9,9d8a16c9), +S(6dc820b8,5db42353,c97453eb,d7d768cc,af9732d7,c261bc6f,8399caab,34cf5b93,30b168c0,2509e2f7,2035f60,8be8b89d,577b6e72,540f521,934fe980,7412459c), +S(bddc1c75,5bca9911,3c9ae05d,c14441e,2c707fe5,8a329f8d,feaa36ff,12771376,8c0087ca,b7688dcf,5a3cf1a3,3c27c7b9,cee36007,3aeb9b9f,d7088623,af20f62a), +S(b6e5ef5a,a84c6d2c,a209d50e,8fa8e1d7,93c61ed9,bfbc22d5,2ecf293c,9489ac13,f76e3adc,a89cb983,b6b670f9,5a7e774a,c015de5,1ba56417,40ac1f68,e3df377a), +S(26699dfe,93788eb8,d91cb601,cae628e5,75e8468e,73f6cefe,588755b8,c396071c,e5f9e00f,bcf6f1cc,cd39ab13,41cd06b0,ef2e789a,17d761f6,6a7a83b1,c036791f), +S(72f7e16f,77f0a4f7,92db2cd0,354382af,34282dc0,7b8cb290,a89a5d60,894e8090,54d32ecc,2cd022c6,c03f1379,657450b0,db9f08e9,6e2be5ab,5233dd84,62733cf), +S(f84ae7f8,a03b60,af9cc359,36f0ae78,96935b2c,534f852,8ad159bb,6c0b44e7,68f4dfc6,20a354d9,d6904bae,67792622,337e3180,ab340828,80bf04fd,f47cc23b), +S(5dea33b1,b5207880,6debbcbf,83ca17db,4617488c,ac5c6997,ad4092a9,8a7bff7b,99976fe5,ae88d36f,597e16bc,6dbbd4b4,547f4dcd,7e85786c,d10af30e,1d81bbaa), +S(31be76bd,4f946e41,7de73037,ceaa0dd7,c6eccedd,74399e14,2ae6fb2f,d2b59def,54ad8748,ef129085,dc3172aa,904cc376,e203fa69,38783007,2af33889,52cc6549), +S(b6124d0b,f603a00a,22662897,8dd0da87,fcaca5c4,fb04ebaa,81a7d13b,39f2e307,34ce08c7,4463b06c,afb03c02,583288eb,7bfa6f2d,3eedfed4,91e31781,dcff10da), +S(6aa4bd60,90bee70a,295c451c,c8af72bd,3665680f,545f46c0,bf5af990,aab5f43d,9c2364d8,25406b76,94ec9db,a0ea2f9d,bb36fb2c,5bda62a2,bec941c6,874e68f7), +S(f72faef0,e268a7b0,a9b00a57,3674d846,c1f0dcab,c9a586da,be318b1c,d2874d8b,16b3cfda,65db0f3d,1b5274cd,20b1329e,61ea0404,1f702e71,cd98708e,28c9307), +S(1d26df15,98a6b9a8,7c0a6171,767539e1,86d51d1d,cef130da,1001226f,87872c5c,f227cd82,becf5493,e2fc7635,6a37bb48,3e681a19,ab4d9dc7,92edb114,3a7b072e), +S(28690cb1,db697840,13e9eec4,d9388723,4c28451c,cf9dd51a,746d1274,b13b4610,d9bc39ef,19228981,a1ead1f8,94deb254,162fed5d,cbb58a76,6d361e04,3d8f62ea), +S(4b0b5258,22dbc31b,1966b004,4d39c5de,f2e16d5,1e032045,f9973b1e,fe83ca12,2db33ff4,3631fdc3,b3608b40,7dd494f6,ae730212,9619ae35,f6ea08eb,51aded12), +S(a5b4e205,462ae419,8fffc1e7,393d366f,c5a9eea4,e98c67ee,1684d63f,cc09aa64,d3c11017,de4ad175,17cd076f,cf6c0ce0,4d3108ed,60eabdf0,935499f9,4226078b), +S(d4f1af69,2a3e9fee,1f92d892,b14eef6a,a0ec1f4e,d4d455f3,c70bcb19,52fa9837,115c7ee,73b90adf,f142191b,eca0be01,7d5a651f,93e2e90,5a36646e,21790294), +S(c275c715,9d6e9757,d2adef47,2ee53856,dcf067db,bd4f380e,892c9832,708d46b6,70da1cdd,853a0dc8,a6a9043,3c319371,25590c9,5807284c,ae168f65,62e4465e), +S(d2e38b27,83156e2a,a8e4eac0,149bf2bd,bc2918af,1cfa2a23,d1c7136e,15dca7b,738a0fe,751ecfa3,459e005f,72b7461f,a4b5e1ed,736420d2,22c5428e,2ec1348c), +S(f8f5b3d,b3659174,e0149fc6,fa747c22,9607c434,8f9eab60,900bfe92,b23f9a4d,d89eff33,94a093fb,549fece1,4e11028a,614cdce2,21d5ac26,1bc25f32,dca3919c), +S(5e266403,e49fb860,e6ed0928,45bc0811,a214a0e,dcd6174b,290ecca9,3539d862,ab300acf,5d8b8a14,2fc9e93f,ea995d2c,d7a2bdb8,6a146f65,2b8bd49c,4e8dac1e), +S(8ff869a7,60230ba6,aa5adf91,1ba61702,17cbfdc,5ffa4018,b8a09b9e,4088d634,db5a5aae,bdb946db,80e4c207,22ce38c9,4921bf41,47eea510,70e5aac5,c592f32a), +S(f3d39b65,95bd5ab9,aeedc43f,f49bdb20,73a211f4,5ec2c1b7,72226a97,a31c059b,ea41abf,9322d733,bb5971ac,194da6a0,a6d41c6c,a0d0acaf,a53e17bd,dabcdc68), +S(18ba0c34,10169914,7f7589f9,179b94f9,daed92c9,287f3468,d283bdcc,d71cc637,b3512d25,c199d2a4,c115bd11,eb53d6a1,6ef16c3d,97be6291,5ff76b76,890ba675), +S(2cf3354d,7f96c58f,3c6336a0,6fa30c83,8da767af,48f89fc5,5ced5b40,e0b86cee,403ac306,f6c3ad55,be1ee5bc,29cdd46e,2e3d83ac,71050909,5f117d21,7ef015cf), +S(5d31d140,f754d6c,5943deb7,6aa4d867,cac81abd,9911618b,82327795,da8d085f,c25f99d0,5bc5d583,a3a72b41,4e5eac5f,c14ced01,98247ded,79ba482c,882bec49)}, +{S(e0ad91c6,6ca95326,d440f44,df91c1cd,cd3ec344,e906d0ef,51fd4a48,31ce293d,3559f3c,e349f7b3,cf66c17b,2c3fe7e6,8ba3804e,438c0816,b68ecfe2,30da18d), +S(d8b8c50c,ae9f285f,d6d9282c,373607d8,5fda59dc,71ba0066,148b378b,37440076,ef83dd08,ddc115cc,24948009,f9eb4afe,dcd65c64,b6ef2194,bb4ddd0f,ece180e6), +S(881fbbf1,a9627bd,369abbc2,edda8026,a61b10f,4161e442,530a5e36,447a0290,6a63cb8f,7e5094d3,c8beba5a,2a1d7b54,bc33005c,efdd5a1b,20d51f74,d00c2fcc), +S(36bc0df,fd1441be,1a4efd6a,43ab9d83,243d44bc,a6e108dc,fa9a79fe,23106f0e,eeec58b7,dc37eedb,4432dcff,4de7f88b,2a0e0721,da2c0a72,eadac4dd,71037ed9), +S(7652c155,c2ca8a23,7680f015,a62c7c31,31682752,a99c4329,77ccfb9d,cb58b03e,5339a51b,36ad4548,197d3018,4bde4428,b38ed983,d624bd18,a5f8c4e7,21871c5), +S(904774bb,fb8b0c12,e2b3f91a,e5a2a0c1,a92971f9,86e29fda,9edc609e,e85b222d,ee843f1,8039e251,9c7b23b3,725ca099,114b4f0,26970111,35ded92b,94445e2d), +S(1c7c82cb,fc14b9b2,cdb3c12c,32df33b6,a08e7af9,df62f310,79fa0710,f28f5f95,93471b5a,17c84eea,a45948df,80c0f56,d8684f35,1aa2ce73,ab84b3c,1b8250a4), +S(bb060f5d,971ce062,427a5630,8f114532,40ec54fe,6479d5e7,9ce81444,27f07927,fcbeaa69,8278eff3,d4dad904,836c0233,40a866b2,d8f66774,70d1159e,201501dd), +S(287c5b8b,8dc168ea,197200ea,97c4f63d,db0b620c,7f30d623,7934f856,a558438a,260630e6,ce310eb0,366acd78,bb58f4ef,27fe4836,edc8ff0f,5d5ff987,b659bd20), +S(7d4c5c6a,d6bb0f0a,47b45a35,1264f4e6,a81ee0b1,65a7e665,4b226c95,feb59dbb,e89691f3,ceb4c57f,5aabc1b7,4acd9aef,41531851,b41ec1c6,608f16a2,2243ab5e), +S(ff806f2b,9eaea4f6,47d85077,d8aa5052,9d4b96d2,1e0b31e0,7c86b14b,73b7ab4f,9acc2f77,a994997c,7a2b1aa0,79c0b5d4,c84f6715,e4a50e33,628b871f,8885ef9), +S(3ce795ba,1d91a9bc,b0260654,3450ced8,c94184dc,8a41ac5,e1b84856,e88927de,3492e696,cd1ab2dd,587d4707,ae34fd67,3ab7902c,d0c0972f,ef419795,1ad88a1), +S(299ad528,afa3f36b,c80a757f,f081a8d,c4a6ddc2,d2b57fbe,1e3d361b,ee5a5d8c,9fe823da,74eda2f,266a8e9b,331d511b,623dcd32,ef9ab7a7,f1d1cb4a,7e6fa65c), +S(bc4b0549,16177aa9,b52b29b7,cb91673c,9ffce4c,7d0066b,e4249383,4467e3b9,55bc6436,7db68687,e699cf6c,f2db9dd8,c2b9a438,8202fef5,11acdce0,ab06f969), +S(a8e8b02c,ff5de02a,9e6652cc,6fbe41cf,6e87ae0a,abb14ac5,a178b3d9,e697d0a1,285d4af9,405b03da,c57c993e,4b40df8e,82c6979,f8de57ce,4543b7ef,3381e0a8), +S(e50aa8b6,d7dcc055,4f406959,b056dac5,21a7842c,ffaf09a1,ac24a3d9,8aef5228,46656395,ee84d061,a9f5788a,cb05975d,5ffa5fae,edced564,1f81b5cd,2491c23f), +S(cb516899,98a214c3,c31e4f2,692cd7d,fad6f1aa,eb8afd1f,b29c23ce,429c224b,2292b8a4,5a89eb80,167238ef,2027f975,98a2aea9,a4460c5,3646d088,82403c63), +S(ded04c06,74c6eb16,557b5bed,4b6703d2,cc37c6aa,bae466e,ee7b5665,6693c3c9,4f3ea70f,8e75b02b,76934cc3,645d2af,eee8e9d0,8d100117,40e8a16c,dbf01113), +S(f3d0a253,edb74436,897e6cc6,621c74e1,30457b41,e0690b6f,7b2a203e,8ccf8ac8,89cb07e0,97495bdf,403490fa,d58eefe3,e79ffa33,1deb5cfd,2c548ec0,8771be67), +S(d4c66267,aa73ed55,bd68fee3,2a166a4a,4ec660fc,4cb13e18,fb6ab0fb,19a5f330,7c2efa2e,4509b221,97589483,40b6c74a,8ee2b5b6,5fbb794,7f838c47,ee03e2f0), +S(dbe9a638,f262f9a4,b3a8955f,22293feb,50ef4b32,20df0184,f363a373,d7110981,4c563e83,5e957756,5e6a8996,97510c67,9561972e,5601c1cb,2f2f789a,c1e0b5dc), +S(70c7c07,1b1e6fd0,a97b55cc,5ee34297,166f4d02,417a796b,eadcfd43,96e1b338,b5c49a6e,2ab03e55,f4755f37,e5211d4b,e23339d9,d464fd28,6781bb29,56db23bb), +S(aabc501c,6d52961a,a613fe66,96f0bacf,fad74fcf,8386cbf8,b18704bc,a1392ffc,cb72ba86,70bc3f7c,85a00d3d,76b11596,6cfa0fc7,44b31e01,9129da9,a5b04b77), +S(47f72ec6,e7820788,f0f7814f,aefd6e2e,af5d90f3,f877677d,ae72569f,c6a2e8f5,6709731b,ef2dd880,4696a6,a0661d13,fe579704,5e0a9bc0,e5fdc0af,789c5830), +S(20026251,d9413e42,47a5884c,209b144c,98e36f7d,8ef3f56a,68efd475,8a2ba8c6,9c5531b6,94ba0d24,a943d0c0,94bb623,75c798ca,c9716181,a9d659af,2acb978c), +S(8dfc6293,9b300c9b,fb617d23,6885f652,424b4f12,d04f8bf1,151d1ccf,935e445d,9a519433,18e94107,5472aa9d,6ec3390,59bd3bcb,bbbf6937,c9290e48,9da25f8f), +S(afef3520,3b558495,6fde0eff,5cfab48d,8fb08806,3c1d7cbe,d7e352c2,9d49176f,a764e6e7,bd0c8fa0,8a6f7f30,aa38d7de,fe71773,fb7ea484,68b3fd9a,8adfd650), +S(354c0a98,1628dfff,8be8ede,ed809db4,bc39c686,39a26828,ff28dd47,90801c46,8ff40299,d607036b,acd7c0ab,66a102ab,38f8d3a8,a271e2a5,20b43cbb,687cce6e), +S(da2e7eb1,1a1e66d9,5001ec4,f7b66c1e,d9f7d0ba,ea3bb326,672b9069,5cf4bebb,cbc1c7da,b9bf7e0c,dea9297c,5f45ad0b,9c4fb093,318b55f6,1e4a8951,726c4021), +S(6f780994,d0662667,8fa74156,4232bafb,b3a7f54b,a5b66507,9df32545,c192c3fb,5f11f0f3,af1a763e,4c9fdcb1,1e5e57a5,6dfe0d0f,f27535b4,4343a312,90e375e3), +S(8e28948c,729c1438,5bd09316,31ed3eb8,7acfc5f4,2a9eb4f8,afa50362,33591cec,2c09fa1c,bf0044eb,a78ae81f,bdbd4271,2a89ffdc,f7d476af,881d29fa,2627f250), +S(4011cf05,a85ef14f,1875cf3f,4f78a51,a52a89bd,a8930a4b,8e13802d,b85f319f,35045b10,45c4f71c,c762fa78,5b8d4daf,aa2a1c07,ba37d82d,ee0592ae,ad807810)}, +{S(9089bcb7,1c1559bf,e17f6c0b,91fc098,c63785fa,18a42d69,798b063f,a0930175,de229175,5e4ebf17,1047f79a,37d81b9f,9004a7cf,df747589,7365e174,ae367969), +S(4d1caeb3,41f25459,3b06f715,9e9418df,e29ee076,76c26e6a,51d8add2,5c70a5d1,bf773e4e,4151d17,650aa7ed,1d6a5505,2d622264,75c50f31,3261aea2,cf68dc37), +S(b0760330,517c0d66,2571cfae,830da7fc,a7e0d86c,1ca7f75f,5150f625,db50ad2,d8d10ef3,9c84bc13,32c2cc01,4d4df0fb,45e5c546,1849fb1b,81b7fdd0,7484d35b), +S(3db731e4,273e5b20,2de984c9,74b4112a,d0feebe3,7bb0d2c7,db38c7a,21bd8c67,b425c1d3,46b6a61f,2893a3aa,a243d60f,5563cba2,40018cca,3f80925f,71f4f1b1), +S(3a74a50d,cfbf2ff2,5c99c4d0,1a13a917,9f510c2d,eca1d5a,7ac8fb69,7eb9795,d2b468b6,714681f4,57bc586f,40585c01,e1dc14a1,a42ce887,9ce044d9,ddb92d82), +S(b9139ad5,4f44fed8,4917ad55,c49c23c7,8d62aadc,e83c9ebd,9efde865,cb1eb298,18068856,c80abc3e,2a02a441,bb7a7609,aae8d2d7,8abf4e2e,378fa3d0,a27aa900), +S(bcf5cf94,5887f183,e58a0128,4ed9a4f9,fcf918dd,4e68d70c,5a54eeff,166cb44c,8235f63b,bb06a1a6,d9ad2ae8,47aeb2b3,4838c962,9608943a,93294c6d,e21a3ab9), +S(4b598ce3,a77680c4,c5f66e90,1b76627a,27f35301,9641520f,53ea3894,dcbbd187,357af043,f77cb9fe,c04ce1f4,dd9b610f,9151d15b,3b7383e4,ecc5384a,8ff7c5cd), +S(b588aec9,2dc045e9,a95abd7e,da929837,28b5a7dc,d637001,ce7f0917,1283b16e,e682f53b,5e94fd84,914b7bed,5afc28a,30ae6f53,e12d948b,3c0d88fd,adc5d0e6), +S(eb8f5080,218ac5c3,cd792042,b59e80ed,e3857a75,a81d67e0,7c2221de,d3b7a677,c5f272cc,3734e0f4,6a5cf40,626973ae,1aa76839,1953f211,eb52693d,254a7fc3), +S(87d119ee,65a60dd9,aac3e784,22049cfb,4123d262,ac9e7473,4c9cc418,fb7ce4f0,5185ac,7bcaee8f,f1334eeb,cdf2c39e,83b8b665,1ef9d8f5,48a029c6,464242da), +S(cfd0438b,186d0f5,c64b5f38,a190baf7,35b67512,a439f486,fb79d501,90294683,985edeea,243e9c6b,d9c57cb5,c7372202,7492156f,e10bd2e5,67edd14,8603daf6), +S(52abfb67,5fa8b12f,1b0cf0d,8e85f402,47696662,64c7252f,69562590,839ce4f8,8483ab43,2d4851d7,4f432c0a,8e3863b9,783cff58,3d4c390a,9865d7f7,d1aed67f), +S(52926df,cf12eebd,3008459d,3c91dc1,73532f1d,ce3c5d0a,874ea4c,67331787,18b48fe6,1bd74ac6,4b34f2d0,e7de8920,2c875625,bcad84bd,cb3c81dc,cc86dd87), +S(695e09db,8c4479cf,4c305005,b5737ca0,63f89d02,aced2010,5600d65f,58e26401,32123a91,8bb23fd8,79715cc7,488184b7,53e0eb00,6dbbc95a,ef9d815f,3833c08d), +S(97c35716,e5aac70c,45dcdd45,a5ca0155,5962f09a,cc22c16b,c79ec272,ce1c12d3,93869464,6470f8e3,c0db18b6,2e4378e6,5caa7203,c4d6a079,a10c8182,b8eaec3a), +S(35197cbb,8da9188a,fc60faf9,71cf08fe,73372e15,f857b93b,6afd77e6,bf4db7ed,4e0efeb5,2db601dc,a539c777,14f4aede,c5ff121d,77dbdeee,7f16fb0c,4cfc19af), +S(afb2f842,e0818237,46c294b5,9ae8afd2,279e834f,100f7d1a,58a9551a,98408d49,5bf5ac43,a3609b4f,f058d6dc,6ae09fec,19a19864,6d12096b,6bd3e06d,95d15ae8), +S(a37f6113,882459,b1dc6335,26253a8d,287c9285,b7862721,f9bee74a,d2c4c09b,9ed3252a,303c6d,e01c9985,c5b3edce,1074d478,db56b961,d91fdaf5,1b1d07cd), +S(4579a503,cb12a345,182b28f6,9a3fb98c,4a80579b,2ff592d1,ab18b32d,212098d8,8c61c503,df79dcf2,6083f15b,c3ee1f99,9e1e0960,8bf6af9f,b3175699,cb0e32d5), +S(545031f7,883a2c8e,bd56a405,52719096,4aa1d40d,b22a1730,242f3064,530ce326,ecb024e8,e762b456,f62072e,5f471180,bbc817e7,54e830ee,7757ed7b,df5c9300), +S(b127fb32,bb3e193c,99d232e,d50dcad0,3b45c072,e0d521dd,de72daa9,e20e97b4,b198428,12a344b9,2e8a8d26,f7964c66,82cb3652,66ab3e28,2ba56197,a159f1a), +S(28cb3582,d312cfcc,75ab637d,66c646f7,5157dc74,7a464e9b,933c0820,2326b6cd,a87042b8,510ef9b3,ecf918d,916128e5,f946f43,6f18fd5f,ad62c46c,5882a23c), +S(8a46cb3,27efbeb6,850e3861,c4845ab1,21414f12,9fb27387,4560826d,c9116886,ab09eda1,9156f1f7,194b7b17,fce15268,ba041c96,24765ce,fd964ede,d1f05b3e), +S(b73af914,10ad56f7,d490dc37,1a2584bb,633de0f5,3f00cd0f,6b8a6019,53c7ef16,3dbf4e52,753e5d33,3fd23cc1,c839f278,38eacb3d,5980cba5,bf1be426,b27641a4), +S(b38c1896,427a0108,3f24a917,2a64314c,d9dc1759,60971135,1b2db6f7,d66ab745,d3a71a74,f80d6919,b65629f9,80c7757a,cc49c2aa,78dc67d3,d6f76d06,91a90d85), +S(6f6ac108,aa819a27,6dae862e,276b3ebd,8ed5a3ad,9b0296e0,4f8b65a7,f6a5ac1a,e3048583,7aa2140e,de381363,788d6d95,25a7ee33,9e3a3ed1,2518619b,30d9f71), +S(72f5809c,7c282ab3,4dd81851,33070d85,53999a6f,14fd753f,2ed8deec,ed7adc5b,112bf5fc,8cfd71e1,84ca1967,d05da765,801f12b4,c852768f,60afa7f1,e9576534), +S(4c348833,10f7dd6f,b385ce25,e2d6eef9,8556a59b,5e79a084,ec83425b,37150085,9c4c97e1,be47b073,5209ecf1,5f05cd8a,58ece74c,1b35a92d,96a70287,11d95add), +S(f16e4513,2291b577,ab360b42,c1babcdd,ac61e623,da85819,e9c4c73d,463204b2,5556d7d3,a2d45955,28781d15,16b59aad,941ea356,1b5c88ec,94d09314,555ea64b), +S(3580d3f7,896fb70e,d48d40f8,1b31a72b,51405574,2c2b4369,e3e7dcb,aca4ca22,e38da3af,f409e459,82098305,87aa0eb3,b2339959,75af2e33,501115ce,648360ee), +S(407e63d2,c0cd6d37,84c27734,f7e517fb,4bdf7847,296dc7c,a8714fab,16199828,2dbe5da7,4b29bcc,fe9adabb,69157830,987ac51f,389c6001,2577f298,710025f1)}, +{S(f61f644b,e2414c07,66c6caf7,5d32683d,efafb133,cb4d341d,7eb565e8,9f6e4158,2848548f,15a6e7d7,2b2eefc1,1b76e318,15ebb4b2,1e3bb938,e0c2e9d4,5ad4f7d1), +S(5b891f8,ec902550,429a5ea8,33fc29c0,bfdddf6b,a6f62c19,87a14050,a9689a9e,77901539,1ef92985,6d3e3b35,4e4ba2db,3bad967c,dedf331a,c0bff08e,f25f2e99), +S(d8c5a617,62655ffa,c943ba35,3b595b46,88012282,af068e52,d918bbec,46bec87b,9263db7f,6f74aa,df0dbdaa,7e7ccd76,cb3a498b,44de2bd5,f9b23029,2ebc6009), +S(2e940645,cd189edd,2d6cc2f4,f9b49e4d,305cfe8a,46f2a596,fbb55200,89fa5b21,4c197028,e89ef8a5,b5b6cbb1,3733e90,408877fe,7425d58,1f4bd063,ce286fb2), +S(396c3bd0,9c6af07d,123a8441,c9f79ab3,a783b8f3,fdd69dba,ba61c7ec,f65e3098,534c8657,aeb8cdc6,ea34b372,68f1c70,70881797,46b8e335,477c8525,cd17aa8b), +S(c34e6be,b079f3e8,d1355754,4246d2b7,a82d551b,740b4b59,b9220bbb,768796fe,57806a7,653ff7f9,3d7cb6,b3acf68e,aed049c5,75988fa5,f03b1ab5,1103aee8), +S(5d2d6aa5,252fc9b2,55d89773,e920891e,7021f233,5f7d0ff8,c6bde5eb,b65c5149,1fdbd2c5,6b8bd095,2d16c304,8d9c588b,2a26379a,17f748d,f1345695,1c5959c4), +S(dec812f5,745fcbed,fc36c635,76874bc5,96dde8ef,68a8724c,201b6af3,be7c6e2c,1715328a,f75f9661,d58c667e,5c5514d4,c99dc881,8cfbe702,e0c3c8cf,662e6906), +S(32499061,83487250,f65684b9,9434489d,3a83ec93,61531dfe,48086b2e,b2bd18fa,53217af1,2a25fc6f,45555d93,98fe46df,4247d38d,7b162e34,945f8928,bfe148ba), +S(d3e9381d,83f31116,ee161490,c9b5d1e5,7e086c6c,1552160,2c913315,af033ddb,97ae863d,34271b26,a4d6577a,32028c12,f092a54,8a18ae1b,a83de384,3b8fb84), +S(1347dca6,d097faa5,7c6583c1,c292c6b3,ed87b31e,9f7afe8f,d0d151ee,50ebdeb0,570a9be,e4806ee3,e30696c9,71b5d905,65d09ee6,8b4fa8e2,8acc54c0,7fabb492), +S(691f9995,af2bccbb,f556f624,b28a1da0,67854c15,4f014e92,a56a8cdd,9a43c25c,81babe0,ae44984c,bfdfa958,4dc5c677,881d1b0f,63993075,a3fc462d,e986866a), +S(cbfb62c0,b91cedb2,5a9d4b3,ada43cac,7839dd18,33174711,24688ca5,5f162cb,77c12161,e8199410,b040dd57,4d90c39c,2f1b499e,6b7c17c7,fb21438d,aff4e674), +S(53627516,8de62d6,2c792f64,f87996af,3ed8ec88,653532a8,ad60942e,cae4a339,d8449a0c,c6ae32ff,45a11652,97b3d3a0,57e67c64,42d3bdb1,ea2cbcbc,cf0655cc), +S(2a81679,7505601c,763e6f6d,f7ed5c0c,14ee7fbf,d75b8755,7bd57f3b,e2d3c7c,99f26af2,10540397,b8423da2,c4809942,63734d99,f0d642d8,96b5f216,767b759e), +S(54b2c03b,7a7ea964,220a255b,bb688767,99ff1b83,f0934e4f,fd03c2bb,51490e22,b3cc56a9,ada9240a,a3730828,26733090,ace3d7e3,91200d08,2cbfacfc,bcdb9c08), +S(aac88c8b,7c96e629,6f023e5e,8f260324,9f4e645,bd701705,8f38e9f3,a034eb57,da03ca87,b98ab6fb,e4869756,7c2db04b,7f9948d9,ea65d93f,d222d52f,6d48c552), +S(bfdd8394,fb49f93d,630f6b8d,469da38b,105529d6,70b9a1ab,e3196816,d2a13978,39a97919,4249f78e,c336b00e,475722e8,7ca734d4,3d1e97d4,d2aed074,abfacbe9), +S(5b5dc3d8,66ba3647,d2aab2ac,3338abbe,4d1ca08c,6cc6fb26,4335f40d,4dfb2331,3432ba31,46a0f15a,beca45e4,1ded8729,e6a0a49e,70315745,bfcb3388,4e1b9e64), +S(77057b28,a1d139a1,91e05ad,a8d72f3a,c7bbd198,7647070a,b6a21355,7bc0c73c,46f32321,4519afd2,f830cf7d,567aacc9,47ae6b8f,8c841adf,988e39ad,e7b9757b), +S(f6c8d1da,e28a3d23,2a388998,1b638b23,f381618f,18dc81ed,67bfa696,4cf1626f,2ad8c92d,43378941,8d298dc8,2cbf288e,86d21467,ac73c0e3,383ef3e0,a03142fe), +S(b75b5cc3,77a55310,a3b97067,34f1929,fc0db732,3265e6da,71405f37,729d5a90,be077940,4dc94ea3,3cff505c,e1aef457,bc120359,521eae05,1de1963d,b1948053), +S(a66d54d4,4b4d06ea,f141307e,27e1c225,41274eef,5c0b1743,e6398b22,fbaa4068,51b6b8a8,48e1c27f,107f82cd,d0554643,b9e16611,fa93b789,a2199b3e,3bdce296), +S(56b2f9,ab707dde,3479d0ea,baeb62a7,6cb9bf03,66278eed,559e027e,a1ea6b3b,27c47e9d,646684dd,2a54db0f,c245fcdd,721416f7,22ed568b,736e3e1b,f195b379), +S(a9642a49,83b19c3f,f5f63ea2,9e76afbf,a25dd0c0,221b32b7,6e67373,36e7f5db,8ada3766,9207c6b6,a6b6006b,5240a4d6,cdb8b807,c0c16a56,91289b7d,62b3a051), +S(18eef8d,f9e64c1c,548db40,ad77d92b,11f3ac43,60c63792,5f7d598d,7d8d5155,806db24,eefc3a64,c84da338,937811e1,20c6d0e6,9cde79ee,9957a4fe,c53d0e12), +S(d11359fe,61861a36,8b3809cc,c727320e,42905036,774d0e83,98453ac0,68d404b9,d4182ce7,2500637c,8e14b977,34fcad09,f061838d,57c555fa,420fbd50,9abf6b81), +S(7ea072e8,f60ff273,9cad1d8f,c560f208,b54149a2,5748900b,50830e3f,738b2db2,af09e54b,d44feb44,b39956af,fdca6894,91c7c6fd,f029e71b,e780f8f6,77a48a0f), +S(f53406ab,9fb1317,b038dc28,f0165abc,115b37fa,77d23bc9,e51993a7,8a15ba00,a293f429,f7fb281,9cb60b53,b2e6f07,afb6537a,39a18be,a5ad4cfa,55aea13a), +S(b1674e5f,9f194a16,94fd12d1,4b64ac8,bba69432,241aefd9,9a1c49d,23287635,83cb0060,1f5c33be,9436d946,22c3b3ce,68d20733,ee1e2392,8c3d6c14,a7cde085), +S(22b72e08,66b96344,ee53b898,ea725f7a,b5832c12,7c0881ce,862ea945,5eafee85,b886e8e9,cba29b15,be5c22d6,ada99b02,1098be69,d1c500fe,6fd2ef8b,f1ecae25), +S(9b7151af,698a85d,2246c88e,8ffb14a0,c762c2e2,64ab70b7,e30e2434,9b8370b7,fd9c3d89,46ee9d90,e7fb6d26,796062eb,4cb6c160,76df4834,eecd2748,c8a7bb4b)}, +{S(5ba26c2d,53b04b63,d260c6cf,392786d9,41a1a7c,b88331c1,149cd3b9,9c207f5c,9c7f6303,bd72850e,d4da618,3427e7f3,eee078,6f67cf9,85d483ef,79c2eab7), +S(f7d26944,6d29f81e,39a44a88,49f12184,b9f42eef,16c5d88f,60c96a28,cf0c3e48,e7ec965e,d0a966a7,45087e09,a058421b,852c8a7c,3790a3ef,7abc6ccc,e17c6084), +S(98590b7d,36bd2ef9,37ce788b,22cab951,d921724b,87c3ad83,8263416f,b5a67dc9,b711c21e,5af72a36,5cc1b220,7ed09ebe,be918555,55e9239a,e9b03d75,c0261d5c), +S(92e1cc4e,5f57514e,9ed13535,5f3e2cda,b0b22f8,8f56a972,a657dba4,7263463d,14d296bd,452f2faa,7d6f42e1,550fe35b,3c47eb2e,fe7636c6,2eef90b2,e7822da8), +S(33de9b1b,e153bdd6,efaf9d1a,30aa9b3d,faf26a32,4486b6aa,ecb1f958,f11c4673,7ef29223,4d3a565b,c0f0e295,deffa3c9,a86a6bf1,933a084f,9334b790,51eb0270), +S(f9fad0b7,e1dfd4b2,47cd2cbe,bd863e1,95b3be25,6cf7a707,5d422b9b,f5631ca6,fd5262f9,e6ca0cee,e1c7e531,83d9f3c0,7ac518b,c024681b,56c06a1d,1826f5ef), +S(455ec5ea,f34c02c6,961469ed,27bc7883,c07c9a18,663e22ca,83830a66,569b2b36,4dbf1b57,a7de2fdd,7c0380c2,b38fa5d3,697a5995,8ca0c0f7,6405244d,bff4c9), +S(940fa05,d96fb400,bac8fa39,e696c536,71a490d7,c98b3851,ef76ed35,c62bac19,b6de17b4,1a045978,5755497f,1c2a953f,17554b4,20f2ccaa,9fcfedf3,5afb9b46), +S(16012402,4ded6478,72374af4,6daf9c2d,a9000841,9a7d4672,98b627c2,420981b,7c2607e0,a4b86f7a,d5df7371,60987d21,35d2ce2d,bc422ab9,b643d080,26f84323), +S(caf887e3,339046ff,42c629ff,c954873,2c629ecf,319c44be,6701a9f7,c81bf842,d253a688,3b3d51c5,a914ffb7,46e7ab68,70c2b1ab,5b7e46c1,7a5119e8,4ab6c878), +S(41a5b720,595bfc66,18406547,d59ec09b,4efba0ec,5bbd3191,9e9256ce,f40a7afb,87ee1be5,2233ba8c,82dc06a3,6fd0a1b,4ee8d7d3,ce89cb56,1dc96c19,dad79613), +S(8bdcbe80,fe8533a,174045a8,3cf9d89a,ca438ef0,8f6393cf,6e24d923,edb02586,8d21379f,c053e8dc,c628f0cb,83a7782c,b4e946e3,ca88d467,36232762,fc6f8fdf), +S(409a4482,6b6f0ff6,36178d65,aedd64cd,2d28b39f,3e9cd93d,a0afe712,b75fcd19,25a3bec4,74216dae,d686825b,bee6aac,487a6d07,606e5aed,7c3032df,f4911eba), +S(b7f414a8,4c0af927,64ed5b52,e96dad38,6995771c,2cd90af1,1986bdc3,cbeb61c0,702b0a68,4e93f0a6,26b807d8,8049db6a,3e65001a,d75a476c,e4be230,89574e8e), +S(58b0cdd7,bed4f327,bcecd493,d835c0f0,e45f6fa4,3687d1d2,f19de285,5bcb5c11,21b0a478,2c1d14db,9c67817d,7b23db21,a96e456e,62a85c3f,2a02c76d,78dcad10), +S(c75cbf00,893447ba,5b3f8462,9d6897ff,64be841a,9a6bb714,54adb849,8df03900,15f68903,ae20ae4b,9b3fc7c4,f8135936,c09de15,18317dde,bcf66190,2db79f72), +S(e6258fd4,b63be23f,38361a16,cf3f81cf,f2d7343e,515b2d0c,ea0bfe38,6d4400a9,aedca212,9b73e49b,9757cd7d,a39a1e92,f885b267,a188521b,32089ae7,6d351eb0), +S(7c61b66d,d76f759f,be92708,8b075147,2ba10db2,eee4f0a4,2eac1ad0,d2dac3a6,c2b79de,70372e1a,6c9449af,2612e34a,8e101cc2,81ef378b,97cd77aa,bff004af), +S(29c71194,bbe41f4e,b2f58bf5,799616c3,7b247283,c993bce4,630929d7,55f7c8c4,c0fb083f,d68f63d3,599c2da,9d0c1f03,b6903545,a47581ff,3caecbf4,359e0235), +S(f1cd983,5bd3eb61,f8207495,48da075b,ff4191ff,d5b1521a,a1f4413f,2173a68e,beac8102,13b643a9,4658cdbc,71713894,39b72abc,f8644697,2fe6310b,4ff0233f), +S(94f2137a,ae170855,3af3dd45,8f8fc9d3,c1bf07e4,35a62798,25d5b93a,37380144,e952d6af,b27f9b2b,80ccbe89,59fb8b15,5fe1b2db,f3ffbf14,cf9c1fe9,b8e8209d), +S(f4f0cd2,e28cb8a1,9b469b18,90033912,4a40a39e,67ae1642,a3a677b9,fa1a9a3c,75412f04,5c41770d,73d7cd77,bce0da96,1e6fc69,cfc6cb9,8f691d43,dce1e548), +S(15ba2fd,b652ea98,f2b326ee,e5e1629b,ec8f8e7a,36078d7,cb8a80c8,c943e98a,db416844,cf757376,cfcd5a9a,9a318b51,184f8c89,fcda3d97,e735c120,43d47c35), +S(5754903c,ffa8cb6a,3ffe3488,e484c498,69163b77,7c07183e,8d2da037,78b8da34,508312e,6c2fd008,fac12bd,afdf8e50,1940a81b,d05efd38,5277a85c,ccf125da), +S(3bc76f6b,8abc155a,dbba76a1,9c0fef85,373bb1bc,9775f431,1b3ccb12,9c7fb29c,48036702,aea467ec,daa8b809,97c41c07,9e38c414,f798bdc3,a531b08b,fb4ae303), +S(2992c410,306d3d40,313ef69c,a2e3383,88f592d4,a9c2bd36,ebd9f51e,883aa33,cdecc5df,41cd193a,ecb26700,baa42486,a0506372,d56de3e2,2fa09156,7b525519), +S(baa7d83d,6d70a528,e6392adb,b2af797c,72772b92,ace96a25,90d65c37,b0ff1bc,9943b555,4e44b480,28815d8b,e50189d3,6617e9b6,c8f07393,8309e04c,9978fad7), +S(935488ea,4c88c2e3,cda6c0ab,5b42d519,e7fabe09,fdd3e36a,7c78c9bd,730612fe,705c8f8b,228be0c5,539eae13,1f4f4bdc,2fecf6ce,6cc567c4,dc692af9,7c1d506d), +S(8edeb68d,e63c35e2,5e36bc8,61445a63,8b0637ab,d14557bc,f0b0648b,db43f86a,d20bd0ec,14ef4f35,68954130,6577ef85,baeeeed1,c1004130,80687a5a,94a9e9a3), +S(9a39617a,27c2ff2d,9869acff,b22fad1e,a7d04271,be154547,43234f54,7f21924b,917bf9f3,fe0563a9,8daacf90,dfdcd8eb,e657f023,3fb15711,79bc5ce6,e6f5dd56), +S(41dcb02d,21137ea3,a179b469,199f9ed7,9b831923,68719a4e,5c727ce0,d42e95d,4669d53,82147266,8327cd6b,31c21b80,d3394bf7,fa29a43c,2ed7441,d866f5e1), +S(d4e3e3cc,387e0364,6688a62d,9390d585,a69ff43e,1060811,431dea8,d868355e,3309af2c,b3d7ca6,55334f2e,c53de6aa,debf5d57,1618ecc2,9dc9830f,ec46144a)}, +{S(90edfd0,61871350,b7f1f029,c4114190,b050065b,2030a400,9a8a7a6f,4b92aae8,872d4f07,5f60ae2d,dcec2f12,98a22da2,53d54de4,fde85560,690dde89,1628db56), +S(27e3ce53,b4258f97,a499f408,ecab24f0,60aabae1,dafe992e,5a34952d,5e6e486,d393fad2,9be684f4,7ecec306,332ad969,75b79d29,b5863d98,5f959c4c,b8ffb442), +S(420f1078,27c75c95,d5bc657b,a46cbe2b,415a7d42,ad62cdf1,6c84ee89,22215379,8b257a28,e0b15fcc,ae0b555a,cdadf4a,be93bbb3,a208ee45,77710a9e,d58da7fa), +S(28859abc,b60a95e6,b26c0996,566bf798,60c1a194,2d3342f7,9655f286,32f9d5df,2a770426,5eeca5d8,87840cab,3923d868,aa7b7b18,f9d19941,738ca006,54ccba6b), +S(c33c317b,490e2266,2649a021,e87f6596,3897633c,90be208a,193f1d52,28614059,af850be0,d18708bf,d0e1f523,c1a4fda8,9a6c156a,55f82518,2bd99bb9,b999a358), +S(2333793d,b961d3d6,740fc725,6cf9d833,81b5dc92,85b6d83c,5c343aa8,5baea83b,accc199a,ced3012f,b6db99c4,7a36c30a,f88e7eee,b62c2ed4,1352b488,ba0167bd), +S(4834aa89,caa9c25,a99ee4da,e4a64c15,25a5d74f,9ce573ab,ff44b63f,c3a578aa,fb189539,448a265b,eb87a268,b71a18db,f12fe8ac,e502720,c83ca156,c1cc7a39), +S(432cc05f,c78b0d56,e605391f,e4708bda,a1ff3edc,96a729ca,aad3cb0,300c4d4d,6675d8ee,ece8c078,e1bad373,247ce383,d4c65519,4a6aed20,a7ca5eca,34a89c30), +S(874fda6c,b284ca2d,a216bbda,28249388,88da3968,7f615ab,52c8e61f,b72a1fd,2d7199e2,b0b88f45,18e6eeb1,240b2ffd,a9bfcee4,6f754a4a,3db7959,dfe857e6), +S(7fe21a0f,3ef67d60,1de05f67,2b163285,a5577948,ee78274b,19fc35cf,2d2f6801,62fb2ade,5e2fffe1,28542038,91a6ea73,aabfa35b,1f7fb000,95190adc,de6663f6), +S(cd0540e9,efe497b6,2beb2227,a2da819,a1ac3bed,8eae24c2,6e2991c0,a707dd69,446c3218,7d0c9229,d65dad2,1392e4a1,66445386,4a5c1620,bf768fad,20a45c06), +S(a533c295,74e78024,52218573,66c30001,6159d772,f5bedcff,28d7f7cc,3051457,b61a106e,e0166d54,5e1b945a,f3e4e285,31a34cb8,56a34dd0,cd21184a,9555eeee), +S(5e144f44,ed88845a,c089331c,eb58811e,4c164e02,5d1758ee,51a7a522,c66deb30,ff95cab7,597cf62f,e1aa8df8,a2cd3844,d71ab4dd,4eb02a9e,6175077d,261e2540), +S(97487a40,9562ee2,7620fc2b,c5bd55ac,6509d487,8ab41257,53884b97,7c5a84be,e52a282,1430c540,5f1143d0,62b69faa,16592f02,cb9b8602,d52c9fc3,70769091), +S(906f9ac1,d3db4690,d74771b7,f1940679,b0de7731,a68d7d35,89caae34,c3760eea,6f2be7,2275dd5,1e77a066,b8d3c303,f565cb97,b8fb0dd3,14445d18,876665a6), +S(4c355a31,d17617f3,5d4066c7,a125b2bf,1d6f77e6,27b585ba,2d271a29,a88c81b1,6a686e69,237b75a1,a1ecb86f,b715e858,35aa0686,e2ef10bb,38b762ca,5646e9db), +S(15aac1a8,9b6b7629,57375d4e,aad87c9d,e1fc2617,89e57d29,731fe96c,1380140c,2968c0b2,2c3dd83f,3286d37a,639bcc14,3d89e75f,cac1da56,7e07799a,62af1008), +S(4642f29b,37b87118,65767998,d00b41b9,fca6718d,bfc55785,1c459efc,2ea15037,a1ded31c,be33cc1,425df851,998ddd11,1518ed13,f84756c1,c7cc46cf,3f5e9a72), +S(9554abe,9e092bd2,c4ff46a0,abd0b7c7,fb73d85f,1c01fd96,83146559,306ea727,89e2c07d,db1fe6b0,9872ce91,66ce9ca8,1124986c,c7e48e09,f80aa3cd,70987711), +S(1c0977a,a773c56,2e36dd15,58b95fff,d35b4dea,ac464011,ad7b79cd,26227238,e04ce4c5,b0b165e9,87dfef97,d32094ea,3e86e6dc,33a4c96f,c9470336,34b0db68), +S(71d44171,60361c39,6295e5cf,b1540ef2,1ca046b5,98645a23,e8c9772,ac21be4f,7122d5b,32a654a0,1476a47f,d3317a25,ed51536,4a21253,f4068dc8,14660c55), +S(d906f59b,4d56bc12,f26d60e6,6fc31733,e5450707,89025ea3,8a6193c8,520ddbc7,cd7f414b,c6cc79aa,4c65b943,ff914234,a9d85cf1,9853be59,2a71fe1d,dbb20163), +S(11b7dfb3,17dd5c23,6b8084d4,f3cc157f,c25c0e70,8248b472,240963dd,9a3fbca7,dd2c8caa,2a2d14d7,fbec267c,9fe87ab2,8b1ddd92,dd100449,f75ef1da,97d4e5e), +S(3ef8274a,fca8fbb3,1894d7a2,d1ad59a9,4951b21,328b1e84,ecaa6df9,cc1ef473,2e4f0c04,f5da671d,4a2b641,46ae749,27bc35b5,98a54190,3883469b,3e7874de), +S(a861fc27,f5e6300d,ec0fbf6c,3315d053,98f90b84,2422992,bdfba11b,1c1c7e34,75a7458,5d98bca4,e3517897,afef848,8e524168,a586d8da,18834171,4b51846b), +S(9537019f,57d7869,2a358758,47e885bd,1594a426,4412c595,62eea9ba,4ab34de8,21bfd84e,a66650dd,79cc8c13,7217a351,57b9c046,938f20b7,246362c8,6637d5f4), +S(71c80c38,b1889d4f,507132a1,87af0c91,79994b84,1b816c8a,6d130a,df1b500f,8de1f76c,100e75b5,1572da6,d08c76b2,10cdc447,cf903e0e,2b00e002,5dee0ce2), +S(2fa9ce2f,59865d10,17880d74,d31cd446,5c28ff56,50f29e6b,d74a810,977ef9ec,6c5a2073,55c0dd3a,b994778a,6bd02b87,80ba8e1a,899330ee,cd8781b7,6e15cddc), +S(96ca6bb4,bf74c748,75703386,ae02dc29,13329d9e,a5395892,df1c6f57,4a84fa4a,fcb510b6,dcfe8da5,690f8fc9,4dea8ac0,e42af209,35c33347,b088cc79,52e98b1d), +S(36a066ae,694f2645,25cba884,956ebd46,ae568acf,11664965,63cee00d,a32f2199,5d1e975a,856ebd1a,cb1fe254,101e89ab,ea089e8a,b38bbd0f,1100e914,709fd965), +S(4922879f,800234c9,63f4b572,d236843,4c4da5c8,ff741982,c3d1e341,841d1309,9b259470,24b5f4cb,97c68d18,f7f8dfca,1e57f7de,63208765,2ce15770,98f35494), +S(ee27e2b,acf95f6d,dbb5ebae,bbf181bb,43322a3d,6db87e78,14e57a3d,41b581e0,3d6a18a,daf69ae4,88ecc9d8,7d6b1bf2,c8d65544,3f4b45b4,c5a0ac51,7b1dc3ca)} +#elif (COMB_BLOCKS == 43) && (COMB_TEETH == 6) && (COMB_SPACING == 1) +{S(e3adcb1a,fe947e6e,7cc4f5a2,df78310a,b235e2c3,bcd75eba,1904de80,c8814c50,e0b27ead,c4bbb9d2,37dad580,6e366674,4a9f9c3d,e024f2bd,1edb11d2,fafc0a22), +S(612c2ec6,f0e9c4ec,c2200d23,ddca77eb,d003be35,61e52b1,890bf846,3bb53ea5,9e944d7b,dfc7a952,c1c5ee15,485d7199,dda19551,2d729628,c215a9e0,285f656a), +S(42053dc0,9f90a7f7,86feca60,d68a5c3c,789e44d2,3479aa13,2d1a427c,621373a3,b338f891,f3930ec7,738669bb,3a29ae2d,637be08a,954f2a8c,acb661af,a660bd22), +S(4d227ea1,5194ed57,10f8ea4d,a639be04,fa68ecb4,f3d2d9c9,43ec32f2,80f74051,28a94075,bda4c195,40c20493,b5391df7,166b1264,b957fef9,683cfbec,4c6e6875), +S(8ec4abf4,d4db3b0,2b0712dc,ef54a405,54b6cac9,3f88fa9d,d8cad4c0,9b94eb1e,4e37e159,517346ee,17e948c4,a17912b0,b29c303e,497cfb38,16796752,99aaf9e8), +S(ab186398,117fb83d,db8b9392,cc888fe2,50d2f604,5a0c3f89,92d8a82c,382175c0,79437224,b3b1604c,fae64902,e3030448,51ef17b8,9af02d7c,54a92c34,7fa2abda), +S(2bd09ae7,1e8d85a2,ba550bf2,9c1787a2,3d2c8985,e2ed07bd,1d39e7b9,8ef8135,ddaebb19,195bfd6c,a44ce82f,b7f3c65a,8af935ae,52e2d2fb,33cc231d,c4b16082), +S(e6da6560,c8265e7c,8f62edca,78dc739b,74bd6543,44a939cd,bac0f489,12b920af,147f5ce4,a98377cb,981dfaf0,1a1e4740,238f1c5d,ee69cfff,a54f3aeb,e53980eb), +S(7df5abfa,d6b46a37,30bd5892,69edb6fa,b70a0d31,936c0787,407d5d6,96506263,bc5cbafd,1f5fee67,1aa449ff,bacefc67,cf7c529f,86100d7c,a8160e4c,376c76d4), +S(ad672823,cfe3ac01,50038d30,81a1a109,d8629b4a,a4e26779,373ee0db,44a466d9,c288397c,f91157e6,770c401e,dc84b328,87b22534,d11e11c,c8415a4b,743fef9b), +S(72032cac,3177c280,ceb3076d,7f510405,2413706c,956ecf38,acf410ad,a12473a6,8bbaf50b,95797824,2994e6d0,a4bf6d07,149792bf,f2a74847,1095f845,bb332036), +S(36a1f78c,3bebf8c7,1fae5e29,cf08485d,d9debdae,348a20,ad84ba5d,4308fec9,842f57e2,e900d07b,eefda7e5,9dc40139,c5deefcf,11c07c42,4629c59d,593e01af), +S(5f418454,72e80d51,1c1b8bc2,b6b89224,9119ed43,5b915809,f7b26d01,8427f579,dfc29fdc,f3c65221,a6615262,cebd9a14,acba91ba,cca22ec5,d84303cc,1fe0085e), +S(5577ced2,f6bf41f9,503aa4a7,c0060fae,6d33cda6,e3c8ec55,1eaec315,bb91fedb,858ded64,6c3d6231,87c0967a,84ee5253,2b21c4e5,b329bdbb,76f6a6d4,93cdcb4b), +S(bc74b56a,c94ef36f,2b356bc5,dfd11d2e,7a371087,653ad607,dbfcfeb8,27abc502,d57d74e7,39e758ee,96a422e2,f1fa61f7,4f6308f5,71e6dca6,3b0b0103,e83cf1f3), +S(727ed91f,be4c5086,50a0d365,b28ae4aa,c9434c0,735fce3c,7b3ebb42,f4be911a,8c595547,5e627c22,6bfa855a,22ebbc56,d3d0b331,9120d8e9,13bed868,7d3bf331), +S(6286bb90,71f8887b,c4fbb3f0,a9ecb19e,fcdd2cf1,5e286da7,1bb60c9b,5ff5e644,73731ec5,ea3e537c,c6060ac5,dde716b7,2aef4117,ba747b12,1c26fc9,eddbda74), +S(3905682b,72282a78,2b8d8dba,72cf147a,de0025dc,a21521e1,ea989040,c248852b,5da3bf52,dceaa14a,a3534068,c05bb015,2cf9c722,cc80c56,cf828ace,d96fc390), +S(561a2ccd,ca12b67f,dad28ee2,c3cee78a,cf811766,9e4a2543,c81b1ca6,eb4bd16e,3b386a9d,553c2746,893477f5,17a05e99,bbc8ddf0,896249e3,85e406d6,a57b9ed9), +S(75bdfa06,6a1a42a7,50f283e8,3ec91cc0,a5b68829,6e6aa24a,28a61e33,65f378e5,c73224ec,797d6736,c1a541ab,34523d91,e1ec3754,fe7fed44,474f9cba,29f305e5), +S(e881a840,847aa2e2,2417cd3d,3e798c56,1e630290,5dff6bf7,754d9419,98d401e3,8816d775,f1555452,f624e45,710a240e,90fc5e23,7536d217,cf555349,b5e31bac), +S(1fb527e9,4e9c70e8,657de745,8b81ef9e,e3c2b4e0,128a675b,f7e28980,e18b201e,bab00a09,1145407e,693d5353,7818bd30,49e1f364,757d228,58b8aac5,416e3e78), +S(1c2bd878,b94169da,722a9de0,c4e317ce,a8802aa9,60458301,11a89d1d,9de4270c,95b7d99,bc3df31e,f0dbf17a,a09ef148,71ae6c5d,870ebe75,1f52aaad,a48dfcc5), +S(41f7fa0a,9a59513a,e221e3b8,4b91995f,c9d40eb5,d120a6d8,e663452a,d92099c8,813dbbe,92ec4b62,25d1cc5e,5820ebb7,dbdc2964,d5dd1ab3,deabceb9,5e793d32), +S(e5cbd627,89c6a843,25a24407,89b88dbb,1dc55afb,9e8296e6,bb8af7de,57a50e60,2cf01cea,ae4f4fed,46c4efd7,86ca5e4d,ef5af524,e9393d74,e9dd224c,604b9408), +S(eb3bc68c,623b1f46,ab905412,c7f2d588,fa25abb7,7a7bd782,ba9bb3aa,c05a70ae,df9f46e0,5dc1d126,2f72ef3e,8db01fbc,11915af6,4aee0c51,da1cfa,b4d15ef0), +S(5702fb8a,2602f41f,52699f68,8d4b005a,128762e1,1dfd13fd,22ea751c,cbedb2ef,a77105b3,7af534e6,46f58c3,b02543fd,9e0666c1,58051952,a61f8389,73f5847), +S(66954eca,5434263,4036fc7,fc0fe33,81f5195e,88433bc3,2c5a8a60,341e2859,d8b1b14,40e9f123,f39c8658,f9d64e7d,fe4071f0,ada879ea,42eebad6,4d37f4fc), +S(592152c3,98d6c719,636a03a6,dad64246,a5a6814a,a62c156b,ce5332f,6759b031,8d22d1e2,d93dcccc,889f3b6e,dd5e2098,2f5586d4,bac06842,d689a37b,4b845c12), +S(5699b93f,c6e1bd29,e09a328d,657a607b,4155b61a,6b5fcbed,d7c12df7,c67df8f5,c147ee87,14325497,6b2e534c,e6902748,2a5c33dc,867732a5,8338f05,73368388), +S(c62c910e,502cb615,a27c5851,2b6cc2c9,4f5742f7,6cb3d12e,c993400a,3695d413,e80c2522,898d8a22,2c4dc0b9,8dc9ce88,740fe252,5144656a,c30f978d,dba83c1f), +S(0,0,3b,78ce563f,89a0ed94,14f5aa28,ad0d96d6,795f9c63,3f3979bf,72ae8202,983dc989,aec7f2ff,2ed91bdd,69ce02fc,700ca10,e59ddf3)}, +{S(efbbec14,ff6d7e18,a8015d68,ba3ceb19,d4cbfbb8,a1bbc09c,ce755a23,e9d4b3f4,a76faa08,2b02b8a4,405573c9,5ac067f3,ab936127,24bfc14e,a27f923d,3adf0133), +S(f02f6744,a0ee7b0c,7bf0abb3,c7ec0a5a,3275d109,92be2893,7e9ef649,23fa932d,955f3630,c5621787,13e6d8f,39efee26,d91a0947,8da36db9,4ab4a89f,aaf9019a), +S(1c6f9f0f,d6ecbca1,fbcfbe9d,1f3d2476,dbd3b6ca,ac8e18a5,949b993d,8c0063e5,e3e3770e,33081dc9,5125d69d,c631450e,5494d21b,2ddf48f1,fbc3169f,3cc1eb91), +S(2062725b,739b793d,c9798397,dfc7b1a7,24ebe8f7,5e5654df,536adff1,25970841,22f3e3e5,c0201265,5e6d8e58,b8ba633d,9a9216b5,445a7f78,a2605688,4a5b6092), +S(57ffda66,e0c29448,7a1570f7,e2f5dc58,ecf9b906,5f374046,5bd1734,841785df,22ae8728,a8800436,1264c380,3beddb65,a9dee575,6f0cfa97,c01dc047,3c806def), +S(4b49fa7d,463cbb43,b14928c6,7b3a85a6,8132ad9c,427d3cc3,bba76d1a,7c9a69dc,7e7e3133,3782b30,5c7c3bd6,94e3c306,b3f9829f,86f76f08,d44b1a76,a207f2c5), +S(411a3b18,c9b638fa,38a0e811,ee370f34,5c9476db,61df1843,863d6d7f,4e803ccf,bb306f97,e97e492d,e74afcd0,a771570a,8210b51,9e61df78,4e6e97e0,ac23e109), +S(c8227510,49de1572,37afdf55,d175196d,5d315001,3670b478,a0255a4c,ebe95075,16be5950,a3f12be9,5905fb52,8d5bd93,23eb5908,211d2e8c,26204b32,556c5c45), +S(e46bdb74,8692d0fa,e6fc9f50,6685487,31d61f8a,5717fb88,4e1f7a41,70e4997e,35398e06,48bea42,cc48fa4f,2805efbe,6bead663,c054eb08,2c15ad46,c0611052), +S(d18293cb,d0902dab,e4c014e1,22969601,84aedbbf,a01526a8,6978e144,88ca794f,3a5b423e,32c1d2f4,bf754f42,9da3c9b6,84bb798c,ea73f637,1777ba77,1aae6086), +S(7b9cbbfa,a53923fd,e9f50845,3130101,3b17548a,8097433,79d3a526,d1c90c31,3fd7b71,5b6801cf,30fe22a9,8b7895d8,1b483f6e,9fdefe7e,6c59fec8,fb9786b4), +S(ab6e185c,be9ee393,5ad6278c,167396fa,aa4d936,80524d16,27ca8e93,48fce67c,7154007d,3f96ce60,d09499a5,1262c7bc,3ab5e9aa,929e0594,994a9671,8f912987), +S(56ffb418,8b9c9c13,4a110c03,8df8636a,71141ff9,8448cb79,eb6dece8,a28b65b4,1d728ed5,23ab821,ac0c40e2,b9348e27,191536c6,79a73835,8bd9b3c,b43dcafe), +S(bdba1eff,bf6af786,dca1da26,cc226102,1f95d56f,245298c0,b87a40e1,fa9a0cbc,fa18673b,41f765b8,25d13f2d,7963f1c6,b3308a14,b033fecd,e1d0d597,583440b2), +S(d9a4a7f5,5b3af93b,1c49ffa7,4f8154e8,23aa1477,16b16441,39fbc86b,2d9346ef,19ca90e7,538ac3d8,a28e1aad,5d8b1dcc,756f8a84,240e5769,a0f561d,f53289b), +S(ee5c17b2,74c78b87,9eda9341,256b1506,25582835,60a0bdcc,c8eba320,32d58638,d7d02f4a,b81215b1,7843e3c6,abb63755,c7e05d20,6c61a4f6,bb08d1b0,256409ee), +S(6953ae81,7ec599fa,5f739bef,dc4cd8b,4670eac6,912bd03a,a3fbe91a,5122ba0b,61bca3fe,36245ced,31cae918,fab6d463,50856947,f64a3be,d0eb62d6,f9f16059), +S(3bd721f7,59f7c3a9,93474472,4ffd3e34,503cbe45,fa7fa5ef,40a5a173,b8bebcea,bd7cbfbb,797034c2,d293aa64,1bf175dc,74a44dba,84b8d99b,8182d2be,6512ef2), +S(ab6e36ae,f5b7ff2f,fd2f42f5,8c408b7f,3ac84253,e1d8a43a,55cac222,678dd38e,ff0dc59e,51e504b2,9a0ba824,3ef26eb1,2446f628,78293da2,d924028c,21f57342), +S(bf1a1f6d,7f73b09d,3efbd73,eb9ea2c6,23e17911,9063489b,42fb1303,7b53fce4,236566ae,5371fc97,b5449b9c,2db32175,90e02412,b44539d3,bda7a814,3cd4a634), +S(fa0c6aa3,4c4825de,304cfece,37a20bbb,f72044e3,94cad202,c9e1edc3,b288ed57,df8e64ba,5f5e8381,4b6891b6,14dd4753,fed5f566,956a6dd,95540bdd,25c73995), +S(d2ea634d,ab56961f,52e39ba8,ebc5c150,ba9eeb1c,6178aef9,64fa97d5,e80581c6,2928f71e,a09a35b9,6fc1976c,f6cd3c1c,7d1fcea0,2142eb80,9666917d,c9a0621b), +S(827d6a36,697cb2f1,cf19e27a,ac30c06d,20d818c9,fe869309,6435f1f2,171c9b75,53237d41,3781a1df,d0d977c2,9690d488,b051dc2a,841ba226,c552cc28,12ae7caf), +S(59dafec5,4df05695,e123004d,38d9aa12,6a06c399,e5215245,5392e5e3,f1a47dd6,ff7d173d,ceda39ec,ca8d2c95,ab1658a8,6ad99618,e3fdb7cb,b07b03c5,2f96a86b), +S(5c4f5b9,2acb30ee,8c7ec4b0,ec577f41,a9502af9,a5a384dd,3386f230,3e62a830,8aea5f7,145de3a3,679c86ce,72e8b36,f3d6ac81,bb1d2043,9d719c32,ca2f8b30), +S(38add3d6,209080c8,efb65551,3d9420aa,457f06ac,d3ec84e8,db84e208,a0664ba9,ad95a891,c60f01a1,5fcddf2c,923790e2,d5596334,4d83a21c,dfe5f6bc,4c2b32b3), +S(9038876f,21c3bc14,111d5f6a,4749d07,34e40a53,7d398c68,809355f,764cf1ba,da030216,5e661aff,53367578,4a4d3447,707f14af,cf0d771a,53e8e687,b9dc29e5), +S(b07d3e8,dbbe686e,5e163725,8402cac3,e1e4f29,fdf154f7,bf008c5f,9969da10,c8b668e0,56bf8173,2d9778e2,eaa069d0,cdfbf826,47f615c3,6d0b311a,b2e14922), +S(8bc89c2,f919ed15,8885c356,844d49,890905c7,9b357322,609c4570,6ce6b514,2cec0c32,283233e9,21889013,c4a76d3e,e8d2cfa9,eed8890f,909c0b30,5736aad8), +S(308913a2,7a52d922,2bc77683,8f73f576,a4d04712,2a9b184b,5ec32ad,51b03f6c,b5a4f6a,bc0141a0,6e1cace0,993fc8a2,57ccc015,7d42e0ed,9f54a102,1701afc8), +S(3f0e80e5,74456d8f,8fa64e04,4b2eb72e,a22eb53f,e1efe3a4,43933aca,7f8cb0e3,34992828,d693436e,16f463f7,b7a2fe4c,6afedac5,59a4ac5b,34fd761c,15a0bbe0), +S(d30199d7,4fb5a22d,47b6e054,e2f378ce,dacffcb8,9904a61d,75d0dbd4,7143e65,6afc7262,f51c2a3c,4c292136,167c7f9a,e089f33c,9b127e69,fa4c00df,dbef9176)}, +{S(983e1ca0,5dd64afe,8d39e15e,43c1cf9f,6badf550,12cdea89,5fd85021,b49f173b,18200e81,d75a745,c1ecfe5a,3ead988e,4327693b,7e3c0e04,329b0c80,3d9934ba), +S(4e1200fe,d3e5048f,f6c9fe53,cdff8ef7,5d78e601,4df471fe,369f3d1e,80e46674,a62aca58,4ecf32bd,cc297b38,c1951c71,a66966f,b9ceb2ad,de411da7,d175df94), +S(82f5f27b,83d49042,63e69f48,b676c9a9,2446955,cbc18ec7,fd4346fe,37e0b0a1,e523d039,d0b8ccae,737bfc11,9b83dfd3,d6878403,862618bd,40ddfc89,9a688557), +S(a8c0fd4e,ab1f0e3a,cab37da6,58fbab3d,949ad9a4,31d6461,b2648308,35762d19,950200a9,63c3dc2e,6d2771e2,eff401d1,f9ce037e,75782943,3fd3c560,a169713a), +S(25c1b464,33068900,bff89a4,ee973937,b03c11a,5b33d16c,4f672fd4,bfaa11f9,471f1f8c,df18e73a,e12cae35,43506cf2,c57894a8,f633ad4c,e1c5330f,eae42b43), +S(497e56ce,8793bfec,4dc6f972,dbaf4a72,aa5d8412,3bcac5d4,2aa9baf8,ccfd6723,1d3d028e,b70105b6,eae66785,5dfe7e0b,7c0a6b3a,b069a475,bd913aa9,c29cf426), +S(68a285b0,d30c2dbb,b543e480,f66824b1,69aa339f,7e2282c1,5dc60f50,8e3f2fa6,87529a0e,1d53588d,21064447,99a8fee8,ffc791a7,cacc9e26,f7a7d94b,25e74cdf), +S(721c92ae,931712f6,15f4d7c,498dda23,e1029dbc,944cea16,9442b22e,d755a100,7ef052e6,a0d2646c,a5dff39f,3b2a1019,de43e3b2,5e51913f,d636281d,cee74a85), +S(aae8418f,1b124d79,ad15f773,1bdcea1b,453493ad,4cb0cd6b,ee5c9a8e,34335ba3,c2558bb1,4a7addab,62d1f308,1a9e6fd5,92a06976,3be877d2,af9ae1e8,b510d98c), +S(b6a06052,b492d74,c68640de,8bd3251d,fbaaeb0,ce1eb997,f2127ead,4bd0b81,7bb4269c,4a9ece8b,e8ea2f0f,d663a193,2a571249,2814eb73,4ae1493a,6a97b39a), +S(42fbe048,38761203,11e43178,6af555ed,d7bea551,d4d5aa18,afaa4c05,2c6d0c19,9ba562c0,a4cdc664,87caf55c,291d8246,65c29f32,2a56360e,90138b06,f109d83b), +S(9d6dcb88,b378e7bd,233464e0,25e97bf9,97768e29,6e4aac31,301fa1c8,bfbcc27b,f434ea54,3f7269cb,1ae1279d,4178dd25,bbc337ad,2f0d5141,610dc061,80daa8c0), +S(fa6fa5c8,1e6713d8,90cd6f29,cc8b7d03,c5f8f2d5,88963652,7c199f6e,8f98e060,726194a,2a5da1b5,85eba1a5,5e06ce36,5aebb89d,ba5f7971,db3c1571,c848c5f9), +S(809167d6,85e432c7,372dc111,8573e620,d9ce7e9b,c3ebc15b,ea7e866a,3a8addf4,bf0cb694,f00fd1b4,b62119b3,9dd6c88a,22ef1fed,a94ac0a0,9cf33225,9298b968), +S(99d28ee0,962c1bd2,fa63109d,f5485653,964059df,bd514206,24117eb4,e582ad46,8cbdbb66,4ee209b3,419c0b80,36affa24,85cdfbc6,e6644429,33908490,f6f6353e), +S(559d0617,e0860cd1,87f1c68,e9a9951b,fef7dba,3a67594a,eade6e25,e2df593f,194595af,721e3b26,786fc25a,cf5b2d77,1fe00649,b0cc4938,7168a9ea,1fd2947e), +S(ab9a7228,f0d533a3,4a95af3a,51021b55,bb324229,8d101a66,eb362c97,eac48b02,80a6d111,e451eb09,cbebdd5,a3ce4fdb,42b3665,f8974df8,912221b0,fc7db6af), +S(42c6680d,18b0f528,274c2eb5,422e4731,560ea862,adcc4419,68bb5f3a,5bbfcf16,c8961934,e5bd6e7d,d64d4b1d,f7867c7b,978d182e,48315ae4,3fe2e082,a9455792), +S(e8bc0f9a,c3687bb,69627de1,df71fb34,21e0252a,e33d15da,3039a200,43ec3374,15de9e9e,2ac74839,5116ef76,1267300d,78c2e583,9286e6b6,366c8a6a,540036cf), +S(604ee928,4ea1c8a9,a0a75bf7,be3a5c02,8f52d71,b5fb6340,53401e8d,8f5d3d6,43bc16af,5875b25d,8e2427b3,3913f6e0,bda886a7,52a3b812,f8f191cf,f7bee54b), +S(7f589067,5b771ce,ac754412,a9984352,4f2a4a6b,a5cecae8,5106bb81,f7e4d9dd,a74f2eee,584696c6,88157a3f,fc27085c,6445c7eb,e363c6ed,7a1f5902,63de2024), +S(80aa63b3,1fdd0d2d,ef602bab,dd88a15,71abd052,270988a4,63abe269,fc489fcb,9a145d0f,6dcf0edb,a4873287,a2f221dc,4be613df,f70f7755,f28879a,6e7d9978), +S(ca9793bd,796ae0c1,d778f3,6445bb8b,aa50d859,4be28fe0,ea1e2d16,30972503,7304af4,4c56106e,f0064436,c09b411b,926c4c56,cd82577f,f8c01ab9,9f7c4c6), +S(4cca305c,baf6615e,8cdc8a58,f3a9e52e,97ee20f3,ef080793,ce105a03,a1f1d3d2,637ea84c,9578321b,b5dcc239,7f6206bd,abdfff2a,128d3645,47d474f4,8a8ae493), +S(cb7813b1,a7227a24,cc53b458,1985592a,a9077113,28fb2a05,1d4972a2,3127a913,208b7263,f8f8193b,76dbfb1a,6c4f9264,dd12f63f,e852aa8f,8375fd00,eef83482), +S(d61676be,a11dd1a5,79164cfb,b2488e54,555d7d55,23ddc8a5,454c49e,4a86d8ea,1875b5d5,6db950bc,60b1eeb8,17a94af6,5a19f690,d48aacb0,e483ad52,61995cd9), +S(7142872,6f59c2c8,b398b22f,2ab5c3ca,e803a34c,ab580947,9b7b4d83,1f8e8c0d,3702510e,324c0911,b3154c61,e064ba23,68eff5fe,6f05353a,34980ec1,87ab9ad), +S(72a73d15,9cf7f79b,950971b7,1c66f362,54d13783,c5ef67b8,4da61dcd,4cf432aa,77d711e5,c1793c36,eabf9b86,bf3a692d,234f02db,e4d5854f,5deef926,b9818d2f), +S(ab3b2de,c0a8f23a,a7d3b9d2,88870969,2e45ef47,37a792a5,d65af92b,e58f6347,ac4e4ac9,1bbb57cc,1437e612,e4d4ce37,3d4f0286,5905b621,56826d80,895cb48f), +S(fe5efdb4,32715fed,f9bbb16f,d65ddf6,4ce9bb13,14a491c3,3bfedeba,cb4704ef,e5dc36f5,5fac6974,eaf9d8e4,232a22c3,8aacc6b9,1225a97c,2e695940,ec795c21), +S(5f94851c,a4149e75,e44a0d8,17e81c75,132eb242,c96ae41f,53f30ab9,c8c828a,d9473c47,505e0117,1a0f8682,777d0ca1,7399208a,b03974fb,4014f5bf,c8cbaeaf), +S(5d1bdb4e,a172fa79,fce4cc29,83d8f8d9,fc318b85,f423de0d,edcb6306,9b920471,d7bc7d98,86c861d1,86b4466b,c75dd9a9,8614e166,693a9184,8fccf998,847cb2c)}, +{S(8cf1437b,d87e7faf,550fcbe8,81b0741c,ae0b48e6,e0b3c3d9,9b1b1924,2a925a26,17b40146,c313b1c5,3bdf52e0,efebda45,80d86783,910996fb,8b2d0acf,9bd253a8), +S(fedc65f0,d622743c,53fa6172,e68d983c,2c9b8fbc,58943f48,ad06a852,c98403e1,ca5dcf28,1a2f20d1,5fed06db,89dfd290,8c9d32a,cf40fe6,817e861f,7f168ea9), +S(846c6a48,e771b90,4ae1e8d9,d3daf1ff,d1cb4c88,bbd1de98,454bc817,954654f0,5a4947a5,8a0ab994,3cd0a90,6c1d1f0c,3fb73d2a,49b9c0d1,a9bd35b2,99475dd7), +S(4998795e,a7d80937,e4abee13,6c14585b,40b2dbbb,3fd3c80f,c8c61b45,cd1d59e8,8c817eab,e210398b,59b31660,267c099,baf29e4c,894e09d9,a50894b4,ebbe97f6), +S(76c29921,eb3f3d12,5c11b906,3d1ac276,a205804b,b3e7484b,72ae0483,e4b3c517,48d8a8a5,fe658e1a,4d736bd1,8f07b5ad,28cbb775,d9388e7c,4fecd837,c8eb964e), +S(78de8fb2,b15ff39a,487135a2,636bdcea,57bb3a14,efc329f5,a06bcefa,7ffeaf09,a55c9e98,5e1f529b,ba7ccb6d,3a5c00ce,28cb14f3,e9012d5f,9f66d8cd,5e45113d), +S(71fb3464,b0600c08,7fe70906,2792a676,27830e50,a0bfb473,ec909530,30799282,fe036726,cb928a8a,8affed30,38977377,2a016e7b,1dbde8f3,6db810d7,cd423f36), +S(f1f23de6,bf779cfc,d864dbc7,b46009cd,d2306b1d,cd0de2c3,65f3006e,565d9e21,84f5a011,74ff8e7c,c9a98f52,3f2980eb,4b013695,f6239016,9cc5a1d2,acd8193), +S(f18909a5,a5db0daa,ece22ac7,12ae04ee,53484793,6fd8355f,9ff5ffcc,ab6ca78d,6306f08d,13ce2b0c,b60b3b8a,b0bd5f46,b3ef2a83,7edce51f,2cb5ce41,79f46344), +S(7598d96b,189f476c,bcb1992d,9a38adcf,9d1b6ce9,6c87058e,24584514,a0df4770,7f288be1,b82da02c,a37f655b,d23643ba,bf3d5cff,3b6c2726,9ece7409,ed78bcd6), +S(1e44193e,8223b57d,8fc68641,924cded8,3a04d806,cf9dc18b,7af06010,4e6216eb,45ea3ada,fd726fbe,2b1d6fd1,858ccc2d,57b237bb,bc2965ca,ebcd1b63,53c2f2e1), +S(80954b78,4872286,8fe38891,2d76dfba,d676b4e7,7901e559,dd5983e4,e908f13b,6fe564cd,a0966639,e0237f29,46e92d29,4aee124f,cb33da53,974e8882,b0acf22f), +S(6dd8a9a9,ad7196c1,3c36464a,5cb9fb41,aad8b0cc,423b99fa,a281c5d5,a6ce6ead,79a51469,8767adcf,ed287aa4,914eedc5,8382af71,5485c2aa,c3a77c4,efe2aafe), +S(1895cc13,f99534a1,187a3d65,d1480669,f0d0c0bf,b42b2a9f,1a6392d8,7e1d2c6e,b7905afa,c0eb9b66,51de3583,2cfb9ac7,ae8d2355,1e334d59,32909de9,ed400a0c), +S(f1efcb8a,86f94997,65b38fa5,8edced68,7737b8e9,49dc05f2,190b359d,9ed2c0a9,79b2c874,22de216b,904572b7,a39f320d,f53e392,8dd8e533,a1a28d93,fdd4945e), +S(2944af80,c207d107,ce89fb90,ea6daa2a,c65c1360,503c36c5,ad798333,90432ce4,44b53d27,fd6730f2,33b8c29,f1256136,c637740,86e001a7,60bc1ff0,f8b6211a), +S(938a1dff,117540c8,f6ee7df6,2d3c2cf3,31b38593,131527c3,9804c6d9,874752fb,4683d049,f096bc21,2e0dc20,a8600b8a,a02133d5,d0ae8d6c,b26e4694,ed6f2aa9), +S(b1b3b2c9,54d1fb6e,98fcf939,efc18803,2f693989,81f19c32,f5e55b30,1f59eb16,423a6754,1a29bb01,95adfe15,c1df1c9d,81394e96,2bd1926c,cc789f5b,eef9ec29), +S(52e23f2b,19048d43,b1725695,e3bdfc7a,f4ebd2bf,594f013b,b80d6d0c,ec10632f,258a6236,67090b8a,73881e39,746906f8,eabb6683,4bc3473e,1c6fa8b6,54641cdc), +S(5799a463,cc817bfa,26bcc543,efe0e00b,ca9d2320,1989e854,620fc9b1,ffc14afe,645238d1,8c69338f,9c9395fa,5ef7c9b0,f62b7dc9,c4c6b9e,c545c850,95b6b7ad), +S(1c493b52,3f5a4467,6268220a,58246228,608a603d,f6edb1c9,1a90b060,70b16069,8d311a45,5af92640,e9fe08f0,2981db57,3b22bf4,8fd5e76f,31c175be,23e24fd4), +S(84f5a9c9,cda291c0,71aa9f8d,d7af5a8b,f2ee0dda,fb9139ed,fee08f0f,3f9cec2,df381327,16527be1,d86991a9,bc28966a,c3c6c081,4fc0def8,85e5d160,f59f4458), +S(712c18a0,ae87e7e,87856c8b,ff6e3f6d,996ae157,970e754f,bba80bee,421fcde7,b2788ba,e69d34ba,eb5dfd62,830396df,3b8934a9,551c6070,ae8203c,ccc50ac9), +S(a5104184,ee996a34,7122a460,cd2eba5a,11d04aaa,a47d2ede,25ea13fa,41ee01f4,f4b5f2b3,f7ec9061,7ab5f3cb,763233d0,afc8d4bf,fc535076,5dc5fd41,cc547ffc), +S(79383075,a46a8eb,559ac95e,7dbeb9ee,51b6e906,27e55b9b,922473fd,5f2eb63a,243f56e9,84fd2673,58754a67,2bac56a7,9ae71a5d,68150740,8eaf8c2d,b55ef1a0), +S(2b22635f,b83ab0d4,9b82edfa,a54d6234,8d3bee6f,edbe3315,9470b18a,96f97821,41d9f3a9,52f17182,ccf02725,b58ebd7a,1f37235,1fbb8f2c,15403890,e8b25b82), +S(272fdefe,148ef7e6,957fb8a1,4a07f0ad,b17c27ea,49532547,5114c1f4,76170a0,789b8eb,5846e50b,d489bf66,cebd873f,7cf05619,3674e7d7,147ea691,6a9b8451), +S(2c2da4c0,8a6e73c0,2efd6e7d,13006135,f09990a1,4c0a0eac,1f286108,f4a2a0e0,8011a521,d7fa0ab2,75765bcf,50dba163,9e89c9,300e8a25,31942cc3,fdade63d), +S(19a9e5f,1c4cae30,797a98db,a24051a4,38ea755c,7b062094,a34ed6e8,8a8ff2cf,e199e398,157a50a1,56f9d6d,afed48cf,f3181411,77b792ca,c5881898,24f1b8dd), +S(f7a4be3d,fc6579fb,43c5a960,890893a,f4026165,ed9e3ab2,e6929378,b63968b6,29221093,17dc39ea,de2d6fcf,c735ad2d,668fd047,2eefefa1,b6b225cc,2f3cb3ad), +S(e5380fe8,575f26ad,b7924ae0,d58138d2,68112776,b11bd34b,3eb5e196,33f0e9aa,4680278c,6f784be2,a496ec9c,6db371fe,9046b1a2,b97ce71,3b45bec8,bf7d8a20), +S(4c1b9866,ed9a7e9b,553973c6,c93b02bf,b62fb01,2edfb59d,d2712a5c,af92c541,3e086d2c,df4175f0,804348ac,31a91963,39ad1528,1bc14e52,8d3b0c01,39701c0f)}, +{S(5a13b9b2,5aa6944,1b0b5fb7,f7665dda,95aff48,e3250b4e,34cf8e28,87406c1c,245f250d,78fd66ef,24745e09,f067f16d,d400c387,66d2a1a9,af51e84f,733d0ba2), +S(617b41c6,8534072f,2302290a,4956171e,902634bb,4356bd27,ab83a377,13d8ef6,c867779b,97383817,dfd0971f,64c09c71,7c468a5d,e1bb8ed0,feb98f5a,305d8e6a), +S(ac118ae,ed46b08b,488202f6,96d9469a,e9af3cc7,ddc8eb71,44903a3c,ee43c654,f992fcea,8268ca6b,79f5ce69,c87dcfa8,42ea0345,d7c85a5e,a1b89f1c,9d0217aa), +S(741dc47d,2e0d1424,3f2bd96d,9033d8c2,9008c1e3,66bfb5a1,1434f1cb,3d85310f,c3135d44,e86f574c,266f97d0,f0279dc7,52aee0ab,8e3ed78b,ee0fc646,c133000e), +S(2e973138,f6cea9c0,5adfbb5f,49a7b928,5ae0c15d,120a8b1f,2d0c76ea,1fdcc7e,a05bc93e,68bc82da,7a7c7e38,95a647d8,7f724f20,c5b312b,738c6dee,90de771a), +S(9d938d6a,cf56104b,5522351a,3c424e01,e4d21c33,771ca94c,f55357fc,fe51f91e,881d05fd,4658b08b,edf87396,be3fbb95,ec9f371d,79ff4586,6ddc803b,65ae763f), +S(37a8ed2b,4085961b,84645ad9,8f2c901d,fe9a9a8b,4de8fc89,2cfbca5,c8909835,b1bdacde,94fdc3a8,78cd0eac,f8865d64,77e2cff5,efef834,97b93835,5b7b401a), +S(da1ba954,899cb05,dae6fea2,dc8ddd7c,5ef9e91c,3455838c,a49d2858,ec217d66,14051c3a,12332d70,f8ad8ffa,567765fb,def69640,4291b81e,d3978bf4,51f788a0), +S(92d2ef44,5a10dbc6,73f8c1e6,af093596,13bd1e3,c1b24705,bad2b3de,e8f11b81,adfed916,800d3e94,91a42c59,dd72e3ea,57d47081,b9afe668,f853cec2,c72cbab7), +S(e62208bd,52620df8,91b3ddbf,e6cf0641,6059ce50,be3b00ce,14e7a53,6d1acb07,1fb71455,53b2f4c,431fd86f,c72d51af,5e2edb41,dd3f69a1,9a971371,d9679dc5), +S(191fec47,a48ee268,35ed37ff,6758a80a,532f7011,ab099048,b024efbf,cde1d0ab,47b6389,b3f5534d,a632924d,5b65918d,f65c9c2a,2904ee95,4f24aeb8,c6a02557), +S(60c822fe,a74d52b7,d5203720,b2c2a361,b7628aea,d2a683bd,80655636,bed15714,5d9a9c0,120211b2,e8285b77,8f8d4bc0,b06240d9,22944248,e1cfce73,826bb67d), +S(f602da1c,788ea65b,cc339a38,c353bc08,1c43c2f2,6e05ea6d,ab52648f,890c9101,21468929,89348f8c,67cfdeda,31ef3429,23b5a450,9fbe9a49,f011d42e,57f998e6), +S(756240c0,5274101b,3059d23c,76d9ecdf,33e1312b,7a36af,ea21e6a6,c52cdbb5,ab24e853,900c7ff1,3e4f813d,a2acdd5f,53ce1710,b785ffbd,e74494ab,43e583ee), +S(8e815b45,7adf482e,e30e475a,4949e5ae,c7ff494a,a918f07f,c39441e2,360d112c,a966f04,2dd9edb9,436b0005,7473aa66,dd19b4d2,cbb3bd95,932ba98e,f1a1f406), +S(216d5970,36c661f2,cf6122c0,a136cc33,9660306f,1c190ee5,5f3bf438,e7189261,5bd23797,c6753eee,6fb934bf,dc897ec1,25b31c9b,9e797ef1,56d8cdda,2ec1d128), +S(a30ed06e,ce34d29,a92fdeef,6954b5ed,4065d592,3af09be4,f0ad4a73,e7c24346,f6aea418,6b38c188,b84fb6f4,2b6559be,f52dc13e,b7ddeccb,6e85a76a,8d4efeaa), +S(aa42d20d,8acf7c47,fe40ac15,3fcaf1ac,6c4ecc37,dfa39c39,fe5c7657,c78444d5,bc3becdf,464ce930,e42a05b2,c52d3589,fd45ff37,c7d1767e,f8462811,89634059), +S(7585f8cb,4812e306,de02393c,c06e9871,1953133c,bcb67ec5,c6a79228,69124ff5,c242853d,1a2c1c9,a1cd1b5b,364888fd,28bb23c1,2b0e169,67961b3,e7c8b95b), +S(b096a1aa,fb969483,185ba1fb,a5fa3f3a,d1da87dc,a10344a4,d74ef471,45cc45ce,edbc9490,23801861,44ee39a5,7bdec0a,230df57a,72649e04,c4c99883,a147f0f), +S(7034d8cf,df226df1,6b310628,35dc02c9,f3e3f91a,635ad93c,46be292d,56c0ec2d,f783b6e8,29447cf2,32769a13,7ff33f82,f9fb166a,aec8c3ad,1679c48,c1a913a0), +S(9f88b01b,b936defd,f61156a,b55ca290,cf282a2f,3718b0cc,2d32dd3c,49248b4f,c24ebc35,27052f77,41087cb0,e772ce12,1035042e,603f1301,e19cee0d,30029230), +S(14443190,51f05d23,343367e9,6add133d,fcb63209,2f218847,59ad81ba,c65e7445,745216d2,d11f2556,7e3b224b,6dce478c,bb988b96,a1240093,964e4ba6,f3b15554), +S(54aa2e02,3038fd83,f17a633f,d9ab441f,e039dd0c,57f9368,3da013f1,751c6813,f4d163ac,a9af3e08,c009b44c,b358ebaf,31aacc5e,e0bdb917,d1b4df5,420d2d34), +S(fd44e742,b04ca4df,693e3109,3ec52ad6,1f67264e,100618e5,88d638d6,d96efa41,7ac09cb1,2134a246,b17ce054,b38f3bb7,7d794f9b,2fca3786,a0ba665d,3eb0f14e), +S(f662f214,10cf6e6b,7f79fca8,7ea3925,6760d83,17478419,ff4671f0,2bd2ab6c,be4ad0b7,a56c87a4,8fbc166b,da8473ba,6518385d,bd004b5d,c85126bf,7b9309bb), +S(520e2755,1cd804d2,67f9b4ca,b3986952,612bb099,cbd3f3be,a54f505e,bba4db64,e3c75cd2,c831c539,b095ac18,3bb13a72,ba7cb152,5b9c214c,288509a3,98a9cc3b), +S(4d16fa3,2d74de13,111b3f22,c4133ea5,377921ba,e54af1d,666d6e9f,b01e117d,a63bdf75,78d73f03,75aed657,fbb8675,67230c3f,868f2cf7,b7275ec8,b02ac46f), +S(ab928c61,ad1c2f68,905b9e95,3c2629ad,9bb7c8cf,ad9f5a4b,35b35fad,69a92568,bf39ec2f,6c1ae222,ff383375,6bd83882,c05be6ae,692539a4,5124b972,e72159ee), +S(8731aaa4,93580732,66b96577,7bc24a46,17fb8363,51c5ad8f,c8bc053e,802dd07d,36957f16,1020430f,69a3b1c3,1a6df993,d9ca8e2a,8f487b7f,b2ccd44c,7f528ce9), +S(3514d41e,8b9388e4,33d1155c,c777c398,22ab36c6,3c2aaad7,ce674831,21231d11,7657cdaf,e9fe8147,10bb2ee1,a97c6100,6c7b0ce2,f5086141,ac2904be,1c7a9e3a), +S(9bb8a13,2dcad2f2,c8731a0b,37cbcafd,b3b2dd82,4f23cd3e,7f64eae,9ad1b1f7,6ba44d4d,50111c46,490622d7,b079c17a,f0ab57bf,b8ad2ac,9becf9d7,3c7edfaf)}, +{S(e60e88f1,dc24f079,f835b687,778307af,83446e,71809086,e6dac0db,9f191de4,25a0b86e,ff05c849,d79a176,1a634d82,29d2a759,fbd003c,b6ca4147,85726362), +S(55a5e6cd,c9e11103,749d469,dae84a88,9e744dd5,d9309762,15050a83,fef66fe,31278ace,a971a41,e2f9fb16,8e80ec03,37009cd5,b0a49938,c28c902c,a7c71fd2), +S(5f25326d,e99a7c5a,d81bf2ea,61a9195d,48832bfa,8caa9777,53e483f2,b5a62580,40b69a1b,baa2d0d1,5cb78a3d,3d184c08,5b4c16d2,90751ec9,bf3eb3c1,d3ec2ab5), +S(1a32cc7a,f88976b6,cbc89865,89e685ac,49381a29,1579bd2a,13f9488,c0333d19,4667a3e2,2048f781,11f009ae,5d7b1b97,850a3a5b,86f77880,b128c8c6,a9720acc), +S(1d78e74c,66247be9,352c9797,5fbc1299,743f9c74,68c64d1,f2d8affd,1b098769,37adf553,fa2a11e2,6913ab02,e59c4f2a,48133fe7,d9aa3c24,a76e9af9,48a29d8f), +S(8bb6f83f,6be5c2e2,b2bb265a,9bb3c931,bd5cce6a,48efccdf,daa040da,535dbc86,eb96a982,23278b81,6284fee7,b5b8a121,7350cdba,d56b6078,89602bba,bc003481), +S(a9f19ca8,1d2ebafc,4bb5d881,76bce19a,d2d2564e,185f905b,8f9eeca5,d6b12d6b,ab23a468,3279a9f8,e2e8f880,e0accbba,844bd4e9,4d3e4b43,74db2b55,d998771e), +S(19680305,c887467a,c3bfe44a,dbe65431,7323d8e5,62271e25,1a794b78,618befcc,48e9303a,3d6371f7,cf0a904c,cf16f606,b0007583,87ae0952,c6119521,dde25532), +S(18b3bd03,ed4696c4,7c3f896e,5df7481d,6f19a5ef,53d62ff8,2948f1b2,154175c,85fb04fd,6573a19f,2c136753,a1ca2585,d39dd64e,6e6e3fa5,fba362b4,ae9dad36), +S(38e3bee1,3cc3e18,a57aef97,4ffee58a,a392c311,14ac8baa,1376b56d,f8ac017e,8596c7f9,8abf3e44,4ab5491b,fe659619,f6defb58,f48e2b7b,57842193,cd48fb5c), +S(dba5da08,8cd17c33,3e892bcc,46261694,d2ff3a22,2a26f92c,17c5e1e2,797b235f,ebc15bfc,5729e986,85a19afb,d7f9a219,e60d2784,e9b283cd,3697f782,630fe222), +S(60456426,caea7fc4,48970eea,fab9c632,3e66fcee,50f92630,7b130937,25b85ee8,dd22df89,c5dae339,49459593,1433f2f6,5f719379,68e56f92,a38d1290,17881924), +S(f1ceac82,15670212,279f36e,f0c8ce28,3a192f66,460e4875,d90b2f51,a38f5712,ec11f026,c76c271e,fabd7d55,c8488d99,3298f6ff,c05ad4a1,26be9447,7d878b9e), +S(2c2fb5de,5dea249a,7bf6b196,8bdec8d6,1838da2c,12147045,18d6494,f09e50e,6c0d020a,d6bfd730,5ee73634,e939ac89,ca2b7d4f,dcbe760c,2d14ade2,659a5b8c), +S(2858ea8c,d40ce264,3c9a305,c0e2c758,af5b0b4d,5c57ed21,60cf7ec4,154ffdc2,33586bf,a61a3cde,4f402bea,123c69e2,2410c0e,590a71d8,9a1e8130,85cbb579), +S(2749a250,4e7cefa3,a3647088,6bec22fa,339fec0d,ec63c02,19e6af39,7fdec3e0,d3415104,a9070af8,b61acdf0,77954d37,57fcf742,a006129a,5c0d3529,3aa95407), +S(8edf3ad8,39e661b5,f7f16a85,dce3caf5,d571acf4,d0c6195c,53b618e,5311ac65,fca0efba,35a14d3a,9944367e,db4ac982,7dcb7aa0,12438c5b,aa5627c4,c4a15ed1), +S(dbb92358,8b083294,5d6d3b62,9cced57d,7085d3e2,d0890bde,95d80400,98c22745,42816661,5515429a,aa023737,5fb77804,624546f2,1f0a34ac,1ed4e4ee,dceddccd), +S(aaca9896,6196ce2,e13df3c7,d058a5e5,10566df0,5fefc04e,51f9dd,85a4349d,be389d02,789548e0,aaf67c57,3b91b389,c7e6ed80,ca3e637a,2a147262,80cd5620), +S(104804ab,e621b098,61fc540f,f4d6be9a,750e3cfb,706170db,e34c06ed,e0a431ea,14b700ed,a465915a,eff84ab9,fa568535,f63e4f44,1387ab4b,d841072d,97739071), +S(b2215070,a8ea71f9,b71f1a73,d6ce366,c322b8ac,4d135b3d,ccea68e5,a51f1c0f,9e415cce,3bcc4e62,6f3c8caf,fd632eea,862a046e,bb45e8bc,84fc05a7,a4b87e00), +S(600c6e28,be5ebd28,d83e6c86,bbbd9b0b,aca22006,42234634,6124133f,3348e6f7,822b1816,79e253db,a47ddd76,c0023991,5c0c0206,8f422415,94ac38b8,ff7994fc), +S(f8b9b45f,c6298dc7,13355bf1,3ee33ca9,c4c1aace,c09776e2,56b3746d,ccae0dc,bf4b7f96,1844aa2c,6ed8c8a5,6626386d,174e77fb,98c0c23e,e2b6de90,3f1f79cd), +S(63987b18,67fe5a17,333eef28,5385a0a6,576163e,2c74cd00,ba87b6fc,96a57faa,23ce6655,51d48ca3,c702a5be,87a05d98,b5aa1097,dd5877e8,11e32d76,4873e6cc), +S(2eb06c4f,5ea9ad2b,124a1202,cb13c1ae,aec4394f,4af7c96,d6a9ecba,540ee04d,b96173dc,77b46ef0,6f4141ec,96ce8d1,4adfcfde,f0bd7ea2,479ba6d7,522d037f), +S(5db39904,153002b3,1498bb01,16efd0b5,838e3495,62d9428c,912987f1,3aae5ae,2115b896,479d0d0f,75ffcb54,ff9e3f73,d1356ff1,4b14292c,6bfe8d54,e9e08861), +S(c0f14110,7f324ffc,f3d3e6c,78d5a628,6000d57d,7e85bc7c,fda15f75,79cef3ed,6eb03650,c8142b12,b330b96b,81214a4d,d01b068a,74827611,53d2c52e,e1315ce5), +S(7e3d1510,ed767345,bbb3515b,6f9819bf,33ed9dea,fab35d01,a7284cc2,371b0e66,4c6b3aca,5159023b,d43ec0b2,57e77e87,e5a6b745,178bb017,9960c89,361fd97), +S(f98b5dbc,3e54c227,b002f458,94850ff4,3b15b2d6,1607851c,50d1e4d1,8b20907d,1d2af742,b0b5ec05,bd6c648f,94563361,be03261f,da56f1ca,7caece63,a336d580), +S(188ef3bf,ab103784,37e1859a,7d53e899,6124c395,ca422c05,d6b21b3c,efc76ac1,19036685,fad04566,2ffa04e7,30c671c7,1b299217,c389c78a,cae9fcd4,ad70fb73), +S(b89070ae,96ead4dc,49be16f6,a1a30bc2,41b4e98b,c18d0227,4c9d9d87,dcbf00eb,90db373d,375d2770,b8dd6b1b,3e3b5899,34f274f9,49469598,e480e431,e4f195e1), +S(381c4ad7,a7a97bfd,a61c6031,c118495f,c4ea4bc0,8f6766d6,76bee908,47d297fd,6c950ac4,dc71111b,70c1a058,f66ea133,fbaefcd,246c63ff,6c531ce6,82b6bc6a)}, +{S(704a26e5,19071bb0,24eb2a16,48b66a6e,157b07a5,52fc4258,381f7d02,e8fd9b9e,d169ea55,8650aa3b,ce52c645,278422a3,69326e74,46039381,920f5857,fa3492fa), +S(705063f5,e481735,60afe9dd,5b2beeda,d6e46391,75ad7fb6,f73350df,51edf53c,32290956,61cacec9,472dd014,bda52364,3399d7cd,9ce2bb31,a5b23590,22c6ee5c), +S(972e7585,f8b1725f,4a7d18e7,77939aa6,3600a7b,a07c4d94,5da9511e,889473f5,aae4382a,49b4c97a,bc3edb7d,26ba7bad,16752047,dc99b434,f306c673,2851310b), +S(260530bb,e338f4a9,71930b12,4f18c82d,e33f22d7,32de9375,81e2b0d5,15e59d97,77c66c36,d6b09205,8ade9c0c,a311b60,a705c8c8,73fd2d4,8e6327cb,71eefd4a), +S(15464351,d21d2513,f792ee32,aca3eca7,a52cdaaf,9862172a,e7224a03,804581dd,d9ee19f4,502f1c44,f656ad4e,add6adbb,d8a04f5e,5344b554,894721eb,8e3e2285), +S(4fcc68a3,caf3ff91,dcce9770,11ad5157,6c844132,321791ca,a652593f,6cafcd04,e8db3aea,a35371bf,9700a9bf,b85e90b3,93cc088,9cd655e3,fb18635b,49fb59eb), +S(301c65ad,567952dc,6b39134e,c361030b,3f5203ed,da89784b,225548ec,45c75eee,95e2c0bc,7b4a7cde,8ce3dace,a691bafb,f78fb03f,3170b347,ee807f42,6b947560), +S(96ca5edd,8047ec6f,b5f8c37c,a734533d,13f0e6e7,f55d28,b8b10b5,121f4abe,1a21f1ba,f6a2e336,247f6ee0,88cca9ff,816b0667,3badc241,d9ed00fc,d2070f1f), +S(c94633f5,783c4e68,12143fc4,6e670978,8e8a526,819afea9,404dfa00,df1bd839,874f8760,33dba9,de463f86,b597219a,c1ea37f3,970bdadf,97c96c48,feec4ec5), +S(963146d4,5bdaa05a,b5115ac2,3ee862a0,2305ba9c,75003f4e,ab838f7,e9adcdfa,d75766de,299e5cf3,c8796234,5db9866c,cf011c5c,f108a794,5642a6b9,dd33eb0f), +S(dde8c870,ec4e9890,2a820a7b,c5784034,19ac261e,de5afd0b,82b8c5a0,7c2863df,e7833936,50d4af7,427158e4,6c753ac1,a5143c9b,c09de97b,3b27065,6f622b82), +S(546c6b00,c5e34a8c,4cc7255c,2edea33,d9f77e7a,c67ee629,bbdf1879,4622265e,5ae149da,f65aba8d,e214fa63,be9bbf11,3dd3c220,12d206bd,d56026e7,4a019897), +S(4340c850,d91b63fd,bdaf41bc,f8d8c010,d3f30eb5,79eeefc9,e67c127a,33324071,dd66033e,eb5ae8b3,3c47a940,2482bcdd,4db5c8e,dda3b422,35627434,dc3bad1f), +S(f796431f,679d9cb5,77bce62d,236d1fe9,b5042a83,fdd1cf94,1a2e00c0,5a261d11,718fffe7,68aa2591,d3fb1fd6,1a00537,3b529f9d,f9c27f79,31ad0df9,9aa74859), +S(f3d17ae9,d5b4cf41,27423894,916318bc,adfb5a9a,6c2ca219,1b918e43,eb5ed129,9da6d033,49c64d79,7ad09a33,bf71cf87,8e0ae6a7,9fa7bc3f,1451126,18415960), +S(23899a73,a4b52d27,29ab52a8,370ceef8,4c7684c,cd535f4a,ec360eab,cfa5a787,f1bcde3c,5c4740c7,37b066b3,8a1a88fa,c43357f7,f3fa23a7,b00815a3,85336320), +S(5805914f,5418971b,106305cf,c397dc12,f63a3b2d,8b451e5d,c1b60c2e,3d9e9208,616e2764,67a5f1b6,46279ee9,6e8645f3,76a80192,31e17a5,c92e26ec,1b76e62b), +S(85ffc326,1991848b,5e918c4a,53f291a8,31796b52,d56a5c38,8bebe625,d78cd07e,95fd6f12,7b155e6f,806c089f,3f5abe95,344e9014,e2c72fdb,e720bb8a,93d864a6), +S(6f1a57d0,6566bf81,f4b16329,19f477ef,aab4f829,e29dda4b,27627b96,1cbfe3eb,de1e8359,7ff20c63,3d28b909,caef273a,a57898f1,a797fd91,e00e2e7,da1872f4), +S(3d28eba0,4d0ae2f6,208adc71,cd6af3b0,e208860c,722903ab,e106feb0,af3185e9,cf8bad18,40d3d2b4,abfb5b28,b50702dc,512527d9,5bcd403d,9713ec7b,ef1dda13), +S(46511979,2b845113,1b32663e,baa14c4,20a5408c,1f53afb2,43cd496e,c4b18118,40bd602b,d740096e,842b40fd,8304ee73,842b074e,415d5ac,c5e2243,28b241f9), +S(393a8bb8,7ffe4265,51678524,8ac940c6,4aa70e71,512ebbc2,3485596c,a6d9da5,25773032,d2c1f80b,3954715a,6e4132a1,74b4468e,658f6cc,4d5f5d63,d458980b), +S(aeb37eed,35274696,2b85a21c,86b9fe34,60433606,9ae4ce6e,3a8235a5,e43c48ad,d94165e1,5372330a,7b5507f0,377a966a,541b6fcb,949398c8,dd6d6f52,2411d86d), +S(b0bbb4d3,100ce417,dec58f46,95ed8b30,4306f21f,ee11f899,585fb37e,a9905c59,89141fcc,9344e617,7742e31c,42c565d0,45c7ccb8,6f88df7c,5c36d23e,45e51d75), +S(49694de7,330b4c3,1431d452,f3e9e562,eddfe029,1cce58c,73b69cdc,b89e0f49,4715ea3,b6fdccf8,e224b93a,ffdfd1ac,ad8baf44,c5bce735,e7c9b4ef,3faa2656), +S(9a7720b3,ff0363dd,7263e1ad,83c32526,67b67d3b,72c2c14c,95f0804a,dd20f7a8,815a769c,494824a,a819ea,54b560ed,e1df9bca,d7df2d63,aae69c4,a02f9f20), +S(b89c399f,cdb3aacf,99796fd0,95c17cc9,786d79b4,9a1a255d,5c9ee510,38d3620e,ee3c4936,d4f9d1f9,d2642043,d25b21a1,aa32fa3e,ef44f409,73db47a,96965cdc), +S(add68bac,d7180ac1,4e9bbc5d,1e0c3911,f2202d90,1f9ba649,46b06507,b86ed5e9,fcffc13e,a9be67be,8d090a77,9fd9107b,f62bc785,3f98e97a,c0358b3b,7719944d), +S(9bf62cdc,cdabe928,c1ab44ce,38cd0611,135971ed,81ce6c6f,dfc5eb5b,d9c3996f,885b2381,e0e785d3,cc3f78c7,f9fd54b,ecdd9d9f,c2979e23,f97e9bd2,a4f10d3c), +S(bb223cee,f11450d5,c5e0b75d,20eb44be,9d44dcff,28574117,12fc5875,3b839339,8bb29985,87eb6f5c,c8cb615,285d9e69,71db8e31,8d81fd5d,6318909b,8311a00b), +S(9ed73f09,2263e84c,d022388e,da82361b,445f573d,b646cc62,fa0bf5c5,1d33fd27,49ce7698,144e1115,118a4b0c,bdb98dab,6b57dc1a,4d1bb6cb,a68d4d20,d8e937d7), +S(e747333f,d75d5175,5a0cc9f0,a7287084,65a02c58,7737a7b8,b8fa1b8b,4bb2629a,d5001fe,baf8f3ee,b33bc9fc,7fb3da7e,377c8955,91e56965,607269e4,96b90559)}, +{S(516ea742,3f956765,a15f856,182f1e7c,18d3e548,b9fdca94,ea46024a,1a8fcbc0,cfddd98f,be500604,28beb901,92c01282,aaf9ed93,76f0e26b,e912ac42,a378d542), +S(8ed072cb,13eb90c9,e5c107f6,ba576a87,ac75a431,53025e6a,86addb95,bba7862f,eece8a5a,f28d356b,f1a84c14,2549ae7f,894f7c07,305194f3,18e30e0c,873fb5b0), +S(3e0e488b,1c8acc24,f81bfa1a,f6761afd,b4fa5f5e,6317ba72,bfba2f07,6c2ff593,2c4ec39b,2f37b948,fdbc9e3b,a3fa82c0,dcfde2e2,57830a04,4c178759,ef5bbc8b), +S(f6b3f19f,cd7eac5a,13630ad1,bbf331b9,5608b7a0,ceaa67e2,4a1e830b,5eed9825,a2a64078,19ba0f7e,7a890df9,523c6736,50ad08d5,c2557cd1,53c6ee65,ef493ce5), +S(e2de599a,879285ad,398b1031,8e1a8387,21a5c4f7,1105469e,affc8e37,189548a,dd977e62,3dc05cd6,3155a71d,d87d91d1,d74d6e31,5f73434b,679b605d,9db264df), +S(bc52359,683840a8,59fc2c8e,22d72ec5,da2493a4,bb5073a7,f658517a,de83f867,76a13651,712ff42e,a0c10f09,45c4ce41,2cc8c4f2,ac3ae446,7cb383c8,656d3186), +S(d87e2b08,154e38c0,13847a5a,2f84c276,dc088b6f,c19a9bf3,90a8a433,fa937fda,d91447f,7c9042e4,6f64a9c6,40a5c294,1a6f08f5,d3eed464,b09b2a1,1c402c0c), +S(4359ac5c,5dd70bf1,d4f24f00,1301b9f6,17d9c69b,63350dad,9a8cd192,655ea801,c9e14f94,d8ec39aa,46092657,5d770426,350794a4,b1ef2410,a9b0118c,ac5d55c1), +S(3ab3f503,29e22bf6,8fe9882b,3b4dc989,36dfd840,be6af588,ab6d61de,48094b9e,4a40c435,9f4dd21b,11664095,4b55e6b9,f0308786,eec32fca,6bcdd5d5,8d055ac6), +S(a0f1ac27,d046d987,b9a98bf3,f770205,931fce93,59b75944,6494d221,dc2752b2,bf1bfdc6,47a15997,97b5b6cf,2d8a2a44,e9c36b9a,20b53abe,58ce9e0a,395fab70), +S(a9aa63ed,3b0260d2,b35b030c,7a5511c8,679b6170,b8a4f386,fae0bfa8,92735905,2f654620,4dba9b31,99bd648c,cb1605f7,62f312d1,67123d98,ed66f02e,4d794e90), +S(e51da157,5e566ef3,bad3fa66,e55432e1,29be6c36,32e390c0,f4d76719,bae34b7b,55930b79,2ffc91de,674820b1,eec290d7,74ecd79d,1ca07d6f,5ea2f817,4759984e), +S(8b69a891,b417ea65,7ba52bae,58920eb7,f101bb92,e150dc1b,c961c37e,73cd18b7,5b824edb,f4a68988,39de0d70,ec16ee1c,a01a4692,f29263e1,1069aec8,6b149214), +S(ee5c1186,77462649,6d37fba,e48e797,dc69f31c,d6ad22cc,937c7224,ef7ce8db,13f7987c,f2cc4551,e058d410,3d5c24d5,13c2d602,ddccadae,fddcab82,d14d5310), +S(b961cb45,5de8b5b6,c4c5c2d5,75303c81,35b5d278,503a15d2,371b0551,5b236b,3ed18486,5ca969ba,31c734a,19055129,1cd664be,576925a3,2a142769,4fc40df2), +S(83effa4,c127b5ad,7a0c53df,5be5596b,d4a42987,2eafd8f1,5498a6d2,47b87314,efa99759,c9360b14,7643de2d,64ad07ea,39cfabe0,2bc94e00,dc9b2e70,c2e2c217), +S(f253fc52,4fb0254e,e772f50e,5d6c16d8,48692455,c855d4be,15ea9f9e,9a5411dc,59bd1f8,2cece609,8a63fa1d,48e4166a,d79174ec,5f8216c3,16c48c1f,e5c9a0d7), +S(6b61024e,e6451d97,1446b55,1476c3ec,eb6b19ec,16155acd,317bc2be,11086411,8de5c6e,37b562da,ca5a203e,ea028b76,e6befaff,e54b49d2,6605f169,592242cd), +S(1691a90b,9cf72621,3680a72d,af59ef24,985448ff,5f35372a,ab6e2aef,df8b33e5,83d2032b,6ca761d1,286c1348,31250054,5a230558,cba391e6,f0db9c9f,97c2d618), +S(42a078dd,cf749f25,aec7a1f2,8e976f79,e268c36,a7b5b0cd,6aaee3e0,925746ea,c60f1846,6f025987,e0ea6ca0,3eb98df6,99389753,b57abbf4,f5739509,bfd09d1e), +S(21bfc1a7,16b09543,5142c31e,5038d4ea,4643d9c3,2fd4728d,1374dba8,1ca1f07a,a991d491,2601ca23,8e289e71,e9d743da,4385aed6,c732f808,6dc21406,74540acb), +S(e75223e8,fe247a98,5220cbfe,52151978,c98d1460,746f8de2,8696d235,71f3336b,dbbb8075,ab489582,52472982,c7b0c3bb,d82a0762,36c07c05,44417ff3,883f6f18), +S(7a756a,72fee88c,7a2758a5,3294da85,6b06129c,1978e217,2bc7952c,92b05265,c4ad3a0d,be72fd27,bfa475d0,fbd16a92,5e939362,bc0748c,c39ba9f0,3ad84133), +S(2bc697fa,d947fd62,63bb2841,1a274520,f5d582f6,7f039114,293438fc,9aad7c82,543109b9,82097efc,cb44d854,11fc5230,6c7a219f,2f58b37e,3dd354a8,2ffb28ad), +S(3862db95,1ed8994b,a8cc0178,f6dc97ef,db6e1380,c18719ab,cd24a88c,87039744,af1e8ac9,2b195d3,6fe1b1be,a68e9215,a453e638,67e65d88,64a8baf3,ff5c1267), +S(7b648cfa,40ce7274,59a6fae2,2836ea1d,b18ada4,9b18a61d,748127f7,7ceee771,f2a30c56,42887135,30124a69,da2c0355,8688bac3,dae6d95f,a252855,a85a9b4f), +S(f803d7c6,1820f980,ebe0b297,87aa688c,ddadefca,229b99b3,a6bde5e9,922f94d2,a5f8b450,34dc7de1,461dfb74,e7ed9c49,80407bf0,4b0d9159,af703a9e,1e0f9c61), +S(2247f630,9be2f3aa,e30c5907,c29095d4,b9198d25,1a17bb34,7cf5cb35,a2ecb1f1,82685964,20d87b07,1c5a4044,b79b069f,7fa6ecf2,cfc0f839,b2161475,5728c2f1), +S(1d770ace,ceb2183,fea7c924,a8fba197,579df37a,908992be,d42c8e80,8bd98ee1,d87077fa,b910a21,8769cfc7,49aad579,c809e35b,f1c2a813,6047e85,e2c6fc65), +S(5d134e5c,5da47f7b,a495f216,b4994d90,4a79057a,3d0076be,34f09013,99f1f366,fc4e10b2,c1f7bafb,d241d5b5,1c57d48c,30fd29e9,df5774c6,1d4f2c15,3b66ed03), +S(a49ed10e,aaab9323,3a5f485d,4bd18c06,28ab2629,f0b8c3db,4d05956d,6c953fa9,338d476b,99f0ac67,1fcb893f,5f202204,a5178acb,6971e7e4,984d42dc,b904afbd), +S(4d000b62,1adb87e1,c53261af,9db2e179,141ecae0,b331a187,aa4040a,ee752b08,95f2a470,e71f2daa,34927daa,7d268d33,34820a0e,e638d6c5,c18d7adf,b7cfcf45)}, +{S(44d42980,eb92b3e3,b16816f5,56858355,e031b88c,99117385,def6599e,f274e56f,6e3de5ba,4f693fdc,747caacd,b2cf0107,f29fa7c,9c6a1a09,a527cd16,4c2c1484), +S(9812d508,d6da303,6f08fa7b,54cae319,232f925d,6af91d71,fe6c2fb3,bc86be5,735081a0,35fc17f6,7547b9b8,f0f91ac7,f0ddb965,19f6f398,dea745a9,81441e2c), +S(5a022ba,a90ddc87,42246783,4f692ecf,dd2c2dcf,b054308f,4afeb17a,7ff6ddcf,6fc2a9a5,4c7a130d,fd26ca48,82406fb9,f67db0ab,7e6a6d95,1675fef,2e2d4256), +S(c181ffeb,a8ea7d69,c524cd42,18af3030,1cac8e97,69eaf6f6,ba73027d,30fb7bd6,e88959e5,b07b7d3e,693756a,f94590bf,997763b,afa7d850,a80dd9fb,88e6e904), +S(e29375b8,21c056c,30954b81,4f9ce795,c1493dfb,ebf06f6f,f32c92c8,c74839f1,a9040ad0,95c9d45b,1527c624,bac86841,a1f28b7,ccf8dd37,b7e950b4,858e6e18), +S(69445948,ebec5ae3,9fcb505d,7efb7902,6bc1e8fd,5d3024ac,2b772439,72142d80,5b6c2694,abac3938,db899396,62635c21,e64fe2aa,40d18678,9c246e9,fe3f4be6), +S(a86f3aad,85d0bbb3,8270956f,58c2fd65,f5425428,27d7a658,da1b111f,64c02c36,dec740b0,ee35208c,dbec5b7f,69bb94f0,7fccd075,7868a37,6ecec7b6,dcbe20e0), +S(ba2bf65f,aac92da5,6c252b50,8dd74e3e,99ace1bc,42b9d2db,9d0ead3d,b6820306,37bcd01a,75027b42,eb0d7b85,564bedf9,67153930,6fc97106,ab6623e9,90b031ba), +S(78419e61,4e8b5eb9,d09e0c09,79ce4eaa,26271696,fcc68269,4cddf1d9,6a4c7fa,a32dbb3b,46f9b6b4,9adf4dde,f1986af9,72662a6,2ed6b6a2,f8b920b6,82b32363), +S(71163271,204207c8,a84b5993,88beb505,640898ab,b24c4c10,42195dd8,15d77985,aab43609,9370f246,7035ddc2,e71d73f3,c0f1aa13,148303bd,840459da,ae307e8d), +S(473f52a1,fb7c3920,f09ba6e0,a3c891c3,3f4a13cd,9dbf51f0,f3d39ef2,d6bc07d0,b2c403d2,4aef71eb,1b834df2,8bc08b67,5130897f,a6d9855a,eb21ccf2,780444cc), +S(581e93b2,99b8c50e,e6338c0f,f426502a,d8699563,f17935dd,abe08a73,6932b71f,46e7c7c5,1e076fd8,9b8aa98c,f0b3933b,8ca82cc4,d21783a4,d74ae1b6,ca9896e6), +S(e65d9e24,e7340122,875a9d86,d6071a48,691ed5e5,a94b315c,76f169f7,f3d69c48,73575103,a70b7a59,90d9b880,8698b651,271c8c0c,ac93122a,66873bf1,fa3c6fab), +S(cceab216,41d0597,f9c7ab64,7f99154c,b22d4827,70395d6f,ddd6db5a,dc752825,4084c57d,5aafc328,e7a250d0,a7c3c0a9,627ddd70,c77c2be7,e5a1af34,359b9b86), +S(4222ee66,5baff421,17f47df5,c1c1df5a,dbafb790,d5c75bb7,2646cf85,7ae85923,43088d6c,a029903f,f5a789f8,16730345,5a5cd0af,cab57e15,12781396,fcc9b6da), +S(391c5d0b,17a8c11,3440cd30,83434841,969b0833,71758b30,27e38a25,e778da6a,69f3d295,be55f859,4bbb2fb4,116de5d0,b24b5a80,ed62c1e2,8fca9b8,3f6d7439), +S(803b7149,f87e86c0,13599898,b0c9050c,d4a6e030,e07e8d7c,a49c46a3,8a6ea6a1,6b367fcc,6d5916e,72f670a6,5ada9c39,54800e90,a8830fad,c9ea17c0,494cd9aa), +S(f7aa1c64,f8f92f20,1319f316,353d89bb,eaa4d65a,e148b01f,ab66aff6,c9deb179,e9b407d9,4c2b385a,74f22eba,93dc35e9,bebca7d5,17401b7b,576b7d2b,e13ff0c0), +S(f1c34d52,2b7493ec,cefd49da,99732e00,85ace2c9,fd14e9b0,69b67dcd,5acd5e23,29460fc7,dd77494e,a5a7764c,7ca6db5d,5d7a5e3f,65e865b9,e5f85d58,e77713e9), +S(db19a1f6,38ef7034,5340e3df,8d384944,7500640d,cdbc3be4,df3b191c,edb9db87,baac873,a76e0b91,c4847770,9dbe88e9,6195e9c4,ac4d7749,71ca204d,4a5e748b), +S(4ad12e00,f384c7f6,ea669c71,3649da6,55e0abec,67551a58,224b9f9d,de6bdaf9,2e0314a,8e3f32da,33a22ecf,ccac4965,1f1f6f5,20baeddf,39a02bd8,423cd40c), +S(c9130b88,cd451d4f,6eafffc8,357ba391,ae920c27,69e7c48b,49881070,fd088ad5,aac7c2f,4f2f8b90,bea9267,897e0079,7d6515e1,937fa4fb,1043111,6b5e1de), +S(88260ca,df81b391,be79bde7,b54f8a98,428aeacf,970cd843,d683e6e9,50686db9,3b9b187a,b998a22b,56b2933c,fcd09333,9c23889d,1180e365,10dc086,9869cf2c), +S(3984fce2,ab73c70f,a5538206,c2a61e60,2f160b1d,2eb61980,fe174cbe,fe464dac,b7e65c1a,46b50c72,621e3fc8,4f4c7cf3,6860567f,abf00743,5fd1b13f,7400326a), +S(e6b48495,c8277598,23b73f35,762c55fd,8cd20420,fe5be6ad,f27f12b0,68bdd63c,ccbb7124,3b0300fd,993dc9d3,4b55534a,12515243,e07ab78a,8d8fd528,46e5a69b), +S(6af5144f,342f492f,3f506ee7,fc515d3d,d3c93d02,27ba841d,74bc6548,97d4830e,b195d9e0,8113eeaf,fab92a2,81ddeaa0,95356053,a51cf84a,b5d60fec,5368f98), +S(14439b7f,e40ca407,374da12e,217edd9a,86c84397,e2fcf6fc,965023,ba984a1f,112ad656,47c47c30,f19ae62,e0c06b35,effd49bf,d5e604d,6d621471,9f088fee), +S(d4a7e4b3,863fece9,660baefc,ee57d09f,6f60cd25,f4aa178c,cb6f8a17,5fda9d4f,4985a973,ab0e8627,f6de7ef4,470de4c7,1adecaf9,f3766197,a86f4e40,14ea474c), +S(facc2a9b,9d3ad0e8,9fa479e0,ba777a0c,90a0c229,59d4b719,a9304df5,f1e96ff0,6e12c914,70c78733,3e5a65b9,8c01d342,827810aa,be3e4e33,cba0b7bf,eb283b91), +S(3d1b3f54,2809a7d7,79ca2416,a7b7d007,4a728599,e4f30cc9,14bbbba8,3daa091f,dccd0d01,e56c7e6b,90829ec1,f0fc733c,8f1cbcb8,79916982,3b466448,cc21674e), +S(a8c1ec56,3cf9596,d761e41a,bd6bc960,562eeaf5,b54eb83e,3d7e7259,f69671d5,a03c9fae,e160ba53,8aaf38cd,96ebe3a6,db59a04c,ce7074e7,dc3757f1,ccc244e9), +S(219b4f9c,ef6c6000,7659c79c,45b0533b,3cc9d916,ce29dbff,133b40ca,a2e96db8,db2639fa,26a61015,a5bbe7f,3fc8d591,c6b0753a,c16fa89a,d820fe57,72c49068)}, +{S(b445babc,55be50ee,bedb8c27,dfc6c37b,492649ef,8a65de02,111b9f74,29b8c3af,6f74c5b9,f9a022b1,8d3a706c,5c608b31,9f7ce25d,8424d893,4cbd2f90,84b78bcb), +S(dbed76fb,7e46e1d4,1b507edd,8994fefc,f94bd651,a9720d9f,8d62dc3e,9f329104,c047fc1a,41689ba6,9855f063,c563bfe8,49bd8b5d,37b3836b,d8bc57ca,fd02ef64), +S(844426e0,860ff2db,f012ed29,ad2919d6,f46eb102,68073862,c1792e99,f1b2b33c,243512c9,c6ca739f,fb181482,a901ecc6,a41893f1,371d6b12,31d5427b,fb486394), +S(1df697b5,9a2f9c91,b2c5d4a7,8035ea2a,351dc000,4e1c3bd5,5973020c,24836aa1,e89f949c,f6af652e,d7e79758,74a994ff,46475df5,4f7b17f2,a5c29cd9,7f722831), +S(cf4ffa86,87d9a39,7b4727ba,a033cd02,7d8a5377,46903db3,34c35f1d,855999a1,d1316b65,e027ed02,b4be11bc,4c21c1a8,585bb247,45cdfa67,5fb9cccc,faf0c546), +S(e3eed1d6,1813e3ec,d1b9fcfe,3e8bd700,bf3412ff,78a0a9b9,724d4dde,ddfd98b,dcb09803,b70d4c34,ee2bd7ae,6444724e,9ab27c20,5c3c736b,172af37f,cc673105), +S(3a202c1d,94a94eef,18fa396e,5cb8a867,8c3ae45a,e4ee64b0,822d207e,6c814fe7,147992e5,ab565bc2,ad1b1355,60f37dc6,b5cbee74,fef8e5ca,4e4f418f,811a0482), +S(6ce8f558,a853dacf,205db4a5,80c4cbab,8ef477a6,47bca6cc,9e676cb6,5f3f696a,e88e3934,b2857ec,6edb911f,acd132fa,f0078971,2805aa59,9afb3d80,942dc23c), +S(7fef9aa5,b2df37ac,99ca488a,ba598b4d,eb222149,55e1cb4d,c3ef1e7e,7bd38945,d4b7cb4a,8f9edfd2,3a0efd1,9882baf,98312b6f,7b5775ca,65e9dbf1,f2c32870), +S(6ec1ce91,d1eff7ed,9beba2b8,2f4a526f,736e717d,6442dd29,b55ae2e5,3371e0a2,28240ee,12e2b1ed,b40698e,ba6f087a,145eb772,caece880,b3e94b97,700dbd58), +S(645b9fba,2cb739ee,9b148b29,8fbe9884,f284893d,968c21d9,50696d2f,efbbf0bd,b06977a1,2d19836f,add50b7,b73399bc,698729fb,fd182026,d918a267,eab4b7c6), +S(7b930561,3a4a7cc5,ce1e7f5f,29314e1b,40bc8b93,40604f81,76578c18,69abf6c5,55a7e3e3,3f19805e,b5056d15,deaaa4f7,aea07a46,f9dc6a09,6ce15392,e146fb3c), +S(bbcc74ca,3697ef46,e7415ec6,b31a0808,1e6ade01,c7e2264c,f9d4d94d,d849ccaf,27bf64e,e1005f13,989639a0,8772c42c,b4249ba6,9d90cae,3f542ee1,78929f26), +S(4a45fd82,af96af67,9e640bbf,561cb385,2585a8e6,1b57df7a,e028ed33,a017c680,4ba76ea1,19a0267a,f019b3b9,89cde9ce,7b1c0b6e,da3c773d,ca31e44,61e048ed), +S(c3efb1ce,ddc6a749,a5db9daf,48032a6f,3f1a848b,aef9e44c,ebd76127,a9c90d0e,3de258cf,e593b489,8f92e8ac,50be894c,3636019b,a9f31f70,28468eac,687b8f89), +S(ec71a2a4,6f24acb2,8fda6bbd,ab9326e9,6590a96f,b9b9a4ef,405949a5,e3d2858d,31a91fa1,71207da3,1b0acc25,cace1e7c,5126ca2b,4c8d9733,4c50a7f5,29bc61d2), +S(65dc268e,cfd191fa,f07421f0,dcb16701,f979ad24,81ac5ddb,1f77d3ab,41a773e0,f8c9ac90,255d6460,15ddd1d3,57b92522,53bb55c7,e2f5d843,d7d9a025,2766d2c0), +S(62152eaf,df564900,4379891,4b53ddd7,3b91fb60,f16f2d18,941caa96,18d8ffd3,3a185ef0,a8d8a35d,112f678a,6a04a654,df565a8f,87e99abd,95fb4977,1b278ce1), +S(f68e6fcd,b2c49661,ae59bff1,f24e97e6,fffe7cfd,f185d0b8,133d58e3,bec1c89b,223dee27,5e9cc518,af12be21,ee7ef3a9,ab39c7d4,4f950fdd,9ce5c78e,b50514a2), +S(5d02e9c4,108860e8,2b1ae04a,4afbe1e2,9e0a8444,89876776,7e5b60d9,3fe5c753,25fdcc35,5f1970c1,43dae96c,a1ef0a7c,f95853e4,102548f3,d869d3d5,8bcf962d), +S(f6217e4d,4e6d06d1,27668a01,8225f88f,a1e0fcd8,e8654fd,ab26c8a5,4c6d4751,65a6acbc,19bb2835,13eea5d2,5fae521d,aeed63ca,50f9aed8,edb74dd9,c43d066b), +S(c18bb768,bae77077,35b4d3d0,e82cac24,fe3d7ee8,dcba20b6,a320ba6b,4695a406,b7a7ded3,a589153f,8ee504cf,fb5b45eb,b2d8e121,40f2f475,186153df,1fcf84d6), +S(36fc443c,1350b40b,881e41c4,d3c15952,31f8da9f,cdcac4be,f1d0c5da,ceb9844b,9843bb7b,fd0ebca1,762785cd,55669233,16c36aa2,d2c37b3f,2b196f78,bfa5830a), +S(ba7b8c09,d5293e6b,a2548e6f,ff3c939b,80d2ac54,34903039,dfb8b661,29e19cfc,54fe2a,9353fc1d,a0d34e07,b6d34d1f,f3a1f67d,f4aafa7e,57919142,95c2a5e3), +S(50a8ddf1,17a91c92,37a48fe8,f1a00e72,a5dff682,7398a4c5,d2f78b4d,d49ee339,f91d9b20,da28e51b,99fa102f,13f5e451,ab22e139,80052384,6b9c66bf,b7da1578), +S(642ece53,832d1859,c6543147,56b618f9,520a5888,2549277f,6b27f16,e08e3d1f,d9963beb,2c6da204,339a49c5,f253498c,48cc3e99,b715ea8f,a5eb00cb,1d6cffbb), +S(a68c0d3c,13d764d0,7b513390,5842c9,a7171e4f,755a9737,609db06e,1a44e5e0,a2c3fdd7,b7938a87,86f3756d,dbc66b29,72469e79,839a1448,b367db5a,2bc52c15), +S(5b33d730,288fde53,b123fdee,177758b1,ec113adf,74e79eac,513eadd4,cef377ac,1bbc10e,9d45cb00,db58fceb,bc4bcf4,a0e2bcd2,a7d3ec12,7d87beb7,a134a20b), +S(aa6dc4d3,ce531af3,e68590fc,acbe7816,c8293af0,d77914a3,c7119e23,e50ce56e,14386670,5dbe1ad6,91e09123,a19a199c,3ee67b25,708ef57,4620c5ca,821224f6), +S(180a4ece,d74ceaab,e0f7db3b,b038034e,5e659c61,3c66a534,8d962d14,efa32402,b675b2a8,b845281d,524403d0,db01417a,59fd3a6c,80232e74,cdfc87f1,3b76b0e1), +S(3a55690d,abb5e00d,c2d0d8a4,96d16c44,76ee767c,a9d0d1d3,694c856e,e5b7ad0d,3c1d71e6,8a5f9a84,4de0453,6810660c,fe353475,353cede7,2936786e,4d173828), +S(33b35baa,195e729d,c350f319,996950df,3bc15b8d,3d0389e7,77d2808b,f13f0351,5a75fe7a,9bf54078,6b9bfc9,db72ad43,559a9f10,4377648f,d43afc32,34728817)}, +{S(52f79560,fdfadd45,6165a204,6bf61bbb,726cd726,3a3ed2d2,45f07631,5b0b6ae8,e6c46e55,5b4a0410,bf38965e,5a7ba381,fd259482,2280482f,877a7ec5,84d14012), +S(29eeb07c,7b27f82a,1b031881,11d324a9,4df77713,a8cadb36,2ce4012f,52cafb19,b7538574,28faf258,f240d4a9,f464797c,dffd0a68,f9f978fc,476f9bf0,1a0a2df5), +S(2191ebbd,a95c29f8,e7062f21,a70cb368,ac67ed59,dc44b167,66e52120,cfe8e742,2812f1fe,557496f8,f21b1f11,2df7bd19,db6aad90,59494ad4,eaed4d77,49f66a4c), +S(3c29d849,e25364d7,576779ec,78755ff1,8aa64e2a,65140a2,bbcf22e3,da33a4f3,3658d9ff,e2e283c,f8973cc0,ede5af6b,69ac4970,3c6b1370,f6a2ba4a,78a28a97), +S(37230e16,e11db299,f54ca0f9,f5423ac6,5f05840a,e166175a,ff0732be,c72ba88d,2a26ac2c,e8f9ebdd,20f1f5f0,f628d3d,d0e02745,6132cbf8,78b614b4,51db28f2), +S(8fbb3be7,98e16bfd,b738a512,ceeea92c,1be8eeb8,5374702b,e927c614,ab1f1baa,8280f387,64a48061,26392be0,c95f0c58,3607343f,d0b24b98,afe3605b,8f7e79f4), +S(fb639ee6,29dd1f03,8e12330c,bf40ec7e,129d7da7,655c1c21,86452fe4,9589c89e,171adbae,2efe213c,8d01e273,ed9c5d47,c4e85503,de3849b4,d909823a,bb1da898), +S(2399064b,876b7584,8c977bf8,e35d6b32,2e45d1a0,c4243fe1,2a31a581,d1d5d826,79c1ef29,c1af9497,f3b3db13,17432d42,86fa014e,17e5d780,a0d39df,b61369c1), +S(dd5663da,63acee49,eb7fde29,6ac1215a,772512ee,6a080f3b,1266c7ef,51da5dd2,d19c7348,3754112e,9cb3a4ce,8986f748,3eba26ab,c415abdc,5effc956,27efb7dc), +S(30862c58,43b161ba,c127bea6,7b23d3cc,46b6c42f,8faaa281,6ae47092,8ae47993,44ea1101,26a7c448,dfed4995,46a0fc8a,3658e540,d7d0c2b4,18f8cba0,accf9537), +S(df7ebbdd,4e416e4d,6b1e67aa,34915da2,cc15bf8f,f6d79ebf,273b63a8,32d6610a,d05fddd7,e9618753,21f473b1,bfb5ff48,65b3c4f7,5cf4ae26,549989a9,e13056d8), +S(13adba8d,2485bda5,1ac7994b,c45a7e4a,1b21aa9e,8188e631,59b217a,29d4237c,6256a795,3f24b25a,c1f59384,aac3eabb,4458e504,af3b99c1,99ae614f,8def4ff4), +S(c160f7ac,9a453a1f,8811d031,9849be43,22d8bbe4,89cad7a9,cb815927,d4b54720,7958b41c,751ef3a1,fb985ea9,c7cc85f6,c16282a4,c374fed,5999d8d6,3227b720), +S(c66af931,82a7a9d6,588e9c7b,3eda3b49,40fdcb99,346aa55e,e6549d4f,f9382228,ffe4d0df,43f00af4,fe885b10,92ae38fb,97d21a8d,26aa5f69,9751d52c,a9ccea4b), +S(d3a4f4d7,d208b43e,c4c2fd00,798d882d,922f302d,a199b707,76a70f36,5ff404aa,8df220d9,63faa827,1c4ee360,67a536ab,25813239,82252483,311616d0,1458e1b), +S(47bdd428,d57ed451,cd8754ea,817c5f5f,9658274c,ef6757b8,70527b4e,96f372f9,fbcf0ee,9fb14de6,3c288dbd,b1888fcf,72f0265d,3fe2209e,12c85841,2c375b53), +S(6e6deb95,7f2eea30,5d625288,3cb311c7,f0365a42,480fb807,36315c4c,62495438,ba0fdd63,9b8678f2,ab0fe3e9,a16512e,ffd9ec23,e691cd4f,86146969,534978aa), +S(dcd2775,7cd691e8,28e21987,d6eb6b54,e8def24d,27238cd5,dfb36a3d,d0f05caf,40bf37e7,bd776a62,ab420209,c71df7df,74d185c,f0d6b038,dea9c8e0,c1dad163), +S(eda569f9,9bccdc21,580da94f,92d89312,cef87d13,f08d8ad5,f1574e12,30a64ce6,4e1da1ac,dd274e3,f9d7def7,746f0652,678e549,ad5476d1,410adbbf,aa3c1e42), +S(5d3d4a29,734fb7ad,2846ae12,4c803364,ea3d57bf,e8d77d9e,6a3b3f8c,9cff26b6,d3e598a7,1a469323,a357df08,319dcd1a,d7337a32,c9894b80,6a30c575,b19756b0), +S(10161fa4,61e87595,f269da7a,ae3ccf01,40e910fa,891276c2,df1bd91a,95e4d046,b36eb5f4,b8291a01,18691f1e,50235d8b,f66e14a7,f0641dd6,18ad7ddd,ea150196), +S(7ba1b276,3df207f3,cfd1d58d,5190eb91,99cf82f4,9d2d0927,f5f260f7,2ea0f178,cb9fcac,4be96ef5,3134f513,5509c178,99af24c6,4eabd650,afc599d8,3927969d), +S(4bd6edd3,aaa1802a,b0f46e05,418ada37,e75728e8,6ea4b293,6619c92f,57674dc3,c9e962d3,de71eafc,7fc9a329,3bfd721f,305e876e,a2a4755a,f9567dca,21aa3c0b), +S(95fa0b32,40b24ba,29a484d7,f8f022eb,b3f30349,9d8abc1,9a69b690,3e885b68,dfe6b88e,56d7b923,dcf8bac6,2241d663,3ac2ae55,c3a01fcb,9f966d62,8f3acadd), +S(d0fd4d10,18c37114,2d5f8a86,617c250e,b6c3d5,210ca465,6ec84e87,d0ae42b3,4e3bf443,290404a9,3af38e72,a54fa06c,3b2d6a81,161fc8de,554edd9b,f3399587), +S(992e1183,d4e7afde,166c45e1,37c71c88,d4039709,4262163,b4ae38e,538293f1,c29534fb,55e47a08,ecee311c,8f0ba4c0,39096edf,b2d7ed63,21a2810,32e85900), +S(9c124ecd,4e6ce167,f60dcf38,25976f33,64cc30ff,61f7dde,966f8c23,4c13a4d9,7ad73bdb,e0afe3c2,c89d9135,d445ba4c,796066eb,1909349,9e6ec14a,f74f664c), +S(b09cdc9b,d102f89f,bcca0ed5,888519b,7c5c02b1,64ccbfa0,3d0fe42,b1b113e4,f409865f,6ab4371d,f52790eb,88df67d,84b3b74,f17edbc1,29b802cb,d5e49a59), +S(6b4f628e,beb6d619,1d616e87,3233ae37,ca4cc3f2,ac881e59,9c2c8e3d,295ccdc0,1140b988,60f4ac7b,659eb012,6fdfb796,c0baa652,2202cce0,bf54b3a4,865f171b), +S(a728c830,10169439,3af3f780,b1ceae3a,6ca08ea,4bdc7554,2ae84564,f542c28b,7b68fde9,8c97db32,eb3cdbd7,15816e51,9aa58458,259dc74f,5d722dc,f48f1933), +S(21ce4401,4ce959de,f16b3912,e88ff48b,d44ef79f,5fed2d35,bdd2dc78,de8f7605,cdc598fd,c5d30508,a2690299,e0e25b91,b1ccb69,97b99fd3,eca1baed,76aae6d0), +S(89912259,11b9132d,28f5c6bc,763ceab7,d18c3706,e8bd1d7,ed44db75,60788c1e,2574b267,83365364,d84789ca,a64ec905,c969637b,21061ee,9ca3bddc,7170ed3e)}, +{S(5914f66e,28c1a0a3,47f461d8,1d7f2c55,845e35eb,3dc39945,584b5efa,8d71a215,cd2ac885,3f681e4d,884e308e,ed3ca185,b47e2d63,3fdfff6e,efb6dc67,4d7de056), +S(519834c0,d16c807a,7867b8fe,c99dc93c,3b9ed218,87a9ca17,9343729d,3ba9e294,b867d57e,fe2dd7c7,8586303,152f02fd,f67b5648,78a7727a,190d304e,64f97203), +S(959eda70,cbabcb05,fa8fae96,7227b6be,876db942,ae38bfea,a9c3d359,977e1c52,86373396,9338053b,b00d206c,5d67abd,f95d35e6,992ad69d,8d8a3322,7370fdc), +S(a40279c8,48dc042b,a3ee929c,2910e326,eded6051,9d9eb1a4,98fdd7e1,fc88a6bd,f2c962a9,f2950d2c,acbfcf29,35bad39e,2709c0e7,36378968,fa04d957,1dadc4ee), +S(650b8181,edcfe46c,eea03ec9,f8ee5af2,c288e618,7d634961,6c95554e,25957334,ce140ddc,c2e2b07,a57e5627,62da3eb5,ca1acba5,463e97,fff7eae3,55b954c0), +S(eb04688d,5f4c8d2e,4ccd509a,3d003b30,8c5539b2,dd160a45,cbe71efe,36fd9005,37d80690,2062bde0,b96fc8ba,7a7f751f,9461f774,a1d6f3e8,8999c676,a2ae76b), +S(f79206b6,7813cd64,9f946313,3bac4aca,3762975e,954e0ef5,a876b78e,4b5a5d24,6033d4ee,936e956a,af117dca,6086eb1f,6466ab18,d503503d,4b011315,365242d8), +S(539c99d7,728cf662,13e024c0,9f1226f5,ca5d09cf,67bac12d,47814b2b,b1e7d611,87efd14e,e58fbff,5491bf85,aafd48ae,6680f37,e0d928f0,13fb6cec,6f417f76), +S(2308281d,e761f602,66fb8228,45eec88b,e32f8fdd,4a06b8b9,3dfad9c3,75d64a8e,933f4071,e4f2a9ae,95f4a299,f4267098,94b661de,c96443c5,f03f6cb,f3be87ed), +S(a93d05ae,fa31c0c1,54c02f68,9184c1ff,344c670b,e13eb304,2de517f2,1f6124ed,b7549c5,4651fa60,61f4103e,27d316a5,db531e3e,d7396a46,e360341e,f53ed274), +S(35717dcb,4feada90,b039aa79,10cf064a,c8c998b5,fcbb037c,22bf9e92,eba560d8,b6ff9371,d98bf532,f82d7fdb,24fffa69,2afeb0e6,59ba30da,602b3399,2bbe5a5), +S(941d37ef,1671d5f5,675be9d0,d5cb1f0a,ab6dffdd,2bd342c5,c5b62be9,88d5a0aa,4834d5ee,a8f9ae8e,ba5cc8b,2fb52260,33e35767,d55b6aa6,d204e0e1,a38b42ed), +S(c22cecb,aff78c61,f6e34c9d,9367c4f4,1acc9c35,29e9d894,7989a874,1d70bccf,38a37fb0,5fd9036e,762e2f0c,fef8fcbd,c04a92a5,4cb72ee2,ba08213c,4b58f1e1), +S(7863ac2f,1704fd,5015f0ed,30edf5d4,33c5ea2c,e150833b,a15c1db8,a5e4d2f4,7468fa15,eeab5653,c9f982b4,ad9e4c99,2f44083f,a7a93e9a,25ff70e5,b8742b4d), +S(f702cfa6,f0cfd275,a2c7af50,b53d0c94,bb965063,c433f4f2,cfd13d99,800da48d,3da4a4e1,bc5f2c6d,2f8daec4,599a9704,7c1b930a,b37d6b2c,f8a586cd,ef7dce1), +S(c3a1da3c,6e6b8f2c,2a8eb81a,1875f2b9,1e9cd286,414862d6,57626b47,a413d7dd,983d0892,5983d42e,bdbce2d8,df116bc2,f5cf8c87,aeb833da,e5e7e9ca,f9699798), +S(df0a4106,d61fc3c6,b2a14ecf,ebea3dcf,7e38069b,8faac576,a0b8a1b,7c8e7b5e,dd97a06b,840d64db,1798cee4,9334b033,e8d3f4a,79da565d,c6130da,f0bea75b), +S(ee9901fd,9312a21c,879d4e1b,24d5a299,b8200a15,6e65e147,4a2055c8,a2363423,1bb2933c,8b10f8e8,ab93ecee,16f861ff,a86781ea,59b891b7,740fdc2d,f6c206d), +S(52890b99,c8dc48c4,b6b1e4ae,5b823fca,ad9495b9,8217fa4a,b4f8e960,77cdaf9,6ca2ba73,72606e82,9c3395fb,dd2e4b74,8c771231,e86fade,5e2d728f,e5e7391e), +S(a372b545,bf56b58,b7c4628d,aca9bc14,e8bc634d,d79d139d,8eb7a2d4,13465c3e,afde23df,fad41c68,df94abdf,90a642b,31d62f84,c6254c4b,4cad2934,376372f2), +S(89d02f41,f88b1f7c,90b01078,d7599a70,a1eb9c23,1059e856,4237b0a3,2c18010a,7e8e810b,b063d75b,c224f41,a842972d,6fe065c2,aa6b16e5,7bf224fe,e44b2275), +S(7ef367d7,23fedacf,86425861,f67c5ba2,2deaf04,69e31988,7a3a3a12,2f39576a,4b81e743,e00bfbc,1d98cbc2,5c98c516,55e574,fc7fa1f5,86f15d9b,1b5b7ac1), +S(2eec37a1,5fdd1caf,b4c0855f,e86c5534,1a78cdc,b3944798,311b1cf4,7df768ac,1c18cf5c,1a98fe9,70d1e635,ab133668,4e0d964b,4df0b16d,7c51ea01,c9a63), +S(72208b8c,fd9840d,76dfa6e0,f36cf57e,502f285b,422f0ba1,9b119500,6b2ddd11,fa09196e,a89ce6a,888415ec,a8eb92f4,149bb39a,50174a85,8a29bf63,65b4e577), +S(9f46479a,69411d57,c3c7ea6a,dfa833f9,1fb2109a,fd30c790,2ce323ae,4b14be0c,6cd6d7e0,8494cb95,9e67c258,1be426f6,4eee4514,83e9a9a1,278b073d,758487c3), +S(a9aaf56b,5016db58,5b8116dd,cbad1169,4b16de8d,9db5ea5a,279ccf4d,91b1d7a,218fed41,389a4abc,44fb2a83,7016eb50,99c40e86,bb419265,7f57714e,194a5900), +S(5f950f20,b610c06b,76949dab,52fc6149,97d254be,a1330a0,493f1ea2,1d608864,d9098481,823b3ff9,d1c0b7d0,bce90856,186b45ec,6f20da26,9b158283,8a4c96df), +S(ed621f77,98add722,b0dc5e52,9c6fec6b,dff60827,b0b12c85,18d798dc,761f1075,a8973e79,a9caf1fc,e3165145,df08b7db,6b7187a5,28b12712,6c62bb5d,4f0c46d7), +S(15b8390d,652d7338,e18ee091,97e0e176,74f8c4ba,fa2e7b85,8f5badc9,9c89240f,87930df3,710172f7,c5422833,385a6066,4cfc9854,a3e5ccca,d1d06106,1cd90be5), +S(ac2acb9b,21999a70,540708ab,68338266,aef650ee,d81c5b30,da1e87d8,a8a923b7,897bbd7a,ee3e8db2,e36505f2,ec2614c,9f4f2f40,ed2d85b0,5d23edb4,2832db89), +S(17c072d5,6bdd1382,a782481b,8aa4d223,2db79438,5870bcad,c3063330,a5cd5379,26fe420b,d7c25f9b,1883edb8,50e2fcb0,76a65389,d9a452f2,8351fad,4ef72f0a), +S(8d262002,50cebdae,120ef31b,4c80cd5,d4cddc8,eadbcf29,fc696d32,c0ade462,1412c44b,8ea40bc8,2ce090d2,3c11c945,e2b504b1,8d9874c5,271f5745,f0d9b523)}, +{S(ad9144f1,bdcc3673,26d51685,a047ea9c,3c4feb3c,da4b9b83,80e1ada6,300ba487,68003744,ab5b2c8a,cca5bcfd,e1c4262e,88ffbc0f,b0ad4206,2d57dcf8,62c93c47), +S(f3b90a0d,7a8cbf1d,9b994b19,7ce215f9,b8ed861d,d526228,59fe1811,68727bab,95f48e8b,c05a7bf1,e47fdc86,87513ae,5a56472a,44f384f6,1153a954,2db68b81), +S(24af6d7a,99122466,45b07a1d,29b4bb1,a68b5b0e,f9422065,b5bb0050,61d43cdb,f9dc7725,f628cc4e,d4e5554e,f0166b22,566d59e0,4d55c28,a4d0e975,8306c31a), +S(f704aa4c,2bf19c9b,128311c7,1bab5f8a,f2a3865b,728f4838,b8ccc7d7,82574cdf,ae7c46ce,c44d54a7,2e3f758a,e855710,dee6b189,1223d120,9e059620,38d12453), +S(f701adf5,a1bd988d,d7e1fadd,9903c453,e6798760,252579c8,b90cdce2,a046d9a7,db39a987,48aa20d0,5d8b8538,fc4b7d77,95e2810b,160198c7,ad98305,96407c31), +S(bfe36081,c492ed7e,3b91059d,bb1af376,47dca509,69be6665,50f94903,d948ca7c,a1fc9468,2c3dc954,764ee858,822b8c1a,5d0184e0,93f12bbe,21e9931,18911f03), +S(e707057a,297a3d7d,904a615d,ac1b8e81,78f52481,32fa166e,afb621e3,af3f4f34,9161a930,bfd583c0,bfd3b121,acf574a3,6eb534ab,5198aa84,ef38253d,9ca19bc1), +S(27a6c7a7,970a9e3a,7cd42b6b,4478be62,31f53ff8,481791c,4394e78a,968fdddb,e4bf1b16,6bd56269,9e2c341b,b1a51c8a,c2ac7d28,807899e1,3ab1f726,90f44108), +S(e65f799a,8fb3b6c,ad088351,614db5c3,d64660ef,8c977ec1,2bbe58fa,a53314d6,5897f680,253a948e,fc260fa2,8ac39377,4e6a445d,d2bde00e,4e01ff03,b4398e2), +S(8406a84c,e9e63204,761dd406,9651f20d,6fc3efe3,f98951d0,d0a2db9b,cf86e0a4,ef81aeb3,eac17e5,676f4455,b4372ccc,e405b786,f11a871b,d781e54c,6307d1f2), +S(c012cad9,4742fd49,8daaedfe,5b847f34,7a45c792,53c8a645,2192e905,e256c7a1,c4866f98,48f31660,8a81da9f,da112a71,34232f9c,75e388,7ff987b6,8404cd42), +S(73795313,e9de1efe,3d91ae5,15c620c4,4f9c6bb3,a21e3097,c8794710,b2032683,548ce754,93817f22,cb96c674,5d67708d,c7b9b133,c71cba5f,fede1316,c55245f7), +S(352b89f4,78a47e86,b5575fbe,ee1373bc,e93ca316,1f594564,9efaf048,a60bfebe,d784e6cd,88d7862a,d82747d9,b23340cd,b50235c7,65f30be2,ab5d313b,edb0624d), +S(8a0b3d0a,80d41abc,4b8a028c,6c523923,7b040445,525dc345,d2d8a319,c4708316,7b7a427a,f0e1f902,dd1b9bb4,65b6b376,ee68b9a3,f495cffa,c52fcb9c,705443e9), +S(b4bf6756,61462829,53f9a44a,ee3b5427,2485b45,951013a2,8d59346d,62f65894,85d0c26d,2987dbd9,e26c9673,71ca86af,3b8cfb4f,cbf97404,c99ef900,5379a1c6), +S(bff69744,3ac23a6e,574bd1ac,d0fea305,2f05ea65,14814f1c,65e86e9d,5b46a2a5,36961462,5b998565,9324f5f,c8714d9d,57ff314b,5d5c2cc6,99a4ab39,69add4f4), +S(ecb09f5d,122adf91,e68e98ea,d39e5790,27e7c4ca,405bcaf2,e086d4a3,e68242ed,aa214cca,8a601fa0,a92633cd,a353ca02,7a7dee71,cf335af8,fc75de56,a3405f60), +S(310d957f,10fb34ef,ca3a2a0b,35c901f5,35c0862c,68310134,10982d7d,e74af8ae,289cda3f,29a489a8,86121f4b,4e75a90d,2bc4ff20,e95aa103,4044ea80,cff56378), +S(3a5f1ad7,c59ee6cf,e090f210,7040b419,4ca15fa7,dc8e9f69,20825fc4,e012ac2e,8ac8986b,48d7bd1f,5ae99535,2505b0ad,78f855e,d8411b03,15725571,dc99c920), +S(725f136d,1b426894,c24f8782,43fdc1d3,79636a23,d682f389,dbaf71de,d4672067,db0ffaac,ec5ffadd,f32396be,3f01fe2f,45bb0cbe,eb0d98bc,2390f3ca,c3dfec84), +S(b28e523e,6fc68192,445180d9,5da3e411,2fe0db1a,5b3bd5e8,e00a1090,39c69d53,7c172a4b,31339fb3,47db74a6,beedf59d,c24e6aea,c71e2a0c,434714b4,d61a2f70), +S(b808db0c,e34b5607,8166b308,1dd4b2ca,cf9d2d51,3079648f,fc3c617b,8e45ba09,d69c4ab2,fc7e490f,6887f2c2,49e0b71a,746e3e2b,97a7415f,34475bd6,f6dee79), +S(5e0b091f,3144d7bd,c7e40dbc,98c716cf,d7e64eda,ee9c1fa4,6838bcd9,e0c6878f,1586cf7c,da66b005,8b2d0d49,f588d444,65eecf42,1d94c004,a078dbd,47e0bd20), +S(1c3e88b3,ba46410d,8e08f9fd,5db8b53f,8dff33c6,4f5314f5,e005442d,10cef1f8,ba365020,5f85aa9f,71a42c0,392345b7,4a17a5ab,9ff91109,4d81252c,d8c8f72f), +S(b4926018,fb0e9c,e80c5614,37c3f22c,cc223412,384321e3,6f408e4c,ec0d81ac,55544f6e,cea3575d,e81649b8,4950f225,8a94e78f,13325ffb,16debe1a,8387173d), +S(f4bfac1c,b0834e1a,e332b7b7,1ab84500,b1c8460d,c8c2efad,6a5efbb8,ae78cd1d,5faab691,59ce9d3f,6bdb1818,676684b1,cc2309af,1401b5d5,137e1c61,5fbe287), +S(220ed3d1,fe028d87,b96e8ad9,37ede03e,ed8f262,ce877c0a,42718ab9,cb2ddfb6,5ee2d60d,f96a1b8b,e11c9139,7abf9b4f,a0671966,34bf37a2,8d26864,31933fb6), +S(dca5fad2,d045526,b6a4c309,de1d95b5,23f9a7db,e669b558,edd75ca9,40c85878,f350337e,877c9c61,98fc912b,5d53876c,78ea94cb,bbf3744b,62a52fa5,f5ae4399), +S(486ba509,3272eb1d,16ece443,861eae7b,2d8604f3,37014bc5,11267485,9872642,688d4585,9fa732c4,f582d65c,45930567,8c27dc6e,5525d47c,3908daff,f0a534fe), +S(96bc1bd3,b4840354,14aa92eb,6db81f5,403e2858,c0552d5a,6b21d33e,c468f31a,83027873,f4066b5e,ba6f413d,6a7d4d18,d57f4a62,25cc9289,b514f153,91ede3b9), +S(9f131ffa,b53e20b4,e9cd9e33,793f4614,922c03df,7d11222c,7fffec33,42fddec6,c4deffeb,3f4cd90b,102cb6b,3f713728,2c5cdcab,bc6e7153,91ea890d,76207bdd), +S(c15c8c23,d90c8e35,c1a214dd,e2d4383c,735ae45,bef61f10,aa1a1c25,5984cf74,d456ab27,d7adddca,372390ba,1da02845,b84088d2,af4fea5d,3b5b7326,c6334c2f)}, +{S(e24b71ae,8fdea24b,2d42a2c6,5cb022a8,2aa9e03a,3067a889,e778caf8,34903fa,f87d5757,cae83263,1233b840,296d6066,52cd1ddc,e8334629,25c992db,7e0ee14a), +S(886a7626,6203c559,807acbf6,7e1631ae,1f6bcacf,3fde277b,ddcd795b,292c38f6,b1f590a1,52d3ecc3,2b699c23,a89b2601,de78be2d,fa11b20a,1ef1235d,93ed9e99), +S(3079f709,3d5270f2,95fee75e,a6c5cd3a,5638ee37,aef535b6,523604d2,eed0d57e,492dd2cf,d76705d7,42187f38,f38982d1,85efa30a,fd1639cb,984a8772,3e912a91), +S(7773469e,c557e3e9,4097bd55,33a87583,a79e3eb4,2e421cd6,592363c2,76641966,f827b0dd,46f4b5fb,4a656731,d827cf21,df1ec610,f36dc2d1,215f33fa,20a08020), +S(ebeec367,eb032a6f,7c421ae6,f8fb56f9,3fcdaa68,f6741e46,1992d019,7567336d,6e78af43,e10c742c,35467149,3323f8c4,a6cff91e,7b1f1c2c,65b21fa1,e63f3a6f), +S(b41658a,3683b512,c751d817,f5d92f8e,5d158632,1f2eb836,b446aebf,d276030,9396ba60,ca61b35e,4e52e879,a457ce5b,fee9121a,b2673c19,15e78236,483e069a), +S(2e2ed965,a926700a,1428af7b,68b7dbf7,61d1bf53,6eb9f75e,d56afa0b,63852cd6,8d77656e,d5f11224,3a94e462,3dcfbbdc,61749930,dfcca6cd,c4303cc4,617e9133), +S(4b54bd22,e5604384,9ff2268e,88c695d6,4766e0c8,4f598f5b,634e1dac,d03063ba,176c68e4,a1f25683,37c14fee,dcf9a64b,17cba4f7,283428c,1514990c,98d29d71), +S(b6cf7c38,2d4c63cf,78557116,55b5de99,1fd32da6,8b8d2dd4,cfcd6f5c,59c45251,c4c190ac,2870142f,2a13d7bd,5d6768b8,fb5ccdfa,b99d0e3f,6bda805f,7741ff52), +S(857b6db4,79177eca,82a6c801,bd763647,c1a49021,e5b4f405,ad02e6ec,7ccc3629,586cde8e,cbf7907f,704a6610,f7a8a910,16599e9a,93bc6a6b,1b70d59e,39c0db4b), +S(a18a23b7,6643d92a,9e1bd2c7,7bfee61d,c5171b33,12f64d7f,821fbe05,6a771ee2,321f460b,b9525303,c159fe3a,6762dd97,60432307,919a2f1d,6120c946,47cd8568), +S(19b91028,cc2e5434,b162cee8,c77351d9,d38d9eb6,8ff9b09b,f26cb25f,4b88510e,cbcf2e52,3335ee5c,56ee2a06,7c565627,a5c60f16,808ea8e9,94250c15,e427c883), +S(657e2f30,b1e0241,82c0e57d,996cccf4,7bdf8eec,5a4da875,1638b3f4,9bcdbd2e,8e0a3117,12da36b2,2d0cd048,a9a99b26,5faadec2,14162083,5210f1bc,bb79fe3), +S(576879e4,f4a8e697,45014bee,36ba4267,cf72038a,f5997d38,cf512f08,f0ccf6b0,3f313f,388c011c,18ba0938,51b0e7e,84627277,2e9da999,b839b58,92ede5c0), +S(ed72d681,6516c2f2,7a9e97c7,1a7c99f2,faeb8e31,a5f29260,faa0bbf8,6475117f,331fb25b,e71c24fe,f4d2133b,f1591f3f,e5f8830b,15615134,36fc47fc,522ad4b), +S(e4ed422d,9152f00b,96bce64b,bc3ddaee,6089e1a4,1ffbb58a,109c688e,ba44699f,6dae6420,2dc6cec2,e4a49203,6b16c9f5,9c517436,bf3b9d9b,7e8f458f,7fcba8e9), +S(fc3b8afc,fe39404c,29622db5,b06c989a,8d7a0c4d,85018796,d006bd5e,43dec087,7b77feef,2211c1a5,724af5e9,a841e09c,d7940bc5,43d70c7e,51676689,7682ef15), +S(9c2421ee,e2e7e66b,decbd152,a8839f1e,4cf03808,649399bd,ba55cfbd,9dda6360,8d9477c1,c66a532b,967895ae,66c7585e,608e9653,4f870bd2,c97d7398,74a84700), +S(22040517,334bb5cd,2252d1b9,8a9da91f,a0bd32b5,562c8218,4d991e01,d94e8f13,167d3dd5,738a467e,f8e03064,d6482c2d,64503892,b4f5b9d3,c4b04684,fe2f15c0), +S(b648a617,3c728243,f3c0c7ec,2aadcb13,dfab92fd,77da9aca,1602d47a,80022dd4,7199a3cf,89c596e0,d607d05e,de95c8db,ad763ac0,76fa7ee8,ba7ae6e1,4c14308a), +S(a7d7ab3,6722b9d7,940b2a15,c94bb9fb,41a139d4,26215286,bfff84ec,57386dae,25ea853a,c3e772b6,f400d850,a7cf5760,ed568add,a145ea75,6f20b77a,c32d9f28), +S(9787a21d,ea3dab74,366214b4,933a702c,7a07c4ab,696eddb7,3a84cb5f,80f2ba41,8663b959,2214a018,e373e360,a9e035be,f68d9e01,f6fff1ab,f8ad9b8d,3f6f324), +S(1d2ebbd0,65bd4f50,8dd697db,9c8bafff,b548edef,856fa4e,39f893cd,349ff205,85d0716b,109ab208,a96367e3,bd0a2314,ad2c8586,169096ef,9521d780,61df01a4), +S(d0a506ce,14e88414,46c08a61,98ea0aaf,75b9dfc2,c4d2cc58,1238a597,154981b4,dc8c863c,7c4611a1,85744975,34c8c02d,7cb7504b,dffa91f5,96765a25,98d1f506), +S(b6c4d103,a57d0af,27e93627,f55a34b7,75b2ef48,47d7612a,5fac0748,b41afee9,dc9440a1,596cbfd9,ff87fade,8b426091,4799dad1,ce0f7b60,d032e94b,bbaf53f9), +S(5b5d965c,f2f37462,86f088e8,b58887ab,2182f874,484686dd,43a245e3,dcbc55c7,6718ba28,cf43ba30,eeba6e49,a7234ec8,7f01762,e915ff12,41fb461b,6d87a875), +S(bc85aec9,55e6e99a,c5fa7738,c7972354,57b3935b,bb69e8e1,ddc69d01,d34a47f5,59dd49e8,cb3100a0,9c48ceaa,20cbde82,5072e13a,7af0a44e,601044a,361dd971), +S(66b7ffeb,a34d8cbc,e00b4d1b,a0558741,5123d8b2,89ebdba0,47587d66,b260bc99,43bafb96,c88a5917,c6333dd9,b4a546b5,1c37ab48,3f3873a3,6932ab97,efd79712), +S(3cf9208a,b230b73d,68470411,bf3d4899,e0f19675,f829e32f,cd148d,3c07d7c8,21fc39b3,ea5079a2,ae131935,9a3e98df,62b97b37,2a473a63,f3b56e24,25b30e27), +S(66e3fcef,b7b24cf7,af0f8de2,8e53c2df,2bd1dfb2,c0289b10,31239da0,948e129e,df4074b0,488f3f09,a674a40c,b3207f34,a0282661,2e5ac4ae,8d443c9e,d1184045), +S(28df781d,4ec05680,590f4658,713c8a91,fef23763,87ddb6dd,674a35c8,b0e74459,eb66159,95ecf0e8,34f24e87,6f0dd86b,5c86a45a,fe5190f5,721f833e,10a196c0), +S(85d8da47,48ad1a73,dec8409b,e84f1a13,16e65c51,96aad27e,766746f,3d477c2d,a76b74ac,99a3996f,a794ac9a,ce103843,6b4f5fdf,cc3b2a59,df867e8f,382e1ebf)}, +{S(39ca1e6b,9fd848fb,ac24c444,5a9f398b,8639fa6f,c0c2f2b0,8058d84e,6c0caf1a,8797227c,f956ba7e,5228a452,91cdd51f,8958ab7f,43f6c8b5,e06176b0,dc9048e4), +S(4c87e538,c9eac9ba,d5a2a81e,43a6f1e5,f8403c01,68ad5020,4adef77d,5e3aecde,6ee8b74d,dd0b9bb7,c8b7ed7a,728dd99d,a9fab941,f0132d0e,998e65b9,ffbcb9a), +S(1226617,9f5dabe4,6d963b7,e96247e1,e29f2440,f831c8be,578d628a,78561987,2d3552bf,ddb5ec6e,a7758830,9badce7b,41d71926,857962d3,c3bb28cd,5d68ce7f), +S(866c0a90,893b6b3d,807a96fe,3c67b928,67c1d95,e05146fb,40038476,26981201,8d8f3188,da86e510,b7a7642a,97778201,3926dae6,4ce80663,335bc448,d204413d), +S(8eeb2e94,9a66824,26a6f474,9e7578d3,1b356aa8,d1b42049,5fb83e5f,5d08f723,e439d710,c45063c6,d23aad9e,ac0cae8e,36552ab,ca6f2c02,6eeb03ff,6162a052), +S(ffc93fcd,85fd9502,78369b3d,291f1ffd,5da44964,888d5eba,3c3f60c,95082bfa,9aa66635,a4b0defc,b2514e97,515aa0ec,3f65ce8,6f6962f7,72eaca41,d5ea7983), +S(605ccd66,4f18c83d,edf36f62,e5089f14,4ed23f4,f88635fa,ab161567,fd20c8d5,cfccf020,363a805c,1d614aa5,b9a912ec,99cfea61,5c088789,275ef531,dddf2418), +S(d1e4f37e,7d3902aa,dfdd7254,fc727a44,98eb9303,a12fe71b,4d193be4,8dba6913,bdd29d53,4881cbc6,c2e301fb,7d757bc9,9224f12f,4296e834,9badb88f,84fcc289), +S(f30b6d53,f330e6b6,b23d21df,7b4350a0,c42ad202,4a92751a,5dfed94c,47bbe60f,b5ca6c03,b31d06cb,183b0b18,4432a06d,8143d511,8af4bc01,c364f024,b46e0c38), +S(540944cd,42101851,c2cdb0ad,61264e18,cbf948cf,2090df68,54482628,74271ecf,cd16820b,4ccf9131,476b8778,ba3e83,f5970507,7110686c,653be572,2e99db3f), +S(846e5478,5d9cad5b,5109c7e4,6fd62b04,f686c723,1fc3c0af,66bea20a,d9d60dd0,4b1a28c1,78bd097f,aeee951f,fd4f46a9,4658a76c,268ee7b7,9aea4bcd,e75fc8ff), +S(16e732f4,43b4c616,24a495d5,4497a23c,c95612d0,58df9f81,11d1340c,4b2b8b5f,61ee476f,30ac2be7,e3ff09f3,ebf0b3e,2d76177a,30573465,101f4cc4,ec0585be), +S(4f30ef37,2c730ba,a6a40ff1,94b6b58f,436111d6,848b4e60,10be9b9f,abef17b7,b579a133,7f3247f2,8c6afc05,644778f2,3668e73a,a7c6d14b,649bf948,ef126142), +S(595a0d2f,1668c0b4,9ba0994f,3b681c42,6e3a67be,e957bf52,47acd9c4,7cb7d009,fb10684b,3ef1b0ee,37684ba7,53858b3,3586b837,aba6d82a,958eb947,2eca319e), +S(85dfb59e,d7ef6e0e,355e0ce1,304fe5dc,8bc6a3ad,74de3352,2089f224,8894e0c4,7a9b9ad1,10099903,d0d84a33,6838d422,5f98abde,32d82ab1,a383969c,1fb98bb2), +S(c8e13980,cf58416d,8719bc94,96e21774,c29f2e1c,5a36e760,2ce7783e,2c810fd5,5bdcded7,bb484d46,48bc40e3,ee6a3761,cf995e36,2af21f0,10048a91,6fbd4717), +S(80b98fb8,e48f44e,db69fd8b,18eec95,2404fb59,45c8a35c,bf3616e8,c746aecd,d44010a7,20622d14,6af5319e,155fe4ff,591bb829,f5086c7b,1fc1ed6a,32ccbaa5), +S(17629b1,cf015e58,eccdc7a,de049215,fca806e7,805e0e35,951df2ed,5cb87d38,afd56698,44f6aefb,528dcea4,29438e0c,d10c4b21,23f02444,a950834,97a61f17), +S(592677f8,695479dc,b215af2d,bbc90621,e7f3cba8,650563fe,1b2a8765,6c44107b,3741e4e1,b39b0e2a,58373166,854a1d31,ed062a25,c4e3e1fe,bc82e941,eeaaf50d), +S(98d5f85a,a93c9d87,e34e3bb3,597d9380,f0a7e5c5,b03ec219,4025c89e,42126453,dbe42a83,66753aec,61f7f01d,e5dfde1e,b4b989cf,95bd36bc,a66ddc0e,ffeea196), +S(9187b546,70d2b9a6,b711c88d,353bee60,72a1f5d9,ce75288d,4e6c9111,80dd4126,80e73529,d211ea24,d4bccff2,b9de3e31,c6307ee2,3cd9267e,b2d372ef,78f8d2c0), +S(e38c81ad,8ddfd8ca,4e01f380,78eafecb,f8edba6,8e33e565,309a3ad9,f0abe0eb,9f0ace1,f05f0a85,a1488644,88cce81a,2cf55eb3,a91fc10a,e6b82f47,60dbe618), +S(7ee9955f,99e218e7,dadfed94,dad7054e,3e0b97ba,932338db,69ed056b,6f8adf55,5244e198,524a105e,2b5de11,81d7bfe9,ad56aa0,128a3d87,8cbcc5c0,25b41ca), +S(eb9e5ae8,cdf64b28,76349aab,c3f75c9c,9d95a335,3214d0cd,d43123c,265b08d,858611ee,f67a32b9,7b8a9090,6639e998,a05d9bbf,8ccb7c1b,9e5eb711,4216b877), +S(2aa74ebd,7e8b6b2d,952effaf,e5545eb9,e99b9678,a2515698,975247ac,c83ff081,1ec12cbe,6877aafc,f353f641,1a5edf9f,e4bcc09f,31422df,d076932d,8f0defbf), +S(1b7b8fee,a3ed4af1,cf9b07fe,b435ef76,1d081b1d,f1007697,f78fe5d9,89076aec,b4aa0d8a,efbc71d9,d338e998,197054f0,20c5a2df,7a9504b1,ca7cb603,6641f85d), +S(3f293b75,9f152a4c,b92e319d,e5d02b82,f5dcfbc,2ee7a53d,fdf7ef6c,e2595c32,b67babb8,d1d1963,fd742c22,5ad5bbe4,27e57d6e,b9e17bbb,f91d2c1e,a455c1d0), +S(3eb9968a,c9b01fd0,882ec65a,8f9f5a5a,b0fbec2d,e2dcbf88,cc9a70b7,887ca59a,1a4de130,4a46fcc7,6719c254,dad5e88e,9d6a0839,9a9a28ac,7f171aa5,9624ee2d), +S(a559381a,f41b944f,e2bab9d9,62ec7c53,efb25ce4,1ff1a456,54a99f46,d97ce20c,ebc6f66f,75ad7322,4c1319c6,4268c997,4142,11ca65b4,58c5f79a,be0ba0cd), +S(a8bc942f,c60ff2e1,140f9a97,3d7e0b8b,77698254,57098005,f9186e97,9e430593,2a82070f,25480267,c4fca773,3173b354,fcd5cc1b,ae497af8,58b7d451,8f048c73), +S(2f456ca2,c4f8806b,ce4caf12,32446b07,3405dcab,7b3cdcc0,247dd079,adc7a626,7308e58,5b9bafab,b2dee049,9bbf60ac,fdb37242,3e915156,15314ffe,5ad866fd), +S(b56f4e9f,9e4fd1fc,7d8edde0,98f935f8,4c750d70,5f0c132b,d8c465b6,6a540f17,cd171acb,d63357a9,2c23ee52,fa7d2e2,de2bd69c,343357ab,b95d0350,fdffec02)}, +{S(ec474055,43c52e68,5c9e6c75,97616fbe,edb70df4,fe3c375d,d7d24729,2b120851,22c1c1a7,30066375,5ef8c2fc,86452667,90347223,f11a32d7,9f1c0169,a467530c), +S(3f50e502,c45fd922,94fcc46c,b3118a86,43af00d1,634bd694,dfc0eeb8,be2dd4d5,7cb992e0,f29fbc5f,2887161e,6d1e266d,f11fb723,3b6c020f,26eee393,fe96b526), +S(443007f2,43c3f6f9,76914031,c41e0d49,61337340,9bd4a681,f20e4c79,934fc2de,df67adec,c9ce5f6a,e6c8312,699e0353,68ee87e3,1e96790f,f0bd3408,94af74e2), +S(1e15b9e9,c28f77df,75837a0,cc9504ab,a3a49aef,26a88d99,543fa7d2,a875620b,2a081093,6afbc025,3808ba31,f5597287,e32898ac,77fd0021,9e6fc298,26b0370c), +S(cfff0258,ff6c0d97,99823447,775b75b9,1881e06e,bbd72ba4,28f8248c,5cf2144e,61e63569,5c4a8fb4,f68f974,219bbf4e,e0c277dd,4b74b843,88f99041,f97809e8), +S(2824f4b,f72f9229,2aa04058,7a0c5395,4c87e7dd,bc5dd199,af6918e,6db62f1e,16ab1473,3bf8382c,521af6fd,27f8b2b3,b421dbdd,bcf06c62,3bcd86b6,4bf33274), +S(5dc7571e,27d6b955,6c789b4a,80d49fd3,17416fa4,e04a7eb2,3cdbb88c,9c253a44,b7261e2e,39327d68,a5456abc,9d4600c7,fb6d6e51,a732c7f6,c64cbd0d,56de4a17), +S(e93f3aaa,5d9edc53,6029d332,9ac0bffe,af27d4dd,40f550e5,3df879a2,a5c1d993,25b442d7,f2e2e03e,c7f1588,16a1af43,b5d2870f,e0cdf429,babb19e8,951c0982), +S(8f91f5a5,7aa40d62,ba407d8,6ea5678b,65c84ea8,f241e2aa,dd6df242,52635a86,e9333d7b,d6c319be,3ff4f5b6,f286a8eb,27d5a1bd,29646cdf,77441c40,f716a6f0), +S(77d398e0,228add30,e9e120ea,b16436be,30c254ae,65016672,8bea1198,de1fb17f,749a20c8,ad6f4312,fb6e6fa,c390a4a8,209704be,3355362f,31db9b45,f3876963), +S(96976a85,b6dfc4b6,13dde357,c21c3518,a5f55c7b,a2d22976,592d7632,3b5a8546,96dc660f,8ef3b0b6,adbe77b5,69024fb5,dd713566,617cfc51,c8013f2,5b424e9a), +S(49093c4d,26194278,623c2ff9,6c8e2f11,f9edf3b,13c33099,57b57f1,9db451c6,5d53ff56,50b82edc,9dee9f43,ed6db93b,f6a26233,2dbcd699,130aa9ce,5130384f), +S(49187f03,d5f5f628,b9210b88,8ac4f1b5,2f754015,541e9f38,162e969d,6a99f4f8,276aacaf,d38d0bbb,416bcd0b,8ea590d9,7735a3f2,8de72aaa,76bed0d,6644bd80), +S(7d64e446,65c03917,87fe0b14,16ab1048,3a6025af,b20e8c19,3bbba444,2864c587,2c0da181,6507f8b6,4c9e5684,a4d53d98,d6b88215,772def9f,fa5a7a1b,f2b2ccee), +S(2223484b,9ee12b26,2a21a18d,2682ab01,c291ee1c,fe3b4b9e,2e93fb67,aa08535d,4cd26418,bf0d62fd,b266a768,5f7aaffe,d7bc95e1,be41c6bc,7a0c9076,1ace3974), +S(aac92330,7d6a55d8,552ce6f3,9c531d5e,afdd5313,f801242c,21df07b3,a656c618,93d9be6c,590901a9,1413cc23,94cb426e,e6a61593,93511c,e67e1ffe,68b8f3c4), +S(6ec1a0c7,fe40c40a,f3515ff6,60f0b53b,8fb6be3d,d912f8b2,c7a473d2,3e49d201,b10f000c,191e9423,60fe6b5b,2b6b7b85,73c0c2ee,384dedd6,e3dc04a9,7834f93b), +S(bd982a5d,a4d0291c,38568fae,ebc76df3,f2b0e5b9,1cd0d3b6,d2f32077,6c35c43,48566068,d5142f6a,90b13105,e7841d32,38d869d4,fcc7319,3e3cc9ea,76739c5b), +S(25d54513,b9084ddc,a3f20e18,94382290,115ad5f2,dc72c267,ba845b3c,6ade39f9,e908508,7fc5efab,a6f62c41,ea8fe856,226c8e2f,95dc31fd,2b49a7c3,6aa53b39), +S(29ab6cd5,add595d0,cb64ead9,c3d275a6,ce383f15,9340faf5,118ec2d2,9bb9a62d,54584613,dc27c249,d5d1210e,9e121aa4,7d606fad,1c4621e1,67f67b90,d8f3434c), +S(4cfd960,86f86d5,bd805667,55979c32,d5419dbc,946d63a3,40dcc6f,6d6dcd48,f032b7b5,9d85d0a9,fdc290bd,a1fc703,55b5404c,f70d45c,d15fa9e1,33ee9319), +S(9c786066,b6642e11,139aa303,800314b0,6afd4d97,e8314a1,de9d7002,5b7bbbf4,463a2ded,e2185c8d,944f29f0,140f6b66,9ad6e3aa,8ab6e292,a2af551e,685e58ca), +S(4e98485,f15c506d,6cd29d7c,3f9a59ea,4bfab065,78d86b63,f25e74e3,e7aea96,2baf3fb1,c26f2b0b,1a0725b1,5d1ceeaa,7ccece9,a1dd8f2,7f80d825,d8cf1d71), +S(8412c389,bc88e9bc,b157d835,85af0dfd,48689532,e38044b9,117b8acd,ce3b62cb,17eaa404,ffc1f62a,38f7ce99,6216b1f,40bb62e9,6d1f7a5,8bca370,c0d30b44), +S(31b9b8dd,7d37b38e,892e94c7,a5e817c8,ffaa1c1c,f56979ff,7f25815b,758b6d09,cdb680a,ae795ef1,f94e7e87,50ad9077,7a55eec6,c6f0795a,ef951e3c,ab45d186), +S(2be45d05,e89c58b1,8212890b,b6eaf935,6f03e37a,fd957291,4ab71a97,7af8350d,c3b05f04,c9f7a1db,b6689fe0,6ecf7a80,3ecaa147,8d09a1ec,9a7d71dd,a75406da), +S(5185cfa5,fe6302c,b5573594,61f375dd,c483a66b,c615b5f9,30abbd4a,5c7ae0c,83cc447e,53c91e07,6f44d265,389bef06,549b8d78,9776000b,5c35ce36,5fcff98d), +S(cd42328d,e4a16323,c51ef888,b7e67ba,f9ee1c53,82161b90,65e1db15,bddcffd9,caed06b1,7e86b470,b665de93,74725fdc,94792c57,5509ebfd,f012133e,122302f3), +S(7a1ffea,8fe5114b,8a638306,3b7811c3,25a40c19,42451557,679e75e2,b304ab91,1ef3fbde,4f0283ab,96efbc83,bcbb5a6a,d781a215,e6ff6262,ca50b9c3,986816db), +S(cf0bdf47,a3f15b24,5c7136c2,3a3e38f2,661fa8a9,4df29fbd,8b41ef22,c291dc6e,c73d1fe3,919e0aee,5b3adc4b,1a86e709,4b039302,d44a0d56,638c3eba,24857a99), +S(3a571630,935c1f02,d6fd8744,2c082060,cd5e792a,1c6f92bd,c4eed01,686df50d,7a1ec78c,4a660cd0,ae90c00d,1a8f5f22,1f5507e1,4487e5ee,2dea74d6,17a69494), +S(1c5e5481,32b49a7f,66ae9fed,8323480e,d1ab974,622e7cf0,8993895e,ec87fac,b00309f0,7c80b970,d446a605,e2b3d52c,5c215314,d901cdb3,aaa284c1,a03d2740)}, +{S(2b081f0,268d6ff4,1090561d,79144bb9,554b1f3f,1f0f2cfb,c9344e2,b692157a,597920fb,c528543c,26499af1,8b5e7d7b,5ebf5ada,5df4a46c,edef52f2,1e2fda5e), +S(2ce45dc7,d84d3143,884d6696,73738e55,e30f6045,8bac95cc,c30e86f8,71779827,deb79108,80a1b675,a628fb09,725c9307,d1629b07,8027f566,29f8560c,2ea80458), +S(9dfa0cb6,2a63244a,35ce3a0c,f102e7c7,93a4530e,21c6c03f,15a4fb91,e116c480,b01c1aff,902f0831,bc8cd268,663f0ce3,6d8e22be,86026460,a8950636,ce44626b), +S(61a32d1d,cbd7e887,def51ffc,9c1dfd55,d06d72f6,a73ee2df,f83cfa0f,52d47bb4,69a3b17c,fa91e3d6,faf28d83,2695aa66,c87b14ee,7b2d987c,80242fc,ce2cc42e), +S(8cbbac63,5414c0ac,b6d7f659,5c6eca39,886c3ceb,82549f72,d07a4019,e3076eb8,c838dc49,fd5af1b5,e503a430,71901bcc,90e1ea38,9f735e31,236491ec,43e7b621), +S(d3f77c5f,663c6efa,670b46bf,dfa96af4,fc4720d8,e1df6e8c,1ffc5696,f37cb55,aeea0c3e,50eee6a9,16462a13,90ce5d47,c060efff,1fd3108,1db77513,34300332), +S(6f774118,4a0210a0,79695d20,f93f861b,658137c,3a072076,7d80e5fb,8b1ebdff,c12aecc9,b55bff04,bd6b6e73,870deadc,666973b8,ec498bfd,bcc4c33d,23f5d674), +S(fecfabd4,897a948e,5fab7200,969f9955,9eb1c9dd,2a31325f,34dc4f80,13da72c9,ba70c9bc,c8048aee,6fdaf5f1,8ef638ba,ce739c0c,76ec4c13,fd4dd6c1,1663d02d), +S(d1a1a1d4,24fb5ab1,3e985c86,9bd7c02f,f81b4633,dcd22e96,c3f33489,2e80d543,cd5b79ae,af62769f,dce99530,8b921b5e,39a60fd6,f989dd7e,1d0d7219,e9ee8865), +S(7099682a,c3c083a8,80467746,fb924d4a,56f23d16,a3b57f10,1a824b9e,653d45,6a9c296b,cdd72c93,df840003,24ce78b9,1f9df6b2,bfa33922,ef32733,fe49cb31), +S(1035866a,4e9ff0fe,6b0066c6,3f0a2392,c0b8cd61,36e13ada,21f6dae9,f22c39eb,6390f9ae,ec1989de,2146cd00,a5eee99e,76ea4536,423e9bf2,c778ff29,85538fa8), +S(e18351cf,163e4df7,d31851c0,409b5705,a5ffd472,dbda5dd3,bf05e66b,fae50fec,264c1d26,41f185e2,72b908d4,77222484,c651e8e1,9a9e1fb0,1ec5ed4a,8a4b6c08), +S(dfbee778,ae4bc89,24f32c18,bc0a740f,d01922b1,28e1a595,283bfb4a,fd8f2f76,d11f9b3c,25ae3619,99ec2c9c,2f1a5903,d80eae59,e096c5d,c5e37201,81756acb), +S(842cdb54,414fa977,58ae3fb1,9316ef32,af9c4f0d,649f0abd,cb94267a,124b59fe,a016f899,288f4caf,dcc6edcc,3cbcb561,fdb4047a,f789e1a8,51ad21b3,be62f5c8), +S(3deb5d22,a81540ef,cff29ac5,585a056a,204e4d67,2fb858f3,5c23e939,790c744f,20d73911,5b24b1a0,a4b31302,73321dc5,d8b74398,d6516439,4e1423f8,b4022d6f), +S(3108e61a,1e0f079,f76e3ab0,d69cf068,779674e6,49768aab,718a09b0,640251bc,245a9f2d,141f390c,f1a17bcc,a06c5a66,5cf3a87c,1096f5e9,b04c4fd6,737d9a56), +S(63b9eaa,ce6b32cb,e4cf4a69,6599f8fb,3797bff5,9103ba0,c1351e78,b16cb0fa,938ac414,ef911df6,bda32b5f,1f69cd7a,87a25d82,177763de,568ca95,e846fa5a), +S(f37a5a41,cc96a04c,c882c27e,e362d054,7d6267f1,c91c5403,2daa5c12,1700abda,108bd6ad,27aa72de,48406693,758a74df,53a3ff98,d490e303,54c45ffa,c64638bc), +S(52afb7fd,6a16dc6b,98f9d7d9,555068d2,c2317035,b7492e8a,fbf28aea,3b1fcaf3,548d8ca1,2cf7a11a,b0100455,c7240290,6cb2e92b,101d1e2e,32ab263a,3f3de413), +S(f644fce7,874e9154,8f3fb284,b7d1ba17,fad6eb6e,c581f57d,5aa11804,86de9889,b2acbd8f,5bed4270,ec131ea,1891c99d,ddb46648,60acaf39,d68d2a54,a58b92bc), +S(863cdfdc,66065a4,50c2b7c3,1d694c58,e63ccc09,fccfacdb,4fdf0b1a,b150e937,14d168dc,c84d859b,55a3d5af,9d17ce4e,83bd90ed,55e099f2,50932dad,b5d2693a), +S(9f5569e0,50a26288,5d91f82e,9cacdf9e,f4487676,730f4da1,46ce8769,3d8807ba,cba008af,547e01f,6a9c6c45,8f9d71e1,8ad062c4,4b305b52,3f3a6655,3979a2b9), +S(a0f14b6b,99f0bd74,cf9864bb,17b1c43d,51ade513,6cd72a15,89312dee,706d50c1,e2bd2d82,c549b4da,cebfb888,c1a7c5d2,f0757fe8,53e07fb4,747eb7a1,542137cc), +S(9e7bd727,fbab45a9,db36d17d,5e5437eb,f028a10b,392ea470,d5cff0d3,2e9bb0fc,fb52eb32,e43133db,28125280,e3cc55a6,76877c58,b4051f70,9c031f84,24f12d9f), +S(7d8b1a30,4c0c8931,2d5d712d,ab396efc,7a117483,7f62bb38,62a81a3,660b1068,472a2dbf,ee99c398,94d14c1e,ce87cf17,c8aee10a,4720357c,c5ce79a6,19aafe2b), +S(d5eb073c,a5afa126,70602de8,417fe50d,bd8f0768,d615ef2f,565e421b,4e7842f9,52571e4c,e4f0c424,fd288f3e,8b6c54b7,7481c3a,43230fc7,4c6f48b,d4041089), +S(3c41cd8a,77728b07,ea592254,5ac45462,2c7927fb,6a7aeefd,61635cc4,217acc90,eab0f00e,84099fcc,c2f4d339,77ff7d68,4c035b3f,34c6c4e6,aa812c9f,43af366c), +S(1eeeca5e,cadfae31,188218ae,7beda45,57f85c25,5c99dd64,1d00d8ce,2e6f7809,d7e5c2ff,4ddf421f,cf09b655,113b8cf1,a4c25e90,d02b871,91dbac55,d6606ef1), +S(54c13737,454414e1,76bfaf1e,b4402bf8,678daab1,555412d0,c6d3fd44,653510ae,753f6a,f382d0be,f1dab47c,1b9368f9,57d586c4,acef0d19,dc241ce,f008ea3a), +S(4d608c37,1fd6f635,9ffc57c1,9f1aa5b3,5b0f5702,55851cec,34278d2f,465fc8f4,12d1592d,c2cbccc3,7e5c9fae,4b864e9a,ca6967e,9d4852e,9adc9e31,1000670f), +S(71157d64,8c0a1a33,19b713af,f806cf9b,c91a446e,2ba48ca2,fdd1ed4e,8c43da59,c2ac2aeb,a298fcfa,c0d5eefc,6eae54ec,330f5c0,5b34952a,9821ded3,9b72d383), +S(327f876c,93652555,fa80a054,968b4712,930dc930,12ee6b8d,c10263ed,3b89a762,4d2bfb15,4cadbfd9,4f6696da,a1e66846,8aacaf8f,1428201,636026a5,46dfc92e)}, +{S(4d3b98d0,b5926a8b,3e5622d6,10b30e88,caa6e7ba,eb10bc3c,38aec3c9,c1267578,e84056d8,4726291d,e6880a2f,2d3b0b01,371dfa27,5b8c9cff,4805d18e,a5bdc788), +S(6e463689,111d1a5d,d10b09e1,5d0f438b,1cebdb67,7a6cd230,bf6349c3,70667db9,de10eb3f,5d2f4320,7265952f,b33fc23f,154972fd,d3394cc0,21b66276,1b51db3), +S(48235e7b,76c94c32,b0d21bf1,7fc215ed,df036366,a9ef494,19723485,78db8943,4f5ac8ce,36c77403,b2fa83ba,1fd0e88a,3da4c8cd,3c10ddb,aafe2d0,5da40a2d), +S(668af7c1,4063dfba,1a4187f3,f69cb457,cae4edbf,a3bf9669,4080b6de,bc3e2f0e,ec95f37c,7bac26a0,df23275a,283828a8,5a102d8,f33eef1e,87cb56c1,850740cf), +S(d4426fcd,cbdfd904,725fb501,656b9f2e,2a9f9ee3,c6522064,f078165f,61c31d97,e73f2a38,c56838a7,174ee704,948d28fa,5b5fdc34,7774df9f,72f27834,a85eb4d), +S(d0f49ec3,42076b68,4cd91172,f15584fc,ea38aaf0,a3201063,f854ff1d,f68d4600,28d95ef2,82dd7d6d,72be7854,e8858b1d,a142002b,ad7b7b4d,bd1e582e,94341e16), +S(8a36f3c3,50bc0f1a,1b1687ed,4ff83a68,1578d74c,c28fa875,f6969e52,4887bbb1,15ab9e05,c3ff823d,ba090e21,1033c34c,fa713d39,feec4346,88e3ecba,4b431e10), +S(4d1ef23b,b316e821,7d2cc409,a1baa273,cc06f79,8fdd4d16,5d5b6805,3718e238,fc37e62b,db18154a,2add98b0,ec221e8b,bd1a2096,ac2dd1fb,cf8e2e64,821301fe), +S(eec1f8e5,76e87803,bac48bb4,65ed923a,a68b2965,64365d45,bd1c24c4,cf7041b0,2c6985e4,7cccbc38,3b85be66,ab34a166,c98a8000,aad08f73,616e4d2,2a15a009), +S(9fbc20e4,de372a38,90443786,bd7cfc40,c6195b29,4cf51874,6ccd9ecb,cee223e9,f734bfc8,a6973209,903edaf8,52f63bca,b23fc1a4,879906b9,1c5df169,9d24683a), +S(7984c8ec,a2b02dd4,e8b9c69c,6f803418,48f108fc,b63d7038,de58e567,e64a894,49adeb11,13c705a0,9eb01e50,dea5be48,86e5d424,689282ba,6d64f745,74f45e8c), +S(29a7400b,16f5712a,2324becc,29fc4b98,57fbb926,f8741dcb,324c26d9,6cd178a3,90770be5,5c0a94a8,16bcfa38,aeab11aa,5ec3b7d9,eb4e7d29,1bee1c02,69e87267), +S(8299d2fc,84770bff,a4e630b9,8f2e6744,99b1c7f5,719e907a,d70515f5,140a00c0,c790968f,a3c3d4ee,3e25a1ee,8e1d4923,3c22f034,ba7c172,b5d7d91c,99eabfa8), +S(5cfbd498,5b608db3,5d1a2872,b9cd03fd,d775e498,4629f41,61a2bde4,fb2582f1,8288fa89,81056333,acd1720b,bde757b4,e83d0a49,9c5f2220,521a0bc8,dbe827f8), +S(281f1fb8,e144e39d,9ec2104a,96bf0f1d,494eaff3,57fe084,63825228,ecd286e1,ae40e075,d19ee5f1,1dd7e22b,f1c277ed,9a02abe9,975609c,a7c811f9,7c54e493), +S(51698bc6,73d3289d,6b4882b0,cab14e67,97d31221,e5bc6e53,af061aa1,9f546daf,7b850099,fe977f6c,f50abe00,bb3a0b68,90f47c45,24e9ae02,94304b6b,20bd986c), +S(db701e92,9b696c86,d7fb459c,97af90df,ef847bf5,1d235337,2a2a4792,5fa8ae53,edc7e98f,1a108bc4,8fdbe65c,2904058f,7da5e188,e71d194c,4e9dbfe6,8a55d18), +S(c900f57f,e9a31d4c,9715a822,5a906e90,22d81c12,714006d5,81be0c7b,aeb5a490,8493eb00,90517d9c,b70a4bc,39043ffb,e0863475,17cde2,a9bd0d95,f616bb66), +S(d74e842a,fe2da0df,df6342bb,582fc223,717e0641,f1ce0e14,584c5c63,f379ad72,cefdbd7b,b4ecbdaf,409a0fa3,9a0a305,b1bd986,4913f977,bec6c47d,5d525c10), +S(b00a5f4,f033d126,64366147,a1bbb9b6,ea60a38b,863c5f3a,e323e8e5,1aee200c,11e9f1f7,b7701a4c,26a881ce,30fac1e0,92e157d7,b9c2e4ce,cffeb38a,172ead87), +S(5772b134,68bf9f09,d26074c5,16dd7472,d3a83624,92306cc5,c7544ec0,d73de280,2e2a7931,aefe2a31,6dbad1c,e7f9d42b,7f38197f,43024f16,9e27fbc,7af31d0b), +S(8bf8af02,ccdafb15,598f6725,5f172fb4,ba8960e0,6c81ffa5,b4c1c313,6f95f29b,96041af,1efd128,ef133e5e,b0aa3d9a,6ac3a651,92598b09,ed649847,385a9b2c), +S(292889a2,d8329139,3410385f,cf5bf489,7e1ee23c,e0e3b5ed,82ceb340,d89f87a5,41099fb2,bcec7e07,d4d4ad14,3cf605d1,b0a32487,1736063b,e49b06f6,d648aa69), +S(ad227b2b,737dfd02,665c15d5,9b28c7e8,76413dfa,25c9068b,90efa79,ce83142b,82f1eef1,7e2f6b86,87137302,741f8486,26fad679,9c6a22fb,3a49e341,c7e51e9a), +S(b4245dc5,5df02839,4cff2610,4a4255ef,89f3e708,21541c3a,4631ee4c,145bf85,5a40f72b,7a67540b,6858e5e,b15005c1,3fee862f,3cc54a52,7643204f,d0b8be2f), +S(15aaef61,568537a4,695c1990,c05ecb9,2c605232,f705c3e3,729720de,7e9c2400,e6efd1fb,b74e9e43,ec1244e0,bb4cff2,2a5ede8c,848a4f5f,5eec6f8c,77b6f5af), +S(ade2e63d,4a828328,b4c53763,30790331,734d7f90,37c1a584,59570c8,f31e72d1,c42898ab,3633dc7b,f3a454f9,d5475c4e,fcf1f786,d1f609fa,23616f26,b23a1a97), +S(d8a9043d,297681ea,c5ed63a5,de306500,6b66e4fb,167aa5ec,c76a8e2e,df426e7a,cd3de058,a0a93d7d,7122d777,1446b2b4,d4cf53a7,1759fea3,78adcf03,5093f4f6), +S(30651cb,592d282b,5348f192,5be3f04d,da5033bc,39e2fc7b,1e61500d,9769b57c,dadf77cc,44853f67,53b1dbdc,17a97d3c,e6d5b2e7,8c6087fa,800c5af9,ccdc54c), +S(aa21c60d,6f7d6253,9d42bafe,95c58f30,33c0396c,fa0ceef9,6d2f91ed,d6569044,4d519779,3b19515,92e2d952,2be520b1,11a5453e,ec6d7ccb,3e6db3bd,3d0c84c9), +S(5335cea5,e99eeb23,765b3444,d9bc7be6,1da67d6,91bcc42f,d43ae543,e9c22bc,c4072fdf,8963addb,5f980f7f,f314795,373cf3dc,935e0e64,c3d3d98c,342530cf), +S(708a530e,9e52c73b,ee87c9d8,8161c810,5d5762,2c29ae69,1cf999a8,3a1187a5,6477b7ee,1e065768,569a923,4492c7d7,c13258c3,92cac175,a75b0e63,b8c2426f)}, +{S(3fbd5a4d,ebbeff54,8c2271e5,33dbaed3,fb8ccc23,e3fa2579,4c8f7fd4,47ae186e,fdee4625,45bf75f5,5c29a724,6496d970,4616c54,ba01d0ba,feb9155b,cafdb555), +S(c1483ec1,78ba8414,c371b57c,49c687fa,69669e2,e3e1067,cc6d2a93,b1a9d24e,edf94395,962adfed,c2ebb3bb,29346136,e4dc870c,5c76299e,3b07da35,6bb26bed), +S(26df4b09,693a3905,2cb7a1be,5bc2cc97,222b70e3,3d297a54,43741228,beb77017,a3d6b908,cc6d5f80,aecda93,99e17a8,ce33c423,f00cfc4,dea8354,af502760), +S(b6c265d5,90475506,e15b2388,b894718b,be67aee5,ba40ceca,51876946,e336d903,b451d3f1,2c666c85,bf9486cb,296c39f9,49f9f98e,96d50a19,80f86f85,a1efb999), +S(da6cf69c,feec7b1f,c848639a,7e27932a,f49cd695,a2e56a50,693fa862,6e257c66,4506f44e,e6e0d822,7e67dc4a,9f8e2ec2,d3ec0c3a,c2a70c7d,d25fa9c,34cbe3ae), +S(d57648db,adf18b77,db185ffe,3f86f859,6adeec05,ffb86215,4b894a0,75776eac,a3f2fec7,7186d81e,4228cb1f,691e00e4,82a125a5,bbe9e6ec,45a53604,4932491e), +S(4de6cc4,d64d52c8,49c0e8d2,45409ba0,88248399,1916df3c,238be4de,ee2504d2,b389e7f1,ca72e9e,1cec2ed7,70f21441,590f0bab,1b963f1c,411f9bff,d3df03f0), +S(15f1bb11,78b01439,f26412,1d36a44d,b8333fbc,eabeb471,da2e6c1,1b8b3ee3,bf137117,35e10854,95acf3ad,97fe510b,33d6628f,e67d0067,499169e6,6c12274c), +S(badc76d5,481e77,9df71d6e,223c6965,9e9ecba5,8415a95b,eb7118cc,b1f931a4,475e47ee,115d258,3f0607f1,7542ccde,626e6d57,6ea884f9,61b41354,aef37247), +S(13015ae5,d770f4a9,75825bae,a6cbbece,78536dcb,78d7116e,d304aaca,199add2b,f5e28585,7a99ff0d,a4fd15e,8ac650a,98421629,aa102bca,25df3930,7e5cfcbb), +S(7734d2ac,707b9b53,afbd2a64,50c9b609,1905c391,958d7215,f92353ee,fd3c7be5,751d38f,ef48bfea,8fa5de06,8188a658,ae6c9c75,a359a4ea,efaac5e0,b5e859aa), +S(91814384,7fe2ed34,e9cc9c61,3fc2c67f,2b76a208,561d0e06,271b812f,6c678856,9a97f6e0,9888e076,93c9ebec,b8773485,c3c5d208,c793a77c,6984fb09,cdfd67b2), +S(98c516fa,2a2c8073,70ca9d7c,b4187108,ea345652,c8f814b8,b4a9405f,b48c2469,7b3446f3,2e8c4764,a8eb0846,4be3bc6d,f42b8d14,b7da65a2,ac96a018,d0831c7a), +S(5691d1e3,713302c7,48bf633,189ce032,10832e4a,9af1dbc2,abc3e99b,cb4faa01,d802e21a,fab84c54,5d665ed3,54130d9d,f7f18283,30b3a1fe,e30c2298,8b71654b), +S(1f8cb624,6440b640,aadba62d,c02f4ce7,a0ba9be7,d535987,64072fed,95c0d1a1,647a9dea,abf8247e,f46b60c6,53cdcd5e,cff0d1c6,88b7519b,a778f9ff,c894e1f6), +S(1489af53,94878f07,5cfb2e20,931fa3a4,e585d494,78818e4,f47f05e1,2be54d14,cd5bd721,4e535822,43270bc2,dc692ba4,c57e494c,eba92d4c,dd2e13c8,e24d8550), +S(66159248,2b2229d,ea70ff76,d2a5e70f,ad1905c0,9211e3d6,2d1d652f,ccd709a7,8f5ab85,57948aec,67e7b538,fa9c704b,ae939c22,4041e6e1,65f6045b,a6fd0fdf), +S(bc894efc,d0fb7497,ba70d654,961aa79b,6ac5a795,95537c80,82c525c,2c44c3a1,d5254292,ef0fb6fc,db587393,3b17adfd,87d13320,858ff783,75e356fb,b0d3415e), +S(f117d7f6,85457601,9b559bf5,f5bc150c,13363e7b,72123435,b441d98b,d169dd27,a8f4a23e,dd0d8f2f,8950c124,1abac43e,e629b0e1,8505c3bf,780ea378,20c8623f), +S(2a8d99c2,65a66a51,56b60556,775c61bc,b2a81a62,34896a31,16238c2a,b7716ec3,5e842306,efe11f66,5a049c0b,fe3ac74d,d72c5033,6dbd04bf,2a77b0d2,4d7ea651), +S(ea747597,1a6b6d94,2e368bcd,9979876e,afa4622d,313d819d,5e8291c8,2da95830,41d4a478,e0950bb9,e6595bef,3c197e5,d0f77ae,6340ca8f,2947fec4,70465193), +S(114150c3,fe8853ed,9f3e26f7,bc9f3d6f,aa50ba0e,6ae2d8ff,97924b04,dd9678b4,5530c22f,4ac30716,2b272015,460426ba,9602256c,ab735174,1a2fbbfd,cd71d134), +S(65d96017,c91a746a,656eb595,d39dc9bb,3476dc0b,1a1036f2,df7a4ea8,1846631,de46bfad,6999b108,1f358edd,7809919e,4de768f1,fb21dc09,4b248bbd,56b3dc76), +S(f2aec214,61c040c0,ebd18204,bc277312,732b452,266bfd55,aa071853,e458bea7,52a1b71a,eefe2c48,5ae2918,e236f1d2,1622d37,13b4bc04,36124567,d48cc453), +S(370ef89d,c39e637e,8bf6be31,63a4f76,7cebb202,c868647e,db18f991,977681de,e6d402ba,9769363a,b963729,822bd6aa,9794592f,36e54461,579ae53d,4c8bc4cd), +S(8db6356c,4f31f46f,961e4342,a1ff50a1,8240629c,a0decc36,e1bb24af,f6742e57,8ee033bb,96958466,15626ad6,736e9025,fb76320b,c2d0ceb8,ce3400cd,743f934f), +S(14a1a08,502510e7,1060f291,1316ee01,b1fc2bd3,c88eaa1b,656cba0e,8e3515e4,5a4dc536,b62c349b,c9bfc6d7,fb677231,1368655e,fcb9c89e,572aa58d,a57f08c6), +S(d530fc7c,4d5b4fd1,5f44ba0,c0f08f24,889b278a,93ee30ab,935fc112,5e244a58,39c0ea88,5984c2d1,d5119a28,7cd842ca,d3cb54c8,ab6aa222,c01e7152,98aad10f), +S(50f3c13c,a474f604,ca992658,bca68207,76acff7d,d507d63a,b5e18b28,5c5be81,685e151c,7d8fddee,6c9d8513,dbf167e2,c32dd70f,b6932397,38eb63fb,3e4f7afe), +S(c0a72dda,13174682,726ede1f,7ecbaa68,b4b137f6,d44298d3,4e17b7fc,10e19d4a,1e45ff3e,4a4239ba,4a897489,a265a60d,9a91f3f2,83fe2e75,9e770156,c9ebb3ca), +S(7121e14e,7e2a2c57,76b5702e,c83a845c,28785c79,9099a007,54b50fca,b4d9279c,e6b3ff5,ce62fa35,adf45e69,2ccafb91,b805d7cc,b6eff2e1,38dfd680,d0684401), +S(c0c01f34,ae41b8cf,e466b4c9,c6a5d5f6,14f570d6,fcbef768,a81a6c8f,5ff4adb,f47b0a41,1bca80a3,836c85f4,bf8a4731,3243bc2e,8f2ea47a,3b1008b,53caebca)}, +{S(4f20ce51,84fb2949,443286cd,8ea201eb,15248749,6e15aab6,f3ea2597,d4cbf47c,bf37770,62691c5a,d6c6cb8d,c30fd3b1,73578a3,bcac43c2,c8404b6e,c085c97c), +S(17ceec36,7fad4365,520599d7,62c66c12,5abeff81,ce7242dc,bde4e799,402759c0,91e024e1,e2147fe9,73091001,984dd3c0,9f887257,73bbbedf,47f00c7b,c5c9f626), +S(dcffc4a,5557f2a6,ac7a3527,5a8228ec,325ea0bd,d9bf52c3,8a78217,6bd716,87658163,dcfc39e2,ad08a499,898a1505,988d9b86,72b6d1e9,64e6845b,cb41129e), +S(625fff43,45337aa2,1efa0884,73a6662e,d5340470,a79576a4,362e30dc,bcf762d4,92ad0a66,d0bea2a6,d7eedfe0,e33295b9,3a656e45,9220026a,5ff3be8f,83184187), +S(410b96fe,7db23faa,7d123e97,c1a82a9,7b24a26a,80143e50,dcc6d9a9,1a6e81d2,ca0da356,dce88799,5f8cc790,64d197aa,6532dcf1,20fe93c3,7e2ff4e2,f14394a7), +S(8136e51d,ca68170b,136d40fe,76980b4d,d4f435fd,e38f3017,99de734c,b2491551,823dd1ed,de1d326,676649a3,9d5ba083,7c48e180,ecd0a648,eaffe7c8,2894e819), +S(64a67d9a,a7a31390,3ed9e348,e7300917,6c1d52d0,be63f54b,aa3ee8f3,f1227a52,ef770581,df4aa013,6d191e99,cde7f42,a14fdfbb,11e1b709,bb540100,1dcda1ed), +S(5fcf0465,a905aa8a,a1da5073,d524e8a3,30af663e,f4307359,c03a4c6c,c423dd9a,58641e67,fa821fc1,2307a66e,fe0129e6,4ace7c36,c3f5395c,db87667e,5e742a42), +S(aae6747f,ac3d8c9a,148009ea,c2d25574,39a8b546,455fb3a4,5016891a,f9372dbc,66084f19,b1d97f2c,22e182c,d5450d43,d56636e4,92651cf6,22772a7c,5b46d9), +S(377dc2c8,13e36bb4,642d1e02,cafce754,2ac6d9fd,c2212edb,5432674f,38e242c7,8395dc2a,39d3b1cd,ae9d7d37,d0c4597a,11f931d8,75c8bcf5,8eab03d3,674d5841), +S(35c598c0,7ac5dc3,7afe6895,29d815e9,406e2608,292ffa0e,7b24df65,3278d8ce,e793aaf1,45f538f6,c133309e,53cb5343,dc1dcc74,985c57f,27361c6,aae23e7b), +S(e9666336,6bb286d6,c86f425a,2d5658cc,1d733223,9ec2e9de,c8ab7295,4b329845,908dc533,b6098e9e,9b737e4,b96e7345,e635f591,60bb3df2,75e165bd,d1cc5998), +S(46fab0d4,7fc6586a,31cf861d,ff5e0bb8,cc12b60a,53103007,c8974ab8,204af703,845ab7de,7654fb36,618dbfb1,287534a9,2d43db6f,2a2cdfe4,5365b4b1,893fbe9d), +S(4d80486f,fe748d1e,f88aabff,76bb82c6,a45db736,76ee0ff7,d77cdffa,2c69d07c,2a52044b,d4fc6a59,fd16dabb,df35bb6,8057ad4c,292e87b5,528a3847,c8784638), +S(2c1d108b,f4945c0e,df62cc15,6bf0830f,7a45ca,1baa636e,9f759bb8,bf078a55,81e34495,d269afbc,1be39ce7,5417ebf7,d45decb6,eaddb635,4480a7f7,8d7dd34d), +S(320a08af,eac009eb,7f254b8d,395d836f,ca6ea527,4e2f309d,dfb7f120,2f1b3cdb,75269a2,41199c0e,5f1cd9e2,d102a294,2324a3ea,cfae231f,99ccc5f3,e0af00c2), +S(8b893fcb,9a524af2,6de5aa38,8ac3dc,2159817c,7062e5c9,d5c9e7f7,dd889ad8,cec6a5ba,481f7e3,88c1cab,c11505f2,39afa143,68977ed6,52ea2d6a,19d2cbfd), +S(ea89b95b,36cd5203,22c8e5af,7461f9e1,a054f23d,2720469,fa74cd67,3a52aab5,5325d16c,4b866865,2437b6b8,598b31c4,59e6101,2cc1b147,e0473527,5935ada5), +S(cdfea79a,8c16ce78,75a9fa1c,5b7647fb,ad641ffc,5b73fb31,d9354a1c,3c1cabfc,184c309b,b0f52999,2a3f6b93,77b9e6a7,6da6b943,d8031408,b71223cf,b3ba9100), +S(3daaaf0f,785a6397,c652331,630931ee,a975c522,35899736,1b7e5fcb,409880fa,952efec1,ebd4c107,b26a98f3,c6683903,a498c840,ff4a36c7,ade569e4,c899919b), +S(7cea5226,a56d21cd,45bfdbb0,7fea6670,40247c70,ef43da28,d1f8bbf0,6e4ef2a6,b03edff9,6a5f32c5,6f8f63f6,ba9feb68,f43cca62,391ea587,81396c7,5d963b67), +S(1f5f4c27,847ba921,c63cef52,8b57fef5,5bdb5344,ea67a23,c173828d,c59199e1,d37f6918,e0b774ad,317dbcac,75c82188,c8a421f0,ff5756bd,853a05b0,c95ef089), +S(36cd4da9,2a77613b,6417deac,1d9a0dad,1bf0e506,de26b529,7fffbd03,4a4663f3,ec1dd8f6,6226bf5f,fedb071f,7027f9d8,d931e6af,e378396c,d0d296eb,a63737e3), +S(9835c156,1be21ce5,db788f1,3c0aea13,fde6861e,97cb2cc7,a67fbd37,17eefadc,7fad454b,7372d969,a6e754ef,c73415d7,fcd4d589,c8e255c5,1adf5a9a,82228f42), +S(5e218414,d9c9f0ad,35ded20e,76a484a1,d41b3894,ba9d3be4,bcb4546b,371d0e4d,76966ecf,9c8f1c9a,452cf971,10ca6555,2ca59d31,21092e93,333bbd61,6eb9cfcb), +S(b105f4f4,40e7ea66,e3272da9,d6b2b76e,d00e96a5,6f95224,34ca0df1,5340c585,9e8fea7f,35bc9eec,f82ae118,bcb6dc33,acae587b,f37b149,1f8312e2,4ffc86d8), +S(81d5713e,a732b4d,98d085a9,75cac9b5,3fa65171,e5cf49f4,684a7856,7c24fea9,32480cf7,a0bc0c50,a4de8200,9343b524,4a0f2aa8,ce67b11f,4a5482cc,6fa00bf2), +S(d47b4f1,88f5e7ed,a9eab2e5,8ad2c140,c6278d63,517bfaca,8cfb64aa,cfbcc7ec,524f6580,9d3ee034,afb1e64b,1a0f8ae1,1e464915,3722e345,bfda9671,1b94a5), +S(16e85ad8,6a953564,39b97957,7bedd0e9,6e2eed72,76ba269a,626fbd58,447996c0,8bf74f51,4bdbb6c1,4de81a3,1ff12aa5,de49bd52,63a962ab,b777439a,47eadee7), +S(c299c6b0,6e6c78ae,852bd55c,dea35f99,d264cb5a,e836b77d,ab209ac9,c05201e1,a558c66d,c9ee7fa4,c777b0db,288b328,428b230a,7c53d516,522ccec4,af0ae08c), +S(63c4624,35ef974b,393b05b3,7d1c89d7,b0d8958,ebd541d7,584e2bbc,7235c795,1d806446,ecfc7bfb,bce099f2,6ce37a49,61b53453,5b65642,c0cd23f5,a4eef9d7), +S(6d36d105,ed8cc5ce,53f2cb69,8ab620f9,469a3e5c,b25bf6e6,d413f414,c5af726a,1b45a3cb,1c889961,8d273993,6a3affd6,233a66c9,4bef75ca,3a8fb6e4,ec05ffb2)}, +{S(abdd85c7,a2f8bc31,343382d4,e405978,3874c8d0,1405ef14,85047cf7,f0f71d50,d5a03157,798fe828,a03ab63e,84bb007,b53a5315,1db7af14,e4ab612a,736232d4), +S(31297c6c,f567267b,3c8d65a9,72afc752,e8525eb3,de2958aa,76f72e14,5ad903d7,f4735877,a6c6d89b,d1beb50b,edd1235b,5f5e5d0a,878e4610,e7d756c5,34813389), +S(64706e69,11a7d0a1,2e587c86,91117a27,cbfa64ad,1d5617fc,81e55b8f,64e0da86,59d9af3a,419957d2,b3de7570,15fd8531,a5d07430,5cfd7444,5e7e70b3,57e62772), +S(a4988ed2,89d8397c,f1b897d3,b81d80fa,92658ce,2b935f4e,e2e3f0f9,b193af51,813fb8,73270570,50f23c3,f8082bcb,563ee366,1f6b2af3,f631f5f,916c8478), +S(3c2769bf,af401993,595a01d9,9c72a6f2,699591fe,4b869077,8c6b5ee0,f172a866,f3e0d8b6,cff168c,95873f44,871d27b,df1e79a2,d9e0bd4f,6f90f682,538c1f24), +S(49d93e09,df655edd,19548fbe,affce201,4c6822a7,e196eb98,b9ddfad7,3cb70125,26e6957f,84ced17b,c64f710f,b37656d,31ba088a,98350337,8f7323cf,c57f0eee), +S(53c06842,49997cb2,6e5b4cc,5f166fe7,db0ec0bd,52639a27,d8fedea7,214a78bb,8a8dd3f1,b3081dd4,948255ed,dd178089,a0fa7342,1ea616a5,1f639057,34a489c6), +S(7a8e7d18,3f2aa640,d0e2ea02,4cdca5fa,1767d9f,9ae38805,5d38e8e4,a81d33b7,c2fb92e3,5556fd14,4a894be2,3d97c6d4,30872ccb,25e239c0,d959a24f,aaa77397), +S(a5221a2d,29cf3ef2,f066897e,4ee74ac2,95b34cd7,54390814,297537ae,abdf603f,ec0ff395,8c3a1d1b,5a379417,5a61cd97,782c9683,aa2c7c07,5d147db0,2b134278), +S(3f49da9c,1d62eccc,afc4b88c,814e44dc,8440341d,f347de1,fb60126d,59578dbd,c3cf8e96,2fa9478a,f1be08dc,961734ca,5021a65d,b1b45f80,78fa8e8f,d2813a79), +S(47b16636,f5a64b07,534be14d,6ae37af1,9da79b42,666b201d,d0afda9a,2daeb6b6,f1218111,31ebd87e,e1fd7c9b,d24a0a11,a12e842c,6c00f445,6a342309,9b171f12), +S(a7b5a8ea,1f23ff1e,c1768eaa,2fef0d66,b75744c6,dc925d3f,9068beff,d69b32f4,fe01a23c,b0e5acac,e60058a2,889f9434,9ffce6c,b24f4a3a,662e0079,99bcb690), +S(3e14e2c,e7044716,d42e9b6e,54cb0500,a1e4374e,18917336,e3205e31,4a878b47,18af982d,2c78ac23,818644f1,ec657da8,ad66bdb,3d5a9b84,3f8bb988,4ef8dc3f), +S(71393e6,746d3072,830299c6,c1244303,750742be,361c5f08,6eb314a4,76f1736a,dc30a7ea,9b644339,ce8f1fe4,eeeba07,5bb08934,8135e819,cb061559,aa14e42f), +S(37721b73,e6861f15,7993696d,aded49d3,78994f3d,e2b573a7,defc907e,d6f301f2,ee0e1755,d8efb25a,5cf4d783,ec847425,746fa442,d460cead,56e73a69,42561de1), +S(7803c663,509fc9cc,55b372e1,dc0fa76f,149a0ca6,3db4516c,4abc0ad7,5961e4ac,356c72f7,794a70e4,db3463be,eaa847cb,256c58cd,b6a7d862,fde4feef,d896a46f), +S(32976d3d,a3d033cd,c073b0f,c7d2f445,25646893,fbded1c8,1a0a1761,a8703206,5e6b7900,a069ce4c,65cd84cd,f290f092,2b936bda,af6ea40d,9c36a321,a7c9dcfc), +S(c52c7b18,b5fae5fd,a4e3597f,39c96b8c,6d6c856a,80f1210d,a1ebfc15,34850763,b87e50f6,20b66865,307d0747,a02ba22a,b310712b,cb0bab6b,963c589c,c5a208b1), +S(7de59c8c,3922fd51,68f4151e,cb783775,cd0bc0b,1185ecd3,6ee075e1,14e66cf7,688e2cad,e4d49da7,fe691497,412c9372,c67288e8,ca4b64de,308eabeb,723fda15), +S(ac49d6f3,32cda691,c345f099,667ace08,f6496126,830c39ed,877223a8,cedc8be7,267e6a52,5ddae4f6,7e05f0e8,52b9dfe,5101c491,64d791c9,82249937,c3a7b031), +S(5a92401b,698cc394,c9b714c8,bdcd92d1,20fa72e9,b1311839,1c383024,f469ed22,e29f2926,84c255a9,a739ef0e,5d13cc55,987f5b3a,5c6ed623,29431033,70edb4a2), +S(3609dfba,51616095,ab060e77,d6779025,6456eddb,f4b651c6,442cd673,f893878b,f85fa9b9,442050d8,ad9d9314,9ede8698,91dfc99e,c05a2a48,5854cbeb,55a6c380), +S(332bb85e,92c90a15,3bf602f,45dc33ab,3dea5ef8,7034af81,944113a7,4edc94d9,59b6c879,5f2a824,96d631b5,ceff8708,507db41a,375c7257,7533d8f1,461fbaf1), +S(16e9e054,916b894e,730d7126,27b1bc81,e7689efc,23cfeacb,a423362,df49d87f,303d0428,b701f1e5,fe8ae460,e54adeb2,d4b5aa93,a9c7d7ec,7b903cdb,20e440f3), +S(4e62ab89,1d4c4cf0,2c44915a,4e2fd522,88d75aa8,e22b7463,30dbfbca,97ed9515,a7c21be3,3b5cd522,4de0bd25,faa9c2de,5d6b2f50,b50a6901,b7167b3,c4def4fc), +S(649eedbd,5612c429,2aedbacc,b846e759,4a2b5870,f21452f3,1ac9e697,fc055db9,ca694303,ee206a4a,acfc4cb3,1aa9f117,8fcbe37f,d6bd11a4,669289cc,7c37b0a), +S(e858f72a,5fe2314,951ca096,8ba59760,e3d2a667,e76dcae8,c3ae2d7a,4721afbc,f1c7131d,3e2d0468,ebb89bce,700e0eca,ae83afee,75620938,60e1fc70,cfc7810a), +S(cd351498,29ccb1e6,9b18e35a,72a24b30,e074793d,aab4028b,1c5eefe2,b7b3163d,6bbd52b2,93b43cb3,a19f6291,69ba5b2a,59785099,16842093,1e58dc1,a040c57b), +S(19ffe344,69ba3e43,7d901863,3be7c298,ad8b65a0,49c30dc1,bae30ca5,97ab38fb,c33ca345,efeec662,96802e95,69f0f34d,12ceb64f,30fc704b,b77261f0,ce5c98eb), +S(8a2f772a,a38a5b55,a7081d0a,61bc27d5,89f9832b,1326a230,d81b9f58,4d634293,a4707a0e,a7bf528c,ac62e361,6f09287c,182aa3b6,6862430,dcfef3c2,69d1567), +S(e583bee3,1dbc8f0e,572eb18f,366f88e9,3a7a0484,b4299b9d,335f7836,a222636,8a4fae1b,be122228,be9d8afe,be223c53,dac161ae,ca77ff4f,e14d9c78,894fdfec), +S(8b6e862a,35566848,50b6d4f4,39a25950,47abf695,c08b6414,f95a1335,8dd553fd,15a1f76e,f12ee34b,f2ef43d2,b14605e,db53c3a5,e7cc7c2f,27fc252b,c1641642)}, +{S(1c185b5d,e988ae93,eebe0aa4,a98396aa,fff2671d,8246ce2a,4394db96,7a541ad4,dddf4244,3532024f,d225f86d,133b8156,3ea8c92b,f0ddc354,88e6a04a,d41da5fc), +S(addee173,b29de510,58dc107a,47fad47e,c48bf332,2f7c138d,51060322,571faacd,3890624e,c6920ca8,6a9aadb9,90a15156,799dd771,901823e4,9f3bfcb6,8bf05d5c), +S(9e20bbed,ccdd5c05,d683b2ef,852760b8,e246e5d9,6a8900ea,999ac8bf,5e8f12e3,9f28e5a,b4257f6b,b826d076,ffff259e,1caeda5b,7d34f658,ddfa0401,7cd4768e), +S(dc825d1,5f707c17,46920014,9d7be56,acf2d1d3,c444fa27,cefe5019,6f5b317f,226d4960,2f349d4e,9cd10a57,6bf01681,e4981cb6,4235d347,72a1e5ce,7694a20c), +S(812e2cda,3f473919,1f2ac087,7d70d0e0,2ca0551f,10c96d95,51ba4199,22780f90,c8d05855,6b6364bc,d9aff119,5b9ef26b,85cffa72,1e880b83,eed73cbd,d23b91e1), +S(aebb0870,76f5918e,68194fce,42d0b925,cfd2a506,359af62,146c8ceb,8502231a,c1c19605,e0456cb1,3e57229b,45c83ff7,c3695a2a,31908bc0,31b08140,9c2040fb), +S(af7332f3,3ec274c8,d34f47ac,fcbacf8f,7980cca5,7220012b,8fd5ff94,cbf3d8b,d439f1a2,89799dc8,92b7e53d,a84a9e82,7bf7bdb,7240c34a,caef59a,777188a8), +S(baf71cf,7851a758,12d7e09b,192741f2,1b5d2a36,6e31892e,e54c282c,81163a6b,1ef3bf2c,a81acdfe,17cd6b45,266241a0,22caf299,ed4d87cd,68d3d4d4,526b4d38), +S(615991c5,d250cebf,8c7bd1b2,969213f0,403c7da1,a3ba913,44fd0490,88c2474d,4f014177,12142cca,163f29a1,f5790004,32726712,93c2e87e,f46d2138,c14040b6), +S(b1574b39,26ff27c7,b2dace6,6108b93f,3fc75d48,478567cc,66bba29e,750c0c86,2759967d,8d05be25,3f88a66f,830dfb83,5139e32d,6f42cf3a,43b199ce,ab85b55c), +S(5fe92a7f,d1c407fc,967dbb19,e9fd4d,8361aefc,a3d5a9a4,2f8237a3,458e5e90,9b89ce35,57d469b6,9b9034d6,cee93609,c291d023,d1d9349a,d9bcf47a,aed8b2a9), +S(d65b0a5c,41b2dc6b,e5aa87e4,adfef952,621f9d99,550e424d,176ee180,65f666e,a76fad91,3b80b0cc,3d3ceac3,9a465cb9,4d8c79de,92adf9cd,7a4efae7,c85c30a4), +S(61d7de65,fe34ec4c,f82f67b1,75887263,11683263,145cbeae,18ed192d,14ced48d,effac049,d7e5412c,5052160a,6d1b2230,7157669b,856af0e,e3e46a25,2d2ee19), +S(9a77de66,5165a9be,3dd2758c,39aa7a14,1f052358,fbac634,5ea701c8,c93bc4a9,1044e428,bb381024,70108d56,b8b3e3bc,7512300c,5a6ae44b,93b2098f,a13fc023), +S(4ccb2426,b6eed0f9,651f80d5,72b23e78,d84d6928,57648077,3663216a,bc8a418f,1840c,bf5ccdd7,19f7fa8a,33e7151,aa351d,2f620972,6ec67d1f,fdada8c6), +S(53f96bb7,86516e0f,2ac7611a,1aac61d3,cfbca76a,9609fe,e4cae036,52f68271,97e3acbc,468b777b,bd1c8263,5332dcc4,b08b9fb1,9608fb35,cd51e49b,f09d9087), +S(395253a1,b6a349a6,dfdb0d6e,65695572,2a597411,fdea9294,6e48d107,596d8ac8,c57110d4,dd0d35a7,663fb3aa,e15d8c4,dce5cb67,d3173d91,a6ff7ea2,1bd28107), +S(d7becbeb,c47d0de0,899ffdff,8620124d,b29c8e5a,fd71a0ed,76e0df76,bd4e26e6,d8b52fa3,e16e7f19,e925e769,3b5a731e,18f3163b,1ad4db34,7f878381,da75fc15), +S(fa3afae0,4f8c1fa2,778446af,88e29150,256b0413,d9bfeb44,45288e41,29be15a5,9dc76b39,574453ab,2d46cf23,ce8cdb06,cbe7df15,d03fd390,4470a86a,822028df), +S(da56da00,dfdd788f,90a12219,db03cac1,403efef3,ae706031,e153aa16,b3a52eb3,1012ca8e,ae20efdf,41c77128,93fce97,fc66b187,caec55db,7aaa81ef,cc12838f), +S(f07866f0,cca3474c,40e7fa59,26df41c0,e46fbfce,faab92e8,9ccd1170,3ccf79bf,37ed66d5,5e891f4,e644fa04,182f34b6,1f1aedbc,d2a71aac,dd1523bf,f579dace), +S(853f72d1,edc4d953,348528b8,3656894d,67032333,28a16801,64a84511,15c5e968,197737d0,de83e73b,247d861,510ac76c,3ca214d8,ebbb958a,74ce1c31,5e715e87), +S(7c7eae74,dce7ef76,9ef0ce86,cf6ac861,ffc3d4a9,6313f379,fb2c0325,daa91aba,131e1689,fb017e15,e869d858,2dcfc4f9,153d1e8d,246fd613,b3922302,5b5c3767), +S(56028652,bce9dc6c,c8b4ad5c,f9f222c3,412e5bb7,8d556225,2c06c2e4,25a6e31a,2aa8e093,d8653f6,2153972c,e54806ba,666d49ba,3787934a,a37bd65e,79374ac0), +S(2eb98b01,550a00dc,59d280b0,71e472b5,a65eea89,298fe4b0,9f6f0e07,9315ad7b,74e22fc5,3daecdee,6a0b50be,e17f1d7d,d9481e77,28778957,274c8d29,a0d89fdc), +S(7268b61b,f65d73aa,37766a99,9960d998,18aa4ef2,ae8a97b1,db8476fb,32f891a4,824e99db,3157b706,38573513,618f6901,ce1fcff8,f25019d7,c5c45bb2,f3bb4d47), +S(cdea8771,4a764e3f,a94e7356,879b7d9e,da7c5b6f,28c172a6,1c0ab012,9b9ddd7b,daf49b12,34ce20aa,263b4349,f9f88cc5,1334db26,59b7c587,212f407f,cbb2644b), +S(38831af3,193c8860,690a9d74,cc0079c0,9e3ae0fa,ebe0da02,67ad6e23,99059d19,be188500,5193f11f,e34e70a7,e470cedc,902bee92,e7041403,c2f1ae53,7a8bb077), +S(df3acf55,bc6d97ce,34ebeb46,34fe1010,ed3d6d6,383c11a7,53a93123,e9c8381f,94c22735,ae6f858a,59f663e2,12532e14,4efe6a81,981c7619,e5002968,dcb81df5), +S(a8b08b86,4946ac6d,a101eb85,ee7bdf55,24282ca1,a9956e4a,eeb1afe9,87a43ddc,80ff0174,375c95fe,cea6e00,20f17123,5cda4bba,d4ef893c,78ffe37c,205d5577), +S(fd9941ce,e1c26864,248f7035,352787d1,aba9e93e,f5edd333,d08c89b8,4bb9dc8f,85be138a,42bf190b,921681fa,8777a619,878d4d02,11016c72,8bc151ab,1a687b5a), +S(7e2cd40e,f8c94077,f44b1d15,48425e3d,7e125be6,46707bad,2818b0ed,a7dc0151,6fa48af7,d523054c,7d59e574,cde106a2,776411bf,5111f7d3,65c43ac5,df8ddd68)}, +{S(282c5bc7,5b9c3f76,73e244af,29f22b80,c171bcac,d7e39b4f,ac7ba6cc,656d9010,e8db2eb0,a56699f1,73d5b5f7,99d39e14,dde34648,f159ad0d,c1c7767a,19ae72aa), +S(d5a3eb7,cad9c548,839ceb7f,67145e0e,96c60d8d,68415f76,73b73700,5ce3a8b,34ae4e12,355d1df1,25149efb,1a9050fd,490e251c,4404fb37,31b6dcc9,e233b9f1), +S(68718b29,86197989,3c5f7b1b,48abc254,13bd23c8,96c8828,7000ebe6,6db742a2,bef9d2a1,bb596fdb,3caeef3b,4d950568,8a94b436,4fd85f37,30b20de,b9e9a4fe), +S(a5ed1709,7932011f,9a597511,a526e61d,f22d0bd8,ae3e15ce,655820e6,b1ff1238,dd6dc7d0,81e70b57,4f235c10,c7c3dbed,a57743c1,15e6fe01,a20ac751,34cdace), +S(ec37904d,9662a487,1a471d9f,778bdef3,430df9e2,26fc42e4,7fc4bf5f,80e3a9c4,8413f2f,1ed55d8b,8448e336,ac9d1418,c48846e2,acb43509,f1a3d935,8cd18ed6), +S(b09f9c76,1cbbb685,f695328,61c459a4,d2b16989,3cbbad2f,dd49c87,1d860db4,dbbf9049,31b18454,21c41f2d,2185e3d6,c8f55dad,f1514ab0,655c856e,3391278e), +S(ed1a335,48864e9d,b41abcad,3344f4e7,ebe57841,9371095a,6b95fe4e,ae59edd7,d1146be9,83adc77d,bd9c107f,3092a9c2,9c271248,d78ce913,7831127c,c403f44c), +S(d39507d2,6ba377f5,c7116aca,78df331d,a795cdfb,54e5c912,8558c0e,997b74f3,fa7971d4,32cb5a44,589dc705,de28ad0,382206f1,6e11229f,86ff82a,efef3131), +S(641dc311,baa77b14,35f408e1,528445df,53c5ac5c,32803a3d,84e8fb66,60d71d,2b355a47,76980bfc,8a4e0ac6,a3bd7b02,a6b20ffb,f8650b16,89010456,6953e49c), +S(c3dd73bc,cfb744bc,57bc95f9,3c260df7,7f3b7e74,9e4a5237,f3865f8f,f0c89489,15d58dfb,83654754,80e53bb2,a1ba430a,ce213c38,ea71c7b7,51b4143a,fe2bd124), +S(3f883fa2,e834df1a,6e9b5c46,b663880e,9a0b42f1,891b92c,eb30600d,5641813e,7acff194,a5fb76e5,bd3d8fd4,2c7f07a8,3a991ba6,7f0e4edd,c55e1d8d,3025d590), +S(70c8d4f5,21d808e7,221bd5c6,31f171a9,5e7aa358,4477068c,2549c59e,228688bf,52badf20,6106d5a8,d7bff921,580eb5f5,3a53aeff,e0369f9a,1bcb282d,7b4cc26c), +S(ae08db34,874d39ff,bfb9c92c,5e4253ca,7ed508fd,d12841e9,63600461,b2b3a0e1,dc0504ef,d209be71,381eefd2,91508120,fb6ec1e,df62d24a,cb1d77a9,4eebf1be), +S(627027ad,4aa88477,c6240517,87a25b88,7d5b98f,b968221e,8e45d584,3db6e0bc,34b553a7,c6ac7004,69367afd,17c5eaf8,7605ca18,6a57667b,650fd8c7,18ab76aa), +S(573ef2ae,72ae8fd5,eeb0b720,f8967b44,f7d9d96d,c8a4825c,23a3dbed,fd2bb442,264f508e,efe5e0a2,55e55a54,bbf739b5,757d0a15,994ea9b8,4e139738,7c484786), +S(28907848,cf84eec6,773a384,ce5f4a5a,24c8afc8,606488a2,ccdeff25,6f896dab,ea87d4f,41df8727,2fe84537,d11126d5,62618bee,a51008ca,895a8375,4f05ec5e), +S(163db4c4,536114f0,75521627,c61f62d9,14214d64,ac9e66b0,d85b9991,62c54c5b,7ffc24e1,11c0c845,1c574e9b,e4b56610,696d3667,a6e6abbe,9d1a5ea9,4122aa1f), +S(2ec11f2b,71ffc28c,fb5e779e,7739098e,c969bc78,3cca5a22,77d2bf53,e6574d1c,c5e9fdf2,89a3090d,e24d13a1,bc8377d9,de0bcf4,63482ebd,9620cbdf,8b238fb0), +S(30549ae0,9a46820f,f49423b8,9c6fdd48,703132fd,ac166dc9,90609a32,49039f0a,b8c94b4b,36d39247,732976e,fc16d500,ccdc07f0,d7b97db5,7a1d9fbf,d8cb5d2), +S(98685f94,a043399c,89e74674,240322ac,5a595bf5,41aaa44e,d781781a,10fe9225,4506b1ef,7561b811,53f44e8f,325f9222,e999013,30523885,d8c59864,db13a3ef), +S(9acba196,3014ea9,1baba28c,e273c6e5,b9a2e1a0,98216c3b,194a0b56,dbda7db6,f0a3358e,eec737af,2e313262,b29ddf6,2adb9e97,9f56def2,65102272,6b976290), +S(e81e91a1,7aeec75c,19df0ef7,fd07ce2b,e477c031,179e3f99,4ca13857,e2b5ff93,65236424,aaa8b6cb,fdb2fb20,46ae3c3d,839d97ed,217edc6c,c774f889,d2ce8b45), +S(61e60aeb,821c3f80,b53ca78,2c3b4d21,960eb0d2,c90202f0,a5fba651,1725ddba,b029275f,690d10bc,b52e8bb2,acceb071,f07834fe,64175088,a97e26f9,8ff34432), +S(a931c410,8fcb4346,6802dd4a,8ec2cf62,cf4c5558,8129299c,b3a87e95,6fba2a34,c21a8dd1,43d4a639,339fec49,f56d717c,658a55af,ad72da1,c754fb9a,117cf6e7), +S(fb5c106f,da32fcfe,ffe9b5a2,7ac8cfd8,d9a73da1,5fa6340f,e09d187a,7a83cb88,a3f4c60f,90003375,11c39100,ba4c4721,c6c138c1,cbae09dc,46d1a1d2,5baa95fb), +S(dc25e18a,18f0035c,2a05e80d,d6b38830,7fb3b176,a66ae451,8c5640db,7540dd24,3ba83beb,3e6b29c2,97a0ead6,148f9b93,711556e2,e5fd513e,ee649228,c62d8f3c), +S(ecf7b689,ca03b840,739283e9,c3003a01,65ea3f,3bd17e06,9260c0e9,ad403273,5e0dbad6,47997fca,c0d7ba2a,170a7954,67036ae8,e61dc532,b5fe184a,deb8a7ea), +S(263e593b,10c982d5,1bf89ec9,71f6a3f4,60e53bf4,cb2f6af3,2381214b,2e981bbc,e255bc11,11470eb8,894ead67,c618d7ca,a6b76ae1,a230ef05,3830d028,4a7faf37), +S(f79781e7,a4137ac4,7a9a9d00,9d239b37,6cd0fa3c,b9f5de46,8cba5a11,ffcdd69,d10ba78,896e086d,5e25444,165e79d9,7b3d485,1a448e03,26d8906b,2f27745b), +S(a4d28024,11f577c1,c5d08fbc,457a46bd,428f4d2a,b29475ea,ef622876,593e49f0,e4855491,ac0342b1,dc804bc2,7ae23877,82eeaf22,52874a00,4d4e0d66,b07b434f), +S(87195a80,dc83be4e,cfc9d4b8,29725cbe,11101c26,13c98f2,641753af,1ee840f8,f9fce233,66931c51,4ea09224,b565dec7,22763d8f,6f572057,fdd7d96e,9811289a), +S(210a917a,d9df2779,6746ff30,1ad9ccc8,78f61a5f,1ff4082b,5364dacd,57b4a278,98f1e4ab,af4a1a84,85c6417e,7298c82,c87619e5,500df403,80d8ec01,f384d9fe)}, +{S(6c16768c,433a000b,adc47b10,ba4e132c,f5480e65,ae83d1a6,8aec34d2,c00c76dd,ca1a47dd,66c2d74a,ddb69283,2ea289a6,ae679669,edd80bdd,4ffdab2a,defbb542), +S(f757e860,ed04db61,594bd647,5ae81b7e,d6d2fa58,80cad10a,7756fa26,810b8543,f2aad599,4e491e02,9b1ae256,2c90912a,1c9aa6cd,dfc2331d,a0069a79,861208a), +S(d9f269d2,9aa777c6,9989d706,91403dfd,9025b491,ba03ddf2,dab8c9,af7dc764,1b39b85c,cb4749fb,7f47086a,ce63924f,3ebb4213,af5dfc95,fda796e6,c6b0b8b0), +S(d05e14f6,fe6ca50,a23e9339,e449f771,98e842d4,80a72d5f,8f5e4c06,43547cb9,869e3bea,6b19e0f2,de83fa8a,e932086d,d66e838b,637ea603,5f0fa081,5912c8f1), +S(9f723d81,1818046f,b7512abb,faeb46d3,52d8da9b,3abe819f,3861f9c9,963c583b,37df0657,3f2bb428,6c8b3efe,f6941e2c,4e48d90b,931cd0a5,78a4b52c,6581ee7a), +S(e65f0b42,54ad9df9,681c0db,3428ccaa,11194df3,9e837aab,4564e1fa,254e6f97,43e59d10,54aa35b5,80373e08,cfc02aaa,4f581406,58341e67,7e50977e,5bd893dc), +S(605d61db,589fd91,78a65cb5,99697001,73afca81,23f663ac,5fffd0b9,8f5e7a64,a976a9d,a47745f,447ad4dd,7eb63875,c228b979,a7dda50a,d994df7b,545d0ede), +S(43298aaa,faf20e9d,c2240256,493ef19f,5630db6b,f6376e4a,356bd034,98f1be7e,737551c3,653af1cb,4f8c1e8a,347f50ca,142c03e0,29007f29,c76bb763,b52ed053), +S(8afba782,d6373f0c,54734f1b,4b854e63,bf2a3b4f,23f57a3e,6c2eae86,25c691c6,da3054aa,228b9757,b7321da0,64516249,652fd814,15017584,2c7683ce,1a3638a2), +S(3156d359,e01d64fa,9c8fd8b2,c47c3492,82dd459a,fe89d94f,35906da2,c6d0208,85a23f48,f0a7448f,c57ee545,3978e2cb,6f2bbe54,864477d8,a63775f7,7ff7289f), +S(88e1a44b,1e206eab,49fe97af,30be5e47,25e1011f,312a5222,e9e80819,b3f26357,63dfe52e,10317c4e,227b2a5a,d61b713e,2d93aba7,f0a51e0,b5621191,40c58ded), +S(d10de96b,f2e4a6f8,78e0f37e,15569f3c,b9717a38,7fb98928,6d65d171,4b81b301,2c0a46c9,6a2e3884,e7f3b146,acc170d2,6d12b959,4b75a901,dd535016,f0355ae2), +S(eb36b865,812ae4bd,7893e50,5aef6f14,bfc90c1b,42e75ea5,91615aa,a2101784,1523537b,790b2a61,fb6696d0,a622ff31,efea1255,e1dceb86,763985e2,6c5ca8df), +S(d54be697,e41396c6,3d63f96f,bde9bba2,36ab1649,3c156159,3df81d69,c906fe60,89de1769,433b5678,1f7a2206,ca98258,20d0c7dd,a6d5b672,f77029c2,6bb59f33), +S(d5aa72fc,9063c2f8,bd0fec97,da30dcd2,a5b36aee,7894463a,f7042795,1998c979,ed90095d,bf2d3a67,2d2f1a0f,9737c4d,e8f3e773,c04b77d3,a2345dda,ab662199), +S(ee8cd53e,8ab64281,b5cad323,f03fd06d,7aa166eb,df9185b4,8d0eb0e6,1bd0bc61,4781263d,fc518e4,809e5ea7,7f9df238,1ee7b6ee,ab25c1d9,5cbf3fce,72fdd615), +S(ddc5e95e,2a46736d,9bbeee74,74364bf9,e1d2724f,c43522c,cffa05a8,7d078be9,60908fb,809ae964,98a26cd9,67532df2,409491aa,73c46803,eb58861,7ad86745), +S(ae503a2b,b6250ff6,6ee615d8,41cae4e,751160fe,aef7f33b,dd1feaad,ab236b58,6e53d2a8,8f09f78f,96d49a6,2f98773b,a282b575,ab6e24f8,4d604c3c,8583a66c), +S(8a502d08,bb3cc1da,438c5780,e2120c50,12c1a972,8aaab6bc,5d908b6e,2a83dbdb,fa02e62b,85d7de78,8cf11d9e,ab406523,bba1f912,53006a89,f3a491cc,dd99575c), +S(b52891bf,5acb0019,a8d10b94,87fa47c4,22f83a00,ef539e91,44bd9f7a,d5075716,ae58653a,b167a9e4,b71eecd7,4f4a8786,19c19026,29c0018d,9f082e7d,5920b2f), +S(176170af,b40a3e79,8a88b98f,fb6c578c,d1ab56b5,b7f4142e,b2d84a59,1b009f97,6e1d5682,36176d58,ce5fcac6,4d4d2885,5db107dd,5215c71f,b4076aaf,d31c5d24), +S(27359f7e,8d3e3dcd,ea35f603,d8fb15c0,bd63c08c,3ae4d2fe,8cb6434a,ae2e716d,a0d64207,8fba00dd,4683d597,27f3c67f,3c25075d,802a4ea8,eb08c20b,47a941f9), +S(1e9d955a,2f68478,5227e866,491990a5,62c802ed,1b106bb5,42d02d29,93542067,fbf5f466,57707b3,3f116a23,fe5991a8,90a9583f,fd063943,37a19aa7,e125ccab), +S(b5c4c10b,1b0e0813,37deef6f,c089d2e1,ee14da2d,cbb48e6,74b7dbd4,429e22eb,fdac245,b109947,8f6a53a2,8a1ec4fe,da43b4a9,2ef49e25,8f9296c2,88c48378), +S(bd2f651c,ff6e614b,1d6535cf,c3a2c5d2,da5307b0,1cbd3f48,655623c2,55503916,3715b0bd,bcf29d97,63d7fb35,f9c5b54c,769a863,3517a2d6,8dde74e4,c4277779), +S(c4c2a8e8,f1e257a8,fcd0b11b,30635c5e,782fedcf,f99b0b95,828e1369,3ee0af73,3a08abb4,777a066b,49ec4c01,aa5b0868,f100a473,e555def1,7c5a9d84,b1fd9ae), +S(4aa77ce,15dd97ff,47c1125a,77bcf0e9,d302b79a,e8a919c6,a875e5ed,eab8c2ec,31a09dc4,89a90240,334836f6,302d7e81,57f19762,a9b5f727,6c276e63,560a9d10), +S(3731162e,66a8d2d8,ff4df3e,95950103,f5a03f9a,86bf37e5,605746,cf4be846,e19d7835,10af5daf,724be4b1,932dfac6,23899ccf,7e3e58ab,5bf3265c,667cbf28), +S(6e2114ed,297ba44f,c3926603,bc03a87c,af3e9f2c,8bd84e64,afcd9846,bd9c8f0a,faa6e5f3,3d90f91d,82592af0,7f3a26e2,f6f4138a,2f06a6a1,4286eb71,2e95cf94), +S(50d775b5,f72d186b,266a14e5,254cb5aa,49fd8633,81c36b09,842632d7,ff004130,cbfe11c4,f0b15aa8,3ae8dbcb,9feff9c0,d68a26ba,11095b3c,ba94739a,2091bbc2), +S(8f3ccf31,f8b74b9b,624a5d1b,7cb1096e,202fe5e7,233777aa,859864d3,775732c0,980e32c1,d2c61ace,7610b926,6831ac27,56f19788,ab7a77da,18c421a0,9a46bad4), +S(24cfc017,6da2b46f,a8bb5bf9,636be1ef,fd7e297f,29122fb3,e84c9ab0,c18ada5f,14007044,f8639e59,67978eb2,a21256d8,126a635e,5b07eb0d,97059ec5,6875a3c4)}, +{S(6efcde8b,e99d9ee8,ee0d6b8f,a76584bd,5deb67b1,415e729b,48f3d41a,f1cc1386,4ddcef4a,a6ad5be2,d1e83449,9eca4a8c,73219f80,464a4a9e,ebb7f3f1,b70c9fd2), +S(24fdd052,a1e535e2,4eb79ffd,f03093f8,5778241a,b845b548,31b9a82,730ee3e,c6ff8ebc,7edfd9df,9b7410a,7f54c93d,ff0c682b,152bf4db,1b79c0a6,9889797d), +S(aaaf86ed,31dbf395,687c959,6c445b6c,dcd1fe7e,ba2aa13d,1bf41867,dc87b30b,582b9683,729be5c,85fa83e,2ba2abac,e3037262,2a24aa35,c7753312,1de2fb93), +S(23930d0d,2400db1d,56525bb4,67172fe5,e930bf86,21124fa4,6c5a421e,10b059ea,5e11f202,beca3d5e,dc9b282c,2e86d5d9,30f8f5d,27a5c4d,d2cd6dde,6d1497ef), +S(76ad10d0,bc92ad8f,55b665a1,8ee84e01,2e92dcc9,37e0c604,59bd6fd4,ded2d9d2,d4ca7386,ce0c4c5,fd37da9b,eb8fb040,97d98891,35a5ecc0,320dfffc,95ed3c10), +S(10b324b,e959f0c8,c8a82142,4a8f87c1,fdeb7f88,258e1021,c9cfaa66,b131f3a4,590e672a,daa8d547,161f635f,153fa12,b3500694,677af3f8,90dd4ca6,9620bb56), +S(afdb2acb,ebfdc11b,3577177c,d8bcde00,65151495,79c6ad2b,f7386275,a9314317,87d7786f,5ba775c0,ea47b141,cd0d4274,c7e58634,b64e4c58,f7f0c595,5a307250), +S(574cddbd,59b34df5,30f5dc4,61884ebd,1c867ed5,6cb60832,4a11b1f5,29ebf733,1bfea852,34256807,1fdb4f5c,485caa73,7136e3c3,b4b8a39a,389d7dc8,724f16fa), +S(97ffea2b,5de34e1d,495ce082,d0097b72,e7daddb8,1cb622c,2c14e814,bb0ae278,45fd3d2a,9d1e6530,61b4a356,65aa9e70,43766950,622e1c25,2baab265,6d4ed95f), +S(de627f6a,a7b56d85,535c398,80d0276b,b910ce4a,6066435b,512e5e0,21ef55ef,753033a6,ef89f053,c0e662c8,1557e6cc,d8b4c8d4,5204f5d,e1c8c742,72b96277), +S(b126c35a,d7993bd0,164867c,58c88421,d2cddb6f,d16a5966,7d9683bb,1b428eb5,a9ed1bb9,e9eacf9e,e712bcda,be629143,ad0e7652,82cbfab8,e586cb07,131e5b1d), +S(847b90cb,8837f488,ef027a05,71b2be6c,620cf86f,ead235ad,e76e3408,d0dd1073,9a2667be,6ce36e7b,d76117a9,fdb8f1b8,7c7cb45e,1a03f53e,7b32bc63,6f274b4e), +S(8ea21457,6f1bc2c8,fbee9065,c2cf5c3e,7a3b9fe5,403fb980,96040c2e,c6fe1488,dd75b454,acef4d3b,1b355a63,f0cd17a6,66aa9e65,f1262629,f0c7d1e0,33571b58), +S(9e11bb60,ba4b2d5d,fe53bb9c,598edb16,f3926bea,1cc90eb4,3e05ca34,e83f1896,263d7ea,50d83b8,791fc911,a4f73cac,4d42a09a,85f5ae68,86cb268a,fd562dd0), +S(f741856e,f353a7c7,17db9a13,4ab4aaf9,13c1f7da,cef08bfd,6f81dce5,b1130e20,ee63dd2f,48e15f68,d1acbf49,705fe010,231e26b5,14146c47,1a689960,d568eca4), +S(671ef05a,410b41d9,3b092b04,e523468d,44837d2d,26dba670,27400715,eeefa2b5,686d318b,b49eccbf,40dc18af,f1938fa9,b16d4d2c,c19dd2ac,47c7830f,89b8d55c), +S(59ea10a1,e5bffd6,a34593f4,b986e37b,5294ddbc,60d92906,52c6e9d5,75f88c55,c0a2eb5,6d04ab99,abb5b76f,7b2b55c9,c09d44f3,3785e3d4,64554c3e,dc7a334c), +S(37f2c7c5,69c6fd15,9b721d8b,8d40138e,a8873b6b,452af0a7,89a50551,e9703c1e,d114109f,f9e6d7c0,c51a542f,b2e3ef5b,74654bef,5eb84aef,c87f5096,2a30c078), +S(1d280637,9d77e509,126a440e,b1e56f1a,8723728e,97c92420,5ed13081,98c868b9,85003989,93a4a600,dfd0f092,b13a1186,c1c52dd8,8fc8eace,9bd4f1c1,b47741d8), +S(39aed6e6,a123061c,228d3d1f,857e0c9b,29b70cdc,549b4ea1,9fb6802a,8d8beacf,6bf86967,98e3dbf6,75865a1a,b86d5bbb,10f16e8,4f9104a0,87602c,e6946aec), +S(aa770804,4a996b1d,5bda074c,941b30c1,d1474da6,47d7c4ed,248d3a9f,c3c3c525,10ba5652,b20482f,fc2434ce,ac185d8d,af6cc6e3,61e77d3f,258abc0b,55610879), +S(7626a96,9f9ef188,3ea82737,83e5571e,4054fb73,6190977a,299c2a05,d29f60c7,172d4618,e5f88055,ebfcdf9f,55267ee5,cdf18396,1d5a2a22,55b87ba3,c7d7d2b9), +S(1a550684,d1195da7,c4b09349,f149eae2,5a1ef23,c9a81109,115d93e4,c98cdbda,c31d7dc1,b7efd019,c491010e,30f90ed8,7503c4bb,1b37fbce,3dae199,6609e3b1), +S(8d2306d2,dd8c753f,2b093e62,86fc282,ba3e1dbd,7b4ba101,83d1070e,d2d06e67,97dd14c2,d2c1d147,486a3dfc,d608ba28,d63fc03,251a106c,9811b088,978e3a5e), +S(f31a69ab,97039589,940851f3,cdc98bc5,d400ff93,63794f81,4d88215,4f00db99,56e510be,7f41b009,89792401,13641590,40f5f305,f27fe1e2,db1cd7ae,887ff5e2), +S(ce96b3e9,6eb94b7c,8bdc7b57,efd2186f,62e23e72,f11337ab,be7405d3,43b4d762,cc0dc898,33e7a632,d0fed52d,487f2c60,96c69664,f698a71c,3f7f8a29,4f181add), +S(bed97e25,7d31b5fd,36b41227,462d9122,706da670,d432ceff,5d37d83e,fefda56c,c0cfe30a,2c3b3074,1af492b6,e3797c4f,3b00593c,6806e2e2,f2cff401,6e877ff6), +S(923bc6c9,960f6e4d,ca2a7070,6d160e19,83a16b6c,8535783f,12c5ac69,4aad226a,c47f08b,6b2056d2,8e9f119,9ec6d5ce,b48801b8,7ee56280,d6a48352,b2b42b1), +S(b3cb28fb,7465cdda,85823dcf,819400b,357bce11,23d9229a,eddb8262,53b246c3,cf035ed0,b624dd68,8a31eb5f,3b28e83e,c0bf9453,649dfe4f,44078eac,9345f2a6), +S(c69aa2b9,ab3c5e45,8713a912,e7f95bb,4e5498a6,d4090323,5cfed1c9,c2a60709,2e95e198,85637d55,744e32a7,98c02c7e,b29f3bf5,7cf7c870,7cafdf41,f6710f0c), +S(45996af,5795ea6e,43e0fd29,671e119b,8b8d6ad2,4eac90ec,d27d5db1,b4fb6a39,2c0651f3,1c7d93cc,98d79d7d,f21110fc,8c1dd74b,f1ffefac,a3d52484,f8da40cd), +S(264559d8,7829256b,ed116900,d82d0c37,9f0e4d12,53c68e6f,cf2d41ae,7cddab8b,861a42e6,d92caed3,108439c8,fcbf8d28,8579ce50,c6350e19,3609b4b9,ffe217bc)}, +{S(ac13e0db,846df259,fc38d6,3b0248ec,9fcfa8d4,bcee368b,1f4a66c6,d41f9163,6721b0df,2ed06fda,9b485cc2,aa56f1f3,3a913e99,f6809577,95280451,9ddc96ec), +S(b6b7227f,228da269,a0e5640b,8a2cfa34,555ccdec,2243dd0d,4b2da0a9,b4cb5e4,6afe41f9,dc129d28,4786bb76,17786c8d,42597435,240f998a,f970620,90d32059), +S(460672e4,4f2768b9,842a98a5,ca8b90df,7d9f52bc,3fa2665c,4272d7ad,5f48b992,6d7f8b36,13b38fa5,80ed1d4a,21d196bf,be491b46,49c96da8,342441d3,f4af4ca2), +S(1d3d4ab8,b7c06136,8dfd612a,25f04016,2bd3f6cd,cd9cf58d,c11647a0,d28b629f,434dca08,17bbf964,eb0a316c,b2b0df20,eb2e965d,c4d8d795,1d4a6e66,5ed23d38), +S(9c5d6342,303bc7c,73763cb9,2e3d7069,7d913f6c,1a6e2f47,39470da3,22b7c1b,825c1a54,395b9b41,ccc2e3b5,3fe1092,f04c382f,b2ee3a29,fd59e255,5f22fc15), +S(5c751285,a4b55535,fe24fe27,568868f3,afb43cf,aad1d2b1,c6367daa,ac9493b2,e2388ab4,414c4e4f,e34a4c78,c869dd93,7094ce2d,4f4666a8,2eef8047,3b681fb6), +S(8c1592f3,8d73e277,d3c18d4,ee3eabce,32e86f92,2b27c2f0,1f50509e,74dbc211,399c9507,43cd9e7b,200c255b,bd25670e,cc111462,2d5dfacf,9dbebdb,53ee59ab), +S(85320e36,5f40235e,621171d5,c8008845,890f521b,d7e2cccb,88c7fb7f,a08200c,96bbe8ec,74b5f1b1,2a6ecbee,2aea6f0c,b6d0657,aac3556c,ddc4e46f,17cd31f1), +S(5fc21f20,13403705,687b8ad3,387a725a,f2e6300e,13d28403,569d4cb6,5dda7f34,bb709fbf,be95aeef,c00846b6,a4645b72,dbb4e1ed,866edb57,7bc5d9a8,2b774b0b), +S(fa40e658,588f8a5e,29d380e9,c1a6482c,888f53e5,e331f8ed,9b1273a,56d094e5,5486cff5,3b7baaaa,de8a51a6,1d1a9255,95ecbea3,39955c0e,172de4c3,4762eca1), +S(9f87d04c,5fcc48f,375c40af,3197c28f,c62cf3c5,10fcdbde,5aec79a7,4e6a6bc0,9a6cc172,23a770c7,a03b5333,6922170d,e9409f3b,9846b9f4,41832ae1,f70f5c57), +S(bb66dd5b,2f88786e,736b50,e536a0f0,3d59b86,511e95ca,1bab2cb8,875bb187,f8215527,c76a5af,e5e76a33,eb2622d,5f822ad1,d284c68b,c840f65d,4f731c63), +S(74d223e4,bce2d6b9,b2fa9482,46327e04,18eda6c,cfdd76c7,cda8be62,3f6dd3a9,b7276bb3,817c0e72,e1ab002d,50531065,e1c4ca30,9c6d6f18,bb5796ee,457b4270), +S(80298b62,eb4d3ff2,e10607dd,dbcacda4,3bb30e57,f3c8c7e7,6f9f3968,bf391316,fc72362b,48c8b606,4e428e6d,1ef2ac87,931caf75,e936a33c,228a5caa,c460c5f0), +S(8519f27d,9560acdc,509fddee,9b4a4a7a,8b13496f,14760a08,22609f3b,2ae07963,193aa9db,b98293f8,e70d7253,51d3893,a9f5fc73,6f3c8e65,28a2dfd6,5dedc220), +S(49d734f9,c8eff915,fd66757b,d6484bc9,548f20a,d8c05959,f651eb36,e45cb88,4c594f79,7651b49e,929932a4,59e3bf21,3e74c0b8,acb20907,697a25a7,ed21057b), +S(6a528c6d,19b1e28d,b4d9ec9e,19c4f7be,184ef845,1f41b322,e5932f05,575bcc4a,7350b90b,c22ab41f,40c13a80,18749e8c,275d3e5e,5d687df0,ddd47133,c092eadc), +S(6f3df40f,5647143f,cb93b23,4795d9a7,708db6b8,2e2df53c,f214de4b,61eebaa9,dee10664,54b625ba,34ce4b54,f7285714,7874b5d0,6b38b1dc,37b7471f,9208dcbe), +S(5abfd7eb,c4c5dc14,cbf0e335,f2e87e7a,7db96a2c,b78000aa,64fa8aca,517ee1e5,d65a98d4,20d872e4,4c43997,55cca5c0,bb94ccc0,b25e30fe,8b5a4fcc,7006364f), +S(1bf3714b,da04d769,782e37cb,c4a4b347,f9fc769d,a18e4940,d19a205a,bdd22a23,d0852b21,33b04274,183f4de4,9a20769e,4a08ea16,ca8782bd,84a4da7c,64c928a2), +S(773ab884,a4e90454,ea467caf,f18c3b64,e316468c,b5789fa6,4958f73e,ec3204f1,62213b85,b4838d8a,fa3a14f9,32db7d79,dec538c7,d872d9d2,be1640a2,4e367caa), +S(33a283d9,9b7564ed,6ed10d88,ba26ba,fbf3a375,e00e10d1,577fce27,2c204324,bf84dac3,40736625,cf0e6be6,98072c6c,5fb8bf9d,2850be84,ded3f652,569370d2), +S(62bdb467,596ffefa,22b735fd,d3dfd959,d4a6e187,a19d4c1f,4d647b4e,db380c3b,210a8e93,d1a184d0,a3548be7,6283d180,8e4f32d9,52834c,515953f0,4ecf9acf), +S(ef16d3a3,233f03da,5864801a,c246756b,d1356137,971bc164,b5512067,b369dad2,480e3d32,7f1df8b0,646c3b58,a51126a3,e91522de,ab77d6d2,f2bedadf,1219b877), +S(a6834869,c006d044,a9dcfb9,8a5a14df,86469fd1,fd0cba06,a4e8ec1e,1fba89d6,c1a3e0ff,7abeb1e,8cac11a6,8c1b4c1c,f663e615,9f508c35,f4747f50,84b247e9), +S(203d13c6,53a3c36e,55a836de,b38c7443,681d7b7,c681d71a,38fb0fde,986ce9c3,5d7dba97,573ffd85,b3fd4417,f09192e1,92461c4c,125a9947,ced9c737,ecac65b2), +S(c48819,b8f32966,bd50f162,30f5c0a1,45cb3570,d9c4b104,4c306696,2f4d6667,feb08ff9,1f543c1,35b6e0f0,b21d7624,91ac096a,181bf093,fcf8f4b7,bf6559a2), +S(31441031,ceabfbf8,59bcdd8b,6e177f91,df7bfe08,f9015172,1ead3acd,ab64acff,e36315bf,6f087118,32894704,8d2fd259,6b8beae2,e1917341,d690a1f,786db5f9), +S(e3e4750a,310c48,38920654,b6afa032,79589d52,74e48139,6e22d9cd,3c05fbdd,f05868fe,5452da6f,fadcafb6,69e5edfb,4b9a7840,3a305004,345ddce1,eaca43ac), +S(3659ba70,60d8200c,9512facb,5c730111,4cdc2b9b,aa5bde14,8ad9bf9d,d8f8fb8c,b1185617,f91fe5e8,ec68da07,138b6780,e1ebd12b,1bb44eca,8a1b9472,8337585e), +S(1a46b7e9,fe99a4ea,492fbc90,3281b924,6831fe59,9360af53,bde4ce8b,43ed5996,97c317e1,5c0e23a8,f1c11f7b,5f9c140a,af786b56,10ad8ba3,d11b12ed,17379f81), +S(f16a409c,677a40be,402f8efb,3752373c,aced053c,6f702b82,8bda222c,a412b6fd,d5becee8,ebacd866,285958a5,8b1cf1b1,e9abf9a6,db61435b,d9725187,135fa955)}, +{S(32c932eb,6fa35974,90a408a6,ce962d7a,ef2bd22,72cdaa,cb80f798,9836abc3,3e145848,c2f8197e,f0264d7c,370d1784,7d21a09c,77fd9f7a,73245ce7,2ce5f3f6), +S(49d837bb,63890b4e,8a4a63ea,6d88599a,c9961c20,80dcc955,aca6a101,bc935f16,358a7f6d,b540ac03,78d83bc,304cabf1,84c7580c,46c258ec,58f22254,e3e03aa), +S(23516ddd,40dab9d8,63dcad6c,cd325503,b783379,d870ace0,9a0b238c,d7b6b2bf,b1fb8268,a7814efc,54f3d637,88956b67,a3997a31,10a738a4,94786d95,a5fa1c59), +S(6b4e215e,df56e6ce,6378fa98,5d604f68,e3561275,788283d6,c40cb249,6d16310e,cdf9537e,e6f4304,dbcb9df,f8b97a71,a12f014e,29867e5,6b09b388,78c9ce8d), +S(f4f30433,c933dddb,949ad868,cc0d52e5,247149a,7ec98f12,3e7f63b4,acf6b7b9,949123eb,9fb848fc,d7b94401,a64b21a3,53fde44c,d93dae3e,b7a2d3d4,78055af2), +S(dc3523f4,cd24be1b,30787dd7,5140fff,b66e4487,55e9a3c9,6124fe59,c07faa28,47b945d7,6d37b855,8ede59fe,e297a8e7,81d3ea96,f0a81588,b68db767,9b9641f5), +S(b92cace4,e31dc724,3b3530b1,f90de1c3,47cc39e5,5b247455,a1296912,4e3c2a09,78f89163,c735cb13,b1d8fe7e,547027f,68d8f864,a1f093b,22c0d9cb,95fe28c2), +S(8f7ed1f1,77238a68,bbe32e4d,ae6d67fd,be07c9a2,9fc6b940,6ef81a77,65a8fb75,98e627d8,d32095bd,5913daa5,920c5c60,334691be,537b08d6,da5b8c19,7bf81c8f), +S(47fa27ac,e035176f,34c59581,8d3dca2c,a2b40e53,29756c55,7cf81f75,4f99316a,ddee5921,f381ce26,3634a34d,1b4f9513,4d9f0e50,5e96bb79,b87d98b5,282aee7c), +S(7584cb68,60ae81f2,e0df701c,97d7aa29,8132cb96,fee4ad59,15424bec,2e280dcd,4b76458b,e3926b5b,bf59ad77,da4a96c9,1f9147bd,88a234c0,74b0e09c,a6993228), +S(2cd4c11e,70d432ff,38550aca,481667f4,d09ad11f,9aed62d4,968492d7,5de28dae,82514708,3e5bf46,341177e8,9ad2cfb3,a215578c,52f9436,d3952432,e9acd430), +S(2b524c99,2b18a6e0,30863716,362d236d,e385a10c,3679eec,2774edb7,a1a89be9,1abb3b28,3477769a,5c7b4580,93efb22e,f2e61296,da90d4d3,1d6b2ff9,1766f89d), +S(d642c258,9d0e47ef,f5b2b60d,9321db58,727315ad,522daf58,3bdf35d3,336f1081,1019505a,994030be,484e5340,abea0b7a,51276c99,29cd1f1b,9d19798d,aaacf3d9), +S(3e5e09b,451bcf5d,549f1d66,b96da0ea,25e63270,dae59606,bfc3165b,f89f29fd,2ce591f5,32d57be6,4fc48aa4,ba40231d,9c104615,b76ed6bf,11957694,16d2f0e9), +S(4756965b,82fc23bf,64041d23,a23cba32,cb71d3e7,ef3e6c03,51b20f3f,dec049b6,75f3a293,eae07f4,362c47e9,44a78561,323244ac,bcecef0c,6ba2f5fe,d1933679), +S(f296e9c1,41694ce2,c197ba85,bb9f172,54fc0bc7,f89347,a0e92b8,58491a39,73c7fbdd,91c88ce2,bbcadaae,df8880f6,aff07e8d,3a1ce05,a468e3c2,3008d98d), +S(b437d387,ddae39cf,e26edc85,b0e600bc,2a199b6d,b521f9c9,88d921dd,7e6fc761,ce2111f4,d937905e,2af7f2dc,559f9eb8,bef286dd,909a5965,53c7f61c,f51aed18), +S(bacd4a38,f3e1b7b1,b601fa5a,2a579e87,459fd189,a607314,5a0dcfb6,1ad3c21b,8be8d6b3,9553648f,2a94754f,4c6c14,c4917296,aa9987f0,deeb7e66,d81c3777), +S(d8164748,8ac6bca,945fce70,e8f6a256,90512c8c,dd4c8fbf,b553c131,96f7f607,16b75474,65a80a67,86b5246f,e086d32c,4db03b83,58271938,41877c38,2b5bebed), +S(9c2b0749,cef57b4e,36b31328,e01503dd,ddbd2834,5cd86ca2,5224b967,1b7897d5,f020f978,4eed44cf,cc4aa33f,b2eb4b71,f24b5f3a,a8dc1d72,569bdefb,5f6276fc), +S(ea277814,9f0ab428,63b05bb1,c113ea7b,91afb815,e8f0791,12c11573,eb044e4d,8199dc4f,4e72b691,1c562ded,1220aec3,ab967029,139d30ac,58c753d2,f4548030), +S(4be6fcf8,40673659,5a9e4f2e,877ad7bf,28e25177,63d74238,6e43e6da,1c1c43fd,eb5456f3,d1c40592,2eec37ab,3e0d08da,b7cd257d,9babab02,44e5b2d6,a827a7cb), +S(3f1e9ab5,160733e2,9e7aeb6a,378d169f,9be5c266,a6462e2d,e736be07,f88c1da1,907fa0b9,814da057,9bb11309,aab51902,a0699aad,9422e1f,b8f1a8c4,aa9d4399), +S(eb730dbb,1ba9b2f,50453837,35ec6da2,ac4b5019,86cf03f0,3f5061e2,a28285f9,41dc3a0f,f42fb1b5,d3b464a5,6e305fe5,c60c4561,d6e5a6a7,8cba87ce,15c392fa), +S(aeac891d,f7032059,54d0bab7,41927702,1a595a4c,37bfc34c,5d315876,3e85f3ce,168f8260,1331b5c5,edf941d9,9f1cfe0b,72fd2a1c,3e4bbc2c,c62d47e6,6399105d), +S(667e04e6,daca6315,4888ac26,a268876a,f4b754fb,87687ca7,3c4cff77,d6dac204,1826a26a,cdca828,8e2ac81b,855a4d40,86f3009b,162fba82,83cb1d17,b9f7aa4c), +S(240ec754,24e46326,8e2e4ce0,240f335c,78c46150,1e18cedc,b32e3c51,77ade3e5,be09c744,cf6e9c6a,75976c03,530329af,5e11e5ed,3fb13e8c,af40bc5f,95872645), +S(7b83e4b7,70e7a442,3efbb501,c23881c5,2bbde4b9,d5aacc38,7071decf,79690025,d242ed74,441d1ae6,61b97c83,70be2f52,dcbc2831,1e8707e0,1b44c827,420dcd45), +S(63589c41,fa8975f4,c8738f57,579cc0d8,447218c,69da1a34,ee06af0c,215f6c27,c34f2ede,7589760b,80dbae48,94a3c13c,7351bf5c,33a35351,9988ba67,c2a56d48), +S(db092b0f,4638ee45,190473c7,cb60cfec,45c94d96,99aaf289,82d28af,16ba1c66,8a96ecdf,29ad2fd6,3a85a7fc,b73ab6d7,719bbd6b,98d052b3,3a0cd91,ab924f46), +S(11c4dd8d,3eddc0cd,9a832dd2,dc91c8bd,491fdb51,f5da21c5,732dfb7a,362b8df9,ba4c2789,740b53d5,51bc9fbc,776f049b,e848182d,9d13abb7,e7cd860,96911eff), +S(a65a3a01,df3b5ef2,e620d431,49fbe1,4d71457f,19d1ed35,aea39d57,89303fdd,86715f6b,f300a390,470bc272,6f12d389,7979e2fd,b0512c35,252bb571,fd19752c)}, +{S(d8558716,b7033a9b,29afb346,fd71663a,79b6e86,2719bf12,6e532dc3,f581b7d,a790d460,1e5b2630,1ad6ec43,62f79120,d75449d,8a500e2e,6f17c021,8037a405), +S(842c01b3,54353026,c43dedc7,8829ead1,db35b63d,8c4d0d36,8cd7b661,2eb5e11,f173cab1,a37ee0d0,68e097af,17bf859c,13bb4823,255abddb,f0dbeffc,573e06e7), +S(acac5cae,414c3556,38e293fa,e7cda051,18924e9d,abb81bdd,29123768,c7f21443,5d17b909,5b997254,5f56770c,dcb8af40,e37e1a8b,6e01a989,34ec22fb,f7d04537), +S(c8e73810,c024c6eb,d5398bda,3784261e,542279e5,3438d49e,83b2639f,80889aea,7a2183e7,be4f81,b0337993,935657c4,c55d20df,729d2e8d,b25af238,f3d85f4b), +S(f78c5f53,5e7ccb53,555a6f2c,b1de1c5f,da379360,f6a7fc52,eaf86ecb,dbb3bf90,cf2d33f5,eab2ee1d,5a32eea3,18f9927b,cc982fed,cc9add2e,2461ba14,9def0292), +S(4cd40c43,bdb8a042,2babc95d,eb00a405,59519074,5906a8b7,b5ea00da,b7f995f5,a0bd1c74,79125d2f,5baec520,21fb7c62,415817f7,2afb80,b2315dda,40cf4c4b), +S(647c5950,f8a7fe9,e6945b3d,6da91932,b486a4fb,ed903836,9fbc20f9,a849ac08,d331cf32,d72e148,b47af547,6794a33d,55f49ffb,be47db02,7d2de73c,542bbbc5), +S(97664e78,980a49f0,8269ff19,649e86cd,7da88a76,eb1441d8,a5f3fd2a,6ea335d2,b2898268,9a8cd91b,b1b16be8,ee46b4bc,252f7016,f72432c0,431f1264,8d7450a7), +S(523f68b,b39c2ded,81d22a4e,575483ee,d8748c9f,52c06081,391c27d7,7afe3805,5c331672,4464dc47,cc9cfe32,864b6095,ee8bd6a8,42c1af98,b3e33183,a46e2471), +S(cad866de,5cbf4938,989b835d,5aed958,cd7c316b,980b7c76,2109d688,d7ef5b25,3ae79c30,e77d6d4e,fe404b8f,324179c2,f677f434,8a1c4367,2a0fb5eb,b706607a), +S(ba065cef,b47170a,d6796033,d157fd1c,f22052d0,7b7f9992,6cd58e03,316f95a5,26177c08,bc535702,7f5d03a4,9d982637,d74a6b20,696be76e,742243be,b9ecddca), +S(27ca8178,be756f12,22cebe6c,d05310d8,5a6c4f59,eec71613,7777b625,e6d44c97,de547a17,ddee9fc5,1fecee01,45f9cda1,b65f097,5d1b9544,aa2317f5,102537cb), +S(bfab1d94,d04f4909,dee00998,78f77cc7,d9de94a7,96b5b770,5f3c759d,2ae97ec1,6ec39a8d,9e900a79,b8b88098,d1e5d79d,b75ef91,e442cb2f,27ea531a,2db21bb9), +S(b48a4f25,9d82242,59f08a08,29b05ac8,ae6b97b5,56cccb84,20a479f5,df92701e,beef137f,9f9acdc,8d31a780,2290fe79,9d9470a7,3c8a01a,35abcbee,9730d17a), +S(e3f5d307,ed2f2f13,d23ae5b5,9dd567f0,8161996,776d506f,b53e14f2,70a52370,40ecc697,7da3771f,32a66438,cf70ea9e,550194be,1af85a08,c8a4515d,32365803), +S(818b0462,d5a815b1,57216ab2,4d795886,17cc99bb,7a7abd91,655cacb7,ba99335e,c6ad05a6,7d2a391,a15b6600,7716e02c,9fe40db2,f82283ef,c9c0a7d5,ebe854f4), +S(7e3c52ed,23220b73,ba47613a,bf39a9fb,b3a6bb6e,22c972a9,3ef21a61,50cdf830,b3e4a95b,875719b3,bec1b113,9e29670d,4ba8f39a,2fb76700,f69961fc,bd01fa2c), +S(594e4129,9c1a117e,ce8564bf,ba88ce04,297f2e14,7ecda07c,5f12913c,14d9a5cc,60bd54c8,7395fee2,7fedccfb,ca9524a9,b5750b2d,d712621d,e73e0692,46c2e347), +S(3686bf4c,f387e340,6fc314e,f02c637,ee6f5062,ee9bb567,235b00e6,4d516648,915e5904,b106e057,a0db43,151ac9f8,4871e843,126d5285,337dc3b4,52c67165), +S(338dbd1f,42480753,516d3cb2,c535492b,72849870,a7828b48,643dc143,b966ea61,e51f11ec,86104334,9e9dae17,78116032,949c66c5,3c4d441d,3c88cf4,c7ad61c9), +S(56f935ae,f58bb403,7649198d,1a5da704,26fd944e,c5694c18,38c94287,2130215e,a9c48fd0,3b059474,e528016f,9c998e02,cac7c40b,8c859852,b58b9082,b638222b), +S(1faadcd3,da472591,cc723754,82546c81,c4f53c14,d0d8dc5e,ab95a58f,379e6899,5bdcc45f,42d0ba3f,783a0806,a5d08c74,a1b1b9f5,a559248a,8681b153,74e74958), +S(532d8cd8,fdf838c7,70b87ca1,4703a884,116dd928,74acc660,6ddddd61,8eb053e8,67a31325,853db4d,3e5877b1,c76e7e39,468d2d77,37cb8a6d,af4f16b8,17cd8ad8), +S(8a8cbfd6,d45e5740,aa9f0a21,f40e3671,ef897fe,bf2bf753,ee2d2f75,844ab648,d00eed6,efefa26,46c36895,3a54045d,96da4cce,8ffc554b,9bc989ac,2f62acaf), +S(38ae095a,3c705343,38e542ff,6789902b,cc1b0b5,7763cc43,2f50cd9a,d26d0f1f,100de239,87026f09,e483839c,fe9e1f10,11bcfafa,662a9331,95a8e4ec,e4fc804c), +S(82d9e5a8,385de1f4,fa541796,a00533d9,a3aa4656,74583d07,c4b3c344,b6ff8cb6,70bf2bd3,5765165b,a466171e,f26439e8,f790e178,f67ef5,d2e15e98,435b4261), +S(1843988b,42ddec86,dcc6c9ab,36b381ad,a0ca745e,aba36bf0,237e2830,579aaafb,f1bdc95c,d76337e,cb3cfd65,d9c30b7a,e7c7d667,65c9db5a,6130edf5,e240cec4), +S(a32f7415,9c2743c7,2ef192bf,4a68e838,97a86a0a,3997d1ce,fda3c581,b4fbe683,8862903b,db791be1,dbab5031,ca57f161,b6449bcc,db061438,f45e610d,50ff3bef), +S(b09dcc04,d9c30c35,2bd63880,a766da1,f6314287,dc201bbf,9605516,3db2a09,7580cf9b,7e30dbf2,8f72e9c,a5152ff1,956ae42b,3e952b18,512824c4,7f4e5b9c), +S(ca07cbfb,b24ad1a5,edd9a12a,8ac54157,6f3f2ba1,4b878d82,ab7dc996,bd7e2c95,5123ceef,cd20f120,b575dd98,43ef9936,c53cf90c,c481f340,fa166452,72af1c8d), +S(f8058324,c6b9c2e7,e62147e9,a41ad78d,60e3ecf4,17524c05,80832add,f11349e2,6a39f1a5,f577a932,3217e559,f15eeddc,af6b674a,9d921772,a053b960,a4dfd633), +S(2e3c0532,6255d80f,a42fc69,d5c92aa4,cd326a5,3e8535f0,435efb7b,694a09ec,ffe0076e,9a93904a,42251dbf,47d03e54,1fb75ac3,8f8499ae,dacb797d,7738c9b1)}, +{S(7bd8469f,80f009fa,b4960cdc,bbfbd4ad,d8e37bb,a4b2a34e,d5c95d17,7a94c207,f3062154,c3bcc160,b2aee99,aac98446,516a277d,85b37fe9,34be9359,4af905c6), +S(139b7c10,ce844844,f5d0a9db,bd7e1679,17b37e93,9bb6fd2b,4f19643,f22a6af1,32d4e929,27f5951b,ffa4ddde,4b091ec8,531a6f23,f1772c9d,ada292db,2b88c1da), +S(f49cdd14,14b38e5a,2dd34bff,3d01e1cf,ddefb954,2a641edb,df698d83,774ee70,5c10a6d9,200253e4,a932a42d,2e770f18,69d0336f,3c44f480,ce4a3305,83050c31), +S(ee145304,4771c862,745f03b,4776ade3,104ea0cf,eda8710a,5fa108eb,1e946826,b91f14,b6127808,f1175b72,2c09e02c,66eae622,109f4156,261d65ff,506b88bd), +S(c9ebbdda,867f333d,39142483,2e4150a8,c98090ec,d8ba9c09,3673330a,8777d790,c46d72c6,6028477c,e4754960,d40bd10b,b2defc1a,17dbf018,538b71b2,d208372b), +S(707bf0e3,9f2c8c6c,73a5d0d3,3edb32e3,46e73ac5,dbeef6c,60fb5ebe,b95ece24,adc6edbe,151bcb7c,2ed5fab,1cb9b2de,3ba2f3c5,ca762082,902582cd,e9e27f54), +S(4a002722,9fda7d33,320fc533,27b6c9cb,88a09c2d,e95aad01,65f68f45,1c82e2a9,233416cf,1cc1ee38,90816a09,17c5fd0d,da0bb8f5,d33c709f,6f1b07b9,916282e0), +S(8efefe27,b45a4cc0,3088b6e2,2a0baeb0,3e772763,58af3217,f39c7222,1e20b59a,b9f3fcdf,65771acf,cc7b7486,9b10bc3f,b0e8fb2,960cad82,24ea2430,fdad1634), +S(7bd3815d,6779f321,195047a5,b1772b19,ab29a99f,cc082e93,c442e927,b1ddb235,eeeb4bb7,480f3dd0,92e627a,a042533d,a9eb598a,6cd1ed9,2d75aa89,a592ae8f), +S(883f939b,6bc9169a,3860d36,9894c4b5,2863b5d8,d0348c4c,62368a94,737fc3cb,96e37629,c05b7231,8800ad96,8e922f7e,5b8a2c39,62a25210,9b3e4949,4b24934f), +S(4ad8cfb8,e8ba3265,4d32a656,76822176,95c40b92,e97c6d3b,af26449e,149f4b3c,491d3eda,89930ffe,e401b994,54919947,7ef33a8d,6cfdfb9,eb28295a,c5ce94fa), +S(ff15cd1a,4b185632,c6f11ee7,19319bd3,abce5167,65539608,b1ee3a4e,1671369,24df322b,ab157296,40aeb039,c00ab8,91c96833,4142f87a,1e00d105,b08c63a2), +S(3fc8e63e,995fda97,1d09d96d,4c84e18c,3e0f4d6a,9f8bca41,20c7c832,c4e9d49b,e17b239d,8e6f7fe7,3c4e46d,3aef9040,6bd631f2,da1677c8,db698484,85922ae0), +S(d88b3d3,6bfe995e,45812e9f,63e8b6e7,d5a87562,1c4a23b7,ab46f6ad,2f7d1e81,63079694,7c97b00e,311bfdb6,198c2c4e,eda57d09,b17fd74f,7140b40e,d2842d06), +S(da6971d1,4c0e9c37,e5b1379b,9cedd83e,218ba47,95e4af56,7956922,c64750cc,a14a0bd1,ebd0727a,258c1246,3aaff478,fcb490f0,53d08a40,44cedc97,61be43fa), +S(d098ccf,e2fee442,74377e1c,57d0b924,45f0954f,7af643d8,b7ed5d9e,956fba23,85c99754,17df24ef,2786621b,c0be5069,a9c59da1,61e14759,ee26b524,e9587d18), +S(633f9b18,9dd98f3d,8c4de,528f7170,1c9d5972,cabe3219,cc4e5f0e,e7e094e0,3d69c877,28c859e5,d9e8ea27,3c04ec98,ca084df5,d7c9cb34,9fa537ad,f7da0b5e), +S(2e14be9,af0dda46,8d59df19,9ac9edb0,c875f829,5715cdda,5ba3bed5,d54fcab2,a04688b,64c5d34c,752396d7,f864a9e1,59eea9aa,db8c93c1,5b4d7cd2,539bfa0), +S(adc92567,7678b197,1e45ed31,38a991dd,a4cd3586,a1ce1498,3ea30be0,642a2dbf,70f2c69b,7e47aa55,cab6ab22,e6482571,e8e1ea52,a52df934,d5ab99d9,acce2770), +S(ab89aaff,b04be186,183e8076,46c64111,476c552d,10df84ba,10a1efdb,32c01162,fccc1e20,17ac63a4,b9f78ca5,af245a74,4174c952,cfe29491,11ce1d71,e4be8f87), +S(1195576c,ca3a49b9,4db3f827,1ff2308e,66b66739,a30d73cd,e8acda4,a382002c,6a2be3a1,c6ae9f0d,b314a7d4,728977f2,17e7c7d0,199868fb,c7f79d31,6cd7d32), +S(8a15bed9,bfa215f5,42c62c89,da0adb8,c68cbc33,d9c879d7,a2f2803,b7068556,ef437bc1,1786cd73,6dc321d0,a3360517,3e7572f5,bce04b7a,3c8cdb85,cb56e697), +S(b4521d6d,f9b20080,4d3799ca,4cdc87bc,ea970d60,a3005071,e910210f,5654da88,c4ddf348,2a23e98f,b86ab9e8,4decc6b6,ad18d879,e96435d7,4b944d8c,f7cc1587), +S(4074523e,b75634f7,6442c3bf,ed9a52d4,5f6636f0,77a10e84,5e6ed950,b673a9ea,d8889a02,ec95b511,aa74db1a,10c91547,45b89b57,981011a0,9d4ce57b,e4e1f032), +S(b2e71794,e6ac98d1,ce532974,df79215e,bed8c668,561dc391,505e9e26,8444a858,94d8ff19,b8a74599,fbe5f471,e7f3e1ff,e0238cc9,2504f99a,9c4f5fbc,2e482854), +S(2e672209,8a6b4ca3,905a8910,6d23daf5,aeee63e4,34e7bade,70395df0,e5a1af87,708e9fd2,a3c0a3b1,b0e03f96,c3d477d2,abf6dc0c,bb8a7181,ca4b18db,ed12e3f7), +S(7ec0fbda,63fe7404,438e36,d567d9b0,bdf18b09,d929329b,24a8bc91,816fd433,f80ab84a,99d214fa,5d288e3b,c2b5560f,a1791412,9c3d16ff,cabed23,736e4f85), +S(f166252a,34572146,8bf3a3d7,bdd2c67c,b0d36b02,d7a70033,add26f97,128b1637,134eea4a,42b482c7,ad625407,bb89a615,656e3a2d,232f4bbb,a33660b2,403d6f63), +S(7af860e0,27d3b1df,aa0244e2,51f9459e,488afbce,6a3aa610,e5e223b7,81ba2d93,6ad22116,415c668,7dd3f4f7,b5a00599,bd35705,951e1093,3f85f815,4d0c8af7), +S(e4ad03b7,e01c5d04,7030fe59,6e7e7960,77d9847a,e604d2c1,d801a5eb,e21c0731,c7757ea8,f57af58f,b64f3eb9,c7762bc9,544f28d,a80d760f,c46b0c9,589019e8), +S(ffb2f941,f27aa8ad,6a59ed01,24e83bb,d49f2588,a5602389,15329f6d,f77220dc,6812c00,7dd44185,fc3f0687,6f7a62d0,d31adc40,c0a1a060,c67df13b,e4cd4873), +S(42ca15ab,9f245041,ce991e19,3d696f4f,4c277df9,8cad603,8ad0772c,2da6e03,972d10d9,37e3a836,9b831b2e,347ff11,29917a59,7ef94158,7c977604,73cb849c)}, +{S(576abbcb,8b768480,882aefc,81380709,831625a4,b9de63a7,b9c3390b,9cb4c7fe,b49453e4,6e17bbae,2f1f8f9b,91c3ed04,80d5f194,33917cbb,d28b878,bc6e6c4e), +S(f9d8a157,c151181,3e085c1d,ef2958fb,df2a17b2,6b330a0b,2d512c87,69e5c14,7eacca29,26aa402f,f8b78d57,f5e02add,91087da9,d607bb41,b56a40f8,f7b19b78), +S(d31dff73,7d2d3422,7fe39dee,b2eb4bcf,dc84bd39,d6cfc4ea,ff09812,23400a80,4af55e8a,fe8e8ab1,2e2c4463,d41824df,7b53e44d,efd946ad,b6128214,4244055d), +S(9ff2bce8,a9fe6c7e,e68a8292,f0d2f4c4,8d963c47,63018a55,89a2c130,667bc861,eb138923,94aadfc9,6eca7c5,78c0fcc,bb31a40f,98d02e93,a140abd3,cdf041b3), +S(1397cd8b,aed3c601,b6eee616,243a61dc,fead22fa,da50db4c,1f9e2776,6e5bc9da,534a8e23,59316503,cae233f0,3b6ef0bf,1c7e0105,119d1a49,ee688f04,40695c66), +S(4ee38986,511024ab,5385f710,42bc3778,7cbedd0a,ad13ba24,b77f42c8,2ae935d6,cf87468,24a38404,f313965,a64c927e,4ba0b7e0,140f6b36,47e2049e,35a74e4f), +S(7614cd7f,9b5f1455,74d0d38a,370f69ca,f3050297,10af5242,e5ccf19b,187dd189,b946eded,e4b72db1,6dad876e,396e836a,875d7c9a,28a9c739,f665c741,29ab7648), +S(67fd2372,5bfb445e,5254bc42,f7dbc1a1,65bd4017,cd1019b0,8a3f96cb,7b3c7cec,b556e950,a144febd,a81dd3f2,f729ecb,fe43fc78,8658bb7d,2b9735d7,b06a920a), +S(57572b1f,1f17070e,e9b30b6b,c7168cb2,4741ec5a,b3496b1a,c06ec0f2,5f910fce,8cfe2940,3dc97084,7ab28da7,167e78fe,f2310c83,ca476a97,91ca35a9,610bc560), +S(1f111332,1e31a294,cab4df73,5a0ef333,e753c2f,7c83705c,9a5bf27,1321c4f8,8fed26de,202c92dc,5b51b13b,6e9bae41,3c7c88d7,b858dd72,a11f073b,e2945f9b), +S(23c9e313,b04eebff,15fb0625,43de6975,d19decf6,6416f4d1,855083bb,d738f882,90673370,f380ca49,e876d5f4,b6649de0,24f4454,29cf9d91,96f4f196,8a49ed99), +S(b9251407,6ef7f01e,379aafe8,8f22c156,65a85ead,f671d8c9,89602c5f,804725e7,b3a9409f,7bf973b4,aebe08e1,5ac65562,56983f89,23415e0d,eeda4f4a,c0d7c5f2), +S(1bf9deee,948b3fe1,b4345800,51c67266,8fdb7af2,99275a83,706f9716,7e38e38a,688fc563,771b7d27,f9c09953,c40cf009,a55d7329,5fae0b7d,8fc37710,729a3911), +S(34e20de5,9d00dd45,19a70b3f,2994fc4,588ca9b2,beaf6afe,4a1daae2,10692f33,7804aff0,a43f1ba0,438c05ff,8d013e3b,e5180bb7,3a969825,fc97ef52,6da1b6a8), +S(80fc119e,85e88b0c,8c84d23f,9e44e96,1c7722d8,51d4c9bf,f1824fb3,365823ef,efee941a,a60743a,28b6ffd,39762f9e,437d2b00,206e718,7fc423bf,4eff0caa), +S(89ab0f45,e3860393,f24b1845,7578fe79,b812926a,6bff3788,c8baf283,89fdbf0e,57d4fc21,42bf446f,8e4d9331,659e7673,90391fe6,62e77ee7,f7a4886c,8f440c8d), +S(1f092931,664bb98b,79d8fb4c,efb287a0,f72641fb,1cebe711,820c92e0,42f26437,59f63ca0,b8e2e959,a97b1c1d,1cfa4c02,380dc42f,db7cb5b0,995ceba1,d7e45d94), +S(95ac9f36,f447928f,344eb4e1,9ea806,717600e5,148ad222,fdd52a83,86bc3537,cdef5355,e928b1e8,225b5a53,bb976822,6fb50d1f,6c6cde20,758d5ab0,e9f28543), +S(a9ce5ddd,9941ea69,1343ea71,c85fcd73,6269826d,6eda5008,44cdd1b9,4a66044d,29504563,1fcb7590,27d904c5,4ffc325,2ce3c8a2,169f4649,907db751,2ef3ce43), +S(2edf81,45c4bcf9,62425707,ccb36a3e,7fa1fadb,4062d14d,f78771eb,f3ec15d9,dcfc961c,547548f2,73206d9e,aa0d829,a235f66c,5afbba92,68fdf9e6,abc8260b), +S(fcf5b985,c935e207,92907b93,e9976ceb,ed320a14,3cf286ad,b38540ad,b4077dee,8a0e0b4d,1ab114b1,caf7d0d,f749f219,b75acea5,6a71b3f6,aa18557b,1da85262), +S(2bd4391f,42b70803,2baf4816,54569f48,95f1434e,be6f146d,41447f45,f14581b3,dbc9937a,61f43af7,42635810,5cdfa02c,49a5186,ce67bbbd,7ced2842,19a37506), +S(eb00deae,6cf69ccb,abe6b93e,e5493e05,d22e53c1,b069a6c,4660330a,4e3bc069,18002f2f,61b08dbb,60c784b8,4c6d71d4,6b004e6a,3089a0d5,f853d659,83242d9b), +S(c7c62a5f,ce15326f,25b2fc5b,697cc7f7,26d231e9,e7e9f15f,5998b8b0,10605524,8ad4b370,ea0506f6,f25eb16a,78f87ef2,f408cd17,fa4ec63d,4ee21448,1d5286b7), +S(aa1c2baf,632673de,6f6704aa,5a76c077,be0c24b5,223de654,88d1386f,15a1a002,de4648e4,fda7db79,41efc5e8,6fec516b,c6aef164,62d6dc08,fe93ebc7,a85261f5), +S(11871161,13c3c670,e7f5366,edd22282,1f76ec98,d1a3500d,f0e483d1,e53f2666,d0042c9e,8dc1d72c,f53836f3,2c76b5b6,52fd5ec1,9e8fde42,aed1a7e0,9d242745), +S(e232a92c,9ffdabcf,24422182,34078534,6b6b79f5,983ddd57,2580c3df,e4ee4118,6c1144c7,471342d8,7a784fe1,587ef576,483f8980,bbab12f9,35aa0d84,83c1d76d), +S(1ab6237,3998ded,e50aec45,8043f830,8a7b4564,2dd3c9bb,4804f98e,a7c2e0c9,5528e158,6a23aec9,81f04b51,b90576a9,a9e7feb5,53200e51,e604898f,e55996f3), +S(3bc24bc9,cbc58de3,46644de9,b17ffa7,39a0f7d8,83eb9f52,af19eaf4,d8d52891,f71cf5b,7587c275,8014127b,f404b70b,b257bad7,f5b78d2e,c792e1d6,b2fa4493), +S(a2bf9afe,e6eec182,ef5866a,b4bdfe2e,9d045323,343aa422,8c1a13ae,fee515dd,a5ee438e,3aa35484,c5f5a1d5,43fa15d7,ff0d1c55,6571678b,a3c5694e,f93cfa1d), +S(a0b2b4f,ed0ddd23,8812806c,fccdfa9,7fb3b42a,748721ae,6477dc9b,18953133,325ee7d3,4a540d0,fc3e3935,79ba7372,905c7b17,4f12b456,b43db5c7,cb50ec66), +S(e7b9796b,5ca006d1,632f482d,7f0fe393,2cf16a5a,e104eea7,a7ea1c25,1073e879,ed476773,e6e961d0,20bdefd5,8c833e35,634a40da,1256750c,c718ef75,45575e97)}, +{S(a8565dfb,75f8eea8,ca9ca6e7,c7d18a5,bf3e437,5d1f4e87,68d5f51a,57199ac5,daa7d7c6,cb86b215,a682141f,2308d9b,4c204196,eb779f32,fcddf0a2,da194921), +S(974619cb,86016199,20f507ee,62d10295,44abdd66,456952a0,54e945bd,7890250f,6c8d8bc8,21da8234,1ffd0cf0,548be1dc,a1bb7e1f,47354dc8,3e5d96c8,2c9676f3), +S(24bbdc24,1396b6e1,75f23a6,4bfb41fe,f8329b80,d306586a,ecb69e41,2a2014b8,aa6448ec,f00a0710,27b46f09,d696642b,9274ce3a,11a3e986,e84536e3,7fd7b2f8), +S(eed9a6b4,68004486,e3bd388d,591d3b65,2f35a3f1,9401e5de,81a86f5e,b7cd972c,c81f9aaa,d7300671,b3dbdd73,a095c79a,d73ffbd5,9f845eed,9fe3a57f,a41e9468), +S(5260e0f6,fdc6858a,8d519d3f,6c71929e,36979b65,431a6f3a,a7507797,e54654e2,5af860e5,fbcf9676,bb00c329,d3e2e058,efc860e5,cb9006ad,224caa32,9fd49f07), +S(11e53f67,d844c511,b8118f28,de6ed9b8,66b70bf,214d4d1,ed79adfc,18f7d6bb,20dd984d,7b9eb626,77e411da,2e823045,685a4bc5,2a3f4e5a,3aedea95,1060b330), +S(98c9f3f6,81e220e6,d4bba711,fa8b212a,dcc21bc4,8219b813,eabdfac5,70e6fffe,bd0890be,d9cf4ec9,44f6abb1,56250329,a37d2cc4,64c931ba,8606c2ce,55127aa2), +S(f644f4f0,43d7713,ba4380d8,26ad0f3f,44106352,142fef13,7c6e05ce,5e9f1571,3192280b,f3971223,c00ed336,94779866,536020b6,79ba8cb5,c4f9c515,69d2f97d), +S(334ab869,32c4097c,8d8d8a23,d529b079,6a759d47,a907b047,548e735,5b3b766b,18f11736,d54c9d00,89149b07,35f5a9de,b15d9c,cde4ee06,155a4f3f,de98a7bb), +S(983542dc,61b43ee5,c4a7f36f,22da008e,832d4eac,6791d838,1a095d0e,6d8a14b5,cd225f2c,334d1ba9,94b0a681,d6be7696,16bfb2a2,426e9939,d2238cd4,43bebd02), +S(dbbac008,1b678270,4a0fcef0,73683822,a3d4107,d7f1a71b,41497da2,9cc942a0,cd25ef66,36f21a8b,532371ae,b91b67a,deea5797,983dc36f,54aed202,d9845515), +S(477949e6,9ed28a74,879bc8cc,660696cb,115e6392,5a63817b,60e5187f,1f77f3be,dd8ca490,fb8e3bc1,44f5fe9f,aa41ce73,56a1dce1,cc8e11c3,ca5725e,d1b764ae), +S(7616b9e8,b451d4e2,e4826586,9bfad6c8,ddbf5fd2,2342c6c8,42d6bbe0,398f94a2,4e3bb1a9,76efd345,3c4474ad,e6a494f6,4f97c6dc,c98cf68,72bca24b,dd9e946f), +S(b87fb86f,6ec4ad7d,5bec3bee,e8d68618,c8e4906d,1c584368,ec819f2f,871ca8c3,c64723c0,aa587911,4dda9c75,493f739b,e8c2221b,b2e75ee5,b25def54,a1d40c2f), +S(56ea4572,61a6ccfb,46fced99,1258b31e,8573a488,aa53b7ad,65333452,b06d41c4,d8529601,4145e12e,a72ae4d1,5ae6e5f6,9cc88ba1,4d79467a,3df3bfef,33d84290), +S(e4c6540a,19682e0d,e1f1258a,103037a0,af4df5ad,f27cbb7c,9e3ce67d,e23e13d8,a31e1b08,4bdb5728,36e53d93,1de035,c90b1efe,b5343b23,cac0e7a2,bf5b3868), +S(1902cf3b,90fae93,1855d758,24995b03,e1604861,199853c6,ece84903,c8bd67de,3b3d41e3,a2dd4c28,16d492c3,e6c53c83,e5203c7c,a8100d41,24cbdcd0,e12fe222), +S(6519f4d3,b5d731fe,eada6806,727623a4,daf924d9,b6337a51,56096ec4,16ab42d4,62e2c1ea,afa990bd,6dd91675,e3b2d1e2,acb2fee8,e56ec30b,eb25bdce,2d3ad30e), +S(e1128682,519b965e,a933f197,eb0997a0,16114459,4c0915cc,9c55ea4,a30c1ad1,cb1370ba,f8707c78,84817ee2,8227da3b,7b7c97c8,266af491,e0d52bc4,ea0f9614), +S(4366960c,6ff595d4,143d4cd4,f0f0df2d,3bf864d5,73790abc,23419c7d,6ed9201b,623faab8,2caebb5a,b4c42bf6,b2a686cd,4640cde1,a95a65a1,f8d32836,9e5ee0cb), +S(5acdf691,3c1e283,c1220355,6efd99c5,d347ef69,bda1d040,44cbf0d1,ae41b16a,1bca567a,71698c35,e35e031c,e8a98dd,e34270d1,4e2e2439,9276a7c3,6a1f35df), +S(807ecc04,81aae374,1efec46c,8c6c194b,faa0607c,cf5b7cda,fc270fb5,f5b416bd,cc60be6a,6736dd94,848fbb05,375f6459,d556866d,165a2c16,41984411,dda33fc3), +S(29c470bd,97737284,765adcaa,68a98af7,5e6c275f,fa4fcd5d,6c71d428,bd3bfd79,b19ab40f,33c15201,41fed31f,681f176,4a464373,23283677,a40b4018,99a6eec0), +S(41a6d6f9,40bb8abd,9e8748,dcfd23f0,5333eafc,4ce311a3,161440a6,619efabb,b2a3702e,900788cd,bf35204,27e0facd,b090804,62f65418,89a3c34f,ae2cc008), +S(d5f5096d,776ffdb,f694b9c5,c9a9ae0,ab5897ff,c5ca3320,82cb2a2b,8afcfe89,6461e24e,28560d11,336244da,2038e13,c69ed5,a7a435a6,d07a1d72,be43daa0), +S(f65a00d9,5707ce6c,6bd55e8b,4b0ef381,1eca65b2,9845433,d7b307e,518ea724,7ae079e,9aecabf0,4ec5210a,b85328f7,e60c8ff7,3deb037b,c8e6e1d3,34774734), +S(346cff42,23c9ed2d,a1082d46,eeaff341,f7866e72,d95561b9,678def2d,9f7cd187,f97f85d8,92002750,11efcc9e,f8ff58e4,894f105f,b94e7390,7e185806,575698b3), +S(d0e3cdff,a47417f3,86f64a2,4029e3fd,9bc7a464,6e839bf9,232a0d50,349ecaac,5bdbecfa,2a2b35d3,ce2e9837,222535c1,57ec5bda,5fd70186,1920803d,a279989e), +S(f6bbbd77,b8dba8fd,704a3f02,1bc418c0,f8524c49,ccfcd14d,fcbf7f04,c9a18d,38184001,f39b257b,6755a132,86bdfd92,1a8ff771,fdd5530d,48eb8227,ea70be1), +S(6717bbd0,f019990a,c01a2996,e34f0912,9bf60a3a,71d8124e,fd86c777,9cfad879,9581f790,a16825d8,f44f316f,db3f0bde,108643cc,b390bdc0,75f3dccb,6520029f), +S(417c765,ca91a120,2b07459e,3713d5e1,bbcdfa82,bbc26cc9,279fa2d2,59251fa2,8d281a2a,ed38a80a,cb697989,9839d7c5,265b4e78,8a9e1176,66cfb6fc,4143a935), +S(e2f349b0,f89c69bd,3c8cf2a4,10730dc5,8e0beed4,7048c58c,15f9ffc2,508d2cc2,e014d0d7,f07d8dc8,7e79f513,89fdea45,bdcbb417,1f63424c,81cb8426,1f2b3be0)}, +{S(aceb57c9,187a5202,118c4dbe,573906e1,646b0325,781c1352,574ed15,e02b87b,68ece7f4,4037ff05,d4b89120,d90f08c6,f0499fee,d4e9e02a,a2e68bcc,2d7b70a), +S(365e555e,de9130af,99f996b2,106229c,c36c1ea5,8b065cb5,38af3a5d,2cec1145,81fc523c,b4b97bd3,cccce1e1,d1980769,fbc25a9f,a403adba,8e7e8074,13c87f9c), +S(d0a3812c,b9244d7b,c1f5b652,adbb0df,22d15d41,b1724071,fd297d00,bc8cbb01,f8d7650e,fda2be30,24ded196,eb3baf7f,351d2013,2479640,16ee4c6,b469951), +S(8f4ce1e2,a51c0ebf,f5e34cb1,c6aedd87,85e415dc,e7307119,143bbe9a,3e7fc632,617009a,ee7d4d81,1ba2bf14,d1647911,bf1a3bbe,cba8dfda,9030ead3,803fc217), +S(ea23d837,54cb2e9f,9b884ef,f4d1a708,bc1826c6,59fea53b,f52c51b4,9fe0e21b,7e8c691b,cfc3b7e,56a7699c,4f56a11a,fca5710d,963d602b,b102e1c5,b36dc3f7), +S(e3c970b2,c764c9f4,ad8bdc15,cf995690,807f5d09,4a849787,9d278d18,eed6ceea,aad8c7d7,13ce5439,b9125d9e,c14b5d8a,1c694cfc,f84e6922,b7e58df1,4ab8a34), +S(8ade30a,94b236b9,9597be14,bd421130,b6843895,a85f3613,efcfa0ce,9781a96e,78e4bf57,974cfc0f,c310330e,6aa47de8,2b0f5009,5a703fa,50093d5b,a8e8f0ef), +S(fa5f1cd3,4427d71a,de7e79b2,b0a17ba9,66d05324,1f9996e,be8143c1,9186d24a,3481bd18,2319ddf8,f344e4b0,196b3ddb,10deef4,d8d75c0e,49fc5fe4,cbd6e1fc), +S(8b88b79e,8cd28dc8,58e3f1a6,155146b9,af1e3697,d57b7435,c649f6c3,bf8574ca,e736106c,8ffb9bf4,d83d79f2,4faf8fe2,ba5dc258,e4ae1b1b,b58a1a7e,4922c95b), +S(12078a5,5c46d721,96a148d2,fdf6505b,d059e423,c5d5295e,facc2b82,aec51f5d,66a4332d,cd265085,add9a141,2fae2562,17441d7,dd9596ad,46309a0d,bfb4e55e), +S(841523ef,29f5bffa,b31a526d,bf74b16b,fac3a3aa,391f7404,6675cba8,7808c171,7a3fc3f5,bac300b5,7ef843df,e334c995,cf66de11,2283fdfc,767cb57e,e7e7329c), +S(c8831ece,f4545c2b,387378b4,f88173d3,f1cc7db9,d064774d,60394f0a,ecaf5af6,f32f1a48,bb802757,34e66cbe,58a3ed3f,adf28dd9,374a42a,a824a095,6701322a), +S(30001ff3,7b1b1691,9a135a31,287e3a3d,383ef3ca,94748e33,a52c146,586dc4b9,9d71b25d,6fc35db7,26b58d1f,642dabb,46dfa64d,e0ec2a12,c53c414a,9dd2574b), +S(a17d3621,74d3c789,c4e00888,fd4b0f4e,ea3929cf,afea9e9f,7439901e,d6b0fe44,2bcf09ef,2d374194,510fc057,dc973fe1,bb687428,91e9056d,a5b4df7a,de79bf), +S(4725e2cb,19f95359,2da72565,d8405e09,92a64299,9ad74ed5,b41f5d24,44cb6b0f,be23fa82,7aec3121,7b90028d,b61d1092,764b2824,9f3fa0ef,91929db9,b2e0246b), +S(67c560dd,74c276c4,df3efbe3,b95dca4e,a56c4563,cc7ae225,ebea90e0,f5e212fa,2c9e3120,d52ef31d,7e9e36d6,eafbcdbf,ed8a28c6,7edefb94,1df7f38b,cb05c160), +S(8468da98,7b33b79b,e08da5fd,57cee298,ef86f624,3bb975da,2f4261e8,f7531d92,455d8a8e,b4f45693,2e8b6f77,ddefe070,591dd37f,d6ad3a27,5774055b,8a63e650), +S(3416d4bb,78de7570,b1e9e912,83a3e8fc,be6524d7,4463dee6,82e84e52,72c0953c,94e55458,2c17fffd,321364c9,16658093,986b56,f88b4818,129e19c8,ce685dcc), +S(5c66928c,a39a6e22,672c1dba,8bb6331b,41a7f43c,a72276a6,afd3e209,6416a9c5,181cdfc4,15e7d6a3,c24be94b,7fe328cc,ab624c0d,dfabb1b8,f9a73b56,cdfe1b28), +S(971f7a13,210680a3,ff6a12ad,17481a63,ef624d0c,e4121166,7e63aaf7,9cafce,5de2fd1b,3149e321,2980c7d,88a49018,26f83f18,7438061e,9bdb8d33,6a4b08f6), +S(650277a7,9ae7682d,c5f3526b,8411342e,b48277a4,9876caa7,61ec6815,9a1f13c3,a903255,fcf09808,cd97d92a,26dbd2e4,7a29571f,ae91107a,7cb6f0cc,3aea1985), +S(76a79cc9,7fb80ab3,e8d31e5a,d11f5ce4,74696d23,f1ee7aab,3928848b,556f483e,1779bc5d,c763d1ef,7611b402,4583f07d,c7dc9c96,7afcf83f,e6b6ee42,da75972e), +S(6e0b4015,474fe250,d372400f,13efa0b,70c4e8df,816c0e8b,8d27a7,a8168d54,f01dafc8,8178dcf7,15f8fcc2,e7163137,9e1dd4cb,6c88cfbd,e9a051f,ad204c3f), +S(ee5dd472,b5efeba9,d0e354dc,21228d22,40a69088,842d00dd,6b6358e4,309d1f5b,bce93ac,3fd4a098,9e74448f,5832643a,a2a172f2,eff1e2c6,c61ea99,cc6e7cdc), +S(df99a925,ef2e9f9e,57208fe6,5373ff94,876b1448,368dd453,436c59d0,f2a857e2,88dcfe81,4397f990,56f92a27,143c902a,2a6a66f7,f7e9652e,e0b16f13,33252a1e), +S(9186f095,43dc64db,ad582561,cab96a38,89be54ad,e1cdb27d,674af836,22b5d284,71ae31d5,a73b29f,54af1aa3,2d8e541b,14f7cebc,9279ac26,c846d466,aa975e20), +S(645da454,63357bfb,61b5b932,1897a73b,87af6077,81487bc8,af1a66ba,49bc5e37,4b925aab,aa7bbc19,5963025e,f1896cb5,b6463242,cd8a35f3,b1b404b0,7faf8e94), +S(5cd9a6c4,e9d372db,90a65f11,1f1a1b5,f28bc3b8,3d05f599,39979c91,5277eab1,c147420f,7dfc4ef4,d7d1f08c,1c523771,86a8d7a7,10cd7017,82b97c55,63287aa8), +S(4fd699b1,2c720ccd,7b977e5b,dc622671,18ac3028,bd635b26,fce648cf,335cacbb,3936f10c,aee5f51,26b86240,3249e1d3,5566714d,17ddc19e,cc4d1056,71e523f6), +S(b3fa0545,83510d6b,91226711,4bac7f4,ba4ae5fb,baad7230,3003efcc,4c3c18e1,57a3db8a,6cbf0a94,845e7d67,f9b01902,55aa5b66,ef31abd,34ee0410,ed55d0e0), +S(fd73c052,b194c6c6,dd46aca9,d640981a,ec796009,17a565eb,e77fd534,649a2115,9df8973e,37e877bb,fdf5454f,d9082926,fb09fea9,d162bde0,fb635483,59458f55), +S(2982dbbc,5f366c9f,78e29ebb,ecb1bb22,3deb5c4e,e638b458,3bd3a9af,3149f8ef,59e4a416,5099ddf5,4605acc6,384a4362,f6a2466b,ed1c127b,a918d94e,e93859e7)}, +{S(8aff982a,fbe09f67,6d6e0f49,e2b68f88,8f20dd2e,6387b420,8157ab24,d19027ed,f2350ea9,d00fd861,41259f03,b51b5326,6ce05a34,74508bd4,31ca74e1,366eaf92), +S(27ff285b,1e14961d,a042bd31,56aa44af,5e73717d,fa413a47,b300f358,e1960dd0,8b578851,264a8fea,2e29bbcb,cb9c90e4,93e8c731,983f4bba,65381e88,bc83a8a7), +S(fc6acf61,4a214859,1418ab5e,e0f0274a,309a8248,2cf01f73,db8a9790,825486df,190b9c0d,46953d99,c9de2bdc,bcc15d23,ee90485c,c4c152d8,e28322e5,4c927d54), +S(743bcc6,ac86b0be,a25cce81,1fdcfc13,73f23926,e3116fc8,9415b8c0,4c1983ac,f3df10fd,4afbd3b2,dc0678c9,102150e6,3d005680,6a5e45fe,c95ea495,9f2b8555), +S(bb165428,2c13362b,b1e017e6,6402613e,f82bbe22,9429f29c,6a1a522e,4956d67c,ec542c4f,7fbf3c6c,32e71aaa,86e3e4e7,e52b0d43,98589e15,1f17417a,40b31b07), +S(b3f74ed2,5a7fd474,5820a2f6,221349aa,e41af762,25c2b706,160a463d,1d59e7bc,a9af9d2d,89dcdfe0,21ca46e8,5f09bdb5,197c8f23,bc56d705,851974da,1608dad2), +S(20c0914e,f88902a8,6af0ff68,a08f96ea,25f15ea6,b1128900,120028a1,1049a77a,339567f2,71a1f1c7,16eb1d56,2f009537,68ff4cd3,9a81c6d9,6359d2f5,5d04db9c), +S(a3555aea,2e96f05,d25a145e,4ad0d2f7,fb453974,8c4a3097,a703c0f5,404a0e7a,ee236b76,8fa04380,b0448beb,d2589665,e43299ee,b9fc78fe,5a64db69,ac92b7df), +S(37191a66,1d2a7bd,c96a0767,ce47d97d,7acfb8c0,ce251e9a,1421e2d6,29230c2b,57093393,4783afd7,2df6524e,871c8e67,ff2f48c9,c73e2054,fb219cf,d3c2a68c), +S(1a2f5b6c,5fbe300,8b01407a,b0b28c3d,c823cca9,b4c71a1e,4fc814f1,e186e062,b2f1c632,a9d92a4c,82d31a55,f4fd50bd,d53ee99a,2e1452a4,e43e5ac4,b8cbb7c6), +S(2976575c,c5e70e47,cd9c10e1,32a593dd,12b65215,38613cb7,22390022,73fb41d0,c3f0b08d,a2a3a046,d87dcb13,38c35824,1a8b9959,e2567543,852dcf73,c4f5cba2), +S(bca28e75,5cd6ab74,85884b30,ef399f,44188695,fe9c4143,6016bdec,25d502f7,b67538d4,a571e935,cbefe237,c5a01f77,4ade337c,fec9f95f,3e9cf042,f0dbab6c), +S(3dfe1718,beafee83,b858048d,25d46ee6,7741d3bc,bec1e52d,3ce308cf,738b715,8499a8b3,a0c7c599,f62f056a,a8364494,ec6e0198,2c47bbf8,7e2df591,de0ff537), +S(bde6252e,a9e4823f,36bd1737,ce188fae,be4a271,9984c9e6,76f9f4ff,d070c1c1,2e5db82d,e02a1fe1,39d903,7e7adeef,12d40366,de0eb164,c04b2ef4,b1e849b6), +S(2c74b766,eebac93c,479fc0bd,78a3805,de50be2d,a6ad70c6,9048e43e,c91343af,ab7a929d,588ff993,55094f83,6e7b3b58,c6381bfb,b284f9bf,7dc44cc2,67cc741c), +S(f0e084a7,af9abfaf,b6ebf3c5,b526d545,b8b1cf5e,8e09a06c,3670aa37,6af83a3d,6bb279db,abdb2b2d,fcc97510,2b075dfe,b42d9e6a,5a8af760,f4b63788,c315f8dc), +S(715aa28f,4e52fc26,a2f4b9f,391894b0,6adc3b69,b508827b,4732396f,5eb7edb5,c5d0d68b,3a8c6532,2f6303ab,99b6c48a,88572c4c,56c780d1,d51d2441,86ba110c), +S(8f980e69,3511ec3,cca89b2c,362835bd,24d9c8a6,97deff86,5540f7c2,b0bacf8b,2d5e8470,f2041ca6,f158ac83,e92a2345,4059492f,af27ada2,92bbe51a,d67ee5aa), +S(6823a4e9,d80838b2,750dada3,cc31f59f,3cc9d6c8,3f33aa5,38d949aa,c4d6d657,e087127a,3ddc19cb,e4088a2a,6df61863,e8e81cf5,c5cea4a8,8cd2ab51,bfbcba8d), +S(8c9af14b,11add28a,818f85f3,276d1d7a,1e3478f4,23e74b72,caaf2440,24558538,a7c17174,da1eed2,7eeb7b6,c1d93e4e,9e4515c5,3cd888d5,e4527cdb,94a64fb1), +S(5fadb7c4,74b13592,a1a27f76,b7c2011a,102b66a0,c6ffbc02,a7306e1b,a9ccf49c,7bf61c4f,134f8b4,7a5f90a1,938918a1,4d151f2c,7f1c4bea,cb8c4773,cf11609d), +S(79a21b82,9325fbe5,c01ac979,8b89cc47,379eac4b,682fd747,47e0c04b,879b404d,abfa4d5c,7ff916c4,9f489c12,5235bd9d,a0395b6b,84d6699f,c96f9c0a,5a4cab2b), +S(b213d11d,e4b5601a,5f10827e,d39a7825,adc36bc0,1130a055,cf8b3e8e,fc96d10b,27c0e5b7,a739fcd0,5eb73321,3fd1bbb7,873de0a1,2b92cbc,21de57c6,2bc812b1), +S(299ef8e,e131f523,72949d7e,fc2f0f7f,9965e69b,56725b55,4d513435,ac8b0b3e,8ba93fa0,2a4d97b3,54d47c10,d5d791c5,f724b68,4f501eda,1931e563,2c6faf06), +S(5143ab76,43a21de7,7ffd0f3b,c4746fb0,589be22d,9d9ca2fb,89403a39,86d1cd37,20991000,a7c01e4b,26d9236a,45568bbd,91c968d6,c87dbe05,15063af,d8db5a03), +S(738ce787,49783066,923ceec9,a841d60b,56855e09,93e67cb9,9d52a0ca,7a64b5b2,aba44aa,272945fc,5eee0330,976b659a,a6455dde,feb5c894,cd274414,71610384), +S(6bb9bbe0,4c3dbaf,6c60adcf,5ec5fa59,29cdb04b,14633c10,5dfca2c,183e1d97,b7dac59b,5c35c3cc,15521da5,89c0d333,9dac05c6,614acaf1,d8b9e834,8e02c3c9), +S(f325eeb8,49a32b2a,fb19491d,8c3c112f,a7ca3443,438c8c19,99bdf791,6981472a,169df94,a26f26b1,e5aaf810,7a148faa,17742c3,10cac320,6daa69,f8aad67b), +S(d279bc0c,f7f58a85,b5abbe25,936d5088,4d8ae8a4,b415ea30,2c0f1133,de98603c,1e69875e,2e38f9af,222e03b1,6c833bd1,12bc10f8,1724da7e,bc780e6f,5228fd17), +S(224d079c,750eb1b4,2d70aa25,5fa0c3fd,c2d07c1d,ae4d6cb1,17c1e61c,5ec1c6b3,e54dc4fa,b0e05362,60d27fa7,ba53c37c,a37ce30e,617365dd,1c58a59d,2c93ed00), +S(f4741ca2,b3de5414,49426531,d546272d,ab8fc55d,8f65eb3d,5ea780a,d25cb53b,3b7290bf,dda0d0f,e3704f27,d8537063,f2fe9571,4a0791a3,e1a25865,96a5b0b1), +S(e931258e,8eb5559c,6d697272,8a704c17,b775a26,5b4527d4,a4d4d742,bbfd71fa,4e1ccc9,b3c0211f,17a14be9,636ab4bf,4c6b931e,44a1ca0c,c2642f2b,e8b2c928)}, +{S(fa01dd32,fada2843,c51845e6,3cdc1b76,79dee47b,d2f4b767,5133aa5e,62029057,6ea6596a,b0ea0201,b6c8bbbc,e9d07fd0,8040e1fa,198c8767,67ab6055,34a33), +S(97ec9d33,a9d88115,471619b5,3fc58a08,e6997662,7a28afc0,b75b2f49,55eba300,92ad1d6a,f8a0a100,27d9ba90,4f7fea6a,571f3c32,53393a7c,79abb0cf,9a558390), +S(ef364ba7,626d72a6,87bc9776,3f2c0833,50640bec,6ff4d80,9dabcf2,9ea9145c,7f8d7c48,1bd80ff4,8eccfa9d,2de5eeb9,800cb98,c02da539,3b3d6c83,8fe4a2ff), +S(45961303,a778b47a,89109231,3242304,30a17859,7d940e9f,fc2f25bd,1be61c00,5fa356b6,daad2b1d,7bb19cce,b9148d31,58d2651a,b4b436c0,b99193f6,61cb037d), +S(8aa677f4,d73c0725,e2163e5d,ed3dc312,7ad5ee29,f9bad8b8,5ab8f50d,ec6d77e9,1fcb9da0,d3fb9bc3,3a45bcdc,50aa2ba1,16c2b05e,836b539c,863163c7,2436c334), +S(8c6b2873,33082bf5,a3597a26,7cc14696,2b689f20,59caf1d9,35972c98,9b14c86b,aa86d282,a1107f8e,21307e0,2ad1420a,a4061fea,3e3dd6f5,9b5d6900,6bf4827c), +S(46c963f0,3bdbf433,cbd60858,5a4d659e,9815c286,9f684eaa,cd879d66,ceed05ed,be2a8d60,636c5b80,e443b13a,9e21c91e,90a9e781,1c35101a,dfd61a49,88193e86), +S(cdbe43bc,582f3936,314a88f9,fa21b29d,f4b5acf5,96cdac3,4667e9c4,b02b926b,8f2a5b62,4f20b20d,cce97348,d73d5653,8651dd41,dfedd476,8792fa61,1bac16fa), +S(6f9c5a84,1ffededc,ec45dc63,7ad15aea,207116aa,a3036ea,ddfb0656,1dfd074d,ddf1c22f,9168232d,cf9c36a5,dfb7835e,62d28405,bd32ec15,5e3ddb3e,df834088), +S(42000663,3223b99,e15a2a65,bd95b229,820abee9,5ba5be39,6f8879b7,3f421e7b,9f3f3d66,40de91bf,c6872619,87487226,57e60a4,3ac633cc,48f684c0,cf42696b), +S(f02335b9,9cb524b7,45fa9167,41da4259,f70279d8,efe851ab,f5b0b8a5,32a087ff,e1bc6767,ba068b22,cb7389e1,36805da5,c30ca62a,e59b92f5,48e4dd1d,5327cfbc), +S(5bbda2dc,cf8154e8,28ed49b1,2fdabc3e,eb8c3f9,d0da5878,77611ff4,49d72e7c,312377c3,6277addb,d5b044e,fe136626,d5f7e7e7,a73cfc4b,9751e8ec,e4f77084), +S(5cd5cb83,ba2f225d,1ef4faf6,22e226e1,eaa85a5e,99721d3,82aae23a,f9dff565,254f58ba,9c49244a,4178a067,b0e4bf6f,dd7f3e15,65e21637,bc41f9f3,55045908), +S(3b20ebc6,b453aa16,e5b61381,1d81ca1,f87dce6d,b54ba85b,d8578ee3,23bff228,8c9d3054,56627a,7313505b,4aa5140d,9fc40d83,bcc298a6,43842f04,70a335f4), +S(cd531999,cf33db75,df4430c6,bbcf32d0,642be18,5736d79b,f2947989,e52c8da8,c7e99c34,2a5c1112,400edc69,f58246e3,d3cebbef,2c43ff47,7520b027,e2aa7d78), +S(40b3856b,273bd2a5,145b70f,d16a2972,e488b33a,f3719143,db7bc567,26ff23c4,d8bf895f,71d96e6e,20d523dd,1256bd56,44e11442,c4757d74,f116ae44,69dc9b21), +S(1a0cf029,2d7cce75,32b74bae,acec6864,9e682788,3cef4bbd,c8b24fdb,53e885a5,32c87d92,6f969f69,7989837d,d8ec8ef1,c284d9db,2a86ecd3,5bbb6772,dea06692), +S(7fd5d251,93dd6c17,8d0be92c,342f94a3,c92c061,27998f4d,54f7dfb6,1326fefa,ea7d0755,e0a804bb,8b7ac8f2,dbe1dd7f,9ccd36ca,2fa94365,631ae455,5d080075), +S(51820145,6c07fa3e,70d7b24a,7754b11,e42828e9,c1b96b0f,fd79bead,2f44050e,126f5526,1a55b724,7d08c868,366d697f,a6898c66,d14094f3,44b0a6b3,613d043c), +S(86f2c87d,46038770,3be5ddc6,c2ba1ccc,7dfdd56c,a9986663,882f85da,345886da,ed34fc59,4b66f7e,f06f54de,84f3c7f6,ea1d877b,bbfc4185,3df800f2,fa805027), +S(7f7d8a69,5b86ca13,d6a0ba07,99e60cbe,f5c4e1f4,c74a62bb,93d94d4f,8c1f296e,44a14061,b2c741c4,6a52639e,9bf08776,5db5d496,92bb6d1b,e97f9ced,a89953e7), +S(ec61e956,83714c55,1b72ed5f,c5b09663,6dbdea9a,64d13ef8,c9cf0c55,89da5ae4,39a6e38f,9f7927cf,ffcd0847,df91e856,4188bf58,276bb4df,3721335a,5357c77a), +S(e5906c85,4a75bee9,322d70be,a5d31c76,31a59389,2f479bf,8dc11bef,9ad5101c,125b3a58,8c6c1c5f,288402f4,3ce74ec5,fe9c2dd3,3f3605df,9ab41210,32641524), +S(6c9faca8,b1b435b9,213fde17,ce967f05,46e6f27a,cf0e131,8496bcfd,d856b092,a04ac67d,4bb07624,6b59df97,2fbe2c1f,bb04be99,e269d33f,ccc81f76,3f567577), +S(61031a0c,730cf279,d929b4f,5608f16e,6b1b1440,c04e5a9,fa300ff8,27303def,acc4d732,afb4730e,39a10c0f,5ae2cee9,5099be03,bf5612ac,391900fd,b51238c2), +S(e58fa07f,ebc3aa4c,61ff4fe3,bfc8d04d,48a1e3c9,935317e1,83d537f9,7f35826b,d2402844,7e63c3e0,be630190,8139a75e,2e1a2b22,b05ce52b,e453ccf7,7c35f1fa), +S(4d4d2982,b45a339,1720af,cf6b92df,2c201d38,5b9b6c3a,8fc53b4f,fd3bfdd1,d72650fb,d130482d,116524fa,e2b3dd71,c68b4bbd,54fc55a8,f9824578,cff67503), +S(9adeb6f9,6ffcbb56,f171919c,a8a64c70,9b8ae835,828bd573,b7a1bbc1,3efb0360,6d920a2e,d2e3f41b,4820d838,ca342b66,9af35a7f,7b6d09fd,b1ba4298,9abe027c), +S(64ba9514,d8680f6c,dd66895d,9c5ad45f,6e29,33506f8e,d7be9770,822aa9b8,7ed8c0dd,59bce424,481f88d7,3e641b61,a010e490,97cf84ca,16c2e045,49e9c6ca), +S(9e4ac64d,9ca58a04,46ca18bf,c1700f2c,16937ef9,c82e8b55,9ccbd751,2a0c0222,315ba2e2,601b18b2,cf7f1720,9c4cff2f,e39dfcf0,c2546fb1,e930898b,5b023274), +S(f13a99e5,8dc72fcb,c62a492,d2850704,621ddf48,f1f433e6,9a9814c4,17d4b84a,cc3d3732,f0f4166a,55946e32,e1c01f91,491c82b8,ef0d269d,7a66f039,ac02dfae), +S(fd6451fb,84cfb18d,3ef0acf8,56c4ef4d,553c562,f7ae4d2a,303f2ea3,3e8f62bb,18ba314d,4e78ea87,490185a3,e43cbb33,5d54b6d,2dff17c0,2f526f78,ecd3f31e)}, +{S(f1a749e,6cab100e,2aeef8ee,6a654551,c0ff906e,e7d11d77,f390955e,9d29d783,a60f7295,3c8c3bf7,f1dc1358,e39baf76,3dba22a0,1ce7917e,aeabd996,e21e845b), +S(e8c45020,4c9d9ad,f7878aa4,184a1576,d4c65d5a,c1c4d494,ff427e86,8b42ec1e,aabff02d,8a715c2c,34414b27,808882c3,f656389c,71c26d6b,d7ac8013,83f4c57c), +S(c1febd79,1784fea9,bf378237,a05522de,1081df6c,27d22ea2,aad805dc,7c2f1c4c,c38782e9,fcc1004e,aa677f79,536aa069,7dd9002d,ac16936a,1f0b8736,8734174e), +S(91925564,4d9bdf2d,4522825b,5758b3f0,4d32da73,3a4e772,bfe3eb2c,9909f4ef,2568e18c,1a0448ab,d038ae62,a652d152,4bdec1c1,a0293695,9772bb65,d78450af), +S(25bb4202,9880c39,f5bde432,c1fa357c,7f4867f,6fee48b9,24ecc824,5e1a5253,5d43db2,64c23715,b4b0eeae,63127cf6,9b4bc79e,f96ea8cc,d17cb53c,c3ac0fc5), +S(2597e080,e772d36f,df500782,5f92f797,c61c72c7,8d801dd1,3deb1bfb,9e91b12a,fd5918b8,938ded17,a458d0ae,559c552f,bd45feb2,5b4f59a8,f5b735cb,bdab92c1), +S(70231b28,f578dc12,9f8ac54d,fd4e3577,73d0fcbf,65be0dcd,51132882,5a36e7e1,d12aaf89,4dff3e42,405a4140,58524e30,b7c6c4a1,83763268,6852acff,83d2b7cb), +S(f8ddfd5e,5f3b46a1,ab363169,12b74c97,b0bd654d,87824040,bf6d2cad,b4fcbf95,931fc58f,5b429636,5e080d5a,8fb35ba0,f92104d3,891d6584,8d24c0f7,fc95bda3), +S(a033f432,670a382c,57e2408d,3bdcd2a6,6758e63d,77574598,6b123e4,d927bf10,83b52f69,d79e7445,b6e33545,c23f16fd,52691151,96ee60d6,2ecf6de5,b2961a0c), +S(8e77f9ed,9a643f3c,bf197b09,80f92860,f37d8b83,93633230,6c902c38,3bb7f96d,71b4da9f,f736f9f1,f8c3c099,c34c1bc4,30bb5d26,3c861e2c,726fe891,cdf7d1ce), +S(f34153e2,29934c71,5d895102,778425f5,e92f4ee5,b96d1333,df207e62,6761b328,954584f1,64c8f35f,2c4e0188,76186ceb,3e91523a,5e8ac03,d5998253,49b7b8ac), +S(e133aa94,c0252f05,379b5bd7,75ba3f1f,92de2876,68613613,28946904,11b3ed52,40363801,613423b7,cc6c0cc5,af3641f4,dc2a7ac4,48b178ae,d1393605,103bd6dc), +S(28119c6f,a57d02ac,856edb00,f594c7be,22772b0d,700ad14,713affe5,7e385b5c,969b6cb2,8ef483a0,db4a96bb,92e2d332,6e6d86a1,73bc3d5,831bcbf2,5efc437d), +S(8c728d65,8b78d96,44003708,dadb48b,65dcf6f,6d9412c0,e31cce3d,b5384d00,bb3cf660,9908467d,285f1c7f,40f4f18,ca618093,92633beb,b4ce30c4,72680662), +S(477321b6,1fd5b4ef,3acd1e8d,8432ef5d,e70576ee,6b8490e6,839f1985,dec96490,88008466,2244e3f9,7d2ded5d,2193c70e,2d72c67b,9b7cd70e,9f14cb05,70306bee), +S(ebedd345,ccdd0e04,4bd3d4d7,e1f1cb6b,ec06ad10,5ec2cec8,821580f5,d08a63d4,4278bfb0,88e4ff81,a3fe15bb,e01be36,40450f85,885da0ae,e15c6b9f,e78ad14a), +S(f4795df9,41b315e2,24a98dac,d9149ff2,8b9a7bf,e758a1f3,5cb6336e,8e1f9814,6982bc20,285c27d8,12c5c648,d0635c7,fdab85e6,fc6905d8,65da387e,596da791), +S(65a5964e,ea76e30c,74c472bd,3ddd5169,11dcfb3e,1f2d7ff2,9661e254,4a38ea30,a7c394af,80ebbd32,e442271f,b061bb42,a7b1758f,44d7d952,89152fa2,a88fe3f9), +S(f3d10725,da6b6c76,6da6fb5b,969ba7c6,3e33b4f2,d062a446,4ffb20bc,dea2d376,4d65ff69,64518942,bc3142a6,2148137,7bb609dd,6545bc26,9678ea19,62812ddb), +S(f5fe0f35,cde00589,301584a2,f4f5b1e9,3977082e,9c9bd50d,7c2c672b,ef05cc9f,1b49ac82,486e6344,dea32ca3,c96cd5c1,446ac2fb,2a658a42,50425f32,e8d72e94), +S(fce17cd4,d9519a53,f0a100f,ca54d1f9,fa87eded,98a12654,769c95be,151e33c3,40d342ef,83a4d715,bd13df0,f21197c6,e189681b,efa0d925,248733f,5ee07b8), +S(fd5d90d4,a97c1d1c,4fe18a10,6808f46,f6ea4422,c4408860,c2256b66,618af8e0,7ffc76c3,539bdec8,f59a9a7b,b36fd534,99306cf3,2dd1fe43,a2db5b59,af2f7e3b), +S(1c71f824,1b147abd,9e3c1ca3,fad5146b,e531dae7,c9c0d586,4f2d8bd8,7a6457aa,23e7e758,2cfc92dd,e498c0af,b942182a,92e02bb0,7e95bc1b,763d8030,9ddc9137), +S(dfb018bd,9bc352,f36889b8,2a46512,8850a158,872554cb,3a3dad28,a0496f20,2daa780b,d615d778,3de8f5e8,25cbd934,5c021df1,9ef80a6d,39ff87db,67a1e5ca), +S(bbd320ee,dacc1282,d3fd2109,c26f6bcd,46205ec1,d96b806,dca35dc1,4baa823d,ae7beed,3b5f72bc,ba66c794,89c05666,efa85966,14bacc45,9a99d882,8edbdbfe), +S(c9cd8f21,a068f78b,ce5129c0,9084be52,e48d688b,f474e690,b7720360,e362dfce,a10b9fe2,78c7814e,37851b5a,36aa4246,54c6279e,89df8fc2,ed189ad8,11518f22), +S(ddcf39db,aba52181,72c36a53,50883245,39ccddd8,46ec4258,e72dcd71,40041491,85cadc4a,a569265d,412c8fa9,4872bce1,51a750f7,b43385e2,2db405e3,54428a5b), +S(391562c3,f19436b1,a6e41d06,7435fbe8,27de66b5,555540c5,3ca7a0cc,c7876457,c3a5d917,5b56f67c,66ff269f,72c0a1ac,74503106,9059aaad,f3e66dad,45a80ee0), +S(63d4b224,7b2d441d,680a0437,604533cf,1dc48c9,bdcb9e10,e0249960,f966b2d4,e25cb896,ba165d4,46f73344,61e6856d,4c186736,402dc6a0,d866f97e,bd5cdfff), +S(5fe057df,5d2508c7,611dc21d,412739fe,3e7cf9e9,670812df,cbb00255,4d44cb22,33b88e83,9f22a7bd,17c149bb,65bd4a49,29b2a29e,b975776c,e82189de,80c7d45c), +S(5ac83bf,498f7eb0,79507ac,d5fbc587,f758afa9,b6a1ee51,4c857f8d,608a7b1f,1c866393,8a91904,f0530bdd,ba15390a,4c10fc4c,ef8a2f07,d799c89f,69a33097), +S(b0f9e4b9,b29790b6,33bcc04f,d860cb0f,823d8d1a,4cc1a1c1,413c1606,cc9a8e2c,b617d40e,7bc52192,be344f46,f9021c0f,ccaf33fd,3e8e3118,93df993a,20c2ee7b)}, +{S(eccf169c,9514a854,8f7e1131,a7438469,4df7dc2,fab96462,20e5799d,6d3f672d,452e53a2,aaf17617,37055bb7,24b64f4a,8fef29ed,f8571ff9,a3a6d2c4,6acdb91e), +S(a77bcddd,a0fecd39,a99c6387,57bda1bf,30257248,6a3fa08a,e2d6ff17,e6e4b6ea,debf4c03,b4bb5c7a,8a386a7b,1e459426,6c78eec6,55f01cd9,f137ff18,c5fbdb1d), +S(a77b91ec,6db6a66e,a48de424,65079a2e,ce220fba,da221601,840546ec,2295c2a2,3c721fea,81f5fe05,163562a9,849bdb15,90bc818e,86674ea2,57fe8d0f,4050086c), +S(99a28a1a,3bf2af79,ef2bf134,d31880a,71c2aad3,824fb7df,23fc4fb7,d54b2910,620e2918,df14e3c5,dbfceada,1905941d,32d77592,89ec607a,f9851a6c,5592803d), +S(7eb52616,71402db8,905c7097,2af96b72,2bd77d,f124bbcc,5418f217,f6d8c7be,ce1e663c,8276bf42,99919592,11feb13b,66aefa37,5d6dca15,8b7b66a3,ffc6d7ec), +S(c88b1815,f183e6ea,3ce8469f,ada5b88b,805288d6,828473ba,368d52f,2145e7a4,55e78f2d,3784952e,cb47f6b0,113eb471,8cc58225,85f9d1e,2198dc60,af3f90c9), +S(dd893c5e,a2e4e70a,fcc161ac,47232d3b,77f16c95,54c98770,9a7a6f64,c0cf7ab1,a1a58b41,c7d4d0c5,6d6c908b,32d97467,b8b6ed20,73e6a677,54935665,6a92a480), +S(591b8422,eddbfaf9,d2c27c28,dd84dd30,712a15a0,deb752dd,dee682cc,34722ea7,877b0c99,6a248082,fd442509,b0ce4cc1,b181d4f8,8ff755dd,5f0dba66,ebf95d1f), +S(75f9e3a1,ffdd902,be5d1d2b,3ed18b37,62e42e95,8b024ce8,f892ee13,ab35c674,57fabc84,7c68cfdf,272effee,e7a6ee2c,5fecdeb4,8e4877c,ccf2a8de,2e2264cc), +S(c8a53d5,e1895cdf,db61c1a2,395a866b,ad803773,23ea43ca,775408f7,a4141cc6,ddeff9c9,97a11e4d,d0afab30,f63229f8,1b52ac,c25749ae,2749ddb4,2b201196), +S(8827d23,668f3006,b966cb19,698a066d,aea556b7,4108739d,d6414d8,e8dd78c9,3d009ac7,2baabb53,2e3658e1,8941d4b1,efe512b8,fd7696d5,14b12216,215f10e0), +S(dde6b713,5cf165c6,53c7f5db,542501bc,b0a41e3c,c6779aa6,194ac1d1,8a830b01,25c04b4a,937e16b6,e72616e2,d64a413b,6a7c29ac,71dfcbf6,826261dc,2507c16), +S(8cf6210c,5426b3a5,ed594699,da6cb8b9,8069a3d7,b926cae9,fedb2e96,25d20010,78017fc1,7915e3c8,62557896,acd002fc,7263bd7f,aed38fca,d63ccaa3,57391f30), +S(35772904,91661db6,340e7d1c,a98db8bc,2f50f557,283ccf11,8e446f71,925ade9b,fd8a4a56,b82dff95,a59e29ea,bd5d8e8b,a876bf95,4abf0537,60832d1b,d7edc31c), +S(60e4201c,4dbd9c99,c5eda030,f041f51d,f648f77b,13ec2072,4d89b6be,f249d793,ebd96057,d60b7cae,71349c51,1a3ca99e,847419ff,cdcaa0c8,54d75d40,8263d81b), +S(f39d686,b17ad518,3a7d300f,1bfd015c,57541f08,a96b5943,43d2ef82,bf20bb6e,f1298c9c,633a958a,a0d12b87,8c95836d,f9f93b5f,5e3a6bbd,93a75ba1,1b96d4d0), +S(37ea7dc7,2a3a0b4e,72412814,bc1e1233,17ea3e1c,c4ede35f,cab5d31c,93cc330b,aebaca93,8b14b315,708360e,5662e6a1,af25069b,cc07f581,225dd131,c656c607), +S(ff1f61ed,ba3cfc6f,2fdf3043,37205bab,301c965b,501e4f2f,fcacde00,dd127b25,fb1edca5,90e00f87,eeb56ef9,94e9bb12,b64ad1ea,f5ef0afe,6f0125b4,e100a483), +S(3376e0a5,b93f15b6,c46b92ea,e313ea2d,c5d0b961,a2623487,8919cf5e,25e9ae38,a4cfc1be,fd24647a,20ae014b,2041ab3f,6cb7585d,48a607f8,299e0c10,96bcfdf1), +S(be575f99,842187b,30bcb312,987ea218,3d613442,8878804,b5e7d920,12e58d9c,154e02b0,b573c81b,d2079a4f,4a6efe5f,5f37502b,33225064,4eb38b2c,3f0a1767), +S(5a215836,f1f2997d,9b09d4d0,6473c55c,46cd3006,70d85d6f,6b5531aa,c8da043f,ef5fba1e,b6e1deaf,bd83e480,fcd4984d,cf60f91f,4799e8b4,3f4edd15,f94b6d33), +S(d73fee2,16a503e3,a7d6b0a2,675adb1d,197c3fae,4d43835c,b6b043f5,d44909f5,6f2cc9ee,fbc36b4d,a5b3e704,c13daee9,cb81ce9d,470f1290,8b32f047,ae231053), +S(26fda2e4,d07ec613,ded58e14,ee84e421,ea58e5b9,5beed698,d847f6a6,db185f6,683c0002,bcd6d8b7,1d06e592,db4b961d,f2518fbb,47cb4ad9,96020d84,ed8a5909), +S(cd3d4e87,e433f2b5,5dc55c6,a3f280fc,57f899e1,f8f0777f,32705f3,eb24551c,2315d85b,7521e4e3,8e69f29b,a49a3203,abc7f280,ad49365a,4c78f21c,3ee12542), +S(652476ac,2c9b871c,e108fa13,6a739766,879a76ec,60d787ac,a1b7447a,67f8afd8,b66bf6db,cd3a1393,f70ac7b6,d53069d2,5f084dd7,f05c97a8,76f7f646,c7b67fa8), +S(61ab4f97,61c96446,ea7a75de,7ed36224,7b5ce8b9,e7b442c3,432f9fa0,ec74355e,f391652b,17a910f,6d476c29,89e854f7,d31a5f31,fead5fbd,5013ea56,e44985db), +S(36e4bb36,e62f78a6,a6155351,36a73637,13fe8be8,1c8c3460,a94bb642,4f7c80de,12b38983,c10079f9,b0016df7,fba0685d,5eb87518,234c65c0,7dff006,dbb6a563), +S(f5e4cb89,101e90d4,b2f50097,aa0b22fa,df076932,f4045e9b,7a9ce8d6,ba178780,c12ec4ae,56db7971,3a1bb0f,2559e3a1,de3f8200,650c809,74101940,181eb52b), +S(1138ad12,333790f9,20e979f6,a6471274,2a9bb567,2356e3c,80ffff89,a9de533,4caada48,a78ae26b,790f23c1,606a5820,98642f4,bab5bfff,95748f6a,2ccd6bc), +S(e0346d21,121ff741,fe4eaa23,938a4347,a71df442,9e55359e,aa20d691,c9f40840,c1197c8e,c36676a9,28a92b56,811845d5,d9632c65,9142cc12,3c65e54f,c282dbd), +S(d42011d6,1061388,fec6b7f,3f332b20,24ab318f,2a9ba7ed,23731207,3e32478e,451a2b16,5c82b1f3,e4d2a0a0,bb407b84,904db4d0,caaf733c,e31fa97d,2fb1735b), +S(63964eee,619074e0,780140fe,2e90836,e72328d2,448386d4,59c5be23,187f5048,c49304c5,947630be,5c60064e,3cb40436,c2a7f46c,b221937b,c7c5d7b1,76cf5e37)}, +{S(8f741868,43906722,ab9f5c6e,c796573b,622fa2ae,6d985bfe,ea4d51a1,8f141792,61ecd858,d6992c4a,bdc22472,fb8fd242,929dc5b5,36ea8e35,d1c8c513,bcb9e31e), +S(f2dd67e5,6f75c397,76b29366,149ba4ff,69385651,53afb385,3853acb7,9fc2bd59,3a24641c,d48ca39f,fe992bf1,50291604,79066af4,a0cf364c,9e52a9f,9b51eb66), +S(9386aa5e,c3452728,a8f65141,e2f43a14,c250173,9349c96d,e1968a4e,48cd30,a3a38a85,4f68048,d8f81002,3eef9966,780262cb,aacbe859,a8fc66fc,27c37f32), +S(6c9901ea,920c0f4c,ce51fde7,ab283f98,eebc30dd,c76a3219,1df4c4c9,db467aac,6773f8ae,e601201a,84dd4485,fa560e22,81d5eabb,25a3b3d8,525f4a56,65f79d05), +S(ed1eafec,876154e0,58597caa,8250b0ef,7262ff70,b719ec9b,63c0b2ff,9d167fe3,73505073,2b563f93,89078cc3,93ff9837,227ab8a6,c7d789c,6d2bc835,2018f77b), +S(515806b5,13d4d4bf,83533b56,3b39bf9c,4e808426,f2e4cf16,562e759,cf2d56ab,83c2fb,673a08a6,b8a86b1f,e1ba23d9,3347903a,d6855059,130d8d39,737ddead), +S(30a726d4,d8db1cfa,d59331a0,8f4b5376,36848547,914dcd75,37a80e40,109b8e35,8f628b92,6a11ddc0,dfb1cebb,934aeaa4,3840d442,a61145d1,803f771f,7bf65d7d), +S(116867a8,d72fb00e,80a6f267,e8163770,34affeff,36d667cd,38219f0a,e3c9038d,270f15be,b2a3d50a,4088720a,e9a2c3dd,df00817e,31e1a5b1,d850fe23,dfe51657), +S(abea2899,e4575c6a,201c96fb,a60a24ee,1f1fe0c9,b718b54d,18981938,4f33a1f4,d43d54e3,9e8d433a,ce66ad94,e52662fa,8013e778,f8bc2e3f,31938a6d,5681c660), +S(1fa796d,9f658aa8,6ccd0fb4,6a735651,102e3181,302e371b,8c965dd4,5f078b30,69815d18,43c3b4d9,f7fa4af7,3d2e5dda,390e5372,28608912,79826a53,a437031b), +S(f5022faa,c8caa29f,304ab6c3,d00931c5,e09085e2,73beab04,c1d18f87,1c0fde73,c6abd143,73213fe8,ffa5ec64,8a22dddd,fdf2cb25,e32afd58,4f92dca7,838e8249), +S(8190b10b,97bc72b9,301b1dc,cc60e69e,460e389e,ce7d5d11,4e2284de,853ea02f,f1ffb850,dbff0f06,561b75f6,5d2ae823,cb13881f,d1150eff,274db4cf,112d2253), +S(ea230fbe,c58d7e2f,1a3cea60,f9018562,d2fefb49,8efa312f,4beafcd3,139da094,6045d69d,b27bee6f,3357180c,e92d3866,b480faf4,2debc033,cfa118fb,b89c3bd2), +S(333d063,ccef6aa1,f78c81b3,4a84975c,82bc2cc2,bb897608,f0bed49d,cd08829,90ad105e,f72b7e4f,a927240d,9c69729b,a0aae3a2,3a45342,3be2c385,488840a1), +S(4d95cb48,58d808a3,be82f42c,da4f64a8,c64ae176,3b50257f,2e4ae655,fcc4802,5f6efffc,99bdde54,6d8ffde5,e9a551d0,121a39c3,86db540d,1caf74b4,894c5559), +S(b87b5eca,6e0111bc,5b756816,fa82acf9,77f81376,af796cdd,1e8fcb09,4d363d1,442fa845,9c2a7799,2c09015a,db80f26a,28428851,62432660,56026009,5f2f9d8), +S(34baf43a,a82cce1b,19945590,ef3cb582,2ab03142,6538ea5f,74571582,ee14cf00,9a76705b,c0141e29,75d11543,215377b5,3c051c31,156697cb,e8f56721,da386e2b), +S(f07c5d98,a2579c17,53b3fb58,22a139a1,53e7deeb,ae9304b,114bb188,d61cdd0a,ec18ce0c,1e51b8bc,e12f3d85,23739f8,bc25708f,32deb81d,a780cc4a,1010692d), +S(9a146c80,5ee261ad,3e708885,aa41041a,53bd3758,b3d1ab7a,1a44aa7e,29acca7,fc553e71,4ad8a192,90f94ed7,7c171f9d,9ad9a047,6791f045,4a3ab7d3,f8834f02), +S(c8088409,650feb74,9f9fe21,61772279,128db1d7,58610a9a,8b9fc4b,ef9683b7,692a6be3,132a08d3,82295ee7,1c469465,ae7d63d6,c20c7357,36935003,28799639), +S(53bdc8b2,3edd83b6,b149b79,c88130c6,b207f999,e7045285,7e828f81,b325b9d8,a9a039d1,79486167,8fda727b,1706e9ec,7ec3eba3,26e6a4a7,e27db3f0,8a6ee238), +S(fd08fb47,4af98207,3d8b4ef6,d39012f4,78225f1a,bde3c841,6fe3881b,4fd276c6,d38b53a9,100da1fd,1d2a987e,6907370e,7bca492e,98a22e5d,74a10c9b,93a197e1), +S(5c42ad93,2306363e,a0edf86d,31d14b04,c3f7bc09,c9fe1326,d1ad085d,c84fb7ca,1bdbf812,e9ad6486,ff572d4e,de5be4c9,59de7be3,f21e0dd8,76534091,f13ca3d3), +S(66257876,115e7eef,fc0e6132,9f177d3a,437c81c4,1150f86b,45904f86,30bae971,df830e1e,33aeded,721a0a6a,62a3a402,1d2b7ab7,438b7b3c,d834d67,3a857aed), +S(4feea8d8,2cc6601,c847deff,35581c1d,bf1f4070,402bb948,ce9a41ee,16661627,c09a7686,ad6bcec4,ea9ab0f7,8316adc0,7c7a2899,9332cee7,5d70c50d,249a0007), +S(2d3bb180,73f5345e,cff0c901,9a6f2fb4,e0a237a3,9315fc11,fd53c7d5,aec43670,58a9459c,2710ac75,ff2510ce,4c9c5fb8,b4f739c9,c6611494,daf896db,e43b080), +S(bd539f5f,47b25116,7159153,f402e0af,8650c17a,de7c5dde,2d9d9cc2,d0a3103d,407cab4f,ca581f10,eb386e20,9158541b,8649951e,b2f5e454,2a7032b6,53f0caa), +S(c7a72935,87ba5114,744c3d0,9030be19,9ccea57c,7f80be87,bfb5b79a,8f8e200,7c2a98f8,b4526422,b211bea0,875a81f1,d379012,486db005,e31b9682,404e14a3), +S(cdf20316,f4b63ca4,c472e1cd,b3a938be,291d98b9,3ad8f3f2,4f3e1a4d,f1a6cc80,ab993e8a,68988d4d,2749760f,241399c8,136ee9d2,3d4ae7d1,e6d26df4,6056587b), +S(1d341e6,2eca1f48,165bb2f3,86207da1,f2c7fc86,dff7a75,cb63976e,f21bb074,923b53ed,fe2f5bb5,56aa7cae,2a08ff86,160e3344,c4adc09a,e7aeadfd,f31e2b9c), +S(8704f69d,9d335975,f0e3bb83,5aa3024a,60103d89,e5578253,fbb3d537,1983ce4e,94f4adfa,31dbd13d,95c738b2,8a569453,cba45efd,1cfd20b6,78f6a724,e9a8d025), +S(7175407f,1b58f010,d4cda4c6,2511e59d,b7edcf28,f5476d99,5cf39944,b26b64f1,bc4baabc,bb1c2aaf,c92cbfe,ecb33791,4fe01748,8bb8e2d5,bd918104,4dbdc75a)}, +{S(a16a4734,9afd167f,d5e79d24,9784d87,5121fa24,2c3a54a,88f9b176,72669aa1,4df3fb81,7021efdc,e5296735,3a177b5c,51cc7762,4755ba28,1084db5c,4809e02c), +S(91d1abe3,8d3f9671,54337e03,d27fa6f8,c3007f32,323d15c8,31306b3a,30900328,b1b8c71d,b388009b,98000f9f,291b66e3,f890d42c,a5812f34,5c070f6,e73b89f6), +S(61b64b1c,f2a0e720,e9bb4853,537b9f38,66baff87,388df7f9,f14c06c7,12080b96,44444011,b6ddadc5,7ee00eed,b6af3772,e4116711,c3853f13,7f9c7b00,2bf2a1dc), +S(3b96b2ee,d9cf1ed6,3aaed8e9,aca3a464,d6a6fbf5,e6fd48f0,5aacc318,dfdbc87,51c41c26,506cac9e,bd0c5dbc,8ce8788a,3469e42d,de794707,9e0cd0d9,826e9c58), +S(7bbd147f,77b293ac,ff797b9,a52778de,5874b754,c08df397,72ed8ef7,53242efc,3e843df6,75abef2b,e4d32c83,205322cb,f53bde0,2563eb8e,b5dfbe6f,27d4cada), +S(becb7fc5,de84f994,9f97e55d,bdb6981e,f38c0c45,1e0e454,519c9411,5bae3d7,754145ec,ee6f9c7c,f91963fb,5b0a683a,ef24313f,1125570c,c9b9c7d2,afab99a8), +S(87936626,aa88346d,6d4330d9,3a188ed6,992bdbb8,387b395e,d21dab93,63d833eb,a28c868a,18dbe0e7,12ca5237,53f6a50b,9313e687,85f3b31f,ec778fac,87aa9327), +S(e79d4b2d,e33de1e2,e7eeeb44,d197aec1,f5883ad6,1394dd79,de274ef9,76e3b022,9c73ade7,7c661527,ec2ec6cd,b370c130,380bdd9a,4bcb668a,86e8259a,ca85f293), +S(863f0d65,34de1222,237a07bd,ebb066b5,2c1cdd83,d7a6d3df,278e57c8,bb59b8c7,c546940b,99d04ae2,5cf7c881,28a985cc,9b6223a6,ffc06fe9,8a3e0f68,439b9db6), +S(71e9b0d1,3972d9e0,2225bbda,5a1024e9,c373eeb,f6514eb1,c113ef88,dffdc3a,4436c52e,c58a34c,562638c7,8d76e3fa,29e3586d,fa667577,2fe0b932,c4a1ade2), +S(ef4a500d,c9369125,78fcfcbe,3c313bbf,f0323caa,75750b6b,a317e1e3,dd8054e3,e807e9cf,7911c919,fef82e40,a3ae1b9b,ed8ded38,e74126ed,863199de,c031f2a1), +S(806dd4e0,fbf72904,c7b0080b,4e7526bc,c71fc52d,f143da80,e571041,dd964915,2f4e2e67,3348c32d,6bcc0741,2e76f4fe,944f96bb,d73382c8,c6880cf6,629eac8a), +S(9ea889e8,acf0441d,6e299687,c532ea8f,8b9453ed,5fea0d47,f735056b,1eb3bfa1,e43f79a8,4d302f3c,88698896,5fa7b8f1,a92c620,dfcb3462,c4befe3f,4afbff5e), +S(9a990a22,1d27d8f3,40e79489,58cff3ca,2720dd3d,ac71688d,9c3c9e2f,afeee215,4957ac76,db370918,7992b0b,63370656,7e0f4a0a,9e5f1d9a,be8f7d5e,8a950eef), +S(d3291003,98ad3d92,1d6cbc6e,b37ac3f3,9360cea6,23edd794,64d34a51,fa7adc60,657c110,5bd8df6b,fe52addb,2fdfc41b,810a1ff7,8662021c,7c220b0d,b3509d14), +S(65eac576,e149d97,3cbd772c,60570870,e437e92c,2e5138ac,9c0534df,b7a45582,ebecb6a,edb87cc1,417e066e,fef8e52e,c61eb00c,dfd98205,91d63c08,9dfd5347), +S(b6976b99,c2515be2,2bc901fd,8671255b,f8fd76ca,1f3474cd,5eebe39e,98f6f14f,84ab17d,da67b61c,fd01881e,b1087e69,845aecc9,ce1535fe,1c7245fa,8df98181), +S(c7f5967f,f9910e10,54c93e1e,d0290102,9bf31787,15f53758,f5c53cd3,c79a52bf,bfb01210,5c1ec69c,847ceffc,d1105ac0,528129fe,3b4d3086,2b6e9562,836f5eb2), +S(ffa0da7a,e8984e4,d02a0fe3,acfb4e81,2a63d6b1,682dcd78,fd2635bf,88cfdf1d,49459ee5,3fad1097,2da786fe,45a2d201,e4a52260,7779b1d4,23defafe,29e70098), +S(5a53d683,3bc1740f,d02f278b,2ac15a87,f137f2,c798f157,725a4aac,63f1798d,ac5e7ec5,86d5960f,e25f9e25,7cf9ee05,110d9595,e440d032,c416d444,6242d10), +S(70f67487,9b030a4d,9ccc0bf2,90d5b78,c455f8bd,a1fa2b52,efaf0200,6b70a6cb,10f5b1c5,dc46c0c0,68757804,93805aa7,5a259dbd,95b4bd14,d031daef,a81bf7bb), +S(a4baf0d7,d3a53c29,e78ff81a,52d634a,e009391b,30a9aa91,df8f7bb6,13e3c16,c9f9d001,7f388f09,b6802c45,e1fa036e,99443467,c3847937,8b384a9,dada35cc), +S(351b8e4a,dd336c28,5d140c57,9f2b089d,3c656a05,b1ed6,4cdbaf7e,e5bc671b,b996e61c,cb14553c,b7b79803,c82332b6,704ce97,fb4e19a5,1f70b13d,f8e2732f), +S(d248350,92898c0,5dbb4465,18695efa,e59ac007,d58b5df5,bb3b7038,82f18e61,23455e7f,5104051a,44600be5,2e242a90,95213d25,b4fd65a7,62c3f9fc,9234c2a5), +S(d3fc6d75,10cb0b20,77b52b00,93e84d47,54dd1a28,4f3928f2,a9e3cc02,f1869588,26e86b02,c16171d4,4238aecf,666146f4,9386e0eb,52dfed87,7ac0b409,51ced10b), +S(1c611335,3d115906,182b0e4a,7eb3d39f,552c49f0,c5928aa1,15ae00dd,c23a3b70,1c3e3c4e,ed65ce47,ea82a33f,df8fa6b5,532977d9,53aed38a,f0f4dab3,98477c6b), +S(6487c5c6,abb6e673,821c4b0,2c485f83,3e149af1,83b3f025,71a81fbe,660e98e3,63c4f6b4,7eb37ed6,7668b41,1e83d9c8,9ea323f3,2d9e8918,20b65276,a8a7b7d6), +S(1b0baf63,6daaf2c4,28a62bce,bdd41de2,a4d7cf69,93f0b931,222a40cb,9f5f9a2f,af9a62cd,f91e2c6b,47f308af,e2de8f16,def4972f,24bf4c5b,2b1f92ac,f63a21d2), +S(95bbd974,78e1b8a4,1622e460,8aa82fe9,e77d43e7,492a0d21,1146d987,f9b8f255,412bc52e,d61c3880,210d3df7,f0d2f27,d458d51,388803e,fef1c016,d7c9e7f6), +S(a3466715,5b6ea598,2d3945fb,743a1510,c671b96f,a3494a57,e0349010,55ae087,de35a05a,1a943a42,7210165e,55d5cfae,3ac15490,dc6b0f43,34c0c99b,dd778608), +S(2ed76c11,52ac3600,7aa17c85,a5f902f1,7c845a05,a4e0aaa0,7b05e836,cbcad59,9c60b2bf,bc47a0dd,d3259151,37e89812,a08d39fe,cf5806be,eb538575,3b159531), +S(20e6e2e7,96946bb6,30c7071e,f1b92ea3,d53d280e,e450111,5f5da36f,840dd273,2c528501,b0eaa61b,b5f45e52,6878b9aa,7ee13686,c25796c3,3f8302e9,44b9469c)}, +{S(f040a2fa,b839c753,4e216425,6c2e8dd8,57c52dac,e75cab3d,512a7b1d,562075d2,9e211cf4,bab24e0e,f745e2a0,9b59565b,be7b2449,8123f6b2,ba383b25,29554b36), +S(c66813b,904831cc,12f89e3f,bde93eb3,11ade6dd,f6cfdf72,21cc61a2,ff46bf26,3b60c8c9,9da35a58,7b9650af,f85ca785,ece41146,e7ed8d6d,ef8d26d1,f3d055ce), +S(cd38ece8,7c7a9cbb,e23b58ef,59925633,734de285,f93b3ac4,eb01e03a,adab904b,a5f48fcd,afe414f4,1a44917a,5dcb80b9,30373af9,70e9f22d,c1af80f,a8b06320), +S(72d6f7db,199564a2,6e823a49,eb0171cb,ebb76a2a,add56c7c,6d3102a,9c3ee18c,bd2039ec,c390839f,e2d4451c,9437fef6,3e98dc62,3ab0eea4,c56376f2,e270f626), +S(2ea6c313,1e101867,2454e15c,d34d5fad,13829d5,4ca86062,f074e512,a274167e,67e3d34a,56f12e1b,43687f51,c2d56395,14355d7d,9cc52726,22f62ab8,91c8d5de), +S(d66bdd78,8b20da57,b2485804,a2f6e2ef,78c7ca87,ae1c5c66,fc533533,5b624162,1a8688a1,300e6d39,e130037d,3f074ccc,57addd1b,88977e64,2ab296ac,d6e054c9), +S(6d6458d5,d1683d85,8f9e40,15e5a94a,73868bda,54f6139c,446b5283,6d211542,e44c7ca8,33761e13,ca5458ff,dc682f22,3819411c,a3c5feb6,24f06412,471b9f5a), +S(49f8063,b3d98f6,f159f2e6,65b0e07,8ef8c7df,44da06c3,55b592ac,13402b9d,b0b2d838,4b24ab11,de895013,e897e825,45c3e8dd,6cdb0990,555cd871,5841f8cd), +S(8b2fb6bd,e60449da,2684f8e3,6267fff7,254051ac,faea37cb,102c9bfa,91abb2ca,c82cb9ab,c33c1817,10d3fb19,60f2850b,5072eb47,404266f7,c7964fb,f98027d3), +S(cf90331d,73644476,47c8f86e,eaa6e67b,f3fb4b3c,4c895b82,30ab2f38,80df1e42,9c493d94,3471efd5,ea90f723,db576a35,8cd1ee6,1a119b56,6c65b16,112eb2b4), +S(e70667cd,8d2759e,43f99417,ecc505a7,953d80dd,e7ce2cba,3c8c948b,d43a912a,92a534b3,51449a66,82395a3,b75877ae,851923a2,e791acc3,7293d728,777a2247), +S(4cc07d11,4a715e9f,4b4d0811,f0337c7e,f4bdff74,2fb008d2,1ce8a9ea,50520d47,478b279,613d92d3,dd24ae31,2658eda8,28a7d628,f0346e04,5d5d9dff,7178f093), +S(2a1cee60,4cdd3b2c,1f859bfa,af63d7e3,9aaad910,92c2a38f,a9362798,9753c30d,325c248e,d409e8f3,b96dccab,6fdb62a6,83b7eaf3,36d2864e,b821663d,ed31a6f5), +S(7c18d0bb,9a01969d,302616cb,bad8722e,d00103fa,9985a50b,765f0e2b,9a2ea14c,f6365a6c,b06e4749,c48c212c,63fff571,8f46e0cf,6fdc8c42,34d4db64,ceb72080), +S(a706754,65cde407,e853e882,8fb7a2fe,98638d7e,64b8df0f,b0a7a0b4,e629d0ee,fd694b2c,1354a5e9,415926d0,1590eaac,dbd1b36b,5ecc46b5,72467c3f,e2cc5393), +S(7ff57a63,36ec73da,cc1dc527,928cc6ff,4ec063b1,56b36292,23b8bbb0,a8ba2c7d,4d6dfc43,da0fe33a,1e75dbf5,5e5d669e,be6471d,eb8a4102,6db1a41e,9d5da637), +S(d4b1f7bc,87558c27,7e0fdf91,22bfe488,b3182555,599dd384,5cc039b5,93218e9f,992f79dd,c8f46682,5c44a54d,626b352f,7f59139b,d545f78b,44bd0b10,55c3c1fa), +S(897773f,a9de63a7,46ebf58e,248d0c21,10f90230,a792f0b6,ea5a25a0,9d1940d2,b7c7dd27,d561dde6,ca7588b,8e3a09c7,16297ba3,9d501744,f9639a25,b16b4f64), +S(2f9cb312,b071a600,e5d24cfc,79b0fdbb,f6da1527,10ec395d,53078453,1a3b07e7,49260f95,757b9b66,feeb5c20,16aa051,b444bfc8,836fd8a2,ab913e98,731fa97e), +S(5c53b27c,4c0ae241,8569c806,8dd43a5c,6fa23ac6,74d001b4,eee6a748,d86c9e6d,4e0e720a,f430972f,b0ab89b8,79a56eb4,f0af5161,cae83ca1,964a8099,f9d497c1), +S(2671ef50,5a321387,9560f0d9,e8588707,1c2b87b5,d06bb9d,dec3d179,6275b098,750ea18b,dbdcbd58,be994eb5,cb3539f5,8c3dc630,7d50bbf1,f69050d6,235826be), +S(59baa5ce,9d6e707b,b0738f4f,d37c9890,801ed796,a82afb2a,ecd2c5ba,195be65b,e6b66356,20a8a106,b58dc202,10cccdd1,e0e8c579,c92f3fbf,8b335765,b30d8291), +S(bb422d0,88abedca,e4686479,295aa1a1,77d7e68b,293bcd31,4097ddc1,af764b90,a96d33f6,46d4861d,a471f853,e0c6ae32,b1eea619,a7441504,a773f5e6,fc548805), +S(8d7df8a5,f08ba2b0,923662fc,e594f111,5295cf6e,c5aec9ca,df0f7236,fe51b362,26452215,7d8572d0,f057ec9,6f572f11,ebad3fd0,56032240,26b54df2,991f9046), +S(9e2b1637,352d8988,1e76fdec,c2dc8533,6a71b18b,3c98e59d,397602f5,8b9087d7,b997404f,1c0b1e64,90cff078,fdc47c42,aedf245a,e69d844c,18a146fa,6d8c3d13), +S(972c7bc4,6362bca6,b58f1d0b,87c0b8dc,fe91a217,7f0cd7af,404f6f06,47e09358,cfda778,a59d944b,4ec08bcb,3aabd70a,cd6f29c5,204c9161,fe2e5bd5,71d03f6c), +S(f7dd32ed,89ceaace,52e7f26b,202ea6a5,c7e8ff42,6c7ebc16,12106f7c,6fe51a54,7349c17d,d8ea6ad1,cb346e3d,26989950,3de00c11,4205b3b5,50d27ff7,67efe75d), +S(c9f96f88,3e34aed,97351928,fa77b07a,f4b01bb4,e4515f0,59745ee5,decc9fbc,6fd62453,dd6c9dab,10fb7d79,c15b4822,178eba31,258800e4,519e627e,91ef20c9), +S(9c28cc93,809b35b3,96a4c9cd,d6976391,17402ffb,5588cd76,7c41a92f,e971a9a,ca3b2634,28513516,86a661c4,747aece4,c8130d93,ff4108a6,3365e725,2bbb80ce), +S(263a3fca,437c4dbf,48ca36e6,aa892d61,abf58f6a,d3c82b1f,fe945dc,c45a15f8,604c5bec,69e0f1a7,3e8f8a80,4f762cda,96ba2198,35268887,910c8df0,e83d3044), +S(7fe42dfb,b3466ed4,d7cb8242,5e66b5e1,14a70516,b118911f,9170656a,3dbb04fa,d11385f4,b0a3a0bc,cf09374f,868cb30f,55175d77,2d3e463a,a298b6ed,1e6f6fa6), +S(53f2432b,a8171714,3fa9df3d,ff41ced2,4a29b314,bc5a8c96,f5f6400a,d7c0979,42ad1004,3e0f8648,332b1c1f,6ee4f821,b42a5b0a,361747ba,60816f2,ac84c58d)}, +{S(b8b52efd,bc367a5c,a1534bb2,f339a048,50fc4941,1668780,dcdb8b51,2daa7526,306a2236,856070ea,1e0fd846,cd571889,e20968cf,cd490bc9,facfc4e5,a5970b2a), +S(27e226b6,ed59c89,5bd20232,bc677c35,495d9601,5f37493f,5075718c,2017e42c,a130a5c8,54ed8ab3,ed657e0f,ee4a04cc,1b59a291,6e96465a,74b36144,7cc621f0), +S(1733f1c0,da38fa79,a7b37988,1822ef51,2392d8cb,59d1ad1e,afaf535a,e151fb64,a93ddb00,af779565,eeb230af,815f9434,958ad39b,387d9069,a03cfcb,8563a920), +S(e3f4b777,d06c8839,c63dfb41,73281787,72debac0,ed45b991,724c19bd,85ab3c58,2d6a84ea,745decd9,e6444b7a,dbb59fa3,21957b7c,3f8141e6,fd633d3f,7539dcdb), +S(a2b42fb8,9ac10df9,ebe804b6,c105e855,25cb05eb,7407d383,3031e216,64491ff5,d0973dbd,346a87b4,f4ddfddd,12be3131,e6c6a397,8e17be2c,bda23683,62f80765), +S(66d441a2,6b28378b,cf23d33b,ffac1163,7c23bbb7,7ad352e7,ff5cc09f,41bf6e91,982aa46e,5fa0b19a,28ecfdee,539c0ec,b5f06e2a,217f7687,6c3cbf1d,e92a6068), +S(3721fccc,20338f8a,779aceb8,eae3fc7f,bb0e4040,1de2c8e5,27941ad7,f789d0f7,d9516c54,8f457b6e,d44f6bbb,23f36c7c,e6fdc548,60e5042a,b3dd4f67,ad5ae3ea), +S(959e980,4618bed2,73b5cc48,5565df27,b8e09c9,a128a9e5,ea92e347,66e02015,cee3a227,1bddee01,3582d65d,c3e26c8a,f80a4601,abcff432,b1737127,91ca15a4), +S(f2e81d16,c19466a6,1b56931,935d03e2,aad6b8d0,7f193d97,bacdc29e,13cdcf38,afb8e973,8d563dc4,1e7688f6,c6b43b71,f366b576,9479f093,88fd09e5,faa1063f), +S(98305a48,800f92e0,bd815192,7b8658fc,60a97c64,c9f97383,68812614,3563910e,d32db8e2,6699b969,5b644c08,39f2399,f8ef45ad,a0d857c5,2d0c7c3f,96a62993), +S(8dbd4fba,113c50ba,ff3f251c,96aba9c0,440d8e69,8a1e4ec1,84fba97a,e497c443,f642b1d9,b012eef2,40c6ad38,fe441d11,cfb42ec2,a3c15540,45e64c76,c15be491), +S(deea5bb6,c7eac0a2,93e90817,836a3e55,2a7f1354,395296c8,97964c2a,4527a727,4cbe143c,c9fde617,c6142335,4323b9d8,bba13dd7,fbd46db5,cbf4ce2a,11c9c00), +S(349f46a5,8621034,9f207a07,4558787d,336cb0cf,a56c1e67,8dd3b04a,4d717f4b,a6479d8,f7f017a6,32c369c0,8679fecd,985b3c16,ff444ba0,3fe43575,8b96455a), +S(a6d818c9,dc182fd7,81343bc,b737811f,611c8499,e44a6cdb,33a36beb,7e44b545,1a8ee36e,d6e9b191,b9cbf840,3924e6b8,ac3c3f8a,cbc9a0b5,ff965d6c,734d741d), +S(f9cad333,7b82ea41,b00f3ef1,bba37b8b,fcb24e8a,175fe380,8e399b67,d21837e7,cb206854,4e7f2cda,28c035f1,b8b50009,319797da,bb498b99,d31e24e2,4fa27ca9), +S(25042b59,f9b29a9c,83d04515,c78f1c25,5b72f97f,7b387113,e88c36b9,9da3ebdc,d966ba9e,a7eb9734,e1481cb7,2df63252,7492e1c4,43f1fc9,c0efe0af,442b5a13), +S(e19c4319,b5c9a01e,b588c629,8a7c6b76,1b80bf4b,e39424e0,666ddc45,fd57e0f6,1b0a2c30,4a1bb1b1,9dd0b50d,29df625,bf958a73,46cc6b1d,81a213a3,b6636285), +S(59c26fc1,6d151c0c,7da0edf6,c474463c,587c45cb,7e407589,d15aad89,de223fa0,f7b227b5,4855c1c4,1e576431,8a7106eb,ea214686,ef815b42,db8c18ae,d5c940a5), +S(d967d546,e07ac1c2,31fd50df,a1d25a51,60594d88,3e816f6a,bd62c15c,446aeff6,8eee6c03,6e22a014,5d9cf3af,9b44e005,cd1d36ce,d7c420f5,e2db918b,e4fbf83a), +S(424b149d,9104254e,3f5772b3,7d973cf2,a46bd110,89d8f850,7666f04d,382c972b,7272ad07,54c18fbd,66e2abb3,952c526,d8113d5b,166283a3,9ac8ecb6,2b7a3e46), +S(40b4b2ac,bd299270,e64bc470,8607c586,3bb50913,45e573f3,a5e2461b,47dbc053,d094311e,fd81bb1f,4622d17e,68044107,a2a8ed54,3cd5ff8c,b531b28b,9b8ec99e), +S(79e79ded,6bc0d851,fa769b5e,8d65857c,f3c1c18e,6c990cb6,116fc45b,63e9f924,4fa89e89,ae4214fc,ba2aecdb,41984788,8154eaa6,bcbbe29f,f04a6262,7299f469), +S(a11ad5ea,59c644b2,8dd7402a,5f96a970,68d96e2e,eb77d59a,fe0c2fc,cc6b252f,38e379d5,150109df,36ed7ad3,a2daa41b,4fb9c731,718cf5e3,8f8f8cf7,d1f3d770), +S(b3002b00,ec5be154,cd8e5226,4b97f5c9,f444b305,8f59c4c,bcda5edf,9d10dc88,f7e9ae65,6ad2c823,8e8e0436,e007d821,b17d9405,984eccc4,617e3977,2f25d636), +S(8d1267b1,94da1334,fd20cb8f,77fa01cf,9e9ced90,43107e41,d1ec4057,eb6115a2,a7ba1b9e,2c6f0f19,5ff1f50a,7a8da22f,da3003fa,44b818b9,953b44d3,e6d6a0df), +S(dde6a3a0,87ef41db,2359e4e7,ca7cb064,424301e7,c5cbb5bc,4b6e504a,f3af4837,24175b1c,b4b39b7e,23718e26,be4a22bb,8bf54302,1f156234,e7f18010,d538aeb), +S(79efa584,2fb89f4e,85f556e,c4be170d,5c1ef88a,3f3f75cf,6b20fc1a,96984ad,b0704aaf,e4d8f30f,4f29fa85,6d55da13,91f5f3ce,36cdef6f,25133999,cc8541a), +S(3aa09dc0,5546977f,7369fa0a,7b60c94b,eab86a1,10d85af7,7b826475,7d67eaf5,343b4cf3,aa6c5ebc,ed041d7f,2381be57,f7fd32d0,29a2f42,a7c8821,4f425d8), +S(53fe8af9,8f9419fb,1445bb6a,94750d46,46be9f37,b7155763,88b11064,df2e6ffa,6cd77e81,84aeab9d,e9bf3093,bb9b21a3,a04a0719,fc428f3d,d5e14c8c,9b16cd7a), +S(fc395dc4,a5114dc8,bcb0f7f0,3a4d3e9,38a87a3d,78d33864,d1dc4cba,9e41e20e,c7991f6e,f72f82f6,b3147e56,74224929,fddeb8a5,97a1b1d4,8852733c,1830b941), +S(b1d25d51,b4558f5f,d0ccb868,3af9a9cf,62a169c6,91627fa5,92d80b18,36695f94,8f92258d,fcf16f4e,8415f53,e65457e8,9f090e72,5479c123,5a481464,a11cd4f9), +S(b866d6b1,42df940f,2cf28b54,c92f0c12,94e0b6a2,2a91f2ef,44bcd88c,4384480d,e6eb4f4c,bd95148f,765d8728,15652853,db1add7f,b4e27929,f19a64b7,f3b34c87)}, +{S(d4aaf32e,dd897f48,5ab16466,76d747cd,fec5b7a0,a05c917b,60c07311,45d16f89,4857e649,beff4089,2470f700,cadb73ae,d4866a84,c3f0b975,6fd8570e,df9d29d8), +S(4ebe5b8a,f3b1583c,9f41bd98,afaba549,f45c381e,c93ec9d1,4a543d87,9cefbaf3,63ed1d17,9b215f34,f1f580be,4b3f671f,2fa14689,74cd03d1,19fadae3,c92935ff), +S(20df6160,556dd3c5,a26620a8,70ff5323,cbbab94,be3e3bdd,ad8b936c,554f28f1,7168e7ab,cdbafad,26fbb5be,a09dadde,ed36c20f,c8deafaf,75a480b5,f980e981), +S(86c663f2,cc6f7ee2,50be25fd,fb35fd8e,5873481b,c477bf5b,c0f1bc1e,3f1320f,c13c5795,cce0ad92,e31240da,90a823d0,d2352314,f2852e67,cb62e0c2,8570994a), +S(3fd8085e,57f6870,b25e41d9,7df00126,150f6022,a7ede22f,d27da581,d234ccaa,c6657746,98d6ac03,ed1ea2d7,bd3f70be,86351c5d,407a4d7c,227fd7d0,c330ef04), +S(1510fcf0,b0fb5a23,c15e041,2a7f6d89,9fac4244,8ce316bf,d88fb58b,10e23118,1879dc07,b34d0a62,f0b9517f,3629ae0f,e8aa6d30,5a783f37,69bdf65,3b40408e), +S(67e9982b,c40f427,98df055c,2d8bdcc0,2ea97bad,68d34024,85498e27,d99e19e4,4f087caa,efc5fd7f,b099c134,a273635c,d1d5ace3,26e3103f,9b2fe22c,e95f288e), +S(e8eeab21,6c3395d6,d16338ea,de4108da,1c25e471,67fb13d5,99e8daaf,93e7345f,7fd2ba87,7c7266a5,65a31548,99950ea1,e5e3d73f,612addea,318ad5bc,a965228d), +S(ab20c7c8,9dd74c20,447bc762,e374c548,e7a8ac31,eec11123,1a040cd1,36e9a546,facacfb2,52eb9532,9106a26a,60128a0f,ae1d7e5c,fd39483c,aeb54dd9,231b55ee), +S(a9004211,b7cf5494,1c530a0e,d658fb6e,3b5d2ee1,37b7d7de,45fef6b5,efddbf0f,58cbaf67,5cc5a131,54eef7a1,462b5d1c,408d5d56,3553ca55,45388d77,c6391d4f), +S(8db64274,422ae0f5,6474b83a,89912fa,e3a39b6c,b3fb8370,308a572e,b6b3c020,9b71a72d,431fd44,aa8632ee,23bc678,61d96edd,b7ec72ab,4b1679b8,a4ba73e4), +S(301b330,c478189d,dce4314f,380cb2d8,38bbd416,5dfad8c4,90f2443f,2a56148b,737da0,1c05cf73,db36a1a4,5005f562,1327167f,d3b60ece,3da3705d,131903fe), +S(b9cbc7a3,4a1cbdd1,300e789d,b7ffec1c,dfe1f738,fbe1fe19,67dd7c14,92ee3ba7,5a816a0,9d08c4be,2b8d062f,200fde4c,534fbdbf,854c7e53,64532c08,60c97c01), +S(6a15e51,6f9f0a7c,25f2eac7,649285ea,d86a1e01,5a386dc,c7f94ba8,4782b45b,63eafaf4,660642ef,988aeee7,642e7c4b,71124a1c,2ca2f5c3,d4f2eec9,ea72d70e), +S(f4c696f3,34c56e08,2ab2e627,4d450f34,fa7fbce,29f3e14d,332bcadf,87d5fe0d,6ee44edf,f574d9a8,363f98b6,b863a648,27d3020,1e652935,be12041c,1bbcc7c3), +S(d2f3d5ad,237ba177,ed312bbb,1d02fb2a,e4bda20f,41f5c217,8b70dd9a,c26ee9b0,cc5912d4,2ea1af01,aa914b98,8c42cef8,46a5a059,664c8545,fc00b662,e0327920), +S(5588b0a4,757beb33,e668af5f,adf87fb3,2aed8e16,e6ceabf1,1576ae17,c6a2b843,70d8961b,ac203e89,c437b88b,ce331309,c4f7eca8,d614299c,74aef9b2,17c741de), +S(f993cd07,f6eb75ad,d41426d5,cc653886,18dde1f0,88aaf3cd,b2631eea,158373e0,1e74f804,5f60349e,a85b90e1,206f0ea4,b0cd3740,861186be,648063e8,58c2b395), +S(2cd04170,78291a50,750218b9,44a11a64,1cb75e7d,cd5cc4f9,64f53c8d,a1ef08f4,1f54a70f,f66ce343,c7ed3001,78d7b99e,49129e23,90b5a026,7ba25af5,3467b6d3), +S(52fc09a1,d4f0cc2e,c7ba6cba,1b30e1f0,b9e37f31,3bf59c33,60fc02e3,3b0c15e8,9bd14bcd,a7c7b6ab,dd7e17dd,ab81a1cf,846be336,a76858a1,8e8a0194,776f0a80), +S(90d29aba,764a9b49,1e2878c2,9b964bf6,e510af,3dd5aeb9,3fe29e13,b8f03b23,396b1f6d,e9b8fd1f,463ca064,11b09823,26cb6ab7,4bbbcaef,43031497,f149a620), +S(ce1d6e8e,ddd063f4,316cb006,b4a4213d,9e0952c8,43e6e1e7,570a35e,aad7fa53,a1abcd7,e2c23ad0,895c5aa8,82a805cd,1d7d434c,72f0c528,2ceec0b7,a7016f7c), +S(4df6e1d3,93a4be66,6fd88c75,e1e686a1,e2cb1844,f5f1303e,f7b0a751,99b42bf5,262d8b9,f7e202a3,78a51928,91ee3fc9,f269ef5d,e14dd0ff,f1fec9f4,4cdb9058), +S(f71b9bfa,bcc353a4,11a055eb,a7d6f48f,47c4e437,96382bcc,d5b882cd,a9059638,bfc1c3f8,fea7df8b,d1cd088c,b1f02e47,377d6656,9b3bd8a2,8b457ba5,e6453468), +S(ad5e9212,d3ef76d,8319fd98,72ee753f,ec99fab0,6244f4e2,a9567068,a635eb53,65fb538f,cb570092,7b0f775a,73ffb0c1,4e6c2274,e253f3a6,2dba9caa,27a3e5e4), +S(81a7dfa0,7659fdbd,57e1c8ae,20c14a80,ee8bfc51,46caa8db,b077b653,23bad4a,ac9d2438,3ef0d99f,26744be4,77e69d51,49d44ab9,84801bb,759d3a5b,7fed1e2e), +S(3bf8c1d0,a613316b,e90eab62,6788c8c3,b1641d65,d0e50245,9e5b19c1,ab1eb617,e1edb59a,8594d73,669e6f93,ca10e210,1254946b,8c9e8eac,3fa84a49,e4e2944f), +S(30f9283a,767c551c,e9fe87d9,5730eff6,fc302a4a,1810e4de,11ba62af,fa17eaae,1f383ac5,e714240d,67980617,d96b0c05,76008599,f8e3fa04,d57454e9,927e729b), +S(1c5d4ac6,91d37d0c,9db5e071,c809a59e,99056263,fbd83028,ee9530e9,c2b3f3c9,b98c7be9,7a2fb196,3d3d4a75,4f7fecce,2fbeb8fd,cf23c229,73b6875,86bd276a), +S(3dfe48e5,2f055be1,1db79fa4,501b44d8,f5ae8447,c5a1293a,a79f683f,1e1e9bf,2d7e8303,b988fc17,18c9b64c,1c03373d,27fd0c32,13c26e6c,b684ca94,49f79d5e), +S(adc4251b,c3ef6d5a,a4d6eb22,46ab2787,bc835b65,1d17ceae,489f181d,57d37248,4f0c489d,1c3a1814,a2f9c1c9,d6fe908f,4623aa44,ebaf4143,79fb8352,15234014), +S(edfe16b2,db401803,11f98920,7a2fef7,d05b2a3b,b676899f,9c6e2192,d38f93e0,1196fd0e,35a24c9,6b28b055,b4fa2f2d,a4c2aeff,3b91dd81,c2fe86c1,1d6bf682)}, +{S(b4b57b34,6e1b41b,7394e655,e059cc93,44c5d83e,f0ef17db,2789db5d,13777b78,6fa187a7,4d983b7e,cea6a268,61039430,63e599e7,cba73144,fe3eb23,677c6265), +S(9937238,7300054a,1a8713b7,6942f003,99818b28,6c6fba5d,ca60b9da,1aa7767,6c003e1,848ce30a,65601eb,e7b76acf,b2e122b6,882d7439,f463c042,ef1b31a), +S(ebd177f8,b5757665,58851c7f,a7ada38b,e3596080,f2679181,f994efd3,82714784,d0fe8eb0,97c852f7,b284d5f,bd81f77f,79c35af7,cc30338f,4e63211e,9e20a244), +S(367ef258,b279f923,a18215b7,67d396e4,c6bc874d,2be6b369,b01afffa,2e041741,bd961242,2452eddb,33831dcf,fb3a0273,30ef141f,6db77d91,5bec2ecb,553b6e6c), +S(d89f40c4,f752cff4,5bae5762,37f5096e,f27d46d0,d934d4de,c3011a28,59464e8e,9d32661e,e2319d6,f6427ea,a3014b5,cf98306c,666c4a9e,970dba2e,65e04886), +S(ef6ff862,1d84c5d5,22e490f4,db21181f,a17bafea,3e097868,4f1f0215,759772b9,ae006f11,aa85db2d,3624cf51,ae319ad2,8b4970e7,eac03301,1eb15db8,dc59d62b), +S(8b0a1b1b,d476bbfb,6da1d071,68d3a988,3b49802f,8ffa04a3,bbba1313,17c67a00,d8c74ae5,65c38597,8d9fda2e,dff6b932,1512e40b,ec5b62e2,59d4d81c,9b657b55), +S(9aa9250f,12ffde38,21384434,849d7eb7,bcafbae5,9a3b861f,68a3addb,d75d3715,19b4b1a8,8d8bfeee,38b54564,30d5f73e,ed0365fe,49092d4a,2f52fa12,b65e3ac7), +S(aaccc00f,55e96f0c,e66d9b43,12f9abd2,6772d8b4,30a6c62,edd2aab4,97acbc22,5af203cf,97d11147,82374056,f012f5f9,2825476a,820dce2e,6e1c4b33,cef5337c), +S(43c34546,2836b544,ddd6e432,6aa19b4a,9de7fdfb,86f43cae,ce98ce7c,32b1ccaf,dd518249,f1fbc1c7,1f905860,17a6f60b,15be97d4,18c66d42,f4f9adcd,d722c096), +S(9a38eb25,935441ab,8983097a,d58c821f,d784f091,5f85dc26,3f0c020b,b80c39a0,d8e6dc57,41e4ba5c,576179e8,9a6dcb4a,bd7178c6,13a1100b,e2bd731b,f3e819e2), +S(6b10527,204a4f7c,e8afcacc,fe771634,376f18da,f12774e,50f23a90,9efe0223,b443a451,529b4208,c8c98904,f5eb2ee,2e2ede2d,80ca50c5,aa29b95a,714181a), +S(1a88fecc,71d7550a,abf7faf0,8e884e95,835f6dc8,1815d2d7,7dab66ea,b238d6b2,82cf6711,1dd38ff,d0daebbc,ea2c064b,17b6b21d,34804f93,4a037936,9edec692), +S(a6b1751,f30507fe,5927b6d4,a92ebbbd,77358c02,9b750d05,a56e4d41,efb99144,ccf7c048,a2a0983d,fdcccfd,a071b80,e8035866,c479ea3a,3bbe7c80,12dfd618), +S(e186601e,8b545292,f23ae7fa,4cadc80f,1fcaa1a3,430ddec6,cfd2af47,c835cdf9,fd471f16,7c3683c9,2e5fc0c2,89b646a5,c8ebda6d,5b6da213,d862c3f6,2c292e9c), +S(91b3935,d8ce2b80,c179ee0b,87850140,4c418c7f,7cf94e4a,64b9fc9c,e396a093,45a5043b,8184546c,9083982f,550807d7,d61fe677,ce73ef21,9b3aa573,97c4eca1), +S(3fbe61fd,2a387654,dfc428e7,dac09e91,5e1ae7d6,d3794288,743f2535,20858ef5,206fd9a0,e3265118,e0a6475c,4df87e43,cc7c8898,59d8aa08,23b6dfff,b68b34d7), +S(31a35020,697d9d52,55476289,ce4fcbdc,4e1e0690,c2c36d1b,a4f4640f,e4c5ba18,91f660f9,159d463a,857f08ad,fc3c5ddd,804507c5,92f73de4,2ee44555,da538553), +S(7dc6b83a,10f26e71,e12c0bd,7ebcccc6,61b4394a,68f576fe,bfca1c23,256e8aa2,b4796977,bd386b98,f3e2fa8e,798b6548,fae52a80,40bb12ea,7e6ad675,64fc8736), +S(67a9c3ff,9c7b99b2,5318ff9e,2b85ee23,ec244655,8042ea8b,d880e23e,7d3ce221,a7bcbbbb,895ca7b1,847a43fa,98809ce4,98016123,bbb3c099,7d34debd,cb023f8d), +S(36602997,eba2c6c5,aab4cb65,8d4efd46,65ab3965,f643ff66,831646dc,f6443bf3,c82d0e80,ef775f9c,7ec7862a,e3158556,a57038d,e6850dd1,e349629c,2546239), +S(9eb9244b,299bfd76,f63bb140,ae2f42a1,4af1e072,83f8259f,6478ac03,d411df24,e43f91da,13c2ba21,a9d17d2f,7a8a629d,9051346,93a6552a,538d590,9d2bdd38), +S(ba4450fa,27d57993,3d949a1a,66013906,7d1a9716,6794993d,e45d9426,b4bfc5ed,8e3f3b9b,d8cb8210,7a5ae2af,92b96259,b73633f,6528ac04,20c3e101,6bb6d8a8), +S(e6d5335f,b67abd36,2712c7fe,575ee769,53d488b,236e8589,5664fb04,7f385248,9e255154,e070a5bb,e98e97f6,281fb999,a75e68ea,7681fdf6,fea093bd,919aa5a4), +S(63c099a9,300a331a,3bc4348a,a373e514,c84a810e,1ea8a8d1,4ac8d7cb,818eb534,ed7d4029,3cc4a086,b1d40c24,6086109,a02dbcb5,a864777,27939024,6fd6bf91), +S(7088617f,66cd93f,8130eaeb,874c3f77,3bcb0daa,78923b70,a65512a2,992554dc,5a3ff995,3c712066,291ac4a2,dfc2573e,1d0aed56,75ef526b,b15f61b0,d20f8156), +S(cc354aaf,746d756b,9387730f,2f12b999,4fcd815c,6d25cdab,2e1185aa,4aa639b3,fb1dead9,4fd76859,1aac8d79,5c337924,dc155fbe,5cd5c953,4d55ee31,f6d284a6), +S(e7e47c3a,e2bf4307,7f33ec3d,ac1ae2a7,5c669e61,e248f588,590cfe07,3e2186e0,46e9c4d5,4429fdcf,5efb375e,18f46cc8,3fa7a403,6013777f,e450e228,aebd4cd0), +S(c6ed5e63,28ec31b1,37041108,42d4ac23,fbb883bb,349d88db,1d747cf7,325fe9cc,21b8bfa4,b62807cf,d6cd11ed,a38084d5,7bb068d4,9f0dd30e,f3f431e2,b57022e4), +S(331924c7,50b3417e,48e279ff,6ed7f3dc,ba2e121e,9f69f8fb,e64093b6,c38a8cc6,357086be,e4cc0fe7,d45a49fe,41d004ce,80466165,316164ab,3b171f03,b9a7841f), +S(b4319cc9,f3e0d3b,313626ae,5385796b,c49b300c,88ba4553,dec0aa4a,77829372,b8f82bb6,6590afd4,48501f,63de3a5d,8f84edea,35c1ee44,88d6333e,f522a808), +S(da433d5e,11ceccc0,abc5c762,6ce7bab4,2e89b221,f785c409,282de545,f3fceb19,1b67242c,de57efcf,e214423b,506a1ade,718803d2,6dd84d88,97b18ede,590a2fcb)}, +{S(ebae7464,ece277b8,309d056c,cf3775e2,11a77aa,81c316a4,b6b7b953,6f90f539,62a4d6a2,95fdd811,f2051715,322f17e5,1753c9d0,835ef9a0,3e8dc9f5,c69bda31), +S(5b20700c,1af63394,a3e4cadb,b0ea682e,19cf495a,b908418a,d41f4c6c,ea4477f0,8435d15e,34b03a22,ca89c73d,326c2f88,142d2e23,8964ef6d,99287125,79738f78), +S(b2f25f91,138b974b,561328c0,92f50fdf,8a694004,fd4879d0,12a4a6cc,c8059d1d,d9ec0b2d,b8e81b2,f6feaf55,9776ff78,cda3401b,47826de0,ca06e579,9d866c09), +S(64e79790,7783d6fb,321eb785,acdeb2ff,4628270,fec3342c,527c8db0,a34c516e,c0c4da5d,abf3da9c,a8613f82,ca28e84,a1a18376,c9967c08,aaf70bea,7a2f34c2), +S(a50cae84,a08f4f32,4984853a,d4c6bdaf,77eaa788,9cb8fe95,72fd56e,9078ee73,120ecbf,76e47b38,503067c8,36853003,60ed261a,40fdf440,161ef2cd,19786b93), +S(76ff3ac8,80a451a9,6741fc70,82bc4c82,7bd51c08,95eea8c8,b422b8a9,96ba4794,25555d71,d4313a60,b189a154,58e0a40,c643c204,277334e,966e2abd,3a23f6c3), +S(307e5caa,8b708760,3d9f845b,b78f4f10,72c49c10,9747f91d,192f8d6e,70c0f4be,f5992f76,37167e1d,55cd31a6,1cdb4756,85eb1503,34fb6009,e0240a18,fa8302b8), +S(d8e62e47,b28fd165,4d420137,884dcf21,c10cb159,d9885843,da34b0d9,d09b2758,675c75b4,b903ad28,2055a71b,2c470e2b,1dce69bf,ecf160cf,3a027de2,55b723a9), +S(ec5639c7,73dac23d,3008dabf,732dc005,3154703d,26ee3bbc,7689286e,4a73e39f,aaa5ea4a,2cf5c23d,57cdd0d8,8accbbf9,9fd2c213,97bb45f8,83547876,3aab1248), +S(3d928ee3,2065557a,27722f08,5202f48f,40917ed6,c7ac1963,2a122226,8b0cb9df,ece112a4,e7a05ad6,17c2fd83,76f89102,9eb94eb0,930fb23c,a342d7f1,f021a0c7), +S(616228a5,cc6c3a92,cd694cec,4b2b8574,5dfffa90,b2a36d8f,32eaa6cc,2530de51,50d8d140,4d506a78,700f8e82,967b36a5,1b4de644,a8e22271,d509c8d2,4adf3148), +S(6f85f308,e847b145,c13a07be,bebdd4ba,6c138939,6afd8670,733f44a8,969406d0,864bcde1,be36c9c7,b7e04ef8,cd2366e2,b3c22902,5ee7efce,d005ef5b,ff7dae2b), +S(f3d1ecc3,53a82621,1bd78f0f,aa029076,c6ca779,9bdc54b2,128fa3d2,aec00c6d,75aa0cf9,fd0b7bed,19183e34,58142f66,c94a5aa9,3915435e,ae51c1fe,96625ccd), +S(a5918de3,f18cd8d1,2a234f9f,3a19fcaa,2d83671d,ad566f16,8aea2b1f,b9de41b0,c515dda6,43c0f6c8,d8fea16,5d595917,16b7f521,43d3b941,f28585ed,8a49c06c), +S(3c49e2ef,1d2d3464,81e05fa0,33366609,1615ebcf,535f68,be257f35,7bd91592,57901e0a,c8af3a31,aa404987,72c903fd,175c9b00,5c7458ee,ed667f11,57754132), +S(6c45ff2,c0c30320,83c343e6,dc80b4f7,bb64502a,52f412ff,ada521d4,a544a9f0,e9936037,f1e641eb,1cd6f801,a25271d3,cffe67d,c9bd7162,babf9855,9628a9a8), +S(66df5d95,acdf4260,ee0e465c,e3916d79,395df522,aa086aeb,6c283bb7,18296d46,cb1f3deb,8aea0337,625f46dd,903416d1,5647be91,1633c4c0,cf0e629a,413476fc), +S(428bbba5,ef351762,9eee4059,a3339213,40c7fee2,5ccb051c,88d75e53,ba618858,ef18809c,28227ccc,115876df,b3f378cd,374c0670,2c658501,7990e767,922c3999), +S(e8993820,d782657,ed8badc2,d515e360,df336730,860ef49e,65ca58dd,1a385815,feb41189,16c7e8f6,53c7f5a6,5fd7b6be,7a029814,149c4645,847073be,770959de), +S(2b77e42a,63502244,d6c7d1be,54b7857,9e2742d2,7143d689,fa3f70a3,5986ccf1,721e9da8,bee88ad4,2155dc9a,28be505,86648378,1d6af5b1,382cd47d,771e6428), +S(acffce3a,ed6beea3,2ad71633,21e1ca53,6f69ce76,fb5cd394,9d9dcc14,bbcee96,5384c97d,eac946c0,afc85b54,f3a56b79,fe263bff,63e1894c,6d52ef1b,9b456602), +S(e13d8d4b,25bece8d,90a008e4,c004824e,223b06b,7cf65ebd,1215e910,2e67b639,3e92b1f,14cbee57,9640b377,6ed5938f,9aee0e11,b139a678,3f207cf4,6dd7e20b), +S(d8f356d9,d2498eaa,b3fa12a,cb64562b,dff3e270,c7809a31,25a9ae65,9cb1f3c5,a5403cda,2d499a7c,7402b57c,c9f99e3,64f5085a,91fd8cfd,8a67c7a8,dbce04fb), +S(350062b5,22fb5d0a,8c79a5db,29e92216,79b9fbc6,3e1f95ca,e7d087f3,fedffc49,74cacf57,5e79c85b,d7e77a40,796e8750,a3ca57f2,a47cce8f,3d2de02d,b014df0b), +S(c159e630,9ab23135,58ebfb6b,22edaf93,eac8e16,77cd9f3b,1cd9492,20bf8e54,99244263,53f32608,831c6d74,da51d61,c344defe,f4c1e401,6a3db44b,4c57f4ae), +S(9e568b70,36a2daa2,fe7a3a52,49c56eb8,3fe4afa2,decebd60,c6c3245f,17bc6a13,b6643644,274eab9f,5e83c598,14978681,17c5a078,765156ae,3cb65fd3,830e06ae), +S(9bee7471,6b6c1ff5,62c1bc36,57a65de3,99a91d72,115703f4,4c3819a1,87828478,bb23a3da,ed34028a,7d39a7ed,fe2e23b,73e085a2,d6c2e51a,ef2a2076,a7c902ad), +S(c430c044,91228a7a,ea835698,ecf0f552,9aeef469,a36305cb,52902ccd,1c897473,40d0ee56,61cca82,80d11da,bffe97c4,f10c93aa,5e07224c,154b666e,bcd58c74), +S(2be073f6,b294a01b,eda0b0db,6832e4bc,23ef3889,7a859853,5ee726ef,ae0f15b1,6153f846,349c2a98,26008065,8be2a7d8,311a5e70,cc310ff8,7935a901,8d575813), +S(2934de46,a6d921f8,720567e6,b46e6362,36a7ed53,483b13ed,20958452,3225accd,5979698,26927cd7,abfe4c57,c53fc72a,48b71679,174c749d,aecdb057,e6a2d961), +S(4e9991b,92fa8c4e,4e8efe45,66966073,319e80d3,a54d4b7a,b61cfcc4,7ddaa5d5,9d03ea22,21d4d80e,261952e2,73f6a8cf,c31f6091,e5aa0a8f,2281ffbf,1345df9e), +S(ff3d6136,ffac5b0c,bfc6c5c0,c30dc01a,7ea3d56c,20bd3103,b178e3d3,ae180068,eccdc641,7b1bfff1,bf2fc8d3,2269523e,ab89890d,bffe0a19,8f594490,e7739bb8)} +#else +# error Configuration mismatch, invalid COMB_* parameters. Try deleting precomputed_ecmult_gen.c before the build. +#endif +}; +#undef S diff --git a/src/secp256k1/src/precomputed_ecmult_gen.h b/src/secp256k1/src/precomputed_ecmult_gen.h new file mode 100644 index 000000000..283738a5c --- /dev/null +++ b/src/secp256k1/src/precomputed_ecmult_gen.h @@ -0,0 +1,26 @@ +/********************************************************************************* + * Copyright (c) 2013, 2014, 2015, 2021 Thomas Daede, Cory Fields, Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php. * + *********************************************************************************/ + +#ifndef SECP256K1_PRECOMPUTED_ECMULT_GEN_H +#define SECP256K1_PRECOMPUTED_ECMULT_GEN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "group.h" +#include "ecmult_gen.h" +#ifdef EXHAUSTIVE_TEST_ORDER +static secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS]; +#else +extern const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS]; +#endif /* defined(EXHAUSTIVE_TEST_ORDER) */ + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_PRECOMPUTED_ECMULT_GEN_H */ diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h index a8d059c3e..70f49b1cf 100644 --- a/src/secp256k1/src/scalar.h +++ b/src/secp256k1/src/scalar.h @@ -1,40 +1,44 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_SCALAR_H #define SECP256K1_SCALAR_H -#include "num.h" - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif +#include "util.h" #if defined(EXHAUSTIVE_TEST_ORDER) #include "scalar_low.h" -#elif defined(USE_SCALAR_4X64) +#elif defined(SECP256K1_WIDEMUL_INT128) #include "scalar_4x64.h" -#elif defined(USE_SCALAR_8X32) +#elif defined(SECP256K1_WIDEMUL_INT64) #include "scalar_8x32.h" #else -#error "Please select scalar implementation" +#error "Please select wide multiplication implementation" #endif /** Clear a scalar to prevent the leak of sensitive data. */ static void secp256k1_scalar_clear(secp256k1_scalar *r); -/** Access bits from a scalar. All requested bits must belong to the same 32-bit limb. */ -static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count); +/** Access bits (1 < count <= 32) from a scalar. All requested bits must belong to the same 32-bit limb. */ +static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count); -/** Access bits from a scalar. Not constant time. */ -static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); +/** Access bits (1 < count <= 32) from a scalar. offset + count must be < 256. Not constant time in offset and count. */ +static uint32_t secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); -/** Set a scalar from a big endian byte array. */ +/** Set a scalar from a big endian byte array. The scalar will be reduced modulo group order `n`. + * In: bin: pointer to a 32-byte array. + * Out: r: scalar to be set. + * overflow: non-zero if the scalar was bigger or equal to `n` before reduction, zero otherwise (can be NULL). + */ static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow); +/** Set a scalar from a big endian byte array and returns 1 if it is a valid + * seckey and 0 otherwise. */ +static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin); + /** Set a scalar to an unsigned integer. */ static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); @@ -50,13 +54,6 @@ static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int /** Multiply two scalars (modulo the group order). */ static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); -/** Shift a scalar right by some amount strictly between 0 and 16, returning - * the low bits that were shifted off */ -static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n); - -/** Compute the square of a scalar (modulo the group order). */ -static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a); - /** Compute the inverse of a scalar (modulo the group order). */ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); @@ -66,6 +63,9 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_sc /** Compute the complement of a scalar (modulo the group order). */ static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a); +/** Multiply a scalar with the multiplicative inverse of 2. */ +static void secp256k1_scalar_half(secp256k1_scalar *r, const secp256k1_scalar *a); + /** Check whether a scalar equals zero. */ static int secp256k1_scalar_is_zero(const secp256k1_scalar *a); @@ -82,25 +82,24 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a); * Returns -1 if the number was negated, 1 otherwise */ static int secp256k1_scalar_cond_negate(secp256k1_scalar *a, int flag); -#ifndef USE_NUM_NONE -/** Convert a scalar to a number. */ -static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a); - -/** Get the order of the group as a number. */ -static void secp256k1_scalar_order_get_num(secp256k1_num *r); -#endif - /** Compare two scalars. */ static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b); -#ifdef USE_ENDOMORPHISM -/** Find r1 and r2 such that r1+r2*2^128 = a. */ -static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); -/** Find r1 and r2 such that r1+r2*lambda = a, and r1 and r2 are maximum 128 bits long (see secp256k1_gej_mul_lambda). */ -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); -#endif +/** Find r1 and r2 such that r1+r2*2^128 = k. */ +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k); +/** Find r1 and r2 such that r1+r2*lambda = k, where r1 and r2 or their + * negations are maximum 128 bits long (see secp256k1_ge_mul_lambda). It is + * required that r1, r2, and k all point to different objects. */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT r1, secp256k1_scalar * SECP256K1_RESTRICT r2, const secp256k1_scalar * SECP256K1_RESTRICT k); /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ +static void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag); + +/** Check invariants on a scalar (no-op unless VERIFY is enabled). */ +static void secp256k1_scalar_verify(const secp256k1_scalar *r); +#define SECP256K1_SCALAR_VERIFY(r) secp256k1_scalar_verify(r) + #endif /* SECP256K1_SCALAR_H */ diff --git a/src/secp256k1/src/scalar_4x64.h b/src/secp256k1/src/scalar_4x64.h index e46df1f33..700964291 100644 --- a/src/secp256k1/src/scalar_4x64.h +++ b/src/secp256k1/src/scalar_4x64.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_SCALAR_REPR_H #define SECP256K1_SCALAR_REPR_H diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h index 5c72512e7..4aaec85b9 100644 --- a/src/secp256k1/src/scalar_4x64_impl.h +++ b/src/secp256k1/src/scalar_4x64_impl.h @@ -1,12 +1,17 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "checkmem.h" +#include "int128.h" +#include "modinv64_impl.h" +#include "util.h" + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) #define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) @@ -36,21 +41,28 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig r->d[1] = 0; r->d[2] = 0; r->d[3] = 0; + + SECP256K1_SCALAR_VERIFY(r); } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { +SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + SECP256K1_SCALAR_VERIFY(a); + VERIFY_CHECK(count > 0 && count <= 32); VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); - return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); + + return (a->d[offset >> 6] >> (offset & 0x3F)) & (0xFFFFFFFF >> (32 - count)); } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - VERIFY_CHECK(count < 32); +SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + SECP256K1_SCALAR_VERIFY(a); + VERIFY_CHECK(count > 0 && count <= 32); VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 6 == offset >> 6) { - return secp256k1_scalar_get_bits(a, offset, count); + return secp256k1_scalar_get_bits_limb32(a, offset, count); } else { VERIFY_CHECK((offset >> 6) + 1 < 4); - return ((a->d[offset >> 6] >> (offset & 0x3F)) | (a->d[(offset >> 6) + 1] << (64 - (offset & 0x3F)))) & ((((uint64_t)1) << count) - 1); + return ((a->d[offset >> 6] >> (offset & 0x3F)) | (a->d[(offset >> 6) + 1] << (64 - (offset & 0x3F)))) & (0xFFFFFFFF >> (32 - count)); } } @@ -67,95 +79,177 @@ SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scal } SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, unsigned int overflow) { - uint128_t t; + secp256k1_uint128 t; VERIFY_CHECK(overflow <= 1); - t = (uint128_t)r->d[0] + overflow * SECP256K1_N_C_0; - r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[1] + overflow * SECP256K1_N_C_1; - r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[2] + overflow * SECP256K1_N_C_2; - r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint64_t)r->d[3]; - r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; + + secp256k1_u128_from_u64(&t, r->d[0]); + secp256k1_u128_accum_u64(&t, overflow * SECP256K1_N_C_0); + r->d[0] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[1]); + secp256k1_u128_accum_u64(&t, overflow * SECP256K1_N_C_1); + r->d[1] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[2]); + secp256k1_u128_accum_u64(&t, overflow * SECP256K1_N_C_2); + r->d[2] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[3]); + r->d[3] = secp256k1_u128_to_u64(&t); + + SECP256K1_SCALAR_VERIFY(r); return overflow; } static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { int overflow; - uint128_t t = (uint128_t)a->d[0] + b->d[0]; - r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)a->d[1] + b->d[1]; - r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)a->d[2] + b->d[2]; - r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)a->d[3] + b->d[3]; - r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - overflow = t + secp256k1_scalar_check_overflow(r); + secp256k1_uint128 t; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); + + secp256k1_u128_from_u64(&t, a->d[0]); + secp256k1_u128_accum_u64(&t, b->d[0]); + r->d[0] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, a->d[1]); + secp256k1_u128_accum_u64(&t, b->d[1]); + r->d[1] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, a->d[2]); + secp256k1_u128_accum_u64(&t, b->d[2]); + r->d[2] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, a->d[3]); + secp256k1_u128_accum_u64(&t, b->d[3]); + r->d[3] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + overflow = secp256k1_u128_to_u64(&t) + secp256k1_scalar_check_overflow(r); VERIFY_CHECK(overflow == 0 || overflow == 1); secp256k1_scalar_reduce(r, overflow); + + SECP256K1_SCALAR_VERIFY(r); return overflow; } static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { - uint128_t t; + secp256k1_uint128 t; + volatile int vflag = flag; + SECP256K1_SCALAR_VERIFY(r); VERIFY_CHECK(bit < 256); - bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ - t = (uint128_t)r->d[0] + (((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); - r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[1] + (((uint64_t)((bit >> 6) == 1)) << (bit & 0x3F)); - r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[2] + (((uint64_t)((bit >> 6) == 2)) << (bit & 0x3F)); - r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; - t += (uint128_t)r->d[3] + (((uint64_t)((bit >> 6) == 3)) << (bit & 0x3F)); - r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; -#ifdef VERIFY - VERIFY_CHECK((t >> 64) == 0); - VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -#endif + + bit += ((uint32_t) vflag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ + secp256k1_u128_from_u64(&t, r->d[0]); + secp256k1_u128_accum_u64(&t, ((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); + r->d[0] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[1]); + secp256k1_u128_accum_u64(&t, ((uint64_t)((bit >> 6) == 1)) << (bit & 0x3F)); + r->d[1] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[2]); + secp256k1_u128_accum_u64(&t, ((uint64_t)((bit >> 6) == 2)) << (bit & 0x3F)); + r->d[2] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[3]); + secp256k1_u128_accum_u64(&t, ((uint64_t)((bit >> 6) == 3)) << (bit & 0x3F)); + r->d[3] = secp256k1_u128_to_u64(&t); + + SECP256K1_SCALAR_VERIFY(r); + VERIFY_CHECK(secp256k1_u128_hi_u64(&t) == 0); } static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { int over; - r->d[0] = (uint64_t)b32[31] | (uint64_t)b32[30] << 8 | (uint64_t)b32[29] << 16 | (uint64_t)b32[28] << 24 | (uint64_t)b32[27] << 32 | (uint64_t)b32[26] << 40 | (uint64_t)b32[25] << 48 | (uint64_t)b32[24] << 56; - r->d[1] = (uint64_t)b32[23] | (uint64_t)b32[22] << 8 | (uint64_t)b32[21] << 16 | (uint64_t)b32[20] << 24 | (uint64_t)b32[19] << 32 | (uint64_t)b32[18] << 40 | (uint64_t)b32[17] << 48 | (uint64_t)b32[16] << 56; - r->d[2] = (uint64_t)b32[15] | (uint64_t)b32[14] << 8 | (uint64_t)b32[13] << 16 | (uint64_t)b32[12] << 24 | (uint64_t)b32[11] << 32 | (uint64_t)b32[10] << 40 | (uint64_t)b32[9] << 48 | (uint64_t)b32[8] << 56; - r->d[3] = (uint64_t)b32[7] | (uint64_t)b32[6] << 8 | (uint64_t)b32[5] << 16 | (uint64_t)b32[4] << 24 | (uint64_t)b32[3] << 32 | (uint64_t)b32[2] << 40 | (uint64_t)b32[1] << 48 | (uint64_t)b32[0] << 56; + r->d[0] = secp256k1_read_be64(&b32[24]); + r->d[1] = secp256k1_read_be64(&b32[16]); + r->d[2] = secp256k1_read_be64(&b32[8]); + r->d[3] = secp256k1_read_be64(&b32[0]); over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); if (overflow) { *overflow = over; } + + SECP256K1_SCALAR_VERIFY(r); } static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { - bin[0] = a->d[3] >> 56; bin[1] = a->d[3] >> 48; bin[2] = a->d[3] >> 40; bin[3] = a->d[3] >> 32; bin[4] = a->d[3] >> 24; bin[5] = a->d[3] >> 16; bin[6] = a->d[3] >> 8; bin[7] = a->d[3]; - bin[8] = a->d[2] >> 56; bin[9] = a->d[2] >> 48; bin[10] = a->d[2] >> 40; bin[11] = a->d[2] >> 32; bin[12] = a->d[2] >> 24; bin[13] = a->d[2] >> 16; bin[14] = a->d[2] >> 8; bin[15] = a->d[2]; - bin[16] = a->d[1] >> 56; bin[17] = a->d[1] >> 48; bin[18] = a->d[1] >> 40; bin[19] = a->d[1] >> 32; bin[20] = a->d[1] >> 24; bin[21] = a->d[1] >> 16; bin[22] = a->d[1] >> 8; bin[23] = a->d[1]; - bin[24] = a->d[0] >> 56; bin[25] = a->d[0] >> 48; bin[26] = a->d[0] >> 40; bin[27] = a->d[0] >> 32; bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; + SECP256K1_SCALAR_VERIFY(a); + + secp256k1_write_be64(&bin[0], a->d[3]); + secp256k1_write_be64(&bin[8], a->d[2]); + secp256k1_write_be64(&bin[16], a->d[1]); + secp256k1_write_be64(&bin[24], a->d[0]); } SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + return (a->d[0] | a->d[1] | a->d[2] | a->d[3]) == 0; } static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { uint64_t nonzero = 0xFFFFFFFFFFFFFFFFULL * (secp256k1_scalar_is_zero(a) == 0); - uint128_t t = (uint128_t)(~a->d[0]) + SECP256K1_N_0 + 1; - r->d[0] = t & nonzero; t >>= 64; - t += (uint128_t)(~a->d[1]) + SECP256K1_N_1; - r->d[1] = t & nonzero; t >>= 64; - t += (uint128_t)(~a->d[2]) + SECP256K1_N_2; - r->d[2] = t & nonzero; t >>= 64; - t += (uint128_t)(~a->d[3]) + SECP256K1_N_3; - r->d[3] = t & nonzero; + secp256k1_uint128 t; + SECP256K1_SCALAR_VERIFY(a); + + secp256k1_u128_from_u64(&t, ~a->d[0]); + secp256k1_u128_accum_u64(&t, SECP256K1_N_0 + 1); + r->d[0] = secp256k1_u128_to_u64(&t) & nonzero; secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, ~a->d[1]); + secp256k1_u128_accum_u64(&t, SECP256K1_N_1); + r->d[1] = secp256k1_u128_to_u64(&t) & nonzero; secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, ~a->d[2]); + secp256k1_u128_accum_u64(&t, SECP256K1_N_2); + r->d[2] = secp256k1_u128_to_u64(&t) & nonzero; secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, ~a->d[3]); + secp256k1_u128_accum_u64(&t, SECP256K1_N_3); + r->d[3] = secp256k1_u128_to_u64(&t) & nonzero; + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_half(secp256k1_scalar *r, const secp256k1_scalar *a) { + /* Writing `/` for field division and `//` for integer division, we compute + * + * a/2 = (a - (a&1))/2 + (a&1)/2 + * = (a >> 1) + (a&1 ? 1/2 : 0) + * = (a >> 1) + (a&1 ? n//2+1 : 0), + * + * where n is the group order and in the last equality we have used 1/2 = n//2+1 (mod n). + * For n//2, we have the constants SECP256K1_N_H_0, ... + * + * This sum does not overflow. The most extreme case is a = -2, the largest odd scalar. Here: + * - the left summand is: a >> 1 = (a - a&1)/2 = (n-2-1)//2 = (n-3)//2 + * - the right summand is: a&1 ? n//2+1 : 0 = n//2+1 = (n-1)//2 + 2//2 = (n+1)//2 + * Together they sum to (n-3)//2 + (n+1)//2 = (2n-2)//2 = n - 1, which is less than n. + */ + uint64_t mask = -(uint64_t)(a->d[0] & 1U); + secp256k1_uint128 t; + SECP256K1_SCALAR_VERIFY(a); + + secp256k1_u128_from_u64(&t, (a->d[0] >> 1) | (a->d[1] << 63)); + secp256k1_u128_accum_u64(&t, (SECP256K1_N_H_0 + 1U) & mask); + r->d[0] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, (a->d[1] >> 1) | (a->d[2] << 63)); + secp256k1_u128_accum_u64(&t, SECP256K1_N_H_1 & mask); + r->d[1] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, (a->d[2] >> 1) | (a->d[3] << 63)); + secp256k1_u128_accum_u64(&t, SECP256K1_N_H_2 & mask); + r->d[2] = secp256k1_u128_to_u64(&t); secp256k1_u128_rshift(&t, 64); + r->d[3] = secp256k1_u128_to_u64(&t) + (a->d[3] >> 1) + (SECP256K1_N_H_3 & mask); +#ifdef VERIFY + /* The line above only computed the bottom 64 bits of r->d[3]; redo the computation + * in full 128 bits to make sure the top 64 bits are indeed zero. */ + secp256k1_u128_accum_u64(&t, a->d[3] >> 1); + secp256k1_u128_accum_u64(&t, SECP256K1_N_H_3 & mask); + secp256k1_u128_rshift(&t, 64); + VERIFY_CHECK(secp256k1_u128_to_u64(&t) == 0); + + SECP256K1_SCALAR_VERIFY(r); +#endif } SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3]) == 0; } static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { int yes = 0; int no = 0; + SECP256K1_SCALAR_VERIFY(a); + no |= (a->d[3] < SECP256K1_N_H_3); yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; /* No need for a > check. */ @@ -168,16 +262,26 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { /* If we are flag = 0, mask = 00...00 and this is a no-op; * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ - uint64_t mask = !flag - 1; + volatile int vflag = flag; + uint64_t mask = -vflag; uint64_t nonzero = (secp256k1_scalar_is_zero(r) != 0) - 1; - uint128_t t = (uint128_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); - r->d[0] = t & nonzero; t >>= 64; - t += (uint128_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); - r->d[1] = t & nonzero; t >>= 64; - t += (uint128_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); - r->d[2] = t & nonzero; t >>= 64; - t += (uint128_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); - r->d[3] = t & nonzero; + secp256k1_uint128 t; + SECP256K1_SCALAR_VERIFY(r); + + secp256k1_u128_from_u64(&t, r->d[0] ^ mask); + secp256k1_u128_accum_u64(&t, (SECP256K1_N_0 + 1) & mask); + r->d[0] = secp256k1_u128_to_u64(&t) & nonzero; secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[1] ^ mask); + secp256k1_u128_accum_u64(&t, SECP256K1_N_1 & mask); + r->d[1] = secp256k1_u128_to_u64(&t) & nonzero; secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[2] ^ mask); + secp256k1_u128_accum_u64(&t, SECP256K1_N_2 & mask); + r->d[2] = secp256k1_u128_to_u64(&t) & nonzero; secp256k1_u128_rshift(&t, 64); + secp256k1_u128_accum_u64(&t, r->d[3] ^ mask); + secp256k1_u128_accum_u64(&t, SECP256K1_N_3 & mask); + r->d[3] = secp256k1_u128_to_u64(&t) & nonzero; + + SECP256K1_SCALAR_VERIFY(r); return 2 * (mask == 0) - 1; } @@ -187,14 +291,15 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { #define muladd(a,b) { \ uint64_t tl, th; \ { \ - uint128_t t = (uint128_t)a * b; \ - th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ - tl = t; \ + secp256k1_uint128 t; \ + secp256k1_u128_mul(&t, a, b); \ + th = secp256k1_u128_hi_u64(&t); /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = secp256k1_u128_to_u64(&t); \ } \ c0 += tl; /* overflow is handled on the next line */ \ - th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \ c1 += th; /* overflow is handled on the next line */ \ - c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ + c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \ VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ } @@ -202,51 +307,30 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { #define muladd_fast(a,b) { \ uint64_t tl, th; \ { \ - uint128_t t = (uint128_t)a * b; \ - th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ - tl = t; \ + secp256k1_uint128 t; \ + secp256k1_u128_mul(&t, a, b); \ + th = secp256k1_u128_hi_u64(&t); /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = secp256k1_u128_to_u64(&t); \ } \ c0 += tl; /* overflow is handled on the next line */ \ - th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \ c1 += th; /* never overflows by contract (verified in the next line) */ \ VERIFY_CHECK(c1 >= th); \ } -/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ -#define muladd2(a,b) { \ - uint64_t tl, th, th2, tl2; \ - { \ - uint128_t t = (uint128_t)a * b; \ - th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ - tl = t; \ - } \ - th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ - c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ - VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ - tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \ - th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ - c0 += tl2; /* overflow is handled on the next line */ \ - th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ - c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ - VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ - c1 += th2; /* overflow is handled on the next line */ \ - c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ - VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ -} - /** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ #define sumadd(a) { \ unsigned int over; \ c0 += (a); /* overflow is handled on the next line */ \ - over = (c0 < (a)) ? 1 : 0; \ + over = (c0 < (a)); \ c1 += over; /* overflow is handled on the next line */ \ - c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ + c2 += (c1 < over); /* never overflows by contract */ \ } /** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ #define sumadd_fast(a) { \ c0 += (a); /* overflow is handled on the next line */ \ - c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \ VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ VERIFY_CHECK(c2 == 0); \ } @@ -375,10 +459,18 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) "movq %%r10, %q5\n" /* extract m6 */ "movq %%r8, %q6\n" - : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) - : "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "=&g"(m0), "=&g"(m1), "=&g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) + : "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1) : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); + SECP256K1_CHECKMEM_MSAN_DEFINE(&m0, sizeof(m0)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&m1, sizeof(m1)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&m2, sizeof(m2)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&m3, sizeof(m3)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&m4, sizeof(m4)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&m5, sizeof(m5)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&m6, sizeof(m6)); + /* Reduce 385 bits into 258. */ __asm__ __volatile__( /* Preload */ @@ -455,9 +547,15 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) /* extract p4 */ "movq %%r9, %q4\n" : "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4) - : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1) : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc"); + SECP256K1_CHECKMEM_MSAN_DEFINE(&p0, sizeof(p0)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&p1, sizeof(p1)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&p2, sizeof(p2)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&p3, sizeof(p3)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&p4, sizeof(p4)); + /* Reduce 258 bits into 256. */ __asm__ __volatile__( /* Preload */ @@ -501,11 +599,15 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) /* Extract c */ "movq %%r9, %q0\n" : "=g"(c) - : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1) : "rax", "rdx", "r8", "r9", "r10", "cc", "memory"); + + SECP256K1_CHECKMEM_MSAN_DEFINE(r, sizeof(*r)); + SECP256K1_CHECKMEM_MSAN_DEFINE(&c, sizeof(c)); + #else - uint128_t c; - uint64_t c0, c1, c2; + secp256k1_uint128 c128; + uint64_t c, c0, c1, c2; uint64_t n0 = l[4], n1 = l[5], n2 = l[6], n3 = l[7]; uint64_t m0, m1, m2, m3, m4, m5; uint32_t m6; @@ -562,21 +664,25 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) /* Reduce 258 bits into 256. */ /* r[0..3] = p[0..3] + p[4] * SECP256K1_N_C. */ - c = p0 + (uint128_t)SECP256K1_N_C_0 * p4; - r->d[0] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; - c += p1 + (uint128_t)SECP256K1_N_C_1 * p4; - r->d[1] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; - c += p2 + (uint128_t)p4; - r->d[2] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; - c += p3; - r->d[3] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + secp256k1_u128_from_u64(&c128, p0); + secp256k1_u128_accum_mul(&c128, SECP256K1_N_C_0, p4); + r->d[0] = secp256k1_u128_to_u64(&c128); secp256k1_u128_rshift(&c128, 64); + secp256k1_u128_accum_u64(&c128, p1); + secp256k1_u128_accum_mul(&c128, SECP256K1_N_C_1, p4); + r->d[1] = secp256k1_u128_to_u64(&c128); secp256k1_u128_rshift(&c128, 64); + secp256k1_u128_accum_u64(&c128, p2); + secp256k1_u128_accum_u64(&c128, p4); + r->d[2] = secp256k1_u128_to_u64(&c128); secp256k1_u128_rshift(&c128, 64); + secp256k1_u128_accum_u64(&c128, p3); + r->d[3] = secp256k1_u128_to_u64(&c128); + c = secp256k1_u128_hi_u64(&c128); #endif /* Final reduction of r. */ secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); } -static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, const secp256k1_scalar *b) { +static void secp256k1_scalar_mul_512(uint64_t *l8, const secp256k1_scalar *a, const secp256k1_scalar *b) { #ifdef USE_ASM_X86_64 const uint64_t *pb = b->d; __asm__ __volatile__( @@ -591,7 +697,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c /* (rax,rdx) = a0 * b0 */ "movq %%r15, %%rax\n" "mulq %%r11\n" - /* Extract l0 */ + /* Extract l8[0] */ "movq %%rax, 0(%%rsi)\n" /* (r8,r9,r10) = (rdx) */ "movq %%rdx, %%r8\n" @@ -609,7 +715,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c "addq %%rax, %%r8\n" "adcq %%rdx, %%r9\n" "adcq $0, %%r10\n" - /* Extract l1 */ + /* Extract l8[1] */ "movq %%r8, 8(%%rsi)\n" "xorq %%r8, %%r8\n" /* (r9,r10,r8) += a0 * b2 */ @@ -630,7 +736,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c "addq %%rax, %%r9\n" "adcq %%rdx, %%r10\n" "adcq $0, %%r8\n" - /* Extract l2 */ + /* Extract l8[2] */ "movq %%r9, 16(%%rsi)\n" "xorq %%r9, %%r9\n" /* (r10,r8,r9) += a0 * b3 */ @@ -659,7 +765,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c "addq %%rax, %%r10\n" "adcq %%rdx, %%r8\n" "adcq $0, %%r9\n" - /* Extract l3 */ + /* Extract l8[3] */ "movq %%r10, 24(%%rsi)\n" "xorq %%r10, %%r10\n" /* (r8,r9,r10) += a1 * b3 */ @@ -680,7 +786,7 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c "addq %%rax, %%r8\n" "adcq %%rdx, %%r9\n" "adcq $0, %%r10\n" - /* Extract l4 */ + /* Extract l8[4] */ "movq %%r8, 32(%%rsi)\n" "xorq %%r8, %%r8\n" /* (r9,r10,r8) += a2 * b3 */ @@ -695,188 +801,54 @@ static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, c "addq %%rax, %%r9\n" "adcq %%rdx, %%r10\n" "adcq $0, %%r8\n" - /* Extract l5 */ + /* Extract l8[5] */ "movq %%r9, 40(%%rsi)\n" /* (r10,r8) += a3 * b3 */ "movq %%r15, %%rax\n" "mulq %%r14\n" "addq %%rax, %%r10\n" "adcq %%rdx, %%r8\n" - /* Extract l6 */ + /* Extract l8[6] */ "movq %%r10, 48(%%rsi)\n" - /* Extract l7 */ + /* Extract l8[7] */ "movq %%r8, 56(%%rsi)\n" : "+d"(pb) - : "S"(l), "D"(a->d) + : "S"(l8), "D"(a->d) : "rax", "rbx", "rcx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "cc", "memory"); + + SECP256K1_CHECKMEM_MSAN_DEFINE(l8, sizeof(*l8) * 8); + #else /* 160 bit accumulator. */ uint64_t c0 = 0, c1 = 0; uint32_t c2 = 0; - /* l[0..7] = a[0..3] * b[0..3]. */ + /* l8[0..7] = a[0..3] * b[0..3]. */ muladd_fast(a->d[0], b->d[0]); - extract_fast(l[0]); + extract_fast(l8[0]); muladd(a->d[0], b->d[1]); muladd(a->d[1], b->d[0]); - extract(l[1]); + extract(l8[1]); muladd(a->d[0], b->d[2]); muladd(a->d[1], b->d[1]); muladd(a->d[2], b->d[0]); - extract(l[2]); + extract(l8[2]); muladd(a->d[0], b->d[3]); muladd(a->d[1], b->d[2]); muladd(a->d[2], b->d[1]); muladd(a->d[3], b->d[0]); - extract(l[3]); + extract(l8[3]); muladd(a->d[1], b->d[3]); muladd(a->d[2], b->d[2]); muladd(a->d[3], b->d[1]); - extract(l[4]); + extract(l8[4]); muladd(a->d[2], b->d[3]); muladd(a->d[3], b->d[2]); - extract(l[5]); + extract(l8[5]); muladd_fast(a->d[3], b->d[3]); - extract_fast(l[6]); + extract_fast(l8[6]); VERIFY_CHECK(c1 == 0); - l[7] = c0; -#endif -} - -static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { -#ifdef USE_ASM_X86_64 - __asm__ __volatile__( - /* Preload */ - "movq 0(%%rdi), %%r11\n" - "movq 8(%%rdi), %%r12\n" - "movq 16(%%rdi), %%r13\n" - "movq 24(%%rdi), %%r14\n" - /* (rax,rdx) = a0 * a0 */ - "movq %%r11, %%rax\n" - "mulq %%r11\n" - /* Extract l0 */ - "movq %%rax, 0(%%rsi)\n" - /* (r8,r9,r10) = (rdx,0) */ - "movq %%rdx, %%r8\n" - "xorq %%r9, %%r9\n" - "xorq %%r10, %%r10\n" - /* (r8,r9,r10) += 2 * a0 * a1 */ - "movq %%r11, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* Extract l1 */ - "movq %%r8, 8(%%rsi)\n" - "xorq %%r8, %%r8\n" - /* (r9,r10,r8) += 2 * a0 * a2 */ - "movq %%r11, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* (r9,r10,r8) += a1 * a1 */ - "movq %%r12, %%rax\n" - "mulq %%r12\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* Extract l2 */ - "movq %%r9, 16(%%rsi)\n" - "xorq %%r9, %%r9\n" - /* (r10,r8,r9) += 2 * a0 * a3 */ - "movq %%r11, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* (r10,r8,r9) += 2 * a1 * a2 */ - "movq %%r12, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - "adcq $0, %%r9\n" - /* Extract l3 */ - "movq %%r10, 24(%%rsi)\n" - "xorq %%r10, %%r10\n" - /* (r8,r9,r10) += 2 * a1 * a3 */ - "movq %%r12, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* (r8,r9,r10) += a2 * a2 */ - "movq %%r13, %%rax\n" - "mulq %%r13\n" - "addq %%rax, %%r8\n" - "adcq %%rdx, %%r9\n" - "adcq $0, %%r10\n" - /* Extract l4 */ - "movq %%r8, 32(%%rsi)\n" - "xorq %%r8, %%r8\n" - /* (r9,r10,r8) += 2 * a2 * a3 */ - "movq %%r13, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - "addq %%rax, %%r9\n" - "adcq %%rdx, %%r10\n" - "adcq $0, %%r8\n" - /* Extract l5 */ - "movq %%r9, 40(%%rsi)\n" - /* (r10,r8) += a3 * a3 */ - "movq %%r14, %%rax\n" - "mulq %%r14\n" - "addq %%rax, %%r10\n" - "adcq %%rdx, %%r8\n" - /* Extract l6 */ - "movq %%r10, 48(%%rsi)\n" - /* Extract l7 */ - "movq %%r8, 56(%%rsi)\n" - : - : "S"(l), "D"(a->d) - : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); -#else - /* 160 bit accumulator. */ - uint64_t c0 = 0, c1 = 0; - uint32_t c2 = 0; - - /* l[0..7] = a[0..3] * b[0..3]. */ - muladd_fast(a->d[0], a->d[0]); - extract_fast(l[0]); - muladd2(a->d[0], a->d[1]); - extract(l[1]); - muladd2(a->d[0], a->d[2]); - muladd(a->d[1], a->d[1]); - extract(l[2]); - muladd2(a->d[0], a->d[3]); - muladd2(a->d[1], a->d[2]); - extract(l[3]); - muladd2(a->d[1], a->d[3]); - muladd(a->d[2], a->d[2]); - extract(l[4]); - muladd2(a->d[2], a->d[3]); - extract(l[5]); - muladd_fast(a->d[3], a->d[3]); - extract_fast(l[6]); - VERIFY_CHECK(c1 == 0); - l[7] = c0; + l8[7] = c0; #endif } @@ -884,48 +856,40 @@ static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { #undef sumadd_fast #undef muladd #undef muladd_fast -#undef muladd2 #undef extract #undef extract_fast static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { uint64_t l[8]; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); + secp256k1_scalar_mul_512(l, a, b); secp256k1_scalar_reduce_512(r, l); -} -static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { - int ret; - VERIFY_CHECK(n > 0); - VERIFY_CHECK(n < 16); - ret = r->d[0] & ((1 << n) - 1); - r->d[0] = (r->d[0] >> n) + (r->d[1] << (64 - n)); - r->d[1] = (r->d[1] >> n) + (r->d[2] << (64 - n)); - r->d[2] = (r->d[2] >> n) + (r->d[3] << (64 - n)); - r->d[3] = (r->d[3] >> n); - return ret; + SECP256K1_SCALAR_VERIFY(r); } -static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { - uint64_t l[8]; - secp256k1_scalar_sqr_512(l, a); - secp256k1_scalar_reduce_512(r, l); -} +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { + SECP256K1_SCALAR_VERIFY(k); -#ifdef USE_ENDOMORPHISM -static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { - r1->d[0] = a->d[0]; - r1->d[1] = a->d[1]; + r1->d[0] = k->d[0]; + r1->d[1] = k->d[1]; r1->d[2] = 0; r1->d[3] = 0; - r2->d[0] = a->d[2]; - r2->d[1] = a->d[3]; + r2->d[0] = k->d[2]; + r2->d[1] = k->d[3]; r2->d[2] = 0; r2->d[3] = 0; + + SECP256K1_SCALAR_VERIFY(r1); + SECP256K1_SCALAR_VERIFY(r2); } -#endif SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0; } @@ -934,7 +898,10 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, unsigned int shiftlimbs; unsigned int shiftlow; unsigned int shifthigh; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); shiftlimbs = shift >> 6; shiftlow = shift & 0x3F; @@ -944,6 +911,97 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, r->d[2] = shift < 384 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; r->d[3] = shift < 320 ? (l[3 + shiftlimbs] >> shiftlow) : 0; secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); + + SECP256K1_SCALAR_VERIFY(r); +} + +static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { + uint64_t mask0, mask1; + volatile int vflag = flag; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_CHECKMEM_CHECK_VERIFY(r->d, sizeof(r->d)); + + mask0 = vflag + ~((uint64_t)0); + mask1 = ~mask0; + r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1); + r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1); + r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1); + r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_from_signed62(secp256k1_scalar *r, const secp256k1_modinv64_signed62 *a) { + const uint64_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4]; + + /* The output from secp256k1_modinv64{_var} should be normalized to range [0,modulus), and + * have limbs in [0,2^62). The modulus is < 2^256, so the top limb must be below 2^(256-62*4). + */ + VERIFY_CHECK(a0 >> 62 == 0); + VERIFY_CHECK(a1 >> 62 == 0); + VERIFY_CHECK(a2 >> 62 == 0); + VERIFY_CHECK(a3 >> 62 == 0); + VERIFY_CHECK(a4 >> 8 == 0); + + r->d[0] = a0 | a1 << 62; + r->d[1] = a1 >> 2 | a2 << 60; + r->d[2] = a2 >> 4 | a3 << 58; + r->d[3] = a3 >> 6 | a4 << 56; + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_to_signed62(secp256k1_modinv64_signed62 *r, const secp256k1_scalar *a) { + const uint64_t M62 = UINT64_MAX >> 2; + const uint64_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3]; + SECP256K1_SCALAR_VERIFY(a); + + r->v[0] = a0 & M62; + r->v[1] = (a0 >> 62 | a1 << 2) & M62; + r->v[2] = (a1 >> 60 | a2 << 4) & M62; + r->v[3] = (a2 >> 58 | a3 << 6) & M62; + r->v[4] = a3 >> 56; +} + +static const secp256k1_modinv64_modinfo secp256k1_const_modinfo_scalar = { + {{0x3FD25E8CD0364141LL, 0x2ABB739ABD2280EELL, -0x15LL, 0, 256}}, + 0x34F20099AA774EC1LL +}; + +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { + secp256k1_modinv64_signed62 s; +#ifdef VERIFY + int zero_in = secp256k1_scalar_is_zero(x); +#endif + SECP256K1_SCALAR_VERIFY(x); + + secp256k1_scalar_to_signed62(&s, x); + secp256k1_modinv64(&s, &secp256k1_const_modinfo_scalar); + secp256k1_scalar_from_signed62(r, &s); + + SECP256K1_SCALAR_VERIFY(r); + VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in); +} + +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { + secp256k1_modinv64_signed62 s; +#ifdef VERIFY + int zero_in = secp256k1_scalar_is_zero(x); +#endif + SECP256K1_SCALAR_VERIFY(x); + + secp256k1_scalar_to_signed62(&s, x); + secp256k1_modinv64_var(&s, &secp256k1_const_modinfo_scalar); + secp256k1_scalar_from_signed62(r, &s); + + SECP256K1_SCALAR_VERIFY(r); + VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in); +} + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + + return !(a->d[0] & 1); } #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_8x32.h b/src/secp256k1/src/scalar_8x32.h index 9c1d8b25a..17863ef93 100644 --- a/src/secp256k1/src/scalar_8x32.h +++ b/src/secp256k1/src/scalar_8x32.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_SCALAR_REPR_H #define SECP256K1_SCALAR_REPR_H diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h index b19b058f9..c7d87b17d 100644 --- a/src/secp256k1/src/scalar_8x32_impl.h +++ b/src/secp256k1/src/scalar_8x32_impl.h @@ -1,12 +1,16 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "checkmem.h" +#include "modinv32_impl.h" +#include "util.h" + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) #define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) @@ -54,21 +58,28 @@ SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsig r->d[5] = 0; r->d[6] = 0; r->d[7] = 0; + + SECP256K1_SCALAR_VERIFY(r); } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { +SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + SECP256K1_SCALAR_VERIFY(a); + VERIFY_CHECK(count > 0 && count <= 32); VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); - return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); + + return (a->d[offset >> 5] >> (offset & 0x1F)) & (0xFFFFFFFF >> (32 - count)); } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - VERIFY_CHECK(count < 32); +SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + SECP256K1_SCALAR_VERIFY(a); + VERIFY_CHECK(count > 0 && count <= 32); VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 5 == offset >> 5) { - return secp256k1_scalar_get_bits(a, offset, count); + return secp256k1_scalar_get_bits_limb32(a, offset, count); } else { VERIFY_CHECK((offset >> 5) + 1 < 8); - return ((a->d[offset >> 5] >> (offset & 0x1F)) | (a->d[(offset >> 5) + 1] << (32 - (offset & 0x1F)))) & ((((uint32_t)1) << count) - 1); + return ((a->d[offset >> 5] >> (offset & 0x1F)) | (a->d[(offset >> 5) + 1] << (32 - (offset & 0x1F)))) & (0xFFFFFFFF >> (32 - count)); } } @@ -93,6 +104,7 @@ SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scal SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, uint32_t overflow) { uint64_t t; VERIFY_CHECK(overflow <= 1); + t = (uint64_t)r->d[0] + overflow * SECP256K1_N_C_0; r->d[0] = t & 0xFFFFFFFFUL; t >>= 32; t += (uint64_t)r->d[1] + overflow * SECP256K1_N_C_1; @@ -109,12 +121,17 @@ SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, uint32_ r->d[6] = t & 0xFFFFFFFFUL; t >>= 32; t += (uint64_t)r->d[7]; r->d[7] = t & 0xFFFFFFFFUL; + + SECP256K1_SCALAR_VERIFY(r); return overflow; } static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { int overflow; uint64_t t = (uint64_t)a->d[0] + b->d[0]; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; t += (uint64_t)a->d[1] + b->d[1]; r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; @@ -133,13 +150,18 @@ static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, overflow = t + secp256k1_scalar_check_overflow(r); VERIFY_CHECK(overflow == 0 || overflow == 1); secp256k1_scalar_reduce(r, overflow); + + SECP256K1_SCALAR_VERIFY(r); return overflow; } static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { uint64_t t; + volatile int vflag = flag; + SECP256K1_SCALAR_VERIFY(r); VERIFY_CHECK(bit < 256); - bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ + + bit += ((uint32_t) vflag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ t = (uint64_t)r->d[0] + (((uint32_t)((bit >> 5) == 0)) << (bit & 0x1F)); r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; t += (uint64_t)r->d[1] + (((uint32_t)((bit >> 5) == 1)) << (bit & 0x1F)); @@ -156,46 +178,53 @@ static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; t += (uint64_t)r->d[7] + (((uint32_t)((bit >> 5) == 7)) << (bit & 0x1F)); r->d[7] = t & 0xFFFFFFFFULL; -#ifdef VERIFY + + SECP256K1_SCALAR_VERIFY(r); VERIFY_CHECK((t >> 32) == 0); - VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -#endif } static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { int over; - r->d[0] = (uint32_t)b32[31] | (uint32_t)b32[30] << 8 | (uint32_t)b32[29] << 16 | (uint32_t)b32[28] << 24; - r->d[1] = (uint32_t)b32[27] | (uint32_t)b32[26] << 8 | (uint32_t)b32[25] << 16 | (uint32_t)b32[24] << 24; - r->d[2] = (uint32_t)b32[23] | (uint32_t)b32[22] << 8 | (uint32_t)b32[21] << 16 | (uint32_t)b32[20] << 24; - r->d[3] = (uint32_t)b32[19] | (uint32_t)b32[18] << 8 | (uint32_t)b32[17] << 16 | (uint32_t)b32[16] << 24; - r->d[4] = (uint32_t)b32[15] | (uint32_t)b32[14] << 8 | (uint32_t)b32[13] << 16 | (uint32_t)b32[12] << 24; - r->d[5] = (uint32_t)b32[11] | (uint32_t)b32[10] << 8 | (uint32_t)b32[9] << 16 | (uint32_t)b32[8] << 24; - r->d[6] = (uint32_t)b32[7] | (uint32_t)b32[6] << 8 | (uint32_t)b32[5] << 16 | (uint32_t)b32[4] << 24; - r->d[7] = (uint32_t)b32[3] | (uint32_t)b32[2] << 8 | (uint32_t)b32[1] << 16 | (uint32_t)b32[0] << 24; + r->d[0] = secp256k1_read_be32(&b32[28]); + r->d[1] = secp256k1_read_be32(&b32[24]); + r->d[2] = secp256k1_read_be32(&b32[20]); + r->d[3] = secp256k1_read_be32(&b32[16]); + r->d[4] = secp256k1_read_be32(&b32[12]); + r->d[5] = secp256k1_read_be32(&b32[8]); + r->d[6] = secp256k1_read_be32(&b32[4]); + r->d[7] = secp256k1_read_be32(&b32[0]); over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); if (overflow) { *overflow = over; } + + SECP256K1_SCALAR_VERIFY(r); } static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { - bin[0] = a->d[7] >> 24; bin[1] = a->d[7] >> 16; bin[2] = a->d[7] >> 8; bin[3] = a->d[7]; - bin[4] = a->d[6] >> 24; bin[5] = a->d[6] >> 16; bin[6] = a->d[6] >> 8; bin[7] = a->d[6]; - bin[8] = a->d[5] >> 24; bin[9] = a->d[5] >> 16; bin[10] = a->d[5] >> 8; bin[11] = a->d[5]; - bin[12] = a->d[4] >> 24; bin[13] = a->d[4] >> 16; bin[14] = a->d[4] >> 8; bin[15] = a->d[4]; - bin[16] = a->d[3] >> 24; bin[17] = a->d[3] >> 16; bin[18] = a->d[3] >> 8; bin[19] = a->d[3]; - bin[20] = a->d[2] >> 24; bin[21] = a->d[2] >> 16; bin[22] = a->d[2] >> 8; bin[23] = a->d[2]; - bin[24] = a->d[1] >> 24; bin[25] = a->d[1] >> 16; bin[26] = a->d[1] >> 8; bin[27] = a->d[1]; - bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; + SECP256K1_SCALAR_VERIFY(a); + + secp256k1_write_be32(&bin[0], a->d[7]); + secp256k1_write_be32(&bin[4], a->d[6]); + secp256k1_write_be32(&bin[8], a->d[5]); + secp256k1_write_be32(&bin[12], a->d[4]); + secp256k1_write_be32(&bin[16], a->d[3]); + secp256k1_write_be32(&bin[20], a->d[2]); + secp256k1_write_be32(&bin[24], a->d[1]); + secp256k1_write_be32(&bin[28], a->d[0]); } SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + return (a->d[0] | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; } static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(a) == 0); uint64_t t = (uint64_t)(~a->d[0]) + SECP256K1_N_0 + 1; + SECP256K1_SCALAR_VERIFY(a); + r->d[0] = t & nonzero; t >>= 32; t += (uint64_t)(~a->d[1]) + SECP256K1_N_1; r->d[1] = t & nonzero; t >>= 32; @@ -211,15 +240,69 @@ static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar r->d[6] = t & nonzero; t >>= 32; t += (uint64_t)(~a->d[7]) + SECP256K1_N_7; r->d[7] = t & nonzero; + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_half(secp256k1_scalar *r, const secp256k1_scalar *a) { + /* Writing `/` for field division and `//` for integer division, we compute + * + * a/2 = (a - (a&1))/2 + (a&1)/2 + * = (a >> 1) + (a&1 ? 1/2 : 0) + * = (a >> 1) + (a&1 ? n//2+1 : 0), + * + * where n is the group order and in the last equality we have used 1/2 = n//2+1 (mod n). + * For n//2, we have the constants SECP256K1_N_H_0, ... + * + * This sum does not overflow. The most extreme case is a = -2, the largest odd scalar. Here: + * - the left summand is: a >> 1 = (a - a&1)/2 = (n-2-1)//2 = (n-3)//2 + * - the right summand is: a&1 ? n//2+1 : 0 = n//2+1 = (n-1)//2 + 2//2 = (n+1)//2 + * Together they sum to (n-3)//2 + (n+1)//2 = (2n-2)//2 = n - 1, which is less than n. + */ + uint32_t mask = -(uint32_t)(a->d[0] & 1U); + uint64_t t = (uint32_t)((a->d[0] >> 1) | (a->d[1] << 31)); + SECP256K1_SCALAR_VERIFY(a); + + t += (SECP256K1_N_H_0 + 1U) & mask; + r->d[0] = t; t >>= 32; + t += (uint32_t)((a->d[1] >> 1) | (a->d[2] << 31)); + t += SECP256K1_N_H_1 & mask; + r->d[1] = t; t >>= 32; + t += (uint32_t)((a->d[2] >> 1) | (a->d[3] << 31)); + t += SECP256K1_N_H_2 & mask; + r->d[2] = t; t >>= 32; + t += (uint32_t)((a->d[3] >> 1) | (a->d[4] << 31)); + t += SECP256K1_N_H_3 & mask; + r->d[3] = t; t >>= 32; + t += (uint32_t)((a->d[4] >> 1) | (a->d[5] << 31)); + t += SECP256K1_N_H_4 & mask; + r->d[4] = t; t >>= 32; + t += (uint32_t)((a->d[5] >> 1) | (a->d[6] << 31)); + t += SECP256K1_N_H_5 & mask; + r->d[5] = t; t >>= 32; + t += (uint32_t)((a->d[6] >> 1) | (a->d[7] << 31)); + t += SECP256K1_N_H_6 & mask; + r->d[6] = t; t >>= 32; + r->d[7] = (uint32_t)t + (uint32_t)(a->d[7] >> 1) + (SECP256K1_N_H_7 & mask); + + /* The line above only computed the bottom 32 bits of r->d[7]. Redo the computation + * in full 64 bits to make sure the top 32 bits are indeed zero. */ + VERIFY_CHECK((t + (a->d[7] >> 1) + (SECP256K1_N_H_7 & mask)) >> 32 == 0); + + SECP256K1_SCALAR_VERIFY(r); } SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; } static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { int yes = 0; int no = 0; + SECP256K1_SCALAR_VERIFY(a); + no |= (a->d[7] < SECP256K1_N_H_7); yes |= (a->d[7] > SECP256K1_N_H_7) & ~no; no |= (a->d[6] < SECP256K1_N_H_6) & ~yes; /* No need for a > check. */ @@ -238,9 +321,12 @@ static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { /* If we are flag = 0, mask = 00...00 and this is a no-op; * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ - uint32_t mask = !flag - 1; + volatile int vflag = flag; + uint32_t mask = -vflag; uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + SECP256K1_SCALAR_VERIFY(r); + r->d[0] = t & nonzero; t >>= 32; t += (uint64_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); r->d[1] = t & nonzero; t >>= 32; @@ -256,6 +342,8 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { r->d[6] = t & nonzero; t >>= 32; t += (uint64_t)(r->d[7] ^ mask) + (SECP256K1_N_7 & mask); r->d[7] = t & nonzero; + + SECP256K1_SCALAR_VERIFY(r); return 2 * (mask == 0) - 1; } @@ -271,9 +359,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { tl = t; \ } \ c0 += tl; /* overflow is handled on the next line */ \ - th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + th += (c0 < tl); /* at most 0xFFFFFFFF */ \ c1 += th; /* overflow is handled on the next line */ \ - c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ + c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \ VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ } @@ -286,46 +374,24 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { tl = t; \ } \ c0 += tl; /* overflow is handled on the next line */ \ - th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + th += (c0 < tl); /* at most 0xFFFFFFFF */ \ c1 += th; /* never overflows by contract (verified in the next line) */ \ VERIFY_CHECK(c1 >= th); \ } -/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ -#define muladd2(a,b) { \ - uint32_t tl, th, th2, tl2; \ - { \ - uint64_t t = (uint64_t)a * b; \ - th = t >> 32; /* at most 0xFFFFFFFE */ \ - tl = t; \ - } \ - th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \ - c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ - VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ - tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \ - th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ - c0 += tl2; /* overflow is handled on the next line */ \ - th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ - c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ - VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ - c1 += th2; /* overflow is handled on the next line */ \ - c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ - VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ -} - /** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ #define sumadd(a) { \ unsigned int over; \ c0 += (a); /* overflow is handled on the next line */ \ - over = (c0 < (a)) ? 1 : 0; \ + over = (c0 < (a)); \ c1 += over; /* overflow is handled on the next line */ \ - c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ + c2 += (c1 < over); /* never overflows by contract */ \ } /** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ #define sumadd_fast(a) { \ c0 += (a); /* overflow is handled on the next line */ \ - c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \ VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ VERIFY_CHECK(c2 == 0); \ } @@ -576,124 +642,52 @@ static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, con l[15] = c0; } -static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { - /* 96 bit accumulator. */ - uint32_t c0 = 0, c1 = 0, c2 = 0; - - /* l[0..15] = a[0..7]^2. */ - muladd_fast(a->d[0], a->d[0]); - extract_fast(l[0]); - muladd2(a->d[0], a->d[1]); - extract(l[1]); - muladd2(a->d[0], a->d[2]); - muladd(a->d[1], a->d[1]); - extract(l[2]); - muladd2(a->d[0], a->d[3]); - muladd2(a->d[1], a->d[2]); - extract(l[3]); - muladd2(a->d[0], a->d[4]); - muladd2(a->d[1], a->d[3]); - muladd(a->d[2], a->d[2]); - extract(l[4]); - muladd2(a->d[0], a->d[5]); - muladd2(a->d[1], a->d[4]); - muladd2(a->d[2], a->d[3]); - extract(l[5]); - muladd2(a->d[0], a->d[6]); - muladd2(a->d[1], a->d[5]); - muladd2(a->d[2], a->d[4]); - muladd(a->d[3], a->d[3]); - extract(l[6]); - muladd2(a->d[0], a->d[7]); - muladd2(a->d[1], a->d[6]); - muladd2(a->d[2], a->d[5]); - muladd2(a->d[3], a->d[4]); - extract(l[7]); - muladd2(a->d[1], a->d[7]); - muladd2(a->d[2], a->d[6]); - muladd2(a->d[3], a->d[5]); - muladd(a->d[4], a->d[4]); - extract(l[8]); - muladd2(a->d[2], a->d[7]); - muladd2(a->d[3], a->d[6]); - muladd2(a->d[4], a->d[5]); - extract(l[9]); - muladd2(a->d[3], a->d[7]); - muladd2(a->d[4], a->d[6]); - muladd(a->d[5], a->d[5]); - extract(l[10]); - muladd2(a->d[4], a->d[7]); - muladd2(a->d[5], a->d[6]); - extract(l[11]); - muladd2(a->d[5], a->d[7]); - muladd(a->d[6], a->d[6]); - extract(l[12]); - muladd2(a->d[6], a->d[7]); - extract(l[13]); - muladd_fast(a->d[7], a->d[7]); - extract_fast(l[14]); - VERIFY_CHECK(c1 == 0); - l[15] = c0; -} - #undef sumadd #undef sumadd_fast #undef muladd #undef muladd_fast -#undef muladd2 #undef extract #undef extract_fast static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { uint32_t l[16]; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); + secp256k1_scalar_mul_512(l, a, b); secp256k1_scalar_reduce_512(r, l); -} -static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { - int ret; - VERIFY_CHECK(n > 0); - VERIFY_CHECK(n < 16); - ret = r->d[0] & ((1 << n) - 1); - r->d[0] = (r->d[0] >> n) + (r->d[1] << (32 - n)); - r->d[1] = (r->d[1] >> n) + (r->d[2] << (32 - n)); - r->d[2] = (r->d[2] >> n) + (r->d[3] << (32 - n)); - r->d[3] = (r->d[3] >> n) + (r->d[4] << (32 - n)); - r->d[4] = (r->d[4] >> n) + (r->d[5] << (32 - n)); - r->d[5] = (r->d[5] >> n) + (r->d[6] << (32 - n)); - r->d[6] = (r->d[6] >> n) + (r->d[7] << (32 - n)); - r->d[7] = (r->d[7] >> n); - return ret; + SECP256K1_SCALAR_VERIFY(r); } -static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { - uint32_t l[16]; - secp256k1_scalar_sqr_512(l, a); - secp256k1_scalar_reduce_512(r, l); -} +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *k) { + SECP256K1_SCALAR_VERIFY(k); -#ifdef USE_ENDOMORPHISM -static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { - r1->d[0] = a->d[0]; - r1->d[1] = a->d[1]; - r1->d[2] = a->d[2]; - r1->d[3] = a->d[3]; + r1->d[0] = k->d[0]; + r1->d[1] = k->d[1]; + r1->d[2] = k->d[2]; + r1->d[3] = k->d[3]; r1->d[4] = 0; r1->d[5] = 0; r1->d[6] = 0; r1->d[7] = 0; - r2->d[0] = a->d[4]; - r2->d[1] = a->d[5]; - r2->d[2] = a->d[6]; - r2->d[3] = a->d[7]; + r2->d[0] = k->d[4]; + r2->d[1] = k->d[5]; + r2->d[2] = k->d[6]; + r2->d[3] = k->d[7]; r2->d[4] = 0; r2->d[5] = 0; r2->d[6] = 0; r2->d[7] = 0; + + SECP256K1_SCALAR_VERIFY(r1); + SECP256K1_SCALAR_VERIFY(r2); } -#endif SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3]) | (a->d[4] ^ b->d[4]) | (a->d[5] ^ b->d[5]) | (a->d[6] ^ b->d[6]) | (a->d[7] ^ b->d[7])) == 0; } @@ -702,7 +696,10 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, unsigned int shiftlimbs; unsigned int shiftlow; unsigned int shifthigh; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); shiftlimbs = shift >> 5; shiftlow = shift & 0x1F; @@ -716,6 +713,115 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, r->d[6] = shift < 320 ? (l[6 + shiftlimbs] >> shiftlow | (shift < 288 && shiftlow ? (l[7 + shiftlimbs] << shifthigh) : 0)) : 0; r->d[7] = shift < 288 ? (l[7 + shiftlimbs] >> shiftlow) : 0; secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); + + SECP256K1_SCALAR_VERIFY(r); +} + +static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { + uint32_t mask0, mask1; + volatile int vflag = flag; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_CHECKMEM_CHECK_VERIFY(r->d, sizeof(r->d)); + + mask0 = vflag + ~((uint32_t)0); + mask1 = ~mask0; + r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1); + r->d[1] = (r->d[1] & mask0) | (a->d[1] & mask1); + r->d[2] = (r->d[2] & mask0) | (a->d[2] & mask1); + r->d[3] = (r->d[3] & mask0) | (a->d[3] & mask1); + r->d[4] = (r->d[4] & mask0) | (a->d[4] & mask1); + r->d[5] = (r->d[5] & mask0) | (a->d[5] & mask1); + r->d[6] = (r->d[6] & mask0) | (a->d[6] & mask1); + r->d[7] = (r->d[7] & mask0) | (a->d[7] & mask1); + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_from_signed30(secp256k1_scalar *r, const secp256k1_modinv32_signed30 *a) { + const uint32_t a0 = a->v[0], a1 = a->v[1], a2 = a->v[2], a3 = a->v[3], a4 = a->v[4], + a5 = a->v[5], a6 = a->v[6], a7 = a->v[7], a8 = a->v[8]; + + /* The output from secp256k1_modinv32{_var} should be normalized to range [0,modulus), and + * have limbs in [0,2^30). The modulus is < 2^256, so the top limb must be below 2^(256-30*8). + */ + VERIFY_CHECK(a0 >> 30 == 0); + VERIFY_CHECK(a1 >> 30 == 0); + VERIFY_CHECK(a2 >> 30 == 0); + VERIFY_CHECK(a3 >> 30 == 0); + VERIFY_CHECK(a4 >> 30 == 0); + VERIFY_CHECK(a5 >> 30 == 0); + VERIFY_CHECK(a6 >> 30 == 0); + VERIFY_CHECK(a7 >> 30 == 0); + VERIFY_CHECK(a8 >> 16 == 0); + + r->d[0] = a0 | a1 << 30; + r->d[1] = a1 >> 2 | a2 << 28; + r->d[2] = a2 >> 4 | a3 << 26; + r->d[3] = a3 >> 6 | a4 << 24; + r->d[4] = a4 >> 8 | a5 << 22; + r->d[5] = a5 >> 10 | a6 << 20; + r->d[6] = a6 >> 12 | a7 << 18; + r->d[7] = a7 >> 14 | a8 << 16; + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_to_signed30(secp256k1_modinv32_signed30 *r, const secp256k1_scalar *a) { + const uint32_t M30 = UINT32_MAX >> 2; + const uint32_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3], + a4 = a->d[4], a5 = a->d[5], a6 = a->d[6], a7 = a->d[7]; + SECP256K1_SCALAR_VERIFY(a); + + r->v[0] = a0 & M30; + r->v[1] = (a0 >> 30 | a1 << 2) & M30; + r->v[2] = (a1 >> 28 | a2 << 4) & M30; + r->v[3] = (a2 >> 26 | a3 << 6) & M30; + r->v[4] = (a3 >> 24 | a4 << 8) & M30; + r->v[5] = (a4 >> 22 | a5 << 10) & M30; + r->v[6] = (a5 >> 20 | a6 << 12) & M30; + r->v[7] = (a6 >> 18 | a7 << 14) & M30; + r->v[8] = a7 >> 16; +} + +static const secp256k1_modinv32_modinfo secp256k1_const_modinfo_scalar = { + {{0x10364141L, 0x3F497A33L, 0x348A03BBL, 0x2BB739ABL, -0x146L, 0, 0, 0, 65536}}, + 0x2A774EC1L +}; + +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { + secp256k1_modinv32_signed30 s; +#ifdef VERIFY + int zero_in = secp256k1_scalar_is_zero(x); +#endif + SECP256K1_SCALAR_VERIFY(x); + + secp256k1_scalar_to_signed30(&s, x); + secp256k1_modinv32(&s, &secp256k1_const_modinfo_scalar); + secp256k1_scalar_from_signed30(r, &s); + + SECP256K1_SCALAR_VERIFY(r); + VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in); +} + +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { + secp256k1_modinv32_signed30 s; +#ifdef VERIFY + int zero_in = secp256k1_scalar_is_zero(x); +#endif + SECP256K1_SCALAR_VERIFY(x); + + secp256k1_scalar_to_signed30(&s, x); + secp256k1_modinv32_var(&s, &secp256k1_const_modinfo_scalar); + secp256k1_scalar_from_signed30(r, &s); + + SECP256K1_SCALAR_VERIFY(r); + VERIFY_CHECK(secp256k1_scalar_is_zero(r) == zero_in); +} + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + + return !(a->d[0] & 1); } #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h index ee26b206d..972d8041b 100644 --- a/src/secp256k1/src/scalar_impl.h +++ b/src/secp256k1/src/scalar_impl.h @@ -1,279 +1,116 @@ -/************************************************************************ - * Copyright (c) 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_SCALAR_IMPL_H #define SECP256K1_SCALAR_IMPL_H -#include "group.h" -#include "scalar.h" - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" +#ifdef VERIFY +#include #endif +#include "scalar.h" +#include "util.h" + #if defined(EXHAUSTIVE_TEST_ORDER) #include "scalar_low_impl.h" -#elif defined(USE_SCALAR_4X64) +#elif defined(SECP256K1_WIDEMUL_INT128) #include "scalar_4x64_impl.h" -#elif defined(USE_SCALAR_8X32) +#elif defined(SECP256K1_WIDEMUL_INT64) #include "scalar_8x32_impl.h" #else -#error "Please select scalar implementation" +#error "Please select wide multiplication implementation" #endif -#ifndef USE_NUM_NONE -static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) { - unsigned char c[32]; - secp256k1_scalar_get_b32(c, a); - secp256k1_num_set_bin(r, c, 32); -} - -/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */ -static void secp256k1_scalar_order_get_num(secp256k1_num *r) { -#if defined(EXHAUSTIVE_TEST_ORDER) - static const unsigned char order[32] = { - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER - }; -#else - static const unsigned char order[32] = { - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, - 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, - 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 - }; -#endif - secp256k1_num_set_bin(r, order, 32); -} -#endif - -static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { -#if defined(EXHAUSTIVE_TEST_ORDER) - int i; - *r = 0; - for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) - if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1) - *r = i; - /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus - * have a composite group order; fix it in exhaustive_tests.c). */ - VERIFY_CHECK(*r != 0); -} -#else - secp256k1_scalar *t; - int i; - /* First compute xN as x ^ (2^N - 1) for some values of N, - * and uM as x ^ M for some values of M. */ - secp256k1_scalar x2, x3, x6, x8, x14, x28, x56, x112, x126; - secp256k1_scalar u2, u5, u9, u11, u13; - - secp256k1_scalar_sqr(&u2, x); - secp256k1_scalar_mul(&x2, &u2, x); - secp256k1_scalar_mul(&u5, &u2, &x2); - secp256k1_scalar_mul(&x3, &u5, &u2); - secp256k1_scalar_mul(&u9, &x3, &u2); - secp256k1_scalar_mul(&u11, &u9, &u2); - secp256k1_scalar_mul(&u13, &u11, &u2); - - secp256k1_scalar_sqr(&x6, &u13); - secp256k1_scalar_sqr(&x6, &x6); - secp256k1_scalar_mul(&x6, &x6, &u11); +static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); +static const secp256k1_scalar secp256k1_scalar_zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); - secp256k1_scalar_sqr(&x8, &x6); - secp256k1_scalar_sqr(&x8, &x8); - secp256k1_scalar_mul(&x8, &x8, &x2); +static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin) { + int overflow; + secp256k1_scalar_set_b32(r, bin, &overflow); - secp256k1_scalar_sqr(&x14, &x8); - for (i = 0; i < 5; i++) { - secp256k1_scalar_sqr(&x14, &x14); - } - secp256k1_scalar_mul(&x14, &x14, &x6); - - secp256k1_scalar_sqr(&x28, &x14); - for (i = 0; i < 13; i++) { - secp256k1_scalar_sqr(&x28, &x28); - } - secp256k1_scalar_mul(&x28, &x28, &x14); - - secp256k1_scalar_sqr(&x56, &x28); - for (i = 0; i < 27; i++) { - secp256k1_scalar_sqr(&x56, &x56); - } - secp256k1_scalar_mul(&x56, &x56, &x28); - - secp256k1_scalar_sqr(&x112, &x56); - for (i = 0; i < 55; i++) { - secp256k1_scalar_sqr(&x112, &x112); - } - secp256k1_scalar_mul(&x112, &x112, &x56); - - secp256k1_scalar_sqr(&x126, &x112); - for (i = 0; i < 13; i++) { - secp256k1_scalar_sqr(&x126, &x126); - } - secp256k1_scalar_mul(&x126, &x126, &x14); - - /* Then accumulate the final result (t starts at x126). */ - t = &x126; - for (i = 0; i < 3; i++) { - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u5); /* 101 */ - for (i = 0; i < 4; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &x3); /* 111 */ - for (i = 0; i < 4; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u5); /* 101 */ - for (i = 0; i < 5; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u11); /* 1011 */ - for (i = 0; i < 4; i++) { - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u11); /* 1011 */ - for (i = 0; i < 4; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &x3); /* 111 */ - for (i = 0; i < 5; i++) { /* 00 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &x3); /* 111 */ - for (i = 0; i < 6; i++) { /* 00 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u13); /* 1101 */ - for (i = 0; i < 4; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u5); /* 101 */ - for (i = 0; i < 3; i++) { - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &x3); /* 111 */ - for (i = 0; i < 5; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u9); /* 1001 */ - for (i = 0; i < 6; i++) { /* 000 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u5); /* 101 */ - for (i = 0; i < 10; i++) { /* 0000000 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &x3); /* 111 */ - for (i = 0; i < 4; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &x3); /* 111 */ - for (i = 0; i < 9; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &x8); /* 11111111 */ - for (i = 0; i < 5; i++) { /* 0 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u9); /* 1001 */ - for (i = 0; i < 6; i++) { /* 00 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u11); /* 1011 */ - for (i = 0; i < 4; i++) { - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u13); /* 1101 */ - for (i = 0; i < 5; i++) { - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &x2); /* 11 */ - for (i = 0; i < 6; i++) { /* 00 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u13); /* 1101 */ - for (i = 0; i < 10; i++) { /* 000000 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u13); /* 1101 */ - for (i = 0; i < 4; i++) { - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, &u9); /* 1001 */ - for (i = 0; i < 6; i++) { /* 00000 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(t, t, x); /* 1 */ - for (i = 0; i < 8; i++) { /* 00 */ - secp256k1_scalar_sqr(t, t); - } - secp256k1_scalar_mul(r, t, &x6); /* 111111 */ + SECP256K1_SCALAR_VERIFY(r); + return (!overflow) & (!secp256k1_scalar_is_zero(r)); } -SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { - return !(a->d[0] & 1); -} -#endif +static void secp256k1_scalar_verify(const secp256k1_scalar *r) { + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { -#if defined(USE_SCALAR_INV_BUILTIN) - secp256k1_scalar_inverse(r, x); -#elif defined(USE_SCALAR_INV_NUM) - unsigned char b[32]; - secp256k1_num n, m; - secp256k1_scalar t = *x; - secp256k1_scalar_get_b32(b, &t); - secp256k1_num_set_bin(&n, b, 32); - secp256k1_scalar_order_get_num(&m); - secp256k1_num_mod_inverse(&n, &n, &m); - secp256k1_num_get_bin(b, 32, &n); - secp256k1_scalar_set_b32(r, b, NULL); - /* Verify that the inverse was computed correctly, without GMP code. */ - secp256k1_scalar_mul(&t, &t, r); - CHECK(secp256k1_scalar_is_one(&t)); -#else -#error "Please select scalar inverse implementation" -#endif + (void)r; } -#ifdef USE_ENDOMORPHISM #if defined(EXHAUSTIVE_TEST_ORDER) +/* Begin of section generated by sage/gen_exhaustive_groups.sage. */ +# if EXHAUSTIVE_TEST_ORDER == 7 +# define EXHAUSTIVE_TEST_LAMBDA 2 +# elif EXHAUSTIVE_TEST_ORDER == 13 +# define EXHAUSTIVE_TEST_LAMBDA 9 +# elif EXHAUSTIVE_TEST_ORDER == 199 +# define EXHAUSTIVE_TEST_LAMBDA 92 +# else +# error No known lambda for the specified exhaustive test group order. +# endif +/* End of section generated by sage/gen_exhaustive_groups.sage. */ + /** - * Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the - * full case we don't bother making k1 and k2 be small, we just want them to be + * Find r1 and r2 given k, such that r1 + r2 * lambda == k mod n; unlike in the + * full case we don't bother making r1 and r2 be small, we just want them to be * nontrivial to get full test coverage for the exhaustive tests. We therefore - * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda. + * (arbitrarily) set r2 = k + 5 (mod n) and r1 = k - r2 * lambda (mod n). */ -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { - *r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER; - *r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER; +static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT r1, secp256k1_scalar * SECP256K1_RESTRICT r2, const secp256k1_scalar * SECP256K1_RESTRICT k) { + SECP256K1_SCALAR_VERIFY(k); + VERIFY_CHECK(r1 != k); + VERIFY_CHECK(r2 != k); + VERIFY_CHECK(r1 != r2); + + *r2 = (*k + 5) % EXHAUSTIVE_TEST_ORDER; + *r1 = (*k + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER; + + SECP256K1_SCALAR_VERIFY(r1); + SECP256K1_SCALAR_VERIFY(r2); } #else /** * The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where - * lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a, - * 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72} + * lambda is: */ +static const secp256k1_scalar secp256k1_const_lambda = SECP256K1_SCALAR_CONST( + 0x5363AD4CUL, 0xC05C30E0UL, 0xA5261C02UL, 0x8812645AUL, + 0x122E22EAUL, 0x20816678UL, 0xDF02967CUL, 0x1B23BD72UL +); + +#ifdef VERIFY +static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, const secp256k1_scalar *r2, const secp256k1_scalar *k); +#endif + +/* + * Both lambda and beta are primitive cube roots of unity. That is lamba^3 == 1 mod n and + * beta^3 == 1 mod p, where n is the curve order and p is the field order. * - * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm - * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1 - * and k2 have a small size. - * It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are: + * Furthermore, because (X^3 - 1) = (X - 1)(X^2 + X + 1), the primitive cube roots of unity are + * roots of X^2 + X + 1. Therefore lambda^2 + lamba == -1 mod n and beta^2 + beta == -1 mod p. + * (The other primitive cube roots of unity are lambda^2 and beta^2 respectively.) + * + * Let l = -1/2 + i*sqrt(3)/2, the complex root of X^2 + X + 1. We can define a ring + * homomorphism phi : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n. The kernel of phi + * is a lattice over Z[l] (considering Z[l] as a Z-module). This lattice is generated by a + * reduced basis {a1 + b1*l, a2 + b2*l} where * * - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} * - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3} * - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8} * - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} * - * The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives + * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm + * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1 + * and k2 are small in absolute value. + * + * The algorithm computes c1 = round(b2 * k / n) and c2 = round((-b1) * k / n), and gives * k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and - * compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2. + * compute r2 = k2 mod n, and r1 = k1 mod n = (k - r2 * lambda) mod n, avoiding the need for + * the constants a1 and a2. * * g1, g2 are precomputed constants used to replace division with a rounded multiplication * when decomposing the scalar for an endomorphism-based point multiplication. @@ -285,21 +122,21 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar * Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez), * Section 4.3 (here we use a somewhat higher-precision estimate): * d = a1*b2 - b1*a2 - * g1 = round((2^272)*b2/d) - * g2 = round((2^272)*b1/d) + * g1 = round(2^384 * b2/d) + * g2 = round(2^384 * (-b1)/d) + * + * (Note that d is also equal to the curve order, n, here because [a1,b1] and [a2,b2] + * can be found as outputs of the Extended Euclidean Algorithm on inputs n and lambda). * - * (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found - * as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda'). + * The function below splits k into r1 and r2, such that + * - r1 + lambda * r2 == k (mod n) + * - either r1 < 2^128 or -r1 mod n < 2^128 + * - either r2 < 2^128 or -r2 mod n < 2^128 * - * The function below splits a in r1 and r2, such that r1 + lambda * r2 == a (mod order). + * See proof below. */ - -static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { +static void secp256k1_scalar_split_lambda(secp256k1_scalar * SECP256K1_RESTRICT r1, secp256k1_scalar * SECP256K1_RESTRICT r2, const secp256k1_scalar * SECP256K1_RESTRICT k) { secp256k1_scalar c1, c2; - static const secp256k1_scalar minus_lambda = SECP256K1_SCALAR_CONST( - 0xAC9C52B3UL, 0x3FA3CF1FUL, 0x5AD9E3FDUL, 0x77ED9BA4UL, - 0xA880B9FCUL, 0x8EC739C2UL, 0xE0CFC810UL, 0xB51283CFUL - ); static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST( 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL @@ -309,25 +146,172 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar 0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL ); static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST( - 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL, - 0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL + 0x3086D221UL, 0xA7D46BCDUL, 0xE86C90E4UL, 0x9284EB15UL, + 0x3DAA8A14UL, 0x71E8CA7FUL, 0xE893209AUL, 0x45DBB031UL ); static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST( - 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL, - 0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL + 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C4UL, + 0x221208ACUL, 0x9DF506C6UL, 0x1571B4AEUL, 0x8AC47F71UL ); - VERIFY_CHECK(r1 != a); - VERIFY_CHECK(r2 != a); + SECP256K1_SCALAR_VERIFY(k); + VERIFY_CHECK(r1 != k); + VERIFY_CHECK(r2 != k); + VERIFY_CHECK(r1 != r2); + /* these _var calls are constant time since the shift amount is constant */ - secp256k1_scalar_mul_shift_var(&c1, a, &g1, 272); - secp256k1_scalar_mul_shift_var(&c2, a, &g2, 272); + secp256k1_scalar_mul_shift_var(&c1, k, &g1, 384); + secp256k1_scalar_mul_shift_var(&c2, k, &g2, 384); secp256k1_scalar_mul(&c1, &c1, &minus_b1); secp256k1_scalar_mul(&c2, &c2, &minus_b2); secp256k1_scalar_add(r2, &c1, &c2); - secp256k1_scalar_mul(r1, r2, &minus_lambda); - secp256k1_scalar_add(r1, r1, a); -} -#endif + secp256k1_scalar_mul(r1, r2, &secp256k1_const_lambda); + secp256k1_scalar_negate(r1, r1); + secp256k1_scalar_add(r1, r1, k); + + SECP256K1_SCALAR_VERIFY(r1); + SECP256K1_SCALAR_VERIFY(r2); +#ifdef VERIFY + secp256k1_scalar_split_lambda_verify(r1, r2, k); #endif +} + +#ifdef VERIFY +/* + * Proof for secp256k1_scalar_split_lambda's bounds. + * + * Let + * - epsilon1 = 2^256 * |g1/2^384 - b2/d| + * - epsilon2 = 2^256 * |g2/2^384 - (-b1)/d| + * - c1 = round(k*g1/2^384) + * - c2 = round(k*g2/2^384) + * + * Lemma 1: |c1 - k*b2/d| < 2^-1 + epsilon1 + * + * |c1 - k*b2/d| + * = + * |c1 - k*g1/2^384 + k*g1/2^384 - k*b2/d| + * <= {triangle inequality} + * |c1 - k*g1/2^384| + |k*g1/2^384 - k*b2/d| + * = + * |c1 - k*g1/2^384| + k*|g1/2^384 - b2/d| + * < {rounding in c1 and 0 <= k < 2^256} + * 2^-1 + 2^256 * |g1/2^384 - b2/d| + * = {definition of epsilon1} + * 2^-1 + epsilon1 + * + * Lemma 2: |c2 - k*(-b1)/d| < 2^-1 + epsilon2 + * + * |c2 - k*(-b1)/d| + * = + * |c2 - k*g2/2^384 + k*g2/2^384 - k*(-b1)/d| + * <= {triangle inequality} + * |c2 - k*g2/2^384| + |k*g2/2^384 - k*(-b1)/d| + * = + * |c2 - k*g2/2^384| + k*|g2/2^384 - (-b1)/d| + * < {rounding in c2 and 0 <= k < 2^256} + * 2^-1 + 2^256 * |g2/2^384 - (-b1)/d| + * = {definition of epsilon2} + * 2^-1 + epsilon2 + * + * Let + * - k1 = k - c1*a1 - c2*a2 + * - k2 = - c1*b1 - c2*b2 + * + * Lemma 3: |k1| < (a1 + a2 + 1)/2 < 2^128 + * + * |k1| + * = {definition of k1} + * |k - c1*a1 - c2*a2| + * = {(a1*b2 - b1*a2)/n = 1} + * |k*(a1*b2 - b1*a2)/n - c1*a1 - c2*a2| + * = + * |a1*(k*b2/n - c1) + a2*(k*(-b1)/n - c2)| + * <= {triangle inequality} + * a1*|k*b2/n - c1| + a2*|k*(-b1)/n - c2| + * < {Lemma 1 and Lemma 2} + * a1*(2^-1 + epsilon1) + a2*(2^-1 + epsilon2) + * < {rounding up to an integer} + * (a1 + a2 + 1)/2 + * < {rounding up to a power of 2} + * 2^128 + * + * Lemma 4: |k2| < (-b1 + b2)/2 + 1 < 2^128 + * + * |k2| + * = {definition of k2} + * |- c1*a1 - c2*a2| + * = {(b1*b2 - b1*b2)/n = 0} + * |k*(b1*b2 - b1*b2)/n - c1*b1 - c2*b2| + * = + * |b1*(k*b2/n - c1) + b2*(k*(-b1)/n - c2)| + * <= {triangle inequality} + * (-b1)*|k*b2/n - c1| + b2*|k*(-b1)/n - c2| + * < {Lemma 1 and Lemma 2} + * (-b1)*(2^-1 + epsilon1) + b2*(2^-1 + epsilon2) + * < {rounding up to an integer} + * (-b1 + b2)/2 + 1 + * < {rounding up to a power of 2} + * 2^128 + * + * Let + * - r2 = k2 mod n + * - r1 = k - r2*lambda mod n. + * + * Notice that r1 is defined such that r1 + r2 * lambda == k (mod n). + * + * Lemma 5: r1 == k1 mod n. + * + * r1 + * == {definition of r1 and r2} + * k - k2*lambda + * == {definition of k2} + * k - (- c1*b1 - c2*b2)*lambda + * == + * k + c1*b1*lambda + c2*b2*lambda + * == {a1 + b1*lambda == 0 mod n and a2 + b2*lambda == 0 mod n} + * k - c1*a1 - c2*a2 + * == {definition of k1} + * k1 + * + * From Lemma 3, Lemma 4, Lemma 5 and the definition of r2, we can conclude that + * + * - either r1 < 2^128 or -r1 mod n < 2^128 + * - either r2 < 2^128 or -r2 mod n < 2^128. + * + * Q.E.D. + */ +static void secp256k1_scalar_split_lambda_verify(const secp256k1_scalar *r1, const secp256k1_scalar *r2, const secp256k1_scalar *k) { + secp256k1_scalar s; + unsigned char buf1[32]; + unsigned char buf2[32]; + + /* (a1 + a2 + 1)/2 is 0xa2a8918ca85bafe22016d0b917e4dd77 */ + static const unsigned char k1_bound[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa2, 0xa8, 0x91, 0x8c, 0xa8, 0x5b, 0xaf, 0xe2, 0x20, 0x16, 0xd0, 0xb9, 0x17, 0xe4, 0xdd, 0x77 + }; + + /* (-b1 + b2)/2 + 1 is 0x8a65287bd47179fb2be08846cea267ed */ + static const unsigned char k2_bound[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8a, 0x65, 0x28, 0x7b, 0xd4, 0x71, 0x79, 0xfb, 0x2b, 0xe0, 0x88, 0x46, 0xce, 0xa2, 0x67, 0xed + }; + + secp256k1_scalar_mul(&s, &secp256k1_const_lambda, r2); + secp256k1_scalar_add(&s, &s, r1); + VERIFY_CHECK(secp256k1_scalar_eq(&s, k)); + + secp256k1_scalar_negate(&s, r1); + secp256k1_scalar_get_b32(buf1, r1); + secp256k1_scalar_get_b32(buf2, &s); + VERIFY_CHECK(secp256k1_memcmp_var(buf1, k1_bound, 32) < 0 || secp256k1_memcmp_var(buf2, k1_bound, 32) < 0); + + secp256k1_scalar_negate(&s, r2); + secp256k1_scalar_get_b32(buf1, r2); + secp256k1_scalar_get_b32(buf2, &s); + VERIFY_CHECK(secp256k1_memcmp_var(buf1, k2_bound, 32) < 0 || secp256k1_memcmp_var(buf2, k2_bound, 32) < 0); +} +#endif /* VERIFY */ +#endif /* !defined(EXHAUSTIVE_TEST_ORDER) */ #endif /* SECP256K1_SCALAR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h index 7bfd1cf65..2711eb932 100644 --- a/src/secp256k1/src/scalar_low.h +++ b/src/secp256k1/src/scalar_low.h @@ -1,8 +1,8 @@ -/************************************************************************ - * Copyright (c) 2015 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015, 2022 Andrew Poelstra, Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_SCALAR_REPR_H #define SECP256K1_SCALAR_REPR_H @@ -12,4 +12,13 @@ /** A scalar modulo the group order of the secp256k1 curve. */ typedef uint32_t secp256k1_scalar; +/* A compile-time constant equal to 2^32 (modulo order). */ +#define SCALAR_2P32 ((0xffffffffUL % EXHAUSTIVE_TEST_ORDER) + 1U) + +/* Compute a*2^32 + b (modulo order). */ +#define SCALAR_HORNER(a, b) (((uint64_t)(a) * SCALAR_2P32 + (b)) % EXHAUSTIVE_TEST_ORDER) + +/* Evaluates to the provided 256-bit constant reduced modulo order. */ +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) SCALAR_HORNER(SCALAR_HORNER(SCALAR_HORNER(SCALAR_HORNER(SCALAR_HORNER(SCALAR_HORNER(SCALAR_HORNER((d7), (d6)), (d5)), (d4)), (d3)), (d2)), (d1)), (d0)) + #endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h index 9b338feea..45d2f3e46 100644 --- a/src/secp256k1/src/scalar_low_impl.h +++ b/src/secp256k1/src/scalar_low_impl.h @@ -1,114 +1,208 @@ -/************************************************************************ - * Copyright (c) 2015 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "checkmem.h" #include "scalar.h" +#include "util.h" #include SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + return !(*a & 1); } SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; } -SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - if (offset < 32) - return ((*a >> offset) & ((((uint32_t)1) << count) - 1)); - else +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + *r = v % EXHAUSTIVE_TEST_ORDER; + + SECP256K1_SCALAR_VERIFY(r); +} + +SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_limb32(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + SECP256K1_SCALAR_VERIFY(a); + + VERIFY_CHECK(count > 0 && count <= 32); + if (offset < 32) { + return (*a >> offset) & (0xFFFFFFFF >> (32 - count)); + } else { return 0; + } } -SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { - return secp256k1_scalar_get_bits(a, offset, count); +SECP256K1_INLINE static uint32_t secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + SECP256K1_SCALAR_VERIFY(a); + + return secp256k1_scalar_get_bits_limb32(a, offset, count); } SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; } static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); + *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER; + + SECP256K1_SCALAR_VERIFY(r); return *r < *b; } static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + SECP256K1_SCALAR_VERIFY(r); + if (flag && bit < 32) - *r += (1 << bit); -#ifdef VERIFY - VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); -#endif + *r += ((uint32_t)1 << bit); + + SECP256K1_SCALAR_VERIFY(r); + VERIFY_CHECK(bit < 32); + /* Verify that adding (1 << bit) will not overflow any in-range scalar *r by overflowing the underlying uint32_t. */ + VERIFY_CHECK(((uint32_t)1 << bit) - 1 <= UINT32_MAX - EXHAUSTIVE_TEST_ORDER); } static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { - const int base = 0x100 % EXHAUSTIVE_TEST_ORDER; int i; + int over = 0; *r = 0; for (i = 0; i < 32; i++) { - *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER; + *r = (*r * 0x100) + b32[i]; + if (*r >= EXHAUSTIVE_TEST_ORDER) { + over = 1; + *r %= EXHAUSTIVE_TEST_ORDER; + } } - /* just deny overflow, it basically always happens */ - if (overflow) *overflow = 0; + if (overflow) *overflow = over; + + SECP256K1_SCALAR_VERIFY(r); } static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + SECP256K1_SCALAR_VERIFY(a); + memset(bin, 0, 32); bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a; } SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + return *a == 0; } static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + if (*a == 0) { *r = 0; } else { *r = EXHAUSTIVE_TEST_ORDER - *a; } + + SECP256K1_SCALAR_VERIFY(r); } SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + return *a == 1; } static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + return *a > EXHAUSTIVE_TEST_ORDER / 2; } static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + SECP256K1_SCALAR_VERIFY(r); + if (flag) secp256k1_scalar_negate(r, r); + + SECP256K1_SCALAR_VERIFY(r); return flag ? -1 : 1; } static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { - *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER; -} + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); -static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { - int ret; - VERIFY_CHECK(n > 0); - VERIFY_CHECK(n < 16); - ret = *r & ((1 << n) - 1); - *r >>= n; - return ret; -} + *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER; -static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { - *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; + SECP256K1_SCALAR_VERIFY(r); } static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + *r1 = *a; *r2 = 0; + + SECP256K1_SCALAR_VERIFY(r1); + SECP256K1_SCALAR_VERIFY(r2); } SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_SCALAR_VERIFY(b); + return *a == *b; } +static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) { + uint32_t mask0, mask1; + volatile int vflag = flag; + SECP256K1_SCALAR_VERIFY(a); + SECP256K1_CHECKMEM_CHECK_VERIFY(r, sizeof(*r)); + + mask0 = vflag + ~((uint32_t)0); + mask1 = ~mask0; + *r = (*r & mask0) | (*a & mask1); + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { + int i; + uint32_t res = 0; + SECP256K1_SCALAR_VERIFY(x); + + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { + if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1) { + res = i; + break; + } + } + + /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus + * have a composite group order; fix it in exhaustive_tests.c). */ + VERIFY_CHECK(res != 0); + *r = res; + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { + SECP256K1_SCALAR_VERIFY(x); + + secp256k1_scalar_inverse(r, x); + + SECP256K1_SCALAR_VERIFY(r); +} + +static void secp256k1_scalar_half(secp256k1_scalar *r, const secp256k1_scalar *a) { + SECP256K1_SCALAR_VERIFY(a); + + *r = (*a + ((-(uint32_t)(*a & 1)) & EXHAUSTIVE_TEST_ORDER)) >> 1; + + SECP256K1_SCALAR_VERIFY(r); +} + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scratch.h b/src/secp256k1/src/scratch.h new file mode 100644 index 000000000..9dcb7581f --- /dev/null +++ b/src/secp256k1/src/scratch.h @@ -0,0 +1,42 @@ +/*********************************************************************** + * Copyright (c) 2017 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_SCRATCH_H +#define SECP256K1_SCRATCH_H + +/* The typedef is used internally; the struct name is used in the public API + * (where it is exposed as a different typedef) */ +typedef struct secp256k1_scratch_space_struct { + /** guard against interpreting this object as other types */ + unsigned char magic[8]; + /** actual allocated data */ + void *data; + /** amount that has been allocated (i.e. `data + offset` is the next + * available pointer) */ + size_t alloc_size; + /** maximum size available to allocate */ + size_t max_size; +} secp256k1_scratch; + +static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size); + +static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch); + +/** Returns an opaque object used to "checkpoint" a scratch space. Used + * with `secp256k1_scratch_apply_checkpoint` to undo allocations. */ +static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch); + +/** Applies a check point received from `secp256k1_scratch_checkpoint`, + * undoing all allocations since that point. */ +static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint); + +/** Returns the maximum allocation the scratch space will allow */ +static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t n_objects); + +/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */ +static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n); + +#endif diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h new file mode 100644 index 000000000..f71a20b96 --- /dev/null +++ b/src/secp256k1/src/scratch_impl.h @@ -0,0 +1,99 @@ +/*********************************************************************** + * Copyright (c) 2017 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_SCRATCH_IMPL_H +#define SECP256K1_SCRATCH_IMPL_H + +#include "util.h" +#include "scratch.h" + +static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t size) { + const size_t base_alloc = ROUND_TO_ALIGN(sizeof(secp256k1_scratch)); + void *alloc = checked_malloc(error_callback, base_alloc + size); + secp256k1_scratch* ret = (secp256k1_scratch *)alloc; + if (ret != NULL) { + memset(ret, 0, sizeof(*ret)); + memcpy(ret->magic, "scratch", 8); + ret->data = (void *) ((char *) alloc + base_alloc); + ret->max_size = size; + } + return ret; +} + +static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) { + if (scratch != NULL) { + if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return; + } + VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */ + memset(scratch->magic, 0, sizeof(scratch->magic)); + free(scratch); + } +} + +static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) { + if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return 0; + } + return scratch->alloc_size; +} + +static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) { + if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return; + } + if (checkpoint > scratch->alloc_size) { + secp256k1_callback_call(error_callback, "invalid checkpoint"); + return; + } + scratch->alloc_size = checkpoint; +} + +static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) { + if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return 0; + } + /* Ensure that multiplication will not wrap around */ + if (ALIGNMENT > 1 && objects > SIZE_MAX/(ALIGNMENT - 1)) { + return 0; + } + if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) { + return 0; + } + return scratch->max_size - scratch->alloc_size - objects * (ALIGNMENT - 1); +} + +static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) { + void *ret; + size_t rounded_size; + + rounded_size = ROUND_TO_ALIGN(size); + /* Check that rounding did not wrap around */ + if (rounded_size < size) { + return NULL; + } + size = rounded_size; + + if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { + secp256k1_callback_call(error_callback, "invalid scratch space"); + return NULL; + } + + if (size > scratch->max_size - scratch->alloc_size) { + return NULL; + } + ret = (void *) ((char *) scratch->data + scratch->alloc_size); + memset(ret, 0, size); + scratch->alloc_size += size; + + return ret; +} + +#endif diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c index 6c89b0c9b..72d725a74 100644 --- a/src/secp256k1/src/secp256k1.c +++ b/src/secp256k1/src/secp256k1.c @@ -1,13 +1,29 @@ -/************************************************************************ - * Copyright (c) 2013-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +/* This is a C project. It should not be compiled with a C++ compiler, + * and we error out if we detect one. + * + * We still want to be able to test the project with a C++ compiler + * because it is still good to know if this will lead to real trouble, so + * there is a possibility to override the check. But be warned that + * compiling with a C++ compiler is not supported. */ +#if defined(__cplusplus) && !defined(SECP256K1_CPLUSPLUS_TEST_OVERRIDE) +#error Trying to compile a C project with a C++ compiler. +#endif + +#define SECP256K1_BUILD -#include "include/secp256k1.h" +#include "../include/secp256k1.h" +#include "../include/secp256k1_preallocated.h" +#include "assumptions.h" +#include "checkmem.h" #include "util.h" -#include "num_impl.h" + #include "field_impl.h" #include "scalar_impl.h" #include "group_impl.h" @@ -17,6 +33,14 @@ #include "ecdsa_impl.h" #include "eckey_impl.h" #include "hash_impl.h" +#include "int128_impl.h" +#include "scratch_impl.h" +#include "selftest.h" +#include "hsort_impl.h" + +#ifdef SECP256K1_NO_BUILD +# error "secp256k1.h processed without SECP256K1_BUILD defined while building secp256k1.c" +#endif #define ARG_CHECK(cond) do { \ if (EXPECT(!(cond), 0)) { \ @@ -25,126 +49,214 @@ } \ } while(0) -static void default_illegal_callback_fn(const char* str, void* data) { - (void)data; - fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); - abort(); -} - -static const secp256k1_callback default_illegal_callback = { - default_illegal_callback_fn, - NULL -}; - -static void default_error_callback_fn(const char* str, void* data) { - (void)data; - fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); - abort(); -} - -static const secp256k1_callback default_error_callback = { - default_error_callback_fn, - NULL -}; - +#define ARG_CHECK_VOID(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + secp256k1_callback_call(&ctx->illegal_callback, #cond); \ + return; \ + } \ +} while(0) +/* Note that whenever you change the context struct, you must also change the + * context_eq function. */ struct secp256k1_context_struct { - secp256k1_ecmult_context ecmult_ctx; secp256k1_ecmult_gen_context ecmult_gen_ctx; secp256k1_callback illegal_callback; secp256k1_callback error_callback; + int declassify; }; -secp256k1_context* secp256k1_context_create(unsigned int flags) { - secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context)); - ret->illegal_callback = default_illegal_callback; - ret->error_callback = default_error_callback; +static const secp256k1_context secp256k1_context_static_ = { + { 0 }, + { secp256k1_default_illegal_callback_fn, 0 }, + { secp256k1_default_error_callback_fn, 0 }, + 0 +}; +const secp256k1_context *secp256k1_context_static = &secp256k1_context_static_; +const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_static_; + +/* Helper function that determines if a context is proper, i.e., is not the static context or a copy thereof. + * + * This is intended for "context" functions such as secp256k1_context_clone. Functions that need specific + * features of a context should still check for these features directly. For example, a function that needs + * ecmult_gen should directly check for the existence of the ecmult_gen context. */ +static int secp256k1_context_is_proper(const secp256k1_context* ctx) { + return secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx); +} + +void secp256k1_selftest(void) { + if (!secp256k1_selftest_passes()) { + secp256k1_callback_call(&default_error_callback, "self test failed"); + } +} + +size_t secp256k1_context_preallocated_size(unsigned int flags) { + size_t ret = sizeof(secp256k1_context); + /* A return value of 0 is reserved as an indicator for errors when we call this function internally. */ + VERIFY_CHECK(ret != 0); if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { - secp256k1_callback_call(&ret->illegal_callback, + secp256k1_callback_call(&default_illegal_callback, "Invalid flags"); - free(ret); - return NULL; + return 0; } - secp256k1_ecmult_context_init(&ret->ecmult_ctx); - secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); + if (EXPECT(!SECP256K1_CHECKMEM_RUNNING() && (flags & SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY), 0)) { + secp256k1_callback_call(&default_illegal_callback, + "Declassify flag requires running with memory checking"); + return 0; + } + + return ret; +} + +size_t secp256k1_context_preallocated_clone_size(const secp256k1_context* ctx) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_context_is_proper(ctx)); + return sizeof(secp256k1_context); +} + +secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigned int flags) { + size_t prealloc_size; + secp256k1_context* ret; + + secp256k1_selftest(); - if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { - secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); + prealloc_size = secp256k1_context_preallocated_size(flags); + if (prealloc_size == 0) { + return NULL; } - if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { - secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback); + VERIFY_CHECK(prealloc != NULL); + ret = (secp256k1_context*)prealloc; + ret->illegal_callback = default_illegal_callback; + ret->error_callback = default_error_callback; + + /* Flags have been checked by secp256k1_context_preallocated_size. */ + VERIFY_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_CONTEXT); + secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx); + ret->declassify = !!(flags & SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY); + + return ret; +} + +secp256k1_context* secp256k1_context_create(unsigned int flags) { + size_t const prealloc_size = secp256k1_context_preallocated_size(flags); + secp256k1_context* ctx = (secp256k1_context*)checked_malloc(&default_error_callback, prealloc_size); + if (EXPECT(secp256k1_context_preallocated_create(ctx, flags) == NULL, 0)) { + free(ctx); + return NULL; } + return ctx; +} + +secp256k1_context* secp256k1_context_preallocated_clone(const secp256k1_context* ctx, void* prealloc) { + secp256k1_context* ret; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(prealloc != NULL); + ARG_CHECK(secp256k1_context_is_proper(ctx)); + + ret = (secp256k1_context*)prealloc; + *ret = *ctx; return ret; } secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { - secp256k1_context* ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_context)); - ret->illegal_callback = ctx->illegal_callback; - ret->error_callback = ctx->error_callback; - secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback); - secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback); + secp256k1_context* ret; + size_t prealloc_size; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_context_is_proper(ctx)); + + prealloc_size = secp256k1_context_preallocated_clone_size(ctx); + ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, prealloc_size); + ret = secp256k1_context_preallocated_clone(ctx, ret); return ret; } +void secp256k1_context_preallocated_destroy(secp256k1_context* ctx) { + ARG_CHECK_VOID(ctx == NULL || secp256k1_context_is_proper(ctx)); + + /* Defined as noop */ + if (ctx == NULL) { + return; + } + + secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); +} + void secp256k1_context_destroy(secp256k1_context* ctx) { - if (ctx != NULL) { - secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); - secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); + ARG_CHECK_VOID(ctx == NULL || secp256k1_context_is_proper(ctx)); - free(ctx); + /* Defined as noop */ + if (ctx == NULL) { + return; } + + secp256k1_context_preallocated_destroy(ctx); + free(ctx); } void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + /* We compare pointers instead of checking secp256k1_context_is_proper() here + because setting callbacks is allowed on *copies* of the static context: + it's harmless and makes testing easier. */ + ARG_CHECK_VOID(ctx != secp256k1_context_static); if (fun == NULL) { - fun = default_illegal_callback_fn; + fun = secp256k1_default_illegal_callback_fn; } ctx->illegal_callback.fn = fun; ctx->illegal_callback.data = data; } void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + /* We compare pointers instead of checking secp256k1_context_is_proper() here + because setting callbacks is allowed on *copies* of the static context: + it's harmless and makes testing easier. */ + ARG_CHECK_VOID(ctx != secp256k1_context_static); if (fun == NULL) { - fun = default_error_callback_fn; + fun = secp256k1_default_error_callback_fn; } ctx->error_callback.fn = fun; ctx->error_callback.data = data; } +secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t max_size) { + VERIFY_CHECK(ctx != NULL); + return secp256k1_scratch_create(&ctx->error_callback, max_size); +} + +void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scratch_space* scratch) { + VERIFY_CHECK(ctx != NULL); + secp256k1_scratch_destroy(&ctx->error_callback, scratch); +} + +/* Mark memory as no-longer-secret for the purpose of analysing constant-time behaviour + * of the software. + */ +static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, const void *p, size_t len) { + if (EXPECT(ctx->declassify, 0)) SECP256K1_CHECKMEM_DEFINE(p, len); +} + static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { - if (sizeof(secp256k1_ge_storage) == 64) { - /* When the secp256k1_ge_storage type is exactly 64 byte, use its - * representation inside secp256k1_pubkey, as conversion is very fast. - * Note that secp256k1_pubkey_save must use the same representation. */ - secp256k1_ge_storage s; - memcpy(&s, &pubkey->data[0], 64); - secp256k1_ge_from_storage(ge, &s); - } else { - /* Otherwise, fall back to 32-byte big endian for X and Y. */ - secp256k1_fe x, y; - secp256k1_fe_set_b32(&x, pubkey->data); - secp256k1_fe_set_b32(&y, pubkey->data + 32); - secp256k1_ge_set_xy(ge, &x, &y); - } + secp256k1_ge_storage s; + + /* We require that the secp256k1_ge_storage type is exactly 64 bytes. + * This is formally not guaranteed by the C standard, but should hold on any + * sane compiler in the real world. */ + STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64); + memcpy(&s, &pubkey->data[0], 64); + secp256k1_ge_from_storage(ge, &s); ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); return 1; } static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { - if (sizeof(secp256k1_ge_storage) == 64) { - secp256k1_ge_storage s; - secp256k1_ge_to_storage(&s, ge); - memcpy(&pubkey->data[0], &s, 64); - } else { - VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); - secp256k1_fe_normalize_var(&ge->x); - secp256k1_fe_normalize_var(&ge->y); - secp256k1_fe_get_b32(pubkey->data, &ge->x); - secp256k1_fe_get_b32(pubkey->data + 32, &ge->y); - } + secp256k1_ge_storage s; + + STATIC_ASSERT(sizeof(secp256k1_ge_storage) == 64); + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_ge_to_storage(&s, ge); + memcpy(&pubkey->data[0], &s, 64); } int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { @@ -157,6 +269,9 @@ int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pu if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) { return 0; } + if (!secp256k1_ge_is_in_correct_subgroup(&Q)) { + return 0; + } secp256k1_pubkey_save(pubkey, &Q); secp256k1_ge_clear(&Q); return 1; @@ -169,7 +284,7 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o VERIFY_CHECK(ctx != NULL); ARG_CHECK(outputlen != NULL); - ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65)); + ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33u : 65u)); len = *outputlen; *outputlen = 0; ARG_CHECK(output != NULL); @@ -185,6 +300,60 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o return ret; } +int secp256k1_ec_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_pubkey* pubkey0, const secp256k1_pubkey* pubkey1) { + unsigned char out[2][33]; + const secp256k1_pubkey* pk[2]; + int i; + + VERIFY_CHECK(ctx != NULL); + pk[0] = pubkey0; pk[1] = pubkey1; + for (i = 0; i < 2; i++) { + size_t out_size = sizeof(out[i]); + /* If the public key is NULL or invalid, ec_pubkey_serialize will call + * the illegal_callback and return 0. In that case we will serialize the + * key as all zeros which is less than any valid public key. This + * results in consistent comparisons even if NULL or invalid pubkeys are + * involved and prevents edge cases such as sorting algorithms that use + * this function and do not terminate as a result. */ + if (!secp256k1_ec_pubkey_serialize(ctx, out[i], &out_size, pk[i], SECP256K1_EC_COMPRESSED)) { + /* Note that ec_pubkey_serialize should already set the output to + * zero in that case, but it's not guaranteed by the API, we can't + * test it and writing a VERIFY_CHECK is more complex than + * explicitly memsetting (again). */ + memset(out[i], 0, sizeof(out[i])); + } + } + return secp256k1_memcmp_var(out[0], out[1], sizeof(out[0])); +} + +static int secp256k1_ec_pubkey_sort_cmp(const void* pk1, const void* pk2, void *ctx) { + return secp256k1_ec_pubkey_cmp((secp256k1_context *)ctx, + *(secp256k1_pubkey **)pk1, + *(secp256k1_pubkey **)pk2); +} + +int secp256k1_ec_pubkey_sort(const secp256k1_context* ctx, const secp256k1_pubkey **pubkeys, size_t n_pubkeys) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkeys != NULL); + + /* Suppress wrong warning (fixed in MSVC 19.33) */ + #if defined(_MSC_VER) && (_MSC_VER < 1933) + #pragma warning(push) + #pragma warning(disable: 4090) + #endif + + /* Casting away const is fine because neither secp256k1_hsort nor + * secp256k1_ec_pubkey_sort_cmp modify the data pointed to by the cmp_data + * argument. */ + secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_ec_pubkey_sort_cmp, (void *)ctx); + + #if defined(_MSC_VER) && (_MSC_VER < 1933) + #pragma warning(pop) + #endif + + return 1; +} + static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) { (void)ctx; if (sizeof(secp256k1_scalar) == 32) { @@ -290,47 +459,53 @@ int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ return ret; } -int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) { +int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msghash32, const secp256k1_pubkey *pubkey) { secp256k1_ge q; secp256k1_scalar r, s; secp256k1_scalar m; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(msg32 != NULL); + ARG_CHECK(msghash32 != NULL); ARG_CHECK(sig != NULL); ARG_CHECK(pubkey != NULL); - secp256k1_scalar_set_b32(&m, msg32, NULL); + secp256k1_scalar_set_b32(&m, msghash32, NULL); secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); return (!secp256k1_scalar_is_high(&s) && secp256k1_pubkey_load(ctx, &q, pubkey) && - secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m)); + secp256k1_ecdsa_sig_verify(&r, &s, &q, &m)); +} + +static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *offset, const void *data, unsigned int len) { + memcpy(buf + *offset, data, len); + *offset += len; } static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { unsigned char keydata[112]; - int keylen = 64; + unsigned int offset = 0; secp256k1_rfc6979_hmac_sha256 rng; unsigned int i; + secp256k1_scalar msg; + unsigned char msgmod32[32]; + secp256k1_scalar_set_b32(&msg, msg32, NULL); + secp256k1_scalar_get_b32(msgmod32, &msg); /* We feed a byte array to the PRNG as input, consisting of: - * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. + * - the private key (32 bytes) and reduced message (32 bytes), see RFC 6979 3.2d. * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data. * - optionally 16 extra bytes with the algorithm name. * Because the arguments have distinct fixed lengths it is not possible for * different argument mixtures to emulate each other and result in the same * nonces. */ - memcpy(keydata, key32, 32); - memcpy(keydata + 32, msg32, 32); + buffer_append(keydata, &offset, key32, 32); + buffer_append(keydata, &offset, msgmod32, 32); if (data != NULL) { - memcpy(keydata + 64, data, 32); - keylen = 96; + buffer_append(keydata, &offset, data, 32); } if (algo16 != NULL) { - memcpy(keydata + keylen, algo16, 16); - keylen += 16; + buffer_append(keydata, &offset, algo16, 16); } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, offset); memset(keydata, 0, sizeof(keydata)); for (i = 0; i <= counter; i++) { secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); @@ -342,70 +517,102 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; -int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { - secp256k1_scalar r, s; +static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { secp256k1_scalar sec, non, msg; int ret = 0; - int overflow = 0; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(signature != NULL); - ARG_CHECK(seckey != NULL); + int is_sec_valid; + unsigned char nonce32[32]; + unsigned int count = 0; + /* Default initialization here is important so we won't pass uninit values to the cmov in the end */ + *r = secp256k1_scalar_zero; + *s = secp256k1_scalar_zero; + if (recid) { + *recid = 0; + } if (noncefp == NULL) { noncefp = secp256k1_nonce_function_default; } - secp256k1_scalar_set_b32(&sec, seckey, &overflow); /* Fail if the secret key is invalid. */ - if (!overflow && !secp256k1_scalar_is_zero(&sec)) { - unsigned char nonce32[32]; - unsigned int count = 0; - secp256k1_scalar_set_b32(&msg, msg32, NULL); - while (1) { - ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); - if (!ret) { + is_sec_valid = secp256k1_scalar_set_b32_seckey(&sec, seckey); + secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !is_sec_valid); + secp256k1_scalar_set_b32(&msg, msg32, NULL); + while (1) { + int is_nonce_valid; + ret = !!noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + is_nonce_valid = secp256k1_scalar_set_b32_seckey(&non, nonce32); + /* The nonce is still secret here, but it being invalid is less likely than 1:2^255. */ + secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid)); + if (is_nonce_valid) { + ret = secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, r, s, &sec, &msg, &non, recid); + /* The final signature is no longer a secret, nor is the fact that we were successful or not. */ + secp256k1_declassify(ctx, &ret, sizeof(ret)); + if (ret) { break; } - secp256k1_scalar_set_b32(&non, nonce32, &overflow); - if (!overflow && !secp256k1_scalar_is_zero(&non)) { - if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) { - break; - } - } - count++; } - memset(nonce32, 0, 32); - secp256k1_scalar_clear(&msg); - secp256k1_scalar_clear(&non); - secp256k1_scalar_clear(&sec); + count++; } - if (ret) { - secp256k1_ecdsa_signature_save(signature, &r, &s); - } else { - memset(signature, 0, sizeof(*signature)); + /* We don't want to declassify is_sec_valid and therefore the range of + * seckey. As a result is_sec_valid is included in ret only after ret was + * used as a branching variable. */ + ret &= is_sec_valid; + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&msg); + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + secp256k1_scalar_cmov(r, &secp256k1_scalar_zero, !ret); + secp256k1_scalar_cmov(s, &secp256k1_scalar_zero, !ret); + if (recid) { + const int zero = 0; + secp256k1_int_cmov(recid, &zero, !ret); } return ret; } +int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msghash32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { + secp256k1_scalar r, s; + int ret; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msghash32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + + ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msghash32, seckey, noncefp, noncedata); + secp256k1_ecdsa_signature_save(signature, &r, &s); + return ret; +} + int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey) { secp256k1_scalar sec; int ret; - int overflow; VERIFY_CHECK(ctx != NULL); ARG_CHECK(seckey != NULL); - secp256k1_scalar_set_b32(&sec, seckey, &overflow); - ret = !overflow && !secp256k1_scalar_is_zero(&sec); + ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); secp256k1_scalar_clear(&sec); return ret; } -int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { +static int secp256k1_ec_pubkey_create_helper(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar *seckey_scalar, secp256k1_ge *p, const unsigned char *seckey) { secp256k1_gej pj; + int ret; + + ret = secp256k1_scalar_set_b32_seckey(seckey_scalar, seckey); + secp256k1_scalar_cmov(seckey_scalar, &secp256k1_scalar_one, !ret); + + secp256k1_ecmult_gen(ecmult_gen_ctx, &pj, seckey_scalar); + secp256k1_ge_set_gej(p, &pj); + return ret; +} + +int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { secp256k1_ge p; - secp256k1_scalar sec; - int overflow; + secp256k1_scalar seckey_scalar; int ret = 0; VERIFY_CHECK(ctx != NULL); ARG_CHECK(pubkey != NULL); @@ -413,27 +620,31 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); ARG_CHECK(seckey != NULL); - secp256k1_scalar_set_b32(&sec, seckey, &overflow); - ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec)); - if (ret) { - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec); - secp256k1_ge_set_gej(&p, &pj); - secp256k1_pubkey_save(pubkey, &p); - } - secp256k1_scalar_clear(&sec); + ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey); + secp256k1_pubkey_save(pubkey, &p); + secp256k1_memczero(pubkey, sizeof(*pubkey), !ret); + + secp256k1_scalar_clear(&seckey_scalar); return ret; } -int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) { +int secp256k1_ec_seckey_negate(const secp256k1_context* ctx, unsigned char *seckey) { secp256k1_scalar sec; + int ret = 0; VERIFY_CHECK(ctx != NULL); ARG_CHECK(seckey != NULL); - secp256k1_scalar_set_b32(&sec, seckey, NULL); + ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); + secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret); secp256k1_scalar_negate(&sec, &sec); secp256k1_scalar_get_b32(seckey, &sec); - return 1; + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *seckey) { + return secp256k1_ec_seckey_negate(ctx, seckey); } int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *pubkey) { @@ -451,90 +662,100 @@ int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *p return ret; } -int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + +static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak32) { secp256k1_scalar term; + int overflow = 0; + int ret = 0; + + secp256k1_scalar_set_b32(&term, tweak32, &overflow); + ret = (!overflow) & secp256k1_eckey_privkey_tweak_add(sec, &term); + secp256k1_scalar_clear(&term); + return ret; +} + +int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { secp256k1_scalar sec; int ret = 0; - int overflow = 0; VERIFY_CHECK(ctx != NULL); ARG_CHECK(seckey != NULL); - ARG_CHECK(tweak != NULL); + ARG_CHECK(tweak32 != NULL); - secp256k1_scalar_set_b32(&term, tweak, &overflow); - secp256k1_scalar_set_b32(&sec, seckey, NULL); - - ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term); - memset(seckey, 0, 32); - if (ret) { - secp256k1_scalar_get_b32(seckey, &sec); - } + ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); + ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak32); + secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret); + secp256k1_scalar_get_b32(seckey, &sec); secp256k1_scalar_clear(&sec); - secp256k1_scalar_clear(&term); return ret; } -int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { - secp256k1_ge p; +int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { + return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak32); +} + +static int secp256k1_ec_pubkey_tweak_add_helper(secp256k1_ge *p, const unsigned char *tweak32) { secp256k1_scalar term; - int ret = 0; int overflow = 0; + secp256k1_scalar_set_b32(&term, tweak32, &overflow); + return !overflow && secp256k1_eckey_pubkey_tweak_add(p, &term); +} + +int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) { + secp256k1_ge p; + int ret = 0; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(pubkey != NULL); - ARG_CHECK(tweak != NULL); + ARG_CHECK(tweak32 != NULL); - secp256k1_scalar_set_b32(&term, tweak, &overflow); - ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + ret = secp256k1_pubkey_load(ctx, &p, pubkey); memset(pubkey, 0, sizeof(*pubkey)); + ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&p, tweak32); if (ret) { - if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) { - secp256k1_pubkey_save(pubkey, &p); - } else { - ret = 0; - } + secp256k1_pubkey_save(pubkey, &p); } return ret; } -int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { +int secp256k1_ec_seckey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { secp256k1_scalar factor; secp256k1_scalar sec; int ret = 0; int overflow = 0; VERIFY_CHECK(ctx != NULL); ARG_CHECK(seckey != NULL); - ARG_CHECK(tweak != NULL); + ARG_CHECK(tweak32 != NULL); - secp256k1_scalar_set_b32(&factor, tweak, &overflow); - secp256k1_scalar_set_b32(&sec, seckey, NULL); - ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor); - memset(seckey, 0, 32); - if (ret) { - secp256k1_scalar_get_b32(seckey, &sec); - } + secp256k1_scalar_set_b32(&factor, tweak32, &overflow); + ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); + ret &= (!overflow) & secp256k1_eckey_privkey_tweak_mul(&sec, &factor); + secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret); + secp256k1_scalar_get_b32(seckey, &sec); secp256k1_scalar_clear(&sec); secp256k1_scalar_clear(&factor); return ret; } -int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { +int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32) { + return secp256k1_ec_seckey_tweak_mul(ctx, seckey, tweak32); +} + +int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak32) { secp256k1_ge p; secp256k1_scalar factor; int ret = 0; int overflow = 0; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(pubkey != NULL); - ARG_CHECK(tweak != NULL); + ARG_CHECK(tweak32 != NULL); - secp256k1_scalar_set_b32(&factor, tweak, &overflow); + secp256k1_scalar_set_b32(&factor, tweak32, &overflow); ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); memset(pubkey, 0, sizeof(*pubkey)); if (ret) { - if (secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor)) { + if (secp256k1_eckey_pubkey_tweak_mul(&p, &factor)) { secp256k1_pubkey_save(pubkey, &p); } else { ret = 0; @@ -546,8 +767,11 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) { VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + ARG_CHECK(secp256k1_context_is_proper(ctx)); + + if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) { + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + } return 1; } @@ -556,6 +780,7 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * secp256k1_gej Qj; secp256k1_ge Q; + VERIFY_CHECK(ctx != NULL); ARG_CHECK(pubnonce != NULL); memset(pubnonce, 0, sizeof(*pubnonce)); ARG_CHECK(n >= 1); @@ -564,6 +789,7 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * secp256k1_gej_set_infinity(&Qj); for (i = 0; i < n; i++) { + ARG_CHECK(pubnonces[i] != NULL); secp256k1_pubkey_load(ctx, &Q, pubnonces[i]); secp256k1_gej_add_ge(&Qj, &Qj, &Q); } @@ -575,6 +801,19 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * return 1; } +int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, const unsigned char *tag, size_t taglen, const unsigned char *msg, size_t msglen) { + secp256k1_sha256 sha; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(hash32 != NULL); + ARG_CHECK(tag != NULL); + ARG_CHECK(msg != NULL); + + secp256k1_sha256_initialize_tagged(&sha, tag, taglen); + secp256k1_sha256_write(&sha, msg, msglen); + secp256k1_sha256_finalize(&sha, hash32); + return 1; +} + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/main_impl.h" #endif @@ -582,3 +821,15 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif + +#ifdef ENABLE_MODULE_EXTRAKEYS +# include "modules/extrakeys/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/main_impl.h" +#endif diff --git a/src/secp256k1/src/selftest.h b/src/secp256k1/src/selftest.h new file mode 100644 index 000000000..d083ac952 --- /dev/null +++ b/src/secp256k1/src/selftest.h @@ -0,0 +1,32 @@ +/*********************************************************************** + * Copyright (c) 2020 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_SELFTEST_H +#define SECP256K1_SELFTEST_H + +#include "hash.h" + +#include + +static int secp256k1_selftest_sha256(void) { + static const char *input63 = "For this sample, this 63-byte string will be used as input data"; + static const unsigned char output32[32] = { + 0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e, + 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42, + }; + unsigned char out[32]; + secp256k1_sha256 hasher; + secp256k1_sha256_initialize(&hasher); + secp256k1_sha256_write(&hasher, (const unsigned char*)input63, 63); + secp256k1_sha256_finalize(&hasher, out); + return secp256k1_memcmp_var(out, output32, 32) == 0; +} + +static int secp256k1_selftest_passes(void) { + return secp256k1_selftest_sha256(); +} + +#endif /* SECP256K1_SELFTEST_H */ diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h index 339cb1c7a..3c1ed3d45 100644 --- a/src/secp256k1/src/testrand.h +++ b/src/secp256k1/src/testrand.h @@ -1,38 +1,48 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_TESTRAND_H #define SECP256K1_TESTRAND_H -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif +#include "util.h" /* A non-cryptographic RNG used only for test infrastructure. */ /** Seed the pseudorandom number generator for testing. */ -SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16); +SECP256K1_INLINE static void testrand_seed(const unsigned char *seed16); /** Generate a pseudorandom number in the range [0..2**32-1]. */ -static uint32_t secp256k1_rand32(void); +SECP256K1_INLINE static uint32_t testrand32(void); + +/** Generate a pseudorandom number in the range [0..2**64-1]. */ +SECP256K1_INLINE static uint64_t testrand64(void); /** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or * more. */ -static uint32_t secp256k1_rand_bits(int bits); +SECP256K1_INLINE static uint64_t testrand_bits(int bits); /** Generate a pseudorandom number in the range [0..range-1]. */ -static uint32_t secp256k1_rand_int(uint32_t range); +static uint32_t testrand_int(uint32_t range); /** Generate a pseudorandom 32-byte array. */ -static void secp256k1_rand256(unsigned char *b32); +static void testrand256(unsigned char *b32); /** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */ -static void secp256k1_rand256_test(unsigned char *b32); +static void testrand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ -static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); +static void testrand_bytes_test(unsigned char *bytes, size_t len); + +/** Flip a single random bit in a byte array */ +static void testrand_flip(unsigned char *b, size_t len); + +/** Initialize the test RNG using (hex encoded) array up to 16 bytes, or randomly if hexseed is NULL. */ +static void testrand_init(const char* hexseed); + +/** Print final test information. */ +static void testrand_finish(void); #endif /* SECP256K1_TESTRAND_H */ diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h index 2b70623e2..07564f7f3 100644 --- a/src/secp256k1/src/testrand_impl.h +++ b/src/secp256k1/src/testrand_impl.h @@ -1,100 +1,110 @@ -/************************************************************************ - * Copyright (c) 2013-2015 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_TESTRAND_IMPL_H #define SECP256K1_TESTRAND_IMPL_H #include +#include #include #include "testrand.h" #include "hash.h" +#include "util.h" -static secp256k1_rfc6979_hmac_sha256 secp256k1_test_rng; -static uint32_t secp256k1_test_rng_precomputed[8]; -static int secp256k1_test_rng_precomputed_used = 8; -static uint64_t secp256k1_test_rng_integer; -static int secp256k1_test_rng_integer_bits_left = 0; +static uint64_t secp256k1_test_state[4]; -SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) { - secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16); -} +SECP256K1_INLINE static void testrand_seed(const unsigned char *seed16) { + static const unsigned char PREFIX[19] = "secp256k1 test init"; + unsigned char out32[32]; + secp256k1_sha256 hash; + int i; -SECP256K1_INLINE static uint32_t secp256k1_rand32(void) { - if (secp256k1_test_rng_precomputed_used == 8) { - secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, (unsigned char*)(&secp256k1_test_rng_precomputed[0]), sizeof(secp256k1_test_rng_precomputed)); - secp256k1_test_rng_precomputed_used = 0; + /* Use SHA256(PREFIX || seed16) as initial state. */ + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, PREFIX, sizeof(PREFIX)); + secp256k1_sha256_write(&hash, seed16, 16); + secp256k1_sha256_finalize(&hash, out32); + for (i = 0; i < 4; ++i) { + uint64_t s = 0; + int j; + for (j = 0; j < 8; ++j) s = (s << 8) | out32[8*i + j]; + secp256k1_test_state[i] = s; } - return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++]; } -static uint32_t secp256k1_rand_bits(int bits) { - uint32_t ret; - if (secp256k1_test_rng_integer_bits_left < bits) { - secp256k1_test_rng_integer |= (((uint64_t)secp256k1_rand32()) << secp256k1_test_rng_integer_bits_left); - secp256k1_test_rng_integer_bits_left += 32; - } - ret = secp256k1_test_rng_integer; - secp256k1_test_rng_integer >>= bits; - secp256k1_test_rng_integer_bits_left -= bits; - ret &= ((~((uint32_t)0)) >> (32 - bits)); - return ret; +SECP256K1_INLINE static uint64_t rotl(const uint64_t x, int k) { + return (x << k) | (x >> (64 - k)); } -static uint32_t secp256k1_rand_int(uint32_t range) { - /* We want a uniform integer between 0 and range-1, inclusive. - * B is the smallest number such that range <= 2**B. - * two mechanisms implemented here: - * - generate B bits numbers until one below range is found, and return it - * - find the largest multiple M of range that is <= 2**(B+A), generate B+A - * bits numbers until one below M is found, and return it modulo range - * The second mechanism consumes A more bits of entropy in every iteration, - * but may need fewer iterations due to M being closer to 2**(B+A) then - * range is to 2**B. The array below (indexed by B) contains a 0 when the - * first mechanism is to be used, and the number A otherwise. - */ - static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0}; - uint32_t trange, mult; - int bits = 0; - if (range <= 1) { - return 0; - } - trange = range - 1; - while (trange > 0) { - trange >>= 1; - bits++; - } - if (addbits[bits]) { - bits = bits + addbits[bits]; - mult = ((~((uint32_t)0)) >> (32 - bits)) / range; - trange = range * mult; - } else { - trange = range; - mult = 1; +SECP256K1_INLINE static uint64_t testrand64(void) { + /* Test-only Xoshiro256++ RNG. See https://prng.di.unimi.it/ */ + const uint64_t result = rotl(secp256k1_test_state[0] + secp256k1_test_state[3], 23) + secp256k1_test_state[0]; + const uint64_t t = secp256k1_test_state[1] << 17; + secp256k1_test_state[2] ^= secp256k1_test_state[0]; + secp256k1_test_state[3] ^= secp256k1_test_state[1]; + secp256k1_test_state[1] ^= secp256k1_test_state[2]; + secp256k1_test_state[0] ^= secp256k1_test_state[3]; + secp256k1_test_state[2] ^= t; + secp256k1_test_state[3] = rotl(secp256k1_test_state[3], 45); + return result; +} + +SECP256K1_INLINE static uint64_t testrand_bits(int bits) { + if (bits == 0) return 0; + return testrand64() >> (64 - bits); +} + +SECP256K1_INLINE static uint32_t testrand32(void) { + return testrand64() >> 32; +} + +static uint32_t testrand_int(uint32_t range) { + uint32_t mask = 0; + uint32_t range_copy; + /* Reduce range by 1, changing its meaning to "maximum value". */ + VERIFY_CHECK(range != 0); + range -= 1; + /* Count the number of bits in range. */ + range_copy = range; + while (range_copy) { + mask = (mask << 1) | 1U; + range_copy >>= 1; } - while(1) { - uint32_t x = secp256k1_rand_bits(bits); - if (x < trange) { - return (mult == 1) ? x : (x % range); - } + /* Generation loop. */ + while (1) { + uint32_t val = testrand64() & mask; + if (val <= range) return val; } } -static void secp256k1_rand256(unsigned char *b32) { - secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32); +static void testrand256(unsigned char *b32) { + int i; + for (i = 0; i < 4; ++i) { + uint64_t val = testrand64(); + b32[0] = val; + b32[1] = val >> 8; + b32[2] = val >> 16; + b32[3] = val >> 24; + b32[4] = val >> 32; + b32[5] = val >> 40; + b32[6] = val >> 48; + b32[7] = val >> 56; + b32 += 8; + } } -static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) { +static void testrand_bytes_test(unsigned char *bytes, size_t len) { size_t bits = 0; memset(bytes, 0, len); while (bits < len * 8) { int now; uint32_t val; - now = 1 + (secp256k1_rand_bits(6) * secp256k1_rand_bits(5) + 16) / 31; - val = secp256k1_rand_bits(1); + now = 1 + (testrand_bits(6) * testrand_bits(5) + 16) / 31; + val = testrand_bits(1); while (now > 0 && bits < len * 8) { bytes[bits / 8] |= val << (bits % 8); now--; @@ -103,8 +113,55 @@ static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) { } } -static void secp256k1_rand256_test(unsigned char *b32) { - secp256k1_rand_bytes_test(b32, 32); +static void testrand256_test(unsigned char *b32) { + testrand_bytes_test(b32, 32); +} + +static void testrand_flip(unsigned char *b, size_t len) { + b[testrand_int(len)] ^= (1 << testrand_bits(3)); +} + +static void testrand_init(const char* hexseed) { + unsigned char seed16[16] = {0}; + if (hexseed && strlen(hexseed) != 0) { + int pos = 0; + while (pos < 16 && hexseed[0] != 0 && hexseed[1] != 0) { + unsigned short sh; + if ((sscanf(hexseed, "%2hx", &sh)) == 1) { + seed16[pos] = sh; + } else { + break; + } + hexseed += 2; + pos++; + } + } else { + FILE *frand = fopen("/dev/urandom", "rb"); + if ((frand == NULL) || fread(&seed16, 1, sizeof(seed16), frand) != sizeof(seed16)) { + uint64_t t = time(NULL) * (uint64_t)1337; + fprintf(stderr, "WARNING: could not read 16 bytes from /dev/urandom; falling back to insecure PRNG\n"); + seed16[0] ^= t; + seed16[1] ^= t >> 8; + seed16[2] ^= t >> 16; + seed16[3] ^= t >> 24; + seed16[4] ^= t >> 32; + seed16[5] ^= t >> 40; + seed16[6] ^= t >> 48; + seed16[7] ^= t >> 56; + } + if (frand) { + fclose(frand); + } + } + + printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); + testrand_seed(seed16); +} + +static void testrand_finish(void) { + unsigned char run32[32]; + testrand256(run32); + printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); } #endif /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c index 743ca6cca..70c15f870 100644 --- a/src/secp256k1/src/tests.c +++ b/src/secp256k1/src/tests.c @@ -1,12 +1,8 @@ -/************************************************************************ - * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif +/*********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #include #include @@ -14,39 +10,80 @@ #include +#ifdef USE_EXTERNAL_DEFAULT_CALLBACKS + #pragma message("Ignoring USE_EXTERNAL_CALLBACKS in tests.") + #undef USE_EXTERNAL_DEFAULT_CALLBACKS +#endif +#if defined(VERIFY) && defined(COVERAGE) + #pragma message("Defining VERIFY for tests being built for coverage analysis support is meaningless.") +#endif #include "secp256k1.c" -#include "include/secp256k1.h" + +#include "../include/secp256k1.h" +#include "../include/secp256k1_preallocated.h" #include "testrand_impl.h" +#include "checkmem.h" +#include "testutil.h" +#include "util.h" -#ifdef ENABLE_OPENSSL_TESTS -#include "openssl/bn.h" -#include "openssl/ec.h" -#include "openssl/ecdsa.h" -#include "openssl/obj_mac.h" -#endif +#include "../contrib/lax_der_parsing.c" +#include "../contrib/lax_der_privatekey_parsing.c" -#include "contrib/lax_der_parsing.c" -#include "contrib/lax_der_privatekey_parsing.c" - -#if !defined(VG_CHECK) -# if defined(VALGRIND) -# include -# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y)) -# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y)) -# else -# define VG_UNDEF(x,y) -# define VG_CHECK(x,y) -# endif +#include "modinv32_impl.h" +#ifdef SECP256K1_WIDEMUL_INT128 +#include "modinv64_impl.h" +#include "int128_impl.h" #endif -static int count = 64; -static secp256k1_context *ctx = NULL; +#define CONDITIONAL_TEST(cnt, nam) if (COUNT < (cnt)) { printf("Skipping %s (iteration count too low)\n", nam); } else + +static int COUNT = 64; +static secp256k1_context *CTX = NULL; +static secp256k1_context *STATIC_CTX = NULL; -static void counting_illegal_callback_fn(const char* str, void* data) { +static int all_bytes_equal(const void* s, unsigned char value, size_t n) { + const unsigned char *p = s; + size_t i; + + for (i = 0; i < n; i++) { + if (p[i] != value) { + return 0; + } + } + return 1; +} + +#define CHECK_COUNTING_CALLBACK_VOID(ctx, expr_or_stmt, callback, callback_setter) do { \ + int32_t _calls_to_callback = 0; \ + secp256k1_callback _saved_callback = ctx->callback; \ + callback_setter(ctx, counting_callback_fn, &_calls_to_callback); \ + { expr_or_stmt; } \ + ctx->callback = _saved_callback; \ + CHECK(_calls_to_callback == 1); \ +} while(0); + +/* CHECK that expr_or_stmt calls the error or illegal callback of ctx exactly once + * + * Useful for checking functions that return void (e.g., API functions that use ARG_CHECK_VOID) */ +#define CHECK_ERROR_VOID(ctx, expr_or_stmt) \ + CHECK_COUNTING_CALLBACK_VOID(ctx, expr_or_stmt, error_callback, secp256k1_context_set_error_callback) +#define CHECK_ILLEGAL_VOID(ctx, expr_or_stmt) \ + CHECK_COUNTING_CALLBACK_VOID(ctx, expr_or_stmt, illegal_callback, secp256k1_context_set_illegal_callback) + +/* CHECK that + * - expr calls the illegal callback of ctx exactly once and, + * - expr == 0 (or equivalently, expr == NULL) + * + * Useful for checking functions that return an integer or a pointer. */ +#define CHECK_ILLEGAL(ctx, expr) CHECK_ILLEGAL_VOID(ctx, CHECK((expr) == 0)) +#define CHECK_ERROR(ctx, expr) CHECK_ERROR_VOID(ctx, CHECK((expr) == 0)) + +static void counting_callback_fn(const char* str, void* data) { /* Dummy callback function that just counts. */ int32_t *p; (void)str; p = data; + CHECK(*p != INT32_MAX); (*p)++; } @@ -55,209 +92,375 @@ static void uncounting_illegal_callback_fn(const char* str, void* data) { int32_t *p; (void)str; p = data; + CHECK(*p != INT32_MIN); (*p)--; } -void random_field_element_test(secp256k1_fe *fe) { - do { - unsigned char b32[32]; - secp256k1_rand256_test(b32); - if (secp256k1_fe_set_b32(fe, b32)) { - break; +static void run_xoshiro256pp_tests(void) { + { + size_t i; + /* Sanity check that we run before the actual seeding. */ + for (i = 0; i < sizeof(secp256k1_test_state)/sizeof(secp256k1_test_state[0]); i++) { + CHECK(secp256k1_test_state[i] == 0); } - } while(1); -} - -void random_field_element_magnitude(secp256k1_fe *fe) { - secp256k1_fe zero; - int n = secp256k1_rand_int(9); - secp256k1_fe_normalize(fe); - if (n == 0) { - return; } - secp256k1_fe_clear(&zero); - secp256k1_fe_negate(&zero, &zero, 0); - secp256k1_fe_mul_int(&zero, n - 1); - secp256k1_fe_add(fe, &zero); - VERIFY_CHECK(fe->magnitude == n); + { + int i; + unsigned char buf32[32]; + unsigned char seed16[16] = { + 'C', 'H', 'I', 'C', 'K', 'E', 'N', '!', + 'C', 'H', 'I', 'C', 'K', 'E', 'N', '!', + }; + unsigned char buf32_expected[32] = { + 0xAF, 0xCC, 0xA9, 0x16, 0xB5, 0x6C, 0xE3, 0xF0, + 0x44, 0x3F, 0x45, 0xE0, 0x47, 0xA5, 0x08, 0x36, + 0x4C, 0xCC, 0xC1, 0x18, 0xB2, 0xD8, 0x8F, 0xEF, + 0x43, 0x26, 0x15, 0x57, 0x37, 0x00, 0xEF, 0x30, + }; + testrand_seed(seed16); + for (i = 0; i < 17; i++) { + testrand256(buf32); + } + CHECK(secp256k1_memcmp_var(buf32, buf32_expected, sizeof(buf32)) == 0); + } } -void random_group_element_test(secp256k1_ge *ge) { - secp256k1_fe fe; - do { - random_field_element_test(&fe); - if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_rand_bits(1))) { - secp256k1_fe_normalize(&ge->y); - break; - } - } while(1); +static void run_selftest_tests(void) { + /* Test public API */ + secp256k1_selftest(); } -void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { - secp256k1_fe z2, z3; - do { - random_field_element_test(&gej->z); - if (!secp256k1_fe_is_zero(&gej->z)) { - break; - } - } while(1); - secp256k1_fe_sqr(&z2, &gej->z); - secp256k1_fe_mul(&z3, &z2, &gej->z); - secp256k1_fe_mul(&gej->x, &ge->x, &z2); - secp256k1_fe_mul(&gej->y, &ge->y, &z3); - gej->infinity = ge->infinity; +static int ecmult_gen_context_eq(const secp256k1_ecmult_gen_context *a, const secp256k1_ecmult_gen_context *b) { + return a->built == b->built + && secp256k1_scalar_eq(&a->scalar_offset, &b->scalar_offset) + && secp256k1_ge_eq_var(&a->ge_offset, &b->ge_offset) + && secp256k1_fe_equal(&a->proj_blind, &b->proj_blind); } -void random_scalar_order_test(secp256k1_scalar *num) { - do { - unsigned char b32[32]; - int overflow = 0; - secp256k1_rand256_test(b32); - secp256k1_scalar_set_b32(num, b32, &overflow); - if (overflow || secp256k1_scalar_is_zero(num)) { - continue; - } - break; - } while(1); +static int context_eq(const secp256k1_context *a, const secp256k1_context *b) { + return a->declassify == b->declassify + && ecmult_gen_context_eq(&a->ecmult_gen_ctx, &b->ecmult_gen_ctx) + && a->illegal_callback.fn == b->illegal_callback.fn + && a->illegal_callback.data == b->illegal_callback.data + && a->error_callback.fn == b->error_callback.fn + && a->error_callback.data == b->error_callback.data; } -void random_scalar_order(secp256k1_scalar *num) { - do { - unsigned char b32[32]; - int overflow = 0; - secp256k1_rand256(b32); - secp256k1_scalar_set_b32(num, b32, &overflow); - if (overflow || secp256k1_scalar_is_zero(num)) { - continue; - } - break; - } while(1); +static void run_deprecated_context_flags_test(void) { + /* Check that a context created with any of the flags in the flags array is + * identical to the NONE context. */ + unsigned int flags[] = { SECP256K1_CONTEXT_SIGN, + SECP256K1_CONTEXT_VERIFY, + SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY }; + secp256k1_context *none_ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + int i; + for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++) { + secp256k1_context *tmp_ctx; + CHECK(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE) == secp256k1_context_preallocated_size(flags[i])); + tmp_ctx = secp256k1_context_create(flags[i]); + CHECK(context_eq(none_ctx, tmp_ctx)); + secp256k1_context_destroy(tmp_ctx); + } + secp256k1_context_destroy(none_ctx); } -void run_context_tests(void) { +static void run_ec_illegal_argument_tests(void) { secp256k1_pubkey pubkey; secp256k1_pubkey zero_pubkey; secp256k1_ecdsa_signature sig; unsigned char ctmp[32]; - int32_t ecount; - int32_t ecount2; - secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + /* Setup */ + memset(ctmp, 1, 32); + memset(&zero_pubkey, 0, sizeof(zero_pubkey)); + + /* Verify context-type checking illegal-argument errors. */ + CHECK_ILLEGAL(STATIC_CTX, secp256k1_ec_pubkey_create(STATIC_CTX, &pubkey, ctmp)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_ecdsa_sign(STATIC_CTX, &sig, ctmp, ctmp, NULL, NULL)); + SECP256K1_CHECKMEM_UNDEFINE(&sig, sizeof(sig)); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, ctmp, ctmp, NULL, NULL) == 1); + SECP256K1_CHECKMEM_CHECK(&sig, sizeof(sig)); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, ctmp, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(STATIC_CTX, &sig, ctmp, &pubkey) == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp) == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(STATIC_CTX, &pubkey, ctmp) == 1); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, ctmp) == 1); + CHECK(secp256k1_ec_pubkey_negate(STATIC_CTX, &pubkey) == 1); + CHECK(secp256k1_ec_pubkey_negate(CTX, &pubkey) == 1); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_ec_pubkey_negate(STATIC_CTX, &zero_pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_negate(CTX, NULL)); + CHECK(secp256k1_ec_pubkey_tweak_mul(STATIC_CTX, &pubkey, ctmp) == 1); +} + +static void run_static_context_tests(int use_prealloc) { + /* Check that deprecated secp256k1_context_no_precomp is an alias to secp256k1_context_static. */ + CHECK(secp256k1_context_no_precomp == secp256k1_context_static); + + { + unsigned char seed[32] = {0x17}; + + /* Randomizing secp256k1_context_static is not supported. */ + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_randomize(STATIC_CTX, seed)); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_randomize(STATIC_CTX, NULL)); + + /* Destroying or cloning secp256k1_context_static is not supported. */ + if (use_prealloc) { + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_preallocated_clone_size(STATIC_CTX)); + { + secp256k1_context *my_static_ctx = malloc(sizeof(*STATIC_CTX)); + CHECK(my_static_ctx != NULL); + memset(my_static_ctx, 0x2a, sizeof(*my_static_ctx)); + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_preallocated_clone(STATIC_CTX, my_static_ctx)); + CHECK(all_bytes_equal(my_static_ctx, 0x2a, sizeof(*my_static_ctx))); + free(my_static_ctx); + } + CHECK_ILLEGAL_VOID(STATIC_CTX, secp256k1_context_preallocated_destroy(STATIC_CTX)); + } else { + CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_clone(STATIC_CTX)); + CHECK_ILLEGAL_VOID(STATIC_CTX, secp256k1_context_destroy(STATIC_CTX)); + } + } + + { + /* Verify that setting and resetting illegal callback works */ + int32_t dummy = 0; + secp256k1_context_set_illegal_callback(STATIC_CTX, counting_callback_fn, &dummy); + CHECK(STATIC_CTX->illegal_callback.fn == counting_callback_fn); + CHECK(STATIC_CTX->illegal_callback.data == &dummy); + secp256k1_context_set_illegal_callback(STATIC_CTX, NULL, NULL); + CHECK(STATIC_CTX->illegal_callback.fn == secp256k1_default_illegal_callback_fn); + CHECK(STATIC_CTX->illegal_callback.data == NULL); + } +} + +static void run_proper_context_tests(int use_prealloc) { + int32_t dummy = 0; + secp256k1_context *my_ctx, *my_ctx_fresh; + void *my_ctx_prealloc = NULL; + unsigned char seed[32] = {0x17}; secp256k1_gej pubj; secp256k1_ge pub; secp256k1_scalar msg, key, nonce; secp256k1_scalar sigr, sigs; - memset(&zero_pubkey, 0, sizeof(zero_pubkey)); + /* Fresh reference context for comparison */ + my_ctx_fresh = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + + if (use_prealloc) { + my_ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); + CHECK(my_ctx_prealloc != NULL); + my_ctx = secp256k1_context_preallocated_create(my_ctx_prealloc, SECP256K1_CONTEXT_NONE); + } else { + my_ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + } - ecount = 0; - ecount2 = 10; - secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount2); - secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL); - CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + /* Randomize and reset randomization */ + CHECK(context_eq(my_ctx, my_ctx_fresh)); + CHECK(secp256k1_context_randomize(my_ctx, seed) == 1); + CHECK(!context_eq(my_ctx, my_ctx_fresh)); + CHECK(secp256k1_context_randomize(my_ctx, NULL) == 1); + CHECK(context_eq(my_ctx, my_ctx_fresh)); + + /* set error callback (to a function that still aborts in case malloc() fails in secp256k1_context_clone() below) */ + secp256k1_context_set_error_callback(my_ctx, secp256k1_default_illegal_callback_fn, NULL); + CHECK(my_ctx->error_callback.fn != secp256k1_default_error_callback_fn); + CHECK(my_ctx->error_callback.fn == secp256k1_default_illegal_callback_fn); + + /* check if sizes for cloning are consistent */ + CHECK(secp256k1_context_preallocated_clone_size(my_ctx) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); /*** clone and destroy all of them to make sure cloning was complete ***/ { secp256k1_context *ctx_tmp; - ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_destroy(ctx_tmp); - ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_destroy(ctx_tmp); - ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_destroy(ctx_tmp); - ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp); + if (use_prealloc) { + /* clone into a non-preallocated context and then again into a new preallocated one. */ + ctx_tmp = my_ctx; + my_ctx = secp256k1_context_clone(my_ctx); + CHECK(context_eq(ctx_tmp, my_ctx)); + secp256k1_context_preallocated_destroy(ctx_tmp); + + free(my_ctx_prealloc); + my_ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); + CHECK(my_ctx_prealloc != NULL); + ctx_tmp = my_ctx; + my_ctx = secp256k1_context_preallocated_clone(my_ctx, my_ctx_prealloc); + CHECK(context_eq(ctx_tmp, my_ctx)); + secp256k1_context_destroy(ctx_tmp); + } else { + /* clone into a preallocated context and then again into a new non-preallocated one. */ + void *prealloc_tmp; + + prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); + CHECK(prealloc_tmp != NULL); + ctx_tmp = my_ctx; + my_ctx = secp256k1_context_preallocated_clone(my_ctx, prealloc_tmp); + CHECK(context_eq(ctx_tmp, my_ctx)); + secp256k1_context_destroy(ctx_tmp); + + ctx_tmp = my_ctx; + my_ctx = secp256k1_context_clone(my_ctx); + CHECK(context_eq(ctx_tmp, my_ctx)); + secp256k1_context_preallocated_destroy(ctx_tmp); + free(prealloc_tmp); + } } /* Verify that the error callback makes it across the clone. */ - CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + CHECK(my_ctx->error_callback.fn != secp256k1_default_error_callback_fn); + CHECK(my_ctx->error_callback.fn == secp256k1_default_illegal_callback_fn); /* And that it resets back to default. */ - secp256k1_context_set_error_callback(sign, NULL, NULL); - CHECK(vrfy->error_callback.fn == sign->error_callback.fn); + secp256k1_context_set_error_callback(my_ctx, NULL, NULL); + CHECK(my_ctx->error_callback.fn == secp256k1_default_error_callback_fn); + CHECK(context_eq(my_ctx, my_ctx_fresh)); + + /* Verify that setting and resetting illegal callback works */ + secp256k1_context_set_illegal_callback(my_ctx, counting_callback_fn, &dummy); + CHECK(my_ctx->illegal_callback.fn == counting_callback_fn); + CHECK(my_ctx->illegal_callback.data == &dummy); + secp256k1_context_set_illegal_callback(my_ctx, NULL, NULL); + CHECK(my_ctx->illegal_callback.fn == secp256k1_default_illegal_callback_fn); + CHECK(my_ctx->illegal_callback.data == NULL); + CHECK(context_eq(my_ctx, my_ctx_fresh)); /*** attempt to use them ***/ - random_scalar_order_test(&msg); - random_scalar_order_test(&key); - secp256k1_ecmult_gen(&both->ecmult_gen_ctx, &pubj, &key); + testutil_random_scalar_order_test(&msg); + testutil_random_scalar_order_test(&key); + secp256k1_ecmult_gen(&my_ctx->ecmult_gen_ctx, &pubj, &key); secp256k1_ge_set_gej(&pub, &pubj); - /* Verify context-type checking illegal-argument errors. */ - memset(ctmp, 1, 32); - CHECK(secp256k1_ec_pubkey_create(vrfy, &pubkey, ctmp) == 0); - CHECK(ecount == 1); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(sign, &pubkey, ctmp) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ecdsa_sign(vrfy, &sig, ctmp, ctmp, NULL, NULL) == 0); - CHECK(ecount == 2); - VG_UNDEF(&sig, sizeof(sig)); - CHECK(secp256k1_ecdsa_sign(sign, &sig, ctmp, ctmp, NULL, NULL) == 1); - VG_CHECK(&sig, sizeof(sig)); - CHECK(ecount2 == 10); - CHECK(secp256k1_ecdsa_verify(sign, &sig, ctmp, &pubkey) == 0); - CHECK(ecount2 == 11); - CHECK(secp256k1_ecdsa_verify(vrfy, &sig, ctmp, &pubkey) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_tweak_add(sign, &pubkey, ctmp) == 0); - CHECK(ecount2 == 12); - CHECK(secp256k1_ec_pubkey_tweak_add(vrfy, &pubkey, ctmp) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_tweak_mul(sign, &pubkey, ctmp) == 0); - CHECK(ecount2 == 13); - CHECK(secp256k1_ec_pubkey_negate(vrfy, &pubkey) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_negate(sign, &pubkey) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_ec_pubkey_negate(sign, NULL) == 0); - CHECK(ecount2 == 14); - CHECK(secp256k1_ec_pubkey_negate(vrfy, &zero_pubkey) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_context_randomize(vrfy, ctmp) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_context_randomize(sign, NULL) == 1); - CHECK(ecount2 == 14); - secp256k1_context_set_illegal_callback(vrfy, NULL, NULL); - secp256k1_context_set_illegal_callback(sign, NULL, NULL); - - /* This shouldn't leak memory, due to already-set tests. */ - secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL); - secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL); - /* obtain a working nonce */ do { - random_scalar_order_test(&nonce); - } while(!secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + testutil_random_scalar_order_test(&nonce); + } while(!secp256k1_ecdsa_sig_sign(&my_ctx->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); /* try signing */ - CHECK(secp256k1_ecdsa_sig_sign(&sign->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); - CHECK(secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + CHECK(secp256k1_ecdsa_sig_sign(&my_ctx->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); /* try verifying */ - CHECK(secp256k1_ecdsa_sig_verify(&vrfy->ecmult_ctx, &sigr, &sigs, &pub, &msg)); - CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + CHECK(secp256k1_ecdsa_sig_verify(&sigr, &sigs, &pub, &msg)); /* cleanup */ - secp256k1_context_destroy(none); - secp256k1_context_destroy(sign); - secp256k1_context_destroy(vrfy); - secp256k1_context_destroy(both); + if (use_prealloc) { + secp256k1_context_preallocated_destroy(my_ctx); + free(my_ctx_prealloc); + } else { + secp256k1_context_destroy(my_ctx); + } + secp256k1_context_destroy(my_ctx_fresh); + /* Defined as no-op. */ secp256k1_context_destroy(NULL); + secp256k1_context_preallocated_destroy(NULL); +} + +static void run_scratch_tests(void) { + const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + + size_t checkpoint; + size_t checkpoint_2; + secp256k1_scratch_space *scratch; + secp256k1_scratch_space local_scratch; + + /* Test public API */ + scratch = secp256k1_scratch_space_create(CTX, 1000); + CHECK(scratch != NULL); + + /* Test internal API */ + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0) == 1000); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1)); + CHECK(scratch->alloc_size == 0); + CHECK(scratch->alloc_size % ALIGNMENT == 0); + + /* Allocating 500 bytes succeeds */ + checkpoint = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 500) != NULL); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0) == 1000 - adj_alloc); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1)); + CHECK(scratch->alloc_size != 0); + CHECK(scratch->alloc_size % ALIGNMENT == 0); + + /* Allocating another 501 bytes fails */ + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 501) == NULL); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0) == 1000 - adj_alloc); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1)); + CHECK(scratch->alloc_size != 0); + CHECK(scratch->alloc_size % ALIGNMENT == 0); + + /* ...but it succeeds once we apply the checkpoint to undo it */ + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint); + CHECK(scratch->alloc_size == 0); + CHECK(secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0) == 1000); + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 500) != NULL); + CHECK(scratch->alloc_size != 0); + + /* try to apply a bad checkpoint */ + checkpoint_2 = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch); + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint); + CHECK_ERROR_VOID(CTX, secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint_2)); /* checkpoint_2 is after checkpoint */ + CHECK_ERROR_VOID(CTX, secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, (size_t) -1)); /* this is just wildly invalid */ + + /* try to use badly initialized scratch space */ + secp256k1_scratch_space_destroy(CTX, scratch); + memset(&local_scratch, 0, sizeof(local_scratch)); + scratch = &local_scratch; + CHECK_ERROR(CTX, secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, 0)); + CHECK_ERROR(CTX, secp256k1_scratch_alloc(&CTX->error_callback, scratch, 500)); + CHECK_ERROR_VOID(CTX, secp256k1_scratch_space_destroy(CTX, scratch)); + + /* Test that large integers do not wrap around in a bad way */ + scratch = secp256k1_scratch_space_create(CTX, 1000); + /* Try max allocation with a large number of objects. Only makes sense if + * ALIGNMENT is greater than 1 because otherwise the objects take no extra + * space. */ + CHECK(ALIGNMENT <= 1 || !secp256k1_scratch_max_allocation(&CTX->error_callback, scratch, (SIZE_MAX / (ALIGNMENT - 1)) + 1)); + /* Try allocating SIZE_MAX to test wrap around which only happens if + * ALIGNMENT > 1, otherwise it returns NULL anyway because the scratch + * space is too small. */ + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, SIZE_MAX) == NULL); + secp256k1_scratch_space_destroy(CTX, scratch); + + /* cleanup */ + secp256k1_scratch_space_destroy(CTX, NULL); /* no-op */ +} + +static void run_ctz_tests(void) { + static const uint32_t b32[] = {1, 0xffffffff, 0x5e56968f, 0xe0d63129}; + static const uint64_t b64[] = {1, 0xffffffffffffffff, 0xbcd02462139b3fc3, 0x98b5f80c769693ef}; + int shift; + unsigned i; + for (i = 0; i < sizeof(b32) / sizeof(b32[0]); ++i) { + for (shift = 0; shift < 32; ++shift) { + CHECK(secp256k1_ctz32_var_debruijn(b32[i] << shift) == shift); + CHECK(secp256k1_ctz32_var(b32[i] << shift) == shift); + } + } + for (i = 0; i < sizeof(b64) / sizeof(b64[0]); ++i) { + for (shift = 0; shift < 64; ++shift) { + CHECK(secp256k1_ctz64_var_debruijn(b64[i] << shift) == shift); + CHECK(secp256k1_ctz64_var(b64[i] << shift) == shift); + } + } } /***** HASH TESTS *****/ -void run_sha256_tests(void) { - static const char *inputs[8] = { +static void run_sha256_known_output_tests(void) { + static const char *inputs[] = { "", "abc", "message digest", "secure hash algorithm", "SHA256 is considered to be safe", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "For this sample, this 63-byte string will be used as input data", - "This is exactly 64 bytes long, not counting the terminating byte" + "This is exactly 64 bytes long, not counting the terminating byte", + "aaaaa", + }; + static const unsigned int repeat[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1000000/5 }; - static const unsigned char outputs[8][32] = { + static const unsigned char outputs[][32] = { {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}, {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}, {0xf7, 0x84, 0x6f, 0x55, 0xcf, 0x23, 0xe1, 0x4e, 0xeb, 0xea, 0xb5, 0xb4, 0xe1, 0x55, 0x0c, 0xad, 0x5b, 0x50, 0x9e, 0x33, 0x48, 0xfb, 0xc4, 0xef, 0xa3, 0xa1, 0x41, 0x3d, 0x39, 0x3c, 0xb6, 0x50}, @@ -265,28 +468,158 @@ void run_sha256_tests(void) { {0x68, 0x19, 0xd9, 0x15, 0xc7, 0x3f, 0x4d, 0x1e, 0x77, 0xe4, 0xe1, 0xb5, 0x2d, 0x1f, 0xa0, 0xf9, 0xcf, 0x9b, 0xea, 0xea, 0xd3, 0x93, 0x9f, 0x15, 0x87, 0x4b, 0xd9, 0x88, 0xe2, 0xa2, 0x36, 0x30}, {0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}, {0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e, 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42}, - {0xab, 0x64, 0xef, 0xf7, 0xe8, 0x8e, 0x2e, 0x46, 0x16, 0x5e, 0x29, 0xf2, 0xbc, 0xe4, 0x18, 0x26, 0xbd, 0x4c, 0x7b, 0x35, 0x52, 0xf6, 0xb3, 0x82, 0xa9, 0xe7, 0xd3, 0xaf, 0x47, 0xc2, 0x45, 0xf8} + {0xab, 0x64, 0xef, 0xf7, 0xe8, 0x8e, 0x2e, 0x46, 0x16, 0x5e, 0x29, 0xf2, 0xbc, 0xe4, 0x18, 0x26, 0xbd, 0x4c, 0x7b, 0x35, 0x52, 0xf6, 0xb3, 0x82, 0xa9, 0xe7, 0xd3, 0xaf, 0x47, 0xc2, 0x45, 0xf8}, + {0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92, 0x81, 0xa1, 0xc7, 0xe2, 0x84, 0xd7, 0x3e, 0x67, 0xf1, 0x80, 0x9a, 0x48, 0xa4, 0x97, 0x20, 0x0e, 0x04, 0x6d, 0x39, 0xcc, 0xc7, 0x11, 0x2c, 0xd0}, }; - int i; - for (i = 0; i < 8; i++) { + unsigned int i, ninputs; + + /* Skip last input vector for low iteration counts */ + ninputs = sizeof(inputs)/sizeof(inputs[0]) - 1; + CONDITIONAL_TEST(16, "run_sha256_known_output_tests 1000000") ninputs++; + + for (i = 0; i < ninputs; i++) { unsigned char out[32]; secp256k1_sha256 hasher; + unsigned int j; + /* 1. Run: simply write the input bytestrings */ + j = repeat[i]; secp256k1_sha256_initialize(&hasher); - secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + while (j > 0) { + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + j--; + } secp256k1_sha256_finalize(&hasher, out); - CHECK(memcmp(out, outputs[i], 32) == 0); + CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); + /* 2. Run: split the input bytestrings randomly before writing */ if (strlen(inputs[i]) > 0) { - int split = secp256k1_rand_int(strlen(inputs[i])); + int split = testrand_int(strlen(inputs[i])); secp256k1_sha256_initialize(&hasher); - secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); - secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + j = repeat[i]; + while (j > 0) { + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + j--; + } secp256k1_sha256_finalize(&hasher, out); - CHECK(memcmp(out, outputs[i], 32) == 0); + CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); } } } -void run_hmac_sha256_tests(void) { +/** SHA256 counter tests + +The tests verify that the SHA256 counter doesn't wrap around at message length +2^i bytes for i = 20, ..., 33. This wide range aims at being independent of the +implementation of the counter and it catches multiple natural 32-bit overflows +(e.g., counting bits, counting bytes, counting blocks, ...). + +The test vectors have been generated using following Python script which relies +on https://github.com/cloudtools/sha256/ (v0.3 on Python v3.10.2). + +``` +from sha256 import sha256 +from copy import copy + +def midstate_c_definition(hasher): + ret = ' {{0x' + hasher.state[0].hex('_', 4).replace('_', ', 0x') + '},\n' + ret += ' {0x00}, ' + str(hex(hasher.state[1])) + '}' + return ret + +def output_c_literal(hasher): + return '{0x' + hasher.digest().hex('_').replace('_', ', 0x') + '}' + +MESSAGE = b'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno' +assert(len(MESSAGE) == 64) +BYTE_BOUNDARIES = [(2**b)//len(MESSAGE) - 1 for b in range(20, 34)] + +midstates = [] +digests = [] +hasher = sha256() +for i in range(BYTE_BOUNDARIES[-1] + 1): + if i in BYTE_BOUNDARIES: + midstates.append(midstate_c_definition(hasher)) + hasher_copy = copy(hasher) + hasher_copy.update(MESSAGE) + digests.append(output_c_literal(hasher_copy)) + hasher.update(MESSAGE) + +for x in midstates: + print(x + ',') + +for x in digests: + print(x + ',') +``` +*/ +static void run_sha256_counter_tests(void) { + static const char *input = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"; + static const secp256k1_sha256 midstates[] = { + {{0xa2b5c8bb, 0x26c88bb3, 0x2abdc3d2, 0x9def99a3, 0xdfd21a6e, 0x41fe585b, 0x7ef2c440, 0x2b79adda}, + {0x00}, 0xfffc0}, + {{0xa0d29445, 0x9287de66, 0x76aabd71, 0x41acd765, 0x0c7528b4, 0x84e14906, 0x942faec6, 0xcc5a7b26}, + {0x00}, 0x1fffc0}, + {{0x50449526, 0xb9f1d657, 0xa0fc13e9, 0x50860f10, 0xa550c431, 0x3fbc97c1, 0x7bbb2d89, 0xdb67bac1}, + {0x00}, 0x3fffc0}, + {{0x54a6efdc, 0x46762e7b, 0x88bfe73f, 0xbbd149c7, 0x41620c43, 0x1168da7b, 0x2c5960f9, 0xeccffda6}, + {0x00}, 0x7fffc0}, + {{0x2515a8f5, 0x5faa2977, 0x3a850486, 0xac858cad, 0x7b7276ee, 0x235c0385, 0xc53a157c, 0x7cb3e69c}, + {0x00}, 0xffffc0}, + {{0x34f39828, 0x409fedb7, 0x4bbdd0fb, 0x3b643634, 0x7806bf2e, 0xe0d1b713, 0xca3f2e1e, 0xe38722c2}, + {0x00}, 0x1ffffc0}, + {{0x389ef5c5, 0x38c54167, 0x8f5d56ab, 0x582a75cc, 0x8217caef, 0xf10947dd, 0x6a1998a8, 0x048f0b8c}, + {0x00}, 0x3ffffc0}, + {{0xd6c3f394, 0x0bee43b9, 0x6783f497, 0x29fa9e21, 0x6ce491c1, 0xa81fe45e, 0x2fc3859a, 0x269012d0}, + {0x00}, 0x7ffffc0}, + {{0x6dd3c526, 0x44d88aa0, 0x806a1bae, 0xfbcc0d32, 0x9d6144f3, 0x9d2bd757, 0x9851a957, 0xb50430ad}, + {0x00}, 0xfffffc0}, + {{0x2add4021, 0xdfe8a9e6, 0xa56317c6, 0x7a15f5bb, 0x4a48aacd, 0x5d368414, 0x4f00e6f0, 0xd9355023}, + {0x00}, 0x1fffffc0}, + {{0xb66666b4, 0xdbeac32b, 0x0ea351ae, 0xcba9da46, 0x6278b874, 0x8c508e23, 0xe16ca776, 0x8465bac1}, + {0x00}, 0x3fffffc0}, + {{0xb6744789, 0x9cce87aa, 0xc4c478b7, 0xf38404d8, 0x2e38ba62, 0xa3f7019b, 0x50458fe7, 0x3047dbec}, + {0x00}, 0x7fffffc0}, + {{0x8b1297ba, 0xba261a80, 0x2ba1b0dd, 0xfbc67d6d, 0x61072c4e, 0x4b5a2a0f, 0x52872760, 0x2dfeb162}, + {0x00}, 0xffffffc0}, + {{0x24f33cf7, 0x41ad6583, 0x41c8ff5d, 0xca7ef35f, 0x50395756, 0x021b743e, 0xd7126cd7, 0xd037473a}, + {0x00}, 0x1ffffffc0}, + }; + static const unsigned char outputs[][32] = { + {0x0e, 0x83, 0xe2, 0xc9, 0x4f, 0xb2, 0xb8, 0x2b, 0x89, 0x06, 0x92, 0x78, 0x04, 0x03, 0x48, 0x5c, 0x48, 0x44, 0x67, 0x61, 0x77, 0xa4, 0xc7, 0x90, 0x9e, 0x92, 0x55, 0x10, 0x05, 0xfe, 0x39, 0x15}, + {0x1d, 0x1e, 0xd7, 0xb8, 0xa3, 0xa7, 0x8a, 0x79, 0xfd, 0xa0, 0x05, 0x08, 0x9c, 0xeb, 0xf0, 0xec, 0x67, 0x07, 0x9f, 0x8e, 0x3c, 0x0d, 0x8e, 0xf9, 0x75, 0x55, 0x13, 0xc1, 0xe8, 0x77, 0xf8, 0xbb}, + {0x66, 0x95, 0x6c, 0xc9, 0xe0, 0x39, 0x65, 0xb6, 0xb0, 0x05, 0xd1, 0xaf, 0xaf, 0xf3, 0x1d, 0xb9, 0xa4, 0xda, 0x6f, 0x20, 0xcd, 0x3a, 0xae, 0x64, 0xc2, 0xdb, 0xee, 0xf5, 0xb8, 0x8d, 0x57, 0x0e}, + {0x3c, 0xbb, 0x1c, 0x12, 0x5e, 0x17, 0xfd, 0x54, 0x90, 0x45, 0xa7, 0x7b, 0x61, 0x6c, 0x1d, 0xfe, 0xe6, 0xcc, 0x7f, 0xee, 0xcf, 0xef, 0x33, 0x35, 0x50, 0x62, 0x16, 0x70, 0x2f, 0x87, 0xc3, 0xc9}, + {0x53, 0x4d, 0xa8, 0xe7, 0x1e, 0x98, 0x73, 0x8d, 0xd9, 0xa3, 0x54, 0xa5, 0x0e, 0x59, 0x2c, 0x25, 0x43, 0x6f, 0xaa, 0xa2, 0xf5, 0x21, 0x06, 0x3e, 0xc9, 0x82, 0x06, 0x94, 0x98, 0x72, 0x9d, 0xa7}, + {0xef, 0x7e, 0xe9, 0x6b, 0xd3, 0xe5, 0xb7, 0x41, 0x4c, 0xc8, 0xd3, 0x07, 0x52, 0x9a, 0x5a, 0x8b, 0x4e, 0x1e, 0x75, 0xa4, 0x17, 0x78, 0xc8, 0x36, 0xcd, 0xf8, 0x2e, 0xd9, 0x57, 0xe3, 0xd7, 0x07}, + {0x87, 0x16, 0xfb, 0xf9, 0xa5, 0xf8, 0xc4, 0x56, 0x2b, 0x48, 0x52, 0x8e, 0x2d, 0x30, 0x85, 0xb6, 0x4c, 0x56, 0xb5, 0xd1, 0x16, 0x9c, 0xcf, 0x32, 0x95, 0xad, 0x03, 0xe8, 0x05, 0x58, 0x06, 0x76}, + {0x75, 0x03, 0x80, 0x28, 0xf2, 0xa7, 0x63, 0x22, 0x1a, 0x26, 0x9c, 0x68, 0xe0, 0x58, 0xfc, 0x73, 0xeb, 0x42, 0xf6, 0x86, 0x16, 0x24, 0x4b, 0xbc, 0x24, 0xf7, 0x02, 0xc8, 0x3d, 0x90, 0xe2, 0xb0}, + {0xdf, 0x49, 0x0f, 0x15, 0x7b, 0x7d, 0xbf, 0xe0, 0xd4, 0xcf, 0x47, 0xc0, 0x80, 0x93, 0x4a, 0x61, 0xaa, 0x03, 0x07, 0x66, 0xb3, 0x38, 0x5d, 0xc8, 0xc9, 0x07, 0x61, 0xfb, 0x97, 0x10, 0x2f, 0xd8}, + {0x77, 0x19, 0x40, 0x56, 0x41, 0xad, 0xbc, 0x59, 0xda, 0x1e, 0xc5, 0x37, 0x14, 0x63, 0x7b, 0xfb, 0x79, 0xe2, 0x7a, 0xb1, 0x55, 0x42, 0x99, 0x42, 0x56, 0xfe, 0x26, 0x9d, 0x0f, 0x7e, 0x80, 0xc6}, + {0x50, 0xe7, 0x2a, 0x0e, 0x26, 0x44, 0x2f, 0xe2, 0x55, 0x2d, 0xc3, 0x93, 0x8a, 0xc5, 0x86, 0x58, 0x22, 0x8c, 0x0c, 0xbf, 0xb1, 0xd2, 0xca, 0x87, 0x2a, 0xe4, 0x35, 0x26, 0x6f, 0xcd, 0x05, 0x5e}, + {0xe4, 0x80, 0x6f, 0xdb, 0x3d, 0x7d, 0xba, 0xde, 0x50, 0x3f, 0xea, 0x00, 0x3d, 0x46, 0x59, 0x64, 0xfd, 0x58, 0x1c, 0xa1, 0xb8, 0x7d, 0x5f, 0xac, 0x94, 0x37, 0x9e, 0xa0, 0xc0, 0x9c, 0x93, 0x8b}, + {0x2c, 0xf3, 0xa9, 0xf6, 0x15, 0x25, 0x80, 0x70, 0x76, 0x99, 0x7d, 0xf1, 0xc3, 0x2f, 0xa3, 0x31, 0xff, 0x92, 0x35, 0x2e, 0x8d, 0x04, 0x13, 0x33, 0xd8, 0x0d, 0xdb, 0x4a, 0xf6, 0x8c, 0x03, 0x34}, + {0xec, 0x12, 0x24, 0x9f, 0x35, 0xa4, 0x29, 0x8b, 0x9e, 0x4a, 0x95, 0xf8, 0x61, 0xaf, 0x61, 0xc5, 0x66, 0x55, 0x3e, 0x3f, 0x2a, 0x98, 0xea, 0x71, 0x16, 0x6b, 0x1c, 0xd9, 0xe4, 0x09, 0xd2, 0x8e}, + }; + unsigned int i; + for (i = 0; i < sizeof(midstates)/sizeof(midstates[0]); i++) { + unsigned char out[32]; + secp256k1_sha256 hasher = midstates[i]; + secp256k1_sha256_write(&hasher, (const unsigned char*)input, strlen(input)); + secp256k1_sha256_finalize(&hasher, out); + CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); + } +} + +/* Tests for the equality of two sha256 structs. This function only produces a + * correct result if an integer multiple of 64 many bytes have been written + * into the hash functions. This function is used by some module tests. */ +static void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { + /* Is buffer fully consumed? */ + CHECK((sha1->bytes & 0x3F) == 0); + + CHECK(sha1->bytes == sha2->bytes); + CHECK(secp256k1_memcmp_var(sha1->s, sha2->s, sizeof(sha1->s)) == 0); +} + +static void run_hmac_sha256_tests(void) { static const char *keys[6] = { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", "\x4a\x65\x66\x65", @@ -318,19 +651,19 @@ void run_hmac_sha256_tests(void) { secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); secp256k1_hmac_sha256_finalize(&hasher, out); - CHECK(memcmp(out, outputs[i], 32) == 0); + CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); if (strlen(inputs[i]) > 0) { - int split = secp256k1_rand_int(strlen(inputs[i])); + int split = testrand_int(strlen(inputs[i])); secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); secp256k1_hmac_sha256_finalize(&hasher, out); - CHECK(memcmp(out, outputs[i], 32) == 0); + CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); } } } -void run_rfc6979_hmac_sha256_tests(void) { +static void run_rfc6979_hmac_sha256_tests(void) { static const unsigned char key1[65] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x4b, 0xf5, 0x12, 0x2f, 0x34, 0x45, 0x54, 0xc5, 0x3b, 0xde, 0x2e, 0xbb, 0x8c, 0xd2, 0xb7, 0xe3, 0xd1, 0x60, 0x0a, 0xd6, 0x31, 0xc3, 0x85, 0xa5, 0xd7, 0xcc, 0xe2, 0x3c, 0x77, 0x85, 0x45, 0x9a, 0}; static const unsigned char out1[3][32] = { {0x4f, 0xe2, 0x95, 0x25, 0xb2, 0x08, 0x68, 0x09, 0x15, 0x9a, 0xcd, 0xf0, 0x50, 0x6e, 0xfb, 0x86, 0xb0, 0xec, 0x93, 0x2c, 0x7b, 0xa4, 0x42, 0x56, 0xab, 0x32, 0x1e, 0x42, 0x1e, 0x67, 0xe9, 0xfb}, @@ -352,315 +685,1347 @@ void run_rfc6979_hmac_sha256_tests(void) { secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 64); for (i = 0; i < 3; i++) { secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); - CHECK(memcmp(out, out1[i], 32) == 0); + CHECK(secp256k1_memcmp_var(out, out1[i], 32) == 0); } secp256k1_rfc6979_hmac_sha256_finalize(&rng); secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 65); for (i = 0; i < 3; i++) { secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); - CHECK(memcmp(out, out1[i], 32) != 0); + CHECK(secp256k1_memcmp_var(out, out1[i], 32) != 0); } secp256k1_rfc6979_hmac_sha256_finalize(&rng); secp256k1_rfc6979_hmac_sha256_initialize(&rng, key2, 64); for (i = 0; i < 3; i++) { secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); - CHECK(memcmp(out, out2[i], 32) == 0); + CHECK(secp256k1_memcmp_var(out, out2[i], 32) == 0); } secp256k1_rfc6979_hmac_sha256_finalize(&rng); } -/***** RANDOM TESTS *****/ - -void test_rand_bits(int rand32, int bits) { - /* (1-1/2^B)^rounds[B] < 1/10^9, so rounds is the number of iterations to - * get a false negative chance below once in a billion */ - static const unsigned int rounds[7] = {1, 30, 73, 156, 322, 653, 1316}; - /* We try multiplying the results with various odd numbers, which shouldn't - * influence the uniform distribution modulo a power of 2. */ - static const uint32_t mults[6] = {1, 3, 21, 289, 0x9999, 0x80402011}; - /* We only select up to 6 bits from the output to analyse */ - unsigned int usebits = bits > 6 ? 6 : bits; - unsigned int maxshift = bits - usebits; - /* For each of the maxshift+1 usebits-bit sequences inside a bits-bit - number, track all observed outcomes, one per bit in a uint64_t. */ - uint64_t x[6][27] = {{0}}; - unsigned int i, shift, m; - /* Multiply the output of all rand calls with the odd number m, which - should not change the uniformity of its distribution. */ - for (i = 0; i < rounds[usebits]; i++) { - uint32_t r = (rand32 ? secp256k1_rand32() : secp256k1_rand_bits(bits)); - CHECK((((uint64_t)r) >> bits) == 0); - for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { - uint32_t rm = r * mults[m]; - for (shift = 0; shift <= maxshift; shift++) { - x[m][shift] |= (((uint64_t)1) << ((rm >> shift) & ((1 << usebits) - 1))); +static void run_tagged_sha256_tests(void) { + unsigned char tag[32] = { 0 }; + unsigned char msg[32] = { 0 }; + unsigned char hash32[32]; + unsigned char hash_expected[32] = { + 0x04, 0x7A, 0x5E, 0x17, 0xB5, 0x86, 0x47, 0xC1, + 0x3C, 0xC6, 0xEB, 0xC0, 0xAA, 0x58, 0x3B, 0x62, + 0xFB, 0x16, 0x43, 0x32, 0x68, 0x77, 0x40, 0x6C, + 0xE2, 0x76, 0x55, 0x9A, 0x3B, 0xDE, 0x55, 0xB3 + }; + + /* API test */ + CHECK(secp256k1_tagged_sha256(CTX, hash32, tag, sizeof(tag), msg, sizeof(msg)) == 1); + CHECK_ILLEGAL(CTX, secp256k1_tagged_sha256(CTX, NULL, tag, sizeof(tag), msg, sizeof(msg))); + CHECK_ILLEGAL(CTX, secp256k1_tagged_sha256(CTX, hash32, NULL, 0, msg, sizeof(msg))); + CHECK_ILLEGAL(CTX, secp256k1_tagged_sha256(CTX, hash32, tag, sizeof(tag), NULL, 0)); + + /* Static test vector */ + memcpy(tag, "tag", 3); + memcpy(msg, "msg", 3); + CHECK(secp256k1_tagged_sha256(CTX, hash32, tag, 3, msg, 3) == 1); + CHECK(secp256k1_memcmp_var(hash32, hash_expected, sizeof(hash32)) == 0); +} + +/***** MODINV TESTS *****/ + +/* Compute the modular inverse of (odd) x mod 2^64. */ +static uint64_t modinv2p64(uint64_t x) { + /* If w = 1/x mod 2^(2^L), then w*(2 - w*x) = 1/x mod 2^(2^(L+1)). See + * Hacker's Delight second edition, Henry S. Warren, Jr., pages 245-247 for + * why. Start with L=0, for which it is true for every odd x that + * 1/x=1 mod 2. Iterating 6 times gives us 1/x mod 2^64. */ + int l; + uint64_t w = 1; + CHECK(x & 1); + for (l = 0; l < 6; ++l) w *= (2 - w*x); + return w; +} + + +/* compute out = (a*b) mod m; if b=NULL, treat b=1; if m=NULL, treat m=infinity. + * + * Out is a 512-bit number (represented as 32 uint16_t's in LE order). The other + * arguments are 256-bit numbers (represented as 16 uint16_t's in LE order). */ +static void mulmod256(uint16_t* out, const uint16_t* a, const uint16_t* b, const uint16_t* m) { + uint16_t mul[32]; + uint64_t c = 0; + int i, j; + int m_bitlen = 0; + int mul_bitlen = 0; + + if (b != NULL) { + /* Compute the product of a and b, and put it in mul. */ + for (i = 0; i < 32; ++i) { + for (j = i <= 15 ? 0 : i - 15; j <= i && j <= 15; j++) { + c += (uint64_t)a[j] * b[i - j]; + } + mul[i] = c & 0xFFFF; + c >>= 16; + } + CHECK(c == 0); + + /* compute the highest set bit in mul */ + for (i = 511; i >= 0; --i) { + if ((mul[i >> 4] >> (i & 15)) & 1) { + mul_bitlen = i; + break; + } + } + } else { + /* if b==NULL, set mul=a. */ + memcpy(mul, a, 32); + memset(mul + 16, 0, 32); + /* compute the highest set bit in mul */ + for (i = 255; i >= 0; --i) { + if ((mul[i >> 4] >> (i & 15)) & 1) { + mul_bitlen = i; + break; } } } - for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { - for (shift = 0; shift <= maxshift; shift++) { - /* Test that the lower usebits bits of x[shift] are 1 */ - CHECK(((~x[m][shift]) << (64 - (1 << usebits))) == 0); + + if (m) { + /* Compute the highest set bit in m. */ + for (i = 255; i >= 0; --i) { + if ((m[i >> 4] >> (i & 15)) & 1) { + m_bitlen = i; + break; + } + } + + /* Try do mul -= m<= 0; --i) { + uint16_t mul2[32]; + int64_t cs; + + /* Compute mul2 = mul - m<= 0 && bitpos < 256) { + sub |= ((m[bitpos >> 4] >> (bitpos & 15)) & 1) << p; + } + } + /* Add mul[j]-sub to accumulator, and shift bottom 16 bits out to mul2[j]. */ + cs += mul[j]; + cs -= sub; + mul2[j] = (cs & 0xFFFF); + cs >>= 16; + } + /* If remainder of subtraction is 0, set mul = mul2. */ + if (cs == 0) { + memcpy(mul, mul2, sizeof(mul)); + } + } + /* Sanity check: test that all limbs higher than m's highest are zero */ + for (i = (m_bitlen >> 4) + 1; i < 32; ++i) { + CHECK(mul[i] == 0); } } + memcpy(out, mul, 32); } -/* Subrange must be a whole divisor of range, and at most 64 */ -void test_rand_int(uint32_t range, uint32_t subrange) { - /* (1-1/subrange)^rounds < 1/10^9 */ - int rounds = (subrange * 2073) / 100; +/* Convert a 256-bit number represented as 16 uint16_t's to signed30 notation. */ +static void uint16_to_signed30(secp256k1_modinv32_signed30* out, const uint16_t* in) { int i; - uint64_t x = 0; - CHECK((range % subrange) == 0); - for (i = 0; i < rounds; i++) { - uint32_t r = secp256k1_rand_int(range); - CHECK(r < range); - r = r % subrange; - x |= (((uint64_t)1) << r); + memset(out->v, 0, sizeof(out->v)); + for (i = 0; i < 256; ++i) { + out->v[i / 30] |= (int32_t)(((in[i >> 4]) >> (i & 15)) & 1) << (i % 30); } - /* Test that the lower subrange bits of x are 1. */ - CHECK(((~x) << (64 - subrange)) == 0); } -void run_rand_bits(void) { - size_t b; - test_rand_bits(1, 32); - for (b = 1; b <= 32; b++) { - test_rand_bits(0, b); +/* Convert a 256-bit number in signed30 notation to a representation as 16 uint16_t's. */ +static void signed30_to_uint16(uint16_t* out, const secp256k1_modinv32_signed30* in) { + int i; + memset(out, 0, 32); + for (i = 0; i < 256; ++i) { + out[i >> 4] |= (((in->v[i / 30]) >> (i % 30)) & 1) << (i & 15); } } -void run_rand_int(void) { - static const uint32_t ms[] = {1, 3, 17, 1000, 13771, 999999, 33554432}; - static const uint32_t ss[] = {1, 3, 6, 9, 13, 31, 64}; - unsigned int m, s; - for (m = 0; m < sizeof(ms) / sizeof(ms[0]); m++) { - for (s = 0; s < sizeof(ss) / sizeof(ss[0]); s++) { - test_rand_int(ms[m] * ss[s], ss[s]); +/* Randomly mutate the sign of limbs in signed30 representation, without changing the value. */ +static void mutate_sign_signed30(secp256k1_modinv32_signed30* x) { + int i; + for (i = 0; i < 16; ++i) { + int pos = testrand_bits(3); + if (x->v[pos] > 0 && x->v[pos + 1] <= 0x3fffffff) { + x->v[pos] -= 0x40000000; + x->v[pos + 1] += 1; + } else if (x->v[pos] < 0 && x->v[pos + 1] >= 0x3fffffff) { + x->v[pos] += 0x40000000; + x->v[pos + 1] -= 1; } } } -/***** NUM TESTS *****/ - -#ifndef USE_NUM_NONE -void random_num_negate(secp256k1_num *num) { - if (secp256k1_rand_bits(1)) { - secp256k1_num_negate(num); +/* Test secp256k1_modinv32{_var}, using inputs in 16-bit limb format, and returning inverse. */ +static void test_modinv32_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod) { + uint16_t tmp[16]; + secp256k1_modinv32_signed30 x; + secp256k1_modinv32_modinfo m; + int i, vartime, nonzero; + + uint16_to_signed30(&x, in); + nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4] | x.v[5] | x.v[6] | x.v[7] | x.v[8]) != 0; + uint16_to_signed30(&m.modulus, mod); + + /* compute 1/modulus mod 2^30 */ + m.modulus_inv30 = modinv2p64(m.modulus.v[0]) & 0x3fffffff; + CHECK(((m.modulus_inv30 * m.modulus.v[0]) & 0x3fffffff) == 1); + + /* Test secp256k1_jacobi32_maybe_var. */ + if (nonzero) { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed30(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 1 (or uncomputable). */ + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == 0 || jac == 1); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed30(&x, sqr); + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == 0 || jac == 1 - (mod[0] & 2)); + } + + uint16_to_signed30(&x, in); + mutate_sign_signed30(&m.modulus); + for (vartime = 0; vartime < 2; ++vartime) { + /* compute inverse */ + (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m); + + /* produce output */ + signed30_to_uint16(out, &x); + + /* check if the inverse times the input is 1 (mod m), unless x is 0. */ + mulmod256(tmp, out, in, mod); + CHECK(tmp[0] == nonzero); + for (i = 1; i < 16; ++i) CHECK(tmp[i] == 0); + + /* invert again */ + (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m); + + /* check if the result is equal to the input */ + signed30_to_uint16(tmp, &x); + for (i = 0; i < 16; ++i) CHECK(tmp[i] == in[i]); } } -void random_num_order_test(secp256k1_num *num) { - secp256k1_scalar sc; - random_scalar_order_test(&sc); - secp256k1_scalar_get_num(num, &sc); +#ifdef SECP256K1_WIDEMUL_INT128 +/* Convert a 256-bit number represented as 16 uint16_t's to signed62 notation. */ +static void uint16_to_signed62(secp256k1_modinv64_signed62* out, const uint16_t* in) { + int i; + memset(out->v, 0, sizeof(out->v)); + for (i = 0; i < 256; ++i) { + out->v[i / 62] |= (int64_t)(((in[i >> 4]) >> (i & 15)) & 1) << (i % 62); + } } -void random_num_order(secp256k1_num *num) { - secp256k1_scalar sc; - random_scalar_order(&sc); - secp256k1_scalar_get_num(num, &sc); -} - -void test_num_negate(void) { - secp256k1_num n1; - secp256k1_num n2; - random_num_order_test(&n1); /* n1 = R */ - random_num_negate(&n1); - secp256k1_num_copy(&n2, &n1); /* n2 = R */ - secp256k1_num_sub(&n1, &n2, &n1); /* n1 = n2-n1 = 0 */ - CHECK(secp256k1_num_is_zero(&n1)); - secp256k1_num_copy(&n1, &n2); /* n1 = R */ - secp256k1_num_negate(&n1); /* n1 = -R */ - CHECK(!secp256k1_num_is_zero(&n1)); - secp256k1_num_add(&n1, &n2, &n1); /* n1 = n2+n1 = 0 */ - CHECK(secp256k1_num_is_zero(&n1)); - secp256k1_num_copy(&n1, &n2); /* n1 = R */ - secp256k1_num_negate(&n1); /* n1 = -R */ - CHECK(secp256k1_num_is_neg(&n1) != secp256k1_num_is_neg(&n2)); - secp256k1_num_negate(&n1); /* n1 = R */ - CHECK(secp256k1_num_eq(&n1, &n2)); -} - -void test_num_add_sub(void) { +/* Convert a 256-bit number in signed62 notation to a representation as 16 uint16_t's. */ +static void signed62_to_uint16(uint16_t* out, const secp256k1_modinv64_signed62* in) { int i; - secp256k1_scalar s; - secp256k1_num n1; - secp256k1_num n2; - secp256k1_num n1p2, n2p1, n1m2, n2m1; - random_num_order_test(&n1); /* n1 = R1 */ - if (secp256k1_rand_bits(1)) { - random_num_negate(&n1); - } - random_num_order_test(&n2); /* n2 = R2 */ - if (secp256k1_rand_bits(1)) { - random_num_negate(&n2); - } - secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */ - secp256k1_num_add(&n2p1, &n2, &n1); /* n2p1 = R2 + R1 */ - secp256k1_num_sub(&n1m2, &n1, &n2); /* n1m2 = R1 - R2 */ - secp256k1_num_sub(&n2m1, &n2, &n1); /* n2m1 = R2 - R1 */ - CHECK(secp256k1_num_eq(&n1p2, &n2p1)); - CHECK(!secp256k1_num_eq(&n1p2, &n1m2)); - secp256k1_num_negate(&n2m1); /* n2m1 = -R2 + R1 */ - CHECK(secp256k1_num_eq(&n2m1, &n1m2)); - CHECK(!secp256k1_num_eq(&n2m1, &n1)); - secp256k1_num_add(&n2m1, &n2m1, &n2); /* n2m1 = -R2 + R1 + R2 = R1 */ - CHECK(secp256k1_num_eq(&n2m1, &n1)); - CHECK(!secp256k1_num_eq(&n2p1, &n1)); - secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */ - CHECK(secp256k1_num_eq(&n2p1, &n1)); - - /* check is_one */ - secp256k1_scalar_set_int(&s, 1); - secp256k1_scalar_get_num(&n1, &s); - CHECK(secp256k1_num_is_one(&n1)); - /* check that 2^n + 1 is never 1 */ - secp256k1_scalar_get_num(&n2, &s); - for (i = 0; i < 250; ++i) { - secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */ - secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */ - CHECK(!secp256k1_num_is_one(&n1p2)); - } -} - -void test_num_mod(void) { + memset(out, 0, 32); + for (i = 0; i < 256; ++i) { + out[i >> 4] |= (((in->v[i / 62]) >> (i % 62)) & 1) << (i & 15); + } +} + +/* Randomly mutate the sign of limbs in signed62 representation, without changing the value. */ +static void mutate_sign_signed62(secp256k1_modinv64_signed62* x) { + static const int64_t M62 = (int64_t)(UINT64_MAX >> 2); int i; - secp256k1_scalar s; - secp256k1_num order, n; - - /* check that 0 mod anything is 0 */ - random_scalar_order_test(&s); - secp256k1_scalar_get_num(&order, &s); - secp256k1_scalar_set_int(&s, 0); - secp256k1_scalar_get_num(&n, &s); - secp256k1_num_mod(&n, &order); - CHECK(secp256k1_num_is_zero(&n)); - - /* check that anything mod 1 is 0 */ - secp256k1_scalar_set_int(&s, 1); - secp256k1_scalar_get_num(&order, &s); - secp256k1_scalar_get_num(&n, &s); - secp256k1_num_mod(&n, &order); - CHECK(secp256k1_num_is_zero(&n)); - - /* check that increasing the number past 2^256 does not break this */ - random_scalar_order_test(&s); - secp256k1_scalar_get_num(&n, &s); - /* multiply by 2^8, which'll test this case with high probability */ for (i = 0; i < 8; ++i) { - secp256k1_num_add(&n, &n, &n); + int pos = testrand_bits(2); + if (x->v[pos] > 0 && x->v[pos + 1] <= M62) { + x->v[pos] -= (M62 + 1); + x->v[pos + 1] += 1; + } else if (x->v[pos] < 0 && x->v[pos + 1] >= -M62) { + x->v[pos] += (M62 + 1); + x->v[pos + 1] -= 1; + } } - secp256k1_num_mod(&n, &order); - CHECK(secp256k1_num_is_zero(&n)); } -void test_num_jacobi(void) { - secp256k1_scalar sqr; - secp256k1_scalar small; - secp256k1_scalar five; /* five is not a quadratic residue */ - secp256k1_num order, n; +/* Test secp256k1_modinv64{_var}, using inputs in 16-bit limb format, and returning inverse. */ +static void test_modinv64_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod) { + static const int64_t M62 = (int64_t)(UINT64_MAX >> 2); + uint16_t tmp[16]; + secp256k1_modinv64_signed62 x; + secp256k1_modinv64_modinfo m; + int i, vartime, nonzero; + + uint16_to_signed62(&x, in); + nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4]) != 0; + uint16_to_signed62(&m.modulus, mod); + + /* compute 1/modulus mod 2^62 */ + m.modulus_inv62 = modinv2p64(m.modulus.v[0]) & M62; + CHECK(((m.modulus_inv62 * m.modulus.v[0]) & M62) == 1); + + /* Test secp256k1_jacobi64_maybe_var. */ + if (nonzero) { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed62(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 1 (or uncomputable). */ + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == 0 || jac == 1); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed62(&x, sqr); + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == 0 || jac == 1 - (mod[0] & 2)); + } + + uint16_to_signed62(&x, in); + mutate_sign_signed62(&m.modulus); + for (vartime = 0; vartime < 2; ++vartime) { + /* compute inverse */ + (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m); + + /* produce output */ + signed62_to_uint16(out, &x); + + /* check if the inverse times the input is 1 (mod m), unless x is 0. */ + mulmod256(tmp, out, in, mod); + CHECK(tmp[0] == nonzero); + for (i = 1; i < 16; ++i) CHECK(tmp[i] == 0); + + /* invert again */ + (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m); + + /* check if the result is equal to the input */ + signed62_to_uint16(tmp, &x); + for (i = 0; i < 16; ++i) CHECK(tmp[i] == in[i]); + } +} +#endif + +/* test if a and b are coprime */ +static int coprime(const uint16_t* a, const uint16_t* b) { + uint16_t x[16], y[16], t[16]; int i; - /* squares mod 5 are 1, 4 */ - const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 }; + int iszero; + memcpy(x, a, 32); + memcpy(y, b, 32); + + /* simple gcd loop: while x!=0, (x,y)=(y%x,x) */ + while (1) { + iszero = 1; + for (i = 0; i < 16; ++i) { + if (x[i] != 0) { + iszero = 0; + break; + } + } + if (iszero) break; + mulmod256(t, y, NULL, x); + memcpy(y, x, 32); + memcpy(x, t, 32); + } + + /* return whether y=1 */ + if (y[0] != 1) return 0; + for (i = 1; i < 16; ++i) { + if (y[i] != 0) return 0; + } + return 1; +} + +static void run_modinv_tests(void) { + /* Fixed test cases. Each tuple is (input, modulus, output), each as 16x16 bits in LE order. */ + static const uint16_t CASES[][3][16] = { + /* Test cases triggering edge cases in divsteps */ + + /* Test case known to need 713 divsteps */ + {{0x1513, 0x5389, 0x54e9, 0x2798, 0x1957, 0x66a0, 0x8057, 0x3477, + 0x7784, 0x1052, 0x326a, 0x9331, 0x6506, 0xa95c, 0x91f3, 0xfb5e}, + {0x2bdd, 0x8df4, 0xcc61, 0x481f, 0xdae5, 0x5ca7, 0xf43b, 0x7d54, + 0x13d6, 0x469b, 0x2294, 0x20f4, 0xb2a4, 0xa2d1, 0x3ff1, 0xfd4b}, + {0xffd8, 0xd9a0, 0x456e, 0x81bb, 0xbabd, 0x6cea, 0x6dbd, 0x73ab, + 0xbb94, 0x3d3c, 0xdf08, 0x31c4, 0x3e32, 0xc179, 0x2486, 0xb86b}}, + /* Test case known to need 589 divsteps, reaching delta=-140 and + delta=141. */ + {{0x3fb1, 0x903b, 0x4eb7, 0x4813, 0xd863, 0x26bf, 0xd89f, 0xa8a9, + 0x02fe, 0x57c6, 0x554a, 0x4eab, 0x165e, 0x3d61, 0xee1e, 0x456c}, + {0x9295, 0x823b, 0x5c1f, 0x5386, 0x48e0, 0x02ff, 0x4c2a, 0xa2da, + 0xe58f, 0x967c, 0xc97e, 0x3f5a, 0x69fb, 0x52d9, 0x0a86, 0xb4a3}, + {0x3d30, 0xb893, 0xa809, 0xa7a8, 0x26f5, 0x5b42, 0x55be, 0xf4d0, + 0x12c2, 0x7e6a, 0xe41a, 0x90c7, 0xebfa, 0xf920, 0x304e, 0x1419}}, + /* Test case known to need 650 divsteps, and doing 65 consecutive (f,g/2) steps. */ + {{0x8583, 0x5058, 0xbeae, 0xeb69, 0x48bc, 0x52bb, 0x6a9d, 0xcc94, + 0x2a21, 0x87d5, 0x5b0d, 0x42f6, 0x5b8a, 0x2214, 0xe9d6, 0xa040}, + {0x7531, 0x27cb, 0x7e53, 0xb739, 0x6a5f, 0x83f5, 0xa45c, 0xcb1d, + 0x8a87, 0x1c9c, 0x51d7, 0x851c, 0xb9d8, 0x1fbe, 0xc241, 0xd4a3}, + {0xcdb4, 0x275c, 0x7d22, 0xa906, 0x0173, 0xc054, 0x7fdf, 0x5005, + 0x7fb8, 0x9059, 0xdf51, 0x99df, 0x2654, 0x8f6e, 0x070f, 0xb347}}, + /* example needing 713 divsteps; delta=-2..3 */ + {{0xe2e9, 0xee91, 0x4345, 0xe5ad, 0xf3ec, 0x8f42, 0x0364, 0xd5c9, + 0xff49, 0xbef5, 0x4544, 0x4c7c, 0xae4b, 0xfd9d, 0xb35b, 0xda9d}, + {0x36e7, 0x8cca, 0x2ed0, 0x47b3, 0xaca4, 0xb374, 0x7d2a, 0x0772, + 0x6bdb, 0xe0a7, 0x900b, 0xfe10, 0x788c, 0x6f22, 0xd909, 0xf298}, + {0xd8c6, 0xba39, 0x13ed, 0x198c, 0x16c8, 0xb837, 0xa5f2, 0x9797, + 0x0113, 0x882a, 0x15b5, 0x324c, 0xabee, 0xe465, 0x8170, 0x85ac}}, + /* example needing 713 divsteps; delta=-2..3 */ + {{0xd5b7, 0x2966, 0x040e, 0xf59a, 0x0387, 0xd96d, 0xbfbc, 0xd850, + 0x2d96, 0x872a, 0xad81, 0xc03c, 0xbb39, 0xb7fa, 0xd904, 0xef78}, + {0x6279, 0x4314, 0xfdd3, 0x1568, 0x0982, 0x4d13, 0x625f, 0x010c, + 0x22b1, 0x0cc3, 0xf22d, 0x5710, 0x1109, 0x5751, 0x7714, 0xfcf2}, + {0xdb13, 0x5817, 0x232e, 0xe456, 0xbbbc, 0x6fbe, 0x4572, 0xa358, + 0xc76d, 0x928e, 0x0162, 0x5314, 0x8325, 0x5683, 0xe21b, 0xda88}}, + /* example needing 713 divsteps; delta=-2..3 */ + {{0xa06f, 0x71ee, 0x3bac, 0x9ebb, 0xdeaa, 0x09ed, 0x1cf7, 0x9ec9, + 0x7158, 0x8b72, 0x5d53, 0x5479, 0x5c75, 0xbb66, 0x9125, 0xeccc}, + {0x2941, 0xd46c, 0x3cd4, 0x4a9d, 0x5c4a, 0x256b, 0xbd6c, 0x9b8e, + 0x8fe0, 0x8a14, 0xffe8, 0x2496, 0x618d, 0xa9d7, 0x5018, 0xfb29}, + {0x437c, 0xbd60, 0x7590, 0x94bb, 0x0095, 0xd35e, 0xd4fe, 0xd6da, + 0x0d4e, 0x5342, 0x4cd2, 0x169b, 0x661c, 0x1380, 0xed2d, 0x85c1}}, + /* example reaching delta=-64..65; 661 divsteps */ + {{0xfde4, 0x68d6, 0x6c48, 0x7f77, 0x1c78, 0x96de, 0x2fd9, 0xa6c2, + 0xbbb5, 0xd319, 0x69cf, 0xd4b3, 0xa321, 0xcda0, 0x172e, 0xe530}, + {0xd9e3, 0x0f60, 0x3d86, 0xeeab, 0x25ee, 0x9582, 0x2d50, 0xfe16, + 0xd4e2, 0xe3ba, 0x94e2, 0x9833, 0x6c5e, 0x8982, 0x13b6, 0xe598}, + {0xe675, 0xf55a, 0x10f6, 0xabde, 0x5113, 0xecaa, 0x61ae, 0xad9f, + 0x0c27, 0xef33, 0x62e5, 0x211d, 0x08fa, 0xa78d, 0xc675, 0x8bae}}, + /* example reaching delta=-64..65; 661 divsteps */ + {{0x21bf, 0x52d5, 0x8fd4, 0xaa18, 0x156a, 0x7247, 0xebb8, 0x5717, + 0x4eb5, 0x1421, 0xb58f, 0x3b0b, 0x5dff, 0xe533, 0xb369, 0xd28a}, + {0x9f6b, 0xe463, 0x2563, 0xc74d, 0x6d81, 0x636a, 0x8fc8, 0x7a94, + 0x9429, 0x1585, 0xf35e, 0x7ff5, 0xb64f, 0x9720, 0xba74, 0xe108}, + {0xa5ab, 0xea7b, 0xfe5e, 0x8a85, 0x13be, 0x7934, 0xe8a0, 0xa187, + 0x86b5, 0xe477, 0xb9a4, 0x75d7, 0x538f, 0xdd70, 0xc781, 0xb67d}}, + /* example reaching delta=-64..65; 661 divsteps */ + {{0xa41a, 0x3e8d, 0xf1f5, 0x9493, 0x868c, 0x5103, 0x2725, 0x3ceb, + 0x6032, 0x3624, 0xdc6b, 0x9120, 0xbf4c, 0x8821, 0x91ad, 0xb31a}, + {0x5c0b, 0xdda5, 0x20f8, 0x32a1, 0xaf73, 0x6ec5, 0x4779, 0x43d6, + 0xd454, 0x9573, 0xbf84, 0x5a58, 0xe04e, 0x307e, 0xd1d5, 0xe230}, + {0xda15, 0xbcd6, 0x7180, 0xabd3, 0x04e6, 0x6986, 0xc0d7, 0x90bb, + 0x3a4d, 0x7c95, 0xaaab, 0x9ab3, 0xda34, 0xa7f6, 0x9636, 0x6273}}, + /* example doing 123 consecutive (f,g/2) steps; 615 divsteps */ + {{0xb4d6, 0xb38f, 0x00aa, 0xebda, 0xd4c2, 0x70b8, 0x9dad, 0x58ee, + 0x68f8, 0x48d3, 0xb5ff, 0xf422, 0x9e46, 0x2437, 0x18d0, 0xd9cc}, + {0x5c83, 0xfed7, 0x97f5, 0x3f07, 0xcaad, 0x95b1, 0xb4a4, 0xb005, + 0x23af, 0xdd27, 0x6c0d, 0x932c, 0xe2b2, 0xe3ae, 0xfb96, 0xdf67}, + {0x3105, 0x0127, 0xfd48, 0x039b, 0x35f1, 0xbc6f, 0x6c0a, 0xb572, + 0xe4df, 0xebad, 0x8edc, 0xb89d, 0x9555, 0x4c26, 0x1fef, 0x997c}}, + /* example doing 123 consecutive (f,g/2) steps; 614 divsteps */ + {{0x5138, 0xd474, 0x385f, 0xc964, 0x00f2, 0x6df7, 0x862d, 0xb185, + 0xb264, 0xe9e1, 0x466c, 0xf39e, 0xafaf, 0x5f41, 0x47e2, 0xc89d}, + {0x8607, 0x9c81, 0x46a2, 0x7dcc, 0xcb0c, 0x9325, 0xe149, 0x2bde, + 0x6632, 0x2869, 0xa261, 0xb163, 0xccee, 0x22ae, 0x91e0, 0xcfd5}, + {0x831c, 0xda22, 0xb080, 0xba7a, 0x26e2, 0x54b0, 0x073b, 0x5ea0, + 0xed4b, 0xcb3d, 0xbba1, 0xbec8, 0xf2ad, 0xae0d, 0x349b, 0x17d1}}, + /* example doing 123 consecutive (f,g/2) steps; 614 divsteps */ + {{0xe9a5, 0xb4ad, 0xd995, 0x9953, 0xcdff, 0x50d7, 0xf715, 0x9dc7, + 0x3e28, 0x15a9, 0x95a3, 0x8554, 0x5b5e, 0xad1d, 0x6d57, 0x3d50}, + {0x3ad9, 0xbd60, 0x5cc7, 0x6b91, 0xadeb, 0x71f6, 0x7cc4, 0xa58a, + 0x2cce, 0xf17c, 0x38c9, 0x97ed, 0x65fb, 0x3fa6, 0xa6bc, 0xeb24}, + {0xf96c, 0x1963, 0x8151, 0xa0cc, 0x299b, 0xf277, 0x001a, 0x16bb, + 0xfd2e, 0x532d, 0x0410, 0xe117, 0x6b00, 0x44ec, 0xca6a, 0x1745}}, + /* example doing 446 (f,g/2) steps; 523 divsteps */ + {{0x3758, 0xa56c, 0xe41e, 0x4e47, 0x0975, 0xa82b, 0x107c, 0x89cf, + 0x2093, 0x5a0c, 0xda37, 0xe007, 0x6074, 0x4f68, 0x2f5a, 0xbb8a}, + {0x4beb, 0xa40f, 0x2c42, 0xd9d6, 0x97e8, 0xca7c, 0xd395, 0x894f, + 0x1f50, 0x8067, 0xa233, 0xb850, 0x1746, 0x1706, 0xbcda, 0xdf32}, + {0x762a, 0xceda, 0x4c45, 0x1ca0, 0x8c37, 0xd8c5, 0xef57, 0x7a2c, + 0x6e98, 0xe38a, 0xc50e, 0x2ca9, 0xcb85, 0x24d5, 0xc29c, 0x61f6}}, + /* example doing 446 (f,g/2) steps; 523 divsteps */ + {{0x6f38, 0x74ad, 0x7332, 0x4073, 0x6521, 0xb876, 0xa370, 0xa6bd, + 0xcea5, 0xbd06, 0x969f, 0x77c6, 0x1e69, 0x7c49, 0x7d51, 0xb6e7}, + {0x3f27, 0x4be4, 0xd81e, 0x1396, 0xb21f, 0x92aa, 0x6dc3, 0x6283, + 0x6ada, 0x3ca2, 0xc1e5, 0x8b9b, 0xd705, 0x5598, 0x8ba1, 0xe087}, + {0x6a22, 0xe834, 0xbc8d, 0xcee9, 0x42fc, 0xfc77, 0x9c45, 0x1ca8, + 0xeb66, 0xed74, 0xaaf9, 0xe75f, 0xfe77, 0x46d2, 0x179b, 0xbf3e}}, + /* example doing 336 (f,(f+g)/2) steps; 693 divsteps */ + {{0x7ea7, 0x444e, 0x84ea, 0xc447, 0x7c1f, 0xab97, 0x3de6, 0x5878, + 0x4e8b, 0xc017, 0x03e0, 0xdc40, 0xbbd0, 0x74ce, 0x0169, 0x7ab5}, + {0x4023, 0x154f, 0xfbe4, 0x8195, 0xfda0, 0xef54, 0x9e9a, 0xc703, + 0x2803, 0xf760, 0x6302, 0xed5b, 0x7157, 0x6456, 0xdd7d, 0xf14b}, + {0xb6fb, 0xe3b3, 0x0733, 0xa77e, 0x44c5, 0x3003, 0xc937, 0xdd4d, + 0x5355, 0x14e9, 0x184e, 0xcefe, 0xe6b5, 0xf2e0, 0x0a28, 0x5b74}}, + /* example doing 336 (f,(f+g)/2) steps; 687 divsteps */ + {{0xa893, 0xb5f4, 0x1ede, 0xa316, 0x242c, 0xbdcc, 0xb017, 0x0836, + 0x3a37, 0x27fb, 0xfb85, 0x251e, 0xa189, 0xb15d, 0xa4b8, 0xc24c}, + {0xb0b7, 0x57ba, 0xbb6d, 0x9177, 0xc896, 0xc7f2, 0x43b4, 0x85a6, + 0xe6c4, 0xe50e, 0x3109, 0x7ca5, 0xd73d, 0x13ff, 0x0c3d, 0xcd62}, + {0x48ca, 0xdb34, 0xe347, 0x2cef, 0x4466, 0x10fb, 0x7ee1, 0x6344, + 0x4308, 0x966d, 0xd4d1, 0xb099, 0x994f, 0xd025, 0x2187, 0x5866}}, + /* example doing 267 (g,(g-f)/2) steps; 678 divsteps */ + {{0x0775, 0x1754, 0x01f6, 0xdf37, 0xc0be, 0x8197, 0x072f, 0x6cf5, + 0x8b36, 0x8069, 0x5590, 0xb92d, 0x6084, 0x47a4, 0x23fe, 0xddd5}, + {0x8e1b, 0xda37, 0x27d9, 0x312e, 0x3a2f, 0xef6d, 0xd9eb, 0x8153, + 0xdcba, 0x9fa3, 0x9f80, 0xead5, 0x134d, 0x2ebb, 0x5ec0, 0xe032}, + {0x1cb6, 0x5a61, 0x1bed, 0x77d6, 0xd5d1, 0x7498, 0xef33, 0x2dd2, + 0x1089, 0xedbd, 0x6958, 0x16ae, 0x336c, 0x45e6, 0x4361, 0xbadc}}, + /* example doing 267 (g,(g-f)/2) steps; 676 divsteps */ + {{0x0207, 0xf948, 0xc430, 0xf36b, 0xf0a7, 0x5d36, 0x751f, 0x132c, + 0x6f25, 0xa630, 0xca1f, 0xc967, 0xaf9c, 0x34e7, 0xa38f, 0xbe9f}, + {0x5fb9, 0x7321, 0x6561, 0x5fed, 0x54ec, 0x9c3a, 0xee0e, 0x6717, + 0x49af, 0xb896, 0xf4f5, 0x451c, 0x722a, 0xf116, 0x64a9, 0xcf0b}, + {0xf4d7, 0xdb47, 0xfef2, 0x4806, 0x4cb8, 0x18c7, 0xd9a7, 0x4951, + 0x14d8, 0x5c3a, 0xd22d, 0xd7b2, 0x750c, 0x3de7, 0x8b4a, 0x19aa}}, + + /* Test cases triggering edge cases in divsteps variant starting with delta=1/2 */ + + /* example needing 590 divsteps; delta=-5/2..7/2 */ + {{0x9118, 0xb640, 0x53d7, 0x30ab, 0x2a23, 0xd907, 0x9323, 0x5b3a, + 0xb6d4, 0x538a, 0x7637, 0xfe97, 0xfd05, 0x3cc0, 0x453a, 0xfb7e}, + {0x6983, 0x4f75, 0x4ad1, 0x48ad, 0xb2d9, 0x521d, 0x3dbc, 0x9cc0, + 0x4b60, 0x0ac6, 0xd3be, 0x0fb6, 0xd305, 0x3895, 0x2da5, 0xfdf8}, + {0xcec1, 0x33ac, 0xa801, 0x8194, 0xe36c, 0x65ef, 0x103b, 0xca54, + 0xfa9b, 0xb41d, 0x9b52, 0xb6f7, 0xa611, 0x84aa, 0x3493, 0xbf54}}, + /* example needing 590 divsteps; delta=-3/2..5/2 */ + {{0xb5f2, 0x42d0, 0x35e8, 0x8ca0, 0x4b62, 0x6e1d, 0xbdf3, 0x890e, + 0x8c82, 0x23d8, 0xc79a, 0xc8e8, 0x789e, 0x353d, 0x9766, 0xea9d}, + {0x6fa1, 0xacba, 0x4b7a, 0x5de1, 0x95d0, 0xc845, 0xebbf, 0x6f5a, + 0x30cf, 0x52db, 0x69b7, 0xe278, 0x4b15, 0x8411, 0x2ab2, 0xf3e7}, + {0xf12c, 0x9d6d, 0x95fa, 0x1878, 0x9f13, 0x4fb5, 0x3c8b, 0xa451, + 0x7182, 0xc4b6, 0x7e2a, 0x7bb7, 0x6e0e, 0x5b68, 0xde55, 0x9927}}, + /* example needing 590 divsteps; delta=-3/2..5/2 */ + {{0x229c, 0x4ef8, 0x1e93, 0xe5dc, 0xcde5, 0x6d62, 0x263b, 0xad11, + 0xced0, 0x88ff, 0xae8e, 0x3183, 0x11d2, 0xa50b, 0x350d, 0xeb40}, + {0x3157, 0xe2ea, 0x8a02, 0x0aa3, 0x5ae1, 0xb26c, 0xea27, 0x6805, + 0x87e2, 0x9461, 0x37c1, 0x2f8d, 0x85d2, 0x77a8, 0xf805, 0xeec9}, + {0x6f4e, 0x2748, 0xf7e5, 0xd8d3, 0xabe2, 0x7270, 0xc4e0, 0xedc7, + 0xf196, 0x78ca, 0x9139, 0xd8af, 0x72c6, 0xaf2f, 0x85d2, 0x6cd3}}, + /* example needing 590 divsteps; delta=-5/2..7/2 */ + {{0xdce8, 0xf1fe, 0x6708, 0x021e, 0xf1ca, 0xd609, 0x5443, 0x85ce, + 0x7a05, 0x8f9c, 0x90c3, 0x52e7, 0x8e1d, 0x97b8, 0xc0bf, 0xf2a1}, + {0xbd3d, 0xed11, 0x1625, 0xb4c5, 0x844c, 0xa413, 0x2569, 0xb9ba, + 0xcd35, 0xff84, 0xcd6e, 0x7f0b, 0x7d5d, 0x10df, 0x3efe, 0xfbe5}, + {0xa9dd, 0xafef, 0xb1b7, 0x4c8d, 0x50e4, 0xafbf, 0x2d5a, 0xb27c, + 0x0653, 0x66b6, 0x5d36, 0x4694, 0x7e35, 0xc47c, 0x857f, 0x32c5}}, + /* example needing 590 divsteps; delta=-3/2..5/2 */ + {{0x7902, 0xc9f8, 0x926b, 0xaaeb, 0x90f8, 0x1c89, 0xcce3, 0x96b7, + 0x28b2, 0x87a2, 0x136d, 0x695a, 0xa8df, 0x9061, 0x9e31, 0xee82}, + {0xd3a9, 0x3c02, 0x818c, 0x6b81, 0x34b3, 0xebbb, 0xe2c8, 0x7712, + 0xbfd6, 0x8248, 0xa6f4, 0xba6f, 0x03bb, 0xfb54, 0x7575, 0xfe89}, + {0x8246, 0x0d63, 0x478e, 0xf946, 0xf393, 0x0451, 0x08c2, 0x5919, + 0x5fd6, 0x4c61, 0xbeb7, 0x9a15, 0x30e1, 0x55fc, 0x6a01, 0x3724}}, + /* example reaching delta=-127/2..129/2; 571 divsteps */ + {{0x3eff, 0x926a, 0x77f5, 0x1fff, 0x1a5b, 0xf3ef, 0xf64b, 0x8681, + 0xf800, 0xf9bc, 0x761d, 0xe268, 0x62b0, 0xa032, 0xba9c, 0xbe56}, + {0xb8f9, 0x00e7, 0x47b7, 0xdffc, 0xfd9d, 0x5abb, 0xa19b, 0x1868, + 0x31fd, 0x3b29, 0x3674, 0x5449, 0xf54d, 0x1d19, 0x6ac7, 0xff6f}, + {0xf1d7, 0x3551, 0x5682, 0x9adf, 0xe8aa, 0x19a5, 0x8340, 0x71db, + 0xb7ab, 0x4cfd, 0xf661, 0x632c, 0xc27e, 0xd3c6, 0xdf42, 0xd306}}, + /* example reaching delta=-127/2..129/2; 571 divsteps */ + {{0x0000, 0x0000, 0x0000, 0x0000, 0x3aff, 0x2ed7, 0xf2e0, 0xabc7, + 0x8aee, 0x166e, 0x7ed0, 0x9ac7, 0x714a, 0xb9c5, 0x4d58, 0xad6c}, + {0x9cf9, 0x47e2, 0xa421, 0xb277, 0xffc2, 0x2747, 0x6486, 0x94c1, + 0x1d99, 0xd49b, 0x1096, 0x991a, 0xe986, 0xae02, 0xe89b, 0xea36}, + {0x1fb4, 0x98d8, 0x19b7, 0x80e9, 0xcdac, 0xaa5a, 0xf1e6, 0x0074, + 0xe393, 0xed8b, 0x8d5c, 0xe17d, 0x81b3, 0xc16d, 0x54d3, 0x9be3}}, + /* example reaching delta=-127/2..129/2; 571 divsteps */ + {{0xd047, 0x7e36, 0x3157, 0x7ab6, 0xb4d9, 0x8dae, 0x7534, 0x4f5d, + 0x489e, 0xa8ab, 0x8a3d, 0xd52c, 0x62af, 0xa032, 0xba9c, 0xbe56}, + {0xb1f1, 0x737f, 0x5964, 0x5afb, 0x3712, 0x8ef9, 0x19f7, 0x9669, + 0x664d, 0x03ad, 0xc352, 0xf7a5, 0xf545, 0x1d19, 0x6ac7, 0xff6f}, + {0xa834, 0x5256, 0x27bc, 0x33bd, 0xba11, 0x5a7b, 0x791e, 0xe6c0, + 0x9ac4, 0x9370, 0x1130, 0x28b4, 0x2b2e, 0x231b, 0x082a, 0x796e}}, + /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */ + {{0x6ab1, 0x6ea0, 0x1a99, 0xe0c2, 0xdd45, 0x645d, 0x8dbc, 0x466a, + 0xfa64, 0x4289, 0xd3f7, 0xfc8f, 0x2894, 0xe3c5, 0xa008, 0xcc14}, + {0xc75f, 0xc083, 0x4cc2, 0x64f2, 0x2aff, 0x4c12, 0x8461, 0xc4ae, + 0xbbfa, 0xb336, 0xe4b2, 0x3ac5, 0x2c22, 0xf56c, 0x5381, 0xe943}, + {0xcd80, 0x760d, 0x4395, 0xb3a6, 0xd497, 0xf583, 0x82bd, 0x1daa, + 0xbe92, 0x2613, 0xfdfb, 0x869b, 0x0425, 0xa333, 0x7056, 0xc9c5}}, + /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */ + {{0x71d4, 0x64df, 0xec4f, 0x74d8, 0x7e0c, 0x40d3, 0x7073, 0x4cc8, + 0x2a2a, 0xb1ff, 0x8518, 0x6513, 0xb0ea, 0x640a, 0x62d9, 0xd5f4}, + {0xdc75, 0xd937, 0x3b13, 0x1d36, 0xdf83, 0xd034, 0x1c1c, 0x4332, + 0x4cc3, 0xeeec, 0x7d94, 0x6771, 0x3384, 0x74b0, 0x947d, 0xf2c4}, + {0x0a82, 0x37a4, 0x12d5, 0xec97, 0x972c, 0xe6bf, 0xc348, 0xa0a9, + 0xc50c, 0xdc7c, 0xae30, 0x19d1, 0x0fca, 0x35e1, 0xd6f6, 0x81ee}}, + /* example doing 123 consecutive (f,g/2) steps; 554 divsteps */ + {{0xa6b1, 0xabc5, 0x5bbc, 0x7f65, 0xdd32, 0xaa73, 0xf5a3, 0x1982, + 0xced4, 0xe949, 0x0fd6, 0x2bc4, 0x2bd7, 0xe3c5, 0xa008, 0xcc14}, + {0x4b5f, 0x8f96, 0xa375, 0xfbcf, 0x1c7d, 0xf1ec, 0x03f5, 0xb35d, + 0xb999, 0xdb1f, 0xc9a1, 0xb4c7, 0x1dd5, 0xf56c, 0x5381, 0xe943}, + {0xaa3d, 0x38b9, 0xf17d, 0xeed9, 0x9988, 0x69ee, 0xeb88, 0x1495, + 0x203f, 0x18c8, 0x82b7, 0xdcb2, 0x34a7, 0x6b00, 0x6998, 0x589a}}, + /* example doing 453 (f,g/2) steps; 514 divsteps */ + {{0xa478, 0xe60d, 0x3244, 0x60e6, 0xada3, 0xfe50, 0xb6b1, 0x2eae, + 0xd0ef, 0xa7b1, 0xef63, 0x05c0, 0xe213, 0x443e, 0x4427, 0x2448}, + {0x258f, 0xf9ef, 0xe02b, 0x92dd, 0xd7f3, 0x252b, 0xa503, 0x9089, + 0xedff, 0x96c1, 0xfe3a, 0x3a39, 0x198a, 0x981d, 0x0627, 0xedb7}, + {0x595a, 0x45be, 0x8fb0, 0x2265, 0xc210, 0x02b8, 0xdce9, 0xe241, + 0xcab6, 0xbf0d, 0x0049, 0x8d9a, 0x2f51, 0xae54, 0x5785, 0xb411}}, + /* example doing 453 (f,g/2) steps; 514 divsteps */ + {{0x48f0, 0x7db3, 0xdafe, 0x1c92, 0x5912, 0xe11a, 0xab52, 0xede1, + 0x3182, 0x8980, 0x5d2b, 0x9b5b, 0x8718, 0xda27, 0x1683, 0x1de2}, + {0x168f, 0x6f36, 0xce7a, 0xf435, 0x19d4, 0xda5e, 0x2351, 0x9af5, + 0xb003, 0x0ef5, 0x3b4c, 0xecec, 0xa9f0, 0x78e1, 0xdfef, 0xe823}, + {0x5f55, 0xfdcc, 0xb233, 0x2914, 0x84f0, 0x97d1, 0x9cf4, 0x2159, + 0xbf56, 0xb79c, 0x17a3, 0x7cef, 0xd5de, 0x34f0, 0x5311, 0x4c54}}, + /* example doing 510 (f,(f+g)/2) steps; 512 divsteps */ + {{0x2789, 0x2e04, 0x6e0e, 0xb6cd, 0xe4de, 0x4dbf, 0x228d, 0x7877, + 0xc335, 0x806b, 0x38cd, 0x8049, 0xa73b, 0xcfa2, 0x82f7, 0x9e19}, + {0xc08d, 0xb99d, 0xb8f3, 0x663d, 0xbbb3, 0x1284, 0x1485, 0x1d49, + 0xc98f, 0x9e78, 0x1588, 0x11e3, 0xd91a, 0xa2c7, 0xfff1, 0xc7b9}, + {0x1e1f, 0x411d, 0x7c49, 0x0d03, 0xe789, 0x2f8e, 0x5d55, 0xa95e, + 0x826e, 0x8de5, 0x52a0, 0x1abc, 0x4cd7, 0xd13a, 0x4395, 0x63e1}}, + /* example doing 510 (f,(f+g)/2) steps; 512 divsteps */ + {{0xd5a1, 0xf786, 0x555c, 0xb14b, 0x44ae, 0x535f, 0x4a49, 0xffc3, + 0xf497, 0x70d1, 0x57c8, 0xa933, 0xc85a, 0x1910, 0x75bf, 0x960b}, + {0xfe53, 0x5058, 0x496d, 0xfdff, 0x6fb8, 0x4100, 0x92bd, 0xe0c4, + 0xda89, 0xe0a4, 0x841b, 0x43d4, 0xa388, 0x957f, 0x99ca, 0x9abf}, + {0xe530, 0x05bc, 0xfeec, 0xfc7e, 0xbcd3, 0x1239, 0x54cb, 0x7042, + 0xbccb, 0x139e, 0x9076, 0x0203, 0x6068, 0x90c7, 0x1ddf, 0x488d}}, + /* example doing 228 (g,(g-f)/2) steps; 538 divsteps */ + {{0x9488, 0xe54b, 0x0e43, 0x81d2, 0x06e7, 0x4b66, 0x36d0, 0x53d6, + 0x2b68, 0x22ec, 0x3fa9, 0xc1a7, 0x9ad2, 0xa596, 0xb3ac, 0xdf42}, + {0xe31f, 0x0b28, 0x5f3b, 0xc1ff, 0x344c, 0xbf5f, 0xd2ec, 0x2936, + 0x9995, 0xdeb2, 0xae6c, 0x2852, 0xa2c6, 0xb306, 0x8120, 0xe305}, + {0xa56e, 0xfb98, 0x1537, 0x4d85, 0x619e, 0x866c, 0x3cd4, 0x779a, + 0xdd66, 0xa80d, 0xdc2f, 0xcae4, 0xc74c, 0x5175, 0xa65d, 0x605e}}, + /* example doing 228 (g,(g-f)/2) steps; 537 divsteps */ + {{0x8cd5, 0x376d, 0xd01b, 0x7176, 0x19ef, 0xcf09, 0x8403, 0x5e52, + 0x83c1, 0x44de, 0xb91e, 0xb33d, 0xe15c, 0x51e7, 0xbad8, 0x6359}, + {0x3b75, 0xf812, 0x5f9e, 0xa04e, 0x92d3, 0x226e, 0x540e, 0x7c9a, + 0x31c6, 0x46d2, 0x0b7b, 0xdb4a, 0xe662, 0x4950, 0x0265, 0xf76f}, + {0x09ed, 0x692f, 0xe8f1, 0x3482, 0xab54, 0x36b4, 0x8442, 0x6ae9, + 0x4329, 0x6505, 0x183b, 0x1c1d, 0x482d, 0x7d63, 0xb44f, 0xcc09}}, + + /* Test cases with the group order as modulus. */ + + /* Test case with the group order as modulus, needing 635 divsteps. */ + {{0x95ed, 0x6c01, 0xd113, 0x5ff1, 0xd7d0, 0x29cc, 0x5817, 0x6120, + 0xca8e, 0xaad1, 0x25ae, 0x8e84, 0x9af6, 0x30bf, 0xf0ed, 0x1686}, + {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x1631, 0xbf4a, 0x286a, 0x2716, 0x469f, 0x2ac8, 0x1312, 0xe9bc, + 0x04f4, 0x304b, 0x9931, 0x113b, 0xd932, 0xc8f4, 0x0d0d, 0x01a1}}, + /* example with group size as modulus needing 631 divsteps */ + {{0x85ed, 0xc284, 0x9608, 0x3c56, 0x19b6, 0xbb5b, 0x2850, 0xdab7, + 0xa7f5, 0xe9ab, 0x06a4, 0x5bbb, 0x1135, 0xa186, 0xc424, 0xc68b}, + {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x8479, 0x450a, 0x8fa3, 0xde05, 0xb2f5, 0x7793, 0x7269, 0xbabb, + 0xc3b3, 0xd49b, 0x3377, 0x03c6, 0xe694, 0xc760, 0xd3cb, 0x2811}}, + /* example with group size as modulus needing 565 divsteps starting at delta=1/2 */ + {{0x8432, 0x5ceb, 0xa847, 0x6f1e, 0x51dd, 0x535a, 0x6ddc, 0x70ce, + 0x6e70, 0xc1f6, 0x18f2, 0x2a7e, 0xc8e7, 0x39f8, 0x7e96, 0xebbf}, + {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x257e, 0x449f, 0x689f, 0x89aa, 0x3989, 0xb661, 0x376c, 0x1e32, + 0x654c, 0xee2e, 0xf4e2, 0x33c8, 0x3f2f, 0x9716, 0x6046, 0xcaa3}}, + /* Test case with the group size as modulus, needing 981 divsteps with + broken eta handling. */ + {{0xfeb9, 0xb877, 0xee41, 0x7fa3, 0x87da, 0x94c4, 0x9d04, 0xc5ae, + 0x5708, 0x0994, 0xfc79, 0x0916, 0xbf32, 0x3ad8, 0xe11c, 0x5ca2}, + {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x0f12, 0x075e, 0xce1c, 0x6f92, 0xc80f, 0xca92, 0x9a04, 0x6126, + 0x4b6c, 0x57d6, 0xca31, 0x97f3, 0x1f99, 0xf4fd, 0xda4d, 0x42ce}}, + /* Test case with the group size as modulus, input = 0. */ + {{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}}, + /* Test case with the group size as modulus, input = 1. */ + {{0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}}, + /* Test case with the group size as modulus, input = 2. */ + {{0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x20a1, 0x681b, 0x2f46, 0xdfe9, 0x501d, 0x57a4, 0x6e73, 0x5d57, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff}}, + /* Test case with the group size as modulus, input = group - 1. */ + {{0x4140, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x4141, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x4140, 0xd036, 0x5e8c, 0xbfd2, 0xa03b, 0xaf48, 0xdce6, 0xbaae, + 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}}, + + /* Test cases with the field size as modulus. */ + + /* Test case with the field size as modulus, needing 637 divsteps. */ + {{0x9ec3, 0x1919, 0xca84, 0x7c11, 0xf996, 0x06f3, 0x5408, 0x6688, + 0x1320, 0xdb8a, 0x632a, 0x0dcb, 0x8a84, 0x6bee, 0x9c95, 0xe34e}, + {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x18e5, 0x19b6, 0xdf92, 0x1aaa, 0x09fb, 0x8a3f, 0x52b0, 0x8701, + 0xac0c, 0x2582, 0xda44, 0x9bcc, 0x6828, 0x1c53, 0xbd8f, 0xbd2c}}, + /* example with field size as modulus needing 637 divsteps */ + {{0xaec3, 0xa7cf, 0x2f2d, 0x0693, 0x5ad5, 0xa8ff, 0x7ec7, 0x30ff, + 0x0c8b, 0xc242, 0xcab2, 0x063a, 0xf86e, 0x6057, 0x9cbd, 0xf6d8}, + {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x0310, 0x579d, 0xcb38, 0x9030, 0x3ded, 0x9bb9, 0x1234, 0x63ce, + 0x0c63, 0x8e3d, 0xacfe, 0x3c20, 0xdc85, 0xf859, 0x919e, 0x1d45}}, + /* example with field size as modulus needing 564 divsteps starting at delta=1/2 */ + {{0x63ae, 0x8d10, 0x0071, 0xdb5c, 0xb454, 0x78d1, 0x744a, 0x5f8e, + 0xe4d8, 0x87b1, 0x8e62, 0x9590, 0xcede, 0xa070, 0x36b4, 0x7f6f}, + {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0xfdc8, 0xe8d5, 0xbe15, 0x9f86, 0xa5fe, 0xf18e, 0xa7ff, 0xd291, + 0xf4c2, 0x9c87, 0xf150, 0x073e, 0x69b8, 0xf7c4, 0xee4b, 0xc7e6}}, + /* Test case with the field size as modulus, needing 935 divsteps with + broken eta handling. */ + {{0x1b37, 0xbdc3, 0x8bcd, 0x25e3, 0x1eae, 0x567d, 0x30b6, 0xf0d8, + 0x9277, 0x0cf8, 0x9c2e, 0xecd7, 0x631d, 0xe38f, 0xd4f8, 0x5c93}, + {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x1622, 0xe05b, 0xe880, 0x7de9, 0x3e45, 0xb682, 0xee6c, 0x67ed, + 0xa179, 0x15db, 0x6b0d, 0xa656, 0x7ccb, 0x8ef7, 0xa2ff, 0xe279}}, + /* Test case with the field size as modulus, input = 0. */ + {{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}}, + /* Test case with the field size as modulus, input = 1. */ + {{0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}}, + /* Test case with the field size as modulus, input = 2. */ + {{0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, + {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0xfe18, 0x7fff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7fff}}, + /* Test case with the field size as modulus, input = field - 1. */ + {{0xfc2e, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, + {0xfc2e, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}}, + + /* Selected from a large number of random inputs to reach small/large + * d/e values in various configurations. */ + {{0x3a08, 0x23e1, 0x4d8c, 0xe606, 0x3263, 0x67af, 0x9bf1, 0x9d70, + 0xf5fd, 0x12e4, 0x03c8, 0xb9ca, 0xe847, 0x8c5d, 0x6322, 0xbd30}, + {0x8359, 0x59dd, 0x1831, 0x7c1a, 0x1e83, 0xaee1, 0x770d, 0xcea8, + 0xfbb1, 0xeed6, 0x10b5, 0xe2c6, 0x36ea, 0xee17, 0xe32c, 0xffff}, + {0x1727, 0x0f36, 0x6f85, 0x5d0c, 0xca6c, 0x3072, 0x9628, 0x5842, + 0xcb44, 0x7c2b, 0xca4f, 0x62e5, 0x29b1, 0x6ffd, 0x9055, 0xc196}}, + {{0x905d, 0x41c8, 0xa2ff, 0x295b, 0x72bb, 0x4679, 0x6d01, 0x2c98, + 0xb3e0, 0xc537, 0xa310, 0xe07e, 0xe72f, 0x4999, 0x1148, 0xf65e}, + {0x5b41, 0x4239, 0x3c37, 0x5130, 0x30e3, 0xff35, 0xc51f, 0x1a43, + 0xdb23, 0x13cf, 0x9f49, 0xf70c, 0x5e70, 0xd411, 0x3005, 0xf8c6}, + {0xc30e, 0x68f0, 0x201a, 0xe10c, 0x864a, 0x6243, 0xe946, 0x43ae, + 0xf3f1, 0x52dc, 0x1f7f, 0x50d4, 0x2797, 0x064c, 0x5ca4, 0x90e3}}, + {{0xf1b5, 0xc6e5, 0xd2c4, 0xff95, 0x27c5, 0x0c92, 0x5d19, 0x7ae5, + 0x4fbe, 0x5438, 0x99e1, 0x880d, 0xd892, 0xa05c, 0x6ffd, 0x7eac}, + {0x2153, 0xcc9d, 0xfc6c, 0x8358, 0x49a1, 0x01e2, 0xcef0, 0x4969, + 0xd69a, 0x8cef, 0xf5b2, 0xfd95, 0xdcc2, 0x71f4, 0x6ae2, 0xceeb}, + {0x9b2e, 0xcdc6, 0x0a5c, 0x7317, 0x9084, 0xe228, 0x56cf, 0xd512, + 0x628a, 0xce21, 0x3473, 0x4e13, 0x8823, 0x1ed0, 0x34d0, 0xbfa3}}, + {{0x5bae, 0x53e5, 0x5f4d, 0x21ca, 0xb875, 0x8ecf, 0x9aa6, 0xbe3c, + 0x9f96, 0x7b82, 0x375d, 0x4d3e, 0x491c, 0xb1eb, 0x04c9, 0xb6c8}, + {0xfcfd, 0x10b7, 0x73b2, 0xd23b, 0xa357, 0x67da, 0x0d9f, 0x8702, + 0xa037, 0xff8e, 0x0e8b, 0x1801, 0x2c5c, 0x4e6e, 0x4558, 0xfff2}, + {0xc50f, 0x5654, 0x6713, 0x5ef5, 0xa7ce, 0xa647, 0xc832, 0x69ce, + 0x1d5c, 0x4310, 0x0746, 0x5a01, 0x96ea, 0xde4b, 0xa88b, 0x5543}}, + {{0xdc7f, 0x5e8c, 0x89d1, 0xb077, 0xd521, 0xcf90, 0x32fa, 0x5737, + 0x839e, 0x1464, 0x007c, 0x09c6, 0x9371, 0xe8ea, 0xc1cb, 0x75c4}, + {0xe3a3, 0x107f, 0xa82a, 0xa375, 0x4578, 0x60f4, 0x75c9, 0x5ee4, + 0x3fd7, 0x2736, 0x2871, 0xd3d2, 0x5f1d, 0x1abb, 0xa764, 0xffff}, + {0x45c6, 0x1f2e, 0xb14c, 0x84d7, 0x7bb7, 0x5a04, 0x0504, 0x3f33, + 0x5cc1, 0xb07a, 0x6a6c, 0x786f, 0x647f, 0xe1d7, 0x78a2, 0x4cf4}}, + {{0xc006, 0x356f, 0x8cd2, 0x967b, 0xb49e, 0x2d4e, 0x14bf, 0x4bcb, + 0xddab, 0xd3f9, 0xa068, 0x2c1c, 0xd242, 0xa56d, 0xf2c7, 0x5f97}, + {0x465b, 0xb745, 0x0e0d, 0x69a9, 0x987d, 0xcb37, 0xf637, 0xb311, + 0xc4d6, 0x2ddb, 0xf68f, 0x2af9, 0x959d, 0x3f53, 0x98f2, 0xf640}, + {0xc0f2, 0x6bfb, 0xf5c3, 0x91c1, 0x6b05, 0x0825, 0x5ca0, 0x7df7, + 0x9d55, 0x6d9e, 0xfe94, 0x2ad9, 0xd9f0, 0xe68b, 0xa72b, 0xd1b2}}, + {{0x2279, 0x61ba, 0x5bc6, 0x136b, 0xf544, 0x717c, 0xafda, 0x02bd, + 0x79af, 0x1fad, 0xea09, 0x81bb, 0x932b, 0x32c9, 0xdf1d, 0xe576}, + {0x8215, 0x7817, 0xca82, 0x43b0, 0x9b06, 0xea65, 0x1291, 0x0621, + 0x0089, 0x46fe, 0xc5a6, 0xddd7, 0x8065, 0xc6a0, 0x214b, 0xfc64}, + {0x04bf, 0x6f2a, 0x86b2, 0x841a, 0x4a95, 0xc632, 0x97b7, 0x5821, + 0x2b18, 0x1bb0, 0x3e97, 0x935e, 0xcc7d, 0x066b, 0xd513, 0xc251}}, + {{0x76e8, 0x5bc2, 0x3eaa, 0x04fc, 0x9974, 0x92c1, 0x7c15, 0xfa89, + 0x1151, 0x36ee, 0x48b2, 0x049c, 0x5f16, 0xcee4, 0x925b, 0xe98e}, + {0x913f, 0x0a2d, 0xa185, 0x9fea, 0xda5a, 0x4025, 0x40d7, 0x7cfa, + 0x88ca, 0xbbe8, 0xb265, 0xb7e4, 0x6cb1, 0xed64, 0xc6f9, 0xffb5}, + {0x6ab1, 0x1a86, 0x5009, 0x152b, 0x1cc4, 0xe2c8, 0x960b, 0x19d0, + 0x3554, 0xc562, 0xd013, 0xcf91, 0x10e1, 0x7933, 0xe195, 0xcf49}}, + {{0x9cb5, 0xd2d7, 0xc6ed, 0xa818, 0xb495, 0x06ee, 0x0f4a, 0x06e3, + 0x4c5a, 0x80ce, 0xd49a, 0x4cd7, 0x7487, 0x92af, 0xe516, 0x676c}, + {0xd6e9, 0x6b85, 0x619a, 0xb52c, 0x20a0, 0x2f79, 0x3545, 0x1edd, + 0x5a6f, 0x8082, 0x9b80, 0xf8f8, 0xc78a, 0xd0a3, 0xadf4, 0xffff}, + {0x01c2, 0x2118, 0xef5e, 0xa877, 0x046a, 0xd2c2, 0x2ad5, 0x951c, + 0x8900, 0xa5c9, 0x8d0f, 0x6b61, 0x55d3, 0xd572, 0x48de, 0x9219}}, + {{0x5114, 0x0644, 0x23dd, 0x01d3, 0xc101, 0xa659, 0xea17, 0x640f, + 0xf767, 0x2644, 0x9cec, 0xd8ba, 0xd6da, 0x9156, 0x8aeb, 0x875a}, + {0xc1bf, 0xdae9, 0xe96b, 0xce77, 0xf7a1, 0x3e99, 0x5c2e, 0x973b, + 0xd048, 0x5bd0, 0x4e8a, 0xcb85, 0xce39, 0x37f5, 0x815d, 0xffff}, + {0x48cc, 0x35b6, 0x26d4, 0x2ea6, 0x50d6, 0xa2f9, 0x64b6, 0x03bf, + 0xd00c, 0xe057, 0x3343, 0xfb79, 0x3ce5, 0xf717, 0xc5af, 0xe185}}, + {{0x13ff, 0x6c76, 0x2077, 0x16e0, 0xd5ca, 0xf2ad, 0x8dba, 0x8f49, + 0x7887, 0x16f9, 0xb646, 0xfc87, 0xfa31, 0x5096, 0xf08c, 0x3fbe}, + {0x8139, 0x6fd7, 0xf6df, 0xa7bf, 0x6699, 0x5361, 0x6f65, 0x13c8, + 0xf4d1, 0xe28f, 0xc545, 0x0a8c, 0x5274, 0xb0a6, 0xffff, 0xffff}, + {0x22ca, 0x0cd6, 0xc1b5, 0xb064, 0x44a7, 0x297b, 0x495f, 0x34ac, + 0xfa95, 0xec62, 0xf08d, 0x621c, 0x66a6, 0xba94, 0x84c6, 0x8ee0}}, + {{0xaa30, 0x312e, 0x439c, 0x4e88, 0x2e2f, 0x32dc, 0xb880, 0xa28e, + 0xf795, 0xc910, 0xb406, 0x8dd7, 0xb187, 0xa5a5, 0x38f1, 0xe49e}, + {0xfb19, 0xf64a, 0xba6a, 0x8ec2, 0x7255, 0xce89, 0x2cf9, 0x9cba, + 0xe1fe, 0x50da, 0x1705, 0xac52, 0xe3d4, 0x4269, 0x0648, 0xfd77}, + {0xb4c8, 0x6e8a, 0x2b5f, 0x4c2d, 0x5a67, 0xa7bb, 0x7d6d, 0x5569, + 0xa0ea, 0x244a, 0xc0f2, 0xf73d, 0x58cf, 0xac7f, 0xd32b, 0x3018}}, + {{0xc953, 0x1ae1, 0xae46, 0x8709, 0x19c2, 0xa986, 0x9abe, 0x1611, + 0x0395, 0xd5ab, 0xf0f6, 0xb5b0, 0x5b2b, 0x0317, 0x80ba, 0x376d}, + {0xfe77, 0xbc03, 0xac2f, 0x9d00, 0xa175, 0x293d, 0x3b56, 0x0e3a, + 0x0a9c, 0xf40c, 0x690e, 0x1508, 0x95d4, 0xddc4, 0xe805, 0xffff}, + {0xb1ce, 0x0929, 0xa5fe, 0x4b50, 0x9d5d, 0x8187, 0x2557, 0x4376, + 0x11ba, 0xdcef, 0xc1f3, 0xd531, 0x1824, 0x93f6, 0xd81f, 0x8f83}}, + {{0xb8d2, 0xb900, 0x4a0c, 0x7188, 0xa5bf, 0x1b0b, 0x2ae5, 0xa35b, + 0x98e0, 0x610c, 0x86db, 0x2487, 0xa267, 0x002c, 0xebb6, 0xc5f4}, + {0x9cdd, 0x1c1b, 0x2f06, 0x43d1, 0xce47, 0xc334, 0x6e60, 0xc016, + 0x989e, 0x0ab2, 0x0cac, 0x1196, 0xe2d9, 0x2e04, 0xc62b, 0xffff}, + {0xdc36, 0x1f05, 0x6aa9, 0x7a20, 0x944f, 0x2fd3, 0xa553, 0xdb4f, + 0xbd5c, 0x3a75, 0x25d4, 0xe20e, 0xa387, 0x1410, 0xdbb1, 0x1b60}}, + {{0x76b3, 0x2207, 0x4930, 0x5dd7, 0x65a0, 0xd55c, 0xb443, 0x53b7, + 0x5c22, 0x818a, 0xb2e7, 0x9de8, 0x9985, 0xed45, 0x33b1, 0x53e8}, + {0x7913, 0x44e1, 0xf15b, 0x5edd, 0x34f3, 0x4eba, 0x0758, 0x7104, + 0x32d9, 0x28f3, 0x4401, 0x85c5, 0xb695, 0xb899, 0xc0f2, 0xffff}, + {0x7f43, 0xd202, 0x24c9, 0x69f3, 0x74dc, 0x1a69, 0xeaee, 0x5405, + 0x1755, 0x4bb8, 0x04e3, 0x2fd2, 0xada8, 0x39eb, 0x5b4d, 0x96ca}}, + {{0x807b, 0x7112, 0xc088, 0xdafd, 0x02fa, 0x9d95, 0x5e42, 0xc033, + 0xde0a, 0xeecf, 0x8e90, 0x8da1, 0xb17e, 0x9a5b, 0x4c6d, 0x1914}, + {0x4871, 0xd1cb, 0x47d7, 0x327f, 0x09ec, 0x97bb, 0x2fae, 0xd346, + 0x6b78, 0x3707, 0xfeb2, 0xa6ab, 0x13df, 0x76b0, 0x8fb9, 0xffb3}, + {0x179e, 0xb63b, 0x4784, 0x231e, 0x9f42, 0x7f1a, 0xa3fb, 0xdd8c, + 0xd1eb, 0xb4c9, 0x8ca7, 0x018c, 0xf691, 0x576c, 0xa7d6, 0xce27}}, + {{0x5f45, 0x7c64, 0x083d, 0xedd5, 0x08a0, 0x0c64, 0x6c6f, 0xec3c, + 0xe2fb, 0x352c, 0x9303, 0x75e4, 0xb4e0, 0x8b09, 0xaca4, 0x7025}, + {0x1025, 0xb482, 0xfed5, 0xa678, 0x8966, 0x9359, 0x5329, 0x98bb, + 0x85b2, 0x73ba, 0x9982, 0x6fdc, 0xf190, 0xbe8c, 0xdc5c, 0xfd93}, + {0x83a2, 0x87a4, 0xa680, 0x52a1, 0x1ba1, 0x8848, 0x5db7, 0x9744, + 0x409c, 0x0745, 0x0e1e, 0x1cfc, 0x00cd, 0xf573, 0x2071, 0xccaa}}, + {{0xf61f, 0x63d4, 0x536c, 0x9eb9, 0x5ddd, 0xbb11, 0x9014, 0xe904, + 0xfe01, 0x6b45, 0x1858, 0xcb5b, 0x4c38, 0x43e1, 0x381d, 0x7f94}, + {0xf61f, 0x63d4, 0xd810, 0x7ca3, 0x8a04, 0x4b83, 0x11fc, 0xdf94, + 0x4169, 0xbd05, 0x608e, 0x7151, 0x4fbf, 0xb31a, 0x38a7, 0xa29b}, + {0xe621, 0xdfa5, 0x3d06, 0x1d03, 0x81e6, 0x00da, 0x53a6, 0x965e, + 0x93e5, 0x2164, 0x5b61, 0x59b8, 0xa629, 0x8d73, 0x699a, 0x6111}}, + {{0x4cc3, 0xd29e, 0xf4a3, 0x3428, 0x2048, 0xeec9, 0x5f50, 0x99a4, + 0x6de9, 0x05f2, 0x5aa9, 0x5fd2, 0x98b4, 0x1adc, 0x225f, 0x777f}, + {0xe649, 0x37da, 0x5ba6, 0x5765, 0x3f4a, 0x8a1c, 0x2e79, 0xf550, + 0x1a54, 0xcd1e, 0x7218, 0x3c3c, 0x6311, 0xfe28, 0x95fb, 0xed97}, + {0xe9b6, 0x0c47, 0x3f0e, 0x849b, 0x11f8, 0xe599, 0x5e4d, 0xd618, + 0xa06d, 0x33a0, 0x9a3e, 0x44db, 0xded8, 0x10f0, 0x94d2, 0x81fb}}, + {{0x2e59, 0x7025, 0xd413, 0x455a, 0x1ce3, 0xbd45, 0x7263, 0x27f7, + 0x23e3, 0x518e, 0xbe06, 0xc8c4, 0xe332, 0x4276, 0x68b4, 0xb166}, + {0x596f, 0x0cf6, 0xc8ec, 0x787b, 0x04c1, 0x473c, 0xd2b8, 0x8d54, + 0x9cdf, 0x77f2, 0xd3f3, 0x6735, 0x0638, 0xf80e, 0x9467, 0xc6aa}, + {0xc7e7, 0x1822, 0xb62a, 0xec0d, 0x89cd, 0x7846, 0xbfa2, 0x35d5, + 0xfa38, 0x870f, 0x494b, 0x1697, 0x8b17, 0xf904, 0x10b6, 0x9822}}, + {{0x6d5b, 0x1d4f, 0x0aaf, 0x807b, 0x35fb, 0x7ee8, 0x00c6, 0x059a, + 0xddf0, 0x1fb1, 0xc38a, 0xd78e, 0x2aa4, 0x79e7, 0xad28, 0xc3f1}, + {0xe3bb, 0x174e, 0xe0a8, 0x74b6, 0xbd5b, 0x35f6, 0x6d23, 0x6328, + 0xc11f, 0x83e1, 0xf928, 0xa918, 0x838e, 0xbf43, 0xe243, 0xfffb}, + {0x9cf2, 0x6b8b, 0x3476, 0x9d06, 0xdcf2, 0xdb8a, 0x89cd, 0x4857, + 0x75c2, 0xabb8, 0x490b, 0xc9bd, 0x890e, 0xe36e, 0xd552, 0xfffa}}, + {{0x2f09, 0x9d62, 0xa9fc, 0xf090, 0xd6d1, 0x9d1d, 0x1828, 0xe413, + 0xc92b, 0x3d5a, 0x1373, 0x368c, 0xbaf2, 0x2158, 0x71eb, 0x08a3}, + {0x2f09, 0x1d62, 0x4630, 0x0de1, 0x06dc, 0xf7f1, 0xc161, 0x1e92, + 0x7495, 0x97e4, 0x94b6, 0xa39e, 0x4f1b, 0x18f8, 0x7bd4, 0x0c4c}, + {0xeb3d, 0x723d, 0x0907, 0x525b, 0x463a, 0x49a8, 0xc6b8, 0xce7f, + 0x740c, 0x0d7d, 0xa83b, 0x457f, 0xae8e, 0xc6af, 0xd331, 0x0475}}, + {{0x6abd, 0xc7af, 0x3e4e, 0x95fd, 0x8fc4, 0xee25, 0x1f9c, 0x0afe, + 0x291d, 0xcde0, 0x48f4, 0xb2e8, 0xf7af, 0x8f8d, 0x0bd6, 0x078d}, + {0x4037, 0xbf0e, 0x2081, 0xf363, 0x13b2, 0x381e, 0xfb6e, 0x818e, + 0x27e4, 0x5662, 0x18b0, 0x0cd2, 0x81f5, 0x9415, 0x0d6c, 0xf9fb}, + {0xd205, 0x0981, 0x0498, 0x1f08, 0xdb93, 0x1732, 0x0579, 0x1424, + 0xad95, 0x642f, 0x050c, 0x1d6d, 0xfc95, 0xfc4a, 0xd41b, 0x3521}}, + {{0xf23a, 0x4633, 0xaef4, 0x1a92, 0x3c8b, 0x1f09, 0x30f3, 0x4c56, + 0x2a2f, 0x4f62, 0xf5e4, 0x8329, 0x63cc, 0xb593, 0xec6a, 0xc428}, + {0x93a7, 0xfcf6, 0x606d, 0xd4b2, 0x2aad, 0x28b4, 0xc65b, 0x8998, + 0x4e08, 0xd178, 0x0900, 0xc82b, 0x7470, 0xa342, 0x7c0f, 0xffff}, + {0x315f, 0xf304, 0xeb7b, 0xe5c3, 0x1451, 0x6311, 0x8f37, 0x93a8, + 0x4a38, 0xa6c6, 0xe393, 0x1087, 0x6301, 0xd673, 0x4ec4, 0xffff}}, + {{0x892e, 0xeed0, 0x1165, 0xcbc1, 0x5545, 0xa280, 0x7243, 0x10c9, + 0x9536, 0x36af, 0xb3fc, 0x2d7c, 0xe8a5, 0x09d6, 0xe1d4, 0xe85d}, + {0xae09, 0xc28a, 0xd777, 0xbd80, 0x23d6, 0xf980, 0xeb7c, 0x4e0e, + 0xf7dc, 0x6475, 0xf10a, 0x2d33, 0x5dfd, 0x797a, 0x7f1c, 0xf71a}, + {0x4064, 0x8717, 0xd091, 0x80b0, 0x4527, 0x8442, 0xac8b, 0x9614, + 0xc633, 0x35f5, 0x7714, 0x2e83, 0x4aaa, 0xd2e4, 0x1acd, 0x0562}}, + {{0xdb64, 0x0937, 0x308b, 0x53b0, 0x00e8, 0xc77f, 0x2f30, 0x37f7, + 0x79ce, 0xeb7f, 0xde81, 0x9286, 0xafda, 0x0e62, 0xae00, 0x0067}, + {0x2cc7, 0xd362, 0xb161, 0x0557, 0x4ff2, 0xb9c8, 0x06fe, 0x5f2b, + 0xde33, 0x0190, 0x28c6, 0xb886, 0xee2b, 0x5a4e, 0x3289, 0x0185}, + {0x4215, 0x923e, 0xf34f, 0xb362, 0x88f8, 0xceec, 0xafdd, 0x7f42, + 0x0c57, 0x56b2, 0xa366, 0x6a08, 0x0826, 0xfb8f, 0x1b03, 0x0163}}, + {{0xa4ba, 0x8408, 0x810a, 0xdeba, 0x47a3, 0x853a, 0xeb64, 0x2f74, + 0x3039, 0x038c, 0x7fbb, 0x498e, 0xd1e9, 0x46fb, 0x5691, 0x32a4}, + {0xd749, 0xb49d, 0x20b7, 0x2af6, 0xd34a, 0xd2da, 0x0a10, 0xf781, + 0x58c9, 0x171f, 0x3cb6, 0x6337, 0x88cd, 0xcf1e, 0xb246, 0x7351}, + {0xf729, 0xcf0a, 0x96ea, 0x032c, 0x4a8f, 0x42fe, 0xbac8, 0xec65, + 0x1510, 0x0d75, 0x4c17, 0x8d29, 0xa03f, 0x8b7e, 0x2c49, 0x0000}}, + {{0x0fa4, 0x8e1c, 0x3788, 0xba3c, 0x8d52, 0xd89d, 0x12c8, 0xeced, + 0x9fe6, 0x9b88, 0xecf3, 0xe3c8, 0xac48, 0x76ed, 0xf23e, 0xda79}, + {0x1103, 0x227c, 0x5b00, 0x3fcf, 0xc5d0, 0x2d28, 0x8020, 0x4d1c, + 0xc6b9, 0x67f9, 0x6f39, 0x989a, 0xda53, 0x3847, 0xd416, 0xe0d0}, + {0xdd8e, 0xcf31, 0x3710, 0x7e44, 0xa511, 0x933c, 0x0cc3, 0x5145, + 0xf632, 0x5e1d, 0x038f, 0x5ce7, 0x7265, 0xda9d, 0xded6, 0x08f8}}, + {{0xe2c8, 0x91d5, 0xa5f5, 0x735f, 0x6b58, 0x56dc, 0xb39d, 0x5c4a, + 0x57d0, 0xa1c2, 0xd92f, 0x9ad4, 0xf7c4, 0x51dd, 0xaf5c, 0x0096}, + {0x1739, 0x7207, 0x7505, 0xbf35, 0x42de, 0x0a29, 0xa962, 0xdedf, + 0x53e8, 0x12bf, 0xcde7, 0xd8e2, 0x8d4d, 0x2c4b, 0xb1b1, 0x0628}, + {0x992d, 0xe3a7, 0xb422, 0xc198, 0x23ab, 0xa6ef, 0xb45d, 0x50da, + 0xa738, 0x014a, 0x2310, 0x85fb, 0x5fe8, 0x1b18, 0x1774, 0x03a7}}, + {{0x1f16, 0x2b09, 0x0236, 0xee90, 0xccf9, 0x9775, 0x8130, 0x4c91, + 0x9091, 0x310b, 0x6dc4, 0x86f6, 0xc2e8, 0xef60, 0xfc0e, 0xf3a4}, + {0x9f49, 0xac15, 0x02af, 0x110f, 0xc59d, 0x5677, 0xa1a9, 0x38d5, + 0x914f, 0xa909, 0x3a3a, 0x4a39, 0x3703, 0xea30, 0x73da, 0xffad}, + {0x15ed, 0xdd16, 0x83c7, 0x270a, 0x862f, 0xd8ad, 0xcaa1, 0x5f41, + 0x99a9, 0x3fc8, 0x7bb2, 0x360a, 0xb06d, 0xfadc, 0x1b36, 0xffa8}}, + {{0xc4e0, 0xb8fd, 0x5106, 0xe169, 0x754c, 0xa58c, 0xc413, 0x8224, + 0x5483, 0x63ec, 0xd477, 0x8473, 0x4778, 0x9281, 0x0000, 0x0000}, + {0x85e1, 0xff54, 0xb200, 0xe413, 0xf4f4, 0x4c0f, 0xfcec, 0xc183, + 0x60d3, 0x1b0c, 0x3834, 0x601c, 0x943c, 0xbe6e, 0x0002, 0x0000}, + {0xf4f8, 0xfd5e, 0x61ef, 0xece8, 0x9199, 0xe5c4, 0x05a6, 0xe6c3, + 0xc4ae, 0x8b28, 0x66b1, 0x8a95, 0x9ece, 0x8f4a, 0x0001, 0x0000}}, + {{0xeae9, 0xa1b4, 0xc6d8, 0x2411, 0x2b5a, 0x1dd0, 0x2dc9, 0xb57b, + 0x5ccd, 0x4957, 0xaf59, 0xa04b, 0x5f42, 0xab7c, 0x2826, 0x526f}, + {0xf407, 0x165a, 0xb724, 0x2f12, 0x2ea1, 0x470b, 0x4464, 0xbd35, + 0x606f, 0xd73e, 0x50d3, 0x8a7f, 0x8029, 0x7ffc, 0xbe31, 0x6cfb}, + {0x8171, 0x1f4c, 0xced2, 0x9c99, 0x6d7e, 0x5a0f, 0xfefb, 0x59e3, + 0xa0c8, 0xabd9, 0xc4c5, 0x57d3, 0xbfa3, 0x4f11, 0x96a2, 0x5a7d}}, + {{0xe068, 0x4cc0, 0x8bcd, 0xc903, 0x9e52, 0xb3e1, 0xd745, 0x0995, + 0xdd8f, 0xf14b, 0xd2ac, 0xd65a, 0xda1d, 0xa742, 0xbac5, 0x474c}, + {0x7481, 0xf2ad, 0x9757, 0x2d82, 0xb683, 0xb16b, 0x0002, 0x7b60, + 0x8f0c, 0x2594, 0x8f64, 0x3b7a, 0x3552, 0x8d9d, 0xb9d7, 0x67eb}, + {0xcaab, 0xb9a1, 0xf966, 0xe311, 0x5b34, 0x0fa0, 0x6abc, 0x8134, + 0xab3d, 0x90f6, 0x1984, 0x9232, 0xec17, 0x74e5, 0x2ceb, 0x434e}}, + {{0x0fb1, 0x7a55, 0x1a5c, 0x53eb, 0xd7b3, 0x7a01, 0xca32, 0x31f6, + 0x3b74, 0x679e, 0x1501, 0x6c57, 0xdb20, 0x8b7c, 0xd7d0, 0x8097}, + {0xb127, 0xb20c, 0xe3a2, 0x96f3, 0xe0d8, 0xd50c, 0x14b4, 0x0b40, + 0x6eeb, 0xa258, 0x99db, 0x3c8c, 0x0f51, 0x4198, 0x3887, 0xffd0}, + {0x0273, 0x9f8c, 0x9669, 0xbbba, 0x1c49, 0x767c, 0xc2af, 0x59f0, + 0x1366, 0xd397, 0x63ac, 0x6fe8, 0x1a9a, 0x1259, 0x01d0, 0x0016}}, + {{0x7876, 0x2a35, 0xa24a, 0x433e, 0x5501, 0x573c, 0xd76d, 0xcb82, + 0x1334, 0xb4a6, 0xf290, 0xc797, 0xeae9, 0x2b83, 0x1e2b, 0x8b14}, + {0x3885, 0x8aef, 0x9dea, 0x2b8c, 0xdd7c, 0xd7cd, 0xb0cc, 0x05ee, + 0x361b, 0x3800, 0xb0d4, 0x4c23, 0xbd3f, 0x5180, 0x9783, 0xff80}, + {0xab36, 0x3104, 0xdae8, 0x0704, 0x4a28, 0x6714, 0x824b, 0x0051, + 0x8134, 0x1f6a, 0x712d, 0x1f03, 0x03b2, 0xecac, 0x377d, 0xfef9}} + }; - /* check some small values with 5 as the order */ - secp256k1_scalar_set_int(&five, 5); - secp256k1_scalar_get_num(&order, &five); - for (i = 0; i < 10; ++i) { - secp256k1_scalar_set_int(&small, i); - secp256k1_scalar_get_num(&n, &small); - CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]); + int i, j, ok; + + /* Test known inputs/outputs */ + for (i = 0; (size_t)i < sizeof(CASES) / sizeof(CASES[0]); ++i) { + uint16_t out[16]; + test_modinv32_uint16(out, CASES[i][0], CASES[i][1]); + for (j = 0; j < 16; ++j) CHECK(out[j] == CASES[i][2][j]); +#ifdef SECP256K1_WIDEMUL_INT128 + test_modinv64_uint16(out, CASES[i][0], CASES[i][1]); + for (j = 0; j < 16; ++j) CHECK(out[j] == CASES[i][2][j]); +#endif } - /** test large values with 5 as group order */ - secp256k1_scalar_get_num(&order, &five); - /* we first need a scalar which is not a multiple of 5 */ - do { - secp256k1_num fiven; - random_scalar_order_test(&sqr); - secp256k1_scalar_get_num(&fiven, &five); - secp256k1_scalar_get_num(&n, &sqr); - secp256k1_num_mod(&n, &fiven); - } while (secp256k1_num_is_zero(&n)); - /* next force it to be a residue. 2 is a nonresidue mod 5 so we can - * just multiply by two, i.e. add the number to itself */ - if (secp256k1_num_jacobi(&n, &order) == -1) { - secp256k1_num_add(&n, &n, &n); - } - - /* test residue */ - CHECK(secp256k1_num_jacobi(&n, &order) == 1); - /* test nonresidue */ - secp256k1_num_add(&n, &n, &n); - CHECK(secp256k1_num_jacobi(&n, &order) == -1); - - /** test with secp group order as order */ - secp256k1_scalar_order_get_num(&order); - random_scalar_order_test(&sqr); - secp256k1_scalar_sqr(&sqr, &sqr); - /* test residue */ - secp256k1_scalar_get_num(&n, &sqr); - CHECK(secp256k1_num_jacobi(&n, &order) == 1); - /* test nonresidue */ - secp256k1_scalar_mul(&sqr, &sqr, &five); - secp256k1_scalar_get_num(&n, &sqr); - CHECK(secp256k1_num_jacobi(&n, &order) == -1); - /* test multiple of the order*/ - CHECK(secp256k1_num_jacobi(&order, &order) == 0); - - /* check one less than the order */ - secp256k1_scalar_set_int(&small, 1); - secp256k1_scalar_get_num(&n, &small); - secp256k1_num_sub(&n, &order, &n); - CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */ -} - -void run_num_smalltests(void) { + for (i = 0; i < 100 * COUNT; ++i) { + /* 256-bit numbers in 16-uint16_t's notation */ + static const uint16_t ZERO[16] = {0}; + uint16_t xd[16]; /* the number (in range [0,2^256)) to be inverted */ + uint16_t md[16]; /* the modulus (odd, in range [3,2^256)) */ + uint16_t id[16]; /* the inverse of xd mod md */ + + /* generate random xd and md, so that md is odd, md>1, xd>= 16; + } +} + +/* Negate a 256-bit number (represented as 16 uint16_t's in LE order) mod 2^256. */ +static void neg256(uint16_t* out, const uint16_t* a) { + int i; + uint32_t carry = 1; + for (i = 0; i < 16; ++i) { + carry += (uint16_t)~a[i]; + out[i] = carry; + carry >>= 16; + } +} + +/* Right-shift a 256-bit number (represented as 16 uint16_t's in LE order). */ +static void rshift256(uint16_t* out, const uint16_t* a, int n, int sign_extend) { + uint16_t sign = sign_extend && (a[15] >> 15); + int i, j; + for (i = 15; i >= 0; --i) { + uint16_t v = 0; + for (j = 0; j < 16; ++j) { + int frompos = i*16 + j + n; + if (frompos >= 256) { + v |= sign << j; + } else { + v |= ((uint16_t)((a[frompos >> 4] >> (frompos & 15)) & 1)) << j; + } + } + out[i] = v; + } +} + +/* Load a 64-bit unsigned integer into an array of 16 uint16_t's in LE order representing a 256-bit value. */ +static void load256u64(uint16_t* out, uint64_t v, int is_signed) { + int i; + uint64_t sign = is_signed && (v >> 63) ? UINT64_MAX : 0; + for (i = 0; i < 4; ++i) { + out[i] = v >> (16 * i); + } + for (i = 4; i < 16; ++i) { + out[i] = sign; + } +} + +/* Load a 128-bit unsigned integer into an array of 16 uint16_t's in LE order representing a 256-bit value. */ +static void load256two64(uint16_t* out, uint64_t hi, uint64_t lo, int is_signed) { int i; - for (i = 0; i < 100*count; i++) { - test_num_negate(); - test_num_add_sub(); - test_num_mod(); - test_num_jacobi(); + uint64_t sign = is_signed && (hi >> 63) ? UINT64_MAX : 0; + for (i = 0; i < 4; ++i) { + out[i] = lo >> (16 * i); + } + for (i = 4; i < 8; ++i) { + out[i] = hi >> (16 * (i - 4)); + } + for (i = 8; i < 16; ++i) { + out[i] = sign; + } +} + +/* Check whether the 256-bit value represented by array of 16-bit values is in range -2^127 < v < 2^127. */ +static int int256is127(const uint16_t* v) { + int all_0 = ((v[7] & 0x8000) == 0), all_1 = ((v[7] & 0x8000) == 0x8000); + int i; + for (i = 8; i < 16; ++i) { + if (v[i] != 0) all_0 = 0; + if (v[i] != 0xffff) all_1 = 0; + } + return all_0 || all_1; +} + +static void load256u128(uint16_t* out, const secp256k1_uint128* v) { + uint64_t lo = secp256k1_u128_to_u64(v), hi = secp256k1_u128_hi_u64(v); + load256two64(out, hi, lo, 0); +} + +static void load256i128(uint16_t* out, const secp256k1_int128* v) { + uint64_t lo; + int64_t hi; + secp256k1_int128 c = *v; + lo = secp256k1_i128_to_u64(&c); + secp256k1_i128_rshift(&c, 64); + hi = secp256k1_i128_to_i64(&c); + load256two64(out, hi, lo, 1); +} + +static void run_int128_test_case(void) { + unsigned char buf[32]; + uint64_t v[4]; + secp256k1_int128 swa, swz; + secp256k1_uint128 uwa, uwz; + uint64_t ub, uc; + int64_t sb, sc; + uint16_t rswa[16], rswz[32], rswr[32], ruwa[16], ruwz[32], ruwr[32]; + uint16_t rub[16], ruc[16], rsb[16], rsc[16]; + int i; + + /* Generate 32-byte random value. */ + testrand256_test(buf); + /* Convert into 4 64-bit integers. */ + for (i = 0; i < 4; ++i) { + uint64_t vi = 0; + int j; + for (j = 0; j < 8; ++j) vi = (vi << 8) + buf[8*i + j]; + v[i] = vi; + } + /* Convert those into a 128-bit value and two 64-bit values (signed and unsigned). */ + secp256k1_u128_load(&uwa, v[1], v[0]); + secp256k1_i128_load(&swa, v[1], v[0]); + ub = v[2]; + sb = v[2]; + uc = v[3]; + sc = v[3]; + /* Load those also into 16-bit array representations. */ + load256u128(ruwa, &uwa); + load256i128(rswa, &swa); + load256u64(rub, ub, 0); + load256u64(rsb, sb, 1); + load256u64(ruc, uc, 0); + load256u64(rsc, sc, 1); + /* test secp256k1_u128_mul */ + mulmod256(ruwr, rub, ruc, NULL); + secp256k1_u128_mul(&uwz, ub, uc); + load256u128(ruwz, &uwz); + CHECK(secp256k1_memcmp_var(ruwr, ruwz, 16) == 0); + /* test secp256k1_u128_accum_mul */ + mulmod256(ruwr, rub, ruc, NULL); + add256(ruwr, ruwr, ruwa); + uwz = uwa; + secp256k1_u128_accum_mul(&uwz, ub, uc); + load256u128(ruwz, &uwz); + CHECK(secp256k1_memcmp_var(ruwr, ruwz, 16) == 0); + /* test secp256k1_u128_accum_u64 */ + add256(ruwr, rub, ruwa); + uwz = uwa; + secp256k1_u128_accum_u64(&uwz, ub); + load256u128(ruwz, &uwz); + CHECK(secp256k1_memcmp_var(ruwr, ruwz, 16) == 0); + /* test secp256k1_u128_rshift */ + rshift256(ruwr, ruwa, uc % 128, 0); + uwz = uwa; + secp256k1_u128_rshift(&uwz, uc % 128); + load256u128(ruwz, &uwz); + CHECK(secp256k1_memcmp_var(ruwr, ruwz, 16) == 0); + /* test secp256k1_u128_to_u64 */ + CHECK(secp256k1_u128_to_u64(&uwa) == v[0]); + /* test secp256k1_u128_hi_u64 */ + CHECK(secp256k1_u128_hi_u64(&uwa) == v[1]); + /* test secp256k1_u128_from_u64 */ + secp256k1_u128_from_u64(&uwz, ub); + load256u128(ruwz, &uwz); + CHECK(secp256k1_memcmp_var(rub, ruwz, 16) == 0); + /* test secp256k1_u128_check_bits */ + { + int uwa_bits = 0; + int j; + for (j = 0; j < 128; ++j) { + if (ruwa[j / 16] >> (j % 16)) uwa_bits = 1 + j; + } + for (j = 0; j < 128; ++j) { + CHECK(secp256k1_u128_check_bits(&uwa, j) == (uwa_bits <= j)); + } + } + /* test secp256k1_i128_mul */ + mulmod256(rswr, rsb, rsc, NULL); + secp256k1_i128_mul(&swz, sb, sc); + load256i128(rswz, &swz); + CHECK(secp256k1_memcmp_var(rswr, rswz, 16) == 0); + /* test secp256k1_i128_accum_mul */ + mulmod256(rswr, rsb, rsc, NULL); + add256(rswr, rswr, rswa); + if (int256is127(rswr)) { + swz = swa; + secp256k1_i128_accum_mul(&swz, sb, sc); + load256i128(rswz, &swz); + CHECK(secp256k1_memcmp_var(rswr, rswz, 16) == 0); + } + /* test secp256k1_i128_det */ + { + uint16_t rsd[16], rse[16], rst[32]; + int64_t sd = v[0], se = v[1]; + load256u64(rsd, sd, 1); + load256u64(rse, se, 1); + mulmod256(rst, rsc, rsd, NULL); + neg256(rst, rst); + mulmod256(rswr, rsb, rse, NULL); + add256(rswr, rswr, rst); + secp256k1_i128_det(&swz, sb, sc, sd, se); + load256i128(rswz, &swz); + CHECK(secp256k1_memcmp_var(rswr, rswz, 16) == 0); + } + /* test secp256k1_i128_rshift */ + rshift256(rswr, rswa, uc % 127, 1); + swz = swa; + secp256k1_i128_rshift(&swz, uc % 127); + load256i128(rswz, &swz); + CHECK(secp256k1_memcmp_var(rswr, rswz, 16) == 0); + /* test secp256k1_i128_to_u64 */ + CHECK(secp256k1_i128_to_u64(&swa) == v[0]); + /* test secp256k1_i128_from_i64 */ + secp256k1_i128_from_i64(&swz, sb); + load256i128(rswz, &swz); + CHECK(secp256k1_memcmp_var(rsb, rswz, 16) == 0); + /* test secp256k1_i128_to_i64 */ + CHECK(secp256k1_i128_to_i64(&swz) == sb); + /* test secp256k1_i128_eq_var */ + { + int expect = (uc & 1); + swz = swa; + if (!expect) { + /* Make sure swz != swa */ + uint64_t v0c = v[0], v1c = v[1]; + if (ub & 64) { + v1c ^= (((uint64_t)1) << (ub & 63)); + } else { + v0c ^= (((uint64_t)1) << (ub & 63)); + } + secp256k1_i128_load(&swz, v1c, v0c); + } + CHECK(secp256k1_i128_eq_var(&swa, &swz) == expect); + } + /* test secp256k1_i128_check_pow2 (sign == 1) */ + { + int expect = (uc & 1); + int pos = ub % 127; + if (expect) { + /* If expect==1, set swz to exactly 2^pos. */ + uint64_t hi = 0; + uint64_t lo = 0; + if (pos >= 64) { + hi = (((uint64_t)1) << (pos & 63)); + } else { + lo = (((uint64_t)1) << (pos & 63)); + } + secp256k1_i128_load(&swz, hi, lo); + } else { + /* If expect==0, set swz = swa, but update expect=1 if swa happens to equal 2^pos. */ + if (pos >= 64) { + if ((v[1] == (((uint64_t)1) << (pos & 63))) && v[0] == 0) expect = 1; + } else { + if ((v[0] == (((uint64_t)1) << (pos & 63))) && v[1] == 0) expect = 1; + } + swz = swa; + } + CHECK(secp256k1_i128_check_pow2(&swz, pos, 1) == expect); + } + /* test secp256k1_i128_check_pow2 (sign == -1) */ + { + int expect = (uc & 1); + int pos = ub % 127; + if (expect) { + /* If expect==1, set swz to exactly -2^pos. */ + uint64_t hi = ~(uint64_t)0; + uint64_t lo = ~(uint64_t)0; + if (pos >= 64) { + hi <<= (pos & 63); + lo = 0; + } else { + lo <<= (pos & 63); + } + secp256k1_i128_load(&swz, hi, lo); + } else { + /* If expect==0, set swz = swa, but update expect=1 if swa happens to equal -2^pos. */ + if (pos >= 64) { + if ((v[1] == ((~(uint64_t)0) << (pos & 63))) && v[0] == 0) expect = 1; + } else { + if ((v[0] == ((~(uint64_t)0) << (pos & 63))) && v[1] == ~(uint64_t)0) expect = 1; + } + swz = swa; + } + CHECK(secp256k1_i128_check_pow2(&swz, pos, -1) == expect); + } +} + +static void run_int128_tests(void) { + { /* secp256k1_u128_accum_mul */ + secp256k1_uint128 res; + + /* Check secp256k1_u128_accum_mul overflow */ + secp256k1_u128_mul(&res, UINT64_MAX, UINT64_MAX); + secp256k1_u128_accum_mul(&res, UINT64_MAX, UINT64_MAX); + CHECK(secp256k1_u128_to_u64(&res) == 2); + CHECK(secp256k1_u128_hi_u64(&res) == 18446744073709551612U); + } + { /* secp256k1_u128_accum_mul */ + secp256k1_int128 res; + + /* Compute INT128_MAX = 2^127 - 1 with secp256k1_i128_accum_mul */ + secp256k1_i128_mul(&res, INT64_MAX, INT64_MAX); + secp256k1_i128_accum_mul(&res, INT64_MAX, INT64_MAX); + CHECK(secp256k1_i128_to_u64(&res) == 2); + secp256k1_i128_accum_mul(&res, 4, 9223372036854775807); + secp256k1_i128_accum_mul(&res, 1, 1); + CHECK(secp256k1_i128_to_u64(&res) == UINT64_MAX); + secp256k1_i128_rshift(&res, 64); + CHECK(secp256k1_i128_to_i64(&res) == INT64_MAX); + + /* Compute INT128_MIN = - 2^127 with secp256k1_i128_accum_mul */ + secp256k1_i128_mul(&res, INT64_MAX, INT64_MIN); + CHECK(secp256k1_i128_to_u64(&res) == (uint64_t)INT64_MIN); + secp256k1_i128_accum_mul(&res, INT64_MAX, INT64_MIN); + CHECK(secp256k1_i128_to_u64(&res) == 0); + secp256k1_i128_accum_mul(&res, 2, INT64_MIN); + CHECK(secp256k1_i128_to_u64(&res) == 0); + secp256k1_i128_rshift(&res, 64); + CHECK(secp256k1_i128_to_i64(&res) == INT64_MIN); + } + { + /* Randomized tests. */ + int i; + for (i = 0; i < 256 * COUNT; ++i) run_int128_test_case(); } } #endif /***** SCALAR TESTS *****/ -void scalar_test(void) { +static void scalar_test(void) { secp256k1_scalar s; secp256k1_scalar s1; secp256k1_scalar s2; -#ifndef USE_NUM_NONE - secp256k1_num snum, s1num, s2num; - secp256k1_num order, half_order; -#endif unsigned char c[32]; /* Set 's' to a random scalar, with value 'snum'. */ - random_scalar_order_test(&s); + testutil_random_scalar_order_test(&s); /* Set 's1' to a random scalar, with value 's1num'. */ - random_scalar_order_test(&s1); + testutil_random_scalar_order_test(&s1); /* Set 's2' to a random scalar, with value 'snum2', and byte array representation 'c'. */ - random_scalar_order_test(&s2); + testutil_random_scalar_order_test(&s2); secp256k1_scalar_get_b32(c, &s2); -#ifndef USE_NUM_NONE - secp256k1_scalar_get_num(&snum, &s); - secp256k1_scalar_get_num(&s1num, &s1); - secp256k1_scalar_get_num(&s2num, &s2); - - secp256k1_scalar_order_get_num(&order); - half_order = order; - secp256k1_num_shift(&half_order, 1); -#endif - { int i; /* Test that fetching groups of 4 bits from a scalar and recursing n(i)=16*n(i-1)+p(i) reconstructs it. */ @@ -669,7 +2034,7 @@ void scalar_test(void) { for (i = 0; i < 256; i += 4) { secp256k1_scalar t; int j; - secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits(&s, 256 - 4 - i, 4)); + secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits_limb32(&s, 256 - 4 - i, 4)); for (j = 0; j < 4; j++) { secp256k1_scalar_add(&n, &n, &n); } @@ -686,7 +2051,7 @@ void scalar_test(void) { while (i < 256) { secp256k1_scalar t; int j; - int now = secp256k1_rand_int(15) + 1; + int now = testrand_int(15) + 1; if (now + i > 256) { now = 256 - i; } @@ -700,122 +2065,6 @@ void scalar_test(void) { CHECK(secp256k1_scalar_eq(&n, &s)); } -#ifndef USE_NUM_NONE - { - /* Test that adding the scalars together is equal to adding their numbers together modulo the order. */ - secp256k1_num rnum; - secp256k1_num r2num; - secp256k1_scalar r; - secp256k1_num_add(&rnum, &snum, &s2num); - secp256k1_num_mod(&rnum, &order); - secp256k1_scalar_add(&r, &s, &s2); - secp256k1_scalar_get_num(&r2num, &r); - CHECK(secp256k1_num_eq(&rnum, &r2num)); - } - - { - /* Test that multiplying the scalars is equal to multiplying their numbers modulo the order. */ - secp256k1_scalar r; - secp256k1_num r2num; - secp256k1_num rnum; - secp256k1_num_mul(&rnum, &snum, &s2num); - secp256k1_num_mod(&rnum, &order); - secp256k1_scalar_mul(&r, &s, &s2); - secp256k1_scalar_get_num(&r2num, &r); - CHECK(secp256k1_num_eq(&rnum, &r2num)); - /* The result can only be zero if at least one of the factors was zero. */ - CHECK(secp256k1_scalar_is_zero(&r) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_zero(&s2))); - /* The results can only be equal to one of the factors if that factor was zero, or the other factor was one. */ - CHECK(secp256k1_num_eq(&rnum, &snum) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_one(&s2))); - CHECK(secp256k1_num_eq(&rnum, &s2num) == (secp256k1_scalar_is_zero(&s2) || secp256k1_scalar_is_one(&s))); - } - - { - secp256k1_scalar neg; - secp256k1_num negnum; - secp256k1_num negnum2; - /* Check that comparison with zero matches comparison with zero on the number. */ - CHECK(secp256k1_num_is_zero(&snum) == secp256k1_scalar_is_zero(&s)); - /* Check that comparison with the half order is equal to testing for high scalar. */ - CHECK(secp256k1_scalar_is_high(&s) == (secp256k1_num_cmp(&snum, &half_order) > 0)); - secp256k1_scalar_negate(&neg, &s); - secp256k1_num_sub(&negnum, &order, &snum); - secp256k1_num_mod(&negnum, &order); - /* Check that comparison with the half order is equal to testing for high scalar after negation. */ - CHECK(secp256k1_scalar_is_high(&neg) == (secp256k1_num_cmp(&negnum, &half_order) > 0)); - /* Negating should change the high property, unless the value was already zero. */ - CHECK((secp256k1_scalar_is_high(&s) == secp256k1_scalar_is_high(&neg)) == secp256k1_scalar_is_zero(&s)); - secp256k1_scalar_get_num(&negnum2, &neg); - /* Negating a scalar should be equal to (order - n) mod order on the number. */ - CHECK(secp256k1_num_eq(&negnum, &negnum2)); - secp256k1_scalar_add(&neg, &neg, &s); - /* Adding a number to its negation should result in zero. */ - CHECK(secp256k1_scalar_is_zero(&neg)); - secp256k1_scalar_negate(&neg, &neg); - /* Negating zero should still result in zero. */ - CHECK(secp256k1_scalar_is_zero(&neg)); - } - - { - /* Test secp256k1_scalar_mul_shift_var. */ - secp256k1_scalar r; - secp256k1_num one; - secp256k1_num rnum; - secp256k1_num rnum2; - unsigned char cone[1] = {0x01}; - unsigned int shift = 256 + secp256k1_rand_int(257); - secp256k1_scalar_mul_shift_var(&r, &s1, &s2, shift); - secp256k1_num_mul(&rnum, &s1num, &s2num); - secp256k1_num_shift(&rnum, shift - 1); - secp256k1_num_set_bin(&one, cone, 1); - secp256k1_num_add(&rnum, &rnum, &one); - secp256k1_num_shift(&rnum, 1); - secp256k1_scalar_get_num(&rnum2, &r); - CHECK(secp256k1_num_eq(&rnum, &rnum2)); - } - - { - /* test secp256k1_scalar_shr_int */ - secp256k1_scalar r; - int i; - random_scalar_order_test(&r); - for (i = 0; i < 100; ++i) { - int low; - int shift = 1 + secp256k1_rand_int(15); - int expected = r.d[0] % (1 << shift); - low = secp256k1_scalar_shr_int(&r, shift); - CHECK(expected == low); - } - } -#endif - - { - /* Test that scalar inverses are equal to the inverse of their number modulo the order. */ - if (!secp256k1_scalar_is_zero(&s)) { - secp256k1_scalar inv; -#ifndef USE_NUM_NONE - secp256k1_num invnum; - secp256k1_num invnum2; -#endif - secp256k1_scalar_inverse(&inv, &s); -#ifndef USE_NUM_NONE - secp256k1_num_mod_inverse(&invnum, &snum, &order); - secp256k1_scalar_get_num(&invnum2, &inv); - CHECK(secp256k1_num_eq(&invnum, &invnum2)); -#endif - secp256k1_scalar_mul(&inv, &inv, &s); - /* Multiplying a scalar with its inverse must result in one. */ - CHECK(secp256k1_scalar_is_one(&inv)); - secp256k1_scalar_inverse(&inv, &inv); - /* Inverting one must result in one. */ - CHECK(secp256k1_scalar_is_one(&inv)); -#ifndef USE_NUM_NONE - secp256k1_scalar_get_num(&invnum, &inv); - CHECK(secp256k1_num_is_one(&invnum)); -#endif - } - } - { /* Test commutativity of add. */ secp256k1_scalar r1, r2; @@ -829,7 +2078,7 @@ void scalar_test(void) { secp256k1_scalar b; int i; /* Test add_bit. */ - int bit = secp256k1_rand_bits(8); + int bit = testrand_bits(8); secp256k1_scalar_set_int(&b, 1); CHECK(secp256k1_scalar_is_one(&b)); for (i = 0; i < bit; i++) { @@ -886,72 +2135,117 @@ void scalar_test(void) { CHECK(secp256k1_scalar_eq(&r1, &r2)); } - { - /* Test square. */ - secp256k1_scalar r1, r2; - secp256k1_scalar_sqr(&r1, &s1); - secp256k1_scalar_mul(&r2, &s1, &s1); - CHECK(secp256k1_scalar_eq(&r1, &r2)); - } - { /* Test multiplicative identity. */ - secp256k1_scalar r1, v1; - secp256k1_scalar_set_int(&v1,1); - secp256k1_scalar_mul(&r1, &s1, &v1); + secp256k1_scalar r1; + secp256k1_scalar_mul(&r1, &s1, &secp256k1_scalar_one); CHECK(secp256k1_scalar_eq(&r1, &s1)); } { /* Test additive identity. */ - secp256k1_scalar r1, v0; - secp256k1_scalar_set_int(&v0,0); - secp256k1_scalar_add(&r1, &s1, &v0); + secp256k1_scalar r1; + secp256k1_scalar_add(&r1, &s1, &secp256k1_scalar_zero); CHECK(secp256k1_scalar_eq(&r1, &s1)); } { /* Test zero product property. */ - secp256k1_scalar r1, v0; - secp256k1_scalar_set_int(&v0,0); - secp256k1_scalar_mul(&r1, &s1, &v0); - CHECK(secp256k1_scalar_eq(&r1, &v0)); + secp256k1_scalar r1; + secp256k1_scalar_mul(&r1, &s1, &secp256k1_scalar_zero); + CHECK(secp256k1_scalar_eq(&r1, &secp256k1_scalar_zero)); + } + + { + /* Test halving. */ + secp256k1_scalar r; + secp256k1_scalar_add(&r, &s, &s); + secp256k1_scalar_half(&r, &r); + CHECK(secp256k1_scalar_eq(&r, &s)); } +} + +static void run_scalar_set_b32_seckey_tests(void) { + unsigned char b32[32]; + secp256k1_scalar s1; + secp256k1_scalar s2; + + /* Usually set_b32 and set_b32_seckey give the same result */ + testutil_random_scalar_order_b32(b32); + secp256k1_scalar_set_b32(&s1, b32, NULL); + CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 1); + CHECK(secp256k1_scalar_eq(&s1, &s2) == 1); + memset(b32, 0, sizeof(b32)); + CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0); + memset(b32, 0xFF, sizeof(b32)); + CHECK(secp256k1_scalar_set_b32_seckey(&s2, b32) == 0); } -void run_scalar_tests(void) { +static void run_scalar_tests(void) { int i; - for (i = 0; i < 128 * count; i++) { + for (i = 0; i < 128 * COUNT; i++) { scalar_test(); } + for (i = 0; i < COUNT; i++) { + run_scalar_set_b32_seckey_tests(); + } + + { + /* Check that the scalar constants secp256k1_scalar_zero and + secp256k1_scalar_one contain the expected values. */ + secp256k1_scalar zero, one; + + CHECK(secp256k1_scalar_is_zero(&secp256k1_scalar_zero)); + secp256k1_scalar_set_int(&zero, 0); + CHECK(secp256k1_scalar_eq(&zero, &secp256k1_scalar_zero)); + + CHECK(secp256k1_scalar_is_one(&secp256k1_scalar_one)); + secp256k1_scalar_set_int(&one, 1); + CHECK(secp256k1_scalar_eq(&one, &secp256k1_scalar_one)); + } { /* (-1)+1 should be zero. */ - secp256k1_scalar s, o; - secp256k1_scalar_set_int(&s, 1); - CHECK(secp256k1_scalar_is_one(&s)); - secp256k1_scalar_negate(&o, &s); - secp256k1_scalar_add(&o, &o, &s); + secp256k1_scalar o; + secp256k1_scalar_negate(&o, &secp256k1_scalar_one); + secp256k1_scalar_add(&o, &o, &secp256k1_scalar_one); CHECK(secp256k1_scalar_is_zero(&o)); secp256k1_scalar_negate(&o, &o); CHECK(secp256k1_scalar_is_zero(&o)); } -#ifndef USE_NUM_NONE { - /* A scalar with value of the curve order should be 0. */ - secp256k1_num order; - secp256k1_scalar zero; - unsigned char bin[32]; - int overflow = 0; - secp256k1_scalar_order_get_num(&order); - secp256k1_num_get_bin(bin, 32, &order); - secp256k1_scalar_set_b32(&zero, bin, &overflow); - CHECK(overflow == 1); - CHECK(secp256k1_scalar_is_zero(&zero)); + /* Test that halving and doubling roundtrips on some fixed values. */ + static const secp256k1_scalar HALF_TESTS[] = { + /* 0 */ + SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0), + /* 1 */ + SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1), + /* -1 */ + SECP256K1_SCALAR_CONST(0xfffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffeul, 0xbaaedce6ul, 0xaf48a03bul, 0xbfd25e8cul, 0xd0364140ul), + /* -2 (largest odd value) */ + SECP256K1_SCALAR_CONST(0xfffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffeul, 0xbaaedce6ul, 0xaf48a03bul, 0xbfd25e8cul, 0xd036413Ful), + /* Half the secp256k1 order */ + SECP256K1_SCALAR_CONST(0x7ffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffful, 0x5d576e73ul, 0x57a4501dul, 0xdfe92f46ul, 0x681b20a0ul), + /* Half the secp256k1 order + 1 */ + SECP256K1_SCALAR_CONST(0x7ffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffful, 0x5d576e73ul, 0x57a4501dul, 0xdfe92f46ul, 0x681b20a1ul), + /* 2^255 */ + SECP256K1_SCALAR_CONST(0x80000000ul, 0, 0, 0, 0, 0, 0, 0), + /* 2^255 - 1 */ + SECP256K1_SCALAR_CONST(0x7ffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffful, 0xfffffffful), + }; + unsigned n; + for (n = 0; n < sizeof(HALF_TESTS) / sizeof(HALF_TESTS[0]); ++n) { + secp256k1_scalar s; + secp256k1_scalar_half(&s, &HALF_TESTS[n]); + secp256k1_scalar_add(&s, &s, &s); + CHECK(secp256k1_scalar_eq(&s, &HALF_TESTS[n])); + secp256k1_scalar_add(&s, &s, &s); + secp256k1_scalar_half(&s, &s); + CHECK(secp256k1_scalar_eq(&s, &HALF_TESTS[n])); + } } -#endif { /* Does check_overflow check catch all ones? */ @@ -972,12 +2266,9 @@ void run_scalar_tests(void) { secp256k1_scalar y; secp256k1_scalar z; secp256k1_scalar zz; - secp256k1_scalar one; secp256k1_scalar r1; secp256k1_scalar r2; -#if defined(USE_SCALAR_INV_NUM) secp256k1_scalar zzv; -#endif int overflow; unsigned char chal[33][2][32] = { {{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, @@ -1511,7 +2802,6 @@ void run_scalar_tests(void) { 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}} }; - secp256k1_scalar_set_int(&one, 1); for (i = 0; i < 33; i++) { secp256k1_scalar_set_b32(&x, chal[i][0], &overflow); CHECK(!overflow); @@ -1522,91 +2812,40 @@ void run_scalar_tests(void) { secp256k1_scalar_set_b32(&r2, res[i][1], &overflow); CHECK(!overflow); secp256k1_scalar_mul(&z, &x, &y); - CHECK(!secp256k1_scalar_check_overflow(&z)); CHECK(secp256k1_scalar_eq(&r1, &z)); if (!secp256k1_scalar_is_zero(&y)) { secp256k1_scalar_inverse(&zz, &y); - CHECK(!secp256k1_scalar_check_overflow(&zz)); -#if defined(USE_SCALAR_INV_NUM) secp256k1_scalar_inverse_var(&zzv, &y); CHECK(secp256k1_scalar_eq(&zzv, &zz)); -#endif secp256k1_scalar_mul(&z, &z, &zz); - CHECK(!secp256k1_scalar_check_overflow(&z)); CHECK(secp256k1_scalar_eq(&x, &z)); secp256k1_scalar_mul(&zz, &zz, &y); - CHECK(!secp256k1_scalar_check_overflow(&zz)); - CHECK(secp256k1_scalar_eq(&one, &zz)); + CHECK(secp256k1_scalar_eq(&secp256k1_scalar_one, &zz)); } secp256k1_scalar_mul(&z, &x, &x); - CHECK(!secp256k1_scalar_check_overflow(&z)); - secp256k1_scalar_sqr(&zz, &x); - CHECK(!secp256k1_scalar_check_overflow(&zz)); - CHECK(secp256k1_scalar_eq(&zz, &z)); - CHECK(secp256k1_scalar_eq(&r2, &zz)); + CHECK(secp256k1_scalar_eq(&r2, &z)); } } } /***** FIELD TESTS *****/ -void random_fe(secp256k1_fe *x) { - unsigned char bin[32]; - do { - secp256k1_rand256(bin); - if (secp256k1_fe_set_b32(x, bin)) { - return; - } - } while(1); -} - -void random_fe_test(secp256k1_fe *x) { - unsigned char bin[32]; - do { - secp256k1_rand256_test(bin); - if (secp256k1_fe_set_b32(x, bin)) { - return; - } - } while(1); -} - -void random_fe_non_zero(secp256k1_fe *nz) { - int tries = 10; - while (--tries >= 0) { - random_fe(nz); - secp256k1_fe_normalize(nz); - if (!secp256k1_fe_is_zero(nz)) { - break; - } - } - /* Infinitesimal probability of spurious failure here */ - CHECK(tries >= 0); -} - -void random_fe_non_square(secp256k1_fe *ns) { +static void random_fe_non_square(secp256k1_fe *ns) { secp256k1_fe r; - random_fe_non_zero(ns); + testutil_random_fe_non_zero(ns); if (secp256k1_fe_sqrt(&r, ns)) { secp256k1_fe_negate(ns, ns, 1); } } -int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { +static int fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { secp256k1_fe an = *a; secp256k1_fe bn = *b; secp256k1_fe_normalize_weak(&an); - secp256k1_fe_normalize_var(&bn); - return secp256k1_fe_equal_var(&an, &bn); -} - -int check_fe_inverse(const secp256k1_fe *a, const secp256k1_fe *ai) { - secp256k1_fe x; - secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); - secp256k1_fe_mul(&x, a, ai); - return check_fe_equal(&x, &one); + return secp256k1_fe_equal(&an, &bn); } -void run_field_convert(void) { +static void run_field_convert(void) { static const unsigned char b32[32] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, @@ -1625,63 +2864,204 @@ void run_field_convert(void) { unsigned char b322[32]; secp256k1_fe_storage fes2; /* Check conversions to fe. */ - CHECK(secp256k1_fe_set_b32(&fe2, b32)); - CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + CHECK(secp256k1_fe_set_b32_limit(&fe2, b32)); + CHECK(secp256k1_fe_equal(&fe, &fe2)); secp256k1_fe_from_storage(&fe2, &fes); - CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + CHECK(secp256k1_fe_equal(&fe, &fe2)); /* Check conversion from fe. */ secp256k1_fe_get_b32(b322, &fe); - CHECK(memcmp(b322, b32, 32) == 0); + CHECK(secp256k1_memcmp_var(b322, b32, 32) == 0); secp256k1_fe_to_storage(&fes2, &fe); - CHECK(memcmp(&fes2, &fes, sizeof(fes)) == 0); + CHECK(secp256k1_memcmp_var(&fes2, &fes, sizeof(fes)) == 0); +} + +static void run_field_be32_overflow(void) { + { + static const unsigned char zero_overflow[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, + }; + static const unsigned char zero[32] = { 0x00 }; + unsigned char out[32]; + secp256k1_fe fe; + CHECK(secp256k1_fe_set_b32_limit(&fe, zero_overflow) == 0); + secp256k1_fe_set_b32_mod(&fe, zero_overflow); + CHECK(secp256k1_fe_normalizes_to_zero(&fe) == 1); + secp256k1_fe_normalize(&fe); + CHECK(secp256k1_fe_is_zero(&fe) == 1); + secp256k1_fe_get_b32(out, &fe); + CHECK(secp256k1_memcmp_var(out, zero, 32) == 0); + } + { + static const unsigned char one_overflow[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30, + }; + static const unsigned char one[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + unsigned char out[32]; + secp256k1_fe fe; + CHECK(secp256k1_fe_set_b32_limit(&fe, one_overflow) == 0); + secp256k1_fe_set_b32_mod(&fe, one_overflow); + secp256k1_fe_normalize(&fe); + CHECK(secp256k1_fe_cmp_var(&fe, &secp256k1_fe_one) == 0); + secp256k1_fe_get_b32(out, &fe); + CHECK(secp256k1_memcmp_var(out, one, 32) == 0); + } + { + static const unsigned char ff_overflow[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + static const unsigned char ff[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xD0, + }; + unsigned char out[32]; + secp256k1_fe fe; + const secp256k1_fe fe_ff = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0x01, 0x000003d0); + CHECK(secp256k1_fe_set_b32_limit(&fe, ff_overflow) == 0); + secp256k1_fe_set_b32_mod(&fe, ff_overflow); + secp256k1_fe_normalize(&fe); + CHECK(secp256k1_fe_cmp_var(&fe, &fe_ff) == 0); + secp256k1_fe_get_b32(out, &fe); + CHECK(secp256k1_memcmp_var(out, ff, 32) == 0); + } +} + +/* Returns true if two field elements have the same representation. */ +static int fe_identical(const secp256k1_fe *a, const secp256k1_fe *b) { + int ret = 1; + /* Compare the struct member that holds the limbs. */ + ret &= (secp256k1_memcmp_var(a->n, b->n, sizeof(a->n)) == 0); + return ret; } -int fe_memcmp(const secp256k1_fe *a, const secp256k1_fe *b) { - secp256k1_fe t = *b; +static void run_field_half(void) { + secp256k1_fe t, u; + int m; + + /* Check magnitude 0 input */ + secp256k1_fe_get_bounds(&t, 0); + secp256k1_fe_half(&t); #ifdef VERIFY - t.magnitude = a->magnitude; - t.normalized = a->normalized; + CHECK(t.magnitude == 1); + CHECK(t.normalized == 0); #endif - return memcmp(a, &t, sizeof(secp256k1_fe)); + CHECK(secp256k1_fe_normalizes_to_zero(&t)); + + /* Check non-zero magnitudes in the supported range */ + for (m = 1; m < 32; m++) { + /* Check max-value input */ + secp256k1_fe_get_bounds(&t, m); + + u = t; + secp256k1_fe_half(&u); +#ifdef VERIFY + CHECK(u.magnitude == (m >> 1) + 1); + CHECK(u.normalized == 0); +#endif + secp256k1_fe_normalize_weak(&u); + secp256k1_fe_add(&u, &u); + CHECK(fe_equal(&t, &u)); + + /* Check worst-case input: ensure the LSB is 1 so that P will be added, + * which will also cause all carries to be 1, since all limbs that can + * generate a carry are initially even and all limbs of P are odd in + * every existing field implementation. */ + secp256k1_fe_get_bounds(&t, m); + CHECK(t.n[0] > 0); + CHECK((t.n[0] & 1) == 0); + --t.n[0]; + + u = t; + secp256k1_fe_half(&u); +#ifdef VERIFY + CHECK(u.magnitude == (m >> 1) + 1); + CHECK(u.normalized == 0); +#endif + secp256k1_fe_normalize_weak(&u); + secp256k1_fe_add(&u, &u); + CHECK(fe_equal(&t, &u)); + } } -void run_field_misc(void) { +static void run_field_misc(void) { secp256k1_fe x; secp256k1_fe y; secp256k1_fe z; secp256k1_fe q; + int v; secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); int i, j; - for (i = 0; i < 5*count; i++) { + for (i = 0; i < 1000 * COUNT; i++) { secp256k1_fe_storage xs, ys, zs; - random_fe(&x); - random_fe_non_zero(&y); + if (i & 1) { + testutil_random_fe(&x); + } else { + testutil_random_fe_test(&x); + } + testutil_random_fe_non_zero(&y); + v = testrand_bits(15); + /* Test that fe_add_int is equivalent to fe_set_int + fe_add. */ + secp256k1_fe_set_int(&q, v); /* q = v */ + z = x; /* z = x */ + secp256k1_fe_add(&z, &q); /* z = x+v */ + q = x; /* q = x */ + secp256k1_fe_add_int(&q, v); /* q = x+v */ + CHECK(fe_equal(&q, &z)); /* Test the fe equality and comparison operations. */ CHECK(secp256k1_fe_cmp_var(&x, &x) == 0); - CHECK(secp256k1_fe_equal_var(&x, &x)); + CHECK(secp256k1_fe_equal(&x, &x)); z = x; secp256k1_fe_add(&z,&y); /* Test fe conditional move; z is not normalized here. */ q = x; secp256k1_fe_cmov(&x, &z, 0); - VERIFY_CHECK(!x.normalized && x.magnitude == z.magnitude); +#ifdef VERIFY + CHECK(!x.normalized); + CHECK((x.magnitude == q.magnitude) || (x.magnitude == z.magnitude)); + CHECK((x.magnitude >= q.magnitude) && (x.magnitude >= z.magnitude)); +#endif + x = q; secp256k1_fe_cmov(&x, &x, 1); - CHECK(fe_memcmp(&x, &z) != 0); - CHECK(fe_memcmp(&x, &q) == 0); + CHECK(!fe_identical(&x, &z)); + CHECK(fe_identical(&x, &q)); secp256k1_fe_cmov(&q, &z, 1); - VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude); - CHECK(fe_memcmp(&q, &z) == 0); +#ifdef VERIFY + CHECK(!q.normalized); + CHECK((q.magnitude == x.magnitude) || (q.magnitude == z.magnitude)); + CHECK((q.magnitude >= x.magnitude) && (q.magnitude >= z.magnitude)); +#endif + CHECK(fe_identical(&q, &z)); + q = z; secp256k1_fe_normalize_var(&x); secp256k1_fe_normalize_var(&z); - CHECK(!secp256k1_fe_equal_var(&x, &z)); + CHECK(!secp256k1_fe_equal(&x, &z)); secp256k1_fe_normalize_var(&q); secp256k1_fe_cmov(&q, &z, (i&1)); - VERIFY_CHECK(q.normalized && q.magnitude == 1); +#ifdef VERIFY + CHECK(q.normalized && q.magnitude == 1); +#endif for (j = 0; j < 6; j++) { - secp256k1_fe_negate(&z, &z, j+1); + secp256k1_fe_negate_unchecked(&z, &z, j+1); secp256k1_fe_normalize_var(&q); secp256k1_fe_cmov(&q, &z, (j&1)); - VERIFY_CHECK(!q.normalized && q.magnitude == (j+2)); +#ifdef VERIFY + CHECK(!q.normalized && q.magnitude == z.magnitude); +#endif } secp256k1_fe_normalize_var(&z); /* Test storage conversion and conditional moves. */ @@ -1690,9 +3070,9 @@ void run_field_misc(void) { secp256k1_fe_to_storage(&zs, &z); secp256k1_fe_storage_cmov(&zs, &xs, 0); secp256k1_fe_storage_cmov(&zs, &zs, 1); - CHECK(memcmp(&xs, &zs, sizeof(xs)) != 0); + CHECK(secp256k1_memcmp_var(&xs, &zs, sizeof(xs)) != 0); secp256k1_fe_storage_cmov(&ys, &xs, 1); - CHECK(memcmp(&xs, &ys, sizeof(xs)) == 0); + CHECK(secp256k1_memcmp_var(&xs, &ys, sizeof(xs)) == 0); secp256k1_fe_from_storage(&x, &xs); secp256k1_fe_from_storage(&y, &ys); secp256k1_fe_from_storage(&z, &zs); @@ -1701,85 +3081,124 @@ void run_field_misc(void) { secp256k1_fe_add(&y, &x); z = x; secp256k1_fe_mul_int(&z, 3); - CHECK(check_fe_equal(&y, &z)); + CHECK(fe_equal(&y, &z)); secp256k1_fe_add(&y, &x); secp256k1_fe_add(&z, &x); - CHECK(check_fe_equal(&z, &y)); + CHECK(fe_equal(&z, &y)); z = x; secp256k1_fe_mul_int(&z, 5); secp256k1_fe_mul(&q, &x, &fe5); - CHECK(check_fe_equal(&z, &q)); + CHECK(fe_equal(&z, &q)); secp256k1_fe_negate(&x, &x, 1); secp256k1_fe_add(&z, &x); secp256k1_fe_add(&q, &x); - CHECK(check_fe_equal(&y, &z)); - CHECK(check_fe_equal(&q, &y)); + CHECK(fe_equal(&y, &z)); + CHECK(fe_equal(&q, &y)); + /* Check secp256k1_fe_half. */ + z = x; + secp256k1_fe_half(&z); + secp256k1_fe_add(&z, &z); + CHECK(fe_equal(&x, &z)); + secp256k1_fe_add(&z, &z); + secp256k1_fe_half(&z); + CHECK(fe_equal(&x, &z)); } } -void run_field_inv(void) { - secp256k1_fe x, xi, xii; +static void test_fe_mul(const secp256k1_fe* a, const secp256k1_fe* b, int use_sqr) +{ + secp256k1_fe c, an, bn; + /* Variables in BE 32-byte format. */ + unsigned char a32[32], b32[32], c32[32]; + /* Variables in LE 16x uint16_t format. */ + uint16_t a16[16], b16[16], c16[16]; + /* Field modulus in LE 16x uint16_t format. */ + static const uint16_t m16[16] = { + 0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + }; + uint16_t t16[32]; int i; - for (i = 0; i < 10*count; i++) { - random_fe_non_zero(&x); - secp256k1_fe_inv(&xi, &x); - CHECK(check_fe_inverse(&x, &xi)); - secp256k1_fe_inv(&xii, &xi); - CHECK(check_fe_equal(&x, &xii)); + + /* Compute C = A * B in fe format. */ + c = *a; + if (use_sqr) { + secp256k1_fe_sqr(&c, &c); + } else { + secp256k1_fe_mul(&c, &c, b); } + + /* Convert A, B, C into LE 16x uint16_t format. */ + an = *a; + bn = *b; + secp256k1_fe_normalize_var(&c); + secp256k1_fe_normalize_var(&an); + secp256k1_fe_normalize_var(&bn); + secp256k1_fe_get_b32(a32, &an); + secp256k1_fe_get_b32(b32, &bn); + secp256k1_fe_get_b32(c32, &c); + for (i = 0; i < 16; ++i) { + a16[i] = a32[31 - 2*i] + ((uint16_t)a32[30 - 2*i] << 8); + b16[i] = b32[31 - 2*i] + ((uint16_t)b32[30 - 2*i] << 8); + c16[i] = c32[31 - 2*i] + ((uint16_t)c32[30 - 2*i] << 8); + } + /* Compute T = A * B in LE 16x uint16_t format. */ + mulmod256(t16, a16, b16, m16); + /* Compare */ + CHECK(secp256k1_memcmp_var(t16, c16, 32) == 0); } -void run_field_inv_var(void) { - secp256k1_fe x, xi, xii; +static void run_fe_mul(void) { int i; - for (i = 0; i < 10*count; i++) { - random_fe_non_zero(&x); - secp256k1_fe_inv_var(&xi, &x); - CHECK(check_fe_inverse(&x, &xi)); - secp256k1_fe_inv_var(&xii, &xi); - CHECK(check_fe_equal(&x, &xii)); + for (i = 0; i < 100 * COUNT; ++i) { + secp256k1_fe a, b, c, d; + testutil_random_fe(&a); + testutil_random_fe_magnitude(&a, 8); + testutil_random_fe(&b); + testutil_random_fe_magnitude(&b, 8); + testutil_random_fe_test(&c); + testutil_random_fe_magnitude(&c, 8); + testutil_random_fe_test(&d); + testutil_random_fe_magnitude(&d, 8); + test_fe_mul(&a, &a, 1); + test_fe_mul(&c, &c, 1); + test_fe_mul(&a, &b, 0); + test_fe_mul(&a, &c, 0); + test_fe_mul(&c, &b, 0); + test_fe_mul(&c, &d, 0); } } -void run_field_inv_all_var(void) { - secp256k1_fe x[16], xi[16], xii[16]; +static void run_sqr(void) { int i; - /* Check it's safe to call for 0 elements */ - secp256k1_fe_inv_all_var(xi, x, 0); - for (i = 0; i < count; i++) { - size_t j; - size_t len = secp256k1_rand_int(15) + 1; - for (j = 0; j < len; j++) { - random_fe_non_zero(&x[j]); - } - secp256k1_fe_inv_all_var(xi, x, len); - for (j = 0; j < len; j++) { - CHECK(check_fe_inverse(&x[j], &xi[j])); - } - secp256k1_fe_inv_all_var(xii, xi, len); - for (j = 0; j < len; j++) { - CHECK(check_fe_equal(&x[j], &xii[j])); - } - } -} + secp256k1_fe x, y, lhs, rhs, tmp; -void run_sqr(void) { - secp256k1_fe x, s; + secp256k1_fe_set_int(&x, 1); + secp256k1_fe_negate(&x, &x, 1); - { - int i; - secp256k1_fe_set_int(&x, 1); - secp256k1_fe_negate(&x, &x, 1); + for (i = 1; i <= 512; ++i) { + secp256k1_fe_mul_int(&x, 2); + secp256k1_fe_normalize(&x); - for (i = 1; i <= 512; ++i) { - secp256k1_fe_mul_int(&x, 2); - secp256k1_fe_normalize(&x); - secp256k1_fe_sqr(&s, &x); - } + /* Check that (x+y)*(x-y) = x^2 - y*2 for some random values y */ + testutil_random_fe_test(&y); + + lhs = x; + secp256k1_fe_add(&lhs, &y); /* lhs = x+y */ + secp256k1_fe_negate(&tmp, &y, 1); /* tmp = -y */ + secp256k1_fe_add(&tmp, &x); /* tmp = x-y */ + secp256k1_fe_mul(&lhs, &lhs, &tmp); /* lhs = (x+y)*(x-y) */ + + secp256k1_fe_sqr(&rhs, &x); /* rhs = x^2 */ + secp256k1_fe_sqr(&tmp, &y); /* tmp = y^2 */ + secp256k1_fe_negate(&tmp, &tmp, 1); /* tmp = -y^2 */ + secp256k1_fe_add(&rhs, &tmp); /* rhs = x^2 - y^2 */ + + CHECK(fe_equal(&lhs, &rhs)); } } -void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { +static void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { secp256k1_fe r1, r2; int v = secp256k1_fe_sqrt(&r1, a); CHECK((v == 0) == (k == NULL)); @@ -1793,7 +3212,7 @@ void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { } } -void run_sqrt(void) { +static void run_sqrt(void) { secp256k1_fe ns, x, s, t; int i; @@ -1815,11 +3234,13 @@ void run_sqrt(void) { for (i = 0; i < 10; i++) { int j; random_fe_non_square(&ns); - for (j = 0; j < count; j++) { - random_fe(&x); + for (j = 0; j < COUNT; j++) { + testutil_random_fe(&x); secp256k1_fe_sqr(&s, &x); + CHECK(secp256k1_fe_is_square_var(&s)); test_sqrt(&s, &x); secp256k1_fe_negate(&t, &s, 1); + CHECK(!secp256k1_fe_is_square_var(&t)); test_sqrt(&t, NULL); secp256k1_fe_mul(&t, &s, &ns); test_sqrt(&t, NULL); @@ -1827,19 +3248,394 @@ void run_sqrt(void) { } } -/***** GROUP TESTS *****/ +/***** FIELD/SCALAR INVERSE TESTS *****/ + +static const secp256k1_scalar scalar_minus_one = SECP256K1_SCALAR_CONST( + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, + 0xBAAEDCE6, 0xAF48A03B, 0xBFD25E8C, 0xD0364140 +); + +static const secp256k1_fe fe_minus_one = SECP256K1_FE_CONST( + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFC2E +); + +/* These tests test the following identities: + * + * for x==0: 1/x == 0 + * for x!=0: x*(1/x) == 1 + * for x!=0 and x!=1: 1/(1/x - 1) + 1 == -1/(x-1) + */ + +static void test_inverse_scalar(secp256k1_scalar* out, const secp256k1_scalar* x, int var) +{ + secp256k1_scalar l, r, t; + + (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse)(&l, x); /* l = 1/x */ + if (out) *out = l; + if (secp256k1_scalar_is_zero(x)) { + CHECK(secp256k1_scalar_is_zero(&l)); + return; + } + secp256k1_scalar_mul(&t, x, &l); /* t = x*(1/x) */ + CHECK(secp256k1_scalar_is_one(&t)); /* x*(1/x) == 1 */ + secp256k1_scalar_add(&r, x, &scalar_minus_one); /* r = x-1 */ + if (secp256k1_scalar_is_zero(&r)) return; + (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse)(&r, &r); /* r = 1/(x-1) */ + secp256k1_scalar_add(&l, &scalar_minus_one, &l); /* l = 1/x-1 */ + (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse)(&l, &l); /* l = 1/(1/x-1) */ + secp256k1_scalar_add(&l, &l, &secp256k1_scalar_one); /* l = 1/(1/x-1)+1 */ + secp256k1_scalar_add(&l, &r, &l); /* l = 1/(1/x-1)+1 + 1/(x-1) */ + CHECK(secp256k1_scalar_is_zero(&l)); /* l == 0 */ +} + +static void test_inverse_field(secp256k1_fe* out, const secp256k1_fe* x, int var) +{ + secp256k1_fe l, r, t; + + (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&l, x) ; /* l = 1/x */ + if (out) *out = l; + t = *x; /* t = x */ + if (secp256k1_fe_normalizes_to_zero_var(&t)) { + CHECK(secp256k1_fe_normalizes_to_zero(&l)); + return; + } + secp256k1_fe_mul(&t, x, &l); /* t = x*(1/x) */ + secp256k1_fe_add(&t, &fe_minus_one); /* t = x*(1/x)-1 */ + CHECK(secp256k1_fe_normalizes_to_zero(&t)); /* x*(1/x)-1 == 0 */ + r = *x; /* r = x */ + secp256k1_fe_add(&r, &fe_minus_one); /* r = x-1 */ + if (secp256k1_fe_normalizes_to_zero_var(&r)) return; + (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&r, &r); /* r = 1/(x-1) */ + secp256k1_fe_add(&l, &fe_minus_one); /* l = 1/x-1 */ + (var ? secp256k1_fe_inv_var : secp256k1_fe_inv)(&l, &l); /* l = 1/(1/x-1) */ + secp256k1_fe_add_int(&l, 1); /* l = 1/(1/x-1)+1 */ + secp256k1_fe_add(&l, &r); /* l = 1/(1/x-1)+1 + 1/(x-1) */ + CHECK(secp256k1_fe_normalizes_to_zero_var(&l)); /* l == 0 */ +} + +static void run_inverse_tests(void) +{ + /* Fixed test cases for field inverses: pairs of (x, 1/x) mod p. */ + static const secp256k1_fe fe_cases[][2] = { + /* 0 */ + {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), + SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0)}, + /* 1 */ + {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), + SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1)}, + /* -1 */ + {SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e), + SECP256K1_FE_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xfffffc2e)}, + /* 2 */ + {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2), + SECP256K1_FE_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7ffffe18)}, + /* 2**128 */ + {SECP256K1_FE_CONST(0, 0, 0, 1, 0, 0, 0, 0), + SECP256K1_FE_CONST(0xbcb223fe, 0xdc24a059, 0xd838091d, 0xd2253530, 0xffffffff, 0xffffffff, 0xffffffff, 0x434dd931)}, + /* Input known to need 637 divsteps */ + {SECP256K1_FE_CONST(0xe34e9c95, 0x6bee8a84, 0x0dcb632a, 0xdb8a1320, 0x66885408, 0x06f3f996, 0x7c11ca84, 0x19199ec3), + SECP256K1_FE_CONST(0xbd2cbd8f, 0x1c536828, 0x9bccda44, 0x2582ac0c, 0x870152b0, 0x8a3f09fb, 0x1aaadf92, 0x19b618e5)}, + /* Input known to need 567 divsteps starting with delta=1/2. */ + {SECP256K1_FE_CONST(0xf6bc3ba3, 0x636451c4, 0x3e46357d, 0x2c21d619, 0x0988e234, 0x15985661, 0x6672982b, 0xa7549bfc), + SECP256K1_FE_CONST(0xb024fdc7, 0x5547451e, 0x426c585f, 0xbd481425, 0x73df6b75, 0xeef6d9d0, 0x389d87d4, 0xfbb440ba)}, + /* Input known to need 566 divsteps starting with delta=1/2. */ + {SECP256K1_FE_CONST(0xb595d81b, 0x2e3c1e2f, 0x482dbc65, 0xe4865af7, 0x9a0a50aa, 0x29f9e618, 0x6f87d7a5, 0x8d1063ae), + SECP256K1_FE_CONST(0xc983337c, 0x5d5c74e1, 0x49918330, 0x0b53afb5, 0xa0428a0b, 0xce6eef86, 0x059bd8ef, 0xe5b908de)}, + /* Set of 10 inputs accessing all 128 entries in the modinv32 divsteps_var table */ + {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0xe0ff1f80, 0x1f000000, 0x00000000, 0x00000000, 0xfeff0100, 0x00000000), + SECP256K1_FE_CONST(0x9faf9316, 0x77e5049d, 0x0b5e7a1b, 0xef70b893, 0x18c9e30c, 0x045e7fd7, 0x29eddf8c, 0xd62e9e3d)}, + {SECP256K1_FE_CONST(0x621a538d, 0x511b2780, 0x35688252, 0x53f889a4, 0x6317c3ac, 0x32ba0a46, 0x6277c0d1, 0xccd31192), + SECP256K1_FE_CONST(0x38513b0c, 0x5eba856f, 0xe29e882e, 0x9b394d8c, 0x34bda011, 0xeaa66943, 0x6a841a4c, 0x6ae8bcff)}, + {SECP256K1_FE_CONST(0x00000200, 0xf0ffff1f, 0x00000000, 0x0000e0ff, 0xffffffff, 0xfffcffff, 0xffffffff, 0xffff0100), + SECP256K1_FE_CONST(0x5da42a52, 0x3640de9e, 0x13e64343, 0x0c7591b7, 0x6c1e3519, 0xf048c5b6, 0x0484217c, 0xedbf8b2f)}, + {SECP256K1_FE_CONST(0xd1343ef9, 0x4b952621, 0x7c52a2ee, 0x4ea1281b, 0x4ab46410, 0x9f26998d, 0xa686a8ff, 0x9f2103e8), + SECP256K1_FE_CONST(0x84044385, 0x9a4619bf, 0x74e35b6d, 0xa47e0c46, 0x6b7fb47d, 0x9ffab128, 0xb0775aa3, 0xcb318bd1)}, + {SECP256K1_FE_CONST(0xb27235d2, 0xc56a52be, 0x210db37a, 0xd50d23a4, 0xbe621bdd, 0x5df22c6a, 0xe926ba62, 0xd2e4e440), + SECP256K1_FE_CONST(0x67a26e54, 0x483a9d3c, 0xa568469e, 0xd258ab3d, 0xb9ec9981, 0xdca9b1bd, 0x8d2775fe, 0x53ae429b)}, + {SECP256K1_FE_CONST(0x00000000, 0x00000000, 0x00e0ffff, 0xffffff83, 0xffffffff, 0x3f00f00f, 0x000000e0, 0xffffffff), + SECP256K1_FE_CONST(0x310e10f8, 0x23bbfab0, 0xac94907d, 0x076c9a45, 0x8d357d7f, 0xc763bcee, 0x00d0e615, 0x5a6acef6)}, + {SECP256K1_FE_CONST(0xfeff0300, 0x001c0000, 0xf80700c0, 0x0ff0ffff, 0xffffffff, 0x0fffffff, 0xffff0100, 0x7f0000fe), + SECP256K1_FE_CONST(0x28e2fdb4, 0x0709168b, 0x86f598b0, 0x3453a370, 0x530cf21f, 0x32f978d5, 0x1d527a71, 0x59269b0c)}, + {SECP256K1_FE_CONST(0xc2591afa, 0x7bb98ef7, 0x090bb273, 0x85c14f87, 0xbb0b28e0, 0x54d3c453, 0x85c66753, 0xd5574d2f), + SECP256K1_FE_CONST(0xfdca70a2, 0x70ce627c, 0x95e66fae, 0x848a6dbb, 0x07ffb15c, 0x5f63a058, 0xba4140ed, 0x6113b503)}, + {SECP256K1_FE_CONST(0xf5475db3, 0xedc7b5a3, 0x411c047e, 0xeaeb452f, 0xc625828e, 0x1cf5ad27, 0x8eec1060, 0xc7d3e690), + SECP256K1_FE_CONST(0x5eb756c0, 0xf963f4b9, 0xdc6a215e, 0xec8cc2d8, 0x2e9dec01, 0xde5eb88d, 0x6aba7164, 0xaecb2c5a)}, + {SECP256K1_FE_CONST(0x00000000, 0x00f8ffff, 0xffffffff, 0x01000000, 0xe0ff1f00, 0x00000000, 0xffffff7f, 0x00000000), + SECP256K1_FE_CONST(0xe0d2e3d8, 0x49b6157d, 0xe54e88c2, 0x1a7f02ca, 0x7dd28167, 0xf1125d81, 0x7bfa444e, 0xbe110037)}, + /* Selection of randomly generated inputs that reach high/low d/e values in various configurations. */ + {SECP256K1_FE_CONST(0x13cc08a4, 0xd8c41f0f, 0x179c3e67, 0x54c46c67, 0xc4109221, 0x09ab3b13, 0xe24d9be1, 0xffffe950), + SECP256K1_FE_CONST(0xb80c8006, 0xd16abaa7, 0xcabd71e5, 0xcf6714f4, 0x966dd3d0, 0x64767a2d, 0xe92c4441, 0x51008cd1)}, + {SECP256K1_FE_CONST(0xaa6db990, 0x95efbca1, 0x3cc6ff71, 0x0602e24a, 0xf49ff938, 0x99fffc16, 0x46f40993, 0xc6e72057), + SECP256K1_FE_CONST(0xd5d3dd69, 0xb0c195e5, 0x285f1d49, 0xe639e48c, 0x9223f8a9, 0xca1d731d, 0x9ca482f9, 0xa5b93e06)}, + {SECP256K1_FE_CONST(0x1c680eac, 0xaeabffd8, 0x9bdc4aee, 0x1781e3de, 0xa3b08108, 0x0015f2e0, 0x94449e1b, 0x2f67a058), + SECP256K1_FE_CONST(0x7f083f8d, 0x31254f29, 0x6510f475, 0x245c373d, 0xc5622590, 0x4b323393, 0x32ed1719, 0xc127444b)}, + {SECP256K1_FE_CONST(0x147d44b3, 0x012d83f8, 0xc160d386, 0x1a44a870, 0x9ba6be96, 0x8b962707, 0x267cbc1a, 0xb65b2f0a), + SECP256K1_FE_CONST(0x555554ff, 0x170aef1e, 0x50a43002, 0xe51fbd36, 0xafadb458, 0x7a8aded1, 0x0ca6cd33, 0x6ed9087c)}, + {SECP256K1_FE_CONST(0x12423796, 0x22f0fe61, 0xf9ca017c, 0x5384d107, 0xa1fbf3b2, 0x3b018013, 0x916a3c37, 0x4000b98c), + SECP256K1_FE_CONST(0x20257700, 0x08668f94, 0x1177e306, 0x136c01f5, 0x8ed1fbd2, 0x95ec4589, 0xae38edb9, 0xfd19b6d7)}, + {SECP256K1_FE_CONST(0xdcf2d030, 0x9ab42cb4, 0x93ffa181, 0xdcd23619, 0x39699b52, 0x08909a20, 0xb5a17695, 0x3a9dcf21), + SECP256K1_FE_CONST(0x1f701dea, 0xe211fb1f, 0x4f37180d, 0x63a0f51c, 0x29fe1e40, 0xa40b6142, 0x2e7b12eb, 0x982b06b6)}, + {SECP256K1_FE_CONST(0x79a851f6, 0xa6314ed3, 0xb35a55e6, 0xca1c7d7f, 0xe32369ea, 0xf902432e, 0x375308c5, 0xdfd5b600), + SECP256K1_FE_CONST(0xcaae00c5, 0xe6b43851, 0x9dabb737, 0x38cba42c, 0xa02c8549, 0x7895dcbf, 0xbd183d71, 0xafe4476a)}, + {SECP256K1_FE_CONST(0xede78fdd, 0xcfc92bf1, 0x4fec6c6c, 0xdb8d37e2, 0xfb66bc7b, 0x28701870, 0x7fa27c9a, 0x307196ec), + SECP256K1_FE_CONST(0x68193a6c, 0x9a8b87a7, 0x2a760c64, 0x13e473f6, 0x23ae7bed, 0x1de05422, 0x88865427, 0xa3418265)}, + {SECP256K1_FE_CONST(0xa40b2079, 0xb8f88e89, 0xa7617997, 0x89baf5ae, 0x174df343, 0x75138eae, 0x2711595d, 0x3fc3e66c), + SECP256K1_FE_CONST(0x9f99c6a5, 0x6d685267, 0xd4b87c37, 0x9d9c4576, 0x358c692b, 0x6bbae0ed, 0x3389c93d, 0x7fdd2655)}, + {SECP256K1_FE_CONST(0x7c74c6b6, 0xe98d9151, 0x72645cf1, 0x7f06e321, 0xcefee074, 0x15b2113a, 0x10a9be07, 0x08a45696), + SECP256K1_FE_CONST(0x8c919a88, 0x898bc1e0, 0x77f26f97, 0x12e655b7, 0x9ba0ac40, 0xe15bb19e, 0x8364cc3b, 0xe227a8ee)}, + {SECP256K1_FE_CONST(0x109ba1ce, 0xdafa6d4a, 0xa1cec2b2, 0xeb1069f4, 0xb7a79e5b, 0xec6eb99b, 0xaec5f643, 0xee0e723e), + SECP256K1_FE_CONST(0x93d13eb8, 0x4bb0bcf9, 0xe64f5a71, 0xdbe9f359, 0x7191401c, 0x6f057a4a, 0xa407fe1b, 0x7ecb65cc)}, + {SECP256K1_FE_CONST(0x3db076cd, 0xec74a5c9, 0xf61dd138, 0x90e23e06, 0xeeedd2d0, 0x74cbc4e0, 0x3dbe1e91, 0xded36a78), + SECP256K1_FE_CONST(0x3f07f966, 0x8e2a1e09, 0x706c71df, 0x02b5e9d5, 0xcb92ddbf, 0xcdd53010, 0x16545564, 0xe660b107)}, + {SECP256K1_FE_CONST(0xe31c73ed, 0xb4c4b82c, 0x02ae35f7, 0x4cdec153, 0x98b522fd, 0xf7d2460c, 0x6bf7c0f8, 0x4cf67b0d), + SECP256K1_FE_CONST(0x4b8f1faf, 0x94e8b070, 0x19af0ff6, 0xa319cd31, 0xdf0a7ffb, 0xefaba629, 0x59c50666, 0x1fe5b843)}, + {SECP256K1_FE_CONST(0x4c8b0e6e, 0x83392ab6, 0xc0e3e9f1, 0xbbd85497, 0x16698897, 0xf552d50d, 0x79652ddb, 0x12f99870), + SECP256K1_FE_CONST(0x56d5101f, 0xd23b7949, 0x17dc38d6, 0xf24022ef, 0xcf18e70a, 0x5cc34424, 0x438544c3, 0x62da4bca)}, + {SECP256K1_FE_CONST(0xb0e040e2, 0x40cc35da, 0x7dd5c611, 0x7fccb178, 0x28888137, 0xbc930358, 0xea2cbc90, 0x775417dc), + SECP256K1_FE_CONST(0xca37f0d4, 0x016dd7c8, 0xab3ae576, 0x96e08d69, 0x68ed9155, 0xa9b44270, 0x900ae35d, 0x7c7800cd)}, + {SECP256K1_FE_CONST(0x8a32ea49, 0x7fbb0bae, 0x69724a9d, 0x8e2105b2, 0xbdf69178, 0x862577ef, 0x35055590, 0x667ddaef), + SECP256K1_FE_CONST(0xd02d7ead, 0xc5e190f0, 0x559c9d72, 0xdaef1ffc, 0x64f9f425, 0xf43645ea, 0x7341e08d, 0x11768e96)}, + {SECP256K1_FE_CONST(0xa3592d98, 0x9abe289d, 0x579ebea6, 0xbb0857a8, 0xe242ab73, 0x85f9a2ce, 0xb6998f0f, 0xbfffbfc6), + SECP256K1_FE_CONST(0x093c1533, 0x32032efa, 0x6aa46070, 0x0039599e, 0x589c35f4, 0xff525430, 0x7fe3777a, 0x44b43ddc)}, + {SECP256K1_FE_CONST(0x647178a3, 0x229e607b, 0xcc98521a, 0xcce3fdd9, 0x1e1bc9c9, 0x97fb7c6a, 0x61b961e0, 0x99b10709), + SECP256K1_FE_CONST(0x98217c13, 0xd51ddf78, 0x96310e77, 0xdaebd908, 0x602ca683, 0xcb46d07a, 0xa1fcf17e, 0xc8e2feb3)}, + {SECP256K1_FE_CONST(0x7334627c, 0x73f98968, 0x99464b4b, 0xf5964958, 0x1b95870d, 0xc658227e, 0x5e3235d8, 0xdcab5787), + SECP256K1_FE_CONST(0x000006fd, 0xc7e9dd94, 0x40ae367a, 0xe51d495c, 0x07603b9b, 0x2d088418, 0x6cc5c74c, 0x98514307)}, + {SECP256K1_FE_CONST(0x82e83876, 0x96c28938, 0xa50dd1c5, 0x605c3ad1, 0xc048637d, 0x7a50825f, 0x335ed01a, 0x00005760), + SECP256K1_FE_CONST(0xb0393f9f, 0x9f2aa55e, 0xf5607e2e, 0x5287d961, 0x60b3e704, 0xf3e16e80, 0xb4f9a3ea, 0xfec7f02d)}, + {SECP256K1_FE_CONST(0xc97b6cec, 0x3ee6b8dc, 0x98d24b58, 0x3c1970a1, 0xfe06297a, 0xae813529, 0xe76bb6bd, 0x771ae51d), + SECP256K1_FE_CONST(0x0507c702, 0xd407d097, 0x47ddeb06, 0xf6625419, 0x79f48f79, 0x7bf80d0b, 0xfc34b364, 0x253a5db1)}, + {SECP256K1_FE_CONST(0xd559af63, 0x77ea9bc4, 0x3cf1ad14, 0x5c7a4bbb, 0x10e7d18b, 0x7ce0dfac, 0x380bb19d, 0x0bb99bd3), + SECP256K1_FE_CONST(0x00196119, 0xb9b00d92, 0x34edfdb5, 0xbbdc42fc, 0xd2daa33a, 0x163356ca, 0xaa8754c8, 0xb0ec8b0b)}, + {SECP256K1_FE_CONST(0x8ddfa3dc, 0x52918da0, 0x640519dc, 0x0af8512a, 0xca2d33b2, 0xbde52514, 0xda9c0afc, 0xcb29fce4), + SECP256K1_FE_CONST(0xb3e4878d, 0x5cb69148, 0xcd54388b, 0xc23acce0, 0x62518ba8, 0xf09def92, 0x7b31e6aa, 0x6ba35b02)}, + {SECP256K1_FE_CONST(0xf8207492, 0xe3049f0a, 0x65285f2b, 0x0bfff996, 0x00ca112e, 0xc05da837, 0x546d41f9, 0x5194fb91), + SECP256K1_FE_CONST(0x7b7ee50b, 0xa8ed4bbd, 0xf6469930, 0x81419a5c, 0x071441c7, 0x290d046e, 0x3b82ea41, 0x611c5f95)}, + {SECP256K1_FE_CONST(0x050f7c80, 0x5bcd3c6b, 0x823cb724, 0x5ce74db7, 0xa4e39f5c, 0xbd8828d7, 0xfd4d3e07, 0x3ec2926a), + SECP256K1_FE_CONST(0x000d6730, 0xb0171314, 0x4764053d, 0xee157117, 0x48fd61da, 0xdea0b9db, 0x1d5e91c6, 0xbdc3f59e)}, + {SECP256K1_FE_CONST(0x3e3ea8eb, 0x05d760cf, 0x23009263, 0xb3cb3ac9, 0x088f6f0d, 0x3fc182a3, 0xbd57087c, 0xe67c62f9), + SECP256K1_FE_CONST(0xbe988716, 0xa29c1bf6, 0x4456aed6, 0xab1e4720, 0x49929305, 0x51043bf4, 0xebd833dd, 0xdd511e8b)}, + {SECP256K1_FE_CONST(0x6964d2a9, 0xa7fa6501, 0xa5959249, 0x142f4029, 0xea0c1b5f, 0x2f487ef6, 0x301ac80a, 0x768be5cd), + SECP256K1_FE_CONST(0x3918ffe4, 0x07492543, 0xed24d0b7, 0x3df95f8f, 0xaffd7cb4, 0x0de2191c, 0x9ec2f2ad, 0x2c0cb3c6)}, + {SECP256K1_FE_CONST(0x37c93520, 0xf6ddca57, 0x2b42fd5e, 0xb5c7e4de, 0x11b5b81c, 0xb95e91f3, 0x95c4d156, 0x39877ccb), + SECP256K1_FE_CONST(0x9a94b9b5, 0x57eb71ee, 0x4c975b8b, 0xac5262a8, 0x077b0595, 0xe12a6b1f, 0xd728edef, 0x1a6bf956)} + }; + /* Fixed test cases for scalar inverses: pairs of (x, 1/x) mod n. */ + static const secp256k1_scalar scalar_cases[][2] = { + /* 0 */ + {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0), + SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0)}, + /* 1 */ + {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1), + SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1)}, + /* -1 */ + {SECP256K1_SCALAR_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xbaaedce6, 0xaf48a03b, 0xbfd25e8c, 0xd0364140), + SECP256K1_SCALAR_CONST(0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0xbaaedce6, 0xaf48a03b, 0xbfd25e8c, 0xd0364140)}, + /* 2 */ + {SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 2), + SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x5d576e73, 0x57a4501d, 0xdfe92f46, 0x681b20a1)}, + /* 2**128 */ + {SECP256K1_SCALAR_CONST(0, 0, 0, 1, 0, 0, 0, 0), + SECP256K1_SCALAR_CONST(0x50a51ac8, 0x34b9ec24, 0x4b0dff66, 0x5588b13e, 0x9984d5b3, 0xcf80ef0f, 0xd6a23766, 0xa3ee9f22)}, + /* Input known to need 635 divsteps */ + {SECP256K1_SCALAR_CONST(0xcb9f1d35, 0xdd4416c2, 0xcd71bf3f, 0x6365da66, 0x3c9b3376, 0x8feb7ae9, 0x32a5ef60, 0x19199ec3), + SECP256K1_SCALAR_CONST(0x1d7c7bba, 0xf1893d53, 0xb834bd09, 0x36b411dc, 0x42c2e42f, 0xec72c428, 0x5e189791, 0x8e9bc708)}, + /* Input known to need 566 divsteps starting with delta=1/2. */ + {SECP256K1_SCALAR_CONST(0x7e3c993d, 0xa4272488, 0xbc015b49, 0x2db54174, 0xd382083a, 0xebe6db35, 0x80f82eff, 0xcd132c72), + SECP256K1_SCALAR_CONST(0x086f34a0, 0x3e631f76, 0x77418f28, 0xcc84ac95, 0x6304439d, 0x365db268, 0x312c6ded, 0xd0b934f8)}, + /* Input known to need 565 divsteps starting with delta=1/2. */ + {SECP256K1_SCALAR_CONST(0xbad7e587, 0x3f307859, 0x60d93147, 0x8a18491e, 0xb38a9fd5, 0x254350d3, 0x4b1f0e4b, 0x7dd6edc4), + SECP256K1_SCALAR_CONST(0x89f2df26, 0x39e2b041, 0xf19bd876, 0xd039c8ac, 0xc2223add, 0x29c4943e, 0x6632d908, 0x515f467b)}, + /* Selection of randomly generated inputs that reach low/high d/e values in various configurations. */ + {SECP256K1_SCALAR_CONST(0x1950d757, 0xb37a5809, 0x435059bb, 0x0bb8997e, 0x07e1e3c8, 0x5e5d7d2c, 0x6a0ed8e3, 0xdbde180e), + SECP256K1_SCALAR_CONST(0xbf72af9b, 0x750309e2, 0x8dda230b, 0xfe432b93, 0x7e25e475, 0x4388251e, 0x633d894b, 0x3bcb6f8c)}, + {SECP256K1_SCALAR_CONST(0x9bccf4e7, 0xc5a515e3, 0x50637aa9, 0xbb65a13f, 0x391749a1, 0x62de7d4e, 0xf6d7eabb, 0x3cd10ce0), + SECP256K1_SCALAR_CONST(0xaf2d5623, 0xb6385a33, 0xcd0365be, 0x5e92a70d, 0x7f09179c, 0x3baaf30f, 0x8f9cc83b, 0x20092f67)}, + {SECP256K1_SCALAR_CONST(0x73a57111, 0xb242952a, 0x5c5dee59, 0xf3be2ace, 0xa30a7659, 0xa46e5f47, 0xd21267b1, 0x39e642c9), + SECP256K1_SCALAR_CONST(0xa711df07, 0xcbcf13ef, 0xd61cc6be, 0xbcd058ce, 0xb02cf157, 0x272d4a18, 0x86d0feb3, 0xcd5fa004)}, + {SECP256K1_SCALAR_CONST(0x04884963, 0xce0580b1, 0xba547030, 0x3c691db3, 0x9cd2c84f, 0x24c7cebd, 0x97ebfdba, 0x3e785ec2), + SECP256K1_SCALAR_CONST(0xaaaaaf14, 0xd7c99ba7, 0x517ce2c1, 0x78a28b4c, 0x3769a851, 0xe5c5a03d, 0x4cc28f33, 0x0ec4dc5d)}, + {SECP256K1_SCALAR_CONST(0x1679ed49, 0x21f537b1, 0x815cb8ae, 0x9efc511c, 0x5b9fa037, 0x0b0f275e, 0x6c985281, 0x6c4a9905), + SECP256K1_SCALAR_CONST(0xb14ac3d5, 0x62b52999, 0xef34ead1, 0xffca4998, 0x0294341a, 0x1f8172aa, 0xea1624f9, 0x302eea62)}, + {SECP256K1_SCALAR_CONST(0x626b37c0, 0xf0057c35, 0xee982f83, 0x452a1fd3, 0xea826506, 0x48b08a9d, 0x1d2c4799, 0x4ad5f6ec), + SECP256K1_SCALAR_CONST(0xe38643b7, 0x567bfc2f, 0x5d2f1c15, 0xe327239c, 0x07112443, 0x69509283, 0xfd98e77a, 0xdb71c1e8)}, + {SECP256K1_SCALAR_CONST(0x1850a3a7, 0x759efc56, 0x54f287b2, 0x14d1234b, 0xe263bbc9, 0xcf4d8927, 0xd5f85f27, 0x965bd816), + SECP256K1_SCALAR_CONST(0x3b071831, 0xcac9619a, 0xcceb0596, 0xf614d63b, 0x95d0db2f, 0xc6a00901, 0x8eaa2621, 0xabfa0009)}, + {SECP256K1_SCALAR_CONST(0x94ae5d06, 0xa27dc400, 0x487d72be, 0xaa51ebed, 0xe475b5c0, 0xea675ffc, 0xf4df627a, 0xdca4222f), + SECP256K1_SCALAR_CONST(0x01b412ed, 0xd7830956, 0x1532537e, 0xe5e3dc99, 0x8fd3930a, 0x54f8d067, 0x32ef5760, 0x594438a5)}, + {SECP256K1_SCALAR_CONST(0x1f24278a, 0xb5bfe374, 0xa328dbbc, 0xebe35f48, 0x6620e009, 0xd58bb1b4, 0xb5a6bf84, 0x8815f63a), + SECP256K1_SCALAR_CONST(0xfe928416, 0xca5ba2d3, 0xfde513da, 0x903a60c7, 0x9e58ad8a, 0x8783bee4, 0x083a3843, 0xa608c914)}, + {SECP256K1_SCALAR_CONST(0xdc107d58, 0x274f6330, 0x67dba8bc, 0x26093111, 0x5201dfb8, 0x968ce3f5, 0xf34d1bd4, 0xf2146504), + SECP256K1_SCALAR_CONST(0x660cfa90, 0x13c3d93e, 0x7023b1e5, 0xedd09e71, 0x6d9c9d10, 0x7a3d2cdb, 0xdd08edc3, 0xaa78fcfb)}, + {SECP256K1_SCALAR_CONST(0x7cd1e905, 0xc6f02776, 0x2f551cc7, 0x5da61cff, 0x7da05389, 0x1119d5a4, 0x631c7442, 0x894fd4f7), + SECP256K1_SCALAR_CONST(0xff20862a, 0x9d3b1a37, 0x1628803b, 0x3004ccae, 0xaa23282a, 0xa89a1109, 0xd94ece5e, 0x181bdc46)}, + {SECP256K1_SCALAR_CONST(0x5b9dade8, 0x23d26c58, 0xcd12d818, 0x25b8ae97, 0x3dea04af, 0xf482c96b, 0xa062f254, 0x9e453640), + SECP256K1_SCALAR_CONST(0x50c38800, 0x15fa53f4, 0xbe1e5392, 0x5c9b120a, 0x262c22c7, 0x18fa0816, 0x5f2baab4, 0x8cb5db46)}, + {SECP256K1_SCALAR_CONST(0x11cdaeda, 0x969c464b, 0xef1f4ab0, 0x5b01d22e, 0x656fd098, 0x882bea84, 0x65cdbe7a, 0x0c19ff03), + SECP256K1_SCALAR_CONST(0x1968d0fa, 0xac46f103, 0xb55f1f72, 0xb3820bed, 0xec6b359a, 0x4b1ae0ad, 0x7e38e1fb, 0x295ccdfb)}, + {SECP256K1_SCALAR_CONST(0x2c351aa1, 0x26e91589, 0x194f8a1e, 0x06561f66, 0x0cb97b7f, 0x10914454, 0x134d1c03, 0x157266b4), + SECP256K1_SCALAR_CONST(0xbe49ada6, 0x92bd8711, 0x41b176c4, 0xa478ba95, 0x14883434, 0x9d1cd6f3, 0xcc4b847d, 0x22af80f5)}, + {SECP256K1_SCALAR_CONST(0x6ba07c6e, 0x13a60edb, 0x6247f5c3, 0x84b5fa56, 0x76fe3ec5, 0x80426395, 0xf65ec2ae, 0x623ba730), + SECP256K1_SCALAR_CONST(0x25ac23f7, 0x418cd747, 0x98376f9d, 0x4a11c7bf, 0x24c8ebfe, 0x4c8a8655, 0x345f4f52, 0x1c515595)}, + {SECP256K1_SCALAR_CONST(0x9397a712, 0x8abb6951, 0x2d4a3d54, 0x703b1c2a, 0x0661dca8, 0xd75c9b31, 0xaed4d24b, 0xd2ab2948), + SECP256K1_SCALAR_CONST(0xc52e8bef, 0xd55ce3eb, 0x1c897739, 0xeb9fb606, 0x36b9cd57, 0x18c51cc2, 0x6a87489e, 0xffd0dcf3)}, + {SECP256K1_SCALAR_CONST(0xe6a808cc, 0xeb437888, 0xe97798df, 0x4e224e44, 0x7e3b380a, 0x207c1653, 0x889f3212, 0xc6738b6f), + SECP256K1_SCALAR_CONST(0x31f9ae13, 0xd1e08b20, 0x757a2e5e, 0x5243a0eb, 0x8ae35f73, 0x19bb6122, 0xb910f26b, 0xda70aa55)}, + {SECP256K1_SCALAR_CONST(0xd0320548, 0xab0effe7, 0xa70779e0, 0x61a347a6, 0xb8c1e010, 0x9d5281f8, 0x2ee588a6, 0x80000000), + SECP256K1_SCALAR_CONST(0x1541897e, 0x78195c90, 0x7583dd9e, 0x728b6100, 0xbce8bc6d, 0x7a53b471, 0x5dcd9e45, 0x4425fcaf)}, + {SECP256K1_SCALAR_CONST(0x93d623f1, 0xd45b50b0, 0x796e9186, 0x9eac9407, 0xd30edc20, 0xef6304cf, 0x250494e7, 0xba503de9), + SECP256K1_SCALAR_CONST(0x7026d638, 0x1178b548, 0x92043952, 0x3c7fb47c, 0xcd3ea236, 0x31d82b01, 0x612fc387, 0x80b9b957)}, + {SECP256K1_SCALAR_CONST(0xf860ab39, 0x55f5d412, 0xa4d73bcc, 0x3b48bd90, 0xc248ffd3, 0x13ca10be, 0x8fba84cc, 0xdd28d6a3), + SECP256K1_SCALAR_CONST(0x5c32fc70, 0xe0b15d67, 0x76694700, 0xfe62be4d, 0xeacdb229, 0x7a4433d9, 0x52155cd0, 0x7649ab59)}, + {SECP256K1_SCALAR_CONST(0x4e41311c, 0x0800af58, 0x7a690a8e, 0xe175c9ba, 0x6981ab73, 0xac532ea8, 0x5c1f5e63, 0x6ac1f189), + SECP256K1_SCALAR_CONST(0xfffffff9, 0xd075982c, 0x7fbd3825, 0xc05038a2, 0x4533b91f, 0x94ec5f45, 0xb280b28f, 0x842324dc)}, + {SECP256K1_SCALAR_CONST(0x48e473bf, 0x3555eade, 0xad5d7089, 0x2424c4e4, 0x0a99397c, 0x2dc796d8, 0xb7a43a69, 0xd0364141), + SECP256K1_SCALAR_CONST(0x634976b2, 0xa0e47895, 0x1ec38593, 0x266d6fd0, 0x6f602644, 0x9bb762f1, 0x7180c704, 0xe23a4daa)}, + {SECP256K1_SCALAR_CONST(0xbe83878d, 0x3292fc54, 0x26e71c62, 0x556ccedc, 0x7cbb8810, 0x4032a720, 0x34ead589, 0xe4d6bd13), + SECP256K1_SCALAR_CONST(0x6cd150ad, 0x25e59d0f, 0x74cbae3d, 0x6377534a, 0x1e6562e8, 0xb71b9d18, 0xe1e5d712, 0x8480abb3)}, + {SECP256K1_SCALAR_CONST(0xcdddf2e5, 0xefc15f88, 0xc9ee06de, 0x8a846ca9, 0x28561581, 0x68daa5fb, 0xd1cf3451, 0xeb1782d0), + SECP256K1_SCALAR_CONST(0xffffffd9, 0xed8d2af4, 0x993c865a, 0x23e9681a, 0x3ca3a3dc, 0xe6d5a46e, 0xbd86bd87, 0x61b55c70)}, + {SECP256K1_SCALAR_CONST(0xb6a18f1f, 0x04872df9, 0x08165ec4, 0x319ca19c, 0x6c0359ab, 0x1f7118fb, 0xc2ef8082, 0xca8b7785), + SECP256K1_SCALAR_CONST(0xff55b19b, 0x0f1ac78c, 0x0f0c88c2, 0x2358d5ad, 0x5f455e4e, 0x3330b72f, 0x274dc153, 0xffbf272b)}, + {SECP256K1_SCALAR_CONST(0xea4898e5, 0x30eba3e8, 0xcf0e5c3d, 0x06ec6844, 0x01e26fb6, 0x75636225, 0xc5d08f4c, 0x1decafa0), + SECP256K1_SCALAR_CONST(0xe5a014a8, 0xe3c4ec1e, 0xea4f9b32, 0xcfc7b386, 0x00630806, 0x12c08d02, 0x6407ccc2, 0xb067d90e)}, + {SECP256K1_SCALAR_CONST(0x70e9aea9, 0x7e933af0, 0x8a23bfab, 0x23e4b772, 0xff951863, 0x5ffcf47d, 0x6bebc918, 0x2ca58265), + SECP256K1_SCALAR_CONST(0xf4e00006, 0x81bc6441, 0x4eb6ec02, 0xc194a859, 0x80ad7c48, 0xba4e9afb, 0x8b6bdbe0, 0x989d8f77)}, + {SECP256K1_SCALAR_CONST(0x3c56c774, 0x46efe6f0, 0xe93618b8, 0xf9b5a846, 0xd247df61, 0x83b1e215, 0x06dc8bcc, 0xeefc1bf5), + SECP256K1_SCALAR_CONST(0xfff8937a, 0x2cd9586b, 0x43c25e57, 0xd1cefa7a, 0x9fb91ed3, 0x95b6533d, 0x8ad0de5b, 0xafb93f00)}, + {SECP256K1_SCALAR_CONST(0xfb5c2772, 0x5cb30e83, 0xe38264df, 0xe4e3ebf3, 0x392aa92e, 0xa68756a1, 0x51279ac5, 0xb50711a8), + SECP256K1_SCALAR_CONST(0x000013af, 0x1105bfe7, 0xa6bbd7fb, 0x3d638f99, 0x3b266b02, 0x072fb8bc, 0x39251130, 0x2e0fd0ea)} + }; + int i, var, testrand; + unsigned char b32[32]; + secp256k1_fe x_fe; + secp256k1_scalar x_scalar; + memset(b32, 0, sizeof(b32)); + /* Test fixed test cases through test_inverse_{scalar,field}, both ways. */ + for (i = 0; (size_t)i < sizeof(fe_cases)/sizeof(fe_cases[0]); ++i) { + for (var = 0; var <= 1; ++var) { + test_inverse_field(&x_fe, &fe_cases[i][0], var); + CHECK(fe_equal(&x_fe, &fe_cases[i][1])); + test_inverse_field(&x_fe, &fe_cases[i][1], var); + CHECK(fe_equal(&x_fe, &fe_cases[i][0])); + } + } + for (i = 0; (size_t)i < sizeof(scalar_cases)/sizeof(scalar_cases[0]); ++i) { + for (var = 0; var <= 1; ++var) { + test_inverse_scalar(&x_scalar, &scalar_cases[i][0], var); + CHECK(secp256k1_scalar_eq(&x_scalar, &scalar_cases[i][1])); + test_inverse_scalar(&x_scalar, &scalar_cases[i][1], var); + CHECK(secp256k1_scalar_eq(&x_scalar, &scalar_cases[i][0])); + } + } + /* Test inputs 0..999 and their respective negations. */ + for (i = 0; i < 1000; ++i) { + b32[31] = i & 0xff; + b32[30] = (i >> 8) & 0xff; + secp256k1_scalar_set_b32(&x_scalar, b32, NULL); + secp256k1_fe_set_b32_mod(&x_fe, b32); + for (var = 0; var <= 1; ++var) { + test_inverse_scalar(NULL, &x_scalar, var); + test_inverse_field(NULL, &x_fe, var); + } + secp256k1_scalar_negate(&x_scalar, &x_scalar); + secp256k1_fe_negate(&x_fe, &x_fe, 1); + for (var = 0; var <= 1; ++var) { + test_inverse_scalar(NULL, &x_scalar, var); + test_inverse_field(NULL, &x_fe, var); + } + } + /* test 128*count random inputs; half with testrand256_test, half with testrand256 */ + for (testrand = 0; testrand <= 1; ++testrand) { + for (i = 0; i < 64 * COUNT; ++i) { + (testrand ? testrand256_test : testrand256)(b32); + secp256k1_scalar_set_b32(&x_scalar, b32, NULL); + secp256k1_fe_set_b32_mod(&x_fe, b32); + for (var = 0; var <= 1; ++var) { + test_inverse_scalar(NULL, &x_scalar, var); + test_inverse_field(NULL, &x_fe, var); + } + } + } +} + +/***** HSORT TESTS *****/ + +static void test_heap_swap(void) { + unsigned char a[600]; + unsigned char e[sizeof(a)]; + memset(a, 21, 200); + memset(a + 200, 99, 200); + memset(a + 400, 42, 200); + memset(e, 42, 200); + memset(e + 200, 99, 200); + memset(e + 400, 21, 200); + secp256k1_heap_swap(a, 0, 2, 200); + CHECK(secp256k1_memcmp_var(a, e, sizeof(a)) == 0); +} + +static void test_hsort_is_sorted(unsigned char *elements, size_t n, size_t len) { + size_t i; + for (i = 1; i < n; i++) { + CHECK(secp256k1_memcmp_var(&elements[(i-1) * len], &elements[i * len], len) <= 0); + } +} + +struct test_hsort_cmp_data { + size_t counter; + size_t element_len; +}; + + +static int test_hsort_cmp(const void *ele1, const void *ele2, void *data) { + struct test_hsort_cmp_data *d = (struct test_hsort_cmp_data *) data; + d->counter += 1; + return secp256k1_memcmp_var((unsigned char *)ele1, (unsigned char *)ele2, d->element_len); +} + +#define NUM 65 +#define MAX_ELEMENT_LEN 65 +static void test_hsort(size_t element_len) { + unsigned char elements[NUM * MAX_ELEMENT_LEN] = { 0 }; + struct test_hsort_cmp_data data; + int i; + + VERIFY_CHECK(element_len <= MAX_ELEMENT_LEN); + data.counter = 0; + data.element_len = element_len; + + secp256k1_hsort(elements, 0, element_len, test_hsort_cmp, &data); + CHECK(data.counter == 0); + secp256k1_hsort(elements, 1, element_len, test_hsort_cmp, &data); + CHECK(data.counter == 0); + secp256k1_hsort(elements, NUM, element_len, test_hsort_cmp, &data); + CHECK(data.counter >= NUM - 1); + test_hsort_is_sorted(elements, NUM, element_len); + + /* Test hsort with array of random length n */ + for (i = 0; i < COUNT; i++) { + int n = testrand_int(NUM); + testrand_bytes_test(elements, n*element_len); + secp256k1_hsort(elements, n, element_len, test_hsort_cmp, &data); + test_hsort_is_sorted(elements, n, element_len); + } +} +#undef NUM +#undef MAX_ELEMENT_LEN + -void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { - CHECK(a->infinity == b->infinity); - if (a->infinity) { - return; - } - CHECK(secp256k1_fe_equal_var(&a->x, &b->x)); - CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); +static void run_hsort_tests(void) { + test_heap_swap(); + test_hsort(1); + test_hsort(64); + test_hsort(65); } +/***** GROUP TESTS *****/ + /* This compares jacobian points including their Z, not just their geometric meaning. */ -int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { +static int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { secp256k1_gej a2; secp256k1_gej b2; int ret = 1; @@ -1860,101 +3656,71 @@ int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { return ret; } -void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { - secp256k1_fe z2s; - secp256k1_fe u1, u2, s1, s2; - CHECK(a->infinity == b->infinity); - if (a->infinity) { - return; - } - /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */ - secp256k1_fe_sqr(&z2s, &b->z); - secp256k1_fe_mul(&u1, &a->x, &z2s); - u2 = b->x; secp256k1_fe_normalize_weak(&u2); - secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z); - s2 = b->y; secp256k1_fe_normalize_weak(&s2); - CHECK(secp256k1_fe_equal_var(&u1, &u2)); - CHECK(secp256k1_fe_equal_var(&s1, &s2)); -} - -void test_ge(void) { +static void test_ge(void) { int i, i1; -#ifdef USE_ENDOMORPHISM int runs = 6; -#else - int runs = 4; -#endif - /* Points: (infinity, p1, p1, -p1, -p1, p2, p2, -p2, -p2, p3, p3, -p3, -p3, p4, p4, -p4, -p4). - * The second in each pair of identical points uses a random Z coordinate in the Jacobian form. - * All magnitudes are randomized. - * All 17*17 combinations of points are added to each other, using all applicable methods. - * - * When the endomorphism code is compiled in, p5 = lambda*p1 and p6 = lambda^2*p1 are added as well. + /* 25 points are used: + * - infinity + * - for each of four random points p1 p2 p3 p4, we add the point, its + * negation, and then those two again but with randomized Z coordinate. + * - The same is then done for lambda*p1 and lambda^2*p1. */ - secp256k1_ge *ge = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * (1 + 4 * runs)); - secp256k1_gej *gej = (secp256k1_gej *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_gej) * (1 + 4 * runs)); - secp256k1_fe *zinv = (secp256k1_fe *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_fe) * (1 + 4 * runs)); - secp256k1_fe zf; + secp256k1_ge *ge = (secp256k1_ge *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_ge) * (1 + 4 * runs)); + secp256k1_gej *gej = (secp256k1_gej *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_gej) * (1 + 4 * runs)); + secp256k1_fe zf, r; secp256k1_fe zfi2, zfi3; secp256k1_gej_set_infinity(&gej[0]); secp256k1_ge_clear(&ge[0]); secp256k1_ge_set_gej_var(&ge[0], &gej[0]); for (i = 0; i < runs; i++) { - int j; + int j, k; secp256k1_ge g; - random_group_element_test(&g); -#ifdef USE_ENDOMORPHISM + testutil_random_ge_test(&g); if (i >= runs - 2) { secp256k1_ge_mul_lambda(&g, &ge[1]); + CHECK(!secp256k1_ge_eq_var(&g, &ge[1])); } if (i >= runs - 1) { secp256k1_ge_mul_lambda(&g, &g); } -#endif ge[1 + 4 * i] = g; ge[2 + 4 * i] = g; secp256k1_ge_neg(&ge[3 + 4 * i], &g); secp256k1_ge_neg(&ge[4 + 4 * i], &g); secp256k1_gej_set_ge(&gej[1 + 4 * i], &ge[1 + 4 * i]); - random_group_element_jacobian_test(&gej[2 + 4 * i], &ge[2 + 4 * i]); + testutil_random_ge_jacobian_test(&gej[2 + 4 * i], &ge[2 + 4 * i]); secp256k1_gej_set_ge(&gej[3 + 4 * i], &ge[3 + 4 * i]); - random_group_element_jacobian_test(&gej[4 + 4 * i], &ge[4 + 4 * i]); + testutil_random_ge_jacobian_test(&gej[4 + 4 * i], &ge[4 + 4 * i]); for (j = 0; j < 4; j++) { - random_field_element_magnitude(&ge[1 + j + 4 * i].x); - random_field_element_magnitude(&ge[1 + j + 4 * i].y); - random_field_element_magnitude(&gej[1 + j + 4 * i].x); - random_field_element_magnitude(&gej[1 + j + 4 * i].y); - random_field_element_magnitude(&gej[1 + j + 4 * i].z); + testutil_random_ge_x_magnitude(&ge[1 + j + 4 * i]); + testutil_random_ge_y_magnitude(&ge[1 + j + 4 * i]); + testutil_random_gej_x_magnitude(&gej[1 + j + 4 * i]); + testutil_random_gej_y_magnitude(&gej[1 + j + 4 * i]); + testutil_random_gej_z_magnitude(&gej[1 + j + 4 * i]); } - } - /* Compute z inverses. */ - { - secp256k1_fe *zs = checked_malloc(&ctx->error_callback, sizeof(secp256k1_fe) * (1 + 4 * runs)); - for (i = 0; i < 4 * runs + 1; i++) { - if (i == 0) { - /* The point at infinity does not have a meaningful z inverse. Any should do. */ - do { - random_field_element_test(&zs[i]); - } while(secp256k1_fe_is_zero(&zs[i])); - } else { - zs[i] = gej[i].z; + for (j = 0; j < 4; ++j) { + for (k = 0; k < 4; ++k) { + int expect_equal = (j >> 1) == (k >> 1); + CHECK(secp256k1_ge_eq_var(&ge[1 + j + 4 * i], &ge[1 + k + 4 * i]) == expect_equal); + CHECK(secp256k1_gej_eq_var(&gej[1 + j + 4 * i], &gej[1 + k + 4 * i]) == expect_equal); + CHECK(secp256k1_gej_eq_ge_var(&gej[1 + j + 4 * i], &ge[1 + k + 4 * i]) == expect_equal); + CHECK(secp256k1_gej_eq_ge_var(&gej[1 + k + 4 * i], &ge[1 + j + 4 * i]) == expect_equal); } } - secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1); - free(zs); } /* Generate random zf, and zfi2 = 1/zf^2, zfi3 = 1/zf^3 */ - do { - random_field_element_test(&zf); - } while(secp256k1_fe_is_zero(&zf)); - random_field_element_magnitude(&zf); + testutil_random_fe_non_zero_test(&zf); + testutil_random_fe_magnitude(&zf, 8); secp256k1_fe_inv_var(&zfi3, &zf); secp256k1_fe_sqr(&zfi2, &zfi3); secp256k1_fe_mul(&zfi3, &zfi3, &zfi2); + /* Generate random r */ + testutil_random_fe_non_zero_test(&r); + for (i1 = 0; i1 < 1 + 4 * runs; i1++) { int i2; for (i2 = 0; i2 < 1 + 4 * runs; i2++) { @@ -1966,16 +3732,16 @@ void test_ge(void) { /* Check Z ratio. */ if (!secp256k1_gej_is_infinity(&gej[i1]) && !secp256k1_gej_is_infinity(&refj)) { secp256k1_fe zrz; secp256k1_fe_mul(&zrz, &zr, &gej[i1].z); - CHECK(secp256k1_fe_equal_var(&zrz, &refj.z)); + CHECK(secp256k1_fe_equal(&zrz, &refj.z)); } secp256k1_ge_set_gej_var(&ref, &refj); /* Test gej + ge with Z ratio result (var). */ secp256k1_gej_add_ge_var(&resj, &gej[i1], &ge[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); - ge_equals_gej(&ref, &resj); + CHECK(secp256k1_gej_eq_ge_var(&resj, &ref)); if (!secp256k1_gej_is_infinity(&gej[i1]) && !secp256k1_gej_is_infinity(&resj)) { secp256k1_fe zrz; secp256k1_fe_mul(&zrz, &zr, &gej[i1].z); - CHECK(secp256k1_fe_equal_var(&zrz, &resj.z)); + CHECK(secp256k1_fe_equal(&zrz, &resj.z)); } /* Test gej + ge (var, with additional Z factor). */ @@ -1983,17 +3749,17 @@ void test_ge(void) { secp256k1_ge ge2_zfi = ge[i2]; /* the second term with x and y rescaled for z = 1/zf */ secp256k1_fe_mul(&ge2_zfi.x, &ge2_zfi.x, &zfi2); secp256k1_fe_mul(&ge2_zfi.y, &ge2_zfi.y, &zfi3); - random_field_element_magnitude(&ge2_zfi.x); - random_field_element_magnitude(&ge2_zfi.y); + testutil_random_ge_x_magnitude(&ge2_zfi); + testutil_random_ge_y_magnitude(&ge2_zfi); secp256k1_gej_add_zinv_var(&resj, &gej[i1], &ge2_zfi, &zf); - ge_equals_gej(&ref, &resj); + CHECK(secp256k1_gej_eq_ge_var(&resj, &ref)); } /* Test gej + ge (const). */ if (i2 != 0) { /* secp256k1_gej_add_ge does not support its second argument being infinity. */ secp256k1_gej_add_ge(&resj, &gej[i1], &ge[i2]); - ge_equals_gej(&ref, &resj); + CHECK(secp256k1_gej_eq_ge_var(&resj, &ref)); } /* Test doubling (var). */ @@ -2001,13 +3767,16 @@ void test_ge(void) { secp256k1_fe zr2; /* Normal doubling with Z ratio result. */ secp256k1_gej_double_var(&resj, &gej[i1], &zr2); - ge_equals_gej(&ref, &resj); + CHECK(secp256k1_gej_eq_ge_var(&resj, &ref)); /* Check Z ratio. */ secp256k1_fe_mul(&zr2, &zr2, &gej[i1].z); - CHECK(secp256k1_fe_equal_var(&zr2, &resj.z)); + CHECK(secp256k1_fe_equal(&zr2, &resj.z)); /* Normal doubling. */ secp256k1_gej_double_var(&resj, &gej[i2], NULL); - ge_equals_gej(&ref, &resj); + CHECK(secp256k1_gej_eq_ge_var(&resj, &ref)); + /* Constant-time doubling. */ + secp256k1_gej_double(&resj, &gej[i2]); + CHECK(secp256k1_gej_eq_ge_var(&resj, &ref)); } /* Test adding opposites. */ @@ -2019,12 +3788,12 @@ void test_ge(void) { if (i1 == 0) { CHECK(secp256k1_ge_is_infinity(&ge[i1])); CHECK(secp256k1_gej_is_infinity(&gej[i1])); - ge_equals_gej(&ref, &gej[i2]); + CHECK(secp256k1_gej_eq_ge_var(&gej[i2], &ref)); } if (i2 == 0) { CHECK(secp256k1_ge_is_infinity(&ge[i2])); CHECK(secp256k1_gej_is_infinity(&gej[i2])); - ge_equals_gej(&ref, &gej[i1]); + CHECK(secp256k1_gej_eq_ge_var(&gej[i1], &ref)); } } } @@ -2032,12 +3801,12 @@ void test_ge(void) { /* Test adding all points together in random order equals infinity. */ { secp256k1_gej sum = SECP256K1_GEJ_CONST_INFINITY; - secp256k1_gej *gej_shuffled = (secp256k1_gej *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_gej)); + secp256k1_gej *gej_shuffled = (secp256k1_gej *)checked_malloc(&CTX->error_callback, (4 * runs + 1) * sizeof(secp256k1_gej)); for (i = 0; i < 4 * runs + 1; i++) { gej_shuffled[i] = gej[i]; } for (i = 0; i < 4 * runs + 1; i++) { - int swap = i + secp256k1_rand_int(4 * runs + 1 - i); + int swap = i + testrand_int(4 * runs + 1 - i); if (swap != i) { secp256k1_gej t = gej_shuffled[i]; gej_shuffled[i] = gej_shuffled[swap]; @@ -2051,37 +3820,109 @@ void test_ge(void) { free(gej_shuffled); } - /* Test batch gej -> ge conversion with and without known z ratios. */ + /* Test batch gej -> ge conversion without known z ratios. */ { - secp256k1_fe *zr = (secp256k1_fe *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_fe)); - secp256k1_ge *ge_set_table = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); - secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); - for (i = 0; i < 4 * runs + 1; i++) { - /* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */ - if (i < 4 * runs) { - secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z); - } - } - secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1); - secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback); + secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&CTX->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); + secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1); for (i = 0; i < 4 * runs + 1; i++) { secp256k1_fe s; - random_fe_non_zero(&s); + testutil_random_fe_non_zero(&s); secp256k1_gej_rescale(&gej[i], &s); - ge_equals_gej(&ge_set_table[i], &gej[i]); - ge_equals_gej(&ge_set_all[i], &gej[i]); + CHECK(secp256k1_gej_eq_ge_var(&gej[i], &ge_set_all[i])); } - free(ge_set_table); free(ge_set_all); - free(zr); + } + + /* Test that all elements have X coordinates on the curve. */ + for (i = 1; i < 4 * runs + 1; i++) { + secp256k1_fe n; + CHECK(secp256k1_ge_x_on_curve_var(&ge[i].x)); + /* And the same holds after random rescaling. */ + secp256k1_fe_mul(&n, &zf, &ge[i].x); + CHECK(secp256k1_ge_x_frac_on_curve_var(&n, &zf)); + } + + /* Test correspondence of secp256k1_ge_x{,_frac}_on_curve_var with ge_set_xo. */ + { + secp256k1_fe n; + secp256k1_ge q; + int ret_on_curve, ret_frac_on_curve, ret_set_xo; + secp256k1_fe_mul(&n, &zf, &r); + ret_on_curve = secp256k1_ge_x_on_curve_var(&r); + ret_frac_on_curve = secp256k1_ge_x_frac_on_curve_var(&n, &zf); + ret_set_xo = secp256k1_ge_set_xo_var(&q, &r, 0); + CHECK(ret_on_curve == ret_frac_on_curve); + CHECK(ret_on_curve == ret_set_xo); + if (ret_set_xo) CHECK(secp256k1_fe_equal(&r, &q.x)); + } + + /* Test batch gej -> ge conversion with many infinities. */ + for (i = 0; i < 4 * runs + 1; i++) { + int odd; + testutil_random_ge_test(&ge[i]); + odd = secp256k1_fe_is_odd(&ge[i].x); + CHECK(odd == 0 || odd == 1); + /* randomly set half the points to infinity */ + if (odd == i % 2) { + secp256k1_ge_set_infinity(&ge[i]); + } + secp256k1_gej_set_ge(&gej[i], &ge[i]); + } + /* batch convert */ + secp256k1_ge_set_all_gej_var(ge, gej, 4 * runs + 1); + /* check result */ + for (i = 0; i < 4 * runs + 1; i++) { + CHECK(secp256k1_gej_eq_ge_var(&gej[i], &ge[i])); + } + + /* Test batch gej -> ge conversion with all infinities. */ + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_gej_set_infinity(&gej[i]); + } + /* batch convert */ + secp256k1_ge_set_all_gej_var(ge, gej, 4 * runs + 1); + /* check result */ + for (i = 0; i < 4 * runs + 1; i++) { + CHECK(secp256k1_ge_is_infinity(&ge[i])); } free(ge); free(gej); - free(zinv); } -void test_add_neg_y_diff_x(void) { +static void test_intialized_inf(void) { + secp256k1_ge p; + secp256k1_gej pj, npj, infj1, infj2, infj3; + secp256k1_fe zinv; + + /* Test that adding P+(-P) results in a fully initialized infinity*/ + testutil_random_ge_test(&p); + secp256k1_gej_set_ge(&pj, &p); + secp256k1_gej_neg(&npj, &pj); + + secp256k1_gej_add_var(&infj1, &pj, &npj, NULL); + CHECK(secp256k1_gej_is_infinity(&infj1)); + CHECK(secp256k1_fe_is_zero(&infj1.x)); + CHECK(secp256k1_fe_is_zero(&infj1.y)); + CHECK(secp256k1_fe_is_zero(&infj1.z)); + + secp256k1_gej_add_ge_var(&infj2, &npj, &p, NULL); + CHECK(secp256k1_gej_is_infinity(&infj2)); + CHECK(secp256k1_fe_is_zero(&infj2.x)); + CHECK(secp256k1_fe_is_zero(&infj2.y)); + CHECK(secp256k1_fe_is_zero(&infj2.z)); + + secp256k1_fe_set_int(&zinv, 1); + secp256k1_gej_add_zinv_var(&infj3, &npj, &p, &zinv); + CHECK(secp256k1_gej_is_infinity(&infj3)); + CHECK(secp256k1_fe_is_zero(&infj3.x)); + CHECK(secp256k1_fe_is_zero(&infj3.y)); + CHECK(secp256k1_fe_is_zero(&infj3.z)); + + +} + +static void test_add_neg_y_diff_x(void) { /* The point of this test is to check that we can add two points * whose y-coordinates are negatives of each other but whose x * coordinates differ. If the x-coordinates were the same, these @@ -2095,22 +3936,15 @@ void test_add_neg_y_diff_x(void) { * which this test is a regression test for. * * These points were generated in sage as - * # secp256k1 params - * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) - * C = EllipticCurve ([F (0), F (7)]) - * G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798) - * N = FiniteField(G.order()) * - * # endomorphism values (lambda is 1^{1/3} in N, beta is 1^{1/3} in F) - * x = polygen(N) - * lam = (1 - x^3).roots()[1][0] + * load("secp256k1_params.sage") * * # random "bad pair" * P = C.random_element() - * Q = -int(lam) * P - * print " P: %x %x" % P.xy() - * print " Q: %x %x" % Q.xy() - * print "P + Q: %x %x" % (P + Q).xy() + * Q = -int(LAMBDA) * P + * print(" P: %x %x" % P.xy()) + * print(" Q: %x %x" % Q.xy()) + * print("P + Q: %x %x" % (P + Q).xy()) */ secp256k1_gej aj = SECP256K1_GEJ_CONST( 0x8d24cd95, 0x0a355af1, 0x3c543505, 0x44238d30, @@ -2137,27 +3971,72 @@ void test_add_neg_y_diff_x(void) { secp256k1_gej_add_var(&resj, &aj, &bj, NULL); secp256k1_ge_set_gej(&res, &resj); - ge_equals_gej(&res, &sumj); + CHECK(secp256k1_gej_eq_ge_var(&sumj, &res)); secp256k1_gej_add_ge(&resj, &aj, &b); secp256k1_ge_set_gej(&res, &resj); - ge_equals_gej(&res, &sumj); + CHECK(secp256k1_gej_eq_ge_var(&sumj, &res)); secp256k1_gej_add_ge_var(&resj, &aj, &b, NULL); secp256k1_ge_set_gej(&res, &resj); - ge_equals_gej(&res, &sumj); + CHECK(secp256k1_gej_eq_ge_var(&sumj, &res)); } -void run_ge(void) { +static void run_ge(void) { int i; - for (i = 0; i < count * 32; i++) { + for (i = 0; i < COUNT * 32; i++) { test_ge(); } test_add_neg_y_diff_x(); + test_intialized_inf(); +} + +static void test_gej_cmov(const secp256k1_gej *a, const secp256k1_gej *b) { + secp256k1_gej t = *a; + secp256k1_gej_cmov(&t, b, 0); + CHECK(gej_xyz_equals_gej(&t, a)); + secp256k1_gej_cmov(&t, b, 1); + CHECK(gej_xyz_equals_gej(&t, b)); +} + +static void run_gej(void) { + int i; + secp256k1_gej a, b; + + /* Tests for secp256k1_gej_cmov */ + for (i = 0; i < COUNT; i++) { + secp256k1_gej_set_infinity(&a); + secp256k1_gej_set_infinity(&b); + test_gej_cmov(&a, &b); + + testutil_random_gej_test(&a); + test_gej_cmov(&a, &b); + test_gej_cmov(&b, &a); + + b = a; + test_gej_cmov(&a, &b); + + testutil_random_gej_test(&b); + test_gej_cmov(&a, &b); + test_gej_cmov(&b, &a); + } + + /* Tests for secp256k1_gej_eq_var */ + for (i = 0; i < COUNT; i++) { + secp256k1_fe fe; + testutil_random_gej_test(&a); + testutil_random_gej_test(&b); + CHECK(!secp256k1_gej_eq_var(&a, &b)); + + b = a; + testutil_random_fe_non_zero_test(&fe); + secp256k1_gej_rescale(&a, &fe); + CHECK(secp256k1_gej_eq_var(&a, &b)); + } } -void test_ec_combine(void) { - secp256k1_scalar sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); +static void test_ec_combine(void) { + secp256k1_scalar sum = secp256k1_scalar_zero; secp256k1_pubkey data[6]; const secp256k1_pubkey* d[6]; secp256k1_pubkey sd; @@ -2167,103 +4046,148 @@ void test_ec_combine(void) { int i; for (i = 1; i <= 6; i++) { secp256k1_scalar s; - random_scalar_order_test(&s); + testutil_random_scalar_order_test(&s); secp256k1_scalar_add(&sum, &sum, &s); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &s); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &Qj, &s); secp256k1_ge_set_gej(&Q, &Qj); secp256k1_pubkey_save(&data[i - 1], &Q); d[i - 1] = &data[i - 1]; - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sum); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &Qj, &sum); secp256k1_ge_set_gej(&Q, &Qj); secp256k1_pubkey_save(&sd, &Q); - CHECK(secp256k1_ec_pubkey_combine(ctx, &sd2, d, i) == 1); - CHECK(memcmp(&sd, &sd2, sizeof(sd)) == 0); + CHECK(secp256k1_ec_pubkey_combine(CTX, &sd2, d, i) == 1); + CHECK(secp256k1_memcmp_var(&sd, &sd2, sizeof(sd)) == 0); } } -void run_ec_combine(void) { +static void run_ec_combine(void) { int i; - for (i = 0; i < count * 8; i++) { + for (i = 0; i < COUNT * 8; i++) { test_ec_combine(); } } -void test_group_decompress(const secp256k1_fe* x) { +static void test_group_decompress(const secp256k1_fe* x) { /* The input itself, normalized. */ secp256k1_fe fex = *x; - secp256k1_fe fez; - /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */ - secp256k1_ge ge_quad, ge_even, ge_odd; - secp256k1_gej gej_quad; + /* Results of set_xo_var(..., 0), set_xo_var(..., 1). */ + secp256k1_ge ge_even, ge_odd; /* Return values of the above calls. */ - int res_quad, res_even, res_odd; + int res_even, res_odd; secp256k1_fe_normalize_var(&fex); - res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex); res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0); res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1); - CHECK(res_quad == res_even); - CHECK(res_quad == res_odd); + CHECK(res_even == res_odd); - if (res_quad) { - secp256k1_fe_normalize_var(&ge_quad.x); + if (res_even) { secp256k1_fe_normalize_var(&ge_odd.x); secp256k1_fe_normalize_var(&ge_even.x); - secp256k1_fe_normalize_var(&ge_quad.y); secp256k1_fe_normalize_var(&ge_odd.y); secp256k1_fe_normalize_var(&ge_even.y); /* No infinity allowed. */ - CHECK(!ge_quad.infinity); CHECK(!ge_even.infinity); CHECK(!ge_odd.infinity); /* Check that the x coordinates check out. */ - CHECK(secp256k1_fe_equal_var(&ge_quad.x, x)); - CHECK(secp256k1_fe_equal_var(&ge_even.x, x)); - CHECK(secp256k1_fe_equal_var(&ge_odd.x, x)); - - /* Check that the Y coordinate result in ge_quad is a square. */ - CHECK(secp256k1_fe_is_quad_var(&ge_quad.y)); + CHECK(secp256k1_fe_equal(&ge_even.x, x)); + CHECK(secp256k1_fe_equal(&ge_odd.x, x)); /* Check odd/even Y in ge_odd, ge_even. */ CHECK(secp256k1_fe_is_odd(&ge_odd.y)); CHECK(!secp256k1_fe_is_odd(&ge_even.y)); - - /* Check secp256k1_gej_has_quad_y_var. */ - secp256k1_gej_set_ge(&gej_quad, &ge_quad); - CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); - do { - random_fe_test(&fez); - } while (secp256k1_fe_is_zero(&fez)); - secp256k1_gej_rescale(&gej_quad, &fez); - CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); - secp256k1_gej_neg(&gej_quad, &gej_quad); - CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); - do { - random_fe_test(&fez); - } while (secp256k1_fe_is_zero(&fez)); - secp256k1_gej_rescale(&gej_quad, &fez); - CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); - secp256k1_gej_neg(&gej_quad, &gej_quad); - CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); } } -void run_group_decompress(void) { +static void run_group_decompress(void) { int i; - for (i = 0; i < count * 4; i++) { + for (i = 0; i < COUNT * 4; i++) { secp256k1_fe fe; - random_fe_test(&fe); + testutil_random_fe_test(&fe); test_group_decompress(&fe); } } /***** ECMULT TESTS *****/ -void run_ecmult_chain(void) { +static void test_pre_g_table(const secp256k1_ge_storage * pre_g, size_t n) { + /* Tests the pre_g / pre_g_128 tables for consistency. + * For independent verification we take a "geometric" approach to verification. + * We check that every entry is on-curve. + * We check that for consecutive entries p and q, that p + gg - q = 0 by checking + * (1) p, gg, and -q are colinear. + * (2) p, gg, and -q are all distinct. + * where gg is twice the generator, where the generator is the first table entry. + * + * Checking the table's generators are correct is done in run_ecmult_pre_g. + */ + secp256k1_gej g2; + secp256k1_ge p, q, gg; + secp256k1_fe dpx, dpy, dqx, dqy; + size_t i; + + CHECK(0 < n); + + secp256k1_ge_from_storage(&p, &pre_g[0]); + CHECK(secp256k1_ge_is_valid_var(&p)); + + secp256k1_gej_set_ge(&g2, &p); + secp256k1_gej_double_var(&g2, &g2, NULL); + secp256k1_ge_set_gej_var(&gg, &g2); + for (i = 1; i < n; ++i) { + secp256k1_fe_negate(&dpx, &p.x, 1); secp256k1_fe_add(&dpx, &gg.x); secp256k1_fe_normalize_weak(&dpx); + secp256k1_fe_negate(&dpy, &p.y, 1); secp256k1_fe_add(&dpy, &gg.y); secp256k1_fe_normalize_weak(&dpy); + /* Check that p is not equal to gg */ + CHECK(!secp256k1_fe_normalizes_to_zero_var(&dpx) || !secp256k1_fe_normalizes_to_zero_var(&dpy)); + + secp256k1_ge_from_storage(&q, &pre_g[i]); + CHECK(secp256k1_ge_is_valid_var(&q)); + + secp256k1_fe_negate(&dqx, &q.x, 1); secp256k1_fe_add(&dqx, &gg.x); + dqy = q.y; secp256k1_fe_add(&dqy, &gg.y); + /* Check that -q is not equal to gg */ + CHECK(!secp256k1_fe_normalizes_to_zero_var(&dqx) || !secp256k1_fe_normalizes_to_zero_var(&dqy)); + + /* Check that -q is not equal to p */ + CHECK(!secp256k1_fe_equal(&dpx, &dqx) || !secp256k1_fe_equal(&dpy, &dqy)); + + /* Check that p, -q and gg are colinear */ + secp256k1_fe_mul(&dpx, &dpx, &dqy); + secp256k1_fe_mul(&dpy, &dpy, &dqx); + CHECK(secp256k1_fe_equal(&dpx, &dpy)); + + p = q; + } +} + +static void run_ecmult_pre_g(void) { + secp256k1_ge_storage gs; + secp256k1_gej gj; + secp256k1_ge g; + size_t i; + + /* Check that the pre_g and pre_g_128 tables are consistent. */ + test_pre_g_table(secp256k1_pre_g, ECMULT_TABLE_SIZE(WINDOW_G)); + test_pre_g_table(secp256k1_pre_g_128, ECMULT_TABLE_SIZE(WINDOW_G)); + + /* Check the first entry from the pre_g table. */ + secp256k1_ge_to_storage(&gs, &secp256k1_ge_const_g); + CHECK(secp256k1_memcmp_var(&gs, &secp256k1_pre_g[0], sizeof(gs)) == 0); + + /* Check the first entry from the pre_g_128 table. */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + for (i = 0; i < 128; ++i) { + secp256k1_gej_double_var(&gj, &gj, NULL); + } + secp256k1_ge_set_gej(&g, &gj); + secp256k1_ge_to_storage(&gs, &g); + CHECK(secp256k1_memcmp_var(&gs, &secp256k1_pre_g_128[0], sizeof(gs)) == 0); +} + +static void run_ecmult_chain(void) { /* random starting point A (on the curve) */ secp256k1_gej a = SECP256K1_GEJ_CONST( 0x8b30bbe9, 0xae2a9906, 0x96b22f67, 0x0709dff3, @@ -2284,8 +4208,8 @@ void run_ecmult_chain(void) { static const secp256k1_scalar xf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x1337); static const secp256k1_scalar gf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x7113); /* accumulators with the resulting coefficients to A and G */ - secp256k1_scalar ae = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); - secp256k1_scalar ge = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar ae = secp256k1_scalar_one; + secp256k1_scalar ge = secp256k1_scalar_zero; /* actual points */ secp256k1_gej x; secp256k1_gej x2; @@ -2293,9 +4217,9 @@ void run_ecmult_chain(void) { /* the point being computed */ x = a; - for (i = 0; i < 200*count; i++) { + for (i = 0; i < 200*COUNT; i++) { /* in each iteration, compute X = xn*X + gn*G; */ - secp256k1_ecmult(&ctx->ecmult_ctx, &x, &x, &xn, &gn); + secp256k1_ecmult(&x, &x, &xn, &gn); /* also compute ae and ge: the actual accumulated factors for A and G */ /* if X was (ae*A+ge*G), xn*X + gn*G results in (xn*ae*A + (xn*ge+gn)*G) */ secp256k1_scalar_mul(&ae, &ae, &xn); @@ -2314,36 +4238,28 @@ void run_ecmult_chain(void) { 0xB95CBCA2, 0xC77DA786, 0x539BE8FD, 0x53354D2D, 0x3B4F566A, 0xE6580454, 0x07ED6015, 0xEE1B2A88 ); - - secp256k1_gej_neg(&rp, &rp); - secp256k1_gej_add_var(&rp, &rp, &x, NULL); - CHECK(secp256k1_gej_is_infinity(&rp)); + CHECK(secp256k1_gej_eq_var(&rp, &x)); } } /* redo the computation, but directly with the resulting ae and ge coefficients: */ - secp256k1_ecmult(&ctx->ecmult_ctx, &x2, &a, &ae, &ge); - secp256k1_gej_neg(&x2, &x2); - secp256k1_gej_add_var(&x2, &x2, &x, NULL); - CHECK(secp256k1_gej_is_infinity(&x2)); + secp256k1_ecmult(&x2, &a, &ae, &ge); + CHECK(secp256k1_gej_eq_var(&x, &x2)); } -void test_point_times_order(const secp256k1_gej *point) { +static void test_point_times_order(const secp256k1_gej *point) { /* X * (point + G) + (order-X) * (pointer + G) = 0 */ secp256k1_scalar x; secp256k1_scalar nx; - secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); - secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); secp256k1_gej res1, res2; secp256k1_ge res3; unsigned char pub[65]; size_t psize = 65; - random_scalar_order_test(&x); + testutil_random_scalar_order_test(&x); secp256k1_scalar_negate(&nx, &x); - secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &x, &x); /* calc res1 = x * point + x * G; */ - secp256k1_ecmult(&ctx->ecmult_ctx, &res2, point, &nx, &nx); /* calc res2 = (order - x) * point + (order - x) * G; */ + secp256k1_ecmult(&res1, point, &x, &x); /* calc res1 = x * point + x * G; */ + secp256k1_ecmult(&res2, point, &nx, &nx); /* calc res2 = (order - x) * point + (order - x) * G; */ secp256k1_gej_add_var(&res1, &res1, &res2, NULL); CHECK(secp256k1_gej_is_infinity(&res1)); - CHECK(secp256k1_gej_is_valid_var(&res1) == 0); secp256k1_ge_set_gej(&res3, &res1); CHECK(secp256k1_ge_is_infinity(&res3)); CHECK(secp256k1_ge_is_valid_var(&res3) == 0); @@ -2351,18 +4267,98 @@ void test_point_times_order(const secp256k1_gej *point) { psize = 65; CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 1) == 0); /* check zero/one edge cases */ - secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &zero); + secp256k1_ecmult(&res1, point, &secp256k1_scalar_zero, &secp256k1_scalar_zero); secp256k1_ge_set_gej(&res3, &res1); CHECK(secp256k1_ge_is_infinity(&res3)); - secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &one, &zero); + secp256k1_ecmult(&res1, point, &secp256k1_scalar_one, &secp256k1_scalar_zero); secp256k1_ge_set_gej(&res3, &res1); - ge_equals_gej(&res3, point); - secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &one); + CHECK(secp256k1_gej_eq_ge_var(point, &res3)); + secp256k1_ecmult(&res1, point, &secp256k1_scalar_zero, &secp256k1_scalar_one); secp256k1_ge_set_gej(&res3, &res1); - ge_equals_ge(&res3, &secp256k1_ge_const_g); + CHECK(secp256k1_ge_eq_var(&secp256k1_ge_const_g, &res3)); +} + +/* These scalars reach large (in absolute value) outputs when fed to secp256k1_scalar_split_lambda. + * + * They are computed as: + * - For a in [-2, -1, 0, 1, 2]: + * - For b in [-3, -1, 1, 3]: + * - Output (a*LAMBDA + (ORDER+b)/2) % ORDER + */ +static const secp256k1_scalar scalars_near_split_bounds[20] = { + SECP256K1_SCALAR_CONST(0xd938a566, 0x7f479e3e, 0xb5b3c7fa, 0xefdb3749, 0x3aa0585c, 0xc5ea2367, 0xe1b660db, 0x0209e6fc), + SECP256K1_SCALAR_CONST(0xd938a566, 0x7f479e3e, 0xb5b3c7fa, 0xefdb3749, 0x3aa0585c, 0xc5ea2367, 0xe1b660db, 0x0209e6fd), + SECP256K1_SCALAR_CONST(0xd938a566, 0x7f479e3e, 0xb5b3c7fa, 0xefdb3749, 0x3aa0585c, 0xc5ea2367, 0xe1b660db, 0x0209e6fe), + SECP256K1_SCALAR_CONST(0xd938a566, 0x7f479e3e, 0xb5b3c7fa, 0xefdb3749, 0x3aa0585c, 0xc5ea2367, 0xe1b660db, 0x0209e6ff), + SECP256K1_SCALAR_CONST(0x2c9c52b3, 0x3fa3cf1f, 0x5ad9e3fd, 0x77ed9ba5, 0xb294b893, 0x3722e9a5, 0x00e698ca, 0x4cf7632d), + SECP256K1_SCALAR_CONST(0x2c9c52b3, 0x3fa3cf1f, 0x5ad9e3fd, 0x77ed9ba5, 0xb294b893, 0x3722e9a5, 0x00e698ca, 0x4cf7632e), + SECP256K1_SCALAR_CONST(0x2c9c52b3, 0x3fa3cf1f, 0x5ad9e3fd, 0x77ed9ba5, 0xb294b893, 0x3722e9a5, 0x00e698ca, 0x4cf7632f), + SECP256K1_SCALAR_CONST(0x2c9c52b3, 0x3fa3cf1f, 0x5ad9e3fd, 0x77ed9ba5, 0xb294b893, 0x3722e9a5, 0x00e698ca, 0x4cf76330), + SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd576e735, 0x57a4501d, 0xdfe92f46, 0x681b209f), + SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd576e735, 0x57a4501d, 0xdfe92f46, 0x681b20a0), + SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd576e735, 0x57a4501d, 0xdfe92f46, 0x681b20a1), + SECP256K1_SCALAR_CONST(0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xd576e735, 0x57a4501d, 0xdfe92f46, 0x681b20a2), + SECP256K1_SCALAR_CONST(0xd363ad4c, 0xc05c30e0, 0xa5261c02, 0x88126459, 0xf85915d7, 0x7825b696, 0xbeebc5c2, 0x833ede11), + SECP256K1_SCALAR_CONST(0xd363ad4c, 0xc05c30e0, 0xa5261c02, 0x88126459, 0xf85915d7, 0x7825b696, 0xbeebc5c2, 0x833ede12), + SECP256K1_SCALAR_CONST(0xd363ad4c, 0xc05c30e0, 0xa5261c02, 0x88126459, 0xf85915d7, 0x7825b696, 0xbeebc5c2, 0x833ede13), + SECP256K1_SCALAR_CONST(0xd363ad4c, 0xc05c30e0, 0xa5261c02, 0x88126459, 0xf85915d7, 0x7825b696, 0xbeebc5c2, 0x833ede14), + SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a42), + SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a43), + SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a44), + SECP256K1_SCALAR_CONST(0x26c75a99, 0x80b861c1, 0x4a4c3805, 0x1024c8b4, 0x704d760e, 0xe95e7cd3, 0xde1bfdb1, 0xce2c5a45) +}; + +static void test_ecmult_target(const secp256k1_scalar* target, int mode) { + /* Mode: 0=ecmult_gen, 1=ecmult, 2=ecmult_const */ + secp256k1_scalar n1, n2; + secp256k1_ge p; + secp256k1_gej pj, p1j, p2j, ptj; + + /* Generate random n1,n2 such that n1+n2 = -target. */ + testutil_random_scalar_order_test(&n1); + secp256k1_scalar_add(&n2, &n1, target); + secp256k1_scalar_negate(&n2, &n2); + + /* Generate a random input point. */ + if (mode != 0) { + testutil_random_ge_test(&p); + secp256k1_gej_set_ge(&pj, &p); + } + + /* EC multiplications */ + if (mode == 0) { + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &p1j, &n1); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &p2j, &n2); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &ptj, target); + } else if (mode == 1) { + secp256k1_ecmult(&p1j, &pj, &n1, &secp256k1_scalar_zero); + secp256k1_ecmult(&p2j, &pj, &n2, &secp256k1_scalar_zero); + secp256k1_ecmult(&ptj, &pj, target, &secp256k1_scalar_zero); + } else { + secp256k1_ecmult_const(&p1j, &p, &n1); + secp256k1_ecmult_const(&p2j, &p, &n2); + secp256k1_ecmult_const(&ptj, &p, target); + } + + /* Add them all up: n1*P + n2*P + target*P = (n1+n2+target)*P = (n1+n1-n1-n2)*P = 0. */ + secp256k1_gej_add_var(&ptj, &ptj, &p1j, NULL); + secp256k1_gej_add_var(&ptj, &ptj, &p2j, NULL); + CHECK(secp256k1_gej_is_infinity(&ptj)); +} + +static void run_ecmult_near_split_bound(void) { + int i; + unsigned j; + for (i = 0; i < 4*COUNT; ++i) { + for (j = 0; j < sizeof(scalars_near_split_bounds) / sizeof(scalars_near_split_bounds[0]); ++j) { + test_ecmult_target(&scalars_near_split_bounds[j], 0); + test_ecmult_target(&scalars_near_split_bounds[j], 1); + test_ecmult_target(&scalars_near_split_bounds[j], 2); + } + } } -void run_point_times_order(void) { +static void run_point_times_order(void) { int i; secp256k1_fe x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2); static const secp256k1_fe xr = SECP256K1_FE_CONST( @@ -2375,16 +4371,15 @@ void run_point_times_order(void) { secp256k1_gej j; CHECK(secp256k1_ge_is_valid_var(&p)); secp256k1_gej_set_ge(&j, &p); - CHECK(secp256k1_gej_is_valid_var(&j)); test_point_times_order(&j); } secp256k1_fe_sqr(&x, &x); } secp256k1_fe_normalize_var(&x); - CHECK(secp256k1_fe_equal_var(&x, &xr)); + CHECK(secp256k1_fe_equal(&x, &xr)); } -void ecmult_const_random_mult(void) { +static void ecmult_const_random_mult(void) { /* random starting point A (on the curve) */ secp256k1_ge a = SECP256K1_GE_CONST( 0x6d986544, 0x57ff52b8, 0xcf1b8126, 0x5b802a5b, @@ -2408,18 +4403,18 @@ void ecmult_const_random_mult(void) { secp256k1_ecmult_const(&b, &a, &xn); CHECK(secp256k1_ge_is_valid_var(&a)); - ge_equals_gej(&expected_b, &b); + CHECK(secp256k1_gej_eq_ge_var(&b, &expected_b)); } -void ecmult_const_commutativity(void) { +static void ecmult_const_commutativity(void) { secp256k1_scalar a; secp256k1_scalar b; secp256k1_gej res1; secp256k1_gej res2; secp256k1_ge mid1; secp256k1_ge mid2; - random_scalar_order_test(&a); - random_scalar_order_test(&b); + testutil_random_scalar_order_test(&a); + testutil_random_scalar_order_test(&b); secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); @@ -2429,32 +4424,134 @@ void ecmult_const_commutativity(void) { secp256k1_ecmult_const(&res2, &mid2, &a); secp256k1_ge_set_gej(&mid1, &res1); secp256k1_ge_set_gej(&mid2, &res2); - ge_equals_ge(&mid1, &mid2); + CHECK(secp256k1_ge_eq_var(&mid1, &mid2)); } -void ecmult_const_mult_zero_one(void) { - secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); - secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); +static void ecmult_const_mult_zero_one(void) { + secp256k1_scalar s; secp256k1_scalar negone; secp256k1_gej res1; secp256k1_ge res2; secp256k1_ge point; - secp256k1_scalar_negate(&negone, &one); + secp256k1_ge inf; - random_group_element_test(&point); - secp256k1_ecmult_const(&res1, &point, &zero); - secp256k1_ge_set_gej(&res2, &res1); - CHECK(secp256k1_ge_is_infinity(&res2)); - secp256k1_ecmult_const(&res1, &point, &one); + testutil_random_scalar_order_test(&s); + secp256k1_scalar_negate(&negone, &secp256k1_scalar_one); + testutil_random_ge_test(&point); + secp256k1_ge_set_infinity(&inf); + + /* 0*point */ + secp256k1_ecmult_const(&res1, &point, &secp256k1_scalar_zero); + CHECK(secp256k1_gej_is_infinity(&res1)); + + /* s*inf */ + secp256k1_ecmult_const(&res1, &inf, &s); + CHECK(secp256k1_gej_is_infinity(&res1)); + + /* 1*point */ + secp256k1_ecmult_const(&res1, &point, &secp256k1_scalar_one); secp256k1_ge_set_gej(&res2, &res1); - ge_equals_ge(&res2, &point); + CHECK(secp256k1_ge_eq_var(&res2, &point)); + + /* -1*point */ secp256k1_ecmult_const(&res1, &point, &negone); secp256k1_gej_neg(&res1, &res1); secp256k1_ge_set_gej(&res2, &res1); - ge_equals_ge(&res2, &point); + CHECK(secp256k1_ge_eq_var(&res2, &point)); +} + +static void ecmult_const_check_result(const secp256k1_ge *A, const secp256k1_scalar* q, const secp256k1_gej *res) { + secp256k1_gej pointj, res2j; + secp256k1_ge res2; + secp256k1_gej_set_ge(&pointj, A); + secp256k1_ecmult(&res2j, &pointj, q, &secp256k1_scalar_zero); + secp256k1_ge_set_gej(&res2, &res2j); + CHECK(secp256k1_gej_eq_ge_var(res, &res2)); +} + +static void ecmult_const_edges(void) { + secp256k1_scalar q; + secp256k1_ge point; + secp256k1_gej res; + size_t i; + size_t cases = 1 + sizeof(scalars_near_split_bounds) / sizeof(scalars_near_split_bounds[0]); + + /* We are trying to reach the following edge cases (variables are defined as + * in ecmult_const_impl.h): + * 1. i = 0: s = 0 <=> q = -K + * 2. i > 0: v1, v2 large values + * <=> s1, s2 large values + * <=> s = scalars_near_split_bounds[i] + * <=> q = 2*scalars_near_split_bounds[i] - K + */ + for (i = 0; i < cases; ++i) { + secp256k1_scalar_negate(&q, &secp256k1_ecmult_const_K); + if (i > 0) { + secp256k1_scalar_add(&q, &q, &scalars_near_split_bounds[i - 1]); + secp256k1_scalar_add(&q, &q, &scalars_near_split_bounds[i - 1]); + } + testutil_random_ge_test(&point); + secp256k1_ecmult_const(&res, &point, &q); + ecmult_const_check_result(&point, &q, &res); + } +} + +static void ecmult_const_mult_xonly(void) { + int i; + + /* Test correspondence between secp256k1_ecmult_const and secp256k1_ecmult_const_xonly. */ + for (i = 0; i < 2*COUNT; ++i) { + secp256k1_ge base; + secp256k1_gej basej, resj; + secp256k1_fe n, d, resx, v; + secp256k1_scalar q; + int res; + /* Random base point. */ + testutil_random_ge_test(&base); + /* Random scalar to multiply it with. */ + testutil_random_scalar_order_test(&q); + /* If i is odd, n=d*base.x for random non-zero d */ + if (i & 1) { + testutil_random_fe_non_zero_test(&d); + secp256k1_fe_mul(&n, &base.x, &d); + } else { + n = base.x; + } + /* Perform x-only multiplication. */ + res = secp256k1_ecmult_const_xonly(&resx, &n, (i & 1) ? &d : NULL, &q, i & 2); + CHECK(res); + /* Perform normal multiplication. */ + secp256k1_gej_set_ge(&basej, &base); + secp256k1_ecmult(&resj, &basej, &q, NULL); + /* Check that resj's X coordinate corresponds with resx. */ + secp256k1_fe_sqr(&v, &resj.z); + secp256k1_fe_mul(&v, &v, &resx); + CHECK(fe_equal(&v, &resj.x)); + } + + /* Test that secp256k1_ecmult_const_xonly correctly rejects X coordinates not on curve. */ + for (i = 0; i < 2*COUNT; ++i) { + secp256k1_fe x, n, d, r; + int res; + secp256k1_scalar q; + testutil_random_scalar_order_test(&q); + /* Generate random X coordinate not on the curve. */ + do { + testutil_random_fe_test(&x); + } while (secp256k1_ge_x_on_curve_var(&x)); + /* If i is odd, n=d*x for random non-zero d. */ + if (i & 1) { + testutil_random_fe_non_zero_test(&d); + secp256k1_fe_mul(&n, &x, &d); + } else { + n = x; + } + res = secp256k1_ecmult_const_xonly(&r, &n, (i & 1) ? &d : NULL, &q, 0); + CHECK(res == 0); + } } -void ecmult_const_chain_multiply(void) { +static void ecmult_const_chain_multiply(void) { /* Check known result (randomly generated test problem from sage) */ const secp256k1_scalar scalar = SECP256K1_SCALAR_CONST( 0x4968d524, 0x2abf9b7a, 0x466abbcf, 0x34b11b6d, @@ -2477,17 +4574,639 @@ void ecmult_const_chain_multiply(void) { secp256k1_ecmult_const(&point, &tmp, &scalar); } secp256k1_ge_set_gej(&res, &point); - ge_equals_gej(&res, &expected_point); + CHECK(secp256k1_gej_eq_ge_var(&expected_point, &res)); } -void run_ecmult_const_tests(void) { +static void run_ecmult_const_tests(void) { ecmult_const_mult_zero_one(); + ecmult_const_edges(); ecmult_const_random_mult(); ecmult_const_commutativity(); ecmult_const_chain_multiply(); + ecmult_const_mult_xonly(); +} + +typedef struct { + secp256k1_scalar *sc; + secp256k1_ge *pt; +} ecmult_multi_data; + +static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_multi_data *data = (ecmult_multi_data*) cbdata; + *sc = data->sc[idx]; + *pt = data->pt[idx]; + return 1; +} + +static int ecmult_multi_false_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + (void)sc; + (void)pt; + (void)idx; + (void)cbdata; + return 0; +} + +static void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func ecmult_multi) { + int ncount; + secp256k1_scalar sc[32]; + secp256k1_ge pt[32]; + secp256k1_gej r; + secp256k1_gej r2; + ecmult_multi_data data; + + data.sc = sc; + data.pt = pt; + + /* No points to multiply */ + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, NULL, ecmult_multi_callback, &data, 0)); + + /* Check 1- and 2-point multiplies against ecmult */ + for (ncount = 0; ncount < COUNT; ncount++) { + secp256k1_ge ptg; + secp256k1_gej ptgj; + testutil_random_scalar_order(&sc[0]); + testutil_random_scalar_order(&sc[1]); + + testutil_random_ge_test(&ptg); + secp256k1_gej_set_ge(&ptgj, &ptg); + pt[0] = ptg; + pt[1] = secp256k1_ge_const_g; + + /* only G scalar */ + secp256k1_ecmult(&r2, &ptgj, &secp256k1_scalar_zero, &sc[0]); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0)); + CHECK(secp256k1_gej_eq_var(&r, &r2)); + + /* 1-point */ + secp256k1_ecmult(&r2, &ptgj, &sc[0], &secp256k1_scalar_zero); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 1)); + CHECK(secp256k1_gej_eq_var(&r, &r2)); + + /* Try to multiply 1 point, but callback returns false */ + CHECK(!ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_false_callback, &data, 1)); + + /* 2-point */ + secp256k1_ecmult(&r2, &ptgj, &sc[0], &sc[1]); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 2)); + CHECK(secp256k1_gej_eq_var(&r, &r2)); + + /* 2-point with G scalar */ + secp256k1_ecmult(&r2, &ptgj, &sc[0], &sc[1]); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1)); + CHECK(secp256k1_gej_eq_var(&r, &r2)); + } + + /* Check infinite outputs of various forms */ + for (ncount = 0; ncount < COUNT; ncount++) { + secp256k1_ge ptg; + size_t i, j; + size_t sizes[] = { 2, 10, 32 }; + + for (j = 0; j < 3; j++) { + for (i = 0; i < 32; i++) { + testutil_random_scalar_order(&sc[i]); + secp256k1_ge_set_infinity(&pt[i]); + } + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + for (j = 0; j < 3; j++) { + for (i = 0; i < 32; i++) { + testutil_random_ge_test(&ptg); + pt[i] = ptg; + secp256k1_scalar_set_int(&sc[i], 0); + } + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + for (j = 0; j < 3; j++) { + testutil_random_ge_test(&ptg); + for (i = 0; i < 16; i++) { + testutil_random_scalar_order(&sc[2*i]); + secp256k1_scalar_negate(&sc[2*i + 1], &sc[2*i]); + pt[2 * i] = ptg; + pt[2 * i + 1] = ptg; + } + + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); + CHECK(secp256k1_gej_is_infinity(&r)); + + testutil_random_scalar_order(&sc[0]); + for (i = 0; i < 16; i++) { + testutil_random_ge_test(&ptg); + + sc[2*i] = sc[0]; + sc[2*i+1] = sc[0]; + pt[2 * i] = ptg; + secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]); + } + + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, sizes[j])); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + testutil_random_ge_test(&ptg); + secp256k1_scalar_set_int(&sc[0], 0); + pt[0] = ptg; + for (i = 1; i < 32; i++) { + pt[i] = ptg; + + testutil_random_scalar_order(&sc[i]); + secp256k1_scalar_add(&sc[0], &sc[0], &sc[i]); + secp256k1_scalar_negate(&sc[i], &sc[i]); + } + + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 32)); + CHECK(secp256k1_gej_is_infinity(&r)); + } + + /* Check random points, constant scalar */ + for (ncount = 0; ncount < COUNT; ncount++) { + size_t i; + secp256k1_gej_set_infinity(&r); + + testutil_random_scalar_order(&sc[0]); + for (i = 0; i < 20; i++) { + secp256k1_ge ptg; + sc[i] = sc[0]; + testutil_random_ge_test(&ptg); + pt[i] = ptg; + secp256k1_gej_add_ge_var(&r, &r, &pt[i], NULL); + } + + secp256k1_ecmult(&r2, &r, &sc[0], &secp256k1_scalar_zero); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 20)); + CHECK(secp256k1_gej_eq_var(&r, &r2)); + } + + /* Check random scalars, constant point */ + for (ncount = 0; ncount < COUNT; ncount++) { + size_t i; + secp256k1_ge ptg; + secp256k1_gej p0j; + secp256k1_scalar rs; + secp256k1_scalar_set_int(&rs, 0); + + testutil_random_ge_test(&ptg); + for (i = 0; i < 20; i++) { + testutil_random_scalar_order(&sc[i]); + pt[i] = ptg; + secp256k1_scalar_add(&rs, &rs, &sc[i]); + } + + secp256k1_gej_set_ge(&p0j, &pt[0]); + secp256k1_ecmult(&r2, &p0j, &rs, &secp256k1_scalar_zero); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 20)); + CHECK(secp256k1_gej_eq_var(&r, &r2)); + } + + /* Sanity check that zero scalars don't cause problems */ + for (ncount = 0; ncount < 20; ncount++) { + testutil_random_scalar_order(&sc[ncount]); + testutil_random_ge_test(&pt[ncount]); + } + + secp256k1_scalar_clear(&sc[0]); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 20)); + secp256k1_scalar_clear(&sc[1]); + secp256k1_scalar_clear(&sc[2]); + secp256k1_scalar_clear(&sc[3]); + secp256k1_scalar_clear(&sc[4]); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 6)); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 5)); + CHECK(secp256k1_gej_is_infinity(&r)); + + /* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */ + { + const size_t TOP = 8; + size_t s0i, s1i; + size_t t0i, t1i; + secp256k1_ge ptg; + secp256k1_gej ptgj; + + testutil_random_ge_test(&ptg); + secp256k1_gej_set_ge(&ptgj, &ptg); + + for(t0i = 0; t0i < TOP; t0i++) { + for(t1i = 0; t1i < TOP; t1i++) { + secp256k1_gej t0p, t1p; + secp256k1_scalar t0, t1; + + secp256k1_scalar_set_int(&t0, (t0i + 1) / 2); + secp256k1_scalar_cond_negate(&t0, t0i & 1); + secp256k1_scalar_set_int(&t1, (t1i + 1) / 2); + secp256k1_scalar_cond_negate(&t1, t1i & 1); + + secp256k1_ecmult(&t0p, &ptgj, &t0, &secp256k1_scalar_zero); + secp256k1_ecmult(&t1p, &ptgj, &t1, &secp256k1_scalar_zero); + + for(s0i = 0; s0i < TOP; s0i++) { + for(s1i = 0; s1i < TOP; s1i++) { + secp256k1_scalar tmp1, tmp2; + secp256k1_gej expected, actual; + + secp256k1_ge_set_gej(&pt[0], &t0p); + secp256k1_ge_set_gej(&pt[1], &t1p); + + secp256k1_scalar_set_int(&sc[0], (s0i + 1) / 2); + secp256k1_scalar_cond_negate(&sc[0], s0i & 1); + secp256k1_scalar_set_int(&sc[1], (s1i + 1) / 2); + secp256k1_scalar_cond_negate(&sc[1], s1i & 1); + + secp256k1_scalar_mul(&tmp1, &t0, &sc[0]); + secp256k1_scalar_mul(&tmp2, &t1, &sc[1]); + secp256k1_scalar_add(&tmp1, &tmp1, &tmp2); + + secp256k1_ecmult(&expected, &ptgj, &tmp1, &secp256k1_scalar_zero); + CHECK(ecmult_multi(&CTX->error_callback, scratch, &actual, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 2)); + CHECK(secp256k1_gej_eq_var(&actual, &expected)); + } + } + } + } + } +} + +static int test_ecmult_multi_random(secp256k1_scratch *scratch) { + /* Large random test for ecmult_multi_* functions which exercises: + * - Few or many inputs (0 up to 128, roughly exponentially distributed). + * - Few or many 0*P or a*INF inputs (roughly uniformly distributed). + * - Including or excluding an nonzero a*G term (or such a term at all). + * - Final expected result equal to infinity or not (roughly 50%). + * - ecmult_multi_var, ecmult_strauss_single_batch, ecmult_pippenger_single_batch + */ + + /* These 4 variables define the eventual input to the ecmult_multi function. + * g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and + * scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points + * which form its normal inputs. */ + int filled = 0; + secp256k1_scalar g_scalar = secp256k1_scalar_zero; + secp256k1_scalar scalars[128]; + secp256k1_gej gejs[128]; + /* The expected result, and the computed result. */ + secp256k1_gej expected, computed; + /* Temporaries. */ + secp256k1_scalar sc_tmp; + secp256k1_ge ge_tmp; + /* Variables needed for the actual input to ecmult_multi. */ + secp256k1_ge ges[128]; + ecmult_multi_data data; + + int i; + /* Which multiplication function to use */ + int fn = testrand_int(3); + secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var : + fn == 1 ? secp256k1_ecmult_strauss_batch_single : + secp256k1_ecmult_pippenger_batch_single; + /* Simulate exponentially distributed num. */ + int num_bits = 2 + testrand_int(6); + /* Number of (scalar, point) inputs (excluding g). */ + int num = testrand_int((1 << num_bits) + 1); + /* Number of those which are nonzero. */ + int num_nonzero = testrand_int(num + 1); + /* Whether we're aiming to create an input with nonzero expected result. */ + int nonzero_result = testrand_bits(1); + /* Whether we will provide nonzero g multiplicand. In some cases our hand + * is forced here based on num_nonzero and nonzero_result. */ + int g_nonzero = num_nonzero == 0 ? nonzero_result : + num_nonzero == 1 && !nonzero_result ? 1 : + (int)testrand_bits(1); + /* Which g_scalar pointer to pass into ecmult_multi(). */ + const secp256k1_scalar* g_scalar_ptr = (g_nonzero || testrand_bits(1)) ? &g_scalar : NULL; + /* How many EC multiplications were performed in this function. */ + int mults = 0; + /* How many randomization steps to apply to the input list. */ + int rands = (int)testrand_bits(3); + if (rands > num_nonzero) rands = num_nonzero; + + secp256k1_gej_set_infinity(&expected); + secp256k1_gej_set_infinity(&gejs[0]); + secp256k1_scalar_set_int(&scalars[0], 0); + + if (g_nonzero) { + /* If g_nonzero, set g_scalar to nonzero value r. */ + testutil_random_scalar_order_test(&g_scalar); + if (!nonzero_result) { + /* If expected=0 is desired, add a (a*r, -(1/a)*g) term to compensate. */ + CHECK(num_nonzero > filled); + testutil_random_scalar_order_test(&sc_tmp); + secp256k1_scalar_mul(&scalars[filled], &sc_tmp, &g_scalar); + secp256k1_scalar_inverse_var(&sc_tmp, &sc_tmp); + secp256k1_scalar_negate(&sc_tmp, &sc_tmp); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &gejs[filled], &sc_tmp); + ++filled; + ++mults; + } + } + + if (nonzero_result && filled < num_nonzero) { + /* If a nonzero result is desired, and there is space, add a random nonzero term. */ + testutil_random_scalar_order_test(&scalars[filled]); + testutil_random_ge_test(&ge_tmp); + secp256k1_gej_set_ge(&gejs[filled], &ge_tmp); + ++filled; + } + + if (nonzero_result) { + /* Compute the expected result using normal ecmult. */ + CHECK(filled <= 1); + secp256k1_ecmult(&expected, &gejs[0], &scalars[0], &g_scalar); + mults += filled + g_nonzero; + } + + /* At this point we have expected = scalar_g*G + sum(scalars[i]*gejs[i] for i=0..filled-1). */ + CHECK(filled <= 1 + !nonzero_result); + CHECK(filled <= num_nonzero); + + /* Add entries to scalars,gejs so that there are num of them. All the added entries + * either have scalar=0 or point=infinity, so these do not change the expected result. */ + while (filled < num) { + if (testrand_bits(1)) { + secp256k1_gej_set_infinity(&gejs[filled]); + testutil_random_scalar_order_test(&scalars[filled]); + } else { + secp256k1_scalar_set_int(&scalars[filled], 0); + testutil_random_ge_test(&ge_tmp); + secp256k1_gej_set_ge(&gejs[filled], &ge_tmp); + } + ++filled; + } + + /* Now perform cheapish transformations on gejs and scalars, for indices + * 0..num_nonzero-1, which do not change the expected result, but may + * convert some of them to be both non-0-scalar and non-infinity-point. */ + for (i = 0; i < rands; ++i) { + int j; + secp256k1_scalar v, iv; + /* Shuffle the entries. */ + for (j = 0; j < num_nonzero; ++j) { + int k = testrand_int(num_nonzero - j); + if (k != 0) { + secp256k1_gej gej = gejs[j]; + secp256k1_scalar sc = scalars[j]; + gejs[j] = gejs[j + k]; + scalars[j] = scalars[j + k]; + gejs[j + k] = gej; + scalars[j + k] = sc; + } + } + /* Perturb all consecutive pairs of inputs: + * a*P + b*Q -> (a+b)*P + b*(Q-P). */ + for (j = 0; j + 1 < num_nonzero; j += 2) { + secp256k1_gej gej; + secp256k1_scalar_add(&scalars[j], &scalars[j], &scalars[j+1]); + secp256k1_gej_neg(&gej, &gejs[j]); + secp256k1_gej_add_var(&gejs[j+1], &gejs[j+1], &gej, NULL); + } + /* Transform the last input: a*P -> (v*a) * ((1/v)*P). */ + CHECK(num_nonzero >= 1); + testutil_random_scalar_order_test(&v); + secp256k1_scalar_inverse(&iv, &v); + secp256k1_scalar_mul(&scalars[num_nonzero - 1], &scalars[num_nonzero - 1], &v); + secp256k1_ecmult(&gejs[num_nonzero - 1], &gejs[num_nonzero - 1], &iv, NULL); + ++mults; + } + + /* Shuffle all entries (0..num-1). */ + for (i = 0; i < num; ++i) { + int j = testrand_int(num - i); + if (j != 0) { + secp256k1_gej gej = gejs[i]; + secp256k1_scalar sc = scalars[i]; + gejs[i] = gejs[i + j]; + scalars[i] = scalars[i + j]; + gejs[i + j] = gej; + scalars[i + j] = sc; + } + } + + /* Compute affine versions of all inputs. */ + secp256k1_ge_set_all_gej_var(ges, gejs, filled); + /* Invoke ecmult_multi code. */ + data.sc = scalars; + data.pt = ges; + CHECK(ecmult_multi(&CTX->error_callback, scratch, &computed, g_scalar_ptr, ecmult_multi_callback, &data, filled)); + mults += num_nonzero + g_nonzero; + /* Compare with expected result. */ + CHECK(secp256k1_gej_eq_var(&computed, &expected)); + return mults; +} + +static void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) { + secp256k1_scalar sc; + secp256k1_ge pt; + secp256k1_gej r; + ecmult_multi_data data; + secp256k1_scratch *scratch_empty; + + testutil_random_ge_test(&pt); + testutil_random_scalar_order(&sc); + data.sc = ≻ + data.pt = &pt; + + /* Try to multiply 1 point, but scratch space is empty.*/ + scratch_empty = secp256k1_scratch_create(&CTX->error_callback, 0); + CHECK(!ecmult_multi(&CTX->error_callback, scratch_empty, &r, &secp256k1_scalar_zero, ecmult_multi_callback, &data, 1)); + secp256k1_scratch_destroy(&CTX->error_callback, scratch_empty); +} + +static void test_secp256k1_pippenger_bucket_window_inv(void) { + int i; + + CHECK(secp256k1_pippenger_bucket_window_inv(0) == 0); + for(i = 1; i <= PIPPENGER_MAX_BUCKET_WINDOW; i++) { + /* Bucket_window of 8 is not used with endo */ + if (i == 8) { + continue; + } + CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)) == i); + if (i != PIPPENGER_MAX_BUCKET_WINDOW) { + CHECK(secp256k1_pippenger_bucket_window(secp256k1_pippenger_bucket_window_inv(i)+1) > i); + } + } +} + +/** + * Probabilistically test the function returning the maximum number of possible points + * for a given scratch space. + */ +static void test_ecmult_multi_pippenger_max_points(void) { + size_t scratch_size = testrand_bits(8); + size_t max_size = secp256k1_pippenger_scratch_size(secp256k1_pippenger_bucket_window_inv(PIPPENGER_MAX_BUCKET_WINDOW-1)+512, 12); + secp256k1_scratch *scratch; + size_t n_points_supported; + int bucket_window = 0; + + for(; scratch_size < max_size; scratch_size+=256) { + size_t i; + size_t total_alloc; + size_t checkpoint; + scratch = secp256k1_scratch_create(&CTX->error_callback, scratch_size); + CHECK(scratch != NULL); + checkpoint = secp256k1_scratch_checkpoint(&CTX->error_callback, scratch); + n_points_supported = secp256k1_pippenger_max_points(&CTX->error_callback, scratch); + if (n_points_supported == 0) { + secp256k1_scratch_destroy(&CTX->error_callback, scratch); + continue; + } + bucket_window = secp256k1_pippenger_bucket_window(n_points_supported); + /* allocate `total_alloc` bytes over `PIPPENGER_SCRATCH_OBJECTS` many allocations */ + total_alloc = secp256k1_pippenger_scratch_size(n_points_supported, bucket_window); + for (i = 0; i < PIPPENGER_SCRATCH_OBJECTS - 1; i++) { + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, 1)); + total_alloc--; + } + CHECK(secp256k1_scratch_alloc(&CTX->error_callback, scratch, total_alloc)); + secp256k1_scratch_apply_checkpoint(&CTX->error_callback, scratch, checkpoint); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); + } + CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW); +} + +static void test_ecmult_multi_batch_size_helper(void) { + size_t n_batches, n_batch_points, max_n_batch_points, n; + + max_n_batch_points = 0; + n = 1; + CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 0); + + max_n_batch_points = 1; + n = 0; + CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1); + CHECK(n_batches == 0); + CHECK(n_batch_points == 0); + + max_n_batch_points = 2; + n = 5; + CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1); + CHECK(n_batches == 3); + CHECK(n_batch_points == 2); + + max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH; + n = ECMULT_MAX_POINTS_PER_BATCH; + CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1); + CHECK(n_batches == 1); + CHECK(n_batch_points == ECMULT_MAX_POINTS_PER_BATCH); + + max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH + 1; + n = ECMULT_MAX_POINTS_PER_BATCH + 1; + CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1); + CHECK(n_batches == 2); + CHECK(n_batch_points == ECMULT_MAX_POINTS_PER_BATCH/2 + 1); + + max_n_batch_points = 1; + n = SIZE_MAX; + CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1); + CHECK(n_batches == SIZE_MAX); + CHECK(n_batch_points == 1); + + max_n_batch_points = 2; + n = SIZE_MAX; + CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1); + CHECK(n_batches == SIZE_MAX/2 + 1); + CHECK(n_batch_points == 2); +} + +/** + * Run secp256k1_ecmult_multi_var with num points and a scratch space restricted to + * 1 <= i <= num points. + */ +static void test_ecmult_multi_batching(void) { + static const int n_points = 2*ECMULT_PIPPENGER_THRESHOLD; + secp256k1_scalar scG; + secp256k1_scalar *sc = (secp256k1_scalar *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_scalar) * n_points); + secp256k1_ge *pt = (secp256k1_ge *)checked_malloc(&CTX->error_callback, sizeof(secp256k1_ge) * n_points); + secp256k1_gej r; + secp256k1_gej r2; + ecmult_multi_data data; + int i; + secp256k1_scratch *scratch; + + secp256k1_gej_set_infinity(&r2); + + /* Get random scalars and group elements and compute result */ + testutil_random_scalar_order(&scG); + secp256k1_ecmult(&r2, &r2, &secp256k1_scalar_zero, &scG); + for(i = 0; i < n_points; i++) { + secp256k1_ge ptg; + secp256k1_gej ptgj; + testutil_random_ge_test(&ptg); + secp256k1_gej_set_ge(&ptgj, &ptg); + pt[i] = ptg; + testutil_random_scalar_order(&sc[i]); + secp256k1_ecmult(&ptgj, &ptgj, &sc[i], NULL); + secp256k1_gej_add_var(&r2, &r2, &ptgj, NULL); + } + data.sc = sc; + data.pt = pt; + secp256k1_gej_neg(&r2, &r2); + + /* Test with empty scratch space. It should compute the correct result using + * ecmult_mult_simple algorithm which doesn't require a scratch space. */ + scratch = secp256k1_scratch_create(&CTX->error_callback, 0); + CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); + + /* Test with space for 1 point in pippenger. That's not enough because + * ecmult_multi selects strauss which requires more memory. It should + * therefore select the simple algorithm. */ + scratch = secp256k1_scratch_create(&CTX->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); + CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); + + for(i = 1; i <= n_points; i++) { + if (i > ECMULT_PIPPENGER_THRESHOLD) { + int bucket_window = secp256k1_pippenger_bucket_window(i); + size_t scratch_size = secp256k1_pippenger_scratch_size(i, bucket_window); + scratch = secp256k1_scratch_create(&CTX->error_callback, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); + } else { + size_t scratch_size = secp256k1_strauss_scratch_size(i); + scratch = secp256k1_scratch_create(&CTX->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); + } + CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); + secp256k1_gej_add_var(&r, &r, &r2, NULL); + CHECK(secp256k1_gej_is_infinity(&r)); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); + } + free(sc); + free(pt); +} + +static void run_ecmult_multi_tests(void) { + secp256k1_scratch *scratch; + int64_t todo = (int64_t)320 * COUNT; + + test_secp256k1_pippenger_bucket_window_inv(); + test_ecmult_multi_pippenger_max_points(); + scratch = secp256k1_scratch_create(&CTX->error_callback, 819200); + test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); + test_ecmult_multi(NULL, secp256k1_ecmult_multi_var); + test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single); + test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single); + test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single); + test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single); + while (todo > 0) { + todo -= test_ecmult_multi_random(scratch); + } + secp256k1_scratch_destroy(&CTX->error_callback, scratch); + + /* Run test_ecmult_multi with space for exactly one point */ + scratch = secp256k1_scratch_create(&CTX->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); + test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); + secp256k1_scratch_destroy(&CTX->error_callback, scratch); + + test_ecmult_multi_batch_size_helper(); + test_ecmult_multi_batching(); } -void test_wnaf(const secp256k1_scalar *number, int w) { +static void test_wnaf(const secp256k1_scalar *number, int w) { secp256k1_scalar x, two, t; int wnaf[256]; int zeroes = -1; @@ -2521,76 +5240,117 @@ void test_wnaf(const secp256k1_scalar *number, int w) { CHECK(secp256k1_scalar_eq(&x, number)); /* check that wnaf represents number */ } -void test_constant_wnaf_negate(const secp256k1_scalar *number) { - secp256k1_scalar neg1 = *number; - secp256k1_scalar neg2 = *number; - int sign1 = 1; - int sign2 = 1; +static void test_fixed_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, shift; + int wnaf[256] = {0}; + int i; + int skew; + secp256k1_scalar num, unused; + + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&shift, 1 << w); + /* Make num a 128-bit scalar. */ + secp256k1_scalar_split_128(&num, &unused, number); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + + for (i = WNAF_SIZE(w)-1; i >= 0; --i) { + secp256k1_scalar t; + int v = wnaf[i]; + CHECK(v == 0 || v & 1); /* check parity */ + CHECK(v > -(1 << w)); /* check range above */ + CHECK(v < (1 << w)); /* check range below */ + + secp256k1_scalar_mul(&x, &x, &shift); + if (v >= 0) { + secp256k1_scalar_set_int(&t, v); + } else { + secp256k1_scalar_set_int(&t, -v); + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_add(&x, &x, &t); + } + /* If skew is 1 then add 1 to num */ + secp256k1_scalar_cadd_bit(&num, 0, skew == 1); + CHECK(secp256k1_scalar_eq(&x, &num)); +} - if (!secp256k1_scalar_get_bits(&neg1, 0, 1)) { - secp256k1_scalar_negate(&neg1, &neg1); - sign1 = -1; +/* Checks that the first 8 elements of wnaf are equal to wnaf_expected and the + * rest is 0.*/ +static void test_fixed_wnaf_small_helper(int *wnaf, int *wnaf_expected, int w) { + int i; + for (i = WNAF_SIZE(w)-1; i >= 8; --i) { + CHECK(wnaf[i] == 0); + } + for (i = 7; i >= 0; --i) { + CHECK(wnaf[i] == wnaf_expected[i]); } - sign2 = secp256k1_scalar_cond_negate(&neg2, secp256k1_scalar_is_even(&neg2)); - CHECK(sign1 == sign2); - CHECK(secp256k1_scalar_eq(&neg1, &neg2)); } -void test_constant_wnaf(const secp256k1_scalar *number, int w) { - secp256k1_scalar x, shift; +static void test_fixed_wnaf_small(void) { + int w = 4; int wnaf[256] = {0}; int i; int skew; - secp256k1_scalar num = *number; + secp256k1_scalar num; - secp256k1_scalar_set_int(&x, 0); - secp256k1_scalar_set_int(&shift, 1 << w); - /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */ -#ifdef USE_ENDOMORPHISM - for (i = 0; i < 16; ++i) { - secp256k1_scalar_shr_int(&num, 8); + secp256k1_scalar_set_int(&num, 0); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + for (i = WNAF_SIZE(w)-1; i >= 0; --i) { + int v = wnaf[i]; + CHECK(v == 0); } -#endif - skew = secp256k1_wnaf_const(wnaf, num, w); + CHECK(skew == 0); - for (i = WNAF_SIZE(w); i >= 0; --i) { - secp256k1_scalar t; + secp256k1_scalar_set_int(&num, 1); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + for (i = WNAF_SIZE(w)-1; i >= 1; --i) { int v = wnaf[i]; - CHECK(v != 0); /* check nonzero */ - CHECK(v & 1); /* check parity */ - CHECK(v > -(1 << w)); /* check range above */ - CHECK(v < (1 << w)); /* check range below */ + CHECK(v == 0); + } + CHECK(wnaf[0] == 1); + CHECK(skew == 0); - secp256k1_scalar_mul(&x, &x, &shift); - if (v >= 0) { - secp256k1_scalar_set_int(&t, v); - } else { - secp256k1_scalar_set_int(&t, -v); - secp256k1_scalar_negate(&t, &t); - } - secp256k1_scalar_add(&x, &x, &t); + { + int wnaf_expected[8] = { 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf }; + secp256k1_scalar_set_int(&num, 0xffffffff); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w); + CHECK(skew == 0); + } + { + int wnaf_expected[8] = { -1, -1, -1, -1, -1, -1, -1, 0xf }; + secp256k1_scalar_set_int(&num, 0xeeeeeeee); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w); + CHECK(skew == 1); + } + { + int wnaf_expected[8] = { 1, 0, 1, 0, 1, 0, 1, 0 }; + secp256k1_scalar_set_int(&num, 0x01010101); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w); + CHECK(skew == 0); + } + { + int wnaf_expected[8] = { -0xf, 0, 0xf, -0xf, 0, 0xf, 1, 0 }; + secp256k1_scalar_set_int(&num, 0x01ef1ef1); + skew = secp256k1_wnaf_fixed(wnaf, &num, w); + test_fixed_wnaf_small_helper(wnaf, wnaf_expected, w); + CHECK(skew == 0); } - /* Skew num because when encoding numbers as odd we use an offset */ - secp256k1_scalar_cadd_bit(&num, skew == 2, 1); - CHECK(secp256k1_scalar_eq(&x, &num)); } -void run_wnaf(void) { +static void run_wnaf(void) { int i; - secp256k1_scalar n = {{0}}; - - /* Sanity check: 1 and 2 are the smallest odd and even numbers and should - * have easier-to-diagnose failure modes */ - n.d[0] = 1; - test_constant_wnaf(&n, 4); - n.d[0] = 2; - test_constant_wnaf(&n, 4); + secp256k1_scalar n; + + /* Test 0 for fixed wnaf */ + test_fixed_wnaf_small(); /* Random tests */ - for (i = 0; i < count; i++) { - random_scalar_order(&n); + for (i = 0; i < COUNT; i++) { + testutil_random_scalar_order(&n); test_wnaf(&n, 4+(i%10)); - test_constant_wnaf_negate(&n); - test_constant_wnaf(&n, 4 + (i % 10)); + test_fixed_wnaf(&n, 4 + (i % 10)); } secp256k1_scalar_set_int(&n, 0); CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1); @@ -2599,96 +5359,243 @@ void run_wnaf(void) { CHECK(secp256k1_scalar_is_zero(&n)); } -void test_ecmult_constants(void) { - /* Test ecmult_gen() for [0..36) and [order-36..0). */ - secp256k1_scalar x; - secp256k1_gej r; - secp256k1_ge ng; - int i; - int j; - secp256k1_ge_neg(&ng, &secp256k1_ge_const_g); - for (i = 0; i < 36; i++ ) { - secp256k1_scalar_set_int(&x, i); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); - for (j = 0; j < i; j++) { - if (j == i - 1) { - ge_equals_gej(&secp256k1_ge_const_g, &r); - } - secp256k1_gej_add_ge(&r, &r, &ng); - } - CHECK(secp256k1_gej_is_infinity(&r)); +static int test_ecmult_accumulate_cb(secp256k1_scalar* sc, secp256k1_ge* pt, size_t idx, void* data) { + const secp256k1_scalar* indata = (const secp256k1_scalar*)data; + *sc = *indata; + *pt = secp256k1_ge_const_g; + CHECK(idx == 0); + return 1; +} + +static void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar* x, secp256k1_scratch* scratch) { + /* Compute x*G in 6 different ways, serialize it uncompressed, and feed it into acc. */ + secp256k1_gej rj1, rj2, rj3, rj4, rj5, rj6, gj, infj; + secp256k1_ge r; + unsigned char bytes[65]; + size_t size = 65; + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + secp256k1_gej_set_infinity(&infj); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &rj1, x); + secp256k1_ecmult(&rj2, &gj, x, &secp256k1_scalar_zero); + secp256k1_ecmult(&rj3, &infj, &secp256k1_scalar_zero, x); + CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &rj4, x, NULL, NULL, 0)); + CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &rj5, &secp256k1_scalar_zero, test_ecmult_accumulate_cb, (void*)x, 1)); + secp256k1_ecmult_const(&rj6, &secp256k1_ge_const_g, x); + secp256k1_ge_set_gej_var(&r, &rj1); + CHECK(secp256k1_gej_eq_ge_var(&rj2, &r)); + CHECK(secp256k1_gej_eq_ge_var(&rj3, &r)); + CHECK(secp256k1_gej_eq_ge_var(&rj4, &r)); + CHECK(secp256k1_gej_eq_ge_var(&rj5, &r)); + CHECK(secp256k1_gej_eq_ge_var(&rj6, &r)); + if (secp256k1_ge_is_infinity(&r)) { + /* Store infinity as 0x00 */ + const unsigned char zerobyte[1] = {0}; + secp256k1_sha256_write(acc, zerobyte, 1); + } else { + /* Store other points using their uncompressed serialization. */ + secp256k1_eckey_pubkey_serialize(&r, bytes, &size, 0); + CHECK(size == 65); + secp256k1_sha256_write(acc, bytes, size); } - for (i = 1; i <= 36; i++ ) { +} + +static void test_ecmult_constants_2bit(void) { + /* Using test_ecmult_accumulate, test ecmult for: + * - For i in 0..36: + * - Key i + * - Key -i + * - For i in 0..255: + * - For j in 1..255 (only odd values): + * - Key (j*2^i) mod order + */ + secp256k1_scalar x; + secp256k1_sha256 acc; + unsigned char b32[32]; + int i, j; + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(CTX, 65536); + + /* Expected hash of all the computed points; created with an independent + * implementation. */ + static const unsigned char expected32[32] = { + 0xe4, 0x71, 0x1b, 0x4d, 0x14, 0x1e, 0x68, 0x48, + 0xb7, 0xaf, 0x47, 0x2b, 0x4c, 0xd2, 0x04, 0x14, + 0x3a, 0x75, 0x87, 0x60, 0x1a, 0xf9, 0x63, 0x60, + 0xd0, 0xcb, 0x1f, 0xaa, 0x85, 0x9a, 0xb7, 0xb4 + }; + secp256k1_sha256_initialize(&acc); + for (i = 0; i <= 36; ++i) { secp256k1_scalar_set_int(&x, i); + test_ecmult_accumulate(&acc, &x, scratch); secp256k1_scalar_negate(&x, &x); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); - for (j = 0; j < i; j++) { - if (j == i - 1) { - ge_equals_gej(&ng, &r); - } - secp256k1_gej_add_ge(&r, &r, &secp256k1_ge_const_g); + test_ecmult_accumulate(&acc, &x, scratch); + }; + for (i = 0; i < 256; ++i) { + for (j = 1; j < 256; j += 2) { + int k; + secp256k1_scalar_set_int(&x, j); + for (k = 0; k < i; ++k) secp256k1_scalar_add(&x, &x, &x); + test_ecmult_accumulate(&acc, &x, scratch); } - CHECK(secp256k1_gej_is_infinity(&r)); } + secp256k1_sha256_finalize(&acc, b32); + CHECK(secp256k1_memcmp_var(b32, expected32, 32) == 0); + + secp256k1_scratch_space_destroy(CTX, scratch); +} + +static void test_ecmult_constants_sha(uint32_t prefix, size_t iter, const unsigned char* expected32) { + /* Using test_ecmult_accumulate, test ecmult for: + * - Key 0 + * - Key 1 + * - Key -1 + * - For i in range(iter): + * - Key SHA256(LE32(prefix) || LE16(i)) + */ + secp256k1_scalar x; + secp256k1_sha256 acc; + unsigned char b32[32]; + unsigned char inp[6]; + size_t i; + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(CTX, 65536); + + inp[0] = prefix & 0xFF; + inp[1] = (prefix >> 8) & 0xFF; + inp[2] = (prefix >> 16) & 0xFF; + inp[3] = (prefix >> 24) & 0xFF; + secp256k1_sha256_initialize(&acc); + secp256k1_scalar_set_int(&x, 0); + test_ecmult_accumulate(&acc, &x, scratch); + secp256k1_scalar_set_int(&x, 1); + test_ecmult_accumulate(&acc, &x, scratch); + secp256k1_scalar_negate(&x, &x); + test_ecmult_accumulate(&acc, &x, scratch); + + for (i = 0; i < iter; ++i) { + secp256k1_sha256 gen; + inp[4] = i & 0xff; + inp[5] = (i >> 8) & 0xff; + secp256k1_sha256_initialize(&gen); + secp256k1_sha256_write(&gen, inp, sizeof(inp)); + secp256k1_sha256_finalize(&gen, b32); + secp256k1_scalar_set_b32(&x, b32, NULL); + test_ecmult_accumulate(&acc, &x, scratch); + } + secp256k1_sha256_finalize(&acc, b32); + CHECK(secp256k1_memcmp_var(b32, expected32, 32) == 0); + + secp256k1_scratch_space_destroy(CTX, scratch); } -void run_ecmult_constants(void) { - test_ecmult_constants(); +static void run_ecmult_constants(void) { + /* Expected hashes of all points in the tests below. Computed using an + * independent implementation. */ + static const unsigned char expected32_6bit20[32] = { + 0x68, 0xb6, 0xed, 0x6f, 0x28, 0xca, 0xc9, 0x7f, + 0x8e, 0x8b, 0xd6, 0xc0, 0x61, 0x79, 0x34, 0x6e, + 0x5a, 0x8f, 0x2b, 0xbc, 0x3e, 0x1f, 0xc5, 0x2e, + 0x2a, 0xd0, 0x45, 0x67, 0x7f, 0x95, 0x95, 0x8e + }; + static const unsigned char expected32_8bit8[32] = { + 0x8b, 0x65, 0x8e, 0xea, 0x86, 0xae, 0x3c, 0x95, + 0x90, 0xb6, 0x77, 0xa4, 0x8c, 0x76, 0xd9, 0xec, + 0xf5, 0xab, 0x8a, 0x2f, 0xfd, 0xdb, 0x19, 0x12, + 0x1a, 0xee, 0xe6, 0xb7, 0x6e, 0x05, 0x3f, 0xc6 + }; + /* For every combination of 6 bit positions out of 256, restricted to + * 20-bit windows (i.e., the first and last bit position are no more than + * 19 bits apart), all 64 bit patterns occur in the input scalars used in + * this test. */ + CONDITIONAL_TEST(1, "test_ecmult_constants_sha 1024") { + test_ecmult_constants_sha(4808378u, 1024, expected32_6bit20); + } + + /* For every combination of 8 consecutive bit positions, all 256 bit + * patterns occur in the input scalars used in this test. */ + CONDITIONAL_TEST(3, "test_ecmult_constants_sha 2048") { + test_ecmult_constants_sha(1607366309u, 2048, expected32_8bit8); + } + + CONDITIONAL_TEST(35, "test_ecmult_constants_2bit") { + test_ecmult_constants_2bit(); + } } -void test_ecmult_gen_blind(void) { +static void test_ecmult_gen_blind(void) { /* Test ecmult_gen() blinding and confirm that the blinding changes, the affine points match, and the z's don't match. */ secp256k1_scalar key; secp256k1_scalar b; unsigned char seed32[32]; secp256k1_gej pgej; secp256k1_gej pgej2; - secp256k1_gej i; + secp256k1_ge p; secp256k1_ge pge; - random_scalar_order_test(&key); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key); - secp256k1_rand256(seed32); - b = ctx->ecmult_gen_ctx.blind; - i = ctx->ecmult_gen_ctx.initial; - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); - CHECK(!secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej2, &key); + testutil_random_scalar_order_test(&key); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej, &key); + testrand256(seed32); + b = CTX->ecmult_gen_ctx.scalar_offset; + p = CTX->ecmult_gen_ctx.ge_offset; + secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, seed32); + CHECK(!secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.scalar_offset)); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej2, &key); CHECK(!gej_xyz_equals_gej(&pgej, &pgej2)); - CHECK(!gej_xyz_equals_gej(&i, &ctx->ecmult_gen_ctx.initial)); + CHECK(!secp256k1_ge_eq_var(&p, &CTX->ecmult_gen_ctx.ge_offset)); secp256k1_ge_set_gej(&pge, &pgej); - ge_equals_gej(&pge, &pgej2); + CHECK(secp256k1_gej_eq_ge_var(&pgej2, &pge)); } -void test_ecmult_gen_blind_reset(void) { +static void test_ecmult_gen_blind_reset(void) { /* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */ secp256k1_scalar b; - secp256k1_gej initial; - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); - b = ctx->ecmult_gen_ctx.blind; - initial = ctx->ecmult_gen_ctx.initial; - secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); - CHECK(secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); - CHECK(gej_xyz_equals_gej(&initial, &ctx->ecmult_gen_ctx.initial)); + secp256k1_ge p1, p2; + secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0); + b = CTX->ecmult_gen_ctx.scalar_offset; + p1 = CTX->ecmult_gen_ctx.ge_offset; + secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0); + CHECK(secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.scalar_offset)); + p2 = CTX->ecmult_gen_ctx.ge_offset; + CHECK(secp256k1_ge_eq_var(&p1, &p2)); +} + +/* Verify that ecmult_gen for scalars gn for which gn + scalar_offset = {-1,0,1}. */ +static void test_ecmult_gen_edge_cases(void) { + int i; + secp256k1_gej res1, res2, res3; + secp256k1_scalar gn = secp256k1_scalar_one; /* gn = 1 */ + secp256k1_scalar_add(&gn, &gn, &CTX->ecmult_gen_ctx.scalar_offset); /* gn = 1 + scalar_offset */ + secp256k1_scalar_negate(&gn, &gn); /* gn = -1 - scalar_offset */ + + for (i = -1; i < 2; ++i) { + /* Run test with gn = i - scalar_offset (so that the ecmult_gen recoded value represents i). */ + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &res1, &gn); + secp256k1_ecmult(&res2, NULL, &secp256k1_scalar_zero, &gn); + secp256k1_ecmult_const(&res3, &secp256k1_ge_const_g, &gn); + CHECK(secp256k1_gej_eq_var(&res1, &res2)); + CHECK(secp256k1_gej_eq_var(&res1, &res3)); + secp256k1_scalar_add(&gn, &gn, &secp256k1_scalar_one); + } } -void run_ecmult_gen_blind(void) { +static void run_ecmult_gen_blind(void) { int i; test_ecmult_gen_blind_reset(); + test_ecmult_gen_edge_cases(); for (i = 0; i < 10; i++) { test_ecmult_gen_blind(); } } -#ifdef USE_ENDOMORPHISM /***** ENDOMORPHISH TESTS *****/ -void test_scalar_split(void) { - secp256k1_scalar full; - secp256k1_scalar s1, slam; +static void test_scalar_split(const secp256k1_scalar* full) { + secp256k1_scalar s, s1, slam; const unsigned char zero[32] = {0}; unsigned char tmp[32]; - random_scalar_order_test(&full); - secp256k1_scalar_split_lambda(&s1, &slam, &full); + secp256k1_scalar_split_lambda(&s1, &slam, full); + + /* check slam*lambda + s1 == full */ + secp256k1_scalar_mul(&s, &secp256k1_const_lambda, &slam); + secp256k1_scalar_add(&s, &s, &s1); + CHECK(secp256k1_scalar_eq(&s, full)); /* check that both are <= 128 bits in size */ if (secp256k1_scalar_is_high(&s1)) { @@ -2699,29 +5606,44 @@ void test_scalar_split(void) { } secp256k1_scalar_get_b32(tmp, &s1); - CHECK(memcmp(zero, tmp, 16) == 0); + CHECK(secp256k1_memcmp_var(zero, tmp, 16) == 0); secp256k1_scalar_get_b32(tmp, &slam); - CHECK(memcmp(zero, tmp, 16) == 0); + CHECK(secp256k1_memcmp_var(zero, tmp, 16) == 0); } -void run_endomorphism_tests(void) { - test_scalar_split(); + +static void run_endomorphism_tests(void) { + unsigned i; + static secp256k1_scalar s; + test_scalar_split(&secp256k1_scalar_zero); + test_scalar_split(&secp256k1_scalar_one); + secp256k1_scalar_negate(&s,&secp256k1_scalar_one); + test_scalar_split(&s); + test_scalar_split(&secp256k1_const_lambda); + secp256k1_scalar_add(&s, &secp256k1_const_lambda, &secp256k1_scalar_one); + test_scalar_split(&s); + + for (i = 0; i < 100U * COUNT; ++i) { + secp256k1_scalar full; + testutil_random_scalar_order_test(&full); + test_scalar_split(&full); + } + for (i = 0; i < sizeof(scalars_near_split_bounds) / sizeof(scalars_near_split_bounds[0]); ++i) { + test_scalar_split(&scalars_near_split_bounds[i]); + } } -#endif -void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) { +static void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) { unsigned char pubkeyc[65]; secp256k1_pubkey pubkey; secp256k1_ge ge; size_t pubkeyclen; - int32_t ecount; - ecount = 0; - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + for (pubkeyclen = 3; pubkeyclen <= 65; pubkeyclen++) { /* Smaller sizes are tested exhaustively elsewhere. */ int32_t i; memcpy(&pubkeyc[1], input, 64); - VG_UNDEF(&pubkeyc[pubkeyclen], 65 - pubkeyclen); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeyc[pubkeyclen], 65 - pubkeyclen); for (i = 0; i < 256; i++) { /* Try all type bytes. */ int xpass; @@ -2740,51 +5662,45 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali unsigned char pubkeyo[65]; size_t outl; memset(&pubkey, 0, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - ecount = 0; - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, pubkeyclen) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); outl = 65; - VG_UNDEF(pubkeyo, 65); - CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); - VG_CHECK(pubkeyo, outl); + SECP256K1_CHECKMEM_UNDEFINE(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + SECP256K1_CHECKMEM_CHECK(pubkeyo, outl); CHECK(outl == 33); - CHECK(memcmp(&pubkeyo[1], &pubkeyc[1], 32) == 0); + CHECK(secp256k1_memcmp_var(&pubkeyo[1], &pubkeyc[1], 32) == 0); CHECK((pubkeyclen != 33) || (pubkeyo[0] == pubkeyc[0])); if (ypass) { /* This test isn't always done because we decode with alternative signs, so the y won't match. */ CHECK(pubkeyo[0] == ysign); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 1); memset(&pubkey, 0, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); secp256k1_pubkey_save(&pubkey, &ge); - VG_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); outl = 65; - VG_UNDEF(pubkeyo, 65); - CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); - VG_CHECK(pubkeyo, outl); + SECP256K1_CHECKMEM_UNDEFINE(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + SECP256K1_CHECKMEM_CHECK(pubkeyo, outl); CHECK(outl == 65); CHECK(pubkeyo[0] == 4); - CHECK(memcmp(&pubkeyo[1], input, 64) == 0); + CHECK(secp256k1_memcmp_var(&pubkeyo[1], input, 64) == 0); } - CHECK(ecount == 0); } else { /* These cases must fail to parse. */ memset(&pubkey, 0xfe, sizeof(pubkey)); - ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); - CHECK(ecount == 1); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, pubkeyclen) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_pubkey_load(CTX, &ge, &pubkey)); } } } - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); } -void run_ec_pubkey_parse_test(void) { +static void run_ec_pubkey_parse_test(void) { #define SECP256K1_EC_PARSE_TEST_NVALID (12) const unsigned char valid[SECP256K1_EC_PARSE_TEST_NVALID][64] = { { @@ -2964,141 +5880,99 @@ void run_ec_pubkey_parse_test(void) { 0xB8, 0x00 }; unsigned char sout[65]; - unsigned char shortkey[2]; + unsigned char shortkey[2] = { 0 }; secp256k1_ge ge; secp256k1_pubkey pubkey; size_t len; int32_t i; - int32_t ecount; - int32_t ecount2; - ecount = 0; + /* Nothing should be reading this far into pubkeyc. */ - VG_UNDEF(&pubkeyc[65], 1); - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeyc[65], 1); /* Zero length claimed, fail, zeroize, no illegal arg error. */ memset(&pubkey, 0xfe, sizeof(pubkey)); - ecount = 0; - VG_UNDEF(shortkey, 2); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 0) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); - CHECK(ecount == 1); + SECP256K1_CHECKMEM_UNDEFINE(shortkey, 2); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 0) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_pubkey_load(CTX, &ge, &pubkey)); /* Length one claimed, fail, zeroize, no illegal arg error. */ for (i = 0; i < 256 ; i++) { memset(&pubkey, 0xfe, sizeof(pubkey)); - ecount = 0; shortkey[0] = i; - VG_UNDEF(&shortkey[1], 1); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 1) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); - CHECK(ecount == 1); + SECP256K1_CHECKMEM_UNDEFINE(&shortkey[1], 1); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 1) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_pubkey_load(CTX, &ge, &pubkey)); } /* Length two claimed, fail, zeroize, no illegal arg error. */ for (i = 0; i < 65536 ; i++) { memset(&pubkey, 0xfe, sizeof(pubkey)); - ecount = 0; shortkey[0] = i & 255; shortkey[1] = i >> 8; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 2) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); - CHECK(ecount == 1); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 2) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_pubkey_load(CTX, &ge, &pubkey)); } memset(&pubkey, 0xfe, sizeof(pubkey)); - ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); /* 33 bytes claimed on otherwise valid input starting with 0x04, fail, zeroize output, no illegal arg error. */ - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 33) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); - CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 33) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_pubkey_load(CTX, &ge, &pubkey)); /* NULL pubkey, illegal arg error. Pubkey isn't rewritten before this step, since it's NULL into the parser. */ - CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, pubkeyc, 65) == 0); - CHECK(ecount == 2); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_parse(CTX, NULL, pubkeyc, 65)); /* NULL input string. Illegal arg and zeroize output. */ memset(&pubkey, 0xfe, sizeof(pubkey)); - ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, NULL, 65) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 1); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); - CHECK(ecount == 2); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_parse(CTX, &pubkey, NULL, 65)); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_pubkey_load(CTX, &ge, &pubkey)); /* 64 bytes claimed on input starting with 0x04, fail, zeroize output, no illegal arg error. */ memset(&pubkey, 0xfe, sizeof(pubkey)); - ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 64) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); - CHECK(ecount == 1); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 64) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_pubkey_load(CTX, &ge, &pubkey)); /* 66 bytes claimed, fail, zeroize output, no illegal arg error. */ memset(&pubkey, 0xfe, sizeof(pubkey)); - ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 66) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 0); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); - CHECK(ecount == 1); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 66) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_pubkey_load(CTX, &ge, &pubkey)); /* Valid parse. */ memset(&pubkey, 0, sizeof(pubkey)); - ecount = 0; - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(ecount == 0); - VG_UNDEF(&ge, sizeof(ge)); - CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); - VG_CHECK(&ge.x, sizeof(ge.x)); - VG_CHECK(&ge.y, sizeof(ge.y)); - VG_CHECK(&ge.infinity, sizeof(ge.infinity)); - ge_equals_ge(&secp256k1_ge_const_g, &ge); - CHECK(ecount == 0); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 65) == 1); + CHECK(secp256k1_ec_pubkey_parse(secp256k1_context_static, &pubkey, pubkeyc, 65) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + SECP256K1_CHECKMEM_UNDEFINE(&ge, sizeof(ge)); + CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 1); + SECP256K1_CHECKMEM_CHECK(&ge.x, sizeof(ge.x)); + SECP256K1_CHECKMEM_CHECK(&ge.y, sizeof(ge.y)); + SECP256K1_CHECKMEM_CHECK(&ge.infinity, sizeof(ge.infinity)); + CHECK(secp256k1_ge_eq_var(&ge, &secp256k1_ge_const_g)); /* secp256k1_ec_pubkey_serialize illegal args. */ - ecount = 0; len = 65; - CHECK(secp256k1_ec_pubkey_serialize(ctx, NULL, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); - CHECK(ecount == 1); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_serialize(CTX, NULL, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED)); CHECK(len == 0); - CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); - CHECK(ecount == 2); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_serialize(CTX, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED)); len = 65; - VG_UNDEF(sout, 65); - CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED) == 0); - VG_CHECK(sout, 65); - CHECK(ecount == 3); + SECP256K1_CHECKMEM_UNDEFINE(sout, 65); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_serialize(CTX, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED)); + SECP256K1_CHECKMEM_CHECK(sout, 65); CHECK(len == 0); len = 65; - CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, ~0) == 0); - CHECK(ecount == 4); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_serialize(CTX, sout, &len, &pubkey, ~0)); CHECK(len == 0); len = 65; - VG_UNDEF(sout, 65); - CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); - VG_CHECK(sout, 65); - CHECK(ecount == 4); + SECP256K1_CHECKMEM_UNDEFINE(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(CTX, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + SECP256K1_CHECKMEM_CHECK(sout, 65); CHECK(len == 65); /* Multiple illegal args. Should still set arg error only once. */ - ecount = 0; - ecount2 = 11; - CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); - CHECK(ecount == 1); - /* Does the illegal arg callback actually change the behavior? */ - secp256k1_context_set_illegal_callback(ctx, uncounting_illegal_callback_fn, &ecount2); - CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); - CHECK(ecount == 1); - CHECK(ecount2 == 10); - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_parse(CTX, NULL, NULL, 65)); /* Try a bunch of prefabbed points with all possible encodings. */ for (i = 0; i < SECP256K1_EC_PARSE_TEST_NVALID; i++) { ec_pubkey_parse_pointtest(valid[i], 1, 1); @@ -3111,7 +5985,7 @@ void run_ec_pubkey_parse_test(void) { } } -void run_eckey_edge_case_test(void) { +static void run_eckey_edge_case_test(void) { const unsigned char orderc[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, @@ -3127,259 +6001,284 @@ void run_eckey_edge_case_test(void) { secp256k1_pubkey pubkey_negone; const secp256k1_pubkey *pubkeys[3]; size_t len; - int32_t ecount; /* Group order is too large, reject. */ - CHECK(secp256k1_ec_seckey_verify(ctx, orderc) == 0); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, orderc) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, orderc) == 0); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, orderc) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* Maximum value is too large, reject. */ memset(ctmp, 255, 32); - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0); memset(&pubkey, 1, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* Zero is too small, reject. */ memset(ctmp, 0, 32); - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0); memset(&pubkey, 1, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* One must be accepted. */ ctmp[31] = 0x01; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 1); memset(&pubkey, 0, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); pubkey_one = pubkey; /* Group order + 1 is too large, reject. */ memcpy(ctmp, orderc, 32); ctmp[31] = 0x42; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0); memset(&pubkey, 1, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* -1 must be accepted. */ ctmp[31] = 0x40; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 1); memset(&pubkey, 0, sizeof(pubkey)); - VG_UNDEF(&pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); - VG_CHECK(&pubkey, sizeof(pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); pubkey_negone = pubkey; - /* Tweak of zero leaves the value changed. */ + /* Tweak of zero leaves the value unchanged. */ memset(ctmp2, 0, 32); - CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1); - CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp, ctmp2) == 1); + CHECK(secp256k1_memcmp_var(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40); memcpy(&pubkey2, &pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); - CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 1); + CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); /* Multiply tweak of zero zeroizes the output. */ - CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, ctmp2) == 0); - CHECK(memcmp(zeros, ctmp, 32) == 0); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0); - CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + CHECK(secp256k1_ec_seckey_tweak_mul(CTX, ctmp, ctmp2) == 0); + CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, ctmp2) == 0); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); - /* Overflowing key tweak zeroizes. */ + /* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing + seckey, the seckey is zeroized. */ + memcpy(ctmp, orderc, 32); + memset(ctmp2, 0, 32); + ctmp2[31] = 0x01; + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp2) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp, ctmp2) == 0); + CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); + memcpy(ctmp, orderc, 32); + CHECK(secp256k1_ec_seckey_tweak_mul(CTX, ctmp, ctmp2) == 0); + CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); + /* If seckey_tweak_add or seckey_tweak_mul are called with an overflowing + tweak, the seckey is zeroized. */ memcpy(ctmp, orderc, 32); ctmp[31] = 0x40; - CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0); - CHECK(memcmp(zeros, ctmp, 32) == 0); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp, orderc) == 0); + CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); memcpy(ctmp, orderc, 32); ctmp[31] = 0x40; - CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, orderc) == 0); - CHECK(memcmp(zeros, ctmp, 32) == 0); + CHECK(secp256k1_ec_seckey_tweak_mul(CTX, ctmp, orderc) == 0); + CHECK(secp256k1_memcmp_var(zeros, ctmp, 32) == 0); memcpy(ctmp, orderc, 32); ctmp[31] = 0x40; - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0); - CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + /* If pubkey_tweak_add or pubkey_tweak_mul are called with an overflowing + tweak, the pubkey is zeroized. */ + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, orderc) == 0); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0); - CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, orderc) == 0); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); - /* Private key tweaks results in a key of zero. */ + /* If the resulting key in secp256k1_ec_seckey_tweak_add and + * secp256k1_ec_pubkey_tweak_add is 0 the functions fail and in the latter + * case the pubkey is zeroized. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + memset(ctmp2, 0, 32); ctmp2[31] = 1; - CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0); - CHECK(memcmp(zeros, ctmp2, 32) == 0); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp2, ctmp) == 0); + CHECK(secp256k1_memcmp_var(zeros, ctmp2, 32) == 0); ctmp2[31] = 1; - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); - CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 0); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); /* Tweak computation wraps and results in a key of 1. */ ctmp2[31] = 2; - CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 1); - CHECK(memcmp(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1); + CHECK(secp256k1_ec_seckey_tweak_add(CTX, ctmp2, ctmp) == 1); + CHECK(secp256k1_memcmp_var(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1); ctmp2[31] = 2; - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 1); ctmp2[31] = 1; - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, ctmp2) == 1); - CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey2, ctmp2) == 1); + CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); /* Tweak mul * 2 = 1+1. */ - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2) == 1); ctmp2[31] = 2; - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 1); - CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); - /* Test argument errors. */ - ecount = 0; - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); - CHECK(ecount == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey2, ctmp2) == 1); + CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); /* Zeroize pubkey on parse error. */ memset(&pubkey, 0, 32); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); - CHECK(ecount == 1); - CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, ctmp2)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(pubkey)) == 0); memcpy(&pubkey, &pubkey2, sizeof(pubkey)); memset(&pubkey2, 0, 32); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 0); - CHECK(ecount == 2); - CHECK(memcmp(&pubkey2, zeros, sizeof(pubkey2)) == 0); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey2, ctmp2)); + CHECK(secp256k1_memcmp_var(&pubkey2, zeros, sizeof(pubkey2)) == 0); /* Plain argument errors. */ - ecount = 0; - CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); - CHECK(ecount == 0); - CHECK(secp256k1_ec_seckey_verify(ctx, NULL) == 0); - CHECK(ecount == 1); - ecount = 0; + CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ec_seckey_verify(CTX, NULL)); memset(ctmp2, 0, 32); ctmp2[31] = 4; - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, NULL, ctmp2) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, NULL) == 0); - CHECK(ecount == 2); - ecount = 0; + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_tweak_add(CTX, NULL, ctmp2)); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, NULL)); memset(ctmp2, 0, 32); ctmp2[31] = 4; - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, NULL, ctmp2) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, NULL) == 0); - CHECK(ecount == 2); - ecount = 0; + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_tweak_mul(CTX, NULL, ctmp2)); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, NULL)); memset(ctmp2, 0, 32); - CHECK(secp256k1_ec_privkey_tweak_add(ctx, NULL, ctmp2) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, NULL) == 0); - CHECK(ecount == 2); - ecount = 0; + CHECK_ILLEGAL(CTX, secp256k1_ec_seckey_tweak_add(CTX, NULL, ctmp2)); + CHECK_ILLEGAL(CTX, secp256k1_ec_seckey_tweak_add(CTX, ctmp, NULL)); memset(ctmp2, 0, 32); ctmp2[31] = 1; - CHECK(secp256k1_ec_privkey_tweak_mul(ctx, NULL, ctmp2) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, NULL) == 0); - CHECK(ecount == 2); - ecount = 0; - CHECK(secp256k1_ec_pubkey_create(ctx, NULL, ctmp) == 0); - CHECK(ecount == 1); + CHECK_ILLEGAL(CTX, secp256k1_ec_seckey_tweak_mul(CTX, NULL, ctmp2)); + CHECK_ILLEGAL(CTX, secp256k1_ec_seckey_tweak_mul(CTX, ctmp, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_create(CTX, NULL, ctmp)); memset(&pubkey, 1, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); - CHECK(ecount == 2); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_create(CTX, &pubkey, NULL)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* secp256k1_ec_pubkey_combine tests. */ - ecount = 0; pubkeys[0] = &pubkey_one; - VG_UNDEF(&pubkeys[0], sizeof(secp256k1_pubkey *)); - VG_UNDEF(&pubkeys[1], sizeof(secp256k1_pubkey *)); - VG_UNDEF(&pubkeys[2], sizeof(secp256k1_pubkey *)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[0], sizeof(secp256k1_pubkey *)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[1], sizeof(secp256k1_pubkey *)); + SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[2], sizeof(secp256k1_pubkey *)); memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 0) == 0); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ec_pubkey_combine(ctx, NULL, pubkeys, 1) == 0); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); - CHECK(ecount == 2); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 0)); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_combine(CTX, NULL, pubkeys, 1)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, NULL, 1) == 0); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); - CHECK(ecount == 3); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_combine(CTX, &pubkey, NULL, 1)); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); pubkeys[0] = &pubkey_negone; memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 1) == 1); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); - CHECK(ecount == 3); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 1) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); len = 33; - CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); - CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1); - CHECK(memcmp(ctmp, ctmp2, 33) == 0); + CHECK(secp256k1_ec_pubkey_serialize(CTX, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(CTX, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_memcmp_var(ctmp, ctmp2, 33) == 0); /* Result is infinity. */ pubkeys[0] = &pubkey_one; pubkeys[1] = &pubkey_negone; memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 0); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); - CHECK(ecount == 3); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 2) == 0); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); /* Passes through infinity but comes out one. */ pubkeys[2] = &pubkey_one; memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 3) == 1); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); - CHECK(ecount == 3); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 3) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); len = 33; - CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); - CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1); - CHECK(memcmp(ctmp, ctmp2, 33) == 0); + CHECK(secp256k1_ec_pubkey_serialize(CTX, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(CTX, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_memcmp_var(ctmp, ctmp2, 33) == 0); /* Adds to two. */ pubkeys[1] = &pubkey_one; memset(&pubkey, 255, sizeof(secp256k1_pubkey)); - VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 1); - VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); - CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); - CHECK(ecount == 3); - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 2) == 1); + SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); +} + +static void run_eckey_negate_test(void) { + unsigned char seckey[32]; + unsigned char seckey_tmp[32]; + + testutil_random_scalar_order_b32(seckey); + memcpy(seckey_tmp, seckey, 32); + + /* Verify negation changes the key and changes it back */ + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 1); + CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) != 0); + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 1); + CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); + + /* Check that privkey alias gives same result */ + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 1); + CHECK(secp256k1_ec_privkey_negate(CTX, seckey_tmp) == 1); + CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); + + /* Negating all 0s fails */ + memset(seckey, 0, 32); + memset(seckey_tmp, 0, 32); + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 0); + /* Check that seckey is not modified */ + CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); + + /* Negating an overflowing seckey fails and the seckey is zeroed. In this + * test, the seckey has 16 random bytes to ensure that ec_seckey_negate + * doesn't just set seckey to a constant value in case of failure. */ + testutil_random_scalar_order_b32(seckey); + memset(seckey, 0xFF, 16); + memset(seckey_tmp, 0, 32); + CHECK(secp256k1_ec_seckey_negate(CTX, seckey) == 0); + CHECK(secp256k1_memcmp_var(seckey, seckey_tmp, 32) == 0); } -void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { +static void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { secp256k1_scalar nonce; do { - random_scalar_order_test(&nonce); - } while(!secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); + testutil_random_scalar_order_test(&nonce); + } while(!secp256k1_ecdsa_sig_sign(&CTX->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); } -void test_ecdsa_sign_verify(void) { +static void test_ecdsa_sign_verify(void) { secp256k1_gej pubj; secp256k1_ge pub; secp256k1_scalar one; secp256k1_scalar msg, key; secp256k1_scalar sigr, sigs; - int recid; int getrec; - random_scalar_order_test(&msg); - random_scalar_order_test(&key); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key); + int recid; + testutil_random_scalar_order_test(&msg); + testutil_random_scalar_order_test(&key); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pubj, &key); secp256k1_ge_set_gej(&pub, &pubj); - getrec = secp256k1_rand_bits(1); - random_sign(&sigr, &sigs, &key, &msg, getrec?&recid:NULL); + getrec = testrand_bits(1); + /* The specific way in which this conditional is written sidesteps a potential bug in clang. + See the commit messages of the commit that introduced this comment for details. */ if (getrec) { + random_sign(&sigr, &sigs, &key, &msg, &recid); CHECK(recid >= 0 && recid < 4); + } else { + random_sign(&sigr, &sigs, &key, &msg, NULL); } - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + CHECK(secp256k1_ecdsa_sig_verify(&sigr, &sigs, &pub, &msg)); secp256k1_scalar_set_int(&one, 1); secp256k1_scalar_add(&msg, &msg, &one); - CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + CHECK(!secp256k1_ecdsa_sig_verify(&sigr, &sigs, &pub, &msg)); } -void run_ecdsa_sign_verify(void) { +static void run_ecdsa_sign_verify(void) { int i; - for (i = 0; i < 10*count; i++) { + for (i = 0; i < 10*COUNT; i++) { test_ecdsa_sign_verify(); } } @@ -3431,12 +6330,12 @@ static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 5); } -int is_empty_signature(const secp256k1_ecdsa_signature *sig) { +static int is_empty_signature(const secp256k1_ecdsa_signature *sig) { static const unsigned char res[sizeof(secp256k1_ecdsa_signature)] = {0}; - return memcmp(sig, res, sizeof(secp256k1_ecdsa_signature)) == 0; + return secp256k1_memcmp_var(sig, res, sizeof(secp256k1_ecdsa_signature)) == 0; } -void test_ecdsa_end_to_end(void) { +static void test_ecdsa_end_to_end(void) { unsigned char extra[32] = {0x00}; unsigned char privkey[32]; unsigned char message[32]; @@ -3455,140 +6354,154 @@ void test_ecdsa_end_to_end(void) { /* Generate a random key and message. */ { secp256k1_scalar msg, key; - random_scalar_order_test(&msg); - random_scalar_order_test(&key); + testutil_random_scalar_order_test(&msg); + testutil_random_scalar_order_test(&key); secp256k1_scalar_get_b32(privkey, &key); secp256k1_scalar_get_b32(message, &msg); } /* Construct and verify corresponding public key. */ - CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + CHECK(secp256k1_ec_seckey_verify(CTX, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, privkey) == 1); /* Verify exporting and importing public key. */ - CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_rand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED)); + CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyc, &pubkeyclen, &pubkey, testrand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED)); memset(&pubkey, 0, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, pubkeyclen) == 1); /* Verify negation changes the key and changes it back */ memcpy(&pubkey_tmp, &pubkey, sizeof(pubkey)); - CHECK(secp256k1_ec_pubkey_negate(ctx, &pubkey_tmp) == 1); - CHECK(memcmp(&pubkey_tmp, &pubkey, sizeof(pubkey)) != 0); - CHECK(secp256k1_ec_pubkey_negate(ctx, &pubkey_tmp) == 1); - CHECK(memcmp(&pubkey_tmp, &pubkey, sizeof(pubkey)) == 0); + CHECK(secp256k1_ec_pubkey_negate(CTX, &pubkey_tmp) == 1); + CHECK(secp256k1_memcmp_var(&pubkey_tmp, &pubkey, sizeof(pubkey)) != 0); + CHECK(secp256k1_ec_pubkey_negate(CTX, &pubkey_tmp) == 1); + CHECK(secp256k1_memcmp_var(&pubkey_tmp, &pubkey, sizeof(pubkey)) == 0); /* Verify private key import and export. */ - CHECK(ec_privkey_export_der(ctx, seckey, &seckeylen, privkey, secp256k1_rand_bits(1) == 1)); - CHECK(ec_privkey_import_der(ctx, privkey2, seckey, seckeylen) == 1); - CHECK(memcmp(privkey, privkey2, 32) == 0); + CHECK(ec_privkey_export_der(CTX, seckey, &seckeylen, privkey, testrand_bits(1) == 1)); + CHECK(ec_privkey_import_der(CTX, privkey2, seckey, seckeylen) == 1); + CHECK(secp256k1_memcmp_var(privkey, privkey2, 32) == 0); /* Optionally tweak the keys using addition. */ - if (secp256k1_rand_int(3) == 0) { + if (testrand_int(3) == 0) { int ret1; int ret2; + int ret3; unsigned char rnd[32]; + unsigned char privkey_tmp[32]; secp256k1_pubkey pubkey2; - secp256k1_rand256_test(rnd); - ret1 = secp256k1_ec_privkey_tweak_add(ctx, privkey, rnd); - ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd); + testrand256_test(rnd); + memcpy(privkey_tmp, privkey, 32); + ret1 = secp256k1_ec_seckey_tweak_add(CTX, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_add(CTX, &pubkey, rnd); + /* Check that privkey alias gives same result */ + ret3 = secp256k1_ec_privkey_tweak_add(CTX, privkey_tmp, rnd); CHECK(ret1 == ret2); + CHECK(ret2 == ret3); if (ret1 == 0) { return; } - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); - CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + CHECK(secp256k1_memcmp_var(privkey, privkey_tmp, 32) == 0); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey2, privkey) == 1); + CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); } /* Optionally tweak the keys using multiplication. */ - if (secp256k1_rand_int(3) == 0) { + if (testrand_int(3) == 0) { int ret1; int ret2; + int ret3; unsigned char rnd[32]; + unsigned char privkey_tmp[32]; secp256k1_pubkey pubkey2; - secp256k1_rand256_test(rnd); - ret1 = secp256k1_ec_privkey_tweak_mul(ctx, privkey, rnd); - ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd); + testrand256_test(rnd); + memcpy(privkey_tmp, privkey, 32); + ret1 = secp256k1_ec_seckey_tweak_mul(CTX, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_mul(CTX, &pubkey, rnd); + /* Check that privkey alias gives same result */ + ret3 = secp256k1_ec_privkey_tweak_mul(CTX, privkey_tmp, rnd); CHECK(ret1 == ret2); + CHECK(ret2 == ret3); if (ret1 == 0) { return; } - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); - CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + CHECK(secp256k1_memcmp_var(privkey, privkey_tmp, 32) == 0); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey2, privkey) == 1); + CHECK(secp256k1_memcmp_var(&pubkey, &pubkey2, sizeof(pubkey)) == 0); } /* Sign. */ - CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign(ctx, &signature[4], message, privkey, NULL, NULL) == 1); - CHECK(secp256k1_ecdsa_sign(ctx, &signature[1], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[1], message, privkey, NULL, extra) == 1); extra[31] = 1; - CHECK(secp256k1_ecdsa_sign(ctx, &signature[2], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[2], message, privkey, NULL, extra) == 1); extra[31] = 0; extra[0] = 1; - CHECK(secp256k1_ecdsa_sign(ctx, &signature[3], message, privkey, NULL, extra) == 1); - CHECK(memcmp(&signature[0], &signature[4], sizeof(signature[0])) == 0); - CHECK(memcmp(&signature[0], &signature[1], sizeof(signature[0])) != 0); - CHECK(memcmp(&signature[0], &signature[2], sizeof(signature[0])) != 0); - CHECK(memcmp(&signature[0], &signature[3], sizeof(signature[0])) != 0); - CHECK(memcmp(&signature[1], &signature[2], sizeof(signature[0])) != 0); - CHECK(memcmp(&signature[1], &signature[3], sizeof(signature[0])) != 0); - CHECK(memcmp(&signature[2], &signature[3], sizeof(signature[0])) != 0); + CHECK(secp256k1_ecdsa_sign(CTX, &signature[3], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_memcmp_var(&signature[0], &signature[4], sizeof(signature[0])) == 0); + CHECK(secp256k1_memcmp_var(&signature[0], &signature[1], sizeof(signature[0])) != 0); + CHECK(secp256k1_memcmp_var(&signature[0], &signature[2], sizeof(signature[0])) != 0); + CHECK(secp256k1_memcmp_var(&signature[0], &signature[3], sizeof(signature[0])) != 0); + CHECK(secp256k1_memcmp_var(&signature[1], &signature[2], sizeof(signature[0])) != 0); + CHECK(secp256k1_memcmp_var(&signature[1], &signature[3], sizeof(signature[0])) != 0); + CHECK(secp256k1_memcmp_var(&signature[2], &signature[3], sizeof(signature[0])) != 0); /* Verify. */ - CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[1], message, &pubkey) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[2], message, &pubkey) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[3], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[0], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[1], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[2], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[3], message, &pubkey) == 1); /* Test lower-S form, malleate, verify and fail, test again, malleate again */ - CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[0])); - secp256k1_ecdsa_signature_load(ctx, &r, &s, &signature[0]); + CHECK(!secp256k1_ecdsa_signature_normalize(CTX, NULL, &signature[0])); + secp256k1_ecdsa_signature_load(CTX, &r, &s, &signature[0]); secp256k1_scalar_negate(&s, &s); secp256k1_ecdsa_signature_save(&signature[5], &r, &s); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 0); - CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); - CHECK(secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); - CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); - CHECK(!secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[5], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_signature_normalize(CTX, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_signature_normalize(CTX, &signature[5], &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(CTX, NULL, &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(CTX, &signature[5], &signature[5])); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[5], message, &pubkey) == 1); secp256k1_scalar_negate(&s, &s); secp256k1_ecdsa_signature_save(&signature[5], &r, &s); - CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); - CHECK(memcmp(&signature[5], &signature[0], 64) == 0); + CHECK(!secp256k1_ecdsa_signature_normalize(CTX, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[5], message, &pubkey) == 1); + CHECK(secp256k1_memcmp_var(&signature[5], &signature[0], 64) == 0); /* Serialize/parse DER and verify again */ - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, sig, &siglen, &signature[0]) == 1); memset(&signature[0], 0, sizeof(signature[0])); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &signature[0], sig, siglen) == 1); + CHECK(secp256k1_ecdsa_verify(CTX, &signature[0], message, &pubkey) == 1); /* Serialize/destroy/parse DER and verify again. */ siglen = 74; - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); - sig[secp256k1_rand_int(siglen)] += 1 + secp256k1_rand_int(255); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 0 || - secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, sig, &siglen, &signature[0]) == 1); + sig[testrand_int(siglen)] += 1 + testrand_int(255); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &signature[0], sig, siglen) == 0 || + secp256k1_ecdsa_verify(CTX, &signature[0], message, &pubkey) == 0); } -void test_random_pubkeys(void) { +static void test_random_pubkeys(void) { secp256k1_ge elem; secp256k1_ge elem2; unsigned char in[65]; /* Generate some randomly sized pubkeys. */ - size_t len = secp256k1_rand_bits(2) == 0 ? 65 : 33; - if (secp256k1_rand_bits(2) == 0) { - len = secp256k1_rand_bits(6); + size_t len = testrand_bits(2) == 0 ? 65 : 33; + if (testrand_bits(2) == 0) { + len = testrand_bits(6); } if (len == 65) { - in[0] = secp256k1_rand_bits(1) ? 4 : (secp256k1_rand_bits(1) ? 6 : 7); + in[0] = testrand_bits(1) ? 4 : (testrand_bits(1) ? 6 : 7); } else { - in[0] = secp256k1_rand_bits(1) ? 2 : 3; + in[0] = testrand_bits(1) ? 2 : 3; } - if (secp256k1_rand_bits(3) == 0) { - in[0] = secp256k1_rand_bits(8); + if (testrand_bits(3) == 0) { + in[0] = testrand_bits(8); } if (len > 1) { - secp256k1_rand256(&in[1]); + testrand256(&in[1]); } if (len > 33) { - secp256k1_rand256(&in[33]); + testrand256(&in[33]); } if (secp256k1_eckey_pubkey_parse(&elem, in, len)) { unsigned char out[65]; @@ -3599,7 +6512,7 @@ void test_random_pubkeys(void) { /* If the pubkey can be parsed, it should round-trip... */ CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, len == 33)); CHECK(size == len); - CHECK(memcmp(&in[1], &out[1], len-1) == 0); + CHECK(secp256k1_memcmp_var(&in[1], &out[1], len-1) == 0); /* ... except for the type of hybrid inputs. */ if ((in[0] != 6) && (in[0] != 7)) { CHECK(in[0] == out[0]); @@ -3608,9 +6521,9 @@ void test_random_pubkeys(void) { CHECK(secp256k1_eckey_pubkey_serialize(&elem, in, &size, 0)); CHECK(size == 65); CHECK(secp256k1_eckey_pubkey_parse(&elem2, in, size)); - ge_equals_ge(&elem,&elem2); + CHECK(secp256k1_ge_eq_var(&elem2, &elem)); /* Check that the X9.62 hybrid type is checked. */ - in[0] = secp256k1_rand_bits(1) ? 6 : 7; + in[0] = testrand_bits(1) ? 6 : 7; res = secp256k1_eckey_pubkey_parse(&elem2, in, size); if (firstb == 2 || firstb == 3) { if (in[0] == firstb + 4) { @@ -3620,37 +6533,221 @@ void test_random_pubkeys(void) { } } if (res) { - ge_equals_ge(&elem,&elem2); + CHECK(secp256k1_ge_eq_var(&elem, &elem2)); CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, 0)); - CHECK(memcmp(&in[1], &out[1], 64) == 0); + CHECK(secp256k1_memcmp_var(&in[1], &out[1], 64) == 0); + } + } +} + +static void run_pubkey_comparison(void) { + unsigned char pk1_ser[33] = { + 0x02, + 0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11, + 0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23 + }; + const unsigned char pk2_ser[33] = { + 0x02, + 0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d, + 0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c + }; + secp256k1_pubkey pk1; + secp256k1_pubkey pk2; + + CHECK(secp256k1_ec_pubkey_parse(CTX, &pk1, pk1_ser, sizeof(pk1_ser)) == 1); + CHECK(secp256k1_ec_pubkey_parse(CTX, &pk2, pk2_ser, sizeof(pk2_ser)) == 1); + + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_ec_pubkey_cmp(CTX, NULL, &pk2) < 0)); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk1, NULL) > 0)); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk1, &pk2) < 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk2, &pk1) > 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk1, &pk1) == 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk2, &pk2) == 0); + { + secp256k1_pubkey pk_tmp; + memset(&pk_tmp, 0, sizeof(pk_tmp)); /* illegal pubkey */ + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk_tmp, &pk2) < 0)); + { + int32_t ecount = 0; + secp256k1_context_set_illegal_callback(CTX, counting_callback_fn, &ecount); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk_tmp, &pk_tmp) == 0); + CHECK(ecount == 2); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); + } + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk2, &pk_tmp) > 0)); + } + + /* Make pk2 the same as pk1 but with 3 rather than 2. Note that in + * an uncompressed encoding, these would have the opposite ordering */ + pk1_ser[0] = 3; + CHECK(secp256k1_ec_pubkey_parse(CTX, &pk2, pk1_ser, sizeof(pk1_ser)) == 1); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk1, &pk2) < 0); + CHECK(secp256k1_ec_pubkey_cmp(CTX, &pk2, &pk1) > 0); +} + +static void test_sort_helper(secp256k1_pubkey *pk, size_t *pk_order, size_t n_pk) { + size_t i; + const secp256k1_pubkey *pk_test[5]; + + for (i = 0; i < n_pk; i++) { + pk_test[i] = &pk[pk_order[i]]; + } + secp256k1_ec_pubkey_sort(CTX, pk_test, n_pk); + for (i = 0; i < n_pk; i++) { + CHECK(secp256k1_memcmp_var(pk_test[i], &pk[i], sizeof(*pk_test[i])) == 0); + } +} + +static void permute(size_t *arr, size_t n) { + size_t i; + for (i = n - 1; i >= 1; i--) { + size_t tmp, j; + j = testrand_int(i + 1); + tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} + +static void test_sort_api(void) { + secp256k1_pubkey pks[2]; + const secp256k1_pubkey *pks_ptr[2]; + + pks_ptr[0] = &pks[0]; + pks_ptr[1] = &pks[1]; + + testutil_random_pubkey_test(&pks[0]); + testutil_random_pubkey_test(&pks[1]); + + CHECK(secp256k1_ec_pubkey_sort(CTX, pks_ptr, 2) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_sort(CTX, NULL, 2)); + CHECK(secp256k1_ec_pubkey_sort(CTX, pks_ptr, 0) == 1); + /* Test illegal public keys */ + memset(&pks[0], 0, sizeof(pks[0])); + CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_ec_pubkey_sort(CTX, pks_ptr, 2) == 1)); + memset(&pks[1], 0, sizeof(pks[1])); + { + int32_t ecount = 0; + secp256k1_context_set_illegal_callback(CTX, counting_callback_fn, &ecount); + CHECK(secp256k1_ec_pubkey_sort(CTX, pks_ptr, 2) == 1); + CHECK(ecount == 2); + secp256k1_context_set_illegal_callback(CTX, NULL, NULL); + } +} + +static void test_sort(void) { + secp256k1_pubkey pk[5]; + unsigned char pk_ser[5][33] = { + { 0x02, 0x08 }, + { 0x02, 0x0b }, + { 0x02, 0x0c }, + { 0x03, 0x05 }, + { 0x03, 0x0a }, + }; + int i; + size_t pk_order[5] = { 0, 1, 2, 3, 4 }; + + for (i = 0; i < 5; i++) { + CHECK(secp256k1_ec_pubkey_parse(CTX, &pk[i], pk_ser[i], sizeof(pk_ser[i]))); + } + + permute(pk_order, 1); + test_sort_helper(pk, pk_order, 1); + permute(pk_order, 2); + test_sort_helper(pk, pk_order, 2); + permute(pk_order, 3); + test_sort_helper(pk, pk_order, 3); + for (i = 0; i < COUNT; i++) { + permute(pk_order, 4); + test_sort_helper(pk, pk_order, 4); + } + for (i = 0; i < COUNT; i++) { + permute(pk_order, 5); + test_sort_helper(pk, pk_order, 5); + } + /* Check that sorting also works for random pubkeys */ + for (i = 0; i < COUNT; i++) { + int j; + const secp256k1_pubkey *pk_ptr[5]; + for (j = 0; j < 5; j++) { + testutil_random_pubkey_test(&pk[j]); + pk_ptr[j] = &pk[j]; } + secp256k1_ec_pubkey_sort(CTX, pk_ptr, 5); + for (j = 1; j < 5; j++) { + CHECK(secp256k1_ec_pubkey_sort_cmp(&pk_ptr[j - 1], &pk_ptr[j], CTX) <= 0); + } + } +} + +/* Test vectors from BIP-MuSig2 */ +static void test_sort_vectors(void) { + enum { N_PUBKEYS = 6 }; + unsigned char pk_ser[N_PUBKEYS][33] = { + { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, + 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, + 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 }, + { 0x02, 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, + 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45, 0x83, + 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 }, + { 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, + 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, + 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 }, + { 0x02, 0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18, 0x15, 0xC2, + 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3, 0x14, 0x93, 0x16, 0xC3, 0x51, + 0x8C, 0xE7, 0xB7, 0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66 }, + { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, + 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, + 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xFF }, + { 0x02, 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, 0x12, 0x1F, + 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, 0x01, 0x39, 0x71, 0x53, 0x09, + 0xB0, 0x86, 0xC9, 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 } + }; + secp256k1_pubkey pubkeys[N_PUBKEYS]; + secp256k1_pubkey *sorted[N_PUBKEYS]; + const secp256k1_pubkey *pks_ptr[N_PUBKEYS]; + int i; + + sorted[0] = &pubkeys[3]; + sorted[1] = &pubkeys[0]; + sorted[2] = &pubkeys[0]; + sorted[3] = &pubkeys[4]; + sorted[4] = &pubkeys[1]; + sorted[5] = &pubkeys[2]; + + for (i = 0; i < N_PUBKEYS; i++) { + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkeys[i], pk_ser[i], sizeof(pk_ser[i]))); + pks_ptr[i] = &pubkeys[i]; } + CHECK(secp256k1_ec_pubkey_sort(CTX, pks_ptr, N_PUBKEYS) == 1); + for (i = 0; i < N_PUBKEYS; i++) { + CHECK(secp256k1_memcmp_var(pks_ptr[i], sorted[i], sizeof(secp256k1_pubkey)) == 0); + } +} + +static void run_pubkey_sort(void) { + test_sort_api(); + test_sort(); + test_sort_vectors(); } -void run_random_pubkeys(void) { + +static void run_random_pubkeys(void) { int i; - for (i = 0; i < 10*count; i++) { + for (i = 0; i < 10*COUNT; i++) { test_random_pubkeys(); } } -void run_ecdsa_end_to_end(void) { +static void run_ecdsa_end_to_end(void) { int i; - for (i = 0; i < 64*count; i++) { + for (i = 0; i < 64*COUNT; i++) { test_ecdsa_end_to_end(); } } -int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) { +static int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) { static const unsigned char zeroes[32] = {0}; -#ifdef ENABLE_OPENSSL_TESTS - static const unsigned char max_scalar[32] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, - 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40 - }; -#endif int ret = 0; @@ -3666,32 +6763,24 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_ size_t len_der_lax = 2048; int parsed_der_lax = 0, valid_der_lax = 0, roundtrips_der_lax = 0; -#ifdef ENABLE_OPENSSL_TESTS - ECDSA_SIG *sig_openssl; - const unsigned char *sigptr; - unsigned char roundtrip_openssl[2048]; - int len_openssl = 2048; - int parsed_openssl, valid_openssl = 0, roundtrips_openssl = 0; -#endif - - parsed_der = secp256k1_ecdsa_signature_parse_der(ctx, &sig_der, sig, siglen); + parsed_der = secp256k1_ecdsa_signature_parse_der(CTX, &sig_der, sig, siglen); if (parsed_der) { - ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der, &sig_der)) << 0; - valid_der = (memcmp(compact_der, zeroes, 32) != 0) && (memcmp(compact_der + 32, zeroes, 32) != 0); + ret |= (!secp256k1_ecdsa_signature_serialize_compact(CTX, compact_der, &sig_der)) << 0; + valid_der = (secp256k1_memcmp_var(compact_der, zeroes, 32) != 0) && (secp256k1_memcmp_var(compact_der + 32, zeroes, 32) != 0); } if (valid_der) { - ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der, &len_der, &sig_der)) << 1; - roundtrips_der = (len_der == siglen) && memcmp(roundtrip_der, sig, siglen) == 0; + ret |= (!secp256k1_ecdsa_signature_serialize_der(CTX, roundtrip_der, &len_der, &sig_der)) << 1; + roundtrips_der = (len_der == siglen) && secp256k1_memcmp_var(roundtrip_der, sig, siglen) == 0; } - parsed_der_lax = ecdsa_signature_parse_der_lax(ctx, &sig_der_lax, sig, siglen); + parsed_der_lax = ecdsa_signature_parse_der_lax(CTX, &sig_der_lax, sig, siglen); if (parsed_der_lax) { - ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der_lax, &sig_der_lax)) << 10; - valid_der_lax = (memcmp(compact_der_lax, zeroes, 32) != 0) && (memcmp(compact_der_lax + 32, zeroes, 32) != 0); + ret |= (!secp256k1_ecdsa_signature_serialize_compact(CTX, compact_der_lax, &sig_der_lax)) << 10; + valid_der_lax = (secp256k1_memcmp_var(compact_der_lax, zeroes, 32) != 0) && (secp256k1_memcmp_var(compact_der_lax + 32, zeroes, 32) != 0); } if (valid_der_lax) { - ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11; - roundtrips_der_lax = (len_der_lax == siglen) && memcmp(roundtrip_der_lax, sig, siglen) == 0; + ret |= (!secp256k1_ecdsa_signature_serialize_der(CTX, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11; + roundtrips_der_lax = (len_der_lax == siglen) && secp256k1_memcmp_var(roundtrip_der_lax, sig, siglen) == 0; } if (certainly_der) { @@ -3707,49 +6796,13 @@ int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_ if (valid_der) { ret |= (!roundtrips_der_lax) << 12; ret |= (len_der != len_der_lax) << 13; - ret |= (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0) << 14; + ret |= ((len_der != len_der_lax) || (secp256k1_memcmp_var(roundtrip_der_lax, roundtrip_der, len_der) != 0)) << 14; } ret |= (roundtrips_der != roundtrips_der_lax) << 15; if (parsed_der) { ret |= (!parsed_der_lax) << 16; } -#ifdef ENABLE_OPENSSL_TESTS - sig_openssl = ECDSA_SIG_new(); - sigptr = sig; - parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL); - if (parsed_openssl) { - valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256; - if (valid_openssl) { - unsigned char tmp[32] = {0}; - BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r)); - valid_openssl = memcmp(tmp, max_scalar, 32) < 0; - } - if (valid_openssl) { - unsigned char tmp[32] = {0}; - BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s)); - valid_openssl = memcmp(tmp, max_scalar, 32) < 0; - } - } - len_openssl = i2d_ECDSA_SIG(sig_openssl, NULL); - if (len_openssl <= 2048) { - unsigned char *ptr = roundtrip_openssl; - CHECK(i2d_ECDSA_SIG(sig_openssl, &ptr) == len_openssl); - roundtrips_openssl = valid_openssl && ((size_t)len_openssl == siglen) && (memcmp(roundtrip_openssl, sig, siglen) == 0); - } else { - len_openssl = 0; - } - ECDSA_SIG_free(sig_openssl); - - ret |= (parsed_der && !parsed_openssl) << 4; - ret |= (valid_der && !valid_openssl) << 5; - ret |= (roundtrips_openssl && !parsed_der) << 6; - ret |= (roundtrips_der != roundtrips_openssl) << 7; - if (roundtrips_openssl) { - ret |= (len_der != (size_t)len_openssl) << 8; - ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9; - } -#endif return ret; } @@ -3767,27 +6820,27 @@ static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) { static void damage_array(unsigned char *sig, size_t *len) { int pos; - int action = secp256k1_rand_bits(3); + int action = testrand_bits(3); if (action < 1 && *len > 3) { /* Delete a byte. */ - pos = secp256k1_rand_int(*len); + pos = testrand_int(*len); memmove(sig + pos, sig + pos + 1, *len - pos - 1); (*len)--; return; } else if (action < 2 && *len < 2048) { /* Insert a byte. */ - pos = secp256k1_rand_int(1 + *len); + pos = testrand_int(1 + *len); memmove(sig + pos + 1, sig + pos, *len - pos); - sig[pos] = secp256k1_rand_bits(8); + sig[pos] = testrand_bits(8); (*len)++; return; } else if (action < 4) { /* Modify a byte. */ - sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255); + sig[testrand_int(*len)] += 1 + testrand_int(255); return; } else { /* action < 8 */ /* Modify a bit. */ - sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3); + sig[testrand_int(*len)] ^= 1 << testrand_bits(3); return; } } @@ -3800,23 +6853,23 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly int n; *len = 0; - der = secp256k1_rand_bits(2) == 0; + der = testrand_bits(2) == 0; *certainly_der = der; *certainly_not_der = 0; - indet = der ? 0 : secp256k1_rand_int(10) == 0; + indet = der ? 0 : testrand_int(10) == 0; for (n = 0; n < 2; n++) { /* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */ - nlow[n] = der ? 1 : (secp256k1_rand_bits(3) != 0); + nlow[n] = der ? 1 : (testrand_bits(3) != 0); /* The length of the number in bytes (the first byte of which will always be nonzero) */ - nlen[n] = nlow[n] ? secp256k1_rand_int(33) : 32 + secp256k1_rand_int(200) * secp256k1_rand_int(8) / 8; + nlen[n] = nlow[n] ? testrand_int(33) : 32 + testrand_int(200) * testrand_bits(3) / 8; CHECK(nlen[n] <= 232); /* The top bit of the number. */ - nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_rand_bits(1)); + nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : testrand_bits(1)); /* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */ - nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_rand_bits(7) : 1 + secp256k1_rand_int(127)); + nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + testrand_bits(7) : 1 + testrand_int(127)); /* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */ - nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_rand_int(3) : secp256k1_rand_int(300 - nlen[n]) * secp256k1_rand_int(8) / 8); + nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? testrand_int(3) : testrand_int(300 - nlen[n]) * testrand_bits(3) / 8); if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) { *certainly_not_der = 1; } @@ -3825,7 +6878,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2); if (!der) { /* nlenlen[n] max 127 bytes */ - int add = secp256k1_rand_int(127 - nlenlen[n]) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256; + int add = testrand_int(127 - nlenlen[n]) * testrand_bits(4) * testrand_bits(4) / 256; nlenlen[n] += add; if (add != 0) { *certainly_not_der = 1; @@ -3839,7 +6892,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly CHECK(tlen <= 856); /* The length of the garbage inside the tuple. */ - elen = (der || indet) ? 0 : secp256k1_rand_int(980 - tlen) * secp256k1_rand_int(8) / 8; + elen = (der || indet) ? 0 : testrand_int(980 - tlen) * testrand_bits(3) / 8; if (elen != 0) { *certainly_not_der = 1; } @@ -3847,7 +6900,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly CHECK(tlen <= 980); /* The length of the garbage after the end of the tuple. */ - glen = der ? 0 : secp256k1_rand_int(990 - tlen) * secp256k1_rand_int(8) / 8; + glen = der ? 0 : testrand_int(990 - tlen) * testrand_bits(3) / 8; if (glen != 0) { *certainly_not_der = 1; } @@ -3862,7 +6915,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly } else { int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2); if (!der) { - int add = secp256k1_rand_int(127 - tlenlen) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256; + int add = testrand_int(127 - tlenlen) * testrand_bits(4) * testrand_bits(4) / 256; tlenlen += add; if (add != 0) { *certainly_not_der = 1; @@ -3913,13 +6966,13 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly nlen[n]--; } /* Generate remaining random bytes of number */ - secp256k1_rand_bytes_test(sig + *len, nlen[n]); + testrand_bytes_test(sig + *len, nlen[n]); *len += nlen[n]; nlen[n] = 0; } /* Generate random garbage inside tuple. */ - secp256k1_rand_bytes_test(sig + *len, elen); + testrand_bytes_test(sig + *len, elen); *len += elen; /* Generate end-of-contents bytes. */ @@ -3931,16 +6984,16 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly CHECK(tlen + glen <= 1121); /* Generate random garbage outside tuple. */ - secp256k1_rand_bytes_test(sig + *len, glen); + testrand_bytes_test(sig + *len, glen); *len += glen; tlen += glen; CHECK(tlen <= 1121); CHECK(tlen == *len); } -void run_ecdsa_der_parse(void) { +static void run_ecdsa_der_parse(void) { int i,j; - for (i = 0; i < 200 * count; i++) { + for (i = 0; i < 200 * COUNT; i++) { unsigned char buffer[2048]; size_t buflen = 0; int certainly_der = 0; @@ -3970,7 +7023,7 @@ void run_ecdsa_der_parse(void) { } /* Tests several edge cases. */ -void test_ecdsa_edge_cases(void) { +static void test_ecdsa_edge_cases(void) { int t; secp256k1_ecdsa_signature sig; @@ -3984,10 +7037,10 @@ void test_ecdsa_edge_cases(void) { secp256k1_scalar_negate(&ss, &ss); secp256k1_scalar_inverse(&ss, &ss); secp256k1_scalar_set_int(&sr, 1); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &keyj, &sr); + secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &keyj, &sr); secp256k1_ge_set_gej(&key, &keyj); msg = ss; - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 0); } /* Verify signature with r of zero fails. */ @@ -4006,7 +7059,7 @@ void test_ecdsa_edge_cases(void) { secp256k1_scalar_set_int(&msg, 0); secp256k1_scalar_set_int(&sr, 0); CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey_mods_zero, 33)); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify( &sr, &ss, &key, &msg) == 0); } /* Verify signature with s of zero fails. */ @@ -4025,7 +7078,7 @@ void test_ecdsa_edge_cases(void) { secp256k1_scalar_set_int(&msg, 0); secp256k1_scalar_set_int(&sr, 1); CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 0); } /* Verify signature with message 0 passes. */ @@ -4053,14 +7106,14 @@ void test_ecdsa_edge_cases(void) { secp256k1_scalar_set_int(&sr, 2); CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33)); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key2, &msg) == 1); secp256k1_scalar_negate(&ss, &ss); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key2, &msg) == 1); secp256k1_scalar_set_int(&ss, 1); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key2, &msg) == 0); } /* Verify signature with message 1 passes. */ @@ -4094,15 +7147,15 @@ void test_ecdsa_edge_cases(void) { secp256k1_scalar_set_b32(&sr, csr, NULL); CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33)); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key2, &msg) == 1); secp256k1_scalar_negate(&ss, &ss); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key2, &msg) == 1); secp256k1_scalar_set_int(&ss, 2); secp256k1_scalar_inverse_var(&ss, &ss); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key2, &msg) == 0); } /* Verify signature with message -1 passes. */ @@ -4128,19 +7181,18 @@ void test_ecdsa_edge_cases(void) { secp256k1_scalar_negate(&msg, &msg); secp256k1_scalar_set_b32(&sr, csr, NULL); CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 1); secp256k1_scalar_negate(&ss, &ss); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 1); secp256k1_scalar_set_int(&ss, 3); secp256k1_scalar_inverse_var(&ss, &ss); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 0); } /* Signature where s would be zero. */ { secp256k1_pubkey pubkey; size_t siglen; - int32_t ecount; unsigned char signature[72]; static const unsigned char nonce[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -4166,72 +7218,42 @@ void test_ecdsa_edge_cases(void) { 0xb8, 0x12, 0xe0, 0x0b, 0x81, 0x7a, 0x77, 0x62, 0x65, 0xdf, 0xdd, 0x31, 0xb9, 0x3e, 0x29, 0xa9, }; - ecount = 0; - secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 0); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, precomputed_nonce_function, nonce) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, precomputed_nonce_function, nonce2) == 0); msg[31] = 0xaa; - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 1); - CHECK(ecount == 0); - CHECK(secp256k1_ecdsa_sign(ctx, NULL, msg, key, precomputed_nonce_function, nonce2) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, NULL, key, precomputed_nonce_function, nonce2) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, NULL, precomputed_nonce_function, nonce2) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, key) == 1); - CHECK(secp256k1_ecdsa_verify(ctx, NULL, msg, &pubkey) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, NULL, &pubkey) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, NULL) == 0); - CHECK(ecount == 6); - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 1); - CHECK(ecount == 6); - CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); - CHECK(ecount == 7); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, precomputed_nonce_function, nonce) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_sign(CTX, NULL, msg, key, precomputed_nonce_function, nonce2)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_sign(CTX, &sig, NULL, key, precomputed_nonce_function, nonce2)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_sign(CTX, &sig, msg, NULL, precomputed_nonce_function, nonce2)); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, precomputed_nonce_function, nonce2) == 1); + CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, key) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_verify(CTX, NULL, msg, &pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_verify(CTX, &sig, NULL, &pubkey)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_verify(CTX, &sig, msg, NULL)); + CHECK(secp256k1_ecdsa_verify(CTX, &sig, msg, &pubkey) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ec_pubkey_create(CTX, &pubkey, NULL)); /* That pubkeyload fails via an ARGCHECK is a little odd but makes sense because pubkeys are an opaque data type. */ - CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 0); - CHECK(ecount == 8); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_verify(CTX, &sig, msg, &pubkey)); siglen = 72; - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, NULL, &siglen, &sig) == 0); - CHECK(ecount == 9); - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, NULL, &sig) == 0); - CHECK(ecount == 10); - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, NULL) == 0); - CHECK(ecount == 11); - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 1); - CHECK(ecount == 11); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, NULL, signature, siglen) == 0); - CHECK(ecount == 12); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, NULL, siglen) == 0); - CHECK(ecount == 13); - CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, signature, siglen) == 1); - CHECK(ecount == 13); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_serialize_der(CTX, NULL, &siglen, &sig)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_serialize_der(CTX, signature, NULL, &sig)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_serialize_der(CTX, signature, &siglen, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, signature, &siglen, &sig) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_parse_der(CTX, NULL, signature, siglen)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_parse_der(CTX, &sig, NULL, siglen)); + CHECK(secp256k1_ecdsa_signature_parse_der(CTX, &sig, signature, siglen) == 1); siglen = 10; /* Too little room for a signature does not fail via ARGCHECK. */ - CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 0); - CHECK(ecount == 13); - ecount = 0; - CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, NULL) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, NULL, &sig) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, NULL) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, &sig) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, NULL, signature) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, NULL) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 1); - CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_signature_serialize_der(CTX, signature, &siglen, &sig) == 0); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_normalize(CTX, NULL, NULL)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_serialize_compact(CTX, NULL, &sig)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_serialize_compact(CTX, signature, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_compact(CTX, signature, &sig) == 1); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_parse_compact(CTX, NULL, signature)); + CHECK_ILLEGAL(CTX, secp256k1_ecdsa_signature_parse_compact(CTX, &sig, NULL)); + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &sig, signature) == 1); memset(signature, 255, 64); - CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 0); - CHECK(ecount == 5); - secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + CHECK(secp256k1_ecdsa_signature_parse_compact(CTX, &sig, signature) == 0); } /* Nonce function corner cases. */ @@ -4248,33 +7270,33 @@ void test_ecdsa_edge_cases(void) { msg[31] = 1; /* High key results in signature failure. */ memset(key, 0xFF, 32); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, NULL, extra) == 0); CHECK(is_empty_signature(&sig)); /* Zero key results in signature failure. */ memset(key, 0, 32); - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, NULL, extra) == 0); CHECK(is_empty_signature(&sig)); /* Nonce function failure results in signature failure. */ key[31] = 1; - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_fail, extra) == 0); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, nonce_function_test_fail, extra) == 0); CHECK(is_empty_signature(&sig)); /* The retry loop successfully makes its way to the first good value. */ - CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_retry, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig, msg, key, nonce_function_test_retry, extra) == 1); CHECK(!is_empty_signature(&sig)); - CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, nonce_function_rfc6979, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig2, msg, key, nonce_function_rfc6979, extra) == 1); CHECK(!is_empty_signature(&sig2)); - CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + CHECK(secp256k1_memcmp_var(&sig, &sig2, sizeof(sig)) == 0); /* The default nonce function is deterministic. */ - CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig2, msg, key, NULL, extra) == 1); CHECK(!is_empty_signature(&sig2)); - CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + CHECK(secp256k1_memcmp_var(&sig, &sig2, sizeof(sig)) == 0); /* The default nonce function changes output with different messages. */ for(i = 0; i < 256; i++) { int j; msg[0] = i; - CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig2, msg, key, NULL, extra) == 1); CHECK(!is_empty_signature(&sig2)); - secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + secp256k1_ecdsa_signature_load(CTX, &sr[i], &ss, &sig2); for (j = 0; j < i; j++) { CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); } @@ -4285,9 +7307,9 @@ void test_ecdsa_edge_cases(void) { for(i = 256; i < 512; i++) { int j; key[0] = i - 256; - CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_sign(CTX, &sig2, msg, key, NULL, extra) == 1); CHECK(!is_empty_signature(&sig2)); - secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + secp256k1_ecdsa_signature_load(CTX, &sr[i], &ss, &sig2); for (j = 0; j < i; j++) { CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); } @@ -4302,24 +7324,24 @@ void test_ecdsa_edge_cases(void) { unsigned char nonce2[32]; unsigned char nonce3[32]; unsigned char nonce4[32]; - VG_UNDEF(nonce,32); - VG_UNDEF(nonce2,32); - VG_UNDEF(nonce3,32); - VG_UNDEF(nonce4,32); + SECP256K1_CHECKMEM_UNDEFINE(nonce,32); + SECP256K1_CHECKMEM_UNDEFINE(nonce2,32); + SECP256K1_CHECKMEM_UNDEFINE(nonce3,32); + SECP256K1_CHECKMEM_UNDEFINE(nonce4,32); CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1); - VG_CHECK(nonce,32); + SECP256K1_CHECKMEM_CHECK(nonce,32); CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1); - VG_CHECK(nonce2,32); + SECP256K1_CHECKMEM_CHECK(nonce2,32); CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1); - VG_CHECK(nonce3,32); + SECP256K1_CHECKMEM_CHECK(nonce3,32); CHECK(nonce_function_rfc6979(nonce4, zeros, zeros, zeros, (void *)zeros, 0) == 1); - VG_CHECK(nonce4,32); - CHECK(memcmp(nonce, nonce2, 32) != 0); - CHECK(memcmp(nonce, nonce3, 32) != 0); - CHECK(memcmp(nonce, nonce4, 32) != 0); - CHECK(memcmp(nonce2, nonce3, 32) != 0); - CHECK(memcmp(nonce2, nonce4, 32) != 0); - CHECK(memcmp(nonce3, nonce4, 32) != 0); + SECP256K1_CHECKMEM_CHECK(nonce4,32); + CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0); + CHECK(secp256k1_memcmp_var(nonce, nonce3, 32) != 0); + CHECK(secp256k1_memcmp_var(nonce, nonce4, 32) != 0); + CHECK(secp256k1_memcmp_var(nonce2, nonce3, 32) != 0); + CHECK(secp256k1_memcmp_var(nonce2, nonce4, 32) != 0); + CHECK(secp256k1_memcmp_var(nonce3, nonce4, 32) != 0); } @@ -4333,71 +7355,53 @@ void test_ecdsa_edge_cases(void) { 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, }; size_t outlen = 300; - CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 0)); + CHECK(!ec_privkey_export_der(CTX, privkey, &outlen, seckey, 0)); outlen = 300; - CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 1)); + CHECK(!ec_privkey_export_der(CTX, privkey, &outlen, seckey, 1)); } } -void run_ecdsa_edge_cases(void) { +static void run_ecdsa_edge_cases(void) { test_ecdsa_edge_cases(); } -#ifdef ENABLE_OPENSSL_TESTS -EC_KEY *get_openssl_key(const unsigned char *key32) { - unsigned char privkey[300]; - size_t privkeylen; - const unsigned char* pbegin = privkey; - int compr = secp256k1_rand_bits(1); - EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_secp256k1); - CHECK(ec_privkey_export_der(ctx, privkey, &privkeylen, key32, compr)); - CHECK(d2i_ECPrivateKey(&ec_key, &pbegin, privkeylen)); - CHECK(EC_KEY_check_key(ec_key)); - return ec_key; -} +/** Wycheproof tests -void test_ecdsa_openssl(void) { - secp256k1_gej qj; - secp256k1_ge q; - secp256k1_scalar sigr, sigs; - secp256k1_scalar one; - secp256k1_scalar msg2; - secp256k1_scalar key, msg; - EC_KEY *ec_key; - unsigned int sigsize = 80; - size_t secp_sigsize = 80; - unsigned char message[32]; - unsigned char signature[80]; - unsigned char key32[32]; - secp256k1_rand256_test(message); - secp256k1_scalar_set_b32(&msg, message, NULL); - random_scalar_order_test(&key); - secp256k1_scalar_get_b32(key32, &key); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &qj, &key); - secp256k1_ge_set_gej(&q, &qj); - ec_key = get_openssl_key(key32); - CHECK(ec_key != NULL); - CHECK(ECDSA_sign(0, message, sizeof(message), signature, &sigsize, ec_key)); - CHECK(secp256k1_ecdsa_sig_parse(&sigr, &sigs, signature, sigsize)); - CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg)); - secp256k1_scalar_set_int(&one, 1); - secp256k1_scalar_add(&msg2, &msg, &one); - CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg2)); +The tests check for known attacks (range checks in (r,s), arithmetic errors, malleability). +*/ +static void test_ecdsa_wycheproof(void) { + #include "wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h" - random_sign(&sigr, &sigs, &key, &msg, NULL); - CHECK(secp256k1_ecdsa_sig_serialize(signature, &secp_sigsize, &sigr, &sigs)); - CHECK(ECDSA_verify(0, message, sizeof(message), signature, secp_sigsize, ec_key) == 1); + int t; + for (t = 0; t < SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS; t++) { + secp256k1_ecdsa_signature signature; + secp256k1_sha256 hasher; + secp256k1_pubkey pubkey; + const unsigned char *msg, *sig, *pk; + unsigned char out[32] = {0}; + int actual_verify = 0; - EC_KEY_free(ec_key); -} + memset(&pubkey, 0, sizeof(pubkey)); + pk = &wycheproof_ecdsa_public_keys[testvectors[t].pk_offset]; + CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pk, 65) == 1); -void run_ecdsa_openssl(void) { - int i; - for (i = 0; i < 10*count; i++) { - test_ecdsa_openssl(); + secp256k1_sha256_initialize(&hasher); + msg = &wycheproof_ecdsa_messages[testvectors[t].msg_offset]; + secp256k1_sha256_write(&hasher, msg, testvectors[t].msg_len); + secp256k1_sha256_finalize(&hasher, out); + + sig = &wycheproof_ecdsa_signatures[testvectors[t].sig_offset]; + if (secp256k1_ecdsa_signature_parse_der(CTX, &signature, sig, testvectors[t].sig_len) == 1) { + actual_verify = secp256k1_ecdsa_verify(CTX, (const secp256k1_ecdsa_signature *)&signature, out, &pubkey); + } + CHECK(testvectors[t].expected_verify == actual_verify); } } -#endif + +/* Tests cases from Wycheproof test suite. */ +static void run_ecdsa_wycheproof(void) { + test_ecdsa_wycheproof(); +} #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/tests_impl.h" @@ -4407,97 +7411,327 @@ void run_ecdsa_openssl(void) { # include "modules/recovery/tests_impl.h" #endif +#ifdef ENABLE_MODULE_EXTRAKEYS +# include "modules/extrakeys/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/tests_impl.h" +#endif + +static void run_secp256k1_memczero_test(void) { + unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; + unsigned char buf2[sizeof(buf1)]; + + /* secp256k1_memczero(..., ..., 0) is a noop. */ + memcpy(buf2, buf1, sizeof(buf1)); + secp256k1_memczero(buf1, sizeof(buf1), 0); + CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0); + + /* secp256k1_memczero(..., ..., 1) zeros the buffer. */ + memset(buf2, 0, sizeof(buf2)); + secp256k1_memczero(buf1, sizeof(buf1) , 1); + CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0); +} + +static void run_secp256k1_byteorder_tests(void) { + { + const uint32_t x = 0xFF03AB45; + const unsigned char x_be[4] = {0xFF, 0x03, 0xAB, 0x45}; + unsigned char buf[4]; + uint32_t x_; + + secp256k1_write_be32(buf, x); + CHECK(secp256k1_memcmp_var(buf, x_be, sizeof(buf)) == 0); + + x_ = secp256k1_read_be32(buf); + CHECK(x == x_); + } + + { + const uint64_t x = 0xCAFE0123BEEF4567; + const unsigned char x_be[8] = {0xCA, 0xFE, 0x01, 0x23, 0xBE, 0xEF, 0x45, 0x67}; + unsigned char buf[8]; + uint64_t x_; + + secp256k1_write_be64(buf, x); + CHECK(secp256k1_memcmp_var(buf, x_be, sizeof(buf)) == 0); + + x_ = secp256k1_read_be64(buf); + CHECK(x == x_); + } +} + +static void int_cmov_test(void) { + int r = INT_MAX; + int a = 0; + + secp256k1_int_cmov(&r, &a, 0); + CHECK(r == INT_MAX); + + r = 0; a = INT_MAX; + secp256k1_int_cmov(&r, &a, 1); + CHECK(r == INT_MAX); + + a = 0; + secp256k1_int_cmov(&r, &a, 1); + CHECK(r == 0); + + a = 1; + secp256k1_int_cmov(&r, &a, 1); + CHECK(r == 1); + + r = 1; a = 0; + secp256k1_int_cmov(&r, &a, 0); + CHECK(r == 1); + +} + +static void fe_cmov_test(void) { + static const secp256k1_fe zero = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0); + static const secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + static const secp256k1_fe max = SECP256K1_FE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL + ); + secp256k1_fe r = max; + secp256k1_fe a = zero; + + secp256k1_fe_cmov(&r, &a, 0); + CHECK(fe_identical(&r, &max)); + + r = zero; a = max; + secp256k1_fe_cmov(&r, &a, 1); + CHECK(fe_identical(&r, &max)); + + a = zero; + secp256k1_fe_cmov(&r, &a, 1); + CHECK(fe_identical(&r, &zero)); + + a = one; + secp256k1_fe_cmov(&r, &a, 1); + CHECK(fe_identical(&r, &one)); + + r = one; a = zero; + secp256k1_fe_cmov(&r, &a, 0); + CHECK(fe_identical(&r, &one)); +} + +static void fe_storage_cmov_test(void) { + static const secp256k1_fe_storage zero = SECP256K1_FE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 0); + static const secp256k1_fe_storage one = SECP256K1_FE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + static const secp256k1_fe_storage max = SECP256K1_FE_STORAGE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL + ); + secp256k1_fe_storage r = max; + secp256k1_fe_storage a = zero; + + secp256k1_fe_storage_cmov(&r, &a, 0); + CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0); + + r = zero; a = max; + secp256k1_fe_storage_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0); + + a = zero; + secp256k1_fe_storage_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &zero, sizeof(r)) == 0); + + a = one; + secp256k1_fe_storage_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); + + r = one; a = zero; + secp256k1_fe_storage_cmov(&r, &a, 0); + CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); +} + +static void scalar_cmov_test(void) { + static const secp256k1_scalar max = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364140UL + ); + secp256k1_scalar r = max; + secp256k1_scalar a = secp256k1_scalar_zero; + + secp256k1_scalar_cmov(&r, &a, 0); + CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0); + + r = secp256k1_scalar_zero; a = max; + secp256k1_scalar_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0); + + a = secp256k1_scalar_zero; + secp256k1_scalar_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &secp256k1_scalar_zero, sizeof(r)) == 0); + + a = secp256k1_scalar_one; + secp256k1_scalar_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &secp256k1_scalar_one, sizeof(r)) == 0); + + r = secp256k1_scalar_one; a = secp256k1_scalar_zero; + secp256k1_scalar_cmov(&r, &a, 0); + CHECK(secp256k1_memcmp_var(&r, &secp256k1_scalar_one, sizeof(r)) == 0); +} + +static void ge_storage_cmov_test(void) { + static const secp256k1_ge_storage zero = SECP256K1_GE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + static const secp256k1_ge_storage one = SECP256K1_GE_STORAGE_CONST(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1); + static const secp256k1_ge_storage max = SECP256K1_GE_STORAGE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL + ); + secp256k1_ge_storage r = max; + secp256k1_ge_storage a = zero; + + secp256k1_ge_storage_cmov(&r, &a, 0); + CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0); + + r = zero; a = max; + secp256k1_ge_storage_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &max, sizeof(r)) == 0); + + a = zero; + secp256k1_ge_storage_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &zero, sizeof(r)) == 0); + + a = one; + secp256k1_ge_storage_cmov(&r, &a, 1); + CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); + + r = one; a = zero; + secp256k1_ge_storage_cmov(&r, &a, 0); + CHECK(secp256k1_memcmp_var(&r, &one, sizeof(r)) == 0); +} + +static void run_cmov_tests(void) { + int_cmov_test(); + fe_cmov_test(); + fe_storage_cmov_test(); + scalar_cmov_test(); + ge_storage_cmov_test(); +} + int main(int argc, char **argv) { - unsigned char seed16[16] = {0}; - unsigned char run32[32] = {0}; + /* Disable buffering for stdout to improve reliability of getting + * diagnostic information. Happens right at the start of main because + * setbuf must be used before any other operation on the stream. */ + setbuf(stdout, NULL); + /* Also disable buffering for stderr because it's not guaranteed that it's + * unbuffered on all systems. */ + setbuf(stderr, NULL); + /* find iteration count */ if (argc > 1) { - count = strtol(argv[1], NULL, 0); - } - - /* find random seed */ - if (argc > 2) { - int pos = 0; - const char* ch = argv[2]; - while (pos < 16 && ch[0] != 0 && ch[1] != 0) { - unsigned short sh; - if (sscanf(ch, "%2hx", &sh)) { - seed16[pos] = sh; - } else { - break; - } - ch += 2; - pos++; - } + COUNT = strtol(argv[1], NULL, 0); } else { - FILE *frand = fopen("/dev/urandom", "r"); - if ((frand == NULL) || !fread(&seed16, sizeof(seed16), 1, frand)) { - uint64_t t = time(NULL) * (uint64_t)1337; - seed16[0] ^= t; - seed16[1] ^= t >> 8; - seed16[2] ^= t >> 16; - seed16[3] ^= t >> 24; - seed16[4] ^= t >> 32; - seed16[5] ^= t >> 40; - seed16[6] ^= t >> 48; - seed16[7] ^= t >> 56; + const char* env = getenv("SECP256K1_TEST_ITERS"); + if (env && strlen(env) > 0) { + COUNT = strtol(env, NULL, 0); } - fclose(frand); } - secp256k1_rand_seed(seed16); + if (COUNT <= 0) { + fputs("An iteration count of 0 or less is not allowed.\n", stderr); + return EXIT_FAILURE; + } + printf("test count = %i\n", COUNT); - printf("test count = %i\n", count); - printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); + /* run test RNG tests (must run before we really initialize the test RNG) */ + run_xoshiro256pp_tests(); - /* initialize */ - run_context_tests(); - ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - if (secp256k1_rand_bits(1)) { - secp256k1_rand256(run32); - CHECK(secp256k1_context_randomize(ctx, secp256k1_rand_bits(1) ? run32 : NULL)); - } + /* find random seed */ + testrand_init(argc > 2 ? argv[2] : NULL); + + /*** Setup test environment ***/ + + /* Create a global context available to all tests */ + CTX = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + /* Randomize the context only with probability 15/16 + to make sure we test without context randomization from time to time. + TODO Reconsider this when recalibrating the tests. */ + if (testrand_bits(4)) { + unsigned char rand32[32]; + testrand256(rand32); + CHECK(secp256k1_context_randomize(CTX, rand32)); + } + /* Make a writable copy of secp256k1_context_static in order to test the effect of API functions + that write to the context. The API does not support cloning the static context, so we use + memcpy instead. The user is not supposed to copy a context but we should still ensure that + the API functions handle copies of the static context gracefully. */ + STATIC_CTX = malloc(sizeof(*secp256k1_context_static)); + CHECK(STATIC_CTX != NULL); + memcpy(STATIC_CTX, secp256k1_context_static, sizeof(secp256k1_context)); + CHECK(!secp256k1_context_is_proper(STATIC_CTX)); + + /*** Run actual tests ***/ + + /* selftest tests */ + run_selftest_tests(); + + /* context tests */ + run_proper_context_tests(0); run_proper_context_tests(1); + run_static_context_tests(0); run_static_context_tests(1); + run_deprecated_context_flags_test(); + + /* scratch tests */ + run_scratch_tests(); + + /* integer arithmetic tests */ +#ifdef SECP256K1_WIDEMUL_INT128 + run_int128_tests(); +#endif + run_ctz_tests(); + run_modinv_tests(); + run_inverse_tests(); - run_rand_bits(); - run_rand_int(); + /* sorting tests */ + run_hsort_tests(); - run_sha256_tests(); + /* hash tests */ + run_sha256_known_output_tests(); + run_sha256_counter_tests(); run_hmac_sha256_tests(); run_rfc6979_hmac_sha256_tests(); - -#ifndef USE_NUM_NONE - /* num tests */ - run_num_smalltests(); -#endif + run_tagged_sha256_tests(); /* scalar tests */ run_scalar_tests(); /* field tests */ - run_field_inv(); - run_field_inv_var(); - run_field_inv_all_var(); + run_field_half(); run_field_misc(); run_field_convert(); + run_field_be32_overflow(); + run_fe_mul(); run_sqr(); run_sqrt(); /* group tests */ run_ge(); + run_gej(); run_group_decompress(); /* ecmult tests */ + run_ecmult_pre_g(); run_wnaf(); run_point_times_order(); + run_ecmult_near_split_bound(); run_ecmult_chain(); run_ecmult_constants(); run_ecmult_gen_blind(); run_ecmult_const_tests(); + run_ecmult_multi_tests(); run_ec_combine(); /* endomorphism tests */ -#ifdef USE_ENDOMORPHISM run_endomorphism_tests(); -#endif /* EC point parser test */ run_ec_pubkey_parse_test(); @@ -4505,31 +7739,53 @@ int main(int argc, char **argv) { /* EC key edge cases */ run_eckey_edge_case_test(); + /* EC key arithmetic test */ + run_eckey_negate_test(); + #ifdef ENABLE_MODULE_ECDH /* ecdh tests */ run_ecdh_tests(); #endif /* ecdsa tests */ + run_ec_illegal_argument_tests(); + run_pubkey_comparison(); + run_pubkey_sort(); run_random_pubkeys(); run_ecdsa_der_parse(); run_ecdsa_sign_verify(); run_ecdsa_end_to_end(); run_ecdsa_edge_cases(); -#ifdef ENABLE_OPENSSL_TESTS - run_ecdsa_openssl(); -#endif + run_ecdsa_wycheproof(); #ifdef ENABLE_MODULE_RECOVERY /* ECDSA pubkey recovery tests */ run_recovery_tests(); #endif - secp256k1_rand256(run32); - printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); +#ifdef ENABLE_MODULE_EXTRAKEYS + run_extrakeys_tests(); +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG + run_schnorrsig_tests(); +#endif + +#ifdef ENABLE_MODULE_ELLSWIFT + run_ellswift_tests(); +#endif + + /* util tests */ + run_secp256k1_memczero_test(); + run_secp256k1_byteorder_tests(); + + run_cmov_tests(); + + /*** Tear down test environment ***/ + free(STATIC_CTX); + secp256k1_context_destroy(CTX); - /* shutdown */ - secp256k1_context_destroy(ctx); + testrand_finish(); printf("no problems found\n"); return 0; diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c index d4f09d63c..6efa88982 100644 --- a/src/secp256k1/src/tests_exhaustive.c +++ b/src/secp256k1/src/tests_exhaustive.c @@ -1,75 +1,48 @@ -/************************************************************************** - * Copyright (c) 2016 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ - -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif +/*********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #include #include - #include -#undef USE_ECMULT_STATIC_PRECOMPUTATION - #ifndef EXHAUSTIVE_TEST_ORDER /* see group_impl.h for allowable values */ #define EXHAUSTIVE_TEST_ORDER 13 -#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */ #endif -#include "include/secp256k1.h" -#include "group.h" -#include "secp256k1.c" -#include "testrand_impl.h" +/* These values of B are all values in [1, 8] that result in a curve with even order. */ +#define EXHAUSTIVE_TEST_CURVE_HAS_EVEN_ORDER (SECP256K1_B == 1 || SECP256K1_B == 6 || SECP256K1_B == 8) -#ifdef ENABLE_MODULE_RECOVERY -#include "src/modules/recovery/main_impl.h" -#include "include/secp256k1_recovery.h" +#ifdef USE_EXTERNAL_DEFAULT_CALLBACKS + #pragma message("Ignoring USE_EXTERNAL_CALLBACKS in exhaustive_tests.") + #undef USE_EXTERNAL_DEFAULT_CALLBACKS #endif +#include "secp256k1.c" -/** stolen from tests.c */ -void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { - CHECK(a->infinity == b->infinity); - if (a->infinity) { - return; - } - CHECK(secp256k1_fe_equal_var(&a->x, &b->x)); - CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); -} +#include "../include/secp256k1.h" +#include "assumptions.h" +#include "group.h" +#include "testrand_impl.h" +#include "ecmult_compute_table_impl.h" +#include "ecmult_gen_compute_table_impl.h" +#include "testutil.h" +#include "util.h" -void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { - secp256k1_fe z2s; - secp256k1_fe u1, u2, s1, s2; - CHECK(a->infinity == b->infinity); - if (a->infinity) { - return; - } - /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */ - secp256k1_fe_sqr(&z2s, &b->z); - secp256k1_fe_mul(&u1, &a->x, &z2s); - u2 = b->x; secp256k1_fe_normalize_weak(&u2); - secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z); - s2 = b->y; secp256k1_fe_normalize_weak(&s2); - CHECK(secp256k1_fe_equal_var(&u1, &u2)); - CHECK(secp256k1_fe_equal_var(&s1, &s2)); -} +static int count = 2; -void random_fe(secp256k1_fe *x) { - unsigned char bin[32]; - do { - secp256k1_rand256(bin); - if (secp256k1_fe_set_b32(x, bin)) { - return; - } - } while(1); +static uint32_t num_cores = 1; +static uint32_t this_core = 0; + +SECP256K1_INLINE static int skip_section(uint64_t* iter) { + if (num_cores == 1) return 0; + *iter += 0xe7037ed1a0b428dbULL; + return ((((uint32_t)*iter ^ (*iter >> 32)) * num_cores) >> 32) != this_core; } -/** END stolen from tests.c */ -int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32, +static int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int attempt) { secp256k1_scalar s; @@ -89,115 +62,184 @@ int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned cha return 1; } -#ifdef USE_ENDOMORPHISM -void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) { +static void test_exhaustive_endomorphism(const secp256k1_ge *group) { int i; - for (i = 0; i < order; i++) { + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { secp256k1_ge res; secp256k1_ge_mul_lambda(&res, &group[i]); - ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res); + CHECK(secp256k1_ge_eq_var(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res)); } } -#endif -void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { +static void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj) { int i, j; + uint64_t iter = 0; /* Sanity-check (and check infinity functions) */ CHECK(secp256k1_ge_is_infinity(&group[0])); CHECK(secp256k1_gej_is_infinity(&groupj[0])); - for (i = 1; i < order; i++) { + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { CHECK(!secp256k1_ge_is_infinity(&group[i])); CHECK(!secp256k1_gej_is_infinity(&groupj[i])); } /* Check all addition formulae */ - for (j = 0; j < order; j++) { + for (j = 0; j < EXHAUSTIVE_TEST_ORDER; j++) { secp256k1_fe fe_inv; + if (skip_section(&iter)) continue; secp256k1_fe_inv(&fe_inv, &groupj[j].z); - for (i = 0; i < order; i++) { + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { secp256k1_ge zless_gej; secp256k1_gej tmp; /* add_var */ secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL); - ge_equals_gej(&group[(i + j) % order], &tmp); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(i + j) % EXHAUSTIVE_TEST_ORDER])); /* add_ge */ if (j > 0) { secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]); - ge_equals_gej(&group[(i + j) % order], &tmp); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(i + j) % EXHAUSTIVE_TEST_ORDER])); } /* add_ge_var */ secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL); - ge_equals_gej(&group[(i + j) % order], &tmp); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(i + j) % EXHAUSTIVE_TEST_ORDER])); /* add_zinv_var */ zless_gej.infinity = groupj[j].infinity; zless_gej.x = groupj[j].x; zless_gej.y = groupj[j].y; secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv); - ge_equals_gej(&group[(i + j) % order], &tmp); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(i + j) % EXHAUSTIVE_TEST_ORDER])); } } /* Check doubling */ - for (i = 0; i < order; i++) { + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { secp256k1_gej tmp; - if (i > 0) { - secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL); - ge_equals_gej(&group[(2 * i) % order], &tmp); - } + secp256k1_gej_double(&tmp, &groupj[i]); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(2 * i) % EXHAUSTIVE_TEST_ORDER])); secp256k1_gej_double_var(&tmp, &groupj[i], NULL); - ge_equals_gej(&group[(2 * i) % order], &tmp); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(2 * i) % EXHAUSTIVE_TEST_ORDER])); } /* Check negation */ - for (i = 1; i < order; i++) { + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { secp256k1_ge tmp; secp256k1_gej tmpj; secp256k1_ge_neg(&tmp, &group[i]); - ge_equals_ge(&group[order - i], &tmp); + CHECK(secp256k1_ge_eq_var(&tmp, &group[EXHAUSTIVE_TEST_ORDER - i])); secp256k1_gej_neg(&tmpj, &groupj[i]); - ge_equals_gej(&group[order - i], &tmpj); + CHECK(secp256k1_gej_eq_ge_var(&tmpj, &group[EXHAUSTIVE_TEST_ORDER - i])); } } -void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { +static void test_exhaustive_ecmult(const secp256k1_ge *group, const secp256k1_gej *groupj) { int i, j, r_log; - for (r_log = 1; r_log < order; r_log++) { - for (j = 0; j < order; j++) { - for (i = 0; i < order; i++) { + uint64_t iter = 0; + for (r_log = 1; r_log < EXHAUSTIVE_TEST_ORDER; r_log++) { + for (j = 0; j < EXHAUSTIVE_TEST_ORDER; j++) { + if (skip_section(&iter)) continue; + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { secp256k1_gej tmp; secp256k1_scalar na, ng; secp256k1_scalar_set_int(&na, i); secp256k1_scalar_set_int(&ng, j); - secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng); - ge_equals_gej(&group[(i * r_log + j) % order], &tmp); + secp256k1_ecmult(&tmp, &groupj[r_log], &na, &ng); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(i * r_log + j) % EXHAUSTIVE_TEST_ORDER])); + } + } + } + + for (j = 0; j < EXHAUSTIVE_TEST_ORDER; j++) { + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { + int ret; + secp256k1_gej tmp; + secp256k1_fe xn, xd, tmpf; + secp256k1_scalar ng; + + if (skip_section(&iter)) continue; + + secp256k1_scalar_set_int(&ng, j); + + /* Test secp256k1_ecmult_const. */ + secp256k1_ecmult_const(&tmp, &group[i], &ng); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(i * j) % EXHAUSTIVE_TEST_ORDER])); - if (i > 0) { - secp256k1_ecmult_const(&tmp, &group[i], &ng); - ge_equals_gej(&group[(i * j) % order], &tmp); + if (i != 0 && j != 0) { + /* Test secp256k1_ecmult_const_xonly with all curve X coordinates, and xd=NULL. */ + ret = secp256k1_ecmult_const_xonly(&tmpf, &group[i].x, NULL, &ng, 0); + CHECK(ret); + CHECK(secp256k1_fe_equal(&tmpf, &group[(i * j) % EXHAUSTIVE_TEST_ORDER].x)); + + /* Test secp256k1_ecmult_const_xonly with all curve X coordinates, with random xd. */ + testutil_random_fe_non_zero(&xd); + secp256k1_fe_mul(&xn, &xd, &group[i].x); + ret = secp256k1_ecmult_const_xonly(&tmpf, &xn, &xd, &ng, 0); + CHECK(ret); + CHECK(secp256k1_fe_equal(&tmpf, &group[(i * j) % EXHAUSTIVE_TEST_ORDER].x)); + } + } + } +} + +typedef struct { + secp256k1_scalar sc[2]; + secp256k1_ge pt[2]; +} ecmult_multi_data; + +static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) { + ecmult_multi_data *data = (ecmult_multi_data*) cbdata; + *sc = data->sc[idx]; + *pt = data->pt[idx]; + return 1; +} + +static void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group) { + int i, j, k, x, y; + uint64_t iter = 0; + secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 4096); + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) { + for (j = 0; j < EXHAUSTIVE_TEST_ORDER; j++) { + for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) { + for (x = 0; x < EXHAUSTIVE_TEST_ORDER; x++) { + if (skip_section(&iter)) continue; + for (y = 0; y < EXHAUSTIVE_TEST_ORDER; y++) { + secp256k1_gej tmp; + secp256k1_scalar g_sc; + ecmult_multi_data data; + + secp256k1_scalar_set_int(&data.sc[0], i); + secp256k1_scalar_set_int(&data.sc[1], j); + secp256k1_scalar_set_int(&g_sc, k); + data.pt[0] = group[x]; + data.pt[1] = group[y]; + + secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2); + CHECK(secp256k1_gej_eq_ge_var(&tmp, &group[(i * x + j * y + k) % EXHAUSTIVE_TEST_ORDER])); + } } } } } + secp256k1_scratch_destroy(&ctx->error_callback, scratch); } -void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { +static void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k, int* overflow) { secp256k1_fe x; unsigned char x_bin[32]; k %= EXHAUSTIVE_TEST_ORDER; x = group[k].x; secp256k1_fe_normalize(&x); secp256k1_fe_get_b32(x_bin, &x); - secp256k1_scalar_set_b32(r, x_bin, NULL); + secp256k1_scalar_set_b32(r, x_bin, overflow); } -void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { +static void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group) { int s, r, msg, key; - for (s = 1; s < order; s++) { - for (r = 1; r < order; r++) { - for (msg = 1; msg < order; msg++) { - for (key = 1; key < order; key++) { + uint64_t iter = 0; + for (s = 1; s < EXHAUSTIVE_TEST_ORDER; s++) { + for (r = 1; r < EXHAUSTIVE_TEST_ORDER; r++) { + for (msg = 1; msg < EXHAUSTIVE_TEST_ORDER; msg++) { + for (key = 1; key < EXHAUSTIVE_TEST_ORDER; key++) { secp256k1_ge nonconst_ge; secp256k1_ecdsa_signature sig; secp256k1_pubkey pk; @@ -206,6 +248,8 @@ void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *gr int k, should_verify; unsigned char msg32[32]; + if (skip_section(&iter)) continue; + secp256k1_scalar_set_int(&s_s, s); secp256k1_scalar_set_int(&r_s, r); secp256k1_scalar_set_int(&msg_s, msg); @@ -215,9 +259,9 @@ void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *gr /* Run through every k value that gives us this r and check that *one* works. * Note there could be none, there could be multiple, ECDSA is weird. */ should_verify = 0; - for (k = 0; k < order; k++) { + for (k = 0; k < EXHAUSTIVE_TEST_ORDER; k++) { secp256k1_scalar check_x_s; - r_from_k(&check_x_s, group, k); + r_from_k(&check_x_s, group, k, NULL); if (r_s == check_x_s) { secp256k1_scalar_set_int(&s_times_k_s, k); secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); @@ -242,14 +286,17 @@ void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *gr } } -void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { +static void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { int i, j, k; + uint64_t iter = 0; /* Loop */ - for (i = 1; i < order; i++) { /* message */ - for (j = 1; j < order; j++) { /* key */ - for (k = 1; k < order; k++) { /* nonce */ + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { /* message */ + for (j = 1; j < EXHAUSTIVE_TEST_ORDER; j++) { /* key */ + if (skip_section(&iter)) continue; + for (k = 1; k < EXHAUSTIVE_TEST_ORDER; k++) { /* nonce */ const int starting_k = k; + int ret; secp256k1_ecdsa_signature sig; secp256k1_scalar sk, msg, r, s, expected_r; unsigned char sk32[32], msg32[32]; @@ -258,16 +305,17 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou secp256k1_scalar_get_b32(sk32, &sk); secp256k1_scalar_get_b32(msg32, &msg); - secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + ret = secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + CHECK(ret == 1); secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); /* Note that we compute expected_r *after* signing -- this is important * because our nonce-computing function function might change k during * signing. */ - r_from_k(&expected_r, group, k); + r_from_k(&expected_r, group, k, NULL); CHECK(r == expected_r); - CHECK((k * s) % order == (i + r * j) % order || - (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + CHECK((k * s) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % EXHAUSTIVE_TEST_ORDER == (i + r * j) % EXHAUSTIVE_TEST_ORDER); /* Overflow means we've tried every possible nonce */ if (k < starting_k) { @@ -288,183 +336,131 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou } #ifdef ENABLE_MODULE_RECOVERY -void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { - int i, j, k; - - /* Loop */ - for (i = 1; i < order; i++) { /* message */ - for (j = 1; j < order; j++) { /* key */ - for (k = 1; k < order; k++) { /* nonce */ - const int starting_k = k; - secp256k1_fe r_dot_y_normalized; - secp256k1_ecdsa_recoverable_signature rsig; - secp256k1_ecdsa_signature sig; - secp256k1_scalar sk, msg, r, s, expected_r; - unsigned char sk32[32], msg32[32]; - int expected_recid; - int recid; - secp256k1_scalar_set_int(&msg, i); - secp256k1_scalar_set_int(&sk, j); - secp256k1_scalar_get_b32(sk32, &sk); - secp256k1_scalar_get_b32(msg32, &msg); +#include "modules/recovery/tests_exhaustive_impl.h" +#endif - secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k); +#ifdef ENABLE_MODULE_EXTRAKEYS +#include "modules/extrakeys/tests_exhaustive_impl.h" +#endif - /* Check directly */ - secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig); - r_from_k(&expected_r, group, k); - CHECK(r == expected_r); - CHECK((k * s) % order == (i + r * j) % order || - (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); - /* In computing the recid, there is an overflow condition that is disabled in - * scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value - * will exceed the group order, and our signing code always holds out for r - * values that don't overflow, so with a proper overflow check the tests would - * loop indefinitely. */ - r_dot_y_normalized = group[k].y; - secp256k1_fe_normalize(&r_dot_y_normalized); - /* Also the recovery id is flipped depending if we hit the low-s branch */ - if ((k * s) % order == (i + r * j) % order) { - expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0; - } else { - expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1; - } - CHECK(recid == expected_recid); +#ifdef ENABLE_MODULE_SCHNORRSIG +#include "modules/schnorrsig/tests_exhaustive_impl.h" +#endif - /* Convert to a standard sig then check */ - secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); - secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); - /* Note that we compute expected_r *after* signing -- this is important - * because our nonce-computing function function might change k during - * signing. */ - r_from_k(&expected_r, group, k); - CHECK(r == expected_r); - CHECK((k * s) % order == (i + r * j) % order || - (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); +#ifdef ENABLE_MODULE_ELLSWIFT +#include "modules/ellswift/tests_exhaustive_impl.h" +#endif - /* Overflow means we've tried every possible nonce */ - if (k < starting_k) { - break; - } - } +int main(int argc, char** argv) { + int i; + secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; + secp256k1_ge group[EXHAUSTIVE_TEST_ORDER]; + unsigned char rand32[32]; + secp256k1_context *ctx; + + /* Disable buffering for stdout to improve reliability of getting + * diagnostic information. Happens right at the start of main because + * setbuf must be used before any other operation on the stream. */ + setbuf(stdout, NULL); + /* Also disable buffering for stderr because it's not guaranteed that it's + * unbuffered on all systems. */ + setbuf(stderr, NULL); + + printf("Exhaustive tests for order %lu\n", (unsigned long)EXHAUSTIVE_TEST_ORDER); + + /* find iteration count */ + if (argc > 1) { + count = strtol(argv[1], NULL, 0); + } + printf("test count = %i\n", count); + + /* find random seed */ + testrand_init(argc > 2 ? argv[2] : NULL); + + /* set up split processing */ + if (argc > 4) { + num_cores = strtol(argv[3], NULL, 0); + this_core = strtol(argv[4], NULL, 0); + if (num_cores < 1 || this_core >= num_cores) { + fprintf(stderr, "Usage: %s [count] [seed] [numcores] [thiscore]\n", argv[0]); + return 1; } + printf("running tests for core %lu (out of [0..%lu])\n", (unsigned long)this_core, (unsigned long)num_cores - 1); } -} -void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { - /* This is essentially a copy of test_exhaustive_verify, with recovery added */ - int s, r, msg, key; - for (s = 1; s < order; s++) { - for (r = 1; r < order; r++) { - for (msg = 1; msg < order; msg++) { - for (key = 1; key < order; key++) { - secp256k1_ge nonconst_ge; - secp256k1_ecdsa_recoverable_signature rsig; - secp256k1_ecdsa_signature sig; - secp256k1_pubkey pk; - secp256k1_scalar sk_s, msg_s, r_s, s_s; - secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s; - int recid = 0; - int k, should_verify; - unsigned char msg32[32]; + /* Recreate the ecmult{,_gen} tables using the right generator (as selected via EXHAUSTIVE_TEST_ORDER) */ + secp256k1_ecmult_gen_compute_table(&secp256k1_ecmult_gen_prec_table[0][0], &secp256k1_ge_const_g, COMB_BLOCKS, COMB_TEETH, COMB_SPACING); + secp256k1_ecmult_compute_two_tables(secp256k1_pre_g, secp256k1_pre_g_128, WINDOW_G, &secp256k1_ge_const_g); + + while (count--) { + /* Build context */ + ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + testrand256(rand32); + CHECK(secp256k1_context_randomize(ctx, rand32)); + + /* Generate the entire group */ + secp256k1_gej_set_infinity(&groupj[0]); + secp256k1_ge_set_gej(&group[0], &groupj[0]); + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { + secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g); + secp256k1_ge_set_gej(&group[i], &groupj[i]); + if (count != 0) { + /* Set a different random z-value for each Jacobian point, except z=1 + is used in the last iteration. */ + secp256k1_fe z; + testutil_random_fe(&z); + secp256k1_gej_rescale(&groupj[i], &z); + } - secp256k1_scalar_set_int(&s_s, s); - secp256k1_scalar_set_int(&r_s, r); - secp256k1_scalar_set_int(&msg_s, msg); - secp256k1_scalar_set_int(&sk_s, key); - secp256k1_scalar_get_b32(msg32, &msg_s); + /* Verify against ecmult_gen */ + { + secp256k1_scalar scalar_i; + secp256k1_gej generatedj; + secp256k1_ge generated; - /* Verify by hand */ - /* Run through every k value that gives us this r and check that *one* works. - * Note there could be none, there could be multiple, ECDSA is weird. */ - should_verify = 0; - for (k = 0; k < order; k++) { - secp256k1_scalar check_x_s; - r_from_k(&check_x_s, group, k); - if (r_s == check_x_s) { - secp256k1_scalar_set_int(&s_times_k_s, k); - secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); - secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s); - secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s); - should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s); - } - } - /* nb we have a "high s" rule */ - should_verify &= !secp256k1_scalar_is_high(&s_s); + secp256k1_scalar_set_int(&scalar_i, i); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i); + secp256k1_ge_set_gej(&generated, &generatedj); - /* We would like to try recovering the pubkey and checking that it matches, - * but pubkey recovery is impossible in the exhaustive tests (the reason - * being that there are 12 nonzero r values, 12 nonzero points, and no - * overlap between the sets, so there are no valid signatures). */ - - /* Verify by converting to a standard signature and calling verify */ - secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid); - secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); - memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); - secp256k1_pubkey_save(&pk, &nonconst_ge); - CHECK(should_verify == - secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); - } + CHECK(group[i].infinity == 0); + CHECK(generated.infinity == 0); + CHECK(secp256k1_fe_equal(&generated.x, &group[i].x)); + CHECK(secp256k1_fe_equal(&generated.y, &group[i].y)); } } - } -} -#endif -int main(void) { - int i; - secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; - secp256k1_ge group[EXHAUSTIVE_TEST_ORDER]; + /* Run the tests */ + test_exhaustive_endomorphism(group); + test_exhaustive_addition(group, groupj); + test_exhaustive_ecmult(group, groupj); + test_exhaustive_ecmult_multi(ctx, group); + test_exhaustive_sign(ctx, group); + test_exhaustive_verify(ctx, group); - /* Build context */ - secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - - /* TODO set z = 1, then do num_tests runs with random z values */ +#ifdef ENABLE_MODULE_RECOVERY + test_exhaustive_recovery(ctx, group); +#endif +#ifdef ENABLE_MODULE_EXTRAKEYS + test_exhaustive_extrakeys(ctx, group); +#endif +#ifdef ENABLE_MODULE_SCHNORRSIG + test_exhaustive_schnorrsig(ctx); +#endif +#ifdef ENABLE_MODULE_ELLSWIFT + /* The ellswift algorithm does have additional edge cases when operating on + * curves of even order, which are not included in the code as secp256k1 is + * of odd order. Skip the ellswift tests if the used exhaustive tests curve + * is even-ordered accordingly. */ + #if !EXHAUSTIVE_TEST_CURVE_HAS_EVEN_ORDER + test_exhaustive_ellswift(ctx, group); + #endif +#endif - /* Generate the entire group */ - secp256k1_gej_set_infinity(&groupj[0]); - secp256k1_ge_set_gej(&group[0], &groupj[0]); - for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { - /* Set a different random z-value for each Jacobian point */ - secp256k1_fe z; - random_fe(&z); - - secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g); - secp256k1_ge_set_gej(&group[i], &groupj[i]); - secp256k1_gej_rescale(&groupj[i], &z); - - /* Verify against ecmult_gen */ - { - secp256k1_scalar scalar_i; - secp256k1_gej generatedj; - secp256k1_ge generated; - - secp256k1_scalar_set_int(&scalar_i, i); - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i); - secp256k1_ge_set_gej(&generated, &generatedj); - - CHECK(group[i].infinity == 0); - CHECK(generated.infinity == 0); - CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x)); - CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y)); - } + secp256k1_context_destroy(ctx); } - /* Run the tests */ -#ifdef USE_ENDOMORPHISM - test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER); -#endif - test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER); - test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER); - test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); - test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); + testrand_finish(); -#ifdef ENABLE_MODULE_RECOVERY - test_exhaustive_recovery_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); - test_exhaustive_recovery_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); -#endif - - secp256k1_context_destroy(ctx); + printf("no problems found\n"); return 0; } - diff --git a/src/secp256k1/src/testutil.h b/src/secp256k1/src/testutil.h new file mode 100644 index 000000000..fc56854dd --- /dev/null +++ b/src/secp256k1/src/testutil.h @@ -0,0 +1,148 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_TESTUTIL_H +#define SECP256K1_TESTUTIL_H + +#include "field.h" +#include "group.h" +#include "testrand.h" +#include "util.h" + +static void testutil_random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + testrand256(bin); + if (secp256k1_fe_set_b32_limit(x, bin)) { + return; + } + } while(1); +} + +static void testutil_random_fe_non_zero(secp256k1_fe *nz) { + do { + testutil_random_fe(nz); + } while (secp256k1_fe_is_zero(nz)); +} + +static void testutil_random_fe_magnitude(secp256k1_fe *fe, int m) { + secp256k1_fe zero; + int n = testrand_int(m + 1); + secp256k1_fe_normalize(fe); + if (n == 0) { + return; + } + secp256k1_fe_clear(&zero); + secp256k1_fe_negate(&zero, &zero, 0); + secp256k1_fe_mul_int_unchecked(&zero, n - 1); + secp256k1_fe_add(fe, &zero); +#ifdef VERIFY + CHECK(fe->magnitude == n); +#endif +} + +static void testutil_random_fe_test(secp256k1_fe *x) { + unsigned char bin[32]; + do { + testrand256_test(bin); + if (secp256k1_fe_set_b32_limit(x, bin)) { + return; + } + } while(1); +} + +static void testutil_random_fe_non_zero_test(secp256k1_fe *fe) { + do { + testutil_random_fe_test(fe); + } while(secp256k1_fe_is_zero(fe)); +} + +static void testutil_random_ge_x_magnitude(secp256k1_ge *ge) { + testutil_random_fe_magnitude(&ge->x, SECP256K1_GE_X_MAGNITUDE_MAX); +} + +static void testutil_random_ge_y_magnitude(secp256k1_ge *ge) { + testutil_random_fe_magnitude(&ge->y, SECP256K1_GE_Y_MAGNITUDE_MAX); +} + +static void testutil_random_gej_x_magnitude(secp256k1_gej *gej) { + testutil_random_fe_magnitude(&gej->x, SECP256K1_GEJ_X_MAGNITUDE_MAX); +} + +static void testutil_random_gej_y_magnitude(secp256k1_gej *gej) { + testutil_random_fe_magnitude(&gej->y, SECP256K1_GEJ_Y_MAGNITUDE_MAX); +} + +static void testutil_random_gej_z_magnitude(secp256k1_gej *gej) { + testutil_random_fe_magnitude(&gej->z, SECP256K1_GEJ_Z_MAGNITUDE_MAX); +} + +static void testutil_random_ge_test(secp256k1_ge *ge) { + secp256k1_fe fe; + do { + testutil_random_fe_test(&fe); + if (secp256k1_ge_set_xo_var(ge, &fe, testrand_bits(1))) { + secp256k1_fe_normalize(&ge->y); + break; + } + } while(1); + ge->infinity = 0; +} + +static void testutil_random_ge_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { + secp256k1_fe z2, z3; + testutil_random_fe_non_zero_test(&gej->z); + secp256k1_fe_sqr(&z2, &gej->z); + secp256k1_fe_mul(&z3, &z2, &gej->z); + secp256k1_fe_mul(&gej->x, &ge->x, &z2); + secp256k1_fe_mul(&gej->y, &ge->y, &z3); + gej->infinity = ge->infinity; +} + +static void testutil_random_gej_test(secp256k1_gej *gej) { + secp256k1_ge ge; + testutil_random_ge_test(&ge); + testutil_random_ge_jacobian_test(gej, &ge); +} + +static void testutil_random_pubkey_test(secp256k1_pubkey *pk) { + secp256k1_ge ge; + testutil_random_ge_test(&ge); + secp256k1_pubkey_save(pk, &ge); +} + +static void testutil_random_scalar_order_test(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + testrand256_test(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +static void testutil_random_scalar_order(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + testrand256(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +static void testutil_random_scalar_order_b32(unsigned char *b32) { + secp256k1_scalar num; + testutil_random_scalar_order(&num); + secp256k1_scalar_get_b32(b32, &num); +} + +#endif /* SECP256K1_TESTUTIL_H */ diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h index 708807823..ca73752cc 100644 --- a/src/secp256k1/src/util.h +++ b/src/secp256k1/src/util.h @@ -1,19 +1,82 @@ -/************************************************************************ - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php .* - ************************************************************************/ +/*********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ #ifndef SECP256K1_UTIL_H #define SECP256K1_UTIL_H -#if defined HAVE_CONFIG_H -#include "libsecp256k1-config.h" -#endif +#include "../include/secp256k1.h" #include #include #include +#include + +#define STR_(x) #x +#define STR(x) STR_(x) +#define DEBUG_CONFIG_MSG(x) "DEBUG_CONFIG: " x +#define DEBUG_CONFIG_DEF(x) DEBUG_CONFIG_MSG(#x "=" STR(x)) + +/* Debug helper for printing arrays of unsigned char. */ +#define PRINT_BUF(buf, len) do { \ + printf("%s[%lu] = ", #buf, (unsigned long)len); \ + print_buf_plain(buf, len); \ +} while(0) + +static void print_buf_plain(const unsigned char *buf, size_t len) { + size_t i; + printf("{"); + for (i = 0; i < len; i++) { + if (i % 8 == 0) { + printf("\n "); + } else { + printf(" "); + } + printf("0x%02X,", buf[i]); + } + printf("\n}\n"); +} + +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(2,7) +# define SECP256K1_INLINE __inline__ +# elif (defined(_MSC_VER)) +# define SECP256K1_INLINE __inline +# else +# define SECP256K1_INLINE +# endif +# else +# define SECP256K1_INLINE inline +# endif + +/** Assert statically that expr is true. + * + * This is a statement-like macro and can only be used inside functions. + */ +#define STATIC_ASSERT(expr) do { \ + switch(0) { \ + case 0: \ + /* If expr evaluates to 0, we have two case labels "0", which is illegal. */ \ + case /* ERROR: static assertion failed */ (expr): \ + ; \ + } \ +} while(0) + +/** Assert statically that expr is an integer constant expression, and run stmt. + * + * Useful for example to enforce that magnitude arguments are constant. + */ +#define ASSERT_INT_CONST_AND_DO(expr, stmt) do { \ + switch(42) { \ + /* C allows only integer constant expressions as case labels. */ \ + case /* ERROR: integer argument is not constant */ (expr): \ + break; \ + default: ; \ + } \ + stmt; \ +} while(0) typedef struct { void (*fn)(const char *text, void* data); @@ -24,6 +87,33 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * cb->fn(text, (void*)cb->data); } +#ifndef USE_EXTERNAL_DEFAULT_CALLBACKS +static void secp256k1_default_illegal_callback_fn(const char* str, void* data) { + (void)data; + fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); + abort(); +} +static void secp256k1_default_error_callback_fn(const char* str, void* data) { + (void)data; + fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); + abort(); +} +#else +void secp256k1_default_illegal_callback_fn(const char* str, void* data); +void secp256k1_default_error_callback_fn(const char* str, void* data); +#endif + +static const secp256k1_callback default_illegal_callback = { + secp256k1_default_illegal_callback_fn, + NULL +}; + +static const secp256k1_callback default_error_callback = { + secp256k1_default_error_callback_fn, + NULL +}; + + #ifdef DETERMINISTIC #define TEST_FAILURE(msg) do { \ fprintf(stderr, "%s\n", msg); \ @@ -36,7 +126,7 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * } while(0) #endif -#ifdef HAVE_BUILTIN_EXPECT +#if SECP256K1_GNUC_PREREQ(3, 0) #define EXPECT(x,c) __builtin_expect((x),(c)) #else #define EXPECT(x,c) (x) @@ -56,16 +146,11 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * } while(0) #endif -/* Like assert(), but when VERIFY is defined, and side-effect safe. */ -#if defined(COVERAGE) -#define VERIFY_CHECK(check) -#define VERIFY_SETUP(stmt) -#elif defined(VERIFY) +/* Like assert(), but when VERIFY is defined. */ +#if defined(VERIFY) #define VERIFY_CHECK CHECK -#define VERIFY_SETUP(stmt) do { stmt; } while(0) #else -#define VERIFY_CHECK(cond) do { (void)(cond); } while(0) -#define VERIFY_SETUP(stmt) +#define VERIFY_CHECK(cond) #endif static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) { @@ -76,6 +161,20 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_ return ret; } +#if defined(__BIGGEST_ALIGNMENT__) +#define ALIGNMENT __BIGGEST_ALIGNMENT__ +#else +/* Using 16 bytes alignment because common architectures never have alignment + * requirements above 8 for any of the types we care about. In addition we + * leave some room because currently we don't care about a few bytes. */ +#define ALIGNMENT 16 +#endif + +/* ceil(x/y) for integers x > 0 and y > 0. Here, / denotes rational division. */ +#define CEIL_DIV(x, y) (1 + ((x) - 1) / (y)) + +#define ROUND_TO_ALIGN(size) (CEIL_DIV(size, ALIGNMENT) * ALIGNMENT) + /* Macro for restrict, when available and not in a VERIFY build. */ #if defined(SECP256K1_BUILD) && defined(VERIFY) # define SECP256K1_RESTRICT @@ -101,13 +200,210 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_ # define I64uFORMAT "llu" #endif -#if defined(HAVE___INT128) -# if defined(__GNUC__) -# define SECP256K1_GNUC_EXT __extension__ -# else -# define SECP256K1_GNUC_EXT -# endif -SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; +#if defined(__GNUC__) +# define SECP256K1_GNUC_EXT __extension__ +#else +# define SECP256K1_GNUC_EXT +#endif + +/* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */ +static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) { + unsigned char *p = (unsigned char *)s; + /* Access flag with a volatile-qualified lvalue. + This prevents clang from figuring out (after inlining) that flag can + take only be 0 or 1, which leads to variable time code. */ + volatile int vflag = flag; + unsigned char mask = -(unsigned char) vflag; + while (len) { + *p &= ~mask; + p++; + len--; + } +} + +/** Semantics like memcmp. Variable-time. + * + * We use this to avoid possible compiler bugs with memcmp, e.g. + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189 + */ +static SECP256K1_INLINE int secp256k1_memcmp_var(const void *s1, const void *s2, size_t n) { + const unsigned char *p1 = s1, *p2 = s2; + size_t i; + + for (i = 0; i < n; i++) { + int diff = p1[i] - p2[i]; + if (diff != 0) { + return diff; + } + } + return 0; +} + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized and non-negative.*/ +static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag) { + unsigned int mask0, mask1, r_masked, a_masked; + /* Access flag with a volatile-qualified lvalue. + This prevents clang from figuring out (after inlining) that flag can + take only be 0 or 1, which leads to variable time code. */ + volatile int vflag = flag; + + /* Casting a negative int to unsigned and back to int is implementation defined behavior */ + VERIFY_CHECK(*r >= 0 && *a >= 0); + + mask0 = (unsigned int)vflag + ~0u; + mask1 = ~mask0; + r_masked = ((unsigned int)*r & mask0); + a_masked = ((unsigned int)*a & mask1); + + *r = (int)(r_masked | a_masked); +} + +#if defined(USE_FORCE_WIDEMUL_INT128_STRUCT) +/* If USE_FORCE_WIDEMUL_INT128_STRUCT is set, use int128_struct. */ +# define SECP256K1_WIDEMUL_INT128 1 +# define SECP256K1_INT128_STRUCT 1 +#elif defined(USE_FORCE_WIDEMUL_INT128) +/* If USE_FORCE_WIDEMUL_INT128 is set, use int128. */ +# define SECP256K1_WIDEMUL_INT128 1 +# define SECP256K1_INT128_NATIVE 1 +#elif defined(USE_FORCE_WIDEMUL_INT64) +/* If USE_FORCE_WIDEMUL_INT64 is set, use int64. */ +# define SECP256K1_WIDEMUL_INT64 1 +#elif defined(UINT128_MAX) || defined(__SIZEOF_INT128__) +/* If a native 128-bit integer type exists, use int128. */ +# define SECP256K1_WIDEMUL_INT128 1 +# define SECP256K1_INT128_NATIVE 1 +#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) +/* On 64-bit MSVC targets (x86_64 and arm64), use int128_struct + * (which has special logic to implement using intrinsics on those systems). */ +# define SECP256K1_WIDEMUL_INT128 1 +# define SECP256K1_INT128_STRUCT 1 +#elif SIZE_MAX > 0xffffffff +/* Systems with 64-bit pointers (and thus registers) very likely benefit from + * using 64-bit based arithmetic (even if we need to fall back to 32x32->64 based + * multiplication logic). */ +# define SECP256K1_WIDEMUL_INT128 1 +# define SECP256K1_INT128_STRUCT 1 +#else +/* Lastly, fall back to int64 based arithmetic. */ +# define SECP256K1_WIDEMUL_INT64 1 +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +/* Determine the number of trailing zero bits in a (non-zero) 32-bit x. + * This function is only intended to be used as fallback for + * secp256k1_ctz32_var, but permits it to be tested separately. */ +static SECP256K1_INLINE int secp256k1_ctz32_var_debruijn(uint32_t x) { + static const uint8_t debruijn[32] = { + 0x00, 0x01, 0x02, 0x18, 0x03, 0x13, 0x06, 0x19, 0x16, 0x04, 0x14, 0x0A, + 0x10, 0x07, 0x0C, 0x1A, 0x1F, 0x17, 0x12, 0x05, 0x15, 0x09, 0x0F, 0x0B, + 0x1E, 0x11, 0x08, 0x0E, 0x1D, 0x0D, 0x1C, 0x1B + }; + return debruijn[(uint32_t)((x & -x) * 0x04D7651FU) >> 27]; +} + +/* Determine the number of trailing zero bits in a (non-zero) 64-bit x. + * This function is only intended to be used as fallback for + * secp256k1_ctz64_var, but permits it to be tested separately. */ +static SECP256K1_INLINE int secp256k1_ctz64_var_debruijn(uint64_t x) { + static const uint8_t debruijn[64] = { + 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, + 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, + 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, + 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12 + }; + return debruijn[(uint64_t)((x & -x) * 0x022FDD63CC95386DU) >> 58]; +} + +/* Determine the number of trailing zero bits in a (non-zero) 32-bit x. */ +static SECP256K1_INLINE int secp256k1_ctz32_var(uint32_t x) { + VERIFY_CHECK(x != 0); +#if (__has_builtin(__builtin_ctz) || SECP256K1_GNUC_PREREQ(3,4)) + /* If the unsigned type is sufficient to represent the largest uint32_t, consider __builtin_ctz. */ + if (((unsigned)UINT32_MAX) == UINT32_MAX) { + return __builtin_ctz(x); + } +#endif +#if (__has_builtin(__builtin_ctzl) || SECP256K1_GNUC_PREREQ(3,4)) + /* Otherwise consider __builtin_ctzl (the unsigned long type is always at least 32 bits). */ + return __builtin_ctzl(x); +#else + /* If no suitable CTZ builtin is available, use a (variable time) software emulation. */ + return secp256k1_ctz32_var_debruijn(x); +#endif +} + +/* Determine the number of trailing zero bits in a (non-zero) 64-bit x. */ +static SECP256K1_INLINE int secp256k1_ctz64_var(uint64_t x) { + VERIFY_CHECK(x != 0); +#if (__has_builtin(__builtin_ctzl) || SECP256K1_GNUC_PREREQ(3,4)) + /* If the unsigned long type is sufficient to represent the largest uint64_t, consider __builtin_ctzl. */ + if (((unsigned long)UINT64_MAX) == UINT64_MAX) { + return __builtin_ctzl(x); + } +#endif +#if (__has_builtin(__builtin_ctzll) || SECP256K1_GNUC_PREREQ(3,4)) + /* Otherwise consider __builtin_ctzll (the unsigned long long type is always at least 64 bits). */ + return __builtin_ctzll(x); +#else + /* If no suitable CTZ builtin is available, use a (variable time) software emulation. */ + return secp256k1_ctz64_var_debruijn(x); +#endif +} + +/* Read a uint32_t in big endian */ +SECP256K1_INLINE static uint32_t secp256k1_read_be32(const unsigned char* p) { + return (uint32_t)p[0] << 24 | + (uint32_t)p[1] << 16 | + (uint32_t)p[2] << 8 | + (uint32_t)p[3]; +} + +/* Write a uint32_t in big endian */ +SECP256K1_INLINE static void secp256k1_write_be32(unsigned char* p, uint32_t x) { + p[3] = x; + p[2] = x >> 8; + p[1] = x >> 16; + p[0] = x >> 24; +} + +/* Read a uint64_t in big endian */ +SECP256K1_INLINE static uint64_t secp256k1_read_be64(const unsigned char* p) { + return (uint64_t)p[0] << 56 | + (uint64_t)p[1] << 48 | + (uint64_t)p[2] << 40 | + (uint64_t)p[3] << 32 | + (uint64_t)p[4] << 24 | + (uint64_t)p[5] << 16 | + (uint64_t)p[6] << 8 | + (uint64_t)p[7]; +} + +/* Write a uint64_t in big endian */ +SECP256K1_INLINE static void secp256k1_write_be64(unsigned char* p, uint64_t x) { + p[7] = x; + p[6] = x >> 8; + p[5] = x >> 16; + p[4] = x >> 24; + p[3] = x >> 32; + p[2] = x >> 40; + p[1] = x >> 48; + p[0] = x >> 56; +} + +/* Rotate a uint32_t to the right. */ +SECP256K1_INLINE static uint32_t secp256k1_rotr32(const uint32_t x, const unsigned int by) { +#if defined(_MSC_VER) + return _rotr(x, by); /* needs */ +#else + /* Reduce rotation amount to avoid UB when shifting. */ + const unsigned int mask = CHAR_BIT * sizeof(x) - 1; + /* Turned into a rot instruction by GCC and clang. */ + return (x >> (by & mask)) | (x << ((-by) & mask)); #endif +} #endif /* SECP256K1_UTIL_H */ diff --git a/src/secp256k1/src/wycheproof/WYCHEPROOF_COPYING b/src/secp256k1/src/wycheproof/WYCHEPROOF_COPYING new file mode 100644 index 000000000..cddf9d918 --- /dev/null +++ b/src/secp256k1/src/wycheproof/WYCHEPROOF_COPYING @@ -0,0 +1,212 @@ +* The file `ecdsa_secp256k1_sha256_bitcoin_test.json` in this directory + comes from Google's project Wycheproof with git commit + `b063b4aedae951c69df014cd25fa6d69ae9e8cb9`, see + https://github.com/google/wycheproof/blob/b063b4aedae951c69df014cd25fa6d69ae9e8cb9/testvectors_v1/ecdsa_secp256k1_sha256_bitcoin_test.json + +* The file `ecdsa_secp256k1_sha256_bitcoin_test.h` is generated from + `ecdsa_secp256k1_sha256_bitcoin_test.json` using the script + `tests_wycheproof_generate.py`. + +------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h b/src/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h new file mode 100644 index 000000000..736737fd6 --- /dev/null +++ b/src/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h @@ -0,0 +1,1564 @@ +/* Note: this file was autogenerated using tests_wycheproof_generate.py. Do not edit. */ +#define SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS (463) + +typedef struct { + size_t pk_offset; + size_t msg_offset; + size_t msg_len; + size_t sig_offset; + size_t sig_len; + int expected_verify; +} wycheproof_ecdsa_testvector; + +static const unsigned char wycheproof_ecdsa_messages[] = { 0x31,0x32,0x33,0x34,0x30,0x30, + 0x32,0x35,0x35,0x38,0x35, + 0x34,0x32,0x36,0x34,0x37,0x39,0x37,0x32,0x34, + 0x37,0x31,0x33,0x38,0x36,0x38,0x34,0x38,0x39,0x31, + 0x31,0x30,0x33,0x35,0x39,0x33,0x33,0x31,0x36,0x36,0x38, + 0x33,0x39,0x34,0x39,0x34,0x30,0x31,0x32,0x31,0x35, + 0x31,0x33,0x34,0x34,0x32,0x39,0x33,0x30,0x37,0x39, + 0x33,0x37,0x30,0x36,0x32,0x31,0x31,0x37,0x31,0x32, + 0x33,0x34,0x33,0x36,0x38,0x38,0x37,0x31,0x32, + 0x31,0x33,0x35,0x31,0x35,0x33,0x30,0x33,0x37,0x30, + 0x36,0x35,0x35,0x33,0x32,0x30,0x33,0x31,0x32,0x36, + 0x31,0x35,0x36,0x34,0x33,0x34,0x36,0x36,0x30,0x33, + 0x34,0x34,0x32,0x39,0x35,0x33,0x39,0x31,0x31,0x37, + 0x31,0x30,0x39,0x35,0x33,0x32,0x36,0x31,0x33,0x35,0x31, + 0x35,0x39,0x38,0x37,0x33,0x35,0x30,0x30,0x34,0x31, + 0x33,0x34,0x36,0x33,0x30,0x30,0x36,0x38,0x37,0x38, + 0x39,0x38,0x31,0x37,0x33,0x32,0x30,0x32,0x38,0x37, + 0x33,0x32,0x32,0x32,0x30,0x34,0x31,0x30,0x34,0x36, + 0x36,0x36,0x36,0x36,0x33,0x30,0x37,0x31,0x30,0x34, + 0x31,0x30,0x33,0x35,0x39,0x35,0x31,0x38,0x39,0x38, + 0x31,0x38,0x34,0x36,0x35,0x39,0x37,0x31,0x39,0x35, + 0x33,0x31,0x33,0x36,0x30,0x34,0x36,0x31,0x38,0x39, + 0x32,0x36,0x36,0x33,0x37,0x38,0x34,0x32,0x35,0x34, + 0x31,0x36,0x35,0x32,0x31,0x30,0x30,0x35,0x32,0x34, + 0x35,0x37,0x34,0x38,0x30,0x38,0x31,0x36,0x39,0x36, + 0x36,0x33,0x34,0x33,0x39,0x31,0x33,0x34,0x36,0x38, + 0x31,0x35,0x34,0x31,0x31,0x30,0x33,0x35,0x39,0x38, + 0x31,0x30,0x34,0x37,0x38,0x35,0x38,0x30,0x31,0x32,0x38, + 0x31,0x30,0x35,0x33,0x36,0x32,0x38,0x35,0x35,0x36,0x38, + 0x39,0x35,0x33,0x39,0x30,0x34,0x31,0x30,0x35, + 0x39,0x37,0x38,0x38,0x34,0x38,0x30,0x33,0x39, + 0x33,0x36,0x31,0x30,0x36,0x37,0x32,0x34,0x34,0x32, + 0x31,0x30,0x35,0x34,0x32,0x34,0x30,0x37,0x30,0x35, + 0x35,0x31,0x37,0x34,0x34,0x34,0x38,0x31,0x39,0x37, + 0x31,0x39,0x36,0x37,0x35,0x36,0x31,0x32,0x35,0x31, + 0x33,0x34,0x34,0x37,0x32,0x35,0x33,0x33,0x34,0x33, + 0x33,0x36,0x38,0x32,0x36,0x34,0x33,0x31,0x38, + 0x33,0x32,0x36,0x31,0x31,0x39,0x38,0x36,0x30,0x38, + 0x39,0x36,0x37,0x38,0x37,0x38,0x31,0x30,0x39,0x34, + 0x34,0x39,0x35,0x38,0x38,0x32,0x33,0x38,0x32,0x33, + 0x38,0x32,0x34,0x36,0x33,0x37,0x38,0x33,0x37, + 0x31,0x31,0x30,0x32,0x30,0x38,0x33,0x33,0x37,0x37,0x36, + 0x31,0x33,0x33,0x38,0x37,0x31,0x36,0x34,0x38, + 0x33,0x32,0x32,0x31,0x34,0x34,0x31,0x36,0x32, + 0x31,0x30,0x36,0x38,0x36,0x36,0x35,0x35,0x35,0x34,0x36, + 0x36,0x32,0x31,0x35,0x35,0x32,0x34,0x36, + 0x37,0x30,0x33,0x30,0x38,0x31,0x38,0x37,0x37,0x34, + 0x35,0x39,0x32,0x34,0x35,0x32,0x33,0x37,0x34,0x34, + 0x31,0x34,0x39,0x35,0x35,0x38,0x36,0x36,0x32,0x31, + 0x34,0x30,0x30,0x35,0x33,0x31,0x34,0x34,0x30,0x36, + 0x33,0x30,0x39,0x36,0x34,0x35,0x37,0x35,0x31,0x32, + 0x32,0x37,0x38,0x34,0x30,0x32,0x35,0x36,0x32,0x30, + 0x32,0x36,0x31,0x38,0x37,0x38,0x37,0x34,0x31,0x38, + 0x31,0x36,0x34,0x32,0x36,0x32,0x35,0x32,0x36,0x32, + 0x36,0x38,0x32,0x34,0x31,0x38,0x39,0x34,0x33,0x36, + 0x34,0x38,0x34,0x32,0x34,0x35,0x34,0x32,0x35, + 0x4d,0x73,0x67, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4d,0x65,0x73,0x73,0x61,0x67,0x65}; + +static const unsigned char wycheproof_ecdsa_public_keys[] = { 0x04,0xb8,0x38,0xff,0x44,0xe5,0xbc,0x17,0x7b,0xf2,0x11,0x89,0xd0,0x76,0x60,0x82,0xfc,0x9d,0x84,0x32,0x26,0x88,0x7f,0xc9,0x76,0x03,0x71,0x10,0x0b,0x7e,0xe2,0x0a,0x6f,0xf0,0xc9,0xd7,0x5b,0xfb,0xa7,0xb3,0x1a,0x6b,0xca,0x19,0x74,0x49,0x6e,0xeb,0x56,0xde,0x35,0x70,0x71,0x95,0x5d,0x83,0xc4,0xb1,0xba,0xda,0xa0,0xb2,0x18,0x32,0xe9, + 0x04,0x07,0x31,0x0f,0x90,0xa9,0xea,0xe1,0x49,0xa0,0x84,0x02,0xf5,0x41,0x94,0xa0,0xf7,0xb4,0xac,0x42,0x7b,0xf8,0xd9,0xbd,0x6c,0x76,0x81,0x07,0x1d,0xc4,0x7d,0xc3,0x62,0x26,0xa6,0xd3,0x7a,0xc4,0x6d,0x61,0xfd,0x60,0x0c,0x0b,0xf1,0xbf,0xf8,0x76,0x89,0xed,0x11,0x7d,0xda,0x6b,0x0e,0x59,0x31,0x8a,0xe0,0x10,0xa1,0x97,0xa2,0x6c,0xa0, + 0x04,0xbc,0x97,0xe7,0x58,0x5e,0xec,0xad,0x48,0xe1,0x66,0x83,0xbc,0x40,0x91,0x70,0x8e,0x1a,0x93,0x0c,0x68,0x3f,0xc4,0x70,0x01,0xd4,0xb3,0x83,0x59,0x4f,0x2c,0x4e,0x22,0x70,0x59,0x89,0xcf,0x69,0xda,0xea,0xdd,0x4e,0x4e,0x4b,0x81,0x51,0xed,0x88,0x8d,0xfe,0xc2,0x0f,0xb0,0x17,0x28,0xd8,0x9d,0x56,0xb3,0xf3,0x8f,0x2a,0xe9,0xc8,0xc5, + 0x04,0x44,0xad,0x33,0x9a,0xfb,0xc2,0x1e,0x9a,0xbf,0x7b,0x60,0x2a,0x5c,0xa5,0x35,0xea,0x37,0x81,0x35,0xb6,0xd1,0x0d,0x81,0x31,0x0b,0xdd,0x82,0x93,0xd1,0xdf,0x32,0x52,0xb6,0x3f,0xf7,0xd0,0x77,0x47,0x70,0xf8,0xfe,0x1d,0x17,0x22,0xfa,0x83,0xac,0xd0,0x2f,0x43,0x4e,0x4f,0xc1,0x10,0xa0,0xcc,0x8f,0x6d,0xdd,0xd3,0x7d,0x56,0xc4,0x63, + 0x04,0x12,0x60,0xc2,0x12,0x2c,0x9e,0x24,0x4e,0x1a,0xf5,0x15,0x1b,0xed,0xe0,0xc3,0xae,0x23,0xb5,0x4d,0x7c,0x59,0x68,0x81,0xd3,0xee,0xba,0xd2,0x1f,0x37,0xdd,0x87,0x8c,0x5c,0x9a,0x0c,0x1a,0x9a,0xde,0x76,0x73,0x7a,0x88,0x11,0xbd,0x6a,0x7f,0x92,0x87,0xc9,0x78,0xee,0x39,0x6a,0xa8,0x9c,0x11,0xe4,0x72,0x29,0xd2,0xcc,0xb5,0x52,0xf0, + 0x04,0x18,0x77,0x04,0x5b,0xe2,0x5d,0x34,0xa1,0xd0,0x60,0x0f,0x9d,0x5c,0x00,0xd0,0x64,0x5a,0x2a,0x54,0x37,0x9b,0x6c,0xee,0xfa,0xd2,0xe6,0xbf,0x5c,0x2a,0x33,0x52,0xce,0x82,0x1a,0x53,0x2c,0xc1,0x75,0x1e,0xe1,0xd3,0x6d,0x41,0xc3,0xd6,0xab,0x4e,0x9b,0x14,0x3e,0x44,0xec,0x46,0xd7,0x34,0x78,0xea,0x6a,0x79,0xa5,0xc0,0xe5,0x41,0x59, + 0x04,0x45,0x54,0x39,0xfc,0xc3,0xd2,0xde,0xec,0xed,0xde,0xae,0xce,0x60,0xe7,0xbd,0x17,0x30,0x4f,0x36,0xeb,0xb6,0x02,0xad,0xf5,0xa2,0x2e,0x0b,0x8f,0x1d,0xb4,0x6a,0x50,0xae,0xc3,0x8f,0xb2,0xba,0xf2,0x21,0xe9,0xa8,0xd1,0x88,0x7c,0x7b,0xf6,0x22,0x2d,0xd1,0x83,0x46,0x34,0xe7,0x72,0x63,0x31,0x5a,0xf6,0xd2,0x36,0x09,0xd0,0x4f,0x77, + 0x04,0x2e,0x1f,0x46,0x6b,0x02,0x4c,0x0c,0x3a,0xce,0x24,0x37,0xde,0x09,0x12,0x7f,0xed,0x04,0xb7,0x06,0xf9,0x4b,0x19,0xa2,0x1b,0xb1,0xc2,0xac,0xf3,0x5c,0xec,0xe7,0x18,0x04,0x49,0xae,0x35,0x23,0xd7,0x25,0x34,0xe9,0x64,0x97,0x2c,0xfd,0x3b,0x38,0xaf,0x0b,0xdd,0xd9,0x61,0x9e,0x5a,0xf2,0x23,0xe4,0xd1,0xa4,0x0f,0x34,0xcf,0x9f,0x1d, + 0x04,0x8e,0x7a,0xbd,0xbb,0xd1,0x8d,0xe7,0x45,0x23,0x74,0xc1,0x87,0x9a,0x1c,0x3b,0x01,0xd1,0x32,0x61,0xe7,0xd4,0x57,0x1c,0x3b,0x47,0xa1,0xc7,0x6c,0x55,0xa2,0x33,0x73,0x26,0xed,0x89,0x7c,0xd5,0x17,0xa4,0xf5,0x34,0x9d,0xb8,0x09,0x78,0x0f,0x6d,0x2f,0x2b,0x9f,0x62,0x99,0xd8,0xb5,0xa8,0x90,0x77,0xf1,0x11,0x9a,0x71,0x8f,0xd7,0xb3, + 0x04,0x7b,0x33,0x3d,0x43,0x40,0xd3,0xd7,0x18,0xdd,0x3e,0x6a,0xff,0x7d,0xe7,0xbb,0xf8,0xb7,0x2b,0xfd,0x61,0x6c,0x84,0x20,0x05,0x60,0x52,0x84,0x23,0x76,0xb9,0xaf,0x19,0x42,0x11,0x7c,0x5a,0xfe,0xac,0x75,0x5d,0x6f,0x37,0x6f,0xc6,0x32,0x9a,0x7d,0x76,0x05,0x1b,0x87,0x12,0x3a,0x4a,0x5d,0x0b,0xc4,0xa5,0x39,0x38,0x0f,0x03,0xde,0x7b, + 0x04,0xd3,0x0c,0xa4,0xa0,0xdd,0xb6,0x61,0x6c,0x85,0x1d,0x30,0xce,0xd6,0x82,0xc4,0x0f,0x83,0xc6,0x27,0x58,0xa1,0xf2,0x75,0x99,0x88,0xd6,0x76,0x3a,0x88,0xf1,0xc0,0xe5,0x03,0xa8,0x0d,0x54,0x15,0x65,0x0d,0x41,0x23,0x97,0x84,0xe8,0xe2,0xfb,0x12,0x35,0xe9,0xfe,0x99,0x1d,0x11,0x2e,0xbb,0x81,0x18,0x6c,0xbf,0x0d,0xa2,0xde,0x3a,0xff, + 0x04,0x48,0x96,0x9b,0x39,0x99,0x12,0x97,0xb3,0x32,0xa6,0x52,0xd3,0xee,0x6e,0x01,0xe9,0x09,0xb3,0x99,0x04,0xe7,0x1f,0xa2,0x35,0x4a,0x78,0x30,0xc7,0x75,0x0b,0xaf,0x24,0xb4,0x01,0x2d,0x1b,0x83,0x0d,0x19,0x9c,0xcb,0x1f,0xc9,0x72,0xb3,0x2b,0xfd,0xed,0x55,0xf0,0x9c,0xd6,0x2d,0x25,0x7e,0x5e,0x84,0x4e,0x27,0xe5,0x7a,0x15,0x94,0xec, + 0x04,0x02,0xef,0x4d,0x6d,0x6c,0xfd,0x5a,0x94,0xf1,0xd7,0x78,0x42,0x26,0xe3,0xe2,0xa6,0xc0,0xa4,0x36,0xc5,0x58,0x39,0x61,0x9f,0x38,0xfb,0x44,0x72,0xb5,0xf9,0xee,0x77,0x7e,0xb4,0xac,0xd4,0xee,0xbd,0xa5,0xcd,0x72,0x87,0x5f,0xfd,0x2a,0x2f,0x26,0x22,0x9c,0x2d,0xc6,0xb4,0x65,0x00,0x91,0x9a,0x43,0x2c,0x86,0x73,0x9f,0x3a,0xe8,0x66, + 0x04,0x46,0x4f,0x4f,0xf7,0x15,0x72,0x9c,0xae,0x50,0x72,0xca,0x3b,0xd8,0x01,0xd3,0x19,0x5b,0x67,0xae,0xc6,0x5e,0x9b,0x01,0xaa,0xd2,0x0a,0x29,0x43,0xdc,0xbc,0xb5,0x84,0xb1,0xaf,0xd2,0x9d,0x31,0xa3,0x9a,0x11,0xd5,0x70,0xaa,0x15,0x97,0x43,0x9b,0x3b,0x2d,0x19,0x71,0xbf,0x2f,0x1a,0xbf,0x15,0x43,0x2d,0x02,0x07,0xb1,0x0d,0x1d,0x08, + 0x04,0x15,0x7f,0x8f,0xdd,0xf3,0x73,0xeb,0x5f,0x49,0xcf,0xcf,0x10,0xd8,0xb8,0x53,0xcf,0x91,0xcb,0xcd,0x7d,0x66,0x5c,0x35,0x22,0xba,0x7d,0xd7,0x38,0xdd,0xb7,0x9a,0x4c,0xde,0xad,0xf1,0xa5,0xc4,0x48,0xea,0x3c,0x9f,0x41,0x91,0xa8,0x99,0x9a,0xbf,0xcc,0x75,0x7a,0xc6,0xd6,0x45,0x67,0xef,0x07,0x2c,0x47,0xfe,0xc6,0x13,0x44,0x3b,0x8f, + 0x04,0x09,0x34,0xa5,0x37,0x46,0x6c,0x07,0x43,0x0e,0x2c,0x48,0xfe,0xb9,0x90,0xbb,0x19,0xfb,0x78,0xce,0xcc,0x9c,0xee,0x42,0x4e,0xa4,0xd1,0x30,0x29,0x1a,0xa2,0x37,0xf0,0xd4,0xf9,0x2d,0x23,0xb4,0x62,0x80,0x4b,0x5b,0x68,0xc5,0x25,0x58,0xc0,0x1c,0x99,0x96,0xdb,0xf7,0x27,0xfc,0xca,0xbb,0xee,0xdb,0x96,0x21,0xa4,0x00,0x53,0x5a,0xfa, + 0x04,0xd6,0xef,0x20,0xbe,0x66,0xc8,0x93,0xf7,0x41,0xa9,0xbf,0x90,0xd9,0xb7,0x46,0x75,0xd1,0xc2,0xa3,0x12,0x96,0x39,0x7a,0xcb,0x3e,0xf1,0x74,0xfd,0x0b,0x30,0x0c,0x65,0x4a,0x0c,0x95,0x47,0x8c,0xa0,0x03,0x99,0x16,0x2d,0x7f,0x0f,0x2d,0xc8,0x9e,0xfd,0xc2,0xb2,0x8a,0x30,0xfb,0xab,0xe2,0x85,0x85,0x72,0x95,0xa4,0xb0,0xc4,0xe2,0x65, + 0x04,0xb7,0x29,0x1d,0x14,0x04,0xe0,0xc0,0xc0,0x7d,0xab,0x93,0x72,0x18,0x9f,0x4b,0xd5,0x8d,0x2c,0xea,0xa8,0xd1,0x5e,0xde,0x54,0x4d,0x95,0x14,0x54,0x5b,0xa9,0xee,0x06,0x29,0xc9,0xa6,0x3d,0x5e,0x30,0x87,0x69,0xcc,0x30,0xec,0x27,0x6a,0x41,0x0e,0x64,0x64,0xa2,0x7e,0xea,0xfd,0x9e,0x59,0x9d,0xb1,0x0f,0x05,0x3a,0x4f,0xe4,0xa8,0x29, + 0x04,0x6e,0x28,0x30,0x33,0x05,0xd6,0x42,0xcc,0xb9,0x23,0xb7,0x22,0xea,0x86,0xb2,0xa0,0xbc,0x8e,0x37,0x35,0xec,0xb2,0x6e,0x84,0x9b,0x19,0xc9,0xf7,0x6b,0x2f,0xdb,0xb8,0x18,0x6e,0x80,0xd6,0x4d,0x8c,0xab,0x16,0x4f,0x52,0x38,0xf5,0x31,0x84,0x61,0xbf,0x89,0xd4,0xd9,0x6e,0xe6,0x54,0x4c,0x81,0x6c,0x75,0x66,0x94,0x77,0x74,0xe0,0xf6, + 0x04,0x37,0x5b,0xda,0x93,0xf6,0xaf,0x92,0xfb,0x5f,0x8f,0x4b,0x1b,0x5f,0x05,0x34,0xe3,0xba,0xfa,0xb3,0x4c,0xb7,0xad,0x9f,0xb9,0xd0,0xb7,0x22,0xe4,0xa5,0xc3,0x02,0xa9,0xa0,0x0b,0x9f,0x38,0x7a,0x5a,0x39,0x60,0x97,0xaa,0x21,0x62,0xfc,0x5b,0xbc,0xf4,0xa5,0x26,0x33,0x72,0xf6,0x81,0xc9,0x4d,0xa5,0x1e,0x97,0x99,0x12,0x09,0x90,0xfd, + 0x04,0xd7,0x5b,0x68,0x21,0x6b,0xab,0xe0,0x3a,0xe2,0x57,0xe9,0x4b,0x4e,0x3b,0xf1,0xc5,0x2f,0x44,0xe3,0xdf,0x26,0x6d,0x15,0x24,0xff,0x8c,0x5e,0xa6,0x9d,0xa7,0x31,0x97,0xda,0x4b,0xff,0x9e,0xd1,0xc5,0x3f,0x44,0x91,0x7a,0x67,0xd7,0xb9,0x78,0x59,0x8e,0x89,0xdf,0x35,0x9e,0x3d,0x59,0x13,0xea,0xea,0x24,0xf3,0xae,0x25,0x9a,0xbc,0x44, + 0x04,0x78,0xbc,0xda,0x14,0x0a,0xed,0x23,0xd4,0x30,0xcb,0x23,0xc3,0xdc,0x0d,0x01,0xf4,0x23,0xdb,0x13,0x4e,0xe9,0x4a,0x3a,0x8c,0xb4,0x83,0xf2,0xde,0xac,0x2a,0xc6,0x53,0x11,0x81,0x14,0xf6,0xf3,0x30,0x45,0xd4,0xe9,0xed,0x91,0x07,0x08,0x50,0x07,0xbf,0xbd,0xdf,0x8f,0x58,0xfe,0x7a,0x1a,0x24,0x45,0xd6,0x6a,0x99,0x00,0x45,0x47,0x6e, + 0x04,0xbb,0x79,0xf6,0x18,0x57,0xf7,0x43,0xbf,0xa1,0xb6,0xe7,0x11,0x1c,0xe4,0x09,0x43,0x77,0x25,0x69,0x69,0xe4,0xe1,0x51,0x59,0x12,0x3d,0x95,0x48,0xac,0xc3,0xbe,0x6c,0x1f,0x9d,0x9f,0x88,0x60,0xdc,0xff,0xd3,0xeb,0x36,0xdd,0x6c,0x31,0xff,0x2e,0x72,0x26,0xc2,0x00,0x9c,0x4c,0x94,0xd8,0xd7,0xd2,0xb5,0x68,0x6b,0xf7,0xab,0xd6,0x77, + 0x04,0x93,0x59,0x18,0x27,0xd9,0xe6,0x71,0x3b,0x4e,0x9f,0xae,0xa6,0x2c,0x72,0xb2,0x8d,0xfe,0xfa,0x68,0xe0,0xc0,0x51,0x60,0xb5,0xd6,0xaa,0xe8,0x8f,0xd2,0xe3,0x6c,0x36,0x07,0x3f,0x55,0x45,0xad,0x5a,0xf4,0x10,0xaf,0x26,0xaf,0xff,0x68,0x65,0x4c,0xf7,0x2d,0x45,0xe4,0x93,0x48,0x93,0x11,0x20,0x32,0x47,0x34,0x7a,0x89,0x0f,0x45,0x18, + 0x04,0x31,0xed,0x30,0x81,0xae,0xfe,0x00,0x1e,0xb6,0x40,0x20,0x69,0xee,0x2c,0xcc,0x18,0x62,0x93,0x7b,0x85,0x99,0x51,0x44,0xdb,0xa9,0x50,0x39,0x43,0x58,0x7b,0xf0,0xda,0xda,0x01,0xb8,0xcc,0x4d,0xf3,0x4f,0x5a,0xb3,0xb1,0xa3,0x59,0x61,0x52,0x08,0x94,0x6e,0x5e,0xe3,0x5f,0x98,0xee,0x77,0x5b,0x8c,0xce,0xcd,0x86,0xcc,0xc1,0x65,0x0f, + 0x04,0x7d,0xff,0x66,0xfa,0x98,0x50,0x9f,0xf3,0xe2,0xe5,0x10,0x45,0xf4,0x39,0x05,0x23,0xdc,0xcd,0xa4,0x3a,0x3b,0xc2,0x88,0x5e,0x58,0xc2,0x48,0x09,0x09,0x90,0xee,0xa8,0x54,0xc7,0x6c,0x2b,0x9a,0xde,0xb6,0xbb,0x57,0x18,0x23,0xe0,0x7f,0xd7,0xc6,0x5c,0x86,0x39,0xcf,0x9d,0x90,0x52,0x60,0x06,0x4c,0x8e,0x76,0x75,0xce,0x6d,0x98,0xb4, + 0x04,0x42,0x80,0x50,0x9a,0xab,0x64,0xed,0xfc,0x0b,0x4a,0x29,0x67,0xe4,0xcb,0xce,0x84,0x9c,0xb5,0x44,0xe4,0xa7,0x73,0x13,0xc8,0xe6,0xec,0xe5,0x79,0xfb,0xd7,0x42,0x0a,0x2e,0x89,0xfe,0x5c,0xc1,0x92,0x7d,0x55,0x4e,0x6a,0x3b,0xb1,0x40,0x33,0xea,0x7c,0x92,0x2c,0xd7,0x5c,0xba,0x2c,0x74,0x15,0xfd,0xab,0x52,0xf2,0x0b,0x18,0x60,0xf1, + 0x04,0x4f,0x8d,0xf1,0x45,0x19,0x4e,0x3c,0x4f,0xc3,0xee,0xa2,0x6d,0x43,0xce,0x75,0xb4,0x02,0xd6,0xb1,0x74,0x72,0xdd,0xcb,0xb2,0x54,0xb8,0xa7,0x9b,0x0b,0xf3,0xd9,0xcb,0x2a,0xa2,0x0d,0x82,0x84,0x4c,0xb2,0x66,0x34,0x4e,0x71,0xca,0x78,0xf2,0xad,0x27,0xa7,0x5a,0x09,0xe5,0xbc,0x0f,0xa5,0x7e,0x4e,0xfd,0x9d,0x46,0x5a,0x08,0x88,0xdb, + 0x04,0x95,0x98,0xa5,0x7d,0xd6,0x7e,0xc3,0xe1,0x6b,0x58,0x7a,0x33,0x8a,0xa3,0xa1,0x0a,0x3a,0x39,0x13,0xb4,0x1a,0x3a,0xf3,0x2e,0x3e,0xd3,0xff,0x01,0x35,0x8c,0x6b,0x14,0x12,0x28,0x19,0xed,0xf8,0x07,0x4b,0xbc,0x52,0x1f,0x7d,0x4c,0xdc,0xe8,0x2f,0xef,0x7a,0x51,0x67,0x06,0xaf,0xfb,0xa1,0xd9,0x3d,0x9d,0xea,0x9c,0xca,0xe1,0xa2,0x07, + 0x04,0x91,0x71,0xfe,0xc3,0xca,0x20,0x80,0x6b,0xc0,0x84,0xf1,0x2f,0x07,0x60,0x91,0x1b,0x60,0x99,0x0b,0xd8,0x0e,0x5b,0x2a,0x71,0xca,0x03,0xa0,0x48,0xb2,0x0f,0x83,0x7e,0x63,0x4f,0xd1,0x78,0x63,0x76,0x1b,0x29,0x58,0xd2,0xbe,0x4e,0x14,0x9f,0x8d,0x3d,0x7a,0xbb,0xdc,0x18,0xbe,0x03,0xf4,0x51,0xab,0x6c,0x17,0xfa,0x0a,0x1f,0x83,0x30, + 0x04,0x77,0x7c,0x89,0x30,0xb6,0xe1,0xd2,0x71,0x10,0x0f,0xe6,0x8c,0xe9,0x3f,0x16,0x3f,0xa3,0x76,0x12,0xc5,0xff,0xf6,0x7f,0x4a,0x62,0xfc,0x3b,0xaf,0xaf,0x3d,0x17,0xa9,0xed,0x73,0xd8,0x6f,0x60,0xa5,0x1b,0x5e,0xd9,0x13,0x53,0xa3,0xb0,0x54,0xed,0xc0,0xaa,0x92,0xc9,0xeb,0xcb,0xd0,0xb7,0x5d,0x18,0x8f,0xdc,0x88,0x27,0x91,0xd6,0x8d, + 0x04,0xea,0xbc,0x24,0x8f,0x62,0x6e,0x0a,0x63,0xe1,0xeb,0x81,0xc4,0x3d,0x46,0x1a,0x39,0xa1,0xdb,0xa8,0x81,0xeb,0x6e,0xe2,0x15,0x2b,0x07,0xc3,0x2d,0x71,0xbc,0xf4,0x70,0x06,0x03,0xca,0xa8,0xb9,0xd3,0x3d,0xb1,0x3a,0xf4,0x4c,0x6e,0xfb,0xec,0x8a,0x19,0x8e,0xd6,0x12,0x4a,0xc9,0xeb,0x17,0xea,0xaf,0xd2,0x82,0x4a,0x54,0x5e,0xc0,0x00, + 0x04,0x9f,0x7a,0x13,0xad,0xa1,0x58,0xa5,0x5f,0x9d,0xdf,0x1a,0x45,0xf0,0x44,0xf0,0x73,0xd9,0xb8,0x00,0x30,0xef,0xdc,0xfc,0x9f,0x9f,0x58,0x41,0x8f,0xbc,0xea,0xf0,0x01,0xf8,0xad,0xa0,0x17,0x50,0x90,0xf8,0x0d,0x47,0x22,0x7d,0x67,0x13,0xb6,0x74,0x0f,0x9a,0x00,0x91,0xd8,0x8a,0x83,0x7d,0x0a,0x1c,0xd7,0x7b,0x58,0xa8,0xf2,0x8d,0x73, + 0x04,0x11,0xc4,0xf3,0xe4,0x61,0xcd,0x01,0x9b,0x5c,0x06,0xea,0x0c,0xea,0x4c,0x40,0x90,0xc3,0xcc,0x3e,0x3c,0x5d,0x9f,0x3c,0x6d,0x65,0xb4,0x36,0x82,0x6d,0xa9,0xb4,0xdb,0xbb,0xeb,0x7a,0x77,0xe4,0xcb,0xfd,0xa2,0x07,0x09,0x7c,0x43,0x42,0x37,0x05,0xf7,0x2c,0x80,0x47,0x6d,0xa3,0xda,0xc4,0x0a,0x48,0x3b,0x0a,0xb0,0xf2,0xea,0xd1,0xcb, + 0x04,0xe2,0xe1,0x86,0x82,0xd5,0x31,0x23,0xaa,0x01,0xa6,0xc5,0xd0,0x0b,0x0c,0x62,0x3d,0x67,0x1b,0x46,0x2e,0xa8,0x0b,0xdd,0xd6,0x52,0x27,0xfd,0x51,0x05,0x98,0x8a,0xa4,0x16,0x19,0x07,0xb3,0xfd,0x25,0x04,0x4a,0x94,0x9e,0xa4,0x1c,0x8e,0x2e,0xa8,0x45,0x9d,0xc6,0xf1,0x65,0x48,0x56,0xb8,0xb6,0x1b,0x31,0x54,0x3b,0xb1,0xb4,0x5b,0xdb, + 0x04,0x90,0xf8,0xd4,0xca,0x73,0xde,0x08,0xa6,0x56,0x4a,0xaf,0x00,0x52,0x47,0xb6,0xf0,0xff,0xe9,0x78,0x50,0x4d,0xce,0x52,0x60,0x5f,0x46,0xb7,0xc3,0xe5,0x61,0x97,0xda,0xfa,0xdb,0xe5,0x28,0xeb,0x70,0xd9,0xee,0x7e,0xa0,0xe7,0x07,0x02,0xdb,0x54,0xf7,0x21,0x51,0x4c,0x7b,0x86,0x04,0xac,0x2c,0xb2,0x14,0xf1,0xde,0xcb,0x7e,0x38,0x3d, + 0x04,0x82,0x4c,0x19,0x5c,0x73,0xcf,0xfd,0xf0,0x38,0xd1,0x01,0xbc,0xe1,0x68,0x7b,0x5c,0x3b,0x61,0x46,0xf3,0x95,0xc8,0x85,0x97,0x6f,0x77,0x53,0xb2,0x37,0x6b,0x94,0x8e,0x3c,0xde,0xfa,0x6f,0xc3,0x47,0xd1,0x3e,0x4d,0xcb,0xc6,0x3a,0x0b,0x03,0xa1,0x65,0x18,0x0c,0xd2,0xbe,0x14,0x31,0xa0,0xcf,0x74,0xce,0x1e,0xa2,0x50,0x82,0xd2,0xbc, + 0x04,0x27,0x88,0xa5,0x2f,0x07,0x8e,0xb3,0xf2,0x02,0xc4,0xfa,0x73,0xe0,0xd3,0x38,0x6f,0xaf,0x3d,0xf6,0xbe,0x85,0x60,0x03,0x63,0x6f,0x59,0x99,0x22,0xd4,0xf5,0x26,0x8f,0x30,0xb4,0xf2,0x07,0xc9,0x19,0xbb,0xdf,0x5e,0x67,0xa8,0xbe,0x42,0x65,0xa8,0x17,0x47,0x54,0xb3,0xab,0xa8,0xf1,0x6e,0x57,0x5b,0x77,0xff,0x4d,0x5a,0x7e,0xb6,0x4f, + 0x04,0xd5,0x33,0xb7,0x89,0xa4,0xaf,0x89,0x0f,0xa7,0xa8,0x2a,0x1f,0xae,0x58,0xc4,0x04,0xf9,0xa6,0x2a,0x50,0xb4,0x9a,0xda,0xfa,0xb3,0x49,0xc5,0x13,0xb4,0x15,0x08,0x74,0x01,0xb4,0x17,0x1b,0x80,0x3e,0x76,0xb3,0x4a,0x98,0x61,0xe1,0x0f,0x7b,0xc2,0x89,0xa0,0x66,0xfd,0x01,0xbd,0x29,0xf8,0x4c,0x98,0x7a,0x10,0xa5,0xfb,0x18,0xc2,0xd4, + 0x04,0x3a,0x31,0x50,0x79,0x8c,0x8a,0xf6,0x9d,0x1e,0x6e,0x98,0x1f,0x3a,0x45,0x40,0x2b,0xa1,0xd7,0x32,0xf4,0xbe,0x83,0x30,0xc5,0x16,0x4f,0x49,0xe1,0x0e,0xc5,0x55,0xb4,0x22,0x1b,0xd8,0x42,0xbc,0x5e,0x4d,0x97,0xef,0xf3,0x71,0x65,0xf6,0x0e,0x39,0x98,0xa4,0x24,0xd7,0x2a,0x45,0x0c,0xf9,0x5e,0xa4,0x77,0xc7,0x82,0x87,0xd0,0x34,0x3a, + 0x04,0x3b,0x37,0xdf,0x5f,0xb3,0x47,0xc6,0x9a,0x0f,0x17,0xd8,0x5c,0x0c,0x7c,0xa8,0x37,0x36,0x88,0x3a,0x82,0x5e,0x13,0x14,0x3d,0x0f,0xcf,0xc8,0x10,0x1e,0x85,0x1e,0x80,0x0d,0xe3,0xc0,0x90,0xb6,0xca,0x21,0xba,0x54,0x35,0x17,0x33,0x0c,0x04,0xb1,0x2f,0x94,0x8c,0x6b,0xad,0xf1,0x4a,0x63,0xab,0xff,0xdf,0x4e,0xf8,0xc7,0x53,0x70,0x26, + 0x04,0xfe,0xb5,0x16,0x3b,0x0e,0xce,0x30,0xff,0x3e,0x03,0xc7,0xd5,0x5c,0x43,0x80,0xfa,0x2f,0xa8,0x1e,0xe2,0xc0,0x35,0x49,0x42,0xff,0x6f,0x08,0xc9,0x9d,0x0c,0xd8,0x2c,0xe8,0x7d,0xe0,0x5e,0xe1,0xbd,0xa0,0x89,0xd3,0xe4,0xe2,0x48,0xfa,0x0f,0x72,0x11,0x02,0xac,0xff,0xfd,0xf5,0x0e,0x65,0x4b,0xe2,0x81,0x43,0x39,0x99,0xdf,0x89,0x7e, + 0x04,0x23,0x8c,0xed,0x00,0x1c,0xf2,0x2b,0x88,0x53,0xe0,0x2e,0xdc,0x89,0xcb,0xec,0xa5,0x05,0x0b,0xa7,0xe0,0x42,0xa7,0xa7,0x7f,0x93,0x82,0xcd,0x41,0x49,0x22,0x89,0x76,0x40,0x68,0x3d,0x30,0x94,0x64,0x38,0x40,0xf2,0x95,0x89,0x0a,0xa4,0xc1,0x8a,0xa3,0x9b,0x41,0xd7,0x7d,0xd0,0xfb,0x3b,0xb2,0x70,0x0e,0x4f,0x9e,0xc2,0x84,0xff,0xc2, + 0x04,0x96,0x1c,0xf6,0x48,0x17,0xc0,0x6c,0x0e,0x51,0xb3,0xc2,0x73,0x6c,0x92,0x2f,0xde,0x18,0xbd,0x8c,0x49,0x06,0xfc,0xd7,0xf5,0xef,0x66,0xc4,0x67,0x85,0x08,0xf3,0x5e,0xd2,0xc5,0xd1,0x81,0x68,0xcf,0xbe,0x70,0xf2,0xf1,0x23,0xbd,0x74,0x19,0x23,0x2b,0xb9,0x2d,0xd6,0x91,0x13,0xe2,0x94,0x10,0x61,0x88,0x94,0x81,0xc5,0xa0,0x27,0xbf, + 0x04,0x13,0x68,0x1e,0xae,0x16,0x8c,0xd4,0xea,0x7c,0xf2,0xe2,0xa4,0x5d,0x05,0x27,0x42,0xd1,0x0a,0x9f,0x64,0xe7,0x96,0x86,0x7d,0xbd,0xcb,0x82,0x9f,0xe0,0xb1,0x02,0x88,0x16,0x52,0x87,0x60,0xd1,0x77,0x37,0x6c,0x09,0xdf,0x79,0xde,0x39,0x55,0x7c,0x32,0x9c,0xc1,0x75,0x35,0x17,0xac,0xff,0xe8,0xfa,0x2e,0xc2,0x98,0x02,0x6b,0x83,0x84, + 0x04,0x5a,0xa7,0xab,0xfd,0xb6,0xb4,0x08,0x6d,0x54,0x33,0x25,0xe5,0xd7,0x9c,0x6e,0x95,0xce,0x42,0xf8,0x66,0xd2,0xbb,0x84,0x90,0x96,0x33,0xa0,0x4b,0xb1,0xaa,0x31,0xc2,0x91,0xc8,0x00,0x88,0x79,0x49,0x05,0xe1,0xda,0x33,0x33,0x6d,0x87,0x4e,0x2f,0x91,0xcc,0xf4,0x5c,0xc5,0x91,0x85,0xbe,0xde,0x5d,0xd6,0xf3,0xf7,0xac,0xaa,0xe1,0x8b, + 0x04,0x00,0x27,0x77,0x91,0xb3,0x05,0xa4,0x5b,0x2b,0x39,0x59,0x0b,0x2f,0x05,0xd3,0x39,0x2a,0x6c,0x81,0x82,0xce,0xf4,0xeb,0x54,0x01,0x20,0xe0,0xf5,0xc2,0x06,0xc3,0xe4,0x64,0x10,0x82,0x33,0xfb,0x0b,0x8c,0x3a,0xc8,0x92,0xd7,0x9e,0xf8,0xe0,0xfb,0xf9,0x2e,0xd1,0x33,0xad,0xdb,0x45,0x54,0x27,0x01,0x32,0x58,0x4d,0xc5,0x2e,0xef,0x41, + 0x04,0x6e,0xfa,0x09,0x2b,0x68,0xde,0x94,0x60,0xf0,0xbc,0xc9,0x19,0x00,0x5a,0x5f,0x6e,0x80,0xe1,0x9d,0xe9,0x89,0x68,0xbe,0x3c,0xd2,0xc7,0x70,0xa9,0x94,0x9b,0xfb,0x1a,0xc7,0x5e,0x6e,0x50,0x87,0xd6,0x55,0x0d,0x5f,0x9b,0xeb,0x1e,0x79,0xe5,0x02,0x93,0x07,0xbc,0x25,0x52,0x35,0xe2,0xd5,0xdc,0x99,0x24,0x1a,0xc3,0xab,0x88,0x6c,0x49, + 0x04,0x72,0xd4,0xa1,0x9c,0x4f,0x9d,0x2c,0xf5,0x84,0x8e,0xa4,0x04,0x45,0xb7,0x0d,0x46,0x96,0xb5,0xf0,0x2d,0x63,0x2c,0x0c,0x65,0x4c,0xc7,0xd7,0xee,0xb0,0xc6,0xd0,0x58,0xe8,0xc4,0xcd,0x99,0x43,0xe4,0x59,0x17,0x4c,0x7a,0xc0,0x1f,0xa7,0x42,0x19,0x8e,0x47,0xe6,0xc1,0x9a,0x6b,0xdb,0x0c,0x4f,0x6c,0x23,0x78,0x31,0xc1,0xb3,0xf9,0x42, + 0x04,0x2a,0x8e,0xa2,0xf5,0x0d,0xcc,0xed,0x0c,0x21,0x75,0x75,0xbd,0xfa,0x7c,0xd4,0x7d,0x1c,0x6f,0x10,0x00,0x41,0xec,0x0e,0x35,0x51,0x27,0x94,0xc1,0xbe,0x7e,0x74,0x02,0x58,0xf8,0xc1,0x71,0x22,0xed,0x30,0x3f,0xda,0x71,0x43,0xeb,0x58,0xbe,0xde,0x70,0x29,0x5b,0x65,0x32,0x66,0x01,0x3b,0x0b,0x0e,0xbd,0x3f,0x05,0x31,0x37,0xf6,0xec, + 0x04,0x88,0xde,0x68,0x9c,0xe9,0xaf,0x1e,0x94,0xbe,0x6a,0x20,0x89,0xc8,0xa8,0xb1,0x25,0x3f,0xfd,0xbb,0x6c,0x8e,0x9c,0x86,0x24,0x9b,0xa2,0x20,0x00,0x1a,0x4a,0xd3,0xb8,0x0c,0x49,0x98,0xe5,0x48,0x42,0xf4,0x13,0xb9,0xed,0xb1,0x82,0x5a,0xcb,0xb6,0x33,0x5e,0x81,0xe4,0xd1,0x84,0xb2,0xb0,0x1c,0x8b,0xeb,0xdc,0x85,0xd1,0xf2,0x89,0x46, + 0x04,0xfe,0xa2,0xd3,0x1f,0x70,0xf9,0x0d,0x5f,0xb3,0xe0,0x0e,0x18,0x6a,0xc4,0x2a,0xb3,0xc1,0x61,0x5c,0xee,0x71,0x4e,0x0b,0x4e,0x11,0x31,0xb3,0xd4,0xd8,0x22,0x5b,0xf7,0xb0,0x37,0xa1,0x8d,0xf2,0xac,0x15,0x34,0x3f,0x30,0xf7,0x40,0x67,0xdd,0xf2,0x9e,0x81,0x7d,0x5f,0x77,0xf8,0xdc,0xe0,0x57,0x14,0xda,0x59,0xc0,0x94,0xf0,0xcd,0xa9, + 0x04,0x72,0x58,0x91,0x1e,0x3d,0x42,0x33,0x49,0x16,0x64,0x79,0xdb,0xe0,0xb8,0x34,0x1a,0xf7,0xfb,0xd0,0x3d,0x0a,0x7e,0x10,0xed,0xcc,0xb3,0x6b,0x6c,0xee,0xa5,0xa3,0xdb,0x17,0xac,0x2b,0x89,0x92,0x79,0x11,0x28,0xfa,0x3b,0x96,0xdc,0x2f,0xbd,0x4c,0xa3,0xbf,0xa7,0x82,0xef,0x28,0x32,0xfc,0x66,0x56,0x94,0x3d,0xb1,0x8e,0x73,0x46,0xb0, + 0x04,0x4f,0x28,0x46,0x1d,0xea,0x64,0x47,0x4d,0x6b,0xb3,0x4d,0x14,0x99,0xc9,0x7d,0x37,0xb9,0xe9,0x56,0x33,0xdf,0x1c,0xee,0xea,0xac,0xd4,0x50,0x16,0xc9,0x8b,0x39,0x14,0xc8,0x81,0x88,0x10,0xb8,0xcc,0x06,0xdd,0xb4,0x0e,0x8a,0x12,0x61,0xc5,0x28,0xfa,0xa5,0x89,0x45,0x5d,0x5a,0x6d,0xf9,0x3b,0x77,0xbc,0x5e,0x0e,0x49,0x3c,0x74,0x70, + 0x04,0x74,0xf2,0xa8,0x14,0xfb,0x5d,0x8e,0xca,0x91,0xa6,0x9b,0x5e,0x60,0x71,0x27,0x32,0xb3,0x93,0x7d,0xe3,0x28,0x29,0xbe,0x97,0x4e,0xd7,0xb6,0x8c,0x5c,0x2f,0x5d,0x66,0xef,0xf0,0xf0,0x7c,0x56,0xf9,0x87,0xa6,0x57,0xf4,0x21,0x96,0x20,0x5f,0x58,0x8c,0x0f,0x1d,0x96,0xfd,0x8a,0x63,0xa5,0xf2,0x38,0xb4,0x8f,0x47,0x87,0x88,0xfe,0x3b, + 0x04,0x19,0x5b,0x51,0xa7,0xcc,0x4a,0x21,0xb8,0x27,0x4a,0x70,0xa9,0x0d,0xe7,0x79,0x81,0x4c,0x3c,0x8c,0xa3,0x58,0x32,0x82,0x08,0xc0,0x9a,0x29,0xf3,0x36,0xb8,0x2d,0x6a,0xb2,0x41,0x6b,0x7c,0x92,0xff,0xfd,0xc2,0x9c,0x3b,0x12,0x82,0xdd,0x2a,0x77,0xa4,0xd0,0x4d,0xf7,0xf7,0x45,0x20,0x47,0x39,0x3d,0x84,0x99,0x89,0xc5,0xce,0xe9,0xad, + 0x04,0x62,0x2f,0xc7,0x47,0x32,0x03,0x4b,0xec,0x2d,0xdf,0x3b,0xc1,0x6d,0x34,0xb3,0xd1,0xf7,0xa3,0x27,0xdd,0x2a,0x8c,0x19,0xba,0xb4,0xbb,0x4f,0xe3,0xa2,0x4b,0x58,0xaa,0x73,0x6b,0x2f,0x2f,0xae,0x76,0xf4,0xdf,0xae,0xcc,0x90,0x96,0x33,0x3b,0x01,0x32,0x8d,0x51,0xeb,0x3f,0xda,0x9c,0x92,0x27,0xe9,0x0d,0x0b,0x44,0x99,0x83,0xc4,0xf0, + 0x04,0x1f,0x7f,0x85,0xca,0xf2,0xd7,0x55,0x0e,0x7a,0xf9,0xb6,0x50,0x23,0xeb,0xb4,0xdc,0xe3,0x45,0x03,0x11,0x69,0x23,0x09,0xdb,0x26,0x99,0x69,0xb8,0x34,0xb6,0x11,0xc7,0x08,0x27,0xf4,0x5b,0x78,0x02,0x0e,0xcb,0xba,0xf4,0x84,0xfd,0xd5,0xbf,0xaa,0xe6,0x87,0x0f,0x11,0x84,0xc2,0x15,0x81,0xba,0xf6,0xef,0x82,0xbd,0x7b,0x53,0x0f,0x93, + 0x04,0x49,0xc1,0x97,0xdc,0x80,0xad,0x1d,0xa4,0x7a,0x43,0x42,0xb9,0x38,0x93,0xe8,0xe1,0xfb,0x0b,0xb9,0x4f,0xc3,0x3a,0x83,0xe7,0x83,0xc0,0x0b,0x24,0xc7,0x81,0x37,0x7a,0xef,0xc2,0x0d,0xa9,0x2b,0xac,0x76,0x29,0x51,0xf7,0x24,0x74,0xbe,0xcc,0x73,0x4d,0x4c,0xc2,0x2b,0xa8,0x1b,0x89,0x5e,0x28,0x2f,0xda,0xc4,0xdf,0x7a,0xf0,0xf3,0x7d, + 0x04,0xd8,0xcb,0x68,0x51,0x7b,0x61,0x6a,0x56,0x40,0x0a,0xa3,0x86,0x86,0x35,0xe5,0x4b,0x6f,0x69,0x95,0x98,0xa2,0xf6,0x16,0x77,0x57,0x65,0x49,0x80,0xba,0xf6,0xac,0xbe,0x7e,0xc8,0xcf,0x44,0x9c,0x84,0x9a,0xa0,0x34,0x61,0xa3,0x0e,0xfa,0xda,0x41,0x45,0x3c,0x57,0xc6,0xe6,0xfb,0xc9,0x3b,0xbc,0x6f,0xa4,0x9a,0xda,0x6d,0xc0,0x55,0x5c, + 0x04,0x03,0x07,0x13,0xfb,0x63,0xf2,0xaa,0x6f,0xe2,0xca,0xdf,0x1b,0x20,0xef,0xc2,0x59,0xc7,0x74,0x45,0xda,0xfa,0x87,0xda,0xc3,0x98,0xb8,0x40,0x65,0xca,0x34,0x7d,0xf3,0xb2,0x27,0x81,0x8d,0xe1,0xa3,0x9b,0x58,0x9c,0xb0,0x71,0xd8,0x3e,0x53,0x17,0xcc,0xcd,0xc2,0x33,0x8e,0x51,0xe3,0x12,0xfe,0x31,0xd8,0xdc,0x34,0xa4,0x80,0x17,0x50, + 0x04,0xba,0xbb,0x36,0x77,0xb0,0x95,0x58,0x02,0xd8,0xe9,0x29,0xa4,0x13,0x55,0x64,0x0e,0xaf,0x1e,0xa1,0x35,0x3f,0x8a,0x77,0x13,0x31,0xc4,0x94,0x6e,0x34,0x80,0xaf,0xa7,0x25,0x2f,0x19,0x6c,0x87,0xed,0x3d,0x2a,0x59,0xd3,0xb1,0xb5,0x59,0x13,0x7f,0xed,0x00,0x13,0xfe,0xce,0xfc,0x19,0xfb,0x5a,0x92,0x68,0x2b,0x9b,0xca,0x51,0xb9,0x50, + 0x04,0x1a,0xab,0x20,0x18,0x79,0x34,0x71,0x11,0x1a,0x8a,0x0e,0x9b,0x14,0x3f,0xde,0x02,0xfc,0x95,0x92,0x07,0x96,0xd3,0xa6,0x3d,0xe3,0x29,0xb4,0x24,0x39,0x6f,0xba,0x60,0xbb,0xe4,0x13,0x07,0x05,0x17,0x47,0x92,0x44,0x1b,0x31,0x8d,0x3a,0xa3,0x1d,0xfe,0x85,0x77,0x82,0x1e,0x9b,0x44,0x6e,0xc5,0x73,0xd2,0x72,0xe0,0x36,0xc4,0xeb,0xe9, + 0x04,0x8c,0xb0,0xb9,0x09,0x49,0x9c,0x83,0xea,0x80,0x6c,0xd8,0x85,0xb1,0xdd,0x46,0x7a,0x01,0x19,0xf0,0x6a,0x88,0xa0,0x27,0x6e,0xb0,0xcf,0xda,0x27,0x45,0x35,0xa8,0xff,0x47,0xb5,0x42,0x88,0x33,0xbc,0x3f,0x2c,0x8b,0xf9,0xd9,0x04,0x11,0x58,0xcf,0x33,0x71,0x8a,0x69,0x96,0x1c,0xd0,0x17,0x29,0xbc,0x00,0x11,0xd1,0xe5,0x86,0xab,0x75, + 0x04,0x8f,0x03,0xcf,0x1a,0x42,0x27,0x2b,0xb1,0x53,0x27,0x23,0x09,0x3f,0x72,0xe6,0xfe,0xea,0xc8,0x5e,0x17,0x00,0xe9,0xfb,0xe9,0xa6,0xa2,0xdd,0x64,0x2d,0x74,0xbf,0x5d,0x3b,0x89,0xa7,0x18,0x9d,0xad,0x8c,0xf7,0x5f,0xc2,0x2f,0x6f,0x15,0x8a,0xa2,0x7f,0x9c,0x2c,0xa0,0x0d,0xac,0xa7,0x85,0xbe,0x33,0x58,0xf2,0xbd,0xa3,0x86,0x2c,0xa0, + 0x04,0x44,0xde,0x3b,0x9c,0x7a,0x57,0xa8,0xc9,0xe8,0x20,0x95,0x27,0x53,0x42,0x1e,0x7d,0x98,0x7b,0xb3,0xd7,0x9f,0x71,0xf0,0x13,0x80,0x5c,0x89,0x7e,0x01,0x8f,0x8a,0xce,0xa2,0x46,0x07,0x58,0xc8,0xf9,0x8d,0x3f,0xdc,0xe1,0x21,0xa9,0x43,0x65,0x9e,0x37,0x2c,0x32,0x6f,0xff,0x2e,0x5f,0xc2,0xae,0x7f,0xa3,0xf7,0x9d,0xaa,0xe1,0x3c,0x12, + 0x04,0x6f,0xb8,0xb2,0xb4,0x8e,0x33,0x03,0x12,0x68,0xad,0x6a,0x51,0x74,0x84,0xdc,0x88,0x39,0xea,0x90,0xf6,0x66,0x9e,0xa0,0xc7,0xac,0x32,0x33,0xe2,0xac,0x31,0x39,0x4a,0x0a,0xc8,0xbb,0xe7,0xf7,0x3c,0x2f,0xf4,0xdf,0x99,0x78,0x72,0x7a,0xc1,0xdf,0xc2,0xfd,0x58,0x64,0x7d,0x20,0xf3,0x1f,0x99,0x10,0x53,0x16,0xb6,0x46,0x71,0xf2,0x04, + 0x04,0xbe,0xa7,0x11,0x22,0xa0,0x48,0x69,0x3e,0x90,0x5f,0xf6,0x02,0xb3,0xcf,0x9d,0xd1,0x8a,0xf6,0x9b,0x9f,0xc9,0xd8,0x43,0x1d,0x2b,0x1d,0xd2,0x6b,0x94,0x2c,0x95,0xe6,0xf4,0x3c,0x7b,0x8b,0x95,0xeb,0x62,0x08,0x2c,0x12,0xdb,0x9d,0xbd,0xa7,0xfe,0x38,0xe4,0x5c,0xbe,0x4a,0x48,0x86,0x90,0x7f,0xb8,0x1b,0xdb,0x0c,0x5e,0xa9,0x24,0x6c, + 0x04,0xda,0x91,0x8c,0x73,0x1b,0xa0,0x6a,0x20,0xcb,0x94,0xef,0x33,0xb7,0x78,0xe9,0x81,0xa4,0x04,0xa3,0x05,0xf1,0x94,0x1f,0xe3,0x36,0x66,0xb4,0x5b,0x03,0x35,0x31,0x56,0xe2,0xbb,0x26,0x94,0xf5,0x75,0xb4,0x51,0x83,0xbe,0x78,0xe5,0xc9,0xb5,0x21,0x0b,0xf3,0xbf,0x48,0x8f,0xd4,0xc8,0x29,0x45,0x16,0xd8,0x95,0x72,0xca,0x4f,0x53,0x91, + 0x04,0x30,0x07,0xe9,0x2c,0x39,0x37,0xda,0xde,0x79,0x64,0xdf,0xa3,0x5b,0x0e,0xff,0x03,0x1f,0x7e,0xb0,0x2a,0xed,0x0a,0x03,0x14,0x41,0x11,0x06,0xcd,0xeb,0x70,0xfe,0x3d,0x5a,0x75,0x46,0xfc,0x05,0x52,0x99,0x7b,0x20,0xe3,0xd6,0xf4,0x13,0xe7,0x5e,0x2c,0xb6,0x6e,0x11,0x63,0x22,0x69,0x71,0x14,0xb7,0x9b,0xac,0x73,0x4b,0xfc,0x4d,0xc5, + 0x04,0x60,0xe7,0x34,0xef,0x56,0x24,0xd3,0xcb,0xf0,0xdd,0xd3,0x75,0x01,0x1b,0xd6,0x63,0xd6,0xd6,0xae,0xbc,0x64,0x4e,0xb5,0x99,0xfd,0xf9,0x8d,0xbd,0xcd,0x18,0xce,0x9b,0xd2,0xd9,0x0b,0x3a,0xc3,0x1f,0x13,0x9a,0xf8,0x32,0xcc,0xcf,0x6c,0xcb,0xbb,0x2c,0x6e,0xa1,0x1f,0xa9,0x73,0x70,0xdc,0x99,0x06,0xda,0x47,0x4d,0x7d,0x8a,0x75,0x67, + 0x04,0x85,0xa9,0x00,0xe9,0x78,0x58,0xf6,0x93,0xc0,0xb7,0xdf,0xa2,0x61,0xe3,0x80,0xda,0xd6,0xea,0x04,0x6d,0x1f,0x65,0xdd,0xee,0xed,0xd5,0xf7,0xd8,0xaf,0x0b,0xa3,0x37,0x69,0x74,0x4d,0x15,0xad,0xd4,0xf6,0xc0,0xbc,0x3b,0x0d,0xa2,0xae,0xc9,0x3b,0x34,0xcb,0x8c,0x65,0xf9,0x34,0x0d,0xdf,0x74,0xe7,0xb0,0x00,0x9e,0xee,0xcc,0xce,0x3c, + 0x04,0x38,0x06,0x6f,0x75,0xd8,0x8e,0xfc,0x4c,0x93,0xde,0x36,0xf4,0x9e,0x03,0x7b,0x23,0x4c,0xc1,0x8b,0x1d,0xe5,0x60,0x87,0x50,0xa6,0x2c,0xab,0x03,0x45,0x40,0x10,0x46,0xa3,0xe8,0x4b,0xed,0x8c,0xfc,0xb8,0x19,0xef,0x4d,0x55,0x04,0x44,0xf2,0xce,0x4b,0x65,0x17,0x66,0xb6,0x9e,0x2e,0x29,0x01,0xf8,0x88,0x36,0xff,0x90,0x03,0x4f,0xed, + 0x04,0x98,0xf6,0x81,0x77,0xdc,0x95,0xc1,0xb4,0xcb,0xfa,0x52,0x45,0x48,0x8c,0xa5,0x23,0xa7,0xd5,0x62,0x94,0x70,0xd0,0x35,0xd6,0x21,0xa4,0x43,0xc7,0x2f,0x39,0xaa,0xbf,0xa3,0x3d,0x29,0x54,0x6f,0xa1,0xc6,0x48,0xf2,0xc7,0xd5,0xcc,0xf7,0x0c,0xf1,0xce,0x4a,0xb7,0x9b,0x5d,0xb1,0xac,0x05,0x9d,0xbe,0xcd,0x06,0x8d,0xbd,0xff,0x1b,0x89, + 0x04,0x5c,0x2b,0xbf,0xa2,0x3c,0x9b,0x9a,0xd0,0x7f,0x03,0x8a,0xa8,0x9b,0x49,0x30,0xbf,0x26,0x7d,0x94,0x01,0xe4,0x25,0x5d,0xe9,0xe8,0xda,0x0a,0x50,0x78,0xec,0x82,0x77,0xe3,0xe8,0x82,0xa3,0x1d,0x5e,0x6a,0x37,0x9e,0x07,0x93,0x98,0x3c,0xcd,0xed,0x39,0xb9,0x5c,0x43,0x53,0xab,0x2f,0xf0,0x1e,0xa5,0x36,0x9b,0xa4,0x7b,0x0c,0x31,0x91, + 0x04,0x2e,0xa7,0x13,0x34,0x32,0x33,0x9c,0x69,0xd2,0x7f,0x9b,0x26,0x72,0x81,0xbd,0x2d,0xdd,0x5f,0x19,0xd6,0x33,0x8d,0x40,0x0a,0x05,0xcd,0x36,0x47,0xb1,0x57,0xa3,0x85,0x35,0x47,0x80,0x82,0x98,0x44,0x8e,0xdb,0x5e,0x70,0x1a,0xde,0x84,0xcd,0x5f,0xb1,0xac,0x95,0x67,0xba,0x5e,0x8f,0xb6,0x8a,0x6b,0x93,0x3e,0xc4,0xb5,0xcc,0x84,0xcc, + 0x04,0x2e,0xa7,0x13,0x34,0x32,0x33,0x9c,0x69,0xd2,0x7f,0x9b,0x26,0x72,0x81,0xbd,0x2d,0xdd,0x5f,0x19,0xd6,0x33,0x8d,0x40,0x0a,0x05,0xcd,0x36,0x47,0xb1,0x57,0xa3,0x85,0xca,0xb8,0x7f,0x7d,0x67,0xbb,0x71,0x24,0xa1,0x8f,0xe5,0x21,0x7b,0x32,0xa0,0x4e,0x53,0x6a,0x98,0x45,0xa1,0x70,0x49,0x75,0x94,0x6c,0xc1,0x3a,0x4a,0x33,0x77,0x63, + 0x04,0x8a,0xa2,0xc6,0x4f,0xa9,0xc6,0x43,0x75,0x63,0xab,0xfb,0xcb,0xd0,0x0b,0x20,0x48,0xd4,0x8c,0x18,0xc1,0x52,0xa2,0xa6,0xf4,0x90,0x36,0xde,0x76,0x47,0xeb,0xe8,0x2e,0x1c,0xe6,0x43,0x87,0x99,0x5c,0x68,0xa0,0x60,0xfa,0x3b,0xc0,0x39,0x9b,0x05,0xcc,0x06,0xee,0xc7,0xd5,0x98,0xf7,0x50,0x41,0xa4,0x91,0x7e,0x69,0x2b,0x7f,0x51,0xff, + 0x04,0x39,0x14,0x27,0xff,0x7e,0xe7,0x80,0x13,0xc1,0x4a,0xec,0x7d,0x96,0xa8,0xa0,0x62,0x20,0x92,0x98,0xa7,0x83,0x83,0x5e,0x94,0xfd,0x65,0x49,0xd5,0x02,0xff,0xf7,0x1f,0xdd,0x66,0x24,0xec,0x34,0x3a,0xd9,0xfc,0xf4,0xd9,0x87,0x21,0x81,0xe5,0x9f,0x84,0x2f,0x9b,0xa4,0xcc,0xca,0xe0,0x9a,0x6c,0x09,0x72,0xfb,0x6a,0xc6,0xb4,0xc6,0xbd, + 0x04,0xe7,0x62,0xb8,0xa2,0x19,0xb4,0xf1,0x80,0x21,0x9c,0xc7,0xa9,0x05,0x92,0x45,0xe4,0x96,0x1b,0xd1,0x91,0xc0,0x38,0x99,0x78,0x9c,0x7a,0x34,0xb8,0x9e,0x8c,0x13,0x8e,0xc1,0x53,0x3e,0xf0,0x41,0x9b,0xb7,0x37,0x6e,0x0b,0xfd,0xe9,0x31,0x9d,0x10,0xa0,0x69,0x68,0x79,0x1d,0x9e,0xa0,0xee,0xd9,0xc1,0xce,0x63,0x45,0xae,0xd9,0x75,0x9e, + 0x04,0x9a,0xed,0xb0,0xd2,0x81,0xdb,0x16,0x4e,0x13,0x00,0x00,0xc5,0x69,0x7f,0xae,0x0f,0x30,0x5e,0xf8,0x48,0xbe,0x6f,0xff,0xb4,0x3a,0xc5,0x93,0xfb,0xb9,0x50,0xe9,0x52,0xfa,0x6f,0x63,0x33,0x59,0xbd,0xcd,0x82,0xb5,0x6b,0x0b,0x9f,0x96,0x5b,0x03,0x77,0x89,0xd4,0x6b,0x9a,0x81,0x41,0xb7,0x91,0xb2,0xae,0xfa,0x71,0x3f,0x96,0xc1,0x75, + 0x04,0x8a,0xd4,0x45,0xdb,0x62,0x81,0x62,0x60,0xe4,0xe6,0x87,0xfd,0x18,0x84,0xe4,0x8b,0x9f,0xc0,0x63,0x6d,0x03,0x15,0x47,0xd6,0x33,0x15,0xe7,0x92,0xe1,0x9b,0xfa,0xee,0x1d,0xe6,0x4f,0x99,0xd5,0xf1,0xcd,0x8b,0x6e,0xc9,0xcb,0x0f,0x78,0x7a,0x65,0x4a,0xe8,0x69,0x93,0xba,0x3d,0xb1,0x00,0x8e,0xf4,0x3c,0xff,0x06,0x84,0xcb,0x22,0xbd, + 0x04,0x1f,0x57,0x99,0xc9,0x5b,0xe8,0x90,0x63,0xb2,0x4f,0x26,0xe4,0x0c,0xb9,0x28,0xc1,0xa8,0x68,0xa7,0x6f,0xb0,0x09,0x46,0x07,0xe8,0x04,0x3d,0xb4,0x09,0xc9,0x1c,0x32,0xe7,0x57,0x24,0xe8,0x13,0xa4,0x19,0x1e,0x3a,0x83,0x90,0x07,0xf0,0x8e,0x2e,0x89,0x73,0x88,0xb0,0x6d,0x4a,0x00,0xde,0x6d,0xe6,0x0e,0x53,0x6d,0x91,0xfa,0xb5,0x66, + 0x04,0xa3,0x33,0x1a,0x4e,0x1b,0x42,0x23,0xec,0x2c,0x02,0x7e,0xdd,0x48,0x2c,0x92,0x8a,0x14,0xed,0x35,0x8d,0x93,0xf1,0xd4,0x21,0x7d,0x39,0xab,0xf6,0x9f,0xcb,0x5c,0xcc,0x28,0xd6,0x84,0xd2,0xaa,0xab,0xcd,0x63,0x83,0x77,0x5c,0xaa,0x62,0x39,0xde,0x26,0xd4,0xc6,0x93,0x7b,0xb6,0x03,0xec,0xb4,0x19,0x60,0x82,0xf4,0xcf,0xfd,0x50,0x9d, + 0x04,0x3f,0x39,0x52,0x19,0x97,0x74,0xc7,0xcf,0x39,0xb3,0x8b,0x66,0xcb,0x10,0x42,0xa6,0x26,0x0d,0x86,0x80,0x80,0x38,0x45,0xe4,0xd4,0x33,0xad,0xba,0x3b,0xb2,0x48,0x18,0x5e,0xa4,0x95,0xb6,0x8c,0xbc,0x7e,0xd4,0x17,0x3e,0xe6,0x3c,0x90,0x42,0xdc,0x50,0x26,0x25,0xc7,0xeb,0x7e,0x21,0xfb,0x02,0xca,0x9a,0x91,0x14,0xe0,0xa3,0xa1,0x8d, + 0x04,0xcd,0xfb,0x8c,0x0f,0x42,0x2e,0x14,0x4e,0x13,0x7c,0x24,0x12,0xc8,0x6c,0x17,0x1f,0x5f,0xe3,0xfa,0x3f,0x5b,0xbb,0x54,0x4e,0x90,0x76,0x28,0x8f,0x3c,0xed,0x78,0x6e,0x05,0x4f,0xd0,0x72,0x1b,0x77,0xc1,0x1c,0x79,0xbe,0xac,0xb3,0xc9,0x42,0x11,0xb0,0xa1,0x9b,0xda,0x08,0x65,0x2e,0xfe,0xaf,0x92,0x51,0x3a,0x3b,0x0a,0x16,0x36,0x98, + 0x04,0x73,0x59,0x8a,0x6a,0x1c,0x68,0x27,0x8f,0xa6,0xbf,0xd0,0xce,0x40,0x64,0xe6,0x82,0x35,0xbc,0x1c,0x0f,0x6b,0x20,0xa9,0x28,0x10,0x8b,0xe3,0x36,0x73,0x0f,0x87,0xe3,0xcb,0xae,0x61,0x25,0x19,0xb5,0x03,0x2e,0xcc,0x85,0xae,0xd8,0x11,0x27,0x1a,0x95,0xfe,0x79,0x39,0xd5,0xd3,0x46,0x01,0x40,0xba,0x31,0x8f,0x4d,0x14,0xab,0xa3,0x1d, + 0x04,0x58,0xde,0xbd,0x9a,0x7e,0xe2,0xc9,0xd5,0x91,0x32,0x47,0x8a,0x54,0x40,0xae,0x4d,0x5d,0x7e,0xd4,0x37,0x30,0x83,0x69,0xf9,0x2e,0xa8,0x6c,0x82,0x18,0x3f,0x10,0xa1,0x67,0x73,0xe7,0x6f,0x5e,0xdb,0xf4,0xda,0x0e,0x4f,0x1b,0xdf,0xfa,0xc0,0xf5,0x72,0x57,0xe1,0xdf,0xa4,0x65,0x84,0x29,0x31,0x30,0x9a,0x24,0x24,0x5f,0xda,0x6a,0x5d, + 0x04,0x8b,0x90,0x4d,0xe4,0x79,0x67,0x34,0x0c,0x5f,0x8c,0x35,0x72,0xa7,0x20,0x92,0x4e,0xf7,0x57,0x86,0x37,0xfe,0xab,0x19,0x49,0xac,0xb2,0x41,0xa5,0xa6,0xac,0x3f,0x5b,0x95,0x09,0x04,0x49,0x6f,0x98,0x24,0xb1,0xd6,0x3f,0x33,0x13,0xba,0xe2,0x1b,0x89,0xfa,0xe8,0x9a,0xfd,0xfc,0x81,0x1b,0x5e,0xce,0x03,0xfd,0x5a,0xa3,0x01,0x86,0x4f, + 0x04,0xf4,0x89,0x2b,0x6d,0x52,0x5c,0x77,0x1e,0x03,0x5f,0x2a,0x25,0x27,0x08,0xf3,0x78,0x4e,0x48,0x23,0x86,0x04,0xb4,0xf9,0x4d,0xc5,0x6e,0xaa,0x1e,0x54,0x6d,0x94,0x1a,0x34,0x6b,0x1a,0xa0,0xbc,0xe6,0x8b,0x1c,0x50,0xe5,0xb5,0x2f,0x50,0x9f,0xb5,0x52,0x2e,0x5c,0x25,0xe0,0x28,0xbc,0x8f,0x86,0x34,0x02,0xed,0xb7,0xbc,0xad,0x8b,0x1b, + 0x04,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98,0x48,0x3a,0xda,0x77,0x26,0xa3,0xc4,0x65,0x5d,0xa4,0xfb,0xfc,0x0e,0x11,0x08,0xa8,0xfd,0x17,0xb4,0x48,0xa6,0x85,0x54,0x19,0x9c,0x47,0xd0,0x8f,0xfb,0x10,0xd4,0xb8, + 0x04,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98,0xb7,0xc5,0x25,0x88,0xd9,0x5c,0x3b,0x9a,0xa2,0x5b,0x04,0x03,0xf1,0xee,0xf7,0x57,0x02,0xe8,0x4b,0xb7,0x59,0x7a,0xab,0xe6,0x63,0xb8,0x2f,0x6f,0x04,0xef,0x27,0x77, + 0x04,0x78,0x2c,0x8e,0xd1,0x7e,0x3b,0x2a,0x78,0x3b,0x54,0x64,0xf3,0x3b,0x09,0x65,0x2a,0x71,0xc6,0x78,0xe0,0x5e,0xc5,0x1e,0x84,0xe2,0xbc,0xfc,0x66,0x3a,0x3d,0xe9,0x63,0xaf,0x9a,0xcb,0x42,0x80,0xb8,0xc7,0xf7,0xc4,0x2f,0x4e,0xf9,0xab,0xa6,0x24,0x5e,0xc1,0xec,0x17,0x12,0xfd,0x38,0xa0,0xfa,0x96,0x41,0x8d,0x8c,0xd6,0xaa,0x61,0x52, + 0x04,0x6e,0x82,0x35,0x55,0x45,0x29,0x14,0x09,0x91,0x82,0xc6,0xb2,0xc1,0xd6,0xf0,0xb5,0xd2,0x8d,0x50,0xcc,0xd0,0x05,0xaf,0x2c,0xe1,0xbb,0xa5,0x41,0xaa,0x40,0xca,0xff,0x00,0x00,0x00,0x01,0x06,0x04,0x92,0xd5,0xa5,0x67,0x3e,0x0f,0x25,0xd8,0xd5,0x0f,0xb7,0xe5,0x8c,0x49,0xd8,0x6d,0x46,0xd4,0x21,0x69,0x55,0xe0,0xaa,0x3d,0x40,0xe1, + 0x04,0x6e,0x82,0x35,0x55,0x45,0x29,0x14,0x09,0x91,0x82,0xc6,0xb2,0xc1,0xd6,0xf0,0xb5,0xd2,0x8d,0x50,0xcc,0xd0,0x05,0xaf,0x2c,0xe1,0xbb,0xa5,0x41,0xaa,0x40,0xca,0xff,0xff,0xff,0xff,0xfe,0xf9,0xfb,0x6d,0x2a,0x5a,0x98,0xc1,0xf0,0xda,0x27,0x2a,0xf0,0x48,0x1a,0x73,0xb6,0x27,0x92,0xb9,0x2b,0xde,0x96,0xaa,0x1e,0x55,0xc2,0xbb,0x4e, + 0x04,0x00,0x00,0x00,0x01,0x3f,0xd2,0x22,0x48,0xd6,0x4d,0x95,0xf7,0x3c,0x29,0xb4,0x8a,0xb4,0x86,0x31,0x85,0x0b,0xe5,0x03,0xfd,0x00,0xf8,0x46,0x8b,0x5f,0x0f,0x70,0xe0,0xf6,0xee,0x7a,0xa4,0x3b,0xc2,0xc6,0xfd,0x25,0xb1,0xd8,0x26,0x92,0x41,0xcb,0xdd,0x9d,0xbb,0x0d,0xac,0x96,0xdc,0x96,0x23,0x1f,0x43,0x07,0x05,0xf8,0x38,0x71,0x7d, + 0x04,0x25,0xaf,0xd6,0x89,0xac,0xab,0xae,0xd6,0x7c,0x1f,0x29,0x6d,0xe5,0x94,0x06,0xf8,0xc5,0x50,0xf5,0x71,0x46,0xa0,0xb4,0xec,0x2c,0x97,0x87,0x6d,0xff,0xff,0xff,0xff,0xfa,0x46,0xa7,0x6e,0x52,0x03,0x22,0xdf,0xbc,0x49,0x1e,0xc4,0xf0,0xcc,0x19,0x74,0x20,0xfc,0x4e,0xa5,0x88,0x3d,0x8f,0x6d,0xd5,0x3c,0x35,0x4b,0xc4,0xf6,0x7c,0x35, + 0x04,0xd1,0x2e,0x6c,0x66,0xb6,0x77,0x34,0xc3,0xc8,0x4d,0x26,0x01,0xcf,0x5d,0x35,0xdc,0x09,0x7e,0x27,0x63,0x7f,0x0a,0xca,0x4a,0x4f,0xdb,0x74,0xb6,0xaa,0xdd,0x3b,0xb9,0x3f,0x5b,0xdf,0xf8,0x8b,0xd5,0x73,0x6d,0xf8,0x98,0xe6,0x99,0x00,0x6e,0xd7,0x50,0xf1,0x1c,0xf0,0x7c,0x58,0x66,0xcd,0x7a,0xd7,0x0c,0x71,0x21,0xff,0xff,0xff,0xff, + 0x04,0x6d,0x4a,0x7f,0x60,0xd4,0x77,0x4a,0x4f,0x0a,0xa8,0xbb,0xde,0xdb,0x95,0x3c,0x7e,0xea,0x79,0x09,0x40,0x7e,0x31,0x64,0x75,0x56,0x64,0xbc,0x28,0x00,0x00,0x00,0x00,0xe6,0x59,0xd3,0x4e,0x4d,0xf3,0x8d,0x9e,0x8c,0x9e,0xaa,0xdf,0xba,0x36,0x61,0x2c,0x76,0x91,0x95,0xbe,0x86,0xc7,0x7a,0xac,0x3f,0x36,0xe7,0x8b,0x53,0x86,0x80,0xfb}; + +static const unsigned char wycheproof_ecdsa_signatures[] = { 0x30,0x46,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x21,0x00,0x90,0x0e,0x75,0xad,0x23,0x3f,0xcc,0x90,0x85,0x09,0xdb,0xff,0x59,0x22,0x64,0x7d,0xb3,0x7c,0x21,0xf4,0xaf,0xd3,0x20,0x3a,0xe8,0xdc,0x4a,0xe7,0x79,0x4b,0x0f,0x87, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x81,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x82,0x00,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x46,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x44,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x85,0x01,0x00,0x00,0x00,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x89,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x84,0x7f,0xff,0xff,0xff,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x84,0x80,0x00,0x00,0x00,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x84,0xff,0xff,0xff,0xff,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x85,0xff,0xff,0xff,0xff,0xff,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x88,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0xff,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x80,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00, + 0x30,0x47,0x00,0x00,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x05,0x00, + 0x30,0x4a,0x49,0x81,0x77,0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x25,0x00,0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x04,0xde,0xad,0xbe,0xef, + 0x30,0x4d,0xaa,0x00,0xbb,0x00,0xcd,0x00,0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4d,0x22,0x29,0xaa,0x00,0xbb,0x00,0xcd,0x00,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4d,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x22,0x28,0xaa,0x00,0xbb,0x00,0xcd,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x81, + 0x30,0x4b,0xaa,0x02,0xaa,0xbb,0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x80,0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00, + 0x30,0x80,0x31,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00, + 0x05,0x00, + 0x2e,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x2f,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x31,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x32,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0xff,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x00, + 0x30,0x49,0x30,0x01,0x02,0x30,0x44,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x44,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31, + 0x30,0x44,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x82,0x10,0x46,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x30,0x80,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00, + 0x30,0x80,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00, + 0x30,0x80,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x05,0x00,0x00,0x00, + 0x30,0x80,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x06,0x08,0x11,0x22,0x00,0x00, + 0x30,0x80,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00,0xfe,0x02,0xbe,0xef, + 0x30,0x80,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x02,0xbe,0xef, + 0x30,0x47,0x30,0x00,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x30,0x00, + 0x30,0x48,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x02,0x01,0x00, + 0x30,0x48,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0xbf,0x7f,0x00, + 0x30,0x49,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0xa0,0x02,0x05,0x00, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0xa0,0x00, + 0x30,0x47,0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x23,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65, + 0x30,0x67,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x43,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x64,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x43,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xca,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x43,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x13,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x43,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x08,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x46,0x02,0x81,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x02,0x82,0x00,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x22,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x20,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4a,0x02,0x85,0x01,0x00,0x00,0x00,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4e,0x02,0x89,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x02,0x84,0x7f,0xff,0xff,0xff,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x02,0x84,0x80,0x00,0x00,0x00,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x02,0x84,0xff,0xff,0xff,0xff,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4a,0x02,0x85,0xff,0xff,0xff,0xff,0xff,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4d,0x02,0x88,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0xff,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x80,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x22,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x23,0x02,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x24,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02, + 0x30,0x47,0x02,0x23,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x00,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x02,0x23,0x00,0x00,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x00,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x02,0x23,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x05,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4a,0x22,0x26,0x49,0x81,0x77,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x22,0x25,0x25,0x00,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4d,0x22,0x23,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x00,0x04,0xde,0xad,0xbe,0xef,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x24,0x02,0x81,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4b,0x22,0x27,0xaa,0x02,0xaa,0xbb,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x22,0x80,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x00,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x22,0x80,0x03,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x00,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x24,0x05,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x00,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x01,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x03,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x04,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0xff,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x24,0x02,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x22,0x25,0x02,0x01,0x00,0x02,0x20,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x02,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0xe5,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x44,0x02,0x20,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x44,0x02,0x20,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x82,0x10,0x48,0x02,0x82,0x10,0x22,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x46,0x02,0x22,0xff,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x25,0x09,0x01,0x80,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x25,0x02,0x01,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x43,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xbb, + 0x30,0x43,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa4,0x56,0xeb,0x31,0xba, + 0x30,0x43,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf7,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x43,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x01,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x46,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x81,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x82,0x00,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x21,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x1f,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4a,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x85,0x01,0x00,0x00,0x00,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4e,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x89,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x84,0x7f,0xff,0xff,0xff,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x84,0x80,0x00,0x00,0x00,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x84,0xff,0xff,0xff,0xff,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4a,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x85,0xff,0xff,0xff,0xff,0xff,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4d,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x88,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0xff,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x80,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x22,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x22,0x00,0x00,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x47,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x22,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x05,0x00, + 0x30,0x4a,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x22,0x25,0x49,0x81,0x77,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x22,0x24,0x25,0x00,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4d,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x22,0x22,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x04,0xde,0xad,0xbe,0xef, + 0x30,0x25,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x81, + 0x30,0x4b,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x22,0x26,0xaa,0x02,0xaa,0xbb,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x49,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x22,0x80,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00, + 0x30,0x49,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x22,0x80,0x03,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00, + 0x30,0x25,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x05,0x00, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x00,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x01,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x03,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x04,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0xff,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x25,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x00, + 0x30,0x49,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x22,0x24,0x02,0x01,0x6f,0x02,0x1f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6d,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0x3a, + 0x30,0x44,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x1f,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31, + 0x30,0x44,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x1f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x82,0x10,0x48,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x82,0x10,0x21,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x30,0x46,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x21,0xff,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x26,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x09,0x01,0x80, + 0x30,0x26,0x02,0x21,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x01,0x00, + 0x30,0x45,0x02,0x21,0x01,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x83,0xb9,0x0d,0xea,0xbc,0xa4,0xb0,0x5c,0x45,0x74,0xe4,0x9b,0x58,0x99,0xb9,0x64,0xa6,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x44,0x02,0x20,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x86,0x43,0xb0,0x30,0xef,0x46,0x1f,0x1b,0xcd,0xf5,0x3f,0xde,0x3e,0xf9,0x4c,0xe2,0x24,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x46,0x02,0x22,0x01,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x84,0x3f,0xad,0x3b,0xf4,0x85,0x3e,0x07,0xf7,0xc9,0x87,0x70,0xc9,0x9b,0xff,0xc4,0x64,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0xff,0x7e,0xc1,0x08,0x63,0x31,0x05,0x65,0xa9,0x08,0x45,0x7f,0xa0,0xf1,0xb8,0x7a,0x7b,0x01,0xa0,0xf2,0x2a,0x0a,0x98,0x43,0xf6,0x4a,0xed,0xc3,0x34,0x36,0x7c,0xdc,0x9b,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x44,0x02,0x20,0x7e,0xc1,0x08,0x63,0x31,0x05,0x65,0xa9,0x08,0x45,0x7f,0xa0,0xf1,0xb8,0x7a,0x79,0xbc,0x4f,0xcf,0x10,0xb9,0xe0,0xe4,0x32,0x0a,0xc0,0x21,0xc1,0x06,0xb3,0x1d,0xdc,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0xfe,0x7e,0xc1,0x08,0x63,0x31,0x05,0x65,0xa9,0x08,0x45,0x7f,0xa0,0xf1,0xb8,0x7a,0x7c,0x46,0xf2,0x15,0x43,0x5b,0x4f,0xa3,0xba,0x8b,0x1b,0x64,0xa7,0x66,0x46,0x9b,0x5a,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x01,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4d,0x02,0x29,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x81,0x3e,0xf7,0x9c,0xce,0xfa,0x9a,0x56,0xf7,0xba,0x80,0x5f,0x0e,0x47,0x85,0x84,0xfe,0x5f,0x0d,0xd5,0xf5,0x67,0xbc,0x09,0xb5,0x12,0x3c,0xcb,0xc9,0x83,0x23,0x65,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x01,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x7f,0xc1,0xe1,0x97,0xd8,0xae,0xbe,0x20,0x3c,0x96,0xc8,0x72,0x32,0x27,0x21,0x72,0xfb,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0xff,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x82,0x4c,0x83,0xde,0x0b,0x50,0x2c,0xdf,0xc5,0x17,0x23,0xb5,0x18,0x86,0xb4,0xf0,0x79,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x46,0x02,0x22,0x01,0x00,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9a,0x3b,0xb6,0x0f,0xa1,0xa1,0x48,0x15,0xbb,0xc0,0xa9,0x54,0xa0,0x75,0x8d,0x2c,0x72,0xba,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x44,0x02,0x20,0x90,0x0e,0x75,0xad,0x23,0x3f,0xcc,0x90,0x85,0x09,0xdb,0xff,0x59,0x22,0x64,0x7e,0xf8,0xcd,0x45,0x0e,0x00,0x8a,0x7f,0xff,0x29,0x09,0xec,0x5a,0xa9,0x14,0xce,0x46,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0xfe,0x90,0x0e,0x75,0xad,0x23,0x3f,0xcc,0x90,0x85,0x09,0xdb,0xff,0x59,0x22,0x64,0x80,0x3e,0x1e,0x68,0x27,0x51,0x41,0xdf,0xc3,0x69,0x37,0x8d,0xcd,0xd8,0xde,0x8d,0x05,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0x01,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x45,0x02,0x21,0xff,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x4d,0x02,0x29,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba,0x02,0x20,0x6f,0xf1,0x8a,0x52,0xdc,0xc0,0x33,0x6f,0x7a,0xf6,0x24,0x00,0xa6,0xdd,0x9b,0x81,0x07,0x32,0xba,0xf1,0xff,0x75,0x80,0x00,0xd6,0xf6,0x13,0xa5,0x56,0xeb,0x31,0xba, + 0x30,0x06,0x02,0x01,0x00,0x02,0x01,0x00, + 0x30,0x06,0x02,0x01,0x00,0x02,0x01,0x01, + 0x30,0x06,0x02,0x01,0x00,0x02,0x01,0xff, + 0x30,0x26,0x02,0x01,0x00,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41, + 0x30,0x26,0x02,0x01,0x00,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40, + 0x30,0x26,0x02,0x01,0x00,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42, + 0x30,0x26,0x02,0x01,0x00,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f, + 0x30,0x26,0x02,0x01,0x00,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30, + 0x30,0x06,0x02,0x01,0x01,0x02,0x01,0x00, + 0x30,0x06,0x02,0x01,0x01,0x02,0x01,0x01, + 0x30,0x06,0x02,0x01,0x01,0x02,0x01,0xff, + 0x30,0x26,0x02,0x01,0x01,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41, + 0x30,0x26,0x02,0x01,0x01,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40, + 0x30,0x26,0x02,0x01,0x01,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42, + 0x30,0x26,0x02,0x01,0x01,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f, + 0x30,0x26,0x02,0x01,0x01,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30, + 0x30,0x06,0x02,0x01,0xff,0x02,0x01,0x00, + 0x30,0x06,0x02,0x01,0xff,0x02,0x01,0x01, + 0x30,0x06,0x02,0x01,0xff,0x02,0x01,0xff, + 0x30,0x26,0x02,0x01,0xff,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41, + 0x30,0x26,0x02,0x01,0xff,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40, + 0x30,0x26,0x02,0x01,0xff,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42, + 0x30,0x26,0x02,0x01,0xff,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f, + 0x30,0x26,0x02,0x01,0xff,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x02,0x01,0x00, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x02,0x01,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x02,0x01,0xff, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40,0x02,0x01,0x00, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40,0x02,0x01,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40,0x02,0x01,0xff, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42,0x02,0x01,0x00, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42,0x02,0x01,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42,0x02,0x01,0xff, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x02,0x01,0x00, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x02,0x01,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x02,0x01,0xff, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30,0x02,0x01,0x00, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30,0x02,0x01,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30,0x02,0x01,0xff, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x40, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x42, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f, + 0x30,0x46,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x30, + 0x30,0x08,0x02,0x01,0x00,0x09,0x03,0x80,0xfe,0x01, + 0x30,0x06,0x02,0x01,0x00,0x09,0x01,0x42, + 0x30,0x06,0x02,0x01,0x00,0x01,0x01,0x01, + 0x30,0x06,0x02,0x01,0x00,0x01,0x01,0x00, + 0x30,0x05,0x02,0x01,0x00,0x05,0x00, + 0x30,0x05,0x02,0x01,0x00,0x0c,0x00, + 0x30,0x06,0x02,0x01,0x00,0x0c,0x01,0x30, + 0x30,0x05,0x02,0x01,0x00,0x30,0x00, + 0x30,0x08,0x02,0x01,0x00,0x30,0x03,0x02,0x01,0x00, + 0x30,0x08,0x02,0x01,0x01,0x09,0x03,0x80,0xfe,0x01, + 0x30,0x06,0x02,0x01,0x01,0x09,0x01,0x42, + 0x30,0x06,0x02,0x01,0x01,0x01,0x01,0x01, + 0x30,0x06,0x02,0x01,0x01,0x01,0x01,0x00, + 0x30,0x05,0x02,0x01,0x01,0x05,0x00, + 0x30,0x05,0x02,0x01,0x01,0x0c,0x00, + 0x30,0x06,0x02,0x01,0x01,0x0c,0x01,0x30, + 0x30,0x05,0x02,0x01,0x01,0x30,0x00, + 0x30,0x08,0x02,0x01,0x01,0x30,0x03,0x02,0x01,0x00, + 0x30,0x08,0x02,0x01,0xff,0x09,0x03,0x80,0xfe,0x01, + 0x30,0x06,0x02,0x01,0xff,0x09,0x01,0x42, + 0x30,0x06,0x02,0x01,0xff,0x01,0x01,0x01, + 0x30,0x06,0x02,0x01,0xff,0x01,0x01,0x00, + 0x30,0x05,0x02,0x01,0xff,0x05,0x00, + 0x30,0x05,0x02,0x01,0xff,0x0c,0x00, + 0x30,0x06,0x02,0x01,0xff,0x0c,0x01,0x30, + 0x30,0x05,0x02,0x01,0xff,0x30,0x00, + 0x30,0x08,0x02,0x01,0xff,0x30,0x03,0x02,0x01,0x00, + 0x30,0x28,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x09,0x03,0x80,0xfe,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x09,0x01,0x42, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x01,0x01,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x01,0x01,0x00, + 0x30,0x25,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x05,0x00, + 0x30,0x25,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x0c,0x00, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x0c,0x01,0x30, + 0x30,0x25,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x30,0x00, + 0x30,0x28,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x41,0x30,0x03,0x02,0x01,0x00, + 0x30,0x28,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x09,0x03,0x80,0xfe,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x09,0x01,0x42, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x01,0x01,0x01, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x01,0x01,0x00, + 0x30,0x25,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x05,0x00, + 0x30,0x25,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x0c,0x00, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x0c,0x01,0x30, + 0x30,0x25,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x30,0x00, + 0x30,0x28,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2f,0x30,0x03,0x02,0x01,0x00, + 0x30,0x0a,0x09,0x03,0x80,0xfe,0x01,0x09,0x03,0x80,0xfe,0x01, + 0x30,0x06,0x09,0x01,0x42,0x09,0x01,0x42, + 0x30,0x06,0x01,0x01,0x01,0x01,0x01,0x01, + 0x30,0x06,0x01,0x01,0x00,0x01,0x01,0x00, + 0x30,0x04,0x05,0x00,0x05,0x00, + 0x30,0x04,0x0c,0x00,0x0c,0x00, + 0x30,0x06,0x0c,0x01,0x30,0x0c,0x01,0x30, + 0x30,0x04,0x30,0x00,0x30,0x00, + 0x30,0x0a,0x30,0x03,0x02,0x01,0x00,0x30,0x03,0x02,0x01,0x00, + 0x30,0x08,0x09,0x03,0x80,0xfe,0x01,0x02,0x01,0x00, + 0x30,0x06,0x09,0x01,0x42,0x02,0x01,0x00, + 0x30,0x06,0x01,0x01,0x01,0x02,0x01,0x00, + 0x30,0x06,0x01,0x01,0x00,0x02,0x01,0x00, + 0x30,0x05,0x05,0x00,0x02,0x01,0x00, + 0x30,0x05,0x0c,0x00,0x02,0x01,0x00, + 0x30,0x06,0x0c,0x01,0x30,0x02,0x01,0x00, + 0x30,0x05,0x30,0x00,0x02,0x01,0x00, + 0x30,0x08,0x30,0x03,0x02,0x01,0x00,0x02,0x01,0x00, + 0x30,0x45,0x02,0x21,0x00,0xdd,0x1b,0x7d,0x09,0xa7,0xbd,0x82,0x18,0x96,0x10,0x34,0xa3,0x9a,0x87,0xfe,0xcf,0x53,0x14,0xf0,0x0c,0x4d,0x25,0xeb,0x58,0xa0,0x7a,0xc8,0x5e,0x85,0xea,0xb5,0x16,0x02,0x20,0x35,0x13,0x8c,0x40,0x1e,0xf8,0xd3,0x49,0x3d,0x65,0xc9,0x00,0x2f,0xe6,0x2b,0x43,0xae,0xe5,0x68,0x73,0x1b,0x74,0x45,0x48,0x35,0x89,0x96,0xd9,0xcc,0x42,0x7e,0x06, + 0x30,0x45,0x02,0x21,0x00,0x95,0xc2,0x92,0x67,0xd9,0x72,0xa0,0x43,0xd9,0x55,0x22,0x45,0x46,0x22,0x2b,0xba,0x34,0x3f,0xc1,0xd4,0xdb,0x0f,0xec,0x26,0x2a,0x33,0xac,0x61,0x30,0x56,0x96,0xae,0x02,0x20,0x6e,0xdf,0xe9,0x67,0x13,0xae,0xd5,0x6f,0x8a,0x28,0xa6,0x65,0x3f,0x57,0xe0,0xb8,0x29,0x71,0x2e,0x5e,0xdd,0xc6,0x7f,0x34,0x68,0x2b,0x24,0xf0,0x67,0x6b,0x26,0x40, + 0x30,0x44,0x02,0x20,0x28,0xf9,0x4a,0x89,0x4e,0x92,0x02,0x46,0x99,0xe3,0x45,0xfe,0x66,0x97,0x1e,0x3e,0xdc,0xd0,0x50,0x02,0x33,0x86,0x13,0x5a,0xb3,0x93,0x9d,0x55,0x08,0x98,0xfb,0x25,0x02,0x20,0x32,0x96,0x3e,0x5b,0xd4,0x1f,0xa5,0x91,0x1e,0xd8,0xf3,0x7d,0xeb,0x86,0xda,0xe0,0xa7,0x62,0xbb,0x61,0x21,0xc8,0x94,0x61,0x50,0x83,0xc5,0xd9,0x5e,0xa0,0x1d,0xb3, + 0x30,0x45,0x02,0x21,0x00,0xbe,0x26,0xb1,0x8f,0x95,0x49,0xf8,0x9f,0x41,0x1a,0x9b,0x52,0x53,0x6b,0x15,0xaa,0x27,0x0b,0x84,0x54,0x8d,0x0e,0x85,0x9a,0x19,0x52,0xa2,0x7a,0xf1,0xa7,0x7a,0xc6,0x02,0x20,0x70,0xc1,0xd4,0xfa,0x9c,0xd0,0x3c,0xc8,0xea,0xa8,0xd5,0x06,0xed,0xb9,0x7e,0xed,0x7b,0x83,0x58,0xb4,0x53,0xc8,0x8a,0xef,0xbb,0x88,0x0a,0x3f,0x0e,0x8d,0x47,0x2f, + 0x30,0x45,0x02,0x21,0x00,0xb1,0xa4,0xb1,0x47,0x8e,0x65,0xcc,0x3e,0xaf,0xdf,0x22,0x5d,0x12,0x98,0xb4,0x3f,0x2d,0xa1,0x9e,0x4b,0xcf,0xf7,0xea,0xcc,0x0a,0x2e,0x98,0xcd,0x4b,0x74,0xb1,0x14,0x02,0x20,0x17,0x9a,0xa3,0x1e,0x30,0x4c,0xc1,0x42,0xcf,0x50,0x73,0x17,0x17,0x51,0xb2,0x8f,0x3f,0x5e,0x0f,0xa8,0x8c,0x99,0x4e,0x7c,0x55,0xf1,0xbc,0x07,0xb8,0xd5,0x6c,0x16, + 0x30,0x44,0x02,0x20,0x32,0x53,0x32,0x02,0x12,0x61,0xf1,0xbd,0x18,0xf2,0x71,0x2a,0xa1,0xe2,0x25,0x2d,0xa2,0x37,0x96,0xda,0x8a,0x4b,0x1f,0xf6,0xea,0x18,0xca,0xfe,0xc7,0xe1,0x71,0xf2,0x02,0x20,0x40,0xb4,0xf5,0xe2,0x87,0xee,0x61,0xfc,0x3c,0x80,0x41,0x86,0x98,0x23,0x60,0x89,0x1e,0xaa,0x35,0xc7,0x5f,0x05,0xa4,0x3e,0xcd,0x48,0xb3,0x5d,0x98,0x4a,0x66,0x48, + 0x30,0x45,0x02,0x21,0x00,0xa2,0x3a,0xd1,0x8d,0x8f,0xc6,0x6d,0x81,0xaf,0x09,0x03,0x89,0x0c,0xbd,0x45,0x3a,0x55,0x4c,0xb0,0x4c,0xdc,0x1a,0x8c,0xa7,0xf7,0xf7,0x8e,0x53,0x67,0xed,0x88,0xa0,0x02,0x20,0x23,0xe3,0xeb,0x2c,0xe1,0xc0,0x4e,0xa7,0x48,0xc3,0x89,0xbd,0x97,0x37,0x4a,0xa9,0x41,0x3b,0x92,0x68,0x85,0x1c,0x04,0xdc,0xd9,0xf8,0x8e,0x78,0x81,0x3f,0xee,0x56, + 0x30,0x44,0x02,0x20,0x2b,0xde,0xa4,0x1c,0xda,0x63,0xa2,0xd1,0x4b,0xf4,0x73,0x53,0xbd,0x20,0x88,0x0a,0x69,0x09,0x01,0xde,0x7c,0xd6,0xe3,0xcc,0x6d,0x8e,0xd5,0xba,0x0c,0xdb,0x10,0x91,0x02,0x20,0x3c,0xea,0x66,0xbc,0xcf,0xc9,0xf9,0xbf,0x8c,0x7c,0xa4,0xe1,0xc1,0x45,0x7c,0xc9,0x14,0x5e,0x13,0xe9,0x36,0xd9,0x0b,0x3d,0x9c,0x77,0x86,0xb8,0xb2,0x6c,0xf4,0xc7, + 0x30,0x45,0x02,0x21,0x00,0xd7,0xcd,0x76,0xec,0x01,0xc1,0xb1,0x07,0x9e,0xba,0x9e,0x2a,0xa2,0xa3,0x97,0x24,0x3c,0x47,0x58,0xc9,0x8a,0x1b,0xa0,0xb7,0x40,0x4a,0x34,0x0b,0x9b,0x00,0xce,0xd6,0x02,0x20,0x35,0x75,0x00,0x1e,0x19,0xd9,0x22,0xe6,0xde,0x8b,0x3d,0x6c,0x84,0xea,0x43,0xb5,0xc3,0x33,0x81,0x06,0xcf,0x29,0x99,0x01,0x34,0xe7,0x66,0x9a,0x82,0x6f,0x78,0xe6, + 0x30,0x45,0x02,0x21,0x00,0xa8,0x72,0xc7,0x44,0xd9,0x36,0xdb,0x21,0xa1,0x0c,0x36,0x1d,0xd5,0xc9,0x06,0x33,0x55,0xf8,0x49,0x02,0x21,0x96,0x52,0xf6,0xfc,0x56,0xdc,0x95,0xa7,0x13,0x9d,0x96,0x02,0x20,0x40,0x0d,0xf7,0x57,0x5d,0x97,0x56,0x21,0x0e,0x9c,0xcc,0x77,0x16,0x2c,0x6b,0x59,0x3c,0x77,0x46,0xcf,0xb4,0x8a,0xc2,0x63,0xc4,0x27,0x50,0xb4,0x21,0xef,0x4b,0xb9, + 0x30,0x45,0x02,0x21,0x00,0x9f,0xa9,0xaf,0xe0,0x77,0x52,0xda,0x10,0xb3,0x6d,0x3a,0xfc,0xd0,0xfe,0x44,0xbf,0xc4,0x02,0x44,0xd7,0x52,0x03,0x59,0x9c,0xf8,0xf5,0x04,0x7f,0xa3,0x45,0x38,0x54,0x02,0x20,0x50,0xe0,0xa7,0xc0,0x13,0xbf,0xbf,0x51,0x81,0x97,0x36,0x97,0x2d,0x44,0xb4,0xb5,0x6b,0xc2,0xa2,0xb2,0xc1,0x80,0xdf,0x6e,0xc6,0x72,0xdf,0x17,0x14,0x10,0xd7,0x7a, + 0x30,0x45,0x02,0x21,0x00,0x88,0x56,0x40,0x38,0x4d,0x0d,0x91,0x0e,0xfb,0x17,0x7b,0x46,0xbe,0x6c,0x3d,0xc5,0xca,0xc8,0x1f,0x0b,0x88,0xc3,0x19,0x0b,0xb6,0xb5,0xf9,0x9c,0x26,0x41,0xf2,0x05,0x02,0x20,0x73,0x8e,0xd9,0xbf,0xf1,0x16,0x30,0x6d,0x9c,0xaa,0x0f,0x8f,0xc6,0x08,0xbe,0x24,0x3e,0x0b,0x56,0x77,0x79,0xd8,0xda,0xb0,0x3e,0x8e,0x19,0xd5,0x53,0xf1,0xdc,0x8e, + 0x30,0x44,0x02,0x20,0x2d,0x05,0x1f,0x91,0xc5,0xa9,0xd4,0x40,0xc5,0x67,0x69,0x85,0x71,0x04,0x83,0xbc,0x4f,0x1a,0x6c,0x61,0x1b,0x10,0xc9,0x5a,0x2f,0xf0,0x36,0x3d,0x90,0xc2,0xa4,0x58,0x02,0x20,0x6d,0xdf,0x94,0xe6,0xfb,0xa5,0xbe,0x58,0x68,0x33,0xd0,0xc5,0x3c,0xf2,0x16,0xad,0x39,0x48,0xf3,0x79,0x53,0xc2,0x6c,0x1c,0xf4,0x96,0x8e,0x9a,0x9e,0x82,0x43,0xdc, + 0x30,0x45,0x02,0x21,0x00,0xf3,0xac,0x25,0x23,0x96,0x74,0x82,0xf5,0x3d,0x50,0x85,0x22,0x71,0x2d,0x58,0x3f,0x43,0x79,0xcd,0x82,0x41,0x01,0xff,0x63,0x5e,0xa0,0x93,0x51,0x17,0xba,0xa5,0x4f,0x02,0x20,0x27,0xf1,0x08,0x12,0x22,0x73,0x97,0xe0,0x2c,0xea,0x96,0xfb,0x0e,0x68,0x07,0x61,0x63,0x6d,0xab,0x2b,0x08,0x0d,0x1f,0xc5,0xd1,0x16,0x85,0xcb,0xe8,0x50,0x0c,0xfe, + 0x30,0x45,0x02,0x21,0x00,0x96,0x44,0x7c,0xf6,0x8c,0x3a,0xb7,0x26,0x6e,0xd7,0x44,0x7d,0xe3,0xac,0x52,0xfe,0xd7,0xcc,0x08,0xcb,0xdf,0xea,0x39,0x1c,0x18,0xa9,0xb8,0xab,0x37,0x0b,0xc9,0x13,0x02,0x20,0x0f,0x5e,0x78,0x74,0xd3,0xac,0x0e,0x91,0x8f,0x01,0xc8,0x85,0xa1,0x63,0x91,0x77,0xc9,0x23,0xf8,0x66,0x0d,0x1c,0xeb,0xa1,0xca,0x1f,0x30,0x1b,0xc6,0x75,0xcd,0xbc, + 0x30,0x44,0x02,0x20,0x53,0x0a,0x08,0x32,0xb6,0x91,0xda,0x0b,0x56,0x19,0xa0,0xb1,0x1d,0xe6,0x87,0x7f,0x3c,0x09,0x71,0xba,0xaa,0x68,0xed,0x12,0x27,0x58,0xc2,0x9c,0xaa,0xf4,0x6b,0x72,0x02,0x20,0x6c,0x89,0xe4,0x4f,0x5e,0xb3,0x30,0x60,0xea,0x4b,0x46,0x31,0x8c,0x39,0x13,0x8e,0xae,0xde,0xc7,0x2d,0xe4,0x2b,0xa5,0x76,0x57,0x9a,0x6a,0x46,0x90,0xe3,0x39,0xf3, + 0x30,0x45,0x02,0x21,0x00,0x9c,0x54,0xc2,0x55,0x00,0xbd,0xe0,0xb9,0x2d,0x72,0xd6,0xec,0x48,0x3d,0xc2,0x48,0x2f,0x36,0x54,0x29,0x4c,0xa7,0x4d,0xe7,0x96,0xb6,0x81,0x25,0x5e,0xd5,0x8a,0x77,0x02,0x20,0x67,0x74,0x53,0xc6,0xb5,0x6f,0x52,0x76,0x31,0xc9,0xf6,0x7b,0x3f,0x3e,0xb6,0x21,0xfd,0x88,0x58,0x2b,0x4a,0xff,0x15,0x6d,0x2f,0x15,0x67,0xd6,0x21,0x1a,0x2a,0x33, + 0x30,0x45,0x02,0x21,0x00,0xe7,0x90,0x9d,0x41,0x43,0x9e,0x2f,0x6a,0xf2,0x91,0x36,0xc7,0x34,0x8c,0xa2,0x64,0x1a,0x2b,0x07,0x0d,0x5b,0x64,0xf9,0x1e,0xa9,0xda,0x70,0x70,0xc7,0xa2,0x61,0x8b,0x02,0x20,0x42,0xd7,0x82,0xf1,0x32,0xfa,0x1d,0x36,0xc2,0xc8,0x8b,0xa2,0x7c,0x3d,0x67,0x8d,0x80,0x18,0x4a,0x5d,0x1e,0xcc,0xac,0x75,0x01,0xf0,0xb4,0x7e,0x3d,0x20,0x50,0x08, + 0x30,0x44,0x02,0x20,0x59,0x24,0x87,0x32,0x09,0x59,0x31,0x35,0xa4,0xc3,0xda,0x7b,0xb3,0x81,0x22,0x7f,0x8a,0x4b,0x6a,0xa9,0xf3,0x4f,0xe5,0xbb,0x7f,0x8f,0xbc,0x13,0x1a,0x03,0x9f,0xfe,0x02,0x20,0x1f,0x1b,0xb1,0x1b,0x44,0x1c,0x8f,0xea,0xa4,0x0f,0x44,0x21,0x3d,0x9a,0x40,0x5e,0xd7,0x92,0xd5,0x9f,0xb4,0x9d,0x5b,0xcd,0xd9,0xa4,0x28,0x5a,0xe5,0x69,0x30,0x22, + 0x30,0x45,0x02,0x21,0x00,0xee,0xb6,0x92,0xc9,0xb2,0x62,0x96,0x9b,0x23,0x1c,0x38,0xb5,0xa7,0xf6,0x06,0x49,0xe0,0xc8,0x75,0xcd,0x64,0xdf,0x88,0xf3,0x3a,0xa5,0x71,0xfa,0x3d,0x29,0xab,0x0e,0x02,0x20,0x21,0x8b,0x3a,0x1e,0xb0,0x63,0x79,0xc2,0xc1,0x8c,0xf5,0x1b,0x06,0x43,0x07,0x86,0xd1,0xc6,0x4c,0xd2,0xd2,0x4c,0x9b,0x23,0x2b,0x23,0xe5,0xba,0xc7,0x98,0x9a,0xcd, + 0x30,0x45,0x02,0x21,0x00,0xa4,0x00,0x34,0x17,0x7f,0x36,0x09,0x1c,0x2b,0x65,0x36,0x84,0xa0,0xe3,0xeb,0x5d,0x4b,0xff,0x18,0xe4,0xd0,0x9f,0x66,0x4c,0x28,0x00,0xe7,0xca,0xfd,0xa1,0xda,0xf8,0x02,0x20,0x3a,0x3e,0xc2,0x98,0x53,0x70,0x4e,0x52,0x03,0x1c,0x58,0x92,0x7a,0x80,0x0a,0x96,0x83,0x53,0xad,0xc3,0xd9,0x73,0xbe,0xba,0x91,0x72,0xcb,0xbe,0xab,0x4d,0xd1,0x49, + 0x30,0x45,0x02,0x21,0x00,0xb5,0xd7,0x95,0xcc,0x75,0xce,0xa5,0xc4,0x34,0xfa,0x41,0x85,0x18,0x0c,0xd6,0xbd,0x21,0x22,0x3f,0x3d,0x5a,0x86,0xda,0x66,0x70,0xd7,0x1d,0x95,0x68,0x0d,0xad,0xbf,0x02,0x20,0x54,0xe4,0xd8,0x81,0x0a,0x00,0x1e,0xcb,0xb9,0xf7,0xca,0x1c,0x2e,0xbf,0xdb,0x9d,0x00,0x9e,0x90,0x31,0xa4,0x31,0xac,0xa3,0xc2,0x0a,0xb4,0xe0,0xd1,0x37,0x4e,0xc1, + 0x30,0x44,0x02,0x20,0x07,0xdc,0x24,0x78,0xd4,0x3c,0x12,0x32,0xa4,0x59,0x56,0x08,0xc6,0x44,0x26,0xc3,0x55,0x10,0x05,0x1a,0x63,0x1a,0xe6,0xa5,0xa6,0xeb,0x11,0x61,0xe5,0x7e,0x42,0xe1,0x02,0x20,0x4a,0x59,0xea,0x0f,0xdb,0x72,0xd1,0x21,0x65,0xce,0xa3,0xbf,0x1c,0xa8,0x6b,0xa9,0x75,0x17,0xbd,0x18,0x8d,0xb3,0xdb,0xd2,0x1a,0x5a,0x15,0x78,0x50,0x02,0x19,0x84, + 0x30,0x45,0x02,0x21,0x00,0xdd,0xd2,0x0c,0x4a,0x05,0x59,0x6c,0xa8,0x68,0xb5,0x58,0x83,0x9f,0xce,0x9f,0x65,0x11,0xdd,0xd8,0x3d,0x1c,0xcb,0x53,0xf8,0x2e,0x52,0x69,0xd5,0x59,0xa0,0x15,0x52,0x02,0x20,0x5b,0x91,0x73,0x47,0x29,0xd9,0x30,0x93,0xff,0x22,0x12,0x3c,0x4a,0x25,0x81,0x9d,0x7f,0xeb,0x66,0xa2,0x50,0x66,0x3f,0xc7,0x80,0xcb,0x66,0xfc,0x7b,0x6e,0x6d,0x17, + 0x30,0x45,0x02,0x21,0x00,0x9c,0xde,0x6e,0x0e,0xde,0x0a,0x00,0x3f,0x02,0xfd,0xa0,0xa0,0x1b,0x59,0xfa,0xcf,0xe5,0xde,0xc0,0x63,0x31,0x8f,0x27,0x9c,0xe2,0xde,0x7a,0x9b,0x10,0x62,0xf7,0xb7,0x02,0x20,0x28,0x86,0xa5,0xb8,0xc6,0x79,0xbd,0xf8,0x22,0x4c,0x66,0xf9,0x08,0xfd,0x62,0x05,0x49,0x2c,0xb7,0x0b,0x00,0x68,0xd4,0x6a,0xe4,0xf3,0x3a,0x41,0x49,0xb1,0x2a,0x52, + 0x30,0x45,0x02,0x21,0x00,0xc5,0x77,0x10,0x16,0xd0,0xdd,0x63,0x57,0x14,0x3c,0x89,0xf6,0x84,0xcd,0x74,0x04,0x23,0x50,0x25,0x54,0xc0,0xc5,0x9a,0xa8,0xc9,0x95,0x84,0xf1,0xff,0x38,0xf6,0x09,0x02,0x20,0x54,0xb4,0x05,0xf4,0x47,0x75,0x46,0x68,0x6e,0x46,0x4c,0x54,0x63,0xb4,0xfd,0x41,0x90,0x57,0x2e,0x58,0xd0,0xf7,0xe7,0x35,0x7f,0x6e,0x61,0x94,0x7d,0x20,0x71,0x5c, + 0x30,0x45,0x02,0x21,0x00,0xa2,0x4e,0xbc,0x0e,0xc2,0x24,0xbd,0x67,0xae,0x39,0x7c,0xbe,0x6f,0xa3,0x7b,0x31,0x25,0xad,0xbd,0x34,0x89,0x1a,0xbe,0x2d,0x7c,0x73,0x56,0x92,0x19,0x16,0xdf,0xe6,0x02,0x20,0x34,0xf6,0xeb,0x63,0x74,0x73,0x1b,0xbb,0xaf,0xc4,0x92,0x4f,0xb8,0xb0,0xbd,0xcd,0xda,0x49,0x45,0x6d,0x72,0x4c,0xda,0xe6,0x17,0x8d,0x87,0x01,0x4c,0xb5,0x3d,0x8c, + 0x30,0x44,0x02,0x20,0x25,0x57,0xd6,0x4a,0x7a,0xee,0x2e,0x09,0x31,0xc0,0x12,0xe4,0xfe,0xa1,0xcd,0x3a,0x2c,0x33,0x4e,0xda,0xe6,0x8c,0xde,0xb7,0x15,0x8c,0xaf,0x21,0xb6,0x8e,0x5a,0x24,0x02,0x20,0x7f,0x06,0xcd,0xbb,0x6a,0x90,0x02,0x3a,0x97,0x38,0x82,0xed,0x97,0xb0,0x80,0xfe,0x6b,0x05,0xaf,0x3e,0xc9,0x3d,0xb6,0xf1,0xa4,0x39,0x9a,0x69,0xed,0xf7,0x67,0x0d, + 0x30,0x45,0x02,0x21,0x00,0xc4,0xf2,0xec,0xcb,0xb6,0xa2,0x43,0x50,0xc8,0x46,0x64,0x50,0xb9,0xd6,0x1b,0x20,0x7e,0xe3,0x59,0xe0,0x37,0xb3,0xdc,0xed,0xb4,0x2a,0x3f,0x2e,0x6d,0xd6,0xae,0xb5,0x02,0x20,0x32,0x63,0xc6,0xb5,0x9a,0x2f,0x55,0xcd,0xd1,0xc6,0xe1,0x48,0x94,0xd5,0xe5,0x96,0x3b,0x28,0xbc,0x3e,0x24,0x69,0xac,0x9b,0xa1,0x19,0x79,0x91,0xca,0x7f,0xf9,0xc7, + 0x30,0x45,0x02,0x21,0x00,0xef,0xf0,0x47,0x81,0xc9,0xcb,0xcd,0x16,0x2d,0x0a,0x25,0xa6,0xe2,0xeb,0xcc,0xa4,0x35,0x06,0xc5,0x23,0x38,0x5c,0xb5,0x15,0xd4,0x9e,0xa3,0x8a,0x1b,0x12,0xfc,0xad,0x02,0x20,0x15,0xac,0xd7,0x31,0x94,0xc9,0x1a,0x95,0x47,0x85,0x34,0xf2,0x30,0x15,0xb6,0x72,0xeb,0xed,0x21,0x3e,0x45,0x42,0x4d,0xd2,0xc8,0xe2,0x6a,0xc8,0xb3,0xeb,0x34,0xa5, + 0x30,0x45,0x02,0x21,0x00,0xf5,0x8b,0x4e,0x31,0x10,0xa6,0x4b,0xf1,0xb5,0xdb,0x97,0x63,0x9e,0xe0,0xe5,0xa9,0xc8,0xdf,0xa4,0x9d,0xc5,0x9b,0x67,0x98,0x91,0xf5,0x20,0xfd,0xf0,0x58,0x4c,0x87,0x02,0x20,0x2c,0xd8,0xfe,0x51,0x88,0x8a,0xee,0x9d,0xb3,0xe0,0x75,0x44,0x0f,0xd4,0xdb,0x73,0xb5,0xc7,0x32,0xfb,0x87,0xb5,0x10,0xe9,0x70,0x93,0xd6,0x64,0x15,0xf6,0x2a,0xf7, + 0x30,0x45,0x02,0x21,0x00,0xf8,0xab,0xec,0xaa,0x4f,0x0c,0x50,0x2d,0xe4,0xbf,0x59,0x03,0xd4,0x84,0x17,0xf7,0x86,0xbf,0x92,0xe8,0xad,0x72,0xfe,0xc0,0xbd,0x7f,0xcb,0x78,0x00,0xc0,0xbb,0xe3,0x02,0x20,0x4c,0x7f,0x9e,0x23,0x10,0x76,0xa3,0x0b,0x7a,0xe3,0x6b,0x0c,0xeb,0xe6,0x9c,0xce,0xf1,0xcd,0x19,0x4f,0x7c,0xce,0x93,0xa5,0x58,0x8f,0xd6,0x81,0x4f,0x43,0x7c,0x0e, + 0x30,0x44,0x02,0x20,0x5d,0x5b,0x38,0xbd,0x37,0xad,0x49,0x8b,0x22,0x27,0xa6,0x33,0x26,0x8a,0x8c,0xca,0x87,0x9a,0x5c,0x7c,0x94,0xa4,0xe4,0x16,0xbd,0x0a,0x61,0x4d,0x09,0xe6,0x06,0xd2,0x02,0x20,0x12,0xb8,0xd6,0x64,0xea,0x99,0x91,0x06,0x2e,0xcb,0xb8,0x34,0xe5,0x84,0x00,0xe2,0x5c,0x46,0x00,0x7a,0xf8,0x4f,0x60,0x07,0xd7,0xf1,0x68,0x54,0x43,0x26,0x9a,0xfe, + 0x30,0x44,0x02,0x20,0x0c,0x1c,0xd9,0xfe,0x40,0x34,0xf0,0x86,0xa2,0xb5,0x2d,0x65,0xb9,0xd3,0x83,0x4d,0x72,0xae,0xbe,0x7f,0x33,0xdf,0xe8,0xf9,0x76,0xda,0x82,0x64,0x81,0x77,0xd8,0xe3,0x02,0x20,0x13,0x10,0x57,0x82,0xe3,0xd0,0xcf,0xe8,0x5c,0x27,0x78,0xde,0xc1,0xa8,0x48,0xb2,0x7a,0xc0,0xae,0x07,0x1a,0xa6,0xda,0x34,0x1a,0x95,0x53,0xa9,0x46,0xb4,0x1e,0x59, + 0x30,0x45,0x02,0x21,0x00,0xae,0x79,0x35,0xfb,0x96,0xff,0x24,0x6b,0x7b,0x5d,0x56,0x62,0x87,0x0d,0x1b,0xa5,0x87,0xb0,0x3d,0x6e,0x13,0x60,0xba,0xf4,0x79,0x88,0xb5,0xc0,0x2c,0xcc,0x1a,0x5b,0x02,0x20,0x5f,0x00,0xc3,0x23,0x27,0x20,0x83,0x78,0x2d,0x4a,0x59,0xf2,0xdf,0xd6,0x5e,0x49,0xde,0x06,0x93,0x62,0x70,0x16,0x90,0x0e,0xf7,0xe6,0x14,0x28,0x05,0x66,0x64,0xb3, + 0x30,0x44,0x02,0x20,0x00,0xa1,0x34,0xb5,0xc6,0xcc,0xbc,0xef,0xd4,0xc8,0x82,0xb9,0x45,0xba,0xeb,0x49,0x33,0x44,0x41,0x72,0x79,0x5f,0xa6,0x79,0x6a,0xae,0x14,0x90,0x67,0x54,0x70,0x98,0x02,0x20,0x56,0x6e,0x46,0x10,0x5d,0x24,0xd8,0x90,0x15,0x1e,0x3e,0xea,0x3e,0xbf,0x88,0xf5,0xb9,0x2b,0x3f,0x5e,0xc9,0x3a,0x21,0x77,0x65,0xa6,0xdc,0xbd,0x94,0xf2,0xc5,0x5b, + 0x30,0x44,0x02,0x20,0x2e,0x47,0x21,0x36,0x3a,0xd3,0x99,0x2c,0x13,0x9e,0x5a,0x1c,0x26,0x39,0x5d,0x2c,0x2d,0x77,0x78,0x24,0xaa,0x24,0xfd,0xe0,0x75,0xe0,0xd7,0x38,0x11,0x71,0x30,0x9d,0x02,0x20,0x74,0x0f,0x7c,0x49,0x44,0x18,0xe1,0x30,0x0d,0xd4,0x51,0x2f,0x78,0x2a,0x58,0x80,0x0b,0xff,0x6a,0x7a,0xbd,0xfd,0xd2,0x0f,0xbb,0xd4,0xf0,0x55,0x15,0xca,0x1a,0x4f, + 0x30,0x44,0x02,0x20,0x68,0x52,0xe9,0xd3,0xcd,0x9f,0xe3,0x73,0xc2,0xd5,0x04,0x87,0x79,0x67,0xd3,0x65,0xab,0x14,0x56,0x70,0x7b,0x68,0x17,0xa0,0x42,0x86,0x46,0x94,0xe1,0x96,0x0c,0xcf,0x02,0x20,0x06,0x4b,0x27,0xea,0x14,0x2b,0x30,0x88,0x7b,0x84,0xc8,0x6a,0xdc,0xcb,0x2f,0xa3,0x9a,0x69,0x11,0xad,0x21,0xfc,0x7e,0x81,0x9f,0x59,0x3b,0xe5,0x2b,0xc4,0xf3,0xbd, + 0x30,0x44,0x02,0x20,0x18,0x8a,0x8c,0x56,0x48,0xdc,0x79,0xea,0xce,0x15,0x8c,0xf8,0x86,0xc6,0x2b,0x54,0x68,0xf0,0x5f,0xd9,0x5f,0x03,0xa7,0x63,0x5c,0x5b,0x4c,0x31,0xf0,0x9a,0xf4,0xc5,0x02,0x20,0x36,0x36,0x1a,0x0b,0x57,0x1a,0x00,0xc6,0xcd,0x5e,0x68,0x6c,0xcb,0xfc,0xfa,0x70,0x3c,0x4f,0x97,0xe4,0x89,0x38,0x34,0x6d,0x0c,0x10,0x3f,0xdc,0x76,0xdc,0x58,0x67, + 0x30,0x45,0x02,0x21,0x00,0xa7,0x4f,0x1f,0xb9,0xa8,0x26,0x3f,0x62,0xfc,0x44,0x16,0xa5,0xb7,0xd5,0x84,0xf4,0x20,0x6f,0x39,0x96,0xbb,0x91,0xf6,0xfc,0x8e,0x73,0xb9,0xe9,0x2b,0xad,0x0e,0x13,0x02,0x20,0x68,0x15,0x03,0x2e,0x8c,0x7d,0x76,0xc3,0xab,0x06,0xa8,0x6f,0x33,0x24,0x9c,0xe9,0x94,0x01,0x48,0xcb,0x36,0xd1,0xf4,0x17,0xc2,0xe9,0x92,0xe8,0x01,0xaf,0xa3,0xfa, + 0x30,0x44,0x02,0x20,0x07,0x24,0x48,0x65,0xb7,0x2f,0xf3,0x7e,0x62,0xe3,0x14,0x6f,0x0d,0xc1,0x46,0x82,0xba,0xdd,0x71,0x97,0x79,0x91,0x35,0xf0,0xb0,0x0a,0xde,0x76,0x71,0x74,0x2b,0xfe,0x02,0x20,0x0d,0x80,0xc2,0x23,0x8e,0xdb,0x4e,0x4a,0x7a,0x86,0xa8,0xc5,0x7c,0xa9,0xaf,0x17,0x11,0xf4,0x06,0xf7,0xf5,0xda,0x02,0x99,0xaa,0x04,0xe2,0x93,0x2d,0x96,0x07,0x54, + 0x30,0x45,0x02,0x21,0x00,0xda,0x7f,0xdd,0x05,0xb5,0xba,0xda,0xbd,0x61,0x9d,0x80,0x5c,0x4e,0xe7,0xd9,0xa8,0x4f,0x84,0xdd,0xd5,0xcf,0x9c,0x5b,0xf4,0xd4,0x33,0x81,0x40,0xd6,0x89,0xef,0x08,0x02,0x20,0x28,0xf1,0xcf,0x4f,0xa1,0xc3,0xc5,0x86,0x2c,0xfa,0x14,0x9c,0x00,0x13,0xcf,0x5f,0xe6,0xcf,0x50,0x76,0xca,0xe0,0x00,0x51,0x10,0x63,0xe7,0xde,0x25,0xbb,0x38,0xe5, + 0x30,0x45,0x02,0x21,0x00,0xd3,0x02,0x7c,0x65,0x6f,0x6d,0x4f,0xdf,0xd8,0xed,0xe2,0x20,0x93,0xe3,0xc3,0x03,0xb0,0x13,0x3c,0x34,0x0d,0x61,0x5e,0x77,0x56,0xf6,0x25,0x3a,0xea,0x92,0x72,0x38,0x02,0x20,0x09,0xae,0xf0,0x60,0xc8,0xe4,0xce,0xf9,0x72,0x97,0x40,0x11,0x55,0x8d,0xf1,0x44,0xfe,0xd2,0x5c,0xa6,0x9a,0xe8,0xd0,0xb2,0xea,0xf1,0xa8,0xfe,0xef,0xbe,0xc4,0x17, + 0x30,0x44,0x02,0x20,0x0b,0xf6,0xc0,0x18,0x8d,0xc9,0x57,0x1c,0xd0,0xe2,0x1e,0xec,0xac,0x5f,0xbb,0x19,0xd2,0x43,0x49,0x88,0xe9,0xcc,0x10,0x24,0x45,0x93,0xef,0x3a,0x98,0x09,0x9f,0x69,0x02,0x20,0x48,0x64,0xa5,0x62,0x66,0x1f,0x92,0x21,0xec,0x88,0xe3,0xdd,0x0b,0xc2,0xf6,0xe2,0x7a,0xc1,0x28,0xc3,0x0c,0xc1,0xa8,0x0f,0x79,0xec,0x67,0x0a,0x22,0xb0,0x42,0xee, + 0x30,0x45,0x02,0x21,0x00,0xae,0x45,0x96,0x40,0xd5,0xd1,0x17,0x9b,0xe4,0x7a,0x47,0xfa,0x53,0x8e,0x16,0xd9,0x4d,0xde,0xa5,0x58,0x5e,0x7a,0x24,0x48,0x04,0xa5,0x17,0x42,0xc6,0x86,0x44,0x3a,0x02,0x20,0x6c,0x8e,0x30,0xe5,0x30,0xa6,0x34,0xfa,0xe8,0x0b,0x3c,0xeb,0x06,0x29,0x78,0xb3,0x9e,0xdb,0xe1,0x97,0x77,0xe0,0xa2,0x45,0x53,0xb6,0x88,0x86,0x18,0x1f,0xd8,0x97, + 0x30,0x44,0x02,0x20,0x1c,0xf3,0x51,0x7b,0xa3,0xbf,0x2a,0xb8,0xb9,0xea,0xd4,0xeb,0xb6,0xe8,0x66,0xcb,0x88,0xa1,0xde,0xac,0xb6,0xa7,0x85,0xd3,0xb6,0x3b,0x48,0x3c,0xa0,0x2a,0xc4,0x95,0x02,0x20,0x24,0x9a,0x79,0x8b,0x73,0x60,0x6f,0x55,0xf5,0xf1,0xc7,0x0d,0xe6,0x7c,0xb1,0xa0,0xcf,0xf9,0x5d,0x7d,0xc5,0x0b,0x3a,0x61,0x7d,0xf8,0x61,0xba,0xd3,0xc6,0xb1,0xc9, + 0x30,0x45,0x02,0x21,0x00,0xe6,0x9b,0x52,0x38,0x26,0x5e,0xa3,0x5d,0x77,0xe4,0xdd,0x17,0x22,0x88,0xd8,0xce,0xa1,0x98,0x10,0xa1,0x02,0x92,0x61,0x7d,0x59,0x76,0x51,0x9d,0xc5,0x75,0x7c,0xb8,0x02,0x20,0x4b,0x03,0xc5,0xbc,0x47,0xe8,0x26,0xbd,0xb2,0x73,0x28,0xab,0xd3,0x8d,0x30,0x56,0xd7,0x74,0x76,0xb2,0x13,0x0f,0x3d,0xf6,0xec,0x48,0x91,0xaf,0x08,0xba,0x1e,0x29, + 0x30,0x44,0x02,0x20,0x5f,0x9d,0x7d,0x7c,0x87,0x0d,0x08,0x5f,0xc1,0xd4,0x9f,0xff,0x69,0xe4,0xa2,0x75,0x81,0x28,0x00,0xd2,0xcf,0x89,0x73,0xe7,0x32,0x58,0x66,0xcb,0x40,0xfa,0x2b,0x6f,0x02,0x20,0x6d,0x1f,0x54,0x91,0xd9,0xf7,0x17,0xa5,0x97,0xa1,0x5f,0xd5,0x40,0x40,0x64,0x86,0xd7,0x6a,0x44,0x69,0x7b,0x3f,0x0d,0x9d,0x6d,0xce,0xf6,0x66,0x9f,0x8a,0x0a,0x56, + 0x30,0x44,0x02,0x20,0x0a,0x7d,0x5b,0x19,0x59,0xf7,0x1d,0xf9,0xf8,0x17,0x14,0x6e,0xe4,0x9b,0xd5,0xc8,0x9b,0x43,0x1e,0x79,0x93,0xe2,0xfd,0xec,0xab,0x68,0x58,0x95,0x7d,0xa6,0x85,0xae,0x02,0x20,0x0f,0x8a,0xad,0x2d,0x25,0x46,0x90,0xbd,0xc1,0x3f,0x34,0xa4,0xfe,0xc4,0x4a,0x02,0xfd,0x74,0x5a,0x42,0x2d,0xf0,0x5c,0xcb,0xb5,0x46,0x35,0xa8,0xb8,0x6b,0x96,0x09, + 0x30,0x44,0x02,0x20,0x79,0xe8,0x8b,0xf5,0x76,0xb7,0x4b,0xc0,0x7c,0xa1,0x42,0x39,0x5f,0xda,0x28,0xf0,0x3d,0x3d,0x5e,0x64,0x0b,0x0b,0x4f,0xf0,0x75,0x2c,0x6d,0x94,0xcd,0x55,0x34,0x08,0x02,0x20,0x32,0xce,0xa0,0x5b,0xd2,0xd7,0x06,0xc8,0xf6,0x03,0x6a,0x50,0x7e,0x2a,0xb7,0x76,0x60,0x04,0xf0,0x90,0x4e,0x2e,0x5c,0x58,0x62,0x74,0x9c,0x00,0x73,0x24,0x5d,0x6a, + 0x30,0x45,0x02,0x21,0x00,0x9d,0x54,0xe0,0x37,0xa0,0x02,0x12,0xb3,0x77,0xbc,0x88,0x74,0x79,0x8b,0x8d,0xa0,0x80,0x56,0x4b,0xbd,0xf7,0xe0,0x75,0x91,0xb8,0x61,0x28,0x58,0x09,0xd0,0x14,0x88,0x02,0x20,0x18,0xb4,0xe5,0x57,0x66,0x7a,0x82,0xbd,0x95,0x96,0x5f,0x07,0x06,0xf8,0x1a,0x29,0x24,0x3f,0xbd,0xd8,0x69,0x68,0xa7,0xeb,0xeb,0x43,0x06,0x9d,0xb3,0xb1,0x8c,0x7f, + 0x30,0x44,0x02,0x20,0x26,0x64,0xf1,0xff,0xa9,0x82,0xfe,0xdb,0xcc,0x7c,0xab,0x1b,0x8b,0xc6,0xe2,0xcb,0x42,0x02,0x18,0xd2,0xa6,0x07,0x7a,0xd0,0x8e,0x59,0x1b,0xa9,0xfe,0xab,0x33,0xbd,0x02,0x20,0x49,0xf5,0xc7,0xcb,0x51,0x5e,0x83,0x87,0x2a,0x3d,0x41,0xb4,0xcd,0xb8,0x5f,0x24,0x2a,0xd9,0xd6,0x1a,0x5b,0xfc,0x01,0xde,0xbf,0xbb,0x52,0xc6,0xc8,0x4b,0xa7,0x28, + 0x30,0x44,0x02,0x20,0x58,0x27,0x51,0x83,0x44,0x84,0x4f,0xd6,0xa7,0xde,0x73,0xcb,0xb0,0xa6,0xbe,0xfd,0xea,0x7b,0x13,0xd2,0xde,0xe4,0x47,0x53,0x17,0xf0,0xf1,0x8f,0xfc,0x81,0x52,0x4b,0x02,0x20,0x4f,0x5c,0xcb,0x4e,0x0b,0x48,0x8b,0x5a,0x5d,0x76,0x0a,0xac,0xdd,0xb2,0xd7,0x91,0x97,0x0f,0xe4,0x3d,0xa6,0x1e,0xb3,0x0e,0x2e,0x90,0x20,0x8a,0x81,0x7e,0x46,0xdb, + 0x30,0x45,0x02,0x21,0x00,0x97,0xab,0x19,0xbd,0x13,0x9c,0xac,0x31,0x93,0x25,0x86,0x92,0x18,0xb1,0xbc,0xe1,0x11,0x87,0x5d,0x63,0xfb,0x12,0x09,0x8a,0x04,0xb0,0xcd,0x59,0xb6,0xfd,0xd3,0xa3,0x02,0x20,0x43,0x1d,0x9c,0xea,0x3a,0x24,0x38,0x47,0x30,0x3c,0xeb,0xda,0x56,0x47,0x64,0x31,0xd0,0x34,0x33,0x9f,0x31,0xd7,0x85,0xee,0x88,0x52,0xdb,0x4f,0x04,0x0d,0x49,0x21, + 0x30,0x44,0x02,0x20,0x52,0xc6,0x83,0x14,0x4e,0x44,0x11,0x9a,0xe2,0x01,0x37,0x49,0xd4,0x96,0x4e,0xf6,0x75,0x09,0x27,0x8f,0x6d,0x38,0xba,0x86,0x9a,0xdc,0xfa,0x69,0x97,0x0e,0x12,0x3d,0x02,0x20,0x34,0x79,0x91,0x01,0x67,0x40,0x8f,0x45,0xbd,0xa4,0x20,0xa6,0x26,0xec,0x9c,0x4e,0xc7,0x11,0xc1,0x27,0x4b,0xe0,0x92,0x19,0x8b,0x41,0x87,0xc0,0x18,0xb5,0x62,0xca, + 0x30,0x16,0x02,0x11,0x01,0x45,0x51,0x23,0x19,0x50,0xb7,0x5f,0xc4,0x40,0x2d,0xa1,0x72,0x2f,0xc9,0xba,0xeb,0x02,0x01,0x03, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xfc,0x2c,0x02,0x01,0x03, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x3f,0x02,0x01,0x03, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x3e,0x9a,0x75,0x82,0x88,0x60,0x89,0xc6,0x2f,0xb8,0x40,0xcf,0x3b,0x83,0x06,0x1c,0xd1,0xcf,0xf3,0xae,0x43,0x41,0x80,0x8b,0xb5,0xbd,0xee,0x61,0x91,0x17,0x41,0x77, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x24,0x23,0x8e,0x70,0xb4,0x31,0xb1,0xa6,0x4e,0xfd,0xf9,0x03,0x26,0x69,0x93,0x9d,0x4b,0x77,0xf2,0x49,0x50,0x3f,0xc6,0x90,0x5f,0xeb,0x75,0x40,0xde,0xa3,0xe6,0xd2, + 0x30,0x06,0x02,0x01,0x01,0x02,0x01,0x01, + 0x30,0x06,0x02,0x01,0x01,0x02,0x01,0x02, + 0x30,0x06,0x02,0x01,0x01,0x02,0x01,0x03, + 0x30,0x06,0x02,0x01,0x02,0x02,0x01,0x01, + 0x30,0x06,0x02,0x01,0x02,0x02,0x01,0x02, + 0x30,0x06,0x02,0x01,0x02,0x02,0x01,0x03, + 0x30,0x26,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x41,0x43,0x02,0x01,0x03, + 0x30,0x08,0x02,0x01,0x02,0x02,0x03,0xed,0x29,0x79, + 0x30,0x26,0x02,0x02,0x01,0x01,0x02,0x20,0x3a,0x74,0xe9,0xd3,0xa7,0x4e,0x9d,0x3a,0x74,0xe9,0xd3,0xa7,0x4e,0x9d,0x3a,0x74,0x9f,0x8a,0xb3,0x73,0x2a,0x0a,0x89,0x60,0x4a,0x09,0xbc,0xe5,0xb2,0x91,0x6d,0xa4, + 0x30,0x2b,0x02,0x07,0x2d,0x9b,0x4d,0x34,0x79,0x52,0xcc,0x02,0x20,0x03,0x43,0xae,0xfc,0x2f,0x25,0xd9,0x8b,0x88,0x2e,0x86,0xeb,0x9e,0x30,0xd5,0x5a,0x6e,0xb5,0x08,0xb5,0x16,0x51,0x0b,0x34,0x02,0x4a,0xe4,0xb6,0x36,0x23,0x30,0xb3, + 0x30,0x31,0x02,0x0d,0x10,0x33,0xe6,0x7e,0x37,0xb3,0x2b,0x44,0x55,0x80,0xbf,0x4e,0xfc,0x02,0x20,0x6f,0x90,0x6f,0x90,0x6f,0x90,0x6f,0x90,0x6f,0x90,0x6f,0x90,0x6f,0x90,0x6f,0x8f,0xe1,0xca,0xb5,0xee,0xfd,0xb2,0x14,0x06,0x1d,0xce,0x3b,0x22,0x78,0x9f,0x1d,0x6f, + 0x30,0x26,0x02,0x02,0x01,0x01,0x02,0x20,0x78,0x32,0x66,0xe9,0x0f,0x43,0xda,0xfe,0x5c,0xd9,0xb3,0xb0,0xbe,0x86,0xde,0x22,0xf9,0xde,0x83,0x67,0x7d,0x0f,0x50,0x71,0x3a,0x46,0x8e,0xc7,0x2f,0xcf,0x5d,0x57, + 0x30,0x31,0x02,0x0d,0x06,0x25,0x22,0xbb,0xd3,0xec,0xbe,0x7c,0x39,0xe9,0x3e,0x7c,0x26,0x02,0x20,0x78,0x32,0x66,0xe9,0x0f,0x43,0xda,0xfe,0x5c,0xd9,0xb3,0xb0,0xbe,0x86,0xde,0x22,0xf9,0xde,0x83,0x67,0x7d,0x0f,0x50,0x71,0x3a,0x46,0x8e,0xc7,0x2f,0xcf,0x5d,0x57, + 0x30,0x45,0x02,0x21,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,0xae,0xdc,0xe6,0xaf,0x48,0xa0,0x3b,0xbf,0xd2,0x5e,0x8c,0xd0,0x36,0x40,0xc1,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc0, + 0x30,0x16,0x02,0x09,0x00,0x9c,0x44,0xfe,0xbf,0x31,0xc3,0x59,0x4d,0x02,0x09,0x00,0x83,0x9e,0xd2,0x82,0x47,0xc2,0xb0,0x6b, + 0x30,0x1e,0x02,0x0d,0x09,0xdf,0x8b,0x68,0x24,0x30,0xbe,0xef,0x6f,0x5f,0xd7,0xc7,0xcf,0x02,0x0d,0x0f,0xd0,0xa6,0x2e,0x13,0x77,0x8f,0x42,0x22,0xa0,0xd6,0x1c,0x8a, + 0x30,0x26,0x02,0x11,0x00,0x8a,0x59,0x8e,0x56,0x3a,0x89,0xf5,0x26,0xc3,0x2e,0xbe,0xc8,0xde,0x26,0x36,0x7a,0x02,0x11,0x00,0x84,0xf6,0x33,0xe2,0x04,0x26,0x30,0xe9,0x9d,0xd0,0xf1,0xe1,0x6f,0x7a,0x04,0xbf, + 0x30,0x2e,0x02,0x15,0x00,0xaa,0x6e,0xeb,0x58,0x23,0xf7,0xfa,0x31,0xb4,0x66,0xbb,0x47,0x37,0x97,0xf0,0xd0,0x31,0x4c,0x0b,0xdf,0x02,0x15,0x00,0xe2,0x97,0x7c,0x47,0x9e,0x6d,0x25,0x70,0x3c,0xeb,0xbc,0x6b,0xd5,0x61,0x93,0x8c,0xc9,0xd1,0xbf,0xb9, + 0x30,0x25,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x01,0x01, + 0x30,0x25,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x01,0x00, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x41,0x9d,0x98,0x1c,0x51,0x5a,0xf8,0xcc,0x82,0x54,0x5a,0xac,0x0c,0x85,0xe9,0xe3,0x08,0xfb,0xb2,0xea,0xb6,0xac,0xd7,0xed,0x49,0x7e,0x0b,0x41,0x45,0xa1,0x8f,0xd9, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x1b,0x21,0x71,0x7a,0xd7,0x1d,0x23,0xbb,0xac,0x60,0xa9,0xad,0x0b,0xaf,0x75,0xb0,0x63,0xc9,0xfd,0xf5,0x2a,0x00,0xeb,0xf9,0x9d,0x02,0x21,0x72,0x91,0x09,0x93,0xc9, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x2f,0x58,0x8f,0x66,0x01,0x8f,0x3d,0xd1,0x4d,0xb3,0xe2,0x8e,0x77,0x99,0x64,0x87,0xe3,0x24,0x86,0xb5,0x21,0xed,0x8e,0x5a,0x20,0xf0,0x65,0x91,0x95,0x17,0x77,0xe9, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x09,0x1a,0x08,0x87,0x0f,0xf4,0xda,0xf9,0x12,0x3b,0x30,0xc2,0x0e,0x8c,0x4f,0xc8,0x50,0x57,0x58,0xdc,0xf4,0x07,0x4f,0xca,0xff,0x21,0x70,0xc9,0xbf,0xcf,0x74,0xf4, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x7c,0x37,0x0d,0xc0,0xce,0x8c,0x59,0xa8,0xb2,0x73,0xcb,0xa4,0x4a,0x7c,0x11,0x91,0xfc,0x31,0x86,0xdc,0x03,0xca,0xb9,0x6b,0x05,0x67,0x31,0x2d,0xf0,0xd0,0xb2,0x50, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x70,0xb5,0x9a,0x7d,0x1e,0xe7,0x7a,0x2f,0x9e,0x04,0x91,0xc2,0xa7,0xcf,0xcd,0x0e,0xd0,0x4d,0xf4,0xa3,0x51,0x92,0xf6,0x13,0x2d,0xcc,0x66,0x8c,0x79,0xa6,0x16,0x0e, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x27,0x36,0xd7,0x6e,0x41,0x22,0x46,0xe0,0x97,0x14,0x8e,0x2b,0xf6,0x29,0x15,0x61,0x4e,0xb7,0xc4,0x28,0x91,0x3a,0x58,0xeb,0x5e,0x9c,0xd4,0x67,0x4a,0x94,0x23,0xde, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x4a,0x1e,0x12,0x83,0x1f,0xbe,0x93,0x62,0x7b,0x02,0xd6,0xe7,0xf2,0x4b,0xcc,0xdd,0x6e,0xf4,0xb2,0xd0,0xf4,0x67,0x39,0xea,0xf3,0xb1,0xea,0xf0,0xca,0x11,0x77,0x70, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x06,0xc7,0x78,0xd4,0xdf,0xff,0x7d,0xee,0x06,0xed,0x88,0xbc,0x4e,0x0e,0xd3,0x4f,0xc5,0x53,0xaa,0xd6,0x7c,0xaf,0x79,0x6f,0x2a,0x1c,0x64,0x87,0xc1,0xb2,0xe8,0x77, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x4d,0xe4,0x59,0xef,0x91,0x59,0xaf,0xa0,0x57,0xfe,0xb3,0xec,0x40,0xfe,0xf0,0x1c,0x45,0xb8,0x09,0xf4,0xab,0x29,0x6e,0xa4,0x8c,0x20,0x6d,0x42,0x49,0xa2,0xb4,0x51, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x74,0x5d,0x29,0x49,0x78,0x00,0x73,0x02,0x03,0x35,0x02,0xe1,0xac,0xc4,0x8b,0x63,0xae,0x65,0x00,0xbe,0x43,0xad,0xbe,0xa1,0xb2,0x58,0xd6,0xb4,0x23,0xdb,0xb4,0x16, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x7b,0x2a,0x78,0x5e,0x38,0x96,0xf5,0x9b,0x2d,0x69,0xda,0x57,0x64,0x8e,0x80,0xad,0x3c,0x13,0x3a,0x75,0x0a,0x28,0x47,0xfd,0x20,0x98,0xcc,0xd9,0x02,0x04,0x2b,0x6c, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x71,0xae,0x94,0xa7,0x2c,0xa8,0x96,0x87,0x5e,0x7a,0xa4,0xa4,0xc3,0xd2,0x9a,0xfd,0xb4,0xb3,0x5b,0x69,0x96,0x27,0x3e,0x63,0xc4,0x7a,0xc5,0x19,0x25,0x6c,0x5e,0xb1, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x0f,0xa5,0x27,0xfa,0x73,0x43,0xc0,0xbc,0x9e,0xc3,0x5a,0x62,0x78,0xbf,0xbf,0xf4,0xd8,0x33,0x01,0xb1,0x54,0xfc,0x4b,0xd1,0x4a,0xee,0x7e,0xb9,0x34,0x45,0xb5,0xf9, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc1,0x02,0x20,0x65,0x39,0xc0,0xad,0xad,0xd0,0x52,0x5f,0xf4,0x26,0x22,0x16,0x4c,0xe9,0x31,0x43,0x48,0xbd,0x08,0x63,0xb4,0xc8,0x0e,0x93,0x6b,0x23,0xca,0x04,0x14,0x26,0x46,0x71, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc0, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa1, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xb8,0x02,0x20,0x44,0xa5,0xad,0x0b,0xd0,0x63,0x6d,0x9e,0x12,0xbc,0x9e,0x0a,0x6b,0xdd,0x5e,0x1b,0xba,0x77,0xf5,0x23,0x84,0x21,0x93,0xb3,0xb8,0x2e,0x44,0x8e,0x05,0xd5,0xf1,0x1e, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xb8,0x02,0x20,0x44,0xa5,0xad,0x0b,0xd0,0x63,0x6d,0x9e,0x12,0xbc,0x9e,0x0a,0x6b,0xdd,0x5e,0x1b,0xba,0x77,0xf5,0x23,0x84,0x21,0x93,0xb3,0xb8,0x2e,0x44,0x8e,0x05,0xd5,0xf1,0x1e, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xb8,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xb8, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xb8,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xb8, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x16,0xe1,0xe4,0x59,0x45,0x76,0x79,0xdf,0x5b,0x94,0x34,0xae,0x23,0xf4,0x74,0xb3,0xe8,0xd2,0xa7,0x0b,0xd6,0xb5,0xdb,0xe6,0x92,0xba,0x16,0xda,0x01,0xf1,0xfb,0x0a, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x1c,0x94,0x0f,0x31,0x3f,0x92,0x64,0x7b,0xe2,0x57,0xec,0xcd,0x7e,0xd0,0x8b,0x0b,0xae,0xf3,0xf0,0x47,0x8f,0x25,0x87,0x1b,0x53,0x63,0x53,0x02,0xc5,0xf6,0x31,0x4a, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x15,0xd9,0x4a,0x85,0x07,0x7b,0x49,0x3f,0x91,0xcb,0x71,0x01,0xec,0x63,0xe1,0xb0,0x1b,0xe5,0x8b,0x59,0x4e,0x85,0x5f,0x45,0x05,0x0a,0x8c,0x14,0x06,0x2d,0x68,0x9b, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x5b,0x1d,0x27,0xa7,0x69,0x4c,0x14,0x62,0x44,0xa5,0xad,0x0b,0xd0,0x63,0x6d,0x9d,0x9e,0xf3,0xb9,0xfb,0x58,0x38,0x54,0x18,0xd9,0xc9,0x82,0x10,0x50,0x77,0xd1,0xb7, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x2d,0x85,0x89,0x6b,0x3e,0xb9,0xdb,0xb5,0xa5,0x2f,0x42,0xf9,0xc9,0x26,0x1e,0xd3,0xfc,0x46,0x64,0x4e,0xc6,0x5f,0x06,0xad,0xe3,0xfd,0x78,0xf2,0x57,0xe4,0x34,0x32, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x5b,0x0b,0x12,0xd6,0x7d,0x73,0xb7,0x6b,0x4a,0x5e,0x85,0xf3,0x92,0x4c,0x3d,0xa7,0xf8,0x8c,0xc8,0x9d,0x8c,0xbe,0x0d,0x5b,0xc7,0xfa,0xf1,0xe4,0xaf,0xc8,0x68,0x64, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x69,0x4c,0x14,0x62,0x44,0xa5,0xad,0x0b,0xd0,0x63,0x6d,0x9e,0x12,0xbc,0x9e,0x09,0xe6,0x0e,0x68,0xb9,0x0d,0x0b,0x5e,0x6c,0x5d,0xdd,0xd0,0xcb,0x69,0x4d,0x87,0x99, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x3d,0x7f,0x48,0x7c,0x07,0xbf,0xc5,0xf3,0x08,0x46,0x93,0x8a,0x3d,0xce,0xf6,0x96,0x44,0x47,0x07,0xcf,0x96,0x77,0x25,0x4a,0x92,0xb0,0x6c,0x63,0xab,0x86,0x7d,0x22, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x6c,0x76,0x48,0xfc,0x0f,0xbf,0x8a,0x06,0xad,0xb8,0xb8,0x39,0xf9,0x7b,0x4f,0xf7,0xa8,0x00,0xf1,0x1b,0x1e,0x37,0xc5,0x93,0xb2,0x61,0x39,0x45,0x99,0x79,0x2b,0xa4, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x64,0x1c,0x9c,0x5d,0x79,0x0d,0xc0,0x9c,0xdd,0x3d,0xfa,0xbb,0x62,0xcd,0xf4,0x53,0xe6,0x97,0x47,0xa7,0xe3,0xd7,0xaa,0x1a,0x71,0x41,0x89,0xef,0x53,0x17,0x1a,0x99, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x29,0x79,0x8c,0x5c,0x45,0xbd,0xf5,0x8b,0x4a,0x7b,0x2f,0xdc,0x2c,0x46,0xab,0x4a,0xf1,0x21,0x8c,0x7e,0xeb,0x9f,0x0f,0x27,0xa8,0x8f,0x12,0x67,0x67,0x4d,0xe3,0xb0, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x0b,0x70,0xf2,0x2c,0xa2,0xbb,0x3c,0xef,0xad,0xca,0x1a,0x57,0x11,0xfa,0x3a,0x59,0xf4,0x69,0x53,0x85,0xeb,0x5a,0xed,0xf3,0x49,0x5d,0x0b,0x6d,0x00,0xf8,0xfd,0x85, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x16,0xe1,0xe4,0x59,0x45,0x76,0x79,0xdf,0x5b,0x94,0x34,0xae,0x23,0xf4,0x74,0xb3,0xe8,0xd2,0xa7,0x0b,0xd6,0xb5,0xdb,0xe6,0x92,0xba,0x16,0xda,0x01,0xf1,0xfb,0x0a, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x22,0x52,0xd6,0x85,0xe8,0x31,0xb6,0xcf,0x09,0x5e,0x4f,0x05,0x35,0xee,0xaf,0x0d,0xdd,0x3b,0xfa,0x91,0xc2,0x10,0xc9,0xd9,0xdc,0x17,0x22,0x47,0x02,0xea,0xf8,0x8f, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x75,0x13,0x5a,0xbd,0x7c,0x42,0x5b,0x60,0x37,0x1a,0x47,0x7f,0x09,0xce,0x0f,0x27,0x4f,0x64,0xa8,0xc6,0xb0,0x61,0xa0,0x7b,0x5d,0x63,0xe9,0x3c,0x65,0x04,0x6c,0x53, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x2a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0x3e,0x3a,0x49,0xa2,0x3a,0x6d,0x8a,0xbe,0x95,0x46,0x1f,0x84,0x45,0x67,0x6b,0x17, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x3e,0x88,0x83,0x77,0xac,0x6c,0x71,0xac,0x9d,0xec,0x3f,0xdb,0x9b,0x56,0xc9,0xfe,0xaf,0x0c,0xfa,0xca,0x9f,0x82,0x7f,0xc5,0xeb,0x65,0xfc,0x3e,0xac,0x81,0x12,0x10, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x30,0xbb,0xb7,0x94,0xdb,0x58,0x83,0x63,0xb4,0x06,0x79,0xf6,0xc1,0x82,0xa5,0x0d,0x3c,0xe9,0x67,0x9a,0xcd,0xd3,0xff,0xbe,0x36,0xd7,0x81,0x3d,0xac,0xbd,0xc8,0x18, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x2c,0x37,0xfd,0x99,0x56,0x22,0xc4,0xfb,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc7,0xce,0xe7,0x45,0x11,0x0c,0xb4,0x5a,0xb5,0x58,0xed,0x7c,0x90,0xc1,0x5a,0x2f, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x7f,0xd9,0x95,0x62,0x2c,0x4f,0xb7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x5d,0x88,0x3f,0xfa,0xb5,0xb3,0x26,0x52,0xcc,0xdc,0xaa,0x29,0x0f,0xcc,0xb9,0x7d, + 0x30,0x43,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x1f,0x4c,0xd5,0x3b,0xa7,0x60,0x8f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x9e,0x5c,0xf1,0x43,0xe2,0x53,0x96,0x26,0x19,0x0a,0x3a,0xb0,0x9c,0xce,0x47, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x56,0x22,0xc4,0xfb,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x92,0x8a,0x8f,0x1c,0x7a,0xc7,0xbe,0xc1,0x80,0x8b,0x9f,0x61,0xc0,0x1e,0xc3,0x27, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x44,0x10,0x41,0x04,0x10,0x41,0x04,0x10,0x41,0x04,0x10,0x41,0x04,0x10,0x41,0x03,0xb8,0x78,0x53,0xfd,0x3b,0x7d,0x3f,0x8e,0x17,0x51,0x25,0xb4,0x38,0x2f,0x25,0xed, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x27,0x39,0xce,0x73,0x9c,0xe7,0x39,0xce,0x73,0x9c,0xe7,0x39,0xce,0x73,0x9c,0xe7,0x05,0x56,0x02,0x98,0xd1,0xf2,0xf0,0x8d,0xc4,0x19,0xac,0x27,0x3a,0x5b,0x54,0xd9, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x48,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x31,0xc8,0x3a,0xe8,0x2e,0xbe,0x08,0x98,0x77,0x6b,0x4c,0x69,0xd1,0x1f,0x88,0xde, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x64,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x06,0xdd,0x3a,0x19,0xb8,0xd5,0xfb,0x87,0x52,0x35,0x96,0x3c,0x59,0x3b,0xd2,0xd3, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x6a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0x3e,0x3a,0x49,0xa2,0x3a,0x6d,0x8a,0xbe,0x95,0x46,0x1f,0x84,0x45,0x67,0x6b,0x15, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x2a,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0x3e,0x3a,0x49,0xa2,0x3a,0x6d,0x8a,0xbe,0x95,0x46,0x1f,0x84,0x45,0x67,0x6b,0x17, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe, + 0x30,0x44,0x02,0x20,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x02,0x20,0x18,0x5d,0xdb,0xca,0x6d,0xac,0x41,0xb1,0xda,0x03,0x3c,0xfb,0x60,0xc1,0x52,0x86,0x9e,0x74,0xb3,0xcd,0x66,0xe9,0xff,0xdf,0x1b,0x6b,0xc0,0x9e,0xd6,0x5e,0xe4,0x0c, + 0x30,0x44,0x02,0x20,0x32,0xb0,0xd1,0x0d,0x8d,0x0e,0x04,0xbc,0x8d,0x4d,0x06,0x4d,0x27,0x06,0x99,0xe8,0x7c,0xff,0xc9,0xb4,0x9c,0x5c,0x20,0x73,0x0e,0x1c,0x26,0xf6,0x10,0x5d,0xdc,0xda,0x02,0x20,0x29,0xed,0x3d,0x67,0xb3,0xd5,0x05,0xbe,0x95,0x58,0x0d,0x77,0xd5,0xb7,0x92,0xb4,0x36,0x88,0x11,0x79,0xb2,0xb6,0xb2,0xe0,0x4c,0x5f,0xe5,0x92,0xd3,0x8d,0x82,0xd9, + 0x30,0x44,0x02,0x20,0x32,0xb0,0xd1,0x0d,0x8d,0x0e,0x04,0xbc,0x8d,0x4d,0x06,0x4d,0x27,0x06,0x99,0xe8,0x7c,0xff,0xc9,0xb4,0x9c,0x5c,0x20,0x73,0x0e,0x1c,0x26,0xf6,0x10,0x5d,0xdc,0xda,0x02,0x20,0x29,0xed,0x3d,0x67,0xb3,0xd5,0x05,0xbe,0x95,0x58,0x0d,0x77,0xd5,0xb7,0x92,0xb4,0x36,0x88,0x11,0x79,0xb2,0xb6,0xb2,0xe0,0x4c,0x5f,0xe5,0x92,0xd3,0x8d,0x82,0xd9, + 0x30,0x44,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc0,0x02,0x20,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x32,0xf2,0x22,0xf8,0xfa,0xef,0xdb,0x53,0x3f,0x26,0x5d,0x46,0x1c,0x29,0xa4,0x73,0x73, + 0x30,0x45,0x02,0x21,0x00,0xc6,0x04,0x7f,0x94,0x41,0xed,0x7d,0x6d,0x30,0x45,0x40,0x6e,0x95,0xc0,0x7c,0xd8,0x5c,0x77,0x8e,0x4b,0x8c,0xef,0x3c,0xa7,0xab,0xac,0x09,0xb9,0x5c,0x70,0x9e,0xe5,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc0, + 0x30,0x45,0x02,0x21,0x00,0xc6,0x04,0x7f,0x94,0x41,0xed,0x7d,0x6d,0x30,0x45,0x40,0x6e,0x95,0xc0,0x7c,0xd8,0x5c,0x77,0x8e,0x4b,0x8c,0xef,0x3c,0xa7,0xab,0xac,0x09,0xb9,0x5c,0x70,0x9e,0xe5,0x02,0x20,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x48,0xc7,0x9f,0xac,0xd4,0x32,0x14,0xc0,0x11,0x12,0x3c,0x1b,0x03,0xa9,0x34,0x12,0xa5, + 0x30,0x45,0x02,0x21,0x00,0xc6,0x04,0x7f,0x94,0x41,0xed,0x7d,0x6d,0x30,0x45,0x40,0x6e,0x95,0xc0,0x7c,0xd8,0x5c,0x77,0x8e,0x4b,0x8c,0xef,0x3c,0xa7,0xab,0xac,0x09,0xb9,0x5c,0x70,0x9e,0xe5,0x02,0x20,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x65,0xe4,0x45,0xf1,0xf5,0xdf,0xb6,0xa6,0x7e,0x4c,0xba,0x8c,0x38,0x53,0x48,0xe6,0xe7, + 0x30,0x45,0x02,0x21,0x00,0xc6,0x04,0x7f,0x94,0x41,0xed,0x7d,0x6d,0x30,0x45,0x40,0x6e,0x95,0xc0,0x7c,0xd8,0x5c,0x77,0x8e,0x4b,0x8c,0xef,0x3c,0xa7,0xab,0xac,0x09,0xb9,0x5c,0x70,0x9e,0xe5,0x02,0x20,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x65,0xe4,0x45,0xf1,0xf5,0xdf,0xb6,0xa6,0x7e,0x4c,0xba,0x8c,0x38,0x53,0x48,0xe6,0xe7, + 0x30,0x45,0x02,0x21,0x00,0xc6,0x04,0x7f,0x94,0x41,0xed,0x7d,0x6d,0x30,0x45,0x40,0x6e,0x95,0xc0,0x7c,0xd8,0x5c,0x77,0x8e,0x4b,0x8c,0xef,0x3c,0xa7,0xab,0xac,0x09,0xb9,0x5c,0x70,0x9e,0xe5,0x02,0x20,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x48,0xc7,0x9f,0xac,0xd4,0x32,0x14,0xc0,0x11,0x12,0x3c,0x1b,0x03,0xa9,0x34,0x12,0xa5, + 0x30,0x45,0x02,0x21,0x00,0xc6,0x04,0x7f,0x94,0x41,0xed,0x7d,0x6d,0x30,0x45,0x40,0x6e,0x95,0xc0,0x7c,0xd8,0x5c,0x77,0x8e,0x4b,0x8c,0xef,0x3c,0xa7,0xab,0xac,0x09,0xb9,0x5c,0x70,0x9e,0xe5,0x02,0x20,0x0e,0xb1,0x0e,0x5a,0xb9,0x5f,0x2f,0x27,0x53,0x48,0xd8,0x2a,0xd2,0xe4,0xd7,0x94,0x9c,0x81,0x93,0x80,0x0d,0x8c,0x9c,0x75,0xdf,0x58,0xe3,0x43,0xf0,0xeb,0xba,0x7b, + 0x30,0x44,0x02,0x20,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98,0x02,0x20,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54,0xe8,0xe4,0xf4,0x4c,0xe5,0x18,0x35,0x69,0x3f,0xf0,0xca,0x2e,0xf0,0x12,0x15,0xc0, + 0x30,0x44,0x02,0x20,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98,0x02,0x20,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x48,0xc7,0x9f,0xac,0xd4,0x32,0x14,0xc0,0x11,0x12,0x3c,0x1b,0x03,0xa9,0x34,0x12,0xa5, + 0x30,0x44,0x02,0x20,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98,0x02,0x20,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x65,0xe4,0x45,0xf1,0xf5,0xdf,0xb6,0xa6,0x7e,0x4c,0xba,0x8c,0x38,0x53,0x48,0xe6,0xe7, + 0x30,0x44,0x02,0x20,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98,0x02,0x20,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x65,0xe4,0x45,0xf1,0xf5,0xdf,0xb6,0xa6,0x7e,0x4c,0xba,0x8c,0x38,0x53,0x48,0xe6,0xe7, + 0x30,0x44,0x02,0x20,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98,0x02,0x20,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x48,0xc7,0x9f,0xac,0xd4,0x32,0x14,0xc0,0x11,0x12,0x3c,0x1b,0x03,0xa9,0x34,0x12,0xa5, + 0x30,0x44,0x02,0x20,0x79,0xbe,0x66,0x7e,0xf9,0xdc,0xbb,0xac,0x55,0xa0,0x62,0x95,0xce,0x87,0x0b,0x07,0x02,0x9b,0xfc,0xdb,0x2d,0xce,0x28,0xd9,0x59,0xf2,0x81,0x5b,0x16,0xf8,0x17,0x98,0x02,0x20,0x0e,0xb1,0x0e,0x5a,0xb9,0x5f,0x2f,0x27,0x53,0x48,0xd8,0x2a,0xd2,0xe4,0xd7,0x94,0x9c,0x81,0x93,0x80,0x0d,0x8c,0x9c,0x75,0xdf,0x58,0xe3,0x43,0xf0,0xeb,0xba,0x7b, + 0x30,0x45,0x02,0x21,0x00,0xbb,0x5a,0x52,0xf4,0x2f,0x9c,0x92,0x61,0xed,0x43,0x61,0xf5,0x94,0x22,0xa1,0xe3,0x00,0x36,0xe7,0xc3,0x2b,0x27,0x0c,0x88,0x07,0xa4,0x19,0xfe,0xca,0x60,0x50,0x23,0x02,0x20,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x63,0xcf,0xd6,0x6a,0x19,0x0a,0x60,0x08,0x89,0x1e,0x0d,0x81,0xd4,0x9a,0x09,0x52, + 0x30,0x44,0x02,0x20,0x44,0xa5,0xad,0x0b,0xd0,0x63,0x6d,0x9e,0x12,0xbc,0x9e,0x0a,0x6b,0xdd,0x5e,0x1b,0xba,0x77,0xf5,0x23,0x84,0x21,0x93,0xb3,0xb8,0x2e,0x44,0x8e,0x05,0xd5,0xf1,0x1e,0x02,0x20,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x63,0xcf,0xd6,0x6a,0x19,0x0a,0x60,0x08,0x89,0x1e,0x0d,0x81,0xd4,0x9a,0x09,0x52, + 0x30,0x45,0x02,0x21,0x00,0xbb,0x5a,0x52,0xf4,0x2f,0x9c,0x92,0x61,0xed,0x43,0x61,0xf5,0x94,0x22,0xa1,0xe3,0x00,0x36,0xe7,0xc3,0x2b,0x27,0x0c,0x88,0x07,0xa4,0x19,0xfe,0xca,0x60,0x50,0x23,0x02,0x20,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x63,0xcf,0xd6,0x6a,0x19,0x0a,0x60,0x08,0x89,0x1e,0x0d,0x81,0xd4,0x9a,0x09,0x52, + 0x30,0x44,0x02,0x20,0x44,0xa5,0xad,0x0b,0xd0,0x63,0x6d,0x9e,0x12,0xbc,0x9e,0x0a,0x6b,0xdd,0x5e,0x1b,0xba,0x77,0xf5,0x23,0x84,0x21,0x93,0xb3,0xb8,0x2e,0x44,0x8e,0x05,0xd5,0xf1,0x1e,0x02,0x20,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x92,0x49,0x24,0x63,0xcf,0xd6,0x6a,0x19,0x0a,0x60,0x08,0x89,0x1e,0x0d,0x81,0xd4,0x9a,0x09,0x52, + 0x30,0x45,0x02,0x21,0x00,0xf8,0x0a,0xe4,0xf9,0x6c,0xdb,0xc9,0xd8,0x53,0xf8,0x3d,0x47,0xaa,0xe2,0x25,0xbf,0x40,0x7d,0x51,0xc5,0x6b,0x77,0x76,0xcd,0x67,0xd0,0xdc,0x19,0x5d,0x99,0xa9,0xdc,0x02,0x20,0x4c,0xfc,0x1d,0x94,0x1e,0x08,0xcb,0x9a,0xce,0xad,0xde,0x0f,0x4c,0xce,0xad,0x76,0xb3,0x0d,0x33,0x2f,0xc4,0x42,0x11,0x5d,0x50,0xe6,0x73,0xe2,0x86,0x86,0xb7,0x0b, + 0x30,0x44,0x02,0x20,0x10,0x9c,0xd8,0xae,0x03,0x74,0x35,0x89,0x84,0xa8,0x24,0x9c,0x0a,0x84,0x36,0x28,0xf2,0x83,0x5f,0xfa,0xd1,0xdf,0x1a,0x9a,0x69,0xaa,0x2f,0xe7,0x23,0x55,0x54,0x5c,0x02,0x20,0x53,0x90,0xff,0x25,0x0a,0xc4,0x27,0x4e,0x1c,0xb2,0x5c,0xd6,0xca,0x64,0x91,0xf6,0xb9,0x12,0x81,0xe3,0x2f,0x5b,0x26,0x4d,0x87,0x97,0x7a,0xed,0x4a,0x94,0xe7,0x7b, + 0x30,0x45,0x02,0x21,0x00,0xd0,0x35,0xee,0x1f,0x17,0xfd,0xb0,0xb2,0x68,0x1b,0x16,0x3e,0x33,0xc3,0x59,0x93,0x26,0x59,0x99,0x0a,0xf7,0x7d,0xca,0x63,0x20,0x12,0xb3,0x0b,0x27,0xa0,0x57,0xb3,0x02,0x20,0x19,0x39,0xd9,0xf3,0xb2,0x85,0x8b,0xc1,0x3e,0x34,0x74,0xcb,0x50,0xe6,0xa8,0x2b,0xe4,0x4f,0xaa,0x71,0x94,0x0f,0x87,0x6c,0x1c,0xba,0x4c,0x3e,0x98,0x92,0x02,0xb6, + 0x30,0x44,0x02,0x20,0x4f,0x05,0x3f,0x56,0x3a,0xd3,0x4b,0x74,0xfd,0x8c,0x99,0x34,0xce,0x59,0xe7,0x9c,0x2e,0xb8,0xe6,0xec,0xa0,0xfe,0xf5,0xb3,0x23,0xca,0x67,0xd5,0xac,0x7e,0xd2,0x38,0x02,0x20,0x4d,0x4b,0x05,0xda,0xa0,0x71,0x9e,0x77,0x3d,0x86,0x17,0xdc,0xe5,0x63,0x1c,0x5f,0xd6,0xf5,0x9c,0x9b,0xdc,0x74,0x8e,0x4b,0x55,0xc9,0x70,0x04,0x0a,0xf0,0x1b,0xe5, + 0x30,0x44,0x02,0x20,0x6d,0x6a,0x4f,0x55,0x6c,0xcc,0xe1,0x54,0xe7,0xfb,0x9f,0x19,0xe7,0x6c,0x3d,0xec,0xa1,0x3d,0x59,0xcc,0x2a,0xeb,0x4e,0xca,0xd9,0x68,0xaa,0xb2,0xde,0xd4,0x59,0x65,0x02,0x20,0x53,0xb9,0xfa,0x74,0x80,0x3e,0xde,0x0f,0xc4,0x44,0x1b,0xf6,0x83,0xd5,0x6c,0x56,0x4d,0x3e,0x27,0x4e,0x09,0xcc,0xf4,0x73,0x90,0xba,0xdd,0x14,0x71,0xc0,0x5f,0xb7, + 0x30,0x44,0x02,0x21,0x00,0xaa,0xd5,0x03,0xde,0x9b,0x9f,0xd6,0x6b,0x94,0x8e,0x9a,0xcf,0x59,0x6f,0x0a,0x0e,0x65,0xe7,0x00,0xb2,0x8b,0x26,0xec,0x56,0xe6,0xe4,0x5e,0x84,0x64,0x89,0xb3,0xc4,0x02,0x1f,0x0d,0xdc,0x3a,0x2f,0x89,0xab,0xb8,0x17,0xbb,0x85,0xc0,0x62,0xce,0x02,0xf8,0x23,0xc6,0x3f,0xc2,0x6b,0x26,0x9e,0x0b,0xc9,0xb8,0x4d,0x81,0xa5,0xaa,0x12,0x3d, + 0x30,0x45,0x02,0x21,0x00,0x91,0x82,0xce,0xbd,0x3b,0xb8,0xab,0x57,0x2e,0x16,0x71,0x74,0x39,0x72,0x09,0xef,0x4b,0x1d,0x43,0x9a,0xf3,0xb2,0x00,0xcd,0xf0,0x03,0x62,0x00,0x89,0xe4,0x32,0x25,0x02,0x20,0x54,0x47,0x7c,0x98,0x2e,0xa0,0x19,0xd2,0xe1,0x00,0x04,0x97,0xfc,0x25,0xfc,0xee,0x1b,0xcc,0xae,0x55,0xf2,0xac,0x27,0x53,0x0a,0xe5,0x3b,0x29,0xc4,0xb3,0x56,0xa4, + 0x30,0x44,0x02,0x20,0x38,0x54,0xa3,0x99,0x8a,0xeb,0xdf,0x2d,0xbc,0x28,0xad,0xac,0x41,0x81,0x46,0x2c,0xca,0xc7,0x87,0x39,0x07,0xab,0x7f,0x21,0x2c,0x42,0xdb,0x0e,0x69,0xb5,0x6e,0xd8,0x02,0x20,0x3e,0xd3,0xf6,0xb8,0xa3,0x88,0xd0,0x2f,0x3e,0x4d,0xf9,0xf2,0xae,0x9c,0x1b,0xd2,0xc3,0x91,0x6a,0x68,0x64,0x60,0xdf,0xfc,0xd4,0x29,0x09,0xcd,0x7f,0x82,0x05,0x8e, + 0x30,0x45,0x02,0x21,0x00,0xe9,0x4d,0xbd,0xc3,0x87,0x95,0xfe,0x5c,0x90,0x4d,0x8f,0x16,0xd9,0x69,0xd3,0xb5,0x87,0xf0,0xa2,0x5d,0x2d,0xe9,0x0b,0x6d,0x8c,0x5c,0x53,0xff,0x88,0x7e,0x36,0x07,0x02,0x20,0x7a,0x94,0x73,0x69,0xc1,0x64,0x97,0x25,0x21,0xbb,0x8a,0xf4,0x06,0x81,0x3b,0x2d,0x9f,0x94,0xd2,0xae,0xaa,0x53,0xd4,0xc2,0x15,0xaa,0xa0,0xa2,0x57,0x8a,0x2c,0x5d, + 0x30,0x44,0x02,0x20,0x49,0xfc,0x10,0x2a,0x08,0xca,0x47,0xb6,0x0e,0x08,0x58,0xcd,0x02,0x84,0xd2,0x2c,0xdd,0xd7,0x23,0x3f,0x94,0xaa,0xff,0xbb,0x2d,0xb1,0xdd,0x2c,0xf0,0x84,0x25,0xe1,0x02,0x20,0x5b,0x16,0xfc,0xa5,0xa1,0x2c,0xdb,0x39,0x70,0x16,0x97,0xad,0x8e,0x39,0xff,0xd6,0xbd,0xec,0x00,0x24,0x29,0x8a,0xfa,0xa2,0x32,0x6a,0xea,0x09,0x20,0x0b,0x14,0xd6, + 0x30,0x44,0x02,0x20,0x41,0xef,0xa7,0xd3,0xf0,0x5a,0x00,0x10,0x67,0x5f,0xcb,0x91,0x8a,0x45,0xc6,0x93,0xda,0x4b,0x34,0x8d,0xf2,0x1a,0x59,0xd6,0xf9,0xcd,0x73,0xe0,0xd8,0x31,0xd6,0x7a,0x02,0x20,0x44,0x54,0xad,0xa6,0x93,0xe5,0xe2,0x6b,0x7b,0xd6,0x93,0x23,0x6d,0x34,0x0f,0x80,0x54,0x5c,0x83,0x45,0x77,0xb6,0xf7,0x3d,0x37,0x8c,0x7b,0xcc,0x53,0x42,0x44,0xda, + 0x30,0x45,0x02,0x21,0x00,0xb6,0x15,0x69,0x8c,0x35,0x8b,0x35,0x92,0x0d,0xd8,0x83,0xec,0xa6,0x25,0xa6,0xc5,0xf7,0x56,0x39,0x70,0xcd,0xfc,0x37,0x8f,0x8f,0xe0,0xce,0xe1,0x70,0x92,0x14,0x4c,0x02,0x20,0x25,0xf4,0x7b,0x32,0x6b,0x5b,0xe1,0xfb,0x61,0x0b,0x88,0x51,0x53,0xea,0x84,0xd4,0x1e,0xb4,0x71,0x6b,0xe6,0x6a,0x99,0x4e,0x87,0x79,0x98,0x9d,0xf1,0xc8,0x63,0xd4, + 0x30,0x45,0x02,0x21,0x00,0x87,0xcf,0x8c,0x0e,0xb8,0x2d,0x44,0xf6,0x9c,0x60,0xa2,0xff,0x54,0x57,0xd3,0xaa,0xa3,0x22,0xe7,0xec,0x61,0xae,0x5a,0xec,0xfd,0x67,0x8a,0xe1,0xc1,0x93,0x2b,0x0e,0x02,0x20,0x3a,0xdd,0x3b,0x11,0x58,0x15,0x04,0x7d,0x6e,0xb3,0x40,0xa3,0xe0,0x08,0x98,0x9e,0xaa,0x0f,0x87,0x08,0xd1,0x79,0x48,0x14,0x72,0x90,0x94,0xd0,0x8d,0x24,0x60,0xd3, + 0x30,0x44,0x02,0x20,0x62,0xf4,0x8e,0xf7,0x1a,0xce,0x27,0xbf,0x5a,0x01,0x83,0x4d,0xe1,0xf7,0xe3,0xf9,0x48,0xb9,0xdc,0xe1,0xca,0x1e,0x91,0x1d,0x5e,0x13,0xd3,0xb1,0x04,0x47,0x1d,0x82,0x02,0x20,0x5e,0xa8,0xf3,0x3f,0x0c,0x77,0x89,0x72,0xc4,0x58,0x20,0x80,0xde,0xda,0x9b,0x34,0x18,0x57,0xdd,0x64,0x51,0x4f,0x08,0x49,0xa0,0x5f,0x69,0x64,0xc2,0xe3,0x40,0x22, + 0x30,0x45,0x02,0x21,0x00,0xf6,0xb0,0xe2,0xf6,0xfe,0x02,0x0c,0xf7,0xc0,0xc2,0x01,0x37,0x43,0x43,0x44,0xed,0x7a,0xdd,0x6c,0x4b,0xe5,0x18,0x61,0xe2,0xd1,0x4c,0xbd,0xa4,0x72,0xa6,0xff,0xb4,0x02,0x20,0x64,0x16,0xc8,0xdd,0x3e,0x5c,0x52,0x82,0xb3,0x06,0xe8,0xdc,0x8f,0xf3,0x4a,0xb6,0x4c,0xc9,0x95,0x49,0x23,0x2d,0x67,0x8d,0x71,0x44,0x02,0xeb,0x6c,0xa7,0xaa,0x0f, + 0x30,0x45,0x02,0x21,0x00,0xdb,0x09,0xd8,0x46,0x0f,0x05,0xef,0xf2,0x3b,0xc7,0xe4,0x36,0xb6,0x7d,0xa5,0x63,0xfa,0x4b,0x4e,0xdb,0x58,0xac,0x24,0xce,0x20,0x1f,0xa8,0xa3,0x58,0x12,0x50,0x57,0x02,0x20,0x46,0xda,0x11,0x67,0x54,0x60,0x29,0x40,0xc8,0x99,0x9c,0x8d,0x66,0x5f,0x78,0x6c,0x50,0xf5,0x77,0x2c,0x0a,0x3c,0xdb,0xda,0x07,0x5e,0x77,0xea,0xbc,0x64,0xdf,0x16, + 0x30,0x44,0x02,0x20,0x59,0x2c,0x41,0xe1,0x65,0x17,0xf1,0x2f,0xca,0xbd,0x98,0x26,0x76,0x74,0xf9,0x74,0xb5,0x88,0xe9,0xf3,0x5d,0x35,0x40,0x6c,0x1a,0x7b,0xb2,0xed,0x1d,0x19,0xb7,0xb8,0x02,0x20,0x3e,0x65,0xa0,0x6b,0xd9,0xf8,0x3c,0xaa,0xeb,0x7b,0x00,0xf2,0x36,0x8d,0x7e,0x0d,0xec,0xe6,0xb1,0x22,0x21,0x26,0x9a,0x9b,0x5b,0x76,0x51,0x98,0xf8,0x40,0xa3,0xa1, + 0x30,0x45,0x02,0x21,0x00,0xbe,0x0d,0x70,0x88,0x7d,0x5e,0x40,0x82,0x1a,0x61,0xb6,0x80,0x47,0xde,0x4e,0xa0,0x3d,0xeb,0xfd,0xf5,0x1c,0xdf,0x4d,0x4b,0x19,0x55,0x58,0xb9,0x59,0xa0,0x32,0xb2,0x02,0x20,0x7d,0x99,0x4b,0x2d,0x8f,0x1d,0xbb,0xeb,0x13,0x53,0x4e,0xb3,0xf6,0xe5,0xdc,0xcd,0x85,0xf5,0xc4,0x13,0x3c,0x27,0xd9,0xe6,0x42,0x71,0xb1,0x82,0x6c,0xe1,0xf6,0x7d, + 0x30,0x45,0x02,0x21,0x00,0xfa,0xe9,0x2d,0xfc,0xb2,0xee,0x39,0x2d,0x27,0x0a,0xf3,0xa5,0x73,0x9f,0xaa,0x26,0xd4,0xf9,0x7b,0xfd,0x39,0xed,0x3c,0xbe,0xe4,0xd2,0x9e,0x26,0xaf,0x3b,0x20,0x6a,0x02,0x20,0x6c,0x9b,0xa3,0x7f,0x9f,0xaa,0x6a,0x1f,0xd3,0xf6,0x5f,0x23,0xb4,0xe8,0x53,0xd4,0x69,0x2a,0x72,0x74,0x24,0x0a,0x12,0xdb,0x7b,0xa3,0x88,0x48,0x30,0x63,0x0d,0x16, + 0x30,0x44,0x02,0x20,0x17,0x6a,0x25,0x57,0x56,0x6f,0xfa,0x51,0x8b,0x11,0x22,0x66,0x94,0xeb,0x98,0x02,0xed,0x20,0x98,0xbf,0xe2,0x78,0xe5,0x57,0x0f,0xe1,0xd5,0xd7,0xaf,0x18,0xa9,0x43,0x02,0x20,0x12,0x91,0xdf,0x6a,0x0e,0xd5,0xfc,0x0d,0x15,0x09,0x8e,0x70,0xbc,0xf1,0x3a,0x00,0x92,0x84,0xdf,0xd0,0x68,0x9d,0x3b,0xb4,0xbe,0x6c,0xee,0xb9,0xbe,0x14,0x87,0xc4, + 0x30,0x44,0x02,0x20,0x60,0xbe,0x20,0xc3,0xdb,0xc1,0x62,0xdd,0x34,0xd2,0x67,0x80,0x62,0x1c,0x10,0x4b,0xbe,0x5d,0xac,0xe6,0x30,0x17,0x1b,0x2d,0xae,0xf0,0xd8,0x26,0x40,0x9e,0xe5,0xc2,0x02,0x20,0x42,0x7f,0x7e,0x4d,0x88,0x9d,0x54,0x91,0x70,0xbd,0xa6,0xa9,0x40,0x9f,0xb1,0xcb,0x8b,0x0e,0x76,0x3d,0x13,0xee,0xa7,0xbd,0x97,0xf6,0x4c,0xf4,0x1d,0xc6,0xe4,0x97, + 0x30,0x45,0x02,0x21,0x00,0xed,0xf0,0x3c,0xf6,0x3f,0x65,0x88,0x83,0x28,0x9a,0x1a,0x59,0x3d,0x10,0x07,0x89,0x5b,0x9f,0x23,0x6d,0x27,0xc9,0xc1,0xf1,0x31,0x30,0x89,0xaa,0xed,0x6b,0x16,0xae,0x02,0x20,0x1a,0x4d,0xd6,0xfc,0x08,0x14,0xdc,0x52,0x3d,0x1f,0xef,0xa8,0x1c,0x64,0xfb,0xf5,0xe6,0x18,0xe6,0x51,0xe7,0x09,0x6f,0xcc,0xad,0xbb,0x94,0xcd,0x48,0xe5,0xe0,0xcd}; + +static const wycheproof_ecdsa_testvector testvectors[SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS] = { + /* tcId: 1. Signature malleability */ + {0, 0, 6, 0, 72, 0 }, + /* tcId: 2. valid */ + {0, 0, 6, 72, 71, 1 }, + /* tcId: 3. length of sequence [r, s] uses long form encoding */ + {0, 0, 6, 143, 72, 0 }, + /* tcId: 4. length of sequence [r, s] contains a leading 0 */ + {0, 0, 6, 215, 73, 0 }, + /* tcId: 5. length of sequence [r, s] uses 70 instead of 69 */ + {0, 0, 6, 288, 71, 0 }, + /* tcId: 6. length of sequence [r, s] uses 68 instead of 69 */ + {0, 0, 6, 359, 71, 0 }, + /* tcId: 7. uint32 overflow in length of sequence [r, s] */ + {0, 0, 6, 430, 76, 0 }, + /* tcId: 8. uint64 overflow in length of sequence [r, s] */ + {0, 0, 6, 506, 80, 0 }, + /* tcId: 9. length of sequence [r, s] = 2**31 - 1 */ + {0, 0, 6, 586, 75, 0 }, + /* tcId: 10. length of sequence [r, s] = 2**31 */ + {0, 0, 6, 661, 75, 0 }, + /* tcId: 11. length of sequence [r, s] = 2**32 - 1 */ + {0, 0, 6, 736, 75, 0 }, + /* tcId: 12. length of sequence [r, s] = 2**40 - 1 */ + {0, 0, 6, 811, 76, 0 }, + /* tcId: 13. length of sequence [r, s] = 2**64 - 1 */ + {0, 0, 6, 887, 79, 0 }, + /* tcId: 14. incorrect length of sequence [r, s] */ + {0, 0, 6, 966, 71, 0 }, + /* tcId: 15. replaced sequence [r, s] by an indefinite length tag without termination */ + {0, 0, 6, 1037, 71, 0 }, + /* tcId: 16. removing sequence [r, s] */ + {0, 0, 6, 1108, 0, 0 }, + /* tcId: 17. lonely sequence tag */ + {0, 0, 6, 1108, 1, 0 }, + /* tcId: 18. appending 0's to sequence [r, s] */ + {0, 0, 6, 1109, 73, 0 }, + /* tcId: 19. prepending 0's to sequence [r, s] */ + {0, 0, 6, 1182, 73, 0 }, + /* tcId: 20. appending unused 0's to sequence [r, s] */ + {0, 0, 6, 1255, 73, 0 }, + /* tcId: 21. appending null value to sequence [r, s] */ + {0, 0, 6, 1328, 73, 0 }, + /* tcId: 22. prepending garbage to sequence [r, s] */ + {0, 0, 6, 1401, 76, 0 }, + /* tcId: 23. prepending garbage to sequence [r, s] */ + {0, 0, 6, 1477, 75, 0 }, + /* tcId: 24. appending garbage to sequence [r, s] */ + {0, 0, 6, 1552, 79, 0 }, + /* tcId: 25. including undefined tags */ + {0, 0, 6, 1631, 79, 0 }, + /* tcId: 26. including undefined tags */ + {0, 0, 6, 1710, 79, 0 }, + /* tcId: 27. including undefined tags */ + {0, 0, 6, 1789, 79, 0 }, + /* tcId: 28. truncated length of sequence [r, s] */ + {0, 0, 6, 1868, 2, 0 }, + /* tcId: 29. including undefined tags to sequence [r, s] */ + {0, 0, 6, 1870, 77, 0 }, + /* tcId: 30. using composition with indefinite length for sequence [r, s] */ + {0, 0, 6, 1947, 75, 0 }, + /* tcId: 31. using composition with wrong tag for sequence [r, s] */ + {0, 0, 6, 2022, 75, 0 }, + /* tcId: 32. Replacing sequence [r, s] with NULL */ + {0, 0, 6, 2097, 2, 0 }, + /* tcId: 33. changing tag value of sequence [r, s] */ + {0, 0, 6, 2099, 71, 0 }, + /* tcId: 34. changing tag value of sequence [r, s] */ + {0, 0, 6, 2170, 71, 0 }, + /* tcId: 35. changing tag value of sequence [r, s] */ + {0, 0, 6, 2241, 71, 0 }, + /* tcId: 36. changing tag value of sequence [r, s] */ + {0, 0, 6, 2312, 71, 0 }, + /* tcId: 37. changing tag value of sequence [r, s] */ + {0, 0, 6, 2383, 71, 0 }, + /* tcId: 38. dropping value of sequence [r, s] */ + {0, 0, 6, 2454, 2, 0 }, + /* tcId: 39. using composition for sequence [r, s] */ + {0, 0, 6, 2456, 75, 0 }, + /* tcId: 40. truncated sequence [r, s] */ + {0, 0, 6, 2531, 70, 0 }, + /* tcId: 41. truncated sequence [r, s] */ + {0, 0, 6, 2601, 70, 0 }, + /* tcId: 42. sequence [r, s] of size 4166 to check for overflows */ + {0, 0, 6, 2671, 4170, 0 }, + /* tcId: 43. indefinite length */ + {0, 0, 6, 6841, 73, 0 }, + /* tcId: 44. indefinite length with truncated delimiter */ + {0, 0, 6, 6914, 72, 0 }, + /* tcId: 45. indefinite length with additional element */ + {0, 0, 6, 6986, 75, 0 }, + /* tcId: 46. indefinite length with truncated element */ + {0, 0, 6, 7061, 77, 0 }, + /* tcId: 47. indefinite length with garbage */ + {0, 0, 6, 7138, 77, 0 }, + /* tcId: 48. indefinite length with nonempty EOC */ + {0, 0, 6, 7215, 75, 0 }, + /* tcId: 49. prepend empty sequence */ + {0, 0, 6, 7290, 73, 0 }, + /* tcId: 50. append empty sequence */ + {0, 0, 6, 7363, 73, 0 }, + /* tcId: 51. append zero */ + {0, 0, 6, 7436, 74, 0 }, + /* tcId: 52. append garbage with high tag number */ + {0, 0, 6, 7510, 74, 0 }, + /* tcId: 53. append null with explicit tag */ + {0, 0, 6, 7584, 75, 0 }, + /* tcId: 54. append null with implicit tag */ + {0, 0, 6, 7659, 73, 0 }, + /* tcId: 55. sequence of sequence */ + {0, 0, 6, 7732, 73, 0 }, + /* tcId: 56. truncated sequence: removed last 1 elements */ + {0, 0, 6, 7805, 37, 0 }, + /* tcId: 57. repeating element in sequence */ + {0, 0, 6, 7842, 105, 0 }, + /* tcId: 58. flipped bit 0 in r */ + {0, 0, 6, 7947, 69, 0 }, + /* tcId: 59. flipped bit 32 in r */ + {0, 0, 6, 8016, 69, 0 }, + /* tcId: 60. flipped bit 48 in r */ + {0, 0, 6, 8085, 69, 0 }, + /* tcId: 61. flipped bit 64 in r */ + {0, 0, 6, 8154, 69, 0 }, + /* tcId: 62. length of r uses long form encoding */ + {0, 0, 6, 8223, 72, 0 }, + /* tcId: 63. length of r contains a leading 0 */ + {0, 0, 6, 8295, 73, 0 }, + /* tcId: 64. length of r uses 34 instead of 33 */ + {0, 0, 6, 8368, 71, 0 }, + /* tcId: 65. length of r uses 32 instead of 33 */ + {0, 0, 6, 8439, 71, 0 }, + /* tcId: 66. uint32 overflow in length of r */ + {0, 0, 6, 8510, 76, 0 }, + /* tcId: 67. uint64 overflow in length of r */ + {0, 0, 6, 8586, 80, 0 }, + /* tcId: 68. length of r = 2**31 - 1 */ + {0, 0, 6, 8666, 75, 0 }, + /* tcId: 69. length of r = 2**31 */ + {0, 0, 6, 8741, 75, 0 }, + /* tcId: 70. length of r = 2**32 - 1 */ + {0, 0, 6, 8816, 75, 0 }, + /* tcId: 71. length of r = 2**40 - 1 */ + {0, 0, 6, 8891, 76, 0 }, + /* tcId: 72. length of r = 2**64 - 1 */ + {0, 0, 6, 8967, 79, 0 }, + /* tcId: 73. incorrect length of r */ + {0, 0, 6, 9046, 71, 0 }, + /* tcId: 74. replaced r by an indefinite length tag without termination */ + {0, 0, 6, 9117, 71, 0 }, + /* tcId: 75. removing r */ + {0, 0, 6, 9188, 36, 0 }, + /* tcId: 76. lonely integer tag */ + {0, 0, 6, 9224, 37, 0 }, + /* tcId: 77. lonely integer tag */ + {0, 0, 6, 9261, 38, 0 }, + /* tcId: 78. appending 0's to r */ + {0, 0, 6, 9299, 73, 0 }, + /* tcId: 79. prepending 0's to r */ + {0, 0, 6, 9372, 73, 0 }, + /* tcId: 80. appending unused 0's to r */ + {0, 0, 6, 9445, 73, 0 }, + /* tcId: 81. appending null value to r */ + {0, 0, 6, 9518, 73, 0 }, + /* tcId: 82. prepending garbage to r */ + {0, 0, 6, 9591, 76, 0 }, + /* tcId: 83. prepending garbage to r */ + {0, 0, 6, 9667, 75, 0 }, + /* tcId: 84. appending garbage to r */ + {0, 0, 6, 9742, 79, 0 }, + /* tcId: 85. truncated length of r */ + {0, 0, 6, 9821, 38, 0 }, + /* tcId: 86. including undefined tags to r */ + {0, 0, 6, 9859, 77, 0 }, + /* tcId: 87. using composition with indefinite length for r */ + {0, 0, 6, 9936, 75, 0 }, + /* tcId: 88. using composition with wrong tag for r */ + {0, 0, 6, 10011, 75, 0 }, + /* tcId: 89. Replacing r with NULL */ + {0, 0, 6, 10086, 38, 0 }, + /* tcId: 90. changing tag value of r */ + {0, 0, 6, 10124, 71, 0 }, + /* tcId: 91. changing tag value of r */ + {0, 0, 6, 10195, 71, 0 }, + /* tcId: 92. changing tag value of r */ + {0, 0, 6, 10266, 71, 0 }, + /* tcId: 93. changing tag value of r */ + {0, 0, 6, 10337, 71, 0 }, + /* tcId: 94. changing tag value of r */ + {0, 0, 6, 10408, 71, 0 }, + /* tcId: 95. dropping value of r */ + {0, 0, 6, 10479, 38, 0 }, + /* tcId: 96. using composition for r */ + {0, 0, 6, 10517, 75, 0 }, + /* tcId: 97. modifying first byte of r */ + {0, 0, 6, 10592, 71, 0 }, + /* tcId: 98. modifying last byte of r */ + {0, 0, 6, 10663, 71, 0 }, + /* tcId: 99. truncated r */ + {0, 0, 6, 10734, 70, 0 }, + /* tcId: 100. truncated r */ + {0, 0, 6, 10804, 70, 0 }, + /* tcId: 101. r of size 4130 to check for overflows */ + {0, 0, 6, 10874, 4172, 0 }, + /* tcId: 102. leading ff in r */ + {0, 0, 6, 15046, 72, 0 }, + /* tcId: 103. replaced r by infinity */ + {0, 0, 6, 15118, 39, 0 }, + /* tcId: 104. replacing r with zero */ + {0, 0, 6, 15157, 39, 0 }, + /* tcId: 105. flipped bit 0 in s */ + {0, 0, 6, 15196, 69, 0 }, + /* tcId: 106. flipped bit 32 in s */ + {0, 0, 6, 15265, 69, 0 }, + /* tcId: 107. flipped bit 48 in s */ + {0, 0, 6, 15334, 69, 0 }, + /* tcId: 108. flipped bit 64 in s */ + {0, 0, 6, 15403, 69, 0 }, + /* tcId: 109. length of s uses long form encoding */ + {0, 0, 6, 15472, 72, 0 }, + /* tcId: 110. length of s contains a leading 0 */ + {0, 0, 6, 15544, 73, 0 }, + /* tcId: 111. length of s uses 33 instead of 32 */ + {0, 0, 6, 15617, 71, 0 }, + /* tcId: 112. length of s uses 31 instead of 32 */ + {0, 0, 6, 15688, 71, 0 }, + /* tcId: 113. uint32 overflow in length of s */ + {0, 0, 6, 15759, 76, 0 }, + /* tcId: 114. uint64 overflow in length of s */ + {0, 0, 6, 15835, 80, 0 }, + /* tcId: 115. length of s = 2**31 - 1 */ + {0, 0, 6, 15915, 75, 0 }, + /* tcId: 116. length of s = 2**31 */ + {0, 0, 6, 15990, 75, 0 }, + /* tcId: 117. length of s = 2**32 - 1 */ + {0, 0, 6, 16065, 75, 0 }, + /* tcId: 118. length of s = 2**40 - 1 */ + {0, 0, 6, 16140, 76, 0 }, + /* tcId: 119. length of s = 2**64 - 1 */ + {0, 0, 6, 16216, 79, 0 }, + /* tcId: 120. incorrect length of s */ + {0, 0, 6, 16295, 71, 0 }, + /* tcId: 121. replaced s by an indefinite length tag without termination */ + {0, 0, 6, 16366, 71, 0 }, + /* tcId: 122. appending 0's to s */ + {0, 0, 6, 16437, 73, 0 }, + /* tcId: 123. prepending 0's to s */ + {0, 0, 6, 16510, 73, 0 }, + /* tcId: 124. appending null value to s */ + {0, 0, 6, 16583, 73, 0 }, + /* tcId: 125. prepending garbage to s */ + {0, 0, 6, 16656, 76, 0 }, + /* tcId: 126. prepending garbage to s */ + {0, 0, 6, 16732, 75, 0 }, + /* tcId: 127. appending garbage to s */ + {0, 0, 6, 16807, 79, 0 }, + /* tcId: 128. truncated length of s */ + {0, 0, 6, 16886, 39, 0 }, + /* tcId: 129. including undefined tags to s */ + {0, 0, 6, 16925, 77, 0 }, + /* tcId: 130. using composition with indefinite length for s */ + {0, 0, 6, 17002, 75, 0 }, + /* tcId: 131. using composition with wrong tag for s */ + {0, 0, 6, 17077, 75, 0 }, + /* tcId: 132. Replacing s with NULL */ + {0, 0, 6, 17152, 39, 0 }, + /* tcId: 133. changing tag value of s */ + {0, 0, 6, 17191, 71, 0 }, + /* tcId: 134. changing tag value of s */ + {0, 0, 6, 17262, 71, 0 }, + /* tcId: 135. changing tag value of s */ + {0, 0, 6, 17333, 71, 0 }, + /* tcId: 136. changing tag value of s */ + {0, 0, 6, 17404, 71, 0 }, + /* tcId: 137. changing tag value of s */ + {0, 0, 6, 17475, 71, 0 }, + /* tcId: 138. dropping value of s */ + {0, 0, 6, 17546, 39, 0 }, + /* tcId: 139. using composition for s */ + {0, 0, 6, 17585, 75, 0 }, + /* tcId: 140. modifying first byte of s */ + {0, 0, 6, 17660, 71, 0 }, + /* tcId: 141. modifying last byte of s */ + {0, 0, 6, 17731, 71, 0 }, + /* tcId: 142. truncated s */ + {0, 0, 6, 17802, 70, 0 }, + /* tcId: 143. truncated s */ + {0, 0, 6, 17872, 70, 0 }, + /* tcId: 144. s of size 4129 to check for overflows */ + {0, 0, 6, 17942, 4172, 0 }, + /* tcId: 145. leading ff in s */ + {0, 0, 6, 22114, 72, 0 }, + /* tcId: 146. replaced s by infinity */ + {0, 0, 6, 22186, 40, 0 }, + /* tcId: 147. replacing s with zero */ + {0, 0, 6, 22226, 40, 0 }, + /* tcId: 148. replaced r by r + n */ + {0, 0, 6, 22266, 71, 0 }, + /* tcId: 149. replaced r by r - n */ + {0, 0, 6, 22337, 70, 0 }, + /* tcId: 150. replaced r by r + 256 * n */ + {0, 0, 6, 22407, 72, 0 }, + /* tcId: 151. replaced r by -r */ + {0, 0, 6, 22479, 71, 0 }, + /* tcId: 152. replaced r by n - r */ + {0, 0, 6, 22550, 70, 0 }, + /* tcId: 153. replaced r by -n - r */ + {0, 0, 6, 22620, 71, 0 }, + /* tcId: 154. replaced r by r + 2**256 */ + {0, 0, 6, 22691, 71, 0 }, + /* tcId: 155. replaced r by r + 2**320 */ + {0, 0, 6, 22762, 79, 0 }, + /* tcId: 156. replaced s by s + n */ + {0, 0, 6, 22841, 71, 0 }, + /* tcId: 157. replaced s by s - n */ + {0, 0, 6, 22912, 71, 0 }, + /* tcId: 158. replaced s by s + 256 * n */ + {0, 0, 6, 22983, 72, 0 }, + /* tcId: 159. replaced s by -s */ + {0, 0, 6, 23055, 70, 0 }, + /* tcId: 160. replaced s by -n - s */ + {0, 0, 6, 23125, 71, 0 }, + /* tcId: 161. replaced s by s + 2**256 */ + {0, 0, 6, 23196, 71, 0 }, + /* tcId: 162. replaced s by s - 2**256 */ + {0, 0, 6, 23267, 71, 0 }, + /* tcId: 163. replaced s by s + 2**320 */ + {0, 0, 6, 23338, 79, 0 }, + /* tcId: 164. Signature with special case values r=0 and s=0 */ + {0, 0, 6, 23417, 8, 0 }, + /* tcId: 165. Signature with special case values r=0 and s=1 */ + {0, 0, 6, 23425, 8, 0 }, + /* tcId: 166. Signature with special case values r=0 and s=-1 */ + {0, 0, 6, 23433, 8, 0 }, + /* tcId: 167. Signature with special case values r=0 and s=n */ + {0, 0, 6, 23441, 40, 0 }, + /* tcId: 168. Signature with special case values r=0 and s=n - 1 */ + {0, 0, 6, 23481, 40, 0 }, + /* tcId: 169. Signature with special case values r=0 and s=n + 1 */ + {0, 0, 6, 23521, 40, 0 }, + /* tcId: 170. Signature with special case values r=0 and s=p */ + {0, 0, 6, 23561, 40, 0 }, + /* tcId: 171. Signature with special case values r=0 and s=p + 1 */ + {0, 0, 6, 23601, 40, 0 }, + /* tcId: 172. Signature with special case values r=1 and s=0 */ + {0, 0, 6, 23641, 8, 0 }, + /* tcId: 173. Signature with special case values r=1 and s=1 */ + {0, 0, 6, 23649, 8, 0 }, + /* tcId: 174. Signature with special case values r=1 and s=-1 */ + {0, 0, 6, 23657, 8, 0 }, + /* tcId: 175. Signature with special case values r=1 and s=n */ + {0, 0, 6, 23665, 40, 0 }, + /* tcId: 176. Signature with special case values r=1 and s=n - 1 */ + {0, 0, 6, 23705, 40, 0 }, + /* tcId: 177. Signature with special case values r=1 and s=n + 1 */ + {0, 0, 6, 23745, 40, 0 }, + /* tcId: 178. Signature with special case values r=1 and s=p */ + {0, 0, 6, 23785, 40, 0 }, + /* tcId: 179. Signature with special case values r=1 and s=p + 1 */ + {0, 0, 6, 23825, 40, 0 }, + /* tcId: 180. Signature with special case values r=-1 and s=0 */ + {0, 0, 6, 23865, 8, 0 }, + /* tcId: 181. Signature with special case values r=-1 and s=1 */ + {0, 0, 6, 23873, 8, 0 }, + /* tcId: 182. Signature with special case values r=-1 and s=-1 */ + {0, 0, 6, 23881, 8, 0 }, + /* tcId: 183. Signature with special case values r=-1 and s=n */ + {0, 0, 6, 23889, 40, 0 }, + /* tcId: 184. Signature with special case values r=-1 and s=n - 1 */ + {0, 0, 6, 23929, 40, 0 }, + /* tcId: 185. Signature with special case values r=-1 and s=n + 1 */ + {0, 0, 6, 23969, 40, 0 }, + /* tcId: 186. Signature with special case values r=-1 and s=p */ + {0, 0, 6, 24009, 40, 0 }, + /* tcId: 187. Signature with special case values r=-1 and s=p + 1 */ + {0, 0, 6, 24049, 40, 0 }, + /* tcId: 188. Signature with special case values r=n and s=0 */ + {0, 0, 6, 24089, 40, 0 }, + /* tcId: 189. Signature with special case values r=n and s=1 */ + {0, 0, 6, 24129, 40, 0 }, + /* tcId: 190. Signature with special case values r=n and s=-1 */ + {0, 0, 6, 24169, 40, 0 }, + /* tcId: 191. Signature with special case values r=n and s=n */ + {0, 0, 6, 24209, 72, 0 }, + /* tcId: 192. Signature with special case values r=n and s=n - 1 */ + {0, 0, 6, 24281, 72, 0 }, + /* tcId: 193. Signature with special case values r=n and s=n + 1 */ + {0, 0, 6, 24353, 72, 0 }, + /* tcId: 194. Signature with special case values r=n and s=p */ + {0, 0, 6, 24425, 72, 0 }, + /* tcId: 195. Signature with special case values r=n and s=p + 1 */ + {0, 0, 6, 24497, 72, 0 }, + /* tcId: 196. Signature with special case values r=n - 1 and s=0 */ + {0, 0, 6, 24569, 40, 0 }, + /* tcId: 197. Signature with special case values r=n - 1 and s=1 */ + {0, 0, 6, 24609, 40, 0 }, + /* tcId: 198. Signature with special case values r=n - 1 and s=-1 */ + {0, 0, 6, 24649, 40, 0 }, + /* tcId: 199. Signature with special case values r=n - 1 and s=n */ + {0, 0, 6, 24689, 72, 0 }, + /* tcId: 200. Signature with special case values r=n - 1 and s=n - 1 */ + {0, 0, 6, 24761, 72, 0 }, + /* tcId: 201. Signature with special case values r=n - 1 and s=n + 1 */ + {0, 0, 6, 24833, 72, 0 }, + /* tcId: 202. Signature with special case values r=n - 1 and s=p */ + {0, 0, 6, 24905, 72, 0 }, + /* tcId: 203. Signature with special case values r=n - 1 and s=p + 1 */ + {0, 0, 6, 24977, 72, 0 }, + /* tcId: 204. Signature with special case values r=n + 1 and s=0 */ + {0, 0, 6, 25049, 40, 0 }, + /* tcId: 205. Signature with special case values r=n + 1 and s=1 */ + {0, 0, 6, 25089, 40, 0 }, + /* tcId: 206. Signature with special case values r=n + 1 and s=-1 */ + {0, 0, 6, 25129, 40, 0 }, + /* tcId: 207. Signature with special case values r=n + 1 and s=n */ + {0, 0, 6, 25169, 72, 0 }, + /* tcId: 208. Signature with special case values r=n + 1 and s=n - 1 */ + {0, 0, 6, 25241, 72, 0 }, + /* tcId: 209. Signature with special case values r=n + 1 and s=n + 1 */ + {0, 0, 6, 25313, 72, 0 }, + /* tcId: 210. Signature with special case values r=n + 1 and s=p */ + {0, 0, 6, 25385, 72, 0 }, + /* tcId: 211. Signature with special case values r=n + 1 and s=p + 1 */ + {0, 0, 6, 25457, 72, 0 }, + /* tcId: 212. Signature with special case values r=p and s=0 */ + {0, 0, 6, 25529, 40, 0 }, + /* tcId: 213. Signature with special case values r=p and s=1 */ + {0, 0, 6, 25569, 40, 0 }, + /* tcId: 214. Signature with special case values r=p and s=-1 */ + {0, 0, 6, 25609, 40, 0 }, + /* tcId: 215. Signature with special case values r=p and s=n */ + {0, 0, 6, 25649, 72, 0 }, + /* tcId: 216. Signature with special case values r=p and s=n - 1 */ + {0, 0, 6, 25721, 72, 0 }, + /* tcId: 217. Signature with special case values r=p and s=n + 1 */ + {0, 0, 6, 25793, 72, 0 }, + /* tcId: 218. Signature with special case values r=p and s=p */ + {0, 0, 6, 25865, 72, 0 }, + /* tcId: 219. Signature with special case values r=p and s=p + 1 */ + {0, 0, 6, 25937, 72, 0 }, + /* tcId: 220. Signature with special case values r=p + 1 and s=0 */ + {0, 0, 6, 26009, 40, 0 }, + /* tcId: 221. Signature with special case values r=p + 1 and s=1 */ + {0, 0, 6, 26049, 40, 0 }, + /* tcId: 222. Signature with special case values r=p + 1 and s=-1 */ + {0, 0, 6, 26089, 40, 0 }, + /* tcId: 223. Signature with special case values r=p + 1 and s=n */ + {0, 0, 6, 26129, 72, 0 }, + /* tcId: 224. Signature with special case values r=p + 1 and s=n - 1 */ + {0, 0, 6, 26201, 72, 0 }, + /* tcId: 225. Signature with special case values r=p + 1 and s=n + 1 */ + {0, 0, 6, 26273, 72, 0 }, + /* tcId: 226. Signature with special case values r=p + 1 and s=p */ + {0, 0, 6, 26345, 72, 0 }, + /* tcId: 227. Signature with special case values r=p + 1 and s=p + 1 */ + {0, 0, 6, 26417, 72, 0 }, + /* tcId: 228. Signature encoding contains incorrect types: r=0, s=0.25 */ + {0, 0, 6, 26489, 10, 0 }, + /* tcId: 229. Signature encoding contains incorrect types: r=0, s=nan */ + {0, 0, 6, 26499, 8, 0 }, + /* tcId: 230. Signature encoding contains incorrect types: r=0, s=True */ + {0, 0, 6, 26507, 8, 0 }, + /* tcId: 231. Signature encoding contains incorrect types: r=0, s=False */ + {0, 0, 6, 26515, 8, 0 }, + /* tcId: 232. Signature encoding contains incorrect types: r=0, s=Null */ + {0, 0, 6, 26523, 7, 0 }, + /* tcId: 233. Signature encoding contains incorrect types: r=0, s=empyt UTF-8 string */ + {0, 0, 6, 26530, 7, 0 }, + /* tcId: 234. Signature encoding contains incorrect types: r=0, s="0" */ + {0, 0, 6, 26537, 8, 0 }, + /* tcId: 235. Signature encoding contains incorrect types: r=0, s=empty list */ + {0, 0, 6, 26545, 7, 0 }, + /* tcId: 236. Signature encoding contains incorrect types: r=0, s=list containing 0 */ + {0, 0, 6, 26552, 10, 0 }, + /* tcId: 237. Signature encoding contains incorrect types: r=1, s=0.25 */ + {0, 0, 6, 26562, 10, 0 }, + /* tcId: 238. Signature encoding contains incorrect types: r=1, s=nan */ + {0, 0, 6, 26572, 8, 0 }, + /* tcId: 239. Signature encoding contains incorrect types: r=1, s=True */ + {0, 0, 6, 26580, 8, 0 }, + /* tcId: 240. Signature encoding contains incorrect types: r=1, s=False */ + {0, 0, 6, 26588, 8, 0 }, + /* tcId: 241. Signature encoding contains incorrect types: r=1, s=Null */ + {0, 0, 6, 26596, 7, 0 }, + /* tcId: 242. Signature encoding contains incorrect types: r=1, s=empyt UTF-8 string */ + {0, 0, 6, 26603, 7, 0 }, + /* tcId: 243. Signature encoding contains incorrect types: r=1, s="0" */ + {0, 0, 6, 26610, 8, 0 }, + /* tcId: 244. Signature encoding contains incorrect types: r=1, s=empty list */ + {0, 0, 6, 26618, 7, 0 }, + /* tcId: 245. Signature encoding contains incorrect types: r=1, s=list containing 0 */ + {0, 0, 6, 26625, 10, 0 }, + /* tcId: 246. Signature encoding contains incorrect types: r=-1, s=0.25 */ + {0, 0, 6, 26635, 10, 0 }, + /* tcId: 247. Signature encoding contains incorrect types: r=-1, s=nan */ + {0, 0, 6, 26645, 8, 0 }, + /* tcId: 248. Signature encoding contains incorrect types: r=-1, s=True */ + {0, 0, 6, 26653, 8, 0 }, + /* tcId: 249. Signature encoding contains incorrect types: r=-1, s=False */ + {0, 0, 6, 26661, 8, 0 }, + /* tcId: 250. Signature encoding contains incorrect types: r=-1, s=Null */ + {0, 0, 6, 26669, 7, 0 }, + /* tcId: 251. Signature encoding contains incorrect types: r=-1, s=empyt UTF-8 string */ + {0, 0, 6, 26676, 7, 0 }, + /* tcId: 252. Signature encoding contains incorrect types: r=-1, s="0" */ + {0, 0, 6, 26683, 8, 0 }, + /* tcId: 253. Signature encoding contains incorrect types: r=-1, s=empty list */ + {0, 0, 6, 26691, 7, 0 }, + /* tcId: 254. Signature encoding contains incorrect types: r=-1, s=list containing 0 */ + {0, 0, 6, 26698, 10, 0 }, + /* tcId: 255. Signature encoding contains incorrect types: r=n, s=0.25 */ + {0, 0, 6, 26708, 42, 0 }, + /* tcId: 256. Signature encoding contains incorrect types: r=n, s=nan */ + {0, 0, 6, 26750, 40, 0 }, + /* tcId: 257. Signature encoding contains incorrect types: r=n, s=True */ + {0, 0, 6, 26790, 40, 0 }, + /* tcId: 258. Signature encoding contains incorrect types: r=n, s=False */ + {0, 0, 6, 26830, 40, 0 }, + /* tcId: 259. Signature encoding contains incorrect types: r=n, s=Null */ + {0, 0, 6, 26870, 39, 0 }, + /* tcId: 260. Signature encoding contains incorrect types: r=n, s=empyt UTF-8 string */ + {0, 0, 6, 26909, 39, 0 }, + /* tcId: 261. Signature encoding contains incorrect types: r=n, s="0" */ + {0, 0, 6, 26948, 40, 0 }, + /* tcId: 262. Signature encoding contains incorrect types: r=n, s=empty list */ + {0, 0, 6, 26988, 39, 0 }, + /* tcId: 263. Signature encoding contains incorrect types: r=n, s=list containing 0 */ + {0, 0, 6, 27027, 42, 0 }, + /* tcId: 264. Signature encoding contains incorrect types: r=p, s=0.25 */ + {0, 0, 6, 27069, 42, 0 }, + /* tcId: 265. Signature encoding contains incorrect types: r=p, s=nan */ + {0, 0, 6, 27111, 40, 0 }, + /* tcId: 266. Signature encoding contains incorrect types: r=p, s=True */ + {0, 0, 6, 27151, 40, 0 }, + /* tcId: 267. Signature encoding contains incorrect types: r=p, s=False */ + {0, 0, 6, 27191, 40, 0 }, + /* tcId: 268. Signature encoding contains incorrect types: r=p, s=Null */ + {0, 0, 6, 27231, 39, 0 }, + /* tcId: 269. Signature encoding contains incorrect types: r=p, s=empyt UTF-8 string */ + {0, 0, 6, 27270, 39, 0 }, + /* tcId: 270. Signature encoding contains incorrect types: r=p, s="0" */ + {0, 0, 6, 27309, 40, 0 }, + /* tcId: 271. Signature encoding contains incorrect types: r=p, s=empty list */ + {0, 0, 6, 27349, 39, 0 }, + /* tcId: 272. Signature encoding contains incorrect types: r=p, s=list containing 0 */ + {0, 0, 6, 27388, 42, 0 }, + /* tcId: 273. Signature encoding contains incorrect types: r=0.25, s=0.25 */ + {0, 0, 6, 27430, 12, 0 }, + /* tcId: 274. Signature encoding contains incorrect types: r=nan, s=nan */ + {0, 0, 6, 27442, 8, 0 }, + /* tcId: 275. Signature encoding contains incorrect types: r=True, s=True */ + {0, 0, 6, 27450, 8, 0 }, + /* tcId: 276. Signature encoding contains incorrect types: r=False, s=False */ + {0, 0, 6, 27458, 8, 0 }, + /* tcId: 277. Signature encoding contains incorrect types: r=Null, s=Null */ + {0, 0, 6, 27466, 6, 0 }, + /* tcId: 278. Signature encoding contains incorrect types: r=empyt UTF-8 string, s=empyt UTF-8 string */ + {0, 0, 6, 27472, 6, 0 }, + /* tcId: 279. Signature encoding contains incorrect types: r="0", s="0" */ + {0, 0, 6, 27478, 8, 0 }, + /* tcId: 280. Signature encoding contains incorrect types: r=empty list, s=empty list */ + {0, 0, 6, 27486, 6, 0 }, + /* tcId: 281. Signature encoding contains incorrect types: r=list containing 0, s=list containing 0 */ + {0, 0, 6, 27492, 12, 0 }, + /* tcId: 282. Signature encoding contains incorrect types: r=0.25, s=0 */ + {0, 0, 6, 27504, 10, 0 }, + /* tcId: 283. Signature encoding contains incorrect types: r=nan, s=0 */ + {0, 0, 6, 27514, 8, 0 }, + /* tcId: 284. Signature encoding contains incorrect types: r=True, s=0 */ + {0, 0, 6, 27522, 8, 0 }, + /* tcId: 285. Signature encoding contains incorrect types: r=False, s=0 */ + {0, 0, 6, 27530, 8, 0 }, + /* tcId: 286. Signature encoding contains incorrect types: r=Null, s=0 */ + {0, 0, 6, 27538, 7, 0 }, + /* tcId: 287. Signature encoding contains incorrect types: r=empyt UTF-8 string, s=0 */ + {0, 0, 6, 27545, 7, 0 }, + /* tcId: 288. Signature encoding contains incorrect types: r="0", s=0 */ + {0, 0, 6, 27552, 8, 0 }, + /* tcId: 289. Signature encoding contains incorrect types: r=empty list, s=0 */ + {0, 0, 6, 27560, 7, 0 }, + /* tcId: 290. Signature encoding contains incorrect types: r=list containing 0, s=0 */ + {0, 0, 6, 27567, 10, 0 }, + /* tcId: 291. Edge case for Shamir multiplication */ + {0, 6, 5, 27577, 71, 1 }, + /* tcId: 292. special case hash */ + {0, 11, 9, 27648, 71, 1 }, + /* tcId: 293. special case hash */ + {0, 20, 10, 27719, 70, 1 }, + /* tcId: 294. special case hash */ + {0, 30, 11, 27789, 71, 1 }, + /* tcId: 295. special case hash */ + {0, 41, 10, 27860, 71, 1 }, + /* tcId: 296. special case hash */ + {0, 51, 10, 27931, 70, 1 }, + /* tcId: 297. special case hash */ + {0, 61, 10, 28001, 71, 1 }, + /* tcId: 298. special case hash */ + {0, 71, 9, 28072, 70, 1 }, + /* tcId: 299. special case hash */ + {0, 80, 10, 28142, 71, 1 }, + /* tcId: 300. special case hash */ + {0, 90, 10, 28213, 71, 1 }, + /* tcId: 301. special case hash */ + {0, 100, 10, 28284, 71, 1 }, + /* tcId: 302. special case hash */ + {0, 110, 10, 28355, 71, 1 }, + /* tcId: 303. special case hash */ + {0, 120, 11, 28426, 70, 1 }, + /* tcId: 304. special case hash */ + {0, 131, 10, 28496, 71, 1 }, + /* tcId: 305. special case hash */ + {0, 141, 10, 28567, 71, 1 }, + /* tcId: 306. special case hash */ + {0, 151, 10, 28638, 70, 1 }, + /* tcId: 307. special case hash */ + {0, 161, 10, 28708, 71, 1 }, + /* tcId: 308. special case hash */ + {0, 171, 10, 28779, 71, 1 }, + /* tcId: 309. special case hash */ + {0, 181, 10, 28850, 70, 1 }, + /* tcId: 310. special case hash */ + {0, 191, 10, 28920, 71, 1 }, + /* tcId: 311. special case hash */ + {0, 201, 10, 28991, 71, 1 }, + /* tcId: 312. special case hash */ + {0, 211, 10, 29062, 71, 1 }, + /* tcId: 313. special case hash */ + {0, 221, 10, 29133, 70, 1 }, + /* tcId: 314. special case hash */ + {0, 231, 10, 29203, 71, 1 }, + /* tcId: 315. special case hash */ + {0, 241, 10, 29274, 71, 1 }, + /* tcId: 316. special case hash */ + {0, 251, 10, 29345, 71, 1 }, + /* tcId: 317. special case hash */ + {0, 261, 11, 29416, 71, 1 }, + /* tcId: 318. special case hash */ + {0, 272, 11, 29487, 70, 1 }, + /* tcId: 319. special case hash */ + {0, 283, 9, 29557, 71, 1 }, + /* tcId: 320. special case hash */ + {0, 292, 9, 29628, 71, 1 }, + /* tcId: 321. special case hash */ + {0, 301, 10, 29699, 71, 1 }, + /* tcId: 322. special case hash */ + {0, 311, 10, 29770, 71, 1 }, + /* tcId: 323. special case hash */ + {0, 321, 10, 29841, 70, 1 }, + /* tcId: 324. special case hash */ + {0, 331, 10, 29911, 70, 1 }, + /* tcId: 325. special case hash */ + {0, 341, 10, 29981, 71, 1 }, + /* tcId: 326. special case hash */ + {0, 351, 9, 30052, 70, 1 }, + /* tcId: 327. special case hash */ + {0, 360, 10, 30122, 70, 1 }, + /* tcId: 328. special case hash */ + {0, 370, 10, 30192, 70, 1 }, + /* tcId: 329. special case hash */ + {0, 380, 10, 30262, 70, 1 }, + /* tcId: 330. special case hash */ + {0, 390, 9, 30332, 71, 1 }, + /* tcId: 331. special case hash */ + {0, 399, 11, 30403, 70, 1 }, + /* tcId: 332. special case hash */ + {0, 410, 9, 30473, 71, 1 }, + /* tcId: 333. special case hash */ + {0, 419, 9, 30544, 71, 1 }, + /* tcId: 334. special case hash */ + {0, 428, 11, 30615, 70, 1 }, + /* tcId: 335. special case hash */ + {0, 439, 8, 30685, 71, 1 }, + /* tcId: 336. special case hash */ + {0, 447, 10, 30756, 70, 1 }, + /* tcId: 337. special case hash */ + {0, 457, 10, 30826, 71, 1 }, + /* tcId: 338. special case hash */ + {0, 467, 10, 30897, 70, 1 }, + /* tcId: 339. special case hash */ + {0, 477, 10, 30967, 70, 1 }, + /* tcId: 340. special case hash */ + {0, 487, 10, 31037, 70, 1 }, + /* tcId: 341. special case hash */ + {0, 497, 10, 31107, 71, 1 }, + /* tcId: 342. special case hash */ + {0, 507, 10, 31178, 70, 1 }, + /* tcId: 343. special case hash */ + {0, 517, 10, 31248, 70, 1 }, + /* tcId: 344. special case hash */ + {0, 527, 10, 31318, 71, 1 }, + /* tcId: 345. special case hash */ + {0, 537, 9, 31389, 70, 1 }, + /* tcId: 346. k*G has a large x-coordinate */ + {65, 0, 6, 31459, 24, 1 }, + /* tcId: 347. r too large */ + {65, 0, 6, 31483, 40, 0 }, + /* tcId: 348. r,s are large */ + {130, 0, 6, 31523, 40, 1 }, + /* tcId: 349. r and s^-1 have a large Hamming weight */ + {195, 0, 6, 31563, 70, 1 }, + /* tcId: 350. r and s^-1 have a large Hamming weight */ + {260, 0, 6, 31633, 70, 1 }, + /* tcId: 351. small r and s */ + {325, 0, 6, 31703, 8, 1 }, + /* tcId: 352. small r and s */ + {390, 0, 6, 31711, 8, 1 }, + /* tcId: 353. small r and s */ + {455, 0, 6, 31719, 8, 1 }, + /* tcId: 354. small r and s */ + {520, 0, 6, 31727, 8, 1 }, + /* tcId: 355. small r and s */ + {585, 0, 6, 31735, 8, 1 }, + /* tcId: 356. small r and s */ + {650, 0, 6, 31743, 8, 1 }, + /* tcId: 357. r is larger than n */ + {650, 0, 6, 31751, 40, 0 }, + /* tcId: 358. s is larger than n */ + {715, 0, 6, 31791, 10, 0 }, + /* tcId: 359. small r and s^-1 */ + {780, 0, 6, 31801, 40, 1 }, + /* tcId: 360. smallish r and s^-1 */ + {845, 0, 6, 31841, 45, 1 }, + /* tcId: 361. 100-bit r and small s^-1 */ + {910, 0, 6, 31886, 51, 1 }, + /* tcId: 362. small r and 100 bit s^-1 */ + {975, 0, 6, 31937, 40, 1 }, + /* tcId: 363. 100-bit r and s^-1 */ + {1040, 0, 6, 31977, 51, 1 }, + /* tcId: 364. r and s^-1 are close to n */ + {1105, 0, 6, 32028, 71, 1 }, + /* tcId: 365. r and s are 64-bit integer */ + {1170, 0, 6, 32099, 24, 1 }, + /* tcId: 366. r and s are 100-bit integer */ + {1235, 0, 6, 32123, 32, 1 }, + /* tcId: 367. r and s are 128-bit integer */ + {1300, 0, 6, 32155, 40, 1 }, + /* tcId: 368. r and s are 160-bit integer */ + {1365, 0, 6, 32195, 48, 1 }, + /* tcId: 369. s == 1 */ + {1430, 0, 6, 32243, 39, 1 }, + /* tcId: 370. s == 0 */ + {1430, 0, 6, 32282, 39, 0 }, + /* tcId: 371. edge case modular inverse */ + {1495, 0, 6, 32321, 70, 1 }, + /* tcId: 372. edge case modular inverse */ + {1560, 0, 6, 32391, 70, 1 }, + /* tcId: 373. edge case modular inverse */ + {1625, 0, 6, 32461, 70, 1 }, + /* tcId: 374. edge case modular inverse */ + {1690, 0, 6, 32531, 70, 1 }, + /* tcId: 375. edge case modular inverse */ + {1755, 0, 6, 32601, 70, 1 }, + /* tcId: 376. edge case modular inverse */ + {1820, 0, 6, 32671, 70, 1 }, + /* tcId: 377. edge case modular inverse */ + {1885, 0, 6, 32741, 70, 1 }, + /* tcId: 378. edge case modular inverse */ + {1950, 0, 6, 32811, 70, 1 }, + /* tcId: 379. edge case modular inverse */ + {2015, 0, 6, 32881, 70, 1 }, + /* tcId: 380. edge case modular inverse */ + {2080, 0, 6, 32951, 70, 1 }, + /* tcId: 381. edge case modular inverse */ + {2145, 0, 6, 33021, 70, 1 }, + /* tcId: 382. edge case modular inverse */ + {2210, 0, 6, 33091, 70, 1 }, + /* tcId: 383. edge case modular inverse */ + {2275, 0, 6, 33161, 70, 1 }, + /* tcId: 384. edge case modular inverse */ + {2340, 0, 6, 33231, 70, 1 }, + /* tcId: 385. edge case modular inverse */ + {2405, 0, 6, 33301, 70, 1 }, + /* tcId: 386. point at infinity during verify */ + {2470, 0, 6, 33371, 70, 0 }, + /* tcId: 387. edge case for signature malleability */ + {2535, 0, 6, 33441, 70, 1 }, + /* tcId: 388. edge case for signature malleability */ + {2600, 0, 6, 33511, 70, 0 }, + /* tcId: 389. u1 == 1 */ + {2665, 0, 6, 33581, 70, 1 }, + /* tcId: 390. u1 == n - 1 */ + {2730, 0, 6, 33651, 70, 1 }, + /* tcId: 391. u2 == 1 */ + {2795, 0, 6, 33721, 70, 1 }, + /* tcId: 392. u2 == n - 1 */ + {2860, 0, 6, 33791, 70, 1 }, + /* tcId: 393. edge case for u1 */ + {2925, 0, 6, 33861, 70, 1 }, + /* tcId: 394. edge case for u1 */ + {2990, 0, 6, 33931, 70, 1 }, + /* tcId: 395. edge case for u1 */ + {3055, 0, 6, 34001, 70, 1 }, + /* tcId: 396. edge case for u1 */ + {3120, 0, 6, 34071, 70, 1 }, + /* tcId: 397. edge case for u1 */ + {3185, 0, 6, 34141, 70, 1 }, + /* tcId: 398. edge case for u1 */ + {3250, 0, 6, 34211, 70, 1 }, + /* tcId: 399. edge case for u1 */ + {3315, 0, 6, 34281, 70, 1 }, + /* tcId: 400. edge case for u1 */ + {3380, 0, 6, 34351, 70, 1 }, + /* tcId: 401. edge case for u1 */ + {3445, 0, 6, 34421, 70, 1 }, + /* tcId: 402. edge case for u1 */ + {3510, 0, 6, 34491, 70, 1 }, + /* tcId: 403. edge case for u1 */ + {3575, 0, 6, 34561, 70, 1 }, + /* tcId: 404. edge case for u1 */ + {3640, 0, 6, 34631, 70, 1 }, + /* tcId: 405. edge case for u1 */ + {3705, 0, 6, 34701, 70, 1 }, + /* tcId: 406. edge case for u1 */ + {3770, 0, 6, 34771, 70, 1 }, + /* tcId: 407. edge case for u1 */ + {3835, 0, 6, 34841, 70, 1 }, + /* tcId: 408. edge case for u2 */ + {3900, 0, 6, 34911, 70, 1 }, + /* tcId: 409. edge case for u2 */ + {3965, 0, 6, 34981, 70, 1 }, + /* tcId: 410. edge case for u2 */ + {4030, 0, 6, 35051, 70, 1 }, + /* tcId: 411. edge case for u2 */ + {4095, 0, 6, 35121, 70, 1 }, + /* tcId: 412. edge case for u2 */ + {4160, 0, 6, 35191, 70, 1 }, + /* tcId: 413. edge case for u2 */ + {4225, 0, 6, 35261, 69, 1 }, + /* tcId: 414. edge case for u2 */ + {4290, 0, 6, 35330, 70, 1 }, + /* tcId: 415. edge case for u2 */ + {4355, 0, 6, 35400, 70, 1 }, + /* tcId: 416. edge case for u2 */ + {4420, 0, 6, 35470, 70, 1 }, + /* tcId: 417. edge case for u2 */ + {4485, 0, 6, 35540, 70, 1 }, + /* tcId: 418. edge case for u2 */ + {4550, 0, 6, 35610, 70, 1 }, + /* tcId: 419. edge case for u2 */ + {4615, 0, 6, 35680, 70, 1 }, + /* tcId: 420. edge case for u2 */ + {4680, 0, 6, 35750, 70, 1 }, + /* tcId: 421. edge case for u2 */ + {4745, 0, 6, 35820, 70, 1 }, + /* tcId: 422. edge case for u2 */ + {4810, 0, 6, 35890, 70, 1 }, + /* tcId: 423. point duplication during verification */ + {4875, 0, 6, 35960, 70, 1 }, + /* tcId: 424. duplication bug */ + {4940, 0, 6, 36030, 70, 0 }, + /* tcId: 425. comparison with point at infinity */ + {5005, 0, 6, 36100, 70, 0 }, + /* tcId: 426. extreme value for k and edgecase s */ + {5070, 0, 6, 36170, 71, 1 }, + /* tcId: 427. extreme value for k and s^-1 */ + {5135, 0, 6, 36241, 71, 1 }, + /* tcId: 428. extreme value for k and s^-1 */ + {5200, 0, 6, 36312, 71, 1 }, + /* tcId: 429. extreme value for k and s^-1 */ + {5265, 0, 6, 36383, 71, 1 }, + /* tcId: 430. extreme value for k and s^-1 */ + {5330, 0, 6, 36454, 71, 1 }, + /* tcId: 431. extreme value for k */ + {5395, 0, 6, 36525, 71, 1 }, + /* tcId: 432. extreme value for k and edgecase s */ + {5460, 0, 6, 36596, 70, 1 }, + /* tcId: 433. extreme value for k and s^-1 */ + {5525, 0, 6, 36666, 70, 1 }, + /* tcId: 434. extreme value for k and s^-1 */ + {5590, 0, 6, 36736, 70, 1 }, + /* tcId: 435. extreme value for k and s^-1 */ + {5655, 0, 6, 36806, 70, 1 }, + /* tcId: 436. extreme value for k and s^-1 */ + {5720, 0, 6, 36876, 70, 1 }, + /* tcId: 437. extreme value for k */ + {5785, 0, 6, 36946, 70, 1 }, + /* tcId: 438. public key shares x-coordinate with generator */ + {5850, 0, 6, 37016, 71, 0 }, + /* tcId: 439. public key shares x-coordinate with generator */ + {5850, 0, 6, 37087, 70, 0 }, + /* tcId: 440. public key shares x-coordinate with generator */ + {5915, 0, 6, 37157, 71, 0 }, + /* tcId: 441. public key shares x-coordinate with generator */ + {5915, 0, 6, 37228, 70, 0 }, + /* tcId: 442. pseudorandom signature */ + {5980, 546, 0, 37298, 71, 1 }, + /* tcId: 443. pseudorandom signature */ + {5980, 546, 3, 37369, 70, 1 }, + /* tcId: 444. pseudorandom signature */ + {5980, 0, 6, 37439, 71, 1 }, + /* tcId: 445. pseudorandom signature */ + {5980, 549, 20, 37510, 70, 1 }, + /* tcId: 446. y-coordinate of the public key is small */ + {6045, 569, 7, 37580, 70, 1 }, + /* tcId: 447. y-coordinate of the public key is small */ + {6045, 569, 7, 37650, 70, 1 }, + /* tcId: 448. y-coordinate of the public key is small */ + {6045, 569, 7, 37720, 71, 1 }, + /* tcId: 449. y-coordinate of the public key is large */ + {6110, 569, 7, 37791, 70, 1 }, + /* tcId: 450. y-coordinate of the public key is large */ + {6110, 569, 7, 37861, 71, 1 }, + /* tcId: 451. y-coordinate of the public key is large */ + {6110, 569, 7, 37932, 70, 1 }, + /* tcId: 452. x-coordinate of the public key is small */ + {6175, 569, 7, 38002, 70, 1 }, + /* tcId: 453. x-coordinate of the public key is small */ + {6175, 569, 7, 38072, 71, 1 }, + /* tcId: 454. x-coordinate of the public key is small */ + {6175, 569, 7, 38143, 71, 1 }, + /* tcId: 455. x-coordinate of the public key has many trailing 1's */ + {6240, 569, 7, 38214, 70, 1 }, + /* tcId: 456. x-coordinate of the public key has many trailing 1's */ + {6240, 569, 7, 38284, 71, 1 }, + /* tcId: 457. x-coordinate of the public key has many trailing 1's */ + {6240, 569, 7, 38355, 71, 1 }, + /* tcId: 458. y-coordinate of the public key has many trailing 1's */ + {6305, 569, 7, 38426, 70, 1 }, + /* tcId: 459. y-coordinate of the public key has many trailing 1's */ + {6305, 569, 7, 38496, 71, 1 }, + /* tcId: 460. y-coordinate of the public key has many trailing 1's */ + {6305, 569, 7, 38567, 71, 1 }, + /* tcId: 461. x-coordinate of the public key has many trailing 0's */ + {6370, 569, 7, 38638, 70, 1 }, + /* tcId: 462. x-coordinate of the public key has many trailing 0's */ + {6370, 569, 7, 38708, 70, 1 }, + /* tcId: 463. x-coordinate of the public key has many trailing 0's */ + {6370, 569, 7, 38778, 71, 1 }, + +}; diff --git a/src/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json b/src/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json new file mode 100644 index 000000000..9c9074799 --- /dev/null +++ b/src/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json @@ -0,0 +1,6360 @@ +{ + "algorithm" : "ECDSA", + "schema" : "ecdsa_bitcoin_verify_schema.json", + "generatorVersion" : "0.9rc5", + "numberOfTests" : 463, + "header" : [ + "Test vectors of type EcdsaBitcoinVerify are meant for the verification", + "of a ECDSA variant used for bitcoin, that add signature non-malleability." + ], + "notes" : { + "ArithmeticError" : { + "bugType" : "EDGE_CASE", + "description" : "Some implementations of ECDSA have arithmetic errors that occur when intermediate results have extreme values. This test vector has been constructed to test such occurences.", + "cves" : [ + "CVE-2017-18146" + ] + }, + "BerEncodedSignature" : { + "bugType" : "BER_ENCODING", + "description" : "ECDSA signatures are usually DER encoded. This signature contains valid values for r and s, but it uses alternative BER encoding.", + "effect" : "Accepting alternative BER encodings may be benign in some cases, or be an issue if protocol requires signature malleability.", + "cves" : [ + "CVE-2020-14966", + "CVE-2020-13822", + "CVE-2019-14859", + "CVE-2016-1000342" + ] + }, + "EdgeCasePublicKey" : { + "bugType" : "EDGE_CASE", + "description" : "The test vector uses a special case public key. " + }, + "EdgeCaseShamirMultiplication" : { + "bugType" : "EDGE_CASE", + "description" : "Shamir proposed a fast method for computing the sum of two scalar multiplications efficiently. This test vector has been constructed so that an intermediate result is the point at infinity if Shamir's method is used." + }, + "IntegerOverflow" : { + "bugType" : "CAN_OF_WORMS", + "description" : "The test vector contains an r and s that has been modified, so that the original value is restored if the implementation ignores the most significant bits.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." + }, + "InvalidEncoding" : { + "bugType" : "CAN_OF_WORMS", + "description" : "ECDSA signatures are encoded using ASN.1. This test vector contains an incorrectly encoded signature. The test vector itself was generated from a valid signature by modifying its encoding.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." + }, + "InvalidSignature" : { + "bugType" : "AUTH_BYPASS", + "description" : "The signature contains special case values such as r=0 and s=0. Buggy implementations may accept such values, if the implementation does not check boundaries and computes s^(-1) == 0.", + "effect" : "Accepting such signatures can have the effect that an adversary can forge signatures without even knowning the message to sign.", + "cves" : [ + "CVE-2022-21449", + "CVE-2021-43572", + "CVE-2022-24884" + ] + }, + "InvalidTypesInSignature" : { + "bugType" : "AUTH_BYPASS", + "description" : "The signature contains invalid types. Dynamic typed languages sometime coerce such values of different types into integers. If an implementation is careless and has additional bugs, such as not checking integer boundaries then it may be possible that such signatures are accepted.", + "effect" : "Accepting such signatures can have the effect that an adversary can forge signatures without even knowning the message to sign.", + "cves" : [ + "CVE-2022-21449" + ] + }, + "ModifiedInteger" : { + "bugType" : "CAN_OF_WORMS", + "description" : "The test vector contains an r and s that has been modified. The goal is to check for arithmetic errors.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." + }, + "ModifiedSignature" : { + "bugType" : "CAN_OF_WORMS", + "description" : "The test vector contains an invalid signature that was generated from a valid signature by modifying it.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." + }, + "ModularInverse" : { + "bugType" : "EDGE_CASE", + "description" : "The test vectors contains a signature where computing the modular inverse of s hits an edge case.", + "effect" : "While the signature in this test vector is constructed and similar cases are unlikely to occur, it is important to determine if the underlying arithmetic error can be used to forge signatures.", + "cves" : [ + "CVE-2019-0865" + ] + }, + "PointDuplication" : { + "bugType" : "EDGE_CASE", + "description" : "Some implementations of ECDSA do not handle duplication and points at infinity correctly. This is a test vector that has been specially crafted to check for such an omission.", + "cves" : [ + "2020-12607", + "CVE-2015-2730" + ] + }, + "RangeCheck" : { + "bugType" : "CAN_OF_WORMS", + "description" : "The test vector contains an r and s that has been modified. By adding or subtracting the order of the group (or other values) the test vector checks whether signature verification verifies the range of r and s.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." + }, + "SignatureMalleabilityBitcoin" : { + "bugType" : "SIGNATURE_MALLEABILITY", + "description" : "\"BitCoins\"-curves are curves where signature malleability can be a serious issue. An implementation should only accept a signature s where s < n/2. If an implementation is not meant for uses cases that require signature malleability then this implemenation should be tested with another set of test vectors.", + "effect" : "In bitcoin exchanges, it may be used to make a double deposits or double withdrawals", + "links" : [ + "https://en.bitcoin.it/wiki/Transaction_malleability", + "https://en.bitcoinwiki.org/wiki/Transaction_Malleability" + ] + }, + "SmallRandS" : { + "bugType" : "EDGE_CASE", + "description" : "The test vectors contains a signature where both r and s are small integers. Some libraries cannot verify such signatures.", + "effect" : "While the signature in this test vector is constructed and similar cases are unlikely to occur, it is important to determine if the underlying arithmetic error can be used to forge signatures.", + "cves" : [ + "2020-13895" + ] + }, + "SpecialCaseHash" : { + "bugType" : "EDGE_CASE", + "description" : "The test vector contains a signature where the hash of the message is a special case, e.g., contains a long run of 0 or 1 bits." + }, + "ValidSignature" : { + "bugType" : "BASIC", + "description" : "The test vector contains a valid signature that was generated pseudorandomly. Such signatures should not fail to verify unless some of the parameters (e.g. curve or hash function) are not supported." + } + }, + "testGroups" : [ + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9", + "wx" : "00b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6f", + "wy" : "00f0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEuDj/ROW8F3vyEYnQdmCC/J2EMiaIf8l2\nA3EQC37iCm/wyddb+6ezGmvKGXRJbutW3jVwcZVdg8Sxutqgshgy6Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 1, + "comment" : "Signature malleability", + "flags" : [ + "SignatureMalleabilityBitcoin" + ], + "msg" : "313233343030", + "sig" : "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365022100900e75ad233fcc908509dbff5922647db37c21f4afd3203ae8dc4ae7794b0f87", + "result" : "invalid" + }, + { + "tcId" : 2, + "comment" : "valid", + "flags" : [ + "ValidSignature" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "valid" + }, + { + "tcId" : 3, + "comment" : "length of sequence [r, s] uses long form encoding", + "flags" : [ + "BerEncodedSignature" + ], + "msg" : "313233343030", + "sig" : "308145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 4, + "comment" : "length of sequence [r, s] contains a leading 0", + "flags" : [ + "BerEncodedSignature" + ], + "msg" : "313233343030", + "sig" : "30820045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 5, + "comment" : "length of sequence [r, s] uses 70 instead of 69", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 6, + "comment" : "length of sequence [r, s] uses 68 instead of 69", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 7, + "comment" : "uint32 overflow in length of sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30850100000045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 8, + "comment" : "uint64 overflow in length of sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3089010000000000000045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 9, + "comment" : "length of sequence [r, s] = 2**31 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30847fffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 10, + "comment" : "length of sequence [r, s] = 2**31", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "308480000000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 11, + "comment" : "length of sequence [r, s] = 2**32 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3084ffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 12, + "comment" : "length of sequence [r, s] = 2**40 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3085ffffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 13, + "comment" : "length of sequence [r, s] = 2**64 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3088ffffffffffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 14, + "comment" : "incorrect length of sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30ff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 15, + "comment" : "replaced sequence [r, s] by an indefinite length tag without termination", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 16, + "comment" : "removing sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "", + "result" : "invalid" + }, + { + "tcId" : 17, + "comment" : "lonely sequence tag", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30", + "result" : "invalid" + }, + { + "tcId" : 18, + "comment" : "appending 0's to sequence [r, s]", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" + }, + { + "tcId" : 19, + "comment" : "prepending 0's to sequence [r, s]", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "30470000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 20, + "comment" : "appending unused 0's to sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" + }, + { + "tcId" : 21, + "comment" : "appending null value to sequence [r, s]", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0500", + "result" : "invalid" + }, + { + "tcId" : 22, + "comment" : "prepending garbage to sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304a4981773045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 23, + "comment" : "prepending garbage to sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304925003045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 24, + "comment" : "appending garbage to sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30473045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0004deadbeef", + "result" : "invalid" + }, + { + "tcId" : 25, + "comment" : "including undefined tags", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "304daa00bb00cd003045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 26, + "comment" : "including undefined tags", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304d2229aa00bb00cd00022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 27, + "comment" : "including undefined tags", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652228aa00bb00cd0002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 28, + "comment" : "truncated length of sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3081", + "result" : "invalid" + }, + { + "tcId" : 29, + "comment" : "including undefined tags to sequence [r, s]", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "304baa02aabb3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 30, + "comment" : "using composition with indefinite length for sequence [r, s]", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "30803045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" + }, + { + "tcId" : 31, + "comment" : "using composition with wrong tag for sequence [r, s]", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "30803145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" + }, + { + "tcId" : 32, + "comment" : "Replacing sequence [r, s] with NULL", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "0500", + "result" : "invalid" + }, + { + "tcId" : 33, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "2e45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 34, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "2f45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 35, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 36, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3245022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 37, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "ff45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 38, + "comment" : "dropping value of sequence [r, s]", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3000", + "result" : "invalid" + }, + { + "tcId" : 39, + "comment" : "using composition for sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304930010230442100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 40, + "comment" : "truncated sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31", + "result" : "invalid" + }, + { + "tcId" : 41, + "comment" : "truncated sequence [r, s]", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30442100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 42, + "comment" : "sequence [r, s] of size 4166 to check for overflows", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30821046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid" + }, + { + "tcId" : 43, + "comment" : "indefinite length", + "flags" : [ + "BerEncodedSignature" + ], + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" + }, + { + "tcId" : 44, + "comment" : "indefinite length with truncated delimiter", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba00", + "result" : "invalid" + }, + { + "tcId" : 45, + "comment" : "indefinite length with additional element", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba05000000", + "result" : "invalid" + }, + { + "tcId" : 46, + "comment" : "indefinite length with truncated element", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba060811220000", + "result" : "invalid" + }, + { + "tcId" : 47, + "comment" : "indefinite length with garbage", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000fe02beef", + "result" : "invalid" + }, + { + "tcId" : 48, + "comment" : "indefinite length with nonempty EOC", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0002beef", + "result" : "invalid" + }, + { + "tcId" : 49, + "comment" : "prepend empty sequence", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "30473000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 50, + "comment" : "append empty sequence", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba3000", + "result" : "invalid" + }, + { + "tcId" : 51, + "comment" : "append zero", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba020100", + "result" : "invalid" + }, + { + "tcId" : 52, + "comment" : "append garbage with high tag number", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31babf7f00", + "result" : "invalid" + }, + { + "tcId" : 53, + "comment" : "append null with explicit tag", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31baa0020500", + "result" : "invalid" + }, + { + "tcId" : 54, + "comment" : "append null with implicit tag", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31baa000", + "result" : "invalid" + }, + { + "tcId" : 55, + "comment" : "sequence of sequence", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "30473045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 56, + "comment" : "truncated sequence: removed last 1 elements", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3023022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365", + "result" : "invalid" + }, + { + "tcId" : 57, + "comment" : "repeating element in sequence", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3067022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 58, + "comment" : "flipped bit 0 in r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236402206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 59, + "comment" : "flipped bit 32 in r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccac983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 60, + "comment" : "flipped bit 48 in r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5133ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 61, + "comment" : "flipped bit 64 in r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc08b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 62, + "comment" : "length of r uses long form encoding", + "flags" : [ + "BerEncodedSignature" + ], + "msg" : "313233343030", + "sig" : "304602812100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 63, + "comment" : "length of r contains a leading 0", + "flags" : [ + "BerEncodedSignature" + ], + "msg" : "313233343030", + "sig" : "30470282002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 64, + "comment" : "length of r uses 34 instead of 33", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022200813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 65, + "comment" : "length of r uses 32 instead of 33", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 66, + "comment" : "uint32 overflow in length of r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304a0285010000002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 67, + "comment" : "uint64 overflow in length of r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304e028901000000000000002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 68, + "comment" : "length of r = 2**31 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304902847fffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 69, + "comment" : "length of r = 2**31", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304902848000000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 70, + "comment" : "length of r = 2**32 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30490284ffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 71, + "comment" : "length of r = 2**40 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304a0285ffffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 72, + "comment" : "length of r = 2**64 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304d0288ffffffffffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 73, + "comment" : "incorrect length of r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304502ff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 74, + "comment" : "replaced r by an indefinite length tag without termination", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045028000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 75, + "comment" : "removing r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "302202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 76, + "comment" : "lonely integer tag", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30230202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 77, + "comment" : "lonely integer tag", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3024022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502", + "result" : "invalid" + }, + { + "tcId" : 78, + "comment" : "appending 0's to r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 79, + "comment" : "prepending 0's to r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30470223000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 80, + "comment" : "appending unused 0's to r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 81, + "comment" : "appending null value to r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365050002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 82, + "comment" : "prepending garbage to r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304a2226498177022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 83, + "comment" : "prepending garbage to r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304922252500022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 84, + "comment" : "appending garbage to r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304d2223022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650004deadbeef02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 85, + "comment" : "truncated length of r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3024028102206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 86, + "comment" : "including undefined tags to r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304b2227aa02aabb022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 87, + "comment" : "using composition with indefinite length for r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30492280022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 88, + "comment" : "using composition with wrong tag for r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "30492280032100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 89, + "comment" : "Replacing r with NULL", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3024050002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 90, + "comment" : "changing tag value of r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 91, + "comment" : "changing tag value of r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045012100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 92, + "comment" : "changing tag value of r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045032100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 93, + "comment" : "changing tag value of r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045042100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 94, + "comment" : "changing tag value of r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045ff2100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 95, + "comment" : "dropping value of r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3024020002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 96, + "comment" : "using composition for r", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304922250201000220813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 97, + "comment" : "modifying first byte of r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045022102813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 98, + "comment" : "modifying last byte of r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323e502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 99, + "comment" : "truncated r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3044022000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832302206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 100, + "comment" : "truncated r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "30440220813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 101, + "comment" : "r of size 4130 to check for overflows", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "308210480282102200813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 102, + "comment" : "leading ff in r", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "30460222ff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 103, + "comment" : "replaced r by infinity", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "302509018002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 104, + "comment" : "replacing r with zero", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "302502010002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 105, + "comment" : "flipped bit 0 in s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31bb", + "result" : "invalid" + }, + { + "tcId" : 106, + "comment" : "flipped bit 32 in s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a456eb31ba", + "result" : "invalid" + }, + { + "tcId" : 107, + "comment" : "flipped bit 48 in s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f713a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 108, + "comment" : "flipped bit 64 in s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758001d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 109, + "comment" : "length of s uses long form encoding", + "flags" : [ + "BerEncodedSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650281206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 110, + "comment" : "length of s contains a leading 0", + "flags" : [ + "BerEncodedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028200206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 111, + "comment" : "length of s uses 33 instead of 32", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502216ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 112, + "comment" : "length of s uses 31 instead of 32", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021f6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 113, + "comment" : "uint32 overflow in length of s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028501000000206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 114, + "comment" : "uint64 overflow in length of s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304e022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502890100000000000000206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 115, + "comment" : "length of s = 2**31 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502847fffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 116, + "comment" : "length of s = 2**31", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650284800000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 117, + "comment" : "length of s = 2**32 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650284ffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 118, + "comment" : "length of s = 2**40 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650285ffffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 119, + "comment" : "length of s = 2**64 - 1", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650288ffffffffffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 120, + "comment" : "incorrect length of s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 121, + "comment" : "replaced s by an indefinite length tag without termination", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502806ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 122, + "comment" : "appending 0's to s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502226ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" + }, + { + "tcId" : 123, + "comment" : "prepending 0's to s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365022200006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 124, + "comment" : "appending null value to s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502226ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0500", + "result" : "invalid" + }, + { + "tcId" : 125, + "comment" : "prepending garbage to s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222549817702206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 126, + "comment" : "prepending garbage to s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652224250002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 127, + "comment" : "appending garbage to s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0004deadbeef", + "result" : "invalid" + }, + { + "tcId" : 128, + "comment" : "truncated length of s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650281", + "result" : "invalid" + }, + { + "tcId" : 129, + "comment" : "including undefined tags to s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "304b022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652226aa02aabb02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 130, + "comment" : "using composition with indefinite length for s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365228002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" + }, + { + "tcId" : 131, + "comment" : "using composition with wrong tag for s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365228003206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" + }, + { + "tcId" : 132, + "comment" : "Replacing s with NULL", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650500", + "result" : "invalid" + }, + { + "tcId" : 133, + "comment" : "changing tag value of s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236500206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 134, + "comment" : "changing tag value of s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236501206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 135, + "comment" : "changing tag value of s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236503206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 136, + "comment" : "changing tag value of s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236504206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 137, + "comment" : "changing tag value of s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365ff206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 138, + "comment" : "dropping value of s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650200", + "result" : "invalid" + }, + { + "tcId" : 139, + "comment" : "using composition for s", + "flags" : [ + "InvalidEncoding" + ], + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222402016f021ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 140, + "comment" : "modifying first byte of s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206df18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 141, + "comment" : "modifying last byte of s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb313a", + "result" : "invalid" + }, + { + "tcId" : 142, + "comment" : "truncated s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021f6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31", + "result" : "invalid" + }, + { + "tcId" : 143, + "comment" : "truncated s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 144, + "comment" : "s of size 4129 to check for overflows", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "30821048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028210216ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid" + }, + { + "tcId" : 145, + "comment" : "leading ff in s", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650221ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 146, + "comment" : "replaced s by infinity", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365090180", + "result" : "invalid" + }, + { + "tcId" : 147, + "comment" : "replacing s with zero", + "flags" : [ + "ModifiedSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365020100", + "result" : "invalid" + }, + { + "tcId" : 148, + "comment" : "replaced r by r + n", + "flags" : [ + "RangeCheck" + ], + "msg" : "313233343030", + "sig" : "3045022101813ef79ccefa9a56f7ba805f0e478583b90deabca4b05c4574e49b5899b964a602206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 149, + "comment" : "replaced r by r - n", + "flags" : [ + "RangeCheck" + ], + "msg" : "313233343030", + "sig" : "30440220813ef79ccefa9a56f7ba805f0e47858643b030ef461f1bcdf53fde3ef94ce22402206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 150, + "comment" : "replaced r by r + 256 * n", + "flags" : [ + "RangeCheck" + ], + "msg" : "313233343030", + "sig" : "304602220100813ef79ccefa9a56f7ba805f0e47843fad3bf4853e07f7c98770c99bffc4646502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 151, + "comment" : "replaced r by -r", + "flags" : [ + "ModifiedInteger" + ], + "msg" : "313233343030", + "sig" : "30450221ff7ec10863310565a908457fa0f1b87a7b01a0f22a0a9843f64aedc334367cdc9b02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 152, + "comment" : "replaced r by n - r", + "flags" : [ + "ModifiedInteger" + ], + "msg" : "313233343030", + "sig" : "304402207ec10863310565a908457fa0f1b87a79bc4fcf10b9e0e4320ac021c106b31ddc02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 153, + "comment" : "replaced r by -n - r", + "flags" : [ + "ModifiedInteger" + ], + "msg" : "313233343030", + "sig" : "30450221fe7ec10863310565a908457fa0f1b87a7c46f215435b4fa3ba8b1b64a766469b5a02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 154, + "comment" : "replaced r by r + 2**256", + "flags" : [ + "IntegerOverflow" + ], + "msg" : "313233343030", + "sig" : "3045022101813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 155, + "comment" : "replaced r by r + 2**320", + "flags" : [ + "IntegerOverflow" + ], + "msg" : "313233343030", + "sig" : "304d0229010000000000000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 156, + "comment" : "replaced s by s + n", + "flags" : [ + "RangeCheck" + ], + "msg" : "313233343030", + "sig" : "30450221016ff18a52dcc0336f7af62400a6dd9b7fc1e197d8aebe203c96c87232272172fb02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 157, + "comment" : "replaced s by s - n", + "flags" : [ + "RangeCheck" + ], + "msg" : "313233343030", + "sig" : "30450221ff6ff18a52dcc0336f7af62400a6dd9b824c83de0b502cdfc51723b51886b4f07902206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 158, + "comment" : "replaced s by s + 256 * n", + "flags" : [ + "RangeCheck" + ], + "msg" : "313233343030", + "sig" : "3046022201006ff18a52dcc0336f7af62400a6dd9a3bb60fa1a14815bbc0a954a0758d2c72ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 159, + "comment" : "replaced s by -s", + "flags" : [ + "ModifiedInteger" + ], + "msg" : "313233343030", + "sig" : "30440220900e75ad233fcc908509dbff5922647ef8cd450e008a7fff2909ec5aa914ce4602206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 160, + "comment" : "replaced s by -n - s", + "flags" : [ + "ModifiedInteger" + ], + "msg" : "313233343030", + "sig" : "30450221fe900e75ad233fcc908509dbff592264803e1e68275141dfc369378dcdd8de8d0502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 161, + "comment" : "replaced s by s + 2**256", + "flags" : [ + "IntegerOverflow" + ], + "msg" : "313233343030", + "sig" : "30450221016ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 162, + "comment" : "replaced s by s - 2**256", + "flags" : [ + "IntegerOverflow" + ], + "msg" : "313233343030", + "sig" : "30450221ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 163, + "comment" : "replaced s by s + 2**320", + "flags" : [ + "IntegerOverflow" + ], + "msg" : "313233343030", + "sig" : "304d02290100000000000000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" + }, + { + "tcId" : 164, + "comment" : "Signature with special case values r=0 and s=0", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3006020100020100", + "result" : "invalid" + }, + { + "tcId" : 165, + "comment" : "Signature with special case values r=0 and s=1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3006020100020101", + "result" : "invalid" + }, + { + "tcId" : 166, + "comment" : "Signature with special case values r=0 and s=-1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30060201000201ff", + "result" : "invalid" + }, + { + "tcId" : 167, + "comment" : "Signature with special case values r=0 and s=n", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" + }, + { + "tcId" : 168, + "comment" : "Signature with special case values r=0 and s=n - 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" + }, + { + "tcId" : 169, + "comment" : "Signature with special case values r=0 and s=n + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" + }, + { + "tcId" : 170, + "comment" : "Signature with special case values r=0 and s=p", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" + }, + { + "tcId" : 171, + "comment" : "Signature with special case values r=0 and s=p + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" + }, + { + "tcId" : 172, + "comment" : "Signature with special case values r=1 and s=0", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3006020101020100", + "result" : "invalid" + }, + { + "tcId" : 173, + "comment" : "Signature with special case values r=1 and s=1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3006020101020101", + "result" : "invalid" + }, + { + "tcId" : 174, + "comment" : "Signature with special case values r=1 and s=-1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30060201010201ff", + "result" : "invalid" + }, + { + "tcId" : 175, + "comment" : "Signature with special case values r=1 and s=n", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" + }, + { + "tcId" : 176, + "comment" : "Signature with special case values r=1 and s=n - 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" + }, + { + "tcId" : 177, + "comment" : "Signature with special case values r=1 and s=n + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" + }, + { + "tcId" : 178, + "comment" : "Signature with special case values r=1 and s=p", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" + }, + { + "tcId" : 179, + "comment" : "Signature with special case values r=1 and s=p + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" + }, + { + "tcId" : 180, + "comment" : "Signature with special case values r=-1 and s=0", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30060201ff020100", + "result" : "invalid" + }, + { + "tcId" : 181, + "comment" : "Signature with special case values r=-1 and s=1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30060201ff020101", + "result" : "invalid" + }, + { + "tcId" : 182, + "comment" : "Signature with special case values r=-1 and s=-1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30060201ff0201ff", + "result" : "invalid" + }, + { + "tcId" : 183, + "comment" : "Signature with special case values r=-1 and s=n", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" + }, + { + "tcId" : 184, + "comment" : "Signature with special case values r=-1 and s=n - 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" + }, + { + "tcId" : 185, + "comment" : "Signature with special case values r=-1 and s=n + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" + }, + { + "tcId" : 186, + "comment" : "Signature with special case values r=-1 and s=p", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" + }, + { + "tcId" : 187, + "comment" : "Signature with special case values r=-1 and s=p + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" + }, + { + "tcId" : 188, + "comment" : "Signature with special case values r=n and s=0", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020100", + "result" : "invalid" + }, + { + "tcId" : 189, + "comment" : "Signature with special case values r=n and s=1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101", + "result" : "invalid" + }, + { + "tcId" : 190, + "comment" : "Signature with special case values r=n and s=-1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410201ff", + "result" : "invalid" + }, + { + "tcId" : 191, + "comment" : "Signature with special case values r=n and s=n", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" + }, + { + "tcId" : 192, + "comment" : "Signature with special case values r=n and s=n - 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" + }, + { + "tcId" : 193, + "comment" : "Signature with special case values r=n and s=n + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" + }, + { + "tcId" : 194, + "comment" : "Signature with special case values r=n and s=p", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" + }, + { + "tcId" : 195, + "comment" : "Signature with special case values r=n and s=p + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" + }, + { + "tcId" : 196, + "comment" : "Signature with special case values r=n - 1 and s=0", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140020100", + "result" : "invalid" + }, + { + "tcId" : 197, + "comment" : "Signature with special case values r=n - 1 and s=1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140020101", + "result" : "invalid" + }, + { + "tcId" : 198, + "comment" : "Signature with special case values r=n - 1 and s=-1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641400201ff", + "result" : "invalid" + }, + { + "tcId" : 199, + "comment" : "Signature with special case values r=n - 1 and s=n", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" + }, + { + "tcId" : 200, + "comment" : "Signature with special case values r=n - 1 and s=n - 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" + }, + { + "tcId" : 201, + "comment" : "Signature with special case values r=n - 1 and s=n + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" + }, + { + "tcId" : 202, + "comment" : "Signature with special case values r=n - 1 and s=p", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" + }, + { + "tcId" : 203, + "comment" : "Signature with special case values r=n - 1 and s=p + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" + }, + { + "tcId" : 204, + "comment" : "Signature with special case values r=n + 1 and s=0", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142020100", + "result" : "invalid" + }, + { + "tcId" : 205, + "comment" : "Signature with special case values r=n + 1 and s=1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142020101", + "result" : "invalid" + }, + { + "tcId" : 206, + "comment" : "Signature with special case values r=n + 1 and s=-1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641420201ff", + "result" : "invalid" + }, + { + "tcId" : 207, + "comment" : "Signature with special case values r=n + 1 and s=n", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" + }, + { + "tcId" : 208, + "comment" : "Signature with special case values r=n + 1 and s=n - 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" + }, + { + "tcId" : 209, + "comment" : "Signature with special case values r=n + 1 and s=n + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" + }, + { + "tcId" : 210, + "comment" : "Signature with special case values r=n + 1 and s=p", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" + }, + { + "tcId" : 211, + "comment" : "Signature with special case values r=n + 1 and s=p + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" + }, + { + "tcId" : 212, + "comment" : "Signature with special case values r=p and s=0", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f020100", + "result" : "invalid" + }, + { + "tcId" : 213, + "comment" : "Signature with special case values r=p and s=1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f020101", + "result" : "invalid" + }, + { + "tcId" : 214, + "comment" : "Signature with special case values r=p and s=-1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0201ff", + "result" : "invalid" + }, + { + "tcId" : 215, + "comment" : "Signature with special case values r=p and s=n", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" + }, + { + "tcId" : 216, + "comment" : "Signature with special case values r=p and s=n - 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" + }, + { + "tcId" : 217, + "comment" : "Signature with special case values r=p and s=n + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" + }, + { + "tcId" : 218, + "comment" : "Signature with special case values r=p and s=p", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" + }, + { + "tcId" : 219, + "comment" : "Signature with special case values r=p and s=p + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" + }, + { + "tcId" : 220, + "comment" : "Signature with special case values r=p + 1 and s=0", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30020100", + "result" : "invalid" + }, + { + "tcId" : 221, + "comment" : "Signature with special case values r=p + 1 and s=1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30020101", + "result" : "invalid" + }, + { + "tcId" : 222, + "comment" : "Signature with special case values r=p + 1 and s=-1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc300201ff", + "result" : "invalid" + }, + { + "tcId" : 223, + "comment" : "Signature with special case values r=p + 1 and s=n", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" + }, + { + "tcId" : 224, + "comment" : "Signature with special case values r=p + 1 and s=n - 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" + }, + { + "tcId" : 225, + "comment" : "Signature with special case values r=p + 1 and s=n + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" + }, + { + "tcId" : 226, + "comment" : "Signature with special case values r=p + 1 and s=p", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" + }, + { + "tcId" : 227, + "comment" : "Signature with special case values r=p + 1 and s=p + 1", + "flags" : [ + "InvalidSignature" + ], + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" + }, + { + "tcId" : 228, + "comment" : "Signature encoding contains incorrect types: r=0, s=0.25", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3008020100090380fe01", + "result" : "invalid" + }, + { + "tcId" : 229, + "comment" : "Signature encoding contains incorrect types: r=0, s=nan", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006020100090142", + "result" : "invalid" + }, + { + "tcId" : 230, + "comment" : "Signature encoding contains incorrect types: r=0, s=True", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006020100010101", + "result" : "invalid" + }, + { + "tcId" : 231, + "comment" : "Signature encoding contains incorrect types: r=0, s=False", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006020100010100", + "result" : "invalid" + }, + { + "tcId" : 232, + "comment" : "Signature encoding contains incorrect types: r=0, s=Null", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201000500", + "result" : "invalid" + }, + { + "tcId" : 233, + "comment" : "Signature encoding contains incorrect types: r=0, s=empyt UTF-8 string", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201000c00", + "result" : "invalid" + }, + { + "tcId" : 234, + "comment" : "Signature encoding contains incorrect types: r=0, s=\"0\"", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30060201000c0130", + "result" : "invalid" + }, + { + "tcId" : 235, + "comment" : "Signature encoding contains incorrect types: r=0, s=empty list", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201003000", + "result" : "invalid" + }, + { + "tcId" : 236, + "comment" : "Signature encoding contains incorrect types: r=0, s=list containing 0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30080201003003020100", + "result" : "invalid" + }, + { + "tcId" : 237, + "comment" : "Signature encoding contains incorrect types: r=1, s=0.25", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3008020101090380fe01", + "result" : "invalid" + }, + { + "tcId" : 238, + "comment" : "Signature encoding contains incorrect types: r=1, s=nan", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006020101090142", + "result" : "invalid" + }, + { + "tcId" : 239, + "comment" : "Signature encoding contains incorrect types: r=1, s=True", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006020101010101", + "result" : "invalid" + }, + { + "tcId" : 240, + "comment" : "Signature encoding contains incorrect types: r=1, s=False", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006020101010100", + "result" : "invalid" + }, + { + "tcId" : 241, + "comment" : "Signature encoding contains incorrect types: r=1, s=Null", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201010500", + "result" : "invalid" + }, + { + "tcId" : 242, + "comment" : "Signature encoding contains incorrect types: r=1, s=empyt UTF-8 string", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201010c00", + "result" : "invalid" + }, + { + "tcId" : 243, + "comment" : "Signature encoding contains incorrect types: r=1, s=\"0\"", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30060201010c0130", + "result" : "invalid" + }, + { + "tcId" : 244, + "comment" : "Signature encoding contains incorrect types: r=1, s=empty list", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201013000", + "result" : "invalid" + }, + { + "tcId" : 245, + "comment" : "Signature encoding contains incorrect types: r=1, s=list containing 0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30080201013003020100", + "result" : "invalid" + }, + { + "tcId" : 246, + "comment" : "Signature encoding contains incorrect types: r=-1, s=0.25", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30080201ff090380fe01", + "result" : "invalid" + }, + { + "tcId" : 247, + "comment" : "Signature encoding contains incorrect types: r=-1, s=nan", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30060201ff090142", + "result" : "invalid" + }, + { + "tcId" : 248, + "comment" : "Signature encoding contains incorrect types: r=-1, s=True", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30060201ff010101", + "result" : "invalid" + }, + { + "tcId" : 249, + "comment" : "Signature encoding contains incorrect types: r=-1, s=False", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30060201ff010100", + "result" : "invalid" + }, + { + "tcId" : 250, + "comment" : "Signature encoding contains incorrect types: r=-1, s=Null", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201ff0500", + "result" : "invalid" + }, + { + "tcId" : 251, + "comment" : "Signature encoding contains incorrect types: r=-1, s=empyt UTF-8 string", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201ff0c00", + "result" : "invalid" + }, + { + "tcId" : 252, + "comment" : "Signature encoding contains incorrect types: r=-1, s=\"0\"", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30060201ff0c0130", + "result" : "invalid" + }, + { + "tcId" : 253, + "comment" : "Signature encoding contains incorrect types: r=-1, s=empty list", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050201ff3000", + "result" : "invalid" + }, + { + "tcId" : 254, + "comment" : "Signature encoding contains incorrect types: r=-1, s=list containing 0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30080201ff3003020100", + "result" : "invalid" + }, + { + "tcId" : 255, + "comment" : "Signature encoding contains incorrect types: r=n, s=0.25", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3028022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141090380fe01", + "result" : "invalid" + }, + { + "tcId" : 256, + "comment" : "Signature encoding contains incorrect types: r=n, s=nan", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141090142", + "result" : "invalid" + }, + { + "tcId" : 257, + "comment" : "Signature encoding contains incorrect types: r=n, s=True", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141010101", + "result" : "invalid" + }, + { + "tcId" : 258, + "comment" : "Signature encoding contains incorrect types: r=n, s=False", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141010100", + "result" : "invalid" + }, + { + "tcId" : 259, + "comment" : "Signature encoding contains incorrect types: r=n, s=Null", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410500", + "result" : "invalid" + }, + { + "tcId" : 260, + "comment" : "Signature encoding contains incorrect types: r=n, s=empyt UTF-8 string", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410c00", + "result" : "invalid" + }, + { + "tcId" : 261, + "comment" : "Signature encoding contains incorrect types: r=n, s=\"0\"", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410c0130", + "result" : "invalid" + }, + { + "tcId" : 262, + "comment" : "Signature encoding contains incorrect types: r=n, s=empty list", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641413000", + "result" : "invalid" + }, + { + "tcId" : 263, + "comment" : "Signature encoding contains incorrect types: r=n, s=list containing 0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3028022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641413003020100", + "result" : "invalid" + }, + { + "tcId" : 264, + "comment" : "Signature encoding contains incorrect types: r=p, s=0.25", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3028022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f090380fe01", + "result" : "invalid" + }, + { + "tcId" : 265, + "comment" : "Signature encoding contains incorrect types: r=p, s=nan", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f090142", + "result" : "invalid" + }, + { + "tcId" : 266, + "comment" : "Signature encoding contains incorrect types: r=p, s=True", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f010101", + "result" : "invalid" + }, + { + "tcId" : 267, + "comment" : "Signature encoding contains incorrect types: r=p, s=False", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f010100", + "result" : "invalid" + }, + { + "tcId" : 268, + "comment" : "Signature encoding contains incorrect types: r=p, s=Null", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0500", + "result" : "invalid" + }, + { + "tcId" : 269, + "comment" : "Signature encoding contains incorrect types: r=p, s=empyt UTF-8 string", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0c00", + "result" : "invalid" + }, + { + "tcId" : 270, + "comment" : "Signature encoding contains incorrect types: r=p, s=\"0\"", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0c0130", + "result" : "invalid" + }, + { + "tcId" : 271, + "comment" : "Signature encoding contains incorrect types: r=p, s=empty list", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f3000", + "result" : "invalid" + }, + { + "tcId" : 272, + "comment" : "Signature encoding contains incorrect types: r=p, s=list containing 0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3028022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f3003020100", + "result" : "invalid" + }, + { + "tcId" : 273, + "comment" : "Signature encoding contains incorrect types: r=0.25, s=0.25", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "300a090380fe01090380fe01", + "result" : "invalid" + }, + { + "tcId" : 274, + "comment" : "Signature encoding contains incorrect types: r=nan, s=nan", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006090142090142", + "result" : "invalid" + }, + { + "tcId" : 275, + "comment" : "Signature encoding contains incorrect types: r=True, s=True", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006010101010101", + "result" : "invalid" + }, + { + "tcId" : 276, + "comment" : "Signature encoding contains incorrect types: r=False, s=False", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006010100010100", + "result" : "invalid" + }, + { + "tcId" : 277, + "comment" : "Signature encoding contains incorrect types: r=Null, s=Null", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "300405000500", + "result" : "invalid" + }, + { + "tcId" : 278, + "comment" : "Signature encoding contains incorrect types: r=empyt UTF-8 string, s=empyt UTF-8 string", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30040c000c00", + "result" : "invalid" + }, + { + "tcId" : 279, + "comment" : "Signature encoding contains incorrect types: r=\"0\", s=\"0\"", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30060c01300c0130", + "result" : "invalid" + }, + { + "tcId" : 280, + "comment" : "Signature encoding contains incorrect types: r=empty list, s=empty list", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "300430003000", + "result" : "invalid" + }, + { + "tcId" : 281, + "comment" : "Signature encoding contains incorrect types: r=list containing 0, s=list containing 0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "300a30030201003003020100", + "result" : "invalid" + }, + { + "tcId" : 282, + "comment" : "Signature encoding contains incorrect types: r=0.25, s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3008090380fe01020100", + "result" : "invalid" + }, + { + "tcId" : 283, + "comment" : "Signature encoding contains incorrect types: r=nan, s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006090142020100", + "result" : "invalid" + }, + { + "tcId" : 284, + "comment" : "Signature encoding contains incorrect types: r=True, s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006010101020100", + "result" : "invalid" + }, + { + "tcId" : 285, + "comment" : "Signature encoding contains incorrect types: r=False, s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "3006010100020100", + "result" : "invalid" + }, + { + "tcId" : 286, + "comment" : "Signature encoding contains incorrect types: r=Null, s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050500020100", + "result" : "invalid" + }, + { + "tcId" : 287, + "comment" : "Signature encoding contains incorrect types: r=empyt UTF-8 string, s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30050c00020100", + "result" : "invalid" + }, + { + "tcId" : 288, + "comment" : "Signature encoding contains incorrect types: r=\"0\", s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30060c0130020100", + "result" : "invalid" + }, + { + "tcId" : 289, + "comment" : "Signature encoding contains incorrect types: r=empty list, s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30053000020100", + "result" : "invalid" + }, + { + "tcId" : 290, + "comment" : "Signature encoding contains incorrect types: r=list containing 0, s=0", + "flags" : [ + "InvalidTypesInSignature" + ], + "msg" : "313233343030", + "sig" : "30083003020100020100", + "result" : "invalid" + }, + { + "tcId" : 291, + "comment" : "Edge case for Shamir multiplication", + "flags" : [ + "EdgeCaseShamirMultiplication" + ], + "msg" : "3235353835", + "sig" : "3045022100dd1b7d09a7bd8218961034a39a87fecf5314f00c4d25eb58a07ac85e85eab516022035138c401ef8d3493d65c9002fe62b43aee568731b744548358996d9cc427e06", + "result" : "valid" + }, + { + "tcId" : 292, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "343236343739373234", + "sig" : "304502210095c29267d972a043d955224546222bba343fc1d4db0fec262a33ac61305696ae02206edfe96713aed56f8a28a6653f57e0b829712e5eddc67f34682b24f0676b2640", + "result" : "valid" + }, + { + "tcId" : 293, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "37313338363834383931", + "sig" : "3044022028f94a894e92024699e345fe66971e3edcd050023386135ab3939d550898fb25022032963e5bd41fa5911ed8f37deb86dae0a762bb6121c894615083c5d95ea01db3", + "result" : "valid" + }, + { + "tcId" : 294, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "3130333539333331363638", + "sig" : "3045022100be26b18f9549f89f411a9b52536b15aa270b84548d0e859a1952a27af1a77ac6022070c1d4fa9cd03cc8eaa8d506edb97eed7b8358b453c88aefbb880a3f0e8d472f", + "result" : "valid" + }, + { + "tcId" : 295, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33393439343031323135", + "sig" : "3045022100b1a4b1478e65cc3eafdf225d1298b43f2da19e4bcff7eacc0a2e98cd4b74b1140220179aa31e304cc142cf5073171751b28f3f5e0fa88c994e7c55f1bc07b8d56c16", + "result" : "valid" + }, + { + "tcId" : 296, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31333434323933303739", + "sig" : "30440220325332021261f1bd18f2712aa1e2252da23796da8a4b1ff6ea18cafec7e171f2022040b4f5e287ee61fc3c804186982360891eaa35c75f05a43ecd48b35d984a6648", + "result" : "valid" + }, + { + "tcId" : 297, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33373036323131373132", + "sig" : "3045022100a23ad18d8fc66d81af0903890cbd453a554cb04cdc1a8ca7f7f78e5367ed88a0022023e3eb2ce1c04ea748c389bd97374aa9413b9268851c04dcd9f88e78813fee56", + "result" : "valid" + }, + { + "tcId" : 298, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "333433363838373132", + "sig" : "304402202bdea41cda63a2d14bf47353bd20880a690901de7cd6e3cc6d8ed5ba0cdb109102203cea66bccfc9f9bf8c7ca4e1c1457cc9145e13e936d90b3d9c7786b8b26cf4c7", + "result" : "valid" + }, + { + "tcId" : 299, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31333531353330333730", + "sig" : "3045022100d7cd76ec01c1b1079eba9e2aa2a397243c4758c98a1ba0b7404a340b9b00ced602203575001e19d922e6de8b3d6c84ea43b5c3338106cf29990134e7669a826f78e6", + "result" : "valid" + }, + { + "tcId" : 300, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "36353533323033313236", + "sig" : "3045022100a872c744d936db21a10c361dd5c9063355f84902219652f6fc56dc95a7139d960220400df7575d9756210e9ccc77162c6b593c7746cfb48ac263c42750b421ef4bb9", + "result" : "valid" + }, + { + "tcId" : 301, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31353634333436363033", + "sig" : "30450221009fa9afe07752da10b36d3afcd0fe44bfc40244d75203599cf8f5047fa3453854022050e0a7c013bfbf51819736972d44b4b56bc2a2b2c180df6ec672df171410d77a", + "result" : "valid" + }, + { + "tcId" : 302, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "34343239353339313137", + "sig" : "3045022100885640384d0d910efb177b46be6c3dc5cac81f0b88c3190bb6b5f99c2641f2050220738ed9bff116306d9caa0f8fc608be243e0b567779d8dab03e8e19d553f1dc8e", + "result" : "valid" + }, + { + "tcId" : 303, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "3130393533323631333531", + "sig" : "304402202d051f91c5a9d440c5676985710483bc4f1a6c611b10c95a2ff0363d90c2a45802206ddf94e6fba5be586833d0c53cf216ad3948f37953c26c1cf4968e9a9e8243dc", + "result" : "valid" + }, + { + "tcId" : 304, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "35393837333530303431", + "sig" : "3045022100f3ac2523967482f53d508522712d583f4379cd824101ff635ea0935117baa54f022027f10812227397e02cea96fb0e680761636dab2b080d1fc5d11685cbe8500cfe", + "result" : "valid" + }, + { + "tcId" : 305, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33343633303036383738", + "sig" : "304502210096447cf68c3ab7266ed7447de3ac52fed7cc08cbdfea391c18a9b8ab370bc91302200f5e7874d3ac0e918f01c885a1639177c923f8660d1ceba1ca1f301bc675cdbc", + "result" : "valid" + }, + { + "tcId" : 306, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "39383137333230323837", + "sig" : "30440220530a0832b691da0b5619a0b11de6877f3c0971baaa68ed122758c29caaf46b7202206c89e44f5eb33060ea4b46318c39138eaedec72de42ba576579a6a4690e339f3", + "result" : "valid" + }, + { + "tcId" : 307, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33323232303431303436", + "sig" : "30450221009c54c25500bde0b92d72d6ec483dc2482f3654294ca74de796b681255ed58a770220677453c6b56f527631c9f67b3f3eb621fd88582b4aff156d2f1567d6211a2a33", + "result" : "valid" + }, + { + "tcId" : 308, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "36363636333037313034", + "sig" : "3045022100e7909d41439e2f6af29136c7348ca2641a2b070d5b64f91ea9da7070c7a2618b022042d782f132fa1d36c2c88ba27c3d678d80184a5d1eccac7501f0b47e3d205008", + "result" : "valid" + }, + { + "tcId" : 309, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31303335393531383938", + "sig" : "304402205924873209593135a4c3da7bb381227f8a4b6aa9f34fe5bb7f8fbc131a039ffe02201f1bb11b441c8feaa40f44213d9a405ed792d59fb49d5bcdd9a4285ae5693022", + "result" : "valid" + }, + { + "tcId" : 310, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31383436353937313935", + "sig" : "3045022100eeb692c9b262969b231c38b5a7f60649e0c875cd64df88f33aa571fa3d29ab0e0220218b3a1eb06379c2c18cf51b06430786d1c64cd2d24c9b232b23e5bac7989acd", + "result" : "valid" + }, + { + "tcId" : 311, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33313336303436313839", + "sig" : "3045022100a40034177f36091c2b653684a0e3eb5d4bff18e4d09f664c2800e7cafda1daf802203a3ec29853704e52031c58927a800a968353adc3d973beba9172cbbeab4dd149", + "result" : "valid" + }, + { + "tcId" : 312, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "32363633373834323534", + "sig" : "3045022100b5d795cc75cea5c434fa4185180cd6bd21223f3d5a86da6670d71d95680dadbf022054e4d8810a001ecbb9f7ca1c2ebfdb9d009e9031a431aca3c20ab4e0d1374ec1", + "result" : "valid" + }, + { + "tcId" : 313, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31363532313030353234", + "sig" : "3044022007dc2478d43c1232a4595608c64426c35510051a631ae6a5a6eb1161e57e42e102204a59ea0fdb72d12165cea3bf1ca86ba97517bd188db3dbd21a5a157850021984", + "result" : "valid" + }, + { + "tcId" : 314, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "35373438303831363936", + "sig" : "3045022100ddd20c4a05596ca868b558839fce9f6511ddd83d1ccb53f82e5269d559a0155202205b91734729d93093ff22123c4a25819d7feb66a250663fc780cb66fc7b6e6d17", + "result" : "valid" + }, + { + "tcId" : 315, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "36333433393133343638", + "sig" : "30450221009cde6e0ede0a003f02fda0a01b59facfe5dec063318f279ce2de7a9b1062f7b702202886a5b8c679bdf8224c66f908fd6205492cb70b0068d46ae4f33a4149b12a52", + "result" : "valid" + }, + { + "tcId" : 316, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31353431313033353938", + "sig" : "3045022100c5771016d0dd6357143c89f684cd740423502554c0c59aa8c99584f1ff38f609022054b405f4477546686e464c5463b4fd4190572e58d0f7e7357f6e61947d20715c", + "result" : "valid" + }, + { + "tcId" : 317, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "3130343738353830313238", + "sig" : "3045022100a24ebc0ec224bd67ae397cbe6fa37b3125adbd34891abe2d7c7356921916dfe6022034f6eb6374731bbbafc4924fb8b0bdcdda49456d724cdae6178d87014cb53d8c", + "result" : "valid" + }, + { + "tcId" : 318, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "3130353336323835353638", + "sig" : "304402202557d64a7aee2e0931c012e4fea1cd3a2c334edae68cdeb7158caf21b68e5a2402207f06cdbb6a90023a973882ed97b080fe6b05af3ec93db6f1a4399a69edf7670d", + "result" : "valid" + }, + { + "tcId" : 319, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "393533393034313035", + "sig" : "3045022100c4f2eccbb6a24350c8466450b9d61b207ee359e037b3dcedb42a3f2e6dd6aeb502203263c6b59a2f55cdd1c6e14894d5e5963b28bc3e2469ac9ba1197991ca7ff9c7", + "result" : "valid" + }, + { + "tcId" : 320, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "393738383438303339", + "sig" : "3045022100eff04781c9cbcd162d0a25a6e2ebcca43506c523385cb515d49ea38a1b12fcad022015acd73194c91a95478534f23015b672ebed213e45424dd2c8e26ac8b3eb34a5", + "result" : "valid" + }, + { + "tcId" : 321, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33363130363732343432", + "sig" : "3045022100f58b4e3110a64bf1b5db97639ee0e5a9c8dfa49dc59b679891f520fdf0584c8702202cd8fe51888aee9db3e075440fd4db73b5c732fb87b510e97093d66415f62af7", + "result" : "valid" + }, + { + "tcId" : 322, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31303534323430373035", + "sig" : "3045022100f8abecaa4f0c502de4bf5903d48417f786bf92e8ad72fec0bd7fcb7800c0bbe302204c7f9e231076a30b7ae36b0cebe69ccef1cd194f7cce93a5588fd6814f437c0e", + "result" : "valid" + }, + { + "tcId" : 323, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "35313734343438313937", + "sig" : "304402205d5b38bd37ad498b2227a633268a8cca879a5c7c94a4e416bd0a614d09e606d2022012b8d664ea9991062ecbb834e58400e25c46007af84f6007d7f1685443269afe", + "result" : "valid" + }, + { + "tcId" : 324, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31393637353631323531", + "sig" : "304402200c1cd9fe4034f086a2b52d65b9d3834d72aebe7f33dfe8f976da82648177d8e3022013105782e3d0cfe85c2778dec1a848b27ac0ae071aa6da341a9553a946b41e59", + "result" : "valid" + }, + { + "tcId" : 325, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33343437323533333433", + "sig" : "3045022100ae7935fb96ff246b7b5d5662870d1ba587b03d6e1360baf47988b5c02ccc1a5b02205f00c323272083782d4a59f2dfd65e49de0693627016900ef7e61428056664b3", + "result" : "valid" + }, + { + "tcId" : 326, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "333638323634333138", + "sig" : "3044022000a134b5c6ccbcefd4c882b945baeb4933444172795fa6796aae1490675470980220566e46105d24d890151e3eea3ebf88f5b92b3f5ec93a217765a6dcbd94f2c55b", + "result" : "valid" + }, + { + "tcId" : 327, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33323631313938363038", + "sig" : "304402202e4721363ad3992c139e5a1c26395d2c2d777824aa24fde075e0d7381171309d0220740f7c494418e1300dd4512f782a58800bff6a7abdfdd20fbbd4f05515ca1a4f", + "result" : "valid" + }, + { + "tcId" : 328, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "39363738373831303934", + "sig" : "304402206852e9d3cd9fe373c2d504877967d365ab1456707b6817a042864694e1960ccf0220064b27ea142b30887b84c86adccb2fa39a6911ad21fc7e819f593be52bc4f3bd", + "result" : "valid" + }, + { + "tcId" : 329, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "34393538383233383233", + "sig" : "30440220188a8c5648dc79eace158cf886c62b5468f05fd95f03a7635c5b4c31f09af4c5022036361a0b571a00c6cd5e686ccbfcfa703c4f97e48938346d0c103fdc76dc5867", + "result" : "valid" + }, + { + "tcId" : 330, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "383234363337383337", + "sig" : "3045022100a74f1fb9a8263f62fc4416a5b7d584f4206f3996bb91f6fc8e73b9e92bad0e1302206815032e8c7d76c3ab06a86f33249ce9940148cb36d1f417c2e992e801afa3fa", + "result" : "valid" + }, + { + "tcId" : 331, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "3131303230383333373736", + "sig" : "3044022007244865b72ff37e62e3146f0dc14682badd7197799135f0b00ade7671742bfe02200d80c2238edb4e4a7a86a8c57ca9af1711f406f7f5da0299aa04e2932d960754", + "result" : "valid" + }, + { + "tcId" : 332, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "313333383731363438", + "sig" : "3045022100da7fdd05b5badabd619d805c4ee7d9a84f84ddd5cf9c5bf4d4338140d689ef08022028f1cf4fa1c3c5862cfa149c0013cf5fe6cf5076cae000511063e7de25bb38e5", + "result" : "valid" + }, + { + "tcId" : 333, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "333232313434313632", + "sig" : "3045022100d3027c656f6d4fdfd8ede22093e3c303b0133c340d615e7756f6253aea927238022009aef060c8e4cef972974011558df144fed25ca69ae8d0b2eaf1a8feefbec417", + "result" : "valid" + }, + { + "tcId" : 334, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "3130363836363535353436", + "sig" : "304402200bf6c0188dc9571cd0e21eecac5fbb19d2434988e9cc10244593ef3a98099f6902204864a562661f9221ec88e3dd0bc2f6e27ac128c30cc1a80f79ec670a22b042ee", + "result" : "valid" + }, + { + "tcId" : 335, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "3632313535323436", + "sig" : "3045022100ae459640d5d1179be47a47fa538e16d94ddea5585e7a244804a51742c686443a02206c8e30e530a634fae80b3ceb062978b39edbe19777e0a24553b68886181fd897", + "result" : "valid" + }, + { + "tcId" : 336, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "37303330383138373734", + "sig" : "304402201cf3517ba3bf2ab8b9ead4ebb6e866cb88a1deacb6a785d3b63b483ca02ac4950220249a798b73606f55f5f1c70de67cb1a0cff95d7dc50b3a617df861bad3c6b1c9", + "result" : "valid" + }, + { + "tcId" : 337, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "35393234353233373434", + "sig" : "3045022100e69b5238265ea35d77e4dd172288d8cea19810a10292617d5976519dc5757cb802204b03c5bc47e826bdb27328abd38d3056d77476b2130f3df6ec4891af08ba1e29", + "result" : "valid" + }, + { + "tcId" : 338, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31343935353836363231", + "sig" : "304402205f9d7d7c870d085fc1d49fff69e4a275812800d2cf8973e7325866cb40fa2b6f02206d1f5491d9f717a597a15fd540406486d76a44697b3f0d9d6dcef6669f8a0a56", + "result" : "valid" + }, + { + "tcId" : 339, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "34303035333134343036", + "sig" : "304402200a7d5b1959f71df9f817146ee49bd5c89b431e7993e2fdecab6858957da685ae02200f8aad2d254690bdc13f34a4fec44a02fd745a422df05ccbb54635a8b86b9609", + "result" : "valid" + }, + { + "tcId" : 340, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "33303936343537353132", + "sig" : "3044022079e88bf576b74bc07ca142395fda28f03d3d5e640b0b4ff0752c6d94cd553408022032cea05bd2d706c8f6036a507e2ab7766004f0904e2e5c5862749c0073245d6a", + "result" : "valid" + }, + { + "tcId" : 341, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "32373834303235363230", + "sig" : "30450221009d54e037a00212b377bc8874798b8da080564bbdf7e07591b861285809d01488022018b4e557667a82bd95965f0706f81a29243fbdd86968a7ebeb43069db3b18c7f", + "result" : "valid" + }, + { + "tcId" : 342, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "32363138373837343138", + "sig" : "304402202664f1ffa982fedbcc7cab1b8bc6e2cb420218d2a6077ad08e591ba9feab33bd022049f5c7cb515e83872a3d41b4cdb85f242ad9d61a5bfc01debfbb52c6c84ba728", + "result" : "valid" + }, + { + "tcId" : 343, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "31363432363235323632", + "sig" : "304402205827518344844fd6a7de73cbb0a6befdea7b13d2dee4475317f0f18ffc81524b02204f5ccb4e0b488b5a5d760aacddb2d791970fe43da61eb30e2e90208a817e46db", + "result" : "valid" + }, + { + "tcId" : 344, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "36383234313839343336", + "sig" : "304502210097ab19bd139cac319325869218b1bce111875d63fb12098a04b0cd59b6fdd3a30220431d9cea3a243847303cebda56476431d034339f31d785ee8852db4f040d4921", + "result" : "valid" + }, + { + "tcId" : 345, + "comment" : "special case hash", + "flags" : [ + "SpecialCaseHash" + ], + "msg" : "343834323435343235", + "sig" : "3044022052c683144e44119ae2013749d4964ef67509278f6d38ba869adcfa69970e123d02203479910167408f45bda420a626ec9c4ec711c1274be092198b4187c018b562ca", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0", + "wx" : "07310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc362", + "wy" : "26a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEBzEPkKnq4UmghAL1QZSg97SsQnv42b1s\ndoEHHcR9w2ImptN6xG1h/WAMC/G/+HaJ7RF92msOWTGK4BChl6JsoA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 346, + "comment" : "k*G has a large x-coordinate", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "30160211014551231950b75fc4402da1722fc9baeb020103", + "result" : "valid" + }, + { + "tcId" : 347, + "comment" : "r too large", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2c020103", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5", + "wx" : "00bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22", + "wy" : "705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEvJfnWF7srUjhZoO8QJFwjhqTDGg/xHAB\n1LODWU8sTiJwWYnPadrq3U5OS4FR7YiN/sIPsBco2J1Ws/OPKunIxQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 348, + "comment" : "r,s are large", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f020103", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0444ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463", + "wx" : "44ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252", + "wy" : "00b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000444ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERK0zmvvCHpq/e2AqXKU16jeBNbbRDYEx\nC92Ck9HfMlK2P/fQd0dw+P4dFyL6g6zQL0NOT8EQoMyPbd3TfVbEYw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 349, + "comment" : "r and s^-1 have a large Hamming weight", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203e9a7582886089c62fb840cf3b83061cd1cff3ae4341808bb5bdee6191174177", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0", + "wx" : "1260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c", + "wy" : "5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEEmDCEiyeJE4a9RUb7eDDriO1TXxZaIHT\n7rrSHzfdh4xcmgwamt52c3qIEb1qf5KHyXjuOWqonBHkcinSzLVS8A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 350, + "comment" : "r and s^-1 have a large Hamming weight", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022024238e70b431b1a64efdf9032669939d4b77f249503fc6905feb7540dea3e6d2", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159", + "wx" : "1877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce", + "wy" : "00821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGHcEW+JdNKHQYA+dXADQZFoqVDebbO76\n0ua/XCozUs6CGlMswXUe4dNtQcPWq06bFD5E7EbXNHjqanmlwOVBWQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 351, + "comment" : "small r and s", + "flags" : [ + "SmallRandS", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3006020101020101", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77", + "wx" : "455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50", + "wy" : "00aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERVQ5/MPS3uzt3q7OYOe9FzBPNuu2Aq31\noi4Ljx20alCuw4+yuvIh6ajRiHx79iIt0YNGNOdyYzFa9tI2CdBPdw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 352, + "comment" : "small r and s", + "flags" : [ + "SmallRandS", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3006020101020102", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece7180449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d", + "wx" : "2e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece718", + "wy" : "0449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece7180449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELh9GawJMDDrOJDfeCRJ/7QS3BvlLGaIb\nscKs81zs5xgESa41I9clNOlklyz9OzivC93ZYZ5a8iPk0aQPNM+fHQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 353, + "comment" : "small r and s", + "flags" : [ + "SmallRandS", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3006020101020103", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a2337326ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3", + "wx" : "008e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a23373", + "wy" : "26ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a2337326ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjnq9u9GN50UjdMGHmhw7AdEyYefUVxw7\nR6HHbFWiM3Mm7Yl81Rek9TSduAl4D20vK59imdi1qJB38RGacY/Xsw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 354, + "comment" : "small r and s", + "flags" : [ + "SmallRandS", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3006020102020101", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "047b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af1942117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b", + "wx" : "7b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af19", + "wy" : "42117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200047b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af1942117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEezM9Q0DT1xjdPmr/fee7+Lcr/WFshCAF\nYFKEI3a5rxlCEXxa/qx1XW83b8Yymn12BRuHEjpKXQvEpTk4DwPeew==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 355, + "comment" : "small r and s", + "flags" : [ + "SmallRandS", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3006020102020102", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e503a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff", + "wx" : "00d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e5", + "wy" : "03a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e503a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0wykoN22YWyFHTDO1oLED4PGJ1ih8nWZ\niNZ2OojxwOUDqA1UFWUNQSOXhOji+xI16f6ZHREuu4EYbL8Not46/w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 356, + "comment" : "small r and s", + "flags" : [ + "SmallRandS", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3006020102020103", + "result" : "valid" + }, + { + "tcId" : 357, + "comment" : "r is larger than n", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364143020103", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0448969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec", + "wx" : "48969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24", + "wy" : "00b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000448969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAESJabOZkSl7MyplLT7m4B6QmzmQTnH6I1\nSngwx3ULryS0AS0bgw0ZnMsfyXKzK/3tVfCc1i0lfl6ETiflehWU7A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 358, + "comment" : "s is larger than n", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "30080201020203ed2979", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0402ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee777eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866", + "wx" : "02ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee77", + "wy" : "7eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000402ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee777eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAu9NbWz9WpTx13hCJuPipsCkNsVYOWGf\nOPtEcrX57nd+tKzU7r2lzXKHX/0qLyYinC3GtGUAkZpDLIZznzroZg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 359, + "comment" : "small r and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "30260202010102203a74e9d3a74e9d3a74e9d3a74e9d3a749f8ab3732a0a89604a09bce5b2916da4", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08", + "wx" : "464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584", + "wy" : "00b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERk9P9xVynK5Qcso72AHTGVtnrsZemwGq\n0gopQ9y8tYSxr9KdMaOaEdVwqhWXQ5s7LRlxvy8avxVDLQIHsQ0dCA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 360, + "comment" : "smallish r and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "302b02072d9b4d347952cc02200343aefc2f25d98b882e86eb9e30d55a6eb508b516510b34024ae4b6362330b3", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4cdeadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f", + "wx" : "157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4c", + "wy" : "00deadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4cdeadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFX+P3fNz619Jz88Q2LhTz5HLzX1mXDUi\nun3XON23mkzerfGlxEjqPJ9BkaiZmr/MdXrG1kVn7wcsR/7GE0Q7jw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 361, + "comment" : "100-bit r and small s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3031020d1033e67e37b32b445580bf4efc02206f906f906f906f906f906f906f906f8fe1cab5eefdb214061dce3b22789f1d6f", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "040934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa", + "wx" : "0934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0", + "wy" : "00d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200040934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECTSlN0ZsB0MOLEj+uZC7Gft4zsyc7kJO\npNEwKRqiN/DU+S0jtGKAS1toxSVYwByZltv3J/zKu+7bliGkAFNa+g==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 362, + "comment" : "small r and 100 bit s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3026020201010220783266e90f43dafe5cd9b3b0be86de22f9de83677d0f50713a468ec72fcf5d57", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c654a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265", + "wx" : "00d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c65", + "wy" : "4a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c654a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1u8gvmbIk/dBqb+Q2bdGddHCoxKWOXrL\nPvF0/QswDGVKDJVHjKADmRYtfw8tyJ79wrKKMPur4oWFcpWksMTiZQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 363, + "comment" : "100-bit r and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3031020d062522bbd3ecbe7c39e93e7c260220783266e90f43dafe5cd9b3b0be86de22f9de83677d0f50713a468ec72fcf5d57", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee0629c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829", + "wx" : "00b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee06", + "wy" : "29c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee0629c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtykdFATgwMB9q5NyGJ9L1Y0s6qjRXt5U\nTZUUVFup7gYpyaY9XjCHacww7CdqQQ5kZKJ+6v2eWZ2xDwU6T+SoKQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 364, + "comment" : "r and s^-1 are close to n", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3045022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03640c1022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6", + "wx" : "6e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8", + "wy" : "186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbigwMwXWQsy5I7ci6oayoLyONzXssm6E\nmxnJ92sv27gYboDWTYyrFk9SOPUxhGG/idTZbuZUTIFsdWaUd3Tg9g==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 365, + "comment" : "r and s are 64-bit integer", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "30160209009c44febf31c3594d020900839ed28247c2b06b", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd", + "wx" : "375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9", + "wy" : "00a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEN1vak/avkvtfj0sbXwU047r6s0y3rZ+5\n0Lci5KXDAqmgC584elo5YJeqIWL8W7z0pSYzcvaByU2lHpeZEgmQ/Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 366, + "comment" : "r and s are 100-bit integer", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "301e020d09df8b682430beef6f5fd7c7cf020d0fd0a62e13778f4222a0d61c8a", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44", + "wx" : "00d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197", + "wy" : "00da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE11toIWur4DriV+lLTjvxxS9E498mbRUk\n/4xepp2nMZfaS/+e0cU/RJF6Z9e5eFmOid81nj1ZE+rqJPOuJZq8RA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 367, + "comment" : "r and s are 128-bit integer", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "30260211008a598e563a89f526c32ebec8de26367a02110084f633e2042630e99dd0f1e16f7a04bf", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0478bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e", + "wx" : "78bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653", + "wy" : "118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000478bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeLzaFArtI9QwyyPD3A0B9CPbE07pSjqM\ntIPy3qwqxlMRgRT28zBF1OntkQcIUAe/vd+PWP56GiRF1mqZAEVHbg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 368, + "comment" : "r and s are 160-bit integer", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "302e021500aa6eeb5823f7fa31b466bb473797f0d0314c0bdf021500e2977c479e6d25703cebbc6bd561938cc9d1bfb9", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677", + "wx" : "00bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c", + "wy" : "1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEu3n2GFf3Q7+htucRHOQJQ3claWnk4VFZ\nEj2VSKzDvmwfnZ+IYNz/0+s23Wwx/y5yJsIAnEyU2NfStWhr96vWdw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 369, + "comment" : "s == 1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3025022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1020101", + "result" : "valid" + }, + { + "tcId" : 370, + "comment" : "s == 0", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3025022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1020100", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0493591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518", + "wx" : "0093591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36", + "wy" : "073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000493591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEk1kYJ9nmcTtOn66mLHKyjf76aODAUWC1\n1qroj9LjbDYHP1VFrVr0EK8mr/9oZUz3LUXkk0iTESAyRzR6iQ9FGA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 371, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220419d981c515af8cc82545aac0c85e9e308fbb2eab6acd7ed497e0b4145a18fd9", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0431ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0dada01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f", + "wx" : "31ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0da", + "wy" : "00da01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000431ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0dada01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMe0wga7+AB62QCBp7izMGGKTe4WZUUTb\nqVA5Q1h78NraAbjMTfNPWrOxo1lhUgiUbl7jX5jud1uMzs2GzMFlDw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 372, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102201b21717ad71d23bbac60a9ad0baf75b063c9fdf52a00ebf99d022172910993c9", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "047dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea854c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4", + "wx" : "7dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea8", + "wy" : "54c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200047dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea854c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEff9m+phQn/Pi5RBF9DkFI9zNpDo7wohe\nWMJICQmQ7qhUx2wrmt62u1cYI+B/18ZchjnPnZBSYAZMjnZ1zm2YtA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 373, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102202f588f66018f3dd14db3e28e77996487e32486b521ed8e5a20f06591951777e9", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "044280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1", + "wx" : "4280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a", + "wy" : "2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200044280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEQoBQmqtk7fwLSiln5MvOhJy1ROSncxPI\n5uzlefvXQgouif5cwZJ9VU5qO7FAM+p8kizXXLosdBX9q1LyCxhg8Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 374, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220091a08870ff4daf9123b30c20e8c4fc8505758dcf4074fcaff2170c9bfcf74f4", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "044f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db", + "wx" : "4f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb", + "wy" : "2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200044f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAET43xRRlOPE/D7qJtQ851tALWsXRy3cuy\nVLinmwvz2csqog2ChEyyZjROccp48q0np1oJ5bwPpX5O/Z1GWgiI2w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 375, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102207c370dc0ce8c59a8b273cba44a7c1191fc3186dc03cab96b0567312df0d0b250", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "049598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207", + "wx" : "009598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14", + "wy" : "122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200049598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElZilfdZ+w+FrWHoziqOhCjo5E7QaOvMu\nPtP/ATWMaxQSKBnt+AdLvFIffUzc6C/velFnBq/7odk9neqcyuGiBw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 376, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022070b59a7d1ee77a2f9e0491c2a7cfcd0ed04df4a35192f6132dcc668c79a6160e", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "049171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330", + "wx" : "009171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e", + "wy" : "634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200049171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkXH+w8oggGvAhPEvB2CRG2CZC9gOWypx\nygOgSLIPg35jT9F4Y3YbKVjSvk4Un409ervcGL4D9FGrbBf6Ch+DMA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 377, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102202736d76e412246e097148e2bf62915614eb7c428913a58eb5e9cd4674a9423de", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d", + "wx" : "777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9", + "wy" : "00ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEd3yJMLbh0nEQD+aM6T8WP6N2EsX/9n9K\nYvw7r689F6ntc9hvYKUbXtkTU6OwVO3AqpLJ68vQt10Yj9yIJ5HWjQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 378, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102204a1e12831fbe93627b02d6e7f24bccdd6ef4b2d0f46739eaf3b1eaf0ca117770", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf4700603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000", + "wx" : "00eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf470", + "wy" : "0603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf4700603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE6rwkj2JuCmPh64HEPUYaOaHbqIHrbuIV\nKwfDLXG89HAGA8qoudM9sTr0TG777IoZjtYSSsnrF+qv0oJKVF7AAA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 379, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022006c778d4dfff7dee06ed88bc4e0ed34fc553aad67caf796f2a1c6487c1b2e877", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "049f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73", + "wx" : "009f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001", + "wy" : "00f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200049f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEn3oTraFYpV+d3xpF8ETwc9m4ADDv3Pyf\nn1hBj7zq8AH4raAXUJD4DUcifWcTtnQPmgCR2IqDfQoc13tYqPKNcw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 380, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102204de459ef9159afa057feb3ec40fef01c45b809f4ab296ea48c206d4249a2b451", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0411c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4dbbbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb", + "wx" : "11c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4db", + "wy" : "00bbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000411c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4dbbbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEEcTz5GHNAZtcBuoM6kxAkMPMPjxdnzxt\nZbQ2gm2ptNu763p35Mv9ogcJfENCNwX3LIBHbaPaxApIOwqw8urRyw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 381, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220745d294978007302033502e1acc48b63ae6500be43adbea1b258d6b423dbb416", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb", + "wx" : "00e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4", + "wy" : "161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE4uGGgtUxI6oBpsXQCwxiPWcbRi6oC93W\nUif9UQWYiqQWGQez/SUESpSepByOLqhFncbxZUhWuLYbMVQ7sbRb2w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 382, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102207b2a785e3896f59b2d69da57648e80ad3c133a750a2847fd2098ccd902042b6c", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0490f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197dafadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d", + "wx" : "0090f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197da", + "wy" : "00fadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000490f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197dafadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkPjUynPeCKZWSq8AUke28P/peFBNzlJg\nX0a3w+Vhl9r62+Uo63DZ7n6g5wcC21T3IVFMe4YErCyyFPHey344PQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 383, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022071ae94a72ca896875e7aa4a4c3d29afdb4b35b6996273e63c47ac519256c5eb1", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc", + "wx" : "00824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e", + "wy" : "3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEgkwZXHPP/fA40QG84Wh7XDthRvOVyIWX\nb3dTsjdrlI483vpvw0fRPk3LxjoLA6FlGAzSvhQxoM90zh6iUILSvA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 384, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102200fa527fa7343c0bc9ec35a6278bfbff4d83301b154fc4bd14aee7eb93445b5f9", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f", + "wx" : "2788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f", + "wy" : "30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEJ4ilLweOs/ICxPpz4NM4b6899r6FYANj\nb1mZItT1Jo8wtPIHyRm7315nqL5CZagXR1Szq6jxbldbd/9NWn62Tw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 385, + "comment" : "edge case modular inverse", + "flags" : [ + "ModularInverse", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102206539c0adadd0525ff42622164ce9314348bd0863b4c80e936b23ca0414264671", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b415087401b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4", + "wx" : "00d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b4150874", + "wy" : "01b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b415087401b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1TO3iaSviQ+nqCofrljEBPmmKlC0mtr6\ns0nFE7QVCHQBtBcbgD52s0qYYeEPe8KJoGb9Ab0p+EyYehCl+xjC1A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 386, + "comment" : "point at infinity during verify", + "flags" : [ + "PointDuplication", + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a", + "wx" : "3a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4", + "wy" : "221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOjFQeYyK9p0ebpgfOkVAK6HXMvS+gzDF\nFk9J4Q7FVbQiG9hCvF5Nl+/zcWX2DjmYpCTXKkUM+V6kd8eCh9A0Og==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 387, + "comment" : "edge case for signature malleability", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "043b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e800de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026", + "wx" : "3b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e80", + "wy" : "0de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200043b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e800de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOzffX7NHxpoPF9hcDHyoNzaIOoJeExQ9\nD8/IEB6FHoAN48CQtsohulQ1FzMMBLEvlIxrrfFKY6v/3074x1NwJg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 388, + "comment" : "edge case for signature malleability", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82ce87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e", + "wx" : "00feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82c", + "wy" : "00e87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82ce87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/rUWOw7OMP8+A8fVXEOA+i+oHuLANUlC\n/28IyZ0M2CzofeBe4b2gidPk4kj6D3IRAqz//fUOZUvigUM5md+Jfg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 389, + "comment" : "u1 == 1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd414922897640683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2", + "wx" : "238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd4149228976", + "wy" : "40683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd414922897640683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEI4ztABzyK4hT4C7cicvspQULp+BCp6d/\nk4LNQUkiiXZAaD0wlGQ4QPKViQqkwYqjm0HXfdD7O7JwDk+ewoT/wg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 390, + "comment" : "u1 == n - 1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35ed2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf", + "wx" : "00961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35e", + "wy" : "00d2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35ed2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElhz2SBfAbA5Rs8JzbJIv3hi9jEkG/Nf1\n72bEZ4UI817SxdGBaM++cPLxI710GSMruS3WkRPilBBhiJSBxaAnvw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 391, + "comment" : "u2 == 1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0413681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b1028816528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384", + "wx" : "13681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b10288", + "wy" : "16528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000413681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b1028816528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEE2gerhaM1Op88uKkXQUnQtEKn2TnloZ9\nvcuCn+CxAogWUodg0Xc3bAnfed45VXwynMF1NRes/+j6LsKYAmuDhA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 392, + "comment" : "u2 == n - 1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "045aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c291c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b", + "wx" : "5aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c2", + "wy" : "0091c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200045aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c291c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWqer/ba0CG1UMyXl15xulc5C+GbSu4SQ\nljOgS7GqMcKRyACIeUkF4dozM22HTi+RzPRcxZGFvt5d1vP3rKrhiw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 393, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022016e1e459457679df5b9434ae23f474b3e8d2a70bd6b5dbe692ba16da01f1fb0a", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0400277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e464108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41", + "wx" : "277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e4", + "wy" : "64108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000400277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e464108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEACd3kbMFpFsrOVkLLwXTOSpsgYLO9OtU\nASDg9cIGw+RkEIIz+wuMOsiS15744Pv5LtEzrdtFVCcBMlhNxS7vQQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 394, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02201c940f313f92647be257eccd7ed08b0baef3f0478f25871b53635302c5f6314a", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1ac75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49", + "wx" : "6efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1a", + "wy" : "00c75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1ac75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbvoJK2jelGDwvMkZAFpfboDhnemJaL48\n0sdwqZSb+xrHXm5Qh9ZVDV+b6x555QKTB7wlUjXi1dyZJBrDq4hsSQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 395, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022015d94a85077b493f91cb7101ec63e1b01be58b594e855f45050a8c14062d689b", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0472d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942", + "wx" : "72d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058", + "wy" : "00e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000472d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEctShnE+dLPWEjqQERbcNRpa18C1jLAxl\nTMfX7rDG0FjoxM2ZQ+RZF0x6wB+nQhmOR+bBmmvbDE9sI3gxwbP5Qg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 396, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205b1d27a7694c146244a5ad0bd0636d9d9ef3b9fb58385418d9c982105077d1b7", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e740258f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec", + "wx" : "2a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e7402", + "wy" : "58f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e740258f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKo6i9Q3M7QwhdXW9+nzUfRxvEABB7A41\nUSeUwb5+dAJY+MFxIu0wP9pxQ+tYvt5wKVtlMmYBOwsOvT8FMTf27A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 397, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202d85896b3eb9dbb5a52f42f9c9261ed3fc46644ec65f06ade3fd78f257e43432", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0488de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b80c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946", + "wx" : "0088de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b8", + "wy" : "0c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000488de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b80c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEiN5onOmvHpS+aiCJyKixJT/9u2yOnIYk\nm6IgABpK07gMSZjlSEL0E7ntsYJay7YzXoHk0YSysByL69yF0fKJRg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 398, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205b0b12d67d73b76b4a5e85f3924c3da7f88cc89d8cbe0d5bc7faf1e4afc86864", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9", + "wx" : "00fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7", + "wy" : "00b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/qLTH3D5DV+z4A4YasQqs8FhXO5xTgtO\nETGz1NgiW/ewN6GN8qwVND8w90Bn3fKegX1fd/jc4FcU2lnAlPDNqQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 399, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220694c146244a5ad0bd0636d9e12bc9e09e60e68b90d0b5e6c5dddd0cb694d8799", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "047258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0", + "wx" : "7258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db", + "wy" : "17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200047258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEcliRHj1CM0kWZHnb4Lg0Gvf70D0KfhDt\nzLNrbO6lo9sXrCuJknkRKPo7ltwvvUyjv6eC7ygy/GZWlD2xjnNGsA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 400, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203d7f487c07bfc5f30846938a3dcef696444707cf9677254a92b06c63ab867d22", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "044f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470", + "wx" : "4f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914", + "wy" : "00c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200044f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAETyhGHepkR01rs00Umcl9N7npVjPfHO7q\nrNRQFsmLORTIgYgQuMwG3bQOihJhxSj6pYlFXVpt+Tt3vF4OSTx0cA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 401, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206c7648fc0fbf8a06adb8b839f97b4ff7a800f11b1e37c593b261394599792ba4", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0474f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b", + "wx" : "74f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66", + "wy" : "00eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000474f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEdPKoFPtdjsqRppteYHEnMrOTfeMoKb6X\nTte2jFwvXWbv8PB8VvmHplf0IZYgX1iMDx2W/YpjpfI4tI9Hh4j+Ow==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 402, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220641c9c5d790dc09cdd3dfabb62cdf453e69747a7e3d7aa1a714189ef53171a99", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6ab2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad", + "wx" : "195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6a", + "wy" : "00b2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6ab2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGVtRp8xKIbgnSnCpDed5gUw8jKNYMoII\nwJop8za4LWqyQWt8kv/9wpw7EoLdKnek0E3390UgRzk9hJmJxc7prQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 403, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022029798c5c45bdf58b4a7b2fdc2c46ab4af1218c7eeb9f0f27a88f1267674de3b0", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0", + "wx" : "622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa", + "wy" : "736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYi/HRzIDS+wt3zvBbTSz0fejJ90qjBm6\ntLtP46JLWKpzay8vrnb0367MkJYzOwEyjVHrP9qckifpDQtEmYPE8A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 404, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02200b70f22ca2bb3cefadca1a5711fa3a59f4695385eb5aedf3495d0b6d00f8fd85", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c70827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93", + "wx" : "1f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c7", + "wy" : "0827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c70827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEH3+FyvLXVQ56+bZQI+u03ONFAxFpIwnb\nJplpuDS2EccIJ/RbeAIOy7r0hP3Vv6rmhw8RhMIVgbr274K9e1MPkw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 405, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022016e1e459457679df5b9434ae23f474b3e8d2a70bd6b5dbe692ba16da01f1fb0a", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0449c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377aefc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d", + "wx" : "49c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377a", + "wy" : "00efc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000449c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377aefc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEScGX3ICtHaR6Q0K5OJPo4fsLuU/DOoPn\ng8ALJMeBN3rvwg2pK6x2KVH3JHS+zHNNTMIrqBuJXigv2sTfevDzfQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 406, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202252d685e831b6cf095e4f0535eeaf0ddd3bfa91c210c9d9dc17224702eaf88f", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c", + "wx" : "00d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe", + "wy" : "7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2MtoUXthalZACqOGhjXlS29plZii9hZ3\nV2VJgLr2rL5+yM9EnISaoDRhow762kFFPFfG5vvJO7xvpJrabcBVXA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 407, + "comment" : "edge case for u1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022075135abd7c425b60371a477f09ce0f274f64a8c6b061a07b5d63e93c65046c53", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750", + "wx" : "030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3", + "wy" : "00b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAwcT+2Pyqm/iyt8bIO/CWcd0Rdr6h9rD\nmLhAZco0ffOyJ4GN4aObWJywcdg+UxfMzcIzjlHjEv4x2Nw0pIAXUA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 408, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b17", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950", + "wx" : "00babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7", + "wy" : "252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEurs2d7CVWALY6SmkE1VkDq8eoTU/incT\nMcSUbjSAr6clLxlsh+09KlnTsbVZE3/tABP+zvwZ+1qSaCubylG5UA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 409, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203e888377ac6c71ac9dec3fdb9b56c9feaf0cfaca9f827fc5eb65fc3eac811210", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9", + "wx" : "1aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60", + "wy" : "00bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGqsgGHk0cREaig6bFD/eAvyVkgeW06Y9\n4ym0JDlvumC75BMHBRdHkkQbMY06ox3+hXeCHptEbsVz0nLgNsTr6Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 410, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022030bbb794db588363b40679f6c182a50d3ce9679acdd3ffbe36d7813dacbdc818", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75", + "wx" : "008cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff", + "wy" : "47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjLC5CUmcg+qAbNiFsd1GegEZ8GqIoCdu\nsM/aJ0U1qP9HtUKIM7w/LIv52QQRWM8zcYpplhzQFym8ABHR5YardQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 411, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202c37fd995622c4fb7fffffffffffffffc7cee745110cb45ab558ed7c90c15a2f", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0", + "wx" : "008f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d", + "wy" : "3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjwPPGkInK7FTJyMJP3Lm/urIXhcA6fvp\npqLdZC10v107iacYna2M91/CL28ViqJ/nCygDaynhb4zWPK9o4YsoA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 412, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02207fd995622c4fb7ffffffffffffffffff5d883ffab5b32652ccdcaa290fccb97d", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0444de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8acea2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12", + "wx" : "44de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8ace", + "wy" : "00a2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000444de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8acea2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERN47nHpXqMnoIJUnU0IefZh7s9efcfAT\ngFyJfgGPis6iRgdYyPmNP9zhIalDZZ43LDJv/y5fwq5/o/edquE8Eg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 413, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304302207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc021f4cd53ba7608fffffffffffffffffffff9e5cf143e2539626190a3ab09cce47", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204", + "wx" : "6fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a", + "wy" : "0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEb7iytI4zAxJorWpRdITciDnqkPZmnqDH\nrDIz4qwxOUoKyLvn9zwv9N+ZeHJ6wd/C/VhkfSDzH5kQUxa2RnHyBA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 414, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205622c4fb7fffffffffffffffffffffff928a8f1c7ac7bec1808b9f61c01ec327", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c", + "wx" : "00bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6", + "wy" : "00f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEvqcRIqBIaT6QX/YCs8+d0Yr2m5/J2EMd\nKx3Sa5Qsleb0PHuLletiCCwS2529p/445Fy+SkiGkH+4G9sMXqkkbA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 415, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022044104104104104104104104104104103b87853fd3b7d3f8e175125b4382f25ed", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391", + "wx" : "00da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156", + "wy" : "00e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2pGMcxugaiDLlO8zt3jpgaQEowXxlB/j\nNma0WwM1MVbiuyaU9XW0UYO+eOXJtSEL879Ij9TIKUUW2JVyyk9TkQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 416, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202739ce739ce739ce739ce739ce739ce705560298d1f2f08dc419ac273a5b54d9", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "043007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5", + "wx" : "3007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d", + "wy" : "5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200043007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMAfpLDk32t55ZN+jWw7/Ax9+sCrtCgMU\nQREGzetw/j1adUb8BVKZeyDj1vQT514stm4RYyJpcRS3m6xzS/xNxQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 417, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02204888888888888888888888888888888831c83ae82ebe0898776b4c69d11f88de", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0460e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9bd2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567", + "wx" : "60e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9b", + "wy" : "00d2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000460e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9bd2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYOc071Yk08vw3dN1ARvWY9bWrrxkTrWZ\n/fmNvc0YzpvS2Qs6wx8TmvgyzM9sy7ssbqEfqXNw3JkG2kdNfYp1Zw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 418, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206492492492492492492492492492492406dd3a19b8d5fb875235963c593bd2d3", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0485a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba33769744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c", + "wx" : "0085a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba337", + "wy" : "69744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000485a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba33769744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEhakA6XhY9pPAt9+iYeOA2tbqBG0fZd3u\n7dX32K8LozdpdE0VrdT2wLw7DaKuyTs0y4xl+TQN33TnsACe7szOPA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 419, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b15", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0438066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed", + "wx" : "38066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046", + "wy" : "00a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000438066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOAZvddiO/EyT3jb0ngN7I0zBix3lYIdQ\npiyrA0VAEEaj6EvtjPy4Ge9NVQRE8s5LZRdmtp4uKQH4iDb/kANP7Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 420, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b17", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0498f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabfa33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89", + "wx" : "0098f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabf", + "wy" : "00a33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000498f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabfa33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmPaBd9yVwbTL+lJFSIylI6fVYpRw0DXW\nIaRDxy85qr+jPSlUb6HGSPLH1cz3DPHOSrebXbGsBZ2+zQaNvf8biQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 421, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "045c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191", + "wx" : "5c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277", + "wy" : "00e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200045c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEXCu/ojybmtB/A4qom0kwvyZ9lAHkJV3p\n6NoKUHjsgnfj6IKjHV5qN54Hk5g8ze05uVxDU6sv8B6lNpukewwxkQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 422, + "comment" : "edge case for u2", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220185ddbca6dac41b1da033cfb60c152869e74b3cd66e9ffdf1b6bc09ed65ee40c", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a3853547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc", + "wx" : "2ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385", + "wy" : "3547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a3853547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELqcTNDIznGnSf5smcoG9Ld1fGdYzjUAK\nBc02R7FXo4U1R4CCmESO215wGt6EzV+xrJVnul6Ptoprkz7EtcyEzA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 423, + "comment" : "point duplication during verification", + "flags" : [ + "PointDuplication" + ], + "msg" : "313233343030", + "sig" : "3044022032b0d10d8d0e04bc8d4d064d270699e87cffc9b49c5c20730e1c26f6105ddcda022029ed3d67b3d505be95580d77d5b792b436881179b2b6b2e04c5fe592d38d82d9", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763", + "wx" : "2ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385", + "wy" : "00cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELqcTNDIznGnSf5smcoG9Ld1fGdYzjUAK\nBc02R7FXo4XKuH99Z7txJKGP5SF7MqBOU2qYRaFwSXWUbME6SjN3Yw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 424, + "comment" : "duplication bug", + "flags" : [ + "PointDuplication" + ], + "msg" : "313233343030", + "sig" : "3044022032b0d10d8d0e04bc8d4d064d270699e87cffc9b49c5c20730e1c26f6105ddcda022029ed3d67b3d505be95580d77d5b792b436881179b2b6b2e04c5fe592d38d82d9", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff", + "wx" : "008aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e", + "wy" : "1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEiqLGT6nGQ3Vjq/vL0AsgSNSMGMFSoqb0\nkDbedkfr6C4c5kOHmVxooGD6O8A5mwXMBu7H1Zj3UEGkkX5pK39R/w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 425, + "comment" : "comparison with point at infinity ", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0022033333333333333333333333333333332f222f8faefdb533f265d461c29a47373", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71fdd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd", + "wx" : "391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71f", + "wy" : "00dd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71fdd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEORQn/37ngBPBSux9lqigYiCSmKeDg16U\n/WVJ1QL/9x/dZiTsNDrZ/PTZhyGB5Z+EL5ukzMrgmmwJcvtqxrTGvQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 426, + "comment" : "extreme value for k and edgecase s", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138ec1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e", + "wx" : "00e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138e", + "wy" : "00c1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138ec1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE52K4ohm08YAhnMepBZJF5JYb0ZHAOJl4\nnHo0uJ6ME47BUz7wQZu3N24L/ekxnRCgaWh5HZ6g7tnBzmNFrtl1ng==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 427, + "comment" : "extreme value for k and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "049aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175", + "wx" : "009aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952", + "wy" : "00fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200049aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmu2w0oHbFk4TAADFaX+uDzBe+Ei+b/+0\nOsWT+7lQ6VL6b2MzWb3NgrVrC5+WWwN3idRrmoFBt5GyrvpxP5bBdQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 428, + "comment" : "extreme value for k and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd", + "wx" : "008ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee", + "wy" : "1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEitRF22KBYmDk5of9GITki5/AY20DFUfW\nMxXnkuGb+u4d5k+Z1fHNi27Jyw94emVK6GmTuj2xAI70PP8GhMsivQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 429, + "comment" : "extreme value for k and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566", + "wx" : "1f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32", + "wy" : "00e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEH1eZyVvokGOyTybkDLkowahop2+wCUYH\n6AQ9tAnJHDLnVyToE6QZHjqDkAfwji6Jc4iwbUoA3m3mDlNtkfq1Zg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 430, + "comment" : "extreme value for k and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d", + "wx" : "00a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc", + "wy" : "28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEozMaThtCI+wsAn7dSCySihTtNY2T8dQh\nfTmr9p/LXMwo1oTSqqvNY4N3XKpiOd4m1MaTe7YD7LQZYIL0z/1QnQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 431, + "comment" : "extreme value for k", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee502200eb10e5ab95f2f275348d82ad2e4d7949c8193800d8c9c75df58e343f0ebba7b", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "043f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb248185ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d", + "wx" : "3f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb24818", + "wy" : "5ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200043f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb248185ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEPzlSGZd0x885s4tmyxBCpiYNhoCAOEXk\n1DOtujuySBhepJW2jLx+1Bc+5jyQQtxQJiXH634h+wLKmpEU4KOhjQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 432, + "comment" : "extreme value for k and edgecase s", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698", + "wx" : "00cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e", + "wy" : "054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEzfuMD0IuFE4TfCQSyGwXH1/j+j9bu1RO\nkHYojzzteG4FT9ByG3fBHHm+rLPJQhGwoZvaCGUu/q+SUTo7ChY2mA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 433, + "comment" : "extreme value for k and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0473598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d", + "wx" : "73598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3", + "wy" : "00cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000473598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEc1mKahxoJ4+mv9DOQGTmgjW8HA9rIKko\nEIvjNnMPh+PLrmElGbUDLsyFrtgRJxqV/nk51dNGAUC6MY9NFKujHQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 434, + "comment" : "extreme value for k and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0458debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a16773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d", + "wx" : "58debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a1", + "wy" : "6773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000458debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a16773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWN69mn7iydWRMkeKVECuTV1+1Dcwg2n5\nLqhsghg/EKFnc+dvXtv02g5PG9/6wPVyV+HfpGWEKTEwmiQkX9pqXQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 435, + "comment" : "extreme value for k and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f", + "wx" : "008b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b", + "wy" : "00950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEi5BN5HlnNAxfjDVypyCSTvdXhjf+qxlJ\nrLJBpaasP1uVCQRJb5gksdY/MxO64huJ+uia/fyBG17OA/1aowGGTw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 436, + "comment" : "extreme value for k and s^-1", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b", + "wx" : "00f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a", + "wy" : "346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9IkrbVJcdx4DXyolJwjzeE5II4YEtPlN\nxW6qHlRtlBo0axqgvOaLHFDltS9Qn7VSLlwl4Ci8j4Y0Au23vK2LGw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 437, + "comment" : "extreme value for k", + "flags" : [ + "ArithmeticError" + ], + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802200eb10e5ab95f2f275348d82ad2e4d7949c8193800d8c9c75df58e343f0ebba7b", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "wx" : "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "wy" : "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeb5mfvncu6xVoGKVzocLBwKb/NstzijZ\nWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 438, + "comment" : "public key shares x-coordinate with generator", + "flags" : [ + "PointDuplication" + ], + "msg" : "313233343030", + "sig" : "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca60502302202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result" : "invalid" + }, + { + "tcId" : 439, + "comment" : "public key shares x-coordinate with generator", + "flags" : [ + "PointDuplication" + ], + "msg" : "313233343030", + "sig" : "3044022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e02202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", + "wx" : "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "wy" : "00b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeb5mfvncu6xVoGKVzocLBwKb/NstzijZ\nWfKBWxb4F5i3xSWI2Vw7mqJbBAPx7vdXAuhLt1l6q+ZjuC9vBO8ndw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 440, + "comment" : "public key shares x-coordinate with generator", + "flags" : [ + "PointDuplication" + ], + "msg" : "313233343030", + "sig" : "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca60502302202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result" : "invalid" + }, + { + "tcId" : 441, + "comment" : "public key shares x-coordinate with generator", + "flags" : [ + "PointDuplication" + ], + "msg" : "313233343030", + "sig" : "3044022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e02202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result" : "invalid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152", + "wx" : "782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963", + "wy" : "00af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeCyO0X47Kng7VGTzOwllKnHGeOBexR6E\n4rz8Zjo96WOvmstCgLjH98QvTvmrpiRewewXEv04oPqWQY2M1qphUg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 442, + "comment" : "pseudorandom signature", + "flags" : [ + "ValidSignature" + ], + "msg" : "", + "sig" : "3045022100f80ae4f96cdbc9d853f83d47aae225bf407d51c56b7776cd67d0dc195d99a9dc02204cfc1d941e08cb9aceadde0f4ccead76b30d332fc442115d50e673e28686b70b", + "result" : "valid" + }, + { + "tcId" : 443, + "comment" : "pseudorandom signature", + "flags" : [ + "ValidSignature" + ], + "msg" : "4d7367", + "sig" : "30440220109cd8ae0374358984a8249c0a843628f2835ffad1df1a9a69aa2fe72355545c02205390ff250ac4274e1cb25cd6ca6491f6b91281e32f5b264d87977aed4a94e77b", + "result" : "valid" + }, + { + "tcId" : 444, + "comment" : "pseudorandom signature", + "flags" : [ + "ValidSignature" + ], + "msg" : "313233343030", + "sig" : "3045022100d035ee1f17fdb0b2681b163e33c359932659990af77dca632012b30b27a057b302201939d9f3b2858bc13e3474cb50e6a82be44faa71940f876c1cba4c3e989202b6", + "result" : "valid" + }, + { + "tcId" : 445, + "comment" : "pseudorandom signature", + "flags" : [ + "ValidSignature" + ], + "msg" : "0000000000000000000000000000000000000000", + "sig" : "304402204f053f563ad34b74fd8c9934ce59e79c2eb8e6eca0fef5b323ca67d5ac7ed23802204d4b05daa0719e773d8617dce5631c5fd6f59c9bdc748e4b55c970040af01be5", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff00000001060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1", + "wx" : "6e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff", + "wy" : "01060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff00000001060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEboI1VUUpFAmRgsaywdbwtdKNUMzQBa8s\n4bulQapAyv8AAAABBgSS1aVnPg8l2NUPt+WMSdhtRtQhaVXgqj1A4Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 446, + "comment" : "y-coordinate of the public key is small", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "304402206d6a4f556ccce154e7fb9f19e76c3deca13d59cc2aeb4ecad968aab2ded45965022053b9fa74803ede0fc4441bf683d56c564d3e274e09ccf47390badd1471c05fb7", + "result" : "valid" + }, + { + "tcId" : 447, + "comment" : "y-coordinate of the public key is small", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3044022100aad503de9b9fd66b948e9acf596f0a0e65e700b28b26ec56e6e45e846489b3c4021f0ddc3a2f89abb817bb85c062ce02f823c63fc26b269e0bc9b84d81a5aa123d", + "result" : "valid" + }, + { + "tcId" : 448, + "comment" : "y-coordinate of the public key is small", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "30450221009182cebd3bb8ab572e167174397209ef4b1d439af3b200cdf003620089e43225022054477c982ea019d2e1000497fc25fcee1bccae55f2ac27530ae53b29c4b356a4", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40cafffffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e", + "wx" : "6e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff", + "wy" : "00fffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40cafffffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEboI1VUUpFAmRgsaywdbwtdKNUMzQBa8s\n4bulQapAyv/////++fttKlqYwfDaJyrwSBpztieSuSvelqoeVcK7Tg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 449, + "comment" : "y-coordinate of the public key is large", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "304402203854a3998aebdf2dbc28adac4181462ccac7873907ab7f212c42db0e69b56ed802203ed3f6b8a388d02f3e4df9f2ae9c1bd2c3916a686460dffcd42909cd7f82058e", + "result" : "valid" + }, + { + "tcId" : 450, + "comment" : "y-coordinate of the public key is large", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3045022100e94dbdc38795fe5c904d8f16d969d3b587f0a25d2de90b6d8c5c53ff887e360702207a947369c164972521bb8af406813b2d9f94d2aeaa53d4c215aaa0a2578a2c5d", + "result" : "valid" + }, + { + "tcId" : 451, + "comment" : "y-coordinate of the public key is large", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3044022049fc102a08ca47b60e0858cd0284d22cddd7233f94aaffbb2db1dd2cf08425e102205b16fca5a12cdb39701697ad8e39ffd6bdec0024298afaa2326aea09200b14d6", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04000000013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d", + "wx" : "013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0", + "wy" : "00f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004000000013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAAAAAT/SIkjWTZX3PCm0irSGMYUL5QP9\nAPhGi18PcOD27nqkO8LG/SWx2CaSQcvdnbsNrJbcliMfQwcF+DhxfQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 452, + "comment" : "x-coordinate of the public key is small", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3044022041efa7d3f05a0010675fcb918a45c693da4b348df21a59d6f9cd73e0d831d67a02204454ada693e5e26b7bd693236d340f80545c834577b6f73d378c7bcc534244da", + "result" : "valid" + }, + { + "tcId" : 453, + "comment" : "x-coordinate of the public key is small", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3045022100b615698c358b35920dd883eca625a6c5f7563970cdfc378f8fe0cee17092144c022025f47b326b5be1fb610b885153ea84d41eb4716be66a994e8779989df1c863d4", + "result" : "valid" + }, + { + "tcId" : 454, + "comment" : "x-coordinate of the public key is small", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "304502210087cf8c0eb82d44f69c60a2ff5457d3aaa322e7ec61ae5aecfd678ae1c1932b0e02203add3b115815047d6eb340a3e008989eaa0f8708d1794814729094d08d2460d3", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0425afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dfffffffffa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35", + "wx" : "25afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dffffffff", + "wy" : "00fa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000425afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dfffffffffa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEJa/WiayrrtZ8Hylt5ZQG+MVQ9XFGoLTs\nLJeHbf/////6RqduUgMi37xJHsTwzBl0IPxOpYg9j23VPDVLxPZ8NQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 455, + "comment" : "x-coordinate of the public key has many trailing 1's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3044022062f48ef71ace27bf5a01834de1f7e3f948b9dce1ca1e911d5e13d3b104471d8202205ea8f33f0c778972c4582080deda9b341857dd64514f0849a05f6964c2e34022", + "result" : "valid" + }, + { + "tcId" : 456, + "comment" : "x-coordinate of the public key has many trailing 1's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3045022100f6b0e2f6fe020cf7c0c20137434344ed7add6c4be51861e2d14cbda472a6ffb402206416c8dd3e5c5282b306e8dc8ff34ab64cc99549232d678d714402eb6ca7aa0f", + "result" : "valid" + }, + { + "tcId" : 457, + "comment" : "x-coordinate of the public key has many trailing 1's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3045022100db09d8460f05eff23bc7e436b67da563fa4b4edb58ac24ce201fa8a358125057022046da116754602940c8999c8d665f786c50f5772c0a3cdbda075e77eabc64df16", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff", + "wx" : "00d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb9", + "wy" : "3f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0S5sZrZ3NMPITSYBz1013Al+J2N/CspK\nT9t0tqrdO7k/W9/4i9VzbfiY5pkAbtdQ8RzwfFhmzXrXDHEh/////w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 458, + "comment" : "y-coordinate of the public key has many trailing 1's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "30440220592c41e16517f12fcabd98267674f974b588e9f35d35406c1a7bb2ed1d19b7b802203e65a06bd9f83caaeb7b00f2368d7e0dece6b12221269a9b5b765198f840a3a1", + "result" : "valid" + }, + { + "tcId" : 459, + "comment" : "y-coordinate of the public key has many trailing 1's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3045022100be0d70887d5e40821a61b68047de4ea03debfdf51cdf4d4b195558b959a032b202207d994b2d8f1dbbeb13534eb3f6e5dccd85f5c4133c27d9e64271b1826ce1f67d", + "result" : "valid" + }, + { + "tcId" : 460, + "comment" : "y-coordinate of the public key has many trailing 1's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3045022100fae92dfcb2ee392d270af3a5739faa26d4f97bfd39ed3cbee4d29e26af3b206a02206c9ba37f9faa6a1fd3f65f23b4e853d4692a7274240a12db7ba3884830630d16", + "result" : "valid" + } + ] + }, + { + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb", + "wx" : "6d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000", + "wy" : "00e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb" + }, + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbUp/YNR3Sk8KqLve25U8fup5CUB+MWR1\nVmS8KAAAAADmWdNOTfONnoyeqt+6NmEsdpGVvobHeqw/NueLU4aA+w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ + { + "tcId" : 461, + "comment" : "x-coordinate of the public key has many trailing 0's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "30440220176a2557566ffa518b11226694eb9802ed2098bfe278e5570fe1d5d7af18a94302201291df6a0ed5fc0d15098e70bcf13a009284dfd0689d3bb4be6ceeb9be1487c4", + "result" : "valid" + }, + { + "tcId" : 462, + "comment" : "x-coordinate of the public key has many trailing 0's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3044022060be20c3dbc162dd34d26780621c104bbe5dace630171b2daef0d826409ee5c20220427f7e4d889d549170bda6a9409fb1cb8b0e763d13eea7bd97f64cf41dc6e497", + "result" : "valid" + }, + { + "tcId" : 463, + "comment" : "x-coordinate of the public key has many trailing 0's", + "flags" : [ + "EdgeCasePublicKey" + ], + "msg" : "4d657373616765", + "sig" : "3045022100edf03cf63f658883289a1a593d1007895b9f236d27c9c1f1313089aaed6b16ae02201a4dd6fc0814dc523d1fefa81c64fbf5e618e651e7096fccadbb94cd48e5e0cd", + "result" : "valid" + } + ] + } + ] +} diff --git a/src/secp256k1/tools/check-abi.sh b/src/secp256k1/tools/check-abi.sh new file mode 100755 index 000000000..94a98831e --- /dev/null +++ b/src/secp256k1/tools/check-abi.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +set -eu + +default_base_version="$(git describe --match "v*.*.*" --abbrev=0)" +default_new_version="HEAD" + +display_help_and_exit() { + echo "Usage: $0 [ []]" + echo "" + echo "Description: This script uses the ABI Compliance Checker tool to determine if the ABI" + echo " of a new version of libsecp256k1 has changed in a backward-incompatible way." + echo "" + echo "Options:" + echo " base_ver Specify the base version as a git commit-ish" + echo " (default: most recent reachable tag matching \"v.*.*\", currently \"$default_base_version\")" + echo " new_ver Specify the new version as a git commit-ish" + echo " (default: $default_new_version)" + echo " -h, --help Display this help message" + exit 0 +} + +if [ "$#" -eq 0 ]; then + base_version="$default_base_version" + new_version="$default_new_version" +elif [ "$#" -eq 1 ] && { [ "$1" = "-h" ] || [ "$1" = "--help" ]; }; then + display_help_and_exit +elif [ "$#" -eq 1 ] || [ "$#" -eq 2 ]; then + base_version="$1" + if [ "$#" -eq 2 ]; then + new_version="$2" + fi +else + echo "Invalid usage. See help:" + echo "" + display_help_and_exit +fi + +checkout_and_build() { + _orig_dir="$(pwd)" + git worktree add --detach "$1" "$2" + cd "$1" + mkdir build && cd build + cmake -S .. --preset dev-mode \ + -DCMAKE_C_COMPILER=gcc -DCMAKE_BUILD_TYPE=None -DCMAKE_C_FLAGS="-g -Og -gdwarf-4" \ + -DSECP256K1_BUILD_BENCHMARK=OFF \ + -DSECP256K1_BUILD_TESTS=OFF \ + -DSECP256K1_BUILD_EXHAUSTIVE_TESTS=OFF \ + -DSECP256K1_BUILD_CTIME_TESTS=OFF \ + -DSECP256K1_BUILD_EXAMPLES=OFF + cmake --build . -j "$(nproc)" + abi-dumper src/libsecp256k1.so -o ABI.dump -lver "$2" -public-headers ../include/ + cd "$_orig_dir" +} + +echo "Comparing $base_version (base version) to $new_version (new version)" +echo + +base_source_dir="$(mktemp -d)" +checkout_and_build "$base_source_dir" "$base_version" + +new_source_dir="$(mktemp -d)" +checkout_and_build "$new_source_dir" "$new_version" + +abi-compliance-checker -lib libsecp256k1 -old "${base_source_dir}/build/ABI.dump" -new "${new_source_dir}/build/ABI.dump" +git worktree remove "$base_source_dir" +git worktree remove "$new_source_dir" diff --git a/src/secp256k1/tools/tests_wycheproof_generate.py b/src/secp256k1/tools/tests_wycheproof_generate.py new file mode 100755 index 000000000..b26dfa89d --- /dev/null +++ b/src/secp256k1/tools/tests_wycheproof_generate.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 Random "Randy" Lattice and Sean Andersen +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php. +''' +Generate a C file with ECDSA testvectors from the Wycheproof project. +''' + +import json +import sys + +filename_input = sys.argv[1] + +with open(filename_input) as f: + doc = json.load(f) + +num_groups = len(doc['testGroups']) + +def to_c_array(x): + if x == "": + return "" + s = ',0x'.join(a+b for a,b in zip(x[::2], x[1::2])) + return "0x" + s + + +num_vectors = 0 +offset_msg_running, offset_pk_running, offset_sig = 0, 0, 0 +out = "" +messages = "" +signatures = "" +public_keys = "" +cache_msgs = {} +cache_public_keys = {} + +for i in range(num_groups): + group = doc['testGroups'][i] + num_tests = len(group['tests']) + public_key = group['publicKey'] + for j in range(num_tests): + test_vector = group['tests'][j] + # // 2 to convert hex to byte length + sig_size = len(test_vector['sig']) // 2 + msg_size = len(test_vector['msg']) // 2 + + if test_vector['result'] == "invalid": + expected_verify = 0 + elif test_vector['result'] == "valid": + expected_verify = 1 + else: + raise ValueError("invalid result field") + + if num_vectors != 0 and sig_size != 0: + signatures += ",\n " + + new_msg = False + msg = to_c_array(test_vector['msg']) + msg_offset = offset_msg_running + # check for repeated msg + if msg not in cache_msgs: + if num_vectors != 0 and msg_size != 0: + messages += ",\n " + cache_msgs[msg] = offset_msg_running + messages += msg + new_msg = True + else: + msg_offset = cache_msgs[msg] + + new_pk = False + pk = to_c_array(public_key['uncompressed']) + pk_offset = offset_pk_running + # check for repeated pk + if pk not in cache_public_keys: + if num_vectors != 0: + public_keys += ",\n " + cache_public_keys[pk] = offset_pk_running + public_keys += pk + new_pk = True + else: + pk_offset = cache_public_keys[pk] + + signatures += to_c_array(test_vector['sig']) + + out += " /" + "* tcId: " + str(test_vector['tcId']) + ". " + test_vector['comment'] + " *" + "/\n" + out += f" {{{pk_offset}, {msg_offset}, {msg_size}, {offset_sig}, {sig_size}, {expected_verify} }},\n" + if new_msg: + offset_msg_running += msg_size + if new_pk: + offset_pk_running += 65 + offset_sig += sig_size + num_vectors += 1 + +struct_definition = """ +typedef struct { + size_t pk_offset; + size_t msg_offset; + size_t msg_len; + size_t sig_offset; + size_t sig_len; + int expected_verify; +} wycheproof_ecdsa_testvector; +""" + + +print("/* Note: this file was autogenerated using tests_wycheproof_generate.py. Do not edit. */") +print(f"#define SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS ({num_vectors})") + +print(struct_definition) + +print("static const unsigned char wycheproof_ecdsa_messages[] = { " + messages + "};\n") +print("static const unsigned char wycheproof_ecdsa_public_keys[] = { " + public_keys + "};\n") +print("static const unsigned char wycheproof_ecdsa_signatures[] = { " + signatures + "};\n") + +print("static const wycheproof_ecdsa_testvector testvectors[SECP256K1_ECDSA_WYCHEPROOF_NUMBER_TESTVECTORS] = {") +print(out) +print("};") From 8256555c3dc8615626cf4d6016a0054c7bcce4b4 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:03:03 +0000 Subject: [PATCH 005/146] Update univalue subtree to latest upstream --- configure.ac | 2 +- src/Makefile.am | 12 +- src/Makefile.test.include | 2 - src/Makefile.univalue.include | 6 + src/asyncrpcoperation.cpp | 16 +- src/bitcoin-cli.cpp | 10 +- src/gtest/test_rpc.cpp | 2 +- src/httprpc.cpp | 2 +- src/rest.cpp | 16 +- src/rpc/blockchain.cpp | 282 +- src/rpc/mining.cpp | 108 +- src/rpc/misc.cpp | 176 +- src/rpc/net.cpp | 118 +- src/rpc/protocol.cpp | 18 +- src/rpc/rawtransaction.cpp | 178 +- src/rpc/server.cpp | 16 +- src/test/base58_tests.cpp | 12 +- src/test/rpc_tests.cpp | 48 +- src/test/sighash_tests.cpp | 6 +- src/test/transaction_tests.cpp | 4 +- src/test/univalue_tests.cpp | 320 - src/univalue/.travis.yml | 52 - src/univalue/COPYING | 19 - src/univalue/Makefile.am | 109 - src/univalue/README | 7 - src/univalue/TODO | 10 - src/univalue/autogen.sh | 9 - src/univalue/build-aux/m4/.gitignore | 1 - src/univalue/configure.ac | 69 - src/univalue/configure~ | 19224 ---------------- src/univalue/gen/gen.cpp | 84 - src/univalue/include/univalue.h | 247 +- src/univalue/include/univalue_escapes.h | 261 + .../{lib => include}/univalue_utffilter.h | 20 +- src/univalue/lib/univalue.cpp | 297 +- src/univalue/lib/univalue_escapes.h | 262 - src/univalue/lib/univalue_get.cpp | 91 + src/univalue/lib/univalue_read.cpp | 51 +- src/univalue/lib/univalue_write.cpp | 36 +- src/univalue/pc/libunivalue-uninstalled.pc.in | 9 - src/univalue/pc/libunivalue.pc.in | 10 - src/univalue/sources.mk | 86 + src/univalue/test/.gitignore | 3 +- src/univalue/test/fail44.json | 1 + src/univalue/test/fail45.json | 1 + src/univalue/test/no_nul.cpp | 8 - src/univalue/test/object.cpp | 468 + src/univalue/test/pass4.json | 1 + src/univalue/test/test_json.cpp | 4 +- src/univalue/test/unitester.cpp | 73 +- src/wallet/asyncrpcoperation_common.cpp | 14 +- .../asyncrpcoperation_mergetoaddress.cpp | 24 +- .../asyncrpcoperation_saplingmigration.cpp | 14 +- src/wallet/asyncrpcoperation_sendmany.cpp | 24 +- .../asyncrpcoperation_shieldcoinbase.cpp | 22 +- src/wallet/rpcdisclosure.cpp | 32 +- src/wallet/rpcdump.cpp | 6 +- src/wallet/rpcwallet.cpp | 448 +- src/wallet/test/rpc_wallet_tests.cpp | 92 +- 59 files changed, 2035 insertions(+), 21508 deletions(-) create mode 100644 src/Makefile.univalue.include delete mode 100644 src/test/univalue_tests.cpp delete mode 100644 src/univalue/.travis.yml delete mode 100644 src/univalue/COPYING delete mode 100644 src/univalue/Makefile.am delete mode 100644 src/univalue/README delete mode 100644 src/univalue/TODO delete mode 100755 src/univalue/autogen.sh delete mode 100644 src/univalue/build-aux/m4/.gitignore delete mode 100644 src/univalue/configure.ac delete mode 100755 src/univalue/configure~ delete mode 100644 src/univalue/gen/gen.cpp create mode 100644 src/univalue/include/univalue_escapes.h rename src/univalue/{lib => include}/univalue_utffilter.h (89%) delete mode 100644 src/univalue/lib/univalue_escapes.h create mode 100644 src/univalue/lib/univalue_get.cpp delete mode 100644 src/univalue/pc/libunivalue-uninstalled.pc.in delete mode 100644 src/univalue/pc/libunivalue.pc.in create mode 100644 src/univalue/sources.mk create mode 100644 src/univalue/test/fail44.json create mode 100644 src/univalue/test/fail45.json delete mode 100644 src/univalue/test/no_nul.cpp create mode 100644 src/univalue/test/object.cpp create mode 100644 src/univalue/test/pass4.json diff --git a/configure.ac b/configure.ac index 464f3de0a..68e792730 100644 --- a/configure.ac +++ b/configure.ac @@ -948,7 +948,7 @@ unset PKG_CONFIG_LIBDIR PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery" -AC_CONFIG_SUBDIRS([src/secp256k1 src/univalue]) +AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 0d747782c..b7d7665ec 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,7 +5,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . -DIST_SUBDIRS = secp256k1 univalue +DIST_SUBDIRS = secp256k1 AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS) AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS) @@ -31,7 +31,7 @@ BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include -BITCOIN_INCLUDES += -I$(srcdir)/univalue/include +BITCOIN_INCLUDES += -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) LIBBITCOIN_SERVER=libbitcoin_server.a LIBBITCOIN_COMMON=libbitcoin_common.a @@ -39,7 +39,6 @@ LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBSECP256K1=secp256k1/libsecp256k1.la -LIBUNIVALUE=univalue/libunivalue.la LIBZCASH=libzcash.a if ENABLE_ZMQ @@ -55,9 +54,6 @@ endif $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) -$(LIBUNIVALUE): $(wildcard univalue/lib/*) $(wildcard univalue/include/*) - $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) - # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: EXTRA_LIBRARIES += \ @@ -71,6 +67,7 @@ EXTRA_LIBRARIES += \ $(LIBZCASH) lib_LTLIBRARIES = $(LIBZCASH_CONSENSUS) +noinst_LTLIBRARIES = bin_PROGRAMS = noinst_PROGRAMS = @@ -559,7 +556,6 @@ EXTRA_DIST = leveldb $(CTAES_DIST) clean-local: -$(MAKE) -C leveldb clean -$(MAKE) -C secp256k1 clean - -$(MAKE) -C univalue clean rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno rm -f fuzz.cpp rm -rf fuzzing/*/output @@ -597,3 +593,5 @@ endif if ENABLE_BENCH include Makefile.bench.include endif + +include Makefile.univalue.include diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 1fe041add..5b0bbfec0 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -106,7 +106,6 @@ BITCOIN_TESTS =\ test/transaction_tests.cpp \ test/txvalidationcache_tests.cpp \ test/uint256_tests.cpp \ - test/univalue_tests.cpp \ test/util_tests.cpp \ test/sha256compress_tests.cpp @@ -157,7 +156,6 @@ check-local: @echo "Running test/bitcoin-util-test.py..." $(AM_V_at)srcdir=$(srcdir) PYTHONPATH=$(builddir)/test $(srcdir)/test/bitcoin-util-test.py $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check - $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check %.json.h: %.json @$(MKDIR_P) $(@D) diff --git a/src/Makefile.univalue.include b/src/Makefile.univalue.include new file mode 100644 index 000000000..3644e3636 --- /dev/null +++ b/src/Makefile.univalue.include @@ -0,0 +1,6 @@ +include univalue/sources.mk + +LIBUNIVALUE = libunivalue.la +noinst_LTLIBRARIES += $(LIBUNIVALUE) +libunivalue_la_SOURCES = $(UNIVALUE_LIB_SOURCES_INT) $(UNIVALUE_DIST_HEADERS_INT) $(UNIVALUE_LIB_HEADERS_INT) $(UNIVALUE_TEST_FILES_INT) +libunivalue_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) diff --git a/src/asyncrpcoperation.cpp b/src/asyncrpcoperation.cpp index 20186b63e..4fa4ddeaf 100644 --- a/src/asyncrpcoperation.cpp +++ b/src/asyncrpcoperation.cpp @@ -124,8 +124,8 @@ UniValue AsyncRPCOperation::getError() const { std::lock_guard guard(lock_); UniValue error(UniValue::VOBJ); - error.push_back(Pair("code", this->error_code_)); - error.push_back(Pair("message", this->error_message_)); + error.pushKV("code", this->error_code_); + error.pushKV("message", this->error_message_); return error; } @@ -152,21 +152,21 @@ UniValue AsyncRPCOperation::getResult() const { UniValue AsyncRPCOperation::getStatus() const { OperationStatus status = this->getState(); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("id", this->id_)); - obj.push_back(Pair("status", OperationStatusMap[status])); - obj.push_back(Pair("creation_time", this->creation_time_)); + obj.pushKV("id", this->id_); + obj.pushKV("status", OperationStatusMap[status]); + obj.pushKV("creation_time", this->creation_time_); // TODO: Issue #1354: There may be other useful metadata to return to the user. UniValue err = this->getError(); if (!err.isNull()) { - obj.push_back(Pair("error", err.get_obj())); + obj.pushKV("error", err.get_obj()); } UniValue result = this->getResult(); if (!result.isNull()) { - obj.push_back(Pair("result", result)); + obj.pushKV("result", result); // Include execution time for successful operation std::chrono::duration elapsed_seconds = end_time_ - start_time_; - obj.push_back(Pair("execution_secs", elapsed_seconds.count())); + obj.pushKV("execution_secs", elapsed_seconds.count()); } return obj; diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index c6cc5682b..1425bc6d0 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -294,20 +294,20 @@ int CommandLineRPC(int argc, char *argv[]) const UniValue reply = CallRPC(strMethod, params); // Parse reply - const UniValue& result = find_value(reply, "result"); - const UniValue& error = find_value(reply, "error"); + const UniValue& result = reply.find_value("result"); + const UniValue& error = reply.find_value("error"); if (!error.isNull()) { // Error - int code = error["code"].get_int(); + int code = error["code"].getInt(); if (fWait && code == RPC_IN_WARMUP) throw CConnectionFailed("server in warmup"); strPrint = "error: " + error.write(); nRet = abs(code); if (error.isObject()) { - UniValue errCode = find_value(error, "code"); - UniValue errMsg = find_value(error, "message"); + UniValue errCode = error.find_value("code"); + UniValue errMsg = error.find_value("message"); strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n"; if (errMsg.isStr()) diff --git a/src/gtest/test_rpc.cpp b/src/gtest/test_rpc.cpp index 405855c85..d7a8af33e 100644 --- a/src/gtest/test_rpc.cpp +++ b/src/gtest/test_rpc.cpp @@ -24,7 +24,7 @@ TEST(rpc, CheckBlockToJSONReturnsMinifiedSolution) { index.nHeight = 1391; UniValue obj = blockToJSON(block, &index); - EXPECT_EQ("009f44ff7505d789b964d6817734b8ce1377d456255994370d06e59ac99bd5791b6ad174a66fd71c70e60cfc7fd88243ffe06f80b1ad181625f210779c745524629448e25348a5fce4f346a1735e60fdf53e144c0157dbc47c700a21a236f1efb7ee75f65b8d9d9e29026cfd09048233175202b211b9a49de4ab46f1cac71b6ea57a686377bd612378746e70c61a659c9cd683269e9c2a5cbc1d19f1149345302bbd0a1e62bf4bab01e9caeea789a1519441a61b146de35a4cc75dbdf01029127e311ad5073e7e96397f47226a7df9df66b2086b70756db013bbaeb068260157014b2602fc7dc71336e1439c887d2742d9730b4e79b08ec7839c3e2a037ae1565d04e05e351bb3531e5ef42cf7b71ca1482a9205245dd41f4db0f71644f8bdb88e845558537c03834c06ac83f336651e54e2edfc12e15ea9b7ea2c074e6155654d44c4d3bd90d9511050e9ad87d170db01448e5be6f45419cd86008978db5e3ceab79890234f992648d69bf1053855387db646ccdee5575c65f81dd0f670b016d9f9a84707d91f77b862f697b8bb08365ba71fbe6bfa47af39155a75ebdcb1e5d69f59c40c9e3a64988c1ec26f7f5159eef5c244d504a9e46125948ecc389c2ec3028ac4ff39ffd66e7743970819272b21e0c2df75b308bc62896873952147e57ed79446db4cdb5a563e76ec4c25899d41128afb9a5f8fc8063621efb7a58b9dd666d30c73e318cdcf3393bfec200e160f500e645f7baac263db99fa4a7c1cb4fea219fc512193102034d379f244c21a81821301b8d47c90247713a3e902c762d7bafa6cdb744eeb6d3b50dd175599d02b6e9f5bbda59366e04862aa765135968426e7ac0116de7351940dc57c0ae451d63f667e39891bc81e09e6c76f6f8a7582f7447c6f5945f717b0e52a7e3dd0c6db4061362123cc53fd8ede4abed4865201dc4d8eb4e5d48baa565183b69a5304a44c0600bb24dcaeee9d95ceebd27c1b0a33e0b46f23797d7d7907300b2bb7d62ef2fc5aa139250c73930c621bb5f41fc235534ee8014dfaddd5245aeb01198420ba7b5c076545329c94d54fa725a8e807579f5f0cc9d98170598023268f5930893620190275e6b3c6f5181e36310a9a475208316911d78f917d724c5946c553b7ec042c563c540114b6b78bd4c6e808ee391a4a9d93e127032983c5b3708037b14aa604cfb034e7c8b0ffdd6936446fe80216178506a87402653a373926eeff66e704daf992a0a9a5c3ad80566c0339be9e5b8e35b3b3226b2f7767e20d992ea6c3d6e322eca37b0c7f7e60060802f5abcc1975841365cadbdc3867063addfc803766ae525375ecddee61f9df9ffcd20343c83ab82b0e91de039c59cb435c8d3159cc338b4901f40c9b5c27043bcf2bd5fa9b685b65c9ba5a1e11a51dd3f773051560341f9ec81d05bf259e2d4b7161f896fbb6812cfc924a32120b7367d5e40439e267adda6a1315bb0d6200ce6a503174c8d2a638ea6fd6b1f486d68db11bdca63c4f4a725d1ab6231ea875484e70b27d293c05803386924f283d4c12bb953474d92b7dd43d2d97193bd96281ebb63fa075d2f9ecd310c70ee1d97b5330bd8fb5791c5943ecf084e5f2c83915acac57519c46b166136068d6f9ec0dd598616e32c591128ce13705a283ca39d5b211409600e07b3713113374d9700207a45394eac5b3b7afc9b1b2bad7d89fd3f35f6b2413ce615ee7869b3569009403b96fdacdb32ef0a7e5229e2b666d51e95bdfb009b892e88bde70621a9b6509f068781392df4bdbc5723bb15071993f0d9a11575af5ff6ef85eaea39bc86805b35d8beee91b779354147f2d85304b8b49d053e7444fdd3deb9d16de331f2552af5b3be7766bb8f3f6a78c62148efb231f2268", find_value(obj, "solution").get_str()); + EXPECT_EQ("009f44ff7505d789b964d6817734b8ce1377d456255994370d06e59ac99bd5791b6ad174a66fd71c70e60cfc7fd88243ffe06f80b1ad181625f210779c745524629448e25348a5fce4f346a1735e60fdf53e144c0157dbc47c700a21a236f1efb7ee75f65b8d9d9e29026cfd09048233175202b211b9a49de4ab46f1cac71b6ea57a686377bd612378746e70c61a659c9cd683269e9c2a5cbc1d19f1149345302bbd0a1e62bf4bab01e9caeea789a1519441a61b146de35a4cc75dbdf01029127e311ad5073e7e96397f47226a7df9df66b2086b70756db013bbaeb068260157014b2602fc7dc71336e1439c887d2742d9730b4e79b08ec7839c3e2a037ae1565d04e05e351bb3531e5ef42cf7b71ca1482a9205245dd41f4db0f71644f8bdb88e845558537c03834c06ac83f336651e54e2edfc12e15ea9b7ea2c074e6155654d44c4d3bd90d9511050e9ad87d170db01448e5be6f45419cd86008978db5e3ceab79890234f992648d69bf1053855387db646ccdee5575c65f81dd0f670b016d9f9a84707d91f77b862f697b8bb08365ba71fbe6bfa47af39155a75ebdcb1e5d69f59c40c9e3a64988c1ec26f7f5159eef5c244d504a9e46125948ecc389c2ec3028ac4ff39ffd66e7743970819272b21e0c2df75b308bc62896873952147e57ed79446db4cdb5a563e76ec4c25899d41128afb9a5f8fc8063621efb7a58b9dd666d30c73e318cdcf3393bfec200e160f500e645f7baac263db99fa4a7c1cb4fea219fc512193102034d379f244c21a81821301b8d47c90247713a3e902c762d7bafa6cdb744eeb6d3b50dd175599d02b6e9f5bbda59366e04862aa765135968426e7ac0116de7351940dc57c0ae451d63f667e39891bc81e09e6c76f6f8a7582f7447c6f5945f717b0e52a7e3dd0c6db4061362123cc53fd8ede4abed4865201dc4d8eb4e5d48baa565183b69a5304a44c0600bb24dcaeee9d95ceebd27c1b0a33e0b46f23797d7d7907300b2bb7d62ef2fc5aa139250c73930c621bb5f41fc235534ee8014dfaddd5245aeb01198420ba7b5c076545329c94d54fa725a8e807579f5f0cc9d98170598023268f5930893620190275e6b3c6f5181e36310a9a475208316911d78f917d724c5946c553b7ec042c563c540114b6b78bd4c6e808ee391a4a9d93e127032983c5b3708037b14aa604cfb034e7c8b0ffdd6936446fe80216178506a87402653a373926eeff66e704daf992a0a9a5c3ad80566c0339be9e5b8e35b3b3226b2f7767e20d992ea6c3d6e322eca37b0c7f7e60060802f5abcc1975841365cadbdc3867063addfc803766ae525375ecddee61f9df9ffcd20343c83ab82b0e91de039c59cb435c8d3159cc338b4901f40c9b5c27043bcf2bd5fa9b685b65c9ba5a1e11a51dd3f773051560341f9ec81d05bf259e2d4b7161f896fbb6812cfc924a32120b7367d5e40439e267adda6a1315bb0d6200ce6a503174c8d2a638ea6fd6b1f486d68db11bdca63c4f4a725d1ab6231ea875484e70b27d293c05803386924f283d4c12bb953474d92b7dd43d2d97193bd96281ebb63fa075d2f9ecd310c70ee1d97b5330bd8fb5791c5943ecf084e5f2c83915acac57519c46b166136068d6f9ec0dd598616e32c591128ce13705a283ca39d5b211409600e07b3713113374d9700207a45394eac5b3b7afc9b1b2bad7d89fd3f35f6b2413ce615ee7869b3569009403b96fdacdb32ef0a7e5229e2b666d51e95bdfb009b892e88bde70621a9b6509f068781392df4bdbc5723bb15071993f0d9a11575af5ff6ef85eaea39bc86805b35d8beee91b779354147f2d85304b8b49d053e7444fdd3deb9d16de331f2552af5b3be7766bb8f3f6a78c62148efb231f2268", obj.find_value("solution").get_str()); } TEST(rpc, CheckExperimentalDisabledHelpMsg) { diff --git a/src/httprpc.cpp b/src/httprpc.cpp index d0a43ced0..5552c7c44 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -65,7 +65,7 @@ static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const Uni { // Send error reply from json-rpc error object int nStatus = HTTP_INTERNAL_SERVER_ERROR; - int code = find_value(objError, "code").get_int(); + int code = objError.find_value("code").getInt(); if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; diff --git a/src/rest.cpp b/src/rest.cpp index 27b37a935..59f988450 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -563,24 +563,24 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) // pack in some essentials // use more or less the same output as mentioned in Bip64 - objGetUTXOResponse.push_back(Pair("chainHeight", chainActive.Height())); - objGetUTXOResponse.push_back(Pair("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex())); - objGetUTXOResponse.push_back(Pair("bitmap", bitmapStringRepresentation)); + objGetUTXOResponse.pushKV("chainHeight", chainActive.Height()); + objGetUTXOResponse.pushKV("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex()); + objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); UniValue utxos(UniValue::VARR); for (const CCoin& coin : outs) { UniValue utxo(UniValue::VOBJ); - utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer)); - utxo.push_back(Pair("height", (int32_t)coin.nHeight)); - utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); + utxo.pushKV("txvers", (int32_t)coin.nTxVer); + utxo.pushKV("height", (int32_t)coin.nHeight); + utxo.pushKV("value", ValueFromAmount(coin.out.nValue)); // include the script in a json output UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coin.out.scriptPubKey, o, true); - utxo.push_back(Pair("scriptPubKey", o)); + utxo.pushKV("scriptPubKey", o); utxos.push_back(utxo); } - objGetUTXOResponse.push_back(Pair("utxos", utxos)); + objGetUTXOResponse.pushKV("utxos", utxos); // return json string string strJSON = objGetUTXOResponse.write() + "\n"; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 4b611167d..6055d3738 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -87,15 +87,15 @@ static UniValue ValuePoolDesc( const std::optional valueDelta) { UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("id", name)); - rv.push_back(Pair("monitored", (bool)chainValue)); + rv.pushKV("id", name); + rv.pushKV("monitored", (bool)chainValue); if (chainValue) { - rv.push_back(Pair("chainValue", ValueFromAmount(*chainValue))); - rv.push_back(Pair("chainValueZat", *chainValue)); + rv.pushKV("chainValue", ValueFromAmount(*chainValue)); + rv.pushKV("chainValueZat", *chainValue); } if (valueDelta) { - rv.push_back(Pair("valueDelta", ValueFromAmount(*valueDelta))); - rv.push_back(Pair("valueDeltaZat", *valueDelta)); + rv.pushKV("valueDelta", ValueFromAmount(*valueDelta)); + rv.pushKV("valueDeltaZat", *valueDelta); } return rv; } @@ -103,28 +103,28 @@ static UniValue ValuePoolDesc( UniValue blockheaderToJSON(const CBlockIndex* blockindex) { UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); + result.pushKV("hash", blockindex->GetBlockHash().GetHex()); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", confirmations)); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", blockindex->nVersion)); - result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex())); - result.push_back(Pair("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex())); - result.push_back(Pair("time", (int64_t)blockindex->nTime)); - result.push_back(Pair("nonce", blockindex->nNonce.GetHex())); - result.push_back(Pair("solution", HexStr(blockindex->GetBlockHeader().nSolution))); - result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + result.pushKV("confirmations", confirmations); + result.pushKV("height", blockindex->nHeight); + result.pushKV("version", blockindex->nVersion); + result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex()); + result.pushKV("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex()); + result.pushKV("time", (int64_t)blockindex->nTime); + result.pushKV("nonce", blockindex->nNonce.GetHex()); + result.pushKV("solution", HexStr(blockindex->GetBlockHeader().nSolution)); + result.pushKV("bits", strprintf("%08x", blockindex->nBits)); + result.pushKV("difficulty", GetDifficulty(blockindex)); + result.pushKV("chainwork", blockindex->nChainWork.GetHex()); if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); return result; } @@ -132,16 +132,16 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) { UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", block.GetHash().GetHex())); + result.pushKV("hash", block.GetHash().GetHex()); // Only report confirmations if the block is on the main chain if (!chainActive.Contains(blockindex)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block is an orphan"); int confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", confirmations)); - result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + result.pushKV("confirmations", confirmations); + result.pushKV("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)); + result.pushKV("height", blockindex->nHeight); + result.pushKV("version", block.nVersion); + result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); UniValue deltas(UniValue::VARR); for (unsigned int i = 0; i < block.vtx.size(); i++) { @@ -149,8 +149,8 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) const uint256 txhash = tx.GetHash(); UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", txhash.GetHex())); - entry.push_back(Pair("index", (int)i)); + entry.pushKV("txid", txhash.GetHex()); + entry.pushKV("index", (int)i); UniValue inputs(UniValue::VARR); if (!tx.IsCoinBase()) { @@ -165,17 +165,17 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) } CTxDestination dest = DestFromAddressHash(spentInfo.addressType, spentInfo.addressHash); if (IsValidDestination(dest)) { - delta.push_back(Pair("address", EncodeDestination(dest))); + delta.pushKV("address", EncodeDestination(dest)); } - delta.push_back(Pair("satoshis", -1 * spentInfo.satoshis)); - delta.push_back(Pair("index", (int)j)); - delta.push_back(Pair("prevtxid", input.prevout.hash.GetHex())); - delta.push_back(Pair("prevout", (int)input.prevout.n)); + delta.pushKV("satoshis", -1 * spentInfo.satoshis); + delta.pushKV("index", (int)j); + delta.pushKV("prevtxid", input.prevout.hash.GetHex()); + delta.pushKV("prevout", (int)input.prevout.n); inputs.push_back(delta); } } - entry.push_back(Pair("inputs", inputs)); + entry.pushKV("inputs", inputs); UniValue outputs(UniValue::VARR); for (unsigned int k = 0; k < tx.vout.size(); k++) { @@ -190,47 +190,47 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) dest = CKeyID(addrhash); } if (IsValidDestination(dest)) { - delta.push_back(Pair("address", EncodeDestination(dest))); + delta.pushKV("address", EncodeDestination(dest)); } - delta.push_back(Pair("address", EncodeDestination(dest))); - delta.push_back(Pair("satoshis", out.nValue)); - delta.push_back(Pair("index", (int)k)); + delta.pushKV("address", EncodeDestination(dest)); + delta.pushKV("satoshis", out.nValue); + delta.pushKV("index", (int)k); outputs.push_back(delta); } - entry.push_back(Pair("outputs", outputs)); + entry.pushKV("outputs", outputs); deltas.push_back(entry); } - result.push_back(Pair("deltas", deltas)); - result.push_back(Pair("time", block.GetBlockTime())); - result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); - result.push_back(Pair("nonce", block.nNonce.GetHex())); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + result.pushKV("deltas", deltas); + result.pushKV("time", block.GetBlockTime()); + result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); + result.pushKV("nonce", block.nNonce.GetHex()); + result.pushKV("bits", strprintf("%08x", block.nBits)); + result.pushKV("difficulty", GetDifficulty(blockindex)); + result.pushKV("chainwork", blockindex->nChainWork.GetHex()); if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); return result; } UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) { UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", block.GetHash().GetHex())); + result.pushKV("hash", block.GetHash().GetHex()); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", confirmations)); - result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("finalsaplingroot", block.hashFinalSaplingRoot.GetHex())); + result.pushKV("confirmations", confirmations); + result.pushKV("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)); + result.pushKV("height", blockindex->nHeight); + result.pushKV("version", block.nVersion); + result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); + result.pushKV("finalsaplingroot", block.hashFinalSaplingRoot.GetHex()); UniValue txs(UniValue::VARR); for (const CTransaction&tx : block.vtx) { @@ -243,25 +243,25 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx else txs.push_back(tx.GetHash().GetHex()); } - result.push_back(Pair("tx", txs)); - result.push_back(Pair("time", block.GetBlockTime())); - result.push_back(Pair("nonce", block.nNonce.GetHex())); - result.push_back(Pair("solution", HexStr(block.nSolution))); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); - result.push_back(Pair("anchor", blockindex->hashFinalSproutRoot.GetHex())); + result.pushKV("tx", txs); + result.pushKV("time", block.GetBlockTime()); + result.pushKV("nonce", block.nNonce.GetHex()); + result.pushKV("solution", HexStr(block.nSolution)); + result.pushKV("bits", strprintf("%08x", block.nBits)); + result.pushKV("difficulty", GetDifficulty(blockindex)); + result.pushKV("chainwork", blockindex->nChainWork.GetHex()); + result.pushKV("anchor", blockindex->hashFinalSproutRoot.GetHex()); UniValue valuePools(UniValue::VARR); valuePools.push_back(ValuePoolDesc("sprout", blockindex->nChainSproutValue, blockindex->nSproutValue)); valuePools.push_back(ValuePoolDesc("sapling", blockindex->nChainSaplingValue, blockindex->nSaplingValue)); - result.push_back(Pair("valuePools", valuePools)); + result.pushKV("valuePools", valuePools); if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); return result; } @@ -326,12 +326,12 @@ UniValue mempoolToJSON(bool fVerbose = false) { const uint256& hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); - info.push_back(Pair("size", (int)e.GetTxSize())); - info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); - info.push_back(Pair("time", e.GetTime())); - info.push_back(Pair("height", (int)e.GetHeight())); - info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); - info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); + info.pushKV("size", (int)e.GetTxSize()); + info.pushKV("fee", ValueFromAmount(e.GetFee())); + info.pushKV("time", e.GetTime()); + info.pushKV("height", (int)e.GetHeight()); + info.pushKV("startingpriority", e.GetPriority(e.GetHeight())); + info.pushKV("currentpriority", e.GetPriority(chainActive.Height())); const CTransaction& tx = e.GetTx(); set setDepends; for (const CTxIn& txin : tx.vin) @@ -346,8 +346,8 @@ UniValue mempoolToJSON(bool fVerbose = false) depends.push_back(dep); } - info.push_back(Pair("depends", depends)); - o.push_back(Pair(hash.ToString(), info)); + info.pushKV("depends", depends); + o.pushKV(hash.ToString(), info); } return o; } @@ -531,17 +531,17 @@ UniValue getblockhashes(const UniValue& params, bool fHelp) "Run './bitcoinz-cli help getblockhashes' for instructions on how to enable this feature."); } - unsigned int high = params[0].get_int(); - unsigned int low = params[1].get_int(); + unsigned int high = params[0].getInt(); + unsigned int low = params[1].getInt(); bool fActiveOnly = false; bool fLogicalTS = false; if (params.size() > 2) { - UniValue noOrphans = find_value(params[2].get_obj(), "noOrphans"); + UniValue noOrphans = params[2].get_obj().find_value("noOrphans"); if (!noOrphans.isNull()) fActiveOnly = noOrphans.get_bool(); - UniValue returnLogical = find_value(params[2].get_obj(), "logicalTimes"); + UniValue returnLogical = params[2].get_obj().find_value("logicalTimes"); if (!returnLogical.isNull()) fLogicalTS = returnLogical.get_bool(); } @@ -559,8 +559,8 @@ UniValue getblockhashes(const UniValue& params, bool fHelp) it!=blockHashes.end(); it++) { if (fLogicalTS) { UniValue item(UniValue::VOBJ); - item.push_back(Pair("blockhash", it->first.GetHex())); - item.push_back(Pair("logicalts", (int)it->second)); + item.pushKV("blockhash", it->first.GetHex()); + item.pushKV("logicalts", (int)it->second); result.push_back(item); } else { result.push_back(it->first.GetHex()); @@ -617,7 +617,7 @@ UniValue getblockhash(const UniValue& params, bool fHelp) LOCK(cs_main); - const CBlockIndex* pblockindex = chainActive[interpretHeightArg(params[0].get_int(), chainActive.Height())]; + const CBlockIndex* pblockindex = chainActive[interpretHeightArg(params[0].getInt(), chainActive.Height())]; return pblockindex->GetBlockHash().GetHex(); } @@ -744,7 +744,7 @@ UniValue getblock(const UniValue& params, bool fHelp) int verbosity = 1; if (params.size() > 1) { if(params[1].isNum()) { - verbosity = params[1].get_int(); + verbosity = params[1].getInt(); } else { verbosity = params[1].get_bool() ? 1 : 0; } @@ -804,13 +804,13 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) CCoinsStats stats; FlushStateToDisk(); if (pcoinsTip->GetStats(stats)) { - ret.push_back(Pair("height", (int64_t)stats.nHeight)); - ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); - ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); - ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs)); - ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize)); - ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); - ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); + ret.pushKV("height", (int64_t)stats.nHeight); + ret.pushKV("bestblock", stats.hashBlock.GetHex()); + ret.pushKV("transactions", (int64_t)stats.nTransactions); + ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs); + ret.pushKV("bytes_serialized", (int64_t)stats.nSerializedSize); + ret.pushKV("hash_serialized", stats.hashSerialized.GetHex()); + ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount)); } return ret; } @@ -859,7 +859,7 @@ UniValue gettxout(const UniValue& params, bool fHelp) std::string strHash = params[0].get_str(); uint256 hash(uint256S(strHash)); - int n = params[1].get_int(); + int n = params[1].getInt(); bool fMempool = true; if (params.size() > 2) fMempool = params[2].get_bool(); @@ -880,17 +880,17 @@ UniValue gettxout(const UniValue& params, bool fHelp) BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); CBlockIndex *pindex = it->second; - ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); + ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) - ret.push_back(Pair("confirmations", 0)); + ret.pushKV("confirmations", 0); else - ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); - ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); + ret.pushKV("confirmations", pindex->nHeight - coins.nHeight + 1); + ret.pushKV("value", ValueFromAmount(coins.vout[n].nValue)); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); - ret.push_back(Pair("scriptPubKey", o)); - ret.push_back(Pair("version", coins.nVersion)); - ret.push_back(Pair("coinbase", coins.fCoinBase)); + ret.pushKV("scriptPubKey", o); + ret.pushKV("version", coins.nVersion); + ret.pushKV("coinbase", coins.fCoinBase); return ret; } @@ -916,9 +916,9 @@ UniValue verifychain(const UniValue& params, bool fHelp) LOCK(cs_main); if (params.size() > 0) - nCheckLevel = params[0].get_int(); + nCheckLevel = params[0].getInt(); if (params.size() > 1) - nCheckDepth = params[1].get_int(); + nCheckDepth = params[1].getInt(); return CVerifyDB().VerifyDB(Params(), pcoinsTip, nCheckLevel, nCheckDepth); } @@ -936,20 +936,20 @@ static UniValue SoftForkMajorityDesc(int minVersion, CBlockIndex* pindex, int nR } UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("status", nFound >= nRequired)); - rv.push_back(Pair("found", nFound)); - rv.push_back(Pair("required", nRequired)); - rv.push_back(Pair("window", consensusParams.nMajorityWindow)); + rv.pushKV("status", nFound >= nRequired); + rv.pushKV("found", nFound); + rv.pushKV("required", nRequired); + rv.pushKV("window", consensusParams.nMajorityWindow); return rv; } static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams) { UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("id", name)); - rv.push_back(Pair("version", version)); - rv.push_back(Pair("enforce", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams))); - rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityRejectBlockOutdated, consensusParams))); + rv.pushKV("id", name); + rv.pushKV("version", version); + rv.pushKV("enforce", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)); + rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams.nMajorityRejectBlockOutdated, consensusParams)); return rv; } @@ -957,14 +957,14 @@ static UniValue NetworkUpgradeDesc(const Consensus::Params& consensusParams, Con { UniValue rv(UniValue::VOBJ); auto upgrade = NetworkUpgradeInfo[idx]; - rv.push_back(Pair("name", upgrade.strName)); - rv.push_back(Pair("activationheight", consensusParams.vUpgrades[idx].nActivationHeight)); + rv.pushKV("name", upgrade.strName); + rv.pushKV("activationheight", consensusParams.vUpgrades[idx].nActivationHeight); switch (NetworkUpgradeState(height, consensusParams, idx)) { - case UPGRADE_DISABLED: rv.push_back(Pair("status", "disabled")); break; - case UPGRADE_PENDING: rv.push_back(Pair("status", "pending")); break; - case UPGRADE_ACTIVE: rv.push_back(Pair("status", "active")); break; + case UPGRADE_DISABLED: rv.pushKV("status", "disabled"); break; + case UPGRADE_PENDING: rv.pushKV("status", "pending"); break; + case UPGRADE_ACTIVE: rv.pushKV("status", "active"); break; } - rv.push_back(Pair("info", upgrade.strInfo)); + rv.pushKV("info", upgrade.strInfo); return rv; } @@ -978,9 +978,9 @@ void NetworkUpgradeDescPushBack( // hidden. This is used when network upgrade implementations are merged // without specifying the activation height. if (consensusParams.vUpgrades[idx].nActivationHeight != Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) { - networkUpgrades.push_back(Pair( + networkUpgrades.pushKV( HexInt(NetworkUpgradeInfo[idx].nBranchId), - NetworkUpgradeDesc(consensusParams, idx, height))); + NetworkUpgradeDesc(consensusParams, idx, height)); } } @@ -1038,43 +1038,43 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) LOCK(cs_main); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("chain", Params().NetworkIDString())); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); - obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); - obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); - obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip()))); - obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); - obj.push_back(Pair("pruned", fPruneMode)); - obj.push_back(Pair("size_on_disk", CalculateCurrentUsage())); + obj.pushKV("chain", Params().NetworkIDString()); + obj.pushKV("blocks", (int)chainActive.Height()); + obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); + obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()); + obj.pushKV("difficulty", (double)GetNetworkDifficulty()); + obj.pushKV("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip())); + obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex()); + obj.pushKV("pruned", fPruneMode); + obj.pushKV("size_on_disk", CalculateCurrentUsage()); SproutMerkleTree tree; pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree); - obj.push_back(Pair("commitments", static_cast(tree.size()))); + obj.pushKV("commitments", static_cast(tree.size())); CBlockIndex* tip = chainActive.Tip(); UniValue valuePools(UniValue::VARR); valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, std::nullopt)); valuePools.push_back(ValuePoolDesc("sapling", tip->nChainSaplingValue, std::nullopt)); - obj.push_back(Pair("valuePools", valuePools)); + obj.pushKV("valuePools", valuePools); const Consensus::Params& consensusParams = Params().GetConsensus(); UniValue softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - obj.push_back(Pair("softforks", softforks)); + obj.pushKV("softforks", softforks); UniValue upgrades(UniValue::VOBJ); for (int i = Consensus::UPGRADE_OVERWINTER; i < Consensus::MAX_NETWORK_UPGRADES; i++) { NetworkUpgradeDescPushBack(upgrades, consensusParams, Consensus::UpgradeIndex(i), tip->nHeight); } - obj.push_back(Pair("upgrades", upgrades)); + obj.pushKV("upgrades", upgrades); UniValue consensus(UniValue::VOBJ); - consensus.push_back(Pair("chaintip", HexInt(CurrentEpochBranchId(tip->nHeight, consensusParams)))); - consensus.push_back(Pair("nextblock", HexInt(CurrentEpochBranchId(tip->nHeight + 1, consensusParams)))); - obj.push_back(Pair("consensus", consensus)); + consensus.pushKV("chaintip", HexInt(CurrentEpochBranchId(tip->nHeight, consensusParams))); + consensus.pushKV("nextblock", HexInt(CurrentEpochBranchId(tip->nHeight + 1, consensusParams))); + obj.pushKV("consensus", consensus); if (fPruneMode) { @@ -1082,11 +1082,11 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) block = block->pprev; - obj.push_back(Pair("pruneheight", block->nHeight)); + obj.pushKV("pruneheight", block->nHeight); } if (Params().NetworkIDString() == "regtest") { - obj.push_back(Pair("fullyNotified", ChainIsFullyNotified())); + obj.pushKV("fullyNotified", ChainIsFullyNotified()); } return obj; @@ -1163,11 +1163,11 @@ UniValue getchaintips(const UniValue& params, bool fHelp) for (const CBlockIndex* block : setTips) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("height", block->nHeight)); - obj.push_back(Pair("hash", block->phashBlock->GetHex())); + obj.pushKV("height", block->nHeight); + obj.pushKV("hash", block->phashBlock->GetHex()); const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight; - obj.push_back(Pair("branchlen", branchLen)); + obj.pushKV("branchlen", branchLen); string status; if (chainActive.Contains(block)) { @@ -1189,7 +1189,7 @@ UniValue getchaintips(const UniValue& params, bool fHelp) // No clue. status = "unknown"; } - obj.push_back(Pair("status", status)); + obj.pushKV("status", status); res.push_back(obj); } @@ -1308,12 +1308,12 @@ UniValue z_gettreestate(const UniValue& params, bool fHelp) UniValue mempoolInfoToJSON() { UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("size", (int64_t) mempool.size())); - ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); - ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); + ret.pushKV("size", (int64_t) mempool.size()); + ret.pushKV("bytes", (int64_t) mempool.GetTotalTxSize()); + ret.pushKV("usage", (int64_t) mempool.DynamicMemoryUsage()); if (Params().NetworkIDString() == "regtest") { - ret.push_back(Pair("fullyNotified", mempool.IsFullyNotified())); + ret.pushKV("fullyNotified", mempool.IsFullyNotified()); } return ret; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 37ad4c3b2..c385dc204 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -115,7 +115,7 @@ UniValue getnetworksolps(const UniValue& params, bool fHelp) ); LOCK(cs_main); - return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); + return GetNetworkHashPS(params.size() > 0 ? params[0].getInt() : 120, params.size() > 1 ? params[1].getInt() : -1); } UniValue getnetworkhashps(const UniValue& params, bool fHelp) @@ -138,7 +138,7 @@ UniValue getnetworkhashps(const UniValue& params, bool fHelp) ); LOCK(cs_main); - return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); + return GetNetworkHashPS(params.size() > 0 ? params[0].getInt() : 120, params.size() > 1 ? params[1].getInt() : -1); } #ifdef ENABLE_MINING @@ -183,7 +183,7 @@ UniValue generate(const UniValue& params, bool fHelp) int nHeightStart = 0; int nHeightEnd = 0; int nHeight = 0; - int nGenerate = params[0].get_int(); + int nGenerate = params[0].getInt(); boost::shared_ptr coinbaseScript; GetMainSignals().ScriptForMining(coinbaseScript); @@ -303,7 +303,7 @@ UniValue setgenerate(const UniValue& params, bool fHelp) int nGenProcLimit = -1; if (params.size() > 1) { - nGenProcLimit = params[1].get_int(); + nGenProcLimit = params[1].getInt(); if (nGenProcLimit == 0) fGenerate = false; } @@ -346,20 +346,20 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) LOCK(cs_main); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); - obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); - obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", DEFAULT_GENERATE_THREADS))); - obj.push_back(Pair("localsolps" , getlocalsolps(params, false))); - obj.push_back(Pair("networksolps", getnetworksolps(params, false))); - obj.push_back(Pair("networkhashps", getnetworksolps(params, false))); - obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); - obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.pushKV("blocks", (int)chainActive.Height()); + obj.pushKV("currentblocksize", (uint64_t)nLastBlockSize); + obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx); + obj.pushKV("difficulty", (double)GetNetworkDifficulty()); + obj.pushKV("errors", GetWarnings("statusbar")); + obj.pushKV("genproclimit", (int)GetArg("-genproclimit", DEFAULT_GENERATE_THREADS)); + obj.pushKV("localsolps" , getlocalsolps(params, false)); + obj.pushKV("networksolps", getnetworksolps(params, false)); + obj.pushKV("networkhashps", getnetworksolps(params, false)); + obj.pushKV("pooledtx", (uint64_t)mempool.size()); + obj.pushKV("testnet", Params().TestnetToBeDeprecatedFieldRPC()); + obj.pushKV("chain", Params().NetworkIDString()); #ifdef ENABLE_MINING - obj.push_back(Pair("generate", getgenerate(params, false))); + obj.pushKV("generate", getgenerate(params, false)); #endif return obj; } @@ -390,7 +390,7 @@ UniValue prioritisetransaction(const UniValue& params, bool fHelp) LOCK(cs_main); uint256 hash = ParseHashStr(params[0].get_str(), "txid"); - CAmount nAmount = params[2].get_int64(); + CAmount nAmount = params[2].getInt(); mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount); return true; @@ -500,7 +500,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (params.size() > 0) { const UniValue& oparam = params[0].get_obj(); - const UniValue& modeval = find_value(oparam, "mode"); + const UniValue& modeval = oparam.find_value("mode"); if (modeval.isStr()) strMode = modeval.get_str(); else if (modeval.isNull()) @@ -509,11 +509,11 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) } else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - lpval = find_value(oparam, "longpollid"); + lpval = oparam.find_value("longpollid"); if (strMode == "proposal") { - const UniValue& dataval = find_value(oparam, "data"); + const UniValue& dataval = oparam.find_value("data"); if (!dataval.isStr()) throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal"); @@ -659,9 +659,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("data", EncodeHexTx(tx))); + entry.pushKV("data", EncodeHexTx(tx)); - entry.push_back(Pair("hash", txHash.GetHex())); + entry.pushKV("hash", txHash.GetHex()); UniValue deps(UniValue::VARR); for (const CTxIn &in : tx.vin) @@ -669,17 +669,17 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (setTxIndex.count(in.prevout.hash)) deps.push_back(setTxIndex[in.prevout.hash]); } - entry.push_back(Pair("depends", deps)); + entry.pushKV("depends", deps); int index_in_template = i - 1; - entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); - entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); + entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]); + entry.pushKV("sigops", pblocktemplate->vTxSigOps[index_in_template]); if (tx.IsCoinBase()) { // Show community fee if it is required if (pblock->vtx[0].vout.size() > 1) { // Correct this if GetBlockTemplate changes the order - entry.push_back(Pair("foundersreward", (int64_t)tx.vout[1].nValue)); + entry.pushKV("foundersreward", (int64_t)tx.vout[1].nValue); // GITHUB issue #66 - Add founderaddress to gbt const CScript & scriptPublicKey = tx.vout[1].scriptPubKey; @@ -692,10 +692,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) for (const CTxDestination& addr : addresses) { o.push_back(EncodeDestination(addr)); } - entry.push_back(Pair("foundersraddress", o)); + entry.pushKV("foundersraddress", o); } - entry.push_back(Pair("required", true)); + entry.pushKV("required", true); txCoinbase = entry; } else { transactions.push_back(entry); @@ -703,7 +703,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) } UniValue aux(UniValue::VOBJ); - aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + aux.pushKV("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); @@ -716,29 +716,29 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) } UniValue result(UniValue::VOBJ); - result.push_back(Pair("capabilities", aCaps)); - result.push_back(Pair("version", pblock->nVersion)); - result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); - result.push_back(Pair("finalsaplingroothash", pblock->hashFinalSaplingRoot.GetHex())); - result.push_back(Pair("merkleroot", BlockMerkleRoot(*pblock).GetHex())); - result.push_back(Pair("transactions", transactions)); + result.pushKV("capabilities", aCaps); + result.pushKV("version", pblock->nVersion); + result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); + result.pushKV("finalsaplingroothash", pblock->hashFinalSaplingRoot.GetHex()); + result.pushKV("merkleroot", BlockMerkleRoot(*pblock).GetHex()); + result.pushKV("transactions", transactions); if (coinbasetxn) { assert(txCoinbase.isObject()); - result.push_back(Pair("coinbasetxn", txCoinbase)); + result.pushKV("coinbasetxn", txCoinbase); } else { - result.push_back(Pair("coinbaseaux", aux)); - result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); + result.pushKV("coinbaseaux", aux); + result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue); } - result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); - result.push_back(Pair("target", hashTarget.GetHex())); - result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); - result.push_back(Pair("mutable", aMutable)); - result.push_back(Pair("noncerange", "00000000ffffffff")); - result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); - result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); - result.push_back(Pair("curtime", pblock->GetBlockTime())); - result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); - result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)); + result.pushKV("target", hashTarget.GetHex()); + result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1); + result.pushKV("mutable", aMutable); + result.pushKV("noncerange", "00000000ffffffff"); + result.pushKV("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS); + result.pushKV("sizelimit", (int64_t)MAX_BLOCK_SIZE); + result.pushKV("curtime", pblock->GetBlockTime()); + result.pushKV("bits", strprintf("%08x", pblock->nBits)); + result.pushKV("height", (int64_t)(pindexPrev->nHeight+1)); return result; } @@ -849,7 +849,7 @@ UniValue estimatefee(const UniValue& params, bool fHelp) RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - int nBlocks = params[0].get_int(); + int nBlocks = params[0].getInt(); if (nBlocks < 1) nBlocks = 1; @@ -881,7 +881,7 @@ UniValue estimatepriority(const UniValue& params, bool fHelp) RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - int nBlocks = params[0].get_int(); + int nBlocks = params[0].getInt(); if (nBlocks < 1) nBlocks = 1; @@ -907,7 +907,7 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp) ); LOCK(cs_main); - int nHeight = (params.size()==1) ? params[0].get_int() : chainActive.Height(); + int nHeight = (params.size()==1) ? params[0].getInt() : chainActive.Height(); if (nHeight < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); @@ -918,8 +918,8 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp) nReward -= nCommunityFee; } UniValue result(UniValue::VOBJ); - result.push_back(Pair("miner", ValueFromAmount(nReward))); - result.push_back(Pair("founders", ValueFromAmount(nCommunityFee))); + result.pushKV("miner", ValueFromAmount(nReward)); + result.pushKV("founders", ValueFromAmount(nCommunityFee)); return result; } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index e4677b854..fb0b86852 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -83,31 +83,31 @@ UniValue getinfo(const UniValue& params, bool fHelp) GetProxy(NET_IPV4, proxy); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); + obj.pushKV("version", CLIENT_VERSION); + obj.pushKV("protocolversion", PROTOCOL_VERSION); #ifdef ENABLE_WALLET if (pwalletMain) { - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.pushKV("walletversion", pwalletMain->GetVersion()); + obj.pushKV("balance", ValueFromAmount(pwalletMain->GetBalance())); } #endif - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("timeoffset", GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); + obj.pushKV("blocks", (int)chainActive.Height()); + obj.pushKV("timeoffset", GetTimeOffset()); + obj.pushKV("connections", (int)vNodes.size()); + obj.pushKV("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())); + obj.pushKV("difficulty", (double)GetDifficulty()); + obj.pushKV("testnet", Params().TestnetToBeDeprecatedFieldRPC()); #ifdef ENABLE_WALLET if (pwalletMain) { - obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + obj.pushKV("keypoololdest", pwalletMain->GetOldestKeyPoolTime()); + obj.pushKV("keypoolsize", (int)pwalletMain->GetKeyPoolSize()); } if (pwalletMain && pwalletMain->IsCrypted()) - obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); - obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); + obj.pushKV("unlocked_until", nWalletUnlockTime); + obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); #endif - obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); + obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); + obj.pushKV("errors", GetWarnings("statusbar")); return obj; } @@ -120,10 +120,10 @@ class DescribeAddressVisitor UniValue operator()(const CKeyID &keyID) const { UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; - obj.push_back(Pair("isscript", false)); + obj.pushKV("isscript", false); if (pwalletMain && pwalletMain->GetPubKey(keyID, vchPubKey)) { - obj.push_back(Pair("pubkey", HexStr(vchPubKey))); - obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + obj.pushKV("pubkey", HexStr(vchPubKey)); + obj.pushKV("iscompressed", vchPubKey.IsCompressed()); } return obj; } @@ -131,21 +131,21 @@ class DescribeAddressVisitor UniValue operator()(const CScriptID &scriptID) const { UniValue obj(UniValue::VOBJ); CScript subscript; - obj.push_back(Pair("isscript", true)); + obj.pushKV("isscript", true); if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { std::vector addresses; txnouttype whichType; int nRequired; ExtractDestinations(subscript, whichType, addresses, nRequired); - obj.push_back(Pair("script", GetTxnOutputType(whichType))); - obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + obj.pushKV("script", GetTxnOutputType(whichType)); + obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); UniValue a(UniValue::VARR); for (const CTxDestination& addr : addresses) { a.push_back(EncodeDestination(addr)); } - obj.push_back(Pair("addresses", a)); + obj.pushKV("addresses", a); if (whichType == TX_MULTISIG) - obj.push_back(Pair("sigsrequired", nRequired)); + obj.pushKV("sigsrequired", nRequired); } return obj; } @@ -186,23 +186,23 @@ UniValue validateaddress(const UniValue& params, bool fHelp) bool isValid = IsValidDestination(dest); UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("isvalid", isValid)); + ret.pushKV("isvalid", isValid); if (isValid) { std::string currentAddress = EncodeDestination(dest); - ret.push_back(Pair("address", currentAddress)); + ret.pushKV("address", currentAddress); CScript scriptPubKey = GetScriptForDestination(dest); - ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); #ifdef ENABLE_WALLET isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; - ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); - ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false)); + ret.pushKV("ismine", (mine & ISMINE_SPENDABLE) ? true : false); + ret.pushKV("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false); UniValue detail = std::visit(DescribeAddressVisitor(), dest); ret.pushKVs(detail); if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); + ret.pushKV("account", pwalletMain->mapAddressBook[dest].name); #endif } return ret; @@ -216,12 +216,12 @@ class DescribePaymentAddressVisitor UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("type", "sprout")); - obj.push_back(Pair("payingkey", zaddr.a_pk.GetHex())); - obj.push_back(Pair("transmissionkey", zaddr.pk_enc.GetHex())); + obj.pushKV("type", "sprout"); + obj.pushKV("payingkey", zaddr.a_pk.GetHex()); + obj.pushKV("transmissionkey", zaddr.pk_enc.GetHex()); #ifdef ENABLE_WALLET if (pwalletMain) { - obj.push_back(Pair("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr))); + obj.pushKV("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr)); } #endif return obj; @@ -229,12 +229,12 @@ class DescribePaymentAddressVisitor UniValue operator()(const libzcash::SaplingPaymentAddress &zaddr) const { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("type", "sapling")); - obj.push_back(Pair("diversifier", HexStr(zaddr.d))); - obj.push_back(Pair("diversifiedtransmissionkey", zaddr.pk_d.GetHex())); + obj.pushKV("type", "sapling"); + obj.pushKV("diversifier", HexStr(zaddr.d)); + obj.pushKV("diversifiedtransmissionkey", zaddr.pk_d.GetHex()); #ifdef ENABLE_WALLET if (pwalletMain) { - obj.push_back(Pair("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr))); + obj.pushKV("ismine", HaveSpendingKeyForPaymentAddress(pwalletMain)(zaddr)); } #endif return obj; @@ -278,10 +278,10 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp) bool isValid = IsValidPaymentAddress(address); UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("isvalid", isValid)); + ret.pushKV("isvalid", isValid); if (isValid) { - ret.push_back(Pair("address", strAddress)); + ret.pushKV("address", strAddress); UniValue detail = std::visit(DescribePaymentAddressVisitor(), address); ret.pushKVs(detail); } @@ -294,7 +294,7 @@ UniValue z_validateaddress(const UniValue& params, bool fHelp) */ CScript _createmultisig_redeemScript(const UniValue& params) { - int nRequired = params[0].get_int(); + int nRequired = params[0].getInt(); const UniValue& keys = params[1].get_array(); // Gather public keys @@ -388,8 +388,8 @@ UniValue createmultisig(const UniValue& params, bool fHelp) CScriptID innerID(inner); UniValue result(UniValue::VOBJ); - result.push_back(Pair("address", EncodeDestination(innerID))); - result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + result.pushKV("address", EncodeDestination(innerID)); + result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); return result; } @@ -471,7 +471,7 @@ UniValue setmocktime(const UniValue& params, bool fHelp) LOCK2(cs_main, cs_vNodes); RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - SetMockTime(params[0].get_int64()); + SetMockTime(params[0].getInt()); uint64_t t = GetTime(); for (CNode* pnode : vNodes) { @@ -550,7 +550,7 @@ static bool getAddressesFromParams( if (params[0].isStr()) { param_addresses.push_back(params[0].get_str()); } else if (params[0].isObject()) { - UniValue addressValues = find_value(params[0].get_obj(), "addresses"); + UniValue addressValues = params[0].get_obj().find_value("addresses"); if (!addressValues.isArray()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Addresses is expected to be an array"); @@ -639,14 +639,14 @@ UniValue getaddressmempool(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); } UniValue delta(UniValue::VOBJ); - delta.push_back(Pair("address", address)); - delta.push_back(Pair("txid", it.first.txhash.GetHex())); - delta.push_back(Pair("index", (int)it.first.index)); - delta.push_back(Pair("satoshis", it.second.amount)); - delta.push_back(Pair("timestamp", it.second.time)); + delta.pushKV("address", address); + delta.pushKV("txid", it.first.txhash.GetHex()); + delta.pushKV("index", (int)it.first.index); + delta.pushKV("satoshis", it.second.amount); + delta.pushKV("timestamp", it.second.time); if (it.second.amount < 0) { - delta.push_back(Pair("prevtxid", it.second.prevhash.GetHex())); - delta.push_back(Pair("prevout", (int)it.second.prevout)); + delta.pushKV("prevtxid", it.second.prevhash.GetHex()); + delta.pushKV("prevout", (int)it.second.prevout); } result.push_back(delta); } @@ -715,7 +715,7 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) bool includeChainInfo = false; if (params[0].isObject()) { - UniValue chainInfo = find_value(params[0].get_obj(), "chainInfo"); + UniValue chainInfo = params[0].get_obj().find_value("chainInfo"); if (!chainInfo.isNull()) { includeChainInfo = chainInfo.get_bool(); } @@ -743,12 +743,12 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); } - output.push_back(Pair("address", address)); - output.push_back(Pair("txid", it.first.txhash.GetHex())); - output.push_back(Pair("outputIndex", (int)it.first.index)); - output.push_back(Pair("script", HexStr(it.second.script.begin(), it.second.script.end()))); - output.push_back(Pair("satoshis", it.second.satoshis)); - output.push_back(Pair("height", it.second.blockHeight)); + output.pushKV("address", address); + output.pushKV("txid", it.first.txhash.GetHex()); + output.pushKV("outputIndex", (int)it.first.index); + output.pushKV("script", HexStr(it.second.script.begin(), it.second.script.end())); + output.pushKV("satoshis", it.second.satoshis); + output.pushKV("height", it.second.blockHeight); utxos.push_back(output); } @@ -756,11 +756,11 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) return utxos; UniValue result(UniValue::VOBJ); - result.push_back(Pair("utxos", utxos)); + result.pushKV("utxos", utxos); LOCK(cs_main); // for chainActive - result.push_back(Pair("hash", chainActive.Tip()->GetBlockHash().GetHex())); - result.push_back(Pair("height", (int)chainActive.Height())); + result.pushKV("hash", chainActive.Tip()->GetBlockHash().GetHex()); + result.pushKV("height", (int)chainActive.Height()); return result; } @@ -769,12 +769,12 @@ static void getHeightRange(const UniValue& params, int& start, int& end) start = 0; end = 0; if (params[0].isObject()) { - UniValue startValue = find_value(params[0].get_obj(), "start"); - UniValue endValue = find_value(params[0].get_obj(), "end"); + UniValue startValue = params[0].get_obj().find_value("start"); + UniValue endValue = params[0].get_obj().find_value("end"); // If either is not specified, the other is ignored. if (!startValue.isNull() && !endValue.isNull()) { - start = startValue.get_int(); - end = endValue.get_int(); + start = startValue.getInt(); + end = endValue.getInt(); if (start <= 0 || end <= 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start and end are expected to be greater than zero"); @@ -890,7 +890,7 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) bool includeChainInfo = false; if (params[0].isObject()) { - UniValue chainInfo = find_value(params[0].get_obj(), "chainInfo"); + UniValue chainInfo = params[0].get_obj().find_value("chainInfo"); if (!chainInfo.isNull()) { includeChainInfo = chainInfo.get_bool(); } @@ -904,12 +904,12 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) } UniValue delta(UniValue::VOBJ); - delta.push_back(Pair("address", address)); - delta.push_back(Pair("blockindex", (int)it.first.txindex)); - delta.push_back(Pair("height", it.first.blockHeight)); - delta.push_back(Pair("index", (int)it.first.index)); - delta.push_back(Pair("satoshis", it.second)); - delta.push_back(Pair("txid", it.first.txhash.GetHex())); + delta.pushKV("address", address); + delta.pushKV("blockindex", (int)it.first.txindex); + delta.pushKV("height", it.first.blockHeight); + delta.pushKV("index", (int)it.first.index); + delta.pushKV("satoshis", it.second); + delta.pushKV("txid", it.first.txhash.GetHex()); deltas.push_back(delta); } @@ -926,15 +926,15 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) if (start > chainActive.Height() || end > chainActive.Height()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start or end is outside chain range"); } - startInfo.push_back(Pair("hash", chainActive[start]->GetBlockHash().GetHex())); - endInfo.push_back(Pair("hash", chainActive[end]->GetBlockHash().GetHex())); + startInfo.pushKV("hash", chainActive[start]->GetBlockHash().GetHex()); + endInfo.pushKV("hash", chainActive[end]->GetBlockHash().GetHex()); } - startInfo.push_back(Pair("height", start)); - endInfo.push_back(Pair("height", end)); + startInfo.pushKV("height", start); + endInfo.pushKV("height", end); - result.push_back(Pair("deltas", deltas)); - result.push_back(Pair("start", startInfo)); - result.push_back(Pair("end", endInfo)); + result.pushKV("deltas", deltas); + result.pushKV("start", startInfo); + result.pushKV("end", endInfo); return result; } @@ -991,8 +991,8 @@ UniValue getaddressbalance(const UniValue& params, bool fHelp) balance += it.second; } UniValue result(UniValue::VOBJ); - result.push_back(Pair("balance", balance)); - result.push_back(Pair("received", received)); + result.pushKV("balance", balance); + result.pushKV("received", received); return result; } @@ -1094,15 +1094,15 @@ UniValue getspentinfo(const UniValue& params, bool fHelp) "Run './bitcoinz-cli help getspentinfo' for instructions on how to enable this feature."); } - UniValue txidValue = find_value(params[0].get_obj(), "txid"); - UniValue indexValue = find_value(params[0].get_obj(), "index"); + UniValue txidValue = params[0].get_obj().find_value("txid"); + UniValue indexValue = params[0].get_obj().find_value("index"); if (!txidValue.isStr()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid txid, must be a string"); if (!indexValue.isNum()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid index, must be an integer"); uint256 txid = ParseHashV(txidValue, "txid"); - int outputIndex = indexValue.get_int(); + int outputIndex = indexValue.getInt(); CSpentIndexKey key(txid, outputIndex); CSpentIndexValue value; @@ -1114,9 +1114,9 @@ UniValue getspentinfo(const UniValue& params, bool fHelp) } } UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("txid", value.txid.GetHex())); - obj.push_back(Pair("index", (int)value.inputIndex)); - obj.push_back(Pair("height", value.blockHeight)); + obj.pushKV("txid", value.txid.GetHex()); + obj.pushKV("index", (int)value.inputIndex); + obj.pushKV("height", value.blockHeight); return obj; } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7db0f5c03..59c32f6fd 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -124,38 +124,38 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); CNodeStateStats statestats; bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); - obj.push_back(Pair("id", stats.nodeid)); - obj.push_back(Pair("addr", stats.addrName)); + obj.pushKV("id", stats.nodeid); + obj.pushKV("addr", stats.addrName); if (!(stats.addrLocal.empty())) - obj.push_back(Pair("addrlocal", stats.addrLocal)); - obj.push_back(Pair("services", strprintf("%016x", stats.nServices))); - obj.push_back(Pair("lastsend", stats.nLastSend)); - obj.push_back(Pair("lastrecv", stats.nLastRecv)); - obj.push_back(Pair("bytessent", stats.nSendBytes)); - obj.push_back(Pair("bytesrecv", stats.nRecvBytes)); - obj.push_back(Pair("conntime", stats.nTimeConnected)); - obj.push_back(Pair("timeoffset", stats.nTimeOffset)); - obj.push_back(Pair("pingtime", stats.dPingTime)); + obj.pushKV("addrlocal", stats.addrLocal); + obj.pushKV("services", strprintf("%016x", stats.nServices)); + obj.pushKV("lastsend", stats.nLastSend); + obj.pushKV("lastrecv", stats.nLastRecv); + obj.pushKV("bytessent", stats.nSendBytes); + obj.pushKV("bytesrecv", stats.nRecvBytes); + obj.pushKV("conntime", stats.nTimeConnected); + obj.pushKV("timeoffset", stats.nTimeOffset); + obj.pushKV("pingtime", stats.dPingTime); if (stats.dPingWait > 0.0) - obj.push_back(Pair("pingwait", stats.dPingWait)); - obj.push_back(Pair("version", stats.nVersion)); + obj.pushKV("pingwait", stats.dPingWait); + obj.pushKV("version", stats.nVersion); // Use the sanitized form of subver here, to avoid tricksy remote peers from // corrupting or modifying the JSON output by putting special characters in // their ver message. - obj.push_back(Pair("subver", stats.cleanSubVer)); - obj.push_back(Pair("inbound", stats.fInbound)); - obj.push_back(Pair("startingheight", stats.nStartingHeight)); + obj.pushKV("subver", stats.cleanSubVer); + obj.pushKV("inbound", stats.fInbound); + obj.pushKV("startingheight", stats.nStartingHeight); if (fStateStats) { - obj.push_back(Pair("banscore", statestats.nMisbehavior)); - obj.push_back(Pair("synced_headers", statestats.nSyncHeight)); - obj.push_back(Pair("synced_blocks", statestats.nCommonHeight)); + obj.pushKV("banscore", statestats.nMisbehavior); + obj.pushKV("synced_headers", statestats.nSyncHeight); + obj.pushKV("synced_blocks", statestats.nCommonHeight); UniValue heights(UniValue::VARR); for (int height : statestats.vHeightInFlight) { heights.push_back(height); } - obj.push_back(Pair("inflight", heights)); + obj.pushKV("inflight", heights); } - obj.push_back(Pair("whitelisted", stats.fWhitelisted)); + obj.pushKV("whitelisted", stats.fWhitelisted); ret.push_back(obj); } @@ -297,7 +297,7 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) { for (const std::string& strAddNode : laddedNodes) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", strAddNode)); + obj.pushKV("addednode", strAddNode); ret.push_back(obj); } return ret; @@ -311,10 +311,10 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) else { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", strAddNode)); - obj.push_back(Pair("connected", false)); + obj.pushKV("addednode", strAddNode); + obj.pushKV("connected", false); UniValue addresses(UniValue::VARR); - obj.push_back(Pair("addresses", addresses)); + obj.pushKV("addresses", addresses); } } @@ -322,29 +322,29 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) for (list > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", it->first)); + obj.pushKV("addednode", it->first); UniValue addresses(UniValue::VARR); bool fConnected = false; for (const CService& addrNode : it->second) { bool fFound = false; UniValue node(UniValue::VOBJ); - node.push_back(Pair("address", addrNode.ToString())); + node.pushKV("address", addrNode.ToString()); for (CNode* pnode : vNodes) { if (pnode->addr == addrNode) { fFound = true; fConnected = true; - node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); + node.pushKV("connected", pnode->fInbound ? "inbound" : "outbound"); break; } } if (!fFound) - node.push_back(Pair("connected", "false")); + node.pushKV("connected", "false"); addresses.push_back(node); } - obj.push_back(Pair("connected", fConnected)); - obj.push_back(Pair("addresses", addresses)); + obj.pushKV("connected", fConnected); + obj.pushKV("addresses", addresses); ret.push_back(obj); } @@ -370,9 +370,9 @@ UniValue getnettotals(const UniValue& params, bool fHelp) ); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv())); - obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent())); - obj.push_back(Pair("timemillis", GetTimeMillis())); + obj.pushKV("totalbytesrecv", CNode::GetTotalBytesRecv()); + obj.pushKV("totalbytessent", CNode::GetTotalBytesSent()); + obj.pushKV("timemillis", GetTimeMillis()); return obj; } @@ -387,11 +387,11 @@ static UniValue GetNetworksInfo() proxyType proxy; UniValue obj(UniValue::VOBJ); GetProxy(network, proxy); - obj.push_back(Pair("name", GetNetworkName(network))); - obj.push_back(Pair("limited", IsLimited(network))); - obj.push_back(Pair("reachable", IsReachable(network))); - obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())); - obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials)); + obj.pushKV("name", GetNetworkName(network)); + obj.pushKV("limited", IsLimited(network)); + obj.pushKV("reachable", IsReachable(network)); + obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()); + obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials); networks.push_back(obj); } return networks; @@ -416,10 +416,10 @@ UniValue getdeprecationinfo(const UniValue& params, bool fHelp) ); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("subversion", - FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()))); - obj.push_back(Pair("deprecationheight", DEPRECATION_HEIGHT)); + obj.pushKV("version", CLIENT_VERSION); + obj.pushKV("subversion", + FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); + obj.pushKV("deprecationheight", DEPRECATION_HEIGHT); return obj; } @@ -466,28 +466,28 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) LOCK(cs_main); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("subversion", strSubVersion)); - obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); - obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); - obj.push_back(Pair("timeoffset", GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("networks", GetNetworksInfo())); - obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); + obj.pushKV("version", CLIENT_VERSION); + obj.pushKV("subversion", strSubVersion); + obj.pushKV("protocolversion",PROTOCOL_VERSION); + obj.pushKV("localservices", strprintf("%016x", nLocalServices)); + obj.pushKV("timeoffset", GetTimeOffset()); + obj.pushKV("connections", (int)vNodes.size()); + obj.pushKV("networks", GetNetworksInfo()); + obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); UniValue localAddresses(UniValue::VARR); { LOCK(cs_mapLocalHost); for (const std::pair &item : mapLocalHost) { UniValue rec(UniValue::VOBJ); - rec.push_back(Pair("address", item.first.ToString())); - rec.push_back(Pair("port", item.second.nPort)); - rec.push_back(Pair("score", item.second.nScore)); + rec.pushKV("address", item.first.ToString()); + rec.pushKV("port", item.second.nPort); + rec.pushKV("score", item.second.nScore); localAddresses.push_back(rec); } } - obj.push_back(Pair("localaddresses", localAddresses)); - obj.push_back(Pair("warnings", GetWarnings("statusbar"))); + obj.pushKV("localaddresses", localAddresses); + obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } @@ -534,7 +534,7 @@ UniValue setban(const UniValue& params, bool fHelp) int64_t banTime = 0; //use standard bantime if not specified if (params.size() >= 3 && !params[2].isNull()) - banTime = params[2].get_int64(); + banTime = params[2].getInt(); bool absolute = false; if (params.size() == 4 && params[3].isTrue()) @@ -573,8 +573,8 @@ UniValue listbanned(const UniValue& params, bool fHelp) for (std::map::iterator it = banMap.begin(); it != banMap.end(); it++) { UniValue rec(UniValue::VOBJ); - rec.push_back(Pair("address", (*it).first.ToString())); - rec.push_back(Pair("banned_until", (*it).second)); + rec.pushKV("address", (*it).first.ToString()); + rec.pushKV("banned_until", (*it).second); bannedAddresses.push_back(rec); } diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index 1361fe6f8..4125757e1 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -29,9 +29,9 @@ using namespace std; string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id) { UniValue request(UniValue::VOBJ); - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); + request.pushKV("method", strMethod); + request.pushKV("params", params); + request.pushKV("id", id); return request.write() + "\n"; } @@ -39,11 +39,11 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un { UniValue reply(UniValue::VOBJ); if (!error.isNull()) - reply.push_back(Pair("result", NullUniValue)); + reply.pushKV("result", NullUniValue); else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); + reply.pushKV("result", result); + reply.pushKV("error", error); + reply.pushKV("id", id); return reply; } @@ -56,8 +56,8 @@ string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValu UniValue JSONRPCError(int code, const string& message) { UniValue error(UniValue::VOBJ); - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); + error.pushKV("code", code); + error.pushKV("message", message); return error; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 2e8c6c0ac..29e4b3111 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -40,23 +40,23 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud vector addresses; int nRequired; - out.push_back(Pair("asm", ScriptToAsmStr(scriptPubKey))); + out.pushKV("asm", ScriptToAsmStr(scriptPubKey)); if (fIncludeHex) - out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())); if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { - out.push_back(Pair("type", GetTxnOutputType(type))); + out.pushKV("type", GetTxnOutputType(type)); return; } - out.push_back(Pair("reqSigs", nRequired)); - out.push_back(Pair("type", GetTxnOutputType(type))); + out.pushKV("reqSigs", nRequired); + out.pushKV("type", GetTxnOutputType(type)); UniValue a(UniValue::VARR); for (const CTxDestination& addr : addresses) { a.push_back(EncodeDestination(addr)); } - out.push_back(Pair("addresses", a)); + out.pushKV("addresses", a); } @@ -67,19 +67,19 @@ UniValue TxJoinSplitToJSON(const CTransaction& tx) { const JSDescription& jsdescription = tx.vJoinSplit[i]; UniValue joinsplit(UniValue::VOBJ); - joinsplit.push_back(Pair("vpub_old", ValueFromAmount(jsdescription.vpub_old))); - joinsplit.push_back(Pair("vpub_oldZat", jsdescription.vpub_old)); - joinsplit.push_back(Pair("vpub_new", ValueFromAmount(jsdescription.vpub_new))); - joinsplit.push_back(Pair("vpub_newZat", jsdescription.vpub_new)); + joinsplit.pushKV("vpub_old", ValueFromAmount(jsdescription.vpub_old)); + joinsplit.pushKV("vpub_oldZat", jsdescription.vpub_old); + joinsplit.pushKV("vpub_new", ValueFromAmount(jsdescription.vpub_new)); + joinsplit.pushKV("vpub_newZat", jsdescription.vpub_new); - joinsplit.push_back(Pair("anchor", jsdescription.anchor.GetHex())); + joinsplit.pushKV("anchor", jsdescription.anchor.GetHex()); { UniValue nullifiers(UniValue::VARR); for (const uint256 nf : jsdescription.nullifiers) { nullifiers.push_back(nf.GetHex()); } - joinsplit.push_back(Pair("nullifiers", nullifiers)); + joinsplit.pushKV("nullifiers", nullifiers); } { @@ -87,31 +87,31 @@ UniValue TxJoinSplitToJSON(const CTransaction& tx) { for (const uint256 commitment : jsdescription.commitments) { commitments.push_back(commitment.GetHex()); } - joinsplit.push_back(Pair("commitments", commitments)); + joinsplit.pushKV("commitments", commitments); } - joinsplit.push_back(Pair("onetimePubKey", jsdescription.ephemeralKey.GetHex())); - joinsplit.push_back(Pair("randomSeed", jsdescription.randomSeed.GetHex())); + joinsplit.pushKV("onetimePubKey", jsdescription.ephemeralKey.GetHex()); + joinsplit.pushKV("randomSeed", jsdescription.randomSeed.GetHex()); { UniValue macs(UniValue::VARR); for (const uint256 mac : jsdescription.macs) { macs.push_back(mac.GetHex()); } - joinsplit.push_back(Pair("macs", macs)); + joinsplit.pushKV("macs", macs); } CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION); auto ps = SproutProofSerializer(ssProof, useGroth); std::visit(ps, jsdescription.proof); - joinsplit.push_back(Pair("proof", HexStr(ssProof.begin(), ssProof.end()))); + joinsplit.pushKV("proof", HexStr(ssProof.begin(), ssProof.end())); { UniValue ciphertexts(UniValue::VARR); for (const ZCNoteEncryption::Ciphertext ct : jsdescription.ciphertexts) { ciphertexts.push_back(HexStr(ct.begin(), ct.end())); } - joinsplit.push_back(Pair("ciphertexts", ciphertexts)); + joinsplit.pushKV("ciphertexts", ciphertexts); } vJoinSplit.push_back(joinsplit); @@ -123,12 +123,12 @@ UniValue TxShieldedSpendsToJSON(const CTransaction& tx) { UniValue vdesc(UniValue::VARR); for (const SpendDescription& spendDesc : tx.vShieldedSpend) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("cv", spendDesc.cv.GetHex())); - obj.push_back(Pair("anchor", spendDesc.anchor.GetHex())); - obj.push_back(Pair("nullifier", spendDesc.nullifier.GetHex())); - obj.push_back(Pair("rk", spendDesc.rk.GetHex())); - obj.push_back(Pair("proof", HexStr(spendDesc.zkproof.begin(), spendDesc.zkproof.end()))); - obj.push_back(Pair("spendAuthSig", HexStr(spendDesc.spendAuthSig.begin(), spendDesc.spendAuthSig.end()))); + obj.pushKV("cv", spendDesc.cv.GetHex()); + obj.pushKV("anchor", spendDesc.anchor.GetHex()); + obj.pushKV("nullifier", spendDesc.nullifier.GetHex()); + obj.pushKV("rk", spendDesc.rk.GetHex()); + obj.pushKV("proof", HexStr(spendDesc.zkproof.begin(), spendDesc.zkproof.end())); + obj.pushKV("spendAuthSig", HexStr(spendDesc.spendAuthSig.begin(), spendDesc.spendAuthSig.end())); vdesc.push_back(obj); } return vdesc; @@ -138,12 +138,12 @@ UniValue TxShieldedOutputsToJSON(const CTransaction& tx) { UniValue vdesc(UniValue::VARR); for (const OutputDescription& outputDesc : tx.vShieldedOutput) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("cv", outputDesc.cv.GetHex())); - obj.push_back(Pair("cmu", outputDesc.cmu.GetHex())); - obj.push_back(Pair("ephemeralKey", outputDesc.ephemeralKey.GetHex())); - obj.push_back(Pair("encCiphertext", HexStr(outputDesc.encCiphertext.begin(), outputDesc.encCiphertext.end()))); - obj.push_back(Pair("outCiphertext", HexStr(outputDesc.outCiphertext.begin(), outputDesc.outCiphertext.end()))); - obj.push_back(Pair("proof", HexStr(outputDesc.zkproof.begin(), outputDesc.zkproof.end()))); + obj.pushKV("cv", outputDesc.cv.GetHex()); + obj.pushKV("cmu", outputDesc.cmu.GetHex()); + obj.pushKV("ephemeralKey", outputDesc.ephemeralKey.GetHex()); + obj.pushKV("encCiphertext", HexStr(outputDesc.encCiphertext.begin(), outputDesc.encCiphertext.end())); + obj.pushKV("outCiphertext", HexStr(outputDesc.outCiphertext.begin(), outputDesc.outCiphertext.end())); + obj.pushKV("proof", HexStr(outputDesc.zkproof.begin(), outputDesc.zkproof.end())); vdesc.push_back(obj); } return vdesc; @@ -152,99 +152,99 @@ UniValue TxShieldedOutputsToJSON(const CTransaction& tx) { void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { const uint256 txid = tx.GetHash(); - entry.push_back(Pair("txid", txid.GetHex())); - entry.push_back(Pair("overwintered", tx.fOverwintered)); - entry.push_back(Pair("version", tx.nVersion)); + entry.pushKV("txid", txid.GetHex()); + entry.pushKV("overwintered", tx.fOverwintered); + entry.pushKV("version", tx.nVersion); if (tx.fOverwintered) { - entry.push_back(Pair("versiongroupid", HexInt(tx.nVersionGroupId))); + entry.pushKV("versiongroupid", HexInt(tx.nVersionGroupId)); } - entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + entry.pushKV("locktime", (int64_t)tx.nLockTime); if (tx.fOverwintered) { - entry.push_back(Pair("expiryheight", (int64_t)tx.nExpiryHeight)); + entry.pushKV("expiryheight", (int64_t)tx.nExpiryHeight); } UniValue vin(UniValue::VARR); for (const CTxIn& txin : tx.vin) { UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) - in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); else { - in.push_back(Pair("txid", txin.prevout.hash.GetHex())); - in.push_back(Pair("vout", (int64_t)txin.prevout.n)); + in.pushKV("txid", txin.prevout.hash.GetHex()); + in.pushKV("vout", (int64_t)txin.prevout.n); UniValue o(UniValue::VOBJ); - o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true))); - o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - in.push_back(Pair("scriptSig", o)); + o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true)); + o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); + in.pushKV("scriptSig", o); // Add address and value info if spentindex enabled CSpentIndexValue spentInfo; CSpentIndexKey spentKey(txin.prevout.hash, txin.prevout.n); if (fSpentIndex && GetSpentIndex(spentKey, spentInfo)) { - in.push_back(Pair("value", ValueFromAmount(spentInfo.satoshis))); - in.push_back(Pair("valueSat", spentInfo.satoshis)); + in.pushKV("value", ValueFromAmount(spentInfo.satoshis)); + in.pushKV("valueSat", spentInfo.satoshis); CTxDestination dest = DestFromAddressHash(spentInfo.addressType, spentInfo.addressHash); if (IsValidDestination(dest)) { - in.push_back(Pair("address", EncodeDestination(dest))); + in.pushKV("address", EncodeDestination(dest)); } } } - in.push_back(Pair("sequence", (int64_t)txin.nSequence)); + in.pushKV("sequence", (int64_t)txin.nSequence); vin.push_back(in); } - entry.push_back(Pair("vin", vin)); + entry.pushKV("vin", vin); UniValue vout(UniValue::VARR); for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& txout = tx.vout[i]; UniValue out(UniValue::VOBJ); - out.push_back(Pair("value", ValueFromAmount(txout.nValue))); - out.push_back(Pair("valueZat", txout.nValue)); - out.push_back(Pair("valueSat", txout.nValue)); - out.push_back(Pair("n", (int64_t)i)); + out.pushKV("value", ValueFromAmount(txout.nValue)); + out.pushKV("valueZat", txout.nValue); + out.pushKV("valueSat", txout.nValue); + out.pushKV("n", (int64_t)i); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(txout.scriptPubKey, o, true); - out.push_back(Pair("scriptPubKey", o)); + out.pushKV("scriptPubKey", o); // Add spent information if spentindex is enabled CSpentIndexValue spentInfo; CSpentIndexKey spentKey(txid, i); if (fSpentIndex && GetSpentIndex(spentKey, spentInfo)) { - out.push_back(Pair("spentTxId", spentInfo.txid.GetHex())); - out.push_back(Pair("spentIndex", (int)spentInfo.inputIndex)); - out.push_back(Pair("spentHeight", spentInfo.blockHeight)); + out.pushKV("spentTxId", spentInfo.txid.GetHex()); + out.pushKV("spentIndex", (int)spentInfo.inputIndex); + out.pushKV("spentHeight", spentInfo.blockHeight); } vout.push_back(out); } - entry.push_back(Pair("vout", vout)); + entry.pushKV("vout", vout); UniValue vJoinSplit = TxJoinSplitToJSON(tx); - entry.push_back(Pair("vJoinSplit", vJoinSplit)); + entry.pushKV("vJoinSplit", vJoinSplit); if (tx.fOverwintered && tx.nVersion >= SAPLING_TX_VERSION) { - entry.push_back(Pair("valueBalance", ValueFromAmount(tx.valueBalance))); - entry.push_back(Pair("valueBalanceZat", tx.valueBalance)); + entry.pushKV("valueBalance", ValueFromAmount(tx.valueBalance)); + entry.pushKV("valueBalanceZat", tx.valueBalance); UniValue vspenddesc = TxShieldedSpendsToJSON(tx); - entry.push_back(Pair("vShieldedSpend", vspenddesc)); + entry.pushKV("vShieldedSpend", vspenddesc); UniValue voutputdesc = TxShieldedOutputsToJSON(tx); - entry.push_back(Pair("vShieldedOutput", voutputdesc)); + entry.pushKV("vShieldedOutput", voutputdesc); if (!(vspenddesc.empty() && voutputdesc.empty())) { - entry.push_back(Pair("bindingSig", HexStr(tx.bindingSig.begin(), tx.bindingSig.end()))); + entry.pushKV("bindingSig", HexStr(tx.bindingSig.begin(), tx.bindingSig.end())); } } if (!hashBlock.IsNull()) { - entry.push_back(Pair("blockhash", hashBlock.GetHex())); + entry.pushKV("blockhash", hashBlock.GetHex()); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; if (chainActive.Contains(pindex)) { - entry.push_back(Pair("height", pindex->nHeight)); - entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); - entry.push_back(Pair("time", pindex->GetBlockTime())); - entry.push_back(Pair("blocktime", pindex->GetBlockTime())); + entry.pushKV("height", pindex->nHeight); + entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight); + entry.pushKV("time", pindex->GetBlockTime()); + entry.pushKV("blocktime", pindex->GetBlockTime()); } else { - entry.push_back(Pair("height", -1)); - entry.push_back(Pair("confirmations", 0)); + entry.pushKV("height", -1); + entry.pushKV("confirmations", 0); } } } @@ -349,7 +349,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) bool fVerbose = false; if (params.size() > 1) - fVerbose = (params[1].get_int() != 0); + fVerbose = (params[1].getInt() != 0); LOCK(cs_main); @@ -364,7 +364,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) return strHex; UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", strHex)); + result.pushKV("hex", strHex); TxToJSON(tx, hashBlock, result); return result; } @@ -534,7 +534,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) Params().GetConsensus(), nextBlockHeight); if (params.size() > 2 && !params[2].isNull()) { - int64_t nLockTime = params[2].get_int64(); + int64_t nLockTime = params[2].getInt(); if (nLockTime < 0 || nLockTime > std::numeric_limits::max()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); rawTx.nLockTime = nLockTime; @@ -542,7 +542,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) if (params.size() > 3 && !params[3].isNull()) { if (Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_OVERWINTER)) { - int64_t nExpiryHeight = params[3].get_int64(); + int64_t nExpiryHeight = params[3].getInt(); if (nExpiryHeight < 0 || nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, expiryheight must be nonnegative and less than %d.", TX_EXPIRY_HEIGHT_THRESHOLD)); } @@ -564,19 +564,19 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) uint256 txid = ParseHashO(o, "txid"); - const UniValue& vout_v = find_value(o, "vout"); + const UniValue& vout_v = o.find_value("vout"); if (!vout_v.isNum()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); - int nOutput = vout_v.get_int(); + int nOutput = vout_v.getInt(); if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); // set the sequence number if passed in the parameters object - const UniValue& sequenceObj = find_value(o, "sequence"); + const UniValue& sequenceObj = o.find_value("sequence"); if (sequenceObj.isNum()) - nSequence = sequenceObj.get_int(); + nSequence = sequenceObj.getInt(); CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); @@ -747,7 +747,7 @@ UniValue decodescript(const UniValue& params, bool fHelp) } ScriptPubKeyToJSON(script, r, false); - r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script)))); + r.pushKV("p2sh", EncodeDestination(CScriptID(script))); return r; } @@ -755,11 +755,11 @@ UniValue decodescript(const UniValue& params, bool fHelp) static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) { UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", txin.prevout.hash.ToString())); - entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); - entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - entry.push_back(Pair("sequence", (uint64_t)txin.nSequence)); - entry.push_back(Pair("error", strMessage)); + entry.pushKV("txid", txin.prevout.hash.ToString()); + entry.pushKV("vout", (uint64_t)txin.prevout.n); + entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); + entry.pushKV("sequence", (uint64_t)txin.nSequence); + entry.pushKV("error", strMessage); vErrorsRet.push_back(entry); } @@ -904,7 +904,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) uint256 txid = ParseHashO(prevOut, "txid"); - int nOut = find_value(prevOut, "vout").get_int(); + int nOut = prevOut.find_value("vout").getInt(); if (nOut < 0) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); @@ -924,7 +924,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) coins->vout[nOut].scriptPubKey = scriptPubKey; coins->vout[nOut].nValue = 0; if (prevOut.exists("amount")) { - coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount")); + coins->vout[nOut].nValue = AmountFromValue(prevOut.find_value("amount")); } } @@ -932,7 +932,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) // given), add redeemScript to the tempKeystore so it can be signed: if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR)); - UniValue v = find_value(prevOut, "redeemScript"); + UniValue v = prevOut.find_value("redeemScript"); if (!v.isNull()) { vector rsData(ParseHexV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); @@ -1021,10 +1021,10 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) bool fComplete = vErrors.empty(); UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", EncodeHexTx(mergedTx))); - result.push_back(Pair("complete", fComplete)); + result.pushKV("hex", EncodeHexTx(mergedTx)); + result.pushKV("complete", fComplete); if (!vErrors.empty()) { - result.push_back(Pair("errors", vErrors)); + result.pushKV("errors", vErrors); } return result; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 3006616fc..c6d48f80d 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -96,7 +96,7 @@ void RPCTypeCheckObj(const UniValue& o, { for (const std::pair& t : typesExpected) { - const UniValue& v = find_value(o, t.first); + const UniValue& v = o.find_value(t.first); if (!fAllowNull && v.isNull()) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); @@ -142,9 +142,9 @@ uint256 ParseHashV(const UniValue& v, string strName) result.SetHex(strHex); return result; } -uint256 ParseHashO(const UniValue& o, string strKey) +uint256 ParseHashO(const UniValue& o, std::string strKey) { - return ParseHashV(find_value(o, strKey), strKey); + return ParseHashV(o.find_value(strKey), strKey); } vector ParseHexV(const UniValue& v, string strName) { @@ -155,9 +155,9 @@ vector ParseHexV(const UniValue& v, string strName) throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); return ParseHex(strHex); } -vector ParseHexO(const UniValue& o, string strKey) +std::vector ParseHexO(const UniValue& o, std::string strKey) { - return ParseHexV(find_value(o, strKey), strKey); + return ParseHexV(o.find_value(strKey), strKey); } /** @@ -372,10 +372,10 @@ void JSONRequest::parse(const UniValue& valRequest) const UniValue& request = valRequest.get_obj(); // Parse id now so errors from here on will have the id - id = find_value(request, "id"); + id = request.find_value("id"); // Parse method - UniValue valMethod = find_value(request, "method"); + UniValue valMethod = request.find_value("method"); if (valMethod.isNull()) throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); if (!valMethod.isStr()) @@ -385,7 +385,7 @@ void JSONRequest::parse(const UniValue& valRequest) LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); // Parse params - UniValue valParams = find_value(request, "params"); + UniValue valParams = request.find_value("params"); if (valParams.isArray()) params = valParams.get_array(); else if (valParams.isNull()) diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 49031fa82..82cbf1c87 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -92,15 +92,15 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) std::string exp_base58string = test[0].get_str(); std::vector exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); - bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "chain").get_str() == "testnet"; + bool isPrivkey = metadata.find_value("isPrivkey").get_bool(); + bool isTestnet = metadata.find_value("chain").get_str() == "testnet"; if (isTestnet) { SelectParams(CBaseChainParams::TESTNET); } else { SelectParams(CBaseChainParams::MAIN); } if (isPrivkey) { - bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + bool isCompressed = metadata.find_value("isCompressed").get_bool(); // Must be valid private key privkey = DecodeSecret(exp_base58string); BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest); @@ -140,15 +140,15 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) std::string exp_base58string = test[0].get_str(); std::vector exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); - bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "chain").get_str() == "testnet"; + bool isPrivkey = metadata.find_value("isPrivkey").get_bool(); + bool isTestnet = metadata.find_value("chain").get_str() == "testnet"; if (isTestnet) { SelectParams(CBaseChainParams::TESTNET); } else { SelectParams(CBaseChainParams::MAIN); } if (isPrivkey) { - bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + bool isCompressed = metadata.find_value("isCompressed").get_bool(); CKey key; key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); assert(key.IsValid()); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 7a8e73069..8e555883e 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -52,7 +52,7 @@ UniValue CallRPC(string args) return result; } catch (const UniValue& objError) { - throw runtime_error(find_value(objError, "message").get_str()); + throw runtime_error(objError.find_value("message").get_str()); } } @@ -96,8 +96,8 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), runtime_error); string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000"; BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx)); - BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1); - BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0); + BOOST_CHECK_EQUAL(r.get_obj().find_value("version").getInt(), 1); + BOOST_CHECK_EQUAL(r.get_obj().find_value("locktime").getInt(), 0); BOOST_CHECK_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx+" extra"), runtime_error); BOOST_CHECK_THROW(CallRPC("signrawtransaction"), runtime_error); @@ -131,9 +131,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign) string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"[]"); - BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); + BOOST_CHECK(r.get_obj().find_value("complete").get_bool() == false); r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"["+privkey1+","+privkey2+"]"); - BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); + BOOST_CHECK(r.get_obj().find_value("complete").get_bool() == true); } BOOST_AUTO_TEST_CASE(rpc_format_monetary_values) @@ -171,10 +171,10 @@ BOOST_AUTO_TEST_CASE(rpc_format_monetary_values) BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000000).write(), "0.00000001"); } -static UniValue ValueFromString(const std::string &str) +static UniValue ValueFromString(const std::string& str) noexcept { UniValue value; - BOOST_CHECK(value.setNumStr(str)); + value.setNumStr(str); return value; } @@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); UniValue ar = r.get_array(); UniValue o1 = ar[0].get_obj(); - UniValue adr = find_value(o1, "address"); + UniValue adr = o1.find_value("address"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.255"); BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0 remove")));; BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); @@ -253,10 +253,10 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); - UniValue banned_until = find_value(o1, "banned_until"); + adr = o1.find_value("address"); + UniValue banned_until = o1.find_value("banned_until"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0"); - BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check + BOOST_CHECK_EQUAL(banned_until.getInt(), 1607731200); // absolute time check BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); @@ -264,12 +264,12 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); - banned_until = find_value(o1, "banned_until"); + adr = o1.find_value("address"); + banned_until = o1.find_value("banned_until"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0"); int64_t now = GetTime(); - BOOST_CHECK(banned_until.get_int64() > now); - BOOST_CHECK(banned_until.get_int64()-now <= 200); + BOOST_CHECK(banned_until.getInt() > now); + BOOST_CHECK(banned_until.getInt()-now <= 200); // must throw an exception because 127.0.0.1 is in already banned subnet range BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.1 add")), runtime_error); @@ -295,7 +295,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); + adr = o1.find_value("address"); BOOST_CHECK_EQUAL(adr.get_str(), "fe80::202:b3ff:fe1e:8329/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); @@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); + adr = o1.find_value("address"); BOOST_CHECK_EQUAL(adr.get_str(), "2001:db8::/ffff:fffc:0:0:0:0:0:0"); BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); @@ -311,7 +311,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); - adr = find_value(o1, "address"); + adr = o1.find_value("address"); BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); } @@ -333,11 +333,11 @@ BOOST_AUTO_TEST_CASE(rpc_raw_create_overwinter_v3) "{\"tmHU5HLMu3yS8eoNvbrU1NWeJaGf6jxehru\":11}"); std::string rawhex = r.get_str(); BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ") + rawhex)); - BOOST_CHECK_EQUAL(find_value(r.get_obj(), "overwintered").get_bool(), true); - BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 3); - BOOST_CHECK_EQUAL(find_value(r.get_obj(), "expiryheight").get_int(), 21); + BOOST_CHECK_EQUAL(r.get_obj().find_value("overwintered").get_bool(), true); + BOOST_CHECK_EQUAL(r.get_obj().find_value("version").getInt(), 3); + BOOST_CHECK_EQUAL(r.get_obj().find_value("expiryheight").getInt(), 21); BOOST_CHECK_EQUAL( - ParseHexToUInt32(find_value(r.get_obj(), "versiongroupid").get_str()), + ParseHexToUInt32(r.get_obj().find_value("versiongroupid").get_str()), OVERWINTER_VERSION_GROUP_ID); // Sanity check we can deserialize the raw hex @@ -409,7 +409,7 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer) CheckRPCThrows("getaddressutxos {\"addressesmisspell\":[]}", "Addresses is expected to be an array"); CheckRPCThrows("getaddressutxos {\"addresses\":[],\"chainInfo\":1}", - "JSON value is not a boolean as expected"); + "JSON value of type number is not of expected type bool"); BOOST_CHECK_NO_THROW(CallRPC("getaddressdeltas {\"addresses\":[]}")); CheckRPCThrows("getaddressdeltas {\"addresses\":[],\"start\":0,\"end\":0,\"chainInfo\":true}", @@ -450,7 +450,7 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer) // Unfortunately, an unknown or mangled key is ignored BOOST_CHECK_NO_THROW(CallRPC("getblockhashes 1477641360 1477641360 {\"AAAnoOrphans\":true,\"logicalTimes\":true}")); CheckRPCThrows("getblockhashes 1477641360 1477641360 {\"noOrphans\":true,\"logicalTimes\":1}", - "JSON value is not a boolean as expected"); + "JSON value of type number is not of expected type bool"); CheckRPCThrows("getblockhashes 1477641360 1477641360 {\"noOrphans\":True,\"logicalTimes\":false}", "Error parsing JSON:{\"noOrphans\":True,\"logicalTimes\":false}"); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 8f1ed9e4a..8e5fa52a6 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -289,9 +289,9 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) // deserialize test data raw_tx = test[0].get_str(); raw_script = test[1].get_str(); - nIn = test[2].get_int(); - nHashType = test[3].get_int(); - consensusBranchId = test[4].get_int(); + nIn = test[2].getInt(); + nHashType = test[3].getInt(); + consensusBranchId = test[4].getInt(); sigHashHex = test[5].get_str(); uint256 sh; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 14fd3515b..4ffe12133 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -138,7 +138,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) break; } - mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str()); + mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].getInt())] = ParseScript(vinput[2].get_str()); } if (!fValid) { @@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) break; } - mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str()); + mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].getInt())] = ParseScript(vinput[2].get_str()); } if (!fValid) { diff --git a/src/test/univalue_tests.cpp b/src/test/univalue_tests.cpp deleted file mode 100644 index 51db25e8b..000000000 --- a/src/test/univalue_tests.cpp +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2014 BitPay, Inc. -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#include -#include -#include -#include -#include -#include "test/test_bitcoin.h" - -#include - -using namespace std; - -BOOST_FIXTURE_TEST_SUITE(univalue_tests, BasicTestingSetup) - -BOOST_AUTO_TEST_CASE(univalue_constructor) -{ - UniValue v1; - BOOST_CHECK(v1.isNull()); - - UniValue v2(UniValue::VSTR); - BOOST_CHECK(v2.isStr()); - - UniValue v3(UniValue::VSTR, "foo"); - BOOST_CHECK(v3.isStr()); - BOOST_CHECK_EQUAL(v3.getValStr(), "foo"); - - UniValue numTest; - BOOST_CHECK(numTest.setNumStr("82")); - BOOST_CHECK(numTest.isNum()); - BOOST_CHECK_EQUAL(numTest.getValStr(), "82"); - - uint64_t vu64 = 82; - UniValue v4(vu64); - BOOST_CHECK(v4.isNum()); - BOOST_CHECK_EQUAL(v4.getValStr(), "82"); - - int64_t vi64 = -82; - UniValue v5(vi64); - BOOST_CHECK(v5.isNum()); - BOOST_CHECK_EQUAL(v5.getValStr(), "-82"); - - int vi = -688; - UniValue v6(vi); - BOOST_CHECK(v6.isNum()); - BOOST_CHECK_EQUAL(v6.getValStr(), "-688"); - - double vd = -7.21; - UniValue v7(vd); - BOOST_CHECK(v7.isNum()); - BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); - - string vs("yawn"); - UniValue v8(vs); - BOOST_CHECK(v8.isStr()); - BOOST_CHECK_EQUAL(v8.getValStr(), "yawn"); - - const char *vcs = "zappa"; - UniValue v9(vcs); - BOOST_CHECK(v9.isStr()); - BOOST_CHECK_EQUAL(v9.getValStr(), "zappa"); -} - -BOOST_AUTO_TEST_CASE(univalue_typecheck) -{ - UniValue v1; - BOOST_CHECK(v1.setNumStr("1")); - BOOST_CHECK(v1.isNum()); - BOOST_CHECK_THROW(v1.get_bool(), runtime_error); - - UniValue v2; - BOOST_CHECK(v2.setBool(true)); - BOOST_CHECK_EQUAL(v2.get_bool(), true); - BOOST_CHECK_THROW(v2.get_int(), runtime_error); - - UniValue v3; - BOOST_CHECK(v3.setNumStr("32482348723847471234")); - BOOST_CHECK_THROW(v3.get_int64(), runtime_error); - BOOST_CHECK(v3.setNumStr("1000")); - BOOST_CHECK_EQUAL(v3.get_int64(), 1000); - - UniValue v4; - BOOST_CHECK(v4.setNumStr("2147483648")); - BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648); - BOOST_CHECK_THROW(v4.get_int(), runtime_error); - BOOST_CHECK(v4.setNumStr("1000")); - BOOST_CHECK_EQUAL(v4.get_int(), 1000); - BOOST_CHECK_THROW(v4.get_str(), runtime_error); - BOOST_CHECK_EQUAL(v4.get_real(), 1000); - BOOST_CHECK_THROW(v4.get_array(), runtime_error); - BOOST_CHECK_THROW(v4.getKeys(), runtime_error); - BOOST_CHECK_THROW(v4.getValues(), runtime_error); - BOOST_CHECK_THROW(v4.get_obj(), runtime_error); - - UniValue v5; - BOOST_CHECK(v5.read("[true, 10]")); - BOOST_CHECK_NO_THROW(v5.get_array()); - std::vector vals = v5.getValues(); - BOOST_CHECK_THROW(vals[0].get_int(), runtime_error); - BOOST_CHECK_EQUAL(vals[0].get_bool(), true); - - BOOST_CHECK_EQUAL(vals[1].get_int(), 10); - BOOST_CHECK_THROW(vals[1].get_bool(), runtime_error); -} - -BOOST_AUTO_TEST_CASE(univalue_set) -{ - UniValue v(UniValue::VSTR, "foo"); - v.clear(); - BOOST_CHECK(v.isNull()); - BOOST_CHECK_EQUAL(v.getValStr(), ""); - - BOOST_CHECK(v.setObject()); - BOOST_CHECK(v.isObject()); - BOOST_CHECK_EQUAL(v.size(), 0); - BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ); - BOOST_CHECK(v.empty()); - - BOOST_CHECK(v.setArray()); - BOOST_CHECK(v.isArray()); - BOOST_CHECK_EQUAL(v.size(), 0); - - BOOST_CHECK(v.setStr("zum")); - BOOST_CHECK(v.isStr()); - BOOST_CHECK_EQUAL(v.getValStr(), "zum"); - - BOOST_CHECK(v.setFloat(-1.01)); - BOOST_CHECK(v.isNum()); - BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); - - BOOST_CHECK(v.setInt((int)1023)); - BOOST_CHECK(v.isNum()); - BOOST_CHECK_EQUAL(v.getValStr(), "1023"); - - BOOST_CHECK(v.setInt((int64_t)-1023LL)); - BOOST_CHECK(v.isNum()); - BOOST_CHECK_EQUAL(v.getValStr(), "-1023"); - - BOOST_CHECK(v.setInt((uint64_t)1023ULL)); - BOOST_CHECK(v.isNum()); - BOOST_CHECK_EQUAL(v.getValStr(), "1023"); - - BOOST_CHECK(v.setNumStr("-688")); - BOOST_CHECK(v.isNum()); - BOOST_CHECK_EQUAL(v.getValStr(), "-688"); - - BOOST_CHECK(v.setBool(false)); - BOOST_CHECK_EQUAL(v.isBool(), true); - BOOST_CHECK_EQUAL(v.isTrue(), false); - BOOST_CHECK_EQUAL(v.isFalse(), true); - BOOST_CHECK_EQUAL(v.getBool(), false); - - BOOST_CHECK(v.setBool(true)); - BOOST_CHECK_EQUAL(v.isBool(), true); - BOOST_CHECK_EQUAL(v.isTrue(), true); - BOOST_CHECK_EQUAL(v.isFalse(), false); - BOOST_CHECK_EQUAL(v.getBool(), true); - - BOOST_CHECK(!v.setNumStr("zombocom")); - - BOOST_CHECK(v.setNull()); - BOOST_CHECK(v.isNull()); -} - -BOOST_AUTO_TEST_CASE(univalue_array) -{ - UniValue arr(UniValue::VARR); - - UniValue v((int64_t)1023LL); - BOOST_CHECK(arr.push_back(v)); - - string vStr("zippy"); - BOOST_CHECK(arr.push_back(vStr)); - - const char *s = "pippy"; - BOOST_CHECK(arr.push_back(s)); - - vector vec; - v.setStr("boing"); - vec.push_back(v); - - v.setStr("going"); - vec.push_back(v); - - BOOST_CHECK(arr.push_backV(vec)); - - BOOST_CHECK_EQUAL(arr.empty(), false); - BOOST_CHECK_EQUAL(arr.size(), 5); - - BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023"); - BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy"); - BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy"); - BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing"); - BOOST_CHECK_EQUAL(arr[4].getValStr(), "going"); - - BOOST_CHECK_EQUAL(arr[999].getValStr(), ""); - - arr.clear(); - BOOST_CHECK(arr.empty()); - BOOST_CHECK_EQUAL(arr.size(), 0); -} - -BOOST_AUTO_TEST_CASE(univalue_object) -{ - UniValue obj(UniValue::VOBJ); - string strKey, strVal; - UniValue v; - - strKey = "age"; - v.setInt(100); - BOOST_CHECK(obj.pushKV(strKey, v)); - - strKey = "first"; - strVal = "John"; - BOOST_CHECK(obj.pushKV(strKey, strVal)); - - strKey = "last"; - const char *cVal = "Smith"; - BOOST_CHECK(obj.pushKV(strKey, cVal)); - - strKey = "distance"; - BOOST_CHECK(obj.pushKV(strKey, (int64_t) 25)); - - strKey = "time"; - BOOST_CHECK(obj.pushKV(strKey, (uint64_t) 3600)); - - strKey = "calories"; - BOOST_CHECK(obj.pushKV(strKey, (int) 12)); - - strKey = "temperature"; - BOOST_CHECK(obj.pushKV(strKey, (double) 90.012)); - - UniValue obj2(UniValue::VOBJ); - BOOST_CHECK(obj2.pushKV("cat1", 9000)); - BOOST_CHECK(obj2.pushKV("cat2", 12345)); - - BOOST_CHECK(obj.pushKVs(obj2)); - - BOOST_CHECK_EQUAL(obj.empty(), false); - BOOST_CHECK_EQUAL(obj.size(), 9); - - BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100"); - BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John"); - BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith"); - BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25"); - BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600"); - BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12"); - BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012"); - BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000"); - BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345"); - - BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), ""); - - BOOST_CHECK(obj.exists("age")); - BOOST_CHECK(obj.exists("first")); - BOOST_CHECK(obj.exists("last")); - BOOST_CHECK(obj.exists("distance")); - BOOST_CHECK(obj.exists("time")); - BOOST_CHECK(obj.exists("calories")); - BOOST_CHECK(obj.exists("temperature")); - BOOST_CHECK(obj.exists("cat1")); - BOOST_CHECK(obj.exists("cat2")); - - BOOST_CHECK(!obj.exists("nyuknyuknyuk")); - - map objTypes; - objTypes["age"] = UniValue::VNUM; - objTypes["first"] = UniValue::VSTR; - objTypes["last"] = UniValue::VSTR; - objTypes["distance"] = UniValue::VNUM; - objTypes["time"] = UniValue::VNUM; - objTypes["calories"] = UniValue::VNUM; - objTypes["temperature"] = UniValue::VNUM; - objTypes["cat1"] = UniValue::VNUM; - objTypes["cat2"] = UniValue::VNUM; - BOOST_CHECK(obj.checkObject(objTypes)); - - objTypes["cat2"] = UniValue::VSTR; - BOOST_CHECK(!obj.checkObject(objTypes)); - - obj.clear(); - BOOST_CHECK(obj.empty()); - BOOST_CHECK_EQUAL(obj.size(), 0); -} - -static const char *json1 = -"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]"; - -BOOST_AUTO_TEST_CASE(univalue_readwrite) -{ - UniValue v; - BOOST_CHECK(v.read(json1)); - - string strJson1(json1); - BOOST_CHECK(v.read(strJson1)); - - BOOST_CHECK(v.isArray()); - BOOST_CHECK_EQUAL(v.size(), 2); - - BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000"); - - UniValue obj = v[1]; - BOOST_CHECK(obj.isObject()); - BOOST_CHECK_EQUAL(obj.size(), 3); - - BOOST_CHECK(obj["key1"].isStr()); - std::string correctValue("str"); - correctValue.push_back('\0'); - BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue); - BOOST_CHECK(obj["key2"].isNum()); - BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800"); - BOOST_CHECK(obj["key3"].isObject()); - - BOOST_CHECK_EQUAL(strJson1, v.write()); -} - -BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/univalue/.travis.yml b/src/univalue/.travis.yml deleted file mode 100644 index 132743d34..000000000 --- a/src/univalue/.travis.yml +++ /dev/null @@ -1,52 +0,0 @@ -language: cpp - -compiler: - - clang - - gcc - -os: - - linux - - osx - -sudo: false - -env: - global: - - MAKEJOBS=-j3 - - RUN_TESTS=true - - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out - -cache: - apt: true - -addons: - apt: - packages: - - pkg-config - -before_script: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew uninstall libtool; brew install libtool; fi - - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi - - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh - -script: - - if [ -n "$UNIVALUE_CONFIG" ]; then unset CC; unset CXX; fi - - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST - - UNIVALUE_CONFIG_ALL="--prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" - - ./configure --cache-file=config.cache $UNIVALUE_CONFIG_ALL $UNIVALUE_CONFIG || ( cat config.log && false) - - make -s $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL ; false ) - - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib - - if [ "$RUN_TESTS" = "true" ]; then make $MAKEJOBS distcheck; fi - -matrix: - fast_finish: true - include: - - os: linux - compiler: gcc - env: UNIVALUE_CONFIG=--host=x86_64-w64-mingw32 RUN_TESTS=false - addons: - apt: - packages: - - g++-mingw-w64-x86-64 - - gcc-mingw-w64-x86-64 - - binutils-mingw-w64-x86-64 diff --git a/src/univalue/COPYING b/src/univalue/COPYING deleted file mode 100644 index 1fb429f35..000000000 --- a/src/univalue/COPYING +++ /dev/null @@ -1,19 +0,0 @@ - -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. - diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am deleted file mode 100644 index 532fa1949..000000000 --- a/src/univalue/Makefile.am +++ /dev/null @@ -1,109 +0,0 @@ -ACLOCAL_AMFLAGS = -I build-aux/m4 -.PHONY: gen -.INTERMEDIATE: $(GENBIN) - -include_HEADERS = include/univalue.h -noinst_HEADERS = lib/univalue_escapes.h lib/univalue_utffilter.h - -lib_LTLIBRARIES = libunivalue.la - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = pc/libunivalue.pc - -libunivalue_la_SOURCES = \ - lib/univalue.cpp \ - lib/univalue_read.cpp \ - lib/univalue_write.cpp - -libunivalue_la_LDFLAGS = \ - -version-info $(LIBUNIVALUE_CURRENT):$(LIBUNIVALUE_REVISION):$(LIBUNIVALUE_AGE) \ - -no-undefined -libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include - -TESTS = test/unitester test/no_nul - -GENBIN = gen/gen$(BUILD_EXEEXT) -GEN_SRCS = gen/gen.cpp - -$(GENBIN): $(GEN_SRCS) - @echo Building $@ - $(AM_V_at)c++ -I$(top_srcdir)/include -o $@ $< - -gen: lib/univalue_escapes.h $(GENBIN) - @echo Updating $< - $(AM_V_at)$(GENBIN) > lib/univalue_escapes.h - -noinst_PROGRAMS = $(TESTS) test/test_json - -TEST_DATA_DIR=test - -test_unitester_SOURCES = test/unitester.cpp -test_unitester_LDADD = libunivalue.la -test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\" -test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) - -test_test_json_SOURCES = test/test_json.cpp -test_test_json_LDADD = libunivalue.la -test_test_json_CXXFLAGS = -I$(top_srcdir)/include -test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) - -test_no_nul_SOURCES = test/no_nul.cpp -test_no_nul_LDADD = libunivalue.la -test_no_nul_CXXFLAGS = -I$(top_srcdir)/include -test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) - -TEST_FILES = \ - $(TEST_DATA_DIR)/fail10.json \ - $(TEST_DATA_DIR)/fail11.json \ - $(TEST_DATA_DIR)/fail12.json \ - $(TEST_DATA_DIR)/fail13.json \ - $(TEST_DATA_DIR)/fail14.json \ - $(TEST_DATA_DIR)/fail15.json \ - $(TEST_DATA_DIR)/fail16.json \ - $(TEST_DATA_DIR)/fail17.json \ - $(TEST_DATA_DIR)/fail18.json \ - $(TEST_DATA_DIR)/fail19.json \ - $(TEST_DATA_DIR)/fail1.json \ - $(TEST_DATA_DIR)/fail20.json \ - $(TEST_DATA_DIR)/fail21.json \ - $(TEST_DATA_DIR)/fail22.json \ - $(TEST_DATA_DIR)/fail23.json \ - $(TEST_DATA_DIR)/fail24.json \ - $(TEST_DATA_DIR)/fail25.json \ - $(TEST_DATA_DIR)/fail26.json \ - $(TEST_DATA_DIR)/fail27.json \ - $(TEST_DATA_DIR)/fail28.json \ - $(TEST_DATA_DIR)/fail29.json \ - $(TEST_DATA_DIR)/fail2.json \ - $(TEST_DATA_DIR)/fail30.json \ - $(TEST_DATA_DIR)/fail31.json \ - $(TEST_DATA_DIR)/fail32.json \ - $(TEST_DATA_DIR)/fail33.json \ - $(TEST_DATA_DIR)/fail34.json \ - $(TEST_DATA_DIR)/fail35.json \ - $(TEST_DATA_DIR)/fail36.json \ - $(TEST_DATA_DIR)/fail37.json \ - $(TEST_DATA_DIR)/fail38.json \ - $(TEST_DATA_DIR)/fail39.json \ - $(TEST_DATA_DIR)/fail40.json \ - $(TEST_DATA_DIR)/fail41.json \ - $(TEST_DATA_DIR)/fail42.json \ - $(TEST_DATA_DIR)/fail3.json \ - $(TEST_DATA_DIR)/fail4.json \ - $(TEST_DATA_DIR)/fail5.json \ - $(TEST_DATA_DIR)/fail6.json \ - $(TEST_DATA_DIR)/fail7.json \ - $(TEST_DATA_DIR)/fail8.json \ - $(TEST_DATA_DIR)/fail9.json \ - $(TEST_DATA_DIR)/pass1.json \ - $(TEST_DATA_DIR)/pass2.json \ - $(TEST_DATA_DIR)/pass3.json \ - $(TEST_DATA_DIR)/round1.json \ - $(TEST_DATA_DIR)/round2.json \ - $(TEST_DATA_DIR)/round3.json \ - $(TEST_DATA_DIR)/round4.json \ - $(TEST_DATA_DIR)/round5.json \ - $(TEST_DATA_DIR)/round6.json \ - $(TEST_DATA_DIR)/round7.json - -EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS) diff --git a/src/univalue/README b/src/univalue/README deleted file mode 100644 index 48167b083..000000000 --- a/src/univalue/README +++ /dev/null @@ -1,7 +0,0 @@ - - UniValue - -A universal value object, with JSON encoding (output) and decoding (input). - -Built as a single dynamic RAII C++ object class, and no templates. - diff --git a/src/univalue/TODO b/src/univalue/TODO deleted file mode 100644 index 5530048e9..000000000 --- a/src/univalue/TODO +++ /dev/null @@ -1,10 +0,0 @@ - -Rearrange tree for easier 'git subtree' style use - -Move towards C++11 etc. - -Namespace support - must come up with useful shorthand, avoiding -long Univalue::Univalue::Univalue usages forced upon library users. - -Improve test suite - diff --git a/src/univalue/autogen.sh b/src/univalue/autogen.sh deleted file mode 100755 index 4b38721fa..000000000 --- a/src/univalue/autogen.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e -srcdir="$(dirname $0)" -cd "$srcdir" -if [ -z ${LIBTOOLIZE} ] && GLIBTOOLIZE="`which glibtoolize 2>/dev/null`"; then - LIBTOOLIZE="${GLIBTOOLIZE}" - export LIBTOOLIZE -fi -autoreconf --install --force diff --git a/src/univalue/build-aux/m4/.gitignore b/src/univalue/build-aux/m4/.gitignore deleted file mode 100644 index f06368652..000000000 --- a/src/univalue/build-aux/m4/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.m4 diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac deleted file mode 100644 index 8298332ac..000000000 --- a/src/univalue/configure.ac +++ /dev/null @@ -1,69 +0,0 @@ -m4_define([libunivalue_major_version], [1]) -m4_define([libunivalue_minor_version], [1]) -m4_define([libunivalue_micro_version], [3]) -m4_define([libunivalue_interface_age], [3]) -# If you need a modifier for the version number. -# Normally empty, but can be used to make "fixup" releases. -m4_define([libunivalue_extraversion], []) - -dnl libtool versioning from libunivalue -m4_define([libunivalue_current], [m4_eval(100 * libunivalue_minor_version + libunivalue_micro_version - libunivalue_interface_age)]) -m4_define([libunivalue_binary_age], [m4_eval(100 * libunivalue_minor_version + libunivalue_micro_version)]) -m4_define([libunivalue_revision], [libunivalue_interface_age]) -m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_interface_age)]) -m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()]) - - -AC_INIT([univalue], [1.0.3], - [http://github.com/jgarzik/univalue/]) - -dnl make the compilation flags quiet unless V=1 is used -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) - -AC_PREREQ(2.60) -AC_CONFIG_SRCDIR([lib/univalue.cpp]) -AC_CONFIG_AUX_DIR([build-aux]) -AC_CONFIG_MACRO_DIR([build-aux/m4]) -AC_CONFIG_HEADERS([univalue-config.h]) -AM_INIT_AUTOMAKE([subdir-objects foreign]) - -LIBUNIVALUE_MAJOR_VERSION=libunivalue_major_version -LIBUNIVALUE_MINOR_VERSION=libunivalue_minor_version -LIBUNIVALUE_MICRO_VERSION=libunivalue_micro_version -LIBUNIVALUE_INTERFACE_AGE=libunivalue_interface_age - -# ABI version -# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html -LIBUNIVALUE_CURRENT=libunivalue_current -LIBUNIVALUE_REVISION=libunivalue_revision -LIBUNIVALUE_AGE=libunivalue_age - -AC_SUBST(LIBUNIVALUE_CURRENT) -AC_SUBST(LIBUNIVALUE_REVISION) -AC_SUBST(LIBUNIVALUE_AGE) - -LT_INIT -LT_LANG([C++]) - -case $host in - *mingw*) - LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static" - ;; -esac - -BUILD_EXEEXT= -case $build in - *mingw*) - BUILD_EXEEXT=".exe" - ;; -esac - -AC_CONFIG_FILES([ - Makefile - pc/libunivalue.pc - pc/libunivalue-uninstalled.pc]) - -AC_SUBST(LIBTOOL_APP_LDFLAGS) -AC_SUBST(BUILD_EXEEXT) -AC_OUTPUT - diff --git a/src/univalue/configure~ b/src/univalue/configure~ deleted file mode 100755 index 6d1ba687d..000000000 --- a/src/univalue/configure~ +++ /dev/null @@ -1,19224 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for univalue 1.0.3. -# -# Report bugs to . -# -# -# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, -# Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -as_nop=: -if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 -then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else $as_nop - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - - -# Reset variables that may have inherited troublesome values from -# the environment. - -# IFS needs to be set, to space, tab, and newline, in precisely that order. -# (If _AS_PATH_WALK were called with IFS unset, it would have the -# side effect of setting IFS to empty, thus disabling word splitting.) -# Quoting is to prevent editors from complaining about space-tab. -as_nl=' -' -export as_nl -IFS=" "" $as_nl" - -PS1='$ ' -PS2='> ' -PS4='+ ' - -# Ensure predictable behavior from utilities with locale-dependent output. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# We cannot yet rely on "unset" to work, but we need these variables -# to be unset--not just set to an empty or harmless value--now, to -# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct -# also avoids known problems related to "unset" and subshell syntax -# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). -for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH -do eval test \${$as_var+y} \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done - -# Ensure that fds 0, 1, and 2 are open. -if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi -if (exec 3>&2) ; then :; else exec 2>/dev/null; fi - -# The user is always right. -if ${PATH_SEPARATOR+false} :; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - test -r "$as_dir$0" && as_myself=$as_dir$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="as_nop=: -if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 -then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else \$as_nop - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ) -then : - -else \$as_nop - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -blah=\$(echo \$(echo blah)) -test x\"\$blah\" = xblah || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 - - test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( - ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' - ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO - ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO - PATH=/empty FPATH=/empty; export PATH FPATH - test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ - || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null -then : - as_have_required=yes -else $as_nop - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null -then : - -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null -then : - CONFIG_SHELL=$as_shell as_have_required=yes - if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null -then : - break 2 -fi -fi - done;; - esac - as_found=false -done -IFS=$as_save_IFS -if $as_found -then : - -else $as_nop - if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null -then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi -fi - - - if test "x$CONFIG_SHELL" != x -then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno -then : - printf "%s\n" "$0: This script requires a shell more modern than all" - printf "%s\n" "$0: the shells that I found on your system." - if test ${ZSH_VERSION+y} ; then - printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" - printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." - else - printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and -$0: http://github.com/jgarzik/univalue/ about your system, -$0: including any error possibly output before this -$0: message. Then install a modern shell, or manually run -$0: the script under such a shell if you do have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit -# as_fn_nop -# --------- -# Do nothing but, unlike ":", preserve the value of $?. -as_fn_nop () -{ - return $? -} -as_nop=as_fn_nop - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null -then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else $as_nop - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null -then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else $as_nop - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - -# as_fn_nop -# --------- -# Do nothing but, unlike ":", preserve the value of $?. -as_fn_nop () -{ - return $? -} -as_nop=as_fn_nop - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - printf "%s\n" "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - - -# Determine whether it's possible to make 'echo' print without a newline. -# These variables are no longer used directly by Autoconf, but are AC_SUBSTed -# for compatibility with existing Makefiles. -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -# For backward compatibility with old third-party macros, we provide -# the shell variables $as_echo and $as_echo_n. New code should use -# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. -as_echo='printf %s\n' -as_echo_n='printf %s' - - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - -SHELL=${CONFIG_SHELL-/bin/sh} - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME='univalue' -PACKAGE_TARNAME='univalue' -PACKAGE_VERSION='1.0.3' -PACKAGE_STRING='univalue 1.0.3' -PACKAGE_BUGREPORT='http://github.com/jgarzik/univalue/' -PACKAGE_URL='' - -ac_unique_file="lib/univalue.cpp" -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_STDIO_H -# include -#endif -#ifdef HAVE_STDLIB_H -# include -#endif -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_header_c_list= -ac_subst_vars='am__EXEEXT_FALSE -am__EXEEXT_TRUE -LTLIBOBJS -LIBOBJS -BUILD_EXEEXT -LIBTOOL_APP_LDFLAGS -CXXCPP -am__fastdepCXX_FALSE -am__fastdepCXX_TRUE -CXXDEPMODE -ac_ct_CXX -CXXFLAGS -CXX -LT_SYS_LIBRARY_PATH -OTOOL64 -OTOOL -LIPO -NMEDIT -DSYMUTIL -MANIFEST_TOOL -RANLIB -ac_ct_AR -AR -DLLTOOL -OBJDUMP -LN_S -NM -ac_ct_DUMPBIN -DUMPBIN -LD -FGREP -EGREP -GREP -SED -am__fastdepCC_FALSE -am__fastdepCC_TRUE -CCDEPMODE -am__nodep -AMDEPBACKSLASH -AMDEP_FALSE -AMDEP_TRUE -am__include -DEPDIR -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -LIBTOOL -LIBUNIVALUE_AGE -LIBUNIVALUE_REVISION -LIBUNIVALUE_CURRENT -CSCOPE -ETAGS -CTAGS -am__untar -am__tar -AMTAR -am__leading_dot -SET_MAKE -AWK -mkdir_p -MKDIR_P -INSTALL_STRIP_PROGRAM -STRIP -install_sh -MAKEINFO -AUTOHEADER -AUTOMAKE -AUTOCONF -ACLOCAL -VERSION -PACKAGE -CYGPATH_W -am__isrc -INSTALL_DATA -INSTALL_SCRIPT -INSTALL_PROGRAM -AM_BACKSLASH -AM_DEFAULT_VERBOSITY -AM_DEFAULT_V -AM_V -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -runstatedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL -am__quote' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -enable_silent_rules -enable_shared -enable_static -with_pic -enable_fast_install -with_aix_soname -enable_dependency_tracking -with_gnu_ld -with_sysroot -enable_libtool_lock -' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -LT_SYS_LIBRARY_PATH -CXX -CXXFLAGS -CCC -CXXCPP' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: \`$ac_useropt'" - ac_useropt_orig=$ac_useropt - ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: \`$ac_useropt'" - ac_useropt_orig=$ac_useropt - ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: \`$ac_useropt'" - ac_useropt_orig=$ac_useropt - ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: \`$ac_useropt'" - ac_useropt_orig=$ac_useropt - ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures univalue 1.0.3 to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/univalue] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF - -Program names: - --program-prefix=PREFIX prepend PREFIX to installed program names - --program-suffix=SUFFIX append SUFFIX to installed program names - --program-transform-name=PROGRAM run sed PROGRAM on installed program names - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of univalue 1.0.3:";; - esac - cat <<\_ACEOF - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-silent-rules less verbose build output (undo: "make V=1") - --disable-silent-rules verbose build output (undo: "make V=0") - --enable-shared[=PKGS] build shared libraries [default=yes] - --enable-static[=PKGS] build static libraries [default=yes] - --enable-fast-install[=PKGS] - optimize for fast installation [default=yes] - --enable-dependency-tracking - do not reject slow dependency extractors - --disable-dependency-tracking - speeds up one-time build - --disable-libtool-lock avoid locking (might break parallel builds) - -Optional Packages: - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use - both] - --with-aix-soname=aix|svr4|both - shared library versioning (aka "SONAME") variant to - provide on AIX, [default=aix]. - --with-gnu-ld assume the C compiler uses GNU ld [default=no] - --with-sysroot[=DIR] Search for dependent libraries within DIR (or the - compiler's sysroot if not specified). - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - LT_SYS_LIBRARY_PATH - User-defined run-time library search path. - CXX C++ compiler command - CXXFLAGS C++ compiler flags - CXXCPP C++ preprocessor - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to . -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for configure.gnu first; this name is used for a wrapper for - # Metaconfig's "Configure" on case-insensitive file systems. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -univalue configure 1.0.3 -generated by GNU Autoconf 2.71 - -Copyright (C) 2021 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest.beam - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - } -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - -# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_c_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -printf %s "checking for $2... " >&6; } -if eval test \${$3+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - eval "$3=yes" -else $as_nop - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -eval ac_res=\$$3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_compile - -# ac_fn_c_check_func LINENO FUNC VAR -# ---------------------------------- -# Tests whether FUNC exists, setting the cache variable VAR accordingly -ac_fn_c_check_func () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -printf %s "checking for $2... " >&6; } -if eval test \${$3+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Define $2 to an innocuous variant, in case declares $2. - For example, HP-UX 11i declares gettimeofday. */ -#define $2 innocuous_$2 - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. */ - -#include -#undef $2 - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $2 (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$2 || defined __stub___$2 -choke me -#endif - -int -main (void) -{ -return $2 (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - eval "$3=yes" -else $as_nop - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -fi -eval ac_res=\$$3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_func - -# ac_fn_cxx_try_compile LINENO -# ---------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest.beam - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_compile - -# ac_fn_cxx_try_cpp LINENO -# ------------------------ -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || - test ! -s conftest.err - } -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_cpp - -# ac_fn_cxx_try_link LINENO -# ------------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - } -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_link -ac_configure_args_raw= -for ac_arg -do - case $ac_arg in - *\'*) - ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append ac_configure_args_raw " '$ac_arg'" -done - -case $ac_configure_args_raw in - *$as_nl*) - ac_safe_unquote= ;; - *) - ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. - ac_unsafe_a="$ac_unsafe_z#~" - ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" - ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; -esac - -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by univalue $as_me 1.0.3, which was -generated by GNU Autoconf 2.71. Invocation command line was - - $ $0$ac_configure_args_raw - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - printf "%s\n" "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Sanitize IFS. - IFS=" "" $as_nl" - # Save into config.log some information that might help in debugging. - { - echo - - printf "%s\n" "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - printf "%s\n" "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - printf "%s\n" "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - printf "%s\n" "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - printf "%s\n" "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - printf "%s\n" "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - printf "%s\n" "$as_me: caught signal $ac_signal" - printf "%s\n" "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -printf "%s\n" "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -if test -n "$CONFIG_SITE"; then - ac_site_files="$CONFIG_SITE" -elif test "x$prefix" != xNONE; then - ac_site_files="$prefix/share/config.site $prefix/etc/config.site" -else - ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" -fi - -for ac_site_file in $ac_site_files -do - case $ac_site_file in #( - */*) : - ;; #( - *) : - ac_site_file=./$ac_site_file ;; -esac - if test -f "$ac_site_file" && test -r "$ac_site_file"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -printf "%s\n" "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -printf "%s\n" "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Test code for whether the C compiler supports C89 (global declarations) -ac_c_conftest_c89_globals=' -/* Does the compiler advertise C89 conformance? - Do not test the value of __STDC__, because some compilers set it to 0 - while being otherwise adequately conformant. */ -#if !defined __STDC__ -# error "Compiler does not advertise C89 conformance" -#endif - -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ -struct buf { int x; }; -struct buf * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not \xHH hex character constants. - These do not provoke an error unfortunately, instead are silently treated - as an "x". The following induces an error, until -std is added to get - proper ANSI mode. Curiously \x00 != x always comes out true, for an - array size at least. It is necessary to write \x00 == 0 to get something - that is true only with -std. */ -int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) '\''x'\'' -int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), - int, int);' - -# Test code for whether the C compiler supports C89 (body of main). -ac_c_conftest_c89_main=' -ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); -' - -# Test code for whether the C compiler supports C99 (global declarations) -ac_c_conftest_c99_globals=' -// Does the compiler advertise C99 conformance? -#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L -# error "Compiler does not advertise C99 conformance" -#endif - -#include -extern int puts (const char *); -extern int printf (const char *, ...); -extern int dprintf (int, const char *, ...); -extern void *malloc (size_t); - -// Check varargs macros. These examples are taken from C99 6.10.3.5. -// dprintf is used instead of fprintf to avoid needing to declare -// FILE and stderr. -#define debug(...) dprintf (2, __VA_ARGS__) -#define showlist(...) puts (#__VA_ARGS__) -#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) -static void -test_varargs_macros (void) -{ - int x = 1234; - int y = 5678; - debug ("Flag"); - debug ("X = %d\n", x); - showlist (The first, second, and third items.); - report (x>y, "x is %d but y is %d", x, y); -} - -// Check long long types. -#define BIG64 18446744073709551615ull -#define BIG32 4294967295ul -#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) -#if !BIG_OK - #error "your preprocessor is broken" -#endif -#if BIG_OK -#else - #error "your preprocessor is broken" -#endif -static long long int bignum = -9223372036854775807LL; -static unsigned long long int ubignum = BIG64; - -struct incomplete_array -{ - int datasize; - double data[]; -}; - -struct named_init { - int number; - const wchar_t *name; - double average; -}; - -typedef const char *ccp; - -static inline int -test_restrict (ccp restrict text) -{ - // See if C++-style comments work. - // Iterate through items via the restricted pointer. - // Also check for declarations in for loops. - for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) - continue; - return 0; -} - -// Check varargs and va_copy. -static bool -test_varargs (const char *format, ...) -{ - va_list args; - va_start (args, format); - va_list args_copy; - va_copy (args_copy, args); - - const char *str = ""; - int number = 0; - float fnumber = 0; - - while (*format) - { - switch (*format++) - { - case '\''s'\'': // string - str = va_arg (args_copy, const char *); - break; - case '\''d'\'': // int - number = va_arg (args_copy, int); - break; - case '\''f'\'': // float - fnumber = va_arg (args_copy, double); - break; - default: - break; - } - } - va_end (args_copy); - va_end (args); - - return *str && number && fnumber; -} -' - -# Test code for whether the C compiler supports C99 (body of main). -ac_c_conftest_c99_main=' - // Check bool. - _Bool success = false; - success |= (argc != 0); - - // Check restrict. - if (test_restrict ("String literal") == 0) - success = true; - char *restrict newvar = "Another string"; - - // Check varargs. - success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); - test_varargs_macros (); - - // Check flexible array members. - struct incomplete_array *ia = - malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); - ia->datasize = 10; - for (int i = 0; i < ia->datasize; ++i) - ia->data[i] = i * 1.234; - - // Check named initializers. - struct named_init ni = { - .number = 34, - .name = L"Test wide string", - .average = 543.34343, - }; - - ni.number = 58; - - int dynamic_array[ni.number]; - dynamic_array[0] = argv[0][0]; - dynamic_array[ni.number - 1] = 543; - - // work around unused variable warnings - ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' - || dynamic_array[ni.number - 1] != 543); -' - -# Test code for whether the C compiler supports C11 (global declarations) -ac_c_conftest_c11_globals=' -// Does the compiler advertise C11 conformance? -#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L -# error "Compiler does not advertise C11 conformance" -#endif - -// Check _Alignas. -char _Alignas (double) aligned_as_double; -char _Alignas (0) no_special_alignment; -extern char aligned_as_int; -char _Alignas (0) _Alignas (int) aligned_as_int; - -// Check _Alignof. -enum -{ - int_alignment = _Alignof (int), - int_array_alignment = _Alignof (int[100]), - char_alignment = _Alignof (char) -}; -_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); - -// Check _Noreturn. -int _Noreturn does_not_return (void) { for (;;) continue; } - -// Check _Static_assert. -struct test_static_assert -{ - int x; - _Static_assert (sizeof (int) <= sizeof (long int), - "_Static_assert does not work in struct"); - long int y; -}; - -// Check UTF-8 literals. -#define u8 syntax error! -char const utf8_literal[] = u8"happens to be ASCII" "another string"; - -// Check duplicate typedefs. -typedef long *long_ptr; -typedef long int *long_ptr; -typedef long_ptr long_ptr; - -// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. -struct anonymous -{ - union { - struct { int i; int j; }; - struct { int k; long int l; } w; - }; - int m; -} v1; -' - -# Test code for whether the C compiler supports C11 (body of main). -ac_c_conftest_c11_main=' - _Static_assert ((offsetof (struct anonymous, i) - == offsetof (struct anonymous, w.k)), - "Anonymous union alignment botch"); - v1.i = 2; - v1.w.k = 5; - ok |= v1.i != 5; -' - -# Test code for whether the C compiler supports C11 (complete). -ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} -${ac_c_conftest_c99_globals} -${ac_c_conftest_c11_globals} - -int -main (int argc, char **argv) -{ - int ok = 0; - ${ac_c_conftest_c89_main} - ${ac_c_conftest_c99_main} - ${ac_c_conftest_c11_main} - return ok; -} -" - -# Test code for whether the C compiler supports C99 (complete). -ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} -${ac_c_conftest_c99_globals} - -int -main (int argc, char **argv) -{ - int ok = 0; - ${ac_c_conftest_c89_main} - ${ac_c_conftest_c99_main} - return ok; -} -" - -# Test code for whether the C compiler supports C89 (complete). -ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} - -int -main (int argc, char **argv) -{ - int ok = 0; - ${ac_c_conftest_c89_main} - return ok; -} -" - -as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" -as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" -as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" -as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" -as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" -as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" -as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" -as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" -as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" -# Test code for whether the C++ compiler supports C++98 (global declarations) -ac_cxx_conftest_cxx98_globals=' -// Does the compiler advertise C++98 conformance? -#if !defined __cplusplus || __cplusplus < 199711L -# error "Compiler does not advertise C++98 conformance" -#endif - -// These inclusions are to reject old compilers that -// lack the unsuffixed header files. -#include -#include - -// and are *not* freestanding headers in C++98. -extern void assert (int); -namespace std { - extern int strcmp (const char *, const char *); -} - -// Namespaces, exceptions, and templates were all added after "C++ 2.0". -using std::exception; -using std::strcmp; - -namespace { - -void test_exception_syntax() -{ - try { - throw "test"; - } catch (const char *s) { - // Extra parentheses suppress a warning when building autoconf itself, - // due to lint rules shared with more typical C programs. - assert (!(strcmp) (s, "test")); - } -} - -template struct test_template -{ - T const val; - explicit test_template(T t) : val(t) {} - template T add(U u) { return static_cast(u) + val; } -}; - -} // anonymous namespace -' - -# Test code for whether the C++ compiler supports C++98 (body of main) -ac_cxx_conftest_cxx98_main=' - assert (argc); - assert (! argv[0]); -{ - test_exception_syntax (); - test_template tt (2.0); - assert (tt.add (4) == 6.0); - assert (true && !false); -} -' - -# Test code for whether the C++ compiler supports C++11 (global declarations) -ac_cxx_conftest_cxx11_globals=' -// Does the compiler advertise C++ 2011 conformance? -#if !defined __cplusplus || __cplusplus < 201103L -# error "Compiler does not advertise C++11 conformance" -#endif - -namespace cxx11test -{ - constexpr int get_val() { return 20; } - - struct testinit - { - int i; - double d; - }; - - class delegate - { - public: - delegate(int n) : n(n) {} - delegate(): delegate(2354) {} - - virtual int getval() { return this->n; }; - protected: - int n; - }; - - class overridden : public delegate - { - public: - overridden(int n): delegate(n) {} - virtual int getval() override final { return this->n * 2; } - }; - - class nocopy - { - public: - nocopy(int i): i(i) {} - nocopy() = default; - nocopy(const nocopy&) = delete; - nocopy & operator=(const nocopy&) = delete; - private: - int i; - }; - - // for testing lambda expressions - template Ret eval(Fn f, Ret v) - { - return f(v); - } - - // for testing variadic templates and trailing return types - template auto sum(V first) -> V - { - return first; - } - template auto sum(V first, Args... rest) -> V - { - return first + sum(rest...); - } -} -' - -# Test code for whether the C++ compiler supports C++11 (body of main) -ac_cxx_conftest_cxx11_main=' -{ - // Test auto and decltype - auto a1 = 6538; - auto a2 = 48573953.4; - auto a3 = "String literal"; - - int total = 0; - for (auto i = a3; *i; ++i) { total += *i; } - - decltype(a2) a4 = 34895.034; -} -{ - // Test constexpr - short sa[cxx11test::get_val()] = { 0 }; -} -{ - // Test initializer lists - cxx11test::testinit il = { 4323, 435234.23544 }; -} -{ - // Test range-based for - int array[] = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3, - 14, 19, 17, 8, 6, 20, 16, 2, 11, 1}; - for (auto &x : array) { x += 23; } -} -{ - // Test lambda expressions - using cxx11test::eval; - assert (eval ([](int x) { return x*2; }, 21) == 42); - double d = 2.0; - assert (eval ([&](double x) { return d += x; }, 3.0) == 5.0); - assert (d == 5.0); - assert (eval ([=](double x) mutable { return d += x; }, 4.0) == 9.0); - assert (d == 5.0); -} -{ - // Test use of variadic templates - using cxx11test::sum; - auto a = sum(1); - auto b = sum(1, 2); - auto c = sum(1.0, 2.0, 3.0); -} -{ - // Test constructor delegation - cxx11test::delegate d1; - cxx11test::delegate d2(); - cxx11test::delegate d3(45); -} -{ - // Test override and final - cxx11test::overridden o1(55464); -} -{ - // Test nullptr - char *c = nullptr; -} -{ - // Test template brackets - test_template<::test_template> v(test_template(12)); -} -{ - // Unicode literals - char const *utf8 = u8"UTF-8 string \u2500"; - char16_t const *utf16 = u"UTF-8 string \u2500"; - char32_t const *utf32 = U"UTF-32 string \u2500"; -} -' - -# Test code for whether the C compiler supports C++11 (complete). -ac_cxx_conftest_cxx11_program="${ac_cxx_conftest_cxx98_globals} -${ac_cxx_conftest_cxx11_globals} - -int -main (int argc, char **argv) -{ - int ok = 0; - ${ac_cxx_conftest_cxx98_main} - ${ac_cxx_conftest_cxx11_main} - return ok; -} -" - -# Test code for whether the C compiler supports C++98 (complete). -ac_cxx_conftest_cxx98_program="${ac_cxx_conftest_cxx98_globals} -int -main (int argc, char **argv) -{ - int ok = 0; - ${ac_cxx_conftest_cxx98_main} - return ok; -} -" - - -# Auxiliary files required by this configure script. -ac_aux_files="compile config.guess config.sub ltmain.sh missing install-sh" - -# Locations in which to look for auxiliary files. -ac_aux_dir_candidates="${srcdir}/build-aux" - -# Search for a directory containing all of the required auxiliary files, -# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. -# If we don't find one directory that contains all the files we need, -# we report the set of missing files from the *first* directory in -# $ac_aux_dir_candidates and give up. -ac_missing_aux_files="" -ac_first_candidate=: -printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in $ac_aux_dir_candidates -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - as_found=: - - printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 - ac_aux_dir_found=yes - ac_install_sh= - for ac_aux in $ac_aux_files - do - # As a special case, if "install-sh" is required, that requirement - # can be satisfied by any of "install-sh", "install.sh", or "shtool", - # and $ac_install_sh is set appropriately for whichever one is found. - if test x"$ac_aux" = x"install-sh" - then - if test -f "${as_dir}install-sh"; then - printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 - ac_install_sh="${as_dir}install-sh -c" - elif test -f "${as_dir}install.sh"; then - printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 - ac_install_sh="${as_dir}install.sh -c" - elif test -f "${as_dir}shtool"; then - printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 - ac_install_sh="${as_dir}shtool install -c" - else - ac_aux_dir_found=no - if $ac_first_candidate; then - ac_missing_aux_files="${ac_missing_aux_files} install-sh" - else - break - fi - fi - else - if test -f "${as_dir}${ac_aux}"; then - printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 - else - ac_aux_dir_found=no - if $ac_first_candidate; then - ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" - else - break - fi - fi - fi - done - if test "$ac_aux_dir_found" = yes; then - ac_aux_dir="$as_dir" - break - fi - ac_first_candidate=false - - as_found=false -done -IFS=$as_save_IFS -if $as_found -then : - -else $as_nop - as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 -fi - - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -if test -f "${ac_aux_dir}config.guess"; then - ac_config_guess="$SHELL ${ac_aux_dir}config.guess" -fi -if test -f "${ac_aux_dir}config.sub"; then - ac_config_sub="$SHELL ${ac_aux_dir}config.sub" -fi -if test -f "$ac_aux_dir/configure"; then - ac_configure="$SHELL ${ac_aux_dir}configure" -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' - and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - -# Check whether --enable-silent-rules was given. -if test ${enable_silent_rules+y} -then : - enableval=$enable_silent_rules; -fi - -case $enable_silent_rules in # ((( - yes) AM_DEFAULT_VERBOSITY=0;; - no) AM_DEFAULT_VERBOSITY=1;; - *) AM_DEFAULT_VERBOSITY=0;; -esac -am_make=${MAKE-make} -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 -printf %s "checking whether $am_make supports nested variables... " >&6; } -if test ${am_cv_make_support_nested_variables+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if printf "%s\n" 'TRUE=$(BAR$(V)) -BAR0=false -BAR1=true -V=1 -am__doit: - @$(TRUE) -.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then - am_cv_make_support_nested_variables=yes -else - am_cv_make_support_nested_variables=no -fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 -printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } -if test $am_cv_make_support_nested_variables = yes; then - AM_V='$(V)' - AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' -else - AM_V=$AM_DEFAULT_VERBOSITY - AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY -fi -AM_BACKSLASH='\' - - - - - - -ac_config_headers="$ac_config_headers univalue-config.h" - -am__api_version='1.16' - - - - # Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -# Reject install programs that cannot install multiple files. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -printf %s "checking for a BSD-compatible install... " >&6; } -if test -z "$INSTALL"; then -if test ${ac_cv_path_install+y} -then : - printf %s "(cached) " >&6 -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - # Account for fact that we put trailing slashes in our PATH walk. -case $as_dir in #(( - ./ | /[cC]/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - rm -rf conftest.one conftest.two conftest.dir - echo one > conftest.one - echo two > conftest.two - mkdir conftest.dir - if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && - test -s conftest.one && test -s conftest.two && - test -s conftest.dir/conftest.one && - test -s conftest.dir/conftest.two - then - ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - fi - done - done - ;; -esac - - done -IFS=$as_save_IFS - -rm -rf conftest.one conftest.two conftest.dir - -fi - if test ${ac_cv_path_install+y}; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. Don't cache a - # value for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - INSTALL=$ac_install_sh - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -printf "%s\n" "$INSTALL" >&6; } - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 -printf %s "checking whether build environment is sane... " >&6; } -# Reject unsafe characters in $srcdir or the absolute working directory -# name. Accept space and tab only in the latter. -am_lf=' -' -case `pwd` in - *[\\\"\#\$\&\'\`$am_lf]*) - as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; -esac -case $srcdir in - *[\\\"\#\$\&\'\`$am_lf\ \ ]*) - as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; -esac - -# Do 'set' in a subshell so we don't clobber the current shell's -# arguments. Must try -L first in case configure is actually a -# symlink; some systems play weird games with the mod time of symlinks -# (eg FreeBSD returns the mod time of the symlink's containing -# directory). -if ( - am_has_slept=no - for am_try in 1 2; do - echo "timestamp, slept: $am_has_slept" > conftest.file - set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` - if test "$*" = "X"; then - # -L didn't work. - set X `ls -t "$srcdir/configure" conftest.file` - fi - if test "$*" != "X $srcdir/configure conftest.file" \ - && test "$*" != "X conftest.file $srcdir/configure"; then - - # If neither matched, then we have a broken ls. This can happen - # if, for instance, CONFIG_SHELL is bash and it inherits a - # broken ls alias from the environment. This has actually - # happened. Such a system could not be considered "sane". - as_fn_error $? "ls -t appears to fail. Make sure there is not a broken - alias in your environment" "$LINENO" 5 - fi - if test "$2" = conftest.file || test $am_try -eq 2; then - break - fi - # Just in case. - sleep 1 - am_has_slept=yes - done - test "$2" = conftest.file - ) -then - # Ok. - : -else - as_fn_error $? "newly created file is older than distributed files! -Check your system clock" "$LINENO" 5 -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } -# If we didn't sleep, we still need to ensure time stamps of config.status and -# generated files are strictly newer. -am_sleep_pid= -if grep 'slept: no' conftest.file >/dev/null 2>&1; then - ( sleep 1 ) & - am_sleep_pid=$! -fi - -rm -f conftest.file - -test "$program_prefix" != NONE && - program_transform_name="s&^&$program_prefix&;$program_transform_name" -# Use a double $ so make ignores it. -test "$program_suffix" != NONE && - program_transform_name="s&\$&$program_suffix&;$program_transform_name" -# Double any \ or $. -# By default was `s,x,x', remove it if useless. -ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' -program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_script"` - - -# Expand $ac_aux_dir to an absolute path. -am_aux_dir=`cd "$ac_aux_dir" && pwd` - - - if test x"${MISSING+set}" != xset; then - MISSING="\${SHELL} '$am_aux_dir/missing'" -fi -# Use eval to expand $SHELL -if eval "$MISSING --is-lightweight"; then - am_missing_run="$MISSING " -else - am_missing_run= - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 -printf "%s\n" "$as_me: WARNING: 'missing' script is too old or missing" >&2;} -fi - -if test x"${install_sh+set}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; - *) - install_sh="\${SHELL} $am_aux_dir/install-sh" - esac -fi - -# Installed binaries are usually stripped using 'strip' when the user -# run "make install-strip". However 'strip' might not be the right -# tool to use in cross-compilation environments, therefore Automake -# will honor the 'STRIP' environment variable to overrule this program. -if test "$cross_compiling" != no; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. -set dummy ${ac_tool_prefix}strip; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_STRIP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$STRIP"; then - ac_cv_prog_STRIP="$STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_STRIP="${ac_tool_prefix}strip" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -STRIP=$ac_cv_prog_STRIP -if test -n "$STRIP"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 -printf "%s\n" "$STRIP" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_STRIP"; then - ac_ct_STRIP=$STRIP - # Extract the first word of "strip", so it can be a program name with args. -set dummy strip; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_STRIP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_STRIP"; then - ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_STRIP="strip" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP -if test -n "$ac_ct_STRIP"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 -printf "%s\n" "$ac_ct_STRIP" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_STRIP" = x; then - STRIP=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - STRIP=$ac_ct_STRIP - fi -else - STRIP="$ac_cv_prog_STRIP" -fi - -fi -INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 -printf %s "checking for a race-free mkdir -p... " >&6; } -if test -z "$MKDIR_P"; then - if test ${ac_cv_path_mkdir+y} -then : - printf %s "(cached) " >&6 -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_prog in mkdir gmkdir; do - for ac_exec_ext in '' $ac_executable_extensions; do - as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue - case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( - 'mkdir ('*'coreutils) '* | \ - 'BusyBox '* | \ - 'mkdir (fileutils) '4.1*) - ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext - break 3;; - esac - done - done - done -IFS=$as_save_IFS - -fi - - test -d ./--version && rmdir ./--version - if test ${ac_cv_path_mkdir+y}; then - MKDIR_P="$ac_cv_path_mkdir -p" - else - # As a last resort, use the slow shell script. Don't cache a - # value for MKDIR_P within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - MKDIR_P="$ac_install_sh -d" - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 -printf "%s\n" "$MKDIR_P" >&6; } - -for ac_prog in gawk mawk nawk awk -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_AWK+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$AWK"; then - ac_cv_prog_AWK="$AWK" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_AWK="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AWK=$ac_cv_prog_AWK -if test -n "$AWK"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 -printf "%s\n" "$AWK" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$AWK" && break -done - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } -set x ${MAKE-make} -ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval test \${ac_cv_prog_make_${ac_make}_set+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat >conftest.make <<\_ACEOF -SHELL = /bin/sh -all: - @echo '@@@%%%=$(MAKE)=@@@%%%' -_ACEOF -# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. -case `${MAKE-make} -f conftest.make 2>/dev/null` in - *@@@%%%=?*=@@@%%%*) - eval ac_cv_prog_make_${ac_make}_set=yes;; - *) - eval ac_cv_prog_make_${ac_make}_set=no;; -esac -rm -f conftest.make -fi -if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } - SET_MAKE= -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } - SET_MAKE="MAKE=${MAKE-make}" -fi - -rm -rf .tst 2>/dev/null -mkdir .tst 2>/dev/null -if test -d .tst; then - am__leading_dot=. -else - am__leading_dot=_ -fi -rmdir .tst 2>/dev/null - -if test "`cd $srcdir && pwd`" != "`pwd`"; then - # Use -I$(srcdir) only when $(srcdir) != ., so that make's output - # is not polluted with repeated "-I." - am__isrc=' -I$(srcdir)' - # test to see if srcdir already configured - if test -f $srcdir/config.status; then - as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 - fi -fi - -# test whether we have cygpath -if test -z "$CYGPATH_W"; then - if (cygpath --version) >/dev/null 2>/dev/null; then - CYGPATH_W='cygpath -w' - else - CYGPATH_W=echo - fi -fi - - -# Define the identity of the package. - PACKAGE='univalue' - VERSION='1.0.3' - - -printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h - - -printf "%s\n" "#define VERSION \"$VERSION\"" >>confdefs.h - -# Some tools Automake needs. - -ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} - - -AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} - - -AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} - - -AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} - - -MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} - -# For better backward compatibility. To be removed once Automake 1.9.x -# dies out for good. For more background, see: -# -# -mkdir_p='$(MKDIR_P)' - -# We need awk for the "check" target (and possibly the TAP driver). The -# system "awk" is bad on some platforms. -# Always define AMTAR for backward compatibility. Yes, it's still used -# in the wild :-( We should find a proper way to deprecate it ... -AMTAR='$${TAR-tar}' - - -# We'll loop over all known methods to create a tar archive until one works. -_am_tools='gnutar pax cpio none' - -am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' - - - - - -# Variables for tags utilities; see am/tags.am -if test -z "$CTAGS"; then - CTAGS=ctags -fi - -if test -z "$ETAGS"; then - ETAGS=etags -fi - -if test -z "$CSCOPE"; then - CSCOPE=cscope -fi - - - -# POSIX will say in a future version that running "rm -f" with no argument -# is OK; and we want to be able to make that assumption in our Makefile -# recipes. So use an aggressive probe to check that the usage we want is -# actually supported "in the wild" to an acceptable degree. -# See automake bug#10828. -# To make any issue more visible, cause the running configure to be aborted -# by default if the 'rm' program in use doesn't match our expectations; the -# user can still override this though. -if rm -f && rm -fr && rm -rf; then : OK; else - cat >&2 <<'END' -Oops! - -Your 'rm' program seems unable to run without file operands specified -on the command line, even when the '-f' option is present. This is contrary -to the behaviour of most rm programs out there, and not conforming with -the upcoming POSIX standard: - -Please tell bug-automake@gnu.org about your system, including the value -of your $PATH and any error possibly output before this message. This -can help us improve future automake versions. - -END - if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then - echo 'Configuration will proceed anyway, since you have set the' >&2 - echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 - echo >&2 - else - cat >&2 <<'END' -Aborting the configuration process, to ensure you take notice of the issue. - -You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . - -If you want to complete the configuration process using your problematic -'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM -to "yes", and re-run configure. - -END - as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 - fi -fi - - -LIBUNIVALUE_MAJOR_VERSION=1 -LIBUNIVALUE_MINOR_VERSION=1 -LIBUNIVALUE_MICRO_VERSION=3 -LIBUNIVALUE_INTERFACE_AGE=3 - -# ABI version -# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html -LIBUNIVALUE_CURRENT=100 -LIBUNIVALUE_REVISION=3 -LIBUNIVALUE_AGE=100 - - - - - -case `pwd` in - *\ * | *\ *) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 -printf "%s\n" "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; -esac - - - -macro_version='2.4.6' -macro_revision='2.4.6' - - - - - - - - - - - - - - -ltmain=$ac_aux_dir/ltmain.sh - - - - # Make sure we can run config.sub. -$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -printf %s "checking build system type... " >&6; } -if test ${ac_cv_build+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -printf "%s\n" "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -printf %s "checking host system type... " >&6; } -if test ${ac_cv_host+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || - as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -printf "%s\n" "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - -# Backslashify metacharacters that are still active within -# double-quoted strings. -sed_quote_subst='s/\(["`$\\]\)/\\\1/g' - -# Same as above, but do not quote variable references. -double_quote_subst='s/\(["`\\]\)/\\\1/g' - -# Sed substitution to delay expansion of an escaped shell variable in a -# double_quote_subst'ed string. -delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' - -# Sed substitution to delay expansion of an escaped single quote. -delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' - -# Sed substitution to avoid accidental globbing in evaled expressions -no_glob_subst='s/\*/\\\*/g' - -ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO -ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 -printf %s "checking how to print strings... " >&6; } -# Test print first, because it will be a builtin if present. -if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ - test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then - ECHO='print -r --' -elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then - ECHO='printf %s\n' -else - # Use this function as a fallback that always works. - func_fallback_echo () - { - eval 'cat <<_LTECHO_EOF -$1 -_LTECHO_EOF' - } - ECHO='func_fallback_echo' -fi - -# func_echo_all arg... -# Invoke $ECHO with all args, space-separated. -func_echo_all () -{ - $ECHO "" -} - -case $ECHO in - printf*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: printf" >&5 -printf "%s\n" "printf" >&6; } ;; - print*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 -printf "%s\n" "print -r" >&6; } ;; - *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cat" >&5 -printf "%s\n" "cat" >&6; } ;; -esac - - - - - - - - - - - - - - - - - - - - - - - -DEPDIR="${am__leading_dot}deps" - -ac_config_commands="$ac_config_commands depfiles" - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 -printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; } -cat > confinc.mk << 'END' -am__doit: - @echo this is the am__doit target >confinc.out -.PHONY: am__doit -END -am__include="#" -am__quote= -# BSD make does it like this. -echo '.include "confinc.mk" # ignored' > confmf.BSD -# Other make implementations (GNU, Solaris 10, AIX) do it like this. -echo 'include confinc.mk # ignored' > confmf.GNU -_am_result=no -for s in GNU BSD; do - { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 - (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - case $?:`cat confinc.out 2>/dev/null` in #( - '0:this is the am__doit target') : - case $s in #( - BSD) : - am__include='.include' am__quote='"' ;; #( - *) : - am__include='include' am__quote='' ;; -esac ;; #( - *) : - ;; -esac - if test "$am__include" != "#"; then - _am_result="yes ($s style)" - break - fi -done -rm -f confinc.* confmf.* -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 -printf "%s\n" "${_am_result}" >&6; } - -# Check whether --enable-dependency-tracking was given. -if test ${enable_dependency_tracking+y} -then : - enableval=$enable_dependency_tracking; -fi - -if test "x$enable_dependency_tracking" != xno; then - am_depcomp="$ac_aux_dir/depcomp" - AMDEPBACKSLASH='\' - am__nodep='_no' -fi - if test "x$enable_dependency_tracking" != xno; then - AMDEP_TRUE= - AMDEP_FALSE='#' -else - AMDEP_TRUE='#' - AMDEP_FALSE= -fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -printf "%s\n" "$ac_ct_CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -printf "%s\n" "$ac_ct_CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. -set dummy ${ac_tool_prefix}clang; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}clang" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "clang", so it can be a program name with args. -set dummy clang; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="clang" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -printf "%s\n" "$ac_ct_CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -fi - - -test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion -version; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -printf %s "checking whether the C compiler works... " >&6; } -ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else $as_nop - ac_file='' -fi -if test -z "$ac_file" -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -printf %s "checking for C compiler default output file name... " >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -printf "%s\n" "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -printf %s "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -printf "%s\n" "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main (void) -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -printf %s "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -printf "%s\n" "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -printf %s "checking for suffix of object files... " >&6; } -if test ${ac_cv_objext+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -printf "%s\n" "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 -printf %s "checking whether the compiler supports GNU C... " >&6; } -if test ${ac_cv_c_compiler_gnu+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_compiler_gnu=yes -else $as_nop - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+y} -ac_save_CFLAGS=$CFLAGS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -printf %s "checking whether $CC accepts -g... " >&6; } -if test ${ac_cv_prog_cc_g+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_g=yes -else $as_nop - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - -else $as_nop - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -printf "%s\n" "$ac_cv_prog_cc_g" >&6; } -if test $ac_test_CFLAGS; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -ac_prog_cc_stdc=no -if test x$ac_prog_cc_stdc = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 -printf %s "checking for $CC option to enable C11 features... " >&6; } -if test ${ac_cv_prog_cc_c11+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c11=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_c_conftest_c11_program -_ACEOF -for ac_arg in '' -std=gnu11 -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_c11=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam - test "x$ac_cv_prog_cc_c11" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC -fi - -if test "x$ac_cv_prog_cc_c11" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c11" = x -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 -printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } - CC="$CC $ac_cv_prog_cc_c11" -fi - ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 - ac_prog_cc_stdc=c11 -fi -fi -if test x$ac_prog_cc_stdc = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 -printf %s "checking for $CC option to enable C99 features... " >&6; } -if test ${ac_cv_prog_cc_c99+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c99=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_c_conftest_c99_program -_ACEOF -for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_c99=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam - test "x$ac_cv_prog_cc_c99" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC -fi - -if test "x$ac_cv_prog_cc_c99" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c99" = x -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 -printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } - CC="$CC $ac_cv_prog_cc_c99" -fi - ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 - ac_prog_cc_stdc=c99 -fi -fi -if test x$ac_prog_cc_stdc = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 -printf %s "checking for $CC option to enable C89 features... " >&6; } -if test ${ac_cv_prog_cc_c89+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_c_conftest_c89_program -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC -fi - -if test "x$ac_cv_prog_cc_c89" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c89" = x -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } - CC="$CC $ac_cv_prog_cc_c89" -fi - ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 - ac_prog_cc_stdc=c89 -fi -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 -printf %s "checking whether $CC understands -c and -o together... " >&6; } -if test ${am_cv_prog_cc_c_o+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF - # Make sure it works both with $CC and with simple cc. - # Following AC_PROG_CC_C_O, we do the test twice because some - # compilers refuse to overwrite an existing .o file with -o, - # though they will create one. - am_cv_prog_cc_c_o=yes - for am_i in 1 2; do - if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 - ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } \ - && test -f conftest2.$ac_objext; then - : OK - else - am_cv_prog_cc_c_o=no - break - fi - done - rm -f core conftest* - unset am_i -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 -printf "%s\n" "$am_cv_prog_cc_c_o" >&6; } -if test "$am_cv_prog_cc_c_o" != yes; then - # Losing compiler, so override with the script. - # FIXME: It is wrong to rewrite CC. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__CC in this case, - # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" - CC="$am_aux_dir/compile $CC" -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -depcc="$CC" am_compiler_list= - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -printf %s "checking dependency style of $depcc... " >&6; } -if test ${am_cv_CC_dependencies_compiler_type+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named 'D' -- because '-MD' means "put the output - # in D". - rm -rf conftest.dir - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_CC_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` - fi - am__universal=false - case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with '-c' and '-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_CC_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir -else - am_cv_CC_dependencies_compiler_type=none -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 -printf "%s\n" "$am_cv_CC_dependencies_compiler_type" >&6; } -CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type - - if - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then - am__fastdepCC_TRUE= - am__fastdepCC_FALSE='#' -else - am__fastdepCC_TRUE='#' - am__fastdepCC_FALSE= -fi - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 -printf %s "checking for a sed that does not truncate output... " >&6; } -if test ${ac_cv_path_SED+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ - for ac_i in 1 2 3 4 5 6 7; do - ac_script="$ac_script$as_nl$ac_script" - done - echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed - { ac_script=; unset ac_script;} - if test -z "$SED"; then - ac_path_SED_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_prog in sed gsed - do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_SED="$as_dir$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_SED" || continue -# Check for GNU ac_path_SED and select it if it is found. - # Check for GNU $ac_path_SED -case `"$ac_path_SED" --version 2>&1` in -*GNU*) - ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; -*) - ac_count=0 - printf %s 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - printf "%s\n" '' >> "conftest.nl" - "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_SED_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_SED="$ac_path_SED" - ac_path_SED_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_SED_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_SED"; then - as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 - fi -else - ac_cv_path_SED=$SED -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 -printf "%s\n" "$ac_cv_path_SED" >&6; } - SED="$ac_cv_path_SED" - rm -f conftest.sed - -test -z "$SED" && SED=sed -Xsed="$SED -e 1s/^X//" - - - - - - - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -printf %s "checking for grep that handles long lines and -e... " >&6; } -if test ${ac_cv_path_GREP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_prog in grep ggrep - do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - printf %s 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - printf "%s\n" 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -printf "%s\n" "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -printf %s "checking for egrep... " >&6; } -if test ${ac_cv_path_EGREP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_prog in egrep - do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - printf %s 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - printf "%s\n" 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -printf "%s\n" "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 -printf %s "checking for fgrep... " >&6; } -if test ${ac_cv_path_FGREP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 - then ac_cv_path_FGREP="$GREP -F" - else - if test -z "$FGREP"; then - ac_path_FGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_prog in fgrep - do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_FGREP="$as_dir$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_FGREP" || continue -# Check for GNU ac_path_FGREP and select it if it is found. - # Check for GNU $ac_path_FGREP -case `"$ac_path_FGREP" --version 2>&1` in -*GNU*) - ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; -*) - ac_count=0 - printf %s 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - printf "%s\n" 'FGREP' >> "conftest.nl" - "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_FGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_FGREP="$ac_path_FGREP" - ac_path_FGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_FGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_FGREP"; then - as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_FGREP=$FGREP -fi - - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 -printf "%s\n" "$ac_cv_path_FGREP" >&6; } - FGREP="$ac_cv_path_FGREP" - - -test -z "$GREP" && GREP=grep - - - - - - - - - - - - - - - - - - - -# Check whether --with-gnu-ld was given. -if test ${with_gnu_ld+y} -then : - withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes -else $as_nop - with_gnu_ld=no -fi - -ac_prog=ld -if test yes = "$GCC"; then - # Check if gcc -print-prog-name=ld gives a path. - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 -printf %s "checking for ld used by $CC... " >&6; } - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return, which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [\\/]* | ?:[\\/]*) - re_direlt='/[^/][^/]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` - while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do - ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD=$ac_prog - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test yes = "$with_gnu_ld"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 -printf %s "checking for GNU ld... " >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 -printf %s "checking for non-GNU ld... " >&6; } -fi -if test ${lt_cv_path_LD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -z "$LD"; then - lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS=$lt_save_ifs - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD=$ac_dir/$ac_prog - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &5 -printf "%s\n" "$LD" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi -test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 -printf %s "checking if the linker ($LD) is GNU ld... " >&6; } -if test ${lt_cv_prog_gnu_ld+y} -then : - printf %s "(cached) " >&6 -else $as_nop - # I'd rather use --version here, but apparently some GNU lds only accept -v. -case `$LD -v 2>&1 &5 -printf "%s\n" "$lt_cv_prog_gnu_ld" >&6; } -with_gnu_ld=$lt_cv_prog_gnu_ld - - - - - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 -printf %s "checking for BSD- or MS-compatible name lister (nm)... " >&6; } -if test ${lt_cv_path_NM+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$NM"; then - # Let the user override the test. - lt_cv_path_NM=$NM -else - lt_nm_to_check=${ac_tool_prefix}nm - if test -n "$ac_tool_prefix" && test "$build" = "$host"; then - lt_nm_to_check="$lt_nm_to_check nm" - fi - for lt_tmp_nm in $lt_nm_to_check; do - lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR - for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do - IFS=$lt_save_ifs - test -z "$ac_dir" && ac_dir=. - tmp_nm=$ac_dir/$lt_tmp_nm - if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then - # Check to see if the nm accepts a BSD-compat flag. - # Adding the 'sed 1q' prevents false positives on HP-UX, which says: - # nm: unknown option "B" ignored - # Tru64's nm complains that /dev/null is an invalid object file - # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty - case $build_os in - mingw*) lt_bad_file=conftest.nm/nofile ;; - *) lt_bad_file=/dev/null ;; - esac - case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in - *$lt_bad_file* | *'Invalid file or object type'*) - lt_cv_path_NM="$tmp_nm -B" - break 2 - ;; - *) - case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in - */dev/null*) - lt_cv_path_NM="$tmp_nm -p" - break 2 - ;; - *) - lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but - continue # so that we can try to find one that supports BSD flags - ;; - esac - ;; - esac - fi - done - IFS=$lt_save_ifs - done - : ${lt_cv_path_NM=no} -fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 -printf "%s\n" "$lt_cv_path_NM" >&6; } -if test no != "$lt_cv_path_NM"; then - NM=$lt_cv_path_NM -else - # Didn't find any BSD compatible name lister, look for dumpbin. - if test -n "$DUMPBIN"; then : - # Let the user override the test. - else - if test -n "$ac_tool_prefix"; then - for ac_prog in dumpbin "link -dump" - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_DUMPBIN+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$DUMPBIN"; then - ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -DUMPBIN=$ac_cv_prog_DUMPBIN -if test -n "$DUMPBIN"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 -printf "%s\n" "$DUMPBIN" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$DUMPBIN" && break - done -fi -if test -z "$DUMPBIN"; then - ac_ct_DUMPBIN=$DUMPBIN - for ac_prog in dumpbin "link -dump" -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_DUMPBIN+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_DUMPBIN"; then - ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN -if test -n "$ac_ct_DUMPBIN"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 -printf "%s\n" "$ac_ct_DUMPBIN" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$ac_ct_DUMPBIN" && break -done - - if test "x$ac_ct_DUMPBIN" = x; then - DUMPBIN=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - DUMPBIN=$ac_ct_DUMPBIN - fi -fi - - case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in - *COFF*) - DUMPBIN="$DUMPBIN -symbols -headers" - ;; - *) - DUMPBIN=: - ;; - esac - fi - - if test : != "$DUMPBIN"; then - NM=$DUMPBIN - fi -fi -test -z "$NM" && NM=nm - - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 -printf %s "checking the name lister ($NM) interface... " >&6; } -if test ${lt_cv_nm_interface+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_nm_interface="BSD nm" - echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) - (eval "$ac_compile" 2>conftest.err) - cat conftest.err >&5 - (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) - (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) - cat conftest.err >&5 - (eval echo "\"\$as_me:$LINENO: output\"" >&5) - cat conftest.out >&5 - if $GREP 'External.*some_variable' conftest.out > /dev/null; then - lt_cv_nm_interface="MS dumpbin" - fi - rm -f conftest* -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 -printf "%s\n" "$lt_cv_nm_interface" >&6; } - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 -printf %s "checking whether ln -s works... " >&6; } -LN_S=$as_ln_s -if test "$LN_S" = "ln -s"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 -printf "%s\n" "no, using $LN_S" >&6; } -fi - -# find the maximum length of command line arguments -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 -printf %s "checking the maximum length of command line arguments... " >&6; } -if test ${lt_cv_sys_max_cmd_len+y} -then : - printf %s "(cached) " >&6 -else $as_nop - i=0 - teststring=ABCD - - case $build_os in - msdosdjgpp*) - # On DJGPP, this test can blow up pretty badly due to problems in libc - # (any single argument exceeding 2000 bytes causes a buffer overrun - # during glob expansion). Even if it were fixed, the result of this - # check would be larger than it should be. - lt_cv_sys_max_cmd_len=12288; # 12K is about right - ;; - - gnu*) - # Under GNU Hurd, this test is not required because there is - # no limit to the length of command line arguments. - # Libtool will interpret -1 as no limit whatsoever - lt_cv_sys_max_cmd_len=-1; - ;; - - cygwin* | mingw* | cegcc*) - # On Win9x/ME, this test blows up -- it succeeds, but takes - # about 5 minutes as the teststring grows exponentially. - # Worse, since 9x/ME are not pre-emptively multitasking, - # you end up with a "frozen" computer, even though with patience - # the test eventually succeeds (with a max line length of 256k). - # Instead, let's just punt: use the minimum linelength reported by - # all of the supported platforms: 8192 (on NT/2K/XP). - lt_cv_sys_max_cmd_len=8192; - ;; - - mint*) - # On MiNT this can take a long time and run out of memory. - lt_cv_sys_max_cmd_len=8192; - ;; - - amigaos*) - # On AmigaOS with pdksh, this test takes hours, literally. - # So we just punt and use a minimum line length of 8192. - lt_cv_sys_max_cmd_len=8192; - ;; - - bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) - # This has been around since 386BSD, at least. Likely further. - if test -x /sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` - elif test -x /usr/sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` - else - lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs - fi - # And add a safety zone - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - ;; - - interix*) - # We know the value 262144 and hardcode it with a safety zone (like BSD) - lt_cv_sys_max_cmd_len=196608 - ;; - - os2*) - # The test takes a long time on OS/2. - lt_cv_sys_max_cmd_len=8192 - ;; - - osf*) - # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure - # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not - # nice to cause kernel panics so lets avoid the loop below. - # First set a reasonable default. - lt_cv_sys_max_cmd_len=16384 - # - if test -x /sbin/sysconfig; then - case `/sbin/sysconfig -q proc exec_disable_arg_limit` in - *1*) lt_cv_sys_max_cmd_len=-1 ;; - esac - fi - ;; - sco3.2v5*) - lt_cv_sys_max_cmd_len=102400 - ;; - sysv5* | sco5v6* | sysv4.2uw2*) - kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` - if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` - else - lt_cv_sys_max_cmd_len=32768 - fi - ;; - *) - lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` - if test -n "$lt_cv_sys_max_cmd_len" && \ - test undefined != "$lt_cv_sys_max_cmd_len"; then - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - else - # Make teststring a little bigger before we do anything with it. - # a 1K string should be a reasonable start. - for i in 1 2 3 4 5 6 7 8; do - teststring=$teststring$teststring - done - SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} - # If test is not a shell built-in, we'll probably end up computing a - # maximum length that is only half of the actual maximum length, but - # we can't tell. - while { test X`env echo "$teststring$teststring" 2>/dev/null` \ - = "X$teststring$teststring"; } >/dev/null 2>&1 && - test 17 != "$i" # 1/2 MB should be enough - do - i=`expr $i + 1` - teststring=$teststring$teststring - done - # Only check the string length outside the loop. - lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` - teststring= - # Add a significant safety factor because C++ compilers can tack on - # massive amounts of additional arguments before passing them to the - # linker. It appears as though 1/2 is a usable value. - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` - fi - ;; - esac - -fi - -if test -n "$lt_cv_sys_max_cmd_len"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 -printf "%s\n" "$lt_cv_sys_max_cmd_len" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none" >&5 -printf "%s\n" "none" >&6; } -fi -max_cmd_len=$lt_cv_sys_max_cmd_len - - - - - - -: ${CP="cp -f"} -: ${MV="mv -f"} -: ${RM="rm -f"} - -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - lt_unset=unset -else - lt_unset=false -fi - - - - - -# test EBCDIC or ASCII -case `echo X|tr X '\101'` in - A) # ASCII based system - # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr - lt_SP2NL='tr \040 \012' - lt_NL2SP='tr \015\012 \040\040' - ;; - *) # EBCDIC based system - lt_SP2NL='tr \100 \n' - lt_NL2SP='tr \r\n \100\100' - ;; -esac - - - - - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 -printf %s "checking how to convert $build file names to $host format... " >&6; } -if test ${lt_cv_to_host_file_cmd+y} -then : - printf %s "(cached) " >&6 -else $as_nop - case $host in - *-*-mingw* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 - ;; - *-*-cygwin* ) - lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 - ;; - * ) # otherwise, assume *nix - lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 - ;; - esac - ;; - *-*-cygwin* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin - ;; - *-*-cygwin* ) - lt_cv_to_host_file_cmd=func_convert_file_noop - ;; - * ) # otherwise, assume *nix - lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin - ;; - esac - ;; - * ) # unhandled hosts (and "normal" native builds) - lt_cv_to_host_file_cmd=func_convert_file_noop - ;; -esac - -fi - -to_host_file_cmd=$lt_cv_to_host_file_cmd -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 -printf "%s\n" "$lt_cv_to_host_file_cmd" >&6; } - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 -printf %s "checking how to convert $build file names to toolchain format... " >&6; } -if test ${lt_cv_to_tool_file_cmd+y} -then : - printf %s "(cached) " >&6 -else $as_nop - #assume ordinary cross tools, or native build. -lt_cv_to_tool_file_cmd=func_convert_file_noop -case $host in - *-*-mingw* ) - case $build in - *-*-mingw* ) # actually msys - lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 - ;; - esac - ;; -esac - -fi - -to_tool_file_cmd=$lt_cv_to_tool_file_cmd -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 -printf "%s\n" "$lt_cv_to_tool_file_cmd" >&6; } - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 -printf %s "checking for $LD option to reload object files... " >&6; } -if test ${lt_cv_ld_reload_flag+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_ld_reload_flag='-r' -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 -printf "%s\n" "$lt_cv_ld_reload_flag" >&6; } -reload_flag=$lt_cv_ld_reload_flag -case $reload_flag in -"" | " "*) ;; -*) reload_flag=" $reload_flag" ;; -esac -reload_cmds='$LD$reload_flag -o $output$reload_objs' -case $host_os in - cygwin* | mingw* | pw32* | cegcc*) - if test yes != "$GCC"; then - reload_cmds=false - fi - ;; - darwin*) - if test yes = "$GCC"; then - reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' - else - reload_cmds='$LD$reload_flag -o $output$reload_objs' - fi - ;; -esac - - - - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. -set dummy ${ac_tool_prefix}objdump; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_OBJDUMP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$OBJDUMP"; then - ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -OBJDUMP=$ac_cv_prog_OBJDUMP -if test -n "$OBJDUMP"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 -printf "%s\n" "$OBJDUMP" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_OBJDUMP"; then - ac_ct_OBJDUMP=$OBJDUMP - # Extract the first word of "objdump", so it can be a program name with args. -set dummy objdump; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_OBJDUMP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_OBJDUMP"; then - ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_OBJDUMP="objdump" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP -if test -n "$ac_ct_OBJDUMP"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 -printf "%s\n" "$ac_ct_OBJDUMP" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_OBJDUMP" = x; then - OBJDUMP="false" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - OBJDUMP=$ac_ct_OBJDUMP - fi -else - OBJDUMP="$ac_cv_prog_OBJDUMP" -fi - -test -z "$OBJDUMP" && OBJDUMP=objdump - - - - - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 -printf %s "checking how to recognize dependent libraries... " >&6; } -if test ${lt_cv_deplibs_check_method+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_file_magic_cmd='$MAGIC_CMD' -lt_cv_file_magic_test_file= -lt_cv_deplibs_check_method='unknown' -# Need to set the preceding variable on all platforms that support -# interlibrary dependencies. -# 'none' -- dependencies not supported. -# 'unknown' -- same as none, but documents that we really don't know. -# 'pass_all' -- all dependencies passed with no checks. -# 'test_compile' -- check by making test program. -# 'file_magic [[regex]]' -- check by looking for files in library path -# that responds to the $file_magic_cmd with a given extended regex. -# If you have 'file' or equivalent on your system and you're not sure -# whether 'pass_all' will *always* work, you probably want this one. - -case $host_os in -aix[4-9]*) - lt_cv_deplibs_check_method=pass_all - ;; - -beos*) - lt_cv_deplibs_check_method=pass_all - ;; - -bsdi[45]*) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' - lt_cv_file_magic_cmd='/usr/bin/file -L' - lt_cv_file_magic_test_file=/shlib/libc.so - ;; - -cygwin*) - # func_win32_libid is a shell function defined in ltmain.sh - lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' - lt_cv_file_magic_cmd='func_win32_libid' - ;; - -mingw* | pw32*) - # Base MSYS/MinGW do not provide the 'file' command needed by - # func_win32_libid shell function, so use a weaker test based on 'objdump', - # unless we find 'file', for example because we are cross-compiling. - if ( file / ) >/dev/null 2>&1; then - lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' - lt_cv_file_magic_cmd='func_win32_libid' - else - # Keep this pattern in sync with the one in func_win32_libid. - lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' - lt_cv_file_magic_cmd='$OBJDUMP -f' - fi - ;; - -cegcc*) - # use the weaker test based on 'objdump'. See mingw*. - lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' - lt_cv_file_magic_cmd='$OBJDUMP -f' - ;; - -darwin* | rhapsody*) - lt_cv_deplibs_check_method=pass_all - ;; - -freebsd* | dragonfly*) - if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then - case $host_cpu in - i*86 ) - # Not sure whether the presence of OpenBSD here was a mistake. - # Let's accept both of them until this is cleared up. - lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` - ;; - esac - else - lt_cv_deplibs_check_method=pass_all - fi - ;; - -haiku*) - lt_cv_deplibs_check_method=pass_all - ;; - -hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=/usr/bin/file - case $host_cpu in - ia64*) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' - lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so - ;; - hppa*64*) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' - lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl - ;; - *) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' - lt_cv_file_magic_test_file=/usr/lib/libc.sl - ;; - esac - ;; - -interix[3-9]*) - # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' - ;; - -irix5* | irix6* | nonstopux*) - case $LD in - *-32|*"-32 ") libmagic=32-bit;; - *-n32|*"-n32 ") libmagic=N32;; - *-64|*"-64 ") libmagic=64-bit;; - *) libmagic=never-match;; - esac - lt_cv_deplibs_check_method=pass_all - ;; - -# This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - lt_cv_deplibs_check_method=pass_all - ;; - -netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' - fi - ;; - -newos6*) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=/usr/lib/libnls.so - ;; - -*nto* | *qnx*) - lt_cv_deplibs_check_method=pass_all - ;; - -openbsd* | bitrig*) - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' - fi - ;; - -osf3* | osf4* | osf5*) - lt_cv_deplibs_check_method=pass_all - ;; - -rdos*) - lt_cv_deplibs_check_method=pass_all - ;; - -solaris*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - lt_cv_deplibs_check_method=pass_all - ;; - -sysv4 | sysv4.3*) - case $host_vendor in - motorola) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` - ;; - ncr) - lt_cv_deplibs_check_method=pass_all - ;; - sequent) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' - ;; - sni) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" - lt_cv_file_magic_test_file=/lib/libc.so - ;; - siemens) - lt_cv_deplibs_check_method=pass_all - ;; - pc) - lt_cv_deplibs_check_method=pass_all - ;; - esac - ;; - -tpf*) - lt_cv_deplibs_check_method=pass_all - ;; -os2*) - lt_cv_deplibs_check_method=pass_all - ;; -esac - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 -printf "%s\n" "$lt_cv_deplibs_check_method" >&6; } - -file_magic_glob= -want_nocaseglob=no -if test "$build" = "$host"; then - case $host_os in - mingw* | pw32*) - if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then - want_nocaseglob=yes - else - file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` - fi - ;; - esac -fi - -file_magic_cmd=$lt_cv_file_magic_cmd -deplibs_check_method=$lt_cv_deplibs_check_method -test -z "$deplibs_check_method" && deplibs_check_method=unknown - - - - - - - - - - - - - - - - - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. -set dummy ${ac_tool_prefix}dlltool; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_DLLTOOL+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$DLLTOOL"; then - ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -DLLTOOL=$ac_cv_prog_DLLTOOL -if test -n "$DLLTOOL"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 -printf "%s\n" "$DLLTOOL" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_DLLTOOL"; then - ac_ct_DLLTOOL=$DLLTOOL - # Extract the first word of "dlltool", so it can be a program name with args. -set dummy dlltool; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_DLLTOOL+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_DLLTOOL"; then - ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_DLLTOOL="dlltool" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL -if test -n "$ac_ct_DLLTOOL"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 -printf "%s\n" "$ac_ct_DLLTOOL" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_DLLTOOL" = x; then - DLLTOOL="false" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - DLLTOOL=$ac_ct_DLLTOOL - fi -else - DLLTOOL="$ac_cv_prog_DLLTOOL" -fi - -test -z "$DLLTOOL" && DLLTOOL=dlltool - - - - - - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 -printf %s "checking how to associate runtime and link libraries... " >&6; } -if test ${lt_cv_sharedlib_from_linklib_cmd+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_sharedlib_from_linklib_cmd='unknown' - -case $host_os in -cygwin* | mingw* | pw32* | cegcc*) - # two different shell functions defined in ltmain.sh; - # decide which one to use based on capabilities of $DLLTOOL - case `$DLLTOOL --help 2>&1` in - *--identify-strict*) - lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib - ;; - *) - lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback - ;; - esac - ;; -*) - # fallback: assume linklib IS sharedlib - lt_cv_sharedlib_from_linklib_cmd=$ECHO - ;; -esac - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 -printf "%s\n" "$lt_cv_sharedlib_from_linklib_cmd" >&6; } -sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd -test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO - - - - - - - - -if test -n "$ac_tool_prefix"; then - for ac_prog in ar - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_AR+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$AR"; then - ac_cv_prog_AR="$AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_AR="$ac_tool_prefix$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AR=$ac_cv_prog_AR -if test -n "$AR"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 -printf "%s\n" "$AR" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$AR" && break - done -fi -if test -z "$AR"; then - ac_ct_AR=$AR - for ac_prog in ar -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_AR+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_AR"; then - ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AR="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_AR=$ac_cv_prog_ac_ct_AR -if test -n "$ac_ct_AR"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 -printf "%s\n" "$ac_ct_AR" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$ac_ct_AR" && break -done - - if test "x$ac_ct_AR" = x; then - AR="false" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - AR=$ac_ct_AR - fi -fi - -: ${AR=ar} -: ${AR_FLAGS=cr} - - - - - - - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 -printf %s "checking for archiver @FILE support... " >&6; } -if test ${lt_cv_ar_at_file+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_ar_at_file=no - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - echo conftest.$ac_objext > conftest.lst - lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' - { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 - (eval $lt_ar_try) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if test 0 -eq "$ac_status"; then - # Ensure the archiver fails upon bogus file names. - rm -f conftest.$ac_objext libconftest.a - { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 - (eval $lt_ar_try) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if test 0 -ne "$ac_status"; then - lt_cv_ar_at_file=@ - fi - fi - rm -f conftest.* libconftest.a - -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 -printf "%s\n" "$lt_cv_ar_at_file" >&6; } - -if test no = "$lt_cv_ar_at_file"; then - archiver_list_spec= -else - archiver_list_spec=$lt_cv_ar_at_file -fi - - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. -set dummy ${ac_tool_prefix}strip; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_STRIP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$STRIP"; then - ac_cv_prog_STRIP="$STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_STRIP="${ac_tool_prefix}strip" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -STRIP=$ac_cv_prog_STRIP -if test -n "$STRIP"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 -printf "%s\n" "$STRIP" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_STRIP"; then - ac_ct_STRIP=$STRIP - # Extract the first word of "strip", so it can be a program name with args. -set dummy strip; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_STRIP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_STRIP"; then - ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_STRIP="strip" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP -if test -n "$ac_ct_STRIP"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 -printf "%s\n" "$ac_ct_STRIP" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_STRIP" = x; then - STRIP=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - STRIP=$ac_ct_STRIP - fi -else - STRIP="$ac_cv_prog_STRIP" -fi - -test -z "$STRIP" && STRIP=: - - - - - - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_RANLIB+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 -printf "%s\n" "$RANLIB" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_RANLIB+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 -printf "%s\n" "$ac_ct_RANLIB" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_RANLIB" = x; then - RANLIB=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - RANLIB=$ac_ct_RANLIB - fi -else - RANLIB="$ac_cv_prog_RANLIB" -fi - -test -z "$RANLIB" && RANLIB=: - - - - - - -# Determine commands to create old-style static archives. -old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' -old_postinstall_cmds='chmod 644 $oldlib' -old_postuninstall_cmds= - -if test -n "$RANLIB"; then - case $host_os in - bitrig* | openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" - ;; - *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" - ;; - esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" -fi - -case $host_os in - darwin*) - lock_old_archive_extraction=yes ;; - *) - lock_old_archive_extraction=no ;; -esac - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# Check for command to grab the raw symbol name followed by C symbol from nm. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 -printf %s "checking command to parse $NM output from $compiler object... " >&6; } -if test ${lt_cv_sys_global_symbol_pipe+y} -then : - printf %s "(cached) " >&6 -else $as_nop - -# These are sane defaults that work on at least a few old systems. -# [They come from Ultrix. What could be older than Ultrix?!! ;)] - -# Character class describing NM global symbol codes. -symcode='[BCDEGRST]' - -# Regexp to match symbols that can be accessed directly from C. -sympat='\([_A-Za-z][_A-Za-z0-9]*\)' - -# Define system-specific variables. -case $host_os in -aix*) - symcode='[BCDT]' - ;; -cygwin* | mingw* | pw32* | cegcc*) - symcode='[ABCDGISTW]' - ;; -hpux*) - if test ia64 = "$host_cpu"; then - symcode='[ABCDEGRST]' - fi - ;; -irix* | nonstopux*) - symcode='[BCDEGRST]' - ;; -osf*) - symcode='[BCDEGQRST]' - ;; -solaris*) - symcode='[BDRT]' - ;; -sco3.2v5*) - symcode='[DT]' - ;; -sysv4.2uw2*) - symcode='[DT]' - ;; -sysv5* | sco5v6* | unixware* | OpenUNIX*) - symcode='[ABDT]' - ;; -sysv4) - symcode='[DFNSTU]' - ;; -esac - -# If we're using GNU nm, then use its standard symbol codes. -case `$NM -V 2>&1` in -*GNU* | *'with BFD'*) - symcode='[ABCDGIRSTW]' ;; -esac - -if test "$lt_cv_nm_interface" = "MS dumpbin"; then - # Gets list of data symbols to import. - lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" - # Adjust the below global symbol transforms to fixup imported variables. - lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" - lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" - lt_c_name_lib_hook="\ - -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ - -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" -else - # Disable hooks by default. - lt_cv_sys_global_symbol_to_import= - lt_cdecl_hook= - lt_c_name_hook= - lt_c_name_lib_hook= -fi - -# Transform an extracted symbol line into a proper C declaration. -# Some systems (esp. on ia64) link data and code symbols differently, -# so use this general approach. -lt_cv_sys_global_symbol_to_cdecl="sed -n"\ -$lt_cdecl_hook\ -" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ -" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" - -# Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ -$lt_c_name_hook\ -" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ -" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" - -# Transform an extracted symbol line into symbol name with lib prefix and -# symbol address. -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ -$lt_c_name_lib_hook\ -" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ -" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ -" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" - -# Handle CRLF in mingw tool chain -opt_cr= -case $build_os in -mingw*) - opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp - ;; -esac - -# Try without a prefix underscore, then with it. -for ac_symprfx in "" "_"; do - - # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. - symxfrm="\\1 $ac_symprfx\\2 \\2" - - # Write the raw and C identifiers. - if test "$lt_cv_nm_interface" = "MS dumpbin"; then - # Fake it for dumpbin and say T for any non-static function, - # D for any global variable and I for any imported variable. - # Also find C++ and __fastcall symbols from MSVC++, - # which start with @ or ?. - lt_cv_sys_global_symbol_pipe="$AWK '"\ -" {last_section=section; section=\$ 3};"\ -" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ -" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ -" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ -" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ -" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ -" \$ 0!~/External *\|/{next};"\ -" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ -" {if(hide[section]) next};"\ -" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ -" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ -" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ -" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ -" ' prfx=^$ac_symprfx" - else - lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" - fi - lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" - - # Check to see that the pipe works correctly. - pipe_works=no - - rm -f conftest* - cat > conftest.$ac_ext <<_LT_EOF -#ifdef __cplusplus -extern "C" { -#endif -char nm_test_var; -void nm_test_func(void); -void nm_test_func(void){} -#ifdef __cplusplus -} -#endif -int main(){nm_test_var='a';nm_test_func();return(0);} -_LT_EOF - - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - # Now try to grab the symbols. - nlist=conftest.nm - $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5 - if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then - # Try sorting and uniquifying the output. - if sort "$nlist" | uniq > "$nlist"T; then - mv -f "$nlist"T "$nlist" - else - rm -f "$nlist"T - fi - - # Make sure that we snagged all the symbols we need. - if $GREP ' nm_test_var$' "$nlist" >/dev/null; then - if $GREP ' nm_test_func$' "$nlist" >/dev/null; then - cat <<_LT_EOF > conftest.$ac_ext -/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ -#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE -/* DATA imports from DLLs on WIN32 can't be const, because runtime - relocations are performed -- see ld's documentation on pseudo-relocs. */ -# define LT_DLSYM_CONST -#elif defined __osf__ -/* This system does not cope well with relocations in const data. */ -# define LT_DLSYM_CONST -#else -# define LT_DLSYM_CONST const -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -_LT_EOF - # Now generate the symbol file. - eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' - - cat <<_LT_EOF >> conftest.$ac_ext - -/* The mapping between symbol names and symbols. */ -LT_DLSYM_CONST struct { - const char *name; - void *address; -} -lt__PROGRAM__LTX_preloaded_symbols[] = -{ - { "@PROGRAM@", (void *) 0 }, -_LT_EOF - $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext - cat <<\_LT_EOF >> conftest.$ac_ext - {0, (void *) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt__PROGRAM__LTX_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif -_LT_EOF - # Now try linking the two files. - mv conftest.$ac_objext conftstm.$ac_objext - lt_globsym_save_LIBS=$LIBS - lt_globsym_save_CFLAGS=$CFLAGS - LIBS=conftstm.$ac_objext - CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 - (eval $ac_link) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && test -s conftest$ac_exeext; then - pipe_works=yes - fi - LIBS=$lt_globsym_save_LIBS - CFLAGS=$lt_globsym_save_CFLAGS - else - echo "cannot find nm_test_func in $nlist" >&5 - fi - else - echo "cannot find nm_test_var in $nlist" >&5 - fi - else - echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 - fi - else - echo "$progname: failed program was:" >&5 - cat conftest.$ac_ext >&5 - fi - rm -rf conftest* conftst* - - # Do not use the global_symbol_pipe unless it works. - if test yes = "$pipe_works"; then - break - else - lt_cv_sys_global_symbol_pipe= - fi -done - -fi - -if test -z "$lt_cv_sys_global_symbol_pipe"; then - lt_cv_sys_global_symbol_to_cdecl= -fi -if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: failed" >&5 -printf "%s\n" "failed" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ok" >&5 -printf "%s\n" "ok" >&6; } -fi - -# Response file support. -if test "$lt_cv_nm_interface" = "MS dumpbin"; then - nm_file_list_spec='@' -elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then - nm_file_list_spec='@' -fi - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 -printf %s "checking for sysroot... " >&6; } - -# Check whether --with-sysroot was given. -if test ${with_sysroot+y} -then : - withval=$with_sysroot; -else $as_nop - with_sysroot=no -fi - - -lt_sysroot= -case $with_sysroot in #( - yes) - if test yes = "$GCC"; then - lt_sysroot=`$CC --print-sysroot 2>/dev/null` - fi - ;; #( - /*) - lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` - ;; #( - no|'') - ;; #( - *) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 -printf "%s\n" "$with_sysroot" >&6; } - as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 - ;; -esac - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 -printf "%s\n" "${lt_sysroot:-no}" >&6; } - - - - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 -printf %s "checking for a working dd... " >&6; } -if test ${ac_cv_path_lt_DD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - printf 0123456789abcdef0123456789abcdef >conftest.i -cat conftest.i conftest.i >conftest2.i -: ${lt_DD:=$DD} -if test -z "$lt_DD"; then - ac_path_lt_DD_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_prog in dd - do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_lt_DD="$as_dir$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_lt_DD" || continue -if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then - cmp -s conftest.i conftest.out \ - && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: -fi - $ac_path_lt_DD_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_lt_DD"; then - : - fi -else - ac_cv_path_lt_DD=$lt_DD -fi - -rm -f conftest.i conftest2.i conftest.out -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 -printf "%s\n" "$ac_cv_path_lt_DD" >&6; } - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 -printf %s "checking how to truncate binary pipes... " >&6; } -if test ${lt_cv_truncate_bin+y} -then : - printf %s "(cached) " >&6 -else $as_nop - printf 0123456789abcdef0123456789abcdef >conftest.i -cat conftest.i conftest.i >conftest2.i -lt_cv_truncate_bin= -if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then - cmp -s conftest.i conftest.out \ - && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" -fi -rm -f conftest.i conftest2.i conftest.out -test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 -printf "%s\n" "$lt_cv_truncate_bin" >&6; } - - - - - - - -# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. -func_cc_basename () -{ - for cc_temp in $*""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac - done - func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` -} - -# Check whether --enable-libtool-lock was given. -if test ${enable_libtool_lock+y} -then : - enableval=$enable_libtool_lock; -fi - -test no = "$enable_libtool_lock" || enable_libtool_lock=yes - -# Some flags need to be propagated to the compiler or linker for good -# libtool support. -case $host in -ia64-*-hpux*) - # Find out what ABI is being produced by ac_compile, and set mode - # options accordingly. - echo 'int i;' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - case `/usr/bin/file conftest.$ac_objext` in - *ELF-32*) - HPUX_IA64_MODE=32 - ;; - *ELF-64*) - HPUX_IA64_MODE=64 - ;; - esac - fi - rm -rf conftest* - ;; -*-*-irix6*) - # Find out what ABI is being produced by ac_compile, and set linker - # options accordingly. - echo '#line '$LINENO' "configure"' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - if test yes = "$lt_cv_prog_gnu_ld"; then - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -melf32bsmip" - ;; - *N32*) - LD="${LD-ld} -melf32bmipn32" - ;; - *64-bit*) - LD="${LD-ld} -melf64bmip" - ;; - esac - else - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -32" - ;; - *N32*) - LD="${LD-ld} -n32" - ;; - *64-bit*) - LD="${LD-ld} -64" - ;; - esac - fi - fi - rm -rf conftest* - ;; - -mips64*-*linux*) - # Find out what ABI is being produced by ac_compile, and set linker - # options accordingly. - echo '#line '$LINENO' "configure"' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - emul=elf - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - emul="${emul}32" - ;; - *64-bit*) - emul="${emul}64" - ;; - esac - case `/usr/bin/file conftest.$ac_objext` in - *MSB*) - emul="${emul}btsmip" - ;; - *LSB*) - emul="${emul}ltsmip" - ;; - esac - case `/usr/bin/file conftest.$ac_objext` in - *N32*) - emul="${emul}n32" - ;; - esac - LD="${LD-ld} -m $emul" - fi - rm -rf conftest* - ;; - -x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ -s390*-*linux*|s390*-*tpf*|sparc*-*linux*) - # Find out what ABI is being produced by ac_compile, and set linker - # options accordingly. Note that the listed cases only cover the - # situations where additional linker options are needed (such as when - # doing 32-bit compilation for a host where ld defaults to 64-bit, or - # vice versa); the common cases where no linker options are needed do - # not appear in the list. - echo 'int i;' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - case `/usr/bin/file conftest.o` in - *32-bit*) - case $host in - x86_64-*kfreebsd*-gnu) - LD="${LD-ld} -m elf_i386_fbsd" - ;; - x86_64-*linux*) - case `/usr/bin/file conftest.o` in - *x86-64*) - LD="${LD-ld} -m elf32_x86_64" - ;; - *) - LD="${LD-ld} -m elf_i386" - ;; - esac - ;; - powerpc64le-*linux*) - LD="${LD-ld} -m elf32lppclinux" - ;; - powerpc64-*linux*) - LD="${LD-ld} -m elf32ppclinux" - ;; - s390x-*linux*) - LD="${LD-ld} -m elf_s390" - ;; - sparc64-*linux*) - LD="${LD-ld} -m elf32_sparc" - ;; - esac - ;; - *64-bit*) - case $host in - x86_64-*kfreebsd*-gnu) - LD="${LD-ld} -m elf_x86_64_fbsd" - ;; - x86_64-*linux*) - LD="${LD-ld} -m elf_x86_64" - ;; - powerpcle-*linux*) - LD="${LD-ld} -m elf64lppc" - ;; - powerpc-*linux*) - LD="${LD-ld} -m elf64ppc" - ;; - s390*-*linux*|s390*-*tpf*) - LD="${LD-ld} -m elf64_s390" - ;; - sparc*-*linux*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -*-*-sco3.2v5*) - # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS=$CFLAGS - CFLAGS="$CFLAGS -belf" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 -printf %s "checking whether the C compiler needs -belf... " >&6; } -if test ${lt_cv_cc_needs_belf+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - lt_cv_cc_needs_belf=yes -else $as_nop - lt_cv_cc_needs_belf=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 -printf "%s\n" "$lt_cv_cc_needs_belf" >&6; } - if test yes != "$lt_cv_cc_needs_belf"; then - # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS=$SAVE_CFLAGS - fi - ;; -*-*solaris*) - # Find out what ABI is being produced by ac_compile, and set linker - # options accordingly. - echo 'int i;' > conftest.$ac_ext - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - case `/usr/bin/file conftest.o` in - *64-bit*) - case $lt_cv_prog_gnu_ld in - yes*) - case $host in - i?86-*-solaris*|x86_64-*-solaris*) - LD="${LD-ld} -m elf_x86_64" - ;; - sparc*-*-solaris*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - # GNU ld 2.21 introduced _sol2 emulations. Use them if available. - if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then - LD=${LD-ld}_sol2 - fi - ;; - *) - if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then - LD="${LD-ld} -64" - fi - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; -esac - -need_locks=$enable_libtool_lock - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. -set dummy ${ac_tool_prefix}mt; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_MANIFEST_TOOL+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$MANIFEST_TOOL"; then - ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL -if test -n "$MANIFEST_TOOL"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 -printf "%s\n" "$MANIFEST_TOOL" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_MANIFEST_TOOL"; then - ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL - # Extract the first word of "mt", so it can be a program name with args. -set dummy mt; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_MANIFEST_TOOL+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_MANIFEST_TOOL"; then - ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL -if test -n "$ac_ct_MANIFEST_TOOL"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 -printf "%s\n" "$ac_ct_MANIFEST_TOOL" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_MANIFEST_TOOL" = x; then - MANIFEST_TOOL=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL - fi -else - MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" -fi - -test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 -printf %s "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } -if test ${lt_cv_path_mainfest_tool+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_path_mainfest_tool=no - echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 - $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out - cat conftest.err >&5 - if $GREP 'Manifest Tool' conftest.out > /dev/null; then - lt_cv_path_mainfest_tool=yes - fi - rm -f conftest* -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 -printf "%s\n" "$lt_cv_path_mainfest_tool" >&6; } -if test yes != "$lt_cv_path_mainfest_tool"; then - MANIFEST_TOOL=: -fi - - - - - - - case $host_os in - rhapsody* | darwin*) - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. -set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_DSYMUTIL+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$DSYMUTIL"; then - ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -DSYMUTIL=$ac_cv_prog_DSYMUTIL -if test -n "$DSYMUTIL"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 -printf "%s\n" "$DSYMUTIL" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_DSYMUTIL"; then - ac_ct_DSYMUTIL=$DSYMUTIL - # Extract the first word of "dsymutil", so it can be a program name with args. -set dummy dsymutil; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_DSYMUTIL+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_DSYMUTIL"; then - ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL -if test -n "$ac_ct_DSYMUTIL"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 -printf "%s\n" "$ac_ct_DSYMUTIL" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_DSYMUTIL" = x; then - DSYMUTIL=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - DSYMUTIL=$ac_ct_DSYMUTIL - fi -else - DSYMUTIL="$ac_cv_prog_DSYMUTIL" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. -set dummy ${ac_tool_prefix}nmedit; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_NMEDIT+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$NMEDIT"; then - ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -NMEDIT=$ac_cv_prog_NMEDIT -if test -n "$NMEDIT"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 -printf "%s\n" "$NMEDIT" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_NMEDIT"; then - ac_ct_NMEDIT=$NMEDIT - # Extract the first word of "nmedit", so it can be a program name with args. -set dummy nmedit; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_NMEDIT+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_NMEDIT"; then - ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_NMEDIT="nmedit" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT -if test -n "$ac_ct_NMEDIT"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 -printf "%s\n" "$ac_ct_NMEDIT" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_NMEDIT" = x; then - NMEDIT=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - NMEDIT=$ac_ct_NMEDIT - fi -else - NMEDIT="$ac_cv_prog_NMEDIT" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. -set dummy ${ac_tool_prefix}lipo; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_LIPO+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$LIPO"; then - ac_cv_prog_LIPO="$LIPO" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_LIPO="${ac_tool_prefix}lipo" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -LIPO=$ac_cv_prog_LIPO -if test -n "$LIPO"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 -printf "%s\n" "$LIPO" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_LIPO"; then - ac_ct_LIPO=$LIPO - # Extract the first word of "lipo", so it can be a program name with args. -set dummy lipo; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_LIPO+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_LIPO"; then - ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_LIPO="lipo" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO -if test -n "$ac_ct_LIPO"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 -printf "%s\n" "$ac_ct_LIPO" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_LIPO" = x; then - LIPO=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - LIPO=$ac_ct_LIPO - fi -else - LIPO="$ac_cv_prog_LIPO" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. -set dummy ${ac_tool_prefix}otool; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_OTOOL+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$OTOOL"; then - ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_OTOOL="${ac_tool_prefix}otool" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -OTOOL=$ac_cv_prog_OTOOL -if test -n "$OTOOL"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 -printf "%s\n" "$OTOOL" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_OTOOL"; then - ac_ct_OTOOL=$OTOOL - # Extract the first word of "otool", so it can be a program name with args. -set dummy otool; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_OTOOL+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_OTOOL"; then - ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_OTOOL="otool" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL -if test -n "$ac_ct_OTOOL"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 -printf "%s\n" "$ac_ct_OTOOL" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_OTOOL" = x; then - OTOOL=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - OTOOL=$ac_ct_OTOOL - fi -else - OTOOL="$ac_cv_prog_OTOOL" -fi - - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. -set dummy ${ac_tool_prefix}otool64; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_OTOOL64+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$OTOOL64"; then - ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -OTOOL64=$ac_cv_prog_OTOOL64 -if test -n "$OTOOL64"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 -printf "%s\n" "$OTOOL64" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_OTOOL64"; then - ac_ct_OTOOL64=$OTOOL64 - # Extract the first word of "otool64", so it can be a program name with args. -set dummy otool64; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_OTOOL64+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_OTOOL64"; then - ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_OTOOL64="otool64" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 -if test -n "$ac_ct_OTOOL64"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 -printf "%s\n" "$ac_ct_OTOOL64" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_OTOOL64" = x; then - OTOOL64=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - OTOOL64=$ac_ct_OTOOL64 - fi -else - OTOOL64="$ac_cv_prog_OTOOL64" -fi - - - - - - - - - - - - - - - - - - - - - - - - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 -printf %s "checking for -single_module linker flag... " >&6; } -if test ${lt_cv_apple_cc_single_mod+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_apple_cc_single_mod=no - if test -z "$LT_MULTI_MODULE"; then - # By default we will add the -single_module flag. You can override - # by either setting the environment variable LT_MULTI_MODULE - # non-empty at configure time, or by adding -multi_module to the - # link flags. - rm -rf libconftest.dylib* - echo "int foo(void){return 1;}" > conftest.c - echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ --dynamiclib -Wl,-single_module conftest.c" >&5 - $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ - -dynamiclib -Wl,-single_module conftest.c 2>conftest.err - _lt_result=$? - # If there is a non-empty error log, and "single_module" - # appears in it, assume the flag caused a linker warning - if test -s conftest.err && $GREP single_module conftest.err; then - cat conftest.err >&5 - # Otherwise, if the output was created with a 0 exit code from - # the compiler, it worked. - elif test -f libconftest.dylib && test 0 = "$_lt_result"; then - lt_cv_apple_cc_single_mod=yes - else - cat conftest.err >&5 - fi - rm -rf libconftest.dylib* - rm -f conftest.* - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 -printf "%s\n" "$lt_cv_apple_cc_single_mod" >&6; } - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 -printf %s "checking for -exported_symbols_list linker flag... " >&6; } -if test ${lt_cv_ld_exported_symbols_list+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_ld_exported_symbols_list=no - save_LDFLAGS=$LDFLAGS - echo "_main" > conftest.sym - LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - lt_cv_ld_exported_symbols_list=yes -else $as_nop - lt_cv_ld_exported_symbols_list=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - LDFLAGS=$save_LDFLAGS - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 -printf "%s\n" "$lt_cv_ld_exported_symbols_list" >&6; } - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 -printf %s "checking for -force_load linker flag... " >&6; } -if test ${lt_cv_ld_force_load+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_ld_force_load=no - cat > conftest.c << _LT_EOF -int forced_loaded() { return 2;} -_LT_EOF - echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 - $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 - echo "$AR cr libconftest.a conftest.o" >&5 - $AR cr libconftest.a conftest.o 2>&5 - echo "$RANLIB libconftest.a" >&5 - $RANLIB libconftest.a 2>&5 - cat > conftest.c << _LT_EOF -int main() { return 0;} -_LT_EOF - echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 - $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err - _lt_result=$? - if test -s conftest.err && $GREP force_load conftest.err; then - cat conftest.err >&5 - elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then - lt_cv_ld_force_load=yes - else - cat conftest.err >&5 - fi - rm -f conftest.err libconftest.a conftest conftest.c - rm -rf conftest.dSYM - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 -printf "%s\n" "$lt_cv_ld_force_load" >&6; } - case $host_os in - rhapsody* | darwin1.[012]) - _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; - darwin1.*) - _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - darwin*) # darwin 5.x on - # if running on 10.5 or later, the deployment target defaults - # to the OS version, if on x86, and 10.4, the deployment - # target defaults to 10.4. Don't you love it? - case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in - 10.0,*86*-darwin8*|10.0,*-darwin[912]*) - _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; - 10.[012][,.]*) - _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - 10.*|11.*) - _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; - esac - ;; - esac - if test yes = "$lt_cv_apple_cc_single_mod"; then - _lt_dar_single_mod='$single_module' - fi - if test yes = "$lt_cv_ld_exported_symbols_list"; then - _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' - else - _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' - fi - if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then - _lt_dsymutil='~$DSYMUTIL $lib || :' - else - _lt_dsymutil= - fi - ;; - esac - -# func_munge_path_list VARIABLE PATH -# ----------------------------------- -# VARIABLE is name of variable containing _space_ separated list of -# directories to be munged by the contents of PATH, which is string -# having a format: -# "DIR[:DIR]:" -# string "DIR[ DIR]" will be prepended to VARIABLE -# ":DIR[:DIR]" -# string "DIR[ DIR]" will be appended to VARIABLE -# "DIRP[:DIRP]::[DIRA:]DIRA" -# string "DIRP[ DIRP]" will be prepended to VARIABLE and string -# "DIRA[ DIRA]" will be appended to VARIABLE -# "DIR[:DIR]" -# VARIABLE will be replaced by "DIR[ DIR]" -func_munge_path_list () -{ - case x$2 in - x) - ;; - *:) - eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" - ;; - x:*) - eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" - ;; - *::*) - eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" - eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" - ;; - *) - eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" - ;; - esac -} - -ac_header= ac_cache= -for ac_item in $ac_header_c_list -do - if test $ac_cache; then - ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" - if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then - printf "%s\n" "#define $ac_item 1" >> confdefs.h - fi - ac_header= ac_cache= - elif test $ac_header; then - ac_cache=$ac_item - else - ac_header=$ac_item - fi -done - - - - - - - - -if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes -then : - -printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h - -fi -ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default -" -if test "x$ac_cv_header_dlfcn_h" = xyes -then : - printf "%s\n" "#define HAVE_DLFCN_H 1" >>confdefs.h - -fi - - - - - -# Set options - - - - enable_dlopen=no - - - enable_win32_dll=no - - - # Check whether --enable-shared was given. -if test ${enable_shared+y} -then : - enableval=$enable_shared; p=${PACKAGE-default} - case $enableval in - yes) enable_shared=yes ;; - no) enable_shared=no ;; - *) - enable_shared=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, - for pkg in $enableval; do - IFS=$lt_save_ifs - if test "X$pkg" = "X$p"; then - enable_shared=yes - fi - done - IFS=$lt_save_ifs - ;; - esac -else $as_nop - enable_shared=yes -fi - - - - - - - - - - # Check whether --enable-static was given. -if test ${enable_static+y} -then : - enableval=$enable_static; p=${PACKAGE-default} - case $enableval in - yes) enable_static=yes ;; - no) enable_static=no ;; - *) - enable_static=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, - for pkg in $enableval; do - IFS=$lt_save_ifs - if test "X$pkg" = "X$p"; then - enable_static=yes - fi - done - IFS=$lt_save_ifs - ;; - esac -else $as_nop - enable_static=yes -fi - - - - - - - - - - -# Check whether --with-pic was given. -if test ${with_pic+y} -then : - withval=$with_pic; lt_p=${PACKAGE-default} - case $withval in - yes|no) pic_mode=$withval ;; - *) - pic_mode=default - # Look at the argument we got. We use all the common list separators. - lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, - for lt_pkg in $withval; do - IFS=$lt_save_ifs - if test "X$lt_pkg" = "X$lt_p"; then - pic_mode=yes - fi - done - IFS=$lt_save_ifs - ;; - esac -else $as_nop - pic_mode=default -fi - - - - - - - - - # Check whether --enable-fast-install was given. -if test ${enable_fast_install+y} -then : - enableval=$enable_fast_install; p=${PACKAGE-default} - case $enableval in - yes) enable_fast_install=yes ;; - no) enable_fast_install=no ;; - *) - enable_fast_install=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, - for pkg in $enableval; do - IFS=$lt_save_ifs - if test "X$pkg" = "X$p"; then - enable_fast_install=yes - fi - done - IFS=$lt_save_ifs - ;; - esac -else $as_nop - enable_fast_install=yes -fi - - - - - - - - - shared_archive_member_spec= -case $host,$enable_shared in -power*-*-aix[5-9]*,yes) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 -printf %s "checking which variant of shared library versioning to provide... " >&6; } - -# Check whether --with-aix-soname was given. -if test ${with_aix_soname+y} -then : - withval=$with_aix_soname; case $withval in - aix|svr4|both) - ;; - *) - as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 - ;; - esac - lt_cv_with_aix_soname=$with_aix_soname -else $as_nop - if test ${lt_cv_with_aix_soname+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_with_aix_soname=aix -fi - - with_aix_soname=$lt_cv_with_aix_soname -fi - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 -printf "%s\n" "$with_aix_soname" >&6; } - if test aix != "$with_aix_soname"; then - # For the AIX way of multilib, we name the shared archive member - # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', - # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. - # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, - # the AIX toolchain works better with OBJECT_MODE set (default 32). - if test 64 = "${OBJECT_MODE-32}"; then - shared_archive_member_spec=shr_64 - else - shared_archive_member_spec=shr - fi - fi - ;; -*) - with_aix_soname=aix - ;; -esac - - - - - - - - - - -# This can be used to rebuild libtool when needed -LIBTOOL_DEPS=$ltmain - -# Always use our own libtool. -LIBTOOL='$(SHELL) $(top_builddir)/libtool' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -test -z "$LN_S" && LN_S="ln -s" - - - - - - - - - - - - - - -if test -n "${ZSH_VERSION+set}"; then - setopt NO_GLOB_SUBST -fi - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 -printf %s "checking for objdir... " >&6; } -if test ${lt_cv_objdir+y} -then : - printf %s "(cached) " >&6 -else $as_nop - rm -f .libs 2>/dev/null -mkdir .libs 2>/dev/null -if test -d .libs; then - lt_cv_objdir=.libs -else - # MS-DOS does not allow filenames that begin with a dot. - lt_cv_objdir=_libs -fi -rmdir .libs 2>/dev/null -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 -printf "%s\n" "$lt_cv_objdir" >&6; } -objdir=$lt_cv_objdir - - - - - -printf "%s\n" "#define LT_OBJDIR \"$lt_cv_objdir/\"" >>confdefs.h - - - - -case $host_os in -aix3*) - # AIX sometimes has problems with the GCC collect2 program. For some - # reason, if we set the COLLECT_NAMES environment variable, the problems - # vanish in a puff of smoke. - if test set != "${COLLECT_NAMES+set}"; then - COLLECT_NAMES= - export COLLECT_NAMES - fi - ;; -esac - -# Global variables: -ofile=libtool -can_build_shared=yes - -# All known linkers require a '.a' archive for static linking (except MSVC, -# which needs '.lib'). -libext=a - -with_gnu_ld=$lt_cv_prog_gnu_ld - -old_CC=$CC -old_CFLAGS=$CFLAGS - -# Set sane defaults for various variables -test -z "$CC" && CC=cc -test -z "$LTCC" && LTCC=$CC -test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS -test -z "$LD" && LD=ld -test -z "$ac_objext" && ac_objext=o - -func_cc_basename $compiler -cc_basename=$func_cc_basename_result - - -# Only perform the check for file, if the check method requires it -test -z "$MAGIC_CMD" && MAGIC_CMD=file -case $deplibs_check_method in -file_magic*) - if test "$file_magic_cmd" = '$MAGIC_CMD'; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 -printf %s "checking for ${ac_tool_prefix}file... " >&6; } -if test ${lt_cv_path_MAGIC_CMD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - case $MAGIC_CMD in -[\\/*] | ?:[\\/]*) - lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD=$MAGIC_CMD - lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR - ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" - for ac_dir in $ac_dummy; do - IFS=$lt_save_ifs - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/${ac_tool_prefix}file"; then - lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD=$lt_cv_path_MAGIC_CMD - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <<_LT_EOF 1>&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -_LT_EOF - fi ;; - esac - fi - break - fi - done - IFS=$lt_save_ifs - MAGIC_CMD=$lt_save_MAGIC_CMD - ;; -esac -fi - -MAGIC_CMD=$lt_cv_path_MAGIC_CMD -if test -n "$MAGIC_CMD"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 -printf "%s\n" "$MAGIC_CMD" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - - - -if test -z "$lt_cv_path_MAGIC_CMD"; then - if test -n "$ac_tool_prefix"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for file" >&5 -printf %s "checking for file... " >&6; } -if test ${lt_cv_path_MAGIC_CMD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - case $MAGIC_CMD in -[\\/*] | ?:[\\/]*) - lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD=$MAGIC_CMD - lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR - ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" - for ac_dir in $ac_dummy; do - IFS=$lt_save_ifs - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/file"; then - lt_cv_path_MAGIC_CMD=$ac_dir/"file" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD=$lt_cv_path_MAGIC_CMD - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <<_LT_EOF 1>&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org - -_LT_EOF - fi ;; - esac - fi - break - fi - done - IFS=$lt_save_ifs - MAGIC_CMD=$lt_save_MAGIC_CMD - ;; -esac -fi - -MAGIC_CMD=$lt_cv_path_MAGIC_CMD -if test -n "$MAGIC_CMD"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 -printf "%s\n" "$MAGIC_CMD" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - else - MAGIC_CMD=: - fi -fi - - fi - ;; -esac - -# Use C for the default configuration in the libtool script - -lt_save_CC=$CC -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -# Source file extension for C test sources. -ac_ext=c - -# Object file extension for compiled C test sources. -objext=o -objext=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;" - -# Code to be used in simple link tests -lt_simple_link_test_code='int main(){return(0);}' - - - - - - - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - -# Save the default compiler, since it gets overwritten when the other -# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. -compiler_DEFAULT=$CC - -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -echo "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$RM conftest* - -ac_outfile=conftest.$ac_objext -echo "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$RM -r conftest* - - -## CAVEAT EMPTOR: -## There is no encapsulation within the following macros, do not change -## the running order or otherwise move them around unless you know exactly -## what you are doing... -if test -n "$compiler"; then - -lt_prog_compiler_no_builtin_flag= - -if test yes = "$GCC"; then - case $cc_basename in - nvcc*) - lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; - *) - lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; - esac - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 -printf %s "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } -if test ${lt_cv_prog_compiler_rtti_exceptions+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_rtti_exceptions=no - ac_outfile=conftest.$ac_objext - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_rtti_exceptions=yes - fi - fi - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 -printf "%s\n" "$lt_cv_prog_compiler_rtti_exceptions" >&6; } - -if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then - lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" -else - : -fi - -fi - - - - - - - lt_prog_compiler_wl= -lt_prog_compiler_pic= -lt_prog_compiler_static= - - - if test yes = "$GCC"; then - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_static='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test ia64 = "$host_cpu"; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static='-Bstatic' - fi - lt_prog_compiler_pic='-fPIC' - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - lt_prog_compiler_pic='-fPIC' - ;; - m68k) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the '-m68020' flag to GCC prevents building anything better, - # like '-m68040'. - lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' - ;; - esac - ;; - - beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - - mingw* | cygwin* | pw32* | os2* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - # Although the cygwin gcc ignores -fPIC, still need this for old-style - # (--disable-auto-import) libraries - lt_prog_compiler_pic='-DDLL_EXPORT' - case $host_os in - os2*) - lt_prog_compiler_static='$wl-static' - ;; - esac - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic='-fno-common' - ;; - - haiku*) - # PIC is the default for Haiku. - # The "-static" flag exists, but is broken. - lt_prog_compiler_static= - ;; - - hpux*) - # PIC is the default for 64-bit PA HP-UX, but not for 32-bit - # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag - # sets the default TLS model and affects inlining. - case $host_cpu in - hppa*64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic='-fPIC' - ;; - esac - ;; - - interix[3-9]*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - lt_prog_compiler_can_build_shared=no - enable_shared=no - ;; - - *nto* | *qnx*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - lt_prog_compiler_pic='-fPIC -shared' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic=-Kconform_pic - fi - ;; - - *) - lt_prog_compiler_pic='-fPIC' - ;; - esac - - case $cc_basename in - nvcc*) # Cuda Compiler Driver 2.2 - lt_prog_compiler_wl='-Xlinker ' - if test -n "$lt_prog_compiler_pic"; then - lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" - fi - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - lt_prog_compiler_wl='-Wl,' - if test ia64 = "$host_cpu"; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static='-Bstatic' - else - lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' - fi - ;; - - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic='-fno-common' - case $cc_basename in - nagfor*) - # NAG Fortran compiler - lt_prog_compiler_wl='-Wl,-Wl,,' - lt_prog_compiler_pic='-PIC' - lt_prog_compiler_static='-Bstatic' - ;; - esac - ;; - - mingw* | cygwin* | pw32* | os2* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic='-DDLL_EXPORT' - case $host_os in - os2*) - lt_prog_compiler_static='$wl-static' - ;; - esac - ;; - - hpux9* | hpux10* | hpux11*) - lt_prog_compiler_wl='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - lt_prog_compiler_static='$wl-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - lt_prog_compiler_wl='-Wl,' - # PIC (with -KPIC) is the default. - lt_prog_compiler_static='-non_shared' - ;; - - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - # old Intel for x86_64, which still supported -KPIC. - ecc*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-static' - ;; - # flang / f18. f95 an alias for gfortran or flang on Debian - flang* | f18* | f95*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fPIC' - lt_prog_compiler_static='-static' - ;; - # icc used to be incompatible with GCC. - # ICC 10 doesn't accept -KPIC any more. - icc* | ifort*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fPIC' - lt_prog_compiler_static='-static' - ;; - # Lahey Fortran 8.1. - lf95*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='--shared' - lt_prog_compiler_static='--static' - ;; - nagfor*) - # NAG Fortran compiler - lt_prog_compiler_wl='-Wl,-Wl,,' - lt_prog_compiler_pic='-PIC' - lt_prog_compiler_static='-Bstatic' - ;; - tcc*) - # Fabrice Bellard et al's Tiny C Compiler - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fPIC' - lt_prog_compiler_static='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fpic' - lt_prog_compiler_static='-Bstatic' - ;; - ccc*) - lt_prog_compiler_wl='-Wl,' - # All Alpha code is PIC. - lt_prog_compiler_static='-non_shared' - ;; - xl* | bgxl* | bgf* | mpixl*) - # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-qpic' - lt_prog_compiler_static='-qstaticlink' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) - # Sun Fortran 8.3 passes all unrecognized flags to the linker - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - lt_prog_compiler_wl='' - ;; - *Sun\ F* | *Sun*Fortran*) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - lt_prog_compiler_wl='-Qoption ld ' - ;; - *Sun\ C*) - # Sun C 5.9 - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - lt_prog_compiler_wl='-Wl,' - ;; - *Intel*\ [CF]*Compiler*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fPIC' - lt_prog_compiler_static='-static' - ;; - *Portland\ Group*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fpic' - lt_prog_compiler_static='-Bstatic' - ;; - esac - ;; - esac - ;; - - newsos6) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - *nto* | *qnx*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - lt_prog_compiler_pic='-fPIC -shared' - ;; - - osf3* | osf4* | osf5*) - lt_prog_compiler_wl='-Wl,' - # All OSF/1 code is PIC. - lt_prog_compiler_static='-non_shared' - ;; - - rdos*) - lt_prog_compiler_static='-non_shared' - ;; - - solaris*) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - case $cc_basename in - f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) - lt_prog_compiler_wl='-Qoption ld ';; - *) - lt_prog_compiler_wl='-Wl,';; - esac - ;; - - sunos4*) - lt_prog_compiler_wl='-Qoption ld ' - lt_prog_compiler_pic='-PIC' - lt_prog_compiler_static='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic='-Kconform_pic' - lt_prog_compiler_static='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - - unicos*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_can_build_shared=no - ;; - - uts4*) - lt_prog_compiler_pic='-pic' - lt_prog_compiler_static='-Bstatic' - ;; - - *) - lt_prog_compiler_can_build_shared=no - ;; - esac - fi - -case $host_os in - # For platforms that do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic= - ;; - *) - lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" - ;; -esac - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 -printf %s "checking for $compiler option to produce PIC... " >&6; } -if test ${lt_cv_prog_compiler_pic+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_pic=$lt_prog_compiler_pic -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 -printf "%s\n" "$lt_cv_prog_compiler_pic" >&6; } -lt_prog_compiler_pic=$lt_cv_prog_compiler_pic - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 -printf %s "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } -if test ${lt_cv_prog_compiler_pic_works+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_pic_works=no - ac_outfile=conftest.$ac_objext - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_pic_works=yes - fi - fi - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 -printf "%s\n" "$lt_cv_prog_compiler_pic_works" >&6; } - -if test yes = "$lt_cv_prog_compiler_pic_works"; then - case $lt_prog_compiler_pic in - "" | " "*) ;; - *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; - esac -else - lt_prog_compiler_pic= - lt_prog_compiler_can_build_shared=no -fi - -fi - - - - - - - - - - - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -printf %s "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } -if test ${lt_cv_prog_compiler_static_works+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_static_works=no - save_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - echo "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_static_works=yes - fi - else - lt_cv_prog_compiler_static_works=yes - fi - fi - $RM -r conftest* - LDFLAGS=$save_LDFLAGS - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 -printf "%s\n" "$lt_cv_prog_compiler_static_works" >&6; } - -if test yes = "$lt_cv_prog_compiler_static_works"; then - : -else - lt_prog_compiler_static= -fi - - - - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 -printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } -if test ${lt_cv_prog_compiler_c_o+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_c_o=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o=yes - fi - fi - chmod u+w . 2>&5 - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 -printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; } - - - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 -printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } -if test ${lt_cv_prog_compiler_c_o+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_c_o=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o=yes - fi - fi - chmod u+w . 2>&5 - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 -printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; } - - - - -hard_links=nottested -if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then - # do not overwrite the value of need_locks provided by the user - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 -printf %s "checking if we can lock with hard links... " >&6; } - hard_links=yes - $RM conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 -printf "%s\n" "$hard_links" >&6; } - if test no = "$hard_links"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 -printf "%s\n" "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - - - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -printf %s "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } - - runpath_var= - allow_undefined_flag= - always_export_symbols=no - archive_cmds= - archive_expsym_cmds= - compiler_needs_object=no - enable_shared_with_static_runtimes=no - export_dynamic_flag_spec= - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - hardcode_automatic=no - hardcode_direct=no - hardcode_direct_absolute=no - hardcode_libdir_flag_spec= - hardcode_libdir_separator= - hardcode_minus_L=no - hardcode_shlibpath_var=unsupported - inherit_rpath=no - link_all_deplibs=unknown - module_cmds= - module_expsym_cmds= - old_archive_from_new_cmds= - old_archive_from_expsyms_cmds= - thread_safe_flag_spec= - whole_archive_flag_spec= - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - include_expsyms= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ' (' and ')$', so one must not match beginning or - # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', - # as well as any symbol that contains 'd'. - exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - # Exclude shared library initialization/finalization symbols. - extract_expsyms_cmds= - - case $host_os in - cygwin* | mingw* | pw32* | cegcc*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test yes != "$GCC"; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd* | bitrig*) - with_gnu_ld=no - ;; - linux* | k*bsd*-gnu | gnu*) - link_all_deplibs=no - ;; - esac - - ld_shlibs=yes - - # On some targets, GNU ld is compatible enough with the native linker - # that we're better off using the native interface for both. - lt_use_gnu_ld_interface=no - if test yes = "$with_gnu_ld"; then - case $host_os in - aix*) - # The AIX port of GNU ld has always aspired to compatibility - # with the native linker. However, as the warning in the GNU ld - # block says, versions before 2.19.5* couldn't really create working - # shared libraries, regardless of the interface used. - case `$LD -v 2>&1` in - *\ \(GNU\ Binutils\)\ 2.19.5*) ;; - *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; - *\ \(GNU\ Binutils\)\ [3-9]*) ;; - *) - lt_use_gnu_ld_interface=yes - ;; - esac - ;; - *) - lt_use_gnu_ld_interface=yes - ;; - esac - fi - - if test yes = "$lt_use_gnu_ld_interface"; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='$wl' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' - export_dynamic_flag_spec='$wl--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' - else - whole_archive_flag_spec= - fi - supports_anon_versioning=no - case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in - *GNU\ gold*) supports_anon_versioning=yes ;; - *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix[3-9]*) - # On AIX/PPC, the GNU linker is very broken - if test ia64 != "$host_cpu"; then - ld_shlibs=no - cat <<_LT_EOF 1>&2 - -*** Warning: the GNU linker, at least up to release 2.19, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to install binutils -*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. -*** You will then need to restart the configuration process. - -_LT_EOF - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds='' - ;; - m68k) - archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - ;; - esac - ;; - - beos*) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - else - ld_shlibs=no - fi - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec='-L$libdir' - export_dynamic_flag_spec='$wl--export-all-symbols' - allow_undefined_flag=unsupported - always_export_symbols=no - enable_shared_with_static_runtimes=yes - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' - exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' - - if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file, use it as - # is; otherwise, prepend EXPORTS... - archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs=no - fi - ;; - - haiku*) - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - link_all_deplibs=yes - ;; - - os2*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - allow_undefined_flag=unsupported - shrext_cmds=.dll - archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ - $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ - $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ - $ECHO EXPORTS >> $output_objdir/$libname.def~ - emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ - $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ - emximp -o $lib $output_objdir/$libname.def' - archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ - $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ - $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ - $ECHO EXPORTS >> $output_objdir/$libname.def~ - prefix_cmds="$SED"~ - if test EXPORTS = "`$SED 1q $export_symbols`"; then - prefix_cmds="$prefix_cmds -e 1d"; - fi~ - prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ - cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ - $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ - emximp -o $lib $output_objdir/$libname.def' - old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' - enable_shared_with_static_runtimes=yes - ;; - - interix[3-9]*) - hardcode_direct=no - hardcode_shlibpath_var=no - hardcode_libdir_flag_spec='$wl-rpath,$libdir' - export_dynamic_flag_spec='$wl-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) - tmp_diet=no - if test linux-dietlibc = "$host_os"; then - case $cc_basename in - diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) - esac - fi - if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ - && test no = "$tmp_diet" - then - tmp_addflag=' $pic_flag' - tmp_sharedflag='-shared' - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95* | pgfortran*) - # Portland Group f77 and f90 compilers - whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - lf95*) # Lahey Fortran 8.1 - whole_archive_flag_spec= - tmp_sharedflag='--shared' ;; - nagfor*) # NAGFOR 5.3 - tmp_sharedflag='-Wl,-shared' ;; - xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) - tmp_sharedflag='-qmkshrobj' - tmp_addflag= ;; - nvcc*) # Cuda Compiler Driver 2.2 - whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' - compiler_needs_object=yes - ;; - esac - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) # Sun C 5.9 - whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' - compiler_needs_object=yes - tmp_sharedflag='-G' ;; - *Sun\ F*) # Sun Fortran 8.3 - tmp_sharedflag='-G' ;; - esac - archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - - if test yes = "$supports_anon_versioning"; then - archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' - fi - - case $cc_basename in - tcc*) - export_dynamic_flag_spec='-rdynamic' - ;; - xlf* | bgf* | bgxlf* | mpixlf*) - # IBM XL Fortran 10.1 on PPC cannot create shared libs itself - whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' - hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' - archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' - if test yes = "$supports_anon_versioning"; then - archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' - fi - ;; - esac - else - ld_shlibs=no - fi - ;; - - netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then - ld_shlibs=no - cat <<_LT_EOF 1>&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF - ;; - *) - # For security reasons, it is highly recommended that you always - # use absolute paths for naming shared libraries, and exclude the - # DT_RUNPATH tag from executables and libraries. But doing so - # requires that you compile everything twice, which is a pain. - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - esac - ;; - - sunos4*) - archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - *) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - esac - - if test no = "$ld_shlibs"; then - runpath_var= - hardcode_libdir_flag_spec= - export_dynamic_flag_spec= - whole_archive_flag_spec= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - allow_undefined_flag=unsupported - always_export_symbols=yes - archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L=yes - if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct=unsupported - fi - ;; - - aix[4-9]*) - if test ia64 = "$host_cpu"; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag= - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to GNU nm, but means don't demangle to AIX nm. - # Without the "-l" option, or with the "-B" option, AIX nm treats - # weak defined symbols like other global defined symbols, whereas - # GNU nm marks them as "W". - # While the 'weak' keyword is ignored in the Export File, we need - # it in the Import File for the 'aix-soname' feature, so we have - # to replace the "-B" option with "-P" for AIX nm. - if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # have runtime linking enabled, and use it for executables. - # For shared libraries, we enable/disable runtime linking - # depending on the kind of the shared library created - - # when "with_aix_soname,aix_use_runtimelinking" is: - # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables - # "aix,yes" lib.so shared, rtl:yes, for executables - # lib.a static archive - # "both,no" lib.so.V(shr.o) shared, rtl:yes - # lib.a(lib.so.V) shared, rtl:no, for executables - # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables - # lib.a(lib.so.V) shared, rtl:no - # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables - # lib.a static archive - case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) - for ld_flag in $LDFLAGS; do - if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then - aix_use_runtimelinking=yes - break - fi - done - if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then - # With aix-soname=svr4, we create the lib.so.V shared archives only, - # so we don't have lib.a shared libs to link our executables. - # We have to force runtime linking in this case. - aix_use_runtimelinking=yes - LDFLAGS="$LDFLAGS -Wl,-brtl" - fi - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds='' - hardcode_direct=yes - hardcode_direct_absolute=yes - hardcode_libdir_separator=':' - link_all_deplibs=yes - file_list_spec='$wl-f,' - case $with_aix_soname,$aix_use_runtimelinking in - aix,*) ;; # traditional, no import file - svr4,* | *,yes) # use import file - # The Import File defines what to hardcode. - hardcode_direct=no - hardcode_direct_absolute=no - ;; - esac - - if test yes = "$GCC"; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`$CC -print-prog-name=collect2` - if test -f "$collect2name" && - strings "$collect2name" | $GREP resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - hardcode_direct=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L=yes - hardcode_libdir_flag_spec='-L$libdir' - hardcode_libdir_separator= - fi - ;; - esac - shared_flag='-shared' - if test yes = "$aix_use_runtimelinking"; then - shared_flag="$shared_flag "'$wl-G' - fi - # Need to ensure runtime linking is disabled for the traditional - # shared library, or the linker may eventually find shared libraries - # /with/ Import File - we do not want to mix them. - shared_flag_aix='-shared' - shared_flag_svr4='-shared $wl-G' - else - # not using gcc - if test ia64 = "$host_cpu"; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test yes = "$aix_use_runtimelinking"; then - shared_flag='$wl-G' - else - shared_flag='$wl-bM:SRE' - fi - shared_flag_aix='$wl-bM:SRE' - shared_flag_svr4='$wl-G' - fi - fi - - export_dynamic_flag_spec='$wl-bexpall' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols=yes - if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag='-berok' - # Determine the default libpath from the value encoded in an - # empty executable. - if test set = "${lt_cv_aix_libpath+set}"; then - aix_libpath=$lt_cv_aix_libpath -else - if test ${lt_cv_aix_libpath_+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - - lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }' - lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$lt_cv_aix_libpath_"; then - lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - if test -z "$lt_cv_aix_libpath_"; then - lt_cv_aix_libpath_=/usr/lib:/lib - fi - -fi - - aix_libpath=$lt_cv_aix_libpath_ -fi - - hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag - else - if test ia64 = "$host_cpu"; then - hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' - allow_undefined_flag="-z nodefs" - archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an - # empty executable. - if test set = "${lt_cv_aix_libpath+set}"; then - aix_libpath=$lt_cv_aix_libpath -else - if test ${lt_cv_aix_libpath_+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - - lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }' - lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$lt_cv_aix_libpath_"; then - lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - if test -z "$lt_cv_aix_libpath_"; then - lt_cv_aix_libpath_=/usr/lib:/lib - fi - -fi - - aix_libpath=$lt_cv_aix_libpath_ -fi - - hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag=' $wl-bernotok' - allow_undefined_flag=' $wl-berok' - if test yes = "$with_gnu_ld"; then - # We only use this code for GNU lds that support --whole-archive. - whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' - else - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec='$convenience' - fi - archive_cmds_need_lc=yes - archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' - # -brtl affects multiple linker settings, -berok does not and is overridden later - compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' - if test svr4 != "$with_aix_soname"; then - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' - fi - if test aix != "$with_aix_soname"; then - archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' - else - # used by -dlpreopen to get the symbols - archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' - fi - archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' - fi - fi - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds='' - ;; - m68k) - archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - ;; - esac - ;; - - bsdi[45]*) - export_dynamic_flag_spec=-rdynamic - ;; - - cygwin* | mingw* | pw32* | cegcc*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - case $cc_basename in - cl*) - # Native MSVC - hardcode_libdir_flag_spec=' ' - allow_undefined_flag=unsupported - always_export_symbols=yes - file_list_spec='@' - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=.dll - # FIXME: Setting linknames here is a bad hack. - archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' - archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then - cp "$export_symbols" "$output_objdir/$soname.def"; - echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; - else - $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; - fi~ - $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ - linknames=' - # The linker will not automatically build a static lib if we build a DLL. - # _LT_TAGVAR(old_archive_from_new_cmds, )='true' - enable_shared_with_static_runtimes=yes - exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' - # Don't use ranlib - old_postinstall_cmds='chmod 644 $oldlib' - postlink_cmds='lt_outputfile="@OUTPUT@"~ - lt_tool_outputfile="@TOOL_OUTPUT@"~ - case $lt_outputfile in - *.exe|*.EXE) ;; - *) - lt_outputfile=$lt_outputfile.exe - lt_tool_outputfile=$lt_tool_outputfile.exe - ;; - esac~ - if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then - $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; - $RM "$lt_outputfile.manifest"; - fi' - ;; - *) - # Assume MSVC wrapper - hardcode_libdir_flag_spec=' ' - allow_undefined_flag=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=.dll - # FIXME: Setting linknames here is a bad hack. - archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_from_new_cmds='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' - enable_shared_with_static_runtimes=yes - ;; - esac - ;; - - darwin* | rhapsody*) - - - archive_cmds_need_lc=no - hardcode_direct=no - hardcode_automatic=yes - hardcode_shlibpath_var=unsupported - if test yes = "$lt_cv_ld_force_load"; then - whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' - - else - whole_archive_flag_spec='' - fi - link_all_deplibs=yes - allow_undefined_flag=$_lt_dar_allow_undefined - case $cc_basename in - ifort*|nagfor*) _lt_dar_can_shared=yes ;; - *) _lt_dar_can_shared=$GCC ;; - esac - if test yes = "$_lt_dar_can_shared"; then - output_verbose_link_cmd=func_echo_all - archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" - module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" - archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" - module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" - - else - ld_shlibs=no - fi - - ;; - - dgux*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_shlibpath_var=no - ;; - - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2.*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes - hardcode_minus_L=yes - hardcode_shlibpath_var=no - ;; - - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | dragonfly*) - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - hpux9*) - if test yes = "$GCC"; then - archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' - else - archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' - fi - hardcode_libdir_flag_spec='$wl+b $wl$libdir' - hardcode_libdir_separator=: - hardcode_direct=yes - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - export_dynamic_flag_spec='$wl-E' - ;; - - hpux10*) - if test yes,no = "$GCC,$with_gnu_ld"; then - archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test no = "$with_gnu_ld"; then - hardcode_libdir_flag_spec='$wl+b $wl$libdir' - hardcode_libdir_separator=: - hardcode_direct=yes - hardcode_direct_absolute=yes - export_dynamic_flag_spec='$wl-E' - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - fi - ;; - - hpux11*) - if test yes,no = "$GCC,$with_gnu_ld"; then - case $host_cpu in - hppa*64*) - archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - - # Older versions of the 11.00 compiler do not understand -b yet - # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 -printf %s "checking if $CC understands -b... " >&6; } -if test ${lt_cv_prog_compiler__b+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler__b=no - save_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS -b" - echo "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler__b=yes - fi - else - lt_cv_prog_compiler__b=yes - fi - fi - $RM -r conftest* - LDFLAGS=$save_LDFLAGS - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 -printf "%s\n" "$lt_cv_prog_compiler__b" >&6; } - -if test yes = "$lt_cv_prog_compiler__b"; then - archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' -else - archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' -fi - - ;; - esac - fi - if test no = "$with_gnu_ld"; then - hardcode_libdir_flag_spec='$wl+b $wl$libdir' - hardcode_libdir_separator=: - - case $host_cpu in - hppa*64*|ia64*) - hardcode_direct=no - hardcode_shlibpath_var=no - ;; - *) - hardcode_direct=yes - hardcode_direct_absolute=yes - export_dynamic_flag_spec='$wl-E' - - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - ;; - esac - fi - ;; - - irix5* | irix6* | nonstopux*) - if test yes = "$GCC"; then - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' - # Try to use the -exported_symbol ld option, if it does not - # work, assume that -exports_file does not work either and - # implicitly export all symbols. - # This should be the same for all languages, so no per-tag cache variable. - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 -printf %s "checking whether the $host_os linker accepts -exported_symbol... " >&6; } -if test ${lt_cv_irix_exported_symbol+y} -then : - printf %s "(cached) " >&6 -else $as_nop - save_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -int foo (void) { return 0; } -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - lt_cv_irix_exported_symbol=yes -else $as_nop - lt_cv_irix_exported_symbol=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - LDFLAGS=$save_LDFLAGS -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 -printf "%s\n" "$lt_cv_irix_exported_symbol" >&6; } - if test yes = "$lt_cv_irix_exported_symbol"; then - archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' - fi - link_all_deplibs=no - else - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' - fi - archive_cmds_need_lc='no' - hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' - hardcode_libdir_separator=: - inherit_rpath=yes - link_all_deplibs=yes - ;; - - linux*) - case $cc_basename in - tcc*) - # Fabrice Bellard et al's Tiny C Compiler - ld_shlibs=yes - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - netbsd* | netbsdelf*-gnu) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - - newsos6) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes - hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' - hardcode_libdir_separator=: - hardcode_shlibpath_var=no - ;; - - *nto* | *qnx*) - ;; - - openbsd* | bitrig*) - if test -f /usr/libexec/ld.so; then - hardcode_direct=yes - hardcode_shlibpath_var=no - hardcode_direct_absolute=yes - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' - hardcode_libdir_flag_spec='$wl-rpath,$libdir' - export_dynamic_flag_spec='$wl-E' - else - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec='$wl-rpath,$libdir' - fi - else - ld_shlibs=no - fi - ;; - - os2*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - allow_undefined_flag=unsupported - shrext_cmds=.dll - archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ - $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ - $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ - $ECHO EXPORTS >> $output_objdir/$libname.def~ - emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ - $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ - emximp -o $lib $output_objdir/$libname.def' - archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ - $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ - $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ - $ECHO EXPORTS >> $output_objdir/$libname.def~ - prefix_cmds="$SED"~ - if test EXPORTS = "`$SED 1q $export_symbols`"; then - prefix_cmds="$prefix_cmds -e 1d"; - fi~ - prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ - cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ - $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ - emximp -o $lib $output_objdir/$libname.def' - old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' - enable_shared_with_static_runtimes=yes - ;; - - osf3*) - if test yes = "$GCC"; then - allow_undefined_flag=' $wl-expect_unresolved $wl\*' - archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' - else - allow_undefined_flag=' -expect_unresolved \*' - archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' - fi - archive_cmds_need_lc='no' - hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' - hardcode_libdir_separator=: - ;; - - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test yes = "$GCC"; then - allow_undefined_flag=' $wl-expect_unresolved $wl\*' - archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' - hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' - else - allow_undefined_flag=' -expect_unresolved \*' - archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' - archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' - - # Both c and cxx compiler support -rpath directly - hardcode_libdir_flag_spec='-rpath $libdir' - fi - archive_cmds_need_lc='no' - hardcode_libdir_separator=: - ;; - - solaris*) - no_undefined_flag=' -z defs' - if test yes = "$GCC"; then - wlarc='$wl' - archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' - else - case `$CC -V 2>&1` in - *"Compilers 5.0"*) - wlarc='' - archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' - archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' - ;; - *) - wlarc='$wl' - archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' - ;; - esac - fi - hardcode_libdir_flag_spec='-R$libdir' - hardcode_shlibpath_var=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine and reorder linker options, - # but understands '-z linker_flag'. GCC discards it without '$wl', - # but is careful enough not to reorder. - # Supported since Solaris 2.6 (maybe 2.5.1?) - if test yes = "$GCC"; then - whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' - else - whole_archive_flag_spec='-z allextract$convenience -z defaultextract' - fi - ;; - esac - link_all_deplibs=yes - ;; - - sunos4*) - if test sequent = "$host_vendor"; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - hardcode_libdir_flag_spec='-L$libdir' - hardcode_direct=yes - hardcode_minus_L=yes - hardcode_shlibpath_var=no - ;; - - sysv4) - case $host_vendor in - sni) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' - reload_cmds='$CC -r -o $output$reload_objs' - hardcode_direct=no - ;; - motorola) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - hardcode_shlibpath_var=no - ;; - - sysv4.3*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var=no - export_dynamic_flag_spec='-Bexport' - ;; - - sysv4*MP*) - if test -d /usr/nec; then - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - ld_shlibs=yes - fi - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) - no_undefined_flag='$wl-z,text' - archive_cmds_need_lc=no - hardcode_shlibpath_var=no - runpath_var='LD_RUN_PATH' - - if test yes = "$GCC"; then - archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We CANNOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag='$wl-z,text' - allow_undefined_flag='$wl-z,nodefs' - archive_cmds_need_lc=no - hardcode_shlibpath_var=no - hardcode_libdir_flag_spec='$wl-R,$libdir' - hardcode_libdir_separator=':' - link_all_deplibs=yes - export_dynamic_flag_spec='$wl-Bexport' - runpath_var='LD_RUN_PATH' - - if test yes = "$GCC"; then - archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - - uts4*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_shlibpath_var=no - ;; - - *) - ld_shlibs=no - ;; - esac - - if test sni = "$host_vendor"; then - case $host in - sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) - export_dynamic_flag_spec='$wl-Blargedynsym' - ;; - esac - fi - fi - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 -printf "%s\n" "$ld_shlibs" >&6; } -test no = "$ld_shlibs" && can_build_shared=no - -with_gnu_ld=$with_gnu_ld - - - - - - - - - - - - - - - -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc=yes - - if test yes,yes = "$GCC,$enable_shared"; then - case $archive_cmds in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 -printf %s "checking whether -lc should be explicitly linked in... " >&6; } -if test ${lt_cv_archive_cmds_need_lc+y} -then : - printf %s "(cached) " >&6 -else $as_nop - $RM conftest* - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl - pic_flag=$lt_prog_compiler_pic - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag - allow_undefined_flag= - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 - (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - then - lt_cv_archive_cmds_need_lc=no - else - lt_cv_archive_cmds_need_lc=yes - fi - allow_undefined_flag=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 -printf "%s\n" "$lt_cv_archive_cmds_need_lc" >&6; } - archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc - ;; - esac - fi - ;; -esac - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 -printf %s "checking dynamic linker characteristics... " >&6; } - -if test yes = "$GCC"; then - case $host_os in - darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; - *) lt_awk_arg='/^libraries:/' ;; - esac - case $host_os in - mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; - *) lt_sed_strip_eq='s|=/|/|g' ;; - esac - lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` - case $lt_search_path_spec in - *\;*) - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` - ;; - *) - lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` - ;; - esac - # Ok, now we have the path, separated by spaces, we can step through it - # and add multilib dir if necessary... - lt_tmp_lt_search_path_spec= - lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` - # ...but if some path component already ends with the multilib dir we assume - # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). - case "$lt_multi_os_dir; $lt_search_path_spec " in - "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) - lt_multi_os_dir= - ;; - esac - for lt_sys_path in $lt_search_path_spec; do - if test -d "$lt_sys_path$lt_multi_os_dir"; then - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" - elif test -n "$lt_multi_os_dir"; then - test -d "$lt_sys_path" && \ - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" - fi - done - lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' -BEGIN {RS = " "; FS = "/|\n";} { - lt_foo = ""; - lt_count = 0; - for (lt_i = NF; lt_i > 0; lt_i--) { - if ($lt_i != "" && $lt_i != ".") { - if ($lt_i == "..") { - lt_count++; - } else { - if (lt_count == 0) { - lt_foo = "/" $lt_i lt_foo; - } else { - lt_count--; - } - } - } - } - if (lt_foo != "") { lt_freq[lt_foo]++; } - if (lt_freq[lt_foo] == 1) { print lt_foo; } -}'` - # AWK program above erroneously prepends '/' to C:/dos/paths - # for these hosts. - case $host_os in - mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ - $SED 's|/\([A-Za-z]:\)|\1|g'` ;; - esac - sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=.so -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - - - -case $host_os in -aix3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$release$shared_ext$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='$libname$release$shared_ext$major' - ;; - -aix[4-9]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test ia64 = "$host_cpu"; then - # AIX 5 supports IA64 - library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line '#! .'. This would cause the generated library to - # depend on '.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # Using Import Files as archive members, it is possible to support - # filename-based versioning of shared library archives on AIX. While - # this would work for both with and without runtime linking, it will - # prevent static linking of such archives. So we do filename-based - # shared library versioning with .so extension only, which is used - # when both runtime linking and shared linking is enabled. - # Unfortunately, runtime linking may impact performance, so we do - # not want this to be the default eventually. Also, we use the - # versioned .so libs for executables only if there is the -brtl - # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. - # To allow for filename-based versioning support, we need to create - # libNAME.so.V as an archive file, containing: - # *) an Import File, referring to the versioned filename of the - # archive as well as the shared archive member, telling the - # bitwidth (32 or 64) of that shared object, and providing the - # list of exported symbols of that shared object, eventually - # decorated with the 'weak' keyword - # *) the shared object with the F_LOADONLY flag set, to really avoid - # it being seen by the linker. - # At run time we better use the real file rather than another symlink, - # but for link time we create the symlink libNAME.so -> libNAME.so.V - - case $with_aix_soname,$aix_use_runtimelinking in - # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - aix,yes) # traditional libtool - dynamic_linker='AIX unversionable lib.so' - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - ;; - aix,no) # traditional AIX only - dynamic_linker='AIX lib.a(lib.so.V)' - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='$libname$release.a $libname.a' - soname_spec='$libname$release$shared_ext$major' - ;; - svr4,*) # full svr4 only - dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" - library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' - # We do not specify a path in Import Files, so LIBPATH fires. - shlibpath_overrides_runpath=yes - ;; - *,yes) # both, prefer svr4 - dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" - library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' - # unpreferred sharedlib libNAME.a needs extra handling - postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' - postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' - # We do not specify a path in Import Files, so LIBPATH fires. - shlibpath_overrides_runpath=yes - ;; - *,no) # both, prefer aix - dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" - library_names_spec='$libname$release.a $libname.a' - soname_spec='$libname$release$shared_ext$major' - # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling - postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' - postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' - ;; - esac - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - case $host_cpu in - powerpc) - # Since July 2007 AmigaOS4 officially supports .so libraries. - # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - ;; - m68k) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - esac - ;; - -beos*) - library_names_spec='$libname$shared_ext' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[45]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32* | cegcc*) - version_type=windows - shrext_cmds=.dll - need_version=no - need_lib_prefix=no - - case $GCC,$cc_basename in - yes,*) - # gcc - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \$file`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname~ - if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then - eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; - fi' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' - - sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" - ;; - mingw* | cegcc*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' - ;; - esac - dynamic_linker='Win32 ld.exe' - ;; - - *,cl*) - # Native MSVC - libname_spec='$name' - soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' - library_names_spec='$libname.dll.lib' - - case $build_os in - mingw*) - sys_lib_search_path_spec= - lt_save_ifs=$IFS - IFS=';' - for lt_path in $LIB - do - IFS=$lt_save_ifs - # Let DOS variable expansion print the short 8.3 style file name. - lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` - sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" - done - IFS=$lt_save_ifs - # Convert to MSYS style. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` - ;; - cygwin*) - # Convert to unix form, then to dos form, then back to unix form - # but this time dos style (no spaces!) so that the unix form looks - # like /cygdrive/c/PROGRA~1:/cygdr... - sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` - sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` - sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - ;; - *) - sys_lib_search_path_spec=$LIB - if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - # FIXME: find the short name or the path components, as spaces are - # common. (e.g. "Program Files" -> "PROGRA~1") - ;; - esac - - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \$file`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - dynamic_linker='Win32 link.exe' - ;; - - *) - # Assume MSVC wrapper - library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' - dynamic_linker='Win32 ld.exe' - ;; - esac - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' - soname_spec='$libname$release$major$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - - sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[23].*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2.*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - *) # from 4.6 on, and DragonFly - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -haiku*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - dynamic_linker="$host_os runtime_loader" - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LIBRARY_PATH - shlibpath_overrides_runpath=no - sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - if test 32 = "$HPUX_IA64_MODE"; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - sys_lib_dlsearch_path_spec=/usr/lib/hpux32 - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - sys_lib_dlsearch_path_spec=/usr/lib/hpux64 - fi - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555, ... - postinstall_cmds='chmod 555 $lib' - # or fails outright, so override atomically: - install_override_mode=555 - ;; - -interix[3-9]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test yes = "$lt_cv_prog_gnu_ld"; then - version_type=linux # correct to gnu/linux during the next big refactor - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='$libname$release$shared_ext$major' - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" - sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -linux*android*) - version_type=none # Android doesn't support versioned libraries. - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext' - soname_spec='$libname$release$shared_ext' - finish_cmds= - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - dynamic_linker='Android linker' - # Don't embed -rpath directories since the linker doesn't support them. - hardcode_libdir_flag_spec='-L$libdir' - ;; - -# This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - - # Some binutils ld are patched to set DT_RUNPATH - if test ${lt_cv_shlibpath_overrides_runpath+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_shlibpath_overrides_runpath=no - save_LDFLAGS=$LDFLAGS - save_libdir=$libdir - eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ - LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null -then : - lt_cv_shlibpath_overrides_runpath=yes -fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - LDFLAGS=$save_LDFLAGS - libdir=$save_libdir - -fi - - shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath - - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Ideally, we could use ldconfig to report *all* directores which are - # searched for libraries, however this is still not possible. Aside from not - # being certain /sbin/ldconfig is available, command - # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, - # even though it is searched at run-time. Try to do the best guess by - # appending ld.so.conf contents (and includes) to the search path. - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -netbsdelf*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='NetBSD ld.elf_so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -*nto* | *qnx*) - version_type=qnx - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='ldqnx.so' - ;; - -openbsd* | bitrig*) - version_type=sunos - sys_lib_dlsearch_path_spec=/usr/lib - need_lib_prefix=no - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then - need_version=no - else - need_version=yes - fi - library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -os2*) - libname_spec='$name' - version_type=windows - shrext_cmds=.dll - need_version=no - need_lib_prefix=no - # OS/2 can only load a DLL with a base name of 8 characters or less. - soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; - v=$($ECHO $release$versuffix | tr -d .-); - n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); - $ECHO $n$v`$shared_ext' - library_names_spec='${libname}_dll.$libext' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=BEGINLIBPATH - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - postinstall_cmds='base_file=`basename \$file`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname~ - if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then - eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; - fi' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='$libname$release$shared_ext$major' - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - -rdos*) - dynamic_linker=no - ;; - -solaris*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test yes = "$with_gnu_ld"; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec; then - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' - soname_spec='$libname$shared_ext.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=sco - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - if test yes = "$with_gnu_ld"; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -tpf*) - # TPF is a cross-target only. Preferred cross-host = GNU/Linux. - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -uts4*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 -printf "%s\n" "$dynamic_linker" >&6; } -test no = "$dynamic_linker" && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test yes = "$GCC"; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then - sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec -fi - -if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then - sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec -fi - -# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... -configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec - -# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code -func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" - -# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool -configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 -printf %s "checking how to hardcode library paths into programs... " >&6; } -hardcode_action= -if test -n "$hardcode_libdir_flag_spec" || - test -n "$runpath_var" || - test yes = "$hardcode_automatic"; then - - # We can hardcode non-existent directories. - if test no != "$hardcode_direct" && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && - test no != "$hardcode_minus_L"; then - # Linking always hardcodes the temporary library directory. - hardcode_action=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action=unsupported -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 -printf "%s\n" "$hardcode_action" >&6; } - -if test relink = "$hardcode_action" || - test yes = "$inherit_rpath"; then - # Fast installation is not supported - enable_fast_install=no -elif test yes = "$shlibpath_overrides_runpath" || - test no = "$enable_shared"; then - # Fast installation is not necessary - enable_fast_install=needless -fi - - - - - - - if test yes != "$enable_dlopen"; then - enable_dlopen=unknown - enable_dlopen_self=unknown - enable_dlopen_self_static=unknown -else - lt_cv_dlopen=no - lt_cv_dlopen_libs= - - case $host_os in - beos*) - lt_cv_dlopen=load_add_on - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ;; - - mingw* | pw32* | cegcc*) - lt_cv_dlopen=LoadLibrary - lt_cv_dlopen_libs= - ;; - - cygwin*) - lt_cv_dlopen=dlopen - lt_cv_dlopen_libs= - ;; - - darwin*) - # if libdl is installed we need to link against it - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 -printf %s "checking for dlopen in -ldl... " >&6; } -if test ${ac_cv_lib_dl_dlopen+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dlopen (); -int -main (void) -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_dl_dlopen=yes -else $as_nop - ac_cv_lib_dl_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 -printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = xyes -then : - lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl -else $as_nop - - lt_cv_dlopen=dyld - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - -fi - - ;; - - tpf*) - # Don't try to run any link tests for TPF. We know it's impossible - # because TPF is a cross-compiler, and we know how we open DSOs. - lt_cv_dlopen=dlopen - lt_cv_dlopen_libs= - lt_cv_dlopen_self=no - ;; - - *) - ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" -if test "x$ac_cv_func_shl_load" = xyes -then : - lt_cv_dlopen=shl_load -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 -printf %s "checking for shl_load in -ldld... " >&6; } -if test ${ac_cv_lib_dld_shl_load+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldld $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char shl_load (); -int -main (void) -{ -return shl_load (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_dld_shl_load=yes -else $as_nop - ac_cv_lib_dld_shl_load=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 -printf "%s\n" "$ac_cv_lib_dld_shl_load" >&6; } -if test "x$ac_cv_lib_dld_shl_load" = xyes -then : - lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld -else $as_nop - ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" -if test "x$ac_cv_func_dlopen" = xyes -then : - lt_cv_dlopen=dlopen -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 -printf %s "checking for dlopen in -ldl... " >&6; } -if test ${ac_cv_lib_dl_dlopen+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dlopen (); -int -main (void) -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_dl_dlopen=yes -else $as_nop - ac_cv_lib_dl_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 -printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } -if test "x$ac_cv_lib_dl_dlopen" = xyes -then : - lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 -printf %s "checking for dlopen in -lsvld... " >&6; } -if test ${ac_cv_lib_svld_dlopen+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsvld $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dlopen (); -int -main (void) -{ -return dlopen (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_svld_dlopen=yes -else $as_nop - ac_cv_lib_svld_dlopen=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 -printf "%s\n" "$ac_cv_lib_svld_dlopen" >&6; } -if test "x$ac_cv_lib_svld_dlopen" = xyes -then : - lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 -printf %s "checking for dld_link in -ldld... " >&6; } -if test ${ac_cv_lib_dld_dld_link+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldld $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -char dld_link (); -int -main (void) -{ -return dld_link (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - ac_cv_lib_dld_dld_link=yes -else $as_nop - ac_cv_lib_dld_dld_link=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 -printf "%s\n" "$ac_cv_lib_dld_dld_link" >&6; } -if test "x$ac_cv_lib_dld_dld_link" = xyes -then : - lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld -fi - - -fi - - -fi - - -fi - - -fi - - -fi - - ;; - esac - - if test no = "$lt_cv_dlopen"; then - enable_dlopen=no - else - enable_dlopen=yes - fi - - case $lt_cv_dlopen in - dlopen) - save_CPPFLAGS=$CPPFLAGS - test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - - save_LDFLAGS=$LDFLAGS - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" - - save_LIBS=$LIBS - LIBS="$lt_cv_dlopen_libs $LIBS" - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 -printf %s "checking whether a program can dlopen itself... " >&6; } -if test ${lt_cv_dlopen_self+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test yes = "$cross_compiling"; then : - lt_cv_dlopen_self=cross -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext <<_LT_EOF -#line $LINENO "configure" -#include "confdefs.h" - -#if HAVE_DLFCN_H -#include -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -/* When -fvisibility=hidden is used, assume the code has been annotated - correspondingly for the symbols needed. */ -#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) -int fnord () __attribute__((visibility("default"))); -#endif - -int fnord () { return 42; } -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else - { - if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - else puts (dlerror ()); - } - /* dlclose (self); */ - } - else - puts (dlerror ()); - - return status; -} -_LT_EOF - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 - (eval $ac_link) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then - (./conftest; exit; ) >&5 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; - x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; - x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; - esac - else : - # compilation failed - lt_cv_dlopen_self=no - fi -fi -rm -fr conftest* - - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 -printf "%s\n" "$lt_cv_dlopen_self" >&6; } - - if test yes = "$lt_cv_dlopen_self"; then - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 -printf %s "checking whether a statically linked program can dlopen itself... " >&6; } -if test ${lt_cv_dlopen_self_static+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test yes = "$cross_compiling"; then : - lt_cv_dlopen_self_static=cross -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext <<_LT_EOF -#line $LINENO "configure" -#include "confdefs.h" - -#if HAVE_DLFCN_H -#include -#endif - -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -/* When -fvisibility=hidden is used, assume the code has been annotated - correspondingly for the symbols needed. */ -#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) -int fnord () __attribute__((visibility("default"))); -#endif - -int fnord () { return 42; } -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; - - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else - { - if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - else puts (dlerror ()); - } - /* dlclose (self); */ - } - else - puts (dlerror ()); - - return status; -} -_LT_EOF - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 - (eval $ac_link) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then - (./conftest; exit; ) >&5 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; - x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; - x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; - esac - else : - # compilation failed - lt_cv_dlopen_self_static=no - fi -fi -rm -fr conftest* - - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 -printf "%s\n" "$lt_cv_dlopen_self_static" >&6; } - fi - - CPPFLAGS=$save_CPPFLAGS - LDFLAGS=$save_LDFLAGS - LIBS=$save_LIBS - ;; - esac - - case $lt_cv_dlopen_self in - yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; - *) enable_dlopen_self=unknown ;; - esac - - case $lt_cv_dlopen_self_static in - yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; - *) enable_dlopen_self_static=unknown ;; - esac -fi - - - - - - - - - - - - - - - - - -striplib= -old_striplib= -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 -printf %s "checking whether stripping libraries is possible... " >&6; } -if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } -else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP"; then - striplib="$STRIP -x" - old_striplib="$STRIP -S" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } - fi - ;; - *) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } - ;; - esac -fi - - - - - - - - - - - - - # Report what library types will actually be built - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 -printf %s "checking if libtool supports shared libraries... " >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 -printf "%s\n" "$can_build_shared" >&6; } - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 -printf %s "checking whether to build shared libraries... " >&6; } - test no = "$can_build_shared" && enable_shared=no - - # On AIX, shared libraries and static libraries use the same namespace, and - # are all built from PIC. - case $host_os in - aix3*) - test yes = "$enable_shared" && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; - - aix[4-9]*) - if test ia64 != "$host_cpu"; then - case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in - yes,aix,yes) ;; # shared object as lib.so file only - yes,svr4,*) ;; # shared object as lib.so archive member only - yes,*) enable_static=no ;; # shared object in lib.a archive as well - esac - fi - ;; - esac - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 -printf "%s\n" "$enable_shared" >&6; } - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 -printf %s "checking whether to build static libraries... " >&6; } - # Make sure either enable_shared or enable_static is yes. - test yes = "$enable_shared" || enable_static=yes - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 -printf "%s\n" "$enable_static" >&6; } - - - - -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -CC=$lt_save_CC - - - - - - - - - - - - - - - - ac_config_commands="$ac_config_commands libtool" - - - - -# Only expand once: - - - - - - - - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -if test -z "$CXX"; then - if test -n "$CCC"; then - CXX=$CCC - else - if test -n "$ac_tool_prefix"; then - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++ - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CXX"; then - ac_cv_prog_CXX="$CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CXX=$ac_cv_prog_CXX -if test -n "$CXX"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 -printf "%s\n" "$CXX" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$CXX" && break - done -fi -if test -z "$CXX"; then - ac_ct_CXX=$CXX - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++ -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CXX"; then - ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CXX="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CXX=$ac_cv_prog_ac_ct_CXX -if test -n "$ac_ct_CXX"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 -printf "%s\n" "$ac_ct_CXX" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$ac_ct_CXX" && break -done - - if test "x$ac_ct_CXX" = x; then - CXX="g++" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CXX=$ac_ct_CXX - fi -fi - - fi -fi -# Provide some information about the compiler. -printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C++" >&5 -printf %s "checking whether the compiler supports GNU C++... " >&6; } -if test ${ac_cv_cxx_compiler_gnu+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - ac_compiler_gnu=yes -else $as_nop - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -ac_cv_cxx_compiler_gnu=$ac_compiler_gnu - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 -printf "%s\n" "$ac_cv_cxx_compiler_gnu" >&6; } -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -if test $ac_compiler_gnu = yes; then - GXX=yes -else - GXX= -fi -ac_test_CXXFLAGS=${CXXFLAGS+y} -ac_save_CXXFLAGS=$CXXFLAGS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 -printf %s "checking whether $CXX accepts -g... " >&6; } -if test ${ac_cv_prog_cxx_g+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_save_cxx_werror_flag=$ac_cxx_werror_flag - ac_cxx_werror_flag=yes - ac_cv_prog_cxx_g=no - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - ac_cv_prog_cxx_g=yes -else $as_nop - CXXFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - -else $as_nop - ac_cxx_werror_flag=$ac_save_cxx_werror_flag - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO" -then : - ac_cv_prog_cxx_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - ac_cxx_werror_flag=$ac_save_cxx_werror_flag -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 -printf "%s\n" "$ac_cv_prog_cxx_g" >&6; } -if test $ac_test_CXXFLAGS; then - CXXFLAGS=$ac_save_CXXFLAGS -elif test $ac_cv_prog_cxx_g = yes; then - if test "$GXX" = yes; then - CXXFLAGS="-g -O2" - else - CXXFLAGS="-g" - fi -else - if test "$GXX" = yes; then - CXXFLAGS="-O2" - else - CXXFLAGS= - fi -fi -ac_prog_cxx_stdcxx=no -if test x$ac_prog_cxx_stdcxx = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5 -printf %s "checking for $CXX option to enable C++11 features... " >&6; } -if test ${ac_cv_prog_cxx_11+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cxx_11=no -ac_save_CXX=$CXX -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_cxx_conftest_cxx11_program -_ACEOF -for ac_arg in '' -std=gnu++11 -std=gnu++0x -std=c++11 -std=c++0x -qlanglvl=extended0x -AA -do - CXX="$ac_save_CXX $ac_arg" - if ac_fn_cxx_try_compile "$LINENO" -then : - ac_cv_prog_cxx_cxx11=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam - test "x$ac_cv_prog_cxx_cxx11" != "xno" && break -done -rm -f conftest.$ac_ext -CXX=$ac_save_CXX -fi - -if test "x$ac_cv_prog_cxx_cxx11" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cxx_cxx11" = x -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5 -printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; } - CXX="$CXX $ac_cv_prog_cxx_cxx11" -fi - ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11 - ac_prog_cxx_stdcxx=cxx11 -fi -fi -if test x$ac_prog_cxx_stdcxx = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5 -printf %s "checking for $CXX option to enable C++98 features... " >&6; } -if test ${ac_cv_prog_cxx_98+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cxx_98=no -ac_save_CXX=$CXX -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_cxx_conftest_cxx98_program -_ACEOF -for ac_arg in '' -std=gnu++98 -std=c++98 -qlanglvl=extended -AA -do - CXX="$ac_save_CXX $ac_arg" - if ac_fn_cxx_try_compile "$LINENO" -then : - ac_cv_prog_cxx_cxx98=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam - test "x$ac_cv_prog_cxx_cxx98" != "xno" && break -done -rm -f conftest.$ac_ext -CXX=$ac_save_CXX -fi - -if test "x$ac_cv_prog_cxx_cxx98" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cxx_cxx98" = x -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5 -printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; } - CXX="$CXX $ac_cv_prog_cxx_cxx98" -fi - ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98 - ac_prog_cxx_stdcxx=cxx98 -fi -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -depcc="$CXX" am_compiler_list= - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -printf %s "checking dependency style of $depcc... " >&6; } -if test ${am_cv_CXX_dependencies_compiler_type+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named 'D' -- because '-MD' means "put the output - # in D". - rm -rf conftest.dir - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_CXX_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` - fi - am__universal=false - case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with '-c' and '-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_CXX_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir -else - am_cv_CXX_dependencies_compiler_type=none -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 -printf "%s\n" "$am_cv_CXX_dependencies_compiler_type" >&6; } -CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type - - if - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then - am__fastdepCXX_TRUE= - am__fastdepCXX_FALSE='#' -else - am__fastdepCXX_TRUE='#' - am__fastdepCXX_FALSE= -fi - - - - - -func_stripname_cnf () -{ - case $2 in - .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; - *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; - esac -} # func_stripname_cnf - - if test -n "$CXX" && ( test no != "$CXX" && - ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || - (test g++ != "$CXX"))); then - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 -printf %s "checking how to run the C++ preprocessor... " >&6; } -if test -z "$CXXCPP"; then - if test ${ac_cv_prog_CXXCPP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - # Double quotes because $CXX needs to be expanded - for CXXCPP in "$CXX -E" cpp /lib/cpp - do - ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - Syntax error -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO" -then : - -else $as_nop - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO" -then : - # Broken: success on invalid input. -continue -else $as_nop - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok -then : - break -fi - - done - ac_cv_prog_CXXCPP=$CXXCPP - -fi - CXXCPP=$ac_cv_prog_CXXCPP -else - ac_cv_prog_CXXCPP=$CXXCPP -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 -printf "%s\n" "$CXXCPP" >&6; } -ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - Syntax error -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO" -then : - -else $as_nop - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO" -then : - # Broken: success on invalid input. -continue -else $as_nop - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok -then : - -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -else - _lt_caught_CXX_error=yes -fi - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -archive_cmds_need_lc_CXX=no -allow_undefined_flag_CXX= -always_export_symbols_CXX=no -archive_expsym_cmds_CXX= -compiler_needs_object_CXX=no -export_dynamic_flag_spec_CXX= -hardcode_direct_CXX=no -hardcode_direct_absolute_CXX=no -hardcode_libdir_flag_spec_CXX= -hardcode_libdir_separator_CXX= -hardcode_minus_L_CXX=no -hardcode_shlibpath_var_CXX=unsupported -hardcode_automatic_CXX=no -inherit_rpath_CXX=no -module_cmds_CXX= -module_expsym_cmds_CXX= -link_all_deplibs_CXX=unknown -old_archive_cmds_CXX=$old_archive_cmds -reload_flag_CXX=$reload_flag -reload_cmds_CXX=$reload_cmds -no_undefined_flag_CXX= -whole_archive_flag_spec_CXX= -enable_shared_with_static_runtimes_CXX=no - -# Source file extension for C++ test sources. -ac_ext=cpp - -# Object file extension for compiled C++ test sources. -objext=o -objext_CXX=$objext - -# No sense in running all these tests if we already determined that -# the CXX compiler isn't working. Some variables (like enable_shared) -# are currently assumed to apply to all compilers on this platform, -# and will be corrupted by setting them based on a non-working compiler. -if test yes != "$_lt_caught_CXX_error"; then - # Code to be used in simple compile tests - lt_simple_compile_test_code="int some_variable = 0;" - - # Code to be used in simple link tests - lt_simple_link_test_code='int main(int, char *[]) { return(0); }' - - # ltmain only uses $CC for tagged configurations so make sure $CC is set. - - - - - - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - - # save warnings/boilerplate of simple test code - ac_outfile=conftest.$ac_objext -echo "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$RM conftest* - - ac_outfile=conftest.$ac_objext -echo "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$RM -r conftest* - - - # Allow CC to be a program name with arguments. - lt_save_CC=$CC - lt_save_CFLAGS=$CFLAGS - lt_save_LD=$LD - lt_save_GCC=$GCC - GCC=$GXX - lt_save_with_gnu_ld=$with_gnu_ld - lt_save_path_LD=$lt_cv_path_LD - if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then - lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx - else - $as_unset lt_cv_prog_gnu_ld - fi - if test -n "${lt_cv_path_LDCXX+set}"; then - lt_cv_path_LD=$lt_cv_path_LDCXX - else - $as_unset lt_cv_path_LD - fi - test -z "${LDCXX+set}" || LD=$LDCXX - CC=${CXX-"c++"} - CFLAGS=$CXXFLAGS - compiler=$CC - compiler_CXX=$CC - func_cc_basename $compiler -cc_basename=$func_cc_basename_result - - - if test -n "$compiler"; then - # We don't want -fno-exception when compiling C++ code, so set the - # no_builtin_flag separately - if test yes = "$GXX"; then - lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' - else - lt_prog_compiler_no_builtin_flag_CXX= - fi - - if test yes = "$GXX"; then - # Set up default GNU C++ configuration - - - -# Check whether --with-gnu-ld was given. -if test ${with_gnu_ld+y} -then : - withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes -else $as_nop - with_gnu_ld=no -fi - -ac_prog=ld -if test yes = "$GCC"; then - # Check if gcc -print-prog-name=ld gives a path. - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 -printf %s "checking for ld used by $CC... " >&6; } - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return, which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [\\/]* | ?:[\\/]*) - re_direlt='/[^/][^/]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` - while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do - ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD=$ac_prog - ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test yes = "$with_gnu_ld"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 -printf %s "checking for GNU ld... " >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 -printf %s "checking for non-GNU ld... " >&6; } -fi -if test ${lt_cv_path_LD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -z "$LD"; then - lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS=$lt_save_ifs - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD=$ac_dir/$ac_prog - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &5 -printf "%s\n" "$LD" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi -test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 -printf %s "checking if the linker ($LD) is GNU ld... " >&6; } -if test ${lt_cv_prog_gnu_ld+y} -then : - printf %s "(cached) " >&6 -else $as_nop - # I'd rather use --version here, but apparently some GNU lds only accept -v. -case `$LD -v 2>&1 &5 -printf "%s\n" "$lt_cv_prog_gnu_ld" >&6; } -with_gnu_ld=$lt_cv_prog_gnu_ld - - - - - - - - # Check if GNU C++ uses GNU ld as the underlying linker, since the - # archiving commands below assume that GNU ld is being used. - if test yes = "$with_gnu_ld"; then - archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - - hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' - export_dynamic_flag_spec_CXX='$wl--export-dynamic' - - # If archive_cmds runs LD, not CC, wlarc should be empty - # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to - # investigate it a little bit more. (MM) - wlarc='$wl' - - # ancient GNU ld didn't support --whole-archive et. al. - if eval "`$CC -print-prog-name=ld` --help 2>&1" | - $GREP 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' - else - whole_archive_flag_spec_CXX= - fi - else - with_gnu_ld=no - wlarc= - - # A generic and very simple default shared library creation - # command for GNU C++ for the case where it uses the native - # linker, instead of GNU ld. If possible, this setting should - # overridden to take advantage of the native linker features on - # the platform it is being used on. - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - fi - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' - - else - GXX=no - with_gnu_ld=no - wlarc= - fi - - # PORTME: fill in a description of your system's C++ link characteristics - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -printf %s "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } - ld_shlibs_CXX=yes - case $host_os in - aix3*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aix[4-9]*) - if test ia64 = "$host_cpu"; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag= - else - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # have runtime linking enabled, and use it for executables. - # For shared libraries, we enable/disable runtime linking - # depending on the kind of the shared library created - - # when "with_aix_soname,aix_use_runtimelinking" is: - # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables - # "aix,yes" lib.so shared, rtl:yes, for executables - # lib.a static archive - # "both,no" lib.so.V(shr.o) shared, rtl:yes - # lib.a(lib.so.V) shared, rtl:no, for executables - # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables - # lib.a(lib.so.V) shared, rtl:no - # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables - # lib.a static archive - case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) - for ld_flag in $LDFLAGS; do - case $ld_flag in - *-brtl*) - aix_use_runtimelinking=yes - break - ;; - esac - done - if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then - # With aix-soname=svr4, we create the lib.so.V shared archives only, - # so we don't have lib.a shared libs to link our executables. - # We have to force runtime linking in this case. - aix_use_runtimelinking=yes - LDFLAGS="$LDFLAGS -Wl,-brtl" - fi - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds_CXX='' - hardcode_direct_CXX=yes - hardcode_direct_absolute_CXX=yes - hardcode_libdir_separator_CXX=':' - link_all_deplibs_CXX=yes - file_list_spec_CXX='$wl-f,' - case $with_aix_soname,$aix_use_runtimelinking in - aix,*) ;; # no import file - svr4,* | *,yes) # use import file - # The Import File defines what to hardcode. - hardcode_direct_CXX=no - hardcode_direct_absolute_CXX=no - ;; - esac - - if test yes = "$GXX"; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`$CC -print-prog-name=collect2` - if test -f "$collect2name" && - strings "$collect2name" | $GREP resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - hardcode_direct_CXX=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L_CXX=yes - hardcode_libdir_flag_spec_CXX='-L$libdir' - hardcode_libdir_separator_CXX= - fi - esac - shared_flag='-shared' - if test yes = "$aix_use_runtimelinking"; then - shared_flag=$shared_flag' $wl-G' - fi - # Need to ensure runtime linking is disabled for the traditional - # shared library, or the linker may eventually find shared libraries - # /with/ Import File - we do not want to mix them. - shared_flag_aix='-shared' - shared_flag_svr4='-shared $wl-G' - else - # not using gcc - if test ia64 = "$host_cpu"; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test yes = "$aix_use_runtimelinking"; then - shared_flag='$wl-G' - else - shared_flag='$wl-bM:SRE' - fi - shared_flag_aix='$wl-bM:SRE' - shared_flag_svr4='$wl-G' - fi - fi - - export_dynamic_flag_spec_CXX='$wl-bexpall' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to - # export. - always_export_symbols_CXX=yes - if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - # The "-G" linker flag allows undefined symbols. - no_undefined_flag_CXX='-bernotok' - # Determine the default libpath from the value encoded in an empty - # executable. - if test set = "${lt_cv_aix_libpath+set}"; then - aix_libpath=$lt_cv_aix_libpath -else - if test ${lt_cv_aix_libpath__CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO" -then : - - lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }' - lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$lt_cv_aix_libpath__CXX"; then - lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - if test -z "$lt_cv_aix_libpath__CXX"; then - lt_cv_aix_libpath__CXX=/usr/lib:/lib - fi - -fi - - aix_libpath=$lt_cv_aix_libpath__CXX -fi - - hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" - - archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag - else - if test ia64 = "$host_cpu"; then - hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' - allow_undefined_flag_CXX="-z nodefs" - archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an - # empty executable. - if test set = "${lt_cv_aix_libpath+set}"; then - aix_libpath=$lt_cv_aix_libpath -else - if test ${lt_cv_aix_libpath__CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO" -then : - - lt_aix_libpath_sed=' - /Import File Strings/,/^$/ { - /^0/ { - s/^0 *\([^ ]*\) *$/\1/ - p - } - }' - lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - # Check for a 64-bit object if we didn't find anything. - if test -z "$lt_cv_aix_libpath__CXX"; then - lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` - fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - if test -z "$lt_cv_aix_libpath__CXX"; then - lt_cv_aix_libpath__CXX=/usr/lib:/lib - fi - -fi - - aix_libpath=$lt_cv_aix_libpath__CXX -fi - - hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag_CXX=' $wl-bernotok' - allow_undefined_flag_CXX=' $wl-berok' - if test yes = "$with_gnu_ld"; then - # We only use this code for GNU lds that support --whole-archive. - whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' - else - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec_CXX='$convenience' - fi - archive_cmds_need_lc_CXX=yes - archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' - # -brtl affects multiple linker settings, -berok does not and is overridden later - compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' - if test svr4 != "$with_aix_soname"; then - # This is similar to how AIX traditionally builds its shared - # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. - archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' - fi - if test aix != "$with_aix_soname"; then - archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' - else - # used by -dlpreopen to get the symbols - archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' - fi - archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' - fi - fi - ;; - - beos*) - if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag_CXX=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - else - ld_shlibs_CXX=no - fi - ;; - - chorus*) - case $cc_basename in - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - cygwin* | mingw* | pw32* | cegcc*) - case $GXX,$cc_basename in - ,cl* | no,cl*) - # Native MSVC - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec_CXX=' ' - allow_undefined_flag_CXX=unsupported - always_export_symbols_CXX=yes - file_list_spec_CXX='@' - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=.dll - # FIXME: Setting linknames here is a bad hack. - archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' - archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then - cp "$export_symbols" "$output_objdir/$soname.def"; - echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; - else - $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; - fi~ - $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ - linknames=' - # The linker will not automatically build a static lib if we build a DLL. - # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' - enable_shared_with_static_runtimes_CXX=yes - # Don't use ranlib - old_postinstall_cmds_CXX='chmod 644 $oldlib' - postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ - lt_tool_outputfile="@TOOL_OUTPUT@"~ - case $lt_outputfile in - *.exe|*.EXE) ;; - *) - lt_outputfile=$lt_outputfile.exe - lt_tool_outputfile=$lt_tool_outputfile.exe - ;; - esac~ - func_to_tool_file "$lt_outputfile"~ - if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then - $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; - $RM "$lt_outputfile.manifest"; - fi' - ;; - *) - # g++ - # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec_CXX='-L$libdir' - export_dynamic_flag_spec_CXX='$wl--export-all-symbols' - allow_undefined_flag_CXX=unsupported - always_export_symbols_CXX=no - enable_shared_with_static_runtimes_CXX=yes - - if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file, use it as - # is; otherwise, prepend EXPORTS... - archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs_CXX=no - fi - ;; - esac - ;; - darwin* | rhapsody*) - - - archive_cmds_need_lc_CXX=no - hardcode_direct_CXX=no - hardcode_automatic_CXX=yes - hardcode_shlibpath_var_CXX=unsupported - if test yes = "$lt_cv_ld_force_load"; then - whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' - - else - whole_archive_flag_spec_CXX='' - fi - link_all_deplibs_CXX=yes - allow_undefined_flag_CXX=$_lt_dar_allow_undefined - case $cc_basename in - ifort*|nagfor*) _lt_dar_can_shared=yes ;; - *) _lt_dar_can_shared=$GCC ;; - esac - if test yes = "$_lt_dar_can_shared"; then - output_verbose_link_cmd=func_echo_all - archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" - module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" - archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" - module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" - if test yes != "$lt_cv_apple_cc_single_mod"; then - archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" - archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" - fi - - else - ld_shlibs_CXX=no - fi - - ;; - - os2*) - hardcode_libdir_flag_spec_CXX='-L$libdir' - hardcode_minus_L_CXX=yes - allow_undefined_flag_CXX=unsupported - shrext_cmds=.dll - archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ - $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ - $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ - $ECHO EXPORTS >> $output_objdir/$libname.def~ - emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ - $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ - emximp -o $lib $output_objdir/$libname.def' - archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ - $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ - $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ - $ECHO EXPORTS >> $output_objdir/$libname.def~ - prefix_cmds="$SED"~ - if test EXPORTS = "`$SED 1q $export_symbols`"; then - prefix_cmds="$prefix_cmds -e 1d"; - fi~ - prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ - cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ - $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ - emximp -o $lib $output_objdir/$libname.def' - old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' - enable_shared_with_static_runtimes_CXX=yes - ;; - - dgux*) - case $cc_basename in - ec++*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - ghcx*) - # Green Hills C++ Compiler - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - freebsd2.*) - # C++ shared libraries reported to be fairly broken before - # switch to ELF - ld_shlibs_CXX=no - ;; - - freebsd-elf*) - archive_cmds_need_lc_CXX=no - ;; - - freebsd* | dragonfly*) - # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF - # conventions - ld_shlibs_CXX=yes - ;; - - haiku*) - archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - link_all_deplibs_CXX=yes - ;; - - hpux9*) - hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' - hardcode_libdir_separator_CXX=: - export_dynamic_flag_spec_CXX='$wl-E' - hardcode_direct_CXX=yes - hardcode_minus_L_CXX=yes # Not in the search PATH, - # but as the default - # location of the library. - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aCC*) - archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test yes = "$GXX"; then - archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - - hpux10*|hpux11*) - if test no = "$with_gnu_ld"; then - hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' - hardcode_libdir_separator_CXX=: - - case $host_cpu in - hppa*64*|ia64*) - ;; - *) - export_dynamic_flag_spec_CXX='$wl-E' - ;; - esac - fi - case $host_cpu in - hppa*64*|ia64*) - hardcode_direct_CXX=no - hardcode_shlibpath_var_CXX=no - ;; - *) - hardcode_direct_CXX=yes - hardcode_direct_absolute_CXX=yes - hardcode_minus_L_CXX=yes # Not in the search PATH, - # but as the default - # location of the library. - ;; - esac - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aCC*) - case $host_cpu in - hppa*64*) - archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test yes = "$GXX"; then - if test no = "$with_gnu_ld"; then - case $host_cpu in - hppa*64*) - archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - fi - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - - interix[3-9]*) - hardcode_direct_CXX=no - hardcode_shlibpath_var_CXX=no - hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' - export_dynamic_flag_spec_CXX='$wl-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - irix5* | irix6*) - case $cc_basename in - CC*) - # SGI C++ - archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' - - # Archives containing C++ object files must be created using - # "CC -ar", where "CC" is the IRIX C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' - ;; - *) - if test yes = "$GXX"; then - if test no = "$with_gnu_ld"; then - archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' - else - archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' - fi - fi - link_all_deplibs_CXX=yes - ;; - esac - hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' - hardcode_libdir_separator_CXX=: - inherit_rpath_CXX=yes - ;; - - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - - hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' - export_dynamic_flag_spec_CXX='$wl--export-dynamic' - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' - ;; - icpc* | ecpc* ) - # Intel C++ - with_gnu_ld=yes - # version 8.0 and above of icpc choke on multiply defined symbols - # if we add $predep_objects and $postdep_objects, however 7.1 and - # earlier do not add the objects themselves. - case `$CC -V 2>&1` in - *"Version 7."*) - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 8.0 or newer - tmp_idyn= - case $host_cpu in - ia64*) tmp_idyn=' -i_dynamic';; - esac - archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - ;; - esac - archive_cmds_need_lc_CXX=no - hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' - export_dynamic_flag_spec_CXX='$wl--export-dynamic' - whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' - ;; - pgCC* | pgcpp*) - # Portland Group C++ compiler - case `$CC -V` in - *pgCC\ [1-5].* | *pgcpp\ [1-5].*) - prelink_cmds_CXX='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ - compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' - old_archive_cmds_CXX='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ - $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ - $RANLIB $oldlib' - archive_cmds_CXX='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 6 and above use weak symbols - archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - ;; - esac - - hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' - export_dynamic_flag_spec_CXX='$wl--export-dynamic' - whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' - ;; - cxx*) - # Compaq C++ - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' - - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec_CXX='-rpath $libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' - ;; - xl* | mpixl* | bgxl*) - # IBM XL 8.0 on PPC, with GNU ld - hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' - export_dynamic_flag_spec_CXX='$wl--export-dynamic' - archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - if test yes = "$supports_anon_versioning"; then - archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' - fi - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - no_undefined_flag_CXX=' -zdefs' - archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' - hardcode_libdir_flag_spec_CXX='-R$libdir' - whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' - compiler_needs_object_CXX=yes - - # Not sure whether something based on - # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 - # would be better. - output_verbose_link_cmd='func_echo_all' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' - ;; - esac - ;; - esac - ;; - - lynxos*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - - m88k*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - - mvs*) - case $cc_basename in - cxx*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' - wlarc= - hardcode_libdir_flag_spec_CXX='-R$libdir' - hardcode_direct_CXX=yes - hardcode_shlibpath_var_CXX=no - fi - # Workaround some broken pre-1.5 toolchains - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' - ;; - - *nto* | *qnx*) - ld_shlibs_CXX=yes - ;; - - openbsd* | bitrig*) - if test -f /usr/libexec/ld.so; then - hardcode_direct_CXX=yes - hardcode_shlibpath_var_CXX=no - hardcode_direct_absolute_CXX=yes - archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then - archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' - export_dynamic_flag_spec_CXX='$wl-E' - whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' - fi - output_verbose_link_cmd=func_echo_all - else - ld_shlibs_CXX=no - fi - ;; - - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' - hardcode_libdir_separator_CXX=: - - # Archives containing C++ object files must be created using - # the KAI C++ compiler. - case $host in - osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; - *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; - esac - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - cxx*) - case $host in - osf3*) - allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' - archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' - hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' - ;; - *) - allow_undefined_flag_CXX=' -expect_unresolved \*' - archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' - archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ - $RM $lib.exp' - hardcode_libdir_flag_spec_CXX='-rpath $libdir' - ;; - esac - - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - ;; - *) - if test yes,no = "$GXX,$with_gnu_ld"; then - allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' - case $host in - osf3*) - archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' - ;; - *) - archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' - ;; - esac - - hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' - - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - - psos*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - lcc*) - # Lucid - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - solaris*) - case $cc_basename in - CC* | sunCC*) - # Sun C++ 4.2, 5.x and Centerline C++ - archive_cmds_need_lc_CXX=yes - no_undefined_flag_CXX=' -zdefs' - archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - hardcode_libdir_flag_spec_CXX='-R$libdir' - hardcode_shlibpath_var_CXX=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine and reorder linker options, - # but understands '-z linker_flag'. - # Supported since Solaris 2.6 (maybe 2.5.1?) - whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' - ;; - esac - link_all_deplibs_CXX=yes - - output_verbose_link_cmd='func_echo_all' - - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' - ;; - gcx*) - # Green Hills C++ Compiler - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' - - # The C++ compiler must be used to create the archive. - old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' - ;; - *) - # GNU C++ compiler with Solaris linker - if test yes,no = "$GXX,$with_gnu_ld"; then - no_undefined_flag_CXX=' $wl-z ${wl}defs' - if $CC --version | $GREP -v '^2\.7' > /dev/null; then - archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' - archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' - else - # g++ 2.7 appears to require '-G' NOT '-shared' on this - # platform. - archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' - archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' - fi - - hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' - ;; - esac - fi - ;; - esac - ;; - - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) - no_undefined_flag_CXX='$wl-z,text' - archive_cmds_need_lc_CXX=no - hardcode_shlibpath_var_CXX=no - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - sysv5* | sco3.2v5* | sco5v6*) - # Note: We CANNOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag_CXX='$wl-z,text' - allow_undefined_flag_CXX='$wl-z,nodefs' - archive_cmds_need_lc_CXX=no - hardcode_shlibpath_var_CXX=no - hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' - hardcode_libdir_separator_CXX=':' - link_all_deplibs_CXX=yes - export_dynamic_flag_spec_CXX='$wl-Bexport' - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ - '"$old_archive_cmds_CXX" - reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ - '"$reload_cmds_CXX" - ;; - *) - archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - - vxworks*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 -printf "%s\n" "$ld_shlibs_CXX" >&6; } - test no = "$ld_shlibs_CXX" && can_build_shared=no - - GCC_CXX=$GXX - LD_CXX=$LD - - ## CAVEAT EMPTOR: - ## There is no encapsulation within the following macros, do not change - ## the running order or otherwise move them around unless you know exactly - ## what you are doing... - # Dependencies to place before and after the object being linked: -predep_objects_CXX= -postdep_objects_CXX= -predeps_CXX= -postdeps_CXX= -compiler_lib_search_path_CXX= - -cat > conftest.$ac_ext <<_LT_EOF -class Foo -{ -public: - Foo (void) { a = 0; } -private: - int a; -}; -_LT_EOF - - -_lt_libdeps_save_CFLAGS=$CFLAGS -case "$CC $CFLAGS " in #( -*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; -*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; -*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; -esac - -if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - # Parse the compiler output and extract the necessary - # objects, libraries and library flags. - - # Sentinel used to keep track of whether or not we are before - # the conftest object file. - pre_test_object_deps_done=no - - for p in `eval "$output_verbose_link_cmd"`; do - case $prev$p in - - -L* | -R* | -l*) - # Some compilers place space between "-{L,R}" and the path. - # Remove the space. - if test x-L = "$p" || - test x-R = "$p"; then - prev=$p - continue - fi - - # Expand the sysroot to ease extracting the directories later. - if test -z "$prev"; then - case $p in - -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; - -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; - -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; - esac - fi - case $p in - =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; - esac - if test no = "$pre_test_object_deps_done"; then - case $prev in - -L | -R) - # Internal compiler library paths should come after those - # provided the user. The postdeps already come after the - # user supplied libs so there is no need to process them. - if test -z "$compiler_lib_search_path_CXX"; then - compiler_lib_search_path_CXX=$prev$p - else - compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" - fi - ;; - # The "-l" case would never come before the object being - # linked, so don't bother handling this case. - esac - else - if test -z "$postdeps_CXX"; then - postdeps_CXX=$prev$p - else - postdeps_CXX="${postdeps_CXX} $prev$p" - fi - fi - prev= - ;; - - *.lto.$objext) ;; # Ignore GCC LTO objects - *.$objext) - # This assumes that the test object file only shows up - # once in the compiler output. - if test "$p" = "conftest.$objext"; then - pre_test_object_deps_done=yes - continue - fi - - if test no = "$pre_test_object_deps_done"; then - if test -z "$predep_objects_CXX"; then - predep_objects_CXX=$p - else - predep_objects_CXX="$predep_objects_CXX $p" - fi - else - if test -z "$postdep_objects_CXX"; then - postdep_objects_CXX=$p - else - postdep_objects_CXX="$postdep_objects_CXX $p" - fi - fi - ;; - - *) ;; # Ignore the rest. - - esac - done - - # Clean up. - rm -f a.out a.exe -else - echo "libtool.m4: error: problem compiling CXX test program" -fi - -$RM -f confest.$objext -CFLAGS=$_lt_libdeps_save_CFLAGS - -# PORTME: override above test on systems where it is broken -case $host_os in -interix[3-9]*) - # Interix 3.5 installs completely hosed .la files for C++, so rather than - # hack all around it, let's just trust "g++" to DTRT. - predep_objects_CXX= - postdep_objects_CXX= - postdeps_CXX= - ;; -esac - - -case " $postdeps_CXX " in -*" -lc "*) archive_cmds_need_lc_CXX=no ;; -esac - compiler_lib_search_dirs_CXX= -if test -n "${compiler_lib_search_path_CXX}"; then - compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` -fi - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - lt_prog_compiler_wl_CXX= -lt_prog_compiler_pic_CXX= -lt_prog_compiler_static_CXX= - - - # C++ specific cases for pic, static, wl, etc. - if test yes = "$GXX"; then - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test ia64 = "$host_cpu"; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_CXX='-Bstatic' - fi - lt_prog_compiler_pic_CXX='-fPIC' - ;; - - amigaos*) - case $host_cpu in - powerpc) - # see comment about AmigaOS4 .so support - lt_prog_compiler_pic_CXX='-fPIC' - ;; - m68k) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the '-m68020' flag to GCC prevents building anything better, - # like '-m68040'. - lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' - ;; - esac - ;; - - beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | cygwin* | os2* | pw32* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - # Although the cygwin gcc ignores -fPIC, still need this for old-style - # (--disable-auto-import) libraries - lt_prog_compiler_pic_CXX='-DDLL_EXPORT' - case $host_os in - os2*) - lt_prog_compiler_static_CXX='$wl-static' - ;; - esac - ;; - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic_CXX='-fno-common' - ;; - *djgpp*) - # DJGPP does not support shared libraries at all - lt_prog_compiler_pic_CXX= - ;; - haiku*) - # PIC is the default for Haiku. - # The "-static" flag exists, but is broken. - lt_prog_compiler_static_CXX= - ;; - interix[3-9]*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic_CXX=-Kconform_pic - fi - ;; - hpux*) - # PIC is the default for 64-bit PA HP-UX, but not for 32-bit - # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag - # sets the default TLS model and affects inlining. - case $host_cpu in - hppa*64*) - ;; - *) - lt_prog_compiler_pic_CXX='-fPIC' - ;; - esac - ;; - *qnx* | *nto*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - lt_prog_compiler_pic_CXX='-fPIC -shared' - ;; - *) - lt_prog_compiler_pic_CXX='-fPIC' - ;; - esac - else - case $host_os in - aix[4-9]*) - # All AIX code is PIC. - if test ia64 = "$host_cpu"; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_CXX='-Bstatic' - else - lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' - fi - ;; - chorus*) - case $cc_basename in - cxch68*) - # Green Hills C++ Compiler - # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" - ;; - esac - ;; - mingw* | cygwin* | os2* | pw32* | cegcc*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_CXX='-DDLL_EXPORT' - ;; - dgux*) - case $cc_basename in - ec++*) - lt_prog_compiler_pic_CXX='-KPIC' - ;; - ghcx*) - # Green Hills C++ Compiler - lt_prog_compiler_pic_CXX='-pic' - ;; - *) - ;; - esac - ;; - freebsd* | dragonfly*) - # FreeBSD uses GNU C++ - ;; - hpux9* | hpux10* | hpux11*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='$wl-a ${wl}archive' - if test ia64 != "$host_cpu"; then - lt_prog_compiler_pic_CXX='+Z' - fi - ;; - aCC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='$wl-a ${wl}archive' - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_CXX='+Z' - ;; - esac - ;; - *) - ;; - esac - ;; - interix*) - # This is c89, which is MS Visual C++ (no shared libs) - # Anyone wants to do a port? - ;; - irix5* | irix6* | nonstopux*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='-non_shared' - # CC pic flag -KPIC is the default. - ;; - *) - ;; - esac - ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - case $cc_basename in - KCC*) - # KAI C++ Compiler - lt_prog_compiler_wl_CXX='--backend -Wl,' - lt_prog_compiler_pic_CXX='-fPIC' - ;; - ecpc* ) - # old Intel C++ for x86_64, which still supported -KPIC. - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-static' - ;; - icpc* ) - # Intel C++, used to be incompatible with GCC. - # ICC 10 doesn't accept -KPIC any more. - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-fPIC' - lt_prog_compiler_static_CXX='-static' - ;; - pgCC* | pgcpp*) - # Portland Group C++ compiler - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-fpic' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - cxx*) - # Compaq C++ - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - lt_prog_compiler_pic_CXX= - lt_prog_compiler_static_CXX='-non_shared' - ;; - xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) - # IBM XL 8.0, 9.0 on PPC and BlueGene - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-qpic' - lt_prog_compiler_static_CXX='-qstaticlink' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - lt_prog_compiler_wl_CXX='-Qoption ld ' - ;; - esac - ;; - esac - ;; - lynxos*) - ;; - m88k*) - ;; - mvs*) - case $cc_basename in - cxx*) - lt_prog_compiler_pic_CXX='-W c,exportall' - ;; - *) - ;; - esac - ;; - netbsd* | netbsdelf*-gnu) - ;; - *qnx* | *nto*) - # QNX uses GNU C++, but need to define -shared option too, otherwise - # it will coredump. - lt_prog_compiler_pic_CXX='-fPIC -shared' - ;; - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - lt_prog_compiler_wl_CXX='--backend -Wl,' - ;; - RCC*) - # Rational C++ 2.4.1 - lt_prog_compiler_pic_CXX='-pic' - ;; - cxx*) - # Digital/Compaq C++ - lt_prog_compiler_wl_CXX='-Wl,' - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - lt_prog_compiler_pic_CXX= - lt_prog_compiler_static_CXX='-non_shared' - ;; - *) - ;; - esac - ;; - psos*) - ;; - solaris*) - case $cc_basename in - CC* | sunCC*) - # Sun C++ 4.2, 5.x and Centerline C++ - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - lt_prog_compiler_wl_CXX='-Qoption ld ' - ;; - gcx*) - # Green Hills C++ Compiler - lt_prog_compiler_pic_CXX='-PIC' - ;; - *) - ;; - esac - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - lt_prog_compiler_pic_CXX='-pic' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - lcc*) - # Lucid - lt_prog_compiler_pic_CXX='-pic' - ;; - *) - ;; - esac - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - lt_prog_compiler_pic_CXX='-KPIC' - ;; - *) - ;; - esac - ;; - vxworks*) - ;; - *) - lt_prog_compiler_can_build_shared_CXX=no - ;; - esac - fi - -case $host_os in - # For platforms that do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic_CXX= - ;; - *) - lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" - ;; -esac - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 -printf %s "checking for $compiler option to produce PIC... " >&6; } -if test ${lt_cv_prog_compiler_pic_CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 -printf "%s\n" "$lt_cv_prog_compiler_pic_CXX" >&6; } -lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic_CXX"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 -printf %s "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } -if test ${lt_cv_prog_compiler_pic_works_CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_pic_works_CXX=no - ac_outfile=conftest.$ac_objext - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_pic_works_CXX=yes - fi - fi - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 -printf "%s\n" "$lt_cv_prog_compiler_pic_works_CXX" >&6; } - -if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then - case $lt_prog_compiler_pic_CXX in - "" | " "*) ;; - *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; - esac -else - lt_prog_compiler_pic_CXX= - lt_prog_compiler_can_build_shared_CXX=no -fi - -fi - - - - - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -printf %s "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } -if test ${lt_cv_prog_compiler_static_works_CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_static_works_CXX=no - save_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - echo "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_static_works_CXX=yes - fi - else - lt_cv_prog_compiler_static_works_CXX=yes - fi - fi - $RM -r conftest* - LDFLAGS=$save_LDFLAGS - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 -printf "%s\n" "$lt_cv_prog_compiler_static_works_CXX" >&6; } - -if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then - : -else - lt_prog_compiler_static_CXX= -fi - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 -printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } -if test ${lt_cv_prog_compiler_c_o_CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_c_o_CXX=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_CXX=yes - fi - fi - chmod u+w . 2>&5 - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 -printf "%s\n" "$lt_cv_prog_compiler_c_o_CXX" >&6; } - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 -printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; } -if test ${lt_cv_prog_compiler_c_o_CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_prog_compiler_c_o_CXX=no - $RM -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_CXX=yes - fi - fi - chmod u+w . 2>&5 - $RM conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files - $RM out/* && rmdir out - cd .. - $RM -r conftest - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 -printf "%s\n" "$lt_cv_prog_compiler_c_o_CXX" >&6; } - - - - -hard_links=nottested -if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then - # do not overwrite the value of need_locks provided by the user - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 -printf %s "checking if we can lock with hard links... " >&6; } - hard_links=yes - $RM conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 -printf "%s\n" "$hard_links" >&6; } - if test no = "$hard_links"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 -printf "%s\n" "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -printf %s "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } - - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' - case $host_os in - aix[4-9]*) - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to GNU nm, but means don't demangle to AIX nm. - # Without the "-l" option, or with the "-B" option, AIX nm treats - # weak defined symbols like other global defined symbols, whereas - # GNU nm marks them as "W". - # While the 'weak' keyword is ignored in the Export File, we need - # it in the Import File for the 'aix-soname' feature, so we have - # to replace the "-B" option with "-P" for AIX nm. - if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' - fi - ;; - pw32*) - export_symbols_cmds_CXX=$ltdll_cmds - ;; - cygwin* | mingw* | cegcc*) - case $cc_basename in - cl*) - exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' - ;; - *) - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' - exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' - ;; - esac - ;; - linux* | k*bsd*-gnu | gnu*) - link_all_deplibs_CXX=no - ;; - *) - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - ;; - esac - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 -printf "%s\n" "$ld_shlibs_CXX" >&6; } -test no = "$ld_shlibs_CXX" && can_build_shared=no - -with_gnu_ld_CXX=$with_gnu_ld - - - - - - -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc_CXX" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc_CXX=yes - - if test yes,yes = "$GCC,$enable_shared"; then - case $archive_cmds_CXX in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 -printf %s "checking whether -lc should be explicitly linked in... " >&6; } -if test ${lt_cv_archive_cmds_need_lc_CXX+y} -then : - printf %s "(cached) " >&6 -else $as_nop - $RM conftest* - echo "$lt_simple_compile_test_code" > conftest.$ac_ext - - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl_CXX - pic_flag=$lt_prog_compiler_pic_CXX - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag_CXX - allow_undefined_flag_CXX= - if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 - (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - then - lt_cv_archive_cmds_need_lc_CXX=no - else - lt_cv_archive_cmds_need_lc_CXX=yes - fi - allow_undefined_flag_CXX=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $RM conftest* - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 -printf "%s\n" "$lt_cv_archive_cmds_need_lc_CXX" >&6; } - archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX - ;; - esac - fi - ;; -esac - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 -printf %s "checking dynamic linker characteristics... " >&6; } - -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=.so -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - - - -case $host_os in -aix3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$release$shared_ext$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='$libname$release$shared_ext$major' - ;; - -aix[4-9]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test ia64 = "$host_cpu"; then - # AIX 5 supports IA64 - library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line '#! .'. This would cause the generated library to - # depend on '.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # Using Import Files as archive members, it is possible to support - # filename-based versioning of shared library archives on AIX. While - # this would work for both with and without runtime linking, it will - # prevent static linking of such archives. So we do filename-based - # shared library versioning with .so extension only, which is used - # when both runtime linking and shared linking is enabled. - # Unfortunately, runtime linking may impact performance, so we do - # not want this to be the default eventually. Also, we use the - # versioned .so libs for executables only if there is the -brtl - # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. - # To allow for filename-based versioning support, we need to create - # libNAME.so.V as an archive file, containing: - # *) an Import File, referring to the versioned filename of the - # archive as well as the shared archive member, telling the - # bitwidth (32 or 64) of that shared object, and providing the - # list of exported symbols of that shared object, eventually - # decorated with the 'weak' keyword - # *) the shared object with the F_LOADONLY flag set, to really avoid - # it being seen by the linker. - # At run time we better use the real file rather than another symlink, - # but for link time we create the symlink libNAME.so -> libNAME.so.V - - case $with_aix_soname,$aix_use_runtimelinking in - # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - aix,yes) # traditional libtool - dynamic_linker='AIX unversionable lib.so' - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - ;; - aix,no) # traditional AIX only - dynamic_linker='AIX lib.a(lib.so.V)' - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='$libname$release.a $libname.a' - soname_spec='$libname$release$shared_ext$major' - ;; - svr4,*) # full svr4 only - dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" - library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' - # We do not specify a path in Import Files, so LIBPATH fires. - shlibpath_overrides_runpath=yes - ;; - *,yes) # both, prefer svr4 - dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" - library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' - # unpreferred sharedlib libNAME.a needs extra handling - postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' - postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' - # We do not specify a path in Import Files, so LIBPATH fires. - shlibpath_overrides_runpath=yes - ;; - *,no) # both, prefer aix - dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" - library_names_spec='$libname$release.a $libname.a' - soname_spec='$libname$release$shared_ext$major' - # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling - postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' - postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' - ;; - esac - shlibpath_var=LIBPATH - fi - ;; - -amigaos*) - case $host_cpu in - powerpc) - # Since July 2007 AmigaOS4 officially supports .so libraries. - # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - ;; - m68k) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; - esac - ;; - -beos*) - library_names_spec='$libname$shared_ext' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; - -bsdi[45]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; - -cygwin* | mingw* | pw32* | cegcc*) - version_type=windows - shrext_cmds=.dll - need_version=no - need_lib_prefix=no - - case $GCC,$cc_basename in - yes,*) - # gcc - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \$file`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname~ - if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then - eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; - fi' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' - - ;; - mingw* | cegcc*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' - ;; - esac - dynamic_linker='Win32 ld.exe' - ;; - - *,cl*) - # Native MSVC - libname_spec='$name' - soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' - library_names_spec='$libname.dll.lib' - - case $build_os in - mingw*) - sys_lib_search_path_spec= - lt_save_ifs=$IFS - IFS=';' - for lt_path in $LIB - do - IFS=$lt_save_ifs - # Let DOS variable expansion print the short 8.3 style file name. - lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` - sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" - done - IFS=$lt_save_ifs - # Convert to MSYS style. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` - ;; - cygwin*) - # Convert to unix form, then to dos form, then back to unix form - # but this time dos style (no spaces!) so that the unix form looks - # like /cygdrive/c/PROGRA~1:/cygdr... - sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` - sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` - sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - ;; - *) - sys_lib_search_path_spec=$LIB - if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH. - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - # FIXME: find the short name or the path components, as spaces are - # common. (e.g. "Program Files" -> "PROGRA~1") - ;; - esac - - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \$file`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - shlibpath_overrides_runpath=yes - dynamic_linker='Win32 link.exe' - ;; - - *) - # Assume MSVC wrapper - library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' - dynamic_linker='Win32 ld.exe' - ;; - esac - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; - -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' - soname_spec='$libname$release$major$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; - -dgux*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[23].*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2.*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - *) # from 4.6 on, and DragonFly - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; - -haiku*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - dynamic_linker="$host_os runtime_loader" - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LIBRARY_PATH - shlibpath_overrides_runpath=no - sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' - hardcode_into_libs=yes - ;; - -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - if test 32 = "$HPUX_IA64_MODE"; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - sys_lib_dlsearch_path_spec=/usr/lib/hpux32 - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - sys_lib_dlsearch_path_spec=/usr/lib/hpux64 - fi - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555, ... - postinstall_cmds='chmod 555 $lib' - # or fails outright, so override atomically: - install_override_mode=555 - ;; - -interix[3-9]*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test yes = "$lt_cv_prog_gnu_ld"; then - version_type=linux # correct to gnu/linux during the next big refactor - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='$libname$release$shared_ext$major' - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" - sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" - hardcode_into_libs=yes - ;; - -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; - -linux*android*) - version_type=none # Android doesn't support versioned libraries. - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext' - soname_spec='$libname$release$shared_ext' - finish_cmds= - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - dynamic_linker='Android linker' - # Don't embed -rpath directories since the linker doesn't support them. - hardcode_libdir_flag_spec_CXX='-L$libdir' - ;; - -# This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - - # Some binutils ld are patched to set DT_RUNPATH - if test ${lt_cv_shlibpath_overrides_runpath+y} -then : - printf %s "(cached) " >&6 -else $as_nop - lt_cv_shlibpath_overrides_runpath=no - save_LDFLAGS=$LDFLAGS - save_libdir=$libdir - eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ - LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO" -then : - if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null -then : - lt_cv_shlibpath_overrides_runpath=yes -fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext - LDFLAGS=$save_LDFLAGS - libdir=$save_libdir - -fi - - shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath - - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - - # Ideally, we could use ldconfig to report *all* directores which are - # searched for libraries, however this is still not possible. Aside from not - # being certain /sbin/ldconfig is available, command - # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, - # even though it is searched at run-time. Try to do the best guess by - # appending ld.so.conf contents (and includes) to the search path. - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; - -netbsdelf*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='NetBSD ld.elf_so' - ;; - -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - -newsos6) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -*nto* | *qnx*) - version_type=qnx - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='ldqnx.so' - ;; - -openbsd* | bitrig*) - version_type=sunos - sys_lib_dlsearch_path_spec=/usr/lib - need_lib_prefix=no - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then - need_version=no - else - need_version=yes - fi - library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; - -os2*) - libname_spec='$name' - version_type=windows - shrext_cmds=.dll - need_version=no - need_lib_prefix=no - # OS/2 can only load a DLL with a base name of 8 characters or less. - soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; - v=$($ECHO $release$versuffix | tr -d .-); - n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); - $ECHO $n$v`$shared_ext' - library_names_spec='${libname}_dll.$libext' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=BEGINLIBPATH - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - postinstall_cmds='base_file=`basename \$file`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname~ - if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then - eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; - fi' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $RM \$dlpath' - ;; - -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='$libname$release$shared_ext$major' - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - -rdos*) - dynamic_linker=no - ;; - -solaris*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; - -sunos4*) - version_type=sunos - library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test yes = "$with_gnu_ld"; then - need_lib_prefix=no - fi - need_version=yes - ;; - -sysv4 | sysv4.3*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; - -sysv4*MP*) - if test -d /usr/nec; then - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' - soname_spec='$libname$shared_ext.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; - -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=sco - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - if test yes = "$with_gnu_ld"; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -tpf*) - # TPF is a cross-target only. Preferred cross-host = GNU/Linux. - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - -uts4*) - version_type=linux # correct to gnu/linux during the next big refactor - library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' - soname_spec='$libname$release$shared_ext$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 -printf "%s\n" "$dynamic_linker" >&6; } -test no = "$dynamic_linker" && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test yes = "$GCC"; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then - sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec -fi - -if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then - sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec -fi - -# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... -configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec - -# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code -func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" - -# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool -configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 -printf %s "checking how to hardcode library paths into programs... " >&6; } -hardcode_action_CXX= -if test -n "$hardcode_libdir_flag_spec_CXX" || - test -n "$runpath_var_CXX" || - test yes = "$hardcode_automatic_CXX"; then - - # We can hardcode non-existent directories. - if test no != "$hardcode_direct_CXX" && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && - test no != "$hardcode_minus_L_CXX"; then - # Linking always hardcodes the temporary library directory. - hardcode_action_CXX=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action_CXX=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action_CXX=unsupported -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 -printf "%s\n" "$hardcode_action_CXX" >&6; } - -if test relink = "$hardcode_action_CXX" || - test yes = "$inherit_rpath_CXX"; then - # Fast installation is not supported - enable_fast_install=no -elif test yes = "$shlibpath_overrides_runpath" || - test no = "$enable_shared"; then - # Fast installation is not necessary - enable_fast_install=needless -fi - - - - - - - - fi # test -n "$compiler" - - CC=$lt_save_CC - CFLAGS=$lt_save_CFLAGS - LDCXX=$LD - LD=$lt_save_LD - GCC=$lt_save_GCC - with_gnu_ld=$lt_save_with_gnu_ld - lt_cv_path_LDCXX=$lt_cv_path_LD - lt_cv_path_LD=$lt_save_path_LD - lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld - lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld -fi # test yes != "$_lt_caught_CXX_error" - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - -case $host in - *mingw*) - LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static" - ;; -esac - -BUILD_EXEEXT= -case $build in - *mingw*) - BUILD_EXEEXT=".exe" - ;; -esac - -ac_config_files="$ac_config_files Makefile pc/libunivalue.pc pc/libunivalue-uninstalled.pc" - - - - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -printf "%s\n" "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 -printf %s "checking that generated files are newer than configure... " >&6; } - if test -n "$am_sleep_pid"; then - # Hide warnings about reused PIDs. - wait $am_sleep_pid 2>/dev/null - fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5 -printf "%s\n" "done" >&6; } - if test -n "$EXEEXT"; then - am__EXEEXT_TRUE= - am__EXEEXT_FALSE='#' -else - am__EXEEXT_TRUE='#' - am__EXEEXT_FALSE= -fi - -if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then - as_fn_error $? "conditional \"AMDEP\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then - as_fn_error $? "conditional \"am__fastdepCC\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi -if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then - as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -as_nop=: -if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 -then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else $as_nop - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - - -# Reset variables that may have inherited troublesome values from -# the environment. - -# IFS needs to be set, to space, tab, and newline, in precisely that order. -# (If _AS_PATH_WALK were called with IFS unset, it would have the -# side effect of setting IFS to empty, thus disabling word splitting.) -# Quoting is to prevent editors from complaining about space-tab. -as_nl=' -' -export as_nl -IFS=" "" $as_nl" - -PS1='$ ' -PS2='> ' -PS4='+ ' - -# Ensure predictable behavior from utilities with locale-dependent output. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# We cannot yet rely on "unset" to work, but we need these variables -# to be unset--not just set to an empty or harmless value--now, to -# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct -# also avoids known problems related to "unset" and subshell syntax -# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). -for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH -do eval test \${$as_var+y} \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done - -# Ensure that fds 0, 1, and 2 are open. -if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi -if (exec 3>&2) ; then :; else exec 2>/dev/null; fi - -# The user is always right. -if ${PATH_SEPARATOR+false} :; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - test -r "$as_dir$0" && as_myself=$as_dir$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - printf "%s\n" "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null -then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else $as_nop - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null -then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else $as_nop - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - -# Determine whether it's possible to make 'echo' print without a newline. -# These variables are no longer used directly by Autoconf, but are AC_SUBSTed -# for compatibility with existing Makefiles. -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -# For backward compatibility with old third-party macros, we provide -# the shell variables $as_echo and $as_echo_n. New code should use -# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. -as_echo='printf %s\n' -as_echo_n='printf %s' - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by univalue $as_me 1.0.3, which was -generated by GNU Autoconf 2.71. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - -case $ac_config_headers in *" -"*) set x $ac_config_headers; shift; ac_config_headers=$*;; -esac - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" -config_commands="$ac_config_commands" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Configuration commands: -$config_commands - -Report bugs to ." - -_ACEOF -ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` -ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config='$ac_cs_config_escaped' -ac_cs_version="\\ -univalue config.status 1.0.3 -configured by $0, generated by GNU Autoconf 2.71, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2021 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -INSTALL='$INSTALL' -MKDIR_P='$MKDIR_P' -AWK='$AWK' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - printf "%s\n" "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - printf "%s\n" "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append CONFIG_HEADERS " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h) - # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; - --help | --hel | -h ) - printf "%s\n" "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - printf "%s\n" "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# -# INIT-COMMANDS -# -AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" - - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -sed_quote_subst='$sed_quote_subst' -double_quote_subst='$double_quote_subst' -delay_variable_subst='$delay_variable_subst' -macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' -macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' -enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' -enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' -pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' -enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' -shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' -SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' -ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' -PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' -host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' -host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' -host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' -build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' -build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' -build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' -SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' -Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' -GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' -EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' -FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' -LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' -NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' -LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' -max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' -ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' -exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' -lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' -lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' -lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' -lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' -lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' -reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' -reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' -OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' -deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' -file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' -file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' -want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' -DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' -sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' -AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' -AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' -archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' -STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' -RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' -old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' -old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' -old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' -lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' -CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' -CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' -compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' -GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' -lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' -nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' -lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' -lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' -objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' -MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' -lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' -need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' -MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' -DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' -NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' -LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' -OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' -OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' -libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' -shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' -extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' -archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' -enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' -export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' -whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' -compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' -old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' -old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' -archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' -archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' -module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' -module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' -with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' -allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' -no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' -hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' -hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' -hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' -hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' -hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' -hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' -hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' -inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' -link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' -always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' -export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' -exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' -include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' -prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' -postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' -file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' -variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' -need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' -need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' -version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' -runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' -shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' -shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' -libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' -library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' -soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' -install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' -postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' -postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' -finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' -finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' -hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' -sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' -configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' -configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' -hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' -enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' -enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' -enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' -old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' -striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' -compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' -predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' -postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' -predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' -postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' -compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' -LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' -reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' -reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' -old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' -compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' -GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' -lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' -lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' -archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' -enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' -export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' -whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' -compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' -old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' -old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' -archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' -archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' -module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' -module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' -with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' -allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' -no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' -inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' -link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' -always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' -export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' -exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' -include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' -prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' -postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' -file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' -hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' -compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' -predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' -postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' -predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' -postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' -compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' - -LTCC='$LTCC' -LTCFLAGS='$LTCFLAGS' -compiler='$compiler_DEFAULT' - -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -\$1 -_LTECHO_EOF' -} - -# Quote evaled strings. -for var in SHELL \ -ECHO \ -PATH_SEPARATOR \ -SED \ -GREP \ -EGREP \ -FGREP \ -LD \ -NM \ -LN_S \ -lt_SP2NL \ -lt_NL2SP \ -reload_flag \ -OBJDUMP \ -deplibs_check_method \ -file_magic_cmd \ -file_magic_glob \ -want_nocaseglob \ -DLLTOOL \ -sharedlib_from_linklib_cmd \ -AR \ -AR_FLAGS \ -archiver_list_spec \ -STRIP \ -RANLIB \ -CC \ -CFLAGS \ -compiler \ -lt_cv_sys_global_symbol_pipe \ -lt_cv_sys_global_symbol_to_cdecl \ -lt_cv_sys_global_symbol_to_import \ -lt_cv_sys_global_symbol_to_c_name_address \ -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ -lt_cv_nm_interface \ -nm_file_list_spec \ -lt_cv_truncate_bin \ -lt_prog_compiler_no_builtin_flag \ -lt_prog_compiler_pic \ -lt_prog_compiler_wl \ -lt_prog_compiler_static \ -lt_cv_prog_compiler_c_o \ -need_locks \ -MANIFEST_TOOL \ -DSYMUTIL \ -NMEDIT \ -LIPO \ -OTOOL \ -OTOOL64 \ -shrext_cmds \ -export_dynamic_flag_spec \ -whole_archive_flag_spec \ -compiler_needs_object \ -with_gnu_ld \ -allow_undefined_flag \ -no_undefined_flag \ -hardcode_libdir_flag_spec \ -hardcode_libdir_separator \ -exclude_expsyms \ -include_expsyms \ -file_list_spec \ -variables_saved_for_relink \ -libname_spec \ -library_names_spec \ -soname_spec \ -install_override_mode \ -finish_eval \ -old_striplib \ -striplib \ -compiler_lib_search_dirs \ -predep_objects \ -postdep_objects \ -predeps \ -postdeps \ -compiler_lib_search_path \ -LD_CXX \ -reload_flag_CXX \ -compiler_CXX \ -lt_prog_compiler_no_builtin_flag_CXX \ -lt_prog_compiler_pic_CXX \ -lt_prog_compiler_wl_CXX \ -lt_prog_compiler_static_CXX \ -lt_cv_prog_compiler_c_o_CXX \ -export_dynamic_flag_spec_CXX \ -whole_archive_flag_spec_CXX \ -compiler_needs_object_CXX \ -with_gnu_ld_CXX \ -allow_undefined_flag_CXX \ -no_undefined_flag_CXX \ -hardcode_libdir_flag_spec_CXX \ -hardcode_libdir_separator_CXX \ -exclude_expsyms_CXX \ -include_expsyms_CXX \ -file_list_spec_CXX \ -compiler_lib_search_dirs_CXX \ -predep_objects_CXX \ -postdep_objects_CXX \ -predeps_CXX \ -postdeps_CXX \ -compiler_lib_search_path_CXX; do - case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in - *[\\\\\\\`\\"\\\$]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes - ;; - *) - eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" - ;; - esac -done - -# Double-quote double-evaled strings. -for var in reload_cmds \ -old_postinstall_cmds \ -old_postuninstall_cmds \ -old_archive_cmds \ -extract_expsyms_cmds \ -old_archive_from_new_cmds \ -old_archive_from_expsyms_cmds \ -archive_cmds \ -archive_expsym_cmds \ -module_cmds \ -module_expsym_cmds \ -export_symbols_cmds \ -prelink_cmds \ -postlink_cmds \ -postinstall_cmds \ -postuninstall_cmds \ -finish_cmds \ -sys_lib_search_path_spec \ -configure_time_dlsearch_path \ -configure_time_lt_sys_library_path \ -reload_cmds_CXX \ -old_archive_cmds_CXX \ -old_archive_from_new_cmds_CXX \ -old_archive_from_expsyms_cmds_CXX \ -archive_cmds_CXX \ -archive_expsym_cmds_CXX \ -module_cmds_CXX \ -module_expsym_cmds_CXX \ -export_symbols_cmds_CXX \ -prelink_cmds_CXX \ -postlink_cmds_CXX; do - case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in - *[\\\\\\\`\\"\\\$]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes - ;; - *) - eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" - ;; - esac -done - -ac_aux_dir='$ac_aux_dir' - -# See if we are running on zsh, and set the options that allow our -# commands through without removal of \ escapes INIT. -if test -n "\${ZSH_VERSION+set}"; then - setopt NO_GLOB_SUBST -fi - - - PACKAGE='$PACKAGE' - VERSION='$VERSION' - RM='$RM' - ofile='$ofile' - - - - - - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "univalue-config.h") CONFIG_HEADERS="$CONFIG_HEADERS univalue-config.h" ;; - "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; - "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; - "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "pc/libunivalue.pc") CONFIG_FILES="$CONFIG_FILES pc/libunivalue.pc" ;; - "pc/libunivalue-uninstalled.pc") CONFIG_FILES="$CONFIG_FILES pc/libunivalue-uninstalled.pc" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files - test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers - test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - -# Set up the scripts for CONFIG_HEADERS section. -# No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. -if test -n "$CONFIG_HEADERS"; then -cat >"$ac_tmp/defines.awk" <<\_ACAWK || -BEGIN { -_ACEOF - -# Transform confdefs.h into an awk script `defines.awk', embedded as -# here-document in config.status, that substitutes the proper values into -# config.h.in to produce config.h. - -# Create a delimiter string that does not exist in confdefs.h, to ease -# handling of long lines. -ac_delim='%!_!# ' -for ac_last_try in false false :; do - ac_tt=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_tt"; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done - -# For the awk script, D is an array of macro values keyed by name, -# likewise P contains macro parameters if any. Preserve backslash -# newline sequences. - -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -sed -n ' -s/.\{148\}/&'"$ac_delim"'/g -t rset -:rset -s/^[ ]*#[ ]*define[ ][ ]*/ / -t def -d -:def -s/\\$// -t bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3"/p -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p -d -:bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3\\\\\\n"\\/p -t cont -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p -t cont -d -:cont -n -s/.\{148\}/&'"$ac_delim"'/g -t clear -:clear -s/\\$// -t bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/"/p -d -:bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p -b cont -' >$CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - for (key in D) D_is_set[key] = 1 - FS = "" -} -/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { - line = \$ 0 - split(line, arg, " ") - if (arg[1] == "#") { - defundef = arg[2] - mac1 = arg[3] - } else { - defundef = substr(arg[1], 2) - mac1 = arg[2] - } - split(mac1, mac2, "(") #) - macro = mac2[1] - prefix = substr(line, 1, index(line, defundef) - 1) - if (D_is_set[macro]) { - # Preserve the white space surrounding the "#". - print prefix "define", macro P[macro] D[macro] - next - } else { - # Replace #undef with comments. This is necessary, for example, - # in the case of _POSIX_SOURCE, which is predefined and required - # on some systems where configure will not decide to define it. - if (defundef == "undef") { - print "/*", prefix defundef, macro, "*/" - next - } - } -} -{ print } -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 -fi # test -n "$CONFIG_HEADERS" - - -eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -printf "%s\n" "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`printf "%s\n" "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; - esac - ac_MKDIR_P=$MKDIR_P - case $MKDIR_P in - [\\/$]* | ?:[\\/]* ) ;; - */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; - esac -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -s&@INSTALL@&$ac_INSTALL&;t t -s&@MKDIR_P@&$ac_MKDIR_P&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - :H) - # - # CONFIG_HEADER - # - if test x"$ac_file" != x-; then - { - printf "%s\n" "/* $configure_input */" >&1 \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" - } >"$ac_tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} - else - rm -f "$ac_file" - mv "$ac_tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - fi - else - printf "%s\n" "/* $configure_input */" >&1 \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 - fi -# Compute "$ac_file"'s index in $config_headers. -_am_arg="$ac_file" -_am_stamp_count=1 -for _am_header in $config_headers :; do - case $_am_header in - $_am_arg | $_am_arg:* ) - break ;; - * ) - _am_stamp_count=`expr $_am_stamp_count + 1` ;; - esac -done -echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || -$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$_am_arg" : 'X\(//\)[^/]' \| \ - X"$_am_arg" : 'X\(//\)$' \| \ - X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$_am_arg" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'`/stamp-h$_am_stamp_count - ;; - - :C) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 -printf "%s\n" "$as_me: executing $ac_file commands" >&6;} - ;; - esac - - - case $ac_file$ac_mode in - "depfiles":C) test x"$AMDEP_TRUE" != x"" || { - # Older Autoconf quotes --file arguments for eval, but not when files - # are listed without --file. Let's play safe and only enable the eval - # if we detect the quoting. - # TODO: see whether this extra hack can be removed once we start - # requiring Autoconf 2.70 or later. - case $CONFIG_FILES in #( - *\'*) : - eval set x "$CONFIG_FILES" ;; #( - *) : - set x $CONFIG_FILES ;; #( - *) : - ;; -esac - shift - # Used to flag and report bootstrapping failures. - am_rc=0 - for am_mf - do - # Strip MF so we end up with the name of the file. - am_mf=`printf "%s\n" "$am_mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile which includes - # dependency-tracking related rules and includes. - # Grep'ing the whole file directly is not great: AIX grep has a line - # limit of 2048, but all sed's we know have understand at least 4000. - sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ - || continue - am_dirpart=`$as_dirname -- "$am_mf" || -$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$am_mf" : 'X\(//\)[^/]' \| \ - X"$am_mf" : 'X\(//\)$' \| \ - X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$am_mf" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - am_filepart=`$as_basename -- "$am_mf" || -$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ - X"$am_mf" : 'X\(//\)$' \| \ - X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X/"$am_mf" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - { echo "$as_me:$LINENO: cd "$am_dirpart" \ - && sed -e '/# am--include-marker/d' "$am_filepart" \ - | $MAKE -f - am--depfiles" >&5 - (cd "$am_dirpart" \ - && sed -e '/# am--include-marker/d' "$am_filepart" \ - | $MAKE -f - am--depfiles) >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } || am_rc=$? - done - if test $am_rc -ne 0; then - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. If GNU make was not used, consider - re-running the configure script with MAKE=\"gmake\" (or whatever is - necessary). You can also try re-running configure with the - '--disable-dependency-tracking' option to at least be able to build - the package (albeit without support for automatic dependency tracking). -See \`config.log' for more details" "$LINENO" 5; } - fi - { am_dirpart=; unset am_dirpart;} - { am_filepart=; unset am_filepart;} - { am_mf=; unset am_mf;} - { am_rc=; unset am_rc;} - rm -f conftest-deps.mk -} - ;; - "libtool":C) - - # See if we are running on zsh, and set the options that allow our - # commands through without removal of \ escapes. - if test -n "${ZSH_VERSION+set}"; then - setopt NO_GLOB_SUBST - fi - - cfgfile=${ofile}T - trap "$RM \"$cfgfile\"; exit 1" 1 2 15 - $RM "$cfgfile" - - cat <<_LT_EOF >> "$cfgfile" -#! $SHELL -# Generated automatically by $as_me ($PACKAGE) $VERSION -# NOTE: Changes made to this file will be lost: look at ltmain.sh. - -# Provide generalized library-building support services. -# Written by Gordon Matzigkeit, 1996 - -# Copyright (C) 2014 Free Software Foundation, Inc. -# This is free software; see the source for copying conditions. There is NO -# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -# GNU Libtool is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of of the License, or -# (at your option) any later version. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program or library that is built -# using GNU Libtool, you may include this file under the same -# distribution terms that you use for the rest of that program. -# -# GNU Libtool is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -# The names of the tagged configurations supported by this script. -available_tags='CXX ' - -# Configured defaults for sys_lib_dlsearch_path munging. -: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} - -# ### BEGIN LIBTOOL CONFIG - -# Which release of libtool.m4 was used? -macro_version=$macro_version -macro_revision=$macro_revision - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# What type of objects to build. -pic_mode=$pic_mode - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# Shared archive member basename,for filename based shared library versioning on AIX. -shared_archive_member_spec=$shared_archive_member_spec - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# An echo program that protects backslashes. -ECHO=$lt_ECHO - -# The PATH separator for the build system. -PATH_SEPARATOR=$lt_PATH_SEPARATOR - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# A sed program that does not truncate output. -SED=$lt_SED - -# Sed that helps us avoid accidentally triggering echo(1) options like -n. -Xsed="\$SED -e 1s/^X//" - -# A grep program that handles long lines. -GREP=$lt_GREP - -# An ERE matcher. -EGREP=$lt_EGREP - -# A literal string matcher. -FGREP=$lt_FGREP - -# A BSD- or MS-compatible name lister. -NM=$lt_NM - -# Whether we need soft or hard links. -LN_S=$lt_LN_S - -# What is the maximum length of a command? -max_cmd_len=$max_cmd_len - -# Object file suffix (normally "o"). -objext=$ac_objext - -# Executable file suffix (normally ""). -exeext=$exeext - -# whether the shell understands "unset". -lt_unset=$lt_unset - -# turn spaces into newlines. -SP2NL=$lt_lt_SP2NL - -# turn newlines into spaces. -NL2SP=$lt_lt_NL2SP - -# convert \$build file names to \$host format. -to_host_file_cmd=$lt_cv_to_host_file_cmd - -# convert \$build files to toolchain format. -to_tool_file_cmd=$lt_cv_to_tool_file_cmd - -# An object symbol dumper. -OBJDUMP=$lt_OBJDUMP - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method = "file_magic". -file_magic_cmd=$lt_file_magic_cmd - -# How to find potential files when deplibs_check_method = "file_magic". -file_magic_glob=$lt_file_magic_glob - -# Find potential files using nocaseglob when deplibs_check_method = "file_magic". -want_nocaseglob=$lt_want_nocaseglob - -# DLL creation program. -DLLTOOL=$lt_DLLTOOL - -# Command to associate shared and link libraries. -sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd - -# The archiver. -AR=$lt_AR - -# Flags to create an archive. -AR_FLAGS=$lt_AR_FLAGS - -# How to feed a file listing to the archiver. -archiver_list_spec=$lt_archiver_list_spec - -# A symbol stripping program. -STRIP=$lt_STRIP - -# Commands used to install an old-style archive. -RANLIB=$lt_RANLIB -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Whether to use a lock for old archive extraction. -lock_old_archive_extraction=$lock_old_archive_extraction - -# A C compiler. -LTCC=$lt_CC - -# LTCC compiler flags. -LTCFLAGS=$lt_CFLAGS - -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe - -# Transform the output of nm in a proper C declaration. -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl - -# Transform the output of nm into a list of symbols to manually relocate. -global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import - -# Transform the output of nm in a C name address pair. -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address - -# Transform the output of nm in a C name address pair when lib prefix is needed. -global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix - -# The name lister interface. -nm_interface=$lt_lt_cv_nm_interface - -# Specify filename containing input files for \$NM. -nm_file_list_spec=$lt_nm_file_list_spec - -# The root where to search for dependent libraries,and where our libraries should be installed. -lt_sysroot=$lt_sysroot - -# Command to truncate a binary pipe. -lt_truncate_bin=$lt_lt_cv_truncate_bin - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# Used to examine libraries when file_magic_cmd begins with "file". -MAGIC_CMD=$MAGIC_CMD - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Manifest tool. -MANIFEST_TOOL=$lt_MANIFEST_TOOL - -# Tool to manipulate archived DWARF debug symbol files on Mac OS X. -DSYMUTIL=$lt_DSYMUTIL - -# Tool to change global to local symbols on Mac OS X. -NMEDIT=$lt_NMEDIT - -# Tool to manipulate fat objects and archives on Mac OS X. -LIPO=$lt_LIPO - -# ldd/readelf like tool for Mach-O binaries on Mac OS X. -OTOOL=$lt_OTOOL - -# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. -OTOOL64=$lt_OTOOL64 - -# Old archive suffix (normally "a"). -libext=$libext - -# Shared library suffix (normally ".so"). -shrext_cmds=$lt_shrext_cmds - -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Variables whose values should be saved in libtool wrapper scripts and -# restored at link time. -variables_saved_for_relink=$lt_variables_saved_for_relink - -# Do we need the "lib" prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Library versioning type. -version_type=$version_type - -# Shared library runtime path variable. -runpath_var=$runpath_var - -# Shared library path variable. -shlibpath_var=$shlibpath_var - -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Permission mode override for installation of shared libraries. -install_override_mode=$lt_install_override_mode - -# Command to use after installation of a shared archive. -postinstall_cmds=$lt_postinstall_cmds - -# Command to use after uninstallation of a shared archive. -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds - -# As "finish_cmds", except a single script fragment to be evaled but -# not shown. -finish_eval=$lt_finish_eval - -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs - -# Compile-time system search path for libraries. -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec - -# Detected run-time system search path for libraries. -sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path - -# Explicit LT_SYS_LIBRARY_PATH set during ./configure time. -configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - - -# The linker used to build libraries. -LD=$lt_LD - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# Commands used to build an old-style archive. -old_archive_cmds=$lt_old_archive_cmds - -# A language specific compiler. -CC=$lt_compiler - -# Is the compiler the GNU compiler? -with_gcc=$GCC - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc - -# Whether or not to disallow shared libs when runtime libs are static. -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec - -# Whether the compiler copes with passing no objects directly. -compiler_needs_object=$lt_compiler_needs_object - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds - -# Commands used to build a shared archive. -archive_cmds=$lt_archive_cmds -archive_expsym_cmds=$lt_archive_expsym_cmds - -# Commands used to build a loadable module if different from building -# a shared archive. -module_cmds=$lt_module_cmds -module_expsym_cmds=$lt_module_expsym_cmds - -# Whether we are building with GNU ld or not. -with_gnu_ld=$lt_with_gnu_ld - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag - -# Flag that enforces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec - -# Whether we need a single "-rpath" flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator - -# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes -# DIR into the resulting binary. -hardcode_direct=$hardcode_direct - -# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes -# DIR into the resulting binary and the resulting library dependency is -# "absolute",i.e impossible to change by setting \$shlibpath_var if the -# library is relocated. -hardcode_direct_absolute=$hardcode_direct_absolute - -# Set to "yes" if using the -LDIR flag during linking hardcodes DIR -# into the resulting binary. -hardcode_minus_L=$hardcode_minus_L - -# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR -# into the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var - -# Set to "yes" if building a shared library automatically hardcodes DIR -# into the library and all subsequent libraries and executables linked -# against it. -hardcode_automatic=$hardcode_automatic - -# Set to yes if linker adds runtime paths of dependent libraries -# to runtime path list. -inherit_rpath=$inherit_rpath - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs - -# Set to "yes" if exported symbols are required. -always_export_symbols=$always_export_symbols - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms - -# Commands necessary for linking programs (against libraries) with templates. -prelink_cmds=$lt_prelink_cmds - -# Commands necessary for finishing linking programs. -postlink_cmds=$lt_postlink_cmds - -# Specify filename containing input files. -file_list_spec=$lt_file_list_spec - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action - -# The directories searched by this compiler when creating a shared library. -compiler_lib_search_dirs=$lt_compiler_lib_search_dirs - -# Dependencies to place before and after the objects being linked to -# create a shared library. -predep_objects=$lt_predep_objects -postdep_objects=$lt_postdep_objects -predeps=$lt_predeps -postdeps=$lt_postdeps - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path - -# ### END LIBTOOL CONFIG - -_LT_EOF - - cat <<'_LT_EOF' >> "$cfgfile" - -# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE - -# func_munge_path_list VARIABLE PATH -# ----------------------------------- -# VARIABLE is name of variable containing _space_ separated list of -# directories to be munged by the contents of PATH, which is string -# having a format: -# "DIR[:DIR]:" -# string "DIR[ DIR]" will be prepended to VARIABLE -# ":DIR[:DIR]" -# string "DIR[ DIR]" will be appended to VARIABLE -# "DIRP[:DIRP]::[DIRA:]DIRA" -# string "DIRP[ DIRP]" will be prepended to VARIABLE and string -# "DIRA[ DIRA]" will be appended to VARIABLE -# "DIR[:DIR]" -# VARIABLE will be replaced by "DIR[ DIR]" -func_munge_path_list () -{ - case x$2 in - x) - ;; - *:) - eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" - ;; - x:*) - eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" - ;; - *::*) - eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" - eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" - ;; - *) - eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" - ;; - esac -} - - -# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. -func_cc_basename () -{ - for cc_temp in $*""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac - done - func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` -} - - -# ### END FUNCTIONS SHARED WITH CONFIGURE - -_LT_EOF - - case $host_os in - aix3*) - cat <<\_LT_EOF >> "$cfgfile" -# AIX sometimes has problems with the GCC collect2 program. For some -# reason, if we set the COLLECT_NAMES environment variable, the problems -# vanish in a puff of smoke. -if test set != "${COLLECT_NAMES+set}"; then - COLLECT_NAMES= - export COLLECT_NAMES -fi -_LT_EOF - ;; - esac - - - -ltmain=$ac_aux_dir/ltmain.sh - - - # We use sed instead of cat because bash on DJGPP gets confused if - # if finds mixed CR/LF and LF-only lines. Since sed operates in - # text mode, it properly converts lines to CR/LF. This bash problem - # is reportedly fixed, but why not run on old versions too? - sed '$q' "$ltmain" >> "$cfgfile" \ - || (rm -f "$cfgfile"; exit 1) - - mv -f "$cfgfile" "$ofile" || - (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") - chmod +x "$ofile" - - - cat <<_LT_EOF >> "$ofile" - -# ### BEGIN LIBTOOL TAG CONFIG: CXX - -# The linker used to build libraries. -LD=$lt_LD_CXX - -# How to create reloadable object files. -reload_flag=$lt_reload_flag_CXX -reload_cmds=$lt_reload_cmds_CXX - -# Commands used to build an old-style archive. -old_archive_cmds=$lt_old_archive_cmds_CXX - -# A language specific compiler. -CC=$lt_compiler_CXX - -# Is the compiler the GNU compiler? -with_gcc=$GCC_CXX - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_CXX - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_CXX - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_CXX - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_CXX - -# Whether or not to disallow shared libs when runtime libs are static. -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX - -# Whether the compiler copes with passing no objects directly. -compiler_needs_object=$lt_compiler_needs_object_CXX - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX - -# Commands used to build a shared archive. -archive_cmds=$lt_archive_cmds_CXX -archive_expsym_cmds=$lt_archive_expsym_cmds_CXX - -# Commands used to build a loadable module if different from building -# a shared archive. -module_cmds=$lt_module_cmds_CXX -module_expsym_cmds=$lt_module_expsym_cmds_CXX - -# Whether we are building with GNU ld or not. -with_gnu_ld=$lt_with_gnu_ld_CXX - -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_CXX - -# Flag that enforces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_CXX - -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX - -# Whether we need a single "-rpath" flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX - -# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes -# DIR into the resulting binary. -hardcode_direct=$hardcode_direct_CXX - -# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes -# DIR into the resulting binary and the resulting library dependency is -# "absolute",i.e impossible to change by setting \$shlibpath_var if the -# library is relocated. -hardcode_direct_absolute=$hardcode_direct_absolute_CXX - -# Set to "yes" if using the -LDIR flag during linking hardcodes DIR -# into the resulting binary. -hardcode_minus_L=$hardcode_minus_L_CXX - -# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR -# into the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX - -# Set to "yes" if building a shared library automatically hardcodes DIR -# into the library and all subsequent libraries and executables linked -# against it. -hardcode_automatic=$hardcode_automatic_CXX - -# Set to yes if linker adds runtime paths of dependent libraries -# to runtime path list. -inherit_rpath=$inherit_rpath_CXX - -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_CXX - -# Set to "yes" if exported symbols are required. -always_export_symbols=$always_export_symbols_CXX - -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_CXX - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_CXX - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_CXX - -# Commands necessary for linking programs (against libraries) with templates. -prelink_cmds=$lt_prelink_cmds_CXX - -# Commands necessary for finishing linking programs. -postlink_cmds=$lt_postlink_cmds_CXX - -# Specify filename containing input files. -file_list_spec=$lt_file_list_spec_CXX - -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_CXX - -# The directories searched by this compiler when creating a shared library. -compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX - -# Dependencies to place before and after the objects being linked to -# create a shared library. -predep_objects=$lt_predep_objects_CXX -postdep_objects=$lt_postdep_objects_CXX -predeps=$lt_predeps_CXX -postdeps=$lt_postdeps_CXX - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_CXX - -# ### END LIBTOOL TAG CONFIG: CXX -_LT_EOF - - ;; - - esac -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - - - diff --git a/src/univalue/gen/gen.cpp b/src/univalue/gen/gen.cpp deleted file mode 100644 index a6530481d..000000000 --- a/src/univalue/gen/gen.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2014 BitPay Inc. -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -// -// To re-create univalue_escapes.h: -// $ g++ -o gen gen.cpp -// $ ./gen > univalue_escapes.h -// - -#include -#include -#include "univalue.h" - -using namespace std; - -static bool initEscapes; -static std::string escapes[256]; - -static void initJsonEscape() -{ - // Escape all lower control characters (some get overridden with smaller sequences below) - for (int ch=0x00; ch<0x20; ++ch) { - char tmpbuf[20]; - snprintf(tmpbuf, sizeof(tmpbuf), "\\u%04x", ch); - escapes[ch] = std::string(tmpbuf); - } - - escapes[(int)'"'] = "\\\""; - escapes[(int)'\\'] = "\\\\"; - escapes[(int)'\b'] = "\\b"; - escapes[(int)'\f'] = "\\f"; - escapes[(int)'\n'] = "\\n"; - escapes[(int)'\r'] = "\\r"; - escapes[(int)'\t'] = "\\t"; - escapes[(int)'\x7f'] = "\\u007f"; // U+007F DELETE - - initEscapes = true; -} - -static void outputEscape() -{ - printf( "// Automatically generated file. Do not modify.\n" - "#ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n" - "#define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n" - "static const char *escapes[256] = {\n"); - - for (unsigned int i = 0; i < 256; i++) { - if (escapes[i].empty()) { - printf("\tNULL,\n"); - } else { - printf("\t\""); - - unsigned int si; - for (si = 0; si < escapes[i].size(); si++) { - char ch = escapes[i][si]; - switch (ch) { - case '"': - printf("\\\""); - break; - case '\\': - printf("\\\\"); - break; - default: - printf("%c", escapes[i][si]); - break; - } - } - - printf("\",\n"); - } - } - - printf( "};\n" - "#endif // BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H\n"); -} - -int main (int argc, char *argv[]) -{ - initJsonEscape(); - outputEscape(); - return 0; -} - diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index f4dea4af9..da1215755 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -1,66 +1,68 @@ // Copyright 2014 BitPay Inc. // Copyright 2015 Bitcoin Core Developers // Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . +// file COPYING or https://opensource.org/licenses/mit-license.php. -#ifndef UNIVALUE_H__ -#define UNIVALUE_H__ - -#include +#ifndef BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_H +#define BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_H +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include -#include -#include - -#include // .get_int64() -#include // std::pair +// NOLINTNEXTLINE(misc-no-recursion) class UniValue { public: enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, }; + class type_error : public std::runtime_error + { + using std::runtime_error::runtime_error; + }; + UniValue() { typ = VNULL; } - UniValue(UniValue::VType initialType, const std::string& initialStr = "") { - typ = initialType; - val = initialStr; - } - UniValue(uint64_t val_) { - setInt(val_); - } - UniValue(int64_t val_) { - setInt(val_); - } - UniValue(bool val_) { - setBool(val_); - } - UniValue(int val_) { - setInt(val_); - } - UniValue(double val_) { - setFloat(val_); - } - UniValue(const std::string& val_) { - setStr(val_); - } - UniValue(const char *val_) { - std::string s(val_); - setStr(s); + UniValue(UniValue::VType type, std::string str = {}) : typ{type}, val{std::move(str)} {} + template >, + std::enable_if_t || // setFloat + std::is_same_v || // setBool + std::is_signed_v || std::is_unsigned_v || // setInt + std::is_constructible_v, // setStr + bool> = true> + UniValue(Ref&& val) + { + if constexpr (std::is_floating_point_v) { + setFloat(val); + } else if constexpr (std::is_same_v) { + setBool(val); + } else if constexpr (std::is_signed_v) { + setInt(int64_t{val}); + } else if constexpr (std::is_unsigned_v) { + setInt(uint64_t{val}); + } else { + setStr(std::string{std::forward(val)}); + } } - ~UniValue() {} void clear(); - bool setNull(); - bool setBool(bool val); - bool setNumStr(const std::string& val); - bool setInt(uint64_t val); - bool setInt(int64_t val); - bool setInt(int val_) { return setInt((int64_t)val_); } - bool setFloat(double val); - bool setStr(const std::string& val); - bool setArray(); - bool setObject(); + void setNull(); + void setBool(bool val); + void setNumStr(std::string str); + void setInt(uint64_t val); + void setInt(int64_t val); + void setInt(int val_) { return setInt(int64_t{val_}); } + void setFloat(double val); + void setStr(std::string str); + void setArray(); + void setObject(); enum VType getType() const { return typ; } const std::string& getValStr() const { return val; } @@ -68,8 +70,8 @@ class UniValue { size_t size() const { return values.size(); } - bool getBool() const { return isTrue(); } - bool checkObject(const std::map& memberTypes); + void getObjMap(std::map& kv) const; + bool checkObject(const std::map& memberTypes) const; const UniValue& operator[](const std::string& key) const; const UniValue& operator[](size_t index) const; bool exists(const std::string& key) const { size_t i; return findKey(key, i); } @@ -83,64 +85,19 @@ class UniValue { bool isArray() const { return (typ == VARR); } bool isObject() const { return (typ == VOBJ); } - bool push_back(const UniValue& val); - bool push_back(const std::string& val_) { - UniValue tmpVal(VSTR, val_); - return push_back(tmpVal); - } - bool push_back(const char *val_) { - std::string s(val_); - return push_back(s); - } - bool push_back(uint64_t val_) { - UniValue tmpVal(val_); - return push_back(tmpVal); - } - bool push_back(int64_t val_) { - UniValue tmpVal(val_); - return push_back(tmpVal); - } - bool push_back(int val_) { - UniValue tmpVal(val_); - return push_back(tmpVal); - } - bool push_backV(const std::vector& vec); + void push_back(UniValue val); + void push_backV(const std::vector& vec); + template + void push_backV(It first, It last); - bool pushKV(const std::string& key, const UniValue& val); - bool pushKV(const std::string& key, const std::string& val_) { - UniValue tmpVal(VSTR, val_); - return pushKV(key, tmpVal); - } - bool pushKV(const std::string& key, const char *val_) { - std::string _val(val_); - return pushKV(key, _val); - } - bool pushKV(const std::string& key, int64_t val_) { - UniValue tmpVal(val_); - return pushKV(key, tmpVal); - } - bool pushKV(const std::string& key, uint64_t val_) { - UniValue tmpVal(val_); - return pushKV(key, tmpVal); - } - bool pushKV(const std::string& key, int val_) { - UniValue tmpVal((int64_t)val_); - return pushKV(key, tmpVal); - } - bool pushKV(const std::string& key, double val_) { - UniValue tmpVal(val_); - return pushKV(key, tmpVal); - } - bool pushKVs(const UniValue& obj); + void pushKVEnd(std::string key, UniValue val); + void pushKV(std::string key, UniValue val); + void pushKVs(UniValue obj); std::string write(unsigned int prettyIndent = 0, unsigned int indentLevel = 0) const; - bool read(const char *raw, size_t len); - bool read(const char *raw); - bool read(const std::string& rawStr) { - return read(rawStr.data(), rawStr.size()); - } + bool read(std::string_view raw); private: UniValue::VType typ; @@ -148,7 +105,8 @@ class UniValue { std::vector keys; std::vector values; - bool findKey(const std::string& key, size_t& ret) const; + void checkType(const VType& expected) const; + bool findKey(const std::string& key, size_t& retIdx) const; void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; @@ -157,83 +115,36 @@ class UniValue { // value is of unexpected type const std::vector& getKeys() const; const std::vector& getValues() const; + template + Int getInt() const; bool get_bool() const; const std::string& get_str() const; - int get_int() const; - int64_t get_int64() const; double get_real() const; const UniValue& get_obj() const; const UniValue& get_array() const; enum VType type() const { return getType(); } - bool push_back(std::pair pear) { - return pushKV(pear.first, pear.second); - } - friend const UniValue& find_value( const UniValue& obj, const std::string& name); + const UniValue& find_value(std::string_view key) const; }; -// -// The following were added for compatibility with json_spirit. -// Most duplicate other methods, and should be removed. -// -static inline std::pair Pair(const char *cKey, const char *cVal) -{ - std::string key(cKey); - UniValue uVal(cVal); - return std::make_pair(key, uVal); -} - -static inline std::pair Pair(const char *cKey, std::string strVal) -{ - std::string key(cKey); - UniValue uVal(strVal); - return std::make_pair(key, uVal); -} - -static inline std::pair Pair(const char *cKey, uint64_t u64Val) -{ - std::string key(cKey); - UniValue uVal(u64Val); - return std::make_pair(key, uVal); -} - -static inline std::pair Pair(const char *cKey, int64_t i64Val) +template +void UniValue::push_backV(It first, It last) { - std::string key(cKey); - UniValue uVal(i64Val); - return std::make_pair(key, uVal); + checkType(VARR); + values.insert(values.end(), first, last); } -static inline std::pair Pair(const char *cKey, bool iVal) +template +Int UniValue::getInt() const { - std::string key(cKey); - UniValue uVal(iVal); - return std::make_pair(key, uVal); -} - -static inline std::pair Pair(const char *cKey, int iVal) -{ - std::string key(cKey); - UniValue uVal(iVal); - return std::make_pair(key, uVal); -} - -static inline std::pair Pair(const char *cKey, double dVal) -{ - std::string key(cKey); - UniValue uVal(dVal); - return std::make_pair(key, uVal); -} - -static inline std::pair Pair(const char *cKey, const UniValue& uVal) -{ - std::string key(cKey); - return std::make_pair(key, uVal); -} - -static inline std::pair Pair(std::string key, const UniValue& uVal) -{ - return std::make_pair(key, uVal); + static_assert(std::is_integral::value); + checkType(VNUM); + Int result; + const auto [first_nonmatching, error_condition] = std::from_chars(val.data(), val.data() + val.size(), result); + if (first_nonmatching != val.data() + val.size() || error_condition != std::errc{}) { + throw std::runtime_error("JSON integer out of range"); + } + return result; } enum jtokentype { @@ -291,6 +202,4 @@ static inline bool json_isspace(int ch) extern const UniValue NullUniValue; -const UniValue& find_value( const UniValue& obj, const std::string& name); - -#endif // UNIVALUE_H__ +#endif // BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_H diff --git a/src/univalue/include/univalue_escapes.h b/src/univalue/include/univalue_escapes.h new file mode 100644 index 000000000..83767e8ac --- /dev/null +++ b/src/univalue/include/univalue_escapes.h @@ -0,0 +1,261 @@ +#ifndef BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_ESCAPES_H +#define BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_ESCAPES_H +static const char *escapes[256] = { + "\\u0000", + "\\u0001", + "\\u0002", + "\\u0003", + "\\u0004", + "\\u0005", + "\\u0006", + "\\u0007", + "\\b", + "\\t", + "\\n", + "\\u000b", + "\\f", + "\\r", + "\\u000e", + "\\u000f", + "\\u0010", + "\\u0011", + "\\u0012", + "\\u0013", + "\\u0014", + "\\u0015", + "\\u0016", + "\\u0017", + "\\u0018", + "\\u0019", + "\\u001a", + "\\u001b", + "\\u001c", + "\\u001d", + "\\u001e", + "\\u001f", + nullptr, + nullptr, + "\\\"", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + "\\\\", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + "\\u007f", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; +#endif // BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_ESCAPES_H diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/include/univalue_utffilter.h similarity index 89% rename from src/univalue/lib/univalue_utffilter.h rename to src/univalue/include/univalue_utffilter.h index 41da1b0c4..41d8e6bb0 100644 --- a/src/univalue/lib/univalue_utffilter.h +++ b/src/univalue/include/univalue_utffilter.h @@ -1,8 +1,8 @@ // Copyright 2016 Wladimir J. van der Laan // Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . -#ifndef UNIVALUE_UTFFILTER_H -#define UNIVALUE_UTFFILTER_H +// file COPYING or https://opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_UTFFILTER_H +#define BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_UTFFILTER_H #include @@ -13,8 +13,8 @@ class JSONUTF8StringFilter { public: - JSONUTF8StringFilter(std::string &s): - str(s), is_valid(true), codepoint(0), state(0), surpair(0) + explicit JSONUTF8StringFilter(std::string& s) + : str(s) { } // Write single 8-bit char (may be part of UTF-8 sequence) @@ -79,10 +79,10 @@ class JSONUTF8StringFilter } private: std::string &str; - bool is_valid; + bool is_valid{true}; // Current UTF-8 decoding state - unsigned int codepoint; - int state; // Top bit to be filled in for next UTF-8 byte, or 0 + unsigned int codepoint{0}; + int state{0}; // Top bit to be filled in for next UTF-8 byte, or 0 // Keep track of the following state to handle the following section of // RFC4627: @@ -94,7 +94,7 @@ class JSONUTF8StringFilter // "\uD834\uDD1E". // // Two subsequent \u.... may have to be replaced with one actual codepoint. - unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0 + unsigned int surpair{0}; // First half of open UTF-16 surrogate pair, or 0 void append_codepoint(unsigned int codepoint_) { @@ -116,4 +116,4 @@ class JSONUTF8StringFilter } }; -#endif +#endif // BITCOIN_UNIVALUE_INCLUDE_UNIVALUE_UTFFILTER_H diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index aaa027546..656d2e820 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -1,79 +1,17 @@ // Copyright 2014 BitPay Inc. // Copyright 2015 Bitcoin Core Developers // Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . +// file COPYING or https://opensource.org/licenses/mit-license.php. + +#include -#include -#include #include -#include +#include +#include #include -#include -#include -#include - -#include "univalue.h" - -namespace -{ -static bool ParsePrechecks(const std::string& str) -{ - if (str.empty()) // No empty string allowed - return false; - if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed - return false; - if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed - return false; - return true; -} - -bool ParseInt32(const std::string& str, int32_t *out) -{ - if (!ParsePrechecks(str)) - return false; - char *endp = NULL; - errno = 0; // strtol will not set errno if valid - long int n = strtol(str.c_str(), &endp, 10); - if(out) *out = (int32_t)n; - // Note that strtol returns a *long int*, so even if strtol doesn't report an over/underflow - // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit - // platforms the size of these types may be different. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits::min() && - n <= std::numeric_limits::max(); -} - -bool ParseInt64(const std::string& str, int64_t *out) -{ - if (!ParsePrechecks(str)) - return false; - char *endp = NULL; - errno = 0; // strtoll will not set errno if valid - long long int n = strtoll(str.c_str(), &endp, 10); - if(out) *out = (int64_t)n; - // Note that strtoll returns a *long long int*, so even if strtol doesn't report an over/underflow - // we still have to check that the returned value is within the range of an *int64_t*. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits::min() && - n <= std::numeric_limits::max(); -} - -bool ParseDouble(const std::string& str, double *out) -{ - if (!ParsePrechecks(str)) - return false; - if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed - return false; - std::istringstream text(str); - text.imbue(std::locale::classic()); - double result; - text >> result; - if(out) *out = result; - return text.eof() && !text.fail(); -} -} - -using namespace std; +#include +#include +#include const UniValue NullUniValue; @@ -85,138 +23,141 @@ void UniValue::clear() values.clear(); } -bool UniValue::setNull() +void UniValue::setNull() { clear(); - return true; } -bool UniValue::setBool(bool val_) +void UniValue::setBool(bool val_) { clear(); typ = VBOOL; if (val_) val = "1"; - return true; } -static bool validNumStr(const string& s) +static bool validNumStr(const std::string& s) { - string tokenVal; + std::string tokenVal; unsigned int consumed; enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size()); return (tt == JTOK_NUMBER); } -bool UniValue::setNumStr(const string& val_) +void UniValue::setNumStr(std::string str) { - if (!validNumStr(val_)) - return false; + if (!validNumStr(str)) { + throw std::runtime_error{"The string '" + str + "' is not a valid JSON number"}; + } clear(); typ = VNUM; - val = val_; - return true; + val = std::move(str); } -bool UniValue::setInt(uint64_t val_) +void UniValue::setInt(uint64_t val_) { - ostringstream oss; + std::ostringstream oss; oss << val_; return setNumStr(oss.str()); } -bool UniValue::setInt(int64_t val_) +void UniValue::setInt(int64_t val_) { - ostringstream oss; + std::ostringstream oss; oss << val_; return setNumStr(oss.str()); } -bool UniValue::setFloat(double val_) +void UniValue::setFloat(double val_) { - ostringstream oss; + std::ostringstream oss; oss << std::setprecision(16) << val_; - bool ret = setNumStr(oss.str()); - typ = VNUM; - return ret; + return setNumStr(oss.str()); } -bool UniValue::setStr(const string& val_) +void UniValue::setStr(std::string str) { clear(); typ = VSTR; - val = val_; - return true; + val = std::move(str); } -bool UniValue::setArray() +void UniValue::setArray() { clear(); typ = VARR; - return true; } -bool UniValue::setObject() +void UniValue::setObject() { clear(); typ = VOBJ; - return true; } -bool UniValue::push_back(const UniValue& val_) +void UniValue::push_back(UniValue val) { - if (typ != VARR) - return false; + checkType(VARR); - values.push_back(val_); - return true; + values.push_back(std::move(val)); } -bool UniValue::push_backV(const std::vector& vec) +void UniValue::push_backV(const std::vector& vec) { - if (typ != VARR) - return false; + checkType(VARR); values.insert(values.end(), vec.begin(), vec.end()); +} - return true; +void UniValue::pushKVEnd(std::string key, UniValue val) +{ + checkType(VOBJ); + + keys.push_back(std::move(key)); + values.push_back(std::move(val)); } -bool UniValue::pushKV(const std::string& key, const UniValue& val_) +void UniValue::pushKV(std::string key, UniValue val) { - if (typ != VOBJ) - return false; + checkType(VOBJ); - keys.push_back(key); - values.push_back(val_); - return true; + size_t idx; + if (findKey(key, idx)) + values[idx] = std::move(val); + else + pushKVEnd(std::move(key), std::move(val)); } -bool UniValue::pushKVs(const UniValue& obj) +void UniValue::pushKVs(UniValue obj) { - if (typ != VOBJ || obj.typ != VOBJ) - return false; + checkType(VOBJ); + obj.checkType(VOBJ); - for (unsigned int i = 0; i < obj.keys.size(); i++) { - keys.push_back(obj.keys[i]); - values.push_back(obj.values.at(i)); - } + for (size_t i = 0; i < obj.keys.size(); i++) + pushKVEnd(std::move(obj.keys.at(i)), std::move(obj.values.at(i))); +} - return true; +void UniValue::getObjMap(std::map& kv) const +{ + if (typ != VOBJ) + return; + + kv.clear(); + for (size_t i = 0; i < keys.size(); i++) + kv[keys[i]] = values[i]; } -bool UniValue::findKey(const std::string& key, size_t& ret) const +bool UniValue::findKey(const std::string& key, size_t& retIdx) const { for (size_t i = 0; i < keys.size(); i++) { if (keys[i] == key) { - ret = i; + retIdx = i; return true; } } @@ -224,16 +165,21 @@ bool UniValue::findKey(const std::string& key, size_t& ret) const return false; } -bool UniValue::checkObject(const std::map& t) +bool UniValue::checkObject(const std::map& t) const { - for (std::map::const_iterator it = t.begin(); - it != t.end(); ++it) { - size_t idx; - if (!findKey(it->first, idx)) + if (typ != VOBJ) { + return false; + } + + for (const auto& object: t) { + size_t idx = 0; + if (!findKey(object.first, idx)) { return false; + } - if (values.at(idx).getType() != it->second) + if (values.at(idx).getType() != object.second) { return false; + } } return true; @@ -244,7 +190,7 @@ const UniValue& UniValue::operator[](const std::string& key) const if (typ != VOBJ) return NullUniValue; - size_t index; + size_t index = 0; if (!findKey(key, index)) return NullUniValue; @@ -261,6 +207,14 @@ const UniValue& UniValue::operator[](size_t index) const return values.at(index); } +void UniValue::checkType(const VType& expected) const +{ + if (typ != expected) { + throw type_error{"JSON value of type " + std::string{uvTypeName(typ)} + " is not of expected type " + + std::string{uvTypeName(expected)}}; + } +} + const char *uvTypeName(UniValue::VType t) { switch (t) { @@ -273,87 +227,16 @@ const char *uvTypeName(UniValue::VType t) } // not reached - return NULL; + return nullptr; } -const UniValue& find_value(const UniValue& obj, const std::string& name) +const UniValue& UniValue::find_value(std::string_view key) const { - for (unsigned int i = 0; i < obj.keys.size(); i++) - if (obj.keys[i] == name) - return obj.values.at(i); - + for (unsigned int i = 0; i < keys.size(); ++i) { + if (keys[i] == key) { + return values.at(i); + } + } return NullUniValue; } -const std::vector& UniValue::getKeys() const -{ - if (typ != VOBJ) - throw std::runtime_error("JSON value is not an object as expected"); - return keys; -} - -const std::vector& UniValue::getValues() const -{ - if (typ != VOBJ && typ != VARR) - throw std::runtime_error("JSON value is not an object or array as expected"); - return values; -} - -bool UniValue::get_bool() const -{ - if (typ != VBOOL) - throw std::runtime_error("JSON value is not a boolean as expected"); - return getBool(); -} - -const std::string& UniValue::get_str() const -{ - if (typ != VSTR) - throw std::runtime_error("JSON value is not a string as expected"); - return getValStr(); -} - -int UniValue::get_int() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not an integer as expected"); - int32_t retval; - if (!ParseInt32(getValStr(), &retval)) - throw std::runtime_error("JSON integer out of range"); - return retval; -} - -int64_t UniValue::get_int64() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not an integer as expected"); - int64_t retval; - if (!ParseInt64(getValStr(), &retval)) - throw std::runtime_error("JSON integer out of range"); - return retval; -} - -double UniValue::get_real() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not a number as expected"); - double retval; - if (!ParseDouble(getValStr(), &retval)) - throw std::runtime_error("JSON double out of range"); - return retval; -} - -const UniValue& UniValue::get_obj() const -{ - if (typ != VOBJ) - throw std::runtime_error("JSON value is not an object as expected"); - return *this; -} - -const UniValue& UniValue::get_array() const -{ - if (typ != VARR) - throw std::runtime_error("JSON value is not an array as expected"); - return *this; -} - diff --git a/src/univalue/lib/univalue_escapes.h b/src/univalue/lib/univalue_escapes.h deleted file mode 100644 index 74596aab6..000000000 --- a/src/univalue/lib/univalue_escapes.h +++ /dev/null @@ -1,262 +0,0 @@ -// Automatically generated file. Do not modify. -#ifndef BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H -#define BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H -static const char *escapes[256] = { - "\\u0000", - "\\u0001", - "\\u0002", - "\\u0003", - "\\u0004", - "\\u0005", - "\\u0006", - "\\u0007", - "\\b", - "\\t", - "\\n", - "\\u000b", - "\\f", - "\\r", - "\\u000e", - "\\u000f", - "\\u0010", - "\\u0011", - "\\u0012", - "\\u0013", - "\\u0014", - "\\u0015", - "\\u0016", - "\\u0017", - "\\u0018", - "\\u0019", - "\\u001a", - "\\u001b", - "\\u001c", - "\\u001d", - "\\u001e", - "\\u001f", - NULL, - NULL, - "\\\"", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - "\\\\", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - "\\u007f", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, -}; -#endif // BITCOIN_UNIVALUE_UNIVALUE_ESCAPES_H diff --git a/src/univalue/lib/univalue_get.cpp b/src/univalue/lib/univalue_get.cpp new file mode 100644 index 000000000..037449ca0 --- /dev/null +++ b/src/univalue/lib/univalue_get.cpp @@ -0,0 +1,91 @@ +// Copyright 2014 BitPay Inc. +// Copyright 2015 Bitcoin Core Developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +static bool ParsePrechecks(const std::string& str) +{ + if (str.empty()) // No empty string allowed + return false; + if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed + return false; + if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed + return false; + return true; +} + +bool ParseDouble(const std::string& str, double *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed + return false; + std::istringstream text(str); + text.imbue(std::locale::classic()); + double result; + text >> result; + if(out) *out = result; + return text.eof() && !text.fail(); +} +} + +const std::vector& UniValue::getKeys() const +{ + checkType(VOBJ); + return keys; +} + +const std::vector& UniValue::getValues() const +{ + if (typ != VOBJ && typ != VARR) + throw std::runtime_error("JSON value is not an object or array as expected"); + return values; +} + +bool UniValue::get_bool() const +{ + checkType(VBOOL); + return isTrue(); +} + +const std::string& UniValue::get_str() const +{ + checkType(VSTR); + return getValStr(); +} + +double UniValue::get_real() const +{ + checkType(VNUM); + double retval; + if (!ParseDouble(getValStr(), &retval)) + throw std::runtime_error("JSON double out of range"); + return retval; +} + +const UniValue& UniValue::get_obj() const +{ + checkType(VOBJ); + return *this; +} + +const UniValue& UniValue::get_array() const +{ + checkType(VARR); + return *this; +} diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp index 35a9c726d..40d465f43 100644 --- a/src/univalue/lib/univalue_read.cpp +++ b/src/univalue/lib/univalue_read.cpp @@ -1,14 +1,24 @@ // Copyright 2014 BitPay Inc. // Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . +// file COPYING or https://opensource.org/licenses/mit-license.php. -#include +#include +#include + +#include +#include +#include +#include +#include #include -#include -#include "univalue.h" -#include "univalue_utffilter.h" -using namespace std; +/* + * According to stackexchange, the original json test suite wanted + * to limit depth to 22. Widely-deployed PHP bails at depth 512, + * so we will follow PHP's lead, which should be more than sufficient + * (further stackexchange comments indicate depth > 32 rarely occurs). + */ +static constexpr size_t MAX_JSON_DEPTH = 512; static bool json_isdigit(int ch) { @@ -42,7 +52,7 @@ static const char *hatoui(const char *first, const char *last, return first; } -enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, +enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed, const char *raw, const char *end) { tokenVal.clear(); @@ -114,7 +124,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, case '8': case '9': { // part 1: int - string numStr; + std::string numStr; const char *first = raw; @@ -174,11 +184,11 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, case '"': { raw++; // skip " - string valStr; + std::string valStr; JSONUTF8StringFilter writer(valStr); - while (raw < end) { - if ((unsigned char)*raw < 0x20) + while (true) { + if (raw >= end || (unsigned char)*raw < 0x20) return JTOK_ERR; else if (*raw == '\\') { @@ -221,7 +231,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, } else { - writer.push_back(*raw); + writer.push_back(static_cast(*raw)); raw++; } } @@ -238,7 +248,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, } } -enum expect_bits { +enum expect_bits : unsigned { EXP_OBJ_NAME = (1U << 0), EXP_COLON = (1U << 1), EXP_ARR_VALUE = (1U << 2), @@ -250,18 +260,19 @@ enum expect_bits { #define setExpect(bit) (expectMask |= EXP_##bit) #define clearExpect(bit) (expectMask &= ~EXP_##bit) -bool UniValue::read(const char *raw, size_t size) +bool UniValue::read(std::string_view str_in) { clear(); uint32_t expectMask = 0; - vector stack; + std::vector stack; - string tokenVal; + std::string tokenVal; unsigned int consumed; enum jtokentype tok = JTOK_NONE; enum jtokentype last_tok = JTOK_NONE; - const char* end = raw + size; + const char* raw{str_in.data()}; + const char* end{raw + str_in.size()}; do { last_tok = tok; @@ -325,6 +336,9 @@ bool UniValue::read(const char *raw, size_t size) stack.push_back(newTop); } + if (stack.size() > MAX_JSON_DEPTH) + return false; + if (utyp == VOBJ) setExpect(OBJ_NAME); else @@ -449,6 +463,3 @@ bool UniValue::read(const char *raw, size_t size) return true; } -bool UniValue::read(const char *raw) { - return read(raw, strlen(raw)); -} diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp index 0337e690d..4a2219061 100644 --- a/src/univalue/lib/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -1,37 +1,37 @@ // Copyright 2014 BitPay Inc. // Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . +// file COPYING or https://opensource.org/licenses/mit-license.php. -#include -#include -#include -#include "univalue.h" -#include "univalue_escapes.h" +#include +#include -using namespace std; +#include +#include +#include -static string json_escape(const string& inS) +static std::string json_escape(const std::string& inS) { - string outS; + std::string outS; outS.reserve(inS.size() * 2); for (unsigned int i = 0; i < inS.size(); i++) { - unsigned char ch = inS[i]; + unsigned char ch = static_cast(inS[i]); const char *escStr = escapes[ch]; if (escStr) outS += escStr; else - outS += ch; + outS += static_cast(ch); } return outS; } -string UniValue::write(unsigned int prettyIndent, - unsigned int indentLevel) const +// NOLINTNEXTLINE(misc-no-recursion) +std::string UniValue::write(unsigned int prettyIndent, + unsigned int indentLevel) const { - string s; + std::string s; s.reserve(1024); unsigned int modIndent = indentLevel; @@ -62,12 +62,13 @@ string UniValue::write(unsigned int prettyIndent, return s; } -static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, string& s) +static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) { s.append(prettyIndent * indentLevel, ' '); } -void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const +// NOLINTNEXTLINE(misc-no-recursion) +void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const { s += "["; if (prettyIndent) @@ -89,7 +90,8 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s s += "]"; } -void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s) const +// NOLINTNEXTLINE(misc-no-recursion) +void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const { s += "{"; if (prettyIndent) diff --git a/src/univalue/pc/libunivalue-uninstalled.pc.in b/src/univalue/pc/libunivalue-uninstalled.pc.in deleted file mode 100644 index b7f53e875..000000000 --- a/src/univalue/pc/libunivalue-uninstalled.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libunivalue -Description: libunivalue, C++ universal value object and JSON library -Version: @VERSION@ -Libs: ${pc_top_builddir}/${pcfiledir}/libunivalue.la diff --git a/src/univalue/pc/libunivalue.pc.in b/src/univalue/pc/libunivalue.pc.in deleted file mode 100644 index 358a2d5f7..000000000 --- a/src/univalue/pc/libunivalue.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libunivalue -Description: libunivalue, C++ universal value object and JSON library -Version: @VERSION@ -Libs: -L${libdir} -lunivalue -Cflags: -I${includedir} diff --git a/src/univalue/sources.mk b/src/univalue/sources.mk new file mode 100644 index 000000000..5e4d9c383 --- /dev/null +++ b/src/univalue/sources.mk @@ -0,0 +1,86 @@ +# - All Variables ending in _HEADERS or _SOURCES confuse automake, so the +# _INT postfix is applied. +# - Convenience variables, for example a UNIVALUE_TEST_DIR should not be used +# as they interfere with automatic dependency generation +# - The %reldir% is the relative path from the Makefile.am. + +UNIVALUE_INCLUDE_DIR_INT = %reldir%/include + +UNIVALUE_DIST_HEADERS_INT = +UNIVALUE_DIST_HEADERS_INT += %reldir%/include/univalue.h +UNIVALUE_DIST_HEADERS_INT += %reldir%/include/univalue_utffilter.h +UNIVALUE_DIST_HEADERS_INT += %reldir%/include/univalue_escapes.h + +UNIVALUE_LIB_SOURCES_INT = +UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue.cpp +UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_get.cpp +UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_read.cpp +UNIVALUE_LIB_SOURCES_INT += %reldir%/lib/univalue_write.cpp + +UNIVALUE_TEST_DATA_DIR_INT = %reldir%/test + +UNIVALUE_TEST_UNITESTER_INT = +UNIVALUE_TEST_UNITESTER_INT += %reldir%/test/unitester.cpp + +UNIVALUE_TEST_JSON_INT = +UNIVALUE_TEST_JSON_INT += %reldir%/test/test_json.cpp + +UNIVALUE_TEST_OBJECT_INT = +UNIVALUE_TEST_OBJECT_INT += %reldir%/test/object.cpp + +UNIVALUE_TEST_FILES_INT = +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail1.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail2.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail3.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail4.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail5.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail6.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail7.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail8.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail9.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail10.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail11.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail12.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail13.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail14.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail15.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail16.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail17.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail18.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail19.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail20.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail21.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail22.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail23.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail24.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail25.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail26.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail27.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail28.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail29.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail30.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail31.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail32.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail33.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail34.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail35.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail36.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail37.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail38.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail39.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail40.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail41.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail42.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail44.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/fail45.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/pass1.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/pass2.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/pass3.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/pass4.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/round1.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/round2.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/round3.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/round4.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/round5.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/round6.json +UNIVALUE_TEST_FILES_INT += %reldir%/test/round7.json diff --git a/src/univalue/test/.gitignore b/src/univalue/test/.gitignore index 9d0ed5701..5812c96b1 100644 --- a/src/univalue/test/.gitignore +++ b/src/univalue/test/.gitignore @@ -1,6 +1,7 @@ + +object unitester test_json -no_nul *.trs *.log diff --git a/src/univalue/test/fail44.json b/src/univalue/test/fail44.json new file mode 100644 index 000000000..80edceddf --- /dev/null +++ b/src/univalue/test/fail44.json @@ -0,0 +1 @@ +"This file ends without a newline or close-quote. \ No newline at end of file diff --git a/src/univalue/test/fail45.json b/src/univalue/test/fail45.json new file mode 100644 index 000000000..03a30d880 --- /dev/null +++ b/src/univalue/test/fail45.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] diff --git a/src/univalue/test/no_nul.cpp b/src/univalue/test/no_nul.cpp deleted file mode 100644 index 83d292200..000000000 --- a/src/univalue/test/no_nul.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "univalue.h" - -int main (int argc, char *argv[]) -{ - char buf[] = "___[1,2,3]___"; - UniValue val; - return val.read(buf + 3, 7) ? 0 : 1; -} diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp new file mode 100644 index 000000000..2577c682d --- /dev/null +++ b/src/univalue/test/object.cpp @@ -0,0 +1,468 @@ +// Copyright (c) 2014 BitPay Inc. +// Copyright (c) 2014-2022 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BOOST_CHECK(expr) assert(expr) +#define BOOST_CHECK_EQUAL(v1, v2) assert((v1) == (v2)) +#define BOOST_CHECK_THROW(stmt, excMatch) { \ + try { \ + (stmt); \ + assert(0 && "No exception caught"); \ + } catch (excMatch&) { \ + } catch (...) { \ + assert(0 && "Wrong exception caught"); \ + } \ + } +#define BOOST_CHECK_NO_THROW(stmt) { \ + try { \ + (stmt); \ + } catch (...) { \ + assert(0); \ + } \ + } + +void univalue_constructor() +{ + UniValue v1; + BOOST_CHECK(v1.isNull()); + + UniValue v2(UniValue::VSTR); + BOOST_CHECK(v2.isStr()); + + UniValue v3(UniValue::VSTR, "foo"); + BOOST_CHECK(v3.isStr()); + BOOST_CHECK_EQUAL(v3.getValStr(), "foo"); + + UniValue numTest; + numTest.setNumStr("82"); + BOOST_CHECK(numTest.isNum()); + BOOST_CHECK_EQUAL(numTest.getValStr(), "82"); + + uint64_t vu64 = 82; + UniValue v4(vu64); + BOOST_CHECK(v4.isNum()); + BOOST_CHECK_EQUAL(v4.getValStr(), "82"); + + int64_t vi64 = -82; + UniValue v5(vi64); + BOOST_CHECK(v5.isNum()); + BOOST_CHECK_EQUAL(v5.getValStr(), "-82"); + + int vi = -688; + UniValue v6(vi); + BOOST_CHECK(v6.isNum()); + BOOST_CHECK_EQUAL(v6.getValStr(), "-688"); + + double vd = -7.21; + UniValue v7(vd); + BOOST_CHECK(v7.isNum()); + BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); + + std::string vs("yawn"); + UniValue v8(vs); + BOOST_CHECK(v8.isStr()); + BOOST_CHECK_EQUAL(v8.getValStr(), "yawn"); + + const char *vcs = "zappa"; + UniValue v9(vcs); + BOOST_CHECK(v9.isStr()); + BOOST_CHECK_EQUAL(v9.getValStr(), "zappa"); +} + +void univalue_push_throw() +{ + UniValue j; + BOOST_CHECK_THROW(j.push_back(1), std::runtime_error); + BOOST_CHECK_THROW(j.push_backV({1}), std::runtime_error); + BOOST_CHECK_THROW(j.pushKVEnd("k", 1), std::runtime_error); + BOOST_CHECK_THROW(j.pushKV("k", 1), std::runtime_error); + BOOST_CHECK_THROW(j.pushKVs({}), std::runtime_error); +} + +void univalue_typecheck() +{ + UniValue v1; + v1.setNumStr("1"); + BOOST_CHECK(v1.isNum()); + BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error); + + { + UniValue v_negative; + v_negative.setNumStr("-1"); + BOOST_CHECK_THROW(v_negative.getInt(), std::runtime_error); + BOOST_CHECK_EQUAL(v_negative.getInt(), -1); + } + + UniValue v2; + v2.setBool(true); + BOOST_CHECK_EQUAL(v2.get_bool(), true); + BOOST_CHECK_THROW(v2.getInt(), std::runtime_error); + + UniValue v3; + v3.setNumStr("32482348723847471234"); + BOOST_CHECK_THROW(v3.getInt(), std::runtime_error); + v3.setNumStr("1000"); + BOOST_CHECK_EQUAL(v3.getInt(), 1000); + + UniValue v4; + v4.setNumStr("2147483648"); + BOOST_CHECK_EQUAL(v4.getInt(), 2147483648); + BOOST_CHECK_THROW(v4.getInt(), std::runtime_error); + v4.setNumStr("1000"); + BOOST_CHECK_EQUAL(v4.getInt(), 1000); + BOOST_CHECK_THROW(v4.get_str(), std::runtime_error); + BOOST_CHECK_EQUAL(v4.get_real(), 1000); + BOOST_CHECK_THROW(v4.get_array(), std::runtime_error); + BOOST_CHECK_THROW(v4.getKeys(), std::runtime_error); + BOOST_CHECK_THROW(v4.getValues(), std::runtime_error); + BOOST_CHECK_THROW(v4.get_obj(), std::runtime_error); + + UniValue v5; + BOOST_CHECK(v5.read("[true, 10]")); + BOOST_CHECK_NO_THROW(v5.get_array()); + std::vector vals = v5.getValues(); + BOOST_CHECK_THROW(vals[0].getInt(), std::runtime_error); + BOOST_CHECK_EQUAL(vals[0].get_bool(), true); + + BOOST_CHECK_EQUAL(vals[1].getInt(), 10); + BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error); +} + +void univalue_set() +{ + UniValue v(UniValue::VSTR, "foo"); + v.clear(); + BOOST_CHECK(v.isNull()); + BOOST_CHECK_EQUAL(v.getValStr(), ""); + + v.setObject(); + BOOST_CHECK(v.isObject()); + BOOST_CHECK_EQUAL(v.size(), 0); + BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ); + BOOST_CHECK(v.empty()); + + v.setArray(); + BOOST_CHECK(v.isArray()); + BOOST_CHECK_EQUAL(v.size(), 0); + + v.setStr("zum"); + BOOST_CHECK(v.isStr()); + BOOST_CHECK_EQUAL(v.getValStr(), "zum"); + + { + std::string_view sv{"ab\0c", 4}; + UniValue j{sv}; + BOOST_CHECK(j.isStr()); + BOOST_CHECK_EQUAL(j.getValStr(), sv); + BOOST_CHECK_EQUAL(j.write(), "\"ab\\u0000c\""); + } + + v.setFloat(-1.01); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); + + v.setInt(int{1023}); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "1023"); + + v.setInt(int64_t{-1023LL}); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-1023"); + + v.setInt(uint64_t{1023ULL}); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "1023"); + + v.setNumStr("-688"); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-688"); + + v.setBool(false); + BOOST_CHECK_EQUAL(v.isBool(), true); + BOOST_CHECK_EQUAL(v.isTrue(), false); + BOOST_CHECK_EQUAL(v.isFalse(), true); + BOOST_CHECK_EQUAL(v.get_bool(), false); + + v.setBool(true); + BOOST_CHECK_EQUAL(v.isBool(), true); + BOOST_CHECK_EQUAL(v.isTrue(), true); + BOOST_CHECK_EQUAL(v.isFalse(), false); + BOOST_CHECK_EQUAL(v.get_bool(), true); + + BOOST_CHECK_THROW(v.setNumStr("zombocom"), std::runtime_error); + + v.setNull(); + BOOST_CHECK(v.isNull()); +} + +void univalue_array() +{ + UniValue arr(UniValue::VARR); + + UniValue v((int64_t)1023LL); + arr.push_back(v); + + std::string vStr("zippy"); + arr.push_back(vStr); + + const char *s = "pippy"; + arr.push_back(s); + + std::vector vec; + v.setStr("boing"); + vec.push_back(v); + + v.setStr("going"); + vec.push_back(v); + + arr.push_backV(vec); + + arr.push_back(uint64_t{400ULL}); + arr.push_back(int64_t{-400LL}); + arr.push_back(int{-401}); + arr.push_back(-40.1); + arr.push_back(true); + + BOOST_CHECK_EQUAL(arr.empty(), false); + BOOST_CHECK_EQUAL(arr.size(), 10); + + BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023"); + BOOST_CHECK_EQUAL(arr[0].getType(), UniValue::VNUM); + BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy"); + BOOST_CHECK_EQUAL(arr[1].getType(), UniValue::VSTR); + BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy"); + BOOST_CHECK_EQUAL(arr[2].getType(), UniValue::VSTR); + BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing"); + BOOST_CHECK_EQUAL(arr[3].getType(), UniValue::VSTR); + BOOST_CHECK_EQUAL(arr[4].getValStr(), "going"); + BOOST_CHECK_EQUAL(arr[4].getType(), UniValue::VSTR); + BOOST_CHECK_EQUAL(arr[5].getValStr(), "400"); + BOOST_CHECK_EQUAL(arr[5].getType(), UniValue::VNUM); + BOOST_CHECK_EQUAL(arr[6].getValStr(), "-400"); + BOOST_CHECK_EQUAL(arr[6].getType(), UniValue::VNUM); + BOOST_CHECK_EQUAL(arr[7].getValStr(), "-401"); + BOOST_CHECK_EQUAL(arr[7].getType(), UniValue::VNUM); + BOOST_CHECK_EQUAL(arr[8].getValStr(), "-40.1"); + BOOST_CHECK_EQUAL(arr[8].getType(), UniValue::VNUM); + BOOST_CHECK_EQUAL(arr[9].getValStr(), "1"); + BOOST_CHECK_EQUAL(arr[9].getType(), UniValue::VBOOL); + + BOOST_CHECK_EQUAL(arr[999].getValStr(), ""); + + arr.clear(); + BOOST_CHECK(arr.empty()); + BOOST_CHECK_EQUAL(arr.size(), 0); +} + +void univalue_object() +{ + UniValue obj(UniValue::VOBJ); + std::string strKey, strVal; + UniValue v; + + strKey = "age"; + v.setInt(100); + obj.pushKV(strKey, v); + + strKey = "first"; + strVal = "John"; + obj.pushKV(strKey, strVal); + + strKey = "last"; + const char* cVal = "Smith"; + obj.pushKV(strKey, cVal); + + strKey = "distance"; + obj.pushKV(strKey, int64_t{25}); + + strKey = "time"; + obj.pushKV(strKey, uint64_t{3600}); + + strKey = "calories"; + obj.pushKV(strKey, int{12}); + + strKey = "temperature"; + obj.pushKV(strKey, double{90.012}); + + strKey = "moon"; + obj.pushKV(strKey, true); + + strKey = "spoon"; + obj.pushKV(strKey, false); + + UniValue obj2(UniValue::VOBJ); + obj2.pushKV("cat1", 9000); + obj2.pushKV("cat2", 12345); + + obj.pushKVs(obj2); + + BOOST_CHECK_EQUAL(obj.empty(), false); + BOOST_CHECK_EQUAL(obj.size(), 11); + + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100"); + BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John"); + BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith"); + BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25"); + BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600"); + BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12"); + BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012"); + BOOST_CHECK_EQUAL(obj["moon"].getValStr(), "1"); + BOOST_CHECK_EQUAL(obj["spoon"].getValStr(), ""); + BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000"); + BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345"); + + BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), ""); + + BOOST_CHECK(obj.exists("age")); + BOOST_CHECK(obj.exists("first")); + BOOST_CHECK(obj.exists("last")); + BOOST_CHECK(obj.exists("distance")); + BOOST_CHECK(obj.exists("time")); + BOOST_CHECK(obj.exists("calories")); + BOOST_CHECK(obj.exists("temperature")); + BOOST_CHECK(obj.exists("moon")); + BOOST_CHECK(obj.exists("spoon")); + BOOST_CHECK(obj.exists("cat1")); + BOOST_CHECK(obj.exists("cat2")); + + BOOST_CHECK(!obj.exists("nyuknyuknyuk")); + + std::map objTypes; + objTypes["age"] = UniValue::VNUM; + objTypes["first"] = UniValue::VSTR; + objTypes["last"] = UniValue::VSTR; + objTypes["distance"] = UniValue::VNUM; + objTypes["time"] = UniValue::VNUM; + objTypes["calories"] = UniValue::VNUM; + objTypes["temperature"] = UniValue::VNUM; + objTypes["moon"] = UniValue::VBOOL; + objTypes["spoon"] = UniValue::VBOOL; + objTypes["cat1"] = UniValue::VNUM; + objTypes["cat2"] = UniValue::VNUM; + BOOST_CHECK(obj.checkObject(objTypes)); + + objTypes["cat2"] = UniValue::VSTR; + BOOST_CHECK(!obj.checkObject(objTypes)); + + obj.clear(); + BOOST_CHECK(obj.empty()); + BOOST_CHECK_EQUAL(obj.size(), 0); + BOOST_CHECK_EQUAL(obj.getType(), UniValue::VNULL); + + obj.setObject(); + UniValue uv; + uv.setInt(42); + obj.pushKVEnd("age", uv); + BOOST_CHECK_EQUAL(obj.size(), 1); + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "42"); + + uv.setInt(43); + obj.pushKV("age", uv); + BOOST_CHECK_EQUAL(obj.size(), 1); + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "43"); + + obj.pushKV("name", "foo bar"); + + std::map kv; + obj.getObjMap(kv); + BOOST_CHECK_EQUAL(kv["age"].getValStr(), "43"); + BOOST_CHECK_EQUAL(kv["name"].getValStr(), "foo bar"); + +} + +static const char *json1 = +"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]"; + +void univalue_readwrite() +{ + UniValue v; + BOOST_CHECK(v.read(json1)); + + std::string strJson1(json1); + BOOST_CHECK(v.read(strJson1)); + + BOOST_CHECK(v.isArray()); + BOOST_CHECK_EQUAL(v.size(), 2); + + BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000"); + + UniValue obj = v[1]; + BOOST_CHECK(obj.isObject()); + BOOST_CHECK_EQUAL(obj.size(), 3); + + BOOST_CHECK(obj["key1"].isStr()); + std::string correctValue("str"); + correctValue.push_back('\0'); + BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue); + BOOST_CHECK(obj["key2"].isNum()); + BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800"); + BOOST_CHECK(obj["key3"].isObject()); + + BOOST_CHECK_EQUAL(strJson1, v.write()); + + // Valid + BOOST_CHECK(v.read("1.0") && (v.get_real() == 1.0)); + BOOST_CHECK(v.read("true") && v.get_bool()); + BOOST_CHECK(v.read("[false]") && !v[0].get_bool()); + BOOST_CHECK(v.read("{\"a\": true}") && v["a"].get_bool()); + BOOST_CHECK(v.read("{\"1\": \"true\"}") && (v["1"].get_str() == "true")); + // Valid, with leading or trailing whitespace + BOOST_CHECK(v.read(" 1.0") && (v.get_real() == 1.0)); + BOOST_CHECK(v.read("1.0 ") && (v.get_real() == 1.0)); + BOOST_CHECK(v.read("0.00000000000000000000000000000000000001e+30 ")); + + BOOST_CHECK(!v.read(".19e-6")); //should fail, missing leading 0, therefore invalid JSON + // Invalid, initial garbage + BOOST_CHECK(!v.read("[1.0")); + BOOST_CHECK(!v.read("a1.0")); + // Invalid, trailing garbage + BOOST_CHECK(!v.read("1.0sds")); + BOOST_CHECK(!v.read("1.0]")); + // Invalid, keys have to be names + BOOST_CHECK(!v.read("{1: \"true\"}")); + BOOST_CHECK(!v.read("{true: 1}")); + BOOST_CHECK(!v.read("{[1]: 1}")); + BOOST_CHECK(!v.read("{{\"a\": \"a\"}: 1}")); + // BTC addresses should fail parsing + BOOST_CHECK(!v.read("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); + BOOST_CHECK(!v.read("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNL")); + + /* Check for (correctly reporting) a parsing error if the initial + JSON construct is followed by more stuff. Note that whitespace + is, of course, exempt. */ + + BOOST_CHECK(v.read(" {}\n ")); + BOOST_CHECK(v.isObject()); + BOOST_CHECK(v.read(" []\n ")); + BOOST_CHECK(v.isArray()); + + BOOST_CHECK(!v.read("@{}")); + BOOST_CHECK(!v.read("{} garbage")); + BOOST_CHECK(!v.read("[]{}")); + BOOST_CHECK(!v.read("{}[]")); + BOOST_CHECK(!v.read("{} 42")); +} + +int main(int argc, char* argv[]) +{ + univalue_constructor(); + univalue_push_throw(); + univalue_typecheck(); + univalue_set(); + univalue_array(); + univalue_object(); + univalue_readwrite(); + return 0; +} diff --git a/src/univalue/test/pass4.json b/src/univalue/test/pass4.json new file mode 100644 index 000000000..f5a680b31 --- /dev/null +++ b/src/univalue/test/pass4.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] diff --git a/src/univalue/test/test_json.cpp b/src/univalue/test/test_json.cpp index 2943bae2b..f8c10238d 100644 --- a/src/univalue/test/test_json.cpp +++ b/src/univalue/test/test_json.cpp @@ -4,9 +4,11 @@ // It reads JSON input from stdin and exits with code 0 if it can be parsed // successfully. It also pretty prints the parsed JSON value to stdout. +#include + #include +#include #include -#include "univalue.h" using namespace std; diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp index 7492e63e8..d27e647e5 100644 --- a/src/univalue/test/unitester.cpp +++ b/src/univalue/test/unitester.cpp @@ -1,28 +1,18 @@ // Copyright 2014 BitPay Inc. -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or https://opensource.org/licenses/mit-license.php. + +#include -#include -#include -#include #include +#include #include -#include "univalue.h" #ifndef JSON_TEST_SRC #error JSON_TEST_SRC must point to test source directory #endif -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#endif - -using namespace std; -string srcdir(JSON_TEST_SRC); -static bool test_failed = false; - -#define d_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", filename.c_str()); } } -#define f_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", __func__); } } +std::string srcdir(JSON_TEST_SRC); static std::string rtrim(std::string s) { @@ -30,9 +20,9 @@ static std::string rtrim(std::string s) return s; } -static void runtest(string filename, const string& jdata) +static void runtest(std::string filename, const std::string& jdata) { - string prefix = filename.substr(0, 4); + std::string prefix = filename.substr(0, 4); bool wantPass = (prefix == "pass") || (prefix == "roun"); bool wantFail = (prefix == "fail"); @@ -43,9 +33,9 @@ static void runtest(string filename, const string& jdata) bool testResult = val.read(jdata); if (wantPass) { - d_assert(testResult == true); + assert(testResult == true); } else { - d_assert(testResult == false); + assert(testResult == false); } if (wantRoundTrip) { @@ -56,19 +46,19 @@ static void runtest(string filename, const string& jdata) static void runtest_file(const char *filename_) { - string basename(filename_); - string filename = srcdir + "/" + basename; + std::string basename(filename_); + std::string filename = srcdir + "/" + basename; FILE *f = fopen(filename.c_str(), "r"); - assert(f != NULL); + assert(f != nullptr); - string jdata; + std::string jdata; char buf[4096]; while (!feof(f)) { int bread = fread(buf, 1, sizeof(buf), f); assert(!ferror(f)); - string s(buf, bread); + std::string s(buf, bread); jdata += s; } @@ -114,6 +104,8 @@ static const char *filenames[] = { "fail40.json", // invalid unicode: broken UTF-8 "fail41.json", // invalid unicode: unfinished UTF-8 "fail42.json", // valid json with garbage following a nul byte + "fail44.json", // unterminated string + "fail45.json", // nested beyond max depth "fail3.json", "fail4.json", // extra comma "fail5.json", @@ -124,6 +116,7 @@ static const char *filenames[] = { "pass1.json", "pass2.json", "pass3.json", + "pass4.json", "round1.json", // round-trip test "round2.json", // unicode "round3.json", // bare string @@ -140,30 +133,38 @@ void unescape_unicode_test() bool testResult; // Escaped ASCII (quote) testResult = val.read("[\"\\u0022\"]"); - f_assert(testResult); - f_assert(val[0].get_str() == "\""); + assert(testResult); + assert(val[0].get_str() == "\""); // Escaped Basic Plane character, two-byte UTF-8 testResult = val.read("[\"\\u0191\"]"); - f_assert(testResult); - f_assert(val[0].get_str() == "\xc6\x91"); + assert(testResult); + assert(val[0].get_str() == "\xc6\x91"); // Escaped Basic Plane character, three-byte UTF-8 testResult = val.read("[\"\\u2191\"]"); - f_assert(testResult); - f_assert(val[0].get_str() == "\xe2\x86\x91"); + assert(testResult); + assert(val[0].get_str() == "\xe2\x86\x91"); // Escaped Supplementary Plane character U+1d161 testResult = val.read("[\"\\ud834\\udd61\"]"); - f_assert(testResult); - f_assert(val[0].get_str() == "\xf0\x9d\x85\xa1"); + assert(testResult); + assert(val[0].get_str() == "\xf0\x9d\x85\xa1"); +} + +void no_nul_test() +{ + char buf[] = "___[1,2,3]___"; + UniValue val; + assert(val.read({buf + 3, 7})); } int main (int argc, char *argv[]) { - for (unsigned int fidx = 0; fidx < ARRAY_SIZE(filenames); fidx++) { - runtest_file(filenames[fidx]); + for (const auto& f: filenames) { + runtest_file(f); } unescape_unicode_test(); + no_nul_test(); - return test_failed ? 1 : 0; + return 0; } diff --git a/src/wallet/asyncrpcoperation_common.cpp b/src/wallet/asyncrpcoperation_common.cpp index 23a669a8a..9999adf7d 100644 --- a/src/wallet/asyncrpcoperation_common.cpp +++ b/src/wallet/asyncrpcoperation_common.cpp @@ -15,19 +15,19 @@ UniValue SendTransaction(CTransaction& tx, std::optional SignSendRawTransaction(UniValue obj, std::optional> reservekey, bool testmode) { // Sign the raw transaction - UniValue rawtxnValue = find_value(obj, "rawtxn"); + UniValue rawtxnValue = obj.find_value("rawtxn"); if (rawtxnValue.isNull()) { throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for raw transaction"); } @@ -37,14 +37,14 @@ std::pair SignSendRawTransaction(UniValue obj, std::opti params.push_back(rawtxn); UniValue signResultValue = signrawtransaction(params, false); UniValue signResultObject = signResultValue.get_obj(); - UniValue completeValue = find_value(signResultObject, "complete"); + UniValue completeValue = signResultObject.find_value("complete"); bool complete = completeValue.get_bool(); if (!complete) { // TODO: #1366 Maybe get "errors" and print array vErrors into a string throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to sign transaction"); } - UniValue hexValue = find_value(signResultObject, "hex"); + UniValue hexValue = signResultObject.find_value("hex"); if (hexValue.isNull()) { throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for signed transaction"); } diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index 7236aeccd..0a3ad6204 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -38,7 +38,7 @@ using namespace libzcash; int mta_find_output(UniValue obj, int n) { - UniValue outputMapValue = find_value(obj, "outputmap"); + UniValue outputMapValue = obj.find_value("outputmap"); if (!outputMapValue.isArray()) { throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); } @@ -46,7 +46,7 @@ int mta_find_output(UniValue obj, int n) UniValue outputMap = outputMapValue.get_array(); assert(outputMap.size() == ZC_NUM_JS_OUTPUTS); for (size_t i = 0; i < outputMap.size(); i++) { - if (outputMap[i].get_int() == n) { + if (outputMap[i].getInt() == n) { return i; } } @@ -145,8 +145,8 @@ void AsyncRPCOperation_mergetoaddress::main() try { success = main_impl(); } catch (const UniValue& objError) { - int code = find_value(objError, "code").get_int(); - std::string message = find_value(objError, "message").get_str(); + int code = objError.find_value("code").getInt(); + std::string message = objError.find_value("message").get_str(); set_error_code(code); set_error_message(message); } catch (const runtime_error& e) { @@ -370,7 +370,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() */ if (isPureTaddrOnlyTx) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("rawtxn", EncodeHexTx(tx_))); + obj.pushKV("rawtxn", EncodeHexTx(tx_)); auto txAndResult = SignSendRawTransaction(obj, std::nullopt, testmode); tx_ = txAndResult.first; set_result(txAndResult.second); @@ -901,11 +901,11 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( // !!! Payment disclosure END UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("encryptednote1", encryptedNote1)); - obj.push_back(Pair("encryptednote2", encryptedNote2)); - obj.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); - obj.push_back(Pair("inputmap", arrInputMap)); - obj.push_back(Pair("outputmap", arrOutputMap)); + obj.pushKV("encryptednote1", encryptedNote1); + obj.pushKV("encryptednote2", encryptedNote2); + obj.pushKV("rawtxn", HexStr(ss.begin(), ss.end())); + obj.pushKV("inputmap", arrInputMap); + obj.pushKV("outputmap", arrOutputMap); return obj; } @@ -944,8 +944,8 @@ UniValue AsyncRPCOperation_mergetoaddress::getStatus() const } UniValue obj = v.get_obj(); - obj.push_back(Pair("method", "z_mergetoaddress")); - obj.push_back(Pair("params", contextinfo_)); + obj.pushKV("method", "z_mergetoaddress"); + obj.pushKV("params", contextinfo_); return obj; } diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 81bab2387..9960758d5 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -33,8 +33,8 @@ void AsyncRPCOperation_saplingmigration::main() { try { success = main_impl(); } catch (const UniValue& objError) { - int code = find_value(objError, "code").get_int(); - std::string message = find_value(objError, "message").get_str(); + int code = objError.find_value("code").getInt(); + std::string message = objError.find_value("message").get_str(); set_error_code(code); set_error_message(message); } catch (const runtime_error& e) { @@ -170,13 +170,13 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { void AsyncRPCOperation_saplingmigration::setMigrationResult(int numTxCreated, const CAmount& amountMigrated, const std::vector& migrationTxIds) { UniValue res(UniValue::VOBJ); - res.push_back(Pair("num_tx_created", numTxCreated)); - res.push_back(Pair("amount_migrated", FormatMoney(amountMigrated))); + res.pushKV("num_tx_created", numTxCreated); + res.pushKV("amount_migrated", FormatMoney(amountMigrated)); UniValue txIds(UniValue::VARR); for (const std::string& txId : migrationTxIds) { txIds.push_back(txId); } - res.push_back(Pair("migration_txids", txIds)); + res.pushKV("migration_txids", txIds); set_result(res); } @@ -234,7 +234,7 @@ void AsyncRPCOperation_saplingmigration::cancel() { UniValue AsyncRPCOperation_saplingmigration::getStatus() const { UniValue v = AsyncRPCOperation::getStatus(); UniValue obj = v.get_obj(); - obj.push_back(Pair("method", "saplingmigration")); - obj.push_back(Pair("target_height", targetHeight_)); + obj.pushKV("method", "saplingmigration"); + obj.pushKV("target_height", targetHeight_); return obj; } diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index afeea714b..ed62772f5 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -39,7 +39,7 @@ using namespace libzcash; int find_output(UniValue obj, int n) { - UniValue outputMapValue = find_value(obj, "outputmap"); + UniValue outputMapValue = obj.find_value("outputmap"); if (!outputMapValue.isArray()) { throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); } @@ -47,7 +47,7 @@ int find_output(UniValue obj, int n) { UniValue outputMap = outputMapValue.get_array(); assert(outputMap.size() == ZC_NUM_JS_OUTPUTS); for (size_t i = 0; i < outputMap.size(); i++) { - if (outputMap[i].get_int() == n) { + if (outputMap[i].getInt() == n) { return i; } } @@ -141,8 +141,8 @@ void AsyncRPCOperation_sendmany::main() { try { success = main_impl(); } catch (const UniValue& objError) { - int code = find_value(objError, "code").get_int(); - std::string message = find_value(objError, "message").get_str(); + int code = objError.find_value("code").getInt(); + std::string message = objError.find_value("message").get_str(); set_error_code(code); set_error_message(message); } catch (const runtime_error& e) { @@ -492,7 +492,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { } UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("rawtxn", EncodeHexTx(tx_))); + obj.pushKV("rawtxn", EncodeHexTx(tx_)); auto txAndResult = SignSendRawTransaction(obj, keyChange, testmode); tx_ = txAndResult.first; set_result(txAndResult.second); @@ -1181,11 +1181,11 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( // !!! Payment disclosure END UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("encryptednote1", encryptedNote1)); - obj.push_back(Pair("encryptednote2", encryptedNote2)); - obj.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); - obj.push_back(Pair("inputmap", arrInputMap)); - obj.push_back(Pair("outputmap", arrOutputMap)); + obj.pushKV("encryptednote1", encryptedNote1); + obj.pushKV("encryptednote2", encryptedNote2); + obj.pushKV("rawtxn", HexStr(ss.begin(), ss.end())); + obj.pushKV("inputmap", arrInputMap); + obj.pushKV("outputmap", arrOutputMap); return obj; } @@ -1263,7 +1263,7 @@ UniValue AsyncRPCOperation_sendmany::getStatus() const { } UniValue obj = v.get_obj(); - obj.push_back(Pair("method", "z_sendmany")); - obj.push_back(Pair("params", contextinfo_ )); + obj.pushKV("method", "z_sendmany"); + obj.pushKV("params", contextinfo_ ); return obj; } diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 27fd4c568..8895ed417 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -40,7 +40,7 @@ using namespace libzcash; static int find_output(UniValue obj, int n) { - UniValue outputMapValue = find_value(obj, "outputmap"); + UniValue outputMapValue = obj.find_value("outputmap"); if (!outputMapValue.isArray()) { throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); } @@ -48,7 +48,7 @@ static int find_output(UniValue obj, int n) { UniValue outputMap = outputMapValue.get_array(); assert(outputMap.size() == ZC_NUM_JS_OUTPUTS); for (size_t i = 0; i < outputMap.size(); i++) { - if (outputMap[i].get_int() == n) { + if (outputMap[i].getInt() == n) { return i; } } @@ -118,8 +118,8 @@ void AsyncRPCOperation_shieldcoinbase::main() { try { success = main_impl(); } catch (const UniValue& objError) { - int code = find_value(objError, "code").get_int(); - std::string message = find_value(objError, "message").get_str(); + int code = objError.find_value("code").getInt(); + std::string message = objError.find_value("message").get_str(); set_error_code(code); set_error_message(message); } catch (const runtime_error& e) { @@ -409,11 +409,11 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf // !!! Payment disclosure END UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("encryptednote1", encryptedNote1)); - obj.push_back(Pair("encryptednote2", encryptedNote2)); - obj.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); - obj.push_back(Pair("inputmap", arrInputMap)); - obj.push_back(Pair("outputmap", arrOutputMap)); + obj.pushKV("encryptednote1", encryptedNote1); + obj.pushKV("encryptednote2", encryptedNote2); + obj.pushKV("rawtxn", HexStr(ss.begin(), ss.end())); + obj.pushKV("inputmap", arrInputMap); + obj.pushKV("outputmap", arrOutputMap); return obj; } @@ -427,8 +427,8 @@ UniValue AsyncRPCOperation_shieldcoinbase::getStatus() const { } UniValue obj = v.get_obj(); - obj.push_back(Pair("method", "z_shieldcoinbase")); - obj.push_back(Pair("params", contextinfo_ )); + obj.pushKV("method", "z_shieldcoinbase"); + obj.pushKV("params", contextinfo_ ); return obj; } diff --git a/src/wallet/rpcdisclosure.cpp b/src/wallet/rpcdisclosure.cpp index 5515909cd..8d60b5146 100644 --- a/src/wallet/rpcdisclosure.cpp +++ b/src/wallet/rpcdisclosure.cpp @@ -102,13 +102,13 @@ UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp) } // Check js_index - int js_index = params[1].get_int(); + int js_index = params[1].getInt(); if (js_index < 0 || js_index >= wtx.vJoinSplit.size()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid js_index"); } // Check output_index - int output_index = params[2].get_int(); + int output_index = params[2].getInt(); if (output_index < 0 || output_index >= ZC_NUM_JS_OUTPUTS) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid output_index"); } @@ -223,29 +223,29 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) UniValue errs(UniValue::VARR); UniValue o(UniValue::VOBJ); - o.push_back(Pair("txid", pd.payload.txid.ToString())); + o.pushKV("txid", pd.payload.txid.ToString()); // Check js_index if (pd.payload.js >= tx.vJoinSplit.size()) { errs.push_back("Payment disclosure refers to an invalid joinsplit index"); } - o.push_back(Pair("jsIndex", pd.payload.js)); + o.pushKV("jsIndex", pd.payload.js); if (pd.payload.n < 0 || pd.payload.n >= ZC_NUM_JS_OUTPUTS) { errs.push_back("Payment disclosure refers to an invalid output index"); } - o.push_back(Pair("outputIndex", pd.payload.n)); - o.push_back(Pair("version", pd.payload.version)); - o.push_back(Pair("onetimePrivKey", pd.payload.esk.ToString())); - o.push_back(Pair("message", pd.payload.message)); - o.push_back(Pair("joinSplitPubKey", tx.joinSplitPubKey.ToString())); + o.pushKV("outputIndex", pd.payload.n); + o.pushKV("version", pd.payload.version); + o.pushKV("onetimePrivKey", pd.payload.esk.ToString()); + o.pushKV("message", pd.payload.message); + o.pushKV("joinSplitPubKey", tx.joinSplitPubKey.ToString()); // Verify the payment disclosure was signed using the same key as the transaction i.e. the joinSplitPrivKey. uint256 dataToBeSigned = SerializeHash(pd.payload, SER_GETHASH, 0); bool sigVerified = (crypto_sign_verify_detached(pd.payloadSig.data(), dataToBeSigned.begin(), 32, tx.joinSplitPubKey.begin()) == 0); - o.push_back(Pair("signatureVerified", sigVerified)); + o.pushKV("signatureVerified", sigVerified); if (!sigVerified) { errs.push_back("Payment disclosure signature does not match transaction signature"); } @@ -253,7 +253,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) // Check the payment address is valid SproutPaymentAddress zaddr = pd.payload.zaddr; { - o.push_back(Pair("paymentAddress", EncodePaymentAddress(zaddr))); + o.pushKV("paymentAddress", EncodePaymentAddress(zaddr)); try { // Decrypt the note to get value and memo field @@ -273,15 +273,15 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) ssPlain >> npt; string memoHexString = HexStr(npt.memo().data(), npt.memo().data() + npt.memo().size()); - o.push_back(Pair("memo", memoHexString)); - o.push_back(Pair("value", ValueFromAmount(npt.value()))); + o.pushKV("memo", memoHexString); + o.pushKV("value", ValueFromAmount(npt.value())); // Check the blockchain commitment matches decrypted note commitment uint256 cm_blockchain = jsdesc.commitments[pd.payload.n]; SproutNote note = npt.note(zaddr); uint256 cm_decrypted = note.cm(); bool cm_match = (cm_decrypted == cm_blockchain); - o.push_back(Pair("commitmentMatch", cm_match)); + o.pushKV("commitmentMatch", cm_match); if (!cm_match) { errs.push_back("Commitment derived from payment disclosure does not match blockchain commitment"); } @@ -291,9 +291,9 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) } bool isValid = errs.empty(); - o.push_back(Pair("valid", isValid)); + o.pushKV("valid", isValid); if (!isValid) { - o.push_back(Pair("errors", errs)); + o.pushKV("errors", errs); } return o; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index def887728..47543362b 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -726,7 +726,7 @@ UniValue z_importkey(const UniValue& params, bool fHelp) RPC_INVALID_PARAMETER, "rescan must be \"yes\", \"no\" or \"whenkeyisnew\""); } - fRescan = jVal[0].getBool(); + fRescan = jVal[0].get_bool(); } } } @@ -734,7 +734,7 @@ UniValue z_importkey(const UniValue& params, bool fHelp) // Height to rescan from int nRescanHeight = 0; if (params.size() > 2) - nRescanHeight = params[2].get_int(); + nRescanHeight = params[2].getInt(); if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } @@ -827,7 +827,7 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp) // Height to rescan from int nRescanHeight = 0; if (params.size() > 2) { - nRescanHeight = params[2].get_int(); + nRescanHeight = params[2].getInt(); } if (nRescanHeight < 0 || nRescanHeight > chainActive.Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 451684be3..b375e8036 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -90,15 +90,15 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) int confirms = wtx.GetDepthInMainChain(); std::string status = "waiting"; - entry.push_back(Pair("confirmations", confirms)); + entry.pushKV("confirmations", confirms); if (wtx.IsCoinBase()) - entry.push_back(Pair("generated", true)); + entry.pushKV("generated", true); if (confirms > 0) { - entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); - entry.push_back(Pair("blockindex", wtx.nIndex)); - entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); - entry.push_back(Pair("expiryheight", (int64_t)wtx.nExpiryHeight)); + entry.pushKV("blockhash", wtx.hashBlock.GetHex()); + entry.pushKV("blockindex", wtx.nIndex); + entry.pushKV("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()); + entry.pushKV("expiryheight", (int64_t)wtx.nExpiryHeight); status = "mined"; } else @@ -109,20 +109,20 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) else if (IsExpiredTx(wtx, height)) status = "expired"; } - entry.push_back(Pair("status", status)); + entry.pushKV("status", status); uint256 hash = wtx.GetHash(); - entry.push_back(Pair("txid", hash.GetHex())); + entry.pushKV("txid", hash.GetHex()); UniValue conflicts(UniValue::VARR); for (const uint256& conflict : wtx.GetConflicts()) conflicts.push_back(conflict.GetHex()); - entry.push_back(Pair("walletconflicts", conflicts)); - entry.push_back(Pair("time", wtx.GetTxTime())); - entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); + entry.pushKV("walletconflicts", conflicts); + entry.pushKV("time", wtx.GetTxTime()); + entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived); for (const std::pair& item : wtx.mapValue) - entry.push_back(Pair(item.first, item.second)); + entry.pushKV(item.first, item.second); - entry.push_back(Pair("vJoinSplit", TxJoinSplitToJSON(wtx))); + entry.pushKV("vJoinSplit", TxJoinSplitToJSON(wtx)); } string AccountFromValue(const UniValue& value) @@ -634,7 +634,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) // Minimum confirmations int nMinDepth = 1; if (params.size() > 1) - nMinDepth = params[1].get_int(); + nMinDepth = params[1].getInt(); // Tally CAmount nAmount = 0; @@ -684,7 +684,7 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) // Minimum confirmations int nMinDepth = 1; if (params.size() > 1) - nMinDepth = params[1].get_int(); + nMinDepth = params[1].getInt(); // Get the set of pub keys assigned to account string strAccount = AccountFromValue(params[0]); @@ -774,7 +774,7 @@ UniValue getbalance(const UniValue& params, bool fHelp) int nMinDepth = 1; if (params.size() > 1) - nMinDepth = params[1].get_int(); + nMinDepth = params[1].getInt(); isminefilter filter = ISMINE_SPENDABLE; if(params.size() > 2) if(params[2].get_bool()) @@ -866,7 +866,7 @@ UniValue movecmd(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); if (params.size() > 3) // unused parameter, used to be nMinDepth, keep type-checking it though - (void)params[3].get_int(); + (void)params[3].getInt(); string strComment; if (params.size() > 4) strComment = params[4].get_str(); @@ -948,7 +948,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); int nMinDepth = 1; if (params.size() > 3) - nMinDepth = params[3].get_int(); + nMinDepth = params[3].getInt(); CWalletTx wtx; wtx.strFromAccount = strAccount; @@ -1017,7 +1017,7 @@ UniValue sendmany(const UniValue& params, bool fHelp) UniValue sendTo = params[1].get_obj(); int nMinDepth = 1; if (params.size() > 2) - nMinDepth = params[2].get_int(); + nMinDepth = params[2].getInt(); CWalletTx wtx; wtx.strFromAccount = strAccount; @@ -1153,7 +1153,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) // Minimum confirmations int nMinDepth = 1; if (params.size() > 0) - nMinDepth = params[0].get_int(); + nMinDepth = params[0].getInt(); // Whether to include empty accounts bool fIncludeEmpty = false; @@ -1227,11 +1227,11 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) { UniValue obj(UniValue::VOBJ); if(fIsWatchonly) - obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("address", EncodeDestination(dest))); - obj.push_back(Pair("account", strAccount)); - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + obj.pushKV("involvesWatchonly", true); + obj.pushKV("address", EncodeDestination(dest)); + obj.pushKV("account", strAccount); + obj.pushKV("amount", ValueFromAmount(nAmount)); + obj.pushKV("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf)); UniValue transactions(UniValue::VARR); if (it != mapTally.end()) { @@ -1240,7 +1240,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) transactions.push_back(item.GetHex()); } } - obj.push_back(Pair("txids", transactions)); + obj.pushKV("txids", transactions); ret.push_back(obj); } } @@ -1253,10 +1253,10 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) int nConf = (*it).second.nConf; UniValue obj(UniValue::VOBJ); if((*it).second.fIsWatchonly) - obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("account", (*it).first)); - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + obj.pushKV("involvesWatchonly", true); + obj.pushKV("account", (*it).first); + obj.pushKV("amount", ValueFromAmount(nAmount)); + obj.pushKV("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf)); ret.push_back(obj); } } @@ -1340,7 +1340,7 @@ UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) { if (IsValidDestination(dest)) { - entry.push_back(Pair("address", EncodeDestination(dest))); + entry.pushKV("address", EncodeDestination(dest)); } } @@ -1363,16 +1363,16 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { UniValue entry(UniValue::VOBJ); if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) - entry.push_back(Pair("involvesWatchonly", true)); - entry.push_back(Pair("account", strSentAccount)); + entry.pushKV("involvesWatchonly", true); + entry.pushKV("account", strSentAccount); MaybePushAddress(entry, s.destination); - entry.push_back(Pair("category", "send")); - entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); - entry.push_back(Pair("vout", s.vout)); - entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + entry.pushKV("category", "send"); + entry.pushKV("amount", ValueFromAmount(-s.amount)); + entry.pushKV("vout", s.vout); + entry.pushKV("fee", ValueFromAmount(-nFee)); if (fLong) WalletTxToJSON(wtx, entry); - entry.push_back(Pair("size", static_cast(GetSerializeSize(static_cast(wtx), SER_NETWORK, PROTOCOL_VERSION)))); + entry.pushKV("size", static_cast(GetSerializeSize(static_cast(wtx), SER_NETWORK, PROTOCOL_VERSION))); ret.push_back(entry); } } @@ -1389,27 +1389,27 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { UniValue entry(UniValue::VOBJ); if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) - entry.push_back(Pair("involvesWatchonly", true)); - entry.push_back(Pair("account", account)); + entry.pushKV("involvesWatchonly", true); + entry.pushKV("account", account); MaybePushAddress(entry, r.destination); if (wtx.IsCoinBase()) { if (wtx.GetDepthInMainChain() < 1) - entry.push_back(Pair("category", "orphan")); + entry.pushKV("category", "orphan"); else if (wtx.GetBlocksToMaturity() > 0) - entry.push_back(Pair("category", "immature")); + entry.pushKV("category", "immature"); else - entry.push_back(Pair("category", "generate")); + entry.pushKV("category", "generate"); } else { - entry.push_back(Pair("category", "receive")); + entry.pushKV("category", "receive"); } - entry.push_back(Pair("amount", ValueFromAmount(r.amount))); - entry.push_back(Pair("vout", r.vout)); + entry.pushKV("amount", ValueFromAmount(r.amount)); + entry.pushKV("vout", r.vout); if (fLong) WalletTxToJSON(wtx, entry); - entry.push_back(Pair("size", static_cast(GetSerializeSize(static_cast(wtx), SER_NETWORK, PROTOCOL_VERSION)))); + entry.pushKV("size", static_cast(GetSerializeSize(static_cast(wtx), SER_NETWORK, PROTOCOL_VERSION))); ret.push_back(entry); } } @@ -1423,12 +1423,12 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Un if (fAllAccounts || acentry.strAccount == strAccount) { UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("account", acentry.strAccount)); - entry.push_back(Pair("category", "move")); - entry.push_back(Pair("time", acentry.nTime)); - entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); - entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); - entry.push_back(Pair("comment", acentry.strComment)); + entry.pushKV("account", acentry.strAccount); + entry.pushKV("category", "move"); + entry.pushKV("time", acentry.nTime); + entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit)); + entry.pushKV("otheraccount", acentry.strOtherAccount); + entry.pushKV("comment", acentry.strComment); ret.push_back(entry); } } @@ -1500,10 +1500,10 @@ UniValue listtransactions(const UniValue& params, bool fHelp) strAccount = params[0].get_str(); int nCount = 10; if (params.size() > 1) - nCount = params[1].get_int(); + nCount = params[1].getInt(); int nFrom = 0; if (params.size() > 2) - nFrom = params[2].get_int(); + nFrom = params[2].getInt(); isminefilter filter = ISMINE_SPENDABLE; if(params.size() > 3) if(params[3].get_bool()) @@ -1589,7 +1589,7 @@ UniValue listaccounts(const UniValue& params, bool fHelp) int nMinDepth = 1; if (params.size() > 0) - nMinDepth = params[0].get_int(); + nMinDepth = params[0].getInt(); isminefilter includeWatchonly = ISMINE_SPENDABLE; if(params.size() > 1) if(params[1].get_bool()) @@ -1632,7 +1632,7 @@ UniValue listaccounts(const UniValue& params, bool fHelp) UniValue ret(UniValue::VOBJ); for (const std::pair& accountBalance : mapAccountBalances) { - ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + ret.pushKV(accountBalance.first, ValueFromAmount(accountBalance.second)); } return ret; } @@ -1698,7 +1698,7 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) if (params.size() > 1) { - target_confirms = params[1].get_int(); + target_confirms = params[1].getInt(); if (target_confirms < 1) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); @@ -1724,8 +1724,8 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("transactions", transactions)); - ret.push_back(Pair("lastblock", lastblock.GetHex())); + ret.pushKV("transactions", transactions); + ret.pushKV("lastblock", lastblock.GetHex()); return ret; } @@ -1803,18 +1803,18 @@ UniValue gettransaction(const UniValue& params, bool fHelp) CAmount nNet = nCredit - nDebit; CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); - entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + entry.pushKV("amount", ValueFromAmount(nNet - nFee)); if (wtx.IsFromMe(filter)) - entry.push_back(Pair("fee", ValueFromAmount(nFee))); + entry.pushKV("fee", ValueFromAmount(nFee)); WalletTxToJSON(wtx, entry); UniValue details(UniValue::VARR); ListTransactions(wtx, "*", 0, false, details, filter); - entry.push_back(Pair("details", details)); + entry.pushKV("details", details); string strHex = EncodeHexTx(static_cast(wtx)); - entry.push_back(Pair("hex", strHex)); + entry.pushKV("hex", strHex); return entry; } @@ -1885,9 +1885,9 @@ UniValue keypoolrefill(const UniValue& params, bool fHelp) // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool unsigned int kpSize = 0; if (params.size() > 0) { - if (params[0].get_int() < 0) + if (params[0].getInt() < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); - kpSize = (unsigned int)params[0].get_int(); + kpSize = (unsigned int)params[0].getInt(); } EnsureWalletIsUnlocked(); @@ -1960,7 +1960,7 @@ UniValue walletpassphrase(const UniValue& params, bool fHelp) pwalletMain->UpdateNullifierNoteMap(); pwalletMain->TopUpKeyPool(); - int64_t nSleepTime = params[1].get_int64(); + int64_t nSleepTime = params[1].getInt(); LOCK(cs_nWalletUnlockTime); nWalletUnlockTime = GetTime() + nSleepTime; RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); @@ -2185,11 +2185,11 @@ UniValue lockunspent(const UniValue& params, bool fHelp) RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); - string txid = find_value(o, "txid").get_str(); + string txid = o.find_value("txid").get_str(); if (!IsHex(txid)) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); - int nOutput = find_value(o, "vout").get_int(); + int nOutput = o.find_value("vout").getInt(); if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); @@ -2245,8 +2245,8 @@ UniValue listlockunspent(const UniValue& params, bool fHelp) for (COutPoint &outpt : vOutpts) { UniValue o(UniValue::VOBJ); - o.push_back(Pair("txid", outpt.hash.GetHex())); - o.push_back(Pair("vout", (int)outpt.n)); + o.pushKV("txid", outpt.hash.GetHex()); + o.pushKV("vout", (int)outpt.n); ret.push_back(o); } @@ -2310,19 +2310,19 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); - obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()))); - obj.push_back(Pair("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance()))); - obj.push_back(Pair("txcount", (int)pwalletMain->mapWallet.size())); - obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + obj.pushKV("walletversion", pwalletMain->GetVersion()); + obj.pushKV("balance", ValueFromAmount(pwalletMain->GetBalance())); + obj.pushKV("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance())); + obj.pushKV("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance())); + obj.pushKV("txcount", (int)pwalletMain->mapWallet.size()); + obj.pushKV("keypoololdest", pwalletMain->GetOldestKeyPoolTime()); + obj.pushKV("keypoolsize", (int)pwalletMain->GetKeyPoolSize()); if (pwalletMain->IsCrypted()) - obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); - obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); + obj.pushKV("unlocked_until", nWalletUnlockTime); + obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); uint256 seedFp = pwalletMain->GetHDChain().seedFp; if (!seedFp.IsNull()) - obj.push_back(Pair("seedfp", seedFp.GetHex())); + obj.pushKV("seedfp", seedFp.GetHex()); return obj; } @@ -2399,11 +2399,11 @@ UniValue listunspent(const UniValue& params, bool fHelp) int nMinDepth = 1; if (params.size() > 0) - nMinDepth = params[0].get_int(); + nMinDepth = params[0].getInt(); int nMaxDepth = 9999999; if (params.size() > 1) - nMaxDepth = params[1].get_int(); + nMaxDepth = params[1].getInt(); std::set destinations; if (params.size() > 2) { @@ -2437,28 +2437,28 @@ UniValue listunspent(const UniValue& params, bool fHelp) continue; UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); - entry.push_back(Pair("vout", out.i)); - entry.push_back(Pair("generated", out.tx->IsCoinBase())); + entry.pushKV("txid", out.tx->GetHash().GetHex()); + entry.pushKV("vout", out.i); + entry.pushKV("generated", out.tx->IsCoinBase()); if (fValidAddress) { - entry.push_back(Pair("address", EncodeDestination(address))); + entry.pushKV("address", EncodeDestination(address)); if (pwalletMain->mapAddressBook.count(address)) - entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + entry.pushKV("account", pwalletMain->mapAddressBook[address].name); if (scriptPubKey.IsPayToScriptHash()) { const CScriptID& hash = std::get(address); CScript redeemScript; if (pwalletMain->GetCScript(hash, redeemScript)) - entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); } } - entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - entry.push_back(Pair("amount", ValueFromAmount(out.tx->vout[out.i].nValue))); - entry.push_back(Pair("confirmations", out.nDepth)); - entry.push_back(Pair("spendable", out.fSpendable)); + entry.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); + entry.pushKV("amount", ValueFromAmount(out.tx->vout[out.i].nValue)); + entry.pushKV("confirmations", out.nDepth); + entry.pushKV("spendable", out.fSpendable); results.push_back(entry); } @@ -2516,7 +2516,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) int nMinDepth = 1; if (params.size() > 0) { - nMinDepth = params[0].get_int(); + nMinDepth = params[0].getInt(); } if (nMinDepth < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); @@ -2524,7 +2524,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) int nMaxDepth = 9999999; if (params.size() > 1) { - nMaxDepth = params[1].get_int(); + nMaxDepth = params[1].getInt(); } if (nMaxDepth < nMinDepth) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Maximum number of confirmations must be greater or equal to the minimum number of confirmations"); @@ -2593,34 +2593,34 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) for (auto & entry : sproutEntries) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("txid", entry.jsop.hash.ToString())); - obj.push_back(Pair("jsindex", (int)entry.jsop.js )); - obj.push_back(Pair("jsoutindex", (int)entry.jsop.n)); - obj.push_back(Pair("confirmations", entry.confirmations)); + obj.pushKV("txid", entry.jsop.hash.ToString()); + obj.pushKV("jsindex", (int)entry.jsop.js ); + obj.pushKV("jsoutindex", (int)entry.jsop.n); + obj.pushKV("confirmations", entry.confirmations); bool hasSproutSpendingKey = HaveSpendingKeyForPaymentAddress(pwalletMain)(entry.address); - obj.push_back(Pair("spendable", hasSproutSpendingKey)); - obj.push_back(Pair("address", EncodePaymentAddress(entry.address))); - obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value())))); + obj.pushKV("spendable", hasSproutSpendingKey); + obj.pushKV("address", EncodePaymentAddress(entry.address)); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); std::string data(entry.memo.begin(), entry.memo.end()); - obj.push_back(Pair("memo", HexStr(data))); + obj.pushKV("memo", HexStr(data)); if (hasSproutSpendingKey) { - obj.push_back(Pair("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop))); + obj.pushKV("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)); } results.push_back(obj); } for (auto & entry : saplingEntries) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("txid", entry.op.hash.ToString())); - obj.push_back(Pair("outindex", (int)entry.op.n)); - obj.push_back(Pair("confirmations", entry.confirmations)); + obj.pushKV("txid", entry.op.hash.ToString()); + obj.pushKV("outindex", (int)entry.op.n); + obj.pushKV("confirmations", entry.confirmations); bool hasSaplingSpendingKey = HaveSpendingKeyForPaymentAddress(pwalletMain)(entry.address); - obj.push_back(Pair("spendable", hasSaplingSpendingKey)); - obj.push_back(Pair("address", EncodePaymentAddress(entry.address))); - obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value())))); // note.value() is equivalent to plaintext.value() - obj.push_back(Pair("memo", HexStr(entry.memo))); + obj.pushKV("spendable", hasSaplingSpendingKey); + obj.pushKV("address", EncodePaymentAddress(entry.address)); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); // note.value() is equivalent to plaintext.value() + obj.pushKV("memo", HexStr(entry.memo)); if (hasSaplingSpendingKey) { - obj.push_back(Pair("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op))); + obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); } results.push_back(obj); } @@ -2686,9 +2686,9 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", EncodeHexTx(tx))); - result.push_back(Pair("changepos", nChangePos)); - result.push_back(Pair("fee", ValueFromAmount(nFee))); + result.pushKV("hex", EncodeHexTx(tx)); + result.pushKV("changepos", nChangePos); + result.pushKV("fee", ValueFromAmount(nFee)); return result; } @@ -2749,7 +2749,7 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) LOCK(cs_main); std::string benchmarktype = params[0].get_str(); - int samplecount = params[1].get_int(); + int samplecount = params[1].getInt(); if (samplecount <= 0) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid samplecount"); @@ -2773,7 +2773,7 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) if (params.size() < 3) { sample_times.push_back(benchmark_create_joinsplit()); } else { - int nThreads = params[2].get_int(); + int nThreads = params[2].getInt(); std::vector vals = benchmark_create_joinsplit_threaded(nThreads); // Divide by nThreads^2 to get average seconds per JoinSplit because // we are running one JoinSplit per thread. @@ -2786,7 +2786,7 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) if (params.size() < 3) { sample_times.push_back(benchmark_solve_equihash()); } else { - int nThreads = params[2].get_int(); + int nThreads = params[2].getInt(); std::vector vals = benchmark_solve_equihash_threaded(nThreads); sample_times.insert(sample_times.end(), vals.begin(), vals.end()); } @@ -2797,20 +2797,20 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) // Number of inputs in the spending transaction that we will simulate int nInputs = 11130; if (params.size() >= 3) { - nInputs = params[2].get_int(); + nInputs = params[2].getInt(); } sample_times.push_back(benchmark_large_tx(nInputs)); } else if (benchmarktype == "trydecryptnotes") { - int nKeys = params[2].get_int(); + int nKeys = params[2].getInt(); sample_times.push_back(benchmark_try_decrypt_sprout_notes(nKeys)); } else if (benchmarktype == "trydecryptsaplingnotes") { - int nKeys = params[2].get_int(); + int nKeys = params[2].getInt(); sample_times.push_back(benchmark_try_decrypt_sapling_notes(nKeys)); } else if (benchmarktype == "incnotewitnesses") { - int nTxs = params[2].get_int(); + int nTxs = params[2].getInt(); sample_times.push_back(benchmark_increment_sprout_note_witnesses(nTxs)); } else if (benchmarktype == "incsaplingnotewitnesses") { - int nTxs = params[2].get_int(); + int nTxs = params[2].getInt(); sample_times.push_back(benchmark_increment_sapling_note_witnesses(nTxs)); } else if (benchmarktype == "connectblockslow") { if (Params().NetworkIDString() != "regtest") { @@ -2846,7 +2846,7 @@ UniValue zc_benchmark(const UniValue& params, bool fHelp) UniValue results(UniValue::VARR); for (auto time : sample_times) { UniValue result(UniValue::VOBJ); - result.push_back(Pair("runningtime", time)); + result.pushKV("runningtime", time); results.push_back(result); } @@ -2932,9 +2932,9 @@ UniValue zc_raw_receive(const UniValue& params, bool fHelp) ss << npt; UniValue result(UniValue::VOBJ); - result.push_back(Pair("amount", ValueFromAmount(decrypted_note.value()))); - result.push_back(Pair("note", HexStr(ss.begin(), ss.end()))); - result.push_back(Pair("exists", (bool) witnesses[0])); + result.pushKV("amount", ValueFromAmount(decrypted_note.value())); + result.pushKV("note", HexStr(ss.begin(), ss.end())); + result.pushKV("exists", (bool) witnesses[0]); return result; } @@ -3133,9 +3133,9 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) } UniValue result(UniValue::VOBJ); - result.push_back(Pair("encryptednote1", encryptedNote1)); - result.push_back(Pair("encryptednote2", encryptedNote2)); - result.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); + result.pushKV("encryptednote1", encryptedNote1); + result.pushKV("encryptednote2", encryptedNote2); + result.pushKV("rawtxn", HexStr(ss.begin(), ss.end())); return result; } @@ -3164,9 +3164,9 @@ UniValue zc_raw_keygen(const UniValue& params, bool fHelp) auto viewing_key = k.viewing_key(); UniValue result(UniValue::VOBJ); - result.push_back(Pair("zcaddress", EncodePaymentAddress(addr))); - result.push_back(Pair("zcsecretkey", EncodeSpendingKey(k))); - result.push_back(Pair("zcviewingkey", EncodeViewingKey(viewing_key))); + result.pushKV("zcaddress", EncodePaymentAddress(addr)); + result.pushKV("zcsecretkey", EncodeSpendingKey(k)); + result.pushKV("zcviewingkey", EncodeViewingKey(viewing_key)); return result; } @@ -3353,7 +3353,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) int nMinDepth = 1; if (params.size() > 1) { - nMinDepth = params[1].get_int(); + nMinDepth = params[1].getInt(); } if (nMinDepth < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); @@ -3386,26 +3386,26 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) if (std::get_if(&zaddr) != nullptr) { for (SproutNoteEntry & entry : sproutEntries) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("txid", entry.jsop.hash.ToString())); - obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value())))); + obj.pushKV("txid", entry.jsop.hash.ToString()); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); std::string data(entry.memo.begin(), entry.memo.end()); - obj.push_back(Pair("memo", HexStr(data))); - obj.push_back(Pair("jsindex", entry.jsop.js)); - obj.push_back(Pair("jsoutindex", entry.jsop.n)); + obj.pushKV("memo", HexStr(data)); + obj.pushKV("jsindex", entry.jsop.js); + obj.pushKV("jsoutindex", entry.jsop.n); if (hasSpendingKey) { - obj.push_back(Pair("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop))); + obj.pushKV("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)); } result.push_back(obj); } } else if (std::get_if(&zaddr) != nullptr) { for (SaplingNoteEntry & entry : saplingEntries) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("txid", entry.op.hash.ToString())); - obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value())))); - obj.push_back(Pair("memo", HexStr(entry.memo))); - obj.push_back(Pair("outindex", (int)entry.op.n)); + obj.pushKV("txid", entry.op.hash.ToString()); + obj.pushKV("amount", ValueFromAmount(CAmount(entry.note.value()))); + obj.pushKV("memo", HexStr(entry.memo)); + obj.pushKV("outindex", (int)entry.op.n); if (hasSpendingKey) { - obj.push_back(Pair("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op))); + obj.pushKV("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)); } result.push_back(obj); } @@ -3442,7 +3442,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp) int nMinDepth = 1; if (params.size() > 1) { - nMinDepth = params[1].get_int(); + nMinDepth = params[1].getInt(); } if (nMinDepth < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); @@ -3508,7 +3508,7 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp) int nMinDepth = 1; if (params.size() > 0) { - nMinDepth = params[0].get_int(); + nMinDepth = params[0].getInt(); } if (nMinDepth < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); @@ -3527,9 +3527,9 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp) CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth, !fIncludeWatchonly); CAmount nTotalBalance = nBalance + nPrivateBalance; UniValue result(UniValue::VOBJ); - result.push_back(Pair("transparent", FormatMoney(nBalance))); - result.push_back(Pair("private", FormatMoney(nPrivateBalance))); - result.push_back(Pair("total", FormatMoney(nTotalBalance))); + result.pushKV("transparent", FormatMoney(nBalance)); + result.pushKV("private", FormatMoney(nPrivateBalance)); + result.pushKV("total", FormatMoney(nTotalBalance)); return result; } @@ -3595,13 +3595,13 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - entry.push_back(Pair("txid", hash.GetHex())); + entry.pushKV("txid", hash.GetHex()); UniValue spends(UniValue::VARR); UniValue outputs(UniValue::VARR); auto addMemo = [](UniValue &entry, std::array &memo) { - entry.push_back(Pair("memo", HexStr(memo))); + entry.pushKV("memo", HexStr(memo)); // If the leading byte is 0xF4 or lower, the memo field should be interpreted as a // UTF-8-encoded text string. @@ -3613,7 +3613,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) [](unsigned char v) { return v != 0; }); std::string memoStr(memo.begin(), end.base()); if (utf8::is_valid(memoStr)) { - entry.push_back(Pair("memoStr", memoStr)); + entry.pushKV("memoStr", memoStr); } } }; @@ -3636,15 +3636,15 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) auto pa = decrypted.second; UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("type", ADDR_TYPE_SPROUT)); - entry.push_back(Pair("js", (int)i)); - entry.push_back(Pair("jsSpend", (int)j)); - entry.push_back(Pair("txidPrev", jsop.hash.GetHex())); - entry.push_back(Pair("jsPrev", (int)jsop.js)); - entry.push_back(Pair("jsOutputPrev", (int)jsop.n)); - entry.push_back(Pair("address", EncodePaymentAddress(pa))); - entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); - entry.push_back(Pair("valueZat", notePt.value())); + entry.pushKV("type", ADDR_TYPE_SPROUT); + entry.pushKV("js", (int)i); + entry.pushKV("jsSpend", (int)j); + entry.pushKV("txidPrev", jsop.hash.GetHex()); + entry.pushKV("jsPrev", (int)jsop.js); + entry.pushKV("jsOutputPrev", (int)jsop.n); + entry.pushKV("address", EncodePaymentAddress(pa)); + entry.pushKV("value", ValueFromAmount(notePt.value())); + entry.pushKV("valueZat", notePt.value()); spends.push_back(entry); } } @@ -3659,12 +3659,12 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) auto memo = notePt.memo(); UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("type", ADDR_TYPE_SPROUT)); - entry.push_back(Pair("js", (int)jsop.js)); - entry.push_back(Pair("jsOutput", (int)jsop.n)); - entry.push_back(Pair("address", EncodePaymentAddress(pa))); - entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); - entry.push_back(Pair("valueZat", notePt.value())); + entry.pushKV("type", ADDR_TYPE_SPROUT); + entry.pushKV("js", (int)jsop.js); + entry.pushKV("jsOutput", (int)jsop.n); + entry.pushKV("address", EncodePaymentAddress(pa)); + entry.pushKV("value", ValueFromAmount(notePt.value())); + entry.pushKV("valueZat", notePt.value()); addMemo(entry, memo); outputs.push_back(entry); } @@ -3699,13 +3699,13 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) ovks.insert(extfvk.fvk.ovk); UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("type", ADDR_TYPE_SAPLING)); - entry.push_back(Pair("spend", (int)i)); - entry.push_back(Pair("txidPrev", op.hash.GetHex())); - entry.push_back(Pair("outputPrev", (int)op.n)); - entry.push_back(Pair("address", EncodePaymentAddress(pa))); - entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); - entry.push_back(Pair("valueZat", notePt.value())); + entry.pushKV("type", ADDR_TYPE_SAPLING); + entry.pushKV("spend", (int)i); + entry.pushKV("txidPrev", op.hash.GetHex()); + entry.pushKV("outputPrev", (int)op.n); + entry.pushKV("address", EncodePaymentAddress(pa)); + entry.pushKV("value", ValueFromAmount(notePt.value())); + entry.pushKV("valueZat", notePt.value()); spends.push_back(entry); } @@ -3737,18 +3737,18 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) auto memo = notePt.memo(); UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("type", ADDR_TYPE_SAPLING)); - entry.push_back(Pair("output", (int)op.n)); - entry.push_back(Pair("outgoing", isOutgoing)); - entry.push_back(Pair("address", EncodePaymentAddress(pa))); - entry.push_back(Pair("value", ValueFromAmount(notePt.value()))); - entry.push_back(Pair("valueZat", notePt.value())); + entry.pushKV("type", ADDR_TYPE_SAPLING); + entry.pushKV("output", (int)op.n); + entry.pushKV("outgoing", isOutgoing); + entry.pushKV("address", EncodePaymentAddress(pa)); + entry.pushKV("value", ValueFromAmount(notePt.value())); + entry.pushKV("valueZat", notePt.value()); addMemo(entry, memo); outputs.push_back(entry); } - entry.push_back(Pair("spends", spends)); - entry.push_back(Pair("outputs", outputs)); + entry.pushKV("spends", spends); + entry.pushKV("outputs", outputs); return entry; } @@ -3844,8 +3844,8 @@ UniValue z_getoperationstatus_IMPL(const UniValue& params, bool fRemoveFinishedO // sort results chronologically by creation_time std::sort(arrTmp.begin(), arrTmp.end(), [](UniValue a, UniValue b) -> bool { - const int64_t t1 = find_value(a.get_obj(), "creation_time").get_int64(); - const int64_t t2 = find_value(b.get_obj(), "creation_time").get_int64(); + const int64_t t1 = a.get_obj().find_value("creation_time").getInt(); + const int64_t t2 = b.get_obj().find_value("creation_time").getInt(); return t1 < t2; }); @@ -3956,7 +3956,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown key: ")+s); } - string address = find_value(o, "address").get_str(); + string address = o.find_value("address").get_str(); bool isZaddr = false; CTxDestination taddr = DecodeDestination(address); if (!IsValidDestination(taddr)) { @@ -3993,7 +3993,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+address); setAddress.insert(address); - UniValue memoValue = find_value(o, "memo"); + UniValue memoValue = o.find_value("memo"); string memo; if (!memoValue.isNull()) { memo = memoValue.get_str(); @@ -4007,7 +4007,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) } } - UniValue av = find_value(o, "amount"); + UniValue av = o.find_value("amount"); CAmount nAmount = AmountFromValue( av ); if (nAmount < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive"); @@ -4079,7 +4079,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) // Minimum confirmations int nMinDepth = DEFAULT_NOTE_CONFIRMATIONS; if (params.size() > 2) { - nMinDepth = params[2].get_int(); + nMinDepth = params[2].getInt(); } if (nMinDepth < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); @@ -4113,10 +4113,10 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) // Use input parameters as the optional context info to be returned by z_getoperationstatus and z_getoperationresult. UniValue o(UniValue::VOBJ); - o.push_back(Pair("fromaddress", params[0])); - o.push_back(Pair("amounts", params[1])); - o.push_back(Pair("minconf", nMinDepth)); - o.push_back(Pair("fee", std::stod(FormatMoney(nFee)))); + o.pushKV("fromaddress", params[0]); + o.pushKV("amounts", params[1]); + o.pushKV("minconf", nMinDepth); + o.pushKV("fee", std::stod(FormatMoney(nFee))); UniValue contextInfo = o; if (!fromTaddr || !zaddrRecipients.empty()) { @@ -4199,12 +4199,12 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) { ); LOCK2(cs_main, pwalletMain->cs_wallet); UniValue migrationStatus(UniValue::VOBJ); - migrationStatus.push_back(Pair("enabled", pwalletMain->fSaplingMigrationEnabled)); + migrationStatus.pushKV("enabled", pwalletMain->fSaplingMigrationEnabled); // The "destination_address" field MAY be omitted if the "-migrationdestaddress" // parameter is not set and no default address has yet been generated. // Note: The following function may return the default address even if it has not been added to the wallet auto destinationAddress = AsyncRPCOperation_saplingmigration::getMigrationDestAddress(pwalletMain->GetHDSeedForRPC()); - migrationStatus.push_back(Pair("destination_address", EncodePaymentAddress(destinationAddress))); + migrationStatus.pushKV("destination_address", EncodePaymentAddress(destinationAddress)); // The values of "unmigrated_amount" and "migrated_amount" MUST take into // account failed transactions, that were not mined within their expiration // height. @@ -4219,7 +4219,7 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) { for (const auto& sproutEntry : sproutEntries) { unmigratedAmount += sproutEntry.note.value(); } - migrationStatus.push_back(Pair("unmigrated_amount", FormatMoney(unmigratedAmount))); + migrationStatus.pushKV("unmigrated_amount", FormatMoney(unmigratedAmount)); } // "migration_txids" is a list of strings representing transaction IDs of all // known migration transactions involving this wallet, as lowercase hexadecimal @@ -4268,13 +4268,13 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) { } } } - migrationStatus.push_back(Pair("unfinalized_migrated_amount", FormatMoney(unfinalizedMigratedAmount))); - migrationStatus.push_back(Pair("finalized_migrated_amount", FormatMoney(finalizedMigratedAmount))); - migrationStatus.push_back(Pair("finalized_migration_transactions", numFinalizedMigrationTxs)); + migrationStatus.pushKV("unfinalized_migrated_amount", FormatMoney(unfinalizedMigratedAmount)); + migrationStatus.pushKV("finalized_migrated_amount", FormatMoney(finalizedMigratedAmount)); + migrationStatus.pushKV("finalized_migration_transactions", numFinalizedMigrationTxs); if (timeStarted > 0) { - migrationStatus.push_back(Pair("time_started", timeStarted)); + migrationStatus.pushKV("time_started", timeStarted); } - migrationStatus.push_back(Pair("migration_txids", migrationTxids)); + migrationStatus.pushKV("migration_txids", migrationTxids); return migrationStatus; } @@ -4359,7 +4359,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) int nLimit = SHIELD_COINBASE_DEFAULT_LIMIT; if (params.size() > 3) { - nLimit = params[3].get_int(); + nLimit = params[3].getInt(); if (nLimit < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of utxos cannot be negative"); } @@ -4459,9 +4459,9 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) // Keep record of parameters in context object UniValue contextInfo(UniValue::VOBJ); - contextInfo.push_back(Pair("fromaddress", params[0])); - contextInfo.push_back(Pair("toaddress", params[1])); - contextInfo.push_back(Pair("fee", ValueFromAmount(nFee))); + contextInfo.pushKV("fromaddress", params[0]); + contextInfo.pushKV("toaddress", params[1]); + contextInfo.pushKV("fee", ValueFromAmount(nFee)); // Builder (used if Sapling addresses are involved) TransactionBuilder builder = TransactionBuilder( @@ -4483,11 +4483,11 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) // Return continuation information UniValue o(UniValue::VOBJ); - o.push_back(Pair("remainingUTXOs", static_cast(utxoCounter - numUtxos))); - o.push_back(Pair("remainingValue", ValueFromAmount(remainingValue))); - o.push_back(Pair("shieldingUTXOs", static_cast(numUtxos))); - o.push_back(Pair("shieldingValue", ValueFromAmount(shieldedValue))); - o.push_back(Pair("opid", operationId)); + o.pushKV("remainingUTXOs", static_cast(utxoCounter - numUtxos)); + o.pushKV("remainingValue", ValueFromAmount(remainingValue)); + o.pushKV("shieldingUTXOs", static_cast(numUtxos)); + o.pushKV("shieldingValue", ValueFromAmount(shieldedValue)); + o.pushKV("opid", operationId); return o; } @@ -4644,7 +4644,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) int nUTXOLimit = MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT; if (params.size() > 3) { - nUTXOLimit = params[3].get_int(); + nUTXOLimit = params[3].getInt(); if (nUTXOLimit < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of UTXOs cannot be negative"); } @@ -4653,7 +4653,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) int sproutNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT; int saplingNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT; if (params.size() > 4) { - int nNoteLimit = params[4].get_int(); + int nNoteLimit = params[4].getInt(); if (nNoteLimit < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); } @@ -4857,9 +4857,9 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) // Keep record of parameters in context object UniValue contextInfo(UniValue::VOBJ); - contextInfo.push_back(Pair("fromaddresses", params[0])); - contextInfo.push_back(Pair("toaddress", params[1])); - contextInfo.push_back(Pair("fee", ValueFromAmount(nFee))); + contextInfo.pushKV("fromaddresses", params[0]); + contextInfo.pushKV("toaddress", params[1]); + contextInfo.pushKV("fee", ValueFromAmount(nFee)); if (!sproutNoteInputs.empty() || !saplingNoteInputs.empty() || !IsValidDestination(taddr)) { // We have shielded inputs or the recipient is a shielded address, and @@ -4893,15 +4893,15 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) // Return continuation information UniValue o(UniValue::VOBJ); - o.push_back(Pair("remainingUTXOs", static_cast(utxoCounter - numUtxos))); - o.push_back(Pair("remainingTransparentValue", ValueFromAmount(remainingUTXOValue))); - o.push_back(Pair("remainingNotes", static_cast(noteCounter - numNotes))); - o.push_back(Pair("remainingShieldedValue", ValueFromAmount(remainingNoteValue))); - o.push_back(Pair("mergingUTXOs", static_cast(numUtxos))); - o.push_back(Pair("mergingTransparentValue", ValueFromAmount(mergedUTXOValue))); - o.push_back(Pair("mergingNotes", static_cast(numNotes))); - o.push_back(Pair("mergingShieldedValue", ValueFromAmount(mergedNoteValue))); - o.push_back(Pair("opid", operationId)); + o.pushKV("remainingUTXOs", static_cast(utxoCounter - numUtxos)); + o.pushKV("remainingTransparentValue", ValueFromAmount(remainingUTXOValue)); + o.pushKV("remainingNotes", static_cast(noteCounter - numNotes)); + o.pushKV("remainingShieldedValue", ValueFromAmount(remainingNoteValue)); + o.pushKV("mergingUTXOs", static_cast(numUtxos)); + o.pushKV("mergingTransparentValue", ValueFromAmount(mergedUTXOValue)); + o.pushKV("mergingNotes", static_cast(numNotes)); + o.pushKV("mergingShieldedValue", ValueFromAmount(mergedNoteValue)); + o.pushKV("opid", operationId); return o; } diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 387bad760..0432765d6 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -47,13 +47,13 @@ extern UniValue CallRPC(string args); extern CWallet* pwalletMain; bool find_error(const UniValue& objError, const std::string& expected) { - return find_value(objError, "message").get_str().find(expected) != string::npos; + return objError.find_value("message").get_str().find(expected) != string::npos; } -static UniValue ValueFromString(const std::string &str) +static UniValue ValueFromString(const std::string& str) noexcept { UniValue value; - BOOST_CHECK(value.setNumStr(str)); + value.setNumStr(str); return value; } @@ -275,39 +275,39 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 50000")); UniValue obj = retValue.get_obj(); - BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 12500.0); - BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0); + BOOST_CHECK_EQUAL(obj.find_value("miner").get_real(), 12500.0); + BOOST_CHECK_EQUAL(obj.find_value("founders").get_real(), 0.0); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 653599")); // Blossom activation - 1 obj = retValue.get_obj(); - BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 11875.0); - BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 625.0); + BOOST_CHECK_EQUAL(obj.find_value("miner").get_real(), 11875.0); + BOOST_CHECK_EQUAL(obj.find_value("founders").get_real(), 625.0); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 653600")); // Blossom activation obj = retValue.get_obj(); - BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 11875.0); - BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 625.0); + BOOST_CHECK_EQUAL(obj.find_value("miner").get_real(), 11875.0); + BOOST_CHECK_EQUAL(obj.find_value("founders").get_real(), 625.0); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 1046399")); obj = retValue.get_obj(); - BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 5937.5); - BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 312.5); + BOOST_CHECK_EQUAL(obj.find_value("miner").get_real(), 5937.5); + BOOST_CHECK_EQUAL(obj.find_value("founders").get_real(), 312.5); // slow start + blossom activation + (pre blossom halving - blossom activation) * 2 BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 1046400")); obj = retValue.get_obj(); - BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 5937.5); - BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 312.5); + BOOST_CHECK_EQUAL(obj.find_value("miner").get_real(), 5937.5); + BOOST_CHECK_EQUAL(obj.find_value("founders").get_real(), 312.5); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 2726399")); obj = retValue.get_obj(); - BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 1562.5); - BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0); + BOOST_CHECK_EQUAL(obj.find_value("miner").get_real(), 1562.5); + BOOST_CHECK_EQUAL(obj.find_value("founders").get_real(), 0.0); BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 2726400")); obj = retValue.get_obj(); - BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 1562.5); - BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0); + BOOST_CHECK_EQUAL(obj.find_value("miner").get_real(), 1562.5); + BOOST_CHECK_EQUAL(obj.find_value("founders").get_real(), 0.0); /* * getblock @@ -386,46 +386,46 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_validateaddress) // This address is not valid, it belongs to another network BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress ztaaga95QAPyp1kSQ1hD2kguCpzyMHjxWZqaYDEkzbvo7uYQYAw2S8X4Kx98AvhhofMtQL8PAXKHuZsmhRcanavKRKmdCzk")); UniValue resultObj = retValue.get_obj(); - bool b = find_value(resultObj, "isvalid").get_bool(); + bool b = resultObj.find_value("isvalid").get_bool(); BOOST_CHECK_EQUAL(b, false); // This address is valid, but the spending key is not in this wallet BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress zcfA19SDAKRYHLoRDoShcoz4nPohqWxuHcqg8WAxsiB2jFrrs6k7oSvst3UZvMYqpMNSRBkxBsnyjjngX5L55FxMzLKach8")); resultObj = retValue.get_obj(); - b = find_value(resultObj, "isvalid").get_bool(); + b = resultObj.find_value("isvalid").get_bool(); BOOST_CHECK_EQUAL(b, true); - BOOST_CHECK_EQUAL(find_value(resultObj, "type").get_str(), "sprout"); - b = find_value(resultObj, "ismine").get_bool(); + BOOST_CHECK_EQUAL(resultObj.find_value("type").get_str(), "sprout"); + b = resultObj.find_value("ismine").get_bool(); BOOST_CHECK_EQUAL(b, false); // Let's import a spending key to the wallet and validate its payment address BOOST_CHECK_NO_THROW(CallRPC("z_importkey SKxoWv77WGwFnUJitQKNEcD636bL4X5Gd6wWmgaA4Q9x8jZBPJXT")); BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL")); resultObj = retValue.get_obj(); - b = find_value(resultObj, "isvalid").get_bool(); + b = resultObj.find_value("isvalid").get_bool(); BOOST_CHECK_EQUAL(b, true); - BOOST_CHECK_EQUAL(find_value(resultObj, "type").get_str(), "sprout"); - b = find_value(resultObj, "ismine").get_bool(); + BOOST_CHECK_EQUAL(resultObj.find_value("type").get_str(), "sprout"); + b = resultObj.find_value("ismine").get_bool(); BOOST_CHECK_EQUAL(b, true); - BOOST_CHECK_EQUAL(find_value(resultObj, "payingkey").get_str(), "f5bb3c888ccc9831e3f6ba06e7528e26a312eec3acc1823be8918b6a3a5e20ad"); - BOOST_CHECK_EQUAL(find_value(resultObj, "transmissionkey").get_str(), "7a58c7132446564e6b810cf895c20537b3528357dc00150a8e201f491efa9c1a"); + BOOST_CHECK_EQUAL(resultObj.find_value("payingkey").get_str(), "f5bb3c888ccc9831e3f6ba06e7528e26a312eec3acc1823be8918b6a3a5e20ad"); + BOOST_CHECK_EQUAL(resultObj.find_value("transmissionkey").get_str(), "7a58c7132446564e6b810cf895c20537b3528357dc00150a8e201f491efa9c1a"); // This Sapling address is not valid, it belongs to another network BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress ztestsapling1knww2nyjc62njkard0jmx7hlsj6twxmxwprn7anvrv4dc2zxanl3nemc0qx2hvplxmd2uau8gyw")); resultObj = retValue.get_obj(); - b = find_value(resultObj, "isvalid").get_bool(); + b = resultObj.find_value("isvalid").get_bool(); BOOST_CHECK_EQUAL(b, false); // This Sapling address is valid, but the spending key is not in this wallet BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9slya")); resultObj = retValue.get_obj(); - b = find_value(resultObj, "isvalid").get_bool(); + b = resultObj.find_value("isvalid").get_bool(); BOOST_CHECK_EQUAL(b, true); - BOOST_CHECK_EQUAL(find_value(resultObj, "type").get_str(), "sapling"); - b = find_value(resultObj, "ismine").get_bool(); + BOOST_CHECK_EQUAL(resultObj.find_value("type").get_str(), "sapling"); + b = resultObj.find_value("ismine").get_bool(); BOOST_CHECK_EQUAL(b, false); - BOOST_CHECK_EQUAL(find_value(resultObj, "diversifier").get_str(), "1787997c30e94f050c634d"); - BOOST_CHECK_EQUAL(find_value(resultObj, "diversifiedtransmissionkey").get_str(), "34ed1f60f5db5763beee1ddbb37dd5f7e541d4d4fbdcc09fbfcc6b8e949bbe9d"); + BOOST_CHECK_EQUAL(resultObj.find_value("diversifier").get_str(), "1787997c30e94f050c634d"); + BOOST_CHECK_EQUAL(resultObj.find_value("diversifiedtransmissionkey").get_str(), "34ed1f60f5db5763beee1ddbb37dd5f7e541d4d4fbdcc09fbfcc6b8e949bbe9d"); } BOOST_AUTO_TEST_CASE(rpc_wallet_z_importkey_paymentaddress) { @@ -435,13 +435,13 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importkey_paymentaddress) { auto testAddress = [](const std::string& type, const std::string& key) { UniValue ret; BOOST_CHECK_NO_THROW(ret = CallRPC("z_importkey " + key)); - auto defaultAddr = find_value(ret, "address").get_str(); - BOOST_CHECK_EQUAL(type, find_value(ret, "type").get_str()); + auto defaultAddr = ret.find_value("address").get_str(); + BOOST_CHECK_EQUAL(type, ret.find_value("type").get_str()); BOOST_CHECK_NO_THROW(ret = CallRPC("z_validateaddress " + defaultAddr)); ret = ret.get_obj(); - BOOST_CHECK_EQUAL(true, find_value(ret, "isvalid").get_bool()); - BOOST_CHECK_EQUAL(true, find_value(ret, "ismine").get_bool()); - BOOST_CHECK_EQUAL(type, find_value(ret, "type").get_str()); + BOOST_CHECK_EQUAL(true, ret.find_value("isvalid").get_bool()); + BOOST_CHECK_EQUAL(true, ret.find_value("ismine").get_bool()); + BOOST_CHECK_EQUAL(type, ret.find_value("type").get_str()); }; testAddress("sapling", "secret-extended-key-main1qya4wae0qqqqqqpxfq3ukywunn" @@ -960,7 +960,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_getoperations) for (UniValue v : array.getValues()) { UniValue obj = v.get_obj(); - UniValue id = find_value(obj, "id"); + UniValue id = obj.find_value("id"); UniValue result; // removes result from internal storage @@ -969,7 +969,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_getoperations) BOOST_CHECK(resultArray.size() == 1); UniValue resultObj = resultArray[0].get_obj(); - UniValue resultId = find_value(resultObj, "id"); + UniValue resultId = resultObj.find_value("id"); BOOST_CHECK_EQUAL(id.get_str(), resultId.get_str()); // verify the operation has been removed @@ -1103,11 +1103,11 @@ BOOST_AUTO_TEST_CASE(asyncrpcoperation_sign_send_raw_transaction) { // Raw joinsplit is a zaddr->zaddr std::string raw = "020000000000000000000100000000000000001027000000000000183a0d4c46c369078705e39bcfebee59a978dbd210ce8de3efc9555a03fbabfd3cea16693d730c63850d7e48ccde79854c19adcb7e9dcd7b7d18805ee09083f6b16e1860729d2d4a90e2f2acd009cf78b5eb0f4a6ee4bdb64b1262d7ce9eb910c460b02022991e968d0c50ee44908e4ccccbc591d0053bcca154dd6d6fc400a29fa686af4682339832ccea362a62aeb9df0d5aa74f86a1e75ac0f48a8ccc41e0a940643c6c33e1d09223b0a46eaf47a1bb4407cfc12b1dcf83a29c0cef51e45c7876ca5b9e5bae86d92976eb3ef68f29cd29386a8be8451b50f82bf9da10c04651868655194da8f6ed3d241bb5b5ff93a3e2bbe44644544d88bcde5cc35978032ee92699c7a61fcbb395e7583f47e698c4d53ede54f956629400bf510fb5e22d03158cc10bdcaaf29e418ef18eb6480dd9c8b9e2a377809f9f32a556ef872febd0021d4ad013aa9f0b7255e98e408d302abefd33a71180b720271835b487ab309e160b06dfe51932120fb84a7ede16b20c53599a11071592109e10260f265ee60d48c62bfe24074020e9b586ce9e9356e68f2ad1a9538258234afe4b83a209f178f45202270eaeaeecaf2ce3100b2c5a714f75f35777a9ebff5ebf47059d2bbf6f3726190216468f2b152673b766225b093f3a2f827c86d6b48b42117fec1d0ac38dd7af700308dcfb02eba821612b16a2c164c47715b9b0c93900893b1aba2ea03765c94d87022db5be06ab338d1912e0936dfe87586d0a8ee49144a6cd2e306abdcb652faa3e0222739deb23154d778b50de75069a4a2cce1208cd1ced3cb4744c9888ce1c2fcd2e66dc31e62d3aa9e423d7275882525e9981f92e84ac85975b8660739407efbe1e34c2249420fde7e17db3096d5b22e83d051d01f0e6e7690dca7d168db338aadf0897fedac10de310db2b1bff762d322935dddbb60c2efb8b15d231fa17b84630371cb275c209f0c4c7d0c68b150ea5cd514122215e3f7fcfb351d69514788d67c2f3c8922581946e3a04bdf1f07f15696ca76eb95b10698bf1188fd882945c57657515889d042a6fc45d38cbc943540c4f0f6d1c45a1574c81f3e42d1eb8702328b729909adee8a5cfed7c79d54627d1fd389af941d878376f7927b9830ca659bf9ab18c5ca5192d52d02723008728d03701b8ab3e1c4a3109409ec0b13df334c7deec3523eeef4c97b5603e643de3a647b873f4c1b47fbfc6586ba66724f112e51fc93839648005043620aa3ce458e246d77977b19c53d98e3e812de006afc1a79744df236582943631d04cc02941ac4be500e4ed9fb9e3e7cc187b1c4050fad1d9d09d5fd70d5d01d615b439d8c0015d2eb10398bcdbf8c4b2bd559dbe4c288a186aed3f86f608da4d582e120c4a896e015e2241900d1daeccd05db968852677c71d752bec46de9962174b46f980e8cc603654daf8b98a3ee92dac066033954164a89568b70b1780c2ce2410b2f816dbeddb2cd463e0c8f21a52cf6427d9647a6fd4bafa8fb4cd4d47ac057b0160bee86c6b2fb8adce214c2bcdda277512200adf0eaa5d2114a2c077b009836a68ec254bfe56f51d147b9afe2ddd9cb917c0c2de19d81b7b8fd9f4574f51fa1207630dc13976f4d7587c962f761af267de71f3909a576e6bedaf6311633910d291ac292c467cc8331ef577aef7646a5d949322fa0367a49f20597a13def53136ee31610395e3e48d291fd8f58504374031fe9dcfba5e06086ebcf01a9106f6a4d6e16e19e4c5bb893f7da79419c94eca31a384be6fa1747284dee0fc3bbc8b1b860172c10b29c1594bb8c747d7fe05827358ff2160f49050001625ffe2e880bd7fc26cd0ffd89750745379a8e862816e08a5a2008043921ab6a4976064ac18f7ee37b6628bc0127d8d5ebd3548e41d8881a082d86f20b32e33094f15a0e6ea6074b08c6cd28142de94713451640a55985051f5577eb54572699d838cb34a79c8939e981c0c277d06a6e2ce69ccb74f8a691ff08f81d8b99e6a86223d29a2b7c8e7b041aba44ea678ae654277f7e91cbfa79158b989164a3d549d9f4feb0cc43169699c13e321fe3f4b94258c75d198ff9184269cd6986c55409e07528c93f64942c6c283ce3917b4bf4c3be2fe3173c8c38cccb35f1fbda0ca88b35a599c0678cb22aa8eabea8249dbd2e4f849fffe69803d299e435ebcd7df95854003d8eda17a74d98b4be0e62d45d7fe48c06a6f464a14f8e0570077cc631279092802a89823f031eef5e1028a6d6fdbd502869a731ee7d28b4d6c71b419462a30d31442d3ee444ffbcbd16d558c9000c97e949c2b1f9d6f6d8db7b9131ebd963620d3fc8595278d6f8fdf49084325373196d53e64142fa5a23eccd6ef908c4d80b8b3e6cc334b7f7012103a3682e4678e9b518163d262a39a2c1a69bf88514c52b7ccd7cc8dc80e71f7c2ec0701cff982573eb0c2c4daeb47fa0b586f4451c10d1da2e5d182b03dd067a5e971b3a6138ca6667aaf853d2ac03b80a1d5870905f2cfb6c78ec3c3719c02f973d638a0f973424a2b0f2b0023f136d60092fe15fba4bc180b9176bd0ff576e053f1af6939fe9ca256203ffaeb3e569f09774d2a6cbf91873e56651f4d6ff77e0b5374b0a1a201d7e523604e0247644544cc571d48c458a4f96f45580b"; UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("rawtxn", raw)); + obj.pushKV("rawtxn", raw); // Verify test mode is returning output (since no input taddrs, signed and unsigned are the same). std::pair txAndResult = SignSendRawTransaction(obj, std::nullopt, true); UniValue resultObj = txAndResult.second.get_obj(); - std::string hex = find_value(resultObj, "hex").get_str(); + std::string hex = resultObj.find_value("hex").get_str(); BOOST_CHECK_EQUAL(hex, raw); } @@ -1340,7 +1340,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) auto consensusParams = Params().GetConsensus(); retValue = CallRPC("getblockcount"); - int nextBlockHeight = retValue.get_int() + 1; + int nextBlockHeight = retValue.getInt() + 1; // Add a fake transaction to the wallet CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight); @@ -2061,8 +2061,8 @@ void TestWTxStatus(const Consensus::Params consensusParams, const int delta) { for(int i=0; i<=delta + 1; i++) { auto retObj = FakeMine(i, false); - BOOST_CHECK_EQUAL(find_value(retObj, "confirmations").get_real(), -1); - auto status = find_value(retObj, "status").get_str(); + BOOST_CHECK_EQUAL(retObj.find_value("confirmations").get_real(), -1); + auto status = retObj.find_value("status").get_str(); if (i >= delta - TX_EXPIRING_SOON_THRESHOLD && i <= delta) BOOST_CHECK_EQUAL(status, "expiringsoon"); else if (i >= delta - TX_EXPIRING_SOON_THRESHOLD) @@ -2074,8 +2074,8 @@ void TestWTxStatus(const Consensus::Params consensusParams, const int delta) { // Now mine including the transaction, check status auto retObj = FakeMine(delta + 2, true); - BOOST_CHECK_EQUAL(find_value(retObj, "confirmations").get_real(), 1); - BOOST_CHECK_EQUAL(find_value(retObj, "status").get_str(), "mined"); + BOOST_CHECK_EQUAL(retObj.find_value("confirmations").get_real(), 1); + BOOST_CHECK_EQUAL(retObj.find_value("status").get_str(), "mined"); // Cleanup chainActive.SetTip(NULL); From 84d17b44ffa4bb1afd5f2a67c61b644d00a7201e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:04:26 +0000 Subject: [PATCH 006/146] Update leveldb subtree to latest upstream --- Makefile.am | 6 - configure.ac | 34 +- src/Makefile.am | 24 +- src/Makefile.leveldb.include | 152 +++ src/dbwrapper.cpp | 2 +- src/leveldb/.appveyor.yml | 35 + src/leveldb/.clang-format | 18 + src/leveldb/.gitignore | 21 +- src/leveldb/.travis.yml | 82 ++ src/leveldb/CMakeLists.txt | 465 +++++++ src/leveldb/CONTRIBUTING.md | 4 +- src/leveldb/Makefile | 227 ---- src/leveldb/README | 51 - src/leveldb/README.md | 135 +- src/leveldb/WINDOWS.md | 39 - src/leveldb/{db => benchmarks}/db_bench.cc | 257 ++-- .../bench => benchmarks}/db_bench_sqlite3.cc | 184 ++- .../bench => benchmarks}/db_bench_tree_db.cc | 130 +- src/leveldb/build_detect_platform | 222 ---- src/leveldb/cmake/leveldbConfig.cmake | 1 + src/leveldb/db/autocompact_test.cc | 36 +- src/leveldb/db/builder.cc | 25 +- src/leveldb/db/builder.h | 8 +- src/leveldb/db/c.cc | 389 +++--- src/leveldb/db/c_test.c | 36 +- src/leveldb/db/corruption_test.cc | 100 +- src/leveldb/db/db_impl.cc | 710 ++++++----- src/leveldb/db/db_impl.h | 154 +-- src/leveldb/db/db_iter.cc | 93 +- src/leveldb/db/db_iter.h | 12 +- src/leveldb/db/db_test.cc | 748 ++++++----- src/leveldb/db/dbformat.cc | 43 +- src/leveldb/db/dbformat.h | 80 +- src/leveldb/db/dbformat_test.cc | 95 +- src/leveldb/db/dumpfile.cc | 29 +- src/leveldb/db/fault_injection_test.cc | 552 ++++++++ src/leveldb/db/filename.cc | 37 +- src/leveldb/db/filename.h | 31 +- src/leveldb/db/filename_test.cc | 86 +- .../db/{leveldb_main.cc => leveldbutil.cc} | 20 +- src/leveldb/db/log_format.h | 2 +- src/leveldb/db/log_reader.cc | 50 +- src/leveldb/db/log_reader.h | 42 +- src/leveldb/db/log_test.cc | 344 ++--- src/leveldb/db/log_writer.cc | 38 +- src/leveldb/db/log_writer.h | 20 +- src/leveldb/db/memtable.cc | 70 +- src/leveldb/db/memtable.h | 28 +- src/leveldb/db/recovery_test.cc | 330 +++++ src/leveldb/db/repair.cc | 95 +- src/leveldb/db/skiplist.h | 190 ++- src/leveldb/db/skiplist_test.cc | 65 +- src/leveldb/db/snapshot.h | 79 +- src/leveldb/db/table_cache.cc | 47 +- src/leveldb/db/table_cache.h | 33 +- src/leveldb/db/version_edit.cc | 55 +- src/leveldb/db/version_edit.h | 27 +- src/leveldb/db/version_edit_test.cc | 6 +- src/leveldb/db/version_set.cc | 604 +++++---- src/leveldb/db/version_set.h | 127 +- src/leveldb/db/version_set_test.cc | 243 +++- src/leveldb/db/write_batch.cc | 23 +- src/leveldb/db/write_batch_internal.h | 10 +- src/leveldb/db/write_batch_test.cc | 73 +- src/leveldb/doc/benchmark.html | 6 +- src/leveldb/doc/doc.css | 89 -- src/leveldb/doc/impl.html | 213 ---- src/leveldb/doc/impl.md | 172 +++ src/leveldb/doc/index.html | 549 -------- src/leveldb/doc/index.md | 523 ++++++++ src/leveldb/doc/log_format.md | 75 ++ src/leveldb/doc/log_format.txt | 75 -- src/leveldb/doc/table_format.md | 107 ++ src/leveldb/doc/table_format.txt | 104 -- src/leveldb/helpers/memenv/memenv.cc | 205 +-- src/leveldb/helpers/memenv/memenv.h | 4 +- src/leveldb/helpers/memenv/memenv_test.cc | 73 +- src/leveldb/include/leveldb/c.h | 320 +++-- src/leveldb/include/leveldb/cache.h | 32 +- src/leveldb/include/leveldb/comparator.h | 11 +- src/leveldb/include/leveldb/db.h | 60 +- src/leveldb/include/leveldb/dumpfile.h | 5 +- src/leveldb/include/leveldb/env.h | 237 ++-- src/leveldb/include/leveldb/export.h | 33 + src/leveldb/include/leveldb/filter_policy.h | 12 +- src/leveldb/include/leveldb/iterator.h | 36 +- src/leveldb/include/leveldb/options.h | 110 +- src/leveldb/include/leveldb/slice.h | 38 +- src/leveldb/include/leveldb/status.h | 62 +- src/leveldb/include/leveldb/table.h | 33 +- src/leveldb/include/leveldb/table_builder.h | 11 +- src/leveldb/include/leveldb/write_batch.h | 37 +- src/leveldb/issues/issue178_test.cc | 12 +- src/leveldb/issues/issue200_test.cc | 10 +- src/leveldb/issues/issue320_test.cc | 128 ++ src/leveldb/port/{README => README.md} | 2 +- src/leveldb/port/atomic_pointer.h | 223 ---- src/leveldb/port/port.h | 8 +- src/leveldb/port/port_config.h.in | 39 + src/leveldb/port/port_example.h | 69 +- src/leveldb/port/port_posix.cc | 54 - src/leveldb/port/port_posix.h | 158 --- src/leveldb/port/port_stdcxx.h | 153 +++ src/leveldb/port/port_win.cc | 147 --- src/leveldb/port/port_win.h | 174 --- src/leveldb/port/thread_annotations.h | 78 +- src/leveldb/port/win/stdint.h | 24 - src/leveldb/table/block.cc | 69 +- src/leveldb/table/block.h | 16 +- src/leveldb/table/block_builder.cc | 23 +- src/leveldb/table/block_builder.h | 26 +- src/leveldb/table/filter_block.cc | 25 +- src/leveldb/table/filter_block.h | 21 +- src/leveldb/table/filter_block_test.cc | 38 +- src/leveldb/table/format.cc | 24 +- src/leveldb/table/format.h | 42 +- src/leveldb/table/iterator.cc | 75 +- src/leveldb/table/iterator_wrapper.h | 61 +- src/leveldb/table/merger.cc | 52 +- src/leveldb/table/merger.h | 4 +- src/leveldb/table/table.cc | 68 +- src/leveldb/table/table_builder.cc | 61 +- src/leveldb/table/table_test.cc | 333 +++-- src/leveldb/table/two_level_iterator.cc | 91 +- src/leveldb/table/two_level_iterator.h | 11 +- src/leveldb/util/arena.cc | 18 +- src/leveldb/util/arena.h | 29 +- src/leveldb/util/arena_test.cc | 19 +- src/leveldb/util/bloom.cc | 29 +- src/leveldb/util/bloom_test.cc | 59 +- src/leveldb/util/cache.cc | 231 ++-- src/leveldb/util/cache_test.cc | 96 +- src/leveldb/util/coding.cc | 84 +- src/leveldb/util/coding.h | 136 +- src/leveldb/util/coding_test.cc | 42 +- src/leveldb/util/comparator.cc | 40 +- src/leveldb/util/crc32c.cc | 646 +++++----- src/leveldb/util/crc32c.h | 6 +- src/leveldb/util/crc32c_test.cc | 31 +- src/leveldb/util/env.cc | 28 +- src/leveldb/util/env_posix.cc | 1110 +++++++++++------ src/leveldb/util/env_posix_test.cc | 350 ++++++ src/leveldb/util/env_posix_test_helper.h | 28 + src/leveldb/util/env_test.cc | 241 +++- src/leveldb/util/env_win.cc | 873 ------------- src/leveldb/util/env_windows.cc | 849 +++++++++++++ src/leveldb/util/env_windows_test.cc | 64 + src/leveldb/util/env_windows_test_helper.h | 25 + src/leveldb/util/filter_policy.cc | 2 +- src/leveldb/util/hash.cc | 15 +- src/leveldb/util/hash.h | 4 +- src/leveldb/util/hash_test.cc | 32 +- src/leveldb/util/histogram.cc | 207 ++- src/leveldb/util/histogram.h | 20 +- src/leveldb/util/logging.cc | 52 +- src/leveldb/util/logging.h | 14 +- src/leveldb/util/logging_test.cc | 143 +++ src/leveldb/util/mutexlock.h | 12 +- src/leveldb/util/no_destructor.h | 46 + src/leveldb/util/no_destructor_test.cc | 47 + src/leveldb/util/options.cc | 17 +- src/leveldb/util/posix_logger.h | 168 ++- src/leveldb/util/random.h | 9 +- src/leveldb/util/status.cc | 14 +- src/leveldb/util/status_test.cc | 40 + src/leveldb/util/testharness.cc | 18 +- src/leveldb/util/testharness.h | 85 +- src/leveldb/util/testutil.cc | 12 +- src/leveldb/util/testutil.h | 33 +- src/leveldb/util/windows_logger.h | 124 ++ 170 files changed, 11214 insertions(+), 8459 deletions(-) create mode 100644 src/Makefile.leveldb.include create mode 100644 src/leveldb/.appveyor.yml create mode 100644 src/leveldb/.clang-format create mode 100644 src/leveldb/.travis.yml create mode 100644 src/leveldb/CMakeLists.txt delete mode 100644 src/leveldb/Makefile delete mode 100644 src/leveldb/README delete mode 100644 src/leveldb/WINDOWS.md rename src/leveldb/{db => benchmarks}/db_bench.cc (82%) rename src/leveldb/{doc/bench => benchmarks}/db_bench_sqlite3.cc (83%) rename src/leveldb/{doc/bench => benchmarks}/db_bench_tree_db.cc (85%) delete mode 100755 src/leveldb/build_detect_platform create mode 100644 src/leveldb/cmake/leveldbConfig.cmake create mode 100644 src/leveldb/db/fault_injection_test.cc rename src/leveldb/db/{leveldb_main.cc => leveldbutil.cc} (71%) create mode 100644 src/leveldb/db/recovery_test.cc delete mode 100644 src/leveldb/doc/doc.css delete mode 100644 src/leveldb/doc/impl.html create mode 100644 src/leveldb/doc/impl.md delete mode 100644 src/leveldb/doc/index.html create mode 100644 src/leveldb/doc/index.md create mode 100644 src/leveldb/doc/log_format.md delete mode 100644 src/leveldb/doc/log_format.txt create mode 100644 src/leveldb/doc/table_format.md delete mode 100644 src/leveldb/doc/table_format.txt create mode 100644 src/leveldb/include/leveldb/export.h create mode 100644 src/leveldb/issues/issue320_test.cc rename src/leveldb/port/{README => README.md} (82%) delete mode 100644 src/leveldb/port/atomic_pointer.h create mode 100644 src/leveldb/port/port_config.h.in delete mode 100644 src/leveldb/port/port_posix.cc delete mode 100644 src/leveldb/port/port_posix.h create mode 100644 src/leveldb/port/port_stdcxx.h delete mode 100644 src/leveldb/port/port_win.cc delete mode 100644 src/leveldb/port/port_win.h delete mode 100644 src/leveldb/port/win/stdint.h create mode 100644 src/leveldb/util/env_posix_test.cc create mode 100644 src/leveldb/util/env_posix_test_helper.h delete mode 100644 src/leveldb/util/env_win.cc create mode 100644 src/leveldb/util/env_windows.cc create mode 100644 src/leveldb/util/env_windows_test.cc create mode 100644 src/leveldb/util/env_windows_test_helper.h create mode 100644 src/leveldb/util/logging_test.cc create mode 100644 src/leveldb/util/no_destructor.h create mode 100644 src/leveldb/util/no_destructor_test.cc create mode 100644 src/leveldb/util/status_test.cc create mode 100644 src/leveldb/util/windows_logger.h diff --git a/Makefile.am b/Makefile.am index e163253bb..310135cff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,15 +34,9 @@ COVERAGE_INFO = baseline_filtered_combined.info baseline.info \ bitcoinz-gtest.info bitcoinz-gtest_filtered.info bitcoinz-gtest_coverage.info dist-hook: - -$(MAKE) -C $(top_distdir)/src/leveldb clean -$(MAKE) -C $(top_distdir)/src/secp256k1 distclean -$(GIT) archive --format=tar HEAD -- src/clientversion.cpp | $(AMTAR) -C $(top_distdir) -xf - -distcheck-hook: - $(MKDIR_P) $(top_distdir)/_build/src/leveldb - cp -rf $(top_srcdir)/src/leveldb/* $(top_distdir)/_build/src/leveldb/ - -$(MAKE) -C $(top_distdir)/_build/src/leveldb clean - distcleancheck: @: diff --git a/configure.ac b/configure.ac index 68e792730..27c1e152c 100644 --- a/configure.ac +++ b/configure.ac @@ -347,7 +347,6 @@ case $host in fi CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB" - LEVELDB_TARGET_FLAGS="TARGET_OS=OS_WINDOWS_CROSSCOMPILE" if test "x$CXXFLAGS_overridden" = "xno"; then CXXFLAGS="$CXXFLAGS -w" fi @@ -369,7 +368,6 @@ case $host in ;; *darwin*) TARGET_OS=darwin - LEVELDB_TARGET_FLAGS="TARGET_OS=Darwin" if test x$cross_compiling != xyes; then BUILD_OS=darwin AC_CHECK_PROG([PORT],port, port) @@ -628,17 +626,34 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([ ] ) +dnl LevelDB platform checks +AC_MSG_CHECKING(for fdatasync) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ fdatasync(0); ]])], + [ AC_MSG_RESULT(yes); HAVE_FDATASYNC=1 ], + [ AC_MSG_RESULT(no); HAVE_FDATASYNC=0 ] +) + +AC_MSG_CHECKING(for F_FULLFSYNC) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ fcntl(0, F_FULLFSYNC, 0); ]])], + [ AC_MSG_RESULT(yes); HAVE_FULLFSYNC=1 ], + [ AC_MSG_RESULT(no); HAVE_FULLFSYNC=0 ] +) + +AC_MSG_CHECKING(for O_CLOEXEC) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[ open("", O_CLOEXEC); ]])], + [ AC_MSG_RESULT(yes); HAVE_O_CLOEXEC=1 ], + [ AC_MSG_RESULT(no); HAVE_O_CLOEXEC=0 ] +) + if test x$use_reduce_exports = xyes; then AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], [AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])]) fi -LEVELDB_CPPFLAGS= -LIBLEVELDB= LIBMEMENV= -AM_CONDITIONAL([EMBEDDED_LEVELDB],[true]) -AC_SUBST(LEVELDB_CPPFLAGS) -AC_SUBST(LIBLEVELDB) AC_SUBST(LIBMEMENV) if test x$enable_wallet != xno; then @@ -882,6 +897,7 @@ AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes]) AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) +AM_CONDITIONAL([WORDS_BIGENDIAN], [test x$ac_cv_c_bigendian = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) @@ -915,11 +931,13 @@ AC_SUBST(SANITIZER_LDFLAGS) AC_SUBST(LIBTOOL_APP_LDFLAGS) AC_SUBST(BOOST_LIBS) AC_SUBST(TESTDEFS) -AC_SUBST(LEVELDB_TARGET_FLAGS) AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) AC_SUBST(LIBZCASH_LIBS) +AC_SUBST(HAVE_FDATASYNC) +AC_SUBST(HAVE_FULLFSYNC) +AC_SUBST(HAVE_O_CLOEXEC) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh]) AC_CONFIG_FILES([qa/pull-tester/tests-config.sh],[chmod +x qa/pull-tester/tests-config.sh]) diff --git a/src/Makefile.am b/src/Makefile.am index b7d7665ec..2a9683023 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,21 +12,6 @@ AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_C AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES = -if EMBEDDED_LEVELDB -LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include -LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv -LIBLEVELDB += $(builddir)/leveldb/libleveldb.a -LIBMEMENV += $(builddir)/leveldb/libmemenv.a - -# NOTE: This dependency is not strictly necessary, but without it make may try to build both in parallel, which breaks the LevelDB build system in a race -$(LIBLEVELDB): $(LIBMEMENV) - -$(LIBLEVELDB) $(LIBMEMENV): - @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ - CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ - OPT="$(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS" -endif - BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) @@ -547,16 +532,15 @@ CTAES_DIST += crypto/ctaes/ctaes.h CTAES_DIST += crypto/ctaes/README.md CTAES_DIST += crypto/ctaes/test.c -CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno */*.gcno wallet/*/*.gcno +CLEANFILES = *.gcda *.gcno */*.gcno wallet/*/*.gcno DISTCLEANFILES = obj/build.h -EXTRA_DIST = leveldb $(CTAES_DIST) +EXTRA_DIST = $(CTAES_DIST) clean-local: - -$(MAKE) -C leveldb clean -$(MAKE) -C secp256k1 clean - rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno + -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno rm -f fuzz.cpp rm -rf fuzzing/*/output -rm -f config.h @@ -585,6 +569,8 @@ endif @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $( #include #include -#include +#include #include #include diff --git a/src/leveldb/.appveyor.yml b/src/leveldb/.appveyor.yml new file mode 100644 index 000000000..c24b17e80 --- /dev/null +++ b/src/leveldb/.appveyor.yml @@ -0,0 +1,35 @@ +# Build matrix / environment variables are explained on: +# https://www.appveyor.com/docs/appveyor-yml/ +# This file can be validated on: https://ci.appveyor.com/tools/validate-yaml + +version: "{build}" + +environment: + matrix: + # AppVeyor currently has no custom job name feature. + # http://help.appveyor.com/discussions/questions/1623-can-i-provide-a-friendly-name-for-jobs + - JOB: Visual Studio 2017 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + CMAKE_GENERATOR: Visual Studio 15 2017 + +platform: + - x86 + - x64 + +configuration: + - RelWithDebInfo + - Debug + +build_script: + - git submodule update --init --recursive + - mkdir build + - cd build + - if "%platform%"=="x64" set CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64 + - cmake --version + - cmake .. -G "%CMAKE_GENERATOR%" + -DCMAKE_CONFIGURATION_TYPES="%CONFIGURATION%" + - cmake --build . --config "%CONFIGURATION%" + - cd .. + +test_script: + - cd build && ctest --verbose --build-config "%CONFIGURATION%" && cd .. diff --git a/src/leveldb/.clang-format b/src/leveldb/.clang-format new file mode 100644 index 000000000..f493f7538 --- /dev/null +++ b/src/leveldb/.clang-format @@ -0,0 +1,18 @@ +# Run manually to reformat a file: +# clang-format -i --style=file +# find . -iname '*.cc' -o -iname '*.h' -o -iname '*.h.in' | xargs clang-format -i --style=file +BasedOnStyle: Google +DerivePointerAlignment: false + +# Public headers are in a different location in the internal Google repository. +# Order them so that when imported to the authoritative repository they will be +# in correct alphabetical order. +IncludeCategories: + - Regex: '^(<|"(benchmarks|db|helpers)/)' + Priority: 1 + - Regex: '^"(leveldb)/' + Priority: 2 + - Regex: '^(<|"(issues|port|table|third_party|util)/)' + Priority: 3 + - Regex: '.*' + Priority: 4 diff --git a/src/leveldb/.gitignore b/src/leveldb/.gitignore index 71d87a4ee..c4b242534 100644 --- a/src/leveldb/.gitignore +++ b/src/leveldb/.gitignore @@ -1,13 +1,8 @@ -build_config.mk -*.a -*.o -*.dylib* -*.so -*.so.* -*_test -db_bench -leveldbutil -Release -Debug -Benchmark -vs2010.* +# Editors. +*.sw* +.vscode +.DS_Store + +# Build directory. +build/ +out/ diff --git a/src/leveldb/.travis.yml b/src/leveldb/.travis.yml new file mode 100644 index 000000000..42cbe64fd --- /dev/null +++ b/src/leveldb/.travis.yml @@ -0,0 +1,82 @@ +# Build matrix / environment variables are explained on: +# http://about.travis-ci.org/docs/user/build-configuration/ +# This file can be validated on: http://lint.travis-ci.org/ + +language: cpp +dist: bionic +osx_image: xcode10.3 + +compiler: +- gcc +- clang +os: +- linux +- osx + +env: +- BUILD_TYPE=Debug +- BUILD_TYPE=RelWithDebInfo + +addons: + apt: + sources: + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntu-toolchain-r/test' + packages: + - clang-9 + - cmake + - gcc-9 + - g++-9 + - libgoogle-perftools-dev + - libkyotocabinet-dev + - libsnappy-dev + - libsqlite3-dev + - ninja-build + homebrew: + packages: + - cmake + - crc32c + - gcc@9 + - gperftools + - kyoto-cabinet + - llvm@9 + - ninja + - snappy + - sqlite3 + update: true + +install: +# The following Homebrew packages aren't linked by default, and need to be +# prepended to the path explicitly. +- if [ "$TRAVIS_OS_NAME" = "osx" ]; then + export PATH="$(brew --prefix llvm)/bin:$PATH"; + fi +# /usr/bin/gcc points to an older compiler on both Linux and macOS. +- if [ "$CXX" = "g++" ]; then export CXX="g++-9" CC="gcc-9"; fi +# /usr/bin/clang points to an older compiler on both Linux and macOS. +# +# Homebrew's llvm package doesn't ship a versioned clang++ binary, so the values +# below don't work on macOS. Fortunately, the path change above makes the +# default values (clang and clang++) resolve to the correct compiler on macOS. +- if [ "$TRAVIS_OS_NAME" = "linux" ]; then + if [ "$CXX" = "clang++" ]; then export CXX="clang++-9" CC="clang-9"; fi; + fi +- echo ${CC} +- echo ${CXX} +- ${CXX} --version +- cmake --version + +before_script: +- mkdir -p build && cd build +- cmake .. -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE + -DCMAKE_INSTALL_PREFIX=$HOME/.local +- cmake --build . +- cd .. + +script: +- cd build && ctest --verbose && cd .. +- "if [ -f build/db_bench ] ; then build/db_bench ; fi" +- "if [ -f build/db_bench_sqlite3 ] ; then build/db_bench_sqlite3 ; fi" +- "if [ -f build/db_bench_tree_db ] ; then build/db_bench_tree_db ; fi" +- cd build && cmake --build . --target install diff --git a/src/leveldb/CMakeLists.txt b/src/leveldb/CMakeLists.txt new file mode 100644 index 000000000..1cb46256c --- /dev/null +++ b/src/leveldb/CMakeLists.txt @@ -0,0 +1,465 @@ +# Copyright 2017 The LevelDB Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. See the AUTHORS file for names of contributors. + +cmake_minimum_required(VERSION 3.9) +# Keep the version below in sync with the one in db.h +project(leveldb VERSION 1.22.0 LANGUAGES C CXX) + +# This project can use C11, but will gracefully decay down to C89. +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED OFF) +set(CMAKE_C_EXTENSIONS OFF) + +# This project requires C++11. +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +if (WIN32) + set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_WINDOWS) + # TODO(cmumford): Make UNICODE configurable for Windows. + add_definitions(-D_UNICODE -DUNICODE) +else (WIN32) + set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_POSIX) +endif (WIN32) + +option(LEVELDB_BUILD_TESTS "Build LevelDB's unit tests" ON) +option(LEVELDB_BUILD_BENCHMARKS "Build LevelDB's benchmarks" ON) +option(LEVELDB_INSTALL "Install LevelDB's header and library" ON) + +include(TestBigEndian) +test_big_endian(LEVELDB_IS_BIG_ENDIAN) + +include(CheckIncludeFile) +check_include_file("unistd.h" HAVE_UNISTD_H) + +include(CheckLibraryExists) +check_library_exists(crc32c crc32c_value "" HAVE_CRC32C) +check_library_exists(snappy snappy_compress "" HAVE_SNAPPY) +check_library_exists(tcmalloc malloc "" HAVE_TCMALLOC) + +include(CheckCXXSymbolExists) +# Using check_cxx_symbol_exists() instead of check_c_symbol_exists() because +# we're including the header from C++, and feature detection should use the same +# compiler language that the project will use later. Principles aside, some +# versions of do not expose fdatasync() in in standard C mode +# (-std=c11), but do expose the function in standard C++ mode (-std=c++11). +check_cxx_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC) +check_cxx_symbol_exists(F_FULLFSYNC "fcntl.h" HAVE_FULLFSYNC) +check_cxx_symbol_exists(O_CLOEXEC "fcntl.h" HAVE_O_CLOEXEC) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Disable C++ exceptions. + string(REGEX REPLACE "/EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHs-c-") + add_definitions(-D_HAS_EXCEPTIONS=0) + + # Disable RTTI. + string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-") +else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Enable strict prototype warnings for C code in clang and gcc. + if(NOT CMAKE_C_FLAGS MATCHES "-Wstrict-prototypes") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes") + endif(NOT CMAKE_C_FLAGS MATCHES "-Wstrict-prototypes") + + # Disable C++ exceptions. + string(REGEX REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") + + # Disable RTTI. + string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + +# Test whether -Wthread-safety is available. See +# https://clang.llvm.org/docs/ThreadSafetyAnalysis.html +include(CheckCXXCompilerFlag) +check_cxx_compiler_flag(-Wthread-safety HAVE_CLANG_THREAD_SAFETY) + +include(CheckCXXSourceCompiles) + +# Test whether C++17 __has_include is available. +check_cxx_source_compiles(" +#if defined(__has_include) && __has_include() +#include +#endif +int main() { std::string str; return 0; } +" HAVE_CXX17_HAS_INCLUDE) + +set(LEVELDB_PUBLIC_INCLUDE_DIR "include/leveldb") +set(LEVELDB_PORT_CONFIG_DIR "include/port") + +configure_file( + "port/port_config.h.in" + "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" +) + +include_directories( + "${PROJECT_BINARY_DIR}/include" + "." +) + +if(BUILD_SHARED_LIBS) + # Only export LEVELDB_EXPORT symbols from the shared library. + add_compile_options(-fvisibility=hidden) +endif(BUILD_SHARED_LIBS) + +# Must be included before CMAKE_INSTALL_INCLUDEDIR is used. +include(GNUInstallDirs) + +add_library(leveldb "") +target_sources(leveldb + PRIVATE + "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" + "db/builder.cc" + "db/builder.h" + "db/c.cc" + "db/db_impl.cc" + "db/db_impl.h" + "db/db_iter.cc" + "db/db_iter.h" + "db/dbformat.cc" + "db/dbformat.h" + "db/dumpfile.cc" + "db/filename.cc" + "db/filename.h" + "db/log_format.h" + "db/log_reader.cc" + "db/log_reader.h" + "db/log_writer.cc" + "db/log_writer.h" + "db/memtable.cc" + "db/memtable.h" + "db/repair.cc" + "db/skiplist.h" + "db/snapshot.h" + "db/table_cache.cc" + "db/table_cache.h" + "db/version_edit.cc" + "db/version_edit.h" + "db/version_set.cc" + "db/version_set.h" + "db/write_batch_internal.h" + "db/write_batch.cc" + "port/port_stdcxx.h" + "port/port.h" + "port/thread_annotations.h" + "table/block_builder.cc" + "table/block_builder.h" + "table/block.cc" + "table/block.h" + "table/filter_block.cc" + "table/filter_block.h" + "table/format.cc" + "table/format.h" + "table/iterator_wrapper.h" + "table/iterator.cc" + "table/merger.cc" + "table/merger.h" + "table/table_builder.cc" + "table/table.cc" + "table/two_level_iterator.cc" + "table/two_level_iterator.h" + "util/arena.cc" + "util/arena.h" + "util/bloom.cc" + "util/cache.cc" + "util/coding.cc" + "util/coding.h" + "util/comparator.cc" + "util/crc32c.cc" + "util/crc32c.h" + "util/env.cc" + "util/filter_policy.cc" + "util/hash.cc" + "util/hash.h" + "util/logging.cc" + "util/logging.h" + "util/mutexlock.h" + "util/no_destructor.h" + "util/options.cc" + "util/random.h" + "util/status.cc" + + # Only CMake 3.3+ supports PUBLIC sources in targets exported by "install". + $<$:PUBLIC> + "${LEVELDB_PUBLIC_INCLUDE_DIR}/c.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/cache.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/comparator.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/db.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/dumpfile.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/env.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/export.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/filter_policy.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/iterator.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/options.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/slice.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/status.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/table_builder.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/table.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/write_batch.h" +) + +if (WIN32) + target_sources(leveldb + PRIVATE + "util/env_windows.cc" + "util/windows_logger.h" + ) +else (WIN32) + target_sources(leveldb + PRIVATE + "util/env_posix.cc" + "util/posix_logger.h" + ) +endif (WIN32) + +# MemEnv is not part of the interface and could be pulled to a separate library. +target_sources(leveldb + PRIVATE + "helpers/memenv/memenv.cc" + "helpers/memenv/memenv.h" +) + +target_include_directories(leveldb + PUBLIC + $ + $ +) + +set_target_properties(leveldb + PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) + +target_compile_definitions(leveldb + PRIVATE + # Used by include/export.h when building shared libraries. + LEVELDB_COMPILE_LIBRARY + # Used by port/port.h. + ${LEVELDB_PLATFORM_NAME}=1 +) +if (NOT HAVE_CXX17_HAS_INCLUDE) + target_compile_definitions(leveldb + PRIVATE + LEVELDB_HAS_PORT_CONFIG_H=1 + ) +endif(NOT HAVE_CXX17_HAS_INCLUDE) + +if(BUILD_SHARED_LIBS) + target_compile_definitions(leveldb + PUBLIC + # Used by include/export.h. + LEVELDB_SHARED_LIBRARY + ) +endif(BUILD_SHARED_LIBS) + +if(HAVE_CLANG_THREAD_SAFETY) + target_compile_options(leveldb + PUBLIC + -Werror -Wthread-safety) +endif(HAVE_CLANG_THREAD_SAFETY) + +if(HAVE_CRC32C) + target_link_libraries(leveldb crc32c) +endif(HAVE_CRC32C) +if(HAVE_SNAPPY) + target_link_libraries(leveldb snappy) +endif(HAVE_SNAPPY) +if(HAVE_TCMALLOC) + target_link_libraries(leveldb tcmalloc) +endif(HAVE_TCMALLOC) + +# Needed by port_stdcxx.h +find_package(Threads REQUIRED) +target_link_libraries(leveldb Threads::Threads) + +add_executable(leveldbutil + "db/leveldbutil.cc" +) +target_link_libraries(leveldbutil leveldb) + +if(LEVELDB_BUILD_TESTS) + enable_testing() + + function(leveldb_test test_file) + get_filename_component(test_target_name "${test_file}" NAME_WE) + + add_executable("${test_target_name}" "") + target_sources("${test_target_name}" + PRIVATE + "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" + "util/testharness.cc" + "util/testharness.h" + "util/testutil.cc" + "util/testutil.h" + + "${test_file}" + ) + target_link_libraries("${test_target_name}" leveldb) + target_compile_definitions("${test_target_name}" + PRIVATE + ${LEVELDB_PLATFORM_NAME}=1 + ) + if (NOT HAVE_CXX17_HAS_INCLUDE) + target_compile_definitions("${test_target_name}" + PRIVATE + LEVELDB_HAS_PORT_CONFIG_H=1 + ) + endif(NOT HAVE_CXX17_HAS_INCLUDE) + + add_test(NAME "${test_target_name}" COMMAND "${test_target_name}") + endfunction(leveldb_test) + + leveldb_test("db/c_test.c") + leveldb_test("db/fault_injection_test.cc") + + leveldb_test("issues/issue178_test.cc") + leveldb_test("issues/issue200_test.cc") + leveldb_test("issues/issue320_test.cc") + + leveldb_test("util/env_test.cc") + leveldb_test("util/status_test.cc") + leveldb_test("util/no_destructor_test.cc") + + if(NOT BUILD_SHARED_LIBS) + leveldb_test("db/autocompact_test.cc") + leveldb_test("db/corruption_test.cc") + leveldb_test("db/db_test.cc") + leveldb_test("db/dbformat_test.cc") + leveldb_test("db/filename_test.cc") + leveldb_test("db/log_test.cc") + leveldb_test("db/recovery_test.cc") + leveldb_test("db/skiplist_test.cc") + leveldb_test("db/version_edit_test.cc") + leveldb_test("db/version_set_test.cc") + leveldb_test("db/write_batch_test.cc") + + leveldb_test("helpers/memenv/memenv_test.cc") + + leveldb_test("table/filter_block_test.cc") + leveldb_test("table/table_test.cc") + + leveldb_test("util/arena_test.cc") + leveldb_test("util/bloom_test.cc") + leveldb_test("util/cache_test.cc") + leveldb_test("util/coding_test.cc") + leveldb_test("util/crc32c_test.cc") + leveldb_test("util/hash_test.cc") + leveldb_test("util/logging_test.cc") + + # TODO(costan): This test also uses + # "util/env_{posix|windows}_test_helper.h" + if (WIN32) + leveldb_test("util/env_windows_test.cc") + else (WIN32) + leveldb_test("util/env_posix_test.cc") + endif (WIN32) + endif(NOT BUILD_SHARED_LIBS) +endif(LEVELDB_BUILD_TESTS) + +if(LEVELDB_BUILD_BENCHMARKS) + function(leveldb_benchmark bench_file) + get_filename_component(bench_target_name "${bench_file}" NAME_WE) + + add_executable("${bench_target_name}" "") + target_sources("${bench_target_name}" + PRIVATE + "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" + "util/histogram.cc" + "util/histogram.h" + "util/testharness.cc" + "util/testharness.h" + "util/testutil.cc" + "util/testutil.h" + + "${bench_file}" + ) + target_link_libraries("${bench_target_name}" leveldb) + target_compile_definitions("${bench_target_name}" + PRIVATE + ${LEVELDB_PLATFORM_NAME}=1 + ) + if (NOT HAVE_CXX17_HAS_INCLUDE) + target_compile_definitions("${bench_target_name}" + PRIVATE + LEVELDB_HAS_PORT_CONFIG_H=1 + ) + endif(NOT HAVE_CXX17_HAS_INCLUDE) + endfunction(leveldb_benchmark) + + if(NOT BUILD_SHARED_LIBS) + leveldb_benchmark("benchmarks/db_bench.cc") + endif(NOT BUILD_SHARED_LIBS) + + check_library_exists(sqlite3 sqlite3_open "" HAVE_SQLITE3) + if(HAVE_SQLITE3) + leveldb_benchmark("benchmarks/db_bench_sqlite3.cc") + target_link_libraries(db_bench_sqlite3 sqlite3) + endif(HAVE_SQLITE3) + + # check_library_exists is insufficient here because the library names have + # different manglings when compiled with clang or gcc, at least when installed + # with Homebrew on Mac. + set(OLD_CMAKE_REQURED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES kyotocabinet) + check_cxx_source_compiles(" +#include + +int main() { + kyotocabinet::TreeDB* db = new kyotocabinet::TreeDB(); + delete db; + return 0; +} + " HAVE_KYOTOCABINET) + set(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQURED_LIBRARIES}) + if(HAVE_KYOTOCABINET) + leveldb_benchmark("benchmarks/db_bench_tree_db.cc") + target_link_libraries(db_bench_tree_db kyotocabinet) + endif(HAVE_KYOTOCABINET) +endif(LEVELDB_BUILD_BENCHMARKS) + +if(LEVELDB_INSTALL) + install(TARGETS leveldb + EXPORT leveldbTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + install( + FILES + "${LEVELDB_PUBLIC_INCLUDE_DIR}/c.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/cache.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/comparator.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/db.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/dumpfile.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/env.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/export.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/filter_policy.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/iterator.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/options.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/slice.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/status.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/table_builder.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/table.h" + "${LEVELDB_PUBLIC_INCLUDE_DIR}/write_batch.h" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/leveldb + ) + + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/leveldbConfigVersion.cmake" + COMPATIBILITY SameMajorVersion + ) + install( + EXPORT leveldbTargets + NAMESPACE leveldb:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/leveldb" + ) + install( + FILES + "cmake/leveldbConfig.cmake" + "${PROJECT_BINARY_DIR}/leveldbConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/leveldb" + ) +endif(LEVELDB_INSTALL) diff --git a/src/leveldb/CONTRIBUTING.md b/src/leveldb/CONTRIBUTING.md index cd600ff46..a74572a59 100644 --- a/src/leveldb/CONTRIBUTING.md +++ b/src/leveldb/CONTRIBUTING.md @@ -31,6 +31,6 @@ the CLA. ## Writing Code ## -If your contribution contains code, please make sure that it follows -[the style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml). +If your contribution contains code, please make sure that it follows +[the style guide](http://google.github.io/styleguide/cppguide.html). Otherwise we will have to ask you to make changes, and that's no fun for anyone. diff --git a/src/leveldb/Makefile b/src/leveldb/Makefile deleted file mode 100644 index 2bd2cadcd..000000000 --- a/src/leveldb/Makefile +++ /dev/null @@ -1,227 +0,0 @@ -# Copyright (c) 2011 The LevelDB Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. See the AUTHORS file for names of contributors. - -#----------------------------------------------- -# Uncomment exactly one of the lines labelled (A), (B), and (C) below -# to switch between compilation modes. - -# (A) Production use (optimized mode) -OPT ?= -O2 -DNDEBUG -# (B) Debug mode, w/ full line-level debugging symbols -# OPT ?= -g2 -# (C) Profiling mode: opt, but w/debugging symbols -# OPT ?= -O2 -g2 -DNDEBUG -#----------------------------------------------- - -# detect what platform we're building on -$(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \ - ./build_detect_platform build_config.mk ./) -# this file is generated by the previous line to set build flags and sources -include build_config.mk - -CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT) -CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) - -LDFLAGS += $(PLATFORM_LDFLAGS) -LIBS += $(PLATFORM_LIBS) - -LIBOBJECTS = $(SOURCES:.cc=.o) -MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o) - -TESTUTIL = ./util/testutil.o -TESTHARNESS = ./util/testharness.o $(TESTUTIL) - -# Note: iOS should probably be using libtool, not ar. -ifeq ($(PLATFORM), IOS) -AR=xcrun ar -endif - -TESTS = \ - arena_test \ - autocompact_test \ - bloom_test \ - c_test \ - cache_test \ - coding_test \ - corruption_test \ - crc32c_test \ - db_test \ - dbformat_test \ - env_test \ - filename_test \ - filter_block_test \ - hash_test \ - issue178_test \ - issue200_test \ - log_test \ - memenv_test \ - skiplist_test \ - table_test \ - version_edit_test \ - version_set_test \ - write_batch_test - -PROGRAMS = db_bench leveldbutil $(TESTS) -BENCHMARKS = db_bench_sqlite3 db_bench_tree_db - -LIBRARY = libleveldb.a -MEMENVLIBRARY = libmemenv.a - -default: all - -# Should we build shared libraries? -ifneq ($(PLATFORM_SHARED_EXT),) - -ifneq ($(PLATFORM_SHARED_VERSIONED),true) -SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) -SHARED2 = $(SHARED1) -SHARED3 = $(SHARED1) -SHARED = $(SHARED1) -else -# Update db.h if you change these. -SHARED_MAJOR = 1 -SHARED_MINOR = 18 -SHARED1 = libleveldb.$(PLATFORM_SHARED_EXT) -SHARED2 = $(SHARED1).$(SHARED_MAJOR) -SHARED3 = $(SHARED1).$(SHARED_MAJOR).$(SHARED_MINOR) -SHARED = $(SHARED1) $(SHARED2) $(SHARED3) -$(SHARED1): $(SHARED3) - ln -fs $(SHARED3) $(SHARED1) -$(SHARED2): $(SHARED3) - ln -fs $(SHARED3) $(SHARED2) -endif - -$(SHARED3): - $(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS) - -endif # PLATFORM_SHARED_EXT - -all: $(SHARED) $(LIBRARY) - -check: all $(PROGRAMS) $(TESTS) - for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done - -clean: - -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk - -rm -rf ios-x86/* ios-arm/* - -$(LIBRARY): $(LIBOBJECTS) - rm -f $@ - $(AR) -rs $@ $(LIBOBJECTS) - -db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) -o $@ $(LIBS) - -db_bench_sqlite3: doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) doc/bench/db_bench_sqlite3.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lsqlite3 $(LIBS) - -db_bench_tree_db: doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) - $(CXX) $(LDFLAGS) doc/bench/db_bench_tree_db.o $(LIBOBJECTS) $(TESTUTIL) -o $@ -lkyotocabinet $(LIBS) - -leveldbutil: db/leveldb_main.o $(LIBOBJECTS) - $(CXX) $(LDFLAGS) db/leveldb_main.o $(LIBOBJECTS) -o $@ $(LIBS) - -arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -autocompact_test: db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/autocompact_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -filter_block_test: table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) table/filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -hash_test: util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) util/hash_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -issue178_test: issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) issues/issue178_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -issue200_test: issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) issues/issue200_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) - $(CXX) $(LDFLAGS) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) -o $@ $(LIBS) - -$(MEMENVLIBRARY) : $(MEMENVOBJECTS) - rm -f $@ - $(AR) -rs $@ $(MEMENVOBJECTS) - -memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) - $(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS) - -ifeq ($(PLATFORM), IOS) -# For iOS, create universal object files to be used on both the simulator and -# a device. -PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms -SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer -DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer -IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString) -IOSARCH=-arch armv6 -arch armv7 -arch armv7s -arch arm64 - -.cc.o: - mkdir -p ios-x86/$(dir $@) - xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@ - mkdir -p ios-arm/$(dir $@) - xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@ - xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@ - -.c.o: - mkdir -p ios-x86/$(dir $@) - xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@ - mkdir -p ios-arm/$(dir $@) - xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@ - xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@ - -else -.cc.o: - $(CXX) $(CXXFLAGS) -c $< -o $@ - -.c.o: - $(CC) $(CFLAGS) -c $< -o $@ -endif diff --git a/src/leveldb/README b/src/leveldb/README deleted file mode 100644 index 3618adeee..000000000 --- a/src/leveldb/README +++ /dev/null @@ -1,51 +0,0 @@ -leveldb: A key-value store -Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) - -The code under this directory implements a system for maintaining a -persistent key/value store. - -See doc/index.html for more explanation. -See doc/impl.html for a brief overview of the implementation. - -The public interface is in include/*.h. Callers should not include or -rely on the details of any other header files in this package. Those -internal APIs may be changed without warning. - -Guide to header files: - -include/db.h - Main interface to the DB: Start here - -include/options.h - Control over the behavior of an entire database, and also - control over the behavior of individual reads and writes. - -include/comparator.h - Abstraction for user-specified comparison function. If you want - just bytewise comparison of keys, you can use the default comparator, - but clients can write their own comparator implementations if they - want custom ordering (e.g. to handle different character - encodings, etc.) - -include/iterator.h - Interface for iterating over data. You can get an iterator - from a DB object. - -include/write_batch.h - Interface for atomically applying multiple updates to a database. - -include/slice.h - A simple module for maintaining a pointer and a length into some - other byte array. - -include/status.h - Status is returned from many of the public interfaces and is used - to report success and various kinds of errors. - -include/env.h - Abstraction of the OS environment. A posix implementation of - this interface is in util/env_posix.cc - -include/table.h -include/table_builder.h - Lower-level modules that most clients probably won't use directly diff --git a/src/leveldb/README.md b/src/leveldb/README.md index 480affb5c..dadfd5693 100644 --- a/src/leveldb/README.md +++ b/src/leveldb/README.md @@ -1,8 +1,12 @@ **LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.** +[![Build Status](https://travis-ci.org/google/leveldb.svg?branch=master)](https://travis-ci.org/google/leveldb) +[![Build status](https://ci.appveyor.com/api/projects/status/g2j5j4rfkda6eyw5/branch/master?svg=true)](https://ci.appveyor.com/project/pwnall/leveldb) + Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) # Features + * Keys and values are arbitrary byte arrays. * Data is stored sorted by key. * Callers can provide a custom comparison function to override the sort order. @@ -10,16 +14,98 @@ Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com) * Multiple changes can be made in one atomic batch. * Users can create a transient snapshot to get a consistent view of data. * Forward and backward iteration is supported over the data. - * Data is automatically compressed using the [Snappy compression library](http://code.google.com/p/snappy). + * Data is automatically compressed using the [Snappy compression library](http://google.github.io/snappy/). * External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions. - * [Detailed documentation](http://htmlpreview.github.io/?https://github.com/google/leveldb/blob/master/doc/index.html) about how to use the library is included with the source code. +# Documentation + + [LevelDB library documentation](https://github.com/google/leveldb/blob/master/doc/index.md) is online and bundled with the source code. # Limitations + * This is not a SQL database. It does not have a relational data model, it does not support SQL queries, and it has no support for indexes. * Only a single process (possibly multi-threaded) can access a particular database at a time. * There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library. +# Building + +This project supports [CMake](https://cmake.org/) out of the box. + +### Build for POSIX + +Quick start: + +```bash +mkdir -p build && cd build +cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . +``` + +### Building for Windows + +First generate the Visual Studio 2017 project/solution files: + +```cmd +mkdir build +cd build +cmake -G "Visual Studio 15" .. +``` +The default default will build for x86. For 64-bit run: + +```cmd +cmake -G "Visual Studio 15 Win64" .. +``` + +To compile the Windows solution from the command-line: + +```cmd +devenv /build Debug leveldb.sln +``` + +or open leveldb.sln in Visual Studio and build from within. + +Please see the CMake documentation and `CMakeLists.txt` for more advanced usage. + +# Contributing to the leveldb Project + +The leveldb project welcomes contributions. leveldb's primary goal is to be +a reliable and fast key/value store. Changes that are in line with the +features/limitations outlined above, and meet the requirements below, +will be considered. + +Contribution requirements: + +1. **Tested platforms only**. We _generally_ will only accept changes for + platforms that are compiled and tested. This means POSIX (for Linux and + macOS) or Windows. Very small changes will sometimes be accepted, but + consider that more of an exception than the rule. + +2. **Stable API**. We strive very hard to maintain a stable API. Changes that + require changes for projects using leveldb _might_ be rejected without + sufficient benefit to the project. + +3. **Tests**: All changes must be accompanied by a new (or changed) test, or + a sufficient explanation as to why a new (or changed) test is not required. + +4. **Consistent Style**: This project conforms to the + [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). + To ensure your changes are properly formatted please run: + + ``` + clang-format -i --style=file + ``` + +## Submitting a Pull Request + +Before any pull request will be accepted the author must first sign a +Contributor License Agreement (CLA) at https://cla.developers.google.com/. + +In order to keep the commit timeline linear +[squash](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits) +your changes down to a single commit and [rebase](https://git-scm.com/docs/git-rebase) +on google/leveldb/master. This keeps the commit timeline linear and more easily sync'ed +with the internal repository at Google. More information at GitHub's +[About Git rebase](https://help.github.com/articles/about-git-rebase/) page. + # Performance Here is a performance report (with explanations) from the run of the @@ -78,61 +164,62 @@ by the one or two disk seeks needed to fetch the data from disk. Write performance will be mostly unaffected by whether or not the working set fits in memory. - readrandom : 16.677 micros/op; (approximately 60,000 reads per second) - readseq : 0.476 micros/op; 232.3 MB/s - readreverse : 0.724 micros/op; 152.9 MB/s + readrandom : 16.677 micros/op; (approximately 60,000 reads per second) + readseq : 0.476 micros/op; 232.3 MB/s + readreverse : 0.724 micros/op; 152.9 MB/s LevelDB compacts its underlying storage data in the background to improve read performance. The results listed above were done immediately after a lot of random writes. The results after compactions (which are usually triggered automatically) are better. - readrandom : 11.602 micros/op; (approximately 85,000 reads per second) - readseq : 0.423 micros/op; 261.8 MB/s - readreverse : 0.663 micros/op; 166.9 MB/s + readrandom : 11.602 micros/op; (approximately 85,000 reads per second) + readseq : 0.423 micros/op; 261.8 MB/s + readreverse : 0.663 micros/op; 166.9 MB/s Some of the high cost of reads comes from repeated decompression of blocks read from disk. If we supply enough cache to the leveldb so it can hold the uncompressed blocks in memory, the read performance improves again: - readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction) - readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction) + readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction) + readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction) ## Repository contents -See doc/index.html for more explanation. See doc/impl.html for a brief overview of the implementation. +See [doc/index.md](doc/index.md) for more explanation. See +[doc/impl.md](doc/impl.md) for a brief overview of the implementation. -The public interface is in include/*.h. Callers should not include or +The public interface is in include/leveldb/*.h. Callers should not include or rely on the details of any other header files in this package. Those internal APIs may be changed without warning. Guide to header files: -* **include/db.h**: Main interface to the DB: Start here +* **include/leveldb/db.h**: Main interface to the DB: Start here. -* **include/options.h**: Control over the behavior of an entire database, +* **include/leveldb/options.h**: Control over the behavior of an entire database, and also control over the behavior of individual reads and writes. -* **include/comparator.h**: Abstraction for user-specified comparison function. +* **include/leveldb/comparator.h**: Abstraction for user-specified comparison function. If you want just bytewise comparison of keys, you can use the default comparator, but clients can write their own comparator implementations if they -want custom ordering (e.g. to handle different character encodings, etc.) +want custom ordering (e.g. to handle different character encodings, etc.). -* **include/iterator.h**: Interface for iterating over data. You can get +* **include/leveldb/iterator.h**: Interface for iterating over data. You can get an iterator from a DB object. -* **include/write_batch.h**: Interface for atomically applying multiple +* **include/leveldb/write_batch.h**: Interface for atomically applying multiple updates to a database. -* **include/slice.h**: A simple module for maintaining a pointer and a +* **include/leveldb/slice.h**: A simple module for maintaining a pointer and a length into some other byte array. -* **include/status.h**: Status is returned from many of the public interfaces +* **include/leveldb/status.h**: Status is returned from many of the public interfaces and is used to report success and various kinds of errors. -* **include/env.h**: +* **include/leveldb/env.h**: Abstraction of the OS environment. A posix implementation of this interface is -in util/env_posix.cc +in util/env_posix.cc. -* **include/table.h, include/table_builder.h**: Lower-level modules that most -clients probably won't use directly +* **include/leveldb/table.h, include/leveldb/table_builder.h**: Lower-level modules that most +clients probably won't use directly. diff --git a/src/leveldb/WINDOWS.md b/src/leveldb/WINDOWS.md deleted file mode 100644 index 5b76c2448..000000000 --- a/src/leveldb/WINDOWS.md +++ /dev/null @@ -1,39 +0,0 @@ -# Building LevelDB On Windows - -## Prereqs - -Install the [Windows Software Development Kit version 7.1](http://www.microsoft.com/downloads/dlx/en-us/listdetailsview.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b). - -Download and extract the [Snappy source distribution](http://snappy.googlecode.com/files/snappy-1.0.5.tar.gz) - -1. Open the "Windows SDK 7.1 Command Prompt" : - Start Menu -> "Microsoft Windows SDK v7.1" > "Windows SDK 7.1 Command Prompt" -2. Change the directory to the leveldb project - -## Building the Static lib - -* 32 bit Version - - setenv /x86 - msbuild.exe /p:Configuration=Release /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5 - -* 64 bit Version - - setenv /x64 - msbuild.exe /p:Configuration=Release /p:Platform=x64 /p:Snappy=..\snappy-1.0.5 - - -## Building and Running the Benchmark app - -* 32 bit Version - - setenv /x86 - msbuild.exe /p:Configuration=Benchmark /p:Platform=Win32 /p:Snappy=..\snappy-1.0.5 - Benchmark\leveldb.exe - -* 64 bit Version - - setenv /x64 - msbuild.exe /p:Configuration=Benchmark /p:Platform=x64 /p:Snappy=..\snappy-1.0.5 - x64\Benchmark\leveldb.exe - diff --git a/src/leveldb/db/db_bench.cc b/src/leveldb/benchmarks/db_bench.cc similarity index 82% rename from src/leveldb/db/db_bench.cc rename to src/leveldb/benchmarks/db_bench.cc index 705a170aa..3696023b7 100644 --- a/src/leveldb/db/db_bench.cc +++ b/src/leveldb/benchmarks/db_bench.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include #include #include -#include "db/db_impl.h" -#include "db/version_set.h" +#include + #include "leveldb/cache.h" #include "leveldb/db.h" #include "leveldb/env.h" +#include "leveldb/filter_policy.h" #include "leveldb/write_batch.h" #include "port/port.h" #include "util/crc32c.h" @@ -33,8 +33,8 @@ // readmissing -- read N missing keys in random order // readhot -- read N times in random order from 1% section of DB // seekrandom -- N random seeks +// open -- cost of opening a DB // crc32c -- repeated crc32c of 4K of data -// acquireload -- load N*1000 times // Meta operations: // compact -- Compact the entire DB // stats -- Print DB stats @@ -56,9 +56,7 @@ static const char* FLAGS_benchmarks = "fill100K," "crc32c," "snappycomp," - "snappyuncomp," - "acquireload," - ; + "snappyuncomp,"; // Number of key/values to place in database static int FLAGS_num = 1000000; @@ -83,6 +81,14 @@ static bool FLAGS_histogram = false; // (initialized to default value by "main") static int FLAGS_write_buffer_size = 0; +// Number of bytes written to each file. +// (initialized to default value by "main") +static int FLAGS_max_file_size = 0; + +// Approximate size of user data packed per block (before compression. +// (initialized to default value by "main") +static int FLAGS_block_size = 0; + // Number of bytes to use as a cache of uncompressed data. // Negative means use default settings. static int FLAGS_cache_size = -1; @@ -99,12 +105,16 @@ static int FLAGS_bloom_bits = -1; // benchmark will fail. static bool FLAGS_use_existing_db = false; +// If true, reuse existing log/MANIFEST files when re-opening a database. +static bool FLAGS_reuse_logs = false; + // Use the db with the following name. -static const char* FLAGS_db = NULL; +static const char* FLAGS_db = nullptr; namespace leveldb { namespace { +leveldb::Env* g_env = nullptr; // Helper for quickly generating random data. class RandomGenerator { @@ -138,17 +148,19 @@ class RandomGenerator { } }; +#if defined(__linux) static Slice TrimSpace(Slice s) { size_t start = 0; while (start < s.size() && isspace(s[start])) { start++; } size_t limit = s.size(); - while (limit > start && isspace(s[limit-1])) { + while (limit > start && isspace(s[limit - 1])) { limit--; } return Slice(s.data() + start, limit - start); } +#endif static void AppendWithSpace(std::string* str, Slice msg) { if (msg.empty()) return; @@ -175,14 +187,12 @@ class Stats { void Start() { next_report_ = 100; - last_op_finish_ = start_; hist_.Clear(); done_ = 0; bytes_ = 0; seconds_ = 0; - start_ = Env::Default()->NowMicros(); - finish_ = start_; message_.clear(); + start_ = finish_ = last_op_finish_ = g_env->NowMicros(); } void Merge(const Stats& other) { @@ -198,17 +208,15 @@ class Stats { } void Stop() { - finish_ = Env::Default()->NowMicros(); + finish_ = g_env->NowMicros(); seconds_ = (finish_ - start_) * 1e-6; } - void AddMessage(Slice msg) { - AppendWithSpace(&message_, msg); - } + void AddMessage(Slice msg) { AppendWithSpace(&message_, msg); } void FinishedSingleOp() { if (FLAGS_histogram) { - double now = Env::Default()->NowMicros(); + double now = g_env->NowMicros(); double micros = now - last_op_finish_; hist_.Add(micros); if (micros > 20000) { @@ -220,21 +228,26 @@ class Stats { done_++; if (done_ >= next_report_) { - if (next_report_ < 1000) next_report_ += 100; - else if (next_report_ < 5000) next_report_ += 500; - else if (next_report_ < 10000) next_report_ += 1000; - else if (next_report_ < 50000) next_report_ += 5000; - else if (next_report_ < 100000) next_report_ += 10000; - else if (next_report_ < 500000) next_report_ += 50000; - else next_report_ += 100000; + if (next_report_ < 1000) + next_report_ += 100; + else if (next_report_ < 5000) + next_report_ += 500; + else if (next_report_ < 10000) + next_report_ += 1000; + else if (next_report_ < 50000) + next_report_ += 5000; + else if (next_report_ < 100000) + next_report_ += 10000; + else if (next_report_ < 500000) + next_report_ += 50000; + else + next_report_ += 100000; fprintf(stderr, "... finished %d ops%30s\r", done_, ""); fflush(stderr); } } - void AddBytes(int64_t n) { - bytes_ += n; - } + void AddBytes(int64_t n) { bytes_ += n; } void Report(const Slice& name) { // Pretend at least one op was done in case we are running a benchmark @@ -253,11 +266,8 @@ class Stats { } AppendWithSpace(&extra, message_); - fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", - name.ToString().c_str(), - seconds_ * 1e6 / done_, - (extra.empty() ? "" : " "), - extra.c_str()); + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(), + seconds_ * 1e6 / done_, (extra.empty() ? "" : " "), extra.c_str()); if (FLAGS_histogram) { fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); } @@ -268,8 +278,8 @@ class Stats { // State shared by all concurrent executions of the same benchmark. struct SharedState { port::Mutex mu; - port::CondVar cv; - int total; + port::CondVar cv GUARDED_BY(mu); + int total GUARDED_BY(mu); // Each thread goes through the following states: // (1) initializing @@ -277,24 +287,22 @@ struct SharedState { // (3) running // (4) done - int num_initialized; - int num_done; - bool start; + int num_initialized GUARDED_BY(mu); + int num_done GUARDED_BY(mu); + bool start GUARDED_BY(mu); - SharedState() : cv(&mu) { } + SharedState(int total) + : cv(&mu), total(total), num_initialized(0), num_done(0), start(false) {} }; // Per-thread state for concurrent executions of the same benchmark. struct ThreadState { - int tid; // 0..n-1 when running in n threads - Random rand; // Has different seeds for different threads + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads Stats stats; SharedState* shared; - ThreadState(int index) - : tid(index), - rand(1000 + index) { - } + ThreadState(int index) : tid(index), rand(1000 + index), shared(nullptr) {} }; } // namespace @@ -320,20 +328,20 @@ class Benchmark { static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); fprintf(stdout, "Entries: %d\n", num_); fprintf(stdout, "RawSize: %.1f MB (estimated)\n", - ((static_cast(kKeySize + FLAGS_value_size) * num_) - / 1048576.0)); + ((static_cast(kKeySize + FLAGS_value_size) * num_) / + 1048576.0)); fprintf(stdout, "FileSize: %.1f MB (estimated)\n", - (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) - / 1048576.0)); + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) / + 1048576.0)); PrintWarnings(); fprintf(stdout, "------------------------------------------------\n"); } void PrintWarnings() { #if defined(__GNUC__) && !defined(__OPTIMIZE__) - fprintf(stdout, - "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" - ); + fprintf( + stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"); #endif #ifndef NDEBUG fprintf(stdout, @@ -351,22 +359,22 @@ class Benchmark { } void PrintEnvironment() { - fprintf(stderr, "LevelDB: version %d.%d\n", - kMajorVersion, kMinorVersion); + fprintf(stderr, "LevelDB: version %d.%d\n", kMajorVersion, + kMinorVersion); #if defined(__linux) - time_t now = time(NULL); + time_t now = time(nullptr); fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); - if (cpuinfo != NULL) { + if (cpuinfo != nullptr) { char line[1000]; int num_cpus = 0; std::string cpu_type; std::string cache_size; - while (fgets(line, sizeof(line), cpuinfo) != NULL) { + while (fgets(line, sizeof(line), cpuinfo) != nullptr) { const char* sep = strchr(line, ':'); - if (sep == NULL) { + if (sep == nullptr) { continue; } Slice key = TrimSpace(Slice(line, sep - 1 - line)); @@ -387,21 +395,21 @@ class Benchmark { public: Benchmark() - : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL), - filter_policy_(FLAGS_bloom_bits >= 0 - ? NewBloomFilterPolicy(FLAGS_bloom_bits) - : NULL), - db_(NULL), - num_(FLAGS_num), - value_size_(FLAGS_value_size), - entries_per_batch_(1), - reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), - heap_counter_(0) { + : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : nullptr), + filter_policy_(FLAGS_bloom_bits >= 0 + ? NewBloomFilterPolicy(FLAGS_bloom_bits) + : nullptr), + db_(nullptr), + num_(FLAGS_num), + value_size_(FLAGS_value_size), + entries_per_batch_(1), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + heap_counter_(0) { std::vector files; - Env::Default()->GetChildren(FLAGS_db, &files); + g_env->GetChildren(FLAGS_db, &files); for (size_t i = 0; i < files.size(); i++) { if (Slice(files[i]).starts_with("heap-")) { - Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); + g_env->DeleteFile(std::string(FLAGS_db) + "/" + files[i]); } } if (!FLAGS_use_existing_db) { @@ -420,12 +428,12 @@ class Benchmark { Open(); const char* benchmarks = FLAGS_benchmarks; - while (benchmarks != NULL) { + while (benchmarks != nullptr) { const char* sep = strchr(benchmarks, ','); Slice name; - if (sep == NULL) { + if (sep == nullptr) { name = benchmarks; - benchmarks = NULL; + benchmarks = nullptr; } else { name = Slice(benchmarks, sep - benchmarks); benchmarks = sep + 1; @@ -438,11 +446,15 @@ class Benchmark { entries_per_batch_ = 1; write_options_ = WriteOptions(); - void (Benchmark::*method)(ThreadState*) = NULL; + void (Benchmark::*method)(ThreadState*) = nullptr; bool fresh_db = false; int num_threads = FLAGS_threads; - if (name == Slice("fillseq")) { + if (name == Slice("open")) { + method = &Benchmark::OpenBench; + num_ /= 10000; + if (num_ < 1) num_ = 1; + } else if (name == Slice("fillseq")) { fresh_db = true; method = &Benchmark::WriteSeq; } else if (name == Slice("fillbatch")) { @@ -491,8 +503,6 @@ class Benchmark { method = &Benchmark::Compact; } else if (name == Slice("crc32c")) { method = &Benchmark::Crc32c; - } else if (name == Slice("acquireload")) { - method = &Benchmark::AcquireLoad; } else if (name == Slice("snappycomp")) { method = &Benchmark::SnappyCompress; } else if (name == Slice("snappyuncomp")) { @@ -504,7 +514,7 @@ class Benchmark { } else if (name == Slice("sstables")) { PrintStats("leveldb.sstables"); } else { - if (name != Slice()) { // No error message for empty name + if (!name.empty()) { // No error message for empty name fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str()); } } @@ -513,16 +523,16 @@ class Benchmark { if (FLAGS_use_existing_db) { fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n", name.ToString().c_str()); - method = NULL; + method = nullptr; } else { delete db_; - db_ = NULL; + db_ = nullptr; DestroyDB(FLAGS_db, Options()); Open(); } } - if (method != NULL) { + if (method != nullptr) { RunBenchmark(num_threads, name, method); } } @@ -566,11 +576,7 @@ class Benchmark { void RunBenchmark(int n, Slice name, void (Benchmark::*method)(ThreadState*)) { - SharedState shared; - shared.total = n; - shared.num_initialized = 0; - shared.num_done = 0; - shared.start = false; + SharedState shared(n); ThreadArg* arg = new ThreadArg[n]; for (int i = 0; i < n; i++) { @@ -579,7 +585,7 @@ class Benchmark { arg[i].shared = &shared; arg[i].thread = new ThreadState(i); arg[i].thread->shared = &shared; - Env::Default()->StartThread(ThreadBody, &arg[i]); + g_env->StartThread(ThreadBody, &arg[i]); } shared.mu.Lock(); @@ -624,22 +630,6 @@ class Benchmark { thread->stats.AddMessage(label); } - void AcquireLoad(ThreadState* thread) { - int dummy; - port::AtomicPointer ap(&dummy); - int count = 0; - void *ptr = NULL; - thread->stats.AddMessage("(each op is 1000 loads)"); - while (count < 100000) { - for (int i = 0; i < 1000; i++) { - ptr = ap.Acquire_Load(); - } - count++; - thread->stats.FinishedSingleOp(); - } - if (ptr == NULL) exit(1); // Disable unused variable warning. - } - void SnappyCompress(ThreadState* thread) { RandomGenerator gen; Slice input = gen.Generate(Options().block_size); @@ -673,8 +663,8 @@ class Benchmark { int64_t bytes = 0; char* uncompressed = new char[input.size()]; while (ok && bytes < 1024 * 1048576) { // Compress 1G - ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), - uncompressed); + ok = port::Snappy_Uncompress(compressed.data(), compressed.size(), + uncompressed); bytes += input.size(); thread->stats.FinishedSingleOp(); } @@ -688,13 +678,17 @@ class Benchmark { } void Open() { - assert(db_ == NULL); + assert(db_ == nullptr); Options options; + options.env = g_env; options.create_if_missing = !FLAGS_use_existing_db; options.block_cache = cache_; options.write_buffer_size = FLAGS_write_buffer_size; + options.max_file_size = FLAGS_max_file_size; + options.block_size = FLAGS_block_size; options.max_open_files = FLAGS_open_files; options.filter_policy = filter_policy_; + options.reuse_logs = FLAGS_reuse_logs; Status s = DB::Open(options, FLAGS_db, &db_); if (!s.ok()) { fprintf(stderr, "open error: %s\n", s.ToString().c_str()); @@ -702,13 +696,17 @@ class Benchmark { } } - void WriteSeq(ThreadState* thread) { - DoWrite(thread, true); + void OpenBench(ThreadState* thread) { + for (int i = 0; i < num_; i++) { + delete db_; + Open(); + thread->stats.FinishedSingleOp(); + } } - void WriteRandom(ThreadState* thread) { - DoWrite(thread, false); - } + void WriteSeq(ThreadState* thread) { DoWrite(thread, true); } + + void WriteRandom(ThreadState* thread) { DoWrite(thread, false); } void DoWrite(ThreadState* thread, bool seq) { if (num_ != FLAGS_num) { @@ -724,7 +722,7 @@ class Benchmark { for (int i = 0; i < num_; i += entries_per_batch_) { batch.Clear(); for (int j = 0; j < entries_per_batch_; j++) { - const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + const int k = seq ? i + j : (thread->rand.Next() % FLAGS_num); char key[100]; snprintf(key, sizeof(key), "%016d", k); batch.Put(key, gen.Generate(value_size_)); @@ -834,7 +832,7 @@ class Benchmark { for (int i = 0; i < num_; i += entries_per_batch_) { batch.Clear(); for (int j = 0; j < entries_per_batch_; j++) { - const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num); + const int k = seq ? i + j : (thread->rand.Next() % FLAGS_num); char key[100]; snprintf(key, sizeof(key), "%016d", k); batch.Delete(key); @@ -848,13 +846,9 @@ class Benchmark { } } - void DeleteSeq(ThreadState* thread) { - DoDelete(thread, true); - } + void DeleteSeq(ThreadState* thread) { DoDelete(thread, true); } - void DeleteRandom(ThreadState* thread) { - DoDelete(thread, false); - } + void DeleteRandom(ThreadState* thread) { DoDelete(thread, false); } void ReadWhileWriting(ThreadState* thread) { if (thread->tid > 0) { @@ -886,9 +880,7 @@ class Benchmark { } } - void Compact(ThreadState* thread) { - db_->CompactRange(NULL, NULL); - } + void Compact(ThreadState* thread) { db_->CompactRange(nullptr, nullptr); } void PrintStats(const char* key) { std::string stats; @@ -906,7 +898,7 @@ class Benchmark { char fname[100]; snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_); WritableFile* file; - Status s = Env::Default()->NewWritableFile(fname, &file); + Status s = g_env->NewWritableFile(fname, &file); if (!s.ok()) { fprintf(stderr, "%s\n", s.ToString().c_str()); return; @@ -915,7 +907,7 @@ class Benchmark { delete file; if (!ok) { fprintf(stderr, "heap profiling not supported\n"); - Env::Default()->DeleteFile(fname); + g_env->DeleteFile(fname); } } }; @@ -924,6 +916,8 @@ class Benchmark { int main(int argc, char** argv) { FLAGS_write_buffer_size = leveldb::Options().write_buffer_size; + FLAGS_max_file_size = leveldb::Options().max_file_size; + FLAGS_block_size = leveldb::Options().block_size; FLAGS_open_files = leveldb::Options().max_open_files; std::string default_db_path; @@ -941,6 +935,9 @@ int main(int argc, char** argv) { } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 && (n == 0 || n == 1)) { FLAGS_use_existing_db = n; + } else if (sscanf(argv[i], "--reuse_logs=%d%c", &n, &junk) == 1 && + (n == 0 || n == 1)) { + FLAGS_reuse_logs = n; } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { FLAGS_num = n; } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { @@ -951,6 +948,10 @@ int main(int argc, char** argv) { FLAGS_value_size = n; } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) { FLAGS_write_buffer_size = n; + } else if (sscanf(argv[i], "--max_file_size=%d%c", &n, &junk) == 1) { + FLAGS_max_file_size = n; + } else if (sscanf(argv[i], "--block_size=%d%c", &n, &junk) == 1) { + FLAGS_block_size = n; } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) { FLAGS_cache_size = n; } else if (sscanf(argv[i], "--bloom_bits=%d%c", &n, &junk) == 1) { @@ -965,11 +966,13 @@ int main(int argc, char** argv) { } } + leveldb::g_env = leveldb::Env::Default(); + // Choose a location for the test database if none given with --db= - if (FLAGS_db == NULL) { - leveldb::Env::Default()->GetTestDirectory(&default_db_path); - default_db_path += "/dbbench"; - FLAGS_db = default_db_path.c_str(); + if (FLAGS_db == nullptr) { + leveldb::g_env->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); } leveldb::Benchmark benchmark; diff --git a/src/leveldb/doc/bench/db_bench_sqlite3.cc b/src/leveldb/benchmarks/db_bench_sqlite3.cc similarity index 83% rename from src/leveldb/doc/bench/db_bench_sqlite3.cc rename to src/leveldb/benchmarks/db_bench_sqlite3.cc index e63aaa8dc..f183f4fcf 100644 --- a/src/leveldb/doc/bench/db_bench_sqlite3.cc +++ b/src/leveldb/benchmarks/db_bench_sqlite3.cc @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include #include #include -#include + #include "util/histogram.h" #include "util/random.h" #include "util/testutil.h" @@ -38,8 +39,7 @@ static const char* FLAGS_benchmarks = "fillrand100K," "fillseq100K," "readseq," - "readrand100K," - ; + "readrand100K,"; // Number of key/values to place in database static int FLAGS_num = 1000000; @@ -76,10 +76,9 @@ static bool FLAGS_transaction = true; static bool FLAGS_WAL_enabled = true; // Use the db with the following name. -static const char* FLAGS_db = NULL; +static const char* FLAGS_db = nullptr; -inline -static void ExecErrorCheck(int status, char *err_msg) { +inline static void ExecErrorCheck(int status, char* err_msg) { if (status != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", err_msg); sqlite3_free(err_msg); @@ -87,27 +86,25 @@ static void ExecErrorCheck(int status, char *err_msg) { } } -inline -static void StepErrorCheck(int status) { +inline static void StepErrorCheck(int status) { if (status != SQLITE_DONE) { fprintf(stderr, "SQL step error: status = %d\n", status); exit(1); } } -inline -static void ErrorCheck(int status) { +inline static void ErrorCheck(int status) { if (status != SQLITE_OK) { fprintf(stderr, "sqlite3 error: status = %d\n", status); exit(1); } } -inline -static void WalCheckpoint(sqlite3* db_) { +inline static void WalCheckpoint(sqlite3* db_) { // Flush all writes to disk if (FLAGS_WAL_enabled) { - sqlite3_wal_checkpoint_v2(db_, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL); + sqlite3_wal_checkpoint_v2(db_, nullptr, SQLITE_CHECKPOINT_FULL, nullptr, + nullptr); } } @@ -152,7 +149,7 @@ static Slice TrimSpace(Slice s) { start++; } int limit = s.size(); - while (limit > start && isspace(s[limit-1])) { + while (limit > start && isspace(s[limit - 1])) { limit--; } return Slice(s.data() + start, limit - start); @@ -176,7 +173,7 @@ class Benchmark { // State kept for progress messages int done_; - int next_report_; // When to report next + int next_report_; // When to report next void PrintHeader() { const int kKeySize = 16; @@ -185,17 +182,17 @@ class Benchmark { fprintf(stdout, "Values: %d bytes each\n", FLAGS_value_size); fprintf(stdout, "Entries: %d\n", num_); fprintf(stdout, "RawSize: %.1f MB (estimated)\n", - ((static_cast(kKeySize + FLAGS_value_size) * num_) - / 1048576.0)); + ((static_cast(kKeySize + FLAGS_value_size) * num_) / + 1048576.0)); PrintWarnings(); fprintf(stdout, "------------------------------------------------\n"); } void PrintWarnings() { #if defined(__GNUC__) && !defined(__OPTIMIZE__) - fprintf(stdout, - "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" - ); + fprintf( + stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"); #endif #ifndef NDEBUG fprintf(stdout, @@ -207,18 +204,18 @@ class Benchmark { fprintf(stderr, "SQLite: version %s\n", SQLITE_VERSION); #if defined(__linux) - time_t now = time(NULL); + time_t now = time(nullptr); fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); - if (cpuinfo != NULL) { + if (cpuinfo != nullptr) { char line[1000]; int num_cpus = 0; std::string cpu_type; std::string cache_size; - while (fgets(line, sizeof(line), cpuinfo) != NULL) { + while (fgets(line, sizeof(line), cpuinfo) != nullptr) { const char* sep = strchr(line, ':'); - if (sep == NULL) { + if (sep == nullptr) { continue; } Slice key = TrimSpace(Slice(line, sep - 1 - line)); @@ -261,13 +258,20 @@ class Benchmark { done_++; if (done_ >= next_report_) { - if (next_report_ < 1000) next_report_ += 100; - else if (next_report_ < 5000) next_report_ += 500; - else if (next_report_ < 10000) next_report_ += 1000; - else if (next_report_ < 50000) next_report_ += 5000; - else if (next_report_ < 100000) next_report_ += 10000; - else if (next_report_ < 500000) next_report_ += 50000; - else next_report_ += 100000; + if (next_report_ < 1000) + next_report_ += 100; + else if (next_report_ < 5000) + next_report_ += 500; + else if (next_report_ < 10000) + next_report_ += 1000; + else if (next_report_ < 50000) + next_report_ += 5000; + else if (next_report_ < 100000) + next_report_ += 10000; + else if (next_report_ < 500000) + next_report_ += 50000; + else + next_report_ += 100000; fprintf(stderr, "... finished %d ops%30s\r", done_, ""); fflush(stderr); } @@ -285,16 +289,14 @@ class Benchmark { snprintf(rate, sizeof(rate), "%6.1f MB/s", (bytes_ / 1048576.0) / (finish - start_)); if (!message_.empty()) { - message_ = std::string(rate) + " " + message_; + message_ = std::string(rate) + " " + message_; } else { message_ = rate; } } - fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", - name.ToString().c_str(), - (finish - start_) * 1e6 / done_, - (message_.empty() ? "" : " "), + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(), + (finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "), message_.c_str()); if (FLAGS_histogram) { fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); @@ -303,22 +305,16 @@ class Benchmark { } public: - enum Order { - SEQUENTIAL, - RANDOM - }; - enum DBState { - FRESH, - EXISTING - }; + enum Order { SEQUENTIAL, RANDOM }; + enum DBState { FRESH, EXISTING }; Benchmark() - : db_(NULL), - db_num_(0), - num_(FLAGS_num), - reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), - bytes_(0), - rand_(301) { + : db_(nullptr), + db_num_(0), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { std::vector files; std::string test_dir; Env::Default()->GetTestDirectory(&test_dir); @@ -345,12 +341,12 @@ class Benchmark { Open(); const char* benchmarks = FLAGS_benchmarks; - while (benchmarks != NULL) { + while (benchmarks != nullptr) { const char* sep = strchr(benchmarks, ','); Slice name; - if (sep == NULL) { + if (sep == nullptr) { name = benchmarks; - benchmarks = NULL; + benchmarks = nullptr; } else { name = Slice(benchmarks, sep - benchmarks); benchmarks = sep + 1; @@ -415,20 +411,18 @@ class Benchmark { } void Open() { - assert(db_ == NULL); + assert(db_ == nullptr); int status; char file_name[100]; - char* err_msg = NULL; + char* err_msg = nullptr; db_num_++; // Open database std::string tmp_dir; Env::Default()->GetTestDirectory(&tmp_dir); - snprintf(file_name, sizeof(file_name), - "%s/dbbench_sqlite3-%d.db", - tmp_dir.c_str(), - db_num_); + snprintf(file_name, sizeof(file_name), "%s/dbbench_sqlite3-%d.db", + tmp_dir.c_str(), db_num_); status = sqlite3_open(file_name, &db_); if (status) { fprintf(stderr, "open error: %s\n", sqlite3_errmsg(db_)); @@ -439,7 +433,7 @@ class Benchmark { char cache_size[100]; snprintf(cache_size, sizeof(cache_size), "PRAGMA cache_size = %d", FLAGS_num_pages); - status = sqlite3_exec(db_, cache_size, NULL, NULL, &err_msg); + status = sqlite3_exec(db_, cache_size, nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); // FLAGS_page_size is defaulted to 1024 @@ -447,7 +441,7 @@ class Benchmark { char page_size[100]; snprintf(page_size, sizeof(page_size), "PRAGMA page_size = %d", FLAGS_page_size); - status = sqlite3_exec(db_, page_size, NULL, NULL, &err_msg); + status = sqlite3_exec(db_, page_size, nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); } @@ -457,26 +451,28 @@ class Benchmark { // LevelDB's default cache size is a combined 4 MB std::string WAL_checkpoint = "PRAGMA wal_autocheckpoint = 4096"; - status = sqlite3_exec(db_, WAL_stmt.c_str(), NULL, NULL, &err_msg); + status = sqlite3_exec(db_, WAL_stmt.c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); - status = sqlite3_exec(db_, WAL_checkpoint.c_str(), NULL, NULL, &err_msg); + status = + sqlite3_exec(db_, WAL_checkpoint.c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); } // Change locking mode to exclusive and create tables/index for database std::string locking_stmt = "PRAGMA locking_mode = EXCLUSIVE"; std::string create_stmt = - "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))"; - std::string stmt_array[] = { locking_stmt, create_stmt }; + "CREATE TABLE test (key blob, value blob, PRIMARY KEY(key))"; + std::string stmt_array[] = {locking_stmt, create_stmt}; int stmt_array_length = sizeof(stmt_array) / sizeof(std::string); for (int i = 0; i < stmt_array_length; i++) { - status = sqlite3_exec(db_, stmt_array[i].c_str(), NULL, NULL, &err_msg); + status = + sqlite3_exec(db_, stmt_array[i].c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); } } - void Write(bool write_sync, Order order, DBState state, - int num_entries, int value_size, int entries_per_batch) { + void Write(bool write_sync, Order order, DBState state, int num_entries, + int value_size, int entries_per_batch) { // Create new database if state == FRESH if (state == FRESH) { if (FLAGS_use_existing_db) { @@ -484,7 +480,7 @@ class Benchmark { return; } sqlite3_close(db_); - db_ = NULL; + db_ = nullptr; Open(); Start(); } @@ -495,7 +491,7 @@ class Benchmark { message_ = msg; } - char* err_msg = NULL; + char* err_msg = nullptr; int status; sqlite3_stmt *replace_stmt, *begin_trans_stmt, *end_trans_stmt; @@ -504,20 +500,20 @@ class Benchmark { std::string end_trans_str = "END TRANSACTION;"; // Check for synchronous flag in options - std::string sync_stmt = (write_sync) ? "PRAGMA synchronous = FULL" : - "PRAGMA synchronous = OFF"; - status = sqlite3_exec(db_, sync_stmt.c_str(), NULL, NULL, &err_msg); + std::string sync_stmt = + (write_sync) ? "PRAGMA synchronous = FULL" : "PRAGMA synchronous = OFF"; + status = sqlite3_exec(db_, sync_stmt.c_str(), nullptr, nullptr, &err_msg); ExecErrorCheck(status, err_msg); // Preparing sqlite3 statements - status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, - &replace_stmt, NULL); + status = sqlite3_prepare_v2(db_, replace_str.c_str(), -1, &replace_stmt, + nullptr); ErrorCheck(status); status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, - &begin_trans_stmt, NULL); + &begin_trans_stmt, nullptr); ErrorCheck(status); - status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, - &end_trans_stmt, NULL); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt, + nullptr); ErrorCheck(status); bool transaction = (entries_per_batch > 1); @@ -535,16 +531,16 @@ class Benchmark { const char* value = gen_.Generate(value_size).data(); // Create values for key-value pair - const int k = (order == SEQUENTIAL) ? i + j : - (rand_.Next() % num_entries); + const int k = + (order == SEQUENTIAL) ? i + j : (rand_.Next() % num_entries); char key[100]; snprintf(key, sizeof(key), "%016d", k); // Bind KV values into replace_stmt status = sqlite3_bind_blob(replace_stmt, 1, key, 16, SQLITE_STATIC); ErrorCheck(status); - status = sqlite3_bind_blob(replace_stmt, 2, value, - value_size, SQLITE_STATIC); + status = sqlite3_bind_blob(replace_stmt, 2, value, value_size, + SQLITE_STATIC); ErrorCheck(status); // Execute replace_stmt @@ -588,12 +584,12 @@ class Benchmark { // Preparing sqlite3 statements status = sqlite3_prepare_v2(db_, begin_trans_str.c_str(), -1, - &begin_trans_stmt, NULL); + &begin_trans_stmt, nullptr); ErrorCheck(status); - status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, - &end_trans_stmt, NULL); + status = sqlite3_prepare_v2(db_, end_trans_str.c_str(), -1, &end_trans_stmt, + nullptr); ErrorCheck(status); - status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, NULL); + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &read_stmt, nullptr); ErrorCheck(status); bool transaction = (entries_per_batch > 1); @@ -618,7 +614,8 @@ class Benchmark { ErrorCheck(status); // Execute read statement - while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) {} + while ((status = sqlite3_step(read_stmt)) == SQLITE_ROW) { + } StepErrorCheck(status); // Reset SQLite statement for another use @@ -648,10 +645,10 @@ class Benchmark { void ReadSequential() { int status; - sqlite3_stmt *pStmt; + sqlite3_stmt* pStmt; std::string read_str = "SELECT * FROM test ORDER BY key"; - status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, NULL); + status = sqlite3_prepare_v2(db_, read_str.c_str(), -1, &pStmt, nullptr); ErrorCheck(status); for (int i = 0; i < reads_ && SQLITE_ROW == sqlite3_step(pStmt); i++) { bytes_ += sqlite3_column_bytes(pStmt, 1) + sqlite3_column_bytes(pStmt, 2); @@ -661,7 +658,6 @@ class Benchmark { status = sqlite3_finalize(pStmt); ErrorCheck(status); } - }; } // namespace leveldb @@ -706,10 +702,10 @@ int main(int argc, char** argv) { } // Choose a location for the test database if none given with --db= - if (FLAGS_db == NULL) { - leveldb::Env::Default()->GetTestDirectory(&default_db_path); - default_db_path += "/dbbench"; - FLAGS_db = default_db_path.c_str(); + if (FLAGS_db == nullptr) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); } leveldb::Benchmark benchmark; diff --git a/src/leveldb/doc/bench/db_bench_tree_db.cc b/src/leveldb/benchmarks/db_bench_tree_db.cc similarity index 85% rename from src/leveldb/doc/bench/db_bench_tree_db.cc rename to src/leveldb/benchmarks/db_bench_tree_db.cc index 4ca381f11..b2f6646d8 100644 --- a/src/leveldb/doc/bench/db_bench_tree_db.cc +++ b/src/leveldb/benchmarks/db_bench_tree_db.cc @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include #include #include -#include + #include "util/histogram.h" #include "util/random.h" #include "util/testutil.h" @@ -34,8 +35,7 @@ static const char* FLAGS_benchmarks = "fillrand100K," "fillseq100K," "readseq100K," - "readrand100K," - ; + "readrand100K,"; // Number of key/values to place in database static int FLAGS_num = 1000000; @@ -69,11 +69,9 @@ static bool FLAGS_use_existing_db = false; static bool FLAGS_compression = true; // Use the db with the following name. -static const char* FLAGS_db = NULL; +static const char* FLAGS_db = nullptr; -inline -static void DBSynchronize(kyotocabinet::TreeDB* db_) -{ +inline static void DBSynchronize(kyotocabinet::TreeDB* db_) { // Synchronize will flush writes to disk if (!db_->synchronize()) { fprintf(stderr, "synchronize error: %s\n", db_->error().name()); @@ -121,7 +119,7 @@ static Slice TrimSpace(Slice s) { start++; } int limit = s.size(); - while (limit > start && isspace(s[limit-1])) { + while (limit > start && isspace(s[limit - 1])) { limit--; } return Slice(s.data() + start, limit - start); @@ -146,7 +144,7 @@ class Benchmark { // State kept for progress messages int done_; - int next_report_; // When to report next + int next_report_; // When to report next void PrintHeader() { const int kKeySize = 16; @@ -157,20 +155,20 @@ class Benchmark { static_cast(FLAGS_value_size * FLAGS_compression_ratio + 0.5)); fprintf(stdout, "Entries: %d\n", num_); fprintf(stdout, "RawSize: %.1f MB (estimated)\n", - ((static_cast(kKeySize + FLAGS_value_size) * num_) - / 1048576.0)); + ((static_cast(kKeySize + FLAGS_value_size) * num_) / + 1048576.0)); fprintf(stdout, "FileSize: %.1f MB (estimated)\n", - (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) - / 1048576.0)); + (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_) / + 1048576.0)); PrintWarnings(); fprintf(stdout, "------------------------------------------------\n"); } void PrintWarnings() { #if defined(__GNUC__) && !defined(__OPTIMIZE__) - fprintf(stdout, - "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" - ); + fprintf( + stdout, + "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"); #endif #ifndef NDEBUG fprintf(stdout, @@ -183,18 +181,18 @@ class Benchmark { kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV); #if defined(__linux) - time_t now = time(NULL); + time_t now = time(nullptr); fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); - if (cpuinfo != NULL) { + if (cpuinfo != nullptr) { char line[1000]; int num_cpus = 0; std::string cpu_type; std::string cache_size; - while (fgets(line, sizeof(line), cpuinfo) != NULL) { + while (fgets(line, sizeof(line), cpuinfo) != nullptr) { const char* sep = strchr(line, ':'); - if (sep == NULL) { + if (sep == nullptr) { continue; } Slice key = TrimSpace(Slice(line, sep - 1 - line)); @@ -237,13 +235,20 @@ class Benchmark { done_++; if (done_ >= next_report_) { - if (next_report_ < 1000) next_report_ += 100; - else if (next_report_ < 5000) next_report_ += 500; - else if (next_report_ < 10000) next_report_ += 1000; - else if (next_report_ < 50000) next_report_ += 5000; - else if (next_report_ < 100000) next_report_ += 10000; - else if (next_report_ < 500000) next_report_ += 50000; - else next_report_ += 100000; + if (next_report_ < 1000) + next_report_ += 100; + else if (next_report_ < 5000) + next_report_ += 500; + else if (next_report_ < 10000) + next_report_ += 1000; + else if (next_report_ < 50000) + next_report_ += 5000; + else if (next_report_ < 100000) + next_report_ += 10000; + else if (next_report_ < 500000) + next_report_ += 50000; + else + next_report_ += 100000; fprintf(stderr, "... finished %d ops%30s\r", done_, ""); fflush(stderr); } @@ -261,16 +266,14 @@ class Benchmark { snprintf(rate, sizeof(rate), "%6.1f MB/s", (bytes_ / 1048576.0) / (finish - start_)); if (!message_.empty()) { - message_ = std::string(rate) + " " + message_; + message_ = std::string(rate) + " " + message_; } else { message_ = rate; } } - fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", - name.ToString().c_str(), - (finish - start_) * 1e6 / done_, - (message_.empty() ? "" : " "), + fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n", name.ToString().c_str(), + (finish - start_) * 1e6 / done_, (message_.empty() ? "" : " "), message_.c_str()); if (FLAGS_histogram) { fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str()); @@ -279,21 +282,15 @@ class Benchmark { } public: - enum Order { - SEQUENTIAL, - RANDOM - }; - enum DBState { - FRESH, - EXISTING - }; + enum Order { SEQUENTIAL, RANDOM }; + enum DBState { FRESH, EXISTING }; Benchmark() - : db_(NULL), - num_(FLAGS_num), - reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), - bytes_(0), - rand_(301) { + : db_(nullptr), + num_(FLAGS_num), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + bytes_(0), + rand_(301) { std::vector files; std::string test_dir; Env::Default()->GetTestDirectory(&test_dir); @@ -321,12 +318,12 @@ class Benchmark { Open(false); const char* benchmarks = FLAGS_benchmarks; - while (benchmarks != NULL) { + while (benchmarks != nullptr) { const char* sep = strchr(benchmarks, ','); Slice name; - if (sep == NULL) { + if (sep == nullptr) { name = benchmarks; - benchmarks = NULL; + benchmarks = nullptr; } else { name = Slice(benchmarks, sep - benchmarks); benchmarks = sep + 1; @@ -386,8 +383,8 @@ class Benchmark { } private: - void Open(bool sync) { - assert(db_ == NULL); + void Open(bool sync) { + assert(db_ == nullptr); // Initialize db_ db_ = new kyotocabinet::TreeDB(); @@ -395,16 +392,14 @@ class Benchmark { db_num_++; std::string test_dir; Env::Default()->GetTestDirectory(&test_dir); - snprintf(file_name, sizeof(file_name), - "%s/dbbench_polyDB-%d.kct", - test_dir.c_str(), - db_num_); + snprintf(file_name, sizeof(file_name), "%s/dbbench_polyDB-%d.kct", + test_dir.c_str(), db_num_); // Create tuning options and open the database - int open_options = kyotocabinet::PolyDB::OWRITER | - kyotocabinet::PolyDB::OCREATE; - int tune_options = kyotocabinet::TreeDB::TSMALL | - kyotocabinet::TreeDB::TLINEAR; + int open_options = + kyotocabinet::PolyDB::OWRITER | kyotocabinet::PolyDB::OCREATE; + int tune_options = + kyotocabinet::TreeDB::TSMALL | kyotocabinet::TreeDB::TLINEAR; if (FLAGS_compression) { tune_options |= kyotocabinet::TreeDB::TCOMPRESS; db_->tune_compressor(&comp_); @@ -412,7 +407,7 @@ class Benchmark { db_->tune_options(tune_options); db_->tune_page_cache(FLAGS_cache_size); db_->tune_page(FLAGS_page_size); - db_->tune_map(256LL<<20); + db_->tune_map(256LL << 20); if (sync) { open_options |= kyotocabinet::PolyDB::OAUTOSYNC; } @@ -421,8 +416,8 @@ class Benchmark { } } - void Write(bool sync, Order order, DBState state, - int num_entries, int value_size, int entries_per_batch) { + void Write(bool sync, Order order, DBState state, int num_entries, + int value_size, int entries_per_batch) { // Create new database if state == FRESH if (state == FRESH) { if (FLAGS_use_existing_db) { @@ -430,7 +425,7 @@ class Benchmark { return; } delete db_; - db_ = NULL; + db_ = nullptr; Open(sync); Start(); // Do not count time taken to destroy/open } @@ -442,8 +437,7 @@ class Benchmark { } // Write to database - for (int i = 0; i < num_entries; i++) - { + for (int i = 0; i < num_entries; i++) { const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries); char key[100]; snprintf(key, sizeof(key), "%016d", k); @@ -516,10 +510,10 @@ int main(int argc, char** argv) { } // Choose a location for the test database if none given with --db= - if (FLAGS_db == NULL) { - leveldb::Env::Default()->GetTestDirectory(&default_db_path); - default_db_path += "/dbbench"; - FLAGS_db = default_db_path.c_str(); + if (FLAGS_db == nullptr) { + leveldb::Env::Default()->GetTestDirectory(&default_db_path); + default_db_path += "/dbbench"; + FLAGS_db = default_db_path.c_str(); } leveldb::Benchmark benchmark; diff --git a/src/leveldb/build_detect_platform b/src/leveldb/build_detect_platform deleted file mode 100755 index be6c13848..000000000 --- a/src/leveldb/build_detect_platform +++ /dev/null @@ -1,222 +0,0 @@ -#!/bin/sh -# -# Detects OS we're compiling on and outputs a file specified by the first -# argument, which in turn gets read while processing Makefile. -# -# The output will set the following variables: -# CC C Compiler path -# CXX C++ Compiler path -# PLATFORM_LDFLAGS Linker flags -# PLATFORM_LIBS Libraries flags -# PLATFORM_SHARED_EXT Extension for shared libraries -# PLATFORM_SHARED_LDFLAGS Flags for building shared library -# This flag is embedded just before the name -# of the shared library without intervening spaces -# PLATFORM_SHARED_CFLAGS Flags for compiling objects for shared library -# PLATFORM_CCFLAGS C compiler flags -# PLATFORM_CXXFLAGS C++ compiler flags. Will contain: -# PLATFORM_SHARED_VERSIONED Set to 'true' if platform supports versioned -# shared libraries, empty otherwise. -# -# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following: -# -# -DLEVELDB_ATOMIC_PRESENT if is present -# -DLEVELDB_PLATFORM_POSIX for Posix-based platforms -# -DSNAPPY if the Snappy library is present -# - -OUTPUT=$1 -PREFIX=$2 -if test -z "$OUTPUT" || test -z "$PREFIX"; then - echo "usage: $0 " >&2 - exit 1 -fi - -# Delete existing output, if it exists -rm -f $OUTPUT -touch $OUTPUT - -if test -z "$CC"; then - CC=cc -fi - -if test -z "$CXX"; then - CXX=g++ -fi - -if test -z "$TMPDIR"; then - TMPDIR=/tmp -fi - -# Detect OS -if test -z "$TARGET_OS"; then - TARGET_OS=`uname -s` -fi - -COMMON_FLAGS= -CROSS_COMPILE= -PLATFORM_CCFLAGS= -PLATFORM_CXXFLAGS="-std=c++17" -PLATFORM_LDFLAGS= -PLATFORM_LIBS= -PLATFORM_SHARED_EXT="so" -PLATFORM_SHARED_LDFLAGS="-shared -Wl,-soname -Wl," -PLATFORM_SHARED_CFLAGS="-fPIC" -PLATFORM_SHARED_VERSIONED=true - -MEMCMP_FLAG= -if [ "$CXX" = "g++" ]; then - # Use libc's memcmp instead of GCC's memcmp. This results in ~40% - # performance improvement on readrandom under gcc 4.4.3 on Linux/x86. - MEMCMP_FLAG="-fno-builtin-memcmp" -fi - -case "$TARGET_OS" in - CYGWIN_*) - PLATFORM=OS_LINUX - COMMON_FLAGS="$MEMCMP_FLAG -lpthread -DOS_LINUX -DCYGWIN" - PLATFORM_LDFLAGS="-lpthread" - PORT_FILE=port/port_posix.cc - ;; - Darwin) - PLATFORM=OS_MACOSX - COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" - PLATFORM_SHARED_EXT=dylib - [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` - PLATFORM_SHARED_LDFLAGS="-dynamiclib -install_name $INSTALL_PATH/" - PORT_FILE=port/port_posix.cc - ;; - Linux) - PLATFORM=OS_LINUX - COMMON_FLAGS="$MEMCMP_FLAG -pthread -DOS_LINUX" - PLATFORM_LDFLAGS="-pthread" - PORT_FILE=port/port_posix.cc - ;; - SunOS) - PLATFORM=OS_SOLARIS - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_SOLARIS" - PLATFORM_LIBS="-lpthread -lrt" - PORT_FILE=port/port_posix.cc - ;; - FreeBSD) - PLATFORM=OS_FREEBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_FREEBSD" - PLATFORM_LIBS="-lpthread" - PORT_FILE=port/port_posix.cc - ;; - GNU/kFreeBSD) - PLATFORM=OS_KFREEBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_KFREEBSD" - PLATFORM_LIBS="-lpthread" - PORT_FILE=port/port_posix.cc - ;; - NetBSD) - PLATFORM=OS_NETBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_NETBSD" - PLATFORM_LIBS="-lpthread -lgcc_s" - PORT_FILE=port/port_posix.cc - ;; - OpenBSD) - PLATFORM=OS_OPENBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_OPENBSD" - PLATFORM_LDFLAGS="-pthread" - PORT_FILE=port/port_posix.cc - ;; - DragonFly) - PLATFORM=OS_DRAGONFLYBSD - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_DRAGONFLYBSD" - PLATFORM_LIBS="-lpthread" - PORT_FILE=port/port_posix.cc - ;; - OS_ANDROID_CROSSCOMPILE) - PLATFORM=OS_ANDROID - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX" - PLATFORM_LDFLAGS="" # All pthread features are in the Android C library - PORT_FILE=port/port_posix.cc - CROSS_COMPILE=true - ;; - HP-UX) - PLATFORM=OS_HPUX - COMMON_FLAGS="$MEMCMP_FLAG -D_REENTRANT -DOS_HPUX" - PLATFORM_LDFLAGS="-pthread" - PORT_FILE=port/port_posix.cc - # man ld: +h internal_name - PLATFORM_SHARED_LDFLAGS="-shared -Wl,+h -Wl," - ;; - IOS) - PLATFORM=IOS - COMMON_FLAGS="$MEMCMP_FLAG -DOS_MACOSX" - [ -z "$INSTALL_PATH" ] && INSTALL_PATH=`pwd` - PORT_FILE=port/port_posix.cc - PLATFORM_SHARED_EXT= - PLATFORM_SHARED_LDFLAGS= - PLATFORM_SHARED_CFLAGS= - PLATFORM_SHARED_VERSIONED= - ;; - OS_WINDOWS_CROSSCOMPILE | NATIVE_WINDOWS) - PLATFORM=OS_WINDOWS - COMMON_FLAGS="-fno-builtin-memcmp -D_REENTRANT -DOS_WINDOWS -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1" - PLATFORM_SOURCES="util/env_win.cc" - PLATFORM_LIBS="-lshlwapi" - PORT_FILE=port/port_win.cc - CROSS_COMPILE=true - ;; - *) - echo "Unknown platform!" >&2 - exit 1 -esac - -# We want to make a list of all cc files within util, db, table, and helpers -# except for the test and benchmark files. By default, find will output a list -# of all files matching either rule, so we need to append -print to make the -# prune take effect. -DIRS="$PREFIX/db $PREFIX/util $PREFIX/table" - -set -f # temporarily disable globbing so that our patterns aren't expanded -PRUNE_TEST="-name *test*.cc -prune" -PRUNE_BENCH="-name *_bench.cc -prune" -PRUNE_TOOL="-name leveldb_main.cc -prune" -PORTABLE_FILES=`find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_TOOL -o -name '*.cc' -print | sort | sed "s,^$PREFIX/,," | tr "\n" " "` - -set +f # re-enable globbing - -# The sources consist of the portable files, plus the platform-specific port -# file. -echo "SOURCES=$PORTABLE_FILES $PORT_FILE" >> $OUTPUT -echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> $OUTPUT - -if [ "$CROSS_COMPILE" = "true" ]; then - # Cross-compiling; do not try any compilation tests. - true -else - CXXOUTPUT="${TMPDIR}/leveldb_build_detect_platform-cxx.$$" - - # As -std=c++17 works, use as fallback for when memory barriers - # are not available. - COMMON_FLAGS="$COMMON_FLAGS -DLEVELDB_PLATFORM_POSIX -DLEVELDB_ATOMIC_PRESENT" - - # Test whether tcmalloc is available - $CXX $CXXFLAGS -x c++ - -o $CXXOUTPUT -ltcmalloc 2>/dev/null </dev/null -fi - -PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS" -PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS" - -echo "CC=$CC" >> $OUTPUT -echo "CXX=$CXX" >> $OUTPUT -echo "PLATFORM=$PLATFORM" >> $OUTPUT -echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> $OUTPUT -echo "PLATFORM_LIBS=$PLATFORM_LIBS" >> $OUTPUT -echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> $OUTPUT -echo "PLATFORM_CXXFLAGS=$PLATFORM_CXXFLAGS" >> $OUTPUT -echo "PLATFORM_SHARED_CFLAGS=$PLATFORM_SHARED_CFLAGS" >> $OUTPUT -echo "PLATFORM_SHARED_EXT=$PLATFORM_SHARED_EXT" >> $OUTPUT -echo "PLATFORM_SHARED_LDFLAGS=$PLATFORM_SHARED_LDFLAGS" >> $OUTPUT -echo "PLATFORM_SHARED_VERSIONED=$PLATFORM_SHARED_VERSIONED" >> $OUTPUT diff --git a/src/leveldb/cmake/leveldbConfig.cmake b/src/leveldb/cmake/leveldbConfig.cmake new file mode 100644 index 000000000..eea6e5c47 --- /dev/null +++ b/src/leveldb/cmake/leveldbConfig.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/leveldbTargets.cmake") diff --git a/src/leveldb/db/autocompact_test.cc b/src/leveldb/db/autocompact_test.cc index d20a2362c..e6c97a05a 100644 --- a/src/leveldb/db/autocompact_test.cc +++ b/src/leveldb/db/autocompact_test.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include "leveldb/db.h" #include "db/db_impl.h" #include "leveldb/cache.h" +#include "leveldb/db.h" #include "util/testharness.h" #include "util/testutil.h" @@ -12,11 +12,6 @@ namespace leveldb { class AutoCompactTest { public: - std::string dbname_; - Cache* tiny_cache_; - Options options_; - DB* db_; - AutoCompactTest() { dbname_ = test::TmpDir() + "/autocompact_test"; tiny_cache_ = NewLRUCache(100); @@ -47,6 +42,12 @@ class AutoCompactTest { } void DoReads(int n); + + private: + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; }; static const int kValueSize = 200 * 1024; @@ -81,17 +82,16 @@ void AutoCompactTest::DoReads(int n) { ASSERT_LT(read, 100) << "Taking too long to compact"; Iterator* iter = db_->NewIterator(ReadOptions()); for (iter->SeekToFirst(); - iter->Valid() && iter->key().ToString() < limit_key; - iter->Next()) { + iter->Valid() && iter->key().ToString() < limit_key; iter->Next()) { // Drop data } delete iter; // Wait a little bit to allow any triggered compactions to complete. Env::Default()->SleepForMicroseconds(1000000); uint64_t size = Size(Key(0), Key(n)); - fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", - read+1, size/1048576.0, Size(Key(n), Key(kCount))/1048576.0); - if (size <= initial_size/10) { + fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", read + 1, + size / 1048576.0, Size(Key(n), Key(kCount)) / 1048576.0); + if (size <= initial_size / 10) { break; } } @@ -100,19 +100,13 @@ void AutoCompactTest::DoReads(int n) { // is pretty much unchanged. const int64_t final_other_size = Size(Key(n), Key(kCount)); ASSERT_LE(final_other_size, initial_other_size + 1048576); - ASSERT_GE(final_other_size, initial_other_size/5 - 1048576); + ASSERT_GE(final_other_size, initial_other_size / 5 - 1048576); } -TEST(AutoCompactTest, ReadAll) { - DoReads(kCount); -} +TEST(AutoCompactTest, ReadAll) { DoReads(kCount); } -TEST(AutoCompactTest, ReadHalf) { - DoReads(kCount/2); -} +TEST(AutoCompactTest, ReadHalf) { DoReads(kCount / 2); } } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/builder.cc b/src/leveldb/db/builder.cc index f41988219..9520ee453 100644 --- a/src/leveldb/db/builder.cc +++ b/src/leveldb/db/builder.cc @@ -4,8 +4,8 @@ #include "db/builder.h" -#include "db/filename.h" #include "db/dbformat.h" +#include "db/filename.h" #include "db/table_cache.h" #include "db/version_edit.h" #include "leveldb/db.h" @@ -14,12 +14,8 @@ namespace leveldb { -Status BuildTable(const std::string& dbname, - Env* env, - const Options& options, - TableCache* table_cache, - Iterator* iter, - FileMetaData* meta) { +Status BuildTable(const std::string& dbname, Env* env, const Options& options, + TableCache* table_cache, Iterator* iter, FileMetaData* meta) { Status s; meta->file_size = 0; iter->SeekToFirst(); @@ -41,14 +37,10 @@ Status BuildTable(const std::string& dbname, } // Finish and check for builder errors + s = builder->Finish(); if (s.ok()) { - s = builder->Finish(); - if (s.ok()) { - meta->file_size = builder->FileSize(); - assert(meta->file_size > 0); - } - } else { - builder->Abandon(); + meta->file_size = builder->FileSize(); + assert(meta->file_size > 0); } delete builder; @@ -60,12 +52,11 @@ Status BuildTable(const std::string& dbname, s = file->Close(); } delete file; - file = NULL; + file = nullptr; if (s.ok()) { // Verify that the table is usable - Iterator* it = table_cache->NewIterator(ReadOptions(), - meta->number, + Iterator* it = table_cache->NewIterator(ReadOptions(), meta->number, meta->file_size); s = it->status(); delete it; diff --git a/src/leveldb/db/builder.h b/src/leveldb/db/builder.h index 62431fcf4..7bd0b8049 100644 --- a/src/leveldb/db/builder.h +++ b/src/leveldb/db/builder.h @@ -22,12 +22,8 @@ class VersionEdit; // *meta will be filled with metadata about the generated table. // If no data is present in *iter, meta->file_size will be set to // zero, and no Table file will be produced. -extern Status BuildTable(const std::string& dbname, - Env* env, - const Options& options, - TableCache* table_cache, - Iterator* iter, - FileMetaData* meta); +Status BuildTable(const std::string& dbname, Env* env, const Options& options, + TableCache* table_cache, Iterator* iter, FileMetaData* meta); } // namespace leveldb diff --git a/src/leveldb/db/c.cc b/src/leveldb/db/c.cc index 08ff0ad90..3a492f9ac 100644 --- a/src/leveldb/db/c.cc +++ b/src/leveldb/db/c.cc @@ -4,8 +4,9 @@ #include "leveldb/c.h" -#include -#include +#include +#include + #include "leveldb/cache.h" #include "leveldb/comparator.h" #include "leveldb/db.h" @@ -43,69 +44,72 @@ using leveldb::WriteOptions; extern "C" { -struct leveldb_t { DB* rep; }; -struct leveldb_iterator_t { Iterator* rep; }; -struct leveldb_writebatch_t { WriteBatch rep; }; -struct leveldb_snapshot_t { const Snapshot* rep; }; -struct leveldb_readoptions_t { ReadOptions rep; }; -struct leveldb_writeoptions_t { WriteOptions rep; }; -struct leveldb_options_t { Options rep; }; -struct leveldb_cache_t { Cache* rep; }; -struct leveldb_seqfile_t { SequentialFile* rep; }; -struct leveldb_randomfile_t { RandomAccessFile* rep; }; -struct leveldb_writablefile_t { WritableFile* rep; }; -struct leveldb_logger_t { Logger* rep; }; -struct leveldb_filelock_t { FileLock* rep; }; +struct leveldb_t { + DB* rep; +}; +struct leveldb_iterator_t { + Iterator* rep; +}; +struct leveldb_writebatch_t { + WriteBatch rep; +}; +struct leveldb_snapshot_t { + const Snapshot* rep; +}; +struct leveldb_readoptions_t { + ReadOptions rep; +}; +struct leveldb_writeoptions_t { + WriteOptions rep; +}; +struct leveldb_options_t { + Options rep; +}; +struct leveldb_cache_t { + Cache* rep; +}; +struct leveldb_seqfile_t { + SequentialFile* rep; +}; +struct leveldb_randomfile_t { + RandomAccessFile* rep; +}; +struct leveldb_writablefile_t { + WritableFile* rep; +}; +struct leveldb_logger_t { + Logger* rep; +}; +struct leveldb_filelock_t { + FileLock* rep; +}; struct leveldb_comparator_t : public Comparator { - void* state_; - void (*destructor_)(void*); - int (*compare_)( - void*, - const char* a, size_t alen, - const char* b, size_t blen); - const char* (*name_)(void*); + ~leveldb_comparator_t() override { (*destructor_)(state_); } - virtual ~leveldb_comparator_t() { - (*destructor_)(state_); - } - - virtual int Compare(const Slice& a, const Slice& b) const { + int Compare(const Slice& a, const Slice& b) const override { return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); } - virtual const char* Name() const { - return (*name_)(state_); - } + const char* Name() const override { return (*name_)(state_); } // No-ops since the C binding does not support key shortening methods. - virtual void FindShortestSeparator(std::string*, const Slice&) const { } - virtual void FindShortSuccessor(std::string* key) const { } -}; + void FindShortestSeparator(std::string*, const Slice&) const override {} + void FindShortSuccessor(std::string* key) const override {} -struct leveldb_filterpolicy_t : public FilterPolicy { void* state_; void (*destructor_)(void*); + int (*compare_)(void*, const char* a, size_t alen, const char* b, + size_t blen); const char* (*name_)(void*); - char* (*create_)( - void*, - const char* const* key_array, const size_t* key_length_array, - int num_keys, - size_t* filter_length); - unsigned char (*key_match_)( - void*, - const char* key, size_t length, - const char* filter, size_t filter_length); - - virtual ~leveldb_filterpolicy_t() { - (*destructor_)(state_); - } +}; - virtual const char* Name() const { - return (*name_)(state_); - } +struct leveldb_filterpolicy_t : public FilterPolicy { + ~leveldb_filterpolicy_t() override { (*destructor_)(state_); } - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + const char* Name() const override { return (*name_)(state_); } + + void CreateFilter(const Slice* keys, int n, std::string* dst) const override { std::vector key_pointers(n); std::vector key_sizes(n); for (int i = 0; i < n; i++) { @@ -118,10 +122,19 @@ struct leveldb_filterpolicy_t : public FilterPolicy { free(filter); } - virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { - return (*key_match_)(state_, key.data(), key.size(), - filter.data(), filter.size()); + bool KeyMayMatch(const Slice& key, const Slice& filter) const override { + return (*key_match_)(state_, key.data(), key.size(), filter.data(), + filter.size()); } + + void* state_; + void (*destructor_)(void*); + const char* (*name_)(void*); + char* (*create_)(void*, const char* const* key_array, + const size_t* key_length_array, int num_keys, + size_t* filter_length); + uint8_t (*key_match_)(void*, const char* key, size_t length, + const char* filter, size_t filter_length); }; struct leveldb_env_t { @@ -130,10 +143,10 @@ struct leveldb_env_t { }; static bool SaveError(char** errptr, const Status& s) { - assert(errptr != NULL); + assert(errptr != nullptr); if (s.ok()) { return false; - } else if (*errptr == NULL) { + } else if (*errptr == nullptr) { *errptr = strdup(s.ToString().c_str()); } else { // TODO(sanjay): Merge with existing error? @@ -149,13 +162,11 @@ static char* CopyString(const std::string& str) { return result; } -leveldb_t* leveldb_open( - const leveldb_options_t* options, - const char* name, - char** errptr) { +leveldb_t* leveldb_open(const leveldb_options_t* options, const char* name, + char** errptr) { DB* db; if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { - return NULL; + return nullptr; } leveldb_t* result = new leveldb_t; result->rep = db; @@ -167,40 +178,27 @@ void leveldb_close(leveldb_t* db) { delete db; } -void leveldb_put( - leveldb_t* db, - const leveldb_writeoptions_t* options, - const char* key, size_t keylen, - const char* val, size_t vallen, - char** errptr) { +void leveldb_put(leveldb_t* db, const leveldb_writeoptions_t* options, + const char* key, size_t keylen, const char* val, size_t vallen, + char** errptr) { SaveError(errptr, db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); } -void leveldb_delete( - leveldb_t* db, - const leveldb_writeoptions_t* options, - const char* key, size_t keylen, - char** errptr) { +void leveldb_delete(leveldb_t* db, const leveldb_writeoptions_t* options, + const char* key, size_t keylen, char** errptr) { SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); } - -void leveldb_write( - leveldb_t* db, - const leveldb_writeoptions_t* options, - leveldb_writebatch_t* batch, - char** errptr) { +void leveldb_write(leveldb_t* db, const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, char** errptr) { SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); } -char* leveldb_get( - leveldb_t* db, - const leveldb_readoptions_t* options, - const char* key, size_t keylen, - size_t* vallen, - char** errptr) { - char* result = NULL; +char* leveldb_get(leveldb_t* db, const leveldb_readoptions_t* options, + const char* key, size_t keylen, size_t* vallen, + char** errptr) { + char* result = nullptr; std::string tmp; Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); if (s.ok()) { @@ -216,45 +214,40 @@ char* leveldb_get( } leveldb_iterator_t* leveldb_create_iterator( - leveldb_t* db, - const leveldb_readoptions_t* options) { + leveldb_t* db, const leveldb_readoptions_t* options) { leveldb_iterator_t* result = new leveldb_iterator_t; result->rep = db->rep->NewIterator(options->rep); return result; } -const leveldb_snapshot_t* leveldb_create_snapshot( - leveldb_t* db) { +const leveldb_snapshot_t* leveldb_create_snapshot(leveldb_t* db) { leveldb_snapshot_t* result = new leveldb_snapshot_t; result->rep = db->rep->GetSnapshot(); return result; } -void leveldb_release_snapshot( - leveldb_t* db, - const leveldb_snapshot_t* snapshot) { +void leveldb_release_snapshot(leveldb_t* db, + const leveldb_snapshot_t* snapshot) { db->rep->ReleaseSnapshot(snapshot->rep); delete snapshot; } -char* leveldb_property_value( - leveldb_t* db, - const char* propname) { +char* leveldb_property_value(leveldb_t* db, const char* propname) { std::string tmp; if (db->rep->GetProperty(Slice(propname), &tmp)) { // We use strdup() since we expect human readable output. return strdup(tmp.c_str()); } else { - return NULL; + return nullptr; } } -void leveldb_approximate_sizes( - leveldb_t* db, - int num_ranges, - const char* const* range_start_key, const size_t* range_start_key_len, - const char* const* range_limit_key, const size_t* range_limit_key_len, - uint64_t* sizes) { +void leveldb_approximate_sizes(leveldb_t* db, int num_ranges, + const char* const* range_start_key, + const size_t* range_start_key_len, + const char* const* range_limit_key, + const size_t* range_limit_key_len, + uint64_t* sizes) { Range* ranges = new Range[num_ranges]; for (int i = 0; i < num_ranges; i++) { ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); @@ -264,28 +257,23 @@ void leveldb_approximate_sizes( delete[] ranges; } -void leveldb_compact_range( - leveldb_t* db, - const char* start_key, size_t start_key_len, - const char* limit_key, size_t limit_key_len) { +void leveldb_compact_range(leveldb_t* db, const char* start_key, + size_t start_key_len, const char* limit_key, + size_t limit_key_len) { Slice a, b; db->rep->CompactRange( - // Pass NULL Slice if corresponding "const char*" is NULL - (start_key ? (a = Slice(start_key, start_key_len), &a) : NULL), - (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : NULL)); + // Pass null Slice if corresponding "const char*" is null + (start_key ? (a = Slice(start_key, start_key_len), &a) : nullptr), + (limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr)); } -void leveldb_destroy_db( - const leveldb_options_t* options, - const char* name, - char** errptr) { +void leveldb_destroy_db(const leveldb_options_t* options, const char* name, + char** errptr) { SaveError(errptr, DestroyDB(name, options->rep)); } -void leveldb_repair_db( - const leveldb_options_t* options, - const char* name, - char** errptr) { +void leveldb_repair_db(const leveldb_options_t* options, const char* name, + char** errptr) { SaveError(errptr, RepairDB(name, options->rep)); } @@ -294,7 +282,7 @@ void leveldb_iter_destroy(leveldb_iterator_t* iter) { delete iter; } -unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) { +uint8_t leveldb_iter_valid(const leveldb_iterator_t* iter) { return iter->rep->Valid(); } @@ -310,13 +298,9 @@ void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { iter->rep->Seek(Slice(k, klen)); } -void leveldb_iter_next(leveldb_iterator_t* iter) { - iter->rep->Next(); -} +void leveldb_iter_next(leveldb_iterator_t* iter) { iter->rep->Next(); } -void leveldb_iter_prev(leveldb_iterator_t* iter) { - iter->rep->Prev(); -} +void leveldb_iter_prev(leveldb_iterator_t* iter) { iter->rep->Prev(); } const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { Slice s = iter->rep->key(); @@ -338,41 +322,34 @@ leveldb_writebatch_t* leveldb_writebatch_create() { return new leveldb_writebatch_t; } -void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { - delete b; -} +void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { delete b; } -void leveldb_writebatch_clear(leveldb_writebatch_t* b) { - b->rep.Clear(); -} +void leveldb_writebatch_clear(leveldb_writebatch_t* b) { b->rep.Clear(); } -void leveldb_writebatch_put( - leveldb_writebatch_t* b, - const char* key, size_t klen, - const char* val, size_t vlen) { +void leveldb_writebatch_put(leveldb_writebatch_t* b, const char* key, + size_t klen, const char* val, size_t vlen) { b->rep.Put(Slice(key, klen), Slice(val, vlen)); } -void leveldb_writebatch_delete( - leveldb_writebatch_t* b, - const char* key, size_t klen) { +void leveldb_writebatch_delete(leveldb_writebatch_t* b, const char* key, + size_t klen) { b->rep.Delete(Slice(key, klen)); } -void leveldb_writebatch_iterate( - leveldb_writebatch_t* b, - void* state, - void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), - void (*deleted)(void*, const char* k, size_t klen)) { +void leveldb_writebatch_iterate(const leveldb_writebatch_t* b, void* state, + void (*put)(void*, const char* k, size_t klen, + const char* v, size_t vlen), + void (*deleted)(void*, const char* k, + size_t klen)) { class H : public WriteBatch::Handler { public: void* state_; void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); void (*deleted_)(void*, const char* k, size_t klen); - virtual void Put(const Slice& key, const Slice& value) { + void Put(const Slice& key, const Slice& value) override { (*put_)(state_, key.data(), key.size(), value.data(), value.size()); } - virtual void Delete(const Slice& key) { + void Delete(const Slice& key) override { (*deleted_)(state_, key.data(), key.size()); } }; @@ -383,47 +360,43 @@ void leveldb_writebatch_iterate( b->rep.Iterate(&handler); } -leveldb_options_t* leveldb_options_create() { - return new leveldb_options_t; +void leveldb_writebatch_append(leveldb_writebatch_t* destination, + const leveldb_writebatch_t* source) { + destination->rep.Append(source->rep); } -void leveldb_options_destroy(leveldb_options_t* options) { - delete options; -} +leveldb_options_t* leveldb_options_create() { return new leveldb_options_t; } + +void leveldb_options_destroy(leveldb_options_t* options) { delete options; } -void leveldb_options_set_comparator( - leveldb_options_t* opt, - leveldb_comparator_t* cmp) { +void leveldb_options_set_comparator(leveldb_options_t* opt, + leveldb_comparator_t* cmp) { opt->rep.comparator = cmp; } -void leveldb_options_set_filter_policy( - leveldb_options_t* opt, - leveldb_filterpolicy_t* policy) { +void leveldb_options_set_filter_policy(leveldb_options_t* opt, + leveldb_filterpolicy_t* policy) { opt->rep.filter_policy = policy; } -void leveldb_options_set_create_if_missing( - leveldb_options_t* opt, unsigned char v) { +void leveldb_options_set_create_if_missing(leveldb_options_t* opt, uint8_t v) { opt->rep.create_if_missing = v; } -void leveldb_options_set_error_if_exists( - leveldb_options_t* opt, unsigned char v) { +void leveldb_options_set_error_if_exists(leveldb_options_t* opt, uint8_t v) { opt->rep.error_if_exists = v; } -void leveldb_options_set_paranoid_checks( - leveldb_options_t* opt, unsigned char v) { +void leveldb_options_set_paranoid_checks(leveldb_options_t* opt, uint8_t v) { opt->rep.paranoid_checks = v; } void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { - opt->rep.env = (env ? env->rep : NULL); + opt->rep.env = (env ? env->rep : nullptr); } void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { - opt->rep.info_log = (l ? l->rep : NULL); + opt->rep.info_log = (l ? l->rep : nullptr); } void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { @@ -446,17 +419,18 @@ void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { opt->rep.block_restart_interval = n; } +void leveldb_options_set_max_file_size(leveldb_options_t* opt, size_t s) { + opt->rep.max_file_size = s; +} + void leveldb_options_set_compression(leveldb_options_t* opt, int t) { opt->rep.compression = static_cast(t); } leveldb_comparator_t* leveldb_comparator_create( - void* state, - void (*destructor)(void*), - int (*compare)( - void*, - const char* a, size_t alen, - const char* b, size_t blen), + void* state, void (*destructor)(void*), + int (*compare)(void*, const char* a, size_t alen, const char* b, + size_t blen), const char* (*name)(void*)) { leveldb_comparator_t* result = new leveldb_comparator_t; result->state_ = state; @@ -466,22 +440,15 @@ leveldb_comparator_t* leveldb_comparator_create( return result; } -void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { - delete cmp; -} +void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { delete cmp; } leveldb_filterpolicy_t* leveldb_filterpolicy_create( - void* state, - void (*destructor)(void*), - char* (*create_filter)( - void*, - const char* const* key_array, const size_t* key_length_array, - int num_keys, - size_t* filter_length), - unsigned char (*key_may_match)( - void*, - const char* key, size_t length, - const char* filter, size_t filter_length), + void* state, void (*destructor)(void*), + char* (*create_filter)(void*, const char* const* key_array, + const size_t* key_length_array, int num_keys, + size_t* filter_length), + uint8_t (*key_may_match)(void*, const char* key, size_t length, + const char* filter, size_t filter_length), const char* (*name)(void*)) { leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t; result->state_ = state; @@ -501,7 +468,8 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { // they delegate to a NewBloomFilterPolicy() instead of user // supplied C functions. struct Wrapper : public leveldb_filterpolicy_t { - const FilterPolicy* rep_; + static void DoNothing(void*) {} + ~Wrapper() { delete rep_; } const char* Name() const { return rep_->Name(); } void CreateFilter(const Slice* keys, int n, std::string* dst) const { @@ -510,11 +478,12 @@ leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) { bool KeyMayMatch(const Slice& key, const Slice& filter) const { return rep_->KeyMayMatch(key, filter); } - static void DoNothing(void*) { } + + const FilterPolicy* rep_; }; Wrapper* wrapper = new Wrapper; wrapper->rep_ = NewBloomFilterPolicy(bits_per_key); - wrapper->state_ = NULL; + wrapper->state_ = nullptr; wrapper->destructor_ = &Wrapper::DoNothing; return wrapper; } @@ -523,37 +492,29 @@ leveldb_readoptions_t* leveldb_readoptions_create() { return new leveldb_readoptions_t; } -void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { - delete opt; -} +void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { delete opt; } -void leveldb_readoptions_set_verify_checksums( - leveldb_readoptions_t* opt, - unsigned char v) { +void leveldb_readoptions_set_verify_checksums(leveldb_readoptions_t* opt, + uint8_t v) { opt->rep.verify_checksums = v; } -void leveldb_readoptions_set_fill_cache( - leveldb_readoptions_t* opt, unsigned char v) { +void leveldb_readoptions_set_fill_cache(leveldb_readoptions_t* opt, uint8_t v) { opt->rep.fill_cache = v; } -void leveldb_readoptions_set_snapshot( - leveldb_readoptions_t* opt, - const leveldb_snapshot_t* snap) { - opt->rep.snapshot = (snap ? snap->rep : NULL); +void leveldb_readoptions_set_snapshot(leveldb_readoptions_t* opt, + const leveldb_snapshot_t* snap) { + opt->rep.snapshot = (snap ? snap->rep : nullptr); } leveldb_writeoptions_t* leveldb_writeoptions_create() { return new leveldb_writeoptions_t; } -void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { - delete opt; -} +void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { delete opt; } -void leveldb_writeoptions_set_sync( - leveldb_writeoptions_t* opt, unsigned char v) { +void leveldb_writeoptions_set_sync(leveldb_writeoptions_t* opt, uint8_t v) { opt->rep.sync = v; } @@ -580,16 +541,22 @@ void leveldb_env_destroy(leveldb_env_t* env) { delete env; } -void leveldb_free(void* ptr) { - free(ptr); -} +char* leveldb_env_get_test_directory(leveldb_env_t* env) { + std::string result; + if (!env->rep->GetTestDirectory(&result).ok()) { + return nullptr; + } -int leveldb_major_version() { - return kMajorVersion; + char* buffer = static_cast(malloc(result.size() + 1)); + memcpy(buffer, result.data(), result.size()); + buffer[result.size()] = '\0'; + return buffer; } -int leveldb_minor_version() { - return kMinorVersion; -} +void leveldb_free(void* ptr) { free(ptr); } + +int leveldb_major_version() { return kMajorVersion; } + +int leveldb_minor_version() { return kMinorVersion; } } // end extern "C" diff --git a/src/leveldb/db/c_test.c b/src/leveldb/db/c_test.c index 7cd5ee020..16c77eed6 100644 --- a/src/leveldb/db/c_test.c +++ b/src/leveldb/db/c_test.c @@ -8,24 +8,14 @@ #include #include #include -#include -#include const char* phase = ""; -static char dbname[200]; static void StartPhase(const char* name) { fprintf(stderr, "=== Test %s\n", name); phase = name; } -static const char* GetTempDir(void) { - const char* ret = getenv("TEST_TMPDIR"); - if (ret == NULL || ret[0] == '\0') - ret = "/tmp"; - return ret; -} - #define CheckNoError(err) \ if ((err) != NULL) { \ fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \ @@ -130,7 +120,7 @@ static const char* CmpName(void* arg) { } // Custom filter policy -static unsigned char fake_filter_result = 1; +static uint8_t fake_filter_result = 1; static void FilterDestroy(void* arg) { } static const char* FilterName(void* arg) { return "TestFilter"; @@ -145,10 +135,8 @@ static char* FilterCreate( memcpy(result, "fake", 4); return result; } -unsigned char FilterKeyMatch( - void* arg, - const char* key, size_t length, - const char* filter, size_t filter_length) { +uint8_t FilterKeyMatch(void* arg, const char* key, size_t length, + const char* filter, size_t filter_length) { CheckCondition(filter_length == 4); CheckCondition(memcmp(filter, "fake", 4) == 0); return fake_filter_result; @@ -162,21 +150,19 @@ int main(int argc, char** argv) { leveldb_options_t* options; leveldb_readoptions_t* roptions; leveldb_writeoptions_t* woptions; + char* dbname; char* err = NULL; int run = -1; CheckCondition(leveldb_major_version() >= 1); CheckCondition(leveldb_minor_version() >= 1); - snprintf(dbname, sizeof(dbname), - "%s/leveldb_c_test-%d", - GetTempDir(), - ((int) geteuid())); - StartPhase("create_objects"); cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); env = leveldb_create_default_env(); cache = leveldb_cache_create_lru(100000); + dbname = leveldb_env_get_test_directory(env); + CheckCondition(dbname != NULL); options = leveldb_options_create(); leveldb_options_set_comparator(options, cmp); @@ -189,6 +175,7 @@ int main(int argc, char** argv) { leveldb_options_set_max_open_files(options, 10); leveldb_options_set_block_size(options, 1024); leveldb_options_set_block_restart_interval(options, 8); + leveldb_options_set_max_file_size(options, 3 << 20); leveldb_options_set_compression(options, leveldb_no_compression); roptions = leveldb_readoptions_create(); @@ -239,12 +226,18 @@ int main(int argc, char** argv) { leveldb_writebatch_clear(wb); leveldb_writebatch_put(wb, "bar", 3, "b", 1); leveldb_writebatch_put(wb, "box", 3, "c", 1); - leveldb_writebatch_delete(wb, "bar", 3); + + leveldb_writebatch_t* wb2 = leveldb_writebatch_create(); + leveldb_writebatch_delete(wb2, "bar", 3); + leveldb_writebatch_append(wb, wb2); + leveldb_writebatch_destroy(wb2); + leveldb_write(db, woptions, wb, &err); CheckNoError(err); CheckGet(db, roptions, "foo", "hello"); CheckGet(db, roptions, "bar", NULL); CheckGet(db, roptions, "box", "c"); + int pos = 0; leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); CheckCondition(pos == 3); @@ -381,6 +374,7 @@ int main(int argc, char** argv) { leveldb_options_destroy(options); leveldb_readoptions_destroy(roptions); leveldb_writeoptions_destroy(woptions); + leveldb_free(dbname); leveldb_cache_destroy(cache); leveldb_comparator_destroy(cmp); leveldb_env_destroy(env); diff --git a/src/leveldb/db/corruption_test.cc b/src/leveldb/db/corruption_test.cc index 96afc6891..42f5237c6 100644 --- a/src/leveldb/db/corruption_test.cc +++ b/src/leveldb/db/corruption_test.cc @@ -2,20 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include "leveldb/db.h" - -#include -#include -#include #include -#include "leveldb/cache.h" -#include "leveldb/env.h" -#include "leveldb/table.h" -#include "leveldb/write_batch.h" + #include "db/db_impl.h" #include "db/filename.h" #include "db/log_format.h" #include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" #include "util/logging.h" #include "util/testharness.h" #include "util/testutil.h" @@ -26,44 +22,35 @@ static const int kValueSize = 1000; class CorruptionTest { public: - test::ErrorEnv env_; - std::string dbname_; - Cache* tiny_cache_; - Options options_; - DB* db_; - - CorruptionTest() { - tiny_cache_ = NewLRUCache(100); + CorruptionTest() + : db_(nullptr), + dbname_("/memenv/corruption_test"), + tiny_cache_(NewLRUCache(100)) { options_.env = &env_; options_.block_cache = tiny_cache_; - dbname_ = test::TmpDir() + "/db_test"; DestroyDB(dbname_, options_); - db_ = NULL; options_.create_if_missing = true; Reopen(); options_.create_if_missing = false; } ~CorruptionTest() { - delete db_; - DestroyDB(dbname_, Options()); - delete tiny_cache_; + delete db_; + delete tiny_cache_; } Status TryReopen() { delete db_; - db_ = NULL; + db_ = nullptr; return DB::Open(options_, dbname_, &db_); } - void Reopen() { - ASSERT_OK(TryReopen()); - } + void Reopen() { ASSERT_OK(TryReopen()); } void RepairDB() { delete db_; - db_ = NULL; + db_ = nullptr; ASSERT_OK(::leveldb::RepairDB(dbname_, options_)); } @@ -71,7 +58,7 @@ class CorruptionTest { std::string key_space, value_space; WriteBatch batch; for (int i = 0; i < n; i++) { - //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); + // if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n); Slice key = Key(i, &key_space); batch.Clear(); batch.Put(key, Value(i, &value_space)); @@ -100,8 +87,7 @@ class CorruptionTest { // Ignore boundary keys. continue; } - if (!ConsumeDecimalNumber(&in, &key) || - !in.empty() || + if (!ConsumeDecimalNumber(&in, &key) || !in.empty() || key < next_expected) { bad_keys++; continue; @@ -126,14 +112,13 @@ class CorruptionTest { void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { // Pick file to corrupt std::vector filenames; - ASSERT_OK(env_.GetChildren(dbname_, &filenames)); + ASSERT_OK(env_.target()->GetChildren(dbname_, &filenames)); uint64_t number; FileType type; std::string fname; int picked_number = -1; for (size_t i = 0; i < filenames.size(); i++) { - if (ParseFileName(filenames[i], &number, &type) && - type == filetype && + if (ParseFileName(filenames[i], &number, &type) && type == filetype && int(number) > picked_number) { // Pick latest file fname = dbname_ + "/" + filenames[i]; picked_number = number; @@ -141,35 +126,32 @@ class CorruptionTest { } ASSERT_TRUE(!fname.empty()) << filetype; - struct stat sbuf; - if (stat(fname.c_str(), &sbuf) != 0) { - const char* msg = strerror(errno); - ASSERT_TRUE(false) << fname << ": " << msg; - } + uint64_t file_size; + ASSERT_OK(env_.target()->GetFileSize(fname, &file_size)); if (offset < 0) { // Relative to end of file; make it absolute - if (-offset > sbuf.st_size) { + if (-offset > file_size) { offset = 0; } else { - offset = sbuf.st_size + offset; + offset = file_size + offset; } } - if (offset > sbuf.st_size) { - offset = sbuf.st_size; + if (offset > file_size) { + offset = file_size; } - if (offset + bytes_to_corrupt > sbuf.st_size) { - bytes_to_corrupt = sbuf.st_size - offset; + if (offset + bytes_to_corrupt > file_size) { + bytes_to_corrupt = file_size - offset; } // Do it std::string contents; - Status s = ReadFileToString(Env::Default(), fname, &contents); + Status s = ReadFileToString(env_.target(), fname, &contents); ASSERT_TRUE(s.ok()) << s.ToString(); for (int i = 0; i < bytes_to_corrupt; i++) { contents[i + offset] ^= 0x80; } - s = WriteStringToFile(Env::Default(), contents, fname); + s = WriteStringToFile(env_.target(), contents, fname); ASSERT_TRUE(s.ok()) << s.ToString(); } @@ -197,12 +179,20 @@ class CorruptionTest { Random r(k); return test::RandomString(&r, kValueSize, storage); } + + test::ErrorEnv env_; + Options options_; + DB* db_; + + private: + std::string dbname_; + Cache* tiny_cache_; }; TEST(CorruptionTest, Recovery) { Build(100); Check(100, 100); - Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record + Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block Reopen(); @@ -237,8 +227,8 @@ TEST(CorruptionTest, TableFile) { Build(100); DBImpl* dbi = reinterpret_cast(db_); dbi->TEST_CompactMemTable(); - dbi->TEST_CompactRange(0, NULL, NULL); - dbi->TEST_CompactRange(1, NULL, NULL); + dbi->TEST_CompactRange(0, nullptr, nullptr); + dbi->TEST_CompactRange(1, nullptr, nullptr); Corrupt(kTableFile, 100, 1); Check(90, 99); @@ -251,8 +241,8 @@ TEST(CorruptionTest, TableFileRepair) { Build(100); DBImpl* dbi = reinterpret_cast(db_); dbi->TEST_CompactMemTable(); - dbi->TEST_CompactRange(0, NULL, NULL); - dbi->TEST_CompactRange(1, NULL, NULL); + dbi->TEST_CompactRange(0, nullptr, nullptr); + dbi->TEST_CompactRange(1, nullptr, nullptr); Corrupt(kTableFile, 100, 1); RepairDB(); @@ -302,7 +292,7 @@ TEST(CorruptionTest, CorruptedDescriptor) { ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello")); DBImpl* dbi = reinterpret_cast(db_); dbi->TEST_CompactMemTable(); - dbi->TEST_CompactRange(0, NULL, NULL); + dbi->TEST_CompactRange(0, nullptr, nullptr); Corrupt(kDescriptorFile, 0, 1000); Status s = TryReopen(); @@ -343,7 +333,7 @@ TEST(CorruptionTest, CompactionInputErrorParanoid) { Corrupt(kTableFile, 100, 1); env_.SleepForMicroseconds(100000); } - dbi->CompactRange(NULL, NULL); + dbi->CompactRange(nullptr, nullptr); // Write must fail because of corrupted table std::string tmp1, tmp2; @@ -369,6 +359,4 @@ TEST(CorruptionTest, UnrelatedKeys) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index 49b95953b..65e31724b 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -4,12 +4,15 @@ #include "db/db_impl.h" +#include +#include + #include +#include #include #include -#include -#include #include + #include "db/builder.h" #include "db/db_iter.h" #include "db/dbformat.h" @@ -39,16 +42,33 @@ const int kNumNonTableCacheFiles = 10; // Information kept for every waiting writer struct DBImpl::Writer { + explicit Writer(port::Mutex* mu) + : batch(nullptr), sync(false), done(false), cv(mu) {} + Status status; WriteBatch* batch; bool sync; bool done; port::CondVar cv; - - explicit Writer(port::Mutex* mu) : cv(mu) { } }; struct DBImpl::CompactionState { + // Files produced by compaction + struct Output { + uint64_t number; + uint64_t file_size; + InternalKey smallest, largest; + }; + + Output* current_output() { return &outputs[outputs.size() - 1]; } + + explicit CompactionState(Compaction* c) + : compaction(c), + smallest_snapshot(0), + outfile(nullptr), + builder(nullptr), + total_bytes(0) {} + Compaction* const compaction; // Sequence numbers < smallest_snapshot are not significant since we @@ -57,12 +77,6 @@ struct DBImpl::CompactionState { // we can drop all entries for the same key with sequence numbers < S. SequenceNumber smallest_snapshot; - // Files produced by compaction - struct Output { - uint64_t number; - uint64_t file_size; - InternalKey smallest, largest; - }; std::vector outputs; // State kept for output being generated @@ -70,19 +84,10 @@ struct DBImpl::CompactionState { TableBuilder* builder; uint64_t total_bytes; - - Output* current_output() { return &outputs[outputs.size()-1]; } - - explicit CompactionState(Compaction* c) - : compaction(c), - outfile(NULL), - builder(NULL), - total_bytes(0) { - } }; // Fix user-supplied options to be reasonable -template +template static void ClipToRange(T* ptr, V minvalue, V maxvalue) { if (static_cast(*ptr) > maxvalue) *ptr = maxvalue; if (static_cast(*ptr) < minvalue) *ptr = minvalue; @@ -93,26 +98,32 @@ Options SanitizeOptions(const std::string& dbname, const Options& src) { Options result = src; result.comparator = icmp; - result.filter_policy = (src.filter_policy != NULL) ? ipolicy : NULL; - ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); - ClipToRange(&result.write_buffer_size, 64<<10, 1<<30); - ClipToRange(&result.block_size, 1<<10, 4<<20); - if (result.info_log == NULL) { + result.filter_policy = (src.filter_policy != nullptr) ? ipolicy : nullptr; + ClipToRange(&result.max_open_files, 64 + kNumNonTableCacheFiles, 50000); + ClipToRange(&result.write_buffer_size, 64 << 10, 1 << 30); + ClipToRange(&result.max_file_size, 1 << 20, 1 << 30); + ClipToRange(&result.block_size, 1 << 10, 4 << 20); + if (result.info_log == nullptr) { // Open a log file in the same directory as the db src.env->CreateDir(dbname); // In case it does not exist src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname)); Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log); if (!s.ok()) { // No place suitable for logging - result.info_log = NULL; + result.info_log = nullptr; } } - if (result.block_cache == NULL) { + if (result.block_cache == nullptr) { result.block_cache = NewLRUCache(8 << 20); } return result; } +static int TableCacheSize(const Options& sanitized_options) { + // Reserve ten files or so for other uses and give the rest to TableCache. + return sanitized_options.max_open_files - kNumNonTableCacheFiles; +} + DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) : env_(raw_options.env), internal_comparator_(raw_options.comparator), @@ -122,45 +133,39 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) owns_info_log_(options_.info_log != raw_options.info_log), owns_cache_(options_.block_cache != raw_options.block_cache), dbname_(dbname), - db_lock_(NULL), - shutting_down_(NULL), - bg_cv_(&mutex_), - mem_(new MemTable(internal_comparator_)), - imm_(NULL), - logfile_(NULL), + table_cache_(new TableCache(dbname_, options_, TableCacheSize(options_))), + db_lock_(nullptr), + shutting_down_(false), + background_work_finished_signal_(&mutex_), + mem_(nullptr), + imm_(nullptr), + has_imm_(false), + logfile_(nullptr), logfile_number_(0), - log_(NULL), + log_(nullptr), seed_(0), tmp_batch_(new WriteBatch), - bg_compaction_scheduled_(false), - manual_compaction_(NULL) { - mem_->Ref(); - has_imm_.Release_Store(NULL); - - // Reserve ten files or so for other uses and give the rest to TableCache. - const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles; - table_cache_ = new TableCache(dbname_, &options_, table_cache_size); - - versions_ = new VersionSet(dbname_, &options_, table_cache_, - &internal_comparator_); -} + background_compaction_scheduled_(false), + manual_compaction_(nullptr), + versions_(new VersionSet(dbname_, &options_, table_cache_, + &internal_comparator_)) {} DBImpl::~DBImpl() { - // Wait for background work to finish + // Wait for background work to finish. mutex_.Lock(); - shutting_down_.Release_Store(this); // Any non-NULL value is ok - while (bg_compaction_scheduled_) { - bg_cv_.Wait(); + shutting_down_.store(true, std::memory_order_release); + while (background_compaction_scheduled_) { + background_work_finished_signal_.Wait(); } mutex_.Unlock(); - if (db_lock_ != NULL) { + if (db_lock_ != nullptr) { env_->UnlockFile(db_lock_); } delete versions_; - if (mem_ != NULL) mem_->Unref(); - if (imm_ != NULL) imm_->Unref(); + if (mem_ != nullptr) mem_->Unref(); + if (imm_ != nullptr) imm_->Unref(); delete tmp_batch_; delete log_; delete logfile_; @@ -216,6 +221,8 @@ void DBImpl::MaybeIgnoreError(Status* s) const { } void DBImpl::DeleteObsoleteFiles() { + mutex_.AssertHeld(); + if (!bg_error_.ok()) { // After a background error, we don't know whether a new version may // or may not have been committed, so we cannot safely garbage collect. @@ -227,11 +234,12 @@ void DBImpl::DeleteObsoleteFiles() { versions_->AddLiveFiles(&live); std::vector filenames; - env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose + env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose uint64_t number; FileType type; - for (size_t i = 0; i < filenames.size(); i++) { - if (ParseFileName(filenames[i], &number, &type)) { + std::vector files_to_delete; + for (std::string& filename : filenames) { + if (ParseFileName(filename, &number, &type)) { bool keep = true; switch (type) { case kLogFile: @@ -259,26 +267,34 @@ void DBImpl::DeleteObsoleteFiles() { } if (!keep) { + files_to_delete.push_back(std::move(filename)); if (type == kTableFile) { table_cache_->Evict(number); } - Log(options_.info_log, "Delete type=%d #%lld\n", - int(type), + Log(options_.info_log, "Delete type=%d #%lld\n", static_cast(type), static_cast(number)); - env_->DeleteFile(dbname_ + "/" + filenames[i]); } } } + + // While deleting all files unblock other threads. All files being deleted + // have unique names which will not collide with newly created files and + // are therefore safe to delete while allowing other threads to proceed. + mutex_.Unlock(); + for (const std::string& filename : files_to_delete) { + env_->DeleteFile(dbname_ + "/" + filename); + } + mutex_.Lock(); } -Status DBImpl::Recover(VersionEdit* edit) { +Status DBImpl::Recover(VersionEdit* edit, bool* save_manifest) { mutex_.AssertHeld(); // Ignore error from CreateDir since the creation of the DB is // committed only when the descriptor is created, and this directory // may already exist from a previous failed creation attempt. env_->CreateDir(dbname_); - assert(db_lock_ == NULL); + assert(db_lock_ == nullptr); Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); if (!s.ok()) { return s; @@ -296,82 +312,85 @@ Status DBImpl::Recover(VersionEdit* edit) { } } else { if (options_.error_if_exists) { - return Status::InvalidArgument( - dbname_, "exists (error_if_exists is true)"); + return Status::InvalidArgument(dbname_, + "exists (error_if_exists is true)"); } } - s = versions_->Recover(); - if (s.ok()) { - SequenceNumber max_sequence(0); - - // Recover from all newer log files than the ones named in the - // descriptor (new log files may have been added by the previous - // incarnation without registering them in the descriptor). - // - // Note that PrevLogNumber() is no longer used, but we pay - // attention to it in case we are recovering a database - // produced by an older version of leveldb. - const uint64_t min_log = versions_->LogNumber(); - const uint64_t prev_log = versions_->PrevLogNumber(); - std::vector filenames; - s = env_->GetChildren(dbname_, &filenames); + s = versions_->Recover(save_manifest); + if (!s.ok()) { + return s; + } + SequenceNumber max_sequence(0); + + // Recover from all newer log files than the ones named in the + // descriptor (new log files may have been added by the previous + // incarnation without registering them in the descriptor). + // + // Note that PrevLogNumber() is no longer used, but we pay + // attention to it in case we are recovering a database + // produced by an older version of leveldb. + const uint64_t min_log = versions_->LogNumber(); + const uint64_t prev_log = versions_->PrevLogNumber(); + std::vector filenames; + s = env_->GetChildren(dbname_, &filenames); + if (!s.ok()) { + return s; + } + std::set expected; + versions_->AddLiveFiles(&expected); + uint64_t number; + FileType type; + std::vector logs; + for (size_t i = 0; i < filenames.size(); i++) { + if (ParseFileName(filenames[i], &number, &type)) { + expected.erase(number); + if (type == kLogFile && ((number >= min_log) || (number == prev_log))) + logs.push_back(number); + } + } + if (!expected.empty()) { + char buf[50]; + snprintf(buf, sizeof(buf), "%d missing files; e.g.", + static_cast(expected.size())); + return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); + } + + // Recover in the order in which the logs were generated + std::sort(logs.begin(), logs.end()); + for (size_t i = 0; i < logs.size(); i++) { + s = RecoverLogFile(logs[i], (i == logs.size() - 1), save_manifest, edit, + &max_sequence); if (!s.ok()) { return s; } - std::set expected; - versions_->AddLiveFiles(&expected); - uint64_t number; - FileType type; - std::vector logs; - for (size_t i = 0; i < filenames.size(); i++) { - if (ParseFileName(filenames[i], &number, &type)) { - expected.erase(number); - if (type == kLogFile && ((number >= min_log) || (number == prev_log))) - logs.push_back(number); - } - } - if (!expected.empty()) { - char buf[50]; - snprintf(buf, sizeof(buf), "%d missing files; e.g.", - static_cast(expected.size())); - return Status::Corruption(buf, TableFileName(dbname_, *(expected.begin()))); - } - - // Recover in the order in which the logs were generated - std::sort(logs.begin(), logs.end()); - for (size_t i = 0; i < logs.size(); i++) { - s = RecoverLogFile(logs[i], edit, &max_sequence); - // The previous incarnation may not have written any MANIFEST - // records after allocating this log number. So we manually - // update the file number allocation counter in VersionSet. - versions_->MarkFileNumberUsed(logs[i]); - } + // The previous incarnation may not have written any MANIFEST + // records after allocating this log number. So we manually + // update the file number allocation counter in VersionSet. + versions_->MarkFileNumberUsed(logs[i]); + } - if (s.ok()) { - if (versions_->LastSequence() < max_sequence) { - versions_->SetLastSequence(max_sequence); - } - } + if (versions_->LastSequence() < max_sequence) { + versions_->SetLastSequence(max_sequence); } - return s; + return Status::OK(); } -Status DBImpl::RecoverLogFile(uint64_t log_number, - VersionEdit* edit, +Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, + bool* save_manifest, VersionEdit* edit, SequenceNumber* max_sequence) { struct LogReporter : public log::Reader::Reporter { Env* env; Logger* info_log; const char* fname; - Status* status; // NULL if options_.paranoid_checks==false - virtual void Corruption(size_t bytes, const Status& s) { + Status* status; // null if options_.paranoid_checks==false + void Corruption(size_t bytes, const Status& s) override { Log(info_log, "%s%s: dropping %d bytes; %s", - (this->status == NULL ? "(ignoring error) " : ""), - fname, static_cast(bytes), s.ToString().c_str()); - if (this->status != NULL && this->status->ok()) *this->status = s; + (this->status == nullptr ? "(ignoring error) " : ""), fname, + static_cast(bytes), s.ToString().c_str()); + if (this->status != nullptr && this->status->ok()) *this->status = s; } }; @@ -391,31 +410,30 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, reporter.env = env_; reporter.info_log = options_.info_log; reporter.fname = fname.c_str(); - reporter.status = (options_.paranoid_checks ? &status : NULL); + reporter.status = (options_.paranoid_checks ? &status : nullptr); // We intentionally make log::Reader do checksumming even if // paranoid_checks==false so that corruptions cause entire commits // to be skipped instead of propagating bad information (like overly // large sequence numbers). - log::Reader reader(file, &reporter, true/*checksum*/, - 0/*initial_offset*/); + log::Reader reader(file, &reporter, true /*checksum*/, 0 /*initial_offset*/); Log(options_.info_log, "Recovering log #%llu", - (unsigned long long) log_number); + (unsigned long long)log_number); // Read all the records and add to a memtable std::string scratch; Slice record; WriteBatch batch; - MemTable* mem = NULL; - while (reader.ReadRecord(&record, &scratch) && - status.ok()) { + int compactions = 0; + MemTable* mem = nullptr; + while (reader.ReadRecord(&record, &scratch) && status.ok()) { if (record.size() < 12) { - reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + reporter.Corruption(record.size(), + Status::Corruption("log record too small", fname)); continue; } WriteBatchInternal::SetContents(&batch, record); - if (mem == NULL) { + if (mem == nullptr) { mem = new MemTable(internal_comparator_); mem->Ref(); } @@ -424,33 +442,59 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, if (!status.ok()) { break; } - const SequenceNumber last_seq = - WriteBatchInternal::Sequence(&batch) + - WriteBatchInternal::Count(&batch) - 1; + const SequenceNumber last_seq = WriteBatchInternal::Sequence(&batch) + + WriteBatchInternal::Count(&batch) - 1; if (last_seq > *max_sequence) { *max_sequence = last_seq; } if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) { - status = WriteLevel0Table(mem, edit, NULL); + compactions++; + *save_manifest = true; + status = WriteLevel0Table(mem, edit, nullptr); + mem->Unref(); + mem = nullptr; if (!status.ok()) { // Reflect errors immediately so that conditions like full // file-systems cause the DB::Open() to fail. break; } - mem->Unref(); - mem = NULL; } } - if (status.ok() && mem != NULL) { - status = WriteLevel0Table(mem, edit, NULL); - // Reflect errors immediately so that conditions like full - // file-systems cause the DB::Open() to fail. + delete file; + + // See if we should keep reusing the last log file. + if (status.ok() && options_.reuse_logs && last_log && compactions == 0) { + assert(logfile_ == nullptr); + assert(log_ == nullptr); + assert(mem_ == nullptr); + uint64_t lfile_size; + if (env_->GetFileSize(fname, &lfile_size).ok() && + env_->NewAppendableFile(fname, &logfile_).ok()) { + Log(options_.info_log, "Reusing old log %s \n", fname.c_str()); + log_ = new log::Writer(logfile_, lfile_size); + logfile_number_ = log_number; + if (mem != nullptr) { + mem_ = mem; + mem = nullptr; + } else { + // mem can be nullptr if lognum exists but was empty. + mem_ = new MemTable(internal_comparator_); + mem_->Ref(); + } + } + } + + if (mem != nullptr) { + // mem did not get reused; compact it. + if (status.ok()) { + *save_manifest = true; + status = WriteLevel0Table(mem, edit, nullptr); + } + mem->Unref(); } - if (mem != NULL) mem->Unref(); - delete file; return status; } @@ -463,7 +507,7 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, pending_outputs_.insert(meta.number); Iterator* iter = mem->NewIterator(); Log(options_.info_log, "Level-0 table #%llu: started", - (unsigned long long) meta.number); + (unsigned long long)meta.number); Status s; { @@ -473,24 +517,22 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, } Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s", - (unsigned long long) meta.number, - (unsigned long long) meta.file_size, + (unsigned long long)meta.number, (unsigned long long)meta.file_size, s.ToString().c_str()); delete iter; pending_outputs_.erase(meta.number); - // Note that if file_size is zero, the file has been deleted and // should not be added to the manifest. int level = 0; if (s.ok() && meta.file_size > 0) { const Slice min_user_key = meta.smallest.user_key(); const Slice max_user_key = meta.largest.user_key(); - if (base != NULL) { + if (base != nullptr) { level = base->PickLevelForMemTableOutput(min_user_key, max_user_key); } - edit->AddFile(level, meta.number, meta.file_size, - meta.smallest, meta.largest); + edit->AddFile(level, meta.number, meta.file_size, meta.smallest, + meta.largest); } CompactionStats stats; @@ -502,7 +544,7 @@ Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, void DBImpl::CompactMemTable() { mutex_.AssertHeld(); - assert(imm_ != NULL); + assert(imm_ != nullptr); // Save the contents of the memtable as a new Table VersionEdit edit; @@ -511,7 +553,7 @@ void DBImpl::CompactMemTable() { Status s = WriteLevel0Table(imm_, &edit, base); base->Unref(); - if (s.ok() && shutting_down_.Acquire_Load()) { + if (s.ok() && shutting_down_.load(std::memory_order_acquire)) { s = Status::IOError("Deleting DB during memtable compaction"); } @@ -525,8 +567,8 @@ void DBImpl::CompactMemTable() { if (s.ok()) { // Commit to the new state imm_->Unref(); - imm_ = NULL; - has_imm_.Release_Store(NULL); + imm_ = nullptr; + has_imm_.store(false, std::memory_order_release); DeleteObsoleteFiles(); } else { RecordBackgroundError(s); @@ -544,13 +586,14 @@ void DBImpl::CompactRange(const Slice* begin, const Slice* end) { } } } - TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap + TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap for (int level = 0; level < max_level_with_files; level++) { TEST_CompactRange(level, begin, end); } } -void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { +void DBImpl::TEST_CompactRange(int level, const Slice* begin, + const Slice* end) { assert(level >= 0); assert(level + 1 < config::kNumLevels); @@ -559,44 +602,45 @@ void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) { ManualCompaction manual; manual.level = level; manual.done = false; - if (begin == NULL) { - manual.begin = NULL; + if (begin == nullptr) { + manual.begin = nullptr; } else { begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek); manual.begin = &begin_storage; } - if (end == NULL) { - manual.end = NULL; + if (end == nullptr) { + manual.end = nullptr; } else { end_storage = InternalKey(*end, 0, static_cast(0)); manual.end = &end_storage; } MutexLock l(&mutex_); - while (!manual.done && !shutting_down_.Acquire_Load() && bg_error_.ok()) { - if (manual_compaction_ == NULL) { // Idle + while (!manual.done && !shutting_down_.load(std::memory_order_acquire) && + bg_error_.ok()) { + if (manual_compaction_ == nullptr) { // Idle manual_compaction_ = &manual; MaybeScheduleCompaction(); } else { // Running either my compaction or another compaction. - bg_cv_.Wait(); + background_work_finished_signal_.Wait(); } } if (manual_compaction_ == &manual) { // Cancel my manual compaction since we aborted early for some reason. - manual_compaction_ = NULL; + manual_compaction_ = nullptr; } } Status DBImpl::TEST_CompactMemTable() { - // NULL batch means just wait for earlier writes to be done - Status s = Write(WriteOptions(), NULL); + // nullptr batch means just wait for earlier writes to be done + Status s = Write(WriteOptions(), nullptr); if (s.ok()) { // Wait until the compaction completes MutexLock l(&mutex_); - while (imm_ != NULL && bg_error_.ok()) { - bg_cv_.Wait(); + while (imm_ != nullptr && bg_error_.ok()) { + background_work_finished_signal_.Wait(); } - if (imm_ != NULL) { + if (imm_ != nullptr) { s = bg_error_; } } @@ -607,24 +651,23 @@ void DBImpl::RecordBackgroundError(const Status& s) { mutex_.AssertHeld(); if (bg_error_.ok()) { bg_error_ = s; - bg_cv_.SignalAll(); + background_work_finished_signal_.SignalAll(); } } void DBImpl::MaybeScheduleCompaction() { mutex_.AssertHeld(); - if (bg_compaction_scheduled_) { + if (background_compaction_scheduled_) { // Already scheduled - } else if (shutting_down_.Acquire_Load()) { + } else if (shutting_down_.load(std::memory_order_acquire)) { // DB is being deleted; no more background compactions } else if (!bg_error_.ok()) { // Already got an error; no more changes - } else if (imm_ == NULL && - manual_compaction_ == NULL && + } else if (imm_ == nullptr && manual_compaction_ == nullptr && !versions_->NeedsCompaction()) { // No work to be done } else { - bg_compaction_scheduled_ = true; + background_compaction_scheduled_ = true; env_->Schedule(&DBImpl::BGWork, this); } } @@ -635,8 +678,8 @@ void DBImpl::BGWork(void* db) { void DBImpl::BackgroundCall() { MutexLock l(&mutex_); - assert(bg_compaction_scheduled_); - if (shutting_down_.Acquire_Load()) { + assert(background_compaction_scheduled_); + if (shutting_down_.load(std::memory_order_acquire)) { // No more background work when shutting down. } else if (!bg_error_.ok()) { // No more background work after a background error. @@ -644,36 +687,35 @@ void DBImpl::BackgroundCall() { BackgroundCompaction(); } - bg_compaction_scheduled_ = false; + background_compaction_scheduled_ = false; // Previous compaction may have produced too many files in a level, // so reschedule another compaction if needed. MaybeScheduleCompaction(); - bg_cv_.SignalAll(); + background_work_finished_signal_.SignalAll(); } void DBImpl::BackgroundCompaction() { mutex_.AssertHeld(); - if (imm_ != NULL) { + if (imm_ != nullptr) { CompactMemTable(); return; } Compaction* c; - bool is_manual = (manual_compaction_ != NULL); + bool is_manual = (manual_compaction_ != nullptr); InternalKey manual_end; if (is_manual) { ManualCompaction* m = manual_compaction_; c = versions_->CompactRange(m->level, m->begin, m->end); - m->done = (c == NULL); - if (c != NULL) { + m->done = (c == nullptr); + if (c != nullptr) { manual_end = c->input(0, c->num_input_files(0) - 1)->largest; } Log(options_.info_log, "Manual compaction at level-%d from %s .. %s; will stop at %s\n", - m->level, - (m->begin ? m->begin->DebugString().c_str() : "(begin)"), + m->level, (m->begin ? m->begin->DebugString().c_str() : "(begin)"), (m->end ? m->end->DebugString().c_str() : "(end)"), (m->done ? "(end)" : manual_end.DebugString().c_str())); } else { @@ -681,26 +723,24 @@ void DBImpl::BackgroundCompaction() { } Status status; - if (c == NULL) { + if (c == nullptr) { // Nothing to do } else if (!is_manual && c->IsTrivialMove()) { // Move file to next level assert(c->num_input_files(0) == 1); FileMetaData* f = c->input(0, 0); c->edit()->DeleteFile(c->level(), f->number); - c->edit()->AddFile(c->level() + 1, f->number, f->file_size, - f->smallest, f->largest); + c->edit()->AddFile(c->level() + 1, f->number, f->file_size, f->smallest, + f->largest); status = versions_->LogAndApply(c->edit(), &mutex_); if (!status.ok()) { RecordBackgroundError(status); } VersionSet::LevelSummaryStorage tmp; Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n", - static_cast(f->number), - c->level() + 1, + static_cast(f->number), c->level() + 1, static_cast(f->file_size), - status.ToString().c_str(), - versions_->LevelSummary(&tmp)); + status.ToString().c_str(), versions_->LevelSummary(&tmp)); } else { CompactionState* compact = new CompactionState(c); status = DoCompactionWork(compact); @@ -715,11 +755,10 @@ void DBImpl::BackgroundCompaction() { if (status.ok()) { // Done - } else if (shutting_down_.Acquire_Load()) { + } else if (shutting_down_.load(std::memory_order_acquire)) { // Ignore compaction errors found during shutting down } else { - Log(options_.info_log, - "Compaction error: %s", status.ToString().c_str()); + Log(options_.info_log, "Compaction error: %s", status.ToString().c_str()); } if (is_manual) { @@ -733,18 +772,18 @@ void DBImpl::BackgroundCompaction() { m->tmp_storage = manual_end; m->begin = &m->tmp_storage; } - manual_compaction_ = NULL; + manual_compaction_ = nullptr; } } void DBImpl::CleanupCompaction(CompactionState* compact) { mutex_.AssertHeld(); - if (compact->builder != NULL) { + if (compact->builder != nullptr) { // May happen if we get a shutdown call in the middle of compaction compact->builder->Abandon(); delete compact->builder; } else { - assert(compact->outfile == NULL); + assert(compact->outfile == nullptr); } delete compact->outfile; for (size_t i = 0; i < compact->outputs.size(); i++) { @@ -755,8 +794,8 @@ void DBImpl::CleanupCompaction(CompactionState* compact) { } Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { - assert(compact != NULL); - assert(compact->builder == NULL); + assert(compact != nullptr); + assert(compact->builder == nullptr); uint64_t file_number; { mutex_.Lock(); @@ -781,9 +820,9 @@ Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) { Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, Iterator* input) { - assert(compact != NULL); - assert(compact->outfile != NULL); - assert(compact->builder != NULL); + assert(compact != nullptr); + assert(compact->outfile != nullptr); + assert(compact->builder != nullptr); const uint64_t output_number = compact->current_output()->number; assert(output_number != 0); @@ -800,7 +839,7 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, compact->current_output()->file_size = current_bytes; compact->total_bytes += current_bytes; delete compact->builder; - compact->builder = NULL; + compact->builder = nullptr; // Finish and check for file errors if (s.ok()) { @@ -810,34 +849,29 @@ Status DBImpl::FinishCompactionOutputFile(CompactionState* compact, s = compact->outfile->Close(); } delete compact->outfile; - compact->outfile = NULL; + compact->outfile = nullptr; if (s.ok() && current_entries > 0) { // Verify that the table is usable - Iterator* iter = table_cache_->NewIterator(ReadOptions(), - output_number, - current_bytes); + Iterator* iter = + table_cache_->NewIterator(ReadOptions(), output_number, current_bytes); s = iter->status(); delete iter; if (s.ok()) { - Log(options_.info_log, - "Generated table #%llu: %lld keys, %lld bytes", - (unsigned long long) output_number, - (unsigned long long) current_entries, - (unsigned long long) current_bytes); + Log(options_.info_log, "Generated table #%llu@%d: %lld keys, %lld bytes", + (unsigned long long)output_number, compact->compaction->level(), + (unsigned long long)current_entries, + (unsigned long long)current_bytes); } } return s; } - Status DBImpl::InstallCompactionResults(CompactionState* compact) { mutex_.AssertHeld(); - Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", - compact->compaction->num_input_files(0), - compact->compaction->level(), - compact->compaction->num_input_files(1), - compact->compaction->level() + 1, + Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes", + compact->compaction->num_input_files(0), compact->compaction->level(), + compact->compaction->num_input_files(1), compact->compaction->level() + 1, static_cast(compact->total_bytes)); // Add compaction outputs @@ -845,9 +879,8 @@ Status DBImpl::InstallCompactionResults(CompactionState* compact) { const int level = compact->compaction->level(); for (size_t i = 0; i < compact->outputs.size(); i++) { const CompactionState::Output& out = compact->outputs[i]; - compact->compaction->edit()->AddFile( - level + 1, - out.number, out.file_size, out.smallest, out.largest); + compact->compaction->edit()->AddFile(level + 1, out.number, out.file_size, + out.smallest, out.largest); } return versions_->LogAndApply(compact->compaction->edit(), &mutex_); } @@ -856,39 +889,40 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { const uint64_t start_micros = env_->NowMicros(); int64_t imm_micros = 0; // Micros spent doing imm_ compactions - Log(options_.info_log, "Compacting %d@%d + %d@%d files", - compact->compaction->num_input_files(0), - compact->compaction->level(), + Log(options_.info_log, "Compacting %d@%d + %d@%d files", + compact->compaction->num_input_files(0), compact->compaction->level(), compact->compaction->num_input_files(1), compact->compaction->level() + 1); assert(versions_->NumLevelFiles(compact->compaction->level()) > 0); - assert(compact->builder == NULL); - assert(compact->outfile == NULL); + assert(compact->builder == nullptr); + assert(compact->outfile == nullptr); if (snapshots_.empty()) { compact->smallest_snapshot = versions_->LastSequence(); } else { - compact->smallest_snapshot = snapshots_.oldest()->number_; + compact->smallest_snapshot = snapshots_.oldest()->sequence_number(); } + Iterator* input = versions_->MakeInputIterator(compact->compaction); + // Release mutex while we're actually doing the compaction work mutex_.Unlock(); - Iterator* input = versions_->MakeInputIterator(compact->compaction); input->SeekToFirst(); Status status; ParsedInternalKey ikey; std::string current_user_key; bool has_current_user_key = false; SequenceNumber last_sequence_for_key = kMaxSequenceNumber; - for (; input->Valid() && !shutting_down_.Acquire_Load(); ) { + while (input->Valid() && !shutting_down_.load(std::memory_order_acquire)) { // Prioritize immutable compaction work - if (has_imm_.NoBarrier_Load() != NULL) { + if (has_imm_.load(std::memory_order_relaxed)) { const uint64_t imm_start = env_->NowMicros(); mutex_.Lock(); - if (imm_ != NULL) { + if (imm_ != nullptr) { CompactMemTable(); - bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary + // Wake up MakeRoomForWrite() if necessary. + background_work_finished_signal_.SignalAll(); } mutex_.Unlock(); imm_micros += (env_->NowMicros() - imm_start); @@ -896,7 +930,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { Slice key = input->key(); if (compact->compaction->ShouldStopBefore(key) && - compact->builder != NULL) { + compact->builder != nullptr) { status = FinishCompactionOutputFile(compact, input); if (!status.ok()) { break; @@ -912,8 +946,8 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { last_sequence_for_key = kMaxSequenceNumber; } else { if (!has_current_user_key || - user_comparator()->Compare(ikey.user_key, - Slice(current_user_key)) != 0) { + user_comparator()->Compare(ikey.user_key, Slice(current_user_key)) != + 0) { // First occurrence of this user key current_user_key.assign(ikey.user_key.data(), ikey.user_key.size()); has_current_user_key = true; @@ -922,7 +956,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { if (last_sequence_for_key <= compact->smallest_snapshot) { // Hidden by an newer entry for same user key - drop = true; // (A) + drop = true; // (A) } else if (ikey.type == kTypeDeletion && ikey.sequence <= compact->smallest_snapshot && compact->compaction->IsBaseLevelForKey(ikey.user_key)) { @@ -950,7 +984,7 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { if (!drop) { // Open output file if necessary - if (compact->builder == NULL) { + if (compact->builder == nullptr) { status = OpenCompactionOutputFile(compact); if (!status.ok()) { break; @@ -975,17 +1009,17 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { input->Next(); } - if (status.ok() && shutting_down_.Acquire_Load()) { + if (status.ok() && shutting_down_.load(std::memory_order_acquire)) { status = Status::IOError("Deleting DB during compaction"); } - if (status.ok() && compact->builder != NULL) { + if (status.ok() && compact->builder != nullptr) { status = FinishCompactionOutputFile(compact, input); } if (status.ok()) { status = input->status(); } delete input; - input = NULL; + input = nullptr; CompactionStats stats; stats.micros = env_->NowMicros() - start_micros - imm_micros; @@ -1008,34 +1042,37 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) { RecordBackgroundError(status); } VersionSet::LevelSummaryStorage tmp; - Log(options_.info_log, - "compacted to: %s", versions_->LevelSummary(&tmp)); + Log(options_.info_log, "compacted to: %s", versions_->LevelSummary(&tmp)); return status; } namespace { + struct IterState { - port::Mutex* mu; - Version* version; - MemTable* mem; - MemTable* imm; + port::Mutex* const mu; + Version* const version GUARDED_BY(mu); + MemTable* const mem GUARDED_BY(mu); + MemTable* const imm GUARDED_BY(mu); + + IterState(port::Mutex* mutex, MemTable* mem, MemTable* imm, Version* version) + : mu(mutex), version(version), mem(mem), imm(imm) {} }; static void CleanupIteratorState(void* arg1, void* arg2) { IterState* state = reinterpret_cast(arg1); state->mu->Lock(); state->mem->Unref(); - if (state->imm != NULL) state->imm->Unref(); + if (state->imm != nullptr) state->imm->Unref(); state->version->Unref(); state->mu->Unlock(); delete state; } -} // namespace + +} // anonymous namespace Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, SequenceNumber* latest_snapshot, uint32_t* seed) { - IterState* cleanup = new IterState; mutex_.Lock(); *latest_snapshot = versions_->LastSequence(); @@ -1043,7 +1080,7 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, std::vector list; list.push_back(mem_->NewIterator()); mem_->Ref(); - if (imm_ != NULL) { + if (imm_ != nullptr) { list.push_back(imm_->NewIterator()); imm_->Ref(); } @@ -1052,11 +1089,8 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options, NewMergingIterator(&internal_comparator_, &list[0], list.size()); versions_->current()->Ref(); - cleanup->mu = &mutex_; - cleanup->mem = mem_; - cleanup->imm = imm_; - cleanup->version = versions_->current(); - internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL); + IterState* cleanup = new IterState(&mutex_, mem_, imm_, versions_->current()); + internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, nullptr); *seed = ++seed_; mutex_.Unlock(); @@ -1074,14 +1108,14 @@ int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() { return versions_->MaxNextLevelOverlappingBytes(); } -Status DBImpl::Get(const ReadOptions& options, - const Slice& key, +Status DBImpl::Get(const ReadOptions& options, const Slice& key, std::string* value) { Status s; MutexLock l(&mutex_); SequenceNumber snapshot; - if (options.snapshot != NULL) { - snapshot = reinterpret_cast(options.snapshot)->number_; + if (options.snapshot != nullptr) { + snapshot = + static_cast(options.snapshot)->sequence_number(); } else { snapshot = versions_->LastSequence(); } @@ -1090,7 +1124,7 @@ Status DBImpl::Get(const ReadOptions& options, MemTable* imm = imm_; Version* current = versions_->current(); mem->Ref(); - if (imm != NULL) imm->Ref(); + if (imm != nullptr) imm->Ref(); current->Ref(); bool have_stat_update = false; @@ -1103,7 +1137,7 @@ Status DBImpl::Get(const ReadOptions& options, LookupKey lkey(key, snapshot); if (mem->Get(lkey, value, &s)) { // Done - } else if (imm != NULL && imm->Get(lkey, value, &s)) { + } else if (imm != nullptr && imm->Get(lkey, value, &s)) { // Done } else { s = current->Get(options, lkey, value, &stats); @@ -1116,7 +1150,7 @@ Status DBImpl::Get(const ReadOptions& options, MaybeScheduleCompaction(); } mem->Unref(); - if (imm != NULL) imm->Unref(); + if (imm != nullptr) imm->Unref(); current->Unref(); return s; } @@ -1125,12 +1159,12 @@ Iterator* DBImpl::NewIterator(const ReadOptions& options) { SequenceNumber latest_snapshot; uint32_t seed; Iterator* iter = NewInternalIterator(options, &latest_snapshot, &seed); - return NewDBIterator( - this, user_comparator(), iter, - (options.snapshot != NULL - ? reinterpret_cast(options.snapshot)->number_ - : latest_snapshot), - seed); + return NewDBIterator(this, user_comparator(), iter, + (options.snapshot != nullptr + ? static_cast(options.snapshot) + ->sequence_number() + : latest_snapshot), + seed); } void DBImpl::RecordReadSample(Slice key) { @@ -1145,9 +1179,9 @@ const Snapshot* DBImpl::GetSnapshot() { return snapshots_.New(versions_->LastSequence()); } -void DBImpl::ReleaseSnapshot(const Snapshot* s) { +void DBImpl::ReleaseSnapshot(const Snapshot* snapshot) { MutexLock l(&mutex_); - snapshots_.Delete(reinterpret_cast(s)); + snapshots_.Delete(static_cast(snapshot)); } // Convenience methods @@ -1159,9 +1193,9 @@ Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { return DB::Delete(options, key); } -Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { +Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) { Writer w(&mutex_); - w.batch = my_batch; + w.batch = updates; w.sync = options.sync; w.done = false; @@ -1175,13 +1209,13 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { } // May temporarily unlock and wait. - Status status = MakeRoomForWrite(my_batch == NULL); + Status status = MakeRoomForWrite(updates == nullptr); uint64_t last_sequence = versions_->LastSequence(); Writer* last_writer = &w; - if (status.ok() && my_batch != NULL) { // NULL batch is for compactions - WriteBatch* updates = BuildBatchGroup(&last_writer); - WriteBatchInternal::SetSequence(updates, last_sequence + 1); - last_sequence += WriteBatchInternal::Count(updates); + if (status.ok() && updates != nullptr) { // nullptr batch is for compactions + WriteBatch* write_batch = BuildBatchGroup(&last_writer); + WriteBatchInternal::SetSequence(write_batch, last_sequence + 1); + last_sequence += WriteBatchInternal::Count(write_batch); // Add to log and apply to memtable. We can release the lock // during this phase since &w is currently responsible for logging @@ -1189,7 +1223,7 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { // into mem_. { mutex_.Unlock(); - status = log_->AddRecord(WriteBatchInternal::Contents(updates)); + status = log_->AddRecord(WriteBatchInternal::Contents(write_batch)); bool sync_error = false; if (status.ok() && options.sync) { status = logfile_->Sync(); @@ -1198,7 +1232,7 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { } } if (status.ok()) { - status = WriteBatchInternal::InsertInto(updates, mem_); + status = WriteBatchInternal::InsertInto(write_batch, mem_); } mutex_.Lock(); if (sync_error) { @@ -1208,7 +1242,7 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { RecordBackgroundError(status); } } - if (updates == tmp_batch_) tmp_batch_->Clear(); + if (write_batch == tmp_batch_) tmp_batch_->Clear(); versions_->SetLastSequence(last_sequence); } @@ -1233,12 +1267,13 @@ Status DBImpl::Write(const WriteOptions& options, WriteBatch* my_batch) { } // REQUIRES: Writer list must be non-empty -// REQUIRES: First writer must have a non-NULL batch +// REQUIRES: First writer must have a non-null batch WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { + mutex_.AssertHeld(); assert(!writers_.empty()); Writer* first = writers_.front(); WriteBatch* result = first->batch; - assert(result != NULL); + assert(result != nullptr); size_t size = WriteBatchInternal::ByteSize(first->batch); @@ -1246,8 +1281,8 @@ WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { // original write is small, limit the growth so we do not slow // down the small write too much. size_t max_size = 1 << 20; - if (size <= (128<<10)) { - max_size = size + (128<<10); + if (size <= (128 << 10)) { + max_size = size + (128 << 10); } *last_writer = first; @@ -1260,7 +1295,7 @@ WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) { break; } - if (w->batch != NULL) { + if (w->batch != nullptr) { size += WriteBatchInternal::ByteSize(w->batch); if (size > max_size) { // Do not make batch too big @@ -1293,9 +1328,8 @@ Status DBImpl::MakeRoomForWrite(bool force) { // Yield previous error s = bg_error_; break; - } else if ( - allow_delay && - versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) { + } else if (allow_delay && versions_->NumLevelFiles(0) >= + config::kL0_SlowdownWritesTrigger) { // We are getting close to hitting a hard limit on the number of // L0 files. Rather than delaying a single write by several // seconds when we hit the hard limit, start delaying each @@ -1310,20 +1344,20 @@ Status DBImpl::MakeRoomForWrite(bool force) { (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) { // There is room in current memtable break; - } else if (imm_ != NULL) { + } else if (imm_ != nullptr) { // We have filled up the current memtable, but the previous // one is still being compacted, so we wait. Log(options_.info_log, "Current memtable full; waiting...\n"); - bg_cv_.Wait(); + background_work_finished_signal_.Wait(); } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) { // There are too many level-0 files. Log(options_.info_log, "Too many L0 files; waiting...\n"); - bg_cv_.Wait(); + background_work_finished_signal_.Wait(); } else { // Attempt to switch to a new memtable and trigger compaction of old assert(versions_->PrevLogNumber() == 0); uint64_t new_log_number = versions_->NewFileNumber(); - WritableFile* lfile = NULL; + WritableFile* lfile = nullptr; s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile); if (!s.ok()) { // Avoid chewing through file number space in a tight loop. @@ -1336,10 +1370,10 @@ Status DBImpl::MakeRoomForWrite(bool force) { logfile_number_ = new_log_number; log_ = new log::Writer(lfile); imm_ = mem_; - has_imm_.Release_Store(imm_); + has_imm_.store(true, std::memory_order_release); mem_ = new MemTable(internal_comparator_); mem_->Ref(); - force = false; // Do not force another compaction if have room + force = false; // Do not force another compaction if have room MaybeScheduleCompaction(); } } @@ -1373,21 +1407,16 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) { snprintf(buf, sizeof(buf), " Compactions\n" "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n" - "--------------------------------------------------\n" - ); + "--------------------------------------------------\n"); value->append(buf); for (int level = 0; level < config::kNumLevels; level++) { int files = versions_->NumLevelFiles(level); if (stats_[level].micros > 0 || files > 0) { - snprintf( - buf, sizeof(buf), - "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", - level, - files, - versions_->NumLevelBytes(level) / 1048576.0, - stats_[level].micros / 1e6, - stats_[level].bytes_read / 1048576.0, - stats_[level].bytes_written / 1048576.0); + snprintf(buf, sizeof(buf), "%3d %8d %8.0f %9.0f %8.0f %9.0f\n", level, + files, versions_->NumLevelBytes(level) / 1048576.0, + stats_[level].micros / 1e6, + stats_[level].bytes_read / 1048576.0, + stats_[level].bytes_written / 1048576.0); value->append(buf); } } @@ -1395,21 +1424,29 @@ bool DBImpl::GetProperty(const Slice& property, std::string* value) { } else if (in == "sstables") { *value = versions_->current()->DebugString(); return true; + } else if (in == "approximate-memory-usage") { + size_t total_usage = options_.block_cache->TotalCharge(); + if (mem_) { + total_usage += mem_->ApproximateMemoryUsage(); + } + if (imm_) { + total_usage += imm_->ApproximateMemoryUsage(); + } + char buf[50]; + snprintf(buf, sizeof(buf), "%llu", + static_cast(total_usage)); + value->append(buf); + return true; } return false; } -void DBImpl::GetApproximateSizes( - const Range* range, int n, - uint64_t* sizes) { +void DBImpl::GetApproximateSizes(const Range* range, int n, uint64_t* sizes) { // TODO(opt): better implementation - Version* v; - { - MutexLock l(&mutex_); - versions_->current()->Ref(); - v = versions_->current(); - } + MutexLock l(&mutex_); + Version* v = versions_->current(); + v->Ref(); for (int i = 0; i < n; i++) { // Convert user_key into a corresponding internal key. @@ -1420,10 +1457,7 @@ void DBImpl::GetApproximateSizes( sizes[i] = (limit >= start ? limit - start : 0); } - { - MutexLock l(&mutex_); - v->Unref(); - } + v->Unref(); } // Default implementations of convenience methods that subclasses of DB @@ -1440,17 +1474,19 @@ Status DB::Delete(const WriteOptions& opt, const Slice& key) { return Write(opt, &batch); } -DB::~DB() { } +DB::~DB() = default; -Status DB::Open(const Options& options, const std::string& dbname, - DB** dbptr) { - *dbptr = NULL; +Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) { + *dbptr = nullptr; DBImpl* impl = new DBImpl(options, dbname); impl->mutex_.Lock(); VersionEdit edit; - Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists - if (s.ok()) { + // Recover handles create_if_missing, error_if_exists + bool save_manifest = false; + Status s = impl->Recover(&edit, &save_manifest); + if (s.ok() && impl->mem_ == nullptr) { + // Create new log and a corresponding memtable. uint64_t new_log_number = impl->versions_->NewFileNumber(); WritableFile* lfile; s = options.env->NewWritableFile(LogFileName(dbname, new_log_number), @@ -1460,15 +1496,22 @@ Status DB::Open(const Options& options, const std::string& dbname, impl->logfile_ = lfile; impl->logfile_number_ = new_log_number; impl->log_ = new log::Writer(lfile); - s = impl->versions_->LogAndApply(&edit, &impl->mutex_); - } - if (s.ok()) { - impl->DeleteObsoleteFiles(); - impl->MaybeScheduleCompaction(); + impl->mem_ = new MemTable(impl->internal_comparator_); + impl->mem_->Ref(); } } + if (s.ok() && save_manifest) { + edit.SetPrevLogNumber(0); // No older logs needed after recovery. + edit.SetLogNumber(impl->logfile_number_); + s = impl->versions_->LogAndApply(&edit, &impl->mutex_); + } + if (s.ok()) { + impl->DeleteObsoleteFiles(); + impl->MaybeScheduleCompaction(); + } impl->mutex_.Unlock(); if (s.ok()) { + assert(impl->mem_ != nullptr); *dbptr = impl; } else { delete impl; @@ -1476,21 +1519,20 @@ Status DB::Open(const Options& options, const std::string& dbname, return s; } -Snapshot::~Snapshot() { -} +Snapshot::~Snapshot() = default; Status DestroyDB(const std::string& dbname, const Options& options) { Env* env = options.env; std::vector filenames; - // Ignore error in case directory does not exist - env->GetChildren(dbname, &filenames); - if (filenames.empty()) { + Status result = env->GetChildren(dbname, &filenames); + if (!result.ok()) { + // Ignore error in case directory does not exist return Status::OK(); } FileLock* lock; const std::string lockname = LockFileName(dbname); - Status result = env->LockFile(lockname, &lock); + result = env->LockFile(lockname, &lock); if (result.ok()) { uint64_t number; FileType type; diff --git a/src/leveldb/db/db_impl.h b/src/leveldb/db/db_impl.h index cfc998164..685735c73 100644 --- a/src/leveldb/db/db_impl.h +++ b/src/leveldb/db/db_impl.h @@ -5,8 +5,11 @@ #ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_ #define STORAGE_LEVELDB_DB_DB_IMPL_H_ +#include #include #include +#include + #include "db/dbformat.h" #include "db/log_writer.h" #include "db/snapshot.h" @@ -26,21 +29,25 @@ class VersionSet; class DBImpl : public DB { public: DBImpl(const Options& options, const std::string& dbname); - virtual ~DBImpl(); + + DBImpl(const DBImpl&) = delete; + DBImpl& operator=(const DBImpl&) = delete; + + ~DBImpl() override; // Implementations of the DB interface - virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value); - virtual Status Delete(const WriteOptions&, const Slice& key); - virtual Status Write(const WriteOptions& options, WriteBatch* updates); - virtual Status Get(const ReadOptions& options, - const Slice& key, - std::string* value); - virtual Iterator* NewIterator(const ReadOptions&); - virtual const Snapshot* GetSnapshot(); - virtual void ReleaseSnapshot(const Snapshot* snapshot); - virtual bool GetProperty(const Slice& property, std::string* value); - virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes); - virtual void CompactRange(const Slice* begin, const Slice* end); + Status Put(const WriteOptions&, const Slice& key, + const Slice& value) override; + Status Delete(const WriteOptions&, const Slice& key) override; + Status Write(const WriteOptions& options, WriteBatch* updates) override; + Status Get(const ReadOptions& options, const Slice& key, + std::string* value) override; + Iterator* NewIterator(const ReadOptions&) override; + const Snapshot* GetSnapshot() override; + void ReleaseSnapshot(const Snapshot* snapshot) override; + bool GetProperty(const Slice& property, std::string* value) override; + void GetApproximateSizes(const Range* range, int n, uint64_t* sizes) override; + void CompactRange(const Slice* begin, const Slice* end) override; // Extra methods (for testing) that are not in the public DB interface @@ -69,6 +76,31 @@ class DBImpl : public DB { struct CompactionState; struct Writer; + // Information for a manual compaction + struct ManualCompaction { + int level; + bool done; + const InternalKey* begin; // null means beginning of key range + const InternalKey* end; // null means end of key range + InternalKey tmp_storage; // Used to keep track of compaction progress + }; + + // Per level compaction stats. stats_[level] stores the stats for + // compactions that produced data for the specified "level". + struct CompactionStats { + CompactionStats() : micros(0), bytes_read(0), bytes_written(0) {} + + void Add(const CompactionStats& c) { + this->micros += c.micros; + this->bytes_read += c.bytes_read; + this->bytes_written += c.bytes_written; + } + + int64_t micros; + int64_t bytes_read; + int64_t bytes_written; + }; + Iterator* NewInternalIterator(const ReadOptions&, SequenceNumber* latest_snapshot, uint32_t* seed); @@ -78,21 +110,21 @@ class DBImpl : public DB { // Recover the descriptor from persistent storage. May do a significant // amount of work to recover recently logged updates. Any changes to // be made to the descriptor are added to *edit. - Status Recover(VersionEdit* edit) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Status Recover(VersionEdit* edit, bool* save_manifest) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); void MaybeIgnoreError(Status* s) const; // Delete any unneeded files and stale in-memory entries. - void DeleteObsoleteFiles(); + void DeleteObsoleteFiles() EXCLUSIVE_LOCKS_REQUIRED(mutex_); // Compact the in-memory write buffer to disk. Switches to a new // log-file/memtable and writes a new descriptor iff successful. // Errors are recorded in bg_error_. void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_); - Status RecoverLogFile(uint64_t log_number, - VersionEdit* edit, - SequenceNumber* max_sequence) + Status RecoverLogFile(uint64_t log_number, bool last_log, bool* save_manifest, + VersionEdit* edit, SequenceNumber* max_sequence) EXCLUSIVE_LOCKS_REQUIRED(mutex_); Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base) @@ -100,14 +132,15 @@ class DBImpl : public DB { Status MakeRoomForWrite(bool force /* compact even if there is room? */) EXCLUSIVE_LOCKS_REQUIRED(mutex_); - WriteBatch* BuildBatchGroup(Writer** last_writer); + WriteBatch* BuildBatchGroup(Writer** last_writer) + EXCLUSIVE_LOCKS_REQUIRED(mutex_); void RecordBackgroundError(const Status& s); void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); static void BGWork(void* db); void BackgroundCall(); - void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_); void CleanupCompaction(CompactionState* compact) EXCLUSIVE_LOCKS_REQUIRED(mutex_); Status DoCompactionWork(CompactionState* compact) @@ -118,93 +151,66 @@ class DBImpl : public DB { Status InstallCompactionResults(CompactionState* compact) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + const Comparator* user_comparator() const { + return internal_comparator_.user_comparator(); + } + // Constant after construction Env* const env_; const InternalKeyComparator internal_comparator_; const InternalFilterPolicy internal_filter_policy_; const Options options_; // options_.comparator == &internal_comparator_ - bool owns_info_log_; - bool owns_cache_; + const bool owns_info_log_; + const bool owns_cache_; const std::string dbname_; // table_cache_ provides its own synchronization - TableCache* table_cache_; + TableCache* const table_cache_; - // Lock over the persistent DB state. Non-NULL iff successfully acquired. + // Lock over the persistent DB state. Non-null iff successfully acquired. FileLock* db_lock_; // State below is protected by mutex_ port::Mutex mutex_; - port::AtomicPointer shutting_down_; - port::CondVar bg_cv_; // Signalled when background work finishes + std::atomic shutting_down_; + port::CondVar background_work_finished_signal_ GUARDED_BY(mutex_); MemTable* mem_; - MemTable* imm_; // Memtable being compacted - port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_ + MemTable* imm_ GUARDED_BY(mutex_); // Memtable being compacted + std::atomic has_imm_; // So bg thread can detect non-null imm_ WritableFile* logfile_; - uint64_t logfile_number_; + uint64_t logfile_number_ GUARDED_BY(mutex_); log::Writer* log_; - uint32_t seed_; // For sampling. + uint32_t seed_ GUARDED_BY(mutex_); // For sampling. // Queue of writers. - std::deque writers_; - WriteBatch* tmp_batch_; + std::deque writers_ GUARDED_BY(mutex_); + WriteBatch* tmp_batch_ GUARDED_BY(mutex_); - SnapshotList snapshots_; + SnapshotList snapshots_ GUARDED_BY(mutex_); // Set of table files to protect from deletion because they are // part of ongoing compactions. - std::set pending_outputs_; + std::set pending_outputs_ GUARDED_BY(mutex_); // Has a background compaction been scheduled or is running? - bool bg_compaction_scheduled_; + bool background_compaction_scheduled_ GUARDED_BY(mutex_); - // Information for a manual compaction - struct ManualCompaction { - int level; - bool done; - const InternalKey* begin; // NULL means beginning of key range - const InternalKey* end; // NULL means end of key range - InternalKey tmp_storage; // Used to keep track of compaction progress - }; - ManualCompaction* manual_compaction_; + ManualCompaction* manual_compaction_ GUARDED_BY(mutex_); - VersionSet* versions_; + VersionSet* const versions_ GUARDED_BY(mutex_); // Have we encountered a background error in paranoid mode? - Status bg_error_; + Status bg_error_ GUARDED_BY(mutex_); - // Per level compaction stats. stats_[level] stores the stats for - // compactions that produced data for the specified "level". - struct CompactionStats { - int64_t micros; - int64_t bytes_read; - int64_t bytes_written; - - CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { } - - void Add(const CompactionStats& c) { - this->micros += c.micros; - this->bytes_read += c.bytes_read; - this->bytes_written += c.bytes_written; - } - }; - CompactionStats stats_[config::kNumLevels]; - - // No copying allowed - DBImpl(const DBImpl&); - void operator=(const DBImpl&); - - const Comparator* user_comparator() const { - return internal_comparator_.user_comparator(); - } + CompactionStats stats_[config::kNumLevels] GUARDED_BY(mutex_); }; // Sanitize db options. The caller should delete result.info_log if // it is not equal to src.info_log. -extern Options SanitizeOptions(const std::string& db, - const InternalKeyComparator* icmp, - const InternalFilterPolicy* ipolicy, - const Options& src); +Options SanitizeOptions(const std::string& db, + const InternalKeyComparator* icmp, + const InternalFilterPolicy* ipolicy, + const Options& src); } // namespace leveldb diff --git a/src/leveldb/db/db_iter.cc b/src/leveldb/db/db_iter.cc index 3b2035e9e..98715a950 100644 --- a/src/leveldb/db/db_iter.cc +++ b/src/leveldb/db/db_iter.cc @@ -4,9 +4,9 @@ #include "db/db_iter.h" -#include "db/filename.h" #include "db/db_impl.h" #include "db/dbformat.h" +#include "db/filename.h" #include "leveldb/env.h" #include "leveldb/iterator.h" #include "port/port.h" @@ -36,17 +36,14 @@ namespace { // combines multiple entries for the same userkey found in the DB // representation into a single entry while accounting for sequence // numbers, deletion markers, overwrites, etc. -class DBIter: public Iterator { +class DBIter : public Iterator { public: // Which direction is the iterator currently moving? // (1) When moving forward, the internal iterator is positioned at // the exact entry that yields this->key(), this->value() // (2) When moving backwards, the internal iterator is positioned // just before all entries whose user key == this->key(). - enum Direction { - kForward, - kReverse - }; + enum Direction { kForward, kReverse }; DBIter(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s, uint32_t seed) @@ -57,21 +54,22 @@ class DBIter: public Iterator { direction_(kForward), valid_(false), rnd_(seed), - bytes_counter_(RandomPeriod()) { - } - virtual ~DBIter() { - delete iter_; - } - virtual bool Valid() const { return valid_; } - virtual Slice key() const { + bytes_until_read_sampling_(RandomCompactionPeriod()) {} + + DBIter(const DBIter&) = delete; + DBIter& operator=(const DBIter&) = delete; + + ~DBIter() override { delete iter_; } + bool Valid() const override { return valid_; } + Slice key() const override { assert(valid_); return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_; } - virtual Slice value() const { + Slice value() const override { assert(valid_); return (direction_ == kForward) ? iter_->value() : saved_value_; } - virtual Status status() const { + Status status() const override { if (status_.ok()) { return iter_->status(); } else { @@ -79,11 +77,11 @@ class DBIter: public Iterator { } } - virtual void Next(); - virtual void Prev(); - virtual void Seek(const Slice& target); - virtual void SeekToFirst(); - virtual void SeekToLast(); + void Next() override; + void Prev() override; + void Seek(const Slice& target) override; + void SeekToFirst() override; + void SeekToLast() override; private: void FindNextUserEntry(bool skipping, std::string* skip); @@ -103,38 +101,35 @@ class DBIter: public Iterator { } } - // Pick next gap with average value of config::kReadBytesPeriod. - ssize_t RandomPeriod() { - return rnd_.Uniform(2*config::kReadBytesPeriod); + // Picks the number of bytes that can be read until a compaction is scheduled. + size_t RandomCompactionPeriod() { + return rnd_.Uniform(2 * config::kReadBytesPeriod); } DBImpl* db_; const Comparator* const user_comparator_; Iterator* const iter_; SequenceNumber const sequence_; - Status status_; - std::string saved_key_; // == current key when direction_==kReverse - std::string saved_value_; // == current raw value when direction_==kReverse + std::string saved_key_; // == current key when direction_==kReverse + std::string saved_value_; // == current raw value when direction_==kReverse Direction direction_; bool valid_; - Random rnd_; - ssize_t bytes_counter_; - - // No copying allowed - DBIter(const DBIter&); - void operator=(const DBIter&); + size_t bytes_until_read_sampling_; }; inline bool DBIter::ParseKey(ParsedInternalKey* ikey) { Slice k = iter_->key(); - ssize_t n = k.size() + iter_->value().size(); - bytes_counter_ -= n; - while (bytes_counter_ < 0) { - bytes_counter_ += RandomPeriod(); + + size_t bytes_read = k.size() + iter_->value().size(); + while (bytes_until_read_sampling_ < bytes_read) { + bytes_until_read_sampling_ += RandomCompactionPeriod(); db_->RecordReadSample(k); } + assert(bytes_until_read_sampling_ >= bytes_read); + bytes_until_read_sampling_ -= bytes_read; + if (!ParseInternalKey(k, ikey)) { status_ = Status::Corruption("corrupted internal key in DBIter"); return false; @@ -165,6 +160,15 @@ void DBIter::Next() { } else { // Store in saved_key_ the current key so we skip it below. SaveKey(ExtractUserKey(iter_->key()), &saved_key_); + + // iter_ is pointing to current key. We can now safely move to the next to + // avoid checking current key. + iter_->Next(); + if (!iter_->Valid()) { + valid_ = false; + saved_key_.clear(); + return; + } } FindNextUserEntry(true, &saved_key_); @@ -218,8 +222,8 @@ void DBIter::Prev() { ClearSavedValue(); return; } - if (user_comparator_->Compare(ExtractUserKey(iter_->key()), - saved_key_) < 0) { + if (user_comparator_->Compare(ExtractUserKey(iter_->key()), saved_key_) < + 0) { break; } } @@ -275,8 +279,8 @@ void DBIter::Seek(const Slice& target) { direction_ = kForward; ClearSavedValue(); saved_key_.clear(); - AppendInternalKey( - &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek)); + AppendInternalKey(&saved_key_, + ParsedInternalKey(target, sequence_, kValueTypeForSeek)); iter_->Seek(saved_key_); if (iter_->Valid()) { FindNextUserEntry(false, &saved_key_ /* temporary storage */); @@ -305,12 +309,9 @@ void DBIter::SeekToLast() { } // anonymous namespace -Iterator* NewDBIterator( - DBImpl* db, - const Comparator* user_key_comparator, - Iterator* internal_iter, - SequenceNumber sequence, - uint32_t seed) { +Iterator* NewDBIterator(DBImpl* db, const Comparator* user_key_comparator, + Iterator* internal_iter, SequenceNumber sequence, + uint32_t seed) { return new DBIter(db, user_key_comparator, internal_iter, sequence, seed); } diff --git a/src/leveldb/db/db_iter.h b/src/leveldb/db/db_iter.h index 04927e937..fd93e912a 100644 --- a/src/leveldb/db/db_iter.h +++ b/src/leveldb/db/db_iter.h @@ -6,8 +6,9 @@ #define STORAGE_LEVELDB_DB_DB_ITER_H_ #include -#include "leveldb/db.h" + #include "db/dbformat.h" +#include "leveldb/db.h" namespace leveldb { @@ -16,12 +17,9 @@ class DBImpl; // Return a new iterator that converts internal keys (yielded by // "*internal_iter") that were live at the specified "sequence" number // into appropriate user keys. -extern Iterator* NewDBIterator( - DBImpl* db, - const Comparator* user_key_comparator, - Iterator* internal_iter, - SequenceNumber sequence, - uint32_t seed); +Iterator* NewDBIterator(DBImpl* db, const Comparator* user_key_comparator, + Iterator* internal_iter, SequenceNumber sequence, + uint32_t seed); } // namespace leveldb diff --git a/src/leveldb/db/db_test.cc b/src/leveldb/db/db_test.cc index 0fed9137d..beb1d3bde 100644 --- a/src/leveldb/db/db_test.cc +++ b/src/leveldb/db/db_test.cc @@ -3,14 +3,20 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include "leveldb/db.h" -#include "leveldb/filter_policy.h" + +#include +#include + #include "db/db_impl.h" #include "db/filename.h" #include "db/version_set.h" #include "db/write_batch_internal.h" #include "leveldb/cache.h" #include "leveldb/env.h" +#include "leveldb/filter_policy.h" #include "leveldb/table.h" +#include "port/port.h" +#include "port/thread_annotations.h" #include "util/hash.h" #include "util/logging.h" #include "util/mutexlock.h" @@ -25,83 +31,116 @@ static std::string RandomString(Random* rnd, int len) { return r; } +static std::string RandomKey(Random* rnd) { + int len = + (rnd->OneIn(3) ? 1 // Short sometimes to encourage collisions + : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); + return test::RandomKey(rnd, len); +} + namespace { class AtomicCounter { - private: - port::Mutex mu_; - int count_; public: - AtomicCounter() : count_(0) { } - void Increment() { - IncrementBy(1); - } - void IncrementBy(int count) { + AtomicCounter() : count_(0) {} + void Increment() { IncrementBy(1); } + void IncrementBy(int count) LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); count_ += count; } - int Read() { + int Read() LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); return count_; } - void Reset() { + void Reset() LOCKS_EXCLUDED(mu_) { MutexLock l(&mu_); count_ = 0; } + + private: + port::Mutex mu_; + int count_ GUARDED_BY(mu_); }; void DelayMilliseconds(int millis) { Env::Default()->SleepForMicroseconds(millis * 1000); } -} +} // namespace -// Special Env used to delay background operations +// Test Env to override default Env behavior for testing. +class TestEnv : public EnvWrapper { + public: + explicit TestEnv(Env* base) : EnvWrapper(base), ignore_dot_files_(false) {} + + void SetIgnoreDotFiles(bool ignored) { ignore_dot_files_ = ignored; } + + Status GetChildren(const std::string& dir, + std::vector* result) override { + Status s = target()->GetChildren(dir, result); + if (!s.ok() || !ignore_dot_files_) { + return s; + } + + std::vector::iterator it = result->begin(); + while (it != result->end()) { + if ((*it == ".") || (*it == "..")) { + it = result->erase(it); + } else { + ++it; + } + } + + return s; + } + + private: + bool ignore_dot_files_; +}; + +// Special Env used to delay background operations. class SpecialEnv : public EnvWrapper { public: - // sstable/log Sync() calls are blocked while this pointer is non-NULL. - port::AtomicPointer delay_data_sync_; + // sstable/log Sync() calls are blocked while this pointer is non-null. + std::atomic delay_data_sync_; // sstable/log Sync() calls return an error. - port::AtomicPointer data_sync_error_; + std::atomic data_sync_error_; - // Simulate no-space errors while this pointer is non-NULL. - port::AtomicPointer no_space_; + // Simulate no-space errors while this pointer is non-null. + std::atomic no_space_; - // Simulate non-writable file system while this pointer is non-NULL - port::AtomicPointer non_writable_; + // Simulate non-writable file system while this pointer is non-null. + std::atomic non_writable_; - // Force sync of manifest files to fail while this pointer is non-NULL - port::AtomicPointer manifest_sync_error_; + // Force sync of manifest files to fail while this pointer is non-null. + std::atomic manifest_sync_error_; - // Force write to manifest files to fail while this pointer is non-NULL - port::AtomicPointer manifest_write_error_; + // Force write to manifest files to fail while this pointer is non-null. + std::atomic manifest_write_error_; bool count_random_reads_; AtomicCounter random_read_counter_; - explicit SpecialEnv(Env* base) : EnvWrapper(base) { - delay_data_sync_.Release_Store(NULL); - data_sync_error_.Release_Store(NULL); - no_space_.Release_Store(NULL); - non_writable_.Release_Store(NULL); - count_random_reads_ = false; - manifest_sync_error_.Release_Store(NULL); - manifest_write_error_.Release_Store(NULL); - } + explicit SpecialEnv(Env* base) + : EnvWrapper(base), + delay_data_sync_(false), + data_sync_error_(false), + no_space_(false), + non_writable_(false), + manifest_sync_error_(false), + manifest_write_error_(false), + count_random_reads_(false) {} Status NewWritableFile(const std::string& f, WritableFile** r) { class DataFile : public WritableFile { private: - SpecialEnv* env_; - WritableFile* base_; + SpecialEnv* const env_; + WritableFile* const base_; public: - DataFile(SpecialEnv* env, WritableFile* base) - : env_(env), - base_(base) { - } + DataFile(SpecialEnv* env, WritableFile* base) : env_(env), base_(base) {} ~DataFile() { delete base_; } Status Append(const Slice& data) { - if (env_->no_space_.Acquire_Load() != NULL) { + if (env_->no_space_.load(std::memory_order_acquire)) { // Drop writes on the floor return Status::OK(); } else { @@ -111,24 +150,26 @@ class SpecialEnv : public EnvWrapper { Status Close() { return base_->Close(); } Status Flush() { return base_->Flush(); } Status Sync() { - if (env_->data_sync_error_.Acquire_Load() != NULL) { + if (env_->data_sync_error_.load(std::memory_order_acquire)) { return Status::IOError("simulated data sync error"); } - while (env_->delay_data_sync_.Acquire_Load() != NULL) { + while (env_->delay_data_sync_.load(std::memory_order_acquire)) { DelayMilliseconds(100); } return base_->Sync(); } + std::string GetName() const override { return ""; } }; class ManifestFile : public WritableFile { private: SpecialEnv* env_; WritableFile* base_; + public: - ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) { } + ManifestFile(SpecialEnv* env, WritableFile* b) : env_(env), base_(b) {} ~ManifestFile() { delete base_; } Status Append(const Slice& data) { - if (env_->manifest_write_error_.Acquire_Load() != NULL) { + if (env_->manifest_write_error_.load(std::memory_order_acquire)) { return Status::IOError("simulated writer error"); } else { return base_->Append(data); @@ -137,24 +178,25 @@ class SpecialEnv : public EnvWrapper { Status Close() { return base_->Close(); } Status Flush() { return base_->Flush(); } Status Sync() { - if (env_->manifest_sync_error_.Acquire_Load() != NULL) { + if (env_->manifest_sync_error_.load(std::memory_order_acquire)) { return Status::IOError("simulated sync error"); } else { return base_->Sync(); } } + std::string GetName() const override { return ""; } }; - if (non_writable_.Acquire_Load() != NULL) { + if (non_writable_.load(std::memory_order_acquire)) { return Status::IOError("simulated write error"); } Status s = target()->NewWritableFile(f, r); if (s.ok()) { - if (strstr(f.c_str(), ".ldb") != NULL || - strstr(f.c_str(), ".log") != NULL) { + if (strstr(f.c_str(), ".ldb") != nullptr || + strstr(f.c_str(), ".log") != nullptr) { *r = new DataFile(this, *r); - } else if (strstr(f.c_str(), "MANIFEST") != NULL) { + } else if (strstr(f.c_str(), "MANIFEST") != nullptr) { *r = new ManifestFile(this, *r); } } @@ -166,16 +208,17 @@ class SpecialEnv : public EnvWrapper { private: RandomAccessFile* target_; AtomicCounter* counter_; + public: CountingFile(RandomAccessFile* target, AtomicCounter* counter) - : target_(target), counter_(counter) { - } - virtual ~CountingFile() { delete target_; } - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { + : target_(target), counter_(counter) {} + ~CountingFile() override { delete target_; } + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { counter_->Increment(); return target_->Read(offset, n, result, scratch); } + std::string GetName() const override { return ""; } }; Status s = target()->NewRandomAccessFile(f, r); @@ -187,18 +230,6 @@ class SpecialEnv : public EnvWrapper { }; class DBTest { - private: - const FilterPolicy* filter_policy_; - - // Sequence of option configurations to try - enum OptionConfig { - kDefault, - kFilter, - kUncompressed, - kEnd - }; - int option_config_; - public: std::string dbname_; SpecialEnv* env_; @@ -206,12 +237,11 @@ class DBTest { Options last_options_; - DBTest() : option_config_(kDefault), - env_(new SpecialEnv(Env::Default())) { + DBTest() : env_(new SpecialEnv(Env::Default())), option_config_(kDefault) { filter_policy_ = NewBloomFilterPolicy(10); dbname_ = test::TmpDir() + "/db_test"; DestroyDB(dbname_, Options()); - db_ = NULL; + db_ = nullptr; Reopen(); } @@ -237,7 +267,11 @@ class DBTest { // Return the current option configuration. Options CurrentOptions() { Options options; + options.reuse_logs = false; switch (option_config_) { + case kReuse: + options.reuse_logs = true; + break; case kFilter: options.filter_policy = filter_policy_; break; @@ -250,31 +284,27 @@ class DBTest { return options; } - DBImpl* dbfull() { - return reinterpret_cast(db_); - } + DBImpl* dbfull() { return reinterpret_cast(db_); } - void Reopen(Options* options = NULL) { - ASSERT_OK(TryReopen(options)); - } + void Reopen(Options* options = nullptr) { ASSERT_OK(TryReopen(options)); } void Close() { delete db_; - db_ = NULL; + db_ = nullptr; } - void DestroyAndReopen(Options* options = NULL) { + void DestroyAndReopen(Options* options = nullptr) { delete db_; - db_ = NULL; + db_ = nullptr; DestroyDB(dbname_, Options()); ASSERT_OK(TryReopen(options)); } Status TryReopen(Options* options) { delete db_; - db_ = NULL; + db_ = nullptr; Options opts; - if (options != NULL) { + if (options != nullptr) { opts = *options; } else { opts = CurrentOptions(); @@ -289,11 +319,9 @@ class DBTest { return db_->Put(WriteOptions(), k, v); } - Status Delete(const std::string& k) { - return db_->Delete(WriteOptions(), k); - } + Status Delete(const std::string& k) { return db_->Delete(WriteOptions(), k); } - std::string Get(const std::string& k, const Snapshot* snapshot = NULL) { + std::string Get(const std::string& k, const Snapshot* snapshot = nullptr) { ReadOptions options; options.snapshot = snapshot; std::string result; @@ -377,10 +405,9 @@ class DBTest { int NumTableFilesAtLevel(int level) { std::string property; - ASSERT_TRUE( - db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level), - &property)); - return atoi(property.c_str()); + ASSERT_TRUE(db_->GetProperty( + "leveldb.num-files-at-level" + NumberToString(level), &property)); + return std::stoi(property); } int TotalTableFiles() { @@ -426,11 +453,12 @@ class DBTest { } // Do n memtable compactions, each of which produces an sstable - // covering the range [small,large]. - void MakeTables(int n, const std::string& small, const std::string& large) { + // covering the range [small_key,large_key]. + void MakeTables(int n, const std::string& small_key, + const std::string& large_key) { for (int i = 0; i < n; i++) { - Put(small, "begin"); - Put(large, "end"); + Put(small_key, "begin"); + Put(large_key, "end"); dbfull()->TEST_CompactMemTable(); } } @@ -443,9 +471,9 @@ class DBTest { void DumpFileCounts(const char* label) { fprintf(stderr, "---\n%s:\n", label); - fprintf(stderr, "maxoverlap: %lld\n", - static_cast( - dbfull()->TEST_MaxNextLevelOverlappingBytes())); + fprintf( + stderr, "maxoverlap: %lld\n", + static_cast(dbfull()->TEST_MaxNextLevelOverlappingBytes())); for (int level = 0; level < config::kNumLevels; level++) { int num = NumTableFilesAtLevel(level); if (num > 0) { @@ -501,15 +529,42 @@ class DBTest { } return files_renamed; } + + private: + // Sequence of option configurations to try + enum OptionConfig { kDefault, kReuse, kFilter, kUncompressed, kEnd }; + + const FilterPolicy* filter_policy_; + int option_config_; }; TEST(DBTest, Empty) { do { - ASSERT_TRUE(db_ != NULL); + ASSERT_TRUE(db_ != nullptr); ASSERT_EQ("NOT_FOUND", Get("foo")); } while (ChangeOptions()); } +TEST(DBTest, EmptyKey) { + do { + ASSERT_OK(Put("", "v1")); + ASSERT_EQ("v1", Get("")); + ASSERT_OK(Put("", "v2")); + ASSERT_EQ("v2", Get("")); + } while (ChangeOptions()); +} + +TEST(DBTest, EmptyValue) { + do { + ASSERT_OK(Put("key", "v1")); + ASSERT_EQ("v1", Get("key")); + ASSERT_OK(Put("key", "")); + ASSERT_EQ("", Get("key")); + ASSERT_OK(Put("key", "v2")); + ASSERT_EQ("v2", Get("key")); + } while (ChangeOptions()); +} + TEST(DBTest, ReadWrite) { do { ASSERT_OK(Put("foo", "v1")); @@ -542,11 +597,13 @@ TEST(DBTest, GetFromImmutableLayer) { ASSERT_OK(Put("foo", "v1")); ASSERT_EQ("v1", Get("foo")); - env_->delay_data_sync_.Release_Store(env_); // Block sync calls - Put("k1", std::string(100000, 'x')); // Fill memtable - Put("k2", std::string(100000, 'y')); // Trigger compaction + // Block sync calls. + env_->delay_data_sync_.store(true, std::memory_order_release); + Put("k1", std::string(100000, 'x')); // Fill memtable. + Put("k2", std::string(100000, 'y')); // Trigger compaction. ASSERT_EQ("v1", Get("foo")); - env_->delay_data_sync_.Release_Store(NULL); // Release sync calls + // Release sync calls. + env_->delay_data_sync_.store(false, std::memory_order_release); } while (ChangeOptions()); } @@ -558,6 +615,17 @@ TEST(DBTest, GetFromVersions) { } while (ChangeOptions()); } +TEST(DBTest, GetMemUsage) { + do { + ASSERT_OK(Put("foo", "v1")); + std::string val; + ASSERT_TRUE(db_->GetProperty("leveldb.approximate-memory-usage", &val)); + int mem_usage = std::stoi(val); + ASSERT_GT(mem_usage, 0); + ASSERT_LT(mem_usage, 5 * 1024 * 1024); + } while (ChangeOptions()); +} + TEST(DBTest, GetSnapshot) { do { // Try with both a short key and a long key @@ -576,6 +644,55 @@ TEST(DBTest, GetSnapshot) { } while (ChangeOptions()); } +TEST(DBTest, GetIdenticalSnapshots) { + do { + // Try with both a short key and a long key + for (int i = 0; i < 2; i++) { + std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x'); + ASSERT_OK(Put(key, "v1")); + const Snapshot* s1 = db_->GetSnapshot(); + const Snapshot* s2 = db_->GetSnapshot(); + const Snapshot* s3 = db_->GetSnapshot(); + ASSERT_OK(Put(key, "v2")); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s1)); + ASSERT_EQ("v1", Get(key, s2)); + ASSERT_EQ("v1", Get(key, s3)); + db_->ReleaseSnapshot(s1); + dbfull()->TEST_CompactMemTable(); + ASSERT_EQ("v2", Get(key)); + ASSERT_EQ("v1", Get(key, s2)); + db_->ReleaseSnapshot(s2); + ASSERT_EQ("v1", Get(key, s3)); + db_->ReleaseSnapshot(s3); + } + } while (ChangeOptions()); +} + +TEST(DBTest, IterateOverEmptySnapshot) { + do { + const Snapshot* snapshot = db_->GetSnapshot(); + ReadOptions read_options; + read_options.snapshot = snapshot; + ASSERT_OK(Put("foo", "v1")); + ASSERT_OK(Put("foo", "v2")); + + Iterator* iterator1 = db_->NewIterator(read_options); + iterator1->SeekToFirst(); + ASSERT_TRUE(!iterator1->Valid()); + delete iterator1; + + dbfull()->TEST_CompactMemTable(); + + Iterator* iterator2 = db_->NewIterator(read_options); + iterator2->SeekToFirst(); + ASSERT_TRUE(!iterator2->Valid()); + delete iterator2; + + db_->ReleaseSnapshot(snapshot); + } while (ChangeOptions()); +} + TEST(DBTest, GetLevel0Ordering) { do { // Check that we process level-0 files in correct order. The code @@ -630,8 +747,7 @@ TEST(DBTest, GetEncountersEmptyLevel) { // Step 1: First place sstables in levels 0 and 2 int compaction_count = 0; - while (NumTableFilesAtLevel(0) == 0 || - NumTableFilesAtLevel(2) == 0) { + while (NumTableFilesAtLevel(0) == 0 || NumTableFilesAtLevel(2) == 0) { ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2"; compaction_count++; Put("a", "begin"); @@ -640,7 +756,7 @@ TEST(DBTest, GetEncountersEmptyLevel) { } // Step 2: clear level 1 if necessary. - dbfull()->TEST_CompactRange(1, NULL, NULL); + dbfull()->TEST_CompactRange(1, nullptr, nullptr); ASSERT_EQ(NumTableFilesAtLevel(0), 1); ASSERT_EQ(NumTableFilesAtLevel(1), 0); ASSERT_EQ(NumTableFilesAtLevel(2), 1); @@ -768,10 +884,10 @@ TEST(DBTest, IterMulti) { ASSERT_EQ(IterStatus(iter), "b->vb"); // Make sure iter stays at snapshot - ASSERT_OK(Put("a", "va2")); + ASSERT_OK(Put("a", "va2")); ASSERT_OK(Put("a2", "va3")); - ASSERT_OK(Put("b", "vb2")); - ASSERT_OK(Put("c", "vc2")); + ASSERT_OK(Put("b", "vb2")); + ASSERT_OK(Put("c", "vc2")); ASSERT_OK(Delete("b")); iter->SeekToFirst(); ASSERT_EQ(IterStatus(iter), "a->va"); @@ -962,7 +1078,7 @@ TEST(DBTest, RecoverWithLargeLog) { TEST(DBTest, CompactionsGenerateMultipleFiles) { Options options = CurrentOptions(); - options.write_buffer_size = 100000000; // Large write buffer + options.write_buffer_size = 100000000; // Large write buffer Reopen(&options); Random rnd(301); @@ -977,7 +1093,7 @@ TEST(DBTest, CompactionsGenerateMultipleFiles) { // Reopening moves updates to level-0 Reopen(&options); - dbfull()->TEST_CompactRange(0, NULL, NULL); + dbfull()->TEST_CompactRange(0, nullptr, nullptr); ASSERT_EQ(NumTableFilesAtLevel(0), 0); ASSERT_GT(NumTableFilesAtLevel(1), 1); @@ -1001,7 +1117,7 @@ TEST(DBTest, RepeatedWritesToSameKey) { for (int i = 0; i < 5 * kMaxFiles; i++) { Put("key", value); ASSERT_LE(TotalTableFiles(), kMaxFiles); - fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles()); + fprintf(stderr, "after %d: %d files\n", i + 1, TotalTableFiles()); } } @@ -1028,29 +1144,28 @@ TEST(DBTest, SparseMerge) { } Put("C", "vc"); dbfull()->TEST_CompactMemTable(); - dbfull()->TEST_CompactRange(0, NULL, NULL); + dbfull()->TEST_CompactRange(0, nullptr, nullptr); // Make sparse update - Put("A", "va2"); + Put("A", "va2"); Put("B100", "bvalue2"); - Put("C", "vc2"); + Put("C", "vc2"); dbfull()->TEST_CompactMemTable(); // Compactions should not cause us to create a situation where // a file overlaps too much data at the next level. - ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); - dbfull()->TEST_CompactRange(0, NULL, NULL); - ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); - dbfull()->TEST_CompactRange(1, NULL, NULL); - ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20 * 1048576); + dbfull()->TEST_CompactRange(0, nullptr, nullptr); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20 * 1048576); + dbfull()->TEST_CompactRange(1, nullptr, nullptr); + ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20 * 1048576); } static bool Between(uint64_t val, uint64_t low, uint64_t high) { bool result = (val >= low) && (val <= high); if (!result) { fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", - (unsigned long long)(val), - (unsigned long long)(low), + (unsigned long long)(val), (unsigned long long)(low), (unsigned long long)(high)); } return result; @@ -1059,7 +1174,7 @@ static bool Between(uint64_t val, uint64_t low, uint64_t high) { TEST(DBTest, ApproximateSizes) { do { Options options = CurrentOptions(); - options.write_buffer_size = 100000000; // Large write buffer + options.write_buffer_size = 100000000; // Large write buffer options.compression = kNoCompression; DestroyAndReopen(); @@ -1080,18 +1195,27 @@ TEST(DBTest, ApproximateSizes) { // 0 because GetApproximateSizes() does not account for memtable space ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + if (options.reuse_logs) { + // Recovery will reuse memtable, and GetApproximateSizes() does not + // account for memtable usage; + Reopen(&options); + ASSERT_TRUE(Between(Size("", Key(50)), 0, 0)); + continue; + } + // Check sizes across recovery by reopening a few times for (int run = 0; run < 3; run++) { Reopen(&options); for (int compact_start = 0; compact_start < N; compact_start += 10) { for (int i = 0; i < N; i += 10) { - ASSERT_TRUE(Between(Size("", Key(i)), S1*i, S2*i)); - ASSERT_TRUE(Between(Size("", Key(i)+".suffix"), S1*(i+1), S2*(i+1))); - ASSERT_TRUE(Between(Size(Key(i), Key(i+10)), S1*10, S2*10)); + ASSERT_TRUE(Between(Size("", Key(i)), S1 * i, S2 * i)); + ASSERT_TRUE(Between(Size("", Key(i) + ".suffix"), S1 * (i + 1), + S2 * (i + 1))); + ASSERT_TRUE(Between(Size(Key(i), Key(i + 10)), S1 * 10, S2 * 10)); } - ASSERT_TRUE(Between(Size("", Key(50)), S1*50, S2*50)); - ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), S1*50, S2*50)); + ASSERT_TRUE(Between(Size("", Key(50)), S1 * 50, S2 * 50)); + ASSERT_TRUE(Between(Size("", Key(50) + ".suffix"), S1 * 50, S2 * 50)); std::string cstart_str = Key(compact_start); std::string cend_str = Key(compact_start + 9); @@ -1123,6 +1247,11 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000))); ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000))); + if (options.reuse_logs) { + // Need to force a memtable compaction since recovery does not do so. + ASSERT_OK(dbfull()->TEST_CompactMemTable()); + } + // Check sizes across recovery by reopening a few times for (int run = 0; run < 3; run++) { Reopen(&options); @@ -1139,7 +1268,7 @@ TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) { ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000)); - dbfull()->TEST_CompactRange(0, NULL, NULL); + dbfull()->TEST_CompactRange(0, nullptr, nullptr); } } while (ChangeOptions()); } @@ -1153,7 +1282,7 @@ TEST(DBTest, IteratorPinsRef) { // Write to force compactions Put("foo", "newvalue1"); for (int i = 0; i < 100; i++) { - ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values + ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values } Put("foo", "newvalue2"); @@ -1205,7 +1334,7 @@ TEST(DBTest, HiddenValuesAreRemoved) { Put("pastfoo", "v"); const Snapshot* snapshot = db_->GetSnapshot(); Put("foo", "tiny"); - Put("pastfoo2", "v2"); // Advance sequence number one more + Put("pastfoo2", "v2"); // Advance sequence number one more ASSERT_OK(dbfull()->TEST_CompactMemTable()); ASSERT_GT(NumTableFilesAtLevel(0), 0); @@ -1215,11 +1344,11 @@ TEST(DBTest, HiddenValuesAreRemoved) { db_->ReleaseSnapshot(snapshot); ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]"); Slice x("x"); - dbfull()->TEST_CompactRange(0, NULL, &x); + dbfull()->TEST_CompactRange(0, nullptr, &x); ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); ASSERT_EQ(NumTableFilesAtLevel(0), 0); ASSERT_GE(NumTableFilesAtLevel(1), 1); - dbfull()->TEST_CompactRange(1, NULL, &x); + dbfull()->TEST_CompactRange(1, nullptr, &x); ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]"); ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000)); @@ -1230,14 +1359,14 @@ TEST(DBTest, DeletionMarkers1) { Put("foo", "v1"); ASSERT_OK(dbfull()->TEST_CompactMemTable()); const int last = config::kMaxMemCompactLevel; - ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level // Place a table at level last-1 to prevent merging with preceding mutation Put("a", "begin"); Put("z", "end"); dbfull()->TEST_CompactMemTable(); ASSERT_EQ(NumTableFilesAtLevel(last), 1); - ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + ASSERT_EQ(NumTableFilesAtLevel(last - 1), 1); Delete("foo"); Put("foo", "v2"); @@ -1245,11 +1374,11 @@ TEST(DBTest, DeletionMarkers1) { ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]"); Slice z("z"); - dbfull()->TEST_CompactRange(last-2, NULL, &z); + dbfull()->TEST_CompactRange(last - 2, nullptr, &z); // DEL eliminated, but v1 remains because we aren't compacting that level // (DEL can be eliminated because v2 hides v1). ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]"); - dbfull()->TEST_CompactRange(last-1, NULL, NULL); + dbfull()->TEST_CompactRange(last - 1, nullptr, nullptr); // Merging last-1 w/ last, so we are the base level for "foo", so // DEL is removed. (as is v1). ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]"); @@ -1259,23 +1388,23 @@ TEST(DBTest, DeletionMarkers2) { Put("foo", "v1"); ASSERT_OK(dbfull()->TEST_CompactMemTable()); const int last = config::kMaxMemCompactLevel; - ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level // Place a table at level last-1 to prevent merging with preceding mutation Put("a", "begin"); Put("z", "end"); dbfull()->TEST_CompactMemTable(); ASSERT_EQ(NumTableFilesAtLevel(last), 1); - ASSERT_EQ(NumTableFilesAtLevel(last-1), 1); + ASSERT_EQ(NumTableFilesAtLevel(last - 1), 1); Delete("foo"); ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2 ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); - dbfull()->TEST_CompactRange(last-2, NULL, NULL); + dbfull()->TEST_CompactRange(last - 2, nullptr, nullptr); // DEL kept: "last" file overlaps ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]"); - dbfull()->TEST_CompactRange(last-1, NULL, NULL); + dbfull()->TEST_CompactRange(last - 1, nullptr, nullptr); // Merging last-1 w/ last, so we are the base level for "foo", so // DEL is removed. (as is v1). ASSERT_EQ(AllEntriesFor("foo"), "[ ]"); @@ -1285,7 +1414,8 @@ TEST(DBTest, OverlapInLevel0) { do { ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config"; - // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0. + // Fill levels 1 and 2 to disable the pushing of new memtables to levels > + // 0. ASSERT_OK(Put("100", "v100")); ASSERT_OK(Put("999", "v999")); dbfull()->TEST_CompactMemTable(); @@ -1308,8 +1438,8 @@ TEST(DBTest, OverlapInLevel0) { ASSERT_EQ("2,1,1", FilesPerLevel()); // Compact away the placeholder files we created initially - dbfull()->TEST_CompactRange(1, NULL, NULL); - dbfull()->TEST_CompactRange(2, NULL, NULL); + dbfull()->TEST_CompactRange(1, nullptr, nullptr); + dbfull()->TEST_CompactRange(2, nullptr, nullptr); ASSERT_EQ("2", FilesPerLevel()); // Do a memtable compaction. Before bug-fix, the compaction would @@ -1341,21 +1471,21 @@ TEST(DBTest, L0_CompactionBug_Issue44_a) { TEST(DBTest, L0_CompactionBug_Issue44_b) { Reopen(); - Put("",""); + Put("", ""); Reopen(); Delete("e"); - Put("",""); + Put("", ""); Reopen(); Put("c", "cv"); Reopen(); - Put("",""); + Put("", ""); Reopen(); - Put("",""); + Put("", ""); DelayMilliseconds(1000); // Wait for compaction to finish Reopen(); - Put("d","dv"); + Put("d", "dv"); Reopen(); - Put("",""); + Put("", ""); Reopen(); Delete("d"); Delete("b"); @@ -1365,17 +1495,26 @@ TEST(DBTest, L0_CompactionBug_Issue44_b) { ASSERT_EQ("(->)(c->cv)", Contents()); } +TEST(DBTest, Fflush_Issue474) { + static const int kNum = 100000; + Random rnd(test::RandomSeed()); + for (int i = 0; i < kNum; i++) { + fflush(nullptr); + ASSERT_OK(Put(RandomKey(&rnd), RandomString(&rnd, 100))); + } +} + TEST(DBTest, ComparatorCheck) { class NewComparator : public Comparator { public: - virtual const char* Name() const { return "leveldb.NewComparator"; } - virtual int Compare(const Slice& a, const Slice& b) const { + const char* Name() const override { return "leveldb.NewComparator"; } + int Compare(const Slice& a, const Slice& b) const override { return BytewiseComparator()->Compare(a, b); } - virtual void FindShortestSeparator(std::string* s, const Slice& l) const { + void FindShortestSeparator(std::string* s, const Slice& l) const override { BytewiseComparator()->FindShortestSeparator(s, l); } - virtual void FindShortSuccessor(std::string* key) const { + void FindShortSuccessor(std::string* key) const override { BytewiseComparator()->FindShortSuccessor(key); } }; @@ -1391,21 +1530,22 @@ TEST(DBTest, ComparatorCheck) { TEST(DBTest, CustomComparator) { class NumberComparator : public Comparator { public: - virtual const char* Name() const { return "test.NumberComparator"; } - virtual int Compare(const Slice& a, const Slice& b) const { + const char* Name() const override { return "test.NumberComparator"; } + int Compare(const Slice& a, const Slice& b) const override { return ToNumber(a) - ToNumber(b); } - virtual void FindShortestSeparator(std::string* s, const Slice& l) const { - ToNumber(*s); // Check format - ToNumber(l); // Check format + void FindShortestSeparator(std::string* s, const Slice& l) const override { + ToNumber(*s); // Check format + ToNumber(l); // Check format } - virtual void FindShortSuccessor(std::string* key) const { - ToNumber(*key); // Check format + void FindShortSuccessor(std::string* key) const override { + ToNumber(*key); // Check format } + private: static int ToNumber(const Slice& x) { // Check that there are no extra characters. - ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']') + ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size() - 1] == ']') << EscapeString(x); int val; char ignored; @@ -1418,7 +1558,7 @@ TEST(DBTest, CustomComparator) { Options new_options = CurrentOptions(); new_options.create_if_missing = true; new_options.comparator = &cmp; - new_options.filter_policy = NULL; // Cannot use bloom filters + new_options.filter_policy = nullptr; // Cannot use bloom filters new_options.write_buffer_size = 1000; // Compact more often DestroyAndReopen(&new_options); ASSERT_OK(Put("[10]", "ten")); @@ -1436,7 +1576,7 @@ TEST(DBTest, CustomComparator) { for (int run = 0; run < 2; run++) { for (int i = 0; i < 1000; i++) { char buf[100]; - snprintf(buf, sizeof(buf), "[%d]", i*10); + snprintf(buf, sizeof(buf), "[%d]", i * 10); ASSERT_OK(Put(buf, buf)); } Compact("[0]", "[1000000]"); @@ -1473,7 +1613,7 @@ TEST(DBTest, ManualCompaction) { // Compact all MakeTables(1, "a", "z"); ASSERT_EQ("0,1,2", FilesPerLevel()); - db_->CompactRange(NULL, NULL); + db_->CompactRange(nullptr, nullptr); ASSERT_EQ("0,0,1", FilesPerLevel()); } @@ -1482,42 +1622,94 @@ TEST(DBTest, DBOpen_Options) { DestroyDB(dbname, Options()); // Does not exist, and create_if_missing == false: error - DB* db = NULL; + DB* db = nullptr; Options opts; opts.create_if_missing = false; Status s = DB::Open(opts, dbname, &db); - ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL); - ASSERT_TRUE(db == NULL); + ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != nullptr); + ASSERT_TRUE(db == nullptr); // Does not exist, and create_if_missing == true: OK opts.create_if_missing = true; s = DB::Open(opts, dbname, &db); ASSERT_OK(s); - ASSERT_TRUE(db != NULL); + ASSERT_TRUE(db != nullptr); delete db; - db = NULL; + db = nullptr; // Does exist, and error_if_exists == true: error opts.create_if_missing = false; opts.error_if_exists = true; s = DB::Open(opts, dbname, &db); - ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL); - ASSERT_TRUE(db == NULL); + ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != nullptr); + ASSERT_TRUE(db == nullptr); // Does exist, and error_if_exists == false: OK opts.create_if_missing = true; opts.error_if_exists = false; s = DB::Open(opts, dbname, &db); ASSERT_OK(s); - ASSERT_TRUE(db != NULL); + ASSERT_TRUE(db != nullptr); delete db; - db = NULL; + db = nullptr; +} + +TEST(DBTest, DestroyEmptyDir) { + std::string dbname = test::TmpDir() + "/db_empty_dir"; + TestEnv env(Env::Default()); + env.DeleteDir(dbname); + ASSERT_TRUE(!env.FileExists(dbname)); + + Options opts; + opts.env = &env; + + ASSERT_OK(env.CreateDir(dbname)); + ASSERT_TRUE(env.FileExists(dbname)); + std::vector children; + ASSERT_OK(env.GetChildren(dbname, &children)); + // The stock Env's do not filter out '.' and '..' special files. + ASSERT_EQ(2, children.size()); + ASSERT_OK(DestroyDB(dbname, opts)); + ASSERT_TRUE(!env.FileExists(dbname)); + + // Should also be destroyed if Env is filtering out dot files. + env.SetIgnoreDotFiles(true); + ASSERT_OK(env.CreateDir(dbname)); + ASSERT_TRUE(env.FileExists(dbname)); + ASSERT_OK(env.GetChildren(dbname, &children)); + ASSERT_EQ(0, children.size()); + ASSERT_OK(DestroyDB(dbname, opts)); + ASSERT_TRUE(!env.FileExists(dbname)); +} + +TEST(DBTest, DestroyOpenDB) { + std::string dbname = test::TmpDir() + "/open_db_dir"; + env_->DeleteDir(dbname); + ASSERT_TRUE(!env_->FileExists(dbname)); + + Options opts; + opts.create_if_missing = true; + DB* db = nullptr; + ASSERT_OK(DB::Open(opts, dbname, &db)); + ASSERT_TRUE(db != nullptr); + + // Must fail to destroy an open db. + ASSERT_TRUE(env_->FileExists(dbname)); + ASSERT_TRUE(!DestroyDB(dbname, Options()).ok()); + ASSERT_TRUE(env_->FileExists(dbname)); + + delete db; + db = nullptr; + + // Should succeed destroying a closed db. + ASSERT_OK(DestroyDB(dbname, Options())); + ASSERT_TRUE(!env_->FileExists(dbname)); } TEST(DBTest, Locking) { - DB* db2 = NULL; + DB* db2 = nullptr; Status s = DB::Open(CurrentOptions(), dbname_, &db2); ASSERT_TRUE(!s.ok()) << "Locking did not prevent re-opening db"; } @@ -1532,13 +1724,14 @@ TEST(DBTest, NoSpace) { ASSERT_EQ("v1", Get("foo")); Compact("a", "z"); const int num_files = CountFiles(); - env_->no_space_.Release_Store(env_); // Force out-of-space errors + // Force out-of-space errors. + env_->no_space_.store(true, std::memory_order_release); for (int i = 0; i < 10; i++) { - for (int level = 0; level < config::kNumLevels-1; level++) { - dbfull()->TEST_CompactRange(level, NULL, NULL); + for (int level = 0; level < config::kNumLevels - 1; level++) { + dbfull()->TEST_CompactRange(level, nullptr, nullptr); } } - env_->no_space_.Release_Store(NULL); + env_->no_space_.store(false, std::memory_order_release); ASSERT_LT(CountFiles(), num_files + 3); } @@ -1548,7 +1741,8 @@ TEST(DBTest, NonWritableFileSystem) { options.env = env_; Reopen(&options); ASSERT_OK(Put("foo", "v1")); - env_->non_writable_.Release_Store(env_); // Force errors for new files + // Force errors for new files. + env_->non_writable_.store(true, std::memory_order_release); std::string big(100000, 'x'); int errors = 0; for (int i = 0; i < 20; i++) { @@ -1559,7 +1753,7 @@ TEST(DBTest, NonWritableFileSystem) { } } ASSERT_GT(errors, 0); - env_->non_writable_.Release_Store(NULL); + env_->non_writable_.store(false, std::memory_order_release); } TEST(DBTest, WriteSyncError) { @@ -1569,7 +1763,7 @@ TEST(DBTest, WriteSyncError) { Options options = CurrentOptions(); options.env = env_; Reopen(&options); - env_->data_sync_error_.Release_Store(env_); + env_->data_sync_error_.store(true, std::memory_order_release); // (b) Normal write should succeed WriteOptions w; @@ -1583,7 +1777,7 @@ TEST(DBTest, WriteSyncError) { ASSERT_EQ("NOT_FOUND", Get("k2")); // (d) make sync behave normally - env_->data_sync_error_.Release_Store(NULL); + env_->data_sync_error_.store(false, std::memory_order_release); // (e) Do a non-sync write; should fail w.sync = false; @@ -1603,9 +1797,8 @@ TEST(DBTest, ManifestWriteError) { // We iterate twice. In the second iteration, everything is the // same except the log record never makes it to the MANIFEST file. for (int iter = 0; iter < 2; iter++) { - port::AtomicPointer* error_type = (iter == 0) - ? &env_->manifest_sync_error_ - : &env_->manifest_write_error_; + std::atomic* error_type = (iter == 0) ? &env_->manifest_sync_error_ + : &env_->manifest_write_error_; // Insert foo=>bar mapping Options options = CurrentOptions(); @@ -1620,15 +1813,15 @@ TEST(DBTest, ManifestWriteError) { dbfull()->TEST_CompactMemTable(); ASSERT_EQ("bar", Get("foo")); const int last = config::kMaxMemCompactLevel; - ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level + ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo=>bar is now in last level // Merging compaction (will fail) - error_type->Release_Store(env_); - dbfull()->TEST_CompactRange(last, NULL, NULL); // Should fail + error_type->store(true, std::memory_order_release); + dbfull()->TEST_CompactRange(last, nullptr, nullptr); // Should fail ASSERT_EQ("bar", Get("foo")); // Recovery: should not lose data - error_type->Release_Store(NULL); + error_type->store(false, std::memory_order_release); Reopen(&options); ASSERT_EQ("bar", Get("foo")); } @@ -1648,8 +1841,7 @@ TEST(DBTest, MissingSSTFile) { options.paranoid_checks = true; Status s = TryReopen(&options); ASSERT_TRUE(!s.ok()); - ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) - << s.ToString(); + ASSERT_TRUE(s.ToString().find("issing") != std::string::npos) << s.ToString(); } TEST(DBTest, StillReadSST) { @@ -1699,7 +1891,7 @@ TEST(DBTest, BloomFilter) { dbfull()->TEST_CompactMemTable(); // Prevent auto compactions triggered by seeks - env_->delay_data_sync_.Release_Store(env_); + env_->delay_data_sync_.store(true, std::memory_order_release); // Lookup present keys. Should rarely read from small sstable. env_->random_read_counter_.Reset(); @@ -1709,7 +1901,7 @@ TEST(DBTest, BloomFilter) { int reads = env_->random_read_counter_.Read(); fprintf(stderr, "%d present => %d reads\n", N, reads); ASSERT_GE(reads, N); - ASSERT_LE(reads, N + 2*N/100); + ASSERT_LE(reads, N + 2 * N / 100); // Lookup present keys. Should rarely read from either sstable. env_->random_read_counter_.Reset(); @@ -1718,9 +1910,9 @@ TEST(DBTest, BloomFilter) { } reads = env_->random_read_counter_.Read(); fprintf(stderr, "%d missing => %d reads\n", N, reads); - ASSERT_LE(reads, 3*N/100); + ASSERT_LE(reads, 3 * N / 100); - env_->delay_data_sync_.Release_Store(NULL); + env_->delay_data_sync_.store(false, std::memory_order_release); Close(); delete options.block_cache; delete options.filter_policy; @@ -1735,9 +1927,9 @@ static const int kNumKeys = 1000; struct MTState { DBTest* test; - port::AtomicPointer stop; - port::AtomicPointer counter[kNumThreads]; - port::AtomicPointer thread_done[kNumThreads]; + std::atomic stop; + std::atomic counter[kNumThreads]; + std::atomic thread_done[kNumThreads]; }; struct MTThread { @@ -1749,13 +1941,13 @@ static void MTThreadBody(void* arg) { MTThread* t = reinterpret_cast(arg); int id = t->id; DB* db = t->state->test->db_; - uintptr_t counter = 0; + int counter = 0; fprintf(stderr, "... starting thread %d\n", id); Random rnd(1000 + id); std::string value; char valbuf[1500]; - while (t->state->stop.Acquire_Load() == NULL) { - t->state->counter[id].Release_Store(reinterpret_cast(counter)); + while (!t->state->stop.load(std::memory_order_acquire)) { + t->state->counter[id].store(counter, std::memory_order_release); int key = rnd.Uniform(kNumKeys); char keybuf[20]; @@ -1764,8 +1956,8 @@ static void MTThreadBody(void* arg) { if (rnd.OneIn(2)) { // Write values of the form . // We add some padding for force compactions. - snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", - key, id, static_cast(counter)); + snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d", key, id, + static_cast(counter)); ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf))); } else { // Read a value and verify that it matches the pattern written above. @@ -1780,14 +1972,13 @@ static void MTThreadBody(void* arg) { ASSERT_EQ(k, key); ASSERT_GE(w, 0); ASSERT_LT(w, kNumThreads); - ASSERT_LE(static_cast(c), reinterpret_cast( - t->state->counter[w].Acquire_Load())); + ASSERT_LE(c, t->state->counter[w].load(std::memory_order_acquire)); } } counter++; } - t->state->thread_done[id].Release_Store(t); - fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter)); + t->state->thread_done[id].store(true, std::memory_order_release); + fprintf(stderr, "... stopping thread %d after %d ops\n", id, counter); } } // namespace @@ -1797,10 +1988,10 @@ TEST(DBTest, MultiThreaded) { // Initialize state MTState mt; mt.test = this; - mt.stop.Release_Store(0); + mt.stop.store(false, std::memory_order_release); for (int id = 0; id < kNumThreads; id++) { - mt.counter[id].Release_Store(0); - mt.thread_done[id].Release_Store(0); + mt.counter[id].store(false, std::memory_order_release); + mt.thread_done[id].store(false, std::memory_order_release); } // Start threads @@ -1815,9 +2006,9 @@ TEST(DBTest, MultiThreaded) { DelayMilliseconds(kTestSeconds * 1000); // Stop the threads and wait for them to finish - mt.stop.Release_Store(&mt); + mt.stop.store(true, std::memory_order_release); for (int id = 0; id < kNumThreads; id++) { - while (mt.thread_done[id].Acquire_Load() == NULL) { + while (!mt.thread_done[id].load(std::memory_order_acquire)) { DelayMilliseconds(100); } } @@ -1828,28 +2019,28 @@ namespace { typedef std::map KVMap; } -class ModelDB: public DB { +class ModelDB : public DB { public: class ModelSnapshot : public Snapshot { public: KVMap map_; }; - explicit ModelDB(const Options& options): options_(options) { } - ~ModelDB() { } - virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) { + explicit ModelDB(const Options& options) : options_(options) {} + ~ModelDB() override = default; + Status Put(const WriteOptions& o, const Slice& k, const Slice& v) override { return DB::Put(o, k, v); } - virtual Status Delete(const WriteOptions& o, const Slice& key) { + Status Delete(const WriteOptions& o, const Slice& key) override { return DB::Delete(o, key); } - virtual Status Get(const ReadOptions& options, - const Slice& key, std::string* value) { - assert(false); // Not implemented + Status Get(const ReadOptions& options, const Slice& key, + std::string* value) override { + assert(false); // Not implemented return Status::NotFound(key); } - virtual Iterator* NewIterator(const ReadOptions& options) { - if (options.snapshot == NULL) { + Iterator* NewIterator(const ReadOptions& options) override { + if (options.snapshot == nullptr) { KVMap* saved = new KVMap; *saved = map_; return new ModelIter(saved, true); @@ -1859,68 +2050,65 @@ class ModelDB: public DB { return new ModelIter(snapshot_state, false); } } - virtual const Snapshot* GetSnapshot() { + const Snapshot* GetSnapshot() override { ModelSnapshot* snapshot = new ModelSnapshot; snapshot->map_ = map_; return snapshot; } - virtual void ReleaseSnapshot(const Snapshot* snapshot) { + void ReleaseSnapshot(const Snapshot* snapshot) override { delete reinterpret_cast(snapshot); } - virtual Status Write(const WriteOptions& options, WriteBatch* batch) { + Status Write(const WriteOptions& options, WriteBatch* batch) override { class Handler : public WriteBatch::Handler { public: KVMap* map_; - virtual void Put(const Slice& key, const Slice& value) { + void Put(const Slice& key, const Slice& value) override { (*map_)[key.ToString()] = value.ToString(); } - virtual void Delete(const Slice& key) { - map_->erase(key.ToString()); - } + void Delete(const Slice& key) override { map_->erase(key.ToString()); } }; Handler handler; handler.map_ = &map_; return batch->Iterate(&handler); } - virtual bool GetProperty(const Slice& property, std::string* value) { + bool GetProperty(const Slice& property, std::string* value) override { return false; } - virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) { + void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) override { for (int i = 0; i < n; i++) { sizes[i] = 0; } } - virtual void CompactRange(const Slice* start, const Slice* end) { - } + void CompactRange(const Slice* start, const Slice* end) override {} private: - class ModelIter: public Iterator { + class ModelIter : public Iterator { public: ModelIter(const KVMap* map, bool owned) - : map_(map), owned_(owned), iter_(map_->end()) { - } - ~ModelIter() { + : map_(map), owned_(owned), iter_(map_->end()) {} + ~ModelIter() override { if (owned_) delete map_; } - virtual bool Valid() const { return iter_ != map_->end(); } - virtual void SeekToFirst() { iter_ = map_->begin(); } - virtual void SeekToLast() { + bool Valid() const override { return iter_ != map_->end(); } + void SeekToFirst() override { iter_ = map_->begin(); } + void SeekToLast() override { if (map_->empty()) { iter_ = map_->end(); } else { iter_ = map_->find(map_->rbegin()->first); } } - virtual void Seek(const Slice& k) { + void Seek(const Slice& k) override { iter_ = map_->lower_bound(k.ToString()); } - virtual void Next() { ++iter_; } - virtual void Prev() { --iter_; } - virtual Slice key() const { return iter_->first; } - virtual Slice value() const { return iter_->second; } - virtual Status status() const { return Status::OK(); } + void Next() override { ++iter_; } + void Prev() override { --iter_; } + Slice key() const override { return iter_->first; } + Slice value() const override { return iter_->second; } + Status status() const override { return Status::OK(); } + private: const KVMap* const map_; const bool owned_; // Do we own map_ @@ -1930,16 +2118,7 @@ class ModelDB: public DB { KVMap map_; }; -static std::string RandomKey(Random* rnd) { - int len = (rnd->OneIn(3) - ? 1 // Short sometimes to encourage collisions - : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10))); - return test::RandomKey(rnd, len); -} - -static bool CompareIterators(int step, - DB* model, - DB* db, +static bool CompareIterators(int step, DB* model, DB* db, const Snapshot* model_snap, const Snapshot* db_snap) { ReadOptions options; @@ -1950,12 +2129,10 @@ static bool CompareIterators(int step, bool ok = true; int count = 0; for (miter->SeekToFirst(), dbiter->SeekToFirst(); - ok && miter->Valid() && dbiter->Valid(); - miter->Next(), dbiter->Next()) { + ok && miter->Valid() && dbiter->Valid(); miter->Next(), dbiter->Next()) { count++; if (miter->key().compare(dbiter->key()) != 0) { - fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", - step, + fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n", step, EscapeString(miter->key()).c_str(), EscapeString(dbiter->key()).c_str()); ok = false; @@ -1964,8 +2141,7 @@ static bool CompareIterators(int step, if (miter->value().compare(dbiter->value()) != 0) { fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n", - step, - EscapeString(miter->key()).c_str(), + step, EscapeString(miter->key()).c_str(), EscapeString(miter->value()).c_str(), EscapeString(miter->value()).c_str()); ok = false; @@ -1990,8 +2166,8 @@ TEST(DBTest, Randomized) { do { ModelDB model(CurrentOptions()); const int N = 10000; - const Snapshot* model_snap = NULL; - const Snapshot* db_snap = NULL; + const Snapshot* model_snap = nullptr; + const Snapshot* db_snap = nullptr; std::string k, v; for (int step = 0; step < N; step++) { if (step % 100 == 0) { @@ -1999,22 +2175,19 @@ TEST(DBTest, Randomized) { } // TODO(sanjay): Test Get() works int p = rnd.Uniform(100); - if (p < 45) { // Put + if (p < 45) { // Put k = RandomKey(&rnd); - v = RandomString(&rnd, - rnd.OneIn(20) - ? 100 + rnd.Uniform(100) - : rnd.Uniform(8)); + v = RandomString( + &rnd, rnd.OneIn(20) ? 100 + rnd.Uniform(100) : rnd.Uniform(8)); ASSERT_OK(model.Put(WriteOptions(), k, v)); ASSERT_OK(db_->Put(WriteOptions(), k, v)); - } else if (p < 90) { // Delete + } else if (p < 90) { // Delete k = RandomKey(&rnd); ASSERT_OK(model.Delete(WriteOptions(), k)); ASSERT_OK(db_->Delete(WriteOptions(), k)); - - } else { // Multi-element batch + } else { // Multi-element batch WriteBatch b; const int num = rnd.Uniform(8); for (int i = 0; i < num; i++) { @@ -2036,23 +2209,23 @@ TEST(DBTest, Randomized) { } if ((step % 100) == 0) { - ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, nullptr, nullptr)); ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap)); // Save a snapshot from each DB this time that we'll use next // time we compare things, to make sure the current state is // preserved with the snapshot - if (model_snap != NULL) model.ReleaseSnapshot(model_snap); - if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + if (model_snap != nullptr) model.ReleaseSnapshot(model_snap); + if (db_snap != nullptr) db_->ReleaseSnapshot(db_snap); Reopen(); - ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL)); + ASSERT_TRUE(CompareIterators(step, &model, db_, nullptr, nullptr)); model_snap = model.GetSnapshot(); db_snap = db_->GetSnapshot(); } } - if (model_snap != NULL) model.ReleaseSnapshot(model_snap); - if (db_snap != NULL) db_->ReleaseSnapshot(db_snap); + if (model_snap != nullptr) model.ReleaseSnapshot(model_snap); + if (db_snap != nullptr) db_->ReleaseSnapshot(db_snap); } while (ChangeOptions()); } @@ -2066,15 +2239,15 @@ void BM_LogAndApply(int iters, int num_base_files) { std::string dbname = test::TmpDir() + "/leveldb_test_benchmark"; DestroyDB(dbname, Options()); - DB* db = NULL; + DB* db = nullptr; Options opts; opts.create_if_missing = true; Status s = DB::Open(opts, dbname, &db); ASSERT_OK(s); - ASSERT_TRUE(db != NULL); + ASSERT_TRUE(db != nullptr); delete db; - db = NULL; + db = nullptr; Env* env = Env::Default(); @@ -2083,13 +2256,14 @@ void BM_LogAndApply(int iters, int num_base_files) { InternalKeyComparator cmp(BytewiseComparator()); Options options; - VersionSet vset(dbname, &options, NULL, &cmp); - ASSERT_OK(vset.Recover()); + VersionSet vset(dbname, &options, nullptr, &cmp); + bool save_manifest; + ASSERT_OK(vset.Recover(&save_manifest)); VersionEdit vbase; uint64_t fnum = 1; for (int i = 0; i < num_base_files; i++) { - InternalKey start(MakeKey(2*fnum), 1, kTypeValue); - InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + InternalKey start(MakeKey(2 * fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion); vbase.AddFile(2, fnum++, 1 /* file size */, start, limit); } ASSERT_OK(vset.LogAndApply(&vbase, &mu)); @@ -2099,8 +2273,8 @@ void BM_LogAndApply(int iters, int num_base_files) { for (int i = 0; i < iters; i++) { VersionEdit vedit; vedit.DeleteFile(2, fnum); - InternalKey start(MakeKey(2*fnum), 1, kTypeValue); - InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion); + InternalKey start(MakeKey(2 * fnum), 1, kTypeValue); + InternalKey limit(MakeKey(2 * fnum + 1), 1, kTypeDeletion); vedit.AddFile(2, fnum++, 1 /* file size */, start, limit); vset.LogAndApply(&vedit, &mu); } @@ -2109,8 +2283,8 @@ void BM_LogAndApply(int iters, int num_base_files) { char buf[16]; snprintf(buf, sizeof(buf), "%d", num_base_files); fprintf(stderr, - "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", - buf, iters, us, ((float)us) / iters); + "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n", buf, + iters, us, ((float)us) / iters); } } // namespace leveldb diff --git a/src/leveldb/db/dbformat.cc b/src/leveldb/db/dbformat.cc index 20a7ca446..459eddf5b 100644 --- a/src/leveldb/db/dbformat.cc +++ b/src/leveldb/db/dbformat.cc @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include #include "db/dbformat.h" + +#include + +#include + #include "port/port.h" #include "util/coding.h" @@ -21,26 +25,20 @@ void AppendInternalKey(std::string* result, const ParsedInternalKey& key) { } std::string ParsedInternalKey::DebugString() const { - char buf[50]; - snprintf(buf, sizeof(buf), "' @ %llu : %d", - (unsigned long long) sequence, - int(type)); - std::string result = "'"; - result += EscapeString(user_key.ToString()); - result += buf; - return result; + std::ostringstream ss; + ss << '\'' << EscapeString(user_key.ToString()) << "' @ " << sequence << " : " + << static_cast(type); + return ss.str(); } std::string InternalKey::DebugString() const { - std::string result; ParsedInternalKey parsed; if (ParseInternalKey(rep_, &parsed)) { - result = parsed.DebugString(); - } else { - result = "(bad)"; - result.append(EscapeString(rep_)); + return parsed.DebugString(); } - return result; + std::ostringstream ss; + ss << "(bad)" << EscapeString(rep_); + return ss.str(); } const char* InternalKeyComparator::Name() const { @@ -65,9 +63,8 @@ int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const { return r; } -void InternalKeyComparator::FindShortestSeparator( - std::string* start, - const Slice& limit) const { +void InternalKeyComparator::FindShortestSeparator(std::string* start, + const Slice& limit) const { // Attempt to shorten the user portion of the key Slice user_start = ExtractUserKey(*start); Slice user_limit = ExtractUserKey(limit); @@ -77,7 +74,8 @@ void InternalKeyComparator::FindShortestSeparator( user_comparator_->Compare(user_start, tmp) < 0) { // User key has become shorter physically, but larger logically. // Tack on the earliest possible number to the shortened user key. - PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + PutFixed64(&tmp, + PackSequenceAndType(kMaxSequenceNumber, kValueTypeForSeek)); assert(this->Compare(*start, tmp) < 0); assert(this->Compare(tmp, limit) < 0); start->swap(tmp); @@ -92,15 +90,14 @@ void InternalKeyComparator::FindShortSuccessor(std::string* key) const { user_comparator_->Compare(user_key, tmp) < 0) { // User key has become shorter physically, but larger logically. // Tack on the earliest possible number to the shortened user key. - PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek)); + PutFixed64(&tmp, + PackSequenceAndType(kMaxSequenceNumber, kValueTypeForSeek)); assert(this->Compare(*key, tmp) < 0); key->swap(tmp); } } -const char* InternalFilterPolicy::Name() const { - return user_policy_->Name(); -} +const char* InternalFilterPolicy::Name() const { return user_policy_->Name(); } void InternalFilterPolicy::CreateFilter(const Slice* keys, int n, std::string* dst) const { diff --git a/src/leveldb/db/dbformat.h b/src/leveldb/db/dbformat.h index ea897b13c..a1c30ed88 100644 --- a/src/leveldb/db/dbformat.h +++ b/src/leveldb/db/dbformat.h @@ -5,7 +5,10 @@ #ifndef STORAGE_LEVELDB_DB_DBFORMAT_H_ #define STORAGE_LEVELDB_DB_DBFORMAT_H_ -#include +#include +#include +#include + #include "leveldb/comparator.h" #include "leveldb/db.h" #include "leveldb/filter_policy.h" @@ -48,10 +51,7 @@ class InternalKey; // Value types encoded as the last component of internal keys. // DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk // data structures. -enum ValueType { - kTypeDeletion = 0x0, - kTypeValue = 0x1 -}; +enum ValueType { kTypeDeletion = 0x0, kTypeValue = 0x1 }; // kValueTypeForSeek defines the ValueType that should be passed when // constructing a ParsedInternalKey object for seeking to a particular // sequence number (since we sort sequence numbers in decreasing order @@ -64,17 +64,16 @@ typedef uint64_t SequenceNumber; // We leave eight bits empty at the bottom so a type and sequence# // can be packed together into 64-bits. -static const SequenceNumber kMaxSequenceNumber = - ((0x1ull << 56) - 1); +static const SequenceNumber kMaxSequenceNumber = ((0x1ull << 56) - 1); struct ParsedInternalKey { Slice user_key; SequenceNumber sequence; ValueType type; - ParsedInternalKey() { } // Intentionally left uninitialized (for speed) + ParsedInternalKey() {} // Intentionally left uninitialized (for speed) ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t) - : user_key(u), sequence(seq), type(t) { } + : user_key(u), sequence(seq), type(t) {} std::string DebugString() const; }; @@ -84,15 +83,13 @@ inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) { } // Append the serialization of "key" to *result. -extern void AppendInternalKey(std::string* result, - const ParsedInternalKey& key); +void AppendInternalKey(std::string* result, const ParsedInternalKey& key); // Attempt to parse an internal key from "internal_key". On success, // stores the parsed data in "*result", and returns true. // // On error, returns false, leaves "*result" in an undefined state. -extern bool ParseInternalKey(const Slice& internal_key, - ParsedInternalKey* result); +bool ParseInternalKey(const Slice& internal_key, ParsedInternalKey* result); // Returns the user key portion of an internal key. inline Slice ExtractUserKey(const Slice& internal_key) { @@ -100,27 +97,19 @@ inline Slice ExtractUserKey(const Slice& internal_key) { return Slice(internal_key.data(), internal_key.size() - 8); } -inline ValueType ExtractValueType(const Slice& internal_key) { - assert(internal_key.size() >= 8); - const size_t n = internal_key.size(); - uint64_t num = DecodeFixed64(internal_key.data() + n - 8); - unsigned char c = num & 0xff; - return static_cast(c); -} - // A comparator for internal keys that uses a specified comparator for // the user key portion and breaks ties by decreasing sequence number. class InternalKeyComparator : public Comparator { private: const Comparator* user_comparator_; + public: - explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { } - virtual const char* Name() const; - virtual int Compare(const Slice& a, const Slice& b) const; - virtual void FindShortestSeparator( - std::string* start, - const Slice& limit) const; - virtual void FindShortSuccessor(std::string* key) const; + explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) {} + const char* Name() const override; + int Compare(const Slice& a, const Slice& b) const override; + void FindShortestSeparator(std::string* start, + const Slice& limit) const override; + void FindShortSuccessor(std::string* key) const override; const Comparator* user_comparator() const { return user_comparator_; } @@ -131,11 +120,12 @@ class InternalKeyComparator : public Comparator { class InternalFilterPolicy : public FilterPolicy { private: const FilterPolicy* const user_policy_; + public: - explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) { } - virtual const char* Name() const; - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const; - virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const; + explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) {} + const char* Name() const override; + void CreateFilter(const Slice* keys, int n, std::string* dst) const override; + bool KeyMayMatch(const Slice& key, const Slice& filter) const override; }; // Modules in this directory should keep internal keys wrapped inside @@ -144,13 +134,18 @@ class InternalFilterPolicy : public FilterPolicy { class InternalKey { private: std::string rep_; + public: - InternalKey() { } // Leave rep_ as empty to indicate it is invalid + InternalKey() {} // Leave rep_ as empty to indicate it is invalid InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) { AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t)); } - void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); } + bool DecodeFrom(const Slice& s) { + rep_.assign(s.data(), s.size()); + return !rep_.empty(); + } + Slice Encode() const { assert(!rep_.empty()); return rep_; @@ -168,8 +163,8 @@ class InternalKey { std::string DebugString() const; }; -inline int InternalKeyComparator::Compare( - const InternalKey& a, const InternalKey& b) const { +inline int InternalKeyComparator::Compare(const InternalKey& a, + const InternalKey& b) const { return Compare(a.Encode(), b.Encode()); } @@ -178,11 +173,11 @@ inline bool ParseInternalKey(const Slice& internal_key, const size_t n = internal_key.size(); if (n < 8) return false; uint64_t num = DecodeFixed64(internal_key.data() + n - 8); - unsigned char c = num & 0xff; + uint8_t c = num & 0xff; result->sequence = num >> 8; result->type = static_cast(c); result->user_key = Slice(internal_key.data(), n - 8); - return (c <= static_cast(kTypeValue)); + return (c <= static_cast(kTypeValue)); } // A helper class useful for DBImpl::Get() @@ -192,6 +187,9 @@ class LookupKey { // the specified sequence number. LookupKey(const Slice& user_key, SequenceNumber sequence); + LookupKey(const LookupKey&) = delete; + LookupKey& operator=(const LookupKey&) = delete; + ~LookupKey(); // Return a key suitable for lookup in a MemTable. @@ -214,11 +212,7 @@ class LookupKey { const char* start_; const char* kstart_; const char* end_; - char space_[200]; // Avoid allocation for short keys - - // No copying allowed - LookupKey(const LookupKey&); - void operator=(const LookupKey&); + char space_[200]; // Avoid allocation for short keys }; inline LookupKey::~LookupKey() { diff --git a/src/leveldb/db/dbformat_test.cc b/src/leveldb/db/dbformat_test.cc index 5d82f5d31..1209369c3 100644 --- a/src/leveldb/db/dbformat_test.cc +++ b/src/leveldb/db/dbformat_test.cc @@ -8,8 +8,7 @@ namespace leveldb { -static std::string IKey(const std::string& user_key, - uint64_t seq, +static std::string IKey(const std::string& user_key, uint64_t seq, ValueType vt) { std::string encoded; AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt)); @@ -28,9 +27,7 @@ static std::string ShortSuccessor(const std::string& s) { return result; } -static void TestKey(const std::string& key, - uint64_t seq, - ValueType vt) { +static void TestKey(const std::string& key, uint64_t seq, ValueType vt) { std::string encoded = IKey(key, seq, vt); Slice in(encoded); @@ -44,16 +41,22 @@ static void TestKey(const std::string& key, ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded)); } -class FormatTest { }; +class FormatTest {}; TEST(FormatTest, InternalKey_EncodeDecode) { - const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" }; - const uint64_t seq[] = { - 1, 2, 3, - (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1, - (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1, - (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1 - }; + const char* keys[] = {"", "k", "hello", "longggggggggggggggggggggg"}; + const uint64_t seq[] = {1, + 2, + 3, + (1ull << 8) - 1, + 1ull << 8, + (1ull << 8) + 1, + (1ull << 16) - 1, + 1ull << 16, + (1ull << 16) + 1, + (1ull << 32) - 1, + 1ull << 32, + (1ull << 32) + 1}; for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) { for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) { TestKey(keys[k], seq[s], kTypeValue); @@ -62,40 +65,44 @@ TEST(FormatTest, InternalKey_EncodeDecode) { } } +TEST(FormatTest, InternalKey_DecodeFromEmpty) { + InternalKey internal_key; + + ASSERT_TRUE(!internal_key.DecodeFrom("")); +} + TEST(FormatTest, InternalKeyShortSeparator) { // When user keys are same ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foo", 99, kTypeValue))); - ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foo", 101, kTypeValue))); - ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foo", 100, kTypeValue))); - ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foo", 100, kTypeDeletion))); + Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 99, kTypeValue))); + ASSERT_EQ( + IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 101, kTypeValue))); + ASSERT_EQ( + IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 100, kTypeValue))); + ASSERT_EQ( + IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), IKey("foo", 100, kTypeDeletion))); // When user keys are misordered ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("bar", 99, kTypeValue))); + Shorten(IKey("foo", 100, kTypeValue), IKey("bar", 99, kTypeValue))); // When user keys are different, but correctly ordered - ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek), - Shorten(IKey("foo", 100, kTypeValue), - IKey("hello", 200, kTypeValue))); + ASSERT_EQ( + IKey("g", kMaxSequenceNumber, kValueTypeForSeek), + Shorten(IKey("foo", 100, kTypeValue), IKey("hello", 200, kTypeValue))); // When start user key is prefix of limit user key - ASSERT_EQ(IKey("foo", 100, kTypeValue), - Shorten(IKey("foo", 100, kTypeValue), - IKey("foobar", 200, kTypeValue))); + ASSERT_EQ( + IKey("foo", 100, kTypeValue), + Shorten(IKey("foo", 100, kTypeValue), IKey("foobar", 200, kTypeValue))); // When limit user key is prefix of start user key - ASSERT_EQ(IKey("foobar", 100, kTypeValue), - Shorten(IKey("foobar", 100, kTypeValue), - IKey("foo", 200, kTypeValue))); + ASSERT_EQ( + IKey("foobar", 100, kTypeValue), + Shorten(IKey("foobar", 100, kTypeValue), IKey("foo", 200, kTypeValue))); } TEST(FormatTest, InternalKeyShortestSuccessor) { @@ -105,8 +112,20 @@ TEST(FormatTest, InternalKeyShortestSuccessor) { ShortSuccessor(IKey("\xff\xff", 100, kTypeValue))); } -} // namespace leveldb +TEST(FormatTest, ParsedInternalKeyDebugString) { + ParsedInternalKey key("The \"key\" in 'single quotes'", 42, kTypeValue); + + ASSERT_EQ("'The \"key\" in 'single quotes'' @ 42 : 1", key.DebugString()); +} + +TEST(FormatTest, InternalKeyDebugString) { + InternalKey key("The \"key\" in 'single quotes'", 42, kTypeValue); + ASSERT_EQ("'The \"key\" in 'single quotes'' @ 42 : 1", key.DebugString()); -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); + InternalKey invalid_key; + ASSERT_EQ("(bad)", invalid_key.DebugString()); } + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/dumpfile.cc b/src/leveldb/db/dumpfile.cc index 61c47c2ff..77d59003c 100644 --- a/src/leveldb/db/dumpfile.cc +++ b/src/leveldb/db/dumpfile.cc @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "leveldb/dumpfile.h" + #include + #include "db/dbformat.h" #include "db/filename.h" #include "db/log_reader.h" @@ -35,8 +38,7 @@ bool GuessType(const std::string& fname, FileType* type) { // Notified when log reader encounters corruption. class CorruptionReporter : public log::Reader::Reporter { public: - WritableFile* dst_; - virtual void Corruption(size_t bytes, const Status& status) { + void Corruption(size_t bytes, const Status& status) override { std::string r = "corruption: "; AppendNumberTo(&r, bytes); r += " bytes; "; @@ -44,6 +46,8 @@ class CorruptionReporter : public log::Reader::Reporter { r.push_back('\n'); dst_->Append(r); } + + WritableFile* dst_; }; // Print contents of a log file. (*func)() is called on every record. @@ -70,8 +74,7 @@ Status PrintLogContents(Env* env, const std::string& fname, // Called on every item found in a WriteBatch. class WriteBatchItemPrinter : public WriteBatch::Handler { public: - WritableFile* dst_; - virtual void Put(const Slice& key, const Slice& value) { + void Put(const Slice& key, const Slice& value) override { std::string r = " put '"; AppendEscapedStringTo(&r, key); r += "' '"; @@ -79,14 +82,15 @@ class WriteBatchItemPrinter : public WriteBatch::Handler { r += "'\n"; dst_->Append(r); } - virtual void Delete(const Slice& key) { + void Delete(const Slice& key) override { std::string r = " del '"; AppendEscapedStringTo(&r, key); r += "'\n"; dst_->Append(r); } -}; + WritableFile* dst_; +}; // Called on every log record (each one of which is a WriteBatch) // found in a kLogFile. @@ -142,8 +146,8 @@ Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) { Status DumpTable(Env* env, const std::string& fname, WritableFile* dst) { uint64_t file_size; - RandomAccessFile* file = NULL; - Table* table = NULL; + RandomAccessFile* file = nullptr; + Table* table = nullptr; Status s = env->GetFileSize(fname, &file_size); if (s.ok()) { s = env->NewRandomAccessFile(fname, &file); @@ -213,9 +217,12 @@ Status DumpFile(Env* env, const std::string& fname, WritableFile* dst) { return Status::InvalidArgument(fname + ": unknown file type"); } switch (ftype) { - case kLogFile: return DumpLog(env, fname, dst); - case kDescriptorFile: return DumpDescriptor(env, fname, dst); - case kTableFile: return DumpTable(env, fname, dst); + case kLogFile: + return DumpLog(env, fname, dst); + case kDescriptorFile: + return DumpDescriptor(env, fname, dst); + case kTableFile: + return DumpTable(env, fname, dst); default: break; } diff --git a/src/leveldb/db/fault_injection_test.cc b/src/leveldb/db/fault_injection_test.cc new file mode 100644 index 000000000..bf705cb60 --- /dev/null +++ b/src/leveldb/db/fault_injection_test.cc @@ -0,0 +1,552 @@ +// Copyright 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// This test uses a custom Env to keep track of the state of a filesystem as of +// the last "sync". It then checks for data loss errors by purposely dropping +// file data (or entire files) not protected by a "sync". + +#include +#include + +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/log_format.h" +#include "db/version_set.h" +#include "leveldb/cache.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/table.h" +#include "leveldb/write_batch.h" +#include "port/port.h" +#include "port/thread_annotations.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +static const int kValueSize = 1000; +static const int kMaxNumValues = 2000; +static const size_t kNumIterations = 3; + +class FaultInjectionTestEnv; + +namespace { + +// Assume a filename, and not a directory name like "/foo/bar/" +static std::string GetDirName(const std::string& filename) { + size_t found = filename.find_last_of("/\\"); + if (found == std::string::npos) { + return ""; + } else { + return filename.substr(0, found); + } +} + +Status SyncDir(const std::string& dir) { + // As this is a test it isn't required to *actually* sync this directory. + return Status::OK(); +} + +// A basic file truncation function suitable for this test. +Status Truncate(const std::string& filename, uint64_t length) { + leveldb::Env* env = leveldb::Env::Default(); + + SequentialFile* orig_file; + Status s = env->NewSequentialFile(filename, &orig_file); + if (!s.ok()) return s; + + char* scratch = new char[length]; + leveldb::Slice result; + s = orig_file->Read(length, &result, scratch); + delete orig_file; + if (s.ok()) { + std::string tmp_name = GetDirName(filename) + "/truncate.tmp"; + WritableFile* tmp_file; + s = env->NewWritableFile(tmp_name, &tmp_file); + if (s.ok()) { + s = tmp_file->Append(result); + delete tmp_file; + if (s.ok()) { + s = env->RenameFile(tmp_name, filename); + } else { + env->DeleteFile(tmp_name); + } + } + } + + delete[] scratch; + + return s; +} + +struct FileState { + std::string filename_; + int64_t pos_; + int64_t pos_at_last_sync_; + int64_t pos_at_last_flush_; + + FileState(const std::string& filename) + : filename_(filename), + pos_(-1), + pos_at_last_sync_(-1), + pos_at_last_flush_(-1) {} + + FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {} + + bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; } + + Status DropUnsyncedData() const; +}; + +} // anonymous namespace + +// A wrapper around WritableFile which informs another Env whenever this file +// is written to or sync'ed. +class TestWritableFile : public WritableFile { + public: + TestWritableFile(const FileState& state, WritableFile* f, + FaultInjectionTestEnv* env); + ~TestWritableFile() override; + Status Append(const Slice& data) override; + Status Close() override; + Status Flush() override; + Status Sync() override; + std::string GetName() const override { return ""; } + + private: + FileState state_; + WritableFile* target_; + bool writable_file_opened_; + FaultInjectionTestEnv* env_; + + Status SyncParent(); +}; + +class FaultInjectionTestEnv : public EnvWrapper { + public: + FaultInjectionTestEnv() + : EnvWrapper(Env::Default()), filesystem_active_(true) {} + ~FaultInjectionTestEnv() override = default; + Status NewWritableFile(const std::string& fname, + WritableFile** result) override; + Status NewAppendableFile(const std::string& fname, + WritableFile** result) override; + Status DeleteFile(const std::string& f) override; + Status RenameFile(const std::string& s, const std::string& t) override; + + void WritableFileClosed(const FileState& state); + Status DropUnsyncedFileData(); + Status DeleteFilesCreatedAfterLastDirSync(); + void DirWasSynced(); + bool IsFileCreatedSinceLastDirSync(const std::string& filename); + void ResetState(); + void UntrackFile(const std::string& f); + // Setting the filesystem to inactive is the test equivalent to simulating a + // system reset. Setting to inactive will freeze our saved filesystem state so + // that it will stop being recorded. It can then be reset back to the state at + // the time of the reset. + bool IsFilesystemActive() LOCKS_EXCLUDED(mutex_) { + MutexLock l(&mutex_); + return filesystem_active_; + } + void SetFilesystemActive(bool active) LOCKS_EXCLUDED(mutex_) { + MutexLock l(&mutex_); + filesystem_active_ = active; + } + + private: + port::Mutex mutex_; + std::map db_file_state_ GUARDED_BY(mutex_); + std::set new_files_since_last_dir_sync_ GUARDED_BY(mutex_); + bool filesystem_active_ GUARDED_BY(mutex_); // Record flushes, syncs, writes +}; + +TestWritableFile::TestWritableFile(const FileState& state, WritableFile* f, + FaultInjectionTestEnv* env) + : state_(state), target_(f), writable_file_opened_(true), env_(env) { + assert(f != nullptr); +} + +TestWritableFile::~TestWritableFile() { + if (writable_file_opened_) { + Close(); + } + delete target_; +} + +Status TestWritableFile::Append(const Slice& data) { + Status s = target_->Append(data); + if (s.ok() && env_->IsFilesystemActive()) { + state_.pos_ += data.size(); + } + return s; +} + +Status TestWritableFile::Close() { + writable_file_opened_ = false; + Status s = target_->Close(); + if (s.ok()) { + env_->WritableFileClosed(state_); + } + return s; +} + +Status TestWritableFile::Flush() { + Status s = target_->Flush(); + if (s.ok() && env_->IsFilesystemActive()) { + state_.pos_at_last_flush_ = state_.pos_; + } + return s; +} + +Status TestWritableFile::SyncParent() { + Status s = SyncDir(GetDirName(state_.filename_)); + if (s.ok()) { + env_->DirWasSynced(); + } + return s; +} + +Status TestWritableFile::Sync() { + if (!env_->IsFilesystemActive()) { + return Status::OK(); + } + // Ensure new files referred to by the manifest are in the filesystem. + Status s = target_->Sync(); + if (s.ok()) { + state_.pos_at_last_sync_ = state_.pos_; + } + if (env_->IsFileCreatedSinceLastDirSync(state_.filename_)) { + Status ps = SyncParent(); + if (s.ok() && !ps.ok()) { + s = ps; + } + } + return s; +} + +Status FaultInjectionTestEnv::NewWritableFile(const std::string& fname, + WritableFile** result) { + WritableFile* actual_writable_file; + Status s = target()->NewWritableFile(fname, &actual_writable_file); + if (s.ok()) { + FileState state(fname); + state.pos_ = 0; + *result = new TestWritableFile(state, actual_writable_file, this); + // NewWritableFile doesn't append to files, so if the same file is + // opened again then it will be truncated - so forget our saved + // state. + UntrackFile(fname); + MutexLock l(&mutex_); + new_files_since_last_dir_sync_.insert(fname); + } + return s; +} + +Status FaultInjectionTestEnv::NewAppendableFile(const std::string& fname, + WritableFile** result) { + WritableFile* actual_writable_file; + Status s = target()->NewAppendableFile(fname, &actual_writable_file); + if (s.ok()) { + FileState state(fname); + state.pos_ = 0; + { + MutexLock l(&mutex_); + if (db_file_state_.count(fname) == 0) { + new_files_since_last_dir_sync_.insert(fname); + } else { + state = db_file_state_[fname]; + } + } + *result = new TestWritableFile(state, actual_writable_file, this); + } + return s; +} + +Status FaultInjectionTestEnv::DropUnsyncedFileData() { + Status s; + MutexLock l(&mutex_); + for (const auto& kvp : db_file_state_) { + if (!s.ok()) { + break; + } + const FileState& state = kvp.second; + if (!state.IsFullySynced()) { + s = state.DropUnsyncedData(); + } + } + return s; +} + +void FaultInjectionTestEnv::DirWasSynced() { + MutexLock l(&mutex_); + new_files_since_last_dir_sync_.clear(); +} + +bool FaultInjectionTestEnv::IsFileCreatedSinceLastDirSync( + const std::string& filename) { + MutexLock l(&mutex_); + return new_files_since_last_dir_sync_.find(filename) != + new_files_since_last_dir_sync_.end(); +} + +void FaultInjectionTestEnv::UntrackFile(const std::string& f) { + MutexLock l(&mutex_); + db_file_state_.erase(f); + new_files_since_last_dir_sync_.erase(f); +} + +Status FaultInjectionTestEnv::DeleteFile(const std::string& f) { + Status s = EnvWrapper::DeleteFile(f); + ASSERT_OK(s); + if (s.ok()) { + UntrackFile(f); + } + return s; +} + +Status FaultInjectionTestEnv::RenameFile(const std::string& s, + const std::string& t) { + Status ret = EnvWrapper::RenameFile(s, t); + + if (ret.ok()) { + MutexLock l(&mutex_); + if (db_file_state_.find(s) != db_file_state_.end()) { + db_file_state_[t] = db_file_state_[s]; + db_file_state_.erase(s); + } + + if (new_files_since_last_dir_sync_.erase(s) != 0) { + assert(new_files_since_last_dir_sync_.find(t) == + new_files_since_last_dir_sync_.end()); + new_files_since_last_dir_sync_.insert(t); + } + } + + return ret; +} + +void FaultInjectionTestEnv::ResetState() { + // Since we are not destroying the database, the existing files + // should keep their recorded synced/flushed state. Therefore + // we do not reset db_file_state_ and new_files_since_last_dir_sync_. + SetFilesystemActive(true); +} + +Status FaultInjectionTestEnv::DeleteFilesCreatedAfterLastDirSync() { + // Because DeleteFile access this container make a copy to avoid deadlock + mutex_.Lock(); + std::set new_files(new_files_since_last_dir_sync_.begin(), + new_files_since_last_dir_sync_.end()); + mutex_.Unlock(); + Status status; + for (const auto& new_file : new_files) { + Status delete_status = DeleteFile(new_file); + if (!delete_status.ok() && status.ok()) { + status = std::move(delete_status); + } + } + return status; +} + +void FaultInjectionTestEnv::WritableFileClosed(const FileState& state) { + MutexLock l(&mutex_); + db_file_state_[state.filename_] = state; +} + +Status FileState::DropUnsyncedData() const { + int64_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_; + return Truncate(filename_, sync_pos); +} + +class FaultInjectionTest { + public: + enum ExpectedVerifResult { VAL_EXPECT_NO_ERROR, VAL_EXPECT_ERROR }; + enum ResetMethod { RESET_DROP_UNSYNCED_DATA, RESET_DELETE_UNSYNCED_FILES }; + + FaultInjectionTestEnv* env_; + std::string dbname_; + Cache* tiny_cache_; + Options options_; + DB* db_; + + FaultInjectionTest() + : env_(new FaultInjectionTestEnv), + tiny_cache_(NewLRUCache(100)), + db_(nullptr) { + dbname_ = test::TmpDir() + "/fault_test"; + DestroyDB(dbname_, Options()); // Destroy any db from earlier run + options_.reuse_logs = true; + options_.env = env_; + options_.paranoid_checks = true; + options_.block_cache = tiny_cache_; + options_.create_if_missing = true; + } + + ~FaultInjectionTest() { + CloseDB(); + DestroyDB(dbname_, Options()); + delete tiny_cache_; + delete env_; + } + + void ReuseLogs(bool reuse) { options_.reuse_logs = reuse; } + + void Build(int start_idx, int num_vals) { + std::string key_space, value_space; + WriteBatch batch; + for (int i = start_idx; i < start_idx + num_vals; i++) { + Slice key = Key(i, &key_space); + batch.Clear(); + batch.Put(key, Value(i, &value_space)); + WriteOptions options; + ASSERT_OK(db_->Write(options, &batch)); + } + } + + Status ReadValue(int i, std::string* val) const { + std::string key_space, value_space; + Slice key = Key(i, &key_space); + Value(i, &value_space); + ReadOptions options; + return db_->Get(options, key, val); + } + + Status Verify(int start_idx, int num_vals, + ExpectedVerifResult expected) const { + std::string val; + std::string value_space; + Status s; + for (int i = start_idx; i < start_idx + num_vals && s.ok(); i++) { + Value(i, &value_space); + s = ReadValue(i, &val); + if (expected == VAL_EXPECT_NO_ERROR) { + if (s.ok()) { + ASSERT_EQ(value_space, val); + } + } else if (s.ok()) { + fprintf(stderr, "Expected an error at %d, but was OK\n", i); + s = Status::IOError(dbname_, "Expected value error:"); + } else { + s = Status::OK(); // An expected error + } + } + return s; + } + + // Return the ith key + Slice Key(int i, std::string* storage) const { + char buf[100]; + snprintf(buf, sizeof(buf), "%016d", i); + storage->assign(buf, strlen(buf)); + return Slice(*storage); + } + + // Return the value to associate with the specified key + Slice Value(int k, std::string* storage) const { + Random r(k); + return test::RandomString(&r, kValueSize, storage); + } + + Status OpenDB() { + delete db_; + db_ = nullptr; + env_->ResetState(); + return DB::Open(options_, dbname_, &db_); + } + + void CloseDB() { + delete db_; + db_ = nullptr; + } + + void DeleteAllData() { + Iterator* iter = db_->NewIterator(ReadOptions()); + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { + ASSERT_OK(db_->Delete(WriteOptions(), iter->key())); + } + + delete iter; + } + + void ResetDBState(ResetMethod reset_method) { + switch (reset_method) { + case RESET_DROP_UNSYNCED_DATA: + ASSERT_OK(env_->DropUnsyncedFileData()); + break; + case RESET_DELETE_UNSYNCED_FILES: + ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync()); + break; + default: + assert(false); + } + } + + void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) { + DeleteAllData(); + Build(0, num_pre_sync); + db_->CompactRange(nullptr, nullptr); + Build(num_pre_sync, num_post_sync); + } + + void PartialCompactTestReopenWithFault(ResetMethod reset_method, + int num_pre_sync, int num_post_sync) { + env_->SetFilesystemActive(false); + CloseDB(); + ResetDBState(reset_method); + ASSERT_OK(OpenDB()); + ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::VAL_EXPECT_NO_ERROR)); + ASSERT_OK(Verify(num_pre_sync, num_post_sync, + FaultInjectionTest::VAL_EXPECT_ERROR)); + } + + void NoWriteTestPreFault() {} + + void NoWriteTestReopenWithFault(ResetMethod reset_method) { + CloseDB(); + ResetDBState(reset_method); + ASSERT_OK(OpenDB()); + } + + void DoTest() { + Random rnd(0); + ASSERT_OK(OpenDB()); + for (size_t idx = 0; idx < kNumIterations; idx++) { + int num_pre_sync = rnd.Uniform(kMaxNumValues); + int num_post_sync = rnd.Uniform(kMaxNumValues); + + PartialCompactTestPreFault(num_pre_sync, num_post_sync); + PartialCompactTestReopenWithFault(RESET_DROP_UNSYNCED_DATA, num_pre_sync, + num_post_sync); + + NoWriteTestPreFault(); + NoWriteTestReopenWithFault(RESET_DROP_UNSYNCED_DATA); + + PartialCompactTestPreFault(num_pre_sync, num_post_sync); + // No new files created so we expect all values since no files will be + // dropped. + PartialCompactTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES, + num_pre_sync + num_post_sync, 0); + + NoWriteTestPreFault(); + NoWriteTestReopenWithFault(RESET_DELETE_UNSYNCED_FILES); + } + } +}; + +TEST(FaultInjectionTest, FaultTestNoLogReuse) { + ReuseLogs(false); + DoTest(); +} + +TEST(FaultInjectionTest, FaultTestWithLogReuse) { + ReuseLogs(true); + DoTest(); +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/filename.cc b/src/leveldb/db/filename.cc index da32946d9..85de45c50 100644 --- a/src/leveldb/db/filename.cc +++ b/src/leveldb/db/filename.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "db/filename.h" + #include #include -#include "db/filename.h" + #include "db/dbformat.h" #include "leveldb/env.h" #include "util/logging.h" @@ -12,31 +14,30 @@ namespace leveldb { // A utility routine: write "data" to the named file and Sync() it. -extern Status WriteStringToFileSync(Env* env, const Slice& data, - const std::string& fname); +Status WriteStringToFileSync(Env* env, const Slice& data, + const std::string& fname); -static std::string MakeFileName(const std::string& name, uint64_t number, +static std::string MakeFileName(const std::string& dbname, uint64_t number, const char* suffix) { char buf[100]; snprintf(buf, sizeof(buf), "/%06llu.%s", - static_cast(number), - suffix); - return name + buf; + static_cast(number), suffix); + return dbname + buf; } -std::string LogFileName(const std::string& name, uint64_t number) { +std::string LogFileName(const std::string& dbname, uint64_t number) { assert(number > 0); - return MakeFileName(name, number, "log"); + return MakeFileName(dbname, number, "log"); } -std::string TableFileName(const std::string& name, uint64_t number) { +std::string TableFileName(const std::string& dbname, uint64_t number) { assert(number > 0); - return MakeFileName(name, number, "ldb"); + return MakeFileName(dbname, number, "ldb"); } -std::string SSTTableFileName(const std::string& name, uint64_t number) { +std::string SSTTableFileName(const std::string& dbname, uint64_t number) { assert(number > 0); - return MakeFileName(name, number, "sst"); + return MakeFileName(dbname, number, "sst"); } std::string DescriptorFileName(const std::string& dbname, uint64_t number) { @@ -51,9 +52,7 @@ std::string CurrentFileName(const std::string& dbname) { return dbname + "/CURRENT"; } -std::string LockFileName(const std::string& dbname) { - return dbname + "/LOCK"; -} +std::string LockFileName(const std::string& dbname) { return dbname + "/LOCK"; } std::string TempFileName(const std::string& dbname, uint64_t number) { assert(number > 0); @@ -69,7 +68,6 @@ std::string OldInfoLogFileName(const std::string& dbname) { return dbname + "/LOG.old"; } - // Owned filenames have the form: // dbname/CURRENT // dbname/LOCK @@ -77,10 +75,9 @@ std::string OldInfoLogFileName(const std::string& dbname) { // dbname/LOG.old // dbname/MANIFEST-[0-9]+ // dbname/[0-9]+.(log|sst|ldb) -bool ParseFileName(const std::string& fname, - uint64_t* number, +bool ParseFileName(const std::string& filename, uint64_t* number, FileType* type) { - Slice rest(fname); + Slice rest(filename); if (rest == "CURRENT") { *number = 0; *type = kCurrentFile; diff --git a/src/leveldb/db/filename.h b/src/leveldb/db/filename.h index 87a752605..524e813c0 100644 --- a/src/leveldb/db/filename.h +++ b/src/leveldb/db/filename.h @@ -8,7 +8,9 @@ #define STORAGE_LEVELDB_DB_FILENAME_H_ #include + #include + #include "leveldb/slice.h" #include "leveldb/status.h" #include "port/port.h" @@ -30,55 +32,52 @@ enum FileType { // Return the name of the log file with the specified number // in the db named by "dbname". The result will be prefixed with // "dbname". -extern std::string LogFileName(const std::string& dbname, uint64_t number); +std::string LogFileName(const std::string& dbname, uint64_t number); // Return the name of the sstable with the specified number // in the db named by "dbname". The result will be prefixed with // "dbname". -extern std::string TableFileName(const std::string& dbname, uint64_t number); +std::string TableFileName(const std::string& dbname, uint64_t number); // Return the legacy file name for an sstable with the specified number // in the db named by "dbname". The result will be prefixed with // "dbname". -extern std::string SSTTableFileName(const std::string& dbname, uint64_t number); +std::string SSTTableFileName(const std::string& dbname, uint64_t number); // Return the name of the descriptor file for the db named by // "dbname" and the specified incarnation number. The result will be // prefixed with "dbname". -extern std::string DescriptorFileName(const std::string& dbname, - uint64_t number); +std::string DescriptorFileName(const std::string& dbname, uint64_t number); // Return the name of the current file. This file contains the name // of the current manifest file. The result will be prefixed with // "dbname". -extern std::string CurrentFileName(const std::string& dbname); +std::string CurrentFileName(const std::string& dbname); // Return the name of the lock file for the db named by // "dbname". The result will be prefixed with "dbname". -extern std::string LockFileName(const std::string& dbname); +std::string LockFileName(const std::string& dbname); // Return the name of a temporary file owned by the db named "dbname". // The result will be prefixed with "dbname". -extern std::string TempFileName(const std::string& dbname, uint64_t number); +std::string TempFileName(const std::string& dbname, uint64_t number); // Return the name of the info log file for "dbname". -extern std::string InfoLogFileName(const std::string& dbname); +std::string InfoLogFileName(const std::string& dbname); // Return the name of the old info log file for "dbname". -extern std::string OldInfoLogFileName(const std::string& dbname); +std::string OldInfoLogFileName(const std::string& dbname); // If filename is a leveldb file, store the type of the file in *type. // The number encoded in the filename is stored in *number. If the // filename was successfully parsed, returns true. Else return false. -extern bool ParseFileName(const std::string& filename, - uint64_t* number, - FileType* type); +bool ParseFileName(const std::string& filename, uint64_t* number, + FileType* type); // Make the CURRENT file point to the descriptor file with the // specified number. -extern Status SetCurrentFile(Env* env, const std::string& dbname, - uint64_t descriptor_number); - +Status SetCurrentFile(Env* env, const std::string& dbname, + uint64_t descriptor_number); } // namespace leveldb diff --git a/src/leveldb/db/filename_test.cc b/src/leveldb/db/filename_test.cc index a32556dea..952f32008 100644 --- a/src/leveldb/db/filename_test.cc +++ b/src/leveldb/db/filename_test.cc @@ -11,7 +11,7 @@ namespace leveldb { -class FileNameTest { }; +class FileNameTest {}; TEST(FileNameTest, Parse) { Slice db; @@ -24,17 +24,17 @@ TEST(FileNameTest, Parse) { uint64_t number; FileType type; } cases[] = { - { "100.log", 100, kLogFile }, - { "0.log", 0, kLogFile }, - { "0.sst", 0, kTableFile }, - { "0.ldb", 0, kTableFile }, - { "CURRENT", 0, kCurrentFile }, - { "LOCK", 0, kDBLockFile }, - { "MANIFEST-2", 2, kDescriptorFile }, - { "MANIFEST-7", 7, kDescriptorFile }, - { "LOG", 0, kInfoLogFile }, - { "LOG.old", 0, kInfoLogFile }, - { "18446744073709551615.log", 18446744073709551615ull, kLogFile }, + {"100.log", 100, kLogFile}, + {"0.log", 0, kLogFile}, + {"0.sst", 0, kTableFile}, + {"0.ldb", 0, kTableFile}, + {"CURRENT", 0, kCurrentFile}, + {"LOCK", 0, kDBLockFile}, + {"MANIFEST-2", 2, kDescriptorFile}, + {"MANIFEST-7", 7, kDescriptorFile}, + {"LOG", 0, kInfoLogFile}, + {"LOG.old", 0, kInfoLogFile}, + {"18446744073709551615.log", 18446744073709551615ull, kLogFile}, }; for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { std::string f = cases[i].fname; @@ -44,30 +44,28 @@ TEST(FileNameTest, Parse) { } // Errors - static const char* errors[] = { - "", - "foo", - "foo-dx-100.log", - ".log", - "", - "manifest", - "CURREN", - "CURRENTX", - "MANIFES", - "MANIFEST", - "MANIFEST-", - "XMANIFEST-3", - "MANIFEST-3x", - "LOC", - "LOCKx", - "LO", - "LOGx", - "18446744073709551616.log", - "184467440737095516150.log", - "100", - "100.", - "100.lop" - }; + static const char* errors[] = {"", + "foo", + "foo-dx-100.log", + ".log", + "", + "manifest", + "CURREN", + "CURRENTX", + "MANIFES", + "MANIFEST", + "MANIFEST-", + "XMANIFEST-3", + "MANIFEST-3x", + "LOC", + "LOCKx", + "LO", + "LOGx", + "18446744073709551616.log", + "184467440737095516150.log", + "100", + "100.", + "100.lop"}; for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) { std::string f = errors[i]; ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f; @@ -114,10 +112,20 @@ TEST(FileNameTest, Construction) { ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); ASSERT_EQ(999, number); ASSERT_EQ(kTempFile, type); + + fname = InfoLogFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kInfoLogFile, type); + + fname = OldInfoLogFileName("foo"); + ASSERT_EQ("foo/", std::string(fname.data(), 4)); + ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type)); + ASSERT_EQ(0, number); + ASSERT_EQ(kInfoLogFile, type); } } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/leveldb_main.cc b/src/leveldb/db/leveldbutil.cc similarity index 71% rename from src/leveldb/db/leveldb_main.cc rename to src/leveldb/db/leveldbutil.cc index 9f4b7dd70..9ed9667d3 100644 --- a/src/leveldb/db/leveldb_main.cc +++ b/src/leveldb/db/leveldbutil.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include + #include "leveldb/dumpfile.h" #include "leveldb/env.h" #include "leveldb/status.h" @@ -12,13 +13,14 @@ namespace { class StdoutPrinter : public WritableFile { public: - virtual Status Append(const Slice& data) { + Status Append(const Slice& data) override { fwrite(data.data(), 1, data.size(), stdout); return Status::OK(); } - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } + Status Close() override { return Status::OK(); } + Status Flush() override { return Status::OK(); } + Status Sync() override { return Status::OK(); } + std::string GetName() const override { return "[stdout]"; } }; bool HandleDumpCommand(Env* env, char** files, int num) { @@ -38,11 +40,9 @@ bool HandleDumpCommand(Env* env, char** files, int num) { } // namespace leveldb static void Usage() { - fprintf( - stderr, - "Usage: leveldbutil command...\n" - " dump files... -- dump contents of specified files\n" - ); + fprintf(stderr, + "Usage: leveldbutil command...\n" + " dump files... -- dump contents of specified files\n"); } int main(int argc, char** argv) { @@ -54,7 +54,7 @@ int main(int argc, char** argv) { } else { std::string command = argv[1]; if (command == "dump") { - ok = leveldb::HandleDumpCommand(env, argv+2, argc-2); + ok = leveldb::HandleDumpCommand(env, argv + 2, argc - 2); } else { Usage(); ok = false; diff --git a/src/leveldb/db/log_format.h b/src/leveldb/db/log_format.h index a8c06efe1..356e69fca 100644 --- a/src/leveldb/db/log_format.h +++ b/src/leveldb/db/log_format.h @@ -3,7 +3,7 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. // // Log format information shared by reader and writer. -// See ../doc/log_format.txt for more detail. +// See ../doc/log_format.md for more detail. #ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_ #define STORAGE_LEVELDB_DB_LOG_FORMAT_H_ diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc index e44b66c85..1ccfb7b34 100644 --- a/src/leveldb/db/log_reader.cc +++ b/src/leveldb/db/log_reader.cc @@ -5,6 +5,7 @@ #include "db/log_reader.h" #include + #include "leveldb/env.h" #include "util/coding.h" #include "util/crc32c.h" @@ -12,8 +13,7 @@ namespace leveldb { namespace log { -Reader::Reporter::~Reporter() { -} +Reader::Reporter::~Reporter() = default; Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, uint64_t initial_offset) @@ -25,20 +25,17 @@ Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum, eof_(false), last_record_offset_(0), end_of_buffer_offset_(0), - initial_offset_(initial_offset) { -} + initial_offset_(initial_offset), + resyncing_(initial_offset > 0) {} -Reader::~Reader() { - delete[] backing_store_; -} +Reader::~Reader() { delete[] backing_store_; } bool Reader::SkipToInitialBlock() { - size_t offset_in_block = initial_offset_ % kBlockSize; + const size_t offset_in_block = initial_offset_ % kBlockSize; uint64_t block_start_location = initial_offset_ - offset_in_block; // Don't search a block if we'd be in the trailer if (offset_in_block > kBlockSize - 6) { - offset_in_block = 0; block_start_location += kBlockSize; } @@ -72,8 +69,25 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) { Slice fragment; while (true) { - uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size(); const unsigned int record_type = ReadPhysicalRecord(&fragment); + + // ReadPhysicalRecord may have only had an empty trailer remaining in its + // internal buffer. Calculate the offset of the next physical record now + // that it has returned, properly accounting for its header size. + uint64_t physical_record_offset = + end_of_buffer_offset_ - buffer_.size() - kHeaderSize - fragment.size(); + + if (resyncing_) { + if (record_type == kMiddleType) { + continue; + } else if (record_type == kLastType) { + resyncing_ = false; + continue; + } else { + resyncing_ = false; + } + } + switch (record_type) { case kFullType: if (in_fragmented_record) { @@ -81,9 +95,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) { // it could emit an empty kFirstType record at the tail end // of a block followed by a kFullType or kFirstType record // at the beginning of the next block. - if (scratch->empty()) { - in_fragmented_record = false; - } else { + if (!scratch->empty()) { ReportCorruption(scratch->size(), "partial record without end(1)"); } } @@ -99,9 +111,7 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) { // it could emit an empty kFirstType record at the tail end // of a block followed by a kFullType or kFirstType record // at the beginning of the next block. - if (scratch->empty()) { - in_fragmented_record = false; - } else { + if (!scratch->empty()) { ReportCorruption(scratch->size(), "partial record without end(2)"); } } @@ -163,16 +173,14 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) { return false; } -uint64_t Reader::LastRecordOffset() { - return last_record_offset_; -} +uint64_t Reader::LastRecordOffset() { return last_record_offset_; } void Reader::ReportCorruption(uint64_t bytes, const char* reason) { - ReportDrop(bytes, Status::Corruption(reason)); + ReportDrop(bytes, Status::Corruption(reason, file_->GetName())); } void Reader::ReportDrop(uint64_t bytes, const Status& reason) { - if (reporter_ != NULL && + if (reporter_ != nullptr && end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) { reporter_->Corruption(static_cast(bytes), reason); } diff --git a/src/leveldb/db/log_reader.h b/src/leveldb/db/log_reader.h index 6aff79171..001da8948 100644 --- a/src/leveldb/db/log_reader.h +++ b/src/leveldb/db/log_reader.h @@ -32,7 +32,7 @@ class Reader { // Create a reader that will return log records from "*file". // "*file" must remain live while this Reader is in use. // - // If "reporter" is non-NULL, it is notified whenever some data is + // If "reporter" is non-null, it is notified whenever some data is // dropped due to a detected corruption. "*reporter" must remain // live while this Reader is in use. // @@ -43,6 +43,9 @@ class Reader { Reader(SequentialFile* file, Reporter* reporter, bool checksum, uint64_t initial_offset); + Reader(const Reader&) = delete; + Reader& operator=(const Reader&) = delete; + ~Reader(); // Read the next record into *record. Returns true if read @@ -58,21 +61,6 @@ class Reader { uint64_t LastRecordOffset(); private: - SequentialFile* const file_; - Reporter* const reporter_; - bool const checksum_; - char* const backing_store_; - Slice buffer_; - bool eof_; // Last Read() indicated EOF by returning < kBlockSize - - // Offset of the last record returned by ReadRecord. - uint64_t last_record_offset_; - // Offset of the first location past the end of buffer_. - uint64_t end_of_buffer_offset_; - - // Offset at which to start looking for the first record to return - uint64_t const initial_offset_; - // Extend record types with the following special values enum { kEof = kMaxRecordType + 1, @@ -97,9 +85,25 @@ class Reader { void ReportCorruption(uint64_t bytes, const char* reason); void ReportDrop(uint64_t bytes, const Status& reason); - // No copying allowed - Reader(const Reader&); - void operator=(const Reader&); + SequentialFile* const file_; + Reporter* const reporter_; + bool const checksum_; + char* const backing_store_; + Slice buffer_; + bool eof_; // Last Read() indicated EOF by returning < kBlockSize + + // Offset of the last record returned by ReadRecord. + uint64_t last_record_offset_; + // Offset of the first location past the end of buffer_. + uint64_t end_of_buffer_offset_; + + // Offset at which to start looking for the first record to return + uint64_t const initial_offset_; + + // True if we are resynchronizing after a seek (initial_offset_ > 0). In + // particular, a run of kMiddleType and kLastType records can be silently + // skipped in this mode + bool resyncing_; }; } // namespace log diff --git a/src/leveldb/db/log_test.cc b/src/leveldb/db/log_test.cc index dcf056265..41fc04306 100644 --- a/src/leveldb/db/log_test.cc +++ b/src/leveldb/db/log_test.cc @@ -37,95 +37,29 @@ static std::string RandomSkewedString(int i, Random* rnd) { } class LogTest { - private: - class StringDest : public WritableFile { - public: - std::string contents_; - - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } - virtual Status Append(const Slice& slice) { - contents_.append(slice.data(), slice.size()); - return Status::OK(); - } - }; - - class StringSource : public SequentialFile { - public: - Slice contents_; - bool force_error_; - bool returned_partial_; - StringSource() : force_error_(false), returned_partial_(false) { } - - virtual Status Read(size_t n, Slice* result, char* scratch) { - ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; - - if (force_error_) { - force_error_ = false; - returned_partial_ = true; - return Status::Corruption("read error"); - } - - if (contents_.size() < n) { - n = contents_.size(); - returned_partial_ = true; - } - *result = Slice(contents_.data(), n); - contents_.remove_prefix(n); - return Status::OK(); - } - - virtual Status Skip(uint64_t n) { - if (n > contents_.size()) { - contents_.clear(); - return Status::NotFound("in-memory file skipepd past end"); - } - - contents_.remove_prefix(n); - - return Status::OK(); - } - }; - - class ReportCollector : public Reader::Reporter { - public: - size_t dropped_bytes_; - std::string message_; - - ReportCollector() : dropped_bytes_(0) { } - virtual void Corruption(size_t bytes, const Status& status) { - dropped_bytes_ += bytes; - message_.append(status.ToString()); - } - }; - - StringDest dest_; - StringSource source_; - ReportCollector report_; - bool reading_; - Writer writer_; - Reader reader_; - - // Record metadata for testing initial offset functionality - static size_t initial_offset_record_sizes_[]; - static uint64_t initial_offset_last_record_offsets_[]; - public: - LogTest() : reading_(false), - writer_(&dest_), - reader_(&source_, &report_, true/*checksum*/, - 0/*initial_offset*/) { + LogTest() + : reading_(false), + writer_(new Writer(&dest_)), + reader_(new Reader(&source_, &report_, true /*checksum*/, + 0 /*initial_offset*/)) {} + + ~LogTest() { + delete writer_; + delete reader_; + } + + void ReopenForAppend() { + delete writer_; + writer_ = new Writer(&dest_, dest_.contents_.size()); } void Write(const std::string& msg) { ASSERT_TRUE(!reading_) << "Write() after starting to read"; - writer_.AddRecord(Slice(msg)); + writer_->AddRecord(Slice(msg)); } - size_t WrittenBytes() const { - return dest_.contents_.size(); - } + size_t WrittenBytes() const { return dest_.contents_.size(); } std::string Read() { if (!reading_) { @@ -134,7 +68,7 @@ class LogTest { } std::string scratch; Slice record; - if (reader_.ReadRecord(&record, &scratch)) { + if (reader_->ReadRecord(&record, &scratch)) { return record.ToString(); } else { return "EOF"; @@ -155,22 +89,16 @@ class LogTest { void FixChecksum(int header_offset, int len) { // Compute crc of type/len/data - uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len); + uint32_t crc = crc32c::Value(&dest_.contents_[header_offset + 6], 1 + len); crc = crc32c::Mask(crc); EncodeFixed32(&dest_.contents_[header_offset], crc); } - void ForceError() { - source_.force_error_ = true; - } + void ForceError() { source_.force_error_ = true; } - size_t DroppedBytes() const { - return report_.dropped_bytes_; - } + size_t DroppedBytes() const { return report_.dropped_bytes_; } - std::string ReportMessage() const { - return report_.message_; - } + std::string ReportMessage() const { return report_.message_; } // Returns OK iff recorded error message contains "msg" std::string MatchError(const std::string& msg) const { @@ -182,18 +110,23 @@ class LogTest { } void WriteInitialOffsetLog() { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < num_initial_offset_records_; i++) { std::string record(initial_offset_record_sizes_[i], static_cast('a' + i)); Write(record); } } + void StartReadingAt(uint64_t initial_offset) { + delete reader_; + reader_ = new Reader(&source_, &report_, true /*checksum*/, initial_offset); + } + void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) { WriteInitialOffsetLog(); reading_ = true; source_.contents_ = Slice(dest_.contents_); - Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, + Reader* offset_reader = new Reader(&source_, &report_, true /*checksum*/, WrittenBytes() + offset_past_end); Slice record; std::string scratch; @@ -206,38 +139,128 @@ class LogTest { WriteInitialOffsetLog(); reading_ = true; source_.contents_ = Slice(dest_.contents_); - Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/, - initial_offset); - Slice record; - std::string scratch; - ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); - ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], - record.size()); - ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], - offset_reader->LastRecordOffset()); - ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + Reader* offset_reader = + new Reader(&source_, &report_, true /*checksum*/, initial_offset); + + // Read all records from expected_record_offset through the last one. + ASSERT_LT(expected_record_offset, num_initial_offset_records_); + for (; expected_record_offset < num_initial_offset_records_; + ++expected_record_offset) { + Slice record; + std::string scratch; + ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch)); + ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset], + record.size()); + ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset], + offset_reader->LastRecordOffset()); + ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]); + } delete offset_reader; } + private: + class StringDest : public WritableFile { + public: + Status Close() override { return Status::OK(); } + Status Flush() override { return Status::OK(); } + Status Sync() override { return Status::OK(); } + Status Append(const Slice& slice) override { + contents_.append(slice.data(), slice.size()); + return Status::OK(); + } + std::string GetName() const override { return ""; } + + std::string contents_; + }; + + class StringSource : public SequentialFile { + public: + StringSource() : force_error_(false), returned_partial_(false) {} + + Status Read(size_t n, Slice* result, char* scratch) override { + ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error"; + + if (force_error_) { + force_error_ = false; + returned_partial_ = true; + return Status::Corruption("read error"); + } + + if (contents_.size() < n) { + n = contents_.size(); + returned_partial_ = true; + } + *result = Slice(contents_.data(), n); + contents_.remove_prefix(n); + return Status::OK(); + } + + Status Skip(uint64_t n) override { + if (n > contents_.size()) { + contents_.clear(); + return Status::NotFound("in-memory file skipped past end"); + } + + contents_.remove_prefix(n); + + return Status::OK(); + } + std::string GetName() const { return ""; } + + Slice contents_; + bool force_error_; + bool returned_partial_; + }; + + class ReportCollector : public Reader::Reporter { + public: + ReportCollector() : dropped_bytes_(0) {} + void Corruption(size_t bytes, const Status& status) override { + dropped_bytes_ += bytes; + message_.append(status.ToString()); + } + + size_t dropped_bytes_; + std::string message_; + }; + + // Record metadata for testing initial offset functionality + static size_t initial_offset_record_sizes_[]; + static uint64_t initial_offset_last_record_offsets_[]; + static int num_initial_offset_records_; + + StringDest dest_; + StringSource source_; + ReportCollector report_; + bool reading_; + Writer* writer_; + Reader* reader_; }; -size_t LogTest::initial_offset_record_sizes_[] = - {10000, // Two sizable records in first block - 10000, - 2 * log::kBlockSize - 1000, // Span three blocks - 1}; +size_t LogTest::initial_offset_record_sizes_[] = { + 10000, // Two sizable records in first block + 10000, + 2 * log::kBlockSize - 1000, // Span three blocks + 1, + 13716, // Consume all but two bytes of block 3. + log::kBlockSize - kHeaderSize, // Consume the entirety of block 4. +}; -uint64_t LogTest::initial_offset_last_record_offsets_[] = - {0, - kHeaderSize + 10000, - 2 * (kHeaderSize + 10000), - 2 * (kHeaderSize + 10000) + - (2 * log::kBlockSize - 1000) + 3 * kHeaderSize}; +uint64_t LogTest::initial_offset_last_record_offsets_[] = { + 0, + kHeaderSize + 10000, + 2 * (kHeaderSize + 10000), + 2 * (kHeaderSize + 10000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize, + 2 * (kHeaderSize + 10000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize + + kHeaderSize + 1, + 3 * log::kBlockSize, +}; +// LogTest::initial_offset_last_record_offsets_ must be defined before this. +int LogTest::num_initial_offset_records_ = + sizeof(LogTest::initial_offset_last_record_offsets_) / sizeof(uint64_t); -TEST(LogTest, Empty) { - ASSERT_EQ("EOF", Read()); -} +TEST(LogTest, Empty) { ASSERT_EQ("EOF", Read()); } TEST(LogTest, ReadWrite) { Write("foo"); @@ -274,7 +297,7 @@ TEST(LogTest, Fragmentation) { TEST(LogTest, MarginalTrailer) { // Make a trailer that is exactly the same length as an empty record. - const int n = kBlockSize - 2*kHeaderSize; + const int n = kBlockSize - 2 * kHeaderSize; Write(BigString("foo", n)); ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); Write(""); @@ -287,7 +310,7 @@ TEST(LogTest, MarginalTrailer) { TEST(LogTest, MarginalTrailer2) { // Make a trailer that is exactly the same length as an empty record. - const int n = kBlockSize - 2*kHeaderSize; + const int n = kBlockSize - 2 * kHeaderSize; Write(BigString("foo", n)); ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes()); Write("bar"); @@ -299,7 +322,7 @@ TEST(LogTest, MarginalTrailer2) { } TEST(LogTest, ShortTrailer) { - const int n = kBlockSize - 2*kHeaderSize + 4; + const int n = kBlockSize - 2 * kHeaderSize + 4; Write(BigString("foo", n)); ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); Write(""); @@ -311,13 +334,22 @@ TEST(LogTest, ShortTrailer) { } TEST(LogTest, AlignedEof) { - const int n = kBlockSize - 2*kHeaderSize + 4; + const int n = kBlockSize - 2 * kHeaderSize + 4; Write(BigString("foo", n)); ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes()); ASSERT_EQ(BigString("foo", n), Read()); ASSERT_EQ("EOF", Read()); } +TEST(LogTest, OpenForAppend) { + Write("hello"); + ReopenForAppend(); + Write("world"); + ASSERT_EQ("hello", Read()); + ASSERT_EQ("world", Read()); + ASSERT_EQ("EOF", Read()); +} + TEST(LogTest, RandomRead) { const int N = 500; Random write_rnd(301); @@ -353,7 +385,7 @@ TEST(LogTest, BadRecordType) { TEST(LogTest, TruncatedTrailingRecordIsIgnored) { Write("foo"); - ShrinkSize(4); // Drop all payload as well as a header byte + ShrinkSize(4); // Drop all payload as well as a header byte ASSERT_EQ("EOF", Read()); // Truncated last record is ignored, not treated as an error. ASSERT_EQ(0, DroppedBytes()); @@ -445,6 +477,22 @@ TEST(LogTest, PartialLastIsIgnored) { ASSERT_EQ(0, DroppedBytes()); } +TEST(LogTest, SkipIntoMultiRecord) { + // Consider a fragmented record: + // first(R1), middle(R1), last(R1), first(R2) + // If initial_offset points to a record after first(R1) but before first(R2) + // incomplete fragment errors are not actual errors, and must be suppressed + // until a new first or full record is encountered. + Write(BigString("foo", 3 * kBlockSize)); + Write("correct"); + StartReadingAt(kBlockSize); + + ASSERT_EQ("correct", Read()); + ASSERT_EQ("", ReportMessage()); + ASSERT_EQ(0, DroppedBytes()); + ASSERT_EQ("EOF", Read()); +} + TEST(LogTest, ErrorJoinsRecords) { // Consider two fragmented records: // first(R1) last(R1) first(R2) last(R2) @@ -457,44 +505,30 @@ TEST(LogTest, ErrorJoinsRecords) { Write("correct"); // Wipe the middle block - for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) { + for (int offset = kBlockSize; offset < 2 * kBlockSize; offset++) { SetByte(offset, 'x'); } ASSERT_EQ("correct", Read()); ASSERT_EQ("EOF", Read()); const size_t dropped = DroppedBytes(); - ASSERT_LE(dropped, 2*kBlockSize + 100); - ASSERT_GE(dropped, 2*kBlockSize); + ASSERT_LE(dropped, 2 * kBlockSize + 100); + ASSERT_GE(dropped, 2 * kBlockSize); } -TEST(LogTest, ReadStart) { - CheckInitialOffsetRecord(0, 0); -} +TEST(LogTest, ReadStart) { CheckInitialOffsetRecord(0, 0); } -TEST(LogTest, ReadSecondOneOff) { - CheckInitialOffsetRecord(1, 1); -} +TEST(LogTest, ReadSecondOneOff) { CheckInitialOffsetRecord(1, 1); } -TEST(LogTest, ReadSecondTenThousand) { - CheckInitialOffsetRecord(10000, 1); -} +TEST(LogTest, ReadSecondTenThousand) { CheckInitialOffsetRecord(10000, 1); } -TEST(LogTest, ReadSecondStart) { - CheckInitialOffsetRecord(10007, 1); -} +TEST(LogTest, ReadSecondStart) { CheckInitialOffsetRecord(10007, 1); } -TEST(LogTest, ReadThirdOneOff) { - CheckInitialOffsetRecord(10008, 2); -} +TEST(LogTest, ReadThirdOneOff) { CheckInitialOffsetRecord(10008, 2); } -TEST(LogTest, ReadThirdStart) { - CheckInitialOffsetRecord(20014, 2); -} +TEST(LogTest, ReadThirdStart) { CheckInitialOffsetRecord(20014, 2); } -TEST(LogTest, ReadFourthOneOff) { - CheckInitialOffsetRecord(20015, 3); -} +TEST(LogTest, ReadFourthOneOff) { CheckInitialOffsetRecord(20015, 3); } TEST(LogTest, ReadFourthFirstBlockTrailer) { CheckInitialOffsetRecord(log::kBlockSize - 4, 3); @@ -514,17 +548,15 @@ TEST(LogTest, ReadFourthStart) { 3); } -TEST(LogTest, ReadEnd) { - CheckOffsetPastEndReturnsNoRecords(0); +TEST(LogTest, ReadInitialOffsetIntoBlockPadding) { + CheckInitialOffsetRecord(3 * log::kBlockSize - 3, 5); } -TEST(LogTest, ReadPastEnd) { - CheckOffsetPastEndReturnsNoRecords(5); -} +TEST(LogTest, ReadEnd) { CheckOffsetPastEndReturnsNoRecords(0); } + +TEST(LogTest, ReadPastEnd) { CheckOffsetPastEndReturnsNoRecords(5); } } // namespace log } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/log_writer.cc b/src/leveldb/db/log_writer.cc index 2da99ac08..bfb16fb48 100644 --- a/src/leveldb/db/log_writer.cc +++ b/src/leveldb/db/log_writer.cc @@ -5,6 +5,7 @@ #include "db/log_writer.h" #include + #include "leveldb/env.h" #include "util/coding.h" #include "util/crc32c.h" @@ -12,18 +13,24 @@ namespace leveldb { namespace log { -Writer::Writer(WritableFile* dest) - : dest_(dest), - block_offset_(0) { +static void InitTypeCrc(uint32_t* type_crc) { for (int i = 0; i <= kMaxRecordType; i++) { char t = static_cast(i); - type_crc_[i] = crc32c::Value(&t, 1); + type_crc[i] = crc32c::Value(&t, 1); } } -Writer::~Writer() { +Writer::Writer(WritableFile* dest) : dest_(dest), block_offset_(0) { + InitTypeCrc(type_crc_); +} + +Writer::Writer(WritableFile* dest, uint64_t dest_length) + : dest_(dest), block_offset_(dest_length % kBlockSize) { + InitTypeCrc(type_crc_); } +Writer::~Writer() = default; + Status Writer::AddRecord(const Slice& slice) { const char* ptr = slice.data(); size_t left = slice.size(); @@ -40,7 +47,7 @@ Status Writer::AddRecord(const Slice& slice) { // Switch to a new block if (leftover > 0) { // Fill the trailer (literal below relies on kHeaderSize being 7) - assert(kHeaderSize == 7); + static_assert(kHeaderSize == 7, ""); dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); } block_offset_ = 0; @@ -72,30 +79,31 @@ Status Writer::AddRecord(const Slice& slice) { return s; } -Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) { - assert(n <= 0xffff); // Must fit in two bytes - assert(block_offset_ + kHeaderSize + n <= kBlockSize); +Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, + size_t length) { + assert(length <= 0xffff); // Must fit in two bytes + assert(block_offset_ + kHeaderSize + length <= kBlockSize); // Format the header char buf[kHeaderSize]; - buf[4] = static_cast(n & 0xff); - buf[5] = static_cast(n >> 8); + buf[4] = static_cast(length & 0xff); + buf[5] = static_cast(length >> 8); buf[6] = static_cast(t); // Compute the crc of the record type and the payload. - uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n); - crc = crc32c::Mask(crc); // Adjust for storage + uint32_t crc = crc32c::Extend(type_crc_[t], ptr, length); + crc = crc32c::Mask(crc); // Adjust for storage EncodeFixed32(buf, crc); // Write the header and the payload Status s = dest_->Append(Slice(buf, kHeaderSize)); if (s.ok()) { - s = dest_->Append(Slice(ptr, n)); + s = dest_->Append(Slice(ptr, length)); if (s.ok()) { s = dest_->Flush(); } } - block_offset_ += kHeaderSize + n; + block_offset_ += kHeaderSize + length; return s; } diff --git a/src/leveldb/db/log_writer.h b/src/leveldb/db/log_writer.h index a3a954d96..c0a21147e 100644 --- a/src/leveldb/db/log_writer.h +++ b/src/leveldb/db/log_writer.h @@ -6,6 +6,7 @@ #define STORAGE_LEVELDB_DB_LOG_WRITER_H_ #include + #include "db/log_format.h" #include "leveldb/slice.h" #include "leveldb/status.h" @@ -22,24 +23,29 @@ class Writer { // "*dest" must be initially empty. // "*dest" must remain live while this Writer is in use. explicit Writer(WritableFile* dest); + + // Create a writer that will append data to "*dest". + // "*dest" must have initial length "dest_length". + // "*dest" must remain live while this Writer is in use. + Writer(WritableFile* dest, uint64_t dest_length); + + Writer(const Writer&) = delete; + Writer& operator=(const Writer&) = delete; + ~Writer(); Status AddRecord(const Slice& slice); private: + Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); + WritableFile* dest_; - int block_offset_; // Current offset in block + int block_offset_; // Current offset in block // crc32c values for all supported record types. These are // pre-computed to reduce the overhead of computing the crc of the // record type stored in the header. uint32_t type_crc_[kMaxRecordType + 1]; - - Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length); - - // No copying allowed - Writer(const Writer&); - void operator=(const Writer&); }; } // namespace log diff --git a/src/leveldb/db/memtable.cc b/src/leveldb/db/memtable.cc index bfec0a7e7..00931d467 100644 --- a/src/leveldb/db/memtable.cc +++ b/src/leveldb/db/memtable.cc @@ -18,20 +18,15 @@ static Slice GetLengthPrefixedSlice(const char* data) { return Slice(p, len); } -MemTable::MemTable(const InternalKeyComparator& cmp) - : comparator_(cmp), - refs_(0), - table_(comparator_, &arena_) { -} +MemTable::MemTable(const InternalKeyComparator& comparator) + : comparator_(comparator), refs_(0), table_(comparator_, &arena_) {} -MemTable::~MemTable() { - assert(refs_ == 0); -} +MemTable::~MemTable() { assert(refs_ == 0); } size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); } -int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr) - const { +int MemTable::KeyComparator::operator()(const char* aptr, + const char* bptr) const { // Internal keys are encoded as length-prefixed strings. Slice a = GetLengthPrefixedSlice(aptr); Slice b = GetLengthPrefixedSlice(bptr); @@ -48,39 +43,37 @@ static const char* EncodeKey(std::string* scratch, const Slice& target) { return scratch->data(); } -class MemTableIterator: public Iterator { +class MemTableIterator : public Iterator { public: - explicit MemTableIterator(MemTable::Table* table) : iter_(table) { } - - virtual bool Valid() const { return iter_.Valid(); } - virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); } - virtual void SeekToFirst() { iter_.SeekToFirst(); } - virtual void SeekToLast() { iter_.SeekToLast(); } - virtual void Next() { iter_.Next(); } - virtual void Prev() { iter_.Prev(); } - virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); } - virtual Slice value() const { + explicit MemTableIterator(MemTable::Table* table) : iter_(table) {} + + MemTableIterator(const MemTableIterator&) = delete; + MemTableIterator& operator=(const MemTableIterator&) = delete; + + ~MemTableIterator() override = default; + + bool Valid() const override { return iter_.Valid(); } + void Seek(const Slice& k) override { iter_.Seek(EncodeKey(&tmp_, k)); } + void SeekToFirst() override { iter_.SeekToFirst(); } + void SeekToLast() override { iter_.SeekToLast(); } + void Next() override { iter_.Next(); } + void Prev() override { iter_.Prev(); } + Slice key() const override { return GetLengthPrefixedSlice(iter_.key()); } + Slice value() const override { Slice key_slice = GetLengthPrefixedSlice(iter_.key()); return GetLengthPrefixedSlice(key_slice.data() + key_slice.size()); } - virtual Status status() const { return Status::OK(); } + Status status() const override { return Status::OK(); } private: MemTable::Table::Iterator iter_; - std::string tmp_; // For passing to EncodeKey - - // No copying allowed - MemTableIterator(const MemTableIterator&); - void operator=(const MemTableIterator&); + std::string tmp_; // For passing to EncodeKey }; -Iterator* MemTable::NewIterator() { - return new MemTableIterator(&table_); -} +Iterator* MemTable::NewIterator() { return new MemTableIterator(&table_); } -void MemTable::Add(SequenceNumber s, ValueType type, - const Slice& key, +void MemTable::Add(SequenceNumber s, ValueType type, const Slice& key, const Slice& value) { // Format of an entry is concatenation of: // key_size : varint32 of internal_key.size() @@ -90,9 +83,9 @@ void MemTable::Add(SequenceNumber s, ValueType type, size_t key_size = key.size(); size_t val_size = value.size(); size_t internal_key_size = key_size + 8; - const size_t encoded_len = - VarintLength(internal_key_size) + internal_key_size + - VarintLength(val_size) + val_size; + const size_t encoded_len = VarintLength(internal_key_size) + + internal_key_size + VarintLength(val_size) + + val_size; char* buf = arena_.Allocate(encoded_len); char* p = EncodeVarint32(buf, internal_key_size); memcpy(p, key.data(), key_size); @@ -101,7 +94,7 @@ void MemTable::Add(SequenceNumber s, ValueType type, p += 8; p = EncodeVarint32(p, val_size); memcpy(p, value.data(), val_size); - assert((p + val_size) - buf == encoded_len); + assert(p + val_size == buf + encoded_len); table_.Insert(buf); } @@ -121,10 +114,9 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) { // all entries with overly large sequence numbers. const char* entry = iter.key(); uint32_t key_length; - const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length); + const char* key_ptr = GetVarint32Ptr(entry, entry + 5, &key_length); if (comparator_.comparator.user_comparator()->Compare( - Slice(key_ptr, key_length - 8), - key.user_key()) == 0) { + Slice(key_ptr, key_length - 8), key.user_key()) == 0) { // Correct user key const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8); switch (static_cast(tag & 0xff)) { diff --git a/src/leveldb/db/memtable.h b/src/leveldb/db/memtable.h index 92e90bb09..9d986b107 100644 --- a/src/leveldb/db/memtable.h +++ b/src/leveldb/db/memtable.h @@ -6,15 +6,15 @@ #define STORAGE_LEVELDB_DB_MEMTABLE_H_ #include -#include "leveldb/db.h" + #include "db/dbformat.h" #include "db/skiplist.h" +#include "leveldb/db.h" #include "util/arena.h" namespace leveldb { class InternalKeyComparator; -class Mutex; class MemTableIterator; class MemTable { @@ -23,6 +23,9 @@ class MemTable { // is zero and the caller must call Ref() at least once. explicit MemTable(const InternalKeyComparator& comparator); + MemTable(const MemTable&) = delete; + MemTable& operator=(const MemTable&) = delete; + // Increase reference count. void Ref() { ++refs_; } @@ -36,10 +39,7 @@ class MemTable { } // Returns an estimate of the number of bytes of data in use by this - // data structure. - // - // REQUIRES: external synchronization to prevent simultaneous - // operations on the same MemTable. + // data structure. It is safe to call when MemTable is being modified. size_t ApproximateMemoryUsage(); // Return an iterator that yields the contents of the memtable. @@ -53,8 +53,7 @@ class MemTable { // Add an entry into memtable that maps key to value at the // specified sequence number and with the specified type. // Typically value will be empty if type==kTypeDeletion. - void Add(SequenceNumber seq, ValueType type, - const Slice& key, + void Add(SequenceNumber seq, ValueType type, const Slice& key, const Slice& value); // If memtable contains a value for key, store it in *value and return true. @@ -64,26 +63,23 @@ class MemTable { bool Get(const LookupKey& key, std::string* value, Status* s); private: - ~MemTable(); // Private since only Unref() should be used to delete it + friend class MemTableIterator; + friend class MemTableBackwardIterator; struct KeyComparator { const InternalKeyComparator comparator; - explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { } + explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) {} int operator()(const char* a, const char* b) const; }; - friend class MemTableIterator; - friend class MemTableBackwardIterator; typedef SkipList Table; + ~MemTable(); // Private since only Unref() should be used to delete it + KeyComparator comparator_; int refs_; Arena arena_; Table table_; - - // No copying allowed - MemTable(const MemTable&); - void operator=(const MemTable&); }; } // namespace leveldb diff --git a/src/leveldb/db/recovery_test.cc b/src/leveldb/db/recovery_test.cc new file mode 100644 index 000000000..547a9591e --- /dev/null +++ b/src/leveldb/db/recovery_test.cc @@ -0,0 +1,330 @@ +// Copyright (c) 2014 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "db/db_impl.h" +#include "db/filename.h" +#include "db/version_set.h" +#include "db/write_batch_internal.h" +#include "leveldb/db.h" +#include "leveldb/env.h" +#include "leveldb/write_batch.h" +#include "util/logging.h" +#include "util/testharness.h" +#include "util/testutil.h" + +namespace leveldb { + +class RecoveryTest { + public: + RecoveryTest() : env_(Env::Default()), db_(nullptr) { + dbname_ = test::TmpDir() + "/recovery_test"; + DestroyDB(dbname_, Options()); + Open(); + } + + ~RecoveryTest() { + Close(); + DestroyDB(dbname_, Options()); + } + + DBImpl* dbfull() const { return reinterpret_cast(db_); } + Env* env() const { return env_; } + + bool CanAppend() { + WritableFile* tmp; + Status s = env_->NewAppendableFile(CurrentFileName(dbname_), &tmp); + delete tmp; + if (s.IsNotSupportedError()) { + return false; + } else { + return true; + } + } + + void Close() { + delete db_; + db_ = nullptr; + } + + Status OpenWithStatus(Options* options = nullptr) { + Close(); + Options opts; + if (options != nullptr) { + opts = *options; + } else { + opts.reuse_logs = true; // TODO(sanjay): test both ways + opts.create_if_missing = true; + } + if (opts.env == nullptr) { + opts.env = env_; + } + return DB::Open(opts, dbname_, &db_); + } + + void Open(Options* options = nullptr) { + ASSERT_OK(OpenWithStatus(options)); + ASSERT_EQ(1, NumLogs()); + } + + Status Put(const std::string& k, const std::string& v) { + return db_->Put(WriteOptions(), k, v); + } + + std::string Get(const std::string& k, const Snapshot* snapshot = nullptr) { + std::string result; + Status s = db_->Get(ReadOptions(), k, &result); + if (s.IsNotFound()) { + result = "NOT_FOUND"; + } else if (!s.ok()) { + result = s.ToString(); + } + return result; + } + + std::string ManifestFileName() { + std::string current; + ASSERT_OK(ReadFileToString(env_, CurrentFileName(dbname_), ¤t)); + size_t len = current.size(); + if (len > 0 && current[len - 1] == '\n') { + current.resize(len - 1); + } + return dbname_ + "/" + current; + } + + std::string LogName(uint64_t number) { return LogFileName(dbname_, number); } + + size_t DeleteLogFiles() { + // Linux allows unlinking open files, but Windows does not. + // Closing the db allows for file deletion. + Close(); + std::vector logs = GetFiles(kLogFile); + for (size_t i = 0; i < logs.size(); i++) { + ASSERT_OK(env_->DeleteFile(LogName(logs[i]))) << LogName(logs[i]); + } + return logs.size(); + } + + void DeleteManifestFile() { ASSERT_OK(env_->DeleteFile(ManifestFileName())); } + + uint64_t FirstLogFile() { return GetFiles(kLogFile)[0]; } + + std::vector GetFiles(FileType t) { + std::vector filenames; + ASSERT_OK(env_->GetChildren(dbname_, &filenames)); + std::vector result; + for (size_t i = 0; i < filenames.size(); i++) { + uint64_t number; + FileType type; + if (ParseFileName(filenames[i], &number, &type) && type == t) { + result.push_back(number); + } + } + return result; + } + + int NumLogs() { return GetFiles(kLogFile).size(); } + + int NumTables() { return GetFiles(kTableFile).size(); } + + uint64_t FileSize(const std::string& fname) { + uint64_t result; + ASSERT_OK(env_->GetFileSize(fname, &result)) << fname; + return result; + } + + void CompactMemTable() { dbfull()->TEST_CompactMemTable(); } + + // Directly construct a log file that sets key to val. + void MakeLogFile(uint64_t lognum, SequenceNumber seq, Slice key, Slice val) { + std::string fname = LogFileName(dbname_, lognum); + WritableFile* file; + ASSERT_OK(env_->NewWritableFile(fname, &file)); + log::Writer writer(file); + WriteBatch batch; + batch.Put(key, val); + WriteBatchInternal::SetSequence(&batch, seq); + ASSERT_OK(writer.AddRecord(WriteBatchInternal::Contents(&batch))); + ASSERT_OK(file->Flush()); + delete file; + } + + private: + std::string dbname_; + Env* env_; + DB* db_; +}; + +TEST(RecoveryTest, ManifestReused) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + ASSERT_OK(Put("foo", "bar")); + Close(); + std::string old_manifest = ManifestFileName(); + Open(); + ASSERT_EQ(old_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); + Open(); + ASSERT_EQ(old_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(RecoveryTest, LargeManifestCompacted) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + ASSERT_OK(Put("foo", "bar")); + Close(); + std::string old_manifest = ManifestFileName(); + + // Pad with zeroes to make manifest file very big. + { + uint64_t len = FileSize(old_manifest); + WritableFile* file; + ASSERT_OK(env()->NewAppendableFile(old_manifest, &file)); + std::string zeroes(3 * 1048576 - static_cast(len), 0); + ASSERT_OK(file->Append(zeroes)); + ASSERT_OK(file->Flush()); + delete file; + } + + Open(); + std::string new_manifest = ManifestFileName(); + ASSERT_NE(old_manifest, new_manifest); + ASSERT_GT(10000, FileSize(new_manifest)); + ASSERT_EQ("bar", Get("foo")); + + Open(); + ASSERT_EQ(new_manifest, ManifestFileName()); + ASSERT_EQ("bar", Get("foo")); +} + +TEST(RecoveryTest, NoLogFiles) { + ASSERT_OK(Put("foo", "bar")); + ASSERT_EQ(1, DeleteLogFiles()); + Open(); + ASSERT_EQ("NOT_FOUND", Get("foo")); + Open(); + ASSERT_EQ("NOT_FOUND", Get("foo")); +} + +TEST(RecoveryTest, LogFileReuse) { + if (!CanAppend()) { + fprintf(stderr, "skipping test because env does not support appending\n"); + return; + } + for (int i = 0; i < 2; i++) { + ASSERT_OK(Put("foo", "bar")); + if (i == 0) { + // Compact to ensure current log is empty + CompactMemTable(); + } + Close(); + ASSERT_EQ(1, NumLogs()); + uint64_t number = FirstLogFile(); + if (i == 0) { + ASSERT_EQ(0, FileSize(LogName(number))); + } else { + ASSERT_LT(0, FileSize(LogName(number))); + } + Open(); + ASSERT_EQ(1, NumLogs()); + ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file"; + ASSERT_EQ("bar", Get("foo")); + Open(); + ASSERT_EQ(1, NumLogs()); + ASSERT_EQ(number, FirstLogFile()) << "did not reuse log file"; + ASSERT_EQ("bar", Get("foo")); + } +} + +TEST(RecoveryTest, MultipleMemTables) { + // Make a large log. + const int kNum = 1000; + for (int i = 0; i < kNum; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "%050d", i); + ASSERT_OK(Put(buf, buf)); + } + ASSERT_EQ(0, NumTables()); + Close(); + ASSERT_EQ(0, NumTables()); + ASSERT_EQ(1, NumLogs()); + uint64_t old_log_file = FirstLogFile(); + + // Force creation of multiple memtables by reducing the write buffer size. + Options opt; + opt.reuse_logs = true; + opt.write_buffer_size = (kNum * 100) / 2; + Open(&opt); + ASSERT_LE(2, NumTables()); + ASSERT_EQ(1, NumLogs()); + ASSERT_NE(old_log_file, FirstLogFile()) << "must not reuse log"; + for (int i = 0; i < kNum; i++) { + char buf[100]; + snprintf(buf, sizeof(buf), "%050d", i); + ASSERT_EQ(buf, Get(buf)); + } +} + +TEST(RecoveryTest, MultipleLogFiles) { + ASSERT_OK(Put("foo", "bar")); + Close(); + ASSERT_EQ(1, NumLogs()); + + // Make a bunch of uncompacted log files. + uint64_t old_log = FirstLogFile(); + MakeLogFile(old_log + 1, 1000, "hello", "world"); + MakeLogFile(old_log + 2, 1001, "hi", "there"); + MakeLogFile(old_log + 3, 1002, "foo", "bar2"); + + // Recover and check that all log files were processed. + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + uint64_t new_log = FirstLogFile(); + ASSERT_LE(old_log + 3, new_log); + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); + + // Test that previous recovery produced recoverable state. + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + if (CanAppend()) { + ASSERT_EQ(new_log, FirstLogFile()); + } + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); + + // Check that introducing an older log file does not cause it to be re-read. + Close(); + MakeLogFile(old_log + 1, 2000, "hello", "stale write"); + Open(); + ASSERT_LE(1, NumTables()); + ASSERT_EQ(1, NumLogs()); + if (CanAppend()) { + ASSERT_EQ(new_log, FirstLogFile()); + } + ASSERT_EQ("bar2", Get("foo")); + ASSERT_EQ("world", Get("hello")); + ASSERT_EQ("there", Get("hi")); +} + +TEST(RecoveryTest, ManifestMissing) { + ASSERT_OK(Put("foo", "bar")); + Close(); + DeleteManifestFile(); + + Status status = OpenWithStatus(); + ASSERT_TRUE(status.IsCorruption()); +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc index 4cd4bb047..04847c3bb 100644 --- a/src/leveldb/db/repair.cc +++ b/src/leveldb/db/repair.cc @@ -54,7 +54,7 @@ class Repairer { owns_cache_(options_.block_cache != options.block_cache), next_file_number_(1) { // TableCache can be small since we expect each table to be opened once. - table_cache_ = new TableCache(dbname_, &options_, 10); + table_cache_ = new TableCache(dbname_, options_, 10); } ~Repairer() { @@ -84,9 +84,7 @@ class Repairer { "recovered %d files; %llu bytes. " "Some data may have been lost. " "****", - dbname_.c_str(), - static_cast(tables_.size()), - bytes); + dbname_.c_str(), static_cast(tables_.size()), bytes); } return status; } @@ -97,22 +95,6 @@ class Repairer { SequenceNumber max_sequence; }; - std::string const dbname_; - Env* const env_; - InternalKeyComparator const icmp_; - InternalFilterPolicy const ipolicy_; - Options const options_; - bool owns_info_log_; - bool owns_cache_; - TableCache* table_cache_; - VersionEdit edit_; - - std::vector manifests_; - std::vector table_numbers_; - std::vector logs_; - std::vector tables_; - uint64_t next_file_number_; - Status FindFiles() { std::vector filenames; Status status = env_->GetChildren(dbname_, &filenames); @@ -152,8 +134,7 @@ class Repairer { Status status = ConvertLogToTable(logs_[i]); if (!status.ok()) { Log(options_.info_log, "Log #%llu: ignoring conversion error: %s", - (unsigned long long) logs_[i], - status.ToString().c_str()); + (unsigned long long)logs_[i], status.ToString().c_str()); } ArchiveFile(logname); } @@ -164,11 +145,10 @@ class Repairer { Env* env; Logger* info_log; uint64_t lognum; - virtual void Corruption(size_t bytes, const Status& s) { + void Corruption(size_t bytes, const Status& s) override { // We print error messages for corruption, but continue repairing. Log(info_log, "Log #%llu: dropping %d bytes; %s", - (unsigned long long) lognum, - static_cast(bytes), + (unsigned long long)lognum, static_cast(bytes), s.ToString().c_str()); } }; @@ -190,8 +170,8 @@ class Repairer { // corruptions cause entire commits to be skipped instead of // propagating bad information (like overly large sequence // numbers). - log::Reader reader(lfile, &reporter, false/*do not checksum*/, - 0/*initial_offset*/); + log::Reader reader(lfile, &reporter, false /*do not checksum*/, + 0 /*initial_offset*/); // Read all the records and add to a memtable std::string scratch; @@ -202,8 +182,8 @@ class Repairer { int counter = 0; while (reader.ReadRecord(&record, &scratch)) { if (record.size() < 12) { - reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + reporter.Corruption(record.size(), + Status::Corruption("log record too small", logname)); continue; } WriteBatchInternal::SetContents(&batch, record); @@ -212,8 +192,7 @@ class Repairer { counter += WriteBatchInternal::Count(&batch); } else { Log(options_.info_log, "Log #%llu: ignoring %s", - (unsigned long long) log, - status.ToString().c_str()); + (unsigned long long)log, status.ToString().c_str()); status = Status::OK(); // Keep going with rest of file } } @@ -227,16 +206,14 @@ class Repairer { status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta); delete iter; mem->Unref(); - mem = NULL; + mem = nullptr; if (status.ok()) { if (meta.file_size > 0) { table_numbers_.push_back(meta.number); } } Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s", - (unsigned long long) log, - counter, - (unsigned long long) meta.number, + (unsigned long long)log, counter, (unsigned long long)meta.number, status.ToString().c_str()); return status; } @@ -272,8 +249,7 @@ class Repairer { ArchiveFile(TableFileName(dbname_, number)); ArchiveFile(SSTTableFileName(dbname_, number)); Log(options_.info_log, "Table #%llu: dropped: %s", - (unsigned long long) t.meta.number, - status.ToString().c_str()); + (unsigned long long)t.meta.number, status.ToString().c_str()); return; } @@ -287,8 +263,7 @@ class Repairer { Slice key = iter->key(); if (!ParseInternalKey(key, &parsed)) { Log(options_.info_log, "Table #%llu: unparsable key %s", - (unsigned long long) t.meta.number, - EscapeString(key).c_str()); + (unsigned long long)t.meta.number, EscapeString(key).c_str()); continue; } @@ -307,9 +282,7 @@ class Repairer { } delete iter; Log(options_.info_log, "Table #%llu: %d entries %s", - (unsigned long long) t.meta.number, - counter, - status.ToString().c_str()); + (unsigned long long)t.meta.number, counter, status.ToString().c_str()); if (status.ok()) { tables_.push_back(t); @@ -350,20 +323,20 @@ class Repairer { } } delete builder; - builder = NULL; + builder = nullptr; if (s.ok()) { s = file->Close(); } delete file; - file = NULL; + file = nullptr; if (counter > 0 && s.ok()) { std::string orig = TableFileName(dbname_, t.meta.number); s = env_->RenameFile(copy, orig); if (s.ok()) { Log(options_.info_log, "Table #%llu: %d entries repaired", - (unsigned long long) t.meta.number, counter); + (unsigned long long)t.meta.number, counter); tables_.push_back(t); } } @@ -395,11 +368,11 @@ class Repairer { for (size_t i = 0; i < tables_.size(); i++) { // TODO(opt): separate out into multiple levels const TableInfo& t = tables_[i]; - edit_.AddFile(0, t.meta.number, t.meta.file_size, - t.meta.smallest, t.meta.largest); + edit_.AddFile(0, t.meta.number, t.meta.file_size, t.meta.smallest, + t.meta.largest); } - //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); + // fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str()); { log::Writer log(file); std::string record; @@ -410,7 +383,7 @@ class Repairer { status = file->Close(); } delete file; - file = NULL; + file = nullptr; if (!status.ok()) { env_->DeleteFile(tmp); @@ -438,18 +411,34 @@ class Repairer { // dir/lost/foo const char* slash = strrchr(fname.c_str(), '/'); std::string new_dir; - if (slash != NULL) { + if (slash != nullptr) { new_dir.assign(fname.data(), slash - fname.data()); } new_dir.append("/lost"); env_->CreateDir(new_dir); // Ignore error std::string new_file = new_dir; new_file.append("/"); - new_file.append((slash == NULL) ? fname.c_str() : slash + 1); + new_file.append((slash == nullptr) ? fname.c_str() : slash + 1); Status s = env_->RenameFile(fname, new_file); - Log(options_.info_log, "Archiving %s: %s\n", - fname.c_str(), s.ToString().c_str()); + Log(options_.info_log, "Archiving %s: %s\n", fname.c_str(), + s.ToString().c_str()); } + + const std::string dbname_; + Env* const env_; + InternalKeyComparator const icmp_; + InternalFilterPolicy const ipolicy_; + const Options options_; + bool owns_info_log_; + bool owns_cache_; + TableCache* table_cache_; + VersionEdit edit_; + + std::vector manifests_; + std::vector table_numbers_; + std::vector logs_; + std::vector tables_; + uint64_t next_file_number_; }; } // namespace diff --git a/src/leveldb/db/skiplist.h b/src/leveldb/db/skiplist.h index ed8b09220..a59b45b38 100644 --- a/src/leveldb/db/skiplist.h +++ b/src/leveldb/db/skiplist.h @@ -1,10 +1,10 @@ -#ifndef STORAGE_LEVELDB_DB_SKIPLIST_H_ -#define STORAGE_LEVELDB_DB_SKIPLIST_H_ - // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -// + +#ifndef STORAGE_LEVELDB_DB_SKIPLIST_H_ +#define STORAGE_LEVELDB_DB_SKIPLIST_H_ + // Thread safety // ------------- // @@ -27,9 +27,10 @@ // // ... prev vs. next pointer ordering ... -#include -#include -#include "port/port.h" +#include +#include +#include + #include "util/arena.h" #include "util/random.h" @@ -37,7 +38,7 @@ namespace leveldb { class Arena; -template +template class SkipList { private: struct Node; @@ -48,6 +49,9 @@ class SkipList { // must remain allocated for the lifetime of the skiplist object. explicit SkipList(Comparator cmp, Arena* arena); + SkipList(const SkipList&) = delete; + SkipList& operator=(const SkipList&) = delete; + // Insert key into the list. // REQUIRES: nothing that compares equal to key is currently in the list. void Insert(const Key& key); @@ -97,24 +101,10 @@ class SkipList { private: enum { kMaxHeight = 12 }; - // Immutable after construction - Comparator const compare_; - Arena* const arena_; // Arena used for allocations of nodes - - Node* const head_; - - // Modified only by Insert(). Read racily by readers, but stale - // values are ok. - port::AtomicPointer max_height_; // Height of the entire list - inline int GetMaxHeight() const { - return static_cast( - reinterpret_cast(max_height_.NoBarrier_Load())); + return max_height_.load(std::memory_order_relaxed); } - // Read/written only by Insert(). - Random rnd_; - Node* NewNode(const Key& key, int height); int RandomHeight(); bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); } @@ -123,9 +113,9 @@ class SkipList { bool KeyIsAfterNode(const Key& key, Node* n) const; // Return the earliest node that comes at or after key. - // Return NULL if there is no such node. + // Return nullptr if there is no such node. // - // If prev is non-NULL, fills prev[level] with pointer to previous + // If prev is non-null, fills prev[level] with pointer to previous // node at "level" for every level in [0..max_height_-1]. Node* FindGreaterOrEqual(const Key& key, Node** prev) const; @@ -137,15 +127,24 @@ class SkipList { // Return head_ if list is empty. Node* FindLast() const; - // No copying allowed - SkipList(const SkipList&); - void operator=(const SkipList&); + // Immutable after construction + Comparator const compare_; + Arena* const arena_; // Arena used for allocations of nodes + + Node* const head_; + + // Modified only by Insert(). Read racily by readers, but stale + // values are ok. + std::atomic max_height_; // Height of the entire list + + // Read/written only by Insert(). + Random rnd_; }; // Implementation details follow -template -struct SkipList::Node { - explicit Node(const Key& k) : key(k) { } +template +struct SkipList::Node { + explicit Node(const Key& k) : key(k) {} Key const key; @@ -155,92 +154,92 @@ struct SkipList::Node { assert(n >= 0); // Use an 'acquire load' so that we observe a fully initialized // version of the returned Node. - return reinterpret_cast(next_[n].Acquire_Load()); + return next_[n].load(std::memory_order_acquire); } void SetNext(int n, Node* x) { assert(n >= 0); // Use a 'release store' so that anybody who reads through this // pointer observes a fully initialized version of the inserted node. - next_[n].Release_Store(x); + next_[n].store(x, std::memory_order_release); } // No-barrier variants that can be safely used in a few locations. Node* NoBarrier_Next(int n) { assert(n >= 0); - return reinterpret_cast(next_[n].NoBarrier_Load()); + return next_[n].load(std::memory_order_relaxed); } void NoBarrier_SetNext(int n, Node* x) { assert(n >= 0); - next_[n].NoBarrier_Store(x); + next_[n].store(x, std::memory_order_relaxed); } private: // Array of length equal to the node height. next_[0] is lowest level link. - port::AtomicPointer next_[1]; + std::atomic next_[1]; }; -template -typename SkipList::Node* -SkipList::NewNode(const Key& key, int height) { - char* mem = arena_->AllocateAligned( - sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); - return new (mem) Node(key); +template +typename SkipList::Node* SkipList::NewNode( + const Key& key, int height) { + char* const node_memory = arena_->AllocateAligned( + sizeof(Node) + sizeof(std::atomic) * (height - 1)); + return new (node_memory) Node(key); } -template -inline SkipList::Iterator::Iterator(const SkipList* list) { +template +inline SkipList::Iterator::Iterator(const SkipList* list) { list_ = list; - node_ = NULL; + node_ = nullptr; } -template -inline bool SkipList::Iterator::Valid() const { - return node_ != NULL; +template +inline bool SkipList::Iterator::Valid() const { + return node_ != nullptr; } -template -inline const Key& SkipList::Iterator::key() const { +template +inline const Key& SkipList::Iterator::key() const { assert(Valid()); return node_->key; } -template -inline void SkipList::Iterator::Next() { +template +inline void SkipList::Iterator::Next() { assert(Valid()); node_ = node_->Next(0); } -template -inline void SkipList::Iterator::Prev() { +template +inline void SkipList::Iterator::Prev() { // Instead of using explicit "prev" links, we just search for the // last node that falls before key. assert(Valid()); node_ = list_->FindLessThan(node_->key); if (node_ == list_->head_) { - node_ = NULL; + node_ = nullptr; } } -template -inline void SkipList::Iterator::Seek(const Key& target) { - node_ = list_->FindGreaterOrEqual(target, NULL); +template +inline void SkipList::Iterator::Seek(const Key& target) { + node_ = list_->FindGreaterOrEqual(target, nullptr); } -template -inline void SkipList::Iterator::SeekToFirst() { +template +inline void SkipList::Iterator::SeekToFirst() { node_ = list_->head_->Next(0); } -template -inline void SkipList::Iterator::SeekToLast() { +template +inline void SkipList::Iterator::SeekToLast() { node_ = list_->FindLast(); if (node_ == list_->head_) { - node_ = NULL; + node_ = nullptr; } } -template -int SkipList::RandomHeight() { +template +int SkipList::RandomHeight() { // Increase height with probability 1 in kBranching static const unsigned int kBranching = 4; int height = 1; @@ -252,15 +251,16 @@ int SkipList::RandomHeight() { return height; } -template -bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { - // NULL n is considered infinite - return (n != NULL) && (compare_(n->key, key) < 0); +template +bool SkipList::KeyIsAfterNode(const Key& key, Node* n) const { + // null n is considered infinite + return (n != nullptr) && (compare_(n->key, key) < 0); } -template -typename SkipList::Node* SkipList::FindGreaterOrEqual(const Key& key, Node** prev) - const { +template +typename SkipList::Node* +SkipList::FindGreaterOrEqual(const Key& key, + Node** prev) const { Node* x = head_; int level = GetMaxHeight() - 1; while (true) { @@ -269,7 +269,7 @@ typename SkipList::Node* SkipList::FindGreaterOr // Keep searching in this list x = next; } else { - if (prev != NULL) prev[level] = x; + if (prev != nullptr) prev[level] = x; if (level == 0) { return next; } else { @@ -280,15 +280,15 @@ typename SkipList::Node* SkipList::FindGreaterOr } } -template -typename SkipList::Node* -SkipList::FindLessThan(const Key& key) const { +template +typename SkipList::Node* +SkipList::FindLessThan(const Key& key) const { Node* x = head_; int level = GetMaxHeight() - 1; while (true) { assert(x == head_ || compare_(x->key, key) < 0); Node* next = x->Next(level); - if (next == NULL || compare_(next->key, key) >= 0) { + if (next == nullptr || compare_(next->key, key) >= 0) { if (level == 0) { return x; } else { @@ -301,14 +301,14 @@ SkipList::FindLessThan(const Key& key) const { } } -template -typename SkipList::Node* SkipList::FindLast() +template +typename SkipList::Node* SkipList::FindLast() const { Node* x = head_; int level = GetMaxHeight() - 1; while (true) { Node* next = x->Next(level); - if (next == NULL) { + if (next == nullptr) { if (level == 0) { return x; } else { @@ -321,43 +321,41 @@ typename SkipList::Node* SkipList::FindLast() } } -template -SkipList::SkipList(Comparator cmp, Arena* arena) +template +SkipList::SkipList(Comparator cmp, Arena* arena) : compare_(cmp), arena_(arena), head_(NewNode(0 /* any key will do */, kMaxHeight)), - max_height_(reinterpret_cast(1)), + max_height_(1), rnd_(0xdeadbeef) { for (int i = 0; i < kMaxHeight; i++) { - head_->SetNext(i, NULL); + head_->SetNext(i, nullptr); } } -template -void SkipList::Insert(const Key& key) { +template +void SkipList::Insert(const Key& key) { // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() // here since Insert() is externally synchronized. Node* prev[kMaxHeight]; Node* x = FindGreaterOrEqual(key, prev); // Our data structure does not allow duplicate insertion - assert(x == NULL || !Equal(key, x->key)); + assert(x == nullptr || !Equal(key, x->key)); int height = RandomHeight(); if (height > GetMaxHeight()) { for (int i = GetMaxHeight(); i < height; i++) { prev[i] = head_; } - //fprintf(stderr, "Change height from %d to %d\n", max_height_, height); - // It is ok to mutate max_height_ without any synchronization // with concurrent readers. A concurrent reader that observes // the new value of max_height_ will see either the old value of - // new level pointers from head_ (NULL), or a new value set in + // new level pointers from head_ (nullptr), or a new value set in // the loop below. In the former case the reader will - // immediately drop to the next level since NULL sorts after all + // immediately drop to the next level since nullptr sorts after all // keys. In the latter case the reader will use the new node. - max_height_.NoBarrier_Store(reinterpret_cast(height)); + max_height_.store(height, std::memory_order_relaxed); } x = NewNode(key, height); @@ -369,10 +367,10 @@ void SkipList::Insert(const Key& key) { } } -template -bool SkipList::Contains(const Key& key) const { - Node* x = FindGreaterOrEqual(key, NULL); - if (x != NULL && Equal(key, x->key)) { +template +bool SkipList::Contains(const Key& key) const { + Node* x = FindGreaterOrEqual(key, nullptr); + if (x != nullptr && Equal(key, x->key)) { return true; } else { return false; diff --git a/src/leveldb/db/skiplist_test.cc b/src/leveldb/db/skiplist_test.cc index c78f4b4fb..9fa2d9682 100644 --- a/src/leveldb/db/skiplist_test.cc +++ b/src/leveldb/db/skiplist_test.cc @@ -3,8 +3,13 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include "db/skiplist.h" + +#include #include + #include "leveldb/env.h" +#include "port/port.h" +#include "port/thread_annotations.h" #include "util/arena.h" #include "util/hash.h" #include "util/random.h" @@ -26,7 +31,7 @@ struct Comparator { } }; -class SkipTest { }; +class SkipTest {}; TEST(SkipTest, Empty) { Arena arena; @@ -112,8 +117,7 @@ TEST(SkipTest, InsertAndLookup) { // Compare against model iterator for (std::set::reverse_iterator model_iter = keys.rbegin(); - model_iter != keys.rend(); - ++model_iter) { + model_iter != keys.rend(); ++model_iter) { ASSERT_TRUE(iter.Valid()); ASSERT_EQ(*model_iter, iter.key()); iter.Prev(); @@ -126,7 +130,7 @@ TEST(SkipTest, InsertAndLookup) { // concurrent readers (with no synchronization other than when a // reader's iterator is created), the reader always observes all the // data that was present in the skip list when the iterator was -// constructor. Because insertions are happening concurrently, we may +// constructed. Because insertions are happening concurrently, we may // also observe new values that were inserted since the iterator was // constructed, but we should never miss any values that were present // at iterator construction time. @@ -155,12 +159,12 @@ class ConcurrentTest { static uint64_t hash(Key key) { return key & 0xff; } static uint64_t HashNumbers(uint64_t k, uint64_t g) { - uint64_t data[2] = { k, g }; + uint64_t data[2] = {k, g}; return Hash(reinterpret_cast(data), sizeof(data), 0); } static Key MakeKey(uint64_t k, uint64_t g) { - assert(sizeof(Key) == sizeof(uint64_t)); + static_assert(sizeof(Key) == sizeof(uint64_t), ""); assert(k <= K); // We sometimes pass K to seek to the end of the skiplist assert(g <= 0xffffffffu); return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff)); @@ -186,13 +190,11 @@ class ConcurrentTest { // Per-key generation struct State { - port::AtomicPointer generation[K]; - void Set(int k, intptr_t v) { - generation[k].Release_Store(reinterpret_cast(v)); - } - intptr_t Get(int k) { - return reinterpret_cast(generation[k].Acquire_Load()); + std::atomic generation[K]; + void Set(int k, int v) { + generation[k].store(v, std::memory_order_release); } + int Get(int k) { return generation[k].load(std::memory_order_acquire); } State() { for (int k = 0; k < K; k++) { @@ -211,7 +213,7 @@ class ConcurrentTest { SkipList list_; public: - ConcurrentTest() : list_(Comparator(), &arena_) { } + ConcurrentTest() : list_(Comparator(), &arena_) {} // REQUIRES: External synchronization void WriteStep(Random* rnd) { @@ -250,11 +252,9 @@ class ConcurrentTest { // Note that generation 0 is never inserted, so it is ok if // <*,0,*> is missing. ASSERT_TRUE((gen(pos) == 0) || - (gen(pos) > initial_state.Get(key(pos))) - ) << "key: " << key(pos) - << "; gen: " << gen(pos) - << "; initgen: " - << initial_state.Get(key(pos)); + (gen(pos) > static_cast(initial_state.Get(key(pos))))) + << "key: " << key(pos) << "; gen: " << gen(pos) + << "; initgen: " << initial_state.Get(key(pos)); // Advance to next key in the valid key space if (key(pos) < key(current)) { @@ -298,21 +298,14 @@ class TestState { public: ConcurrentTest t_; int seed_; - port::AtomicPointer quit_flag_; + std::atomic quit_flag_; - enum ReaderState { - STARTING, - RUNNING, - DONE - }; + enum ReaderState { STARTING, RUNNING, DONE }; explicit TestState(int s) - : seed_(s), - quit_flag_(NULL), - state_(STARTING), - state_cv_(&mu_) {} + : seed_(s), quit_flag_(false), state_(STARTING), state_cv_(&mu_) {} - void Wait(ReaderState s) { + void Wait(ReaderState s) LOCKS_EXCLUDED(mu_) { mu_.Lock(); while (state_ != s) { state_cv_.Wait(); @@ -320,7 +313,7 @@ class TestState { mu_.Unlock(); } - void Change(ReaderState s) { + void Change(ReaderState s) LOCKS_EXCLUDED(mu_) { mu_.Lock(); state_ = s; state_cv_.Signal(); @@ -329,8 +322,8 @@ class TestState { private: port::Mutex mu_; - ReaderState state_; - port::CondVar state_cv_; + ReaderState state_ GUARDED_BY(mu_); + port::CondVar state_cv_ GUARDED_BY(mu_); }; static void ConcurrentReader(void* arg) { @@ -338,7 +331,7 @@ static void ConcurrentReader(void* arg) { Random rnd(state->seed_); int64_t reads = 0; state->Change(TestState::RUNNING); - while (!state->quit_flag_.Acquire_Load()) { + while (!state->quit_flag_.load(std::memory_order_acquire)) { state->t_.ReadStep(&rnd); ++reads; } @@ -360,7 +353,7 @@ static void RunConcurrent(int run) { for (int i = 0; i < kSize; i++) { state.t_.WriteStep(&rnd); } - state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do + state.quit_flag_.store(true, std::memory_order_release); state.Wait(TestState::DONE); } } @@ -373,6 +366,4 @@ TEST(SkipTest, Concurrent5) { RunConcurrent(5); } } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/snapshot.h b/src/leveldb/db/snapshot.h index e7f8fd2c3..9f1d66491 100644 --- a/src/leveldb/db/snapshot.h +++ b/src/leveldb/db/snapshot.h @@ -5,6 +5,7 @@ #ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_ #define STORAGE_LEVELDB_DB_SNAPSHOT_H_ +#include "db/dbformat.h" #include "leveldb/db.h" namespace leveldb { @@ -15,50 +16,78 @@ class SnapshotList; // Each SnapshotImpl corresponds to a particular sequence number. class SnapshotImpl : public Snapshot { public: - SequenceNumber number_; // const after creation + SnapshotImpl(SequenceNumber sequence_number) + : sequence_number_(sequence_number) {} + + SequenceNumber sequence_number() const { return sequence_number_; } private: friend class SnapshotList; - // SnapshotImpl is kept in a doubly-linked circular list + // SnapshotImpl is kept in a doubly-linked circular list. The SnapshotList + // implementation operates on the next/previous fields direcly. SnapshotImpl* prev_; SnapshotImpl* next_; - SnapshotList* list_; // just for sanity checks + const SequenceNumber sequence_number_; + +#if !defined(NDEBUG) + SnapshotList* list_ = nullptr; +#endif // !defined(NDEBUG) }; class SnapshotList { public: - SnapshotList() { - list_.prev_ = &list_; - list_.next_ = &list_; + SnapshotList() : head_(0) { + head_.prev_ = &head_; + head_.next_ = &head_; + } + + bool empty() const { return head_.next_ == &head_; } + SnapshotImpl* oldest() const { + assert(!empty()); + return head_.next_; } + SnapshotImpl* newest() const { + assert(!empty()); + return head_.prev_; + } + + // Creates a SnapshotImpl and appends it to the end of the list. + SnapshotImpl* New(SequenceNumber sequence_number) { + assert(empty() || newest()->sequence_number_ <= sequence_number); + + SnapshotImpl* snapshot = new SnapshotImpl(sequence_number); - bool empty() const { return list_.next_ == &list_; } - SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; } - SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; } - - const SnapshotImpl* New(SequenceNumber seq) { - SnapshotImpl* s = new SnapshotImpl; - s->number_ = seq; - s->list_ = this; - s->next_ = &list_; - s->prev_ = list_.prev_; - s->prev_->next_ = s; - s->next_->prev_ = s; - return s; +#if !defined(NDEBUG) + snapshot->list_ = this; +#endif // !defined(NDEBUG) + snapshot->next_ = &head_; + snapshot->prev_ = head_.prev_; + snapshot->prev_->next_ = snapshot; + snapshot->next_->prev_ = snapshot; + return snapshot; } - void Delete(const SnapshotImpl* s) { - assert(s->list_ == this); - s->prev_->next_ = s->next_; - s->next_->prev_ = s->prev_; - delete s; + // Removes a SnapshotImpl from this list. + // + // The snapshot must have been created by calling New() on this list. + // + // The snapshot pointer should not be const, because its memory is + // deallocated. However, that would force us to change DB::ReleaseSnapshot(), + // which is in the API, and currently takes a const Snapshot. + void Delete(const SnapshotImpl* snapshot) { +#if !defined(NDEBUG) + assert(snapshot->list_ == this); +#endif // !defined(NDEBUG) + snapshot->prev_->next_ = snapshot->next_; + snapshot->next_->prev_ = snapshot->prev_; + delete snapshot; } private: // Dummy head of doubly-linked list of snapshots - SnapshotImpl list_; + SnapshotImpl head_; }; } // namespace leveldb diff --git a/src/leveldb/db/table_cache.cc b/src/leveldb/db/table_cache.cc index e3d82cd3e..73f05fd7b 100644 --- a/src/leveldb/db/table_cache.cc +++ b/src/leveldb/db/table_cache.cc @@ -29,18 +29,14 @@ static void UnrefEntry(void* arg1, void* arg2) { cache->Release(h); } -TableCache::TableCache(const std::string& dbname, - const Options* options, +TableCache::TableCache(const std::string& dbname, const Options& options, int entries) - : env_(options->env), + : env_(options.env), dbname_(dbname), options_(options), - cache_(NewLRUCache(entries)) { -} + cache_(NewLRUCache(entries)) {} -TableCache::~TableCache() { - delete cache_; -} +TableCache::~TableCache() { delete cache_; } Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle** handle) { @@ -49,10 +45,10 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, EncodeFixed64(buf, file_number); Slice key(buf, sizeof(buf)); *handle = cache_->Lookup(key); - if (*handle == NULL) { + if (*handle == nullptr) { std::string fname = TableFileName(dbname_, file_number); - RandomAccessFile* file = NULL; - Table* table = NULL; + RandomAccessFile* file = nullptr; + Table* table = nullptr; s = env_->NewRandomAccessFile(fname, &file); if (!s.ok()) { std::string old_fname = SSTTableFileName(dbname_, file_number); @@ -61,11 +57,11 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, } } if (s.ok()) { - s = Table::Open(*options_, file, file_size, &table); + s = Table::Open(options_, file, file_size, &table); } if (!s.ok()) { - assert(table == NULL); + assert(table == nullptr); delete file; // We do not cache error results so that if the error is transient, // or somebody repairs the file, we recover automatically. @@ -80,14 +76,13 @@ Status TableCache::FindTable(uint64_t file_number, uint64_t file_size, } Iterator* TableCache::NewIterator(const ReadOptions& options, - uint64_t file_number, - uint64_t file_size, + uint64_t file_number, uint64_t file_size, Table** tableptr) { - if (tableptr != NULL) { - *tableptr = NULL; + if (tableptr != nullptr) { + *tableptr = nullptr; } - Cache::Handle* handle = NULL; + Cache::Handle* handle = nullptr; Status s = FindTable(file_number, file_size, &handle); if (!s.ok()) { return NewErrorIterator(s); @@ -96,23 +91,21 @@ Iterator* TableCache::NewIterator(const ReadOptions& options, Table* table = reinterpret_cast(cache_->Value(handle))->table; Iterator* result = table->NewIterator(options); result->RegisterCleanup(&UnrefEntry, cache_, handle); - if (tableptr != NULL) { + if (tableptr != nullptr) { *tableptr = table; } return result; } -Status TableCache::Get(const ReadOptions& options, - uint64_t file_number, - uint64_t file_size, - const Slice& k, - void* arg, - void (*saver)(void*, const Slice&, const Slice&)) { - Cache::Handle* handle = NULL; +Status TableCache::Get(const ReadOptions& options, uint64_t file_number, + uint64_t file_size, const Slice& k, void* arg, + void (*handle_result)(void*, const Slice&, + const Slice&)) { + Cache::Handle* handle = nullptr; Status s = FindTable(file_number, file_size, &handle); if (s.ok()) { Table* t = reinterpret_cast(cache_->Value(handle))->table; - s = t->InternalGet(options, k, arg, saver); + s = t->InternalGet(options, k, arg, handle_result); cache_->Release(handle); } return s; diff --git a/src/leveldb/db/table_cache.h b/src/leveldb/db/table_cache.h index 8cf4aaf12..93069c884 100644 --- a/src/leveldb/db/table_cache.h +++ b/src/leveldb/db/table_cache.h @@ -7,8 +7,10 @@ #ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_ #define STORAGE_LEVELDB_DB_TABLE_CACHE_H_ -#include #include + +#include + #include "db/dbformat.h" #include "leveldb/cache.h" #include "leveldb/table.h" @@ -20,40 +22,35 @@ class Env; class TableCache { public: - TableCache(const std::string& dbname, const Options* options, int entries); + TableCache(const std::string& dbname, const Options& options, int entries); ~TableCache(); // Return an iterator for the specified file number (the corresponding // file length must be exactly "file_size" bytes). If "tableptr" is - // non-NULL, also sets "*tableptr" to point to the Table object - // underlying the returned iterator, or NULL if no Table object underlies - // the returned iterator. The returned "*tableptr" object is owned by - // the cache and should not be deleted, and is valid for as long as the + // non-null, also sets "*tableptr" to point to the Table object + // underlying the returned iterator, or to nullptr if no Table object + // underlies the returned iterator. The returned "*tableptr" object is owned + // by the cache and should not be deleted, and is valid for as long as the // returned iterator is live. - Iterator* NewIterator(const ReadOptions& options, - uint64_t file_number, - uint64_t file_size, - Table** tableptr = NULL); + Iterator* NewIterator(const ReadOptions& options, uint64_t file_number, + uint64_t file_size, Table** tableptr = nullptr); // If a seek to internal key "k" in specified file finds an entry, // call (*handle_result)(arg, found_key, found_value). - Status Get(const ReadOptions& options, - uint64_t file_number, - uint64_t file_size, - const Slice& k, - void* arg, + Status Get(const ReadOptions& options, uint64_t file_number, + uint64_t file_size, const Slice& k, void* arg, void (*handle_result)(void*, const Slice&, const Slice&)); // Evict any entry for the specified file number void Evict(uint64_t file_number); private: + Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); + Env* const env_; const std::string dbname_; - const Options* options_; + const Options& options_; Cache* cache_; - - Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**); }; } // namespace leveldb diff --git a/src/leveldb/db/version_edit.cc b/src/leveldb/db/version_edit.cc index f10a2d58b..cd770ef12 100644 --- a/src/leveldb/db/version_edit.cc +++ b/src/leveldb/db/version_edit.cc @@ -12,15 +12,15 @@ namespace leveldb { // Tag numbers for serialized VersionEdit. These numbers are written to // disk and should not be changed. enum Tag { - kComparator = 1, - kLogNumber = 2, - kNextFileNumber = 3, - kLastSequence = 4, - kCompactPointer = 5, - kDeletedFile = 6, - kNewFile = 7, + kComparator = 1, + kLogNumber = 2, + kNextFileNumber = 3, + kLastSequence = 4, + kCompactPointer = 5, + kDeletedFile = 6, + kNewFile = 7, // 8 was used for large value refs - kPrevLogNumber = 9 + kPrevLogNumber = 9 }; void VersionEdit::Clear() { @@ -66,12 +66,10 @@ void VersionEdit::EncodeTo(std::string* dst) const { PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode()); } - for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); - iter != deleted_files_.end(); - ++iter) { + for (const auto& deleted_file_kvp : deleted_files_) { PutVarint32(dst, kDeletedFile); - PutVarint32(dst, iter->first); // level - PutVarint64(dst, iter->second); // file number + PutVarint32(dst, deleted_file_kvp.first); // level + PutVarint64(dst, deleted_file_kvp.second); // file number } for (size_t i = 0; i < new_files_.size(); i++) { @@ -88,8 +86,7 @@ void VersionEdit::EncodeTo(std::string* dst) const { static bool GetInternalKey(Slice* input, InternalKey* dst) { Slice str; if (GetLengthPrefixedSlice(input, &str)) { - dst->DecodeFrom(str); - return true; + return dst->DecodeFrom(str); } else { return false; } @@ -97,8 +94,7 @@ static bool GetInternalKey(Slice* input, InternalKey* dst) { static bool GetLevel(Slice* input, int* level) { uint32_t v; - if (GetVarint32(input, &v) && - v < config::kNumLevels) { + if (GetVarint32(input, &v) && v < config::kNumLevels) { *level = v; return true; } else { @@ -109,7 +105,7 @@ static bool GetLevel(Slice* input, int* level) { Status VersionEdit::DecodeFrom(const Slice& src) { Clear(); Slice input = src; - const char* msg = NULL; + const char* msg = nullptr; uint32_t tag; // Temporary storage for parsing @@ -119,7 +115,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) { Slice str; InternalKey key; - while (msg == NULL && GetVarint32(&input, &tag)) { + while (msg == nullptr && GetVarint32(&input, &tag)) { switch (tag) { case kComparator: if (GetLengthPrefixedSlice(&input, &str)) { @@ -163,8 +159,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) { break; case kCompactPointer: - if (GetLevel(&input, &level) && - GetInternalKey(&input, &key)) { + if (GetLevel(&input, &level) && GetInternalKey(&input, &key)) { compact_pointers_.push_back(std::make_pair(level, key)); } else { msg = "compaction pointer"; @@ -172,8 +167,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) { break; case kDeletedFile: - if (GetLevel(&input, &level) && - GetVarint64(&input, &number)) { + if (GetLevel(&input, &level) && GetVarint64(&input, &number)) { deleted_files_.insert(std::make_pair(level, number)); } else { msg = "deleted file"; @@ -181,8 +175,7 @@ Status VersionEdit::DecodeFrom(const Slice& src) { break; case kNewFile: - if (GetLevel(&input, &level) && - GetVarint64(&input, &f.number) && + if (GetLevel(&input, &level) && GetVarint64(&input, &f.number) && GetVarint64(&input, &f.file_size) && GetInternalKey(&input, &f.smallest) && GetInternalKey(&input, &f.largest)) { @@ -198,12 +191,12 @@ Status VersionEdit::DecodeFrom(const Slice& src) { } } - if (msg == NULL && !input.empty()) { + if (msg == nullptr && !input.empty()) { msg = "invalid tag"; } Status result; - if (msg != NULL) { + if (msg != nullptr) { result = Status::Corruption("VersionEdit", msg); } return result; @@ -238,13 +231,11 @@ std::string VersionEdit::DebugString() const { r.append(" "); r.append(compact_pointers_[i].second.DebugString()); } - for (DeletedFileSet::const_iterator iter = deleted_files_.begin(); - iter != deleted_files_.end(); - ++iter) { + for (const auto& deleted_files_kvp : deleted_files_) { r.append("\n DeleteFile: "); - AppendNumberTo(&r, iter->first); + AppendNumberTo(&r, deleted_files_kvp.first); r.append(" "); - AppendNumberTo(&r, iter->second); + AppendNumberTo(&r, deleted_files_kvp.second); } for (size_t i = 0; i < new_files_.size(); i++) { const FileMetaData& f = new_files_[i].second; diff --git a/src/leveldb/db/version_edit.h b/src/leveldb/db/version_edit.h index eaef77b32..0de453177 100644 --- a/src/leveldb/db/version_edit.h +++ b/src/leveldb/db/version_edit.h @@ -8,6 +8,7 @@ #include #include #include + #include "db/dbformat.h" namespace leveldb { @@ -15,20 +16,20 @@ namespace leveldb { class VersionSet; struct FileMetaData { + FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) {} + int refs; - int allowed_seeks; // Seeks allowed until compaction + int allowed_seeks; // Seeks allowed until compaction uint64_t number; - uint64_t file_size; // File size in bytes - InternalKey smallest; // Smallest internal key served by table - InternalKey largest; // Largest internal key served by table - - FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { } + uint64_t file_size; // File size in bytes + InternalKey smallest; // Smallest internal key served by table + InternalKey largest; // Largest internal key served by table }; class VersionEdit { public: VersionEdit() { Clear(); } - ~VersionEdit() { } + ~VersionEdit() = default; void Clear(); @@ -59,10 +60,8 @@ class VersionEdit { // Add the specified file at the specified number. // REQUIRES: This version has not been saved (see VersionSet::SaveTo) // REQUIRES: "smallest" and "largest" are smallest and largest keys in file - void AddFile(int level, uint64_t file, - uint64_t file_size, - const InternalKey& smallest, - const InternalKey& largest) { + void AddFile(int level, uint64_t file, uint64_t file_size, + const InternalKey& smallest, const InternalKey& largest) { FileMetaData f; f.number = file; f.file_size = file_size; @@ -84,7 +83,7 @@ class VersionEdit { private: friend class VersionSet; - typedef std::set< std::pair > DeletedFileSet; + typedef std::set> DeletedFileSet; std::string comparator_; uint64_t log_number_; @@ -97,9 +96,9 @@ class VersionEdit { bool has_next_file_number_; bool has_last_sequence_; - std::vector< std::pair > compact_pointers_; + std::vector> compact_pointers_; DeletedFileSet deleted_files_; - std::vector< std::pair > new_files_; + std::vector> new_files_; }; } // namespace leveldb diff --git a/src/leveldb/db/version_edit_test.cc b/src/leveldb/db/version_edit_test.cc index 280310b49..0b7cda885 100644 --- a/src/leveldb/db/version_edit_test.cc +++ b/src/leveldb/db/version_edit_test.cc @@ -17,7 +17,7 @@ static void TestEncodeDecode(const VersionEdit& edit) { ASSERT_EQ(encoded, encoded2); } -class VersionEditTest { }; +class VersionEditTest {}; TEST(VersionEditTest, EncodeDecode) { static const uint64_t kBig = 1ull << 50; @@ -41,6 +41,4 @@ TEST(VersionEditTest, EncodeDecode) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/version_set.cc b/src/leveldb/db/version_set.cc index aa83df55e..cd07346ea 100644 --- a/src/leveldb/db/version_set.cc +++ b/src/leveldb/db/version_set.cc @@ -4,8 +4,10 @@ #include "db/version_set.h" -#include #include + +#include + #include "db/filename.h" #include "db/log_reader.h" #include "db/log_writer.h" @@ -20,21 +22,29 @@ namespace leveldb { -static const int kTargetFileSize = 2 * 1048576; +static size_t TargetFileSize(const Options* options) { + return options->max_file_size; +} // Maximum bytes of overlaps in grandparent (i.e., level+2) before we // stop building a single file in a level->level+1 compaction. -static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize; +static int64_t MaxGrandParentOverlapBytes(const Options* options) { + return 10 * TargetFileSize(options); +} // Maximum number of bytes in all compacted files. We avoid expanding // the lower level file set of a compaction if it would make the // total compaction cover more than this many bytes. -static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize; +static int64_t ExpandedCompactionByteSizeLimit(const Options* options) { + return 25 * TargetFileSize(options); +} -static double MaxBytesForLevel(int level) { +static double MaxBytesForLevel(const Options* options, int level) { // Note: the result for level zero is not really used since we set // the level-0 compaction threshold based on number of files. - double result = 10 * 1048576.0; // Result for both level-0 and level-1 + + // Result for both level-0 and level-1 + double result = 10. * 1048576.0; while (level > 1) { result *= 10; level--; @@ -42,8 +52,9 @@ static double MaxBytesForLevel(int level) { return result; } -static uint64_t MaxFileSizeForLevel(int level) { - return kTargetFileSize; // We could vary per level to reduce number of files? +static uint64_t MaxFileSizeForLevel(const Options* options, int level) { + // We could vary per level to reduce number of files? + return TargetFileSize(options); } static int64_t TotalFileSize(const std::vector& files) { @@ -75,8 +86,7 @@ Version::~Version() { } int FindFile(const InternalKeyComparator& icmp, - const std::vector& files, - const Slice& key) { + const std::vector& files, const Slice& key) { uint32_t left = 0; uint32_t right = files.size(); while (left < right) { @@ -95,26 +105,25 @@ int FindFile(const InternalKeyComparator& icmp, return right; } -static bool AfterFile(const Comparator* ucmp, - const Slice* user_key, const FileMetaData* f) { - // NULL user_key occurs before all keys and is therefore never after *f - return (user_key != NULL && +static bool AfterFile(const Comparator* ucmp, const Slice* user_key, + const FileMetaData* f) { + // null user_key occurs before all keys and is therefore never after *f + return (user_key != nullptr && ucmp->Compare(*user_key, f->largest.user_key()) > 0); } -static bool BeforeFile(const Comparator* ucmp, - const Slice* user_key, const FileMetaData* f) { - // NULL user_key occurs after all keys and is therefore never before *f - return (user_key != NULL && +static bool BeforeFile(const Comparator* ucmp, const Slice* user_key, + const FileMetaData* f) { + // null user_key occurs after all keys and is therefore never before *f + return (user_key != nullptr && ucmp->Compare(*user_key, f->smallest.user_key()) < 0); } -bool SomeFileOverlapsRange( - const InternalKeyComparator& icmp, - bool disjoint_sorted_files, - const std::vector& files, - const Slice* smallest_user_key, - const Slice* largest_user_key) { +bool SomeFileOverlapsRange(const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key) { const Comparator* ucmp = icmp.user_comparator(); if (!disjoint_sorted_files) { // Need to check against all files @@ -132,10 +141,11 @@ bool SomeFileOverlapsRange( // Binary search over file list uint32_t index = 0; - if (smallest_user_key != NULL) { + if (smallest_user_key != nullptr) { // Find the earliest possible internal key for smallest_user_key - InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek); - index = FindFile(icmp, files, small.Encode()); + InternalKey small_key(*smallest_user_key, kMaxSequenceNumber, + kValueTypeForSeek); + index = FindFile(icmp, files, small_key.Encode()); } if (index >= files.size()) { @@ -155,25 +165,21 @@ class Version::LevelFileNumIterator : public Iterator { public: LevelFileNumIterator(const InternalKeyComparator& icmp, const std::vector* flist) - : icmp_(icmp), - flist_(flist), - index_(flist->size()) { // Marks as invalid + : icmp_(icmp), flist_(flist), index_(flist->size()) { // Marks as invalid } - virtual bool Valid() const { - return index_ < flist_->size(); - } - virtual void Seek(const Slice& target) { + bool Valid() const override { return index_ < flist_->size(); } + void Seek(const Slice& target) override { index_ = FindFile(icmp_, *flist_, target); } - virtual void SeekToFirst() { index_ = 0; } - virtual void SeekToLast() { + void SeekToFirst() override { index_ = 0; } + void SeekToLast() override { index_ = flist_->empty() ? 0 : flist_->size() - 1; } - virtual void Next() { + void Next() override { assert(Valid()); index_++; } - virtual void Prev() { + void Prev() override { assert(Valid()); if (index_ == 0) { index_ = flist_->size(); // Marks as invalid @@ -181,17 +187,18 @@ class Version::LevelFileNumIterator : public Iterator { index_--; } } - Slice key() const { + Slice key() const override { assert(Valid()); return (*flist_)[index_]->largest.Encode(); } - Slice value() const { + Slice value() const override { assert(Valid()); EncodeFixed64(value_buf_, (*flist_)[index_]->number); - EncodeFixed64(value_buf_+8, (*flist_)[index_]->file_size); + EncodeFixed64(value_buf_ + 8, (*flist_)[index_]->file_size); return Slice(value_buf_, sizeof(value_buf_)); } - virtual Status status() const { return Status::OK(); } + Status status() const override { return Status::OK(); } + private: const InternalKeyComparator icmp_; const std::vector* const flist_; @@ -201,16 +208,14 @@ class Version::LevelFileNumIterator : public Iterator { mutable char value_buf_[16]; }; -static Iterator* GetFileIterator(void* arg, - const ReadOptions& options, +static Iterator* GetFileIterator(void* arg, const ReadOptions& options, const Slice& file_value) { TableCache* cache = reinterpret_cast(arg); if (file_value.size() != 16) { return NewErrorIterator( Status::Corruption("FileReader invoked with unexpected value")); } else { - return cache->NewIterator(options, - DecodeFixed64(file_value.data()), + return cache->NewIterator(options, DecodeFixed64(file_value.data()), DecodeFixed64(file_value.data() + 8)); } } @@ -218,17 +223,16 @@ static Iterator* GetFileIterator(void* arg, Iterator* Version::NewConcatenatingIterator(const ReadOptions& options, int level) const { return NewTwoLevelIterator( - new LevelFileNumIterator(vset_->icmp_, &files_[level]), - &GetFileIterator, vset_->table_cache_, options); + new LevelFileNumIterator(vset_->icmp_, &files_[level]), &GetFileIterator, + vset_->table_cache_, options); } void Version::AddIterators(const ReadOptions& options, std::vector* iters) { // Merge all level zero files together since they may overlap for (size_t i = 0; i < files_[0].size(); i++) { - iters->push_back( - vset_->table_cache_->NewIterator( - options, files_[0][i]->number, files_[0][i]->file_size)); + iters->push_back(vset_->table_cache_->NewIterator( + options, files_[0][i]->number, files_[0][i]->file_size)); } // For levels > 0, we can use a concatenating iterator that sequentially @@ -255,7 +259,7 @@ struct Saver { Slice user_key; std::string* value; }; -} +} // namespace static void SaveValue(void* arg, const Slice& ikey, const Slice& v) { Saver* s = reinterpret_cast(arg); ParsedInternalKey parsed_key; @@ -275,10 +279,8 @@ static bool NewestFirst(FileMetaData* a, FileMetaData* b) { return a->number > b->number; } -void Version::ForEachOverlapping(Slice user_key, Slice internal_key, - void* arg, +void Version::ForEachOverlapping(Slice user_key, Slice internal_key, void* arg, bool (*func)(void*, int, FileMetaData*)) { - // TODO(sanjay): Change Version::Get() to use this function. const Comparator* ucmp = vset_->icmp_.user_comparator(); // Search level-0 in order from newest to oldest. @@ -320,110 +322,89 @@ void Version::ForEachOverlapping(Slice user_key, Slice internal_key, } } -Status Version::Get(const ReadOptions& options, - const LookupKey& k, - std::string* value, - GetStats* stats) { - Slice ikey = k.internal_key(); - Slice user_key = k.user_key(); - const Comparator* ucmp = vset_->icmp_.user_comparator(); - Status s; - - stats->seek_file = NULL; +Status Version::Get(const ReadOptions& options, const LookupKey& k, + std::string* value, GetStats* stats) { + stats->seek_file = nullptr; stats->seek_file_level = -1; - FileMetaData* last_file_read = NULL; - int last_file_read_level = -1; - // We can search level-by-level since entries never hop across - // levels. Therefore we are guaranteed that if we find data - // in an smaller level, later levels are irrelevant. - std::vector tmp; - FileMetaData* tmp2; - for (int level = 0; level < config::kNumLevels; level++) { - size_t num_files = files_[level].size(); - if (num_files == 0) continue; + struct State { + Saver saver; + GetStats* stats; + const ReadOptions* options; + Slice ikey; + FileMetaData* last_file_read; + int last_file_read_level; - // Get the list of files to search in this level - FileMetaData* const* files = &files_[level][0]; - if (level == 0) { - // Level-0 files may overlap each other. Find all files that - // overlap user_key and process them in order from newest to oldest. - tmp.reserve(num_files); - for (uint32_t i = 0; i < num_files; i++) { - FileMetaData* f = files[i]; - if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 && - ucmp->Compare(user_key, f->largest.user_key()) <= 0) { - tmp.push_back(f); - } - } - if (tmp.empty()) continue; + VersionSet* vset; + Status s; + bool found; - std::sort(tmp.begin(), tmp.end(), NewestFirst); - files = &tmp[0]; - num_files = tmp.size(); - } else { - // Binary search to find earliest index whose largest key >= ikey. - uint32_t index = FindFile(vset_->icmp_, files_[level], ikey); - if (index >= num_files) { - files = NULL; - num_files = 0; - } else { - tmp2 = files[index]; - if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) { - // All of "tmp2" is past any data for user_key - files = NULL; - num_files = 0; - } else { - files = &tmp2; - num_files = 1; - } - } - } + static bool Match(void* arg, int level, FileMetaData* f) { + State* state = reinterpret_cast(arg); - for (uint32_t i = 0; i < num_files; ++i) { - if (last_file_read != NULL && stats->seek_file == NULL) { + if (state->stats->seek_file == nullptr && + state->last_file_read != nullptr) { // We have had more than one seek for this read. Charge the 1st file. - stats->seek_file = last_file_read; - stats->seek_file_level = last_file_read_level; + state->stats->seek_file = state->last_file_read; + state->stats->seek_file_level = state->last_file_read_level; } - FileMetaData* f = files[i]; - last_file_read = f; - last_file_read_level = level; - - Saver saver; - saver.state = kNotFound; - saver.ucmp = ucmp; - saver.user_key = user_key; - saver.value = value; - s = vset_->table_cache_->Get(options, f->number, f->file_size, - ikey, &saver, SaveValue); - if (!s.ok()) { - return s; + state->last_file_read = f; + state->last_file_read_level = level; + + state->s = state->vset->table_cache_->Get(*state->options, f->number, + f->file_size, state->ikey, + &state->saver, SaveValue); + if (!state->s.ok()) { + state->found = true; + return false; } - switch (saver.state) { + switch (state->saver.state) { case kNotFound: - break; // Keep searching in other files + return true; // Keep searching in other files case kFound: - return s; + state->found = true; + return false; case kDeleted: - s = Status::NotFound(Slice()); // Use empty error message for speed - return s; + return false; case kCorrupt: - s = Status::Corruption("corrupted key for ", user_key); - return s; + state->s = + Status::Corruption("corrupted key for ", state->saver.user_key); + state->found = true; + return false; } + + // Not reached. Added to avoid false compilation warnings of + // "control reaches end of non-void function". + return false; } - } + }; + + State state; + state.found = false; + state.stats = stats; + state.last_file_read = nullptr; + state.last_file_read_level = -1; + + state.options = &options; + state.ikey = k.internal_key(); + state.vset = vset_; - return Status::NotFound(Slice()); // Use an empty error message for speed + state.saver.state = kNotFound; + state.saver.ucmp = vset_->icmp_.user_comparator(); + state.saver.user_key = k.user_key(); + state.saver.value = value; + + ForEachOverlapping(state.saver.user_key, state.ikey, &state, &State::Match); + + return state.found ? state.s : Status::NotFound(Slice()); } bool Version::UpdateStats(const GetStats& stats) { FileMetaData* f = stats.seek_file; - if (f != NULL) { + if (f != nullptr) { f->allowed_seeks--; - if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) { + if (f->allowed_seeks <= 0 && file_to_compact_ == nullptr) { file_to_compact_ = f; file_to_compact_level_ = stats.seek_file_level; return true; @@ -470,9 +451,7 @@ bool Version::RecordReadSample(Slice internal_key) { return false; } -void Version::Ref() { - ++refs_; -} +void Version::Ref() { ++refs_; } void Version::Unref() { assert(this != &vset_->dummy_versions_); @@ -483,16 +462,14 @@ void Version::Unref() { } } -bool Version::OverlapInLevel(int level, - const Slice* smallest_user_key, +bool Version::OverlapInLevel(int level, const Slice* smallest_user_key, const Slice* largest_user_key) { return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level], smallest_user_key, largest_user_key); } -int Version::PickLevelForMemTableOutput( - const Slice& smallest_user_key, - const Slice& largest_user_key) { +int Version::PickLevelForMemTableOutput(const Slice& smallest_user_key, + const Slice& largest_user_key) { int level = 0; if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) { // Push to next level if there is no overlap in next level, @@ -508,7 +485,7 @@ int Version::PickLevelForMemTableOutput( // Check that file does not overlap too many grandparent bytes. GetOverlappingInputs(level + 2, &start, &limit, &overlaps); const int64_t sum = TotalFileSize(overlaps); - if (sum > kMaxGrandParentOverlapBytes) { + if (sum > MaxGrandParentOverlapBytes(vset_->options_)) { break; } } @@ -519,40 +496,39 @@ int Version::PickLevelForMemTableOutput( } // Store in "*inputs" all files in "level" that overlap [begin,end] -void Version::GetOverlappingInputs( - int level, - const InternalKey* begin, - const InternalKey* end, - std::vector* inputs) { +void Version::GetOverlappingInputs(int level, const InternalKey* begin, + const InternalKey* end, + std::vector* inputs) { assert(level >= 0); assert(level < config::kNumLevels); inputs->clear(); Slice user_begin, user_end; - if (begin != NULL) { + if (begin != nullptr) { user_begin = begin->user_key(); } - if (end != NULL) { + if (end != nullptr) { user_end = end->user_key(); } const Comparator* user_cmp = vset_->icmp_.user_comparator(); - for (size_t i = 0; i < files_[level].size(); ) { + for (size_t i = 0; i < files_[level].size();) { FileMetaData* f = files_[level][i++]; const Slice file_start = f->smallest.user_key(); const Slice file_limit = f->largest.user_key(); - if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) { + if (begin != nullptr && user_cmp->Compare(file_limit, user_begin) < 0) { // "f" is completely before specified range; skip it - } else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) { + } else if (end != nullptr && user_cmp->Compare(file_start, user_end) > 0) { // "f" is completely after specified range; skip it } else { inputs->push_back(f); if (level == 0) { // Level-0 files may overlap each other. So check if the newly // added file has expanded the range. If so, restart search. - if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) { + if (begin != nullptr && user_cmp->Compare(file_start, user_begin) < 0) { user_begin = file_start; inputs->clear(); i = 0; - } else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) { + } else if (end != nullptr && + user_cmp->Compare(file_limit, user_end) > 0) { user_end = file_limit; inputs->clear(); i = 0; @@ -620,9 +596,7 @@ class VersionSet::Builder { public: // Initialize a builder with the files from *base and other info from *vset - Builder(VersionSet* vset, Version* base) - : vset_(vset), - base_(base) { + Builder(VersionSet* vset, Version* base) : vset_(vset), base_(base) { base_->Ref(); BySmallestKey cmp; cmp.internal_comparator = &vset_->icmp_; @@ -636,8 +610,8 @@ class VersionSet::Builder { const FileSet* added = levels_[level].added_files; std::vector to_unref; to_unref.reserve(added->size()); - for (FileSet::const_iterator it = added->begin(); - it != added->end(); ++it) { + for (FileSet::const_iterator it = added->begin(); it != added->end(); + ++it) { to_unref.push_back(*it); } delete added; @@ -662,12 +636,9 @@ class VersionSet::Builder { } // Delete files - const VersionEdit::DeletedFileSet& del = edit->deleted_files_; - for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin(); - iter != del.end(); - ++iter) { - const int level = iter->first; - const uint64_t number = iter->second; + for (const auto& deleted_file_set_kvp : edit->deleted_files_) { + const int level = deleted_file_set_kvp.first; + const uint64_t number = deleted_file_set_kvp.second; levels_[level].deleted_files.insert(number); } @@ -690,7 +661,7 @@ class VersionSet::Builder { // same as the compaction of 40KB of data. We are a little // conservative and allow approximately one seek for every 16KB // of data before triggering a compaction. - f->allowed_seeks = (f->file_size / 16384); + f->allowed_seeks = static_cast((f->file_size / 16384U)); if (f->allowed_seeks < 100) f->allowed_seeks = 100; levels_[level].deleted_files.erase(f->number); @@ -708,20 +679,17 @@ class VersionSet::Builder { const std::vector& base_files = base_->files_[level]; std::vector::const_iterator base_iter = base_files.begin(); std::vector::const_iterator base_end = base_files.end(); - const FileSet* added = levels_[level].added_files; - v->files_[level].reserve(base_files.size() + added->size()); - for (FileSet::const_iterator added_iter = added->begin(); - added_iter != added->end(); - ++added_iter) { + const FileSet* added_files = levels_[level].added_files; + v->files_[level].reserve(base_files.size() + added_files->size()); + for (const auto& added_file : *added_files) { // Add all smaller files listed in base_ - for (std::vector::const_iterator bpos - = std::upper_bound(base_iter, base_end, *added_iter, cmp); - base_iter != bpos; - ++base_iter) { + for (std::vector::const_iterator bpos = + std::upper_bound(base_iter, base_end, added_file, cmp); + base_iter != bpos; ++base_iter) { MaybeAddFile(v, level, *base_iter); } - MaybeAddFile(v, level, *added_iter); + MaybeAddFile(v, level, added_file); } // Add remaining base files @@ -733,7 +701,7 @@ class VersionSet::Builder { // Make sure there is no overlap in levels > 0 if (level > 0) { for (uint32_t i = 1; i < v->files_[level].size(); i++) { - const InternalKey& prev_end = v->files_[level][i-1]->largest; + const InternalKey& prev_end = v->files_[level][i - 1]->largest; const InternalKey& this_begin = v->files_[level][i]->smallest; if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) { fprintf(stderr, "overlapping ranges in same level %s vs. %s\n", @@ -754,7 +722,7 @@ class VersionSet::Builder { std::vector* files = &v->files_[level]; if (level > 0 && !files->empty()) { // Must not overlap - assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest, + assert(vset_->icmp_.Compare((*files)[files->size() - 1]->largest, f->smallest) < 0); } f->refs++; @@ -763,8 +731,7 @@ class VersionSet::Builder { } }; -VersionSet::VersionSet(const std::string& dbname, - const Options* options, +VersionSet::VersionSet(const std::string& dbname, const Options* options, TableCache* table_cache, const InternalKeyComparator* cmp) : env_(options->env), @@ -777,10 +744,10 @@ VersionSet::VersionSet(const std::string& dbname, last_sequence_(0), log_number_(0), prev_log_number_(0), - descriptor_file_(NULL), - descriptor_log_(NULL), + descriptor_file_(nullptr), + descriptor_log_(nullptr), dummy_versions_(this), - current_(NULL) { + current_(nullptr) { AppendVersion(new Version(this)); } @@ -795,7 +762,7 @@ void VersionSet::AppendVersion(Version* v) { // Make "v" current assert(v->refs_ == 0); assert(v != current_); - if (current_ != NULL) { + if (current_ != nullptr) { current_->Unref(); } current_ = v; @@ -835,10 +802,10 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { // a temporary file that contains a snapshot of the current version. std::string new_manifest_file; Status s; - if (descriptor_log_ == NULL) { + if (descriptor_log_ == nullptr) { // No reason to unlock *mu here since we only hit this path in the // first call to LogAndApply (when opening the database). - assert(descriptor_file_ == NULL); + assert(descriptor_file_ == nullptr); new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_); edit->SetNextFile(next_file_number_); s = env_->NewWritableFile(new_manifest_file, &descriptor_file_); @@ -884,8 +851,8 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { if (!new_manifest_file.empty()) { delete descriptor_log_; delete descriptor_file_; - descriptor_log_ = NULL; - descriptor_file_ = NULL; + descriptor_log_ = nullptr; + descriptor_file_ = nullptr; env_->DeleteFile(new_manifest_file); } } @@ -893,10 +860,10 @@ Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) { return s; } -Status VersionSet::Recover() { +Status VersionSet::Recover(bool* save_manifest) { struct LogReporter : public log::Reader::Reporter { Status* status; - virtual void Corruption(size_t bytes, const Status& s) { + void Corruption(size_t bytes, const Status& s) override { if (this->status->ok()) *this->status = s; } }; @@ -907,7 +874,7 @@ Status VersionSet::Recover() { if (!s.ok()) { return s; } - if (current.empty() || current[current.size()-1] != '\n') { + if (current.empty() || current[current.size() - 1] != '\n') { return Status::Corruption("CURRENT file does not end with newline"); } current.resize(current.size() - 1); @@ -916,6 +883,10 @@ Status VersionSet::Recover() { SequentialFile* file; s = env_->NewSequentialFile(dscname, &file); if (!s.ok()) { + if (s.IsNotFound()) { + return Status::Corruption("CURRENT points to a non-existent file", + s.ToString()); + } return s; } @@ -932,7 +903,8 @@ Status VersionSet::Recover() { { LogReporter reporter; reporter.status = &s; - log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/); + log::Reader reader(file, &reporter, true /*checksum*/, + 0 /*initial_offset*/); Slice record; std::string scratch; while (reader.ReadRecord(&record, &scratch) && s.ok()) { @@ -973,7 +945,7 @@ Status VersionSet::Recover() { } } delete file; - file = NULL; + file = nullptr; if (s.ok()) { if (!have_next_file) { @@ -1003,11 +975,49 @@ Status VersionSet::Recover() { last_sequence_ = last_sequence; log_number_ = log_number; prev_log_number_ = prev_log_number; + + // See if we can reuse the existing MANIFEST file. + if (ReuseManifest(dscname, current)) { + // No need to save new manifest + } else { + *save_manifest = true; + } } return s; } +bool VersionSet::ReuseManifest(const std::string& dscname, + const std::string& dscbase) { + if (!options_->reuse_logs) { + return false; + } + FileType manifest_type; + uint64_t manifest_number; + uint64_t manifest_size; + if (!ParseFileName(dscbase, &manifest_number, &manifest_type) || + manifest_type != kDescriptorFile || + !env_->GetFileSize(dscname, &manifest_size).ok() || + // Make new compacted MANIFEST if old one is too big + manifest_size >= TargetFileSize(options_)) { + return false; + } + + assert(descriptor_file_ == nullptr); + assert(descriptor_log_ == nullptr); + Status r = env_->NewAppendableFile(dscname, &descriptor_file_); + if (!r.ok()) { + Log(options_->info_log, "Reuse MANIFEST: %s\n", r.ToString().c_str()); + assert(descriptor_file_ == nullptr); + return false; + } + + Log(options_->info_log, "Reusing MANIFEST %s\n", dscname.c_str()); + descriptor_log_ = new log::Writer(descriptor_file_, manifest_size); + manifest_file_number_ = manifest_number; + return true; +} + void VersionSet::MarkFileNumberUsed(uint64_t number) { if (next_file_number_ <= number) { next_file_number_ = number + 1; @@ -1019,7 +1029,7 @@ void VersionSet::Finalize(Version* v) { int best_level = -1; double best_score = -1; - for (int level = 0; level < config::kNumLevels-1; level++) { + for (int level = 0; level < config::kNumLevels - 1; level++) { double score; if (level == 0) { // We treat level-0 specially by bounding the number of files @@ -1034,11 +1044,12 @@ void VersionSet::Finalize(Version* v) { // setting, or very high compression ratios, or lots of // overwrites/deletions). score = v->files_[level].size() / - static_cast(config::kL0_CompactionTrigger); + static_cast(config::kL0_CompactionTrigger); } else { // Compute the ratio of current size to size limit. const uint64_t level_bytes = TotalFileSize(v->files_[level]); - score = static_cast(level_bytes) / MaxBytesForLevel(level); + score = + static_cast(level_bytes) / MaxBytesForLevel(options_, level); } if (score > best_score) { @@ -1089,16 +1100,12 @@ int VersionSet::NumLevelFiles(int level) const { const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const { // Update code if kNumLevels changes - assert(config::kNumLevels == 7); + static_assert(config::kNumLevels == 7, ""); snprintf(scratch->buffer, sizeof(scratch->buffer), - "files[ %d %d %d %d %d %d %d ]", - int(current_->files_[0].size()), - int(current_->files_[1].size()), - int(current_->files_[2].size()), - int(current_->files_[3].size()), - int(current_->files_[4].size()), - int(current_->files_[5].size()), - int(current_->files_[6].size())); + "files[ %d %d %d %d %d %d %d ]", int(current_->files_[0].size()), + int(current_->files_[1].size()), int(current_->files_[2].size()), + int(current_->files_[3].size()), int(current_->files_[4].size()), + int(current_->files_[5].size()), int(current_->files_[6].size())); return scratch->buffer; } @@ -1124,7 +1131,7 @@ uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { Table* tableptr; Iterator* iter = table_cache_->NewIterator( ReadOptions(), files[i]->number, files[i]->file_size, &tableptr); - if (tableptr != NULL) { + if (tableptr != nullptr) { result += tableptr->ApproximateOffsetOf(ikey.Encode()); } delete iter; @@ -1135,8 +1142,7 @@ uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) { } void VersionSet::AddLiveFiles(std::set* live) { - for (Version* v = dummy_versions_.next_; - v != &dummy_versions_; + for (Version* v = dummy_versions_.next_; v != &dummy_versions_; v = v->next_) { for (int level = 0; level < config::kNumLevels; level++) { const std::vector& files = v->files_[level]; @@ -1159,7 +1165,7 @@ int64_t VersionSet::MaxNextLevelOverlappingBytes() { for (int level = 1; level < config::kNumLevels - 1; level++) { for (size_t i = 0; i < current_->files_[level].size(); i++) { const FileMetaData* f = current_->files_[level][i]; - current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest, + current_->GetOverlappingInputs(level + 1, &f->smallest, &f->largest, &overlaps); const int64_t sum = TotalFileSize(overlaps); if (sum > result) { @@ -1174,8 +1180,7 @@ int64_t VersionSet::MaxNextLevelOverlappingBytes() { // *smallest, *largest. // REQUIRES: inputs is not empty void VersionSet::GetRange(const std::vector& inputs, - InternalKey* smallest, - InternalKey* largest) { + InternalKey* smallest, InternalKey* largest) { assert(!inputs.empty()); smallest->Clear(); largest->Clear(); @@ -1200,8 +1205,7 @@ void VersionSet::GetRange(const std::vector& inputs, // REQUIRES: inputs is not empty void VersionSet::GetRange2(const std::vector& inputs1, const std::vector& inputs2, - InternalKey* smallest, - InternalKey* largest) { + InternalKey* smallest, InternalKey* largest) { std::vector all = inputs1; all.insert(all.end(), inputs2.begin(), inputs2.end()); GetRange(all, smallest, largest); @@ -1223,8 +1227,8 @@ Iterator* VersionSet::MakeInputIterator(Compaction* c) { if (c->level() + which == 0) { const std::vector& files = c->inputs_[which]; for (size_t i = 0; i < files.size(); i++) { - list[num++] = table_cache_->NewIterator( - options, files[i]->number, files[i]->file_size); + list[num++] = table_cache_->NewIterator(options, files[i]->number, + files[i]->file_size); } } else { // Create concatenating iterator for the files from this level @@ -1247,12 +1251,12 @@ Compaction* VersionSet::PickCompaction() { // We prefer compactions triggered by too much data in a level over // the compactions triggered by seeks. const bool size_compaction = (current_->compaction_score_ >= 1); - const bool seek_compaction = (current_->file_to_compact_ != NULL); + const bool seek_compaction = (current_->file_to_compact_ != nullptr); if (size_compaction) { level = current_->compaction_level_; assert(level >= 0); - assert(level+1 < config::kNumLevels); - c = new Compaction(level); + assert(level + 1 < config::kNumLevels); + c = new Compaction(options_, level); // Pick the first file that comes after compact_pointer_[level] for (size_t i = 0; i < current_->files_[level].size(); i++) { @@ -1269,10 +1273,10 @@ Compaction* VersionSet::PickCompaction() { } } else if (seek_compaction) { level = current_->file_to_compact_level_; - c = new Compaction(level); + c = new Compaction(options_, level); c->inputs_[0].push_back(current_->file_to_compact_); } else { - return NULL; + return nullptr; } c->input_version_ = current_; @@ -1294,12 +1298,94 @@ Compaction* VersionSet::PickCompaction() { return c; } +// Finds the largest key in a vector of files. Returns true if files it not +// empty. +bool FindLargestKey(const InternalKeyComparator& icmp, + const std::vector& files, + InternalKey* largest_key) { + if (files.empty()) { + return false; + } + *largest_key = files[0]->largest; + for (size_t i = 1; i < files.size(); ++i) { + FileMetaData* f = files[i]; + if (icmp.Compare(f->largest, *largest_key) > 0) { + *largest_key = f->largest; + } + } + return true; +} + +// Finds minimum file b2=(l2, u2) in level file for which l2 > u1 and +// user_key(l2) = user_key(u1) +FileMetaData* FindSmallestBoundaryFile( + const InternalKeyComparator& icmp, + const std::vector& level_files, + const InternalKey& largest_key) { + const Comparator* user_cmp = icmp.user_comparator(); + FileMetaData* smallest_boundary_file = nullptr; + for (size_t i = 0; i < level_files.size(); ++i) { + FileMetaData* f = level_files[i]; + if (icmp.Compare(f->smallest, largest_key) > 0 && + user_cmp->Compare(f->smallest.user_key(), largest_key.user_key()) == + 0) { + if (smallest_boundary_file == nullptr || + icmp.Compare(f->smallest, smallest_boundary_file->smallest) < 0) { + smallest_boundary_file = f; + } + } + } + return smallest_boundary_file; +} + +// Extracts the largest file b1 from |compaction_files| and then searches for a +// b2 in |level_files| for which user_key(u1) = user_key(l2). If it finds such a +// file b2 (known as a boundary file) it adds it to |compaction_files| and then +// searches again using this new upper bound. +// +// If there are two blocks, b1=(l1, u1) and b2=(l2, u2) and +// user_key(u1) = user_key(l2), and if we compact b1 but not b2 then a +// subsequent get operation will yield an incorrect result because it will +// return the record from b2 in level i rather than from b1 because it searches +// level by level for records matching the supplied user key. +// +// parameters: +// in level_files: List of files to search for boundary files. +// in/out compaction_files: List of files to extend by adding boundary files. +void AddBoundaryInputs(const InternalKeyComparator& icmp, + const std::vector& level_files, + std::vector* compaction_files) { + InternalKey largest_key; + + // Quick return if compaction_files is empty. + if (!FindLargestKey(icmp, *compaction_files, &largest_key)) { + return; + } + + bool continue_searching = true; + while (continue_searching) { + FileMetaData* smallest_boundary_file = + FindSmallestBoundaryFile(icmp, level_files, largest_key); + + // If a boundary file was found advance largest_key, otherwise we're done. + if (smallest_boundary_file != NULL) { + compaction_files->push_back(smallest_boundary_file); + largest_key = smallest_boundary_file->largest; + } else { + continue_searching = false; + } + } +} + void VersionSet::SetupOtherInputs(Compaction* c) { const int level = c->level(); InternalKey smallest, largest; + + AddBoundaryInputs(icmp_, current_->files_[level], &c->inputs_[0]); GetRange(c->inputs_[0], &smallest, &largest); - current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]); + current_->GetOverlappingInputs(level + 1, &smallest, &largest, + &c->inputs_[1]); // Get entire range covered by compaction InternalKey all_start, all_limit; @@ -1310,26 +1396,24 @@ void VersionSet::SetupOtherInputs(Compaction* c) { if (!c->inputs_[1].empty()) { std::vector expanded0; current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0); + AddBoundaryInputs(icmp_, current_->files_[level], &expanded0); const int64_t inputs0_size = TotalFileSize(c->inputs_[0]); const int64_t inputs1_size = TotalFileSize(c->inputs_[1]); const int64_t expanded0_size = TotalFileSize(expanded0); if (expanded0.size() > c->inputs_[0].size() && - inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) { + inputs1_size + expanded0_size < + ExpandedCompactionByteSizeLimit(options_)) { InternalKey new_start, new_limit; GetRange(expanded0, &new_start, &new_limit); std::vector expanded1; - current_->GetOverlappingInputs(level+1, &new_start, &new_limit, + current_->GetOverlappingInputs(level + 1, &new_start, &new_limit, &expanded1); if (expanded1.size() == c->inputs_[1].size()) { Log(options_->info_log, "Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n", - level, - int(c->inputs_[0].size()), - int(c->inputs_[1].size()), - long(inputs0_size), long(inputs1_size), - int(expanded0.size()), - int(expanded1.size()), - long(expanded0_size), long(inputs1_size)); + level, int(c->inputs_[0].size()), int(c->inputs_[1].size()), + long(inputs0_size), long(inputs1_size), int(expanded0.size()), + int(expanded1.size()), long(expanded0_size), long(inputs1_size)); smallest = new_start; largest = new_limit; c->inputs_[0] = expanded0; @@ -1346,13 +1430,6 @@ void VersionSet::SetupOtherInputs(Compaction* c) { &c->grandparents_); } - if (false) { - Log(options_->info_log, "Compacting %d '%s' .. '%s'", - level, - smallest.DebugString().c_str(), - largest.DebugString().c_str()); - } - // Update the place where we will do the next compaction for this level. // We update this immediately instead of waiting for the VersionEdit // to be applied so that if the compaction fails, we will try a different @@ -1361,14 +1438,12 @@ void VersionSet::SetupOtherInputs(Compaction* c) { c->edit_.SetCompactPointer(level, largest); } -Compaction* VersionSet::CompactRange( - int level, - const InternalKey* begin, - const InternalKey* end) { +Compaction* VersionSet::CompactRange(int level, const InternalKey* begin, + const InternalKey* end) { std::vector inputs; current_->GetOverlappingInputs(level, begin, end, &inputs); if (inputs.empty()) { - return NULL; + return nullptr; } // Avoid compacting too much in one shot in case the range is large. @@ -1376,7 +1451,7 @@ Compaction* VersionSet::CompactRange( // and we must not pick one file and drop another older file if the // two files overlap. if (level > 0) { - const uint64_t limit = MaxFileSizeForLevel(level); + const uint64_t limit = MaxFileSizeForLevel(options_, level); uint64_t total = 0; for (size_t i = 0; i < inputs.size(); i++) { uint64_t s = inputs[i]->file_size; @@ -1388,7 +1463,7 @@ Compaction* VersionSet::CompactRange( } } - Compaction* c = new Compaction(level); + Compaction* c = new Compaction(options_, level); c->input_version_ = current_; c->input_version_->Ref(); c->inputs_[0] = inputs; @@ -1396,10 +1471,10 @@ Compaction* VersionSet::CompactRange( return c; } -Compaction::Compaction(int level) +Compaction::Compaction(const Options* options, int level) : level_(level), - max_output_file_size_(MaxFileSizeForLevel(level)), - input_version_(NULL), + max_output_file_size_(MaxFileSizeForLevel(options, level)), + input_version_(nullptr), grandparent_index_(0), seen_key_(false), overlapped_bytes_(0) { @@ -1409,18 +1484,19 @@ Compaction::Compaction(int level) } Compaction::~Compaction() { - if (input_version_ != NULL) { + if (input_version_ != nullptr) { input_version_->Unref(); } } bool Compaction::IsTrivialMove() const { + const VersionSet* vset = input_version_->vset_; // Avoid a move if there is lots of overlapping grandparent data. // Otherwise, the move could create a parent file that will require // a very expensive merge later on. - return (num_input_files(0) == 1 && - num_input_files(1) == 0 && - TotalFileSize(grandparents_) <= kMaxGrandParentOverlapBytes); + return (num_input_files(0) == 1 && num_input_files(1) == 0 && + TotalFileSize(grandparents_) <= + MaxGrandParentOverlapBytes(vset->options_)); } void Compaction::AddInputDeletions(VersionEdit* edit) { @@ -1436,7 +1512,7 @@ bool Compaction::IsBaseLevelForKey(const Slice& user_key) { const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator(); for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) { const std::vector& files = input_version_->files_[lvl]; - for (; level_ptrs_[lvl] < files.size(); ) { + while (level_ptrs_[lvl] < files.size()) { FileMetaData* f = files[level_ptrs_[lvl]]; if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) { // We've advanced far enough @@ -1453,11 +1529,13 @@ bool Compaction::IsBaseLevelForKey(const Slice& user_key) { } bool Compaction::ShouldStopBefore(const Slice& internal_key) { + const VersionSet* vset = input_version_->vset_; // Scan to find earliest grandparent file that contains key. - const InternalKeyComparator* icmp = &input_version_->vset_->icmp_; + const InternalKeyComparator* icmp = &vset->icmp_; while (grandparent_index_ < grandparents_.size() && - icmp->Compare(internal_key, - grandparents_[grandparent_index_]->largest.Encode()) > 0) { + icmp->Compare(internal_key, + grandparents_[grandparent_index_]->largest.Encode()) > + 0) { if (seen_key_) { overlapped_bytes_ += grandparents_[grandparent_index_]->file_size; } @@ -1465,7 +1543,7 @@ bool Compaction::ShouldStopBefore(const Slice& internal_key) { } seen_key_ = true; - if (overlapped_bytes_ > kMaxGrandParentOverlapBytes) { + if (overlapped_bytes_ > MaxGrandParentOverlapBytes(vset->options_)) { // Too much overlap for current output; start new output overlapped_bytes_ = 0; return true; @@ -1475,9 +1553,9 @@ bool Compaction::ShouldStopBefore(const Slice& internal_key) { } void Compaction::ReleaseInputs() { - if (input_version_ != NULL) { + if (input_version_ != nullptr) { input_version_->Unref(); - input_version_ = NULL; + input_version_ = nullptr; } } diff --git a/src/leveldb/db/version_set.h b/src/leveldb/db/version_set.h index f34d9dd26..69f3d7013 100644 --- a/src/leveldb/db/version_set.h +++ b/src/leveldb/db/version_set.h @@ -18,6 +18,7 @@ #include #include #include + #include "db/dbformat.h" #include "db/version_edit.h" #include "port/port.h" @@ -25,7 +26,9 @@ namespace leveldb { -namespace log { class Writer; } +namespace log { +class Writer; +} class Compaction; class Iterator; @@ -39,30 +42,23 @@ class WritableFile; // Return the smallest index i such that files[i]->largest >= key. // Return files.size() if there is no such file. // REQUIRES: "files" contains a sorted list of non-overlapping files. -extern int FindFile(const InternalKeyComparator& icmp, - const std::vector& files, - const Slice& key); +int FindFile(const InternalKeyComparator& icmp, + const std::vector& files, const Slice& key); // Returns true iff some file in "files" overlaps the user key range // [*smallest,*largest]. -// smallest==NULL represents a key smaller than all keys in the DB. -// largest==NULL represents a key largest than all keys in the DB. +// smallest==nullptr represents a key smaller than all keys in the DB. +// largest==nullptr represents a key largest than all keys in the DB. // REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges // in sorted order. -extern bool SomeFileOverlapsRange( - const InternalKeyComparator& icmp, - bool disjoint_sorted_files, - const std::vector& files, - const Slice* smallest_user_key, - const Slice* largest_user_key); +bool SomeFileOverlapsRange(const InternalKeyComparator& icmp, + bool disjoint_sorted_files, + const std::vector& files, + const Slice* smallest_user_key, + const Slice* largest_user_key); class Version { public: - // Append to *iters a sequence of iterators that will - // yield the contents of this Version when merged together. - // REQUIRES: This version has been saved (see VersionSet::SaveTo) - void AddIterators(const ReadOptions&, std::vector* iters); - // Lookup the value for key. If found, store it in *val and // return OK. Else return a non-OK status. Fills *stats. // REQUIRES: lock is not held @@ -70,6 +66,12 @@ class Version { FileMetaData* seek_file; int seek_file_level; }; + + // Append to *iters a sequence of iterators that will + // yield the contents of this Version when merged together. + // REQUIRES: This version has been saved (see VersionSet::SaveTo) + void AddIterators(const ReadOptions&, std::vector* iters); + Status Get(const ReadOptions&, const LookupKey& key, std::string* val, GetStats* stats); @@ -91,16 +93,15 @@ class Version { void GetOverlappingInputs( int level, - const InternalKey* begin, // NULL means before all keys - const InternalKey* end, // NULL means after all keys + const InternalKey* begin, // nullptr means before all keys + const InternalKey* end, // nullptr means after all keys std::vector* inputs); // Returns true iff some file in the specified level overlaps // some part of [*smallest_user_key,*largest_user_key]. - // smallest_user_key==NULL represents a key smaller than all keys in the DB. - // largest_user_key==NULL represents a key largest than all keys in the DB. - bool OverlapInLevel(int level, - const Slice* smallest_user_key, + // smallest_user_key==nullptr represents a key smaller than all the DB's keys. + // largest_user_key==nullptr represents a key largest than all the DB's keys. + bool OverlapInLevel(int level, const Slice* smallest_user_key, const Slice* largest_user_key); // Return the level at which we should place a new memtable compaction @@ -118,6 +119,22 @@ class Version { friend class VersionSet; class LevelFileNumIterator; + + explicit Version(VersionSet* vset) + : vset_(vset), + next_(this), + prev_(this), + refs_(0), + file_to_compact_(nullptr), + file_to_compact_level_(-1), + compaction_score_(-1), + compaction_level_(-1) {} + + Version(const Version&) = delete; + Version& operator=(const Version&) = delete; + + ~Version(); + Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const; // Call func(arg, level, f) for every file that overlaps user_key in @@ -125,14 +142,13 @@ class Version { // false, makes no more calls. // // REQUIRES: user portion of internal_key == user_key. - void ForEachOverlapping(Slice user_key, Slice internal_key, - void* arg, + void ForEachOverlapping(Slice user_key, Slice internal_key, void* arg, bool (*func)(void*, int, FileMetaData*)); - VersionSet* vset_; // VersionSet to which this Version belongs - Version* next_; // Next version in linked list - Version* prev_; // Previous version in linked list - int refs_; // Number of live refs to this version + VersionSet* vset_; // VersionSet to which this Version belongs + Version* next_; // Next version in linked list + Version* prev_; // Previous version in linked list + int refs_; // Number of live refs to this version // List of files per level std::vector files_[config::kNumLevels]; @@ -146,28 +162,15 @@ class Version { // are initialized by Finalize(). double compaction_score_; int compaction_level_; - - explicit Version(VersionSet* vset) - : vset_(vset), next_(this), prev_(this), refs_(0), - file_to_compact_(NULL), - file_to_compact_level_(-1), - compaction_score_(-1), - compaction_level_(-1) { - } - - ~Version(); - - // No copying allowed - Version(const Version&); - void operator=(const Version&); }; class VersionSet { public: - VersionSet(const std::string& dbname, - const Options* options, - TableCache* table_cache, - const InternalKeyComparator*); + VersionSet(const std::string& dbname, const Options* options, + TableCache* table_cache, const InternalKeyComparator*); + VersionSet(const VersionSet&) = delete; + VersionSet& operator=(const VersionSet&) = delete; + ~VersionSet(); // Apply *edit to the current version to form a new descriptor that @@ -179,7 +182,7 @@ class VersionSet { EXCLUSIVE_LOCKS_REQUIRED(mu); // Recover the last saved descriptor from persistent storage. - Status Recover(); + Status Recover(bool* save_manifest); // Return the current version. Version* current() const { return current_; } @@ -225,19 +228,17 @@ class VersionSet { uint64_t PrevLogNumber() const { return prev_log_number_; } // Pick level and inputs for a new compaction. - // Returns NULL if there is no compaction to be done. + // Returns nullptr if there is no compaction to be done. // Otherwise returns a pointer to a heap-allocated object that // describes the compaction. Caller should delete the result. Compaction* PickCompaction(); // Return a compaction object for compacting the range [begin,end] in - // the specified level. Returns NULL if there is nothing in that + // the specified level. Returns nullptr if there is nothing in that // level that overlaps the specified range. Caller should delete // the result. - Compaction* CompactRange( - int level, - const InternalKey* begin, - const InternalKey* end); + Compaction* CompactRange(int level, const InternalKey* begin, + const InternalKey* end); // Return the maximum overlapping data (in bytes) at next level for any // file at a level >= 1. @@ -250,7 +251,7 @@ class VersionSet { // Returns true iff some level needs a compaction. bool NeedsCompaction() const { Version* v = current_; - return (v->compaction_score_ >= 1) || (v->file_to_compact_ != NULL); + return (v->compaction_score_ >= 1) || (v->file_to_compact_ != nullptr); } // Add all files listed in any live version to *live. @@ -274,16 +275,16 @@ class VersionSet { friend class Compaction; friend class Version; + bool ReuseManifest(const std::string& dscname, const std::string& dscbase); + void Finalize(Version* v); - void GetRange(const std::vector& inputs, - InternalKey* smallest, + void GetRange(const std::vector& inputs, InternalKey* smallest, InternalKey* largest); void GetRange2(const std::vector& inputs1, const std::vector& inputs2, - InternalKey* smallest, - InternalKey* largest); + InternalKey* smallest, InternalKey* largest); void SetupOtherInputs(Compaction* c); @@ -312,10 +313,6 @@ class VersionSet { // Per-level key at which the next compaction at that level should start. // Either an empty string, or a valid InternalKey. std::string compact_pointer_[config::kNumLevels]; - - // No copying allowed - VersionSet(const VersionSet&); - void operator=(const VersionSet&); }; // A Compaction encapsulates information about a compaction. @@ -364,7 +361,7 @@ class Compaction { friend class Version; friend class VersionSet; - explicit Compaction(int level); + Compaction(const Options* options, int level); int level_; uint64_t max_output_file_size_; @@ -372,7 +369,7 @@ class Compaction { VersionEdit edit_; // Each compaction reads inputs from "level_" and "level_+1" - std::vector inputs_[2]; // The two sets of inputs + std::vector inputs_[2]; // The two sets of inputs // State used to check for number of overlapping grandparent files // (parent == level_ + 1, grandparent == level_ + 2) diff --git a/src/leveldb/db/version_set_test.cc b/src/leveldb/db/version_set_test.cc index 501e34d13..c1056a1e7 100644 --- a/src/leveldb/db/version_set_test.cc +++ b/src/leveldb/db/version_set_test.cc @@ -11,10 +11,7 @@ namespace leveldb { class FindFileTest { public: - std::vector files_; - bool disjoint_sorted_files_; - - FindFileTest() : disjoint_sorted_files_(true) { } + FindFileTest() : disjoint_sorted_files_(true) {} ~FindFileTest() { for (int i = 0; i < files_.size(); i++) { @@ -40,20 +37,25 @@ class FindFileTest { bool Overlaps(const char* smallest, const char* largest) { InternalKeyComparator cmp(BytewiseComparator()); - Slice s(smallest != NULL ? smallest : ""); - Slice l(largest != NULL ? largest : ""); + Slice s(smallest != nullptr ? smallest : ""); + Slice l(largest != nullptr ? largest : ""); return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_, - (smallest != NULL ? &s : NULL), - (largest != NULL ? &l : NULL)); + (smallest != nullptr ? &s : nullptr), + (largest != nullptr ? &l : nullptr)); } + + bool disjoint_sorted_files_; + + private: + std::vector files_; }; TEST(FindFileTest, Empty) { ASSERT_EQ(0, Find("foo")); - ASSERT_TRUE(! Overlaps("a", "z")); - ASSERT_TRUE(! Overlaps(NULL, "z")); - ASSERT_TRUE(! Overlaps("a", NULL)); - ASSERT_TRUE(! Overlaps(NULL, NULL)); + ASSERT_TRUE(!Overlaps("a", "z")); + ASSERT_TRUE(!Overlaps(nullptr, "z")); + ASSERT_TRUE(!Overlaps("a", nullptr)); + ASSERT_TRUE(!Overlaps(nullptr, nullptr)); } TEST(FindFileTest, Single) { @@ -65,8 +67,8 @@ TEST(FindFileTest, Single) { ASSERT_EQ(1, Find("q1")); ASSERT_EQ(1, Find("z")); - ASSERT_TRUE(! Overlaps("a", "b")); - ASSERT_TRUE(! Overlaps("z1", "z2")); + ASSERT_TRUE(!Overlaps("a", "b")); + ASSERT_TRUE(!Overlaps("z1", "z2")); ASSERT_TRUE(Overlaps("a", "p")); ASSERT_TRUE(Overlaps("a", "q")); ASSERT_TRUE(Overlaps("a", "z")); @@ -78,15 +80,14 @@ TEST(FindFileTest, Single) { ASSERT_TRUE(Overlaps("q", "q")); ASSERT_TRUE(Overlaps("q", "q1")); - ASSERT_TRUE(! Overlaps(NULL, "j")); - ASSERT_TRUE(! Overlaps("r", NULL)); - ASSERT_TRUE(Overlaps(NULL, "p")); - ASSERT_TRUE(Overlaps(NULL, "p1")); - ASSERT_TRUE(Overlaps("q", NULL)); - ASSERT_TRUE(Overlaps(NULL, NULL)); + ASSERT_TRUE(!Overlaps(nullptr, "j")); + ASSERT_TRUE(!Overlaps("r", nullptr)); + ASSERT_TRUE(Overlaps(nullptr, "p")); + ASSERT_TRUE(Overlaps(nullptr, "p1")); + ASSERT_TRUE(Overlaps("q", nullptr)); + ASSERT_TRUE(Overlaps(nullptr, nullptr)); } - TEST(FindFileTest, Multiple) { Add("150", "200"); Add("200", "250"); @@ -110,10 +111,10 @@ TEST(FindFileTest, Multiple) { ASSERT_EQ(3, Find("450")); ASSERT_EQ(4, Find("451")); - ASSERT_TRUE(! Overlaps("100", "149")); - ASSERT_TRUE(! Overlaps("251", "299")); - ASSERT_TRUE(! Overlaps("451", "500")); - ASSERT_TRUE(! Overlaps("351", "399")); + ASSERT_TRUE(!Overlaps("100", "149")); + ASSERT_TRUE(!Overlaps("251", "299")); + ASSERT_TRUE(!Overlaps("451", "500")); + ASSERT_TRUE(!Overlaps("351", "399")); ASSERT_TRUE(Overlaps("100", "150")); ASSERT_TRUE(Overlaps("100", "200")); @@ -130,25 +131,25 @@ TEST(FindFileTest, MultipleNullBoundaries) { Add("200", "250"); Add("300", "350"); Add("400", "450"); - ASSERT_TRUE(! Overlaps(NULL, "149")); - ASSERT_TRUE(! Overlaps("451", NULL)); - ASSERT_TRUE(Overlaps(NULL, NULL)); - ASSERT_TRUE(Overlaps(NULL, "150")); - ASSERT_TRUE(Overlaps(NULL, "199")); - ASSERT_TRUE(Overlaps(NULL, "200")); - ASSERT_TRUE(Overlaps(NULL, "201")); - ASSERT_TRUE(Overlaps(NULL, "400")); - ASSERT_TRUE(Overlaps(NULL, "800")); - ASSERT_TRUE(Overlaps("100", NULL)); - ASSERT_TRUE(Overlaps("200", NULL)); - ASSERT_TRUE(Overlaps("449", NULL)); - ASSERT_TRUE(Overlaps("450", NULL)); + ASSERT_TRUE(!Overlaps(nullptr, "149")); + ASSERT_TRUE(!Overlaps("451", nullptr)); + ASSERT_TRUE(Overlaps(nullptr, nullptr)); + ASSERT_TRUE(Overlaps(nullptr, "150")); + ASSERT_TRUE(Overlaps(nullptr, "199")); + ASSERT_TRUE(Overlaps(nullptr, "200")); + ASSERT_TRUE(Overlaps(nullptr, "201")); + ASSERT_TRUE(Overlaps(nullptr, "400")); + ASSERT_TRUE(Overlaps(nullptr, "800")); + ASSERT_TRUE(Overlaps("100", nullptr)); + ASSERT_TRUE(Overlaps("200", nullptr)); + ASSERT_TRUE(Overlaps("449", nullptr)); + ASSERT_TRUE(Overlaps("450", nullptr)); } TEST(FindFileTest, OverlapSequenceChecks) { Add("200", "200", 5000, 3000); - ASSERT_TRUE(! Overlaps("199", "199")); - ASSERT_TRUE(! Overlaps("201", "300")); + ASSERT_TRUE(!Overlaps("199", "199")); + ASSERT_TRUE(!Overlaps("201", "300")); ASSERT_TRUE(Overlaps("200", "200")); ASSERT_TRUE(Overlaps("190", "200")); ASSERT_TRUE(Overlaps("200", "210")); @@ -158,8 +159,8 @@ TEST(FindFileTest, OverlappingFiles) { Add("150", "600"); Add("400", "500"); disjoint_sorted_files_ = false; - ASSERT_TRUE(! Overlaps("100", "149")); - ASSERT_TRUE(! Overlaps("601", "700")); + ASSERT_TRUE(!Overlaps("100", "149")); + ASSERT_TRUE(!Overlaps("601", "700")); ASSERT_TRUE(Overlaps("100", "150")); ASSERT_TRUE(Overlaps("100", "200")); ASSERT_TRUE(Overlaps("100", "300")); @@ -172,8 +173,160 @@ TEST(FindFileTest, OverlappingFiles) { ASSERT_TRUE(Overlaps("600", "700")); } -} // namespace leveldb +void AddBoundaryInputs(const InternalKeyComparator& icmp, + const std::vector& level_files, + std::vector* compaction_files); + +class AddBoundaryInputsTest { + public: + std::vector level_files_; + std::vector compaction_files_; + std::vector all_files_; + InternalKeyComparator icmp_; + + AddBoundaryInputsTest() : icmp_(BytewiseComparator()) {} + + ~AddBoundaryInputsTest() { + for (size_t i = 0; i < all_files_.size(); ++i) { + delete all_files_[i]; + } + all_files_.clear(); + } + + FileMetaData* CreateFileMetaData(uint64_t number, InternalKey smallest, + InternalKey largest) { + FileMetaData* f = new FileMetaData(); + f->number = number; + f->smallest = smallest; + f->largest = largest; + all_files_.push_back(f); + return f; + } +}; + +TEST(AddBoundaryInputsTest, TestEmptyFileSets) { + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_TRUE(compaction_files_.empty()); + ASSERT_TRUE(level_files_.empty()); +} + +TEST(AddBoundaryInputsTest, TestEmptyLevelFiles) { + FileMetaData* f1 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("100", 1, kTypeValue))); + compaction_files_.push_back(f1); + + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(1, compaction_files_.size()); + ASSERT_EQ(f1, compaction_files_[0]); + ASSERT_TRUE(level_files_.empty()); +} + +TEST(AddBoundaryInputsTest, TestEmptyCompactionFiles) { + FileMetaData* f1 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("100", 1, kTypeValue))); + level_files_.push_back(f1); + + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_TRUE(compaction_files_.empty()); + ASSERT_EQ(1, level_files_.size()); + ASSERT_EQ(f1, level_files_[0]); +} + +TEST(AddBoundaryInputsTest, TestNoBoundaryFiles) { + FileMetaData* f1 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("100", 1, kTypeValue))); + FileMetaData* f2 = + CreateFileMetaData(1, InternalKey("200", 2, kTypeValue), + InternalKey(InternalKey("200", 1, kTypeValue))); + FileMetaData* f3 = + CreateFileMetaData(1, InternalKey("300", 2, kTypeValue), + InternalKey(InternalKey("300", 1, kTypeValue))); + + level_files_.push_back(f3); + level_files_.push_back(f2); + level_files_.push_back(f1); + compaction_files_.push_back(f2); + compaction_files_.push_back(f3); + + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(2, compaction_files_.size()); +} + +TEST(AddBoundaryInputsTest, TestOneBoundaryFiles) { + FileMetaData* f1 = + CreateFileMetaData(1, InternalKey("100", 3, kTypeValue), + InternalKey(InternalKey("100", 2, kTypeValue))); + FileMetaData* f2 = + CreateFileMetaData(1, InternalKey("100", 1, kTypeValue), + InternalKey(InternalKey("200", 3, kTypeValue))); + FileMetaData* f3 = + CreateFileMetaData(1, InternalKey("300", 2, kTypeValue), + InternalKey(InternalKey("300", 1, kTypeValue))); + + level_files_.push_back(f3); + level_files_.push_back(f2); + level_files_.push_back(f1); + compaction_files_.push_back(f1); + + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(2, compaction_files_.size()); + ASSERT_EQ(f1, compaction_files_[0]); + ASSERT_EQ(f2, compaction_files_[1]); +} + +TEST(AddBoundaryInputsTest, TestTwoBoundaryFiles) { + FileMetaData* f1 = + CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), + InternalKey(InternalKey("100", 5, kTypeValue))); + FileMetaData* f2 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("300", 1, kTypeValue))); + FileMetaData* f3 = + CreateFileMetaData(1, InternalKey("100", 4, kTypeValue), + InternalKey(InternalKey("100", 3, kTypeValue))); + + level_files_.push_back(f2); + level_files_.push_back(f3); + level_files_.push_back(f1); + compaction_files_.push_back(f1); -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(3, compaction_files_.size()); + ASSERT_EQ(f1, compaction_files_[0]); + ASSERT_EQ(f3, compaction_files_[1]); + ASSERT_EQ(f2, compaction_files_[2]); } + +TEST(AddBoundaryInputsTest, TestDisjoinFilePointers) { + FileMetaData* f1 = + CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), + InternalKey(InternalKey("100", 5, kTypeValue))); + FileMetaData* f2 = + CreateFileMetaData(1, InternalKey("100", 6, kTypeValue), + InternalKey(InternalKey("100", 5, kTypeValue))); + FileMetaData* f3 = + CreateFileMetaData(1, InternalKey("100", 2, kTypeValue), + InternalKey(InternalKey("300", 1, kTypeValue))); + FileMetaData* f4 = + CreateFileMetaData(1, InternalKey("100", 4, kTypeValue), + InternalKey(InternalKey("100", 3, kTypeValue))); + + level_files_.push_back(f2); + level_files_.push_back(f3); + level_files_.push_back(f4); + + compaction_files_.push_back(f1); + + AddBoundaryInputs(icmp_, level_files_, &compaction_files_); + ASSERT_EQ(3, compaction_files_.size()); + ASSERT_EQ(f1, compaction_files_[0]); + ASSERT_EQ(f4, compaction_files_[1]); + ASSERT_EQ(f3, compaction_files_[2]); +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/db/write_batch.cc b/src/leveldb/db/write_batch.cc index 33f4a4257..b54313c35 100644 --- a/src/leveldb/db/write_batch.cc +++ b/src/leveldb/db/write_batch.cc @@ -15,10 +15,10 @@ #include "leveldb/write_batch.h" -#include "leveldb/db.h" #include "db/dbformat.h" #include "db/memtable.h" #include "db/write_batch_internal.h" +#include "leveldb/db.h" #include "util/coding.h" namespace leveldb { @@ -26,19 +26,19 @@ namespace leveldb { // WriteBatch header has an 8-byte sequence number followed by a 4-byte count. static const size_t kHeader = 12; -WriteBatch::WriteBatch() { - Clear(); -} +WriteBatch::WriteBatch() { Clear(); } -WriteBatch::~WriteBatch() { } +WriteBatch::~WriteBatch() = default; -WriteBatch::Handler::~Handler() { } +WriteBatch::Handler::~Handler() = default; void WriteBatch::Clear() { rep_.clear(); rep_.resize(kHeader); } +size_t WriteBatch::ApproximateSize() const { return rep_.size(); } + Status WriteBatch::Iterate(Handler* handler) const { Slice input(rep_); if (input.size() < kHeader) { @@ -108,25 +108,28 @@ void WriteBatch::Delete(const Slice& key) { PutLengthPrefixedSlice(&rep_, key); } +void WriteBatch::Append(const WriteBatch& source) { + WriteBatchInternal::Append(this, &source); +} + namespace { class MemTableInserter : public WriteBatch::Handler { public: SequenceNumber sequence_; MemTable* mem_; - virtual void Put(const Slice& key, const Slice& value) { + void Put(const Slice& key, const Slice& value) override { mem_->Add(sequence_, kTypeValue, key, value); sequence_++; } - virtual void Delete(const Slice& key) { + void Delete(const Slice& key) override { mem_->Add(sequence_, kTypeDeletion, key, Slice()); sequence_++; } }; } // namespace -Status WriteBatchInternal::InsertInto(const WriteBatch* b, - MemTable* memtable) { +Status WriteBatchInternal::InsertInto(const WriteBatch* b, MemTable* memtable) { MemTableInserter inserter; inserter.sequence_ = WriteBatchInternal::Sequence(b); inserter.mem_ = memtable; diff --git a/src/leveldb/db/write_batch_internal.h b/src/leveldb/db/write_batch_internal.h index 310a3c891..fce86e3f1 100644 --- a/src/leveldb/db/write_batch_internal.h +++ b/src/leveldb/db/write_batch_internal.h @@ -5,6 +5,7 @@ #ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ #define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ +#include "db/dbformat.h" #include "leveldb/write_batch.h" namespace leveldb { @@ -28,13 +29,9 @@ class WriteBatchInternal { // this batch. static void SetSequence(WriteBatch* batch, SequenceNumber seq); - static Slice Contents(const WriteBatch* batch) { - return Slice(batch->rep_); - } + static Slice Contents(const WriteBatch* batch) { return Slice(batch->rep_); } - static size_t ByteSize(const WriteBatch* batch) { - return batch->rep_.size(); - } + static size_t ByteSize(const WriteBatch* batch) { return batch->rep_.size(); } static void SetContents(WriteBatch* batch, const Slice& contents); @@ -45,5 +42,4 @@ class WriteBatchInternal { } // namespace leveldb - #endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_ diff --git a/src/leveldb/db/write_batch_test.cc b/src/leveldb/db/write_batch_test.cc index 9064e3d85..c32317fb5 100644 --- a/src/leveldb/db/write_batch_test.cc +++ b/src/leveldb/db/write_batch_test.cc @@ -52,7 +52,7 @@ static std::string PrintContents(WriteBatch* b) { return state; } -class WriteBatchTest { }; +class WriteBatchTest {}; TEST(WriteBatchTest, Empty) { WriteBatch batch; @@ -68,10 +68,11 @@ TEST(WriteBatchTest, Multiple) { WriteBatchInternal::SetSequence(&batch, 100); ASSERT_EQ(100, WriteBatchInternal::Sequence(&batch)); ASSERT_EQ(3, WriteBatchInternal::Count(&batch)); - ASSERT_EQ("Put(baz, boo)@102" - "Delete(box)@101" - "Put(foo, bar)@100", - PrintContents(&batch)); + ASSERT_EQ( + "Put(baz, boo)@102" + "Delete(box)@101" + "Put(foo, bar)@100", + PrintContents(&batch)); } TEST(WriteBatchTest, Corruption) { @@ -81,40 +82,56 @@ TEST(WriteBatchTest, Corruption) { WriteBatchInternal::SetSequence(&batch, 200); Slice contents = WriteBatchInternal::Contents(&batch); WriteBatchInternal::SetContents(&batch, - Slice(contents.data(),contents.size()-1)); - ASSERT_EQ("Put(foo, bar)@200" - "ParseError()", - PrintContents(&batch)); + Slice(contents.data(), contents.size() - 1)); + ASSERT_EQ( + "Put(foo, bar)@200" + "ParseError()", + PrintContents(&batch)); } TEST(WriteBatchTest, Append) { WriteBatch b1, b2; WriteBatchInternal::SetSequence(&b1, 200); WriteBatchInternal::SetSequence(&b2, 300); - WriteBatchInternal::Append(&b1, &b2); - ASSERT_EQ("", - PrintContents(&b1)); + b1.Append(b2); + ASSERT_EQ("", PrintContents(&b1)); b2.Put("a", "va"); - WriteBatchInternal::Append(&b1, &b2); - ASSERT_EQ("Put(a, va)@200", - PrintContents(&b1)); + b1.Append(b2); + ASSERT_EQ("Put(a, va)@200", PrintContents(&b1)); b2.Clear(); b2.Put("b", "vb"); - WriteBatchInternal::Append(&b1, &b2); - ASSERT_EQ("Put(a, va)@200" - "Put(b, vb)@201", - PrintContents(&b1)); + b1.Append(b2); + ASSERT_EQ( + "Put(a, va)@200" + "Put(b, vb)@201", + PrintContents(&b1)); b2.Delete("foo"); - WriteBatchInternal::Append(&b1, &b2); - ASSERT_EQ("Put(a, va)@200" - "Put(b, vb)@202" - "Put(b, vb)@201" - "Delete(foo)@203", - PrintContents(&b1)); + b1.Append(b2); + ASSERT_EQ( + "Put(a, va)@200" + "Put(b, vb)@202" + "Put(b, vb)@201" + "Delete(foo)@203", + PrintContents(&b1)); } -} // namespace leveldb +TEST(WriteBatchTest, ApproximateSize) { + WriteBatch batch; + size_t empty_size = batch.ApproximateSize(); + + batch.Put(Slice("foo"), Slice("bar")); + size_t one_key_size = batch.ApproximateSize(); + ASSERT_LT(empty_size, one_key_size); + + batch.Put(Slice("baz"), Slice("boo")); + size_t two_keys_size = batch.ApproximateSize(); + ASSERT_LT(one_key_size, two_keys_size); -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); + batch.Delete(Slice("box")); + size_t post_delete_size = batch.ApproximateSize(); + ASSERT_LT(two_keys_size, post_delete_size); } + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/doc/benchmark.html b/src/leveldb/doc/benchmark.html index c4639772c..f3fd77144 100644 --- a/src/leveldb/doc/benchmark.html +++ b/src/leveldb/doc/benchmark.html @@ -90,9 +90,9 @@

LevelDB Benchmarks

Benchmark Source Code

We wrote benchmark tools for SQLite and Kyoto TreeDB based on LevelDB's db_bench. The code for each of the benchmarks resides here:

Custom Build Specifications

diff --git a/src/leveldb/doc/doc.css b/src/leveldb/doc/doc.css deleted file mode 100644 index 700c564e4..000000000 --- a/src/leveldb/doc/doc.css +++ /dev/null @@ -1,89 +0,0 @@ -body { - margin-left: 0.5in; - margin-right: 0.5in; - background: white; - color: black; -} - -h1 { - margin-left: -0.2in; - font-size: 14pt; -} -h2 { - margin-left: -0in; - font-size: 12pt; -} -h3 { - margin-left: -0in; -} -h4 { - margin-left: -0in; -} -hr { - margin-left: -0in; -} - -/* Definition lists: definition term bold */ -dt { - font-weight: bold; -} - -address { - text-align: center; -} -code,samp,var { - color: blue; -} -kbd { - color: #600000; -} -div.note p { - float: right; - width: 3in; - margin-right: 0%; - padding: 1px; - border: 2px solid #6060a0; - background-color: #fffff0; -} - -ul { - margin-top: -0em; - margin-bottom: -0em; -} - -ol { - margin-top: -0em; - margin-bottom: -0em; -} - -UL.nobullets { - list-style-type: none; - list-style-image: none; - margin-left: -1em; -} - -p { - margin: 1em 0 1em 0; - padding: 0 0 0 0; -} - -pre { - line-height: 1.3em; - padding: 0.4em 0 0.8em 0; - margin: 0 0 0 0; - border: 0 0 0 0; - color: blue; -} - -.datatable { - margin-left: auto; - margin-right: auto; - margin-top: 2em; - margin-bottom: 2em; - border: 1px solid; -} - -.datatable td,th { - padding: 0 0.5em 0 0.5em; - text-align: right; -} diff --git a/src/leveldb/doc/impl.html b/src/leveldb/doc/impl.html deleted file mode 100644 index 6a468be09..000000000 --- a/src/leveldb/doc/impl.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - -Leveldb file layout and compactions - - - - -

Files

- -The implementation of leveldb is similar in spirit to the -representation of a single - -Bigtable tablet (section 5.3). -However the organization of the files that make up the representation -is somewhat different and is explained below. - -

-Each database is represented by a set of files stored in a directory. -There are several different types of files as documented below: -

-

Log files

-

-A log file (*.log) stores a sequence of recent updates. Each update -is appended to the current log file. When the log file reaches a -pre-determined size (approximately 4MB by default), it is converted -to a sorted table (see below) and a new log file is created for future -updates. -

-A copy of the current log file is kept in an in-memory structure (the -memtable). This copy is consulted on every read so that read -operations reflect all logged updates. -

-

Sorted tables

-

-A sorted table (*.sst) stores a sequence of entries sorted by key. -Each entry is either a value for the key, or a deletion marker for the -key. (Deletion markers are kept around to hide obsolete values -present in older sorted tables). -

-The set of sorted tables are organized into a sequence of levels. The -sorted table generated from a log file is placed in a special young -level (also called level-0). When the number of young files exceeds a -certain threshold (currently four), all of the young files are merged -together with all of the overlapping level-1 files to produce a -sequence of new level-1 files (we create a new level-1 file for every -2MB of data.) -

-Files in the young level may contain overlapping keys. However files -in other levels have distinct non-overlapping key ranges. Consider -level number L where L >= 1. When the combined size of files in -level-L exceeds (10^L) MB (i.e., 10MB for level-1, 100MB for level-2, -...), one file in level-L, and all of the overlapping files in -level-(L+1) are merged to form a set of new files for level-(L+1). -These merges have the effect of gradually migrating new updates from -the young level to the largest level using only bulk reads and writes -(i.e., minimizing expensive seeks). - -

Manifest

-

-A MANIFEST file lists the set of sorted tables that make up each -level, the corresponding key ranges, and other important metadata. -A new MANIFEST file (with a new number embedded in the file name) -is created whenever the database is reopened. The MANIFEST file is -formatted as a log, and changes made to the serving state (as files -are added or removed) are appended to this log. -

-

Current

-

-CURRENT is a simple text file that contains the name of the latest -MANIFEST file. -

-

Info logs

-

-Informational messages are printed to files named LOG and LOG.old. -

-

Others

-

-Other files used for miscellaneous purposes may also be present -(LOCK, *.dbtmp). - -

Level 0

-When the log file grows above a certain size (1MB by default): -
    -
  • Create a brand new memtable and log file and direct future updates here -
  • In the background: -
      -
    • Write the contents of the previous memtable to an sstable -
    • Discard the memtable -
    • Delete the old log file and the old memtable -
    • Add the new sstable to the young (level-0) level. -
    -
- -

Compactions

- -

-When the size of level L exceeds its limit, we compact it in a -background thread. The compaction picks a file from level L and all -overlapping files from the next level L+1. Note that if a level-L -file overlaps only part of a level-(L+1) file, the entire file at -level-(L+1) is used as an input to the compaction and will be -discarded after the compaction. Aside: because level-0 is special -(files in it may overlap each other), we treat compactions from -level-0 to level-1 specially: a level-0 compaction may pick more than -one level-0 file in case some of these files overlap each other. - -

-A compaction merges the contents of the picked files to produce a -sequence of level-(L+1) files. We switch to producing a new -level-(L+1) file after the current output file has reached the target -file size (2MB). We also switch to a new output file when the key -range of the current output file has grown enough to overlap more than -ten level-(L+2) files. This last rule ensures that a later compaction -of a level-(L+1) file will not pick up too much data from level-(L+2). - -

-The old files are discarded and the new files are added to the serving -state. - -

-Compactions for a particular level rotate through the key space. In -more detail, for each level L, we remember the ending key of the last -compaction at level L. The next compaction for level L will pick the -first file that starts after this key (wrapping around to the -beginning of the key space if there is no such file). - -

-Compactions drop overwritten values. They also drop deletion markers -if there are no higher numbered levels that contain a file whose range -overlaps the current key. - -

Timing

- -Level-0 compactions will read up to four 1MB files from level-0, and -at worst all the level-1 files (10MB). I.e., we will read 14MB and -write 14MB. - -

-Other than the special level-0 compactions, we will pick one 2MB file -from level L. In the worst case, this will overlap ~ 12 files from -level L+1 (10 because level-(L+1) is ten times the size of level-L, -and another two at the boundaries since the file ranges at level-L -will usually not be aligned with the file ranges at level-L+1). The -compaction will therefore read 26MB and write 26MB. Assuming a disk -IO rate of 100MB/s (ballpark range for modern drives), the worst -compaction cost will be approximately 0.5 second. - -

-If we throttle the background writing to something small, say 10% of -the full 100MB/s speed, a compaction may take up to 5 seconds. If the -user is writing at 10MB/s, we might build up lots of level-0 files -(~50 to hold the 5*10MB). This may significantly increase the cost of -reads due to the overhead of merging more files together on every -read. - -

-Solution 1: To reduce this problem, we might want to increase the log -switching threshold when the number of level-0 files is large. Though -the downside is that the larger this threshold, the more memory we will -need to hold the corresponding memtable. - -

-Solution 2: We might want to decrease write rate artificially when the -number of level-0 files goes up. - -

-Solution 3: We work on reducing the cost of very wide merges. -Perhaps most of the level-0 files will have their blocks sitting -uncompressed in the cache and we will only need to worry about the -O(N) complexity in the merging iterator. - -

Number of files

- -Instead of always making 2MB files, we could make larger files for -larger levels to reduce the total file count, though at the expense of -more bursty compactions. Alternatively, we could shard the set of -files into multiple directories. - -

-An experiment on an ext3 filesystem on Feb 04, 2011 shows -the following timings to do 100K file opens in directories with -varying number of files: - - - - - -
Files in directoryMicroseconds to open a file
10009
1000010
10000016
-So maybe even the sharding is not necessary on modern filesystems? - -

Recovery

- -
    -
  • Read CURRENT to find name of the latest committed MANIFEST -
  • Read the named MANIFEST file -
  • Clean up stale files -
  • We could open all sstables here, but it is probably better to be lazy... -
  • Convert log chunk to a new level-0 sstable -
  • Start directing new writes to a new log file with recovered sequence# -
- -

Garbage collection of files

- -DeleteObsoleteFiles() is called at the end of every -compaction and at the end of recovery. It finds the names of all -files in the database. It deletes all log files that are not the -current log file. It deletes all table files that are not referenced -from some level and are not the output of an active compaction. - - - diff --git a/src/leveldb/doc/impl.md b/src/leveldb/doc/impl.md new file mode 100644 index 000000000..cacabb96f --- /dev/null +++ b/src/leveldb/doc/impl.md @@ -0,0 +1,172 @@ +## Files + +The implementation of leveldb is similar in spirit to the representation of a +single [Bigtable tablet (section 5.3)](http://research.google.com/archive/bigtable.html). +However the organization of the files that make up the representation is +somewhat different and is explained below. + +Each database is represented by a set of files stored in a directory. There are +several different types of files as documented below: + +### Log files + +A log file (*.log) stores a sequence of recent updates. Each update is appended +to the current log file. When the log file reaches a pre-determined size +(approximately 4MB by default), it is converted to a sorted table (see below) +and a new log file is created for future updates. + +A copy of the current log file is kept in an in-memory structure (the +`memtable`). This copy is consulted on every read so that read operations +reflect all logged updates. + +## Sorted tables + +A sorted table (*.ldb) stores a sequence of entries sorted by key. Each entry is +either a value for the key, or a deletion marker for the key. (Deletion markers +are kept around to hide obsolete values present in older sorted tables). + +The set of sorted tables are organized into a sequence of levels. The sorted +table generated from a log file is placed in a special **young** level (also +called level-0). When the number of young files exceeds a certain threshold +(currently four), all of the young files are merged together with all of the +overlapping level-1 files to produce a sequence of new level-1 files (we create +a new level-1 file for every 2MB of data.) + +Files in the young level may contain overlapping keys. However files in other +levels have distinct non-overlapping key ranges. Consider level number L where +L >= 1. When the combined size of files in level-L exceeds (10^L) MB (i.e., 10MB +for level-1, 100MB for level-2, ...), one file in level-L, and all of the +overlapping files in level-(L+1) are merged to form a set of new files for +level-(L+1). These merges have the effect of gradually migrating new updates +from the young level to the largest level using only bulk reads and writes +(i.e., minimizing expensive seeks). + +### Manifest + +A MANIFEST file lists the set of sorted tables that make up each level, the +corresponding key ranges, and other important metadata. A new MANIFEST file +(with a new number embedded in the file name) is created whenever the database +is reopened. The MANIFEST file is formatted as a log, and changes made to the +serving state (as files are added or removed) are appended to this log. + +### Current + +CURRENT is a simple text file that contains the name of the latest MANIFEST +file. + +### Info logs + +Informational messages are printed to files named LOG and LOG.old. + +### Others + +Other files used for miscellaneous purposes may also be present (LOCK, *.dbtmp). + +## Level 0 + +When the log file grows above a certain size (4MB by default): +Create a brand new memtable and log file and direct future updates here. + +In the background: + +1. Write the contents of the previous memtable to an sstable. +2. Discard the memtable. +3. Delete the old log file and the old memtable. +4. Add the new sstable to the young (level-0) level. + +## Compactions + +When the size of level L exceeds its limit, we compact it in a background +thread. The compaction picks a file from level L and all overlapping files from +the next level L+1. Note that if a level-L file overlaps only part of a +level-(L+1) file, the entire file at level-(L+1) is used as an input to the +compaction and will be discarded after the compaction. Aside: because level-0 +is special (files in it may overlap each other), we treat compactions from +level-0 to level-1 specially: a level-0 compaction may pick more than one +level-0 file in case some of these files overlap each other. + +A compaction merges the contents of the picked files to produce a sequence of +level-(L+1) files. We switch to producing a new level-(L+1) file after the +current output file has reached the target file size (2MB). We also switch to a +new output file when the key range of the current output file has grown enough +to overlap more than ten level-(L+2) files. This last rule ensures that a later +compaction of a level-(L+1) file will not pick up too much data from +level-(L+2). + +The old files are discarded and the new files are added to the serving state. + +Compactions for a particular level rotate through the key space. In more detail, +for each level L, we remember the ending key of the last compaction at level L. +The next compaction for level L will pick the first file that starts after this +key (wrapping around to the beginning of the key space if there is no such +file). + +Compactions drop overwritten values. They also drop deletion markers if there +are no higher numbered levels that contain a file whose range overlaps the +current key. + +### Timing + +Level-0 compactions will read up to four 1MB files from level-0, and at worst +all the level-1 files (10MB). I.e., we will read 14MB and write 14MB. + +Other than the special level-0 compactions, we will pick one 2MB file from level +L. In the worst case, this will overlap ~ 12 files from level L+1 (10 because +level-(L+1) is ten times the size of level-L, and another two at the boundaries +since the file ranges at level-L will usually not be aligned with the file +ranges at level-L+1). The compaction will therefore read 26MB and write 26MB. +Assuming a disk IO rate of 100MB/s (ballpark range for modern drives), the worst +compaction cost will be approximately 0.5 second. + +If we throttle the background writing to something small, say 10% of the full +100MB/s speed, a compaction may take up to 5 seconds. If the user is writing at +10MB/s, we might build up lots of level-0 files (~50 to hold the 5*10MB). This +may significantly increase the cost of reads due to the overhead of merging more +files together on every read. + +Solution 1: To reduce this problem, we might want to increase the log switching +threshold when the number of level-0 files is large. Though the downside is that +the larger this threshold, the more memory we will need to hold the +corresponding memtable. + +Solution 2: We might want to decrease write rate artificially when the number of +level-0 files goes up. + +Solution 3: We work on reducing the cost of very wide merges. Perhaps most of +the level-0 files will have their blocks sitting uncompressed in the cache and +we will only need to worry about the O(N) complexity in the merging iterator. + +### Number of files + +Instead of always making 2MB files, we could make larger files for larger levels +to reduce the total file count, though at the expense of more bursty +compactions. Alternatively, we could shard the set of files into multiple +directories. + +An experiment on an ext3 filesystem on Feb 04, 2011 shows the following timings +to do 100K file opens in directories with varying number of files: + + +| Files in directory | Microseconds to open a file | +|-------------------:|----------------------------:| +| 1000 | 9 | +| 10000 | 10 | +| 100000 | 16 | + +So maybe even the sharding is not necessary on modern filesystems? + +## Recovery + +* Read CURRENT to find name of the latest committed MANIFEST +* Read the named MANIFEST file +* Clean up stale files +* We could open all sstables here, but it is probably better to be lazy... +* Convert log chunk to a new level-0 sstable +* Start directing new writes to a new log file with recovered sequence# + +## Garbage collection of files + +`DeleteObsoleteFiles()` is called at the end of every compaction and at the end +of recovery. It finds the names of all files in the database. It deletes all log +files that are not the current log file. It deletes all table files that are not +referenced from some level and are not the output of an active compaction. diff --git a/src/leveldb/doc/index.html b/src/leveldb/doc/index.html deleted file mode 100644 index 3ed0ed9d9..000000000 --- a/src/leveldb/doc/index.html +++ /dev/null @@ -1,549 +0,0 @@ - - - - -Leveldb - - - -

Leveldb

-
Jeff Dean, Sanjay Ghemawat
-

-The leveldb library provides a persistent key value store. Keys and -values are arbitrary byte arrays. The keys are ordered within the key -value store according to a user-specified comparator function. - -

-

Opening A Database

-

-A leveldb database has a name which corresponds to a file system -directory. All of the contents of database are stored in this -directory. The following example shows how to open a database, -creating it if necessary: -

-

-  #include <assert>
-  #include "leveldb/db.h"
-
-  leveldb::DB* db;
-  leveldb::Options options;
-  options.create_if_missing = true;
-  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
-  assert(status.ok());
-  ...
-
-If you want to raise an error if the database already exists, add -the following line before the leveldb::DB::Open call: -
-  options.error_if_exists = true;
-
-

Status

-

-You may have noticed the leveldb::Status type above. Values of this -type are returned by most functions in leveldb that may encounter an -error. You can check if such a result is ok, and also print an -associated error message: -

-

-   leveldb::Status s = ...;
-   if (!s.ok()) cerr << s.ToString() << endl;
-
-

Closing A Database

-

-When you are done with a database, just delete the database object. -Example: -

-

-  ... open the db as described above ...
-  ... do something with db ...
-  delete db;
-
-

Reads And Writes

-

-The database provides Put, Delete, and Get methods to -modify/query the database. For example, the following code -moves the value stored under key1 to key2. -

-  std::string value;
-  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
-  if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
-  if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
-
- -

Atomic Updates

-

-Note that if the process dies after the Put of key2 but before the -delete of key1, the same value may be left stored under multiple keys. -Such problems can be avoided by using the WriteBatch class to -atomically apply a set of updates: -

-

-  #include "leveldb/write_batch.h"
-  ...
-  std::string value;
-  leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
-  if (s.ok()) {
-    leveldb::WriteBatch batch;
-    batch.Delete(key1);
-    batch.Put(key2, value);
-    s = db->Write(leveldb::WriteOptions(), &batch);
-  }
-
-The WriteBatch holds a sequence of edits to be made to the database, -and these edits within the batch are applied in order. Note that we -called Delete before Put so that if key1 is identical to key2, -we do not end up erroneously dropping the value entirely. -

-Apart from its atomicity benefits, WriteBatch may also be used to -speed up bulk updates by placing lots of individual mutations into the -same batch. - -

Synchronous Writes

-By default, each write to leveldb is asynchronous: it -returns after pushing the write from the process into the operating -system. The transfer from operating system memory to the underlying -persistent storage happens asynchronously. The sync flag -can be turned on for a particular write to make the write operation -not return until the data being written has been pushed all the way to -persistent storage. (On Posix systems, this is implemented by calling -either fsync(...) or fdatasync(...) or -msync(..., MS_SYNC) before the write operation returns.) -
-  leveldb::WriteOptions write_options;
-  write_options.sync = true;
-  db->Put(write_options, ...);
-
-Asynchronous writes are often more than a thousand times as fast as -synchronous writes. The downside of asynchronous writes is that a -crash of the machine may cause the last few updates to be lost. Note -that a crash of just the writing process (i.e., not a reboot) will not -cause any loss since even when sync is false, an update -is pushed from the process memory into the operating system before it -is considered done. - -

-Asynchronous writes can often be used safely. For example, when -loading a large amount of data into the database you can handle lost -updates by restarting the bulk load after a crash. A hybrid scheme is -also possible where every Nth write is synchronous, and in the event -of a crash, the bulk load is restarted just after the last synchronous -write finished by the previous run. (The synchronous write can update -a marker that describes where to restart on a crash.) - -

-WriteBatch provides an alternative to asynchronous writes. -Multiple updates may be placed in the same WriteBatch and -applied together using a synchronous write (i.e., -write_options.sync is set to true). The extra cost of -the synchronous write will be amortized across all of the writes in -the batch. - -

-

Concurrency

-

-A database may only be opened by one process at a time. -The leveldb implementation acquires a lock from the -operating system to prevent misuse. Within a single process, the -same leveldb::DB object may be safely shared by multiple -concurrent threads. I.e., different threads may write into or fetch -iterators or call Get on the same database without any -external synchronization (the leveldb implementation will -automatically do the required synchronization). However other objects -(like Iterator and WriteBatch) may require external synchronization. -If two threads share such an object, they must protect access to it -using their own locking protocol. More details are available in -the public header files. -

-

Iteration

-

-The following example demonstrates how to print all key,value pairs -in a database. -

-

-  leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
-  for (it->SeekToFirst(); it->Valid(); it->Next()) {
-    cout << it->key().ToString() << ": "  << it->value().ToString() << endl;
-  }
-  assert(it->status().ok());  // Check for any errors found during the scan
-  delete it;
-
-The following variation shows how to process just the keys in the -range [start,limit): -

-

-  for (it->Seek(start);
-       it->Valid() && it->key().ToString() < limit;
-       it->Next()) {
-    ...
-  }
-
-You can also process entries in reverse order. (Caveat: reverse -iteration may be somewhat slower than forward iteration.) -

-

-  for (it->SeekToLast(); it->Valid(); it->Prev()) {
-    ...
-  }
-
-

Snapshots

-

-Snapshots provide consistent read-only views over the entire state of -the key-value store. ReadOptions::snapshot may be non-NULL to indicate -that a read should operate on a particular version of the DB state. -If ReadOptions::snapshot is NULL, the read will operate on an -implicit snapshot of the current state. -

-Snapshots are created by the DB::GetSnapshot() method: -

-

-  leveldb::ReadOptions options;
-  options.snapshot = db->GetSnapshot();
-  ... apply some updates to db ...
-  leveldb::Iterator* iter = db->NewIterator(options);
-  ... read using iter to view the state when the snapshot was created ...
-  delete iter;
-  db->ReleaseSnapshot(options.snapshot);
-
-Note that when a snapshot is no longer needed, it should be released -using the DB::ReleaseSnapshot interface. This allows the -implementation to get rid of state that was being maintained just to -support reading as of that snapshot. -

Slice

-

-The return value of the it->key() and it->value() calls above -are instances of the leveldb::Slice type. Slice is a simple -structure that contains a length and a pointer to an external byte -array. Returning a Slice is a cheaper alternative to returning a -std::string since we do not need to copy potentially large keys and -values. In addition, leveldb methods do not return null-terminated -C-style strings since leveldb keys and values are allowed to -contain '\0' bytes. -

-C++ strings and null-terminated C-style strings can be easily converted -to a Slice: -

-

-   leveldb::Slice s1 = "hello";
-
-   std::string str("world");
-   leveldb::Slice s2 = str;
-
-A Slice can be easily converted back to a C++ string: -
-   std::string str = s1.ToString();
-   assert(str == std::string("hello"));
-
-Be careful when using Slices since it is up to the caller to ensure that -the external byte array into which the Slice points remains live while -the Slice is in use. For example, the following is buggy: -

-

-   leveldb::Slice slice;
-   if (...) {
-     std::string str = ...;
-     slice = str;
-   }
-   Use(slice);
-
-When the if statement goes out of scope, str will be destroyed and the -backing storage for slice will disappear. -

-

Comparators

-

-The preceding examples used the default ordering function for key, -which orders bytes lexicographically. You can however supply a custom -comparator when opening a database. For example, suppose each -database key consists of two numbers and we should sort by the first -number, breaking ties by the second number. First, define a proper -subclass of leveldb::Comparator that expresses these rules: -

-

-  class TwoPartComparator : public leveldb::Comparator {
-   public:
-    // Three-way comparison function:
-    //   if a < b: negative result
-    //   if a > b: positive result
-    //   else: zero result
-    int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
-      int a1, a2, b1, b2;
-      ParseKey(a, &a1, &a2);
-      ParseKey(b, &b1, &b2);
-      if (a1 < b1) return -1;
-      if (a1 > b1) return +1;
-      if (a2 < b2) return -1;
-      if (a2 > b2) return +1;
-      return 0;
-    }
-
-    // Ignore the following methods for now:
-    const char* Name() const { return "TwoPartComparator"; }
-    void FindShortestSeparator(std::string*, const leveldb::Slice&) const { }
-    void FindShortSuccessor(std::string*) const { }
-  };
-
-Now create a database using this custom comparator: -

-

-  TwoPartComparator cmp;
-  leveldb::DB* db;
-  leveldb::Options options;
-  options.create_if_missing = true;
-  options.comparator = &cmp;
-  leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
-  ...
-
-

Backwards compatibility

-

-The result of the comparator's Name method is attached to the -database when it is created, and is checked on every subsequent -database open. If the name changes, the leveldb::DB::Open call will -fail. Therefore, change the name if and only if the new key format -and comparison function are incompatible with existing databases, and -it is ok to discard the contents of all existing databases. -

-You can however still gradually evolve your key format over time with -a little bit of pre-planning. For example, you could store a version -number at the end of each key (one byte should suffice for most uses). -When you wish to switch to a new key format (e.g., adding an optional -third part to the keys processed by TwoPartComparator), -(a) keep the same comparator name (b) increment the version number -for new keys (c) change the comparator function so it uses the -version numbers found in the keys to decide how to interpret them. -

-

Performance

-

-Performance can be tuned by changing the default values of the -types defined in include/leveldb/options.h. - -

-

Block size

-

-leveldb groups adjacent keys together into the same block and such a -block is the unit of transfer to and from persistent storage. The -default block size is approximately 4096 uncompressed bytes. -Applications that mostly do bulk scans over the contents of the -database may wish to increase this size. Applications that do a lot -of point reads of small values may wish to switch to a smaller block -size if performance measurements indicate an improvement. There isn't -much benefit in using blocks smaller than one kilobyte, or larger than -a few megabytes. Also note that compression will be more effective -with larger block sizes. -

-

Compression

-

-Each block is individually compressed before being written to -persistent storage. Compression is on by default since the default -compression method is very fast, and is automatically disabled for -uncompressible data. In rare cases, applications may want to disable -compression entirely, but should only do so if benchmarks show a -performance improvement: -

-

-  leveldb::Options options;
-  options.compression = leveldb::kNoCompression;
-  ... leveldb::DB::Open(options, name, ...) ....
-
-

Cache

-

-The contents of the database are stored in a set of files in the -filesystem and each file stores a sequence of compressed blocks. If -options.cache is non-NULL, it is used to cache frequently used -uncompressed block contents. -

-

-  #include "leveldb/cache.h"
-
-  leveldb::Options options;
-  options.cache = leveldb::NewLRUCache(100 * 1048576);  // 100MB cache
-  leveldb::DB* db;
-  leveldb::DB::Open(options, name, &db);
-  ... use the db ...
-  delete db
-  delete options.cache;
-
-Note that the cache holds uncompressed data, and therefore it should -be sized according to application level data sizes, without any -reduction from compression. (Caching of compressed blocks is left to -the operating system buffer cache, or any custom Env -implementation provided by the client.) -

-When performing a bulk read, the application may wish to disable -caching so that the data processed by the bulk read does not end up -displacing most of the cached contents. A per-iterator option can be -used to achieve this: -

-

-  leveldb::ReadOptions options;
-  options.fill_cache = false;
-  leveldb::Iterator* it = db->NewIterator(options);
-  for (it->SeekToFirst(); it->Valid(); it->Next()) {
-    ...
-  }
-
-

Key Layout

-

-Note that the unit of disk transfer and caching is a block. Adjacent -keys (according to the database sort order) will usually be placed in -the same block. Therefore the application can improve its performance -by placing keys that are accessed together near each other and placing -infrequently used keys in a separate region of the key space. -

-For example, suppose we are implementing a simple file system on top -of leveldb. The types of entries we might wish to store are: -

-

-   filename -> permission-bits, length, list of file_block_ids
-   file_block_id -> data
-
-We might want to prefix filename keys with one letter (say '/') and the -file_block_id keys with a different letter (say '0') so that scans -over just the metadata do not force us to fetch and cache bulky file -contents. -

-

Filters

-

-Because of the way leveldb data is organized on disk, -a single Get() call may involve multiple reads from disk. -The optional FilterPolicy mechanism can be used to reduce -the number of disk reads substantially. -

-   leveldb::Options options;
-   options.filter_policy = NewBloomFilterPolicy(10);
-   leveldb::DB* db;
-   leveldb::DB::Open(options, "/tmp/testdb", &db);
-   ... use the database ...
-   delete db;
-   delete options.filter_policy;
-
-The preceding code associates a -Bloom filter -based filtering policy with the database. Bloom filter based -filtering relies on keeping some number of bits of data in memory per -key (in this case 10 bits per key since that is the argument we passed -to NewBloomFilterPolicy). This filter will reduce the number of unnecessary -disk reads needed for Get() calls by a factor of -approximately a 100. Increasing the bits per key will lead to a -larger reduction at the cost of more memory usage. We recommend that -applications whose working set does not fit in memory and that do a -lot of random reads set a filter policy. -

-If you are using a custom comparator, you should ensure that the filter -policy you are using is compatible with your comparator. For example, -consider a comparator that ignores trailing spaces when comparing keys. -NewBloomFilterPolicy must not be used with such a comparator. -Instead, the application should provide a custom filter policy that -also ignores trailing spaces. For example: -

-  class CustomFilterPolicy : public leveldb::FilterPolicy {
-   private:
-    FilterPolicy* builtin_policy_;
-   public:
-    CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) { }
-    ~CustomFilterPolicy() { delete builtin_policy_; }
-
-    const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
-
-    void CreateFilter(const Slice* keys, int n, std::string* dst) const {
-      // Use builtin bloom filter code after removing trailing spaces
-      std::vector<Slice> trimmed(n);
-      for (int i = 0; i < n; i++) {
-        trimmed[i] = RemoveTrailingSpaces(keys[i]);
-      }
-      return builtin_policy_->CreateFilter(&trimmed[i], n, dst);
-    }
-
-    bool KeyMayMatch(const Slice& key, const Slice& filter) const {
-      // Use builtin bloom filter code after removing trailing spaces
-      return builtin_policy_->KeyMayMatch(RemoveTrailingSpaces(key), filter);
-    }
-  };
-
-

-Advanced applications may provide a filter policy that does not use -a bloom filter but uses some other mechanism for summarizing a set -of keys. See leveldb/filter_policy.h for detail. -

-

Checksums

-

-leveldb associates checksums with all data it stores in the file system. -There are two separate controls provided over how aggressively these -checksums are verified: -

-

    -
  • ReadOptions::verify_checksums may be set to true to force - checksum verification of all data that is read from the file system on - behalf of a particular read. By default, no such verification is - done. -

    -

  • Options::paranoid_checks may be set to true before opening a - database to make the database implementation raise an error as soon as - it detects an internal corruption. Depending on which portion of the - database has been corrupted, the error may be raised when the database - is opened, or later by another database operation. By default, - paranoid checking is off so that the database can be used even if - parts of its persistent storage have been corrupted. -

    - If a database is corrupted (perhaps it cannot be opened when - paranoid checking is turned on), the leveldb::RepairDB function - may be used to recover as much of the data as possible -

    -

-

Approximate Sizes

-

-The GetApproximateSizes method can used to get the approximate -number of bytes of file system space used by one or more key ranges. -

-

-   leveldb::Range ranges[2];
-   ranges[0] = leveldb::Range("a", "c");
-   ranges[1] = leveldb::Range("x", "z");
-   uint64_t sizes[2];
-   leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes);
-
-The preceding call will set sizes[0] to the approximate number of -bytes of file system space used by the key range [a..c) and -sizes[1] to the approximate number of bytes used by the key range -[x..z). -

-

Environment

-

-All file operations (and other operating system calls) issued by the -leveldb implementation are routed through a leveldb::Env object. -Sophisticated clients may wish to provide their own Env -implementation to get better control. For example, an application may -introduce artificial delays in the file IO paths to limit the impact -of leveldb on other activities in the system. -

-

-  class SlowEnv : public leveldb::Env {
-    .. implementation of the Env interface ...
-  };
-
-  SlowEnv env;
-  leveldb::Options options;
-  options.env = &env;
-  Status s = leveldb::DB::Open(options, ...);
-
-

Porting

-

-leveldb may be ported to a new platform by providing platform -specific implementations of the types/methods/functions exported by -leveldb/port/port.h. See leveldb/port/port_example.h for more -details. -

-In addition, the new platform may need a new default leveldb::Env -implementation. See leveldb/util/env_posix.h for an example. - -

Other Information

- -

-Details about the leveldb implementation may be found in -the following documents: -

- - - diff --git a/src/leveldb/doc/index.md b/src/leveldb/doc/index.md new file mode 100644 index 000000000..3d9a25805 --- /dev/null +++ b/src/leveldb/doc/index.md @@ -0,0 +1,523 @@ +leveldb +======= + +_Jeff Dean, Sanjay Ghemawat_ + +The leveldb library provides a persistent key value store. Keys and values are +arbitrary byte arrays. The keys are ordered within the key value store +according to a user-specified comparator function. + +## Opening A Database + +A leveldb database has a name which corresponds to a file system directory. All +of the contents of database are stored in this directory. The following example +shows how to open a database, creating it if necessary: + +```c++ +#include +#include "leveldb/db.h" + +leveldb::DB* db; +leveldb::Options options; +options.create_if_missing = true; +leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); +assert(status.ok()); +... +``` + +If you want to raise an error if the database already exists, add the following +line before the `leveldb::DB::Open` call: + +```c++ +options.error_if_exists = true; +``` + +## Status + +You may have noticed the `leveldb::Status` type above. Values of this type are +returned by most functions in leveldb that may encounter an error. You can check +if such a result is ok, and also print an associated error message: + +```c++ +leveldb::Status s = ...; +if (!s.ok()) cerr << s.ToString() << endl; +``` + +## Closing A Database + +When you are done with a database, just delete the database object. Example: + +```c++ +... open the db as described above ... +... do something with db ... +delete db; +``` + +## Reads And Writes + +The database provides Put, Delete, and Get methods to modify/query the database. +For example, the following code moves the value stored under key1 to key2. + +```c++ +std::string value; +leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value); +if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value); +if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1); +``` + +## Atomic Updates + +Note that if the process dies after the Put of key2 but before the delete of +key1, the same value may be left stored under multiple keys. Such problems can +be avoided by using the `WriteBatch` class to atomically apply a set of updates: + +```c++ +#include "leveldb/write_batch.h" +... +std::string value; +leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value); +if (s.ok()) { + leveldb::WriteBatch batch; + batch.Delete(key1); + batch.Put(key2, value); + s = db->Write(leveldb::WriteOptions(), &batch); +} +``` + +The `WriteBatch` holds a sequence of edits to be made to the database, and these +edits within the batch are applied in order. Note that we called Delete before +Put so that if key1 is identical to key2, we do not end up erroneously dropping +the value entirely. + +Apart from its atomicity benefits, `WriteBatch` may also be used to speed up +bulk updates by placing lots of individual mutations into the same batch. + +## Synchronous Writes + +By default, each write to leveldb is asynchronous: it returns after pushing the +write from the process into the operating system. The transfer from operating +system memory to the underlying persistent storage happens asynchronously. The +sync flag can be turned on for a particular write to make the write operation +not return until the data being written has been pushed all the way to +persistent storage. (On Posix systems, this is implemented by calling either +`fsync(...)` or `fdatasync(...)` or `msync(..., MS_SYNC)` before the write +operation returns.) + +```c++ +leveldb::WriteOptions write_options; +write_options.sync = true; +db->Put(write_options, ...); +``` + +Asynchronous writes are often more than a thousand times as fast as synchronous +writes. The downside of asynchronous writes is that a crash of the machine may +cause the last few updates to be lost. Note that a crash of just the writing +process (i.e., not a reboot) will not cause any loss since even when sync is +false, an update is pushed from the process memory into the operating system +before it is considered done. + +Asynchronous writes can often be used safely. For example, when loading a large +amount of data into the database you can handle lost updates by restarting the +bulk load after a crash. A hybrid scheme is also possible where every Nth write +is synchronous, and in the event of a crash, the bulk load is restarted just +after the last synchronous write finished by the previous run. (The synchronous +write can update a marker that describes where to restart on a crash.) + +`WriteBatch` provides an alternative to asynchronous writes. Multiple updates +may be placed in the same WriteBatch and applied together using a synchronous +write (i.e., `write_options.sync` is set to true). The extra cost of the +synchronous write will be amortized across all of the writes in the batch. + +## Concurrency + +A database may only be opened by one process at a time. The leveldb +implementation acquires a lock from the operating system to prevent misuse. +Within a single process, the same `leveldb::DB` object may be safely shared by +multiple concurrent threads. I.e., different threads may write into or fetch +iterators or call Get on the same database without any external synchronization +(the leveldb implementation will automatically do the required synchronization). +However other objects (like Iterator and `WriteBatch`) may require external +synchronization. If two threads share such an object, they must protect access +to it using their own locking protocol. More details are available in the public +header files. + +## Iteration + +The following example demonstrates how to print all key,value pairs in a +database. + +```c++ +leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions()); +for (it->SeekToFirst(); it->Valid(); it->Next()) { + cout << it->key().ToString() << ": " << it->value().ToString() << endl; +} +assert(it->status().ok()); // Check for any errors found during the scan +delete it; +``` + +The following variation shows how to process just the keys in the range +[start,limit): + +```c++ +for (it->Seek(start); + it->Valid() && it->key().ToString() < limit; + it->Next()) { + ... +} +``` + +You can also process entries in reverse order. (Caveat: reverse iteration may be +somewhat slower than forward iteration.) + +```c++ +for (it->SeekToLast(); it->Valid(); it->Prev()) { + ... +} +``` + +## Snapshots + +Snapshots provide consistent read-only views over the entire state of the +key-value store. `ReadOptions::snapshot` may be non-NULL to indicate that a +read should operate on a particular version of the DB state. If +`ReadOptions::snapshot` is NULL, the read will operate on an implicit snapshot +of the current state. + +Snapshots are created by the `DB::GetSnapshot()` method: + +```c++ +leveldb::ReadOptions options; +options.snapshot = db->GetSnapshot(); +... apply some updates to db ... +leveldb::Iterator* iter = db->NewIterator(options); +... read using iter to view the state when the snapshot was created ... +delete iter; +db->ReleaseSnapshot(options.snapshot); +``` + +Note that when a snapshot is no longer needed, it should be released using the +`DB::ReleaseSnapshot` interface. This allows the implementation to get rid of +state that was being maintained just to support reading as of that snapshot. + +## Slice + +The return value of the `it->key()` and `it->value()` calls above are instances +of the `leveldb::Slice` type. Slice is a simple structure that contains a length +and a pointer to an external byte array. Returning a Slice is a cheaper +alternative to returning a `std::string` since we do not need to copy +potentially large keys and values. In addition, leveldb methods do not return +null-terminated C-style strings since leveldb keys and values are allowed to +contain `'\0'` bytes. + +C++ strings and null-terminated C-style strings can be easily converted to a +Slice: + +```c++ +leveldb::Slice s1 = "hello"; + +std::string str("world"); +leveldb::Slice s2 = str; +``` + +A Slice can be easily converted back to a C++ string: + +```c++ +std::string str = s1.ToString(); +assert(str == std::string("hello")); +``` + +Be careful when using Slices since it is up to the caller to ensure that the +external byte array into which the Slice points remains live while the Slice is +in use. For example, the following is buggy: + +```c++ +leveldb::Slice slice; +if (...) { + std::string str = ...; + slice = str; +} +Use(slice); +``` + +When the if statement goes out of scope, str will be destroyed and the backing +storage for slice will disappear. + +## Comparators + +The preceding examples used the default ordering function for key, which orders +bytes lexicographically. You can however supply a custom comparator when opening +a database. For example, suppose each database key consists of two numbers and +we should sort by the first number, breaking ties by the second number. First, +define a proper subclass of `leveldb::Comparator` that expresses these rules: + +```c++ +class TwoPartComparator : public leveldb::Comparator { + public: + // Three-way comparison function: + // if a < b: negative result + // if a > b: positive result + // else: zero result + int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const { + int a1, a2, b1, b2; + ParseKey(a, &a1, &a2); + ParseKey(b, &b1, &b2); + if (a1 < b1) return -1; + if (a1 > b1) return +1; + if (a2 < b2) return -1; + if (a2 > b2) return +1; + return 0; + } + + // Ignore the following methods for now: + const char* Name() const { return "TwoPartComparator"; } + void FindShortestSeparator(std::string*, const leveldb::Slice&) const {} + void FindShortSuccessor(std::string*) const {} +}; +``` + +Now create a database using this custom comparator: + +```c++ +TwoPartComparator cmp; +leveldb::DB* db; +leveldb::Options options; +options.create_if_missing = true; +options.comparator = &cmp; +leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); +... +``` + +### Backwards compatibility + +The result of the comparator's Name method is attached to the database when it +is created, and is checked on every subsequent database open. If the name +changes, the `leveldb::DB::Open` call will fail. Therefore, change the name if +and only if the new key format and comparison function are incompatible with +existing databases, and it is ok to discard the contents of all existing +databases. + +You can however still gradually evolve your key format over time with a little +bit of pre-planning. For example, you could store a version number at the end of +each key (one byte should suffice for most uses). When you wish to switch to a +new key format (e.g., adding an optional third part to the keys processed by +`TwoPartComparator`), (a) keep the same comparator name (b) increment the +version number for new keys (c) change the comparator function so it uses the +version numbers found in the keys to decide how to interpret them. + +## Performance + +Performance can be tuned by changing the default values of the types defined in +`include/options.h`. + +### Block size + +leveldb groups adjacent keys together into the same block and such a block is +the unit of transfer to and from persistent storage. The default block size is +approximately 4096 uncompressed bytes. Applications that mostly do bulk scans +over the contents of the database may wish to increase this size. Applications +that do a lot of point reads of small values may wish to switch to a smaller +block size if performance measurements indicate an improvement. There isn't much +benefit in using blocks smaller than one kilobyte, or larger than a few +megabytes. Also note that compression will be more effective with larger block +sizes. + +### Compression + +Each block is individually compressed before being written to persistent +storage. Compression is on by default since the default compression method is +very fast, and is automatically disabled for uncompressible data. In rare cases, +applications may want to disable compression entirely, but should only do so if +benchmarks show a performance improvement: + +```c++ +leveldb::Options options; +options.compression = leveldb::kNoCompression; +... leveldb::DB::Open(options, name, ...) .... +``` + +### Cache + +The contents of the database are stored in a set of files in the filesystem and +each file stores a sequence of compressed blocks. If options.block_cache is +non-NULL, it is used to cache frequently used uncompressed block contents. + +```c++ +#include "leveldb/cache.h" + +leveldb::Options options; +options.block_cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache +leveldb::DB* db; +leveldb::DB::Open(options, name, &db); +... use the db ... +delete db +delete options.block_cache; +``` + +Note that the cache holds uncompressed data, and therefore it should be sized +according to application level data sizes, without any reduction from +compression. (Caching of compressed blocks is left to the operating system +buffer cache, or any custom Env implementation provided by the client.) + +When performing a bulk read, the application may wish to disable caching so that +the data processed by the bulk read does not end up displacing most of the +cached contents. A per-iterator option can be used to achieve this: + +```c++ +leveldb::ReadOptions options; +options.fill_cache = false; +leveldb::Iterator* it = db->NewIterator(options); +for (it->SeekToFirst(); it->Valid(); it->Next()) { + ... +} +``` + +### Key Layout + +Note that the unit of disk transfer and caching is a block. Adjacent keys +(according to the database sort order) will usually be placed in the same block. +Therefore the application can improve its performance by placing keys that are +accessed together near each other and placing infrequently used keys in a +separate region of the key space. + +For example, suppose we are implementing a simple file system on top of leveldb. +The types of entries we might wish to store are: + + filename -> permission-bits, length, list of file_block_ids + file_block_id -> data + +We might want to prefix filename keys with one letter (say '/') and the +`file_block_id` keys with a different letter (say '0') so that scans over just +the metadata do not force us to fetch and cache bulky file contents. + +### Filters + +Because of the way leveldb data is organized on disk, a single `Get()` call may +involve multiple reads from disk. The optional FilterPolicy mechanism can be +used to reduce the number of disk reads substantially. + +```c++ +leveldb::Options options; +options.filter_policy = NewBloomFilterPolicy(10); +leveldb::DB* db; +leveldb::DB::Open(options, "/tmp/testdb", &db); +... use the database ... +delete db; +delete options.filter_policy; +``` + +The preceding code associates a Bloom filter based filtering policy with the +database. Bloom filter based filtering relies on keeping some number of bits of +data in memory per key (in this case 10 bits per key since that is the argument +we passed to `NewBloomFilterPolicy`). This filter will reduce the number of +unnecessary disk reads needed for Get() calls by a factor of approximately +a 100. Increasing the bits per key will lead to a larger reduction at the cost +of more memory usage. We recommend that applications whose working set does not +fit in memory and that do a lot of random reads set a filter policy. + +If you are using a custom comparator, you should ensure that the filter policy +you are using is compatible with your comparator. For example, consider a +comparator that ignores trailing spaces when comparing keys. +`NewBloomFilterPolicy` must not be used with such a comparator. Instead, the +application should provide a custom filter policy that also ignores trailing +spaces. For example: + +```c++ +class CustomFilterPolicy : public leveldb::FilterPolicy { + private: + FilterPolicy* builtin_policy_; + + public: + CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) {} + ~CustomFilterPolicy() { delete builtin_policy_; } + + const char* Name() const { return "IgnoreTrailingSpacesFilter"; } + + void CreateFilter(const Slice* keys, int n, std::string* dst) const { + // Use builtin bloom filter code after removing trailing spaces + std::vector trimmed(n); + for (int i = 0; i < n; i++) { + trimmed[i] = RemoveTrailingSpaces(keys[i]); + } + return builtin_policy_->CreateFilter(&trimmed[i], n, dst); + } +}; +``` + +Advanced applications may provide a filter policy that does not use a bloom +filter but uses some other mechanism for summarizing a set of keys. See +`leveldb/filter_policy.h` for detail. + +## Checksums + +leveldb associates checksums with all data it stores in the file system. There +are two separate controls provided over how aggressively these checksums are +verified: + +`ReadOptions::verify_checksums` may be set to true to force checksum +verification of all data that is read from the file system on behalf of a +particular read. By default, no such verification is done. + +`Options::paranoid_checks` may be set to true before opening a database to make +the database implementation raise an error as soon as it detects an internal +corruption. Depending on which portion of the database has been corrupted, the +error may be raised when the database is opened, or later by another database +operation. By default, paranoid checking is off so that the database can be used +even if parts of its persistent storage have been corrupted. + +If a database is corrupted (perhaps it cannot be opened when paranoid checking +is turned on), the `leveldb::RepairDB` function may be used to recover as much +of the data as possible + +## Approximate Sizes + +The `GetApproximateSizes` method can used to get the approximate number of bytes +of file system space used by one or more key ranges. + +```c++ +leveldb::Range ranges[2]; +ranges[0] = leveldb::Range("a", "c"); +ranges[1] = leveldb::Range("x", "z"); +uint64_t sizes[2]; +leveldb::Status s = db->GetApproximateSizes(ranges, 2, sizes); +``` + +The preceding call will set `sizes[0]` to the approximate number of bytes of +file system space used by the key range `[a..c)` and `sizes[1]` to the +approximate number of bytes used by the key range `[x..z)`. + +## Environment + +All file operations (and other operating system calls) issued by the leveldb +implementation are routed through a `leveldb::Env` object. Sophisticated clients +may wish to provide their own Env implementation to get better control. +For example, an application may introduce artificial delays in the file IO +paths to limit the impact of leveldb on other activities in the system. + +```c++ +class SlowEnv : public leveldb::Env { + ... implementation of the Env interface ... +}; + +SlowEnv env; +leveldb::Options options; +options.env = &env; +Status s = leveldb::DB::Open(options, ...); +``` + +## Porting + +leveldb may be ported to a new platform by providing platform specific +implementations of the types/methods/functions exported by +`leveldb/port/port.h`. See `leveldb/port/port_example.h` for more details. + +In addition, the new platform may need a new default `leveldb::Env` +implementation. See `leveldb/util/env_posix.h` for an example. + +## Other Information + +Details about the leveldb implementation may be found in the following +documents: + +1. [Implementation notes](impl.md) +2. [Format of an immutable Table file](table_format.md) +3. [Format of a log file](log_format.md) diff --git a/src/leveldb/doc/log_format.md b/src/leveldb/doc/log_format.md new file mode 100644 index 000000000..f32cb5d7d --- /dev/null +++ b/src/leveldb/doc/log_format.md @@ -0,0 +1,75 @@ +leveldb Log format +================== +The log file contents are a sequence of 32KB blocks. The only exception is that +the tail of the file may contain a partial block. + +Each block consists of a sequence of records: + + block := record* trailer? + record := + checksum: uint32 // crc32c of type and data[] ; little-endian + length: uint16 // little-endian + type: uint8 // One of FULL, FIRST, MIDDLE, LAST + data: uint8[length] + +A record never starts within the last six bytes of a block (since it won't fit). +Any leftover bytes here form the trailer, which must consist entirely of zero +bytes and must be skipped by readers. + +Aside: if exactly seven bytes are left in the current block, and a new non-zero +length record is added, the writer must emit a FIRST record (which contains zero +bytes of user data) to fill up the trailing seven bytes of the block and then +emit all of the user data in subsequent blocks. + +More types may be added in the future. Some Readers may skip record types they +do not understand, others may report that some data was skipped. + + FULL == 1 + FIRST == 2 + MIDDLE == 3 + LAST == 4 + +The FULL record contains the contents of an entire user record. + +FIRST, MIDDLE, LAST are types used for user records that have been split into +multiple fragments (typically because of block boundaries). FIRST is the type +of the first fragment of a user record, LAST is the type of the last fragment of +a user record, and MIDDLE is the type of all interior fragments of a user +record. + +Example: consider a sequence of user records: + + A: length 1000 + B: length 97270 + C: length 8000 + +**A** will be stored as a FULL record in the first block. + +**B** will be split into three fragments: first fragment occupies the rest of +the first block, second fragment occupies the entirety of the second block, and +the third fragment occupies a prefix of the third block. This will leave six +bytes free in the third block, which will be left empty as the trailer. + +**C** will be stored as a FULL record in the fourth block. + +---- + +## Some benefits over the recordio format: + +1. We do not need any heuristics for resyncing - just go to next block boundary + and scan. If there is a corruption, skip to the next block. As a + side-benefit, we do not get confused when part of the contents of one log + file are embedded as a record inside another log file. + +2. Splitting at approximate boundaries (e.g., for mapreduce) is simple: find the + next block boundary and skip records until we hit a FULL or FIRST record. + +3. We do not need extra buffering for large records. + +## Some downsides compared to recordio format: + +1. No packing of tiny records. This could be fixed by adding a new record type, + so it is a shortcoming of the current implementation, not necessarily the + format. + +2. No compression. Again, this could be fixed by adding new record types. diff --git a/src/leveldb/doc/log_format.txt b/src/leveldb/doc/log_format.txt deleted file mode 100644 index 4cca5ef6e..000000000 --- a/src/leveldb/doc/log_format.txt +++ /dev/null @@ -1,75 +0,0 @@ -The log file contents are a sequence of 32KB blocks. The only -exception is that the tail of the file may contain a partial block. - -Each block consists of a sequence of records: - block := record* trailer? - record := - checksum: uint32 // crc32c of type and data[] ; little-endian - length: uint16 // little-endian - type: uint8 // One of FULL, FIRST, MIDDLE, LAST - data: uint8[length] - -A record never starts within the last six bytes of a block (since it -won't fit). Any leftover bytes here form the trailer, which must -consist entirely of zero bytes and must be skipped by readers. - -Aside: if exactly seven bytes are left in the current block, and a new -non-zero length record is added, the writer must emit a FIRST record -(which contains zero bytes of user data) to fill up the trailing seven -bytes of the block and then emit all of the user data in subsequent -blocks. - -More types may be added in the future. Some Readers may skip record -types they do not understand, others may report that some data was -skipped. - -FULL == 1 -FIRST == 2 -MIDDLE == 3 -LAST == 4 - -The FULL record contains the contents of an entire user record. - -FIRST, MIDDLE, LAST are types used for user records that have been -split into multiple fragments (typically because of block boundaries). -FIRST is the type of the first fragment of a user record, LAST is the -type of the last fragment of a user record, and MIDDLE is the type of -all interior fragments of a user record. - -Example: consider a sequence of user records: - A: length 1000 - B: length 97270 - C: length 8000 -A will be stored as a FULL record in the first block. - -B will be split into three fragments: first fragment occupies the rest -of the first block, second fragment occupies the entirety of the -second block, and the third fragment occupies a prefix of the third -block. This will leave six bytes free in the third block, which will -be left empty as the trailer. - -C will be stored as a FULL record in the fourth block. - -=================== - -Some benefits over the recordio format: - -(1) We do not need any heuristics for resyncing - just go to next -block boundary and scan. If there is a corruption, skip to the next -block. As a side-benefit, we do not get confused when part of the -contents of one log file are embedded as a record inside another log -file. - -(2) Splitting at approximate boundaries (e.g., for mapreduce) is -simple: find the next block boundary and skip records until we -hit a FULL or FIRST record. - -(3) We do not need extra buffering for large records. - -Some downsides compared to recordio format: - -(1) No packing of tiny records. This could be fixed by adding a new -record type, so it is a shortcoming of the current implementation, -not necessarily the format. - -(2) No compression. Again, this could be fixed by adding new record types. diff --git a/src/leveldb/doc/table_format.md b/src/leveldb/doc/table_format.md new file mode 100644 index 000000000..5fe7e7241 --- /dev/null +++ b/src/leveldb/doc/table_format.md @@ -0,0 +1,107 @@ +leveldb File format +=================== + + + [data block 1] + [data block 2] + ... + [data block N] + [meta block 1] + ... + [meta block K] + [metaindex block] + [index block] + [Footer] (fixed size; starts at file_size - sizeof(Footer)) + + +The file contains internal pointers. Each such pointer is called +a BlockHandle and contains the following information: + + offset: varint64 + size: varint64 + +See [varints](https://developers.google.com/protocol-buffers/docs/encoding#varints) +for an explanation of varint64 format. + +1. The sequence of key/value pairs in the file are stored in sorted +order and partitioned into a sequence of data blocks. These blocks +come one after another at the beginning of the file. Each data block +is formatted according to the code in `block_builder.cc`, and then +optionally compressed. + +2. After the data blocks we store a bunch of meta blocks. The +supported meta block types are described below. More meta block types +may be added in the future. Each meta block is again formatted using +`block_builder.cc` and then optionally compressed. + +3. A "metaindex" block. It contains one entry for every other meta +block where the key is the name of the meta block and the value is a +BlockHandle pointing to that meta block. + +4. An "index" block. This block contains one entry per data block, +where the key is a string >= last key in that data block and before +the first key in the successive data block. The value is the +BlockHandle for the data block. + +5. At the very end of the file is a fixed length footer that contains +the BlockHandle of the metaindex and index blocks as well as a magic number. + + metaindex_handle: char[p]; // Block handle for metaindex + index_handle: char[q]; // Block handle for index + padding: char[40-p-q];// zeroed bytes to make fixed length + // (40==2*BlockHandle::kMaxEncodedLength) + magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) + +## "filter" Meta Block + +If a `FilterPolicy` was specified when the database was opened, a +filter block is stored in each table. The "metaindex" block contains +an entry that maps from `filter.` to the BlockHandle for the filter +block where `` is the string returned by the filter policy's +`Name()` method. + +The filter block stores a sequence of filters, where filter i contains +the output of `FilterPolicy::CreateFilter()` on all keys that are stored +in a block whose file offset falls within the range + + [ i*base ... (i+1)*base-1 ] + +Currently, "base" is 2KB. So for example, if blocks X and Y start in +the range `[ 0KB .. 2KB-1 ]`, all of the keys in X and Y will be +converted to a filter by calling `FilterPolicy::CreateFilter()`, and the +resulting filter will be stored as the first filter in the filter +block. + +The filter block is formatted as follows: + + [filter 0] + [filter 1] + [filter 2] + ... + [filter N-1] + + [offset of filter 0] : 4 bytes + [offset of filter 1] : 4 bytes + [offset of filter 2] : 4 bytes + ... + [offset of filter N-1] : 4 bytes + + [offset of beginning of offset array] : 4 bytes + lg(base) : 1 byte + +The offset array at the end of the filter block allows efficient +mapping from a data block offset to the corresponding filter. + +## "stats" Meta Block + +This meta block contains a bunch of stats. The key is the name +of the statistic. The value contains the statistic. + +TODO(postrelease): record following stats. + + data size + index size + key size (uncompressed) + value size (uncompressed) + number of entries + number of data blocks diff --git a/src/leveldb/doc/table_format.txt b/src/leveldb/doc/table_format.txt deleted file mode 100644 index ca8f9b446..000000000 --- a/src/leveldb/doc/table_format.txt +++ /dev/null @@ -1,104 +0,0 @@ -File format -=========== - - - [data block 1] - [data block 2] - ... - [data block N] - [meta block 1] - ... - [meta block K] - [metaindex block] - [index block] - [Footer] (fixed size; starts at file_size - sizeof(Footer)) - - -The file contains internal pointers. Each such pointer is called -a BlockHandle and contains the following information: - offset: varint64 - size: varint64 -See https://developers.google.com/protocol-buffers/docs/encoding#varints -for an explanation of varint64 format. - -(1) The sequence of key/value pairs in the file are stored in sorted -order and partitioned into a sequence of data blocks. These blocks -come one after another at the beginning of the file. Each data block -is formatted according to the code in block_builder.cc, and then -optionally compressed. - -(2) After the data blocks we store a bunch of meta blocks. The -supported meta block types are described below. More meta block types -may be added in the future. Each meta block is again formatted using -block_builder.cc and then optionally compressed. - -(3) A "metaindex" block. It contains one entry for every other meta -block where the key is the name of the meta block and the value is a -BlockHandle pointing to that meta block. - -(4) An "index" block. This block contains one entry per data block, -where the key is a string >= last key in that data block and before -the first key in the successive data block. The value is the -BlockHandle for the data block. - -(6) At the very end of the file is a fixed length footer that contains -the BlockHandle of the metaindex and index blocks as well as a magic number. - metaindex_handle: char[p]; // Block handle for metaindex - index_handle: char[q]; // Block handle for index - padding: char[40-p-q]; // zeroed bytes to make fixed length - // (40==2*BlockHandle::kMaxEncodedLength) - magic: fixed64; // == 0xdb4775248b80fb57 (little-endian) - -"filter" Meta Block -------------------- - -If a "FilterPolicy" was specified when the database was opened, a -filter block is stored in each table. The "metaindex" block contains -an entry that maps from "filter." to the BlockHandle for the filter -block where "" is the string returned by the filter policy's -"Name()" method. - -The filter block stores a sequence of filters, where filter i contains -the output of FilterPolicy::CreateFilter() on all keys that are stored -in a block whose file offset falls within the range - - [ i*base ... (i+1)*base-1 ] - -Currently, "base" is 2KB. So for example, if blocks X and Y start in -the range [ 0KB .. 2KB-1 ], all of the keys in X and Y will be -converted to a filter by calling FilterPolicy::CreateFilter(), and the -resulting filter will be stored as the first filter in the filter -block. - -The filter block is formatted as follows: - - [filter 0] - [filter 1] - [filter 2] - ... - [filter N-1] - - [offset of filter 0] : 4 bytes - [offset of filter 1] : 4 bytes - [offset of filter 2] : 4 bytes - ... - [offset of filter N-1] : 4 bytes - - [offset of beginning of offset array] : 4 bytes - lg(base) : 1 byte - -The offset array at the end of the filter block allows efficient -mapping from a data block offset to the corresponding filter. - -"stats" Meta Block ------------------- - -This meta block contains a bunch of stats. The key is the name -of the statistic. The value contains the statistic. -TODO(postrelease): record following stats. - data size - index size - key size (uncompressed) - value size (uncompressed) - number of entries - number of data blocks diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc index 43ef2e072..47e4481f7 100644 --- a/src/leveldb/helpers/memenv/memenv.cc +++ b/src/leveldb/helpers/memenv/memenv.cc @@ -4,14 +4,18 @@ #include "helpers/memenv/memenv.h" +#include + +#include +#include +#include +#include + #include "leveldb/env.h" #include "leveldb/status.h" #include "port/port.h" +#include "port/thread_annotations.h" #include "util/mutexlock.h" -#include -#include -#include -#include namespace leveldb { @@ -23,6 +27,10 @@ class FileState { // and the caller must call Ref() at least once. FileState() : refs_(0), size_(0) {} + // No copying allowed. + FileState(const FileState&) = delete; + FileState& operator=(const FileState&) = delete; + // Increase the reference count. void Ref() { MutexLock lock(&refs_mutex_); @@ -47,9 +55,22 @@ class FileState { } } - uint64_t Size() const { return size_; } + uint64_t Size() const { + MutexLock lock(&blocks_mutex_); + return size_; + } + + void Truncate() { + MutexLock lock(&blocks_mutex_); + for (char*& block : blocks_) { + delete[] block; + } + blocks_.clear(); + size_ = 0; + } Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { + MutexLock lock(&blocks_mutex_); if (offset > size_) { return Status::IOError("Offset greater than file size."); } @@ -62,16 +83,9 @@ class FileState { return Status::OK(); } - assert(offset / kBlockSize <= SIZE_MAX); + assert(offset / kBlockSize <= std::numeric_limits::max()); size_t block = static_cast(offset / kBlockSize); size_t block_offset = offset % kBlockSize; - - if (n <= kBlockSize - block_offset) { - // The requested bytes are all in the first block. - *result = Slice(blocks_[block] + block_offset, n); - return Status::OK(); - } - size_t bytes_to_copy = n; char* dst = scratch; @@ -96,6 +110,7 @@ class FileState { const char* src = data.data(); size_t src_len = data.size(); + MutexLock lock(&blocks_mutex_); while (src_len > 0) { size_t avail; size_t offset = size_ % kBlockSize; @@ -122,28 +137,17 @@ class FileState { } private: - // Private since only Unref() should be used to delete it. - ~FileState() { - for (std::vector::iterator i = blocks_.begin(); i != blocks_.end(); - ++i) { - delete [] *i; - } - } + enum { kBlockSize = 8 * 1024 }; - // No copying allowed. - FileState(const FileState&); - void operator=(const FileState&); + // Private since only Unref() should be used to delete it. + ~FileState() { Truncate(); } port::Mutex refs_mutex_; - int refs_; // Protected by refs_mutex_; + int refs_ GUARDED_BY(refs_mutex_); - // The following fields are not protected by any mutex. They are only mutable - // while the file is being written, and concurrent access is not allowed - // to writable files. - std::vector blocks_; - uint64_t size_; - - enum { kBlockSize = 8 * 1024 }; + mutable port::Mutex blocks_mutex_; + std::vector blocks_ GUARDED_BY(blocks_mutex_); + uint64_t size_ GUARDED_BY(blocks_mutex_); }; class SequentialFileImpl : public SequentialFile { @@ -152,11 +156,9 @@ class SequentialFileImpl : public SequentialFile { file_->Ref(); } - ~SequentialFileImpl() { - file_->Unref(); - } + ~SequentialFileImpl() override { file_->Unref(); } - virtual Status Read(size_t n, Slice* result, char* scratch) { + Status Read(size_t n, Slice* result, char* scratch) override { Status s = file_->Read(pos_, n, result, scratch); if (s.ok()) { pos_ += result->size(); @@ -164,7 +166,7 @@ class SequentialFileImpl : public SequentialFile { return s; } - virtual Status Skip(uint64_t n) { + Status Skip(uint64_t n) override { if (pos_ > file_->Size()) { return Status::IOError("pos_ > file_->Size()"); } @@ -176,6 +178,7 @@ class SequentialFileImpl : public SequentialFile { return Status::OK(); } + virtual std::string GetName() const override { return "[memenv]"; } private: FileState* file_; uint64_t pos_; @@ -183,66 +186,58 @@ class SequentialFileImpl : public SequentialFile { class RandomAccessFileImpl : public RandomAccessFile { public: - explicit RandomAccessFileImpl(FileState* file) : file_(file) { - file_->Ref(); - } + explicit RandomAccessFileImpl(FileState* file) : file_(file) { file_->Ref(); } - ~RandomAccessFileImpl() { - file_->Unref(); - } + ~RandomAccessFileImpl() override { file_->Unref(); } - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { return file_->Read(offset, n, result, scratch); } + virtual std::string GetName() const override { return "[memenv]"; } private: FileState* file_; }; class WritableFileImpl : public WritableFile { public: - WritableFileImpl(FileState* file) : file_(file) { - file_->Ref(); - } + WritableFileImpl(FileState* file) : file_(file) { file_->Ref(); } - ~WritableFileImpl() { - file_->Unref(); - } + ~WritableFileImpl() override { file_->Unref(); } - virtual Status Append(const Slice& data) { - return file_->Append(data); - } + Status Append(const Slice& data) override { return file_->Append(data); } - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } + Status Close() override { return Status::OK(); } + Status Flush() override { return Status::OK(); } + Status Sync() override { return Status::OK(); } + virtual std::string GetName() const override { return "[memenv]"; } private: FileState* file_; }; class NoOpLogger : public Logger { public: - virtual void Logv(const char* format, va_list ap) { } + void Logv(const char* format, va_list ap) override {} }; class InMemoryEnv : public EnvWrapper { public: - explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { } + explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) {} - virtual ~InMemoryEnv() { - for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ - i->second->Unref(); + ~InMemoryEnv() override { + for (const auto& kvp : file_map_) { + kvp.second->Unref(); } } // Partial implementation of the Env interface. - virtual Status NewSequentialFile(const std::string& fname, - SequentialFile** result) { + Status NewSequentialFile(const std::string& fname, + SequentialFile** result) override { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { - *result = NULL; + *result = nullptr; return Status::IOError(fname, "File not found"); } @@ -250,11 +245,11 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status NewRandomAccessFile(const std::string& fname, - RandomAccessFile** result) { + Status NewRandomAccessFile(const std::string& fname, + RandomAccessFile** result) override { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { - *result = NULL; + *result = nullptr; return Status::IOError(fname, "File not found"); } @@ -262,33 +257,51 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) { + Status NewWritableFile(const std::string& fname, + WritableFile** result) override { MutexLock lock(&mutex_); - if (file_map_.find(fname) != file_map_.end()) { - DeleteFileInternal(fname); + FileSystem::iterator it = file_map_.find(fname); + + FileState* file; + if (it == file_map_.end()) { + // File is not currently open. + file = new FileState(); + file->Ref(); + file_map_[fname] = file; + } else { + file = it->second; + file->Truncate(); } - FileState* file = new FileState(); - file->Ref(); - file_map_[fname] = file; + *result = new WritableFileImpl(file); + return Status::OK(); + } + Status NewAppendableFile(const std::string& fname, + WritableFile** result) override { + MutexLock lock(&mutex_); + FileState** sptr = &file_map_[fname]; + FileState* file = *sptr; + if (file == nullptr) { + file = new FileState(); + file->Ref(); + } *result = new WritableFileImpl(file); return Status::OK(); } - virtual bool FileExists(const std::string& fname) { + bool FileExists(const std::string& fname) override { MutexLock lock(&mutex_); return file_map_.find(fname) != file_map_.end(); } - virtual Status GetChildren(const std::string& dir, - std::vector* result) { + Status GetChildren(const std::string& dir, + std::vector* result) override { MutexLock lock(&mutex_); result->clear(); - for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){ - const std::string& filename = i->first; + for (const auto& kvp : file_map_) { + const std::string& filename = kvp.first; if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' && Slice(filename).starts_with(Slice(dir))) { @@ -299,7 +312,8 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - void DeleteFileInternal(const std::string& fname) { + void DeleteFileInternal(const std::string& fname) + EXCLUSIVE_LOCKS_REQUIRED(mutex_) { if (file_map_.find(fname) == file_map_.end()) { return; } @@ -308,7 +322,7 @@ class InMemoryEnv : public EnvWrapper { file_map_.erase(fname); } - virtual Status DeleteFile(const std::string& fname) { + Status DeleteFile(const std::string& fname) override { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { return Status::IOError(fname, "File not found"); @@ -318,15 +332,11 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status CreateDir(const std::string& dirname) { - return Status::OK(); - } + Status CreateDir(const std::string& dirname) override { return Status::OK(); } - virtual Status DeleteDir(const std::string& dirname) { - return Status::OK(); - } + Status DeleteDir(const std::string& dirname) override { return Status::OK(); } - virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) { + Status GetFileSize(const std::string& fname, uint64_t* file_size) override { MutexLock lock(&mutex_); if (file_map_.find(fname) == file_map_.end()) { return Status::IOError(fname, "File not found"); @@ -336,8 +346,8 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status RenameFile(const std::string& src, - const std::string& target) { + Status RenameFile(const std::string& src, + const std::string& target) override { MutexLock lock(&mutex_); if (file_map_.find(src) == file_map_.end()) { return Status::IOError(src, "File not found"); @@ -349,22 +359,22 @@ class InMemoryEnv : public EnvWrapper { return Status::OK(); } - virtual Status LockFile(const std::string& fname, FileLock** lock) { + Status LockFile(const std::string& fname, FileLock** lock) override { *lock = new FileLock; return Status::OK(); } - virtual Status UnlockFile(FileLock* lock) { + Status UnlockFile(FileLock* lock) override { delete lock; return Status::OK(); } - virtual Status GetTestDirectory(std::string* path) { + Status GetTestDirectory(std::string* path) override { *path = "/test"; return Status::OK(); } - virtual Status NewLogger(const std::string& fname, Logger** result) { + Status NewLogger(const std::string& fname, Logger** result) override { *result = new NoOpLogger; return Status::OK(); } @@ -372,14 +382,13 @@ class InMemoryEnv : public EnvWrapper { private: // Map from filenames to FileState objects, representing a simple file system. typedef std::map FileSystem; + port::Mutex mutex_; - FileSystem file_map_; // Protected by mutex_. + FileSystem file_map_ GUARDED_BY(mutex_); }; } // namespace -Env* NewMemEnv(Env* base_env) { - return new InMemoryEnv(base_env); -} +Env* NewMemEnv(Env* base_env) { return new InMemoryEnv(base_env); } } // namespace leveldb diff --git a/src/leveldb/helpers/memenv/memenv.h b/src/leveldb/helpers/memenv/memenv.h index 03b88de76..3d929e4c4 100644 --- a/src/leveldb/helpers/memenv/memenv.h +++ b/src/leveldb/helpers/memenv/memenv.h @@ -5,6 +5,8 @@ #ifndef STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ #define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_ +#include "leveldb/export.h" + namespace leveldb { class Env; @@ -13,7 +15,7 @@ class Env; // all non-file-storage tasks to base_env. The caller must delete the result // when it is no longer needed. // *base_env must remain live while the result is in use. -Env* NewMemEnv(Env* base_env); +LEVELDB_EXPORT Env* NewMemEnv(Env* base_env); } // namespace leveldb diff --git a/src/leveldb/helpers/memenv/memenv_test.cc b/src/leveldb/helpers/memenv/memenv_test.cc index a44310fed..94ad06be6 100644 --- a/src/leveldb/helpers/memenv/memenv_test.cc +++ b/src/leveldb/helpers/memenv/memenv_test.cc @@ -4,25 +4,22 @@ #include "helpers/memenv/memenv.h" +#include +#include + #include "db/db_impl.h" #include "leveldb/db.h" #include "leveldb/env.h" #include "util/testharness.h" -#include -#include namespace leveldb { class MemEnvTest { public: - Env* env_; + MemEnvTest() : env_(NewMemEnv(Env::Default())) {} + ~MemEnvTest() { delete env_; } - MemEnvTest() - : env_(NewMemEnv(Env::Default())) { - } - ~MemEnvTest() { - delete env_; - } + Env* env_; }; TEST(MemEnvTest, Basics) { @@ -40,6 +37,8 @@ TEST(MemEnvTest, Basics) { // Create a file. ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(0, file_size); delete writable_file; // Check that the file exists. @@ -55,9 +54,16 @@ TEST(MemEnvTest, Basics) { ASSERT_OK(writable_file->Append("abc")); delete writable_file; - // Check for expected size. + // Check that append works. + ASSERT_OK(env_->NewAppendableFile("/dir/f", &writable_file)); ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); ASSERT_EQ(3, file_size); + ASSERT_OK(writable_file->Append("hello")); + delete writable_file; + + // Check for expected size. + ASSERT_OK(env_->GetFileSize("/dir/f", &file_size)); + ASSERT_EQ(8, file_size); // Check that renaming works. ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); @@ -65,7 +71,7 @@ TEST(MemEnvTest, Basics) { ASSERT_TRUE(!env_->FileExists("/dir/f")); ASSERT_TRUE(env_->FileExists("/dir/g")); ASSERT_OK(env_->GetFileSize("/dir/g", &file_size)); - ASSERT_EQ(3, file_size); + ASSERT_EQ(8, file_size); // Check that opening non-existent file fails. SequentialFile* seq_file; @@ -100,25 +106,25 @@ TEST(MemEnvTest, ReadWrite) { // Read sequentially. ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); - ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". + ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". ASSERT_EQ(0, result.compare("hello")); ASSERT_OK(seq_file->Skip(1)); - ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". ASSERT_EQ(0, result.compare("world")); - ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. + ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. ASSERT_EQ(0, result.size()); - ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. + ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. ASSERT_OK(seq_file->Read(1000, &result, scratch)); ASSERT_EQ(0, result.size()); delete seq_file; // Random reads. ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file)); - ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". + ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". ASSERT_EQ(0, result.compare("world")); - ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". + ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". ASSERT_EQ(0, result.compare("hello")); - ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". + ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". ASSERT_EQ(0, result.compare("d")); // Too high offset. @@ -167,7 +173,7 @@ TEST(MemEnvTest, LargeWrite) { SequentialFile* seq_file; Slice result; ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); - ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". + ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". ASSERT_EQ(0, result.compare("foo")); size_t read = 0; @@ -179,7 +185,30 @@ TEST(MemEnvTest, LargeWrite) { } ASSERT_TRUE(write_data == read_data); delete seq_file; - delete [] scratch; + delete[] scratch; +} + +TEST(MemEnvTest, OverwriteOpenFile) { + const char kWrite1Data[] = "Write #1 data"; + const size_t kFileDataLen = sizeof(kWrite1Data) - 1; + const std::string kTestFileName = test::TmpDir() + "/leveldb-TestFile.dat"; + + ASSERT_OK(WriteStringToFile(env_, kWrite1Data, kTestFileName)); + + RandomAccessFile* rand_file; + ASSERT_OK(env_->NewRandomAccessFile(kTestFileName, &rand_file)); + + const char kWrite2Data[] = "Write #2 data"; + ASSERT_OK(WriteStringToFile(env_, kWrite2Data, kTestFileName)); + + // Verify that overwriting an open file will result in the new file data + // being read from files opened before the write. + Slice result; + char scratch[kFileDataLen]; + ASSERT_OK(rand_file->Read(0, kFileDataLen, &result, scratch)); + ASSERT_EQ(0, result.compare(kWrite2Data)); + + delete rand_file; } TEST(MemEnvTest, DBTest) { @@ -227,6 +256,4 @@ TEST(MemEnvTest, DBTest) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/include/leveldb/c.h b/src/leveldb/include/leveldb/c.h index 1048fe3b8..02c79ba72 100644 --- a/src/leveldb/include/leveldb/c.h +++ b/src/leveldb/include/leveldb/c.h @@ -32,7 +32,7 @@ On failure, leveldb frees the old value of *errptr and set *errptr to a malloc()ed error message. - (4) Bools have the type unsigned char (0 == false; rest == true) + (4) Bools have the type uint8_t (0 == false; rest == true) (5) All of the pointer arguments must be non-NULL. */ @@ -48,225 +48,205 @@ extern "C" { #include #include +#include "leveldb/export.h" + /* Exported types */ -typedef struct leveldb_t leveldb_t; -typedef struct leveldb_cache_t leveldb_cache_t; -typedef struct leveldb_comparator_t leveldb_comparator_t; -typedef struct leveldb_env_t leveldb_env_t; -typedef struct leveldb_filelock_t leveldb_filelock_t; -typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; -typedef struct leveldb_iterator_t leveldb_iterator_t; -typedef struct leveldb_logger_t leveldb_logger_t; -typedef struct leveldb_options_t leveldb_options_t; -typedef struct leveldb_randomfile_t leveldb_randomfile_t; -typedef struct leveldb_readoptions_t leveldb_readoptions_t; -typedef struct leveldb_seqfile_t leveldb_seqfile_t; -typedef struct leveldb_snapshot_t leveldb_snapshot_t; -typedef struct leveldb_writablefile_t leveldb_writablefile_t; -typedef struct leveldb_writebatch_t leveldb_writebatch_t; -typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; +typedef struct leveldb_t leveldb_t; +typedef struct leveldb_cache_t leveldb_cache_t; +typedef struct leveldb_comparator_t leveldb_comparator_t; +typedef struct leveldb_env_t leveldb_env_t; +typedef struct leveldb_filelock_t leveldb_filelock_t; +typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t; +typedef struct leveldb_iterator_t leveldb_iterator_t; +typedef struct leveldb_logger_t leveldb_logger_t; +typedef struct leveldb_options_t leveldb_options_t; +typedef struct leveldb_randomfile_t leveldb_randomfile_t; +typedef struct leveldb_readoptions_t leveldb_readoptions_t; +typedef struct leveldb_seqfile_t leveldb_seqfile_t; +typedef struct leveldb_snapshot_t leveldb_snapshot_t; +typedef struct leveldb_writablefile_t leveldb_writablefile_t; +typedef struct leveldb_writebatch_t leveldb_writebatch_t; +typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; /* DB operations */ -extern leveldb_t* leveldb_open( - const leveldb_options_t* options, - const char* name, - char** errptr); +LEVELDB_EXPORT leveldb_t* leveldb_open(const leveldb_options_t* options, + const char* name, char** errptr); -extern void leveldb_close(leveldb_t* db); +LEVELDB_EXPORT void leveldb_close(leveldb_t* db); -extern void leveldb_put( - leveldb_t* db, - const leveldb_writeoptions_t* options, - const char* key, size_t keylen, - const char* val, size_t vallen, - char** errptr); +LEVELDB_EXPORT void leveldb_put(leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, const char* val, + size_t vallen, char** errptr); -extern void leveldb_delete( - leveldb_t* db, - const leveldb_writeoptions_t* options, - const char* key, size_t keylen, - char** errptr); +LEVELDB_EXPORT void leveldb_delete(leveldb_t* db, + const leveldb_writeoptions_t* options, + const char* key, size_t keylen, + char** errptr); -extern void leveldb_write( - leveldb_t* db, - const leveldb_writeoptions_t* options, - leveldb_writebatch_t* batch, - char** errptr); +LEVELDB_EXPORT void leveldb_write(leveldb_t* db, + const leveldb_writeoptions_t* options, + leveldb_writebatch_t* batch, char** errptr); /* Returns NULL if not found. A malloc()ed array otherwise. Stores the length of the array in *vallen. */ -extern char* leveldb_get( - leveldb_t* db, - const leveldb_readoptions_t* options, - const char* key, size_t keylen, - size_t* vallen, - char** errptr); +LEVELDB_EXPORT char* leveldb_get(leveldb_t* db, + const leveldb_readoptions_t* options, + const char* key, size_t keylen, size_t* vallen, + char** errptr); -extern leveldb_iterator_t* leveldb_create_iterator( - leveldb_t* db, - const leveldb_readoptions_t* options); +LEVELDB_EXPORT leveldb_iterator_t* leveldb_create_iterator( + leveldb_t* db, const leveldb_readoptions_t* options); -extern const leveldb_snapshot_t* leveldb_create_snapshot( - leveldb_t* db); +LEVELDB_EXPORT const leveldb_snapshot_t* leveldb_create_snapshot(leveldb_t* db); -extern void leveldb_release_snapshot( - leveldb_t* db, - const leveldb_snapshot_t* snapshot); +LEVELDB_EXPORT void leveldb_release_snapshot( + leveldb_t* db, const leveldb_snapshot_t* snapshot); /* Returns NULL if property name is unknown. Else returns a pointer to a malloc()-ed null-terminated value. */ -extern char* leveldb_property_value( - leveldb_t* db, - const char* propname); - -extern void leveldb_approximate_sizes( - leveldb_t* db, - int num_ranges, - const char* const* range_start_key, const size_t* range_start_key_len, - const char* const* range_limit_key, const size_t* range_limit_key_len, - uint64_t* sizes); - -extern void leveldb_compact_range( - leveldb_t* db, - const char* start_key, size_t start_key_len, - const char* limit_key, size_t limit_key_len); +LEVELDB_EXPORT char* leveldb_property_value(leveldb_t* db, + const char* propname); + +LEVELDB_EXPORT void leveldb_approximate_sizes( + leveldb_t* db, int num_ranges, const char* const* range_start_key, + const size_t* range_start_key_len, const char* const* range_limit_key, + const size_t* range_limit_key_len, uint64_t* sizes); + +LEVELDB_EXPORT void leveldb_compact_range(leveldb_t* db, const char* start_key, + size_t start_key_len, + const char* limit_key, + size_t limit_key_len); /* Management operations */ -extern void leveldb_destroy_db( - const leveldb_options_t* options, - const char* name, - char** errptr); +LEVELDB_EXPORT void leveldb_destroy_db(const leveldb_options_t* options, + const char* name, char** errptr); -extern void leveldb_repair_db( - const leveldb_options_t* options, - const char* name, - char** errptr); +LEVELDB_EXPORT void leveldb_repair_db(const leveldb_options_t* options, + const char* name, char** errptr); /* Iterator */ -extern void leveldb_iter_destroy(leveldb_iterator_t*); -extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); -extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); -extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); -extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); -extern void leveldb_iter_next(leveldb_iterator_t*); -extern void leveldb_iter_prev(leveldb_iterator_t*); -extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); -extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); -extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); +LEVELDB_EXPORT void leveldb_iter_destroy(leveldb_iterator_t*); +LEVELDB_EXPORT uint8_t leveldb_iter_valid(const leveldb_iterator_t*); +LEVELDB_EXPORT void leveldb_iter_seek_to_first(leveldb_iterator_t*); +LEVELDB_EXPORT void leveldb_iter_seek_to_last(leveldb_iterator_t*); +LEVELDB_EXPORT void leveldb_iter_seek(leveldb_iterator_t*, const char* k, + size_t klen); +LEVELDB_EXPORT void leveldb_iter_next(leveldb_iterator_t*); +LEVELDB_EXPORT void leveldb_iter_prev(leveldb_iterator_t*); +LEVELDB_EXPORT const char* leveldb_iter_key(const leveldb_iterator_t*, + size_t* klen); +LEVELDB_EXPORT const char* leveldb_iter_value(const leveldb_iterator_t*, + size_t* vlen); +LEVELDB_EXPORT void leveldb_iter_get_error(const leveldb_iterator_t*, + char** errptr); /* Write batch */ -extern leveldb_writebatch_t* leveldb_writebatch_create(); -extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); -extern void leveldb_writebatch_clear(leveldb_writebatch_t*); -extern void leveldb_writebatch_put( - leveldb_writebatch_t*, - const char* key, size_t klen, - const char* val, size_t vlen); -extern void leveldb_writebatch_delete( - leveldb_writebatch_t*, - const char* key, size_t klen); -extern void leveldb_writebatch_iterate( - leveldb_writebatch_t*, - void* state, +LEVELDB_EXPORT leveldb_writebatch_t* leveldb_writebatch_create(void); +LEVELDB_EXPORT void leveldb_writebatch_destroy(leveldb_writebatch_t*); +LEVELDB_EXPORT void leveldb_writebatch_clear(leveldb_writebatch_t*); +LEVELDB_EXPORT void leveldb_writebatch_put(leveldb_writebatch_t*, + const char* key, size_t klen, + const char* val, size_t vlen); +LEVELDB_EXPORT void leveldb_writebatch_delete(leveldb_writebatch_t*, + const char* key, size_t klen); +LEVELDB_EXPORT void leveldb_writebatch_iterate( + const leveldb_writebatch_t*, void* state, void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), void (*deleted)(void*, const char* k, size_t klen)); +LEVELDB_EXPORT void leveldb_writebatch_append( + leveldb_writebatch_t* destination, const leveldb_writebatch_t* source); /* Options */ -extern leveldb_options_t* leveldb_options_create(); -extern void leveldb_options_destroy(leveldb_options_t*); -extern void leveldb_options_set_comparator( - leveldb_options_t*, - leveldb_comparator_t*); -extern void leveldb_options_set_filter_policy( - leveldb_options_t*, - leveldb_filterpolicy_t*); -extern void leveldb_options_set_create_if_missing( - leveldb_options_t*, unsigned char); -extern void leveldb_options_set_error_if_exists( - leveldb_options_t*, unsigned char); -extern void leveldb_options_set_paranoid_checks( - leveldb_options_t*, unsigned char); -extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); -extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); -extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); -extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); -extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); -extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); -extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); - -enum { - leveldb_no_compression = 0, - leveldb_snappy_compression = 1 -}; -extern void leveldb_options_set_compression(leveldb_options_t*, int); +LEVELDB_EXPORT leveldb_options_t* leveldb_options_create(void); +LEVELDB_EXPORT void leveldb_options_destroy(leveldb_options_t*); +LEVELDB_EXPORT void leveldb_options_set_comparator(leveldb_options_t*, + leveldb_comparator_t*); +LEVELDB_EXPORT void leveldb_options_set_filter_policy(leveldb_options_t*, + leveldb_filterpolicy_t*); +LEVELDB_EXPORT void leveldb_options_set_create_if_missing(leveldb_options_t*, + uint8_t); +LEVELDB_EXPORT void leveldb_options_set_error_if_exists(leveldb_options_t*, + uint8_t); +LEVELDB_EXPORT void leveldb_options_set_paranoid_checks(leveldb_options_t*, + uint8_t); +LEVELDB_EXPORT void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); +LEVELDB_EXPORT void leveldb_options_set_info_log(leveldb_options_t*, + leveldb_logger_t*); +LEVELDB_EXPORT void leveldb_options_set_write_buffer_size(leveldb_options_t*, + size_t); +LEVELDB_EXPORT void leveldb_options_set_max_open_files(leveldb_options_t*, int); +LEVELDB_EXPORT void leveldb_options_set_cache(leveldb_options_t*, + leveldb_cache_t*); +LEVELDB_EXPORT void leveldb_options_set_block_size(leveldb_options_t*, size_t); +LEVELDB_EXPORT void leveldb_options_set_block_restart_interval( + leveldb_options_t*, int); +LEVELDB_EXPORT void leveldb_options_set_max_file_size(leveldb_options_t*, + size_t); + +enum { leveldb_no_compression = 0, leveldb_snappy_compression = 1 }; +LEVELDB_EXPORT void leveldb_options_set_compression(leveldb_options_t*, int); /* Comparator */ -extern leveldb_comparator_t* leveldb_comparator_create( - void* state, - void (*destructor)(void*), - int (*compare)( - void*, - const char* a, size_t alen, - const char* b, size_t blen), +LEVELDB_EXPORT leveldb_comparator_t* leveldb_comparator_create( + void* state, void (*destructor)(void*), + int (*compare)(void*, const char* a, size_t alen, const char* b, + size_t blen), const char* (*name)(void*)); -extern void leveldb_comparator_destroy(leveldb_comparator_t*); +LEVELDB_EXPORT void leveldb_comparator_destroy(leveldb_comparator_t*); /* Filter policy */ -extern leveldb_filterpolicy_t* leveldb_filterpolicy_create( - void* state, - void (*destructor)(void*), - char* (*create_filter)( - void*, - const char* const* key_array, const size_t* key_length_array, - int num_keys, - size_t* filter_length), - unsigned char (*key_may_match)( - void*, - const char* key, size_t length, - const char* filter, size_t filter_length), +LEVELDB_EXPORT leveldb_filterpolicy_t* leveldb_filterpolicy_create( + void* state, void (*destructor)(void*), + char* (*create_filter)(void*, const char* const* key_array, + const size_t* key_length_array, int num_keys, + size_t* filter_length), + uint8_t (*key_may_match)(void*, const char* key, size_t length, + const char* filter, size_t filter_length), const char* (*name)(void*)); -extern void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); +LEVELDB_EXPORT void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*); -extern leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( +LEVELDB_EXPORT leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom( int bits_per_key); /* Read options */ -extern leveldb_readoptions_t* leveldb_readoptions_create(); -extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); -extern void leveldb_readoptions_set_verify_checksums( - leveldb_readoptions_t*, - unsigned char); -extern void leveldb_readoptions_set_fill_cache( - leveldb_readoptions_t*, unsigned char); -extern void leveldb_readoptions_set_snapshot( - leveldb_readoptions_t*, - const leveldb_snapshot_t*); +LEVELDB_EXPORT leveldb_readoptions_t* leveldb_readoptions_create(void); +LEVELDB_EXPORT void leveldb_readoptions_destroy(leveldb_readoptions_t*); +LEVELDB_EXPORT void leveldb_readoptions_set_verify_checksums( + leveldb_readoptions_t*, uint8_t); +LEVELDB_EXPORT void leveldb_readoptions_set_fill_cache(leveldb_readoptions_t*, + uint8_t); +LEVELDB_EXPORT void leveldb_readoptions_set_snapshot(leveldb_readoptions_t*, + const leveldb_snapshot_t*); /* Write options */ -extern leveldb_writeoptions_t* leveldb_writeoptions_create(); -extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); -extern void leveldb_writeoptions_set_sync( - leveldb_writeoptions_t*, unsigned char); +LEVELDB_EXPORT leveldb_writeoptions_t* leveldb_writeoptions_create(void); +LEVELDB_EXPORT void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); +LEVELDB_EXPORT void leveldb_writeoptions_set_sync(leveldb_writeoptions_t*, + uint8_t); /* Cache */ -extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); -extern void leveldb_cache_destroy(leveldb_cache_t* cache); +LEVELDB_EXPORT leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); +LEVELDB_EXPORT void leveldb_cache_destroy(leveldb_cache_t* cache); /* Env */ -extern leveldb_env_t* leveldb_create_default_env(); -extern void leveldb_env_destroy(leveldb_env_t*); +LEVELDB_EXPORT leveldb_env_t* leveldb_create_default_env(void); +LEVELDB_EXPORT void leveldb_env_destroy(leveldb_env_t*); + +/* If not NULL, the returned buffer must be released using leveldb_free(). */ +LEVELDB_EXPORT char* leveldb_env_get_test_directory(leveldb_env_t*); /* Utility */ @@ -275,16 +255,16 @@ extern void leveldb_env_destroy(leveldb_env_t*); in this file. Note that in certain cases (typically on Windows), you may need to call this routine instead of free(ptr) to dispose of malloc()-ed memory returned by this library. */ -extern void leveldb_free(void* ptr); +LEVELDB_EXPORT void leveldb_free(void* ptr); /* Return the major version number for this release. */ -extern int leveldb_major_version(); +LEVELDB_EXPORT int leveldb_major_version(void); /* Return the minor version number for this release. */ -extern int leveldb_minor_version(); +LEVELDB_EXPORT int leveldb_minor_version(void); #ifdef __cplusplus -} /* end extern "C" */ +} /* end extern "C" */ #endif -#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ +#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ diff --git a/src/leveldb/include/leveldb/cache.h b/src/leveldb/include/leveldb/cache.h index 1a201e5e0..7d1a22119 100644 --- a/src/leveldb/include/leveldb/cache.h +++ b/src/leveldb/include/leveldb/cache.h @@ -19,26 +19,31 @@ #define STORAGE_LEVELDB_INCLUDE_CACHE_H_ #include + +#include "leveldb/export.h" #include "leveldb/slice.h" namespace leveldb { -class Cache; +class LEVELDB_EXPORT Cache; // Create a new cache with a fixed size capacity. This implementation // of Cache uses a least-recently-used eviction policy. -extern Cache* NewLRUCache(size_t capacity); +LEVELDB_EXPORT Cache* NewLRUCache(size_t capacity); -class Cache { +class LEVELDB_EXPORT Cache { public: - Cache() { } + Cache() = default; + + Cache(const Cache&) = delete; + Cache& operator=(const Cache&) = delete; // Destroys all existing entries by calling the "deleter" // function that was passed to the constructor. virtual ~Cache(); // Opaque handle to an entry stored in the cache. - struct Handle { }; + struct Handle {}; // Insert a mapping from key->value into the cache and assign it // the specified charge against the total cache capacity. @@ -52,7 +57,7 @@ class Cache { virtual Handle* Insert(const Slice& key, void* value, size_t charge, void (*deleter)(const Slice& key, void* value)) = 0; - // If the cache has no mapping for "key", returns NULL. + // If the cache has no mapping for "key", returns nullptr. // // Else return a handle that corresponds to the mapping. The caller // must call this->Release(handle) when the returned mapping is no @@ -81,6 +86,17 @@ class Cache { // its cache keys. virtual uint64_t NewId() = 0; + // Remove all cache entries that are not actively in use. Memory-constrained + // applications may wish to call this method to reduce memory usage. + // Default implementation of Prune() does nothing. Subclasses are strongly + // encouraged to override the default implementation. A future release of + // leveldb may change Prune() to a pure abstract method. + virtual void Prune() {} + + // Return an estimate of the combined charges of all elements stored in the + // cache. + virtual size_t TotalCharge() const = 0; + private: void LRU_Remove(Handle* e); void LRU_Append(Handle* e); @@ -88,10 +104,6 @@ class Cache { struct Rep; Rep* rep_; - - // No copying allowed - Cache(const Cache&); - void operator=(const Cache&); }; } // namespace leveldb diff --git a/src/leveldb/include/leveldb/comparator.h b/src/leveldb/include/leveldb/comparator.h index 556b984c7..a85b51ebd 100644 --- a/src/leveldb/include/leveldb/comparator.h +++ b/src/leveldb/include/leveldb/comparator.h @@ -7,6 +7,8 @@ #include +#include "leveldb/export.h" + namespace leveldb { class Slice; @@ -15,7 +17,7 @@ class Slice; // used as keys in an sstable or a database. A Comparator implementation // must be thread-safe since leveldb may invoke its methods concurrently // from multiple threads. -class Comparator { +class LEVELDB_EXPORT Comparator { public: virtual ~Comparator(); @@ -43,9 +45,8 @@ class Comparator { // If *start < limit, changes *start to a short string in [start,limit). // Simple comparator implementations may return with *start unchanged, // i.e., an implementation of this method that does nothing is correct. - virtual void FindShortestSeparator( - std::string* start, - const Slice& limit) const = 0; + virtual void FindShortestSeparator(std::string* start, + const Slice& limit) const = 0; // Changes *key to a short string >= *key. // Simple comparator implementations may return with *key unchanged, @@ -56,7 +57,7 @@ class Comparator { // Return a builtin comparator that uses lexicographic byte-wise // ordering. The result remains the property of this module and // must not be deleted. -extern const Comparator* BytewiseComparator(); +LEVELDB_EXPORT const Comparator* BytewiseComparator(); } // namespace leveldb diff --git a/src/leveldb/include/leveldb/db.h b/src/leveldb/include/leveldb/db.h index 4c169bf22..b73014a22 100644 --- a/src/leveldb/include/leveldb/db.h +++ b/src/leveldb/include/leveldb/db.h @@ -7,14 +7,16 @@ #include #include + +#include "leveldb/export.h" #include "leveldb/iterator.h" #include "leveldb/options.h" namespace leveldb { -// Update Makefile if you change these +// Update CMakeLists.txt if you change these static const int kMajorVersion = 1; -static const int kMinorVersion = 18; +static const int kMinorVersion = 22; struct Options; struct ReadOptions; @@ -24,42 +26,44 @@ class WriteBatch; // Abstract handle to particular state of a DB. // A Snapshot is an immutable object and can therefore be safely // accessed from multiple threads without any external synchronization. -class Snapshot { +class LEVELDB_EXPORT Snapshot { protected: virtual ~Snapshot(); }; // A range of keys -struct Range { - Slice start; // Included in the range - Slice limit; // Not included in the range +struct LEVELDB_EXPORT Range { + Range() = default; + Range(const Slice& s, const Slice& l) : start(s), limit(l) {} - Range() { } - Range(const Slice& s, const Slice& l) : start(s), limit(l) { } + Slice start; // Included in the range + Slice limit; // Not included in the range }; // A DB is a persistent ordered map from keys to values. // A DB is safe for concurrent access from multiple threads without // any external synchronization. -class DB { +class LEVELDB_EXPORT DB { public: // Open the database with the specified "name". // Stores a pointer to a heap-allocated database in *dbptr and returns // OK on success. - // Stores NULL in *dbptr and returns a non-OK status on error. + // Stores nullptr in *dbptr and returns a non-OK status on error. // Caller should delete *dbptr when it is no longer needed. - static Status Open(const Options& options, - const std::string& name, + static Status Open(const Options& options, const std::string& name, DB** dbptr); - DB() { } + DB() = default; + + DB(const DB&) = delete; + DB& operator=(const DB&) = delete; + virtual ~DB(); // Set the database entry for "key" to "value". Returns OK on success, // and a non-OK status on error. // Note: consider setting options.sync = true. - virtual Status Put(const WriteOptions& options, - const Slice& key, + virtual Status Put(const WriteOptions& options, const Slice& key, const Slice& value) = 0; // Remove the database entry (if any) for "key". Returns OK on @@ -80,8 +84,8 @@ class DB { // a status for which Status::IsNotFound() returns true. // // May return some other Status on an error. - virtual Status Get(const ReadOptions& options, - const Slice& key, std::string* value) = 0; + virtual Status Get(const ReadOptions& options, const Slice& key, + std::string* value) = 0; // Return a heap-allocated iterator over the contents of the database. // The result of NewIterator() is initially invalid (caller must @@ -115,6 +119,8 @@ class DB { // about the internal operation of the DB. // "leveldb.sstables" - returns a multi-line string that describes all // of the sstables that make up the db contents. + // "leveldb.approximate-memory-usage" - returns the approximate number of + // bytes of memory in use by the DB. virtual bool GetProperty(const Slice& property, std::string* value) = 0; // For each i in [0,n-1], store in "sizes[i]", the approximate @@ -134,27 +140,27 @@ class DB { // needed to access the data. This operation should typically only // be invoked by users who understand the underlying implementation. // - // begin==NULL is treated as a key before all keys in the database. - // end==NULL is treated as a key after all keys in the database. + // begin==nullptr is treated as a key before all keys in the database. + // end==nullptr is treated as a key after all keys in the database. // Therefore the following call will compact the entire database: - // db->CompactRange(NULL, NULL); + // db->CompactRange(nullptr, nullptr); virtual void CompactRange(const Slice* begin, const Slice* end) = 0; - - private: - // No copying allowed - DB(const DB&); - void operator=(const DB&); }; // Destroy the contents of the specified database. // Be very careful using this method. -Status DestroyDB(const std::string& name, const Options& options); +// +// Note: For backwards compatibility, if DestroyDB is unable to list the +// database files, Status::OK() will still be returned masking this failure. +LEVELDB_EXPORT Status DestroyDB(const std::string& name, + const Options& options); // If a DB cannot be opened, you may attempt to call this method to // resurrect as much of the contents of the database as possible. // Some data may be lost, so be careful when calling this function // on a database that contains important information. -Status RepairDB(const std::string& dbname, const Options& options); +LEVELDB_EXPORT Status RepairDB(const std::string& dbname, + const Options& options); } // namespace leveldb diff --git a/src/leveldb/include/leveldb/dumpfile.h b/src/leveldb/include/leveldb/dumpfile.h index 3f97fda16..a58bc6b36 100644 --- a/src/leveldb/include/leveldb/dumpfile.h +++ b/src/leveldb/include/leveldb/dumpfile.h @@ -6,7 +6,9 @@ #define STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_ #include + #include "leveldb/env.h" +#include "leveldb/export.h" #include "leveldb/status.h" namespace leveldb { @@ -18,7 +20,8 @@ namespace leveldb { // // Returns a non-OK result if fname does not name a leveldb storage // file, or if the file cannot be read. -Status DumpFile(Env* env, const std::string& fname, WritableFile* dst); +LEVELDB_EXPORT Status DumpFile(Env* env, const std::string& fname, + WritableFile* dst); } // namespace leveldb diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h index f709514da..96c21b396 100644 --- a/src/leveldb/include/leveldb/env.h +++ b/src/leveldb/include/leveldb/env.h @@ -13,12 +13,36 @@ #ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ #define STORAGE_LEVELDB_INCLUDE_ENV_H_ -#include -#include #include #include + +#include +#include + +#include "leveldb/export.h" #include "leveldb/status.h" +#if defined(_WIN32) +// The leveldb::Env class below contains a DeleteFile method. +// At the same time, , a fairly popular header +// file for Windows applications, defines a DeleteFile macro. +// +// Without any intervention on our part, the result of this +// unfortunate coincidence is that the name of the +// leveldb::Env::DeleteFile method seen by the compiler depends on +// whether was included before or after the LevelDB +// headers. +// +// To avoid headaches, we undefined DeleteFile (if defined) and +// redefine it at the bottom of this file. This way +// can be included before this file (or not at all) and the +// exported method will always be leveldb::Env::DeleteFile. +#if defined(DeleteFile) +#undef DeleteFile +#define LEVELDB_DELETEFILE_UNDEFINED +#endif // defined(DeleteFile) +#endif // defined(_WIN32) + namespace leveldb { class FileLock; @@ -28,9 +52,13 @@ class SequentialFile; class Slice; class WritableFile; -class Env { +class LEVELDB_EXPORT Env { public: - Env() { } + Env() = default; + + Env(const Env&) = delete; + Env& operator=(const Env&) = delete; + virtual ~Env(); // Return a default environment suitable for the current operating @@ -40,20 +68,22 @@ class Env { // The result of Default() belongs to leveldb and must never be deleted. static Env* Default(); - // Create a brand new sequentially-readable file with the specified name. + // Create an object that sequentially reads the file with the specified name. // On success, stores a pointer to the new file in *result and returns OK. - // On failure stores NULL in *result and returns non-OK. If the file does - // not exist, returns a non-OK status. + // On failure stores nullptr in *result and returns non-OK. If the file does + // not exist, returns a non-OK status. Implementations should return a + // NotFound status when the file does not exist. // // The returned file will only be accessed by one thread at a time. virtual Status NewSequentialFile(const std::string& fname, SequentialFile** result) = 0; - // Create a brand new random access read-only file with the + // Create an object supporting random-access reads from the file with the // specified name. On success, stores a pointer to the new file in - // *result and returns OK. On failure stores NULL in *result and + // *result and returns OK. On failure stores nullptr in *result and // returns non-OK. If the file does not exist, returns a non-OK - // status. + // status. Implementations should return a NotFound status when the file does + // not exist. // // The returned file may be concurrently accessed by multiple threads. virtual Status NewRandomAccessFile(const std::string& fname, @@ -62,13 +92,28 @@ class Env { // Create an object that writes to a new file with the specified // name. Deletes any existing file with the same name and creates a // new file. On success, stores a pointer to the new file in - // *result and returns OK. On failure stores NULL in *result and + // *result and returns OK. On failure stores nullptr in *result and // returns non-OK. // // The returned file will only be accessed by one thread at a time. virtual Status NewWritableFile(const std::string& fname, WritableFile** result) = 0; + // Create an object that either appends to an existing file, or + // writes to a new file (if the file does not exist to begin with). + // On success, stores a pointer to the new file in *result and + // returns OK. On failure stores nullptr in *result and returns + // non-OK. + // + // The returned file will only be accessed by one thread at a time. + // + // May return an IsNotSupportedError error if this Env does + // not allow appending to an existing file. Users of Env (including + // the leveldb implementation) must be prepared to deal with + // an Env that does not support appending. + virtual Status NewAppendableFile(const std::string& fname, + WritableFile** result); + // Returns true iff the named file exists. virtual bool FileExists(const std::string& fname) = 0; @@ -95,7 +140,7 @@ class Env { const std::string& target) = 0; // Lock the specified file. Used to prevent concurrent access to - // the same db by multiple processes. On failure, stores NULL in + // the same db by multiple processes. On failure, stores nullptr in // *lock and returns non-OK. // // On success, stores a pointer to the object that represents the @@ -121,16 +166,14 @@ class Env { // added to the same Env may run concurrently in different threads. // I.e., the caller may not assume that background work items are // serialized. - virtual void Schedule( - void (*function)(void* arg), - void* arg) = 0; + virtual void Schedule(void (*function)(void* arg), void* arg) = 0; // Start a new thread, invoking "function(arg)" within the new thread. // When "function(arg)" returns, the thread will be destroyed. virtual void StartThread(void (*function)(void* arg), void* arg) = 0; // *path is set to a temporary directory that can be used for testing. It may - // or many not have just been created. The directory may or may not differ + // or may not have just been created. The directory may or may not differ // between runs of the same process, but subsequent calls will return the // same directory. virtual Status GetTestDirectory(std::string* path) = 0; @@ -144,17 +187,16 @@ class Env { // Sleep/delay the thread for the prescribed number of micro-seconds. virtual void SleepForMicroseconds(int micros) = 0; - - private: - // No copying allowed - Env(const Env&); - void operator=(const Env&); }; // A file abstraction for reading sequentially through a file -class SequentialFile { +class LEVELDB_EXPORT SequentialFile { public: - SequentialFile() { } + SequentialFile() = default; + + SequentialFile(const SequentialFile&) = delete; + SequentialFile& operator=(const SequentialFile&) = delete; + virtual ~SequentialFile(); // Read up to "n" bytes from the file. "scratch[0..n-1]" may be @@ -176,16 +218,18 @@ class SequentialFile { // REQUIRES: External synchronization virtual Status Skip(uint64_t n) = 0; - private: - // No copying allowed - SequentialFile(const SequentialFile&); - void operator=(const SequentialFile&); + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; }; // A file abstraction for randomly reading the contents of a file. -class RandomAccessFile { +class LEVELDB_EXPORT RandomAccessFile { public: - RandomAccessFile() { } + RandomAccessFile() = default; + + RandomAccessFile(const RandomAccessFile&) = delete; + RandomAccessFile& operator=(const RandomAccessFile&) = delete; + virtual ~RandomAccessFile(); // Read up to "n" bytes from the file starting at "offset". @@ -200,18 +244,20 @@ class RandomAccessFile { virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const = 0; - private: - // No copying allowed - RandomAccessFile(const RandomAccessFile&); - void operator=(const RandomAccessFile&); + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; }; // A file abstraction for sequential writing. The implementation // must provide buffering since callers may append small fragments // at a time to the file. -class WritableFile { +class LEVELDB_EXPORT WritableFile { public: - WritableFile() { } + WritableFile() = default; + + WritableFile(const WritableFile&) = delete; + WritableFile& operator=(const WritableFile&) = delete; + virtual ~WritableFile(); virtual Status Append(const Slice& data) = 0; @@ -219,115 +265,132 @@ class WritableFile { virtual Status Flush() = 0; virtual Status Sync() = 0; - private: - // No copying allowed - WritableFile(const WritableFile&); - void operator=(const WritableFile&); + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; }; // An interface for writing log messages. -class Logger { +class LEVELDB_EXPORT Logger { public: - Logger() { } + Logger() = default; + + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + virtual ~Logger(); // Write an entry to the log file with the specified format. virtual void Logv(const char* format, va_list ap) = 0; - - private: - // No copying allowed - Logger(const Logger&); - void operator=(const Logger&); }; - // Identifies a locked file. -class FileLock { +class LEVELDB_EXPORT FileLock { public: - FileLock() { } + FileLock() = default; + + FileLock(const FileLock&) = delete; + FileLock& operator=(const FileLock&) = delete; + virtual ~FileLock(); - private: - // No copying allowed - FileLock(const FileLock&); - void operator=(const FileLock&); }; -// Log the specified data to *info_log if info_log is non-NULL. -extern void Log(Logger* info_log, const char* format, ...) -# if defined(__GNUC__) || defined(__clang__) - __attribute__((__format__ (__printf__, 2, 3))) -# endif +// Log the specified data to *info_log if info_log is non-null. +void Log(Logger* info_log, const char* format, ...) +#if defined(__GNUC__) || defined(__clang__) + __attribute__((__format__(__printf__, 2, 3))) +#endif ; // A utility routine: write "data" to the named file. -extern Status WriteStringToFile(Env* env, const Slice& data, - const std::string& fname); +LEVELDB_EXPORT Status WriteStringToFile(Env* env, const Slice& data, + const std::string& fname); // A utility routine: read contents of named file into *data -extern Status ReadFileToString(Env* env, const std::string& fname, - std::string* data); +LEVELDB_EXPORT Status ReadFileToString(Env* env, const std::string& fname, + std::string* data); // An implementation of Env that forwards all calls to another Env. // May be useful to clients who wish to override just part of the // functionality of another Env. -class EnvWrapper : public Env { +class LEVELDB_EXPORT EnvWrapper : public Env { public: - // Initialize an EnvWrapper that delegates all calls to *t - explicit EnvWrapper(Env* t) : target_(t) { } + // Initialize an EnvWrapper that delegates all calls to *t. + explicit EnvWrapper(Env* t) : target_(t) {} virtual ~EnvWrapper(); - // Return the target to which this Env forwards all calls + // Return the target to which this Env forwards all calls. Env* target() const { return target_; } - // The following text is boilerplate that forwards all methods to target() - Status NewSequentialFile(const std::string& f, SequentialFile** r) { + // The following text is boilerplate that forwards all methods to target(). + Status NewSequentialFile(const std::string& f, SequentialFile** r) override { return target_->NewSequentialFile(f, r); } - Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { + Status NewRandomAccessFile(const std::string& f, + RandomAccessFile** r) override { return target_->NewRandomAccessFile(f, r); } - Status NewWritableFile(const std::string& f, WritableFile** r) { + Status NewWritableFile(const std::string& f, WritableFile** r) override { return target_->NewWritableFile(f, r); } - bool FileExists(const std::string& f) { return target_->FileExists(f); } - Status GetChildren(const std::string& dir, std::vector* r) { + Status NewAppendableFile(const std::string& f, WritableFile** r) override { + return target_->NewAppendableFile(f, r); + } + bool FileExists(const std::string& f) override { + return target_->FileExists(f); + } + Status GetChildren(const std::string& dir, + std::vector* r) override { return target_->GetChildren(dir, r); } - Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } - Status CreateDir(const std::string& d) { return target_->CreateDir(d); } - Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } - Status GetFileSize(const std::string& f, uint64_t* s) { + Status DeleteFile(const std::string& f) override { + return target_->DeleteFile(f); + } + Status CreateDir(const std::string& d) override { + return target_->CreateDir(d); + } + Status DeleteDir(const std::string& d) override { + return target_->DeleteDir(d); + } + Status GetFileSize(const std::string& f, uint64_t* s) override { return target_->GetFileSize(f, s); } - Status RenameFile(const std::string& s, const std::string& t) { + Status RenameFile(const std::string& s, const std::string& t) override { return target_->RenameFile(s, t); } - Status LockFile(const std::string& f, FileLock** l) { + Status LockFile(const std::string& f, FileLock** l) override { return target_->LockFile(f, l); } - Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } - void Schedule(void (*f)(void*), void* a) { + Status UnlockFile(FileLock* l) override { return target_->UnlockFile(l); } + void Schedule(void (*f)(void*), void* a) override { return target_->Schedule(f, a); } - void StartThread(void (*f)(void*), void* a) { + void StartThread(void (*f)(void*), void* a) override { return target_->StartThread(f, a); } - virtual Status GetTestDirectory(std::string* path) { + Status GetTestDirectory(std::string* path) override { return target_->GetTestDirectory(path); } - virtual Status NewLogger(const std::string& fname, Logger** result) { + Status NewLogger(const std::string& fname, Logger** result) override { return target_->NewLogger(fname, result); } - uint64_t NowMicros() { - return target_->NowMicros(); - } - void SleepForMicroseconds(int micros) { + uint64_t NowMicros() override { return target_->NowMicros(); } + void SleepForMicroseconds(int micros) override { target_->SleepForMicroseconds(micros); } + private: Env* target_; }; } // namespace leveldb +// Redefine DeleteFile if necessary. +#if defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED) +#if defined(UNICODE) +#define DeleteFile DeleteFileW +#else +#define DeleteFile DeleteFileA +#endif // defined(UNICODE) +#endif // defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED) + #endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ diff --git a/src/leveldb/include/leveldb/export.h b/src/leveldb/include/leveldb/export.h new file mode 100644 index 000000000..6ba9b183d --- /dev/null +++ b/src/leveldb/include/leveldb/export.h @@ -0,0 +1,33 @@ +// Copyright (c) 2017 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_INCLUDE_EXPORT_H_ +#define STORAGE_LEVELDB_INCLUDE_EXPORT_H_ + +#if !defined(LEVELDB_EXPORT) + +#if defined(LEVELDB_SHARED_LIBRARY) +#if defined(_WIN32) + +#if defined(LEVELDB_COMPILE_LIBRARY) +#define LEVELDB_EXPORT __declspec(dllexport) +#else +#define LEVELDB_EXPORT __declspec(dllimport) +#endif // defined(LEVELDB_COMPILE_LIBRARY) + +#else // defined(_WIN32) +#if defined(LEVELDB_COMPILE_LIBRARY) +#define LEVELDB_EXPORT __attribute__((visibility("default"))) +#else +#define LEVELDB_EXPORT +#endif +#endif // defined(_WIN32) + +#else // defined(LEVELDB_SHARED_LIBRARY) +#define LEVELDB_EXPORT +#endif + +#endif // !defined(LEVELDB_EXPORT) + +#endif // STORAGE_LEVELDB_INCLUDE_EXPORT_H_ diff --git a/src/leveldb/include/leveldb/filter_policy.h b/src/leveldb/include/leveldb/filter_policy.h index 1fba08001..49c8eda77 100644 --- a/src/leveldb/include/leveldb/filter_policy.h +++ b/src/leveldb/include/leveldb/filter_policy.h @@ -18,11 +18,13 @@ #include +#include "leveldb/export.h" + namespace leveldb { class Slice; -class FilterPolicy { +class LEVELDB_EXPORT FilterPolicy { public: virtual ~FilterPolicy(); @@ -38,8 +40,8 @@ class FilterPolicy { // // Warning: do not change the initial contents of *dst. Instead, // append the newly constructed filter to *dst. - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) - const = 0; + virtual void CreateFilter(const Slice* keys, int n, + std::string* dst) const = 0; // "filter" contains the data appended by a preceding call to // CreateFilter() on this class. This method must return true if @@ -63,8 +65,8 @@ class FilterPolicy { // ignores trailing spaces, it would be incorrect to use a // FilterPolicy (like NewBloomFilterPolicy) that does not ignore // trailing spaces in keys. -extern const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); +LEVELDB_EXPORT const FilterPolicy* NewBloomFilterPolicy(int bits_per_key); -} +} // namespace leveldb #endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_ diff --git a/src/leveldb/include/leveldb/iterator.h b/src/leveldb/include/leveldb/iterator.h index 76aced04b..bb9a5df8f 100644 --- a/src/leveldb/include/leveldb/iterator.h +++ b/src/leveldb/include/leveldb/iterator.h @@ -15,14 +15,19 @@ #ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ #define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_ +#include "leveldb/export.h" #include "leveldb/slice.h" #include "leveldb/status.h" namespace leveldb { -class Iterator { +class LEVELDB_EXPORT Iterator { public: Iterator(); + + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + virtual ~Iterator(); // An iterator is either positioned at a key/value pair, or @@ -37,7 +42,7 @@ class Iterator { // Valid() after this call iff the source is not empty. virtual void SeekToLast() = 0; - // Position at the first key in the source that at or past target + // Position at the first key in the source that is at or past target. // The iterator is Valid() after this call iff the source contains // an entry that comes at or past target. virtual void Seek(const Slice& target) = 0; @@ -72,28 +77,35 @@ class Iterator { // // Note that unlike all of the preceding methods, this method is // not abstract and therefore clients should not override it. - typedef void (*CleanupFunction)(void* arg1, void* arg2); + using CleanupFunction = void (*)(void* arg1, void* arg2); void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2); private: - struct Cleanup { + // Cleanup functions are stored in a single-linked list. + // The list's head node is inlined in the iterator. + struct CleanupNode { + // True if the node is not used. Only head nodes might be unused. + bool IsEmpty() const { return function == nullptr; } + // Invokes the cleanup function. + void Run() { + assert(function != nullptr); + (*function)(arg1, arg2); + } + + // The head node is used if the function pointer is not null. CleanupFunction function; void* arg1; void* arg2; - Cleanup* next; + CleanupNode* next; }; - Cleanup cleanup_; - - // No copying allowed - Iterator(const Iterator&); - void operator=(const Iterator&); + CleanupNode cleanup_head_; }; // Return an empty iterator (yields nothing). -extern Iterator* NewEmptyIterator(); +LEVELDB_EXPORT Iterator* NewEmptyIterator(); // Return an empty iterator with the specified status. -extern Iterator* NewErrorIterator(const Status& status); +LEVELDB_EXPORT Iterator* NewErrorIterator(const Status& status); } // namespace leveldb diff --git a/src/leveldb/include/leveldb/options.h b/src/leveldb/include/leveldb/options.h index 7c9b97345..b7487726b 100644 --- a/src/leveldb/include/leveldb/options.h +++ b/src/leveldb/include/leveldb/options.h @@ -7,6 +7,8 @@ #include +#include "leveldb/export.h" + namespace leveldb { class Cache; @@ -23,12 +25,15 @@ class Snapshot; enum CompressionType { // NOTE: do not change the values of existing entries, as these are // part of the persistent format on disk. - kNoCompression = 0x0, + kNoCompression = 0x0, kSnappyCompression = 0x1 }; // Options to control the behavior of a database (passed to DB::Open) -struct Options { +struct LEVELDB_EXPORT Options { + // Create an Options object with default values for all fields. + Options(); + // ------------------- // Parameters that affect behavior @@ -41,20 +46,17 @@ struct Options { const Comparator* comparator; // If true, the database will be created if it is missing. - // Default: false - bool create_if_missing; + bool create_if_missing = false; // If true, an error is raised if the database already exists. - // Default: false - bool error_if_exists; + bool error_if_exists = false; // If true, the implementation will do aggressive checking of the // data it is processing and will stop early if it detects any // errors. This may have unforeseen ramifications: for example, a // corruption of one DB entry may cause a large number of entries to // become unreadable or for the entire DB to become unopenable. - // Default: false - bool paranoid_checks; + bool paranoid_checks = false; // Use the specified object to interact with the environment, // e.g. to read/write files, schedule background work, etc. @@ -62,10 +64,9 @@ struct Options { Env* env; // Any internal progress/error information generated by the db will - // be written to info_log if it is non-NULL, or to a file stored - // in the same directory as the DB contents if info_log is NULL. - // Default: NULL - Logger* info_log; + // be written to info_log if it is non-null, or to a file stored + // in the same directory as the DB contents if info_log is null. + Logger* info_log = nullptr; // ------------------- // Parameters that affect performance @@ -78,39 +79,40 @@ struct Options { // so you may wish to adjust this parameter to control memory usage. // Also, a larger write buffer will result in a longer recovery time // the next time the database is opened. - // - // Default: 4MB - size_t write_buffer_size; + size_t write_buffer_size = 4 * 1024 * 1024; // Number of open files that can be used by the DB. You may need to // increase this if your database has a large working set (budget // one open file per 2MB of working set). - // - // Default: 1000 - int max_open_files; + int max_open_files = 1000; // Control over blocks (user data is stored in a set of blocks, and // a block is the unit of reading from disk). - // If non-NULL, use the specified cache for blocks. - // If NULL, leveldb will automatically create and use an 8MB internal cache. - // Default: NULL - Cache* block_cache; + // If non-null, use the specified cache for blocks. + // If null, leveldb will automatically create and use an 8MB internal cache. + Cache* block_cache = nullptr; // Approximate size of user data packed per block. Note that the // block size specified here corresponds to uncompressed data. The // actual size of the unit read from disk may be smaller if // compression is enabled. This parameter can be changed dynamically. - // - // Default: 4K - size_t block_size; + size_t block_size = 4 * 1024; // Number of keys between restart points for delta encoding of keys. // This parameter can be changed dynamically. Most clients should // leave this parameter alone. - // - // Default: 16 - int block_restart_interval; + int block_restart_interval = 16; + + // Leveldb will write up to this amount of bytes to a file before + // switching to a new one. + // Most clients should leave this parameter alone. However if your + // filesystem is more efficient with larger files, you could + // consider increasing the value. The downside will be longer + // compactions and hence longer latency/performance hiccups. + // Another reason to increase this parameter might be when you are + // initially populating a large database. + size_t max_file_size = 2 * 1024 * 1024; // Compress blocks using the specified compression algorithm. This // parameter can be changed dynamically. @@ -126,47 +128,43 @@ struct Options { // worth switching to kNoCompression. Even if the input data is // incompressible, the kSnappyCompression implementation will // efficiently detect that and will switch to uncompressed mode. - CompressionType compression; + CompressionType compression = kSnappyCompression; - // If non-NULL, use the specified filter policy to reduce disk reads. - // Many applications will benefit from passing the result of - // NewBloomFilterPolicy() here. + // EXPERIMENTAL: If true, append to existing MANIFEST and log files + // when a database is opened. This can significantly speed up open. // - // Default: NULL - const FilterPolicy* filter_policy; + // Default: currently false, but may become true later. + bool reuse_logs = false; - // Create an Options object with default values for all fields. - Options(); + // If non-null, use the specified filter policy to reduce disk reads. + // Many applications will benefit from passing the result of + // NewBloomFilterPolicy() here. + const FilterPolicy* filter_policy = nullptr; }; // Options that control read operations -struct ReadOptions { +struct LEVELDB_EXPORT ReadOptions { + ReadOptions() = default; + // If true, all data read from underlying storage will be // verified against corresponding checksums. - // Default: false - bool verify_checksums; + bool verify_checksums = false; // Should the data read for this iteration be cached in memory? // Callers may wish to set this field to false for bulk scans. - // Default: true - bool fill_cache; + bool fill_cache = true; - // If "snapshot" is non-NULL, read as of the supplied snapshot + // If "snapshot" is non-null, read as of the supplied snapshot // (which must belong to the DB that is being read and which must - // not have been released). If "snapshot" is NULL, use an implicit + // not have been released). If "snapshot" is null, use an implicit // snapshot of the state at the beginning of this read operation. - // Default: NULL - const Snapshot* snapshot; - - ReadOptions() - : verify_checksums(false), - fill_cache(true), - snapshot(NULL) { - } + const Snapshot* snapshot = nullptr; }; // Options that control write operations -struct WriteOptions { +struct LEVELDB_EXPORT WriteOptions { + WriteOptions() = default; + // If true, the write will be flushed from the operating system // buffer cache (by calling WritableFile::Sync()) before the write // is considered complete. If this flag is true, writes will be @@ -181,13 +179,7 @@ struct WriteOptions { // crash semantics as the "write()" system call. A DB write // with sync==true has similar crash semantics to a "write()" // system call followed by "fsync()". - // - // Default: false - bool sync; - - WriteOptions() - : sync(false) { - } + bool sync = false; }; } // namespace leveldb diff --git a/src/leveldb/include/leveldb/slice.h b/src/leveldb/include/leveldb/slice.h index bc367986f..2df417dc3 100644 --- a/src/leveldb/include/leveldb/slice.h +++ b/src/leveldb/include/leveldb/slice.h @@ -18,23 +18,30 @@ #include #include #include + #include +#include "leveldb/export.h" + namespace leveldb { -class Slice { +class LEVELDB_EXPORT Slice { public: // Create an empty slice. - Slice() : data_(""), size_(0) { } + Slice() : data_(""), size_(0) {} // Create a slice that refers to d[0,n-1]. - Slice(const char* d, size_t n) : data_(d), size_(n) { } + Slice(const char* d, size_t n) : data_(d), size_(n) {} // Create a slice that refers to the contents of "s" - Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } + Slice(const std::string& s) : data_(s.data()), size_(s.size()) {} // Create a slice that refers to s[0,strlen(s)-1] - Slice(const char* s) : data_(s), size_(strlen(s)) { } + Slice(const char* s) : data_(s), size_(strlen(s)) {} + + // Intentionally copyable. + Slice(const Slice&) = default; + Slice& operator=(const Slice&) = default; // Return a pointer to the beginning of the referenced data const char* data() const { return data_; } @@ -53,7 +60,10 @@ class Slice { } // Change this slice to refer to an empty array - void clear() { data_ = ""; size_ = 0; } + void clear() { + data_ = ""; + size_ = 0; + } // Drop the first "n" bytes from this slice. void remove_prefix(size_t n) { @@ -73,15 +83,12 @@ class Slice { // Return true iff "x" is a prefix of "*this" bool starts_with(const Slice& x) const { - return ((size_ >= x.size_) && - (memcmp(data_, x.data_, x.size_) == 0)); + return ((size_ >= x.size_) && (memcmp(data_, x.data_, x.size_) == 0)); } private: const char* data_; size_t size_; - - // Intentionally copyable }; inline bool operator==(const Slice& x, const Slice& y) { @@ -89,21 +96,20 @@ inline bool operator==(const Slice& x, const Slice& y) { (memcmp(x.data(), y.data(), x.size()) == 0)); } -inline bool operator!=(const Slice& x, const Slice& y) { - return !(x == y); -} +inline bool operator!=(const Slice& x, const Slice& y) { return !(x == y); } inline int Slice::compare(const Slice& b) const { const size_t min_len = (size_ < b.size_) ? size_ : b.size_; int r = memcmp(data_, b.data_, min_len); if (r == 0) { - if (size_ < b.size_) r = -1; - else if (size_ > b.size_) r = +1; + if (size_ < b.size_) + r = -1; + else if (size_ > b.size_) + r = +1; } return r; } } // namespace leveldb - #endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ diff --git a/src/leveldb/include/leveldb/status.h b/src/leveldb/include/leveldb/status.h index 11dbd4b47..68efe3001 100644 --- a/src/leveldb/include/leveldb/status.h +++ b/src/leveldb/include/leveldb/status.h @@ -13,20 +13,25 @@ #ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ #define STORAGE_LEVELDB_INCLUDE_STATUS_H_ +#include #include + +#include "leveldb/export.h" #include "leveldb/slice.h" namespace leveldb { -class Status { +class LEVELDB_EXPORT Status { public: // Create a success status. - Status() : state_(NULL) { } + Status() noexcept : state_(nullptr) {} ~Status() { delete[] state_; } - // Copy the specified status. - Status(const Status& s); - void operator=(const Status& s); + Status(const Status& rhs); + Status& operator=(const Status& rhs); + + Status(Status&& rhs) noexcept : state_(rhs.state_) { rhs.state_ = nullptr; } + Status& operator=(Status&& rhs) noexcept; // Return a success status. static Status OK() { return Status(); } @@ -49,7 +54,7 @@ class Status { } // Returns true iff the status indicates success. - bool ok() const { return (state_ == NULL); } + bool ok() const { return (state_ == nullptr); } // Returns true iff the status indicates a NotFound error. bool IsNotFound() const { return code() == kNotFound; } @@ -60,18 +65,17 @@ class Status { // Returns true iff the status indicates an IOError. bool IsIOError() const { return code() == kIOError; } + // Returns true iff the status indicates a NotSupportedError. + bool IsNotSupportedError() const { return code() == kNotSupported; } + + // Returns true iff the status indicates an InvalidArgument. + bool IsInvalidArgument() const { return code() == kInvalidArgument; } + // Return a string representation of this status suitable for printing. // Returns the string "OK" for success. std::string ToString() const; private: - // OK status has a NULL state_. Otherwise, state_ is a new[] array - // of the following form: - // state_[0..3] == length of message - // state_[4] == code - // state_[5..] == message - const char* state_; - enum Code { kOk = 0, kNotFound = 1, @@ -82,23 +86,39 @@ class Status { }; Code code() const { - return (state_ == NULL) ? kOk : static_cast(state_[4]); + return (state_ == nullptr) ? kOk : static_cast(state_[4]); } Status(Code code, const Slice& msg, const Slice& msg2); static const char* CopyState(const char* s); + + // OK status has a null state_. Otherwise, state_ is a new[] array + // of the following form: + // state_[0..3] == length of message + // state_[4] == code + // state_[5..] == message + const char* state_; }; -inline Status::Status(const Status& s) { - state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); +inline Status::Status(const Status& rhs) { + state_ = (rhs.state_ == nullptr) ? nullptr : CopyState(rhs.state_); } -inline void Status::operator=(const Status& s) { - // The following condition catches both aliasing (when this == &s), - // and the common case where both s and *this are ok. - if (state_ != s.state_) { + +// NOLINTBEGIN(bugprone-unhandled-self-assignment) +inline Status& Status::operator=(const Status& rhs) { + // The following condition catches both aliasing (when this == &rhs), + // and the common case where both rhs and *this are ok. + if (state_ != rhs.state_) { delete[] state_; - state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + state_ = (rhs.state_ == nullptr) ? nullptr : CopyState(rhs.state_); } + return *this; +} +// NOLINTEND(bugprone-unhandled-self-assignment) + +inline Status& Status::operator=(Status&& rhs) noexcept { + std::swap(state_, rhs.state_); + return *this; } } // namespace leveldb diff --git a/src/leveldb/include/leveldb/table.h b/src/leveldb/include/leveldb/table.h index a9746c3f5..25c601311 100644 --- a/src/leveldb/include/leveldb/table.h +++ b/src/leveldb/include/leveldb/table.h @@ -6,6 +6,8 @@ #define STORAGE_LEVELDB_INCLUDE_TABLE_H_ #include + +#include "leveldb/export.h" #include "leveldb/iterator.h" namespace leveldb { @@ -21,7 +23,7 @@ class TableCache; // A Table is a sorted map from strings to strings. Tables are // immutable and persistent. A Table may be safely accessed from // multiple threads without external synchronization. -class Table { +class LEVELDB_EXPORT Table { public: // Attempt to open the table that is stored in bytes [0..file_size) // of "file", and read the metadata entries necessary to allow @@ -30,15 +32,16 @@ class Table { // If successful, returns ok and sets "*table" to the newly opened // table. The client should delete "*table" when no longer needed. // If there was an error while initializing the table, sets "*table" - // to NULL and returns a non-ok status. Does not take ownership of + // to nullptr and returns a non-ok status. Does not take ownership of // "*source", but the client must ensure that "source" remains live // for the duration of the returned table's lifetime. // // *file must remain live while this Table is in use. - static Status Open(const Options& options, - RandomAccessFile* file, - uint64_t file_size, - Table** table); + static Status Open(const Options& options, RandomAccessFile* file, + uint64_t file_size, Table** table); + + Table(const Table&) = delete; + Table& operator=(const Table&) = delete; ~Table(); @@ -56,28 +59,24 @@ class Table { uint64_t ApproximateOffsetOf(const Slice& key) const; private: + friend class TableCache; struct Rep; - Rep* rep_; - explicit Table(Rep* rep) { rep_ = rep; } static Iterator* BlockReader(void*, const ReadOptions&, const Slice&); + explicit Table(Rep* rep) : rep_(rep) {} + // Calls (*handle_result)(arg, ...) with the entry found after a call // to Seek(key). May not make such a call if filter policy says // that key is not present. - friend class TableCache; - Status InternalGet( - const ReadOptions&, const Slice& key, - void* arg, - void (*handle_result)(void* arg, const Slice& k, const Slice& v)); - + Status InternalGet(const ReadOptions&, const Slice& key, void* arg, + void (*handle_result)(void* arg, const Slice& k, + const Slice& v)); void ReadMeta(const Footer& footer); void ReadFilter(const Slice& filter_handle_value); - // No copying allowed - Table(const Table&); - void operator=(const Table&); + Rep* const rep_; }; } // namespace leveldb diff --git a/src/leveldb/include/leveldb/table_builder.h b/src/leveldb/include/leveldb/table_builder.h index 5fd1dc71f..7d8896bb8 100644 --- a/src/leveldb/include/leveldb/table_builder.h +++ b/src/leveldb/include/leveldb/table_builder.h @@ -14,6 +14,8 @@ #define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_ #include + +#include "leveldb/export.h" #include "leveldb/options.h" #include "leveldb/status.h" @@ -23,13 +25,16 @@ class BlockBuilder; class BlockHandle; class WritableFile; -class TableBuilder { +class LEVELDB_EXPORT TableBuilder { public: // Create a builder that will store the contents of the table it is // building in *file. Does not close the file. It is up to the // caller to close the file after calling Finish(). TableBuilder(const Options& options, WritableFile* file); + TableBuilder(const TableBuilder&) = delete; + TableBuilder& operator=(const TableBuilder&) = delete; + // REQUIRES: Either Finish() or Abandon() has been called. ~TableBuilder(); @@ -81,10 +86,6 @@ class TableBuilder { struct Rep; Rep* rep_; - - // No copying allowed - TableBuilder(const TableBuilder&); - void operator=(const TableBuilder&); }; } // namespace leveldb diff --git a/src/leveldb/include/leveldb/write_batch.h b/src/leveldb/include/leveldb/write_batch.h index ee9aab68e..94d4115fe 100644 --- a/src/leveldb/include/leveldb/write_batch.h +++ b/src/leveldb/include/leveldb/write_batch.h @@ -22,15 +22,29 @@ #define STORAGE_LEVELDB_INCLUDE_WRITE_BATCH_H_ #include + +#include "leveldb/export.h" #include "leveldb/status.h" namespace leveldb { class Slice; -class WriteBatch { +class LEVELDB_EXPORT WriteBatch { public: + class LEVELDB_EXPORT Handler { + public: + virtual ~Handler(); + virtual void Put(const Slice& key, const Slice& value) = 0; + virtual void Delete(const Slice& key) = 0; + }; + WriteBatch(); + + // Intentionally copyable. + WriteBatch(const WriteBatch&) = default; + WriteBatch& operator=(const WriteBatch&) = default; + ~WriteBatch(); // Store the mapping "key->value" in the database. @@ -42,21 +56,26 @@ class WriteBatch { // Clear all updates buffered in this batch. void Clear(); + // The size of the database changes caused by this batch. + // + // This number is tied to implementation details, and may change across + // releases. It is intended for LevelDB usage metrics. + size_t ApproximateSize() const; + + // Copies the operations in "source" to this batch. + // + // This runs in O(source size) time. However, the constant factor is better + // than calling Iterate() over the source batch with a Handler that replicates + // the operations into this batch. + void Append(const WriteBatch& source); + // Support for iterating over the contents of a batch. - class Handler { - public: - virtual ~Handler(); - virtual void Put(const Slice& key, const Slice& value) = 0; - virtual void Delete(const Slice& key) = 0; - }; Status Iterate(Handler* handler) const; private: friend class WriteBatchInternal; std::string rep_; // See comment in write_batch.cc for the format of rep_ - - // Intentionally copyable }; } // namespace leveldb diff --git a/src/leveldb/issues/issue178_test.cc b/src/leveldb/issues/issue178_test.cc index 1b1cf8bb2..d50ffeb9d 100644 --- a/src/leveldb/issues/issue178_test.cc +++ b/src/leveldb/issues/issue178_test.cc @@ -3,9 +3,9 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. // Test for issue 178: a manual compaction causes deleted data to reappear. +#include #include #include -#include #include "leveldb/db.h" #include "leveldb/write_batch.h" @@ -21,11 +21,9 @@ std::string Key1(int i) { return buf; } -std::string Key2(int i) { - return Key1(i) + "_xxx"; -} +std::string Key2(int i) { return Key1(i) + "_xxx"; } -class Issue178 { }; +class Issue178 {}; TEST(Issue178, Test) { // Get rid of any state from an old run. @@ -87,6 +85,4 @@ TEST(Issue178, Test) { } // anonymous namespace -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/issues/issue200_test.cc b/src/leveldb/issues/issue200_test.cc index 1cec79f44..877b2afc4 100644 --- a/src/leveldb/issues/issue200_test.cc +++ b/src/leveldb/issues/issue200_test.cc @@ -11,14 +11,14 @@ namespace leveldb { -class Issue200 { }; +class Issue200 {}; TEST(Issue200, Test) { // Get rid of any state from an old run. std::string dbpath = test::TmpDir() + "/leveldb_issue200_test"; DestroyDB(dbpath, Options()); - DB *db; + DB* db; Options options; options.create_if_missing = true; ASSERT_OK(DB::Open(options, dbpath, &db)); @@ -31,7 +31,7 @@ TEST(Issue200, Test) { ASSERT_OK(db->Put(write_options, "5", "f")); ReadOptions read_options; - Iterator *iter = db->NewIterator(read_options); + Iterator* iter = db->NewIterator(read_options); // Add an element that should not be reflected in the iterator. ASSERT_OK(db->Put(write_options, "25", "cd")); @@ -54,6 +54,4 @@ TEST(Issue200, Test) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/issues/issue320_test.cc b/src/leveldb/issues/issue320_test.cc new file mode 100644 index 000000000..c5fcbfc6e --- /dev/null +++ b/src/leveldb/issues/issue320_test.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2019 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include +#include +#include +#include + +#include "leveldb/db.h" +#include "leveldb/write_batch.h" +#include "util/testharness.h" + +namespace leveldb { + +namespace { + +// Creates a random number in the range of [0, max). +int GenerateRandomNumber(int max) { return std::rand() % max; } + +std::string CreateRandomString(int32_t index) { + static const size_t len = 1024; + char bytes[len]; + size_t i = 0; + while (i < 8) { + bytes[i] = 'a' + ((index >> (4 * i)) & 0xf); + ++i; + } + while (i < sizeof(bytes)) { + bytes[i] = 'a' + GenerateRandomNumber(26); + ++i; + } + return std::string(bytes, sizeof(bytes)); +} + +} // namespace + +class Issue320 {}; + +TEST(Issue320, Test) { + std::srand(0); + + bool delete_before_put = false; + bool keep_snapshots = true; + + std::vector>> test_map( + 10000); + std::vector snapshots(100, nullptr); + + DB* db; + Options options; + options.create_if_missing = true; + + std::string dbpath = test::TmpDir() + "/leveldb_issue320_test"; + ASSERT_OK(DB::Open(options, dbpath, &db)); + + uint32_t target_size = 10000; + uint32_t num_items = 0; + uint32_t count = 0; + std::string key; + std::string value, old_value; + + WriteOptions writeOptions; + ReadOptions readOptions; + while (count < 200000) { + if ((++count % 1000) == 0) { + std::cout << "count: " << count << std::endl; + } + + int index = GenerateRandomNumber(test_map.size()); + WriteBatch batch; + + if (test_map[index] == nullptr) { + num_items++; + test_map[index].reset(new std::pair( + CreateRandomString(index), CreateRandomString(index))); + batch.Put(test_map[index]->first, test_map[index]->second); + } else { + ASSERT_OK(db->Get(readOptions, test_map[index]->first, &old_value)); + if (old_value != test_map[index]->second) { + std::cout << "ERROR incorrect value returned by Get" << std::endl; + std::cout << " count=" << count << std::endl; + std::cout << " old value=" << old_value << std::endl; + std::cout << " test_map[index]->second=" << test_map[index]->second + << std::endl; + std::cout << " test_map[index]->first=" << test_map[index]->first + << std::endl; + std::cout << " index=" << index << std::endl; + ASSERT_EQ(old_value, test_map[index]->second); + } + + if (num_items >= target_size && GenerateRandomNumber(100) > 30) { + batch.Delete(test_map[index]->first); + test_map[index] = nullptr; + --num_items; + } else { + test_map[index]->second = CreateRandomString(index); + if (delete_before_put) batch.Delete(test_map[index]->first); + batch.Put(test_map[index]->first, test_map[index]->second); + } + } + + ASSERT_OK(db->Write(writeOptions, &batch)); + + if (keep_snapshots && GenerateRandomNumber(10) == 0) { + int i = GenerateRandomNumber(snapshots.size()); + if (snapshots[i] != nullptr) { + db->ReleaseSnapshot(snapshots[i]); + } + snapshots[i] = db->GetSnapshot(); + } + } + + for (Snapshot const* snapshot : snapshots) { + if (snapshot) { + db->ReleaseSnapshot(snapshot); + } + } + + delete db; + DestroyDB(dbpath, options); +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/port/README b/src/leveldb/port/README.md similarity index 82% rename from src/leveldb/port/README rename to src/leveldb/port/README.md index 422563e25..8b171532e 100644 --- a/src/leveldb/port/README +++ b/src/leveldb/port/README.md @@ -5,6 +5,6 @@ Code in the rest of the package includes "port.h" from this directory. "port.h" in turn includes a platform specific "port_.h" file that provides the platform specific implementation. -See port_posix.h for an example of what must be provided in a platform +See port_stdcxx.h for an example of what must be provided in a platform specific header file. diff --git a/src/leveldb/port/atomic_pointer.h b/src/leveldb/port/atomic_pointer.h deleted file mode 100644 index 9bf091f75..000000000 --- a/src/leveldb/port/atomic_pointer.h +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -// AtomicPointer provides storage for a lock-free pointer. -// Platform-dependent implementation of AtomicPointer: -// - If the platform provides a cheap barrier, we use it with raw pointers -// - If is present (on newer versions of gcc, it is), we use -// a -based AtomicPointer. However we prefer the memory -// barrier based version, because at least on a gcc 4.4 32-bit build -// on linux, we have encountered a buggy implementation. -// Also, some implementations are much slower than a memory-barrier -// based implementation (~16ns for based acquire-load vs. ~1ns for -// a barrier based acquire-load). -// This code is based on atomicops-internals-* in Google's perftools: -// http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase - -#ifndef PORT_ATOMIC_POINTER_H_ -#define PORT_ATOMIC_POINTER_H_ - -#include -#ifdef LEVELDB_ATOMIC_PRESENT -#include -#endif -#ifdef OS_WIN -#include -#endif -#ifdef OS_MACOSX -#include -#endif - -#if defined(_M_X64) || defined(__x86_64__) -#define ARCH_CPU_X86_FAMILY 1 -#elif defined(_M_IX86) || defined(__i386__) || defined(__i386) -#define ARCH_CPU_X86_FAMILY 1 -#elif defined(__ARMEL__) -#define ARCH_CPU_ARM_FAMILY 1 -#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) -#define ARCH_CPU_PPC_FAMILY 1 -#endif - -namespace leveldb { -namespace port { - -// Define MemoryBarrier() if available -// Windows on x86 -#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) -// windows.h already provides a MemoryBarrier(void) macro -// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx -#define LEVELDB_HAVE_MEMORY_BARRIER - -// Mac OS -#elif defined(OS_MACOSX) -inline void MemoryBarrier() { - OSMemoryBarrier(); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// Gcc on x86 -#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) -inline void MemoryBarrier() { - // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on - // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. - __asm__ __volatile__("" : : : "memory"); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// Sun Studio -#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) -inline void MemoryBarrier() { - // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on - // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. - asm volatile("" : : : "memory"); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// ARM Linux -#elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__) -typedef void (*LinuxKernelMemoryBarrierFunc)(void); -// The Linux ARM kernel provides a highly optimized device-specific memory -// barrier function at a fixed memory address that is mapped in every -// user-level process. -// -// This beats using CPU-specific instructions which are, on single-core -// devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more -// than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking -// shows that the extra function call cost is completely negligible on -// multi-core devices. -// -inline void MemoryBarrier() { - (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -// PPC -#elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__) -inline void MemoryBarrier() { - // TODO for some powerpc expert: is there a cheaper suitable variant? - // Perhaps by having separate barriers for acquire and release ops. - asm volatile("sync" : : : "memory"); -} -#define LEVELDB_HAVE_MEMORY_BARRIER - -#endif - -// AtomicPointer built using platform-specific MemoryBarrier() -#if defined(LEVELDB_HAVE_MEMORY_BARRIER) -class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* p) : rep_(p) {} - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } - inline void* Acquire_Load() const { - void* result = rep_; - MemoryBarrier(); - return result; - } - inline void Release_Store(void* v) { - MemoryBarrier(); - rep_ = v; - } -}; - -// AtomicPointer based on -#elif defined(LEVELDB_ATOMIC_PRESENT) -class AtomicPointer { - private: - std::atomic rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - return rep_.load(std::memory_order_acquire); - } - inline void Release_Store(void* v) { - rep_.store(v, std::memory_order_release); - } - inline void* NoBarrier_Load() const { - return rep_.load(std::memory_order_relaxed); - } - inline void NoBarrier_Store(void* v) { - rep_.store(v, std::memory_order_relaxed); - } -}; - -// Atomic pointer based on sparc memory barriers -#elif defined(__sparcv9) && defined(__GNUC__) -class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - void* val; - __asm__ __volatile__ ( - "ldx [%[rep_]], %[val] \n\t" - "membar #LoadLoad|#LoadStore \n\t" - : [val] "=r" (val) - : [rep_] "r" (&rep_) - : "memory"); - return val; - } - inline void Release_Store(void* v) { - __asm__ __volatile__ ( - "membar #LoadStore|#StoreStore \n\t" - "stx %[v], [%[rep_]] \n\t" - : - : [rep_] "r" (&rep_), [v] "r" (v) - : "memory"); - } - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } -}; - -// Atomic pointer based on ia64 acq/rel -#elif defined(__ia64) && defined(__GNUC__) -class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - void* val ; - __asm__ __volatile__ ( - "ld8.acq %[val] = [%[rep_]] \n\t" - : [val] "=r" (val) - : [rep_] "r" (&rep_) - : "memory" - ); - return val; - } - inline void Release_Store(void* v) { - __asm__ __volatile__ ( - "st8.rel [%[rep_]] = %[v] \n\t" - : - : [rep_] "r" (&rep_), [v] "r" (v) - : "memory" - ); - } - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } -}; - -// We have neither MemoryBarrier(), nor -#else -#error Please implement AtomicPointer for this platform. - -#endif - -#undef LEVELDB_HAVE_MEMORY_BARRIER -#undef ARCH_CPU_X86_FAMILY -#undef ARCH_CPU_ARM_FAMILY -#undef ARCH_CPU_PPC_FAMILY - -} // namespace port -} // namespace leveldb - -#endif // PORT_ATOMIC_POINTER_H_ diff --git a/src/leveldb/port/port.h b/src/leveldb/port/port.h index 4baafa8e2..4b247f74f 100644 --- a/src/leveldb/port/port.h +++ b/src/leveldb/port/port.h @@ -10,12 +10,10 @@ // Include the appropriate platform specific file below. If you are // porting to a new platform, see "port_example.h" for documentation // of what the new port_.h file must provide. -#if defined(LEVELDB_PLATFORM_POSIX) -# include "port/port_posix.h" +#if defined(LEVELDB_PLATFORM_POSIX) || defined(LEVELDB_PLATFORM_WINDOWS) +#include "port/port_stdcxx.h" #elif defined(LEVELDB_PLATFORM_CHROMIUM) -# include "port/port_chromium.h" -#elif defined(LEVELDB_PLATFORM_WINDOWS) -# include "port/port_win.h" +#include "port/port_chromium.h" #endif #endif // STORAGE_LEVELDB_PORT_PORT_H_ diff --git a/src/leveldb/port/port_config.h.in b/src/leveldb/port/port_config.h.in new file mode 100644 index 000000000..21273153a --- /dev/null +++ b/src/leveldb/port/port_config.h.in @@ -0,0 +1,39 @@ +// Copyright 2017 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_CONFIG_H_ +#define STORAGE_LEVELDB_PORT_PORT_CONFIG_H_ + +// Define to 1 if you have a definition for fdatasync() in . +#if !defined(HAVE_FDATASYNC) +#cmakedefine01 HAVE_FDATASYNC +#endif // !defined(HAVE_FDATASYNC) + +// Define to 1 if you have a definition for F_FULLFSYNC in . +#if !defined(HAVE_FULLFSYNC) +#cmakedefine01 HAVE_FULLFSYNC +#endif // !defined(HAVE_FULLFSYNC) + +// Define to 1 if you have a definition for O_CLOEXEC in . +#if !defined(HAVE_O_CLOEXEC) +#cmakedefine01 HAVE_O_CLOEXEC +#endif // !defined(HAVE_O_CLOEXEC) + +// Define to 1 if you have Google CRC32C. +#if !defined(HAVE_CRC32C) +#cmakedefine01 HAVE_CRC32C +#endif // !defined(HAVE_CRC32C) + +// Define to 1 if you have Google Snappy. +#if !defined(HAVE_SNAPPY) +#cmakedefine01 HAVE_SNAPPY +#endif // !defined(HAVE_SNAPPY) + +// Define to 1 if your processor stores words with the most significant byte +// first (like Motorola and SPARC, unlike Intel and VAX). +#if !defined(LEVELDB_IS_BIG_ENDIAN) +#cmakedefine01 LEVELDB_IS_BIG_ENDIAN +#endif // !defined(LEVELDB_IS_BIG_ENDIAN) + +#endif // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_ \ No newline at end of file diff --git a/src/leveldb/port/port_example.h b/src/leveldb/port/port_example.h index ab9e489b3..1a8fca24b 100644 --- a/src/leveldb/port/port_example.h +++ b/src/leveldb/port/port_example.h @@ -10,6 +10,8 @@ #ifndef STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ #define STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_ +#include "port/thread_annotations.h" + namespace leveldb { namespace port { @@ -23,23 +25,23 @@ static const bool kLittleEndian = true /* or some other expression */; // ------------------ Threading ------------------- // A Mutex represents an exclusive lock. -class Mutex { +class LOCKABLE Mutex { public: Mutex(); ~Mutex(); // Lock the mutex. Waits until other lockers have exited. // Will deadlock if the mutex is already locked by this thread. - void Lock(); + void Lock() EXCLUSIVE_LOCK_FUNCTION(); // Unlock the mutex. // REQUIRES: This mutex was locked by this thread. - void Unlock(); + void Unlock() UNLOCK_FUNCTION(); // Optionally crash if this thread does not hold this mutex. // The implementation must be fast, especially if NDEBUG is // defined. The implementation is allowed to skip all checks. - void AssertHeld(); + void AssertHeld() ASSERT_EXCLUSIVE_LOCK(); }; class CondVar { @@ -60,57 +62,18 @@ class CondVar { void SignallAll(); }; -// Thread-safe initialization. -// Used as follows: -// static port::OnceType init_control = LEVELDB_ONCE_INIT; -// static void Initializer() { ... do something ...; } -// ... -// port::InitOnce(&init_control, &Initializer); -typedef intptr_t OnceType; -#define LEVELDB_ONCE_INIT 0 -extern void InitOnce(port::OnceType*, void (*initializer)()); - -// A type that holds a pointer that can be read or written atomically -// (i.e., without word-tearing.) -class AtomicPointer { - private: - intptr_t rep_; - public: - // Initialize to arbitrary value - AtomicPointer(); - - // Initialize to hold v - explicit AtomicPointer(void* v) : rep_(v) { } - - // Read and return the stored pointer with the guarantee that no - // later memory access (read or write) by this thread can be - // reordered ahead of this read. - void* Acquire_Load() const; - - // Set v as the stored pointer with the guarantee that no earlier - // memory access (read or write) by this thread can be reordered - // after this store. - void Release_Store(void* v); - - // Read the stored pointer with no ordering guarantees. - void* NoBarrier_Load() const; - - // Set va as the stored pointer with no ordering guarantees. - void NoBarrier_Store(void* v); -}; - // ------------------ Compression ------------------- // Store the snappy compression of "input[0,input_length-1]" in *output. // Returns false if snappy is not supported by this port. -extern bool Snappy_Compress(const char* input, size_t input_length, - std::string* output); +bool Snappy_Compress(const char* input, size_t input_length, + std::string* output); // If input[0,input_length-1] looks like a valid snappy compressed // buffer, store the size of the uncompressed data in *result and // return true. Else return false. -extern bool Snappy_GetUncompressedLength(const char* input, size_t length, - size_t* result); +bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result); // Attempt to snappy uncompress input[0,input_length-1] into *output. // Returns true if successful, false if the input is invalid lightweight @@ -119,15 +82,21 @@ extern bool Snappy_GetUncompressedLength(const char* input, size_t length, // REQUIRES: at least the first "n" bytes of output[] must be writable // where "n" is the result of a successful call to // Snappy_GetUncompressedLength. -extern bool Snappy_Uncompress(const char* input_data, size_t input_length, - char* output); +bool Snappy_Uncompress(const char* input_data, size_t input_length, + char* output); // ------------------ Miscellaneous ------------------- // If heap profiling is not supported, returns false. // Else repeatedly calls (*func)(arg, data, n) and then returns true. // The concatenation of all "data[0,n-1]" fragments is the heap profile. -extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); +bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg); + +// Extend the CRC to include the first n bytes of buf. +// +// Returns zero if the CRC cannot be extended using acceleration, else returns +// the newly extended CRC value (which may also be zero). +uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size); } // namespace port } // namespace leveldb diff --git a/src/leveldb/port/port_posix.cc b/src/leveldb/port/port_posix.cc deleted file mode 100644 index 5ba127a5b..000000000 --- a/src/leveldb/port/port_posix.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -#include "port/port_posix.h" - -#include -#include -#include -#include "util/logging.h" - -namespace leveldb { -namespace port { - -static void PthreadCall(const char* label, int result) { - if (result != 0) { - fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); - abort(); - } -} - -Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); } - -Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } - -void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); } - -void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } - -CondVar::CondVar(Mutex* mu) - : mu_(mu) { - PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); -} - -CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } - -void CondVar::Wait() { - PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); -} - -void CondVar::Signal() { - PthreadCall("signal", pthread_cond_signal(&cv_)); -} - -void CondVar::SignalAll() { - PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); -} - -void InitOnce(OnceType* once, void (*initializer)()) { - PthreadCall("once", pthread_once(once, initializer)); -} - -} // namespace port -} // namespace leveldb diff --git a/src/leveldb/port/port_posix.h b/src/leveldb/port/port_posix.h deleted file mode 100644 index ccca9939d..000000000 --- a/src/leveldb/port/port_posix.h +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. -// -// See port_example.h for documentation for the following types/functions. - -#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ -#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_ - -#undef PLATFORM_IS_LITTLE_ENDIAN -#if defined(OS_MACOSX) - #include - #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) - #define PLATFORM_IS_LITTLE_ENDIAN \ - (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) - #endif -#elif defined(OS_SOLARIS) - #include - #ifdef _LITTLE_ENDIAN - #define PLATFORM_IS_LITTLE_ENDIAN true - #else - #define PLATFORM_IS_LITTLE_ENDIAN false - #endif -#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) ||\ - defined(OS_NETBSD) || defined(OS_DRAGONFLYBSD) - #include - #include - #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) -#elif defined(OS_HPUX) - #define PLATFORM_IS_LITTLE_ENDIAN false -#elif defined(OS_ANDROID) - // Due to a bug in the NDK x86 definition, - // _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. - // See http://code.google.com/p/android/issues/detail?id=39824 - #include - #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) -#else - #include -#endif - -#include -#ifdef SNAPPY -#include -#endif -#include -#include -#include "port/atomic_pointer.h" - -#ifndef PLATFORM_IS_LITTLE_ENDIAN -#define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) -#endif - -#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD) ||\ - defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) ||\ - defined(OS_ANDROID) || defined(OS_HPUX) || defined(CYGWIN) -// Use fread/fwrite/fflush on platforms without _unlocked variants -#define fread_unlocked fread -#define fwrite_unlocked fwrite -#define fflush_unlocked fflush -#endif - -#if defined(OS_FREEBSD) ||\ - defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) -// Use fsync() on platforms without fdatasync() -#define fdatasync fsync -#endif - -#if defined(OS_MACOSX) -#define fdatasync(fd) fcntl(fd, F_FULLFSYNC, 0) -#endif - -#if defined(OS_ANDROID) && __ANDROID_API__ < 9 -// fdatasync() was only introduced in API level 9 on Android. Use fsync() -// when targetting older platforms. -#define fdatasync fsync -#endif - -namespace leveldb { -namespace port { - -static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; -#undef PLATFORM_IS_LITTLE_ENDIAN - -class CondVar; - -class Mutex { - public: - Mutex(); - ~Mutex(); - - void Lock(); - void Unlock(); - void AssertHeld() { } - - private: - friend class CondVar; - pthread_mutex_t mu_; - - // No copying - Mutex(const Mutex&); - void operator=(const Mutex&); -}; - -class CondVar { - public: - explicit CondVar(Mutex* mu); - ~CondVar(); - void Wait(); - void Signal(); - void SignalAll(); - private: - pthread_cond_t cv_; - Mutex* mu_; -}; - -typedef pthread_once_t OnceType; -#define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT -extern void InitOnce(OnceType* once, void (*initializer)()); - -inline bool Snappy_Compress(const char* input, size_t length, - ::std::string* output) { -#ifdef SNAPPY - output->resize(snappy::MaxCompressedLength(length)); - size_t outlen; - snappy::RawCompress(input, length, &(*output)[0], &outlen); - output->resize(outlen); - return true; -#endif - - return false; -} - -inline bool Snappy_GetUncompressedLength(const char* input, size_t length, - size_t* result) { -#ifdef SNAPPY - return snappy::GetUncompressedLength(input, length, result); -#else - return false; -#endif -} - -inline bool Snappy_Uncompress(const char* input, size_t length, - char* output) { -#ifdef SNAPPY - return snappy::RawUncompress(input, length, output); -#else - return false; -#endif -} - -inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { - return false; -} - -} // namespace port -} // namespace leveldb - -#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ diff --git a/src/leveldb/port/port_stdcxx.h b/src/leveldb/port/port_stdcxx.h new file mode 100644 index 000000000..e9cb0e53a --- /dev/null +++ b/src/leveldb/port/port_stdcxx.h @@ -0,0 +1,153 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_PORT_PORT_STDCXX_H_ +#define STORAGE_LEVELDB_PORT_PORT_STDCXX_H_ + +// port/port_config.h availability is automatically detected via __has_include +// in newer compilers. If LEVELDB_HAS_PORT_CONFIG_H is defined, it overrides the +// configuration detection. +#if defined(LEVELDB_HAS_PORT_CONFIG_H) + +#if LEVELDB_HAS_PORT_CONFIG_H +#include "port/port_config.h" +#endif // LEVELDB_HAS_PORT_CONFIG_H + +#elif defined(__has_include) + +#if __has_include("port/port_config.h") +#include "port/port_config.h" +#endif // __has_include("port/port_config.h") + +#endif // defined(LEVELDB_HAS_PORT_CONFIG_H) + +#if HAVE_CRC32C +#include +#endif // HAVE_CRC32C +#if HAVE_SNAPPY +#include +#endif // HAVE_SNAPPY + +#include +#include // NOLINT +#include +#include +#include // NOLINT +#include + +#include "port/thread_annotations.h" + +namespace leveldb { +namespace port { + +static const bool kLittleEndian = !LEVELDB_IS_BIG_ENDIAN; + +class CondVar; + +// Thinly wraps std::mutex. +class LOCKABLE Mutex { + public: + Mutex() = default; + ~Mutex() = default; + + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; + + void Lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.lock(); } + void Unlock() UNLOCK_FUNCTION() { mu_.unlock(); } + void AssertHeld() ASSERT_EXCLUSIVE_LOCK() {} + + private: + friend class CondVar; + std::mutex mu_; +}; + +// Thinly wraps std::condition_variable. +class CondVar { + public: + explicit CondVar(Mutex* mu) : mu_(mu) { assert(mu != nullptr); } + ~CondVar() = default; + + CondVar(const CondVar&) = delete; + CondVar& operator=(const CondVar&) = delete; + + void Wait() { + std::unique_lock lock(mu_->mu_, std::adopt_lock); + cv_.wait(lock); + lock.release(); + } + void Signal() { cv_.notify_one(); } + void SignalAll() { cv_.notify_all(); } + + private: + std::condition_variable cv_; + Mutex* const mu_; +}; + +inline bool Snappy_Compress(const char* input, size_t length, + std::string* output) { +#if HAVE_SNAPPY + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#else + // Silence compiler warnings about unused arguments. + (void)input; + (void)length; + (void)output; +#endif // HAVE_SNAPPY + + return false; +} + +inline bool Snappy_GetUncompressedLength(const char* input, size_t length, + size_t* result) { +#if HAVE_SNAPPY + return snappy::GetUncompressedLength(input, length, result); +#else + // Silence compiler warnings about unused arguments. + (void)input; + (void)length; + (void)result; + return false; +#endif // HAVE_SNAPPY +} + +inline bool Snappy_Uncompress(const char* input, size_t length, char* output) { +#if HAVE_SNAPPY + return snappy::RawUncompress(input, length, output); +#else + // Silence compiler warnings about unused arguments. + (void)input; + (void)length; + (void)output; + return false; +#endif // HAVE_SNAPPY +} + +inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { + // Silence compiler warnings about unused arguments. + (void)func; + (void)arg; + return false; +} + +inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { +#if HAVE_CRC32C + return ::crc32c::Extend(crc, reinterpret_cast(buf), size); +#else + // Silence compiler warnings about unused arguments. + (void)crc; + (void)buf; + (void)size; + return 0; +#endif // HAVE_CRC32C +} + +} // namespace port +} // namespace leveldb + +#endif // STORAGE_LEVELDB_PORT_PORT_STDCXX_H_ diff --git a/src/leveldb/port/port_win.cc b/src/leveldb/port/port_win.cc deleted file mode 100644 index 1b0f060a1..000000000 --- a/src/leveldb/port/port_win.cc +++ /dev/null @@ -1,147 +0,0 @@ -// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. -// -// See port_example.h for documentation for the following types/functions. - -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of the University of California, Berkeley 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 REGENTS 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 REGENTS AND 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 "port/port_win.h" - -#include -#include - -namespace leveldb { -namespace port { - -Mutex::Mutex() : - cs_(NULL) { - assert(!cs_); - cs_ = static_cast(new CRITICAL_SECTION()); - ::InitializeCriticalSection(static_cast(cs_)); - assert(cs_); -} - -Mutex::~Mutex() { - assert(cs_); - ::DeleteCriticalSection(static_cast(cs_)); - delete static_cast(cs_); - cs_ = NULL; - assert(!cs_); -} - -void Mutex::Lock() { - assert(cs_); - ::EnterCriticalSection(static_cast(cs_)); -} - -void Mutex::Unlock() { - assert(cs_); - ::LeaveCriticalSection(static_cast(cs_)); -} - -void Mutex::AssertHeld() { - assert(cs_); - assert(1); -} - -CondVar::CondVar(Mutex* mu) : - waiting_(0), - mu_(mu), - sem1_(::CreateSemaphore(NULL, 0, 10000, NULL)), - sem2_(::CreateSemaphore(NULL, 0, 10000, NULL)) { - assert(mu_); -} - -CondVar::~CondVar() { - ::CloseHandle(sem1_); - ::CloseHandle(sem2_); -} - -void CondVar::Wait() { - mu_->AssertHeld(); - - wait_mtx_.Lock(); - ++waiting_; - wait_mtx_.Unlock(); - - mu_->Unlock(); - - // initiate handshake - ::WaitForSingleObject(sem1_, INFINITE); - ::ReleaseSemaphore(sem2_, 1, NULL); - mu_->Lock(); -} - -void CondVar::Signal() { - wait_mtx_.Lock(); - if (waiting_ > 0) { - --waiting_; - - // finalize handshake - ::ReleaseSemaphore(sem1_, 1, NULL); - ::WaitForSingleObject(sem2_, INFINITE); - } - wait_mtx_.Unlock(); -} - -void CondVar::SignalAll() { - wait_mtx_.Lock(); - ::ReleaseSemaphore(sem1_, waiting_, NULL); - while(waiting_ > 0) { - --waiting_; - ::WaitForSingleObject(sem2_, INFINITE); - } - wait_mtx_.Unlock(); -} - -AtomicPointer::AtomicPointer(void* v) { - Release_Store(v); -} - -void InitOnce(OnceType* once, void (*initializer)()) { - once->InitOnce(initializer); -} - -void* AtomicPointer::Acquire_Load() const { - void * p = NULL; - InterlockedExchangePointer(&p, rep_); - return p; -} - -void AtomicPointer::Release_Store(void* v) { - InterlockedExchangePointer(&rep_, v); -} - -void* AtomicPointer::NoBarrier_Load() const { - return rep_; -} - -void AtomicPointer::NoBarrier_Store(void* v) { - rep_ = v; -} - -} -} diff --git a/src/leveldb/port/port_win.h b/src/leveldb/port/port_win.h deleted file mode 100644 index 45bf2f0ea..000000000 --- a/src/leveldb/port/port_win.h +++ /dev/null @@ -1,174 +0,0 @@ -// LevelDB Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. -// -// See port_example.h for documentation for the following types/functions. - -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * 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. -// * Neither the name of the University of California, Berkeley 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 REGENTS 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 REGENTS AND 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. -// - -#ifndef STORAGE_LEVELDB_PORT_PORT_WIN_H_ -#define STORAGE_LEVELDB_PORT_PORT_WIN_H_ - -#ifdef _MSC_VER -#define snprintf _snprintf -#define close _close -#define fread_unlocked _fread_nolock -#endif - -#include -#include -#ifdef SNAPPY -#include -#endif - -namespace leveldb { -namespace port { - -// Windows is little endian (for now :p) -static const bool kLittleEndian = true; - -class CondVar; - -class Mutex { - public: - Mutex(); - ~Mutex(); - - void Lock(); - void Unlock(); - void AssertHeld(); - - private: - friend class CondVar; - // critical sections are more efficient than mutexes - // but they are not recursive and can only be used to synchronize threads within the same process - // we use opaque void * to avoid including windows.h in port_win.h - void * cs_; - - // No copying - Mutex(const Mutex&); - void operator=(const Mutex&); -}; - -// the Win32 API offers a dependable condition variable mechanism, but only starting with -// Windows 2008 and Vista -// no matter what we will implement our own condition variable with a semaphore -// implementation as described in a paper written by Andrew D. Birrell in 2003 -class CondVar { - public: - explicit CondVar(Mutex* mu); - ~CondVar(); - void Wait(); - void Signal(); - void SignalAll(); - private: - Mutex* mu_; - - Mutex wait_mtx_; - long waiting_; - - void * sem1_; - void * sem2_; - - -}; - -class OnceType { -public: -// OnceType() : init_(false) {} - OnceType(const OnceType &once) : init_(once.init_) {} - OnceType(bool f) : init_(f) {} - void InitOnce(void (*initializer)()) { - mutex_.Lock(); - if (!init_) { - init_ = true; - initializer(); - } - mutex_.Unlock(); - } - -private: - bool init_; - Mutex mutex_; -}; - -#define LEVELDB_ONCE_INIT false -extern void InitOnce(port::OnceType*, void (*initializer)()); - -// Storage for a lock-free pointer -class AtomicPointer { - private: - void * rep_; - public: - AtomicPointer() : rep_(NULL) { } - explicit AtomicPointer(void* v); - void* Acquire_Load() const; - - void Release_Store(void* v); - - void* NoBarrier_Load() const; - - void NoBarrier_Store(void* v); -}; - -inline bool Snappy_Compress(const char* input, size_t length, - ::std::string* output) { -#ifdef SNAPPY - output->resize(snappy::MaxCompressedLength(length)); - size_t outlen; - snappy::RawCompress(input, length, &(*output)[0], &outlen); - output->resize(outlen); - return true; -#endif - - return false; -} - -inline bool Snappy_GetUncompressedLength(const char* input, size_t length, - size_t* result) { -#ifdef SNAPPY - return snappy::GetUncompressedLength(input, length, result); -#else - return false; -#endif -} - -inline bool Snappy_Uncompress(const char* input, size_t length, - char* output) { -#ifdef SNAPPY - return snappy::RawUncompress(input, length, output); -#else - return false; -#endif -} - -inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { - return false; -} - -} -} - -#endif // STORAGE_LEVELDB_PORT_PORT_WIN_H_ diff --git a/src/leveldb/port/thread_annotations.h b/src/leveldb/port/thread_annotations.h index 9470ef587..1547df908 100644 --- a/src/leveldb/port/thread_annotations.h +++ b/src/leveldb/port/thread_annotations.h @@ -5,56 +5,104 @@ #ifndef STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ #define STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ -// Some environments provide custom macros to aid in static thread-safety -// analysis. Provide empty definitions of such macros unless they are already -// defined. +// Use Clang's thread safety analysis annotations when available. In other +// environments, the macros receive empty definitions. +// Usage documentation: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html + +#if !defined(THREAD_ANNOTATION_ATTRIBUTE__) + +#if defined(__clang__) + +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#endif // !defined(THREAD_ANNOTATION_ATTRIBUTE__) + +#ifndef GUARDED_BY +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +#endif + +#ifndef PT_GUARDED_BY +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) +#endif + +#ifndef ACQUIRED_AFTER +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) +#endif + +#ifndef ACQUIRED_BEFORE +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) +#endif #ifndef EXCLUSIVE_LOCKS_REQUIRED -#define EXCLUSIVE_LOCKS_REQUIRED(...) +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) #endif #ifndef SHARED_LOCKS_REQUIRED -#define SHARED_LOCKS_REQUIRED(...) +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) #endif #ifndef LOCKS_EXCLUDED -#define LOCKS_EXCLUDED(...) +#define LOCKS_EXCLUDED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) #endif #ifndef LOCK_RETURNED -#define LOCK_RETURNED(x) +#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) #endif #ifndef LOCKABLE -#define LOCKABLE +#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable) #endif #ifndef SCOPED_LOCKABLE -#define SCOPED_LOCKABLE +#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) #endif #ifndef EXCLUSIVE_LOCK_FUNCTION -#define EXCLUSIVE_LOCK_FUNCTION(...) +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) #endif #ifndef SHARED_LOCK_FUNCTION -#define SHARED_LOCK_FUNCTION(...) +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) #endif #ifndef EXCLUSIVE_TRYLOCK_FUNCTION -#define EXCLUSIVE_TRYLOCK_FUNCTION(...) +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) #endif #ifndef SHARED_TRYLOCK_FUNCTION -#define SHARED_TRYLOCK_FUNCTION(...) +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) #endif #ifndef UNLOCK_FUNCTION -#define UNLOCK_FUNCTION(...) +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) #endif #ifndef NO_THREAD_SAFETY_ANALYSIS -#define NO_THREAD_SAFETY_ANALYSIS +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) +#endif + +#ifndef ASSERT_EXCLUSIVE_LOCK +#define ASSERT_EXCLUSIVE_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) +#endif + +#ifndef ASSERT_SHARED_LOCK +#define ASSERT_SHARED_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) #endif #endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ diff --git a/src/leveldb/port/win/stdint.h b/src/leveldb/port/win/stdint.h deleted file mode 100644 index 39edd0db1..000000000 --- a/src/leveldb/port/win/stdint.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2011 The LevelDB Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. See the AUTHORS file for names of contributors. - -// MSVC didn't ship with this file until the 2010 version. - -#ifndef STORAGE_LEVELDB_PORT_WIN_STDINT_H_ -#define STORAGE_LEVELDB_PORT_WIN_STDINT_H_ - -#if !defined(_MSC_VER) -#error This file should only be included when compiling with MSVC. -#endif - -// Define C99 equivalent types. -typedef signed char int8_t; -typedef signed short int16_t; -typedef signed int int32_t; -typedef signed long long int64_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; - -#endif // STORAGE_LEVELDB_PORT_WIN_STDINT_H_ diff --git a/src/leveldb/table/block.cc b/src/leveldb/table/block.cc index 43e402c9c..2fe89eaa4 100644 --- a/src/leveldb/table/block.cc +++ b/src/leveldb/table/block.cc @@ -6,8 +6,10 @@ #include "table/block.h" -#include #include +#include +#include + #include "leveldb/comparator.h" #include "table/format.h" #include "util/coding.h" @@ -27,7 +29,7 @@ Block::Block(const BlockContents& contents) if (size_ < sizeof(uint32_t)) { size_ = 0; // Error marker } else { - size_t max_restarts_allowed = (size_-sizeof(uint32_t)) / sizeof(uint32_t); + size_t max_restarts_allowed = (size_ - sizeof(uint32_t)) / sizeof(uint32_t); if (NumRestarts() > max_restarts_allowed) { // The size is too small for NumRestarts() size_ = 0; @@ -48,27 +50,26 @@ Block::~Block() { // and the length of the value in "*shared", "*non_shared", and // "*value_length", respectively. Will not dereference past "limit". // -// If any errors are detected, returns NULL. Otherwise, returns a +// If any errors are detected, returns nullptr. Otherwise, returns a // pointer to the key delta (just past the three decoded values). static inline const char* DecodeEntry(const char* p, const char* limit, - uint32_t* shared, - uint32_t* non_shared, + uint32_t* shared, uint32_t* non_shared, uint32_t* value_length) { - if (limit - p < 3) return NULL; - *shared = reinterpret_cast(p)[0]; - *non_shared = reinterpret_cast(p)[1]; - *value_length = reinterpret_cast(p)[2]; + if (limit - p < 3) return nullptr; + *shared = reinterpret_cast(p)[0]; + *non_shared = reinterpret_cast(p)[1]; + *value_length = reinterpret_cast(p)[2]; if ((*shared | *non_shared | *value_length) < 128) { // Fast path: all three values are encoded in one byte each p += 3; } else { - if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL; - if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL; - if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL; + if ((p = GetVarint32Ptr(p, limit, shared)) == nullptr) return nullptr; + if ((p = GetVarint32Ptr(p, limit, non_shared)) == nullptr) return nullptr; + if ((p = GetVarint32Ptr(p, limit, value_length)) == nullptr) return nullptr; } if (static_cast(limit - p) < (*non_shared + *value_length)) { - return NULL; + return nullptr; } return p; } @@ -76,9 +77,9 @@ static inline const char* DecodeEntry(const char* p, const char* limit, class Block::Iter : public Iterator { private: const Comparator* const comparator_; - const char* const data_; // underlying block contents - uint32_t const restarts_; // Offset of restart array (list of fixed32) - uint32_t const num_restarts_; // Number of uint32_t entries in restart array + const char* const data_; // underlying block contents + uint32_t const restarts_; // Offset of restart array (list of fixed32) + uint32_t const num_restarts_; // Number of uint32_t entries in restart array // current_ is offset in data_ of current entry. >= restarts_ if !Valid uint32_t current_; @@ -112,9 +113,7 @@ class Block::Iter : public Iterator { } public: - Iter(const Comparator* comparator, - const char* data, - uint32_t restarts, + Iter(const Comparator* comparator, const char* data, uint32_t restarts, uint32_t num_restarts) : comparator_(comparator), data_(data), @@ -125,23 +124,23 @@ class Block::Iter : public Iterator { assert(num_restarts_ > 0); } - virtual bool Valid() const { return current_ < restarts_; } - virtual Status status() const { return status_; } - virtual Slice key() const { + bool Valid() const override { return current_ < restarts_; } + Status status() const override { return status_; } + Slice key() const override { assert(Valid()); return key_; } - virtual Slice value() const { + Slice value() const override { assert(Valid()); return value_; } - virtual void Next() { + void Next() override { assert(Valid()); ParseNextKey(); } - virtual void Prev() { + void Prev() override { assert(Valid()); // Scan backwards to a restart point before current_ @@ -162,7 +161,7 @@ class Block::Iter : public Iterator { } while (ParseNextKey() && NextEntryOffset() < original); } - virtual void Seek(const Slice& target) { + void Seek(const Slice& target) override { // Binary search in restart array to find the last restart point // with a key < target uint32_t left = 0; @@ -171,10 +170,10 @@ class Block::Iter : public Iterator { uint32_t mid = (left + right + 1) / 2; uint32_t region_offset = GetRestartPoint(mid); uint32_t shared, non_shared, value_length; - const char* key_ptr = DecodeEntry(data_ + region_offset, - data_ + restarts_, - &shared, &non_shared, &value_length); - if (key_ptr == NULL || (shared != 0)) { + const char* key_ptr = + DecodeEntry(data_ + region_offset, data_ + restarts_, &shared, + &non_shared, &value_length); + if (key_ptr == nullptr || (shared != 0)) { CorruptionError(); return; } @@ -202,12 +201,12 @@ class Block::Iter : public Iterator { } } - virtual void SeekToFirst() { + void SeekToFirst() override { SeekToRestartPoint(0); ParseNextKey(); } - virtual void SeekToLast() { + void SeekToLast() override { SeekToRestartPoint(num_restarts_ - 1); while (ParseNextKey() && NextEntryOffset() < restarts_) { // Keep skipping @@ -237,7 +236,7 @@ class Block::Iter : public Iterator { // Decode next entry uint32_t shared, non_shared, value_length; p = DecodeEntry(p, limit, &shared, &non_shared, &value_length); - if (p == NULL || key_.size() < shared) { + if (p == nullptr || key_.size() < shared) { CorruptionError(); return false; } else { @@ -253,7 +252,7 @@ class Block::Iter : public Iterator { } }; -Iterator* Block::NewIterator(const Comparator* cmp) { +Iterator* Block::NewIterator(const Comparator* comparator) { if (size_ < sizeof(uint32_t)) { return NewErrorIterator(Status::Corruption("bad block contents")); } @@ -261,7 +260,7 @@ Iterator* Block::NewIterator(const Comparator* cmp) { if (num_restarts == 0) { return NewEmptyIterator(); } else { - return new Iter(cmp, data_, restart_offset_, num_restarts); + return new Iter(comparator, data_, restart_offset_, num_restarts); } } diff --git a/src/leveldb/table/block.h b/src/leveldb/table/block.h index 2493eb9f9..c8f1f7b43 100644 --- a/src/leveldb/table/block.h +++ b/src/leveldb/table/block.h @@ -7,6 +7,7 @@ #include #include + #include "leveldb/iterator.h" namespace leveldb { @@ -19,24 +20,23 @@ class Block { // Initialize the block with the specified contents. explicit Block(const BlockContents& contents); + Block(const Block&) = delete; + Block& operator=(const Block&) = delete; + ~Block(); size_t size() const { return size_; } Iterator* NewIterator(const Comparator* comparator); private: + class Iter; + uint32_t NumRestarts() const; const char* data_; size_t size_; - uint32_t restart_offset_; // Offset in data_ of restart array - bool owned_; // Block owns data_[] - - // No copying allowed - Block(const Block&); - void operator=(const Block&); - - class Iter; + uint32_t restart_offset_; // Offset in data_ of restart array + bool owned_; // Block owns data_[] }; } // namespace leveldb diff --git a/src/leveldb/table/block_builder.cc b/src/leveldb/table/block_builder.cc index db660cd07..919cff5c9 100644 --- a/src/leveldb/table/block_builder.cc +++ b/src/leveldb/table/block_builder.cc @@ -28,36 +28,35 @@ #include "table/block_builder.h" -#include #include + +#include + #include "leveldb/comparator.h" -#include "leveldb/table_builder.h" +#include "leveldb/options.h" #include "util/coding.h" namespace leveldb { BlockBuilder::BlockBuilder(const Options* options) - : options_(options), - restarts_(), - counter_(0), - finished_(false) { + : options_(options), restarts_(), counter_(0), finished_(false) { assert(options->block_restart_interval >= 1); - restarts_.push_back(0); // First restart point is at offset 0 + restarts_.push_back(0); // First restart point is at offset 0 } void BlockBuilder::Reset() { buffer_.clear(); restarts_.clear(); - restarts_.push_back(0); // First restart point is at offset 0 + restarts_.push_back(0); // First restart point is at offset 0 counter_ = 0; finished_ = false; last_key_.clear(); } size_t BlockBuilder::CurrentSizeEstimate() const { - return (buffer_.size() + // Raw data buffer - restarts_.size() * sizeof(uint32_t) + // Restart array - sizeof(uint32_t)); // Restart array length + return (buffer_.size() + // Raw data buffer + restarts_.size() * sizeof(uint32_t) + // Restart array + sizeof(uint32_t)); // Restart array length } Slice BlockBuilder::Finish() { @@ -74,7 +73,7 @@ void BlockBuilder::Add(const Slice& key, const Slice& value) { Slice last_key_piece(last_key_); assert(!finished_); assert(counter_ <= options_->block_restart_interval); - assert(buffer_.empty() // No values yet? + assert(buffer_.empty() // No values yet? || options_->comparator->Compare(key, last_key_piece) > 0); size_t shared = 0; if (counter_ < options_->block_restart_interval) { diff --git a/src/leveldb/table/block_builder.h b/src/leveldb/table/block_builder.h index 4fbcb3397..f91f5e6d4 100644 --- a/src/leveldb/table/block_builder.h +++ b/src/leveldb/table/block_builder.h @@ -5,9 +5,10 @@ #ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ #define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_ +#include + #include -#include #include "leveldb/slice.h" namespace leveldb { @@ -18,6 +19,9 @@ class BlockBuilder { public: explicit BlockBuilder(const Options* options); + BlockBuilder(const BlockBuilder&) = delete; + BlockBuilder& operator=(const BlockBuilder&) = delete; + // Reset the contents as if the BlockBuilder was just constructed. void Reset(); @@ -35,21 +39,15 @@ class BlockBuilder { size_t CurrentSizeEstimate() const; // Return true iff no entries have been added since the last Reset() - bool empty() const { - return buffer_.empty(); - } + bool empty() const { return buffer_.empty(); } private: - const Options* options_; - std::string buffer_; // Destination buffer - std::vector restarts_; // Restart points - int counter_; // Number of entries emitted since restart - bool finished_; // Has Finish() been called? - std::string last_key_; - - // No copying allowed - BlockBuilder(const BlockBuilder&); - void operator=(const BlockBuilder&); + const Options* options_; + std::string buffer_; // Destination buffer + std::vector restarts_; // Restart points + int counter_; // Number of entries emitted since restart + bool finished_; // Has Finish() been called? + std::string last_key_; }; } // namespace leveldb diff --git a/src/leveldb/table/filter_block.cc b/src/leveldb/table/filter_block.cc index 203e15c8b..09ec0094b 100644 --- a/src/leveldb/table/filter_block.cc +++ b/src/leveldb/table/filter_block.cc @@ -9,15 +9,14 @@ namespace leveldb { -// See doc/table_format.txt for an explanation of the filter block format. +// See doc/table_format.md for an explanation of the filter block format. // Generate new filter every 2KB of data static const size_t kFilterBaseLg = 11; static const size_t kFilterBase = 1 << kFilterBaseLg; FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy) - : policy_(policy) { -} + : policy_(policy) {} void FilterBlockBuilder::StartBlock(uint64_t block_offset) { uint64_t filter_index = (block_offset / kFilterBase); @@ -62,13 +61,13 @@ void FilterBlockBuilder::GenerateFilter() { tmp_keys_.resize(num_keys); for (size_t i = 0; i < num_keys; i++) { const char* base = keys_.data() + start_[i]; - size_t length = start_[i+1] - start_[i]; + size_t length = start_[i + 1] - start_[i]; tmp_keys_[i] = Slice(base, length); } // Generate filter for current set of keys and append to result_. filter_offsets_.push_back(result_.size()); - policy_->CreateFilter(&tmp_keys_[0], num_keys, &result_); + policy_->CreateFilter(&tmp_keys_[0], static_cast(num_keys), &result_); tmp_keys_.clear(); keys_.clear(); @@ -77,14 +76,10 @@ void FilterBlockBuilder::GenerateFilter() { FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, const Slice& contents) - : policy_(policy), - data_(NULL), - offset_(NULL), - num_(0), - base_lg_(0) { + : policy_(policy), data_(nullptr), offset_(nullptr), num_(0), base_lg_(0) { size_t n = contents.size(); if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array - base_lg_ = contents[n-1]; + base_lg_ = contents[n - 1]; uint32_t last_word = DecodeFixed32(contents.data() + n - 5); if (last_word > n - 5) return; data_ = contents.data(); @@ -95,9 +90,9 @@ FilterBlockReader::FilterBlockReader(const FilterPolicy* policy, bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { uint64_t index = block_offset >> base_lg_; if (index < num_) { - uint32_t start = DecodeFixed32(offset_ + index*4); - uint32_t limit = DecodeFixed32(offset_ + index*4 + 4); - if (start <= limit && limit <= (offset_ - data_)) { + uint32_t start = DecodeFixed32(offset_ + index * 4); + uint32_t limit = DecodeFixed32(offset_ + index * 4 + 4); + if (start <= limit && limit <= static_cast(offset_ - data_)) { Slice filter = Slice(data_ + start, limit - start); return policy_->KeyMayMatch(key, filter); } else if (start == limit) { @@ -108,4 +103,4 @@ bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) { return true; // Errors are treated as potential matches } -} +} // namespace leveldb diff --git a/src/leveldb/table/filter_block.h b/src/leveldb/table/filter_block.h index c67d010bd..73b539924 100644 --- a/src/leveldb/table/filter_block.h +++ b/src/leveldb/table/filter_block.h @@ -11,8 +11,10 @@ #include #include + #include #include + #include "leveldb/slice.h" #include "util/hash.h" @@ -30,6 +32,9 @@ class FilterBlockBuilder { public: explicit FilterBlockBuilder(const FilterPolicy*); + FilterBlockBuilder(const FilterBlockBuilder&) = delete; + FilterBlockBuilder& operator=(const FilterBlockBuilder&) = delete; + void StartBlock(uint64_t block_offset); void AddKey(const Slice& key); Slice Finish(); @@ -38,20 +43,16 @@ class FilterBlockBuilder { void GenerateFilter(); const FilterPolicy* policy_; - std::string keys_; // Flattened key contents - std::vector start_; // Starting index in keys_ of each key - std::string result_; // Filter data computed so far - std::vector tmp_keys_; // policy_->CreateFilter() argument + std::string keys_; // Flattened key contents + std::vector start_; // Starting index in keys_ of each key + std::string result_; // Filter data computed so far + std::vector tmp_keys_; // policy_->CreateFilter() argument std::vector filter_offsets_; - - // No copying allowed - FilterBlockBuilder(const FilterBlockBuilder&); - void operator=(const FilterBlockBuilder&); }; class FilterBlockReader { public: - // REQUIRES: "contents" and *policy must stay live while *this is live. + // REQUIRES: "contents" and *policy must stay live while *this is live. FilterBlockReader(const FilterPolicy* policy, const Slice& contents); bool KeyMayMatch(uint64_t block_offset, const Slice& key); @@ -63,6 +64,6 @@ class FilterBlockReader { size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file) }; -} +} // namespace leveldb #endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_ diff --git a/src/leveldb/table/filter_block_test.cc b/src/leveldb/table/filter_block_test.cc index 8c4a4741f..8b33bbdd1 100644 --- a/src/leveldb/table/filter_block_test.cc +++ b/src/leveldb/table/filter_block_test.cc @@ -16,18 +16,16 @@ namespace leveldb { // For testing: emit an array with one hash value per key class TestHashFilter : public FilterPolicy { public: - virtual const char* Name() const { - return "TestHashFilter"; - } + const char* Name() const override { return "TestHashFilter"; } - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + void CreateFilter(const Slice* keys, int n, std::string* dst) const override { for (int i = 0; i < n; i++) { uint32_t h = Hash(keys[i].data(), keys[i].size(), 1); PutFixed32(dst, h); } } - virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const { + bool KeyMayMatch(const Slice& key, const Slice& filter) const override { uint32_t h = Hash(key.data(), key.size(), 1); for (size_t i = 0; i + 4 <= filter.size(); i += 4) { if (h == DecodeFixed32(filter.data() + i)) { @@ -69,8 +67,8 @@ TEST(FilterBlockTest, SingleChunk) { ASSERT_TRUE(reader.KeyMayMatch(100, "box")); ASSERT_TRUE(reader.KeyMayMatch(100, "hello")); ASSERT_TRUE(reader.KeyMayMatch(100, "foo")); - ASSERT_TRUE(! reader.KeyMayMatch(100, "missing")); - ASSERT_TRUE(! reader.KeyMayMatch(100, "other")); + ASSERT_TRUE(!reader.KeyMayMatch(100, "missing")); + ASSERT_TRUE(!reader.KeyMayMatch(100, "other")); } TEST(FilterBlockTest, MultiChunk) { @@ -99,30 +97,28 @@ TEST(FilterBlockTest, MultiChunk) { // Check first filter ASSERT_TRUE(reader.KeyMayMatch(0, "foo")); ASSERT_TRUE(reader.KeyMayMatch(2000, "bar")); - ASSERT_TRUE(! reader.KeyMayMatch(0, "box")); - ASSERT_TRUE(! reader.KeyMayMatch(0, "hello")); + ASSERT_TRUE(!reader.KeyMayMatch(0, "box")); + ASSERT_TRUE(!reader.KeyMayMatch(0, "hello")); // Check second filter ASSERT_TRUE(reader.KeyMayMatch(3100, "box")); - ASSERT_TRUE(! reader.KeyMayMatch(3100, "foo")); - ASSERT_TRUE(! reader.KeyMayMatch(3100, "bar")); - ASSERT_TRUE(! reader.KeyMayMatch(3100, "hello")); + ASSERT_TRUE(!reader.KeyMayMatch(3100, "foo")); + ASSERT_TRUE(!reader.KeyMayMatch(3100, "bar")); + ASSERT_TRUE(!reader.KeyMayMatch(3100, "hello")); // Check third filter (empty) - ASSERT_TRUE(! reader.KeyMayMatch(4100, "foo")); - ASSERT_TRUE(! reader.KeyMayMatch(4100, "bar")); - ASSERT_TRUE(! reader.KeyMayMatch(4100, "box")); - ASSERT_TRUE(! reader.KeyMayMatch(4100, "hello")); + ASSERT_TRUE(!reader.KeyMayMatch(4100, "foo")); + ASSERT_TRUE(!reader.KeyMayMatch(4100, "bar")); + ASSERT_TRUE(!reader.KeyMayMatch(4100, "box")); + ASSERT_TRUE(!reader.KeyMayMatch(4100, "hello")); // Check last filter ASSERT_TRUE(reader.KeyMayMatch(9000, "box")); ASSERT_TRUE(reader.KeyMayMatch(9000, "hello")); - ASSERT_TRUE(! reader.KeyMayMatch(9000, "foo")); - ASSERT_TRUE(! reader.KeyMayMatch(9000, "bar")); + ASSERT_TRUE(!reader.KeyMayMatch(9000, "foo")); + ASSERT_TRUE(!reader.KeyMayMatch(9000, "bar")); } } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc index aa63144c9..a3d67de2e 100644 --- a/src/leveldb/table/format.cc +++ b/src/leveldb/table/format.cc @@ -21,8 +21,7 @@ void BlockHandle::EncodeTo(std::string* dst) const { } Status BlockHandle::DecodeFrom(Slice* input) { - if (GetVarint64(input, &offset_) && - GetVarint64(input, &size_)) { + if (GetVarint64(input, &offset_) && GetVarint64(input, &size_)) { return Status::OK(); } else { return Status::Corruption("bad block handle"); @@ -30,15 +29,14 @@ Status BlockHandle::DecodeFrom(Slice* input) { } void Footer::EncodeTo(std::string* dst) const { -#ifndef NDEBUG const size_t original_size = dst->size(); -#endif metaindex_handle_.EncodeTo(dst); index_handle_.EncodeTo(dst); dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding PutFixed32(dst, static_cast(kTableMagicNumber & 0xffffffffu)); PutFixed32(dst, static_cast(kTableMagicNumber >> 32)); assert(dst->size() == original_size + kEncodedLength); + (void)original_size; // Disable unused variable warning. } Status Footer::DecodeFrom(Slice* input) { @@ -63,10 +61,8 @@ Status Footer::DecodeFrom(Slice* input) { return result; } -Status ReadBlock(RandomAccessFile* file, - const ReadOptions& options, - const BlockHandle& handle, - BlockContents* result) { +Status ReadBlock(RandomAccessFile* file, const ReadOptions& options, + const BlockHandle& handle, BlockContents* result) { result->data = Slice(); result->cachable = false; result->heap_allocated = false; @@ -83,17 +79,17 @@ Status ReadBlock(RandomAccessFile* file, } if (contents.size() != n + kBlockTrailerSize) { delete[] buf; - return Status::Corruption("truncated block read"); + return Status::Corruption("truncated block read", file->GetName()); } // Check the crc of the type and the block contents - const char* data = contents.data(); // Pointer to where Read put the data + const char* data = contents.data(); // Pointer to where Read put the data if (options.verify_checksums) { const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1)); const uint32_t actual = crc32c::Value(data, n + 1); if (actual != crc) { delete[] buf; - s = Status::Corruption("block checksum mismatch"); + s = Status::Corruption("block checksum mismatch", file->GetName()); return s; } } @@ -120,13 +116,13 @@ Status ReadBlock(RandomAccessFile* file, size_t ulength = 0; if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { delete[] buf; - return Status::Corruption("corrupted compressed block contents"); + return Status::Corruption("corrupted compressed block contents", file->GetName()); } char* ubuf = new char[ulength]; if (!port::Snappy_Uncompress(data, n, ubuf)) { delete[] buf; delete[] ubuf; - return Status::Corruption("corrupted compressed block contents"); + return Status::Corruption("corrupted compressed block contents", file->GetName()); } delete[] buf; result->data = Slice(ubuf, ulength); @@ -136,7 +132,7 @@ Status ReadBlock(RandomAccessFile* file, } default: delete[] buf; - return Status::Corruption("bad block type"); + return Status::Corruption("bad block type", file->GetName()); } return Status::OK(); diff --git a/src/leveldb/table/format.h b/src/leveldb/table/format.h index 6c0b80c01..e49dfdc04 100644 --- a/src/leveldb/table/format.h +++ b/src/leveldb/table/format.h @@ -5,8 +5,10 @@ #ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_ #define STORAGE_LEVELDB_TABLE_FORMAT_H_ -#include #include + +#include + #include "leveldb/slice.h" #include "leveldb/status.h" #include "leveldb/table_builder.h" @@ -21,6 +23,9 @@ struct ReadOptions; // block or a meta block. class BlockHandle { public: + // Maximum encoding length of a BlockHandle + enum { kMaxEncodedLength = 10 + 10 }; + BlockHandle(); // The offset of the block in the file. @@ -34,9 +39,6 @@ class BlockHandle { void EncodeTo(std::string* dst) const; Status DecodeFrom(Slice* input); - // Maximum encoding length of a BlockHandle - enum { kMaxEncodedLength = 10 + 10 }; - private: uint64_t offset_; uint64_t size_; @@ -46,30 +48,24 @@ class BlockHandle { // end of every table file. class Footer { public: - Footer() { } + // Encoded length of a Footer. Note that the serialization of a + // Footer will always occupy exactly this many bytes. It consists + // of two block handles and a magic number. + enum { kEncodedLength = 2 * BlockHandle::kMaxEncodedLength + 8 }; + + Footer() = default; // The block handle for the metaindex block of the table const BlockHandle& metaindex_handle() const { return metaindex_handle_; } void set_metaindex_handle(const BlockHandle& h) { metaindex_handle_ = h; } // The block handle for the index block of the table - const BlockHandle& index_handle() const { - return index_handle_; - } - void set_index_handle(const BlockHandle& h) { - index_handle_ = h; - } + const BlockHandle& index_handle() const { return index_handle_; } + void set_index_handle(const BlockHandle& h) { index_handle_ = h; } void EncodeTo(std::string* dst) const; Status DecodeFrom(Slice* input); - // Encoded length of a Footer. Note that the serialization of a - // Footer will always occupy exactly this many bytes. It consists - // of two block handles and a magic number. - enum { - kEncodedLength = 2*BlockHandle::kMaxEncodedLength + 8 - }; - private: BlockHandle metaindex_handle_; BlockHandle index_handle_; @@ -91,17 +87,13 @@ struct BlockContents { // Read the block identified by "handle" from "file". On failure // return non-OK. On success fill *result and return OK. -extern Status ReadBlock(RandomAccessFile* file, - const ReadOptions& options, - const BlockHandle& handle, - BlockContents* result); +Status ReadBlock(RandomAccessFile* file, const ReadOptions& options, + const BlockHandle& handle, BlockContents* result); // Implementation details follow. Clients should ignore, inline BlockHandle::BlockHandle() - : offset_(~static_cast(0)), - size_(~static_cast(0)) { -} + : offset_(~static_cast(0)), size_(~static_cast(0)) {} } // namespace leveldb diff --git a/src/leveldb/table/iterator.cc b/src/leveldb/table/iterator.cc index 3d1c87fde..dfef083d4 100644 --- a/src/leveldb/table/iterator.cc +++ b/src/leveldb/table/iterator.cc @@ -7,58 +7,67 @@ namespace leveldb { Iterator::Iterator() { - cleanup_.function = NULL; - cleanup_.next = NULL; + cleanup_head_.function = nullptr; + cleanup_head_.next = nullptr; } Iterator::~Iterator() { - if (cleanup_.function != NULL) { - (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2); - for (Cleanup* c = cleanup_.next; c != NULL; ) { - (*c->function)(c->arg1, c->arg2); - Cleanup* next = c->next; - delete c; - c = next; + if (!cleanup_head_.IsEmpty()) { + cleanup_head_.Run(); + for (CleanupNode* node = cleanup_head_.next; node != nullptr;) { + node->Run(); + CleanupNode* next_node = node->next; + delete node; + node = next_node; } } } void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { - assert(func != NULL); - Cleanup* c; - if (cleanup_.function == NULL) { - c = &cleanup_; + assert(func != nullptr); + CleanupNode* node; + if (cleanup_head_.IsEmpty()) { + node = &cleanup_head_; } else { - c = new Cleanup; - c->next = cleanup_.next; - cleanup_.next = c; + node = new CleanupNode(); + node->next = cleanup_head_.next; + cleanup_head_.next = node; } - c->function = func; - c->arg1 = arg1; - c->arg2 = arg2; + node->function = func; + node->arg1 = arg1; + node->arg2 = arg2; } namespace { + class EmptyIterator : public Iterator { public: - EmptyIterator(const Status& s) : status_(s) { } - virtual bool Valid() const { return false; } - virtual void Seek(const Slice& target) { } - virtual void SeekToFirst() { } - virtual void SeekToLast() { } - virtual void Next() { assert(false); } - virtual void Prev() { assert(false); } - Slice key() const { assert(false); return Slice(); } - Slice value() const { assert(false); return Slice(); } - virtual Status status() const { return status_; } + EmptyIterator(const Status& s) : status_(s) {} + ~EmptyIterator() override = default; + + bool Valid() const override { return false; } + void Seek(const Slice& target) override {} + void SeekToFirst() override {} + void SeekToLast() override {} + void Next() override { assert(false); } + void Prev() override { assert(false); } + Slice key() const override { + assert(false); + return Slice(); + } + Slice value() const override { + assert(false); + return Slice(); + } + Status status() const override { return status_; } + private: Status status_; }; -} // namespace -Iterator* NewEmptyIterator() { - return new EmptyIterator(Status::OK()); -} +} // anonymous namespace + +Iterator* NewEmptyIterator() { return new EmptyIterator(Status::OK()); } Iterator* NewErrorIterator(const Status& status) { return new EmptyIterator(status); diff --git a/src/leveldb/table/iterator_wrapper.h b/src/leveldb/table/iterator_wrapper.h index 9e16b3dbe..c23057252 100644 --- a/src/leveldb/table/iterator_wrapper.h +++ b/src/leveldb/table/iterator_wrapper.h @@ -5,6 +5,9 @@ #ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ #define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_ +#include "leveldb/iterator.h" +#include "leveldb/slice.h" + namespace leveldb { // A internal wrapper class with an interface similar to Iterator that @@ -13,10 +16,8 @@ namespace leveldb { // cache locality. class IteratorWrapper { public: - IteratorWrapper(): iter_(NULL), valid_(false) { } - explicit IteratorWrapper(Iterator* iter): iter_(NULL) { - Set(iter); - } + IteratorWrapper() : iter_(nullptr), valid_(false) {} + explicit IteratorWrapper(Iterator* iter) : iter_(nullptr) { Set(iter); } ~IteratorWrapper() { delete iter_; } Iterator* iter() const { return iter_; } @@ -25,25 +26,53 @@ class IteratorWrapper { void Set(Iterator* iter) { delete iter_; iter_ = iter; - if (iter_ == NULL) { + if (iter_ == nullptr) { valid_ = false; } else { Update(); } } - // Iterator interface methods - bool Valid() const { return valid_; } - Slice key() const { assert(Valid()); return key_; } - Slice value() const { assert(Valid()); return iter_->value(); } - // Methods below require iter() != NULL - Status status() const { assert(iter_); return iter_->status(); } - void Next() { assert(iter_); iter_->Next(); Update(); } - void Prev() { assert(iter_); iter_->Prev(); Update(); } - void Seek(const Slice& k) { assert(iter_); iter_->Seek(k); Update(); } - void SeekToFirst() { assert(iter_); iter_->SeekToFirst(); Update(); } - void SeekToLast() { assert(iter_); iter_->SeekToLast(); Update(); } + bool Valid() const { return valid_; } + Slice key() const { + assert(Valid()); + return key_; + } + Slice value() const { + assert(Valid()); + return iter_->value(); + } + // Methods below require iter() != nullptr + Status status() const { + assert(iter_); + return iter_->status(); + } + void Next() { + assert(iter_); + iter_->Next(); + Update(); + } + void Prev() { + assert(iter_); + iter_->Prev(); + Update(); + } + void Seek(const Slice& k) { + assert(iter_); + iter_->Seek(k); + Update(); + } + void SeekToFirst() { + assert(iter_); + iter_->SeekToFirst(); + Update(); + } + void SeekToLast() { + assert(iter_); + iter_->SeekToLast(); + Update(); + } private: void Update() { diff --git a/src/leveldb/table/merger.cc b/src/leveldb/table/merger.cc index 2dde4dc21..76441b1cc 100644 --- a/src/leveldb/table/merger.cc +++ b/src/leveldb/table/merger.cc @@ -17,22 +17,18 @@ class MergingIterator : public Iterator { : comparator_(comparator), children_(new IteratorWrapper[n]), n_(n), - current_(NULL), + current_(nullptr), direction_(kForward) { for (int i = 0; i < n; i++) { children_[i].Set(children[i]); } } - virtual ~MergingIterator() { - delete[] children_; - } + ~MergingIterator() override { delete[] children_; } - virtual bool Valid() const { - return (current_ != NULL); - } + bool Valid() const override { return (current_ != nullptr); } - virtual void SeekToFirst() { + void SeekToFirst() override { for (int i = 0; i < n_; i++) { children_[i].SeekToFirst(); } @@ -40,7 +36,7 @@ class MergingIterator : public Iterator { direction_ = kForward; } - virtual void SeekToLast() { + void SeekToLast() override { for (int i = 0; i < n_; i++) { children_[i].SeekToLast(); } @@ -48,7 +44,7 @@ class MergingIterator : public Iterator { direction_ = kReverse; } - virtual void Seek(const Slice& target) { + void Seek(const Slice& target) override { for (int i = 0; i < n_; i++) { children_[i].Seek(target); } @@ -56,7 +52,7 @@ class MergingIterator : public Iterator { direction_ = kForward; } - virtual void Next() { + void Next() override { assert(Valid()); // Ensure that all children are positioned after key(). @@ -82,7 +78,7 @@ class MergingIterator : public Iterator { FindSmallest(); } - virtual void Prev() { + void Prev() override { assert(Valid()); // Ensure that all children are positioned before key(). @@ -111,17 +107,17 @@ class MergingIterator : public Iterator { FindLargest(); } - virtual Slice key() const { + Slice key() const override { assert(Valid()); return current_->key(); } - virtual Slice value() const { + Slice value() const override { assert(Valid()); return current_->value(); } - virtual Status status() const { + Status status() const override { Status status; for (int i = 0; i < n_; i++) { status = children_[i].status(); @@ -133,6 +129,9 @@ class MergingIterator : public Iterator { } private: + // Which direction is the iterator moving? + enum Direction { kForward, kReverse }; + void FindSmallest(); void FindLargest(); @@ -143,21 +142,15 @@ class MergingIterator : public Iterator { IteratorWrapper* children_; int n_; IteratorWrapper* current_; - - // Which direction is the iterator moving? - enum Direction { - kForward, - kReverse - }; Direction direction_; }; void MergingIterator::FindSmallest() { - IteratorWrapper* smallest = NULL; + IteratorWrapper* smallest = nullptr; for (int i = 0; i < n_; i++) { IteratorWrapper* child = &children_[i]; if (child->Valid()) { - if (smallest == NULL) { + if (smallest == nullptr) { smallest = child; } else if (comparator_->Compare(child->key(), smallest->key()) < 0) { smallest = child; @@ -168,11 +161,11 @@ void MergingIterator::FindSmallest() { } void MergingIterator::FindLargest() { - IteratorWrapper* largest = NULL; - for (int i = n_-1; i >= 0; i--) { + IteratorWrapper* largest = nullptr; + for (int i = n_ - 1; i >= 0; i--) { IteratorWrapper* child = &children_[i]; if (child->Valid()) { - if (largest == NULL) { + if (largest == nullptr) { largest = child; } else if (comparator_->Compare(child->key(), largest->key()) > 0) { largest = child; @@ -183,14 +176,15 @@ void MergingIterator::FindLargest() { } } // namespace -Iterator* NewMergingIterator(const Comparator* cmp, Iterator** list, int n) { +Iterator* NewMergingIterator(const Comparator* comparator, Iterator** children, + int n) { assert(n >= 0); if (n == 0) { return NewEmptyIterator(); } else if (n == 1) { - return list[0]; + return children[0]; } else { - return new MergingIterator(cmp, list, n); + return new MergingIterator(comparator, children, n); } } diff --git a/src/leveldb/table/merger.h b/src/leveldb/table/merger.h index 91ddd80fa..41cedc525 100644 --- a/src/leveldb/table/merger.h +++ b/src/leveldb/table/merger.h @@ -18,8 +18,8 @@ class Iterator; // key is present in K child iterators, it will be yielded K times. // // REQUIRES: n >= 0 -extern Iterator* NewMergingIterator( - const Comparator* comparator, Iterator** children, int n); +Iterator* NewMergingIterator(const Comparator* comparator, Iterator** children, + int n); } // namespace leveldb diff --git a/src/leveldb/table/table.cc b/src/leveldb/table/table.cc index dff8a8259..b07bc88c7 100644 --- a/src/leveldb/table/table.cc +++ b/src/leveldb/table/table.cc @@ -20,7 +20,7 @@ namespace leveldb { struct Table::Rep { ~Rep() { delete filter; - delete [] filter_data; + delete[] filter_data; delete index_block; } @@ -35,11 +35,9 @@ struct Table::Rep { Block* index_block; }; -Status Table::Open(const Options& options, - RandomAccessFile* file, - uint64_t size, - Table** table) { - *table = NULL; +Status Table::Open(const Options& options, RandomAccessFile* file, + uint64_t size, Table** table) { + *table = nullptr; if (size < Footer::kEncodedLength) { return Status::Corruption("file is too short to be an sstable"); } @@ -55,41 +53,36 @@ Status Table::Open(const Options& options, if (!s.ok()) return s; // Read the index block - BlockContents contents; - Block* index_block = NULL; + BlockContents index_block_contents; if (s.ok()) { ReadOptions opt; if (options.paranoid_checks) { opt.verify_checksums = true; } - s = ReadBlock(file, opt, footer.index_handle(), &contents); - if (s.ok()) { - index_block = new Block(contents); - } + s = ReadBlock(file, opt, footer.index_handle(), &index_block_contents); } if (s.ok()) { // We've successfully read the footer and the index block: we're // ready to serve requests. + Block* index_block = new Block(index_block_contents); Rep* rep = new Table::Rep; rep->options = options; rep->file = file; rep->metaindex_handle = footer.metaindex_handle(); rep->index_block = index_block; rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0); - rep->filter_data = NULL; - rep->filter = NULL; + rep->filter_data = nullptr; + rep->filter = nullptr; *table = new Table(rep); (*table)->ReadMeta(footer); - } else { - if (index_block) delete index_block; } return s; } void Table::ReadMeta(const Footer& footer) { - if (rep_->options.filter_policy == NULL) { + if (rep_->options.filter_policy == nullptr) { return; // Do not need any metadata } @@ -135,14 +128,12 @@ void Table::ReadFilter(const Slice& filter_handle_value) { return; } if (block.heap_allocated) { - rep_->filter_data = block.data.data(); // Will need to delete later + rep_->filter_data = block.data.data(); // Will need to delete later } rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data); } -Table::~Table() { - delete rep_; -} +Table::~Table() { delete rep_; } static void DeleteBlock(void* arg, void* ignored) { delete reinterpret_cast(arg); @@ -161,13 +152,12 @@ static void ReleaseBlock(void* arg, void* h) { // Convert an index iterator value (i.e., an encoded BlockHandle) // into an iterator over the contents of the corresponding block. -Iterator* Table::BlockReader(void* arg, - const ReadOptions& options, +Iterator* Table::BlockReader(void* arg, const ReadOptions& options, const Slice& index_value) { Table* table = reinterpret_cast(arg); Cache* block_cache = table->rep_->options.block_cache; - Block* block = NULL; - Cache::Handle* cache_handle = NULL; + Block* block = nullptr; + Cache::Handle* cache_handle = nullptr; BlockHandle handle; Slice input = index_value; @@ -177,21 +167,21 @@ Iterator* Table::BlockReader(void* arg, if (s.ok()) { BlockContents contents; - if (block_cache != NULL) { + if (block_cache != nullptr) { char cache_key_buffer[16]; EncodeFixed64(cache_key_buffer, table->rep_->cache_id); - EncodeFixed64(cache_key_buffer+8, handle.offset()); + EncodeFixed64(cache_key_buffer + 8, handle.offset()); Slice key(cache_key_buffer, sizeof(cache_key_buffer)); cache_handle = block_cache->Lookup(key); - if (cache_handle != NULL) { + if (cache_handle != nullptr) { block = reinterpret_cast(block_cache->Value(cache_handle)); } else { s = ReadBlock(table->rep_->file, options, handle, &contents); if (s.ok()) { block = new Block(contents); if (contents.cachable && options.fill_cache) { - cache_handle = block_cache->Insert( - key, block, block->size(), &DeleteCachedBlock); + cache_handle = block_cache->Insert(key, block, block->size(), + &DeleteCachedBlock); } } } @@ -204,10 +194,10 @@ Iterator* Table::BlockReader(void* arg, } Iterator* iter; - if (block != NULL) { + if (block != nullptr) { iter = block->NewIterator(table->rep_->options.comparator); - if (cache_handle == NULL) { - iter->RegisterCleanup(&DeleteBlock, block, NULL); + if (cache_handle == nullptr) { + iter->RegisterCleanup(&DeleteBlock, block, nullptr); } else { iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle); } @@ -223,9 +213,9 @@ Iterator* Table::NewIterator(const ReadOptions& options) const { &Table::BlockReader, const_cast(this), options); } -Status Table::InternalGet(const ReadOptions& options, const Slice& k, - void* arg, - void (*saver)(void*, const Slice&, const Slice&)) { +Status Table::InternalGet(const ReadOptions& options, const Slice& k, void* arg, + void (*handle_result)(void*, const Slice&, + const Slice&)) { Status s; Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator); iiter->Seek(k); @@ -233,15 +223,14 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k, Slice handle_value = iiter->value(); FilterBlockReader* filter = rep_->filter; BlockHandle handle; - if (filter != NULL && - handle.DecodeFrom(&handle_value).ok() && + if (filter != nullptr && handle.DecodeFrom(&handle_value).ok() && !filter->KeyMayMatch(handle.offset(), k)) { // Not found } else { Iterator* block_iter = BlockReader(this, options, iiter->value()); block_iter->Seek(k); if (block_iter->Valid()) { - (*saver)(arg, block_iter->key(), block_iter->value()); + (*handle_result)(arg, block_iter->key(), block_iter->value()); } s = block_iter->status(); delete block_iter; @@ -254,7 +243,6 @@ Status Table::InternalGet(const ReadOptions& options, const Slice& k, return s; } - uint64_t Table::ApproximateOffsetOf(const Slice& key) const { Iterator* index_iter = rep_->index_block->NewIterator(rep_->options.comparator); diff --git a/src/leveldb/table/table_builder.cc b/src/leveldb/table/table_builder.cc index 62002c84f..278febf94 100644 --- a/src/leveldb/table/table_builder.cc +++ b/src/leveldb/table/table_builder.cc @@ -5,6 +5,7 @@ #include "leveldb/table_builder.h" #include + #include "leveldb/comparator.h" #include "leveldb/env.h" #include "leveldb/filter_policy.h" @@ -18,6 +19,22 @@ namespace leveldb { struct TableBuilder::Rep { + Rep(const Options& opt, WritableFile* f) + : options(opt), + index_block_options(opt), + file(f), + offset(0), + data_block(&options), + index_block(&index_block_options), + num_entries(0), + closed(false), + filter_block(opt.filter_policy == nullptr + ? nullptr + : new FilterBlockBuilder(opt.filter_policy)), + pending_index_entry(false) { + index_block_options.block_restart_interval = 1; + } + Options options; Options index_block_options; WritableFile* file; @@ -27,7 +44,7 @@ struct TableBuilder::Rep { BlockBuilder index_block; std::string last_key; int64_t num_entries; - bool closed; // Either Finish() or Abandon() has been called. + bool closed; // Either Finish() or Abandon() has been called. FilterBlockBuilder* filter_block; // We do not emit the index entry for a block until we have seen the @@ -43,26 +60,11 @@ struct TableBuilder::Rep { BlockHandle pending_handle; // Handle to add to index block std::string compressed_output; - - Rep(const Options& opt, WritableFile* f) - : options(opt), - index_block_options(opt), - file(f), - offset(0), - data_block(&options), - index_block(&index_block_options), - num_entries(0), - closed(false), - filter_block(opt.filter_policy == NULL ? NULL - : new FilterBlockBuilder(opt.filter_policy)), - pending_index_entry(false) { - index_block_options.block_restart_interval = 1; - } }; TableBuilder::TableBuilder(const Options& options, WritableFile* file) : rep_(new Rep(options, file)) { - if (rep_->filter_block != NULL) { + if (rep_->filter_block != nullptr) { rep_->filter_block->StartBlock(0); } } @@ -106,7 +108,7 @@ void TableBuilder::Add(const Slice& key, const Slice& value) { r->pending_index_entry = false; } - if (r->filter_block != NULL) { + if (r->filter_block != nullptr) { r->filter_block->AddKey(key); } @@ -131,7 +133,7 @@ void TableBuilder::Flush() { r->pending_index_entry = true; r->status = r->file->Flush(); } - if (r->filter_block != NULL) { + if (r->filter_block != nullptr) { r->filter_block->StartBlock(r->offset); } } @@ -173,8 +175,7 @@ void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) { } void TableBuilder::WriteRawBlock(const Slice& block_contents, - CompressionType type, - BlockHandle* handle) { + CompressionType type, BlockHandle* handle) { Rep* r = rep_; handle->set_offset(r->offset); handle->set_size(block_contents.size()); @@ -184,7 +185,7 @@ void TableBuilder::WriteRawBlock(const Slice& block_contents, trailer[0] = type; uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size()); crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type - EncodeFixed32(trailer+1, crc32c::Mask(crc)); + EncodeFixed32(trailer + 1, crc32c::Mask(crc)); r->status = r->file->Append(Slice(trailer, kBlockTrailerSize)); if (r->status.ok()) { r->offset += block_contents.size() + kBlockTrailerSize; @@ -192,9 +193,7 @@ void TableBuilder::WriteRawBlock(const Slice& block_contents, } } -Status TableBuilder::status() const { - return rep_->status; -} +Status TableBuilder::status() const { return rep_->status; } Status TableBuilder::Finish() { Rep* r = rep_; @@ -205,7 +204,7 @@ Status TableBuilder::Finish() { BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle; // Write filter block - if (ok() && r->filter_block != NULL) { + if (ok() && r->filter_block != nullptr) { WriteRawBlock(r->filter_block->Finish(), kNoCompression, &filter_block_handle); } @@ -213,7 +212,7 @@ Status TableBuilder::Finish() { // Write metaindex block if (ok()) { BlockBuilder meta_index_block(&r->options); - if (r->filter_block != NULL) { + if (r->filter_block != nullptr) { // Add mapping from "filter.Name" to location of filter data std::string key = "filter."; key.append(r->options.filter_policy->Name()); @@ -259,12 +258,8 @@ void TableBuilder::Abandon() { r->closed = true; } -uint64_t TableBuilder::NumEntries() const { - return rep_->num_entries; -} +uint64_t TableBuilder::NumEntries() const { return rep_->num_entries; } -uint64_t TableBuilder::FileSize() const { - return rep_->offset; -} +uint64_t TableBuilder::FileSize() const { return rep_->offset; } } // namespace leveldb diff --git a/src/leveldb/table/table_test.cc b/src/leveldb/table/table_test.cc index c723bf84c..17aaea2f9 100644 --- a/src/leveldb/table/table_test.cc +++ b/src/leveldb/table/table_test.cc @@ -6,6 +6,7 @@ #include #include + #include "db/dbformat.h" #include "db/memtable.h" #include "db/write_batch_internal.h" @@ -27,8 +28,8 @@ namespace leveldb { static std::string Reverse(const Slice& key) { std::string str(key.ToString()); std::string rev(""); - for (std::string::reverse_iterator rit = str.rbegin(); - rit != str.rend(); ++rit) { + for (std::string::reverse_iterator rit = str.rbegin(); rit != str.rend(); + ++rit) { rev.push_back(*rit); } return rev; @@ -37,24 +38,23 @@ static std::string Reverse(const Slice& key) { namespace { class ReverseKeyComparator : public Comparator { public: - virtual const char* Name() const { + const char* Name() const override { return "leveldb.ReverseBytewiseComparator"; } - virtual int Compare(const Slice& a, const Slice& b) const { + int Compare(const Slice& a, const Slice& b) const override { return BytewiseComparator()->Compare(Reverse(a), Reverse(b)); } - virtual void FindShortestSeparator( - std::string* start, - const Slice& limit) const { + void FindShortestSeparator(std::string* start, + const Slice& limit) const override { std::string s = Reverse(*start); std::string l = Reverse(limit); BytewiseComparator()->FindShortestSeparator(&s, l); *start = Reverse(s); } - virtual void FindShortSuccessor(std::string* key) const { + void FindShortSuccessor(std::string* key) const override { std::string s = Reverse(*key); BytewiseComparator()->FindShortSuccessor(&s); *key = Reverse(s); @@ -79,47 +79,46 @@ namespace { struct STLLessThan { const Comparator* cmp; - STLLessThan() : cmp(BytewiseComparator()) { } - STLLessThan(const Comparator* c) : cmp(c) { } + STLLessThan() : cmp(BytewiseComparator()) {} + STLLessThan(const Comparator* c) : cmp(c) {} bool operator()(const std::string& a, const std::string& b) const { return cmp->Compare(Slice(a), Slice(b)) < 0; } }; } // namespace -class StringSink: public WritableFile { +class StringSink : public WritableFile { public: - ~StringSink() { } + ~StringSink() override = default; const std::string& contents() const { return contents_; } - virtual Status Close() { return Status::OK(); } - virtual Status Flush() { return Status::OK(); } - virtual Status Sync() { return Status::OK(); } + Status Close() override { return Status::OK(); } + Status Flush() override { return Status::OK(); } + Status Sync() override { return Status::OK(); } - virtual Status Append(const Slice& data) { + Status Append(const Slice& data) override { contents_.append(data.data(), data.size()); return Status::OK(); } + std::string GetName() const override { return ""; } private: std::string contents_; }; - -class StringSource: public RandomAccessFile { +class StringSource : public RandomAccessFile { public: StringSource(const Slice& contents) - : contents_(contents.data(), contents.size()) { - } + : contents_(contents.data(), contents.size()) {} - virtual ~StringSource() { } + ~StringSource() override = default; uint64_t Size() const { return contents_.size(); } - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { - if (offset > contents_.size()) { + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { + if (offset >= contents_.size()) { return Status::InvalidArgument("invalid Read offset"); } if (offset + n > contents_.size()) { @@ -130,6 +129,7 @@ class StringSource: public RandomAccessFile { return Status::OK(); } + std::string GetName() const { return ""; } private: std::string contents_; }; @@ -140,8 +140,8 @@ typedef std::map KVMap; // BlockBuilder/TableBuilder and Block/Table. class Constructor { public: - explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { } - virtual ~Constructor() { } + explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) {} + virtual ~Constructor() = default; void Add(const std::string& key, const Slice& value) { data_[key] = value.ToString(); @@ -150,15 +150,12 @@ class Constructor { // Finish constructing the data structure with all the keys that have // been added so far. Returns the keys in sorted order in "*keys" // and stores the key/value pairs in "*kvmap" - void Finish(const Options& options, - std::vector* keys, + void Finish(const Options& options, std::vector* keys, KVMap* kvmap) { *kvmap = data_; keys->clear(); - for (KVMap::const_iterator it = data_.begin(); - it != data_.end(); - ++it) { - keys->push_back(it->first); + for (const auto& kvp : data_) { + keys->push_back(kvp.first); } data_.clear(); Status s = FinishImpl(options, *kvmap); @@ -170,32 +167,26 @@ class Constructor { virtual Iterator* NewIterator() const = 0; - virtual const KVMap& data() { return data_; } + const KVMap& data() const { return data_; } - virtual DB* db() const { return NULL; } // Overridden in DBConstructor + virtual DB* db() const { return nullptr; } // Overridden in DBConstructor private: KVMap data_; }; -class BlockConstructor: public Constructor { +class BlockConstructor : public Constructor { public: explicit BlockConstructor(const Comparator* cmp) - : Constructor(cmp), - comparator_(cmp), - block_(NULL) { } - ~BlockConstructor() { + : Constructor(cmp), comparator_(cmp), block_(nullptr) {} + ~BlockConstructor() override { delete block_; } + Status FinishImpl(const Options& options, const KVMap& data) override { delete block_; - } - virtual Status FinishImpl(const Options& options, const KVMap& data) { - delete block_; - block_ = NULL; + block_ = nullptr; BlockBuilder builder(&options); - for (KVMap::const_iterator it = data.begin(); - it != data.end(); - ++it) { - builder.Add(it->first, it->second); + for (const auto& kvp : data) { + builder.Add(kvp.first, kvp.second); } // Open the block data_ = builder.Finish().ToString(); @@ -206,36 +197,30 @@ class BlockConstructor: public Constructor { block_ = new Block(contents); return Status::OK(); } - virtual Iterator* NewIterator() const { + Iterator* NewIterator() const override { return block_->NewIterator(comparator_); } private: - const Comparator* comparator_; + const Comparator* const comparator_; std::string data_; Block* block_; BlockConstructor(); }; -class TableConstructor: public Constructor { +class TableConstructor : public Constructor { public: TableConstructor(const Comparator* cmp) - : Constructor(cmp), - source_(NULL), table_(NULL) { - } - ~TableConstructor() { - Reset(); - } - virtual Status FinishImpl(const Options& options, const KVMap& data) { + : Constructor(cmp), source_(nullptr), table_(nullptr) {} + ~TableConstructor() override { Reset(); } + Status FinishImpl(const Options& options, const KVMap& data) override { Reset(); StringSink sink; TableBuilder builder(options, &sink); - for (KVMap::const_iterator it = data.begin(); - it != data.end(); - ++it) { - builder.Add(it->first, it->second); + for (const auto& kvp : data) { + builder.Add(kvp.first, kvp.second); ASSERT_TRUE(builder.status().ok()); } Status s = builder.Finish(); @@ -250,7 +235,7 @@ class TableConstructor: public Constructor { return Table::Open(table_options, source_, sink.contents().size(), &table_); } - virtual Iterator* NewIterator() const { + Iterator* NewIterator() const override { return table_->NewIterator(ReadOptions()); } @@ -262,8 +247,8 @@ class TableConstructor: public Constructor { void Reset() { delete table_; delete source_; - table_ = NULL; - source_ = NULL; + table_ = nullptr; + source_ = nullptr; } StringSource* source_; @@ -273,23 +258,28 @@ class TableConstructor: public Constructor { }; // A helper class that converts internal format keys into user keys -class KeyConvertingIterator: public Iterator { +class KeyConvertingIterator : public Iterator { public: - explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { } - virtual ~KeyConvertingIterator() { delete iter_; } - virtual bool Valid() const { return iter_->Valid(); } - virtual void Seek(const Slice& target) { + explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) {} + + KeyConvertingIterator(const KeyConvertingIterator&) = delete; + KeyConvertingIterator& operator=(const KeyConvertingIterator&) = delete; + + ~KeyConvertingIterator() override { delete iter_; } + + bool Valid() const override { return iter_->Valid(); } + void Seek(const Slice& target) override { ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue); std::string encoded; AppendInternalKey(&encoded, ikey); iter_->Seek(encoded); } - virtual void SeekToFirst() { iter_->SeekToFirst(); } - virtual void SeekToLast() { iter_->SeekToLast(); } - virtual void Next() { iter_->Next(); } - virtual void Prev() { iter_->Prev(); } + void SeekToFirst() override { iter_->SeekToFirst(); } + void SeekToLast() override { iter_->SeekToLast(); } + void Next() override { iter_->Next(); } + void Prev() override { iter_->Prev(); } - virtual Slice key() const { + Slice key() const override { assert(Valid()); ParsedInternalKey key; if (!ParseInternalKey(iter_->key(), &key)) { @@ -299,82 +289,68 @@ class KeyConvertingIterator: public Iterator { return key.user_key; } - virtual Slice value() const { return iter_->value(); } - virtual Status status() const { + Slice value() const override { return iter_->value(); } + Status status() const override { return status_.ok() ? iter_->status() : status_; } private: mutable Status status_; Iterator* iter_; - - // No copying allowed - KeyConvertingIterator(const KeyConvertingIterator&); - void operator=(const KeyConvertingIterator&); }; -class MemTableConstructor: public Constructor { +class MemTableConstructor : public Constructor { public: explicit MemTableConstructor(const Comparator* cmp) - : Constructor(cmp), - internal_comparator_(cmp) { + : Constructor(cmp), internal_comparator_(cmp) { memtable_ = new MemTable(internal_comparator_); memtable_->Ref(); } - ~MemTableConstructor() { - memtable_->Unref(); - } - virtual Status FinishImpl(const Options& options, const KVMap& data) { + ~MemTableConstructor() override { memtable_->Unref(); } + Status FinishImpl(const Options& options, const KVMap& data) override { memtable_->Unref(); memtable_ = new MemTable(internal_comparator_); memtable_->Ref(); int seq = 1; - for (KVMap::const_iterator it = data.begin(); - it != data.end(); - ++it) { - memtable_->Add(seq, kTypeValue, it->first, it->second); + for (const auto& kvp : data) { + memtable_->Add(seq, kTypeValue, kvp.first, kvp.second); seq++; } return Status::OK(); } - virtual Iterator* NewIterator() const { + Iterator* NewIterator() const override { return new KeyConvertingIterator(memtable_->NewIterator()); } private: - InternalKeyComparator internal_comparator_; + const InternalKeyComparator internal_comparator_; MemTable* memtable_; }; -class DBConstructor: public Constructor { +class DBConstructor : public Constructor { public: explicit DBConstructor(const Comparator* cmp) - : Constructor(cmp), - comparator_(cmp) { - db_ = NULL; + : Constructor(cmp), comparator_(cmp) { + db_ = nullptr; NewDB(); } - ~DBConstructor() { - delete db_; - } - virtual Status FinishImpl(const Options& options, const KVMap& data) { + ~DBConstructor() override { delete db_; } + Status FinishImpl(const Options& options, const KVMap& data) override { delete db_; - db_ = NULL; + db_ = nullptr; NewDB(); - for (KVMap::const_iterator it = data.begin(); - it != data.end(); - ++it) { + for (const auto& kvp : data) { WriteBatch batch; - batch.Put(it->first, it->second); + batch.Put(kvp.first, kvp.second); ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok()); } return Status::OK(); } - virtual Iterator* NewIterator() const { + Iterator* NewIterator() const override { return db_->NewIterator(ReadOptions()); } - virtual DB* db() const { return db_; } + DB* db() const override { return db_; } private: void NewDB() { @@ -392,16 +368,11 @@ class DBConstructor: public Constructor { ASSERT_TRUE(status.ok()) << status.ToString(); } - const Comparator* comparator_; + const Comparator* const comparator_; DB* db_; }; -enum TestType { - TABLE_TEST, - BLOCK_TEST, - MEMTABLE_TEST, - DB_TEST -}; +enum TestType { TABLE_TEST, BLOCK_TEST, MEMTABLE_TEST, DB_TEST }; struct TestArgs { TestType type; @@ -410,37 +381,37 @@ struct TestArgs { }; static const TestArgs kTestArgList[] = { - { TABLE_TEST, false, 16 }, - { TABLE_TEST, false, 1 }, - { TABLE_TEST, false, 1024 }, - { TABLE_TEST, true, 16 }, - { TABLE_TEST, true, 1 }, - { TABLE_TEST, true, 1024 }, - - { BLOCK_TEST, false, 16 }, - { BLOCK_TEST, false, 1 }, - { BLOCK_TEST, false, 1024 }, - { BLOCK_TEST, true, 16 }, - { BLOCK_TEST, true, 1 }, - { BLOCK_TEST, true, 1024 }, - - // Restart interval does not matter for memtables - { MEMTABLE_TEST, false, 16 }, - { MEMTABLE_TEST, true, 16 }, - - // Do not bother with restart interval variations for DB - { DB_TEST, false, 16 }, - { DB_TEST, true, 16 }, + {TABLE_TEST, false, 16}, + {TABLE_TEST, false, 1}, + {TABLE_TEST, false, 1024}, + {TABLE_TEST, true, 16}, + {TABLE_TEST, true, 1}, + {TABLE_TEST, true, 1024}, + + {BLOCK_TEST, false, 16}, + {BLOCK_TEST, false, 1}, + {BLOCK_TEST, false, 1024}, + {BLOCK_TEST, true, 16}, + {BLOCK_TEST, true, 1}, + {BLOCK_TEST, true, 1024}, + + // Restart interval does not matter for memtables + {MEMTABLE_TEST, false, 16}, + {MEMTABLE_TEST, true, 16}, + + // Do not bother with restart interval variations for DB + {DB_TEST, false, 16}, + {DB_TEST, true, 16}, }; static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]); class Harness { public: - Harness() : constructor_(NULL) { } + Harness() : constructor_(nullptr) {} void Init(const TestArgs& args) { delete constructor_; - constructor_ = NULL; + constructor_ = nullptr; options_ = Options(); options_.block_restart_interval = args.restart_interval; @@ -466,9 +437,7 @@ class Harness { } } - ~Harness() { - delete constructor_; - } + ~Harness() { delete constructor_; } void Add(const std::string& key, const std::string& value) { constructor_->Add(key, value); @@ -490,8 +459,7 @@ class Harness { ASSERT_TRUE(!iter->Valid()); iter->SeekToFirst(); for (KVMap::const_iterator model_iter = data.begin(); - model_iter != data.end(); - ++model_iter) { + model_iter != data.end(); ++model_iter) { ASSERT_EQ(ToString(data, model_iter), ToString(iter)); iter->Next(); } @@ -505,8 +473,7 @@ class Harness { ASSERT_TRUE(!iter->Valid()); iter->SeekToLast(); for (KVMap::const_reverse_iterator model_iter = data.rbegin(); - model_iter != data.rend(); - ++model_iter) { + model_iter != data.rend(); ++model_iter) { ASSERT_EQ(ToString(data, model_iter), ToString(iter)); iter->Prev(); } @@ -514,8 +481,7 @@ class Harness { delete iter; } - void TestRandomAccess(Random* rnd, - const std::vector& keys, + void TestRandomAccess(Random* rnd, const std::vector& keys, const KVMap& data) { static const bool kVerbose = false; Iterator* iter = constructor_->NewIterator(); @@ -546,8 +512,8 @@ class Harness { case 2: { std::string key = PickRandomKey(rnd, keys); model_iter = data.lower_bound(key); - if (kVerbose) fprintf(stderr, "Seek '%s'\n", - EscapeString(key).c_str()); + if (kVerbose) + fprintf(stderr, "Seek '%s'\n", EscapeString(key).c_str()); iter->Seek(Slice(key)); ASSERT_EQ(ToString(data, model_iter), ToString(iter)); break; @@ -558,7 +524,7 @@ class Harness { if (kVerbose) fprintf(stderr, "Prev\n"); iter->Prev(); if (model_iter == data.begin()) { - model_iter = data.end(); // Wrap around to invalid value + model_iter = data.end(); // Wrap around to invalid value } else { --model_iter; } @@ -621,8 +587,8 @@ class Harness { break; case 1: { // Attempt to return something smaller than an existing key - if (result.size() > 0 && result[result.size()-1] > '\0') { - result[result.size()-1]--; + if (!result.empty() && result[result.size() - 1] > '\0') { + result[result.size() - 1]--; } break; } @@ -636,7 +602,7 @@ class Harness { } } - // Returns NULL if not running against a DB + // Returns nullptr if not running against a DB DB* db() const { return constructor_->db(); } private: @@ -720,8 +686,8 @@ TEST(Harness, Randomized) { for (int num_entries = 0; num_entries < 2000; num_entries += (num_entries < 50 ? 1 : 200)) { if ((num_entries % 10) == 0) { - fprintf(stderr, "case %d of %d: num_entries = %d\n", - (i + 1), int(kNumTestArgs), num_entries); + fprintf(stderr, "case %d of %d: num_entries = %d\n", (i + 1), + int(kNumTestArgs), num_entries); } for (int e = 0; e < num_entries; e++) { std::string v; @@ -735,7 +701,7 @@ TEST(Harness, Randomized) { TEST(Harness, RandomizedLongDB) { Random rnd(test::RandomSeed()); - TestArgs args = { DB_TEST, false, 16 }; + TestArgs args = {DB_TEST, false, 16}; Init(args); int num_entries = 100000; for (int e = 0; e < num_entries; e++) { @@ -757,7 +723,7 @@ TEST(Harness, RandomizedLongDB) { ASSERT_GT(files, 0); } -class MemTableTest { }; +class MemTableTest {}; TEST(MemTableTest, Simple) { InternalKeyComparator cmp(BytewiseComparator()); @@ -774,8 +740,7 @@ TEST(MemTableTest, Simple) { Iterator* iter = memtable->NewIterator(); iter->SeekToFirst(); while (iter->Valid()) { - fprintf(stderr, "key: '%s' -> '%s'\n", - iter->key().ToString().c_str(), + fprintf(stderr, "key: '%s' -> '%s'\n", iter->key().ToString().c_str(), iter->value().ToString().c_str()); iter->Next(); } @@ -788,14 +753,13 @@ static bool Between(uint64_t val, uint64_t low, uint64_t high) { bool result = (val >= low) && (val <= high); if (!result) { fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n", - (unsigned long long)(val), - (unsigned long long)(low), + (unsigned long long)(val), (unsigned long long)(low), (unsigned long long)(high)); } return result; } -class TableTest { }; +class TableTest {}; TEST(TableTest, ApproximateOffsetOfPlain) { TableConstructor c(BytewiseComparator()); @@ -813,18 +777,17 @@ TEST(TableTest, ApproximateOffsetOfPlain) { options.compression = kNoCompression; c.Finish(options, &keys, &kvmap); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000)); ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); - + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000)); } static bool SnappyCompressionSupported() { @@ -853,16 +816,22 @@ TEST(TableTest, ApproximateOffsetOfCompressed) { options.compression = kSnappyCompression; c.Finish(options, &keys, &kvmap); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000)); - ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000)); + // Expected upper and lower bounds of space used by compressible strings. + static const int kSlop = 1000; // Compressor effectiveness varies. + const int expected = 2500; // 10000 * compression ratio (0.25) + const int min_z = expected - kSlop; + const int max_z = expected + kSlop; + + ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, kSlop)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, kSlop)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, kSlop)); + // Have now emitted a large compressible string, so adjust expected offset. + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), min_z, max_z)); + ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), min_z, max_z)); + // Have now emitted two large compressible strings, so adjust expected offset. + ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 2 * min_z, 2 * max_z)); } } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/table/two_level_iterator.cc b/src/leveldb/table/two_level_iterator.cc index 7822ebab9..144790dd9 100644 --- a/src/leveldb/table/two_level_iterator.cc +++ b/src/leveldb/table/two_level_iterator.cc @@ -15,38 +15,33 @@ namespace { typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&); -class TwoLevelIterator: public Iterator { +class TwoLevelIterator : public Iterator { public: - TwoLevelIterator( - Iterator* index_iter, - BlockFunction block_function, - void* arg, - const ReadOptions& options); - - virtual ~TwoLevelIterator(); - - virtual void Seek(const Slice& target); - virtual void SeekToFirst(); - virtual void SeekToLast(); - virtual void Next(); - virtual void Prev(); - - virtual bool Valid() const { - return data_iter_.Valid(); - } - virtual Slice key() const { + TwoLevelIterator(Iterator* index_iter, BlockFunction block_function, + void* arg, const ReadOptions& options); + + ~TwoLevelIterator() override; + + void Seek(const Slice& target) override; + void SeekToFirst() override; + void SeekToLast() override; + void Next() override; + void Prev() override; + + bool Valid() const override { return data_iter_.Valid(); } + Slice key() const override { assert(Valid()); return data_iter_.key(); } - virtual Slice value() const { + Slice value() const override { assert(Valid()); return data_iter_.value(); } - virtual Status status() const { + Status status() const override { // It'd be nice if status() returned a const Status& instead of a Status if (!index_iter_.status().ok()) { return index_iter_.status(); - } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) { + } else if (data_iter_.iter() != nullptr && !data_iter_.status().ok()) { return data_iter_.status(); } else { return status_; @@ -67,45 +62,41 @@ class TwoLevelIterator: public Iterator { const ReadOptions options_; Status status_; IteratorWrapper index_iter_; - IteratorWrapper data_iter_; // May be NULL - // If data_iter_ is non-NULL, then "data_block_handle_" holds the + IteratorWrapper data_iter_; // May be nullptr + // If data_iter_ is non-null, then "data_block_handle_" holds the // "index_value" passed to block_function_ to create the data_iter_. std::string data_block_handle_; }; -TwoLevelIterator::TwoLevelIterator( - Iterator* index_iter, - BlockFunction block_function, - void* arg, - const ReadOptions& options) +TwoLevelIterator::TwoLevelIterator(Iterator* index_iter, + BlockFunction block_function, void* arg, + const ReadOptions& options) : block_function_(block_function), arg_(arg), options_(options), index_iter_(index_iter), - data_iter_(NULL) { -} + data_iter_(nullptr) {} -TwoLevelIterator::~TwoLevelIterator() { -} +TwoLevelIterator::~TwoLevelIterator() = default; void TwoLevelIterator::Seek(const Slice& target) { index_iter_.Seek(target); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.Seek(target); + if (data_iter_.iter() != nullptr) data_iter_.Seek(target); SkipEmptyDataBlocksForward(); } void TwoLevelIterator::SeekToFirst() { index_iter_.SeekToFirst(); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst(); SkipEmptyDataBlocksForward(); } void TwoLevelIterator::SeekToLast() { index_iter_.SeekToLast(); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + if (data_iter_.iter() != nullptr) data_iter_.SeekToLast(); SkipEmptyDataBlocksBackward(); } @@ -121,44 +112,44 @@ void TwoLevelIterator::Prev() { SkipEmptyDataBlocksBackward(); } - void TwoLevelIterator::SkipEmptyDataBlocksForward() { - while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + while (data_iter_.iter() == nullptr || !data_iter_.Valid()) { // Move to next block if (!index_iter_.Valid()) { - SetDataIterator(NULL); + SetDataIterator(nullptr); return; } index_iter_.Next(); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.SeekToFirst(); + if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst(); } } void TwoLevelIterator::SkipEmptyDataBlocksBackward() { - while (data_iter_.iter() == NULL || !data_iter_.Valid()) { + while (data_iter_.iter() == nullptr || !data_iter_.Valid()) { // Move to next block if (!index_iter_.Valid()) { - SetDataIterator(NULL); + SetDataIterator(nullptr); return; } index_iter_.Prev(); InitDataBlock(); - if (data_iter_.iter() != NULL) data_iter_.SeekToLast(); + if (data_iter_.iter() != nullptr) data_iter_.SeekToLast(); } } void TwoLevelIterator::SetDataIterator(Iterator* data_iter) { - if (data_iter_.iter() != NULL) SaveError(data_iter_.status()); + if (data_iter_.iter() != nullptr) SaveError(data_iter_.status()); data_iter_.Set(data_iter); } void TwoLevelIterator::InitDataBlock() { if (!index_iter_.Valid()) { - SetDataIterator(NULL); + SetDataIterator(nullptr); } else { Slice handle = index_iter_.value(); - if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) { + if (data_iter_.iter() != nullptr && + handle.compare(data_block_handle_) == 0) { // data_iter_ is already constructed with this iterator, so // no need to change anything } else { @@ -171,11 +162,9 @@ void TwoLevelIterator::InitDataBlock() { } // namespace -Iterator* NewTwoLevelIterator( - Iterator* index_iter, - BlockFunction block_function, - void* arg, - const ReadOptions& options) { +Iterator* NewTwoLevelIterator(Iterator* index_iter, + BlockFunction block_function, void* arg, + const ReadOptions& options) { return new TwoLevelIterator(index_iter, block_function, arg, options); } diff --git a/src/leveldb/table/two_level_iterator.h b/src/leveldb/table/two_level_iterator.h index 629ca3452..81ffe809a 100644 --- a/src/leveldb/table/two_level_iterator.h +++ b/src/leveldb/table/two_level_iterator.h @@ -20,14 +20,11 @@ struct ReadOptions; // // Uses a supplied function to convert an index_iter value into // an iterator over the contents of the corresponding block. -extern Iterator* NewTwoLevelIterator( +Iterator* NewTwoLevelIterator( Iterator* index_iter, - Iterator* (*block_function)( - void* arg, - const ReadOptions& options, - const Slice& index_value), - void* arg, - const ReadOptions& options); + Iterator* (*block_function)(void* arg, const ReadOptions& options, + const Slice& index_value), + void* arg, const ReadOptions& options); } // namespace leveldb diff --git a/src/leveldb/util/arena.cc b/src/leveldb/util/arena.cc index 9367f7149..46e3b2eb8 100644 --- a/src/leveldb/util/arena.cc +++ b/src/leveldb/util/arena.cc @@ -3,17 +3,13 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. #include "util/arena.h" -#include namespace leveldb { static const int kBlockSize = 4096; -Arena::Arena() { - blocks_memory_ = 0; - alloc_ptr_ = NULL; // First allocation will allocate a block - alloc_bytes_remaining_ = 0; -} +Arena::Arena() + : alloc_ptr_(nullptr), alloc_bytes_remaining_(0), memory_usage_(0) {} Arena::~Arena() { for (size_t i = 0; i < blocks_.size(); i++) { @@ -41,8 +37,9 @@ char* Arena::AllocateFallback(size_t bytes) { char* Arena::AllocateAligned(size_t bytes) { const int align = (sizeof(void*) > 8) ? sizeof(void*) : 8; - assert((align & (align-1)) == 0); // Pointer size should be a power of 2 - size_t current_mod = reinterpret_cast(alloc_ptr_) & (align-1); + static_assert((align & (align - 1)) == 0, + "Pointer size should be a power of 2"); + size_t current_mod = reinterpret_cast(alloc_ptr_) & (align - 1); size_t slop = (current_mod == 0 ? 0 : align - current_mod); size_t needed = bytes + slop; char* result; @@ -54,14 +51,15 @@ char* Arena::AllocateAligned(size_t bytes) { // AllocateFallback always returned aligned memory result = AllocateFallback(bytes); } - assert((reinterpret_cast(result) & (align-1)) == 0); + assert((reinterpret_cast(result) & (align - 1)) == 0); return result; } char* Arena::AllocateNewBlock(size_t block_bytes) { char* result = new char[block_bytes]; - blocks_memory_ += block_bytes; blocks_.push_back(result); + memory_usage_.fetch_add(block_bytes + sizeof(char*), + std::memory_order_relaxed); return result; } diff --git a/src/leveldb/util/arena.h b/src/leveldb/util/arena.h index 73bbf1cb9..68fc55d4d 100644 --- a/src/leveldb/util/arena.h +++ b/src/leveldb/util/arena.h @@ -5,29 +5,33 @@ #ifndef STORAGE_LEVELDB_UTIL_ARENA_H_ #define STORAGE_LEVELDB_UTIL_ARENA_H_ +#include +#include +#include +#include #include -#include -#include -#include namespace leveldb { class Arena { public: Arena(); + + Arena(const Arena&) = delete; + Arena& operator=(const Arena&) = delete; + ~Arena(); // Return a pointer to a newly allocated memory block of "bytes" bytes. char* Allocate(size_t bytes); - // Allocate memory with the normal alignment guarantees provided by malloc + // Allocate memory with the normal alignment guarantees provided by malloc. char* AllocateAligned(size_t bytes); // Returns an estimate of the total memory usage of data allocated - // by the arena (including space allocated but not yet used for user - // allocations). + // by the arena. size_t MemoryUsage() const { - return blocks_memory_ + blocks_.capacity() * sizeof(char*); + return memory_usage_.load(std::memory_order_relaxed); } private: @@ -41,12 +45,11 @@ class Arena { // Array of new[] allocated memory blocks std::vector blocks_; - // Bytes of memory in blocks allocated so far - size_t blocks_memory_; - - // No copying allowed - Arena(const Arena&); - void operator=(const Arena&); + // Total memory usage of the arena. + // + // TODO(costan): This member is accessed via atomics, but the others are + // accessed without any locking. Is this OK? + std::atomic memory_usage_; }; inline char* Arena::Allocate(size_t bytes) { diff --git a/src/leveldb/util/arena_test.cc b/src/leveldb/util/arena_test.cc index 58e870ec4..e917228f4 100644 --- a/src/leveldb/util/arena_test.cc +++ b/src/leveldb/util/arena_test.cc @@ -9,14 +9,12 @@ namespace leveldb { -class ArenaTest { }; +class ArenaTest {}; -TEST(ArenaTest, Empty) { - Arena arena; -} +TEST(ArenaTest, Empty) { Arena arena; } TEST(ArenaTest, Simple) { - std::vector > allocated; + std::vector> allocated; Arena arena; const int N = 100000; size_t bytes = 0; @@ -26,8 +24,9 @@ TEST(ArenaTest, Simple) { if (i % (N / 10) == 0) { s = i; } else { - s = rnd.OneIn(4000) ? rnd.Uniform(6000) : - (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); + s = rnd.OneIn(4000) + ? rnd.Uniform(6000) + : (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); } if (s == 0) { // Our arena disallows size 0 allocations. @@ -47,7 +46,7 @@ TEST(ArenaTest, Simple) { bytes += s; allocated.push_back(std::make_pair(s, r)); ASSERT_GE(arena.MemoryUsage(), bytes); - if (i > N/10) { + if (i > N / 10) { ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); } } @@ -63,6 +62,4 @@ TEST(ArenaTest, Simple) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/bloom.cc b/src/leveldb/util/bloom.cc index a27a2ace2..87547a7e6 100644 --- a/src/leveldb/util/bloom.cc +++ b/src/leveldb/util/bloom.cc @@ -15,24 +15,17 @@ static uint32_t BloomHash(const Slice& key) { } class BloomFilterPolicy : public FilterPolicy { - private: - size_t bits_per_key_; - size_t k_; - public: - explicit BloomFilterPolicy(int bits_per_key) - : bits_per_key_(bits_per_key) { + explicit BloomFilterPolicy(int bits_per_key) : bits_per_key_(bits_per_key) { // We intentionally round down to reduce probing cost a little bit k_ = static_cast(bits_per_key * 0.69); // 0.69 =~ ln(2) if (k_ < 1) k_ = 1; if (k_ > 30) k_ = 30; } - virtual const char* Name() const { - return "leveldb.BuiltinBloomFilter2"; - } + const char* Name() const override { return "leveldb.BuiltinBloomFilter2"; } - virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const { + void CreateFilter(const Slice* keys, int n, std::string* dst) const override { // Compute bloom filter size (in both bits and bytes) size_t bits = n * bits_per_key_; @@ -47,20 +40,20 @@ class BloomFilterPolicy : public FilterPolicy { dst->resize(init_size + bytes, 0); dst->push_back(static_cast(k_)); // Remember # of probes in filter char* array = &(*dst)[init_size]; - for (size_t i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { // Use double-hashing to generate a sequence of hash values. // See analysis in [Kirsch,Mitzenmacher 2006]. uint32_t h = BloomHash(keys[i]); const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits for (size_t j = 0; j < k_; j++) { const uint32_t bitpos = h % bits; - array[bitpos/8] |= (1 << (bitpos % 8)); + array[bitpos / 8] |= (1 << (bitpos % 8)); h += delta; } } } - virtual bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const { + bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const override { const size_t len = bloom_filter.size(); if (len < 2) return false; @@ -69,7 +62,7 @@ class BloomFilterPolicy : public FilterPolicy { // Use the encoded k so that we can read filters generated by // bloom filters created using different parameters. - const size_t k = array[len-1]; + const size_t k = array[len - 1]; if (k > 30) { // Reserved for potentially new encodings for short bloom filters. // Consider it a match. @@ -80,13 +73,17 @@ class BloomFilterPolicy : public FilterPolicy { const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits for (size_t j = 0; j < k; j++) { const uint32_t bitpos = h % bits; - if ((array[bitpos/8] & (1 << (bitpos % 8))) == 0) return false; + if ((array[bitpos / 8] & (1 << (bitpos % 8))) == 0) return false; h += delta; } return true; } + + private: + size_t bits_per_key_; + size_t k_; }; -} +} // namespace const FilterPolicy* NewBloomFilterPolicy(int bits_per_key) { return new BloomFilterPolicy(bits_per_key); diff --git a/src/leveldb/util/bloom_test.cc b/src/leveldb/util/bloom_test.cc index 77fb1b315..436daa9e9 100644 --- a/src/leveldb/util/bloom_test.cc +++ b/src/leveldb/util/bloom_test.cc @@ -19,26 +19,17 @@ static Slice Key(int i, char* buffer) { } class BloomTest { - private: - const FilterPolicy* policy_; - std::string filter_; - std::vector keys_; - public: - BloomTest() : policy_(NewBloomFilterPolicy(10)) { } + BloomTest() : policy_(NewBloomFilterPolicy(10)) {} - ~BloomTest() { - delete policy_; - } + ~BloomTest() { delete policy_; } void Reset() { keys_.clear(); filter_.clear(); } - void Add(const Slice& s) { - keys_.push_back(s.ToString()); - } + void Add(const Slice& s) { keys_.push_back(s.ToString()); } void Build() { std::vector key_slices; @@ -46,21 +37,20 @@ class BloomTest { key_slices.push_back(Slice(keys_[i])); } filter_.clear(); - policy_->CreateFilter(&key_slices[0], key_slices.size(), &filter_); + policy_->CreateFilter(&key_slices[0], static_cast(key_slices.size()), + &filter_); keys_.clear(); if (kVerbose >= 2) DumpFilter(); } - size_t FilterSize() const { - return filter_.size(); - } + size_t FilterSize() const { return filter_.size(); } void DumpFilter() { fprintf(stderr, "F("); - for (size_t i = 0; i+1 < filter_.size(); i++) { + for (size_t i = 0; i + 1 < filter_.size(); i++) { const unsigned int c = static_cast(filter_[i]); for (int j = 0; j < 8; j++) { - fprintf(stderr, "%c", (c & (1 < keys_; }; TEST(BloomTest, EmptyFilter) { - ASSERT_TRUE(! Matches("hello")); - ASSERT_TRUE(! Matches("world")); + ASSERT_TRUE(!Matches("hello")); + ASSERT_TRUE(!Matches("world")); } TEST(BloomTest, Small) { @@ -95,8 +90,8 @@ TEST(BloomTest, Small) { Add("world"); ASSERT_TRUE(Matches("hello")); ASSERT_TRUE(Matches("world")); - ASSERT_TRUE(! Matches("x")); - ASSERT_TRUE(! Matches("foo")); + ASSERT_TRUE(!Matches("x")); + ASSERT_TRUE(!Matches("foo")); } static int NextLength(int length) { @@ -139,23 +134,23 @@ TEST(BloomTest, VaryingLengths) { double rate = FalsePositiveRate(); if (kVerbose >= 1) { fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n", - rate*100.0, length, static_cast(FilterSize())); + rate * 100.0, length, static_cast(FilterSize())); } - ASSERT_LE(rate, 0.02); // Must not be over 2% - if (rate > 0.0125) mediocre_filters++; // Allowed, but not too often - else good_filters++; + ASSERT_LE(rate, 0.02); // Must not be over 2% + if (rate > 0.0125) + mediocre_filters++; // Allowed, but not too often + else + good_filters++; } if (kVerbose >= 1) { - fprintf(stderr, "Filters: %d good, %d mediocre\n", - good_filters, mediocre_filters); + fprintf(stderr, "Filters: %d good, %d mediocre\n", good_filters, + mediocre_filters); } - ASSERT_LE(mediocre_filters, good_filters/5); + ASSERT_LE(mediocre_filters, good_filters / 5); } // Different bits-per-byte } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/cache.cc b/src/leveldb/util/cache.cc index 8b197bc02..12de306ca 100644 --- a/src/leveldb/util/cache.cc +++ b/src/leveldb/util/cache.cc @@ -8,17 +8,34 @@ #include "leveldb/cache.h" #include "port/port.h" +#include "port/thread_annotations.h" #include "util/hash.h" #include "util/mutexlock.h" namespace leveldb { -Cache::~Cache() { -} +Cache::~Cache() {} namespace { // LRU cache implementation +// +// Cache entries have an "in_cache" boolean indicating whether the cache has a +// reference on the entry. The only ways that this can become false without the +// entry being passed to its "deleter" are via Erase(), via Insert() when +// an element with a duplicate key is inserted, or on destruction of the cache. +// +// The cache keeps two linked lists of items in the cache. All items in the +// cache are in one list or the other, and never both. Items still referenced +// by clients but erased from the cache are in neither list. The lists are: +// - in-use: contains the items currently referenced by clients, in no +// particular order. (This list is used for invariant checking. If we +// removed the check, elements that would otherwise be on this list could be +// left as disconnected singleton lists.) +// - LRU: contains the items not currently referenced by clients, in LRU order +// Elements are moved between these lists by the Ref() and Unref() methods, +// when they detect an element in the cache acquiring or losing its only +// external reference. // An entry is a variable length heap-allocated structure. Entries // are kept in a circular doubly linked list ordered by access time. @@ -28,20 +45,19 @@ struct LRUHandle { LRUHandle* next_hash; LRUHandle* next; LRUHandle* prev; - size_t charge; // TODO(opt): Only allow uint32_t? + size_t charge; // TODO(opt): Only allow uint32_t? size_t key_length; - uint32_t refs; - uint32_t hash; // Hash of key(); used for fast sharding and comparisons - char key_data[1]; // Beginning of key + bool in_cache; // Whether entry is in the cache. + uint32_t refs; // References, including cache reference, if present. + uint32_t hash; // Hash of key(); used for fast sharding and comparisons + char key_data[1]; // Beginning of key Slice key() const { - // For cheaper lookups, we allow a temporary Handle object - // to store a pointer to a key in "value". - if (next == this) { - return *(reinterpret_cast(value)); - } else { - return Slice(key_data, key_length); - } + // next_ is only equal to this if the LRU handle is the list head of an + // empty list. List heads never have meaningful keys. + assert(next != this); + + return Slice(key_data, key_length); } }; @@ -52,7 +68,7 @@ struct LRUHandle { // 4.4.3's builtin hashtable. class HandleTable { public: - HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); } + HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); } ~HandleTable() { delete[] list_; } LRUHandle* Lookup(const Slice& key, uint32_t hash) { @@ -62,9 +78,9 @@ class HandleTable { LRUHandle* Insert(LRUHandle* h) { LRUHandle** ptr = FindPointer(h->key(), h->hash); LRUHandle* old = *ptr; - h->next_hash = (old == NULL ? NULL : old->next_hash); + h->next_hash = (old == nullptr ? nullptr : old->next_hash); *ptr = h; - if (old == NULL) { + if (old == nullptr) { ++elems_; if (elems_ > length_) { // Since each cache entry is fairly large, we aim for a small @@ -78,7 +94,7 @@ class HandleTable { LRUHandle* Remove(const Slice& key, uint32_t hash) { LRUHandle** ptr = FindPointer(key, hash); LRUHandle* result = *ptr; - if (result != NULL) { + if (result != nullptr) { *ptr = result->next_hash; --elems_; } @@ -97,8 +113,7 @@ class HandleTable { // pointer to the trailing slot in the corresponding linked list. LRUHandle** FindPointer(const Slice& key, uint32_t hash) { LRUHandle** ptr = &list_[hash & (length_ - 1)]; - while (*ptr != NULL && - ((*ptr)->hash != hash || key != (*ptr)->key())) { + while (*ptr != nullptr && ((*ptr)->hash != hash || key != (*ptr)->key())) { ptr = &(*ptr)->next_hash; } return ptr; @@ -114,7 +129,7 @@ class HandleTable { uint32_t count = 0; for (uint32_t i = 0; i < length_; i++) { LRUHandle* h = list_[i]; - while (h != NULL) { + while (h != nullptr) { LRUHandle* next = h->next_hash; uint32_t hash = h->hash; LRUHandle** ptr = &new_list[hash & (new_length - 1)]; @@ -141,55 +156,83 @@ class LRUCache { void SetCapacity(size_t capacity) { capacity_ = capacity; } // Like Cache methods, but with an extra "hash" parameter. - Cache::Handle* Insert(const Slice& key, uint32_t hash, - void* value, size_t charge, + Cache::Handle* Insert(const Slice& key, uint32_t hash, void* value, + size_t charge, void (*deleter)(const Slice& key, void* value)); Cache::Handle* Lookup(const Slice& key, uint32_t hash); void Release(Cache::Handle* handle); void Erase(const Slice& key, uint32_t hash); + void Prune(); + size_t TotalCharge() const { + MutexLock l(&mutex_); + return usage_; + } private: void LRU_Remove(LRUHandle* e); - void LRU_Append(LRUHandle* e); + void LRU_Append(LRUHandle* list, LRUHandle* e); + void Ref(LRUHandle* e); void Unref(LRUHandle* e); + bool FinishErase(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_); // Initialized before use. size_t capacity_; // mutex_ protects the following state. - port::Mutex mutex_; - size_t usage_; + mutable port::Mutex mutex_; + size_t usage_ GUARDED_BY(mutex_); // Dummy head of LRU list. // lru.prev is newest entry, lru.next is oldest entry. - LRUHandle lru_; + // Entries have refs==1 and in_cache==true. + LRUHandle lru_ GUARDED_BY(mutex_); + + // Dummy head of in-use list. + // Entries are in use by clients, and have refs >= 2 and in_cache==true. + LRUHandle in_use_ GUARDED_BY(mutex_); - HandleTable table_; + HandleTable table_ GUARDED_BY(mutex_); }; -LRUCache::LRUCache() - : usage_(0) { - // Make empty circular linked list +LRUCache::LRUCache() : capacity_(0), usage_(0) { + // Make empty circular linked lists. lru_.next = &lru_; lru_.prev = &lru_; + in_use_.next = &in_use_; + in_use_.prev = &in_use_; } LRUCache::~LRUCache() { - for (LRUHandle* e = lru_.next; e != &lru_; ) { + assert(in_use_.next == &in_use_); // Error if caller has an unreleased handle + for (LRUHandle* e = lru_.next; e != &lru_;) { LRUHandle* next = e->next; - assert(e->refs == 1); // Error if caller has an unreleased handle + assert(e->in_cache); + e->in_cache = false; + assert(e->refs == 1); // Invariant of lru_ list. Unref(e); e = next; } } +void LRUCache::Ref(LRUHandle* e) { + if (e->refs == 1 && e->in_cache) { // If on lru_ list, move to in_use_ list. + LRU_Remove(e); + LRU_Append(&in_use_, e); + } + e->refs++; +} + void LRUCache::Unref(LRUHandle* e) { assert(e->refs > 0); e->refs--; - if (e->refs <= 0) { - usage_ -= e->charge; + if (e->refs == 0) { // Deallocate. + assert(!e->in_cache); (*e->deleter)(e->key(), e->value); free(e); + } else if (e->in_cache && e->refs == 1) { + // No longer in use; move to lru_ list. + LRU_Remove(e); + LRU_Append(&lru_, e); } } @@ -198,10 +241,10 @@ void LRUCache::LRU_Remove(LRUHandle* e) { e->prev->next = e->next; } -void LRUCache::LRU_Append(LRUHandle* e) { - // Make "e" newest entry by inserting just before lru_ - e->next = &lru_; - e->prev = lru_.prev; +void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) { + // Make "e" newest entry by inserting just before *list + e->next = list; + e->prev = list->prev; e->prev->next = e; e->next->prev = e; } @@ -209,10 +252,8 @@ void LRUCache::LRU_Append(LRUHandle* e) { Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) { MutexLock l(&mutex_); LRUHandle* e = table_.Lookup(key, hash); - if (e != NULL) { - e->refs++; - LRU_Remove(e); - LRU_Append(e); + if (e != nullptr) { + Ref(e); } return reinterpret_cast(e); } @@ -222,46 +263,73 @@ void LRUCache::Release(Cache::Handle* handle) { Unref(reinterpret_cast(handle)); } -Cache::Handle* LRUCache::Insert( - const Slice& key, uint32_t hash, void* value, size_t charge, - void (*deleter)(const Slice& key, void* value)) { +Cache::Handle* LRUCache::Insert(const Slice& key, uint32_t hash, void* value, + size_t charge, + void (*deleter)(const Slice& key, + void* value)) { MutexLock l(&mutex_); - LRUHandle* e = reinterpret_cast( - malloc(sizeof(LRUHandle)-1 + key.size())); + LRUHandle* e = + reinterpret_cast(malloc(sizeof(LRUHandle) - 1 + key.size())); e->value = value; e->deleter = deleter; e->charge = charge; e->key_length = key.size(); e->hash = hash; - e->refs = 2; // One from LRUCache, one for the returned handle + e->in_cache = false; + e->refs = 1; // for the returned handle. memcpy(e->key_data, key.data(), key.size()); - LRU_Append(e); - usage_ += charge; - LRUHandle* old = table_.Insert(e); - if (old != NULL) { - LRU_Remove(old); - Unref(old); + if (capacity_ > 0) { + e->refs++; // for the cache's reference. + e->in_cache = true; + LRU_Append(&in_use_, e); + usage_ += charge; + FinishErase(table_.Insert(e)); + } else { // don't cache. (capacity_==0 is supported and turns off caching.) + // next is read by key() in an assert, so it must be initialized + e->next = nullptr; } - while (usage_ > capacity_ && lru_.next != &lru_) { LRUHandle* old = lru_.next; - LRU_Remove(old); - table_.Remove(old->key(), old->hash); - Unref(old); + assert(old->refs == 1); + bool erased = FinishErase(table_.Remove(old->key(), old->hash)); + if (!erased) { // to avoid unused variable when compiled NDEBUG + assert(erased); + } } return reinterpret_cast(e); } -void LRUCache::Erase(const Slice& key, uint32_t hash) { - MutexLock l(&mutex_); - LRUHandle* e = table_.Remove(key, hash); - if (e != NULL) { +// If e != nullptr, finish removing *e from the cache; it has already been +// removed from the hash table. Return whether e != nullptr. +bool LRUCache::FinishErase(LRUHandle* e) { + if (e != nullptr) { + assert(e->in_cache); LRU_Remove(e); + e->in_cache = false; + usage_ -= e->charge; Unref(e); } + return e != nullptr; +} + +void LRUCache::Erase(const Slice& key, uint32_t hash) { + MutexLock l(&mutex_); + FinishErase(table_.Remove(key, hash)); +} + +void LRUCache::Prune() { + MutexLock l(&mutex_); + while (lru_.next != &lru_) { + LRUHandle* e = lru_.next; + assert(e->refs == 1); + bool erased = FinishErase(table_.Remove(e->key(), e->hash)); + if (!erased) { // to avoid unused variable when compiled NDEBUG + assert(erased); + } + } } static const int kNumShardBits = 4; @@ -277,49 +345,56 @@ class ShardedLRUCache : public Cache { return Hash(s.data(), s.size(), 0); } - static uint32_t Shard(uint32_t hash) { - return hash >> (32 - kNumShardBits); - } + static uint32_t Shard(uint32_t hash) { return hash >> (32 - kNumShardBits); } public: - explicit ShardedLRUCache(size_t capacity) - : last_id_(0) { + explicit ShardedLRUCache(size_t capacity) : last_id_(0) { const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards; for (int s = 0; s < kNumShards; s++) { shard_[s].SetCapacity(per_shard); } } - virtual ~ShardedLRUCache() { } - virtual Handle* Insert(const Slice& key, void* value, size_t charge, - void (*deleter)(const Slice& key, void* value)) { + ~ShardedLRUCache() override {} + Handle* Insert(const Slice& key, void* value, size_t charge, + void (*deleter)(const Slice& key, void* value)) override { const uint32_t hash = HashSlice(key); return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter); } - virtual Handle* Lookup(const Slice& key) { + Handle* Lookup(const Slice& key) override { const uint32_t hash = HashSlice(key); return shard_[Shard(hash)].Lookup(key, hash); } - virtual void Release(Handle* handle) { + void Release(Handle* handle) override { LRUHandle* h = reinterpret_cast(handle); shard_[Shard(h->hash)].Release(handle); } - virtual void Erase(const Slice& key) { + void Erase(const Slice& key) override { const uint32_t hash = HashSlice(key); shard_[Shard(hash)].Erase(key, hash); } - virtual void* Value(Handle* handle) { + void* Value(Handle* handle) override { return reinterpret_cast(handle)->value; } - virtual uint64_t NewId() { + uint64_t NewId() override { MutexLock l(&id_mutex_); return ++(last_id_); } + void Prune() override { + for (int s = 0; s < kNumShards; s++) { + shard_[s].Prune(); + } + } + size_t TotalCharge() const override { + size_t total = 0; + for (int s = 0; s < kNumShards; s++) { + total += shard_[s].TotalCharge(); + } + return total; + } }; } // end anonymous namespace -Cache* NewLRUCache(size_t capacity) { - return new ShardedLRUCache(capacity); -} +Cache* NewLRUCache(size_t capacity) { return new ShardedLRUCache(capacity); } } // namespace leveldb diff --git a/src/leveldb/util/cache_test.cc b/src/leveldb/util/cache_test.cc index 43716715a..974334b9f 100644 --- a/src/leveldb/util/cache_test.cc +++ b/src/leveldb/util/cache_test.cc @@ -25,8 +25,6 @@ static int DecodeValue(void* v) { return reinterpret_cast(v); } class CacheTest { public: - static CacheTest* current_; - static void Deleter(const Slice& key, void* v) { current_->deleted_keys_.push_back(DecodeKey(key)); current_->deleted_values_.push_back(DecodeValue(v)); @@ -37,18 +35,14 @@ class CacheTest { std::vector deleted_values_; Cache* cache_; - CacheTest() : cache_(NewLRUCache(kCacheSize)) { - current_ = this; - } + CacheTest() : cache_(NewLRUCache(kCacheSize)) { current_ = this; } - ~CacheTest() { - delete cache_; - } + ~CacheTest() { delete cache_; } int Lookup(int key) { Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); - const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle)); - if (handle != NULL) { + const int r = (handle == nullptr) ? -1 : DecodeValue(cache_->Value(handle)); + if (handle != nullptr) { cache_->Release(handle); } return r; @@ -59,9 +53,14 @@ class CacheTest { &CacheTest::Deleter)); } - void Erase(int key) { - cache_->Erase(EncodeKey(key)); + Cache::Handle* InsertAndReturnHandle(int key, int value, int charge = 1) { + return cache_->Insert(EncodeKey(key), EncodeValue(value), charge, + &CacheTest::Deleter); } + + void Erase(int key) { cache_->Erase(EncodeKey(key)); } + + static CacheTest* current_; }; CacheTest* CacheTest::current_; @@ -70,18 +69,18 @@ TEST(CacheTest, HitAndMiss) { Insert(100, 101); ASSERT_EQ(101, Lookup(100)); - ASSERT_EQ(-1, Lookup(200)); - ASSERT_EQ(-1, Lookup(300)); + ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(-1, Lookup(300)); Insert(200, 201); ASSERT_EQ(101, Lookup(100)); ASSERT_EQ(201, Lookup(200)); - ASSERT_EQ(-1, Lookup(300)); + ASSERT_EQ(-1, Lookup(300)); Insert(100, 102); ASSERT_EQ(102, Lookup(100)); ASSERT_EQ(201, Lookup(200)); - ASSERT_EQ(-1, Lookup(300)); + ASSERT_EQ(-1, Lookup(300)); ASSERT_EQ(1, deleted_keys_.size()); ASSERT_EQ(100, deleted_keys_[0]); @@ -95,14 +94,14 @@ TEST(CacheTest, Erase) { Insert(100, 101); Insert(200, 201); Erase(100); - ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(-1, Lookup(100)); ASSERT_EQ(201, Lookup(200)); ASSERT_EQ(1, deleted_keys_.size()); ASSERT_EQ(100, deleted_keys_[0]); ASSERT_EQ(101, deleted_values_[0]); Erase(100); - ASSERT_EQ(-1, Lookup(100)); + ASSERT_EQ(-1, Lookup(100)); ASSERT_EQ(201, Lookup(200)); ASSERT_EQ(1, deleted_keys_.size()); } @@ -135,15 +134,37 @@ TEST(CacheTest, EntriesArePinned) { TEST(CacheTest, EvictionPolicy) { Insert(100, 101); Insert(200, 201); + Insert(300, 301); + Cache::Handle* h = cache_->Lookup(EncodeKey(300)); - // Frequently used entry must be kept around + // Frequently used entry must be kept around, + // as must things that are still in use. for (int i = 0; i < kCacheSize + 100; i++) { - Insert(1000+i, 2000+i); - ASSERT_EQ(2000+i, Lookup(1000+i)); + Insert(1000 + i, 2000 + i); + ASSERT_EQ(2000 + i, Lookup(1000 + i)); ASSERT_EQ(101, Lookup(100)); } ASSERT_EQ(101, Lookup(100)); ASSERT_EQ(-1, Lookup(200)); + ASSERT_EQ(301, Lookup(300)); + cache_->Release(h); +} + +TEST(CacheTest, UseExceedsCacheSize) { + // Overfill the cache, keeping handles on all inserted entries. + std::vector h; + for (int i = 0; i < kCacheSize + 100; i++) { + h.push_back(InsertAndReturnHandle(1000 + i, 2000 + i)); + } + + // Check that all the entries can be found in the cache. + for (int i = 0; i < h.size(); i++) { + ASSERT_EQ(2000 + i, Lookup(1000 + i)); + } + + for (int i = 0; i < h.size(); i++) { + cache_->Release(h[i]); + } } TEST(CacheTest, HeavyEntries) { @@ -154,9 +175,9 @@ TEST(CacheTest, HeavyEntries) { const int kHeavy = 10; int added = 0; int index = 0; - while (added < 2*kCacheSize) { + while (added < 2 * kCacheSize) { const int weight = (index & 1) ? kLight : kHeavy; - Insert(index, 1000+index, weight); + Insert(index, 1000 + index, weight); added += weight; index++; } @@ -167,10 +188,10 @@ TEST(CacheTest, HeavyEntries) { int r = Lookup(i); if (r >= 0) { cached_weight += weight; - ASSERT_EQ(1000+i, r); + ASSERT_EQ(1000 + i, r); } } - ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10); + ASSERT_LE(cached_weight, kCacheSize + kCacheSize / 10); } TEST(CacheTest, NewId) { @@ -179,8 +200,27 @@ TEST(CacheTest, NewId) { ASSERT_NE(a, b); } -} // namespace leveldb +TEST(CacheTest, Prune) { + Insert(1, 100); + Insert(2, 200); + + Cache::Handle* handle = cache_->Lookup(EncodeKey(1)); + ASSERT_TRUE(handle); + cache_->Prune(); + cache_->Release(handle); -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); + ASSERT_EQ(100, Lookup(1)); + ASSERT_EQ(-1, Lookup(2)); } + +TEST(CacheTest, ZeroSizeCache) { + delete cache_; + cache_ = NewLRUCache(0); + + Insert(1, 100); + ASSERT_EQ(-1, Lookup(1)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/coding.cc b/src/leveldb/util/coding.cc index 21e3186d5..df3fa10f0 100644 --- a/src/leveldb/util/coding.cc +++ b/src/leveldb/util/coding.cc @@ -6,32 +6,6 @@ namespace leveldb { -void EncodeFixed32(char* buf, uint32_t value) { - if (port::kLittleEndian) { - memcpy(buf, &value, sizeof(value)); - } else { - buf[0] = value & 0xff; - buf[1] = (value >> 8) & 0xff; - buf[2] = (value >> 16) & 0xff; - buf[3] = (value >> 24) & 0xff; - } -} - -void EncodeFixed64(char* buf, uint64_t value) { - if (port::kLittleEndian) { - memcpy(buf, &value, sizeof(value)); - } else { - buf[0] = value & 0xff; - buf[1] = (value >> 8) & 0xff; - buf[2] = (value >> 16) & 0xff; - buf[3] = (value >> 24) & 0xff; - buf[4] = (value >> 32) & 0xff; - buf[5] = (value >> 40) & 0xff; - buf[6] = (value >> 48) & 0xff; - buf[7] = (value >> 56) & 0xff; - } -} - void PutFixed32(std::string* dst, uint32_t value) { char buf[sizeof(value)]; EncodeFixed32(buf, value); @@ -46,28 +20,28 @@ void PutFixed64(std::string* dst, uint64_t value) { char* EncodeVarint32(char* dst, uint32_t v) { // Operate on characters as unsigneds - unsigned char* ptr = reinterpret_cast(dst); + uint8_t* ptr = reinterpret_cast(dst); static const int B = 128; - if (v < (1<<7)) { + if (v < (1 << 7)) { *(ptr++) = v; - } else if (v < (1<<14)) { + } else if (v < (1 << 14)) { *(ptr++) = v | B; - *(ptr++) = v>>7; - } else if (v < (1<<21)) { + *(ptr++) = v >> 7; + } else if (v < (1 << 21)) { *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = v>>14; - } else if (v < (1<<28)) { + *(ptr++) = (v >> 7) | B; + *(ptr++) = v >> 14; + } else if (v < (1 << 28)) { *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = (v>>14) | B; - *(ptr++) = v>>21; + *(ptr++) = (v >> 7) | B; + *(ptr++) = (v >> 14) | B; + *(ptr++) = v >> 21; } else { *(ptr++) = v | B; - *(ptr++) = (v>>7) | B; - *(ptr++) = (v>>14) | B; - *(ptr++) = (v>>21) | B; - *(ptr++) = v>>28; + *(ptr++) = (v >> 7) | B; + *(ptr++) = (v >> 14) | B; + *(ptr++) = (v >> 21) | B; + *(ptr++) = v >> 28; } return reinterpret_cast(ptr); } @@ -80,12 +54,12 @@ void PutVarint32(std::string* dst, uint32_t v) { char* EncodeVarint64(char* dst, uint64_t v) { static const int B = 128; - unsigned char* ptr = reinterpret_cast(dst); + uint8_t* ptr = reinterpret_cast(dst); while (v >= B) { - *(ptr++) = (v & (B-1)) | B; + *(ptr++) = v | B; v >>= 7; } - *(ptr++) = static_cast(v); + *(ptr++) = static_cast(v); return reinterpret_cast(ptr); } @@ -109,12 +83,11 @@ int VarintLength(uint64_t v) { return len; } -const char* GetVarint32PtrFallback(const char* p, - const char* limit, +const char* GetVarint32PtrFallback(const char* p, const char* limit, uint32_t* value) { uint32_t result = 0; for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) { - uint32_t byte = *(reinterpret_cast(p)); + uint32_t byte = *(reinterpret_cast(p)); p++; if (byte & 128) { // More bytes are present @@ -125,14 +98,14 @@ const char* GetVarint32PtrFallback(const char* p, return reinterpret_cast(p); } } - return NULL; + return nullptr; } bool GetVarint32(Slice* input, uint32_t* value) { const char* p = input->data(); const char* limit = p + input->size(); const char* q = GetVarint32Ptr(p, limit, value); - if (q == NULL) { + if (q == nullptr) { return false; } else { *input = Slice(q, limit - q); @@ -143,7 +116,7 @@ bool GetVarint32(Slice* input, uint32_t* value) { const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) { uint64_t result = 0; for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) { - uint64_t byte = *(reinterpret_cast(p)); + uint64_t byte = *(reinterpret_cast(p)); p++; if (byte & 128) { // More bytes are present @@ -154,14 +127,14 @@ const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) { return reinterpret_cast(p); } } - return NULL; + return nullptr; } bool GetVarint64(Slice* input, uint64_t* value) { const char* p = input->data(); const char* limit = p + input->size(); const char* q = GetVarint64Ptr(p, limit, value); - if (q == NULL) { + if (q == nullptr) { return false; } else { *input = Slice(q, limit - q); @@ -173,16 +146,15 @@ const char* GetLengthPrefixedSlice(const char* p, const char* limit, Slice* result) { uint32_t len; p = GetVarint32Ptr(p, limit, &len); - if (p == NULL) return NULL; - if (p + len > limit) return NULL; + if (p == nullptr) return nullptr; + if (p + len > limit) return nullptr; *result = Slice(p, len); return p + len; } bool GetLengthPrefixedSlice(Slice* input, Slice* result) { uint32_t len; - if (GetVarint32(input, &len) && - input->size() >= len) { + if (GetVarint32(input, &len) && input->size() >= len) { *result = Slice(input->data(), len); input->remove_prefix(len); return true; diff --git a/src/leveldb/util/coding.h b/src/leveldb/util/coding.h index 3993c4a75..1983ae717 100644 --- a/src/leveldb/util/coding.h +++ b/src/leveldb/util/coding.h @@ -10,87 +10,147 @@ #ifndef STORAGE_LEVELDB_UTIL_CODING_H_ #define STORAGE_LEVELDB_UTIL_CODING_H_ -#include -#include +#include +#include #include + #include "leveldb/slice.h" #include "port/port.h" namespace leveldb { // Standard Put... routines append to a string -extern void PutFixed32(std::string* dst, uint32_t value); -extern void PutFixed64(std::string* dst, uint64_t value); -extern void PutVarint32(std::string* dst, uint32_t value); -extern void PutVarint64(std::string* dst, uint64_t value); -extern void PutLengthPrefixedSlice(std::string* dst, const Slice& value); +void PutFixed32(std::string* dst, uint32_t value); +void PutFixed64(std::string* dst, uint64_t value); +void PutVarint32(std::string* dst, uint32_t value); +void PutVarint64(std::string* dst, uint64_t value); +void PutLengthPrefixedSlice(std::string* dst, const Slice& value); // Standard Get... routines parse a value from the beginning of a Slice // and advance the slice past the parsed value. -extern bool GetVarint32(Slice* input, uint32_t* value); -extern bool GetVarint64(Slice* input, uint64_t* value); -extern bool GetLengthPrefixedSlice(Slice* input, Slice* result); +bool GetVarint32(Slice* input, uint32_t* value); +bool GetVarint64(Slice* input, uint64_t* value); +bool GetLengthPrefixedSlice(Slice* input, Slice* result); // Pointer-based variants of GetVarint... These either store a value // in *v and return a pointer just past the parsed value, or return -// NULL on error. These routines only look at bytes in the range +// nullptr on error. These routines only look at bytes in the range // [p..limit-1] -extern const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v); -extern const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v); +const char* GetVarint32Ptr(const char* p, const char* limit, uint32_t* v); +const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* v); // Returns the length of the varint32 or varint64 encoding of "v" -extern int VarintLength(uint64_t v); +int VarintLength(uint64_t v); // Lower-level versions of Put... that write directly into a character buffer +// and return a pointer just past the last byte written. // REQUIRES: dst has enough space for the value being written -extern void EncodeFixed32(char* dst, uint32_t value); -extern void EncodeFixed64(char* dst, uint64_t value); +char* EncodeVarint32(char* dst, uint32_t value); +char* EncodeVarint64(char* dst, uint64_t value); + +// TODO(costan): Remove port::kLittleEndian and the fast paths based on +// std::memcpy when clang learns to optimize the generic code, as +// described in https://bugs.llvm.org/show_bug.cgi?id=41761 +// +// The platform-independent code in DecodeFixed{32,64}() gets optimized to mov +// on x86 and ldr on ARM64, by both clang and gcc. However, only gcc optimizes +// the platform-independent code in EncodeFixed{32,64}() to mov / str. // Lower-level versions of Put... that write directly into a character buffer -// and return a pointer just past the last byte written. // REQUIRES: dst has enough space for the value being written -extern char* EncodeVarint32(char* dst, uint32_t value); -extern char* EncodeVarint64(char* dst, uint64_t value); + +inline void EncodeFixed32(char* dst, uint32_t value) { + uint8_t* const buffer = reinterpret_cast(dst); + + if (port::kLittleEndian) { + // Fast path for little-endian CPUs. All major compilers optimize this to a + // single mov (x86_64) / str (ARM) instruction. + std::memcpy(buffer, &value, sizeof(uint32_t)); + return; + } + + // Platform-independent code. + // Currently, only gcc optimizes this to a single mov / str instruction. + buffer[0] = static_cast(value); + buffer[1] = static_cast(value >> 8); + buffer[2] = static_cast(value >> 16); + buffer[3] = static_cast(value >> 24); +} + +inline void EncodeFixed64(char* dst, uint64_t value) { + uint8_t* const buffer = reinterpret_cast(dst); + + if (port::kLittleEndian) { + // Fast path for little-endian CPUs. All major compilers optimize this to a + // single mov (x86_64) / str (ARM) instruction. + std::memcpy(buffer, &value, sizeof(uint64_t)); + return; + } + + // Platform-independent code. + // Currently, only gcc optimizes this to a single mov / str instruction. + buffer[0] = static_cast(value); + buffer[1] = static_cast(value >> 8); + buffer[2] = static_cast(value >> 16); + buffer[3] = static_cast(value >> 24); + buffer[4] = static_cast(value >> 32); + buffer[5] = static_cast(value >> 40); + buffer[6] = static_cast(value >> 48); + buffer[7] = static_cast(value >> 56); +} // Lower-level versions of Get... that read directly from a character buffer // without any bounds checking. inline uint32_t DecodeFixed32(const char* ptr) { + const uint8_t* const buffer = reinterpret_cast(ptr); + if (port::kLittleEndian) { - // Load the raw bytes + // Fast path for little-endian CPUs. All major compilers optimize this to a + // single mov (x86_64) / ldr (ARM) instruction. uint32_t result; - memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + std::memcpy(&result, buffer, sizeof(uint32_t)); return result; - } else { - return ((static_cast(static_cast(ptr[0]))) - | (static_cast(static_cast(ptr[1])) << 8) - | (static_cast(static_cast(ptr[2])) << 16) - | (static_cast(static_cast(ptr[3])) << 24)); } + + // Platform-independent code. + // Clang and gcc optimize this to a single mov / ldr instruction. + return (static_cast(buffer[0])) | + (static_cast(buffer[1]) << 8) | + (static_cast(buffer[2]) << 16) | + (static_cast(buffer[3]) << 24); } inline uint64_t DecodeFixed64(const char* ptr) { + const uint8_t* const buffer = reinterpret_cast(ptr); + if (port::kLittleEndian) { - // Load the raw bytes + // Fast path for little-endian CPUs. All major compilers optimize this to a + // single mov (x86_64) / ldr (ARM) instruction. uint64_t result; - memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load + std::memcpy(&result, buffer, sizeof(uint64_t)); return result; - } else { - uint64_t lo = DecodeFixed32(ptr); - uint64_t hi = DecodeFixed32(ptr + 4); - return (hi << 32) | lo; } + + // Platform-independent code. + // Clang and gcc optimize this to a single mov / ldr instruction. + return (static_cast(buffer[0])) | + (static_cast(buffer[1]) << 8) | + (static_cast(buffer[2]) << 16) | + (static_cast(buffer[3]) << 24) | + (static_cast(buffer[4]) << 32) | + (static_cast(buffer[5]) << 40) | + (static_cast(buffer[6]) << 48) | + (static_cast(buffer[7]) << 56); } // Internal routine for use by fallback path of GetVarint32Ptr -extern const char* GetVarint32PtrFallback(const char* p, - const char* limit, - uint32_t* value); -inline const char* GetVarint32Ptr(const char* p, - const char* limit, +const char* GetVarint32PtrFallback(const char* p, const char* limit, + uint32_t* value); +inline const char* GetVarint32Ptr(const char* p, const char* limit, uint32_t* value) { if (p < limit) { - uint32_t result = *(reinterpret_cast(p)); + uint32_t result = *(reinterpret_cast(p)); if ((result & 128) == 0) { *value = result; return p + 1; diff --git a/src/leveldb/util/coding_test.cc b/src/leveldb/util/coding_test.cc index 521541ea6..0d2a0c51f 100644 --- a/src/leveldb/util/coding_test.cc +++ b/src/leveldb/util/coding_test.cc @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include "util/coding.h" +#include +#include "util/coding.h" #include "util/testharness.h" namespace leveldb { -class Coding { }; +class Coding {}; TEST(Coding, Fixed32) { std::string s; @@ -38,15 +39,15 @@ TEST(Coding, Fixed64) { uint64_t v = static_cast(1) << power; uint64_t actual; actual = DecodeFixed64(p); - ASSERT_EQ(v-1, actual); + ASSERT_EQ(v - 1, actual); p += sizeof(uint64_t); actual = DecodeFixed64(p); - ASSERT_EQ(v+0, actual); + ASSERT_EQ(v + 0, actual); p += sizeof(uint64_t); actual = DecodeFixed64(p); - ASSERT_EQ(v+1, actual); + ASSERT_EQ(v + 1, actual); p += sizeof(uint64_t); } } @@ -88,7 +89,7 @@ TEST(Coding, Varint32) { uint32_t actual; const char* start = p; p = GetVarint32Ptr(p, limit, &actual); - ASSERT_TRUE(p != NULL); + ASSERT_TRUE(p != nullptr); ASSERT_EQ(expected, actual); ASSERT_EQ(VarintLength(actual), p - start); } @@ -107,8 +108,8 @@ TEST(Coding, Varint64) { // Test values near powers of two const uint64_t power = 1ull << k; values.push_back(power); - values.push_back(power-1); - values.push_back(power+1); + values.push_back(power - 1); + values.push_back(power + 1); } std::string s; @@ -123,19 +124,18 @@ TEST(Coding, Varint64) { uint64_t actual; const char* start = p; p = GetVarint64Ptr(p, limit, &actual); - ASSERT_TRUE(p != NULL); + ASSERT_TRUE(p != nullptr); ASSERT_EQ(values[i], actual); ASSERT_EQ(VarintLength(actual), p - start); } ASSERT_EQ(p, limit); - } TEST(Coding, Varint32Overflow) { uint32_t result; std::string input("\x81\x82\x83\x84\x85\x11"); - ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result) - == NULL); + ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), + &result) == nullptr); } TEST(Coding, Varint32Truncation) { @@ -144,17 +144,18 @@ TEST(Coding, Varint32Truncation) { PutVarint32(&s, large_value); uint32_t result; for (size_t len = 0; len < s.size() - 1; len++) { - ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL); + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == nullptr); } - ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != + nullptr); ASSERT_EQ(large_value, result); } TEST(Coding, Varint64Overflow) { uint64_t result; std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11"); - ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result) - == NULL); + ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), + &result) == nullptr); } TEST(Coding, Varint64Truncation) { @@ -163,9 +164,10 @@ TEST(Coding, Varint64Truncation) { PutVarint64(&s, large_value); uint64_t result; for (size_t len = 0; len < s.size() - 1; len++) { - ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL); + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == nullptr); } - ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL); + ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != + nullptr); ASSERT_EQ(large_value, result); } @@ -191,6 +193,4 @@ TEST(Coding, Strings) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/comparator.cc b/src/leveldb/util/comparator.cc index 4b7b5724e..c5766e946 100644 --- a/src/leveldb/util/comparator.cc +++ b/src/leveldb/util/comparator.cc @@ -2,33 +2,34 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include -#include #include "leveldb/comparator.h" + +#include +#include +#include +#include + #include "leveldb/slice.h" -#include "port/port.h" #include "util/logging.h" +#include "util/no_destructor.h" namespace leveldb { -Comparator::~Comparator() { } +Comparator::~Comparator() = default; namespace { class BytewiseComparatorImpl : public Comparator { public: - BytewiseComparatorImpl() { } + BytewiseComparatorImpl() = default; - virtual const char* Name() const { - return "leveldb.BytewiseComparator"; - } + const char* Name() const override { return "leveldb.BytewiseComparator"; } - virtual int Compare(const Slice& a, const Slice& b) const { + int Compare(const Slice& a, const Slice& b) const override { return a.compare(b); } - virtual void FindShortestSeparator( - std::string* start, - const Slice& limit) const { + void FindShortestSeparator(std::string* start, + const Slice& limit) const override { // Find length of common prefix size_t min_length = std::min(start->size(), limit.size()); size_t diff_index = 0; @@ -50,14 +51,14 @@ class BytewiseComparatorImpl : public Comparator { } } - virtual void FindShortSuccessor(std::string* key) const { + void FindShortSuccessor(std::string* key) const override { // Find first character that can be incremented size_t n = key->size(); for (size_t i = 0; i < n; i++) { const uint8_t byte = (*key)[i]; if (byte != static_cast(0xff)) { (*key)[i] = byte + 1; - key->resize(i+1); + key->resize(i + 1); return; } } @@ -66,16 +67,9 @@ class BytewiseComparatorImpl : public Comparator { }; } // namespace -static port::OnceType once = LEVELDB_ONCE_INIT; -static const Comparator* bytewise; - -static void InitModule() { - bytewise = new BytewiseComparatorImpl; -} - const Comparator* BytewiseComparator() { - port::InitOnce(&once, InitModule); - return bytewise; + static NoDestructor singleton; + return singleton.get(); } } // namespace leveldb diff --git a/src/leveldb/util/crc32c.cc b/src/leveldb/util/crc32c.cc index 6db9e7707..c2e61f7db 100644 --- a/src/leveldb/util/crc32c.cc +++ b/src/leveldb/util/crc32c.cc @@ -2,330 +2,378 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. // -// A portable implementation of crc32c, optimized to handle -// four bytes at a time. +// A portable implementation of crc32c. #include "util/crc32c.h" +#include #include + +#include "port/port.h" #include "util/coding.h" namespace leveldb { namespace crc32c { -static const uint32_t table0_[256] = { - 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, - 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, - 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, - 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, - 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, - 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, - 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, - 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, - 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, - 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, - 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, - 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, - 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, - 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, - 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, - 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, - 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, - 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, - 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, - 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, - 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, - 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, - 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, - 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, - 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, - 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, - 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, - 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, - 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, - 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, - 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, - 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, - 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, - 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, - 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, - 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, - 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, - 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, - 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, - 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, - 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, - 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, - 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, - 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, - 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, - 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, - 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, - 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, - 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, - 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, - 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, - 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, - 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, - 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, - 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, - 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, - 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, - 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, - 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, - 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, - 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, - 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, - 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, - 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 -}; -static const uint32_t table1_[256] = { - 0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899, - 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945, - 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21, - 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd, - 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918, - 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4, - 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0, - 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c, - 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b, - 0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47, - 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823, - 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff, - 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a, - 0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6, - 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2, - 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e, - 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d, - 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41, - 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25, - 0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9, - 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c, - 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0, - 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4, - 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78, - 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f, - 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43, - 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27, - 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb, - 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e, - 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2, - 0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6, - 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a, - 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260, - 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc, - 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8, - 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004, - 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1, - 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d, - 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059, - 0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185, - 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162, - 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be, - 0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da, - 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306, - 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3, - 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f, - 0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b, - 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287, - 0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464, - 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8, - 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc, - 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600, - 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5, - 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439, - 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d, - 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781, - 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766, - 0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba, - 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de, - 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502, - 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7, - 0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b, - 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f, - 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483 -}; -static const uint32_t table2_[256] = { - 0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073, - 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469, - 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6, - 0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac, - 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9, - 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3, - 0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c, - 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726, - 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67, - 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d, - 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2, - 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8, - 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed, - 0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7, - 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828, - 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32, - 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa, - 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0, - 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f, - 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75, - 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20, - 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a, - 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5, - 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff, - 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe, - 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4, - 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b, - 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161, - 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634, - 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e, - 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1, - 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb, - 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730, - 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a, - 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5, - 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def, - 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba, - 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0, - 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f, - 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065, - 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24, - 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e, - 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1, - 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb, - 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae, - 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4, - 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b, - 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71, - 0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9, - 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3, - 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c, - 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36, - 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63, - 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79, - 0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6, - 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc, - 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd, - 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7, - 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238, - 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622, - 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177, - 0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d, - 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2, - 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8 -}; -static const uint32_t table3_[256] = { - 0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939, - 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca, - 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf, - 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c, - 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804, - 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7, - 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2, - 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11, - 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2, - 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41, - 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54, - 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7, - 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f, - 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c, - 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69, - 0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a, - 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de, - 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d, - 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538, - 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb, - 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3, - 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610, - 0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405, - 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6, - 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255, - 0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6, - 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3, - 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040, - 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368, - 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b, - 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e, - 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d, - 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006, - 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5, - 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0, - 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213, - 0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b, - 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8, - 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd, - 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e, - 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d, - 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e, - 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b, - 0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698, - 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0, - 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443, - 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656, - 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5, - 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1, - 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12, - 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07, - 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4, - 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc, - 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f, - 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a, - 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9, - 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a, - 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99, - 0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c, - 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f, - 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57, - 0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4, - 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1, - 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842 -}; +namespace { + +const uint32_t kByteExtensionTable[256] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, + 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c, + 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, + 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512, + 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, + 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf, + 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, + 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f, + 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, + 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e, + 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, + 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4, + 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, + 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5, + 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, + 0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905, + 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, + 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8, + 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, + 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6, + 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, + 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351}; + +const uint32_t kStrideExtensionTable0[256] = { + 0x00000000, 0x30d23865, 0x61a470ca, 0x517648af, 0xc348e194, 0xf39ad9f1, + 0xa2ec915e, 0x923ea93b, 0x837db5d9, 0xb3af8dbc, 0xe2d9c513, 0xd20bfd76, + 0x4035544d, 0x70e76c28, 0x21912487, 0x11431ce2, 0x03171d43, 0x33c52526, + 0x62b36d89, 0x526155ec, 0xc05ffcd7, 0xf08dc4b2, 0xa1fb8c1d, 0x9129b478, + 0x806aa89a, 0xb0b890ff, 0xe1ced850, 0xd11ce035, 0x4322490e, 0x73f0716b, + 0x228639c4, 0x125401a1, 0x062e3a86, 0x36fc02e3, 0x678a4a4c, 0x57587229, + 0xc566db12, 0xf5b4e377, 0xa4c2abd8, 0x941093bd, 0x85538f5f, 0xb581b73a, + 0xe4f7ff95, 0xd425c7f0, 0x461b6ecb, 0x76c956ae, 0x27bf1e01, 0x176d2664, + 0x053927c5, 0x35eb1fa0, 0x649d570f, 0x544f6f6a, 0xc671c651, 0xf6a3fe34, + 0xa7d5b69b, 0x97078efe, 0x8644921c, 0xb696aa79, 0xe7e0e2d6, 0xd732dab3, + 0x450c7388, 0x75de4bed, 0x24a80342, 0x147a3b27, 0x0c5c750c, 0x3c8e4d69, + 0x6df805c6, 0x5d2a3da3, 0xcf149498, 0xffc6acfd, 0xaeb0e452, 0x9e62dc37, + 0x8f21c0d5, 0xbff3f8b0, 0xee85b01f, 0xde57887a, 0x4c692141, 0x7cbb1924, + 0x2dcd518b, 0x1d1f69ee, 0x0f4b684f, 0x3f99502a, 0x6eef1885, 0x5e3d20e0, + 0xcc0389db, 0xfcd1b1be, 0xada7f911, 0x9d75c174, 0x8c36dd96, 0xbce4e5f3, + 0xed92ad5c, 0xdd409539, 0x4f7e3c02, 0x7fac0467, 0x2eda4cc8, 0x1e0874ad, + 0x0a724f8a, 0x3aa077ef, 0x6bd63f40, 0x5b040725, 0xc93aae1e, 0xf9e8967b, + 0xa89eded4, 0x984ce6b1, 0x890ffa53, 0xb9ddc236, 0xe8ab8a99, 0xd879b2fc, + 0x4a471bc7, 0x7a9523a2, 0x2be36b0d, 0x1b315368, 0x096552c9, 0x39b76aac, + 0x68c12203, 0x58131a66, 0xca2db35d, 0xfaff8b38, 0xab89c397, 0x9b5bfbf2, + 0x8a18e710, 0xbacadf75, 0xebbc97da, 0xdb6eafbf, 0x49500684, 0x79823ee1, + 0x28f4764e, 0x18264e2b, 0x18b8ea18, 0x286ad27d, 0x791c9ad2, 0x49cea2b7, + 0xdbf00b8c, 0xeb2233e9, 0xba547b46, 0x8a864323, 0x9bc55fc1, 0xab1767a4, + 0xfa612f0b, 0xcab3176e, 0x588dbe55, 0x685f8630, 0x3929ce9f, 0x09fbf6fa, + 0x1baff75b, 0x2b7dcf3e, 0x7a0b8791, 0x4ad9bff4, 0xd8e716cf, 0xe8352eaa, + 0xb9436605, 0x89915e60, 0x98d24282, 0xa8007ae7, 0xf9763248, 0xc9a40a2d, + 0x5b9aa316, 0x6b489b73, 0x3a3ed3dc, 0x0aecebb9, 0x1e96d09e, 0x2e44e8fb, + 0x7f32a054, 0x4fe09831, 0xddde310a, 0xed0c096f, 0xbc7a41c0, 0x8ca879a5, + 0x9deb6547, 0xad395d22, 0xfc4f158d, 0xcc9d2de8, 0x5ea384d3, 0x6e71bcb6, + 0x3f07f419, 0x0fd5cc7c, 0x1d81cddd, 0x2d53f5b8, 0x7c25bd17, 0x4cf78572, + 0xdec92c49, 0xee1b142c, 0xbf6d5c83, 0x8fbf64e6, 0x9efc7804, 0xae2e4061, + 0xff5808ce, 0xcf8a30ab, 0x5db49990, 0x6d66a1f5, 0x3c10e95a, 0x0cc2d13f, + 0x14e49f14, 0x2436a771, 0x7540efde, 0x4592d7bb, 0xd7ac7e80, 0xe77e46e5, + 0xb6080e4a, 0x86da362f, 0x97992acd, 0xa74b12a8, 0xf63d5a07, 0xc6ef6262, + 0x54d1cb59, 0x6403f33c, 0x3575bb93, 0x05a783f6, 0x17f38257, 0x2721ba32, + 0x7657f29d, 0x4685caf8, 0xd4bb63c3, 0xe4695ba6, 0xb51f1309, 0x85cd2b6c, + 0x948e378e, 0xa45c0feb, 0xf52a4744, 0xc5f87f21, 0x57c6d61a, 0x6714ee7f, + 0x3662a6d0, 0x06b09eb5, 0x12caa592, 0x22189df7, 0x736ed558, 0x43bced3d, + 0xd1824406, 0xe1507c63, 0xb02634cc, 0x80f40ca9, 0x91b7104b, 0xa165282e, + 0xf0136081, 0xc0c158e4, 0x52fff1df, 0x622dc9ba, 0x335b8115, 0x0389b970, + 0x11ddb8d1, 0x210f80b4, 0x7079c81b, 0x40abf07e, 0xd2955945, 0xe2476120, + 0xb331298f, 0x83e311ea, 0x92a00d08, 0xa272356d, 0xf3047dc2, 0xc3d645a7, + 0x51e8ec9c, 0x613ad4f9, 0x304c9c56, 0x009ea433}; + +const uint32_t kStrideExtensionTable1[256] = { + 0x00000000, 0x54075546, 0xa80eaa8c, 0xfc09ffca, 0x55f123e9, 0x01f676af, + 0xfdff8965, 0xa9f8dc23, 0xabe247d2, 0xffe51294, 0x03eced5e, 0x57ebb818, + 0xfe13643b, 0xaa14317d, 0x561dceb7, 0x021a9bf1, 0x5228f955, 0x062fac13, + 0xfa2653d9, 0xae21069f, 0x07d9dabc, 0x53de8ffa, 0xafd77030, 0xfbd02576, + 0xf9cabe87, 0xadcdebc1, 0x51c4140b, 0x05c3414d, 0xac3b9d6e, 0xf83cc828, + 0x043537e2, 0x503262a4, 0xa451f2aa, 0xf056a7ec, 0x0c5f5826, 0x58580d60, + 0xf1a0d143, 0xa5a78405, 0x59ae7bcf, 0x0da92e89, 0x0fb3b578, 0x5bb4e03e, + 0xa7bd1ff4, 0xf3ba4ab2, 0x5a429691, 0x0e45c3d7, 0xf24c3c1d, 0xa64b695b, + 0xf6790bff, 0xa27e5eb9, 0x5e77a173, 0x0a70f435, 0xa3882816, 0xf78f7d50, + 0x0b86829a, 0x5f81d7dc, 0x5d9b4c2d, 0x099c196b, 0xf595e6a1, 0xa192b3e7, + 0x086a6fc4, 0x5c6d3a82, 0xa064c548, 0xf463900e, 0x4d4f93a5, 0x1948c6e3, + 0xe5413929, 0xb1466c6f, 0x18beb04c, 0x4cb9e50a, 0xb0b01ac0, 0xe4b74f86, + 0xe6add477, 0xb2aa8131, 0x4ea37efb, 0x1aa42bbd, 0xb35cf79e, 0xe75ba2d8, + 0x1b525d12, 0x4f550854, 0x1f676af0, 0x4b603fb6, 0xb769c07c, 0xe36e953a, + 0x4a964919, 0x1e911c5f, 0xe298e395, 0xb69fb6d3, 0xb4852d22, 0xe0827864, + 0x1c8b87ae, 0x488cd2e8, 0xe1740ecb, 0xb5735b8d, 0x497aa447, 0x1d7df101, + 0xe91e610f, 0xbd193449, 0x4110cb83, 0x15179ec5, 0xbcef42e6, 0xe8e817a0, + 0x14e1e86a, 0x40e6bd2c, 0x42fc26dd, 0x16fb739b, 0xeaf28c51, 0xbef5d917, + 0x170d0534, 0x430a5072, 0xbf03afb8, 0xeb04fafe, 0xbb36985a, 0xef31cd1c, + 0x133832d6, 0x473f6790, 0xeec7bbb3, 0xbac0eef5, 0x46c9113f, 0x12ce4479, + 0x10d4df88, 0x44d38ace, 0xb8da7504, 0xecdd2042, 0x4525fc61, 0x1122a927, + 0xed2b56ed, 0xb92c03ab, 0x9a9f274a, 0xce98720c, 0x32918dc6, 0x6696d880, + 0xcf6e04a3, 0x9b6951e5, 0x6760ae2f, 0x3367fb69, 0x317d6098, 0x657a35de, + 0x9973ca14, 0xcd749f52, 0x648c4371, 0x308b1637, 0xcc82e9fd, 0x9885bcbb, + 0xc8b7de1f, 0x9cb08b59, 0x60b97493, 0x34be21d5, 0x9d46fdf6, 0xc941a8b0, + 0x3548577a, 0x614f023c, 0x635599cd, 0x3752cc8b, 0xcb5b3341, 0x9f5c6607, + 0x36a4ba24, 0x62a3ef62, 0x9eaa10a8, 0xcaad45ee, 0x3eced5e0, 0x6ac980a6, + 0x96c07f6c, 0xc2c72a2a, 0x6b3ff609, 0x3f38a34f, 0xc3315c85, 0x973609c3, + 0x952c9232, 0xc12bc774, 0x3d2238be, 0x69256df8, 0xc0ddb1db, 0x94dae49d, + 0x68d31b57, 0x3cd44e11, 0x6ce62cb5, 0x38e179f3, 0xc4e88639, 0x90efd37f, + 0x39170f5c, 0x6d105a1a, 0x9119a5d0, 0xc51ef096, 0xc7046b67, 0x93033e21, + 0x6f0ac1eb, 0x3b0d94ad, 0x92f5488e, 0xc6f21dc8, 0x3afbe202, 0x6efcb744, + 0xd7d0b4ef, 0x83d7e1a9, 0x7fde1e63, 0x2bd94b25, 0x82219706, 0xd626c240, + 0x2a2f3d8a, 0x7e2868cc, 0x7c32f33d, 0x2835a67b, 0xd43c59b1, 0x803b0cf7, + 0x29c3d0d4, 0x7dc48592, 0x81cd7a58, 0xd5ca2f1e, 0x85f84dba, 0xd1ff18fc, + 0x2df6e736, 0x79f1b270, 0xd0096e53, 0x840e3b15, 0x7807c4df, 0x2c009199, + 0x2e1a0a68, 0x7a1d5f2e, 0x8614a0e4, 0xd213f5a2, 0x7beb2981, 0x2fec7cc7, + 0xd3e5830d, 0x87e2d64b, 0x73814645, 0x27861303, 0xdb8fecc9, 0x8f88b98f, + 0x267065ac, 0x727730ea, 0x8e7ecf20, 0xda799a66, 0xd8630197, 0x8c6454d1, + 0x706dab1b, 0x246afe5d, 0x8d92227e, 0xd9957738, 0x259c88f2, 0x719bddb4, + 0x21a9bf10, 0x75aeea56, 0x89a7159c, 0xdda040da, 0x74589cf9, 0x205fc9bf, + 0xdc563675, 0x88516333, 0x8a4bf8c2, 0xde4cad84, 0x2245524e, 0x76420708, + 0xdfbadb2b, 0x8bbd8e6d, 0x77b471a7, 0x23b324e1}; + +const uint32_t kStrideExtensionTable2[256] = { + 0x00000000, 0x678efd01, 0xcf1dfa02, 0xa8930703, 0x9bd782f5, 0xfc597ff4, + 0x54ca78f7, 0x334485f6, 0x3243731b, 0x55cd8e1a, 0xfd5e8919, 0x9ad07418, + 0xa994f1ee, 0xce1a0cef, 0x66890bec, 0x0107f6ed, 0x6486e636, 0x03081b37, + 0xab9b1c34, 0xcc15e135, 0xff5164c3, 0x98df99c2, 0x304c9ec1, 0x57c263c0, + 0x56c5952d, 0x314b682c, 0x99d86f2f, 0xfe56922e, 0xcd1217d8, 0xaa9cead9, + 0x020fedda, 0x658110db, 0xc90dcc6c, 0xae83316d, 0x0610366e, 0x619ecb6f, + 0x52da4e99, 0x3554b398, 0x9dc7b49b, 0xfa49499a, 0xfb4ebf77, 0x9cc04276, + 0x34534575, 0x53ddb874, 0x60993d82, 0x0717c083, 0xaf84c780, 0xc80a3a81, + 0xad8b2a5a, 0xca05d75b, 0x6296d058, 0x05182d59, 0x365ca8af, 0x51d255ae, + 0xf94152ad, 0x9ecfafac, 0x9fc85941, 0xf846a440, 0x50d5a343, 0x375b5e42, + 0x041fdbb4, 0x639126b5, 0xcb0221b6, 0xac8cdcb7, 0x97f7ee29, 0xf0791328, + 0x58ea142b, 0x3f64e92a, 0x0c206cdc, 0x6bae91dd, 0xc33d96de, 0xa4b36bdf, + 0xa5b49d32, 0xc23a6033, 0x6aa96730, 0x0d279a31, 0x3e631fc7, 0x59ede2c6, + 0xf17ee5c5, 0x96f018c4, 0xf371081f, 0x94fff51e, 0x3c6cf21d, 0x5be20f1c, + 0x68a68aea, 0x0f2877eb, 0xa7bb70e8, 0xc0358de9, 0xc1327b04, 0xa6bc8605, + 0x0e2f8106, 0x69a17c07, 0x5ae5f9f1, 0x3d6b04f0, 0x95f803f3, 0xf276fef2, + 0x5efa2245, 0x3974df44, 0x91e7d847, 0xf6692546, 0xc52da0b0, 0xa2a35db1, + 0x0a305ab2, 0x6dbea7b3, 0x6cb9515e, 0x0b37ac5f, 0xa3a4ab5c, 0xc42a565d, + 0xf76ed3ab, 0x90e02eaa, 0x387329a9, 0x5ffdd4a8, 0x3a7cc473, 0x5df23972, + 0xf5613e71, 0x92efc370, 0xa1ab4686, 0xc625bb87, 0x6eb6bc84, 0x09384185, + 0x083fb768, 0x6fb14a69, 0xc7224d6a, 0xa0acb06b, 0x93e8359d, 0xf466c89c, + 0x5cf5cf9f, 0x3b7b329e, 0x2a03aaa3, 0x4d8d57a2, 0xe51e50a1, 0x8290ada0, + 0xb1d42856, 0xd65ad557, 0x7ec9d254, 0x19472f55, 0x1840d9b8, 0x7fce24b9, + 0xd75d23ba, 0xb0d3debb, 0x83975b4d, 0xe419a64c, 0x4c8aa14f, 0x2b045c4e, + 0x4e854c95, 0x290bb194, 0x8198b697, 0xe6164b96, 0xd552ce60, 0xb2dc3361, + 0x1a4f3462, 0x7dc1c963, 0x7cc63f8e, 0x1b48c28f, 0xb3dbc58c, 0xd455388d, + 0xe711bd7b, 0x809f407a, 0x280c4779, 0x4f82ba78, 0xe30e66cf, 0x84809bce, + 0x2c139ccd, 0x4b9d61cc, 0x78d9e43a, 0x1f57193b, 0xb7c41e38, 0xd04ae339, + 0xd14d15d4, 0xb6c3e8d5, 0x1e50efd6, 0x79de12d7, 0x4a9a9721, 0x2d146a20, + 0x85876d23, 0xe2099022, 0x878880f9, 0xe0067df8, 0x48957afb, 0x2f1b87fa, + 0x1c5f020c, 0x7bd1ff0d, 0xd342f80e, 0xb4cc050f, 0xb5cbf3e2, 0xd2450ee3, + 0x7ad609e0, 0x1d58f4e1, 0x2e1c7117, 0x49928c16, 0xe1018b15, 0x868f7614, + 0xbdf4448a, 0xda7ab98b, 0x72e9be88, 0x15674389, 0x2623c67f, 0x41ad3b7e, + 0xe93e3c7d, 0x8eb0c17c, 0x8fb73791, 0xe839ca90, 0x40aacd93, 0x27243092, + 0x1460b564, 0x73ee4865, 0xdb7d4f66, 0xbcf3b267, 0xd972a2bc, 0xbefc5fbd, + 0x166f58be, 0x71e1a5bf, 0x42a52049, 0x252bdd48, 0x8db8da4b, 0xea36274a, + 0xeb31d1a7, 0x8cbf2ca6, 0x242c2ba5, 0x43a2d6a4, 0x70e65352, 0x1768ae53, + 0xbffba950, 0xd8755451, 0x74f988e6, 0x137775e7, 0xbbe472e4, 0xdc6a8fe5, + 0xef2e0a13, 0x88a0f712, 0x2033f011, 0x47bd0d10, 0x46bafbfd, 0x213406fc, + 0x89a701ff, 0xee29fcfe, 0xdd6d7908, 0xbae38409, 0x1270830a, 0x75fe7e0b, + 0x107f6ed0, 0x77f193d1, 0xdf6294d2, 0xb8ec69d3, 0x8ba8ec25, 0xec261124, + 0x44b51627, 0x233beb26, 0x223c1dcb, 0x45b2e0ca, 0xed21e7c9, 0x8aaf1ac8, + 0xb9eb9f3e, 0xde65623f, 0x76f6653c, 0x1178983d}; + +const uint32_t kStrideExtensionTable3[256] = { + 0x00000000, 0xf20c0dfe, 0xe1f46d0d, 0x13f860f3, 0xc604aceb, 0x3408a115, + 0x27f0c1e6, 0xd5fccc18, 0x89e52f27, 0x7be922d9, 0x6811422a, 0x9a1d4fd4, + 0x4fe183cc, 0xbded8e32, 0xae15eec1, 0x5c19e33f, 0x162628bf, 0xe42a2541, + 0xf7d245b2, 0x05de484c, 0xd0228454, 0x222e89aa, 0x31d6e959, 0xc3dae4a7, + 0x9fc30798, 0x6dcf0a66, 0x7e376a95, 0x8c3b676b, 0x59c7ab73, 0xabcba68d, + 0xb833c67e, 0x4a3fcb80, 0x2c4c517e, 0xde405c80, 0xcdb83c73, 0x3fb4318d, + 0xea48fd95, 0x1844f06b, 0x0bbc9098, 0xf9b09d66, 0xa5a97e59, 0x57a573a7, + 0x445d1354, 0xb6511eaa, 0x63add2b2, 0x91a1df4c, 0x8259bfbf, 0x7055b241, + 0x3a6a79c1, 0xc866743f, 0xdb9e14cc, 0x29921932, 0xfc6ed52a, 0x0e62d8d4, + 0x1d9ab827, 0xef96b5d9, 0xb38f56e6, 0x41835b18, 0x527b3beb, 0xa0773615, + 0x758bfa0d, 0x8787f7f3, 0x947f9700, 0x66739afe, 0x5898a2fc, 0xaa94af02, + 0xb96ccff1, 0x4b60c20f, 0x9e9c0e17, 0x6c9003e9, 0x7f68631a, 0x8d646ee4, + 0xd17d8ddb, 0x23718025, 0x3089e0d6, 0xc285ed28, 0x17792130, 0xe5752cce, + 0xf68d4c3d, 0x048141c3, 0x4ebe8a43, 0xbcb287bd, 0xaf4ae74e, 0x5d46eab0, + 0x88ba26a8, 0x7ab62b56, 0x694e4ba5, 0x9b42465b, 0xc75ba564, 0x3557a89a, + 0x26afc869, 0xd4a3c597, 0x015f098f, 0xf3530471, 0xe0ab6482, 0x12a7697c, + 0x74d4f382, 0x86d8fe7c, 0x95209e8f, 0x672c9371, 0xb2d05f69, 0x40dc5297, + 0x53243264, 0xa1283f9a, 0xfd31dca5, 0x0f3dd15b, 0x1cc5b1a8, 0xeec9bc56, + 0x3b35704e, 0xc9397db0, 0xdac11d43, 0x28cd10bd, 0x62f2db3d, 0x90fed6c3, + 0x8306b630, 0x710abbce, 0xa4f677d6, 0x56fa7a28, 0x45021adb, 0xb70e1725, + 0xeb17f41a, 0x191bf9e4, 0x0ae39917, 0xf8ef94e9, 0x2d1358f1, 0xdf1f550f, + 0xcce735fc, 0x3eeb3802, 0xb13145f8, 0x433d4806, 0x50c528f5, 0xa2c9250b, + 0x7735e913, 0x8539e4ed, 0x96c1841e, 0x64cd89e0, 0x38d46adf, 0xcad86721, + 0xd92007d2, 0x2b2c0a2c, 0xfed0c634, 0x0cdccbca, 0x1f24ab39, 0xed28a6c7, + 0xa7176d47, 0x551b60b9, 0x46e3004a, 0xb4ef0db4, 0x6113c1ac, 0x931fcc52, + 0x80e7aca1, 0x72eba15f, 0x2ef24260, 0xdcfe4f9e, 0xcf062f6d, 0x3d0a2293, + 0xe8f6ee8b, 0x1afae375, 0x09028386, 0xfb0e8e78, 0x9d7d1486, 0x6f711978, + 0x7c89798b, 0x8e857475, 0x5b79b86d, 0xa975b593, 0xba8dd560, 0x4881d89e, + 0x14983ba1, 0xe694365f, 0xf56c56ac, 0x07605b52, 0xd29c974a, 0x20909ab4, + 0x3368fa47, 0xc164f7b9, 0x8b5b3c39, 0x795731c7, 0x6aaf5134, 0x98a35cca, + 0x4d5f90d2, 0xbf539d2c, 0xacabfddf, 0x5ea7f021, 0x02be131e, 0xf0b21ee0, + 0xe34a7e13, 0x114673ed, 0xc4babff5, 0x36b6b20b, 0x254ed2f8, 0xd742df06, + 0xe9a9e704, 0x1ba5eafa, 0x085d8a09, 0xfa5187f7, 0x2fad4bef, 0xdda14611, + 0xce5926e2, 0x3c552b1c, 0x604cc823, 0x9240c5dd, 0x81b8a52e, 0x73b4a8d0, + 0xa64864c8, 0x54446936, 0x47bc09c5, 0xb5b0043b, 0xff8fcfbb, 0x0d83c245, + 0x1e7ba2b6, 0xec77af48, 0x398b6350, 0xcb876eae, 0xd87f0e5d, 0x2a7303a3, + 0x766ae09c, 0x8466ed62, 0x979e8d91, 0x6592806f, 0xb06e4c77, 0x42624189, + 0x519a217a, 0xa3962c84, 0xc5e5b67a, 0x37e9bb84, 0x2411db77, 0xd61dd689, + 0x03e11a91, 0xf1ed176f, 0xe215779c, 0x10197a62, 0x4c00995d, 0xbe0c94a3, + 0xadf4f450, 0x5ff8f9ae, 0x8a0435b6, 0x78083848, 0x6bf058bb, 0x99fc5545, + 0xd3c39ec5, 0x21cf933b, 0x3237f3c8, 0xc03bfe36, 0x15c7322e, 0xe7cb3fd0, + 0xf4335f23, 0x063f52dd, 0x5a26b1e2, 0xa82abc1c, 0xbbd2dcef, 0x49ded111, + 0x9c221d09, 0x6e2e10f7, 0x7dd67004, 0x8fda7dfa}; + +// CRCs are pre- and post- conditioned by xoring with all ones. +static constexpr const uint32_t kCRC32Xor = static_cast(0xffffffffU); + +// Reads a little-endian 32-bit integer from a 32-bit-aligned buffer. +inline uint32_t ReadUint32LE(const uint8_t* buffer) { + return DecodeFixed32(reinterpret_cast(buffer)); +} + +// Returns the smallest address >= the given address that is aligned to N bytes. +// +// N must be a power of two. +template +constexpr inline const uint8_t* RoundUp(const uint8_t* pointer) { + return reinterpret_cast( + (reinterpret_cast(pointer) + (N - 1)) & + ~static_cast(N - 1)); +} + +} // namespace + +// Determine if the CPU running this program can accelerate the CRC32C +// calculation. +static bool CanAccelerateCRC32C() { + // port::AcceleretedCRC32C returns zero when unable to accelerate. + static const char kTestCRCBuffer[] = "TestCRCBuffer"; + static const char kBufSize = sizeof(kTestCRCBuffer) - 1; + static const uint32_t kTestCRCValue = 0xdcbc59fa; -// Used to fetch a naturally-aligned 32-bit word in little endian byte-order -static inline uint32_t LE_LOAD32(const uint8_t *p) { - return DecodeFixed32(reinterpret_cast(p)); + return port::AcceleratedCRC32C(0, kTestCRCBuffer, kBufSize) == kTestCRCValue; } -uint32_t Extend(uint32_t crc, const char* buf, size_t size) { - const uint8_t *p = reinterpret_cast(buf); - const uint8_t *e = p + size; - uint32_t l = crc ^ 0xffffffffu; +uint32_t Extend(uint32_t crc, const char* data, size_t n) { + static bool accelerate = CanAccelerateCRC32C(); + if (accelerate) { + return port::AcceleratedCRC32C(crc, data, n); + } + + const uint8_t* p = reinterpret_cast(data); + const uint8_t* e = p + n; + uint32_t l = crc ^ kCRC32Xor; + +// Process one byte at a time. +#define STEP1 \ + do { \ + int c = (l & 0xff) ^ *p++; \ + l = kByteExtensionTable[c] ^ (l >> 8); \ + } while (0) + +// Process one of the 4 strides of 4-byte data. +#define STEP4(s) \ + do { \ + crc##s = ReadUint32LE(p + s * 4) ^ kStrideExtensionTable3[crc##s & 0xff] ^ \ + kStrideExtensionTable2[(crc##s >> 8) & 0xff] ^ \ + kStrideExtensionTable1[(crc##s >> 16) & 0xff] ^ \ + kStrideExtensionTable0[crc##s >> 24]; \ + } while (0) -#define STEP1 do { \ - int c = (l & 0xff) ^ *p++; \ - l = table0_[c] ^ (l >> 8); \ -} while (0) -#define STEP4 do { \ - uint32_t c = l ^ LE_LOAD32(p); \ - p += 4; \ - l = table3_[c & 0xff] ^ \ - table2_[(c >> 8) & 0xff] ^ \ - table1_[(c >> 16) & 0xff] ^ \ - table0_[c >> 24]; \ -} while (0) +// Process a 16-byte swath of 4 strides, each of which has 4 bytes of data. +#define STEP16 \ + do { \ + STEP4(0); \ + STEP4(1); \ + STEP4(2); \ + STEP4(3); \ + p += 16; \ + } while (0) - // Point x at first 4-byte aligned byte in string. This might be - // just past the end of the string. - const uintptr_t pval = reinterpret_cast(p); - const uint8_t* x = reinterpret_cast(((pval + 3) >> 2) << 2); +// Process 4 bytes that were already loaded into a word. +#define STEP4W(w) \ + do { \ + w ^= l; \ + for (size_t i = 0; i < 4; ++i) { \ + w = (w >> 8) ^ kByteExtensionTable[w & 0xff]; \ + } \ + l = w; \ + } while (0) + + // Point x at first 4-byte aligned byte in the buffer. This might be past the + // end of the buffer. + const uint8_t* x = RoundUp<4>(p); if (x <= e) { - // Process bytes until finished or p is 4-byte aligned + // Process bytes p is 4-byte aligned. while (p != x) { STEP1; } } - // Process bytes 16 at a time - while ((e-p) >= 16) { - STEP4; STEP4; STEP4; STEP4; - } - // Process bytes 4 at a time - while ((e-p) >= 4) { - STEP4; + + if ((e - p) >= 16) { + // Load a 16-byte swath into the stride partial results. + uint32_t crc0 = ReadUint32LE(p + 0 * 4) ^ l; + uint32_t crc1 = ReadUint32LE(p + 1 * 4); + uint32_t crc2 = ReadUint32LE(p + 2 * 4); + uint32_t crc3 = ReadUint32LE(p + 3 * 4); + p += 16; + + // It is possible to get better speeds (at least on x86) by interleaving + // prefetching 256 bytes ahead with processing 64 bytes at a time. See the + // portable implementation in https://github.com/google/crc32c/. + + // Process one 16-byte swath at a time. + while ((e - p) >= 16) { + STEP16; + } + + // Advance one word at a time as far as possible. + while ((e - p) >= 4) { + STEP4(0); + uint32_t tmp = crc0; + crc0 = crc1; + crc1 = crc2; + crc2 = crc3; + crc3 = tmp; + p += 4; + } + + // Combine the 4 partial stride results. + l = 0; + STEP4W(crc0); + STEP4W(crc1); + STEP4W(crc2); + STEP4W(crc3); } - // Process the last few bytes + + // Process the last few bytes. while (p != e) { STEP1; } +#undef STEP4W +#undef STEP16 #undef STEP4 #undef STEP1 - return l ^ 0xffffffffu; + return l ^ kCRC32Xor; } } // namespace crc32c diff --git a/src/leveldb/util/crc32c.h b/src/leveldb/util/crc32c.h index 1d7e5c075..98fabb0d2 100644 --- a/src/leveldb/util/crc32c.h +++ b/src/leveldb/util/crc32c.h @@ -14,12 +14,10 @@ namespace crc32c { // Return the crc32c of concat(A, data[0,n-1]) where init_crc is the // crc32c of some string A. Extend() is often used to maintain the // crc32c of a stream of data. -extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n); +uint32_t Extend(uint32_t init_crc, const char* data, size_t n); // Return the crc32c of data[0,n-1] -inline uint32_t Value(const char* data, size_t n) { - return Extend(0, data, n); -} +inline uint32_t Value(const char* data, size_t n) { return Extend(0, data, n); } static const uint32_t kMaskDelta = 0xa282ead8ul; diff --git a/src/leveldb/util/crc32c_test.cc b/src/leveldb/util/crc32c_test.cc index 4b957ee12..18a849482 100644 --- a/src/leveldb/util/crc32c_test.cc +++ b/src/leveldb/util/crc32c_test.cc @@ -8,7 +8,7 @@ namespace leveldb { namespace crc32c { -class CRC { }; +class CRC {}; TEST(CRC, StandardResults) { // From rfc3720 section B.4. @@ -30,30 +30,19 @@ TEST(CRC, StandardResults) { } ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf))); - unsigned char data[48] = { - 0x01, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x18, - 0x28, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + uint8_t data[48] = { + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; ASSERT_EQ(0xd9963a56, Value(reinterpret_cast(data), sizeof(data))); } -TEST(CRC, Values) { - ASSERT_NE(Value("a", 1), Value("foo", 3)); -} +TEST(CRC, Values) { ASSERT_NE(Value("a", 1), Value("foo", 3)); } TEST(CRC, Extend) { - ASSERT_EQ(Value("hello world", 11), - Extend(Value("hello ", 6), "world", 5)); + ASSERT_EQ(Value("hello world", 11), Extend(Value("hello ", 6), "world", 5)); } TEST(CRC, Mask) { @@ -67,6 +56,4 @@ TEST(CRC, Mask) { } // namespace crc32c } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/env.cc b/src/leveldb/util/env.cc index c2600e964..d2f0aef32 100644 --- a/src/leveldb/util/env.cc +++ b/src/leveldb/util/env.cc @@ -6,26 +6,24 @@ namespace leveldb { -Env::~Env() { -} +Env::~Env() = default; -SequentialFile::~SequentialFile() { +Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) { + return Status::NotSupported("NewAppendableFile", fname); } -RandomAccessFile::~RandomAccessFile() { -} +SequentialFile::~SequentialFile() = default; -WritableFile::~WritableFile() { -} +RandomAccessFile::~RandomAccessFile() = default; -Logger::~Logger() { -} +WritableFile::~WritableFile() = default; -FileLock::~FileLock() { -} +Logger::~Logger() = default; + +FileLock::~FileLock() = default; void Log(Logger* info_log, const char* format, ...) { - if (info_log != NULL) { + if (info_log != nullptr) { va_list ap; va_start(ap, format); info_log->Logv(format, ap); @@ -34,8 +32,7 @@ void Log(Logger* info_log, const char* format, ...) { } static Status DoWriteStringToFile(Env* env, const Slice& data, - const std::string& fname, - bool should_sync) { + const std::string& fname, bool should_sync) { WritableFile* file; Status s = env->NewWritableFile(fname, &file); if (!s.ok()) { @@ -90,7 +87,6 @@ Status ReadFileToString(Env* env, const std::string& fname, std::string* data) { return s; } -EnvWrapper::~EnvWrapper() { -} +EnvWrapper::~EnvWrapper() {} } // namespace leveldb diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index ba2667864..fac41be6c 100644 --- a/src/leveldb/util/env_posix.cc +++ b/src/leveldb/util/env_posix.cc @@ -1,608 +1,906 @@ // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#if !defined(LEVELDB_PLATFORM_WINDOWS) #include -#include #include #include -#include -#include -#include #include +#include #include #include #include -#include #include -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include + #include "leveldb/env.h" #include "leveldb/slice.h" +#include "leveldb/status.h" #include "port/port.h" -#include "util/logging.h" -#include "util/mutexlock.h" +#include "port/thread_annotations.h" +#include "util/env_posix_test_helper.h" #include "util/posix_logger.h" namespace leveldb { namespace { -static Status IOError(const std::string& context, int err_number) { - return Status::IOError(context, strerror(err_number)); +// Set by EnvPosixTestHelper::SetReadOnlyMMapLimit() and MaxOpenFiles(). +int g_open_read_only_file_limit = -1; + +// Up to 4096 mmap regions for 64-bit binaries; none for 32-bit. +constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 4096 : 0; + +// Can be set using EnvPosixTestHelper::SetReadOnlyMMapLimit(). +int g_mmap_limit = kDefaultMmapLimit; + +// Common flags defined for all posix open operations +#if HAVE_O_CLOEXEC +constexpr const int kOpenBaseFlags = O_CLOEXEC; +#else +constexpr const int kOpenBaseFlags = 0; +#endif // defined(HAVE_O_CLOEXEC) + +constexpr const size_t kWritableFileBufferSize = 65536; + +Status PosixError(const std::string& context, int error_number) { + if (error_number == ENOENT) { + return Status::NotFound(context, std::strerror(error_number)); + } else { + return Status::IOError(context, std::strerror(error_number)); + } } -class PosixSequentialFile: public SequentialFile { +// Helper class to limit resource usage to avoid exhaustion. +// Currently used to limit read-only file descriptors and mmap file usage +// so that we do not run out of file descriptors or virtual memory, or run into +// kernel performance problems for very large databases. +class Limiter { + public: + // Limit maximum number of resources to |max_acquires|. + Limiter(int max_acquires) : acquires_allowed_(max_acquires) {} + + Limiter(const Limiter&) = delete; + Limiter operator=(const Limiter&) = delete; + + // If another resource is available, acquire it and return true. + // Else return false. + bool Acquire() { + int old_acquires_allowed = + acquires_allowed_.fetch_sub(1, std::memory_order_relaxed); + + if (old_acquires_allowed > 0) return true; + + acquires_allowed_.fetch_add(1, std::memory_order_relaxed); + return false; + } + + // Release a resource acquired by a previous call to Acquire() that returned + // true. + void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); } + private: - std::string filename_; - FILE* file_; + // The number of available resources. + // + // This is a counter and is not tied to the invariants of any other class, so + // it can be operated on safely using std::memory_order_relaxed. + std::atomic acquires_allowed_; +}; +// Implements sequential read access in a file using read(). +// +// Instances of this class are thread-friendly but not thread-safe, as required +// by the SequentialFile API. +class PosixSequentialFile final : public SequentialFile { public: - PosixSequentialFile(const std::string& fname, FILE* f) - : filename_(fname), file_(f) { } - virtual ~PosixSequentialFile() { fclose(file_); } - - virtual Status Read(size_t n, Slice* result, char* scratch) { - Status s; - size_t r = fread_unlocked(scratch, 1, n, file_); - *result = Slice(scratch, r); - if (r < n) { - if (feof(file_)) { - // We leave status as ok if we hit the end of the file - } else { - // A partial read with an error: return a non-ok status - s = IOError(filename_, errno); + PosixSequentialFile(std::string filename, int fd) + : fd_(fd), filename_(filename) {} + ~PosixSequentialFile() override { close(fd_); } + + Status Read(size_t n, Slice* result, char* scratch) override { + Status status; + while (true) { + ::ssize_t read_size = ::read(fd_, scratch, n); + if (read_size < 0) { // Read error. + if (errno == EINTR) { + continue; // Retry + } + status = PosixError(filename_, errno); + break; } + *result = Slice(scratch, read_size); + break; } - return s; + return status; } - virtual Status Skip(uint64_t n) { - if (fseek(file_, n, SEEK_CUR)) { - return IOError(filename_, errno); + Status Skip(uint64_t n) override { + if (::lseek(fd_, n, SEEK_CUR) == static_cast(-1)) { + return PosixError(filename_, errno); } return Status::OK(); } -}; -// pread() based random-access -class PosixRandomAccessFile: public RandomAccessFile { + virtual std::string GetName() const override { return filename_; } + private: - std::string filename_; - int fd_; + const int fd_; + const std::string filename_; +}; +// Implements random read access in a file using pread(). +// +// Instances of this class are thread-safe, as required by the RandomAccessFile +// API. Instances are immutable and Read() only calls thread-safe library +// functions. +class PosixRandomAccessFile final : public RandomAccessFile { public: - PosixRandomAccessFile(const std::string& fname, int fd) - : filename_(fname), fd_(fd) { } - virtual ~PosixRandomAccessFile() { close(fd_); } - - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { - Status s; - ssize_t r = pread(fd_, scratch, n, static_cast(offset)); - *result = Slice(scratch, (r < 0) ? 0 : r); - if (r < 0) { - // An error: return a non-ok status - s = IOError(filename_, errno); + // The new instance takes ownership of |fd|. |fd_limiter| must outlive this + // instance, and will be used to determine if . + PosixRandomAccessFile(std::string filename, int fd, Limiter* fd_limiter) + : has_permanent_fd_(fd_limiter->Acquire()), + fd_(has_permanent_fd_ ? fd : -1), + fd_limiter_(fd_limiter), + filename_(std::move(filename)) { + if (!has_permanent_fd_) { + assert(fd_ == -1); + ::close(fd); // The file will be opened on every read. } - return s; } -}; -// Helper class to limit mmap file usage so that we do not end up -// running out virtual memory or running into kernel performance -// problems for very large databases. -class MmapLimiter { - public: - // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. - MmapLimiter() { - SetAllowed(sizeof(void*) >= 8 ? 1000 : 0); + ~PosixRandomAccessFile() override { + if (has_permanent_fd_) { + assert(fd_ != -1); + ::close(fd_); + fd_limiter_->Release(); + } } - // If another mmap slot is available, acquire it and return true. - // Else return false. - bool Acquire() { - if (GetAllowed() <= 0) { - return false; + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { + int fd = fd_; + if (!has_permanent_fd_) { + fd = ::open(filename_.c_str(), O_RDONLY | kOpenBaseFlags); + if (fd < 0) { + return PosixError(filename_, errno); + } } - MutexLock l(&mu_); - intptr_t x = GetAllowed(); - if (x <= 0) { - return false; - } else { - SetAllowed(x - 1); - return true; + + assert(fd != -1); + + Status status; + ssize_t read_size = ::pread(fd, scratch, n, static_cast(offset)); + *result = Slice(scratch, (read_size < 0) ? 0 : read_size); + if (read_size < 0) { + // An error: return a non-ok status. + status = PosixError(filename_, errno); + } + if (!has_permanent_fd_) { + // Close the temporary file descriptor opened earlier. + assert(fd != fd_); + ::close(fd); } + return status; } - // Release a slot acquired by a previous call to Acquire() that returned true. - void Release() { - MutexLock l(&mu_); - SetAllowed(GetAllowed() + 1); - } + virtual std::string GetName() const override { return filename_; } private: - port::Mutex mu_; - port::AtomicPointer allowed_; + const bool has_permanent_fd_; // If false, the file is opened on every read. + const int fd_; // -1 if has_permanent_fd_ is false. + Limiter* const fd_limiter_; + const std::string filename_; +}; - intptr_t GetAllowed() const { - return reinterpret_cast(allowed_.Acquire_Load()); - } +// Implements random read access in a file using mmap(). +// +// Instances of this class are thread-safe, as required by the RandomAccessFile +// API. Instances are immutable and Read() only calls thread-safe library +// functions. +class PosixMmapReadableFile final : public RandomAccessFile { + public: + // mmap_base[0, length-1] points to the memory-mapped contents of the file. It + // must be the result of a successful call to mmap(). This instances takes + // over the ownership of the region. + // + // |mmap_limiter| must outlive this instance. The caller must have already + // aquired the right to use one mmap region, which will be released when this + // instance is destroyed. + PosixMmapReadableFile(std::string filename, char* mmap_base, size_t length, + Limiter* mmap_limiter) + : mmap_base_(mmap_base), + length_(length), + mmap_limiter_(mmap_limiter), + filename_(std::move(filename)) {} + + ~PosixMmapReadableFile() override { + ::munmap(static_cast(mmap_base_), length_); + mmap_limiter_->Release(); + } + + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { + if (offset + n > length_) { + *result = Slice(); + return PosixError(filename_, EINVAL); + } - // REQUIRES: mu_ must be held - void SetAllowed(intptr_t v) { - allowed_.Release_Store(reinterpret_cast(v)); + *result = Slice(mmap_base_ + offset, n); + return Status::OK(); } - MmapLimiter(const MmapLimiter&); - void operator=(const MmapLimiter&); -}; + virtual std::string GetName() const override { return filename_; } -// mmap() based random-access -class PosixMmapReadableFile: public RandomAccessFile { private: - std::string filename_; - void* mmapped_region_; - size_t length_; - MmapLimiter* limiter_; + char* const mmap_base_; + const size_t length_; + Limiter* const mmap_limiter_; + const std::string filename_; +}; +class PosixWritableFile final : public WritableFile { public: - // base[0,length-1] contains the mmapped contents of the file. - PosixMmapReadableFile(const std::string& fname, void* base, size_t length, - MmapLimiter* limiter) - : filename_(fname), mmapped_region_(base), length_(length), - limiter_(limiter) { + PosixWritableFile(std::string filename, int fd) + : pos_(0), + fd_(fd), + is_manifest_(IsManifest(filename)), + filename_(std::move(filename)), + dirname_(Dirname(filename_)) {} + + ~PosixWritableFile() override { + if (fd_ >= 0) { + // Ignoring any potential errors + Close(); + } } - virtual ~PosixMmapReadableFile() { - munmap(mmapped_region_, length_); - limiter_->Release(); - } + Status Append(const Slice& data) override { + size_t write_size = data.size(); + const char* write_data = data.data(); - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { - Status s; - if (offset + n > length_) { - *result = Slice(); - s = IOError(filename_, EINVAL); - } else { - *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); + // Fit as much as possible into buffer. + size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_); + std::memcpy(buf_ + pos_, write_data, copy_size); + write_data += copy_size; + write_size -= copy_size; + pos_ += copy_size; + if (write_size == 0) { + return Status::OK(); } - return s; - } -}; -class PosixWritableFile : public WritableFile { - private: - std::string filename_; - FILE* file_; - - public: - PosixWritableFile(const std::string& fname, FILE* f) - : filename_(fname), file_(f) { } + // Can't fit in buffer, so need to do at least one write. + Status status = FlushBuffer(); + if (!status.ok()) { + return status; + } - ~PosixWritableFile() { - if (file_ != NULL) { - // Ignoring any potential errors - fclose(file_); + // Small writes go to buffer, large writes are written directly. + if (write_size < kWritableFileBufferSize) { + std::memcpy(buf_, write_data, write_size); + pos_ = write_size; + return Status::OK(); } + return WriteUnbuffered(write_data, write_size); } - virtual Status Append(const Slice& data) { - size_t r = fwrite_unlocked(data.data(), 1, data.size(), file_); - if (r != data.size()) { - return IOError(filename_, errno); + Status Close() override { + Status status = FlushBuffer(); + const int close_result = ::close(fd_); + if (close_result < 0 && status.ok()) { + status = PosixError(filename_, errno); } - return Status::OK(); + fd_ = -1; + return status; } - virtual Status Close() { - Status result; - if (fclose(file_) != 0) { - result = IOError(filename_, errno); + Status Flush() override { return FlushBuffer(); } + + Status Sync() override { + // Ensure new files referred to by the manifest are in the filesystem. + // + // This needs to happen before the manifest file is flushed to disk, to + // avoid crashing in a state where the manifest refers to files that are not + // yet on disk. + Status status = SyncDirIfManifest(); + if (!status.ok()) { + return status; } - file_ = NULL; - return result; + + status = FlushBuffer(); + if (!status.ok()) { + return status; + } + + return SyncFd(fd_, filename_, false); } - virtual Status Flush() { - if (fflush_unlocked(file_) != 0) { - return IOError(filename_, errno); + private: + Status FlushBuffer() { + Status status = WriteUnbuffered(buf_, pos_); + pos_ = 0; + return status; + } + + Status WriteUnbuffered(const char* data, size_t size) { + while (size > 0) { + ssize_t write_result = ::write(fd_, data, size); + if (write_result < 0) { + if (errno == EINTR) { + continue; // Retry + } + return PosixError(filename_, errno); + } + data += write_result; + size -= write_result; } return Status::OK(); } Status SyncDirIfManifest() { - const char* f = filename_.c_str(); - const char* sep = strrchr(f, '/'); - Slice basename; - std::string dir; - if (sep == NULL) { - dir = "."; - basename = f; + Status status; + if (!is_manifest_) { + return status; + } + + int fd = ::open(dirname_.c_str(), O_RDONLY | kOpenBaseFlags); + if (fd < 0) { + status = PosixError(dirname_, errno); } else { - dir = std::string(f, sep - f); - basename = sep + 1; + status = SyncFd(fd, dirname_, true); + ::close(fd); } - Status s; - if (basename.starts_with("MANIFEST")) { - int fd = open(dir.c_str(), O_RDONLY); - if (fd < 0) { - s = IOError(dir, errno); - } else { - if (fsync(fd) < 0) { - s = IOError(dir, errno); - } - close(fd); - } + return status; + } + + // Ensures that all the caches associated with the given file descriptor's + // data are flushed all the way to durable media, and can withstand power + // failures. + // + // The path argument is only used to populate the description string in the + // returned Status if an error occurs. + static Status SyncFd(int fd, const std::string& fd_path, bool syncing_dir) { +#if HAVE_FULLFSYNC + // On macOS and iOS, fsync() doesn't guarantee durability past power + // failures. fcntl(F_FULLFSYNC) is required for that purpose. Some + // filesystems don't support fcntl(F_FULLFSYNC), and require a fallback to + // fsync(). + if (::fcntl(fd, F_FULLFSYNC) == 0) { + return Status::OK(); } - return s; +#endif // HAVE_FULLFSYNC + +#if HAVE_FDATASYNC + bool sync_success = ::fdatasync(fd) == 0; +#else + bool sync_success = ::fsync(fd) == 0; +#endif // HAVE_FDATASYNC + + if (sync_success) { + return Status::OK(); + } + // Do not crash if filesystem can't fsync directories + // (see https://github.com/bitcoin/bitcoin/pull/10000) + if (syncing_dir && errno == EINVAL) { + return Status::OK(); + } + return PosixError(fd_path, errno); } - virtual Status Sync() { - // Ensure new files referred to by the manifest are in the filesystem. - Status s = SyncDirIfManifest(); - if (!s.ok()) { - return s; + // Returns the directory name in a path pointing to a file. + // + // Returns "." if the path does not contain any directory separator. + static std::string Dirname(const std::string& filename) { + std::string::size_type separator_pos = filename.rfind('/'); + if (separator_pos == std::string::npos) { + return std::string("."); } - if (fflush_unlocked(file_) != 0 || - fdatasync(fileno(file_)) != 0) { - s = Status::IOError(filename_, strerror(errno)); + // The filename component should not contain a path separator. If it does, + // the splitting was done incorrectly. + assert(filename.find('/', separator_pos + 1) == std::string::npos); + + return filename.substr(0, separator_pos); + } + + // Extracts the file name from a path pointing to a file. + // + // The returned Slice points to |filename|'s data buffer, so it is only valid + // while |filename| is alive and unchanged. + static Slice Basename(const std::string& filename) { + std::string::size_type separator_pos = filename.rfind('/'); + if (separator_pos == std::string::npos) { + return Slice(filename); } - return s; + // The filename component should not contain a path separator. If it does, + // the splitting was done incorrectly. + assert(filename.find('/', separator_pos + 1) == std::string::npos); + + return Slice(filename.data() + separator_pos + 1, + filename.length() - separator_pos - 1); } + + // True if the given file is a manifest file. + static bool IsManifest(const std::string& filename) { + return Basename(filename).starts_with("MANIFEST"); + } + + virtual std::string GetName() const override { return filename_; } + + // buf_[0, pos_ - 1] contains data to be written to fd_. + char buf_[kWritableFileBufferSize]; + size_t pos_; + int fd_; + + const bool is_manifest_; // True if the file's name starts with MANIFEST. + const std::string filename_; + const std::string dirname_; // The directory of filename_. }; -static int LockOrUnlock(int fd, bool lock) { +int LockOrUnlock(int fd, bool lock) { errno = 0; - struct flock f; - memset(&f, 0, sizeof(f)); - f.l_type = (lock ? F_WRLCK : F_UNLCK); - f.l_whence = SEEK_SET; - f.l_start = 0; - f.l_len = 0; // Lock/unlock entire file - return fcntl(fd, F_SETLK, &f); + struct ::flock file_lock_info; + std::memset(&file_lock_info, 0, sizeof(file_lock_info)); + file_lock_info.l_type = (lock ? F_WRLCK : F_UNLCK); + file_lock_info.l_whence = SEEK_SET; + file_lock_info.l_start = 0; + file_lock_info.l_len = 0; // Lock/unlock entire file. + return ::fcntl(fd, F_SETLK, &file_lock_info); } +// Instances are thread-safe because they are immutable. class PosixFileLock : public FileLock { public: - int fd_; - std::string name_; + PosixFileLock(int fd, std::string filename) + : fd_(fd), filename_(std::move(filename)) {} + + int fd() const { return fd_; } + const std::string& filename() const { return filename_; } + + private: + const int fd_; + const std::string filename_; }; -// Set of locked files. We keep a separate set instead of just -// relying on fcntrl(F_SETLK) since fcntl(F_SETLK) does not provide -// any protection against multiple uses from the same process. +// Tracks the files locked by PosixEnv::LockFile(). +// +// We maintain a separate set instead of relying on fcntl(F_SETLK) because +// fcntl(F_SETLK) does not provide any protection against multiple uses from the +// same process. +// +// Instances are thread-safe because all member data is guarded by a mutex. class PosixLockTable { - private: - port::Mutex mu_; - std::set locked_files_; public: - bool Insert(const std::string& fname) { - MutexLock l(&mu_); - return locked_files_.insert(fname).second; - } - void Remove(const std::string& fname) { - MutexLock l(&mu_); + bool Insert(const std::string& fname) LOCKS_EXCLUDED(mu_) { + mu_.Lock(); + bool succeeded = locked_files_.insert(fname).second; + mu_.Unlock(); + return succeeded; + } + void Remove(const std::string& fname) LOCKS_EXCLUDED(mu_) { + mu_.Lock(); locked_files_.erase(fname); + mu_.Unlock(); } + + private: + port::Mutex mu_; + std::set locked_files_ GUARDED_BY(mu_); }; class PosixEnv : public Env { public: PosixEnv(); - virtual ~PosixEnv() { - char msg[] = "Destroying Env::Default()\n"; - fwrite(msg, 1, sizeof(msg), stderr); - abort(); - } - - virtual Status NewSequentialFile(const std::string& fname, - SequentialFile** result) { - FILE* f = fopen(fname.c_str(), "r"); - if (f == NULL) { - *result = NULL; - return IOError(fname, errno); - } else { - *result = new PosixSequentialFile(fname, f); - return Status::OK(); + ~PosixEnv() override { + static const char msg[] = + "PosixEnv singleton destroyed. Unsupported behavior!\n"; + std::fwrite(msg, 1, sizeof(msg), stderr); + std::abort(); + } + + Status NewSequentialFile(const std::string& filename, + SequentialFile** result) override { + int fd = ::open(filename.c_str(), O_RDONLY | kOpenBaseFlags); + if (fd < 0) { + *result = nullptr; + return PosixError(filename, errno); } + + *result = new PosixSequentialFile(filename, fd); + return Status::OK(); } - virtual Status NewRandomAccessFile(const std::string& fname, - RandomAccessFile** result) { - *result = NULL; - Status s; - int fd = open(fname.c_str(), O_RDONLY); + Status NewRandomAccessFile(const std::string& filename, + RandomAccessFile** result) override { + *result = nullptr; + int fd = ::open(filename.c_str(), O_RDONLY | kOpenBaseFlags); if (fd < 0) { - s = IOError(fname, errno); - } else if (mmap_limit_.Acquire()) { - uint64_t size; - s = GetFileSize(fname, &size); - if (s.ok()) { - void* base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (base != MAP_FAILED) { - *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); - } else { - s = IOError(fname, errno); - } - } - close(fd); - if (!s.ok()) { - mmap_limit_.Release(); + return PosixError(filename, errno); + } + + if (!mmap_limiter_.Acquire()) { + *result = new PosixRandomAccessFile(filename, fd, &fd_limiter_); + return Status::OK(); + } + + uint64_t file_size; + Status status = GetFileSize(filename, &file_size); + if (status.ok()) { + void* mmap_base = + ::mmap(/*addr=*/nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0); + if (mmap_base != MAP_FAILED) { + *result = new PosixMmapReadableFile(filename, + reinterpret_cast(mmap_base), + file_size, &mmap_limiter_); + } else { + status = PosixError(filename, errno); } - } else { - *result = new PosixRandomAccessFile(fname, fd); } - return s; + ::close(fd); + if (!status.ok()) { + mmap_limiter_.Release(); + } + return status; } - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) { - Status s; - FILE* f = fopen(fname.c_str(), "w"); - if (f == NULL) { - *result = NULL; - s = IOError(fname, errno); - } else { - *result = new PosixWritableFile(fname, f); + Status NewWritableFile(const std::string& filename, + WritableFile** result) override { + int fd = ::open(filename.c_str(), + O_TRUNC | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644); + if (fd < 0) { + *result = nullptr; + return PosixError(filename, errno); } - return s; + + *result = new PosixWritableFile(filename, fd); + return Status::OK(); } - virtual bool FileExists(const std::string& fname) { - return access(fname.c_str(), F_OK) == 0; + Status NewAppendableFile(const std::string& filename, + WritableFile** result) override { + int fd = ::open(filename.c_str(), + O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644); + if (fd < 0) { + *result = nullptr; + return PosixError(filename, errno); + } + + *result = new PosixWritableFile(filename, fd); + return Status::OK(); + } + + bool FileExists(const std::string& filename) override { + return ::access(filename.c_str(), F_OK) == 0; } - virtual Status GetChildren(const std::string& dir, - std::vector* result) { + Status GetChildren(const std::string& directory_path, + std::vector* result) override { result->clear(); - DIR* d = opendir(dir.c_str()); - if (d == NULL) { - return IOError(dir, errno); + ::DIR* dir = ::opendir(directory_path.c_str()); + if (dir == nullptr) { + return PosixError(directory_path, errno); } - struct dirent* entry; - while ((entry = readdir(d)) != NULL) { - result->push_back(entry->d_name); + struct ::dirent* entry; + while ((entry = ::readdir(dir)) != nullptr) { + result->emplace_back(entry->d_name); } - closedir(d); + ::closedir(dir); return Status::OK(); } - virtual Status DeleteFile(const std::string& fname) { - Status result; - if (unlink(fname.c_str()) != 0) { - result = IOError(fname, errno); + Status DeleteFile(const std::string& filename) override { + if (::unlink(filename.c_str()) != 0) { + return PosixError(filename, errno); } - return result; + return Status::OK(); } - virtual Status CreateDir(const std::string& name) { - Status result; - if (mkdir(name.c_str(), 0755) != 0) { - result = IOError(name, errno); + Status CreateDir(const std::string& dirname) override { + if (::mkdir(dirname.c_str(), 0755) != 0) { + return PosixError(dirname, errno); } - return result; + return Status::OK(); } - virtual Status DeleteDir(const std::string& name) { - Status result; - if (rmdir(name.c_str()) != 0) { - result = IOError(name, errno); + Status DeleteDir(const std::string& dirname) override { + if (::rmdir(dirname.c_str()) != 0) { + return PosixError(dirname, errno); } - return result; + return Status::OK(); } - virtual Status GetFileSize(const std::string& fname, uint64_t* size) { - Status s; - struct stat sbuf; - if (stat(fname.c_str(), &sbuf) != 0) { + Status GetFileSize(const std::string& filename, uint64_t* size) override { + struct ::stat file_stat; + if (::stat(filename.c_str(), &file_stat) != 0) { *size = 0; - s = IOError(fname, errno); - } else { - *size = sbuf.st_size; + return PosixError(filename, errno); } - return s; + *size = file_stat.st_size; + return Status::OK(); } - virtual Status RenameFile(const std::string& src, const std::string& target) { - Status result; - if (rename(src.c_str(), target.c_str()) != 0) { - result = IOError(src, errno); + Status RenameFile(const std::string& from, const std::string& to) override { + if (std::rename(from.c_str(), to.c_str()) != 0) { + return PosixError(from, errno); } - return result; + return Status::OK(); } - virtual Status LockFile(const std::string& fname, FileLock** lock) { - *lock = NULL; - Status result; - int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + Status LockFile(const std::string& filename, FileLock** lock) override { + *lock = nullptr; + + int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | kOpenBaseFlags, 0644); if (fd < 0) { - result = IOError(fname, errno); - } else if (!locks_.Insert(fname)) { - close(fd); - result = Status::IOError("lock " + fname, "already held by process"); - } else if (LockOrUnlock(fd, true) == -1) { - result = IOError("lock " + fname, errno); - close(fd); - locks_.Remove(fname); - } else { - PosixFileLock* my_lock = new PosixFileLock; - my_lock->fd_ = fd; - my_lock->name_ = fname; - *lock = my_lock; + return PosixError(filename, errno); } - return result; + + if (!locks_.Insert(filename)) { + ::close(fd); + return Status::IOError("lock " + filename, "already held by process"); + } + + if (LockOrUnlock(fd, true) == -1) { + int lock_errno = errno; + ::close(fd); + locks_.Remove(filename); + return PosixError("lock " + filename, lock_errno); + } + + *lock = new PosixFileLock(fd, filename); + return Status::OK(); } - virtual Status UnlockFile(FileLock* lock) { - PosixFileLock* my_lock = reinterpret_cast(lock); - Status result; - if (LockOrUnlock(my_lock->fd_, false) == -1) { - result = IOError("unlock", errno); + Status UnlockFile(FileLock* lock) override { + PosixFileLock* posix_file_lock = static_cast(lock); + if (LockOrUnlock(posix_file_lock->fd(), false) == -1) { + return PosixError("unlock " + posix_file_lock->filename(), errno); } - locks_.Remove(my_lock->name_); - close(my_lock->fd_); - delete my_lock; - return result; + locks_.Remove(posix_file_lock->filename()); + ::close(posix_file_lock->fd()); + delete posix_file_lock; + return Status::OK(); } - virtual void Schedule(void (*function)(void*), void* arg); + void Schedule(void (*background_work_function)(void* background_work_arg), + void* background_work_arg) override; - virtual void StartThread(void (*function)(void* arg), void* arg); + void StartThread(void (*thread_main)(void* thread_main_arg), + void* thread_main_arg) override { + std::thread new_thread(thread_main, thread_main_arg); + new_thread.detach(); + } - virtual Status GetTestDirectory(std::string* result) { - const char* env = getenv("TEST_TMPDIR"); + Status GetTestDirectory(std::string* result) override { + const char* env = std::getenv("TEST_TMPDIR"); if (env && env[0] != '\0') { *result = env; } else { char buf[100]; - snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); + std::snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", + static_cast(::geteuid())); *result = buf; } - // Directory may already exist + + // The CreateDir status is ignored because the directory may already exist. CreateDir(*result); + return Status::OK(); } - static uint64_t gettid() { - pthread_t tid = pthread_self(); - uint64_t thread_id = 0; - memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); - return thread_id; - } + Status NewLogger(const std::string& filename, Logger** result) override { + int fd = ::open(filename.c_str(), + O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644); + if (fd < 0) { + *result = nullptr; + return PosixError(filename, errno); + } - virtual Status NewLogger(const std::string& fname, Logger** result) { - FILE* f = fopen(fname.c_str(), "w"); - if (f == NULL) { - *result = NULL; - return IOError(fname, errno); + std::FILE* fp = ::fdopen(fd, "w"); + if (fp == nullptr) { + ::close(fd); + *result = nullptr; + return PosixError(filename, errno); } else { - *result = new PosixLogger(f, &PosixEnv::gettid); + *result = new PosixLogger(fp); return Status::OK(); } } - virtual uint64_t NowMicros() { - struct timeval tv; - gettimeofday(&tv, NULL); - return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + uint64_t NowMicros() override { + static constexpr uint64_t kUsecondsPerSecond = 1000000; + struct ::timeval tv; + ::gettimeofday(&tv, nullptr); + return static_cast(tv.tv_sec) * kUsecondsPerSecond + tv.tv_usec; } - virtual void SleepForMicroseconds(int micros) { - usleep(micros); + void SleepForMicroseconds(int micros) override { + std::this_thread::sleep_for(std::chrono::microseconds(micros)); } private: - void PthreadCall(const char* label, int result) { - if (result != 0) { - fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); - abort(); - } - } + void BackgroundThreadMain(); - // BGThread() is the body of the background thread - void BGThread(); - static void* BGThreadWrapper(void* arg) { - reinterpret_cast(arg)->BGThread(); - return NULL; + static void BackgroundThreadEntryPoint(PosixEnv* env) { + env->BackgroundThreadMain(); } - pthread_mutex_t mu_; - pthread_cond_t bgsignal_; - pthread_t bgthread_; - bool started_bgthread_; + // Stores the work item data in a Schedule() call. + // + // Instances are constructed on the thread calling Schedule() and used on the + // background thread. + // + // This structure is thread-safe beacuse it is immutable. + struct BackgroundWorkItem { + explicit BackgroundWorkItem(void (*function)(void* arg), void* arg) + : function(function), arg(arg) {} + + void (*const function)(void*); + void* const arg; + }; + + port::Mutex background_work_mutex_; + port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_); + bool started_background_thread_ GUARDED_BY(background_work_mutex_); - // Entry per Schedule() call - struct BGItem { void* arg; void (*function)(void*); }; - typedef std::deque BGQueue; - BGQueue queue_; + std::queue background_work_queue_ + GUARDED_BY(background_work_mutex_); - PosixLockTable locks_; - MmapLimiter mmap_limit_; + PosixLockTable locks_; // Thread-safe. + Limiter mmap_limiter_; // Thread-safe. + Limiter fd_limiter_; // Thread-safe. }; -PosixEnv::PosixEnv() : started_bgthread_(false) { - PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); - PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); +// Return the maximum number of concurrent mmaps. +int MaxMmaps() { return g_mmap_limit; } + +// Return the maximum number of read-only files to keep open. +int MaxOpenFiles() { + if (g_open_read_only_file_limit >= 0) { + return g_open_read_only_file_limit; + } + struct ::rlimit rlim; + if (::getrlimit(RLIMIT_NOFILE, &rlim)) { + // getrlimit failed, fallback to hard-coded default. + g_open_read_only_file_limit = 50; + } else if (rlim.rlim_cur == RLIM_INFINITY) { + g_open_read_only_file_limit = std::numeric_limits::max(); + } else { + // Allow use of 20% of available file descriptors for read-only files. + g_open_read_only_file_limit = rlim.rlim_cur / 5; + } + return g_open_read_only_file_limit; } -void PosixEnv::Schedule(void (*function)(void*), void* arg) { - PthreadCall("lock", pthread_mutex_lock(&mu_)); +} // namespace - // Start background thread if necessary - if (!started_bgthread_) { - started_bgthread_ = true; - PthreadCall( - "create thread", - pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); - } +PosixEnv::PosixEnv() + : background_work_cv_(&background_work_mutex_), + started_background_thread_(false), + mmap_limiter_(MaxMmaps()), + fd_limiter_(MaxOpenFiles()) {} + +void PosixEnv::Schedule( + void (*background_work_function)(void* background_work_arg), + void* background_work_arg) { + background_work_mutex_.Lock(); - // If the queue is currently empty, the background thread may currently be - // waiting. - if (queue_.empty()) { - PthreadCall("signal", pthread_cond_signal(&bgsignal_)); + // Start the background thread, if we haven't done so already. + if (!started_background_thread_) { + started_background_thread_ = true; + std::thread background_thread(PosixEnv::BackgroundThreadEntryPoint, this); + background_thread.detach(); } - // Add to priority queue - queue_.push_back(BGItem()); - queue_.back().function = function; - queue_.back().arg = arg; + // If the queue is empty, the background thread may be waiting for work. + if (background_work_queue_.empty()) { + background_work_cv_.Signal(); + } - PthreadCall("unlock", pthread_mutex_unlock(&mu_)); + background_work_queue_.emplace(background_work_function, background_work_arg); + background_work_mutex_.Unlock(); } -void PosixEnv::BGThread() { +void PosixEnv::BackgroundThreadMain() { while (true) { - // Wait until there is an item that is ready to run - PthreadCall("lock", pthread_mutex_lock(&mu_)); - while (queue_.empty()) { - PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); + background_work_mutex_.Lock(); + + // Wait until there is work to be done. + while (background_work_queue_.empty()) { + background_work_cv_.Wait(); } - void (*function)(void*) = queue_.front().function; - void* arg = queue_.front().arg; - queue_.pop_front(); + assert(!background_work_queue_.empty()); + auto background_work_function = background_work_queue_.front().function; + void* background_work_arg = background_work_queue_.front().arg; + background_work_queue_.pop(); - PthreadCall("unlock", pthread_mutex_unlock(&mu_)); - (*function)(arg); + background_work_mutex_.Unlock(); + background_work_function(background_work_arg); } } namespace { -struct StartThreadState { - void (*user_function)(void*); - void* arg; + +// Wraps an Env instance whose destructor is never created. +// +// Intended usage: +// using PlatformSingletonEnv = SingletonEnv; +// void ConfigurePosixEnv(int param) { +// PlatformSingletonEnv::AssertEnvNotInitialized(); +// // set global configuration flags. +// } +// Env* Env::Default() { +// static PlatformSingletonEnv default_env; +// return default_env.env(); +// } +template +class SingletonEnv { + public: + SingletonEnv() { +#if !defined(NDEBUG) + env_initialized_.store(true, std::memory_order_relaxed); +#endif // !defined(NDEBUG) + static_assert(sizeof(env_storage_) >= sizeof(EnvType), + "env_storage_ will not fit the Env"); + static_assert(alignof(decltype(env_storage_)) >= alignof(EnvType), + "env_storage_ does not meet the Env's alignment needs"); + new (&env_storage_) EnvType(); + } + ~SingletonEnv() = default; + + SingletonEnv(const SingletonEnv&) = delete; + SingletonEnv& operator=(const SingletonEnv&) = delete; + + Env* env() { return reinterpret_cast(&env_storage_); } + + static void AssertEnvNotInitialized() { +#if !defined(NDEBUG) + assert(!env_initialized_.load(std::memory_order_relaxed)); +#endif // !defined(NDEBUG) + } + + private: + typename std::aligned_storage::type + env_storage_; +#if !defined(NDEBUG) + static std::atomic env_initialized_; +#endif // !defined(NDEBUG) }; -} -static void* StartThreadWrapper(void* arg) { - StartThreadState* state = reinterpret_cast(arg); - state->user_function(state->arg); - delete state; - return NULL; -} -void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { - pthread_t t; - StartThreadState* state = new StartThreadState; - state->user_function = function; - state->arg = arg; - PthreadCall("start thread", - pthread_create(&t, NULL, &StartThreadWrapper, state)); -} +#if !defined(NDEBUG) +template +std::atomic SingletonEnv::env_initialized_; +#endif // !defined(NDEBUG) + +using PosixDefaultEnv = SingletonEnv; } // namespace -static pthread_once_t once = PTHREAD_ONCE_INIT; -static Env* default_env; -static void InitDefaultEnv() { default_env = new PosixEnv; } +void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) { + PosixDefaultEnv::AssertEnvNotInitialized(); + g_open_read_only_file_limit = limit; +} + +void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) { + PosixDefaultEnv::AssertEnvNotInitialized(); + g_mmap_limit = limit; +} Env* Env::Default() { - pthread_once(&once, InitDefaultEnv); - return default_env; + static PosixDefaultEnv env_container; + return env_container.env(); } } // namespace leveldb - -#endif diff --git a/src/leveldb/util/env_posix_test.cc b/src/leveldb/util/env_posix_test.cc new file mode 100644 index 000000000..9675d739a --- /dev/null +++ b/src/leveldb/util/env_posix_test.cc @@ -0,0 +1,350 @@ +// Copyright (c) 2011 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "leveldb/env.h" +#include "port/port.h" +#include "util/env_posix_test_helper.h" +#include "util/testharness.h" + +#if HAVE_O_CLOEXEC + +namespace { + +// Exit codes for the helper process spawned by TestCloseOnExec* tests. +// Useful for debugging test failures. +constexpr int kTextCloseOnExecHelperExecFailedCode = 61; +constexpr int kTextCloseOnExecHelperDup2FailedCode = 62; +constexpr int kTextCloseOnExecHelperFoundOpenFdCode = 63; + +// Global set by main() and read in TestCloseOnExec. +// +// The argv[0] value is stored in a std::vector instead of a std::string because +// std::string does not return a mutable pointer to its buffer until C++17. +// +// The vector stores the string pointed to by argv[0], plus the trailing null. +std::vector* GetArgvZero() { + static std::vector program_name; + return &program_name; +} + +// Command-line switch used to run this test as the CloseOnExecSwitch helper. +static const char kTestCloseOnExecSwitch[] = "--test-close-on-exec-helper"; + +// Executed in a separate process by TestCloseOnExec* tests. +// +// main() delegates to this function when the test executable is launched with +// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test +// executable and pass the special command-line switch. +// + +// main() delegates to this function when the test executable is launched with +// a special command-line switch. TestCloseOnExec* tests fork()+exec() the test +// executable and pass the special command-line switch. +// +// When main() delegates to this function, the process probes whether a given +// file descriptor is open, and communicates the result via its exit code. +int TestCloseOnExecHelperMain(char* pid_arg) { + int fd = std::atoi(pid_arg); + // When given the same file descriptor twice, dup2() returns -1 if the + // file descriptor is closed, or the given file descriptor if it is open. + if (::dup2(fd, fd) == fd) { + std::fprintf(stderr, "Unexpected open fd %d\n", fd); + return kTextCloseOnExecHelperFoundOpenFdCode; + } + // Double-check that dup2() is saying the file descriptor is closed. + if (errno != EBADF) { + std::fprintf(stderr, "Unexpected errno after calling dup2 on fd %d: %s\n", + fd, std::strerror(errno)); + return kTextCloseOnExecHelperDup2FailedCode; + } + return 0; +} + +// File descriptors are small non-negative integers. +// +// Returns void so the implementation can use ASSERT_EQ. +void GetMaxFileDescriptor(int* result_fd) { + // Get the maximum file descriptor number. + ::rlimit fd_rlimit; + ASSERT_EQ(0, ::getrlimit(RLIMIT_NOFILE, &fd_rlimit)); + *result_fd = fd_rlimit.rlim_cur; +} + +// Iterates through all possible FDs and returns the currently open ones. +// +// Returns void so the implementation can use ASSERT_EQ. +void GetOpenFileDescriptors(std::unordered_set* open_fds) { + int max_fd = 0; + GetMaxFileDescriptor(&max_fd); + + for (int fd = 0; fd < max_fd; ++fd) { + if (::dup2(fd, fd) != fd) { + // When given the same file descriptor twice, dup2() returns -1 if the + // file descriptor is closed, or the given file descriptor if it is open. + // + // Double-check that dup2() is saying the fd is closed. + ASSERT_EQ(EBADF, errno) + << "dup2() should set errno to EBADF on closed file descriptors"; + continue; + } + open_fds->insert(fd); + } +} + +// Finds an FD open since a previous call to GetOpenFileDescriptors(). +// +// |baseline_open_fds| is the result of a previous GetOpenFileDescriptors() +// call. Assumes that exactly one FD was opened since that call. +// +// Returns void so the implementation can use ASSERT_EQ. +void GetNewlyOpenedFileDescriptor( + const std::unordered_set& baseline_open_fds, int* result_fd) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + for (int fd : baseline_open_fds) { + ASSERT_EQ(1, open_fds.count(fd)) + << "Previously opened file descriptor was closed during test setup"; + open_fds.erase(fd); + } + ASSERT_EQ(1, open_fds.size()) + << "Expected exactly one newly opened file descriptor during test setup"; + *result_fd = *open_fds.begin(); +} + +// Check that a fork()+exec()-ed child process does not have an extra open FD. +void CheckCloseOnExecDoesNotLeakFDs( + const std::unordered_set& baseline_open_fds) { + // Prepare the argument list for the child process. + // execv() wants mutable buffers. + char switch_buffer[sizeof(kTestCloseOnExecSwitch)]; + std::memcpy(switch_buffer, kTestCloseOnExecSwitch, + sizeof(kTestCloseOnExecSwitch)); + + int probed_fd; + GetNewlyOpenedFileDescriptor(baseline_open_fds, &probed_fd); + std::string fd_string = std::to_string(probed_fd); + std::vector fd_buffer(fd_string.begin(), fd_string.end()); + fd_buffer.emplace_back('\0'); + + // The helper process is launched with the command below. + // env_posix_tests --test-close-on-exec-helper 3 + char* child_argv[] = {GetArgvZero()->data(), switch_buffer, fd_buffer.data(), + nullptr}; + + constexpr int kForkInChildProcessReturnValue = 0; + int child_pid = fork(); + if (child_pid == kForkInChildProcessReturnValue) { + ::execv(child_argv[0], child_argv); + std::fprintf(stderr, "Error spawning child process: %s\n", strerror(errno)); + std::exit(kTextCloseOnExecHelperExecFailedCode); + } + + int child_status = 0; + ASSERT_EQ(child_pid, ::waitpid(child_pid, &child_status, 0)); + ASSERT_TRUE(WIFEXITED(child_status)) + << "The helper process did not exit with an exit code"; + ASSERT_EQ(0, WEXITSTATUS(child_status)) + << "The helper process encountered an error"; +} + +} // namespace + +#endif // HAVE_O_CLOEXEC + +namespace leveldb { + +static const int kReadOnlyFileLimit = 4; +static const int kMMapLimit = 4; + +class EnvPosixTest { + public: + static void SetFileLimits(int read_only_file_limit, int mmap_limit) { + EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit); + EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit); + } + + EnvPosixTest() : env_(Env::Default()) {} + + Env* env_; +}; + +TEST(EnvPosixTest, TestOpenOnRead) { + // Write some test data to a single file that will be opened |n| times. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file = test_dir + "/open_on_read.txt"; + + FILE* f = fopen(test_file.c_str(), "we"); + ASSERT_TRUE(f != nullptr); + const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; + fputs(kFileData, f); + fclose(f); + + // Open test file some number above the sum of the two limits to force + // open-on-read behavior of POSIX Env leveldb::RandomAccessFile. + const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5; + leveldb::RandomAccessFile* files[kNumFiles] = {0}; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i])); + } + char scratch; + Slice read_result; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch)); + ASSERT_EQ(kFileData[i], read_result[0]); + } + for (int i = 0; i < kNumFiles; i++) { + delete files[i]; + } + ASSERT_OK(env_->DeleteFile(test_file)); +} + +#if HAVE_O_CLOEXEC + +TEST(EnvPosixTest, TestCloseOnExecSequentialFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_sequential.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::SequentialFile* file = nullptr; + ASSERT_OK(env_->NewSequentialFile(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +TEST(EnvPosixTest, TestCloseOnExecRandomAccessFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_random_access.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + // Exhaust the RandomAccessFile mmap limit. This way, the test + // RandomAccessFile instance below is backed by a file descriptor, not by an + // mmap region. + leveldb::RandomAccessFile* mmapped_files[kReadOnlyFileLimit] = {nullptr}; + for (int i = 0; i < kReadOnlyFileLimit; i++) { + ASSERT_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i])); + } + + leveldb::RandomAccessFile* file = nullptr; + ASSERT_OK(env_->NewRandomAccessFile(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + for (int i = 0; i < kReadOnlyFileLimit; i++) { + delete mmapped_files[i]; + } + ASSERT_OK(env_->DeleteFile(file_path)); +} + +TEST(EnvPosixTest, TestCloseOnExecWritableFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_writable.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::WritableFile* file = nullptr; + ASSERT_OK(env_->NewWritableFile(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +TEST(EnvPosixTest, TestCloseOnExecAppendableFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_appendable.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::WritableFile* file = nullptr; + ASSERT_OK(env_->NewAppendableFile(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +TEST(EnvPosixTest, TestCloseOnExecLockFile) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_lock.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::FileLock* lock = nullptr; + ASSERT_OK(env_->LockFile(file_path, &lock)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + ASSERT_OK(env_->UnlockFile(lock)); + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +TEST(EnvPosixTest, TestCloseOnExecLogger) { + std::unordered_set open_fds; + GetOpenFileDescriptors(&open_fds); + + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string file_path = test_dir + "/close_on_exec_logger.txt"; + ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path)); + + leveldb::Logger* file = nullptr; + ASSERT_OK(env_->NewLogger(file_path, &file)); + CheckCloseOnExecDoesNotLeakFDs(open_fds); + delete file; + + ASSERT_OK(env_->DeleteFile(file_path)); +} + +#endif // HAVE_O_CLOEXEC + +} // namespace leveldb + +int main(int argc, char** argv) { +#if HAVE_O_CLOEXEC + // Check if we're invoked as a helper program, or as the test suite. + for (int i = 1; i < argc; ++i) { + if (!std::strcmp(argv[i], kTestCloseOnExecSwitch)) { + return TestCloseOnExecHelperMain(argv[i + 1]); + } + } + + // Save argv[0] early, because googletest may modify argv. + GetArgvZero()->assign(argv[0], argv[0] + std::strlen(argv[0]) + 1); +#endif // HAVE_O_CLOEXEC + + // All tests currently run with the same read-only file limits. + leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit, + leveldb::kMMapLimit); + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/env_posix_test_helper.h b/src/leveldb/util/env_posix_test_helper.h new file mode 100644 index 000000000..038696059 --- /dev/null +++ b/src/leveldb/util/env_posix_test_helper.h @@ -0,0 +1,28 @@ +// Copyright 2017 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ +#define STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ + +namespace leveldb { + +class EnvPosixTest; + +// A helper for the POSIX Env to facilitate testing. +class EnvPosixTestHelper { + private: + friend class EnvPosixTest; + + // Set the maximum number of read-only files that will be opened. + // Must be called before creating an Env. + static void SetReadOnlyFDLimit(int limit); + + // Set the maximum number of read-only files that will be mapped via mmap. + // Must be called before creating an Env. + static void SetReadOnlyMMapLimit(int limit); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ diff --git a/src/leveldb/util/env_test.cc b/src/leveldb/util/env_test.cc index b72cb4438..7db03fc11 100644 --- a/src/leveldb/util/env_test.cc +++ b/src/leveldb/util/env_test.cc @@ -4,70 +4,144 @@ #include "leveldb/env.h" +#include + #include "port/port.h" +#include "port/thread_annotations.h" +#include "util/mutexlock.h" #include "util/testharness.h" +#include "util/testutil.h" namespace leveldb { static const int kDelayMicros = 100000; -class EnvPosixTest { - private: - port::Mutex mu_; - std::string events_; - +class EnvTest { public: + EnvTest() : env_(Env::Default()) {} + Env* env_; - EnvPosixTest() : env_(Env::Default()) { } }; -static void SetBool(void* ptr) { - reinterpret_cast(ptr)->NoBarrier_Store(ptr); +TEST(EnvTest, ReadWrite) { + Random rnd(test::RandomSeed()); + + // Get file to use for testing. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file_name = test_dir + "/open_on_read.txt"; + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file)); + + // Fill a file with data generated via a sequence of randomly sized writes. + static const size_t kDataSize = 10 * 1048576; + std::string data; + while (data.size() < kDataSize) { + int len = rnd.Skewed(18); // Up to 2^18 - 1, but typically much smaller + std::string r; + test::RandomString(&rnd, len, &r); + ASSERT_OK(writable_file->Append(r)); + data += r; + if (rnd.OneIn(10)) { + ASSERT_OK(writable_file->Flush()); + } + } + ASSERT_OK(writable_file->Sync()); + ASSERT_OK(writable_file->Close()); + delete writable_file; + + // Read all data using a sequence of randomly sized reads. + SequentialFile* sequential_file; + ASSERT_OK(env_->NewSequentialFile(test_file_name, &sequential_file)); + std::string read_result; + std::string scratch; + while (read_result.size() < data.size()) { + int len = std::min(rnd.Skewed(18), data.size() - read_result.size()); + scratch.resize(std::max(len, 1)); // at least 1 so &scratch[0] is legal + Slice read; + ASSERT_OK(sequential_file->Read(len, &read, &scratch[0])); + if (len > 0) { + ASSERT_GT(read.size(), 0); + } + ASSERT_LE(read.size(), len); + read_result.append(read.data(), read.size()); + } + ASSERT_EQ(read_result, data); + delete sequential_file; } -TEST(EnvPosixTest, RunImmediately) { - port::AtomicPointer called (NULL); - env_->Schedule(&SetBool, &called); - Env::Default()->SleepForMicroseconds(kDelayMicros); - ASSERT_TRUE(called.NoBarrier_Load() != NULL); +TEST(EnvTest, RunImmediately) { + struct RunState { + port::Mutex mu; + port::CondVar cvar{&mu}; + bool called = false; + + static void Run(void* arg) { + RunState* state = reinterpret_cast(arg); + MutexLock l(&state->mu); + ASSERT_EQ(state->called, false); + state->called = true; + state->cvar.Signal(); + } + }; + + RunState state; + env_->Schedule(&RunState::Run, &state); + + MutexLock l(&state.mu); + while (!state.called) { + state.cvar.Wait(); + } } -TEST(EnvPosixTest, RunMany) { - port::AtomicPointer last_id (NULL); +TEST(EnvTest, RunMany) { + struct RunState { + port::Mutex mu; + port::CondVar cvar{&mu}; + int last_id = 0; + }; + + struct Callback { + RunState* state_; // Pointer to shared state. + const int id_; // Order# for the execution of this callback. - struct CB { - port::AtomicPointer* last_id_ptr; // Pointer to shared slot - uintptr_t id; // Order# for the execution of this callback + Callback(RunState* s, int id) : state_(s), id_(id) {} - CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { } + static void Run(void* arg) { + Callback* callback = reinterpret_cast(arg); + RunState* state = callback->state_; - static void Run(void* v) { - CB* cb = reinterpret_cast(v); - void* cur = cb->last_id_ptr->NoBarrier_Load(); - ASSERT_EQ(cb->id-1, reinterpret_cast(cur)); - cb->last_id_ptr->Release_Store(reinterpret_cast(cb->id)); + MutexLock l(&state->mu); + ASSERT_EQ(state->last_id, callback->id_ - 1); + state->last_id = callback->id_; + state->cvar.Signal(); } }; - // Schedule in different order than start time - CB cb1(&last_id, 1); - CB cb2(&last_id, 2); - CB cb3(&last_id, 3); - CB cb4(&last_id, 4); - env_->Schedule(&CB::Run, &cb1); - env_->Schedule(&CB::Run, &cb2); - env_->Schedule(&CB::Run, &cb3); - env_->Schedule(&CB::Run, &cb4); - - Env::Default()->SleepForMicroseconds(kDelayMicros); - void* cur = last_id.Acquire_Load(); - ASSERT_EQ(4, reinterpret_cast(cur)); + RunState state; + Callback callback1(&state, 1); + Callback callback2(&state, 2); + Callback callback3(&state, 3); + Callback callback4(&state, 4); + env_->Schedule(&Callback::Run, &callback1); + env_->Schedule(&Callback::Run, &callback2); + env_->Schedule(&Callback::Run, &callback3); + env_->Schedule(&Callback::Run, &callback4); + + MutexLock l(&state.mu); + while (state.last_id != 4) { + state.cvar.Wait(); + } } struct State { port::Mutex mu; - int val; - int num_running; + port::CondVar cvar{&mu}; + + int val GUARDED_BY(mu); + int num_running GUARDED_BY(mu); + + State(int val, int num_running) : val(val), num_running(num_running) {} }; static void ThreadBody(void* arg) { @@ -75,30 +149,89 @@ static void ThreadBody(void* arg) { s->mu.Lock(); s->val += 1; s->num_running -= 1; + s->cvar.Signal(); s->mu.Unlock(); } -TEST(EnvPosixTest, StartThread) { - State state; - state.val = 0; - state.num_running = 3; +TEST(EnvTest, StartThread) { + State state(0, 3); for (int i = 0; i < 3; i++) { env_->StartThread(&ThreadBody, &state); } - while (true) { - state.mu.Lock(); - int num = state.num_running; - state.mu.Unlock(); - if (num == 0) { - break; - } - Env::Default()->SleepForMicroseconds(kDelayMicros); + + MutexLock l(&state.mu); + while (state.num_running != 0) { + state.cvar.Wait(); } ASSERT_EQ(state.val, 3); } -} // namespace leveldb +TEST(EnvTest, TestOpenNonExistentFile) { + // Write some test data to a single file that will be opened |n| times. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + + std::string non_existent_file = test_dir + "/non_existent_file"; + ASSERT_TRUE(!env_->FileExists(non_existent_file)); + + RandomAccessFile* random_access_file; + Status status = + env_->NewRandomAccessFile(non_existent_file, &random_access_file); + ASSERT_TRUE(status.IsNotFound()); -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); + SequentialFile* sequential_file; + status = env_->NewSequentialFile(non_existent_file, &sequential_file); + ASSERT_TRUE(status.IsNotFound()); } + +TEST(EnvTest, ReopenWritableFile) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file_name = test_dir + "/reopen_writable_file.txt"; + env_->DeleteFile(test_file_name); + + WritableFile* writable_file; + ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file)); + std::string data("hello world!"); + ASSERT_OK(writable_file->Append(data)); + ASSERT_OK(writable_file->Close()); + delete writable_file; + + ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file)); + data = "42"; + ASSERT_OK(writable_file->Append(data)); + ASSERT_OK(writable_file->Close()); + delete writable_file; + + ASSERT_OK(ReadFileToString(env_, test_file_name, &data)); + ASSERT_EQ(std::string("42"), data); + env_->DeleteFile(test_file_name); +} + +TEST(EnvTest, ReopenAppendableFile) { + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file_name = test_dir + "/reopen_appendable_file.txt"; + env_->DeleteFile(test_file_name); + + WritableFile* appendable_file; + ASSERT_OK(env_->NewAppendableFile(test_file_name, &appendable_file)); + std::string data("hello world!"); + ASSERT_OK(appendable_file->Append(data)); + ASSERT_OK(appendable_file->Close()); + delete appendable_file; + + ASSERT_OK(env_->NewAppendableFile(test_file_name, &appendable_file)); + data = "42"; + ASSERT_OK(appendable_file->Append(data)); + ASSERT_OK(appendable_file->Close()); + delete appendable_file; + + ASSERT_OK(ReadFileToString(env_, test_file_name, &data)); + ASSERT_EQ(std::string("hello world!42"), data); + env_->DeleteFile(test_file_name); +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc deleted file mode 100644 index e11a96b79..000000000 --- a/src/leveldb/util/env_win.cc +++ /dev/null @@ -1,873 +0,0 @@ -// This file contains source that originates from: -// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/env_win32.h -// http://code.google.com/p/leveldbwin/source/browse/trunk/win32_impl_src/port_win32.cc -// Those files dont' have any explict license headers but the -// project (http://code.google.com/p/leveldbwin/) lists the 'New BSD License' -// as the license. -#if defined(LEVELDB_PLATFORM_WINDOWS) -#include - - -#include "leveldb/env.h" - -#include "port/port.h" -#include "leveldb/slice.h" -#include "util/logging.h" - -#include -#include -#include -#include -#include -#include -#include - -#ifdef max -#undef max -#endif - -#ifndef va_copy -#define va_copy(d,s) ((d) = (s)) -#endif - -#if defined DeleteFile -#undef DeleteFile -#endif - -//Declarations -namespace leveldb -{ - -namespace Win32 -{ - -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -std::string GetCurrentDir(); -std::wstring GetCurrentDirW(); - -static const std::string CurrentDir = GetCurrentDir(); -static const std::wstring CurrentDirW = GetCurrentDirW(); - -std::string& ModifyPath(std::string& path); -std::wstring& ModifyPath(std::wstring& path); - -std::string GetLastErrSz(); -std::wstring GetLastErrSzW(); - -size_t GetPageSize(); - -typedef void (*ScheduleProc)(void*) ; - -struct WorkItemWrapper -{ - WorkItemWrapper(ScheduleProc proc_,void* content_); - ScheduleProc proc; - void* pContent; -}; - -DWORD WINAPI WorkItemWrapperProc(LPVOID pContent); - -class Win32SequentialFile : public SequentialFile -{ -public: - friend class Win32Env; - virtual ~Win32SequentialFile(); - virtual Status Read(size_t n, Slice* result, char* scratch); - virtual Status Skip(uint64_t n); - BOOL isEnable(); -private: - BOOL _Init(); - void _CleanUp(); - Win32SequentialFile(const std::string& fname); - std::string _filename; - ::HANDLE _hFile; - DISALLOW_COPY_AND_ASSIGN(Win32SequentialFile); -}; - -class Win32RandomAccessFile : public RandomAccessFile -{ -public: - friend class Win32Env; - virtual ~Win32RandomAccessFile(); - virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const; - BOOL isEnable(); -private: - BOOL _Init(LPCWSTR path); - void _CleanUp(); - Win32RandomAccessFile(const std::string& fname); - HANDLE _hFile; - const std::string _filename; - DISALLOW_COPY_AND_ASSIGN(Win32RandomAccessFile); -}; - -class Win32WritableFile : public WritableFile -{ -public: - Win32WritableFile(const std::string& fname); - ~Win32WritableFile(); - - virtual Status Append(const Slice& data); - virtual Status Close(); - virtual Status Flush(); - virtual Status Sync(); - BOOL isEnable(); -private: - std::string filename_; - ::HANDLE _hFile; -}; - -class Win32FileLock : public FileLock -{ -public: - friend class Win32Env; - virtual ~Win32FileLock(); - BOOL isEnable(); -private: - BOOL _Init(LPCWSTR path); - void _CleanUp(); - Win32FileLock(const std::string& fname); - HANDLE _hFile; - std::string _filename; - DISALLOW_COPY_AND_ASSIGN(Win32FileLock); -}; - -class Win32Logger : public Logger -{ -public: - friend class Win32Env; - virtual ~Win32Logger(); - virtual void Logv(const char* format, va_list ap); -private: - explicit Win32Logger(WritableFile* pFile); - WritableFile* _pFileProxy; - DISALLOW_COPY_AND_ASSIGN(Win32Logger); -}; - -class Win32Env : public Env -{ -public: - Win32Env(); - virtual ~Win32Env(); - virtual Status NewSequentialFile(const std::string& fname, - SequentialFile** result); - - virtual Status NewRandomAccessFile(const std::string& fname, - RandomAccessFile** result); - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result); - - virtual bool FileExists(const std::string& fname); - - virtual Status GetChildren(const std::string& dir, - std::vector* result); - - virtual Status DeleteFile(const std::string& fname); - - virtual Status CreateDir(const std::string& dirname); - - virtual Status DeleteDir(const std::string& dirname); - - virtual Status GetFileSize(const std::string& fname, uint64_t* file_size); - - virtual Status RenameFile(const std::string& src, - const std::string& target); - - virtual Status LockFile(const std::string& fname, FileLock** lock); - - virtual Status UnlockFile(FileLock* lock); - - virtual void Schedule( - void (*function)(void* arg), - void* arg); - - virtual void StartThread(void (*function)(void* arg), void* arg); - - virtual Status GetTestDirectory(std::string* path); - - //virtual void Logv(WritableFile* log, const char* format, va_list ap); - - virtual Status NewLogger(const std::string& fname, Logger** result); - - virtual uint64_t NowMicros(); - - virtual void SleepForMicroseconds(int micros); -}; - -void ToWidePath(const std::string& value, std::wstring& target) { - wchar_t buffer[MAX_PATH]; - MultiByteToWideChar(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH); - target = buffer; -} - -void ToNarrowPath(const std::wstring& value, std::string& target) { - char buffer[MAX_PATH]; - WideCharToMultiByte(CP_ACP, 0, value.c_str(), -1, buffer, MAX_PATH, NULL, NULL); - target = buffer; -} - -std::string GetCurrentDir() -{ - CHAR path[MAX_PATH]; - ::GetModuleFileNameA(::GetModuleHandleA(NULL),path,MAX_PATH); - *strrchr(path,'\\') = 0; - return std::string(path); -} - -std::wstring GetCurrentDirW() -{ - WCHAR path[MAX_PATH]; - ::GetModuleFileNameW(::GetModuleHandleW(NULL),path,MAX_PATH); - *wcsrchr(path,L'\\') = 0; - return std::wstring(path); -} - -std::string& ModifyPath(std::string& path) -{ - if(path[0] == '/' || path[0] == '\\'){ - path = CurrentDir + path; - } - std::replace(path.begin(),path.end(),'/','\\'); - - return path; -} - -std::wstring& ModifyPath(std::wstring& path) -{ - if(path[0] == L'/' || path[0] == L'\\'){ - path = CurrentDirW + path; - } - std::replace(path.begin(),path.end(),L'/',L'\\'); - return path; -} - -std::string GetLastErrSz() -{ - LPWSTR lpMsgBuf; - FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - GetLastError(), - 0, // Default language - (LPWSTR) &lpMsgBuf, - 0, - NULL - ); - std::string Err; - ToNarrowPath(lpMsgBuf, Err); - LocalFree( lpMsgBuf ); - return Err; -} - -std::wstring GetLastErrSzW() -{ - LPVOID lpMsgBuf; - FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - GetLastError(), - 0, // Default language - (LPWSTR) &lpMsgBuf, - 0, - NULL - ); - std::wstring Err = (LPCWSTR)lpMsgBuf; - LocalFree(lpMsgBuf); - return Err; -} - -WorkItemWrapper::WorkItemWrapper( ScheduleProc proc_,void* content_ ) : - proc(proc_),pContent(content_) -{ - -} - -DWORD WINAPI WorkItemWrapperProc(LPVOID pContent) -{ - WorkItemWrapper* item = static_cast(pContent); - ScheduleProc TempProc = item->proc; - void* arg = item->pContent; - delete item; - TempProc(arg); - return 0; -} - -size_t GetPageSize() -{ - SYSTEM_INFO si; - GetSystemInfo(&si); - return std::max(si.dwPageSize,si.dwAllocationGranularity); -} - -const size_t g_PageSize = GetPageSize(); - - -Win32SequentialFile::Win32SequentialFile( const std::string& fname ) : - _filename(fname),_hFile(NULL) -{ - _Init(); -} - -Win32SequentialFile::~Win32SequentialFile() -{ - _CleanUp(); -} - -Status Win32SequentialFile::Read( size_t n, Slice* result, char* scratch ) -{ - Status sRet; - DWORD hasRead = 0; - if(_hFile && ReadFile(_hFile,scratch,n,&hasRead,NULL) ){ - *result = Slice(scratch,hasRead); - } else { - sRet = Status::IOError(_filename, Win32::GetLastErrSz() ); - } - return sRet; -} - -Status Win32SequentialFile::Skip( uint64_t n ) -{ - Status sRet; - LARGE_INTEGER Move,NowPointer; - Move.QuadPart = n; - if(!SetFilePointerEx(_hFile,Move,&NowPointer,FILE_CURRENT)){ - sRet = Status::IOError(_filename,Win32::GetLastErrSz()); - } - return sRet; -} - -BOOL Win32SequentialFile::isEnable() -{ - return _hFile ? TRUE : FALSE; -} - -BOOL Win32SequentialFile::_Init() -{ - std::wstring path; - ToWidePath(_filename, path); - _hFile = CreateFileW(path.c_str(), - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - return _hFile ? TRUE : FALSE; -} - -void Win32SequentialFile::_CleanUp() -{ - if(_hFile){ - CloseHandle(_hFile); - _hFile = NULL; - } -} - -Win32RandomAccessFile::Win32RandomAccessFile( const std::string& fname ) : - _filename(fname),_hFile(NULL) -{ - std::wstring path; - ToWidePath(fname, path); - _Init( path.c_str() ); -} - -Win32RandomAccessFile::~Win32RandomAccessFile() -{ - _CleanUp(); -} - -Status Win32RandomAccessFile::Read(uint64_t offset,size_t n,Slice* result,char* scratch) const -{ - Status sRet; - OVERLAPPED ol = {0}; - ZeroMemory(&ol,sizeof(ol)); - ol.Offset = (DWORD)offset; - ol.OffsetHigh = (DWORD)(offset >> 32); - DWORD hasRead = 0; - if(!ReadFile(_hFile,scratch,n,&hasRead,&ol)) - sRet = Status::IOError(_filename,Win32::GetLastErrSz()); - else - *result = Slice(scratch,hasRead); - return sRet; -} - -BOOL Win32RandomAccessFile::_Init( LPCWSTR path ) -{ - BOOL bRet = FALSE; - if(!_hFile) - _hFile = ::CreateFileW(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,NULL); - if(!_hFile || _hFile == INVALID_HANDLE_VALUE ) - _hFile = NULL; - else - bRet = TRUE; - return bRet; -} - -BOOL Win32RandomAccessFile::isEnable() -{ - return _hFile ? TRUE : FALSE; -} - -void Win32RandomAccessFile::_CleanUp() -{ - if(_hFile){ - ::CloseHandle(_hFile); - _hFile = NULL; - } -} - -Win32WritableFile::Win32WritableFile(const std::string& fname) - : filename_(fname) -{ - std::wstring path; - ToWidePath(fname, path); - DWORD Flag = PathFileExistsW(path.c_str()) ? OPEN_EXISTING : CREATE_ALWAYS; - _hFile = CreateFileW(path.c_str(), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE, - NULL, - Flag, - FILE_ATTRIBUTE_NORMAL, - NULL); - // CreateFileW returns INVALID_HANDLE_VALUE in case of error, always check isEnable() before use -} - -Win32WritableFile::~Win32WritableFile() -{ - if (_hFile != INVALID_HANDLE_VALUE) - Close(); -} - -Status Win32WritableFile::Append(const Slice& data) -{ - DWORD r = 0; - if (!WriteFile(_hFile, data.data(), data.size(), &r, NULL) || r != data.size()) { - return Status::IOError("Win32WritableFile.Append::WriteFile: "+filename_, Win32::GetLastErrSz()); - } - return Status::OK(); -} - -Status Win32WritableFile::Close() -{ - if (!CloseHandle(_hFile)) { - return Status::IOError("Win32WritableFile.Close::CloseHandle: "+filename_, Win32::GetLastErrSz()); - } - _hFile = INVALID_HANDLE_VALUE; - return Status::OK(); -} - -Status Win32WritableFile::Flush() -{ - // Nothing to do here, there are no application-side buffers - return Status::OK(); -} - -Status Win32WritableFile::Sync() -{ - if (!FlushFileBuffers(_hFile)) { - return Status::IOError("Win32WritableFile.Sync::FlushFileBuffers "+filename_, Win32::GetLastErrSz()); - } - return Status::OK(); -} - -BOOL Win32WritableFile::isEnable() -{ - return _hFile != INVALID_HANDLE_VALUE; -} - -Win32FileLock::Win32FileLock( const std::string& fname ) : - _hFile(NULL),_filename(fname) -{ - std::wstring path; - ToWidePath(fname, path); - _Init(path.c_str()); -} - -Win32FileLock::~Win32FileLock() -{ - _CleanUp(); -} - -BOOL Win32FileLock::_Init( LPCWSTR path ) -{ - BOOL bRet = FALSE; - if(!_hFile) - _hFile = ::CreateFileW(path,0,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); - if(!_hFile || _hFile == INVALID_HANDLE_VALUE ){ - _hFile = NULL; - } - else - bRet = TRUE; - return bRet; -} - -void Win32FileLock::_CleanUp() -{ - ::CloseHandle(_hFile); - _hFile = NULL; -} - -BOOL Win32FileLock::isEnable() -{ - return _hFile ? TRUE : FALSE; -} - -Win32Logger::Win32Logger(WritableFile* pFile) : _pFileProxy(pFile) -{ - assert(_pFileProxy); -} - -Win32Logger::~Win32Logger() -{ - if(_pFileProxy) - delete _pFileProxy; -} - -void Win32Logger::Logv( const char* format, va_list ap ) -{ - uint64_t thread_id = ::GetCurrentThreadId(); - - // We try twice: the first time with a fixed-size stack allocated buffer, - // and the second time with a much larger dynamically allocated buffer. - char buffer[500]; - for (int iter = 0; iter < 2; iter++) { - char* base; - int bufsize; - if (iter == 0) { - bufsize = sizeof(buffer); - base = buffer; - } else { - bufsize = 30000; - base = new char[bufsize]; - } - char* p = base; - char* limit = base + bufsize; - - SYSTEMTIME st; - GetLocalTime(&st); - p += snprintf(p, limit - p, - "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", - int(st.wYear), - int(st.wMonth), - int(st.wDay), - int(st.wHour), - int(st.wMinute), - int(st.wMinute), - int(st.wMilliseconds), - static_cast(thread_id)); - - // Print the message - if (p < limit) { - va_list backup_ap; - va_copy(backup_ap, ap); - p += vsnprintf(p, limit - p, format, backup_ap); - va_end(backup_ap); - } - - // Truncate to available space if necessary - if (p >= limit) { - if (iter == 0) { - continue; // Try again with larger buffer - } else { - p = limit - 1; - } - } - - // Add newline if necessary - if (p == base || p[-1] != '\n') { - *p++ = '\n'; - } - - assert(p <= limit); - DWORD hasWritten = 0; - if(_pFileProxy){ - _pFileProxy->Append(Slice(base, p - base)); - _pFileProxy->Flush(); - } - if (base != buffer) { - delete[] base; - } - break; - } -} - -bool Win32Env::FileExists(const std::string& fname) -{ - std::string path = fname; - std::wstring wpath; - ToWidePath(ModifyPath(path), wpath); - return ::PathFileExistsW(wpath.c_str()) ? true : false; -} - -Status Win32Env::GetChildren(const std::string& dir, std::vector* result) -{ - Status sRet; - ::WIN32_FIND_DATAW wfd; - std::string path = dir; - ModifyPath(path); - path += "\\*.*"; - std::wstring wpath; - ToWidePath(path, wpath); - - ::HANDLE hFind = ::FindFirstFileW(wpath.c_str() ,&wfd); - if(hFind && hFind != INVALID_HANDLE_VALUE){ - BOOL hasNext = TRUE; - std::string child; - while(hasNext){ - ToNarrowPath(wfd.cFileName, child); - if(child != ".." && child != ".") { - result->push_back(child); - } - hasNext = ::FindNextFileW(hFind,&wfd); - } - ::FindClose(hFind); - } - else - sRet = Status::IOError(dir,"Could not get children."); - return sRet; -} - -void Win32Env::SleepForMicroseconds( int micros ) -{ - ::Sleep((micros + 999) /1000); -} - - -Status Win32Env::DeleteFile( const std::string& fname ) -{ - Status sRet; - std::string path = fname; - std::wstring wpath; - ToWidePath(ModifyPath(path), wpath); - - if(!::DeleteFileW(wpath.c_str())) { - sRet = Status::IOError(path, "Could not delete file."); - } - return sRet; -} - -Status Win32Env::GetFileSize( const std::string& fname, uint64_t* file_size ) -{ - Status sRet; - std::string path = fname; - std::wstring wpath; - ToWidePath(ModifyPath(path), wpath); - - HANDLE file = ::CreateFileW(wpath.c_str(), - GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); - LARGE_INTEGER li; - if(::GetFileSizeEx(file,&li)){ - *file_size = (uint64_t)li.QuadPart; - }else - sRet = Status::IOError(path,"Could not get the file size."); - CloseHandle(file); - return sRet; -} - -Status Win32Env::RenameFile( const std::string& src, const std::string& target ) -{ - Status sRet; - std::string src_path = src; - std::wstring wsrc_path; - ToWidePath(ModifyPath(src_path), wsrc_path); - std::string target_path = target; - std::wstring wtarget_path; - ToWidePath(ModifyPath(target_path), wtarget_path); - - if(!MoveFileW(wsrc_path.c_str(), wtarget_path.c_str() ) ){ - DWORD err = GetLastError(); - if(err == 0x000000b7){ - if(!::DeleteFileW(wtarget_path.c_str() ) ) - sRet = Status::IOError(src, "Could not rename file."); - else if(!::MoveFileW(wsrc_path.c_str(), - wtarget_path.c_str() ) ) - sRet = Status::IOError(src, "Could not rename file."); - } - } - return sRet; -} - -Status Win32Env::LockFile( const std::string& fname, FileLock** lock ) -{ - Status sRet; - std::string path = fname; - ModifyPath(path); - Win32FileLock* _lock = new Win32FileLock(path); - if(!_lock->isEnable()){ - delete _lock; - *lock = NULL; - sRet = Status::IOError(path, "Could not lock file."); - } - else - *lock = _lock; - return sRet; -} - -Status Win32Env::UnlockFile( FileLock* lock ) -{ - Status sRet; - delete lock; - return sRet; -} - -void Win32Env::Schedule( void (*function)(void* arg), void* arg ) -{ - QueueUserWorkItem(Win32::WorkItemWrapperProc, - new Win32::WorkItemWrapper(function,arg), - WT_EXECUTEDEFAULT); -} - -void Win32Env::StartThread( void (*function)(void* arg), void* arg ) -{ - ::_beginthread(function,0,arg); -} - -Status Win32Env::GetTestDirectory( std::string* path ) -{ - Status sRet; - WCHAR TempPath[MAX_PATH]; - ::GetTempPathW(MAX_PATH,TempPath); - ToNarrowPath(TempPath, *path); - path->append("leveldb\\test\\"); - ModifyPath(*path); - return sRet; -} - -uint64_t Win32Env::NowMicros() -{ -#ifndef USE_VISTA_API -#define GetTickCount64 GetTickCount -#endif - return (uint64_t)(GetTickCount64()*1000); -} - -static Status CreateDirInner( const std::string& dirname ) -{ - Status sRet; - DWORD attr = ::GetFileAttributes(dirname.c_str()); - if (attr == INVALID_FILE_ATTRIBUTES) { // doesn't exist: - std::size_t slash = dirname.find_last_of("\\"); - if (slash != std::string::npos){ - sRet = CreateDirInner(dirname.substr(0, slash)); - if (!sRet.ok()) return sRet; - } - BOOL result = ::CreateDirectory(dirname.c_str(), NULL); - if (result == FALSE) { - sRet = Status::IOError(dirname, "Could not create directory."); - return sRet; - } - } - return sRet; -} - -Status Win32Env::CreateDir( const std::string& dirname ) -{ - std::string path = dirname; - if(path[path.length() - 1] != '\\'){ - path += '\\'; - } - ModifyPath(path); - - return CreateDirInner(path); -} - -Status Win32Env::DeleteDir( const std::string& dirname ) -{ - Status sRet; - std::wstring path; - ToWidePath(dirname, path); - ModifyPath(path); - if(!::RemoveDirectoryW( path.c_str() ) ){ - sRet = Status::IOError(dirname, "Could not delete directory."); - } - return sRet; -} - -Status Win32Env::NewSequentialFile( const std::string& fname, SequentialFile** result ) -{ - Status sRet; - std::string path = fname; - ModifyPath(path); - Win32SequentialFile* pFile = new Win32SequentialFile(path); - if(pFile->isEnable()){ - *result = pFile; - }else { - delete pFile; - sRet = Status::IOError(path, Win32::GetLastErrSz()); - } - return sRet; -} - -Status Win32Env::NewRandomAccessFile( const std::string& fname, RandomAccessFile** result ) -{ - Status sRet; - std::string path = fname; - Win32RandomAccessFile* pFile = new Win32RandomAccessFile(ModifyPath(path)); - if(!pFile->isEnable()){ - delete pFile; - *result = NULL; - sRet = Status::IOError(path, Win32::GetLastErrSz()); - }else - *result = pFile; - return sRet; -} - -Status Win32Env::NewLogger( const std::string& fname, Logger** result ) -{ - Status sRet; - std::string path = fname; - Win32WritableFile* pMapFile = new Win32WritableFile(ModifyPath(path)); - if(!pMapFile->isEnable()){ - delete pMapFile; - *result = NULL; - sRet = Status::IOError(path,"could not create a logger."); - }else - *result = new Win32Logger(pMapFile); - return sRet; -} - -Status Win32Env::NewWritableFile( const std::string& fname, WritableFile** result ) -{ - Status sRet; - std::string path = fname; - Win32WritableFile* pFile = new Win32WritableFile(ModifyPath(path)); - if(!pFile->isEnable()){ - *result = NULL; - sRet = Status::IOError(fname,Win32::GetLastErrSz()); - }else - *result = pFile; - return sRet; -} - -Win32Env::Win32Env() -{ - -} - -Win32Env::~Win32Env() -{ - -} - - -} // Win32 namespace - -static port::OnceType once = LEVELDB_ONCE_INIT; -static Env* default_env; -static void InitDefaultEnv() { default_env = new Win32::Win32Env(); } - -Env* Env::Default() { - port::InitOnce(&once, InitDefaultEnv); - return default_env; -} - -} // namespace leveldb - -#endif // defined(LEVELDB_PLATFORM_WINDOWS) diff --git a/src/leveldb/util/env_windows.cc b/src/leveldb/util/env_windows.cc new file mode 100644 index 000000000..aafcdcc3b --- /dev/null +++ b/src/leveldb/util/env_windows.cc @@ -0,0 +1,849 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// Prevent Windows headers from defining min/max macros and instead +// use STL. +#ifndef NOMINMAX +#define NOMINMAX +#endif // ifndef NOMINMAX +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "leveldb/env.h" +#include "leveldb/slice.h" +#include "port/port.h" +#include "port/thread_annotations.h" +#include "util/env_windows_test_helper.h" +#include "util/logging.h" +#include "util/mutexlock.h" +#include "util/windows_logger.h" + +#if defined(DeleteFile) +#undef DeleteFile +#endif // defined(DeleteFile) + +namespace leveldb { + +namespace { + +constexpr const size_t kWritableFileBufferSize = 65536; + +// Up to 1000 mmaps for 64-bit binaries; none for 32-bit. +constexpr int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0; + +// Can be set by by EnvWindowsTestHelper::SetReadOnlyMMapLimit(). +int g_mmap_limit = kDefaultMmapLimit; + +std::string GetWindowsErrorMessage(DWORD error_code) { + std::string message; + char* error_text = nullptr; + // Use MBCS version of FormatMessage to match return value. + size_t error_text_size = ::FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&error_text), 0, nullptr); + if (!error_text) { + return message; + } + message.assign(error_text, error_text_size); + ::LocalFree(error_text); + return message; +} + +Status WindowsError(const std::string& context, DWORD error_code) { + if (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND) + return Status::NotFound(context, GetWindowsErrorMessage(error_code)); + return Status::IOError(context, GetWindowsErrorMessage(error_code)); +} + +class ScopedHandle { + public: + ScopedHandle(HANDLE handle) : handle_(handle) {} + ScopedHandle(const ScopedHandle&) = delete; + ScopedHandle(ScopedHandle&& other) noexcept : handle_(other.Release()) {} + ~ScopedHandle() { Close(); } + + ScopedHandle& operator=(const ScopedHandle&) = delete; + + ScopedHandle& operator=(ScopedHandle&& rhs) noexcept { + if (this != &rhs) handle_ = rhs.Release(); + return *this; + } + + bool Close() { + if (!is_valid()) { + return true; + } + HANDLE h = handle_; + handle_ = INVALID_HANDLE_VALUE; + return ::CloseHandle(h); + } + + bool is_valid() const { + return handle_ != INVALID_HANDLE_VALUE && handle_ != nullptr; + } + + HANDLE get() const { return handle_; } + + HANDLE Release() { + HANDLE h = handle_; + handle_ = INVALID_HANDLE_VALUE; + return h; + } + + private: + HANDLE handle_; +}; + +// Helper class to limit resource usage to avoid exhaustion. +// Currently used to limit read-only file descriptors and mmap file usage +// so that we do not run out of file descriptors or virtual memory, or run into +// kernel performance problems for very large databases. +class Limiter { + public: + // Limit maximum number of resources to |max_acquires|. + Limiter(int max_acquires) : acquires_allowed_(max_acquires) {} + + Limiter(const Limiter&) = delete; + Limiter operator=(const Limiter&) = delete; + + // If another resource is available, acquire it and return true. + // Else return false. + bool Acquire() { + int old_acquires_allowed = + acquires_allowed_.fetch_sub(1, std::memory_order_relaxed); + + if (old_acquires_allowed > 0) return true; + + acquires_allowed_.fetch_add(1, std::memory_order_relaxed); + return false; + } + + // Release a resource acquired by a previous call to Acquire() that returned + // true. + void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); } + + private: + // The number of available resources. + // + // This is a counter and is not tied to the invariants of any other class, so + // it can be operated on safely using std::memory_order_relaxed. + std::atomic acquires_allowed_; +}; + +class WindowsSequentialFile : public SequentialFile { + public: + WindowsSequentialFile(std::string filename, ScopedHandle handle) + : handle_(std::move(handle)), filename_(std::move(filename)) {} + ~WindowsSequentialFile() override {} + + Status Read(size_t n, Slice* result, char* scratch) override { + DWORD bytes_read; + // DWORD is 32-bit, but size_t could technically be larger. However leveldb + // files are limited to leveldb::Options::max_file_size which is clamped to + // 1<<30 or 1 GiB. + assert(n <= std::numeric_limits::max()); + if (!::ReadFile(handle_.get(), scratch, static_cast(n), &bytes_read, + nullptr)) { + return WindowsError(filename_, ::GetLastError()); + } + + *result = Slice(scratch, bytes_read); + return Status::OK(); + } + + Status Skip(uint64_t n) override { + LARGE_INTEGER distance; + distance.QuadPart = n; + if (!::SetFilePointerEx(handle_.get(), distance, nullptr, FILE_CURRENT)) { + return WindowsError(filename_, ::GetLastError()); + } + return Status::OK(); + } + + std::string GetName() const override { return filename_; } + + private: + const ScopedHandle handle_; + const std::string filename_; +}; + +class WindowsRandomAccessFile : public RandomAccessFile { + public: + WindowsRandomAccessFile(std::string filename, ScopedHandle handle) + : handle_(std::move(handle)), filename_(std::move(filename)) {} + + ~WindowsRandomAccessFile() override = default; + + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { + DWORD bytes_read = 0; + OVERLAPPED overlapped = {}; + + overlapped.OffsetHigh = static_cast(offset >> 32); + overlapped.Offset = static_cast(offset); + if (!::ReadFile(handle_.get(), scratch, static_cast(n), &bytes_read, + &overlapped)) { + DWORD error_code = ::GetLastError(); + if (error_code != ERROR_HANDLE_EOF) { + *result = Slice(scratch, 0); + return Status::IOError(filename_, GetWindowsErrorMessage(error_code)); + } + } + + *result = Slice(scratch, bytes_read); + return Status::OK(); + } + + std::string GetName() const override { return filename_; } + + private: + const ScopedHandle handle_; + const std::string filename_; +}; + +class WindowsMmapReadableFile : public RandomAccessFile { + public: + // base[0,length-1] contains the mmapped contents of the file. + WindowsMmapReadableFile(std::string filename, char* mmap_base, size_t length, + Limiter* mmap_limiter) + : mmap_base_(mmap_base), + length_(length), + mmap_limiter_(mmap_limiter), + filename_(std::move(filename)) {} + + ~WindowsMmapReadableFile() override { + ::UnmapViewOfFile(mmap_base_); + mmap_limiter_->Release(); + } + + Status Read(uint64_t offset, size_t n, Slice* result, + char* scratch) const override { + if (offset + n > length_) { + *result = Slice(); + return WindowsError(filename_, ERROR_INVALID_PARAMETER); + } + + *result = Slice(mmap_base_ + offset, n); + return Status::OK(); + } + + std::string GetName() const override { return filename_; } + + private: + char* const mmap_base_; + const size_t length_; + Limiter* const mmap_limiter_; + const std::string filename_; +}; + +class WindowsWritableFile : public WritableFile { + public: + WindowsWritableFile(std::string filename, ScopedHandle handle) + : pos_(0), handle_(std::move(handle)), filename_(std::move(filename)) {} + + ~WindowsWritableFile() override = default; + + Status Append(const Slice& data) override { + size_t write_size = data.size(); + const char* write_data = data.data(); + + // Fit as much as possible into buffer. + size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_); + std::memcpy(buf_ + pos_, write_data, copy_size); + write_data += copy_size; + write_size -= copy_size; + pos_ += copy_size; + if (write_size == 0) { + return Status::OK(); + } + + // Can't fit in buffer, so need to do at least one write. + Status status = FlushBuffer(); + if (!status.ok()) { + return status; + } + + // Small writes go to buffer, large writes are written directly. + if (write_size < kWritableFileBufferSize) { + std::memcpy(buf_, write_data, write_size); + pos_ = write_size; + return Status::OK(); + } + return WriteUnbuffered(write_data, write_size); + } + + Status Close() override { + Status status = FlushBuffer(); + if (!handle_.Close() && status.ok()) { + status = WindowsError(filename_, ::GetLastError()); + } + return status; + } + + Status Flush() override { return FlushBuffer(); } + + Status Sync() override { + // On Windows no need to sync parent directory. Its metadata will be updated + // via the creation of the new file, without an explicit sync. + + Status status = FlushBuffer(); + if (!status.ok()) { + return status; + } + + if (!::FlushFileBuffers(handle_.get())) { + return Status::IOError(filename_, + GetWindowsErrorMessage(::GetLastError())); + } + return Status::OK(); + } + + std::string GetName() const override { return filename_; } + + private: + Status FlushBuffer() { + Status status = WriteUnbuffered(buf_, pos_); + pos_ = 0; + return status; + } + + Status WriteUnbuffered(const char* data, size_t size) { + DWORD bytes_written; + if (!::WriteFile(handle_.get(), data, static_cast(size), + &bytes_written, nullptr)) { + return Status::IOError(filename_, + GetWindowsErrorMessage(::GetLastError())); + } + return Status::OK(); + } + + // buf_[0, pos_-1] contains data to be written to handle_. + char buf_[kWritableFileBufferSize]; + size_t pos_; + + ScopedHandle handle_; + const std::string filename_; +}; + +// Lock or unlock the entire file as specified by |lock|. Returns true +// when successful, false upon failure. Caller should call ::GetLastError() +// to determine cause of failure +bool LockOrUnlock(HANDLE handle, bool lock) { + if (lock) { + return ::LockFile(handle, + /*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0, + /*nNumberOfBytesToLockLow=*/MAXDWORD, + /*nNumberOfBytesToLockHigh=*/MAXDWORD); + } else { + return ::UnlockFile(handle, + /*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0, + /*nNumberOfBytesToLockLow=*/MAXDWORD, + /*nNumberOfBytesToLockHigh=*/MAXDWORD); + } +} + +class WindowsFileLock : public FileLock { + public: + WindowsFileLock(ScopedHandle handle, std::string filename) + : handle_(std::move(handle)), filename_(std::move(filename)) {} + + const ScopedHandle& handle() const { return handle_; } + const std::string& filename() const { return filename_; } + + private: + const ScopedHandle handle_; + const std::string filename_; +}; + +class WindowsEnv : public Env { + public: + WindowsEnv(); + ~WindowsEnv() override { + static const char msg[] = + "WindowsEnv singleton destroyed. Unsupported behavior!\n"; + std::fwrite(msg, 1, sizeof(msg), stderr); + std::abort(); + } + + Status NewSequentialFile(const std::string& filename, + SequentialFile** result) override { + *result = nullptr; + DWORD desired_access = GENERIC_READ; + DWORD share_mode = FILE_SHARE_READ; + auto wFilename = toUtf16(filename); + ScopedHandle handle = ::CreateFileW( + wFilename.c_str(), desired_access, share_mode, + /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + /*hTemplateFile=*/nullptr); + if (!handle.is_valid()) { + return WindowsError(filename, ::GetLastError()); + } + + *result = new WindowsSequentialFile(filename, std::move(handle)); + return Status::OK(); + } + + Status NewRandomAccessFile(const std::string& filename, + RandomAccessFile** result) override { + *result = nullptr; + DWORD desired_access = GENERIC_READ; + DWORD share_mode = FILE_SHARE_READ; + auto wFilename = toUtf16(filename); + ScopedHandle handle = + ::CreateFileW(wFilename.c_str(), desired_access, share_mode, + /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY, + /*hTemplateFile=*/nullptr); + if (!handle.is_valid()) { + return WindowsError(filename, ::GetLastError()); + } + if (!mmap_limiter_.Acquire()) { + *result = new WindowsRandomAccessFile(filename, std::move(handle)); + return Status::OK(); + } + + LARGE_INTEGER file_size; + Status status; + if (!::GetFileSizeEx(handle.get(), &file_size)) { + mmap_limiter_.Release(); + return WindowsError(filename, ::GetLastError()); + } + + ScopedHandle mapping = + ::CreateFileMappingW(handle.get(), + /*security attributes=*/nullptr, PAGE_READONLY, + /*dwMaximumSizeHigh=*/0, + /*dwMaximumSizeLow=*/0, + /*lpName=*/nullptr); + if (mapping.is_valid()) { + void* mmap_base = ::MapViewOfFile(mapping.get(), FILE_MAP_READ, + /*dwFileOffsetHigh=*/0, + /*dwFileOffsetLow=*/0, + /*dwNumberOfBytesToMap=*/0); + if (mmap_base) { + *result = new WindowsMmapReadableFile( + filename, reinterpret_cast(mmap_base), + static_cast(file_size.QuadPart), &mmap_limiter_); + return Status::OK(); + } + } + mmap_limiter_.Release(); + return WindowsError(filename, ::GetLastError()); + } + + Status NewWritableFile(const std::string& filename, + WritableFile** result) override { + DWORD desired_access = GENERIC_WRITE; + DWORD share_mode = 0; // Exclusive access. + auto wFilename = toUtf16(filename); + ScopedHandle handle = ::CreateFileW( + wFilename.c_str(), desired_access, share_mode, + /*lpSecurityAttributes=*/nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + /*hTemplateFile=*/nullptr); + if (!handle.is_valid()) { + *result = nullptr; + return WindowsError(filename, ::GetLastError()); + } + + *result = new WindowsWritableFile(filename, std::move(handle)); + return Status::OK(); + } + + Status NewAppendableFile(const std::string& filename, + WritableFile** result) override { + DWORD desired_access = FILE_APPEND_DATA; + DWORD share_mode = 0; // Exclusive access. + auto wFilename = toUtf16(filename); + ScopedHandle handle = ::CreateFileW( + wFilename.c_str(), desired_access, share_mode, + /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, + /*hTemplateFile=*/nullptr); + if (!handle.is_valid()) { + *result = nullptr; + return WindowsError(filename, ::GetLastError()); + } + + *result = new WindowsWritableFile(filename, std::move(handle)); + return Status::OK(); + } + + bool FileExists(const std::string& filename) override { + auto wFilename = toUtf16(filename); + return GetFileAttributesW(wFilename.c_str()) != INVALID_FILE_ATTRIBUTES; + } + + Status GetChildren(const std::string& directory_path, + std::vector* result) override { + const std::string find_pattern = directory_path + "\\*"; + WIN32_FIND_DATAW find_data; + auto wFind_pattern = toUtf16(find_pattern); + HANDLE dir_handle = ::FindFirstFileW(wFind_pattern.c_str(), &find_data); + if (dir_handle == INVALID_HANDLE_VALUE) { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_FILE_NOT_FOUND) { + return Status::OK(); + } + return WindowsError(directory_path, last_error); + } + do { + char base_name[_MAX_FNAME]; + char ext[_MAX_EXT]; + + auto find_data_filename = toUtf8(find_data.cFileName); + if (!_splitpath_s(find_data_filename.c_str(), nullptr, 0, nullptr, 0, + base_name, ARRAYSIZE(base_name), ext, ARRAYSIZE(ext))) { + result->emplace_back(std::string(base_name) + ext); + } + } while (::FindNextFileW(dir_handle, &find_data)); + DWORD last_error = ::GetLastError(); + ::FindClose(dir_handle); + if (last_error != ERROR_NO_MORE_FILES) { + return WindowsError(directory_path, last_error); + } + return Status::OK(); + } + + Status DeleteFile(const std::string& filename) override { + auto wFilename = toUtf16(filename); + if (!::DeleteFileW(wFilename.c_str())) { + return WindowsError(filename, ::GetLastError()); + } + return Status::OK(); + } + + Status CreateDir(const std::string& dirname) override { + auto wDirname = toUtf16(dirname); + if (!::CreateDirectoryW(wDirname.c_str(), nullptr)) { + return WindowsError(dirname, ::GetLastError()); + } + return Status::OK(); + } + + Status DeleteDir(const std::string& dirname) override { + auto wDirname = toUtf16(dirname); + if (!::RemoveDirectoryW(wDirname.c_str())) { + return WindowsError(dirname, ::GetLastError()); + } + return Status::OK(); + } + + Status GetFileSize(const std::string& filename, uint64_t* size) override { + WIN32_FILE_ATTRIBUTE_DATA file_attributes; + auto wFilename = toUtf16(filename); + if (!::GetFileAttributesExW(wFilename.c_str(), GetFileExInfoStandard, + &file_attributes)) { + return WindowsError(filename, ::GetLastError()); + } + ULARGE_INTEGER file_size; + file_size.HighPart = file_attributes.nFileSizeHigh; + file_size.LowPart = file_attributes.nFileSizeLow; + *size = file_size.QuadPart; + return Status::OK(); + } + + Status RenameFile(const std::string& from, const std::string& to) override { + // Try a simple move first. It will only succeed when |to| doesn't already + // exist. + auto wFrom = toUtf16(from); + auto wTo = toUtf16(to); + if (::MoveFileW(wFrom.c_str(), wTo.c_str())) { + return Status::OK(); + } + DWORD move_error = ::GetLastError(); + + // Try the full-blown replace if the move fails, as ReplaceFile will only + // succeed when |to| does exist. When writing to a network share, we may not + // be able to change the ACLs. Ignore ACL errors then + // (REPLACEFILE_IGNORE_MERGE_ERRORS). + if (::ReplaceFileW(wTo.c_str(), wFrom.c_str(), /*lpBackupFileName=*/nullptr, + REPLACEFILE_IGNORE_MERGE_ERRORS, + /*lpExclude=*/nullptr, /*lpReserved=*/nullptr)) { + return Status::OK(); + } + DWORD replace_error = ::GetLastError(); + // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that + // |to| does not exist. In this case, the more relevant error comes from the + // call to MoveFile. + if (replace_error == ERROR_FILE_NOT_FOUND || + replace_error == ERROR_PATH_NOT_FOUND) { + return WindowsError(from, move_error); + } else { + return WindowsError(from, replace_error); + } + } + + Status LockFile(const std::string& filename, FileLock** lock) override { + *lock = nullptr; + Status result; + auto wFilename = toUtf16(filename); + ScopedHandle handle = ::CreateFileW( + wFilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, + /*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, + nullptr); + if (!handle.is_valid()) { + result = WindowsError(filename, ::GetLastError()); + } else if (!LockOrUnlock(handle.get(), true)) { + result = WindowsError("lock " + filename, ::GetLastError()); + } else { + *lock = new WindowsFileLock(std::move(handle), filename); + } + return result; + } + + Status UnlockFile(FileLock* lock) override { + WindowsFileLock* windows_file_lock = + reinterpret_cast(lock); + if (!LockOrUnlock(windows_file_lock->handle().get(), false)) { + return WindowsError("unlock " + windows_file_lock->filename(), + ::GetLastError()); + } + delete windows_file_lock; + return Status::OK(); + } + + void Schedule(void (*background_work_function)(void* background_work_arg), + void* background_work_arg) override; + + void StartThread(void (*thread_main)(void* thread_main_arg), + void* thread_main_arg) override { + std::thread new_thread(thread_main, thread_main_arg); + new_thread.detach(); + } + + Status GetTestDirectory(std::string* result) override { + const char* env = getenv("TEST_TMPDIR"); + if (env && env[0] != '\0') { + *result = env; + return Status::OK(); + } + + wchar_t wtmp_path[MAX_PATH]; + if (!GetTempPathW(ARRAYSIZE(wtmp_path), wtmp_path)) { + return WindowsError("GetTempPath", ::GetLastError()); + } + std::string tmp_path = toUtf8(std::wstring(wtmp_path)); + std::stringstream ss; + ss << tmp_path << "leveldbtest-" << std::this_thread::get_id(); + *result = ss.str(); + + // Directory may already exist + CreateDir(*result); + return Status::OK(); + } + + Status NewLogger(const std::string& filename, Logger** result) override { + auto wFilename = toUtf16(filename); + std::FILE* fp = _wfopen(wFilename.c_str(), L"w"); + if (fp == nullptr) { + *result = nullptr; + return WindowsError(filename, ::GetLastError()); + } else { + *result = new WindowsLogger(fp); + return Status::OK(); + } + } + + uint64_t NowMicros() override { + // GetSystemTimeAsFileTime typically has a resolution of 10-20 msec. + // TODO(cmumford): Switch to GetSystemTimePreciseAsFileTime which is + // available in Windows 8 and later. + FILETIME ft; + ::GetSystemTimeAsFileTime(&ft); + // Each tick represents a 100-nanosecond intervals since January 1, 1601 + // (UTC). + uint64_t num_ticks = + (static_cast(ft.dwHighDateTime) << 32) + ft.dwLowDateTime; + return num_ticks / 10; + } + + void SleepForMicroseconds(int micros) override { + std::this_thread::sleep_for(std::chrono::microseconds(micros)); + } + + private: + void BackgroundThreadMain(); + + static void BackgroundThreadEntryPoint(WindowsEnv* env) { + env->BackgroundThreadMain(); + } + + // Stores the work item data in a Schedule() call. + // + // Instances are constructed on the thread calling Schedule() and used on the + // background thread. + // + // This structure is thread-safe beacuse it is immutable. + struct BackgroundWorkItem { + explicit BackgroundWorkItem(void (*function)(void* arg), void* arg) + : function(function), arg(arg) {} + + void (*const function)(void*); + void* const arg; + }; + + port::Mutex background_work_mutex_; + port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_); + bool started_background_thread_ GUARDED_BY(background_work_mutex_); + + std::queue background_work_queue_ + GUARDED_BY(background_work_mutex_); + + Limiter mmap_limiter_; // Thread-safe. + + // Converts a Windows wide multi-byte UTF-16 string to a UTF-8 string. + // See http://utf8everywhere.org/#windows + std::string toUtf8(const std::wstring& wstr) { + if (wstr.empty()) return std::string(); + int size_needed = WideCharToMultiByte( + CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); + std::string strTo(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], + size_needed, NULL, NULL); + return strTo; + } + + // Converts a UTF-8 string to a Windows UTF-16 multi-byte wide character + // string. + // See http://utf8everywhere.org/#windows + std::wstring toUtf16(const std::string& str) { + if (str.empty()) return std::wstring(); + int size_needed = + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); + std::wstring strTo(size_needed, 0); + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &strTo[0], + size_needed); + return strTo; + } +}; + +// Return the maximum number of concurrent mmaps. +int MaxMmaps() { return g_mmap_limit; } + +WindowsEnv::WindowsEnv() + : background_work_cv_(&background_work_mutex_), + started_background_thread_(false), + mmap_limiter_(MaxMmaps()) {} + +void WindowsEnv::Schedule( + void (*background_work_function)(void* background_work_arg), + void* background_work_arg) { + background_work_mutex_.Lock(); + + // Start the background thread, if we haven't done so already. + if (!started_background_thread_) { + started_background_thread_ = true; + std::thread background_thread(WindowsEnv::BackgroundThreadEntryPoint, this); + background_thread.detach(); + } + + // If the queue is empty, the background thread may be waiting for work. + if (background_work_queue_.empty()) { + background_work_cv_.Signal(); + } + + background_work_queue_.emplace(background_work_function, background_work_arg); + background_work_mutex_.Unlock(); +} + +void WindowsEnv::BackgroundThreadMain() { + while (true) { + background_work_mutex_.Lock(); + + // Wait until there is work to be done. + while (background_work_queue_.empty()) { + background_work_cv_.Wait(); + } + + assert(!background_work_queue_.empty()); + auto background_work_function = background_work_queue_.front().function; + void* background_work_arg = background_work_queue_.front().arg; + background_work_queue_.pop(); + + background_work_mutex_.Unlock(); + background_work_function(background_work_arg); + } +} + +// Wraps an Env instance whose destructor is never created. +// +// Intended usage: +// using PlatformSingletonEnv = SingletonEnv; +// void ConfigurePosixEnv(int param) { +// PlatformSingletonEnv::AssertEnvNotInitialized(); +// // set global configuration flags. +// } +// Env* Env::Default() { +// static PlatformSingletonEnv default_env; +// return default_env.env(); +// } +template +class SingletonEnv { + public: + SingletonEnv() { +#if !defined(NDEBUG) + env_initialized_.store(true, std::memory_order_relaxed); +#endif // !defined(NDEBUG) + static_assert(sizeof(env_storage_) >= sizeof(EnvType), + "env_storage_ will not fit the Env"); + static_assert(alignof(decltype(env_storage_)) >= alignof(EnvType), + "env_storage_ does not meet the Env's alignment needs"); + new (&env_storage_) EnvType(); + } + ~SingletonEnv() = default; + + SingletonEnv(const SingletonEnv&) = delete; + SingletonEnv& operator=(const SingletonEnv&) = delete; + + Env* env() { return reinterpret_cast(&env_storage_); } + + static void AssertEnvNotInitialized() { +#if !defined(NDEBUG) + assert(!env_initialized_.load(std::memory_order_relaxed)); +#endif // !defined(NDEBUG) + } + + private: + typename std::aligned_storage::type + env_storage_; +#if !defined(NDEBUG) + static std::atomic env_initialized_; +#endif // !defined(NDEBUG) +}; + +#if !defined(NDEBUG) +template +std::atomic SingletonEnv::env_initialized_; +#endif // !defined(NDEBUG) + +using WindowsDefaultEnv = SingletonEnv; + +} // namespace + +void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) { + WindowsDefaultEnv::AssertEnvNotInitialized(); + g_mmap_limit = limit; +} + +Env* Env::Default() { + static WindowsDefaultEnv env_container; + return env_container.env(); +} + +} // namespace leveldb diff --git a/src/leveldb/util/env_windows_test.cc b/src/leveldb/util/env_windows_test.cc new file mode 100644 index 000000000..3c2213389 --- /dev/null +++ b/src/leveldb/util/env_windows_test.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "leveldb/env.h" + +#include "port/port.h" +#include "util/env_windows_test_helper.h" +#include "util/testharness.h" + +namespace leveldb { + +static const int kMMapLimit = 4; + +class EnvWindowsTest { + public: + static void SetFileLimits(int mmap_limit) { + EnvWindowsTestHelper::SetReadOnlyMMapLimit(mmap_limit); + } + + EnvWindowsTest() : env_(Env::Default()) {} + + Env* env_; +}; + +TEST(EnvWindowsTest, TestOpenOnRead) { + // Write some test data to a single file that will be opened |n| times. + std::string test_dir; + ASSERT_OK(env_->GetTestDirectory(&test_dir)); + std::string test_file = test_dir + "/open_on_read.txt"; + + FILE* f = fopen(test_file.c_str(), "w"); + ASSERT_TRUE(f != nullptr); + const char kFileData[] = "abcdefghijklmnopqrstuvwxyz"; + fputs(kFileData, f); + fclose(f); + + // Open test file some number above the sum of the two limits to force + // leveldb::WindowsEnv to switch from mapping the file into memory + // to basic file reading. + const int kNumFiles = kMMapLimit + 5; + leveldb::RandomAccessFile* files[kNumFiles] = {0}; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i])); + } + char scratch; + Slice read_result; + for (int i = 0; i < kNumFiles; i++) { + ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch)); + ASSERT_EQ(kFileData[i], read_result[0]); + } + for (int i = 0; i < kNumFiles; i++) { + delete files[i]; + } + ASSERT_OK(env_->DeleteFile(test_file)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { + // All tests currently run with the same read-only file limits. + leveldb::EnvWindowsTest::SetFileLimits(leveldb::kMMapLimit); + return leveldb::test::RunAllTests(); +} diff --git a/src/leveldb/util/env_windows_test_helper.h b/src/leveldb/util/env_windows_test_helper.h new file mode 100644 index 000000000..e6f602056 --- /dev/null +++ b/src/leveldb/util/env_windows_test_helper.h @@ -0,0 +1,25 @@ +// Copyright 2018 (c) The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_ENV_WINDOWS_TEST_HELPER_H_ +#define STORAGE_LEVELDB_UTIL_ENV_WINDOWS_TEST_HELPER_H_ + +namespace leveldb { + +class EnvWindowsTest; + +// A helper for the Windows Env to facilitate testing. +class EnvWindowsTestHelper { + private: + friend class CorruptionTest; + friend class EnvWindowsTest; + + // Set the maximum number of read-only files that will be mapped via mmap. + // Must be called before creating an Env. + static void SetReadOnlyMMapLimit(int limit); +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_ENV_WINDOWS_TEST_HELPER_H_ diff --git a/src/leveldb/util/filter_policy.cc b/src/leveldb/util/filter_policy.cc index 7b045c8c9..90fd754d6 100644 --- a/src/leveldb/util/filter_policy.cc +++ b/src/leveldb/util/filter_policy.cc @@ -6,6 +6,6 @@ namespace leveldb { -FilterPolicy::~FilterPolicy() { } +FilterPolicy::~FilterPolicy() {} } // namespace leveldb diff --git a/src/leveldb/util/hash.cc b/src/leveldb/util/hash.cc index ed439ce7a..dd47c110e 100644 --- a/src/leveldb/util/hash.cc +++ b/src/leveldb/util/hash.cc @@ -2,15 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "util/hash.h" + #include + #include "util/coding.h" -#include "util/hash.h" // The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through // between switch labels. The real definition should be provided externally. // This one is a fallback version for unsupported compilers. #ifndef FALLTHROUGH_INTENDED -#define FALLTHROUGH_INTENDED do { } while (0) +#define FALLTHROUGH_INTENDED \ + do { \ + } while (0) #endif namespace leveldb { @@ -34,13 +38,13 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) { // Pick up remaining bytes switch (limit - data) { case 3: - h += static_cast(data[2]) << 16; + h += static_cast(data[2]) << 16; FALLTHROUGH_INTENDED; case 2: - h += static_cast(data[1]) << 8; + h += static_cast(data[1]) << 8; FALLTHROUGH_INTENDED; case 1: - h += static_cast(data[0]); + h += static_cast(data[0]); h *= m; h ^= (h >> r); break; @@ -48,5 +52,4 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) { return h; } - } // namespace leveldb diff --git a/src/leveldb/util/hash.h b/src/leveldb/util/hash.h index 8889d56be..74bdb6e7b 100644 --- a/src/leveldb/util/hash.h +++ b/src/leveldb/util/hash.h @@ -12,8 +12,8 @@ namespace leveldb { -extern uint32_t Hash(const char* data, size_t n, uint32_t seed); +uint32_t Hash(const char* data, size_t n, uint32_t seed); -} +} // namespace leveldb #endif // STORAGE_LEVELDB_UTIL_HASH_H_ diff --git a/src/leveldb/util/hash_test.cc b/src/leveldb/util/hash_test.cc index eaa1c92c2..21f8171da 100644 --- a/src/leveldb/util/hash_test.cc +++ b/src/leveldb/util/hash_test.cc @@ -7,26 +7,18 @@ namespace leveldb { -class HASH { }; +class HASH {}; TEST(HASH, SignedUnsignedIssue) { - const unsigned char data1[1] = {0x62}; - const unsigned char data2[2] = {0xc3, 0x97}; - const unsigned char data3[3] = {0xe2, 0x99, 0xa5}; - const unsigned char data4[4] = {0xe1, 0x80, 0xb9, 0x32}; - const unsigned char data5[48] = { - 0x01, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x18, - 0x28, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, + const uint8_t data1[1] = {0x62}; + const uint8_t data2[2] = {0xc3, 0x97}; + const uint8_t data3[3] = {0xe2, 0x99, 0xa5}; + const uint8_t data4[4] = {0xe1, 0x80, 0xb9, 0x32}; + const uint8_t data5[48] = { + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; ASSERT_EQ(Hash(0, 0, 0xbc9f1d34), 0xbc9f1d34); @@ -49,6 +41,4 @@ TEST(HASH, SignedUnsignedIssue) { } // namespace leveldb -int main(int argc, char** argv) { - return leveldb::test::RunAllTests(); -} +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/histogram.cc b/src/leveldb/util/histogram.cc index bb95f583e..65092c88f 100644 --- a/src/leveldb/util/histogram.cc +++ b/src/leveldb/util/histogram.cc @@ -2,36 +2,174 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "util/histogram.h" + #include #include + #include "port/port.h" -#include "util/histogram.h" namespace leveldb { const double Histogram::kBucketLimit[kNumBuckets] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, - 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, - 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000, - 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000, - 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000, - 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000, - 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000, - 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000, - 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000, - 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000, - 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000, - 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000, - 180000000, 200000000, 250000000, 300000000, 350000000, 400000000, - 450000000, 500000000, 600000000, 700000000, 800000000, 900000000, - 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000, - 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0, - 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, - 1e200, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 12, + 14, + 16, + 18, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 60, + 70, + 80, + 90, + 100, + 120, + 140, + 160, + 180, + 200, + 250, + 300, + 350, + 400, + 450, + 500, + 600, + 700, + 800, + 900, + 1000, + 1200, + 1400, + 1600, + 1800, + 2000, + 2500, + 3000, + 3500, + 4000, + 4500, + 5000, + 6000, + 7000, + 8000, + 9000, + 10000, + 12000, + 14000, + 16000, + 18000, + 20000, + 25000, + 30000, + 35000, + 40000, + 45000, + 50000, + 60000, + 70000, + 80000, + 90000, + 100000, + 120000, + 140000, + 160000, + 180000, + 200000, + 250000, + 300000, + 350000, + 400000, + 450000, + 500000, + 600000, + 700000, + 800000, + 900000, + 1000000, + 1200000, + 1400000, + 1600000, + 1800000, + 2000000, + 2500000, + 3000000, + 3500000, + 4000000, + 4500000, + 5000000, + 6000000, + 7000000, + 8000000, + 9000000, + 10000000, + 12000000, + 14000000, + 16000000, + 18000000, + 20000000, + 25000000, + 30000000, + 35000000, + 40000000, + 45000000, + 50000000, + 60000000, + 70000000, + 80000000, + 90000000, + 100000000, + 120000000, + 140000000, + 160000000, + 180000000, + 200000000, + 250000000, + 300000000, + 350000000, + 400000000, + 450000000, + 500000000, + 600000000, + 700000000, + 800000000, + 900000000, + 1000000000, + 1200000000, + 1400000000, + 1600000000, + 1800000000, + 2000000000, + 2500000000.0, + 3000000000.0, + 3500000000.0, + 4000000000.0, + 4500000000.0, + 5000000000.0, + 6000000000.0, + 7000000000.0, + 8000000000.0, + 9000000000.0, + 1e200, }; void Histogram::Clear() { - min_ = kBucketLimit[kNumBuckets-1]; + min_ = kBucketLimit[kNumBuckets - 1]; max_ = 0; num_ = 0; sum_ = 0; @@ -66,9 +204,7 @@ void Histogram::Merge(const Histogram& other) { } } -double Histogram::Median() const { - return Percentile(50.0); -} +double Histogram::Median() const { return Percentile(50.0); } double Histogram::Percentile(double p) const { double threshold = num_ * (p / 100.0); @@ -77,7 +213,7 @@ double Histogram::Percentile(double p) const { sum += buckets_[b]; if (sum >= threshold) { // Scale linearly within this bucket - double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; + double left_point = (b == 0) ? 0 : kBucketLimit[b - 1]; double right_point = kBucketLimit[b]; double left_sum = sum - buckets_[b]; double right_sum = sum; @@ -105,12 +241,10 @@ double Histogram::StandardDeviation() const { std::string Histogram::ToString() const { std::string r; char buf[200]; - snprintf(buf, sizeof(buf), - "Count: %.0f Average: %.4f StdDev: %.2f\n", - num_, Average(), StandardDeviation()); + snprintf(buf, sizeof(buf), "Count: %.0f Average: %.4f StdDev: %.2f\n", num_, + Average(), StandardDeviation()); r.append(buf); - snprintf(buf, sizeof(buf), - "Min: %.4f Median: %.4f Max: %.4f\n", + snprintf(buf, sizeof(buf), "Min: %.4f Median: %.4f Max: %.4f\n", (num_ == 0.0 ? 0.0 : min_), Median(), max_); r.append(buf); r.append("------------------------------------------------------\n"); @@ -119,17 +253,16 @@ std::string Histogram::ToString() const { for (int b = 0; b < kNumBuckets; b++) { if (buckets_[b] <= 0.0) continue; sum += buckets_[b]; - snprintf(buf, sizeof(buf), - "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", - ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left - kBucketLimit[b], // right - buckets_[b], // count - mult * buckets_[b], // percentage - mult * sum); // cumulative percentage + snprintf(buf, sizeof(buf), "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", + ((b == 0) ? 0.0 : kBucketLimit[b - 1]), // left + kBucketLimit[b], // right + buckets_[b], // count + mult * buckets_[b], // percentage + mult * sum); // cumulative percentage r.append(buf); // Add hash marks based on percentage; 20 marks for 100%. - int marks = static_cast(20*(buckets_[b] / num_) + 0.5); + int marks = static_cast(20 * (buckets_[b] / num_) + 0.5); r.append(marks, '#'); r.push_back('\n'); } diff --git a/src/leveldb/util/histogram.h b/src/leveldb/util/histogram.h index 1ef9f3c8a..4da60fba4 100644 --- a/src/leveldb/util/histogram.h +++ b/src/leveldb/util/histogram.h @@ -11,8 +11,8 @@ namespace leveldb { class Histogram { public: - Histogram() { } - ~Histogram() { } + Histogram() {} + ~Histogram() {} void Clear(); void Add(double value); @@ -21,20 +21,22 @@ class Histogram { std::string ToString() const; private: + enum { kNumBuckets = 154 }; + + double Median() const; + double Percentile(double p) const; + double Average() const; + double StandardDeviation() const; + + static const double kBucketLimit[kNumBuckets]; + double min_; double max_; double num_; double sum_; double sum_squares_; - enum { kNumBuckets = 154 }; - static const double kBucketLimit[kNumBuckets]; double buckets_[kNumBuckets]; - - double Median() const; - double Percentile(double p) const; - double Average() const; - double StandardDeviation() const; }; } // namespace leveldb diff --git a/src/leveldb/util/logging.cc b/src/leveldb/util/logging.cc index ca6b32440..75e9d037d 100644 --- a/src/leveldb/util/logging.cc +++ b/src/leveldb/util/logging.cc @@ -8,6 +8,9 @@ #include #include #include + +#include + #include "leveldb/env.h" #include "leveldb/slice.h" @@ -15,7 +18,7 @@ namespace leveldb { void AppendNumberTo(std::string* str, uint64_t num) { char buf[30]; - snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num); + snprintf(buf, sizeof(buf), "%llu", (unsigned long long)num); str->append(buf); } @@ -46,27 +49,36 @@ std::string EscapeString(const Slice& value) { } bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { - uint64_t v = 0; - int digits = 0; - while (!in->empty()) { - char c = (*in)[0]; - if (c >= '0' && c <= '9') { - ++digits; - const int delta = (c - '0'); - static const uint64_t kMaxUint64 = ~static_cast(0); - if (v > kMaxUint64/10 || - (v == kMaxUint64/10 && delta > kMaxUint64%10)) { - // Overflow - return false; - } - v = (v * 10) + delta; - in->remove_prefix(1); - } else { - break; + // Constants that will be optimized away. + constexpr const uint64_t kMaxUint64 = std::numeric_limits::max(); + constexpr const char kLastDigitOfMaxUint64 = + '0' + static_cast(kMaxUint64 % 10); + + uint64_t value = 0; + + // reinterpret_cast-ing from char* to uint8_t* to avoid signedness. + const uint8_t* start = reinterpret_cast(in->data()); + + const uint8_t* end = start + in->size(); + const uint8_t* current = start; + for (; current != end; ++current) { + const uint8_t ch = *current; + if (ch < '0' || ch > '9') break; + + // Overflow check. + // kMaxUint64 / 10 is also constant and will be optimized away. + if (value > kMaxUint64 / 10 || + (value == kMaxUint64 / 10 && ch > kLastDigitOfMaxUint64)) { + return false; } + + value = (value * 10) + (ch - '0'); } - *val = v; - return (digits > 0); + + *val = value; + const size_t digits_consumed = current - start; + in->remove_prefix(digits_consumed); + return digits_consumed != 0; } } // namespace leveldb diff --git a/src/leveldb/util/logging.h b/src/leveldb/util/logging.h index 1b450d248..8ff2da86b 100644 --- a/src/leveldb/util/logging.h +++ b/src/leveldb/util/logging.h @@ -8,9 +8,11 @@ #ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_ #define STORAGE_LEVELDB_UTIL_LOGGING_H_ -#include #include +#include + #include + #include "port/port.h" namespace leveldb { @@ -19,24 +21,24 @@ class Slice; class WritableFile; // Append a human-readable printout of "num" to *str -extern void AppendNumberTo(std::string* str, uint64_t num); +void AppendNumberTo(std::string* str, uint64_t num); // Append a human-readable printout of "value" to *str. // Escapes any non-printable characters found in "value". -extern void AppendEscapedStringTo(std::string* str, const Slice& value); +void AppendEscapedStringTo(std::string* str, const Slice& value); // Return a human-readable printout of "num" -extern std::string NumberToString(uint64_t num); +std::string NumberToString(uint64_t num); // Return a human-readable version of "value". // Escapes any non-printable characters found in "value". -extern std::string EscapeString(const Slice& value); +std::string EscapeString(const Slice& value); // Parse a human-readable number from "*in" into *value. On success, // advances "*in" past the consumed number and sets "*val" to the // numeric value. Otherwise, returns false and leaves *in in an // unspecified state. -extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val); +bool ConsumeDecimalNumber(Slice* in, uint64_t* val); } // namespace leveldb diff --git a/src/leveldb/util/logging_test.cc b/src/leveldb/util/logging_test.cc new file mode 100644 index 000000000..389cbeb14 --- /dev/null +++ b/src/leveldb/util/logging_test.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include + +#include "leveldb/slice.h" +#include "util/logging.h" +#include "util/testharness.h" + +namespace leveldb { + +class Logging {}; + +TEST(Logging, NumberToString) { + ASSERT_EQ("0", NumberToString(0)); + ASSERT_EQ("1", NumberToString(1)); + ASSERT_EQ("9", NumberToString(9)); + + ASSERT_EQ("10", NumberToString(10)); + ASSERT_EQ("11", NumberToString(11)); + ASSERT_EQ("19", NumberToString(19)); + ASSERT_EQ("99", NumberToString(99)); + + ASSERT_EQ("100", NumberToString(100)); + ASSERT_EQ("109", NumberToString(109)); + ASSERT_EQ("190", NumberToString(190)); + ASSERT_EQ("123", NumberToString(123)); + ASSERT_EQ("12345678", NumberToString(12345678)); + + static_assert(std::numeric_limits::max() == 18446744073709551615U, + "Test consistency check"); + ASSERT_EQ("18446744073709551000", NumberToString(18446744073709551000U)); + ASSERT_EQ("18446744073709551600", NumberToString(18446744073709551600U)); + ASSERT_EQ("18446744073709551610", NumberToString(18446744073709551610U)); + ASSERT_EQ("18446744073709551614", NumberToString(18446744073709551614U)); + ASSERT_EQ("18446744073709551615", NumberToString(18446744073709551615U)); +} + +void ConsumeDecimalNumberRoundtripTest(uint64_t number, + const std::string& padding = "") { + std::string decimal_number = NumberToString(number); + std::string input_string = decimal_number + padding; + Slice input(input_string); + Slice output = input; + uint64_t result; + ASSERT_TRUE(ConsumeDecimalNumber(&output, &result)); + ASSERT_EQ(number, result); + ASSERT_EQ(decimal_number.size(), output.data() - input.data()); + ASSERT_EQ(padding.size(), output.size()); +} + +TEST(Logging, ConsumeDecimalNumberRoundtrip) { + ConsumeDecimalNumberRoundtripTest(0); + ConsumeDecimalNumberRoundtripTest(1); + ConsumeDecimalNumberRoundtripTest(9); + + ConsumeDecimalNumberRoundtripTest(10); + ConsumeDecimalNumberRoundtripTest(11); + ConsumeDecimalNumberRoundtripTest(19); + ConsumeDecimalNumberRoundtripTest(99); + + ConsumeDecimalNumberRoundtripTest(100); + ConsumeDecimalNumberRoundtripTest(109); + ConsumeDecimalNumberRoundtripTest(190); + ConsumeDecimalNumberRoundtripTest(123); + ASSERT_EQ("12345678", NumberToString(12345678)); + + for (uint64_t i = 0; i < 100; ++i) { + uint64_t large_number = std::numeric_limits::max() - i; + ConsumeDecimalNumberRoundtripTest(large_number); + } +} + +TEST(Logging, ConsumeDecimalNumberRoundtripWithPadding) { + ConsumeDecimalNumberRoundtripTest(0, " "); + ConsumeDecimalNumberRoundtripTest(1, "abc"); + ConsumeDecimalNumberRoundtripTest(9, "x"); + + ConsumeDecimalNumberRoundtripTest(10, "_"); + ConsumeDecimalNumberRoundtripTest(11, std::string("\0\0\0", 3)); + ConsumeDecimalNumberRoundtripTest(19, "abc"); + ConsumeDecimalNumberRoundtripTest(99, "padding"); + + ConsumeDecimalNumberRoundtripTest(100, " "); + + for (uint64_t i = 0; i < 100; ++i) { + uint64_t large_number = std::numeric_limits::max() - i; + ConsumeDecimalNumberRoundtripTest(large_number, "pad"); + } +} + +void ConsumeDecimalNumberOverflowTest(const std::string& input_string) { + Slice input(input_string); + Slice output = input; + uint64_t result; + ASSERT_EQ(false, ConsumeDecimalNumber(&output, &result)); +} + +TEST(Logging, ConsumeDecimalNumberOverflow) { + static_assert(std::numeric_limits::max() == 18446744073709551615U, + "Test consistency check"); + ConsumeDecimalNumberOverflowTest("18446744073709551616"); + ConsumeDecimalNumberOverflowTest("18446744073709551617"); + ConsumeDecimalNumberOverflowTest("18446744073709551618"); + ConsumeDecimalNumberOverflowTest("18446744073709551619"); + ConsumeDecimalNumberOverflowTest("18446744073709551620"); + ConsumeDecimalNumberOverflowTest("18446744073709551621"); + ConsumeDecimalNumberOverflowTest("18446744073709551622"); + ConsumeDecimalNumberOverflowTest("18446744073709551623"); + ConsumeDecimalNumberOverflowTest("18446744073709551624"); + ConsumeDecimalNumberOverflowTest("18446744073709551625"); + ConsumeDecimalNumberOverflowTest("18446744073709551626"); + + ConsumeDecimalNumberOverflowTest("18446744073709551700"); + + ConsumeDecimalNumberOverflowTest("99999999999999999999"); +} + +void ConsumeDecimalNumberNoDigitsTest(const std::string& input_string) { + Slice input(input_string); + Slice output = input; + uint64_t result; + ASSERT_EQ(false, ConsumeDecimalNumber(&output, &result)); + ASSERT_EQ(input.data(), output.data()); + ASSERT_EQ(input.size(), output.size()); +} + +TEST(Logging, ConsumeDecimalNumberNoDigits) { + ConsumeDecimalNumberNoDigitsTest(""); + ConsumeDecimalNumberNoDigitsTest(" "); + ConsumeDecimalNumberNoDigitsTest("a"); + ConsumeDecimalNumberNoDigitsTest(" 123"); + ConsumeDecimalNumberNoDigitsTest("a123"); + ConsumeDecimalNumberNoDigitsTest(std::string("\000123", 4)); + ConsumeDecimalNumberNoDigitsTest(std::string("\177123", 4)); + ConsumeDecimalNumberNoDigitsTest(std::string("\377123", 4)); +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/mutexlock.h b/src/leveldb/util/mutexlock.h index 1ff5a9efa..0cb2e250f 100644 --- a/src/leveldb/util/mutexlock.h +++ b/src/leveldb/util/mutexlock.h @@ -22,20 +22,18 @@ namespace leveldb { class SCOPED_LOCKABLE MutexLock { public: - explicit MutexLock(port::Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) - : mu_(mu) { + explicit MutexLock(port::Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) { this->mu_->Lock(); } ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } + MutexLock(const MutexLock&) = delete; + MutexLock& operator=(const MutexLock&) = delete; + private: - port::Mutex *const mu_; - // No copying allowed - MutexLock(const MutexLock&); - void operator=(const MutexLock&); + port::Mutex* const mu_; }; } // namespace leveldb - #endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ diff --git a/src/leveldb/util/no_destructor.h b/src/leveldb/util/no_destructor.h new file mode 100644 index 000000000..a0d3b8703 --- /dev/null +++ b/src/leveldb/util/no_destructor.h @@ -0,0 +1,46 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_ +#define STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_ + +#include +#include + +namespace leveldb { + +// Wraps an instance whose destructor is never called. +// +// This is intended for use with function-level static variables. +template +class NoDestructor { + public: + template + explicit NoDestructor(ConstructorArgTypes&&... constructor_args) { + static_assert(sizeof(instance_storage_) >= sizeof(InstanceType), + "instance_storage_ is not large enough to hold the instance"); + static_assert( + alignof(decltype(instance_storage_)) >= alignof(InstanceType), + "instance_storage_ does not meet the instance's alignment requirement"); + new (&instance_storage_) + InstanceType(std::forward(constructor_args)...); + } + + ~NoDestructor() = default; + + NoDestructor(const NoDestructor&) = delete; + NoDestructor& operator=(const NoDestructor&) = delete; + + InstanceType* get() { + return reinterpret_cast(&instance_storage_); + } + + private: + typename std::aligned_storage::type instance_storage_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_ diff --git a/src/leveldb/util/no_destructor_test.cc b/src/leveldb/util/no_destructor_test.cc new file mode 100644 index 000000000..b41caca69 --- /dev/null +++ b/src/leveldb/util/no_destructor_test.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include +#include + +#include "util/no_destructor.h" +#include "util/testharness.h" + +namespace leveldb { + +namespace { + +struct DoNotDestruct { + public: + DoNotDestruct(uint32_t a, uint64_t b) : a(a), b(b) {} + ~DoNotDestruct() { std::abort(); } + + // Used to check constructor argument forwarding. + uint32_t a; + uint64_t b; +}; + +constexpr const uint32_t kGoldenA = 0xdeadbeef; +constexpr const uint64_t kGoldenB = 0xaabbccddeeffaabb; + +} // namespace + +class NoDestructorTest {}; + +TEST(NoDestructorTest, StackInstance) { + NoDestructor instance(kGoldenA, kGoldenB); + ASSERT_EQ(kGoldenA, instance.get()->a); + ASSERT_EQ(kGoldenB, instance.get()->b); +} + +TEST(NoDestructorTest, StaticInstance) { + static NoDestructor instance(kGoldenA, kGoldenB); + ASSERT_EQ(kGoldenA, instance.get()->a); + ASSERT_EQ(kGoldenB, instance.get()->b); +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/options.cc b/src/leveldb/util/options.cc index 76af5b930..62de5bf0d 100644 --- a/src/leveldb/util/options.cc +++ b/src/leveldb/util/options.cc @@ -9,21 +9,6 @@ namespace leveldb { -Options::Options() - : comparator(BytewiseComparator()), - create_if_missing(false), - error_if_exists(false), - paranoid_checks(false), - env(Env::Default()), - info_log(NULL), - write_buffer_size(4<<20), - max_open_files(1000), - block_cache(NULL), - block_size(4096), - block_restart_interval(16), - compression(kSnappyCompression), - filter_policy(NULL) { -} - +Options::Options() : comparator(BytewiseComparator()), env(Env::Default()) {} } // namespace leveldb diff --git a/src/leveldb/util/posix_logger.h b/src/leveldb/util/posix_logger.h index c063c2b7c..28e15d10b 100644 --- a/src/leveldb/util/posix_logger.h +++ b/src/leveldb/util/posix_logger.h @@ -3,94 +3,126 @@ // found in the LICENSE file. See the AUTHORS file for names of contributors. // // Logger implementation that can be shared by all environments -// where enough Posix functionality is available. +// where enough posix functionality is available. #ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ #define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ -#include -#include #include -#include + +#include +#include +#include +#include +#include +#include + #include "leveldb/env.h" namespace leveldb { -class PosixLogger : public Logger { - private: - FILE* file_; - uint64_t (*gettid_)(); // Return the thread id for the current thread +class PosixLogger final : public Logger { public: - PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { } - virtual ~PosixLogger() { - fclose(file_); - } - virtual void Logv(const char* format, va_list ap) { - const uint64_t thread_id = (*gettid_)(); - - // We try twice: the first time with a fixed-size stack allocated buffer, - // and the second time with a much larger dynamically allocated buffer. - char buffer[500]; - for (int iter = 0; iter < 2; iter++) { - char* base; - int bufsize; - if (iter == 0) { - bufsize = sizeof(buffer); - base = buffer; - } else { - bufsize = 30000; - base = new char[bufsize]; - } - char* p = base; - char* limit = base + bufsize; - - struct timeval now_tv; - gettimeofday(&now_tv, NULL); - const time_t seconds = now_tv.tv_sec; - struct tm t; - localtime_r(&seconds, &t); - p += snprintf(p, limit - p, - "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", - t.tm_year + 1900, - t.tm_mon + 1, - t.tm_mday, - t.tm_hour, - t.tm_min, - t.tm_sec, - static_cast(now_tv.tv_usec), - static_cast(thread_id)); - - // Print the message - if (p < limit) { - va_list backup_ap; - va_copy(backup_ap, ap); - p += vsnprintf(p, limit - p, format, backup_ap); - va_end(backup_ap); - } + // Creates a logger that writes to the given file. + // + // The PosixLogger instance takes ownership of the file handle. + explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); } + + ~PosixLogger() override { std::fclose(fp_); } + + void Logv(const char* format, va_list arguments) override { + // Record the time as close to the Logv() call as possible. + struct ::timeval now_timeval; + ::gettimeofday(&now_timeval, nullptr); + const std::time_t now_seconds = now_timeval.tv_sec; + struct std::tm now_components; + ::localtime_r(&now_seconds, &now_components); + + // Record the thread ID. + constexpr const int kMaxThreadIdSize = 32; + std::ostringstream thread_stream; + thread_stream << std::this_thread::get_id(); + std::string thread_id = thread_stream.str(); + if (thread_id.size() > kMaxThreadIdSize) { + thread_id.resize(kMaxThreadIdSize); + } - // Truncate to available space if necessary - if (p >= limit) { - if (iter == 0) { - continue; // Try again with larger buffer - } else { - p = limit - 1; + // We first attempt to print into a stack-allocated buffer. If this attempt + // fails, we make a second attempt with a dynamically allocated buffer. + constexpr const int kStackBufferSize = 512; + char stack_buffer[kStackBufferSize]; + static_assert(sizeof(stack_buffer) == static_cast(kStackBufferSize), + "sizeof(char) is expected to be 1 in C++"); + + int dynamic_buffer_size = 0; // Computed in the first iteration. + for (int iteration = 0; iteration < 2; ++iteration) { + const int buffer_size = + (iteration == 0) ? kStackBufferSize : dynamic_buffer_size; + char* const buffer = + (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size]; + + // Print the header into the buffer. + int buffer_offset = snprintf( + buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", + now_components.tm_year + 1900, now_components.tm_mon + 1, + now_components.tm_mday, now_components.tm_hour, now_components.tm_min, + now_components.tm_sec, static_cast(now_timeval.tv_usec), + thread_id.c_str()); + + // The header can be at most 28 characters (10 date + 15 time + + // 3 delimiters) plus the thread ID, which should fit comfortably into the + // static buffer. + assert(buffer_offset <= 28 + kMaxThreadIdSize); + static_assert(28 + kMaxThreadIdSize < kStackBufferSize, + "stack-allocated buffer may not fit the message header"); + assert(buffer_offset < buffer_size); + + // Print the message into the buffer. + std::va_list arguments_copy; + va_copy(arguments_copy, arguments); + buffer_offset += + std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset, + format, arguments_copy); + va_end(arguments_copy); + + // The code below may append a newline at the end of the buffer, which + // requires an extra character. + if (buffer_offset >= buffer_size - 1) { + // The message did not fit into the buffer. + if (iteration == 0) { + // Re-run the loop and use a dynamically-allocated buffer. The buffer + // will be large enough for the log message, an extra newline and a + // null terminator. + dynamic_buffer_size = buffer_offset + 2; + continue; } + + // The dynamically-allocated buffer was incorrectly sized. This should + // not happen, assuming a correct implementation of (v)snprintf. Fail + // in tests, recover by truncating the log message in production. + assert(false); + buffer_offset = buffer_size - 1; } - // Add newline if necessary - if (p == base || p[-1] != '\n') { - *p++ = '\n'; + // Add a newline if necessary. + if (buffer[buffer_offset - 1] != '\n') { + buffer[buffer_offset] = '\n'; + ++buffer_offset; } - assert(p <= limit); - fwrite(base, 1, p - base, file_); - fflush(file_); - if (base != buffer) { - delete[] base; + assert(buffer_offset <= buffer_size); + std::fwrite(buffer, 1, buffer_offset, fp_); + std::fflush(fp_); + + if (iteration != 0) { + delete[] buffer; } break; } } + + private: + std::FILE* const fp_; }; } // namespace leveldb diff --git a/src/leveldb/util/random.h b/src/leveldb/util/random.h index ddd51b1c7..76f7daf52 100644 --- a/src/leveldb/util/random.h +++ b/src/leveldb/util/random.h @@ -15,6 +15,7 @@ namespace leveldb { class Random { private: uint32_t seed_; + public: explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { // Avoid bad seeds. @@ -23,8 +24,8 @@ class Random { } } uint32_t Next() { - static const uint32_t M = 2147483647L; // 2^31-1 - static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + static const uint32_t M = 2147483647L; // 2^31-1 + static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 // We are computing // seed_ = (seed_ * A) % M, where M = 2^31-1 // @@ -54,9 +55,7 @@ class Random { // Skewed: pick "base" uniformly from range [0,max_log] and then // return "base" random bits. The effect is to pick a number in the // range [0,2^max_log-1] with exponential bias towards smaller numbers. - uint32_t Skewed(int max_log) { - return Uniform(1 << Uniform(max_log + 1)); - } + uint32_t Skewed(int max_log) { return Uniform(1 << Uniform(max_log + 1)); } }; } // namespace leveldb diff --git a/src/leveldb/util/status.cc b/src/leveldb/util/status.cc index a44f35b31..15ce747d8 100644 --- a/src/leveldb/util/status.cc +++ b/src/leveldb/util/status.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. +#include "leveldb/status.h" + #include + #include "port/port.h" -#include "leveldb/status.h" namespace leveldb { @@ -18,8 +20,8 @@ const char* Status::CopyState(const char* state) { Status::Status(Code code, const Slice& msg, const Slice& msg2) { assert(code != kOk); - const uint32_t len1 = msg.size(); - const uint32_t len2 = msg2.size(); + const uint32_t len1 = static_cast(msg.size()); + const uint32_t len2 = static_cast(msg2.size()); const uint32_t size = len1 + (len2 ? (2 + len2) : 0); char* result = new char[size + 5]; memcpy(result, &size, sizeof(size)); @@ -34,7 +36,7 @@ Status::Status(Code code, const Slice& msg, const Slice& msg2) { } std::string Status::ToString() const { - if (state_ == NULL) { + if (state_ == nullptr) { return "OK"; } else { char tmp[30]; @@ -59,8 +61,8 @@ std::string Status::ToString() const { type = "IO error: "; break; default: - snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", - static_cast(code())); + snprintf(tmp, sizeof(tmp), + "Unknown code(%d): ", static_cast(code())); type = tmp; break; } diff --git a/src/leveldb/util/status_test.cc b/src/leveldb/util/status_test.cc new file mode 100644 index 000000000..2842319fb --- /dev/null +++ b/src/leveldb/util/status_test.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include + +#include "leveldb/slice.h" +#include "leveldb/status.h" +#include "util/testharness.h" + +namespace leveldb { + +TEST(Status, MoveConstructor) { + { + Status ok = Status::OK(); + Status ok2 = std::move(ok); + + ASSERT_TRUE(ok2.ok()); + } + + { + Status status = Status::NotFound("custom NotFound status message"); + Status status2 = std::move(status); + + ASSERT_TRUE(status2.IsNotFound()); + ASSERT_EQ("NotFound: custom NotFound status message", status2.ToString()); + } + + { + Status self_moved = Status::IOError("custom IOError status message"); + + // Needed to bypass compiler warning about explicit move-assignment. + Status& self_moved_reference = self_moved; + self_moved_reference = std::move(self_moved); + } +} + +} // namespace leveldb + +int main(int argc, char** argv) { return leveldb::test::RunAllTests(); } diff --git a/src/leveldb/util/testharness.cc b/src/leveldb/util/testharness.cc index 402fab34d..318ecfa3b 100644 --- a/src/leveldb/util/testharness.cc +++ b/src/leveldb/util/testharness.cc @@ -4,11 +4,15 @@ #include "util/testharness.h" -#include #include #include #include +#include +#include + +#include "leveldb/env.h" + namespace leveldb { namespace test { @@ -19,10 +23,10 @@ struct Test { void (*func)(); }; std::vector* tests; -} +} // namespace bool RegisterTest(const char* base, const char* name, void (*func)()) { - if (tests == NULL) { + if (tests == nullptr) { tests = new std::vector; } Test t; @@ -37,14 +41,14 @@ int RunAllTests() { const char* matcher = getenv("LEVELDB_TESTS"); int num = 0; - if (tests != NULL) { + if (tests != nullptr) { for (size_t i = 0; i < tests->size(); i++) { const Test& t = (*tests)[i]; - if (matcher != NULL) { + if (matcher != nullptr) { std::string name = t.base; name.push_back('.'); name.append(t.name); - if (strstr(name.c_str(), matcher) == NULL) { + if (strstr(name.c_str(), matcher) == nullptr) { continue; } } @@ -66,7 +70,7 @@ std::string TmpDir() { int RandomSeed() { const char* env = getenv("TEST_RANDOM_SEED"); - int result = (env != NULL ? atoi(env) : 301); + int result = (env != nullptr ? atoi(env) : 301); if (result <= 0) { result = 301; } diff --git a/src/leveldb/util/testharness.h b/src/leveldb/util/testharness.h index da4fe68bb..72cd1629e 100644 --- a/src/leveldb/util/testharness.h +++ b/src/leveldb/util/testharness.h @@ -7,10 +7,10 @@ #include #include + #include -#include "leveldb/env.h" -#include "leveldb/slice.h" -#include "util/random.h" + +#include "leveldb/status.h" namespace leveldb { namespace test { @@ -27,15 +27,15 @@ namespace test { // // Returns 0 if all tests pass. // Dies or returns a non-zero value if some test fails. -extern int RunAllTests(); +int RunAllTests(); // Return the directory to use for temporary storage. -extern std::string TmpDir(); +std::string TmpDir(); // Return a randomization seed for this run. Typically returns the // same number on repeated invocations of this binary, but automated // runs may be able to vary the seed. -extern int RandomSeed(); +int RandomSeed(); // An instance of Tester is allocated to hold temporary state during // the execution of an assertion. @@ -47,9 +47,7 @@ class Tester { std::stringstream ss_; public: - Tester(const char* f, int l) - : ok_(true), fname_(f), line_(l) { - } + Tester(const char* f, int l) : ok_(true), fname_(f), line_(l) {} ~Tester() { if (!ok_) { @@ -74,14 +72,14 @@ class Tester { return *this; } -#define BINARY_OP(name,op) \ - template \ - Tester& name(const X& x, const Y& y) { \ - if (! (x op y)) { \ - ss_ << " failed: " << x << (" " #op " ") << y; \ - ok_ = false; \ - } \ - return *this; \ +#define BINARY_OP(name, op) \ + template \ + Tester& name(const X& x, const Y& y) { \ + if (!(x op y)) { \ + ss_ << " failed: " << x << (" " #op " ") << y; \ + ok_ = false; \ + } \ + return *this; \ } BINARY_OP(IsEq, ==) @@ -104,33 +102,38 @@ class Tester { #define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c) #define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s)) -#define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b)) -#define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b)) -#define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b)) -#define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b)) -#define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) -#define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b)) - -#define TCONCAT(a,b) TCONCAT1(a,b) -#define TCONCAT1(a,b) a##b - -#define TEST(base,name) \ -class TCONCAT(_Test_,name) : public base { \ - public: \ - void _Run(); \ - static void _RunIt() { \ - TCONCAT(_Test_,name) t; \ - t._Run(); \ - } \ -}; \ -bool TCONCAT(_Test_ignored_,name) = \ - ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \ -void TCONCAT(_Test_,name)::_Run() +#define ASSERT_EQ(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a), (b)) +#define ASSERT_NE(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a), (b)) +#define ASSERT_GE(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a), (b)) +#define ASSERT_GT(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a), (b)) +#define ASSERT_LE(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a), (b)) +#define ASSERT_LT(a, b) \ + ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a), (b)) + +#define TCONCAT(a, b) TCONCAT1(a, b) +#define TCONCAT1(a, b) a##b + +#define TEST(base, name) \ + class TCONCAT(_Test_, name) : public base { \ + public: \ + void _Run(); \ + static void _RunIt() { \ + TCONCAT(_Test_, name) t; \ + t._Run(); \ + } \ + }; \ + bool TCONCAT(_Test_ignored_, name) = ::leveldb::test::RegisterTest( \ + #base, #name, &TCONCAT(_Test_, name)::_RunIt); \ + void TCONCAT(_Test_, name)::_Run() // Register the specified test. Typically not used directly, but // invoked via the macro expansion of TEST. -extern bool RegisterTest(const char* base, const char* name, void (*func)()); - +bool RegisterTest(const char* base, const char* name, void (*func)()); } // namespace test } // namespace leveldb diff --git a/src/leveldb/util/testutil.cc b/src/leveldb/util/testutil.cc index bee56bf75..6b151b9e6 100644 --- a/src/leveldb/util/testutil.cc +++ b/src/leveldb/util/testutil.cc @@ -12,7 +12,7 @@ namespace test { Slice RandomString(Random* rnd, int len, std::string* dst) { dst->resize(len); for (int i = 0; i < len; i++) { - (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' + (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' } return Slice(*dst); } @@ -20,9 +20,8 @@ Slice RandomString(Random* rnd, int len, std::string* dst) { std::string RandomKey(Random* rnd, int len) { // Make sure to generate a wide variety of characters so we // test the boundary conditions for short-key optimizations. - static const char kTestChars[] = { - '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' - }; + static const char kTestChars[] = {'\0', '\1', 'a', 'b', 'c', + 'd', 'e', '\xfd', '\xfe', '\xff'}; std::string result; for (int i = 0; i < len; i++) { result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; @@ -30,9 +29,8 @@ std::string RandomKey(Random* rnd, int len) { return result; } - -extern Slice CompressibleString(Random* rnd, double compressed_fraction, - size_t len, std::string* dst) { +Slice CompressibleString(Random* rnd, double compressed_fraction, size_t len, + std::string* dst) { int raw = static_cast(len * compressed_fraction); if (raw < 1) raw = 1; std::string raw_data; diff --git a/src/leveldb/util/testutil.h b/src/leveldb/util/testutil.h index adad3fc1e..bb4051ba0 100644 --- a/src/leveldb/util/testutil.h +++ b/src/leveldb/util/testutil.h @@ -5,6 +5,7 @@ #ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ #define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ +#include "helpers/memenv/memenv.h" #include "leveldb/env.h" #include "leveldb/slice.h" #include "util/random.h" @@ -14,17 +15,17 @@ namespace test { // Store in *dst a random string of length "len" and return a Slice that // references the generated data. -extern Slice RandomString(Random* rnd, int len, std::string* dst); +Slice RandomString(Random* rnd, int len, std::string* dst); // Return a random key with the specified length that may contain interesting // characters (e.g. \x00, \xff, etc.). -extern std::string RandomKey(Random* rnd, int len); +std::string RandomKey(Random* rnd, int len); // Store in *dst a string of length "len" that will compress to // "N*compressed_fraction" bytes and return a Slice that references // the generated data. -extern Slice CompressibleString(Random* rnd, double compressed_fraction, - size_t len, std::string* dst); +Slice CompressibleString(Random* rnd, double compressed_fraction, size_t len, + std::string* dst); // A wrapper that allows injection of errors. class ErrorEnv : public EnvWrapper { @@ -32,19 +33,31 @@ class ErrorEnv : public EnvWrapper { bool writable_file_error_; int num_writable_file_errors_; - ErrorEnv() : EnvWrapper(Env::Default()), - writable_file_error_(false), - num_writable_file_errors_(0) { } + ErrorEnv() + : EnvWrapper(NewMemEnv(Env::Default())), + writable_file_error_(false), + num_writable_file_errors_(0) {} + ~ErrorEnv() override { delete target(); } - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) { + Status NewWritableFile(const std::string& fname, + WritableFile** result) override { if (writable_file_error_) { ++num_writable_file_errors_; - *result = NULL; + *result = nullptr; return Status::IOError(fname, "fake error"); } return target()->NewWritableFile(fname, result); } + + Status NewAppendableFile(const std::string& fname, + WritableFile** result) override { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = nullptr; + return Status::IOError(fname, "fake error"); + } + return target()->NewAppendableFile(fname, result); + } }; } // namespace test diff --git a/src/leveldb/util/windows_logger.h b/src/leveldb/util/windows_logger.h new file mode 100644 index 000000000..92960638d --- /dev/null +++ b/src/leveldb/util/windows_logger.h @@ -0,0 +1,124 @@ +// Copyright (c) 2018 The LevelDB Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. +// +// Logger implementation for the Windows platform. + +#ifndef STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_ +#define STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_ + +#include +#include +#include +#include +#include +#include + +#include "leveldb/env.h" + +namespace leveldb { + +class WindowsLogger final : public Logger { + public: + // Creates a logger that writes to the given file. + // + // The PosixLogger instance takes ownership of the file handle. + explicit WindowsLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); } + + ~WindowsLogger() override { std::fclose(fp_); } + + void Logv(const char* format, va_list arguments) override { + // Record the time as close to the Logv() call as possible. + SYSTEMTIME now_components; + ::GetLocalTime(&now_components); + + // Record the thread ID. + constexpr const int kMaxThreadIdSize = 32; + std::ostringstream thread_stream; + thread_stream << std::this_thread::get_id(); + std::string thread_id = thread_stream.str(); + if (thread_id.size() > kMaxThreadIdSize) { + thread_id.resize(kMaxThreadIdSize); + } + + // We first attempt to print into a stack-allocated buffer. If this attempt + // fails, we make a second attempt with a dynamically allocated buffer. + constexpr const int kStackBufferSize = 512; + char stack_buffer[kStackBufferSize]; + static_assert(sizeof(stack_buffer) == static_cast(kStackBufferSize), + "sizeof(char) is expected to be 1 in C++"); + + int dynamic_buffer_size = 0; // Computed in the first iteration. + for (int iteration = 0; iteration < 2; ++iteration) { + const int buffer_size = + (iteration == 0) ? kStackBufferSize : dynamic_buffer_size; + char* const buffer = + (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size]; + + // Print the header into the buffer. + int buffer_offset = snprintf( + buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ", + now_components.wYear, now_components.wMonth, now_components.wDay, + now_components.wHour, now_components.wMinute, now_components.wSecond, + static_cast(now_components.wMilliseconds * 1000), + thread_id.c_str()); + + // The header can be at most 28 characters (10 date + 15 time + + // 3 delimiters) plus the thread ID, which should fit comfortably into the + // static buffer. + assert(buffer_offset <= 28 + kMaxThreadIdSize); + static_assert(28 + kMaxThreadIdSize < kStackBufferSize, + "stack-allocated buffer may not fit the message header"); + assert(buffer_offset < buffer_size); + + // Print the message into the buffer. + std::va_list arguments_copy; + va_copy(arguments_copy, arguments); + buffer_offset += + std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset, + format, arguments_copy); + va_end(arguments_copy); + + // The code below may append a newline at the end of the buffer, which + // requires an extra character. + if (buffer_offset >= buffer_size - 1) { + // The message did not fit into the buffer. + if (iteration == 0) { + // Re-run the loop and use a dynamically-allocated buffer. The buffer + // will be large enough for the log message, an extra newline and a + // null terminator. + dynamic_buffer_size = buffer_offset + 2; + continue; + } + + // The dynamically-allocated buffer was incorrectly sized. This should + // not happen, assuming a correct implementation of (v)snprintf. Fail + // in tests, recover by truncating the log message in production. + assert(false); + buffer_offset = buffer_size - 1; + } + + // Add a newline if necessary. + if (buffer[buffer_offset - 1] != '\n') { + buffer[buffer_offset] = '\n'; + ++buffer_offset; + } + + assert(buffer_offset <= buffer_size); + std::fwrite(buffer, 1, buffer_offset, fp_); + std::fflush(fp_); + + if (iteration != 0) { + delete[] buffer; + } + break; + } + } + + private: + std::FILE* const fp_; +}; + +} // namespace leveldb + +#endif // STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_ From f2f13e6e087565a4910ef4ac49b7212e77c3850f Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:04:57 +0000 Subject: [PATCH 007/146] build: CRC32C build system integration --- configure.ac | 95 ++++- doc/Doxyfile | 2 +- src/Makefile.am | 3 +- src/Makefile.crc32c.include | 81 ++++ src/Makefile.leveldb.include | 5 +- src/crc32c/.appveyor.yml | 38 ++ src/crc32c/.clang-format | 3 + src/crc32c/.clang_complete | 8 + src/crc32c/.gitignore | 8 + src/crc32c/.gitmodules | 0 src/crc32c/.travis.yml | 76 ++++ src/crc32c/.ycm_extra_conf.py | 142 +++++++ src/crc32c/AUTHORS | 11 + src/crc32c/CMakeLists.txt | 435 +++++++++++++++++++++ src/crc32c/CONTRIBUTING.md | 23 ++ src/crc32c/Crc32cConfig.cmake.in | 9 + src/crc32c/LICENSE | 28 ++ src/crc32c/README.md | 125 ++++++ src/crc32c/include/crc32c/crc32c.h | 89 +++++ src/crc32c/src/crc32c.cc | 39 ++ src/crc32c/src/crc32c_arm64.cc | 133 +++++++ src/crc32c/src/crc32c_arm64.h | 27 ++ src/crc32c/src/crc32c_arm64_check.h | 68 ++++ src/crc32c/src/crc32c_arm64_unittest.cc | 24 ++ src/crc32c/src/crc32c_benchmark.cc | 106 +++++ src/crc32c/src/crc32c_capi_unittest.c | 66 ++++ src/crc32c/src/crc32c_config.h.in | 36 ++ src/crc32c/src/crc32c_extend_unittests.h | 112 ++++++ src/crc32c/src/crc32c_internal.h | 23 ++ src/crc32c/src/crc32c_portable.cc | 351 +++++++++++++++++ src/crc32c/src/crc32c_portable_unittest.cc | 20 + src/crc32c/src/crc32c_prefetch.h | 46 +++ src/crc32c/src/crc32c_prefetch_unittest.cc | 9 + src/crc32c/src/crc32c_read_le.h | 53 +++ src/crc32c/src/crc32c_read_le_unittest.cc | 32 ++ src/crc32c/src/crc32c_round_up.h | 34 ++ src/crc32c/src/crc32c_round_up_unittest.cc | 84 ++++ src/crc32c/src/crc32c_sse42.cc | 258 ++++++++++++ src/crc32c/src/crc32c_sse42.h | 33 ++ src/crc32c/src/crc32c_sse42_check.h | 50 +++ src/crc32c/src/crc32c_sse42_unittest.cc | 24 ++ src/crc32c/src/crc32c_test_main.cc | 22 ++ src/crc32c/src/crc32c_unittest.cc | 129 ++++++ 43 files changed, 2952 insertions(+), 8 deletions(-) create mode 100644 src/Makefile.crc32c.include create mode 100644 src/crc32c/.appveyor.yml create mode 100644 src/crc32c/.clang-format create mode 100644 src/crc32c/.clang_complete create mode 100644 src/crc32c/.gitignore create mode 100644 src/crc32c/.gitmodules create mode 100644 src/crc32c/.travis.yml create mode 100644 src/crc32c/.ycm_extra_conf.py create mode 100644 src/crc32c/AUTHORS create mode 100644 src/crc32c/CMakeLists.txt create mode 100644 src/crc32c/CONTRIBUTING.md create mode 100644 src/crc32c/Crc32cConfig.cmake.in create mode 100644 src/crc32c/LICENSE create mode 100644 src/crc32c/README.md create mode 100644 src/crc32c/include/crc32c/crc32c.h create mode 100644 src/crc32c/src/crc32c.cc create mode 100644 src/crc32c/src/crc32c_arm64.cc create mode 100644 src/crc32c/src/crc32c_arm64.h create mode 100644 src/crc32c/src/crc32c_arm64_check.h create mode 100644 src/crc32c/src/crc32c_arm64_unittest.cc create mode 100644 src/crc32c/src/crc32c_benchmark.cc create mode 100644 src/crc32c/src/crc32c_capi_unittest.c create mode 100644 src/crc32c/src/crc32c_config.h.in create mode 100644 src/crc32c/src/crc32c_extend_unittests.h create mode 100644 src/crc32c/src/crc32c_internal.h create mode 100644 src/crc32c/src/crc32c_portable.cc create mode 100644 src/crc32c/src/crc32c_portable_unittest.cc create mode 100644 src/crc32c/src/crc32c_prefetch.h create mode 100644 src/crc32c/src/crc32c_prefetch_unittest.cc create mode 100644 src/crc32c/src/crc32c_read_le.h create mode 100644 src/crc32c/src/crc32c_read_le_unittest.cc create mode 100644 src/crc32c/src/crc32c_round_up.h create mode 100644 src/crc32c/src/crc32c_round_up_unittest.cc create mode 100644 src/crc32c/src/crc32c_sse42.cc create mode 100644 src/crc32c/src/crc32c_sse42.h create mode 100644 src/crc32c/src/crc32c_sse42_check.h create mode 100644 src/crc32c/src/crc32c_sse42_unittest.cc create mode 100644 src/crc32c/src/crc32c_test_main.cc create mode 100644 src/crc32c/src/crc32c_unittest.cc diff --git a/configure.ac b/configure.ac index 27c1e152c..ca71e8564 100644 --- a/configure.ac +++ b/configure.ac @@ -286,6 +286,60 @@ if test "x$CXXFLAGS_overridden" = "xno"; then fi CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" +enable_arm_crc=no +enable_sse42=no + +dnl Check for optional instruction set support. Enabling these does _not_ imply that all code will +dnl be compiled with them, rather that specific objects/libs may use them after checking for runtime +dnl compatibility. + +dnl x86 +AX_CHECK_COMPILE_FLAG([-msse4.2], [SSE42_CXXFLAGS="-msse4.2"], [], [$CXXFLAG_WERROR]) + +TEMP_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$SSE42_CXXFLAGS $CXXFLAGS" +AC_MSG_CHECKING([for SSE4.2 intrinsics]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #if defined(_MSC_VER) + #include + #elif defined(__GNUC__) && defined(__SSE4_2__) + #include + #endif + ]],[[ + uint64_t l = 0; + l = _mm_crc32_u8(l, 0); + l = _mm_crc32_u32(l, 0); + l = _mm_crc32_u64(l, 0); + return l; + ]])], + [ AC_MSG_RESULT([yes]); enable_sse42=yes], + [ AC_MSG_RESULT([no])] +) +CXXFLAGS="$TEMP_CXXFLAGS" + +# ARM +AX_CHECK_COMPILE_FLAG([-march=armv8-a+crc+crypto], [ARM_CRC_CXXFLAGS="-march=armv8-a+crc+crypto"], [], [$CXXFLAG_WERROR]) + +TEMP_CXXFLAGS="$CXXFLAGS" +CXXFLAGS="$ARM_CRC_CXXFLAGS $CXXFLAGS" +AC_MSG_CHECKING([for ARMv8 CRC32 intrinsics]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]],[[ +#ifdef __aarch64__ + __crc32cb(0, 0); __crc32ch(0, 0); __crc32cw(0, 0); __crc32cd(0, 0); + vmull_p64(0, 0); +#else +#error "crc32c library does not support hardware acceleration on 32-bit ARM" +#endif + ]])], + [ AC_MSG_RESULT([yes]); enable_arm_crc=yes; ], + [ AC_MSG_RESULT([no])] +) +CXXFLAGS="$TEMP_CXXFLAGS" + AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], [build bitcoinz-cli bitcoinz-tx (default=yes)])], @@ -648,10 +702,36 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [ AC_MSG_RESULT(no); HAVE_O_CLOEXEC=0 ] ) -if test x$use_reduce_exports = xyes; then - AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], - [AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])]) -fi +dnl crc32c platform checks +AC_MSG_CHECKING([for __builtin_prefetch]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ + char data = 0; + const char* address = &data; + __builtin_prefetch(address, 0, 0); + ]])], + [ AC_MSG_RESULT([yes]); HAVE_BUILTIN_PREFETCH=1 ], + [ AC_MSG_RESULT([no]); HAVE_BUILTIN_PREFETCH=0 ] +) + +AC_MSG_CHECKING([for _mm_prefetch]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ + char data = 0; + const char* address = &data; + _mm_prefetch(address, _MM_HINT_NTA); + ]])], + [ AC_MSG_RESULT([yes]); HAVE_MM_PREFETCH=1 ], + [ AC_MSG_RESULT([no]); HAVE_MM_PREFETCH=0 ] +) + +AC_MSG_CHECKING([for strong getauxval support in the system headers]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], [[ + getauxval(AT_HWCAP); + ]])], + [ AC_MSG_RESULT([yes]); HAVE_STRONG_GETAUXVAL=1; AC_DEFINE([HAVE_STRONG_GETAUXVAL], [1], [Define this symbol to build code that uses getauxval]) ], + [ AC_MSG_RESULT([no]); HAVE_STRONG_GETAUXVAL=0 ] +) LIBMEMENV= AC_SUBST(LIBMEMENV) @@ -897,6 +977,8 @@ AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes]) AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) +AM_CONDITIONAL([ENABLE_SSE42],[test x$enable_sse42 = xyes]) +AM_CONDITIONAL([ENABLE_ARM_CRC], [test x$enable_arm_crc = xyes]) AM_CONDITIONAL([WORDS_BIGENDIAN], [test x$ac_cv_c_bigendian = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) @@ -928,6 +1010,8 @@ AC_SUBST(PIC_FLAGS) AC_SUBST(PIE_FLAGS) AC_SUBST(SANITIZER_CXXFLAGS) AC_SUBST(SANITIZER_LDFLAGS) +AC_SUBST(SSE42_CXXFLAGS) +AC_SUBST(ARM_CRC_CXXFLAGS) AC_SUBST(LIBTOOL_APP_LDFLAGS) AC_SUBST(BOOST_LIBS) AC_SUBST(TESTDEFS) @@ -938,6 +1022,9 @@ AC_SUBST(LIBZCASH_LIBS) AC_SUBST(HAVE_FDATASYNC) AC_SUBST(HAVE_FULLFSYNC) AC_SUBST(HAVE_O_CLOEXEC) +AC_SUBST(HAVE_BUILTIN_PREFETCH) +AC_SUBST(HAVE_MM_PREFETCH) +AC_SUBST(HAVE_STRONG_GETAUXVAL) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh]) AC_CONFIG_FILES([qa/pull-tester/tests-config.sh],[chmod +x qa/pull-tester/tests-config.sh]) diff --git a/doc/Doxyfile b/doc/Doxyfile index ec723e503..d41eb5acb 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -683,7 +683,7 @@ RECURSIVE = YES # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. -EXCLUDE = src/leveldb src/json src/test /src/qt/test +EXCLUDE = src/crc32c src/leveldb src/json src/test /src/qt/test # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/src/Makefile.am b/src/Makefile.am index 2a9683023..e592c210d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -532,7 +532,7 @@ CTAES_DIST += crypto/ctaes/ctaes.h CTAES_DIST += crypto/ctaes/README.md CTAES_DIST += crypto/ctaes/test.c -CLEANFILES = *.gcda *.gcno */*.gcno wallet/*/*.gcno +CLEANFILES = *.gcda *.gcno */*.gcno crc32c/src/*.gcda crc32c/src/*.gcno wallet/*/*.gcno DISTCLEANFILES = obj/build.h @@ -569,6 +569,7 @@ endif @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $( +Vadim Skipin +Rodrigo Tobar +Harry Mallon diff --git a/src/crc32c/CMakeLists.txt b/src/crc32c/CMakeLists.txt new file mode 100644 index 000000000..2f52db104 --- /dev/null +++ b/src/crc32c/CMakeLists.txt @@ -0,0 +1,435 @@ +# Copyright 2017 The CRC32C Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. See the AUTHORS file for names of contributors. + +cmake_minimum_required(VERSION 3.1) +project(Crc32c VERSION 1.1.0 LANGUAGES C CXX) + +# C standard can be overridden when this is used as a sub-project. +if(NOT CMAKE_C_STANDARD) + # This project can use C11, but will gracefully decay down to C89. + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED OFF) + set(CMAKE_C_EXTENSIONS OFF) +endif(NOT CMAKE_C_STANDARD) + +# C++ standard can be overridden when this is used as a sub-project. +if(NOT CMAKE_CXX_STANDARD) + # This project requires C++11. + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) +endif(NOT CMAKE_CXX_STANDARD) + +# https://github.com/izenecloud/cmake/blob/master/SetCompilerWarningAll.cmake +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Use the highest warning level for Visual Studio. + set(CMAKE_CXX_WARNING_LEVEL 4) + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + + # Disable C++ exceptions. + string(REGEX REPLACE "/EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHs-c-") + add_definitions(-D_HAS_EXCEPTIONS=0) + + # Disable RTTI. + string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-") +else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Use -Wall for clang and gcc. + if(NOT CMAKE_CXX_FLAGS MATCHES "-Wall") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + endif(NOT CMAKE_CXX_FLAGS MATCHES "-Wall") + + # Use -Wextra for clang and gcc. + if(NOT CMAKE_CXX_FLAGS MATCHES "-Wextra") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") + endif(NOT CMAKE_CXX_FLAGS MATCHES "-Wextra") + + # Use -Werror for clang and gcc. + if(NOT CMAKE_CXX_FLAGS MATCHES "-Werror") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") + endif(NOT CMAKE_CXX_FLAGS MATCHES "-Werror") + + # Disable C++ exceptions. + string(REGEX REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") + + # Disable RTTI. + string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + +option(CRC32C_BUILD_TESTS "Build CRC32C's unit tests" ON) +option(CRC32C_BUILD_BENCHMARKS "Build CRC32C's benchmarks" ON) +option(CRC32C_USE_GLOG "Build CRC32C's tests with Google Logging" ON) +option(CRC32C_INSTALL "Install CRC32C's header and library" ON) + +include(TestBigEndian) +test_big_endian(BYTE_ORDER_BIG_ENDIAN) + +include(CheckCXXCompilerFlag) +# Used by glog. +check_cxx_compiler_flag(-Wno-deprecated CRC32C_HAVE_NO_DEPRECATED) +# Used by glog. +check_cxx_compiler_flag(-Wno-sign-compare CRC32C_HAVE_NO_SIGN_COMPARE) +# Used by glog. +check_cxx_compiler_flag(-Wno-unused-parameter CRC32C_HAVE_NO_UNUSED_PARAMETER) +# Used by googletest. +check_cxx_compiler_flag(-Wno-missing-field-initializers + CRC32C_HAVE_NO_MISSING_FIELD_INITIALIZERS) + +# Check for __builtin_prefetch support in the compiler. +include(CheckCXXSourceCompiles) +check_cxx_source_compiles(" +int main() { + char data = 0; + const char* address = &data; + __builtin_prefetch(address, 0, 0); + return 0; +} +" HAVE_BUILTIN_PREFETCH) + +# Check for _mm_prefetch support in the compiler. +include(CheckCXXSourceCompiles) +check_cxx_source_compiles(" +#if defined(_MSC_VER) +#include +#else // !defined(_MSC_VER) +#include +#endif // defined(_MSC_VER) + +int main() { + char data = 0; + const char* address = &data; + _mm_prefetch(address, _MM_HINT_NTA); + return 0; +} +" HAVE_MM_PREFETCH) + +# Check for SSE4.2 support in the compiler. +set(OLD_CMAKE_REQURED_FLAGS ${CMAKE_REQUIRED_FLAGS}) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /arch:AVX") +else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -msse4.2") +endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +check_cxx_source_compiles(" +#if defined(_MSC_VER) +#include +#else // !defined(_MSC_VER) +#include +#include +#endif // defined(_MSC_VER) + +int main() { + _mm_crc32_u8(0, 0); _mm_crc32_u32(0, 0); +#if defined(_M_X64) || defined(__x86_64__) + _mm_crc32_u64(0, 0); +#endif // defined(_M_X64) || defined(__x86_64__) + return 0; +} +" HAVE_SSE42) +set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQURED_FLAGS}) + +# Check for ARMv8 w/ CRC and CRYPTO extensions support in the compiler. +set(OLD_CMAKE_REQURED_FLAGS ${CMAKE_REQUIRED_FLAGS}) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # TODO(pwnall): Insert correct flag when VS gets ARM CRC32C support. + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /arch:NOTYET") +else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -march=armv8-a+crc+crypto") +endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +check_cxx_source_compiles(" +#include +#include + +int main() { + __crc32cb(0, 0); __crc32ch(0, 0); __crc32cw(0, 0); __crc32cd(0, 0); + vmull_p64(0, 0); + return 0; +} +" HAVE_ARM64_CRC32C) +set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQURED_FLAGS}) + +# Check for strong getauxval() support in the system headers. +check_cxx_source_compiles(" +#include +#include +#include + +int main() { + getauxval(AT_HWCAP); + return 0; +} +" HAVE_STRONG_GETAUXVAL) + +# Check for weak getauxval() support in the compiler. +check_cxx_source_compiles(" +unsigned long getauxval(unsigned long type) __attribute__((weak)); +#define AT_HWCAP 16 + +int main() { + getauxval(AT_HWCAP); + return 0; +} +" HAVE_WEAK_GETAUXVAL) + +if(CRC32C_USE_GLOG) + # glog requires this setting to avoid using dynamic_cast. + set(DISABLE_RTTI ON CACHE BOOL "" FORCE) + + # glog's test targets trigger deprecation warnings, and compiling them burns + # CPU cycles on the CI. + set(BUILD_TESTING_SAVED "${BUILD_TESTING}") + set(BUILD_TESTING OFF CACHE BOOL "" FORCE) + add_subdirectory("third_party/glog" EXCLUDE_FROM_ALL) + set(BUILD_TESTING "${BUILD_TESTING_SAVED}" CACHE BOOL "" FORCE) + + # glog triggers deprecation warnings on OSX. + # https://github.com/google/glog/issues/185 + if(CRC32C_HAVE_NO_DEPRECATED) + set_property(TARGET glog APPEND PROPERTY COMPILE_OPTIONS -Wno-deprecated) + endif(CRC32C_HAVE_NO_DEPRECATED) + + # glog triggers sign comparison warnings on gcc. + if(CRC32C_HAVE_NO_SIGN_COMPARE) + set_property(TARGET glog APPEND PROPERTY COMPILE_OPTIONS -Wno-sign-compare) + endif(CRC32C_HAVE_NO_SIGN_COMPARE) + + # glog triggers unused parameter warnings on clang. + if(CRC32C_HAVE_NO_UNUSED_PARAMETER) + set_property(TARGET glog + APPEND PROPERTY COMPILE_OPTIONS -Wno-unused-parameter) + endif(CRC32C_HAVE_NO_UNUSED_PARAMETER) + + set(CRC32C_TESTS_BUILT_WITH_GLOG 1) +endif(CRC32C_USE_GLOG) + +configure_file( + "src/crc32c_config.h.in" + "${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h" +) + +include_directories("${PROJECT_BINARY_DIR}/include") + +# ARM64 CRC32C code is built separately, so we don't accidentally compile +# unsupported instructions into code that gets run without ARM32 support. +add_library(crc32c_arm64 OBJECT "") +target_sources(crc32c_arm64 + PRIVATE + "${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h" + "src/crc32c_arm64.cc" + "src/crc32c_arm64.h" +) +if(HAVE_ARM64_CRC32C) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # TODO(pwnall): Insert correct flag when VS gets ARM64 CRC32C support. + target_compile_options(crc32c_arm64 PRIVATE "/arch:NOTYET") + else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options(crc32c_arm64 PRIVATE "-march=armv8-a+crc+crypto") + endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +endif(HAVE_ARM64_CRC32C) + +# CMake only enables PIC by default in SHARED and MODULE targets. +if(BUILD_SHARED_LIBS) + set_property(TARGET crc32c_arm64 PROPERTY POSITION_INDEPENDENT_CODE TRUE) +endif(BUILD_SHARED_LIBS) + +# SSE4.2 code is built separately, so we don't accidentally compile unsupported +# instructions into code that gets run without SSE4.2 support. +add_library(crc32c_sse42 OBJECT "") +target_sources(crc32c_sse42 + PRIVATE + "${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h" + "src/crc32c_sse42.cc" + "src/crc32c_sse42.h" +) +if(HAVE_SSE42) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options(crc32c_sse42 PRIVATE "/arch:AVX") + else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + target_compile_options(crc32c_sse42 PRIVATE "-msse4.2") + endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +endif(HAVE_SSE42) + +# CMake only enables PIC by default in SHARED and MODULE targets. +if(BUILD_SHARED_LIBS) + set_property(TARGET crc32c_sse42 PROPERTY POSITION_INDEPENDENT_CODE TRUE) +endif(BUILD_SHARED_LIBS) + +# Must be included before CMAKE_INSTALL_INCLUDEDIR is used. +include(GNUInstallDirs) + +add_library(crc32c "" + # TODO(pwnall): Move the TARGET_OBJECTS generator expressions to the PRIVATE + # section of target_sources when cmake_minimum_required becomes 3.9 or above. + $ + $ +) +target_sources(crc32c + PRIVATE + "${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h" + "src/crc32c_arm64.h" + "src/crc32c_arm64_check.h" + "src/crc32c_internal.h" + "src/crc32c_portable.cc" + "src/crc32c_prefetch.h" + "src/crc32c_read_le.h" + "src/crc32c_round_up.h" + "src/crc32c_sse42.h" + "src/crc32c_sse42_check.h" + "src/crc32c.cc" + + # Only CMake 3.3+ supports PUBLIC sources in targets exported by "install". + $<$:PUBLIC> + "include/crc32c/crc32c.h" +) + +target_include_directories(crc32c + PUBLIC + $ + $ +) + +set_property( + TARGET crc32c_arm64 crc32c_sse42 crc32c + APPEND + PROPERTY COMPILE_DEFINITIONS CRC32C_HAVE_CONFIG_H +) + +set_target_properties(crc32c + PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) + +# Warnings as errors in Visual Studio for this project's targets. +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set_property(TARGET crc32c APPEND PROPERTY COMPILE_OPTIONS "/WX") + set_property(TARGET crc32c_arm64 APPEND PROPERTY COMPILE_OPTIONS "/WX") + set_property(TARGET crc32c_sse42 APPEND PROPERTY COMPILE_OPTIONS "/WX") +endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + +if(CRC32C_BUILD_TESTS) + enable_testing() + + # Prevent overriding the parent project's compiler/linker settings on Windows. + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + set(install_gtest OFF) + set(install_gmock OFF) + + # This project is tested using GoogleTest. + add_subdirectory("third_party/googletest") + + # GoogleTest triggers a missing field initializers warning. + if(CRC32C_HAVE_NO_MISSING_FIELD_INITIALIZERS) + set_property(TARGET gtest + APPEND PROPERTY COMPILE_OPTIONS -Wno-missing-field-initializers) + set_property(TARGET gmock + APPEND PROPERTY COMPILE_OPTIONS -Wno-missing-field-initializers) + endif(CRC32C_HAVE_NO_MISSING_FIELD_INITIALIZERS) + + add_executable(crc32c_tests "") + target_sources(crc32c_tests + PRIVATE + "${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h" + "src/crc32c_arm64_unittest.cc" + "src/crc32c_extend_unittests.h" + "src/crc32c_portable_unittest.cc" + "src/crc32c_prefetch_unittest.cc" + "src/crc32c_read_le_unittest.cc" + "src/crc32c_round_up_unittest.cc" + "src/crc32c_sse42_unittest.cc" + "src/crc32c_unittest.cc" + "src/crc32c_test_main.cc" + ) + target_link_libraries(crc32c_tests crc32c gtest) + + # Warnings as errors in Visual Studio for this project's targets. + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set_property(TARGET crc32c_tests APPEND PROPERTY COMPILE_OPTIONS "/WX") + endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + + if(CRC32C_USE_GLOG) + target_link_libraries(crc32c_tests glog) + endif(CRC32C_USE_GLOG) + + add_test(NAME crc32c_tests COMMAND crc32c_tests) + + add_executable(crc32c_capi_tests "") + target_sources(crc32c_capi_tests + PRIVATE + "src/crc32c_capi_unittest.c" + ) + target_link_libraries(crc32c_capi_tests crc32c) + + # Warnings as errors in Visual Studio for this project's targets. + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set_property(TARGET crc32c_capi_tests APPEND PROPERTY COMPILE_OPTIONS "/WX") + endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + + add_test(NAME crc32c_capi_tests COMMAND crc32c_capi_tests) +endif(CRC32C_BUILD_TESTS) + +if(CRC32C_BUILD_BENCHMARKS) + add_executable(crc32c_bench "") + target_sources(crc32c_bench + PRIVATE + "${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h" + "src/crc32c_benchmark.cc" + ) + target_link_libraries(crc32c_bench crc32c) + + # This project uses Google benchmark for benchmarking. + set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) + set(BENCHMARK_ENABLE_EXCEPTIONS OFF CACHE BOOL "" FORCE) + add_subdirectory("third_party/benchmark") + target_link_libraries(crc32c_bench benchmark) + + if(CRC32C_USE_GLOG) + target_link_libraries(crc32c_bench glog) + endif(CRC32C_USE_GLOG) + + # Warnings as errors in Visual Studio for this project's targets. + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set_property(TARGET crc32c_bench APPEND PROPERTY COMPILE_OPTIONS "/WX") + endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +endif(CRC32C_BUILD_BENCHMARKS) + +if(CRC32C_INSTALL) + install(TARGETS crc32c + EXPORT Crc32cTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + install( + FILES + "include/crc32c/crc32c.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/crc32c" + ) + + include(CMakePackageConfigHelpers) + configure_package_config_file( + "${PROJECT_NAME}Config.cmake.in" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) + write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + COMPATIBILITY SameMajorVersion + ) + install( + EXPORT Crc32cTargets + NAMESPACE Crc32c:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) + install( + FILES + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + ) +endif(CRC32C_INSTALL) diff --git a/src/crc32c/CONTRIBUTING.md b/src/crc32c/CONTRIBUTING.md new file mode 100644 index 000000000..ae319c70a --- /dev/null +++ b/src/crc32c/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution, +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. diff --git a/src/crc32c/Crc32cConfig.cmake.in b/src/crc32c/Crc32cConfig.cmake.in new file mode 100644 index 000000000..c6b8fc791 --- /dev/null +++ b/src/crc32c/Crc32cConfig.cmake.in @@ -0,0 +1,9 @@ +# Copyright 2017 The CRC32C Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. See the AUTHORS file for names of contributors. + +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/Crc32cTargets.cmake") + +check_required_components(Crc32c) diff --git a/src/crc32c/LICENSE b/src/crc32c/LICENSE new file mode 100644 index 000000000..8c8735cf1 --- /dev/null +++ b/src/crc32c/LICENSE @@ -0,0 +1,28 @@ +Copyright 2017, The CRC32C Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * 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. + + * Neither the name of Google Inc. 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 +OWNER 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. diff --git a/src/crc32c/README.md b/src/crc32c/README.md new file mode 100644 index 000000000..58ba38e61 --- /dev/null +++ b/src/crc32c/README.md @@ -0,0 +1,125 @@ +# CRC32C + +[![Build Status](https://travis-ci.org/google/crc32c.svg?branch=master)](https://travis-ci.org/google/crc32c) +[![Build Status](https://ci.appveyor.com/api/projects/status/moiq7331pett4xuj/branch/master?svg=true)](https://ci.appveyor.com/project/pwnall/crc32c) + +New file format authors should consider +[HighwayHash](https://github.com/google/highwayhash). The initial version of +this code was extracted from [LevelDB](https://github.com/google/leveldb), which +is a stable key-value store that is widely used at Google. + +This project collects a few CRC32C implementations under an umbrella that +dispatches to a suitable implementation based on the host computer's hardware +capabilities. + +CRC32C is specified as the CRC that uses the iSCSI polynomial in +[RFC 3720](https://tools.ietf.org/html/rfc3720#section-12.1). The polynomial was +introduced by G. Castagnoli, S. Braeuer and M. Herrmann. CRC32C is used in +software such as Btrfs, ext4, Ceph and leveldb. + + +## Usage + +```cpp +#include "crc32c/crc32c.h" + +int main() { + const std::uint8_t buffer[] = {0, 0, 0, 0}; + std::uint32_t result; + + // Process a raw buffer. + result = crc32c::Crc32c(buffer, 4); + + // Process a std::string. + std::string string; + string.resize(4); + result = crc32c::Crc32c(string); + + // If you have C++17 support, process a std::string_view. + std::string_view string_view(string); + result = crc32c::Crc32c(string_view); + + return 0; +} +``` + + +## Prerequisites + +This project uses [CMake](https://cmake.org/) for building and testing. CMake is +available in all popular Linux distributions, as well as in +[Homebrew](https://brew.sh/). + +This project uses submodules for dependency management. + +```bash +git submodule update --init --recursive +``` + +If you're using [Atom](https://atom.io/), the following packages can help. + +```bash +apm install autocomplete-clang build build-cmake clang-format language-cmake \ + linter linter-clang +``` + +If you don't mind more setup in return for more speed, replace +`autocomplete-clang` and `linter-clang` with `you-complete-me`. This requires +[setting up ycmd](https://github.com/ycm-core/ycmd#building). + +```bash +apm install autocomplete-plus build build-cmake clang-format language-cmake \ + linter you-complete-me +``` + +## Building + +The following commands build and install the project. + +```bash +mkdir build +cd build +cmake -DCRC32C_BUILD_TESTS=0 -DCRC32C_BUILD_BENCHMARKS=0 .. && make all install +``` + + +## Development + +The following command (when executed from `build/`) (re)builds the project and +runs the tests. + +```bash +cmake .. && cmake --build . && ctest --output-on-failure +``` + + +### Android testing + +The following command builds the project against the Android NDK, which is +useful for benchmarking against ARM processors. + +```bash +cmake .. -DCMAKE_SYSTEM_NAME=Android -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \ + -DCMAKE_ANDROID_NDK=$HOME/Library/Android/sdk/ndk-bundle \ + -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang \ + -DCMAKE_ANDROID_STL_TYPE=c++_static -DCRC32C_USE_GLOG=0 \ + -DCMAKE_BUILD_TYPE=Release && cmake --build . +``` + +The following commands install and run the benchmarks. + +```bash +adb push crc32c_bench /data/local/tmp +adb shell chmod +x /data/local/tmp/crc32c_bench +adb shell 'cd /data/local/tmp && ./crc32c_bench' +adb shell rm /data/local/tmp/crc32c_bench +``` + +The following commands install and run the tests. + +```bash +adb push crc32c_tests /data/local/tmp +adb shell chmod +x /data/local/tmp/crc32c_tests +adb shell 'cd /data/local/tmp && ./crc32c_tests' +adb shell rm /data/local/tmp/crc32c_tests +``` diff --git a/src/crc32c/include/crc32c/crc32c.h b/src/crc32c/include/crc32c/crc32c.h new file mode 100644 index 000000000..e8a78170a --- /dev/null +++ b/src/crc32c/include/crc32c/crc32c.h @@ -0,0 +1,89 @@ +/* Copyright 2017 The CRC32C Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. See the AUTHORS file for names of contributors. */ + +#ifndef CRC32C_CRC32C_H_ +#define CRC32C_CRC32C_H_ + +/* The API exported by the CRC32C project. */ + +#if defined(__cplusplus) + +#include +#include +#include + +#else /* !defined(__cplusplus) */ + +#include +#include + +#endif /* !defined(__cplusplus) */ + + +/* The C API. */ + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +/* Extends "crc" with the CRC32C of "count" bytes in the buffer pointed by + "data" */ +uint32_t crc32c_extend(uint32_t crc, const uint8_t* data, size_t count); + +/* Computes the CRC32C of "count" bytes in the buffer pointed by "data". */ +uint32_t crc32c_value(const uint8_t* data, size_t count); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif /* defined(__cplusplus) */ + + +/* The C++ API. */ + +#if defined(__cplusplus) + +namespace crc32c { + +// Extends "crc" with the CRC32C of "count" bytes in the buffer pointed by +// "data". +uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count); + +// Computes the CRC32C of "count" bytes in the buffer pointed by "data". +inline uint32_t Crc32c(const uint8_t* data, size_t count) { + return Extend(0, data, count); +} + +// Computes the CRC32C of "count" bytes in the buffer pointed by "data". +inline uint32_t Crc32c(const char* data, size_t count) { + return Extend(0, reinterpret_cast(data), count); +} + +// Computes the CRC32C of the string's content. +inline uint32_t Crc32c(const std::string& string) { + return Crc32c(reinterpret_cast(string.data()), + string.size()); +} + +} // namespace crc32c + +#if __cplusplus > 201402L +#if __has_include() +#include + +namespace crc32c { + +// Computes the CRC32C of the bytes in the string_view. +inline uint32_t Crc32c(const std::string_view& string_view) { + return Crc32c(reinterpret_cast(string_view.data()), + string_view.size()); +} + +} // namespace crc32c + +#endif // __has_include() +#endif // __cplusplus > 201402L + +#endif /* defined(__cplusplus) */ + +#endif // CRC32C_CRC32C_H_ diff --git a/src/crc32c/src/crc32c.cc b/src/crc32c/src/crc32c.cc new file mode 100644 index 000000000..804133bc1 --- /dev/null +++ b/src/crc32c/src/crc32c.cc @@ -0,0 +1,39 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "crc32c/crc32c.h" + +#include +#include + +#include "./crc32c_arm64.h" +#include "./crc32c_arm64_check.h" +#include "./crc32c_internal.h" +#include "./crc32c_sse42.h" +#include "./crc32c_sse42_check.h" + +namespace crc32c { + +uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) { +#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + static bool can_use_sse42 = CanUseSse42(); + if (can_use_sse42) return ExtendSse42(crc, data, count); +#elif HAVE_ARM64_CRC32C + static bool can_use_arm64_crc32 = CanUseArm64Crc32(); + if (can_use_arm64_crc32) return ExtendArm64(crc, data, count); +#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + + return ExtendPortable(crc, data, count); +} + +extern "C" uint32_t crc32c_extend(uint32_t crc, const uint8_t* data, + size_t count) { + return crc32c::Extend(crc, data, count); +} + +extern "C" uint32_t crc32c_value(const uint8_t* data, size_t count) { + return crc32c::Crc32c(data, count); +} + +} // namespace crc32c diff --git a/src/crc32c/src/crc32c_arm64.cc b/src/crc32c/src/crc32c_arm64.cc new file mode 100644 index 000000000..711616cd2 --- /dev/null +++ b/src/crc32c/src/crc32c_arm64.cc @@ -0,0 +1,133 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "./crc32c_arm64.h" + +// In a separate source file to allow this accelerated CRC32C function to be +// compiled with the appropriate compiler flags to enable ARM NEON CRC32C +// instructions. + +// This implementation is based on https://github.com/google/leveldb/pull/490. + +#include +#include +#include + +#include "./crc32c_internal.h" +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +#if HAVE_ARM64_CRC32C + +#include +#include + +#define KBYTES 1032 +#define SEGMENTBYTES 256 + +// compute 8bytes for each segment parallelly +#define CRC32C32BYTES(P, IND) \ + do { \ + std::memcpy(&d64, (P) + SEGMENTBYTES * 1 + (IND) * 8, sizeof(d64)); \ + crc1 = __crc32cd(crc1, d64); \ + std::memcpy(&d64, (P) + SEGMENTBYTES * 2 + (IND) * 8, sizeof(d64)); \ + crc2 = __crc32cd(crc2, d64); \ + std::memcpy(&d64, (P) + SEGMENTBYTES * 3 + (IND) * 8, sizeof(d64)); \ + crc3 = __crc32cd(crc3, d64); \ + std::memcpy(&d64, (P) + SEGMENTBYTES * 0 + (IND) * 8, sizeof(d64)); \ + crc0 = __crc32cd(crc0, d64); \ + } while (0); + +// compute 8*8 bytes for each segment parallelly +#define CRC32C256BYTES(P, IND) \ + do { \ + CRC32C32BYTES((P), (IND)*8 + 0) \ + CRC32C32BYTES((P), (IND)*8 + 1) \ + CRC32C32BYTES((P), (IND)*8 + 2) \ + CRC32C32BYTES((P), (IND)*8 + 3) \ + CRC32C32BYTES((P), (IND)*8 + 4) \ + CRC32C32BYTES((P), (IND)*8 + 5) \ + CRC32C32BYTES((P), (IND)*8 + 6) \ + CRC32C32BYTES((P), (IND)*8 + 7) \ + } while (0); + +// compute 4*8*8 bytes for each segment parallelly +#define CRC32C1024BYTES(P) \ + do { \ + CRC32C256BYTES((P), 0) \ + CRC32C256BYTES((P), 1) \ + CRC32C256BYTES((P), 2) \ + CRC32C256BYTES((P), 3) \ + (P) += 4 * SEGMENTBYTES; \ + } while (0) + +namespace crc32c { + +uint32_t ExtendArm64(uint32_t crc, const uint8_t *data, size_t size) { + int64_t length = size; + uint32_t crc0, crc1, crc2, crc3; + uint64_t t0, t1, t2; + uint16_t d16; + uint32_t d32; + uint64_t d64; + + // k0=CRC(x^(3*SEGMENTBYTES*8)), k1=CRC(x^(2*SEGMENTBYTES*8)), + // k2=CRC(x^(SEGMENTBYTES*8)) + const poly64_t k0 = 0x8d96551c, k1 = 0xbd6f81f8, k2 = 0xdcb17aa4; + + crc = crc ^ kCRC32Xor; + + while (length >= KBYTES) { + crc0 = crc; + crc1 = 0; + crc2 = 0; + crc3 = 0; + + // Process 1024 bytes in parallel. + CRC32C1024BYTES(data); + + // Merge the 4 partial CRC32C values. + t2 = (uint64_t)vmull_p64(crc2, k2); + t1 = (uint64_t)vmull_p64(crc1, k1); + t0 = (uint64_t)vmull_p64(crc0, k0); + std::memcpy(&d64, data, sizeof(d64)); + crc = __crc32cd(crc3, d64); + data += sizeof(uint64_t); + crc ^= __crc32cd(0, t2); + crc ^= __crc32cd(0, t1); + crc ^= __crc32cd(0, t0); + + length -= KBYTES; + } + + while (length >= 8) { + std::memcpy(&d64, data, sizeof(d64)); + crc = __crc32cd(crc, d64); + data += 8; + length -= 8; + } + + if (length & 4) { + std::memcpy(&d32, data, sizeof(d32)); + crc = __crc32cw(crc, d32); + data += 4; + } + + if (length & 2) { + std::memcpy(&d16, data, sizeof(d16)); + crc = __crc32ch(crc, d16); + data += 2; + } + + if (length & 1) { + crc = __crc32cb(crc, *data); + } + + return crc ^ kCRC32Xor; +} + +} // namespace crc32c + +#endif // HAVE_ARM64_CRC32C diff --git a/src/crc32c/src/crc32c_arm64.h b/src/crc32c/src/crc32c_arm64.h new file mode 100644 index 000000000..e093687dd --- /dev/null +++ b/src/crc32c/src/crc32c_arm64.h @@ -0,0 +1,27 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// ARM-specific code + +#ifndef CRC32C_CRC32C_ARM_H_ +#define CRC32C_CRC32C_ARM_H_ + +#include +#include + +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +#if HAVE_ARM64_CRC32C + +namespace crc32c { + +uint32_t ExtendArm64(uint32_t crc, const uint8_t* data, size_t count); + +} // namespace crc32c + +#endif // HAVE_ARM64_CRC32C + +#endif // CRC32C_CRC32C_ARM_H_ diff --git a/src/crc32c/src/crc32c_arm64_check.h b/src/crc32c/src/crc32c_arm64_check.h new file mode 100644 index 000000000..6b80f7003 --- /dev/null +++ b/src/crc32c/src/crc32c_arm64_check.h @@ -0,0 +1,68 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +// ARM-specific code checking for the availability of CRC32C instructions. + +#ifndef CRC32C_CRC32C_ARM_CHECK_H_ +#define CRC32C_CRC32C_ARM_CHECK_H_ + +#include +#include + +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +#if HAVE_ARM64_CRC32C + +#ifdef __linux__ +#if HAVE_STRONG_GETAUXVAL +#include +#elif HAVE_WEAK_GETAUXVAL +// getauxval() is not available on Android until API level 20. Link it as a weak +// symbol. +extern "C" unsigned long getauxval(unsigned long type) __attribute__((weak)); + +#define AT_HWCAP 16 +#endif // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL +#endif // defined (__linux__) + +#ifdef __APPLE__ +#include +#include +#endif // defined (__APPLE__) + +namespace crc32c { + +inline bool CanUseArm64Crc32() { +#if defined (__linux__) && (HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL) + // From 'arch/arm64/include/uapi/asm/hwcap.h' in Linux kernel source code. + constexpr unsigned long kHWCAP_PMULL = 1 << 4; + constexpr unsigned long kHWCAP_CRC32 = 1 << 7; + unsigned long hwcap = +#if HAVE_STRONG_GETAUXVAL + // Some compilers warn on (&getauxval != nullptr) in the block below. + getauxval(AT_HWCAP); +#elif HAVE_WEAK_GETAUXVAL + (&getauxval != nullptr) ? getauxval(AT_HWCAP) : 0; +#else +#error This is supposed to be nested inside a check for HAVE_*_GETAUXVAL. +#endif // HAVE_STRONG_GETAUXVAL + return (hwcap & (kHWCAP_PMULL | kHWCAP_CRC32)) == + (kHWCAP_PMULL | kHWCAP_CRC32); +#elif defined(__APPLE__) + int val = 0; + size_t len = sizeof(val); + return sysctlbyname("hw.optional.armv8_crc32", &val, &len, nullptr, 0) == 0 + && val != 0; +#else + return false; +#endif // HAVE_STRONG_GETAUXVAL || HAVE_WEAK_GETAUXVAL +} + +} // namespace crc32c + +#endif // HAVE_ARM64_CRC32C + +#endif // CRC32C_CRC32C_ARM_CHECK_H_ diff --git a/src/crc32c/src/crc32c_arm64_unittest.cc b/src/crc32c/src/crc32c_arm64_unittest.cc new file mode 100644 index 000000000..6f917d9c0 --- /dev/null +++ b/src/crc32c/src/crc32c_arm64_unittest.cc @@ -0,0 +1,24 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "gtest/gtest.h" + +#include "./crc32c_arm64.h" +#include "./crc32c_extend_unittests.h" + +namespace crc32c { + +#if HAVE_ARM64_CRC32C + +struct Arm64TestTraits { + static uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) { + return ExtendArm64(crc, data, count); + } +}; + +INSTANTIATE_TYPED_TEST_SUITE_P(Arm64, ExtendTest, Arm64TestTraits); + +#endif // HAVE_ARM64_CRC32C + +} // namespace crc32c diff --git a/src/crc32c/src/crc32c_benchmark.cc b/src/crc32c/src/crc32c_benchmark.cc new file mode 100644 index 000000000..51194b370 --- /dev/null +++ b/src/crc32c/src/crc32c_benchmark.cc @@ -0,0 +1,106 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include +#include + +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +#include "benchmark/benchmark.h" + +#if CRC32C_TESTS_BUILT_WITH_GLOG +#include "glog/logging.h" +#endif // CRC32C_TESTS_BUILT_WITH_GLOG + +#include "./crc32c_arm64.h" +#include "./crc32c_arm64_check.h" +#include "./crc32c_internal.h" +#include "./crc32c_sse42.h" +#include "./crc32c_sse42_check.h" +#include "crc32c/crc32c.h" + +class CRC32CBenchmark : public benchmark::Fixture { + public: + void SetUp(const benchmark::State& state) override { + block_size_ = static_cast(state.range(0)); + block_data_ = std::string(block_size_, 'x'); + block_buffer_ = reinterpret_cast(block_data_.data()); + } + + protected: + std::string block_data_; + const uint8_t* block_buffer_; + size_t block_size_; +}; + +BENCHMARK_DEFINE_F(CRC32CBenchmark, Public)(benchmark::State& state) { + uint32_t crc = 0; + for (auto _ : state) + crc = crc32c::Extend(crc, block_buffer_, block_size_); + state.SetBytesProcessed(state.iterations() * block_size_); +} +BENCHMARK_REGISTER_F(CRC32CBenchmark, Public) + ->RangeMultiplier(16) + ->Range(256, 16777216); // Block size. + +BENCHMARK_DEFINE_F(CRC32CBenchmark, Portable)(benchmark::State& state) { + uint32_t crc = 0; + for (auto _ : state) + crc = crc32c::ExtendPortable(crc, block_buffer_, block_size_); + state.SetBytesProcessed(state.iterations() * block_size_); +} +BENCHMARK_REGISTER_F(CRC32CBenchmark, Portable) + ->RangeMultiplier(16) + ->Range(256, 16777216); // Block size. + +#if HAVE_ARM64_CRC32C + +BENCHMARK_DEFINE_F(CRC32CBenchmark, ArmCRC32C)(benchmark::State& state) { + if (!crc32c::CanUseArm64Crc32()) { + state.SkipWithError("ARM CRC32C instructions not available or not enabled"); + return; + } + + uint32_t crc = 0; + for (auto _ : state) + crc = crc32c::ExtendArm64(crc, block_buffer_, block_size_); + state.SetBytesProcessed(state.iterations() * block_size_); +} +BENCHMARK_REGISTER_F(CRC32CBenchmark, ArmCRC32C) + ->RangeMultiplier(16) + ->Range(256, 16777216); // Block size. + +#endif // HAVE_ARM64_CRC32C + +#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +BENCHMARK_DEFINE_F(CRC32CBenchmark, Sse42)(benchmark::State& state) { + if (!crc32c::CanUseSse42()) { + state.SkipWithError("SSE4.2 instructions not available or not enabled"); + return; + } + + uint32_t crc = 0; + for (auto _ : state) + crc = crc32c::ExtendSse42(crc, block_buffer_, block_size_); + state.SetBytesProcessed(state.iterations() * block_size_); +} +BENCHMARK_REGISTER_F(CRC32CBenchmark, Sse42) + ->RangeMultiplier(16) + ->Range(256, 16777216); // Block size. + +#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +int main(int argc, char** argv) { +#if CRC32C_TESTS_BUILT_WITH_GLOG + google::InitGoogleLogging(argv[0]); + google::InstallFailureSignalHandler(); +#endif // CRC32C_TESTS_BUILT_WITH_GLOG + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + return 0; +} diff --git a/src/crc32c/src/crc32c_capi_unittest.c b/src/crc32c/src/crc32c_capi_unittest.c new file mode 100644 index 000000000..c8993a095 --- /dev/null +++ b/src/crc32c/src/crc32c_capi_unittest.c @@ -0,0 +1,66 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "crc32c/crc32c.h" + +#include +#include +#include +#include +#include + +int main() { + /* From rfc3720 section B.4. */ + uint8_t buf[32]; + + memset(buf, 0, sizeof(buf)); + if ((uint32_t)0x8a9136aa != crc32c_value(buf, sizeof(buf))) { + printf("crc32c_value(zeros) test failed\n"); + return 1; + } + + memset(buf, 0xff, sizeof(buf)); + if ((uint32_t)0x62a8ab43 != crc32c_value(buf, sizeof(buf))) { + printf("crc32c_value(0xff) test failed\n"); + return 1; + } + + for (size_t i = 0; i < 32; ++i) + buf[i] = (uint8_t)i; + if ((uint32_t)0x46dd794e != crc32c_value(buf, sizeof(buf))) { + printf("crc32c_value(0..31) test failed\n"); + return 1; + } + + for (size_t i = 0; i < 32; ++i) + buf[i] = (uint8_t)(31 - i); + if ((uint32_t)0x113fdb5c != crc32c_value(buf, sizeof(buf))) { + printf("crc32c_value(31..0) test failed\n"); + return 1; + } + + uint8_t data[48] = { + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + if ((uint32_t)0xd9963a56 != crc32c_value(data, sizeof(data))) { + printf("crc32c_value(31..0) test failed\n"); + return 1; + } + + const uint8_t* hello_space_world = (const uint8_t*)"hello world"; + const uint8_t* hello_space = (const uint8_t*)"hello "; + const uint8_t* world = (const uint8_t*)"world"; + + if (crc32c_value(hello_space_world, 11) != + crc32c_extend(crc32c_value(hello_space, 6), world, 5)) { + printf("crc32c_extend test failed\n"); + return 1; + } + + printf("All tests passed\n"); + return 0; +} diff --git a/src/crc32c/src/crc32c_config.h.in b/src/crc32c/src/crc32c_config.h.in new file mode 100644 index 000000000..4034fa564 --- /dev/null +++ b/src/crc32c/src/crc32c_config.h.in @@ -0,0 +1,36 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef CRC32C_CRC32C_CONFIG_H_ +#define CRC32C_CRC32C_CONFIG_H_ + +// Define to 1 if building for a big-endian platform. +#cmakedefine01 BYTE_ORDER_BIG_ENDIAN + +// Define to 1 if the compiler has the __builtin_prefetch intrinsic. +#cmakedefine01 HAVE_BUILTIN_PREFETCH + +// Define to 1 if targeting X86 and the compiler has the _mm_prefetch intrinsic. +#cmakedefine01 HAVE_MM_PREFETCH + +// Define to 1 if targeting X86 and the compiler has the _mm_crc32_u{8,32,64} +// intrinsics. +#cmakedefine01 HAVE_SSE42 + +// Define to 1 if targeting ARM and the compiler has the __crc32c{b,h,w,d} and +// the vmull_p64 intrinsics. +#cmakedefine01 HAVE_ARM64_CRC32C + +// Define to 1 if the system libraries have the getauxval function in the +// header. Should be true on Linux and Android API level 20+. +#cmakedefine01 HAVE_STRONG_GETAUXVAL + +// Define to 1 if the compiler supports defining getauxval as a weak symbol. +// Should be true for any compiler that supports __attribute__((weak)). +#cmakedefine01 HAVE_WEAK_GETAUXVAL + +// Define to 1 if CRC32C tests have been built with Google Logging. +#cmakedefine01 CRC32C_TESTS_BUILT_WITH_GLOG + +#endif // CRC32C_CRC32C_CONFIG_H_ diff --git a/src/crc32c/src/crc32c_extend_unittests.h b/src/crc32c/src/crc32c_extend_unittests.h new file mode 100644 index 000000000..073297373 --- /dev/null +++ b/src/crc32c/src/crc32c_extend_unittests.h @@ -0,0 +1,112 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef CRC32C_CRC32C_EXTEND_UNITTESTS_H_ +#define CRC32C_CRC32C_EXTEND_UNITTESTS_H_ + +#include +#include +#include + +#include "gtest/gtest.h" + +// Common test cases for all implementations of CRC32C_Extend(). + +namespace crc32c { + +template +class ExtendTest : public testing::Test {}; + +TYPED_TEST_SUITE_P(ExtendTest); + +TYPED_TEST_P(ExtendTest, StandardResults) { + // From rfc3720 section B.4. + uint8_t buf[32]; + + std::memset(buf, 0, sizeof(buf)); + EXPECT_EQ(static_cast(0x8a9136aa), + TypeParam::Extend(0, buf, sizeof(buf))); + + std::memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(static_cast(0x62a8ab43), + TypeParam::Extend(0, buf, sizeof(buf))); + + for (int i = 0; i < 32; ++i) + buf[i] = static_cast(i); + EXPECT_EQ(static_cast(0x46dd794e), + TypeParam::Extend(0, buf, sizeof(buf))); + + for (int i = 0; i < 32; ++i) + buf[i] = static_cast(31 - i); + EXPECT_EQ(static_cast(0x113fdb5c), + TypeParam::Extend(0, buf, sizeof(buf))); + + uint8_t data[48] = { + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + EXPECT_EQ(static_cast(0xd9963a56), + TypeParam::Extend(0, data, sizeof(data))); +} + +TYPED_TEST_P(ExtendTest, HelloWorld) { + const uint8_t* hello_space_world = + reinterpret_cast("hello world"); + const uint8_t* hello_space = reinterpret_cast("hello "); + const uint8_t* world = reinterpret_cast("world"); + + EXPECT_EQ(TypeParam::Extend(0, hello_space_world, 11), + TypeParam::Extend(TypeParam::Extend(0, hello_space, 6), world, 5)); +} + +TYPED_TEST_P(ExtendTest, BufferSlicing) { + uint8_t buffer[48] = { + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + for (size_t i = 0; i < 48; ++i) { + for (size_t j = i + 1; j <= 48; ++j) { + uint32_t crc = 0; + + if (i > 0) crc = TypeParam::Extend(crc, buffer, i); + crc = TypeParam::Extend(crc, buffer + i, j - i); + if (j < 48) crc = TypeParam::Extend(crc, buffer + j, 48 - j); + + EXPECT_EQ(static_cast(0xd9963a56), crc); + } + } +} + +TYPED_TEST_P(ExtendTest, LargeBufferSlicing) { + uint8_t buffer[2048]; + for (size_t i = 0; i < 2048; i++) + buffer[i] = static_cast(3 * i * i + 7 * i + 11); + + for (size_t i = 0; i < 2048; ++i) { + for (size_t j = i + 1; j <= 2048; ++j) { + uint32_t crc = 0; + + if (i > 0) crc = TypeParam::Extend(crc, buffer, i); + crc = TypeParam::Extend(crc, buffer + i, j - i); + if (j < 2048) crc = TypeParam::Extend(crc, buffer + j, 2048 - j); + + EXPECT_EQ(static_cast(0x36dcc753), crc); + } + } +} + +REGISTER_TYPED_TEST_SUITE_P(ExtendTest, + StandardResults, + HelloWorld, + BufferSlicing, + LargeBufferSlicing); + +} // namespace crc32c + +#endif // CRC32C_CRC32C_EXTEND_UNITTESTS_H_ diff --git a/src/crc32c/src/crc32c_internal.h b/src/crc32c/src/crc32c_internal.h new file mode 100644 index 000000000..2bd23dea4 --- /dev/null +++ b/src/crc32c/src/crc32c_internal.h @@ -0,0 +1,23 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef CRC32C_CRC32C_INTERNAL_H_ +#define CRC32C_CRC32C_INTERNAL_H_ + +// Internal functions that may change between releases. + +#include +#include + +namespace crc32c { + +// Un-accelerated implementation that works on all CPUs. +uint32_t ExtendPortable(uint32_t crc, const uint8_t* data, size_t count); + +// CRCs are pre- and post- conditioned by xoring with all ones. +static constexpr const uint32_t kCRC32Xor = static_cast(0xffffffffU); + +} // namespace crc32c + +#endif // CRC32C_CRC32C_INTERNAL_H_ diff --git a/src/crc32c/src/crc32c_portable.cc b/src/crc32c/src/crc32c_portable.cc new file mode 100644 index 000000000..31ec6eac5 --- /dev/null +++ b/src/crc32c/src/crc32c_portable.cc @@ -0,0 +1,351 @@ +// Copyright 2008 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "./crc32c_internal.h" + +#include +#include + +#include "./crc32c_prefetch.h" +#include "./crc32c_read_le.h" +#include "./crc32c_round_up.h" + +namespace { + +const uint32_t kByteExtensionTable[256] = { + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, + 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c, + 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, + 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512, + 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, + 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf, + 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, + 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f, + 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, + 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e, + 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, + 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4, + 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, + 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5, + 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, + 0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905, + 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, + 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8, + 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, + 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6, + 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, + 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351}; + +const uint32_t kStrideExtensionTable0[256] = { + 0x00000000, 0x30d23865, 0x61a470ca, 0x517648af, 0xc348e194, 0xf39ad9f1, + 0xa2ec915e, 0x923ea93b, 0x837db5d9, 0xb3af8dbc, 0xe2d9c513, 0xd20bfd76, + 0x4035544d, 0x70e76c28, 0x21912487, 0x11431ce2, 0x03171d43, 0x33c52526, + 0x62b36d89, 0x526155ec, 0xc05ffcd7, 0xf08dc4b2, 0xa1fb8c1d, 0x9129b478, + 0x806aa89a, 0xb0b890ff, 0xe1ced850, 0xd11ce035, 0x4322490e, 0x73f0716b, + 0x228639c4, 0x125401a1, 0x062e3a86, 0x36fc02e3, 0x678a4a4c, 0x57587229, + 0xc566db12, 0xf5b4e377, 0xa4c2abd8, 0x941093bd, 0x85538f5f, 0xb581b73a, + 0xe4f7ff95, 0xd425c7f0, 0x461b6ecb, 0x76c956ae, 0x27bf1e01, 0x176d2664, + 0x053927c5, 0x35eb1fa0, 0x649d570f, 0x544f6f6a, 0xc671c651, 0xf6a3fe34, + 0xa7d5b69b, 0x97078efe, 0x8644921c, 0xb696aa79, 0xe7e0e2d6, 0xd732dab3, + 0x450c7388, 0x75de4bed, 0x24a80342, 0x147a3b27, 0x0c5c750c, 0x3c8e4d69, + 0x6df805c6, 0x5d2a3da3, 0xcf149498, 0xffc6acfd, 0xaeb0e452, 0x9e62dc37, + 0x8f21c0d5, 0xbff3f8b0, 0xee85b01f, 0xde57887a, 0x4c692141, 0x7cbb1924, + 0x2dcd518b, 0x1d1f69ee, 0x0f4b684f, 0x3f99502a, 0x6eef1885, 0x5e3d20e0, + 0xcc0389db, 0xfcd1b1be, 0xada7f911, 0x9d75c174, 0x8c36dd96, 0xbce4e5f3, + 0xed92ad5c, 0xdd409539, 0x4f7e3c02, 0x7fac0467, 0x2eda4cc8, 0x1e0874ad, + 0x0a724f8a, 0x3aa077ef, 0x6bd63f40, 0x5b040725, 0xc93aae1e, 0xf9e8967b, + 0xa89eded4, 0x984ce6b1, 0x890ffa53, 0xb9ddc236, 0xe8ab8a99, 0xd879b2fc, + 0x4a471bc7, 0x7a9523a2, 0x2be36b0d, 0x1b315368, 0x096552c9, 0x39b76aac, + 0x68c12203, 0x58131a66, 0xca2db35d, 0xfaff8b38, 0xab89c397, 0x9b5bfbf2, + 0x8a18e710, 0xbacadf75, 0xebbc97da, 0xdb6eafbf, 0x49500684, 0x79823ee1, + 0x28f4764e, 0x18264e2b, 0x18b8ea18, 0x286ad27d, 0x791c9ad2, 0x49cea2b7, + 0xdbf00b8c, 0xeb2233e9, 0xba547b46, 0x8a864323, 0x9bc55fc1, 0xab1767a4, + 0xfa612f0b, 0xcab3176e, 0x588dbe55, 0x685f8630, 0x3929ce9f, 0x09fbf6fa, + 0x1baff75b, 0x2b7dcf3e, 0x7a0b8791, 0x4ad9bff4, 0xd8e716cf, 0xe8352eaa, + 0xb9436605, 0x89915e60, 0x98d24282, 0xa8007ae7, 0xf9763248, 0xc9a40a2d, + 0x5b9aa316, 0x6b489b73, 0x3a3ed3dc, 0x0aecebb9, 0x1e96d09e, 0x2e44e8fb, + 0x7f32a054, 0x4fe09831, 0xddde310a, 0xed0c096f, 0xbc7a41c0, 0x8ca879a5, + 0x9deb6547, 0xad395d22, 0xfc4f158d, 0xcc9d2de8, 0x5ea384d3, 0x6e71bcb6, + 0x3f07f419, 0x0fd5cc7c, 0x1d81cddd, 0x2d53f5b8, 0x7c25bd17, 0x4cf78572, + 0xdec92c49, 0xee1b142c, 0xbf6d5c83, 0x8fbf64e6, 0x9efc7804, 0xae2e4061, + 0xff5808ce, 0xcf8a30ab, 0x5db49990, 0x6d66a1f5, 0x3c10e95a, 0x0cc2d13f, + 0x14e49f14, 0x2436a771, 0x7540efde, 0x4592d7bb, 0xd7ac7e80, 0xe77e46e5, + 0xb6080e4a, 0x86da362f, 0x97992acd, 0xa74b12a8, 0xf63d5a07, 0xc6ef6262, + 0x54d1cb59, 0x6403f33c, 0x3575bb93, 0x05a783f6, 0x17f38257, 0x2721ba32, + 0x7657f29d, 0x4685caf8, 0xd4bb63c3, 0xe4695ba6, 0xb51f1309, 0x85cd2b6c, + 0x948e378e, 0xa45c0feb, 0xf52a4744, 0xc5f87f21, 0x57c6d61a, 0x6714ee7f, + 0x3662a6d0, 0x06b09eb5, 0x12caa592, 0x22189df7, 0x736ed558, 0x43bced3d, + 0xd1824406, 0xe1507c63, 0xb02634cc, 0x80f40ca9, 0x91b7104b, 0xa165282e, + 0xf0136081, 0xc0c158e4, 0x52fff1df, 0x622dc9ba, 0x335b8115, 0x0389b970, + 0x11ddb8d1, 0x210f80b4, 0x7079c81b, 0x40abf07e, 0xd2955945, 0xe2476120, + 0xb331298f, 0x83e311ea, 0x92a00d08, 0xa272356d, 0xf3047dc2, 0xc3d645a7, + 0x51e8ec9c, 0x613ad4f9, 0x304c9c56, 0x009ea433}; + +const uint32_t kStrideExtensionTable1[256] = { + 0x00000000, 0x54075546, 0xa80eaa8c, 0xfc09ffca, 0x55f123e9, 0x01f676af, + 0xfdff8965, 0xa9f8dc23, 0xabe247d2, 0xffe51294, 0x03eced5e, 0x57ebb818, + 0xfe13643b, 0xaa14317d, 0x561dceb7, 0x021a9bf1, 0x5228f955, 0x062fac13, + 0xfa2653d9, 0xae21069f, 0x07d9dabc, 0x53de8ffa, 0xafd77030, 0xfbd02576, + 0xf9cabe87, 0xadcdebc1, 0x51c4140b, 0x05c3414d, 0xac3b9d6e, 0xf83cc828, + 0x043537e2, 0x503262a4, 0xa451f2aa, 0xf056a7ec, 0x0c5f5826, 0x58580d60, + 0xf1a0d143, 0xa5a78405, 0x59ae7bcf, 0x0da92e89, 0x0fb3b578, 0x5bb4e03e, + 0xa7bd1ff4, 0xf3ba4ab2, 0x5a429691, 0x0e45c3d7, 0xf24c3c1d, 0xa64b695b, + 0xf6790bff, 0xa27e5eb9, 0x5e77a173, 0x0a70f435, 0xa3882816, 0xf78f7d50, + 0x0b86829a, 0x5f81d7dc, 0x5d9b4c2d, 0x099c196b, 0xf595e6a1, 0xa192b3e7, + 0x086a6fc4, 0x5c6d3a82, 0xa064c548, 0xf463900e, 0x4d4f93a5, 0x1948c6e3, + 0xe5413929, 0xb1466c6f, 0x18beb04c, 0x4cb9e50a, 0xb0b01ac0, 0xe4b74f86, + 0xe6add477, 0xb2aa8131, 0x4ea37efb, 0x1aa42bbd, 0xb35cf79e, 0xe75ba2d8, + 0x1b525d12, 0x4f550854, 0x1f676af0, 0x4b603fb6, 0xb769c07c, 0xe36e953a, + 0x4a964919, 0x1e911c5f, 0xe298e395, 0xb69fb6d3, 0xb4852d22, 0xe0827864, + 0x1c8b87ae, 0x488cd2e8, 0xe1740ecb, 0xb5735b8d, 0x497aa447, 0x1d7df101, + 0xe91e610f, 0xbd193449, 0x4110cb83, 0x15179ec5, 0xbcef42e6, 0xe8e817a0, + 0x14e1e86a, 0x40e6bd2c, 0x42fc26dd, 0x16fb739b, 0xeaf28c51, 0xbef5d917, + 0x170d0534, 0x430a5072, 0xbf03afb8, 0xeb04fafe, 0xbb36985a, 0xef31cd1c, + 0x133832d6, 0x473f6790, 0xeec7bbb3, 0xbac0eef5, 0x46c9113f, 0x12ce4479, + 0x10d4df88, 0x44d38ace, 0xb8da7504, 0xecdd2042, 0x4525fc61, 0x1122a927, + 0xed2b56ed, 0xb92c03ab, 0x9a9f274a, 0xce98720c, 0x32918dc6, 0x6696d880, + 0xcf6e04a3, 0x9b6951e5, 0x6760ae2f, 0x3367fb69, 0x317d6098, 0x657a35de, + 0x9973ca14, 0xcd749f52, 0x648c4371, 0x308b1637, 0xcc82e9fd, 0x9885bcbb, + 0xc8b7de1f, 0x9cb08b59, 0x60b97493, 0x34be21d5, 0x9d46fdf6, 0xc941a8b0, + 0x3548577a, 0x614f023c, 0x635599cd, 0x3752cc8b, 0xcb5b3341, 0x9f5c6607, + 0x36a4ba24, 0x62a3ef62, 0x9eaa10a8, 0xcaad45ee, 0x3eced5e0, 0x6ac980a6, + 0x96c07f6c, 0xc2c72a2a, 0x6b3ff609, 0x3f38a34f, 0xc3315c85, 0x973609c3, + 0x952c9232, 0xc12bc774, 0x3d2238be, 0x69256df8, 0xc0ddb1db, 0x94dae49d, + 0x68d31b57, 0x3cd44e11, 0x6ce62cb5, 0x38e179f3, 0xc4e88639, 0x90efd37f, + 0x39170f5c, 0x6d105a1a, 0x9119a5d0, 0xc51ef096, 0xc7046b67, 0x93033e21, + 0x6f0ac1eb, 0x3b0d94ad, 0x92f5488e, 0xc6f21dc8, 0x3afbe202, 0x6efcb744, + 0xd7d0b4ef, 0x83d7e1a9, 0x7fde1e63, 0x2bd94b25, 0x82219706, 0xd626c240, + 0x2a2f3d8a, 0x7e2868cc, 0x7c32f33d, 0x2835a67b, 0xd43c59b1, 0x803b0cf7, + 0x29c3d0d4, 0x7dc48592, 0x81cd7a58, 0xd5ca2f1e, 0x85f84dba, 0xd1ff18fc, + 0x2df6e736, 0x79f1b270, 0xd0096e53, 0x840e3b15, 0x7807c4df, 0x2c009199, + 0x2e1a0a68, 0x7a1d5f2e, 0x8614a0e4, 0xd213f5a2, 0x7beb2981, 0x2fec7cc7, + 0xd3e5830d, 0x87e2d64b, 0x73814645, 0x27861303, 0xdb8fecc9, 0x8f88b98f, + 0x267065ac, 0x727730ea, 0x8e7ecf20, 0xda799a66, 0xd8630197, 0x8c6454d1, + 0x706dab1b, 0x246afe5d, 0x8d92227e, 0xd9957738, 0x259c88f2, 0x719bddb4, + 0x21a9bf10, 0x75aeea56, 0x89a7159c, 0xdda040da, 0x74589cf9, 0x205fc9bf, + 0xdc563675, 0x88516333, 0x8a4bf8c2, 0xde4cad84, 0x2245524e, 0x76420708, + 0xdfbadb2b, 0x8bbd8e6d, 0x77b471a7, 0x23b324e1}; + +const uint32_t kStrideExtensionTable2[256] = { + 0x00000000, 0x678efd01, 0xcf1dfa02, 0xa8930703, 0x9bd782f5, 0xfc597ff4, + 0x54ca78f7, 0x334485f6, 0x3243731b, 0x55cd8e1a, 0xfd5e8919, 0x9ad07418, + 0xa994f1ee, 0xce1a0cef, 0x66890bec, 0x0107f6ed, 0x6486e636, 0x03081b37, + 0xab9b1c34, 0xcc15e135, 0xff5164c3, 0x98df99c2, 0x304c9ec1, 0x57c263c0, + 0x56c5952d, 0x314b682c, 0x99d86f2f, 0xfe56922e, 0xcd1217d8, 0xaa9cead9, + 0x020fedda, 0x658110db, 0xc90dcc6c, 0xae83316d, 0x0610366e, 0x619ecb6f, + 0x52da4e99, 0x3554b398, 0x9dc7b49b, 0xfa49499a, 0xfb4ebf77, 0x9cc04276, + 0x34534575, 0x53ddb874, 0x60993d82, 0x0717c083, 0xaf84c780, 0xc80a3a81, + 0xad8b2a5a, 0xca05d75b, 0x6296d058, 0x05182d59, 0x365ca8af, 0x51d255ae, + 0xf94152ad, 0x9ecfafac, 0x9fc85941, 0xf846a440, 0x50d5a343, 0x375b5e42, + 0x041fdbb4, 0x639126b5, 0xcb0221b6, 0xac8cdcb7, 0x97f7ee29, 0xf0791328, + 0x58ea142b, 0x3f64e92a, 0x0c206cdc, 0x6bae91dd, 0xc33d96de, 0xa4b36bdf, + 0xa5b49d32, 0xc23a6033, 0x6aa96730, 0x0d279a31, 0x3e631fc7, 0x59ede2c6, + 0xf17ee5c5, 0x96f018c4, 0xf371081f, 0x94fff51e, 0x3c6cf21d, 0x5be20f1c, + 0x68a68aea, 0x0f2877eb, 0xa7bb70e8, 0xc0358de9, 0xc1327b04, 0xa6bc8605, + 0x0e2f8106, 0x69a17c07, 0x5ae5f9f1, 0x3d6b04f0, 0x95f803f3, 0xf276fef2, + 0x5efa2245, 0x3974df44, 0x91e7d847, 0xf6692546, 0xc52da0b0, 0xa2a35db1, + 0x0a305ab2, 0x6dbea7b3, 0x6cb9515e, 0x0b37ac5f, 0xa3a4ab5c, 0xc42a565d, + 0xf76ed3ab, 0x90e02eaa, 0x387329a9, 0x5ffdd4a8, 0x3a7cc473, 0x5df23972, + 0xf5613e71, 0x92efc370, 0xa1ab4686, 0xc625bb87, 0x6eb6bc84, 0x09384185, + 0x083fb768, 0x6fb14a69, 0xc7224d6a, 0xa0acb06b, 0x93e8359d, 0xf466c89c, + 0x5cf5cf9f, 0x3b7b329e, 0x2a03aaa3, 0x4d8d57a2, 0xe51e50a1, 0x8290ada0, + 0xb1d42856, 0xd65ad557, 0x7ec9d254, 0x19472f55, 0x1840d9b8, 0x7fce24b9, + 0xd75d23ba, 0xb0d3debb, 0x83975b4d, 0xe419a64c, 0x4c8aa14f, 0x2b045c4e, + 0x4e854c95, 0x290bb194, 0x8198b697, 0xe6164b96, 0xd552ce60, 0xb2dc3361, + 0x1a4f3462, 0x7dc1c963, 0x7cc63f8e, 0x1b48c28f, 0xb3dbc58c, 0xd455388d, + 0xe711bd7b, 0x809f407a, 0x280c4779, 0x4f82ba78, 0xe30e66cf, 0x84809bce, + 0x2c139ccd, 0x4b9d61cc, 0x78d9e43a, 0x1f57193b, 0xb7c41e38, 0xd04ae339, + 0xd14d15d4, 0xb6c3e8d5, 0x1e50efd6, 0x79de12d7, 0x4a9a9721, 0x2d146a20, + 0x85876d23, 0xe2099022, 0x878880f9, 0xe0067df8, 0x48957afb, 0x2f1b87fa, + 0x1c5f020c, 0x7bd1ff0d, 0xd342f80e, 0xb4cc050f, 0xb5cbf3e2, 0xd2450ee3, + 0x7ad609e0, 0x1d58f4e1, 0x2e1c7117, 0x49928c16, 0xe1018b15, 0x868f7614, + 0xbdf4448a, 0xda7ab98b, 0x72e9be88, 0x15674389, 0x2623c67f, 0x41ad3b7e, + 0xe93e3c7d, 0x8eb0c17c, 0x8fb73791, 0xe839ca90, 0x40aacd93, 0x27243092, + 0x1460b564, 0x73ee4865, 0xdb7d4f66, 0xbcf3b267, 0xd972a2bc, 0xbefc5fbd, + 0x166f58be, 0x71e1a5bf, 0x42a52049, 0x252bdd48, 0x8db8da4b, 0xea36274a, + 0xeb31d1a7, 0x8cbf2ca6, 0x242c2ba5, 0x43a2d6a4, 0x70e65352, 0x1768ae53, + 0xbffba950, 0xd8755451, 0x74f988e6, 0x137775e7, 0xbbe472e4, 0xdc6a8fe5, + 0xef2e0a13, 0x88a0f712, 0x2033f011, 0x47bd0d10, 0x46bafbfd, 0x213406fc, + 0x89a701ff, 0xee29fcfe, 0xdd6d7908, 0xbae38409, 0x1270830a, 0x75fe7e0b, + 0x107f6ed0, 0x77f193d1, 0xdf6294d2, 0xb8ec69d3, 0x8ba8ec25, 0xec261124, + 0x44b51627, 0x233beb26, 0x223c1dcb, 0x45b2e0ca, 0xed21e7c9, 0x8aaf1ac8, + 0xb9eb9f3e, 0xde65623f, 0x76f6653c, 0x1178983d}; + +const uint32_t kStrideExtensionTable3[256] = { + 0x00000000, 0xf20c0dfe, 0xe1f46d0d, 0x13f860f3, 0xc604aceb, 0x3408a115, + 0x27f0c1e6, 0xd5fccc18, 0x89e52f27, 0x7be922d9, 0x6811422a, 0x9a1d4fd4, + 0x4fe183cc, 0xbded8e32, 0xae15eec1, 0x5c19e33f, 0x162628bf, 0xe42a2541, + 0xf7d245b2, 0x05de484c, 0xd0228454, 0x222e89aa, 0x31d6e959, 0xc3dae4a7, + 0x9fc30798, 0x6dcf0a66, 0x7e376a95, 0x8c3b676b, 0x59c7ab73, 0xabcba68d, + 0xb833c67e, 0x4a3fcb80, 0x2c4c517e, 0xde405c80, 0xcdb83c73, 0x3fb4318d, + 0xea48fd95, 0x1844f06b, 0x0bbc9098, 0xf9b09d66, 0xa5a97e59, 0x57a573a7, + 0x445d1354, 0xb6511eaa, 0x63add2b2, 0x91a1df4c, 0x8259bfbf, 0x7055b241, + 0x3a6a79c1, 0xc866743f, 0xdb9e14cc, 0x29921932, 0xfc6ed52a, 0x0e62d8d4, + 0x1d9ab827, 0xef96b5d9, 0xb38f56e6, 0x41835b18, 0x527b3beb, 0xa0773615, + 0x758bfa0d, 0x8787f7f3, 0x947f9700, 0x66739afe, 0x5898a2fc, 0xaa94af02, + 0xb96ccff1, 0x4b60c20f, 0x9e9c0e17, 0x6c9003e9, 0x7f68631a, 0x8d646ee4, + 0xd17d8ddb, 0x23718025, 0x3089e0d6, 0xc285ed28, 0x17792130, 0xe5752cce, + 0xf68d4c3d, 0x048141c3, 0x4ebe8a43, 0xbcb287bd, 0xaf4ae74e, 0x5d46eab0, + 0x88ba26a8, 0x7ab62b56, 0x694e4ba5, 0x9b42465b, 0xc75ba564, 0x3557a89a, + 0x26afc869, 0xd4a3c597, 0x015f098f, 0xf3530471, 0xe0ab6482, 0x12a7697c, + 0x74d4f382, 0x86d8fe7c, 0x95209e8f, 0x672c9371, 0xb2d05f69, 0x40dc5297, + 0x53243264, 0xa1283f9a, 0xfd31dca5, 0x0f3dd15b, 0x1cc5b1a8, 0xeec9bc56, + 0x3b35704e, 0xc9397db0, 0xdac11d43, 0x28cd10bd, 0x62f2db3d, 0x90fed6c3, + 0x8306b630, 0x710abbce, 0xa4f677d6, 0x56fa7a28, 0x45021adb, 0xb70e1725, + 0xeb17f41a, 0x191bf9e4, 0x0ae39917, 0xf8ef94e9, 0x2d1358f1, 0xdf1f550f, + 0xcce735fc, 0x3eeb3802, 0xb13145f8, 0x433d4806, 0x50c528f5, 0xa2c9250b, + 0x7735e913, 0x8539e4ed, 0x96c1841e, 0x64cd89e0, 0x38d46adf, 0xcad86721, + 0xd92007d2, 0x2b2c0a2c, 0xfed0c634, 0x0cdccbca, 0x1f24ab39, 0xed28a6c7, + 0xa7176d47, 0x551b60b9, 0x46e3004a, 0xb4ef0db4, 0x6113c1ac, 0x931fcc52, + 0x80e7aca1, 0x72eba15f, 0x2ef24260, 0xdcfe4f9e, 0xcf062f6d, 0x3d0a2293, + 0xe8f6ee8b, 0x1afae375, 0x09028386, 0xfb0e8e78, 0x9d7d1486, 0x6f711978, + 0x7c89798b, 0x8e857475, 0x5b79b86d, 0xa975b593, 0xba8dd560, 0x4881d89e, + 0x14983ba1, 0xe694365f, 0xf56c56ac, 0x07605b52, 0xd29c974a, 0x20909ab4, + 0x3368fa47, 0xc164f7b9, 0x8b5b3c39, 0x795731c7, 0x6aaf5134, 0x98a35cca, + 0x4d5f90d2, 0xbf539d2c, 0xacabfddf, 0x5ea7f021, 0x02be131e, 0xf0b21ee0, + 0xe34a7e13, 0x114673ed, 0xc4babff5, 0x36b6b20b, 0x254ed2f8, 0xd742df06, + 0xe9a9e704, 0x1ba5eafa, 0x085d8a09, 0xfa5187f7, 0x2fad4bef, 0xdda14611, + 0xce5926e2, 0x3c552b1c, 0x604cc823, 0x9240c5dd, 0x81b8a52e, 0x73b4a8d0, + 0xa64864c8, 0x54446936, 0x47bc09c5, 0xb5b0043b, 0xff8fcfbb, 0x0d83c245, + 0x1e7ba2b6, 0xec77af48, 0x398b6350, 0xcb876eae, 0xd87f0e5d, 0x2a7303a3, + 0x766ae09c, 0x8466ed62, 0x979e8d91, 0x6592806f, 0xb06e4c77, 0x42624189, + 0x519a217a, 0xa3962c84, 0xc5e5b67a, 0x37e9bb84, 0x2411db77, 0xd61dd689, + 0x03e11a91, 0xf1ed176f, 0xe215779c, 0x10197a62, 0x4c00995d, 0xbe0c94a3, + 0xadf4f450, 0x5ff8f9ae, 0x8a0435b6, 0x78083848, 0x6bf058bb, 0x99fc5545, + 0xd3c39ec5, 0x21cf933b, 0x3237f3c8, 0xc03bfe36, 0x15c7322e, 0xe7cb3fd0, + 0xf4335f23, 0x063f52dd, 0x5a26b1e2, 0xa82abc1c, 0xbbd2dcef, 0x49ded111, + 0x9c221d09, 0x6e2e10f7, 0x7dd67004, 0x8fda7dfa}; + +constexpr const ptrdiff_t kPrefetchHorizon = 256; + +} // namespace + +namespace crc32c { + +uint32_t ExtendPortable(uint32_t crc, const uint8_t* data, size_t size) { + const uint8_t* p = data; + const uint8_t* e = p + size; + uint32_t l = crc ^ kCRC32Xor; + +// Process one byte at a time. +#define STEP1 \ + do { \ + int c = (l & 0xff) ^ *p++; \ + l = kByteExtensionTable[c] ^ (l >> 8); \ + } while (0) + +// Process one of the 4 strides of 4-byte data. +#define STEP4(s) \ + do { \ + crc##s = ReadUint32LE(p + s * 4) ^ kStrideExtensionTable3[crc##s & 0xff] ^ \ + kStrideExtensionTable2[(crc##s >> 8) & 0xff] ^ \ + kStrideExtensionTable1[(crc##s >> 16) & 0xff] ^ \ + kStrideExtensionTable0[crc##s >> 24]; \ + } while (0) + +// Process a 16-byte swath of 4 strides, each of which has 4 bytes of data. +#define STEP16 \ + do { \ + STEP4(0); \ + STEP4(1); \ + STEP4(2); \ + STEP4(3); \ + p += 16; \ + } while (0) + +// Process 4 bytes that were already loaded into a word. +#define STEP4W(w) \ + do { \ + w ^= l; \ + for (size_t i = 0; i < 4; ++i) { \ + w = (w >> 8) ^ kByteExtensionTable[w & 0xff]; \ + } \ + l = w; \ + } while (0) + + // Point x at first 4-byte aligned byte in the buffer. This might be past the + // end of the buffer. + const uint8_t* x = RoundUp<4>(p); + if (x <= e) { + // Process bytes p is 4-byte aligned. + while (p != x) { + STEP1; + } + } + + if ((e - p) >= 16) { + // Load a 16-byte swath into the stride partial results. + uint32_t crc0 = ReadUint32LE(p + 0 * 4) ^ l; + uint32_t crc1 = ReadUint32LE(p + 1 * 4); + uint32_t crc2 = ReadUint32LE(p + 2 * 4); + uint32_t crc3 = ReadUint32LE(p + 3 * 4); + p += 16; + + while ((e - p) > kPrefetchHorizon) { + RequestPrefetch(p + kPrefetchHorizon); + + // Process 64 bytes at a time. + STEP16; + STEP16; + STEP16; + STEP16; + } + + // Process one 16-byte swath at a time. + while ((e - p) >= 16) { + STEP16; + } + + // Advance one word at a time as far as possible. + while ((e - p) >= 4) { + STEP4(0); + uint32_t tmp = crc0; + crc0 = crc1; + crc1 = crc2; + crc2 = crc3; + crc3 = tmp; + p += 4; + } + + // Combine the 4 partial stride results. + l = 0; + STEP4W(crc0); + STEP4W(crc1); + STEP4W(crc2); + STEP4W(crc3); + } + + // Process the last few bytes. + while (p != e) { + STEP1; + } +#undef STEP4W +#undef STEP16 +#undef STEP4 +#undef STEP1 + return l ^ kCRC32Xor; +} + +} // namespace crc32c diff --git a/src/crc32c/src/crc32c_portable_unittest.cc b/src/crc32c/src/crc32c_portable_unittest.cc new file mode 100644 index 000000000..5098e2c37 --- /dev/null +++ b/src/crc32c/src/crc32c_portable_unittest.cc @@ -0,0 +1,20 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "gtest/gtest.h" + +#include "./crc32c_extend_unittests.h" +#include "./crc32c_internal.h" + +namespace crc32c { + +struct PortableTestTraits { + static uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) { + return ExtendPortable(crc, data, count); + } +}; + +INSTANTIATE_TYPED_TEST_SUITE_P(Portable, ExtendTest, PortableTestTraits); + +} // namespace crc32c diff --git a/src/crc32c/src/crc32c_prefetch.h b/src/crc32c/src/crc32c_prefetch.h new file mode 100644 index 000000000..aec7d54e8 --- /dev/null +++ b/src/crc32c/src/crc32c_prefetch.h @@ -0,0 +1,46 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef CRC32C_CRC32C_PREFETCH_H_ +#define CRC32C_CRC32C_PREFETCH_H_ + +#include +#include + +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +#if HAVE_MM_PREFETCH + +#if defined(_MSC_VER) +#include +#else // !defined(_MSC_VER) +#include +#endif // defined(_MSC_VER) + +#endif // HAVE_MM_PREFETCH + +namespace crc32c { + +// Ask the hardware to prefetch the data at the given address into the L1 cache. +inline void RequestPrefetch(const uint8_t* address) { +#if HAVE_BUILTIN_PREFETCH + // Clang and GCC implement the __builtin_prefetch non-standard extension, + // which maps to the best instruction on the target architecture. + __builtin_prefetch(reinterpret_cast(address), 0 /* Read only. */, + 0 /* No temporal locality. */); +#elif HAVE_MM_PREFETCH + // Visual Studio doesn't implement __builtin_prefetch, but exposes the + // PREFETCHNTA instruction via the _mm_prefetch intrinsic. + _mm_prefetch(reinterpret_cast(address), _MM_HINT_NTA); +#else + // No prefetch support. Silence compiler warnings. + (void)address; +#endif // HAVE_BUILTIN_PREFETCH +} + +} // namespace crc32c + +#endif // CRC32C_CRC32C_ROUND_UP_H_ diff --git a/src/crc32c/src/crc32c_prefetch_unittest.cc b/src/crc32c/src/crc32c_prefetch_unittest.cc new file mode 100644 index 000000000..b34ed2d5f --- /dev/null +++ b/src/crc32c/src/crc32c_prefetch_unittest.cc @@ -0,0 +1,9 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "./crc32c_prefetch.h" + +// There is no easy way to test cache prefetching. We can only test that the +// crc32c_prefetch.h header compiles on its own, so it doesn't have any unstated +// dependencies. diff --git a/src/crc32c/src/crc32c_read_le.h b/src/crc32c/src/crc32c_read_le.h new file mode 100644 index 000000000..673a2a0db --- /dev/null +++ b/src/crc32c/src/crc32c_read_le.h @@ -0,0 +1,53 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef CRC32C_CRC32C_READ_LE_H_ +#define CRC32C_CRC32C_READ_LE_H_ + +#include +#include + +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +namespace crc32c { + +// Reads a little-endian 32-bit integer from a 32-bit-aligned buffer. +inline uint32_t ReadUint32LE(const uint8_t* buffer) { +#if BYTE_ORDER_BIG_ENDIAN + return ((static_cast(static_cast(buffer[0]))) | + (static_cast(static_cast(buffer[1])) << 8) | + (static_cast(static_cast(buffer[2])) << 16) | + (static_cast(static_cast(buffer[3])) << 24)); +#else // !BYTE_ORDER_BIG_ENDIAN + uint32_t result; + // This should be optimized to a single instruction. + std::memcpy(&result, buffer, sizeof(result)); + return result; +#endif // BYTE_ORDER_BIG_ENDIAN +} + +// Reads a little-endian 64-bit integer from a 64-bit-aligned buffer. +inline uint64_t ReadUint64LE(const uint8_t* buffer) { +#if BYTE_ORDER_BIG_ENDIAN + return ((static_cast(static_cast(buffer[0]))) | + (static_cast(static_cast(buffer[1])) << 8) | + (static_cast(static_cast(buffer[2])) << 16) | + (static_cast(static_cast(buffer[3])) << 24) | + (static_cast(static_cast(buffer[4])) << 32) | + (static_cast(static_cast(buffer[5])) << 40) | + (static_cast(static_cast(buffer[6])) << 48) | + (static_cast(static_cast(buffer[7])) << 56)); +#else // !BYTE_ORDER_BIG_ENDIAN + uint64_t result; + // This should be optimized to a single instruction. + std::memcpy(&result, buffer, sizeof(result)); + return result; +#endif // BYTE_ORDER_BIG_ENDIAN +} + +} // namespace crc32c + +#endif // CRC32C_CRC32C_READ_LE_H_ diff --git a/src/crc32c/src/crc32c_read_le_unittest.cc b/src/crc32c/src/crc32c_read_le_unittest.cc new file mode 100644 index 000000000..2a30302ad --- /dev/null +++ b/src/crc32c/src/crc32c_read_le_unittest.cc @@ -0,0 +1,32 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "./crc32c_read_le.h" + +#include +#include + +#include "gtest/gtest.h" + +#include "./crc32c_round_up.h" + +namespace crc32c { + +TEST(Crc32CReadLETest, ReadUint32LE) { + // little-endian 0x12345678 + alignas(4) uint8_t bytes[] = {0x78, 0x56, 0x34, 0x12}; + + ASSERT_EQ(RoundUp<4>(bytes), bytes) << "Stack array is not aligned"; + EXPECT_EQ(static_cast(0x12345678), ReadUint32LE(bytes)); +} + +TEST(Crc32CReadLETest, ReadUint64LE) { + // little-endian 0x123456789ABCDEF0 + alignas(8) uint8_t bytes[] = {0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}; + + ASSERT_EQ(RoundUp<8>(bytes), bytes) << "Stack array is not aligned"; + EXPECT_EQ(static_cast(0x123456789ABCDEF0), ReadUint64LE(bytes)); +} + +} // namespace crc32c diff --git a/src/crc32c/src/crc32c_round_up.h b/src/crc32c/src/crc32c_round_up.h new file mode 100644 index 000000000..d3b922beb --- /dev/null +++ b/src/crc32c/src/crc32c_round_up.h @@ -0,0 +1,34 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef CRC32C_CRC32C_ROUND_UP_H_ +#define CRC32C_CRC32C_ROUND_UP_H_ + +#include +#include + +namespace crc32c { + +// Returns the smallest number >= the given number that is evenly divided by N. +// +// N must be a power of two. +template +constexpr inline uintptr_t RoundUp(uintptr_t pointer) { + static_assert((N & (N - 1)) == 0, "N must be a power of two"); + return (pointer + (N - 1)) & ~(N - 1); +} + +// Returns the smallest address >= the given address that is aligned to N bytes. +// +// N must be a power of two. +template +constexpr inline const uint8_t* RoundUp(const uint8_t* pointer) { + static_assert((N & (N - 1)) == 0, "N must be a power of two"); + return reinterpret_cast( + RoundUp(reinterpret_cast(pointer))); +} + +} // namespace crc32c + +#endif // CRC32C_CRC32C_ROUND_UP_H_ diff --git a/src/crc32c/src/crc32c_round_up_unittest.cc b/src/crc32c/src/crc32c_round_up_unittest.cc new file mode 100644 index 000000000..5ff657bb5 --- /dev/null +++ b/src/crc32c/src/crc32c_round_up_unittest.cc @@ -0,0 +1,84 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "./crc32c_round_up.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace crc32c { + +TEST(CRC32CRoundUpTest, RoundUpUintptr) { + uintptr_t zero = 0; + + ASSERT_EQ(zero, RoundUp<1>(zero)); + ASSERT_EQ(1U, RoundUp<1>(1U)); + ASSERT_EQ(2U, RoundUp<1>(2U)); + ASSERT_EQ(3U, RoundUp<1>(3U)); + ASSERT_EQ(~static_cast(0), RoundUp<1>(~static_cast(0))); + ASSERT_EQ(~static_cast(1), RoundUp<1>(~static_cast(1))); + ASSERT_EQ(~static_cast(2), RoundUp<1>(~static_cast(2))); + ASSERT_EQ(~static_cast(3), RoundUp<1>(~static_cast(3))); + + ASSERT_EQ(zero, RoundUp<2>(zero)); + ASSERT_EQ(2U, RoundUp<2>(1U)); + ASSERT_EQ(2U, RoundUp<2>(2U)); + ASSERT_EQ(4U, RoundUp<2>(3U)); + ASSERT_EQ(4U, RoundUp<2>(4U)); + ASSERT_EQ(6U, RoundUp<2>(5U)); + ASSERT_EQ(6U, RoundUp<2>(6U)); + ASSERT_EQ(8U, RoundUp<2>(7U)); + ASSERT_EQ(8U, RoundUp<2>(8U)); + ASSERT_EQ(~static_cast(1), RoundUp<2>(~static_cast(1))); + ASSERT_EQ(~static_cast(1), RoundUp<2>(~static_cast(2))); + ASSERT_EQ(~static_cast(3), RoundUp<2>(~static_cast(3))); + ASSERT_EQ(~static_cast(3), RoundUp<2>(~static_cast(4))); + + ASSERT_EQ(zero, RoundUp<4>(zero)); + ASSERT_EQ(4U, RoundUp<4>(1U)); + ASSERT_EQ(4U, RoundUp<4>(2U)); + ASSERT_EQ(4U, RoundUp<4>(3U)); + ASSERT_EQ(4U, RoundUp<4>(4U)); + ASSERT_EQ(8U, RoundUp<4>(5U)); + ASSERT_EQ(8U, RoundUp<4>(6U)); + ASSERT_EQ(8U, RoundUp<4>(7U)); + ASSERT_EQ(8U, RoundUp<4>(8U)); + ASSERT_EQ(~static_cast(3), RoundUp<4>(~static_cast(3))); + ASSERT_EQ(~static_cast(3), RoundUp<4>(~static_cast(4))); + ASSERT_EQ(~static_cast(3), RoundUp<4>(~static_cast(5))); + ASSERT_EQ(~static_cast(3), RoundUp<4>(~static_cast(6))); + ASSERT_EQ(~static_cast(7), RoundUp<4>(~static_cast(7))); + ASSERT_EQ(~static_cast(7), RoundUp<4>(~static_cast(8))); + ASSERT_EQ(~static_cast(7), RoundUp<4>(~static_cast(9))); +} + +TEST(CRC32CRoundUpTest, RoundUpPointer) { + uintptr_t zero = 0, three = 3, four = 4, seven = 7, eight = 8; + + const uint8_t* zero_ptr = reinterpret_cast(zero); + const uint8_t* three_ptr = reinterpret_cast(three); + const uint8_t* four_ptr = reinterpret_cast(four); + const uint8_t* seven_ptr = reinterpret_cast(seven); + const uint8_t* eight_ptr = reinterpret_cast(eight); + + ASSERT_EQ(zero_ptr, RoundUp<1>(zero_ptr)); + ASSERT_EQ(zero_ptr, RoundUp<4>(zero_ptr)); + ASSERT_EQ(zero_ptr, RoundUp<8>(zero_ptr)); + + ASSERT_EQ(three_ptr, RoundUp<1>(three_ptr)); + ASSERT_EQ(four_ptr, RoundUp<4>(three_ptr)); + ASSERT_EQ(eight_ptr, RoundUp<8>(three_ptr)); + + ASSERT_EQ(four_ptr, RoundUp<1>(four_ptr)); + ASSERT_EQ(four_ptr, RoundUp<4>(four_ptr)); + ASSERT_EQ(eight_ptr, RoundUp<8>(four_ptr)); + + ASSERT_EQ(seven_ptr, RoundUp<1>(seven_ptr)); + ASSERT_EQ(eight_ptr, RoundUp<4>(seven_ptr)); + ASSERT_EQ(eight_ptr, RoundUp<8>(four_ptr)); +} + +} // namespace crc32c diff --git a/src/crc32c/src/crc32c_sse42.cc b/src/crc32c/src/crc32c_sse42.cc new file mode 100644 index 000000000..139520428 --- /dev/null +++ b/src/crc32c/src/crc32c_sse42.cc @@ -0,0 +1,258 @@ +// Copyright 2008 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "./crc32c_sse42.h" + +// In a separate source file to allow this accelerated CRC32C function to be +// compiled with the appropriate compiler flags to enable SSE4.2 instructions. + +// This implementation is loosely based on Intel Pub 323405 from April 2011, +// "Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction". + +#include +#include + +#include "./crc32c_internal.h" +#include "./crc32c_prefetch.h" +#include "./crc32c_read_le.h" +#include "./crc32c_round_up.h" +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +#if defined(_MSC_VER) +#include +#else // !defined(_MSC_VER) +#include +#endif // defined(_MSC_VER) + +namespace crc32c { + +namespace { + +constexpr const ptrdiff_t kGroups = 3; +constexpr const ptrdiff_t kBlock0Size = 16 * 1024 / kGroups / 64 * 64; +constexpr const ptrdiff_t kBlock1Size = 4 * 1024 / kGroups / 8 * 8; +constexpr const ptrdiff_t kBlock2Size = 1024 / kGroups / 8 * 8; + +const uint32_t kBlock0SkipTable[8][16] = { + {0x00000000, 0xff770459, 0xfb027e43, 0x04757a1a, 0xf3e88a77, 0x0c9f8e2e, + 0x08eaf434, 0xf79df06d, 0xe23d621f, 0x1d4a6646, 0x193f1c5c, 0xe6481805, + 0x11d5e868, 0xeea2ec31, 0xead7962b, 0x15a09272}, + {0x00000000, 0xc196b2cf, 0x86c1136f, 0x4757a1a0, 0x086e502f, 0xc9f8e2e0, + 0x8eaf4340, 0x4f39f18f, 0x10dca05e, 0xd14a1291, 0x961db331, 0x578b01fe, + 0x18b2f071, 0xd92442be, 0x9e73e31e, 0x5fe551d1}, + {0x00000000, 0x21b940bc, 0x43728178, 0x62cbc1c4, 0x86e502f0, 0xa75c424c, + 0xc5978388, 0xe42ec334, 0x08267311, 0x299f33ad, 0x4b54f269, 0x6aedb2d5, + 0x8ec371e1, 0xaf7a315d, 0xcdb1f099, 0xec08b025}, + {0x00000000, 0x104ce622, 0x2099cc44, 0x30d52a66, 0x41339888, 0x517f7eaa, + 0x61aa54cc, 0x71e6b2ee, 0x82673110, 0x922bd732, 0xa2fefd54, 0xb2b21b76, + 0xc354a998, 0xd3184fba, 0xe3cd65dc, 0xf38183fe}, + {0x00000000, 0x012214d1, 0x024429a2, 0x03663d73, 0x04885344, 0x05aa4795, + 0x06cc7ae6, 0x07ee6e37, 0x0910a688, 0x0832b259, 0x0b548f2a, 0x0a769bfb, + 0x0d98f5cc, 0x0cbae11d, 0x0fdcdc6e, 0x0efec8bf}, + {0x00000000, 0x12214d10, 0x24429a20, 0x3663d730, 0x48853440, 0x5aa47950, + 0x6cc7ae60, 0x7ee6e370, 0x910a6880, 0x832b2590, 0xb548f2a0, 0xa769bfb0, + 0xd98f5cc0, 0xcbae11d0, 0xfdcdc6e0, 0xefec8bf0}, + {0x00000000, 0x27f8a7f1, 0x4ff14fe2, 0x6809e813, 0x9fe29fc4, 0xb81a3835, + 0xd013d026, 0xf7eb77d7, 0x3a294979, 0x1dd1ee88, 0x75d8069b, 0x5220a16a, + 0xa5cbd6bd, 0x8233714c, 0xea3a995f, 0xcdc23eae}, + {0x00000000, 0x745292f2, 0xe8a525e4, 0x9cf7b716, 0xd4a63d39, 0xa0f4afcb, + 0x3c0318dd, 0x48518a2f, 0xaca00c83, 0xd8f29e71, 0x44052967, 0x3057bb95, + 0x780631ba, 0x0c54a348, 0x90a3145e, 0xe4f186ac}, +}; +const uint32_t kBlock1SkipTable[8][16] = { + {0x00000000, 0x79113270, 0xf22264e0, 0x8b335690, 0xe1a8bf31, 0x98b98d41, + 0x138adbd1, 0x6a9be9a1, 0xc6bd0893, 0xbfac3ae3, 0x349f6c73, 0x4d8e5e03, + 0x2715b7a2, 0x5e0485d2, 0xd537d342, 0xac26e132}, + {0x00000000, 0x889667d7, 0x14c0b95f, 0x9c56de88, 0x298172be, 0xa1171569, + 0x3d41cbe1, 0xb5d7ac36, 0x5302e57c, 0xdb9482ab, 0x47c25c23, 0xcf543bf4, + 0x7a8397c2, 0xf215f015, 0x6e432e9d, 0xe6d5494a}, + {0x00000000, 0xa605caf8, 0x49e7e301, 0xefe229f9, 0x93cfc602, 0x35ca0cfa, + 0xda282503, 0x7c2deffb, 0x2273faf5, 0x8476300d, 0x6b9419f4, 0xcd91d30c, + 0xb1bc3cf7, 0x17b9f60f, 0xf85bdff6, 0x5e5e150e}, + {0x00000000, 0x44e7f5ea, 0x89cfebd4, 0xcd281e3e, 0x1673a159, 0x529454b3, + 0x9fbc4a8d, 0xdb5bbf67, 0x2ce742b2, 0x6800b758, 0xa528a966, 0xe1cf5c8c, + 0x3a94e3eb, 0x7e731601, 0xb35b083f, 0xf7bcfdd5}, + {0x00000000, 0x59ce8564, 0xb39d0ac8, 0xea538fac, 0x62d66361, 0x3b18e605, + 0xd14b69a9, 0x8885eccd, 0xc5acc6c2, 0x9c6243a6, 0x7631cc0a, 0x2fff496e, + 0xa77aa5a3, 0xfeb420c7, 0x14e7af6b, 0x4d292a0f}, + {0x00000000, 0x8eb5fb75, 0x1887801b, 0x96327b6e, 0x310f0036, 0xbfbafb43, + 0x2988802d, 0xa73d7b58, 0x621e006c, 0xecabfb19, 0x7a998077, 0xf42c7b02, + 0x5311005a, 0xdda4fb2f, 0x4b968041, 0xc5237b34}, + {0x00000000, 0xc43c00d8, 0x8d947741, 0x49a87799, 0x1ec49873, 0xdaf898ab, + 0x9350ef32, 0x576cefea, 0x3d8930e6, 0xf9b5303e, 0xb01d47a7, 0x7421477f, + 0x234da895, 0xe771a84d, 0xaed9dfd4, 0x6ae5df0c}, + {0x00000000, 0x7b1261cc, 0xf624c398, 0x8d36a254, 0xe9a5f1c1, 0x92b7900d, + 0x1f813259, 0x64935395, 0xd6a79573, 0xadb5f4bf, 0x208356eb, 0x5b913727, + 0x3f0264b2, 0x4410057e, 0xc926a72a, 0xb234c6e6}, +}; +const uint32_t kBlock2SkipTable[8][16] = { + {0x00000000, 0x8f158014, 0x1bc776d9, 0x94d2f6cd, 0x378eedb2, 0xb89b6da6, + 0x2c499b6b, 0xa35c1b7f, 0x6f1ddb64, 0xe0085b70, 0x74daadbd, 0xfbcf2da9, + 0x589336d6, 0xd786b6c2, 0x4354400f, 0xcc41c01b}, + {0x00000000, 0xde3bb6c8, 0xb99b1b61, 0x67a0ada9, 0x76da4033, 0xa8e1f6fb, + 0xcf415b52, 0x117aed9a, 0xedb48066, 0x338f36ae, 0x542f9b07, 0x8a142dcf, + 0x9b6ec055, 0x4555769d, 0x22f5db34, 0xfcce6dfc}, + {0x00000000, 0xde85763d, 0xb8e69a8b, 0x6663ecb6, 0x742143e7, 0xaaa435da, + 0xccc7d96c, 0x1242af51, 0xe84287ce, 0x36c7f1f3, 0x50a41d45, 0x8e216b78, + 0x9c63c429, 0x42e6b214, 0x24855ea2, 0xfa00289f}, + {0x00000000, 0xd569796d, 0xaf3e842b, 0x7a57fd46, 0x5b917ea7, 0x8ef807ca, + 0xf4affa8c, 0x21c683e1, 0xb722fd4e, 0x624b8423, 0x181c7965, 0xcd750008, + 0xecb383e9, 0x39dafa84, 0x438d07c2, 0x96e47eaf}, + {0x00000000, 0x6ba98c6d, 0xd75318da, 0xbcfa94b7, 0xab4a4745, 0xc0e3cb28, + 0x7c195f9f, 0x17b0d3f2, 0x5378f87b, 0x38d17416, 0x842be0a1, 0xef826ccc, + 0xf832bf3e, 0x939b3353, 0x2f61a7e4, 0x44c82b89}, + {0x00000000, 0xa6f1f0f6, 0x480f971d, 0xeefe67eb, 0x901f2e3a, 0x36eedecc, + 0xd810b927, 0x7ee149d1, 0x25d22a85, 0x8323da73, 0x6dddbd98, 0xcb2c4d6e, + 0xb5cd04bf, 0x133cf449, 0xfdc293a2, 0x5b336354}, + {0x00000000, 0x4ba4550a, 0x9748aa14, 0xdcecff1e, 0x2b7d22d9, 0x60d977d3, + 0xbc3588cd, 0xf791ddc7, 0x56fa45b2, 0x1d5e10b8, 0xc1b2efa6, 0x8a16baac, + 0x7d87676b, 0x36233261, 0xeacfcd7f, 0xa16b9875}, + {0x00000000, 0xadf48b64, 0x5e056039, 0xf3f1eb5d, 0xbc0ac072, 0x11fe4b16, + 0xe20fa04b, 0x4ffb2b2f, 0x7df9f615, 0xd00d7d71, 0x23fc962c, 0x8e081d48, + 0xc1f33667, 0x6c07bd03, 0x9ff6565e, 0x3202dd3a}, +}; + +constexpr const ptrdiff_t kPrefetchHorizon = 256; + +} // namespace + +uint32_t ExtendSse42(uint32_t crc, const uint8_t* data, size_t size) { + const uint8_t* p = data; + const uint8_t* e = data + size; + uint32_t l = crc ^ kCRC32Xor; + +#define STEP1 \ + do { \ + l = _mm_crc32_u8(l, *p++); \ + } while (0) + +#define STEP4(crc) \ + do { \ + crc = _mm_crc32_u32(crc, ReadUint32LE(p)); \ + p += 4; \ + } while (0) + +#define STEP8(crc, data) \ + do { \ + crc = _mm_crc32_u64(crc, ReadUint64LE(data)); \ + data += 8; \ + } while (0) + +#define STEP8BY3(crc0, crc1, crc2, p0, p1, p2) \ + do { \ + STEP8(crc0, p0); \ + STEP8(crc1, p1); \ + STEP8(crc2, p2); \ + } while (0) + +#define STEP8X3(crc0, crc1, crc2, bs) \ + do { \ + crc0 = _mm_crc32_u64(crc0, ReadUint64LE(p)); \ + crc1 = _mm_crc32_u64(crc1, ReadUint64LE(p + bs)); \ + crc2 = _mm_crc32_u64(crc2, ReadUint64LE(p + 2 * bs)); \ + p += 8; \ + } while (0) + +#define SKIP_BLOCK(crc, tab) \ + do { \ + crc = tab[0][crc & 0xf] ^ tab[1][(crc >> 4) & 0xf] ^ \ + tab[2][(crc >> 8) & 0xf] ^ tab[3][(crc >> 12) & 0xf] ^ \ + tab[4][(crc >> 16) & 0xf] ^ tab[5][(crc >> 20) & 0xf] ^ \ + tab[6][(crc >> 24) & 0xf] ^ tab[7][(crc >> 28) & 0xf]; \ + } while (0) + + // Point x at first 8-byte aligned byte in the buffer. This might be past the + // end of the buffer. + const uint8_t* x = RoundUp<8>(p); + if (x <= e) { + // Process bytes p is 8-byte aligned. + while (p != x) { + STEP1; + } + } + + // Proccess the data in predetermined block sizes with tables for quickly + // combining the checksum. Experimentally it's better to use larger block + // sizes where possible so use a hierarchy of decreasing block sizes. + uint64_t l64 = l; + while ((e - p) >= kGroups * kBlock0Size) { + uint64_t l641 = 0; + uint64_t l642 = 0; + for (int i = 0; i < kBlock0Size; i += 8 * 8) { + // Prefetch ahead to hide latency. + RequestPrefetch(p + kPrefetchHorizon); + RequestPrefetch(p + kBlock0Size + kPrefetchHorizon); + RequestPrefetch(p + 2 * kBlock0Size + kPrefetchHorizon); + + // Process 64 bytes at a time. + STEP8X3(l64, l641, l642, kBlock0Size); + STEP8X3(l64, l641, l642, kBlock0Size); + STEP8X3(l64, l641, l642, kBlock0Size); + STEP8X3(l64, l641, l642, kBlock0Size); + STEP8X3(l64, l641, l642, kBlock0Size); + STEP8X3(l64, l641, l642, kBlock0Size); + STEP8X3(l64, l641, l642, kBlock0Size); + STEP8X3(l64, l641, l642, kBlock0Size); + } + + // Combine results. + SKIP_BLOCK(l64, kBlock0SkipTable); + l64 ^= l641; + SKIP_BLOCK(l64, kBlock0SkipTable); + l64 ^= l642; + p += (kGroups - 1) * kBlock0Size; + } + while ((e - p) >= kGroups * kBlock1Size) { + uint64_t l641 = 0; + uint64_t l642 = 0; + for (int i = 0; i < kBlock1Size; i += 8) { + STEP8X3(l64, l641, l642, kBlock1Size); + } + SKIP_BLOCK(l64, kBlock1SkipTable); + l64 ^= l641; + SKIP_BLOCK(l64, kBlock1SkipTable); + l64 ^= l642; + p += (kGroups - 1) * kBlock1Size; + } + while ((e - p) >= kGroups * kBlock2Size) { + uint64_t l641 = 0; + uint64_t l642 = 0; + for (int i = 0; i < kBlock2Size; i += 8) { + STEP8X3(l64, l641, l642, kBlock2Size); + } + SKIP_BLOCK(l64, kBlock2SkipTable); + l64 ^= l641; + SKIP_BLOCK(l64, kBlock2SkipTable); + l64 ^= l642; + p += (kGroups - 1) * kBlock2Size; + } + + // Process bytes 16 at a time + while ((e - p) >= 16) { + STEP8(l64, p); + STEP8(l64, p); + } + + l = static_cast(l64); + // Process the last few bytes. + while (p != e) { + STEP1; + } +#undef SKIP_BLOCK +#undef STEP8X3 +#undef STEP8BY3 +#undef STEP8 +#undef STEP4 +#undef STEP1 + + return l ^ kCRC32Xor; +} + +} // namespace crc32c + +#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) diff --git a/src/crc32c/src/crc32c_sse42.h b/src/crc32c/src/crc32c_sse42.h new file mode 100644 index 000000000..95da92663 --- /dev/null +++ b/src/crc32c/src/crc32c_sse42.h @@ -0,0 +1,33 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef CRC32C_CRC32C_SSE42_H_ +#define CRC32C_CRC32C_SSE42_H_ + +// X86-specific code. + +#include +#include + +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +// The hardware-accelerated implementation is only enabled for 64-bit builds, +// because a straightforward 32-bit implementation actually runs slower than the +// portable version. Most X86 machines are 64-bit nowadays, so it doesn't make +// much sense to spend time building an optimized hardware-accelerated +// implementation. +#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +namespace crc32c { + +// SSE4.2-accelerated implementation in crc32c_sse42.cc +uint32_t ExtendSse42(uint32_t crc, const uint8_t* data, size_t count); + +} // namespace crc32c + +#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +#endif // CRC32C_CRC32C_SSE42_H_ diff --git a/src/crc32c/src/crc32c_sse42_check.h b/src/crc32c/src/crc32c_sse42_check.h new file mode 100644 index 000000000..e7528912a --- /dev/null +++ b/src/crc32c/src/crc32c_sse42_check.h @@ -0,0 +1,50 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#ifndef CRC32C_CRC32C_SSE42_CHECK_H_ +#define CRC32C_CRC32C_SSE42_CHECK_H_ + +// X86-specific code checking the availability of SSE4.2 instructions. + +#include +#include + +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +// If the compiler supports SSE4.2, it definitely supports X86. + +#if defined(_MSC_VER) +#include + +namespace crc32c { + +inline bool CanUseSse42() { + int cpu_info[4]; + __cpuid(cpu_info, 1); + return (cpu_info[2] & (1 << 20)) != 0; +} + +} // namespace crc32c + +#else // !defined(_MSC_VER) +#include + +namespace crc32c { + +inline bool CanUseSse42() { + unsigned int eax, ebx, ecx, edx; + return __get_cpuid(1, &eax, &ebx, &ecx, &edx) && ((ecx & (1 << 20)) != 0); +} + +} // namespace crc32c + +#endif // defined(_MSC_VER) + +#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +#endif // CRC32C_CRC32C_SSE42_CHECK_H_ diff --git a/src/crc32c/src/crc32c_sse42_unittest.cc b/src/crc32c/src/crc32c_sse42_unittest.cc new file mode 100644 index 000000000..c73ad8ddd --- /dev/null +++ b/src/crc32c/src/crc32c_sse42_unittest.cc @@ -0,0 +1,24 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "gtest/gtest.h" + +#include "./crc32c_extend_unittests.h" +#include "./crc32c_sse42.h" + +namespace crc32c { + +#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +struct Sse42TestTraits { + static uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) { + return ExtendSse42(crc, data, count); + } +}; + +INSTANTIATE_TYPED_TEST_SUITE_P(Sse42, ExtendTest, Sse42TestTraits); + +#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__)) + +} // namespace crc32c diff --git a/src/crc32c/src/crc32c_test_main.cc b/src/crc32c/src/crc32c_test_main.cc new file mode 100644 index 000000000..275ee380c --- /dev/null +++ b/src/crc32c/src/crc32c_test_main.cc @@ -0,0 +1,22 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef CRC32C_HAVE_CONFIG_H +#include "crc32c/crc32c_config.h" +#endif + +#include "gtest/gtest.h" + +#if CRC32C_TESTS_BUILT_WITH_GLOG +#include "glog/logging.h" +#endif // CRC32C_TESTS_BUILT_WITH_GLOG + +int main(int argc, char** argv) { +#if CRC32C_TESTS_BUILT_WITH_GLOG + google::InitGoogleLogging(argv[0]); + google::InstallFailureSignalHandler(); +#endif // CRC32C_TESTS_BUILT_WITH_GLOG + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/crc32c/src/crc32c_unittest.cc b/src/crc32c/src/crc32c_unittest.cc new file mode 100644 index 000000000..d6c6af680 --- /dev/null +++ b/src/crc32c/src/crc32c_unittest.cc @@ -0,0 +1,129 @@ +// Copyright 2017 The CRC32C Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. See the AUTHORS file for names of contributors. + +#include "crc32c/crc32c.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "./crc32c_extend_unittests.h" + +TEST(Crc32CTest, Crc32c) { + // From rfc3720 section B.4. + uint8_t buf[32]; + + std::memset(buf, 0, sizeof(buf)); + EXPECT_EQ(static_cast(0x8a9136aa), + crc32c::Crc32c(buf, sizeof(buf))); + + std::memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(static_cast(0x62a8ab43), + crc32c::Crc32c(buf, sizeof(buf))); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(i); + EXPECT_EQ(static_cast(0x46dd794e), + crc32c::Crc32c(buf, sizeof(buf))); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(31 - i); + EXPECT_EQ(static_cast(0x113fdb5c), + crc32c::Crc32c(buf, sizeof(buf))); + + uint8_t data[48] = { + 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + EXPECT_EQ(static_cast(0xd9963a56), + crc32c::Crc32c(data, sizeof(data))); +} + +namespace crc32c { + +struct ApiTestTraits { + static uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) { + return ::crc32c::Extend(crc, data, count); + } +}; + +INSTANTIATE_TYPED_TEST_SUITE_P(Api, ExtendTest, ApiTestTraits); + +} // namespace crc32c + +TEST(CRC32CTest, Crc32cCharPointer) { + char buf[32]; + + std::memset(buf, 0, sizeof(buf)); + EXPECT_EQ(static_cast(0x8a9136aa), + crc32c::Crc32c(buf, sizeof(buf))); + + std::memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(static_cast(0x62a8ab43), + crc32c::Crc32c(buf, sizeof(buf))); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(i); + EXPECT_EQ(static_cast(0x46dd794e), + crc32c::Crc32c(buf, sizeof(buf))); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(31 - i); + EXPECT_EQ(static_cast(0x113fdb5c), + crc32c::Crc32c(buf, sizeof(buf))); +} + +TEST(CRC32CTest, Crc32cStdString) { + std::string buf; + buf.resize(32); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(0x00); + EXPECT_EQ(static_cast(0x8a9136aa), crc32c::Crc32c(buf)); + + for (size_t i = 0; i < 32; ++i) + buf[i] = '\xff'; + EXPECT_EQ(static_cast(0x62a8ab43), crc32c::Crc32c(buf)); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(i); + EXPECT_EQ(static_cast(0x46dd794e), crc32c::Crc32c(buf)); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(31 - i); + EXPECT_EQ(static_cast(0x113fdb5c), crc32c::Crc32c(buf)); +} + +#if __cplusplus > 201402L +#if __has_include() + +TEST(CRC32CTest, Crc32cStdStringView) { + uint8_t buf[32]; + std::string_view view(reinterpret_cast(buf), sizeof(buf)); + + std::memset(buf, 0, sizeof(buf)); + EXPECT_EQ(static_cast(0x8a9136aa), crc32c::Crc32c(view)); + + std::memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(static_cast(0x62a8ab43), crc32c::Crc32c(view)); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(i); + EXPECT_EQ(static_cast(0x46dd794e), crc32c::Crc32c(view)); + + for (size_t i = 0; i < 32; ++i) + buf[i] = static_cast(31 - i); + EXPECT_EQ(static_cast(0x113fdb5c), crc32c::Crc32c(view)); +} + +#endif // __has_include() +#endif // __cplusplus > 201402L + +#define TESTED_EXTEND Extend +#include "./crc32c_extend_unittests.h" +#undef TESTED_EXTEND From b1a4d9e29150425490ee593b6aa887b78654441b Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:26:01 +0000 Subject: [PATCH 008/146] Refactor: build BitcoinZ distibutions --- .gitignore | 5 + Makefile.am | 285 +- build-aux/m4/l_socket.m4 | 36 + configure.ac | 1137 +++---- contrib/devtools/gen-bitcoinz-conf.sh | 80 + contrib/devtools/security-check.py | 411 +-- contrib/devtools/split-debug.sh | 10 - contrib/devtools/split-debug.sh.in | 10 + contrib/devtools/symbol-check.py | 397 ++- contrib/devtools/test-security-check.py | 129 +- contrib/devtools/test-symbol-check.py | 174 ++ contrib/devtools/utils.py | 21 + contrib/filter-lcov.py | 28 + contrib/gitian-descriptors/README.md | 65 - contrib/gitian-descriptors/gitian-linux.yml | 139 - .../gitian-descriptors/gitian-osx-signer.yml | 37 - contrib/gitian-descriptors/gitian-osx.yml | 146 - .../gitian-descriptors/gitian-win-signer.yml | 38 - contrib/gitian-descriptors/gitian-win.yml | 166 - contrib/gitian-downloader/daira.asc | 70 - contrib/gitian-downloader/jack.asc | 449 --- contrib/gitian-downloader/kevin.asc | 171 -- contrib/gitian-downloader/nathan.asc | 52 - contrib/gitian-downloader/sean.asc | 51 - contrib/gitian-downloader/simon.asc | 47 - contrib/guix/INSTALL.md | 814 +++++ contrib/guix/README.md | 434 +++ contrib/guix/guix-attest | 263 ++ contrib/guix/guix-build | 467 +++ contrib/guix/guix-clean | 83 + contrib/guix/guix-codesign | 378 +++ contrib/guix/guix-verify | 174 ++ contrib/guix/libexec/build.sh | 409 +++ contrib/guix/libexec/codesign.sh | 116 + contrib/guix/libexec/prelude.bash | 82 + contrib/guix/manifest.scm | 560 ++++ .../patches/binutils-unaligned-default.patch | 22 + .../guix/patches/gcc-remap-guix-store.patch | 20 + contrib/guix/patches/glibc-guix-prefix.patch | 16 + .../patches/oscrypto-hard-code-openssl.patch | 13 + .../winpthreads-remap-guix-store.patch | 17 + .../macdeploy/Base.lproj/InfoPlist.strings | 1 - contrib/macdeploy/DS_Store | Bin 10244 -> 0 bytes contrib/macdeploy/README.md | 80 +- contrib/macdeploy/background.png | Bin 48690 -> 0 bytes contrib/macdeploy/background.psd | Bin 982442 -> 0 bytes contrib/macdeploy/background.tiff | Bin 202136 -> 0 bytes contrib/macdeploy/background@2x.png | Bin 138890 -> 0 bytes contrib/macdeploy/detached-sig-apply.sh | 52 - contrib/macdeploy/detached-sig-create.sh | 49 +- contrib/macdeploy/fancy.plist | 32 - contrib/macdeploy/gen-sdk | 96 + contrib/macdeploy/macdeployqtplus | 386 +++ contrib/shell/git-utils.bash | 14 + contrib/shell/realpath.bash | 71 + contrib/windeploy/detached-sig-create.sh | 36 + contrib/windeploy/win-codesign.cert | 112 + depends/Makefile | 184 +- depends/README.md | 96 +- depends/builders/darwin.mk | 35 +- depends/builders/default.mk | 7 +- depends/builders/freebsd.mk | 3 + depends/config.guess | 1757 ++++++----- depends/config.site.in | 94 +- depends/config.sub | 2720 +++++++++-------- depends/description.md | 11 +- depends/fix-elf-interpreter.sh | 2 + depends/funcs.mk | 205 +- depends/gen_id | 89 + depends/hosts/darwin.mk | 77 +- depends/hosts/default.mk | 24 +- depends/hosts/freebsd.mk | 18 +- depends/hosts/linux.mk | 19 +- depends/hosts/mingw32.mk | 21 +- depends/packages.md | 62 +- depends/packages/bdb.mk | 15 +- depends/packages/boost.mk | 48 +- depends/packages/libevent.mk | 41 +- depends/packages/librustzcash.mk | 66 +- depends/packages/native_b2.mk | 20 + depends/packages/native_cctools.mk | 65 - depends/packages/packages.mk | 12 +- depends/packages/rust.mk | 88 +- depends/packages/zeromq.mk | 42 +- .../patches/bdb/clang-12-stpcpy-issue.diff | 17 + .../bdb/winioctl-and-atomic_init_db.patch | 136 + depends/patches/boost/signals2-noise.patch | 23 - depends/patches/libevent/cmake_fixups.patch | 35 + .../detect-arch4random_addrandom-fix.patch | 43 - .../detect-arch4random_addrandom.patch | 77 - depends/patches/libevent/fix_mingw_link.patch | 25 + depends/patches/librustzcash/guix.config | 12 + depends/patches/zeromq/builtin_sha1.patch | 17 + .../patches/zeromq/cacheline_undefined.patch | 15 + depends/patches/zeromq/cmake_minimum.patch | 18 + depends/patches/zeromq/fix_have_windows.patch | 54 + .../patches/zeromq/macos_mktemp_check.patch | 16 + depends/patches/zeromq/no_librt.patch | 54 + .../zeromq/openbsd_kqueue_headers.patch | 24 + .../patches/zeromq/remove_libstd_link.patch | 25 + doc/Doxyfile | 1752 ----------- doc/Doxyfile.in | 2459 +++++++++++++++ doc/README_windows.txt | 19 + doc/bitcoinz_logo_doxygen.png | Bin 0 -> 5318 bytes doc/guix.md | 3 + doc/man/Makefile.am | 2 +- doc/release-process.md | 237 +- share/Info.plist.in | 71 + share/examples/bitcoinz.conf | 1 + share/genbuild.sh | 34 +- share/pixmaps/bitcoinz.icns | Bin 0 -> 16384 bytes share/pixmaps/bitcoinz.ico | Bin 0 -> 16958 bytes share/pixmaps/bitcoinz.png | Bin 0 -> 255406 bytes share/pixmaps/bitcoinz128.png | Bin 0 -> 21173 bytes share/pixmaps/bitcoinz128.xpm | 378 +++ share/pixmaps/bitcoinz16.png | Bin 0 -> 2663 bytes share/pixmaps/bitcoinz16.xpm | 231 ++ share/pixmaps/bitcoinz256.png | Bin 0 -> 46931 bytes share/pixmaps/bitcoinz256.xpm | 516 ++++ share/pixmaps/bitcoinz32.png | Bin 0 -> 4570 bytes share/pixmaps/bitcoinz32.xpm | 244 ++ share/pixmaps/bitcoinz64.png | Bin 0 -> 9550 bytes share/pixmaps/bitcoinz64.xpm | 305 ++ share/pixmaps/nsis-header.bmp | Bin 0 -> 25818 bytes share/pixmaps/nsis-wizard.bmp | Bin 0 -> 154542 bytes share/rpcauth/README.md | 19 + share/rpcauth/rpcauth.py | 49 + share/rpcuser/README.md | 10 - share/rpcuser/rpcuser.py | 41 - share/setup.nsi.in | 177 ++ src/Makefile.am | 27 +- src/compat/glibc_compat.cpp | 29 - src/random.h | 28 + src/wallet/wallet.cpp | 4 +- zcutil/fetch-params.sh | 2 +- 135 files changed, 14942 insertions(+), 7059 deletions(-) create mode 100644 build-aux/m4/l_socket.m4 create mode 100755 contrib/devtools/gen-bitcoinz-conf.sh delete mode 100755 contrib/devtools/split-debug.sh create mode 100644 contrib/devtools/split-debug.sh.in create mode 100755 contrib/devtools/test-symbol-check.py create mode 100755 contrib/devtools/utils.py create mode 100755 contrib/filter-lcov.py delete mode 100644 contrib/gitian-descriptors/README.md delete mode 100644 contrib/gitian-descriptors/gitian-linux.yml delete mode 100644 contrib/gitian-descriptors/gitian-osx-signer.yml delete mode 100644 contrib/gitian-descriptors/gitian-osx.yml delete mode 100644 contrib/gitian-descriptors/gitian-win-signer.yml delete mode 100644 contrib/gitian-descriptors/gitian-win.yml delete mode 100644 contrib/gitian-downloader/daira.asc delete mode 100644 contrib/gitian-downloader/jack.asc delete mode 100644 contrib/gitian-downloader/kevin.asc delete mode 100644 contrib/gitian-downloader/nathan.asc delete mode 100644 contrib/gitian-downloader/sean.asc delete mode 100644 contrib/gitian-downloader/simon.asc create mode 100644 contrib/guix/INSTALL.md create mode 100644 contrib/guix/README.md create mode 100755 contrib/guix/guix-attest create mode 100755 contrib/guix/guix-build create mode 100755 contrib/guix/guix-clean create mode 100755 contrib/guix/guix-codesign create mode 100755 contrib/guix/guix-verify create mode 100755 contrib/guix/libexec/build.sh create mode 100755 contrib/guix/libexec/codesign.sh create mode 100644 contrib/guix/libexec/prelude.bash create mode 100644 contrib/guix/manifest.scm create mode 100644 contrib/guix/patches/binutils-unaligned-default.patch create mode 100644 contrib/guix/patches/gcc-remap-guix-store.patch create mode 100644 contrib/guix/patches/glibc-guix-prefix.patch create mode 100644 contrib/guix/patches/oscrypto-hard-code-openssl.patch create mode 100644 contrib/guix/patches/winpthreads-remap-guix-store.patch delete mode 100644 contrib/macdeploy/Base.lproj/InfoPlist.strings delete mode 100644 contrib/macdeploy/DS_Store delete mode 100644 contrib/macdeploy/background.png delete mode 100644 contrib/macdeploy/background.psd delete mode 100644 contrib/macdeploy/background.tiff delete mode 100644 contrib/macdeploy/background@2x.png delete mode 100755 contrib/macdeploy/detached-sig-apply.sh delete mode 100644 contrib/macdeploy/fancy.plist create mode 100755 contrib/macdeploy/gen-sdk create mode 100755 contrib/macdeploy/macdeployqtplus create mode 100644 contrib/shell/git-utils.bash create mode 100644 contrib/shell/realpath.bash create mode 100755 contrib/windeploy/detached-sig-create.sh create mode 100644 contrib/windeploy/win-codesign.cert create mode 100755 depends/fix-elf-interpreter.sh create mode 100755 depends/gen_id create mode 100644 depends/packages/native_b2.mk delete mode 100644 depends/packages/native_cctools.mk create mode 100644 depends/patches/bdb/clang-12-stpcpy-issue.diff create mode 100644 depends/patches/bdb/winioctl-and-atomic_init_db.patch delete mode 100644 depends/patches/boost/signals2-noise.patch create mode 100644 depends/patches/libevent/cmake_fixups.patch delete mode 100644 depends/patches/libevent/detect-arch4random_addrandom-fix.patch delete mode 100644 depends/patches/libevent/detect-arch4random_addrandom.patch create mode 100644 depends/patches/libevent/fix_mingw_link.patch create mode 100644 depends/patches/librustzcash/guix.config create mode 100644 depends/patches/zeromq/builtin_sha1.patch create mode 100644 depends/patches/zeromq/cacheline_undefined.patch create mode 100644 depends/patches/zeromq/cmake_minimum.patch create mode 100644 depends/patches/zeromq/fix_have_windows.patch create mode 100644 depends/patches/zeromq/macos_mktemp_check.patch create mode 100644 depends/patches/zeromq/no_librt.patch create mode 100644 depends/patches/zeromq/openbsd_kqueue_headers.patch create mode 100644 depends/patches/zeromq/remove_libstd_link.patch delete mode 100644 doc/Doxyfile create mode 100644 doc/Doxyfile.in create mode 100644 doc/README_windows.txt create mode 100644 doc/bitcoinz_logo_doxygen.png create mode 100644 doc/guix.md create mode 100644 share/Info.plist.in create mode 100644 share/examples/bitcoinz.conf create mode 100644 share/pixmaps/bitcoinz.icns create mode 100644 share/pixmaps/bitcoinz.ico create mode 100644 share/pixmaps/bitcoinz.png create mode 100644 share/pixmaps/bitcoinz128.png create mode 100644 share/pixmaps/bitcoinz128.xpm create mode 100644 share/pixmaps/bitcoinz16.png create mode 100644 share/pixmaps/bitcoinz16.xpm create mode 100644 share/pixmaps/bitcoinz256.png create mode 100644 share/pixmaps/bitcoinz256.xpm create mode 100644 share/pixmaps/bitcoinz32.png create mode 100644 share/pixmaps/bitcoinz32.xpm create mode 100644 share/pixmaps/bitcoinz64.png create mode 100644 share/pixmaps/bitcoinz64.xpm create mode 100644 share/pixmaps/nsis-header.bmp create mode 100644 share/pixmaps/nsis-wizard.bmp create mode 100644 share/rpcauth/README.md create mode 100755 share/rpcauth/rpcauth.py delete mode 100644 share/rpcuser/README.md delete mode 100755 share/rpcuser/rpcuser.py create mode 100644 share/setup.nsi.in delete mode 100644 src/compat/glibc_compat.cpp diff --git a/.gitignore b/.gitignore index 964206e16..067be8138 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ src/config/bitcoin-config.h src/config/bitcoin-config.h.in src/config/stamp-h1 share/setup.nsi +share/Info.plist cache/ venv-mnf/ @@ -109,6 +110,8 @@ qa/pull-tester/test.*/* /doc/doxygen/ +contrib/devtools/split-debug.sh + libzcashconsensus.pc contrib/debian/files @@ -122,3 +125,5 @@ src/fuzz.cpp src/zcash/CreateJoinSplit src/zcash/GenerateParams + +/guix-build-* diff --git a/Makefile.am b/Makefile.am index 310135cff..4d6b9ab4c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,18 +1,20 @@ # Copyright (c) 2017-2024 The BitcoinZ community # Copyright (c) 2016-2019 The Zcash developers -# Copyright (c) 2013-2019 The Bitcoin Core developers -# Copyright (c) 2013-2019 Bitcoin Developers +# Copyright (c) 2013-2020 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . +# Pattern rule to print variables, e.g. make print-top_srcdir +print-%: FORCE + @echo '$*'='$($*)' + ACLOCAL_AMFLAGS = -I build-aux/m4 SUBDIRS = src if ENABLE_MAN SUBDIRS += doc/man endif .PHONY: deploy FORCE - -GZIP_ENV="-9n" +.INTERMEDIATE: $(COVERAGE_INFO) if BUILD_BITCOIN_LIBS pkgconfigdir = $(libdir)/pkgconfig @@ -20,28 +22,108 @@ pkgconfig_DATA = libzcashconsensus.pc endif BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT) +BITCOIN_TEST_BIN=$(top_builddir)/src/test/$(BITCOIN_TEST_NAME)$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) +BITCOIN_TX_BIN=$(top_builddir)/src/$(BITCOIN_TX_NAME)$(EXEEXT) +BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win64-setup$(EXEEXT) -DIST_DOCS = $(wildcard doc/*.md) $(wildcard doc/release-notes/*.md) +empty := +space := $(empty) $(empty) + +OSX_APP=BitcoinZ.app +OSX_VOLNAME = $(subst $(space),-,$(PACKAGE_NAME)) +OSX_ZIP = $(OSX_VOLNAME).zip +OSX_DEPLOY_SCRIPT=$(top_srcdir)/contrib/macdeploy/macdeployqtplus +OSX_INSTALLER_ICONS=$(top_srcdir)/share/pixmaps/bitcoinz.icns +OSX_PLIST=$(top_builddir)/share/Info.plist #not installed + +DIST_SHARE = \ + $(top_srcdir)/share/genbuild.sh \ + $(top_srcdir)/share/rpcauth BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \ - $(top_srcdir)/contrib/devtools/security-check.py + $(top_srcdir)/contrib/devtools/security-check.py \ + $(top_srcdir)/contrib/devtools/utils.py + +WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoinz.ico \ + $(top_srcdir)/share/pixmaps/nsis-header.bmp \ + $(top_srcdir)/share/pixmaps/nsis-wizard.bmp \ + $(top_srcdir)/doc/README_windows.txt -COVERAGE_INFO = baseline_filtered_combined.info baseline.info \ - leveldb_baseline.info test_bitcoinz_filtered.info total_coverage.info \ - baseline_filtered.info \ - leveldb_baseline_filtered.info test_bitcoinz_coverage.info test_bitcoinz.info \ - bitcoinz-gtest.info bitcoinz-gtest_filtered.info bitcoinz-gtest_coverage.info +OSX_PACKAGING = $(OSX_DEPLOY_SCRIPT) $(OSX_INSTALLER_ICONS) \ + $(top_srcdir)/contrib/macdeploy/detached-sig-create.sh + +COVERAGE_INFO = $(COV_TOOL_WRAPPER) baseline.info \ + test_bitcoinz_filtered.info bitcoinz-gtest_filtered.info total_coverage.info baseline_filtered.info \ + test_bitcoinz_coverage.info test_bitcoinz.info \ + bitcoinz-gtest_coverage.info bitcoinz-gtest.info dist-hook: - -$(MAKE) -C $(top_distdir)/src/secp256k1 distclean -$(GIT) archive --format=tar HEAD -- src/clientversion.cpp | $(AMTAR) -C $(top_distdir) -xf - -distcleancheck: - @: +if TARGET_WINDOWS +$(BITCOIN_WIN_INSTALLER): all-recursive + $(MKDIR_P) $(top_builddir)/release + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIND_BIN) $(top_builddir)/release + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_TEST_BIN) $(top_builddir)/release + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_TX_BIN) $(top_builddir)/release + @test -f $(MAKENSIS) && echo 'OutFile "$@"' | cat $(top_builddir)/share/setup.nsi - | $(MAKENSIS) -V2 - || \ + echo error: could not build $@ + @echo built $@ + +deploy: $(BITCOIN_WIN_INSTALLER) +endif + +if TARGET_DARWIN +$(OSX_APP)/Contents/PkgInfo: + $(MKDIR_P) $(@D) + @echo "APPL????" > $@ + +$(OSX_APP)/Contents/Resources/empty.lproj: + $(MKDIR_P) $(@D) + @touch $@ + +$(OSX_APP)/Contents/Info.plist: $(OSX_PLIST) + $(MKDIR_P) $(@D) + $(INSTALL_DATA) $< $@ + +$(OSX_APP)/Contents/Resources/bitcoinz.icns: $(OSX_INSTALLER_ICONS) + $(MKDIR_P) $(@D) + $(INSTALL_DATA) $< $@ + +$(OSX_APP)/Contents/MacOS/BitcoinZ: all-recursive + $(MKDIR_P) $(@D) + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIND_BIN) $@ + +$(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings: + $(MKDIR_P) $(@D) + echo '{ CFBundleDisplayName = "$(PACKAGE_NAME)"; CFBundleName = "$(PACKAGE_NAME)"; }' > $@ + +OSX_APP_BUILT=$(OSX_APP)/Contents/PkgInfo $(OSX_APP)/Contents/Resources/empty.lproj \ + $(OSX_APP)/Contents/Resources/bitcoinz.icns $(OSX_APP)/Contents/Info.plist \ + $(OSX_APP)/Contents/MacOS/BitcoinZ $(OSX_APP)/Contents/Resources/Base.lproj/InfoPlist.strings + +if BUILD_DARWIN +$(OSX_ZIP): $(OSX_APP_BUILT) $(OSX_PACKAGING) + $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) $(OSX_VOLNAME) -zip -$(if $(findstring src/,$(MAKECMDGOALS)),$(MAKECMDGOALS), none): FORCE - $(MAKE) -C src $(patsubst src/%,%,$@) +deploydir: $(OSX_ZIP) +else !BUILD_DARWIN +APP_DIST_DIR=$(top_builddir)/dist + +$(OSX_ZIP): deploydir + if [ -n "$(SOURCE_DATE_EPOCH)" ]; then find $(APP_DIST_DIR) -exec touch -d @$(SOURCE_DATE_EPOCH) {} +; fi + cd $(APP_DIST_DIR) && find . | sort | $(ZIP) -X@ $@ + +$(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/BitcoinZ: $(OSX_APP_BUILT) $(OSX_PACKAGING) + OBJDUMP=$(OBJDUMP) $(PYTHON) $(OSX_DEPLOY_SCRIPT) $(OSX_APP) $(OSX_VOLNAME) + +deploydir: $(APP_DIST_DIR)/$(OSX_APP)/Contents/MacOS/BitcoinZ +endif !BUILD_DARWIN + +deploy: $(OSX_ZIP) +endif $(BITCOIND_BIN): FORCE $(MAKE) -C src $(@F) @@ -49,116 +131,117 @@ $(BITCOIND_BIN): FORCE $(BITCOIN_CLI_BIN): FORCE $(MAKE) -C src $(@F) -if USE_LCOV +$(BITCOIN_TX_BIN): FORCE + $(MAKE) -C src $(@F) -baseline.info: - $(LCOV) -c -i -d $(abs_builddir)/src -o $@ +if USE_LCOV +LCOV_FILTER_PATTERN = \ + -p "/usr/local/" \ + -p "/usr/include/" \ + -p "/usr/lib/" \ + -p "/usr/lib64/" \ + -p "src/leveldb/" \ + -p "src/crc32c/" \ + -p "src/bench/" \ + -p "src/crypto/ctaes" \ + -p "src/secp256k1" \ + -p "depends" + +GETST_LCOV_FILTER_PATTERN = \ + -p "src/wallet/gtest" \ + -p "src/gtest" + +$(COV_TOOL_WRAPPER): + @echo 'exec $(COV_TOOL) "$$@"' > $(COV_TOOL_WRAPPER) + @chmod +x $(COV_TOOL_WRAPPER) + +baseline.info: $(COV_TOOL_WRAPPER) + $(LCOV) $(LCOV_OPTS) -c -i -d $(abs_builddir)/src -o $@ baseline_filtered.info: baseline.info - $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ - -leveldb_baseline.info: baseline_filtered.info - $(LCOV) -c -i -d $(abs_builddir)/src/leveldb -b $(abs_builddir)/src/leveldb -o $@ - -leveldb_baseline_filtered.info: leveldb_baseline.info - $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ - -baseline_filtered_combined.info: leveldb_baseline_filtered.info baseline_filtered.info - $(LCOV) -a leveldb_baseline_filtered.info -a baseline_filtered.info -o $@ - -test_bitcoinz.info: baseline_filtered_combined.info + $(abs_builddir)/contrib/filter-lcov.py $(LCOV_FILTER_PATTERN) $< $@ + $(LCOV) -a $@ $(LCOV_OPTS) -o $@ + +test_bitcoinz.info: baseline_filtered.info $(MAKE) -C src/ bitcoinz_test_check - $(LCOV) -c -d $(abs_builddir)/src -t test_bitcoinz -o $@ - $(LCOV) -z -d $(abs_builddir)/src - $(LCOV) -z -d $(abs_builddir)/src/leveldb + $(LCOV) -c $(LCOV_OPTS) -d $(abs_builddir)/src -t test_bitcoinz -o $@ + $(LCOV) -z $(LCOV_OPTS) -d $(abs_builddir)/src -test_bitcoinz_filtered.info: test_bitcoinz.info - $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ - -bitcoinz-gtest.info: baseline_filtered_combined.info +bitcoinz-gtest.info: baseline_filtered.info $(MAKE) -C src/ bitcoinz-gtest_check - $(LCOV) -c -d $(abs_builddir)/src -t bitcoinz-gtest -o $@ - $(LCOV) -z -d $(abs_builddir)/src - $(LCOV) -z -d $(abs_builddir)/src/leveldb + $(LCOV) -c $(LCOV_OPTS) -d $(abs_builddir)/src -t bitcoinz-gtest -o $@ + $(LCOV) -z $(LCOV_OPTS) -d $(abs_builddir)/src + +test_bitcoinz_filtered.info: test_bitcoinz.info + $(abs_builddir)/contrib/filter-lcov.py $(LCOV_FILTER_PATTERN) $< $@ + $(LCOV) -a $@ $(LCOV_OPTS) -o $@ bitcoinz-gtest_filtered.info: bitcoinz-gtest.info - $(LCOV) -r $< "/usr/include/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/*.h" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/boost/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gmock/*" \ - "$(abs_builddir)/depends/x86_64-unknown-linux-gnu/include/gtest/*" \ - "$(abs_builddir)/src/gtest/*" \ - "$(abs_builddir)/src/test/*" \ - "$(abs_builddir)/src/wallet/gtest/*" \ - "$(abs_builddir)/src/wallet/test/*" \ - -o $@ - -test_bitcoinz_coverage.info: baseline_filtered_combined.info test_bitcoinz_filtered.info - $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoinz_filtered.info -o $@ - -bitcoinz-gtest_coverage.info: baseline_filtered_combined.info bitcoinz-gtest_filtered.info - $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a bitcoinz-gtest_filtered.info -o $@ - -total_coverage.info: baseline_filtered_combined.info test_bitcoinz_filtered.info bitcoinz-gtest_filtered.info - $(LCOV) -a baseline_filtered.info -a leveldb_baseline_filtered.info -a test_bitcoinz_filtered.info -a bitcoinz-gtest_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt + $(abs_builddir)/contrib/filter-lcov.py $(GETST_LCOV_FILTER_PATTERN) $< $@ + $(LCOV) -a $@ $(LCOV_OPTS) -o $@ + +test_bitcoinz_coverage.info: baseline_filtered.info test_bitcoinz_filtered.info + $(LCOV) $(LCOV_OPTS) -a baseline_filtered.info -a test_bitcoinz_filtered.info -o $@ + +bitcoinz-gtest_coverage.info: baseline_filtered.info bitcoinz-gtest_filtered.info + $(LCOV) $(LCOV_OPTS) -a baseline_filtered.info -a bitcoinz-gtest_filtered.info -o $@ + +total_coverage.info: test_bitcoinz_filtered.info + $(LCOV) $(LCOV_OPTS) -a baseline_filtered.info -a test_bitcoinz_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt test_bitcoinz.coverage/.dirstamp: test_bitcoinz_coverage.info - $(GENHTML) -s $< -o $(@D) + $(GENHTML) -s $(LCOV_OPTS) $< -o $(@D) @touch $@ bitcoinz-gtest.coverage/.dirstamp: bitcoinz-gtest_coverage.info - $(GENHTML) -s $< -o $(@D) + $(GENHTML) -s $(LCOV_OPTS) $< -o $(@D) @touch $@ -cov-bitcoinz: bitcoinz-gtest.coverage/.dirstamp - total.coverage/.dirstamp: total_coverage.info - $(GENHTML) -s $< -o $(@D) + $(GENHTML) -s $(LCOV_OPTS) $< -o $(@D) @touch $@ -cov: test_bitcoinz.coverage/.dirstamp cov-bitcoinz total.coverage/.dirstamp +cov: test_bitcoinz.coverage/.dirstamp bitcoinz-gtest.coverage/.dirstamp total.coverage/.dirstamp endif dist_bin_SCRIPTS = zcutil/fetch-params.sh dist_noinst_SCRIPTS = autogen.sh zcutil/build-debian-package.sh zcutil/build.sh -EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.sh qa/pull-tester/run-bitcoin-cli qa/rpc-tests qa/bitcoinz $(DIST_DOCS) $(BIN_CHECKS) +EXTRA_DIST = $(DIST_SHARE) $(WINDOWS_PACKAGING) $(OSX_PACKAGING) $(BIN_CHECKS) -install-exec-hook: - mv $(DESTDIR)$(bindir)/fetch-params.sh $(DESTDIR)$(bindir)/bitcoinz-fetch-params - -.INTERMEDIATE: $(COVERAGE_INFO) +CLEANFILES = $(OSX_ZIP) $(BITCOIN_WIN_INSTALLER) DISTCHECK_CONFIGURE_FLAGS = --enable-man -clean-local: - rm -rf test_bitcoinz.coverage/ bitcoinz-gtest.coverage/ total.coverage/ - rm -rf afl-temp +doc/doxygen/.stamp: doc/Doxyfile FORCE + $(MKDIR_P) $(@D) + $(DOXYGEN) $^ + $(AM_V_at) touch $@ + +if HAVE_DOXYGEN +docs: doc/doxygen/.stamp +else +docs: + @echo "error: doxygen not found" +endif + +clean-docs: + rm -rf doc/doxygen + +clean-local: clean-docs + rm -rf coverage_percent.txt test_bitcoinz.coverage/ bitcoinz-gtest.coverage/ total.coverage/ cache/ $(OSX_APP) + +test-security-check: +if TARGET_DARWIN + $(AM_V_at) CXX='$(CXX)' CXXFLAGS='$(CXXFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_MACHO + $(AM_V_at) CXX='$(CXX)' CXXFLAGS='$(CXXFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_MACHO +endif +if TARGET_WINDOWS + $(AM_V_at) CXX='$(CXX)' CXXFLAGS='$(CXXFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_PE + $(AM_V_at) CXX='$(CXX)' CXXFLAGS='$(CXXFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_PE +endif +if TARGET_LINUX + $(AM_V_at) CXX='$(CXX)' CXXFLAGS='$(CXXFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_ELF + $(AM_V_at) CXX='$(CXX)' CXXFLAGS='$(CXXFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_ELF +endif diff --git a/build-aux/m4/l_socket.m4 b/build-aux/m4/l_socket.m4 new file mode 100644 index 000000000..38923a98f --- /dev/null +++ b/build-aux/m4/l_socket.m4 @@ -0,0 +1,36 @@ +# Illumos/SmartOS requires linking with -lsocket if +# using getifaddrs & freeifaddrs + +m4_define([_CHECK_SOCKET_testbody], [[ + #include + #include + + int main() { + struct ifaddrs *ifaddr; + getifaddrs(&ifaddr); + freeifaddrs(ifaddr); + } +]]) + +AC_DEFUN([CHECK_SOCKET], [ + + AC_LANG_PUSH(C++) + + AC_MSG_CHECKING([whether ifaddrs funcs can be used without link library]) + + AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_SOCKET_testbody])],[ + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + LIBS="$LIBS -lsocket" + AC_MSG_CHECKING([whether getifaddrs needs -lsocket]) + AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_SOCKET_testbody])],[ + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + AC_MSG_FAILURE([cannot figure out how to use getifaddrs]) + ]) + ]) + + AC_LANG_POP +]) diff --git a/configure.ac b/configure.ac index ca71e8564..1ba5304b9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,4 @@ -dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) -AC_PREREQ([2.60]) +AC_PREREQ([2.69]) define(_CLIENT_VERSION_MAJOR, 2) define(_CLIENT_VERSION_MINOR, 0) define(_CLIENT_VERSION_REVISION, 10) @@ -14,13 +13,31 @@ AC_CONFIG_HEADERS([src/config/bitcoin-config.h]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux/m4]) +m4_ifndef([PKG_PROG_PKG_CONFIG], [m4_fatal([PKG_PROG_PKG_CONFIG macro not found. Please install pkg-config and re-run autogen.sh])]) +PKG_PROG_PKG_CONFIG +if test "$PKG_CONFIG" = ""; then + AC_MSG_ERROR([pkg-config not found]) +fi + +# When compiling with depends, the `PKG_CONFIG_PATH` and `PKG_CONFIG_LIBDIR` variables, +# being set in a `config.site` file, are not exported to let the `--config-cache` option +# work properly. +if test -n "$PKG_CONFIG_PATH"; then + PKG_CONFIG="env PKG_CONFIG_PATH=$PKG_CONFIG_PATH $PKG_CONFIG" +fi +if test -n "$PKG_CONFIG_LIBDIR"; then + PKG_CONFIG="env PKG_CONFIG_LIBDIR=$PKG_CONFIG_LIBDIR $PKG_CONFIG" +fi + BITCOIN_DAEMON_NAME=bitcoinzd +BITCOIN_TEST_NAME=test_bitcoinz BITCOIN_CLI_NAME=bitcoinz-cli BITCOIN_TX_NAME=bitcoinz-tx dnl Unless the user specified ARFLAGS, force it to be cr -AC_ARG_VAR(ARFLAGS, [Flags for the archiver, defaults to if not set]) -if test "x${ARFLAGS+set}" != "xset"; then +dnl This is also the default as-of libtool 2.4.7 +AC_ARG_VAR([ARFLAGS], [Flags for the archiver, defaults to if not set]) +if test "${ARFLAGS+set}" != "set"; then ARFLAGS="cr" fi @@ -30,81 +47,98 @@ AH_TOP([#ifndef BITCOIN_CONFIG_H]) AH_TOP([#define BITCOIN_CONFIG_H]) AH_BOTTOM([#endif //BITCOIN_CONFIG_H]) -dnl faketime breaks configure and is only needed for make. Disable it here. -unset FAKETIME - dnl Automake init set-up and checks -AM_INIT_AUTOMAKE([no-define subdir-objects foreign tar-pax]) +AM_INIT_AUTOMAKE([1.13 no-define subdir-objects foreign]) -dnl faketime messes with timestamps and causes configure to be re-run. -dnl --disable-maintainer-mode can be used to bypass this. AM_MAINTAINER_MODE([enable]) dnl make the compilation flags quiet unless V=1 is used -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AM_SILENT_RULES([yes]) dnl Compiler checks (here before libtool). -if test "x${CXXFLAGS+set}" = "xset"; then +if test "${CXXFLAGS+set}" = "set"; then CXXFLAGS_overridden=yes else CXXFLAGS_overridden=no fi - AC_PROG_CXX -m4_ifdef([AC_PROG_OBJCXX],[AC_PROG_OBJCXX]) -dnl By default, libtool for mingw refuses to link static libs into a dll for -dnl fear of mixing pic/non-pic objects, and import/export complications. Since -dnl we have those under control, re-enable that functionality. +dnl libtool overrides case $host in *mingw*) + dnl By default, libtool for mingw refuses to link static libs into a dll for + dnl fear of mixing pic/non-pic objects, and import/export complications. Since + dnl we have those under control, re-enable that functionality. lt_cv_deplibs_check_method="pass_all" + + dnl Remove unwanted -DDLL_EXPORT from these variables. + dnl We do not use this macro, but system headers may export unwanted symbols + dnl if it's set. + lt_cv_prog_compiler_pic="-DPIC" + lt_cv_prog_compiler_pic_CXX="-DPIC" + ;; + *darwin*) + dnl Because it prints a verbose warning, lld fails the following check + dnl for "-Wl,-single_module" from libtool.m4: + dnl # If there is a non-empty error log, and "single_module" + dnl # appears in it, assume the flag caused a linker warning + dnl "-single_module" works fine on ld64 and lld, so just bypass the test. + dnl Failure to set this to "yes" causes libtool to use a very broken + dnl link-line for shared libs. + lt_cv_apple_cc_single_mod="yes" ;; esac + dnl Require C++17 compiler (no GNU extensions) AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory]) -dnl Check if -latomic is required for -CHECK_ATOMIC +dnl Unless the user specified OBJCXX, force it to be the same as CXX. This ensures +dnl that we get the same -std flags for both. +m4_ifdef([AC_PROG_OBJCXX],[ +if test "${OBJCXX+set}" = ""; then + OBJCXX="${CXX}" +fi +AC_PROG_OBJCXX +]) + +dnl OpenBSD ships with 2.4.2 +LT_PREREQ([2.4.2]) dnl Libtool init checks. -LT_INIT([pic-only]) +LT_INIT([pic-only win32-dll]) dnl Check/return PATH for base programs. -AC_PATH_TOOL(AR, ar) -AC_PATH_TOOL(RANLIB, ranlib) -AC_PATH_TOOL(STRIP, strip) -AC_PATH_TOOL(GCOV, gcov) -AC_PATH_PROG(LCOV, lcov) -AC_PATH_PROG(GENHTML, genhtml) +AC_PATH_TOOL([AR], [ar]) +AC_PATH_TOOL([GCOV], [gcov]) +AC_PATH_TOOL([LLVM_COV], [llvm-cov]) +AC_PATH_PROG([LCOV], [lcov]) +dnl The minimum supported version is specified in .python-version and should be used if available, see doc/dependencies.md +AC_PATH_PROGS([PYTHON], [python3.9 python3.10 python3.11 python3.12 python3 python]) +AC_PATH_PROG([GENHTML], [genhtml]) AC_PATH_PROG([GIT], [git]) -AC_PATH_PROG(CCACHE,ccache) -AC_PATH_PROG(XGETTEXT,xgettext) -AC_PATH_PROG(HEXDUMP,hexdump) -AC_PATH_TOOL(READELF,readelf) -AC_PATH_TOOL(CPPFILT,c++filt) +AC_PATH_PROG([CCACHE], [ccache]) +AC_PATH_PROG([XGETTEXT], [xgettext]) +AC_PATH_PROG([HEXDUMP], [hexdump]) +AC_PATH_PROG([READELF], [readelf]) +AC_PATH_TOOL([OBJDUMP], [objdump]) +AC_PATH_TOOL([OBJCOPY], [objcopy]) +AC_PATH_PROG([DOXYGEN], [doxygen]) +AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) -# Enable wallet AC_ARG_ENABLE([wallet], [AS_HELP_STRING([--disable-wallet], [disable wallet (enabled by default)])], [enable_wallet=$enableval], [enable_wallet=yes]) -AC_ARG_ENABLE([mining], - [AS_HELP_STRING([--enable-mining], - [enable mining (default is yes)])], - [enable_mining=$enableval], - [enable_mining=yes]) - AC_ARG_ENABLE(tests, - AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]), - [use_tests=$enableval], - [use_tests=yes]) + AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]), + [use_tests=$enableval], + [use_tests=yes]) AC_ARG_ENABLE(bench, - AS_HELP_STRING([--enable-bench],[compile benchmarks (default is yes)]), - [use_bench=$enableval], - [use_bench=yes]) + AS_HELP_STRING([--disable-bench],[do not compile benchmarks (default is to compile)]), + [use_bench=$enableval], + [use_bench=yes]) AC_ARG_ENABLE([fuzz-main], [AS_HELP_STRING([--enable-fuzz-main], @@ -114,9 +148,9 @@ AC_ARG_ENABLE([fuzz-main], AC_ARG_ENABLE([hardening], [AS_HELP_STRING([--disable-hardening], - [do not attempt to harden the resulting executables (default is to harden when possible)])], + [do not attempt to harden the resulting executables (default is to harden)])], [use_hardening=$enableval], - [use_hardening=auto]) + [use_hardening=yes]) AC_ARG_ENABLE([reduce-exports], [AS_HELP_STRING([--enable-reduce-exports], @@ -130,161 +164,181 @@ AC_ARG_ENABLE([ccache], [use_ccache=$enableval], [use_ccache=auto]) +dnl Suppress warnings from external headers (e.g. Boost, Qt). +dnl May be useful if warnings from external headers clutter the build output +dnl too much, so that it becomes difficult to spot Bitcoin Core warnings +dnl or if they cause a build failure with --enable-werror. +AC_ARG_ENABLE([suppress-external-warnings], + [AS_HELP_STRING([--disable-suppress-external-warnings], + [Do not suppress warnings from external headers (default is to suppress)])], + [suppress_external_warnings=$enableval], + [suppress_external_warnings=yes]) + AC_ARG_ENABLE([lcov], [AS_HELP_STRING([--enable-lcov], [enable lcov testing (default is no)])], - [use_lcov=yes], + [use_lcov=$enableval], [use_lcov=no]) -AC_ARG_ENABLE([glibc-back-compat], - [AS_HELP_STRING([--enable-glibc-back-compat], - [enable backwards compatibility with glibc])], - [use_glibc_compat=$enableval], - [use_glibc_compat=no]) - AC_ARG_ENABLE([zmq], [AS_HELP_STRING([--disable-zmq], [disable ZMQ notifications])], [use_zmq=$enableval], [use_zmq=yes]) -AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], []) - AC_ARG_ENABLE(man, [AS_HELP_STRING([--disable-man], [do not install man pages (default is to install)])],, enable_man=yes) -AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) +AM_CONDITIONAL([ENABLE_MAN], [test "$enable_man" != "no"]) -# Enable debug +dnl Enable debug AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [use compiler flags and macros suited for debugging (default is no)])], [enable_debug=$enableval], [enable_debug=no]) -# Enable different -fsanitize options +dnl Enable different -fsanitize options AC_ARG_WITH([sanitizers], [AS_HELP_STRING([--with-sanitizers], [comma separated list of extra sanitizers to build with (default is none enabled)])], [use_sanitizers=$withval]) -# Enable gprof profiling -AC_ARG_ENABLE([gprof], - [AS_HELP_STRING([--enable-gprof], - [use gprof profiling compiler flags (default is no)])], - [enable_gprof=$enableval], - [enable_gprof=no]) - -# Turn warnings into errors +dnl Turn warnings into errors AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], - [Treat all compiler warnings as errors (default is no)])], + [Treat compiler warnings as errors (default is no)])], [enable_werror=$enableval], [enable_werror=no]) AC_LANG_PUSH([C++]) -AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) -if test "x$enable_debug" = xyes; then - # Clear default -g -O2 flags - if test "x$CXXFLAGS_overridden" = xno; then - CXXFLAGS="" - fi +dnl Always set -g -O2 in our CXXFLAGS. Autoconf will try and set CXXFLAGS to "-g -O2" by default, +dnl so we suppress that (if CXXFLAGS hasn't been overridden by the user), given we are adding it +dnl ourselves. +CXXFLAGS="$CXXFLAGS -g -O2" - # Disable all optimizations - AX_CHECK_COMPILE_FLAG([-O0], [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"]],,[[$CXXFLAG_WERROR]]) +if test "$CXXFLAGS_overridden" = "no"; then + CXXFLAGS="" +fi - # Prefer -g3, fall back to -g if that is unavailable. - AX_CHECK_COMPILE_FLAG( - [-g3], - [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g3"]], - [AX_CHECK_COMPILE_FLAG([-g],[[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g"]],,[[$CXXFLAG_WERROR]])], - [[$CXXFLAG_WERROR]]) +dnl Check for a flag to turn compiler warnings into errors. This is helpful for checks which may +dnl appear to succeed because by default they merely emit warnings when they fail. +dnl +dnl Note that this is not necessarily a check to see if -Werror is supported, but rather to see if +dnl a compile with -Werror can succeed. This is important because the compiler may already be +dnl warning about something unrelated, for example about some path issue. If that is the case, +dnl -Werror cannot be used because all of those warnings would be turned into errors. +AX_CHECK_COMPILE_FLAG([-Werror], [CXXFLAG_WERROR="-Werror"], [CXXFLAG_WERROR=""]) + +dnl Check for a flag to turn linker warnings into errors. When flags are passed to linkers via the +dnl compiler driver using a -Wl,-foo flag, linker warnings may be swallowed rather than bubbling up. +dnl See note above, the same applies here as well. +dnl +dnl LDFLAG_WERROR Should only be used when testing -Wl,* +case $host in + *darwin*) + AX_CHECK_LINK_FLAG([-Wl,-fatal_warnings], [LDFLAG_WERROR="-Wl,-fatal_warnings"], [LDFLAG_WERROR=""]) + ;; + *) + AX_CHECK_LINK_FLAG([-Wl,--fatal-warnings], [LDFLAG_WERROR="-Wl,--fatal-warnings"], [LDFLAG_WERROR=""]) + ;; +esac - AX_CHECK_PREPROC_FLAG([-DDEBUG],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG"]],,[[$CXXFLAG_WERROR]]) - AX_CHECK_PREPROC_FLAG([-DDEBUG_LOCKORDER],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG_LOCKORDER"]],,[[$CXXFLAG_WERROR]]) - AX_CHECK_COMPILE_FLAG([-ftrapv],[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -ftrapv"],,[[$CXXFLAG_WERROR]]) -else - # Even with enable_debug=no we build with standard debug symbols - AX_CHECK_COMPILE_FLAG([-g],[[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g"]],,[[$CXXFLAG_WERROR]]) +if test "$enable_debug" = "yes"; then + + dnl Disable all optimizations + AX_CHECK_COMPILE_FLAG([-O0], [DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"], [], [$CXXFLAG_WERROR]) - # -ftrapv and -fwrapv conflict, so we only set this if enable_debug=no - AX_CHECK_COMPILE_FLAG([-fwrapv],[CXXFLAGS="$CXXFLAGS -fwrapv"],,[[$CXXFLAG_WERROR]]) + dnl Prefer -g3, fall back to -g if that is unavailable. + AX_CHECK_COMPILE_FLAG( + [-g3], + [DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g3"], + [AX_CHECK_COMPILE_FLAG([-g], [DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g"], [], [$CXXFLAG_WERROR])], + [$CXXFLAG_WERROR]) + + AX_CHECK_PREPROC_FLAG([-DDEBUG], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG"], [], [$CXXFLAG_WERROR]) + AX_CHECK_PREPROC_FLAG([-DDEBUG_LOCKORDER], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG_LOCKORDER"], [], [$CXXFLAG_WERROR]) + AX_CHECK_PREPROC_FLAG([-DDEBUG_LOCKCONTENTION], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG_LOCKCONTENTION"], [], [$CXXFLAG_WERROR]) + AX_CHECK_PREPROC_FLAG([-DRPC_DOC_CHECK], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DRPC_DOC_CHECK"], [], [$CXXFLAG_WERROR]) + AX_CHECK_PREPROC_FLAG([-DABORT_ON_FAILED_ASSUME], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DABORT_ON_FAILED_ASSUME"], [], [$CXXFLAG_WERROR]) + AX_CHECK_COMPILE_FLAG([-ftrapv], [DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -ftrapv"], [], [$CXXFLAG_WERROR]) fi -if test x$use_sanitizers != x; then - # First check if the compiler accepts flags. If an incompatible pair like - # -fsanitize=address,thread is used here, this check will fail. This will also - # fail if a bad argument is passed, e.g. -fsanitize=undfeined +if test "$use_sanitizers" != ""; then + dnl First check if the compiler accepts flags. If an incompatible pair like + dnl -fsanitize=address,thread is used here, this check will fail. This will also + dnl fail if a bad argument is passed, e.g. -fsanitize=undfeined AX_CHECK_COMPILE_FLAG( - [[-fsanitize=$use_sanitizers]], - [[SANITIZER_CXXFLAGS=-fsanitize=$use_sanitizers]], + [-fsanitize=$use_sanitizers], + [SANITIZER_CXXFLAGS="-fsanitize=$use_sanitizers" + SANITIZER_CFLAGS="-fsanitize=$use_sanitizers"], [AC_MSG_ERROR([compiler did not accept requested flags])]) - # Some compilers (e.g. GCC) require additional libraries like libasan, - # libtsan, libubsan, etc. Make sure linking still works with the sanitize - # flag. This is a separate check so we can give a better error message when - # the sanitize flags are supported by the compiler but the actual sanitizer - # libs are missing. + dnl Some compilers (e.g. GCC) require additional libraries like libasan, + dnl libtsan, libubsan, etc. Make sure linking still works with the sanitize + dnl flag. This is a separate check so we can give a better error message when + dnl the sanitize flags are supported by the compiler but the actual sanitizer + dnl libs are missing. AX_CHECK_LINK_FLAG( - [[-fsanitize=$use_sanitizers]], - [[SANITIZER_LDFLAGS=-fsanitize=$use_sanitizers]], - [AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])]) - - AX_CHECK_COMPILE_FLAG( - [-fno-omit-frame-pointer], - [SANITIZER_CXXFLAGS="$SANITIZER_CXXFLAGS -fno-omit-frame-pointer"], - [AC_MSG_ERROR(Cannot enable -fno-omit-frame-pointer)]) - - # libc/libstdc++ static linking is not supported for TSan - case $use_sanitizers in - *thread*) - ;; - *) - AX_CHECK_LINK_FLAG( - [-static-libstdc++], - [SANITIZER_LDFLAGS="$SANITIZER_LDFLAGS -static-libstdc++"], - [AC_MSG_ERROR(Cannot statically link -static-libstdc++)]) - ;; - esac - - case $use_sanitizers in - *address*) - AX_CHECK_LINK_FLAG( - [-static-libasan], - [SANITIZER_LDFLAGS="$SANITIZER_LDFLAGS -static-libasan"], - [AC_MSG_ERROR(Cannot statically link -static-libasan)]) - ;; - *) - ;; - esac + [-fsanitize=$use_sanitizers], + [SANITIZER_LDFLAGS="-fsanitize=$use_sanitizers"], + [AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])], + [], + [AC_LANG_PROGRAM([[ + #include + #include + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { return 0; } + __attribute__((weak)) // allow for libFuzzer linking + ]],[[]])]) fi ERROR_CXXFLAGS= -if test "x$enable_werror" = "xyes"; then - if test "x$CXXFLAG_WERROR" = "x"; then - AC_MSG_ERROR("enable-werror set but -Werror is not usable") +if test "$enable_werror" = "yes"; then + if test "$CXXFLAG_WERROR" = ""; then + AC_MSG_ERROR([enable-werror set but -Werror is not usable]) fi - ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror" + ERROR_CXXFLAGS=$CXXFLAG_WERROR fi -if test "x$CXXFLAGS_overridden" = "xno"; then - AX_CHECK_COMPILE_FLAG([-Wall],[CXXFLAGS="$CXXFLAGS -Wall"],,[[$CXXFLAG_WERROR]]) - AX_CHECK_COMPILE_FLAG([-Wextra],[CXXFLAGS="$CXXFLAGS -Wextra"],,[[$CXXFLAG_WERROR]]) - AX_CHECK_COMPILE_FLAG([-Wformat],[CXXFLAGS="$CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]]) - AX_CHECK_COMPILE_FLAG([-Wvla],[CXXFLAGS="$CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]]) - AX_CHECK_COMPILE_FLAG([-Wformat-security],[CXXFLAGS="$CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]]) - - ## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all - ## unknown options if any other warning is produced. Test the -Wfoo case, and - ## set the -Wno-foo case if it works. - AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[CXXFLAGS="$CXXFLAGS -Wno-unused-parameter"],,[[$CXXFLAG_WERROR]]) - AX_CHECK_COMPILE_FLAG([-Wself-assign],[CXXFLAGS="$CXXFLAGS -Wno-self-assign"],,[[$CXXFLAG_WERROR]]) - AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]]) - AX_CHECK_COMPILE_FLAG([-Wdeprecated-register],[CXXFLAGS="$CXXFLAGS -Wno-deprecated-register"],,[[$CXXFLAG_WERROR]]) -fi -CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" +AX_CHECK_COMPILE_FLAG([-Wall], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wall"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wextra], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wextra"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wgnu], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wgnu"], [], [$CXXFLAG_WERROR]) +dnl some compilers will ignore -Wformat-security without -Wformat, so just combine the two here. +AX_CHECK_COMPILE_FLAG([-Wformat -Wformat-security], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wformat -Wformat-security"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wvla], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wvla"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wshadow-field], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wshadow-field"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wthread-safety], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wthread-safety"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wloop-analysis], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wloop-analysis"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wredundant-decls], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wredundant-decls"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wunused-member-function], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunused-member-function"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wdate-time], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wdate-time"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wconditional-uninitialized], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wconditional-uninitialized"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wduplicated-branches], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wduplicated-branches"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wduplicated-cond], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wduplicated-cond"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wlogical-op], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wlogical-op"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Woverloaded-virtual], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Woverloaded-virtual"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wsuggest-override], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wsuggest-override"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wimplicit-fallthrough], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wimplicit-fallthrough"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wunreachable-code], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunreachable-code"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wdocumentation], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wdocumentation"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wself-assign], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wself-assign"], [], [$CXXFLAG_WERROR]) +AX_CHECK_COMPILE_FLAG([-Wundef], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wundef"], [], [$CXXFLAG_WERROR]) + +dnl Some compilers (gcc) ignore unknown -Wno-* options, but warn about all +dnl unknown options if any other warning is produced. Test the -Wfoo case, and +dnl set the -Wno-foo case if it works. +AX_CHECK_COMPILE_FLAG([-Wunused-parameter], [NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-parameter"], [], [$CXXFLAG_WERROR]) + +dnl Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review. +AX_CHECK_COMPILE_FLAG([-fno-extended-identifiers], [CXXFLAGS="$CXXFLAGS -fno-extended-identifiers"], [], [$CXXFLAG_WERROR]) + +dnl Currently all versions of gcc are subject to a class of bugs, see the +dnl gccbug_90348 test case (only reproduces on GCC 11 and earlier) and +dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111843. To work around that, set +dnl -fstack-reuse=none for all gcc builds. (Only gcc understands this flag) +AX_CHECK_COMPILE_FLAG([-fstack-reuse=none], [CXXFLAGS="$CXXFLAGS -fstack-reuse=none"]) enable_arm_crc=no enable_sse42=no @@ -340,6 +394,8 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ) CXXFLAGS="$TEMP_CXXFLAGS" +CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO" + AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], [build bitcoinz-cli bitcoinz-tx (default=yes)])], @@ -354,62 +410,50 @@ AC_ARG_WITH([libs], AC_ARG_WITH([daemon], [AS_HELP_STRING([--with-daemon], - [build bitcoind daemon (default=yes)])], + [build bitcoinzd daemon (default=yes)])], [build_bitcoind=$withval], [build_bitcoind=yes]) -use_pkgconfig=yes case $host in *mingw*) - - #pkgconfig does more harm than good with MinGW - use_pkgconfig=no - TARGET_OS=windows - AC_CHECK_LIB([mingwthrd], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([kernel32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([user32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([gdi32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([comdlg32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([winspool], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([winmm], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([shell32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([comctl32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([ole32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([oleaut32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([uuid], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([rpcrt4], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([advapi32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([ws2_32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([mswsock], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([shlwapi], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(lib missing)) - - # -static is interpreted by libtool, where it has a different meaning. - # In libtool-speak, it's -all-static. - AX_CHECK_LINK_FLAG([[-static]],[LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"]) - - AC_PATH_PROG([MAKENSIS], [makensis], none) - if test x$MAKENSIS = xnone; then - AC_MSG_WARN("makensis not found. Cannot create installer.") + AC_CHECK_LIB([kernel32], [GetModuleFileNameA], [], [AC_MSG_ERROR([libkernel32 missing])]) + AC_CHECK_LIB([user32], [main], [], [AC_MSG_ERROR([libuser32 missing])]) + AC_CHECK_LIB([gdi32], [main], [], [AC_MSG_ERROR([libgdi32 missing])]) + AC_CHECK_LIB([comdlg32], [main], [], [AC_MSG_ERROR([libcomdlg32 missing])]) + AC_CHECK_LIB([winmm], [main], [], [AC_MSG_ERROR([libwinmm missing])]) + AC_CHECK_LIB([shell32], [SHGetSpecialFolderPathW], [], [AC_MSG_ERROR([libshell32 missing])]) + AC_CHECK_LIB([comctl32], [main], [], [AC_MSG_ERROR([libcomctl32 missing])]) + AC_CHECK_LIB([ole32], [CoCreateInstance], [], [AC_MSG_ERROR([libole32 missing])]) + AC_CHECK_LIB([oleaut32], [main], [], [AC_MSG_ERROR([liboleaut32 missing])]) + AC_CHECK_LIB([uuid], [main], [], [AC_MSG_ERROR([libuuid missing])]) + AC_CHECK_LIB([advapi32], [CryptAcquireContextW], [], [AC_MSG_ERROR([libadvapi32 missing])]) + AC_CHECK_LIB([ws2_32], [WSAStartup], [], [AC_MSG_ERROR([libws2_32 missing])]) + AC_CHECK_LIB([shlwapi], [PathRemoveFileSpecW], [], [AC_MSG_ERROR([libshlwapi missing])]) + AC_CHECK_LIB([iphlpapi], [GetAdaptersAddresses], [], [AC_MSG_ERROR([libiphlpapi missing])]) + AC_CHECK_LIB([ntdll], [main], [], [AC_MSG_ERROR([libntdll missing])]) + AC_CHECK_LIB([bcrypt], [main], [], [AC_MSG_ERROR([libbcrypt missing])]) + + dnl -static is interpreted by libtool, where it has a different meaning. + dnl In libtool-speak, it's -all-static. + AX_CHECK_LINK_FLAG([-static], [LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"]) + + AC_PATH_PROG([MAKENSIS], [makensis], [none]) + if test "$MAKENSIS" = "none"; then + AC_MSG_WARN([makensis not found. Cannot create installer.]) fi - AC_PATH_TOOL(WINDRES, windres, none) - if test x$WINDRES = xnone; then - AC_MSG_ERROR("windres not found") + AC_PATH_TOOL([WINDRES], [windres], [none]) + if test "$WINDRES" = "none"; then + AC_MSG_ERROR([windres not found]) fi - CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB" - if test "x$CXXFLAGS_overridden" = "xno"; then - CXXFLAGS="$CXXFLAGS -w" - fi - case $host in - i?86-*) WINDOWS_BITS=32 ;; - x86_64-*) WINDOWS_BITS=64 ;; - *) AC_MSG_ERROR("Could not determine win32/win64 for installer") ;; - esac - AC_SUBST(WINDOWS_BITS) + CPPFLAGS="$CPPFLAGS -DSECP256K1_STATIC" + + CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -D_WIN32_WINNT=0x0601 -D_WIN32_IE=0x0501 -DWIN32_LEAN_AND_MEAN" + dnl Prevent the definition of min/max macros. + dnl We always want to use the standard library. + CPPFLAGS="$CPPFLAGS -DNOMINMAX" dnl libtool insists upon adding -nostdlib and a list of objects/libs to link against. dnl That breaks our ability to build dll's with static libgcc/libstdc++/libssp. Override @@ -419,34 +463,47 @@ case $host in archive_cmds_CXX="\$CC -shared \$libobjs \$deplibs \$compiler_flags -static -o \$output_objdir/\$soname \${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker \$lib" postdeps_CXX= + dnl We require Windows 7 (NT 6.1) or later + AX_CHECK_LINK_FLAG([-Wl,--major-subsystem-version -Wl,6 -Wl,--minor-subsystem-version -Wl,1], [LDFLAGS="$LDFLAGS -Wl,--major-subsystem-version -Wl,6 -Wl,--minor-subsystem-version -Wl,1"], [], [$LDFLAG_WERROR]) + + dnl Avoid the use of aligned vector instructions when building for Windows. + dnl See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412. + AX_CHECK_COMPILE_FLAG([-Wa,-muse-unaligned-vector-move], [CXXFLAGS="$CXXFLAGS -Wa,-muse-unaligned-vector-move"], [], [$CXXFLAG_WERROR]) ;; *darwin*) TARGET_OS=darwin - if test x$cross_compiling != xyes; then + if test $cross_compiling != "yes"; then BUILD_OS=darwin - AC_CHECK_PROG([PORT],port, port) - if test x$PORT = xport; then - dnl add default macports paths - CPPFLAGS="$CPPFLAGS -isystem /opt/local/include" - LIBS="$LIBS -L/opt/local/lib" - if test -d /opt/local/include/db62; then - CPPFLAGS="$CPPFLAGS -I/opt/local/include/db62" - LIBS="$LIBS -L/opt/local/lib/db62" - fi - fi - AC_CHECK_PROG([BREW],brew, brew) - if test x$BREW = xbrew; then + AX_CHECK_LINK_FLAG([-Wl,-headerpad_max_install_names], [LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names"], [], [$LDFLAG_WERROR]) + + AC_CHECK_PROG([BREW], [brew], [brew]) + if test "$BREW" = "brew"; then dnl These Homebrew packages may be keg-only, meaning that they won't be found dnl in expected paths because they may conflict with system files. Ask dnl Homebrew where each one is located, then adjust paths accordingly. dnl It's safe to add these paths even if the functionality is disabled by - dnl the user (--without-wallet for example). + dnl the user (--without-wallet or --without-gui for example). + + dnl Homebrew may create symlinks in /usr/local/include for some packages. + dnl Because MacOS's clang internally adds "-I /usr/local/include" to its search + dnl paths, this will negate efforts to use -isystem for those packages, as they + dnl will be found first in /usr/local. Use the internal "-internal-isystem" + dnl option to system-ify all /usr/local/include paths without adding it to the list + dnl of search paths in case it's not already there. + if test "$suppress_external_warnings" != "no"; then + AX_CHECK_PREPROC_FLAG([-Xclang -internal-isystem -Xclang /usr/local/include/], [CPPFLAGS="$CPPFLAGS -Xclang -internal-isystem -Xclang /usr/local/include/"], [], [$CXXFLAG_WERROR]) + fi - bdb_prefix=`$BREW --prefix berkeley-db4 2>/dev/null` - if test x$bdb_prefix != x; then - CPPFLAGS="$CPPFLAGS -I$bdb_prefix/include" - LIBS="$LIBS -L$bdb_prefix/lib" + if test $BREW list --versions berkeley-db@4 >/dev/null && test "$BDB_CFLAGS" = "" && test "$BDB_LIBS" = ""; then + bdb_prefix=$($BREW --prefix berkeley-db@4 2>/dev/null) + dnl This must precede the call to BITCOIN_FIND_BDB62 below. + BDB_CFLAGS="-I$bdb_prefix/include" + BDB_LIBS="-L$bdb_prefix/lib -ldb_cxx-4.8" + fi + + if $BREW list --versions qt@5 >/dev/null; then + export PKG_CONFIG_PATH="$($BREW --prefix qt@5 2>/dev/null)/lib/pkgconfig:$PKG_CONFIG_PATH" fi fi else @@ -455,9 +512,8 @@ case $host in BUILD_OS=darwin ;; *) - AC_PATH_TOOL([INSTALLNAMETOOL], [install_name_tool], install_name_tool) - AC_PATH_TOOL([OTOOL], [otool], otool) - AC_PATH_PROGS([GENISOIMAGE], [genisoimage mkisofs],genisoimage) + AC_PATH_TOOL([DSYMUTIL], [dsymutil], [dsymutil]) + AC_PATH_PROG([ZIP], [zip], [zip]) dnl libtool will try to strip the static lib, which is a problem for dnl cross-builds because strip attempts to call a hard-coded ld, @@ -468,41 +524,59 @@ case $host in esac fi - AX_CHECK_LINK_FLAG([[-Wl,-headerpad_max_install_names]], [LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names"]) - CPPFLAGS="$CPPFLAGS -DMAC_OSX" - OBJCXXFLAGS="$CXXFLAGS" + CPPFLAGS="$CPPFLAGS -DMAC_OSX -DOBJC_OLD_DISPATCH_PROTOTYPES=0" + + dnl ignore deprecated-declarations warnings coming from objcxx code + dnl "'NSUserNotificationCenter' is deprecated: first deprecated in macOS 11.0". + OBJCXXFLAGS="$CXXFLAGS -Wno-deprecated-declarations" ;; *linux*) TARGET_OS=linux ;; - *) - ;; esac -if test x$use_pkgconfig = xyes; then - m4_ifndef([PKG_PROG_PKG_CONFIG], [AC_MSG_ERROR(PKG_PROG_PKG_CONFIG macro not found. Please install pkg-config and re-run autogen.sh.)]) - m4_ifdef([PKG_PROG_PKG_CONFIG], [ - PKG_PROG_PKG_CONFIG - if test x"$PKG_CONFIG" = "x"; then - AC_MSG_ERROR(pkg-config not found.) +if test "$use_lcov" = "yes"; then + if test "$LCOV" = ""; then + AC_MSG_ERROR([lcov testing requested but lcov not found]) fi - ]) -fi - -if test x$use_lcov = xyes; then - if test x$LCOV = x; then - AC_MSG_ERROR("lcov testing requested but lcov not found") - fi - if test x$GCOV = x; then - AC_MSG_ERROR("lcov testing requested but gcov not found") + if test "$PYTHON" = ""; then + AC_MSG_ERROR([lcov testing requested but python not found]) fi - if test x$GENHTML = x; then - AC_MSG_ERROR("lcov testing requested but genhtml not found") + if test "$GENHTML" = ""; then + AC_MSG_ERROR([lcov testing requested but genhtml not found]) fi - LCOV="$LCOV --gcov-tool=$GCOV --rc lcov_branch_coverage=1" - GENHTML="$GENHTML --branch-coverage" + + AC_MSG_CHECKING([whether compiler is Clang]) + AC_PREPROC_IFELSE([AC_LANG_SOURCE([[ + #if defined(__clang__) && defined(__llvm__) + // Compiler is Clang + #else + # error Compiler is not Clang + #endif + ]])],[ + AC_MSG_RESULT([yes]) + if test "$LLVM_COV" = ""; then + AC_MSG_ERROR([lcov testing requested but llvm-cov not found]) + fi + COV_TOOL="$LLVM_COV gcov" + ],[ + AC_MSG_RESULT([no]) + if test "$GCOV" = "x"; then + AC_MSG_ERROR([lcov testing requested but gcov not found]) + fi + COV_TOOL="$GCOV" + ]) + AC_SUBST(COV_TOOL) + AC_SUBST(COV_TOOL_WRAPPER, "cov_tool_wrapper.sh") + LCOV="$LCOV --gcov-tool $(pwd)/$COV_TOOL_WRAPPER" + + AX_CHECK_LINK_FLAG([--coverage], [LDFLAGS="$LDFLAGS --coverage"], + [AC_MSG_ERROR([lcov testing requested but --coverage linker flag does not work])]) AX_CHECK_COMPILE_FLAG([--coverage],[CXXFLAGS="$CXXFLAGS --coverage"], - [AC_MSG_ERROR("lcov testing requested but --coverage flag does not work")]) + [AC_MSG_ERROR([lcov testing requested but --coverage flag does not work])]) + CXXFLAGS="$CXXFLAGS -Og" + + AC_SUBST(LCOV_OPTS) fi dnl Check for endianness @@ -511,132 +585,105 @@ AC_C_BIGENDIAN dnl Check for pthread compile/link requirements AX_PTHREAD -# The following macro will add the necessary defines to bitcoin-config.h, but -# they also need to be passed down to any subprojects. Pull the results out of -# the cache and add them to CPPFLAGS. +dnl Check if -latomic is required for +CHECK_ATOMIC + +dnl The following macro will add the necessary defines to bitcoin-config.h, but +dnl they also need to be passed down to any subprojects. Pull the results out of +dnl the cache and add them to CPPFLAGS. AC_SYS_LARGEFILE -# detect POSIX or GNU variant of strerror_r +dnl detect POSIX or GNU variant of strerror_r AC_FUNC_STRERROR_R -if test x$ac_cv_sys_file_offset_bits != x && - test x$ac_cv_sys_file_offset_bits != xno && - test x$ac_cv_sys_file_offset_bits != xunknown; then +if test "$ac_cv_sys_file_offset_bits" != "" && + test "$ac_cv_sys_file_offset_bits" != "no" && + test "$ac_cv_sys_file_offset_bits" != "unknown"; then CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits" fi -if test x$ac_cv_sys_large_files != x && - test x$ac_cv_sys_large_files != xno && - test x$ac_cv_sys_large_files != xunknown; then +if test "$ac_cv_sys_large_files" != "" && + test "$ac_cv_sys_large_files" != "no" && + test "$ac_cv_sys_large_files" != "unknown"; then CPPFLAGS="$CPPFLAGS -D_LARGE_FILES=$ac_cv_sys_large_files" fi -AX_CHECK_LINK_FLAG([[-Wl,--large-address-aware]], [LDFLAGS="$LDFLAGS -Wl,--large-address-aware"]) - -AX_GCC_FUNC_ATTRIBUTE([visibility]) -AX_GCC_FUNC_ATTRIBUTE([dllexport]) -AX_GCC_FUNC_ATTRIBUTE([dllimport]) - -if test x$use_glibc_compat != xno; then - - #glibc absorbed clock_gettime in 2.17. librt (its previous location) is safe to link - #in anyway for back-compat. - AC_CHECK_LIB([rt],[clock_gettime],, AC_MSG_ERROR(lib missing)) - - #__fdelt_chk's params and return type have changed from long unsigned int to long int. - # See which one is present here. - AC_MSG_CHECKING(__fdelt_chk type) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#ifdef _FORTIFY_SOURCE - #undef _FORTIFY_SOURCE - #endif - #define _FORTIFY_SOURCE 2 - #include - extern "C" long unsigned int __fdelt_warn(long unsigned int);]],[[]])], - [ fdelt_type="long unsigned int"], - [ fdelt_type="long int"]) - AC_MSG_RESULT($fdelt_type) - AC_DEFINE_UNQUOTED(FDELT_TYPE, $fdelt_type,[parameter and return value type for __fdelt_chk]) -else - AC_SEARCH_LIBS([clock_gettime],[rt]) -fi - -if test "x$enable_gprof" = xyes; then - dnl -pg is incompatible with -pie. Since hardening and profiling together doesn't make sense, - dnl we simply make them mutually exclusive here. Additionally, hardened toolchains may force - dnl -pie by default, in which case it needs to be turned off with -no-pie. - - if test x$use_hardening = xyes; then - AC_MSG_ERROR(gprof profiling is not compatible with hardening. Reconfigure with --disable-hardening or --disable-gprof) - fi - use_hardening=no - AX_CHECK_COMPILE_FLAG([-pg],[GPROF_CXXFLAGS="-pg"], - [AC_MSG_ERROR(gprof profiling requested but not available)], [[$CXXFLAG_WERROR]]) - - AX_CHECK_LINK_FLAG([[-no-pie]], [GPROF_LDFLAGS="-no-pie"]) - AX_CHECK_LINK_FLAG([[-pg]],[GPROF_LDFLAGS="$GPROF_LDFLAGS -pg"], - [AC_MSG_ERROR(gprof profiling requested but not available)], [[$GPROF_LDFLAGS]]) -fi - -if test x$TARGET_OS != xwindows; then - # All windows code is PIC, forcing it on just adds useless compile warnings - AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"]) +if test "$TARGET_OS" != "windows"; then + dnl All windows code is PIC, forcing it on just adds useless compile warnings + AX_CHECK_COMPILE_FLAG([-fPIC], [PIC_FLAGS="-fPIC"]) fi if test x$use_fuzz_main == xyes; then - CFLAGS="$CFLAGS -DZCASH_FUZZ=1" - CXXFLAGS="$CXXFLAGS -DZCASH_FUZZ=1" + CFLAGS="$CFLAGS -DZCASH_FUZZ=1" + CXXFLAGS="$CXXFLAGS -DZCASH_FUZZ=1" fi -if test x$use_hardening != xno; then - use_hardening=yes - AX_CHECK_COMPILE_FLAG([-Wformat],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wformat"],[AC_MSG_ERROR(Cannot enable -Wformat)]) - AX_CHECK_COMPILE_FLAG([-Wformat-security],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wformat-security"],[AC_MSG_ERROR(Cannot enable -Wformat-security)],[-Wformat]) - AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"],[AC_MSG_ERROR(Cannot enable -Wstack-protector)]) - AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"],[AC_MSG_ERROR(Cannot enable -fstack-protector-all)]) - - AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=2],[ - AX_CHECK_PREPROC_FLAG([-U_FORTIFY_SOURCE],[ - HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -U_FORTIFY_SOURCE" - ],[AC_MSG_ERROR(Cannot enable -U_FORTIFY_SOURCE)]) - HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -D_FORTIFY_SOURCE=2" - ],[AC_MSG_ERROR(Cannot enable -D_FORTIFY_SOURCE=2)]) - - if test x$TARGET_OS = xdarwin || test x$TARGET_OS = xwindows; then - # Xcode's ld (at least ld64-302.3) doesn't support -z - # mingw-w64's ld (at least mingw-w64 4.0.4-2) also appears to not support -z - AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"],[AC_MSG_WARN(Cannot enable RELRO)]) - AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"],[AC_MSG_WARN(Cannot enable BIND_NOW)]) - else - AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"],[AC_MSG_ERROR(Cannot enable RELRO)]) - AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"],[AC_MSG_ERROR(Cannot enable BIND_NOW)]) - fi +if test "$use_hardening" != "no"; then + AX_CHECK_COMPILE_FLAG([-Wstack-protector], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"]) + AX_CHECK_COMPILE_FLAG([-fstack-protector-all], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"]) - if test x$TARGET_OS != xwindows; then - # All windows code is PIC, forcing it on just adds useless compile warnings - AX_CHECK_COMPILE_FLAG([-fPIE],[PIE_FLAGS="-fPIE"],[AC_MSG_ERROR(Cannot enable -fPIE)]) - if test x$BUILD_OS = xdarwin; then - AX_CHECK_LINK_FLAG([[-Wl,-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-pie"],[AC_MSG_ERROR(Cannot enable -Wl,-pie)]) - else - AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"],[AC_MSG_ERROR(Cannot enable -pie)]) - fi - else - # These are only available on Windows. - AX_CHECK_LINK_FLAG([[-Wl,--dynamicbase]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--dynamicbase"],[AC_MSG_ERROR(Cannot enable --dynamicbase)]) - AX_CHECK_LINK_FLAG([[-Wl,--nxcompat]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--nxcompat"],[AC_MSG_ERROR(Cannot enable --nxcompat)]) - AX_CHECK_LINK_FLAG([[-Wl,--high-entropy-va]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"],[AC_MSG_ERROR(Cannot enable ASLR)]) - fi + AX_CHECK_COMPILE_FLAG([-fcf-protection=full], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fcf-protection=full"]) case $host in *mingw*) - AC_CHECK_LIB([ssp], [main],, AC_MSG_ERROR(lib missing)) + dnl stack-clash-protection doesn't compile with GCC 10 and earlier. + dnl In any case, it is a no-op for Windows. + dnl See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90458 for more details. + ;; + *) + AX_CHECK_COMPILE_FLAG([-fstack-clash-protection], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-clash-protection"], [], [$CXXFLAG_WERROR]) + ;; + esac + + case $host in + *aarch64*) + AX_CHECK_COMPILE_FLAG([-mbranch-protection=bti], [HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -mbranch-protection=bti"]) ;; esac + + dnl When enable_debug is yes, all optimizations are disabled. + dnl However, FORTIFY_SOURCE requires that there is some level of optimization, otherwise it does nothing and just creates a compiler warning. + dnl Since FORTIFY_SOURCE is a no-op without optimizations, do not enable it when enable_debug is yes. + if test "$enable_debug" != "yes"; then + AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=3],[ + AX_CHECK_PREPROC_FLAG([-U_FORTIFY_SOURCE],[ + HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -U_FORTIFY_SOURCE" + ]) + HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -D_FORTIFY_SOURCE=3" + ]) + fi + + AX_CHECK_LINK_FLAG([-Wl,--enable-reloc-section], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--enable-reloc-section"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,--dynamicbase], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--dynamicbase"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,--nxcompat], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--nxcompat"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,--high-entropy-va], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,-z,relro], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,-z,now], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,-z,separate-code], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,separate-code"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-fPIE -pie], [PIE_FLAGS="-fPIE"; HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"], [], [$CXXFLAG_WERROR]) fi -dnl this flag screws up non-darwin gcc even when the check fails. special-case it. -if test x$TARGET_OS = xdarwin; then - AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"]) +dnl These flags are specific to ld64, and may cause issues with other linkers. +dnl For example: GNU ld will interpret -dead_strip as -de and then try and use +dnl "ad_strip" as the symbol for the entry point. +if test "$TARGET_OS" = "darwin"; then + AX_CHECK_LINK_FLAG([-Wl,-dead_strip], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,-dead_strip_dylibs], [LDFLAGS="$LDFLAGS -Wl,-dead_strip_dylibs"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,-fixup_chains], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-fixup_chains"], [], [$LDFLAG_WERROR]) fi +AC_CHECK_HEADERS([sys/select.h sys/prctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h]) + +AC_CHECK_DECLS([getifaddrs, freeifaddrs],[CHECK_SOCKET],, + [#include + #include ] +) + +dnl These are used for daemonization in bitcoind +AC_CHECK_DECLS([fork]) +AC_CHECK_DECLS([setsid]) + +AC_CHECK_DECLS([pipe2]) + AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h]) AC_SEARCH_LIBS([getaddrinfo_a], [anl], [AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define this symbol if you have getaddrinfo_a])]) AC_SEARCH_LIBS([inet_pton], [nsl resolv], [AC_DEFINE(HAVE_INET_PTON, 1, [Define this symbol if you have inet_pton])]) @@ -680,27 +727,28 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([ ] ) -dnl LevelDB platform checks -AC_MSG_CHECKING(for fdatasync) +AC_MSG_CHECKING([for fdatasync]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ fdatasync(0); ]])], - [ AC_MSG_RESULT(yes); HAVE_FDATASYNC=1 ], - [ AC_MSG_RESULT(no); HAVE_FDATASYNC=0 ] + [ AC_MSG_RESULT([yes]); HAVE_FDATASYNC=1 ], + [ AC_MSG_RESULT([no]); HAVE_FDATASYNC=0 ] ) +AC_DEFINE_UNQUOTED([HAVE_FDATASYNC], [$HAVE_FDATASYNC], [Define to 1 if fdatasync is available.]) -AC_MSG_CHECKING(for F_FULLFSYNC) +AC_MSG_CHECKING([for F_FULLFSYNC]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ fcntl(0, F_FULLFSYNC, 0); ]])], - [ AC_MSG_RESULT(yes); HAVE_FULLFSYNC=1 ], - [ AC_MSG_RESULT(no); HAVE_FULLFSYNC=0 ] + [ AC_MSG_RESULT([yes]); HAVE_FULLFSYNC=1 ], + [ AC_MSG_RESULT([no]); HAVE_FULLFSYNC=0 ] ) -AC_MSG_CHECKING(for O_CLOEXEC) +AC_MSG_CHECKING([for O_CLOEXEC]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ open("", O_CLOEXEC); ]])], - [ AC_MSG_RESULT(yes); HAVE_O_CLOEXEC=1 ], - [ AC_MSG_RESULT(no); HAVE_O_CLOEXEC=0 ] + [ AC_MSG_RESULT([yes]); HAVE_O_CLOEXEC=1 ], + [ AC_MSG_RESULT([no]); HAVE_O_CLOEXEC=0 ] ) +AC_DEFINE_UNQUOTED([HAVE_O_CLOEXEC], [$HAVE_O_CLOEXEC], [Define to 1 if O_CLOEXEC flag is available.]) dnl crc32c platform checks AC_MSG_CHECKING([for __builtin_prefetch]) @@ -733,17 +781,28 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ [ AC_MSG_RESULT([no]); HAVE_STRONG_GETAUXVAL=0 ] ) -LIBMEMENV= -AC_SUBST(LIBMEMENV) - -if test x$enable_wallet != xno; then +dnl SUPPRESSED_CPPFLAGS=SUPPRESS_WARNINGS([$SOME_CPPFLAGS]) +dnl Replace -I with -isystem in $SOME_CPPFLAGS to suppress warnings from +dnl headers from its include directories and return the result. +dnl See -isystem documentation: +dnl https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html +dnl https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-isystem-directory +dnl Do not change "-I/usr/include" to "-isystem /usr/include" because that +dnl is not necessary (/usr/include is already a system directory) and because +dnl it would break GCC's #include_next. +AC_DEFUN([SUPPRESS_WARNINGS], + [[$(echo $1 |${SED} -E -e 's/(^| )-I/\1-isystem /g' -e 's;-isystem /usr/include/*( |$);-I/usr/include\1;g')]]) + +if test "$enable_wallet" != "no"; then dnl Check for libdb_cxx only if wallet enabled BITCOIN_FIND_BDB62 + if test "$suppress_external_warnings" != "no" ; then + BDB_CPPFLAGS=SUPPRESS_WARNINGS($BDB_CPPFLAGS) + fi fi dnl Check to find the libsodium headers/libraries -AC_CHECK_LIB(sodium, sodium_init,[], -[AC_MSG_ERROR([The Sodium crypto library libraries not found.])]) +PKG_CHECK_MODULES([SODIUM], [libsodium],,[AC_MSG_ERROR(libsodium not found.)]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ #include @@ -755,123 +814,137 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ [AC_MSG_RESULT([checking for version of libsodium... yes])], [AC_MSG_ERROR([Wrong libsodium: version >= 1.0.8 required])]) -if test x$build_bitcoin_utils$build_bitcoind$use_tests = xnonono; then +if test "$build_bitcoin_utils$build_bitcoind$use_tests" = "nonono"; then use_boost=no else use_boost=yes fi -if test x$use_boost = xyes; then +if test "$use_boost" = "yes"; then -dnl Check for boost libs -dnl We need Boost >= 1.62 to fix a potential security bug (https://github.com/zcash/zcash/issues/1241) -AX_BOOST_BASE([1.62]) -AX_BOOST_SYSTEM -AX_BOOST_FILESYSTEM -AX_BOOST_PROGRAM_OPTIONS -AX_BOOST_THREAD -AX_BOOST_CHRONO + dnl Check for Boost headers + AX_BOOST_BASE([1.73.0],[],[AC_MSG_ERROR([Boost is not available!])]) + AX_BOOST_SYSTEM + AX_BOOST_FILESYSTEM + AX_BOOST_PROGRAM_OPTIONS + AX_BOOST_THREAD + AX_BOOST_CHRONO -fi + if test "$want_boost" = "no"; then + AC_MSG_ERROR([Boost is required]) + fi -if test x$use_reduce_exports = xyes; then - CXXFLAGS="$CXXFLAGS $RE_CXXFLAGS" - AX_CHECK_LINK_FLAG([[-Wl,--exclude-libs,ALL]], [RELDFLAGS="-Wl,--exclude-libs,ALL"]) -fi + dnl we don't use multi_index serialization + BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION" -if test x$use_tests = xyes; then + dnl Prevent use of std::unary_function, which was removed in C++17, + dnl and will generate warnings with newer compilers for Boost + dnl older than 1.80. + dnl See: https://github.com/boostorg/config/pull/430. + AX_CHECK_PREPROC_FLAG([-DBOOST_NO_CXX98_FUNCTION_BASE], [BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_NO_CXX98_FUNCTION_BASE"], [], [$CXXFLAG_WERROR], + [AC_LANG_PROGRAM([[#include ]])]) - if test x$HEXDUMP = x; then - AC_MSG_ERROR(hexdump is required for tests) + if test "$suppress_external_warnings" != "no"; then + BOOST_CPPFLAGS=SUPPRESS_WARNINGS($BOOST_CPPFLAGS) fi +fi +dnl Check for reduced exports +if test "$use_reduce_exports" = "yes"; then + AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden"], + [AC_MSG_ERROR([Cannot set hidden symbol visibility. Use --disable-reduce-exports.])], [$CXXFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,--exclude-libs,ALL], [RELDFLAGS="-Wl,--exclude-libs,ALL"], [], [$LDFLAG_WERROR]) + AX_CHECK_LINK_FLAG([-Wl,-no_exported_symbols], [LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -Wl,-no_exported_symbols"], [], [$LDFLAG_WERROR]) +fi - if test x$use_boost = xyes; then - - AX_BOOST_UNIT_TEST_FRAMEWORK - - dnl Determine if -DBOOST_TEST_DYN_LINK is needed - AC_MSG_CHECKING([for dynamic linked boost test]) - TEMP_LIBS="$LIBS" - LIBS="$LIBS $BOOST_LDFLAGS $BOOST_UNIT_TEST_FRAMEWORK_LIB" - TEMP_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - AC_LINK_IFELSE([AC_LANG_SOURCE([ - #define BOOST_TEST_DYN_LINK - #define BOOST_TEST_MAIN - #include +if test "$use_tests" = "yes"; then - ])], - [AC_MSG_RESULT(yes)] - [TESTDEFS="$TESTDEFS -DBOOST_TEST_DYN_LINK"], - [AC_MSG_RESULT(no)]) - LIBS="$TEMP_LIBS" - CPPFLAGS="$TEMP_CPPFLAGS" + if test "$HEXDUMP" = ""; then + AC_MSG_ERROR([hexdump is required for tests]) + fi + if test "$use_boost" = "yes"; then + + AX_BOOST_UNIT_TEST_FRAMEWORK + + dnl Determine if -DBOOST_TEST_DYN_LINK is needed + AC_MSG_CHECKING([for dynamic linked boost test]) + TEMP_LIBS="$LIBS" + LIBS="$LIBS $BOOST_LDFLAGS $BOOST_UNIT_TEST_FRAMEWORK_LIB" + TEMP_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + AC_LINK_IFELSE([AC_LANG_SOURCE([ + #define BOOST_TEST_DYN_LINK + #define BOOST_TEST_MAIN + #include + + ])], + [AC_MSG_RESULT(yes)] + [TESTDEFS="$TESTDEFS -DBOOST_TEST_DYN_LINK"], + [AC_MSG_RESULT(no)]) + LIBS="$TEMP_LIBS" + CPPFLAGS="$TEMP_CPPFLAGS" fi fi -if test x$use_boost = xyes; then -BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_PROGRAM_OPTIONS_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB" +if test "$use_boost" = "yes"; then + BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_PROGRAM_OPTIONS_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB" fi -if test x$use_pkgconfig = xyes; then - : dnl - m4_ifdef( - [PKG_CHECK_MODULES], - [ - if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then - PKG_CHECK_MODULES([EVENT], [libevent],, [AC_MSG_ERROR(libevent not found.)]) - if test x$TARGET_OS != xwindows; then - PKG_CHECK_MODULES([EVENT_PTHREADS], [libevent_pthreads],, [AC_MSG_ERROR(libevent_pthreads not found.)]) - fi - fi +dnl libevent check - if test "x$use_zmq" = "xyes"; then - PKG_CHECK_MODULES([ZMQ],[libzmq >= 4], - [AC_DEFINE([ENABLE_ZMQ],[1],[Define to 1 to enable ZMQ functions])], - [AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) - AC_MSG_WARN([libzmq version 4.x or greater not found, disabling]) - use_zmq=no]) - else - AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) - fi - ] - ) -else - - if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then - AC_CHECK_HEADER([event2/event.h],, AC_MSG_ERROR(libevent headers missing),) - AC_CHECK_LIB([event],[main],EVENT_LIBS=-levent,AC_MSG_ERROR(libevent missing)) - if test x$TARGET_OS != xwindows; then - AC_CHECK_LIB([event_pthreads],[main],EVENT_PTHREADS_LIBS=-levent_pthreads,AC_MSG_ERROR(libevent_pthreads missing)) - fi +use_libevent=no +if test "$build_bitcoin_utils$build_bitcoind$use_tests$use_bench" != "nononono"; then + PKG_CHECK_MODULES([EVENT], [libevent >= 2.1.8], [use_libevent=yes], [AC_MSG_ERROR([libevent version 2.1.8 or greater not found.])]) + if test "$TARGET_OS" != "windows"; then + PKG_CHECK_MODULES([EVENT_PTHREADS], [libevent_pthreads >= 2.1.8], [], [AC_MSG_ERROR([libevent_pthreads version 2.1.8 or greater not found.])]) fi - if test "x$use_zmq" = "xyes"; then - AC_CHECK_HEADER([zmq.h], - [AC_DEFINE([ENABLE_ZMQ],[1],[Define to 1 to enable ZMQ functions])], - [AC_MSG_WARN([zmq.h not found, disabling zmq support]) - use_zmq=no - AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])]) - AC_CHECK_LIB([zmq],[zmq_ctx_shutdown],ZMQ_LIBS=-lzmq, - [AC_MSG_WARN([libzmq >= 4.0 not found, disabling zmq support]) - use_zmq=no - AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])]) - else - AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) + if test "$suppress_external_warnings" != "no"; then + EVENT_CFLAGS=SUPPRESS_WARNINGS($EVENT_CFLAGS) fi +fi - if test "x$use_zmq" = "xyes"; then - dnl Assume libzmq was built for static linking - case $host in - *mingw*) - ZMQ_CFLAGS="$ZMQ_CFLAGS -DZMQ_STATIC" - ;; - esac - fi +if test x$use_libevent = xyes; then + TEMP_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $EVENT_CFLAGS" + AC_MSG_CHECKING([if evhttp_connection_get_peer expects const char**]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]], [[ + evhttp_connection *conn = (evhttp_connection *)1; + const char *host; + uint16_t port; + + evhttp_connection_get_peer(conn, &host, &port); + ]])], + [ AC_MSG_RESULT([yes]); AC_DEFINE([HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR], [1], [Define this symbol if evhttp_connection_get_peer expects const char**]) ], + [ AC_MSG_RESULT([no]) ] + ) + CXXFLAGS="$TEMP_CXXFLAGS" fi +dnl ZMQ check + +if test "$use_zmq" = "yes"; then + PKG_CHECK_MODULES([ZMQ], [libzmq >= 4], + AC_DEFINE([ENABLE_ZMQ], [1], [Define this symbol to enable ZMQ functions]), + [AC_MSG_WARN([libzmq version 4.x or greater not found, disabling]) + use_zmq=no]) +fi + +if test "$use_zmq" = "yes"; then + dnl Assume libzmq was built for static linking + case $host in + *mingw*) + ZMQ_CFLAGS="$ZMQ_CFLAGS -DZMQ_STATIC" + ;; + esac +fi + +AM_CONDITIONAL([ENABLE_ZMQ], [test "$use_zmq" = "yes"]) + RUST_LIBS="-lrustzcash" case $host in *mingw*) @@ -881,23 +954,19 @@ case $host in ;; esac -# Additional BitcoinZ flags -AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing],[CXXFLAGS="$CXXFLAGS -fno-strict-aliasing"]) -AX_CHECK_COMPILE_FLAG([-Wno-builtin-declaration-mismatch],[CXXFLAGS="$CXXFLAGS -Wno-builtin-declaration-mismatch"],,[[$CXXFLAG_WERROR]]) - LIBZCASH_LIBS="$BOOST_SYSTEM_LIB -lsodium $RUST_LIBS" -AC_MSG_CHECKING([whether to build bitcoind]) -AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) +AC_MSG_CHECKING([whether to build bitcoinzd]) +AM_CONDITIONAL([BUILD_BITCOIND], [test $build_bitcoind = "yes"]) AC_MSG_RESULT($build_bitcoind) AC_MSG_CHECKING([whether to build utils (bitcoinz-cli bitcoinz-tx)]) -AM_CONDITIONAL([BUILD_BITCOIN_UTILS], [test x$build_bitcoin_utils = xyes]) +AM_CONDITIONAL([BUILD_BITCOIN_UTILS], [test $build_bitcoin_utils = "yes"]) AC_MSG_RESULT($build_bitcoin_utils) AC_MSG_CHECKING([whether to build libraries]) -AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test x$build_bitcoin_libs = xyes]) -if test x$build_bitcoin_libs = xyes; then +AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test $build_bitcoin_libs = "yes"]) +if test $build_bitcoin_libs = "yes"; then AC_DEFINE(HAVE_CONSENSUS_LIB, 1, [Define this symbol if the consensus lib has been built]) AC_CONFIG_FILES([libzcashconsensus.pc:libzcashconsensus.pc.in]) fi @@ -906,10 +975,10 @@ AC_MSG_RESULT($build_bitcoin_libs) AC_LANG_POP -if test "x$use_ccache" != "xno"; then - AC_MSG_CHECKING(if ccache should be used) - if test x$CCACHE = x; then - if test "x$use_ccache" = "xyes"; then +if test "$use_ccache" != "no"; then + AC_MSG_CHECKING([if ccache should be used]) + if test "$CCACHE" = ""; then + if test "$use_ccache" = "yes"; then AC_MSG_ERROR([ccache not found.]); else use_ccache=no @@ -920,35 +989,34 @@ if test "x$use_ccache" != "xno"; then CXX="$ac_cv_path_CCACHE $CXX" fi AC_MSG_RESULT($use_ccache) -fi -if test "x$use_ccache" = "xyes"; then - AX_CHECK_PREPROC_FLAG([-Qunused-arguments],[CPPFLAGS="-Qunused-arguments $CPPFLAGS"]) + if test "$use_ccache" = "yes"; then + AX_CHECK_COMPILE_FLAG([-fdebug-prefix-map=A=B], [DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -fdebug-prefix-map=\$(abs_top_srcdir)=."], [], [$CXXFLAG_WERROR]) + AX_CHECK_PREPROC_FLAG([-fmacro-prefix-map=A=B], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -fmacro-prefix-map=\$(abs_top_srcdir)=."], [], [$CXXFLAG_WERROR]) + fi fi dnl enable wallet AC_MSG_CHECKING([if wallet should be enabled]) -if test x$enable_wallet != xno; then - AC_MSG_RESULT(yes) +if test "$enable_wallet" != "no"; then + AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([ENABLE_WALLET],[1],[Define to 1 to enable wallet functions]) + enable_wallet=yes else - AC_MSG_RESULT(no) + AC_MSG_RESULT([no]) fi dnl enable mining AC_MSG_CHECKING([if mining should be enabled]) -if test x$enable_mining != xno; then +if test "$enable_mining" != "no"; then AC_MSG_RESULT(yes) AC_DEFINE(ENABLE_MINING, 1, [Define to 1 to enable mining functions]) - else AC_MSG_RESULT(no) fi -AM_CONDITIONAL([ENABLE_ZMQ], [test "x$use_zmq" = "xyes"]) - -AC_MSG_CHECKING([whether to build test_bitcoinz]) -if test x$use_tests = xyes; then +AC_MSG_CHECKING([whether to build test_bitcoin]) +if test "$use_tests" = "yes"; then AC_MSG_RESULT([yes]) BUILD_TEST="yes" else @@ -957,29 +1025,29 @@ else fi AC_MSG_CHECKING([whether to reduce exports]) -if test x$use_reduce_exports = xyes; then +if test "$use_reduce_exports" = "yes"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi -if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$use_tests = xnononono; then - AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon or --enable-tests]) +if test "$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$use_tests$use_tests" = "nonononono"; then + AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --enable-bench or --enable-tests]) fi -AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) -AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin]) -AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows]) -AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes]) -AM_CONDITIONAL([ENABLE_MINING],[test x$enable_mining = xyes]) -AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes]) -AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes]) -AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) -AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) -AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) -AM_CONDITIONAL([ENABLE_SSE42],[test x$enable_sse42 = xyes]) -AM_CONDITIONAL([ENABLE_ARM_CRC], [test x$enable_arm_crc = xyes]) -AM_CONDITIONAL([WORDS_BIGENDIAN], [test x$ac_cv_c_bigendian = xyes]) +AM_CONDITIONAL([TARGET_DARWIN], [test "$TARGET_OS" = "darwin"]) +AM_CONDITIONAL([BUILD_DARWIN], [test "$BUILD_OS" = "darwin"]) +AM_CONDITIONAL([TARGET_LINUX], [test "$TARGET_OS" = "linux"]) +AM_CONDITIONAL([TARGET_WINDOWS], [test "$TARGET_OS" = "windows"]) +AM_CONDITIONAL([ENABLE_WALLET], [test "$enable_wallet" = "yes"]) +AM_CONDITIONAL([ENABLE_MINING],[test "$enable_mining" = "yes"]) +AM_CONDITIONAL([ENABLE_TESTS], [test "$BUILD_TEST" = "yes"]) +AM_CONDITIONAL([ENABLE_BENCH], [test "$use_bench" = "yes"]) +AM_CONDITIONAL([USE_LCOV], [test "$use_lcov" = "yes"]) +AM_CONDITIONAL([HARDEN], [test "$use_hardening" = "yes"]) +AM_CONDITIONAL([ENABLE_SSE42], [test "$enable_sse42" = "yes"]) +AM_CONDITIONAL([ENABLE_ARM_CRC], [test "$enable_arm_crc" = "yes"]) +AM_CONDITIONAL([WORDS_BIGENDIAN], [test "$ac_cv_c_bigendian" = "yes"]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) @@ -994,15 +1062,16 @@ AC_SUBST(CLIENT_VERSION_BUILD, _CLIENT_VERSION_BUILD) AC_SUBST(CLIENT_VERSION_IS_RELEASE, _CLIENT_VERSION_IS_RELEASE) AC_SUBST(COPYRIGHT_YEAR, _COPYRIGHT_YEAR) AC_SUBST(BITCOIN_DAEMON_NAME) +AC_SUBST(BITCOIN_TEST_NAME) AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) AC_SUBST(RELDFLAGS) AC_SUBST(DEBUG_CPPFLAGS) +AC_SUBST(WARN_CXXFLAGS) +AC_SUBST(NOWARN_CXXFLAGS) AC_SUBST(DEBUG_CXXFLAGS) AC_SUBST(ERROR_CXXFLAGS) -AC_SUBST(GPROF_CXXFLAGS) -AC_SUBST(GPROF_LDFLAGS) AC_SUBST(HARDENED_CXXFLAGS) AC_SUBST(HARDENED_CPPFLAGS) AC_SUBST(HARDENED_LDFLAGS) @@ -1015,9 +1084,6 @@ AC_SUBST(ARM_CRC_CXXFLAGS) AC_SUBST(LIBTOOL_APP_LDFLAGS) AC_SUBST(BOOST_LIBS) AC_SUBST(TESTDEFS) -AC_SUBST(EVENT_LIBS) -AC_SUBST(EVENT_PTHREADS_LIBS) -AC_SUBST(ZMQ_LIBS) AC_SUBST(LIBZCASH_LIBS) AC_SUBST(HAVE_FDATASYNC) AC_SUBST(HAVE_FULLFSYNC) @@ -1025,9 +1091,13 @@ AC_SUBST(HAVE_O_CLOEXEC) AC_SUBST(HAVE_BUILTIN_PREFETCH) AC_SUBST(HAVE_MM_PREFETCH) AC_SUBST(HAVE_STRONG_GETAUXVAL) -AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile src/test/buildenv.py]) +AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/Info.plist src/test/buildenv.py]) +AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) +AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])]) +AC_CONFIG_LINKS([contrib/filter-lcov.py:contrib/filter-lcov.py]) AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh]) AC_CONFIG_FILES([qa/pull-tester/tests-config.sh],[chmod +x qa/pull-tester/tests-config.sh]) +AC_CONFIG_LINKS([src/test/Makefile:src/test/Makefile]) dnl boost's m4 checks do something really nasty: they export these vars. As a dnl result, they leak into secp256k1's configure and crazy things happen. @@ -1036,33 +1106,28 @@ CPPFLAGS_TEMP="$CPPFLAGS" unset CPPFLAGS CPPFLAGS="$CPPFLAGS_TEMP" -LDFLAGS_TEMP="$LDFLAGS" -unset LDFLAGS -LDFLAGS="$LDFLAGS_TEMP" - -LIBS_TEMP="$LIBS" -unset LIBS -LIBS="$LIBS_TEMP" - -PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" -unset PKG_CONFIG_PATH -PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" - -PKGCONFIG_LIBDIR_TEMP="$PKG_CONFIG_LIBDIR" -unset PKG_CONFIG_LIBDIR -PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" - -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery" +if test -n "$use_sanitizers"; then + export SECP_CFLAGS="$SECP_CFLAGS $SANITIZER_CFLAGS" +fi +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --disable-module-ecdh" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT -dnl Taken from https://wiki.debian.org/RpathIssue +dnl Replace the BUILDDIR path with the correct Windows path if compiling on Native Windows +case ${OS} in + *Windows*) + sed 's/BUILDDIR="\/\([[a-z]]\)/BUILDDIR="\1:/' test/config.ini > test/config-2.ini + mv test/config-2.ini test/config.ini + ;; +esac + +dnl An old hack similar to a98356fee to remove hard-coded +dnl bind_at_load flag from libtool case $host in - *-*-linux-gnu) - AC_MSG_RESULT([Fixing libtool for -rpath problems.]) - sed < libtool > libtool-2 \ - 's/^hardcode_libdir_flag_spec.*$'/'hardcode_libdir_flag_spec=" -D__LIBTOOL_IS_A_FOOL__ "/' + *darwin*) + AC_MSG_RESULT([Removing -Wl,bind_at_load from libtool.]) + sed < libtool > libtool-2 '/bind_at_load/d' mv libtool-2 libtool chmod 755 libtool ;; @@ -1070,22 +1135,22 @@ esac echo echo "Options used to compile and link:" -echo " with wallet = $enable_wallet" -echo " with zmq = $use_zmq" -echo " with test = $use_tests" -echo " sanitizers = $use_sanitizers" -echo " debug enabled = $enable_debug" -echo " gprof enabled = $enable_gprof" -echo " werror = $enable_werror" +echo " with wallet = $enable_wallet" +echo " with zmq = $use_zmq" +echo " with test = $use_tests" +echo " sanitizers = $use_sanitizers" +echo " debug enabled = $enable_debug" +echo " werror = $enable_werror" echo -echo " target os = $TARGET_OS" -echo " build os = $BUILD_OS" +echo " target os = $host_os" +echo " build os = $build_os" echo -echo " CC = $CC" -echo " CFLAGS = $CFLAGS" -echo " CPPFLAGS = $DEBUG_CPPFLAGS $HARDENED_CPPFLAGS $CPPFLAGS" -echo " CXX = $CXX" -echo " CXXFLAGS = $DEBUG_CXXFLAGS $HARDENED_CXXFLAGS $ERROR_CXXFLAGS $GPROF_CXXFLAGS $CXXFLAGS" -echo " LDFLAGS = $PTHREAD_CFLAGS $HARDENED_LDFLAGS $GPROF_LDFLAGS $LDFLAGS" -echo " ARFLAGS = $ARFLAGS" +echo " CC = $CC" +echo " CFLAGS = $PTHREAD_CFLAGS $SANITIZER_CFLAGS $CFLAGS" +echo " CPPFLAGS = $DEBUG_CPPFLAGS $HARDENED_CPPFLAGS $CPPFLAGS" +echo " CXX = $CXX" +echo " CXXFLAGS = $DEBUG_CXXFLAGS $HARDENED_CXXFLAGS $WARN_CXXFLAGS $NOWARN_CXXFLAGS $ERROR_CXXFLAGS $SANITIZER_CXXFLAGS $CXXFLAGS" +echo " LDFLAGS = $PTHREAD_LIBS $HARDENED_LDFLAGS $SANITIZER_LDFLAGS $LDFLAGS" +echo " AR = $AR" +echo " ARFLAGS = $ARFLAGS" echo diff --git a/contrib/devtools/gen-bitcoinz-conf.sh b/contrib/devtools/gen-bitcoinz-conf.sh new file mode 100755 index 000000000..3e27326ed --- /dev/null +++ b/contrib/devtools/gen-bitcoinz-conf.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# Copyright (c) 2021 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C +TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)} +BUILDDIR=${BUILDDIR:-$TOPDIR} +BINDIR=${BINDIR:-$BUILDDIR/src} +BITCOIND=${BITCOIND:-$BINDIR/bitcoinzd} +SHARE_EXAMPLES_DIR=${SHARE_EXAMPLES_DIR:-$TOPDIR/share/examples} +EXAMPLE_CONF_FILE=${EXAMPLE_CONF_FILE:-$SHARE_EXAMPLES_DIR/bitcoinz.conf} + +[ ! -x "$BITCOIND" ] && echo "$BITCOIND not found or not executable." && exit 1 + +DIRTY="" +VERSION_OUTPUT=$($BITCOIND --version) +if [[ $VERSION_OUTPUT == *"dirty"* ]]; then + DIRTY="${DIRTY}${BITCOIND}\n" +fi + +if [ -n "$DIRTY" ] +then + echo -e "WARNING: $BITCOIND was built from a dirty tree.\n" + echo -e "To safely generate a bitcoinz.conf file, please commit your changes to $BITCOIND, rebuild, then run this script again.\n" +fi + +echo 'Generating example bitcoinz.conf file in share/examples/' + +# create the directory, if it doesn't exist +mkdir -p "${SHARE_EXAMPLES_DIR}" + +# create the header text +cat > "${EXAMPLE_CONF_FILE}" << 'EOF' +## +## bitcoinz.conf configuration file. +## Generated by contrib/devtools/gen-bitcoinz-conf.sh. +## +## Lines beginning with # are comments. +## All possible configuration options are provided. To use, copy this file +## to your data directory (default or specified by -datadir), uncomment +## options you would like to change, and save the file. +## + + +### Options +EOF + +# parse the output from bitcoinzd --help +# adding newlines is a bit funky to ensure portability for BSD +# see here for more details: https://stackoverflow.com/a/24575385 +${BITCOIND} --help \ + | sed '1,/Print this help message and exit/d' \ + | sed -E 's/^[[:space:]]{2}\-/#/' \ + | sed -E 's/^[[:space:]]{7}/# /' \ + | sed -E '/[=[:space:]]/!s/#.*$/&=1/' \ + | awk '/^#[a-z]/{x=$0;next}{if (NF==0) print x"\n",x="";else print}' \ + | sed 's,\(^[[:upper:]].*\)\:$,\ +### \1,' \ + | sed 's/[[:space:]]*$//' >> "${EXAMPLE_CONF_FILE}" + +# create the footer text +cat >> "${EXAMPLE_CONF_FILE}" << 'EOF' + +# [Sections] +# Most options will apply to all networks. To confine an option to a specific +# network, add it under the relevant section below. +# +# Note: If not specified under a network section, the options addnode, connect, +# port, bind, rpcport, rpcbind, and wallet will only apply to mainnet. + +# Options for mainnet +[main] + +# Options for testnet +[test] + +# Options for regtest +[regtest] +EOF diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index 43c825bde..46f9ee915 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -1,218 +1,263 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# Copyright (c) 2015-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' -Perform basic ELF security checks on a series of executables. +Perform basic security checks on a series of executables. Exit status will be 0 if successful, and the program will be silent. Otherwise the exit status will be 1 and it will log which executables failed which checks. -Needs `readelf` (for ELF) and `objdump` (for PE). ''' -from __future__ import division,print_function,unicode_literals -import struct -import subprocess import sys -import os - -READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') -OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump') -NONFATAL = {'HIGH_ENTROPY_VA'} # checks which are non-fatal for now but only generate a warning - -def check_ELF_PIE(executable): - ''' - Check for position independent executable (PIE), allowing for address space randomization. - ''' - p = subprocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - - ok = False - for line in stdout.split(b'\n'): - line = line.split() - if len(line)>=2 and line[0] == b'Type:' and line[1] == b'DYN': - ok = True - return ok - -def get_ELF_program_headers(executable): - '''Return type and flags for ELF program headers''' - p = subprocess.Popen([READELF_CMD, '-l', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - in_headers = False - count = 0 - headers = [] - for line in stdout.split(b'\n'): - if line.startswith(b'Program Headers:'): - in_headers = True - if line == b'': - in_headers = False - if in_headers: - if count == 1: # header line - ofs_typ = line.find(b'Type') - ofs_offset = line.find(b'Offset') - ofs_flags = line.find(b'Flg') - ofs_align = line.find(b'Align') - if ofs_typ == -1 or ofs_offset == -1 or ofs_flags == -1 or ofs_align == -1: - raise ValueError('Cannot parse elfread -lW output') - elif count > 1: - typ = line[ofs_typ:ofs_offset].rstrip() - flags = line[ofs_flags:ofs_align].rstrip() - headers.append((typ, flags)) - count += 1 - return headers - -def check_ELF_NX(executable): - ''' - Check that no sections are writable and executable (including the stack) - ''' - have_wx = False - have_gnu_stack = False - for (typ, flags) in get_ELF_program_headers(executable): - if typ == b'GNU_STACK': - have_gnu_stack = True - if b'W' in flags and b'E' in flags: # section is both writable and executable - have_wx = True - return have_gnu_stack and not have_wx - -def check_ELF_RELRO(executable): + +import lief + +def check_ELF_RELRO(binary) -> bool: ''' Check for read-only relocations. GNU_RELRO program header must exist Dynamic section must have BIND_NOW flag ''' have_gnu_relro = False - for (typ, flags) in get_ELF_program_headers(executable): - # Note: not checking flags == 'R': here as linkers set the permission differently - # This does not affect security: the permission flags of the GNU_RELRO program header are ignored, the PT_LOAD header determines the effective permissions. + for segment in binary.segments: + # Note: not checking p_flags == PF_R: here as linkers set the permission differently + # This does not affect security: the permission flags of the GNU_RELRO program + # header are ignored, the PT_LOAD header determines the effective permissions. # However, the dynamic linker need to write to this area so these are RW. # Glibc itself takes care of mprotecting this area R after relocations are finished. - # See also http://permalink.gmane.org/gmane.comp.gnu.binutils/71347 - if typ == b'GNU_RELRO': + # See also https://marc.info/?l=binutils&m=1498883354122353 + if segment.type == lief.ELF.SEGMENT_TYPES.GNU_RELRO: have_gnu_relro = True have_bindnow = False - p = subprocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - for line in stdout.split(b'\n'): - tokens = line.split() - if len(tokens)>1 and tokens[1] == b'(BIND_NOW)' or (len(tokens)>2 and tokens[1] == b'(FLAGS)' and b'BIND_NOW' in tokens[2:]): + try: + flags = binary.get(lief.ELF.DYNAMIC_TAGS.FLAGS) + if flags.value & lief.ELF.DYNAMIC_FLAGS.BIND_NOW: have_bindnow = True + except Exception: + have_bindnow = False + return have_gnu_relro and have_bindnow -def check_ELF_Canary(executable): +def check_ELF_CANARY(binary) -> bool: ''' Check for use of stack canary ''' - p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - ok = False - for line in stdout.split(b'\n'): - if b'__stack_chk_fail' in line: - ok = True - return ok - -def get_PE_dll_characteristics(executable): - ''' - Get PE DllCharacteristics bits. - Returns a tuple (arch,bits) where arch is 'i386:x86-64' or 'i386' - and bits is the DllCharacteristics value. - ''' - p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - arch = '' - bits = 0 - for line in stdout.split('\n'): - tokens = line.split() - if len(tokens)>=2 and tokens[0] == 'architecture:': - arch = tokens[1].rstrip(',') - if len(tokens)>=2 and tokens[0] == 'DllCharacteristics': - bits = int(tokens[1],16) - return (arch,bits) - -IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020 -IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040 -IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100 - -def check_PE_DYNAMIC_BASE(executable): + return binary.has_symbol('__stack_chk_fail') + +def check_ELF_SEPARATE_CODE(binary): + ''' + Check that sections are appropriately separated in virtual memory, + based on their permissions. This checks for missing -Wl,-z,separate-code + and potentially other problems. + ''' + R = lief.ELF.SEGMENT_FLAGS.R + W = lief.ELF.SEGMENT_FLAGS.W + E = lief.ELF.SEGMENT_FLAGS.X + EXPECTED_FLAGS = { + # Read + execute + '.init': R | E, + '.plt': R | E, + '.plt.got': R | E, + '.plt.sec': R | E, + '.text': R | E, + '.fini': R | E, + # Read-only data + '.interp': R, + '.note.gnu.property': R, + '.note.gnu.build-id': R, + '.note.ABI-tag': R, + '.gnu.hash': R, + '.dynsym': R, + '.dynstr': R, + '.gnu.version': R, + '.gnu.version_r': R, + '.rela.dyn': R, + '.rela.plt': R, + '.rodata': R, + '.eh_frame_hdr': R, + '.eh_frame': R, + '.qtmetadata': R, + '.gcc_except_table': R, + '.stapsdt.base': R, + # Writable data + '.init_array': R | W, + '.fini_array': R | W, + '.dynamic': R | W, + '.got': R | W, + '.data': R | W, + '.bss': R | W, + } + if binary.header.machine_type == lief.ELF.ARCH.PPC64: + # .plt is RW on ppc64 even with separate-code + EXPECTED_FLAGS['.plt'] = R | W + # For all LOAD program headers get mapping to the list of sections, + # and for each section, remember the flags of the associated program header. + flags_per_section = {} + for segment in binary.segments: + if segment.type == lief.ELF.SEGMENT_TYPES.LOAD: + for section in segment.sections: + flags_per_section[section.name] = segment.flags + # Spot-check ELF LOAD program header flags per section + # If these sections exist, check them against the expected R/W/E flags + for (section, flags) in flags_per_section.items(): + if section in EXPECTED_FLAGS: + if int(EXPECTED_FLAGS[section]) != int(flags): + return False + return True + +def check_ELF_CONTROL_FLOW(binary) -> bool: + ''' + Check for control flow instrumentation + ''' + main = binary.get_function_address('main') + content = binary.get_content_from_virtual_address(main, 4, lief.Binary.VA_TYPES.AUTO) + + if content.tolist() == [243, 15, 30, 250]: # endbr64 + return True + return False + +def check_PE_DYNAMIC_BASE(binary) -> bool: '''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)''' - (arch,bits) = get_PE_dll_characteristics(executable) - reqbits = IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE - return (bits & reqbits) == reqbits + return lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists -# On 64 bit, must support high-entropy 64-bit address space layout randomization in addition to DYNAMIC_BASE -# to have secure ASLR. -def check_PE_HIGH_ENTROPY_VA(executable): +# Must support high-entropy 64-bit address space layout randomization +# in addition to DYNAMIC_BASE to have secure ASLR. +def check_PE_HIGH_ENTROPY_VA(binary) -> bool: '''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR''' - (arch,bits) = get_PE_dll_characteristics(executable) - if arch == 'i386:x86-64': - reqbits = IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA - else: # Unnecessary on 32-bit - assert(arch == 'i386') - reqbits = 0 - return (bits & reqbits) == reqbits - -def check_PE_NX(executable): - '''NX: DllCharacteristics bit 0x100 signifies nxcompat (DEP)''' - (arch,bits) = get_PE_dll_characteristics(executable) - return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT + return lief.PE.DLL_CHARACTERISTICS.HIGH_ENTROPY_VA in binary.optional_header.dll_characteristics_lists -CHECKS = { -'ELF': [ - ('PIE', check_ELF_PIE), - ('NX', check_ELF_NX), +def check_PE_RELOC_SECTION(binary) -> bool: + '''Check for a reloc section. This is required for functional ASLR.''' + return binary.has_relocations + +def check_PE_CONTROL_FLOW(binary) -> bool: + ''' + Check for control flow instrumentation + ''' + main = binary.get_symbol('main').value + + section_addr = binary.section_from_rva(main).virtual_address + virtual_address = binary.optional_header.imagebase + section_addr + main + + content = binary.get_content_from_virtual_address(virtual_address, 4, lief.Binary.VA_TYPES.VA) + + if content.tolist() == [243, 15, 30, 250]: # endbr64 + return True + return False + +def check_PE_CANARY(binary) -> bool: + ''' + Check for use of stack canary + ''' + return binary.has_symbol('__stack_chk_fail') + +def check_MACHO_NOUNDEFS(binary) -> bool: + ''' + Check for no undefined references. + ''' + return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS) + +def check_MACHO_FIXUP_CHAINS(binary) -> bool: + ''' + Check for use of chained fixups. + ''' + return binary.has_dyld_chained_fixups + +def check_MACHO_CANARY(binary) -> bool: + ''' + Check for use of stack canary + ''' + return binary.has_symbol('___stack_chk_fail') + +def check_PIE(binary) -> bool: + ''' + Check for position independent executable (PIE), + allowing for address space randomization. + ''' + return binary.is_pie + +def check_NX(binary) -> bool: + ''' + Check for no stack execution + ''' + return binary.has_nx + +def check_MACHO_CONTROL_FLOW(binary) -> bool: + ''' + Check for control flow instrumentation + ''' + content = binary.get_content_from_virtual_address(binary.entrypoint, 4, lief.Binary.VA_TYPES.AUTO) + + if content.tolist() == [243, 15, 30, 250]: # endbr64 + return True + return False + +def check_MACHO_BRANCH_PROTECTION(binary) -> bool: + ''' + Check for branch protection instrumentation + ''' + content = binary.get_content_from_virtual_address(binary.entrypoint, 4, lief.Binary.VA_TYPES.AUTO) + + if content.tolist() == [95, 36, 3, 213]: # bti + return True + return False + +BASE_ELF = [ + ('PIE', check_PIE), + ('NX', check_NX), ('RELRO', check_ELF_RELRO), - ('Canary', check_ELF_Canary) -], -'PE': [ + ('CANARY', check_ELF_CANARY), + ('SEPARATE_CODE', check_ELF_SEPARATE_CODE), +] + +BASE_PE = [ + ('PIE', check_PIE), ('DYNAMIC_BASE', check_PE_DYNAMIC_BASE), ('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA), - ('NX', check_PE_NX) -], -'MachO64': [ + ('NX', check_NX), + ('RELOC_SECTION', check_PE_RELOC_SECTION), + ('CONTROL_FLOW', check_PE_CONTROL_FLOW), + ('CANARY', check_PE_CANARY), ] -} -def identify_executable(executable): - with open(filename, 'rb') as f: - magic = f.read(4) - if magic.startswith(b'MZ'): - return 'PE' - elif magic.startswith(b'\x7fELF'): - return 'ELF' - elif struct.unpack('I', magic)[0] == 0xFEEDFACF: - return 'MachO64' - return None +BASE_MACHO = [ + ('NOUNDEFS', check_MACHO_NOUNDEFS), + ('CANARY', check_MACHO_CANARY), + ('FIXUP_CHAINS', check_MACHO_FIXUP_CHAINS), +] + +CHECKS = { + lief.EXE_FORMATS.ELF: { + lief.ARCHITECTURES.X86: BASE_ELF + [('CONTROL_FLOW', check_ELF_CONTROL_FLOW)], + lief.ARCHITECTURES.ARM: BASE_ELF, + lief.ARCHITECTURES.ARM64: BASE_ELF, + lief.ARCHITECTURES.PPC: BASE_ELF, + lief.ARCHITECTURES.RISCV: BASE_ELF, + }, + lief.EXE_FORMATS.PE: { + lief.ARCHITECTURES.X86: BASE_PE, + }, + lief.EXE_FORMATS.MACHO: { + lief.ARCHITECTURES.X86: BASE_MACHO + [('PIE', check_PIE), + ('NX', check_NX), + ('CONTROL_FLOW', check_MACHO_CONTROL_FLOW)], + lief.ARCHITECTURES.ARM64: BASE_MACHO + [('BRANCH_PROTECTION', check_MACHO_BRANCH_PROTECTION)], + } +} if __name__ == '__main__': - retval = 0 + retval: int = 0 for filename in sys.argv[1:]: - try: - etype = identify_executable(filename) - if etype is None: - print('%s: unknown format' % filename) - retval = 1 - continue - - failed = [] - warning = [] - for (name, func) in CHECKS[etype]: - if not func(filename): - if name in NONFATAL: - warning.append(name) - else: - failed.append(name) - if failed: - print('%s: failed %s' % (filename, ' '.join(failed))) - retval = 1 - if warning: - print('%s: warning %s' % (filename, ' '.join(warning))) - except IOError: - print('%s: cannot open' % filename) - retval = 1 - exit(retval) + binary = lief.parse(filename) + etype = binary.format + arch = binary.abstract.header.architecture + binary.concrete + failed: list[str] = [] + for (name, func) in CHECKS[etype][arch]: + if not func(binary): + failed.append(name) + if failed: + print(f'{filename}: failed {" ".join(failed)}') + retval = 1 + sys.exit(retval) diff --git a/contrib/devtools/split-debug.sh b/contrib/devtools/split-debug.sh deleted file mode 100755 index 16fda3005..000000000 --- a/contrib/devtools/split-debug.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -if [ $# -ne 3 ]; - then echo "usage: $0 " -fi - -/usr/bin/objcopy --enable-deterministic-archives -p --only-keep-debug $1 $3 -/usr/bin/objcopy --enable-deterministic-archives -p --strip-debug $1 $2 -/usr/bin/strip --enable-deterministic-archives -p -s $2 -/usr/bin/objcopy --enable-deterministic-archives -p --add-gnu-debuglink=$3 $2 diff --git a/contrib/devtools/split-debug.sh.in b/contrib/devtools/split-debug.sh.in new file mode 100644 index 000000000..92b72b144 --- /dev/null +++ b/contrib/devtools/split-debug.sh.in @@ -0,0 +1,10 @@ +#!/bin/sh +set -e +if [ $# -ne 3 ]; + then echo "usage: $0 " +fi + +@OBJCOPY@ --enable-deterministic-archives -p --only-keep-debug $1 $3 +@OBJCOPY@ --enable-deterministic-archives -p --strip-debug $1 $2 +@STRIP@ --enable-deterministic-archives -p -s $2 +@OBJCOPY@ --enable-deterministic-archives -p --add-gnu-debuglink=$3 $2 diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index d381d0092..1722c7d29 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -1,159 +1,312 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2014 Wladimir J. van der Laan # Distributed under the MIT software license, see the accompanying -# file COPYING or https://www.opensource.org/licenses/mit-license.php . +# file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' -A script to check that the (Linux) executables produced by gitian only contain -allowed gcc, glibc and libstdc++ version symbols. This makes sure they are -still compatible with the minimum supported Linux distribution versions. +A script to check that release executables only contain certain symbols +and are only linked against allowed libraries. Example usage: - find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py + find ../path/to/binaries -type f -executable | xargs python3 contrib/devtools/symbol-check.py ''' -from __future__ import division, print_function -import subprocess -import re import sys -import os -# Debian 6.0.9 (Squeeze) has: +import lief + +# Debian 11 (Bullseye) EOL: 2026. https://wiki.debian.org/LTS # -# - g++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=g%2B%2B) -# - libc version 2.11.3 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libc6) -# - libstdc++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libstdc%2B%2B6) +# - libgcc version 10.2.1 (https://packages.debian.org/bullseye/libgcc-s1) +# - libc version 2.31 (https://packages.debian.org/source/bullseye/glibc) # -# Ubuntu 10.04.4 (Lucid Lynx) has: +# Ubuntu 20.04 (Focal) EOL: 2030. https://wiki.ubuntu.com/ReleaseTeam # -# - g++ version 4.4.3 (http://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=lucid§ion=all) -# - libc version 2.11.1 (http://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=lucid§ion=all) -# - libstdc++ version 4.4.3 (http://packages.ubuntu.com/search?suite=lucid§ion=all&arch=any&keywords=libstdc%2B%2B&searchon=names) +# - libgcc version 10.5.0 (https://packages.ubuntu.com/focal/libgcc1) +# - libc version 2.31 (https://packages.ubuntu.com/focal/libc6) # -# Taking the minimum of these as our target. +# CentOS Stream 9 EOL: 2027. https://www.centos.org/cl-vs-cs/#end-of-life # -# According to GNU ABI document (http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to: -# GCC 4.4.0: GCC_4.4.0 -# GCC 4.4.2: GLIBCXX_3.4.13, CXXABI_1.3.3 -# (glibc) GLIBC_2_11 +# - libgcc version 12.2.1 (https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/) +# - libc version 2.34 (https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/) # +# See https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html for more info. + MAX_VERSIONS = { -'GCC': (4,4,0), -'CXXABI': (1,3,3), -'GLIBCXX': (3,4,13), -'GLIBC': (2,11) +'GCC': (4,3,0), +'GLIBC': { + lief.ELF.ARCH.x86_64: (2,31), + lief.ELF.ARCH.ARM: (2,31), + lief.ELF.ARCH.AARCH64:(2,31), + lief.ELF.ARCH.PPC64: (2,31), + lief.ELF.ARCH.RISCV: (2,31), +}, +'LIBATOMIC': (1,0), +'V': (0,5,0), # xkb (bitcoin-qt only) } -# See here for a description of _IO_stdin_used: -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109 # Ignore symbols that are exported as part of every executable IGNORE_EXPORTS = { -'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used' +'environ', '_environ', '__environ', '_fini', '_init', 'stdin', +'stdout', 'stderr', } -READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') -CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt') + +# Expected linker-loader names can be found here: +# https://sourceware.org/glibc/wiki/ABIList?action=recall&rev=16 +ELF_INTERPRETER_NAMES: dict[lief.ELF.ARCH, dict[lief.ENDIANNESS, str]] = { + lief.ELF.ARCH.x86_64: { + lief.ENDIANNESS.LITTLE: "/lib64/ld-linux-x86-64.so.2", + }, + lief.ELF.ARCH.ARM: { + lief.ENDIANNESS.LITTLE: "/lib/ld-linux-armhf.so.3", + }, + lief.ELF.ARCH.AARCH64: { + lief.ENDIANNESS.LITTLE: "/lib/ld-linux-aarch64.so.1", + }, + lief.ELF.ARCH.PPC64: { + lief.ENDIANNESS.BIG: "/lib64/ld64.so.1", + lief.ENDIANNESS.LITTLE: "/lib64/ld64.so.2", + }, + lief.ELF.ARCH.RISCV: { + lief.ENDIANNESS.LITTLE: "/lib/ld-linux-riscv64-lp64d.so.1", + }, +} + +ELF_ABIS: dict[lief.ELF.ARCH, dict[lief.ENDIANNESS, list[int]]] = { + lief.ELF.ARCH.x86_64: { + lief.ENDIANNESS.LITTLE: [3,2,0], + }, + lief.ELF.ARCH.ARM: { + lief.ENDIANNESS.LITTLE: [3,2,0], + }, + lief.ELF.ARCH.AARCH64: { + lief.ENDIANNESS.LITTLE: [3,7,0], + }, + lief.ELF.ARCH.PPC64: { + lief.ENDIANNESS.LITTLE: [3,10,0], + lief.ENDIANNESS.BIG: [3,2,0], + }, + lief.ELF.ARCH.RISCV: { + lief.ENDIANNESS.LITTLE: [4,15,0], + }, +} + # Allowed NEEDED libraries -ALLOWED_LIBRARIES = { -# bitcoinzd +ELF_ALLOWED_LIBRARIES = { +# bitcoind and bitcoin-qt 'libgcc_s.so.1', # GCC base support 'libc.so.6', # C library -'libstdc++.so.6', # C++ standard library 'libpthread.so.0', # threading -'libanl.so.1', # DNS resolve 'libm.so.6', # math library -'librt.so.1', # real-time (clock) -'libgomp.so.1', # OpenMP support library +'libatomic.so.1', 'ld-linux-x86-64.so.2', # 64-bit dynamic linker 'ld-linux.so.2', # 32-bit dynamic linker -'libdl.so.2' # programming interface to dynamic linker +'ld-linux-aarch64.so.1', # 64-bit ARM dynamic linker +'ld-linux-armhf.so.3', # 32-bit ARM dynamic linker +'ld64.so.1', # POWER64 ABIv1 dynamic linker +'ld64.so.2', # POWER64 ABIv2 dynamic linker +'ld-linux-riscv64-lp64d.so.1', # 64-bit RISC-V dynamic linker +# bitcoin-qt only +'libxcb.so.1', # part of X11 +'libxkbcommon.so.0', # keyboard keymapping +'libxkbcommon-x11.so.0', # keyboard keymapping +'libfontconfig.so.1', # font support +'libfreetype.so.6', # font parsing +'libdl.so.2', # programming interface to dynamic linker +'libxcb-icccm.so.4', +'libxcb-image.so.0', +'libxcb-shm.so.0', +'libxcb-keysyms.so.1', +'libxcb-randr.so.0', +'libxcb-render-util.so.0', +'libxcb-render.so.0', +'libxcb-shape.so.0', +'libxcb-sync.so.1', +'libxcb-xfixes.so.0', +'libxcb-xinerama.so.0', +'libxcb-xkb.so.1', } -class CPPFilt(object): - ''' - Demangle C++ symbol names. - - Use a pipe to the 'c++filt' command. - ''' - def __init__(self): - self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - - def __call__(self, mangled): - self.proc.stdin.write(mangled + '\n') - return self.proc.stdout.readline().rstrip() - - def close(self): - self.proc.stdin.close() - self.proc.stdout.close() - self.proc.wait() - -def read_symbols(executable, imports=True): - ''' - Parse an ELF executable and return a list of (symbol,version) tuples - for dynamic, imported symbols. - ''' - p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip())) - syms = [] - for line in stdout.split('\n'): - line = line.split() - if len(line)>7 and re.match('[0-9]+:$', line[0]): - (sym, _, version) = line[7].partition('@') - is_import = line[6] == 'UND' - if version.startswith('@'): - version = version[1:] - if is_import == imports: - syms.append((sym, version)) - return syms - -def check_version(max_versions, version): - if '_' in version: - (lib, _, ver) = version.rpartition('_') - else: - lib = version - ver = '0' +MACHO_ALLOWED_LIBRARIES = { +# bitcoind and bitcoin-qt +'libc++.1.dylib', # C++ Standard Library +'libSystem.B.dylib', # libc, libm, libpthread, libinfo +# bitcoin-qt only +'AppKit', # user interface +'ApplicationServices', # common application tasks. +'Carbon', # deprecated c back-compat API +'ColorSync', +'CoreFoundation', # low level func, data types +'CoreGraphics', # 2D rendering +'CoreServices', # operating system services +'CoreText', # interface for laying out text and handling fonts. +'CoreVideo', # video processing +'Foundation', # base layer functionality for apps/frameworks +'ImageIO', # read and write image file formats. +'IOKit', # user-space access to hardware devices and drivers. +'IOSurface', # cross process image/drawing buffers +'libobjc.A.dylib', # Objective-C runtime library +'Metal', # 3D graphics +'Security', # access control and authentication +'QuartzCore', # animation +} + +PE_ALLOWED_LIBRARIES = { +'ADVAPI32.dll', # security & registry +'IPHLPAPI.DLL', # IP helper API +'KERNEL32.dll', # win32 base APIs +'msvcrt.dll', # C standard library for MSVC +'SHELL32.dll', # shell API +'WS2_32.dll', # sockets +# bitcoin-qt only +'dwmapi.dll', # desktop window manager +'GDI32.dll', # graphics device interface +'IMM32.dll', # input method editor +'NETAPI32.dll', # network management +'ole32.dll', # component object model +'OLEAUT32.dll', # OLE Automation API +'SHLWAPI.dll', # light weight shell API +'USER32.dll', # user interface +'USERENV.dll', # user management +'UxTheme.dll', # visual style +'VERSION.dll', # version checking +'WINMM.dll', # WinMM audio API +'WTSAPI32.dll', # Remote Desktop +} + +def check_version(max_versions, version, arch) -> bool: + (lib, _, ver) = version.rpartition('_') ver = tuple([int(x) for x in ver.split('.')]) if not lib in max_versions: return False - return ver <= max_versions[lib] - -def read_libraries(filename): - p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (stdout, stderr) = p.communicate() - if p.returncode: - raise IOError('Error opening file') - libraries = [] - for line in stdout.split('\n'): - tokens = line.split() - if len(tokens)>2 and tokens[1] == '(NEEDED)': - match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) - if match: - libraries.append(match.group(1)) - else: - raise ValueError('Unparseable (NEEDED) specification') - return libraries + if isinstance(max_versions[lib], tuple): + return ver <= max_versions[lib] + else: + return ver <= max_versions[lib][arch] -if __name__ == '__main__': - cppfilt = CPPFilt() - retval = 0 - for filename in sys.argv[1:]: - # Check imported symbols - for sym,version in read_symbols(filename, True): - if version and not check_version(MAX_VERSIONS, version): - print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version)) - retval = 1 - # Check exported symbols - for sym,version in read_symbols(filename, False): - if sym in IGNORE_EXPORTS: - continue - print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym))) - retval = 1 - # Check dependency libraries - for library_name in read_libraries(filename): - if library_name not in ALLOWED_LIBRARIES: - print('%s: NEEDED library %s is not allowed' % (filename, library_name)) - retval = 1 +def check_imported_symbols(binary) -> bool: + ok: bool = True + + for symbol in binary.imported_symbols: + if not symbol.imported: + continue + + version = symbol.symbol_version if symbol.has_version else None - exit(retval) + if version: + aux_version = version.symbol_version_auxiliary.name if version.has_auxiliary_version else None + if aux_version and not check_version(MAX_VERSIONS, aux_version, binary.header.machine_type): + print(f'{filename}: symbol {symbol.name} from unsupported version {version}') + ok = False + return ok +def check_exported_symbols(binary) -> bool: + ok: bool = True + for symbol in binary.dynamic_symbols: + if not symbol.exported: + continue + name = symbol.name + if binary.header.machine_type == lief.ELF.ARCH.RISCV or name in IGNORE_EXPORTS: + continue + print(f'{binary.name}: export of symbol {name} not allowed!') + ok = False + return ok + +def check_RUNPATH(binary) -> bool: + assert binary.get(lief.ELF.DYNAMIC_TAGS.RUNPATH) is None + assert binary.get(lief.ELF.DYNAMIC_TAGS.RPATH) is None + return True + +def check_ELF_libraries(binary) -> bool: + ok: bool = True + for library in binary.libraries: + if library not in ELF_ALLOWED_LIBRARIES: + print(f'{filename}: {library} is not in ALLOWED_LIBRARIES!') + ok = False + return ok + +def check_MACHO_libraries(binary) -> bool: + ok: bool = True + for dylib in binary.libraries: + split = dylib.name.split('/') + if split[-1] not in MACHO_ALLOWED_LIBRARIES: + print(f'{split[-1]} is not in ALLOWED_LIBRARIES!') + ok = False + return ok + +def check_MACHO_min_os(binary) -> bool: + if binary.build_version.minos == [11,0,0]: + return True + return False + +def check_MACHO_sdk(binary) -> bool: + if binary.build_version.sdk == [14, 0, 0]: + return True + return False + +def check_MACHO_lld(binary) -> bool: + if binary.build_version.tools[0].version == [18, 1, 8]: + return True + return False + +def check_PE_libraries(binary) -> bool: + ok: bool = True + for dylib in binary.libraries: + if dylib not in PE_ALLOWED_LIBRARIES: + print(f'{dylib} is not in ALLOWED_LIBRARIES!') + ok = False + return ok + +def check_PE_subsystem_version(binary) -> bool: + major: int = binary.optional_header.major_subsystem_version + minor: int = binary.optional_header.minor_subsystem_version + if major == 6 and minor == 1: + return True + return False + +def check_ELF_interpreter(binary) -> bool: + expected_interpreter = ELF_INTERPRETER_NAMES[binary.header.machine_type][binary.abstract.header.endianness] + + return binary.concrete.interpreter == expected_interpreter + +def check_ELF_ABI(binary) -> bool: + expected_abi = ELF_ABIS[binary.header.machine_type][binary.abstract.header.endianness] + note = binary.concrete.get(lief.ELF.NOTE_TYPES.ABI_TAG) + assert note.details.abi == lief.ELF.NOTE_ABIS.LINUX + return note.details.version == expected_abi + +CHECKS = { +lief.EXE_FORMATS.ELF: [ + ('IMPORTED_SYMBOLS', check_imported_symbols), + ('EXPORTED_SYMBOLS', check_exported_symbols), + ('LIBRARY_DEPENDENCIES', check_ELF_libraries), + ('INTERPRETER_NAME', check_ELF_interpreter), + ('ABI', check_ELF_ABI), + ('RUNPATH', check_RUNPATH), +], +lief.EXE_FORMATS.MACHO: [ + ('DYNAMIC_LIBRARIES', check_MACHO_libraries), + ('MIN_OS', check_MACHO_min_os), + ('SDK', check_MACHO_sdk), + ('LLD', check_MACHO_lld), +], +lief.EXE_FORMATS.PE: [ + ('DYNAMIC_LIBRARIES', check_PE_libraries), + ('SUBSYSTEM_VERSION', check_PE_subsystem_version), +] +} + +if __name__ == '__main__': + retval: int = 0 + for filename in sys.argv[1:]: + binary = lief.parse(filename) + etype = binary.format + + failed: list[str] = [] + for (name, func) in CHECKS[etype]: + if not func(binary): + failed.append(name) + if failed: + print(f'{filename}: failed {" ".join(failed)}') + retval = 1 + sys.exit(retval) diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py index 324b7bcd8..4bec6bfe7 100755 --- a/contrib/devtools/test-security-check.py +++ b/contrib/devtools/test-security-check.py @@ -1,60 +1,125 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 +# Copyright (c) 2015-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' Test script for security-check.py ''' -from __future__ import division,print_function +import lief +import os import subprocess -import sys import unittest +from utils import determine_wellknown_cmd + def write_testcode(filename): - with open(filename, 'w') as f: + with open(filename, 'w', encoding="utf8") as f: f.write(''' - #include + #include int main() { - printf("the quick brown fox jumps over the lazy god\\n"); + std::printf("the quick brown fox jumps over the lazy god\\n"); return 0; } ''') -def call_security_check(cc, source, executable, options): - subprocess.check_call([cc,source,'-o',executable] + options) - p = subprocess.Popen(['./security-check.py',executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (stdout, stderr) = p.communicate() - return (p.returncode, stdout.rstrip()) +def clean_files(source, executable): + os.remove(source) + os.remove(executable) + +def env_flags() -> list[str]: + # This should behave the same as AC_TRY_LINK, so arrange well-known flags + # in the same order as autoconf would. + # + # See the definitions for ac_link in autoconf's lib/autoconf/c.m4 file for + # reference. + flags: list[str] = [] + for var in ['CXXFLAGS', 'CPPFLAGS', 'LDFLAGS']: + flags += filter(None, os.environ.get(var, '').split(' ')) + return flags + +def call_security_check(cxx: str, source: str, executable: str, options) -> tuple: + subprocess.run([*cxx,source,'-o',executable] + env_flags() + options, check=True) + p = subprocess.run([os.path.join(os.path.dirname(__file__), 'security-check.py'), executable], stdout=subprocess.PIPE, text=True) + return (p.returncode, p.stdout.rstrip()) + +def get_arch(cxx, source, executable): + subprocess.run([*cxx, source, '-o', executable] + env_flags(), check=True) + binary = lief.parse(executable) + arch = binary.abstract.header.architecture + os.remove(executable) + return arch class TestSecurityChecks(unittest.TestCase): def test_ELF(self): - source = 'test1.c' + source = 'test1.cpp' executable = 'test1' - cc = 'gcc' + cxx = determine_wellknown_cmd('CXX', 'g++') write_testcode(source) + arch = get_arch(cxx, source, executable) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro']), - (1, executable+': failed PIE NX RELRO Canary')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro']), - (1, executable+': failed PIE RELRO Canary')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro']), - (1, executable+': failed PIE RELRO')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE']), - (1, executable+': failed RELRO')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE']), - (0, '')) + if arch == lief.ARCHITECTURES.X86: + pass_flags = ['-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code', '-fcf-protection=full'] + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-zexecstack']), (1, executable + ': failed NX')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-no-pie','-fno-PIE']), (1, executable + ': failed PIE')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-znorelro']), (1, executable + ': failed RELRO')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-z,noseparate-code']), (1, executable + ': failed SEPARATE_CODE')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fcf-protection=none']), (1, executable + ': failed CONTROL_FLOW')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, '')) + else: + pass_flags = ['-Wl,-znoexecstack', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code'] + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-zexecstack']), (1, executable + ': failed NX')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-no-pie','-fno-PIE']), (1, executable + ': failed PIE')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-znorelro']), (1, executable + ': failed RELRO')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-z,noseparate-code']), (1, executable + ': failed SEPARATE_CODE')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, '')) + + clean_files(source, executable) def test_PE(self): - source = 'test1.c' + source = 'test1.cpp' executable = 'test1.exe' - cc = 'i686-w64-mingw32-gcc' + cxx = determine_wellknown_cmd('CXX', 'x86_64-w64-mingw32-g++') write_testcode(source) - self.assertEqual(call_security_check(cc, source, executable, []), - (1, executable+': failed PIE NX')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat']), - (1, executable+': failed PIE')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']), - (0, '')) + pass_flags = ['-Wl,--nxcompat', '-Wl,--enable-reloc-section', '-Wl,--dynamicbase', '-Wl,--high-entropy-va', '-pie', '-fPIE', '-fcf-protection=full', '-fstack-protector-all', '-lssp'] + + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fno-stack-protector']), (1, executable + ': failed CANARY')) + # https://github.com/lief-project/LIEF/issues/1076 - in future, we could test this individually. + # self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,--disable-reloc-section']), (1, executable + ': failed RELOC_SECTION')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,--disable-nxcompat']), (1, executable + ': failed NX')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,--disable-dynamicbase']), (1, executable + ': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA')) # -pie -fPIE does nothing without --dynamicbase + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,--disable-high-entropy-va']), (1, executable + ': failed HIGH_ENTROPY_VA')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fcf-protection=none']), (1, executable + ': failed CONTROL_FLOW')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, '')) + + clean_files(source, executable) + + def test_MACHO(self): + source = 'test1.cpp' + executable = 'test1' + cxx = determine_wellknown_cmd('CXX', 'clang++') + write_testcode(source) + arch = get_arch(cxx, source, executable) + + if arch == lief.ARCHITECTURES.X86: + pass_flags = ['-Wl,-pie', '-fstack-protector-all', '-fcf-protection=full', '-Wl,-fixup_chains'] + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-no_pie', '-Wl,-no_fixup_chains']), (1, executable+': failed FIXUP_CHAINS PIE')) # -fixup_chains is incompatible with -no_pie + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-no_fixup_chains']), (1, executable + ': failed FIXUP_CHAINS')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fno-stack-protector']), (1, executable + ': failed CANARY')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-flat_namespace']), (1, executable + ': failed NOUNDEFS')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fcf-protection=none']), (1, executable + ': failed CONTROL_FLOW')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, '')) + else: + # arm64 darwin doesn't support non-PIE binaries or executable stacks + pass_flags = ['-fstack-protector-all', '-Wl,-fixup_chains', '-mbranch-protection=bti'] + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-mbranch-protection=none']), (1, executable + ': failed BRANCH_PROTECTION')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-no_fixup_chains']), (1, executable + ': failed FIXUP_CHAINS')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-fno-stack-protector']), (1, executable + ': failed CANARY')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags + ['-Wl,-flat_namespace']), (1, executable + ': failed NOUNDEFS')) + self.assertEqual(call_security_check(cxx, source, executable, pass_flags), (0, '')) + + clean_files(source, executable) if __name__ == '__main__': unittest.main() - diff --git a/contrib/devtools/test-symbol-check.py b/contrib/devtools/test-symbol-check.py new file mode 100755 index 000000000..454dbc6bd --- /dev/null +++ b/contrib/devtools/test-symbol-check.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# Copyright (c) 2020-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +''' +Test script for symbol-check.py +''' +import os +import subprocess +import unittest + +from utils import determine_wellknown_cmd + +def call_symbol_check(cxx: list[str], source, executable, options): + # This should behave the same as AC_TRY_LINK, so arrange well-known flags + # in the same order as autoconf would. + # + # See the definitions for ac_link in autoconf's lib/autoconf/c.m4 file for + # reference. + env_flags: list[str] = [] + for var in ['CXXFLAGS', 'CPPFLAGS', 'LDFLAGS']: + env_flags += filter(None, os.environ.get(var, '').split(' ')) + + subprocess.run([*cxx,source,'-o',executable] + env_flags + options, check=True) + p = subprocess.run([os.path.join(os.path.dirname(__file__), 'symbol-check.py'), executable], stdout=subprocess.PIPE, text=True) + os.remove(source) + os.remove(executable) + return (p.returncode, p.stdout.rstrip()) + +class TestSymbolChecks(unittest.TestCase): + def test_ELF(self): + source = 'test1.cpp' + executable = 'test1' + cxx = determine_wellknown_cmd('CXX', 'g++') + + # -lutil is part of the libc6 package so a safe bet that it's installed + # it's also out of context enough that it's unlikely to ever become a real dependency + source = 'test2.cpp' + executable = 'test2' + with open(source, 'w', encoding="utf8") as f: + f.write(''' + #include + + int main() + { + login(0); + return 0; + } + ''') + + self.assertEqual(call_symbol_check(cxx, source, executable, ['-lutil']), + (1, executable + ': libutil.so.1 is not in ALLOWED_LIBRARIES!\n' + + executable + ': failed LIBRARY_DEPENDENCIES')) + + # finally, check a simple conforming binary + source = 'test3.cpp' + executable = 'test3' + with open(source, 'w', encoding="utf8") as f: + f.write(''' + #include + + int main() + { + std::printf("42"); + return 0; + } + ''') + + self.assertEqual(call_symbol_check(cxx, source, executable, []), + (0, '')) + + def test_MACHO(self): + source = 'test1.cpp' + executable = 'test1' + cxx = determine_wellknown_cmd('CXX', 'clang++') + + with open(source, 'w', encoding="utf8") as f: + f.write(''' + #include + + int main() + { + XML_ExpatVersion(); + return 0; + } + + ''') + + self.assertEqual(call_symbol_check(cxx, source, executable, ['-lexpat', '-Wl,-platform_version','-Wl,macos', '-Wl,11.4', '-Wl,11.4']), + (1, 'libexpat.1.dylib is not in ALLOWED_LIBRARIES!\n' + + f'{executable}: failed DYNAMIC_LIBRARIES MIN_OS SDK')) + + source = 'test2.cpp' + executable = 'test2' + with open(source, 'w', encoding="utf8") as f: + f.write(''' + #include + + int main() + { + CGMainDisplayID(); + return 0; + } + ''') + + self.assertEqual(call_symbol_check(cxx, source, executable, ['-framework', 'CoreGraphics', '-Wl,-platform_version','-Wl,macos', '-Wl,11.4', '-Wl,11.4']), + (1, f'{executable}: failed MIN_OS SDK')) + + source = 'test3.cpp' + executable = 'test3' + with open(source, 'w', encoding="utf8") as f: + f.write(''' + int main() + { + return 0; + } + ''') + + self.assertEqual(call_symbol_check(cxx, source, executable, ['-Wl,-platform_version','-Wl,macos', '-Wl,11.0', '-Wl,11.4']), + (1, f'{executable}: failed SDK')) + + def test_PE(self): + source = 'test1.cpp' + executable = 'test1.exe' + cxx = determine_wellknown_cmd('CXX', 'x86_64-w64-mingw32-g++') + + with open(source, 'w', encoding="utf8") as f: + f.write(''' + #include + + int main() + { + PdhConnectMachineA(NULL); + return 0; + } + ''') + + self.assertEqual(call_symbol_check(cxx, source, executable, ['-lpdh', '-Wl,--major-subsystem-version', '-Wl,6', '-Wl,--minor-subsystem-version', '-Wl,1']), + (1, 'pdh.dll is not in ALLOWED_LIBRARIES!\n' + + executable + ': failed DYNAMIC_LIBRARIES')) + + source = 'test2.cpp' + executable = 'test2.exe' + + with open(source, 'w', encoding="utf8") as f: + f.write(''' + int main() + { + return 0; + } + ''') + + self.assertEqual(call_symbol_check(cxx, source, executable, ['-Wl,--major-subsystem-version', '-Wl,9', '-Wl,--minor-subsystem-version', '-Wl,9']), + (1, executable + ': failed SUBSYSTEM_VERSION')) + + source = 'test3.cpp' + executable = 'test3.exe' + with open(source, 'w', encoding="utf8") as f: + f.write(''' + #include + + int main() + { + CoFreeUnusedLibrariesEx(0,0); + return 0; + } + ''') + + self.assertEqual(call_symbol_check(cxx, source, executable, ['-lole32', '-Wl,--major-subsystem-version', '-Wl,6', '-Wl,--minor-subsystem-version', '-Wl,1']), + (0, '')) + + +if __name__ == '__main__': + unittest.main() diff --git a/contrib/devtools/utils.py b/contrib/devtools/utils.py new file mode 100755 index 000000000..8b4c67c6c --- /dev/null +++ b/contrib/devtools/utils.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +''' +Common utility functions +''' +import shutil +import sys +import os + + +def determine_wellknown_cmd(envvar, progname) -> list[str]: + maybe_env = os.getenv(envvar) + maybe_which = shutil.which(progname) + if maybe_env: + return maybe_env.split(' ') # Well-known vars are often meant to be word-split + elif maybe_which: + return [ maybe_which ] + else: + sys.exit(f"{progname} not found") diff --git a/contrib/filter-lcov.py b/contrib/filter-lcov.py new file mode 100755 index 000000000..db780ad53 --- /dev/null +++ b/contrib/filter-lcov.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-2020 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import argparse + +parser = argparse.ArgumentParser(description='Remove the coverage data from a tracefile for all files matching the pattern.') +parser.add_argument('--pattern', '-p', action='append', help='the pattern of files to remove', required=True) +parser.add_argument('tracefile', help='the tracefile to remove the coverage data from') +parser.add_argument('outfile', help='filename for the output to be written to') + +args = parser.parse_args() +tracefile = args.tracefile +pattern = args.pattern +outfile = args.outfile + +in_remove = False +with open(tracefile, 'r', encoding="utf8") as f: + with open(outfile, 'w', encoding="utf8") as wf: + for line in f: + for p in pattern: + if line.startswith("SF:") and p in line: + in_remove = True + if not in_remove: + wf.write(line) + if line == 'end_of_record\n': + in_remove = False diff --git a/contrib/gitian-descriptors/README.md b/contrib/gitian-descriptors/README.md deleted file mode 100644 index 614970659..000000000 --- a/contrib/gitian-descriptors/README.md +++ /dev/null @@ -1,65 +0,0 @@ -### Gavin's notes on getting gitian builds up and running using KVM - -These instructions distilled from -[https://help.ubuntu.com/community/KVM/Installation](https://help.ubuntu.com/community/KVM/Installation). - -You need the right hardware: you need a 64-bit-capable CPU with hardware virtualization support (Intel VT-x or AMD-V). Not all modern CPUs support hardware virtualization. - -You probably need to enable hardware virtualization in your machine's BIOS. - -You need to be running a recent version of 64-bit-Ubuntu, and you need to install several prerequisites: - - sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm - -Sanity checks: - - sudo service apt-cacher-ng status # Should return apt-cacher-ng is running - ls -l /dev/kvm # Should show a /dev/kvm device - - -Once you've got the right hardware and software: - - git clone git://github.com/bitcoin/bitcoin.git - git clone git://github.com/devrandom/gitian-builder.git - mkdir gitian-builder/inputs - cd gitian-builder/inputs - - # Create base images - cd gitian-builder - bin/make-base-vm --suite trusty --arch amd64 - cd .. - - # Get inputs (see doc/release-process.md for exact inputs needed and where to get them) - ... - - # For further build instructions see doc/release-process.md - ... - ---------------------- - -`gitian-builder` now also supports building using LXC. See -[help.ubuntu.com](https://help.ubuntu.com/14.04/serverguide/lxc.html) -for how to get LXC up and running under Ubuntu. - -If your main machine is a 64-bit Mac or PC with a few gigabytes of memory -and at least 10 gigabytes of free disk space, you can `gitian-build` using -LXC running inside a virtual machine. - -Here's a description of Gavin's setup on OSX 10.6: - -1. Download and install VirtualBox from [https://www.virtualbox.org/](https://www.virtualbox.org/) - -2. Download the 64-bit Ubuntu Desktop 12.04 LTS .iso CD image from - [http://www.ubuntu.com/](http://www.ubuntu.com/) - -3. Run VirtualBox and create a new virtual machine, using the Ubuntu .iso (see the [VirtualBox documentation](https://www.virtualbox.org/wiki/Documentation) for details). Create it with at least 2 gigabytes of memory and a disk that is at least 20 gigabytes big. - -4. Inside the running Ubuntu desktop, install: - - sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder - -5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right hardware and software" instructions above: - - export USE_LXC=1 - git clone git://github.com/bitcoin/bitcoin.git - ... etc diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml deleted file mode 100644 index 79b346507..000000000 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ /dev/null @@ -1,139 +0,0 @@ ---- -name: "bitcoinz-2.0.10" -enable_cache: true -distro: "debian" -suites: -- "jessie" -- "stretch" -architectures: -- "amd64" -packages: -- "curl" -- "autoconf" -- "automake" -- "bsdmainutils" -- "binutils-gold" -- "ca-certificates" -- "faketime" -- "g++-multilib" -- "git-core" -- "libc6-dev" -- "libtool" -- "m4" -- "ncurses-dev" -- "pkg-config" -- "python" -- "unzip" -- "wget" -- "zlib1g-dev" -remotes: -- "url": "https://github.com/zcash/zcash.git" - "dir": "zcash" -files: [] -script: | - WRAP_DIR=$HOME/wrapped - HOSTS="x86_64-unknown-linux-gnu" - CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --enable-hardening" - MAKEOPTS="V=1" - FAKETIME_HOST_PROGS="" - FAKETIME_PROGS="date ar ranlib nm strip objcopy" - HOST_CFLAGS="-fwrapv -fno-strict-aliasing -Werror -g" - HOST_CXXFLAGS="-fwrapv -fno-strict-aliasing -Werror -g" - HOST_LDFLAGS=-static-libstdc++ - - export QT_RCC_TEST=0 - export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" - export TZ="UTC" - export BUILD_DIR=`pwd` - mkdir -p ${WRAP_DIR} - if test -n "$GBUILD_CACHE_ENABLED"; then - export SOURCES_PATH=${GBUILD_COMMON_CACHE} - export BASE_CACHE=${GBUILD_PACKAGE_CACHE} - mkdir -p ${BASE_CACHE} ${SOURCES_PATH} - fi - - function create_global_faketime_wrappers { - for prog in ${FAKETIME_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${prog} - echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} - echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${prog} - chmod +x ${WRAP_DIR}/${prog} - done - } - - function create_per-host_faketime_wrappers { - for i in $HOSTS; do - for prog in ${FAKETIME_HOST_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} - echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} - echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} - chmod +x ${WRAP_DIR}/${i}-${prog} - done - done - } - - export PATH=${WRAP_DIR}:${PATH} - - # Faketime for depends so intermediate results are comparable - create_global_faketime_wrappers "2000-01-01 12:00:00" - create_per-host_faketime_wrappers "2000-01-01 12:00:00" - - cd zcash - BASEPREFIX=`pwd`/depends - # Build dependencies for each host - for i in $HOSTS; do - make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" - done - - # Faketime for binaries - create_global_faketime_wrappers "${REFERENCE_DATETIME}" - create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" - - # Create the release tarball using (arbitrarily) the first host - ./autogen.sh - CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ - make dist - SOURCEDIST=`echo zcash-*.tar.gz` - DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` - # Correct tar file order - mkdir -p temp - pushd temp - tar xf ../$SOURCEDIST - find zcash* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST - popd - - ORIGPATH="$PATH" - # Extract the release tarball into a dir for each host and build - for i in ${HOSTS}; do - export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} - mkdir -p distsrc-${i} - cd distsrc-${i} - INSTALLPATH=`pwd`/installed/${DISTNAME} - mkdir -p ${INSTALLPATH} - tar --strip-components=1 -xf ../$SOURCEDIST - - CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" LDFLAGS="${HOST_LDFLAGS}" - make ${MAKEOPTS} - make ${MAKEOPTS} -C src check-security - make install DESTDIR=${INSTALLPATH} - cd installed - find . -name "lib*.la" -delete - find . -name "lib*.a" -delete - rm -rf ${DISTNAME}/lib/pkgconfig - find ${DISTNAME}/bin -type f -executable -exec objcopy --only-keep-debug {} {}.dbg \; -exec strip -s {} \; -exec objcopy --add-gnu-debuglink={}.dbg {} \; - # Commented out while we don't build any libraries - #find ${DISTNAME}/lib -type f -exec objcopy --only-keep-debug {} {}.dbg \; -exec strip -s {} \; -exec objcopy --add-gnu-debuglink={}.dbg {} \; - find ${DISTNAME} -not -name "*.dbg" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz - find ${DISTNAME} -name "*.dbg" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz - cd ../../ - rm -rf distsrc-${i} - done - mkdir -p $OUTDIR/src - mv $SOURCEDIST $OUTDIR/src - mv ${OUTDIR}/${DISTNAME}-x86_64-*-debug.tar.gz ${OUTDIR}/${DISTNAME}-linux64-debug.tar.gz - mv ${OUTDIR}/${DISTNAME}-x86_64-*.tar.gz ${OUTDIR}/${DISTNAME}-linux64.tar.gz diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml deleted file mode 100644 index b00eb2fb9..000000000 --- a/contrib/gitian-descriptors/gitian-osx-signer.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: "bitcoin-dmg-signer" -suites: -- "trusty" -architectures: -- "amd64" -packages: -- "libc6:i386" -- "faketime" -remotes: -- "url": "https://github.com/bitcoin/bitcoin-detached-sigs.git" - "dir": "signature" -files: -- "bitcoin-osx-unsigned.tar.gz" -script: | - WRAP_DIR=$HOME/wrapped - mkdir -p ${WRAP_DIR} - export PATH=`pwd`:$PATH - FAKETIME_PROGS="dmg genisoimage" - - # Create global faketime wrappers - for prog in ${FAKETIME_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${prog} - echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} - echo "export FAKETIME=\"${REFERENCE_DATETIME}\"" >> ${WRAP_DIR}/${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${prog} - chmod +x ${WRAP_DIR}/${prog} - done - - UNSIGNED=bitcoin-osx-unsigned.tar.gz - SIGNED=bitcoin-osx-signed.dmg - - tar -xf ${UNSIGNED} - ./detached-sig-apply.sh ${UNSIGNED} signature/osx - ${WRAP_DIR}/genisoimage -no-cache-inodes -D -l -probe -V "Bitcoin-Core" -no-pad -r -apple -o uncompressed.dmg signed-app - ${WRAP_DIR}/dmg dmg uncompressed.dmg ${OUTDIR}/${SIGNED} diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml deleted file mode 100644 index 151a962b5..000000000 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ /dev/null @@ -1,146 +0,0 @@ ---- -name: "bitcoinz-osx-2.0.10" -enable_cache: true -suites: -- "trusty" -architectures: -- "amd64" -packages: -- "g++" -- "git-core" -- "pkg-config" -- "autoconf" -- "libtool" -- "automake" -- "faketime" -- "bsdmainutils" -- "cmake" -- "libcap-dev" -- "libz-dev" -- "libbz2-dev" -- "ca-certificates" -- "python" -remotes: -- "url": "https://github.com/btcz/bitcoinz.git" - "dir": "bitcoinz" -files: -- "Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz" -script: | - WRAP_DIR=$HOME/wrapped - HOSTS="x86_64-apple-darwin18" - CONFIGFLAGS="--enable-reduce-exports --disable-bench GENISOIMAGE=$WRAP_DIR/genisoimage" - FAKETIME_HOST_PROGS="" - FAKETIME_PROGS="ar ranlib date dmg genisoimage" - - export QT_RCC_TEST=1 - export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" - export TZ="UTC" - export BUILD_DIR=`pwd` - mkdir -p ${WRAP_DIR} - if test -n "$GBUILD_CACHE_ENABLED"; then - export SOURCES_PATH=${GBUILD_COMMON_CACHE} - export BASE_CACHE=${GBUILD_PACKAGE_CACHE} - mkdir -p ${BASE_CACHE} ${SOURCES_PATH} - fi - - export ZERO_AR_DATE=1 - - function create_global_faketime_wrappers { - for prog in ${FAKETIME_PROGS}; do - echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${prog} - echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} - echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${prog} - chmod +x ${WRAP_DIR}/${prog} - done - } - - function create_per-host_faketime_wrappers { - for i in $HOSTS; do - for prog in ${FAKETIME_HOST_PROGS}; do - echo '#!/usr/bin/env bash' > ${WRAP_DIR}/${i}-${prog} - echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} - echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} - chmod +x ${WRAP_DIR}/${i}-${prog} - done - done - } - - export PATH=${WRAP_DIR}:${PATH} - - # Faketime for depends so intermediate results are comparable - create_global_faketime_wrappers "2000-01-01 12:00:00" - create_per-host_faketime_wrappers "2000-01-01 12:00:00" - - cd bitcoinz - BASEPREFIX=`pwd`/depends - - mkdir -p ${BASEPREFIX}/SDKs - tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/Xcode-11.3.1-11C505-extracted-SDK-with-libcxx-headers.tar.gz - - # Build dependencies for each host - for i in $HOSTS; do - make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" - done - - # Faketime for binaries - create_global_faketime_wrappers "${REFERENCE_DATETIME}" - create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" - - # Create the release tarball using (arbitrarily) the first host - ./autogen.sh - CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ - make dist - SOURCEDIST=`echo bitcoinz-*.tar.gz` - DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` - - # Correct tar file order - mkdir -p temp - pushd temp - tar xf ../$SOURCEDIST - find bitcoinz-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST - popd - - ORIGPATH="$PATH" - # Extract the release tarball into a dir for each host and build - for i in ${HOSTS}; do - export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} - mkdir -p distsrc-${i} - cd distsrc-${i} - INSTALLPATH=`pwd`/installed/${DISTNAME} - mkdir -p ${INSTALLPATH} - tar --strip-components=1 -xf ../$SOURCEDIST - - CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} - make ${MAKEOPTS} - make install-strip DESTDIR=${INSTALLPATH} - - make deploydir - mkdir -p unsigned-app-${i} - cp contrib/macdeploy/detached-sig-apply.sh unsigned-app-${i} - cp contrib/macdeploy/detached-sig-create.sh unsigned-app-${i} - cp ${BASEPREFIX}/${i}/native/bin/dmg ${BASEPREFIX}/${i}/native/bin/genisoimage unsigned-app-${i} - cp ${BASEPREFIX}/${i}/native/bin/${i}-codesign_allocate unsigned-app-${i}/codesign_allocate - cp ${BASEPREFIX}/${i}/native/bin/${i}-pagestuff unsigned-app-${i}/pagestuff - mv dist unsigned-app-${i} - pushd unsigned-app-${i} - find . | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-osx-unsigned.tar.gz - popd - - make deploy - ${WRAP_DIR}/dmg dmg Bitcoin-Core.dmg ${OUTDIR}/${DISTNAME}-osx-unsigned.dmg - - cd installed - find . -name "lib*.la" -delete - find . -name "lib*.a" -delete - rm -rf ${DISTNAME}/lib/pkgconfig - find ${DISTNAME} | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz - cd ../../ - done - mkdir -p $OUTDIR/src - mv $SOURCEDIST $OUTDIR/src - mv ${OUTDIR}/${DISTNAME}-x86_64-*.tar.gz ${OUTDIR}/${DISTNAME}-osx64.tar.gz diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml deleted file mode 100644 index 6b53b89b6..000000000 --- a/contrib/gitian-descriptors/gitian-win-signer.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: "bitcoin-win-signer" -suites: -- "trusty" -architectures: -- "amd64" -packages: -- "libssl-dev" -- "autoconf" -remotes: -- "url": "https://github.com/bitcoin/bitcoin-detached-sigs.git" - "dir": "signature" -files: -- "osslsigncode-1.7.1.tar.gz" -- "osslsigncode-Backports-to-1.7.1.patch" -- "bitcoin-win-unsigned.tar.gz" -script: | - BUILD_DIR=`pwd` - SIGDIR=${BUILD_DIR}/signature/win - UNSIGNED_DIR=${BUILD_DIR}/unsigned - - echo "f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 osslsigncode-1.7.1.tar.gz" | sha256sum -c - echo "a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 osslsigncode-Backports-to-1.7.1.patch" | sha256sum -c - - mkdir -p ${UNSIGNED_DIR} - tar -C ${UNSIGNED_DIR} -xf bitcoin-win-unsigned.tar.gz - - tar xf osslsigncode-1.7.1.tar.gz - cd osslsigncode-1.7.1 - patch -p1 < ${BUILD_DIR}/osslsigncode-Backports-to-1.7.1.patch - - ./configure --without-gsf --without-curl --disable-dependency-tracking - make - find ${UNSIGNED_DIR} -name "*-unsigned.exe" | while read i; do - INFILE="`basename "${i}"`" - OUTFILE="`echo "${INFILE}" | sed s/-unsigned//`" - ./osslsigncode attach-signature -in "${i}" -out "${OUTDIR}/${OUTFILE}" -sigin "${SIGDIR}/${INFILE}.pem" - done diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml deleted file mode 100644 index 8df3e3898..000000000 --- a/contrib/gitian-descriptors/gitian-win.yml +++ /dev/null @@ -1,166 +0,0 @@ ---- -name: "bitcoinz-win-2.0.10" -enable_cache: true -suites: -- "trusty" -architectures: -- "amd64" -packages: -- "g++" -- "git-core" -- "pkg-config" -- "autoconf" -- "libtool" -- "automake" -- "faketime" -- "bsdmainutils" -- "mingw-w64" -- "g++-mingw-w64" -- "nsis" -- "zip" -- "ca-certificates" -- "python" -remotes: -- "url": "https://github.com/btcz/bitcoinz.git" - "dir": "bitcoinz" -files: [] -script: | - WRAP_DIR=$HOME/wrapped - HOSTS="x86_64-w64-mingw32 i686-w64-mingw32" - CONFIGFLAGS="--enable-reduce-exports --disable-bench" - FAKETIME_HOST_PROGS="g++ ar ranlib nm windres strip objcopy" - FAKETIME_PROGS="date makensis zip" - HOST_CFLAGS="-O2 -g" - HOST_CXXFLAGS="-O2 -g" - - export QT_RCC_TEST=1 - export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" - export TZ="UTC" - export BUILD_DIR=`pwd` - mkdir -p ${WRAP_DIR} - if test -n "$GBUILD_CACHE_ENABLED"; then - export SOURCES_PATH=${GBUILD_COMMON_CACHE} - export BASE_CACHE=${GBUILD_PACKAGE_CACHE} - mkdir -p ${BASE_CACHE} ${SOURCES_PATH} - fi - - function create_global_faketime_wrappers { - for prog in ${FAKETIME_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${prog} - echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} - echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${prog} - chmod +x ${WRAP_DIR}/${prog} - done - } - - function create_per-host_faketime_wrappers { - for i in $HOSTS; do - for prog in ${FAKETIME_HOST_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} - echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} - echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} - chmod +x ${WRAP_DIR}/${i}-${prog} - done - done - } - - function create_per-host_linker_wrapper { - # This is only needed for trusty, as the mingw linker leaks a few bytes of - # heap, causing non-determinism. See discussion in https://github.com/bitcoin/bitcoin/pull/6900 - for i in $HOSTS; do - mkdir -p ${WRAP_DIR}/${i} - for prog in collect2; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}/${prog} - REAL=$(${i}-gcc -print-prog-name=${prog}) - echo "export MALLOC_PERTURB_=255" >> ${WRAP_DIR}/${i}/${prog} - echo "${REAL} \$@" >> $WRAP_DIR/${i}/${prog} - chmod +x ${WRAP_DIR}/${i}/${prog} - done - for prog in gcc g++; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} - echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} - echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} - echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} - echo "export COMPILER_PATH=${WRAP_DIR}/${i}" >> ${WRAP_DIR}/${i}-${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} - chmod +x ${WRAP_DIR}/${i}-${prog} - done - done - } - - export PATH=${WRAP_DIR}:${PATH} - - # Faketime for depends so intermediate results are comparable - create_global_faketime_wrappers "2000-01-01 12:00:00" - create_per-host_faketime_wrappers "2000-01-01 12:00:00" - create_per-host_linker_wrapper "2000-01-01 12:00:00" - - cd bitcoinz - BASEPREFIX=`pwd`/depends - # Build dependencies for each host - for i in $HOSTS; do - make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" - done - - # Faketime for binaries - create_global_faketime_wrappers "${REFERENCE_DATETIME}" - create_per-host_faketime_wrappers "${REFERENCE_DATETIME}" - create_per-host_linker_wrapper "${REFERENCE_DATETIME}" - - # Create the release tarball using (arbitrarily) the first host - ./autogen.sh - CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ - make dist - SOURCEDIST=`echo bitcoinz-*.tar.gz` - DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` - - # Correct tar file order - mkdir -p temp - pushd temp - tar xf ../$SOURCEDIST - find bitcoinz-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST - mkdir -p $OUTDIR/src - cp ../$SOURCEDIST $OUTDIR/src - popd - - ORIGPATH="$PATH" - # Extract the release tarball into a dir for each host and build - for i in ${HOSTS}; do - export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} - mkdir -p distsrc-${i} - cd distsrc-${i} - INSTALLPATH=`pwd`/installed/${DISTNAME} - mkdir -p ${INSTALLPATH} - tar --strip-components=1 -xf ../$SOURCEDIST - - CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" - make ${MAKEOPTS} - make ${MAKEOPTS} -C src check-security - make deploy - make install DESTDIR=${INSTALLPATH} - cp -f bitcoinz-*setup*.exe $OUTDIR/ - cd installed - mv ${DISTNAME}/bin/*.dll ${DISTNAME}/lib/ - find . -name "lib*.la" -delete - find . -name "lib*.a" -delete - rm -rf ${DISTNAME}/lib/pkgconfig - find ${DISTNAME}/bin -type f -executable -exec ${i}-objcopy --only-keep-debug {} {}.dbg \; -exec ${i}-strip -s {} \; -exec ${i}-objcopy --add-gnu-debuglink={}.dbg {} \; - # Commented out while we don't build any libraries - #find ${DISTNAME}/lib -type f -exec ${i}-objcopy --only-keep-debug {} {}.dbg \; -exec ${i}-strip -s {} \; -exec ${i}-objcopy --add-gnu-debuglink={}.dbg {} \; - find ${DISTNAME} -not -name "*.dbg" -type f | sort | zip -X@ ${OUTDIR}/${DISTNAME}-${i}.zip - find ${DISTNAME} -name "*.dbg" -type f | sort | zip -X@ ${OUTDIR}/${DISTNAME}-${i}-debug.zip - cd ../../ - rm -rf distsrc-${i} - done - cd $OUTDIR - rename 's/-setup\.exe$/-setup-unsigned.exe/' *-setup.exe - find . -name "*-setup-unsigned.exe" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-win-unsigned.tar.gz - mv ${OUTDIR}/${DISTNAME}-x86_64-*-debug.zip ${OUTDIR}/${DISTNAME}-win64-debug.zip - mv ${OUTDIR}/${DISTNAME}-i686-*-debug.zip ${OUTDIR}/${DISTNAME}-win32-debug.zip - mv ${OUTDIR}/${DISTNAME}-x86_64-*.zip ${OUTDIR}/${DISTNAME}-win64.zip - mv ${OUTDIR}/${DISTNAME}-i686-*.zip ${OUTDIR}/${DISTNAME}-win32.zip diff --git a/contrib/gitian-downloader/daira.asc b/contrib/gitian-downloader/daira.asc deleted file mode 100644 index e60a2bbcb..000000000 --- a/contrib/gitian-downloader/daira.asc +++ /dev/null @@ -1,70 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: SKS 1.1.5 -Comment: Hostname: pgp.mit.edu - -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 -eCJPlVdlz6Oj1tV2kw== -=E8LJ ------END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/jack.asc b/contrib/gitian-downloader/jack.asc deleted file mode 100644 index db9967233..000000000 --- a/contrib/gitian-downloader/jack.asc +++ /dev/null @@ -1,449 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -HifqTn/z+qdV2A== -=phy7 ------END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/kevin.asc b/contrib/gitian-downloader/kevin.asc deleted file mode 100644 index 13a39ee7f..000000000 --- a/contrib/gitian-downloader/kevin.asc +++ /dev/null @@ -1,171 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -=qSqw ------END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/nathan.asc b/contrib/gitian-downloader/nathan.asc deleted file mode 100644 index 2a2be1ee9..000000000 --- a/contrib/gitian-downloader/nathan.asc +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1 - -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 -JfmuGLm2Czs= -=kUB4 ------END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/sean.asc b/contrib/gitian-downloader/sean.asc deleted file mode 100644 index 9b710ff5f..000000000 --- a/contrib/gitian-downloader/sean.asc +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v2 - -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 -=0DUx ------END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/gitian-downloader/simon.asc b/contrib/gitian-downloader/simon.asc deleted file mode 100644 index 3cbf0f57c..000000000 --- a/contrib/gitian-downloader/simon.asc +++ /dev/null @@ -1,47 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: SKS 1.1.5 -Comment: Hostname: pgp.mit.edu - -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 -=nZf9 ------END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/guix/INSTALL.md b/contrib/guix/INSTALL.md new file mode 100644 index 000000000..dbaf701b8 --- /dev/null +++ b/contrib/guix/INSTALL.md @@ -0,0 +1,814 @@ +# Guix Installation and Setup + +This only needs to be done once per machine. If you have already completed the +installation and setup, please proceed to [perform a build](./README.md). + +Otherwise, you may choose from one of the following options to install Guix: + +1. Using the official **shell installer script** [⤓ skip to section][install-script] + - Maintained by Guix developers + - Easiest (automatically performs *most* setup) + - Works on nearly all Linux distributions + - Only installs latest release + - Binary installation only, requires high level of trust + - Note: The script needs to be run as root, so it should be inspected before it's run +2. Using the official **binary tarball** [⤓ skip to section][install-bin-tarball] + - Maintained by Guix developers + - Normal difficulty (full manual setup required) + - Works on nearly all Linux distributions + - Installs any release + - Binary installation only, requires high level of trust +3. Using fanquake's **Docker image** [↗︎ external instructions][install-fanquake-docker] + - Maintained by fanquake + - Easy (automatically performs *some* setup) + - Works wherever Docker images work + - Installs any release + - Binary installation only, requires high level of trust +4. Using a **distribution-maintained package** [⤓ skip to section][install-distro-pkg] + - Maintained by distribution's Guix package maintainer + - Normal difficulty (manual setup required) + - Works only on distributions with Guix packaged, see: https://repology.org/project/guix/versions + - Installs a release decided on by package maintainer + - Source or binary installation depending on the distribution +5. Building **from source** [⤓ skip to section][install-source] + - Maintained by you + - Hard, but rewarding + - Can be made to work on most Linux distributions + - Installs any commit (more granular) + - Source installation, requires lower level of trust + +## Options 1 and 2: Using the official shell installer script or binary tarball + +The installation instructions for both the official shell installer script and +the binary tarballs can be found in the GNU Guix Manual's [Binary Installation +section](https://guix.gnu.org/manual/en/html_node/Binary-Installation.html). + +Note that running through the binary tarball installation steps is largely +equivalent to manually performing what the shell installer script does. + +Note that at the time of writing (July 5th, 2021), the shell installer script +automatically creates an `/etc/profile.d` entry which the binary tarball +installation instructions do not ask you to create. However, you will likely +need this entry for better desktop integration. Please see [this +section](#add-an-etcprofiled-entry) for instructions on how to add a +`/etc/profile.d/guix.sh` entry. + +Regardless of which installation option you chose, the changes to +`/etc/profile.d` will not take effect until the next shell or desktop session, +so you should log out and log back in. + +## Option 3: Using fanquake's Docker image + +Please refer to fanquake's instructions +[here](https://github.com/fanquake/core-review/tree/master/guix). + +## Option 4: Using a distribution-maintained package + +Note that this section is based on the distro packaging situation at the time of +writing (July 2021). Guix is expected to be more widely packaged over time. For +an up-to-date view on Guix's package status/version across distros, please see: +https://repology.org/project/guix/versions + +### Debian / Ubuntu + +Guix is available as a distribution package in [Debian +](https://packages.debian.org/search?keywords=guix) and [Ubuntu +](https://packages.ubuntu.com/search?keywords=guix). + +To install: +```sh +sudo apt install guix +``` + +### Arch Linux + +Guix is available in the AUR as +[`guix`](https://aur.archlinux.org/packages/guix/), please follow the +installation instructions in the Arch Linux Wiki ([live +link](https://wiki.archlinux.org/index.php/Guix#AUR_Package_Installation), +[2021/03/30 +permalink](https://wiki.archlinux.org/index.php?title=Guix&oldid=637559#AUR_Package_Installation)) +to install Guix. + +At the time of writing (2021/03/30), the `check` phase will fail if the path to +guix's build directory is longer than 36 characters due to an anachronistic +character limit on the shebang line. Since the `check` phase happens after the +`build` phase, which may take quite a long time, it is recommended that users +either: + +1. Skip the `check` phase + - For `makepkg`: `makepkg --nocheck ...` + - For `yay`: `yay --mflags="--nocheck" ...` + - For `paru`: `paru --nocheck ...` +2. Or, check their build directory's length beforehand + - For those building with `makepkg`: `pwd | wc -c` + +## Option 5: Building from source + +Building Guix from source is a rather involved process but a rewarding one for +those looking to minimize trust and maximize customizability (e.g. building a +particular commit of Guix). Previous experience with using autotools-style build +systems to build packages from source will be helpful. *hic sunt dracones.* + +I strongly urge you to at least skim through the entire section once before you +start issuing commands, as it will save you a lot of unnecessary pain and +anguish. + +### Installing common build tools + +There are a few basic build tools that are required for most things we'll build, +so let's install them now: + +Text transformation/i18n: +- `autopoint` (sometimes packaged in `gettext`) +- `help2man` +- `po4a` +- `texinfo` + +Build system tools: +- `g++` w/ C++11 support +- `libtool` +- `autoconf` +- `automake` +- `pkg-config` (sometimes packaged as `pkgconf`) +- `make` +- `cmake` + +Miscellaneous: +- `git` +- `gnupg` +- `python3` + +### Building and Installing Guix's dependencies + +In order to build Guix itself from source, we need to first make sure that the +necessary dependencies are installed and discoverable. The most up-to-date list +of Guix's dependencies is kept in the ["Requirements" +section](https://guix.gnu.org/manual/en/html_node/Requirements.html) of the Guix +Reference Manual. + +Depending on your distribution, most or all of these dependencies may already be +packaged and installable without manually building and installing. + +For reference, the graphic below outlines Guix v1.3.0's dependency graph: + +![bootstrap map](https://user-images.githubusercontent.com/6399679/125064185-a9a59880-e0b0-11eb-82c1-9b8e5dc9950d.png) + +If you do not care about building each dependency from source, and Guix is +already packaged for your distribution, you can easily install only the build +dependencies of Guix. For example, to enable deb-src and install the Guix build +dependencies on Ubuntu/Debian: + +```sh +sed -i 's|# deb-src|deb-src|g' /etc/apt/sources.list +apt update +apt-get build-dep -y guix +``` + +If this succeeded, you can likely skip to section +["Building and Installing Guix itself"](#building-and-installing-guix-itself). + +#### Guile + +###### Corner case: Multiple versions of Guile on one system + +It is recommended to only install the required version of Guile, so that build systems do +not get confused about which Guile to use. + +However, if you insist on having more versions of Guile installed on +your system, then you need to **consistently** specify +`GUILE_EFFECTIVE_VERSION=3.0` to all +`./configure` invocations for Guix and its dependencies. + +##### Installing Guile + +If your distribution splits packages into `-dev`-suffixed and +non-`-dev`-suffixed sub-packages (as is the case for Debian-derived +distributions), please make sure to install both. For example, to install Guile +v3.0 on Debian/Ubuntu: + +```sh +apt install guile-3.0 guile-3.0-dev +``` + +#### Mixing distribution packages and source-built packages + +At the time of writing, most distributions have _some_ of Guix's dependencies +packaged, but not all. This means that you may want to install the distribution +package for some dependencies, and manually build-from-source for others. + +Distribution packages usually install to `/usr`, which is different from the +default `./configure` prefix of source-built packages: `/usr/local`. + +This means that if you mix-and-match distribution packages and source-built +packages and do not specify exactly `--prefix=/usr` to `./configure` for +source-built packages, you will need to augment the `GUILE_LOAD_PATH` and +`GUILE_LOAD_COMPILED_PATH` environment variables so that Guile will look +under the right prefix and find your source-built packages. + +For example, if you are using Guile v3.0, and have Guile packages in the +`/usr/local` prefix, either add the following lines to your `.profile` or +`.bash_profile` so that the environment variable is properly set for all future +shell logins, or paste the lines into a POSIX-style shell to temporarily modify +the environment variables of your current shell session. + +```sh +# Help Guile v3.0.x find packages in /usr/local +export GUILE_LOAD_PATH="/usr/local/share/guile/site/3.0${GUILE_LOAD_PATH:+:}$GUILE_LOAD_PATH" +export GUILE_LOAD_COMPILED_PATH="/usr/local/lib/guile/3.0/site-ccache${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_COMPILED_LOAD_PATH" +``` + +Note that these environment variables are used to check for packages during +`./configure`, so they should be set as soon as possible should you want to use +a prefix other than `/usr`. + +#### Building and installing source-built packages + +***IMPORTANT**: A few dependencies have non-obvious quirks/errata which are +documented in the sub-sections immediately below. Please read these sections +before proceeding to build and install these packages.* + +Although you should always refer to the README or INSTALL files for the most +accurate information, most of these dependencies use autoconf-style build +systems (check if there's a `configure.ac` file), and will likely do the right +thing with the following: + +Clone the repository and check out the latest release: +```sh +git clone /.git +cd +git tag -l # check for the latest release +git checkout +``` + +For autoconf-based build systems (if `./autogen.sh` or `configure.ac` exists at +the root of the repository): + +```sh +./autogen.sh || autoreconf -vfi +./configure --prefix= +make +sudo make install +``` + +For CMake-based build systems (if `CMakeLists.txt` exists at the root of the +repository): + +```sh +mkdir build && cd build +cmake .. -DCMAKE_INSTALL_PREFIX= +sudo cmake --build . --target install +``` + +If you choose not to specify exactly `--prefix=/usr` to `./configure`, please +make sure you've carefully read the [previous section] on mixing distribution +packages and source-built packages. + +##### Binding packages require `-dev`-suffixed packages + +Relevant for: +- Everyone + +When building bindings, the `-dev`-suffixed version of the original package +needs to be installed. For example, building `Guile-zlib` on Debian-derived +distributions requires that `zlib1g-dev` is installed. + +When using bindings, the `-dev`-suffixed version of the original package still +needs to be installed. This is particularly problematic when distribution +packages are mispackaged like `guile-sqlite3` is in Ubuntu Focal such that +installing `guile-sqlite3` does not automatically install `libsqlite3-dev` as a +dependency. + +Below is a list of relevant Guile bindings and their corresponding `-dev` +packages in Debian at the time of writing. + +| Guile binding package | -dev Debian package | +|-----------------------|---------------------| +| guile-gcrypt | libgcrypt-dev | +| guile-git | libgit2-dev | +| guile-gnutls | (none) | +| guile-json | (none) | +| guile-lzlib | liblz-dev | +| guile-ssh | libssh-dev | +| guile-sqlite3 | libsqlite3-dev | +| guile-zlib | zlib1g-dev | + +##### `guile-git` actually depends on `libgit2 >= 1.1` + +Relevant for: +- Those building `guile-git` from source against `libgit2 < 1.1` +- Those installing `guile-git` from their distribution where `guile-git` is + built against `libgit2 < 1.1` + +As of v0.5.2, `guile-git` claims to only require `libgit2 >= 0.28.0`, however, +it actually requires `libgit2 >= 1.1`, otherwise, it will be confused by a +reference of `origin/keyring`: instead of interpreting the reference as "the +'keyring' branch of the 'origin' remote", the reference is interpreted as "the +branch literally named 'origin/keyring'" + +This is especially notable because Ubuntu Focal packages `libgit2 v0.28.4`, and +`guile-git` is built against it. + +Should you be in this situation, you need to build both `libgit2 v1.1.x` and +`guile-git` from source. + +Source: https://logs.guix.gnu.org/guix/2020-11-12.log#232527 + +### Building and Installing Guix itself + +Start by cloning Guix: + +``` +git clone https://git.savannah.gnu.org/git/guix.git +cd guix +``` + +You will likely want to build the latest release. +At the time of writing (November 2023), the latest release was `v1.4.0`. + +``` +git branch -a -l 'origin/version-*' # check for the latest release +git checkout +``` + +Bootstrap the build system: +``` +./bootstrap +``` + +Configure with the recommended `--localstatedir` flag: +``` +./configure --localstatedir=/var +``` + +Note: If you intend to hack on Guix in the future, you will need to supply the +same `--localstatedir=` flag for all future Guix `./configure` invocations. See +the last paragraph of this +[section](https://guix.gnu.org/manual/en/html_node/Requirements.html) for more +details. + +Build Guix (this will take a while): +``` +make -j$(nproc) +``` + +Install Guix: + +``` +sudo make install +``` + +### Post-"build from source" Setup + +#### Creating and starting a `guix-daemon-original` service with a fixed `argv[0]` + +At this point, guix will be installed to `${bindir}`, which is likely +`/usr/local/bin` if you did not override directory variables at +`./configure`-time. More information on standard Automake directory variables +can be found +[here](https://www.gnu.org/software/automake/manual/html_node/Standard-Directory-Variables.html). + +However, the Guix init scripts and service configurations for Upstart, systemd, +SysV, and OpenRC are installed (in `${libdir}`) to launch +`${localstatedir}/guix/profiles/per-user/root/current-guix/bin/guix-daemon`, +which does not yet exist, and will only exist after [`root` performs their first +`guix pull`](#guix-pull-as-root). + +We need to create a `-original` version of these init scripts that's pointed to +the binaries we just built and `make install`'ed in `${bindir}` (normally, +`/usr/local/bin`). + +Example for `systemd`, run as `root`: + +```sh +# Create guix-daemon-original.service by modifying guix-daemon.service +libdir=# set according to your PREFIX (default is /usr/local/lib) +bindir="$(dirname $(command -v guix-daemon))" +sed -E -e "s|/\S*/guix/profiles/per-user/root/current-guix/bin/guix-daemon|${bindir}/guix-daemon|" "${libdir}"/systemd/system/guix-daemon.service > /etc/systemd/system/guix-daemon-original.service +chmod 664 /etc/systemd/system/guix-daemon-original.service + +# Make systemd recognize the new service +systemctl daemon-reload + +# Make sure that the non-working guix-daemon.service is stopped and disabled +systemctl stop guix-daemon +systemctl disable guix-daemon + +# Make sure that the working guix-daemon-original.service is started and enabled +systemctl enable guix-daemon-original +systemctl start guix-daemon-original +``` + +#### Creating `guix-daemon` users / groups + +Please see the [relevant +section](https://guix.gnu.org/manual/en/html_node/Build-Environment-Setup.html) +in the Guix Reference Manual for more details. + +## Optional setup + +At this point, you are set up to [use Guix to build BitcoinZ +Core](./README.md#usage). However, if you want to polish your setup a bit and +make it "what Guix intended", then read the next few subsections. + +### Add an `/etc/profile.d` entry + +This section definitely does not apply to you if you installed Guix using: +1. The shell installer script +2. fanquake's Docker image +3. Debian's `guix` package + +#### Background + +Although Guix knows how to update itself and its packages, it does so in a +non-invasive way (it does not modify `/usr/local/bin/guix`). + +Instead, it does the following: + +- After a `guix pull`, it updates + `/var/guix/profiles/per-user/$USER/current-guix`, and creates a symlink + targeting this directory at `$HOME/.config/guix/current` + +- After a `guix install`, it updates + `/var/guix/profiles/per-user/$USER/guix-profile`, and creates a symlink + targeting this directory at `$HOME/.guix-profile` + +Therefore, in order for these operations to affect your shell/desktop sessions +(and for the principle of least astonishment to hold), their corresponding +directories have to be added to well-known environment variables like `$PATH`, +`$INFOPATH`, `$XDG_DATA_DIRS`, etc. + +In other words, if `$HOME/.config/guix/current/bin` does not exist in your +`$PATH`, a `guix pull` will have no effect on what `guix` you are using. Same +goes for `$HOME/.guix-profile/bin`, `guix install`, and installed packages. + +Helpfully, after a `guix pull` or `guix install`, a message will be printed like +so: + +``` +hint: Consider setting the necessary environment variables by running: + + GUIX_PROFILE="$HOME/.guix-profile" + . "$GUIX_PROFILE/etc/profile" + +Alternately, see `guix package --search-paths -p "$HOME/.guix-profile"'. +``` + +However, this is somewhat tedious to do for both `guix pull` and `guix install` +for each user on the system that wants to properly use `guix`. I recommend that +you instead add an entry to `/etc/profile.d` instead. This is done by default +when installing the Debian package later than 1.2.0-4 and when using the shell +script installer. + +#### Instructions + +Create `/etc/profile.d/guix.sh` with the following content: +```sh +# _GUIX_PROFILE: `guix pull` profile +_GUIX_PROFILE="$HOME/.config/guix/current" +if [ -L $_GUIX_PROFILE ]; then + export PATH="$_GUIX_PROFILE/bin${PATH:+:}$PATH" + # Export INFOPATH so that the updated info pages can be found + # and read by both /usr/bin/info and/or $GUIX_PROFILE/bin/info + # When INFOPATH is unset, add a trailing colon so that Emacs + # searches 'Info-default-directory-list'. + export INFOPATH="$_GUIX_PROFILE/share/info:$INFOPATH" +fi + +# GUIX_PROFILE: User's default profile +GUIX_PROFILE="$HOME/.guix-profile" +[ -L $GUIX_PROFILE ] || return +GUIX_LOCPATH="$GUIX_PROFILE/lib/locale" +export GUIX_PROFILE GUIX_LOCPATH + +[ -f "$GUIX_PROFILE/etc/profile" ] && . "$GUIX_PROFILE/etc/profile" + +# set XDG_DATA_DIRS to include Guix installations +export XDG_DATA_DIRS="$GUIX_PROFILE/share:${XDG_DATA_DIRS:-/usr/local/share/:/usr/share/}" +``` + +Please note that this will not take effect until the next shell or desktop +session (log out and log back in). + +### `guix pull` as root + +Before you do this, you need to read the section on [choosing your security +model][security-model] and adjust `guix` and `guix-daemon` flags according to +your choice, as invoking `guix pull` may pull substitutes from substitute +servers (which you may not want). + +As mentioned in a previous section, Guix expects +`${localstatedir}/guix/profiles/per-user/root/current-guix` to be populated with +`root`'s Guix profile, `guix pull`-ed and built by some former version of Guix. +However, this is not the case when we build from source. Therefore, we need to +perform a `guix pull` as `root`: + +```sh +sudo --login guix pull --branch=version- +# or +sudo --login guix pull --commit= +``` + +`guix pull` is quite a long process (especially if you're using +`--no-substitutes`). If you encounter build problems, please refer to the +[troubleshooting section](#troubleshooting). + +Note that running a bare `guix pull` with no commit or branch specified will +pull the latest commit on Guix's master branch, which is likely fine, but not +recommended. + +If you installed Guix from source, you may get an error like the following: +```sh +error: while creating symlink '/root/.config/guix/current' No such file or directory +``` +To resolve this, simply: +``` +sudo mkdir -p /root/.config/guix +``` +Then try the `guix pull` command again. + +After the `guix pull` finishes successfully, +`${localstatedir}/guix/profiles/per-user/root/current-guix` should be populated. + +#### Using the newly-pulled `guix` by restarting the daemon + +Depending on how you installed Guix, you should now make sure that your init +scripts and service configurations point to the newly-pulled `guix-daemon`. + +##### If you built Guix from source + +If you followed the instructions for [fixing argv\[0\]][fix-argv0], you can now +do the following: + +```sh +systemctl stop guix-daemon-original +systemctl disable guix-daemon-original + +systemctl enable guix-daemon +systemctl start guix-daemon +``` + +Remember to set `--no-substitutes` in `$libdir/systemd/system/guix-daemon.service` and other customizations if you used them for `guix-daemon-original.service`. + +##### If you installed Guix via the Debian/Ubuntu distribution packages + +You will need to create a `guix-daemon-latest` service which points to the new +`guix` rather than a pinned one. + +```sh +# Create guix-daemon-latest.service by modifying guix-daemon.service +sed -E -e "s|/usr/bin/guix-daemon|/var/guix/profiles/per-user/root/current-guix/bin/guix-daemon|" /etc/systemd/system/guix-daemon.service > /lib/systemd/system/guix-daemon-latest.service +chmod 664 /lib/systemd/system/guix-daemon-latest.service + +# Make systemd recognize the new service +systemctl daemon-reload + +# Make sure that the old guix-daemon.service is stopped and disabled +systemctl stop guix-daemon +systemctl disable guix-daemon + +# Make sure that the new guix-daemon-latest.service is started and enabled +systemctl enable guix-daemon-latest +systemctl start guix-daemon-latest +``` + +##### If you installed Guix via lantw44's Arch Linux AUR package + +At the time of writing (July 5th, 2021) the systemd unit for "updated Guix" is +`guix-daemon-latest.service`, therefore, you should do the following: + +```sh +systemctl stop guix-daemon +systemctl disable guix-daemon + +systemctl enable guix-daemon-latest +systemctl start guix-daemon-latest +``` + +##### Otherwise... + +Simply do: + +```sh +systemctl restart guix-daemon +``` + +### Checking everything + +If you followed all the steps above to make your Guix setup "prim and proper," +you can check that you did everything properly by running through this +checklist. + +1. `/etc/profile.d/guix.sh` should exist and be sourced at each shell login + +2. `guix describe` should not print `guix describe: error: failed to determine + origin`, but rather something like: + + ``` + Generation 38 Feb 22 2021 16:39:31 (current) + guix f350df4 + repository URL: https://git.savannah.gnu.org/git/guix.git + branch: version-1.2.0 + commit: f350df405fbcd5b9e27e6b6aa500da7f101f41e7 + ``` + +3. `guix-daemon` should be running from `${localstatedir}/guix/profiles/per-user/root/current-guix` + +# Troubleshooting + +## Derivation failed to build + +When you see a build failure like below: + +``` +building /gnu/store/...-foo-3.6.12.drv... +/ 'check' phasenote: keeping build directory `/tmp/guix-build-foo-3.6.12.drv-0' +builder for `/gnu/store/...-foo-3.6.12.drv' failed with exit code 1 +build of /gnu/store/...-foo-3.6.12.drv failed +View build log at '/var/log/guix/drvs/../...-foo-3.6.12.drv.bz2'. +cannot build derivation `/gnu/store/...-qux-7.69.1.drv': 1 dependencies couldn't be built +cannot build derivation `/gnu/store/...-bar-3.16.5.drv': 1 dependencies couldn't be built +cannot build derivation `/gnu/store/...-baz-2.0.5.drv': 1 dependencies couldn't be built +guix time-machine: error: build of `/gnu/store/...-baz-2.0.5.drv' failed +``` + +It means that `guix` failed to build a package named `foo`, which was a +dependency of `qux`, `bar`, and `baz`. Importantly, note that the last "failed" +line is not necessarily the root cause, the first "failed" line is. + +Most of the time, the build failure is due to a spurious test failure or the +package's build system/test suite breaking when running multi-threaded. To +rebuild _just_ this derivation in a single-threaded fashion (please don't forget +to add other `guix` flags like `--no-substitutes` as appropriate): + +```sh +$ guix build --cores=1 /gnu/store/...-foo-3.6.12.drv +``` + +If the single-threaded rebuild did not succeed, you may need to dig deeper. +You may view `foo`'s build logs in `less` like so (please replace paths with the +path you see in the build failure output): + +```sh +$ bzcat /var/log/guix/drvs/../...-foo-3.6.12.drv.bz2 | less +``` + +`foo`'s build directory is also preserved and available at +`/tmp/guix-build-foo-3.6.12.drv-0`. However, if you fail to build `foo` multiple +times, it may be `/tmp/...drv-1` or `/tmp/...drv-2`. Always consult the build +failure output for the most accurate, up-to-date information. + +### python(-minimal): [Errno 84] Invalid or incomplete multibyte or wide character + +This error occurs when your `$TMPDIR` (default: /tmp) exists on a filesystem +which rejects characters not present in the UTF-8 character code set. An example +is ZFS with the utf8only=on option set. + +More information: https://github.com/python/cpython/issues/81765 + +### openssl-1.1.1l and openssl-1.1.1n + +OpenSSL includes tests that will fail once some certificate has expired. +The workarounds from the GnuTLS section immediately below can be used. + +For openssl-1.1.1l use 2022-05-01 as the date. + +### GnuTLS: test-suite FAIL: status-request-revoked + +*The derivation is likely identified by: `/gnu/store/vhphki5sg9xkdhh2pbc8gi6vhpfzryf0-gnutls-3.6.12.drv`* + +This unfortunate error is most common for non-substitute builders who installed +Guix v1.2.0. The problem stems from the fact that one of GnuTLS's tests uses a +hardcoded certificate which expired on 2020-10-24. + +What's more unfortunate is that this GnuTLS derivation is somewhat special in +Guix's dependency graph and is not affected by the package transformation flags +like `--without-tests=`. + +The easiest solution for those encountering this problem is to install a newer +version of Guix. However, there are ways to work around this issue: + +#### Workaround 1: Using substitutes for this single derivation + +If you've authorized the official Guix build farm's key (more info +[here](./README.md#step-1-authorize-the-signing-keys)), then you can use +substitutes just for this single derivation by invoking the following: + +```sh +guix build --substitute-urls="https://ci.guix.gnu.org" /gnu/store/vhphki5sg9xkdhh2pbc8gi6vhpfzryf0-gnutls-3.6.12.drv +``` + +See [this section](./README.md#removing-authorized-keys) for instructions on how +to remove authorized keys if you don't want to keep the build farm's key +authorized. + +#### Workaround 2: Temporarily setting the system clock back + +This workaround was described [here](https://issues.guix.gnu.org/44559#5). + +Basically: + +1. Turn off NTP +2. Set system time to 2020-10-01 +3. guix build --no-substitutes /gnu/store/vhphki5sg9xkdhh2pbc8gi6vhpfzryf0-gnutls-3.6.12.drv +4. Set system time back to accurate current time +5. Turn NTP back on + +For example, + +```sh +sudo timedatectl set-ntp no +sudo date --set "01 oct 2020 15:00:00" +guix build /gnu/store/vhphki5sg9xkdhh2pbc8gi6vhpfzryf0-gnutls-3.6.12.drv +sudo timedatectl set-ntp yes +``` + +#### Workaround 3: Disable the tests in the Guix source code for this single derivation + +If all of the above workarounds fail, you can also disable the `tests` phase of +the derivation via the `arguments` option, as described in the official +[`package` +reference](https://guix.gnu.org/manual/en/html_node/package-Reference.html). + +For example, to disable the openssl-1.1 check phase: + +```diff +diff --git a/gnu/packages/tls.scm b/gnu/packages/tls.scm +index f1e844b..1077c4b 100644 +--- a/gnu/packages/tls.scm ++++ b/gnu/packages/tls.scm +@@ -494,4 +494,5 @@ (define-public openssl-1.1 + (arguments + `(#:parallel-tests? #f ++ #:tests? #f + #:test-target "test" +``` + +### coreutils: FAIL: tests/tail-2/inotify-dir-recreate + +The inotify-dir-create test fails on "remote" filesystems such as overlayfs +(Docker's default filesystem) due to the filesystem being mistakenly recognized +as non-remote. + +A relatively easy workaround to this is to make sure that a somewhat traditional +filesystem is mounted at `/tmp` (where `guix-daemon` performs its builds). For +Docker users, this might mean [using a volume][docker/volumes], [binding +mounting][docker/bind-mnt] from host, or (for those with enough RAM and swap) +[mounting a tmpfs][docker/tmpfs] using the `--tmpfs` flag. + +Please see the following links for more details: + +- An upstream coreutils bug has been filed: [debbugs#47940](https://debbugs.gnu.org/cgi/bugreport.cgi?bug=47940) +- A Guix bug detailing the underlying problem has been filed: [guix-issues#47935](https://issues.guix.gnu.org/47935), [guix-issues#49985](https://issues.guix.gnu.org/49985#5) +- A commit to skip this test in Guix has been merged into the core-updates branch: +[savannah/guix@6ba1058](https://git.savannah.gnu.org/cgit/guix.git/commit/?id=6ba1058df0c4ce5611c2367531ae5c3cdc729ab4) + + +[install-script]: #options-1-and-2-using-the-official-shell-installer-script-or-binary-tarball +[install-bin-tarball]: #options-1-and-2-using-the-official-shell-installer-script-or-binary-tarball +[install-fanquake-docker]: #option-3-using-fanquakes-docker-image +[install-distro-pkg]: #option-4-using-a-distribution-maintained-package +[install-source]: #option-5-building-from-source + +[fix-argv0]: #creating-and-starting-a-guix-daemon-original-service-with-a-fixed-argv0 +[security-model]: ./README.md#choosing-your-security-model + +[docker/volumes]: https://docs.docker.com/storage/volumes/ +[docker/bind-mnt]: https://docs.docker.com/storage/bind-mounts/ +[docker/tmpfs]: https://docs.docker.com/storage/tmpfs/ + +# Purging/Uninstalling Guix + +In the extraordinarily rare case where you messed up your Guix installation in +an irreversible way, you may want to completely purge Guix from your system and +start over. + +1. Uninstall Guix itself according to the way you installed it (e.g. `sudo apt + purge guix` for Ubuntu packaging, `sudo make uninstall` for a build from source). +2. Remove all build users and groups + + You may check for relevant users and groups using: + + ``` + getent passwd | grep guix + getent group | grep guix + ``` + + Then, you may remove users and groups using: + + ``` + sudo userdel + sudo groupdel + ``` + +3. Remove all possible Guix-related directories + - `/var/guix/` + - `/var/log/guix/` + - `/gnu/` + - `/etc/guix/` + - `/home/*/.config/guix/` + - `/home/*/.cache/guix/` + - `/home/*/.guix-profile/` + - `/root/.config/guix/` + - `/root/.cache/guix/` + - `/root/.guix-profile/` diff --git a/contrib/guix/README.md b/contrib/guix/README.md new file mode 100644 index 000000000..76ce34a4e --- /dev/null +++ b/contrib/guix/README.md @@ -0,0 +1,434 @@ +# Bootstrappable BitcoinZ Builds + +This directory contains the files necessary to perform bootstrappable BitcoinZ +Core builds. + +[Bootstrappability][b17e] furthers our binary security guarantees by allowing us +to _audit and reproduce_ our toolchain instead of blindly _trusting_ binary +downloads. + +We achieve bootstrappability by using Guix as a functional package manager. + +# Requirements + +Conservatively, you will need: + +- 16GB of free disk space on the partition that /gnu/store will reside in +- 8GB of free disk space **per platform triple** you're planning on building + (see the `HOSTS` [environment variable description][env-vars-list]) + +# Installation and Setup + +If you don't have Guix installed and set up, please follow the instructions in +[INSTALL.md](./INSTALL.md) + +# Usage + +If you haven't considered your security model yet, please read [the relevant +section](#choosing-your-security-model) before proceeding to perform a build. + +## Making the Xcode SDK available for macOS cross-compilation + +In order to perform a build for macOS (which is included in the default set of +platform triples to build), you'll need to extract the macOS SDK tarball using +tools found in the [`macdeploy` directory](../macdeploy/README.md). + +You can then either point to the SDK using the `SDK_PATH` environment variable: + +```sh +# Extract the SDK tarball to /path/to/parent/dir/of/extracted/SDK/Xcode---extracted-SDK-with-libcxx-headers +tar -C /path/to/parent/dir/of/extracted/SDK -xaf /path/to/Xcode---extracted-SDK-with-libcxx-headers.tar.gz + +# Indicate where to locate the SDK tarball +export SDK_PATH=/path/to/parent/dir/of/extracted/SDK +``` + +or extract it into `depends/SDKs`: + +```sh +mkdir -p depends/SDKs +tar -C depends/SDKs -xaf /path/to/SDK/tarball +``` + +## Building + +*The author highly recommends at least reading over the [common usage patterns +and examples](#common-guix-build-invocation-patterns-and-examples) section below +before starting a build. For a full list of customization options, see the +[recognized environment variables][env-vars-list] section.* + +To build BitcoinZ reproducibly with all default options, invoke the +following from the top of a clean repository: + +```sh +./contrib/guix/guix-build +``` + +## Codesigning build outputs + +The `guix-codesign` command attaches codesignatures (produced by codesigners) to +existing non-codesigned outputs. Please see the [release process +documentation](/doc/release-process.md) for more context. + +It respects many of the same environment variable flags as `guix-build`, with 2 +crucial differences: + +1. Since only Windows and macOS build outputs require codesigning, the `HOSTS` + environment variable will have a sane default value of `x86_64-w64-mingw32 + x86_64-apple-darwin` instead of all the platforms. +2. The `guix-codesign` command ***requires*** a `DETACHED_SIGS_REPO` flag. + * _**DETACHED_SIGS_REPO**_ + + Set the directory where detached codesignatures can be found for the current + BitcoinZ version being built. + + _REQUIRED environment variable_ + +An invocation with all default options would look like: + +``` +env DETACHED_SIGS_REPO= ./contrib/guix/guix-codesign +``` + +## Cleaning intermediate work directories + +By default, `guix-build` leaves all intermediate files or "work directories" +(e.g. `depends/work`, `guix-build-*/distsrc-*`) intact at the end of a build so +that they are available to the user (to aid in debugging, etc.). However, these +directories usually take up a large amount of disk space. Therefore, a +`guix-clean` convenience script is provided which cleans the current `git` +worktree to save disk space: + +``` +./contrib/guix/guix-clean +``` + + +## Attesting to build outputs + +Much like how Gitian build outputs are attested to in a `gitian.sigs` +repository, Guix build outputs are attested to in the [`guix.sigs` +repository](https://github.com/btcz/guix.sigs). + +After you've cloned the `guix.sigs` repository, to attest to the current +worktree's commit/tag: + +``` +env GUIX_SIGS_REPO= SIGNER= ./contrib/guix/guix-attest +``` + +See `./contrib/guix/guix-attest --help` for more information on the various ways +`guix-attest` can be invoked. + +## Verifying build output attestations + +After at least one other signer has uploaded their signatures to the `guix.sigs` +repository: + +``` +git -C pull +env GUIX_SIGS_REPO= ./contrib/guix/guix-verify +``` + + +## Common `guix-build` invocation patterns and examples + +### Keeping caches and SDKs outside of the worktree + +If you perform a lot of builds and have a bunch of worktrees, you may find it +more efficient to keep the depends tree's download cache, build cache, and SDKs +outside of the worktrees to avoid duplicate downloads and unnecessary builds. To +help with this situation, the `guix-build` script honours the `SOURCES_PATH`, +`BASE_CACHE`, and `SDK_PATH` environment variables and will pass them on to the +depends tree so that you can do something like: + +```sh +env SOURCES_PATH="$HOME/depends-SOURCES_PATH" BASE_CACHE="$HOME/depends-BASE_CACHE" SDK_PATH="$HOME/macOS-SDKs" ./contrib/guix/guix-build +``` + +Note that the paths that these environment variables point to **must be +directories**, and **NOT symlinks to directories**. + +See the [recognized environment variables][env-vars-list] section for more +details. + +### Building a subset of platform triples + +Sometimes you only want to build a subset of the supported platform triples, in +which case you can override the default list by setting the space-separated +`HOSTS` environment variable: + +```sh +env HOSTS='x86_64-w64-mingw32 x86_64-apple-darwin' ./contrib/guix/guix-build +``` + +See the [recognized environment variables][env-vars-list] section for more +details. + +### Controlling the number of threads used by `guix` build commands + +Depending on your system's RAM capacity, you may want to decrease the number of +threads used to decrease RAM usage or vice versa. + +By default, the scripts under `./contrib/guix` will invoke all `guix` build +commands with `--cores="$JOBS"`. Note that `$JOBS` defaults to `$(nproc)` if not +specified. However, astute manual readers will also notice that `guix` build +commands also accept a `--max-jobs=` flag (which defaults to 1 if unspecified). + +Here is the difference between `--cores=` and `--max-jobs=`: + +> Note: When I say "derivation," think "package" + +`--cores=` + + - controls the number of CPU cores to build each derivation. This is the value + passed to `make`'s `--jobs=` flag. + +`--max-jobs=` + + - controls how many derivations can be built in parallel + - defaults to 1 + +Therefore, the default is for `guix` build commands to build one derivation at a +time, utilizing `$JOBS` threads. + +Specifying the `$JOBS` environment variable will only modify `--cores=`, but you +can also modify the value for `--max-jobs=` by specifying +`$ADDITIONAL_GUIX_COMMON_FLAGS`. For example, if you have a LOT of memory, you +may want to set: + +```sh +export ADDITIONAL_GUIX_COMMON_FLAGS='--max-jobs=8' +``` + +Which allows for a maximum of 8 derivations to be built at the same time, each +utilizing `$JOBS` threads. + +Or, if you'd like to avoid spurious build failures caused by issues with +parallelism within a single package, but would still like to build multiple +packages when the dependency graph allows for it, you may want to try: + +```sh +export JOBS=1 ADDITIONAL_GUIX_COMMON_FLAGS='--max-jobs=8' +``` + +See the [recognized environment variables][env-vars-list] section for more +details. + +## Recognized environment variables + +* _**HOSTS**_ + + Override the space-separated list of platform triples for which to perform a + bootstrappable build. + + _(defaults to "x86\_64-linux-gnu aarch64-linux-gnu + x86\_64-w64-mingw32 x86\_64-apple-darwin")_ + +* _**SOURCES_PATH**_ + + Set the depends tree download cache for sources. This is passed through to the + depends tree. Setting this to the same directory across multiple builds of the + depends tree can eliminate unnecessary redownloading of package sources. + + The path that this environment variable points to **must be a directory**, and + **NOT a symlink to a directory**. + +* _**BASE_CACHE**_ + + Set the depends tree cache for built packages. This is passed through to the + depends tree. Setting this to the same directory across multiple builds of the + depends tree can eliminate unnecessary building of packages. + + The path that this environment variable points to **must be a directory**, and + **NOT a symlink to a directory**. + +* _**SDK_PATH**_ + + Set the path where _extracted_ SDKs can be found. This is passed through to + the depends tree. Note that this is should be set to the _parent_ directory of + the actual SDK (e.g. `SDK_PATH=$HOME/Downloads/macOS-SDKs` instead of + `$HOME/Downloads/macOS-SDKs/Xcode-12.2-12B45b-extracted-SDK-with-libcxx-headers`). + + The path that this environment variable points to **must be a directory**, and + **NOT a symlink to a directory**. + +* _**JOBS**_ + + Override the number of jobs to run simultaneously, you might want to do so on + a memory-limited machine. This may be passed to: + + - `guix` build commands as in `guix shell --cores="$JOBS"` + - `make` as in `make --jobs="$JOBS"` + - `xargs` as in `xargs -P"$JOBS"` + + See [here](#controlling-the-number-of-threads-used-by-guix-build-commands) for + more details. + + _(defaults to the value of `nproc` outside the container)_ + +* _**SOURCE_DATE_EPOCH**_ + + Override the reference UNIX timestamp used for bit-for-bit reproducibility, + the variable name conforms to [standard][r12e/source-date-epoch]. + + _(defaults to the output of `$(git log --format=%at -1)`)_ + +* _**V**_ + + If non-empty, will pass `V=1` to all `make` invocations, making `make` output + verbose. + + Note that any given value is ignored. The variable is only checked for + emptiness. More concretely, this means that `V=` (setting `V` to the empty + string) is interpreted the same way as not setting `V` at all, and that `V=0` + has the same effect as `V=1`. + +* _**SUBSTITUTE_URLS**_ + + A whitespace-delimited list of URLs from which to download pre-built packages. + A URL is only used if its signing key is authorized (refer to the [substitute + servers section](#option-1-building-with-substitutes) for more details). + +* _**ADDITIONAL_GUIX_COMMON_FLAGS**_ + + Additional flags to be passed to all `guix` commands. + +* _**ADDITIONAL_GUIX_TIMEMACHINE_FLAGS**_ + + Additional flags to be passed to `guix time-machine`. + +* _**ADDITIONAL_GUIX_ENVIRONMENT_FLAGS**_ + + Additional flags to be passed to the invocation of `guix shell` inside + `guix time-machine`. + +# Choosing your security model + +No matter how you installed Guix, you need to decide on your security model for +building packages with Guix. + +Guix allows us to achieve better binary security by using our CPU time to build +everything from scratch. However, it doesn't sacrifice user choice in pursuit of +this: users can decide whether or not to use **substitutes** (pre-built +packages). + +## Option 1: Building with substitutes + +### Step 1: Authorize the signing keys + +Depending on the installation procedure you followed, you may have already +authorized the Guix build farm key. In particular, the official shell installer +script asks you if you want the key installed, and the debian distribution +package authorized the key during installation. + +You can check the current list of authorized keys at `/etc/guix/acl`. + +At the time of writing, a `/etc/guix/acl` with just the Guix build farm key +authorized looks something like: + +```lisp +(acl + (entry + (public-key + (ecc + (curve Ed25519) + (q #8D156F295D24B0D9A86FA5741A840FF2D24F60F7B6C4134814AD55625971B394#) + ) + ) + (tag + (guix import) + ) + ) + ) +``` + +If you've determined that the official Guix build farm key hasn't been +authorized, and you would like to authorize it, run the following as root: + +``` +guix archive --authorize < /var/guix/profiles/per-user/root/current-guix/share/guix/ci.guix.gnu.org.pub +``` + +If +`/var/guix/profiles/per-user/root/current-guix/share/guix/ci.guix.gnu.org.pub` +doesn't exist, try: + +```sh +guix archive --authorize < /share/guix/ci.guix.gnu.org.pub +``` + +Where `` is likely: +- `/usr` if you installed from a distribution package +- `/usr/local` if you installed Guix from source and didn't supply any + prefix-modifying flags to Guix's `./configure` + +For dongcarl's substitute server at https://guix.carldong.io, run as root: + +```sh +wget -qO- 'https://guix.carldong.io/signing-key.pub' | guix archive --authorize +``` + +#### Removing authorized keys + +To remove previously authorized keys, simply edit `/etc/guix/acl` and remove the +`(entry (public-key ...))` entry. + +### Step 2: Specify the substitute servers + +Once its key is authorized, the official Guix build farm at +https://ci.guix.gnu.org is automatically used unless the `--no-substitutes` flag +is supplied. This default list of substitute servers is overridable both on a +`guix-daemon` level and when you invoke `guix` commands. See examples below for +the various ways of adding dongcarl's substitute server after having [authorized +his signing key](#step-1-authorize-the-signing-keys). + +Change the **default list** of substitute servers by starting `guix-daemon` with +the `--substitute-urls` option (you will likely need to edit your init script): + +```sh +guix-daemon --substitute-urls='https://guix.carldong.io https://ci.guix.gnu.org' +``` + +Override the default list of substitute servers by passing the +`--substitute-urls` option for invocations of `guix` commands: + +```sh +guix --substitute-urls='https://guix.carldong.io https://ci.guix.gnu.org' +``` + +For scripts under `./contrib/guix`, set the `SUBSTITUTE_URLS` environment +variable: + +```sh +export SUBSTITUTE_URLS='https://guix.carldong.io https://ci.guix.gnu.org' +``` + +## Option 2: Disabling substitutes on an ad-hoc basis + +If you prefer not to use any substitutes, make sure to supply `--no-substitutes` +like in the following snippet. The first build will take a while, but the +resulting packages will be cached for future builds. + +For direct invocations of `guix`: +```sh +guix --no-substitutes +``` + +For the scripts under `./contrib/guix/`: +```sh +export ADDITIONAL_GUIX_COMMON_FLAGS='--no-substitutes' +``` + +## Option 3: Disabling substitutes by default + +`guix-daemon` accepts a `--no-substitutes` flag, which will make sure that, +unless otherwise overridden by a command line invocation, no substitutes will be +used. + +If you start `guix-daemon` using an init script, you can edit said script to +supply this flag. + +[b17e]: https://bootstrappable.org/ +[r12e/source-date-epoch]: https://reproducible-builds.org/docs/source-date-epoch/ +[env-vars-list]: #recognized-environment-variables diff --git a/contrib/guix/guix-attest b/contrib/guix/guix-attest new file mode 100755 index 000000000..91591a8cc --- /dev/null +++ b/contrib/guix/guix-attest @@ -0,0 +1,263 @@ +#!/usr/bin/env bash +export LC_ALL=C +set -e -o pipefail + +# Source the common prelude, which: +# 1. Checks if we're at the top directory of the BitcoinZ repository +# 2. Defines a few common functions and variables +# +# shellcheck source=libexec/prelude.bash +source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash" + + +################### +## Sanity Checks ## +################### + +################ +# Required non-builtin commands should be invokable +################ + +check_tools cat env basename mkdir diff sort + +if [ -z "$NO_SIGN" ]; then + # make it possible to override the gpg binary + GPG=${GPG:-gpg} + + # $GPG can contain extra arguments passed to the binary + # so let's check only the existence of arg[0] + # shellcheck disable=SC2206 + GPG_ARRAY=($GPG) + check_tools "${GPG_ARRAY[0]}" +fi + +################ +# Required env vars should be non-empty +################ + +cmd_usage() { +cat < \\ + SIGNER=GPG_KEY_NAME[=SIGNER_NAME] \\ + [ NO_SIGN=1 ] + ./contrib/guix/guix-attest + +Example w/o overriding signing name: + + env GUIX_SIGS_REPO=/home/achow101/guix.sigs \\ + SIGNER=achow101 \\ + ./contrib/guix/guix-attest + +Example overriding signing name: + + env GUIX_SIGS_REPO=/home/dongcarl/guix.sigs \\ + SIGNER=0x96AB007F1A7ED999=dongcarl \\ + ./contrib/guix/guix-attest + +Example w/o signing, just creating SHA256SUMS: + + env GUIX_SIGS_REPO=/home/achow101/guix.sigs \\ + SIGNER=achow101 \\ + NO_SIGN=1 \\ + ./contrib/guix/guix-attest + +EOF +} + +if [ -z "$GUIX_SIGS_REPO" ] || [ -z "$SIGNER" ]; then + cmd_usage + exit 1 +fi + +################ +# GUIX_SIGS_REPO should exist as a directory +################ + +if [ ! -d "$GUIX_SIGS_REPO" ]; then +cat << EOF +ERR: The specified GUIX_SIGS_REPO is not an existent directory: + + '$GUIX_SIGS_REPO' + +Hint: Please clone the guix.sigs repository and point to it with the + GUIX_SIGS_REPO environment variable. + +EOF +cmd_usage +exit 1 +fi + +################ +# The key specified in SIGNER should be usable +################ + +IFS='=' read -r gpg_key_name signer_name <<< "$SIGNER" +if [ -z "${signer_name}" ]; then + signer_name="$gpg_key_name" +fi + +if [ -z "$NO_SIGN" ] && ! ${GPG} --dry-run --list-secret-keys "${gpg_key_name}" >/dev/null 2>&1; then + echo "ERR: GPG can't seem to find any key named '${gpg_key_name}'" + exit 1 +fi + +################ +# We should be able to find at least one output +################ + +echo "Looking for build output SHA256SUMS fragments in ${OUTDIR_BASE}" + +shopt -s nullglob +sha256sum_fragments=( "$OUTDIR_BASE"/*/SHA256SUMS.part ) # This expands to an array of directories... +shopt -u nullglob + +noncodesigned_fragments=() +codesigned_fragments=() + +if (( ${#sha256sum_fragments[@]} )); then + echo "Found build output SHA256SUMS fragments:" + for outdir in "${sha256sum_fragments[@]}"; do + echo " '$outdir'" + case "$outdir" in + "$OUTDIR_BASE"/*-codesigned/SHA256SUMS.part) + codesigned_fragments+=("$outdir") + ;; + *) + noncodesigned_fragments+=("$outdir") + ;; + esac + done + echo +else + echo "ERR: Could not find any build output SHA256SUMS fragments in ${OUTDIR_BASE}" + exit 1 +fi + +############## +## Attest ## +############## + +# Usage: out_name $outdir +# +# HOST: The output directory being attested +# +out_name() { + basename "$(dirname "$1")" +} + +shasum_already_exists() { +cat < "$temp_noncodesigned" + if [ -e noncodesigned.SHA256SUMS ]; then + # The SHA256SUMS already exists, make sure it's exactly what we + # expect, error out if not + if diff -u noncodesigned.SHA256SUMS "$temp_noncodesigned"; then + echo "A noncodesigned.SHA256SUMS file already exists for '${VERSION}' and is up-to-date." + else + shasum_already_exists noncodesigned.SHA256SUMS + exit 1 + fi + else + mv "$temp_noncodesigned" noncodesigned.SHA256SUMS + fi + else + echo "ERR: No noncodesigned outputs found for '${VERSION}', exiting..." + exit 1 + fi + + temp_all="$(mktemp)" + trap 'rm -rf -- "$temp_all"' EXIT + + if (( ${#codesigned_fragments[@]} )); then + # Note: all.SHA256SUMS attests to all of $sha256sum_fragments, but is + # not needed if there are no $codesigned_fragments + cat "${sha256sum_fragments[@]}" \ + | sort -u \ + | sort -k2 \ + | basenameify_SHA256SUMS \ + > "$temp_all" + if [ -e all.SHA256SUMS ]; then + # The SHA256SUMS already exists, make sure it's exactly what we + # expect, error out if not + if diff -u all.SHA256SUMS "$temp_all"; then + echo "An all.SHA256SUMS file already exists for '${VERSION}' and is up-to-date." + else + shasum_already_exists all.SHA256SUMS + exit 1 + fi + else + mv "$temp_all" all.SHA256SUMS + fi + else + # It is fine to have the codesigned outputs be missing (perhaps the + # detached codesigs have not been published yet), just print a log + # message instead of erroring out + echo "INFO: No codesigned outputs found for '${VERSION}', skipping..." + fi + + if [ -z "$NO_SIGN" ]; then + echo "Signing SHA256SUMS to produce SHA256SUMS.asc" + for i in *.SHA256SUMS; do + if [ ! -e "$i".asc ]; then + ${GPG} --detach-sign \ + --digest-algo sha256 \ + --local-user "$gpg_key_name" \ + --armor \ + --output "$i".asc "$i" + else + echo "Signature already there" + fi + done + else + echo "Not signing SHA256SUMS as \$NO_SIGN is not empty" + fi + echo "" +) diff --git a/contrib/guix/guix-build b/contrib/guix/guix-build new file mode 100755 index 000000000..97417adcf --- /dev/null +++ b/contrib/guix/guix-build @@ -0,0 +1,467 @@ +#!/usr/bin/env bash +export LC_ALL=C +set -e -o pipefail + +# Source the common prelude, which: +# 1. Checks if we're at the top directory of the BitcoinZ repository +# 2. Defines a few common functions and variables +# +# shellcheck source=libexec/prelude.bash +source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash" + + +################### +## SANITY CHECKS ## +################### + +################ +# Required non-builtin commands should be invocable +################ + +check_tools cat mkdir make getent curl git guix + +################ +# GUIX_BUILD_OPTIONS should be empty +################ +# +# GUIX_BUILD_OPTIONS is an environment variable recognized by guix commands that +# can perform builds. This seems like what we want instead of +# ADDITIONAL_GUIX_COMMON_FLAGS, but the value of GUIX_BUILD_OPTIONS is actually +# _appended_ to normal command-line options. Meaning that they will take +# precedence over the command-specific ADDITIONAL_GUIX__FLAGS. +# +# This seems like a poor user experience. Thus we check for GUIX_BUILD_OPTIONS's +# existence here and direct users of this script to use our (more flexible) +# custom environment variables. +if [ -n "$GUIX_BUILD_OPTIONS" ]; then +cat << EOF +Error: Environment variable GUIX_BUILD_OPTIONS is not empty: + '$GUIX_BUILD_OPTIONS' + +Unfortunately this script is incompatible with GUIX_BUILD_OPTIONS, please unset +GUIX_BUILD_OPTIONS and use ADDITIONAL_GUIX_COMMON_FLAGS to set build options +across guix commands or ADDITIONAL_GUIX__FLAGS to set build options for a +specific guix command. + +See contrib/guix/README.md for more details. +EOF +exit 1 +fi + +################ +# The git worktree should not be dirty +################ + +if ! git diff-index --quiet HEAD -- && [ -z "$FORCE_DIRTY_WORKTREE" ]; then +cat << EOF +ERR: The current git worktree is dirty, which may lead to broken builds. + + Aborting... + +Hint: To make your git worktree clean, You may want to: + 1. Commit your changes, + 2. Stash your changes, or + 3. Set the 'FORCE_DIRTY_WORKTREE' environment variable if you insist on + using a dirty worktree +EOF +exit 1 +fi + +mkdir -p "$VERSION_BASE" + +################ +# Build directories should not exist +################ + +# Default to building for all supported HOSTs (overridable by environment) +# powerpc64le-linux-gnu currently disabled due non-determinism issues across build arches. +export HOSTS="${HOSTS:-x86_64-linux-gnu aarch64-linux-gnu x86_64-w64-mingw32 x86_64-apple-darwin}" + +# Usage: distsrc_for_host HOST +# +# HOST: The current platform triple we're building for +# +distsrc_for_host() { + echo "${DISTSRC_BASE}/distsrc-${VERSION}-${1}" +} + +# Accumulate a list of build directories that already exist... +hosts_distsrc_exists="" +for host in $HOSTS; do + if [ -e "$(distsrc_for_host "$host")" ]; then + hosts_distsrc_exists+=" ${host}" + fi +done + +if [ -n "$hosts_distsrc_exists" ]; then +# ...so that we can print them out nicely in an error message +cat << EOF +ERR: Build directories for this commit already exist for the following platform + triples you're attempting to build, probably because of previous builds. + Please remove, or otherwise deal with them prior to starting another build. + + Aborting... + +Hint: To blow everything away, you may want to use: + + $ ./contrib/guix/guix-clean + +Specifically, this will remove all files without an entry in the index, +excluding the SDK directory, the depends download cache, the depends built +packages cache, the garbage collector roots for Guix environments, and the +output directory. +EOF +for host in $hosts_distsrc_exists; do + echo " ${host} '$(distsrc_for_host "$host")'" +done +exit 1 +else + mkdir -p "$DISTSRC_BASE" +fi + +################ +# When building for darwin, the macOS SDK should exist +################ + +for host in $HOSTS; do + case "$host" in + *darwin*) + OSX_SDK="$(make -C "${PWD}/depends" --no-print-directory HOST="$host" print-OSX_SDK | sed 's@^[^=]\+=@@g')" + if [ -e "$OSX_SDK" ]; then + echo "Found macOS SDK at '${OSX_SDK}', using..." + break + else + echo "macOS SDK does not exist at '${OSX_SDK}', please place the extracted, untarred SDK there to perform darwin builds, or define SDK_PATH environment variable. Exiting..." + exit 1 + fi + ;; + esac +done + +################ +# VERSION_BASE should have enough space +################ + +avail_KiB="$(df -Pk "$VERSION_BASE" | sed 1d | tr -s ' ' | cut -d' ' -f4)" +total_required_KiB=0 +for host in $HOSTS; do + case "$host" in + *darwin*) required_KiB=440000 ;; + *mingw*) required_KiB=7600000 ;; + *) required_KiB=6400000 ;; + esac + total_required_KiB=$((total_required_KiB+required_KiB)) +done + +if (( total_required_KiB > avail_KiB )); then + total_required_GiB=$((total_required_KiB / 1048576)) + avail_GiB=$((avail_KiB / 1048576)) + echo "Performing a BitcoinZ Guix build for the selected HOSTS requires ${total_required_GiB} GiB, however, only ${avail_GiB} GiB is available. Please free up some disk space before performing the build." + exit 1 +fi + +################ +# Check that we can connect to the guix-daemon +################ + +cat << EOF +Checking that we can connect to the guix-daemon... + +Hint: If this hangs, you may want to try turning your guix-daemon off and on + again. + +EOF +if ! guix gc --list-failures > /dev/null; then +cat << EOF + +ERR: Failed to connect to the guix-daemon, please ensure that one is running and + reachable. +EOF +exit 1 +fi + +# Developer note: we could use `guix repl` for this check and run: +# +# (import (guix store)) (close-connection (open-connection)) +# +# However, the internal API is likely to change more than the CLI invocation + +################ +# Services database must have basic entries +################ + +if ! getent services http https ftp > /dev/null 2>&1; then +cat << EOF +ERR: Your system's C library cannot find service database entries for at least + one of the following services: http, https, ftp. + +Hint: Most likely, /etc/services does not exist yet (common for docker images + and minimal distros), or you don't have permissions to access it. + + If /etc/services does not exist yet, you may want to install the + appropriate package for your distro which provides it. + + On Debian/Ubuntu: netbase + On Arch Linux: iana-etc + + For more information, see: getent(1), services(5) + +EOF + +fi + +######### +# SETUP # +######### + +# Determine the maximum number of jobs to run simultaneously (overridable by +# environment) +JOBS="${JOBS:-$(nproc)}" + +# Usage: host_to_commonname HOST +# +# HOST: The current platform triple we're building for +# +host_to_commonname() { + case "$1" in + *darwin*) echo osx ;; + *mingw*) echo win ;; + *linux*) echo linux ;; + *) exit 1 ;; + esac +} + +# Determine the reference time used for determinism (overridable by environment) +SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git -c log.showSignature=false log --format=%at -1)}" + +# Precious directories are those which should not be cleaned between successive +# guix builds +depends_precious_dir_names='SOURCES_PATH BASE_CACHE SDK_PATH' +precious_dir_names="${depends_precious_dir_names} OUTDIR_BASE PROFILES_BASE" + +# Usage: contains IFS-SEPARATED-LIST ITEM +contains() { + for i in ${1}; do + if [ "$i" = "${2}" ]; then + return 0 # Found! + fi + done + return 1 +} + +# If the user explicitly specified a precious directory, create it so we +# can map it into the container +for precious_dir_name in $precious_dir_names; do + precious_dir_path="${!precious_dir_name}" + if [ -n "$precious_dir_path" ]; then + if [ ! -e "$precious_dir_path" ]; then + mkdir -p "$precious_dir_path" + elif [ -L "$precious_dir_path" ]; then + echo "ERR: ${precious_dir_name} cannot be a symbolic link" + exit 1 + elif [ ! -d "$precious_dir_path" ]; then + echo "ERR: ${precious_dir_name} must be a directory" + exit 1 + fi + fi +done + +mkdir -p "$VAR_BASE" + +# Record the _effective_ values of precious directories such that guix-clean can +# avoid clobbering them if appropriate. +# +# shellcheck disable=SC2046,SC2086 +{ + # Get depends precious dir definitions from depends + make -C "${PWD}/depends" \ + --no-print-directory \ + -- $(printf "print-%s\n" $depends_precious_dir_names) + + # Get remaining precious dir definitions from the environment + for precious_dir_name in $precious_dir_names; do + precious_dir_path="${!precious_dir_name}" + if ! contains "$depends_precious_dir_names" "$precious_dir_name"; then + echo "${precious_dir_name}=${precious_dir_path}" + fi + done +} > "${VAR_BASE}/precious_dirs" + +# Make sure an output directory exists for our builds +OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}" +mkdir -p "$OUTDIR_BASE" + +# Download the depends sources now as we won't have internet access in the build +# container +for host in $HOSTS; do + make -C "${PWD}/depends" -j"$JOBS" download-"$(host_to_commonname "$host")" WITH_GUIX=1 ${V:+V=1} ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} +done + +# Usage: outdir_for_host HOST SUFFIX +# +# HOST: The current platform triple we're building for +# +outdir_for_host() { + echo "${OUTDIR_BASE}/${1}${2:+-${2}}" +} + +# Usage: profiledir_for_host HOST SUFFIX +# +# HOST: The current platform triple we're building for +# +profiledir_for_host() { + echo "${PROFILES_BASE}/${1}${2:+-${2}}" +} + + +######### +# BUILD # +######### + +# Function to be called when building for host ${1} and the user interrupts the +# build +int_trap() { +cat << EOF +** INT received while building ${1}, you may want to clean up the relevant + work directories (e.g. distsrc-*) before rebuilding + +Hint: To blow everything away, you may want to use: + + $ ./contrib/guix/guix-clean + +Specifically, this will remove all files without an entry in the index, +excluding the SDK directory, the depends download cache, the depends built +packages cache, the garbage collector roots for Guix environments, and the +output directory. +EOF +} + +# Deterministically build BitcoinZ +# shellcheck disable=SC2153 +for host in $HOSTS; do + + # Display proper warning when the user interrupts the build + trap 'int_trap ${host}' INT + + ( + # Required for 'contrib/guix/manifest.scm' to output the right manifest + # for the particular $HOST we're building for + export HOST="$host" + + # shellcheck disable=SC2030 +cat << EOF +INFO: Building ${VERSION:?not set} for platform triple ${HOST:?not set}: + ...using reference timestamp: ${SOURCE_DATE_EPOCH:?not set} + ...running at most ${JOBS:?not set} jobs + ...from worktree directory: '${PWD}' + ...bind-mounted in container to: '/bitcoinz' + ...in build directory: '$(distsrc_for_host "$HOST")' + ...bind-mounted in container to: '$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")' + ...outputting in: '$(outdir_for_host "$HOST")' + ...bind-mounted in container to: '$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST")' + ADDITIONAL FLAGS (if set) + ADDITIONAL_GUIX_COMMON_FLAGS: ${ADDITIONAL_GUIX_COMMON_FLAGS} + ADDITIONAL_GUIX_ENVIRONMENT_FLAGS: ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} + ADDITIONAL_GUIX_TIMEMACHINE_FLAGS: ${ADDITIONAL_GUIX_TIMEMACHINE_FLAGS} +EOF + + # Run the build script 'contrib/guix/libexec/build.sh' in the build + # container specified by 'contrib/guix/manifest.scm'. + # + # Explanation of `guix shell` flags: + # + # --container run command within an isolated container + # + # Running in an isolated container minimizes build-time differences + # between machines and improves reproducibility + # + # --pure unset existing environment variables + # + # Same rationale as --container + # + # --no-cwd do not share current working directory with an + # isolated container + # + # When --container is specified, the default behavior is to share + # the current working directory with the isolated container at the + # same exact path (e.g. mapping '/home/satoshi/bitcoinz/' to + # '/home/satoshi/bitcoinz/'). This means that the $PWD inside the + # container becomes a source of irreproducibility. --no-cwd disables + # this behaviour. + # + # --share=SPEC for containers, share writable host file system + # according to SPEC + # + # --share="$PWD"=/bitcoinz + # + # maps our current working directory to /bitcoinz + # inside the isolated container, which we later cd + # into. + # + # While we don't want to map our current working directory to the + # same exact path (as this introduces irreproducibility), we do want + # it to be at a _fixed_ path _somewhere_ inside the isolated + # container so that we have something to build. '/bitcoinz' was + # chosen arbitrarily. + # + # ${SOURCES_PATH:+--share="$SOURCES_PATH"} + # + # make the downloaded depends sources path available + # inside the isolated container + # + # The isolated container has no network access as it's in a + # different network namespace from the main machine, so we have to + # make the downloaded depends sources available to it. The sources + # should have been downloaded prior to this invocation. + # + # --keep-failed keep build tree of failed builds + # + # When builds of the Guix environment itself (not BitcoinZ) + # fail, it is useful for the build tree to be kept for debugging + # purposes. + # + # ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} + # + # fetch substitute from SUBSTITUTE_URLS if they are + # authorized + # + # Depending on the user's security model, it may be desirable to use + # substitutes (pre-built packages) from servers that the user trusts. + # Please read the README.md in the same directory as this file for + # more information. + # + # shellcheck disable=SC2086,SC2031 + time-machine shell --manifest="${PWD}/contrib/guix/manifest.scm" \ + --container \ + --network \ + --pure \ + --no-cwd \ + --share="$PWD"=/bitcoinz \ + --share="$DISTSRC_BASE"=/distsrc-base \ + --share="$OUTDIR_BASE"=/outdir-base \ + --expose="$(git rev-parse --git-common-dir)" \ + ${SOURCES_PATH:+--share="$SOURCES_PATH"} \ + ${BASE_CACHE:+--share="$BASE_CACHE"} \ + ${SDK_PATH:+--share="$SDK_PATH"} \ + --cores="$JOBS" \ + --keep-failed \ + --fallback \ + --link-profile \ + --root="$(profiledir_for_host "${HOST}")" \ + ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \ + ${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \ + -- env HOST="$host" \ + DISTNAME="$DISTNAME" \ + JOBS="$JOBS" \ + SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \ + ${V:+V=1} \ + ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \ + ${BASE_CACHE:+BASE_CACHE="$BASE_CACHE"} \ + ${SDK_PATH:+SDK_PATH="$SDK_PATH"} \ + DISTSRC="$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")" \ + OUTDIR="$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST")" \ + DIST_ARCHIVE_BASE=/outdir-base/dist-archive \ + bash -c "cd /bitcoinz && bash contrib/guix/libexec/build.sh" + ) + +done diff --git a/contrib/guix/guix-clean b/contrib/guix/guix-clean new file mode 100755 index 000000000..8e33fa247 --- /dev/null +++ b/contrib/guix/guix-clean @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +export LC_ALL=C +set -e -o pipefail + +# Source the common prelude, which: +# 1. Checks if we're at the top directory of the BitcoinZ repository +# 2. Defines a few common functions and variables +# +# shellcheck source=libexec/prelude.bash +source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash" + + +################### +## Sanity Checks ## +################### + +################ +# Required non-builtin commands should be invokable +################ + +check_tools cat mkdir make git guix + + +############# +## Clean ## +############# + +# Usage: under_dir MAYBE_PARENT MAYBE_CHILD +# +# If MAYBE_CHILD is a subdirectory of MAYBE_PARENT, print the relative path +# from MAYBE_PARENT to MAYBE_CHILD. Otherwise, return 1 as the error code. +# +# NOTE: This does not perform any symlink-resolving or path canonicalization. +# +under_dir() { + local path_residue + path_residue="${2##"${1}"}" + if [ -z "$path_residue" ] || [ "$path_residue" = "$2" ]; then + return 1 + else + echo "$path_residue" + fi +} + +# Usage: dir_under_git_root MAYBE_CHILD +# +# If MAYBE_CHILD is under the current git repository and exists, print the +# relative path from the git repository's top-level directory to MAYBE_CHILD, +# otherwise, exit with an error code. +# +dir_under_git_root() { + local rv + rv="$(under_dir "$(git_root)" "$1")" + [ -n "$rv" ] && echo "$rv" +} + +shopt -s nullglob +found_precious_dirs_files=( "${version_base_prefix}"*/"${var_base_basename}/precious_dirs" ) # This expands to an array of directories... +shopt -u nullglob + +exclude_flags=() + +for precious_dirs_file in "${found_precious_dirs_files[@]}"; do + # Make sure the precious directories (e.g. SOURCES_PATH, BASE_CACHE, SDK_PATH) + # are excluded from git-clean + echo "Found precious_dirs file: '${precious_dirs_file}'" + + # Exclude the precious_dirs file itself + if dirs_file_exclude_fragment=$(dir_under_git_root "$(dirname "$precious_dirs_file")"); then + exclude_flags+=( --exclude="${dirs_file_exclude_fragment}/precious_dirs" ) + fi + + # Read each 'name=dir' pair from the precious_dirs file + while IFS='=' read -r name dir; do + # Add an exclusion flag if the precious directory is under the git root. + if under=$(dir_under_git_root "$dir"); then + echo "Avoiding ${name}: ${under}" + exclude_flags+=( --exclude="$under" ) + fi + done < "$precious_dirs_file" +done + +git clean -xdff "${exclude_flags[@]}" diff --git a/contrib/guix/guix-codesign b/contrib/guix/guix-codesign new file mode 100755 index 000000000..cb1278ed8 --- /dev/null +++ b/contrib/guix/guix-codesign @@ -0,0 +1,378 @@ +#!/usr/bin/env bash +export LC_ALL=C +set -e -o pipefail + +# Source the common prelude, which: +# 1. Checks if we're at the top directory of the BitcoinZ repository +# 2. Defines a few common functions and variables +# +# shellcheck source=libexec/prelude.bash +source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash" + + +################### +## SANITY CHECKS ## +################### + +################ +# Required non-builtin commands should be invocable +################ + +check_tools cat mkdir git guix + +################ +# Required env vars should be non-empty +################ + +cmd_usage() { + cat < \\ + ./contrib/guix/guix-codesign + +EOF +} + +if [ -z "$DETACHED_SIGS_REPO" ]; then + cmd_usage + exit 1 +fi + +################ +# GUIX_BUILD_OPTIONS should be empty +################ +# +# GUIX_BUILD_OPTIONS is an environment variable recognized by guix commands that +# can perform builds. This seems like what we want instead of +# ADDITIONAL_GUIX_COMMON_FLAGS, but the value of GUIX_BUILD_OPTIONS is actually +# _appended_ to normal command-line options. Meaning that they will take +# precedence over the command-specific ADDITIONAL_GUIX__FLAGS. +# +# This seems like a poor user experience. Thus we check for GUIX_BUILD_OPTIONS's +# existence here and direct users of this script to use our (more flexible) +# custom environment variables. +if [ -n "$GUIX_BUILD_OPTIONS" ]; then +cat << EOF +Error: Environment variable GUIX_BUILD_OPTIONS is not empty: + '$GUIX_BUILD_OPTIONS' + +Unfortunately this script is incompatible with GUIX_BUILD_OPTIONS, please unset +GUIX_BUILD_OPTIONS and use ADDITIONAL_GUIX_COMMON_FLAGS to set build options +across guix commands or ADDITIONAL_GUIX__FLAGS to set build options for a +specific guix command. + +See contrib/guix/README.md for more details. +EOF +exit 1 +fi + +################ +# The codesignature git worktree should not be dirty +################ + +if ! git -C "$DETACHED_SIGS_REPO" diff-index --quiet HEAD -- && [ -z "$FORCE_DIRTY_WORKTREE" ]; then + cat << EOF +ERR: The DETACHED CODESIGNATURE git worktree is dirty, which may lead to broken builds. + + Aborting... + +Hint: To make your git worktree clean, You may want to: + 1. Commit your changes, + 2. Stash your changes, or + 3. Set the 'FORCE_DIRTY_WORKTREE' environment variable if you insist on + using a dirty worktree +EOF + exit 1 +fi + +################ +# Build directories should not exist +################ + +# Default to building for all supported HOSTs (overridable by environment) +export HOSTS="${HOSTS:-x86_64-w64-mingw32 x86_64-apple-darwin}" + +# Usage: distsrc_for_host HOST +# +# HOST: The current platform triple we're building for +# +distsrc_for_host() { + echo "${DISTSRC_BASE}/distsrc-${VERSION}-${1}-codesigned" +} + +# Accumulate a list of build directories that already exist... +hosts_distsrc_exists="" +for host in $HOSTS; do + if [ -e "$(distsrc_for_host "$host")" ]; then + hosts_distsrc_exists+=" ${host}" + fi +done + +if [ -n "$hosts_distsrc_exists" ]; then +# ...so that we can print them out nicely in an error message +cat << EOF +ERR: Build directories for this commit already exist for the following platform + triples you're attempting to build, probably because of previous builds. + Please remove, or otherwise deal with them prior to starting another build. + + Aborting... + +Hint: To blow everything away, you may want to use: + + $ ./contrib/guix/guix-clean + +Specifically, this will remove all files without an entry in the index, +excluding the SDK directory, the depends download cache, the depends built +packages cache, the garbage collector roots for Guix environments, and the +output directory. +EOF +for host in $hosts_distsrc_exists; do + echo " ${host} '$(distsrc_for_host "$host")'" +done +exit 1 +else + mkdir -p "$DISTSRC_BASE" +fi + + +################ +# Unsigned tarballs SHOULD exist +################ + +# Usage: outdir_for_host HOST SUFFIX +# +# HOST: The current platform triple we're building for +# +outdir_for_host() { + echo "${OUTDIR_BASE}/${1}${2:+-${2}}" +} + + +unsigned_tarball_for_host() { + case "$1" in + *mingw*) + echo "$(outdir_for_host "$1")/${DISTNAME}-win64-unsigned.tar.gz" + ;; + *darwin*) + echo "$(outdir_for_host "$1")/${DISTNAME}-${1}-unsigned.tar.gz" + ;; + *) + exit 1 + ;; + esac +} + +# Accumulate a list of build directories that already exist... +hosts_unsigned_tarball_missing="" +for host in $HOSTS; do + if [ ! -e "$(unsigned_tarball_for_host "$host")" ]; then + hosts_unsigned_tarball_missing+=" ${host}" + fi +done + +if [ -n "$hosts_unsigned_tarball_missing" ]; then + # ...so that we can print them out nicely in an error message + cat << EOF +ERR: Unsigned tarballs do not exist +... + +EOF +for host in $hosts_unsigned_tarball_missing; do + echo " ${host} '$(unsigned_tarball_for_host "$host")'" +done +exit 1 +fi + +################ +# Check that we can connect to the guix-daemon +################ + +cat << EOF +Checking that we can connect to the guix-daemon... + +Hint: If this hangs, you may want to try turning your guix-daemon off and on + again. + +EOF +if ! guix gc --list-failures > /dev/null; then + cat << EOF + +ERR: Failed to connect to the guix-daemon, please ensure that one is running and + reachable. +EOF + exit 1 +fi + +# Developer note: we could use `guix repl` for this check and run: +# +# (import (guix store)) (close-connection (open-connection)) +# +# However, the internal API is likely to change more than the CLI invocation + + +######### +# SETUP # +######### + +# Determine the maximum number of jobs to run simultaneously (overridable by +# environment) +JOBS="${JOBS:-$(nproc)}" + +# Determine the reference time used for determinism (overridable by environment) +SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git -c log.showSignature=false log --format=%at -1)}" + +# Make sure an output directory exists for our builds +OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}" +mkdir -p "$OUTDIR_BASE" + +# Usage: profiledir_for_host HOST SUFFIX +# +# HOST: The current platform triple we're building for +# +profiledir_for_host() { + echo "${PROFILES_BASE}/${1}${2:+-${2}}" +} + +######### +# BUILD # +######### + +# Function to be called when codesigning for host ${1} and the user interrupts +# the codesign +int_trap() { +cat << EOF +** INT received while codesigning ${1}, you may want to clean up the relevant + work directories (e.g. distsrc-*) before recodesigning + +Hint: To blow everything away, you may want to use: + + $ ./contrib/guix/guix-clean + +Specifically, this will remove all files without an entry in the index, +excluding the SDK directory, the depends download cache, the depends built +packages cache, the garbage collector roots for Guix environments, and the +output directory. +EOF +} + +# Deterministically build BitcoinZ +# shellcheck disable=SC2153 +for host in $HOSTS; do + + # Display proper warning when the user interrupts the build + trap 'int_trap ${host}' INT + + ( + # Required for 'contrib/guix/manifest.scm' to output the right manifest + # for the particular $HOST we're building for + export HOST="$host" + + # shellcheck disable=SC2030 +cat << EOF +INFO: Codesigning ${VERSION:?not set} for platform triple ${HOST:?not set}: + ...using reference timestamp: ${SOURCE_DATE_EPOCH:?not set} + ...from worktree directory: '${PWD}' + ...bind-mounted in container to: '/bitcoinz' + ...in build directory: '$(distsrc_for_host "$HOST")' + ...bind-mounted in container to: '$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")' + ...outputting in: '$(outdir_for_host "$HOST" codesigned)' + ...bind-mounted in container to: '$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST" codesigned)' + ...using detached signatures in: '${DETACHED_SIGS_REPO:?not set}' + ...bind-mounted in container to: '/detached-sigs' +EOF + + + # Run the build script 'contrib/guix/libexec/build.sh' in the build + # container specified by 'contrib/guix/manifest.scm'. + # + # Explanation of `guix shell` flags: + # + # --container run command within an isolated container + # + # Running in an isolated container minimizes build-time differences + # between machines and improves reproducibility + # + # --pure unset existing environment variables + # + # Same rationale as --container + # + # --no-cwd do not share current working directory with an + # isolated container + # + # When --container is specified, the default behavior is to share + # the current working directory with the isolated container at the + # same exact path (e.g. mapping '/home/satoshi/bitcoinz/' to + # '/home/satoshi/bitcoinz/'). This means that the $PWD inside the + # container becomes a source of irreproducibility. --no-cwd disables + # this behaviour. + # + # --share=SPEC for containers, share writable host file system + # according to SPEC + # + # --share="$PWD"=/bitcoinz + # + # maps our current working directory to /bitcoinz + # inside the isolated container, which we later cd + # into. + # + # While we don't want to map our current working directory to the + # same exact path (as this introduces irreproducibility), we do want + # it to be at a _fixed_ path _somewhere_ inside the isolated + # container so that we have something to build. '/bitcoinz' was + # chosen arbitrarily. + # + # ${SOURCES_PATH:+--share="$SOURCES_PATH"} + # + # make the downloaded depends sources path available + # inside the isolated container + # + # The isolated container has no network access as it's in a + # different network namespace from the main machine, so we have to + # make the downloaded depends sources available to it. The sources + # should have been downloaded prior to this invocation. + # + # ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} + # + # fetch substitute from SUBSTITUTE_URLS if they are + # authorized + # + # Depending on the user's security model, it may be desirable to use + # substitutes (pre-built packages) from servers that the user trusts. + # Please read the README.md in the same directory as this file for + # more information. + # + # shellcheck disable=SC2086,SC2031 + time-machine shell --manifest="${PWD}/contrib/guix/manifest.scm" \ + --container \ + --pure \ + --no-cwd \ + --share="$PWD"=/bitcoinz \ + --share="$DISTSRC_BASE"=/distsrc-base \ + --share="$OUTDIR_BASE"=/outdir-base \ + --share="$DETACHED_SIGS_REPO"=/detached-sigs \ + --expose="$(git rev-parse --git-common-dir)" \ + --expose="$(git -C "$DETACHED_SIGS_REPO" rev-parse --git-common-dir)" \ + ${SOURCES_PATH:+--share="$SOURCES_PATH"} \ + --cores="$JOBS" \ + --keep-failed \ + --fallback \ + --link-profile \ + --root="$(profiledir_for_host "${HOST}" codesigned)" \ + ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \ + ${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} \ + -- env HOST="$host" \ + DISTNAME="$DISTNAME" \ + JOBS="$JOBS" \ + SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \ + ${V:+V=1} \ + ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \ + DISTSRC="$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")" \ + OUTDIR="$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST" codesigned)" \ + DIST_ARCHIVE_BASE=/outdir-base/dist-archive \ + DETACHED_SIGS_REPO=/detached-sigs \ + UNSIGNED_TARBALL="$(OUTDIR_BASE=/outdir-base && unsigned_tarball_for_host "$HOST")" \ + bash -c "cd /bitcoinz && bash contrib/guix/libexec/codesign.sh" + ) + +done diff --git a/contrib/guix/guix-verify b/contrib/guix/guix-verify new file mode 100755 index 000000000..8f6acdb26 --- /dev/null +++ b/contrib/guix/guix-verify @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +export LC_ALL=C +set -e -o pipefail + +# Source the common prelude, which: +# 1. Checks if we're at the top directory of the BitcoinZ repository +# 2. Defines a few common functions and variables +# +# shellcheck source=libexec/prelude.bash +source "$(dirname "${BASH_SOURCE[0]}")/libexec/prelude.bash" + + +################### +## Sanity Checks ## +################### + +################ +# Required non-builtin commands should be invokable +################ + +check_tools cat diff gpg + +################ +# Required env vars should be non-empty +################ + +cmd_usage() { +cat < [ SIGNER= ] ./contrib/guix/guix-verify + +Example overriding signer's manifest to use as base + + env GUIX_SIGS_REPO=/home/dongcarl/guix.sigs SIGNER=achow101 ./contrib/guix/guix-verify + +EOF +} + +if [ -z "$GUIX_SIGS_REPO" ]; then + cmd_usage + exit 1 +fi + +################ +# GUIX_SIGS_REPO should exist as a directory +################ + +if [ ! -d "$GUIX_SIGS_REPO" ]; then +cat << EOF +ERR: The specified GUIX_SIGS_REPO is not an existent directory: + + '$GUIX_SIGS_REPO' + +Hint: Please clone the guix.sigs repository and point to it with the + GUIX_SIGS_REPO environment variable. + +EOF +cmd_usage +exit 1 +fi + +############## +## Verify ## +############## + +OUTSIGDIR_BASE="${GUIX_SIGS_REPO}/${VERSION}" +echo "Looking for signature directories in '${OUTSIGDIR_BASE}'" +echo "" + +# Usage: verify compare_manifest current_manifest +verify() { + local compare_manifest="$1" + local current_manifest="$2" + if ! gpg --quiet --batch --verify "$current_manifest".asc "$current_manifest" 1>&2; then + echo "ERR: Failed to verify GPG signature in '${current_manifest}'" + echo "" + echo "Hint: Either the signature is invalid or the public key is missing" + echo "" + failure=1 + elif ! diff --report-identical "$compare_manifest" "$current_manifest" 1>&2; then + echo "ERR: The SHA256SUMS attestation in these two directories differ:" + echo " '${compare_manifest}'" + echo " '${current_manifest}'" + echo "" + failure=1 + else + echo "Verified: '${current_manifest}'" + echo "" + fi +} + +shopt -s nullglob +all_noncodesigned=( "$OUTSIGDIR_BASE"/*/noncodesigned.SHA256SUMS ) +shopt -u nullglob + +echo "--------------------" +echo "" +if (( ${#all_noncodesigned[@]} )); then + compare_noncodesigned="${all_noncodesigned[0]}" + if [[ -n "$SIGNER" ]]; then + signer_noncodesigned="$OUTSIGDIR_BASE/$SIGNER/noncodesigned.SHA256SUMS" + if [[ -f "$signer_noncodesigned" ]]; then + echo "Using $SIGNER's manifest as the base to compare against" + compare_noncodesigned="$signer_noncodesigned" + else + echo "Unable to find $SIGNER's manifest, using the first one found" + fi + else + echo "No SIGNER provided, using the first manifest found" + fi + + for current_manifest in "${all_noncodesigned[@]}"; do + verify "$compare_noncodesigned" "$current_manifest" + done + + echo "DONE: Checking output signatures for noncodesigned.SHA256SUMS" + echo "" +else + echo "WARN: No signature directories with noncodesigned.SHA256SUMS found" + echo "" +fi + +shopt -s nullglob +all_all=( "$OUTSIGDIR_BASE"/*/all.SHA256SUMS ) +shopt -u nullglob + +echo "--------------------" +echo "" +if (( ${#all_all[@]} )); then + compare_all="${all_all[0]}" + if [[ -n "$SIGNER" ]]; then + signer_all="$OUTSIGDIR_BASE/$SIGNER/all.SHA256SUMS" + if [[ -f "$signer_all" ]]; then + echo "Using $SIGNER's manifest as the base to compare against" + compare_all="$signer_all" + else + echo "Unable to find $SIGNER's manifest, using the first one found" + fi + else + echo "No SIGNER provided, using the first manifest found" + fi + + for current_manifest in "${all_all[@]}"; do + verify "$compare_all" "$current_manifest" + done + + # Sanity check: there should be no entries that exist in + # noncodesigned.SHA256SUMS that doesn't exist in all.SHA256SUMS + if [[ "$(comm -23 <(sort "$compare_noncodesigned") <(sort "$compare_all") | wc -c)" -ne 0 ]]; then + echo "ERR: There are unique lines in noncodesigned.SHA256SUMS which" + echo " do not exist in all.SHA256SUMS, something went very wrong." + exit 1 + fi + + echo "DONE: Checking output signatures for all.SHA256SUMS" + echo "" +else + echo "WARN: No signature directories with all.SHA256SUMS found" + echo "" +fi + +echo "====================" +echo "" +if (( ${#all_noncodesigned[@]} + ${#all_all[@]} == 0 )); then + echo "ERR: Unable to perform any verifications as no signature directories" + echo " were found" + echo "" + exit 1 +fi + +if [ -n "$failure" ]; then + exit 1 +fi diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh new file mode 100755 index 000000000..27b3585c3 --- /dev/null +++ b/contrib/guix/libexec/build.sh @@ -0,0 +1,409 @@ +#!/usr/bin/env bash +# Copyright (c) 2024 The BitcoinZ Community +# Copyright (c) 2019-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C +set -e -o pipefail +export TZ=UTC + +# Although Guix _does_ set umask when building its own packages (in our case, +# this is all packages in manifest.scm), it does not set it for `guix +# shell`. It does make sense for at least `guix shell --container` +# to set umask, so if that change gets merged upstream and we bump the +# time-machine to a commit which includes the aforementioned change, we can +# remove this line. +# +# This line should be placed before any commands which creates files. +umask 0022 + +if [ -n "$V" ]; then + # Print both unexpanded (-v) and expanded (-x) forms of commands as they are + # read from this file. + set -vx + # Set VERBOSE for CMake-based builds + export VERBOSE="$V" +fi + +# Check that required environment variables are set +cat << EOF +Required environment variables as seen inside the container: + DIST_ARCHIVE_BASE: ${DIST_ARCHIVE_BASE:?not set} + DISTNAME: ${DISTNAME:?not set} + HOST: ${HOST:?not set} + SOURCE_DATE_EPOCH: ${SOURCE_DATE_EPOCH:?not set} + JOBS: ${JOBS:?not set} + DISTSRC: ${DISTSRC:?not set} + OUTDIR: ${OUTDIR:?not set} +EOF + +ACTUAL_OUTDIR="${OUTDIR}" +OUTDIR="${DISTSRC}/output" + +##################### +# Environment Setup # +##################### + +# The depends folder also serves as a base-prefix for depends packages for +# $HOSTs after successfully building. +BASEPREFIX="${PWD}/depends" + +# Given a package name and an output name, return the path of that output in our +# current guix environment +store_path() { + grep --extended-regexp "/[^-]{32}-${1}-[^-]+${2:+-${2}}" "${GUIX_ENVIRONMENT}/manifest" \ + | head --lines=1 \ + | sed --expression='s|\x29*$||' \ + --expression='s|^[[:space:]]*"||' \ + --expression='s|"[[:space:]]*$||' +} + + +# Set environment variables to point the NATIVE toolchain to the right +# includes/libs +NATIVE_GCC="$(store_path gcc-toolchain)" + +unset LIBRARY_PATH +unset CPATH +unset C_INCLUDE_PATH +unset CPLUS_INCLUDE_PATH +unset OBJC_INCLUDE_PATH +unset OBJCPLUS_INCLUDE_PATH + +export C_INCLUDE_PATH="${NATIVE_GCC}/include" +export CPLUS_INCLUDE_PATH="${NATIVE_GCC}/include/c++:${NATIVE_GCC}/include" + +case "$HOST" in + *darwin*) export LIBRARY_PATH="${NATIVE_GCC}/lib" ;; # Required for qt/qmake + *mingw*) export LIBRARY_PATH="${NATIVE_GCC}/lib" ;; + *) + NATIVE_GCC_STATIC="$(store_path gcc-toolchain static)" + export LIBRARY_PATH="${NATIVE_GCC}/lib:${NATIVE_GCC_STATIC}/lib" + ;; +esac + +# Set environment variables to point the CROSS toolchain to the right +# includes/libs for $HOST +case "$HOST" in + *mingw*) + # Determine output paths to use in CROSS_* environment variables + CROSS_GLIBC="$(store_path "mingw-w64-x86_64-winpthreads")" + CROSS_GCC="$(store_path "gcc-cross-${HOST}")" + CROSS_GCC_LIB_STORE="$(store_path "gcc-cross-${HOST}" lib)" + CROSS_GCC_LIBS=( "${CROSS_GCC_LIB_STORE}/lib/gcc/${HOST}"/* ) # This expands to an array of directories... + CROSS_GCC_LIB="${CROSS_GCC_LIBS[0]}" # ...we just want the first one (there should only be one) + + # The search path ordering is generally: + # 1. gcc-related search paths + # 2. libc-related search paths + # 2. kernel-header-related search paths (not applicable to mingw-w64 hosts) + export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GCC_LIB}/include-fixed:${CROSS_GLIBC}/include" + export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GCC}/include/c++/${HOST}:${CROSS_GCC}/include/c++/backward:${CROSS_C_INCLUDE_PATH}" + export CROSS_LIBRARY_PATH="${CROSS_GCC_LIB_STORE}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib" + ;; + *darwin*) + # The CROSS toolchain for darwin uses the SDK and ignores environment variables. + # See depends/hosts/darwin.mk for more details. + ;; + *linux*) + CROSS_GLIBC="$(store_path "glibc-cross-${HOST}")" + CROSS_GLIBC_STATIC="$(store_path "glibc-cross-${HOST}" static)" + CROSS_KERNEL="$(store_path "linux-libre-headers-cross-${HOST}")" + CROSS_GCC="$(store_path "gcc-cross-${HOST}")" + CROSS_GCC_LIB_STORE="$(store_path "gcc-cross-${HOST}" lib)" + CROSS_GCC_LIBS=( "${CROSS_GCC_LIB_STORE}/lib/gcc/${HOST}"/* ) # This expands to an array of directories... + CROSS_GCC_LIB="${CROSS_GCC_LIBS[0]}" # ...we just want the first one (there should only be one) + + export CROSS_C_INCLUDE_PATH="${CROSS_GCC_LIB}/include:${CROSS_GCC_LIB}/include-fixed:${CROSS_GLIBC}/include:${CROSS_KERNEL}/include" + export CROSS_CPLUS_INCLUDE_PATH="${CROSS_GCC}/include/c++:${CROSS_GCC}/include/c++/${HOST}:${CROSS_GCC}/include/c++/backward:${CROSS_C_INCLUDE_PATH}" + export CROSS_LIBRARY_PATH="${CROSS_GCC_LIB_STORE}/lib:${CROSS_GCC_LIB}:${CROSS_GLIBC}/lib:${CROSS_GLIBC_STATIC}/lib" + ;; + *) + exit 1 ;; +esac + +# Sanity check CROSS_*_PATH directories +IFS=':' read -ra PATHS <<< "${CROSS_C_INCLUDE_PATH}:${CROSS_CPLUS_INCLUDE_PATH}:${CROSS_LIBRARY_PATH}" +for p in "${PATHS[@]}"; do + if [ -n "$p" ] && [ ! -d "$p" ]; then + echo "'$p' doesn't exist or isn't a directory... Aborting..." + exit 1 + fi +done + +# Disable Guix ld auto-rpath behavior +export GUIX_LD_WRAPPER_DISABLE_RPATH=yes + +# Make /usr/bin if it doesn't exist +[ -e /usr/bin ] || mkdir -p /usr/bin + +# Symlink file and env to a conventional path +[ -e /usr/bin/file ] || ln -s --no-dereference "$(command -v file)" /usr/bin/file +[ -e /usr/bin/env ] || ln -s --no-dereference "$(command -v env)" /usr/bin/env + +# Determine the correct value for -Wl,--dynamic-linker for the current $HOST +case "$HOST" in + *linux*) + glibc_dynamic_linker=$( + case "$HOST" in + x86_64-linux-gnu) echo /lib64/ld-linux-x86-64.so.2 ;; + aarch64-linux-gnu) echo /lib/ld-linux-aarch64.so.1 ;; + riscv64-linux-gnu) echo /lib/ld-linux-riscv64-lp64d.so.1 ;; + powerpc64-linux-gnu) echo /lib64/ld64.so.1;; + powerpc64le-linux-gnu) echo /lib64/ld64.so.2;; + *) exit 1 ;; + esac + ) + ;; +esac + +# Environment variables for determinism +export TAR_OPTIONS="--owner=0 --group=0 --numeric-owner --mtime='@${SOURCE_DATE_EPOCH}' --sort=name" +export TZ="UTC" + +#################### +# Depends Building # +#################### + +# Build the depends tree, overriding variables that assume multilib gcc +export LD_LIBRARY_PATH=$GUIX_ENVIRONMENT/lib:$LIBRARY_PATH +make -C depends --jobs="$JOBS" HOST="$HOST" \ + WITH_GUIX=1 \ + ${V:+V=1} \ + ${SOURCES_PATH+SOURCES_PATH="$SOURCES_PATH"} \ + ${BASE_CACHE+BASE_CACHE="$BASE_CACHE"} \ + ${SDK_PATH+SDK_PATH="$SDK_PATH"} \ + x86_64_linux_CC=x86_64-linux-gnu-gcc \ + x86_64_linux_CXX=x86_64-linux-gnu-g++ \ + x86_64_linux_AR=x86_64-linux-gnu-gcc-ar \ + x86_64_linux_RANLIB=x86_64-linux-gnu-gcc-ranlib \ + x86_64_linux_NM=x86_64-linux-gnu-gcc-nm \ + x86_64_linux_STRIP=x86_64-linux-gnu-strip + +case "$HOST" in + *darwin*) + # Unset now that Qt is built + unset C_INCLUDE_PATH + unset CPLUS_INCLUDE_PATH + unset LIBRARY_PATH + ;; +esac + +########################### +# Source Tarball Building # +########################### + +GIT_ARCHIVE="${DIST_ARCHIVE_BASE}/${DISTNAME}.tar.gz" + +# Create the source tarball if not already there +if [ ! -e "$GIT_ARCHIVE" ]; then + mkdir -p "$(dirname "$GIT_ARCHIVE")" + git archive --prefix="${DISTNAME}/" --output="$GIT_ARCHIVE" HEAD +fi + +mkdir -p "$OUTDIR" + +########################### +# Binary Tarball Building # +########################### + +# CONFIGFLAGS +CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests --disable-fuzz-binary" + +# CFLAGS +HOST_CFLAGS="-O2 -g" +HOST_CFLAGS+=$(find /gnu/store -maxdepth 1 -mindepth 1 -type d -exec echo -n " -ffile-prefix-map={}=/usr" \;) +case "$HOST" in + *linux*) HOST_CFLAGS+=" -ffile-prefix-map=${PWD}=." ;; + *mingw*) HOST_CFLAGS+=" -fno-ident" ;; + *darwin*) unset HOST_CFLAGS ;; +esac + +# CXXFLAGS +HOST_CXXFLAGS="$HOST_CFLAGS" + +# LDFLAGS +case "$HOST" in + *linux*) HOST_LDFLAGS="-Wl,--as-needed -Wl,--dynamic-linker=$glibc_dynamic_linker -static-libstdc++ -Wl,-O2" ;; + *mingw*) HOST_LDFLAGS="-Wl,--no-insert-timestamp" ;; +esac + +# Make $HOST-specific native binaries from depends available in $PATH +export PATH="${BASEPREFIX}/${HOST}/native/bin:${PATH}" +mkdir -p "$DISTSRC" +( + cd "$DISTSRC" + + # Extract the source tarball + tar --strip-components=1 -xf "${GIT_ARCHIVE}" + + ./autogen.sh + + # Configure this DISTSRC for $HOST + # shellcheck disable=SC2086 + env CONFIG_SITE="${BASEPREFIX}/${HOST}/share/config.site" \ + ./configure --prefix=/ \ + --disable-ccache \ + --disable-maintainer-mode \ + --disable-dependency-tracking \ + ${CONFIGFLAGS} \ + ${HOST_CFLAGS:+CFLAGS="${HOST_CFLAGS}"} \ + ${HOST_CXXFLAGS:+CXXFLAGS="${HOST_CXXFLAGS}"} \ + ${HOST_LDFLAGS:+LDFLAGS="${HOST_LDFLAGS}"} + + sed -i.old 's/-lstdc++ //g' config.status libtool + + # Build BitcoinZ + make --jobs="$JOBS" ${V:+V=1} + + # Check that symbol/security checks tools are sane. + make test-security-check ${V:+V=1} + # Perform basic security checks on a series of executables. + make -C src --jobs=1 check-security ${V:+V=1} + # Check that executables only contain allowed version symbols. + make -C src --jobs=1 check-symbols ${V:+V=1} + + mkdir -p "$OUTDIR" + + # Make the os-specific installers + case "$HOST" in + *mingw*) + make deploy ${V:+V=1} BITCOIN_WIN_INSTALLER="${OUTDIR}/${DISTNAME}-win64-setup-unsigned.exe" + ;; + esac + + # Setup the directory where our BitcoinZ build for HOST will be + # installed. This directory will also later serve as the input for our + # binary tarballs. + INSTALLPATH="${PWD}/installed/${DISTNAME}" + mkdir -p "${INSTALLPATH}" + # Install built BitcoinZ to $INSTALLPATH + case "$HOST" in + *darwin*) + make install-strip DESTDIR="${INSTALLPATH}" ${V:+V=1} + ;; + *) + make install DESTDIR="${INSTALLPATH}" ${V:+V=1} + ;; + esac + + case "$HOST" in + *darwin*) + make deploydir ${V:+V=1} + mkdir -p "unsigned-app-${HOST}" + cp --target-directory="unsigned-app-${HOST}" \ + contrib/macdeploy/detached-sig-create.sh + mv --target-directory="unsigned-app-${HOST}" dist + ( + cd "unsigned-app-${HOST}" + find . -print0 \ + | sort --zero-terminated \ + | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ + | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-unsigned.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-unsigned.tar.gz" && exit 1 ) + ) + make deploy ${V:+V=1} OSX_ZIP="${OUTDIR}/${DISTNAME}-${HOST}-unsigned.zip" + ;; + esac + ( + cd installed + + # Prune libtool and object archives + find . -name "lib*.la" -delete + find . -name "lib*.a" -delete + + case "$HOST" in + *darwin*) ;; + *) + # Split binaries from their debug symbols + { + find "${DISTNAME}/bin" -type f -executable -not -name "*.sh" -print0 + } | xargs -0 -P"$JOBS" -I{} "${DISTSRC}/contrib/devtools/split-debug.sh" {} {} {}.dbg + ;; + esac + + case "$HOST" in + *mingw*) + cp "${DISTSRC}/doc/README_windows.txt" "${DISTNAME}/readme.txt" + ;; + *linux*) + cp "${DISTSRC}/README.md" "${DISTNAME}/" + ;; + esac + + # copy over the example bitcoinz.conf file. if contrib/devtools/gen-bitcoinz-conf.sh + # has not been run before buildling, this file will be a stub + cp "${DISTSRC}/share/examples/bitcoinz.conf" "${DISTNAME}/" + + cp -r "${DISTSRC}/share/rpcauth" "${DISTNAME}/share/" + + # Finally, deterministically produce {non-,}debug binary tarballs ready + # for release + case "$HOST" in + *mingw*) + find "${DISTNAME}" -not -name "*.dbg" -print0 \ + | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" + find "${DISTNAME}" -not -name "*.dbg" \ + | sort \ + | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" && exit 1 ) + find "${DISTNAME}" -name "*.dbg" -print0 \ + | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" + find "${DISTNAME}" -name "*.dbg" \ + | sort \ + | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-debug.zip" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-debug.zip" && exit 1 ) + ;; + *linux*) + find "${DISTNAME}" -not -name "*.dbg" -print0 \ + | sort --zero-terminated \ + | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ + | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" && exit 1 ) + find "${DISTNAME}" -name "*.dbg" -print0 \ + | sort --zero-terminated \ + | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ + | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-debug.tar.gz" && exit 1 ) + ;; + *darwin*) + find "${DISTNAME}" -print0 \ + | sort --zero-terminated \ + | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ + | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" && exit 1 ) + ;; + esac + ) # $DISTSRC/installed + + case "$HOST" in + *mingw*) + cp -rf --target-directory=. contrib/windeploy + ( + cd ./windeploy + mkdir -p unsigned + cp --target-directory=unsigned/ "${OUTDIR}/${DISTNAME}-win64-setup-unsigned.exe" + find . -print0 \ + | sort --zero-terminated \ + | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ + | gzip -9n > "${OUTDIR}/${DISTNAME}-win64-unsigned.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-win64-unsigned.tar.gz" && exit 1 ) + ) + ;; + esac +) # $DISTSRC + +rm -rf "$ACTUAL_OUTDIR" +mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR" \ + || ( rm -rf "$ACTUAL_OUTDIR" && exit 1 ) + +( + cd /outdir-base + { + echo "$GIT_ARCHIVE" + find "$ACTUAL_OUTDIR" -type f + } | xargs realpath --relative-base="$PWD" \ + | xargs sha256sum \ + | sort -k2 \ + | sponge "$ACTUAL_OUTDIR"/SHA256SUMS.part +) diff --git a/contrib/guix/libexec/codesign.sh b/contrib/guix/libexec/codesign.sh new file mode 100755 index 000000000..9177a58f9 --- /dev/null +++ b/contrib/guix/libexec/codesign.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# Copyright (c) 2024 The BitcoinZ Community +# Copyright (c) 2021-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +export LC_ALL=C +set -e -o pipefail +export TZ=UTC + +# Although Guix _does_ set umask when building its own packages (in our case, +# this is all packages in manifest.scm), it does not set it for `guix +# shell`. It does make sense for at least `guix shell --container` +# to set umask, so if that change gets merged upstream and we bump the +# time-machine to a commit which includes the aforementioned change, we can +# remove this line. +# +# This line should be placed before any commands which creates files. +umask 0022 + +if [ -n "$V" ]; then + # Print both unexpanded (-v) and expanded (-x) forms of commands as they are + # read from this file. + set -vx + # Set VERBOSE for CMake-based builds + export VERBOSE="$V" +fi + +# Check that required environment variables are set +cat << EOF +Required environment variables as seen inside the container: + UNSIGNED_TARBALL: ${UNSIGNED_TARBALL:?not set} + DETACHED_SIGS_REPO: ${DETACHED_SIGS_REPO:?not set} + DIST_ARCHIVE_BASE: ${DIST_ARCHIVE_BASE:?not set} + DISTNAME: ${DISTNAME:?not set} + HOST: ${HOST:?not set} + SOURCE_DATE_EPOCH: ${SOURCE_DATE_EPOCH:?not set} + DISTSRC: ${DISTSRC:?not set} + OUTDIR: ${OUTDIR:?not set} +EOF + +ACTUAL_OUTDIR="${OUTDIR}" +OUTDIR="${DISTSRC}/output" + +git_head_version() { + local recent_tag + if recent_tag="$(git -C "$1" describe --exact-match HEAD 2> /dev/null)"; then + echo "${recent_tag#v}" + else + git -C "$1" rev-parse --short=12 HEAD + fi +} + +CODESIGNATURE_GIT_ARCHIVE="${DIST_ARCHIVE_BASE}/${DISTNAME}-codesignatures-$(git_head_version "$DETACHED_SIGS_REPO").tar.gz" + +# Create the codesignature tarball if not already there +if [ ! -e "$CODESIGNATURE_GIT_ARCHIVE" ]; then + mkdir -p "$(dirname "$CODESIGNATURE_GIT_ARCHIVE")" + git -C "$DETACHED_SIGS_REPO" archive --output="$CODESIGNATURE_GIT_ARCHIVE" HEAD +fi + +mkdir -p "$OUTDIR" + +mkdir -p "$DISTSRC" +( + cd "$DISTSRC" + + tar -xf "$UNSIGNED_TARBALL" + + mkdir -p codesignatures + tar -C codesignatures -xf "$CODESIGNATURE_GIT_ARCHIVE" + + case "$HOST" in + *mingw*) + find "$PWD" -name "*-unsigned.exe" | while read -r infile; do + infile_base="$(basename "$infile")" + + # Codesigned *-unsigned.exe and output to OUTDIR + osslsigncode attach-signature \ + -in "$infile" \ + -out "${OUTDIR}/${infile_base/-unsigned}" \ + -CAfile "$GUIX_ENVIRONMENT/etc/ssl/certs/ca-certificates.crt" \ + -sigin codesignatures/win/"$infile_base".pem + done + ;; + *darwin*) + # Apply detached codesignatures to dist/ (in-place) + signapple apply dist/BitcoinZ.app codesignatures/osx/dist + + # Make a .zip from dist/ + cd dist/ + find . -print0 \ + | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" + find . | sort \ + | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST}.zip" + ;; + *) + exit 1 + ;; + esac +) # $DISTSRC + +rm -rf "$ACTUAL_OUTDIR" +mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR" \ + || ( rm -rf "$ACTUAL_OUTDIR" && exit 1 ) + +( + cd /outdir-base + { + echo "$UNSIGNED_TARBALL" + echo "$CODESIGNATURE_GIT_ARCHIVE" + find "$ACTUAL_OUTDIR" -type f + } | xargs realpath --relative-base="$PWD" \ + | xargs sha256sum \ + | sort -k2 \ + | sponge "$ACTUAL_OUTDIR"/SHA256SUMS.part +) diff --git a/contrib/guix/libexec/prelude.bash b/contrib/guix/libexec/prelude.bash new file mode 100644 index 000000000..6c1212a2b --- /dev/null +++ b/contrib/guix/libexec/prelude.bash @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +export LC_ALL=C +set -e -o pipefail + +# shellcheck source=contrib/shell/realpath.bash +source contrib/shell/realpath.bash + +# shellcheck source=contrib/shell/git-utils.bash +source contrib/shell/git-utils.bash + +################ +# Required non-builtin commands should be invocable +################ + +check_tools() { + for cmd in "$@"; do + if ! command -v "$cmd" > /dev/null 2>&1; then + echo "ERR: This script requires that '$cmd' is installed and available in your \$PATH" + exit 1 + fi + done +} + +check_tools cat env readlink dirname basename git + +################ +# We should be at the top directory of the repository +################ + +same_dir() { + local resolved1 resolved2 + resolved1="$(bash_realpath "${1}")" + resolved2="$(bash_realpath "${2}")" + [ "$resolved1" = "$resolved2" ] +} + +if ! same_dir "${PWD}" "$(git_root)"; then +cat << EOF +ERR: This script must be invoked from the top level of the git repository + +Hint: This may look something like: + env FOO=BAR ./contrib/guix/guix- + +EOF +exit 1 +fi + +################ +# Execute "$@" in a pinned, possibly older version of Guix, for reproducibility +# across time. +time-machine() { + # shellcheck disable=SC2086 + guix time-machine --url=https://git.savannah.gnu.org/git/guix.git \ + --commit=00245fdcd4909d7e6b20fe88f5d089717115adc1 \ + --cores="$JOBS" \ + --keep-failed \ + --fallback \ + ${SUBSTITUTE_URLS:+--substitute-urls="$SUBSTITUTE_URLS"} \ + ${ADDITIONAL_GUIX_COMMON_FLAGS} ${ADDITIONAL_GUIX_TIMEMACHINE_FLAGS} \ + -- "$@" +} + + +################ +# Set common variables +################ + +VERSION="${FORCE_VERSION:-$(git_head_version)}" +DISTNAME="${DISTNAME:-bitcoinz-${VERSION}}" + +version_base_prefix="${PWD}/guix-build-" +VERSION_BASE="${version_base_prefix}${VERSION}" # TOP + +DISTSRC_BASE="${DISTSRC_BASE:-${VERSION_BASE}}" + +OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}" + +var_base_basename="var" +VAR_BASE="${VAR_BASE:-${VERSION_BASE}/${var_base_basename}}" + +profiles_base_basename="profiles" +PROFILES_BASE="${PROFILES_BASE:-${VAR_BASE}/${profiles_base_basename}}" diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm new file mode 100644 index 000000000..524f1cbd3 --- /dev/null +++ b/contrib/guix/manifest.scm @@ -0,0 +1,560 @@ +(use-modules (gnu packages) + (gnu packages autotools) + ((gnu packages bash) #:select (bash-minimal)) + (gnu packages bison) + ((gnu packages certs) #:select (nss-certs)) + ((gnu packages cmake) #:select (cmake-minimal)) + (gnu packages commencement) + (gnu packages compression) + (gnu packages cross-base) + (gnu packages certs) + (gnu packages curl) + (gnu packages file) + (gnu packages gawk) + (gnu packages gcc) + (gnu packages elf) + ((gnu packages installers) #:select (nsis-x86_64)) + ((gnu packages linux) #:select (linux-libre-headers-6.1 util-linux)) + (gnu packages llvm) + (gnu packages mingw) + (gnu packages moreutils) + (gnu packages pkg-config) + ((gnu packages python) #:select (python-minimal)) + ((gnu packages python-build) #:select (python-tomli)) + ((gnu packages python-crypto) #:select (python-asn1crypto)) + ((gnu packages tls) #:select (openssl)) + ((gnu packages version-control) #:select (git-minimal)) + (guix build-system cmake) + (guix build-system gnu) + (guix build-system python) + (guix build-system trivial) + (guix download) + (guix gexp) + (guix git-download) + ((guix licenses) #:prefix license:) + (guix packages) + ((guix utils) #:select (substitute-keyword-arguments))) + +(define-syntax-rule (search-our-patches file-name ...) + "Return the list of absolute file names corresponding to each +FILE-NAME found in ./patches relative to the current file." + (parameterize + ((%patch-path (list (string-append (dirname (current-filename)) "/patches")))) + (list (search-patch file-name) ...))) + +(define building-on (string-append "--build=" (list-ref (string-split (%current-system) #\-) 0) "-guix-linux-gnu")) + +(define (make-cross-toolchain target + base-gcc-for-libc + base-kernel-headers + base-libc + base-gcc) + "Create a cross-compilation toolchain package for TARGET" + (let* ((xbinutils (cross-binutils target)) + ;; 1. Build a cross-compiling gcc without targeting any libc, derived + ;; from BASE-GCC-FOR-LIBC + (xgcc-sans-libc (cross-gcc target + #:xgcc base-gcc-for-libc + #:xbinutils xbinutils)) + ;; 2. Build cross-compiled kernel headers with XGCC-SANS-LIBC, derived + ;; from BASE-KERNEL-HEADERS + (xkernel (cross-kernel-headers target + #:linux-headers base-kernel-headers + #:xgcc xgcc-sans-libc + #:xbinutils xbinutils)) + ;; 3. Build a cross-compiled libc with XGCC-SANS-LIBC and XKERNEL, + ;; derived from BASE-LIBC + (xlibc (cross-libc target + #:libc base-libc + #:xgcc xgcc-sans-libc + #:xbinutils xbinutils + #:xheaders xkernel)) + ;; 4. Build a cross-compiling gcc targeting XLIBC, derived from + ;; BASE-GCC + (xgcc (cross-gcc target + #:xgcc base-gcc + #:xbinutils xbinutils + #:libc xlibc))) + ;; Define a meta-package that propagates the resulting XBINUTILS, XLIBC, and + ;; XGCC + (package + (name (string-append target "-toolchain")) + (version (package-version xgcc)) + (source #f) + (build-system trivial-build-system) + (arguments '(#:builder (begin (mkdir %output) #t))) + (propagated-inputs + (list xbinutils + xlibc + xgcc + `(,xlibc "static") + `(,xgcc "lib"))) + (synopsis (string-append "Complete GCC tool chain for " target)) + (description (string-append "This package provides a complete GCC tool +chain for " target " development.")) + (home-page (package-home-page xgcc)) + (license (package-license xgcc))))) + +(define base-gcc + (package + (inherit gcc-12) ;; 12.3.0 + (version "12.4.0") + (source (origin + (method url-fetch) + (uri (string-append "mirror://gnu/gcc/gcc-" + version "/gcc-" version ".tar.xz")) + (sha256 + (base32 + "0xcida8l2wykvvzvpcrcn649gj0ijn64gwxbplacpg6c0hk6akvh")))))) + +(define base-linux-kernel-headers linux-libre-headers-6.1) + +(define* (make-bitcoinz-cross-toolchain target + #:key + (base-gcc-for-libc linux-base-gcc) + (base-kernel-headers base-linux-kernel-headers) + (base-libc glibc-2.31) + (base-gcc linux-base-gcc)) + "Convenience wrapper around MAKE-CROSS-TOOLCHAIN with default values +desirable for building BitcoinZ release binaries." + (make-cross-toolchain target + base-gcc-for-libc + base-kernel-headers + base-libc + base-gcc)) + +(define (gcc-mingw-patches gcc) + (package-with-extra-patches gcc + (search-our-patches "gcc-remap-guix-store.patch"))) + +(define (binutils-mingw-patches binutils) + (package-with-extra-patches binutils + (search-our-patches "binutils-unaligned-default.patch"))) + +(define (winpthreads-patches mingw-w64-x86_64-winpthreads) + (package-with-extra-patches mingw-w64-x86_64-winpthreads + (search-our-patches "winpthreads-remap-guix-store.patch"))) + +(define (make-mingw-pthreads-cross-toolchain target) + "Create a cross-compilation toolchain package for TARGET" + (let* ((xbinutils (binutils-mingw-patches (cross-binutils target))) + (machine (substring target 0 (string-index target #\-))) + (pthreads-xlibc (winpthreads-patches (make-mingw-w64 machine + #:xgcc (cross-gcc target #:xgcc (gcc-mingw-patches base-gcc)) + #:with-winpthreads? #t))) + (pthreads-xgcc (cross-gcc target + #:xgcc (gcc-mingw-patches mingw-w64-base-gcc) + #:xbinutils xbinutils + #:libc pthreads-xlibc))) + ;; Define a meta-package that propagates the resulting XBINUTILS, XLIBC, and + ;; XGCC + (package + (name (string-append target "-posix-toolchain")) + (version (package-version pthreads-xgcc)) + (source #f) + (build-system trivial-build-system) + (arguments '(#:builder (begin (mkdir %output) #t))) + (propagated-inputs + (list xbinutils + pthreads-xlibc + pthreads-xgcc + `(,pthreads-xgcc "lib"))) + (synopsis (string-append "Complete GCC tool chain for " target)) + (description (string-append "This package provides a complete GCC tool +chain for " target " development.")) + (home-page (package-home-page pthreads-xgcc)) + (license (package-license pthreads-xgcc))))) + +;; While LIEF is packaged in Guix, we maintain our own package, +;; to simplify building, and more easily apply updates. +;; Moreover, the Guix's package uses cmake, which caused build +;; failure; see https://github.com/bitcoin/bitcoin/pull/27296. +(define-public python-lief + (package + (name "python-lief") + (version "0.13.2") + (source (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/lief-project/LIEF") + (commit version))) + (file-name (git-file-name name version)) + (modules '((guix build utils))) + (snippet + '(begin + ;; Configure build for Python bindings. + (substitute* "api/python/config-default.toml" + (("(ninja = )true" all m) + (string-append m "false")) + (("(parallel-jobs = )0" all m) + (string-append m (number->string (parallel-job-count))))))) + (sha256 + (base32 + "0y48x358ppig5xp97ahcphfipx7cg9chldj2q5zrmn610fmi4zll")))) + (build-system python-build-system) + (native-inputs (list cmake-minimal python-tomli)) + (arguments + (list + #:tests? #f ;needs network + #:phases #~(modify-phases %standard-phases + (add-before 'build 'change-directory + (lambda _ + (chdir "api/python"))) + (replace 'build + (lambda _ + (invoke "python" "setup.py" "build")))))) + (home-page "https://github.com/lief-project/LIEF") + (synopsis "Library to instrument executable formats") + (description + "@code{python-lief} is a cross platform library which can parse, modify +and abstract ELF, PE and MachO formats.") + (license license:asl2.0))) + +(define osslsigncode + (package + (name "osslsigncode") + (version "2.5") + (source (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/mtrojnar/osslsigncode") + (commit version))) + (sha256 + (base32 + "1j47vwq4caxfv0xw68kw5yh00qcpbd56d7rq6c483ma3y7s96yyz")))) + (build-system cmake-build-system) + (inputs (list openssl)) + (home-page "https://github.com/mtrojnar/osslsigncode") + (synopsis "Authenticode signing and timestamping tool") + (description "osslsigncode is a small tool that implements part of the +functionality of the Microsoft tool signtool.exe - more exactly the Authenticode +signing and timestamping. But osslsigncode is based on OpenSSL and cURL, and +thus should be able to compile on most platforms where these exist.") + (license license:gpl3+))) ; license is with openssl exception + +(define-public python-elfesteem + (let ((commit "2eb1e5384ff7a220fd1afacd4a0170acff54fe56")) + (package + (name "python-elfesteem") + (version (git-version "0.1" "1" commit)) + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/LRGH/elfesteem") + (commit commit))) + (file-name (git-file-name name commit)) + (sha256 + (base32 + "07x6p8clh11z8s1n2kdxrqwqm2almgc5qpkcr9ckb6y5ivjdr5r6")))) + (build-system python-build-system) + ;; There are no tests, but attempting to run python setup.py test leads to + ;; PYTHONPATH problems, just disable the test + (arguments '(#:tests? #f)) + (home-page "https://github.com/LRGH/elfesteem") + (synopsis "ELF/PE/Mach-O parsing library") + (description "elfesteem parses ELF, PE and Mach-O files.") + (license license:lgpl2.1)))) + +(define-public python-oscrypto + (package + (name "python-oscrypto") + (version "1.3.0") + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/wbond/oscrypto") + (commit version))) + (file-name (git-file-name name version)) + (sha256 + (base32 + "1v5wkmzcyiqy39db8j2dvkdrv2nlsc48556h73x4dzjwd6kg4q0a")) + (patches (search-our-patches "oscrypto-hard-code-openssl.patch")))) + (build-system python-build-system) + (native-search-paths + (list (search-path-specification + (variable "SSL_CERT_FILE") + (file-type 'regular) + (separator #f) ;single entry + (files '("etc/ssl/certs/ca-certificates.crt"))))) + + (propagated-inputs + (list python-asn1crypto openssl)) + (arguments + `(#:phases + (modify-phases %standard-phases + (add-after 'unpack 'hard-code-path-to-libscrypt + (lambda* (#:key inputs #:allow-other-keys) + (let ((openssl (assoc-ref inputs "openssl"))) + (substitute* "oscrypto/__init__.py" + (("@GUIX_OSCRYPTO_USE_OPENSSL@") + (string-append openssl "/lib/libcrypto.so" "," openssl "/lib/libssl.so"))) + #t))) + (add-after 'unpack 'disable-broken-tests + (lambda _ + ;; This test is broken as there is no keyboard interrupt. + (substitute* "tests/test_trust_list.py" + (("^(.*)class TrustListTests" line indent) + (string-append indent + "@unittest.skip(\"Disabled by Guix\")\n" + line))) + (substitute* "tests/test_tls.py" + (("^(.*)class TLSTests" line indent) + (string-append indent + "@unittest.skip(\"Disabled by Guix\")\n" + line))) + #t)) + (replace 'check + (lambda _ + (invoke "python" "run.py" "tests") + #t))))) + (home-page "https://github.com/wbond/oscrypto") + (synopsis "Compiler-free Python crypto library backed by the OS") + (description "oscrypto is a compilation-free, always up-to-date encryption library for Python.") + (license license:expat))) + +(define-public python-oscryptotests + (package (inherit python-oscrypto) + (name "python-oscryptotests") + (propagated-inputs + (list python-oscrypto)) + (arguments + `(#:tests? #f + #:phases + (modify-phases %standard-phases + (add-after 'unpack 'hard-code-path-to-libscrypt + (lambda* (#:key inputs #:allow-other-keys) + (chdir "tests") + #t))))))) + +(define-public python-certvalidator + (let ((commit "a145bf25eb75a9f014b3e7678826132efbba6213")) + (package + (name "python-certvalidator") + (version (git-version "0.1" "1" commit)) + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/achow101/certvalidator") + (commit commit))) + (file-name (git-file-name name commit)) + (sha256 + (base32 + "1qw2k7xis53179lpqdqyylbcmp76lj7sagp883wmxg5i7chhc96k")))) + (build-system python-build-system) + (propagated-inputs + (list python-asn1crypto + python-oscrypto + python-oscryptotests)) ;; certvalidator tests import oscryptotests + (arguments + `(#:phases + (modify-phases %standard-phases + (add-after 'unpack 'disable-broken-tests + (lambda _ + (substitute* "tests/test_certificate_validator.py" + (("^(.*)class CertificateValidatorTests" line indent) + (string-append indent + "@unittest.skip(\"Disabled by Guix\")\n" + line))) + (substitute* "tests/test_crl_client.py" + (("^(.*)def test_fetch_crl" line indent) + (string-append indent + "@unittest.skip(\"Disabled by Guix\")\n" + line))) + (substitute* "tests/test_ocsp_client.py" + (("^(.*)def test_fetch_ocsp" line indent) + (string-append indent + "@unittest.skip(\"Disabled by Guix\")\n" + line))) + (substitute* "tests/test_registry.py" + (("^(.*)def test_build_paths" line indent) + (string-append indent + "@unittest.skip(\"Disabled by Guix\")\n" + line))) + (substitute* "tests/test_validate.py" + (("^(.*)def test_revocation_mode_hard" line indent) + (string-append indent + "@unittest.skip(\"Disabled by Guix\")\n" + line))) + (substitute* "tests/test_validate.py" + (("^(.*)def test_revocation_mode_soft" line indent) + (string-append indent + "@unittest.skip(\"Disabled by Guix\")\n" + line))) + #t)) + (replace 'check + (lambda _ + (invoke "python" "run.py" "tests") + #t))))) + (home-page "https://github.com/wbond/certvalidator") + (synopsis "Python library for validating X.509 certificates and paths") + (description "certvalidator is a Python library for validating X.509 +certificates or paths. Supports various options, including: validation at a +specific moment in time, whitelisting and revocation checks.") + (license license:expat)))) + +(define-public python-signapple + (let ((commit "62155712e7417aba07565c9780a80e452823ae6a")) + (package + (name "python-signapple") + (version (git-version "0.1" "1" commit)) + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/achow101/signapple") + (commit commit))) + (file-name (git-file-name name commit)) + (sha256 + (base32 + "1nm6rm4h4m7kbq729si4cm8rzild62mk4ni8xr5zja7l33fhv3gb")))) + (build-system python-build-system) + (propagated-inputs + (list python-asn1crypto + python-oscrypto + python-certvalidator + python-elfesteem)) + ;; There are no tests, but attempting to run python setup.py test leads to + ;; problems, just disable the test + (arguments '(#:tests? #f)) + (home-page "https://github.com/achow101/signapple") + (synopsis "Mach-O binary signature tool") + (description "signapple is a Python tool for creating, verifying, and +inspecting signatures in Mach-O binaries.") + (license license:expat)))) + +(define-public mingw-w64-base-gcc + (package + (inherit base-gcc) + (arguments + (substitute-keyword-arguments (package-arguments base-gcc) + ((#:configure-flags flags) + `(append ,flags + ;; https://gcc.gnu.org/install/configure.html + (list "--enable-threads=posix", + "--enable-default-ssp=yes", + building-on))))))) + +(define-public linux-base-gcc + (package + (inherit base-gcc) + (arguments + (substitute-keyword-arguments (package-arguments base-gcc) + ((#:configure-flags flags) + `(append ,flags + ;; https://gcc.gnu.org/install/configure.html + (list "--enable-initfini-array=yes", + "--enable-default-ssp=yes", + "--enable-default-pie=yes", + "--enable-standard-branch-protection=yes", + building-on))) + ((#:phases phases) + `(modify-phases ,phases + ;; Given a XGCC package, return a modified package that replace each instance of + ;; -rpath in the default system spec that's inserted by Guix with -rpath-link + (add-after 'pre-configure 'replace-rpath-with-rpath-link + (lambda _ + (substitute* (cons "gcc/config/rs6000/sysv4.h" + (find-files "gcc/config" + "^gnu-user.*\\.h$")) + (("-rpath=") "-rpath-link=")) + #t)))))))) + +(define-public glibc-2.31 + (let ((commit "8e30f03744837a85e33d84ccd34ed3abe30d37c3")) + (package + (inherit glibc) ;; 2.35 + (version "2.31") + (source (origin + (method git-fetch) + (uri (git-reference + (url "https://sourceware.org/git/glibc.git") + (commit commit))) + (file-name (git-file-name "glibc" commit)) + (sha256 + (base32 + "1zi0s9yy5zkisw823vivn7zlj8w6g9p3mm7lmlqiixcxdkz4dbn6")) + (patches (search-our-patches "glibc-guix-prefix.patch")))) + (arguments + (substitute-keyword-arguments (package-arguments glibc) + ((#:configure-flags flags) + `(append ,flags + ;; https://www.gnu.org/software/libc/manual/html_node/Configuring-and-compiling.html + (list "--enable-stack-protector=all", + "--enable-bind-now", + "--disable-werror", + building-on))) + ((#:phases phases) + `(modify-phases ,phases + (add-before 'configure 'set-etc-rpc-installation-directory + (lambda* (#:key outputs #:allow-other-keys) + ;; Install the rpc data base file under `$out/etc/rpc'. + ;; Otherwise build will fail with "Permission denied." + ;; Can be removed when we are building 2.32 or later. + (let ((out (assoc-ref outputs "out"))) + (substitute* "sunrpc/Makefile" + (("^\\$\\(inst_sysconfdir\\)/rpc(.*)$" _ suffix) + (string-append out "/etc/rpc" suffix "\n")) + (("^install-others =.*$") + (string-append "install-others = " out "/etc/rpc\n"))))))))))))) + +(packages->manifest + (append + (list ;; The Basics + bash-minimal + nss-certs + curl + which + coreutils-minimal + util-linux + ;; File(system) inspection + file + grep + diffutils + findutils + ;; File transformation + patch + gawk + sed + moreutils + ;; Compression and archiving + tar + gzip + xz + lbzip2 + zlib + ;; Build tools + gcc-toolchain-12 + cmake-minimal + gnu-make + libtool + autoconf-2.71 + automake + pkg-config + patchelf + ;; Scripting + python-minimal ;; (3.10) + ;; Git + git-minimal + ;; Tests + python-lief) + (let ((target (getenv "HOST"))) + (cond ((string-suffix? "-mingw32" target) + (list zip + (make-mingw-pthreads-cross-toolchain "x86_64-w64-mingw32") + nsis-x86_64 + nss-certs + osslsigncode)) + ((string-contains target "-linux-") + (list bison + (list gcc-toolchain-12 "static") + (make-bitcoinz-cross-toolchain target))) + ((string-contains target "darwin") + (list clang-toolchain-18 + lld-18 + (make-lld-wrapper lld-18 #:lld-as-ld? #t) + python-signapple + zip)) + (else '()))))) diff --git a/contrib/guix/patches/binutils-unaligned-default.patch b/contrib/guix/patches/binutils-unaligned-default.patch new file mode 100644 index 000000000..d1bc71aee --- /dev/null +++ b/contrib/guix/patches/binutils-unaligned-default.patch @@ -0,0 +1,22 @@ +commit 6537181f59ed186a341db621812a6bc35e22eaf6 +Author: fanquake +Date: Wed Apr 10 12:15:52 2024 +0200 + + build: turn on -muse-unaligned-vector-move by default + + This allows us to avoid (more invasively) patching GCC, to avoid + unaligned instruction use. + +diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c +index e0632681477..14a9653abdf 100644 +--- a/gas/config/tc-i386.c ++++ b/gas/config/tc-i386.c +@@ -801,7 +801,7 @@ static unsigned int no_cond_jump_promotion = 0; + static unsigned int sse2avx; + + /* Encode aligned vector move as unaligned vector move. */ +-static unsigned int use_unaligned_vector_move; ++static unsigned int use_unaligned_vector_move = 1; + + /* Encode scalar AVX instructions with specific vector length. */ + static enum diff --git a/contrib/guix/patches/gcc-remap-guix-store.patch b/contrib/guix/patches/gcc-remap-guix-store.patch new file mode 100644 index 000000000..a8b41d485 --- /dev/null +++ b/contrib/guix/patches/gcc-remap-guix-store.patch @@ -0,0 +1,20 @@ +Without ffile-prefix-map, the debug symbols will contain paths for the +guix store which will include the hashes of each package. However, the +hash for the same package will differ when on different architectures. +In order to be reproducible regardless of the architecture used to build +the package, map all guix store prefixes to something fixed, e.g. /usr. + +--- a/libgcc/Makefile.in ++++ b/libgcc/Makefile.in +@@ -854,7 +854,7 @@ endif + # libgcc_eh.a, only LIB2ADDEH matters. If we do, only LIB2ADDEHSTATIC and + # LIB2ADDEHSHARED matter. (Usually all three are identical.) + +-c_flags := -fexceptions ++c_flags := -fexceptions $(shell find /gnu/store -maxdepth 1 -mindepth 1 -type d -exec echo -n " -ffile-prefix-map={}=/usr" \;) + + ifeq ($(enable_shared),yes) + +-- +2.37.0 + diff --git a/contrib/guix/patches/glibc-guix-prefix.patch b/contrib/guix/patches/glibc-guix-prefix.patch new file mode 100644 index 000000000..60e12ca52 --- /dev/null +++ b/contrib/guix/patches/glibc-guix-prefix.patch @@ -0,0 +1,16 @@ +Without ffile-prefix-map, the debug symbols will contain paths for the +guix store which will include the hashes of each package. However, the +hash for the same package will differ when on different architectures. +In order to be reproducible regardless of the architecture used to build +the package, map all guix store prefixes to something fixed, e.g. /usr. + +--- a/Makeconfig ++++ b/Makeconfig +@@ -1007,6 +1007,7 @@ object-suffixes := + CPPFLAGS-.o = $(pic-default) + # libc.a must be compiled with -fPIE/-fpie for static PIE. + CFLAGS-.o = $(filter %frame-pointer,$(+cflags)) $(pie-default) ++CFLAGS-.o += `find /gnu/store -maxdepth 1 -mindepth 1 -type d -exec echo -n " -ffile-prefix-map={}=/usr" \;` + libtype.o := lib%.a + object-suffixes += .o + ifeq (yes,$(build-shared)) diff --git a/contrib/guix/patches/oscrypto-hard-code-openssl.patch b/contrib/guix/patches/oscrypto-hard-code-openssl.patch new file mode 100644 index 000000000..32027f2d0 --- /dev/null +++ b/contrib/guix/patches/oscrypto-hard-code-openssl.patch @@ -0,0 +1,13 @@ +diff --git a/oscrypto/__init__.py b/oscrypto/__init__.py +index eb27313..371ab24 100644 +--- a/oscrypto/__init__.py ++++ b/oscrypto/__init__.py +@@ -302,3 +302,8 @@ def load_order(): + 'oscrypto._win.tls', + 'oscrypto.tls', + ] ++ ++ ++paths = '@GUIX_OSCRYPTO_USE_OPENSSL@'.split(',') ++assert len(paths) == 2, 'Value for OSCRYPTO_USE_OPENSSL env var must be two paths separated by a comma' ++use_openssl(*paths) diff --git a/contrib/guix/patches/winpthreads-remap-guix-store.patch b/contrib/guix/patches/winpthreads-remap-guix-store.patch new file mode 100644 index 000000000..e1f1a6eba --- /dev/null +++ b/contrib/guix/patches/winpthreads-remap-guix-store.patch @@ -0,0 +1,17 @@ +Without ffile-prefix-map, the debug symbols will contain paths for the +guix store which will include the hashes of each package. However, the +hash for the same package will differ when on different architectures. +In order to be reproducible regardless of the architecture used to build +the package, map all guix store prefixes to something fixed, e.g. /usr. + +--- a/mingw-w64-libraries/winpthreads/Makefile.in ++++ b/mingw-w64-libraries/winpthreads/Makefile.in +@@ -478,7 +478,7 @@ top_build_prefix = @top_build_prefix@ + top_builddir = @top_builddir@ + top_srcdir = @top_srcdir@ + SUBDIRS = . tests +-AM_CFLAGS = -Wall -DWIN32_LEAN_AND_MEAN $(am__append_1) ++AM_CFLAGS = -Wall -DWIN32_LEAN_AND_MEAN $(am__append_1) $(shell find /gnu/store -maxdepth 1 -mindepth 1 -type d -exec echo -n " -ffile-prefix-map={}=/usr" \;) + ACLOCAL_AMFLAGS = -I m4 + lib_LTLIBRARIES = libwinpthread.la + include_HEADERS = include/pthread.h include/sched.h include/semaphore.h include/pthread_unistd.h include/pthread_time.h include/pthread_compat.h include/pthread_signal.h diff --git a/contrib/macdeploy/Base.lproj/InfoPlist.strings b/contrib/macdeploy/Base.lproj/InfoPlist.strings deleted file mode 100644 index b259ea141..000000000 --- a/contrib/macdeploy/Base.lproj/InfoPlist.strings +++ /dev/null @@ -1 +0,0 @@ -{ CFBundleDisplayName = "Bitcoin Core"; CFBundleName = "Bitcoin Core"; } diff --git a/contrib/macdeploy/DS_Store b/contrib/macdeploy/DS_Store deleted file mode 100644 index db9d16f1d700f18b64edafa0f8d981a680567b4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 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 KMNUMnLSTZWI*(%j diff --git a/contrib/macdeploy/background.psd b/contrib/macdeploy/background.psd deleted file mode 100644 index fdc4f4ca4a07ea4c6082ee1357b6ec7e8db99d72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 982442 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

!)k`<*rLT67|!`(51D}=SCls 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

{o5I;6^)cSnzyyLxf*K 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 vRP!ZvkUdN1-_9EPCvT%FCr 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

9Y7qJ~d|*B=vPfnkY;$r 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

4XtY}z@xsiDk@#VR$SUSIbDrK7O9=ZX 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 E18DGLm;e9( diff --git a/contrib/macdeploy/background.tiff b/contrib/macdeploy/background.tiff deleted file mode 100644 index 4b44ac672e627be82bb9b74dac0016057b11c126..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202136 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 z 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 diff --git a/contrib/macdeploy/background@2x.png b/contrib/macdeploy/background@2x.png deleted file mode 100644 index 4858183f75c382a9b8d75ae4fb8a74abd830615f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138890 zcmbSyRajh0ur9$Jf&}*jcX!v|PH>kXgS!*l-QC@TySp>N-Q687$==!bKAi77+<9Tv znwF~Ws_OdxYJ%lt#1LSyVL?DZ5G2Hf6+l41_CY{Ey`drB-yqp3n7seNvKLXaR|FW_ zJL%aPfe07^^oa^7#KL2**FUanO#Jfftg8AP((nKS%~TH82?jW zRuN%A20=Cv4t9a}F+`XJg;_*71Q^*^S()fXI0RV#_LZ=*v)8jSF#0>M+55cz_7(nL z`*I4|8tK^sY?S~2i@z2iX9}jM&*3Scw1aZ}|VS zaCGki(fziN|HoqfzIr#q-(UZ;|M!RgJjlrE-EC~&{Uc-sqZ9-byIw+AK*@RWxXoqK zLBxS~am1VfoQjY zo*_66@mrdAq1q0Ul)5D;uW`mThOh{n!9?ArkL0B^ZVGQ?d31bXHOL3+gwc#0p6`g8 zZqJ82SVAIBMyhJC2{j`&`aAGG>!i0muLsc{TCo^ zC;Sc(XxIS_uU*=BtG52-ZPrLGpfG<{9r^e}7*{HFRmdSI>Grk0*-TrPjS1uD7Lzb*nQ%jh}b1|EpJ(s$fH3;*j;XILh|^H zgrJ-M*b!(KK71qo{BoV)BK#|70V7vz_M)4q8E%_}v6fnR0FC}9THg5=1f?E-;!Gw5 zZ@!odC{vE)vSn~$_`xq~bxGcL4=dsmZp8>j*ihttZ@{{b!m185$7Na|2#6W53e@0Q zzpWM2M>o=FPt9MfN4Xm&;d1R41FMKwhHz;#y#}@PcfzZtNrDv!`H2 zHM!|M^VxIz-69m|B<;&uP60o%6e6L&EqUW)<5q08I7b`q6tP=>)rKqRDnutEU}!(D z%dXHRWCVeYwxf1T0UxpsXMyNoKrYIO6cs~J^5B@)p-^7_*XtY!zlhBjf_4OkG)C7f zeyCOz43nUqnD@!i0Y)I`^(Ljb4KWOt2@FMMA4=^hZ|TS(M_qYVMR@q@34F;F{T1gF zmt5wpx{QtPtK`z)=REm$SbBM~`FNoZMxVF=`mO%PgDsVOGyS609t8DwLjrw{KLH~S#rw^psgH2S>$|K6bU!;PFIGb^l zwN@9m&Ju-ajQhpR)9Xk3Pu#3h1j(3qxJGldVe_Mle(;^)Z&za54mBRPOAvF7Oy#bpD^V(y}M`GSktV8p#GKv-mq+pl`p??g*3a50 zM?P1?lzXE>%<5X4mZx+?4G_`a%V8YNWT?s^P~_v`x-EQ;iCACB?mYg|F>{14KG!^; z@0+gijXQpm$7@L67(ecA*$ysfl#keWN8VK@hC#$-#?SLk@tkyfs3QF*=1+ii? zUjce{CZ%{Xlv*4`^3VF;aATHpi>76>4KMlSI5$T^ONJ_&a06lpj^}&sX7uNGRzxCG zxXJkisZi}UF{|sZnbcd+H9E56vH&tZTHf@|PMB;U>T6u0(u7_-vue!O#h9fP(fyoF zK5s_0I<|Zq*LcXje|I^UUM+H951!Rf!BRhtS7im!URPuJ+fglihP#_#*dqGEVIY)M zv2(0oo%Dn8O&pmD;>>o9;XV5oQ4t>>LO!3|VQS*-J2Agp&<(bYF#<6lTnf@JKkzOH z@WXVBV1XN3$*VKYt(5FuPc8_tlE%MLntnCo4*!{UN6Ji}d4k0W^5ggOaB&i5nFlsr zB&DD*glAl8P{eov+$tLs|1>w*ax;pp&Y*8X*%wmkUkX`3vVDR(edGQcSyG{~xobCwzn+FB2{U7x>@KnieBkkdH51&?gMyTp=`bY0 zgmr+Zr(w(anEF9yTu%0Q0oa&@q;6}x5t*Vsk3jgOFxvot`zXLTj!uc+^s#&gqeZit z*j_3{;GFTx7KDN_{7}d_At^_<|9=62HHx*1NYHVNtT6L3V1_?cxi@|RcMVS+X+aaC z2H=;b3?>Ot^#KLlE0lQZ!*crk=k2bEuIIoVW0vGK#K3YOE*!k=X~e+80%bgZUt!N; zOGUbO5*VWYXXs@a`$o6g46lb;^hc`y&N~Za29tQ%kbAG?L#tk80_|BKrd9FStQrX^ zPMu?EuI49+AtXKWv3gqmxw%pGghIuPPKY3v6GwpifK{P9v$JhT3D;y8W|nnEgR=i} z`PF1+{AtWpUQ5dGX#CPZ+StA^EAp_a*VgyTE($`mc47jG0z&c0`)C|5;{QB`WXL6; zee5T+!pQ6(_DFY(<_9d)Iy39kcFW6OcBNQZLu#YzIzI~QqFwl_N&V!!8T+Ly!@rD% z-?72r>JR&CHaPEhSC#&fEh|D2ez1DP@o_{wYd#)onBR^N{pGOrHk?iFnzXW5pkJFi zTiH3gpku-AI|woknbS&Z9+d~fzAp-iDL+}Sq?tENM8Q9tD-5yhiTlEUyTah`>Brxc zLMfn_sz4I<=1b-*GWFLPTwZ3y;jX$-}hfqCGsR!p!O`Ec(utumJ*3nyQhU$ zUT2NfoT6$%qWDzekYyxKi7nDBa@34NjefMUOJZ+!-8}d8T5XkJ7+ZWc+|@-EiW$<# zv-OqO6`A2V-P6?iPidM00dD`{ooXUj*7j)nk~P{s+TkOxailf3ERyjkBgC02gyaiD0=)#SdF44$c~L5_@5stUt7%& zU@1GlU{Cjwm!>9IpC*pOQeIhHo=z*D=z9czg!#ZG&iOr(j%hDt863vw%NDV&KmUzc zU-R89X2i^V2=aD=633ix!K$l>Ej|umPHGc95+df^oNgO&4EnI9Js~l%@!@7@AS~_dQ z_gpL*12KqbN>DOwW$+j`8U|S^#E$>OaM7MFEYFbAX_^hvuO~fBUe0e^4l%2dITtl3 zJ~EkMQ3dg2tb;jRHO!vRu^}$BW&|>dEzOsoRVMN2Z%m*30dM$*aHs<~ZIeIcXDx?! zD^NlDJa{3f`zYh{>^pzUUcElcBy6Ra(t`^4l!Lu$Hk?MUfORrLX%Q7?msXmhEnn6k zvV=$>Cc6x$jd#K3n~z0?Kcz`DHrK}0_r?D|znCA;+LJvHm6u#j8mq1PP@z$zm-4;Y= zBUQ#v(Qaeq8{@gqDP99_LdglbxssU;zrrTOLq%T37@SzQ?asz$k zS>G)t=7=N{fxpH=--g%R=Uv#G5?(bpxso$`NG`&^^ZZZ6ZSp2)atA4t(i?gD@0SJW z`jOU2yvK>;0-&ScHyBG^eaEFgatErkv#FX$r_}gXm(sU_9Ab#C{|M( z32rYzJ*pX4%o+#pD6r8nt3FSUw3yG$LHpNJz1<9_d!ne(}~_^_et$G7A17DqW*7Rf|6;I zA~t7D0sdAU*4|R{PSG!GN$`Ku=PrDN>xb-UUsDg6ni`(%^l_tY%$PnzR>qZOOrGi= zAtQ@n9FLRsqqpk6t^iET3XYbw?HxAs89tO!fGo-uv%wFzJLxgn?VJM8t*XB)d05%Y zPLipU0jGvCCLW8Hs>=Cz80D2lX_zn6od>znD=kLq$ohG40s3NcBF2LRx>fET`L*!D zFkr2E%DnmPJ~Cguk^fx?H)fv5?{aO;(!(2atraE2oZnGJQ?t8MrMPCmLJ+iB^>;G~ z_>7ii!?{jG>k;jw%MloAfc1&ql4V?W#OQaa~DTr@tRvM?b?}HRrCc7!a|$_ zqCtx66r^L950h6Cy5NaI72+2|(Rl4~lqle}x{sg(TUkE->teb{kPDvDkt+xsIV!Yd z`44>xd7si$xk(-qP((wd8+)pvdxO;WB~WhMzY{G9bOx=jfp^|~bu`~f#Q!29!KvfJ zhu_Luitkc1>xt2gQsfhLsvUj_mqHwj5bSa42vY>N3XK+e>C8*%6(EU$Zu0EH;*66zq@jG)ad^Cq;p7p(jx zvwCM(;dfxv;Fmqczd3)G6)YD4^Pn<<(Qv*H@5;%gtTMsWq6!Q`Q2>7$N7B~M_5nb{ z_T^%NXXAQ<7zv0VPeC=ji4Dih{9=-}d$;eBkGm+;eOOYCF&TABF4n6u)p(~xrFh0% zC#$`EC8EfdJisoB4jFRc+UI<%`ip_nSBr%KaFueP(@aaibl772>^vvRlkAcg3w5;;9`VET~+S z(dt!GOkX;3_c3eXl5>aB!s9Wml=AK=*KKU^b-Jpr@m9eT8@0~SpBFTA^`&9wyCqey zK$OY$90$2!GuSV_V0!#OtDA7t`SNeV+I1>0?e`f~^rPvet0Eu}Z;2Vk7_c_ZgmE)G zJ26raG!N~obqdGoRn_8U64p>t?jvg2_zsfQaD8$NM3fl}bXa-=)TY(=a@WRM_N1gX za_mbk(;R?zc8x>Y+LCe#yME4^&?3-$X^Tm0(eb2xCNuRFn`Nv?4}emZ4jNrp@*}3Ar(@d~A=;-b$p4 z7%5J;YIlDb%cjmz5XP5q`YQvxU$@K5Q8J8}DEhFyJ=M)I}<96ZM- zx{yT)vHv-UOy15(7vS|2Gpe$j9*h)!28OeH2ECZy~N=pzYq~R%|G}wUkXe0a$BJ2XseJnY6szF za=jdUWX5oeL?v{7{zQO;OSFug{x>Lc!3#|>$%evG#xfTfFN-bA^ZzR1a(v`oXpI;A zY(VZP7J9H0mqivQV31HT&qF@3{45W6ESg;U8h)Ko>$UC%BJb$SnDEg|XyEwU2$S5l zqut&N+TqEM6E#QX(JUKdU(sYV%1l&pUie|>4u?u_?f$U*zxY^}ZR?CElFGPq|#<2HZ3*r!#DRI93zx0+7wNe z*NdGTM_kAAk^Z6id{@G=CwBcus-`HMLgW)Qe*(DdJU%a1RGE!{k}zu*d`V2f)3N2L zE{>79f%-9619~jBIHuUwXcl&l?7mP_V>D?cGpRtC4Qjawn;C`LA@ethb$k!i8pDo? z^88v`Uq6&G&;)!nvoxqgDY>q{T@qwz^Sf_5Zws+t6qb3)3kt<7KNWJZ;^C@F*YUN2 zEJ29959T**!q~sv`?S!|j~=vG;Yo{Z$6N*lvnSi=A|%ZLSEDo<`(^uB__Y}!mFU+N zA+sdp2gQl*uj&Hn`9F1hRZErr#(g}gQ5P%I`V|KDwTAW^8V6vyQ-$Zts0nYb=S`^& zM8~5Kl6#ixz7Rq4<3kY?H$R^HAQ6u3472YdTaZLPCJspITNuBNN!4{*zBG|S(Ky@- zUVdmKPRvXOO=Fx)zL^&ZbB%$VuIaeR$74bNOAYW_hgABp*-uy`^yIEe^t-sQ*u?TK zD9h~^!f_^5Ni~t=%x`j5$GrIw6`PmAWq!e@jav~bMYNfJaFXwsF_3w))_kmgn({;t zAQWl%|)TwO0jIS(uyN`TJ;@K?Z-k~@w%8gLC>v(%Hp)7ewPYWi}Aap|< zn&D#No)Q7l+Vizfj897z+e&QD+S0>v3)Vqc@z!{~I=p}y*#dQn9-(m+dU)QmjCd_YLJ=ty=1c(?}8Tqxp(3eXFrPJRzE)E#mXqS zowYe#n41Dto>uSL=OzMiZG&9j_kkv~Es%fSlay_*wN`f)v(Ux*E;<#^E}`Z5uye?@ z_8=3#am2#26~^R>^yIw4AU%0vmIE!o77C!qr)-c3eOir)%SSS4(Ac2iAD8|}yxN1Q zKUUFE{`HWasSoX0b7xyOB>bbEFFr9h0<`O_WTYq%vO_Y9vf|tB16UnuqV54-9?T(l zZIJ9_i>ev2rcxUK6G+wr4LvS{=1;nhviVMF-C}Aw%qhQ25IhWu&9Ln5dE* zy_49V$@4TwiUwEQuf{ZbpCtwKS5NH1!4TQ`@tl@zj`vR6pR63lSb*342?Vd_f%8v` zI9^jC7{y*MKrdyl`#>+uXZ#~=wOrv&g$-v%Jx?*PekNL~43gSmBFm*8-t*TGvy2}( zWP8hi!0KN?SXh*?)Wm%l*BU!t6g?EBR3I#KwLpvpk-k@w9!^MJ28`aojKQQJmFG)S zyLt+TFzZh9bpI-i@EPRc*E`ig(|L%J?~VzCLBRXXLiqB0;>1u!b$B_0k#+Xvyc%Wl zFMU8j`(?X2+x8d%fS9dwn{~WZ`bRljXUYots$ks%*XdN~fg# z_8j?cmxtT2vMRD}i!+-hkQau;BGVrqYlnU`gGQPFKjIi?aKkpU{llA*;PBf_?1r{N%pCQBqz*I&jRD* zQd`*cN4LKrly7(^mCnPk8AnF)lzwU3j5e8)RU8hZN>xEU!$M_z2p);)*r^lgW#Iv(}S zI|&-IlG>Ls+$12J{w^!E%gNB-%#WBHLR&_;x=nP)gdYszEU0X-5cZhba^MP%3wO#Q zANduZLK7?Gdero^CJ9}OC&-yK9_MGzt9Co|lZYkX} ze^a$x_#aMoI8W+kRQR0U+BQR|xX+TtiP~@GHB^bpKz*`)CY-1a$sA;rYQBZiUTS;o zGG#8fwM!lqaHeU!%>dg68-Mb0y}q8kX}294(P=2VPf%nh5+%J1qHkpY;KiIb1L9m%=%V;+{PUcJH89oYKx%YPENCq!K%Jz2*9u+ zbLfj#b=}r%5PiDhd$UVTQ}w+aJu*KqcbqI{1h_3N&7O3HTvVN!XmrW4eVw^mo_53D z##~{ae+wh<3t`843z&$$s*K}bPz?KZS7`|EGnJFvq=y=gJD6f%jF3)I&Jn5$=}(C} zieA?BBS$E4<=m(i1QT?tj6%FqL7qC`p@2%7e_TNR82ji3aa*p_Z7^;{w+Bo7+3gQ2 zo`opS*SqghCeeIb^=?Ei*zM(CnOe#7eJkVj+6Wxlpiv9c+-qDPMs%!BRu(6Lu2Z4f zMfjIdP#T}L#MYKQfGd#Se#kFz#C3^dZ6l<%jYkk?$i@{tHzM0GV_POne!LNsIhu~E zVsA*U!Txsi^#&;llcG$-wJG5;t{{@;Hq8>#PcKKU(^(O&_YL$&>IYpBJ)i6iDaZ|( z*q;-tEdF$l@9m*JVh0N3hNhvU$f#3DG_M#3j9d+0JWhb1N=^9lbHlsBfm3OmYI z`wqvGF-_aoUNYY5?6n}Zz6%jL*XZW+Zn!7TPtDfLQ(^>d34U@gxwNuO4N_`Elf4>9 ztOU+3eHNSm&o&X#ffsOY zwJ($wIZV0KZf-s6&E0=()#HxRe))$LpkTYfi3r-4NZ-N{BA`(HL`x9K)kShwGQDU&soc$xp^FZ-_idsVtcv~J6uhV?E zX|Y4yKXBjr{C=zyGSO(J2YNz*iZj>uKvnH|W! zJ3SnRyluDR(d2qC%J7h!+2Y;l3lY?f3aoycgW~6;bOczfO5+kg1%$GX1Vicanc|SlD^6e2=Vlpn(8{(qKRKX_V<hgM91)&z;q7{06aHGO~=pnA`MxIZ_Uo(>_d? zTdXZKds#gw%+zahA7feDW*+mW;DDe?7gKW+FdKdYYrjREKGv6i5GVb}M%1!PHW-d^ z%Zp++r{5F{Wl76UIe^0g{962Jrz;p{Ibv0xK}fT6T~N{3Mzhar{324_F2aTsqER%M z=wGWD}j z?-XtYuBXvkacaCyIf#=!h_MoMSu--_!dGinNhfT?UmkN!j5j-l-aLEh7ax@tAYdH8 z2o(vNUdjhaL6w%G^28fJ2{+k%FLM82E#0cK_NVv7J^qkYgg@jF0*?SG zuqiWS-kVjLoyz{6*BvqfF|Y8u0`RYr2Cn&zmR*><+8euh@)f_;yexPD7ueZ|N5i3qw-1WZ>zkluIoq_~p=Z z1o-knnAF_lnTs9T_8gXg(TZi`qe$CS)`1GpNPN%MM^0M0HAo|L(&OP9K8hd|0*>it z`ytN=<)Mp|br<7Sd7HV!?LP&^MJVpFXSE73@N?E;oJ?nVpu+Ma!Y{g>HG3f(Pa*IDq6;vtm8`qN5La#V&oBdq>TzuHihTB|m*-Jm1*8nK1YB``kVd3?= zy=SYHp1j9ZJt`tMtRADy4yz0UxV4WDbu%(c7e{#-Zw@OEyqD3DRhd)0=TniM*N1zt zGMr}aHZ1h&I`qH<-{yjl81OgOwflayvcwiQQ&ITf%Q%)@4=1|kZ8E-Mk}II6xe@a$ zY z*K!Wq(P9y3$oe`Bd3@XPw3;FrEdCs!dsxwwv=#t-qKtb5Yk1STYKm%p8br)6PRT>~ ze9Q13P7%h0+0evmfZJbi$|jgtP<(ms|#*!DPA zdCPmX2yAC{!+mU7=AFcSJ*<5FdeZUs8hfg&I`Vqz)N!OdiYU+!Z7Z>%m~k1Wtk3%m%Ss*rn6M^&FE%^)9{RyYsd z4xi@k@LEm4O}!W1SW9I5<0G{thUI+QUvDZkI{1$Dkf?G$D{5louEU-4fyf{zQ;`hH zi{a&aUgr|r9BF)CZ&Vhd zrLI||n>U$E#;a;KJ)YGAwU;L1`!in;GH-i1Du%7#Vqe|bDqEhUx@fqa$7d$wkvx70 zU4^N386PdxNTc5)CYjIV(G|%Gy0v_&wJy>m%HZ;NM&9}aafZw*`w0qLC$Q}HM3|h< zmpYNFaXoPeD71bt2zl!CB>1+*=}+i{`6sA*cSU+VZ%KfAkO`nR%6dJ{sIXrsxgRQK zs=v&DuS>|fDzM~K@C+ns+72Bvm;+K@Roz>>oLs@y6;F}!dRkhGScAfNk>qW7&*xKBO%f$e3)eR#X|-QwOgd3B z&VStFd-F)o9j22|=W#GVj*$>{Cd*)l1tk|QlV7+-6Z=(N=Lph#BJJfbKBOeHNB=qq zIXr?VmRD>D$=RKcI@m1G;{Jt{^YEXN4%muHn2HuH+oTJHL(;Ud2ZFt zJS_JoAqnyDc3#^^pMaWn;jXJ!8Elxen5(FxDI)PZve@wf8`0e;Lohx$esjb}A8T}y z+9hU>6B2d@M9cUD;Z&o1kpPQvb@GmWHjacM|AiX|0$9C z{YG~4XOSMq2BDsBM>=nh`%eJp9voNMgNMCLomT#Nx792B39y$=C|)_9i(uJN*oS$S zpIQ~=q#No7bKuj@GUdWY&r9$5W)`-DbbtlUzIfSdB`f_ty`Eib%Ke)2R`_A-)p+Vb zHH|sB>mJvd8CK{u58vxW+U-0|%xeuzjHhXWehpqh zk^*m7h#cy&Qp#vD`#U@yiGmtdbb3;C^LI+CP*(W(4)*zKE}c^+R{=CvDc+zqCspQ> zg>P;nG<+(FfT7ldw)XqY$dWU!H_@9i6Q0tLQS*nF^+4W1*U{fBLkUR)yZ%jWJArJq z&RQ&m;N#Qv!j9{hgZ4u%{!LNdN9B$+ElC{Y8<)$42~U-mM_`9`Tfx=*+v(Y>Oxxx= zC5~8o$E{<{Dp-!MrbPe_ThLKk^YbI#-sE$se2l+kFCNs(G}gm%4_T)N9;K|e$*4Nx zFj%sk%K3Ngy7_A^^gnf*2!&#v2vYoaRnA!Nc2U|8 zwfuZ<@xK%Euy|?4YVeRstFOJi-rcQ=c!aM#@3I~C(Rp5oM%mky7pyXcv2Y%KKvG)) zuq8gWuRXQj6|1%##%tWQ$jS!!HWObAk9U^tqU+m|sMbxH9&&=<^#z-oRJ~4=*X*q5 zGJSdZ9one%OM!3SCOmJ~u>uZqNn{!92kf?^I|?)2p7$!@2??Ipq1=*zz)8lpZjAP- zcUXe3bNkk)rDZ=Qk^p>;@V`;xkvB#L(%EZDZdI3Q^@alW`{h@M&Sh7o@XBugRl&lio+K{`XS+b zY?DlZBmpnWC2u%QXSQ-8Rm&7cqeBnbL`mX1_);Mqmn0L%0wzxIyCnCHDy)+?vN-8% zoL~UJ?a|NtoM-A7U#NDnfxW62qDMUX=F?9P6JDN|PT#u$F?Q06ug4RwW#Tq$FL;$M zW9BN_2Z*ko%naJtkL@k>!yS9s5A;BD$FsIu1q{t|-|`!dCNF6m#owIaB_(_m;gXSZ zMdr(~$^4mS(k1!j`hgT0(caHspY(ar`*$<7h_=IPe(89-E1k80Udh|>#r0VdfPa8(~Z8e8|M&v$$YyqIaJ!RzywQ7J4&;$R7?9h{=`## zw_19gu$ShPar4-+`a9e(dqn$G6ITsmSDWXZ<=vWbXQUNBQ4c~f zZYIE;b@vud$Xs+~YV=YS8Y==o zj9woY;1=Uh6X{czuT}921GF#8`5NCvw#L;n?9mm*%N1YcX(8@K3ModK%}{-7)?lAq zfY_^dwe+H-yqO(cH_R2}mEr|LD*Lap5oFK=hEa7-r?H{sfqp)_Q|0Qb5DTp9`6U;6 zauOQoBlXWr=2DCDSQUD6+z=}Vbn|IrtA0mMpqLl?P>iyVD;@t_{1~>1&d(P5T948~ z@i1tYHB@cxT!hzOF46oAOUv!Y0Kc2#7=yW>OUBj?C5-g6)^v%`dSd4u?_HB;ddAc8 zUW6K%{`FVUZ@;E_GA_5tH0ZRvR&GM_K?8Qtr};SgTwv`5xKIO=n=YV>FPOSvc_89; zc*nAMekN);etT=VP2+o7m{2jB_{?^8fGq3Leqe4t33N0Ekzy!WX}?W7Yj6noPO=_- z#}_k(0{u1gA^<;mPE5ebSe#8(_B;&`bzHja@X*qVP?J~Kz z@eak0?C#(1W^wP8dGa$mr+a6i*t|h4uZ3VwLQ9?OU#z{otj(W2rIsU@f!Fhs z3;%=`DK3aVOtVznop2kso7L7}%v>9Xqb$o$)js;bdAA(rqGjn(GI}(5I|Afcyjwcc zJ~X29DCX3C1AF8IQB>iNEEG~D1e9GES>KB&fX>r?eR*m2)`HTC+{O^Km(co-sI4wt z>18M@Ytok&BQr5SwbTD{Ya1#`CNKJMb?I`4DIIV`H?S^P@Ld%yb7H>q2s*VY5!Z-p z9A=+rj@GvzG~!P=>sH+Ki;|m|_R}IzJv%+6Zr^=v#Xrq_)97}5{vo0KRC+Hj^)r1# zWG&b3amET$HUdaNqIP6g`MwTLGo)_-;>K9XOo_{3q2wge zFZHXT`ozZWZ*@c*WuC7-&GXR@g*+!_M^6osXA49V%#MQGkU2)$sy1v!+6_3VzLhxs z^BM1f4(nl~dbodsWy%bHF|LH1cfn$N+zyEG@T-08 zoposM=A!C%Oeza{UB+g%YOP0(<&UenIl$E+HmkQd(2``$^$hm0m3o5Na7A4FWK~b9 z7qg$ZZql}2!ZFp*3UCs&3#o-PLjUo4;SQoHmjaHT}Hh63gXYKX8l{lwr%quN$w8 z7t7q?cCD?&Be8S_bLe?i=xc~9frWUinD-QewXB4@KC0o@5M+jYbJs@d( zw{N%Y&5F{~jYPuLv+$1bO7fzE-$P(C)ds%f&*h-wYHOaL7M#(Nla%wi&&j(EW4Njf z3QpfgoAg${Yy|8AY0-9aT3AJtQ;6u}T^+2Ryo_B4kWpnV>6iQvIg8$|C%j&-q>l{a zvd=Mha?&zRXweTT8`&IUNnT!^+AT+(&+uIGD#dduss%8_ncDVMwO#U0f(y96+>J6# zD7c-}Hz*F8b9=tSa`=9~0iN+nK7nej4fpYKt4#SgEuD!`5g??W4HYu<3&cHX&@M=E z)UwSdUIi=L6J%+W1&sWL;AEq$w2{LkWY9U1Ju7>Eb832?<2e#+eV)&UF@~kY9ShRF zpMr=FFKb=1lbqy8SoX3d)oA7qxse~8v5+OlTS`NJ|Bu0O^`znC=O3JWPw&;hMHm;Ju}%VuDk)j;^eJJdKf5^_tr`K3PS&uaW5m!tnjmjW@fr-tHN`r1SK zD>@rgD@%W1V~=$sVdrX)OQrR}!E6fVFfT`?c~kSFxsXQv48DVUKtQnmo!&g%{mXk~ zT@9^RN^xKWh|U)ZPb6qb@wT)ZZ`V4lpp!EmV0L|v=HT{NvBG6ru~*!L&$1VwsC8mb zW;yDfqEikNjKEah$TGMUF*Bd(-9Y&stM8`JEg7~exse>VJ4_&rBLwq+!~>ILx$glP zPl~(R6!lJJ{LY?PK`Y&Ns7l==rDco8X--{l949#r9y3V~%kAnVg{b04N9C^48F!(0 z_60)2ckZ_$8$5x1o9ctTFl1@Sa`>Zi#%oK0Ayh12yNb6u!Qw&gIhJ*78+rH4&GGHAFVa-Vmk~M z7e3ZPSA|sY8Z>*J*sVLdO-?|-l6STlUybSlVuFn?!+;K|H|y`R_8j&*k=OQ+ zMb#yZa-b%om8xBf`$+SD$c7%8GaZt}ov^07BuUBw`NPm;>7h`+)ZPY}Ztlc5NKe0) z@A{QR&e9L1-wtHG7T?NxjGY9PUF}8}-Rcdi+uPEgk2L^o^+OJk?3+RBLTSVylhLaU`#oiQ>;Q3~q1iIzT`9l-kY9TgNmd!HssTr`?sNh4_qMRQ>oY zoownpv!5|jW!@)+>AU?;wN?GYjIdUq?h)*%aYY>>z;h;bRjC-t%A@Qu7--$rl^T-y z&ZfOg*vKm5BpKXSzCBiw0%sxMVzFVIZO3=gQAVv4f}9gEKJS@7k9&6X9K~p5SXeBp z@p@z^*NmYl7W!7cV#GY8m|X@9Kl9h9QXmZi^J8mLVP-J^|G_|}8_)>oPI@O%&tLb(W3^KWPrN!mgcU59Jsi*pC~18)RYc(bNb8X4 zcWUtR8mjYJ;}os#-vfm3Z#^)z*KtX-iDSvTGo$cL-K1``}0$Pg#L zhjOmrx;SPW|KKLmoUyA*l?_yLtqdY=zyM2KqW+0qOQZbFOb-2$=hot?`*~>ZFdu3t zt_NPHRB2Xly;S_cy?k-^=c~M^l|y-gOT-EUD^@`=a6`j10wBuiRw1cj^$!n^bRrM* znAD1S9d~NC8(0Zl_*wjOQwMYrTE74*PT_X16Hlnv6qd_U2EW(ut6NZATt~5kf*^c(A<)c<|>Rfc^oqBzva|W@1xv#+i@o#W-B56deTY7zS)!%(fCJ@QWTe<5))d zOXTfA?jAl!%44bs-I#G77yPXAX5#1^bJ%s$sSVS)Q9rpwr27^JjpJB_Xi|yyPKm|D zX&-WamSA0B{-6aIGwUt)!)IP=p<561GJhw1Rb7aRBy*FW~i5&~&Sj z((&TW9PFyOJ=R}<>U*W)3Q`N1wO?+H z_Cy?7%I-tmMvB>OHK`F=<^VH>8U>IWSn%mlF}8~kow9oc^S*Fmbxr;m&g)5`^0l+) zF+dsbP;Rhjti~3Y#@j96?PsL^R6Wh>;oAe%S#AYUdC=&L^Mw#*!4t~@geEH07c$^cE5LP;mi*YG0)m_XOEK}egH`lsV$B+(-_UXr0O zLz`yESpt+BJ86VSkf)5{hW%LN-#4^%v*LH&ji%*I3-#uxqD6+(7|HM3%PI@;tgd2j z8>*tcSRdm2TvAW=542}$V(=HYwWo~B)O985TK}-89B!6T_?hv2`wqEk)GE;M)L1$pSREcBl zyl)=$v2BG|jOv3zOtulh1X-|8{V>^LV*^4(q=xx07=JF1fm=^Ykd>=fINZ-SwAXJv z<-$=?P+`Fa?wl7eOE__*_BcRJtxJ&YmVW;P`BhqCc4RppF}7Uur2bC(um}qOo$T&8 z*)-&Po{F5+a#~yYIhQ?!F*H)`06Xtb{PXO!JYB#sk#DmdR*3o1^1k2`f#3 z_cc78AXY`#LF0e}-69>Tt+1LvqJNUfb*qVF6nvoA1xFq?X?xwpbg>9`pX_c1*mB)T z%lN@AG;gSN1=xii4&E#x6aJ|OLuVcNuL$hrob`sB`X11Sp18PN7@&E}H6&Jh$&3@E zes#U%N{m1#{G4sSbo4uW4IdbM4hloUp)ZXx$F;fth)|HxZ+%rnzxrMT@f$sRFNX*_ zu3ErF_YESkU8)dQwO<|SMaEO9zaR}qnk3s5NNW=+Xa|85t%KM?lN!Kb5@YKEZBwle zmc>5{KDF`7z;P7`LS~2UfL&eWX{w~4{mOqccBgy8`$NqWf+o`McPpnNvL)8Ln>xD8 zmL%~)Nf^NI*yJ)G1X<$snG*K+pJuzSE@>+FL~6s613frQuwN}VtaHZ_`;p!=#>1`C z!uW4u`Y2p_i0KcxT&Imak36#?K2(FB6cqfTQ~Nu|T<5&6=oER?F$vq~ZR~0=%D|06 zhzD1FwiUp#rUHa?s#Z06a)@p@Ub36Mp4MGz<|?#$WkI&wdCp8aQh9eF$H~pzqAbUa zR2KtVGrY#=*^!$dM>+iO*mKjU0!8ga=w=H7;&b%NxHYcuR+azo?E>IdMhEHqjx6?i zC5To=+tNQY2CYzc7x2I|zVSAYV;GgtSz)9fhkg#yB(cR;5md;Z`^lnViOghlcUby= zSULxXyuUV#Z`oM3Z7;i)ZQFKFw(VNBT~A&%mff;#zWuiMKR92U``q|k*Qu!%!;aTv zZ6!MEm#qbqEb0QPnh-B8l=BfsUKw29h%I2RX@z$o_@+o=RA3f?>|~Fx^*Gl^lN-@y zYTh?WepxisgM@Q~rtvYh4kro=eAch+p^nkUCKA}&*IOO3;0$t14nZCdPz!ja z#1P|j#C6{i86?X(htCjEyIjPN<-L>|?3@-+{x(@jpg!d@8iylyhc?8{T>cW{?D4s= z_TGngNppQmOD3dv4qBO8y>?R` z{>b3WV~L{Bfpp68BkIvP@MKQMf?CU%*UXHj%y4AWLh3X}!9+2d@9r@h+XgR$95YT)WvOO0eO(i8tg>s2JccFe@Wp0cEbi z#8Ns`X@(+Kf`38s0OTOA68E|Pa?SSxf{(|c$e6vGoR1FAGb_FJ=Z8E*Z^}pD%ld`S z+teyub4oBL#O&6uK@Jje3#RR5)sJnD%)7|VLe^&Fkvr%;(@f)Cszz~qk3vxdmps08 z!Ms)R1=J{<%z@s9J`LXkEh%~ za^)Pv!hG9M$W;Z>)tDb5(daE3o+qqVWP|hOzPSV!ms(50nqC-OGjkGsuxvb#v@b-< zF7N^3I_ZpNg_CLNXuTuIhzziy-){IgcD^3fdEmc7jKOo5eHA7Z?}?e~&+ip=Ky47g ztRrs9ZeVo%ZnB8J>?8WO-x@`DXbZJp+)MghN-c+@^Ur4`qJQZWO5oGVw!aX(Jsw(= z&Ax+U0EZ2B4lXQvZk+hk*&IlaXEV%iS8CKK~ME-UdA%Lblg%-xzHXdh%}s(eiG z+>9GXw)P=#SAJ-O4t{K>b~7#o!l-?O)^vr~G1NR}qdw)**=!K;i-F9y#+4f(4&+ugjm#{4;N43T|xq-VtB!BMpS+^n9U$fAu5Gs4O-6Yg} zz#lCqO_O4RXB^X1ScsbG%yOk^*`lIxTy#;t+mU7z*23HsOYJxm08 zUFIy&#%GS(nJv7Q%PE0MI9~swXg7cu<(*+Btt&E<1m{!r&qQ zyY~gZI+i7WYA~a%wu4Y)4}IU?AG*7TF&lq>ROr1?|LB!5x7+!grT_kTPpU8n`UQdH=H11CPJgv^%_t`Ed(CYU6oR6CnP%~Nv7u+Ra z1;jm^aW#5ZK6E5LiGRWi3lH|BG?J}O`)oVxT{k~3K1mWN-1Ev4$>87@)~UtIrkUPx zVUEsc5_Liru9)DUAuH^oTVS%T+$bh2EwOHkmR1Y_Q!=_jbIo_{DGO>JY?n!n3-Rx~ zT<*jqRcGPD)^jScGy2ci zD_zUl_VE%AC_|~niIvYo`{tQ1|5ZN<3ntz{K6w}P3)CTtka(r@C%MsOP;{t&?87w1 zZwysxLCCY8X2dK(zllEXaRUno& zP@M~~bMi~vID>Rgh03(2-&&58&%hZH?-SE(I#N5#Kq_gbo5*&7DTMqEvB`AvPh$cy zUAPdWrQ*84G-!(|{VJabi+8kJZaun`>}(i)Yu@H1P@{b1hd|3Cl)p$czu> z)V=ueckI*?p4M;c*dZ?Tu|3rSd%;zWS->gAX3S7zUu9~OKp?II6+H_*>0hM{W~;99 z*`DK#sFuF1R?FW6|0z=M)li9{%>rp!#7%2NMmQAGZ8~_xQu*>eZRNS;#IE(1rW>zs z=OX{LzS#&WNUWLAYWcWRGCmrj?s>(RgQNZe*0#=v6)oNikEw=@uIJ+l(5FKmDq4;U ztdMUeixk;|`h1VS5H<78xU{{w)i}lJ@c5jbk4m(}!mPSo=uYOwo|@|tikv!RE}k^Z zjxJw2l}lm#jT*vZjLIFW4=z7>igVwOwF5r0EFG~JhPd?<&vEyz%U}le!2ko=;r5uJ z$asSvlmZ!s3dCywWnc-LaxnGQQwb~%t6x_6w<5V2fz)7hx`BAL1m{uKh5<<4MGtQ* zsRL&vBIL0uYyNKLj=yx6eQu|rEjJ^_oNEb^^Vu64Z`{Z0)W@3f8W*ns4w!m=rk@t! zX@V4ex~ck)W3b(-7s!ndpRrQ=-+i($R+Ok}j_w3lEBzRgs**?_=)0i>F%>P;<2RI++z6kA5rEFHV(4kEgl`@gon?8Y9-KtrbxC+b@Uk@InU08V zM=igvKGwm`?0ngIu(MxLsLub16`cAmY@B9EzEdKVR~7g;uLy+>@$56r;PJ*vqi5CW z8{D&bYtk;eV>#M(s|=a&bu|$k%MJuRjdH(jh5J@k9GYh4z+S9Ma?t$!7jP5qKRXAr z*AcvXRB{%_yxpJ6TKzaxjVFMWiO`9?%}P<>obKO#gP57`C&MB?-&%8;I`ds{`O;{R z=Unli=l6JF=(Y?edq!3hyc&;)Xhw`b5L3ntmpcDE} z6{9b;?^~ffzRKZ5^@(>psOPOst*p2KE}9VSH1i+#90wXhBKGe>|sJRw! zlWY$)dpGeUhhj)x@z30YY+IL%=@0x8TrYZdwb4tL3;xBD+Bt+j}N(OGAqz^Mtkx`8YP_B zmM8DB-KHUsZ7`YOVabnjgsu%^-DQ-=UHG4p66YO;Y(lA>==2yPpg6=5a4Lm6@<7h4 zd!O1^*~2?>R~(Nm_#_tpoZESyXsLIgr3mfFa6C%1 zl>y7SEu^^XK9Tp#qx@98XFoO;3wlxO~_AU-FIKsP+VJ=N`Dnx%fG&9MJ4LMXL@!=jeW#Sl)bU zEa9Di!TA^#=TlruoW%N`{WNYS8^bppg51#CGzsrJrnOOz_ssq0$(ovW)x|m22e7C^ z<9M!`xhNK_4?z#G>7U!Jw-h;!ys;JN{DD?w zlC**P6veo&ri@=uvp=ka6^{TdjHhkpRq?&H!(uW8*WYRkWsjai0};C}s1pW)*oV_P zXTElMK46+pJ+*J#(bHsX#l9cL&Rx8pU&O?bQ@iY-d<30rM(2Ca>yX4gG|bzt(q{6X z`-=kX+IIN)jZvrwoyme=Bz;Jy7vuH~!u zxq13Ub;(Bsyj~lZ*TtV~C>d*)NuY$towv6AFYpa%50?)=*O#8wB;7IYi#918&k?e> z)r-#@V}1w!LH%x*g4g4|$a%n(gG)ToiRGA5u7V*xmCG z;r2mBQsoLGm4$tRld5lzQwm2`{ASoEY!5;(4=Vyfr-%Z2et({~ThX#WSsgWw`km^}vT>e6K2Vb7IT~U9ep|wS z#$V&RXq5{)eAcwMQQ?W88B)9!d7t**YE=H47R*=cG(&1(M0-s-WgFF~l+Y<{6;syK zX5(Mr)x;g(R2$El&_X0Ro zx6-R1G{{x+rCL>F1$qhHVJ- z&PRMK@&*{`%rD2|-W~&7TD4cWO29|H;gMnQryM zGH83jap3~0hgwh@TeM@v0aM)0yaN-9T!+)ExaNj85o#TVqU96#du`io?EzgvnuL?WQ(b-AslbR>pUYf@QeV&Ii8Z*c^{)&s`E()l{8(sat^KtuA zcjB!6xzdWN9G3-+ibbL94O?F;lE5RT3;X-TNnKH3D>Fedh02VQgw7AnW95aC z__w2migtoGJ)Jq355TDrLA$43i_@;qY?DI+i&%Q_mUPEwj;1_>>jWo^*vkW)v?dgY zR~R8=oG%8M8dhZ8TP0+y;MU9KEy5v{>pu-v9Jr^HmRAZKONj(_&PVVQFj|y`@mKaK zNdpr_%C{8``0_hf|8XAX=HMy-z28;AE5eB~GD+*xSKp=$8dr=eJ1tE&IuVF2dPjzI z)o7-<7QL+nTeb02rb5Yo2V18UycR!m2yUY2)Xu2}OP0so z(u`-u(FQXr?J#i%8oSNwU$0X;wbw5@aa2Wj#@8$pE2sQZVrO$&@wgDT z(P*M{=Eix8Xwt0`Y&KHKi}4&dpuG@e-7leO9J@y|#R0Q$L&Ey*!4t%4^fBwiQ#J25 z{Cam(pYcVndE@2!`2p!E<_~hLdl1)WInf;M-4iyS@8M7NvY!o31_x3Km{dOrGSq2e z!39<~%6EPqe4u+;)$%rHGAEb=OXkU12+-*BNlyD)BD9)D$E=th+ky!(bJYU#-g1NQ zA%9UJ3PGB79m+y-Pt%!3nP7I@L*Et9O0DYj&Vd6V0Pv0V$gpjPMME0N#N8 zN&$XRHE=&eMR{B~%VlBtSa67-27zyP3z@I=*(nfL)30sOh5zfxN9m>ZrD^6;DNm&A zp8?F?vUrYh%}{lclLSJaa(UH z&-)_==RBQ#UGT9u^Oh}R8463wQ|ZxA@iY2L>!`Cv3FWq%fjip;g{#}EsVfw}+~;C= z(@1M>QL|x7S>0f5JQ%0Hv0XVTV7DV%At0?`9Omsz69ohu`gsd|Dkdod7;U(`(D7D~ z@WmNW`==o`@^O@QLSbM*aNMWD#_rEUqW3&tZ3q8v&t0Ha7@79@w-tWY#?&DNjkP+QaLaHr$35TH8keC|N>1-+{T$#w!Ln02ur; zfM}ad+ZW~4Nxu%SVr8=`LfU}_T0IVUA70&TOZLsWiwa+ zp5WjcO!XGqF}=@(1k!zWca^-K9I{dn+`hN{Q^RVF5~FjsSRFw`B}uE{ zx(~85HGeNI8M`u&OLLSmZZ<4J*rod?lA&W#UfJSid;hL;LV<`8J(k2d!>qNSbtL7S zA+&u2#o;p&1Qp~l8rT+DdL?dj@@t$0yFz%4J%;_#1r}rM) zKHLa^ByR6C)org+6}fkcj-E6ZjH(K)FhWna(K(o0m}ET9Vi|5|T^DZT`_J^pnn!(o zz`z0Zlh?S*CNCtfhI-5)VJjAT5#p#7g$UcWf-CB!E2_H%DP<0yQxX%dHsnv{&)xE) zdc9jhU%Y}KLM1Sv%4ho^hSJN{j0ygIAXU*B5`sgmi5Zm(9nBh(M#&~i1w+7Dc?%v_o15&;5e^OM7!meth7@Q zo)YJ1CY4>RR@ESDYLxbFiug%dU7;`ci;R2fRI>foJg{`o?cOgoBOe)sVa8GymFY+`403D>Wphkb!3Id7}b zK^pf=xMivl6s?joobC&(Th|Ge<%}y$9RrfqRbB5^8WiUDCR#~t#lQ1%fm$MMZ%JnL zVJTH0v$etfaf#3hr}eTzoy-X2a~+YeI9>$=26DM{Ja!RAs$`6;7h*A{W_c=6hzuNM zSDX5~{E#89~o};7{<* zW#WuAuG82oC|@`kDdU{Cd}ww1b+6_261v))cWMwF{e`h4G#=46+uBVdRT#eUh}&<= z*zp>M*MP&H^XCx_?HZyHx(|ZS$yCq+j}x==I5g-yj0=AJEE3L_Uod_7=i! z$Wge@yux+-(A$419CN~G>jz0I9S1^L<;>~R$KOn9`@|h)iHtDFH4Ru1=?&i_q}rb< zNjGo#z{uv5L;N>TSw%E6pg*09k>#E6U@_56@*;dm)?`E$|; z>o-UK+AAe*L_YQ5(tDHC$UX#Iz)!svekH4;ODsDkXn1Do78OP6U$_fmLU-Qh<0Ane zBGy`EDfw;1XktI$lA+9|sFP%U*lv3BI8!*XK#bcB>7W$<5SovE7mtwbWWrL>f^ZC` zurgy5Aj?+5tfNQ+N@pRjt^WTQ|H?XL$1JuSU6)GHpuxpu5X3Atww?-LvF@{RCmJX; z#1F~H*x~85*kCS)5sAE!Cinl}xukrtpzRc&hjwW=tzNG3w-T{^BUQ zrHHqUq)~VbK0{2g4x;kAY-^{t>T#om+*%i;e%mX?_JYfa{uehT?FkPH`oGUtIw?u{ zgfPBzC(v^%B2&OzWuTXv#ZfoyNK^(@2aGMN-w$Fp1!o6GZ~n&mD^Zomg@q2=wwF3( zsSkM+JS9}6%Tu4L(`U-Yw2uv)m`yG@$twbRF=IQ>);Hdu^biCG!igGf^lBf`hgAE< z1zrDgqwPV?(z6RXK@}=$I8Z3B4|Y6leB;=ZSU5RYtXF=immbNhfA5T*!IzJ`f|1LG z(Uq)h`#;m)faTa#=I|O>v|P0QzQq0zvGd0ql+qbA69N-;&Rjx zI4r#!l(#7vV{L}rYa0R^QdFEWKv80UAd5Gqpnwfl!C<9{#p$)Y2x0+6M`e4WV2jvg z^A6TK@cawon!hoEQBg*mzbzdw`gSSBu8tq*FDr4px&3qM6eNqtSR4X~$Cb^&x!0xo z{9IR_)L+-|+f?GS(uj8?b#wDOGH*;|tTfl0X~SnP7^nfoqKwd{x1p=Ti1 zTC^_&`FYwPtCMXb52I3Au@`P>C<8CP0E_e`O=l0-l*WM}{{oJ@$Vl^>Hqpt8$bfAf zwJwxv`Ta%TZ6lT&%rP2oVR7jn>Y@YoslM4)_R`s%^q4W&2O%*-da8U@)%?yUNyEL{ z^~*VXjA93W3Q@>(!T!=_(y}HQ_+M)`_4rO^48gINJ|K*DU5=z+)bYZ~J7A9V5ss-s z0T~%KFr5@Kky!|jt_fU_q#!2|AXedX5#>E_Q(C$YGj?=r1hFYjx#9oJz#Ebkn6tZg zj~0!0k*~WL6=jK33c0D9$eLr`&(T^U$ZIux{W72{Sg!(2yX^^cUnV}%k)ePHp zb&wCZoim60!DyNy9e?iU{0G%TYAO|{z5L)cp{uQ9tK8?e6gis_6`i=vfhD)C(H@g+1Ga- z8S^N&W0-?tJ%Od5w@ffBt*+f*ha}q+Lal+4F3OLJvYS*6CzV759~rX2pjXfgE4x>CTe{CP-7>+I;|OFkw_z$Mu}d)E*aiJ4 z{ZcORq5oXiqdE2S@zBHXln5O#?ye`5X5kj8Qj2wr{&*89fH)y!@*ei*J-GV+c5MoV z=dQpyzI`5!8Gb0LQ%_esU}jIPlsapGK>+MMi&=@^sJGq#n}442fw*`6Xj+GRY?@X% ztP5g-3#yhPiBrEDKf6}n)hdMPic|%Y{KA5b+$3nxrTisS?i?OYT@NKM!DSgc*|tk* zzz}|J@V!v}y9n$bL^Qt2S6m1+VFPEKZs-)4d17jYGfs@Z=uE(Ip{q2R9n$ulG{#Jr zMF%;ocpmiUCOnAE()=-z5sXp+$7W5&BRJPX`EYdJO2!-OUj(1vB=ymRBgzsSP)yTK zWFQjv1@i?d862@0ZR#gNWR}`8915I$vI#!peon~ZBMY=k>L9MLTq1H@hLF{-A}o|; za8|4obwy&wY>-ka?;S}v=`LYI*&jFBy`|V>mi@YtsXaIMTrd;%bHjchut%s_3ggI8 zTucm#1AlGyXCOZ>)zSUITVbFoxqjNUFChDsnAC^h%Gy?J@+p!oVLOsoc$C=0d(YUn>PJp#e+B+hx@s9^l@@ z4M{1U?lNRWi%lJ3vs51&wAQo)V?QVy&H|ELE$2Om3_@G~`qmC|%o<-~N(85db=Xer z7qJPTO&H>mD-YJ_<5#snkelQ;F%S`um#LeK8e*`4#WQmdtgEVM3H!IyxFUN*NwJu+ zjVC_qX&X*Z5{6`JX$a0u=4$YG>1OJCU3Ety#0OHE*h(MC@>-uy%da`Zp*Rm-tP2|T ziLCR}y=88&1v*xAu27)8s=h>_KWa#w$go(h+qu8aTaID!ZnxW^{Yz=FV&@RXsDgxc zuC>f8s92Beda3gjbX?DX@a6W|;@Bp348m81cVDXVzAyMVj(^BA+Nsg0QdKGmP;R7f zC(o)iyC>_tF~*Hp>^c_gm;Syvd^;?iQWdowM{B=~T<3|Su_I?ve9K5BZmatWpa4U$ zkxwwqB#o+cp3i&#vUdtpm=P(%H7NPT34h|xsZ6d?Lejf52rgALLLvUGb7K|k0!uAh zoh+YC5_|?}TTbZa$%@uDoRH0aG=Lcu;!&9L-kM`lQxena;KGV87|Q?P7p_hupkrpG zRpO+%>R;%TGurXb=z8*YWzOl(L{hDy%!81Bb(I&{p&f`zn$hPQ&s5HZxz1OB>&L`t zts|-Yv{_7;ZA$gR5|kve1Ikg^Wh>BUZ(zYNH3&;wRoS5gsL@1}|6mlgz;7)Q-jDZ> zNMKFR-A9Gp;TIMJQH~++^RmbKb6aN^V?u!;v#1CjMhsp)Td>9E4x~<}5uEWT-2J7k zDz{|^eZvs%Ws4~M&tMint8jsjt-SSOPI4YWPL*l@W4LEid&*dhEK89+(GTWNOF4;H ze9u1J7ZRVS0~aEafD;{&sG}g~w%)tyE%yg1N4#ruOS;ZvV%jcz!E{Bn6~|n3?rjh! zT!Rx)*&zW5FZUgqz4|@P9$;$i4ON@h?j>|2u-ELykU5|A85f|uoKiddam$@Sf9MMC zyh^Cj1;I<4Vxhcxh$*@f$=A512wjTmAb9m=0dWKUpUq6bdWxS?N-v9Q&B2HOtf}b8 z3p`C<($oCbt^P|OS!XG#FA2v|VgNNslNNzwbZVrjfEIE_GiEl7R=#_U-qQFq688zx zCP4+#S87v<(sC)I0Qli{zLgPZw_nWU(*2I{Fmil)7hZ81quDS?S$Y*N+yRbi*K@)H z7n8eV$+6DHx&hRXNb#dg1Ic0eEjH4ZzkAEYhlx7(|9$lq@?)#J%~7XOvRi1+rMQ=9 zkKaW48DBsyP{w95H&sc!<8_D`78kWrG~bF*BxB3QYqEM!wW+Lo(G{?CRi`QxBVW@w z$JyDwq%7G_VtpG8E<+XR`_%PK7C)`*XSI{`llDq3jA&$14=2VBD1M_tE6`E9U! z^y9C&_0pL+Q20jCJ5dnp6UzP8su00g{JOU!WOtVKMW%xkJ;Nvat~|PQJ2uKd(@1pR z+VGijSSgIeH-yY=q6`)Z2N63=jXw!}*UP#pdNgysb0NWlh!Oz6F*0_T{9?%^D^d;G z9?OeOX9QkhEGu>;+y*X7HmnYNcn2rS6G2bfAv@GfE^Y1Da!$K4rhtl)ozyI;}_E)`bA>d+vYZ(8kGgIzqZuSfmc> zJAw0dLNhsbgGs=vtWxZHTa11eE%&Bf{Qexg#FP0pUMDT7BI`FjM~KKsK~RUm&Zp7y zXu^A57XAH;<^IME(O>*}=?|(7r+3C5bDmuuUT1U4%z{pzAcV;!bnh+}e9N*7LDW%# z9L(&fdm8giD3m6@yc&jjC#Esui)E2u5M8teKcpS4eB-aHBL#&^-VebK2GV!a1sOPS z<*>}EoqbB#!}%^j$k}wosO}#$^Bd&5fFk608)ZH#O5=uH`kEwStH=?jX^KajqYB%B z+hvO>)g0GB?&G-2JVxB)Gm=|#% z+{FynRV)$RaNu%Tb}W7SgZBYp7Fzo^;~rv;v8m7x|8OB!7I*);T~#uyoz98*!3zZyJol^enYn;i>m;)8xM)EmX{7qroH-gsFsM=0dAjBm- zOzwv$Z>sjo_8kW@vQ>T*z43RfBDu!gQ}#q4ckCWsc=5FN0xI(sgS(CI)S*o!>#IK&x4F<^Gc4B@dG z*d!dI>FJ0efA1O;s_K|9dI6jo`H7?WHmZrhWyt&E5S6>6?Ip0Ar@pixT99i@tcI5M z;jx4WvH=ju=OE``eX)M8sAYz=d9o`1X~teqi^u)XSYat|w`1|F-c!T|Bb=q6-jMHh zVyY$UKDV$rb?l%cE7EAEI48gel%?GSRDS2zsh}O}_BUPMu3@e1Eo{)>5g?FUStm+q z6VEj}L9^UH%SEO{08J$U=R@l;ds`lsjks`d5q{~h{9G}Xb}^;rjs?>r)*m>#WXn-? zVTC+KX=l(VsisW*dF&N|-0c191J{Bcns$I;f^V-1O-$9P*IWRdkZG?aVO7!*JPoqg zYWd`zb}HudJd^zHn1xSsaz_xLI{FG(;Wz_cp~k*LBhTE^`EK3c!-k;5Q6JF5LrY1` z^D49FP~`p`veK2W^WA*cU4EzZ{NvkslF2%tJGc)c-v0}?J&F9lw&}2X%G$U_QhK7g5P&+V zQ=Q5BJu8pY;wQ-#=k1rG&4@KA%f>K7yt@|ZCt2J)n3I8Gn^*S*wBN|Q1!L+END24x zO=RV`HwER}YTrR%*5|~m=#sOi{+9Cr@DKyKK}t`MSQ$t*kMJag%HQWTEIJ&6GmH?b zF{v2z=jf)oNFgU3J!g=7sth#_&>)BBs7Xz;*S-BEBjHuC5EY2ZlmCpu#_X`;WHoMm ztELgN=qb&wS;7vxSq!rxMW8v$wn9cXDo^wLLRep;c;M{PC9*2ED&jZA!#RvbTAn5Z zz7G-35r@j}-sfSY?6v8HL1+e*3>M8iq^u%CNuZpgrO>S_pXvSoYqZ$Iv&i}VYKt>( zL}dWpvF(`o)p&h(ry)V?ir?by_#OmGF5!)!9r06R*Ho;~OQH2R`-mG*NDiXSsL_@M zTrX@}LkL;da2ji^A&J38U1m(B;k6;a=$b2TKE61ecQZ*T`I{ph@Pds1soS1kl@M*% zai|X}ZNgyNRdqs6UEM^B(^HYYw*~w=j+Bt>FXgFEQlag18s&i{S~^? zv@1GorSS8ZUvmqkmtOW-)5uu}v<39sc@G|x<%LOY$ntJ&P^ssX|Bf!!Vr(G301O`l z<rQDZAuPD6UnV%y zR~dTa(3x5LOoXNao7mkKoPVBY=j9VXeE^ozRAbQt@80?wq_jFYH$8F zKRW{t=M>6z4lJ@M0mWAK_K$<8hNlB>T85Z`>M&Yy;Ecbjr{gg1;9tKx==hE=!C=eN z8zv{e)%2W4*7weKeWtLWgnPDKiw{l~xtsSI;cSLkmbF#f%MxoKTgodHjb>OVkPbQy zjI+utjyjkbu5jEmB+xc?Avm(yz={82}d=grp0P1XPpa-#Q7g9D1+ zi>!RV{8ee$nE+4N0+m^ao1@9f!&b*g6w7y!R-2JWq(A%NT(UiQ3mYvYzn|Mb_tdqV z5OZS*i#3dPN~x9xIl^vSfn}L~w_5~UBuRtjp+bm=@ux*mymg}5ZH!uz0wDIYQJUyI zv&K@NI}jAc#2tu{&e^}lGAY8XGRXotefS3$vLh6{WI5mbV`csO4m5?`V@cWWVt3))Z3HTG|#PYQh zgs?}B#lcCzaSN}b(cQGp3*MaTmIZRBs2(3@MnnI_w-8Q>L74H}H; zFc7j9Ur%F4wix>Rtu%7V?%nwUS3g|}JD#S>3bEMjYu#(%b5A`S$q&2BETH(l`Zpti zi4H5f%B)=0wFi4>qk(sb{&kXwb7R0Q*3?&J9w?#f`P(I7MipQZE|K9Ee_)B}ng{hY zoIiKvPuocy@5F4oe{q-?l0N#SQR{mC5oWv1%TO{!*$RH+eH~sRXtfx|co*-&11dRIKa~^;%w-IP2WB(6IVqDhlVIM%jHw&Wz`}67 zAP`WvZXv~K8oeS7GtW?1>6jR{$&D?;$HU<95_UjqdE9&D_bX(%z7fgywL`z_%I~4)88*HAQORI%J9{Ne8jM%lCsvh2)HqcL z^z7&V9{uAi%j@6OFx0#OZ2@W`9cH$dVwgaM{#~OzW$p6oQ|MO4Wxl*>@1?`9)&s3J zYNerSb%0-2Ze^V(F4uu*aaTQdWg$TcHdGxv#hMYnA_qBCa(&#>#%r_@CJN}n@x-O-KCW%$m-Ix3W!%_#t3{jyV!iWT`E zTTiCY;#N2dPT#r-vHER7%)$L&?ZX@?z zzP6gjw7&G`zC80=)p|?_kowaOm?R)y81M4IAoyS5cfbe_skl=KDJb}-Tj6QRenKh-dH90;(@I6RH5-<(=XjC>XN+_H^D{|4f&zMYXgNl}lu2Tgrf?FT%s8z{(}W^) zpZHa$)B=7Hwat#05B%x2M-hY|GHCQRl(x!?mgIS{%0xClHkkG@_)D0=pi!Z@sNLn! z?OJg87-*r;MsO0KkIK}aG^zW=KtPiC@J6Y|-9 zyx_`?I(0OcYiA#k8S|r?MAdqTtfrKww`o4_zp&W|x{A|cMaw52%X$v4O5>N13MtF= ziVX)@HXmp+I9~i3O!(c;p;FGaPQ8lYZ+a3N7n*HZ8C|z;qi%T~)WY&ZJy@f3V}2o} zEo}x3twttmTFGO>JdhT@C?G2pbM?KrzFrpIIwCpQc=WQ9=bzX_Sj zBYm2m5hM6k?bkrOJR#vHgEGun-4b90PiP#cfuAisu}yZ+9iB&P?I#kq$_(;3QG7rh z1yYHjZW@WRa|-Xx^sM!T#kf)#ZbZ;z{JKSLmP5r+8mE`C4X+-Y3g)@WM8ANqaUonb zOIG2;mAT+l!6+WiHVejANm+hYRaA|EfGR&FcZ=vM4@l&+#6(TjL(SCaUeZo(pAEfP z;Qu?FVicOcd`2zs6{A!k8FB-4xBjSv%#XJs0uPqfv38IGtJB1;)UMyFqNruwCq9NO zvmf|{9s*L-VYwkA;ZffcuThrf&aRk-y9!*}Me6uMCtw3~+g+`?pBnXr72HIiE0>ZL z7a6rD5Z?RKGZ|hqnrh;mWiB6r9B!e08AT_$C9PDGd4n4{PU-M;n(t~t^HOH{=Tz|M zuCW&8WJH?2?2!vSe)yLH;osX@_2)DqTW#^ax8HCLHIwhlTUM&zc{qiJXfu^$*x#m@ z_eQItay5VdR$L`+ zVlf-MFilFLFpFR6e~!I2A8_vts=QktG+%r+E>^XCZaywvI;QxlIwwywA;I?I+w&6b zzb%Y4gK5%ail;C=BDjKif0{Qg6-eclG-)%{Gom7lRUSr-D@n>-NYlYUkB6wKOAzqP z{86NP6aS78$G~u2axO9a#~cZN%YJ8GiQVIM8;4;V0ZHfw>c&OJ80r_K#H87##=#C( z;eO>@0&nMEMAL8n;p`|2t%?HhK9!}eT z#n-+^FBHgENkieB=R&Yr;BCw^W7eX>@sY5)^T<(;ho_tM;uO}CLz}`?sP;5AH6>@G zf2TFPwQz6H>@mZ?!tYe9NSgDLhFnL!{d{{yi2i@1W>gu`kcO{Y(<=<1xz6q~ad$?m zTx~1oG|Jy$h;9dJag%?tfh$?usoY+IN7y(+1BBlo@buEehdnw3m!>86B-i1fC?)FR z=P%hnEypCaY9}iL z=fRO~!J~D1iUaY}KkpEz0h)|bU-*=w-qU@_Kp}tONQ5IRe;9PmT<>V^yo-=Gd2dcG zWrNR2xb8w4^jvuOB{bNR;I<7xgHM3JiYayF<<>I3^?Dlt63th@3GiRJlje>1X~)`J z3;*|NW93`s5>&M!7z?Q19~r%iY`vpr$YfSJ{nbvMbTS+w)OE0blv*U7pP0r1b!-lt z5t={m!|5!{Zgd-{ayxV{lO%B9v`IJP1@Mm+<4#EWgO(*=$XI+0uFqs;`LbSyMvS*+ z;cta6vP~ZVJK&;CuByN#q5$sX8A^!0=)^696zgnavMxEF{FT~Xrk{R^c_>vg}M@#j?d4rgj@*HvS*u^ZEXqk z;UmrV%{Ik1x!WE`Pw`b;V@=I!@^a4$ciDBdKAf%3rcvK*<5>8q^nNRItxHlhNYQzN zg{ifh5Z@Ihoz9<@8K|G-yAVC8t(aHoFx4(hm%wdB#Ot1FPbW|lsQ0l}2Lm@uivPV| zb~eVU78^-Md!2!z#K;rE;ib=66cP$W-k*ZiVjCfsGWo;_=MeB|;#*!UNfPd%t`WS}Qu5GKH8Hu?&H;IgndVN@i_;_eXqS?SAO`9YN z=IcsbnNd}?x$82jOoe=gGG9z8VS`dmqtmbr1N=xy<`g4`PSr>z+H88U8rJML5#A^# zIZ$2INI}Xk`MDXoZ@qs0ka~_jggiSr4hu)WK64 z2b)mNfYAEaIGpHTSSh;|=s*xWs+~&Tz_(rz`+M4=C3mc|nw^oPu2vK6Gdvl($hNCh z4WKay)RbT-#LvTSLDl_15C8YhGUZyeN(p_!y7Ji*P+xTae_bFb8*$bVs?07d<(g-rLy!#h$F(235rSqWL!!!yr3 zU_RZuYft6L(7w{1jF&AEjTMx9x%!$G+%>E%z(+CH;fBn}X4GL)yZTdU?V{yNwDQr| z&hOnW4ec+UO9!N!p(jj&usJ!w$kWE*5PH?SqJ!}|E#T!XzVy@Ps`Oc3Qm2nQIZC59 z(zWFu>0^fuXZ}n`l;_uU3=-j$9Qqx|hyht<+lNR;m1KH0slV4RSbOe2|2sb1Je*=??%)TmIEF6%x7!9IabIDl#BL#Wtov^Vuz3%h7 zt{J}x>?>%=ca}aTvwo=1Cxzt2xN=ZK%^`UV$vc&&x^F{={EcpX_jQaBYPjhbd6S`( z$d^k?e*^VSxW+m!JY8VeP0GuL6!#uN3b`xo)RnL-+wVJhH?rv6o*SpbOT+o zkD$l3cL+K^LzDi;(lxLLqU_LOYumPM+qTWEZQHhO+wIo2ZFg(8ulL^f4>I%3BsnK1 ziS8uj-777?(KchIh-a>;bb-o@PkZg3p|&cmpnQ9PfIW6hEt=g)^lo_L=gc-U{6&1O zfgTeeq}Q&d^hm^ml_(lb(L{_pCkE}W%o0b1IXSn9t~N7&ySNZ!#!*I|GR)fH*^Ei^ zcgStZx^Ii~ABBLlpT~qyhINb>h-%E5Vj9EfLFWeO8?NW&?Hw${Wqbbo-;$uJi&UQ8 z{(d>)uxskz@L7b@`j%-Hy;xx1;Smj=Pc!&rs~iVkYPfjz*t=a{*D7i41iVMC;u%Wx z;(~3QO!u1x{D5;h&y9y(A3@eI>Z~Scd!=$Wo3RD(qwSMyw$c zB{PYL$Gm#Wquu>peQmDeBAS1%@JUI#%}Y8tS?r{&l<)$;oLF0(4jRAE*QFTmnlU8- z8@D37B+U~Kn;aI)w{?q{AKaJ^R9v7|SyGBFjZ0ZIoYua-4Odty0diFgl(C>T?+=cI zI6)@sIh=|h*4Y`Rush-H!N-P>tn;uP`&?CTFk+@Yj#xxG3E9^K;tP<3A^egjwb&w>WeFWsy?Sa0D7WsG0i1b~~Un-hq;o)QCdaz-e>)0)+yc*%}UB!+*7SVw&q2Eix&E;~2%XOgfJQOyv% zgkZ(=l#|w~&WQfC9`>z|w1#=PxB$~uJ$0u^`-O)wGLcNWTciVNcKiP~Zptku2R8Ub z&Bhw5OkZjrz*(qg=>a3L_J#iNno6u5)w)Ab17Fn+z!Hb}i6&S*$0A$ueL=aJZ3=#v4y}^=k2Va1%q}wvgbPq9TXLI^!aPB~Pw>NWgUYia#!(?681L z`?+mgbZYWSN*v=%Z5{*r>sSbH6@^AlO*p;vgbP_BOX0&Wh8Hj8+lZLyar+YfC|ct+ zI*olG!9CMTImI%GTO)>h_boGezh?y3lbZmlYdVm?0u%B*aUotknmX4stB_VI!`kDO z|9PdoL`=OQC~Hd0(K8GO!n7wqXl3;oZPseq*(}H&QRfaerD`xG5C?>BSzP6?9b4^T zO*?l}U)z&cJBLwa<9rTuj^c?YNEyYWzF;5wo#79SAH^E6>Os$K**(VHuP?Ds?{$qm z9M=Fq&0J64%Y6RXv!m4SZlZq!_yY4HRDPNmE0YCA``>)?D=+y#2B40f&%BI+9@K-< z_l$(xW*EVhCM*IhTve)Zqph8?~M8{km4@T-zb!XMKux{)_CYvA|I%$ z>D7crKqXy(*Mpt)1`%TR$A#o~v;A3({89HRe?M=EM|~4_0wi4gD$rj*F&~zs;dW25 z6FUVCwodj1&8VR)Sk?DYf$%0|X)BmEhp(PTwSFpIE);4vxK&;4V88#J7!``>g;jH5 z2&$`&E%pg_yrzj*f1J(3f@Ww|lzJTtB`ZNl7Qoa3aum#2v>=p$PvDz!rXIotB7cNM z5___1MCZ--$j3Mh32xF3&?qInn26A`0hAsXvW1X4q`-~PKH)l`1(@v-{NKkO!EK-WC4yC01ex4tYX!c=vFEc0 zpPhJ6YW;$IQ z&v86fE@pw_w1G03lU>ua&lr~!23sonFd~}>o%EOh;i)*L-fVC8HGBQp0=FqROqVe3 zC$twhctTE}3y_msbjz_4|BY)cy!69f(W04TlqRQi5uXNE^EIdc`KZ2%_5UN(-{IHj z?EI)6%Hmdp`zvvOK5L_dwp1#24Yv&YH(H0GR?<=YE0?nd7CE1@PkstDIZTIjJk@!I zq4}-DtG#94kkX#|hMv?dkA~p+5o(`s=|81HMIYNET~)R-g6;+&ZV&Pw3Codw(v20V zQ^4$%CAYQX3k7p#4YB#P-WbSWGkKe{y@5wHdR2Rpk(AwYbnPxBky8qP#>!ilKI53Q1vM{HXcF-9dEpn$uXipfw3#DAdmXzD+s)k%0& zOhbH(Nth8HMs;S>>$Z|f{EJnLkHwg<;dDMF`5Z2nR!(Ki+Na*J@Gn^+ZmD$ zOX3UBZbFBk4~p9VlN@`c+tebVyF)TphS<1L8QgsUo73hqVeOW?n@lr$h|F4o#~?<< zT?rm&ue7;G#1;xcoM^HREPzW7^w@Ok;YAEgaxwEnq*%&{4SpqtAx6Y|2;<`#mr1ln zMQkz$qvzGxQr+>!rO#lHDaUMA&z12^`h0JR;?);t$>KIF!iUcFpTCM^S*LDW@!Ji0 z()#Yj_%iaY!QdJ|S@ zrZb}Nu?drcUpd1Qsk&OpW2cbS29z9=q#r1iX$eSIpW~+BL3iq`1a4v>gDdhlNeEx2 z!77)B6#b{YOa@q+NCi12yT(jsVy&$Ju+I&{{>Finf|{dyZj6qSJy%xTU?liPooGC7 z|I}d0obBjq($lbPa+_$jtHm?gOm#-b7V*{p5ks}l)4u`N+iCkhPnlJHpTI>T7@r`> zSVF(ZYhk$v3lOZ$RmGI2e8>{&1)TomP?OZaM(#Qg$zKt)$L{G+Ld&^eWl9WXf?nb&^_KRI`bLi}2jn2IDvqh6#LRx` zwxUgi1V+dq926=QpwudL{@!JzaUbJ+Y} zNZO;)B`AazqMxvJKB`(OtBsP!*veCaIe0ntJh zE(U(pM#*n@Z7U#7Wvtr2sb*+tzeH)hO+6EafAX?jjPV$Ph=t0u6Z+--A>L}!lJbvHA4Cc%k&|0zFPl(+^R(*%=c>7p*`Ol2(e zU=PbgnzU7F(cp9G^$RJa6TF|$gqYy5eUK{MkZg-)-D)Iql3BH^1*!6Yo+ckqexh9{ zDGAnezakx5o?>!8xYO{z9FnlHsI-IgUvA?AE@L9y)3)9C|3s99lIa?#x!Qt^t_ zol&G2B_7B*t(?dlBf{aX9Ga`8m<9unB}5Mbis*>hBkK^5ba+Aq%s67XyGsXOYkA}s zikkl28dEnIqOJ{nLxS45!!&q3*%aSAB|F*{xaJ!Jli^A>KC^DfMf~u$0!PU9ql|hz zQbN&lcitJc`UrFOara|xbL(d$-T74p`z<~`_RfC+2IQ^9^tHG-A;_A1ywS2sEH1*@ zs1cuQjRIO#FU!~+U(%8r(5p5(UKjU8DBwzg!M(d5)Uw+%9qWinSIG?*rMrTR8|kr1 z)f)iyDdWiU{Hc$J`#@mp@{?yzFf-UX49u-L$-Vueq5%2JGaw}P7Zp`b@!NH?d6KJ|66 zM@VqKpyX6cL*TPeFHgk^WARt~$Y>fz>J4;RJ^Py$*`>z;ESH`+uH!T3lkgaT-2QHj zo`>niWlP{W^|(j8k_VeeNC6@;Brx}EorI4(&7vi#wd|I)Iy9F-32$l^l?|Q+@a-X& z^<7Csj3#wq5Q8&VJq>Y!>3%G4TwGSITpAcy--<-xCzc^>!;;ttp?#E!QSj*3u0?5+ zfm{)h9GsOkZX`zk@wOHR++u%)i|rYNA{SV(WO!%O9{>x-#QH~iG+{VGeV!NYqs<|{ zDHWm6a)~C;22@(dPkqapf;AhX@-un+eHB4)VjO%+%X?0sPCfY?l7(z@9STu&1jp1L zku|w?D|%*;*AUKLFWE(Xap7{3!{N~Dpn10b+sq-=iw-%cXT>`Un)Riz1DfJCq-u`pOUnBv4yABM}q%7}35?iwuYD0PVmH#5)&^*DXSstCqXKU!*fJ>6@BMb^!5x zP7uq9`=1VgbpAHI?Gl2lFx~CE)=`Z>AU5%=^>uNLFuD_!haJMF?GaR*`GSy!a_4dl z|3MFi18yeX*>`Udoy7wfb!N9c2%T!J&q@+7$Ydy-vg#Z?r{dI~QRv&K{3D|v_eSRq zEqbbhBSqIHut0}R#T+!;*)Am3t&aTM9U@Ir@-BA;>5cr6YV9c`C!c@l=O8V-EFmZO zQ!fmCW}K%jA=p+cs$3lW>bMm+#DpEL#Wetsk;yrZ!S|(4L&ulRU4qF?Ys|Rc zglnta8=r8?UzoWw6zkj!Jwj%G1NmZsue9wQ%syXwb;+XBw7o(g!LYOJYW%;0}Yj!a2{16g0fpAUjceKgEdbBEL&> z8;)uNnK+gJ`FGMIp$?|O&+%wkvgw>cWdxgEJej8rQp;DY1tpfuUC5yb$d{Up%o;)B zAI)P@9O;*F5N3P?gPF@z0z4(zLYv_5C48F@7B#D2d-+yjM6LC@L!|udb)XEnKKx@! z-sl&w%n^+mRq4esOEk)y2JIT_dex3DR)@g+fso-+b|Mn5Bp+wDgXXul=5dFk?$p;W zLq@xZZu4*6XkI@h>6*Q$A3ExEgsjM$qp(fC3<(-FjoYs(=qWBEbTa8kF~jYX2cpSH zNP=hpAU*|C_h_TuKpB;u4jMVT!^kfPeiEEFEX@OVt$%6XQLDgWjjiB5M(NeFIko6Q zKb0#^w-{d{NNcEA;{YJIaCbl$;3y{8 zXqQu$JUbW`=nkAU|E|S_epm))=*G+Pp8S*Bu87LQ(=qv^C z*E3Xx&cMQt1pRL$L(2+F;?FcaOF6o?c>E{rt2*kM7gw=Q+VYUUMLVhmV^5heikS3Y zqo88|$oja5mny5g2Z=zq%*LRyt`|Axcr-?GN>aeTxE->^#79kR;2N!;DO6*oW3p`I z|KfMF8Xn~r?)*c9SsiqJg=4!#!k`(~gxy+XiNK^u1Ig_RIR^CdO;oFEoMJlAas-U3)^mGww%G}*3PMY@?B<`4`TM${Vl%5fG=uogicX$tx z4~<6Uue!HV)3oFqux-39#mdxp+KR8wo1{Z@+ZMcEiN~p4=pZ2rcB9kGVFv1G7>MX{ zHX5$Y2{KU13}S#vOo-=-D6QqKIU70!ulJks{fCS4rOY9aDmDs)c=bVx2*B%U{uwJJt)%o@&!5n&G*-~)%N3MxsrjHmB zbt9`kdFl=O*V?eMLaE-gO)IU=hA@F89Jl zE{gkJA7bZ=d+%d1pG5;|mC2@N0%>Ydc_1Zf(HPx38GgMl*rGyJm>o;LJTROjzvlu< zfopEc&5A~laa|gjkJ&<|X*u&u4Td&N4R<{NN=bgOBP_C)=in=e0o}z><#oqwWbD8J?`nD85W`?$@XCdveji zr8IHnbZxiq+vIE0_Td}}mdvyDY+Yd~gMUGBsho(vk47VXCyU3qJz>rsDo8YEkXlTU z2a*y(l7b9wt8~c;KI$r9>r89}-73W;IK-njR1!n$4C$q}_{>(4AuaVL0&f*XJRUo7`zNLcm0!2agDPz0#;3uSecGOCVurE2jm%l;ir&rYOJ8p?J?sB_}ZMMQ^$QMWR8T-frfy0I-df=gp9lu)0nm&UC zAp3?@%yh;&_*gIKCTtzPC3qv3-CtpzplbKQs$9pb%-Ea!Y{mg-UI?6R3^xz z${!yPO8On2nt3nqoZ3o2C+`xRK^5Z3cfH9M`N(hz+Bn0iT;6Q*SLRc>jEX5M%>~E~ zOyh!Mp-le4;Z_+Mca||2(L|U?_$oI@QB!aQfP0+r#<&>Z+^LHU3aG%iY^3)LQgz0q zndXnLU9p;J@^W%;yedD8md_s{Doq* z9Terv=z03Hx4Q=+mYCZx-vyqc;dTs;l_nYqM4})YthiJPKr!4gRdSqh+b4~=Li-!m z#~h*(Ywfsei4TaKLekV6PnTDOn>^%Qr}PE95}j{P*8gOrmF7iEGMO})TKTbfxi;yA zZ#%(Xo8<~SDDtEXd7*t?5k8uJE=*acg0sJG)HvDE^&sT zIFZTBly`&)j+yImJ*V@X8p7ucId7A*lDO}}J8uNI;Lp%lX_zfP(TS6_Bnx)?l&tY; z56jSZ%&;((ehazMIPNM|fuUnyFiv9%9mk?(3+E_ShuJbF!VSBjj#n9M5y4jc308`I ztk|$f^aw>iO_&O-Z*Ze9e4%ho9FPaVX|>GIZ|Fcg9|p-v-AJpDAs zy<$o(0(7lb;XGNvfu}YTr+S2ZvIC=`7E6S{Wfc~o`7%6mA!s4X)l~G*U!=){>|k)* zu-|B)`~~iP_Pv=q0&2;Slb;WAIP}dq!=J7{gVO2p%z|6X9wyFeZE~uq>VJlWqj#-j zlxV!!#1RCcGeA6;@4LL{_(WUsK2j+Na8f8AEr4WidaA+ zW_cIF`#Srl?2C=g?svx)CHlov4UH!_g>|wi+BQT}9pZ*u!n!uvl0@^XZCVLp=VF$# z=5nd=GfqM+T&sIUW2@3?P(*$*!Op*Amej6pbegv?~Ja_Gc2dV zNW@|^j**qqdy*cd?BkXIGQK=xWWAo-nviMn|)BaoBtLnf^7Jj2m{yMT3$5 zd`Lm=jy^)u0Glh9Wmw|X*6^*PEMeSy>cVQCaqu_%L6)8r106Ew4D{04ulS6K@29PR z_?jM}3!(@-37buxT_ZC-#10u7xX$E$Lac#Ng#=}e5&2K>6IT;(+;nV|Zd_hZk%Kvs zFL|VaABklqpw`8@e+e~wFx$rD5wJVQ10oa;M8MONZ$GTM8TOMQE%}oJIAzXw5;#Ix zIv7(TG8B8puMpdPuSeToQqKPd3nUDlsi`V;aKXR7Squj_%HNP;$Q>%Dl7I|y?49Bk zb~-HixP|Mdf8II8?wiRy^?~{n1)BB25_dE()b3#rG$f?=0()z3qNY@n3dAK(Zvhtp zKmSF^9J5tvKPI14%qRoYQ>qu;l3W;GruBiB3yHcKn0f|p^xMtZyKs3NHP2}(p*Xn( z`FPprm`JwLGR8?2{nESdzw|>_kZ9){;BmGC7R%b6z`kX(uAxLH*?9RSH~kPs>!@lp zV+V^ko$$Jre60ppZ-WcplwcnWG{poxzu0wtY5=8-P|cDBRyhhcpv@kJ;0KNo7!H_) zQBf++gZhibvT;G(ulwglv`Xz9O>hU0P-GAOg~}3EP-jH~^r}7(d0c{@owl$$f_<=`=>2Lu7W-5n44WJ#W{4~?j+b$u*CuMWT|PHl_6nfyLZdiSn* z2>u&rH>2F(Plb|jh%YyL`*Lp2>^+4Ph9=cEJd95>-{YkOlW+d0W~AcB$ELMX`$`$# zqlpU5$LAT+EcVLwe9X$H2nL2~ygNmihhx_Z8USMbZ|t#8#zdi(@wg;7`)@kz?AFqh zPmjp>t|NnI1fp@e_aZW$0A_erT=@%Tb4=qkQkGQ`Ts}<+U;XLOB)-*u(pYW`BAt4c zxP7^7OZ!@GmSQ?SY50@T=TSUGtBVI#0dZTt{V+)T`; zvI4`$f;Rn2a(X3aKyeLt-_~{~+#ZS|WMidTSL*GE$BP%SigT8yBNZm@Sn2%p z+h%iEwag86EMAjdqwELuxzbzOj6_gKX%+omZgBiNU+Sp|Odt6w3J#n8!={nD`98>| zCS=k%<&Hq!Z@gUFpa1Yh2L@swlMv3CJj){L6MIwXLqf>eN>g{HLG!qbzL!QAXUNwV z>*vIGo7>zljIFG_F;PX3y}jzd8BnYnJ-MeW)1FIZzhqKRwmg@;a7sY?S49dfDPJp& z#uSV(GPYTkTE7eSN8^_V+yhDqU~@Q%THL#|q1%>G>KnQVe|xRjF*3BI^+l4GWTC{Z zBv@vPt8%o^QXR~V>bAP#IjZW_;GqV41=2PUxf(|@W+SC#$c?6J2k}cwaq4c} zwA~f?KNz#bg!>KNZhW>&dvYK3B zroP*0QNgQeYbju(Q+}joW3=zMTNZP*B3MeV4oZ%Yu2276Pwfi{uV|QG^uAJ-@a4uN z0UZ*s)mcRTm@_sj#LJ{X*rIH?YWA*pu*zxUfgtFGNp6kFxd6~;gWSfAjhVcn6RAo=Yn z_{qLpjg)!#Pq%O;cNj;@NWJ^NRDmo*3@Cab=(g^=S1y;=m1?B`7c5>X#rbt#%c`S5 za%^93pY#0a#ZG(t*w1d-Q>CJ==zDVtEC2i<4}hy%8~1@mUP&#h1UWKqh1X7h@w%uB zdSA=gBn7sEk5%AYrfIP0h^L#TbdvFgc(w|j(AaI3PSn9af)zKxX!=T^p^*p%qO>OD z(4t>{b3r&nw`8KRoo)x5HTsMd>f*mWz#ka4Un8e7b=6(0|4nGwP54zY^FF>1EtHvh z@IPfRgb&g;rrI!y%NUb9=EUlGBNlm!AT!%!jeofLC&zP#Ur1~L26W{Z4i>=Oup(|8 zhyh%p)ZmfD49%cu=)RvXu$-kF5nmfwv6v?H2-&WbEBrSa?4tyI>mOy)w@B_&Uaznvx^j6dNiTut{bea73@X{0UO^Vk^r20|J@dEz;f@Hr(dY=n{9JA}T1mvV^CI+taGf!&%JOdPQ3ASPdpk=RZ&5K@)6poD zxv&sLhtsSX;I=ms1~!A4s*jcdG<}x0Muw_ zP%Nn20r*^bB^Ib0pPP-O>$cI4J+s0%rF$-^y`q)KTW!Zr94oL(HAeLORBlmJ$N!tH zmSY7qnb)a|f!syO?n*663yH^;NyeT7x8zsR`?>iaFKW|bpvX}N0NI%MEF2=wKg!Em zv$DDG5J|0Gm9N(m^n6{vz0p|MmY@@WewqXKvhWQy5^d8WdIo-J*TosZmb)RtZMI}d zvOW>Pb21)Lp<;E=+egstZo^>}eC%$@YB*{=D%=B@#iYSy5*pnBwj9BcVAvMX&`+gZ zIu`$>$N29~GKpMu{l!-$q@mIYae+hWjo@U?M8Yv7XTjz_RqqBtS-NI8jzLpu&An?A`_X7u1Sl&?Rp2luSDNp8Cn z(wrblGb3GQ^aL{^XUXI=s9RO1Qu4UG5cibzp1uJ%okWgN8Q}DWF)gaU zZzuyrsDs0r{0Dg_q!VFd(5Sr0FJ=#NP59g4*z?HO@^4OJowD1YocUAkw3|Y5;1a|! zhv*$4vxNckFOhN{Gfgyob8NR1*+AB*LLX?Wr-G!BhMJS(pJZzy;1?1#06B-@O|Xrp zQoAo~j20je8-5nel}wpsMiQngnDj%NcZxZ>Td&BKo^{|J9;nT2J1&bCC*Ct zbW-_*Q~JgPbo`^NrAIqs4ePr+b^a%GQ!WluSZXpXflwHpCJPke!W)=VdLW4ozt%DOQxy-`<)F+c^?z|K|*U zkE22dAJs3Kn@cV}^^jO2BneClrkE7mZumHGe)k-N;SGk$qR5=;&D#PGzsNoSllP<`F`IkV%oRUA4}pC8JQ1a`v7H7^iEH5pOp7aUja1BSoT7j86D97Dfb96@MnSMh{pdoQyVjf2tQ#J}pJY$*dREcFcO*6WPn z3v_^Po>d**D@0T7xpqtqwMJ#AG(nq;J!6T)_rR92Z1>`!a2D0gCs0YxC%K7gypv0F z(qj6}T7b%uU%d5^MEOfVK?8Ecd2jqQuS?4Vc(>$g(}8f>of!@{Ste(kfGyC;RcDe! zri#QglDLctLvB&*fBS;L-wy@N{9Y3P&iW+2C8-pd2A8r_J()Y$UF#|G+%vXi<)4GG zF(gbOr>iF%3ff;Vd3I!vlJ!mmVa!>-0g<=CXWDk(jd zOR0bxj6x_JM!A7XIyL*ZxOewRB3%t!-YA}4vz&CV&{F<|Ztg~p+k>N>U0>VbuUtB= z9EJXh%7a;yXkX*Q!cyOv6VQ3FRxFq1a&yhur}^kg^_Q__Bn7B&ZjQK!@$Jjhog@TP z-rk9{LIHWgdjjOGegl%{V+$EC3J3eo-c^0!eE>lSATn>3K1>>fag%h;rIR;5o1@un zymbCS1i1AP%Y|G*LRN_ZV~{9u5_)m)scG^AT6i$Cu}nr-dl7H~?BjncbGHNRSp&GBQbR~%ESeF+~L&zVwz~hY*WoIIvb~Mb2 zF`X+}1Gg(PTfyMgX_Zx}0#B1+gJz8{T1;<#26qJHuW?_;3tu(Iki|9z?soa{NwA2w zi>kn$5%4^fqEkN8>mT+!5#yX_8yBjy0_@qWEsvwID3v1BlV~ifN$Ci)HF5p)OTo=0 zjb;;<)^Tcu3eOT%`+?ypge4)W;5=7Ezjxu(OX}~DMd8?+-$ir;m0Wc*((XR6P0y7F zteV*AxDHF-;Q8YJ&|C+}turh;RGfHxOqn8ybPPiK77{xa(T+32@fY{dOS`X>0;Cj0 z_TxAn*IU~Jyv9Wn!-%*uiy}ID9yQq_18g^H^d_UqQSpK>W4Olt8PYe?H+N!T9L~R| zVX19_p#)o_;Bfn=)G<5Cxl{RO9@1O38nN`JbKf`w(U8Uv;oyJ2CfH+(r0a|^aI8o@ z{=i@neU#uW(hvVR(ffzA0r@C;T4ADjw;PP?`PM3(9N^|>lM%J|o)lKwWrf&$(ydx} zWLisuk*`y+R1sb%C+~FL+G3fJW#!+$9D9GyKD&X)+)4N4-AP= z6_jv(21{f!eAd}^KA{8-IXy8PZ&FNSbl~jFI9Hx)vRaNvl#*+Z49UR&&`uGCbX*=Y zIX^;cm!KLz$k-iPFT8s5u0$HD7Q`NJBIg+w|6Ol)yZvn zn0)n<5zT5sD{cQU*e#|7vg$&Ai!%$+t``_Uulo)I+1 z)FglMljj8uUMAIjoK@uyMa{uz>!F{W?2tS>@*_c;gHv3Hc0C6pyKM=I9-l8eoo)!j z+wOGuq_cDcmW7>&zF`ibjEgWthRwF#$i-Q1xkKV5oVn?!LAFq@ZtvjH=iv7we?UX) zHrpj_O{b8=JH9uj^kxFxb7nv)gtG!!3=94_&wKQ|GbK7r2A;Q{8#u|mM7o}&6noG+ z883FTnT=i#U$Z$$H;7I%<<@;BBA>;D-QB;bIrkgYRl`>Vhl0F!ZU3pCckPq5e}pG> zCzp*kWjn0V7E5YV)`0VpTskwe;&r3Y!Z; z!;qaUY@WO2AL*-$nV>IkKY|gL^i>=A^TLxXI%>&bv`gRl1$tx|*Zdn;tCSb)lepzx zQoe?w;o1E)foVJW6n*BjauB-AR2JtbV$wYqXi~6aR%sgA!mFiv(Q>w(-w@#JQ{GPt zfNJ;1fG}|)MZR05eLXJSsZH-9Ct&^y{){aOW^q-Y#XLU@|Fk+x{~A77laqtDjC_GP zXd~9b{zo;{g$&w~tq=wJJ7Cr#f+R8aHuSXF@w9TK1(4iZF*rz?cUy+YRWPU{e=9_6 z@2n}49dDYNRM~G59q%Pz9yG#|!8yQ0rd@Y;&rZPN`oKzaqv?_6x~cul>2w~M;SK&i zN}<>659}R1Y3Lh@GJ2?|p4vm*)=`^aCX8b$E{MR{x$6D*Y^1p9Gp? znf%{GBK8+%X?P3y^GJT{WXrUeduCAlYs8f$HxN*!k{r)Dx$$T2l3X1rxKVf1r9s{Y zpAurAE#LDhb4Yjfi7t-Ys&>B?wsv)#DlTY$3zX9ZLupYoEF2w&FE-aj=X@PCghU^(5N*q(7P)zV;R3>v_x>l+gsLq5(R$^uBkdTy4# z`fItujXwcTd3T7QoR_8G8?@^rV)}mbLx|;Ep3>d(&tgJ_)ANdBSm)rnPh|{nu7)3v zYxN4csH-_D{OB>pwp=QqUy>U7&udd^#Yy75NS*|HTwDh@^0MKrd_9bI+K!(n!`!dy zAb?c(=Ox^vLXUvCYfR|nsODZrg0`^zD46sosoYZ6}Jd<9#h8p@l{I#9WB&zI>gHxK{|gyuAb@LW zfKvsqr-G#=%|bqRc2Mj=^Tw|w$ZN!}ZJpM_W?vK`h^)+f1W*C`+sP%6i-Z?17_naD zZVP=Q035<-^%GToAViH|^Mq98LXqXv-Yu#djV^ccK+GaKyN1$CEc#<=yXb7ES@$?o zCn}hvF-zq63-$|btyRgg)LH>kc7)4Zv4PqSCFITDpZo|_a*N8 zeDI`XFa?;KMG2c-zUnNr*Pu9HmFj~`Z#=)3wkON5PQF^zzA4appJ?A$>!-?qFbjV~ z<;mVp`gx99;ZiY#H`q_4vS?eUy*JOpG6iEKN@L0~8XyICw{yB~_lPy$wP$Z$=ta|z zG1^&X&I){`sWr~17_c+2p*(iGa4=dX^T#F{S$+Q)CW&!YpeGN6F>hT_H;iX>JF4(t zuxz=&)xOYB9BH1v$Ixk8j_}Ea{2E}~5`NO%@iUzXBLq~p~FGoFyRHOuX zi|tT2i@;E5xBu~s;u7C%p=-I}5Ws@{L>ExHVH@TUXPp9hm;v1m)H?yq0!@HGqX_)i z+3!22QN%@Rtqsyri7RChzwOY9=t`I_b^ik^x9<6RUJc`gpL$I4oM9DvuN-gMG{z1Q zwl=P>F<_bATrNW3SnDzW@7-dYrJ^bnCt_yb^hD$J2;N1rIa8@<-cf9-PZ)*k@9ep` zq+hCn(a0WadiW=Hy}TB%?;7(G95jqfIdJL=2rgBbaG_sf1$tMSMLEW71({>bUwqm7 zeZQce*kBsf;}m(8_G+`!yoV#1QdaA7c!O)~b!wYr?TCljoRBHV$sBeBG9yNkUK}K` zi^fsXkW3T5(fe0CfwFgf!>g7_C^za4cXj@QoEEU7?=-tn)HHhsZ2ok80(x8{MZoPX zn-Z>M%vn~y$v=w^#n=7f6ocLiDf)F@;Ykm5TVUeFRGW2z+!BXkm`ZkM~uEKH+Yt;lKzp5cIXYk;It@|1g9 z7{GA^l4w{0BSWy%Q7*WvY{p!$4p<4hee@?;zeFCArs1RfY)e2)8QSidmp2rzz& zQJS%PT+tmO3<+h!Yw}95PT;VYzec_2je)E~RC(bo?hc3D96-TSo!_DAg?}pFCV2Cm zLgC5P`bVym>d22+rtBfy;r#Kishck<#JtTk{4I}*YVHiS?`$__AbJ)y|7r>eFgI8r zvB;*hy8~M)mW6AUU&!LGjE_yzym0z{?Tn(AGVK0`QP@L35m;>DQ!JDVG{i`X-XaC; zJS;9AbrM8P;k{aFqWp4$21!C=A=qO72E(w{I-&%D++i^gBi+8H+P~$)FX8hLqQcdr z0saBKhN7$>er_F+6hwy$L%l44M5belUv)XC-7d^@<3`>TU7LU)BgUMsJBvCH+85FWn`uECvQK)aYF2y<&W@FwtFvPHVCw%-#8jgb-Y zMXutnKxk0fO6X}q#FT}h4@*uxFSWb%t>@KSNGw6sAxT#Q1r%z^U43jhI{P=m3bzzl z`ASA1lz^|Qtu8g=PqdBBszVP8iBs(Uj+dUDO;P9l>UhlO7E8L)uzwH2xGbin2arsT z+?|%{FVtuc?7HJiKe!$8##MQ?C72X|{&aK*<|$*A4LfKYCD%$hV;*SlLf0q!g98-g zjCK}c|Au^0ke-@XDxCgZ>zGReVYKz1(iR!k$$aG!{s3_xCE|xj$n@!rzf zpA)JwrMQmEsz|)*&z=weyaLwA;7BTT40N!|(mi|RX%gxHk4hf(nx8kd16V+D=JKRc zgdF5vSKPl;XQ!<}^jD7@3f>wna#`*4!&=kYROvIDET9OMgwOU#K0Mu7AK$>tiEc`A zI{k@cR!4eVI$J^tdv-!Uib?7{XVcv|)v0TWwDuy+>nBs;F8iD=^PyC1QBd&2fT9O2 zhpzCV)$LhDP%}WL63cdPD4;0vOq}ArUuIXaR(?w^srx(FbHO6izY#Ep!>ovR>M8TC z;;+x6GF>4mi@clcI%o;)9qwv0#f9hfDfmSdU{YdQxyu>T)^D=Ynukj)mkDR+;{A-F z;D-}~4EE$!ecnGbOP*6PoE|V0HD!v*mdq06fJ?S9MgvX@m!w86TAi zK`GIJhp>`>uGjL-2m39Wadd;)9Y!%~Vq}Re#@O-2&ytCN{8+w~m$c8liP`R%aYk+} z4`-0F$Oh_>FZZ#=-F36mF#!!oEXNI5N8ypvWvNxDi|5XuIGjy56^|zIw`+DfPD!e# zxWwD6GXGzW7_UtaDV2o@OlIR4a!r4O#E{yzdLw%`QqCHQ zAA#H|@4@)vfO~C|=7%)oRhV3d0in_7pF?F&f4M+NF7^uAhRm1!cnpLZc%v;huw4BF zKrd7F!GjsmlYFjK&1Ts(I17>qXkJp+I+Z}tbSAU*oL{DWeLJaKC<&w%R@qbgttlY4 z+UyN{CG1*9HK;gatJ+XS>4*E>>mW0j&= zO&!9QSyH=19LLyIwuvaoh2z!V^v%7RUcGXz;zfV$K>h+2e(r>{-r=+HSwT*thkU;z z9QNu@zj^BVei`eKB0H``pLX=1->UL<#bzaUyR^FB5kzfI%gg-H036us;*94+x|(p+ zh>ggYBam<7Wk#IuR|}ZJPd>c8i0o{*idZ$1$;`8OrxHp&t01NN4R8!-r0PW`mGnE+ zY8M6thqc%)i~w+;z4^-CP%n&qs-Zv1fd(#qyM%KT#VL%FaVJU>7y*ONbep{|Q!Sm8 zOcI-y0s?EEgr64Dx9pQY#EOLyjZs%5+(Ibc7S1z2c7T8gkoAuc--9b;Xks$7PIGoV z--T2f5ZpC^H|Cu}G7+o{{t_s0`h6CX%5ScqjsQ;$l?H*Sy5f?=)^FgWLlUc$*>**{)!6j88|AubcX}Cd?vTjl?Mj#adV7 z2%zT9|41h@+nGAn5^*Apf*dRVkBX{A_^W5}7B3vnp97F<*p1VroAjVcAZiA+mGs3{^QNtn535Nd zj-zl2MiXU!Tby21)y@!mlRln_=p%cGo!1%CWhA{{KkKfHO9+MKHdC^=#?C;NjyF2Z>i~qN>IL5?&g<< zc4yjVcml=gkrjZR<6RwbrC?e6lRcw7KLZ0PBJ*GPs;K+(IVBr9L-&?bE>fzfw2zHl z62P)XR#!A{Q%HUf>X<3IJG06=kVfaC8;2iG*Dk3?S{F404=kg?E^6@$-jXxlQ=#%& zxh&+Jl7vDBH#5)av}Z)jLYh<W-4o$fD3_dc&Rwsav<-d(_TZ_o@d?Tbjb$4vbNR|~r2 zWyJAiB+HxYT_^#xu`6`o{{ZAb8^5d+mCfyKR7r`pubDNPrs;j6VPXx<$o8i5Zv``* zf+h?|U02?z-puLId8sJm)Hca?0SK0nWBVvgsbz#HO@EG~dzl*7<^hCMjdad!wY_u}%9_a)Ff=?yo##(O zhQO6CveKH+E0E3h(I2x{M|y@+2Q@sf={7A=k@%iB4C&d-A(}&BORXd4BvlTGDVjT% z2jSNz3|AUc-4t7Pg}$$4Hc%C4UI`{Sibf~N>9rJ?s7>7gePVRrfb)Z#ZIWDr_Q&IgE940yz zeYHu!1w>a(D0$W{3ymFc;Tox`)Sl*2oJ(;_9H3zlaOZoA=cQ(>PG-#(Sqait)BoP@ z*;z`9w3#z+KxIhVgUc1?3qgA4d0P(al(hms!D^F7`8}trOWFAespE-tu5Qt9;sWY9 zonc-{Y`oqfsJI=R(~<2>Hzg%0!3u*=+UFw{rDwaW9EEu%29IYALGtRLaJrW7bB!)c zxI9Twv{G{{akp99V~|O;)5Yp9mJuU^h#1GiG{4l4?clBudM=Ssy1!(kFZMbV_D80G ziN9YP)@hu61ND|12fem-CRCte9}P~AgYFQob?K|an$wFFPLxg_H}?reU2q7$`okao zaQMInJ`g_s@sEekeeQF&pMUw6e|dQAwb#OTeb;w|H@x8uq6YS?jbB<^!N_1<&6p$P zPoQ|u)))SqHDB9#A~H(-2t2vr9u$1qb0_&-)sTvr}tTgOP4Uv(^7L>qaSfu5rqfRr?qt-%eC;&S^uhy*)+;JU0_wf=(EgN|vQbO;DdyLrk zQKxD_38%0q@1*<_I~lokp?J{F3!W6!e-&->K!)UY(w`XxwMtK^xFbA4;HeA}oPBcNO0 z@=*-*n&>XI(lL5|%Uj+OKKaQ{cCX;=Z-0CE&p-X2yLM~qE~RE+^!HPDVdz2f5I8oD zOMj9lC%FgjnHnK3L0r`BkK@2$6&fle+=Rp70AH)l>Smy`ddG8j7;lnUx}SxCt_v<^ zj)GGR^p&8!*||?YlMepVY)VVNO}=obVS&8Wlb1~SpJJfb%mr=UX9oHPiQB`7DwS3m zkZpC~k_bM-UUzgbt_C0E{XOE=J}X`qmav-ZOGNuP#qs^$|NTS8`Lpq% z4}B=S?d@+1pZ)AVS5$~@EaA?*Qhr9v`3;S!F!KB`RZmHD-;*uls2e8MOsZ(Cb>s7} zMJ2`{vvlB%teVL_tRLAgk>d&)S^!lMX(|g@KGKi^dES3T8?-seHz2DhewP&+)^byqQSBrTH<@N^I=VMeTZwkV$s#n%Mq z!=Zcxad$GTReYqU5CeWmHRh&qMTP=C8N3?hNn_+;bLlVt@-M?L{n9TTTGl5%@rm&D zue}yN^{G#F83ad81Ik}BXh@UvyL9yvk+X7XmI>`_)moqKg-&5!>iG$U7kIZyYW8q4 zoEPDq`6HMe3BNI!FEiR!Cz;@m%VCZez|P;1j=TVm*PX`PNuW6Km)aR?9N((o@<GDoX=1^!NTyo)l^Y4&T0NETg`!z#o!c^Sl3cQH|EIb zWgjd`vdg8b@10wxzA2--rXa(Z@j4t)iQ9WaRq0>ANwZMBX@*wqJt8Z}z_HEOd`49p zotD{$R9@>bL+a@JD0Pd&eb%ZxNTd~WUuY)h6%p$dC}3#mL=0Vwc*^k*r_aoT-)#f^ zRUK%VA`NnaV4-Bcq+$>}70`nb9>xw9@zAc>)7IOYUP_tSx-B#oG`T~GiUNqrp8UME zJy{d(J`BY$fYiT~JmEdigeFIvIoh-cw$JXPAN}a`>3sh4pAYZ)Ki?I8=4XCp-F?tE zf~wjN3!Z1y`liABlpH^2ysHyrGK1awJaCN^?lpDz(Se%lUvIKIx_!7RUh^GjZKv#S zv%R&Uc{I(X-~3{md89#y!4_kTj<=({FBoKRJO>;DM^_Ck0ubxJ06s@|=(;zd!c;i}WH z?HUVL80&8bE9vujpDL(pC@=#S_za3<2IQ~ej)g&EZXw=B_TF+w%jg=OwNd-~zyEu) zvwQD*-}}6t-AOeI1|{b07xD!;%h}fw751I(g2wH}rqs`Uh0u*h5Loa8b6p4BNVCNTuPL{F_^gUddS&QI!p#B2>@22CU zloInE#Qc_}B?ZNg3DR2!7`<0Mev1>aMF=PLQ>EgLT2xcT4mz!VI0x9Jlkw4tPHuM{=4iz}|`zrSs=I{*D4GLx#;@s#Pj!z)lQ<4fDx0 z$CFnR@s!>q&o7g!7{HQ-I*c+`TpoY>yU^BCVL;2`(75JZlMwa;r(5pKzd{X z3Rm%XqOIhewSTdI{02`N*R`dvPTmS=PfZjeo^@;96qno_nv6E9MMZHHKTZ);ie2b@ zlg@&C5BZctq9?}W!Aq)|os{UIu(-A?BVP#%fizeVn|1gicC=T$n`n`xfNRhH5z6Cu zfMJx#tW5g3mUHmZ>p3%Fizv#mCx$QZqu0I8UW|Y2V;>7|eB&F#pZ@8e))nme%KIG_MXB`zF6F1%7;Q`EW5fTgg22gLyr(mio0-&%u6r~24IyX_QaL{ z=j*ZKeqtw^d{6GJck2A+IA4&yG3Np$Bgp@6_&z1SQIge>$}>&>npI1;f&MBO=S}G6 zKEsDCQT}we2A+qj%|@I-YS`>~bnb)d2o?J|c=R@{6%>y}zei6E9YjU%ced!7|L*VpF1+bYZwep&@Q0h{0qKC8 zJ(pTpxB4E8%aiolV87OnD4b!CDsenTl8_Z+jEY{td9#NUtiA9UJ+Rw-cWl$NajkIh zGpg#~Rz_BXwfAXixJ)!^`USlwM8)^+h)uy>4}cZ;@SX83DTz(Me_0v>M*2-nE!_tC z%Wj;H*lY<1sv;nI4V(u(iaNx4T(NV8hJ3(W$WpNpIt9H)IHkj?Wy`h|0mtBc?llD_ z_k=@Vo`dz3mXhj5`Z{@~r99>-8*@N6dQhWN*q?;c$fDI9f@jy>72`X;<2%An{^U<~ z80nqPEdw)(byajuBReWnRyxTXB4urp@E)b2bN(14mBT&5oT*&RqJv(E`z61m$1G*p z_|0yEy~9IHJzm#T-2El?w$IBx$aadSi(;ogdwB8)l(bmgZ{?yrI? zd(}C&f&MZYWofij4_(z2?Y?@!$ zamvkDL*9C?Nsc^)a5T>-7q8_;2|RBN^4mb7jKS!>nn|Nbg|^~VDJ>2YwU_$+KzH$+ zMZ=ck$A0X`!h7EHo{R>S_#;F8UJg-&OT2cTMrpK7A3w&_pR&S{moS-_!I-9>2nAD= ztxVf{#SpR$&1pbErZkSQvSFx`3m?QH%sz$&QwUaw^I zG%Z&d>8;y=D8%wQL*FJfrF0=Bbm_I=HR9V7v#Pl474>{5c~frAfKV0DH`);xp&?Lq z=a;RDpsqLkfAv>?HN5@pZx5gO%xCnSQh}YI4P{cj;1x)O^%%Y3Hq~#XL551dc^IQ< z3mp+Ab(XFZB%NwQgeqAjTgfAxUc(Bpy44}{^V#y?iX+Dn$iI}0Y|ss?IYGKB24iPO z(Xl27wbj9~-E3V~^1`*y^;jF@yd=&X#Uv;mC3u~zs%vZm{izz}3e)d>WNo!K7)L@* zG$y(gl9QG0iC0clL@LIn(+yxm=j1A)VpWMK_Cfj*V0D!*GCDYtyYAe+DBtL5$=Yzf zfa-yu@SQL?^2Y~dIiL8%C!T}Hee#o^G@^sn{Xua~oOxLEyD(DG*H#K(n$xG2QrhEu zvauamJ#XK{;Fep*m_-VV$!dO9eI0ByG4rQX<~`{TE@3c*-F&N@xe&{frJ}&u^8G{| zWb3ciq(%6)1J%nDV;8|T(4YEfW<;lH(|PLAEf9Wb+k!a#SZryEh7xs-W+_Fz^Ab=9 zYR|ndIBsd&ub+#1iq@)M_23D8QijjyIb;VK;QJpRqLd|*A z2wo795YxXXEnp4l`zax#2ZKwe3QeHXzs<`7u%>_bhkpof{omgjKJ=jvRg6d{A?Hem zY`)gAyN3e`90WV{W_Zx^bd~lGMuuY#;;8|?9vi!q_zIjK6=Gg5hJ20hlW_*^g2wbW z-nR%PEv^@iWTqt#Qh6Fhh;N51o>)PboDrD2$3}jDfPc z{*ThW&D>JCBR}xc{Ysj28|Y8bAdmhX%CCmLDs#Jb2`h%R2vtMx4iyGKl@T{1&u19s z&0p~J)0UN<5S%$(eOX;j^b^V!C{gP|@Vf@`JZj2F8_l^{kQ&;4WwuutF^0xgsV@WH@PM?S4sR z8E2q>5tk>uqNfqtRJ4^{dS{+d0?i+Dn0PegMXq>_sfQ&v0NyipbOb@3g&-?+-n6@l z$?9zd;g+daRiMP*e$lpgA8mB_snAwbnA@Ysh>zhC4T?3;R8gm5_Hy}ov_Ar%I6Wj8 z19GG?-P8(3d<0(q1V%Qn{=`#%y$3Zv$T1(g=&zdf>`}co%vx$#t&cLt=ly5Q*zz3- zoA!!2+tjDEWP$P>!e1V~K%&Dox_k)1ttpSnV7bCD&q*X>RD2XRa2Y@G6aOjv)nEO; z;irD;r%X(@QpOl32j}872JQ}sl>PLB^t70t)p!Hl`?jEQiVACRe^E>fpqRfwqws;b zzHoOeI|;DDxNO<0AZyGm|6Fb$ptbxcQT1@6-4Y$nSh%ag8G(|K&O>Y{HPm?j z{zS)MFxy|Es{;eaVy#P2&XA>K4IGoDnl*uVDuXR*RpYBlO_KR}OHoc6^T3_8snd4hWEu5%={ONJpez7tm4XX_LtlL2J##rV7?Bhzm4glkt%2TCGnRRC zj2G)X<~M10jWU6oquGiVJy(0#bzVX0R8T*^6*(o4^poUnJrhycaEDR7CodC%Shp@f*J}{Nq3VO14q<$V;I^4r{>!KC)-#e|Tt4YZCtrq?8N zi3s-^$~BE_d%95A)#bJ-L0Ls4f82I$fAmLx6kdDnweSaj@CU<(!J^hMEP;Mzmf4Mq z0}s6S#I-l@G++Un8p~Q){9Oz|2vx0lQfov&6)N6lj;w|png78=>?P+3Sq2vm2-DXm zGwaB+Z7oC#q;fqN#K~ivLg*JA1jf8tNlTG&jl)9&X`qQ5d zZ+qL@!UsS2LBj`WN@Q&(%rXNqIF(6sL4_KStZ=Xu+U0__m8+fCIqCwcyz2l*C`7H0 zN=^^z6rAkBTqc_FiAhwcMh^Sfd)` zsoFA!y_5azc8d3bHHliSZRudW@VUz3tm}=ztKEJV5uuct$SL~i0U5e*3n@x*X>&&f zM8<+(5Z5{vpU(I|b<5-hyh2mTBT6$RIM%CrRQ7<4-O-ky z#m>;#{%MPmmdxS#Kzedt^YSH*r|go7g9RgOj>kz=6zG?G6*IsNq`G7sY1BP3h-l(d z&Kr&_&VinoIRWzUu{A3ot8JO5r_n3-9@W6itd?Tsdm(aY2IlHSJq-uJ%n-QWG)=f8mlZ*ge~B|0$a{I-RN zXS1Zuh>^5pmgQAvnbzwlNZw5A`L?%SdT`M&QoP%2!$1sAI|r8CD&;D@LrY=T@7Tb7 zvN`h=k+=T(Hx`Z`r^|;TU{4|Bc^0@0^d~Tqlnv+)1#t}&qdwn)KtV6zJOWsF5LzO} z{~iN~G<&Ub!A&V?HV7h;_6x9PUZN^z+yxuuWu+k{tJn^GDr=4oVHE;=&8fyYPT&=y zLd(3ocS)o7u1mg06zBy0)&grWHqJ$ivrF_OMgf+pj$Q)e{5pQ~H-9s{Va8?ks$ zT@a0Jv!f3U#Hv#uQP*#f4k=4p-F?kFq^+q_5q@e+l%m6UcnjxVl|UNhEzXMzI4ZJe*xS)eyk-_sw)l;i9?;rp4PyZCY`J2Bv{I~z-ztwA^ITy1^ z(p9rmN|_Fgjq})VITIpk$;YDBvNY%J@9Fq1<6bE-or-5jAHJ5X9-2|jsV6GZQU*NB z4td=Na4s-v32@`u{)v*g$73`U@mLh9428Eu;hZO8yh``P+(@^rhG5bgKJUyh>QHV` z(@ma4f(^HQK?cV;V#5hcEOt5Mh!8hDuM-xY&Cf95c{jZ)!+oDN7(S`{SB2k5KND_(fSaqOz+cBY?E;U`BNIF|c( zo|c57AGVZy^(Yw!f@?w6=d*s6b`FEkXw52o0LNT)27a%qlggrf>&Tmsf5JtyURj+d zQaVcV3F7;rS=Ieh8Q=GP-xq%LM}KtiIUIO5Nt;``q&QX+h`f`D7PpIJ>%G(AK+l3< zM$jLv$2(=N_y^f|JTO0mALL+Z6k@D2C)imTb3pd>*mp+_<3sDwYMSQdOqxz%f4_d!e$!mtrM|KJ!=drVj_ma8yKX`@0$Cs{% zJ@z;ivhVW*n-!<5c{-fe)Hd2$Jy5d7CtTDKiGBSt#RTs&1x6f&pfyj@j-j_X&C2t+ zJ68bNfkrd-B6sX%lvU8*{?1vOJ$d}}PyclI_HX}oJEJ*5-8a0@Lq@Xqn-c>%CQ%~+ zE<1Uu6^}Cw$hKBO#FY(G7x%iNq(NX_zO@3M(uZhF!&=DPst1;)w*Mx*Qy) zAP?Mtv{MZnNg2tT_sMwcI7pUwQ?8RUGpCoF zU`r~ueb!2xuUbDO;2iB<=*Fx=#l*$MP*G?4!an%ig7K|;I06K6sv?zS7?kEIdJTH( zb7BXdvF>NoxRXLjsA(w<@Bqh(F*)E z&s@UXxoNfCtfekJHAha@ncE|_kAhXCr5Z){dq-mv{UYr>zCO*t(YW8x@ zZJ?iQqxRO=mj66P&Em)kv?vn6x`lL}|DgRbn6B?Ib5F*lX1AUxa ze0s#u9Dnv_e->VQ?X~c`zx%rf6)Vj>?S|(IY_hA6s>tyt~ziVCwJ_b(d)Xqrr`;X#4qKo zBFamWPX9fRt{8Yu*g+p_%(bU)seksfpAFyot=}4c@fUyb@ab5>N{N9KQkiv5`1T-( z>uZB6eX)wF?|>eTMj!R!2OQ)UElbOAi<%?1uVQMcT0^(*mdY4A`+zqlVIz;rYLnGF zp|r=jbebNxwjl8%Z{p$1K^@`cLF*A|NWtt`g{!^GJi@*5E%f}!1fgcF( zeeZia$qt!OM{}x9$`TUB0HgBUo)%AHgQNwzHxs@GW5|1m#e5kGf7?1bwzmt4 zpT?ZdA4ldZ=eDk%zKqlRI|Qin!dn(fm`lBkFYx+L+^xt^jZVJ?N!!fij9seAn35Fz zUw`csVB2RU<9`rJ32UF{)Z}>^_h7(tW{q?TcGEV{-PxlgZh4eL=xM%qjD4%A$Q+>7 zuu`H7v@M~sE3>s?2UB05Y7P$ti#@25Y6+9>+_KP9bFQ}#A5 zi@yl}@-P4LN{FG)f5D*rz2E!2=l}aMjIa2LuLvLbgtnd(JM=ZkwnTk%td%u0m7&o6v~R)_^ut`?;tDYObmRBimR0e;CP<2&mxnXWa}dT=q&U$;}-`wv}CE3egkR=YYYwK60g zb+?!GNsU-XA1wPOGanHCJF6d8F*sHt@--gH-sOir^r7$#-|!9FIDd)8v+Rfe@NNGg zeBc8g=tKIgFeSEL_(18;w9?2(0b}gahB>z>S*&m#G{$IPW&Xs#@;PD(6Hmo=#gZsy zoO*%0P}}nQA-;=EIvPu}c74QqeDQYd3d=RxrvL#-j|UPeEtriP)yVThwn-+|p!F^a zF~6yKBF%Y2qr{}6baD9ID8812mdL_|AorBmDeeZ3K=AZQzh~yj0aM$=JoR{+@0 z=fQx4%rK&PKaMX(y<_?Lug}WAr|ltUG(4%+0s7DDHn?~0LFo-=#y!t{aoL+=59L{0 zP%cEcj!dM87gKS?jyLxEzyJH;o4)Crwh(%W$AA2f|5x~-ANrwi1W`(=14c)vntrca z;e{4yq%Aq^o%`I#oXZ&;9j4Ciw@}8X+q5B>9xr97S?U>j?yO|zAowOuYIckT;{M2FC2T<%x!0Dk zQ`~3X8OsMP_@s0L{c=kzitHZ8K(EsrA_kS#=-S8t7W~6|>e##(lzw3Q0?8w;f42&m0P0 z{ncL`KK&UIU~5cdWuWs!EmU$C8;3)*Xs49}NmJ+1Fo{{` zTO53nh?QK*ike5}L+M~?Fk@OAu<%<7)C`;F0@2n$rimX+ z+)1v9W>vAK>>C2l&gcwObfp_(B2`1~Dclo=iYt~p0e9oPUVB%eS+Rzd=5JGQ{MBFmRUgeg5~w|2@&#zkCT3aWka3>< z-t_((83E=<*j#jvMSCFU?j(aUsFMm*WFh)2)t`&D4Vej z^pSU_oFk~voQPrOV|%> zDE80){Lio=Y5iWM0BNI)|89{fEmiP zVh$Ks$Ye(RA9o$YBMCvOy>`p3&dw5O5$?q6FMn2QmziD!ICQRIDC-QP)lEMdiEdev zu|Q`_y5JRht95sSNXLUxdtN^0>9K3jL&b7Ve$cPQGI!pL5FH<7`-*YiTJL%|^UfXR zEp5uy5nHTe9La;2TE9UUQ;);@GNe(%7dwXPomW!zc^}*P15; z9iXw2PEX@E%ii-rDf^DJdIBD)(0Gs4<5>%~Z~L}y3;*Na{!KHr93Nas^AyxY(>B6A z=&kgK3XT}(#)?mzckSkGX_OVY39ovZmHD(#?knoIN6&)fg%MTntiIt!k8w)fM2U-D z6sP~z_Us`(cdj;P%BsDL$l*`Gc}B4loxk!1I#dE+{hlkWoYlH@TnrYf~)?udQpr7^uP&a4nB$4)`3+2!&dLBtYG%o#Y z_4?qk_P_yyo}0bP?&9??&~2pGL{M>s(thi>WDjSKtYn=I=ddH_q1L2?LI52NnApBY z;*Fj-Ky`Ax6%^zAK_dOq-BD`kBIz>PiMa$^Vn+||$6jT+m*}g5%r_45S;0Yw0!Hsw{x&YF$v&z;6HFT_1wd7eCku53h(&$?}+GcHN#{-vZ1-jXP_as zB0u!x^fg_H^9VOuZ74&-9QLE3Wip+2^+U?oGm{%>UmTrIi;vGAL}I|za;gmpSX%Yb zGOZ(%QKMDPZz>B1;pG4cshzb`SXKP?RSMF*9*c!*qLs>N zv2BHBS$7~D=3&_!rewLIXA;zi>dmaxvjC5m9D<75oTj$T364u$ z@r5dGo?OT{G`OS^pA{$XZ5a_~k>C0$bCD-3DIQOMe$Q}c_97eg{3mpWK;Ug@h}geq z`>jbp7$X7~hQWJDf|$mAcQ2D(W+`)TL`qXex~2UMdJyq8&i9bV```b5TjNR~=NkK- znY8Q)?Wy`JVK-bYc1LN?hLPpKHi$F?h~UU7g`fr!`k@0`lzFZODrS=iEIT1?nGvBU zQ4GQ zB-+Bx%f*n4bG=1Iy{jWf=ui7Sls`z?1}!`jC@^VaXn|H%2t(|)+3VF>^8_VjF$#%nV>^I-#nFI3K&v*ZdldxUyH! zhkhDlaO;_#q!_5@8T}TanpHBg>Kw=q8eAWuKE7%++9?W>%V1o48}k+!cs?)Egc~oX z99hbYYa3NhhN9CG66J-G&f?PJ)k8e)@iLB2fBMto&%NM#?9`z8^=>2rAkTft`y|`E znA~Z0+ZL|!h{XX|7vgrsZVTG7c~JO;UN{|a4@ja{g`q8d=e!s%yMbOk zQl_mnHG4{ByDSNvPiFJA-3%EIZ-EIi&x+ zXi-!ylgsb3azB<53gHmFQ&P9h&#LC3yGGa$2{-?X;cBnQ9#4LJ*_VCUq;YPn846lH z7wQhU#>TMd%`9rQ1Wo2<7BmBG_s)Cv7g4T;T(I*6a_#EN)q zx6$WJ#jFo5C}X>{JMq`WSq3kKd3ZoSA7TZP0fGr-sfSPGUbQH5hyiYoiDeE`m(wNx z-AiDg>zAsvMU{9qs(IZD`em;&1h`gKqgdGd<8w19HNFH{fHBBP8&Q6^JJCQ`I3*$% zG5~lsG4mu%52Lhp$hY*w1_XJ@6NsS{ZCAbYsrW_rIA?tA*M99mx#;M0Ldr8<0CBd; z<|d6*srIH(vgB)+b7rf__n^gmMdi+D+p)vt6o$mipmfqHvV^VDh??8@;r{a`_oygF zXUILIVG}p#0%Wp1G0su`5c7sY!QrT9!&bF5^?IW3XF$q zS_=UoPOE51x7(FN=xf#xuOB}EK0tL zmk)$8J9@2Io&;clq^64}(BG*3=-ysVh>qOP2U_ra*}bjs&B*LP;qBWx-$NPie)qc% zIM8epIdc;-4-$aU+cubAHq;Sm#A&GPhz`&nyI}Q2+j;?7W-5nIZTK@@zK%o@jB38U z8K8ETaaN%5#_zLK-ortsxK6jYDWl931G>h0BpeFbK9L;w0q8J@=jpojTGn-Qte{xY zPCsvwq;?>+5W4WA2Sai1fPKP{DqSUzD(^q5WsyN~7#~21MRI_Z5VR;8dh&eAzRR3j z@PI7l$!}!zv+dP;kUP87tCNq=yI-QB(H4zx$n5Q;$olpMI6K~#BZU-iAAW?m$4fun z@s4+dulbs<>98onvQ+GacAY%KQmhub&eBY_VuiU3g2%E$hPv&QnI&aiy3a6(eK3=A zO*`_TG4(8fL_lSOgLc{x0Y7jf-USm`nGa706w+RwIa=5;((~Ynrnn2n9W;5&#+lta zJ|jsuInA=IEFH-u7f(KknBqaq$~0B==7=REcGY%lxnB?(zgxrNMyheDSxMmX2rOIA zjVtdnJtHn6Ds~v@w{=C*8ithBqM_$JFL4E9nI0;GSV83Pd+bpgU-^|^8Q%B4_jM7Q z54+J`Eh}n^O&VwNGbhlwTUsV$l?0lBIm@gi(}vVlq^<}vLM#52>sDCla#>X!*8`S{ zS-(q7^29QyvqdLoX3tR?k@izxZn`)mb|X{+4l6WQJ-c)i>$GS9i#KkK^O3zUr&O zul?Gug?eakP0YHgi_qo)wP8Exvav$Fh&Jrf?vi?FaeglJlgX8TIykw{<$CpA)SRTL zt6H0?x7KfQv|Nwt!D!9zoC7ot;jb9xQiL1&dSlP@Az8sV_f*Y>H&UV2I>wD9%)zKpHCot60v9H zIzKznBh4-m)va)J2`ZyZ`5&YS^QQBOLPnEqi#^-pyU+U+V=Tl;ZlnHHA7A%%Ul)G< z=YKxD?)9$|NKjIqMr)&6x$>n^j#>6gbZT%cKX1gXwAPsEe6{QVBaRuV33>LFEt`(o zOR{c!cpB#vC!qahDw{GrQwWBY1U^KDv7}$^<;mMk5Sr)-2buQA!9_tb)e#e13As}^ z@;*H-xCPma%(PGH_ha!`V$lyl3B@7jP5=Fh7$X&Ir5szb*EoMR+#~2yKC=<8Wo-K~ z^$@ZB2TRVNNPCq-u^ny}naz-E_ETLEBqLZVJW)J(&NV?8qC5XWCH1xt|AWHtrSLw^ z3J9}LqYM&>9KJaY`Zubzh|Bv7suyNb>ZkN3WX%|$;Unw$HRUVlUB@=iU+wYE|KpwE z1HbTr@cJ(p=jhfrMj}V;LUsxSY=VW-e3N+_PaDu2G@hpTTWi51Sy-NLo$S^IE%aDP z@-R3Uj_9W`pSX5ISEQAmbuUe=C3lmwKVwy|;1TG9_GD;ZZZng|REx&{&)&PceA{JZ zg8O=X+7Z#fpuh^W$A%I#5HYAgk+!B05fKAHCot$yP{AHB&_tPPdWdT;;-6rkfug8b*VOwyzr#MPwf5fkz1F&)ud1HnqrUGs{Pt;`57%lOPlgDD zGU6mg9&ce2+&C{z`o%9oEW1H;4TiG_JzAYXC zbYU)H+MY2t2zJgHMg(nS`PEOyQ+(}ef>`mb@Nt89qm}m43k^_Q8dCc zRKqsZ1z_)-Qb0v>S|6JDi>}-nqqSro1VJ31u(i}9f=JuNFb4uhs_i)QACC|)Cr(q{ z<^i`ue`LO@Nxg#vIh%T+;MK6}%9Z;&WJjq!<{^LLXRgd*6=CZ!4v z{Yvogy7R@q%X83KCjBn6kOa4`Iols}c@5+DDWSAS)DKg34#y4*C($3>!*<|ZcbZUe ze0}OupDLgK{O3P7=@R|01W)0X06&6^;CM+($*^dT=qN9kaWJ0wm5d4nhRR@Cwl5d= zclNSX24uu-+B)u(fwUF~8?Xc1QH@(>R8x?QqgvIcqSyJ>teC)|MB-vJ?;*?j&vek2 z!H}LI5lo_H_u@_9I_G@X?VHO=8>W<*)#8hq^^SEXGi_+@)R3_p5pP(OnxHmSOlMPw z8^0$ChAO-80q7*_DpCI*YmSQnwK=Rf@~O;fJ$VMrANipwpW$rT3;#6nH32X)`Zg#i zyC!kuX!QTVAN;{rz1J`Eub=supZU@||9<%o|KX*yAz(R6cqm~70V4u^mRC&jI<E z*T5fq3Ci~>J?JPe>NBw#dF@^gK<#`iY+AMM?OG8su{mv=hJP5LAMbWIAbdU} zHSz)oZRj)|e3(>31tyGM>yh7QiYa&Vv$2mE@fg>%2*!}_VcDl`JMqYYClgsp{0V;+b;UNyT!%Y`CY}5(pkaO(O zH^oH-@^(6fgjcUy6dHrzZ8+!G-qy6<{Bl_oUh}rYCjnde?(hEY4?W3W{rd9!`n6yC zwesmt|6j@Sr3GgV1Don}K`mPGWj>%FESW4C_I({J{noK`xz$wfCWEDZ0L0vFqw@qL zB6tU&HY3$t`ggx6f)Gq0}d;)opMg;*!({0`Y*HL~fDd zxc6~46`7hC_b;-YKZ+?ttE_W;DVc}CL^^D#u0*LJHxO@CaYF0;5hxqf{)9idJif{? z&XkNhQBl1QpQUAV<;NV^PMnd97f%LLz5mf)|Mg#&|NDRZkMb9P@fTn9YQGG=zV7S3 zuKdntf2Vx^_kX`n)?~ps>c!hV=udjdkA9t-#)7OP=)^2kbPziA=*PU2y&3fLnS=BQ zG`s$Mxp56S`UFjdV#d+uJddbNH>Z7|PiF~C#c31laC$C1kPks{oJSIh-matHU%VWx|%-YthO~M;gM(kAtGDQO$f{IQKfudEY8prg`anq@HH$3ZZ?H$tMXwu zN@(_f&8P^&6D8xXkFs-9GZ_HYJ_y_0N6yp$7E*9rRhKs2SAbX(G^e(0Rgi zzaKl?5RmRG641%7VIWue&hPxr@{j-cj~{vG{D*$%hstmN_HUK1 z`I@hBeOLCjh-etLnZa{Tvcx9wl_p~~)W{#F)u0f-WJ9f-`RPt_VFLR`PwE--6S}bU zbd^vQjm&n+vDE5W68o{JOOrnKX=p>sxCEy0Du61K!3k` zuq65SY6HSkw?MHyX=T2=FM;}1!s9| zQ`~?n`~p_DF|lJ_+#(dSuub>NbuZS`2<;g)F+n~gXSKai`5xJQONm1wkvhbvFZKvN z=^fgFPV5QsOTTFNy$pMBO(0o4Kn+C(Kk%(}Td|kdf&yNF3{En|IEi<8gqQGm6Ao|5 zhrIO9(pb)8OHT{s8J-*BQkY`t(_yZKXO)*B*8Pl?9Uw~mcmD0){_P|0oPXvspDF+Q zPyVE6E}YSAl-hcNd3rQ(TKkj%^@=+%M)cxS3e=259jvreBBCCYrBzx$Q)=!fCx-gV zSG4qoexv%Rl8h+3|J772ep|!j);$*NYVpyj8pb)vG*24rAx{m=JD={$u2VFPa#8oQ z(fhsdS1;cDCN+zSRin7T+jG9}=G%3}E1U@XB1f`56L|4@sSC0+d&L;JL7ZqB>L2Ndo& zWD*Ng$y0aYVs)Y_-)ZTZI0KE9{SkU{Pe_DA&biO;yEy3)#=3(2(crjT=zy&Rh9&ZN z@qUxK2g!PTy`SS+khgxrnBFL>%vL4Ck7T--bTA9#q(g=yn7B73N?>GGIcS{>r&EBl z9(ep3I<|To*$E6>K!8v;n15e&RfbgOlK*1*PsyOCx`W(x!9~(bKFt-MWEy!R^h2XF zvq#s_c)-Hb^DD<;f8RI%-Z}sH$3Gq-eLlQ$qr3P_M+OIQW!mu{gw2_!$Y9=)zMoRB z8mzV?nQ&`ljnE8ojUa{hj=94cp&{H!eVBDn&+1bpyW?;joY*k)6h+fDN5YF?t;&|B zI~#Q+iA_9wJR@LMH^nWUvp@;&U++8xomrqNv>;%|Xnl*83pWV; zjf_cNN)+*}UHU>cBC1TN?@@tVn#yO}^fORMT-y7t_p>tpb^H0g&5X^6vgwm@*o|Mg zM6ie?=9zQ;_;+uAf~}z3>UoSRXq>-}8+YvRKHrFfNcj4ZANi5;Cx7xMjdSiG+(Z*- zh)TMN5Vq25+NTuaL} zD0`oj!&2~K?LuB=S21pA{_*-Gij^|Pw`r&>hZT6{7tRB>3LIO=WhqIWkmw*w@n*`-9{kt|vcQOhgA;6S{v#sS9!CUdqrmua_m&PCIpq>OH* zcM**&ki;o3k+XfV_(-VyEc$^`v}O%$Ta_jJ{I7N%hjFDb%&n!DAX3Pg6<-!l-?CTJ zUZKo!7C@j<1pRppSj143DrDbYAVGC}`v@oS2IC>g<<1kTctnB;G1x>g=8-pGrpb{{ zbwW~~cYRe{U6pQ^)01^uyaKtzpZ;Zi0`w!u4H#Jp;m#+J*sQiE?8P>k6Nz zzCakdH6D0L`JDk2fy=KGQ%-tU%A5;!M?eR%codogj{{)+$V9=hVNG#Evd$P?7>i+= zn-LX=+MDt|1~CK3rLrM$=9T7O@0LO{b!oPcVjO@}6^Oc+WB+@sK{g7st zH_?#^K|cZg0-K~KA777V8Cu_|rPXTOO`l$p?ceOMhwMrO$NNP)HoWzb-bz2>C`H)N zDU3IDT2#35=37?;*(5fV?qTIxCL~3Hp{OyY%QHaWS<{*cR-U7*NY^0s-A9$Ne*}{7 z-}PPJRX+du&o4XYqeI$$qyEoukV^%Rg@Ob=iXgxuCpDmcCPV4g6gd${^v1k;7wbg( zkXsonT(0$vfB|n04&YgGnQuZ^2QIX0e@o{ZI$Sc^e`C~R$`6Wi1|sKR`!JbD%-nqP zOWMRx=A+Zj@8?Brd4xQ{q>rm&8kJ)7bGlH36!+_j29*8d&7EuoH#uzg+}mXfyv`O~ z{C)XxN{wMRs}KqieAplIAG3cbb%Y^|NG=>Vu}*xPLUj3Fk3Cxnx9obHqttYgNOEQ+ z)V$F@L``@LiZ!~V(=I8GsCT=|3k-rxs%D9*A5q6qG6o#zqtO&r%8&inkCp%TH-6*z z9!;^um`j-OXY_oBriD|o&x6Lbc@Cd8Khc=n;g&V0A%7r9DLNfc>IXnaJ>flGU9D<$ zK%C*ja<77&=yN&^oHi@9)@XFp0d(QbQrgh zk$cI{bGVsXH*j&OjMZbupjlR&9^1J}02lGH7P_wj%8%NXw5&O&(jGzd^!)4uq=k>r z7`9*h#a}GH@g?W{u=q9%umRZ0t6X@yPLmF5O0uy={z;k8GJ$cv7u4VXyx z%O1MkF(JnztE^;my2erl(pGK>|Nhr+{^oC%AN|oEjSWgL08UJS8;+>~l;&JW;H`1| zsv6wzBh~)RW+)7FKc((a@)Vuex>|E0j|Zz*2_p#Mp@5s*EXL!)u1*n9#~YY(=(*_2 zYPC#gqb2n7yX|9IJk!&hvx4JVN<$ft5@*t2Tp7%(ruyKZKbA&sj_xHTjfBcQJ{KUB+}TIWicnklq*05rv@7< zCH5F!7^hY)_06x89!$Ovp~OyV1Iu}kw-@>T*vCHh0SxYa-}&}2a6}dzxoBOsOIx8c z-DrA;uG4?mjdU%8Wlvbdy?M36n!963ZzXu1hZ)a8HIq7<395#YDM?*v} z5NDHMa1l_A-=q>>YQ}5xZKl!NeWMzes0csIdZUs?DBNai#Nlh7C+d{S2)(x{dddL$aKN@Zrpan6fSi8TS9_KIgw%2>LwYyWP~cLiig1rH%%4QPjU#nSj4&v_0-u2_NwTuV{j z`5BdpVwk97PXWWq$A0Zt#`_-hs^}iCYKoSFb$4bsBigGC3EhU?O z)WFSwcnoyK&6OT4x37;uBrg)?y8}*V2g7gUrnn7^?X9smb z51>WA=ca$qg@L^8_W8l=Q4%DI=e}CQ1$9AtO3#`Vfm(^Gy$w33 z_8cHj<0uScd#&jD?ac^jzkd0bf4Tg^FZ{wm#-8;f^DN7*#XbC~NdM&NRG70Ns;&iq zF)cN^!HCY#W~dCnNK$b?_hyj)OzGdGoeX@Y@H~mMFOBM_=*;Ba!Rc}=Qlko01H#v? zc)ru4FbX9o9!kOGmJ0JLJrFs$VWI1!_FECkz2mAV!L1@xY-?WBC`pOV+d7t)1&esv zlk_%?2k7VG*n=e=k;!j0FsKl=<+Ds5z%^A1G@SG|&=Ndqfr+SrpIER}t%b0%Rzr1i zV9M0~Fk8RG79>Xp6&K2v8ScZ_=2+5adUP2io~ia-)f>~#rt3q|$iTZ{O}L|@G0JkWO%RPlBYx&BrqYmoyXKS0a%#`zx5M)37v8< zD_itDNpA}Td2JBv3bb7dbnZFC0a3xgr&Mqu11&*x{p>}?uZi=9TfH6*B;7UNOgdb1 z@O1Xd<(VW->L1>9#!H(aGUm{&@+tG~c2Ee|_yD06^RljEl!abeJe*17Xr#4Ep`w^G zd)~R?UG~##(KzSHK3|cr0pRv1E3DstlGL0el(K=Bk{g(VH>CQ2xTtyo?!Ku?9|K1x zPgZ!cv`}XsTILrW?yT;%H@Z6u1+g1B_4V%6DFd-L{H&5CM<;+B zrP)d)&Ux2)l;j~5Kpo8jo-~RhH{OjDg9n#fXlqim&)|+;4O2h&=t!Yi&AWb6TIo-( zUFpx%MA)Ro!~aefrr(HImr+snN+zR~85|4?XZfV&Nx2rB@|ItdWL%DQi>d8(&!xY+ z{~2jL?pyylI^Kqt@0k-mw)mP?-ShP4Ox_cx+YmdckyAKL*WaTnMZ@|oBU9R(zvI@P zk2|EhlQy&ZRaG&}6fU}6`d?XZwu@8ZNi}#wZ_s^x@{^w|pZnbB6zBW|zkgH)ysr}K zq*3um4zUEwynIqXSRG@F<20tVt9j_*QbnMHBtc31Jr1p)B6P<)iRv; z$P8tLre!OjE#k=R`SW0R5DvIHLq5(?;l%!lsg9Ucwg%h=luw6D7yjoTd;MWcF_+b` zO4IfI7)IsG^JEXSr}zkg&!_2j%A#p*rECHX{a@z=gp7*;vsTXy?@NqRoSRodmpxb>FvL3z z4T(jLaFm*?VJ{gPBF@ooPuPKRLY(xi3Bqw+dj($wl(ml zu2_4~IrlYe+ARfR%BqykMTcP5XHZgL!d)Zr4lJ+W>9aimc#92=cyrJ@Qyk9o=5}d& zXl1r~6{>mBkH4qoT0V{!&98D)y&|1Q%XB}G^!eXo_fd)KRl(DYK?4yl{W?ak&vC5l zk{oIpWUE*VlyDV&d!Ha$rcx-hrvf(nq~>Ya&0Ln3=4E(?+C>?A(%R&AYw=WE$&L0(9qN`I5lG zvqEKewBt!b=IqzY%S-v~-~R3LgFpC#Yg^_*#?`~N(5e^>ESEDcio0y$D7e##phz_% zc4Tc4tclM-ymjJ{1}mS_QmYWkI{NiF2ZpVErDplg92SkNH~AveXRQ0`A=^oZa@-T# z_ZrBAL$NKox}w- z%q9Wm`_}z=*vGDJSrI`&0W*ou$ZbVfwNF%go8uk z{B+P}cEL=3X$l%*s&koC*7=m=5RDEgSb>0d>#awun_FHWgQyMLGE}UXEiWNJhscvk zSk+>!YzpD)w=&%!?qLlVgrU?E_4O^^@+}|0;6Cw*Pi%>2i?$j2qCuZaF`nZBZ?w%9 ze)7Y9yM-z}t_P6%S{XEYXEk^e*_F%irN${i*;qZE0vT?9Lnj`6m|?I?La!m_yCW$a zri?5=0_h;K&a{S!DIRFjtQ1;5-Kro-qE+||i3Zx^v$^Y_M{7H5pS<*@y*LR|trWPV ztJQGO+F~(mXHXrK@fHWk>~K#Q0}057up~n3dSw83nJ@^U%(SW;U@K;IYCwdOF7!c7 zoWuzF>EmE3iUR%VqG!3+Bvn??ALOW9M6aRvVK9ON@Mq59W0@8wV7QgiRl$7epD%t% z=zQ^uUvxjq_kG{@mCt_mv*qi*{_EGxZqh}HP+eyQ`4?fYYK6>sf<0Gw5%hJ=OGimf zh<@b}xw{T3Fixm1o9nbj#Pt_BfU#Lz-iQWT)5$4?XN9LEoTA(>GD;>)8aW*SV>nS* zOb~xIB?~`&Pj4)7C{zl(z!kop=AVPUe!)9WT*{m;41EXS2>lQrZLptj$PR+08#UkY z`FBFS7i^Ltd%?%2CSVlR3qO>6PgX`o7CED|xOs18STRog76Z5dZBVCLhrMwY{7|UN zi}-XX=s>F2*-G!6GlFbo6!MJO-h*?l@+BP@mZdL~U8%eB?g34m-CBjbzV>Us_Cp!E z=hsjD)K8T^{KG%IIp^A(gE`QVH(yb)PMP*9a=nd9mCx{@6(FStMuoaj%F=?yuk+|J z`!+u56mWbiECyU6c$Jq)3s&I1PYDahAv>6S&yub|nhx{sZ@+b)v{jiB>iJ?`8a_`3#o!!%Uox&FKIhQ7v5XbTZ9)y~~oY_7^@V>ObF zfrEwa5gEVt01hzXOJW(s_No@u<&cL!CRAt0lK{dF)_j|wh^g8b|@xTB3zdt(XMe?J# zAbT7rb5c=7)fw1-_xXI}%r?0oK@CG^(Jy$3Hz1vu;G>#!q>wTX8BVid;avDxkWEBI zzNcvz$_A=CyC|W6AW$JldZ;Xp^L8jm8!Lb_?KaszkoUk~qLWbNc*6dF%MLnLvgc3) zEQdmh{!yP48o`wC2!?76B=X~(>bVf<3ED$f2$veHXshmCOL(Hkr-{KyoC`>< z6pZ%2gZ|x*D2bK6-v?8%_i|hvlqdDvYv-O3j{oDo{?}70e(Se>Yx&&gK36{VsZR-V z1drwrsBSF%322Cq#JS5H>k7w0Iok?z)m5zQAidM>8$=Z8nle(O6v$7sKv~psD#X*05 z63%>U3yom}bQ1u-#F81DNH*^uY<*+J5{K*6yo?tNoIU*;^Foajxp6+J>K{q+R^ z8KQCC3ojhwjfmT>j9LhX04{AHv3{Vsdk!SOtdyi=oO~cYe?8y@2D()0+vwYjPecOk=@grZK z{NyLgpZ(dNed#m)*F)0Fr+9TSYIt_U*QzNIRGn$sIMI>IA?y%nM9q$k^OxQDp_CR2 zo)Jt#;)M|vRl|Uu8Dv!q4SDO5JByj)6r{Xmn|dK+35Q2jB7E&8RTRl%ZcD!lbRMf0 zOBH>#dE=BjI^;XyL2CAH?PobY3;yo!{*JB}tc;T!=EHU8WRLcHxzo`(VrcW;c)n|_qrhh@${-e44aU6=w;Xazn@UU1z1BMhCm{khS&|w?W z$$Lz3rER%j+xQP2lL?jyU;^^JI0}tg>qNX zhJg)6iI3a612&zVlwW2X35>tzK_1HIlIGq1*pK~K`K!PBtL4?+|Nd|M#{bjucU$gN z8BIabwv%HV=ElYdb`@mWyHrv^wx`dKHY4V)*&RC!!VSji=ldE41)b&5j(j|HuNeN26l;3Os+Tp6+2G zU{N*Zc_e{9eG?9Te}@0`Y|n&tP8f3A!QhnW@jdwKJI_0o3YW~yY8lD5;GC}<*0Qgv z$76tCS>D#;QS`VJ85Ecs4$ddyA)t63Scp$1G&M||^ZA7_DJ>8gi_Ir_7ixXaFq?&1+1v|u%Frh<+4h$(U%fw*d1VHtmwK@OX&_8De?dmM0Hh6`~y~ zBBDpFvd<*T3}MN^KmF4`{V*o&3t#v``PYB_*T?ri_OXwZ|Lwp0m-2Hz_j4c0;(G(N zFB0&3PJyP`YXT-!A)xe|AkuPHMhU(=`Hyni6i5o3{ELi=xU zl{{I*XRxX+3IhQZRw{f&(I_<-CYt3GFDaBC=Z37nU~yKo?VyX2@+rhZE=QCvT@wUb z3SuPrvU(M1(ucEyzWlso4sTQ>n8zGUMFs9N=lOvFj;4!a2^Ljx%%Cd%5iU{w@CZ*% z+J-MZQ4mtHZNPwg#ni9#F|Kv0k+FD!_)+qsN`mgGFB8$?mZB}>K|K^4AFC3Nk%wik zPv$|bSk*SSseIgKjy4o}2R+FHqV7DySyoYy8y`jvV&X0^$OqOOq%JU%=A0`I`it8; zV+FzU)@y;FK;U5}c`}Nr|C%fkFBu;t2^QEfs5bP#B(SwA5D7y=kXh8fBBbgFW{iNG z&-YvynZls$J)*1FI}J-SxWH#G!gN*MTO`=g@$rS%hu+~et4U6jTNo=z*AiktS+*O4 zDkac-epj85I5$1*UX{z{R8Ph1iiTSBx_3qdU%B$2p4E7hXaHX=0|@evUNo~{r0|?r zlq^?O^ruay;9Q6DG!H<8B|z5=9BYd(Na%5}7W)!VMkYuU)=4Nr=m-dUi>k3d7wmE> zI(Ak`;N|wf3P8jRc+``HLI5bDW3R{IvP|FdBoXiDFjs%X|Ik$ZzdLo)i0h<^>*?8; zjZEX;SyD1P!=jMI?(BrAGhI<8QK;GM%4_ph*RQ{5h;pCYm*OGx9LsWq%11Q;qiQAQ z)tJiGTWgE+4}FezEf@4FM;Ix_Jftw^oVp!ut?6l@a3TvB#R|6pZBfJoQ$?4{W(#;1 zIkCJQC?3kU?2SFqjHp~d-Dkk-(&E_^YVrmJQ>I4-#~rTD!?cH|&9AiSfe~*-#?sL& zcj`T5!1x|!laiy}e_>>L{!k>rJE|__<@DC(;|CSyK(fGHW_{b(sBx-1(yu?}uB956$~`r4|<5ZgC6<m@d7M~@0P(ulYn*Sk+Kc(O%)J;?6E|Si zwe{JtStt|<)JYwYG&MdjWDcnmIAt{eKTA@e$ew$6FCv*Vi`{^M9;z`B!WGn-YAs81 zrw>59kW4b?0g3v_(@Yd!gIGQ3v@aRKcfB_8;Ou&Q{4Yr-MG4Pt*0dD9foXWs@%30F zTxVKL9j)_m%=({#=Le518_fbN$|;}AYc82oWF<$@IZF!{1(u%IybtYHdr?A8Mife!%Pe&xg`t=pcOdWk1C_!< z&NBQ_<&W}gLqI!%K&^Eo6!|J`uWV=oYwn2GUqkh z7kh$ZU{GP_(&m#cn`b{2o`z*JXrWUo3@U}Aa{nf^9!z1gceGJWG#Gj*?+=60mj&X4 zvEAb^^q*JDrA6QRfQG=tpAlpqPu+HBY~yi2bzX6AnA3wB%VW-Ie-Gn~$z#iM=3rp4 z$%s6_-`zRU1zN=W+~|+ddN!*qqdO;P8)aU?wXOaM><~sD!{;tV9E_<^u@2}MpU`>s ze(t+IC(j<2jrG8xCpQpylsdkO4m0ZXj?go+Va}F&7iF_KH=7d0QIu?k3LW)tuDPk)cCX<$7!H*nNMtV_?~qCR2FAly*sAXem`P zsfw?yuzFTMc#QYPQ&Cc&nMal=ql=d7L5>SGx2LTFg=Fc>q^v5Ff~jDlNap6DW*QYm~l%kQE_b8X5gq|sg*v6(2* ztxi7f=Mi$;@hq!yi~+FlqnW{^b+(>;*%o|-t6)C81qj6$McpxqZDTCRpDNl{AK zVBhWOi_)pvx2$QAVwSQ5&T3N%wb#*I#@6UabCO5H$!mS(h0y~PQ;}x@FF$0+BgfZ7 zfCj8yxo|Ezi(anj+R)>|xNC%@0!3VcOX5kz7p$!PEa;}YBUs^3!L~b6a>?pj*TJXI z(x(PvQr~AkCtYBV1Bf!msY)7A7brE@ji;*pmXr=X*4X%*cp!`x8H+zZN@$oX0mDbg zk)9BV<5JACW&5(-<%=cQXz<_Mz|KNJ?kvzPo5oh=DP0Lo6(QC4niUHA4w6B&rwq}t zEB~>?n=R%Pw`j!8CdX(r*I<5hb{tR?P27PT4SJJ{_RK*+x#XM&@}14gcaYiW38a~4r@$xw0$wS!l}a`N2$A&`E4)hW%)0KnV#5R zFFa->OnMx3E;6@UvkWG(ti5E^iiL)y?`UlHBRC{y4FaMq>&OiwZ_^Nz9f1z{dnQ1k zu*g=3Wzq;+_x`c1Cy z(RFlOC9r~(AU*6>aEZ171l^>KbSY6`utb*8u=mYNRy)m(cjxz+6efSbvU_;s^+qWu zO))?)9q}0Tdxu2&IUEV$89{mt;|H8;fhXCOKH$a#rj{puY)^z04|Nv7gKx3LogkST^|wHborGCX~=bLbx86*okpI#cWte8O@m-@ z$=W$zz8ya~&%@|z^NHdX4w4Ga=6+zBluQMWKEmUPw8+jl%2`xrJvFb!eVgHR9qgoe z85V({wt8gl%fZwta}AQ{uVzSBkqg!-UI`>Ux*t+uoW~SSjwK4ym|;NWj9GDaFQnla z}QObBWl}Mk#P6TBIg3^{aF)fuc;`DpP zw}phGC=16oNAVQo{d`xRHr{p??`Mg})L*OhA5G>8t{lddOaRU$>6gu~W!#Z-T89H# zdWZL%prc8;%OOLhb(|UOWvN-0 zz{w?qyelUf$pc=Sx}Fn|4jII@CdP1t|60lOs}dj?&UuuIp92qNUua(y0VUn#Bz;oG z8jl7CRmAd>w@=F%C<)^>DE~9Th(1GVy&q!xT;p57m62!?9Q!LeLAeS7zp_fKw)A zr+XEUao_g!>+k-}QMwA-1Jz5alrcIvY*9z(g z@Ng__JE9=!&S$Sa2FwCox}Lhj1_npEau`KbQxv32ZEGj|V;5v6G;#scyFm(0TF(uboM#qAA#F%?r(I5<# zAGszCC(PrhFCP!5LElBS7*UWt?G6}K+U89-JjO0l)5*(ek@P^s1j$uLfDv>I{o^U< zJ+DJTBPvI$Vp0`eWBHEWPmr?HlF*DEO=^5HeaQ11Gb<5mPg!C)=x++$(jexMI9nqP zZy}g4^zaKtBsnNfDn#Ok#W;rQSQceY2F{k5Rp~N>br#zT`Hu(o+Au3d>oapW@aa)J zd)G8YlFsJAI>_s0JIFC@auSzu$LLTbTqkSBnikKE3^1`W+O&fb>4U4fh?1qFPw}iJ z7~?7=*f=>0#@rza8VR?N9;XLNSY=}Eh_qTmdIk2LpNVEg+|okS$|2onr$=c_MS)LB z9o)17Ed^%F2bHu~r0>EatMK5y^D3 z`Q*7bFoF-ch_a7v^JG6p8m%nuB{C7C*p*G`xU1a8wkbGzcQF_8PTXr(LFyULKT}FJ zmaWPtoyZ_+t(LZ;bP7ExbL`5QX}EK*0+MMne#82t*MDj_sjy)vkT%4LB0JVkY*$cn zk#)NO0qINV+L{!Y^MY7Z?}z2K{9ws3^SyrBR!z!dC|W{o#$@XKU(@)t-~2c2UF|=& zFB3R)we!g5v&xEts5@&T4lfp7=E4P2i0ylLk!&+Qk-LXmX{~;B**;(fYH>SCk=6b&5({v(;`%}HE6nam{cw+ zT9kN%wG>;^7MO7rml!+u-V4T?lwh*s#ZzkqAkKd20)S~;)mAJK_rnd_EMN!yY)iF= zGI;joJ;X}|V}fqy6$lg;MnyXLca^rbGuZwp&~%ppeU7yhxm1*X@o_=OaY;BIIidoD zfl=HSN%Qmkd-c;Kp70^B3g!%zEa`YGOxti%R{b>fRPVo|QO|mBnI1S9070*7RsAQr zT_Lus79B_L!RDa$xODT@xwxIv&`g5x#9XNuKxoA;LueF8`4+QtNkG*Dr6h<0H@`;t zpc)u6+PaYRgLGG|Up!Xusq!I+MJ1WR5B>0gPI7r<(0P5Y^OUZQVTkGD4xHCI`5`F9 z7^yTav08R^s&azmM8nL&$|tf(v!Bd;?lU*T^CWV~%z5ryW{h)gJrJXrJG<6Qpv&m9 zc2_)~Ey@$GIpgi*Y2LI0J?F3vTL3mrE!qHuLQ8@!1- zJ12c~UwDUWUAH5YL&Vc}ZrW{&o>$7qip<^sylUg zm2?@Lbc!xd!WKAdD7aR+?yd0}@oslt0grOa$30|~kX60}x0jubF7mp_P60)7GZle< zb~Ow-mmMr*P!5Q5Lx6N%pZmY$l`w#kM|>76#!i=EvsRRofHkS+9FTw!^bwtTk)-H; zAFfgzecQM^1t@(~*DWIp>C&s+g{!1~Oj5CvYF;-(fwOyDg|r$yqx0*~^pGP1KZ@bE z4V5dDZSugccUM@Pv#|&m{-3g|cICiSga4r&=nd^tMPW*dY{75bFvqQx z>HI#{D359OWcy?+dh)@mB$U%*qq_B|LqnbYnr{=gw~lb}X!D(OpnJmLtNJaaD){2U z5OVf?F6d95eUj<#zU1ClpZTj$*zaK*wB;TBymN|8QD+NL+D?4AN8-79Gn2bVcOcag zznFXOPf*=-jatiLLh5s_wg)3(m58Cl2gb%ybXqAqgq$=4wyTXFEs-d zxJ&*LCv@|KQ4RQAp<^DD0MGT;qHB1H?^9ZT9EAu|6S!3DdF4z1KAO5ow>ZN!Hnfhp@b*nds| z2v|wqYWyNjTa`=9M@jcoep4;S+4v_x77%5L#*Rc`>5rqOZY;{EWQ{WU9whG4I7aIZ z3-Ig2%tE9eH7$zA#~fy%+L%dK##ZF2;UT>w&8IZ=kR3#Ms2ts?%6MxONyG;1X0E$; zOuSP7(kvnXb!>dcRMl}oV&tW^Gsn$PM{ z*Et9`M$dpMvmmHre<7Pt1WCg5jijjrIAM zGtJVCZ)b=cY8iuzBzmr68-v@|msI=edk$-Q%_%?1=66lYG13e_u{Z>x>9O~dK4@8} z4M^#;<$3ZvAIsY*r*zy4BM!)ug+vdtb zoGw5QF+ll6V`X!z^ib;``bJr1SeoI*-9r$bmEA@6eb6*5INHwvrWNOgdx<2kItLCT z@!@LP@X%G2tI{t|JOW&>mrWX&#c z&3a!Z%1p#f?9&&6_hSnlOubJA0G^CLkV>Df=K3O*LWOLJGwHoB%EDW8R}OizO;1h6 z3SJFe4vhJN2j}N#Ey`!<65Zm-AU?DDL# zuBtIYwy-w)kB(x1oh`+|20$XOpug}C$!CeF$k^TEu1is;QsfWp6viZ<7I_>&|AH=A zf}(_Jvt;NR{jRR5LWFW>5VoB`feKJ*Q^%gUh1DUBumAOOmen+>X;COoNdDmIGjrPK zn1Ab$A5pFyk6dpoD}nQ%wJ~1@1sy#D{dM;6=naDOQCeE*S3E)uYX_wQ=X=iIJSZ?1 zF<^ML79m3tjEB6)2l@HKrouF1VpXJ!`u@}kXEOG)tN^P9JOLUclB`BJhl89=MaMio z6Z&05gn3HuD@qVBfu^2!qPr|~oh7fdoD3#t&AmLrNBgGsBSA%De^I0+ zBo1X;jIo0)IP*adT#*$5}Ex01Aps*Q{D z$iSKI(oth1CcC^KTcwAhZoO9x=TGJ1_C}}#xB1zB3=0 zxMCbSAC9|}9QZIC4R`X`oN*p9L$ayt4Xno!T(HFOJ7MW(ldpX=J4Af%nnX&mhYudI z0ZSh2oF6wCo$AK9dv8}{&Zpr6@qu65*6e-*N8}Z+y09*mpoW7OtJ{>|i`Y}z>!7PL z_#Nf~U72uu3u8lJbAeeUzOwY9Ku#rfq|Iygf(&qsAxJ|Foy@u|Z(|>wk#>4ga%XV^=wl-;U;#7o;`~1& z@ES@tJSi+8H3jRbObYKwB%Aw0Kg%i67pQ9d)oLmRBJM89`2~QHBwrK5u54NHE`EfzfaEiq#yC1rtFpWxv$y48Jni-wQ%23S z?nYAO#rM5x<=2`QA>6hiW z%EGU{m=_8;TGTk+=q0k0=SBWV4cdB>63SqiIq4AuI7Tl`{0BEImBq$kYpF+DC8)UF(!2G&xH7lLwH4MJK*(9z|0aK&vI(6&5yWN<|sfVZsU;fyjCQU4;EyZ zX&>_VvK$QrC2WvF0JH)it^p@44CtZ!6iFG3Ks?~Z!U`@$5p3n`G2VJ(?bY@_iJc_Y zI_(Tx%0RE)kq21#uGT!sg!COlr3vP$RLNLGw|F<0e<>;Z7J^>E8&1^syZ}gefZG=L zgvhrS!2mIrJB;HL^x%%rH{#r|w4i0AfY!a8loCGh(!O4H9P{BtCeplx|7c24$SIsMq0NrUpQVA4!f;?5RYGIRprb#K!I6HH1;JPSNut~#~ zU@>vzfeTO0&}*O|ZhddQpC>5`fxeBDp){_c&NKXxJuXv}JwW?;?LRDUp!KF0{ z_E4~RAw8rqaJUVL#9)MdFQcw|Cg%{ohYc^l+XIIEBOT^};alr@cFwQz2dwVvWmm-l zk|)`Pb3m!4H;3M#fCPc^%VzW7bT&>GBCG=lt}|^giu%cn_$A_xO3`B9FlWHSLG_sW z0=$e!6ejLWl<=-gjp`vq-RfroauS#jbOqm*|Hedd=T$?A!i1`t@Z&dX=V$0V-`VHJ z#d+l@d^zdVVfA1W6Mp~pur5m6?p7+egcw!w&7Ak^bduU(V#jjjU{HyRT!6>xDgv8( zrElDWI?qhAdain|=%A`@d?LIrwMX|2qhY!`?6Ke>BY5G4yjg0aZ6nn=2lQ%Dmm0OB zPcx*-FS(}C;<*CtR|)KZvYC3~>*XPV(EI#og;PIYwo|e)hz)~#B4%{O3CE{c@k^!` zmWD^in8gqv0-9`3KjI+kGILtAiI79tNSJksaI=G*I2CL9U2eCVTyQ_% z0-^Xi>8*22XF|n_(a++;A=I-s5OsA<&dUX+90{Rz}#d)?z+vwHb+bmi8R7ZHF{^^URtu5-#Kp{#a{ zm9zaPSqSoP$0VWOX@<(O-s^cQxa^TDIvBlEcii}O+c!7zZN~;~p63G1hUrj58Im~U zG`2Es*|{INIVX*4XHo32T$*w&;NQ*CvD=9&U0{)^n*AruvZv>T$~f%1L>(g)GAja} zCqU#mSA5`8&JGvecVdO}2;&;)-Kr7cn~@>czUS|pCzJ5xOqq6%yX4pGOgG2KBSMo` zxfGAtVviVc2e}jqb;Y|xoMzdny!2?}Tfs^IML@d0q%$?~%->`rI&fm0=p>Xt3-;h4 zIvxQVKamXu&+B;^m&Qb<>Y1`spmrh9A&pjICUq?&!Kpc!d`4(41-vqv(i!&Hb#`8_ zDS((K$NATtXn_Y>B`0_ap;hg?1)LC|PP9vrWI+XdkuAKLu^^@f8$yp5ktx_cKgZtL z#LAH8E}Wy{5(04u3ekeGDO7XuOk}aidg@>zAl<;dZBs?bYPeXx^(2{2{!2hh1n&HTr?`-&x^r*<)D@{m`RP!$*sO~HK0bMEjS{8*!j0|4iT@4elV>y_ z*~Aa!Nw(iU=T&LAdb}we3HbhRlpT(N2j4qk?v**?+*WpTjPvz`Zz=R^rIui7Vs~=K zfx}?>jlEUGWw;~E!x`zaINC(Jo;sNYlIUg_!=T@ovss@;Z`n&K^mwsp%Xz*b->9u4kwwn0|( zSNh0J4*;kO*$h8-QfyGeXpQr@;qC(WpVG9K)IB5%J$e3-Ji9gkJ^K4CTQOHACnfp) zeE=a;B^V}N^Bbtsl$kLIgvZ6u^KXUP&m3GY!NDwFa`N6e7%Dvy8dNYSk02F`%zG3| zf}9^)sVc%PYZT(wbaT@mc*Vm!cMLb=mBb$4G(;(PHg~Mex$YxCCxCX0-uWiNRy*l% zen$oH4YmG-%d-hX1je3dHS6VAr^+G>hG52=jwj8M%6py$;T+czLr0zwqz})B(S(C- zmbk_1VDQxi$rI8ibf$_5p|rFuilrFm@^r@3v9!ET*FN*$EfJw;8FNmY;7Ejg$~uWr z0kF9~miECI3S4AKL#7f7UPMqpK^P2_wTG7?Db`Cy3@C(Zj!fzbF%!4EQYyn@;YxXf zo9u2h`F+_Lk_}F%;QVDocCo0*8L%1X3Xz@R!er49HNH(5CbKubhM_0zV>f~HPeobB zMz1jhL~`tc)_m%84xOpoitaoD15tpERf#-wF5pp+AZ#*4bmbc>fJg`D25Wv~np|vk z_*FJhQ29_(&MnM(g|~A)%GOJ|dBloi#=>DMfdQtpt8EBM=B~@9>+E{x5R~iO+74_N zwD790{u*L=0MNjIv; zOk~i7c!tr9&=WXUiiWZO31j!w)vXlKK@7=Lsz>DzVdtMkwF@kuHy_6Dr-tw?7`>u$ zIDTF^>xbrU5l!DihPpF^=)TmTL?sN=!6A3^#8uFdQ9je3h1>6see8#v$*B{=FqJ$^ zNth8VPiPjqZC-rI=q8&-JS{tp%~pZqWaMn5RT{s*sxpBgz{rx+^;0nlc1{)XMvrd6 zAik001A|*59s!rG^yGn`6J7o~38zK1shNoMso{;A9Mq*f(9SZ?5HGA038rJFrzj%F zoLh7hA~;69bB+=hgzbuT1;dOw%v7RIcsXP~q)faq2J!Nm%bGc#rRZg0jFb*+ZC@QH z2#HQQN5i}~`m)_{(*U6Eq&kX8l(nIo45v}cjVU33Y$GlfjYkRu$$5Aj+U(wQsX(7E9zgd(~aUy+N6n251*7s%Ud zxS&;u$s~du)~8GSimR%xlRvS?uChT;HtKTP(w4>c<%~hWfZBX*pVG(G&3bJk{P&9)E z0kg=x&4kY9bl!t0wW~}Qk&Q<@d*#Y_3(;AW164TdAnSP(Dl!EDME}b7vH(L!TRVr! zX59L zj|%U_Cm2Wh(^M5oQLNK=E?Q~QBvKYJU%UvDBAmm7Uv2RO*iJj z%q81SoPjSo_OUUC+Gl^yB^l`B48n9qV@*Mh9m9$n=qZD_@g@`{3KOB8=6b}N^SMAV z2)27}==fYG+zzl<>5D=_tLZ z_*}$i?HV#>D=W_VEQIJ_kv&o2+nFXqq83?qGp6()WC2O%yS}eg^&7!?!u6ac+v)*s zIGAVE^Rtv*k-h;Ufq2DxP8(;=wv*Er*jU3xQ8OqLYixR5!7>Coj{l2v)g80 zUHgXI!#5){EoH#)ZE)%eyp{3PA;Ef%_NA1-PPqdZqwOR>72L|+c}?nAYq5MM`FfF? z;OwW$FQsJ~XJ9q7ob#lMe5?U6qAJ~w``9!g<2;B*ZBiN2MR*|DRB=G|69rJNqK;;F z3#}pE&bjzKT5HAioSU9p5Dg6(Kc>8Nb7q7t)0Z*YlIU=fcGckKHDci`-@uRH9PWVn zp6E==Vnr1PBa$KfUqn>pB|7%BdQKFT%x$S?@yhibB0wH3JLkTOqIAs`*AxjU9OEbo z7PyLwPs0%Q>F1dzkENYmG9q95N|&FV**hv}90DGoA8DeiP{RC~+`&K>=cKb3K)$8! zL02+*F=de>diRjoB2Lwo_L@_MCxuJ z4l`w)B`B3y-mE!)I#u+%%;A%mUNiSKFw>bl9d(QT*5pn#=yJY3W6S`dD^hYs?^?r* zEXz%lT#J)H({OC1_TdVM0S3NQ^kML&d!&%f-~fo(2sk__n)Srzl!QKyla6G+40s#d zl?5{bsui%&4J|O~HmENp0~-n*(*-nmI_c!0P-kuvkBS3Dp`lUf%*5~!(71;Q<1U$E zVbouB-*PU@&oOVfm}2~=ehy(R-|7_6!oMehQ28Ux_a(3y&wXhnPdD95-uyer9Fa6b z7Y#~$HC^U0nS;XHCM0~V%2|w#`%9i*$;}}tSI=P=WHDz^>obgS#A6a?DTUCL`U~b{ z4TL{YJ)`!*;c?4Bg&@C8S(nnRmzLV-*rV(2@t@-)4$f=jv*%IY;H2}6d3e-xeO?41 z^!zz+wuwTm+GRZTq}qqOJvEmtVEHT#KGx_``Wu(a)(V?gPYBIYj5v%puoE_(j;3E# z1jjA*`L7<G1Sf(PIfrB6Dp(cbWJCZZbq%6A$bV)f(d4+*~2WZCaUlj02hTqSzua_$>>@LS} z%-;#Jc$KYk=ho@Qj=CD?1T+~#8QsSNy?lQ5Sg%zq(~ESTo@Id`kCF(erwgG6A`S?c zhD_L-^U6~PhiO2$W^h+N2G#stp0aP3z4N4D4PiFFuGOURJn!WITiOOf?t^_4g6(vQ zGt~U7?vm(ZX1Qi}T0M zv8>w|XbbW<2Y7hverg9QBalX``)`)& z-rX6K(0qZ&`JUG2L^b{?z@2d*-fSukFr#bkzVm$JoUwOMfJ@#f)sZzj9MiUxpbS7$ zm0J>H3`pXvFWx7d>x{>YRydX5hN2jhKE~g9dK|^op|pPxYTKkz47zg~%@Ag^Q4e)u_g;f0CJrd?Qq=fUIeA z+S_nZkSRpNK}oo)8q~ow37GI!qIrNo)~;6(y8K!1$Fs+>2!R={^>%yADeA+tLPl&A zM=B<2D2-kllLa07xv1R74qzz@hRqIvI8n__$6(Mol~FA)vfSCQrz@X#-QQiz*O8r7!qpUdB{ABELDTiFx zzrSPJ5@i$cFT}eFoKruuyYG2xwsWaRdFGFZHLK#~;K?xcX?mSYgc~7in%b$=YM)l* zc<-YyEUxQ!?}k)UZVbO!a`WBV;yiqr#R@dNp^-E!Wk>{J=|TJ-xy^52xIau2(;*WE zFMU7?f)S>|i96D?H#7t#KKm&+-3A@^#M}SI<)oRW7eID5aQIB;DLMbiOL&F@6mT-nuDL9k2{+2o^U#+eYl7ruQz3$hB zz&XBGcN#zsG#h!8zJuO>t%WI}hJr;ey_PBqt|;p`ZY=NFfwYu0$G|5nQbD4ikd^Mt zSD2CYi}wj0(MGgURW%5A5pY1`V{R?QQkJT76}H3L6@ zv9wyNxT+Xg9klbV>h|~CIZ-)5W@;DIlz<87)R@bbP$zwfe|g9}f49a~eThev-{Lqr zE44dU>Y>~X(PR&%c2YtNy-wXp!_!~&y}osC<+iU|S*3Sijc&iR zCBT>gTzI&MsfuCg^C?y`%pda!1-ZF*}B9vtoOY3DgMR9Uz~@Y zj(%-r(7|UIM~fv?f z9#P!^ReOvnRMm_C3V_VyppcZ9r525FY>AQv*pzASs?P;Ljn#Jh8IU+nJjlRl{C`AE@Olb?I&F2Fw!@b4ehiA6SJO&ly4W3^^SdZF3<`Z)_U@H0rIpm~Zuy7G; zZA?ern8)ub%eL@5%O}cn zi8K@*w9x)6DC0k+4o#2og^sSKOmhE<6h>0;V&4!f@u=1`zr^-$7^6hXSGBZ=@{3 z(`aNm@YZ~(XuQ40#4a)hzD5kxVbEvZlMYnSH32ota?Tm>$O!X9cAsW_4}H^_q~=b& zlAwnf1|+&~rz|C^obgS>+CH?l3oJj=@#|%8+Y>=*WEknq^df}G9Vvd)2Iw+0VWt@+ z!DO7vOE)d{)7GAXMB|}o*m8m3&J&0+IZ`s;9VUH!`6czfK^b zwNqKQW|nGNiF5b#yFHLCFc>GK(r=!1hYW?7O+4GaJ;j8m1c8*QN z+OlERjVqpjFwj0^;0>oc5qS>IRJjzCqO?Cq-R5uFBuS+u_DN0jXlxh#*GqGMlSOF%2lrlCiQcuJD_L_#2Hz- zVCvyfuE>7Dx#D%}UK*2|BVK$wT}p}uYvh3&9eXj5Skcqk4v>m>VcS*SU+*Mm;g~>; z$-IFRd&J0cWJiQ^Mia%yo6eQmbKMJ6vl9|YI!Oq$rN3?z?r|HHDe#B%5sxs8al5XMxDv4};U!n>WI5@(pcvz6 z^hRXwHB3&pPGJWx3h33Mt};hwJEqA$**On?Kj@M#dPoKh*+AddZ9a5{ChlFODi#u1 z3sA5rnH+uN^&*59IU%&fFQM#9c;N8t*-8Bqq?2FF5b9ejZ=f{*MIr{~o&!R-g^4BX zVsK|MT2BAy>NGmmWftk#3oG`J&q^jwRu;fmUkJ-FafXH1^2f+1;Fv`nqWPrb>D&Hw zDE^J#eWR>MBVpRNFT+FTpCOOqVI>MtWiH;$5wjfAp9gl%p*Qf*;b-`+tIB6b9>we% z0|Z4}mn<4AQm&DIw_5qjbYj2F(j-X!w%!d`))e1qAMoH^#^XL%>x|Ncqo6 zV`flgpvbna$49Z|Cp~dE=#OLB6u|XNObLhXwkJ+Yk-dZ}HD+V{Oz{!h0vj^JH>zVz z9J#MdlV~-*c9K}IP}=WUNMlfwOaipIeZ5m(h0+6FpWEUArrEpC1HdzC6Cj69g&5<5 za=n03iV25fuUrX51u~Y_ER6a-ZHek85#wl^%4JYG^HY7qMmw(y=bq}7-7(JiVj%4X@QMhD0A=yOQZ34cJ-bvgJ(Ue5DaUo%JGvEe@q?&4TPGDy zn<-a}>x7`*Ob=gY@66xl${eT`R%Xy^l)F7NRMct}hm5&?&VvZ+yO6=D5VG%A-SHv| zgEn&Tnt4Na;>D=`X24-|GP_t$7dXf+V^=}I#W^jd3nVhAx42zF_RCR4fHvN5Z4A*S+BtjA64?r||5lIp}8y_SDU6X3(9oAH@oVkGc6)$NaGV+C_WZnaR+|U&(&z zGWZ9#p%x!BP24D|?=&oG)m1pK{$qswD1R`xlxJOIns;>Pdkn~Yl>NFO$d6i|!F0)c zK7Z9sm{yWS$x=25f-iS({V94@3#GWX@L@b69E*$bpjMc6)J$-;4IrX0B9!(c>>+ErHc(h;PTq?`J zicsXEOiwLS&sdMxCV6gxz{(`6wEk;SFmxT4?P&)LW;9X;O_ut+1c(fqFB-8GZuuWx_y0J(Sjj;-@pnMaYdjEMx}>P$c8+pD z*8Fi-RmK|QyPh_g1oDL?SHynHNbUE!hVLqr125eoE^8X%fz88uBZn(SEF6t{O*dfl zJ2H$Ul-MjuPcVIj1)_BiN0Q1{{WMX^4^c4{`-?S%ie543B6_>g!y~~4%DF-QvdTyz z?44f=5QP^66ebED6Q_6M&t*P$Ez<96 zf08X9he}yg9ytVoL*W3a0AlTpz1(*I(bl_zen~%V1OdQu-V(SxlctJ>h?{ zd25*VobTpfDM_;`)$5`(1r2>GOQ2G8-WrQNBJG;6B~^Z{fdohAT-<3M7Mw~xFOM)K z`N%%Cqh1+8Zl-+f(2Y>qo(h(1fgu=I?bOyQ;unwmk^UeqZ%IAy)ChT`GUj8^8lCTebuf6kG0tKY6jYC^N+e^x;)cNLSr%&b2;9byDJ@E zJQua?p(;P7Y1r{R%b@MO#zJ75Jk1LQk)yQ3| zGq^=w7HQmYh==RvqGE|QHW)8y z#n9`?SCF`O7X=0mJ0j`9yoKLLb3+%6EWU~RVb01dbFepDaR=u=e|?s(y+58C9KmOwaU4()T7S;-TWRK`PlfZ5(#Bcg(cM+`&Ehmz7j~0NJCr zW??#_ydtBC`K7<#_q*9=x_7Cmb9@~p+%9K_X#7{NJfdd9t~=-B4+2v@d)L>gka&Nb z>Wi^#q|O$dUFl;c^x*U?qiwI`0Nuw&8m2be@~Wdimpd86dRB6T8lo!eW`<%0|M5z; zJ}>q}S)l-j!OIPj2hwL@(X-C5<#zalQw`_wobx2i3(qJeiTeZwc}b5Q5|-j1^RMqG zq6<+?L(0*t#dBC2eU10PDqb}YGV|W z8Nd2;cv04?5^WH29p9bMySZWMyK-$;o z>gKlB_zheoaBj#7`x`yjAya~ylg4%GK&7tse{W#-qjy9AMh+U=ZC{vL-GaD>dS{XN zSCXtmT~+Y7+K#@5N}a^`Ir`c_;bm(%&wKv-)`On8(d)mkwIwp34TuN&IXIK&Z%eQt>8e+p=rEoTM^>q znR7NBwLSV!3`7s%)PV&kz0v1dzxUB04!!eIGEeB)JTcqq2qfS$XvyQk#!SO_mUADt zc6v!X6s-hd+8Pjwlh7&L)93XlQ)lR*-PH{pel=`r9Q-!##B^xv>#T~STgw>rKvn$h ztj;QHc`?@9!z6G~mM2BUHz8v=*3iI3+}{0R5?thc@=kyEz$nXDp;c*vmoVX^&0857 zNfTysOGXV>3$q=^%!yjTDw*zbJ<)Z25CoNTf3syEyjLWsGvrOXeLaUXPoa=ZkUkgm zb=Sg7^Gv&_Zg>!g33El8(T&0&^Wgr~=@`ezYn}(n&!3c71HTPtU@Dw-vUk&*qs|a* zw)SjvrCm%m5LVHHeiM>@iZr17<$(;0-B#+o%A{xSbF8j0DzbQHZo00=#=)ylosnoQ z`}}XmM@Cs15v~{B1y{z63C}cntkil_{)f~TGk z5V_3v-qMtH*>oc2OH=8tLb< z=z0Mp;mpwM7^tm|+TyUcd$4YD-sq_moB7%-MzFP@ z7znJ+|KKA$dCNDmD?b9O|eI_sR>XWM0cdpHY-( zWUUynJs?_YmTYaiQKkz)Z-?`(kXcRD@Rwd3^-3^&Rr`7<4RoW_g6?bJ>ocbja#E=` z`BY=s5dKA&J3q4wYVLw`?8YL!b^^7e7VtF`&dA;1q;daJFSM_KaRZ9BLbh z2^v&%Ncyv{^q+nIEfu_n3+5-+n$pA0klM?4hg{IbNU1d4$J*AY}3VUBZisj)b0 z+lfw9dt7kc!rLM`#PoZ=o~P}}x0eN?qnMsww9G4>I&z8A7n(46hM=AGpWOnmK~zpU zfPPFj3IZ&=of4op+*`}tXXYhbN>$l%pDa(wFCJk87ZJ@)kY4c;oaB1^C7QKXeG$Cm zPI`jmTw>)%P*m@noAUO^3D)^@1y1v5>*mW=G-DVRJm!3q^U0*_Jr|5sG4zdv?41%Ewxs#GECzO!)J_a zhfqMf^+_i4BNK78* z#lMhzA?lN4@a_LNNu5Cwq==j4D4|GiEp~Kg!hQ0>nU$0T8J$0x!yMDOsE;FeXh>V0 zl_`==<9VQ|H9C0{V>v>065*V4M@~c}x`ZN?Vd{W1;s>5vF!-K%G{B|bI1?R}y%PUt zZ8M{Ht1U{GvX-sWq

>@kQkSuqrTZXaSiyURgdf_sG(dE+TBkV=T8&v*yf#AZlTm%xmcQy!pNEkLW0AIJ1Cwn+VWtkg7L!>=IwBI~-xNDwh&fB&s}z zy^vX;6GBX*2Rs*BZq8Z9breptzxiyugdrFsRp=gAlH*RO_o1>Ld|8#EwxE#b?}C(# z%IEygmRJx>sC~=|;5g03RfMx=h00*+dvzj$RBa>HI4z;W;-oXGW}ii-cMm)Cw}?B7(%&e1yFL&tE)sA#!dUPkx_oBcf^&4|1nkb#_}YYBuF_IS8`I z`Ut4kADAl_qHoL~VX2HxY+LYvX;bVwPBx7v;Dq2^SdscjYjD8b6K#!8GP-o6R(cm!_Zh738Y$f_Sa zgFt)U`Ay=)+Q+07SmM*>G^O=b1Y0tcIa}y?H=kN zw}Wr2M$ahIT4Rwp%Q|r@6sDX!Z-l|L)bjGhvZGdkt=pvzkQjNBYgy8iWUNkVnBg%VF|l%xZ<@7Q@$ z*j#~8?JlFxq=xPrh}ua-iDRKfERer&d21}E=CGFmp~f9p*~?Y)V-6}c668YveS{4s zRL_JQO43JLeROBoQoZH(WrAo672R#GTc>Wat%P!HUaH6aE`!sYRu|B9xaVP~2kaUb zkFKlS2WIlOI613}($(vRsa+TCm0k6UN{+IVljp2z&S55QPC}*D%;83IaDeSykH3?T zv_=U}0#fkEO|Tvo98@2b;U)!2h42K0q>bl8_VpWe}Bl953 z^j9Z6x!i+MYhZre9(A8hkK;n$AeqK1t!L>7Mn>&N9FTqsOg!gXM>Tl6r8A$Jo=+4xm#!->$ zONGH(+Q1B{oug~zxg=HNyZc^n;Q$wZ$7>&0+Li2Pi+0UqX^^z9_1DPcXW5o2L|AP8*q(t9zm;flq*^qVai`;Fk|uu zN{?v&kR7Q5pS;p&Mt3!%K|oVCGS$5>nB)>Fr)B{jTyE^9u)j`DH135Z2T!D<(|VM^ zia{D+mwC`Rm;Wn%KO(0E4_C3iUZ901-uLNYEXoVd&~_LyEhZ8MG;Zz)nN4SiMiAk) zn|ynuzNg2b5kj+aK^K;YozeXdO1A+sA44iQDe)ssnoH;bK0pgh_2|V!m43Z zuQ}$ANslmnzZMP0oc{cXNnnJ*PWp&Jd8_21kUDV!wuPX(=4kDH*M5gh0E?XXp#wu| z>x%XYawS(S>H%3A=ioY)WdM?hDJjbeQ^SIYa4tK61VJ^kix*&rejTqy1Se`MVo>%6 zOf!(on(^UvMFKGN4|m$Uw3R(EH52bNwa%0m-QiK>ydp4JQR%Epte_r8UF0BRFM1Ru zYSL?S(N_m_DZhO)pYh0Na^ZXZJ!9`9Pj4!vAI=((!l5$3eWJP)rfw}t-4TPFb89n~ zVX5?4LA^RFJksR(!qu@@^@@7VPd;`ErF1+!*@+y|KK;}TqxjkTtG~yH{ZMvhXNY96 zU_9eq8k{l~-`Rr^FT6-wQ!K6c^SqP+k-Qq#!}sWW=*%jQXx@DW_UM9-5emqLMldSJ z>T=PB7Y8|xuGcq8LU0LfjaE3%=>|rSV-~eFH8j}F#~XMW!gbOB4LjBbVI4TaL#dx1 z-PWla_Mybqc<)MPe3q7a6?zdy4(wX&vP8?LKONs5+&CZ{l{Q%7LnC_h|sbG3Z81 zkb#W5Aq>oJBAlM&SBCodLMzIZ}1Wt5|kC3)GJNJGT zWHC46m`F8c0I#$P97fQ=d`LOa0?ko4K{QyFwmzfNEZ3Mi31uYyq&7)1NDlWmejAkC zR@mZ_Y@-@@VI0U=vwknr@Vuh1IK{Seo;%tOqS{n~p_flB5i``1qWIjvMGGo~KOb0eLPn_@;7l%mtZgv9|z%UJPIH?LM?|I^@EwP~rV*L%8&N5fMSGCoc#&2 z36CyoJp>&ps#XAj!Z2$GX6k?hJp2Bz*A|ltnes*vc~1scbeS4ihK4xLwa2-z7~|kE z#~HcEC69_9VaV5r8QbEwBG50PpE;aICT0sSmI+9m4YzK0r3o+iKfs{5a+B&4A$%Qgo~ieV5zJ@DW?$g0zD8tjoWv<3w923Up(a) z4dId?xW-hFF$0{lbAnv46vEc(#MJnUA6T-=taM%T15yAkaw%Bzu(NY6;%O)f!DTrP zzQUj%CWz*zG_qKrlb~_Lb1}pYnPu~s=o!em-ya)&ErUs&?n3l>vRxrwf>t-nU=knK zWatQ1D)y5Slo0Gt9=snO5+3(<1C*;q7)o-9B7%FatbnQGs~#1U+32flzB>K+?A%9Z zOU20lRTVV+^cEO-clr{mWZ1r}$(UmdA%?S&SqToXQi@?ij14)G2F+5+6qgBQ)4}jP z{+bext>v7`TKJsL18jVkpD&lw)X&ERa#|dqdtBfy`O2|nCQ#Ui*^is-p^td$r zZhK(3kR4zxr*@8afOr+dAnLX8mIPIX!Oi|Z7{VLcTW5^p9EMhKG6*Hxu1G8h)nd@G zZ46X(gy@`mi+L{IbMLUABoV}#E5wqI_9p89ES(M)>Svs_nM*TWJPm_P78fmpzJ%PX z5gw4cB9Y*vD;ccx_JSt>mlVTfXn%=-h(2U6tP2U8Lwc2sq|6MD>(y;L{n+*LyaJf{ z_V8kb+&ybZi59ebM?fSvSN_VO>#M9&gg_2KLvYL)z2Iu!`3cRXL;*l!l0#}(fFV~Z zaf|Q$Vyq96Qke{+nhI-!i5y$RYOx4RBT9zrc3|9*Zjm5_*sjw<_b~0im}~O1(YPEt zM;Q3d+V?ife`ENK^MMWYk}rI?oFbc{uVK8x7Z_#2RL6wwQ5!Lh%ED2AG8oexbjTV* zJuRU<4{hn3vi}uug-oWm(3q@EG@Gw6Hc%x9WnaP-E{jLM*qV5KvOtC5b5&RQ~C4RJf@JWca6;M{kr zRvZqx$&KnCJpWPO;jHX1#-ayJRn+&fBHx1cL1($sr=(fzY14;8T3oX{vJ8?^F37gj z^RL0+$|$a%(-|V+IXju@&UqW;z=F*edH*ym)c>EoFVSu#$CYIG`2T-7X1c6}3jl&i zfT^mdoYV8_+Om=h_9c+%+cdISCW`|crUXt7I!S`tr0WpG@po(KOLb%%veQUA2W;Nk z31(fOE;57~kNXv7_=@E|)Pa#cd#d@pA_nHQ^U{IM2AKwGDYXK$?ln->!w3R_pc5aL z0gR*PrNa#Zsv3XroNLP!p4(O%Fg>Yw=2nIVK8^qrk044|s_#EaW@M5#;~6$<$t ze+_n&Xjriu)0S}$s#R6mI1r{pr(z^Cw-k^yI>+ZDYW z?HNdsJ4x6bXs2=X4j+0+A#&8$dOJ zFhX-vu&xg_YcVmJ#6_npa@+JCKy*E$;3%sGLjyJy5quGh{$5dw${-Rg8I$&C>Ish9 z1VO;pGf1HdA_C&eC|h3v@(eSUi4{TPVJS0ld1M6CJr6{gymR0jgGHsq@o7{}#NAH5 zqX{69$$U*s!5)05$o!xH$6)F^-V-&TN77xS9vldkxuehP3Hb&IFa^}`nEElj^~8nH z=T%=iWd`3#>jF=0;1ebOT;Oeno2iQxB^=niOlKDzv+I-q(ZiqfRIQ|U7I}=pZlaJB zYP8n7{h zqs&pl=4^8oq7e*-|G$_Kz?6Hkiu#xd78!^BdazRm(yM2*zYK>&1Lggm{6=IgYug9k z8Ops9%CS@?n_FZ4&WIuWH4qdQ9daa$XM5#%AiU0cq*#@OM1(x(Bo9O-i4qPWNI%aa z;YDJG_h2ZenY)ou38$8g;2fgSgI)y<+j!17BH!*@=Ctf5z`nGuf~RCiMd^^%!8jX> z@@Y`?GbrpIdop9XzOEA1c)@$ze3Gpo5rs92J2{rJ?91_ueFd>Xq=Rs>i`ljc&8VL@MUr(+roOY(SMhr@Ijm z=kg|S;XeMJ->ECFZjhs*sC#aBXzx8Qu9P69a3to!B3!YK-Gx*%*U)Jk*m5n#Sd-_P z6U7${5X_3&_~62x`(C%dxJ=!1cs@0kowSBx7-#aj#8pQ|ZA-F_c>spA_~#7y=y3uh zet($yk_E3Lx)<5sOjtwv)g8cP0yaDFRByEFC9LdSH z<^iHxgpw`!IW8tY69t=k)cNV*Mg~=S!-3!;nlOdSI1fCAw}Hh8FW{89t$=IQz23SU zIyuh}IxU}^Kv9&)TdqF>aX;!Vth5`MZlzn$Dz!so ze*U;~9`(D$*V8+fH?HKsQ%uOT^JYWia4pb@tZnrD&?A$dbeGc83H?`Dr%mHbs(nUd zBikqieGhg{28V7+zy6wch90*oN>Y##n-O#xcbN;wrZ{e;3gjl(*5X(#rQ~z3#ZWps z>iyHypkd2I_WO^exQcULQw1~W&>?Rg3`ha|(ZX_17W{^cKIkewS{|srI~NNo zj2e4^5BnN$_lh9|j+S`*{f}R(90+3Yg~mDN4{K4Ydw9w@hwIlTr&_R&HSuad;bN4l z3Jj5F6Mk4>^v%;~_a*eRX9(dg0xFAF-XMj}^?QPbQX!*{xcv)vi67H&d9|CY-ND#@KE&wc;gMW{8hno9+{kk zuG&QbuI`b|Q9i&!=ji1Qv&ig)-mwi8-34{%fJ-3eKUM^VVv8-b1bv>}`c%Z;tR`~- zqiR8*NAwx=68agkB#%4oW=-PhOmvH3(mUNBU{K14zB5k|{)t~5d;v*H=F!`!j3J5M z-iTAc&OYL9q{#lbP#i$+y~B5%Ihiru9a0iRLMkSTi(Rn27g}FyfW<~^X>RNW5)k~Q z?UA&C+1KL$xzIRY#^TP2!H5~CRp`hk3?Q#hz^Z6tW|oz;oxphdw!f}*=CbtO=RPc+ zvWSxzQ?-((P#-_m+lxH3K!KQLOX_e&(dC??DU^MBorhzFVH<=g+wIN#+KK^hutYuP zlDoa3T(3o`h4Qhqwbc>)Xzj()&B7T0=j^sn{LO!juvs{EsJ!sHc@EHOxL3Rb1nuvG z*3k?mhcVBv>or-Or|B{Ly7|3LrEE8#5+Q|LZCNRVv0u@_uf7|KlVDHH9@pMC&WjbB zWjN|FTGlb=?|t9Z-%rr@b6OznxIkBsf*!S@Kpn>3KwZ*$d6a8}V=|_}a z{$uPxeVuUBylsxS7H!469Tg1&OCG2=&UsPqv>pfpVOm6#ol)^2UJ3HtCL#_-MLi3T zs`I=raTtb$;CPH)`%quf*t+f=U!ss`u^G36=DlHI=Rt)Vps3Y?wQj` z3;9!0R5R#tKQA~&VH(bO}7 z*#s3OWcFC+d|V2}Ru(#LaEJI=_gT)b<`RF|ayc*-PMLfg1Z~JZ&s8BKkL2Z5@fUCe zb=DlBEalEU_9^DR)DK4RW0!UW?Ub*pdo*^w%T2;G5Cm=zatNVYu6iV$+|Es@@Y)xl zehbtpkhmS6G~zjAxKNEVXIsEkVo8ccHX>`f26t`WMV-X)FT)BG1^f_NhB8kg(?eM% z&(I>}DeoBUo^vj3Q@JvfTv3Lk;2G|%1C{GtFVUky@cKWhPtMW4LHU$nu51F6Z1|6d z#z+P^?1MWv+ClO_3N%_&3(5WX&w)qwjSsz&OW}`Xv9_bgxEYA@_H$j{V%7}itM{wb z&hA9`)$#x?25o{tk{nel%jEj%DZ(J9k-=D`%Wch&Zqu{k{(N=1Xp0*cIC(e5o_$WP z5dmkaW%N!;2z8uih_!eSuDf*kvf+@KxK30#2&&&23qViRF&m^qrXeB$>w=-}p!Jl9 zVT!uooRLWzT(o4udHC+eRjerfQwy2^Jk(1>8tLmN;t=e4@_ugBeu{qrD^iQnjGVJM z;5Zmd`{sXCCrs1vzx1r^=Xb4ehXKNYt|04(n0>=l89h+SLbv>)CftIquldy2M4@sZ zy}$_y_)TR!fl2h)^f+R}>AQXgFI)#{PIIV2xLAgAl?12YehA$atlv;E26w7m3_ckE zS$JwNUT6BJ%=w1wI0sHl&^|BQBuU8nm^cu~IN!IdL5KgFN;8k@85qpo=I|r3>-mx=3SLaZ+8I0Px%R4C0Vds5eROZIX@!Z3GaHxzLG zQvnMN#zW(KI_$IKqF*RC;3pKXtA(WzmeV`JZn%uVw_EuhCU4On17toILas~+ANLKfKgF(3ISP#* z4Okg7$KaV1m(bWqb%OEw?BGQzo_%W+4f$4|5qN3);=tua1zTqfkUF*mf?;IM&zu{( zM79{{| zAdftzK{B2V1C6P;7 zBFWZu$$ciT!Si0`R>)1`Zxrpcw~vq@E$CFOcR9Dzr)h}tGiQiXg!@#q@kG)Nk0CR_ zw}Ur!htl8Q6}4v_|8HC*Vz%cVi&p;Z^>{qmg>hmaup1@TeGMn*<=&-f^KxJ3@p>=1 zw=tQicY$5ZB1|0TxWWp` z#B0JacgGlCtNLQ}^o%tTwIku5WQH0$fO~u+I;Zs>G3Vo*vhq5I%w8ZOP#&$6-lk9TPa&?;in|Vrg$?jXT=+p4K;O_PnCil?wQ2TI%1t0q$viQtO}6y=+baIU zZGmk_j}5s`Y83a{w);;Docz2EN>#zq@ zqa*Gx8dFvXRF7GEyw`=!TGKFNA`VCyQ~`@z5i1WR5%%KUe+FC7_9?Z>F7Ej&(=t=# z>G%$=(FRjX>^%L3OgPs?5HiMQG-w5%N_PIFjl7>i_huIeVHgd0 zo%3uyKLM8xJNR)xj)`TRO-UDHZ#45fHROES?`N76kVH?$g<=Sr4P$Ra=V^1O*z#D5 zD}exB`iqf#6Z^Nq`Ib)EsC2q({L36Fj~YXI>5e`6BbVjIf7X#WT7{bwwtk>&G@5ClmbqGD1>77F?)DI*?B1Z1vr% zQwTZ859!~#2f|AVogqi^&wl#C0N8p8rRPNve5ZEv(-rF}Ok z+geH9$hOiV7apM62mv`Gm<}VrR6RQea65jk4?EQ!Jv&`t-$9t~3>4_KOmJvfBH)d#&2z%|Nyn(}0K6x2KG4O0N6;V}CF}pu?vd?pq&Q~&;0}j= zJf6R;xm+tfR=CH>Wd1P@_IvvaF9+Z zjB$N1jHaXrPW@iob;H&EIT$hWilTJfaQsa26)_QX3(U;RQtKQa|IZ)*B(lbR0|Kj?Y+C$KDzDwvqr#nh;Q7~hhSdBYfahPtn$;a3N)Pwc5P&oh16_d(W` z)AC^n)oB$8iwsDVr(nsfcfBcTNFuNt#%`v32go{Gg>{7F)v0qpt%xg~`>EB6(&(o% zOP!60xD%70D5nw=z~l5}XdamtKj(~duIxxGMOaj{q<5|{ zCbc`S|EhA&T49PYS|`g4v8odt$80RDGj5Q@t3pVsDh+%uxyJ;7r$FfV^Rwbq8D^`* z3CZY!a9x)uLrP%$j>4WfJ;dwM%47e~h;Bvl104$q2Y_D4U}NHxaDUsP1(ikj%eHoe zq8TyQPl1N22R@B*#O%9*@;p)%&_-R@0){cE)y$Zkez0J)gyp~-cg2*@J*k<=v#+eb$kTF)W zRGApllc){DPs1bhnQSp!|4we*M;47af&oirH7{?39y*-pf_nN61YZ@EfyE(8g4=j~ zj^aL<1!(6Z?wG0A|0}=mrp_HfU;uN>1#(VP?z`(A9##oDabLJAe#ekj zP{!G!JBmq}Ob}~cUNZ{NQQ48%&=n^i2}&G3URW|>#mt)13Cz3-gX=AXjhcLs^>(~S zmx=UwHmK3>?)~4n;~XoX0*TzBQKA*&5p0DI!}rU%ER53W^<8$AZ)1OPCpO4Xjo(xo z4)zO@=45lx1Z84XWR?6)8y-I5Bk5JAQ^r4#tjTdpj z6?MrVAIf5hm$oe)-8M2*Rq_v#^;X3uE_ zT#qo|$?mPcdA^*Of16F;lR4Pb0CyO_*XCX^0Wk=>R#w?5T=Q&~&R245u#tY6pmm{> z&%qc0&D0H8_BF0Y4jeuzRD(SO6rpw#p49*pXPfi7uo?Cv^?1|bm$F|_ThDDrEgvX@ zh9Qt&P`ZT6HPyJpKEpl;g`OE}*l0o4;a;->zdq<(lr;1`{Y>z-GoFlfrk#s}&ezfD zsWSri*YW1_cEZLJe-`~&F&7gCEf+Lc6Y{_h0Sk%|1^+fGF*AGb z;Zg2pbfE8lp5%-~eb$Z$2dPVphFpxkI5u1d8+eSw22d$4n&@TjInYb)an4`>t<|1=s&HJC2MYedDiBjei&q7T3v)kt`d;4Du z+M6#?5f!nV&%8T8)?$EQ-y~W~i9x$bTonwZEr2+z-2NO50}~@~9V|v5qn~UMT2X_h z@6mrTYb109!vmXCOZT_(2uYzt4{7t}r60Yv%!c%vT^!=M2k$cBw#egLZpWN%Qa&N0 z#KZ(0m-!ZvZ~oAE=}S9KteAxIom4=9U-mISihxZyio@W2Dms%e3;bkwV|gc-^YDDi z(%pNqu(HOK%kLl;yP0uqh()5l0ApchS06+Em6?u>-pHMc%m)&DXLVOSB9wjP6qbPF zT^S32QaQkB3(wBIkGa(|sE$pgMArwD-swZiY9kU1xv2>0qVYsGagda3d*>mxvh zFq^oQT^Fxn1K-fT)X&h?$oV!Ovg^Tzqe>y+-1PbzjJC+&se95yhtkiEN@~9!fbs!_ z?ZRT&1xMs6fFEnUE+(p&{y?sTy{p*(sBksr#pcevktWCY4VK>mYId z&T|M_HI?U~iVxnJLt(vt;;c(pm1k9WTn!_J0>HI`0219MIOnq|+u?19oH==cv(r5) zheDYNBWEbEoy9868xH3G=X>rnUvT7wp5PxBKOZFs4^`HP?@9ovRH%>BG3UH|g3))v zt?0kJQCyte8I7;tsSM&Rl0Z8}yD*$-@Ab)3nl`5VNv6w``o6KO>+)t;aQWjDVhJN0 z%ItgZpEKqI55chS-yvclqNOGfmx@xwmq25Djost|`#E{1yGAI6k$9-2vCNQ(GYlp` z^*=niG#-k9O%O=8E$?$~r9=~NsWL4retCRF@<=Jl zqc~hW2EVqJB%OC8=-W~@H0vUJq$PVTCy^ubk3qC;$I(o6l}t`u;QM6|_73KShCIfI zteX$nz)c|srcUJjAp}`e*tTSDzz@9)@3E}Zfjpnslue=hzCKBfE^kDbU)T2Lr~dc9 zpZUC!z`2tZyg2(D0p6rCt;R~D35g(r!HuM^r~XP90AdJim~vQ(_vluf|9FulZQ13T8lalvz+B&#Q^lgUS=l-@*>7(U)8PPss&#;Z>qDjvnFit$EQ+pL3Pjth0}V z^Nal@hHzM+6yeb~%Ln7Wf;L^Yt_vXyJzVLPBs!8}Z-hn~jEQq90S30`FrfP9Hv*g3qW?=m56Zr-;1zt6@@`3ZQqs>D zIXW@lPR_Xv9fr(VjxXp%Uu4ap8~U%uJ&o<_GL8{$039Krm)DB-1Ls-a$ET%<>n-Qm@ei$w;!to&y5u18*3|9?TC*T~MY0d9GKfH4hM zsw$#qQeYB$>6IT7h8XxhpPNFa_}^FGYVnQG{=tjvcj4@6K&@>Y7ti{j_ri{|N8;4% zdJ;C!d|t_AV%22@D9h2FBAsI==X-`m}`|Mh_!N49%a0y=*Rp@*!V3`qYLWPRwRI+w*U zB61eX#eU=My~;_8VqJ9#v^E~m1&A26at{&o!pB5mA8NBs0>*nv8Jj6exWCg`?2_nh z!w>7nmWK8S08FHAQhpkL>rQ%aPp~m_Fcl21Iq1Cq*4Q#|JS~47aKMStjn9ba^j*l} zL|vNjv~!-k!y?F@?3K$niafq&Uv`8`w@EjFCvs?AyKZa7@DsgDEE6pqrL|v+lf&wK zUqmp8_naksC&5*UcLI`Az-Qo99{XUUXthFu8&uWXk$jHG!Y7;nW6yJ99HaDkxR?Qe1SRTO=kqb@ET{Di+4?swC9w>}Msd@JIjs|+wfjiNB%_`cAtoK0lBU*+N=H8`^8KgY2VCM)tf7~>c zK1eThZo7Q08DbQQw4fRoy09iL$*VF(9%D-t>b^skv6Waf2FS2?o)u9XOdh1wR^d%4 zonUG)ArLMn+e(D-4wtfWa&x?G%rKi%5450aJd5Ir!g5CdRZ01#zm4*uuBaHd7)qLD z%|W7~Oy}Ds#by52Q%)7nug_j4L?5OV3ON-^GmkiSPWCmRpL59PZyCpl{)iSS{i&lL zbwo}b=EH`*^6Rnm@2Hwrvi;&L`W#TK%P08s6QDV}!)6XT=qs-q^kenM2$c|Lw$6Fc zLT}^EDqMdu?}<^&94;fMdChq*_S8Ew`b<6D<=|ac@_H5rZk^==KWOnT>ac0!>j%^glehzY3-E!8F;;@?;%Vuqt;%ulG|oyhpl@)}smjl?-*2JHdA!aq zcWQ!QT4e(3qHnQeGmjS0@&S@phG8CHRxwsfHZ9Hk4|zExiEgM?0H!6KYP?}|(k1y6 z^{!_C6wS||Mls|b)EJ&NJl2GImogLsK(wrGDBXo+OXX%Ov1ot=223E^{)Uuo;Wg9j2^ffhE7mgM>&$ zBHY6kQsvd(zoCkYQclvYB9}WF<2A{1t4zV5Z$Ir}fIoxJ68D`AqmOTO)?O&p{53Sf zdIlX*XC|c4!%CV8>2(z1SI<)$4maWP_WL^JL zjk3*{(;ZHkt{ZU~dM8^J*7L#RnC#Dyyic?q$=W_0j0129_SU+&K2nPRU-R?+h%Ar($0NZ(0)M1_#a+$F~vKdKbot_pXZ2&@EIq; z!7!UwH%QeaBlWNVHjcOg*B=NAjr>#YHM%DKfl$rbn&CNgaMfmHlGJGa*;PmJy6o#2 zM~9PAs!!^=erB9>OK6zc5tIPY;tZU(RR=>37=f!u>Uh|E?8lgh)|AeQ8cFC8V^+Z`Ns z3>V%ir0`a$>t&P+&ASoXZkC4&v=?lIDrdsz&Nq)vf0ShKN~Ia2PGaYLyKHikjp}3u z_xPgcP!#PmQDpenRHjJLEq8EbMQ=#FI1{*r3o_R zT{r(|P%vYswKM8q<3Uu$4gQWK;2?q8O~U|huJ7W^jl9MSFh6?mqvbOs)3|_ zA>n}gK65y;uWr`VfO@Mr@$D;dpGn3=0-Hk9@RbD{sue<~(+`4aO>|~^E22M6!B0Uq z$NrR3g3?=#H?1ZZyfhtQmU~-{`Hwt;DIcqw!?}1``yxyuPQ1z>0qZm?z$8jfMWei+ z0Uyop&`KcL+pAFK9UQeb?SPHb$QsACwu2_EN=B(gbf!vvW|EU12APLytLG!F@MxLb zH{$|Pe-sq1gCkZ$tqk5CY;!y|M)A&dnzI!XTBDgAXIc@C!6l;}ve-N?bXV3JGHr632I$wm`{4XlEHqW!XRqV4s@lp{=4sY$MU% zW=mpx)pUk1+P(+RNF^!~olaTdTuUcR$Ky&FvE#TiG*!>I(H@6X@K3oRF$yxqeT3Cn zHHYK^w&h(1TD={Y&w-~GZ2BQma^j8(H=F;lKxD0Ljz#YfTXvY*gHFxDrDEk0uiVj| zKWOyePHs0U4#qMEpsXV)%oaX;b&|g1A!ioP&Ss5uq0Fb(`QJxdanHVu2J(V-B_?@gCQLDiGEkf$uMPAS&5zrT)`6Ox zgUzpQ4ORY>x*p&Gg6V57b~0gf{>$DQ_m5Y#b)0t^blrGf@G}+-*^x+t;jf9Q3`I=I zY&=@O7+gC#^Gx)dwEsSP=7&*Xb$nKMh_X1k|QhaKs-svF>RmfnI|U*8sy*f6uq38 z(dav+Eo9MF{AjOz7T6hNHUpOGxEbc}>V!@3(pcA$L*@{*JHt2W>*n|k|KC;{Y=O+GJ%VB2yeUTwTg;kBXYF^p>?lpk?>~~ zPB_P=@~<)3)=j|?7P0i)w_n4l;~ks%dww+yGsH2QzE1j_t_#AUy?X0i@vui~F#@%q z#5toSZ4@IG9W6$_c=N& z=p&Ey53J`$oOVflIT=}N)@G2yd&rB}aXAXGZ9+U6?V9lw?7;aEN#=5|9Sq|*_(YWh z#JM}|Pm_ozZ-L6`JeZ@EVY_ryktlkoR?d_6j9{PQ3|a@UkA^0{5Ht@VUj$1-k8>WG z0pvt)!tfnKMh_1tJR_>p*%tJ?Gytap)&vCNCJ9=HV_r8c!*8!dnTW>-D8@z0~J;^NQf< zQO_R|2*a#CY0hAtLaReQpf;}J+6*;oo^5jwto0P#JtWJ{Dq3CGmpQ+ud@iEc(u+AG znXV8FLRQ2=rLG`p9%Wv6zYc_j^JL97CS@mK z%ML{H7^<}-U-7nK*T~0)wHjXR(o5y|92!;1&_pR zm_(%k)}%;-ZMBgpUU2RFpnUs>d?3Nq-PE=wx3h!EMieVO%lf<<}0Ec+XgyIj=nAL`Hyc zipZNap@F?W7@!djDj~*wmD~6r1A#K%|8E8~aylMQ!$pQMR)i@rrH6LmWPbQA#^=&MEc=zd_V1J9dQ(CemU0f^k!^>-{{~Ihf;zYASo4O zZgdeNrVdy_=Py!`K7EZrY>54xFp?sp%yH6H2H+b3GWGk*PILF=!>02-uD=Jez~p$F z4Bp|Z{HHd$>;F5=O6(hg?A?J3yo?Sr!BT7tq+oVk-fg5JTBB>zG2&$6U;lbrN=}sA zt`b4^OZ#Rp|I?Zn91Bc7emQy=%6yAk+k)6*jZp0-t^s26YPL_tMh=RGaPYuts~6Z5 z70Dws8@}Q}YgXugmGE?#K?DmWFmiA8>7YT|v#&esNWVkzKBUU0jc=W` zG@VTL)2SmN^_N4kU3qV{f+`E!(}i|v^l-^_g336WpNx|uCD&<2IDuQk+g#D7I(`PO z;D6yQ)S^!2EU3NE)U-ijY()zXL=DDK8jQE`g=zCqts&yw1mWPZ?ed-&c5f`_Cw0;x z0~t*=9wgqRZKE4yz^hA)5_p_k$hPsD^nxuJ&qMDqsv($v8iQlVb9}tdgF&~TC}9Q( z4_uIEM+v?Hx#H2e#qv~~C1)vGcS@{4@5S9@7qdH04b-v!$4N2kKvieCahl@7`<|fG zW@$Idhpl?!uQ+q=53OVI`DGx5*U032K>%aH$YvN5X=vmEw(}7lZag_Sy^?J*fus9q z0~XRXrRMXCW3Ha?@eYZKo^etUQ0PpnGUlP}yPaYtOK|x-1WCYSMpU|xpJW(Dy5X5! zq@7_GFR#;?D3+BM^lr*2GL+nmqKfpNORSRIL8e+?5im(v(iVtxtUfuFoc)81;fuDs z9bF72IcAwI%Y^vs?d2Fs;DPtg`uX}wPUJ1h72H+(dv4Hmk|61bnqrdRW@{4H`Mw?@ zdM-5wz3HRRq!;Hb_78Z_o5WB}<;8`6mbdbge|%>*-eBHwLzqSGABshFDK@eKZNd^? z6GS!e(Poo&BKVFKoM@^nM!=a~x{vY6#|%Ak-ghB2vXiBuS_)R>x|uF5hOupN*)RP- z(-UhDFhyvL_6c#ttq43)uTdP$SurdUfGfUxt;R}Mb5i=w=2v9idtlQ1MyH(UfGNpqT>NRz?cc8h5#$P~W$S5uFHAaF0F-`;6UoEw9eX^Y0QNuec3lO-!-C~`LAl;m zWHfkEFhrrCMyKMOQ1|H$Fl7q*yfo_-n<8Tt*mxS=fT+(jgcOW!Q3y%Dz84P~eoZzw zQK!HTdtKN!`hDk&2&Ic94*&@@RSa*m2NKRxSMz?`F5cYP;uQ|I6;m-L0YY5}BdMK=B zn|-k4uDI85?=smmSW{I3l*1??H2WiUsr!j8`_C2P@CQADn*78>Ff=gm@e&zX!f*ZZ z9Sq>hC}x5ct~Xg7%ih3_XdUtxhsasV8yP|&%V)}>1Ywdrfny?ywW8oT+O1>?6S-0s zPRwcGtdubnEgOEu9PxFZ4$Z9;x|{8E9MoOyn1LPvWDAoB6#_jfh7>-!A+ltJLQ|KdLJ>yLF)CB8<2kTKr`-p_s?-8+Z<)J3oiL2?z~NJKHQXA?0Up zaq=cZW&}6Hrif2y@txwVHA11{rz zUc`v4aOUif56mFx$9V{im5AD%<4n1F4dM44h|$a5%0yM3mFw(qch>ZWh(Hfz@q}~k z+9;Dc+&TKG;vNw!ea6x_p6H1-Bee*j9D2So8Ho~NmV<)xenCAs4naCv4ie-;tkcMQ z_?V3ucg`6Q>w&q~n8-LP6I_DCJ7;U*GpK<>!Hf%c2@jCvSz;XRc@6Ln=4U^JjWE41 z%+McFH|e%&FdmF|5gGj%5K_U?!$HV<4o%m~ zze=v}8tlUUFb=dm!_t<_(1hnApmmE{KfjTO)}Xy2eDoN{TkZLjySGNos$#xquKS=w z{z;fiJ#7xvd#!e^5V%ZM%X_0#rfJ_|G6SC22j&m(o-E^jx&t81XILOQNO`mlO}-*^QOR4mQa*+G_I$PNAFiFg67 z7Ro>y4-&fV34O-4bVm|%)M%PPb6U-SgAIig%uPis8#-!C-IWW=om8QbO+R(zn6o)F z5sy`PB3-08=bZ!mDGpe8bSKyhci#tYEj@Pf{c5w^WU3T{E ze_+1z?+U3?KovOA(Fx<0cvZlxeuggBDn1TzR0OjQ5dZxIiqoE{z{{EG=`8t3%;<^d z8I}Em2|gF(Qu}WXjQh&mU$duvCT`Z|gxT9_V1lVKp(svz^gX^S7MLdeq!u|=eTgae z38jLZRbB#2r6<;C)idG9Xj($)n^yBCfar$R{{`npH!6^$^ZEo51ie%i``j;Nq2M@c z69O(%phJKPqsJ>a_72XTFDE&x{d&vSTm_;d$*sapT>B0)S4;@w3=PjW|fvoZUfy`Aeaf zkQ&eu^Bd2KfNWqm0Z> z%!>kukI^YatSYh;IJKcZS@Q32_RIK9Sa;>j7W@a~>a14vXGa-;sWOR#GTAxrZWfT; z5mgf`Tt631#ryM*h}AHD8ilR$Y3uI=j*eL7)ntY*_OAL>(#mxJv1K<&S*?~wsL z=FpEAb-+W6p9M6LvDJ0`)C6*3u`OR{5&7aRB%V8Cf~_Gmtt;jHDevTa8b;5**pozx znw*O8?&XEx*$Zv#*J6NAF*VJFTtePE)O+1QUS=>_aF zO;W)Q7LuKPYY95!nR)o~5DwZ+&yN^#Iuu@t-4ke%xUfw4gvse67 zu1;u#5xVoB5vK~j>(#TIrrWUe9ZaC6rr@FT8aW}i9r{H@vpB)H&~@NB^y4as)xqgz zlz6jt94!Nudpt4rAZt|zXUgW8EdXX-PP6YgieiKBuh^N}0>K$12vxb)gj{^^+d11j zh@7YepSpUnBO}vL3rHjcT{f=8?Rzthy_@$s7Lc#Hy0&);9LST`;5>rxIR^f_h3vEo zO8AJJL0Oxs)K=Hcc=E)#!o5qvTW#?f4=fkUTrL#bmjBh{xa;}t-3KCpMr(9l=obXu zaUFKaNS;#f^7A#kcg7o~dlfT~@lnpD-zNtyw@Z>~JF94B_uz4y8Ss&B80I4_2~8^0dxxwD_ z?i}cF>*&*8I|Sh#nF?9vRroU(sfT!v>g zPe68+w3x+|mHT{7$8VX;8A;OU0`Ou3VjZZ6kaClI??&oss8-po$(L&volaXD`c&=c zJX9LPo6lw2Ks@$c5NY-kreICuxu0-d%Ohj?aa5^eLBKV}1$S+NGQBqRGISI_eyA!2 zj^@h$(dX5J{%$)@a#w*i<8~v?nAc=77PfhS*d^-v7c6xIR69I`sf z2T}4vO)aV_P3T12vqEW%-%b>S#H_yJWcXCdmqpG^e~R)B?LK&qV=Q+JI5fS{f#fSq zj%4z6BPA!DlJu>yh`*&+FcK>IQY?$}j>cVq5v#IhAvd zE9-(nflY@KJw19W6WcupDB*l>qV&$*NtVdQ`Z)@(Z6+{kqww8Wj6G2|`s#W0*woaLL_-XfDmGn_|;ztP}XW>V?Z_6y#5)u+6~@O^*05ywQirKnu9 zy@@5^VsiaW=Xdl7IN zO;VL9OG0A!zw`X2N5U%90QLyFN4Tbr;^S;NltXDyq3N7KU#o42smO^Hb^ZT41SFY}bIf(jCBM;yZ!(Q? z7`-**ud&m9h0v| zMTvJhB;xuwHw!YZhLh_c*!y8S@C05W?-syL}ez zs?aj}Im#nK$6i=OmI(6*^pdw<_H_l^q1^>?&1?%XjE_)*-xyH)9@opzH?yOlK}?{; zrvhp5P&*25J0k6lPhFU_%avcvIG&sA$f{C?gZLG%@#J)nBj2t}L5U3J^iAv3(e1-q zjGOs6(kPJFPV8ays}<%TQYr?<({1(pY))}#2uKU6frf@;{CTkGnd%*-8=_i%_n~P!WO{v~Q{xao%p`l}04ov98gu?mA zTf6P~4ZBU)zRPo_?p{nzm{RyM9`Lpy;;h>tXvL{G;AmV08*4BKoh_DXh$A`n$VmHS zeGN%+Tn`;GJ<$r&0gCCauV*5+jmjLFTp8p+c5|R1hpXeNCEMO^k*u&OA|PS7w!L$t z65?GM)h13vsQ8cesb98vtm293cvN1ZlJT{CRCJV)e%PIPa6IWvkPv1k^*{~0DCjh@ z?Yrx`TMQq>@V#t#vc?xit9`-F&qTeGJ>eE~5ztDOF^ z%R4)IUrEhZuJ1&WJ(5oCWw9R(;fN#t&`(h0Y@IykE0~R<*L~q!xzIBSJ{dGW*aY=& zps5pU6cRt;`ab3Pi6KOf>%q|Jy`G>;^c`dIEVAY-`bMQ#W%Os5``GKUk+rldWDe?d z-?hZc6fpTY-HSPJUsif%ok=ms3PGG+1s3b&k2-lY~ErVfl!dcrDP?^;iDZILfo7AXWor5oGXt~z-4|?%gNvg zE%4~dXLwqgoam!NoEn2)Z(MtRBe@QB_9?ATqKhIyGY#_yc_3T{vl1<%Q2%g~eB8)B zN>}V|Ib}4;cHb3+)Chn+HjD>`rC~I4C@cTjV(N+yn7Wt4*E4EE6BOa0G6T_fDWw3! zQ3lr;1*>-)Tmm}`YKSj)7V{D;o!l{C;<>^7?D8ao=%*p;qH#9H>Zmj2`v%4Z87O~r z`gy|$#Fa!e$ws{8d%xSaEMvakHOL_ng-s=!97bn@I+ux z5G9$P06e9~s4!xttO(W94-92;vh?0*R5sNsz^ldN^=A#v)qMxszyra!aIk|CdRK$p zlT>q#kEBjdO?5ORo7ouK#qt?51wZil*}uvm5R`k!m60HoUl6%tJYWZ%(zZU6WldIL zuY15Q^>;ZWxb%7s|A@^{Egwc&TWNP!D|mqm0Y|ls`*4|*zOo2-wDkyVNc{?^2D1>t z5uf#7a)2>$aEdRc7>LNdW$*Vfihg;eJuBKdzq0$-W5rEe!(v9EZsO(>`j6($USf_s z(ub_IJ6l2CLFW+1ujDQGA$amgKey}7IZ6)T%SGFy7W(24psrS0J|)WjM+wqM|(o-lU1L?jlouTe+5 z3`WVHUU2liNuFqp=N|i3XxeTY$QZM+GAO(qC9jEGW5Cf2@FrJfCBAo_%bEj#-g&#F&2T zYCiP8^=c;HR#Tyf);Nm>$N=v&CI1b82_gjKb%OtGx5t0S9hg3=PO&n#uxWXKNO7Tj4#vf5jM#Lu_YC9 zb&)mKi(HvTT;3N$B_uL{R^tdPCIFzZKYKe+$PO)x{k5>(vvX%E20Q19|HPOisnzFv zb%p?|P`2?MuUhIcdLe2Rmq0-h?`Lq(=r|_EbJU8!Hf`6ONdy{I1^DAJVwu6MeMGOt zbH`Mq1o95g8ZU#m{2wS(saY=(S%2Ch?YK`kL<&lA*{r7L<(@Mb;&yac8ISPY-hV z=#ja4YZ8)~m8#ff0tWJM|BWn3;N!+lF#qYnH9=OAn&eHE_-M46+GkUNQJm|5UXa>- z+>`gDF6q*#7cA*iv%ktlVqu6k-}97EpF1?^ckd%tJ9IP#QDRN(%{!`Y{yWc)78&{H z{|6g1>5+v3;6h+rB>#G=rHL;P{yUOV-#gEb_}JZzvw7|pUS>9u&_H0^HO{FAR9I3n z*?souFY*Z&>TJYiO79O1T1!oP9KA*C(uMVhh^2hjUve5w+P}wP{okqF3sAKx~zk3T@f5S4Rhu> zle3m9ExD=%I}v;jx88!wbF3;x+d|!ggAAs!&4U`w;%IR@7F2+|z)yITp%#CcVfyR< zF}r@LzW5LYY1@JDp_6z&-{*I*F?}5|Jq%y5)gL|HtTWl`CHARx|xECfbxD@FV zf)e8F%94TpTxEu9# z1RL@a4E^2czJ$6yR8-K*AJn}A7?nF!APGco-sjnF>}IYasPm1s5zmf)z*ZhEznPk2 z2*GTi$?gncmeQ6&)dekEi16%qVmwiZ6BDv3!lc2nkm}jP9nv-i^4K+7)D%VsxN5o6 z%yGAB&XViG>Jb%WN^oMu)z@da=m3dAst^6{MfbX_ajpxDEZHYyW&dDUzM?PL5&=`F-cP2ZAM%)G-gOl~0N1G# zkSRzK(g!^JB(zX7h?K$7p;###Rr?{zzTvn?@+} zu!*Y^L);e9!CtEZ=G9{RqCVCT%>IsZgfd010)!)WNL zxv?NqqOaZa9!`}96VAoHef8rDI*3zV(5o3vb*i=^0ZIsfs|H*U9bIWWqHeRE8x3#L zGs_)-B7RHJ4VcMgk>6G1G4(#Ua8a{;tJ3qMd+Y!G&#yXyo>x?j&n<3!#V(cwLr+}> z!{h`4=1pwO9&_67g>CWF))+@e6-|@Opf&e9*kXBQAUQ{1-HL+Yx&2xQ_are_$BebU%xDaYp7+WP#>=JyBi>*&NxwT8n|Wd>`iLY5(2|(ZvK>gz6wN zwTY4otQ0DOC^(Ny3GJAg=Wi{USnLC^bo=|`Esr%%zi0iXj5F^rYDiNJ<($JzIItr5 zh`#QXY_;&WfPk(XrteD;hO{UEQgniL1ar+lXMiCP0c{*OE+~MFgRFy#&!SG&Y3j$P zp3~rY3JsjK@hE66bc|ND2zZ-I&UVGaGzB1B|67K=?b^@qQ87;tx!9fEIs8$YxXZ6& zp7#96&UvJ8lqQ=(?8kc%7%vRrTZsMSI2fCet?2?U?3Zijl9IfZ-jS-@V0M|tY*YyM zh%K#1q@w<=XN~AP93v~Q&_M8$FyM&Z1f>2rbpg`N`^OdB3% zL$@)&v+E@U=e8ZQ-n;NTB2;WN@`3-s#W7B2y*y0uMo`9~Z}qR+9iOcDpi>Otf`5Uc zw;7+)o8Ra8{QiNkSqPa?bDuk>X4>L>6G#X%z8FJhic}EBchU=!*@6^)$?S8*!p}nt zapOf1j=4GK@t}EMfhi$@c3HF@-H2B$9%68no)xDb!S1h5-1hyRju61E&(LKG#JTYY z3_aA@?1YLPtyUOA{^q1vkCp3(6>Jnx&2Kn+k4X*T zr&IuaAqb0aQv5TXA6+(NVi0p8k$8ltbHiTq{cvJe#;)dI(GayC^3d4}m%NRV?rHys zYv!jYY-K|t{3sv8ESbWRbgw$YVLXLWUfaFs8Jq`7tIa{`9LOnQQm-M4^Gh)4I-D5( zY{=;@%f?dx$Gq3FLr3rD;WCZB7yPEsX8Y^jTu6%wWSOo1&6!?g&xP@Z_fc?h8}q=U z>ljS9OZt^Y5P~Pg%i@vHVTfnhyHw3jeFMHp?eT=+^<(JsBi!XHRFSxP$Aq^EkpG*{ zZ-)NT=>f$m8_mlv_#CI0fT$tL(9dp1Vf6liTUtb<0U9~YhQ_rPp~pRPPV@78hb`6- zNCtp+355W~)#4b>Da^jOD$0vemd4U=TxQ!7`7Rc8B+i$2EAs=5Pc!blJ+%LWKC{tr z2@ z7O3T|rwbFcdFIE`1W+_hW2j&VD)l@ZzGf6(rd%2Ibyt){vTX@jE}GR~$LJ43R~%*+ z;mL^ep7S@~Ny#)F=a-|gjwxRaPf4)v<9U9>TE`$-V*S5zFkz~Q7Z`-dJoec#XI)=m zU|XHY)D1n!S#PETcXpUx9d2?M&iNU#rWoAW`Q{}l9k7uJ-YHKC2$YNqL}DQ2k|1Kq zukd!l&c_!z^X!yaz7?E^D_D=<;p!9#mO1*el=gf7E*JTNlR%T4uZ$B@EHyk6$}BLR z%vwvR%z>jkdqszwy2pL{hv!$Jb+^#(FXqDJV8NKX?G#NjijYO(Cq2F7r6eh#2OIts zjw-8tQqv;zAK3T#sOR@`icA`cT>qysP%|DktFk&NFQ<&|`6XLb`BNQ%2sAy`m@?tyaG%ZVdr>CA(5p2$xqN0y3+H}6-i{}r z>Q#d0z?SQCf@(Itc!tpGdC{WcKR#FM6{K+XPT0XPRfRVBe!@kti|~CCpPvw|QJ{Di z)IwMB1WQf9cQK`lt(B$qh2>m1;&Lt~;h&(H@zq6|wHwzr7%BxfA7hC(IQeP6Wm{(L_VHyNu4+ioNDK=Mr_-H7~4k&L~2MgSX z#d{7E3md}PSX2rk_@??C`(&XQP+CJdor3G@oPW&o8$?5uI+%Pu#d&;(hLB);Q`jXu z&Qo9(kV@8bD1QsgYV?TKLpHs{Oc%G3805T!oEPuXH~DE_vDblRyK7NEGhih3LZ{<{ z%Kt@D;f^97^bf%_9~`Mv=WZ#Xe&&~Yo)Eq?&J($ZD*s!=$Kvz!`NOQ%2MU1$paT; zXmQ9!5ibN(Tctxk?JAQ!`Z_cxNyrMUH#;2rI(sqC^o}*he6i(9&+Ev4ruB zGvUG%2F3q3TESd7u&UMu(um4-iMWA%U?TVOUo9vTqTSCbt$hRXsV!8}UI|E;l30ya zAnHPF9mp#Z6!)AxCZ^L#ni$F{KhO)J!_Yk}gV%gjmOeq}mp4-B!d>?RndvvEe$$0%Mw@_YU-wNXv7;;9wl3`XQh-MAa z=_7zFa%%@IL)@C(<(;mzlL)q>rY9mjKVzF@sj% zpYs|*t2F2$qceL{v@0ESzMGrRy=PQrQz1lBXC?+6qs4ZlvW8$XlBg$D(!0Mr523*o z@8HFKO}>N0%9eMS8`;AhTJ?JCh08b++0J<#9IF1;&0bFv-_)E0dG9opO4NB--=m~> zsq?|ksyNt5MT0i#obx!g&LBEMY8~qS8&$9f$Hb#&^`_JA&Gg`AS+jJYY{L!c0xGnc z?(ZxR_&&?euZJ!Oho^C5wyuO|&C?PgUoMGZz2`rnI|Q4qBL@()*qB>`Zr|*Y$L2W%8 zjsb#mZml{Se_#cx1G&L;i*DQ{*Gygu8w7iA2mY}Fm4%l~k^g7FOFT*ew*DN})#l83 z|4RLz7REy9vRMcSeI4?D?ejBr&ebSP)_`*=a`*(5jSYn?QbsO%->c~0fzACUb0>NL zefas{HRC4dyse5`osaChGPrIrW*+aL1w<6kxHC4Bczt>!y2wVzARB`m_P7%54{uVNsIq$MT|5(w0j^)BA0TxeQ zF)Cz6FT?L~a&>}DR*u(ELK4Q15mgwa-!D==hdNW4sO7AGq7Ar72}q7l-O7VYzjXpm zeVy}v_4E5=KXB%&9f)Z^!qo_%ON$W)ET`s$9D1z==RAWw*@q(%<_NQlXt72Hwrrg( zo8$~yGhDsb*g44%FrXh%|L_5^@BPW0PXVru#-mja(lZl?7b#z_g19u)j@KAt|&~J5ip^xlq-+^|#O|yrlU9 zPZVjO=XrGVoep3fh}a=e^;KS*i8Pv`uHksfp|Q?ki1VCqNT5^NQ$Ogpn7v3FrJUk9 zKgIvMB0@w;zth>P?!W#&rPtxFdF|uxjslp^{3(UwAo68p7hnu#ia>F^|H{5kfP*f| zjT-`gZEx}%y6Y<8iKQW|q5?{7B~Qzu#4n|8qQ~g1S#K;82eoVdKiS?rqrQHul^q>O z#!)cJIJ1p8)C4aF1Y9Q;Z=*KV7!rKwZJWsqmjD{3MMo2d_J{-`x51y_-2btXLpVgh zEg(TW_%0<%b!GqcSS>8@*`Ws>{lBoNtFRJiM!~YPISh|E<1CDspAcl5+>XUVT!B^@ z(cm4Prlu<#$0KXR7=oIF2XH&DzHc0c&hOQ)6oxCe0N1UN4Q>8}PCdA*_4fOmchK=H zRY#Q^nmEVjT+@u`Iu)}Q_mFsFmnTJ6DVu>~m-&@JU5ai0dO_FA$Hdu46t40h>MuKM zh9M-?cZ2eseHqbR-m~fvvmx}E)y(#@4v8MqkU>tC$?OFqqgnFrt6vH7evb(_5JjtY zk-XP@aI`xfYSvCt0=p_+x)a#i%Sx zv<1WskmvannA0RmHAMk8rB38mqSo~<{<;SwrC9p~ z*PL_LR~xMnOg>L`?*$1ir%n3 z!TWgN)GDUO{zwy%!s*=C7fnyEFFO1AlO7H80(VYDDq~GOBPscwPzkZX_v#P=O($FT z^>xnwE6;DwqrIaMH^XOEGMY?V>i5S($ByODv@P_5aG(|(SYAP@Gi zzJ7|*r9)z9ib^k7^E)0TpfOInm?@ahLR;<07?x4`6NbE6Vgi}U8ORv}W^7k$M}C_N zJQJtn>13f5Az`dspz(V;oW|>SJu2_MVi-n<+Ld*%!}6&9 zp5_In?N%1+R$?B7?JT5xRq$t@^84_xSm2;sI?Zgy{3!h^Tc=~}p9UU}LA27=6Wn*VV!u)wkd0_UHG^t-~b6Jj^h5{q)vGV|sn2Z*$tJ+~VUPviP;+`^96CRvnn zdWKasS`lw7YqA_bq4r8Z9&f+$i`lX2!t%Y{&jZPD@D9Uy(M3pz#NQ-$(5w zFmb_EE&6@_lQ_iBu72u~1L7bt44 zxE@Pr4on)B6X2>-RjICA2M5*Eq~|I%H=e32lSU{otV7pz7@%mtw4s2!g*H(knwNI#LD0 zxNxca&ThkjP*!}KdXbO_sO|uFr$%R!7##)JMx-5pyCA`HkA3| z#PUTK07hh>)}6%o_f-mdtbqBTw7=^kibkU%-^(e!{XU=gBxcZ|_lP;-OFrk^>hG2X z+j2l+GX7awJrB(Y9ksW%%ZsV&CFgSWWXJfZ{Yos}!ZFdi^{sHdb2Ur2VxfA;JT&lyKSr!f%wPC9sUPwv36CT7r6m93^rBxP2De48A-= zOE2n=2&s2b!d+9$CFVw|3Z_?{@|*=t!Fkk)2zqJJiy%8OG1ETJTZo z=ek@0-r$rU6F;D=m*CtRyQ~va43_H^rqtAK>2RhRP>ipS{e9M-Uk@?jX37!^XSl~? z%L|~xObnMFolr|CopWF8QU+(G2fDL+(DytBqk5C=$dRIq0=?=q!cWjkZRM-ST%>N@ z1M^={I#9$2lzZcd`!e!mP}e$h)y$Z#LBoQFr?XC>qLu?=n~SSw`)UB`^0#e9CVH`- zAnjldjj7Y7DI8A zRi`_+XI99dqCU=JvL|BB69+cTmmmqmoP zx-Wk)VI7#*h^Otwb0x;cjPR(^o}IrI(BAK>fc5Ef%;Z<}z0H7m*^j>EBnXsih3H31 zw_VAbc=f;HKZ@t(D3hAER8ZV7^bzyx71v4byA#jyu_2pPia;-2Qp(f$UQNXY3>JA z)W8Ba+gA-ne)Tq^`HcjRMY83&CBDQF7FAM;MD7*>K`pRnDoEYVXYW^4n02&_eWQcI zl{|jx-JRpr4=;bhJ9|F==`oea1)|pu1HBQbxi3gC2EWgL@%eER2H)kI6pTlPO8|SO zURuj-USm0ejfkmRl-t#ZPbV^&d-o14&xm;5#RahKTdC!W~XK_O{mY?Z6fWA{=Dd8 zuEg?bDh5q0MOW)0XvxpxPOfsJsCSEwP)`SQW+(U#j$(3|q(Oa6Kt;!b_=FSqKL4KQ z_Z1h?k|Y!iRmDULDR~spD95*i=wWs4v(k0A9riPtP1sr^+?;PKj$wYy(;oY6m;%-^ zET9=(bPe0UX!Ndsh2lbd-b1_QFmLvm1!EL}DnD=O7S&H;d;M6Uh@%uK)!E61y#oH) zV38|3-h9HHqn+u;AtS?%FN3Wj(#1tPz~OJofo*L3bG%gi6{hajdJVVvsY;wZ_xW^u zz_$0BQ6Llw*cnD*H1+U~Q}y7Np}^;nEw{7e2_*xT)!Mh?uPR`y${F9+M4t+WesMJa zSAZobD!>NgTUf=(IT-CEa8_fH;W?r`n3|XVH%Gg1273V(n7gH-15Ik83OLA09@T_T zuV9>Q?XvP<^$O@QPRilq+9J8%C6)XNbg1in#e$jxSM6LJsDk}I|3@5jOz4QG2&)+< zlU!C=3oJT=0Nc*_+|-~H4GC9nw2-#)4x7&7-`E;N0jsZto5U*NvgG2l;B4O+I2Q~^ z6b3S5>}?)SRwWY+PreD3Ej^9K5#oa{1L4+1mrTRHobDrC#b8;mZ*=avoHNal568yv z2BaAL-Mz3_a1`Pi$>98XInqYw)q5=l6+^G>s+jyJ_oAVt9!xWN!LZkZ3pdXWXpL7$ z{Uo*0DNux#h|hIXji?J}i6!pELcY)U=^b=YKxDRIwPE3Fnk#xOrt!4*-4q43GTe64 zIq%JmVXEgdYeK5!Nuk?U(4Viiy4wY+oClSnM)9!5cdniDDEh%zd@!?$5AY797&)V4 zL2r3#(9tTxtRPzS`r7nkPOO+Oi7fDbcX*H44~C<(ce$5x49$6JA#p0R zt{Aa(`xnHs=Rs!>gUQ%;NY?owGV#1AI3N|ZABD1{uJ@RYu!WW%IQ+a6Gvk`!qbR+(OWU!(}<3~MQ1BUG^XJqnw z=54ARIIRxEKe25%O7?cY^NPi9p8WglK0oD7!a4xoJL`nSvAvBQJK%{JwGefOBx0_; zdfI6$IG207!`83kuNZl+v~U8Vtq=7gF{P|*c|k@s)EEYY0)Y_6eQdf9?~u7~d_-Ti zHfx5$JDGqSC0FcA)n2{4lg0>O1x?i@raVvCc`>p z=kd09jwWMvUO1}>Fw^KIq!yVPeY6ezkxrc6Vb7!(gkZs)pB?qLKa{|eTs?vb2-b-z zj5Nm-N@(Q$yP>=?yx#JeYob8tIkmT(*x`_rDudf4tUdj+Z6wox{Q9=#pJq$j5&x2N zzQ`UP3qfrF6D^j=*OIOme%cC?7YcTSdij+v~^Qp~I9^lP@zptL$F z>(UN_M{m)~?nX1y{JZbUu8WKGOx9u18?$eYGDrk($))P^cCFjjBmX{Up5J&w4x{k^ zpMk&(_UsamV*o8GWn`glw<}|$x7@cOJ0kRPvfOAW3P(7qH_;)9GpVmhHDvm91FwDW zE3@RO897iJ)-OdGCY!{cPq~29#5N|tZ}I2cVzE^{<|oR^IUrC}PFzCekRaAj+RQ}! z`!EVU6t#cn4}~9P8)QYRTry-agPTifIGofbe5W^A8$Zp?Tu+j_62`-Q-jAK4NR0`@ znrS`LJi$hUjmIVQ{MP;;^42y{jTQl_Cm0}hQfYH2(!-2_Z+F2t`2_oBaxF?UwfPP( z==b?P-Se1(jwGg+1_*M-7irAg1Pz}5Mr4_HZG6k~o0)UM7fq*Y##S?DJoKH$x3Erv zF)(0j50K?Bv%Wuh1{~`Rc)4^eVJBFu=rnEgEURDODkE$ykHN?LE+N7!NRdIsO$jrd zrg*vD+HW{t(Bx!5@Zl-MC63fdeB=G~jPOZXY}9W^V_)eqQVOMB+egROG!s=B8ef&b zsQ7O}I?G-oBod)f}WL23zAy z7I%)3kG8`)bRYn8US{j2UXJ{U=2$3_aIAM)|EE0vweRP@%rl(Xh;-0Na2B_^EX>fb zcS|Iu@@9sr%Pm>a7QH+(@^x>cHlhw&k^^3XQ2u}c7d?z{e>hZP!NLKMF6q4x11gmr zvdII@6c0$2z#|m%5JdWUpj=d7?sK8xhhvb(&o$X8-ksB9HAA96`_AVa>0WMQ>^Tp& zJd6R~MVrkKs-is>;03|ac2~=YGSG`5y)rw!W0T??)YA|kLiJ!czc)}N+us=i?DT-C z@AG|rLph6VKu`&6nNJnf%WE zcN`J3P9tG5mwh17;pp#oNm2Bco!0iaVqf?TpRaTNeQt8l16-(%!^47x4%V%e5wcpL z>##JW00840bHgg5sM%-5z`;nDL#4t)(5p1I0m_CzOE>KAxE0Z#cOZyBldkNy*OzFO z1pERxZ!D{^ls#v?GmD;}Ow@XsgKaSk&N}BZY1G%na%S-LUjok2(f$cmJ>D$D*-(%q z=X`L}IRN`V$V_q0&E!wKB%^9+>2^Zf7tg6J7amM70!IA|i(1u_wobkOVOzCRt{g2Z z`Vy*5@dbbTKA-peWEjZ-F~~IqfpS)$bi7RdJ6U_)MU8vp^b;7An4bNPs8^fmQ`4Kq z3wZVWQI1L$v+ooJq zKOk?Ie_bvVDTFT&qj%Z8@3^@yyb$U?{_q0Q@dBwk=cRZS06iF!MrHPt>~&23Oj`o; zk3uTVhZ1-=Fz4^kgJH+BkxKab=ile!o?nlc@e~-dYvqW^t5Ox%kOavtgIPfAujU;N zia1oNP&?RuxOi5^{28c*vP1^n>8w%mmA?;#J?g(Ti-zcRP&wndUB3phWxrd!H!N2A z?g&Z1B7yL9dE16RVga%=4^Rk-h-uvP(E=qTcj!n(=v&K#Cq_4?HpdQrH=h>iFdNX-T<|AdDpP ziCmk_(}qBDmqwr-K|*e8+Gm{}9MMIOuiJ0)U}}#LLwurgO7Kqwl5c+c_xV2eKR;fX zzr?8_5o95&X(7C=+HF(}aqSf!p?PB^rrO!X+@3EHL6j3k?|!$lpNNU`M>QX9AzsKTsg>eg2O;i#_P?2-7r?8!}H_cbHGk4}B{U9UC%3fQixi;F0ZP@KjPr z!4v8^pNxFn^oH}06K;)dqYyAe<0BU*vLVT6)!PAPI|t}NjPfIqN`(&uHjS}!LxT8S`E+r z&bfLDC-M8VqZ16Ixw{)Rjb=^U)pd2F;P00fU8c2Rzzv%+tkQN2&@BXlOY#N2-=g26 zAUKjMP`}UjiF|%m8T8e=6y9XT!2vF2CYRxof}Jr+)i`ZG58w9ImJG%maUb!g;z`k; zp`02Gk^p>EK#=nji_A~4zsXJE?8?{jsqhn&IGu#WepYjWLW1X)$vx-8TxNuQ_YiXz z=0P*xXBDm=@nj_Y)Rce!R{Ms+|AswDq0uNz10CjbHb5I2TQxt!mY%hF<}cBW?5m;b zBORL{f?j7T?FEbv_0n;7>=y&xo*EooYY55kn@YeHy2i^nuP&3;-cZ@5_<&P(m?D+3 z6bqt0N-%pwr^OwT&4Y+qg75Qvjz7Pe)W<|HL>KwJ{kAHvijvUJ*+m~;$xprwQCe>K z;%Zd8c9I%k@j)N>h;z`__%w5lW8?w#w*Y<_GOdmEPjqf z>&X;fn)2QB?N{@mAjYkl+qC)V0u`Bu?KsuVzW#bDgalmxH?sKrT^!JHp-TgXN+bO@ z9TI*9zL)jIcnGi1eGf9JWhzvTZ&XV^J-V-2(hwV`$Lilxy+v^7_@a9@u2b5K<*It3 z{tqfGaE;!~0!KtI?}`9|FbjYtf+aXQ4*0l3u$_fa_Xv|SCoFi~$}b^YCprV>8N>n@ z79`yhVx8t2O7RuYSPF9Wmhz8I55Xd}s(z)LVlikebf23{fB!z;=U?HVd&Ca!;NG&;4M!}2=Eh1n%RAdMGW4nK*@H2C^+xv_P|UGjr={Aa4Sq>hwSY;hSPk?Y&qd{6 zQd`rSlMHQ!6^_ZLQee)Byo+(_?hje{xYv`HfU(c^irgWk|NabDkBOf?ZT`xE+h+iU zx&YyX7Qb%!_xZOw=TXfjRFyfN+}G)9*4qDgMk1rk&K|GP)&kHWwJVqDVDJ zlpBiJI#dhUM!>kd&*kdd%(jR+$0U2q4B%36E0(Xxc(DZy(Yx<#r#~nVDQ}vf<5MS# z=M&BFkoBXj}nJIJPcu6*cyL`IpStP@B(NN5}aYH)Y;LxFuw3f<6u9n8E=4Q^9gOYm|CTk0Pd(y8HtMyLe@Q)p- ztmQfv<#5zO&Hj>=QuZ@L)V2@jWBn+epJQ(wieOr45(fX8Gq$!5RfK87m%h&V_xUeA zzr(nil(IW5Yaxl5ScJ^pC_+~GOV<=Tjb$@Iu~zp(0j)B=G;WO6lD?rh>+dlvTI{`q zf=w%a{m?kach-drB-XyqO8(X9jw%Erh__X1U9K3M7_ISk5Gde;WjhW$X-q?bxp>UE zMiL@5vPwhes1|Cw!t)akI>VuiSzWn>{rI=@gbc%76M857QM@rFvfkxtsHk=JE%rS| z`snxt^qOGFz2_)qq0M}3*^RA|l`PPGy3?zpP{}hRXwJ+q!-2iW=k@|Avy4?FFT2BX z4RHMWzR&kbe|~JS6wy?xIHEAVJAlbxM3&F+u;I!u*hu{!aEa8qi_BRoj!s}QKv9e( z@$6HOj6r3mWBRhJ36q=-6?Syrn)h`ACzcv>V#TGso(n=}Q~rqs1|E#&tMZ2Kh(>x> zt+hRrBs6gAZh3I?vx8tu35A#o`V z%MU|ZWF7+54Bg&C%OAtY2TwTXW-Q)4 zMnh==AqlU5^4*EfITfj321j;I(18Y@f0ez|<+Y&6g#(buXHsUfDdDGX4vn=&A3l3` z9QInmyeJ*{oOQ!y0$D;>2fcuEM`kdi!yzcas3?~Y%r!9v>bi5+H$l=V+NXHt=w#>i zWhAf^%|5!(u~RG3YA}jQhrG6ei5>A_o;ijJCJKQFHg34I&Ho|Q^R6*{-{@chgO z8+*L@`X8j2HqL>MW*NXt06x3u3553W>+hrY$9sEHeI=@9X`FMeC-|ramUcoN9In$d zzj*(kyXnr=M3OX|J-$cq^yi;bRnBs<8NieT8HsaE&T-+hf#+;HynDU;`h0H8`6Kw; zXLS(dYAx0E{j*IdFy0$I=+L|XC{8#a7y$DVp3X5> z8pp2lqfC()=%oHX%<1t)wHjsMsW;DjrLVO4uNP7>QX5{-IgTK&^NQB#H@gey9^&fsAZL~)my zFCPFDEG_=cgZ6-+cQ8mI9*U|hrFymE87muOuj0Z zFxnE$`OX=!-?v~5|R<3=HyhS1)>t%|`eDK#IvCe@Ix8M&6o+>dDYbYCG$-mx%EVYW?O+ zph$H2vvQ`s6p|$1mhriVYsgA8|EWD`Xv?VhSE zrnAy2i7O1lWtU2RaMB{_^cvTT4Bj8=72#kNn{T^G06NIu8caiNrHbz4M9v!So%9eRS;=A+!hwRG zaJF?89;-wcM!xOg2tfeu2%ajw07(wul;q{;5Y;@JfO-$%)NccNs-`xebGY)YNI_l= zG7;@9C65rFI_XS0f{B9W^G5EBn@rDYL~D;NfY}8St*gaQr#g=upKNP#i9J@Wopf5& z3qa;E2lQSnZ$mioq{`Z^^6^4N?1Fux?)!Y7|IG8VF0_@1B!$LRo)i-2Je9*j{eDeoE+yN0rpMs z<5uh-qY!#r9GNhDRQg+d&UtzM;I1U>=Db)~*RkDkD_7x7m>6VAYcZk#w#MxYR=Fa#P?DL(a;aS07^Jdw>Z+Z60TH zmK>trD5povGCX6avRzZbg1|YfKF9oe)y%~WQ~70NT(n0kM83SA?Y|B_Ni`{LuQ1b> zR*F0|V}V&O68!u3&_Lo3+)dwx+{?|Q6ZC{5w~vp$GM`P*QHVGN#gSaVIorcYvVRpr zbXIP_9N&BEh}xj&82SI$yAtKdZ5_yV{QsYvelJ9>%%g%#bw^#ol7{QUpMB|IDjxV@_dV zVyk*N!q8rG-qESCC)@Aq{9Fcem}-Ffs%=w{LT}zs_D)=_VU}S&azr@lLU&8a!7B}1 z8AmRkEL(xjG(7M3`GT_6u8)%mwEq2W|fePtS^)h?|t7_)^`iWmsc>Z#xv8E4l1Dj0D-I9{yi$*)2}LfN_<{%otF+y~z120xko8pxOf!`>P3 zl!NyB8Cf5Q-0_NDOYa!tn6~A`@O)WqFS4uxL#B8@gQyErG)95oxd^)L-|ZiJokjxr zVsZ`H9O*q~=1XVckY1;hEolAJHu}k+tUXOp`4DtQGjUIdGACudcTSJ0GNn(f>KIW5 z(5VR2TfM)B4*gd2b?GMBVw~rFnxpc1Ff@jhe(iI;92sJqtI;>4pmo(R*Bqsjf^id8 zpY5gSQ@?Q9xMo1;IBzqYB5LP_b<4{TDF=|5Wv)j}XK3*ZV&aQbet{!sdtZ3az@{4L+bb-wZ63>M;wz%#CBtj=Ea-yDpM6PHqZV;a2-0CQ(^%L<*h64 zk7I2gIjM;xOrcgMxpqoni%Zh9U%}03D z4V*m(`urfu0SfCthuUvhKs4_)nl!km%S16}L}m#zk?AsJ__YTzUi$(9z5t7muW$Y) z)Jh(0^X$^a(*&!j_MeV2&TDxLqH}S@rWb!&OVhlk=?=tL^_!q}O11K~pb#(10m(=N z`A#h&pH8z<$tN#8sX+4=?mV{IPdJ~+2?aD)5kETFTmM7HsN8C_=L1F7JHC>aXvN)I zGW5Qj1<$ew1jLO8T5K?W zEgIU&Im(~5#s5EJSQVm*Y!Qs8aq%8je4YpUbZI{~x=5Dhv9v7Z}D-8{T{ks@* z8q1+Whiv-(1)t5v==gX8p(;@k3L-;2&>aAbPT%eIIo1(SZ^r}8Df3;s9GJ&08R>y) z6eYza{1Sf^V^$I0l}Yr{B=Pa@us}*j_;hPJc6D41A(4j-lY$7da3 zjc8Wp{<;26c8}yor^;K;3ejXwR!$>Mhx{-{X?Do*MCvKWbRe<%IXCwoC}&SZoxRHV zvFyFDvNi((p+U)qE)JrN@_Zl3F`XhxSt{}y^2K18r&s?J2ydthtPyzxLEi83zYH#X zagOnl*39lOeDW(O$@je-j?{By41=h|f1Y?~^=5B$BjNj&P&styP(xp;@GIcHxxfC% z%e4qe;gpcuqlG26r(?1A{>5k0*wDompyS^SLhl`|Bgg6sFVq;rx{!})JGXg6-Sd$u$h82ygg#>`hz_nx^Mr%K-+mL@5^ zvsjG2AyQ{@uqo7Z&*uq2+<|1YzDLGCTInZ=vK%MtwqY9J+XN zBjY%>`JqE5eYc=inZL^cph&DcLa=EpH;h$;QLJEO5}}{`iuje1WSTgxS0@Bfi7v{q ztGg>W*s5HFiv~0LA22Of!zvSFlD*6L{GH3Glo8k23#ZZVaEP%d>gjS37DHFWIXL=1 z5eSwMqljc741U}}1HU>5mC!oAnoakqGdKAMI*DDBD=vP z@ZpL6yoLKk1xti&lX2IQNeSZ_+JBNFpWM5jFdPTaJR$d3Zo>Pur>?kW(^*u$(; zw6}A^UwQf-D9cdZQId1$(4R?PcbuYeRP&35PVpkze`>*DuiyQt@y>ItXEG(tk3yYR z^v-2<`AodNeiyzcqow;#Q&;68ViPD*NSkG$*KFCFq^n4vv}v^SS=>T;W!c;x#B3?LpLe3JDe3WyC13}jEb7-%)+>V?l4C5 z_=D6Z>E~XZWz)$LFr>YYM4c> z3ndH@s=*c)L4j(`cV=`__5_6*=i;H_kBqd7={=LSD9%404sVECF_Ycxz)cAkiKfq_ zr|}>elGtFi6#=Y%%%;U;Z;kdj`E*U8><3=*ryTibSBmb`5M47KcME@YFdQB*eq2|0 zT@)8f>y8nB=+JfgF1(r-(0lV_sy7mDjvzr+N*diugr&OV79Dg|#6om{sxb*%mFvvr zSv?)O?Wmy41_A8ijSfr2WlciOr@21utv*zrm)5L1-Wgq6ojzH)kmY1n} z)o^-$E&QiEf`%7-Bqx;Pi*6xSj=tOgrFJPf?ne<-Mzt42Y5c4}piD6Xti!4Pj7DF< z`+3p{njRiFbQqLl)X251#t>)zq_^KvCR02ve~XckwMQA?Q>)R#*h~WQ!~k6e3irY< z@gU(S$PxZew48Cr_Ryh24Sg}Kq2ZCR9~kVP63@1V(TU*J6GR`=H-&VmAV}^ zEqBpVs*UOyfELaGzAB93tLC@kkrvL^E*^wM7C_`x)>wr zV5)2^qlZodgUwW+qztr?=&e_#kzF9B!Ce#sTc-sy3L7C;s!na;27J7 z4&6%MX$;8sveJ&-g%`8QG=kGJ=2>M06ps(H+6I+;ggjb` zFpeOFUs?vUm`i5fkZuC7ExV|a4=DP%t>|4snuQ=g+TIKsJdk58F&-!dox)<0C3A(Vmdu^d3k^V}cft+bt z9gUvQI)?e7L-*3xoMJTg5r1kNLv_>U7aQx@BOB(bt#?xE_6WYK;l0KT|H9mpIvu^1 z8Vln3N{1^+!40N|k}tg?s@%ansm~#G`b?H$?2^C(;Yo@$!H{X@>7PQzQ0T(5)i8&R zfz@ME&hnbWhx?EBE?9ON;b87LiKZ;YZ(@4zqTC4@NbFl~MDPYn16OonBldp2$`nXS zJCOV*D6t0x7?J!v&c~rc|3Uh`{LKi+XY+ZVxIDtF+#!&_vJLQty3D;mmuOyFW%R5b zSOo!HEL$p=KB{$D_5LnK#hyMPw3aM%95kI2OxR*xNi1?D7bvzbg|uO}dGxxkKzl9i zW2v`oSGncM)~Hh-!)y}RtfP%AI^|A6Vwx93GkB-8r>at*G{{9Irqnr*D)brhzC&>6 z(4isvn)`FhsM>PXzCQ^8Ar@Om2G^_xx2sSy83;-57Ik9GPHMs_8<$)q85VTspMJ4c zP&HT8*PDty;`wT;TZ7a-i`=lh(x+q`IQMNDJdD1_io$u-`n*YpCdX3_A^;>Y_**vT zk3U&sjN5+6OR*sZyEB46hq(K@C8Gm@vIQodi=n`~dP*l-eKZ%&>b@21MEX{{32$Gmz} z8|}(RhqbFqb9`WMy*=jq54td{OcQU#$8gOF(8KgPoDwtlS~WQUQnvDk_hjxmpV48N z0HOIa14JPy3lYktKD*PVKf_^|xa!?`ENbobzuL))=-pLYhBM(xu2W2QM9r0UVvT^7 zqUXEX66RZoq?BN@O`m>HB$ktyWoIMjn72us9ydeKze_&KT1zPDE<9HIp+k?R z@d!FUhrZ||43Aib-bXVfGe&sfJvyW%A*0-4b!r^=17!VrLyAb*>Cx`f801wZS_OEk zQN&!%Z8k)&C7iEuP9q(6V8oY(#JAEJn`B$-l45&phnKb>Qg-S^w5tbAq%=ZX8?3X~ z(tK7*Ba-WSJsqb|Y)vVZ-kcy-cY^vov?F}X!HZ*?;cMTS&A+i@OxjRK;o#vrP{=FR zj)1X8*J13LN~fMF#vDCIeJc0I)Jojk-fzzsxp?H6T8NGjJ?+q;L(iq}&(XG}FpCyy z3hJqYM56kBA2+}|zW5XAtSTt8R9&n^AiK&o*PoFFVh5RCi^4$0#c3Eh4gaEumMGfV zb110Ylbfi1uj<6@VD0pK;8ccJMtJQ!_pVwCLu(im7o!u}bKUE&lF=EtC|Xh>>=DPf9uFKomU`|64H5n>ULQ z6b_vKI~SS;M~xZh!+Jt*gHMk8p+k?R?CBS#6+Gv;ol!FKs1Qd&u(PikDHhTI zS3DgK!i7~jR#*Jje7UMDj9;_^QGP&s=oB~{d5DX<0pIY(+9k&)6Q`Yg6{E& z3rMuFi#tN=YyVi=6Em!@{-Kmng(h?0A^qDA9XfQ0zU-^D?g8tmu3Z*maW9?usuh{p z36w)i??VdH?J%M122I(&yD*iBcF@VVVQ(9QR!X^`+rBbwh|rsSjE%zKSxO8X@;_#G zNp4R?$K}^fBg8XPzlyN+;!~pLx+A(`pyqOQyanX=?`63 zl6fm-9K3F{q)w#ehF_hU#fY{*(nNKyJpB}2-6J5T!@AyIRb+XId8nbg;=;gI=2rKb z)xF=b92Annm&G_H_joOPEZuC-P`pQq^LR}WDit42F)Y1d1_SS+p5Z! z?Ct|PPu+U?bfgCg1bHcGkUuxc??S~KMAjAV#3XDRayHQ*^LTdL*3ZpLXi{N=&cnzJ$w>oz&4l!!ox4o|fPHT=A zXWpV=NlW-}-tLIiM0RzMk?L!X*k~M0AJht+}+U>A-gph6vNqjBp3`! z{ZZF0^@uTzHJ^2+K-|J#3=NLBCF#h!noRWHUuZ^u7?W{KJjQzo)Zx9m5pHW0u5`?x zCWCUQ=_sIuWTQ>>?#+$;rdXv)6*acqh@=0)eU?rx)Ieo8Mhb8Y>Io&j5DTs>&LPq* z2~-gYU@6%owI>uaNN`1@VcyH}24(L(cT&|48b(N1*JeqfSg^fFcOqCz~fq zyTj$W;73`i`Z0$nxHRmO4#-^uI>DB*fbP_3>kTV2EM8gylKpuRb7P@Hrq!=GVc_H> z+W}c4ZMvQr0gHfH5BDuWmC=gU%@H1Ed#yrD#MJ_0m_Hf2Ex7h7?v@RT?C-ZcXZ;imedExfLw^!|UlT(Rg%7fuR8)va zLp=4bo}#{}iopHrU4Nmx=511)gf2Q!Y^|1k+SFoC8_5ebIk<`w8U^a-QM?nl+=5q0 z8e}yFwvG>%dkKocT!maor`Z+g?-GtR)KwXu=0H^<<(RFng9R=B7i@&(?)2gtrxp15 zSr_XE;iPLY?eCSK%owu`-cdb`Y*s#08Lg)Qd*4d~hl?-%2nbyajk@;&qpnCo-E0lt z1jm!8tcKXGTj=jmY>j^#fnF&*1+N*bd}X9%M|CT2Db7EC)f*ne^0x%6A9}0G=0KKQ z=dfNbOi%|g@DYQcaStNDI}K}hOEn>7WdbECe%?ZnsL%hvW>Y#nRHl54^h1aK;|997 zj>E2;q9ofZIn{_g*WS%2n=VoFZ90VInh8Q!498oDwXuNa@>KtR6M8wNi;B*#aeThj zLUuNjkjv*tGMXb)$Z^JCNbLI6dS<$uu6(c`5UH-A0(8qUr|cvK zhyLMgq3@s>=SX%hE{Nf6 zoL5R{F^)j-`0~c0y`Ou?^A{HP|94*p7%RXZ;AuG7`^GYIt2!OlnU|^}JYv+&?kNY< z|0XG*{I&}lQE>$be;KGe5xVJ$AZ;dpK58`QtAK~oTDp|Evq9H>8|hNENnlMRvUQ;0 z{J|!L{XLW{8%D(?iS5qFu9YlB@VxR_`Vzv|1S4 z_O>#YZv@&7hWwupu4N9)WZMK`-UdUA2gL~lncbj&eCW`jKa4KaphNsQT2MeuGDoSM zM~#N@FdUYC+pAh#smLN`1{QR=1^PJ}Q+WY0n+qq!_vxHt1nxtzHY)@fCn4$uaP+&F zoBE`Hm3w6-dqz5MTmaTS%zGC6eY~*acnzL`)h(vW*+_-SfYPqzt3^J@NU!Bgl~s9Xj-1qOl0NP;gxOFY_38)NS>NQMGlvND-lX zUi!`AJFNf+{a2aGe=zCfDC)>iSW#i#eW0|(3znjDaIf@pd!y@9K@Aoe33cp(Qqutk z=0T-jaFcFG!33aNz^f?t&TTJbN?4AC5sngGGH+1Ai7tLCBcKRS-xkuLz2@A-gUFDV zC19}&Riat|5}ggoG7R2X*l10-P(~3>XPLgFVHE7NfYEWf<0AmL@sOz>vDx2p+)wKS zg8uuZ448*oqCG5y5)ei9=#r9i1u3ck?a-k^zaM>dB`BjW^#wEeN|Vt+$FgOL$2uYu z6j1J1*>t(z^qg_oDreZtO23S&c>q6LUs+!@i34$xrEE+c0=(+NBzzS&$OYd9xXp}( z2~%nv5j9)3ZQ=LivFl=!Aw13@dB>4)JKvbNjlXX=73i0Fv`?zawGh?7}>hlxBWA z*!c5ckjBgV+zWxJwytU;S9Z&`wMEsGhQQQ|`iyWII>28sqwC6c`37N3 zRxKjg{Zs5f@j{3FdXA^dyutT>t~QG|OHdcF_eO>}ykxjzXboRH$2ixy@q%r4*K#H{ z49bhg|7n@-1suRDrK#@S4!oTW;!Vk8Y#%ywXpFu|swNW+G|;OCa#H&Umm=`fZ#ba1 zun4=9m&j*5Jly?Cugv$4S+vu|wkdqj|9T_XSQY3i;$9Awq5T@nWyS2rbmE@!4OIJ+ zmOtWh_Jxkp4GQBC?W*hPwt0m+it|x<$^a$vo9W`xsLRBVR?HAV5&zg_H$OL(X5+hL+?&fRwic-sKRdJ8 zQ~>Ia&cLypp>0M=ztP2W40Be8-QucHoC{oA)_;su<$feEupxVMGpCpIpKdT(8_%Z2 zB#K$#bSgh|=+KmI#5RHs_e<*IOGFxMQ_N4q&^1U#C96F|h3FJgF1aC7Fv zCMtTJkyMj-4pB^nH#b=(D@XDP18K3q1sUek6_@j#Uo7{efNpuT)LNi02v`JqFHX6Wy~|4!fkcm!D~`0%#TC86A)9O1|N z9^P-O_y?CCIA9#V*Rt|XkM#6h85h#Ca^eO@Kh?9Eg-Py|8DRMF|5fTRgipxD?IQ*Z zSGsb{oFVJRw(X?((?ZK0XCOEO+tFQ&XO~=;k=j+fZtUB#5joW$8*q0Q0tM+l(-AP!g7oIPe$63KITE*&X1|42v8(bSRqs`s=Us{q^52|N}E|z zw-|d*$p$(vPgrn@!RS`?PRXpM8)2YBa)?xI^dPdIA9qtWq*%d8!%1pBPR^(c z#KJ!@CO)rP8e?vms`^6Zn|Lz@jsHeVy$4p$??p$5GO!YotWZB5VW7XY1YqobySp1# zRa*5~U&wqJt^O= " - exit 1 -fi - -if [ -z "$SIGNATURE" ]; then - echo "usage: $0 " - exit 1 -fi - -rm -rf ${TEMPDIR} && mkdir -p ${TEMPDIR} -tar -C ${TEMPDIR} -xf ${UNSIGNED} -cp -rf "${SIGNATURE}"/* ${TEMPDIR} - -if [ -z "${PAGESTUFF}" ]; then - PAGESTUFF=${TEMPDIR}/pagestuff -fi - -if [ -z "${CODESIGN_ALLOCATE}" ]; then - CODESIGN_ALLOCATE=${TEMPDIR}/codesign_allocate -fi - -find ${TEMPDIR} -name "*.sign" | while read i; do - SIZE=`stat -c %s "${i}"` - TARGET_FILE="`echo "${i}" | sed 's/\.sign$//'`" - - echo "Allocating space for the signature of size ${SIZE} in ${TARGET_FILE}" - ${CODESIGN_ALLOCATE} -i "${TARGET_FILE}" -a ${ARCH} ${SIZE} -o "${i}.tmp" - - OFFSET=`${PAGESTUFF} "${i}.tmp" -p | tail -2 | grep offset | sed 's/[^0-9]*//g'` - if [ -z ${QUIET} ]; then - echo "Attaching signature at offset ${OFFSET}" - fi - - dd if="$i" of="${i}.tmp" bs=1 seek=${OFFSET} count=${SIZE} 2>/dev/null - mv "${i}.tmp" "${TARGET_FILE}" - rm "${i}" - echo "Success." -done -mv ${TEMPDIR}/${ROOTDIR} ${OUTDIR} -rm -rf ${TEMPDIR} -echo "Signed: ${OUTDIR}" diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh index 89a2da32f..56bae59a6 100755 --- a/contrib/macdeploy/detached-sig-create.sh +++ b/contrib/macdeploy/detached-sig-create.sh @@ -1,46 +1,31 @@ #!/bin/sh +# Copyright (c) 2024 The BitcoinZ community +# Copyright (c) 2014-2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C set -e ROOTDIR=dist -BUNDLE="${ROOTDIR}/Bitcoin-Qt.app" -CODESIGN=codesign +BUNDLE="${ROOTDIR}/BitcoinZ.app" +BINARY="${BUNDLE}/Contents/MacOS/BitcoinZ" +SIGNAPPLE=signapple TEMPDIR=sign.temp -TEMPLIST=${TEMPDIR}/signatures.txt -OUT=signature.tar.gz -OUTROOT=osx +ARCH=$(${SIGNAPPLE} info ${BINARY} | head -n 1 | cut -d " " -f 1) +OUT="signature-osx-${ARCH}.tar.gz" +OUTROOT=osx/dist -if [ ! -n "$1" ]; then - echo "usage: $0 " - echo "example: $0 -s MyIdentity" +if [ -z "$1" ]; then + echo "usage: $0 " + echo "example: $0 " exit 1 fi -rm -rf ${TEMPDIR} ${TEMPLIST} +rm -rf ${TEMPDIR} mkdir -p ${TEMPDIR} -${CODESIGN} -f --file-list ${TEMPLIST} "$@" "${BUNDLE}" - -grep -v CodeResources < "${TEMPLIST}" | while read i; do - TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`" - SIZE=`pagestuff "$i" -p | tail -2 | grep size | sed 's/[^0-9]*//g'` - OFFSET=`pagestuff "$i" -p | tail -2 | grep offset | sed 's/[^0-9]*//g'` - SIGNFILE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}.sign" - DIRNAME="`dirname "${SIGNFILE}"`" - mkdir -p "${DIRNAME}" - echo "Adding detached signature for: ${TARGETFILE}. Size: ${SIZE}. Offset: ${OFFSET}" - dd if="$i" of="${SIGNFILE}" bs=1 skip=${OFFSET} count=${SIZE} 2>/dev/null -done - -grep CodeResources < "${TEMPLIST}" | while read i; do - TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`" - RESOURCE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}" - DIRNAME="`dirname "${RESOURCE}"`" - mkdir -p "${DIRNAME}" - echo "Adding resource for: "${TARGETFILE}"" - cp "${i}" "${RESOURCE}" -done - -rm ${TEMPLIST} +${SIGNAPPLE} sign -f --detach "${TEMPDIR}/${OUTROOT}" "$@" "${BUNDLE}" --hardened-runtime tar -C "${TEMPDIR}" -czf "${OUT}" . rm -rf "${TEMPDIR}" diff --git a/contrib/macdeploy/fancy.plist b/contrib/macdeploy/fancy.plist deleted file mode 100644 index ef277a7f1..000000000 --- a/contrib/macdeploy/fancy.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - window_bounds - - 300 - 300 - 800 - 620 - - background_picture - background.tiff - icon_size - 96 - applications_symlink - - items_position - - Applications - - 370 - 156 - - Bitcoin-Qt.app - - 128 - 156 - - - - diff --git a/contrib/macdeploy/gen-sdk b/contrib/macdeploy/gen-sdk new file mode 100755 index 000000000..86a6262b5 --- /dev/null +++ b/contrib/macdeploy/gen-sdk @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +import argparse +import plistlib +import pathlib +import sys +import tarfile +import gzip +import os +import contextlib + +@contextlib.contextmanager +def cd(path): + """Context manager that restores PWD even if an exception was raised.""" + old_pwd = os.getcwd() + os.chdir(str(path)) + try: + yield + finally: + os.chdir(old_pwd) + +def run(): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument('xcode_app', metavar='XCODEAPP', nargs=1) + parser.add_argument("-o", metavar='OUTSDKTGZ', nargs=1, dest='out_sdktgz', required=False) + + args = parser.parse_args() + + xcode_app = pathlib.Path(args.xcode_app[0]).resolve() + assert xcode_app.is_dir(), "The supplied Xcode.app path '{}' either does not exist or is not a directory".format(xcode_app) + + xcode_app_plist = xcode_app.joinpath("Contents/version.plist") + with xcode_app_plist.open('rb') as fp: + pl = plistlib.load(fp) + xcode_version = pl['CFBundleShortVersionString'] + xcode_build_id = pl['ProductBuildVersion'] + print("Found Xcode (version: {xcode_version}, build id: {xcode_build_id})".format(xcode_version=xcode_version, xcode_build_id=xcode_build_id)) + + sdk_dir = xcode_app.joinpath("Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk") + sdk_plist = sdk_dir.joinpath("System/Library/CoreServices/SystemVersion.plist") + with sdk_plist.open('rb') as fp: + pl = plistlib.load(fp) + sdk_version = pl['ProductVersion'] + sdk_build_id = pl['ProductBuildVersion'] + print("Found MacOSX SDK (version: {sdk_version}, build id: {sdk_build_id})".format(sdk_version=sdk_version, sdk_build_id=sdk_build_id)) + + out_name = "Xcode-{xcode_version}-{xcode_build_id}-extracted-SDK-with-libcxx-headers".format(xcode_version=xcode_version, xcode_build_id=xcode_build_id) + + if args.out_sdktgz: + out_sdktgz_path = pathlib.Path(args.out_sdktgz_path) + else: + # Construct our own out_sdktgz if not specified on the command line + out_sdktgz_path = pathlib.Path("./{}.tar.gz".format(out_name)) + + def tarfp_add_with_base_change(tarfp, dir_to_add, alt_base_dir): + """Add all files in dir_to_add to tarfp, but prepent alt_base_dir to the files' + names + + e.g. if the only file under /root/bazdir is /root/bazdir/qux, invoking: + + tarfp_add_with_base_change(tarfp, "foo/bar", "/root/bazdir") + + would result in the following members being added to tarfp: + + foo/bar/ -> corresponding to /root/bazdir + foo/bar/qux -> corresponding to /root/bazdir/qux + + """ + def change_tarinfo_base(tarinfo): + if tarinfo.name and tarinfo.name.startswith("./"): + tarinfo.name = str(pathlib.Path(alt_base_dir, tarinfo.name)) + if tarinfo.linkname and tarinfo.linkname.startswith("./"): + tarinfo.linkname = str(pathlib.Path(alt_base_dir, tarinfo.linkname)) + # make metadata deterministic + tarinfo.mtime = 0 + tarinfo.uid, tarinfo.uname = 0, '' + tarinfo.gid, tarinfo.gname = 0, '' + # don't use isdir() as there are also executable files present + tarinfo.mode = 0o0755 if tarinfo.mode & 0o0100 else 0o0644 + return tarinfo + with cd(dir_to_add): + # recursion already adds entries in sorted order + tarfp.add(".", recursive=True, filter=change_tarinfo_base) + + print("Creating output .tar.gz file...") + with out_sdktgz_path.open("wb") as fp: + with gzip.GzipFile(fileobj=fp, mode='wb', compresslevel=9, mtime=0) as gzf: + with tarfile.open(mode="w", fileobj=gzf, format=tarfile.GNU_FORMAT) as tarfp: + print("Adding MacOSX SDK {} files...".format(sdk_version)) + tarfp_add_with_base_change(tarfp, sdk_dir, out_name) + print("Done! Find the resulting gzipped tarball at:") + print(out_sdktgz_path.resolve()) + +if __name__ == '__main__': + run() diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus new file mode 100755 index 000000000..e83b799e9 --- /dev/null +++ b/contrib/macdeploy/macdeployqtplus @@ -0,0 +1,386 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2011 Patrick "p2k" Schneider +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import sys, re, os, platform, shutil, stat, subprocess, os.path +from argparse import ArgumentParser +from pathlib import Path +from subprocess import PIPE, run +from typing import Optional + +# This is ported from the original macdeployqt with modifications + +class FrameworkInfo(object): + def __init__(self): + self.frameworkDirectory = "" + self.frameworkName = "" + self.frameworkPath = "" + self.binaryDirectory = "" + self.binaryName = "" + self.binaryPath = "" + self.version = "" + self.installName = "" + self.deployedInstallName = "" + self.sourceFilePath = "" + self.destinationDirectory = "" + self.sourceResourcesDirectory = "" + self.sourceVersionContentsDirectory = "" + self.sourceContentsDirectory = "" + self.destinationResourcesDirectory = "" + self.destinationVersionContentsDirectory = "" + + def __eq__(self, other): + if self.__class__ == other.__class__: + return self.__dict__ == other.__dict__ + else: + return False + + def __str__(self): + return f""" Framework name: {self.frameworkName} + Framework directory: {self.frameworkDirectory} + Framework path: {self.frameworkPath} + Binary name: {self.binaryName} + Binary directory: {self.binaryDirectory} + Binary path: {self.binaryPath} + Version: {self.version} + Install name: {self.installName} + Deployed install name: {self.deployedInstallName} + Source file Path: {self.sourceFilePath} + Deployed Directory (relative to bundle): {self.destinationDirectory} +""" + + def isDylib(self): + return self.frameworkName.endswith(".dylib") + + reOLine = re.compile(r'^(.+) \(compatibility version [0-9.]+, current version [0-9.]+\)$') + bundleFrameworkDirectory = "Contents/Frameworks" + bundleBinaryDirectory = "Contents/MacOS" + + @classmethod + def fromLibraryLine(cls, line: str) -> Optional['FrameworkInfo']: + # Note: line must be trimmed + if line == "": + return None + + # Don't deploy system libraries + if line.startswith("/System/Library/") or line.startswith("@executable_path") or line.startswith("/usr/lib/"): + return None + + m = cls.reOLine.match(line) + if m is None: + raise RuntimeError(f"Line could not be parsed: {line}") + + path = m.group(1) + + info = cls() + info.sourceFilePath = path + info.installName = path + + if path.endswith(".dylib"): + dirname, filename = os.path.split(path) + info.frameworkName = filename + info.frameworkDirectory = dirname + info.frameworkPath = path + + info.binaryDirectory = dirname + info.binaryName = filename + info.binaryPath = path + info.version = "-" + + info.installName = path + info.deployedInstallName = f"@executable_path/../Frameworks/{info.binaryName}" + info.sourceFilePath = path + info.destinationDirectory = cls.bundleFrameworkDirectory + else: + parts = path.split("/") + i = 0 + # Search for the .framework directory + for part in parts: + if part.endswith(".framework"): + break + i += 1 + if i == len(parts): + raise RuntimeError(f"Could not find .framework or .dylib in line: {line}") + + info.frameworkName = parts[i] + info.frameworkDirectory = "/".join(parts[:i]) + info.frameworkPath = os.path.join(info.frameworkDirectory, info.frameworkName) + + info.binaryName = parts[i+3] + info.binaryDirectory = "/".join(parts[i+1:i+3]) + info.binaryPath = os.path.join(info.binaryDirectory, info.binaryName) + info.version = parts[i+2] + + info.deployedInstallName = f"@executable_path/../Frameworks/{os.path.join(info.frameworkName, info.binaryPath)}" + info.destinationDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, info.binaryDirectory) + + info.sourceResourcesDirectory = os.path.join(info.frameworkPath, "Resources") + info.sourceContentsDirectory = os.path.join(info.frameworkPath, "Contents") + info.sourceVersionContentsDirectory = os.path.join(info.frameworkPath, "Versions", info.version, "Contents") + info.destinationResourcesDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Resources") + info.destinationVersionContentsDirectory = os.path.join(cls.bundleFrameworkDirectory, info.frameworkName, "Versions", info.version, "Contents") + + return info + +class ApplicationBundleInfo(object): + def __init__(self, path: str): + self.path = path + # for backwards compatibility reasons, this must remain as BitcoinZ + self.binaryPath = os.path.join(path, "Contents", "MacOS", "BitcoinZ") + if not os.path.exists(self.binaryPath): + raise RuntimeError(f"Could not find bundle binary for {path}") + self.resourcesPath = os.path.join(path, "Contents", "Resources") + self.pluginPath = os.path.join(path, "Contents", "PlugIns") + +class DeploymentInfo(object): + def __init__(self): + self.pluginPath = None + self.deployedFrameworks = [] + + def usesFramework(self, name: str) -> bool: + for framework in self.deployedFrameworks: + if framework.endswith(".framework"): + if framework.startswith(f"{name}."): + return True + elif framework.endswith(".dylib"): + if framework.startswith(f"lib{name}."): + return True + return False + +def getFrameworks(binaryPath: str, verbose: int) -> list[FrameworkInfo]: + objdump = os.getenv("OBJDUMP", "objdump") + if verbose: + print(f"Inspecting with {objdump}: {binaryPath}") + output = run([objdump, "--macho", "--dylibs-used", binaryPath], stdout=PIPE, stderr=PIPE, text=True) + if output.returncode != 0: + sys.stderr.write(output.stderr) + sys.stderr.flush() + raise RuntimeError(f"{objdump} failed with return code {output.returncode}") + + lines = output.stdout.split("\n") + lines.pop(0) # First line is the inspected binary + if ".framework" in binaryPath or binaryPath.endswith(".dylib"): + lines.pop(0) # Frameworks and dylibs list themselves as a dependency. + + libraries = [] + for line in lines: + line = line.replace("@loader_path", os.path.dirname(binaryPath)) + info = FrameworkInfo.fromLibraryLine(line.strip()) + if info is not None: + if verbose: + print("Found framework:") + print(info) + libraries.append(info) + + return libraries + +def runInstallNameTool(action: str, *args): + installnametoolbin=os.getenv("INSTALL_NAME_TOOL", "install_name_tool") + run([installnametoolbin, "-"+action] + list(args), check=True) + +def changeInstallName(oldName: str, newName: str, binaryPath: str, verbose: int): + if verbose: + print("Using install_name_tool:") + print(" in", binaryPath) + print(" change reference", oldName) + print(" to", newName) + runInstallNameTool("change", oldName, newName, binaryPath) + +def changeIdentification(id: str, binaryPath: str, verbose: int): + if verbose: + print("Using install_name_tool:") + print(" change identification in", binaryPath) + print(" to", id) + runInstallNameTool("id", id, binaryPath) + +def runStrip(binaryPath: str, verbose: int): + stripbin=os.getenv("STRIP", "strip") + if verbose: + print("Using strip:") + print(" stripped", binaryPath) + run([stripbin, "-x", binaryPath], check=True) + +def copyFramework(framework: FrameworkInfo, path: str, verbose: int) -> Optional[str]: + fromPath = framework.sourceFilePath + toDir = os.path.join(path, framework.destinationDirectory) + toPath = os.path.join(toDir, framework.binaryName) + + if framework.isDylib(): + if not os.path.exists(fromPath): + raise RuntimeError(f"No file at {fromPath}") + + if os.path.exists(toPath): + return None # Already there + + if not os.path.exists(toDir): + os.makedirs(toDir) + + shutil.copy2(fromPath, toPath) + if verbose: + print("Copied:", fromPath) + print(" to:", toPath) + else: + to_dir = os.path.join(path, "Contents", "Frameworks", framework.frameworkName) + if os.path.exists(to_dir): + return None # Already there + + from_dir = framework.frameworkPath + if not os.path.exists(from_dir): + raise RuntimeError(f"No directory at {from_dir}") + + shutil.copytree(from_dir, to_dir, symlinks=True) + if verbose: + print("Copied:", from_dir) + print(" to:", to_dir) + + headers_link = os.path.join(to_dir, "Headers") + if os.path.exists(headers_link): + os.unlink(headers_link) + + headers_dir = os.path.join(to_dir, framework.binaryDirectory, "Headers") + if os.path.exists(headers_dir): + shutil.rmtree(headers_dir) + + permissions = os.stat(toPath) + if not permissions.st_mode & stat.S_IWRITE: + os.chmod(toPath, permissions.st_mode | stat.S_IWRITE) + + return toPath + +def deployFrameworks(frameworks: list[FrameworkInfo], bundlePath: str, binaryPath: str, strip: bool, verbose: int, deploymentInfo: Optional[DeploymentInfo] = None) -> DeploymentInfo: + if deploymentInfo is None: + deploymentInfo = DeploymentInfo() + + while len(frameworks) > 0: + framework = frameworks.pop(0) + deploymentInfo.deployedFrameworks.append(framework.frameworkName) + + print("Processing", framework.frameworkName, "...") + + if framework.installName.startswith("@executable_path") or framework.installName.startswith(bundlePath): + print(framework.frameworkName, "already deployed, skipping.") + continue + + # install_name_tool the new id into the binary + changeInstallName(framework.installName, framework.deployedInstallName, binaryPath, verbose) + + # Copy framework to app bundle. + deployedBinaryPath = copyFramework(framework, bundlePath, verbose) + # Skip the rest if already was deployed. + if deployedBinaryPath is None: + continue + + if strip: + runStrip(deployedBinaryPath, verbose) + + # install_name_tool it a new id. + changeIdentification(framework.deployedInstallName, deployedBinaryPath, verbose) + # Check for framework dependencies + dependencies = getFrameworks(deployedBinaryPath, verbose) + + for dependency in dependencies: + changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath, verbose) + + # Deploy framework if necessary. + if dependency.frameworkName not in deploymentInfo.deployedFrameworks and dependency not in frameworks: + frameworks.append(dependency) + + return deploymentInfo + +def deployFrameworksForAppBundle(applicationBundle: ApplicationBundleInfo, strip: bool, verbose: int) -> DeploymentInfo: + frameworks = getFrameworks(applicationBundle.binaryPath, verbose) + if len(frameworks) == 0: + print(f"Warning: Could not find any external frameworks to deploy in {applicationBundle.path}.") + return DeploymentInfo() + else: + return deployFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, strip, verbose) + + +ap = ArgumentParser(description="""Improved version of macdeployqt. + +Outputs a ready-to-deploy app in a folder "dist" and optionally wraps it in a .zip file. +Note, that the "dist" folder will be deleted before deploying on each run.""") + +ap.add_argument("app_bundle", nargs=1, metavar="app-bundle", help="application bundle to be deployed") +ap.add_argument("appname", nargs=1, metavar="appname", help="name of the app being deployed") +ap.add_argument("-verbose", nargs="?", const=True, help="Output additional debugging information") +ap.add_argument("-no-plugins", dest="plugins", action="store_false", default=True, help="skip plugin deployment") +ap.add_argument("-no-strip", dest="strip", action="store_false", default=True, help="don't run 'strip' on the binaries") +ap.add_argument("-zip", nargs="?", const="", metavar="zip", help="create a .zip containing the app bundle") + +config = ap.parse_args() + +verbose = config.verbose + +# ------------------------------------------------ + +app_bundle = config.app_bundle[0] +appname = config.appname[0] + +if not os.path.exists(app_bundle): + sys.stderr.write(f"Error: Could not find app bundle \"{app_bundle}\"\n") + sys.exit(1) + +# ------------------------------------------------ + +if os.path.exists("dist"): + print("+ Removing existing dist folder +") + shutil.rmtree("dist") + +if os.path.exists(appname + ".zip"): + print("+ Removing existing .zip +") + os.unlink(appname + ".zip") + +# ------------------------------------------------ + +target = os.path.join("dist", "BitcoinZ.app") + +print("+ Copying source bundle +") +if verbose: + print(app_bundle, "->", target) + +os.mkdir("dist") +shutil.copytree(app_bundle, target, symlinks=True) + +applicationBundle = ApplicationBundleInfo(target) + +# ------------------------------------------------ + +print("+ Deploying frameworks +") + +try: + deploymentInfo = deployFrameworksForAppBundle(applicationBundle, config.strip, verbose) + config.plugins = False +except RuntimeError as e: + sys.stderr.write(f"Error: {str(e)}\n") + sys.exit(1) + +# ------------------------------------------------ + +if platform.system() == "Darwin": + subprocess.check_call(f"codesign --deep --force --sign - {target}", shell=True) + +# ------------------------------------------------ + +if config.zip is not None: + shutil.make_archive('{}'.format(appname), format='zip', root_dir='dist', base_dir='BitcoinZ.app') + +# ------------------------------------------------ + +print("+ Done +") + +sys.exit(0) diff --git a/contrib/shell/git-utils.bash b/contrib/shell/git-utils.bash new file mode 100644 index 000000000..37bac1f38 --- /dev/null +++ b/contrib/shell/git-utils.bash @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +git_root() { + git rev-parse --show-toplevel 2> /dev/null +} + +git_head_version() { + local recent_tag + if recent_tag="$(git describe --exact-match HEAD 2> /dev/null)"; then + echo "${recent_tag#v}" + else + git rev-parse --short=12 HEAD + fi +} diff --git a/contrib/shell/realpath.bash b/contrib/shell/realpath.bash new file mode 100644 index 000000000..389b77b56 --- /dev/null +++ b/contrib/shell/realpath.bash @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +# Based on realpath.sh written by Michael Kropat +# Found at: https://github.com/mkropat/sh-realpath/blob/65512368b8155b176b67122aa395ac580d9acc5b/realpath.sh + +bash_realpath() { + canonicalize_path "$(resolve_symlinks "$1")" +} + +resolve_symlinks() { + _resolve_symlinks "$1" +} + +_resolve_symlinks() { + _assert_no_path_cycles "$@" || return + + local dir_context path + if path=$(readlink -- "$1"); then + dir_context=$(dirname -- "$1") + _resolve_symlinks "$(_prepend_dir_context_if_necessary "$dir_context" "$path")" "$@" + else + printf '%s\n' "$1" + fi +} + +_prepend_dir_context_if_necessary() { + if [ "$1" = . ]; then + printf '%s\n' "$2" + else + _prepend_path_if_relative "$1" "$2" + fi +} + +_prepend_path_if_relative() { + case "$2" in + /* ) printf '%s\n' "$2" ;; + * ) printf '%s\n' "$1/$2" ;; + esac +} + +_assert_no_path_cycles() { + local target path + + target=$1 + shift + + for path in "$@"; do + if [ "$path" = "$target" ]; then + return 1 + fi + done +} + +canonicalize_path() { + if [ -d "$1" ]; then + _canonicalize_dir_path "$1" + else + _canonicalize_file_path "$1" + fi +} + +_canonicalize_dir_path() { + (cd "$1" 2>/dev/null && pwd -P) +} + +_canonicalize_file_path() { + local dir file + dir=$(dirname -- "$1") + file=$(basename -- "$1") + (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file") +} diff --git a/contrib/windeploy/detached-sig-create.sh b/contrib/windeploy/detached-sig-create.sh new file mode 100755 index 000000000..82fcf2d40 --- /dev/null +++ b/contrib/windeploy/detached-sig-create.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# Copyright (c) 2014-2021 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C +if [ -z "$OSSLSIGNCODE" ]; then + OSSLSIGNCODE=osslsigncode +fi + +if [ -z "$1" ]; then + echo "usage: $0 " + echo "example: $0 -key codesign.key" + exit 1 +fi + +OUT=signature-win.tar.gz +SRCDIR=unsigned +WORKDIR=./.tmp +OUTDIR="${WORKDIR}/out" +OUTSUBDIR="${OUTDIR}/win" +TIMESERVER=http://timestamp.comodoca.com +CERTFILE="win-codesign.cert" + +mkdir -p "${OUTSUBDIR}" +# shellcheck disable=SC2046 +basename -a $(ls -1 "${SRCDIR}"/*-unsigned.exe) | while read UNSIGNED; do + echo Signing "${UNSIGNED}" + "${OSSLSIGNCODE}" sign -certs "${CERTFILE}" -t "${TIMESERVER}" -h sha256 -in "${SRCDIR}/${UNSIGNED}" -out "${WORKDIR}/${UNSIGNED}" "$@" + "${OSSLSIGNCODE}" extract-signature -pem -in "${WORKDIR}/${UNSIGNED}" -out "${OUTSUBDIR}/${UNSIGNED}.pem" && rm "${WORKDIR}/${UNSIGNED}" +done + +rm -f "${OUT}" +tar -C "${OUTDIR}" -czf "${OUT}" . +rm -rf "${WORKDIR}" +echo "Created ${OUT}" diff --git a/contrib/windeploy/win-codesign.cert b/contrib/windeploy/win-codesign.cert new file mode 100644 index 000000000..ec60a3018 --- /dev/null +++ b/contrib/windeploy/win-codesign.cert @@ -0,0 +1,112 @@ +-----BEGIN CERTIFICATE----- +MIIHeTCCBWGgAwIBAgIQBzR46J2yq3g++NbQS/BBVDANBgkqhkiG9w0BAQsFADBp +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMT +OERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0 +IDIwMjEgQ0ExMB4XDTI0MDUyMjAwMDAwMFoXDTI3MDUzMTIzNTk1OVowgYAxCzAJ +BgNVBAYTAlVTMREwDwYDVQQIEwhEZWxhd2FyZTEOMAwGA1UEBxMFTGV3ZXMxJjAk +BgNVBAoTHUJpdGNvaW4gQ29yZSBDb2RlIFNpZ25pbmcgTExDMSYwJAYDVQQDEx1C +aXRjb2luIENvcmUgQ29kZSBTaWduaW5nIExMQzCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBALewxfjztuRTDNAGf7zkqqWNEt28CZmVJHoYltVRxtE1BP45 +BfmptH5eM1JC/XosTPytHRFeOkO4YVAtiELxK9S/82OZlKA7Mx7PW6vv1184u8+m +P3WpTN/KAZTaW9fB0ELTSCuqsvXq2crM2T7NudJnSyWh2VBjLfPPCAcYwzyGKQbl +jQWjFEJDJWFK83t9mK/v0WQgA3jGJeaz+V6CYXMS7UgpdG8dUhg9o63gYJZAW5pY +RIsNRcJCM5LHhwEMW5329UsTmYCfP7/53emepbQ0n8ijVZjgaJ+LZ8NspBLSeCiF +9UPCKX82uWiQAUTbYHCfSi3I0f3wQidXL9ZY+PXmalM7BMuQ+c2xEcl97CnhrDzx +EBwZvvOC9wGoG+8+epV4TjUZWf+7QN1ZYeg1rai7c7c8u9ILogE8su2xVoz333TH +CDvScIgnQXmk+cbKMBtg9kM0F+aLWsN2xVf0uAj3U7sdXLrfJeW0DZIktWtTBQzX +O/OE4Ka+1WFnDg0HJIih0cTjl9YYvfe53L4pCGy+qGt/XGBRqCMfXp3g+H9FGR5r +pensVVcsrv3GbTfYdlpdmp9OHH5G57GTAZueobCZg7r7RKK0zPU9EiTLJxzyXuai +v/Ksd8eIhHRjewMaQuAtQM1tO+oKAbLF0v2M7v7/aVT76X32JllYAizm3zjvAgMB +AAGjggIDMIIB/zAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNV +HQ4EFgQUvCpU58PIuofv0kHJ3Ty0YDKEy3cwPgYDVR0gBDcwNTAzBgZngQwBBAEw +KTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1Ud +DwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOg +UaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRD +b2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDov +L2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdS +U0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsG +AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0 +dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVT +aWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZI +hvcNAQELBQADggIBADdniG9IY9oOfw1e3+uc2lR4hoZqquJQRrCnbWJ1npnCTavI +CfcEEMuQ5ztg4TR7tQNj2KcaHWKuPYxEz2bg8HpSPG27lnXaz4pLgfqvjdZWNH2v +W6DGRUAwuMQHSV0qhuRcJPZuhwSFx/8y4r++jIcBxCbt/Jprt/bqc8vZZZzTDPfG +M6cGaKMDvF//OkUPVzh4s557kV7+LoaX8CigiACZky3Zj3tkQfJYkEvdQseNvX49 +CMJ+cjN+fGweshbn/DszAT5oXW5l2PXeceyGrE+5Ex1ELXCPqNj8ZSn+S9IKZOag +zDFBA93RTVD438peXPz//xgusgnmSqSPS5tCp9KSvew81acu4v/+egg9EgSSx5Ho +9fkOX7JuygvN3r3UZqsddxdwf2dPvBDYlMdieF8qsR7H5DQPQoaTVrIhW4TFtJl/ +UPjVlnDwu+yvMC70F+CaVgQs01uZ0VKuG3KNkkEj6+V/SM54NVVgcY/Q7llKIFA8 +Qk8Ip8/83cVBptKW+OU+i2ZwoYskLbdfDE31X2knUIouNZgBBMhzc5WjJCEGXAPm +9xYZMn87cc+ejxCw6/WC4b6tDCziO8drq76Pl6LTNPOtRkEVqt12p8Uqi9PgznUB +bdHeoF5XHt1Ca2ySpSYuMz5djwIC2ws8kiMm44/AyTm6dwRcesiOTqnaRc+t +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRy +dXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1 +M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZ +wZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI +8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGi +TUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLm +ysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3S +vUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tv +k2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+ +960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3s +MJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FK +PkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1H +s/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAw +HQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LS +cV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF +BQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp +Z2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu +Y29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYy +aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j +cmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQAD +ggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L +/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHV +UHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rd +KOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK +6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43N +b3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4Z +XDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvm +oLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8 +y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMM +B0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+F +SCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhO +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- diff --git a/depends/Makefile b/depends/Makefile index 6b51cb20f..e853d0e08 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -1,26 +1,63 @@ .NOTPARALLEL : +# Pattern rule to print variables, e.g. make print-top_srcdir +print-%: FORCE + @echo '$*'='$($*)' + +# When invoking a sub-make, keep only the command line variable definitions +# matching the pattern in the filter function. +# +# e.g. invoking: +# $ make A=1 C=1 print-MAKEOVERRIDES print-MAKEFLAGS +# +# with the following in the Makefile: +# MAKEOVERRIDES := $(filter A=% B=%,$(MAKEOVERRIDES)) +# +# will print: +# MAKEOVERRIDES = A=1 +# MAKEFLAGS = -- A=1 +# +# this is because as the GNU make manual says: +# The command line variable definitions really appear in the variable +# MAKEOVERRIDES, and MAKEFLAGS contains a reference to this variable. +# +# and since the GNU make manual also says: +# variables defined on the command line are passed to the sub-make through +# MAKEFLAGS +# +# this means that sub-makes will be invoked as if: +# $(MAKE) A=1 blah blah +MAKEOVERRIDES := $(filter V=%,$(MAKEOVERRIDES)) SOURCES_PATH ?= $(BASEDIR)/sources +WORK_PATH = $(BASEDIR)/work BASE_CACHE ?= $(BASEDIR)/built SDK_PATH ?= $(BASEDIR)/SDKs +NO_BOOST ?= +NO_LIBEVENT ?= NO_WALLET ?= -PRIORITY_DOWNLOAD_PATH ?= https://download.z.cash/depends-sources +NO_ZMQ ?= +WITH_GUIX ?= +LTO ?= +NO_HARDEN ?= +FALLBACK_DOWNLOAD_PATH ?= https://download.z.cash/depends-sources + +C_STANDARD ?= c11 +CXX_STANDARD ?= c++17 -BUILD ?= $(shell ./config.guess) +BUILD = $(shell ./config.guess) HOST ?= $(BUILD) PATCHES_PATH = $(BASEDIR)/patches BASEDIR = $(CURDIR) HASH_LENGTH:=11 -DOWNLOAD_CONNECT_TIMEOUT:=10 +DOWNLOAD_CONNECT_TIMEOUT:=30 DOWNLOAD_RETRIES:=3 CRATE_REGISTRY:=vendored-sources - -LIBRUSTZCASH_OVERRIDE ?= +HOST_ID_SALT ?= salt +BUILD_ID_SALT ?= salt host:=$(BUILD) ifneq ($(HOST),) host:=$(HOST) -host_toolchain:=$(HOST)- endif ifneq ($(DEBUG),) @@ -29,9 +66,9 @@ else release_type=release endif -base_build_dir=$(BASEDIR)/work/build -base_staging_dir=$(BASEDIR)/work/staging -base_download_dir=$(BASEDIR)/work/download +base_build_dir=$(WORK_PATH)/build +base_staging_dir=$(WORK_PATH)/staging +base_download_dir=$(WORK_PATH)/download canonical_host:=$(shell ./config.sub $(HOST)) build:=$(shell ./config.sub $(BUILD)) @@ -53,6 +90,7 @@ host_os:=$(findstring linux,$(full_host_os)) host_os+=$(findstring darwin,$(full_host_os)) host_os+=$(findstring freebsd,$(full_host_os)) host_os+=$(findstring mingw32,$(full_host_os)) + host_os:=$(strip $(host_os)) ifeq ($(host_os),) host_os=$(full_host_os) @@ -64,10 +102,6 @@ host_prefix=$($(host_arch)_$(host_os)_prefix) build_prefix=$(host_prefix)/native build_host=$(build) -AT_$(V):= -AT_:=@ -AT:=$(AT_$(V)) - all: install include hosts/$(host_os).mk @@ -76,48 +110,110 @@ include builders/$(build_os).mk include builders/default.mk include packages/packages.mk +# Previously, we directly invoked the well-known programs using $(shell ...) +# to construct build_id_string. However, that was problematic because: +# +# 1. When invoking a shell, GNU Make special-cases exit code 127 (command not +# found) by not capturing the output but instead passing it through. This is +# not done for any other exit code. +# +# 2. Characters like '#' (from these programs' output) would end up in make +# variables like build_id_string, which would be wrongly interpreted by make +# when these variables were used. +# +# Therefore, we should avoid having arbitrary strings in make variables where +# possible. The gen_id script used here hashes the output to construct a +# "make-safe" id. +# +# Also note that these lines need to be: +# +# 1. After including {hosts,builders}/*.mk, since they rely on the tool +# variables (e.g. build_CC, host_STRIP, etc.) to be set. +# +# 2. Before including packages/*.mk (excluding packages/packages.mk), since +# they rely on the build_id variables +# +build_id:=$(shell env CC='$(build_CC)' C_STANDARD='$(C_STANDARD)' CXX='$(build_CXX)' CXX_STANDARD='$(CXX_STANDARD)' AR='$(build_AR) 'NM='$(build_NM)' RANLIB='$(build_RANLIB)' STRIP='$(build_STRIP)' SHA256SUM='$(build_SHA256SUM)' DEBUG='$(DEBUG)' LTO='$(LTO)' NO_HARDEN='$(NO_HARDEN)' ./gen_id '$(BUILD_ID_SALT)' 'GUIX_ENVIRONMENT=$(realpath $(GUIX_ENVIRONMENT))') +$(host_arch)_$(host_os)_id:=$(shell env CC='$(host_CC)' C_STANDARD='$(C_STANDARD)' CXX='$(host_CXX)' CXX_STANDARD='$(CXX_STANDARD)' AR='$(host_AR)' NM='$(host_NM)' RANLIB='$(host_RANLIB)' STRIP='$(host_STRIP)' SHA256SUM='$(build_SHA256SUM)' DEBUG='$(DEBUG)' LTO='$(LTO)' NO_HARDEN='$(NO_HARDEN)' ./gen_id '$(HOST_ID_SALT)' 'GUIX_ENVIRONMENT=$(realpath $(GUIX_ENVIRONMENT))') + +boost_packages_$(NO_BOOST) = $(boost_packages) + +libevent_packages_$(NO_LIBEVENT) = $(libevent_packages) + wallet_packages_$(NO_WALLET) = $(wallet_packages) -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(rust_packages) $(wallet_packages_) +zmq_packages_$(NO_ZMQ) = $(zmq_packages) + +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(boost_packages_) $(libevent_packages_) $(wallet_packages_) $(rust_packages) $(zcash_packages) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) -all_packages = $(packages) $(native_packages) -meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk cargo-checksum.sh +ifneq ($(zmq_packages_),) +packages += $(zmq_packages) +endif + +all_packages = $(packages) $(native_packages) -$(host_arch)_$(host_os)_native_toolchain?=$($(host_os)_native_toolchain) +meta_depends = Makefile config.guess config.sub funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk cargo-checksum.sh fix-elf-interpreter.sh include funcs.mk -toolchain_path=$($($(host_arch)_$(host_os)_native_toolchain)_prefixbin) final_build_id_long+=$(shell $(build_SHA256SUM) config.site.in) -final_build_id+=$(shell echo -n $(final_build_id_long) | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)) +final_build_id+=$(shell echo -n "$(final_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)) $(host_prefix)/.stamp_$(final_build_id): $(native_packages) $(packages) - $(AT)rm -rf $(@D) - $(AT)mkdir -p $(@D) - $(AT)echo copying packages: $^ - $(AT)echo to: $(@D) - $(AT)cd $(@D); $(foreach package,$^, tar xf $($(package)_cached); ) - $(AT)touch $@ + rm -rf $(@D) + mkdir -p $(@D) + echo copying packages: $^ + echo to: $(@D) + cd $(@D); $(foreach package,$^, $(build_TAR) xf $($(package)_cached); ) + touch $@ +# $PATH is not preserved between ./configure and make by convention. Its +# modification and overriding at ./configure time is (as I understand it) +# supposed to be captured by the AC_{PROG_{,OBJ}CXX,PATH_{PROG,TOOL}} macros, +# which will expand the program names to their full absolute paths. The notable +# exception is command line overriding: ./configure CC=clang, which skips the +# program name expansion step, and works because the user implicitly indicates +# with CC=clang that clang will be available in $PATH at all times, and is most +# likely part of the user's system. +# +# Therefore, when we "seed the autoconf cache"/"override well-known program +# vars" by setting AR= in our config.site, either one of two things needs +# to be true for the build system to work correctly: +# +# 1. If we refer to the program by name (e.g. AR=riscv64-gnu-linux-ar), the +# tool needs to be available in $PATH at all times. +# +# 2. If the tool is _**not**_ expected to be available in $PATH at all times +# it needs to be referred to by its absolute path, such as would be output +# by the AC_PATH_{PROG,TOOL} macros. +# +# Minor note: it is also okay to refer to tools by their absolute path even if +# we expect them to be available in $PATH at all times, more specificity does +# not hurt. $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_build_id) - $(AT)@mkdir -p $(@D) - $(AT)sed -e 's|@HOST@|$(host)|' \ - -e 's|@CC@|$(toolchain_path)$(host_CC)|' \ - -e 's|@CXX@|$(toolchain_path)$(host_CXX)|' \ - -e 's|@AR@|$(toolchain_path)$(host_AR)|' \ - -e 's|@RANLIB@|$(toolchain_path)$(host_RANLIB)|' \ - -e 's|@NM@|$(toolchain_path)$(host_NM)|' \ - -e 's|@STRIP@|$(toolchain_path)$(host_STRIP)|' \ + @mkdir -p $(@D) + sed -e 's|@HOST@|$(host)|' \ + -e 's|@CC@|$(host_CC)|' \ + -e 's|@CXX@|$(host_CXX)|' \ + -e 's|@AR@|$(host_AR)|' \ + -e 's|@RANLIB@|$(host_RANLIB)|' \ + -e 's|@NM@|$(host_NM)|' \ + -e 's|@STRIP@|$(host_STRIP)|' \ + -e 's|@OBJDUMP@|$(host_OBJDUMP)|' \ + -e 's|@DSYMUTIL@|$(host_DSYMUTIL)|' \ -e 's|@build_os@|$(build_os)|' \ -e 's|@host_os@|$(host_os)|' \ -e 's|@CFLAGS@|$(strip $(host_CFLAGS) $(host_$(release_type)_CFLAGS))|' \ -e 's|@CXXFLAGS@|$(strip $(host_CXXFLAGS) $(host_$(release_type)_CXXFLAGS))|' \ -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ + -e 's|@no_zmq@|$(NO_ZMQ)|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \ + -e 's|@lto@|$(LTO)|' \ + -e 's|@no_harden@|$(NO_HARDEN)|' \ -e 's|@debug@|$(DEBUG)|' \ $< > $@ - $(AT)touch $@ + touch $@ define check_or_remove_cached @@ -129,9 +225,9 @@ endef define check_or_remove_sources mkdir -p $($(package)_source_dir); cd $($(package)_source_dir); \ - $(build_SHA256SUM) -c $($(package)_fetched) >/dev/null 2>/dev/null || \ - ( if test -f $($(package)_all_sources); then echo "Checksum missing or mismatched for $(package) source. Forcing re-download."; fi; \ - rm -f $($(package)_all_sources) $($(1)_fetched)) + test -f $($(package)_fetched) && ( $(build_SHA256SUM) -c $($(package)_fetched) >/dev/null 2>/dev/null || \ + ( echo "Checksum missing or mismatched for $(package) source. Forcing re-download."; \ + rm -f $($(package)_all_sources) $($(1)_fetched))) || true endef check-packages: @@ -143,17 +239,27 @@ $(host_prefix)/share/config.site: check-packages check-packages: check-sources +clean-all: clean + @rm -rf $(SOURCES_PATH) x86_64* aarch64* + +clean: + @rm -rf $(WORK_PATH) $(BASE_CACHE) $(BUILD) *.log + install: check-packages $(host_prefix)/share/config.site download-one: check-sources $(all_sources) download-osx: - @$(MAKE) -s HOST=x86_64-apple-darwin18 download-one + @$(MAKE) -s HOST=x86_64-apple-darwin download-one download-linux: @$(MAKE) -s HOST=x86_64-unknown-linux-gnu download-one download-win: @$(MAKE) -s HOST=x86_64-w64-mingw32 download-one download: download-osx download-linux download-win -.PHONY: install cached download-one download-osx download-linux download-win download check-packages check-sources +$(foreach package,$(all_packages),$(eval $(call ext_add_stages,$(package)))) + +.PHONY: install cached clean clean-all download-one download-osx download-linux download-win download check-packages check-sources +.PHONY: FORCE +$(V).SILENT: diff --git a/depends/README.md b/depends/README.md index 96634863b..f8147ae78 100644 --- a/depends/README.md +++ b/depends/README.md @@ -12,44 +12,96 @@ For example: make HOST=x86_64-w64-mingw32 -j4 -A prefix will be generated that's suitable for plugging into Bitcoin's -configure. In the above example, a dir named x86_64-w64-mingw32 will be -created. To use it for Bitcoin: +**BitcoinZ's `configure` script by default will ignore the depends output.** In +order for it to pick up libraries, tools, and settings from the depends build, +you must set the `CONFIG_SITE` environment variable to point to a `config.site` settings file. +Make sure that `CONFIG_SITE` is an absolute path. +In the above example, a file named `depends/x86_64-w64-mingw32/share/config.site` will be +created. To use it during compilation: - ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32 + CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure -Common `host-platform-triplets` for cross compilation are: +The default install prefix when using `config.site` is `--prefix=depends/`, +so depends build outputs will be installed in that location. -- `i686-w64-mingw32` for Win32 +Common `host-platform-triplet`s for cross compilation are: + +- `x86_64-pc-linux-gnu` for x86 Linux - `x86_64-w64-mingw32` for Win64 -- `x86_64-apple-darwin18` for MacOSX -- `arm-linux-gnueabihf` for Linux ARM +- `x86_64-apple-darwin` for macOS +- `aarch64-linux-gnu` for Linux ARM 64 bit + +The paths are automatically configured and no other options are needed. + +### Install the required dependencies: Ubuntu & Debian + +#### Common + + apt install automake bison cmake curl libtool make patch pkg-config python3 xz-utils + +#### For macOS cross compilation + + apt install clang lld llvm g++ zip + +Clang 18 or later is required. You must also obtain the macOS SDK before +proceeding with a cross-compile. Under the depends directory, create a +subdirectory named `SDKs`. Then, place the extracted SDK under this new directory. +For more information, see [SDK Extraction](../contrib/macdeploy/README.md#sdk-extraction). + +#### For Win64 cross compilation + +- see [build-windows.md](../doc/build-windows.md#cross-compilation-for-ubuntu-and-windows-subsystem-for-linux) -No other options are needed, the paths are automatically configured. +#### For linux cross compilation -Dependency Options: -The following can be set when running make: make FOO=bar +Common linux dependencies: - SOURCES_PATH: downloaded sources will be placed here - BASE_CACHE: built packages will be placed here - SDK_PATH: Path where sdk's can be found (used by OSX) - PRIORITY_DOWNLOAD_PATH: Try fetching source files from here before using their own URLs - NO_WALLET: Don't download/build/cache libs needed to enable the wallet - DEBUG: disable some optimizations and enable more runtime checking - LIBRUSTZCASH_OVERRIDE: Path to a local librustzcash repository + sudo apt-get install g++-multilib binutils + +For linux AARCH64 cross compilation: + + sudo apt-get install g++-aarch64-linux-gnu binutils-aarch64-linux-gnu + +### Install the required dependencies: FreeBSD + + pkg install bash + +### Dependency Options + +The following can be set when running make: `make FOO=bar` + +- `SOURCES_PATH`: Downloaded sources will be placed here +- `BASE_CACHE`: Built packages will be placed here +- `SDK_PATH`: Path where SDKs can be found (used by macOS) +- `FALLBACK_DOWNLOAD_PATH`: If a source file can't be fetched, try here before giving up +- `C_STANDARD`: Set the C standard version used. Defaults to `c11`. +- `CXX_STANDARD`: Set the C++ standard version used. Defaults to `c++17`. +- `NO_BOOST`: Don't download/build/cache Boost +- `NO_LIBEVENT`: Don't download/build/cache Libevent +- `NO_ZMQ`: Don't download/build/cache packages needed for enabling ZeroMQ +- `NO_WALLET`: Don't download/build/cache libs needed to enable the wallet +- `WITH_GUIX`: Cross compilation using guix containers +- `DEBUG`: Disable some optimizations and enable more runtime checking +- `HOST_ID_SALT`: Optional salt to use when generating host package ids +- `BUILD_ID_SALT`: Optional salt to use when generating build package ids +- `LOG`: Use file-based logging for individual packages. During a package build its log file + resides in the `depends` directory, and the log file is printed out automatically in case + of build error. After successful build log files are moved along with package archives +- `LTO`: Enable options needed for LTO. Does not add `-flto` related options to *FLAGS. +- `NO_HARDEN=1`: Don't use hardening options when building packages If some packages are not built, for example `make NO_WALLET=1`, the appropriate -options will be passed to bitcoin's configure. In this case, `--disable-wallet`. +options will be passed to bitcoinz's configure. In this case, `--disable-wallet`. -Additional targets: +### Additional targets download: run 'make download' to fetch all sources without building them - download-osx: run 'make download-osx' to fetch all sources needed for osx builds + download-osx: run 'make download-osx' to fetch all sources needed for macOS builds download-win: run 'make download-win' to fetch all sources needed for win builds download-linux: run 'make download-linux' to fetch all sources needed for linux builds + ### Other documentation - [description.md](description.md): General description of the depends system - [packages.md](packages.md): Steps for adding packages - diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index f8ff4e23a..2b59353e8 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -1,22 +1,25 @@ -build_darwin_CC: = $(shell xcrun -f clang) -build_darwin_CXX: = $(shell xcrun -f clang++) -build_darwin_AR: = $(shell xcrun -f ar) -build_darwin_RANLIB: = $(shell xcrun -f ranlib) -build_darwin_STRIP: = $(shell xcrun -f strip) -build_darwin_OTOOL: = $(shell xcrun -f otool) -build_darwin_NM: = $(shell xcrun -f nm) -build_darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) -build_darwin_SHA256SUM = shasum -a 256 -build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o +build_darwin_CC:=$(shell xcrun -f clang) -isysroot$(shell xcrun --show-sdk-path) +build_darwin_CXX:=$(shell xcrun -f clang++) -isysroot$(shell xcrun --show-sdk-path) +build_darwin_AR:=$(shell xcrun -f ar) +build_darwin_RANLIB:=$(shell xcrun -f ranlib) +build_darwin_STRIP:=$(shell xcrun -f strip) +build_darwin_OBJDUMP:=$(shell xcrun -f objdump) +build_darwin_NM:=$(shell xcrun -f nm) +build_darwin_DSYMUTIL:=$(shell xcrun -f dsymutil) +build_darwin_SHA256SUM=shasum -a 256 +build_darwin_DOWNLOAD=curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o #darwin host on darwin builder. overrides darwin host preferences. -darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION) -fvisibility=hidden -fvisibility-inlines-hidden --sysroot $(shell xcrun --show-sdk-path) -darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -fvisibility=hidden -fvisibility-inlines-hidden -stdlib=libc++ --sysroot $(shell xcrun --show-sdk-path) +darwin_CC=$(shell xcrun -f clang) -isysroot$(shell xcrun --show-sdk-path) +darwin_CXX:=$(shell xcrun -f clang++) -stdlib=libc++ -isysroot$(shell xcrun --show-sdk-path) darwin_AR:=$(shell xcrun -f ar) darwin_RANLIB:=$(shell xcrun -f ranlib) darwin_STRIP:=$(shell xcrun -f strip) -darwin_LIBTOOL:=$(shell xcrun -f libtool) -darwin_OTOOL:=$(shell xcrun -f otool) +darwin_OBJDUMP:=$(shell xcrun -f objdump) darwin_NM:=$(shell xcrun -f nm) -darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) -darwin_native_toolchain= +darwin_DSYMUTIL:=$(shell xcrun -f dsymutil) + +x86_64_darwin_CFLAGS += -arch x86_64 +x86_64_darwin_CXXFLAGS += -arch x86_64 +aarch64_darwin_CFLAGS += -arch arm64 +aarch64_darwin_CXXFLAGS += -arch arm64 diff --git a/depends/builders/default.mk b/depends/builders/default.mk index f097db65d..2a1709d98 100644 --- a/depends/builders/default.mk +++ b/depends/builders/default.mk @@ -1,18 +1,19 @@ default_build_CC = gcc default_build_CXX = g++ default_build_AR = ar +default_build_OBJDUMP = objdump +default_build_TAR = tar default_build_RANLIB = ranlib default_build_STRIP = strip default_build_NM = nm -default_build_OTOOL = otool -default_build_INSTALL_NAME_TOOL = install_name_tool +default_build_TOUCH = touch -h -m -t 200001011200 define add_build_tool_func build_$(build_os)_$1 ?= $$(default_build_$1) build_$(build_arch)_$(build_os)_$1 ?= $$(build_$(build_os)_$1) build_$1=$$(build_$(build_arch)_$(build_os)_$1) endef -$(foreach var,CC CXX AR RANLIB NM STRIP SHA256SUM DOWNLOAD OTOOL INSTALL_NAME_TOOL,$(eval $(call add_build_tool_func,$(var)))) +$(foreach var,CC CXX AR TAR RANLIB NM STRIP SHA256SUM DOWNLOAD OBJDUMP DSYMUTIL TOUCH,$(eval $(call add_build_tool_func,$(var)))) define add_build_flags_func build_$(build_arch)_$(build_os)_$1 += $(build_$(build_os)_$1) build_$1=$$(build_$(build_arch)_$(build_os)_$1) diff --git a/depends/builders/freebsd.mk b/depends/builders/freebsd.mk index fb2fefe2a..465f58e04 100644 --- a/depends/builders/freebsd.mk +++ b/depends/builders/freebsd.mk @@ -1,2 +1,5 @@ +build_freebsd_CC=clang +build_freebsd_CXX=clang++ + build_freebsd_SHA256SUM = shasum -a 256 build_freebsd_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o diff --git a/depends/config.guess b/depends/config.guess index f357ec6c8..cdfc43920 100755 --- a/depends/config.guess +++ b/depends/config.guess @@ -1,12 +1,14 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2015 Free Software Foundation, Inc. +# Copyright 1992-2023 Free Software Foundation, Inc. -timestamp='2015-03-04' +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2023-08-22' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or +# the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -15,7 +17,7 @@ timestamp='2015-03-04' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see . +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -27,19 +29,27 @@ timestamp='2015-03-04' # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] -Output the configuration name of the system \`$me' is run on. +Output the configuration name of the system '$me' is run on. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -50,13 +60,13 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2015 Free Software Foundation, Inc. +Copyright 1992-2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" -Try \`$me --help' for more information." +Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do @@ -84,78 +94,107 @@ if test $# != 0; then exit 1 fi -trap 'exit 1' 1 2 15 +# Just in case it came from the environment. +GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. +# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still +# use 'HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then +if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown -case "${UNAME_SYSTEM}" in +case $UNAME_SYSTEM in Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu + LIBC=unknown - eval $set_cc_for_build - cat <<-EOF > $dummy.c + set_cc_for_build + cat <<-EOF > "$dummy.c" + #if defined(__ANDROID__) + LIBC=android + #else #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc - #else + #elif defined(__GLIBC__) LIBC=gnu + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif + #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi ;; esac # Note: order is significant - the case branches are not exclusive. -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, @@ -167,29 +206,32 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ - /sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` - case "${UNAME_MACHINE_ARCH}" in + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) - arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` - endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in - arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build + # to ELF recently (or will in the future) and ABI. + case $UNAME_MACHINE_ARCH in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -205,10 +247,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ;; esac # Determine ABI tags. - case "${UNAME_MACHINE_ARCH}" in + case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release @@ -216,40 +258,68 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in + case $UNAME_VERSION in Debian*) release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}${abi}" - exit ;; + GUESS=$machine-${os}${release}${abi-} + ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; + *:MidnightBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} - exit ;; + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; + *:Sortix:*:*) + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; + *:Redox:*:*) + GUESS=$UNAME_MACHINE-unknown-redox + ;; + mips:OSF1:*.*) + GUESS=mips-dec-osf1 + ;; alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` @@ -263,163 +333,158 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in + case $ALPHA_CPU_TYPE in "EV4 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; + GUESS=m68k-unknown-sysv4 + ;; *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; + GUESS=$UNAME_MACHINE-unknown-morphos + ;; *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; + GUESS=i370-ibm-openedition + ;; *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; + GUESS=s390-ibm-zvmoe + ;; *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; + GUESS=powerpc-ibm-os400 + ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; + GUESS=arm-unknown-riscos + ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; + GUESS=hppa1.1-hitachi-hiuxmpp + ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; + GUESS=pyramid-pyramid-svr4 + ;; DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; + GUESS=sparc-icl-nx6 + ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} - exit ;; + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" + set_cc_for_build + SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH="x86_64" + SUN_ARCH=x86_64 fi fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in + case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; + # Japanese Language versions have a version number like '4.1.3-JL'. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case `/bin/arch` in sun3) - echo m68k-sun-sunos${UNAME_RELEASE} + GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) - echo sparc-sun-sunos${UNAME_RELEASE} + GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac - exit ;; + ;; aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor @@ -429,44 +494,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; + GUESS=mips-dec-mach_bsd4.3 + ;; RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { @@ -475,95 +540,96 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; + GUESS=powerpc-motorola-powermax + ;; Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; + GUESS=powerpc-harris-powerunix + ;; m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; + GUESS=m88k-harris-cxux7 + ;; m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; + GUESS=m88k-motorola-sysv4 + ;; m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x then - echo m88k-dg-dgux${UNAME_RELEASE} + GUESS=m88k-dg-dgux$UNAME_RELEASE else - echo m88k-dg-dguxbcs${UNAME_RELEASE} + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else - echo i586-dg-dgux${UNAME_RELEASE} + GUESS=i586-dg-dgux$UNAME_RELEASE fi - exit ;; + ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; + GUESS=m88k-dolphin-sysv3 + ;; M88*:*:R3*:*) # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; + GUESS=m88k-tektronix-sysv3 + ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; + GUESS=m68k-tektronix-bsd + ;; *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; + GUESS=i386-ibm-aix + ;; ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then + if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include main() @@ -574,77 +640,77 @@ EOF exit(0); } EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then - echo "$SYSTEM_NAME" + GUESS=$SYSTEM_NAME else - echo rs6000-ibm-aix3.2.5 + GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 + GUESS=rs6000-ibm-aix3.2.4 else - echo rs6000-ibm-aix3.2 + GUESS=rs6000-ibm-aix3.2 fi - exit ;; + ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi - if [ -x /usr/bin/lslpp ] ; then - IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; + GUESS=rs6000-ibm-aix + ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + GUESS=romp-ibm-bsd4.4 + ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; + GUESS=rs6000-bull-bosx + ;; DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; + GUESS=m68k-bull-sysv3 + ;; 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; + GUESS=m68k-hp-bsd + ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; + GUESS=m68k-hp-bsd4.4 + ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then + if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + case $sc_cpu_version in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + case $sc_kernel_bits in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include @@ -677,13 +743,13 @@ EOF exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if test "$HP_ARCH" = hppa2.0w then - eval $set_cc_for_build + set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -694,23 +760,23 @@ EOF # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include int main () @@ -735,38 +801,38 @@ EOF exit (0); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; + GUESS=unknown-hitachi-hiuxwe2 + ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + GUESS=hppa1.1-hp-bsd + ;; 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; + GUESS=hppa1.0-hp-bsd + ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; + GUESS=hppa1.0-hp-mpeix + ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + GUESS=hppa1.1-hp-osf + ;; hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; + GUESS=hppa1.0-hp-osf + ;; i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk else - echo ${UNAME_MACHINE}-unknown-osf1 + GUESS=$UNAME_MACHINE-unknown-osf1 fi - exit ;; + ;; parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; + GUESS=hppa1.1-hp-lites + ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; + GUESS=c1-convex-bsd + ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd @@ -774,139 +840,174 @@ EOF fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; + GUESS=c34-convex-bsd + ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; + GUESS=c38-convex-bsd + ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; + GUESS=c4-convex-bsd + ;; CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac - exit ;; + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; + GUESS=$UNAME_MACHINE-pc-cygwin + ;; *:MINGW64*:*) - echo ${UNAME_MACHINE}-pc-mingw64 - exit ;; + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; *:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-msys + ;; i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-pw32 + ;; + *:SerenityOS:*:*) + GUESS=$UNAME_MACHINE-pc-serenity + ;; *:Interix*:*) - case ${UNAME_MACHINE} in + case $UNAME_MACHINE in x86) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; + GUESS=i586-pc-interix$UNAME_RELEASE + ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; IA64) - echo ia64-unknown-interix${UNAME_RELEASE} - exit ;; + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; + GUESS=$UNAME_MACHINE-pc-uwin + ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; + GUESS=x86_64-pc-cygwin + ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; + x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-pc-managarm-mlibc" + ;; + *:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" + ;; + *:Minix:*:*) + GUESS=$UNAME_MACHINE-unknown-minix + ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __ARM_EABI__ + #ifdef __ARM_PCS_VFP + ABI=eabihf + #else + ABI=eabi + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; + esac + fi + GUESS=$CPU-unknown-linux-$LIBCABI + ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; @@ -916,175 +1017,246 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="gnulibc1" ; fi - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; arm*:Linux:*:*) - eval $set_cc_for_build + set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi - exit ;; + ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; e2k:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + k1om:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:cos:*:*) + GUESS=$UNAME_MACHINE-unknown-cos + ;; + kvx:mbr:*:*) + GUESS=$UNAME_MACHINE-unknown-mbr + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el + MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} + MIPS_ENDIAN= #else - CPU= + MIPS_ENDIAN= #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) - echo or1k-unknown-linux-${LIBC} - exit ;; + GUESS=or1k-unknown-linux-$LIBC + ;; or32:Linux:*:* | or1k*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; padre:Linux:*:*) - echo sparc-unknown-linux-${LIBC} - exit ;; + GUESS=sparc-unknown-linux-$LIBC + ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-${LIBC} - exit ;; + GUESS=hppa64-unknown-linux-$LIBC + ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; - PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; - *) echo hppa-unknown-linux-${LIBC} ;; + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; esac - exit ;; + ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-${LIBC} - exit ;; + GUESS=powerpc64-unknown-linux-$LIBC + ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-${LIBC} - exit ;; + GUESS=powerpc-unknown-linux-$LIBC + ;; ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-${LIBC} - exit ;; + GUESS=powerpc64le-unknown-linux-$LIBC + ;; ppcle:Linux:*:*) - echo powerpcle-unknown-linux-${LIBC} - exit ;; + GUESS=powerpcle-unknown-linux-$LIBC + ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac + fi + GUESS=$CPU-pc-linux-$LIBCABI + ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; + GUESS=i386-sequent-sysv4 + ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility + # If we were able to find 'uname', then EMX Unix compatibility # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; + GUESS=$UNAME_MACHINE-unknown-stop + ;; i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; + GUESS=$UNAME_MACHINE-unknown-atheos + ;; i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; + GUESS=$UNAME_MACHINE-pc-syllable + ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi - exit ;; + ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in @@ -1092,12 +1264,12 @@ EOF *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 @@ -1107,11 +1279,11 @@ EOF && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else - echo ${UNAME_MACHINE}-pc-sysv32 + GUESS=$UNAME_MACHINE-pc-sysv32 fi - exit ;; + ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about @@ -1119,31 +1291,31 @@ EOF # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; + GUESS=i586-pc-msdosdjgpp + ;; Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; + GUESS=i386-pc-mach3 + ;; paragon:*:*:*) - echo i860-intel-osf1 - exit ;; + GUESS=i860-intel-osf1 + ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi - exit ;; + ;; mini*:CTIX:SYS*5:*) # "miniframe" - echo m68010-convergent-sysv - exit ;; + GUESS=m68010-convergent-sysv + ;; mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; + GUESS=m68k-convergent-sysv + ;; M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; + GUESS=m68k-diab-dnix + ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) @@ -1151,9 +1323,9 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; @@ -1162,248 +1334,444 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; + GUESS=m68k-atari-sysv4 + ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 + GUESS=$UNAME_MACHINE-sni-sysv4 else - echo ns32k-sni-sysv + GUESS=ns32k-sni-sysv fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + ;; + PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort # says - echo i586-unisys-sysv4 - exit ;; + GUESS=i586-unisys-sysv4 + ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; + GUESS=hppa1.1-stratus-sysv4 + ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; + GUESS=i860-stratus-sysv4 + ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; + GUESS=$UNAME_MACHINE-stratus-vos + ;; *:VOS:*:*) # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; + GUESS=hppa1.1-stratus-vos + ;; mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; + GUESS=mips-sony-newsos6 + ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE else - echo mips-unknown-sysv${UNAME_RELEASE} + GUESS=mips-unknown-sysv$UNAME_RELEASE fi - exit ;; + ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; + GUESS=powerpc-be-beos + ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; + GUESS=powerpc-apple-beos + ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; + GUESS=i586-pc-beos + ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; - x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; + GUESS=i586-pc-haiku + ;; + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku + ;; SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; + SX-ACE:SUPER-UX:*:*) + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval $set_cc_for_build - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build fi - if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then - # Avoid executing cc on OS X 10.9, as it ships with a stub - # that puts up a graphical alert prompting to install - # developer tools. Any system running Mac OS X 10.7 or - # later (Darwin 11 and later) is required to have a 64-bit - # processor. This is not true of the ARM version of Darwin - # that Apple uses in portable devices. - UNAME_PROCESSOR=x86_64 + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE fi - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} - exit ;; + GUESS=i386-pc-qnx + ;; + NEO-*:NONSTOP_KERNEL:*:*) + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; + NSR-*:NONSTOP_KERNEL:*:*) + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; + NSV-*:NONSTOP_KERNEL:*:*) + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; + NSX-*:NONSTOP_KERNEL:*:*) + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; + GUESS=mips-compaq-nonstopux + ;; BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; + GUESS=bs2000-siemens-sysv + ;; DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = "386"; then + if test "${cputype-}" = 386; then UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; + GUESS=pdp10-unknown-tops10 + ;; *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; + GUESS=pdp10-unknown-tenex + ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; + GUESS=pdp10-dec-tops20 + ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; + GUESS=pdp10-xkl-tops20 + ;; *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; + GUESS=pdp10-unknown-tops20 + ;; *:ITS:*:*) - echo pdp10-unknown-its - exit ;; + GUESS=pdp10-unknown-its + ;; SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; + GUESS=i386-pc-xenix + ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; - i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros - exit ;; + GUESS=$UNAME_MACHINE-pc-rdos + ;; + i*86:Fiwix:*:*) + GUESS=$UNAME_MACHINE-pc-fiwix + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-unknown-esx - exit ;; + GUESS=$UNAME_MACHINE-unknown-esx + ;; + amd64:Isilon\ OneFS:*:*) + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; +esac + +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case $UNAME_MACHINE:$UNAME_SYSTEM in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 < in order to provide the needed -information to handle your system. +our_year=`echo $timestamp | sed 's,-.*,,'` +thisyear=`date +%Y` +# shellcheck disable=SC2003 +script_age=`expr "$thisyear" - "$our_year"` +if test "$script_age" -lt 3 ; then + cat >&2 </dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" EOF +fi exit 1 # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/depends/config.site.in b/depends/config.site.in index f1c0c0738..8fe9e4e3b 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -1,54 +1,84 @@ -depends_prefix="`dirname ${ac_site_file}`/.." +# shellcheck shell=sh disable=SC2034 # Many variables set will be used in + # ./configure but shellcheck doesn't know + # that, hence: disable=SC2034 + +true # Dummy command because shellcheck treats all directives before first + # command as file-wide, and we only want to disable for one line. + # + # See: https://github.com/koalaman/shellcheck/wiki/Directive + +# shellcheck disable=SC2154 +depends_prefix="$(cd "$(dirname "$ac_site_file")/.." && pwd)" cross_compiling=maybe -host_alias=@HOST@ -ac_tool_prefix=${host_alias}- +host_alias="@HOST@" +ac_tool_prefix="${host_alias}-" -if test -z $with_boost; then - with_boost=$depends_prefix +if test -z "$with_boost"; then + with_boost="$depends_prefix" fi - -if test -z $enable_wallet && test -n "@no_wallet@"; then +if test -z "$enable_wallet" && test -n "@no_wallet@"; then enable_wallet=no fi -if test x@host_os@ = xdarwin; then +if test -z "$enable_zmq" && test -n "@no_zmq@"; then + enable_zmq=no +fi + +if test "@host_os@" = darwin; then BREW=no - PORT=no fi -PATH=$depends_prefix/native/bin:$PATH -PKG_CONFIG="`which pkg-config` --static" +if test -z "$enable_hardening" && test -n "@no_harden@"; then + enable_hardening=no +fi -# These two need to remain exported because pkg-config does not see them -# otherwise. That means they must be unexported at the end of configure.ac to -# avoid ruining the cache. Sigh. +PKG_CONFIG="$(which pkg-config) --static" -export PKG_CONFIG_LIBDIR=$depends_prefix/lib/pkgconfig -export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig +PKG_CONFIG_PATH="${depends_prefix}/share/pkgconfig:${depends_prefix}/lib/pkgconfig" +PKG_CONFIG_LIBDIR="${depends_prefix}/lib/pkgconfig" -CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS" -LDFLAGS="-L$depends_prefix/lib $LDFLAGS" +CPPFLAGS="-I${depends_prefix}/include/ ${CPPFLAGS}" +LDFLAGS="-L${depends_prefix}/lib ${LDFLAGS}" -CC="@CC@" -CXX="@CXX@" -OBJC="${CC}" -OBJCXX="${CXX}" +if test -n "@CC@" -a -z "${CC}"; then + CC="@CC@" +fi +if test -n "@CXX@" -a -z "${CXX}"; then + CXX="@CXX@" +fi if test -n "@AR@"; then - AR=@AR@ - ac_cv_path_ac_pt_AR=${AR} + AR="@AR@" + ac_cv_path_AR="${AR}" fi if test -n "@RANLIB@"; then - RANLIB=@RANLIB@ - ac_cv_path_ac_pt_RANLIB=${RANLIB} + RANLIB="@RANLIB@" + ac_cv_path_ac_pt_RANLIB="${RANLIB}" fi if test -n "@NM@"; then - NM=@NM@ - ac_cv_path_ac_pt_NM=${NM} + NM="@NM@" + ac_cv_path_ac_pt_NM="${NM}" +fi + +if test -n "@STRIP@"; then + STRIP="@STRIP@" + ac_cv_path_ac_pt_STRIP="${STRIP}" +fi + +if test "@host_os@" = darwin; then + if test -n "@OBJDUMP@"; then + OBJDUMP="@OBJDUMP@" + ac_cv_path_OBJDUMP="${OBJDUMP}" + fi + + if test -n "@DSYMUTIL@"; then + DSYMUTIL="@DSYMUTIL@" + ac_cv_path_DSYMUTIL="${DSYMUTIL}" + fi fi if test -n "@debug@"; then @@ -56,14 +86,14 @@ if test -n "@debug@"; then fi if test -n "@CFLAGS@"; then - CFLAGS="@CFLAGS@ $CFLAGS" + CFLAGS="@CFLAGS@ ${CFLAGS}" fi if test -n "@CXXFLAGS@"; then - CXXFLAGS="@CXXFLAGS@ $CXXFLAGS" + CXXFLAGS="@CXXFLAGS@ ${CXXFLAGS}" fi if test -n "@CPPFLAGS@"; then - CPPFLAGS="@CPPFLAGS@ $CPPFLAGS" + CPPFLAGS="@CPPFLAGS@ ${CPPFLAGS}" fi if test -n "@LDFLAGS@"; then - LDFLAGS="@LDFLAGS@ $LDFLAGS" + LDFLAGS="@LDFLAGS@ ${LDFLAGS}" fi diff --git a/depends/config.sub b/depends/config.sub index 8f1229c6f..defe52c0c 100755 --- a/depends/config.sub +++ b/depends/config.sub @@ -1,12 +1,14 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2015 Free Software Foundation, Inc. +# Copyright 1992-2023 Free Software Foundation, Inc. -timestamp='2015-03-08' +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2023-09-19' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or +# the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but @@ -15,7 +17,7 @@ timestamp='2015-03-08' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see . +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -33,7 +35,7 @@ timestamp='2015-03-08' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -50,15 +52,21 @@ timestamp='2015-03-08' # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -68,13 +76,13 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright 1992-2015 Free Software Foundation, Inc. +Copyright 1992-2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" -Try \`$me --help' for more information." +Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do @@ -90,12 +98,12 @@ while test $# -gt 0 ; do - ) # Use stdin as input. break ;; -* ) - echo "$me: invalid option $1$help" + echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. - echo $1 + echo "$1" exit ;; * ) @@ -111,1231 +119,1165 @@ case $# in exit 1;; esac -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ - kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - android-linux) - os=-linux-android - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac +# Split fields of configuration type +# shellcheck disable=SC2162 +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova* | managarm-* \ + | windows-* ) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac ;; - -psos*) - os=-psos + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + basic_os= + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + basic_os=bsd + ;; + convex-c2) + basic_machine=c2-convex + basic_os=bsd + ;; + convex-c32) + basic_machine=c32-convex + basic_os=bsd + ;; + convex-c34) + basic_machine=c34-convex + basic_os=bsd + ;; + convex-c38) + basic_machine=c38-convex + basic_os=bsd + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac ;; esac -# Decode aliases for certain CPU-COMPANY combinations. +# Decode 1-component or ad-hoc basic machines case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ - | avr | avr32 \ - | be32 | be64 \ - | bfin \ - | c4x | c8051 | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | e2k | epiphany \ - | fido | fr30 | frv | ft32 \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | k1om \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r6 | mipsisa32r6el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r6 | mipsisa64r6el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 | nios2eb | nios2el \ - | ns16k | ns32k \ - | open8 | or1k | or1knd | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pyramid \ - | riscv32 | riscv64 \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | visium \ - | we32k \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - leon|leon[3-9]) - basic_machine=sparc-$basic_machine - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) - basic_machine=$basic_machine-unknown - os=-none + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + op50n) + cpu=hppa1.1 + vendor=oki ;; - ms1) - basic_machine=mt-unknown + op60c) + cpu=hppa1.1 + vendor=oki ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown + ibm*) + cpu=i370 + vendor=ibm ;; - xgate) - basic_machine=$basic_machine-unknown - os=-none + orion105) + cpu=clipper + vendor=highlevel ;; - xscaleeb) - basic_machine=armeb-unknown + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple ;; - - xscaleel) - basic_machine=armel-unknown + pmac | pmac-mpw) + cpu=powerpc + vendor=apple ;; - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | c8051-* | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | e2k-* | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | k1om-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa32r6-* | mipsisa32r6el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64r6-* | mipsisa64r6el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | or1k*-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pyramid-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | visium-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown - ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att + cpu=m68000 + vendor=att ;; 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - asmjs) - basic_machine=asmjs-unknown - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + cpu=we32k + vendor=att ;; bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec + cpu=powerpc + vendor=ibm + basic_os=cnk ;; decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 + cpu=pdp10 + vendor=dec + basic_os=tops10 ;; decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 + cpu=pdp10 + vendor=dec + basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola + cpu=m68k + vendor=motorola ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd + dpx2*) + cpu=m68k + vendor=bull + basic_os=sysv3 ;; encore | umax | mmax) - basic_machine=ns32k-encore + cpu=ns32k + vendor=encore ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} ;; fx2800) - basic_machine=i860-alliant + cpu=i860 + vendor=alliant ;; genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 + cpu=ns32k + vendor=ns ;; h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp + cpu=m68000 + vendor=hp ;; hp9k3[2-9][0-9]) - basic_machine=m68k-hp + cpu=m68k + vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm + cpu=hppa1.0 + vendor=hp ;; i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 ;; i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 ;; i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv ;; i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} ;; iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) + cpu=mips + vendor=sgi + case $basic_os in + irix*) ;; *) - os=-irix4 + basic_os=irix4 ;; esac ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - leon-*|leon[3-9]-*) - basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze*) - basic_machine=microblaze-xilinx - ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; - mingw32) - basic_machine=i686-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - moxiebox) - basic_machine=moxie-unknown - os=-moxiebox - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + cpu=m68000 + vendor=convergent ;; - msys) - basic_machine=i686-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint ;; news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $basic_os in + openstep*) + ;; + nextstep*) ;; - -ns2*) - os=-nextstep2 + ns2*) + basic_os=nextstep2 ;; *) - os=-nextstep3 + basic_os=nextstep3 ;; esac ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem + cpu=np1 + vendor=gould ;; op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k + cpu=hppa1.1 + vendor=oki + basic_os=proelf ;; pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 ;; pbd) - basic_machine=sparc-tti + cpu=sparc + vendor=tti ;; pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 + cpu=m68k + vendor=tti ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + pc532) + cpu=ns32k + vendor=pc532 ;; pn) - basic_machine=pn-gould + cpu=pn + vendor=gould ;; - power) basic_machine=power-ibm - ;; - ppc | ppcbe) basic_machine=powerpc-unknown - ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + power) + cpu=power + vendor=ibm ;; ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - os=-rdos - ;; - rdos32) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff + cpu=i386 + vendor=ibm ;; rm[46]00) - basic_machine=mips-siemens + cpu=mips + vendor=siemens ;; rtpc | rtpc-*) - basic_machine=romp-ibm + cpu=romp + vendor=ibm ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks ;; - sde) - basic_machine=mipsisa32-sde - os=-elf + tower | tower-32) + cpu=m68k + vendor=ncr ;; - sei) - basic_machine=mips-sei - os=-seiux + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu ;; - sequent) - basic_machine=i386-sequent + w65) + cpu=w65 + vendor=wdc ;; - sh) - basic_machine=sh-hitachi - os=-hms + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf ;; - sh5el) - basic_machine=sh5le-unknown + none) + cpu=none + vendor=none ;; - sh64) - basic_machine=sh64-unknown + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 + + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read cpu vendor <&2 - exit 1 + # Recognize the canonical CPU types that are allowed with any + # company name. + case $cpu in + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be | aarch64c | arm64ec \ + | abacus \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ + | alphapca5[67] | alpha64pca5[67] \ + | am33_2.0 \ + | amdgcn \ + | arc | arceb | arc32 | arc64 \ + | arm | arm[lb]e | arme[lb] | armv* \ + | avr | avr32 \ + | asmjs \ + | ba \ + | be32 | be64 \ + | bfin | bpf | bs2000 \ + | c[123]* | c30 | [cjt]90 | c4x \ + | c8051 | clipper | craynv | csky | cydra \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | elxsi | epiphany \ + | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ + | javascript \ + | h8300 | h8500 \ + | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i*86 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | kvx \ + | le32 | le64 \ + | lm32 \ + | loongarch32 | loongarch64 \ + | m32c | m32r | m32rle \ + | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ + | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ + | m88110 | m88k | maxq | mb | mcore | mep | metag \ + | microblaze | microblazeel \ + | mips* \ + | mmix \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ + | none | np1 | ns16k | ns32k | nvptx \ + | open8 \ + | or1k* \ + | or32 \ + | orion \ + | picochip \ + | pdp10 | pdp11 | pj | pjl | pn | power \ + | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ + | pru \ + | pyramid \ + | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ + | rl78 | romp | rs6000 | rx \ + | s390 | s390x \ + | score \ + | sh | shl \ + | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ + | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ + | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ + | spu \ + | tahoe \ + | thumbv7* \ + | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ + | tron \ + | ubicom32 \ + | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ + | vax \ + | visium \ + | w65 \ + | wasm32 | wasm64 \ + | we32k \ + | x86 | x86_64 | xc16x | xgate | xps100 \ + | xstormy16 | xtensa* \ + | ymp \ + | z8k | z80) + ;; + + *) + echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2 + exit 1 + ;; + esac ;; esac # Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` +case $vendor in + digital*) + vendor=dec ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + commodore*) + vendor=cbm ;; *) ;; @@ -1343,200 +1285,226 @@ esac # Decode manufacturer-specific aliases for certain operating systems. -if [ x"$os" != x"" ] +if test x"$basic_os" != x then + +# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just +# set os. +obj= +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read kernel os <&2 - exit 1 + # No normalization, but not necessarily accepted, that comes below. ;; esac + else # Here we handle the default operating systems that come with various machines. @@ -1549,261 +1517,443 @@ else # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. -case $basic_machine in +kernel= +obj= +case $cpu-$vendor in score-*) - os=-elf + os= + obj=elf ;; spu-*) - os=-elf + os= + obj=elf ;; *-acorn) - os=-riscix1.2 + os=riscix1.2 ;; arm*-rebel) - os=-linux + kernel=linux + os=gnu ;; arm*-semi) - os=-aout + os= + obj=aout ;; c4x-* | tic4x-*) - os=-coff + os= + obj=coff ;; c8051-*) - os=-elf + os= + obj=elf + ;; + clipper-intergraph) + os=clix ;; hexagon-*) - os=-elf + os= + obj=elf ;; tic54x-*) - os=-coff + os= + obj=coff ;; tic55x-*) - os=-coff + os= + obj=coff ;; tic6x-*) - os=-coff + os= + obj=coff ;; # This must come before the *-dec entry. pdp10-*) - os=-tops20 + os=tops20 ;; pdp11-*) - os=-none + os=none ;; *-dec | vax-*) - os=-ultrix4.2 + os=ultrix4.2 ;; m68*-apollo) - os=-domain + os=domain ;; i386-sun) - os=-sunos4.0.2 + os=sunos4.0.2 ;; m68000-sun) - os=-sunos3 + os=sunos3 ;; m68*-cisco) - os=-aout + os= + obj=aout ;; mep-*) - os=-elf + os= + obj=elf ;; mips*-cisco) - os=-elf + os= + obj=elf ;; mips*-*) - os=-elf + os= + obj=elf ;; or32-*) - os=-coff + os= + obj=coff ;; *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 + os=sysv3 ;; sparc-* | *-sun) - os=-sunos4.1.1 + os=sunos4.1.1 ;; - *-be) - os=-beos + pru-*) + os= + obj=elf ;; - *-haiku) - os=-haiku + *-be) + os=beos ;; *-ibm) - os=-aix + os=aix ;; *-knuth) - os=-mmixware + os=mmixware ;; *-wec) - os=-proelf + os=proelf ;; *-winbond) - os=-proelf + os=proelf ;; *-oki) - os=-proelf + os=proelf ;; *-hp) - os=-hpux + os=hpux ;; *-hitachi) - os=-hiux + os=hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv + os=sysv ;; *-cbm) - os=-amigaos + os=amigaos ;; *-dg) - os=-dgux + os=dgux ;; *-dolphin) - os=-sysv3 + os=sysv3 ;; m68k-ccur) - os=-rtu + os=rtu ;; m88k-omron*) - os=-luna + os=luna ;; - *-next ) - os=-nextstep + *-next) + os=nextstep ;; *-sequent) - os=-ptx + os=ptx ;; *-crds) - os=-unos + os=unos ;; *-ns) - os=-genix + os=genix ;; i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 + os=mvs ;; *-gould) - os=-sysv + os=sysv ;; *-highlevel) - os=-bsd + os=bsd ;; *-encore) - os=-bsd + os=bsd ;; *-sgi) - os=-irix + os=irix ;; *-siemens) - os=-sysv4 + os=sysv4 ;; *-masscomp) - os=-rtu + os=rtu ;; f30[01]-fujitsu | f700-fujitsu) - os=-uxpv + os=uxpv ;; *-rom68k) - os=-coff + os= + obj=coff ;; *-*bug) - os=-coff + os= + obj=coff ;; *-apple) - os=-macos + os=macos ;; *-atari*) - os=-mint + os=mint + ;; + *-wrs) + os=vxworks ;; *) - os=-none + os=none ;; esac + fi +# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ). + +case $os in + # Sometimes we do "kernel-libc", so those need to count as OSes. + musl* | newlib* | relibc* | uclibc*) + ;; + # Likewise for "kernel-abi" + eabi* | gnueabi*) + ;; + # VxWorks passes extra cpu info in the 4th filed. + simlinux | simwindows | spe) + ;; + # See `case $cpu-$os` validation below + ghcjs) + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ + | hiux* | abug | nacl* | netware* | windows* \ + | os9* | macos* | osx* | ios* | tvos* | watchos* \ + | mpw* | magic* | mmixware* | mon960* | lnews* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | mirbsd* | netbsd* | dicos* | openedition* | ose* \ + | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ + | bosx* | nextstep* | cxux* | oabi* \ + | ptx* | ecoff* | winnt* | domain* | vsta* \ + | udi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* | serenity* \ + | cygwin* | msys* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | mint* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ + | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ + | fiwix* | mlibc* | cos* | mbr* ) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + none) + ;; + kernel* | msvc* ) + # Restricted further below + ;; + '') + if test x"$obj" = x + then + echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2 + fi + ;; + *) + echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 + exit 1 + ;; +esac + +case $obj in + aout* | coff* | elf* | pe*) + ;; + '') + # empty is fine + ;; + *) + echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 + exit 1 + ;; +esac + +# Here we handle the constraint that a (synthetic) cpu and os are +# valid only in combination with each other and nowhere else. +case $cpu-$os in + # The "javascript-unknown-ghcjs" triple is used by GHC; we + # accept it here in order to tolerate that, but reject any + # variations. + javascript-ghcjs) + ;; + javascript-* | *-ghcjs) + echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os-$obj in + linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \ + | linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- ) + ;; + uclinux-uclibc*- ) + ;; + managarm-mlibc*- | managarm-kernel*- ) + ;; + windows*-msvc*-) + ;; + -dietlibc*- | -newlib*- | -musl*- | -relibc*- | -uclibc*- | -mlibc*- ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + -kernel*- ) + echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + *-kernel*- ) + echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 + exit 1 + ;; + *-msvc*- ) + echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 + exit 1 + ;; + kfreebsd*-gnu*- | kopensolaris*-gnu*-) + ;; + vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) + ;; + nto-qnx*-) + ;; + os2-emx-) + ;; + *-eabi*- | *-gnueabi*-) + ;; + none--*) + # None (no kernel, i.e. freestanding / bare metal), + # can be paired with an machine code file format + ;; + -*-) + # Blank kernel with real OS is always fine. + ;; + --*) + # Blank kernel and OS with real machine code file format is always fine. + ;; + *-*-*) + echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 + exit 1 + ;; +esac + # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) vendor=acorn ;; - -sunos*) + *-sunos*) vendor=sun ;; - -cnk*|-aix*) + *-cnk* | *-aix*) vendor=ibm ;; - -beos*) + *-beos*) vendor=be ;; - -hpux*) + *-hpux*) vendor=hp ;; - -mpeix*) + *-mpeix*) vendor=hp ;; - -hiux*) + *-hiux*) vendor=hitachi ;; - -unos*) + *-unos*) vendor=crds ;; - -dgux*) + *-dgux*) vendor=dg ;; - -luna*) + *-luna*) vendor=omron ;; - -genix*) + *-genix*) vendor=ns ;; - -mvs* | -opened*) + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) vendor=ibm ;; - -os400*) + s390-* | s390x-*) vendor=ibm ;; - -ptx*) + *-ptx*) vendor=sequent ;; - -tpf*) + *-tpf*) vendor=ibm ;; - -vxsim* | -vxworks* | -windiss*) + *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; - -aux*) + *-aux*) vendor=apple ;; - -hms*) + *-hms*) vendor=hitachi ;; - -mpw* | -macos*) + *-mpw* | *-macos*) vendor=apple ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; - -vos*) + *-vos*) vendor=stratus ;; esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac -echo $basic_machine$os +echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" exit # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/depends/description.md b/depends/description.md index 74f9ef3f2..13d94d9f8 100644 --- a/depends/description.md +++ b/depends/description.md @@ -1,4 +1,4 @@ -This is a system of building and caching dependencies necessary for building Bitcoin. +This is a system of building and caching dependencies necessary for building BitcoinZ. There are several features that make it different from most similar systems: ### It is designed to be builder and host agnostic @@ -6,13 +6,12 @@ There are several features that make it different from most similar systems: In theory, binaries for any target OS/architecture can be created, from a builder running any OS/architecture. In practice, build-side tools must be specified when the defaults don't fit, and packages must be amended to work -on new hosts. For now, a build architecture of x86_64 is assumed, either on -Linux or OSX. +on new hosts. ### No reliance on timestamps File presence is used to determine what needs to be built. This makes the -results distributable and easily digestable by automated builders. +results distributable and easily digestible by automated builders. ### Each build only has its specified dependencies available at build-time. @@ -26,9 +25,9 @@ Before building, a unique build-id is generated for each package. This id consists of a hash of all files used to build the package (Makefiles, packages, etc), and as well as a hash of the same data for each recursive dependency. If any portion of a package's build recipe changes, it will be rebuilt as well as -any other package that depends on it. If any of the main makefiles (Makefile, +any other package that depends on it. If any of the main makefiles (Makefile, funcs.mk, etc) are changed, all packages will be rebuilt. After building, the -results are cached into a tarball that can be re-used and distributed. +results are cached into a tarball that can be reused and distributed. ### Package build results are (relatively) deterministic. diff --git a/depends/fix-elf-interpreter.sh b/depends/fix-elf-interpreter.sh new file mode 100755 index 000000000..a7286950e --- /dev/null +++ b/depends/fix-elf-interpreter.sh @@ -0,0 +1,2 @@ +echo Fixing ELF interpreter on $1 +patchelf --set-interpreter "$(patchelf --print-interpreter "$(realpath "$(which sh)")")" $1 diff --git a/depends/funcs.mk b/depends/funcs.mk index 35ca5abcf..4f1b5a25b 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -1,17 +1,22 @@ define int_vars #Set defaults for vars which may be overridden per-package -$(1)_cc=$($($(1)_type)_CC) -$(1)_cxx=$($($(1)_type)_CXX) -$(1)_objc=$($($(1)_type)_OBJC) -$(1)_objcxx=$($($(1)_type)_OBJCXX) -$(1)_ar=$($($(1)_type)_AR) -$(1)_ranlib=$($($(1)_type)_RANLIB) -$(1)_libtool=$($($(1)_type)_LIBTOOL) -$(1)_nm=$($($(1)_type)_NM) -$(1)_cflags=$($($(1)_type)_CFLAGS) $($($(1)_type)_$(release_type)_CFLAGS) -$(1)_cxxflags=$($($(1)_type)_CXXFLAGS) $($($(1)_type)_$(release_type)_CXXFLAGS) -$(1)_ldflags=$($($(1)_type)_LDFLAGS) $($($(1)_type)_$(release_type)_LDFLAGS) -L$($($(1)_type)_prefix)/lib -$(1)_cppflags=$($($(1)_type)_CPPFLAGS) $($($(1)_type)_$(release_type)_CPPFLAGS) -I$($($(1)_type)_prefix)/include +$(1)_cc=$$($$($(1)_type)_CC) +$(1)_cxx=$$($$($(1)_type)_CXX) +$(1)_objc=$$($$($(1)_type)_OBJC) +$(1)_objcxx=$$($$($(1)_type)_OBJCXX) +$(1)_ar=$$($$($(1)_type)_AR) +$(1)_ranlib=$$($$($(1)_type)_RANLIB) +$(1)_nm=$$($$($(1)_type)_NM) +$(1)_cflags=$$($$($(1)_type)_CFLAGS) \ + $$($$($(1)_type)_$$(release_type)_CFLAGS) +$(1)_cxxflags=$$($$($(1)_type)_CXXFLAGS) \ + $$($$($(1)_type)_$$(release_type)_CXXFLAGS) +$(1)_ldflags=$$($$($(1)_type)_LDFLAGS) \ + $$($$($(1)_type)_$$(release_type)_LDFLAGS) \ + -L$$($($(1)_type)_prefix)/lib +$(1)_cppflags=$$($$($(1)_type)_CPPFLAGS) \ + $$($$($(1)_type)_$$(release_type)_CPPFLAGS) \ + -I$$($$($(1)_type)_prefix)/include $(1)_recipe_hash:= endef @@ -19,15 +24,19 @@ define int_get_all_dependencies $(sort $(foreach dep,$(2),$(2) $(call int_get_all_dependencies,$(1),$($(dep)_dependencies)))) endef -define fetch_file -(test -f $$($(1)_source_dir)/$(4) || \ - ( mkdir -p $$($(1)_download_dir) && echo Fetching $(1)... && \ - ( $(build_DOWNLOAD) "$$($(1)_download_dir)/$(4).temp" "$(PRIORITY_DOWNLOAD_PATH)/$(4)" || \ - $(build_DOWNLOAD) "$$($(1)_download_dir)/$(4).temp" "$(2)/$(3)" ) && \ +define fetch_file_inner + ( mkdir -p $$($(1)_download_dir) && echo Fetching $(3) from $(2) && \ + $(build_DOWNLOAD) "$$($(1)_download_dir)/$(4).temp" "$(2)/$(3)" && \ echo "$(5) $$($(1)_download_dir)/$(4).temp" > $$($(1)_download_dir)/.$(4).hash && \ $(build_SHA256SUM) -c $$($(1)_download_dir)/.$(4).hash && \ mv $$($(1)_download_dir)/$(4).temp $$($(1)_source_dir)/$(4) && \ - rm -rf $$($(1)_download_dir) )) + rm -rf $$($(1)_download_dir) ) +endef + +define fetch_file + ( test -f $$($(1)_source_dir)/$(4) || \ + ( $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)) || \ + $(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5)))) endef define generate_crate_checksum @@ -52,16 +61,12 @@ endef define int_get_build_id $(eval $(1)_dependencies += $($(1)_$(host_arch)_$(host_os)_dependencies) $($(1)_$(host_os)_dependencies)) -$(eval $(1)_all_dependencies:=$(call int_get_all_dependencies,$(1),$($($(1)_type)_native_toolchain) $($(1)_dependencies))) +$(eval $(1)_all_dependencies:=$(call int_get_all_dependencies,$(1),$($(1)_dependencies))) $(foreach dep,$($(1)_all_dependencies),$(eval $(1)_build_id_deps+=$(dep)-$($(dep)_version)-$($(dep)_recipe_hash))) -$(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_type) $($(1)_build_id_deps)) +$(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_type) $($(1)_build_id_deps) $($($(1)_type)_id)) $(eval $(1)_build_id:=$(shell echo -n "$($(1)_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH))) final_build_id_long+=$($(package)_build_id_long) -#override platform specific files and hashes -$(eval $(1)_file_name=$(if $($(1)_exact_file_name),$($(1)_exact_file_name),$(if $($(1)_file_name_$(host_os)),$($(1)_file_name_$(host_os)),$($(1)_file_name)))) -$(eval $(1)_sha256_hash=$(if $($(1)_exact_sha256_hash),$($(1)_exact_sha256_hash),$(if $($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash_$(host_os)),$($(1)_sha256_hash)))) - #compute package-specific paths $(1)_build_subdir?=. $(1)_download_file?=$($(1)_file_name) @@ -76,6 +81,7 @@ $(1)_cached_checksum:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_bui $(1)_patch_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id)/.patches-$($(1)_build_id) $(1)_prefixbin:=$($($(1)_type)_prefix)/bin/ $(1)_cached:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz +$(1)_build_log:=$(BASEDIR)/$(1)-$($(1)_version)-$($(1)_build_id).log $(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources) #stamps @@ -84,7 +90,7 @@ $(1)_extracted=$$($(1)_extract_dir)/.stamp_extracted $(1)_preprocessed=$$($(1)_extract_dir)/.stamp_preprocessed $(1)_cleaned=$$($(1)_extract_dir)/.stamp_cleaned $(1)_built=$$($(1)_build_dir)/.stamp_built -$(1)_configured=$$($(1)_build_dir)/.stamp_configured +$(1)_configured=$(host_prefix)/.$(1)_stamp_configured $(1)_staged=$$($(1)_staging_dir)/.stamp_staged $(1)_postprocessed=$$($(1)_staging_prefix_dir)/.stamp_postprocessed $(1)_download_path_fixed=$(subst :,\:,$$($(1)_download_path)) @@ -93,11 +99,11 @@ $(1)_download_path_fixed=$(subst :,\:,$$($(1)_download_path)) #default commands # The default behavior for tar will try to set ownership when running as uid 0 and may not succeed, --no-same-owner disables this behavior $(1)_fetch_cmds ?= $(call fetch_file,$(1),$(subst \:,:,$$($(1)_download_path_fixed)),$$($(1)_download_file),$($(1)_file_name),$($(1)_sha256_hash)) -$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && tar --no-same-owner --strip-components=1 -xf $$($(1)_source) -$(1)_preprocess_cmds ?= -$(1)_build_cmds ?= -$(1)_config_cmds ?= -$(1)_stage_cmds ?= +$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_TAR) --no-same-owner --strip-components=1 -xf $$($(1)_source) +$(1)_preprocess_cmds ?= true +$(1)_build_cmds ?= true +$(1)_config_cmds ?= true +$(1)_stage_cmds ?= true $(1)_set_vars ?= @@ -145,11 +151,18 @@ $(1)_config_env+=$($(1)_config_env_$(host_arch)_$(host_os)) $($(1)_config_env_$( $(1)_config_env+=PKG_CONFIG_LIBDIR=$($($(1)_type)_prefix)/lib/pkgconfig $(1)_config_env+=PKG_CONFIG_PATH=$($($(1)_type)_prefix)/share/pkgconfig +$(1)_config_env+=PKG_CONFIG_SYSROOT_DIR=/ +$(1)_config_env+=CMAKE_MODULE_PATH=$($($(1)_type)_prefix)/lib/cmake $(1)_config_env+=PATH="$(build_prefix)/bin:$(PATH)" $(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)" $(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)" -$(1)_autoconf=./configure --host=$($($(1)_type)_host) --disable-dependency-tracking --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)" +# Setting a --build type that differs from --host will explicitly enable +# cross-compilation mode. Note that --build defaults to the output of +# config.guess, which is what we set it too here. This also quells autoconf +# warnings, "If you wanted to set the --build type, don't use --host.", +# when using versions older than 2.70. +$(1)_autoconf=./configure --build=$(BUILD) --host=$($($(1)_type)_host) --prefix=$($($(1)_type)_prefix) --with-pic $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)" ifneq ($($(1)_nm),) $(1)_autoconf += NM="$$($(1)_nm)" endif @@ -171,57 +184,91 @@ endif ifneq ($($(1)_ldflags),) $(1)_autoconf += LDFLAGS="$$($(1)_ldflags)" endif + +# We hardcode the library install path to "lib" to match the PKG_CONFIG_PATH +# setting in depends/config.site.in, which also hardcodes "lib". +# Without this setting, CMake by default would use the OS library +# directory, which might be "lib64" or something else, not "lib", on multiarch systems. +$(1)_cmake=env CC="$$($(1)_cc)" \ + CFLAGS="$$($(1)_cppflags) $$($(1)_cflags)" \ + CXX="$$($(1)_cxx)" \ + CXXFLAGS="$$($(1)_cppflags) $$($(1)_cxxflags)" \ + LDFLAGS="$$($(1)_ldflags)" \ + cmake -DCMAKE_INSTALL_PREFIX:PATH="$$($($(1)_type)_prefix)" \ + -DCMAKE_AR=`which $$($(1)_ar)` \ + -DCMAKE_NM=`which $$($(1)_nm)` \ + -DCMAKE_RANLIB=`which $$($(1)_ranlib)` \ + -DCMAKE_INSTALL_LIBDIR=lib/ \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DCMAKE_VERBOSE_MAKEFILE:BOOL=$(V) \ + $$($(1)_config_opts) +ifeq ($($(1)_type),build) +$(1)_cmake += -DCMAKE_INSTALL_RPATH:PATH="$$($($(1)_type)_prefix)/lib" +else +ifneq ($(host),$(build)) +$(1)_cmake += -DCMAKE_SYSTEM_NAME=$($(host_os)_cmake_system) +$(1)_cmake += -DCMAKE_C_COMPILER_TARGET=$(host) +$(1)_cmake += -DCMAKE_CXX_COMPILER_TARGET=$(host) +endif +endif endef define int_add_cmds +ifneq ($(LOG),) +$(1)_logging = >>$$($(1)_build_log) 2>&1 || { if test -f $$($(1)_build_log); then cat $$($(1)_build_log); fi; exit 1; } +endif + $($(1)_fetched): - $(AT)mkdir -p $$(@D) $(SOURCES_PATH) - $(AT)rm -f $$@ - $(AT)touch $$@ - $(AT)cd $$(@D); $(call $(1)_fetch_cmds,$(1)) - $(AT)cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);) - $(AT)touch $$@ + mkdir -p $$(@D) $(SOURCES_PATH) + rm -f $$@ + touch $$@ + cd $$(@D); $($(1)_fetch_cmds) + cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);) + touch $$@ $($(1)_extracted): | $($(1)_fetched) - $(AT)echo Extracting $(1)... - $(AT)mkdir -p $$(@D) - $(AT)cd $$(@D); $(call $(1)_extract_cmds,$(1)) - $(AT)touch $$@ -$($(1)_preprocessed): | $($(1)_dependencies) $($(1)_extracted) - $(AT)echo Preprocessing $(1)... - $(AT)mkdir -p $$(@D) $($(1)_patch_dir) - $(AT)$(foreach patch,$($(1)_patches),cd $(PATCHES_PATH)/$(1); cp $(patch) $($(1)_patch_dir) ;) - $(AT)cd $$(@D); $(call $(1)_preprocess_cmds, $(1)) - $(AT)touch $$@ -$($(1)_configured): | $($(1)_preprocessed) - $(AT)echo Configuring $(1)... - $(AT)rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), tar --no-same-owner -xf $($(package)_cached); ) - $(AT)mkdir -p $$(@D) - $(AT)+cd $$(@D); $($(1)_config_env) $(call $(1)_config_cmds, $(1)) - $(AT)touch $$@ + echo Extracting $(1)... + mkdir -p $$(@D) + cd $$(@D); $($(1)_extract_cmds) + touch $$@ +$($(1)_preprocessed): | $($(1)_extracted) + echo Preprocessing $(1)... + mkdir -p $$(@D) $($(1)_patch_dir) + $(foreach patch,$($(1)_patches),cd $(PATCHES_PATH)/$(1); cp $(patch) $($(1)_patch_dir) ;) + { cd $$(@D); $($(1)_preprocess_cmds); } $$($(1)_logging) + touch $$@ +$($(1)_configured): | $($(1)_dependencies) $($(1)_preprocessed) + echo Configuring $(1)... + rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), $(build_TAR) --no-same-owner -xf $($(package)_cached); ) + mkdir -p $$($(1)_build_dir) + +{ cd $$($(1)_build_dir); export $($(1)_config_env); $($(1)_config_cmds); } $$($(1)_logging) + touch $$@ $($(1)_built): | $($(1)_configured) - $(AT)echo Building $(1)... - $(AT)mkdir -p $$(@D) - $(AT)+cd $$(@D); $($(1)_build_env) $(call $(1)_build_cmds, $(1)) - $(AT)touch $$@ + echo Building $(1)... + mkdir -p $$(@D) + +{ cd $$(@D); export $($(1)_build_env); $($(1)_build_cmds); } $$($(1)_logging) + touch $$@ $($(1)_staged): | $($(1)_built) - $(AT)echo Staging $(1)... - $(AT)mkdir -p $($(1)_staging_dir)/$(host_prefix) - $(AT)cd $($(1)_build_dir); $($(1)_stage_env) $(call $(1)_stage_cmds, $(1)) - $(AT)rm -rf $($(1)_extract_dir) - $(AT)touch $$@ + echo Staging $(1)... + mkdir -p $($(1)_staging_dir)/$(host_prefix) + +{ cd $($(1)_build_dir); export $($(1)_stage_env); $($(1)_stage_cmds); } $$($(1)_logging) + rm -rf $($(1)_extract_dir) + touch $$@ $($(1)_postprocessed): | $($(1)_staged) - $(AT)echo Postprocessing $(1)... - $(AT)cd $($(1)_staging_prefix_dir); $(call $(1)_postprocess_cmds) - $(AT)touch $$@ + echo Postprocessing $(1)... + cd $($(1)_staging_prefix_dir); $($(1)_postprocess_cmds) + touch $$@ $($(1)_cached): | $($(1)_dependencies) $($(1)_postprocessed) - $(AT)echo Caching $(1)... - $(AT)cd $$($(1)_staging_dir)/$(host_prefix); find . | sort | tar --no-recursion -czf $$($(1)_staging_dir)/$$(@F) -T - - $(AT)mkdir -p $$(@D) - $(AT)rm -rf $$(@D) && mkdir -p $$(@D) - $(AT)mv $$($(1)_staging_dir)/$$(@F) $$(@) - $(AT)rm -rf $($(1)_staging_dir) + echo Caching $(1)... + cd $$($(1)_staging_dir)/$(host_prefix); \ + find . ! -name '.stamp_postprocessed' -print0 | TZ=UTC xargs -0r $(build_TOUCH); \ + find . ! -name '.stamp_postprocessed' | LC_ALL=C sort | $(build_TAR) --numeric-owner --no-recursion -czf $$($(1)_staging_dir)/$$(@F) -T - + mkdir -p $$(@D) + rm -rf $$(@D) && mkdir -p $$(@D) + mv $$($(1)_staging_dir)/$$(@F) $$(@) + rm -rf $($(1)_staging_dir) + if test -f $($(1)_build_log); then mv $($(1)_build_log) $$(@D); fi $($(1)_cached_checksum): $($(1)_cached) - $(AT)cd $$(@D); $(build_SHA256SUM) $$( $$(@) + cd $$(@D); $(build_SHA256SUM) $$( $$(@) .PHONY: $(1) $(1): | $($(1)_cached_checksum) @@ -229,6 +276,14 @@ $(1): | $($(1)_cached_checksum) endef +stages = fetched extracted preprocessed configured built staged postprocessed cached cached_checksum + +define ext_add_stages +$(foreach stage,$(stages), + $(1)_$(stage): $($(1)_$(stage)) + .PHONY: $(1)_$(stage)) +endef + # These functions create the build targets for each package. They must be # broken down into small steps so that each part is done for all packages # before moving on to the next step. Otherwise, a package's info @@ -243,7 +298,8 @@ $(foreach package,$(packages),$(eval $(package)_type=$(host_arch)_$(host_os))) $(foreach package,$(all_packages),$(eval $(call int_vars,$(package)))) #include package files -$(foreach package,$(all_packages),$(eval include packages/$(package).mk)) +$(foreach native_package,$(native_packages),$(eval include packages/$(native_package).mk)) +$(foreach package,$(packages),$(eval include packages/$(package).mk)) #compute a hash of all files that comprise this package's build recipe $(foreach package,$(all_packages),$(eval $(call int_get_build_recipe_hash,$(package)))) @@ -256,6 +312,3 @@ $(foreach package,$(all_packages),$(eval $(call int_config_attach_build_config,$ #create build targets $(foreach package,$(all_packages),$(eval $(call int_add_cmds,$(package)))) - -#special exception: if a toolchain package exists, all non-native packages depend on it -$(foreach package,$(packages),$(eval $($(package)_unpacked): |$($($(host_arch)_$(host_os)_native_toolchain)_cached) )) diff --git a/depends/gen_id b/depends/gen_id new file mode 100755 index 000000000..e2e2273b2 --- /dev/null +++ b/depends/gen_id @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +# Usage: env [ CC=... ] [ C_STANDARD=...] [ CXX=... ] [CXX_STANDARD=...] \ +# [ AR=... ] [ NM=... ] [ RANLIB=... ] [ STRIP=... ] [ DEBUG=... ] \ +# [ LTO=... ] [ NO_HARDEN=... ] ./build-id [ID_SALT]... +# +# Prints to stdout a SHA256 hash representing the current toolset, used by +# depends/Makefile as a build id for caching purposes (detecting when the +# toolset has changed and the cache needs to be invalidated). +# +# If the DEBUG environment variable is non-empty and the system has `tee` +# available in its $PATH, the pre-image to the SHA256 hash will be printed to +# stderr. This is to help developers debug caching issues in depends. + +# This script explicitly does not `set -e` because id determination is mostly +# opportunistic: it is fine that things fail, as long as they fail consistently. + +# Command variables (CC/CXX/AR) which can be blank are invoked with `bash -c`, +# because the "command not found" error message printed by shells often include +# the line number, like so: +# +# ./depends/gen_id: line 43: --version: command not found +# +# By invoking with `bash -c`, we ensure that the line number is always 1 + +( + # Redirect stderr to stdout + exec 2>&1 + + echo "BEGIN ALL" + + # Include any ID salts supplied via command line + echo "BEGIN ID SALT" + echo "$@" + echo "END ID SALT" + + # GCC only prints COLLECT_LTO_WRAPPER when invoked with just "-v", but we want + # the information from "-v -E -" as well, so just include both. + echo "BEGIN CC" + bash -c "${CC} -v" + bash -c "${CC} -v -E -xc -o /dev/null - < /dev/null" + bash -c "${CC} -v -E -xobjective-c -o /dev/null - < /dev/null" + echo "C_STANDARD=${C_STANDARD}" + echo "END CC" + + echo "BEGIN CXX" + bash -c "${CXX} -v" + bash -c "${CXX} -v -E -xc++ -o /dev/null - < /dev/null" + bash -c "${CXX} -v -E -xobjective-c++ -o /dev/null - < /dev/null" + echo "CXX_STANDARD=${CXX_STANDARD}" + echo "END CXX" + + echo "BEGIN AR" + bash -c "${AR} --version" + env | grep '^AR_' + echo "END AR" + + echo "BEGIN NM" + bash -c "${NM} --version" + env | grep '^NM_' + echo "END NM" + + echo "BEGIN RANLIB" + bash -c "${RANLIB} --version" + env | grep '^RANLIB_' + echo "END RANLIB" + + echo "BEGIN STRIP" + bash -c "${STRIP} --version" + env | grep '^STRIP_' + echo "END STRIP" + + echo "BEGIN LTO" + echo "LTO=${LTO}" + echo "END LTO" + + echo "BEGIN NO_HARDEN" + echo "NO_HARDEN=${NO_HARDEN}" + echo "END NO_HARDEN" + + echo "END ALL" +) | if [ -n "$DEBUG" ] && command -v tee > /dev/null 2>&1; then + # When debugging and `tee` is available, output the preimage to stderr + # in addition to passing through stdin to stdout + tee >(cat 1>&2) + else + # Otherwise, passthrough stdin to stdout + cat + fi | ${SHA256SUM} - | cut -d' ' -f1 diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index 259ff2864..a50e36110 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -1,10 +1,28 @@ -OSX_MIN_VERSION=10.14 -XCODE_VERSION=11.3.1 -XCODE_BUILD_ID=11C505 -LD64_VERSION=530 +OSX_MIN_VERSION=11.0 +OSX_SDK_VERSION=14.0 +XCODE_VERSION=15.0 +XCODE_BUILD_ID=15A240d +LLD_VERSION=711 OSX_SDK=$(SDK_PATH)/Xcode-$(XCODE_VERSION)-$(XCODE_BUILD_ID)-extracted-SDK-with-libcxx-headers +# We can't just use $(shell command -v clang) because GNU Make handles builtins +# in a special way and doesn't know that `command` is a POSIX-standard builtin +# prior to 1af314465e5dfe3e8baa839a32a72e83c04f26ef, first released in v4.2.90. +# At the time of writing, GNU Make v4.2.1 is still being used in supported +# distro releases. +# +# Source: https://lists.gnu.org/archive/html/bug-make/2017-11/msg00017.html +clang_prog=$(shell $(SHELL) $(.SHELLFLAGS) "command -v clang") +clangxx_prog=$(shell $(SHELL) $(.SHELLFLAGS) "command -v clang++") + +darwin_AR=$(shell $(SHELL) $(.SHELLFLAGS) "command -v llvm-ar") +darwin_DSYMUTIL=$(shell $(SHELL) $(.SHELLFLAGS) "command -v dsymutil") +darwin_NM=$(shell $(SHELL) $(.SHELLFLAGS) "command -v llvm-nm") +darwin_OBJDUMP=$(shell $(SHELL) $(.SHELLFLAGS) "command -v llvm-objdump") +darwin_RANLIB=$(shell $(SHELL) $(.SHELLFLAGS) "command -v llvm-ranlib") +darwin_STRIP=$(shell $(SHELL) $(.SHELLFLAGS) "command -v llvm-strip") + # Flag explanations: # # -mlinker-version @@ -12,32 +30,49 @@ OSX_SDK=$(SDK_PATH)/Xcode-$(XCODE_VERSION)-$(XCODE_BUILD_ID)-extracted-SDK-with- # Ensures that modern linker features are enabled. See here for more # details: https://github.com/bitcoin/bitcoin/pull/19407. # -# -B$(build_prefix)/bin +# -isysroot$(OSX_SDK) -nostdlibinc +# +# Disable default include paths built into the compiler as well as +# those normally included for libc and libc++. The only path that +# remains implicitly is the clang resource dir. # -# Explicitly point to our binaries (e.g. cctools) so that they are -# ensured to be found and preferred over other possibilities. +# -iwithsysroot / -iframeworkwithsysroot # -# -nostdinc++ -isystem $(OSX_SDK)/usr/include/c++/v1 +# Adds the desired paths from the SDK # -# Forces clang to use the libc++ headers from our SDK and completely -# forget about the libc++ headers from the standard directories +# -platform_version # -# TODO: Once we start requiring a clang version that has the -# -stdlib++-isystem flag first introduced here: -# https://reviews.llvm.org/D64089, we should use that instead. Read the -# differential summary there for more details. +# Indicate to the linker the platform, the oldest supported version, +# and the SDK used. # -darwin_CC=clang -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -B$(build_prefix)/bin -darwin_CXX=clang++ -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -stdlib=libc++ -mlinker-version=$(LD64_VERSION) -B$(build_prefix)/bin -nostdinc++ -isystem $(OSX_SDK)/usr/include/c++/v1 +# -no_adhoc_codesign +# +# Disable adhoc codesigning (for now) when using LLVM tooling, to avoid +# non-determinism issues with the Identifier field. + +darwin_CC=$(clang_prog) --target=$(host) \ + -isysroot$(OSX_SDK) -nostdlibinc \ + -iwithsysroot/usr/include -iframeworkwithsysroot/System/Library/Frameworks + +darwin_CXX=$(clangxx_prog) --target=$(host) \ + -isysroot$(OSX_SDK) -nostdlibinc \ + -iwithsysroot/usr/include/c++/v1 \ + -iwithsysroot/usr/include -iframeworkwithsysroot/System/Library/Frameworks + +darwin_CFLAGS=-pipe -std=$(C_STANDARD) -mmacos-version-min=$(OSX_MIN_VERSION) +darwin_CXXFLAGS=-pipe -std=$(CXX_STANDARD) -mmacos-version-min=$(OSX_MIN_VERSION) +darwin_LDFLAGS=-Wl,-platform_version,macos,$(OSX_MIN_VERSION),$(OSX_SDK_VERSION) -darwin_CFLAGS=-pipe -darwin_CXXFLAGS=$(darwin_CFLAGS) +ifneq ($(build_os),darwin) +darwin_CFLAGS += -mlinker-version=$(LLD_VERSION) +darwin_CXXFLAGS += -mlinker-version=$(LLD_VERSION) +darwin_LDFLAGS += -Wl,-no_adhoc_codesign -fuse-ld=lld +endif darwin_release_CFLAGS=-O2 darwin_release_CXXFLAGS=$(darwin_release_CFLAGS) -darwin_debug_CFLAGS=-O1 +darwin_debug_CFLAGS=-O1 -g darwin_debug_CXXFLAGS=$(darwin_debug_CFLAGS) -darwin_native_binutils=native_cctools -darwin_native_toolchain=native_cctools +darwin_cmake_system=Darwin diff --git a/depends/hosts/default.mk b/depends/hosts/default.mk index 6f60d6b3f..d82c33f29 100644 --- a/depends/hosts/default.mk +++ b/depends/hosts/default.mk @@ -1,26 +1,42 @@ +ifneq ($(host),$(build)) +host_toolchain:=$(host)- +endif + default_host_CC = $(host_toolchain)gcc default_host_CXX = $(host_toolchain)g++ default_host_AR = $(host_toolchain)ar default_host_RANLIB = $(host_toolchain)ranlib default_host_STRIP = $(host_toolchain)strip -default_host_LIBTOOL = $(host_toolchain)libtool -default_host_INSTALL_NAME_TOOL = $(host_toolchain)install_name_tool -default_host_OTOOL = $(host_toolchain)otool default_host_NM = $(host_toolchain)nm +default_host_OBJCOPY = $(host_toolchain)objcopy define add_host_tool_func +ifneq ($(filter $(origin $1),undefined default),) +# Do not consider the well-known var $1 if it is undefined or is taking a value +# that is predefined by "make" (e.g. the make variable "CC" has a predefined +# value of "cc") $(host_os)_$1?=$$(default_host_$1) $(host_arch)_$(host_os)_$1?=$$($(host_os)_$1) $(host_arch)_$(host_os)_$(release_type)_$1?=$$($(host_os)_$1) +else +$(host_os)_$1=$(or $($1),$($(host_os)_$1),$(default_host_$1)) +$(host_arch)_$(host_os)_$1=$(or $($1),$($(host_arch)_$(host_os)_$1),$$($(host_os)_$1)) +$(host_arch)_$(host_os)_$(release_type)_$1=$(or $($1),$($(host_arch)_$(host_os)_$(release_type)_$1),$$($(host_os)_$1)) +endif host_$1=$$($(host_arch)_$(host_os)_$1) endef define add_host_flags_func +ifeq ($(filter $(origin $1),undefined default),) +$(host_arch)_$(host_os)_$1 = +$(host_arch)_$(host_os)_$(release_type)_$1 = $($1) +else $(host_arch)_$(host_os)_$1 += $($(host_os)_$1) $(host_arch)_$(host_os)_$(release_type)_$1 += $($(host_os)_$(release_type)_$1) +endif host_$1 = $$($(host_arch)_$(host_os)_$1) host_$(release_type)_$1 = $$($(host_arch)_$(host_os)_$(release_type)_$1) endef -$(foreach tool,CC CXX AR RANLIB STRIP NM LIBTOOL OTOOL INSTALL_NAME_TOOL,$(eval $(call add_host_tool_func,$(tool)))) +$(foreach tool,CC CXX AR RANLIB STRIP NM OBJCOPY OBJDUMP DSYMUTIL,$(eval $(call add_host_tool_func,$(tool)))) $(foreach flags,CFLAGS CXXFLAGS CPPFLAGS LDFLAGS, $(eval $(call add_host_flags_func,$(flags)))) diff --git a/depends/hosts/freebsd.mk b/depends/hosts/freebsd.mk index 07436aef8..055097b03 100644 --- a/depends/hosts/freebsd.mk +++ b/depends/hosts/freebsd.mk @@ -1,24 +1,22 @@ -freebsd_CFLAGS=-pipe -freebsd_CXXFLAGS=$(freebsd_CFLAGS) +freebsd_CFLAGS=-pipe -std=$(C_STANDARD) +freebsd_CXXFLAGS=-pipe -std=$(CXX_STANDARD) -freebsd_release_CFLAGS=-O1 +freebsd_release_CFLAGS=-O2 freebsd_release_CXXFLAGS=$(freebsd_release_CFLAGS) freebsd_debug_CFLAGS=-O1 freebsd_debug_CXXFLAGS=$(freebsd_debug_CFLAGS) -freebsd_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC - ifeq (86,$(findstring 86,$(build_arch))) -i686_freebsd_CC=gcc -m32 -i686_freebsd_CXX=g++ -m32 +i686_freebsd_CC=clang -m32 +i686_freebsd_CXX=clang++ -m32 i686_freebsd_AR=ar i686_freebsd_RANLIB=ranlib i686_freebsd_NM=nm i686_freebsd_STRIP=strip -x86_64_freebsd_CC=gcc -m64 -x86_64_freebsd_CXX=g++ -m64 +x86_64_freebsd_CC=clang -m64 +x86_64_freebsd_CXX=clang++ -m64 x86_64_freebsd_AR=ar x86_64_freebsd_RANLIB=ranlib x86_64_freebsd_NM=nm @@ -29,3 +27,5 @@ i686_freebsd_CXX=$(default_host_CXX) -m32 x86_64_freebsd_CC=$(default_host_CC) -m64 x86_64_freebsd_CXX=$(default_host_CXX) -m64 endif + +freebsd_cmake_system=FreeBSD diff --git a/depends/hosts/linux.mk b/depends/hosts/linux.mk index 31748d662..f5ce2bb0b 100644 --- a/depends/hosts/linux.mk +++ b/depends/hosts/linux.mk @@ -1,14 +1,24 @@ -linux_CFLAGS=-pipe -linux_CXXFLAGS=$(linux_CFLAGS) +linux_CFLAGS=-pipe -std=$(C_STANDARD) +linux_CXXFLAGS=-pipe -std=$(CXX_STANDARD) -linux_release_CFLAGS=-O1 +ifneq ($(LTO),) +linux_AR = $(host_toolchain)gcc-ar +linux_NM = $(host_toolchain)gcc-nm +linux_RANLIB = $(host_toolchain)gcc-ranlib +endif + +linux_release_CFLAGS=-O2 linux_release_CXXFLAGS=$(linux_release_CFLAGS) -linux_debug_CFLAGS=-O1 +linux_debug_CFLAGS=-O1 -g linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) +# https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC +# https://libcxx.llvm.org/Hardening.html +linux_debug_CPPFLAGS+=-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG + ifeq (86,$(findstring 86,$(build_arch))) i686_linux_CC=gcc -m32 i686_linux_CXX=g++ -m32 @@ -29,3 +39,4 @@ i686_linux_CXX=$(default_host_CXX) -m32 x86_64_linux_CC=$(default_host_CC) -m64 x86_64_linux_CXX=$(default_host_CXX) -m64 endif +linux_cmake_system=Linux diff --git a/depends/hosts/mingw32.mk b/depends/hosts/mingw32.mk index 4f5f80503..4c657358f 100644 --- a/depends/hosts/mingw32.mk +++ b/depends/hosts/mingw32.mk @@ -1,13 +1,22 @@ -mingw32_CFLAGS=-pipe -mingw32_CXXFLAGS=$(mingw32_CFLAGS) +ifneq ($(shell $(SHELL) $(.SHELLFLAGS) "command -v $(host)-g++-posix"),) +mingw32_CXX := $(host)-g++-posix +endif -mingw32_LDFLAGS?=-fuse-ld=lld -mingw32_LDFLAGS+=-L/usr/lib/gcc/x86_64-w64-mingw32/$(shell x86_64-w64-mingw32-g++-posix -dumpversion) +mingw32_CFLAGS=-pipe -std=$(C_STANDARD) +mingw32_CXXFLAGS=-pipe -std=$(CXX_STANDARD) -mingw32_release_CFLAGS=-O1 +ifneq ($(LTO),) +mingw32_AR = $(host_toolchain)gcc-ar +mingw32_NM = $(host_toolchain)gcc-nm +mingw32_RANLIB = $(host_toolchain)gcc-ranlib +endif + +mingw32_release_CFLAGS=-O2 mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) -mingw32_debug_CFLAGS=-O1 +mingw32_debug_CFLAGS=-O1 -g mingw32_debug_CXXFLAGS=$(mingw32_debug_CFLAGS) mingw32_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC + +mingw32_cmake_system=Windows diff --git a/depends/packages.md b/depends/packages.md index 7d2bd4670..9afc63870 100644 --- a/depends/packages.md +++ b/depends/packages.md @@ -5,6 +5,10 @@ The package "mylib" will be used here as an example General tips: - mylib_foo is written as $(package)_foo in order to make recipes more similar. +- Secondary dependency packages relative to the bitcoinz binaries/libraries (i.e. + those not in `ALLOWED_LIBRARIES` in `contrib/devtools/symbol-check.py`) don't + need to be shared and should be built statically whenever possible. See + [below](#secondary-dependencies) for more details. ## Identifiers Each package is required to define at least these variables: @@ -28,15 +32,15 @@ These variables are optional: $(package)_build_subdir: cd to this dir before running configure/build/stage commands. - + $(package)_download_file: The file-name of the upstream source if it differs from how it should be stored locally. This can be used to avoid storing file-names with strange characters. - + $(package)_dependencies: Names of any other packages that this one depends on. - + $(package)_patches: Filenames of any patches needed to build the package @@ -70,7 +74,6 @@ These variables may be set to override or append their default values. $(package)_objcxx $(package)_ar $(package)_ranlib - $(package)_libtool $(package)_nm $(package)_cflags $(package)_cxxflags @@ -130,7 +133,7 @@ the user. Other variables may be defined as needed. Stage the build results. If undefined, does nothing. The following variables are available for each recipe: - + $(1)_staging_dir: package's destination sysroot path $(1)_staging_prefix_dir: prefix path inside of the package's staging dir $(1)_extract_dir: path to the package's extracted sources @@ -146,3 +149,52 @@ $($(package)_config_opts) will be appended. Most autotools projects can be properly staged using: $(MAKE) DESTDIR=$($(package)_staging_dir) install + +## Build outputs: + +In general, the output of a depends package should not contain any libtool +archives. Instead, the package should output `.pc` (`pkg-config`) files where +possible. + +From the [Gentoo Wiki entry](https://wiki.gentoo.org/wiki/Project:Quality_Assurance/Handling_Libtool_Archives): + +> Libtool pulls in all direct and indirect dependencies into the .la files it +> creates. This leads to massive overlinking, which is toxic to the Gentoo +> ecosystem, as it leads to a massive number of unnecessary rebuilds. + +Where possible, packages are built with Position Independent Code. Either using +the Autotools `--with-pic` flag, or `CMAKE_POSITION_INDEPENDENT_CODE` with CMake. + +## Secondary dependencies: + +Secondary dependency packages relative to the bitcoinz binaries/libraries (i.e. +those not in `ALLOWED_LIBRARIES` in `contrib/devtools/symbol-check.py`) don't +need to be shared and should be built statically whenever possible. This +improves general build reliability as illustrated by the following example: + +When linking an executable against a shared library `libprimary` that has its +own shared dependency `libsecondary`, we may need to specify the path to +`libsecondary` on the link command using the `-rpath/-rpath-link` options, it is +not sufficient to just say `libprimary`. + +For us, it's much easier to just link a static `libsecondary` into a shared +`libprimary`. Especially because in our case, we are linking against a dummy +`libprimary` anyway that we'll throw away. We don't care if the end-user has a +static or dynamic `libsecondary`, that's not our concern. With a static +`libsecondary`, when we need to link `libprimary` into our executable, there's no +dependency chain to worry about as `libprimary` has all the symbols. + +## Build targets: + +To build an individual package (useful for debugging), following build targets are available. + + make ${package} + make ${package}_fetched + make ${package}_extracted + make ${package}_preprocessed + make ${package}_configured + make ${package}_built + make ${package}_staged + make ${package}_postprocessed + make ${package}_cached + make ${package}_cached_checksum diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index fae0783cf..aec3c3b79 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -4,23 +4,26 @@ $(package)_download_path=https://download.oracle.com/berkeley-db $(package)_file_name=db-$($(package)_version).tar.gz $(package)_sha256_hash=47612c8991aa9ac2f6be721267c8d3cdccf5ac83105df8e50809daea24e95dc7 $(package)_build_subdir=build_unix +$(package)_patches=clang-12-stpcpy-issue.diff winioctl-and-atomic_init_db.patch define $(package)_set_vars -$(package)_config_opts=--disable-shared --enable-cxx --disable-replication +$(package)_config_opts=--disable-shared --enable-cxx --disable-replication --enable-option-checking $(package)_config_opts_mingw32=--enable-mingw $(package)_config_opts_linux=--with-pic $(package)_config_opts_freebsd=--with-pic -ifneq ($(build_os),darwin) +$(package)_config_opts_linux=--disable-atomicsupport $(package)_config_opts_darwin=--disable-atomicsupport -endif $(package)_config_opts_aarch64=--disable-atomicsupport $(package)_cxxflags=-std=c++17 +$(package)_cflags+=-Wno-error=implicit-function-declaration -Wno-error=format-security -Wno-error=implicit-int +$(package)_cppflags_freebsd=-D_XOPEN_SOURCE=600 -D__BSD_VISIBLE=1 +$(package)_cppflags_netbsd=-D_XOPEN_SOURCE=600 +$(package)_cppflags_mingw32=-DUNICODE -D_UNICODE endef define $(package)_preprocess_cmds - sed -i.old 's/WinIoCtl.h/winioctl.h/g' src/dbinc/win_db.h && \ - sed -i.old 's/__atomic_compare_exchange\\(/__atomic_compare_exchange_db(/' src/dbinc/atomic.h && \ - sed -i.old 's/atomic_init/atomic_init_db/' src/dbinc/atomic.h src/mp/mp_region.c src/mp/mp_mvcc.c src/mp/mp_fget.c src/mutex/mut_method.c src/mutex/mut_tas.c + patch -p1 <$($(package)_patch_dir)/clang-12-stpcpy-issue.diff && \ + patch -p1 <$($(package)_patch_dir)/winioctl-and-atomic_init_db.patch endef define $(package)_config_cmds diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index ead894626..0216c4a89 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,44 +1,48 @@ package=boost -$(package)_version=1_74_0 -$(package)_download_path=https://dl.bintray.com/boostorg/release/1.74.0/source -$(package)_file_name=$(package)_$($(package)_version).tar.bz2 -$(package)_sha256_hash=83bfc1507731a0906e387fc28b7ef5417d591429e51e788417fe9ff025e116b1 -$(package)_patches=signals2-noise.patch +$(package)_version=1.81.0 +$(package)_download_path=https://archives.boost.io/release/$($(package)_version)/source/ +$(package)_file_name=boost_$(subst .,_,$($(package)_version)).tar.gz +$(package)_sha256_hash=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6 +$(package)_dependencies=native_b2 define $(package)_set_vars $(package)_config_opts_release=variant=release $(package)_config_opts_debug=variant=debug -$(package)_config_opts=--layout=system -$(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1 -$(package)_config_opts_linux=threadapi=pthread runtime-link=shared -$(package)_config_opts_darwin=--toolset=darwin-4.2.1 runtime-link=shared -$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static -$(package)_config_opts_x86_64_mingw32=address-model=64 -$(package)_config_opts_i686_mingw32=address-model=32 -$(package)_config_opts_i686_linux=address-model=32 architecture=x86 +$(package)_config_opts=--layout=tagged --build-type=complete --user-config=user-config.jam +$(package)_config_opts+=threading=multi link=static -sNO_COMPRESSION=1 +$(package)_config_opts_linux=target-os=linux threadapi=pthread runtime-link=shared +$(package)_config_opts_darwin=target-os=darwin runtime-link=shared +$(package)_config_opts_mingw32=target-os=windows binary-format=pe threadapi=win32 runtime-link=static +$(package)_config_opts_x86_64=architecture=x86 address-model=64 +$(package)_config_opts_i686=architecture=x86 address-model=32 +$(package)_config_opts_aarch64=address-model=64 +$(package)_config_opts_armv7a=address-model=32 +ifneq (,$(findstring clang,$($(package)_cxx))) +$(package)_toolset_$(host_os)=clang +else $(package)_toolset_$(host_os)=gcc -$(package)_archiver_$(host_os)=$($(package)_ar) -$(package)_toolset_darwin=darwin -$(package)_archiver_darwin=$($(package)_libtool) +endif $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test -$(package)_cxxflags=-std=c++17 -fvisibility=hidden +$(package)_cxxflags+=-std=c++17 $(package)_cxxflags_linux=-fPIC $(package)_cxxflags_freebsd=-fPIC +$(package)_cxxflags_openbsd=-fPIC +$(package)_cxxflags_android=-fPIC +$(package)_cxxflags_x86_64=-fcf-protection=full endef define $(package)_preprocess_cmds - patch -p2 < $($(package)_patch_dir)/signals2-noise.patch + echo "using $($(package)_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cflags)\" \"$($(package)_cxxflags)\" \"$($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$($(package)_ar)\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam endef define $(package)_config_cmds - ./bootstrap.sh --without-icu --with-libraries=$($(package)_config_libraries) && \ - sed -i -e "s|using gcc ;|using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$(boost_archiver_$(host_os))\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;|" project-config.jam + ./bootstrap.sh --without-icu --with-libraries=$($(package)_config_libraries) --with-toolset=$($(package)_toolset_$(host_os)) --with-bjam=b2 endef define $(package)_build_cmds - ./b2 -d2 -j2 -d1 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) stage + b2 -d2 -j2 -d1 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) toolset=$($(package)_toolset_$(host_os)) stage endef define $(package)_stage_cmds - ./b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) install + b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) toolset=$($(package)_toolset_$(host_os)) --no-cmake-config install endef diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index 7119176f2..c761310ae 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -1,26 +1,32 @@ -package=libevent -$(package)_version=2.1.8 -$(package)_download_path=https://github.com/libevent/libevent/archive/ +$(package)_version=2.1.12-stable +$(package)_download_path=https://github.com/libevent/libevent/releases/download/release-$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_download_file=release-$($(package)_version)-stable.tar.gz -$(package)_sha256_hash=316ddb401745ac5d222d7c529ef1eada12f58f6376a66c1118eee803cb70f83d -$(package)_patches=detect-arch4random_addrandom.patch detect-arch4random_addrandom-fix.patch +$(package)_sha256_hash=92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb +$(package)_patches=cmake_fixups.patch +$(package)_patches+=fix_mingw_link.patch +$(package)_build_subdir=build -define $(package)_preprocess_cmds - patch -p1 <$($(package)_patch_dir)/detect-arch4random_addrandom.patch && \ - patch -p1 <$($(package)_patch_dir)/detect-arch4random_addrandom-fix.patch && \ - ./autogen.sh +# When building for Windows, we set _WIN32_WINNT to target the same Windows +# version as we do in configure. Due to quirks in libevents build system, this +# is also required to enable support for ipv6. See #19375. +define $(package)_set_vars + $(package)_config_opts=-DEVENT__DISABLE_BENCHMARK=ON -DEVENT__DISABLE_OPENSSL=ON + $(package)_config_opts+=-DEVENT__DISABLE_SAMPLES=ON -DEVENT__DISABLE_REGRESS=ON + $(package)_config_opts+=-DEVENT__DISABLE_TESTS=ON -DEVENT__LIBRARY_TYPE=STATIC + $(package)_cppflags_mingw32=-D_WIN32_WINNT=0x0601 + + ifeq ($(NO_HARDEN),) + $(package)_cppflags+=-D_FORTIFY_SOURCE=3 + endif endef -define $(package)_set_vars - $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress - $(package)_config_opts_release=--disable-debug-mode - $(package)_config_opts_linux=--with-pic - $(package)_config_opts_freebsd=--with-pic +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/cmake_fixups.patch && \ + patch -p1 < $($(package)_patch_dir)/fix_mingw_link.patch endef define $(package)_config_cmds - $($(package)_autoconf) + $($(package)_cmake) -S .. -B . endef define $(package)_build_cmds @@ -32,4 +38,7 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds + rm -rf bin && \ + rm include/ev*.h && \ + rm include/event2/*_compat.h endef diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk index 8dee7b9a6..a818746f7 100644 --- a/depends/packages/librustzcash.mk +++ b/depends/packages/librustzcash.mk @@ -6,15 +6,37 @@ $(package)_download_file=$($(package)_git_commit).tar.gz $(package)_sha256_hash=9909ec59fa7a411c2071d6237b3363a0bc6e5e42358505cf64b7da0f58a7ff5a $(package)_git_commit=06da3b9ac8f278e5d4ae13088cf0a4c03d2c13f5 $(package)_dependencies=rust -ifeq ($(LIBRUSTZCASH_OVERRIDE),) $(package)_dependencies+=$(rust_crates) -$(package)_patches=cargo.config 0001-Start-using-cargo-clippy-for-CI.patch remove-dev-dependencies.diff -endif +$(package)_patches=cargo.config guix.config 0001-Start-using-cargo-clippy-for-CI.patch remove-dev-dependencies.diff + +# Mapping from GCC canonical hosts to Rust targets +$(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu +$(package)_rust_target_aarch64-unknown-linux-gnu=aarch64-unknown-linux-gnu +$(package)_rust_target_x86_64-apple-darwin=x86_64-apple-darwin +$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu + +ifneq ($(WITH_GUIX),) +$(package)_rust_target=$($(package)_rust_target_$(canonical_host)) +else + +# Mapping from GCC canonical hosts to Rust targets +# If a mapping is not present, we assume they are identical, unless $host_os is +# "darwin", in which case we assume x86_64-apple-darwin. +$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu +define rust_target +$(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(2))) +endef + +ifneq ($(canonical_host),$(build)) +$(package)_rust_target=$(call rust_target,$(package),$(canonical_host),$(host_os)) +else $(package)_rust_target=$(if $(rust_rust_target_$(canonical_host)),$(rust_rust_target_$(canonical_host)),$(canonical_host)) +endif +endif -ifeq ($(host_os),mingw32) -$(package)_library_file=target/x86_64-pc-windows-gnu/release/rustzcash.lib +ifneq ($(WITH_GUIX),) +$(package)_library_file=target/$($(package)_rust_target)/release/librustzcash.a else ifneq ($(canonical_host),$(build)) $(package)_library_file=target/$($(package)_rust_target)/release/librustzcash.a else @@ -23,35 +45,19 @@ endif define $(package)_set_vars $(package)_build_opts=--release -ifeq ($(LIBRUSTZCASH_OVERRIDE),) $(package)_build_opts+=--frozen -endif -ifneq ($(canonical_host),$(build)) +ifneq ($(WITH_GUIX),) +$(package)_build_opts+=--target=$($(package)_rust_target) +else ifneq ($(canonical_host),$(build)) $(package)_build_opts+=--target=$($(package)_rust_target) endif endef -ifneq ($(LIBRUSTZCASH_OVERRIDE),) - -define $(package)_fetch_cmds +ifneq ($(WITH_GUIX),) +define $(package)_config_cmds + cat $($(package)_patch_dir)/guix.config >> .cargo/config endef - -define $(package)_extract_cmds -endef - -define $(package)_build_cmds - cd $(LIBRUSTZCASH_OVERRIDE) && \ - $(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts) -endef - -define $(package)_stage_cmds - mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \ - mkdir $($(package)_staging_dir)$(host_prefix)/include/ && \ - cp $(LIBRUSTZCASH_OVERRIDE)/$($(package)_library_file) $($(package)_staging_dir)$(host_prefix)/lib/ && \ - cp $(LIBRUSTZCASH_OVERRIDE)/librustzcash/include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/ -endef - -else +endif define $(package)_preprocess_cmds patch -p1 -d pairing < $($(package)_patch_dir)/0001-Start-using-cargo-clippy-for-CI.patch && \ @@ -61,6 +67,8 @@ define $(package)_preprocess_cmds endef define $(package)_build_cmds + echo "DEBUG: canonical_host=$(canonical_host) host_os=$(host_os) build=$(build) build_os=$(build_os) CC=$(CC) CXX=$(CXX)" && \ + echo "$(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts)" && \ $(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts) endef @@ -70,5 +78,3 @@ define $(package)_stage_cmds cp $($(package)_library_file) $($(package)_staging_dir)$(host_prefix)/lib/ && \ cp librustzcash/include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/ endef - -endif diff --git a/depends/packages/native_b2.mk b/depends/packages/native_b2.mk new file mode 100644 index 000000000..aaa37cdcf --- /dev/null +++ b/depends/packages/native_b2.mk @@ -0,0 +1,20 @@ +package=native_b2 +$(package)_version=$(boost_version) +$(package)_download_path=$(boost_download_path) +$(package)_file_name=$(boost_file_name) +$(package)_sha256_hash=$(boost_sha256_hash) +$(package)_build_subdir=tools/build/src/engine +ifneq (,$(findstring clang,$($(package)_cxx))) +$(package)_toolset_$(host_os)=clang +else +$(package)_toolset_$(host_os)=gcc +endif + +define $(package)_build_cmds + CXX="$($(package)_cxx)" CXXFLAGS="$($(package)_cxxflags)" ./build.sh "$($(package)_toolset_$(host_os))" +endef + +define $(package)_stage_cmds + mkdir -p "$($(package)_staging_prefix_dir)"/bin/ && \ + cp b2 "$($(package)_staging_prefix_dir)"/bin/ +endef diff --git a/depends/packages/native_cctools.mk b/depends/packages/native_cctools.mk deleted file mode 100644 index a065256c1..000000000 --- a/depends/packages/native_cctools.mk +++ /dev/null @@ -1,65 +0,0 @@ -package=native_cctools -$(package)_version=807d6fd1be5d2224872e381870c0a75387fe05e6 -$(package)_download_path=https://github.com/theuni/cctools-port/archive -$(package)_file_name=$($(package)_version).tar.gz -$(package)_sha256_hash=a09c9ba4684670a0375e42d9d67e7f12c1f62581a27f28f7c825d6d7032ccc6a -$(package)_build_subdir=cctools -$(package)_clang_version=3.7.1 -$(package)_clang_download_path=https://llvm.org/releases/$($(package)_clang_version) -$(package)_clang_download_file=clang+llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz -$(package)_clang_file_name=clang-llvm-$($(package)_clang_version)-x86_64-linux-gnu-ubuntu-14.04.tar.xz -$(package)_clang_sha256_hash=99b28a6b48e793705228a390471991386daa33a9717cd9ca007fcdde69608fd9 -$(package)_extra_sources=$($(package)_clang_file_name) - -define $(package)_fetch_cmds -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ -$(call fetch_file,$(package),$($(package)_clang_download_path),$($(package)_clang_download_file),$($(package)_clang_file_name),$($(package)_clang_sha256_hash)) -endef - -define $(package)_extract_cmds - mkdir -p $($(package)_extract_dir) && \ - echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - echo "$($(package)_clang_sha256_hash) $($(package)_source_dir)/$($(package)_clang_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - mkdir -p toolchain/bin toolchain/lib/clang/3.5/include && \ - tar --no-same-owner --strip-components=1 -C toolchain -xf $($(package)_source_dir)/$($(package)_clang_file_name) && \ - rm -f toolchain/lib/libc++abi.so* && \ - echo "#!/bin/sh" > toolchain/bin/$(host)-dsymutil && \ - echo "exit 0" >> toolchain/bin/$(host)-dsymutil && \ - chmod +x toolchain/bin/$(host)-dsymutil && \ - tar --no-same-owner --strip-components=1 -xf $($(package)_source) -endef - -define $(package)_set_vars -$(package)_config_opts=--target=$(host) --disable-lto-support -$(package)_ldflags+=-Wl,-rpath=\\$$$$$$$$\$$$$$$$$ORIGIN/../lib -$(package)_cc=$($(package)_extract_dir)/toolchain/bin/clang -$(package)_cxx=$($(package)_extract_dir)/toolchain/bin/clang++ -endef - -define $(package)_preprocess_cmds - cd $($(package)_build_subdir); ./autogen.sh && \ - sed -i.old "/define HAVE_PTHREADS/d" ld64/src/ld/InputFiles.h -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install && \ - cd $($(package)_extract_dir)/toolchain && \ - mkdir -p $($(package)_staging_prefix_dir)/lib/clang/$($(package)_clang_version)/include && \ - mkdir -p $($(package)_staging_prefix_dir)/bin $($(package)_staging_prefix_dir)/include && \ - cp bin/clang $($(package)_staging_prefix_dir)/bin/ &&\ - cp -P bin/clang++ $($(package)_staging_prefix_dir)/bin/ &&\ - cp lib/libLTO.so $($(package)_staging_prefix_dir)/lib/ && \ - cp -rf lib/clang/$($(package)_clang_version)/include/* $($(package)_staging_prefix_dir)/lib/clang/$($(package)_clang_version)/include/ && \ - cp bin/llvm-dsymutil $($(package)_staging_prefix_dir)/bin/$(host)-dsymutil && \ - if `test -d include/c++/`; then cp -rf include/c++/ $($(package)_staging_prefix_dir)/include/; fi && \ - if `test -d lib/c++/`; then cp -rf lib/c++/ $($(package)_staging_prefix_dir)/lib/; fi -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 1fbc8d39d..03e363256 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -32,12 +32,14 @@ rust_crates := \ crate_winapi_i686_pc_windows_gnu \ crate_winapi \ crate_winapi_x86_64_pc_windows_gnu + rust_packages := rust $(rust_crates) librustzcash zcash_packages := libsodium utfcpp -packages := boost libevent zeromq $(zcash_packages) googletest - +boost_packages := boost +libevent_packages := libevent +zmq_packages := zeromq wallet_packages=bdb -ifneq ($(build_os),darwin) -darwin_native_packages=native_cctools -endif +$(host_arch)_$(host_os)_native_packages += native_b2 + +packages := googletest diff --git a/depends/packages/rust.mk b/depends/packages/rust.mk index 0eb51eef7..6d40ecba9 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -1,53 +1,79 @@ package=rust -$(package)_version=1.32.0 +$(package)_version=1.80.0 $(package)_download_path=https://static.rust-lang.org/dist $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_linux=e024698320d76b74daf0e6e71be3681a1e7923122e3ebd03673fcac3ecc23810 +$(package)_sha256_hash_linux=702d3b60816eb9410c84947895c0188e20f588cb91b3aee8d84cec23ddb63582 $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz -$(package)_sha256_hash_darwin=f0dfba507192f9b5c330b5984ba71d57d434475f3d62bd44a39201e36fa76304 +$(package)_sha256_hash_darwin=18352bc5802e117189c029b6ed63bf83bfbcde7141f33eee69f037acd1767676 $(package)_file_name_freebsd=rust-$($(package)_version)-x86_64-unknown-freebsd.tar.gz -$(package)_sha256_hash_freebsd=20d062493d01f1816014fe9dbe883bda06f1828a6ddbfb7ee5e4f1df20eb1c3a +$(package)_sha256_hash_freebsd=d9eb6a65ee2b51ce982999c093a9f1f2e37f65c3a3d47dff2e0e74b7e01f534c +$(package)_file_name_aarch64_linux=rust-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz +$(package)_sha256_hash_aarch64_linux=0aa0f0327c8054f3805cc4d83be2c17d1e846d23292aa872ecd6f78d86f84ce5 -# Mapping from GCC canonical hosts to Rust targets -# If a mapping is not present, we assume they are identical -$(package)_rust_target_x86_64-apple-darwin11=x86_64-apple-darwin -$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu +# Rust-std x86_64-unknown-linux-gnu +$(package)_rust-std-x86_64-pc-linux-gnu_file_name=rust-std-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz +$(package)_rust-std-x86_64-pc-linux-gnu_sha256_hash=ed301dff3a26da496784ca3de523b0150302fcb001ef71cdcd40ff6d5e2ec75d -# Mapping from Rust targets to SHA-256 hashes -$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=346efe3aef2aff7b71a611bf7661bcec5f9bc4025a599c2866ec5fd330247cb9 -$(package)_rust_std_sha256_hash_x86_64-apple-darwin=7c6806809e010e5fba1780007ecff5c31f0ad2fcac1b414b98ca3baa0fb41b36 -$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=cad5f1454d591c13eeb3657f1c9dbfeb30e648f59680bd0765b94c63e7afc49e +# Rust-std x86_64-apple-darwin +$(package)_rust-std-x86_64-apple-darwin_file_name=rust-std-$($(package)_version)-x86_64-apple-darwin.tar.gz +$(package)_rust-std-x86_64-apple-darwin_sha256_hash=069dcd20861c1031a2e1484ef4085503b1e239fdca6b7c6dd4d834c9cc8aff70 -ifneq ($(canonical_host),$(build)) -$(package)_rust_target=$(if $($(package)_rust_target_$(canonical_host)),$($(package)_rust_target_$(canonical_host)),$(canonical_host)) -$(package)_exact_file_name=rust-std-$($(package)_version)-$($(package)_rust_target).tar.gz -$(package)_exact_sha256_hash=$($(package)_rust_std_sha256_hash_$($(package)_rust_target)) -$(package)_build_subdir=buildos -$(package)_extra_sources=$($(package)_file_name_$(build_os)) +# Rust-std aarch64-unknown-linux-gnu +$(package)_rust-std-aarch64-unknown-linux-gnu_file_name=rust-std-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz +$(package)_rust-std-aarch64-unknown-linux-gnu_sha256_hash=6cdbe8c2b502ca90f42c581b8906b725ccc55bbb3427a332379236bf22be59b3 + +# Rust-std x86_64-pc-windows-gnu +$(package)_rust-std-x86_64-w64-mingw32_file_name=rust-std-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz +$(package)_rust-std-x86_64-w64-mingw32_sha256_hash=0d6a58268bd94d66280812c042c41521f65acd773e3aae3219eb8a088654be72 + +$(package)_extra_sources = $($(package)_rust-std-x86_64-pc-linux-gnu_file_name) +$(package)_extra_sources += $($(package)_rust-std-x86_64-apple-darwin_file_name) +$(package)_extra_sources += $($(package)_rust-std-aarch64-unknown-linux-gnu_file_name) +$(package)_extra_sources += $($(package)_rust-std-x86_64-w64-mingw32_file_name) + +define $(package)_set_vars +$(package)_stage_opts=--disable-ldconfig +$(package)_stage_build_opts=--without=rust-docs-json-preview,rust-docs +endef define $(package)_fetch_cmds -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_download_file),$($(package)_file_name),$($(package)_sha256_hash)) && \ -$(call fetch_file,$(package),$($(package)_download_path),$($(package)_file_name_$(build_os)),$($(package)_file_name_$(build_os)),$($(package)_sha256_hash_$(build_os))) +echo "DEBUG: canonical_host=$(canonical_host) host_os=$(host_os) build=$(build) build_os=$(build_os) CC=$(CC) CXX=$(CXX)" && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_file_name_$(build_os)),$($(package)_file_name_$(build_os)),$($(package)_sha256_hash_$(build_os))) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-x86_64-pc-linux-gnu_file_name),$($(package)_rust-std-x86_64-pc-linux-gnu_file_name),$($(package)_rust-std-x86_64-pc-linux-gnu_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-x86_64-apple-darwin_file_name),$($(package)_rust-std-x86_64-apple-darwin_file_name),$($(package)_rust-std-x86_64-apple-darwin_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-aarch64-unknown-linux-gnu_file_name),$($(package)_rust-std-aarch64-unknown-linux-gnu_file_name),$($(package)_rust-std-aarch64-unknown-linux-gnu_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-x86_64-w64-mingw32_file_name),$($(package)_rust-std-x86_64-w64-mingw32_file_name),$($(package)_rust-std-x86_64-w64-mingw32_sha256_hash)) endef define $(package)_extract_cmds + echo "DEBUG: canonical_host=$(canonical_host) host_os=$(host_os) build=$(build) build_os=$(build_os) CC=$(CC) CXX=$(CXX)" && \ mkdir -p $($(package)_extract_dir) && \ - echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - echo "$($(package)_sha256_hash_$(build_os)) $($(package)_source_dir)/$($(package)_file_name_$(build_os))" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_sha256_hash) $($(package)_source_dir)/$($(package)_file_name_$(build_os))" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \ + echo "$($(package)_rust-std-$(canonical_host)_sha256_hash) $($(package)_source_dir)/$($(package)_rust-std-$(canonical_host)_file_name)" >> $($(package)_extract_dir)/.$($(package)_file_name).hash && \ $(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \ - mkdir $(canonical_host) && \ - tar --strip-components=1 -xf $($(package)_source) -C $(canonical_host) && \ - mkdir buildos && \ - tar --strip-components=1 -xf $($(package)_source_dir)/$($(package)_file_name_$(build_os)) -C buildos + mkdir rust && \ + $(build_TAR) --no-same-owner --strip-components=1 -xf $($(package)_source_dir)/$($(package)_file_name_$(build_os)) -C rust && \ + mkdir rust-std && \ + $(build_TAR) --no-same-owner --strip-components=1 -xf $($(package)_source_dir)/$($(package)_rust-std-$(canonical_host)_file_name) -C rust-std endef define $(package)_stage_cmds - bash ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig && \ - ../$(canonical_host)/install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig + echo "DEBUG: canonical_host=$(canonical_host) host_os=$(host_os) build=$(build) build_os=$(build_os) CC=$(CC) CXX=$(CXX)" && \ + bash ./rust/install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) $($(package)_stage_opts) $($(package)_stage_build_opts) && \ + bash ./rust-std/install.sh --destdir=$($(package)_staging_dir) --prefix=$(build_prefix) $($(package)_stage_opts) endef -else -define $(package)_stage_cmds - bash ./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig +ifneq ($(WITH_GUIX),) +define $(package)_postprocess_cmds + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/cargo" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/cargo-clippy" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/cargo-fmt" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/clippy-driver" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/rls" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/rust-analyzer" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/rust-demangler" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/rustc" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/rustdoc" && \ + $(BASEDIR)/fix-elf-interpreter.sh "$($(package)_staging_dir)$(host_prefix)/native/bin/rustfmt" endef endif diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index af4243162..30c04cbfa 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -1,28 +1,50 @@ package=zeromq -$(package)_version=4.3.1 +$(package)_version=4.3.5 $(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=bcbabe1e2c7d0eec4ed612e10b94b112dd5f06fcefa994a0c79a45d835cd21eb +$(package)_sha256_hash=6653ef5910f17954861fe72332e68b03ca6e4d9c7160eb3a8de5a5a913bfab43 +$(package)_build_subdir=build +$(package)_patches = remove_libstd_link.patch +$(package)_patches += macos_mktemp_check.patch +$(package)_patches += builtin_sha1.patch +$(package)_patches += fix_have_windows.patch +$(package)_patches += openbsd_kqueue_headers.patch +$(package)_patches += cmake_minimum.patch +$(package)_patches += cacheline_undefined.patch +$(package)_patches += no_librt.patch define $(package)_set_vars - $(package)_config_opts=--without-documentation --disable-shared --disable-curve - $(package)_config_opts_linux=--with-pic - $(package)_config_opts_freebsd=--with-pic - $(package)_cxxflags=-std=c++17 + $(package)_config_opts := -DCMAKE_BUILD_TYPE=None -DWITH_DOCS=OFF -DWITH_LIBSODIUM=OFF + $(package)_config_opts += -DWITH_LIBBSD=OFF -DENABLE_CURVE=OFF -DENABLE_CPACK=OFF + $(package)_config_opts += -DBUILD_SHARED=OFF -DBUILD_TESTS=OFF -DZMQ_BUILD_TESTS=OFF + $(package)_config_opts += -DENABLE_DRAFTS=OFF -DZMQ_BUILD_TESTS=OFF + $(package)_cxxflags += -ffile-prefix-map=$($(package)_extract_dir)=/usr + $(package)_config_opts_mingw32 += -DZMQ_WIN32_WINNT=0x0601 -DZMQ_HAVE_IPC=OFF +endef + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/remove_libstd_link.patch && \ + patch -p1 < $($(package)_patch_dir)/macos_mktemp_check.patch && \ + patch -p1 < $($(package)_patch_dir)/builtin_sha1.patch && \ + patch -p1 < $($(package)_patch_dir)/cacheline_undefined.patch && \ + patch -p1 < $($(package)_patch_dir)/fix_have_windows.patch && \ + patch -p1 < $($(package)_patch_dir)/openbsd_kqueue_headers.patch && \ + patch -p1 < $($(package)_patch_dir)/cmake_minimum.patch && \ + patch -p1 < $($(package)_patch_dir)/no_librt.patch endef define $(package)_config_cmds - $($(package)_autoconf) + $($(package)_cmake) -S .. -B . endef define $(package)_build_cmds - $(MAKE) src/libzmq.la + $(MAKE) endef define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-includeHEADERS install-pkgconfigDATA + $(MAKE) DESTDIR=$($(package)_staging_dir) install endef define $(package)_postprocess_cmds - rm -rf bin share + rm -rf share endef diff --git a/depends/patches/bdb/clang-12-stpcpy-issue.diff b/depends/patches/bdb/clang-12-stpcpy-issue.diff new file mode 100644 index 000000000..671938e0b --- /dev/null +++ b/depends/patches/bdb/clang-12-stpcpy-issue.diff @@ -0,0 +1,17 @@ +diff -ur db-6.2.23-orig/src/blob/blob_util.c db-6.2.23/src/blob/blob_util.c +--- db-6.2.23-orig/src/blob/blob_util.c 2016-03-28 20:45:53.000000000 +0100 ++++ db-6.2.23/src/blob/blob_util.c 2021-07-30 19:52:37.082811600 +0100 +@@ -544,7 +544,12 @@ + goto err; + + memset(path, 0, len); +- name_len += sprintf(path, "%s", blob_sub_dir); ++ // Clang 12 introduced an "libcall optimization" that lowers the above ++ // to stpcpy to avoid the machinery involved in parsing format strings. ++ // This causes build problems when cross-compiling to Windows with the ++ // version of mingw-w64 that Zcash supports. We haven't figured out why ++ // but in the meantime using "%s%s" inhibits the optimization. ++ name_len += sprintf(path, "%s%s", blob_sub_dir, ""); + + __blob_calculate_dirs(blob_id, path, &name_len, &depth); + diff --git a/depends/patches/bdb/winioctl-and-atomic_init_db.patch b/depends/patches/bdb/winioctl-and-atomic_init_db.patch new file mode 100644 index 000000000..788da5be4 --- /dev/null +++ b/depends/patches/bdb/winioctl-and-atomic_init_db.patch @@ -0,0 +1,136 @@ +diff --git a/src/dbinc/atomic.h b/src/dbinc/atomic.h +index e4420aa..4799b5f 100644 +--- a/src/dbinc/atomic.h ++++ b/src/dbinc/atomic.h +@@ -70,7 +70,7 @@ typedef struct { + * These have no memory barriers; the caller must include them when necessary. + */ + #define atomic_read(p) ((p)->value) +-#define atomic_init(p, val) ((p)->value = (val)) ++#define atomic_init_db(p, val) ((p)->value = (val)) + + #ifdef HAVE_ATOMIC_SUPPORT + +@@ -225,7 +225,7 @@ static inline int __atomic_compare_exchange_int( + #define atomic_dec(env, p) (--(p)->value) + #define atomic_compare_exchange(env, p, oldval, newval) \ + (DB_ASSERT(env, atomic_read(p) == (oldval)), \ +- atomic_init(p, (newval)), 1) ++ atomic_init_db(p, (newval)), 1) + #else + #define atomic_inc(env, p) __atomic_inc_int(env, p) + #define atomic_dec(env, p) __atomic_dec_int(env, p) +diff --git a/src/dbinc/win_db.h b/src/dbinc/win_db.h +index f5228f4..6e44659 100644 +--- a/src/dbinc/win_db.h ++++ b/src/dbinc/win_db.h +@@ -51,7 +51,7 @@ + #include + #include + #ifndef DB_WINCE +-#include ++#include + #endif + + #ifdef HAVE_GETADDRINFO +diff --git a/src/mp/mp_fget.c b/src/mp/mp_fget.c +index 59fe9fe..fa4ced7 100644 +--- a/src/mp/mp_fget.c ++++ b/src/mp/mp_fget.c +@@ -654,7 +654,7 @@ alloc: /* Allocate a new buffer header and data space. */ + + /* Initialize enough so we can call __memp_bhfree. */ + alloc_bhp->flags = 0; +- atomic_init(&alloc_bhp->ref, 1); ++ atomic_init_db(&alloc_bhp->ref, 1); + #ifdef DIAGNOSTIC + if ((uintptr_t)alloc_bhp->buf & (sizeof(size_t) - 1)) { + __db_errx(env, DB_STR("3025", +@@ -969,7 +969,7 @@ alloc: /* Allocate a new buffer header and data space. */ + MVCC_MPROTECT(bhp->buf, mfp->pagesize, + PROT_READ); + +- atomic_init(&alloc_bhp->ref, 1); ++ atomic_init_db(&alloc_bhp->ref, 1); + MUTEX_LOCK(env, alloc_bhp->mtx_buf); + alloc_bhp->priority = bhp->priority; + alloc_bhp->pgno = bhp->pgno; +diff --git a/src/mp/mp_mvcc.c b/src/mp/mp_mvcc.c +index 83c4d72..0a47202 100644 +--- a/src/mp/mp_mvcc.c ++++ b/src/mp/mp_mvcc.c +@@ -281,7 +281,7 @@ __memp_bh_freeze(dbmp, infop, hp, bhp, need_frozenp) + #else + memcpy(frozen_bhp, bhp, SSZA(BH, buf)); + #endif +- atomic_init(&frozen_bhp->ref, 0); ++ atomic_init_db(&frozen_bhp->ref, 0); + if (mutex != MUTEX_INVALID) + frozen_bhp->mtx_buf = mutex; + else if ((ret = __mutex_alloc(env, MTX_MPOOL_BH, +@@ -440,7 +440,7 @@ __memp_bh_thaw(dbmp, infop, hp, frozen_bhp, alloc_bhp) + #endif + alloc_bhp->mtx_buf = mutex; + MUTEX_LOCK(env, alloc_bhp->mtx_buf); +- atomic_init(&alloc_bhp->ref, 1); ++ atomic_init_db(&alloc_bhp->ref, 1); + F_CLR(alloc_bhp, BH_FROZEN); + } + +diff --git a/src/mp/mp_region.c b/src/mp/mp_region.c +index 4d95e4f..e97459c 100644 +--- a/src/mp/mp_region.c ++++ b/src/mp/mp_region.c +@@ -278,7 +278,7 @@ __memp_init(env, dbmp, reginfo_off, htab_buckets, max_nreg) + MTX_MPOOL_FILE_BUCKET, 0, &htab[i].mtx_hash)) != 0) + return (ret); + SH_TAILQ_INIT(&htab[i].hash_bucket); +- atomic_init(&htab[i].hash_page_dirty, 0); ++ atomic_init_db(&htab[i].hash_page_dirty, 0); + } + + mtx_base = mtx_prev = MUTEX_INVALID; +@@ -332,7 +332,7 @@ no_prealloc: + DB_MUTEX_SHARED, &hp->mtx_hash)) != 0) + return (ret); + SH_TAILQ_INIT(&hp->hash_bucket); +- atomic_init(&hp->hash_page_dirty, 0); ++ atomic_init_db(&hp->hash_page_dirty, 0); + #ifdef HAVE_STATISTICS + hp->hash_io_wait = 0; + hp->hash_frozen = hp->hash_thawed = hp->hash_frozen_freed = 0; +diff --git a/src/mutex/mut_method.c b/src/mutex/mut_method.c +index 72b34de..a9f9868 100644 +--- a/src/mutex/mut_method.c ++++ b/src/mutex/mut_method.c +@@ -501,7 +501,7 @@ __atomic_compare_exchange_int(env, v, oldval, newval) + MUTEX_LOCK(env, mtx); + ret = atomic_read(v) == oldval; + if (ret) +- atomic_init(v, newval); ++ atomic_init_db(v, newval); + MUTEX_UNLOCK(env, mtx); + + return (ret); +diff --git a/src/mutex/mut_tas.c b/src/mutex/mut_tas.c +index 7899c4b..d9420fa 100644 +--- a/src/mutex/mut_tas.c ++++ b/src/mutex/mut_tas.c +@@ -47,7 +47,7 @@ __db_tas_mutex_init(env, mutex, flags) + + #ifdef HAVE_SHARED_LATCHES + if (F_ISSET(mutexp, DB_MUTEX_SHARED)) +- atomic_init(&mutexp->sharecount, 0); ++ atomic_init_db(&mutexp->sharecount, 0); + else + #endif + if (MUTEX_INIT(&mutexp->tas)) { +@@ -643,7 +643,7 @@ was_not_locked: + F_CLR(mutexp, DB_MUTEX_LOCKED); + /* Flush flag update before zeroing count */ + MEMBAR_EXIT(); +- atomic_init(&mutexp->sharecount, 0); ++ atomic_init_db(&mutexp->sharecount, 0); + } else { + DB_ASSERT(env, sharecount > 0); + MEMBAR_EXIT(); diff --git a/depends/patches/boost/signals2-noise.patch b/depends/patches/boost/signals2-noise.patch deleted file mode 100644 index 51d71516e..000000000 --- a/depends/patches/boost/signals2-noise.patch +++ /dev/null @@ -1,23 +0,0 @@ -From fd27423fea5537bc857c1fa14bb0c25b994f77b3 Mon Sep 17 00:00:00 2001 -From: Frank Mori Hess -Date: Mon, 20 Jul 2020 14:17:05 -0400 -Subject: [PATCH] Fix warning about deprecated - boost/function_output_iterator.hpp - ---- - include/boost/signals2/detail/null_output_iterator.hpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/boost/signals2/detail/null_output_iterator.hpp b/include/boost/signals2/detail/null_output_iterator.hpp -index 9e986959..dee4373c 100644 ---- a/include/boost/signals2/detail/null_output_iterator.hpp -+++ b/include/boost/signals2/detail/null_output_iterator.hpp -@@ -11,7 +11,7 @@ - #ifndef BOOST_SIGNALS2_NULL_OUTPUT_ITERATOR_HPP - #define BOOST_SIGNALS2_NULL_OUTPUT_ITERATOR_HPP - --#include -+#include - - namespace boost - { diff --git a/depends/patches/libevent/cmake_fixups.patch b/depends/patches/libevent/cmake_fixups.patch new file mode 100644 index 000000000..d80c1a948 --- /dev/null +++ b/depends/patches/libevent/cmake_fixups.patch @@ -0,0 +1,35 @@ +cmake: set minimum version to 3.5 + +Fix generated pkg-config files, see +https://github.com/libevent/libevent/pull/1165. + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -19,7 +19,7 @@ + # start libevent.sln + # + +-cmake_minimum_required(VERSION 3.1 FATAL_ERROR) ++cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + + if (POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) +diff --git a/cmake/AddEventLibrary.cmake b/cmake/AddEventLibrary.cmake +index 04f5837e..d8ea42c4 100644 +--- a/cmake/AddEventLibrary.cmake ++++ b/cmake/AddEventLibrary.cmake +@@ -20,12 +20,12 @@ macro(generate_pkgconfig LIB_NAME) + + set(LIBS "") + foreach (LIB ${LIB_PLATFORM}) +- set(LIBS "${LIBS} -L${LIB}") ++ set(LIBS "${LIBS} -l${LIB}") + endforeach() + + set(OPENSSL_LIBS "") + foreach(LIB ${OPENSSL_LIBRARIES}) +- set(OPENSSL_LIBS "${OPENSSL_LIBS} -L${LIB}") ++ set(OPENSSL_LIBS "${OPENSSL_LIBS} -l${LIB}") + endforeach() + + configure_file("lib${LIB_NAME}.pc.in" "lib${LIB_NAME}.pc" @ONLY) diff --git a/depends/patches/libevent/detect-arch4random_addrandom-fix.patch b/depends/patches/libevent/detect-arch4random_addrandom-fix.patch deleted file mode 100644 index 14ce2edb8..000000000 --- a/depends/patches/libevent/detect-arch4random_addrandom-fix.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 266f43af7798befa3d27bfabaa9ae699259c3924 Mon Sep 17 00:00:00 2001 -From: Azat Khuzhin -Date: Mon, 27 Mar 2017 15:50:23 +0300 -Subject: [PATCH] Fix arc4random_addrandom() detecting and fallback - (regression) - -But this is kind of hot-fix, we definitelly need more sane arc4random -compat layer. - -Fixes: #488 -Introduced-in: 6541168 ("Detect arch4random_addrandom() existence") ---- - event-config.h.cmake | 3 +++ - include/event2/util.h | 2 +- - 2 files changed, 4 insertions(+), 1 deletion(-) - -diff --git a/event-config.h.cmake b/event-config.h.cmake -index b7f0be57c..5c233a3d9 100644 ---- a/event-config.h.cmake -+++ b/event-config.h.cmake -@@ -53,6 +53,9 @@ - /* Define to 1 if you have the `arc4random_buf' function. */ - #cmakedefine EVENT__HAVE_ARC4RANDOM_BUF 1 - -+/* Define to 1 if you have the `arc4random_addrandom' function. */ -+#cmakedefine EVENT__HAVE_ARC4RANDOM_ADDRANDOM 1 -+ - /* Define if clock_gettime is available in libc */ - #cmakedefine EVENT__DNS_USE_CPU_CLOCK_FOR_ID 1 - -diff --git a/include/event2/util.h b/include/event2/util.h -index c4af2bd60..ca4048944 100644 ---- a/include/event2/util.h -+++ b/include/event2/util.h -@@ -842,7 +842,7 @@ int evutil_secure_rng_init(void); - EVENT2_EXPORT_SYMBOL - int evutil_secure_rng_set_urandom_device_file(char *fname); - --#ifdef EVENT__HAVE_ARC4RANDOM_ADDRANDOM -+#if !defined(EVENT__HAVE_ARC4RANDOM) || defined(EVENT__HAVE_ARC4RANDOM_ADDRANDOM) - /** Seed the random number generator with extra random bytes. - - You should almost never need to call this function; it should be diff --git a/depends/patches/libevent/detect-arch4random_addrandom.patch b/depends/patches/libevent/detect-arch4random_addrandom.patch deleted file mode 100644 index 1f480715f..000000000 --- a/depends/patches/libevent/detect-arch4random_addrandom.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 6541168d7037457b8e5c51cc354f11bd94e618b6 Mon Sep 17 00:00:00 2001 -From: Marek Sebera -Date: Mon, 6 Mar 2017 00:55:16 +0300 -Subject: [PATCH] Detect arch4random_addrandom() existence - -Refs: #370 -Refs: #475 ---- - CMakeLists.txt | 1 + - configure.ac | 1 + - evutil_rand.c | 2 ++ - include/event2/util.h | 2 ++ - 4 files changed, 6 insertions(+) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index a861e7d96..f609d02d0 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -338,6 +338,7 @@ CHECK_FUNCTION_EXISTS_EX(sysctl EVENT__HAVE_SYSCTL) - CHECK_FUNCTION_EXISTS_EX(accept4 EVENT__HAVE_ACCEPT4) - CHECK_FUNCTION_EXISTS_EX(arc4random EVENT__HAVE_ARC4RANDOM) - CHECK_FUNCTION_EXISTS_EX(arc4random_buf EVENT__HAVE_ARC4RANDOM_BUF) -+CHECK_FUNCTION_EXISTS_EX(arc4random_addrandom EVENT__HAVE_ARC4RANDOM_ADDRANDOM) - CHECK_FUNCTION_EXISTS_EX(epoll_create1 EVENT__HAVE_EPOLL_CREATE1) - CHECK_FUNCTION_EXISTS_EX(getegid EVENT__HAVE_GETEGID) - CHECK_FUNCTION_EXISTS_EX(geteuid EVENT__HAVE_GETEUID) -diff --git a/configure.ac b/configure.ac -index a127bbc91..e73c29b14 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -342,6 +342,7 @@ AC_CHECK_FUNCS([ \ - accept4 \ - arc4random \ - arc4random_buf \ -+ arc4random_addrandom \ - eventfd \ - epoll_create1 \ - fcntl \ -diff --git a/evutil_rand.c b/evutil_rand.c -index 046a14b07..4be0b1c5e 100644 ---- a/evutil_rand.c -+++ b/evutil_rand.c -@@ -192,12 +192,14 @@ evutil_secure_rng_get_bytes(void *buf, size_t n) - ev_arc4random_buf(buf, n); - } - -+#if !defined(EVENT__HAVE_ARC4RANDOM) || defined(EVENT__HAVE_ARC4RANDOM_ADDRANDOM) - void - evutil_secure_rng_add_bytes(const char *buf, size_t n) - { - arc4random_addrandom((unsigned char*)buf, - n>(size_t)INT_MAX ? INT_MAX : (int)n); - } -+#endif - - void - evutil_free_secure_rng_globals_(void) -diff --git a/include/event2/util.h b/include/event2/util.h -index dd4bbb69d..c4af2bd60 100644 ---- a/include/event2/util.h -+++ b/include/event2/util.h -@@ -842,6 +842,7 @@ int evutil_secure_rng_init(void); - EVENT2_EXPORT_SYMBOL - int evutil_secure_rng_set_urandom_device_file(char *fname); - -+#ifdef EVENT__HAVE_ARC4RANDOM_ADDRANDOM - /** Seed the random number generator with extra random bytes. - - You should almost never need to call this function; it should be -@@ -858,6 +859,7 @@ int evutil_secure_rng_set_urandom_device_file(char *fname); - */ - EVENT2_EXPORT_SYMBOL - void evutil_secure_rng_add_bytes(const char *dat, size_t datlen); -+#endif - - #ifdef __cplusplus - } diff --git a/depends/patches/libevent/fix_mingw_link.patch b/depends/patches/libevent/fix_mingw_link.patch new file mode 100644 index 000000000..41cbd463c --- /dev/null +++ b/depends/patches/libevent/fix_mingw_link.patch @@ -0,0 +1,25 @@ +commit d108099913c5fdbe518f3f4d711f248f8522bd10 +Author: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> +Date: Mon Apr 22 06:39:35 2024 +0100 + + build: Add `Iphlpapi` to `Libs.private` in `*.pc` files on Windows + + It has been required since https://github.com/libevent/libevent/pull/923 + at least for the `if_nametoindex` call. + + See https://github.com/libevent/libevent/pull/1622. + + +diff --git a/configure.ac b/configure.ac +index d00e063a..cd1fce37 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -906,7 +906,7 @@ if(WIN32) + list(APPEND HDR_PRIVATE WIN32-Code/getopt.h) + + set(EVENT__DNS_USE_FTIME_FOR_ID 1) +- set(LIB_PLATFORM ws2_32 shell32 advapi32) ++ set(LIB_PLATFORM ws2_32 shell32 advapi32 iphlpapi) + add_definitions( + -D_CRT_SECURE_NO_WARNINGS + -D_CRT_NONSTDC_NO_DEPRECATE) diff --git a/depends/patches/librustzcash/guix.config b/depends/patches/librustzcash/guix.config new file mode 100644 index 000000000..515e937d7 --- /dev/null +++ b/depends/patches/librustzcash/guix.config @@ -0,0 +1,12 @@ + +[target.x86_64-unknown-linux-gnu] +linker = "x86_64-unknown-linux-gnu-gcc" + +[target.x86_64-apple-darwin] +linker = "x86_64-unknown-linux-gnu-gcc" + +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" + +[target.x86_64-pc-windows-gnu] +linker = "x86_64-w64-mingw32-gcc" diff --git a/depends/patches/zeromq/builtin_sha1.patch b/depends/patches/zeromq/builtin_sha1.patch new file mode 100644 index 000000000..5481c9dbd --- /dev/null +++ b/depends/patches/zeromq/builtin_sha1.patch @@ -0,0 +1,17 @@ +Don't use builtin sha1 if not using ws + +The builtin SHA1 (ZMQ_USE_BUILTIN_SHA1) is only used in the websocket +engine (ws_engine.cpp). +Upstreamed in https://github.com/zeromq/libzmq/pull/4670. + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -234,7 +234,7 @@ if(NOT ZMQ_USE_GNUTLS) + endif() + endif() + endif() +- if(NOT ZMQ_USE_NSS) ++ if(ENABLE_WS AND NOT ZMQ_USE_NSS) + list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/external/sha1/sha1.c + ${CMAKE_CURRENT_SOURCE_DIR}/external/sha1/sha1.h) + message(STATUS "Using builtin sha1") diff --git a/depends/patches/zeromq/cacheline_undefined.patch b/depends/patches/zeromq/cacheline_undefined.patch new file mode 100644 index 000000000..02bd2a5fe --- /dev/null +++ b/depends/patches/zeromq/cacheline_undefined.patch @@ -0,0 +1,15 @@ +Use proper STREQUAL instead of EQUAL to compare strings.txt + +See: https://github.com/zeromq/libzmq/pull/4711. + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -476,7 +476,7 @@ execute_process( + if(CACHELINE_SIZE STREQUAL "" + OR CACHELINE_SIZE EQUAL 0 + OR CACHELINE_SIZE EQUAL -1 +- OR CACHELINE_SIZE EQUAL "undefined") ++ OR CACHELINE_SIZE STREQUAL "undefined") + set(ZMQ_CACHELINE_SIZE 64) + else() + set(ZMQ_CACHELINE_SIZE ${CACHELINE_SIZE}) diff --git a/depends/patches/zeromq/cmake_minimum.patch b/depends/patches/zeromq/cmake_minimum.patch new file mode 100644 index 000000000..d6b6b5fae --- /dev/null +++ b/depends/patches/zeromq/cmake_minimum.patch @@ -0,0 +1,18 @@ +Set a more sane cmake_minimum_required. + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,12 +1,7 @@ + # CMake build script for ZeroMQ ++cmake_minimum_required(VERSION 3.16) + project(ZeroMQ) + +-if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) +- cmake_minimum_required(VERSION 3.0.2) +-else() +- cmake_minimum_required(VERSION 2.8.12) +-endif() +- + include(CheckIncludeFiles) + include(CheckCCompilerFlag) + include(CheckCXXCompilerFlag) diff --git a/depends/patches/zeromq/fix_have_windows.patch b/depends/patches/zeromq/fix_have_windows.patch new file mode 100644 index 000000000..e77ef31ad --- /dev/null +++ b/depends/patches/zeromq/fix_have_windows.patch @@ -0,0 +1,54 @@ +This fixes several instances where _MSC_VER was +used to determine whether to use afunix.h or not. + +See https://github.com/zeromq/libzmq/pull/4678. +--- a/src/ipc_address.hpp ++++ b/src/ipc_address.hpp +@@ -7,7 +7,7 @@ + + #include + +-#if defined _MSC_VER ++#if defined ZMQ_HAVE_WINDOWS + #include + #else + #include +diff --git a/src/ipc_connecter.cpp b/src/ipc_connecter.cpp +index 3f988745..ed2a0645 100644 +--- a/src/ipc_connecter.cpp ++++ b/src/ipc_connecter.cpp +@@ -16,7 +16,7 @@ + #include "ipc_address.hpp" + #include "session_base.hpp" + +-#ifdef _MSC_VER ++#if defined ZMQ_HAVE_WINDOWS + #include + #else + #include +diff --git a/src/ipc_listener.cpp b/src/ipc_listener.cpp +index 50126040..5428579b 100644 +--- a/src/ipc_listener.cpp ++++ b/src/ipc_listener.cpp +@@ -17,7 +17,7 @@ + #include "socket_base.hpp" + #include "address.hpp" + +-#ifdef _MSC_VER ++#ifdef ZMQ_HAVE_WINDOWS + #ifdef ZMQ_IOTHREAD_POLLER_USE_SELECT + #error On Windows, IPC does not work with POLLER=select, use POLLER=epoll instead, or disable IPC transport + #endif +diff --git a/tests/testutil.cpp b/tests/testutil.cpp +index bdc80283..6f21e8f6 100644 +--- a/tests/testutil.cpp ++++ b/tests/testutil.cpp +@@ -7,7 +7,7 @@ + + #if defined _WIN32 + #include "../src/windows.hpp" +-#if defined _MSC_VER ++#if defined ZMQ_HAVE_WINDOWS + #if defined ZMQ_HAVE_IPC + #include + #include diff --git a/depends/patches/zeromq/macos_mktemp_check.patch b/depends/patches/zeromq/macos_mktemp_check.patch new file mode 100644 index 000000000..c703abcd7 --- /dev/null +++ b/depends/patches/zeromq/macos_mktemp_check.patch @@ -0,0 +1,16 @@ +build: fix mkdtemp check on macOS + +On macOS, mkdtemp is in unistd.h. Fix the CMake check so that is works. +Upstreamed in https://github.com/zeromq/libzmq/pull/4668. + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -599,7 +599,7 @@ if(NOT MSVC) + + check_cxx_symbol_exists(fork unistd.h HAVE_FORK) + check_cxx_symbol_exists(gethrtime sys/time.h HAVE_GETHRTIME) +- check_cxx_symbol_exists(mkdtemp stdlib.h HAVE_MKDTEMP) ++ check_cxx_symbol_exists(mkdtemp "stdlib.h;unistd.h" HAVE_MKDTEMP) + check_cxx_symbol_exists(accept4 sys/socket.h HAVE_ACCEPT4) + check_cxx_symbol_exists(strnlen string.h HAVE_STRNLEN) + else() diff --git a/depends/patches/zeromq/no_librt.patch b/depends/patches/zeromq/no_librt.patch new file mode 100644 index 000000000..b63854c95 --- /dev/null +++ b/depends/patches/zeromq/no_librt.patch @@ -0,0 +1,54 @@ +We don't use librt, so don't try and link against it. + +Related to: https://github.com/zeromq/libzmq/pull/4702. + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 03462271..87ceab3c 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -564,13 +564,6 @@ else() + check_cxx_symbol_exists(SO_BUSY_POLL sys/socket.h ZMQ_HAVE_BUSY_POLL) + endif() + +-if(NOT MINGW) +- find_library(RT_LIBRARY rt) +- if(RT_LIBRARY) +- set(pkg_config_libs_private "${pkg_config_libs_private} -lrt") +- endif() +-endif() +- + find_package(Threads) + + if(WIN32 AND NOT CYGWIN) +@@ -588,9 +581,7 @@ if(WIN32 AND NOT CYGWIN) + endif() + + if(NOT MSVC) +- set(CMAKE_REQUIRED_LIBRARIES rt) + check_cxx_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME) +- set(CMAKE_REQUIRED_LIBRARIES) + + check_cxx_symbol_exists(fork unistd.h HAVE_FORK) + check_cxx_symbol_exists(gethrtime sys/time.h HAVE_GETHRTIME) +@@ -1503,10 +1494,6 @@ if(BUILD_SHARED) + target_link_libraries(libzmq iphlpapi) + endif() + +- if(RT_LIBRARY) +- target_link_libraries(libzmq -lrt) +- endif() +- + if(norm_FOUND) + target_link_libraries(libzmq norm::norm) + endif() +@@ -1553,10 +1540,6 @@ if(BUILD_STATIC) + target_link_libraries(libzmq-static iphlpapi) + endif() + +- if(RT_LIBRARY) +- target_link_libraries(libzmq-static -lrt) +- endif() +- + if(CMAKE_SYSTEM_NAME MATCHES "QNX") + add_definitions(-DUNITY_EXCLUDE_MATH_H) + endif() diff --git a/depends/patches/zeromq/openbsd_kqueue_headers.patch b/depends/patches/zeromq/openbsd_kqueue_headers.patch new file mode 100644 index 000000000..7000e209f --- /dev/null +++ b/depends/patches/zeromq/openbsd_kqueue_headers.patch @@ -0,0 +1,24 @@ +commit ff231d267370493814f933d151441866bf1e200b +Author: Min RK +Date: Fri Feb 23 13:21:08 2024 +0100 + + Problem: cmake search for kqueue missing headers + + Solution: include sys/types.h and sys/time.h as documented by kqueue + and used in autotools + + fixes kqueue detection on openbsd + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index f956f3fd..814d5d46 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -380,7 +380,7 @@ endif(WIN32) + + if(NOT MSVC) + if(POLLER STREQUAL "") +- check_cxx_symbol_exists(kqueue sys/event.h HAVE_KQUEUE) ++ check_cxx_symbol_exists(kqueue "sys/types.h;sys/event.h;sys/time.h" HAVE_KQUEUE) + if(HAVE_KQUEUE) + set(POLLER "kqueue") + endif() diff --git a/depends/patches/zeromq/remove_libstd_link.patch b/depends/patches/zeromq/remove_libstd_link.patch new file mode 100644 index 000000000..ddf91e6ab --- /dev/null +++ b/depends/patches/zeromq/remove_libstd_link.patch @@ -0,0 +1,25 @@ +commit 47d4cd12a2c051815ddda78adebdb3923b260d8a +Author: fanquake +Date: Tue Aug 18 14:45:40 2020 +0800 + + Remove needless linking against libstdc++ + + This is broken for a number of reasons, including: + - g++ understands "static-libstdc++ -lstdc++" to mean "link against + whatever libstdc++ exists, probably shared", which in itself is buggy. + - another stdlib (libc++ for example) may be in use + + See #11981. + +diff --git a/src/libzmq.pc.in b/src/libzmq.pc.in +index 233bc3a..3c2bf0d 100644 +--- a/src/libzmq.pc.in ++++ b/src/libzmq.pc.in +@@ -7,6 +7,6 @@ Name: libzmq + Description: 0MQ c++ library + Version: @VERSION@ + Libs: -L${libdir} -lzmq +-Libs.private: -lstdc++ @pkg_config_libs_private@ ++Libs.private: @pkg_config_libs_private@ + Requires.private: @pkg_config_names_private@ + Cflags: -I${includedir} @pkg_config_defines@ diff --git a/doc/Doxyfile b/doc/Doxyfile deleted file mode 100644 index d41eb5acb..000000000 --- a/doc/Doxyfile +++ /dev/null @@ -1,1752 +0,0 @@ -# Doxyfile 1.7.4 - -# !!! Invoke doxygen from project root using: -# doxygen doc/Doxyfile - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = Bitcoin - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = 0.11.2 - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "P2P Digital Currency" - -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. - -PROJECT_LOGO = doc/bitcoin_logo_doxygen.png - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = doc/doxygen - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or -# section (for LaTeX and RTF). - -INLINE_GROUPED_CLASSES = NO - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = YES - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = src - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py \ - *.f90 \ - *.f \ - *.for \ - *.vhd \ - *.vhdl - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = src/crc32c src/leveldb src/json src/test /src/qt/test - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = boost google - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. - -FILTER_SOURCE_PATTERNS = - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = YES - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is advised to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. - -USE_MATHJAX = NO - -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the -# mathjax.org site, so you can quickly see the result without installing -# MathJax, but it is strongly recommended to install a local copy of MathJax -# before deployment. - -MATHJAX_RELPATH = http://www.mathjax.org/mathjax - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvantages are that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for -# the generated latex document. The footer should contain everything after -# the last chapter. If it is left blank doxygen will generate a -# standard footer. Notice: only use this tag if you know what you are doing! - -LATEX_FOOTER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# pointed to by INCLUDE_PATH will be searched when a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that -# overrules the definition found in the source code. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all references to function-like macros -# that are alone on a line, have an all uppercase name, and do not end with a -# semicolon, because these will confuse the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to -# install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = YES - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will write a font called Helvetica to the output -# directory and reference it in all dot files that doxygen generates. -# When you want a differently looking font you can specify the font name -# using DOT_FONTNAME. You need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = YES - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = YES - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will generate a graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are svg, png, jpg, or gif. -# If left blank png will be used. - -DOT_IMAGE_FORMAT = svg - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the -# \mscfile command). - -MSCFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 000000000..36b3368d6 --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,2459 @@ +# Doxyfile 1.8.12 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "BitcoinZ" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "P2P Digital Currency" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = doc/bitcoinz_logo_doxygen.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/doxygen + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = src doc/README_doxygen.md + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = src/crc32c \ + src/leveldb \ + src/json + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = boost \ + google + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = doc/README_doxygen.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = rocks.btcz.BitcoinZ + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = rocks.btcz.BitcoinZ + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /

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

w$EMgg3muC82wq!$%rdx`g z0K;enL>SubP<#5xGP603_!*xQ6vTM&sK@MECPLiKzQ(+4b$64BkoPk;TW(tSWeV-( z)3t7~nm>+F=g!B4_Z)a@x*;qONL*s;jd5&PGSx`+qf)P%*l9k_uB z6DM{U3o-uM&+od-+Nkni?#FfkDD);;;$Q6`Oew}9SDNc~_byBO-P>TgN1^Q5yo_v~ zB}NyZp6@BLl}tK~Es-;dJXUXt9YXX>6$T+;O$!6m^!W0-a^KX;rITn9eSzU$w0eXf z?=S4UwQ2M;sB0s7?(zpF-ruXMcN1;8cd5FmeI+sf;RX)02MM55*hJdfCFf@aZ<5dB z(1=$z32(2Tl%Qn#Z`G!7=?x;wG}|9PRNYry-^hEFjht(rNYRm5O6~a!4~rR{V_d} zy7RHXHOHT}XDV2QH53>;kDmTX<7vCN5tL;2%JywQeELnq)=&NzP4Km(@Dm7xh%Kp* z|BQ0`2{@^QcGN%avClta87EFTC{&l+Q>;7iN*r)F2rTaxg}WvaN?WL;B@T&Z2xlbv zcmLmzLXPr3_ZgK z?Ph$-^D^p4b;b1!8O#VL)9_f?NoUn~8*%&B^cT2^y5qzwq$%0GZK6xfhbiaWG!gI$ z)#Mhg8^*?IoWf)>9CV!UkarugqNwSAsh}HNIv(|70pThH8GtL%S>mEvdr=W;8Ui@0@ZX!*6&s2S$HviY3uQS+2rC;P$F~`~Vzb0WgcIPncd~$$Xt|7xh zh%I{;-9Te5SwNJ?wOj;Cb_2Jz2un2|F-H94r`hxkY5wHv-ue{3n*`|4vqT{Z8r!Ch zn%udXZ5U<5j=0u6$Op4lQ3qo3;qCsafdm*u0gF)x;WO|J9N|Z+zE53)f)Dwh|D>x| zi8Z>9LBacTSR}fvUKQZyPjoQ>`E{=*Laf%ahvbvAXe>>?+EIg+bN~Dr*`Mdg+?y0Q zj~dIV035>ZWFRdS9|_V@4NXnK`R&#zCe%)Vw9?wGuPSnj;zqYR^K|s2As7LlLK=E~H^pC+ zvui}Uv?c8r>B%Xl0dp{;+5vjq@lSIJ8{@6$GG}X?ZEKAr`K+b-yw1zD0$M&WgcfWz z%~k*gl*3Qneo{L*p;eKLn2V5~`G+a8jI)Rcl))Gu5k>~r%6vH%dUxMzuWP;Whj3xX z$0f3;^iL_%KhO_R$cxgS14--;yQMFSQQ=9cjI>mc+_8RLWx%ScSP_e{!3X&mWGPxS zKA>*fsQQYPDU0q-6CTdY)}p5&OG+_>L3 zjn&SHn)Wn(@0C}d<1sNs%t&)WR*Nf;e8?9>4P&{H97J{H<;MLHN}I6j&$P{#@CK4NKYR=~W83dH=``hW{V0ylhwrFo4jIN^aG&H&ls*-N(X;CREJO!7i7I44M z)tr9r7TCJKb6FQYph;*jnNaePU6SBnC;}xl%4d#B`_DcASQ^jrC7sU{O;kkW`94Q| zVX2qy`;+B&A#J=GZu6{V|InHZmm19eqDM8K+HDerggxIp?NSOO;efnSHQi@W3`icu zDAo#f>=vUv705w?YgT9fi3tjQnUSj!`gJbn#==XrMR>xW+85(nZ0xTRScI2?zu1{= z3znEax_6UCmHAI9BwjQ=IcO7z@s8mJr%LYPpplQw^1IVt?cnrbi*@_if87T{4v+p9 zA29$WJ=o?ggEVD{f7c#440a3H@z6xPvc!I$1YmXr!Jj5PVrqZfla^Ytb9~|RoS}}g z$}w0SkAz`tD|5I+>$Td=%%db27Aw&NlQ>B3pKS{9^i3|AW(9tkOTFMT0ZCY6c~!qw zgH;DxE~BB^NbbKop?-DFf(>pz2EHeGvBZxq1mfspLu?kDCsy%;t4=}{CN5vfM*Q+; zg7${Pbj~-w;&pWq)jQpXiKyJ(02tYGX%Ns{Vt^!2i-T#`mRQEBVqs@TPu*SO!1GM&&SJ4w+Je{ z)o-6(ySJK;g1t`Eh5xbRier^tt(a&2;QBZ6B&?%=>ZpF~O^w^Om)Re)I`)rN8mx2y zE|{E}yYF?>1&63;2oP1otr7g;Q}ov#bW03c(Wa^3lH$#ekw8KUzdxUukO5^sy9xjJ zT4{g$te*8FjoP$n0tRbh6c8S#i;53{$l6vre%hJ&(U`3U)T7+aS0&Yki0CP_i)gw( z$!66Ia}0*>KJ#CMW4xuDTYm~yJ-jz~deeQfK5~8e>{${KO<#apCfMs;zsq)S90n6X z`~B754Fk(Jv;pb=OY(1F9aolaZhsF32M5={O>i7_1Y-p8(U0s(>qN}p84vPa(`L+> zudaD7Mp(IX!1xmNMIj&hqm<=x4@*e5pB9ixOZ8ZbOPa4Z;@GU6$S zzvyBmW99Z2$%jeDw+eQt#}O~B4yNKqn|>!EiLuLy%&s`br~kBR763X-OZR0jiSuCN zeqc+htTilEXB(szM~erM*1A~KEMJ$oyZztdO<B! z9`n9Nx7Km7({{pt(JS^tr$~v+>W==o=IytH^%UFyI05rnP-dUY#zf9ElxHYkheL-i zO&nzEj{Z}*Caprn1rYuJlkok|(sJzpgbVz0f*=F|NrYRbHZw2r;e@GG9yaRzG$GN| zBUye)X%-fi))hSq*&GFj{fzb%+UOW%*aMoLb4qvaZrFfqf?6UolTivxN4oBJQ_Jy^ zCe+T7pVFwF2>-*0NT|)S^Z=%;-~g!!p7&DPMsOBJ1i?1HXaoGK@=zjWI%v%+wy9)1 ziFVW3K!}QvMtafiy@Rd*ezuMVnh`?^6^1A{JWwoqW(bfUDj4qs zbi7m@v@&bgmA_Z$0pbdVt{bWXgB0>zd75@kp&^3{IN}14pa_V0FhgaM{#1VACo2FheD5MH;jZKAOorFW||9> zsR7Y9Qm4a0?Q{0Es2ebL#$=Xi&FO30ZtWMv;kvg9vnq4nntAQLKD6I5n_Q36&v%{O ziyPKe9es|+6*Paz24p%cQefvwXuRSj8Ed~EEFr=kWRbg-KsR^JiRox#1RT7CSBP;F z2}@1pok%6H%w~&KDRgSSMGj%6Ue~UC-uO8uVH@I5Z@k#%@Pf}ic?kS8(neTR$mi3I(f;CLSE)dRsJ^z8TGG=7y@nz6| zl5uSg&^@;4OuHafIp&`m|A(u;jH|Ni*2dw*0;Ic+GS z(M)YV<$RERm`-Lsi6)eL9f=H~^iSiE=s%z&jmN8Z`2NzjWtg$EZNR#-B^p2Yor=q* z37RocQWR-&!pB@R~UAYP2X%Z^_?fos9%A{Xe= zx#}xmdL!-$Iug^5-7~t+QApshb7s=Ba3E_)PpUgmH+cXLJQny^!<5sCv5S*O2MLNz zwK1Q4Q}C|F*EiPmPPoo*tFiV&2iB(xWdZlfLKJ5p9fZnQGV?n3ozUv)fzif+m~QOSD8Z@+=%@qPl0tz2*Y z1eAaYa$1^f$e$2Dm?NKg$SA6bM|4rOuPO{im`@8A^G7TBhctEE@9cPT_!+f-SSz@o z1Q;QDcDzz|$qJOPjQseJwr7ZD%lCwoe(8g5vM)4(8JFJX?mOVM4HuaqUbsG_t*pak zrn?5uRwU3`y9FF735EvAYN^}5<4xx;;nMl+ksmZt%^uws>6`V8+#^%FaXCm#o&$q~ zV3rrOOY2D9N3dF0V0)}^2*Abr_@q54S6;#+VcvBW&)1oN)gRoa_HEGb1~^qlxdvSB z&E|=MkbLYIv$?a$8zc5HoMwGS@{E{|X_U8kie8D8;ZNZY)7yhTcW28>u{A$}2=L10 zIF_5~+hEj{xx4HXz4H`l;!@Bqflf8h_LPq_Xaf{nE7Z6210@PJ0pd#Pn5x~YD4UN# z3C{#`-bxy~IF7CVntk?4b@G}s+i0bNRpk=_#st7NAj5^VQCZtGQMR&-!L8x4WK7?F z%l3#}y>24RgKKLUClmIXpNVtmEHNB@d^?1>?f7-{1)@}9eOf{VqSs&NLE-FbuN&^f zeKmu1#(BERSpOj+@^-jOSHS9?{_rt~u>Lgyr$5VxF6J(oNR_LrEWC<^O+sV>l{GS3 znQq>Wf0>UxY`p0=NsbaTUYy1)HJ0|o8ZMk|*h5^%-_ET5BxkAu8JIg5bzeTcA@jh% zJivF--hdN~%OF3N&Oq@bZj%NeGsc^q;GPR-`5rW@O}%S)A|k4TU!HOX#A+7<9GjNQ zy+>jSO5lTk;#~;k^3S}(%C3aShWE{FveXVobg7JcX$;3rtWza42dC%}4`Ox7uzp-bZ z&Q|xBPWYve{JmBcs)tJVX`G(lRT!b;BST`zi}+M7>Tah4Tl3{qO$lRNdf3~ie*!z?sd}`0J$bnBjBHb66EY< z7K1um(+cq1?Y`n?dvzlLxv$G2k@)?*?5ekB< zF;EeFe8XHeYxX<`Y(JvLNvX|IAMVB=A59zzKuBdo1J2L$=4;C#mMI=$neH!?(MZ|N zIsefOnS0?k;!7WQkR);zzrO9+IAa8{Oh-(t+{ohc$j(5pE30in!{r$ z56@6OwUzsExCb$24GBG9t&_m(IJ)~{pHRyxk|CiAZWIeA9$&I0!DN>g-P<$3s7IX5 z|I5;&Iq}1;Kif+idFCP!GOt~vlum?_h zJ=}`??odz?jawZ25z^gETWGO#IoC`~Bueu;Q5rJpn0qtNAL3_1TO{3_@2*h*mzuy*u&3$bLj$z`R9sN-`q%a8X5i7*SRN72 zNZ`$Xvx1(l7of#IjGvB(KMKdl(VAAg2KxrY=rjmKXlc4kz>tmYi{D|R@@eD431L>i zG6Gw=o+}}-*iU=dhI$)+DQuKAg#j)npOfx{x;$Y(9$tV3%;%5%JzXdfAa2hQYFW8E z#7w2SUxoKEz`H;T-Fz1z;BT;**dCvZv0SFmZ;tRac@_Lfe$H+ps%iO7vq%#c|Kvg{ zIP;zGGdcD7Dej0(#iU`$ux2c&<>FmeJc#5uhJ}!$;ngoXpfeV zQ^bOs5AM(>^+$*>9oGBL5#)r*?voNKGp-k5XWqk22RzGKIM~f=_8FG}9%j%m9q8%- zt9QWR8sfvcM3$jWw?9up+gPi-8L>tqAq*-%)Jp!Q7>i|nnUmcQ3J?GGQaTMRUg zwZ~lmin;~A7~b%@l5z=M@Pm6v`$GueX)45=yo6ORxAgSsAV=%n|B+2#BG96O zh?MTXL4@YiJs;N%ZG52E?Xu>PFpaB3_O^Y_ZJ;V4DKYpkoa5dwJXNORF#OfeJ}`M# zK$&Ctz4Kqi#KubgG578_vUz%=OazE69XH-Qjw%XLClhi>NR!9!igY@&Q$>oOf9ef>fCAbc)lT&hTGiHV%qq=k<8Z<^y*cFUcSohQ{Uo$wG*f?ESB=V%D8FkLC-jF9gZ&cwh6)2ge z*GhyMhh%VkNt@gw85v#;MQ;6s^Kq{17uHR4%0I3`!>8}?cM-1&Zrfpr2{L6NHbRf? z7zgaq6__y$zd4alya(au>028W4wg~pzsq0j1s8cJQTTO+DuVw3f>zfjEs}@J=FqMZ zN%INF!nLu2SkD16HBc=R)xwQuqZ~NieKh^N{9y3;tHO+!{n$|?2WG44J*W(*x0@XG z4iBr_Bz^C78hN6T*oJ6Qhq!@e%Tp#;U+E6%TpI?_dIm=7|F7t({$2z4_mnuqNij$+BqpT=n7#nbV?<$6Lp0V@XO;IlOpp7JreJ386XMdwr8Sw5G5jBiq z(EXIJ6lBn?U#B?gk+?^!*x?9(wJ0^=H?D&W@qDf3D@vk)5M)X0cKi&;$O$Xhd1Apy z^=GlGz^tF4~o^`1csf$K(AiZ#W$^yV?4fx+Xe_@!cs)?IL-|OQ^}$nf#Eg zMRax=e*tN{MHt7Z%_X4@6~yPv8wJtSj!G{(n{kYlvQTk`E)jd**Qoj?#ubAJrHN6{ zz53U}w;$*8>|*x?1Unc_-Xr<5Nt0<#*27YY#YqThK=IM++qZ_Y!>qhT_jln(M$W$C zu=$1i`klQ?<8lM*INi?n#TWb70k)F8p<(pGn(v0{-?Uf^?Ju$6kov8YCs^CKb;c32c^ zkGhx?B3E^J4ij=f;P0tcsM3J~Mbrw`5FV8LB*0Um@ITYI2bPj|?FJPW_h4m23->ID zR~k(RNpgu9?)>8$^FfPRwyWt&m6MR=_Q?Kh<`6hGLp=K6C$z;=*1A4Kx9bB5s7W6MRT;@Q`ccKP5Gue}=k#ep^8|O9E zDBZZp=79chO9^_K>QzbrOP7xzO_cNrs!~@3ROIcks?L1kLgr?~7e@XxSR`T94c9MS zKDdtVKgQ~IF6HBObZCb7;3*=$mp1K^f#WBjE>be_!p)k4N5oiqphgk?kha&(uKYU2 zf5Ix%bM`>2f9q9_0!(hCuG-&JQP*IkeXrRuQ^-%PhBc{QdNOdMd(a;+20XQ)ey@%Hp4W{oCoW!vFA8m<>HC zi8gCA+R_3j{!@O2vA}VuvW~~%4xjt3MlybS(xE?mT{d-S&!0}q*9W6T;VOswC_KWt zCIdgY8-uXTA@G6#s38E9J;~Ho^hm{qbKLKg^jwA!|^0btFiGU6H;Dd_| z!IpxC5~DwS3a*2;2aU5%W-jH8^fRF*UzRcEL__{yd&mSaP%rcI?+iODjGFyz8GL}* zu}!Vq8M2E#G+Ui~Y(LtN`V$M~Vz=R?E2JOelccWtD{^ou_#yYUv2;`O?3iq)f9Pwy zSf@SbNA4rFwWf9rWe=LN#sbJh{pOaimJ}&FAg$v@NtwjM>=|#|h7UAbzxReO!Qshz zZ3TF(_SR$gG%HGN)ZChBRg&JR_}sQrl1$rL%Lg8!bfAFABd1PR`<9Xff~XR7M}SV* zz)5Kh_Fxqjcs~mjZtRg6G)e584|g+m)Lj&w%R~NUIq^yA(d@aD+mRDi}UNK$dEW zbw=7}k!$*N{Fm^A7Sd)>>VA80L(Cmpvz#*a$^Niti2;$#?8mS`dcw7Alpl%#Yr;r? zM_4m`)bmDm6A?Y@fNnb7Q7+@$NzLzpLAf6sM)iySa3Kgu7x6(hKHYx<&*<@xDu&COLXWz4IW?iY|_+eV$JOH_C|< z&-;R^_e%2DaM8odzz09V3vn7i>Nix9x#bg@qG;V0JnzqbKqc=&uxb{5UOM*h^}VEWK_OQ~Q0} zoJi&r*AfYrY#rIh09VLPTJ`*{_t7pmb$I*t@W3#mu<#D;jSe=>272wHZEp_~@-^@H zi{8Nd9?QGOlU(WW0FG8*KKcK$xHUr8+(%dPh0X2be#&gGlR?JEUNGCejDtsk>>m2W zq(Oqd>#B6E)(!#~cM3`7;oS8w*+OdgbBz4iiz|f8fX_}WDsf_nalSU*h;Ri= z5K40^T)DZD zXWF}lcq)_bZ^Jcivi(EI9vW}dI=Omosgm+8VLzSEL}+29?Dn}bcsViUOlYkmE^-yn zjpK(tFtq3j!T2bxE2>dK4*W541v&4kUHQDu0C+$3dxq=StSQEj=V)Y`Q%n7`@!HKz zC7Dg52DWMgD&?rwsH1g??n^)QYEAe$6g6=umKZhmGi{e@OIBp~+cd`?{05nFyfvK4 z1U9Jiqn;v5!^cWnUuiq%-i=6lVTrBKlzRQmq&sx&WnI>?mRVH27qtq`QlU7JuJws! z+mnxEPOJ;mTtB=Mw|Z?5Tv9uy3#WY&er!TnH2Ns{3Q|nWO7as+b@KP*B>q#sQ}E;_ z;di0mIrcv}AJpXby>SqT*WGbHEAZuB5Bx13)NO$slGqf2u2R<@?YU{lYj|`~qb{k4_Ki)gd3_Hz5}$Q?tG8VKayHm3R1*=DC-HHZ*gOBjo~S5m z!TvIY(XjytUlY7c~h-ui<&8QVg>vsqjZ@P%ehijn`6=M6p)e%N-Gk z=V-BXY3f*{9j*8WGiKu_6`Ff%)cW&?eAktjU_kF9H2XOfpYp!ySKPf|$FQG&ocXSU zcRtY)HM~e@s%hX^D}$uTRhK%DkdRv(36KkrzjwH%f_AZT){N}(qx}PXP)w7r3)Jwv z%vxocu~PP8-f3ihN|0edzmKJ#K)#Q|yEshEdW|6Q_N#nzo zz4&8#y`=J6#Nb1^`(qUXI(Q4@HGiSuMaV+wfUaY@LYs%i%>spx_%$5@1fh{HY2c_P zKllpHbd52;rl;xw8QNu7wUxYG{Fw?B_me&Ae3Clh{=-5*(IQu~>mn5HI%EB(aTE`c zAsdWNP+FY{tGw-#>@l#{n?t)3ESA-{lS&a_Mf* ziGI}zXnH_y-2gC7k$Y*NJtzC#U%!jGfgx;i>3VvHd2r`--n`c(62m^%d@DQKCZ7H< zJ+|auIO-z;U9LG=nTaWYWLhaRG#{m4*rlis-^S!ySWKsotZL*{R4a-fNrN*n?r5cc z7IDh?J7W9Ipb0dmjQpXW!)N`yG4h@*0%A>CO0S7$4m^1&gJZi1lCjwqCLBc9;R~5K z%)<7^x0R&N@vOGxZ2E03#R?U0VZoL%6((BDhZ@^zx7fwv4F zykzV~et42c@GzR%6$#W}_~F5L81hY{f_$k*(J1pdW5@PN=BsdR-HY5~dr!^cN1Huc z-pqf^J*{H%$yOV8-|L~>RZOWr)bUJzt>cE{M5vy?MJ;$zLle01m|^x#v(`#+ZkI=< z+>tvP86r;SU36i2gtQSCj6A^g060hhLD|=b0K7h~l0%TN9^QM9_rw2puVeRI5+Mj1 zI>8I~l;zNFFZORa&wc#MGw;$I02KBNIWv{+^nE38WO~u86^^~1tIT1{@}jxCT8n!$$s4;` z0`h2-YoHb24y^j2Xnbn`U-KyB73{oNcoHmMHI+GmQ!d#S(a$&5=@~Do!MR%~ zmcF}uy81{dYH9d?VIq?@m&C*7a$dSA{Xtl?zrGbhni5w@|AK7!JmBO*_?D}<{N!++ z>7scpp*jNZ;Fvi3iNJ$93c~yJ&w8%`unMpp3baXCL)&{NNErbtX8 zg<;?Rv@(|v00Q*RD`JNV2a7pBQngP)F7|nu*7-gm#wfu^{f;;22@k)Dw9)oY62fat z?u9iO)cS(Ks+)XoGM?p51h#rb&j?GjG(u=36Xo0B2eQ@(iw_jNfJoceI-x}P<3p7w&gBD+UCSLenXUMP-47B-)C9AH6 zNGP*zqhV$C_KkieuVWTTJO$n6aNQRFUT=V8|9m1b)qDt~fc&t{RB~icMH%@D|BS*~ zb5W9Bp_i7!c>XbDv?>}A9LV4Yh=yrrL&-1 z$RQw@jA*EM?qFgfKOd{eM7+3o9{TbB2UMkZ3zAb1ISRzj4*T6ZyqD<|YF{$roLyxlf`k@;M2Yx(3#V+;G?s?M#Vo!C_Qi~g%~Z_;{`9?%%tBKF8bT)awj?gV*k^G5w_g-P-s~0JZ@s+*+vDaDf+n|q6EumNEpVdHjZ3?`LT;EiOQ1Rq1dJ}ks0#Le zUwabXJ$Fxkfj~ss^bRK85!GyV5?eGBfkJL>gN!ncf`rL);+uw(XKZ50q)nwyK>n8to;hVa zp1;IvUmFJo(Rn>yrApu*J5J2lM_t)+)>jVXM63Oj(F6yZQP|gFt8o?wOJGxm_FiA( zU9r6nU)FxYIPp1kc(aU? zqqd>^c+?e-imt~kMmPlhz8*QK>zi1l0>`Q~XrJ~5v<^>CxG}7TEefn}dD(|aSi?U4%>NrQ4lpsK_=lm&~6$yWw0YHz3M^(fO3 z=Zlt~g@a4B5L=);k+DljFHrj9@kP1+R?G1J_GMsQV*T@x6g13Cye~k}F9ytJ4u%6j zPrz`r4aAv59Y9J;gv1-))|>0AvmqB2xj8I2)IJWN7I98;ze{*!eQG1@5D;nkC;G*;`qHf_qBYdDyCYtdxCNfRGW_S zQSZZq0PjKi=L;CE=;t@abDF$+AzY=`50cvogpWhj7k^Q1ACtk+9*sOQVVM8JgQ&jv zoCI+ifwcnekF?_TAmB!dQ5P%#ZP)3(YhyHfbgA)eh`AQZH$XUSe-nS7o;CTEY1gQ2 zVP&+2<<)Ze_!l1U)i9JkH_RG~Sv&YE(s2}#G5FKaXZZ5#G3R&}+#TO4i)xVsKDwvO zB$iPTTkSEl-%Fv_OjY7P8nFobul=IuPRX0gPO9H-VdzQ6#z1_?DU!(Wc$R>LGDn2u zJYt=8sbV^ILmgZLq|#Z=H0Xj_%(h#GuhU7-3&}BnyAMy1se5eaj<0>i@5@W2q3G^& zw7BEP|I|fwv*#v;|BL&Zdqs)>=>pX5^e#qVUU-Q@eE~S|e9o~jspl}w9DqQ7b2Z)jT6~8_nM(5XtM1FNYu|qet$o^u+&ni)REpc~g1T0`0-_ z?tikZEjA=J7KmUr<|hU63Jc$Q6=-^wj@eWb79vPYQ;vsXUl4O=;{ofL5&`)N+oFnfk{5Z|B=#VOeRmN5{4#vzkohp(M#Ha2Nmn+ONU-#PX~cW(o_$OGri8KW zxiH;DS_REEn*L21(Dg8*Fd+4ZS8LEj7ZGVGzjN+Jsbmz|l zszONgLX@>WA5V?!zPEdK)*H zm~VGR{)~)Ph$`6!n)8NAmai)@+lyZ&s5*Mg-A`nRCP2CepOwBSS7@=8`kDPVpAa_p zeyIzl)ZuJiWR9_L^W_32L~?56Xze59D>0}xEx$!!_+WhyRQZrk@XjUc-r9{{O~G`S zI))jtcGBD#@EW`mVt=aP=fef(iyzvsX)t(_0JP&JwSMw} z%uFjJv9ofPK(Z^T81>yB=P1JE#%8Fph4y0OyO1_#tZak~`5lZLe1;*^8KSdzV{qqE z^J2`}i0f*XH|GP6iFe$JdNdbjutgDkdAhmV;+O9MFWQfjuTs?M*nRp!hmLky9>h|< zMC@qCrWzHfNFsB8(upjrPsx#&`o=s}VOHYN(wUm1hCfnS<*XmAy!>H+q6WmIF zs()`K9yl;yo@puq>B0t}Bz{OJwPhEB5L8~3j{&$&Cs1QNwkfubo4FU+SLCbG*&5=W&)lPBH2?hF$al&Eij!=m<`=9es!cTfCXT1)j;G#}YcVx28&(WHtK;KO? z_n%6iax?`~z1L0IcEk!c$PJc}zLaIkaLD*)3WxC`lIs%0w%F?7J4f>8dVbfT@pv$` zdt>j~TQ2%|c($trBD9RA@;dJqVsI-00m{Cp5^t}pi4pRXhBF>npBndcBrJ-%&R2_t z=-dAuE`$c6Jh8Vv0>0#v5}W7#+_|Wx;-3mvcJqq!uT5CoQ!eH>@PK{B%M6DS)|T@H zX49~85Ii`h#iyECT2nza(@cvcOP<@n_;uA1{nFO}%ytCJ?&j`SLA{JjMH52^WT zKUnsa6<5#RU>;d~rzGf>`G}>liVcB^J^m2s=d^pDRqc-c4kM_6Yg4@UXV9)zVAWGT z6@FT8t`^THQH%b!u(2dYfXvWIw3~=GgB9{&qUNUtMQGRDuCT&^AkKpVwDr>9q$WRP`|&%^AcDHKe(@)(6>o8D?f7(>~&;ekaFEtCx)N z1X=6#t!GpYII=X-Ke*@ecm^C|e91a~Hf|AaH8awPG;t5at+Yk>1n(T^hBx~4&!B4* zkg~^nQ0}I`L}B{!kw`4kD5o5G*eyGRi6aNdCvJ9p+3d3dQ;6+KxcI_uO8P`Oe2I@S zaQ#==U%q(l-(8}k#U5RnVpq;r$U zCJngSeLfb#eOoxse^iEzOHb#<2rdpy|M{Hh{ar-pLp&}{-*Z_CT`06mmje?HyN&|M zIl4o}i3H*ttznfnL=Qsl68_N+Ckk@Jx(o!9USn@By5~6ko!IfUJ*%Oj)gZua5u^W- zFDiNNiDP8z^;Z>(GPV2y-gv;He_ru=_o+6m55;ygRWe0F z?lG_n6wmUXa#1)_LIGgqj8W!wK49#Y9)bK76swY1<8;Ic0 zsUfS;c(-4nT;-#W=+E(hfekq2$^YWZQrU(KT-Q3$$NpQ%^aH`wpyuZ0DDX7KJ*jVL zP1Jk7p_6zf@MfM{RDbMi+Nhvw?yy~WiudXJOgo~|y)WZmee#2u(E4h_>LGtBB*%7? z-3C7@L%d4KRb_{L(BLbzy&j6f;x}lt$1Kw?pChiwn*RMqY+WKL>*YCm2!f{M%&3LB z@kG^djd#7$vPw{;8oSTnmyRNIuNUMb(mfd~ah(~PJt{HVpC0icoZ3Z@t`(viw4ovN zM|87GcxnUyMbc(TR(qmlf?0>CPdBd$dhfu^ppeXeU;U3je*65-$jF2Z@538VYq#>L z7!rMkU``J?N0Yv8eSDwA&6LcGNpAYru8PC?*d^rpIYuF0cVYjp2nF&Q3mO#^gLou< zD=3w-cQOkNKI`*gb0{|{XWMI;AORuf8zIavT$HpeL%IQlY?<_TIf`cFaEHd1JTR42 zx3jAn5>yil#4(#D)K@rEA8-)4+y051PvbhFR?e=<5H4J5C&}e4X~)qSCST$d={X3n zf_p}_a+XU*8OYb~qN2_s0gzWS5AO($G2dg$1txgB(dz`GCaGMB675w`$aEe}8#>;f z0e+rCS|LigD$2mk?;ENA9{ov;Kn2R1{znZU;Vm00cO8MHyiMA)*4qk;gNwQcLxRDY{FE zjwX}_8Qv7jHI1AjN8bS_c}KhFXt5txu`RTQP?8$KmKa$KK~dL4UK}kr-R+2$sx5qd z4~TW+F=BMwee6~=R{xC@hu41VjP#3Zh8gk|F0~u2@i-6|EoJ)x25NwQ3~-9 z-vk%)kw~hqT;!6rX>T7-ykkmGx?_adA661w$!0o#Pm_cod905yy7z^C#HM-Twt4yc(%$w{_We-w1G)kz{Q!mK;cR>2F>3w_i?s8N|2u!M%lw}fB@YmQ zuC;(fwo!}Q_Bk+Awt-a|FIy4I*@{-6t^D*6Db$dFb#d{c8v{ebg*3VlcTlt>b1myWa1udYT0;1 zq%kQihoj3+B27@$;qPb&ZrV-@L!Ju}u43A1uj>?2(Ur_IPeiUBabJWT3GC@a-^XTQ zhFFM8N8#=S2i}Iu4x7Mc_%U9rP=1Vm>6d9PrV# zE*g1Jr=3}R`+~%d}?;5?SFHDl7f>AH7Zj%qg z@dx6&KO`mmXF~q42bkg=Yak!>x4$5PtL6q=lK&_LG{iYm12R3*`)3}2Nw;uva*_*X zBK~ESyH?gerp`Z=b6#LE+p3{e69pL>y=eBU8&s;|JdIQvv8>b+Irka_URx6DeCY_) zPJ&uGRnmYb7?4T=IQ9bMVQZR@hJmAda27st5ya5o9D8*+^{N zQMVW!er%wVMfKL5CmZ{+Hu!P?#ILQSBET90j;vzAxHV9Wy$0Dk_5 zGX9_G&(BCmHCvh1+y7xu1}-+(>8ecBV*v=<@!49gtt~)R)(f{^7BultDK<{Kgyp(Q8Mbq ze%fDoS=@a0dZz`q5>7RM24nc5kb~tiK$uQ#{C;#qepo>h#LR{bl9+8h9Ny$y`s?C} ztcB)y%Dg=T1;5*qBm!z!PmvlR%8#w}$BhVYk1kfd`2UG=urO+JhXQM|1w#{)hr8iS z;i#6%&cOCp(3ixU9fIZGe^m4@re;v&+XlNRSo>&Nd;eI!+Yl<^n7r5+SP0n~%QRo??y%49Kq_aX63B(aj^LEuMpNvG&Xi<*I_ zH)s`Td2||7!O{cP7J&tb*S`hiOJKVWv}~VbZf?iH%+F-0-gW{BxTpN%)83I=`BzS8 zn(oNg%=eNV<+{ymK7}j|n@p&qr`wXX&HMY$eOC8E7t(Y_jF}YaE$B{w7Q783fHPRo zvST${ko!j#;&Wr;I(=TJ>tkUM6*f+t+1+tJymd<$xSdKq)Des}6phv+^}63jy`f37 z$S|cxsI`|dSYy#a#6>(VL(7D&R1`DTcD-6qAwyQn^6f|9*RN)~+E`{kp0)=C=1nWJ zA_vb64?28h@`KtFcjz!f%>Tv{!mF}dYYSp`?cXWcnkwYwH4v)({pXLdk%>uw71+p( zBr6ttjgbS<+h~b-I@!-&#!~fdM6EN)Oh0Eg*xnEwtnzyMOWW!LBcpV8$b|Lt0!G%m z?FG+fMJ%MGC+Tac?ei+N&atBlxE8xHcfWkPk@)gwQea$TuiZ#tM{f#e(|FUp|Cvnu zRd?eIq$*eQo>nF;%l}OTqnKo-^RHqNqU1>EtX*5xYy5IU4R_ebx+-{5GQn@Bk9W>+ zk!CGW%^R$MJY;=fw(j!PIhmRrs1yIk^387#L~!6Hcy3LdPTJ&*fKa~Czpd*63Z(k$ zte;q%l|CCgyKyrJ-yyJnQA<)RK;maXf#_2n5Dllo^Zy}E8WP&%nD*WneSs8L>9jA0 z*Uns%!v6NHYs4RKnF*=sjU5S-9R6ZT-k&)rAxMHLw(V5SFZvjL-5(I)%G=r>6dc~sPE$1I?j_3lN5ATW^idiS zA5S}3UG}rgO7WM9si0KvGXTSEIG&n=ZK#I}{q5Vg*iv^ekb@pqfCp+-s}%rugoOO} zW|k8`yB1S`o$k<6%+7!kQ5o1HKsc$==OJ0ALlj$yQ&39#y z=@X1BQl;irFoqk0QJ zionarTNSVEdQD+50eANj$fLYm=#nK=Hs9$0Bj$R2^Wb=gH7W^h|IoNfUXkaSg-|$G zC^>K4Wdd@xEgetZd!0VupFr=+1LNgZI-ofI^52AHKo99MAu~3OWBBSzzU#&LFlK{CqbKUOH)~(k)@s?!+^S~|UtSAU3~{qpM1Qx&upr-kq{{I~e8 zNdXP}#)cJ`7&=)51U!s9xiD=4g{^6zvn&TR!X%ML`6R_06X8-&RFqu)x`c4H*AINo zV>N1>GrPPH!P63=siI~Pk_vu5=;6o4VM&vsieQGnN&8DFyD&C@2I+2` z{=!TbnTde!7d?gwa!OgkZlgh4fZx|R|5XlwDqgRf8r&Z$&+6&E70-qSA=Yj5_l@W&I4W72R>7_dL6G4cm~Kp-bDP{uTR6n&zKY2#9ulAyM6DX=G@5WP5jJjL5N25 zn3+%LO#el+(0FhwW|edHI7FNmwX29()CCSPB-*FSQ#`&relcCBTHobr_gUxBVU$r@ z0k^Q_-++#*7;+&Ni;tJqhk59>5-@j{=7o&P-N(%*#pvT7QABnofIl{X9rkbHMFNR; z*r4LO3&3i@tgWp%fl<%HkGFM^R{FE&nBCPQD)C4irsU2d4;uGkZ57|{Q6!lNdHGc~ zxl{&!3}FnLR=VCug{TESP~4gttUuU$e;3pI_{~_gzTG2}G^wYccj1F1q7F(30yf-y z2~%`rr55&4)+a-iVvK`Ck^+J00_?2Etn!ydEW`c;7o@s>bABEllM3ok_YhRB{<$Pf zroPU#M^TEhNqmPOrFILYAx2PtW{?ngd+Yshl}y(9eWs7GFYs;nAcOyd^}x!CCZ6FDWL_fzqSd<$A=8cZucVBzY|h8pF+URq zEB&qdLO*a>)q9V22EF2lY!#B4KIST%o=&0`;VU9R#I`8wAK$td|CEpViug0!;!mpu z>JO!388)O6zp`%Y&#rb7b6Y>$XkSTsN{yyM=q^%~8R)(QFW;4O-Ejmg;2zI+VO4>P*Sx%E7i0f_-<1epOLf~#tZi+#dq50} zH307=AOWEws$K=F+ogc-716zyL%7=BE*dT#fa@%Vlx1d(pnP{6)L`DtXxH0JhNXI) zD#!FPzVV*bN878~?An0KDU!GqMVJT`eTbe(Ayi~C1 zaeOfOV0zsvQdm%9|C2#51e;%MFPv;3o*Fgw80&86N$;Q+C>Uj)B?8?;bmxCt)pIFG z)BHAvVHJ$Xak=CE8W>GYKXLj2Q@?iE*&12SqjGXZOev*)zXRC)g726TS|*U@cb+V( znpxp;VG$yYl=8K6tQv;aR2A_B8yT*aV`|UI@j-$kQP`(T3BQjDO>Q9!2)ielDSr-# zt19vflA40;7dy_rlnJX+ri9%%i6wtklyLeHL{sUeSLl49zI?`DGuDT9f4LmU%m2ECjLKVp*6JYL*=e^na)!pHkzpC z5`I8QKVZM1ZnA5a%m6+qy?nQr3A?(1(A{xkdhLR5|-|` z?|#2~&*kuk=WyJ8YwDSqXYMkZEdNQxMLLWb3D4+^k)E!RQij@FMN=A)M#!z7?Bm72 zciI!vSQwK!mr1QH4)I>HMzRw&7BIQ)erDi&e_RAT7>$(a-n)Fv?;z#Ws{Zz%i+eK( zW0Ul_yzg9HcCXz~-wQeZamrq~iyeP3O!oRw`0a}9db6L2K+7|ofw-#Aw|VPk(}(;- z&vMskMftM#mseM9%4eK|4}i73-2=HC3o}zwKu2MF4@~_LQu_ZG+r3cG%LQ!FvH!<% z!)kN%t(T%F-HaX`9aVv*-hpxCqbgLfvKd{bJ)=XRP@)0k?a~f}agF)mtzQz51d7n#_f^K|rh$7_# zs_@2}W9z8q5S`eRr zGSRkF$vIJM`Ez}lr?2a)XD>_ed3Ap}&*Hu)XZuw3ROV^{zx`NISE}~mNYx#}A1NZB z`I-8&wfuaSW3xi2WU0Bx>MRcFs@YpNUPOeOBrLyF{lfYCr>upSoi(AwP7jL~s*2#J zDq7bv-YZp-Q2F(q=ui7w2V}Uxz0}4bTDdU+Ji$|ZdD-Jn)&q|L*JD|Wo!IJhkYVE? zb9&fCn1%l|?4Quj>m%6K34==FYgrjJ&ba#GD~$^IouDx&JLP~84SyQCF0|rLugG=_xD`C^cBVO<7O>C8 zJ9_KXrXj%3oaaCXCYP{wN`!fuT{}PR*#5U88N4@6txT^p^Vj^vohL1XYzjOEQ<)!!YBa?N@2+Ih5e!fX@8>bOQttsR0)(hOdV zm%7V+QjtNmy0qLl90XrTU-~NFN?4bZDTHFTv9`ixX9vj_u! z`99zG{)2^ks@~-Tk$Bu6Ri;8pWmnl&S?$T_FkggcH45J>J;x0>N*X?fNv0L+r0A(a z#=Zm4r914A^cPb;Kq*XeAIuOc1f#efKAEo~qWyj!J%CCP+p(UGzwPMfl(2e0FPa+S z_TOy%Ha|ZvuPv+Gz#d)yOfzwmv*fujMr!T%RqToFq}0qv&Sj9_F}&&K&E)M5$|7#R zm9+Dl^0YmXGVSEpPP|h&D$~JBXk78+4&J&ZX82%rUmky*(4uEHQR#3$~2qWrVzcO;}wuUT&;=77zKz5i*&m-MDSSEKz1cPJ+E>6<5FA6 z5+VOq;9A+zDD2~NF`UXYCfBC}Y_aT}wK^HJ$;^rbZms)KR$3esWd`LPHv`<&SA_un zZYg|i5=G>Mf!=O7=B)0W-S(S{ZCQlFZ0LPSpQ|q*a&Ju$dVq-=AzX+RHc(nG`YeT^ z<3b;g#1*fA@t+J{6mv#So$SMDXJa1T=LU}Ko`>um=K;Rw9}|YO`|#U$x^)A`0e^5` zHx3}a*n;zoc}#EP7%}n2s);%cmWGZBw&JQni)>;L{>yFV`ugX=cvNd|aQCI?lJXh# z6Ua!RKE(Q_YZ3RRKMsT!Kok&osXK}mP<*D(9~9=j6`jb~;VogZH?`mMJ?ToT?M>Ss z7@39|D{A#U12r$gHOSFi=<-NG5$xHjkh7_{d5GB*pvkWU1Zr>=P1GSvu9Qr`Dei^bGJCahp~8fRn#f+d(4RboheZQK{dNH4>)TyG^w4qE_aNcwtgB0*L3|1No{AYdm0XQbQMO1i?69Df6zWX- zP-YLNcH&_GlFVL>$rmqbI4BU#i;jCU)x7&hFO?aBI7Sxk5J>I2g~mo7TB>G2%S^P= z?<;bjef}x*qi0Jh8n32(>nWSHoVGgGh2gCH zPwDPx$nJeeBh`1M&|azloui7gap$ztPj1!4y`QaUT0Cm8nLeE~hLH`|~G!HGoXY-!Qh$jHwZjDo{;au@t+rGRuU@_1Hg zTH7&Um6_zPcyMt~#Qb;1k84TKe>p=nE>7_uPiX^JTDYBag#5f*8D839Y_U)9Dw(ll zF}+tLBs!H97zq(0^9h&78BM^AKi=?ETJgvmxXHF*J>a)xHdttug1nd zEd2)OpG_fl@zgo|Up&jd?p}KQkTH^T9w(io5|*#XFjD`gwIV!5(lV;gXG4tH7}Mgc zYJrKTzn=cym?n7Q>%cxUMv1)=4q|ORB(d0=K?bSj<8fywX3FrA| zH+{FwlBI#{@3Dtrg?4~6&~FVSaKhBd1$09%MW3k|s)dLK@OC$K(>_<%)bs>Qs*C6e zUb0Bo;ooLJG7BFrE%|a97)7)XA`e=jBm8mU$AM>in_$|KbdU8u3I&vXh(o>cgrkU; zS%N{LRo)(9cLMqFEb8IapE~X`)9jZqVYz&qEBP;@Gqc9ZVc)iR0L0(vMR>m#U}<#7MSz*73uxzj_xZaKA{Ot zI{)2%xE~F{k@VIdW~RU$ZPK`A)&;F#$)+fu`&?5JKe`##lf&|Lc>}q$rR&N#=`}L( zS3@yR0&h4aJu6Kg`L@$jH;;Fm3lg-U^Wf&f2X(^%Uw|lLOF+g#YP8&Q4)k08hAqwtz_fxDXpPf1|i(P5 zgdYcF?4d9tBnG#4lbgz@J%M?k+1?-buO9}(is{r+m`fF99>OeJ{{6Pj^yIfoXe4sN z`!7)ApddgGBXt6GId*97X+SD_r&j;wkB{&PBtA?^w_-TogGO#jP2&^Z zPGvE}{`lb)5BvnNdyzyAp~?3paF{KbTc-CMw)1)qxmfY)=GSV)_<6$@uVtg92?nhK zF1#@sc^wA@IbhI-hC5Ytduy`%Hu(1DI%H0GX!wkCTZ~qy=zscnrx$nH(Q zOt@Z622IZ)5M5gQ&kX_yj!^f#*M4(L&RY^2F64@TJMCv1-sW!uw(7ed%iQ;ri1(Y7 zsi@T$=1i6pQJ+4;RM)dQ9Fi1X2&6`b<>zna4Q+^WYBlC@zE_WR_NEK{DRpuaKk>Nh zX7cC}j+N=(Kjl>#rGc${Lh_RkO>E6@>w7x{R#(Hm``KFsM=f(qpl!0ytZ7sxT3^7> z@)P@~205C&KjmSBVoUE1_a?i;RA)r@*tEE2I|A<#wYeO%T&O4YKIs^#2NS5U_`(_g zOKJ?P2_dU2S^)L3>_SJ#ZHMo7Jq5g4O+LH6Lf1pj_4F10&}#`b2&mn712%K z)tks0uP-x2GtRHV^F;6{M_S2olK$pNydjFYcR!D-n#GQ$p;s{XOCdHLalreUG)~SG z@2Ul%bJq}LOv%@d>o%B2)GB|VP10op2&yMw>vID?oF@f~zV+cNX)CTqxa>^uUUuLZ zbW?IJy*8*Auh5`jZS2*}2}@MH|2j^TWkL4!IJqYE9#g-=^FTmv_#lx3FIFo?;-d!R z5dLVfy4$AQeS1uupZ2N-Y0E>8+>R=i`e0wjzJOlg37j+>UP1!p7VcG|%Mj+NUAnXu z?{aC8jkNm{fmc7yMiOxWX;SV87Kp)a#zCE)tRxt?nKc~1?wM`Vmnie!nPAA_frvh% zq#smEwU=yP3&&(3B`u5k<&1WCmM;xmLtWfoNB+0O=$ z@6gN%b@^>F9V9ZPAvPz(?lS?u8@&jBP`YC)XfDYt?%A{Ox;}3f?s&h5A|>vmE2xKF zEq#+m8N(}RnEaF4bfG9#jfnAMCI6h79i?*#)sHMtub#zK{(K zCQz5|J6(~49iLt18f5+F<`zz>@4fp@SmI!v`O0gV$O$ukncnVoNJcB-hrk2uKb^I5 zc+Z;cx3emyt450{pMBx0;ptbMTm{oH*Q@F{LV_YU@rgr=bjSkN#ngo!ff-54>&Z{ac0Va>q6+eI)_* zaMovGCGuX(-=3Zh9X!D8V&P=j@(nl@$-NzAE_23|n=L;UTb{+~e}9OZcz~;&=`o8Kl2c(6jSoZG{LVcX>`ZEN}jzs=Q99)VdpRIPF1GclA zn&15DQ=v-2uJ7AhP?+*%hTfg~5h$+%v@JxmQ4x;y1I5qN%|Q@(nHzL6bNjWnBDcbz zJk{BJ?z>!~Z&2csGx5&HQeUn+Olw~j;e2*5?iFUMN3ITM_fOvAH9$~B-A#ARSUp6i z@)wJAU7s%OdxiSS=wdH5KLqulLe4AYd@io#*ke$IEKZ3}EvYa?qEL6~2_UePFJo!&4*vCWFML783;Sq_?fYL4%HdbhhlM|6YiaoU zsgX70BvWb1GM~VgdU>H>X&6&D^Tawx1Ml_-+j-J#A!tVQYHkO*6Dg)II2^ZES!(wp z%4f9-{42cAUxw`BwZB4?h!M z(+hCs;U3zt*;UWDH2Y!2=OovuHF-SuI8SpKUCfCVlI%=w(v3paP+q1atn1>3hGn+3(~~5 zf~e!G$Cf=$#8iWNdma;FOB1t3O)@Oc719_u~`qzW0ac;-) z(sm(>iUs$PD91OZ?}FV0D$WnP`O+KWiMH?Ps1ihCBC8JnX1U&=KuOx|6`(pl%~tjl zQ6DT(detZJ|4yiCE;+-blFG2k^c+#OZNZzW>WaoBs2@Ij=*!2)_li|6%1GQcKju66Qj6DO+CL@k#V2a$h0cbuqxdd=*GixWS4=Q1fm zoMccjQ3d61t+vW7l0}w06nF8sq5k~NU&Z@wU)<#9>++;z*Xi7!$vPf4fI;738!7z| zc35E3jQfb(?c|NkE&elR5^~$i@c<@{ze`_VEDw&@m|qjoB9{XfTmdt(LHTh%y1ZZ* z*p*?IR+A~(DYA4v`=CJWkx2;4S|o|At5H_yBA4qe%23E?)4t1l0o}sm_kH2 zR^Q-TNE|NPa2AR`{E7AfyS8(1p4I%sMpWu1{$)qi3}I!^T0Iw%4|KQY2DwBU3^t&A zQ38H{$D|^&pEMJVNTu1$iGF-yR)9aNcy+N?4QMP=P`80Bd#|2ke|fZvdW>-OBnHrfr=Gydo}mNvOjt^OfdK zy=?!TDx9;6?6E}V?kAyOFqWDcFwO|HKQc8qIN0I0`};9DpndahPf1}`j9UILw4-5; zfUX|GfYUDKx)8Vzs1Nx8{CE!>W5no^fRaLe#gm+)(j6&zU=E!&6x_rb7vmNh+{F1}ce)L|% zV8^vl`(OMSbz6Ub2R2mq7Yn?()0R|{dj__u=9~~XQ#mJs^VR;|(Xjh-rc(JK>*XZn z?vzy`NlClT9$hL!*XIPdG>`2?m(FP8lGAx2aC=&px!95j_8=euYYx@)D_P#ot-#OXNdn79RWN8uU*7 z^?ny0TJ6vvgZc+1(Y@jYE+8|7hOl9=jun{BbGx|k@lLP%yf5bM$-F0o2?q-0e3SMx zHl2fkYG*oLtR^|t%!e=ii_7>q<&NR!F)=X?upLG)BY5i-BONJkH#S6@a==DNdS_=x_5_41Z6^AY3E!-17>}h$6!6$F z8r~1l-*;7oHFwy5^)W znP~BT?aW9UZfb71_?j4Xxi79q>1NtoBp%GizxRurU8AEv!X$~>DFyTgSd4xjPWw)5~AL%e)5P@Pfk0{r-z&1b1BT>XFmNBR-#rO zZ+mZQ>C{2G<&0(0L9?HjosG!Rzv}gyUrxDtmjx^#X|`WauXE6p^S@SCSH(mxn_}#3 z)gVy)_XBH~pj5#9?~f1EN&TxI#M(C1H7BR0Zp9W)z1cBiek~kyYUyLBn_SR-p}Kvi zU?W@hl+2QOlt{6Ykgdp1daup}XS1S{y2GF;Kir&8=0vOm)phgx9pwsl!pJe}iLjI3 zHa=EBCg@sG6>KwIZtQ{k^jrjrm7A6--BxNf%5@2c24eVCK9sxd>-rtwlFEFTS*ykO z_0U2Sp%*o)@D%&2LMH=HJ02lECQbux+3X)rvmeiCzIW+CI9Io%j}MNg(qn#^>-8+w z@?gtDapm`F+S}VT)ipGboj*m1N%cwCAu))=0*t zkqnvp?VoVhXt1|={LoAK36T=v z#<=h@Y{4xc;F8}~=mSdsbt!@?V(EZJX)+2QQovP=;L_8NL|yi+AA&Bqu0sT(-5+$N zEZV<*eZ$yeO$wel<_50nWBXaUgfCRTwv7zuY}axNe}FMs!>!Q%Q!3ZW2RP%f1U77s2xnKqeiP!-In*}^>C zf%}b2TxQ8v;h`HQyqluR%AH(bP`*lDj>G5_lvANh3~!lPpZ|Ahj#)eR;^&@uYUxp2mTYMT zv9lik?M@}3a40*pVQgNk@$4)){t z^^yUDBClA9p+B$T#ax?IuVZ;r$4r9mPOn@&H)7Ai&`>M@*qQ_j(2)@dvZ%@ZHUo2e z^bZicKx!xR-e87;YY$pkS?K^%V0v$^FGIRcuYCcy?I{eI7a3msO<#Y5tTxo{3us$> z^{IY^4M9=QOh-p2AS!xtur`>UoB8phbh1V%#>CC>8+*x@U>{&a0xmOlmw=LrO7HE! z`eANdk9EhvQaDNQw7}am&^;kQG&p`ICT4fCP51)E#!g}xV(Jyrr;`c9kxy$`>k43RP^SpL#AVm zQACMDR#2IeUNINe)+FZD(C*GlmSp>DJmx$E*VgtnbA(%!MaRv0)%MN)($Z4iig+2( zO>9|%AckC>@&@v*`uGxzfzJ$il$3JtU*W|pD3J;d0?Z!K&x^^VMz}Q@nUEi{K0esK zkGHUM-uyy(eA1uZcjZ-Y`-ffwUg_Jc>$6nErcqe~KC@*eyPkdzNXa*Y(PV1AzNgeu zBQI@33V36H#PL3v@-9OVOnD0m2<*|CO?CL)UZ36~3OG8n4GkN64_iH0;4ux=L%U-g z_r?SdFOGF#;g*2vnf*Sudf)`Nh<|`f@)}5SXeN1 zbwx4CT5nH|AR`Icm!Lx&w3iL9r4|l(W`&CUV@<;ZgY1u>@*r z`>{*9TuA+Caay%*ec(U6Ibrqi5(feGFD)%Cmk&VmSoL{(?jLAK!A>N4i3&ha5pM*D z09&_U%HWy|WkTR7_7_9M*;?&0zt-pHmZNVagf;NY*J@Zo7ns;?ND#Jz#QsNb2C)(xuG3sk-Ie z)$H=rr8A$Laf9r_4pKxu6Vu`YgbxByP*uf;f-QV$YiqL@T0gY=`}Z#-uT{#e(o{3n z-bDaBK;lmbg27q{1RMlj_ue+}tJjc7WcAK`%jr~BR@P$D(5smyHsd?a&ego}xJ<@e znd^(Aqi`_b*u%tRj!``RP=_luET#7Mil)*WIeNu<6Lq594?j+G z{Qkt={+yUNA_v;a4TwwuhqI-w{-+8T?RSXt>4pMF8e+M+nF#1-9ztSb;-|$r{61b@ zt$G|3?ZPrLGNdlaPg$YZ7+nIte<-pQsDHzTkn+ACdR1LtUw{1z)Z=teB=p|yv0%b_x#Lfr?Ur)X&Wy|5EcK>+?7WmYoSumk8QKuk3A7j%C=^7Y#H3*cQrlz{O zEq7!2$u~|?7b5VOH*i0wf#1K@K|$Bbi>Hi_h*o5GK+sW2sSL9Z4rWlmyUSdZ>BUcM zDVkkwLh<7;s9=XhVrpey+XLFY}9WHL8-XUu>KA6;{B??|(r{UTm{> z3s?hL-op`y@lCG_XDD`v1SqrnsK$D++x8FTWYwY7*)) zWMX{$@Y-3FP<`BQmwb#D=qaW0H~!JP*841Xa3rK%+rGat2Id-quuVMBn4Q zP70nD1r9L9-t(VJOV`VTf`a%_iygPoXvVo{h{lsboaw!idL8c2>sQ%_S=Y0Am+RSu z7-hed2hYI}2LYmdgrooK*Sjx)uWUi4U{jQm+9-JTuwbqW{pk&05ExAqUKo?CxcFJu z#>U1u5Q5WODcHe?p8jOF0ND4x$U=P=pFP#NJK${M3f}udEay&+^Yil;@6po-3gU%1 zwWHD-$A#a8%975LqF+c39MNBePh?5~&Ib-!78Yy8^F%eSlV_rlz*tqUY23`NvmhRU)SKSDpLOl^J2C@Ur+MKui#%ud>o!(rCs z{&iHx?ak>9nBIF0fb!fGaW(AU%(%NsPI0l2Sv3W`jFQ->C|uA86Vw4dInui3Y^t!Z zFul;F0Hw=PP>;Uz%1`z&l`eCMI)5&Xw?S@SDtYMDh>g&P4o&uC*$&W9^6+`_&rpSY zEj}KjK^o8+%g2ssU-*=hxmbyZN|EKg99OEpt%t`!s+Yv|hyDHinhp9o9>R9iVM}Dp zFC6q)R5{N@K`}q+5q#3yN(FsK?SEhk4rWs!Ffc+11vH(UOl~yP`Z+6BOqr-4soXZ3 zMB|@*aga*^b*v-BI-gMS2?-(A9v(tiJa6+Jsgn;9UXzkmih#?vpVvRJ2D{6zuRm6h zVz?!NFiZ*WG=lEf65B90B|yq z5l{;h3Uv#5ODew1^s4+%an^ByHL`1pkNL)h)F045e=S@a!7m79L= zJCv$xXt>MyMu-CY>pJ>{=>pn`Yi#&~eLqwDFvu9$Q|B1+u_r4N5Tuy`9&k$L8c4B2 zQOMnYz$?wIYitbp?w);hu+v<`Na})%J_TKlH<8ZuV+XF=s>7xcv zI~uH`3j1BlgU1YXcJw;gScxSI+<@#OHL|K(SUrN!_VSkfqeci4NV_T&`;)zJR*FY4 zN}8pBxCcBA7cOP(JO|@uZEQ$8YJ`M@q(eYZa106DZrudgo3q|piSP083 zMeB){(s-Q*|IME*u@KnWK&lS;B)m;2mXYBHsV({ zHmf~=)Zlr)&A}cRV!2oW#xnz7(X&pyu7Zqo?1|Mo_zo|@mbX~~;#78SZtk+UzrUX_ zekG+qp&`mjPCLcJBP*ti4wz3R{b%FC;IyW^fidc>o%xrsjRb>b&i{a>xCJvq!&C06 z?cIGT5_zce$uilPXwN5%xi|!(th`(iioMQf-g>5%uhsM_^KUsQB-PNX{$9(ofi$nO zP*l=W2d+v%rr@JTSdjHd=sKW2YuH96pliN(Nu>5Vqd}2}>6Gn*>=;oTatxF76f$I0 zAuw#9*hA9A#U%t>-6802Q50(-A=MVK1}4by$;4++R*nh(0vSeDT{?56O0>M}jrWx~ z8da3F;Gxn6jPuT#N_E=e(w5s$h0aqzT?4)jyU3^=@s9dN%(K>lzy1 z<5g`}0<>cuXa$RmtgHZ-wKZnvcKP(@GDgx#7Vx;rN;Z*qIoUwQ3ZNu1Sx;GI?qPqG z=@yXr{qyI6Mz;V1L6~Lfig*mcBT`Zq)_8(4b8Y4s`_~->9LwLnee(tFE}k_;|8C9N z{Jp`XIy#6Y4Q^2i(N%0&3EGyKgdq^X&1gq~?_bHZebGT1wahoyu(*ueji$SIZ?P5R z$i7?G;Ef#SaS+`5qg1z?K~#44|4K!^16|-^hXlAT;AkwKoSfV|%hv)M!s~{n*}C`8 zvdjvhFOFAPU;o>Rdx|O>kR45|Gf7AYnj?jA@r&Ka9gJ3y4{9=e#Y#}@Jt-0h`YZM` zh^CFSp{_KzN&T#<#X7-Yd$Cb?+pNWLr2 zRq^4av$Hc4h!$G0Eqm&19_ze~X+WR9{c{}(+n(jj?(Xhppj|a#Ij45&>gpufKr5Y6 zDxbgT78s~(Wx`{Mup|fp7wsgy?!fbCaC9cuKAzLi1zGQ~s_n;vwe9Xe6*3;3oa6wzy6(|z3c~E> zKOH3wYm^o-F%6R7mSfTc)yZ{KL5&p%{UMzn^tim)@xp9qqDHvicx(WuU2_-xRlpHk zJWc=cn2iO2TmDWD(eq7CxI>l!425b6v2t_Uv*t^eycgHgE+Q&w@MHZDLiP8$*IF>b zDn@|lv2|Qb874S1C2peyOQLRQI={s*))^tFBo7pP|FpHRxOhzkh#cc-&~4c|%#A+! zxeBSae#;9vQ}ro6W^A?b2$#80@B2O0HLjMHOLyKBc$FhA(t$yAxVsJ*aL_&q>3k%& zVbu}l#>d>;kP`5LN+4U;d(tQD&|xr*@8BnQi9=et>za3R%*HNSYj}}RD2E~tPz5>t$pg9xpa;?6q3R5O zgx>}Dwo5eKRu=HrAG*xFj_t9g`>bWsd3t@&4xUZYK+bs?*R3_bY?r{%!TD94L*~du z`)wdVU@h{sGQ^0mtZ2F#T>Nqj?_R*(v9ch2(Kl52o`Y6vX35Mnn5zjixdHyt4m#|w zDvYqz_YZ-O?}gOjpx4(wmImu=JPqn-6=o*Yu6$D!9%aQtD@Yl|Y&zAJt;U|KUOFyo z@8Ys^M*s@~f{NOT18+TjeRIOtm|ryLd)fwmz`^f{p0zRs$mZ9tU#o%BdegQJ+XumN zH%K%2tKseI%0pZEGw0|;QXc-%$fVlYb3|_q1Mp{Vt?JcbUzfJlDSE>fr8xI~LMR3v! zcfgCQd%;z01-vT(qUi>Z)OmC`0-`jFNnsZS)yJSOyTsMUf`w()2)zZDf&qPilD$pB zcmY}bx4nJ&iYwJ|FX$G&4!gW}@174TV5bjwxS*7o)r zBAjbZV=Q*o#^oN!E(j0B zn(~Q>0buM+O6{P)TRvu`OYqgL>#((mVk;VeXymM?ri-$%6l{Uq45>D8%p0d3Yr&_S z+8h*aYXg)Jdy6Z1(X9DR*g^*%Ra`>v%6>G6W9C_krC5vKKI7EpN=;LvwN!xHU%tV- zv7Z_9lVVU;;i8QalmXWT^v}t>63hOzmxQ#au<%4pAY+XYQIr)}sk@OZsrEa7)5h4x z0a(Nn#51&zr>taJ`p5E|Yns64msi%V08jk7-$sZQTV73@;;fxy$~&DEGlPO@Hj@i0 z*lfyb&L_p_2O;w-(2=Iwjv_%IF2aGOodr8|0$yTcy(S9_;{JcHLq1Xgqq_ZKivvEw zbF+kIvKXJ3sH-;Ok>Mhs^WIZ-;v>k!6R{E5k^O_cQ=4aBQOhraHB6Z;-HYy z*B=2=`vstISkltcw`C5Qr3gnDWzy5Nb}q*7m>9?Z6u1dGeNx<5@IC0Lx`2Zj?gs2{ zDwxR*%y$Xuk`d@QNzIQE$MSOxCZ6n4rYUnzBXHp{RSRK}u}x=VqOR-#Ut zA(a+I4`JAnKVN#w!mnQ^DOKABG*Ka|m%&$~`HCq2v*l<;ZmKxVr<|G`Z*>ze6u08BXpI|A&%qbMsINMCdFV-iRhlN=QLe+s=0 zzTq#Yq-65n&gX#L5+EwPx@|{wyVI8;wp2NzrAHK9U0qK%Htfxy*h5w{?fEq|W8$!b zRR`v8Ss=FJhMPfN(@;WjD3!d{DU@FbelU0+7@)2{IoRN0helF&mY0`F)yJ!xGSd#E zM*7ze1=KzJJpwaJU7yZLR2jheSj&bnEpPOF&-t+fPoM@iF7IW4sZ^@Zi3Nq0lAL;!2 zPoFp;8Aewa5HDX}Sy>sGg@4H1WhT74B4T3q&`>J#;;Bhd$~(wiEcl8zcxk#g_H)TP z3c!80t{E6nY;>7B0Hvkt;K)auGTAYXXrkVGn2CY8tdHH3&C>P{m6eprt$=jK?DnZj zr+IpMo`cX*3T_3l0Dz90*=f4~QC4w^WVb+|j=uiomyf1g;1_z?1+uEde(eto;V=OhqR5)NqzsWfDp{ z)`r#!F}oiSh?q3;WSuT|dLkmCAD9_tzgO>cDlpZ7=!2C7;<|p320eiz3+R*^ z7&r&ygb&T8c;Cjyo8xtBal9$O@YB$rWF8m(0=T`LT7s`H-dqY36=uunQsA_szHrF` zc!m&deeAoHlyM7}RJAE0tnhl>M`vcvNhk~PZeaVUeROBcpw6ch$Y=7-WXK5~vuU@$ z9}wB72&?OM3mjogC|@20h4ipQq-L;AFTAE&x)-JW*i=?WPoMHlK?bm5;h<0ui9mQm zBrvW3fJFjuAob&;qbprumRIM&yn|e~At~RiLuG)meTr76V^J^KTVEWtpd-$5sfic* z`XD4Ex_g)E&Ye3Q#x-$v1r}arV(5%6^IXHrs_j>hD>8U=B(w*Z2Y`jOL_pmkG7w6* z0gMM+lti25xuEV&)5pjAZwi>G;wrsBR|{fUT*C*TN~YQrSa66NM&~KmjJH`eYIF0v z9K6M`$rD$!ZstGzqw?(2#_5JdjtNl4V<1>!DL{OdVnXUOQDcXO0Cq<^?!P7>FH{|{ z+)j7qrrZE4fv;q|r=IGEuL^s&8W7hM9fXc5@IZT3_WkB7ql{I$YiKL<8|}{mZCM=H z&;gq^h?&lNF6|*zm}j`~O+Tr2;O`#IU0+{Y+_B_qM-{Incyp(&KMD^528RlLLfZPs zk8n;di6#z6;vINRO$~@cj%}GEFqvkT93aOEc*=OBQlJ7NU|}yxthLuYyAARa;sf!c zhO1Ed@G1hRXd79)eZd>vzTp?nWZ7j&tc1~4wJk=x5jtfNz{w^xa#)-)Mjv>;vk{{e zvLwT4%R&_g$R9^KCT3@6S1x7hB2@!mkjD@poH$XAt zS0L4QZXsG|J#utuB`xo>MM9;xqxx+5rl$= zl~4ogtY$6Y69<8?dW+S~=|`E;O0E4i58Id)%C-(}Ry}$Mzjgll0+qryoD!&|Ro*YK*w&69|3ZGy>PN3rJ-=HOL*5fs0Am0gv z3eI%F@;ozDSWnG>3&>4{gxJ{Fq@yl}|K!p`S|Id2T~}^3O`5<%aN8KoKb1OL3U}qD ziu*#@q7C`+lDTXL&h?x%b9MD5OZ>X3l~#4gBs`P(`=I_9D-pZ|S_<%aR?vw%NSY$W^$R~~wn zW-CH3;k5z&H}DlxQ|bpX&*FMNhwPuNLo%G|Tp=JQf-G_P4v_3qfP8;;$SmKH)?(SAff#U833KVyH>5>0E@2zw0 zz0a~%k}qFo&&>B5*|RN5MM(w)kpK|_0s`futfU(FJMhmN{vG&pP#7=){z7n+)pdb@ zK*IX-hJ?uaj0e7_WUZm&s-vjDZw9hw2AG3Pfy|!vj$mpC2tg4~M}V0v(3R8_Xld;r zM1I!UOHOKSE<~=)rO2x2C;_yxmi2Z9s(UMGn0ecp@tKp02$KqW@`D-J16={6p7wSQ zF8rQC`ZLj{H&b(Y&@j@c#(spIh$MXt4T`#Ll*d#5V@7Bt0O-Pi-(5? zvj+z=$k~#GjgOCyg_WI!ot+6x!Q|rQ;0o|$a&V#eCkIKOiE9p0-tjNC4le(I2@DvEC%}<~jhXe&BmIrg-0UwpM>l7?zX>-tV*%O$?ST%i zE?`==zi7cIDJuR&{%_gZ+y6!F;wt41*61J4_HU_OG`t*vENVa(kejm^P|6)Fk>a0< zxwxtU{|lP`MRPFuUyU8DK&~JcE6{%c;jh2{g2Its!Wjr~1vzVgKz9GMvdTZXkg~He zvy;+lTRWJ8JY4AhsQ8ZtKuLfrP>39Ca!w{zUM4mk4R%(3F0jct7+Bf)Sy}&1st9%> zbAT)0KM-@U{-20J=GGQo|0Ain8NUU{*&YCf*xDXo31o3}u>6}eMMeIP4lb?$2Q%PD zNg;Bus?65b=KMSsrd;go0COfz3v(VO3m#53CR0{3GbSzzQy|#ecsVT0&Hve65@hD~ z$NYcV|2-1SL1thcf8oXtc64(dHZCqEUULq1CLoZLhlv-+&cnpP#m&oR!NtXA$_@A@ zH)UsQa4-Vw{*~(=t<1q3d0ANj>})_FlPNog1rsZPlbeYT09K8akBf`Vl!uQGz{N&R z`d@M-Kz1N!MUXidbB=$#|4~9h#TjH_Z3q6tMNLMW^rMsnHya-}HxoNE+n<>EoA2_= zTDyR4?DdZ*R0lf!0=^Q9|Tqf4l(*2ITLzt^W<6|2x(H-rmCs2xj`< zc*)<%TtF7C9sp;cm?aqe|IrIs{*&@90Qdi1bui`tpqaTT6CWQq!2r2AxtIU|4jv{T z7Y_#y2NxH>g4g1okNzK2|1Y2Yf1>(7U^TM>I9LL~>6wN6KR%2Z$iW@x{4cg}1ULi0 zSsCc;B1CTC46-K$I6B%{n*sh9j>X-<{2$5gFP|ZG1(E(&g#1T2=0Iobe}U~^rTnD= z>HmlF{zoJI?*jI}^{@XwjKuP%xA}YIu>4hHfL|Bj^eAKUybi}U}x@cG~RAULh_ zgR{?H`RM;%=J>mO(Er;E^v7%dawz_PCNeQ_iv5#MT>cS=;sDFPLQ?qO=S^O7UQP}m zml-(J%y^jCfSdp(J_}P*aPle$!fPef|Bmo`3q2~-PfdmoCupuBmqI{GT)A0O!n&Z(%CF8agptoA< zT}+n%$gYEF!fN89$XYkC@{W*|yYsf8g^hZ}s4rvKLBwdYI z246Y0Tbv>PmZ!5lLwF?&7b{UCBZ%GUV2<0J`wfR4D;+g#>ZkS<<-*ffLh!>TddzS* zd7^9{P}fkDKU2XyvlM?uuhU-$dC{#ZOdOzPo6~CV)1p_cq(z@PV_NUoWx!0LE=SCg zK9Rid+Ta2RvDV z+QRP~By@3;+{RX+5S0oIn}c!l29D%$gDhD}X895|T*~C}E3^8gG%HR54&|9YV{DGaKQBHDw2wXWT5DxTZL8?Shz1`-jqmBTid;Cy^b#}_&Q-?|EIyCP+6oG zb-_y=D&h-+3&f~XTFTMUQF~KW)o~axF)@om*=+jg?!_nTEZUMfd%{Jx89kPV1@$14 zYy9vapsp;P6H$b7`{#*$XS8pVlk40(Ja7mIHLi08daLk?Q2+#jg2B185#GtCnqPp zv?VtC17pv8kemic5*eB_$>XmM*^@>Vy6@+0Qf-<$>|8~XBSG)P;R|PI z(|~|g?skLUmPJKH#`uJUPZ8n;tf&)w15*g3t|FiAb8~a$!y_U#*;!f7vpfZJ>+G|^ z=1{ZI6?$#P*hC%}QlU?kGcz+Y7V>+%E-Ni7BY}zU43i>yrlE$Y=7Ov`IX?DTSZTHm z@NjfI*DSSGV|;I$<=@Evq7PTil?KF!9|fawadB~$%j>jLOHD&_7Rpc77oY|OkG{FS z?zcP=OSs9!$$2eD1~Y8x6qUB2lO6!dZ|r|5b+36Q<(R_~zgH-RkV?tf*CNsN?0{ z_!J4h>!I@H!?SfcL(E$N0;yGB8k7D;4{#z4ZOF7)qZ)b3-29;AzY(89VSfCP> zm)WY1voFB}Pxu#XK5QJ6HbU1g1O+T|>LH3O9StllF+@KlcnRBB03K!XM=(Yg=i7q; z&gWbG@xd}M{SXgmQV?QA`~m_jYu!F~Wsi@KHT3UeG^*yyB0XcPNR8(h8O4X+P5f}R z$nTY(-9+#sj_y%Xv{XxsvMt0=Brim_n%+?TB3=T8MAdI##M(@n`I#p}hc)9fPo^Pf z#ZquNEb&Jv`Wm(p)caxGpapOoh?6Ip*x3B)8U5gMQtjg6g7(h-4NU?9^0L)_^)_O) z#s0~Kj_Sd;=Tu;IL&8jewKG4%I8;ZY(LuJ(g~HCbY?%`#8xChsD9c<=Od+I1ly(|1 z1!_@7c%?J^^1JF5eK`~r?H-I-V-f`xwaf@3>gTw+H;oi5byYc>5IeP*ROhG+OJ!s| zTeS*oIXYBg{_D}FyR$clI!T&)kB<=jUV1EN)4E&{;1&w)`thy@9HcLqQznT(Wle>^s#)GH$|?5!}LMT z>MPFVSh+kW)Zz)7*YXvUk!#6E)9Q2S+b#nMkVW`qFY0u6#zs3xkN|Iyb`d*HU2s&={8H@Yf zUKsSxibM3j#V~gq&SrDlKTf9V_@BQ*6bDvT^ZI`!v*~x6in+{6mulZ}#!7-JCAuy= zU;3bT}{a!%KWGgGJF=U1%qK1bgAj3pZQ%uw5P=_4T_!7{$@%yjy=)qUe~ModwJ z>MnJ$gFEcGTT9t?QVA~$Yld2hDgJ1aF#oA~*5EhGBGL+mb~Zn4LO65|uy21`rqTEH zmdxQ%m*ofcm$M4bv;+DM;d4UsMqOzc8BqJ(hEFXT_AeL6gA|AqwTx2y?wx3c--l;s zz0LptVA$iG2_CD!nX}E@&#&_2T{nu>xdDc261Q}4`?Tm)qIML)P(!fMIp>=ce&UD} z^RNcE2C-TjGijtNzmUIDz-)_qU^M;w{?z%9P4Z>846YOJq~Yk}a$6NPlAbL;KYvDU zuK#&W=Wp*gwaT_iIPE|eX$UzwaHsjaVq4qIl05!7Z(?6-_%aNlnlmC!`$%>`e_>3 z!I=W1!TEJwu~(adb6$l<&`?Yc&O4^g>fEEXW*g*R7nJFoQvFh*sSl3OhGj+$4i4ot zzq|GH&YGu`sIAZFfh3A%LI-?P54!w0!XPInC(SV)N=C*GA`#u~utn7FiD9;k zdey7Ai2M0Zg8Q}yswldrWZlK(3RX&=IzE36oS~aM0Ke<_E}q$_^-ggWggtY1yVWQY>y)9KIfr37_8f zSQ5dR(T-e1Vkb+@HBto_k9D`j4<#DbT=%vSF=Z!<*y4^i^V*w>tE)Z_HZnbczOf_v z)TD{CH+S6Lt6OGP(Ri4vXM1UH$IW?Jd5P9=Jl5^Yd&u;>QxC7_1dBHWyqsVIBb8=Y zf;xpDp!bj^0TdnN_Cpxaq6_RQGjE(|QIuYmof_NRX`S2H&u0`I=4FGQ&#_d84F$;G z2+M64x(mfxrx6RpuCYa}h5dG^g`c~tGca^E4M#LBC1K+KK#MDcQ)QwF1EvZZ@EFF? zem1ePI_wAw3&TkySrc|F9&)Vhml5V2)0|QsG{9-zVzj(dgcofiNSv8wFxl8o?B@0m zJajx8jHZdVvcMITv{Vl^H$bls6`6tFhHpzv)tmNoK@4j!DIl#g6w_V-9CRS2gBUKm zt}YFl8X@T5EU5Tdglbe~p$Q^Mt>UW^qv%~zz0QL^Y&?`8@p9*xkBVZ{YHMq?3Gnmd z5fXO3GpU|uWp1ZIJ6D$WmUxR+hWESJ8=PuzO)3uxJ{7AOjXxO<8Oj~W=8H6T$Fg5bE6 zLYiat)W)`lS5yZd$`F0dc26_N8Et87lqN!(bR9XE{24DK3^&gioZYsV;OtyHQeu~= z8c`TMTcT0G=}I%p=bW7GRux(zJ`NrqLMW@7ejUaIuSu@O6F0*}JDyaejWL!~15^@* zi-=y1zBliwceH%G$=n0j&EL_*Ea7{V4HEn?y-|MO8|AjXjT0F^YvGfja920WQc;SJ zrR7|?lJ;qjy*)!5GnQJT!!7%>v2hTV=?oQ#nsl}b; zte_N?3aeXU`pcTlql3FwD++A8&wOxz&{gb);8bZjbVA1HS=2NCDde-KL#QjS{uV_H zHH4@I1_c}uvq{} zI{3W|42bLREn@dbNco@j-du`oHh#E71&{gg20-%NSTKQA3HZ?O82ujyr8cf_5geYZ z!zPc5uHEsuPVYCVF9$d8hEy$oP^C_n^!*S+q7jTr_4eBJ|vl7?4jEYyd{O+Nw?hYfsEyebU zR1QRib(JaMFF;Jk%Lz4sj_khjiCOag(a;YM7{b7uDJGdhgIT;8&o!Peq+~(}zqtP^ z#xf(X>3%y1@{t2)1tl;L3mFAP$ivihheW>Qhh&xW_7x5SspxI4kk9Yv(On_%=*`|T z``w9v!H#Y^^?g9zIx^ztOq0EscY@y3x%JFYz~R+R_bg>!{~$NpBIIkG9=f>?^;0lA z+C{27KJUm>^rlnQB+1W}(XLsY*~d|Er$`0p52YH#=t8L^O=yiAw<+U^zn^bae0ZG2 zaU~>Ikpt+y2ao=G9e0MJ;w5rUd7^_?f>-r`t9{_1OF+Q!LUpa-_^c&CFjiUBwTFDq z>%*j^0Mlo-PT@=F_xN=I)ocrrIz+XXSv+x$vpG`VH!)~FB!8h!*g2AZZQo*qiIHp`ow>qJ}S_*J%DctE#>-M&xZb2f~n5?$k3l1r@L#{W29v+A3KsXn;eW-$Y8+Z zyqcOC=GB&}AnT<{BZv{XFCy<_+)kGp4`jgqxvM9tA-;c4{L_Wo_~G7rAt!HyPxm76 zEdF_=RnRh$jmXTs_#1<<-zOQg6@xks)$}6!tt0b?g8|U=&Lli!2r+m3XE~?iDet!J zE?u?32Yw9CVI5OUTr&3jwhmA$-vhW`xk@r&q{0c?Bd)StXuAgp9=6eD1iPCZlk=u zUjcWwZM9@?$92z^((&k4OiSIRL-os)wf9#O8G@POsJLVqaMNy66$jn#SrJxQzFqYn zlWY&!AzOweK&-YzNs^x|)$g7Kmh$V{V>ry(Ji439$c)xB+b+Id%3*Yclks|f8IsS< z-a!G81U~#yJCNyq$^AwWos?I8$xtgWu&s_N&^n?j7uqeZxP2r#%~35%4pWnKf=ADm z7Jp$&(*0>o5{gen(hC0<)OMK6$AGnO1GPpT(b~U1Y;Q>-O3W>yGQNe9=1ny^taliL zN9$eXhD4=ymHm%%_4cbRQ!tDrII@tlF5JY{<8Yf?FoDS~7?9RsQB(7HFBq=l`!{bd z@NhDA?)HjxSqRl-!IUNW29T>2vr8Ot_vr9sjw#?iCu)%6J!km?)t(-J)U= z_pLS+O}+oMadTbDz>pQTzP>&P9+!%IKe(cp{7SIxO1nK1u;F#!$Caa0TdScLFq9B% zRz`2&1^6WL)X1fxQf?Eu9-5n+Ch9tJ|4l~ea%M`=ssH<4B~A|qB$;5Sc4d}OCk~In z?&UGh((_2IR=Y*#L{7XXgYOrha1=*`)|gQ80cY@rYpVX;U4qkA0|N~PEW%fpR3D)| zMN1?U4Z!G6sSkWf`}G&79m(@)h=96b4mE7O>kbxEWPP_I=()-WdM!$}(<7mIZ+|+07I2_YU~2nS zb24J%!Y&$lIID9d-`3~yavm?nA z!5CW?Lxc`1dMuUw94vPn|FYV^}%((U-BQc4D@Q1D@SU%2Mtkt~N*J!WFrrDpwf& zT3v@GBZwH6@iI$3-o=%Fdl+ZBYbtnb2ZfOP*}Cy|jP$br@6Lti-PYDtRrIi_JPKC6 zibiqrxj^p%Ld{4sAvG@q=U6#}`ejfr@9_4n<^rmWE?vVzg{!L|qM7>nk{D#EXiNfJ z1N{>J7gt| zv^|Go*zLNg!#*@0>%INZBJ-wL^SNRSc+25)w~G_09)pY2VQqwnC+?5I`O=Gzn3S`M zZD>8*t=yM5Tc&;-1iXfI9% zF{CIesJXVyw6d<&ua~V1zIQatxFC{OwMtl`{S2# z6Z_gxuJ6r)DQdP2Jg?^1t@sgOUSgZ5FqAFbWC^+R{;0R}yn1Knt~K>}4qskH4(5>05Fz8Zec&G5&OtA)G!?X7r$BYE9x z8td=Z*H@l!4h>GNIiJK4&1hqPRRU8eAP@cz0`u077v@pQ0e)%Q{bSK`i}Lb+IGzRp`KRyoPqFhZ;6n zN!_wxT6AEK0PM}uyE7G(dz1eN|GLQ6F+IPCF1 zz<6^p+RUZgGA6Ga`+fKYi?42}S@;3&gCn~{kpY5C5oH9u?}LO{*G>SklPBZXv;tA0 z&wO7-j;KP#j7PS&v8;QX@4Ppze@P@1QR~^hQL&==xPNP~T<26)a%Ib~7qV2Z&^SCf z@oi7JR1Au{gwJXqe4+ZWz)A zWm$TXUZ(8#Soc*pr7I|09rqG!KTjsvA5!ee%e4#YH<<>%*bH>K0SV=2=>qRjwc zpy4-XYHofU2Qi1dgPuErUl>8m^FN?u@n2-U@)D!&$9HMw&o-8dfFI9t$gUM`i(54E z`K&V%E>$0juq6 zk+I5tzp|y2gTkvn7bZobr3v+Ak>JOBp7pHVaS0sn4#pKaC5CwT$-`p#0G{WU(b=wp z7i*)NaNYxBhr}`23^+H;IcpW9e7j#kl;WO#Wao-v^>b6d{raDnq`HxR1T6Gg27icR z?J64*FyfmUrwh?KtX*D2`LN(9u-YCuc=Jnfl_=wI>u0w_LbW!Jfg7kEs>@`8w|g8D zr}JhGl<>I{-1h^w4ycjQHfvYvN-}q68kLYNn6$T0?MK)SQ zsptKd`)zc3?R~UHeKvE=uX6E-JXA+sxLagv@&Qy}E0K&Y`eC7l$?0fCmm0UI z*@Fvu)amf>@E*3Dsw|~=_UN}Ozn^s{UqE13*Tg*TXu*_t>R>X?-0q6E;)h<5sh9W^ zyptHtx!Z4K+A1e2c8>GI7#U8g@;Y2MGsLg!$QAu(eF`Wjmjt*v;+)&IP{={Kt#u0m zYmQz)QM$^Kf||vxP}_@Mr25;hyX%7fbx8b!_NX}Sfyf4p)>*^R9|bb?D>P)wW^pap zGlj`u)aa!6bNsagKj5C0`Vq?{fBbm+8TU$|YM*F=Bs6&S_he(nqU4vd(tAk%K$s{V~WC7WMF$ba>anH0U2z=OOx&#T*cU;I;djzLmjG9=?Dij}jC)FJHf3vg67iq_p zRW!SmEca_oNgI!kzFUXiaFRI zu4$d9eDao}<(B^7j3LeKkBDfLfn;rG^_WO8Sp}~@$3)Z%6MpCub-k5C*YH#x3&SG# zTcwQV`OzqXtn8{vz_ejNv z!K@xcCFz!YhDTD6D8jK{_Uvq_3Fi$yV5%5PrC9SQj4<^1`Rhc)5d}`M)kG!$RlAu+ zc(vstc+GQo*U4Hht(o~le69T`WD0n8unuSl^ZEVHc(VM%%cUUpdaJrn+hOh+cs&Vr zWcwQigQje1##_KQ>Wsv`zl$b0>Wt z8^WCliQ9eHD-R?2a{_m8);;yTtVEODt__iJbwblgN5T+A^r(`%IHa+pD$kvSWL-0h zGQZ6(eGpl8@1~ofX}wMWC&4y|&^LXhtd`ebZ)C@Xqijp>;=NECA z()I_B7IL!WG$Mf`c9)f~;@&#P%#q*3z9(J~jsqAkoS&B5q--pPpG+Qz^^C_%dJjoTk7A`IL2vfx%HB8L3 zb9-Df)vy9YA;gLkth4li^r1@1>> zQAMJC3I4RR)HZxX7vtIVA+nwz_En)e=={c9gDqFy(T*SXB42dyjo#I=3K=M$VqaC_ zjL#bi_Bt*6_)(r3$|~?WkZ636g1ip<16CGc45HE}{=G}Y3A@u+esy&5d%kyDeQaeT zKW)+7As&kITa{VoXz0??daI-u>WNuHEhKtxO|MaoCMobL=$yvpiH0g?h&hl>7d*UBtA0i61{^_??!VuueVK z%lf;ak^>iz9YkPw^6qWY^l=&Dc3h++Rb|>z<{CcU(DLGJm*!C(o&HeNmgRZ3)MT?LH#Fm^EXR`a zd~HBp?%9UnhQ$jdu}tnhRmc~@7%IX9x8mMIagLSaEg+-EwBaz zaCbPWto@B#+bOt?5drJCIA-~3L8(G>39>ljDzUH=-u8M8zJ+srFK8+X zs#I%D(D$8<$Na@bktnO(>37$3+Xk>4u^RHpfiDS6GPU?V+@O_*-A>HuF3wMehwsMc zy9RvH>|xiF&&Bw*vF$d-*mb>+(eogYuK&A!xnToI-0+)Ey?-V)^3C9ICzt+$4fERe z>%<+f_6nKge( zNjFe~;J|#(CD|PBsS47UbD8ki>AI-ajm$tsvzeN}yBu9$k7~x>wmil|+yG@|i4Q~R z2UZ9vsnm+2u(M@H7CetHckJN415egS7vaykt?P6OUK4uXubbpE~=# z>Aev{(5Tdad~-9T=46bq`u=c=Xnz#6m%}R{805<^nW!5wd92B_;zgA>9gX$HJPLt~ zwYX0U6D|lp?FB$Sjn0NsIxT-4XtAj-2Q0SEnrVT_@AkLn#Fh*Zguf&a*8I6y1IFfTBLJirVl}9GOR%2_I|Q3 znk(p45l0BvMdWQo{3(1^T)^&c#Jslwys7TuzHKvVwS{H3C1ktsL#>a5E4rxSfeSxk zqFBo@ys9T-OUc|GHs#(DYL2xAv6qp5El3M+;|yu$b{95c;)Um{ObdFAAqBiApZmr7 z!`47_VM^i^3J^JMg+?X}Wlm`x+A8p|h|n|2n%0CHQT4u;gwS%e1F>ubdBD?fa?EI4 z0oiy_4l}A_$kbsS3X#NLM1vSw+l&4eVssgu{c25C>2k|1PSccJ0q0C)dWLQl2Zz@{ z9YGvGH#kJbe%kwp-4#Q+1KA0>zBnWld*hJDkJ3YKOisCYh$>aLe~&Fsp!m6=FbE!s z4FGwCA5nO2qlIQU0Q59_JT=b+xNh@GOF(gSq-0#H!&#D& z>YghalrGn;qgIU{SQRBn71h>fy+bFMo}lzGE{^xm8W#gg<(fe=Mi~JcLsk1{QyIk= z-ZU1nDvBnaNGYEy3@S;;pbV7^>fp&|6Gzdjw9TgW%jLLMtD&3bFZpQC9;Q$@F~r)q zp5sWDYpy=wipS{=FDvvCG6~fTfg{cUBvJS&tc>W6jQXsRBV87hwfqw~A{u$r@FuvqHBa#EPF_~z{BQGFXUTQ?x~`;#(y z!KX0IDS`4nGbgdIXM~4JNY(80SMl6z{}gFDWrJp0nftCBG%|zlPAEwTgr+qzDchcb zTdD{#K%~_~u9Bu@h38H#|yD?bm#< z`kW}&Gb?f5YhAdY!3oDCpw_8GZ_!4Hw*FX7R5M+@?Q$iuv?=)tZ({TPrm#K>dmb?o z@twEgNtr4P{5Bo8Tz^oJQniCy8cAh6M5t&fr31poLk(u@+BUnONxgmJaN6X1=$CgD z#X}9BfQ{ZYNP{6svsmw1U#Ii#YacfK{3yPV^1ox>ZLsx8UE6U+BK7S_`hxZ)a>O<+ z3E3_E`N(8u4cQgIPhwwI>!Wx9Z?eLeKCydhQ3Js!&n|o_IxZy}ugnK0-2i=<9YwO-WzZhY=PLAkNQRwm zIba%1ob7_~pbl@^e<)!Jn;XJ(wn0QGyv?H@3waq8XoWTNfo&PImu2`5uK8FZ%U0J^6@Q z=7!u8i2JL4W|*A3no|VV3vB!nvd()f69_82iQy<+hB70D^a%>~yeSf0>)8l%1u!Pw zlIXU3?}ZCvq9<(`AAJ<$6bDAW+3S|KwRL1W8*mc5oE|Y}OO%`ms<`UvA>Ou)kzYxZ~eGf?=dmyePTUv0>_e zv33eVn&1lsh!&I!1&C(6!H%I#3>R9=x+mY*qP)M7pR8Qi;+wxC4kXsc?!A^9zTB3E zhjgO^m(xpe*GFc21B6BXT2;_{h!`zj#uE*aBce6$)WWN9B8oVyM_dqg?0ExO-s

y;wds_&El>~HYQ^>ZWyjc?-qaR0LHaiCyrC&^0QVhYLy5XL(y%p(}PF;9h;_5wI21 zPaIgxeWNAO6RY3c_7;S3%-GF91E-v0vtY1KGD*^-12GHpj4-s-6r1t zUYmsS!R6ST3v5Mk|5*9NSmav8eOH}ty68uzWiD0WSB&9^x5{tzW?6Bc`m?x# zZEKV4mKm3N4C}11u2EqkQP4k->T^CAKOyF~okq~15=Axmby6>Nf2)eS6ZvuzQOhG< zq?w;Wn(BCX#N?B#+FWu{U~Pvq){j+0Y;Ibu4GXE0CdHiYBWZieb}e3T-X|ASu$ z-;+RfNhZd`#CW7m09TG6G33FUAluHO*oTHLW0}uy#H+nFfCz=EU6W~f+w|s2wxt5qw5ft)+ZYaiRy^R+gUxe<;0)C8-*TcVb z2PQD3cDpz9EDO<5l^!*3cedJJ3a=VoAQ*VQQ6Q?F=j2se)?ibaw#c8;(WOu9T|Eed zonuKcGctbqdG$cURCNKJ$$kp%Hbe{*hG4Sc@>1F`>LB<`H~BTISXtCJ2d(ce@o&RV0&V-Zy!x$HDSF=@ zvriZ1Bd;BKADqP#z7R7y9d{o;Jkqd)d_l^dm^KdM;zsU6dvij`Yc~_H?TEHlzXTgu zI8TIAL`0;px#j>-sYFd5yxv=DXFy3%yrD>m2k*0<%eV{ zVZdk0{MK$fomg;T)`6}v#^8U^j$qnN-O)8i!yGCCw2MqwQ0VUa!K0Rb252F9CGIKJ z$w=&aisxAKbP2X!?PhWr5J=}HoS)=;vsf#<=uH9dx^-IZmiQz%6ZM7ueW@wYa^60} z=E}&Z!-f?5?}R8#Crw+#$kE+TadG~B;Ek{rVDN#uOD&UYYiqG`6&_6z(v0+c-f~V) z5>iG1ZVd}KT<6f@k{kQ`EZ@$LK;Jm>T*gXQM+rZp{K?!@yXu;#d3Eh}exFu`5^m`! zBx#xRxl;+HT~F4%Y6b9gLc>VbZ8PV#pil3If1i!k$J2 zotvv<+$`2aVBm*qjYU|Tyxr=f_&>GobRGC1Tmq9%S4AJWbvgEHQWrMk8o8pwwurXv zQ#;-3hNew2e>ceoD2JiEP%%o>w!L%#b-@b!q@ke!L9-ZsI&v!x+U2zYFO~QZ@;La~ zGq44sU*Bb{Jz0fC{AO`JLc!;?W?}2>~#9Y^!HRuXXbLL##AD z2}$p=6A_Kd{bXG*I~UhY33yGitHrQPKfKRlf5wIsM^rS3}b*YQu7uZsY5s34tRI#{I9RJ?pp%L=D2 z+h?2ua${&F&d!$;+ndb_1>*43)YJ~(t*;zWl7*d`BdjHW)4*Z0-^`bB-BRzS;s+&V zZ==0=qPqqId<=0n+o(pDQU*n9-M%MFS+XZd$j~c%c0Sz1tYyJOVDO=CUheRgB7Sc{ z++ZlS1VTT@6fD2rP>hbkAOD^6u9ObLlLEa<@k3Vq6V*XYOm0pXPcP2Hk7IrQi)+aE*V)m_keaEdVZPRj;&eOaqk@~o8hn@L9mpm>bSVlRjY9ZG-bn}cqy{^N@y_x z8&YwQ`V7&U@pfr|(O#`^S0AX?E9e-MvAY|fY101X9Dch&9vRaq3gFq9iqvlK9nSBE zCv#}u6VHl zpqOf#^g2h1)y!zU!N+Edo>%1gA~!st>Wu8W zADty??^+lh9(m}yUQwL*I|y)S$7|LxizbaA`yGfBmqkdCp}%^K;U*AgQWf=NR#jkx zF1QI5j1%;pkAZj|Q3m^3?7@4KMP{rFGh*kmrw){rl$17v!OOs13s10!{6F5se`tER z<~evMLPH@7jWHS!7sCI7NU+T0c}8-5#ezm+Y5KLacC|3J!dsd&l+?6;+9Z_4*DX=s zU!%Blu~5emT(r=oc14ydj8G=mA0Af1nbMZv3!OTe^iJStXU`1>${=@6f%jY4$N^;^ zCoTyo-1O%^!Uu6PnGXfB_(Hn9xWP|U;?wF`haQ`L4iIK<&=5mKR`Pp;j&!wIeu5r? zqu6v?&q`YQVh28Y;OqNbW5Ddu{}6-5|BV@5ZG3@ULe=37fY!>(f)+qjz^vE3T~-1P6gsctayxVZWav%de}vVI7VWT?Ddy76q7^;~ie5 zi5axq1CY13_a^vAOnmmbG4~TzSGivGht)d3;PJ{nn%0Uu(Zy_DYMbY@$!xQAhEsWo zBMLngY)2&qM!!pL9^mZ7%l}p~^EE+pIu5$YteN4(;UE-4JV6OT>9fkG_zoi8t@Bfm z#{Kg_mG6~)v>zO0NJvBJ1&_x}jbJ-PNtq}?>Cy5{BCUc_zR$bJ=VhM+1dyB>#P55` z?<6mvZ*~NU-^Hy`1me`q?!X7e;)!mHZeOIC(*>prYbHrx+8%#Z<((WIErUz* z6;d${ZtLWa{nf?>GKDO`S&q;B*f4gFFx$ZfBtWMxU*!XCXp7XD^|=>P2RNvLImiM> zaN2KNK%k)6BE(L_nGDM0L`tkA*jDl0Q=BVA2>Kz zG!kL(R*aGYjFjL4|CpjOqK&5+I$1<>h5J{W08hBJ6&NlxG9kSEi9B6pdel*A`UA!k z-x9ua3t&^P+i4acL@`@o0h!R819QR#RKe4*Zvj}}c78>3tqA4d3wzEBh^z;!C&9x! znUTm4i3Ha>5U-Fd1H4E+`7gTJ$}oV>xn)sDpb~q#1U^eQs@it@ZuXs<4}pDC8JsW9 zl&F*KU!dd=T0oyJ!U$cSw_+Mui7v40V5UNIxnO>|hBXT$`@URd%Sv{m&?;ViQ!oFb z;oqFetKD!)n1?j6qcwfswLoy%(KZ@481RC!r*LoaT?>Z4rYdG(m0*q{tfuvw?sOnw=aQ9 z=*6ihPC<_MeO8(FxFz>12i%ygoDuPHCE=abb@m`YWIkgUea+%AapdEbhVKg03 zKlvtDq8v_5Wqg60pgoEYW_MNDurnP6P4wd(MzqL~_e zc4coY@hy8|AN5^ao|eocq7}`7rKM#6`1JYtf+TsORcp8?ouYEt_}~-6XMU~iM{RSV z2w7d#m{C-HIC!0=qqUU1bceCau??uZQ>GvD)+V|QM_v8P^dYea0Q?QPL69KGqv1tuTor`u&;yoaD4xTFD83ncNQ;LW*LXhPT1 zS1D@tLaBXfP*(HpdbZ$mOfP?GG_#3{oqWdWb?eI_A0)gOX6VBmgBc6IOgDGA z_vqqTEn&KTmnud!^7P}eeM0&DJPuVhV%H&{SZo)&rnUGz6e+Y_Y?mW;s(J`=Aiq*m zgBkAZ(QI!Rcdlm9VyaVMCc7n{-)H*3_|r-clI1b;NzTFUbECDHDYvknw7hCR3Xi60P@+<@s%KigcJIzP{4FbT1@L5=^DQXHnTBrdv}% zX<@>i)^Koy37=u^<@Ijbn$jv!A@?JP9+SlFQc>2wJ*zKmzQVXKLg=;r^bcCd1J?lS zUI#h0t8|gM^IB(PrTn4-s;nFwE?40wgd5bMR!5(Bd*6*`+S3}I}hmZ&o=DIh>C`ob5n0dDla1KqC6l0i~KIvL--tuZ@ zjbCX)xmdIG%4ojOO6$xw2#6n~Z^Lm!+F}gkfc@Pj1CzQ-Q?cQJ#GmWHx6}mobqwUe zE$|_c_CmD^1sC_O*RnT~ykoWcQDE?+>{-afMi7 z`c{Y9(|#F>`pLgP%VOq*hrvBL+5_^KV?gD47LG+F($}|965ue&NO4H=NI2|vpTEri zg03D4Tp+O5o*oE2HiCL@h1H}gl7jFRYV=&D_ZC$~tQj$tN~2tjb~LWz;@S>wwd9$6 zGvn~+-AEMDiZ<<6tu>vUog6rD;M0#k{`i~Q=_6qWU+~-O;ZWnw)Y)>y6<6FkI5_yi zTrSs^2(;E1Qcx(l96DCw*Itt4uRpvN(kA+(jh6W9>wO4sy=9C(i?WON@-TRBgzl4KitiSk#N%^HQcakHc># z|G`6ewFqWvaqml@XkB$+^JR(4SH4t5N2h~?)wTz_Sw&V_7jOjVt8?J zaYZ8V-m5uutV|{j@BO1eUjAQtp{h`mPCaES_@colikc~?KxqUDk&+Pe0y^hL$~^Q) ziH9C7vF|{Y<0oCFXBFkLr*K9rNi-^nM!axt!w~fx(|Adtl=9XaR&8(`;p;Ha)hSrN zRh08KLIlpRInk!5>qiq(dIi+wQo&pg%sM$6%_TuymL0EMyeb?WpirO;pC{p z*o0zkK~X3|wF0FoHM7z*ARhIWzNOR9kr52^!I~jhH>_E|Rx`9#LDK)W(q6iGt-W=6 z*nIudDc=976q_~#tAv`niJQ&twNs?Spp+t$$xtj7XYRV|u1iT}yz2SO!@(uOlc~#zp#EE+ z5fLDXw_R+ooU5uJ?_!rUm9nB(QGQlaj~Ej%O*E#l5(*Oddk6T>x*7@^Dxq%$-&Gp2 z4rtAP{jCi9jz#J1TFxmOk$)zLhx*7bl}a;r-F4Sx<#PG>svh*A6#=xJ3s`OCeDiQ& ze0==t{r&yFoKB~+l}e?hT&-X8Sn)ug#n_a~cfLE%q5Va+uC?gdl0qZ|ao6SD9gTTvIEEXs3y6dhzXEO4a_V@B{ zo)ZE9Pr<5GDhsDho%&K=U*Gi|9Uc9RF5~9!6DS4Qv_VJKZ9Jz zH-Pfrn{;{PUX!2fHR;NFrk>XIh~0n~&J+UVFDxwVyXT&Jt}K_!r_cB$JOcy}D2$Hd z6i=Q!`PpnXdqr<=@0Mmh?B*UhPh0QnvCs%^y*T$k7Ssg-b_&0hvqIHN6!uOQ+6bM)Q`}TOE;!EF*a(vPt9bd`;&IUXY zDamHDOiWDt=$?D-c~-Sr&7IZDc!mfd0Hu^Vb?VfYEz9Z|92~qnV0|lp!&`F~i3kRI zBa|wdTYsG62e&P7{AiI(OftMVhDck87>Ke)2Rqq^#X=3S8sQU#2oOq$0JN$jI!q8@ z`5r#p5aM0!i4~z>v%}B6XY!%1#^~?TNYQ?!&Poutcl~+&zkTq*2mg!K+C39|G{Q4V z0Kxgh#KiZ@9UO8R>vRBgEXo;)|-GS34KEs~$N>5NOV z>nudt^hAWM5Kd^j3LmMW@q7sv(Dkw^^gm1_H3Fb68Vn6-4c#tSihI6q@Vg(6p|l{I z)P&8zGlgJc$23hId+f2d?%TKTPnY-4nL{fAI44#>xNKO#32fWW&dy6OzWCyQ$Y!(K z78e)WhBGz)uD2ggrK%a9s8TGttXX4m>BR}Iyd=S%^J8q^9Dy!BXOFGCRl;T0I}hBT zwRtyP-vmkMIwuI&d|+V+4N`%nA*Q@sJdT1H#o^=dzyZzO4;$Qmzljl*40mgsRqVpk z3<8#)PN!K|SUB?V!wWipgmk+t&2f*I6kU4cv5rVq~?jEf&<4T6ElKHL^9k9u?T0q{5^=|u`G)dCrxpU{vzfPyqYx4Pg+bQLC5Uy*vqF7jP z$>m*ygpRDiK%c=-pTSVC!BC&1r&H3A5@g~Kk3%8?T6tN&rGR2ZQ7S2N1;yN=W@bS% zI;A*0r5T%su^E`pc{)leA?QeZ&Z9H!r^Yt;yLhv>xH$UclTW^FbaeDPL7xH3xaGfF z0th=pB9TaP$BrEzT)%$(+l3Gm3WYOriZnvK-rfcYwxcPPG=-9)T-CU)MoNKYc`0Ts z6QY*zRRCYS1TJ6~YzMTK7}6WriMSvU6IiAoT*aqSUm*lRpzX-W$lvVSx9?rmYIU)3 zQtCex0R&|NZqU=ybLF;e+dk0S+xvXibtx8$Pg&Us!%{b_7qlAuck49c*D;g;T#8kN zp`9(n<8chbU}|dWjsph{{L#$J%spNm4r1?u4P$7qfuPfWqNx0j?<@4fAZwX zliv*Phtu-@oB;d<48W3AFWueUmkkaMzOk>b?^lw^WFL6B?b@x3c_yG$znG>;G#bTM zY^NqBCcZH`I{L+#nVI{8GR>ySe@*})0to8{0au7dqly0h{+IXm_P#Qk&Au?1Om+w% zYRjZnlOFBq03igHWf6%)&{|V07IV2=?ne_76W^YkocwONT+YK%q&MSYKPP}D1Q28` z3lba!5Rb>Ry}i9J%4W0Ib#!z*H<3teiAKGkN!zw-5jN*qPY^<27zU c #3A2C2C", +", c #433C3C", +"< c #4C322E", +"1 c #633E3E", +"2 c #49473E", +"3 c #564B3B", +"4 c #5A533C", +"5 c #65593A", +"6 c #755E30", +"7 c #756639", +"8 c #746436", +"9 c #101845", +"0 c #1C2A4C", +"q c #1C2645", +"w c #1B2B51", +"e c #162850", +"r c #222642", +"t c #242A43", +"y c #2A2E42", +"u c #222D4B", +"i c #2B2D4B", +"p c #25244D", +"a c #2C3244", +"s c #2C344B", +"d c #25314C", +"f c #323542", +"g c #363943", +"h c #3C3D42", +"j c #33354C", +"k c #343A4B", +"l c #3B3C4A", +"z c #383648", +"x c #262D51", +"c c #2A3454", +"v c #333453", +"b c #363857", +"n c #373C63", +"m c #413E49", +"M c #3E4044", +"N c #3E414B", +"B c #3C4255", +"V c #3D4164", +"C c #3F6C7A", +"Z c #444444", +"A c #484744", +"S c #4A4945", +"D c #45464B", +"F c #4B4B4B", +"G c #474849", +"H c #514E43", +"J c #5B4A48", +"K c #555244", +"L c #5C5A44", +"P c #575549", +"I c #484954", +"U c #535353", +"Y c #5B5B5B", +"T c #575759", +"R c #504F53", +"E c #644C4B", +"W c #664746", +"Q c #625D44", +"! c #655353", +"~ c #655A5A", +"^ c #6C6545", +"/ c #696548", +"( c #746C47", +") c #7B7549", +"_ c #706D52", +"` c #636363", +"' c #6A6A6B", +"] c #676767", +"[ c #64797B", +"{ c #6D7070", +"} c #5B6F73", +"| c #AA7B1C", +" . c #987726", +".. c #897739", +"X. c #8B6F2C", +"o. c #A47B21", +"O. c #847B49", +"+. c #B4871C", +"@. c #9B8637", +"#. c #A48A38", +"$. c #A7923A", +"%. c #B69938", +"&. c #B59427", +"*. c #BBA53A", +"=. c #C99617", +"-. c #D19510", +";. c #E39B09", +":. c #D0AD17", +">. c #FEAE01", +",. c #F9AE07", +"<. c #FFB301", +"1. c #FFBB00", +"2. c #FDB40C", +"3. c #F6B708", +"4. c #FDB612", +"5. c #FDB915", +"6. c #FDBC1C", +"7. c #F4BC19", +"8. c #EBAD0A", +"9. c #C09C32", +"0. c #C7A638", +"q. c #D5B63C", +"w. c #D1AF2A", +"e. c #FCBF21", +"r. c #E9B927", +"t. c #FFC400", +"y. c #FFCC02", +"u. c #FDC80B", +"i. c #FFD202", +"p. c #FFD908", +"a. c #FFC719", +"s. c #FFD618", +"d. c #E6D018", +"f. c #FFE700", +"g. c #FFFB01", +"h. c #DBC939", +"j. c #FDC224", +"k. c #FDC62B", +"l. c #FDCC2D", +"z. c #FDCC25", +"x. c #FED32D", +"c. c #FFDD2C", +"v. c #FFD925", +"b. c #FDCB33", +"n. c #FDCE3A", +"m. c #F4C936", +"M. c #FDD233", +"N. c #FFDB34", +"B. c #FDD43C", +"V. c #FFDB3C", +"C. c #F4D73B", +"Z. c #E9CA33", +"A. c #FFE828", +"S. c #FFFE2E", +"D. c #FFE435", +"F. c #FFE43B", +"G. c #FFEC3C", +"H. c #FEEC37", +"J. c #FFF336", +"K. c #FFFE34", +"L. c #FFF33C", +"P. c #FFFC3C", +"I. c #8B844B", +"U. c #978A4A", +"Y. c #9C944A", +"T. c #918C51", +"R. c #A69848", +"E. c #A69A51", +"W. c #AC9747", +"Q. c #B7A747", +"!. c #BBB44A", +"~. c #B6A653", +"^. c #AFA952", +"/. c #C7B748", +"(. c #D5BA48", +"). c #C4AD53", +"_. c #C9B755", +"`. c #D5BD57", +"'. c #C7AD48", +"]. c #C8C356", +"[. c #D9C859", +"{. c #D7CC50", +"}. c #E7D84B", +"|. c #FDD443", +" X c #FEDB44", +".X c #FDD74A", +"XX c #FDDC4C", +"oX c #F4DB4B", +"OX c #E8D65A", +"+X c #FDDD53", +"@X c #FDDD5D", +"#X c #F3D95B", +"$X c #E8C950", +"%X c #FFE344", +"&X c #FFEB43", +"*X c #FEE34C", +"=X c #FFEC4B", +"-X c #F2EA47", +";X c #FFF343", +":X c #FFFE43", +">X c #FFF34C", +",X c #FFFE4C", +"X>XXX|.|.|.|.|.|.B.B.n.n.n.n.n.n.b.b.b.b.k.k.k.k.k.j.j.j.j.j.j.e.e.6.a.s.s.7.&.5 a 0 h Z Z Z Z Z Z Z Z Z Z Z Z Z G U ", +" = U Z Z Z Z Z Z Z Z Z Z Z Z Z Z $ % ^ /.2X6X*X.X.X.X|.|.|.|.|.|.|.B.B.n.n.n.n.n.b.b.b.b.l.l.k.k.k.k.k.j.j.j.j.j.e.6.6.6.6.a.s.u.=.8 a 0 M Z Z Z Z Z Z Z Z Z Z Z Z Z U = ", +" ; U Z Z Z Z Z Z Z Z Z Z Z Z Z t $ ^ _.6X6XXX.XXX.XXX.X|.|.|.|.|.|.|.B.B.n.n.n.n.n.n.b.b.b.b.l.k.k.k.k.k.j.j.j.j.j.6.6.6.6.6.6.6.5.u.u.=.6 u d Z Z Z Z Z Z Z Z Z Z Z Z Z U ; ", +" ; U Z Z Z Z Z Z Z Z Z Z Z Z g X H !.9X9X+X+XXXXXXXXXXX.X|.|.|.|.|.|.|.B.n.B.n.B.B.B.M.M.M.M.M.l.l.l.k.k.j.j.j.j.j.j.e.6.6.6.6.6.6.5.5.5.u.u.=.3 0 k Z Z Z Z Z Z Z Z Z Z Z Z U ; ", +" Z U Z Z Z Z Z Z Z Z Z Z Z Z + & Y.4X9X1X+X+X+X+XXXXXXXXX.X.X.X|.|.|.|. XF.G.G.D.V.B.M.b.m.m.b.b.x.x.c.c.c.v.z.j.j.j.j.j.6.6.6.6.6.6.6.5.5.4.4.y.u. .f 0 Z Z Z Z Z Z Z Z Z Z Z Z U ; ", +" Z F Z Z Z Z Z Z Z Z Z Z Z g . L [.9X4X+X+X+X+X+X+XXXXXXXXX.X.X.X X&X;X&X|.q.*.#...7 5 4 3 2 2 3 4 5 7 ..#.%.w.j.v.s.s.j.e.e.6.6.6.6.6.6.5.5.4.4.4.4.i.8.5 0 g Z Z Z Z Z Z Z Z Z Z Z U ; ", +" ; F Z Z Z Z Z Z Z Z Z Z A & $ T.9X9X3X+X+X+X+X+X+X+XXXXXXX.XXX=X>X X(.$.7 2 - t q q r y a a f f a a y t q q t g 2 7 #.w.z.s.s.a.6.6.6.6.6.5.5.5.4.4.2.2.y.t. .u s Z Z Z Z Z Z Z Z Z Z Z U ; ", +" = U Z Z Z Z Z Z Z Z Z Z Z + : _.aX4X+X3X3X+X+X+X+X+X+XXX.X*X,X=X(.I.K * + $ f h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h f t q a 4 ..w.a.s.a.6.6.6.5.5.5.4.4.4.2.2.1.y.=.h 0 Z Z Z Z Z Z Z Z Z Z Z U ; ", +" @ U Z Z Z Z Z Z Z Z Z Z Z . K .1.,.h s A Z Z Z Z Z Z Z Z U ", +" G G Z Z Z Z Z Z Z Z : + ].aXtXtXtXtXyXyXyXtXaX.>.t.-.d k Z Z Z Z Z Z Z Z G G ", +" = U Z Z Z Z Z Z Z Z Z . E.aXtXtXtXtXtXtXyXtXaX.>.>.t.| e Z Z Z Z Z Z Z Z Z U = ", +" F Z Z Z Z Z Z Z Z Z + Q aXyXtXtXtXtXtXyXtXaXuX, + Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z S u m 3.y.2.2.<.<.<.>.>.<.1.5 w A Z Z Z Z Z Z Z Z F ", +" = F Z Z Z Z Z Z Z Z - & OXaXtXtXtXtXtXtXtXaXsXH . Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G 0 2 1.t.2.<.<.<.<.<.>.1.8.g k Z Z Z Z Z Z Z Z F = ", +" F Z Z Z Z Z Z Z Z Z . E.aXtXtXtXtXtXtXtXsXaX^ . Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z M y r t h f h G Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G G G G Z Z Z Z G Z Z Z Z 0 5 y.1.2.<.<.<.<.>.>.t.| 0 Z Z Z Z Z Z Z Z Z U ", +" = F Z Z Z Z Z Z Z Z % K iXyXtXtXtXtXtXtXyXaXU.o Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h g Q ..%.h.0.h h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G G G G A G G G G G Z Z Z Z Z Z Z Z Z e .i.<.<.<.<.<.<.>.<.1.4 d Z Z Z Z Z Z Z Z F = ", +" F Z Z Z Z Z Z Z Z h $ _.aXeXtXtXtXtXtXtXaX_.. h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z M g *.G.G.H.D.J...t Z Z Z Z Z Z Z Z Z Z Z Z Z Z A G G G G G G Z G G G G G G Z Z Z Z Z Z Z Z h 0 =.y.<.<.<.<.<.>.>.t.-.u M Z Z Z Z Z Z Z Z F ", +" @ F Z Z Z Z Z Z Z Z + ( aXtXtXtXtXtXtXtXaXuX; & Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z g S V.F.B.B.B.H.$.q Z Z Z Z Z Z Z Z Z Z Z G G G Z Z Z G G G Z Z Z Z A Z G G G G Z Z A A Z A G s h 3.t.<.<.<.<.>.>.>.t.6 0 Z Z Z Z Z Z Z Z U = ", +" Z Z Z Z Z Z Z Z Z h & [.aXtXtXtXtXtXtXyXaX) . Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h f q.G.B.B.B.H.0.y Z Z Z g s s a g Z A G G G G Z Z A G G G Z Z Z Z Z Z G G G G G G A A Z A Z G w 8 y.<.<.<.<.>.>.>.1.;.s h Z Z Z Z Z Z Z Z G ", +" U Z Z Z Z Z Z Z Z + ) aXtXtXtXtXtXtXtXaX_.. h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z q $.L.B.B.B.V.m.h 9 t f S 5 7 5 S y u Z G G G A A A Z G Z Z Z G A A Z Z Z Z Z G G G A Z Z G A Z 0 =.y.<.<.<.<.>.>.>.t.6 w Z Z Z Z Z Z Z Z U ", +" ; G Z Z Z Z Z Z Z h & `.sXeXtXtXtXtXtXsXyXS % Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z r ^ F.B.B.B.B.F.0.7 $.q.m.N.c.x.Z.*.7 i s G A A A A Z G G Z Z G G G G G G G G G G G G Z Z G G G s 2 1.t.<.<.<.<.>.>.t.;.s M Z Z Z Z Z Z Z G ; ", +" F Z Z Z Z Z Z Z Z $ ^ iXtX3XtXtXtXtXtXaXE.. Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h y 9 5 G.V.B.B.B.B.D.H.H.D.N.M.M.x.x.A.A.0.Z u G Z Z Z Z G G G G G G G G G G G G G G G G G G G G Z Z e o.i.<.<.<.<.<.>.<.1.6 u Z Z Z Z Z Z Z Z F ", +" @ U Z Z Z Z Z Z Z Z + ~.aXeXtXtXtXtXtXiXtX; * Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z M f t q t M L ) $X%X|.B.B.B.B.B.B.M.M.M.M.M.b.l.l.x.A.z.H u G Z G G G G G G G G G G G G G G G G G G G G G G G Z k h ,.t.<.<.<.<.<.>.t.=.u Z Z Z Z Z Z Z Z U @ ", +" ; G Z Z Z Z Z Z Z * H 3XyXeXtXtXtXtXtXaXU.. Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z A h r r f S ( U.Q.$X%X;X%X|.|.B.B.B.B.B.B.M.M.M.M.M.b.l.x.l.l.A.x.A d G G G G G G G G G G G G G G G G G G G G G G G G G G w .i.<.<.<.<.<.<.<.<.3 s Z Z Z Z Z Z Z G ; ", +" G Z Z Z Z Z Z Z Z + U.aX@XeXtXtXtXtXsXtXZ - Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G a h ) W.(.oX=X>X>X&X X X|.|.|.|.B.B.B.B.B.B.M.M.M.M.M.x.x.l.l.l.A.r.a h G G G G G G G G G G G G G G G G G G G G G G G G G k Z ,.t.<.<.<.<.<.>.t. .w Z Z Z Z Z Z Z Z F ", +" # F Z Z Z Z Z Z Z h & `.iX@X@XeXtXtXtXaXE.. Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z a Z {.7X7X>X*XXXXXXX X X X X X X XB.B.B.B.B.B.B.M.M.M.M.M.x.x.x.l.l.A.@.0 G G G G G G G G G G G G G G G G G G G G G G G G G G w +.y.<.<.<.<.<.<.1.;.f M Z Z Z Z Z Z Z F @ ", +" = F Z Z Z Z Z Z Z & L 4XtX@XeXeXtXtXyXsXL % Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z t '.7X+XXXXXXXXXXXXXXX X X X X X XB.B.B.B.B.B.B.M.M.M.M.M.M.x.x.l.l.c.z.Z l G G G G G G G G G G G G G G G G G G G G G G G G G s 5 1.1.<.<.<.<.<.<.1.5 d Z Z Z Z Z Z Z G ; ", +" Z Z Z Z Z Z Z Z Z + U.pX@X@XeX3X3XtXaX`.% h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z f H 1X2X+XXXXXXXXXXXXX X X X X X X X|.B.B.B.B.B.B.B.M.M.M.M.M.M.x.x.x.l.A...u G G G G G G G G G G G G G G G G G G G G G G G G G G s -.y.<.<.<.<.<.>.t. .w Z Z Z Z Z Z Z Z Z ", +" F Z Z Z Z Z Z Z Z & _.iX@X@XeXtXtXtXaXI.. Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z f H +X2X1X+XXXXXXXXXXXXXXX X X X X X|.B.V.F.L.G.G.J.N.M.M.M.M.M.M.x.x.l.A.0.i G G G G G G G G G G F F F G G G F F G G G G G G G G w .i.<.<.<.<.<.<.t.-.s Z Z Z Z Z Z Z Z F ", +" # U Z Z Z Z Z Z Z f S @XyX@X@X@XtXtXyXtXK & Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h g {.6X1X1XXXXXXXXXXXXX X X X X%X;X;XL.F.h.*.@.7 %.D.D.M.M.M.M.M.x.x.x.A.Z.Z N G G G G G F F F F F F F G F F F F F F F G G G G G k 4 1.t.<.<.<.<.<.<.>.3 k Z Z Z Z Z Z Z F # ", +" = F Z Z Z Z Z Z Z $ ( iX@X@X@X@XtXtXaX`.& h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z $ W.7X1X1X+XXXXXXXXXXXXXXX%X;X X/.W.O.L M y q q q 5 C.D.M.M.M.M.M.x.x.c.x.K k G G F F F F F F F F F F G G F F F F F F G G G G G G j -.y.<.<.<.<.<.<.t.6 u Z Z Z Z Z Z Z F = ", +" ; G Z Z Z Z Z Z Z + Y.iX@X@X@X@XtXtXaXY.+ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z + O.7X1X1X+X+XXXXXXXXXXX*X=XR.Z a r t a h Z Z A Z 9 ..J.M.M.M.M.M.x.x.c.x.L k F F F F F F F F F F F F F F F F F F G G F F F G G G x .i.1.<.<.<.<.>.t.o.0 Z Z Z Z Z Z Z Z ; ", +" Z Z Z Z Z Z Z Z Z & ).iX@X@X@X@XtXtXiX^ $ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z y Q 4X2X1X+X+X*XXXXXXXXX>XR.O a Z Z Z Z Z Z G G G g Z m.D.M.M.M.N.N.x.D.x.K k F F F F F F F F F F F F F F F F F F F F F F F F G G c 8 t.1.<.<.<.<.<.t.=.s Z Z Z Z Z Z Z Z Z ", +" G Z Z Z Z Z Z Z h ; $XyX@X@X@X@X3XyX#XS - Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h Z OX6X1X1X1X*XXXXXXX=XoXZ a A Z Z Z Z G G G G G Z y 0.J.N.N.N.N.N.N.A.Z.G N F F F F F F F F F F F F F F F F F F F F F F F F G G N S 3.t.1.<.<.<.<.1.8.Z h Z Z Z Z Z Z Z G ", +" F Z Z Z Z Z Z Z * K @X3X@X@X@X@X3XsX`.- h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z % ).9X1X1X1X1X*X*XXX>X$Xh h Z G Z Z G G G G G G Z t %.J.N.N.N.N.N.N.S.q.j G F F F F F F F F F F F F F F F F F F F F F F F F F F G k -.y.1.<.<.<.<.<.<.4 j Z Z Z Z Z Z Z F ", +"# F Z Z Z Z Z Z Z % ^ 4X3X@X@X@X@X3XaX~.$ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z + Y.9X1X1X1X1X1X*X*X=XoXK f Z G A G G G G G G G Z t q.J.V.N.N.N.N.N.S.@.u F F F F F F F F F F F F F F F F F F F F F F F F F F F G c +.i.1.1.<.<.<.<.1.6 d Z Z Z Z Z Z Z F # ", +"@ F Z Z Z Z Z Z Z $ O.iX@X@X@X@X@X3XaXU.+ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z t ( 9X2X1X1X1X1X*X*X*X>X( r Z Z G G G G G G G Z 9 ^ G.D.F.F.D.D.N.J.D.F l F F F F F R R F F F F F F F R R R R F F F F F F F F F F c .i.1.1.<.<.<.<.t.X.u Z Z Z Z Z Z Z F @ ", +"= F Z Z Z Z Z Z Z + U.iX@X@X@X@X@X3XsX) $ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z f H 1X4X2X1X1X1X*X*X*X,XW.9 Z Z G G A Z h a r q Q C.L.V.F.F.F.F.D.K.$.u F F F F U U R U F F F F F F R U U U R R F F R F F F F F F c X.y.1.1.1.<.<.<.t.o.u Z Z Z Z Z Z Z G = ", +"; G Z Z Z Z Z Z Z $ E.iX@X@X@X@X3XtXyX^ % Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z m f [.9X2X1X1X1X1X*X*X=X*XK 9 f f t t f G ^ I.Q.G.L.V.F.D.D.D.D.K.h.s D F F R R U U R U U U R R F F R U U U R R R F R U F F F F F b 8 t.t.1.1.<.<.<.t.+.d Z Z Z Z Z Z Z G = ", +"; Z Z Z Z Z Z Z Z % ~.iX@X@X@X@X@XtXtXL & Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z $ ^.9X1X1X1X1X1X1X*XXX>XoX) G K O.R./.oX&X:X:XG.F.F.F.F.D.H.L.*.9 e F R R R F F F U U U U R U U R R R F F U U U U U U F F F F F b 5 t.t.1.1.<.<.<.t.+.s Z Z Z Z Z Z Z Z ; ", +"; G Z Z Z Z Z Z Z & ).iX@X@X@X@X@XyX@XK - Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z + I.9X2X2X2X1X1X1X1X*XXX>X,XoX%X;X:X;X=X%X%X%X%X&XG.G.G.G.G.L.*.I.) D x D R F U U U U U U U U U U R R U U U U U U U U U F F F F B K 1.t.1.1.1.<.<.t.=.s Z Z Z Z Z Z Z Z Z ", +"Z Z Z Z Z Z Z Z Z & ).iX@X@X@X@X@XyX#XS - Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z y Q 8X4X2X2X2X1X1X1X*X*X*X*X=X=X%X%X%X%X%X%X&X&X&X&X&XG.G.G.G.P.K.K.h.U.m v R U U U U U U U U U U U U U U U U U U U U U U R F F B K 3.y.1.1.1.<.<.t.-.j M Z Z Z Z Z Z Z Z ", +"Z Z Z Z Z Z Z Z Z & ).iX@X@X@X@X@XyX#XS - Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h Z OX8X2X2X2X1X1X1X1X*X*X*X*X%X%X%X%X%X%X&X&X=X=X&X&X;XG.G.G.G.G.L.K.K.h.Q c R U U U U U U U U U U U U U U U U U U U U U U F F B H 3.y.1.1.1.<.<.t.-.k M Z Z Z Z Z Z Z Z ", +"Z Z Z Z Z Z Z Z Z & ).iX@X@X@X@X@XyX#XS f Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z & _.9X2X2X2X2X1X1X1X*X*X*X*X*X%X%X%X*X=X=X=X=X;X;X;X;X;XL.L.L.L.G.H.H.K.K./ c U U U U U U U U U U U U U U U U U U U U U U U F D F 3.i.t.1.1.1.<.t.-.k M Z Z Z Z Z Z Z Z ", +"Z Z Z Z Z Z Z Z Z & ).iX@X@X@X@X@XyX#XS - Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z + Y.9X3X2X2X2X1X1X1X1X1X*X*X*X*X*X*X=X=X=X;X;X;X:X:X:X;X;XL.L.L.L.L.J.H.K.J.R b U U U U U U U U U U U U U U U U U U U U U U F B K u.y.t.1.1.1.<.t.-.k Z Z Z Z Z Z Z Z Z ", +"; G Z Z Z Z Z Z Z & ).iX@X@X@X@X@XyX@XH - Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z % ) 9X4X2X2X2X2X2X1X1X1X*X*X*X%X*X=X=X>X,X,X,X:X-X}.:X:X;XL.L.L.L.L.L.J.H.S.h.v R U U U U U U U U U U U U U U U U U U U U U U B P u.i.t.1.1.1.<.t.=.s Z Z Z Z Z Z Z Z Z ", +"; Z Z Z Z Z Z Z Z % ~.iX@X@X@X@X@X3XtXL * Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z f K 4X8X3X2X2X2X2X1X1X1X*X*X*X=X,X,X,X,X}./.Y.) P D L ].:X:XL.L.L.L.L.J.J.J.S.) v U U U U U U U U U U U U U U U U U U U U U U B Q y.i.t.t.1.1.1.t.=.s Z Z Z Z Z Z Z Z Z ", +"; G Z Z Z Z Z Z Z $ E.iX@X@X@X@X@X3XyX/ % Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z g [.9X4X3X2X2X2X2X1X1X1X*X,X>X{.^.I./ G j i j N D s p /.:X:XP.L.L.L.L.J.J.S.q.v U U U U U U U U U U U U U U U U U U U U U U b 7 i.y.t.t.1.1.1.y.+.d Z Z Z Z Z Z Z G ; ", +"= F Z Z Z Z Z Z Z + Y.iX@X@X@X@X@X3XiX) $ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z r ~.pX4X4X2X2X2X2X1X1X*X6X}.L y t i k D F U U U U U l m qX:X:XP.P.P.L.L.J.K.J.P I U U U U U U U U T T T U U U U U U U U U U b ..p.y.y.t.1.1.1.y.o.u Z Z Z Z Z Z Z G = ", +"= F Z Z Z Z Z Z Z $ O.iX@X@X@X@X@X@XaXU.+ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G Z $ I.pX4X4X2X2X2X2X2X2X6X6XL 9 G F F F U U U U U U U U x ^.:X:XP.P.P.P.P.P.J.K.O.b U U T T U T T T T T T T T T T T T U U U U c @.f.y.y.t.t.1.1.t.X.d Z Z Z Z Z Z Z F @ ", +"@ F Z Z Z Z Z Z Z % ^ 4X@X@X@X@X@X@XaX~.$ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G G Z y / 9X4X4X4X2X2X2X2X2X7X'.r G F F F U U U U U U U U U v O.:X:X:XP.P.P.P.P.P.K.R.v U U T T T T T T T T T T T T T T T T U U U b &.f.y.y.t.t.1.1.t.8 d Z Z Z Z Z Z Z F @ ", +" F Z Z Z Z Z Z Z * K +X3X@X@X@X@X@XiX_.* h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h Z #X9X4X4X4X2X2X2X2X7XQ.t G F R U U U U U U U U U U v ) :X:X:XP.P.P.P.P.P.K.!.b U T T T T T T T T T T T T T T T T T T U U B :.f.i.y.t.t.1.1.1.5 s Z Z Z Z Z Z Z F ", +" F Z Z Z Z Z Z Z h Z $X4X@X@X@X@X@XyX#XZ g Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z A Z Z y _.pX4X4X4X2X2X2X2X7X{.h G F R U U U U U U U U U U p R.:X:X:X:X:XP.P.P.P.K./.b U T T T T T T T T Y Y Y T T T T T T T U I U u.f.i.y.y.t.1.t.,.Z k Z Z Z Z Z Z Z F ", +" Z Z Z Z Z Z Z Z Z & ).iX@X@X@X@X@X3XiX/ % Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G Z Z Z Z $ E.pX4X4X4X4X4X4X2X6X6XP l F U U U U U U U U U U c R -X:X:X:X:X:X:XP.P.P.K.Q.b T T T T T T Y Y Y Y Y Y T T Y Y Y T T T B ( f.p.p.i.y.t.1.y.-.j Z Z Z Z Z Z Z Z Z ", +" Z G Z Z Z Z Z Z Z + R.iX@X@X@X@X@X@XaXU.+ Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G Z Z Z Z r ) pX4X4X4X4X4X4X4X4X7X) r R U U U U U U I l v x U }.,X,X:X:X:X:X:X:XP.P.K.T.b T T T Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y T T n $.g.p.p.i.y.t.t.i.+.u Z Z Z Z Z Z Z Z ; ", +" = F Z Z Z Z Z Z Z $ ) 8X@X@X@X@X@X@XaX_.% Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G G G Z Z f K 4X9X4X4X4X4X4X4X4X9X].r z F F D j i b R _ T.!.,X,X,X,X:X:X:X:X:X:XP.P.P.T I T Y T Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y T V w.g.p.p.i.y.t.t.y.X.u Z Z Z Z Z Z Z G = ", +" # U Z Z Z Z Z Z Z * H @X3X@X@X@X@X@XyX3XS - Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G G G G G h h [.pX4X4X4X4X4X4X4X6X9X^.h i l P ) E.]. Z Z Z Z Z Z Z Z U ", +" @ U Z Z Z Z Z Z Z Z Z Z g . I.aXiXtXtXtXtXtXyXyXyXyXyXaXaXwXO.g + f Z A Z Z G Z G G G G G G G F F F F F F F F R R U U U U U U U U U U U T T T T T T J fXcXlXlXlXlXlXlXzXzXjXjXfXC ; , Z Z Z Z Z Z Z U @ ", +" = U Z Z Z Z Z Z Z Z Z Z h o ^ sXaXtXtXtXtXtXyXyXyXyXyXyXaXaXrXT.S $ $ g Z A Z Z G G G G G G G F G F F F F F F R R U U U U R D v b I U U U T T T T T J < < < < < < < < < < > > > , Z Z Z Z Z Z Z U ; ", +" ; F Z Z Z Z Z Z Z Z Z Z Z . S wXaXyXtXtXtXyXyXyXyXyXyXyXyXaXaXuX~.) Z % $ a h Z G G A G G G G G G G F F F F F F F D k i j P ) ) P v I U U U U U U U U U U U F F G G Z Z Z Z Z Z Z Z Z Z Z Z F Z ", +" Z F Z Z Z Z Z Z Z Z Z Z Z $ & ^.aXaXtXtXyXyXyXyXyXyXyXyXyXyXsXaXaXX,X,X:X:X;X&X&X&XG.G.F.L.L.Q k F F F F F G G Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z F Z ", +" Z F Z Z Z Z Z Z Z Z Z Z Z Z & X / [.aXaXtXyXyXyXyXyXyXyXyXyX4X4X4X4X3X3X3X2X2X4X6X6X9X6X6X,X>X>X=X=X=X%X%X%X%X%X%X%XF.F.F.F.K.U.u G G G G G Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z U Z ", +" ; U Z Z Z Z Z Z Z Z Z Z Z Z Z + & I.rXaXaXyXyXyXyXyXyXyXyX4X3X4X3X3X3X3X2X2X2X2X1X1X1X1X*X*X*X*X*X*X%X%X%X%X%X%XF.F.V.V.V.J.@.u G G G Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z U Z ", +" ; U Z Z Z Z Z Z Z Z Z Z Z Z Z h . : I.rXaXaXsXyXyXyXyXyX3X3X3X3X3X3X3X2X1X2X1X1X1X1X1X*X*X%X%X%X%X%X%X X X X XV.V.V.V.N.H.7 a Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z U ; ", +" @ U G Z Z Z Z Z Z Z Z Z Z Z Z Z : . : ) ].aXaXaXyXtXtX3X3X3X3X3X2X1X2X1X1X1X1X1X*X*X*X*X%X%X%X X X X X X XV.V.B.B.B.K.0.t M Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z U @ ", +" F F Z Z Z Z Z Z Z Z Z Z Z Z Z Z : . % L E.rXaXaXaXyX3X3X3X3X3X3X1X1X1X1X1X+XXX*XXXXXXXXXXX X X X X XB.B.V.D.H.D.0.: a Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z F F ", +" Z U Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h % X : / E.[.yXpXpX9X8X4X1X1X+X1X+X+XXXXXXXXXXXXXXXXX X X X%X&XG.G.V.q.$.5 y a Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z U Z ", +" = U G Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z : $ + - L O.E.`.#X4X8X9X9X9X6X6X6X=X=X=X>X>X>X>X;X&X X|.q.$...4 - + r h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G U = ", +" Z U Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h & + + * 2 Q ) U.R.Q.'./.(.(.(.(.(.'.Q.W.U.7 4 2 * + + y h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z U Z ", +" @ F F Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z ; - & $ + + % & * - - - : * & % + + $ & f h Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z F F @ ", +" ; F G Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z h h h : : : h h h h m Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z F F ; ", +" ; U F Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z F U ; ", +" ; F F Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z F U ; ", +" = G F Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G F F ; ", +" @ Z F F Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z F F Z @ ", +" = Z F F G Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z F F Z = ", +" @ ; Z F F G Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z G F F G ; @ ", +" @ ; Z G F F F G G Z Z Z Z Z Z Z Z Z Z Z G F F F Z Z ; @ ", +" @ = = ; ; Z Z Z Z Z Z ; ; = = @ " +}; diff --git a/share/pixmaps/bitcoinz16.png b/share/pixmaps/bitcoinz16.png new file mode 100644 index 0000000000000000000000000000000000000000..bf27951492bb2bcfeab8400be0d72b69aa969e81 GIT binary patch literal 2663 zcmcImdsGuw8jsW$h=q!)+uCmDOeqz0GLO82(L#a}ZIJR11guVGCJ;?BVP+r!l`hy; z@v#(xpumwLzFI6$s)E8=@liM{t8}S6q`E7J!eSS8rCLw6>rNnGcaL@bXXl(dbLY-? zf8X!>zTdrfaw5W)&-8l5OCS);)P`vy`KPa|O`po&U%&qt=O3Qdur;heFvHK)CJBm4 zek~A8%{4~HadEnLlmu-S;v}u7gm$x)w-yK%FST27Vk5-?dMe3iQGrACUx9#;RDrP) z9ip?UsbphV217+uAq)G)ID&>^3C3vU zjI;%CA>w*El~aK{)3FfD)(Kb(>rNA&FxZY;VN{5?LK-*J>7H$BHcwcyTxc5a#m)Dr zz-)B7m4YKFmQG~|Dm0C7hnx;+Tg^C#W2wV6DSSNQ7VwHnixXl5Gsqt z5T!(-l*kt%m=Zz8Om(~tNSwo;&zL5ShVR8mrs#0_$s zpDvL=Bq0_-3IazV1!_PP1g1xEN;HnPiZSvl2RHp3)y0bB5m7`>;XE%$CdH%>X%I^w z92X%FA~lG_GD3n%^$HMp;#WE!C`tvE||47}P zRw5a-9tcrO)mo-~n=B~RC~oRo`2l!Soz zCWBB)j6({8UJuDBQlh8yI6_I0F%~A{IG-_C_}TtPng4889iPl^)voTu8Nt6yoHCS! z51ir8kbxDs8w7$EGPRoEX!&kkTS9U|s-WrbLyt;J4k@%5+M=7szdu!ZqSI_Kmk+kC z_1Zz)Sie2IEWh$f&nKXKBsg$-_I*4&cyjROl-JsBeSm)Zhnj*FYv78tFt@g&y{q+d zyHx8q(8ug?^d@u-4LCD0%7cf->hsGeV%r0ORJ-*U}cT(yu6W`BcaoF zcZS6JH(YOua-0hB$gTZHh&JWY>5k&WSIygd|LB!f`BGPYe{oB1;S%`%&YiK@Sy}BT zvd*Rg{>||9EyVjs&$}A4VV92ua{?ch9qn9w>{dl04wc+$tK9leMNvt7sk0({ zRk79+xccQ!6(4Ctz~PX@)>hw6J12YsAd zeVps^?tNBX)6jEnYeHe^rM|plLD*%c`Fz&eSyy9|`d|J6T;xISKUcK%Z@E9+Tj-h6 zKUjM@=uh4LK{d5oGEb!+9qQE1qfbtbiC{=96{PHoe2&-W^J`}*u}5iY1>->J+k z_~xd)^wlYur9#+m%?3+v&IK=#dR3ih??uDO;WgX#9*Ig*UXMBP@h4wGUd{Hzsf~4h zk2QhY(pn<=Q`&cWpe}KHv)C^4 zKN3(tRE*`#!^OAjCeK(o)ql$S-WA+K`S6_hu3m?4vG#WLm9s_jiw;(wDO)hdEW)gV z;Ra`yt>)PLn9f%0qCTLlxAnWf9y}dG*gG3#bHC4TI5VTZNjFsTm)Zm1E^hej@#cZU zZ?|r`GvYaICj8lEC*iwIZOCjqH?-=DQ!f@~M&=0T<;$-1Dl+yBRQR=ADPH{E%T7)E z4;S|wSpDG2l<%%3-Mo6|aQs`cP30p#Z$5gs)bX!w4Ap&I{JFz~`Ug0l$+K#H^E-Zqv`@Y}59iE_{1db;nV5==H{`EM>!obIz+@xW2yX ztAISIN5a12J0k8k)K|4f?|m)kM$etTx!z3vTlxj|gD=PNc{8)VV@g& c #14213C", +", c #1B2138", +"< c #1B213E", +"1 c #1C223F", +"2 c #392524", +"3 c #20263F", +"4 c #25263E", +"5 c #262A3A", +"6 c #2A2D3A", +"7 c #2B2C3F", +"8 c #36293F", +"9 c #353231", +"0 c #71663F", +"q c #0B1442", +"w c #091748", +"e c #151D44", +"r c #1A1E47", +"t c #122142", +"y c #1F2540", +"u c #1E2743", +"i c #1A2848", +"p c #1E294A", +"a c #1D294D", +"s c #162657", +"d c #212840", +"f c #232843", +"g c #262B44", +"h c #2C2F43", +"j c #282D45", +"k c #2F2D44", +"l c #2E2F48", +"z c #2D3040", +"x c #2F3341", +"c c #2E3043", +"v c #2F3243", +"b c #2D3345", +"n c #2E3647", +"m c #2D314D", +"M c #29344E", +"N c #353642", +"B c #313244", +"V c #323447", +"C c #3B3C45", +"Z c #3F3E44", +"A c #33384A", +"S c #343849", +"D c #353949", +"F c #333B4F", +"G c #35394C", +"H c #343B4C", +"J c #373E4F", +"K c #212550", +"L c #3D2F51", +"P c #20325B", +"I c #333551", +"U c #3A3D55", +"Y c #3E3D55", +"T c #323158", +"R c #3B315B", +"E c #323E5B", +"W c #3B3C58", +"Q c #4A3C55", +"! c #3F4042", +"~ c #3F4248", +"^ c #3E455A", +"/ c #384261", +"( c #3C657C", +") c #404041", +"_ c #434143", +"` c #454043", +"' c #424145", +"] c #404547", +"[ c #4F4D41", +"{ c #42414D", +"} c #4C4C48", +"| c #4A4B4C", +" . c #504C45", +".. c #594C4F", +"X. c #565448", +"o. c #5F5D4B", +"O. c #404556", +"+. c #4A4B51", +"@. c #4A4C54", +"#. c #4E4D54", +"$. c #534450", +"%. c #595756", +"&. c #57565A", +"*. c #635A4F", +"=. c #675D4D", +"-. c #625E56", +";. c #6B6344", +":. c #656248", +">. c #6B674B", +",. c #746841", +"<. c #676155", +"1. c #6B6A51", +"2. c #555B6D", +"3. c #816F3C", +"4. c #8B7432", +"5. c #927D35", +"6. c #857C47", +"7. c #897F4D", +"8. c #917C40", +"9. c #817B55", +"0. c #847C59", +"q. c #BB8C1C", +"w. c #AD842D", +"e. c #A88B38", +"r. c #AB913D", +"t. c #CE9C13", +"y. c #CDA419", +"u. c #ECA606", +"i. c #EDBD05", +"p. c #F6B001", +"a. c #FFB601", +"s. c #F9B800", +"d. c #FFBA00", +"f. c #E8B611", +"g. c #C3A827", +"h. c #C8A22C", +"j. c #D0A92F", +"k. c #DFB62E", +"l. c #D4B833", +"z. c #DFBC3A", +"x. c #FFC800", +"c. c #FDC713", +"v. c #FED12E", +"b. c #E0CA31", +"n. c #E5D03A", +"m. c #E2D53B", +"M. c #EDDF3D", +"N. c #EAE83C", +"B. c #FFFD3A", +"V. c #88874B", +"C. c #8A814D", +"Z. c #908349", +"A. c #82845D", +"S. c #968A5A", +"D. c #A99B4E", +"F. c #A89751", +"G. c #A39F54", +"H. c #AB9A52", +"J. c #A39F5F", +"K. c #B3A844", +"L. c #B9B242", +"P. c #BAB647", +"I. c #A0A053", +"U. c #B0A955", +"Y. c #CAB44C", +"T. c #D0B742", +"R. c #CFB657", +"E. c #C6BE5D", +"W. c #D2BC5E", +"Q. c #D9BF5C", +"!. c #C9C94D", +"~. c #D6C04E", +"^. c #D5C65B", +"/. c #D8C159", +"(. c #D6C959", +"). c #D5CA5B", +"_. c #EED747", +"`. c #EDD54A", +"'. c #E5C959", +"]. c #E5C95A", +"[. c #EFDA51", +"{. c #E0D55C", +"}. c #EADE5B", +"|. c #E7E743", +" X c #FFEF4F", +".X c #FFFF42", +"XX c #FFFF4D", +"oX c #FEE752", +"OX c #FAE05F", +"+X c #FFED5F", +"@X c #FFFF55", +"#X c #FFFF57", +"$X c #FFF859", +"%X c #E5D262", +"&X c #F2E563", +"*X c #F3E866", +"=X c #FBF167", +"-X c #FFFF61", +";X c #FFF864", +":X c #FFFF66", +">X c #F8F168", +",X c #577582", +" 6X", +"6Xj %X&X4 q ` ,.V S H s t.a.^ 6X", +" 0.;X .y X.K.B.b.3.D J F d.w.t ", +"* W./.< g ~.XX;.l.v.G | P q.u.M ", +"z ].H.3 f D.oX:.M.n.I +.E 4.p.' ", +"x '.F.d h >.#X`.V.N.L.W / 5.s._ ", +", Q.R.1 C } $XG.O..X|.L R g.i.m ", +". S.+XZ v B (.-X@X!.%.,X1XA.0 6 ", +"6XA OX^.- c o.1.I.&.Q 2X5X2.Y 6X", +"6X@ -.:XE.N % r K T $.4X3X..9 6X", +"6X6X= <.*X>XU.7.C.P.Z.( -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

dLp_FuSvQ9Nqj<(DY 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

CY`LmN6@yQEue)juHiH%EG#Xpcd{xN4|$ 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 OEa0(##{!Mk0{;&mUiB{k literal 0 HcmV?d00001 diff --git a/share/rpcauth/README.md b/share/rpcauth/README.md new file mode 100644 index 000000000..1b3acb1da --- /dev/null +++ b/share/rpcauth/README.md @@ -0,0 +1,19 @@ +RPC Tools +--------------------- + +### [RPCAuth](/share/rpcauth) ### + +``` +usage: rpcauth.py [-h] username [password] + +Create login credentials for a JSON-RPC user + +positional arguments: + username the username for authentication + password leave empty to generate a random password or specify "-" to + prompt for password + +optional arguments: + -h, --help show this help message and exit + -j, --json output data in json format + ``` diff --git a/share/rpcauth/rpcauth.py b/share/rpcauth/rpcauth.py new file mode 100755 index 000000000..e8fe48dc1 --- /dev/null +++ b/share/rpcauth/rpcauth.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2021 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://www.opensource.org/licenses/mit-license.php . + +from argparse import ArgumentParser +from getpass import getpass +from secrets import token_hex, token_urlsafe +import hmac +import json + +def generate_salt(size): + """Create size byte hex salt""" + return token_hex(size) + +def generate_password(): + """Create 32 byte b64 password""" + return token_urlsafe(32) + +def password_to_hmac(salt, password): + m = hmac.new(salt.encode('utf-8'), password.encode('utf-8'), 'SHA256') + return m.hexdigest() + +def main(): + parser = ArgumentParser(description='Create login credentials for a JSON-RPC user') + parser.add_argument('username', help='the username for authentication') + parser.add_argument('password', help='leave empty to generate a random password or specify "-" to prompt for password', nargs='?') + parser.add_argument("-j", "--json", help="output to json instead of plain-text", action='store_true') + args = parser.parse_args() + + if not args.password: + args.password = generate_password() + elif args.password == '-': + args.password = getpass() + + # Create 16 byte hex salt + salt = generate_salt(16) + password_hmac = password_to_hmac(salt, args.password) + + if args.json: + odict={'username':args.username, 'password':args.password, 'rpcauth':f'{args.username}:{salt}${password_hmac}'} + print(json.dumps(odict)) + else: + print('String to be appended to bitcoin.conf:') + print(f'rpcauth={args.username}:{salt}${password_hmac}') + print(f'Your password:\n{args.password}') + +if __name__ == '__main__': + main() diff --git a/share/rpcuser/README.md b/share/rpcuser/README.md deleted file mode 100644 index 12a8e6fb0..000000000 --- a/share/rpcuser/README.md +++ /dev/null @@ -1,10 +0,0 @@ -RPC Tools ---------------------- - -### [RPCUser](/share/rpcuser) ### - -Create an RPC user login credential. - -Usage: - - ./rpcuser.py diff --git a/share/rpcuser/rpcuser.py b/share/rpcuser/rpcuser.py deleted file mode 100755 index 384a4ee5b..000000000 --- a/share/rpcuser/rpcuser.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python2 -# Copyright (c) 2015 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or https://www.opensource.org/licenses/mit-license.php . - -import hashlib -import sys -import os -from random import SystemRandom -import base64 -import hmac - -if len(sys.argv) < 2: - sys.stderr.write('Please include username as an argument.\n') - sys.exit(0) - -username = sys.argv[1] - -#This uses os.urandom() underneath -cryptogen = SystemRandom() - -#Create 16 byte hex salt -salt_sequence = [cryptogen.randrange(256) for i in range(16)] -hexseq = list(map(hex, salt_sequence)) -salt = "".join([x[2:] for x in hexseq]) - -#Create 32 byte b64 password -password = base64.urlsafe_b64encode(os.urandom(32)) - -digestmod = hashlib.sha256 - -if sys.version_info.major >= 3: - password = password.decode('utf-8') - digestmod = 'SHA256' - -m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), digestmod) -result = m.hexdigest() - -print("String to be appended to zcash.conf:") -print("rpcauth="+username+":"+salt+"$"+result) -print("Your password:\n"+password) diff --git a/share/setup.nsi.in b/share/setup.nsi.in new file mode 100644 index 000000000..c8455e49f --- /dev/null +++ b/share/setup.nsi.in @@ -0,0 +1,177 @@ +Name "@PACKAGE_NAME@ (64-bit)" + +RequestExecutionLevel highest +SetCompressor /SOLID lzma +SetDateSave off +Unicode true + +# Uncomment these lines when investigating reproducibility errors +#SetCompress off +#SetDatablockOptimize off + +# General Symbol Definitions +!define REGKEY "SOFTWARE\$(^Name)" +!define COMPANY "@PACKAGE_NAME@ project" +!define URL @PACKAGE_URL@ + +# MUI Symbol Definitions +!define MUI_ICON "@abs_top_srcdir@/share/pixmaps/bitcoinz.ico" +!define MUI_WELCOMEFINISHPAGE_BITMAP "@abs_top_srcdir@/share/pixmaps/nsis-wizard.bmp" +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_RIGHT +!define MUI_HEADERIMAGE_BITMAP "@abs_top_srcdir@/share/pixmaps/nsis-header.bmp" +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM +!define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY} +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup +!define MUI_STARTMENUPAGE_DEFAULTFOLDER "@PACKAGE_NAME@" +!define MUI_FINISHPAGE_RUN "$WINDIR\explorer.exe" +!define MUI_FINISHPAGE_RUN_PARAMETERS $INSTDIR\@BITCOIND_NAME@@EXEEXT@ +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico" +!define MUI_UNWELCOMEFINISHPAGE_BITMAP "@abs_top_srcdir@/share/pixmaps/nsis-wizard.bmp" +!define MUI_UNFINISHPAGE_NOAUTOCLOSE + +# Included files +!include Sections.nsh +!include MUI2.nsh +!include x64.nsh + +# Variables +Var StartMenuGroup + +# Installer pages +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_STARTMENU Application $StartMenuGroup +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +# Installer languages +!insertmacro MUI_LANGUAGE English + +# Installer attributes +InstallDir $PROGRAMFILES64\BitcoinZ +CRCCheck force +XPStyle on +BrandingText " " +ShowInstDetails show +VIProductVersion @CLIENT_VERSION_MAJOR@.@CLIENT_VERSION_MINOR@.@CLIENT_VERSION_BUILD@.0 +VIAddVersionKey ProductName "@PACKAGE_NAME@" +VIAddVersionKey ProductVersion "@PACKAGE_VERSION@" +VIAddVersionKey CompanyName "${COMPANY}" +VIAddVersionKey CompanyWebsite "${URL}" +VIAddVersionKey FileVersion "@PACKAGE_VERSION@" +VIAddVersionKey FileDescription "Installer for @PACKAGE_NAME@" +VIAddVersionKey LegalCopyright "Copyright (C) 2009-@COPYRIGHT_YEAR@ @COPYRIGHT_HOLDERS_FINAL@" +InstallDirRegKey HKCU "${REGKEY}" Path +ShowUninstDetails show + +# Installer sections +Section -Main SEC0000 + SetOutPath $INSTDIR + SetOverwrite on + File /oname=COPYING.txt @abs_top_srcdir@/COPYING + File /oname=readme.txt @abs_top_srcdir@/doc/README_windows.txt + File @abs_top_srcdir@/share/examples/bitcoinz.conf + SetOutPath $INSTDIR\share\rpcauth + File @abs_top_srcdir@/share/rpcauth/*.* + SetOutPath $INSTDIR\daemon + File @abs_top_builddir@/release/@BITCOIN_DAEMON_NAME@@EXEEXT@ + File @abs_top_builddir@/release/@BITCOIN_CLI_NAME@@EXEEXT@ + File @abs_top_builddir@/release/@BITCOIN_TX_NAME@@EXEEXT@ + File @abs_top_builddir@/release/@BITCOIN_TEST_NAME@@EXEEXT@ + SetOutPath $INSTDIR + WriteRegStr HKCU "${REGKEY}\Components" Main 1 +SectionEnd + +Section -post SEC0001 + WriteRegStr HKCU "${REGKEY}" Path $INSTDIR + SetOutPath $INSTDIR + WriteUninstaller $INSTDIR\uninstall.exe + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateDirectory $SMPROGRAMS\$StartMenuGroup + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" $INSTDIR\@BITCOIND_NAME@@EXEEXT@ + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\@PACKAGE_NAME@ (testnet, 64-bit).lnk" "$INSTDIR\@BITCOIND_NAME@@EXEEXT@" "-testnet" "$INSTDIR\@BITCOIND_NAME@@EXEEXT@" 1 + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" $INSTDIR\uninstall.exe + !insertmacro MUI_STARTMENU_WRITE_END + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "@PACKAGE_VERSION@" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}" + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\bitcoinz.exe + WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe + WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1 + WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1 + WriteRegStr HKCR "@PACKAGE_TARNAME@" "URL Protocol" "" + WriteRegStr HKCR "@PACKAGE_TARNAME@" "" "URL:BitcoinZ" + WriteRegStr HKCR "@PACKAGE_TARNAME@\DefaultIcon" "" $INSTDIR\@BITCOIND_NAME@@EXEEXT@ + WriteRegStr HKCR "@PACKAGE_TARNAME@\shell\open\command" "" '"$INSTDIR\@BITCOIND_NAME@@EXEEXT@" "%1"' +SectionEnd + +# Macro for selecting uninstaller sections +!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID + Push $R0 + ReadRegStr $R0 HKCU "${REGKEY}\Components" "${SECTION_NAME}" + StrCmp $R0 1 0 next${UNSECTION_ID} + !insertmacro SelectSection "${UNSECTION_ID}" + GoTo done${UNSECTION_ID} +next${UNSECTION_ID}: + !insertmacro UnselectSection "${UNSECTION_ID}" +done${UNSECTION_ID}: + Pop $R0 +!macroend + +# Uninstaller sections +Section /o -un.Main UNSEC0000 + Delete /REBOOTOK $INSTDIR\@BITCOIND_NAME@@EXEEXT@ + Delete /REBOOTOK $INSTDIR\COPYING.txt + Delete /REBOOTOK $INSTDIR\readme.txt + Delete /REBOOTOK $INSTDIR\bitcoinz.conf + RMDir /r /REBOOTOK $INSTDIR\share + RMDir /r /REBOOTOK $INSTDIR\daemon + DeleteRegValue HKCU "${REGKEY}\Components" Main +SectionEnd + +Section -un.post UNSEC0001 + DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\@PACKAGE_NAME@ (testnet, 64-bit).lnk" + Delete /REBOOTOK "$SMSTARTUP\BitcoinZ.lnk" + Delete /REBOOTOK $INSTDIR\uninstall.exe + Delete /REBOOTOK $INSTDIR\debug.log + Delete /REBOOTOK $INSTDIR\db.log + DeleteRegValue HKCU "${REGKEY}" StartMenuGroup + DeleteRegValue HKCU "${REGKEY}" Path + DeleteRegKey /IfEmpty HKCU "${REGKEY}\Components" + DeleteRegKey /IfEmpty HKCU "${REGKEY}" + DeleteRegKey HKCR "@PACKAGE_TARNAME@" + RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup + RmDir /REBOOTOK $INSTDIR + Push $R0 + StrCpy $R0 $StartMenuGroup 1 + StrCmp $R0 ">" no_smgroup +no_smgroup: + Pop $R0 +SectionEnd + +# Installer functions +Function .onInit + InitPluginsDir + ${If} ${RunningX64} + ; disable registry redirection (enable access to 64-bit portion of registry) + SetRegView 64 + ${Else} + MessageBox MB_OK|MB_ICONSTOP "Cannot install 64-bit version on a 32-bit system." + Abort + ${EndIf} +FunctionEnd + +# Uninstaller functions +Function un.onInit + ReadRegStr $INSTDIR HKCU "${REGKEY}" Path + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuGroup + !insertmacro SELECT_UNSECTION Main ${UNSEC0000} +FunctionEnd diff --git a/src/Makefile.am b/src/Makefile.am index e592c210d..e5af66b27 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,10 +5,14 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or https://www.opensource.org/licenses/mit-license.php . +# Pattern rule to print variables, e.g. make print-top_srcdir +print-%: FORCE + @echo '$*'='$($*)' + DIST_SUBDIRS = secp256k1 -AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS) -AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS) +AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(SANITIZER_LDFLAGS) +AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(SANITIZER_CXXFLAGS) AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES = @@ -208,8 +212,8 @@ BITCOIN_CORE_H = \ obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj - @$(top_srcdir)/share/genbuild.sh $(abs_top_builddir)/src/obj/build.h \ - $(abs_top_srcdir) + $(AM_V_GEN) $(top_srcdir)/share/genbuild.sh "$(abs_top_builddir)/src/obj/build.h" \ + "$(abs_top_srcdir)" libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h # server: bitcoinzd @@ -386,10 +390,6 @@ libbitcoin_util_a_SOURCES = \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) -if GLIBC_BACK_COMPAT -libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp -endif - # cli: bitcoinz-cli libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -493,7 +493,6 @@ libzcash_a_SOURCES = \ libzcash_a_CPPFLAGS = $(AM_CPPFLAGS) $(PIC_FLAGS) $(BITCOIN_INCLUDES) libzcash_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -libzcash_a_LDFLAGS = $(AM_LDFLAGS) # zcashconsensus library # if BUILD_BITCOIN_LIBS @@ -514,10 +513,6 @@ libzcashconsensus_la_SOURCES = \ uint256.cpp \ utilstrencodings.cpp -if GLIBC_BACK_COMPAT - libzcashconsensus_la_SOURCES += compat/glibc_compat.cpp -endif - libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) libzcashconsensus_la_LIBADD = $(LIBSECP256K1) libzcashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL @@ -553,12 +548,6 @@ clean-local: $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CXXFLAGS) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $< -check-symbols: $(bin_PROGRAMS) -if GLIBC_BACK_COMPAT - @echo "Checking glibc back compat of [$(bin_PROGRAMS)]..." - $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS) -endif - check-security: $(bin_PROGRAMS) if HARDEN @echo "Checking binary security of [$(bin_PROGRAMS)]..." diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp deleted file mode 100644 index a80137cd8..000000000 --- a/src/compat/glibc_compat.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2009-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" -#endif - -#include - -#if defined(HAVE_SYS_SELECT_H) -#include -#endif - -// Prior to GLIBC_2.14, memcpy was aliased to memmove. -extern "C" void* memmove(void* a, const void* b, size_t c); -extern "C" void* memcpy(void* a, const void* b, size_t c) -{ - return memmove(a, b, c); -} - -extern "C" void __chk_fail(void) __attribute__((__noreturn__)); -extern "C" FDELT_TYPE __fdelt_warn(FDELT_TYPE a) -{ - if (a >= FD_SETSIZE) - __chk_fail(); - return a / __NFDBITS; -} -extern "C" FDELT_TYPE __fdelt_chk(FDELT_TYPE) __attribute__((weak, alias("__fdelt_warn"))); diff --git a/src/random.h b/src/random.h index fa53cb85f..4b815d6cc 100644 --- a/src/random.h +++ b/src/random.h @@ -20,6 +20,34 @@ uint64_t GetRand(uint64_t nMax); int GetRandInt(int nMax); uint256 GetRandHash(); +/** + * Implementation of a C++ Uniform Random Number Generator, backed by GetRandBytes. + */ +class ZcashRandomEngine +{ +public: + typedef uint64_t result_type; + + explicit ZcashRandomEngine() {} + + static constexpr result_type min() { + return std::numeric_limits::min(); + } + static constexpr result_type max() { + return std::numeric_limits::max(); + } + + result_type operator()() { + result_type nRand = 0; + GetRandBytes((unsigned char*)&nRand, sizeof(nRand)); + return nRand; + } + + double entropy() const noexcept { + return 0; + } +}; + /** * Identity function for MappedShuffle, so that elements retain their original order. */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e2c03ba26..794b1219b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -17,6 +17,7 @@ #include "main.h" #include "net.h" #include "policy/policy.h" +#include "random.h" #include "rpc/protocol.h" #include "rpc/server.h" #include "script/script.h" @@ -27,6 +28,7 @@ #include "crypter.h" #include "wallet/asyncrpcoperation_saplingmigration.h" +#include #include #include @@ -3424,7 +3426,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int vector > > vValue; CAmount nTotalLower = 0; - random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + std::shuffle(vCoins.begin(), vCoins.end(), ZcashRandomEngine()); for (const COutput &output : vCoins) { diff --git a/zcutil/fetch-params.sh b/zcutil/fetch-params.sh index 1c7e5426c..e5f65673f 100755 --- a/zcutil/fetch-params.sh +++ b/zcutil/fetch-params.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu From 221b8ae5eb0654b00c238db173fb17bda7244caf Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:27:44 +0000 Subject: [PATCH 009/146] .gitignore: Don't ignore depends patches --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 067be8138..b1eabfdf8 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,11 @@ src/univalue/gen *.json.h *.raw.h +# Only ignore unexpected patches +*.patch +!contrib/guix/patches/*.patch +!depends/patches/**/*.patch + #libtool object files *.lo *.la From 57e730561ddab18db22510b5bba3bea976e305eb Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:28:54 +0000 Subject: [PATCH 010/146] Remove libzcashconsensus --- .gitignore | 2 - Makefile.am | 5 -- configure.ac | 19 +----- libzcashconsensus.pc.in | 10 ---- src/Makefile.am | 32 +--------- src/Makefile.gtest.include | 1 - src/Makefile.test.include | 2 +- src/script/zcashconsensus.cpp | 108 ---------------------------------- src/script/zcashconsensus.h | 70 ---------------------- src/test/script_tests.cpp | 9 --- 10 files changed, 4 insertions(+), 254 deletions(-) delete mode 100644 libzcashconsensus.pc.in delete mode 100644 src/script/zcashconsensus.cpp delete mode 100644 src/script/zcashconsensus.h diff --git a/.gitignore b/.gitignore index b1eabfdf8..f93794526 100644 --- a/.gitignore +++ b/.gitignore @@ -117,8 +117,6 @@ qa/pull-tester/test.*/* contrib/devtools/split-debug.sh -libzcashconsensus.pc - contrib/debian/files contrib/debian/substvars diff --git a/Makefile.am b/Makefile.am index 4d6b9ab4c..ea8c20601 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,11 +16,6 @@ endif .PHONY: deploy FORCE .INTERMEDIATE: $(COVERAGE_INFO) -if BUILD_BITCOIN_LIBS -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = libzcashconsensus.pc -endif - BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT) BITCOIN_TEST_BIN=$(top_builddir)/src/test/$(BITCOIN_TEST_NAME)$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) diff --git a/configure.ac b/configure.ac index 1ba5304b9..515f4ab30 100644 --- a/configure.ac +++ b/configure.ac @@ -402,12 +402,6 @@ AC_ARG_WITH([utils], [build_bitcoin_utils=$withval], [build_bitcoin_utils=yes]) -AC_ARG_WITH([libs], - [AS_HELP_STRING([--with-libs], - [build libraries (default=no)])], - [build_bitcoin_libs=$withval], - [build_bitcoin_libs=no]) - AC_ARG_WITH([daemon], [AS_HELP_STRING([--with-daemon], [build bitcoinzd daemon (default=yes)])], @@ -964,15 +958,6 @@ AC_MSG_CHECKING([whether to build utils (bitcoinz-cli bitcoinz-tx)]) AM_CONDITIONAL([BUILD_BITCOIN_UTILS], [test $build_bitcoin_utils = "yes"]) AC_MSG_RESULT($build_bitcoin_utils) -AC_MSG_CHECKING([whether to build libraries]) -AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test $build_bitcoin_libs = "yes"]) -if test $build_bitcoin_libs = "yes"; then - AC_DEFINE(HAVE_CONSENSUS_LIB, 1, [Define this symbol if the consensus lib has been built]) - AC_CONFIG_FILES([libzcashconsensus.pc:libzcashconsensus.pc.in]) -fi - -AC_MSG_RESULT($build_bitcoin_libs) - AC_LANG_POP if test "$use_ccache" != "no"; then @@ -1031,8 +1016,8 @@ else AC_MSG_RESULT([no]) fi -if test "$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$use_tests$use_tests" = "nonononono"; then - AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --enable-bench or --enable-tests]) +if test "$build_bitcoin_utils$build_bitcoind$use_tests$use_tests" = "nononono"; then + AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-daemon --enable-bench or --enable-tests]) fi AM_CONDITIONAL([TARGET_DARWIN], [test "$TARGET_OS" = "darwin"]) diff --git a/libzcashconsensus.pc.in b/libzcashconsensus.pc.in deleted file mode 100644 index 7391bb9da..000000000 --- a/libzcashconsensus.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Zcash consensus library -Description: Library for the Zcash consensus protocol. -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lzcashconsensus -Cflags: -I${includedir} diff --git a/src/Makefile.am b/src/Makefile.am index e5af66b27..75ce0d319 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -33,9 +33,6 @@ LIBZCASH=libzcash.a if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a endif -if BUILD_BITCOIN_LIBS -LIBZCASH_CONSENSUS=libzcashconsensus.la -endif if ENABLE_WALLET LIBBITCOIN_WALLET=libbitcoin_wallet.a endif @@ -55,7 +52,7 @@ EXTRA_LIBRARIES += \ $(LIBBITCOIN_ZMQ) \ $(LIBZCASH) -lib_LTLIBRARIES = $(LIBZCASH_CONSENSUS) +lib_LTLIBRARIES = noinst_LTLIBRARIES = bin_PROGRAMS = @@ -494,33 +491,6 @@ libzcash_a_SOURCES = \ libzcash_a_CPPFLAGS = $(AM_CPPFLAGS) $(PIC_FLAGS) $(BITCOIN_INCLUDES) libzcash_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -# zcashconsensus library # -if BUILD_BITCOIN_LIBS -include_HEADERS = script/zcashconsensus.h -libzcashconsensus_la_SOURCES = \ - crypto/equihash.cpp \ - crypto/hmac_sha512.cpp \ - crypto/ripemd160.cpp \ - crypto/sha1.cpp \ - crypto/sha256.cpp \ - crypto/sha512.cpp \ - hash.cpp \ - primitives/transaction.cpp \ - pubkey.cpp \ - script/zcashconsensus.cpp \ - script/interpreter.cpp \ - script/script.cpp \ - uint256.cpp \ - utilstrencodings.cpp - -libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) -libzcashconsensus_la_LIBADD = $(LIBSECP256K1) -libzcashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL -libzcashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) - -endif -# - CTAES_DIST = crypto/ctaes/bench.c CTAES_DIST += crypto/ctaes/ctaes.c CTAES_DIST += crypto/ctaes/ctaes.h diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 454b9b3cd..592d0244a 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -77,7 +77,6 @@ bitcoinz_gtest_LDADD = \ $(LIBSECP256K1) bitcoinz_gtest_LDADD += \ - $(LIBZCASH_CONSENSUS) \ $(BOOST_LIBS) \ $(BOOST_UNIT_TEST_FRAMEWORK_LIB) \ $(BDB_LIBS) \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 5b0bbfec0..728fa882f 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -129,7 +129,7 @@ test_test_bitcoinz_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_ $(LIBLEVELDB) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) test_test_bitcoinz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_test_bitcoinz_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS) +test_test_bitcoinz_LDADD += $(BDB_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS) test_test_bitcoinz_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static if ENABLE_ZMQ diff --git a/src/script/zcashconsensus.cpp b/src/script/zcashconsensus.cpp deleted file mode 100644 index ec6091551..000000000 --- a/src/script/zcashconsensus.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#include "zcashconsensus.h" - -#include "consensus/upgrades.h" -#include "primitives/transaction.h" -#include "pubkey.h" -#include "script/interpreter.h" -#include "version.h" - -namespace { - -/** A class that deserializes a single CTransaction one time. */ -class TxInputStream -{ -public: - TxInputStream(int nTypeIn, int nVersionIn, const unsigned char *txTo, size_t txToLen) : - m_type(nTypeIn), - m_version(nVersionIn), - m_data(txTo), - m_remaining(txToLen) - {} - - void read(char* pch, size_t nSize) - { - if (nSize > m_remaining) - throw std::ios_base::failure(std::string(__func__) + ": end of data"); - - if (pch == NULL) - throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer"); - - if (m_data == NULL) - throw std::ios_base::failure(std::string(__func__) + ": bad source buffer"); - - memcpy(pch, m_data, nSize); - m_remaining -= nSize; - m_data += nSize; - } - - template - TxInputStream& operator>>(T& obj) - { - ::Unserialize(*this, obj); - return *this; - } - - int GetVersion() const { return m_version; } - int GetType() const { return m_type; } -private: - const int m_type; - const int m_version; - const unsigned char* m_data; - size_t m_remaining; -}; - -inline int set_error(zcashconsensus_error* ret, zcashconsensus_error serror) -{ - if (ret) - *ret = serror; - return 0; -} - -struct ECCryptoClosure -{ - ECCVerifyHandle handle; -}; - -ECCryptoClosure instance_of_eccryptoclosure; -} - -int zcashconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, - const unsigned char *txTo , unsigned int txToLen, - unsigned int nIn, unsigned int flags, zcashconsensus_error* err) -{ - try { - TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); - CTransaction tx; - stream >> tx; - if (nIn >= tx.vin.size()) - return set_error(err, zcashconsensus_ERR_TX_INDEX); - if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) - return set_error(err, zcashconsensus_ERR_TX_SIZE_MISMATCH); - - // Regardless of the verification result, the tx did not error. - set_error(err, zcashconsensus_ERR_OK); - PrecomputedTransactionData txdata(tx); - CAmount am(0); - uint32_t consensusBranchId = SPROUT_BRANCH_ID; - return VerifyScript( - tx.vin[nIn].scriptSig, - CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), - flags, - TransactionSignatureChecker(&tx, nIn, am, txdata), - consensusBranchId, - NULL); - } catch (const std::exception&) { - return set_error(err, zcashconsensus_ERR_TX_DESERIALIZE); // Error deserializing - } -} - -unsigned int zcashconsensus_version() -{ - // Just use the API version for now - return ZCASHCONSENSUS_API_VER; -} diff --git a/src/script/zcashconsensus.h b/src/script/zcashconsensus.h deleted file mode 100644 index a9176dbec..000000000 --- a/src/script/zcashconsensus.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef BITCOIN_ZCASHCONSENSUS_H -#define BITCOIN_ZCASHCONSENSUS_H - -#include - -#if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" - #if defined(_WIN32) - #if defined(DLL_EXPORT) - #if defined(HAVE_FUNC_ATTRIBUTE_DLLEXPORT) - #define EXPORT_SYMBOL __declspec(dllexport) - #else - #define EXPORT_SYMBOL - #endif - #endif - #elif defined(HAVE_FUNC_ATTRIBUTE_VISIBILITY) - #define EXPORT_SYMBOL __attribute__ ((visibility ("default"))) - #endif -#elif defined(MSC_VER) && !defined(STATIC_LIBZCASHCONSENSUS) - #define EXPORT_SYMBOL __declspec(dllimport) -#endif - -#ifndef EXPORT_SYMBOL - #define EXPORT_SYMBOL -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#define ZCASHCONSENSUS_API_VER 0 - -typedef enum zcashconsensus_error_t -{ - zcashconsensus_ERR_OK = 0, - zcashconsensus_ERR_TX_INDEX, - zcashconsensus_ERR_TX_SIZE_MISMATCH, - zcashconsensus_ERR_TX_DESERIALIZE, -} zcashconsensus_error; - -/** Script verification flags */ -enum -{ - zcashconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, - zcashconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts - zcashconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) -}; - -/// Returns 1 if the input nIn of the serialized transaction pointed to by -/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under -/// the additional constraints specified by flags. -/// If not NULL, err will contain an error/success code for the operation -EXPORT_SYMBOL int zcashconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, - const unsigned char *txTo , unsigned int txToLen, - unsigned int nIn, unsigned int flags, zcashconsensus_error* err); - -EXPORT_SYMBOL unsigned int zcashconsensus_version(); - -#ifdef __cplusplus -} // extern "C" -#endif - -#undef EXPORT_SYMBOL - -#endif // BITCOIN_ZCASHCONSENSUS_H diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 6baa2ce75..4a9145af9 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -16,10 +16,6 @@ #include "util.h" #include "test/test_bitcoin.h" -#if defined(HAVE_CONSENSUS_LIB) -#include "script/zcashconsensus.h" -#endif - #include #include #include @@ -96,11 +92,6 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, ui CMutableTransaction tx2 = tx; BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), consensusBranchId, &err) == expect, message); BOOST_CHECK_MESSAGE(expect == (err == SCRIPT_ERR_OK), std::string(ScriptErrorString(err)) + ": " + message); -#if defined(HAVE_CONSENSUS_LIB) - CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); - stream << tx2; - BOOST_CHECK_MESSAGE(zcashconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); -#endif } void static NegateSignatureS(std::vector& vchSig) { From 6518c838a9fccb6f8f90e4ab48ba06f2500b166d Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:29:19 +0000 Subject: [PATCH 011/146] Cleanup: get the definition of ENABLE_VIRTUAL_TERMINAL_PROCESSING from wincon.h --- src/metrics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/metrics.cpp b/src/metrics.cpp index e9ef4038d..6013f65ef 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -20,6 +20,7 @@ #include #ifdef WIN32 #include +#include #else #include #endif @@ -492,7 +493,6 @@ int printInitMessage() } #ifdef WIN32 -#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 bool enableVTMode() { From 51fd06d762d071586d5c23ab2881c87c40b2a775 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:29:58 +0000 Subject: [PATCH 012/146] build: Set minimum required Windows version to Windows 7 --- src/compat.h | 7 ------- src/init.cpp | 11 +---------- src/net.cpp | 11 ----------- src/netbase.cpp | 4 ---- src/support/pagelocker.cpp | 5 ----- src/util.cpp | 11 ----------- 6 files changed, 1 insertion(+), 48 deletions(-) diff --git a/src/compat.h b/src/compat.h index 4d6027c67..5cab5a8c7 100644 --- a/src/compat.h +++ b/src/compat.h @@ -11,13 +11,6 @@ #endif #ifdef WIN32 -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0501 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN 1 -#endif #ifndef NOMINMAX #define NOMINMAX #endif diff --git a/src/init.cpp b/src/init.cpp index 18aa9e986..1f87870b0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -941,16 +941,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #endif #ifdef WIN32 // Enable Data Execution Prevention (DEP) - // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 - // A failure is non-critical and needs no further attention! -#ifndef PROCESS_DEP_ENABLE - // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), - // which is not correct. Can be removed, when GCCs winbase.h is fixed! -#define PROCESS_DEP_ENABLE 0x00000001 -#endif - typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); - PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); - if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); + SetProcessDEPPolicy(PROCESS_DEP_ENABLE); #endif if (!SetupNetworking()) diff --git a/src/net.cpp b/src/net.cpp index 77c4fda6b..13f3ebbd0 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -34,17 +34,6 @@ #define MSG_NOSIGNAL 0 #endif -// Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h. -// Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version. -#ifdef WIN32 -#ifndef PROTECTION_LEVEL_UNRESTRICTED -#define PROTECTION_LEVEL_UNRESTRICTED 10 -#endif -#ifndef IPV6_PROTECTION_LEVEL -#define IPV6_PROTECTION_LEVEL 23 -#endif -#endif - using namespace std; namespace { diff --git a/src/netbase.cpp b/src/netbase.cpp index 7d2021805..0a2992cf3 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -123,11 +123,7 @@ bool static LookupIntern(const char *pszName, std::vector& vIP, unsign aiHint.ai_socktype = SOCK_STREAM; aiHint.ai_protocol = IPPROTO_TCP; aiHint.ai_family = AF_UNSPEC; -#ifdef WIN32 - aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST; -#else aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST; -#endif struct addrinfo *aiRes = NULL; #ifdef HAVE_GETADDRINFO_A diff --git a/src/support/pagelocker.cpp b/src/support/pagelocker.cpp index 47a98ba5d..c65ba5edd 100644 --- a/src/support/pagelocker.cpp +++ b/src/support/pagelocker.cpp @@ -9,11 +9,6 @@ #endif #ifdef WIN32 -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0501 -#define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif diff --git a/src/util.cpp b/src/util.cpp index 35774b7b3..fc90a2f71 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -50,17 +50,6 @@ #pragma warning(disable:4717) #endif -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0501 - -#ifdef _WIN32_IE -#undef _WIN32_IE -#endif -#define _WIN32_IE 0x0501 - -#define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif From 0a901c7f450124f7139f8621ff6dcfd6b45d0413 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:30:29 +0000 Subject: [PATCH 013/146] rpc: Add dumpbootstrap --- doc/bootstrap.md | 44 ++++++++++++++++++++++++++ src/rpc/blockchain.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++ src/rpc/client.cpp | 2 ++ 3 files changed, 117 insertions(+) create mode 100644 doc/bootstrap.md diff --git a/doc/bootstrap.md b/doc/bootstrap.md new file mode 100644 index 000000000..50ccb7f08 --- /dev/null +++ b/doc/bootstrap.md @@ -0,0 +1,44 @@ +### Bootstrap the Blockchain Synchronization + +Normally the BitcoinZ client will download the transaction and network information, called the blockchain, from the network by syncing with the other clients. This process can take quite some time as the [BitcoinZ blockchain](https://explorer.btcz.rocks/) is growing bigger and bigger for each day. Luckily there is a safe and fast way to speed up this process. We'll show you how to bootstrap your blockchain to bring your client up to speed in just a few simple steps. + +### Requirements + +- A fresh install of the BitcoinZ client software. + +### Download the blockchain via GitHub + +BitcoinZ offers an [github file](https://github.com/btcz/bitcoinz) for bootstrapping purposes that is updated often. + +### Importing the blockchain +Exit the BitcoinZ client software if you have it running. Be sure not to have an actively used wallet in use. We are going to copy the download of the blockchain to the BitcoinZ client data directory. You should run the client software at least once so it can generate the data directory. Copy the downloaded bootstrap.dat file into the BitcoinZ data folder. + +**For Windows users:** +Open explorer, and type into the address bar: + + %APPDATA%\BitcoinZ + +This will open up the data folder. It should look like the image below. Copy over the bootstrap.dat from your download folder to this directory. + +**For OSX users:** +Open Finder by pressing Press [shift] + [cmd] + [g] and enter: + + ~/Library/Application Support/BitcoinZ/ + +**For Linux users:** +The directory is hidden in your User folder. Go to: + + ~/.bitcoinz/ + +### Importing the blockchain +Now start the BitcoinZ client software. It should show "Importing blocks from disk" like the image below. + +Wait until the import finishes. This take some time (from some hours to a couple of days depending on your hardware) while the BitcoinZ client verifies the imported blockchain. + +When the import finishes, the client will download the last missing days not covered by the import. You should see the status changed to "Synchronizing with network...". + +Congratulations you have successfully imported the blockchain! + +### Is this safe? + +Yes, the above method is safe. The download contains only raw blockchain data and the client verifies this on import. Do not download the blockchain from unofficial sources, especially if they provide `*.rev` and `*.sst` files. These files are not verified and can contain malicious edits. diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 6055d3738..216fa4e1d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -7,6 +7,7 @@ #include "chain.h" #include "chainparams.h" #include "checkpoints.h" +#include "clientversion.h" #include "consensus/validation.h" #include "experimental_features.h" #include "key_io.h" @@ -1416,6 +1417,75 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp) return NullUniValue; } +UniValue dumpbootstrap(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw std::runtime_error( + "dumpbootstrap [endblock] [startblock=0]\n" + "\nCreates a bootstrap format block dump of the blockchain in destination, which can be a directory or a path with filename, up to the given endblock number.\n" + "\nArguments:\n" + "1. destination (string, required) Pathname of file to write to. If a directory is use, 'bootstrap.dat' is created in that directory.\n" + "2. endblock (numeric, optional, defaults to the last block in the active chain) Height of last block to dump.\n" + "3. startblock (numeric, optional, default=0) Height of first block to dump.\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("dumpbootstrap", "\"/tmp\"") + + HelpExampleRpc("dumpbootstrap", "\"/tmp\"") + ); + + LOCK(cs_main); + + std::string strDest = params[0].get_str(); + int nEndBlock = chainActive.Height(); + int nStartBlock = 0; + + if (params.size() > 1) { + nEndBlock = params[1].getInt(); + if (nEndBlock < 0 || nEndBlock > chainActive.Height()) + throw std::runtime_error("End block number out of range."); + } + + if (params.size() > 2) { + nStartBlock = params[2].getInt(); + if (nStartBlock < 0 || nStartBlock > nEndBlock) + throw std::runtime_error("Start block number out of range."); + } + + boost::filesystem::path pathDest(strDest); + if (boost::filesystem::is_directory(pathDest)) + pathDest /= "bootstrap.dat"; + + try { + FILE* file = fopen(pathDest.string().c_str(), "wb"); + if (!file) + throw JSONRPCError(RPC_MISC_ERROR, "Error: Could not open bootstrap file for writing."); + + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + throw JSONRPCError(RPC_MISC_ERROR, "Error: Could not open bootstrap file for writing."); + + for (int nHeight = nStartBlock; nHeight <= nEndBlock; nHeight++) + { + CBlock block; + CBlockIndex* pblockindex = chainActive[nHeight]; + + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + + if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + fileout << FLATDATA(Params().MessageStart()) + << (unsigned int)GetSerializeSize(fileout, block) + << block; + } + } catch(const boost::filesystem::filesystem_error &e) { + throw JSONRPCError(RPC_MISC_ERROR, "Error: Bootstrap dump failed!"); + } + + return strprintf("dumped %d blocks from %d to %d into %s", nEndBlock - nStartBlock + 1, nStartBlock, nEndBlock, pathDest); +} + static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- @@ -1433,6 +1503,7 @@ static const CRPCCommand commands[] = { "blockchain", "gettxout", &gettxout, true }, { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, { "blockchain", "verifychain", &verifychain, true }, + { "blockchain", "dumpbootstrap", &dumpbootstrap, true }, // insightexplorer { "blockchain", "getblockdeltas", &getblockdeltas, false }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 950e3269c..774aac80a 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -47,6 +47,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getbalance", 1 }, { "getbalance", 2 }, { "getblockhash", 0 }, + { "dumpbootstrap", 1 }, + { "dumpbootstrap", 2 }, { "move", 2 }, { "move", 3 }, { "sendfrom", 2 }, From 26825079175a31ca7d206baef02d6f651724b06a Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 13:49:17 +0000 Subject: [PATCH 014/146] Bring the librustzcash 0.2.0 crate into this repository --- .cargo/config.toml.in | 29 + .gitignore | 4 + configure.ac | 39 +- contrib/guix/libexec/build.sh | 8 +- depends/Makefile | 1 + depends/config.site.in | 6 + depends/packages/crate_aes.mk | 4 +- depends/packages/crate_aes_soft.mk | 4 +- depends/packages/crate_aesni.mk | 4 +- .../{crate_bitflags.mk => crate_arrayref.mk} | 8 +- depends/packages/crate_arrayvec.mk | 4 +- ...rate_stream_cipher.mk => crate_autocfg.mk} | 8 +- depends/packages/crate_bellman.mk | 4 +- ...rate_fuchsia_zircon.mk => crate_bigint.mk} | 8 +- depends/packages/crate_blake2_rfc.mk | 16 - ...ia_zircon_sys.mk => crate_blake2b_simd.mk} | 8 +- depends/packages/crate_blake2s_simd.mk | 15 + depends/packages/crate_block_buffer.mk | 15 + depends/packages/crate_block_cipher_trait.mk | 4 +- depends/packages/crate_block_padding.mk | 15 + depends/packages/crate_byte_tools.mk | 4 +- depends/packages/crate_byteorder.mk | 4 +- depends/packages/crate_c2_chacha.mk | 15 + depends/packages/crate_cfg_if.mk | 15 + depends/packages/crate_constant_time_eq.mk | 4 +- depends/packages/crate_crossbeam.mk | 4 +- depends/packages/crate_crossbeam_channel.mk | 15 + depends/packages/crate_crossbeam_deque.mk | 15 + depends/packages/crate_crossbeam_epoch.mk | 15 + depends/packages/crate_crossbeam_queue.mk | 15 + depends/packages/crate_crossbeam_utils.mk | 15 + depends/packages/crate_crunchy.mk | 15 + depends/packages/crate_crypto_api.mk | 15 + .../packages/crate_crypto_api_chachapoly.mk | 15 + depends/packages/crate_digest.mk | 4 +- depends/packages/crate_directories.mk | 15 + depends/packages/crate_fake_simd.mk | 15 + depends/packages/crate_ff.mk | 15 + depends/packages/crate_ff_derive.mk | 15 + depends/packages/crate_fpe.mk | 4 +- depends/packages/crate_futures.mk | 4 +- depends/packages/crate_generic_array.mk | 4 +- depends/packages/crate_getrandom.mk | 15 + depends/packages/crate_group.mk | 15 + depends/packages/crate_hex.mk | 15 + depends/packages/crate_lazy_static.mk | 4 +- depends/packages/crate_libc.mk | 4 +- depends/packages/crate_log.mk | 15 + depends/packages/crate_memoffset.mk | 15 + depends/packages/crate_nodrop.mk | 4 +- depends/packages/crate_num_bigint.mk | 4 +- depends/packages/crate_num_cpus.mk | 4 +- depends/packages/crate_num_integer.mk | 4 +- depends/packages/crate_num_traits.mk | 4 +- depends/packages/crate_opaque_debug.mk | 4 +- depends/packages/crate_pairing.mk | 4 +- depends/packages/crate_ppv_lite86.mk | 15 + depends/packages/crate_proc_macro2.mk | 15 + depends/packages/crate_quote.mk | 15 + depends/packages/crate_rand.mk | 4 +- depends/packages/crate_rand_chacha.mk | 15 + depends/packages/crate_rand_core.mk | 15 + depends/packages/crate_rand_hc.mk | 15 + depends/packages/crate_rand_xorshift.mk | 15 + depends/packages/crate_rustc_version.mk | 15 + depends/packages/crate_sapling_crypto.mk | 16 - depends/packages/crate_scopeguard.mk | 15 + depends/packages/crate_semver.mk | 15 + depends/packages/crate_semver_parser.mk | 15 + depends/packages/crate_sha2.mk | 15 + depends/packages/crate_syn.mk | 15 + depends/packages/crate_typenum.mk | 4 +- depends/packages/crate_unicode_xid.mk | 15 + depends/packages/crate_wasi.mk | 15 + depends/packages/crate_winapi.mk | 4 +- depends/packages/crate_zcash_history.mk | 15 + depends/packages/crate_zcash_primitives.mk | 15 + depends/packages/crate_zcash_proofs.mk | 15 + depends/packages/crate_zip32.mk | 16 - depends/packages/librustzcash.mk | 80 - depends/packages/packages.mk | 55 +- depends/packages/rust.mk | 33 + ...0001-Start-using-cargo-clippy-for-CI.patch | 42 - depends/patches/librustzcash/guix.config | 12 - .../librustzcash/remove-dev-dependencies.diff | 630 -------- src/Makefile.am | 19 +- src/Makefile.bench.include | 5 +- src/Makefile.gtest.include | 3 +- src/Makefile.test.include | 2 +- src/rustzcash/.gitignore | 2 + src/rustzcash/Cargo.lock | 706 +++++++++ src/rustzcash/Cargo.toml | 38 + src/rustzcash/README.md | 24 + src/rustzcash/include/librustzcash.h | 339 +++++ src/rustzcash/src/rustzcash.rs | 1342 +++++++++++++++++ src/rustzcash/src/tests/key_agreement.rs | 76 + src/rustzcash/src/tests/key_components.rs | 731 +++++++++ src/rustzcash/src/tests/mmr.rs | 225 +++ src/rustzcash/src/tests/mod.rs | 97 ++ src/rustzcash/src/tests/notes.rs | 673 +++++++++ src/rustzcash/src/tests/signatures.rs | 514 +++++++ src/transaction_builder.cpp | 8 +- src/wallet/gtest/test_wallet_zkeys.cpp | 1 - src/zcbenchmarks.cpp | 7 +- 104 files changed, 5632 insertions(+), 903 deletions(-) create mode 100644 .cargo/config.toml.in rename depends/packages/{crate_bitflags.mk => crate_arrayref.mk} (69%) rename depends/packages/{crate_stream_cipher.mk => crate_autocfg.mk} (67%) rename depends/packages/{crate_fuchsia_zircon.mk => crate_bigint.mk} (67%) delete mode 100644 depends/packages/crate_blake2_rfc.mk rename depends/packages/{crate_fuchsia_zircon_sys.mk => crate_blake2b_simd.mk} (66%) create mode 100644 depends/packages/crate_blake2s_simd.mk create mode 100644 depends/packages/crate_block_buffer.mk create mode 100644 depends/packages/crate_block_padding.mk create mode 100644 depends/packages/crate_c2_chacha.mk create mode 100644 depends/packages/crate_cfg_if.mk create mode 100644 depends/packages/crate_crossbeam_channel.mk create mode 100644 depends/packages/crate_crossbeam_deque.mk create mode 100644 depends/packages/crate_crossbeam_epoch.mk create mode 100644 depends/packages/crate_crossbeam_queue.mk create mode 100644 depends/packages/crate_crossbeam_utils.mk create mode 100644 depends/packages/crate_crunchy.mk create mode 100644 depends/packages/crate_crypto_api.mk create mode 100644 depends/packages/crate_crypto_api_chachapoly.mk create mode 100644 depends/packages/crate_directories.mk create mode 100644 depends/packages/crate_fake_simd.mk create mode 100644 depends/packages/crate_ff.mk create mode 100644 depends/packages/crate_ff_derive.mk create mode 100644 depends/packages/crate_getrandom.mk create mode 100644 depends/packages/crate_group.mk create mode 100644 depends/packages/crate_hex.mk create mode 100644 depends/packages/crate_log.mk create mode 100644 depends/packages/crate_memoffset.mk create mode 100644 depends/packages/crate_ppv_lite86.mk create mode 100644 depends/packages/crate_proc_macro2.mk create mode 100644 depends/packages/crate_quote.mk create mode 100644 depends/packages/crate_rand_chacha.mk create mode 100644 depends/packages/crate_rand_core.mk create mode 100644 depends/packages/crate_rand_hc.mk create mode 100644 depends/packages/crate_rand_xorshift.mk create mode 100644 depends/packages/crate_rustc_version.mk delete mode 100644 depends/packages/crate_sapling_crypto.mk create mode 100644 depends/packages/crate_scopeguard.mk create mode 100644 depends/packages/crate_semver.mk create mode 100644 depends/packages/crate_semver_parser.mk create mode 100644 depends/packages/crate_sha2.mk create mode 100644 depends/packages/crate_syn.mk create mode 100644 depends/packages/crate_unicode_xid.mk create mode 100644 depends/packages/crate_wasi.mk create mode 100644 depends/packages/crate_zcash_history.mk create mode 100644 depends/packages/crate_zcash_primitives.mk create mode 100644 depends/packages/crate_zcash_proofs.mk delete mode 100644 depends/packages/crate_zip32.mk delete mode 100644 depends/packages/librustzcash.mk delete mode 100644 depends/patches/librustzcash/0001-Start-using-cargo-clippy-for-CI.patch delete mode 100644 depends/patches/librustzcash/guix.config delete mode 100644 depends/patches/librustzcash/remove-dev-dependencies.diff create mode 100644 src/rustzcash/.gitignore create mode 100644 src/rustzcash/Cargo.lock create mode 100644 src/rustzcash/Cargo.toml create mode 100644 src/rustzcash/README.md create mode 100644 src/rustzcash/include/librustzcash.h create mode 100644 src/rustzcash/src/rustzcash.rs create mode 100644 src/rustzcash/src/tests/key_agreement.rs create mode 100644 src/rustzcash/src/tests/key_components.rs create mode 100644 src/rustzcash/src/tests/mmr.rs create mode 100644 src/rustzcash/src/tests/mod.rs create mode 100644 src/rustzcash/src/tests/notes.rs create mode 100644 src/rustzcash/src/tests/signatures.rs diff --git a/.cargo/config.toml.in b/.cargo/config.toml.in new file mode 100644 index 000000000..3030a8a02 --- /dev/null +++ b/.cargo/config.toml.in @@ -0,0 +1,29 @@ +[source.crates-io] +replace-with = "vendored-sources" + +[source."https://github.com/gtank/blake2-rfc"] +git = "https://github.com/gtank/blake2-rfc" +rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" +replace-with = "vendored-sources" + +[source."https://github.com/zcash-hackworks/sapling-crypto"] +git = "https://github.com/zcash-hackworks/sapling-crypto" +rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" +replace-with = "vendored-sources" + +[source."https://github.com/zcash-hackworks/zip32"] +git = "https://github.com/zcash-hackworks/zip32" +rev = "176470ef41583b5bd0bd749bd1b61d417aa8ec79" +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "@RUST_VENDORED_SOURCES@" + +[target.x86_64-unknown-linux-gnu] +linker = "gcc" + +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" + +[target.x86_64-pc-windows-gnu] +linker = "x86_64-w64-mingw32-gcc" diff --git a/.gitignore b/.gitignore index f93794526..2b49146e9 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,10 @@ src/univalue/gen *.lo *.la +# Rust +.cargo/config.toml +src/rustzcash/target/ + # Compilation src/Makefile doc/man/Makefile diff --git a/configure.ac b/configure.ac index 515f4ab30..f6a8c38ef 100644 --- a/configure.ac +++ b/configure.ac @@ -408,6 +408,30 @@ AC_ARG_WITH([daemon], [build_bitcoind=$withval], [build_bitcoind=yes]) +AC_PATH_PROG([CARGO], [cargo], [none]) +if test "$CARGO" = "none"; then + AC_MSG_ERROR("cargo required to enable rust support.") +fi + +AC_PATH_PROG([RUSTC], [rustc], [none]) +if test "$RUSTC" = "none"; then + AC_MSG_ERROR("rustc required to enable rust support.") +fi + +CARGO_FLAGS="$CARGO_FLAGS --lib --verbose --release --locked" + +case $host in + ${host_cpu}-w64-mingw*) RUST_TARGET=${host_cpu}-pc-windows-gnu ;; + ${host_cpu}-pc-linux*) RUST_TARGET=${host_cpu}-unknown-${host_os} ;; + ${host_cpu}-apple-darwin*) RUST_TARGET=${host_cpu}-apple-darwin ;; + *) RUST_TARGET=${host} ;; +esac + +if test x$RUST_VENDORED_SOURCES != x; then + AC_CONFIG_FILES([.cargo/config.toml:.cargo/config.toml.in]) + CARGO_FLAGS="$CARGO_FLAGS --offline" +fi + case $host in *mingw*) TARGET_OS=windows @@ -939,10 +963,14 @@ fi AM_CONDITIONAL([ENABLE_ZMQ], [test "$use_zmq" = "yes"]) -RUST_LIBS="-lrustzcash" +AC_SEARCH_LIBS([dlsym],[dl]) + +RUST_LIBS="" case $host in *mingw*) ;; + *darwin*) + ;; *) RUST_LIBS="$RUST_LIBS -ldl" ;; @@ -1076,6 +1104,9 @@ AC_SUBST(HAVE_O_CLOEXEC) AC_SUBST(HAVE_BUILTIN_PREFETCH) AC_SUBST(HAVE_MM_PREFETCH) AC_SUBST(HAVE_STRONG_GETAUXVAL) +AC_SUBST(CARGO_FLAGS) +AC_SUBST(RUST_TARGET) +AC_SUBST(RUST_VENDORED_SOURCES) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/Info.plist src/test/buildenv.py]) AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])]) @@ -1139,3 +1170,9 @@ echo " LDFLAGS = $PTHREAD_LIBS $HARDENED_LDFLAGS $SANITIZER_LDFLAGS $LD echo " AR = $AR" echo " ARFLAGS = $ARFLAGS" echo +echo " CARGO = $CARGO" +echo " CARGO_FLAGS = $CARGO_FLAGS" +echo +echo " RUSTC = $RUSTC" +echo " RUST_TARGET = $RUST_TARGET" +echo diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh index 27b3585c3..70a4c02e1 100755 --- a/contrib/guix/libexec/build.sh +++ b/contrib/guix/libexec/build.sh @@ -165,8 +165,11 @@ export TZ="UTC" # Depends Building # #################### +# Export LIBRARY_PATH and LD_LIBRARY_PATH +export LD_LIBRARY_PATH=$LIBRARY_PATH:$GUIX_ENVIRONMENT/lib +export LIBRARY_PATH=$LD_LIBRARY_PATH + # Build the depends tree, overriding variables that assume multilib gcc -export LD_LIBRARY_PATH=$GUIX_ENVIRONMENT/lib:$LIBRARY_PATH make -C depends --jobs="$JOBS" HOST="$HOST" \ WITH_GUIX=1 \ ${V:+V=1} \ @@ -253,6 +256,9 @@ mkdir -p "$DISTSRC" sed -i.old 's/-lstdc++ //g' config.status libtool + # Export LIBRARY_PATH + export LIBRARY_PATH=$LD_LIBRARY_PATH + # Build BitcoinZ make --jobs="$JOBS" ${V:+V=1} diff --git a/depends/Makefile b/depends/Makefile index e853d0e08..2279ccb9d 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -209,6 +209,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ -e 's|@no_zmq@|$(NO_ZMQ)|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \ + -e 's|@rust_target@|$(call rust_target,rust,$(canonical_host),$(host_os))|' \ -e 's|@lto@|$(LTO)|' \ -e 's|@no_harden@|$(NO_HARDEN)|' \ -e 's|@debug@|$(DEBUG)|' \ diff --git a/depends/config.site.in b/depends/config.site.in index 8fe9e4e3b..e3bbd711d 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -97,3 +97,9 @@ fi if test -n "@LDFLAGS@"; then LDFLAGS="@LDFLAGS@ ${LDFLAGS}" fi + +CARGO="${depends_prefix}/native/bin/cargo" +RUSTC="${depends_prefix}/native/bin/rustc" + +RUST_TARGET="@rust_target@" +RUST_VENDORED_SOURCES="$depends_prefix/vendored-sources" diff --git a/depends/packages/crate_aes.mk b/depends/packages/crate_aes.mk index e53d72679..0e2a29392 100644 --- a/depends/packages/crate_aes.mk +++ b/depends/packages/crate_aes.mk @@ -1,9 +1,9 @@ package=crate_aes $(package)_crate_name=aes -$(package)_version=0.2.0 +$(package)_version=0.3.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=e6fb1737cdc8da3db76e90ca817a194249a38fcb500c2e6ecec39b29448aa873 +$(package)_sha256_hash=54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_aes_soft.mk b/depends/packages/crate_aes_soft.mk index bd5a9b585..5dcd153ee 100644 --- a/depends/packages/crate_aes_soft.mk +++ b/depends/packages/crate_aes_soft.mk @@ -1,9 +1,9 @@ package=crate_aes_soft $(package)_crate_name=aes-soft -$(package)_version=0.2.0 +$(package)_version=0.3.3 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=67cc03b0a090a05cb01e96998a01905d7ceedce1bc23b756c0bb7faa0682ccb1 +$(package)_sha256_hash=cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_aesni.mk b/depends/packages/crate_aesni.mk index ccd6bb8c9..dfd93bce2 100644 --- a/depends/packages/crate_aesni.mk +++ b/depends/packages/crate_aesni.mk @@ -1,9 +1,9 @@ package=crate_aesni $(package)_crate_name=aesni -$(package)_version=0.4.1 +$(package)_version=0.6.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=6810b7fb9f2bb4f76f05ac1c170b8dde285b6308955dc3afd89710268c958d9e +$(package)_sha256_hash=2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_bitflags.mk b/depends/packages/crate_arrayref.mk similarity index 69% rename from depends/packages/crate_bitflags.mk rename to depends/packages/crate_arrayref.mk index 844ec37e8..186f5e77b 100644 --- a/depends/packages/crate_bitflags.mk +++ b/depends/packages/crate_arrayref.mk @@ -1,9 +1,9 @@ -package=crate_bitflags -$(package)_crate_name=bitflags -$(package)_version=1.0.1 +package=crate_arrayref +$(package)_crate_name=arrayref +$(package)_version=0.3.5 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf +$(package)_sha256_hash=0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_arrayvec.mk b/depends/packages/crate_arrayvec.mk index 7de373351..faa3b4f55 100644 --- a/depends/packages/crate_arrayvec.mk +++ b/depends/packages/crate_arrayvec.mk @@ -1,9 +1,9 @@ package=crate_arrayvec $(package)_crate_name=arrayvec -$(package)_version=0.4.7 +$(package)_version=0.4.11 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef +$(package)_sha256_hash=b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_stream_cipher.mk b/depends/packages/crate_autocfg.mk similarity index 67% rename from depends/packages/crate_stream_cipher.mk rename to depends/packages/crate_autocfg.mk index b0ede4e55..1b36846ed 100644 --- a/depends/packages/crate_stream_cipher.mk +++ b/depends/packages/crate_autocfg.mk @@ -1,9 +1,9 @@ -package=crate_stream_cipher -$(package)_crate_name=stream-cipher -$(package)_version=0.1.1 +package=crate_autocfg +$(package)_crate_name=autocfg +$(package)_version=0.1.6 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=30dc6118470d69ce0fdcf7e6f95e95853f7f4f72f80d835d4519577c323814ab +$(package)_sha256_hash=b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_bellman.mk b/depends/packages/crate_bellman.mk index 5c7d904ca..49f63e5b4 100644 --- a/depends/packages/crate_bellman.mk +++ b/depends/packages/crate_bellman.mk @@ -1,9 +1,9 @@ package=crate_bellman $(package)_crate_name=bellman -$(package)_version=0.1.0 +$(package)_version=0.6.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=eae372472c7ea8f7c8fc6a62f7d5535db8302de7f1aafda2e13a97c4830d3bcf +$(package)_sha256_hash=2be536193834affcd8a6d362963e66dec8c6bca4d2009f5bac55ec9002776ff2 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_fuchsia_zircon.mk b/depends/packages/crate_bigint.mk similarity index 67% rename from depends/packages/crate_fuchsia_zircon.mk rename to depends/packages/crate_bigint.mk index f8e10aa55..6dae98362 100644 --- a/depends/packages/crate_fuchsia_zircon.mk +++ b/depends/packages/crate_bigint.mk @@ -1,9 +1,9 @@ -package=crate_fuchsia_zircon -$(package)_crate_name=fuchsia-zircon -$(package)_version=0.3.3 +package=crate_bigint +$(package)_crate_name=bigint +$(package)_version=4.4.1 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82 +$(package)_sha256_hash=ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_blake2_rfc.mk b/depends/packages/crate_blake2_rfc.mk deleted file mode 100644 index 73ef7edde..000000000 --- a/depends/packages/crate_blake2_rfc.mk +++ /dev/null @@ -1,16 +0,0 @@ -package=crate_blake2_rfc -$(package)_crate_name=blake2-rfc -$(package)_download_path=https://github.com/gtank/$($(package)_crate_name)/archive/ -$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz -$(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=8a873cc41f02e669e8071ab5919931dd4263f050becf0c19820b0497c07b0ca3 -$(package)_git_commit=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_unpackaged_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_fuchsia_zircon_sys.mk b/depends/packages/crate_blake2b_simd.mk similarity index 66% rename from depends/packages/crate_fuchsia_zircon_sys.mk rename to depends/packages/crate_blake2b_simd.mk index bc978bbb2..c9e3ce0c8 100644 --- a/depends/packages/crate_fuchsia_zircon_sys.mk +++ b/depends/packages/crate_blake2b_simd.mk @@ -1,9 +1,9 @@ -package=crate_fuchsia_zircon_sys -$(package)_crate_name=fuchsia-zircon-sys -$(package)_version=0.3.3 +package=crate_blake2b_simd +$(package)_crate_name=blake2b_simd +$(package)_version=0.5.8 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7 +$(package)_sha256_hash=5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_blake2s_simd.mk b/depends/packages/crate_blake2s_simd.mk new file mode 100644 index 000000000..bea01c9b1 --- /dev/null +++ b/depends/packages/crate_blake2s_simd.mk @@ -0,0 +1,15 @@ +package=crate_blake2s_simd +$(package)_crate_name=blake2s_simd +$(package)_version=0.5.8 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=979da0ce13c897d6be19e005ea77ac12b0fea0157aeeee7feb8c49f91386f0ea +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_block_buffer.mk b/depends/packages/crate_block_buffer.mk new file mode 100644 index 000000000..26d0b5e48 --- /dev/null +++ b/depends/packages/crate_block_buffer.mk @@ -0,0 +1,15 @@ +package=crate_block_buffer +$(package)_crate_name=block-buffer +$(package)_version=0.7.3 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_block_cipher_trait.mk b/depends/packages/crate_block_cipher_trait.mk index a2ef96576..553bd52a9 100644 --- a/depends/packages/crate_block_cipher_trait.mk +++ b/depends/packages/crate_block_cipher_trait.mk @@ -1,9 +1,9 @@ package=crate_block_cipher_trait $(package)_crate_name=block-cipher-trait -$(package)_version=0.5.3 +$(package)_version=0.6.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=370424437b9459f3dfd68428ed9376ddfe03d8b70ede29cc533b3557df186ab4 +$(package)_sha256_hash=1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_block_padding.mk b/depends/packages/crate_block_padding.mk new file mode 100644 index 000000000..310a14cf6 --- /dev/null +++ b/depends/packages/crate_block_padding.mk @@ -0,0 +1,15 @@ +package=crate_block_padding +$(package)_crate_name=block-padding +$(package)_version=0.1.4 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_byte_tools.mk b/depends/packages/crate_byte_tools.mk index 03edeeb70..a5dff7279 100644 --- a/depends/packages/crate_byte_tools.mk +++ b/depends/packages/crate_byte_tools.mk @@ -1,9 +1,9 @@ package=crate_byte_tools $(package)_crate_name=byte-tools -$(package)_version=0.2.0 +$(package)_version=0.3.1 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40 +$(package)_sha256_hash=e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_byteorder.mk b/depends/packages/crate_byteorder.mk index 0df286920..cd991687d 100644 --- a/depends/packages/crate_byteorder.mk +++ b/depends/packages/crate_byteorder.mk @@ -1,9 +1,9 @@ package=crate_byteorder $(package)_crate_name=byteorder -$(package)_version=1.2.2 +$(package)_version=1.3.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87 +$(package)_sha256_hash=a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_c2_chacha.mk b/depends/packages/crate_c2_chacha.mk new file mode 100644 index 000000000..fe29c2101 --- /dev/null +++ b/depends/packages/crate_c2_chacha.mk @@ -0,0 +1,15 @@ +package=crate_c2_chacha +$(package)_crate_name=c2-chacha +$(package)_version=0.2.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_cfg_if.mk b/depends/packages/crate_cfg_if.mk new file mode 100644 index 000000000..af67660f9 --- /dev/null +++ b/depends/packages/crate_cfg_if.mk @@ -0,0 +1,15 @@ +package=crate_cfg_if +$(package)_crate_name=cfg-if +$(package)_version=0.1.9 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_constant_time_eq.mk b/depends/packages/crate_constant_time_eq.mk index b782dfb22..2ed631238 100644 --- a/depends/packages/crate_constant_time_eq.mk +++ b/depends/packages/crate_constant_time_eq.mk @@ -1,9 +1,9 @@ package=crate_constant_time_eq $(package)_crate_name=constant_time_eq -$(package)_version=0.1.3 +$(package)_version=0.1.4 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e +$(package)_sha256_hash=995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_crossbeam.mk b/depends/packages/crate_crossbeam.mk index 3b31aa53b..144f35007 100644 --- a/depends/packages/crate_crossbeam.mk +++ b/depends/packages/crate_crossbeam.mk @@ -1,9 +1,9 @@ package=crate_crossbeam $(package)_crate_name=crossbeam -$(package)_version=0.3.2 +$(package)_version=0.7.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19 +$(package)_sha256_hash=2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_crossbeam_channel.mk b/depends/packages/crate_crossbeam_channel.mk new file mode 100644 index 000000000..618268f41 --- /dev/null +++ b/depends/packages/crate_crossbeam_channel.mk @@ -0,0 +1,15 @@ +package=crate_crossbeam_channel +$(package)_crate_name=crossbeam-channel +$(package)_version=0.3.9 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_crossbeam_deque.mk b/depends/packages/crate_crossbeam_deque.mk new file mode 100644 index 000000000..881a2c75b --- /dev/null +++ b/depends/packages/crate_crossbeam_deque.mk @@ -0,0 +1,15 @@ +package=crate_crossbeam_deque +$(package)_crate_name=crossbeam-deque +$(package)_version=0.7.1 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_crossbeam_epoch.mk b/depends/packages/crate_crossbeam_epoch.mk new file mode 100644 index 000000000..b68701642 --- /dev/null +++ b/depends/packages/crate_crossbeam_epoch.mk @@ -0,0 +1,15 @@ +package=crate_crossbeam_epoch +$(package)_crate_name=crossbeam-epoch +$(package)_version=0.7.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_crossbeam_queue.mk b/depends/packages/crate_crossbeam_queue.mk new file mode 100644 index 000000000..70616caf5 --- /dev/null +++ b/depends/packages/crate_crossbeam_queue.mk @@ -0,0 +1,15 @@ +package=crate_crossbeam_queue +$(package)_crate_name=crossbeam-queue +$(package)_version=0.1.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_crossbeam_utils.mk b/depends/packages/crate_crossbeam_utils.mk new file mode 100644 index 000000000..eabfa26c4 --- /dev/null +++ b/depends/packages/crate_crossbeam_utils.mk @@ -0,0 +1,15 @@ +package=crate_crossbeam_utils +$(package)_crate_name=crossbeam-utils +$(package)_version=0.6.6 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_crunchy.mk b/depends/packages/crate_crunchy.mk new file mode 100644 index 000000000..d984ba848 --- /dev/null +++ b/depends/packages/crate_crunchy.mk @@ -0,0 +1,15 @@ +package=crate_crunchy +$(package)_crate_name=crunchy +$(package)_version=0.1.6 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_crypto_api.mk b/depends/packages/crate_crypto_api.mk new file mode 100644 index 000000000..e65f6a659 --- /dev/null +++ b/depends/packages/crate_crypto_api.mk @@ -0,0 +1,15 @@ +package=crate_crypto_api +$(package)_crate_name=crypto_api +$(package)_version=0.2.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_crypto_api_chachapoly.mk b/depends/packages/crate_crypto_api_chachapoly.mk new file mode 100644 index 000000000..5ae64dcac --- /dev/null +++ b/depends/packages/crate_crypto_api_chachapoly.mk @@ -0,0 +1,15 @@ +package=crate_crypto_api_chachapoly +$(package)_crate_name=crypto_api_chachapoly +$(package)_version=0.2.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_digest.mk b/depends/packages/crate_digest.mk index 029ccd7bd..63d7c74e1 100644 --- a/depends/packages/crate_digest.mk +++ b/depends/packages/crate_digest.mk @@ -1,9 +1,9 @@ package=crate_digest $(package)_crate_name=digest -$(package)_version=0.7.2 +$(package)_version=0.8.1 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603 +$(package)_sha256_hash=f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_directories.mk b/depends/packages/crate_directories.mk new file mode 100644 index 000000000..7a3a93d53 --- /dev/null +++ b/depends/packages/crate_directories.mk @@ -0,0 +1,15 @@ +package=crate_directories +$(package)_crate_name=directories +$(package)_version=1.0.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_fake_simd.mk b/depends/packages/crate_fake_simd.mk new file mode 100644 index 000000000..44331b71a --- /dev/null +++ b/depends/packages/crate_fake_simd.mk @@ -0,0 +1,15 @@ +package=crate_fake_simd +$(package)_crate_name=fake-simd +$(package)_version=0.1.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_ff.mk b/depends/packages/crate_ff.mk new file mode 100644 index 000000000..05372070e --- /dev/null +++ b/depends/packages/crate_ff.mk @@ -0,0 +1,15 @@ +package=crate_ff +$(package)_crate_name=ff +$(package)_version=0.6.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=c4b967a3ee6ae993f0094174257d404a5818f58be79d67a1aea1ec8996d28906 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_ff_derive.mk b/depends/packages/crate_ff_derive.mk new file mode 100644 index 000000000..2d3440472 --- /dev/null +++ b/depends/packages/crate_ff_derive.mk @@ -0,0 +1,15 @@ +package=crate_ff_derive +$(package)_crate_name=ff_derive +$(package)_version=0.6.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=a3776aaf60a45037a9c3cabdd8542b38693acaa3e241ff957181b72579d29feb +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_fpe.mk b/depends/packages/crate_fpe.mk index ba6e344ac..12baf031c 100644 --- a/depends/packages/crate_fpe.mk +++ b/depends/packages/crate_fpe.mk @@ -1,9 +1,9 @@ package=crate_fpe $(package)_crate_name=fpe -$(package)_version=0.1.0 +$(package)_version=0.2.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=ce3371c82bfbd984f624cab093f55e7336f5a6e589f8518e1258f54f011b89ad +$(package)_sha256_hash=21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_futures.mk b/depends/packages/crate_futures.mk index 3e0e6f990..fba10a2a4 100644 --- a/depends/packages/crate_futures.mk +++ b/depends/packages/crate_futures.mk @@ -1,9 +1,9 @@ package=crate_futures $(package)_crate_name=futures -$(package)_version=0.1.21 +$(package)_version=0.1.29 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c +$(package)_sha256_hash=1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_generic_array.mk b/depends/packages/crate_generic_array.mk index ab4a566ee..c84854ab8 100644 --- a/depends/packages/crate_generic_array.mk +++ b/depends/packages/crate_generic_array.mk @@ -1,9 +1,9 @@ package=crate_generic_array $(package)_crate_name=generic-array -$(package)_version=0.9.0 +$(package)_version=0.12.3 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d +$(package)_sha256_hash=c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_getrandom.mk b/depends/packages/crate_getrandom.mk new file mode 100644 index 000000000..460ab8b15 --- /dev/null +++ b/depends/packages/crate_getrandom.mk @@ -0,0 +1,15 @@ +package=crate_getrandom +$(package)_crate_name=getrandom +$(package)_version=0.1.12 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_group.mk b/depends/packages/crate_group.mk new file mode 100644 index 000000000..6931c7a51 --- /dev/null +++ b/depends/packages/crate_group.mk @@ -0,0 +1,15 @@ +package=crate_group +$(package)_crate_name=group +$(package)_version=0.6.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=f15be54742789e36f03307c8fdf0621201e1345e94f1387282024178b5e9ec8c +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_hex.mk b/depends/packages/crate_hex.mk new file mode 100644 index 000000000..f93417a30 --- /dev/null +++ b/depends/packages/crate_hex.mk @@ -0,0 +1,15 @@ +package=crate_hex +$(package)_crate_name=hex +$(package)_version=0.3.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_lazy_static.mk b/depends/packages/crate_lazy_static.mk index 208dc1dad..50f4f6ffc 100644 --- a/depends/packages/crate_lazy_static.mk +++ b/depends/packages/crate_lazy_static.mk @@ -1,9 +1,9 @@ package=crate_lazy_static $(package)_crate_name=lazy_static -$(package)_version=1.0.0 +$(package)_version=1.4.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d +$(package)_sha256_hash=e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_libc.mk b/depends/packages/crate_libc.mk index 2e1a2c074..a8752b964 100644 --- a/depends/packages/crate_libc.mk +++ b/depends/packages/crate_libc.mk @@ -1,9 +1,9 @@ package=crate_libc $(package)_crate_name=libc -$(package)_version=0.2.40 +$(package)_version=0.2.62 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b +$(package)_sha256_hash=34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_log.mk b/depends/packages/crate_log.mk new file mode 100644 index 000000000..3bc1ec01a --- /dev/null +++ b/depends/packages/crate_log.mk @@ -0,0 +1,15 @@ +package=crate_log +$(package)_crate_name=log +$(package)_version=0.4.8 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_memoffset.mk b/depends/packages/crate_memoffset.mk new file mode 100644 index 000000000..c4e9fcd27 --- /dev/null +++ b/depends/packages/crate_memoffset.mk @@ -0,0 +1,15 @@ +package=crate_memoffset +$(package)_crate_name=memoffset +$(package)_version=0.5.1 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_nodrop.mk b/depends/packages/crate_nodrop.mk index 1997574c0..70d589b5c 100644 --- a/depends/packages/crate_nodrop.mk +++ b/depends/packages/crate_nodrop.mk @@ -1,9 +1,9 @@ package=crate_nodrop $(package)_crate_name=nodrop -$(package)_version=0.1.12 +$(package)_version=0.1.13 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2 +$(package)_sha256_hash=2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_num_bigint.mk b/depends/packages/crate_num_bigint.mk index bc1ff4a32..3b97d94f7 100644 --- a/depends/packages/crate_num_bigint.mk +++ b/depends/packages/crate_num_bigint.mk @@ -1,9 +1,9 @@ package=crate_num_bigint $(package)_crate_name=num-bigint -$(package)_version=0.2.0 +$(package)_version=0.2.3 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=3eceac7784c5dc97c2d6edf30259b4e153e6e2b42b3c85e9a6e9f45d06caef6e +$(package)_sha256_hash=f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_num_cpus.mk b/depends/packages/crate_num_cpus.mk index fdaebe4db..63e07acfa 100644 --- a/depends/packages/crate_num_cpus.mk +++ b/depends/packages/crate_num_cpus.mk @@ -1,9 +1,9 @@ package=crate_num_cpus $(package)_crate_name=num_cpus -$(package)_version=1.8.0 +$(package)_version=1.10.1 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30 +$(package)_sha256_hash=bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_num_integer.mk b/depends/packages/crate_num_integer.mk index ea479e8e7..5e9c455a5 100644 --- a/depends/packages/crate_num_integer.mk +++ b/depends/packages/crate_num_integer.mk @@ -1,9 +1,9 @@ package=crate_num_integer $(package)_crate_name=num-integer -$(package)_version=0.1.39 +$(package)_version=0.1.41 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea +$(package)_sha256_hash=b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_num_traits.mk b/depends/packages/crate_num_traits.mk index f0ffbe5a1..19d87838f 100644 --- a/depends/packages/crate_num_traits.mk +++ b/depends/packages/crate_num_traits.mk @@ -1,9 +1,9 @@ package=crate_num_traits $(package)_crate_name=num-traits -$(package)_version=0.2.5 +$(package)_version=0.2.8 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe +$(package)_sha256_hash=6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_opaque_debug.mk b/depends/packages/crate_opaque_debug.mk index 7d7a5e914..7c493bba3 100644 --- a/depends/packages/crate_opaque_debug.mk +++ b/depends/packages/crate_opaque_debug.mk @@ -1,9 +1,9 @@ package=crate_opaque_debug $(package)_crate_name=opaque-debug -$(package)_version=0.1.1 +$(package)_version=0.2.3 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7 +$(package)_sha256_hash=2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_pairing.mk b/depends/packages/crate_pairing.mk index c81e23bed..454b8f106 100644 --- a/depends/packages/crate_pairing.mk +++ b/depends/packages/crate_pairing.mk @@ -1,9 +1,9 @@ package=crate_pairing $(package)_crate_name=pairing -$(package)_version=0.14.2 +$(package)_version=0.16.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=ceda21136251c6d5a422d3d798d8ac22515a6e8d3521bb60c59a8349d36d0d57 +$(package)_sha256_hash=b8290dea210a712682cd65031dc2b34fd132cf2729def3df7ee08f0737ff5ed6 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_ppv_lite86.mk b/depends/packages/crate_ppv_lite86.mk new file mode 100644 index 000000000..30641f95a --- /dev/null +++ b/depends/packages/crate_ppv_lite86.mk @@ -0,0 +1,15 @@ +package=crate_ppv_lite86 +$(package)_crate_name=ppv-lite86 +$(package)_version=0.2.5 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_proc_macro2.mk b/depends/packages/crate_proc_macro2.mk new file mode 100644 index 000000000..4e59111aa --- /dev/null +++ b/depends/packages/crate_proc_macro2.mk @@ -0,0 +1,15 @@ +package=crate_proc_macro2 +$(package)_crate_name=proc-macro2 +$(package)_version=1.0.3 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_quote.mk b/depends/packages/crate_quote.mk new file mode 100644 index 000000000..48e0c8ea1 --- /dev/null +++ b/depends/packages/crate_quote.mk @@ -0,0 +1,15 @@ +package=crate_quote +$(package)_crate_name=quote +$(package)_version=1.0.2 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_rand.mk b/depends/packages/crate_rand.mk index 16fb81753..7832db1b3 100644 --- a/depends/packages/crate_rand.mk +++ b/depends/packages/crate_rand.mk @@ -1,9 +1,9 @@ package=crate_rand $(package)_crate_name=rand -$(package)_version=0.4.2 +$(package)_version=0.7.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5 +$(package)_sha256_hash=d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_rand_chacha.mk b/depends/packages/crate_rand_chacha.mk new file mode 100644 index 000000000..ed2953fab --- /dev/null +++ b/depends/packages/crate_rand_chacha.mk @@ -0,0 +1,15 @@ +package=crate_rand_chacha +$(package)_crate_name=rand_chacha +$(package)_version=0.2.1 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_rand_core.mk b/depends/packages/crate_rand_core.mk new file mode 100644 index 000000000..230b257c9 --- /dev/null +++ b/depends/packages/crate_rand_core.mk @@ -0,0 +1,15 @@ +package=crate_rand_core +$(package)_crate_name=rand_core +$(package)_version=0.5.1 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_rand_hc.mk b/depends/packages/crate_rand_hc.mk new file mode 100644 index 000000000..3d5155359 --- /dev/null +++ b/depends/packages/crate_rand_hc.mk @@ -0,0 +1,15 @@ +package=crate_rand_hc +$(package)_crate_name=rand_hc +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_rand_xorshift.mk b/depends/packages/crate_rand_xorshift.mk new file mode 100644 index 000000000..6daa22805 --- /dev/null +++ b/depends/packages/crate_rand_xorshift.mk @@ -0,0 +1,15 @@ +package=crate_rand_xorshift +$(package)_crate_name=rand_xorshift +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_rustc_version.mk b/depends/packages/crate_rustc_version.mk new file mode 100644 index 000000000..f3c7410ee --- /dev/null +++ b/depends/packages/crate_rustc_version.mk @@ -0,0 +1,15 @@ +package=crate_rustc_version +$(package)_crate_name=rustc_version +$(package)_version=0.2.3 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_sapling_crypto.mk b/depends/packages/crate_sapling_crypto.mk deleted file mode 100644 index 2da5e2406..000000000 --- a/depends/packages/crate_sapling_crypto.mk +++ /dev/null @@ -1,16 +0,0 @@ -package=crate_sapling_crypto -$(package)_crate_name=sapling-crypto -$(package)_download_path=https://github.com/zcash-hackworks/$($(package)_crate_name)/archive/ -$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz -$(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=ae3a122b1f1ce97b4e80e0e8542e19aa1516e99e6c72875688c886af1a881558 -$(package)_git_commit=21084bde2019c04bd34208e63c3560fe2c02fb0e -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_unpackaged_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_scopeguard.mk b/depends/packages/crate_scopeguard.mk new file mode 100644 index 000000000..a3a778dc4 --- /dev/null +++ b/depends/packages/crate_scopeguard.mk @@ -0,0 +1,15 @@ +package=crate_scopeguard +$(package)_crate_name=scopeguard +$(package)_version=1.0.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_semver.mk b/depends/packages/crate_semver.mk new file mode 100644 index 000000000..2ef1ba869 --- /dev/null +++ b/depends/packages/crate_semver.mk @@ -0,0 +1,15 @@ +package=crate_semver +$(package)_crate_name=semver +$(package)_version=0.9.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_semver_parser.mk b/depends/packages/crate_semver_parser.mk new file mode 100644 index 000000000..9316860ac --- /dev/null +++ b/depends/packages/crate_semver_parser.mk @@ -0,0 +1,15 @@ +package=crate_semver_parser +$(package)_crate_name=semver-parser +$(package)_version=0.7.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_sha2.mk b/depends/packages/crate_sha2.mk new file mode 100644 index 000000000..344582dbb --- /dev/null +++ b/depends/packages/crate_sha2.mk @@ -0,0 +1,15 @@ +package=crate_sha2 +$(package)_crate_name=sha2 +$(package)_version=0.8.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_syn.mk b/depends/packages/crate_syn.mk new file mode 100644 index 000000000..37593b2e1 --- /dev/null +++ b/depends/packages/crate_syn.mk @@ -0,0 +1,15 @@ +package=crate_syn +$(package)_crate_name=syn +$(package)_version=1.0.5 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_typenum.mk b/depends/packages/crate_typenum.mk index bc5a235a8..f02fa5001 100644 --- a/depends/packages/crate_typenum.mk +++ b/depends/packages/crate_typenum.mk @@ -1,9 +1,9 @@ package=crate_typenum $(package)_crate_name=typenum -$(package)_version=1.10.0 +$(package)_version=1.11.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169 +$(package)_sha256_hash=6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_unicode_xid.mk b/depends/packages/crate_unicode_xid.mk new file mode 100644 index 000000000..16f7be517 --- /dev/null +++ b/depends/packages/crate_unicode_xid.mk @@ -0,0 +1,15 @@ +package=crate_unicode_xid +$(package)_crate_name=unicode-xid +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_wasi.mk b/depends/packages/crate_wasi.mk new file mode 100644 index 000000000..613c52627 --- /dev/null +++ b/depends/packages/crate_wasi.mk @@ -0,0 +1,15 @@ +package=crate_wasi +$(package)_crate_name=wasi +$(package)_version=0.7.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_winapi.mk b/depends/packages/crate_winapi.mk index 6aafb0251..b8f86a00b 100644 --- a/depends/packages/crate_winapi.mk +++ b/depends/packages/crate_winapi.mk @@ -1,9 +1,9 @@ package=crate_winapi $(package)_crate_name=winapi -$(package)_version=0.3.4 +$(package)_version=0.3.8 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3 +$(package)_sha256_hash=8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_zcash_history.mk b/depends/packages/crate_zcash_history.mk new file mode 100644 index 000000000..fd5570fa4 --- /dev/null +++ b/depends/packages/crate_zcash_history.mk @@ -0,0 +1,15 @@ +package=crate_zcash_history +$(package)_crate_name=zcash_history +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=abfbab9accba014bbf3098d5aa66c1714d0db4abe25b999b8400bbd626ccd2f4 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_zcash_primitives.mk b/depends/packages/crate_zcash_primitives.mk new file mode 100644 index 000000000..8d22560ff --- /dev/null +++ b/depends/packages/crate_zcash_primitives.mk @@ -0,0 +1,15 @@ +package=crate_zcash_primitives +$(package)_crate_name=zcash_primitives +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=8f33b9e4f3b4db97234fc79ea67b12f2d5778bde8f3eab6dbba52eb54c596585 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_zcash_proofs.mk b/depends/packages/crate_zcash_proofs.mk new file mode 100644 index 000000000..c3e40b0f3 --- /dev/null +++ b/depends/packages/crate_zcash_proofs.mk @@ -0,0 +1,15 @@ +package=crate_zcash_proofs +$(package)_crate_name=zcash_proofs +$(package)_version=0.2.0 +$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) +$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate +$(package)_sha256_hash=2011f78f14d5121248d3b4f921434207b1d870fb3bf2efc7d784cae79b19bfbc +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_zip32.mk b/depends/packages/crate_zip32.mk deleted file mode 100644 index e47f1fb94..000000000 --- a/depends/packages/crate_zip32.mk +++ /dev/null @@ -1,16 +0,0 @@ -package=crate_zip32 -$(package)_crate_name=zip32 -$(package)_download_path=https://github.com/zcash-hackworks/$($(package)_crate_name)/archive/ -$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz -$(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=b0b011ea96524f0d918a44c7ab8a3dec6270879d1ff03d7dbda6c676d25caa7e -$(package)_git_commit=176470ef41583b5bd0bd749bd1b61d417aa8ec79 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_unpackaged_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/librustzcash.mk b/depends/packages/librustzcash.mk deleted file mode 100644 index a818746f7..000000000 --- a/depends/packages/librustzcash.mk +++ /dev/null @@ -1,80 +0,0 @@ -package=librustzcash -$(package)_version=0.1 -$(package)_download_path=https://github.com/zcash/$(package)/archive/ -$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz -$(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=9909ec59fa7a411c2071d6237b3363a0bc6e5e42358505cf64b7da0f58a7ff5a -$(package)_git_commit=06da3b9ac8f278e5d4ae13088cf0a4c03d2c13f5 -$(package)_dependencies=rust -$(package)_dependencies+=$(rust_crates) -$(package)_patches=cargo.config guix.config 0001-Start-using-cargo-clippy-for-CI.patch remove-dev-dependencies.diff - -# Mapping from GCC canonical hosts to Rust targets -$(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu -$(package)_rust_target_aarch64-unknown-linux-gnu=aarch64-unknown-linux-gnu -$(package)_rust_target_x86_64-apple-darwin=x86_64-apple-darwin -$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu - -ifneq ($(WITH_GUIX),) -$(package)_rust_target=$($(package)_rust_target_$(canonical_host)) -else - -# Mapping from GCC canonical hosts to Rust targets -# If a mapping is not present, we assume they are identical, unless $host_os is -# "darwin", in which case we assume x86_64-apple-darwin. -$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu - -define rust_target -$(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(2))) -endef - -ifneq ($(canonical_host),$(build)) -$(package)_rust_target=$(call rust_target,$(package),$(canonical_host),$(host_os)) -else -$(package)_rust_target=$(if $(rust_rust_target_$(canonical_host)),$(rust_rust_target_$(canonical_host)),$(canonical_host)) -endif -endif - -ifneq ($(WITH_GUIX),) -$(package)_library_file=target/$($(package)_rust_target)/release/librustzcash.a -else ifneq ($(canonical_host),$(build)) -$(package)_library_file=target/$($(package)_rust_target)/release/librustzcash.a -else -$(package)_library_file=target/release/librustzcash.a -endif - -define $(package)_set_vars -$(package)_build_opts=--release -$(package)_build_opts+=--frozen -ifneq ($(WITH_GUIX),) -$(package)_build_opts+=--target=$($(package)_rust_target) -else ifneq ($(canonical_host),$(build)) -$(package)_build_opts+=--target=$($(package)_rust_target) -endif -endef - -ifneq ($(WITH_GUIX),) -define $(package)_config_cmds - cat $($(package)_patch_dir)/guix.config >> .cargo/config -endef -endif - -define $(package)_preprocess_cmds - patch -p1 -d pairing < $($(package)_patch_dir)/0001-Start-using-cargo-clippy-for-CI.patch && \ - patch -p1 < $($(package)_patch_dir)/remove-dev-dependencies.diff && \ - mkdir .cargo && \ - cat $($(package)_patch_dir)/cargo.config | sed 's|CRATE_REGISTRY|$(host_prefix)/$(CRATE_REGISTRY)|' > .cargo/config -endef - -define $(package)_build_cmds - echo "DEBUG: canonical_host=$(canonical_host) host_os=$(host_os) build=$(build) build_os=$(build_os) CC=$(CC) CXX=$(CXX)" && \ - echo "$(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts)" && \ - $(host_prefix)/native/bin/cargo build --package librustzcash $($(package)_build_opts) -endef - -define $(package)_stage_cmds - mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \ - mkdir $($(package)_staging_dir)$(host_prefix)/include/ && \ - cp $($(package)_library_file) $($(package)_staging_dir)$(host_prefix)/lib/ && \ - cp librustzcash/include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/ -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 03e363256..4fec7a42c 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -2,38 +2,79 @@ rust_crates := \ crate_aes \ crate_aesni \ crate_aes_soft \ + crate_arrayref \ crate_arrayvec \ - crate_bitflags \ + crate_autocfg \ + crate_bellman \ + crate_bigint \ crate_bit_vec \ - crate_blake2_rfc \ + crate_blake2b_simd \ + crate_blake2s_simd \ + crate_block_buffer \ crate_block_cipher_trait \ + crate_block_padding \ crate_byte_tools \ crate_byteorder \ + crate_c2_chacha \ + crate_cfg_if \ crate_constant_time_eq \ + crate_crossbeam_channel \ + crate_crossbeam_deque \ + crate_crossbeam_epoch \ + crate_crossbeam_queue \ + crate_crossbeam_utils \ crate_crossbeam \ + crate_crunchy \ + crate_crypto_api_chachapoly \ + crate_crypto_api \ crate_digest \ + crate_directories \ + crate_fake_simd \ + crate_ff_derive \ + crate_ff \ crate_fpe \ - crate_fuchsia_zircon \ - crate_fuchsia_zircon_sys \ crate_futures_cpupool \ crate_futures \ crate_generic_array \ + crate_getrandom \ + crate_group \ + crate_hex \ crate_lazy_static \ crate_libc \ + crate_log \ + crate_memoffset \ crate_nodrop \ crate_num_bigint \ crate_num_cpus \ crate_num_integer \ crate_num_traits \ crate_opaque_debug \ + crate_pairing \ + crate_ppv_lite86 \ + crate_proc_macro2 \ + crate_quote \ + crate_rand_chacha \ + crate_rand_core \ + crate_rand_hc \ + crate_rand_xorshift \ crate_rand \ - crate_stream_cipher \ + crate_rustc_version \ + crate_scopeguard \ + crate_semver_parser \ + crate_semver \ + crate_sha2 \ + crate_syn \ crate_typenum \ + crate_unicode_xid \ + crate_wasi \ crate_winapi_i686_pc_windows_gnu \ crate_winapi \ - crate_winapi_x86_64_pc_windows_gnu + crate_winapi_x86_64_pc_windows_gnu \ + crate_zcash_history \ + crate_zcash_primitives \ + crate_zcash_proofs -rust_packages := rust $(rust_crates) librustzcash +rust_packages := rust $(rust_crates) zcash_packages := libsodium utfcpp boost_packages := boost libevent_packages := libevent diff --git a/depends/packages/rust.mk b/depends/packages/rust.mk index 6d40ecba9..39744f764 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -31,6 +31,39 @@ $(package)_extra_sources += $($(package)_rust-std-x86_64-apple-darwin_file_name) $(package)_extra_sources += $($(package)_rust-std-aarch64-unknown-linux-gnu_file_name) $(package)_extra_sources += $($(package)_rust-std-x86_64-w64-mingw32_file_name) +ifneq ($(WITH_GUIX),) +# Mapping from GCC canonical hosts to Rust targets +$(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu +$(package)_rust_target_aarch64-unknown-linux-gnu=aarch64-unknown-linux-gnu +$(package)_rust_target_x86_64-apple-darwin=x86_64-apple-darwin +$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu + +define rust_target +$(package)_rust_target=$($(package)_rust_target_$(canonical_host)) +endef + +else + +# Mapping from GCC canonical hosts to Rust targets +# If a mapping is not present, we assume they are identical, unless $host_os is +# "darwin", in which case we assume x86_64-apple-darwin. +$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu + +define rust_target +$(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(2))) +endef + +ifneq ($(canonical_host),$(build)) +$(package)_rust_target=$(call rust_target,$(package),$(canonical_host),$(host_os)) +else +$(package)_rust_target=$(if $(rust_rust_target_$(canonical_host)),$(rust_rust_target_$(canonical_host)),$(canonical_host)) +endif +endif + +define rust_target +$(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(2))) +endef + define $(package)_set_vars $(package)_stage_opts=--disable-ldconfig $(package)_stage_build_opts=--without=rust-docs-json-preview,rust-docs diff --git a/depends/patches/librustzcash/0001-Start-using-cargo-clippy-for-CI.patch b/depends/patches/librustzcash/0001-Start-using-cargo-clippy-for-CI.patch deleted file mode 100644 index 6bfeee574..000000000 --- a/depends/patches/librustzcash/0001-Start-using-cargo-clippy-for-CI.patch +++ /dev/null @@ -1,42 +0,0 @@ -From cc5b83510277632852af67d896a27e0cb40f342b Mon Sep 17 00:00:00 2001 -From: Sean Bowe -Date: Wed, 4 Jul 2018 12:45:08 -0600 -Subject: [PATCH 1/2] Start using cargo-clippy for CI. - ---- - src/lib.rs | 21 ++++++++++----------- - 1 file changed, 10 insertions(+), 11 deletions(-) - -diff --git a/src/lib.rs b/src/lib.rs -index fefdae3..c3640c4 100644 ---- a/src/lib.rs -+++ b/src/lib.rs -@@ -1,15 +1,14 @@ - // `clippy` is a code linting tool for improving code quality by catching --// common mistakes or strange code patterns. If the `clippy` feature is --// provided, it is enabled and all compiler warnings are prohibited. --#![cfg_attr(feature = "clippy", deny(warnings))] --#![cfg_attr(feature = "clippy", feature(plugin))] --#![cfg_attr(feature = "clippy", plugin(clippy))] --#![cfg_attr(feature = "clippy", allow(inline_always))] --#![cfg_attr(feature = "clippy", allow(too_many_arguments))] --#![cfg_attr(feature = "clippy", allow(unreadable_literal))] --#![cfg_attr(feature = "clippy", allow(many_single_char_names))] --#![cfg_attr(feature = "clippy", allow(new_without_default_derive))] --#![cfg_attr(feature = "clippy", allow(write_literal))] -+// common mistakes or strange code patterns. If the `cargo-clippy` feature -+// is provided, all compiler warnings are prohibited. -+#![cfg_attr(feature = "cargo-clippy", deny(warnings))] -+#![cfg_attr(feature = "cargo-clippy", allow(inline_always))] -+#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] -+#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] -+#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))] -+#![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] -+#![cfg_attr(feature = "cargo-clippy", allow(write_literal))] -+ - // Force public structures to implement Debug - #![deny(missing_debug_implementations)] - --- -2.17.1 - diff --git a/depends/patches/librustzcash/guix.config b/depends/patches/librustzcash/guix.config deleted file mode 100644 index 515e937d7..000000000 --- a/depends/patches/librustzcash/guix.config +++ /dev/null @@ -1,12 +0,0 @@ - -[target.x86_64-unknown-linux-gnu] -linker = "x86_64-unknown-linux-gnu-gcc" - -[target.x86_64-apple-darwin] -linker = "x86_64-unknown-linux-gnu-gcc" - -[target.aarch64-unknown-linux-gnu] -linker = "aarch64-linux-gnu-gcc" - -[target.x86_64-pc-windows-gnu] -linker = "x86_64-w64-mingw32-gcc" diff --git a/depends/patches/librustzcash/remove-dev-dependencies.diff b/depends/patches/librustzcash/remove-dev-dependencies.diff deleted file mode 100644 index d013de8da..000000000 --- a/depends/patches/librustzcash/remove-dev-dependencies.diff +++ /dev/null @@ -1,630 +0,0 @@ -diff --git a/Cargo.lock b/Cargo.lock -index bc740bb..3c6c94b 100644 ---- a/Cargo.lock -+++ b/Cargo.lock -@@ -28,22 +28,6 @@ dependencies = [ - "stream-cipher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - ] - --[[package]] --name = "aho-corasick" --version = "0.6.8" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "ansi_term" --version = "0.11.0" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", --] -- - [[package]] - name = "arrayvec" - version = "0.4.7" -@@ -52,27 +36,6 @@ dependencies = [ - "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - ] - --[[package]] --name = "backtrace" --version = "0.3.9" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", -- "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", -- "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "backtrace-sys" --version = "0.1.24" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", -- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", --] -- - [[package]] - name = "bellman" - version = "0.1.0" -@@ -92,11 +55,6 @@ name = "bit-vec" - version = "0.4.4" - source = "registry+https://github.com/rust-lang/crates.io-index" - --[[package]] --name = "bitflags" --version = "0.9.1" --source = "registry+https://github.com/rust-lang/crates.io-index" -- - [[package]] - name = "bitflags" - version = "1.0.1" -@@ -130,61 +88,6 @@ name = "byteorder" - version = "1.2.2" - source = "registry+https://github.com/rust-lang/crates.io-index" - --[[package]] --name = "cargo_metadata" --version = "0.5.8" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", -- "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -- "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", -- "serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", -- "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "cc" --version = "1.0.22" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "cfg-if" --version = "0.1.5" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "clippy" --version = "0.0.200" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", -- "clippy_lints 0.0.200 (registry+https://github.com/rust-lang/crates.io-index)", -- "regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -- "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -- "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "clippy_lints" --version = "0.0.200" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", -- "if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -- "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", -- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -- "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -- "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -- "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", -- "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -- "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -- "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", -- "serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", -- "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -- "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -- "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", --] -- - [[package]] - name = "constant_time_eq" - version = "0.1.3" -@@ -203,19 +106,6 @@ dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - ] - --[[package]] --name = "either" --version = "1.5.0" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "error-chain" --version = "0.11.0" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", --] -- - [[package]] - name = "fpe" - version = "0.1.0" -@@ -256,11 +146,6 @@ dependencies = [ - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - ] - --[[package]] --name = "gcc" --version = "0.3.54" --source = "registry+https://github.com/rust-lang/crates.io-index" -- - [[package]] - name = "generic-array" - version = "0.9.0" -@@ -269,59 +154,6 @@ dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - ] - --[[package]] --name = "getopts" --version = "0.2.18" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "hex-literal" --version = "0.1.1" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -- "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "hex-literal-impl" --version = "0.1.1" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "idna" --version = "0.1.5" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -- "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", -- "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "if_chain" --version = "0.1.3" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "itertools" --version = "0.7.8" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "itoa" --version = "0.4.2" --source = "registry+https://github.com/rust-lang/crates.io-index" -- - [[package]] - name = "lazy_static" - version = "1.0.0" -@@ -347,19 +179,6 @@ dependencies = [ - "zip32 0.0.0", - ] - --[[package]] --name = "matches" --version = "0.1.8" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "memchr" --version = "2.0.2" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", --] -- - [[package]] - name = "nodrop" - version = "0.1.12" -@@ -405,65 +224,6 @@ name = "pairing" - version = "0.14.2" - dependencies = [ - "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -- "clippy 0.0.200 (registry+https://github.com/rust-lang/crates.io-index)", -- "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "percent-encoding" --version = "1.0.1" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "proc-macro-hack" --version = "0.4.0" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "proc-macro-hack-impl" --version = "0.4.0" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "proc-macro2" --version = "0.4.14" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "pulldown-cmark" --version = "0.1.2" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", -- "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "quine-mc_cluskey" --version = "0.2.4" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "quote" --version = "0.6.8" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "proc-macro2 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "rand" --version = "0.3.22" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - ] - -@@ -477,66 +237,6 @@ dependencies = [ - "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - ] - --[[package]] --name = "redox_syscall" --version = "0.1.40" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "regex" --version = "1.0.4" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", -- "memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -- "regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -- "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -- "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "regex-syntax" --version = "0.6.2" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "rust-crypto" --version = "0.2.36" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", -- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", -- "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", -- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -- "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "rustc-demangle" --version = "0.1.9" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "rustc-serialize" --version = "0.3.24" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "rustc_version" --version = "0.2.3" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "ryu" --version = "0.2.6" --source = "registry+https://github.com/rust-lang/crates.io-index" -- - [[package]] - name = "sapling-crypto" - version = "0.0.1" -@@ -545,49 +245,8 @@ dependencies = [ - "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", - "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -- "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.14.2", - "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -- "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "semver" --version = "0.9.0" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -- "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "semver-parser" --version = "0.7.0" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "serde" --version = "1.0.75" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "serde_derive" --version = "1.0.75" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "proc-macro2 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", -- "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", -- "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "serde_json" --version = "1.0.26" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -- "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -- "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", - ] - - [[package]] -@@ -598,90 +257,11 @@ dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - ] - --[[package]] --name = "syn" --version = "0.14.9" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "proc-macro2 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", -- "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", -- "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "thread_local" --version = "0.3.6" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "time" --version = "0.1.40" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", -- "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", -- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "toml" --version = "0.4.6" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)", --] -- - [[package]] - name = "typenum" - version = "1.10.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - --[[package]] --name = "ucd-util" --version = "0.1.1" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "unicode-bidi" --version = "0.3.4" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "unicode-normalization" --version = "0.1.7" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "unicode-width" --version = "0.1.5" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "unicode-xid" --version = "0.1.0" --source = "registry+https://github.com/rust-lang/crates.io-index" -- --[[package]] --name = "url" --version = "1.7.1" --source = "registry+https://github.com/rust-lang/crates.io-index" --dependencies = [ -- "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -- "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -- "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", --] -- --[[package]] --name = "utf8-ranges" --version = "1.0.1" --source = "registry+https://github.com/rust-lang/crates.io-index" -- - [[package]] - name = "winapi" - version = "0.3.4" -@@ -730,87 +310,33 @@ dependencies = [ - "checksum aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6fb1737cdc8da3db76e90ca817a194249a38fcb500c2e6ecec39b29448aa873" - "checksum aes-soft 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67cc03b0a090a05cb01e96998a01905d7ceedce1bc23b756c0bb7faa0682ccb1" - "checksum aesni 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6810b7fb9f2bb4f76f05ac1c170b8dde285b6308955dc3afd89710268c958d9e" --"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" --"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" - "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" --"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" --"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" - "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" --"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" - "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" - "checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "" - "checksum block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "370424437b9459f3dfd68428ed9376ddfe03d8b70ede29cc533b3557df186ab4" - "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" - "checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" --"checksum cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1efca0b863ca03ed4c109fb1c55e0bc4bbeb221d3e103d86251046b06a526bd0" --"checksum cc 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4a6007c146fdd28d4512a794b07ffe9d8e89e6bf86e2e0c4ddff2e1fb54a0007" --"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" --"checksum clippy 0.0.200 (registry+https://github.com/rust-lang/crates.io-index)" = "927a1f79af10deb103df108347f23c6b7fa1731c953d6fb24d68be1748a0993f" --"checksum clippy_lints 0.0.200 (registry+https://github.com/rust-lang/crates.io-index)" = "d2432663f6bdb90255dcf9df5ca504f99b575bb471281591138f62f9d31f863b" - "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" - "checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" - "checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603" --"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" --"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" - "checksum fpe 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce3371c82bfbd984f624cab093f55e7336f5a6e589f8518e1258f54f011b89ad" - "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" - "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - "checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" - "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" --"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" - "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" --"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797" --"checksum hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4da5f0e01bd8a71a224a4eedecaacfcabda388dbb7a80faf04d3514287572d95" --"checksum hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d340b6514f232f6db1bd16db65302a5278a04fef9ce867cb932e7e5fa21130a" --"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" --"checksum if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec" --"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" --"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" - "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" - "checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" --"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" --"checksum memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a3b4142ab8738a78c51896f704f83c11df047ff1bda9a92a661aa6361552d93d" - "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" - "checksum num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3eceac7784c5dc97c2d6edf30259b4e153e6e2b42b3c85e9a6e9f45d06caef6e" - "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" - "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" - "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" - "checksum opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7" --"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" --"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" --"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" --"checksum proc-macro2 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b331c6ad3411474cd55540398dc7ad89fc41488e64ec71fdecc9c9b86de96fb0" --"checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" --"checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" --"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" --"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" - "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" --"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" --"checksum regex 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "67d0301b0c6804eca7e3c275119d0b01ff3b7ab9258a65709e608a66312a1025" --"checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" --"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" --"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" --"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" --"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" --"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" --"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" --"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" --"checksum serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "22d340507cea0b7e6632900a176101fea959c7065d93ba555072da90aaaafc87" --"checksum serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "234fc8b737737b148ccd625175fc6390f5e4dacfdaa543cb93a3430d984a9119" --"checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae" - "checksum stream-cipher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "30dc6118470d69ce0fdcf7e6f95e95853f7f4f72f80d835d4519577c323814ab" --"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" --"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" --"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" --"checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" - "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" --"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" --"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" --"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" --"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" --"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" --"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" --"checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" - "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" - "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml -index 98725aa..dedce80 100644 ---- a/pairing/Cargo.toml -+++ b/pairing/Cargo.toml -@@ -14,7 +14,6 @@ repository = "https://github.com/ebfull/pairing" - [dependencies] - rand = "0.4" - byteorder = "1" --clippy = { version = "0.0.200", optional = true } - - [features] - unstable-features = ["expose-arith"] -diff --git a/sapling-crypto/Cargo.toml b/sapling-crypto/Cargo.toml -index 6e802f2..33e21bf 100644 ---- a/sapling-crypto/Cargo.toml -+++ b/sapling-crypto/Cargo.toml -@@ -22,10 +22,6 @@ byteorder = "1" - git = "https://github.com/gtank/blake2-rfc" - rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" - --[dev-dependencies] --hex-literal = "0.1" --rust-crypto = "0.2" -- - [features] - default = ["u128-support"] - u128-support = ["pairing/u128-support"] diff --git a/src/Makefile.am b/src/Makefile.am index 75ce0d319..f5c341943 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,7 @@ BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include +BITCOIN_INCLUDES += -I$(srcdir)/rustzcash/include BITCOIN_INCLUDES += -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) LIBBITCOIN_SERVER=libbitcoin_server.a @@ -28,6 +29,7 @@ LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBSECP256K1=secp256k1/libsecp256k1.la +LIBRUSTZCASH=rustzcash/target/$(RUST_TARGET)/release/librustzcash.a LIBZCASH=libzcash.a if ENABLE_ZMQ @@ -40,6 +42,11 @@ endif $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) +cargo-build: $(wildcard rustzcash/include/*.h) + RUSTC="$(RUSTC)" $(CARGO) build $(CARGO_FLAGS) --manifest-path=$(srcdir)/rustzcash/Cargo.toml --target=$(RUST_TARGET) + +$(LIBRUSTZCASH): cargo-build + # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: EXTRA_LIBRARIES += \ @@ -82,7 +89,7 @@ LIBZCASH_H = \ zcash/util.h \ zcash/Zcash.h -.PHONY: FORCE check-symbols check-security +.PHONY: FORCE cargo-build check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ addressindex.h \ @@ -428,7 +435,8 @@ bitcoinzd_LDADD += \ $(EVENT_LIBS) \ $(ZMQ_LIBS) \ $(LIBBITCOIN_CRYPTO) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + $(LIBRUSTZCASH) # bitcoin-cli binary # bitcoinz_cli_SOURCES = bitcoin-cli.cpp @@ -448,7 +456,8 @@ bitcoinz_cli_LDADD = \ $(EVENT_LIBS) \ $(LIBZCASH) \ $(LIBBITCOIN_CRYPTO) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + $(LIBRUSTZCASH) # # bitcoinz-tx binary # @@ -469,7 +478,8 @@ bitcoinz_tx_LDADD = \ $(LIBSECP256K1) \ $(LIBZCASH) \ $(LIBBITCOIN_CRYPTO) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + $(LIBRUSTZCASH) bitcoinz_tx_LDADD += $(BOOST_LIBS) # @@ -505,6 +515,7 @@ EXTRA_DIST = $(CTAES_DIST) clean-local: -$(MAKE) -C secp256k1 clean + -rm -rf rustzcash/target -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno rm -f fuzz.cpp rm -rf fuzzing/*/output diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index c7de346d7..977c916b4 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -28,8 +28,7 @@ bench_bench_bitcoinz_LDADD = \ $(LIBLEVELDB) \ $(LIBMEMENV) \ $(LIBSECP256K1) \ - $(LIBZCASH) \ - $(LIBSNARK) + $(LIBZCASH) if ENABLE_ZMQ bench_bench_bitcoinz_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) @@ -39,7 +38,7 @@ if ENABLE_WALLET bench_bench_bitcoinz_LDADD += $(LIBBITCOIN_WALLET) endif -bench_bench_bitcoinz_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(LIBZCASH_LIBS) +bench_bench_bitcoinz_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(LIBZCASH_LIBS) $(LIBRUSTZCASH) bench_bench_bitcoinz_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) CLEAN_BITCOINZ_BENCH = bench/*.gcda bench/*.gcno diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 592d0244a..675f20577 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -85,7 +85,8 @@ bitcoinz_gtest_LDADD += \ $(ZMQ_LIBS) \ $(LIBZCASH) \ $(LIBRUSTZCASH) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + $(LIBRUSTZCASH) bitcoinz_gtest_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 728fa882f..bf83e7d26 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -129,7 +129,7 @@ test_test_bitcoinz_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_ $(LIBLEVELDB) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) test_test_bitcoinz_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_test_bitcoinz_LDADD += $(BDB_LIBS) $(LIBZCASH) $(LIBSNARK) $(LIBZCASH_LIBS) +test_test_bitcoinz_LDADD += $(BDB_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS) $(LIBRUSTZCASH) test_test_bitcoinz_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static if ENABLE_ZMQ diff --git a/src/rustzcash/.gitignore b/src/rustzcash/.gitignore new file mode 100644 index 000000000..f61ba9b79 --- /dev/null +++ b/src/rustzcash/.gitignore @@ -0,0 +1,2 @@ +target +librustzcash.a diff --git a/src/rustzcash/Cargo.lock b/src/rustzcash/Cargo.lock new file mode 100644 index 000000000..5e38bfb3e --- /dev/null +++ b/src/rustzcash/Cargo.lock @@ -0,0 +1,706 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aes" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aes-soft" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aesni" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bellman" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "group 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bigint" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit-vec" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2b_simd" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "blake2s_simd" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-cipher-trait" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "c2-chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "constant_time_eq" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crossbeam" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-channel" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crunchy" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crypto_api" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crypto_api_chachapoly" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "directories" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ff" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ff_derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ff_derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fpe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "group" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "librustzcash" +version = "0.2.0" +dependencies = [ + "bellman 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_history 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_primitives 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_proofs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memoffset" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-bigint" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pairing" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "group 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "sha2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "zcash_history" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zcash_primitives" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "zcash_proofs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bellman 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_primitives 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" +"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" +"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" +"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" +"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum bellman 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2be536193834affcd8a6d362963e66dec8c6bca4d2009f5bac55ec9002776ff2" +"checksum bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282" +"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" +"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" +"checksum blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "979da0ce13c897d6be19e005ea77ac12b0fea0157aeeee7feb8c49f91386f0ea" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" +"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" +"checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" +"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" +"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" +"checksum crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" +"checksum crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4b967a3ee6ae993f0094174257d404a5818f58be79d67a1aea1ec8996d28906" +"checksum ff_derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a3776aaf60a45037a9c3cabdd8542b38693acaa3e241ff957181b72579d29feb" +"checksum fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4" +"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" +"checksum group 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f15be54742789e36f03307c8fdf0621201e1345e94f1387282024178b5e9ec8c" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8290dea210a712682cd65031dc2b34fd132cf2729def3df7ee08f0737ff5ed6" +"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" +"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum zcash_history 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "abfbab9accba014bbf3098d5aa66c1714d0db4abe25b999b8400bbd626ccd2f4" +"checksum zcash_primitives 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f33b9e4f3b4db97234fc79ea67b12f2d5778bde8f3eab6dbba52eb54c596585" +"checksum zcash_proofs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2011f78f14d5121248d3b4f921434207b1d870fb3bf2efc7d784cae79b19bfbc" diff --git a/src/rustzcash/Cargo.toml b/src/rustzcash/Cargo.toml new file mode 100644 index 000000000..9cb295a1d --- /dev/null +++ b/src/rustzcash/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "librustzcash" +description = "Rust FFI used by the zcashd binary. Not an official API." +version = "0.2.0" +authors = [ + "Sean Bowe ", + "Jack Grigg ", + "Jay Graber ", + "Simon Liu " +] +homepage = "https://github.com/zcash/zcash" +repository = "https://github.com/zcash/zcash" +readme = "README.md" +license = "MIT OR Apache-2.0" +edition = "2018" + +[lib] +name = "rustzcash" +path = "src/rustzcash.rs" +crate-type = ["staticlib"] + +[dependencies] +bellman = "0.6" +blake2b_simd = "0.5" +blake2s_simd = "0.5" +ff = "0.6" +libc = "0.2" +pairing = "0.16" +lazy_static = "1" +rand_core = "0.5.1" +zcash_history = "0.2" +zcash_primitives = "0.2" +zcash_proofs = "0.2" + +[profile.release] +lto = true +panic = 'abort' +codegen-units = 1 diff --git a/src/rustzcash/README.md b/src/rustzcash/README.md new file mode 100644 index 000000000..7cdca84d9 --- /dev/null +++ b/src/rustzcash/README.md @@ -0,0 +1,24 @@ +# librustzcash + +`librustzcash` is an FFI library crate that exposes the Zcash Rust components to +the `zcashd` full node. + +The FFI API does not have any stability guarantees, and will change as required +by `zcashd`. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rustzcash/include/librustzcash.h b/src/rustzcash/include/librustzcash.h new file mode 100644 index 000000000..1aa85ed20 --- /dev/null +++ b/src/rustzcash/include/librustzcash.h @@ -0,0 +1,339 @@ +#ifndef LIBRUSTZCASH_INCLUDE_H_ +#define LIBRUSTZCASH_INCLUDE_H_ + +#include + +extern "C" { +#ifdef WIN32 + typedef uint16_t codeunit; +#else + typedef uint8_t codeunit; +#endif + + void librustzcash_to_scalar(const unsigned char *input, unsigned char *result); + + void librustzcash_ask_to_ak(const unsigned char *ask, unsigned char *result); + + void librustzcash_nsk_to_nk(const unsigned char *nsk, unsigned char *result); + + void librustzcash_crh_ivk(const unsigned char *ak, const unsigned char *nk, unsigned char *result); + + bool librustzcash_check_diversifier(const unsigned char *diversifier); + + bool librustzcash_ivk_to_pkd(const unsigned char *ivk, const unsigned char *diversifier, unsigned char *result); + + /// Loads the zk-SNARK parameters into memory and saves + /// paths as necessary. Only called once. + void librustzcash_init_zksnark_params( + const codeunit* spend_path, + size_t spend_path_len, + const char* spend_hash, + const codeunit* output_path, + size_t output_path_len, + const char* output_hash, + const codeunit* sprout_path, + size_t sprout_path_len, + const char* sprout_hash + ); + + /// Validates the provided Equihash solution against + /// the given parameters, input and nonce. + bool librustzcash_eh_isvalid( + uint32_t n, + uint32_t k, + const unsigned char* input, + size_t input_len, + const unsigned char* nonce, + size_t nonce_len, + const unsigned char* soln, + size_t soln_len + ); + + /// Writes the "uncommitted" note value for empty leaves + /// of the merkle tree. `result` must be a valid pointer + /// to 32 bytes which will be written. + void librustzcash_tree_uncommitted( + unsigned char *result + ); + + /// Computes a merkle tree hash for a given depth. + /// The `depth` parameter should not be larger than + /// 62. + /// + /// `a` and `b` each must be of length 32, and must each + /// be scalars of BLS12-381. + /// + /// The result of the merkle tree hash is placed in + /// `result`, which must also be of length 32. + void librustzcash_merkle_hash( + size_t depth, + const unsigned char *a, + const unsigned char *b, + unsigned char *result + ); + + /// Computes the signature for each Spend description, given the key + /// `ask`, the re-randomization `ar`, the 32-byte sighash `sighash`, + /// and an output `result` buffer of 64-bytes for the signature. + /// + /// This function will fail if the provided `ask` or `ar` are invalid. + bool librustzcash_sapling_spend_sig( + const unsigned char *ask, + const unsigned char *ar, + const unsigned char *sighash, + unsigned char *result + ); + + /// Creates a Sapling proving context. Please free this when you're done. + void * librustzcash_sapling_proving_ctx_init(); + + /// This function (using the proving context) constructs a Spend proof + /// given the necessary witness information. It outputs `cv` (the value + /// commitment) and `rk` (so that you don't have to compute it) along + /// with the proof. + bool librustzcash_sapling_spend_proof( + void *ctx, + const unsigned char *ak, + const unsigned char *nsk, + const unsigned char *diversifier, + const unsigned char *rcm, + const unsigned char *ar, + const uint64_t value, + const unsigned char *anchor, + const unsigned char *witness, + unsigned char *cv, + unsigned char *rk, + unsigned char *zkproof + ); + + /// This function (using the proving context) constructs an Output + /// proof given the necessary witness information. It outputs `cv` + /// and the `zkproof`. + bool librustzcash_sapling_output_proof( + void *ctx, + const unsigned char *esk, + const unsigned char *payment_address, + const unsigned char *rcm, + const uint64_t value, + unsigned char *cv, + unsigned char *zkproof + ); + + /// This function (using the proving context) constructs a binding + /// signature. You must provide the intended valueBalance so that + /// we can internally check consistency. + bool librustzcash_sapling_binding_sig( + const void *ctx, + int64_t valueBalance, + const unsigned char *sighash, + unsigned char *result + ); + + /// Frees a Sapling proving context returned from + /// `librustzcash_sapling_proving_ctx_init`. + void librustzcash_sapling_proving_ctx_free(void *); + + /// Creates a Sapling verification context. Please free this + /// when you're done. + void * librustzcash_sapling_verification_ctx_init(); + + /// Check the validity of a Sapling Spend description, + /// accumulating the value commitment into the context. + bool librustzcash_sapling_check_spend( + void *ctx, + const unsigned char *cv, + const unsigned char *anchor, + const unsigned char *nullifier, + const unsigned char *rk, + const unsigned char *zkproof, + const unsigned char *spendAuthSig, + const unsigned char *sighashValue + ); + + /// Check the validity of a Sapling Output description, + /// accumulating the value commitment into the context. + bool librustzcash_sapling_check_output( + void *ctx, + const unsigned char *cv, + const unsigned char *cm, + const unsigned char *ephemeralKey, + const unsigned char *zkproof + ); + + /// Finally checks the validity of the entire Sapling + /// transaction given valueBalance and the binding signature. + bool librustzcash_sapling_final_check( + void *ctx, + int64_t valueBalance, + const unsigned char *bindingSig, + const unsigned char *sighashValue + ); + + /// Frees a Sapling verification context returned from + /// `librustzcash_sapling_verification_ctx_init`. + void librustzcash_sapling_verification_ctx_free(void *); + + /// Compute a Sapling nullifier. + /// + /// The `diversifier` parameter must be 11 bytes in length. + /// The `pk_d`, `r`, `ak` and `nk` parameters must be of length 32. + /// The result is also of length 32 and placed in `result`. + /// Returns false if the diversifier or pk_d is not valid + bool librustzcash_sapling_compute_nf( + const unsigned char *diversifier, + const unsigned char *pk_d, + const uint64_t value, + const unsigned char *r, + const unsigned char *ak, + const unsigned char *nk, + const uint64_t position, + unsigned char *result + ); + + /// Compute a Sapling commitment. + /// + /// The `diversifier` parameter must be 11 bytes in length. + /// The `pk_d` and `r` parameters must be of length 32. + /// The result is also of length 32 and placed in `result`. + /// Returns false if the diversifier or pk_d is not valid + bool librustzcash_sapling_compute_cm( + const unsigned char *diversifier, + const unsigned char *pk_d, + const uint64_t value, + const unsigned char *r, + unsigned char *result + ); + + /// Compute [sk] [8] P for some 32-byte + /// point P, and 32-byte Fs. If P or sk + /// are invalid, returns false. Otherwise, + /// the result is written to the 32-byte + /// `result` buffer. + bool librustzcash_sapling_ka_agree( + const unsigned char *p, + const unsigned char *sk, + unsigned char *result + ); + + /// Compute g_d = GH(diversifier) and returns + /// false if the diversifier is invalid. + /// Computes [esk] g_d and writes the result + /// to the 32-byte `result` buffer. Returns + /// false if `esk` is not a valid scalar. + bool librustzcash_sapling_ka_derivepublic( + const unsigned char *diversifier, + const unsigned char *esk, + unsigned char *result + ); + + /// Generate uniformly random scalar in Jubjub. + /// The result is of length 32. + void librustzcash_sapling_generate_r( + unsigned char *result + ); + + /// Sprout JoinSplit proof generation. + void librustzcash_sprout_prove( + unsigned char *proof_out, + + const unsigned char *phi, + const unsigned char *rt, + const unsigned char *h_sig, + + const unsigned char *in_sk1, + uint64_t in_value1, + const unsigned char *in_rho1, + const unsigned char *in_r1, + const unsigned char *in_auth1, + + const unsigned char *in_sk2, + uint64_t in_value2, + const unsigned char *in_rho2, + const unsigned char *in_r2, + const unsigned char *in_auth2, + + const unsigned char *out_pk1, + uint64_t out_value1, + const unsigned char *out_r1, + + const unsigned char *out_pk2, + uint64_t out_value2, + const unsigned char *out_r2, + + uint64_t vpub_old, + uint64_t vpub_new + ); + + /// Sprout JoinSplit proof verification. + bool librustzcash_sprout_verify( + const unsigned char *proof, + const unsigned char *rt, + const unsigned char *h_sig, + const unsigned char *mac1, + const unsigned char *mac2, + const unsigned char *nf1, + const unsigned char *nf2, + const unsigned char *cm1, + const unsigned char *cm2, + uint64_t vpub_old, + uint64_t vpub_new + ); + + /// Derive the master ExtendedSpendingKey from a seed. + void librustzcash_zip32_xsk_master( + const unsigned char *seed, + size_t seedlen, + unsigned char *xsk_master + ); + + /// Derive a child ExtendedSpendingKey from a parent. + void librustzcash_zip32_xsk_derive( + const unsigned char *xsk_parent, + uint32_t i, + unsigned char *xsk_i + ); + + /// Derive a child ExtendedFullViewingKey from a parent. + bool librustzcash_zip32_xfvk_derive( + const unsigned char *xfvk_parent, + uint32_t i, + unsigned char *xfvk_i + ); + + /// Derive a PaymentAddress from an ExtendedFullViewingKey. + bool librustzcash_zip32_xfvk_address( + const unsigned char *xfvk, + const unsigned char *j, + unsigned char *j_ret, + unsigned char *addr_ret + ); + + uint32_t librustzcash_mmr_append( + uint32_t cbranch, + uint32_t t_len, + const uint32_t *ni_ptr, + const unsigned char *n_ptr, + size_t p_len, + const unsigned char *nn_ptr, + unsigned char *rt_ret, + unsigned char *buf_ret + ); + + uint32_t librustzcash_mmr_delete( + uint32_t cbranch, + uint32_t t_len, + const uint32_t *ni_ptr, + const unsigned char *n_ptr, + size_t p_len, + size_t e_len, + unsigned char *rt_ret + ); + + uint32_t librustzcash_mmr_hash_node( + uint32_t cbranch, + const unsigned char *n_ptr, + unsigned char *h_ret + ); +} + +#endif // LIBRUSTZCASH_INCLUDE_H_ diff --git a/src/rustzcash/src/rustzcash.rs b/src/rustzcash/src/rustzcash.rs new file mode 100644 index 000000000..5c2481c3b --- /dev/null +++ b/src/rustzcash/src/rustzcash.rs @@ -0,0 +1,1342 @@ +//! FFI between the C++ zcashd codebase and the Rust Zcash crates. +//! +//! This is internal to zcashd and is not an officially-supported API. + +// Catch documentation errors caused by code changes. +#![deny(broken_intra_doc_links)] +// Clippy has a default-deny lint to prevent dereferencing raw pointer arguments +// in a non-unsafe function. However, declaring a function as unsafe has the +// side-effect that the entire function body is treated as an unsafe {} block, +// and rustc will not enforce full safety checks on the parts of the function +// that would otherwise be safe. +// +// The functions in this crate are all for FFI usage, so it's obvious to the +// caller (which is only ever zcashd) that the arguments must satisfy the +// necessary assumptions. We therefore ignore this lint to retain the benefit of +// explicitly annotating the parts of each function that must themselves satisfy +// assumptions of underlying code. +// +// See https://github.com/rust-lang/rfcs/pull/2585 for more background. +#![allow(clippy::not_unsafe_ptr_arg_deref)] + +use bellman::groth16::{Parameters, PreparedVerifyingKey, Proof}; +use blake2s_simd::Params as Blake2sParams; +use ff::{PrimeField, PrimeFieldRepr}; +use lazy_static; +use libc::{c_char, c_uchar, size_t}; +use pairing::bls12_381::{Bls12, Fr, FrRepr}; +use rand_core::{OsRng, RngCore}; +use std::ffi::CStr; +use std::fs::File; +use std::io::BufReader; +use std::path::{Path, PathBuf}; +use std::slice; + +#[cfg(not(target_os = "windows"))] +use std::ffi::OsStr; +#[cfg(not(target_os = "windows"))] +use std::os::unix::ffi::OsStrExt; + +#[cfg(target_os = "windows")] +use std::ffi::OsString; +#[cfg(target_os = "windows")] +use std::os::windows::ffi::OsStringExt; + +use zcash_primitives::{ + block::equihash, + constants::CRH_IVK_PERSONALIZATION, + jubjub::{ + edwards, + fs::{Fs, FsRepr}, + FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown, + }, + merkle_tree::MerklePath, + note_encryption::sapling_ka_agree, + primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ViewingKey}, + redjubjub::{self, Signature}, + sapling::{merkle_hash, spend_sig}, + transaction::components::Amount, + zip32, JUBJUB, +}; +use zcash_proofs::{ + circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH, + load_parameters, + sapling::{SaplingProvingContext, SaplingVerificationContext}, + sprout, +}; + +use zcash_history::{Entry as MMREntry, NodeData as MMRNodeData, Tree as MMRTree}; + +#[cfg(test)] +mod tests; + +static mut SAPLING_SPEND_VK: Option> = None; +static mut SAPLING_OUTPUT_VK: Option> = None; +static mut SPROUT_GROTH16_VK: Option> = None; + +static mut SAPLING_SPEND_PARAMS: Option> = None; +static mut SAPLING_OUTPUT_PARAMS: Option> = None; +static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; + +/// Reads an FrRepr from a [u8; 32]. +fn read_fr(from: &[u8; 32]) -> FrRepr { + let mut f = FrRepr::default(); + f.read_le(&from[..]).expect("length is 32 bytes"); + f +} + +/// Reads an FsRepr from a [u8; 32]. +fn read_fs(from: &[u8; 32]) -> FsRepr { + let mut f = <::Fs as PrimeField>::Repr::default(); + f.read_le(&from[..]).expect("length is 32 bytes"); + f +} + +/// Reads an FsRepr from a [u8; 32] +/// and multiplies it by the given base. +fn fixed_scalar_mult(from: &[u8; 32], p_g: FixedGenerators) -> edwards::Point { + let f = read_fs(from); + + JUBJUB.generator(p_g).mul(f, &JUBJUB) +} + +/// Loads the zk-SNARK parameters into memory and saves paths as necessary. +/// Only called once. +#[cfg(not(target_os = "windows"))] +#[no_mangle] +pub extern "C" fn librustzcash_init_zksnark_params( + spend_path: *const u8, + spend_path_len: usize, + spend_hash: *const c_char, + output_path: *const u8, + output_path_len: usize, + output_hash: *const c_char, + sprout_path: *const u8, + sprout_path_len: usize, + sprout_hash: *const c_char, +) { + let spend_path = Path::new(OsStr::from_bytes(unsafe { + slice::from_raw_parts(spend_path, spend_path_len) + })); + let output_path = Path::new(OsStr::from_bytes(unsafe { + slice::from_raw_parts(output_path, output_path_len) + })); + let sprout_path = if sprout_path.is_null() { + None + } else { + Some(Path::new(OsStr::from_bytes(unsafe { + slice::from_raw_parts(sprout_path, sprout_path_len) + }))) + }; + + init_zksnark_params( + spend_path, + spend_hash, + output_path, + output_hash, + sprout_path, + sprout_hash, + ) +} + +/// Loads the zk-SNARK parameters into memory and saves paths as necessary. +/// Only called once. +#[cfg(target_os = "windows")] +#[no_mangle] +pub extern "C" fn librustzcash_init_zksnark_params( + spend_path: *const u16, + spend_path_len: usize, + spend_hash: *const c_char, + output_path: *const u16, + output_path_len: usize, + output_hash: *const c_char, + sprout_path: *const u16, + sprout_path_len: usize, + sprout_hash: *const c_char, +) { + let spend_path = + OsString::from_wide(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }); + let output_path = + OsString::from_wide(unsafe { slice::from_raw_parts(output_path, output_path_len) }); + let sprout_path = if sprout_path.is_null() { + None + } else { + Some(OsString::from_wide(unsafe { + slice::from_raw_parts(sprout_path, sprout_path_len) + })) + }; + + init_zksnark_params( + Path::new(&spend_path), + spend_hash, + Path::new(&output_path), + output_hash, + sprout_path.as_ref().map(|p| Path::new(p)), + sprout_hash, + ) +} + +fn init_zksnark_params( + spend_path: &Path, + spend_hash: *const c_char, + output_path: &Path, + output_hash: *const c_char, + sprout_path: Option<&Path>, + sprout_hash: *const c_char, +) { + // Initialize jubjub parameters here + lazy_static::initialize(&JUBJUB); + + let spend_hash = unsafe { CStr::from_ptr(spend_hash) } + .to_str() + .expect("hash should be a valid string"); + + let output_hash = unsafe { CStr::from_ptr(output_hash) } + .to_str() + .expect("hash should be a valid string"); + + let sprout_hash = if sprout_path.is_none() { + None + } else { + Some( + unsafe { CStr::from_ptr(sprout_hash) } + .to_str() + .expect("hash should be a valid string"), + ) + }; + + // Load params + let (spend_params, spend_vk, output_params, output_vk, sprout_vk) = load_parameters( + spend_path, + spend_hash, + output_path, + output_hash, + sprout_path, + sprout_hash, + ); + + // Caller is responsible for calling this function once, so + // these global mutations are safe. + unsafe { + SAPLING_SPEND_PARAMS = Some(spend_params); + SAPLING_OUTPUT_PARAMS = Some(output_params); + SPROUT_GROTH16_PARAMS_PATH = sprout_path.map(|p| p.to_owned()); + + SAPLING_SPEND_VK = Some(spend_vk); + SAPLING_OUTPUT_VK = Some(output_vk); + SPROUT_GROTH16_VK = sprout_vk; + } +} + +/// Writes the "uncommitted" note value for empty leaves of the Merkle tree. +/// +/// `result` must be a valid pointer to 32 bytes which will be written. +#[no_mangle] +pub extern "C" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) { + let tmp = Note::::uncommitted().into_repr(); + + // Should be okay, caller is responsible for ensuring the pointer + // is a valid pointer to 32 bytes that can be mutated. + let result = unsafe { &mut *result }; + tmp.write_le(&mut result[..]).expect("length is 32 bytes"); +} + +/// Computes a merkle tree hash for a given depth. The `depth` parameter should +/// not be larger than 62. +/// +/// `a` and `b` each must be of length 32, and must each be scalars of BLS12-381. +/// +/// The result of the merkle tree hash is placed in `result`, which must also be +/// of length 32. +#[no_mangle] +pub extern "C" fn librustzcash_merkle_hash( + depth: size_t, + a: *const [c_uchar; 32], + b: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) { + // Should be okay, because caller is responsible for ensuring + // the pointer is a valid pointer to 32 bytes, and that is the + // size of the representation + let a_repr = read_fr(unsafe { &*a }); + + // Should be okay, because caller is responsible for ensuring + // the pointer is a valid pointer to 32 bytes, and that is the + // size of the representation + let b_repr = read_fr(unsafe { &*b }); + + let tmp = merkle_hash(depth, &a_repr, &b_repr); + + // Should be okay, caller is responsible for ensuring the pointer + // is a valid pointer to 32 bytes that can be mutated. + let result = unsafe { &mut *result }; + tmp.write_le(&mut result[..]).expect("length is 32 bytes"); +} + +#[no_mangle] // ToScalar +pub extern "C" fn librustzcash_to_scalar(input: *const [c_uchar; 64], result: *mut [c_uchar; 32]) { + // Should be okay, because caller is responsible for ensuring + // the pointer is a valid pointer to 32 bytes, and that is the + // size of the representation + let scalar = ::Fs::to_uniform(unsafe { &(&*input)[..] }).into_repr(); + + let result = unsafe { &mut *result }; + + scalar + .write_le(&mut result[..]) + .expect("length is 32 bytes"); +} + +#[no_mangle] +pub extern "C" fn librustzcash_ask_to_ak(ask: *const [c_uchar; 32], result: *mut [c_uchar; 32]) { + let ask = unsafe { &*ask }; + let ak = fixed_scalar_mult(ask, FixedGenerators::SpendingKeyGenerator); + + let result = unsafe { &mut *result }; + + ak.write(&mut result[..]).expect("length is 32 bytes"); +} + +#[no_mangle] +pub extern "C" fn librustzcash_nsk_to_nk(nsk: *const [c_uchar; 32], result: *mut [c_uchar; 32]) { + let nsk = unsafe { &*nsk }; + let nk = fixed_scalar_mult(nsk, FixedGenerators::ProofGenerationKey); + + let result = unsafe { &mut *result }; + + nk.write(&mut result[..]).expect("length is 32 bytes"); +} + +#[no_mangle] +pub extern "C" fn librustzcash_crh_ivk( + ak: *const [c_uchar; 32], + nk: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) { + let ak = unsafe { &*ak }; + let nk = unsafe { &*nk }; + + let mut h = Blake2sParams::new() + .hash_length(32) + .personal(CRH_IVK_PERSONALIZATION) + .to_state(); + h.update(ak); + h.update(nk); + let mut h = h.finalize().as_ref().to_vec(); + + // Drop the last five bits, so it can be interpreted as a scalar. + h[31] &= 0b0000_0111; + + let result = unsafe { &mut *result }; + + result.copy_from_slice(&h); +} + +#[no_mangle] +pub extern "C" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool { + let diversifier = Diversifier(unsafe { *diversifier }); + diversifier.g_d::(&JUBJUB).is_some() +} + +#[no_mangle] +pub extern "C" fn librustzcash_ivk_to_pkd( + ivk: *const [c_uchar; 32], + diversifier: *const [c_uchar; 11], + result: *mut [c_uchar; 32], +) -> bool { + let ivk = read_fs(unsafe { &*ivk }); + let diversifier = Diversifier(unsafe { *diversifier }); + if let Some(g_d) = diversifier.g_d::(&JUBJUB) { + let pk_d = g_d.mul(ivk, &JUBJUB); + + let result = unsafe { &mut *result }; + + pk_d.write(&mut result[..]).expect("length is 32 bytes"); + + true + } else { + false + } +} + +/// Test generation of commitment randomness +#[test] +fn test_gen_r() { + let mut r1 = [0u8; 32]; + let mut r2 = [0u8; 32]; + + // Verify different r values are generated + librustzcash_sapling_generate_r(&mut r1); + librustzcash_sapling_generate_r(&mut r2); + assert_ne!(r1, r2); + + // Verify r values are valid in the field + let mut repr = FsRepr::default(); + repr.read_le(&r1[..]).expect("length is not 32 bytes"); + let _ = Fs::from_repr(repr).unwrap(); + repr.read_le(&r2[..]).expect("length is not 32 bytes"); + let _ = Fs::from_repr(repr).unwrap(); +} + +/// Generate uniformly random scalar in Jubjub. The result is of length 32. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { + // create random 64 byte buffer + let mut rng = OsRng; + let mut buffer = [0u8; 64]; + rng.fill_bytes(&mut buffer); + + // reduce to uniform value + let r = ::Fs::to_uniform(&buffer[..]); + let result = unsafe { &mut *result }; + r.into_repr() + .write_le(&mut result[..]) + .expect("result must be 32 bytes"); +} + +// Private utility function to get Note from C parameters +fn priv_get_note( + diversifier: *const [c_uchar; 11], + pk_d: *const [c_uchar; 32], + value: u64, + r: *const [c_uchar; 32], +) -> Result, ()> { + let diversifier = Diversifier(unsafe { *diversifier }); + let g_d = diversifier.g_d::(&JUBJUB).ok_or(())?; + + let pk_d = edwards::Point::::read(&(unsafe { &*pk_d })[..], &JUBJUB) + .map_err(|_| ())?; + + let pk_d = pk_d.as_prime_order(&JUBJUB).ok_or(())?; + + // Deserialize randomness + let r = Fs::from_repr(read_fs(unsafe { &*r })).map_err(|_| ())?; + + let note = Note { + value, + g_d, + pk_d, + r, + }; + + Ok(note) +} + +/// Compute a Sapling nullifier. +/// +/// The `diversifier` parameter must be 11 bytes in length. +/// The `pk_d`, `r`, `ak` and `nk` parameters must be of length 32. +/// The result is also of length 32 and placed in `result`. +/// Returns false if `diversifier` or `pk_d` is not valid. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_compute_nf( + diversifier: *const [c_uchar; 11], + pk_d: *const [c_uchar; 32], + value: u64, + r: *const [c_uchar; 32], + ak: *const [c_uchar; 32], + nk: *const [c_uchar; 32], + position: u64, + result: *mut [c_uchar; 32], +) -> bool { + let note = match priv_get_note(diversifier, pk_d, value, r) { + Ok(p) => p, + Err(_) => return false, + }; + + let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + let ak = match ak.as_prime_order(&JUBJUB) { + Some(ak) => ak, + None => return false, + }; + + let nk = match edwards::Point::::read(&(unsafe { &*nk })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + let nk = match nk.as_prime_order(&JUBJUB) { + Some(nk) => nk, + None => return false, + }; + + let vk = ViewingKey { ak, nk }; + let nf = note.nf(&vk, position, &JUBJUB); + let result = unsafe { &mut *result }; + result.copy_from_slice(&nf); + + true +} + +/// Compute a Sapling commitment. +/// +/// The `diversifier` parameter must be 11 bytes in length. +/// The `pk_d` and `r` parameters must be of length 32. +/// The result is also of length 32 and placed in `result`. +/// Returns false if `diversifier` or `pk_d` is not valid. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_compute_cm( + diversifier: *const [c_uchar; 11], + pk_d: *const [c_uchar; 32], + value: u64, + r: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) -> bool { + let note = match priv_get_note(diversifier, pk_d, value, r) { + Ok(p) => p, + Err(_) => return false, + }; + + let result = unsafe { &mut *result }; + note.cm(&JUBJUB) + .into_repr() + .write_le(&mut result[..]) + .expect("length is 32 bytes"); + + true +} + +/// Computes \[sk\] \[8\] P for some 32-byte point P, and 32-byte Fs. +/// +/// If P or sk are invalid, returns false. Otherwise, the result is written to +/// the 32-byte `result` buffer. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_ka_agree( + p: *const [c_uchar; 32], + sk: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) -> bool { + // Deserialize p + let p = match edwards::Point::::read(&(unsafe { &*p })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // Deserialize sk + let sk = match Fs::from_repr(read_fs(unsafe { &*sk })) { + Ok(p) => p, + Err(_) => return false, + }; + + // Compute key agreement + let ka = sapling_ka_agree(&sk, &p); + + // Produce result + let result = unsafe { &mut *result }; + ka.write(&mut result[..]).expect("length is not 32 bytes"); + + true +} + +/// Compute g_d = GH(diversifier) and returns false if the diversifier is +/// invalid. Computes \[esk\] g_d and writes the result to the 32-byte `result` +/// buffer. Returns false if `esk` is not a valid scalar. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_ka_derivepublic( + diversifier: *const [c_uchar; 11], + esk: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) -> bool { + let diversifier = Diversifier(unsafe { *diversifier }); + + // Compute g_d from the diversifier + let g_d = match diversifier.g_d::(&JUBJUB) { + Some(g) => g, + None => return false, + }; + + // Deserialize esk + let esk = match Fs::from_repr(read_fs(unsafe { &*esk })) { + Ok(p) => p, + Err(_) => return false, + }; + + let p = g_d.mul(esk, &JUBJUB); + + let result = unsafe { &mut *result }; + p.write(&mut result[..]).expect("length is not 32 bytes"); + + true +} + +/// Validates the provided Equihash solution against the given parameters, input +/// and nonce. +#[no_mangle] +pub extern "C" fn librustzcash_eh_isvalid( + n: u32, + k: u32, + input: *const c_uchar, + input_len: size_t, + nonce: *const c_uchar, + nonce_len: size_t, + soln: *const c_uchar, + soln_len: size_t, +) -> bool { + if (k >= n) || (n % 8 != 0) || (soln_len != (1 << k) * ((n / (k + 1)) as usize + 1) / 8) { + return false; + } + let rs_input = unsafe { slice::from_raw_parts(input, input_len) }; + let rs_nonce = unsafe { slice::from_raw_parts(nonce, nonce_len) }; + let rs_soln = unsafe { slice::from_raw_parts(soln, soln_len) }; + equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln) +} + +/// Creates a Sapling verification context. Please free this when you're done. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_verification_ctx_init() -> *mut SaplingVerificationContext { + let ctx = Box::new(SaplingVerificationContext::new()); + + Box::into_raw(ctx) +} + +/// Frees a Sapling verification context returned from +/// [`librustzcash_sapling_verification_ctx_init`]. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_verification_ctx_free(ctx: *mut SaplingVerificationContext) { + drop(unsafe { Box::from_raw(ctx) }); +} + +const GROTH_PROOF_SIZE: usize = 48 // π_A + + 96 // π_B + + 48; // π_C + +/// Check the validity of a Sapling Spend description, accumulating the value +/// commitment into the context. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_check_spend( + ctx: *mut SaplingVerificationContext, + cv: *const [c_uchar; 32], + anchor: *const [c_uchar; 32], + nullifier: *const [c_uchar; 32], + rk: *const [c_uchar; 32], + zkproof: *const [c_uchar; GROTH_PROOF_SIZE], + spend_auth_sig: *const [c_uchar; 64], + sighash_value: *const [c_uchar; 32], +) -> bool { + // Deserialize the value commitment + let cv = match edwards::Point::::read(&(unsafe { &*cv })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // Deserialize the anchor, which should be an element + // of Fr. + let anchor = match Fr::from_repr(read_fr(unsafe { &*anchor })) { + Ok(a) => a, + Err(_) => return false, + }; + + // Deserialize rk + let rk = match redjubjub::PublicKey::::read(&(unsafe { &*rk })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // Deserialize the signature + let spend_auth_sig = match Signature::read(&(unsafe { &*spend_auth_sig })[..]) { + Ok(sig) => sig, + Err(_) => return false, + }; + + // Deserialize the proof + let zkproof = match Proof::::read(&(unsafe { &*zkproof })[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + unsafe { &mut *ctx }.check_spend( + cv, + anchor, + unsafe { &*nullifier }, + rk, + unsafe { &*sighash_value }, + spend_auth_sig, + zkproof, + unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), + &JUBJUB, + ) +} + +/// Check the validity of a Sapling Output description, accumulating the value +/// commitment into the context. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_check_output( + ctx: *mut SaplingVerificationContext, + cv: *const [c_uchar; 32], + cm: *const [c_uchar; 32], + epk: *const [c_uchar; 32], + zkproof: *const [c_uchar; GROTH_PROOF_SIZE], +) -> bool { + // Deserialize the value commitment + let cv = match edwards::Point::::read(&(unsafe { &*cv })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // Deserialize the commitment, which should be an element + // of Fr. + let cm = match Fr::from_repr(read_fr(unsafe { &*cm })) { + Ok(a) => a, + Err(_) => return false, + }; + + // Deserialize the ephemeral key + let epk = match edwards::Point::::read(&(unsafe { &*epk })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // Deserialize the proof + let zkproof = match Proof::::read(&(unsafe { &*zkproof })[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + unsafe { &mut *ctx }.check_output( + cv, + cm, + epk, + zkproof, + unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(), + &JUBJUB, + ) +} + +/// Finally checks the validity of the entire Sapling transaction given +/// valueBalance and the binding signature. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_final_check( + ctx: *mut SaplingVerificationContext, + value_balance: i64, + binding_sig: *const [c_uchar; 64], + sighash_value: *const [c_uchar; 32], +) -> bool { + let value_balance = match Amount::from_i64(value_balance) { + Ok(vb) => vb, + Err(()) => return false, + }; + + // Deserialize the signature + let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) { + Ok(sig) => sig, + Err(_) => return false, + }; + + unsafe { &*ctx }.final_check( + value_balance, + unsafe { &*sighash_value }, + binding_sig, + &JUBJUB, + ) +} + +/// Sprout JoinSplit proof generation. +#[no_mangle] +pub extern "C" fn librustzcash_sprout_prove( + proof_out: *mut [c_uchar; GROTH_PROOF_SIZE], + + phi: *const [c_uchar; 32], + rt: *const [c_uchar; 32], + h_sig: *const [c_uchar; 32], + + // First input + in_sk1: *const [c_uchar; 32], + in_value1: u64, + in_rho1: *const [c_uchar; 32], + in_r1: *const [c_uchar; 32], + in_auth1: *const [c_uchar; sprout::WITNESS_PATH_SIZE], + + // Second input + in_sk2: *const [c_uchar; 32], + in_value2: u64, + in_rho2: *const [c_uchar; 32], + in_r2: *const [c_uchar; 32], + in_auth2: *const [c_uchar; sprout::WITNESS_PATH_SIZE], + + // First output + out_pk1: *const [c_uchar; 32], + out_value1: u64, + out_r1: *const [c_uchar; 32], + + // Second output + out_pk2: *const [c_uchar; 32], + out_value2: u64, + out_r2: *const [c_uchar; 32], + + // Public value + vpub_old: u64, + vpub_new: u64, +) { + // Load parameters from disk + let sprout_fs = File::open( + unsafe { &SPROUT_GROTH16_PARAMS_PATH } + .as_ref() + .expect("parameters should have been initialized"), + ) + .expect("couldn't load Sprout groth16 parameters file"); + + let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs); + + let params = Parameters::::read(&mut sprout_fs, false) + .expect("couldn't deserialize Sprout JoinSplit parameters file"); + + drop(sprout_fs); + + let proof = sprout::create_proof( + unsafe { *phi }, + unsafe { *rt }, + unsafe { *h_sig }, + unsafe { *in_sk1 }, + in_value1, + unsafe { *in_rho1 }, + unsafe { *in_r1 }, + unsafe { &*in_auth1 }, + unsafe { *in_sk2 }, + in_value2, + unsafe { *in_rho2 }, + unsafe { *in_r2 }, + unsafe { &*in_auth2 }, + unsafe { *out_pk1 }, + out_value1, + unsafe { *out_r1 }, + unsafe { *out_pk2 }, + out_value2, + unsafe { *out_r2 }, + vpub_old, + vpub_new, + ¶ms, + ); + + proof + .write(&mut (unsafe { &mut *proof_out })[..]) + .expect("should be able to serialize a proof"); +} + +/// Sprout JoinSplit proof verification. +#[no_mangle] +pub extern "C" fn librustzcash_sprout_verify( + proof: *const [c_uchar; GROTH_PROOF_SIZE], + rt: *const [c_uchar; 32], + h_sig: *const [c_uchar; 32], + mac1: *const [c_uchar; 32], + mac2: *const [c_uchar; 32], + nf1: *const [c_uchar; 32], + nf2: *const [c_uchar; 32], + cm1: *const [c_uchar; 32], + cm2: *const [c_uchar; 32], + vpub_old: u64, + vpub_new: u64, +) -> bool { + sprout::verify_proof( + unsafe { &*proof }, + unsafe { &*rt }, + unsafe { &*h_sig }, + unsafe { &*mac1 }, + unsafe { &*mac2 }, + unsafe { &*nf1 }, + unsafe { &*nf2 }, + unsafe { &*cm1 }, + unsafe { &*cm2 }, + vpub_old, + vpub_new, + unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"), + ) +} + +/// This function (using the proving context) constructs an Output proof given +/// the necessary witness information. It outputs `cv` and the `zkproof`. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_output_proof( + ctx: *mut SaplingProvingContext, + esk: *const [c_uchar; 32], + payment_address: *const [c_uchar; 43], + rcm: *const [c_uchar; 32], + value: u64, + cv: *mut [c_uchar; 32], + zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], +) -> bool { + // Grab `esk`, which the caller should have constructed for the DH key exchange. + let esk = match Fs::from_repr(read_fs(unsafe { &*esk })) { + Ok(p) => p, + Err(_) => return false, + }; + + // Grab the payment address from the caller + let payment_address = + match PaymentAddress::::from_bytes(unsafe { &*payment_address }, &JUBJUB) { + Some(pa) => pa, + None => return false, + }; + + // The caller provides the commitment randomness for the output note + let rcm = match Fs::from_repr(read_fs(unsafe { &*rcm })) { + Ok(p) => p, + Err(_) => return false, + }; + + // Create proof + let (proof, value_commitment) = unsafe { &mut *ctx }.output_proof( + esk, + payment_address, + rcm, + value, + unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(), + &JUBJUB, + ); + + // Write the proof out to the caller + proof + .write(&mut (unsafe { &mut *zkproof })[..]) + .expect("should be able to serialize a proof"); + + // Write the value commitment to the caller + value_commitment + .write(&mut (unsafe { &mut *cv })[..]) + .expect("should be able to serialize rcv"); + + true +} + +/// Computes the signature for each Spend description, given the key `ask`, the +/// re-randomization `ar`, the 32-byte sighash `sighash`, and an output `result` +/// buffer of 64-bytes for the signature. +/// +/// This function will fail if the provided `ask` or `ar` are invalid. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_spend_sig( + ask: *const [c_uchar; 32], + ar: *const [c_uchar; 32], + sighash: *const [c_uchar; 32], + result: *mut [c_uchar; 64], +) -> bool { + // The caller provides the re-randomization of `ak`. + let ar = match Fs::from_repr(read_fs(unsafe { &*ar })) { + Ok(p) => p, + Err(_) => return false, + }; + + // The caller provides `ask`, the spend authorizing key. + let ask = match redjubjub::PrivateKey::::read(&(unsafe { &*ask })[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + // Initialize secure RNG + let mut rng = OsRng; + + // Do the signing + let sig = spend_sig(ask, ar, unsafe { &*sighash }, &mut rng, &JUBJUB); + + // Write out the signature + sig.write(&mut (unsafe { &mut *result })[..]) + .expect("result should be 64 bytes"); + + true +} + +/// This function (using the proving context) constructs a binding signature. +/// +/// You must provide the intended valueBalance so that we can internally check +/// consistency. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_binding_sig( + ctx: *const SaplingProvingContext, + value_balance: i64, + sighash: *const [c_uchar; 32], + result: *mut [c_uchar; 64], +) -> bool { + let value_balance = match Amount::from_i64(value_balance) { + Ok(vb) => vb, + Err(()) => return false, + }; + + // Sign + let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }, &JUBJUB) { + Ok(s) => s, + Err(_) => return false, + }; + + // Write out signature + sig.write(&mut (unsafe { &mut *result })[..]) + .expect("result should be 64 bytes"); + + true +} + +/// This function (using the proving context) constructs a Spend proof given the +/// necessary witness information. It outputs `cv` (the value commitment) and +/// `rk` (so that you don't have to compute it) along with the proof. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_spend_proof( + ctx: *mut SaplingProvingContext, + ak: *const [c_uchar; 32], + nsk: *const [c_uchar; 32], + diversifier: *const [c_uchar; 11], + rcm: *const [c_uchar; 32], + ar: *const [c_uchar; 32], + value: u64, + anchor: *const [c_uchar; 32], + merkle_path: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8], + cv: *mut [c_uchar; 32], + rk_out: *mut [c_uchar; 32], + zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], +) -> bool { + // Grab `ak` from the caller, which should be a point. + let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // `ak` should be prime order. + let ak = match ak.as_prime_order(&JUBJUB) { + Some(p) => p, + None => return false, + }; + + // Grab `nsk` from the caller + let nsk = match Fs::from_repr(read_fs(unsafe { &*nsk })) { + Ok(p) => p, + Err(_) => return false, + }; + + // Construct the proof generation key + let proof_generation_key = ProofGenerationKey { + ak: ak.clone(), + nsk, + }; + + // Grab the diversifier from the caller + let diversifier = Diversifier(unsafe { *diversifier }); + + // The caller chooses the note randomness + let rcm = match Fs::from_repr(read_fs(unsafe { &*rcm })) { + Ok(p) => p, + Err(_) => return false, + }; + + // The caller also chooses the re-randomization of ak + let ar = match Fs::from_repr(read_fs(unsafe { &*ar })) { + Ok(p) => p, + Err(_) => return false, + }; + + // We need to compute the anchor of the Spend. + let anchor = match Fr::from_repr(read_fr(unsafe { &*anchor })) { + Ok(p) => p, + Err(_) => return false, + }; + + // Parse the Merkle path from the caller + let merkle_path = match MerklePath::from_slice(unsafe { &(&*merkle_path)[..] }) { + Ok(w) => w, + Err(_) => return false, + }; + + // Create proof + let (proof, value_commitment, rk) = unsafe { &mut *ctx } + .spend_proof( + proof_generation_key, + diversifier, + rcm, + ar, + value, + anchor, + merkle_path, + unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(), + unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), + &JUBJUB, + ) + .expect("proving should not fail"); + + // Write value commitment to caller + value_commitment + .write(&mut unsafe { &mut *cv }[..]) + .expect("should be able to serialize cv"); + + // Write proof out to caller + proof + .write(&mut (unsafe { &mut *zkproof })[..]) + .expect("should be able to serialize a proof"); + + // Write out `rk` to the caller + rk.write(&mut unsafe { &mut *rk_out }[..]) + .expect("should be able to write to rk_out"); + + true +} + +/// Creates a Sapling proving context. Please free this when you're done. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext { + let ctx = Box::new(SaplingProvingContext::new()); + + Box::into_raw(ctx) +} + +/// Frees a Sapling proving context returned from +/// [`librustzcash_sapling_proving_ctx_init`]. +#[no_mangle] +pub extern "C" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) { + drop(unsafe { Box::from_raw(ctx) }); +} + +/// Derive the master ExtendedSpendingKey from a seed. +#[no_mangle] +pub extern "C" fn librustzcash_zip32_xsk_master( + seed: *const c_uchar, + seedlen: size_t, + xsk_master: *mut [c_uchar; 169], +) { + let seed = unsafe { std::slice::from_raw_parts(seed, seedlen) }; + + let xsk = zip32::ExtendedSpendingKey::master(seed); + + xsk.write(&mut (unsafe { &mut *xsk_master })[..]) + .expect("should be able to serialize an ExtendedSpendingKey"); +} + +/// Derive a child ExtendedSpendingKey from a parent. +#[no_mangle] +pub extern "C" fn librustzcash_zip32_xsk_derive( + xsk_parent: *const [c_uchar; 169], + i: u32, + xsk_i: *mut [c_uchar; 169], +) { + let xsk_parent = zip32::ExtendedSpendingKey::read(&unsafe { *xsk_parent }[..]) + .expect("valid ExtendedSpendingKey"); + let i = zip32::ChildIndex::from_index(i); + + let xsk = xsk_parent.derive_child(i); + + xsk.write(&mut (unsafe { &mut *xsk_i })[..]) + .expect("should be able to serialize an ExtendedSpendingKey"); +} + +/// Derive a child ExtendedFullViewingKey from a parent. +#[no_mangle] +pub extern "C" fn librustzcash_zip32_xfvk_derive( + xfvk_parent: *const [c_uchar; 169], + i: u32, + xfvk_i: *mut [c_uchar; 169], +) -> bool { + let xfvk_parent = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk_parent }[..]) + .expect("valid ExtendedFullViewingKey"); + let i = zip32::ChildIndex::from_index(i); + + let xfvk = match xfvk_parent.derive_child(i) { + Ok(xfvk) => xfvk, + Err(_) => return false, + }; + + xfvk.write(&mut (unsafe { &mut *xfvk_i })[..]) + .expect("should be able to serialize an ExtendedFullViewingKey"); + + true +} + +/// Derive a PaymentAddress from an ExtendedFullViewingKey. +#[no_mangle] +pub extern "C" fn librustzcash_zip32_xfvk_address( + xfvk: *const [c_uchar; 169], + j: *const [c_uchar; 11], + j_ret: *mut [c_uchar; 11], + addr_ret: *mut [c_uchar; 43], +) -> bool { + let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..]) + .expect("valid ExtendedFullViewingKey"); + let j = zip32::DiversifierIndex(unsafe { *j }); + + let addr = match xfvk.address(j) { + Ok(addr) => addr, + Err(_) => return false, + }; + + let j_ret = unsafe { &mut *j_ret }; + let addr_ret = unsafe { &mut *addr_ret }; + + j_ret.copy_from_slice(&(addr.0).0); + addr_ret.copy_from_slice(&addr.1.to_bytes()); + + true +} + +fn construct_mmr_tree( + // Consensus branch id + cbranch: u32, + // Length of tree in array representation + t_len: u32, + + // Indices of provided tree nodes, length of p_len+e_len + ni_ptr: *const u32, + // Provided tree nodes data, length of p_len+e_len + n_ptr: *const [c_uchar; zcash_history::MAX_ENTRY_SIZE], + + // Peaks count + p_len: size_t, + // Extra nodes loaded (for deletion) count + e_len: size_t, +) -> Result { + let (indices, nodes) = unsafe { + ( + slice::from_raw_parts(ni_ptr, p_len + e_len), + slice::from_raw_parts(n_ptr, p_len + e_len), + ) + }; + + let mut peaks: Vec<_> = indices + .iter() + .zip(nodes.iter()) + .map( + |(index, node)| match MMREntry::from_bytes(cbranch, &node[..]) { + Ok(entry) => Ok((*index, entry)), + Err(_) => Err("Invalid encoding"), + }, + ) + .collect::>()?; + let extra = peaks.split_off(p_len); + + Ok(MMRTree::new(t_len, peaks, extra)) +} + +#[no_mangle] +pub extern "system" fn librustzcash_mmr_append( + // Consensus branch id + cbranch: u32, + // Length of tree in array representation + t_len: u32, + // Indices of provided tree nodes, length of p_len + ni_ptr: *const u32, + // Provided tree nodes data, length of p_len + n_ptr: *const [c_uchar; zcash_history::MAX_ENTRY_SIZE], + // Peaks count + p_len: size_t, + // New node pointer + nn_ptr: *const [u8; zcash_history::MAX_NODE_DATA_SIZE], + // Return of root commitment + rt_ret: *mut [u8; 32], + // Return buffer for appended leaves, should be pre-allocated of ceiling(log2(t_len)) length + buf_ret: *mut [c_uchar; zcash_history::MAX_NODE_DATA_SIZE], +) -> u32 { + let new_node_bytes: &[u8; zcash_history::MAX_NODE_DATA_SIZE] = unsafe { + match nn_ptr.as_ref() { + Some(r) => r, + None => { + return 0; + } // Null pointer passed, error + } + }; + + let mut tree = match construct_mmr_tree(cbranch, t_len, ni_ptr, n_ptr, p_len, 0) { + Ok(t) => t, + _ => { + return 0; + } // error + }; + + let node = match MMRNodeData::from_bytes(cbranch, &new_node_bytes[..]) { + Ok(node) => node, + _ => { + return 0; + } // error + }; + + let appended = match tree.append_leaf(node) { + Ok(appended) => appended, + _ => { + return 0; + } + }; + + let return_count = appended.len(); + + let root_node = tree + .root_node() + .expect("Just added, should resolve always; qed"); + unsafe { + *rt_ret = root_node.data().subtree_commitment; + + for (idx, next_buf) in slice::from_raw_parts_mut(buf_ret, return_count as usize) + .iter_mut() + .enumerate() + { + tree.resolve_link(appended[idx]) + .expect("This was generated by the tree and thus resolvable; qed") + .data() + .write(&mut &mut next_buf[..]) + .expect("Write using cursor with enough buffer size cannot fail; qed"); + } + } + + return_count as u32 +} + +#[no_mangle] +pub extern "system" fn librustzcash_mmr_delete( + // Consensus branch id + cbranch: u32, + // Length of tree in array representation + t_len: u32, + // Indices of provided tree nodes, length of p_len+e_len + ni_ptr: *const u32, + // Provided tree nodes data, length of p_len+e_len + n_ptr: *const [c_uchar; zcash_history::MAX_ENTRY_SIZE], + // Peaks count + p_len: size_t, + // Extra nodes loaded (for deletion) count + e_len: size_t, + // Return of root commitment + rt_ret: *mut [u8; 32], +) -> u32 { + let mut tree = match construct_mmr_tree(cbranch, t_len, ni_ptr, n_ptr, p_len, e_len) { + Ok(t) => t, + _ => { + return 0; + } // error + }; + + let truncate_len = match tree.truncate_leaf() { + Ok(v) => v, + _ => { + return 0; + } // Error + }; + + unsafe { + *rt_ret = tree + .root_node() + .expect("Just generated without errors, root should be resolving") + .data() + .subtree_commitment; + } + + truncate_len +} + +#[no_mangle] +pub extern "system" fn librustzcash_mmr_hash_node( + cbranch: u32, + n_ptr: *const [u8; zcash_history::MAX_NODE_DATA_SIZE], + h_ret: *mut [u8; 32], +) -> u32 { + let node_bytes: &[u8; zcash_history::MAX_NODE_DATA_SIZE] = unsafe { + match n_ptr.as_ref() { + Some(r) => r, + None => return 1, + } + }; + + let node = match MMRNodeData::from_bytes(cbranch, &node_bytes[..]) { + Ok(n) => n, + _ => return 1, // error + }; + + unsafe { + *h_ret = node.hash(); + } + + 0 +} diff --git a/src/rustzcash/src/tests/key_agreement.rs b/src/rustzcash/src/tests/key_agreement.rs new file mode 100644 index 000000000..c4f56a139 --- /dev/null +++ b/src/rustzcash/src/tests/key_agreement.rs @@ -0,0 +1,76 @@ +use ff::{PrimeField, PrimeFieldRepr}; +use pairing::bls12_381::Bls12; +use rand_core::{OsRng, RngCore}; +use zcash_primitives::jubjub::{edwards, JubjubBls12}; +use zcash_primitives::primitives::{Diversifier, ViewingKey}; + +use crate::{ + librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, + librustzcash_sapling_ka_derivepublic, +}; + +#[test] +fn test_key_agreement() { + let params = JubjubBls12::new(); + let mut rng = OsRng; + + // Create random viewing key + let vk = ViewingKey:: { + ak: edwards::Point::rand(&mut rng, ¶ms).mul_by_cofactor(¶ms), + nk: edwards::Point::rand(&mut rng, ¶ms).mul_by_cofactor(¶ms), + }; + + // Create a random address with the viewing key + let addr = loop { + let mut d = [0; 11]; + rng.fill_bytes(&mut d); + match vk.to_payment_address(Diversifier(d), ¶ms) { + Some(a) => break a, + None => {} + } + }; + + // Grab ivk from our viewing key in serialized form + let ivk = vk.ivk(); + let mut ivk_serialized = [0u8; 32]; + ivk.into_repr().write_le(&mut ivk_serialized[..]).unwrap(); + + // Create random esk + let mut esk = [0u8; 32]; + librustzcash_sapling_generate_r(&mut esk); + + // The sender will create a shared secret with the recipient + // by multiplying the pk_d from their address with the esk + // we randomly generated + let mut shared_secret_sender = [0u8; 32]; + + // Serialize pk_d for the call to librustzcash_sapling_ka_agree + let mut addr_pk_d = [0u8; 32]; + addr.pk_d().write(&mut addr_pk_d[..]).unwrap(); + + assert!(librustzcash_sapling_ka_agree( + &addr_pk_d, + &esk, + &mut shared_secret_sender + )); + + // Create epk for the recipient, placed in the transaction. Computed + // using the diversifier and esk. + let mut epk = [0u8; 32]; + assert!(librustzcash_sapling_ka_derivepublic( + &addr.diversifier().0, + &esk, + &mut epk + )); + + // Create sharedSecret with ephemeral key + let mut shared_secret_recipient = [0u8; 32]; + assert!(librustzcash_sapling_ka_agree( + &epk, + &ivk_serialized, + &mut shared_secret_recipient + )); + + assert!(!shared_secret_sender.iter().all(|&v| v == 0)); + assert_eq!(shared_secret_sender, shared_secret_recipient); +} diff --git a/src/rustzcash/src/tests/key_components.rs b/src/rustzcash/src/tests/key_components.rs new file mode 100644 index 000000000..5975e5eb1 --- /dev/null +++ b/src/rustzcash/src/tests/key_components.rs @@ -0,0 +1,731 @@ +use ff::{PrimeField, PrimeFieldRepr}; +use pairing::bls12_381::Bls12; +use zcash_primitives::{ + jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams}, + primitives::{Diversifier, ProofGenerationKey}, +}; + +use super::JUBJUB; + +use crate::{ + librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk, + librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk, +}; + +#[test] +fn key_components() { + #![allow(dead_code)] + struct TestVector { + sk: [u8; 32], + ask: [u8; 32], + nsk: [u8; 32], + ovk: [u8; 32], + ak: [u8; 32], + nk: [u8; 32], + ivk: [u8; 32], + default_d: [u8; 11], + default_pk_d: [u8; 32], + note_v: u64, + note_r: [u8; 32], + note_cm: [u8; 32], + note_pos: u64, + note_nf: [u8; 32], + }; + + // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py + let test_vectors = vec![ + TestVector { + sk: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + ask: [ + 0x85, 0x48, 0xa1, 0x4a, 0x47, 0x3e, 0xa5, 0x47, 0xaa, 0x23, 0x78, 0x40, 0x20, 0x44, + 0xf8, 0x18, 0xcf, 0x19, 0x11, 0xcf, 0x5d, 0xd2, 0x05, 0x4f, 0x67, 0x83, 0x45, 0xf0, + 0x0d, 0x0e, 0x88, 0x06, + ], + nsk: [ + 0x30, 0x11, 0x4e, 0xa0, 0xdd, 0x0b, 0xb6, 0x1c, 0xf0, 0xea, 0xea, 0xb6, 0xec, 0x33, + 0x31, 0xf5, 0x81, 0xb0, 0x42, 0x5e, 0x27, 0x33, 0x85, 0x01, 0x26, 0x2d, 0x7e, 0xac, + 0x74, 0x5e, 0x6e, 0x05, + ], + ovk: [ + 0x98, 0xd1, 0x69, 0x13, 0xd9, 0x9b, 0x04, 0x17, 0x7c, 0xab, 0xa4, 0x4f, 0x6e, 0x4d, + 0x22, 0x4e, 0x03, 0xb5, 0xac, 0x03, 0x1d, 0x7c, 0xe4, 0x5e, 0x86, 0x51, 0x38, 0xe1, + 0xb9, 0x96, 0xd6, 0x3b, + ], + ak: [ + 0xf3, 0x44, 0xec, 0x38, 0x0f, 0xe1, 0x27, 0x3e, 0x30, 0x98, 0xc2, 0x58, 0x8c, 0x5d, + 0x3a, 0x79, 0x1f, 0xd7, 0xba, 0x95, 0x80, 0x32, 0x76, 0x07, 0x77, 0xfd, 0x0e, 0xfa, + 0x8e, 0xf1, 0x16, 0x20, + ], + nk: [ + 0xf7, 0xcf, 0x9e, 0x77, 0xf2, 0xe5, 0x86, 0x83, 0x38, 0x3c, 0x15, 0x19, 0xac, 0x7b, + 0x06, 0x2d, 0x30, 0x04, 0x0e, 0x27, 0xa7, 0x25, 0xfb, 0x88, 0xfb, 0x19, 0xa9, 0x78, + 0xbd, 0x3f, 0xd6, 0xba, + ], + ivk: [ + 0xb7, 0x0b, 0x7c, 0xd0, 0xed, 0x03, 0xcb, 0xdf, 0xd7, 0xad, 0xa9, 0x50, 0x2e, 0xe2, + 0x45, 0xb1, 0x3e, 0x56, 0x9d, 0x54, 0xa5, 0x71, 0x9d, 0x2d, 0xaa, 0x0f, 0x5f, 0x14, + 0x51, 0x47, 0x92, 0x04, + ], + default_d: [ + 0xf1, 0x9d, 0x9b, 0x79, 0x7e, 0x39, 0xf3, 0x37, 0x44, 0x58, 0x39, + ], + default_pk_d: [ + 0xdb, 0x4c, 0xd2, 0xb0, 0xaa, 0xc4, 0xf7, 0xeb, 0x8c, 0xa1, 0x31, 0xf1, 0x65, 0x67, + 0xc4, 0x45, 0xa9, 0x55, 0x51, 0x26, 0xd3, 0xc2, 0x9f, 0x14, 0xe3, 0xd7, 0x76, 0xe8, + 0x41, 0xae, 0x74, 0x15, + ], + note_v: 0, + note_r: [ + 0x39, 0x17, 0x6d, 0xac, 0x39, 0xac, 0xe4, 0x98, 0x0e, 0xcc, 0x8d, 0x77, 0x8e, 0x89, + 0x86, 0x02, 0x55, 0xec, 0x36, 0x15, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + note_cm: [ + 0xcb, 0x3c, 0xf9, 0x15, 0x32, 0x70, 0xd5, 0x7e, 0xb9, 0x14, 0xc6, 0xc2, 0xbc, 0xc0, + 0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2, + 0xdd, 0x07, 0x64, 0x39, + ], + note_pos: 0, + note_nf: [ + 0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86, + 0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27, + 0x47, 0xab, 0x40, 0x63, + ], + }, + TestVector { + sk: [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + ], + ask: [ + 0xc9, 0x43, 0x56, 0x29, 0xbf, 0x8b, 0xff, 0xe5, 0x5e, 0x73, 0x35, 0xec, 0x07, 0x77, + 0x18, 0xba, 0x60, 0xba, 0x28, 0xd7, 0xac, 0x37, 0x94, 0xb7, 0x4f, 0x51, 0x2c, 0x31, + 0xaf, 0x0a, 0x53, 0x04, + ], + nsk: [ + 0x11, 0xac, 0xc2, 0xea, 0xd0, 0x7b, 0x5f, 0x00, 0x8c, 0x1f, 0x0f, 0x09, 0x0c, 0xc8, + 0xdd, 0xf3, 0x35, 0x23, 0x6f, 0xf4, 0xb2, 0x53, 0xc6, 0x49, 0x56, 0x95, 0xe9, 0xd6, + 0x39, 0xda, 0xcd, 0x08, + ], + ovk: [ + 0x3b, 0x94, 0x62, 0x10, 0xce, 0x6d, 0x1b, 0x16, 0x92, 0xd7, 0x39, 0x2a, 0xc8, 0x4a, + 0x8b, 0xc8, 0xf0, 0x3b, 0x72, 0x72, 0x3c, 0x7d, 0x36, 0x72, 0x1b, 0x80, 0x9a, 0x79, + 0xc9, 0xd6, 0xe4, 0x5b, + ], + ak: [ + 0x82, 0xff, 0x5e, 0xff, 0xc5, 0x27, 0xae, 0x84, 0x02, 0x0b, 0xf2, 0xd3, 0x52, 0x01, + 0xc1, 0x02, 0x19, 0x13, 0x19, 0x47, 0xff, 0x4b, 0x96, 0xf8, 0x81, 0xa4, 0x5f, 0x2e, + 0x8a, 0xe3, 0x05, 0x18, + ], + nk: [ + 0xc4, 0x53, 0x4d, 0x84, 0x8b, 0xb9, 0x18, 0xcf, 0x4a, 0x7f, 0x8b, 0x98, 0x74, 0x0a, + 0xb3, 0xcc, 0xee, 0x58, 0x67, 0x95, 0xff, 0x4d, 0xf6, 0x45, 0x47, 0xa8, 0x88, 0x8a, + 0x6c, 0x74, 0x15, 0xd2, + ], + ivk: [ + 0xc5, 0x18, 0x38, 0x44, 0x66, 0xb2, 0x69, 0x88, 0xb5, 0x10, 0x90, 0x67, 0x41, 0x8d, + 0x19, 0x2d, 0x9d, 0x6b, 0xd0, 0xd9, 0x23, 0x22, 0x05, 0xd7, 0x74, 0x18, 0xc2, 0x40, + 0xfc, 0x68, 0xa4, 0x06, + ], + default_d: [ + 0xae, 0xf1, 0x80, 0xf6, 0xe3, 0x4e, 0x35, 0x4b, 0x88, 0x8f, 0x81, + ], + default_pk_d: [ + 0xa6, 0xb1, 0x3e, 0xa3, 0x36, 0xdd, 0xb7, 0xa6, 0x7b, 0xb0, 0x9a, 0x0e, 0x68, 0xe9, + 0xd3, 0xcf, 0xb3, 0x92, 0x10, 0x83, 0x1e, 0xa3, 0xa2, 0x96, 0xba, 0x09, 0xa9, 0x22, + 0x06, 0x0f, 0xd3, 0x8b, + ], + note_v: 12227227834928555328, + note_r: [ + 0x47, 0x8b, 0xa0, 0xee, 0x6e, 0x1a, 0x75, 0xb6, 0x00, 0x03, 0x6f, 0x26, 0xf1, 0x8b, + 0x70, 0x15, 0xab, 0x55, 0x6b, 0xed, 0xdf, 0x8b, 0x96, 0x02, 0x38, 0x86, 0x9f, 0x89, + 0xdd, 0x80, 0x4e, 0x06, + ], + note_cm: [ + 0xb5, 0x78, 0x93, 0x50, 0x0b, 0xfb, 0x85, 0xdf, 0x2e, 0x8b, 0x01, 0xac, 0x45, 0x2f, + 0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72, + 0xca, 0xd4, 0x69, 0x50, + ], + note_pos: 763714296, + note_nf: [ + 0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2, + 0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17, + 0x4c, 0x2e, 0x9d, 0x93, + ], + }, + TestVector { + sk: [ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, + ], + ask: [ + 0xee, 0x1c, 0x3d, 0x7e, 0xfe, 0x0a, 0x78, 0x06, 0x3d, 0x6a, 0xf3, 0xd9, 0xd8, 0x12, + 0x12, 0xaf, 0x47, 0xb7, 0xc1, 0xb7, 0x61, 0xf8, 0x5c, 0xcb, 0x06, 0x6f, 0xc1, 0x1a, + 0x6a, 0x42, 0x17, 0x03, + ], + nsk: [ + 0x1d, 0x3b, 0x71, 0x37, 0x55, 0xd7, 0x48, 0x75, 0xe8, 0xea, 0x38, 0xfd, 0x16, 0x6e, + 0x76, 0xc6, 0x2a, 0x42, 0x50, 0x21, 0x6e, 0x6b, 0xbf, 0xe4, 0x8a, 0x5e, 0x2e, 0xab, + 0xad, 0x11, 0x7f, 0x0b, + ], + ovk: [ + 0x8b, 0xf4, 0x39, 0x0e, 0x28, 0xdd, 0xc9, 0x5b, 0x83, 0x02, 0xc3, 0x81, 0xd5, 0x81, + 0x0b, 0x84, 0xba, 0x8e, 0x60, 0x96, 0xe5, 0xa7, 0x68, 0x22, 0x77, 0x4f, 0xd4, 0x9f, + 0x49, 0x1e, 0x8f, 0x49, + ], + ak: [ + 0xab, 0x83, 0x57, 0x4e, 0xb5, 0xde, 0x85, 0x9a, 0x0a, 0xb8, 0x62, 0x9d, 0xec, 0x34, + 0xc7, 0xbe, 0xe8, 0xc3, 0xfc, 0x74, 0xdf, 0xa0, 0xb1, 0x9a, 0x3a, 0x74, 0x68, 0xd1, + 0x5d, 0xca, 0x64, 0xc6, + ], + nk: [ + 0x95, 0xd5, 0x80, 0x53, 0xe0, 0x59, 0x2e, 0x4a, 0x16, 0x9c, 0xc0, 0xb7, 0x92, 0x8a, + 0xaa, 0xc3, 0xde, 0x24, 0xef, 0x15, 0x31, 0xaa, 0x9e, 0xb6, 0xf4, 0xab, 0x93, 0x91, + 0x4d, 0xa8, 0xa0, 0x6e, + ], + ivk: [ + 0x47, 0x1c, 0x24, 0xa3, 0xdc, 0x87, 0x30, 0xe7, 0x50, 0x36, 0xc0, 0xa9, 0x5f, 0x3e, + 0x2f, 0x7d, 0xd1, 0xbe, 0x6f, 0xb9, 0x3a, 0xd2, 0x95, 0x92, 0x20, 0x3d, 0xef, 0x30, + 0x41, 0x95, 0x45, 0x05, + ], + default_d: [ + 0x75, 0x99, 0xf0, 0xbf, 0x9b, 0x57, 0xcd, 0x2d, 0xc2, 0x99, 0xb6, + ], + default_pk_d: [ + 0x66, 0x14, 0x17, 0x39, 0x51, 0x4b, 0x28, 0xf0, 0x5d, 0xef, 0x8a, 0x18, 0xee, 0xee, + 0x5e, 0xed, 0x4d, 0x44, 0xc6, 0x22, 0x5c, 0x3c, 0x65, 0xd8, 0x8d, 0xd9, 0x90, 0x77, + 0x08, 0x01, 0x2f, 0x5a, + ], + note_v: 6007711596147559040, + note_r: [ + 0x14, 0x7c, 0xf2, 0xb5, 0x1b, 0x4c, 0x7c, 0x63, 0xcb, 0x77, 0xb9, 0x9e, 0x8b, 0x78, + 0x3e, 0x5b, 0x51, 0x11, 0xdb, 0x0a, 0x7c, 0xa0, 0x4d, 0x6c, 0x01, 0x4a, 0x1d, 0x7d, + 0xa8, 0x3b, 0xae, 0x0a, + ], + note_cm: [ + 0xdb, 0x85, 0xa7, 0x0a, 0x98, 0x43, 0x7f, 0x73, 0x16, 0x7f, 0xc3, 0x32, 0xd5, 0xb7, + 0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f, + 0x4e, 0x55, 0xf1, 0x51, + ], + note_pos: 1527428592, + note_nf: [ + 0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f, + 0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f, + 0x59, 0xae, 0x1d, 0x14, + ], + }, + TestVector { + sk: [ + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, + ], + ask: [ + 0x00, 0xc3, 0xa1, 0xe1, 0xca, 0x8f, 0x4e, 0x04, 0x80, 0xee, 0x1e, 0xe9, 0x0c, 0xa7, + 0x51, 0x78, 0x79, 0xd3, 0xfc, 0x5c, 0x81, 0x5c, 0x09, 0x03, 0xe5, 0xee, 0xbc, 0x94, + 0xbb, 0x80, 0x95, 0x03, + ], + nsk: [ + 0xe6, 0x62, 0x85, 0xa5, 0xe9, 0xb6, 0x5e, 0x15, 0x7a, 0xd2, 0xfc, 0xd5, 0x43, 0xda, + 0xd9, 0x8c, 0x67, 0xa5, 0x8a, 0xbd, 0xf2, 0x87, 0xe0, 0x55, 0x06, 0xbd, 0x1c, 0x2e, + 0x59, 0xb0, 0x72, 0x0b, + ], + ovk: [ + 0x14, 0x76, 0x78, 0xe0, 0x55, 0x3b, 0x97, 0x82, 0x93, 0x47, 0x64, 0x7c, 0x5b, 0xc7, + 0xda, 0xb4, 0xcc, 0x22, 0x02, 0xb5, 0x4e, 0xc2, 0x9f, 0xd3, 0x1a, 0x3d, 0xe6, 0xbe, + 0x08, 0x25, 0xfc, 0x5e, + ], + ak: [ + 0x3c, 0x9c, 0xde, 0x7e, 0x5d, 0x0d, 0x38, 0xa8, 0x61, 0x0f, 0xaa, 0xdb, 0xcf, 0x4c, + 0x34, 0x3f, 0x5d, 0x3c, 0xfa, 0x31, 0x55, 0xa5, 0xb9, 0x46, 0x61, 0xa6, 0x75, 0x3e, + 0x96, 0xe8, 0x84, 0xea, + ], + nk: [ + 0xb7, 0x7d, 0x36, 0xf5, 0x08, 0x94, 0x1d, 0xbd, 0x61, 0xcf, 0xd0, 0xf1, 0x59, 0xee, + 0x05, 0xcf, 0xaa, 0x78, 0xa2, 0x6c, 0x94, 0x92, 0x90, 0x38, 0x06, 0xd8, 0x3b, 0x59, + 0x8d, 0x3c, 0x1c, 0x2a, + ], + ivk: [ + 0x63, 0x6a, 0xa9, 0x64, 0xbf, 0xc2, 0x3c, 0xe4, 0xb1, 0xfc, 0xf7, 0xdf, 0xc9, 0x91, + 0x79, 0xdd, 0xc4, 0x06, 0xff, 0x55, 0x40, 0x0c, 0x92, 0x95, 0xac, 0xfc, 0x14, 0xf0, + 0x31, 0xc7, 0x26, 0x00, + ], + default_d: [ + 0x1b, 0x81, 0x61, 0x4f, 0x1d, 0xad, 0xea, 0x0f, 0x8d, 0x0a, 0x58, + ], + default_pk_d: [ + 0x25, 0xeb, 0x55, 0xfc, 0xcf, 0x76, 0x1f, 0xc6, 0x4e, 0x85, 0xa5, 0x88, 0xef, 0xe6, + 0xea, 0xd7, 0x83, 0x2f, 0xb1, 0xf0, 0xf7, 0xa8, 0x31, 0x65, 0x89, 0x5b, 0xdf, 0xf9, + 0x42, 0x92, 0x5f, 0x5c, + ], + note_v: 18234939431076114368, + note_r: [ + 0x34, 0xa4, 0xb2, 0xa9, 0x14, 0x4f, 0xf5, 0xea, 0x54, 0xef, 0xee, 0x87, 0xcf, 0x90, + 0x1b, 0x5b, 0xed, 0x5e, 0x35, 0xd2, 0x1f, 0xbb, 0xd7, 0x88, 0xd5, 0xbd, 0x9d, 0x83, + 0x3e, 0x11, 0x28, 0x04, + ], + note_cm: [ + 0xe0, 0x8c, 0xe4, 0x82, 0xb3, 0xa8, 0xfb, 0x3b, 0x35, 0xcc, 0xdb, 0xe3, 0x43, 0x37, + 0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa, + 0x60, 0xd1, 0x9b, 0x6c, + ], + note_pos: 2291142888, + note_nf: [ + 0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56, + 0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75, + 0x8b, 0xec, 0x47, 0xa1, + ], + }, + TestVector { + sk: [ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, + ], + ask: [ + 0x82, 0x36, 0xd1, 0x9d, 0x32, 0x05, 0xd8, 0x55, 0x43, 0xa0, 0x68, 0x11, 0x34, 0x3f, + 0x82, 0x7b, 0x65, 0x63, 0x77, 0x0a, 0x49, 0xaa, 0x4d, 0x0c, 0xa0, 0x08, 0x18, 0x05, + 0xd4, 0xc8, 0xea, 0x0d, + ], + nsk: [ + 0x7e, 0xc1, 0xef, 0x0b, 0xed, 0x82, 0x71, 0x82, 0x72, 0xf0, 0xf4, 0x4f, 0x01, 0x7c, + 0x48, 0x41, 0x74, 0x51, 0x3d, 0x66, 0x1d, 0xd1, 0x68, 0xaf, 0x02, 0xd2, 0x09, 0x2a, + 0x1d, 0x8a, 0x05, 0x07, + ], + ovk: [ + 0x1b, 0x6e, 0x75, 0xec, 0xe3, 0xac, 0xe8, 0xdb, 0xa6, 0xa5, 0x41, 0x0d, 0x9a, 0xd4, + 0x75, 0x56, 0x68, 0xe4, 0xb3, 0x95, 0x85, 0xd6, 0x35, 0xec, 0x1d, 0xa7, 0xc8, 0xdc, + 0xfd, 0x5f, 0xc4, 0xed, + ], + ak: [ + 0x55, 0xe8, 0x83, 0x89, 0xbb, 0x7e, 0x41, 0xde, 0x13, 0x0c, 0xfa, 0x51, 0xa8, 0x71, + 0x5f, 0xde, 0x01, 0xff, 0x9c, 0x68, 0x76, 0x64, 0x7f, 0x01, 0x75, 0xad, 0x34, 0xf0, + 0x58, 0xdd, 0xe0, 0x1a, + ], + nk: [ + 0x72, 0x5d, 0x4a, 0xd6, 0xa1, 0x50, 0x21, 0xcd, 0x1c, 0x48, 0xc5, 0xee, 0x19, 0xde, + 0x6c, 0x1e, 0x76, 0x8a, 0x2c, 0xc0, 0xa9, 0xa7, 0x30, 0xa0, 0x1b, 0xb2, 0x1c, 0x95, + 0xe3, 0xd9, 0xe4, 0x3c, + ], + ivk: [ + 0x67, 0xfa, 0x2b, 0xf7, 0xc6, 0x7d, 0x46, 0x58, 0x24, 0x3c, 0x31, 0x7c, 0x0c, 0xb4, + 0x1f, 0xd3, 0x20, 0x64, 0xdf, 0xd3, 0x70, 0x9f, 0xe0, 0xdc, 0xb7, 0x24, 0xf1, 0x4b, + 0xb0, 0x1a, 0x1d, 0x04, + ], + default_d: [ + 0xfc, 0xfb, 0x68, 0xa4, 0x0d, 0x4b, 0xc6, 0xa0, 0x4b, 0x09, 0xc4, + ], + default_pk_d: [ + 0x8b, 0x2a, 0x33, 0x7f, 0x03, 0x62, 0x2c, 0x24, 0xff, 0x38, 0x1d, 0x4c, 0x54, 0x6f, + 0x69, 0x77, 0xf9, 0x05, 0x22, 0xe9, 0x2f, 0xde, 0x44, 0xc9, 0xd1, 0xbb, 0x09, 0x97, + 0x14, 0xb9, 0xdb, 0x2b, + ], + note_v: 12015423192295118080, + note_r: [ + 0xe5, 0x57, 0x85, 0x13, 0x55, 0x74, 0x7c, 0x09, 0xac, 0x59, 0x01, 0x3c, 0xbd, 0xe8, + 0x59, 0x80, 0x96, 0x4e, 0xc1, 0x84, 0x4d, 0x9c, 0x69, 0x67, 0xca, 0x0c, 0x02, 0x9c, + 0x84, 0x57, 0xbb, 0x04, + ], + note_cm: [ + 0xbd, 0xc8, 0x54, 0xbf, 0x3e, 0x7b, 0x00, 0x82, 0x1f, 0x3b, 0x8b, 0x85, 0x23, 0x8c, + 0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b, + 0xc7, 0x1b, 0x7f, 0x36, + ], + note_pos: 3054857184, + note_nf: [ + 0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f, + 0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03, + 0x01, 0xbf, 0x3d, 0x13, + ], + }, + TestVector { + sk: [ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, + ], + ask: [ + 0xea, 0xe6, 0x88, 0x4d, 0x76, 0x4a, 0x05, 0x40, 0x61, 0xa8, 0xf1, 0xc0, 0x07, 0x6c, + 0x62, 0x4d, 0xcb, 0x73, 0x87, 0x89, 0xf7, 0xad, 0x1e, 0x74, 0x08, 0xe3, 0x1f, 0x24, + 0xdf, 0xc8, 0x26, 0x07, + ], + nsk: [ + 0xfb, 0xe6, 0x10, 0xf4, 0x2a, 0x41, 0x74, 0x9f, 0x9b, 0x6e, 0x6e, 0x4a, 0x54, 0xb5, + 0xa3, 0x2e, 0xbf, 0xe8, 0xf4, 0x38, 0x00, 0x88, 0x1b, 0xa6, 0xcd, 0x13, 0xed, 0x0b, + 0x05, 0x29, 0x46, 0x01, + ], + ovk: [ + 0xc6, 0xbc, 0x1f, 0x39, 0xf0, 0xd7, 0x86, 0x31, 0x4c, 0xb2, 0x0b, 0xf9, 0xab, 0x22, + 0x85, 0x40, 0x91, 0x35, 0x55, 0xf9, 0x70, 0x69, 0x6b, 0x6d, 0x7c, 0x77, 0xbb, 0x33, + 0x23, 0x28, 0x37, 0x2a, + ], + ak: [ + 0xe6, 0x82, 0x76, 0x59, 0x14, 0xe3, 0x86, 0x4c, 0x33, 0x9e, 0x57, 0x82, 0xb8, 0x55, + 0xc0, 0xfd, 0xf4, 0x0e, 0x0d, 0xfc, 0xed, 0xb9, 0xe7, 0xb4, 0x7b, 0xc9, 0x4b, 0x90, + 0xb3, 0xa4, 0xc9, 0x88, + ], + nk: [ + 0x82, 0x25, 0x6b, 0x95, 0x62, 0x3c, 0x67, 0x02, 0x4b, 0x44, 0x24, 0xd9, 0x14, 0x00, + 0xa3, 0x70, 0xe7, 0xac, 0x8e, 0x4d, 0x15, 0x48, 0x2a, 0x37, 0x59, 0xe0, 0x0d, 0x21, + 0x97, 0x49, 0xda, 0xee, + ], + ivk: [ + 0xea, 0x3f, 0x1d, 0x80, 0xe4, 0x30, 0x7c, 0xa7, 0x3b, 0x9f, 0x37, 0x80, 0x1f, 0x91, + 0xfb, 0xa8, 0x10, 0xcc, 0x41, 0xd2, 0x79, 0xfc, 0x29, 0xf5, 0x64, 0x23, 0x56, 0x54, + 0xa2, 0x17, 0x8e, 0x03, + ], + default_d: [ + 0xeb, 0x51, 0x98, 0x82, 0xad, 0x1e, 0x5c, 0xc6, 0x54, 0xcd, 0x59, + ], + default_pk_d: [ + 0x6b, 0x27, 0xda, 0xcc, 0xb5, 0xa8, 0x20, 0x7f, 0x53, 0x2d, 0x10, 0xca, 0x23, 0x8f, + 0x97, 0x86, 0x64, 0x8a, 0x11, 0xb5, 0x96, 0x6e, 0x51, 0xa2, 0xf7, 0xd8, 0x9e, 0x15, + 0xd2, 0x9b, 0x8f, 0xdf, + ], + note_v: 5795906953514121792, + note_r: [ + 0x68, 0xf0, 0x61, 0x04, 0x60, 0x6b, 0x0c, 0x54, 0x49, 0x84, 0x5f, 0xf4, 0xc6, 0x5f, + 0x73, 0xe9, 0x0f, 0x45, 0xef, 0x5a, 0x43, 0xc9, 0xd7, 0x4c, 0xb2, 0xc8, 0x5c, 0xf5, + 0x6c, 0x94, 0xc0, 0x02, + ], + note_cm: [ + 0xe8, 0x26, 0x7d, 0x30, 0xac, 0x11, 0xc1, 0x00, 0xbc, 0x7a, 0x0f, 0xdf, 0x91, 0xf7, + 0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69, + 0xde, 0x1a, 0x5b, 0x4c, + ], + note_pos: 3818571480, + note_nf: [ + 0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2, + 0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20, + 0xb6, 0xc9, 0xa7, 0xe1, + ], + }, + TestVector { + sk: [ + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, + ], + ask: [ + 0xe8, 0xf8, 0x16, 0xb4, 0xbc, 0x08, 0xa7, 0xe5, 0x66, 0x75, 0x0c, 0xc2, 0x8a, 0xfe, + 0x82, 0xa4, 0xce, 0xa9, 0xc2, 0xbe, 0xf2, 0x44, 0xfa, 0x4b, 0x13, 0xc4, 0x73, 0x9b, + 0x28, 0x07, 0x4c, 0x0d, + ], + nsk: [ + 0x32, 0x61, 0x5b, 0x13, 0x7f, 0x28, 0x01, 0xed, 0x44, 0x6e, 0x48, 0x78, 0x1a, 0xb0, + 0x63, 0x45, 0x72, 0xe1, 0x8c, 0xfb, 0x06, 0x93, 0x72, 0x1b, 0x88, 0x03, 0xc0, 0x5b, + 0x82, 0x27, 0xd1, 0x07, + ], + ovk: [ + 0xf6, 0x2c, 0x05, 0xe8, 0x48, 0xa8, 0x73, 0xef, 0x88, 0x5e, 0x12, 0xb0, 0x8c, 0x5e, + 0x7c, 0xa2, 0xf3, 0x24, 0x24, 0xba, 0xcc, 0x75, 0x4c, 0xb6, 0x97, 0x50, 0x44, 0x4d, + 0x35, 0x5f, 0x51, 0x06, + ], + ak: [ + 0xff, 0x27, 0xdb, 0x07, 0x51, 0x94, 0x5d, 0x3e, 0xe4, 0xbe, 0x9c, 0xf1, 0x5c, 0x2e, + 0xa2, 0x11, 0xb2, 0x4b, 0x16, 0x4d, 0x5f, 0x2d, 0x7d, 0xdf, 0xf5, 0xe4, 0xa0, 0x70, + 0x8f, 0x10, 0xb9, 0x5e, + ], + nk: [ + 0x94, 0x38, 0x85, 0x95, 0x9d, 0x4e, 0xf8, 0xa9, 0xcf, 0xca, 0x07, 0xc4, 0x57, 0xf0, + 0x9e, 0xc7, 0x4b, 0x96, 0xf9, 0x93, 0xd8, 0xe0, 0xfa, 0x32, 0xb1, 0x9c, 0x03, 0xe3, + 0xb0, 0x7a, 0x42, 0x0f, + ], + ivk: [ + 0xb5, 0xc5, 0x89, 0x49, 0x43, 0x95, 0x69, 0x33, 0xc0, 0xe5, 0xc1, 0x2d, 0x31, 0x1f, + 0xc1, 0x2c, 0xba, 0x58, 0x35, 0x4b, 0x5c, 0x38, 0x9e, 0xdc, 0x03, 0xda, 0x55, 0x08, + 0x4f, 0x74, 0xc2, 0x05, + ], + default_d: [ + 0xbe, 0xbb, 0x0f, 0xb4, 0x6b, 0x8a, 0xaf, 0xf8, 0x90, 0x40, 0xf6, + ], + default_pk_d: [ + 0xd1, 0x1d, 0xa0, 0x1f, 0x0b, 0x43, 0xbd, 0xd5, 0x28, 0x8d, 0x32, 0x38, 0x5b, 0x87, + 0x71, 0xd2, 0x23, 0x49, 0x3c, 0x69, 0x80, 0x25, 0x44, 0x04, 0x3f, 0x77, 0xcf, 0x1d, + 0x71, 0xc1, 0xcb, 0x8c, + ], + note_v: 18023134788442677120, + note_r: [ + 0x49, 0xf9, 0x0b, 0x47, 0xfd, 0x52, 0xfe, 0xe7, 0xc1, 0xc8, 0x1f, 0x0d, 0xcb, 0x5b, + 0x74, 0xc3, 0xfb, 0x9b, 0x3e, 0x03, 0x97, 0x6f, 0x8b, 0x75, 0x24, 0xea, 0xba, 0xd0, + 0x08, 0x89, 0x21, 0x07, + ], + note_cm: [ + 0x57, 0x2b, 0xa2, 0x05, 0x25, 0xb0, 0xac, 0x4d, 0x6d, 0xc0, 0x1a, 0xc2, 0xea, 0x10, + 0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b, + 0x78, 0x3a, 0x1e, 0x55, + ], + note_pos: 287318480, + note_nf: [ + 0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae, + 0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4, + 0x07, 0xd5, 0x1e, 0x11, + ], + }, + TestVector { + sk: [ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, + ], + ask: [ + 0x74, 0xb4, 0x4a, 0x37, 0xf1, 0x50, 0x23, 0xc0, 0x60, 0x42, 0x7e, 0x1d, 0xae, 0xa3, + 0xf6, 0x43, 0x12, 0xdd, 0x8f, 0xeb, 0x7b, 0x2c, 0xed, 0xf0, 0xdd, 0x55, 0x44, 0x49, + 0x3f, 0x87, 0x2c, 0x06, + ], + nsk: [ + 0x07, 0x5c, 0x35, 0xdb, 0x8b, 0x1b, 0x25, 0x75, 0x42, 0x23, 0xec, 0xee, 0x34, 0xab, + 0x73, 0x0d, 0xdd, 0xd1, 0xf1, 0x4a, 0x6a, 0x54, 0xf4, 0xc6, 0xf4, 0x68, 0x45, 0x3c, + 0x3c, 0x6e, 0xd6, 0x0b, + ], + ovk: [ + 0xe9, 0xe0, 0xdc, 0x1e, 0xd3, 0x11, 0xda, 0xed, 0x64, 0xbd, 0x74, 0xda, 0x5d, 0x94, + 0xfe, 0x88, 0xa6, 0xea, 0x41, 0x4b, 0x73, 0x12, 0xde, 0x3d, 0x2a, 0x78, 0xf6, 0x46, + 0x32, 0xbb, 0xe3, 0x73, + ], + ak: [ + 0x28, 0x3f, 0x9a, 0xaf, 0xa9, 0xbc, 0xb3, 0xe6, 0xce, 0x17, 0xe6, 0x32, 0x12, 0x63, + 0x4c, 0xb3, 0xee, 0x55, 0x0c, 0x47, 0x6b, 0x67, 0x6b, 0xd3, 0x56, 0xa6, 0xdf, 0x8a, + 0xdf, 0x51, 0xd2, 0x5e, + ], + nk: [ + 0xdc, 0x4c, 0x67, 0xb1, 0x0d, 0x4b, 0x0a, 0x21, 0x8d, 0xc6, 0xe1, 0x48, 0x70, 0x66, + 0x74, 0x0a, 0x40, 0x93, 0x17, 0x86, 0x6c, 0x32, 0xe6, 0x64, 0xb5, 0x0e, 0x39, 0x7a, + 0xa8, 0x03, 0x89, 0xd4, + ], + ivk: [ + 0x87, 0x16, 0xc8, 0x28, 0x80, 0xe1, 0x36, 0x83, 0xe1, 0xbb, 0x05, 0x9d, 0xd0, 0x6c, + 0x80, 0xc9, 0x01, 0x34, 0xa9, 0x6d, 0x5a, 0xfc, 0xa8, 0xaa, 0xc2, 0xbb, 0xf6, 0x8b, + 0xb0, 0x5f, 0x84, 0x02, + ], + default_d: [ + 0xad, 0x6e, 0x2e, 0x18, 0x5a, 0x31, 0x00, 0xe3, 0xa6, 0xa8, 0xb3, + ], + default_pk_d: [ + 0x32, 0xcb, 0x28, 0x06, 0xb8, 0x82, 0xf1, 0x36, 0x8b, 0x0d, 0x4a, 0x89, 0x8f, 0x72, + 0xc4, 0xc8, 0xf7, 0x28, 0x13, 0x2c, 0xc1, 0x24, 0x56, 0x94, 0x6e, 0x7f, 0x4c, 0xb0, + 0xfb, 0x05, 0x8d, 0xa9, + ], + note_v: 11803618549661680832, + note_r: [ + 0x51, 0x65, 0xaf, 0xf2, 0x2d, 0xd4, 0xed, 0x56, 0xb4, 0xd8, 0x1d, 0x1f, 0x17, 0x1c, + 0xc3, 0xd6, 0x43, 0x2f, 0xed, 0x1b, 0xeb, 0xf2, 0x0a, 0x7b, 0xea, 0xb1, 0x2d, 0xb1, + 0x42, 0xf9, 0x4a, 0x0c, + ], + note_cm: [ + 0xab, 0x7f, 0xc5, 0x66, 0x87, 0x3c, 0xcd, 0xe6, 0x71, 0xf5, 0x98, 0x27, 0x67, 0x85, + 0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36, + 0xf7, 0x8c, 0x2b, 0x23, + ], + note_pos: 1051032776, + note_nf: [ + 0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f, + 0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc, + 0x8b, 0xb6, 0x98, 0x90, + ], + }, + TestVector { + sk: [ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, + ], + ask: [ + 0x03, 0x9d, 0xd9, 0x3d, 0xf3, 0x11, 0xff, 0x8f, 0xba, 0xb3, 0xfe, 0x23, 0x02, 0x19, + 0xcd, 0x42, 0xac, 0x87, 0x94, 0x84, 0xf3, 0x0b, 0x90, 0x3a, 0x3c, 0x1e, 0x67, 0xcc, + 0xca, 0x5a, 0x7b, 0x0d, + ], + nsk: [ + 0x04, 0x9f, 0xa1, 0x4f, 0x48, 0x6c, 0x75, 0xb9, 0xfa, 0xd7, 0xe3, 0xb6, 0x73, 0xa4, + 0x43, 0xdd, 0x07, 0x4e, 0xaa, 0x96, 0xed, 0xcb, 0x2a, 0x53, 0xea, 0xaa, 0xbd, 0xaf, + 0x70, 0xff, 0xbb, 0x08, + ], + ovk: [ + 0x14, 0x7d, 0xd1, 0x1d, 0x77, 0xeb, 0xa1, 0xb1, 0x63, 0x6f, 0xd6, 0x19, 0x0c, 0x62, + 0xb9, 0xa5, 0xd0, 0x48, 0x1b, 0xee, 0x7e, 0x91, 0x7f, 0xab, 0x02, 0xe2, 0x18, 0x58, + 0x06, 0x3a, 0xb5, 0x04, + ], + ak: [ + 0x36, 0x40, 0x48, 0xee, 0xdb, 0xe8, 0xca, 0x20, 0x5e, 0xb7, 0xe7, 0xba, 0x0a, 0x90, + 0x12, 0x16, 0x6c, 0x7c, 0x7b, 0xd9, 0xeb, 0x22, 0x8e, 0x08, 0x48, 0x14, 0x48, 0xc4, + 0x88, 0xaa, 0x21, 0xd2, + ], + nk: [ + 0xed, 0x60, 0xaf, 0x1c, 0xe7, 0xdf, 0x38, 0x07, 0x0d, 0x38, 0x51, 0x43, 0x2a, 0x96, + 0x48, 0x0d, 0xb0, 0xb4, 0x17, 0xc3, 0x68, 0x2a, 0x1d, 0x68, 0xe3, 0xe8, 0x93, 0x34, + 0x23, 0x5c, 0x0b, 0xdf, + ], + ivk: [ + 0x99, 0xc9, 0xb4, 0xb8, 0x4f, 0x4b, 0x4e, 0x35, 0x0f, 0x78, 0x7d, 0x1c, 0xf7, 0x05, + 0x1d, 0x50, 0xec, 0xc3, 0x4b, 0x1a, 0x5b, 0x20, 0xd2, 0xd2, 0x13, 0x9b, 0x4a, 0xf1, + 0xf1, 0x60, 0xe0, 0x01, + ], + default_d: [ + 0x21, 0xc9, 0x0e, 0x1c, 0x65, 0x8b, 0x3e, 0xfe, 0x86, 0xaf, 0x58, + ], + default_pk_d: [ + 0x9e, 0x64, 0x17, 0x4b, 0x4a, 0xb9, 0x81, 0x40, 0x5c, 0x32, 0x3b, 0x5e, 0x12, 0x47, + 0x59, 0x45, 0xa4, 0x6d, 0x4f, 0xed, 0xf8, 0x06, 0x08, 0x28, 0x04, 0x1c, 0xd2, 0x0e, + 0x62, 0xfd, 0x2c, 0xef, + ], + note_v: 5584102310880684544, + note_r: [ + 0x8c, 0x3e, 0x56, 0x44, 0x9d, 0xc8, 0x63, 0x54, 0xd3, 0x3b, 0x02, 0x5e, 0xf2, 0x79, + 0x34, 0x60, 0xbc, 0xb1, 0x69, 0xf3, 0x32, 0x4e, 0x4a, 0x6b, 0x64, 0xba, 0xa6, 0x08, + 0x32, 0x31, 0x57, 0x04, + ], + note_cm: [ + 0x7b, 0x48, 0xa8, 0x37, 0x5d, 0x3e, 0xbd, 0x56, 0xbc, 0x64, 0x9b, 0xb5, 0xb5, 0x24, + 0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07, + 0x8f, 0xea, 0x4d, 0x04, + ], + note_pos: 1814747072, + note_nf: [ + 0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20, + 0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe, + 0xb1, 0x7c, 0xd6, 0x20, + ], + }, + TestVector { + sk: [ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, + ], + ask: [ + 0xeb, 0xbb, 0x40, 0xa9, 0x80, 0xba, 0x3b, 0x88, 0x60, 0x94, 0x8d, 0x01, 0x1e, 0x1b, + 0xfb, 0x4a, 0xff, 0xe1, 0x6c, 0x65, 0x2e, 0x90, 0xe9, 0x82, 0x58, 0x30, 0x2f, 0x44, + 0x64, 0xc9, 0x1e, 0x0c, + ], + nsk: [ + 0x68, 0x43, 0x1b, 0x19, 0x91, 0x04, 0x21, 0x52, 0x00, 0xb9, 0x5e, 0xe5, 0xcb, 0x71, + 0xbf, 0x8b, 0x88, 0x3a, 0x3e, 0x95, 0xb7, 0x98, 0x9c, 0xad, 0x19, 0x70, 0x63, 0x14, + 0x1e, 0xbb, 0xfd, 0x00, + ], + ovk: [ + 0x57, 0x34, 0x67, 0xa7, 0xb3, 0x0e, 0xad, 0x6c, 0xcc, 0x50, 0x47, 0x44, 0xca, 0x9e, + 0x1a, 0x28, 0x1a, 0x0d, 0x1a, 0x08, 0x73, 0x8b, 0x06, 0xa0, 0x68, 0x4f, 0xea, 0xcd, + 0x1e, 0x9d, 0x12, 0x6d, + ], + ak: [ + 0x71, 0xc3, 0x52, 0x3e, 0xec, 0xa3, 0x53, 0x11, 0xfb, 0xd5, 0xd7, 0xe7, 0xd7, 0x0b, + 0x70, 0x9d, 0x6c, 0x35, 0xa2, 0x4f, 0x26, 0x2b, 0x34, 0xbf, 0x64, 0x05, 0x9b, 0xf2, + 0xc0, 0x2e, 0x0b, 0xa8, + ], + nk: [ + 0x62, 0x44, 0x00, 0x10, 0x3b, 0x65, 0x69, 0xb7, 0x35, 0x8f, 0xe8, 0x0f, 0x6f, 0x6c, + 0xad, 0x43, 0x25, 0xde, 0xfd, 0xa9, 0xd9, 0x49, 0x9c, 0x2b, 0x8f, 0x88, 0x6a, 0x62, + 0x69, 0xa2, 0xaa, 0x52, + ], + ivk: [ + 0xdb, 0x95, 0xea, 0x8b, 0xd9, 0xf9, 0x3d, 0x41, 0xb5, 0xab, 0x2b, 0xeb, 0xc9, 0x1a, + 0x38, 0xed, 0xd5, 0x27, 0x08, 0x3e, 0x2a, 0x6e, 0xf9, 0xf3, 0xc2, 0x97, 0x02, 0xd5, + 0xff, 0x89, 0xed, 0x00, + ], + default_d: [ + 0x23, 0x3c, 0x4a, 0xb8, 0x86, 0xa5, 0x5e, 0x3b, 0xa3, 0x74, 0xc0, + ], + default_pk_d: [ + 0xb6, 0x8e, 0x9e, 0xe0, 0xc0, 0x67, 0x8d, 0x7b, 0x30, 0x36, 0x93, 0x1c, 0x83, 0x1a, + 0x25, 0x25, 0x5f, 0x7e, 0xe4, 0x87, 0x38, 0x5a, 0x30, 0x31, 0x6e, 0x15, 0xf6, 0x48, + 0x2b, 0x87, 0x4f, 0xda, + ], + note_v: 17811330145809239872, + note_r: [ + 0x6e, 0xbb, 0xed, 0x74, 0x36, 0x19, 0xa2, 0x56, 0xf9, 0xad, 0x2e, 0x85, 0x88, 0x0c, + 0xfa, 0xa9, 0x09, 0x8a, 0x5f, 0xdb, 0x16, 0x29, 0x99, 0x0d, 0x9a, 0x7d, 0x3b, 0xb9, + 0x3f, 0xc9, 0x00, 0x03, + ], + note_cm: [ + 0xd3, 0x76, 0xa7, 0xbe, 0xe8, 0xce, 0x67, 0xf4, 0xef, 0xde, 0x56, 0xaa, 0x77, 0xcf, + 0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63, + 0xe4, 0x1d, 0xeb, 0x37, + ], + note_pos: 2578461368, + note_nf: [ + 0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe, + 0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71, + 0x0d, 0x93, 0xc9, 0xe9, + ], + }, + ]; + + for tv in test_vectors { + let mut ask_repr = FsRepr::default(); + let mut nsk_repr = FsRepr::default(); + ask_repr.read_le(&tv.ask[..]).unwrap(); + nsk_repr.read_le(&tv.nsk[..]).unwrap(); + let nsk = ::Fs::from_repr(nsk_repr).unwrap(); + + let ak = JUBJUB + .generator(FixedGenerators::SpendingKeyGenerator) + .mul(ask_repr.clone(), &JUBJUB); + { + let mut vec = Vec::new(); + ak.write(&mut vec).unwrap(); + assert_eq!(&vec, &tv.ak); + } + { + let mut ak = [0u8; 32]; + librustzcash_ask_to_ak(&tv.ask, &mut ak); + assert_eq!(&ak, &tv.ak); + } + + let pgk = ProofGenerationKey { ak, nsk }; + let fvk = pgk.to_viewing_key(&JUBJUB); + { + let mut vec = Vec::new(); + fvk.nk.write(&mut vec).unwrap(); + assert_eq!(&vec, &tv.nk); + } + { + let mut nk = [0u8; 32]; + librustzcash_nsk_to_nk(&tv.nsk, &mut nk); + assert_eq!(&nk, &tv.nk); + } + + { + let mut vec = Vec::new(); + fvk.ivk().into_repr().write_le(&mut vec).unwrap(); + assert_eq!(&vec, &tv.ivk); + } + { + let mut ivk = [0u8; 32]; + librustzcash_crh_ivk(&tv.ak, &tv.nk, &mut ivk); + assert_eq!(&ivk, &tv.ivk); + } + + let diversifier = Diversifier(tv.default_d); + assert!(librustzcash_check_diversifier(&tv.default_d)); + + let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap(); + { + let mut vec = Vec::new(); + addr.pk_d().write(&mut vec).unwrap(); + assert_eq!(&vec, &tv.default_pk_d); + } + { + let mut default_pk_d = [0u8; 32]; + librustzcash_ivk_to_pkd(&tv.ivk, &tv.default_d, &mut default_pk_d); + assert_eq!(&default_pk_d, &tv.default_pk_d); + } + + let mut note_r_repr = FsRepr::default(); + note_r_repr.read_le(&tv.note_r[..]).unwrap(); + let note_r = ::Fs::from_repr(note_r_repr).unwrap(); + let note = addr.create_note(tv.note_v, note_r, &JUBJUB).unwrap(); + { + let mut vec = Vec::new(); + note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap(); + assert_eq!(&vec, &tv.note_cm); + } + + assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf); + } +} diff --git a/src/rustzcash/src/tests/mmr.rs b/src/rustzcash/src/tests/mmr.rs new file mode 100644 index 000000000..aa83aa37d --- /dev/null +++ b/src/rustzcash/src/tests/mmr.rs @@ -0,0 +1,225 @@ +use zcash_history::{Entry, EntryLink, NodeData}; + +use crate::{librustzcash_mmr_append, librustzcash_mmr_delete}; + +const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat"); +const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat"); + +struct TreeView { + peaks: Vec<(u32, Entry)>, + extra: Vec<(u32, Entry)>, +} + +fn draft(into: &mut Vec<(u32, Entry)>, nodes: &[NodeData], peak_pos: usize, h: u32) { + let node_data = nodes[peak_pos - 1].clone(); + let peak: Entry = match h { + 0 => node_data.into(), + _ => Entry::new( + node_data, + EntryLink::Stored((peak_pos - (1 << h) - 1) as u32), + EntryLink::Stored((peak_pos - 2) as u32), + ), + }; + + into.push(((peak_pos - 1) as u32, peak)); +} + +fn prepare_tree(nodes: &[NodeData]) -> TreeView { + assert!(!nodes.is_empty()); + + // integer log2 of (nodes.len()+1), -1 + let mut h = (32 - ((nodes.len() + 1) as u32).leading_zeros() - 1) - 1; + let mut peak_pos = (1 << (h + 1)) - 1; + let mut peaks = Vec::new(); + + // used later + let mut last_peak_pos = 0; + let mut last_peak_h = 0; + + loop { + if peak_pos > nodes.len() { + // left child, -2^h + peak_pos -= 1 << h; + h -= 1; + } + + if peak_pos <= nodes.len() { + draft(&mut peaks, nodes, peak_pos, h); + + // save to be used in next loop + last_peak_pos = peak_pos; + last_peak_h = h; + + // right sibling + peak_pos += (1 << (h + 1)) - 1; + } + + if h == 0 { + break; + } + } + + // for deletion, everything on the right slope of the last peak should be pre-loaded + let mut extra = Vec::new(); + let mut h = last_peak_h; + let mut peak_pos = last_peak_pos; + + while h > 0 { + let left_pos = peak_pos - (1 << h); + let right_pos = peak_pos - 1; + h -= 1; + + // drafting left child + draft(&mut extra, nodes, left_pos, h); + + // drafting right child + draft(&mut extra, nodes, right_pos, h); + + // continuing on right slope + peak_pos = right_pos; + } + + TreeView { peaks, extra } +} + +fn preload_tree_append(nodes: &[NodeData]) -> (Vec, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>) { + assert!(!nodes.is_empty()); + + let tree_view = prepare_tree(nodes); + + let mut indices = Vec::new(); + let mut bytes = Vec::new(); + + for (idx, entry) in tree_view.peaks.into_iter() { + let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE]; + entry + .write(&mut &mut buf[..]) + .expect("Cannot fail if enough buffer length"); + indices.push(idx); + bytes.push(buf); + } + + (indices, bytes) +} + +// also returns number of peaks +fn preload_tree_delete( + nodes: &[NodeData], +) -> (Vec, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>, usize) { + assert!(!nodes.is_empty()); + + let tree_view = prepare_tree(nodes); + + let mut indices = Vec::new(); + let mut bytes = Vec::new(); + + let peak_count = tree_view.peaks.len(); + + for (idx, entry) in tree_view + .peaks + .into_iter() + .chain(tree_view.extra.into_iter()) + { + let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE]; + entry + .write(&mut &mut buf[..]) + .expect("Cannot fail if enough buffer length"); + indices.push(idx); + bytes.push(buf); + } + + (indices, bytes, peak_count) +} + +fn load_nodes(bytes: &'static [u8]) -> Vec { + let mut res = Vec::new(); + let mut cursor = std::io::Cursor::new(bytes); + while (cursor.position() as usize) < bytes.len() { + let node_data = zcash_history::NodeData::read(0, &mut cursor) + .expect("Statically checked to be correct"); + res.push(node_data); + } + + res +} + +#[test] +fn append() { + let nodes = load_nodes(NODE_DATA_16L); + let (indices, peaks) = preload_tree_append(&nodes); + + let mut rt_ret = [0u8; 32]; + + let mut buf_ret = Vec::<[u8; zcash_history::MAX_NODE_DATA_SIZE]>::with_capacity(32); + + let mut new_node_data = [0u8; zcash_history::MAX_NODE_DATA_SIZE]; + let new_node = NodeData { + consensus_branch_id: 0, + subtree_commitment: [0u8; 32], + start_time: 101, + end_time: 110, + start_target: 190, + end_target: 200, + start_sapling_root: [0u8; 32], + end_sapling_root: [0u8; 32], + subtree_total_work: Default::default(), + start_height: 10, + end_height: 10, + sapling_tx: 13, + }; + new_node + .write(&mut &mut new_node_data[..]) + .expect("Failed to write node data"); + + let result = librustzcash_mmr_append( + 0, + nodes.len() as u32, + indices.as_ptr(), + peaks.as_ptr(), + peaks.len(), + &new_node_data, + &mut rt_ret, + buf_ret.as_mut_ptr(), + ); + + unsafe { + buf_ret.set_len(result as usize); + } + + assert_eq!(result, 2); + + let new_node_1 = + NodeData::from_bytes(0, &buf_ret[0][..]).expect("Failed to reconstruct return node #1"); + + let new_node_2 = + NodeData::from_bytes(0, &buf_ret[1][..]).expect("Failed to reconstruct return node #2"); + + assert_eq!(new_node_1.start_height, 10); + assert_eq!(new_node_1.end_height, 10); + + // this is combined new node (which is `new_node_1`) + the one which was there before (for block #9) + assert_eq!(new_node_2.start_height, 9); + assert_eq!(new_node_2.end_height, 10); + assert_eq!(new_node_2.sapling_tx, 27); +} + +#[test] +fn delete() { + let nodes = load_nodes(NODE_DATA_1023L); + let (indices, nodes, peak_count) = preload_tree_delete(&nodes); + + let mut rt_ret = [0u8; 32]; + + let result = librustzcash_mmr_delete( + 0, + nodes.len() as u32, + indices.as_ptr(), + nodes.as_ptr(), + peak_count, + indices.len() - peak_count, + &mut rt_ret, + ); + + // Deleting from full tree of 9 height would result in cascade deleting of 10 nodes + assert_eq!(result, 10); +} diff --git a/src/rustzcash/src/tests/mod.rs b/src/rustzcash/src/tests/mod.rs new file mode 100644 index 000000000..bbaee0abb --- /dev/null +++ b/src/rustzcash/src/tests/mod.rs @@ -0,0 +1,97 @@ +use zcash_primitives::jubjub::{FixedGenerators, JubjubParams}; + +use super::JUBJUB; + +mod key_agreement; +mod key_components; +mod mmr; +mod notes; +mod signatures; + +#[test] +fn sapling_generators() { + struct SaplingGenerators { + skb: [u8; 32], + pkb: [u8; 32], + npb: [u8; 32], + wprb: [u8; 32], + vcvb: [u8; 32], + vcrb: [u8; 32], + }; + + // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_generators.py + let sapling_generators = SaplingGenerators { + skb: [ + 0x30, 0xb5, 0xf2, 0xaa, 0xad, 0x32, 0x56, 0x30, 0xbc, 0xdd, 0xdb, 0xce, 0x4d, 0x67, + 0x65, 0x6d, 0x05, 0xfd, 0x1c, 0xc2, 0xd0, 0x37, 0xbb, 0x53, 0x75, 0xb6, 0xe9, 0x6d, + 0x9e, 0x01, 0xa1, 0xd7, + ], + pkb: [ + 0xe7, 0xe8, 0x5d, 0xe0, 0xf7, 0xf9, 0x7a, 0x46, 0xd2, 0x49, 0xa1, 0xf5, 0xea, 0x51, + 0xdf, 0x50, 0xcc, 0x48, 0x49, 0x0f, 0x84, 0x01, 0xc9, 0xde, 0x7a, 0x2a, 0xdf, 0x18, + 0x07, 0xd1, 0xb6, 0xd4, + ], + npb: [ + 0x65, 0x00, 0x2b, 0xc7, 0x36, 0xfa, 0xf7, 0xa3, 0x42, 0x2e, 0xff, 0xff, 0xe8, 0xb8, + 0x55, 0xe1, 0x8f, 0xba, 0x96, 0xa0, 0x15, 0x8a, 0x9e, 0xfc, 0xa5, 0x84, 0xbf, 0x40, + 0x54, 0x9d, 0x36, 0xe1, + ], + wprb: [ + 0xac, 0x77, 0x6c, 0x79, 0x65, 0x63, 0xfc, 0xd4, 0x4c, 0xc4, 0x9c, 0xfa, 0xea, 0x8b, + 0xb7, 0x96, 0x95, 0x2c, 0x26, 0x6e, 0x47, 0x77, 0x9d, 0x94, 0x57, 0x4c, 0x10, 0xad, + 0x01, 0x75, 0x4b, 0x11, + ], + vcvb: [ + 0xd7, 0xc8, 0x67, 0x06, 0xf5, 0x81, 0x7a, 0xa7, 0x18, 0xcd, 0x1c, 0xfa, 0xd0, 0x32, + 0x33, 0xbc, 0xd6, 0x4a, 0x77, 0x89, 0xfd, 0x94, 0x22, 0xd3, 0xb1, 0x7a, 0xf6, 0x82, + 0x3a, 0x7e, 0x6a, 0xc6, + ], + vcrb: [ + 0x8b, 0x6a, 0x0b, 0x38, 0xb9, 0xfa, 0xae, 0x3c, 0x3b, 0x80, 0x3b, 0x47, 0xb0, 0xf1, + 0x46, 0xad, 0x50, 0xab, 0x22, 0x1e, 0x6e, 0x2a, 0xfb, 0xe6, 0xdb, 0xde, 0x45, 0xcb, + 0xa9, 0xd3, 0x81, 0xed, + ], + }; + + { + let mut vec = Vec::new(); + let p = JUBJUB.generator(FixedGenerators::SpendingKeyGenerator); + p.write(&mut vec).unwrap(); + assert_eq!(&vec, &sapling_generators.skb); + } + + { + let mut vec = Vec::new(); + let p = JUBJUB.generator(FixedGenerators::ProofGenerationKey); + p.write(&mut vec).unwrap(); + assert_eq!(&vec, &sapling_generators.pkb); + } + + { + let mut vec = Vec::new(); + let p = JUBJUB.generator(FixedGenerators::NullifierPosition); + p.write(&mut vec).unwrap(); + assert_eq!(&vec, &sapling_generators.npb); + } + + { + let mut vec = Vec::new(); + let p = JUBJUB.generator(FixedGenerators::NoteCommitmentRandomness); + p.write(&mut vec).unwrap(); + assert_eq!(&vec, &sapling_generators.wprb); + } + + { + let mut vec = Vec::new(); + let p = JUBJUB.generator(FixedGenerators::ValueCommitmentValue); + p.write(&mut vec).unwrap(); + assert_eq!(&vec, &sapling_generators.vcvb); + } + + { + let mut vec = Vec::new(); + let p = JUBJUB.generator(FixedGenerators::ValueCommitmentRandomness); + p.write(&mut vec).unwrap(); + assert_eq!(&vec, &sapling_generators.vcrb); + } +} diff --git a/src/rustzcash/src/tests/notes.rs b/src/rustzcash/src/tests/notes.rs new file mode 100644 index 000000000..6b48b5ff8 --- /dev/null +++ b/src/rustzcash/src/tests/notes.rs @@ -0,0 +1,673 @@ +use crate::librustzcash_sapling_compute_cm; +use crate::librustzcash_sapling_compute_nf; + +#[test] +fn notes() { + #![allow(dead_code)] + struct TestVector { + sk: [u8; 32], + ask: [u8; 32], + nsk: [u8; 32], + ovk: [u8; 32], + ak: [u8; 32], + nk: [u8; 32], + ivk: [u8; 32], + default_d: [u8; 11], + default_pk_d: [u8; 32], + note_v: u64, + note_r: [u8; 32], + note_cm: [u8; 32], + note_pos: u64, + note_nf: [u8; 32], + }; + + // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py + let test_vectors = vec![ + TestVector { + sk: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + ask: [ + 0x85, 0x48, 0xa1, 0x4a, 0x47, 0x3e, 0xa5, 0x47, 0xaa, 0x23, 0x78, 0x40, 0x20, 0x44, + 0xf8, 0x18, 0xcf, 0x19, 0x11, 0xcf, 0x5d, 0xd2, 0x05, 0x4f, 0x67, 0x83, 0x45, 0xf0, + 0x0d, 0x0e, 0x88, 0x06, + ], + nsk: [ + 0x30, 0x11, 0x4e, 0xa0, 0xdd, 0x0b, 0xb6, 0x1c, 0xf0, 0xea, 0xea, 0xb6, 0xec, 0x33, + 0x31, 0xf5, 0x81, 0xb0, 0x42, 0x5e, 0x27, 0x33, 0x85, 0x01, 0x26, 0x2d, 0x7e, 0xac, + 0x74, 0x5e, 0x6e, 0x05, + ], + ovk: [ + 0x98, 0xd1, 0x69, 0x13, 0xd9, 0x9b, 0x04, 0x17, 0x7c, 0xab, 0xa4, 0x4f, 0x6e, 0x4d, + 0x22, 0x4e, 0x03, 0xb5, 0xac, 0x03, 0x1d, 0x7c, 0xe4, 0x5e, 0x86, 0x51, 0x38, 0xe1, + 0xb9, 0x96, 0xd6, 0x3b, + ], + ak: [ + 0xf3, 0x44, 0xec, 0x38, 0x0f, 0xe1, 0x27, 0x3e, 0x30, 0x98, 0xc2, 0x58, 0x8c, 0x5d, + 0x3a, 0x79, 0x1f, 0xd7, 0xba, 0x95, 0x80, 0x32, 0x76, 0x07, 0x77, 0xfd, 0x0e, 0xfa, + 0x8e, 0xf1, 0x16, 0x20, + ], + nk: [ + 0xf7, 0xcf, 0x9e, 0x77, 0xf2, 0xe5, 0x86, 0x83, 0x38, 0x3c, 0x15, 0x19, 0xac, 0x7b, + 0x06, 0x2d, 0x30, 0x04, 0x0e, 0x27, 0xa7, 0x25, 0xfb, 0x88, 0xfb, 0x19, 0xa9, 0x78, + 0xbd, 0x3f, 0xd6, 0xba, + ], + ivk: [ + 0xb7, 0x0b, 0x7c, 0xd0, 0xed, 0x03, 0xcb, 0xdf, 0xd7, 0xad, 0xa9, 0x50, 0x2e, 0xe2, + 0x45, 0xb1, 0x3e, 0x56, 0x9d, 0x54, 0xa5, 0x71, 0x9d, 0x2d, 0xaa, 0x0f, 0x5f, 0x14, + 0x51, 0x47, 0x92, 0x04, + ], + default_d: [ + 0xf1, 0x9d, 0x9b, 0x79, 0x7e, 0x39, 0xf3, 0x37, 0x44, 0x58, 0x39, + ], + default_pk_d: [ + 0xdb, 0x4c, 0xd2, 0xb0, 0xaa, 0xc4, 0xf7, 0xeb, 0x8c, 0xa1, 0x31, 0xf1, 0x65, 0x67, + 0xc4, 0x45, 0xa9, 0x55, 0x51, 0x26, 0xd3, 0xc2, 0x9f, 0x14, 0xe3, 0xd7, 0x76, 0xe8, + 0x41, 0xae, 0x74, 0x15, + ], + note_v: 0, + note_r: [ + 0x39, 0x17, 0x6d, 0xac, 0x39, 0xac, 0xe4, 0x98, 0x0e, 0xcc, 0x8d, 0x77, 0x8e, 0x89, + 0x86, 0x02, 0x55, 0xec, 0x36, 0x15, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + note_cm: [ + 0xcb, 0x3c, 0xf9, 0x15, 0x32, 0x70, 0xd5, 0x7e, 0xb9, 0x14, 0xc6, 0xc2, 0xbc, 0xc0, + 0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2, + 0xdd, 0x07, 0x64, 0x39, + ], + note_pos: 0, + note_nf: [ + 0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86, + 0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27, + 0x47, 0xab, 0x40, 0x63, + ], + }, + TestVector { + sk: [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + ], + ask: [ + 0xc9, 0x43, 0x56, 0x29, 0xbf, 0x8b, 0xff, 0xe5, 0x5e, 0x73, 0x35, 0xec, 0x07, 0x77, + 0x18, 0xba, 0x60, 0xba, 0x28, 0xd7, 0xac, 0x37, 0x94, 0xb7, 0x4f, 0x51, 0x2c, 0x31, + 0xaf, 0x0a, 0x53, 0x04, + ], + nsk: [ + 0x11, 0xac, 0xc2, 0xea, 0xd0, 0x7b, 0x5f, 0x00, 0x8c, 0x1f, 0x0f, 0x09, 0x0c, 0xc8, + 0xdd, 0xf3, 0x35, 0x23, 0x6f, 0xf4, 0xb2, 0x53, 0xc6, 0x49, 0x56, 0x95, 0xe9, 0xd6, + 0x39, 0xda, 0xcd, 0x08, + ], + ovk: [ + 0x3b, 0x94, 0x62, 0x10, 0xce, 0x6d, 0x1b, 0x16, 0x92, 0xd7, 0x39, 0x2a, 0xc8, 0x4a, + 0x8b, 0xc8, 0xf0, 0x3b, 0x72, 0x72, 0x3c, 0x7d, 0x36, 0x72, 0x1b, 0x80, 0x9a, 0x79, + 0xc9, 0xd6, 0xe4, 0x5b, + ], + ak: [ + 0x82, 0xff, 0x5e, 0xff, 0xc5, 0x27, 0xae, 0x84, 0x02, 0x0b, 0xf2, 0xd3, 0x52, 0x01, + 0xc1, 0x02, 0x19, 0x13, 0x19, 0x47, 0xff, 0x4b, 0x96, 0xf8, 0x81, 0xa4, 0x5f, 0x2e, + 0x8a, 0xe3, 0x05, 0x18, + ], + nk: [ + 0xc4, 0x53, 0x4d, 0x84, 0x8b, 0xb9, 0x18, 0xcf, 0x4a, 0x7f, 0x8b, 0x98, 0x74, 0x0a, + 0xb3, 0xcc, 0xee, 0x58, 0x67, 0x95, 0xff, 0x4d, 0xf6, 0x45, 0x47, 0xa8, 0x88, 0x8a, + 0x6c, 0x74, 0x15, 0xd2, + ], + ivk: [ + 0xc5, 0x18, 0x38, 0x44, 0x66, 0xb2, 0x69, 0x88, 0xb5, 0x10, 0x90, 0x67, 0x41, 0x8d, + 0x19, 0x2d, 0x9d, 0x6b, 0xd0, 0xd9, 0x23, 0x22, 0x05, 0xd7, 0x74, 0x18, 0xc2, 0x40, + 0xfc, 0x68, 0xa4, 0x06, + ], + default_d: [ + 0xae, 0xf1, 0x80, 0xf6, 0xe3, 0x4e, 0x35, 0x4b, 0x88, 0x8f, 0x81, + ], + default_pk_d: [ + 0xa6, 0xb1, 0x3e, 0xa3, 0x36, 0xdd, 0xb7, 0xa6, 0x7b, 0xb0, 0x9a, 0x0e, 0x68, 0xe9, + 0xd3, 0xcf, 0xb3, 0x92, 0x10, 0x83, 0x1e, 0xa3, 0xa2, 0x96, 0xba, 0x09, 0xa9, 0x22, + 0x06, 0x0f, 0xd3, 0x8b, + ], + note_v: 12227227834928555328, + note_r: [ + 0x47, 0x8b, 0xa0, 0xee, 0x6e, 0x1a, 0x75, 0xb6, 0x00, 0x03, 0x6f, 0x26, 0xf1, 0x8b, + 0x70, 0x15, 0xab, 0x55, 0x6b, 0xed, 0xdf, 0x8b, 0x96, 0x02, 0x38, 0x86, 0x9f, 0x89, + 0xdd, 0x80, 0x4e, 0x06, + ], + note_cm: [ + 0xb5, 0x78, 0x93, 0x50, 0x0b, 0xfb, 0x85, 0xdf, 0x2e, 0x8b, 0x01, 0xac, 0x45, 0x2f, + 0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72, + 0xca, 0xd4, 0x69, 0x50, + ], + note_pos: 763714296, + note_nf: [ + 0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2, + 0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17, + 0x4c, 0x2e, 0x9d, 0x93, + ], + }, + TestVector { + sk: [ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, + ], + ask: [ + 0xee, 0x1c, 0x3d, 0x7e, 0xfe, 0x0a, 0x78, 0x06, 0x3d, 0x6a, 0xf3, 0xd9, 0xd8, 0x12, + 0x12, 0xaf, 0x47, 0xb7, 0xc1, 0xb7, 0x61, 0xf8, 0x5c, 0xcb, 0x06, 0x6f, 0xc1, 0x1a, + 0x6a, 0x42, 0x17, 0x03, + ], + nsk: [ + 0x1d, 0x3b, 0x71, 0x37, 0x55, 0xd7, 0x48, 0x75, 0xe8, 0xea, 0x38, 0xfd, 0x16, 0x6e, + 0x76, 0xc6, 0x2a, 0x42, 0x50, 0x21, 0x6e, 0x6b, 0xbf, 0xe4, 0x8a, 0x5e, 0x2e, 0xab, + 0xad, 0x11, 0x7f, 0x0b, + ], + ovk: [ + 0x8b, 0xf4, 0x39, 0x0e, 0x28, 0xdd, 0xc9, 0x5b, 0x83, 0x02, 0xc3, 0x81, 0xd5, 0x81, + 0x0b, 0x84, 0xba, 0x8e, 0x60, 0x96, 0xe5, 0xa7, 0x68, 0x22, 0x77, 0x4f, 0xd4, 0x9f, + 0x49, 0x1e, 0x8f, 0x49, + ], + ak: [ + 0xab, 0x83, 0x57, 0x4e, 0xb5, 0xde, 0x85, 0x9a, 0x0a, 0xb8, 0x62, 0x9d, 0xec, 0x34, + 0xc7, 0xbe, 0xe8, 0xc3, 0xfc, 0x74, 0xdf, 0xa0, 0xb1, 0x9a, 0x3a, 0x74, 0x68, 0xd1, + 0x5d, 0xca, 0x64, 0xc6, + ], + nk: [ + 0x95, 0xd5, 0x80, 0x53, 0xe0, 0x59, 0x2e, 0x4a, 0x16, 0x9c, 0xc0, 0xb7, 0x92, 0x8a, + 0xaa, 0xc3, 0xde, 0x24, 0xef, 0x15, 0x31, 0xaa, 0x9e, 0xb6, 0xf4, 0xab, 0x93, 0x91, + 0x4d, 0xa8, 0xa0, 0x6e, + ], + ivk: [ + 0x47, 0x1c, 0x24, 0xa3, 0xdc, 0x87, 0x30, 0xe7, 0x50, 0x36, 0xc0, 0xa9, 0x5f, 0x3e, + 0x2f, 0x7d, 0xd1, 0xbe, 0x6f, 0xb9, 0x3a, 0xd2, 0x95, 0x92, 0x20, 0x3d, 0xef, 0x30, + 0x41, 0x95, 0x45, 0x05, + ], + default_d: [ + 0x75, 0x99, 0xf0, 0xbf, 0x9b, 0x57, 0xcd, 0x2d, 0xc2, 0x99, 0xb6, + ], + default_pk_d: [ + 0x66, 0x14, 0x17, 0x39, 0x51, 0x4b, 0x28, 0xf0, 0x5d, 0xef, 0x8a, 0x18, 0xee, 0xee, + 0x5e, 0xed, 0x4d, 0x44, 0xc6, 0x22, 0x5c, 0x3c, 0x65, 0xd8, 0x8d, 0xd9, 0x90, 0x77, + 0x08, 0x01, 0x2f, 0x5a, + ], + note_v: 6007711596147559040, + note_r: [ + 0x14, 0x7c, 0xf2, 0xb5, 0x1b, 0x4c, 0x7c, 0x63, 0xcb, 0x77, 0xb9, 0x9e, 0x8b, 0x78, + 0x3e, 0x5b, 0x51, 0x11, 0xdb, 0x0a, 0x7c, 0xa0, 0x4d, 0x6c, 0x01, 0x4a, 0x1d, 0x7d, + 0xa8, 0x3b, 0xae, 0x0a, + ], + note_cm: [ + 0xdb, 0x85, 0xa7, 0x0a, 0x98, 0x43, 0x7f, 0x73, 0x16, 0x7f, 0xc3, 0x32, 0xd5, 0xb7, + 0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f, + 0x4e, 0x55, 0xf1, 0x51, + ], + note_pos: 1527428592, + note_nf: [ + 0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f, + 0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f, + 0x59, 0xae, 0x1d, 0x14, + ], + }, + TestVector { + sk: [ + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, + ], + ask: [ + 0x00, 0xc3, 0xa1, 0xe1, 0xca, 0x8f, 0x4e, 0x04, 0x80, 0xee, 0x1e, 0xe9, 0x0c, 0xa7, + 0x51, 0x78, 0x79, 0xd3, 0xfc, 0x5c, 0x81, 0x5c, 0x09, 0x03, 0xe5, 0xee, 0xbc, 0x94, + 0xbb, 0x80, 0x95, 0x03, + ], + nsk: [ + 0xe6, 0x62, 0x85, 0xa5, 0xe9, 0xb6, 0x5e, 0x15, 0x7a, 0xd2, 0xfc, 0xd5, 0x43, 0xda, + 0xd9, 0x8c, 0x67, 0xa5, 0x8a, 0xbd, 0xf2, 0x87, 0xe0, 0x55, 0x06, 0xbd, 0x1c, 0x2e, + 0x59, 0xb0, 0x72, 0x0b, + ], + ovk: [ + 0x14, 0x76, 0x78, 0xe0, 0x55, 0x3b, 0x97, 0x82, 0x93, 0x47, 0x64, 0x7c, 0x5b, 0xc7, + 0xda, 0xb4, 0xcc, 0x22, 0x02, 0xb5, 0x4e, 0xc2, 0x9f, 0xd3, 0x1a, 0x3d, 0xe6, 0xbe, + 0x08, 0x25, 0xfc, 0x5e, + ], + ak: [ + 0x3c, 0x9c, 0xde, 0x7e, 0x5d, 0x0d, 0x38, 0xa8, 0x61, 0x0f, 0xaa, 0xdb, 0xcf, 0x4c, + 0x34, 0x3f, 0x5d, 0x3c, 0xfa, 0x31, 0x55, 0xa5, 0xb9, 0x46, 0x61, 0xa6, 0x75, 0x3e, + 0x96, 0xe8, 0x84, 0xea, + ], + nk: [ + 0xb7, 0x7d, 0x36, 0xf5, 0x08, 0x94, 0x1d, 0xbd, 0x61, 0xcf, 0xd0, 0xf1, 0x59, 0xee, + 0x05, 0xcf, 0xaa, 0x78, 0xa2, 0x6c, 0x94, 0x92, 0x90, 0x38, 0x06, 0xd8, 0x3b, 0x59, + 0x8d, 0x3c, 0x1c, 0x2a, + ], + ivk: [ + 0x63, 0x6a, 0xa9, 0x64, 0xbf, 0xc2, 0x3c, 0xe4, 0xb1, 0xfc, 0xf7, 0xdf, 0xc9, 0x91, + 0x79, 0xdd, 0xc4, 0x06, 0xff, 0x55, 0x40, 0x0c, 0x92, 0x95, 0xac, 0xfc, 0x14, 0xf0, + 0x31, 0xc7, 0x26, 0x00, + ], + default_d: [ + 0x1b, 0x81, 0x61, 0x4f, 0x1d, 0xad, 0xea, 0x0f, 0x8d, 0x0a, 0x58, + ], + default_pk_d: [ + 0x25, 0xeb, 0x55, 0xfc, 0xcf, 0x76, 0x1f, 0xc6, 0x4e, 0x85, 0xa5, 0x88, 0xef, 0xe6, + 0xea, 0xd7, 0x83, 0x2f, 0xb1, 0xf0, 0xf7, 0xa8, 0x31, 0x65, 0x89, 0x5b, 0xdf, 0xf9, + 0x42, 0x92, 0x5f, 0x5c, + ], + note_v: 18234939431076114368, + note_r: [ + 0x34, 0xa4, 0xb2, 0xa9, 0x14, 0x4f, 0xf5, 0xea, 0x54, 0xef, 0xee, 0x87, 0xcf, 0x90, + 0x1b, 0x5b, 0xed, 0x5e, 0x35, 0xd2, 0x1f, 0xbb, 0xd7, 0x88, 0xd5, 0xbd, 0x9d, 0x83, + 0x3e, 0x11, 0x28, 0x04, + ], + note_cm: [ + 0xe0, 0x8c, 0xe4, 0x82, 0xb3, 0xa8, 0xfb, 0x3b, 0x35, 0xcc, 0xdb, 0xe3, 0x43, 0x37, + 0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa, + 0x60, 0xd1, 0x9b, 0x6c, + ], + note_pos: 2291142888, + note_nf: [ + 0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56, + 0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75, + 0x8b, 0xec, 0x47, 0xa1, + ], + }, + TestVector { + sk: [ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, + ], + ask: [ + 0x82, 0x36, 0xd1, 0x9d, 0x32, 0x05, 0xd8, 0x55, 0x43, 0xa0, 0x68, 0x11, 0x34, 0x3f, + 0x82, 0x7b, 0x65, 0x63, 0x77, 0x0a, 0x49, 0xaa, 0x4d, 0x0c, 0xa0, 0x08, 0x18, 0x05, + 0xd4, 0xc8, 0xea, 0x0d, + ], + nsk: [ + 0x7e, 0xc1, 0xef, 0x0b, 0xed, 0x82, 0x71, 0x82, 0x72, 0xf0, 0xf4, 0x4f, 0x01, 0x7c, + 0x48, 0x41, 0x74, 0x51, 0x3d, 0x66, 0x1d, 0xd1, 0x68, 0xaf, 0x02, 0xd2, 0x09, 0x2a, + 0x1d, 0x8a, 0x05, 0x07, + ], + ovk: [ + 0x1b, 0x6e, 0x75, 0xec, 0xe3, 0xac, 0xe8, 0xdb, 0xa6, 0xa5, 0x41, 0x0d, 0x9a, 0xd4, + 0x75, 0x56, 0x68, 0xe4, 0xb3, 0x95, 0x85, 0xd6, 0x35, 0xec, 0x1d, 0xa7, 0xc8, 0xdc, + 0xfd, 0x5f, 0xc4, 0xed, + ], + ak: [ + 0x55, 0xe8, 0x83, 0x89, 0xbb, 0x7e, 0x41, 0xde, 0x13, 0x0c, 0xfa, 0x51, 0xa8, 0x71, + 0x5f, 0xde, 0x01, 0xff, 0x9c, 0x68, 0x76, 0x64, 0x7f, 0x01, 0x75, 0xad, 0x34, 0xf0, + 0x58, 0xdd, 0xe0, 0x1a, + ], + nk: [ + 0x72, 0x5d, 0x4a, 0xd6, 0xa1, 0x50, 0x21, 0xcd, 0x1c, 0x48, 0xc5, 0xee, 0x19, 0xde, + 0x6c, 0x1e, 0x76, 0x8a, 0x2c, 0xc0, 0xa9, 0xa7, 0x30, 0xa0, 0x1b, 0xb2, 0x1c, 0x95, + 0xe3, 0xd9, 0xe4, 0x3c, + ], + ivk: [ + 0x67, 0xfa, 0x2b, 0xf7, 0xc6, 0x7d, 0x46, 0x58, 0x24, 0x3c, 0x31, 0x7c, 0x0c, 0xb4, + 0x1f, 0xd3, 0x20, 0x64, 0xdf, 0xd3, 0x70, 0x9f, 0xe0, 0xdc, 0xb7, 0x24, 0xf1, 0x4b, + 0xb0, 0x1a, 0x1d, 0x04, + ], + default_d: [ + 0xfc, 0xfb, 0x68, 0xa4, 0x0d, 0x4b, 0xc6, 0xa0, 0x4b, 0x09, 0xc4, + ], + default_pk_d: [ + 0x8b, 0x2a, 0x33, 0x7f, 0x03, 0x62, 0x2c, 0x24, 0xff, 0x38, 0x1d, 0x4c, 0x54, 0x6f, + 0x69, 0x77, 0xf9, 0x05, 0x22, 0xe9, 0x2f, 0xde, 0x44, 0xc9, 0xd1, 0xbb, 0x09, 0x97, + 0x14, 0xb9, 0xdb, 0x2b, + ], + note_v: 12015423192295118080, + note_r: [ + 0xe5, 0x57, 0x85, 0x13, 0x55, 0x74, 0x7c, 0x09, 0xac, 0x59, 0x01, 0x3c, 0xbd, 0xe8, + 0x59, 0x80, 0x96, 0x4e, 0xc1, 0x84, 0x4d, 0x9c, 0x69, 0x67, 0xca, 0x0c, 0x02, 0x9c, + 0x84, 0x57, 0xbb, 0x04, + ], + note_cm: [ + 0xbd, 0xc8, 0x54, 0xbf, 0x3e, 0x7b, 0x00, 0x82, 0x1f, 0x3b, 0x8b, 0x85, 0x23, 0x8c, + 0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b, + 0xc7, 0x1b, 0x7f, 0x36, + ], + note_pos: 3054857184, + note_nf: [ + 0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f, + 0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03, + 0x01, 0xbf, 0x3d, 0x13, + ], + }, + TestVector { + sk: [ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, + ], + ask: [ + 0xea, 0xe6, 0x88, 0x4d, 0x76, 0x4a, 0x05, 0x40, 0x61, 0xa8, 0xf1, 0xc0, 0x07, 0x6c, + 0x62, 0x4d, 0xcb, 0x73, 0x87, 0x89, 0xf7, 0xad, 0x1e, 0x74, 0x08, 0xe3, 0x1f, 0x24, + 0xdf, 0xc8, 0x26, 0x07, + ], + nsk: [ + 0xfb, 0xe6, 0x10, 0xf4, 0x2a, 0x41, 0x74, 0x9f, 0x9b, 0x6e, 0x6e, 0x4a, 0x54, 0xb5, + 0xa3, 0x2e, 0xbf, 0xe8, 0xf4, 0x38, 0x00, 0x88, 0x1b, 0xa6, 0xcd, 0x13, 0xed, 0x0b, + 0x05, 0x29, 0x46, 0x01, + ], + ovk: [ + 0xc6, 0xbc, 0x1f, 0x39, 0xf0, 0xd7, 0x86, 0x31, 0x4c, 0xb2, 0x0b, 0xf9, 0xab, 0x22, + 0x85, 0x40, 0x91, 0x35, 0x55, 0xf9, 0x70, 0x69, 0x6b, 0x6d, 0x7c, 0x77, 0xbb, 0x33, + 0x23, 0x28, 0x37, 0x2a, + ], + ak: [ + 0xe6, 0x82, 0x76, 0x59, 0x14, 0xe3, 0x86, 0x4c, 0x33, 0x9e, 0x57, 0x82, 0xb8, 0x55, + 0xc0, 0xfd, 0xf4, 0x0e, 0x0d, 0xfc, 0xed, 0xb9, 0xe7, 0xb4, 0x7b, 0xc9, 0x4b, 0x90, + 0xb3, 0xa4, 0xc9, 0x88, + ], + nk: [ + 0x82, 0x25, 0x6b, 0x95, 0x62, 0x3c, 0x67, 0x02, 0x4b, 0x44, 0x24, 0xd9, 0x14, 0x00, + 0xa3, 0x70, 0xe7, 0xac, 0x8e, 0x4d, 0x15, 0x48, 0x2a, 0x37, 0x59, 0xe0, 0x0d, 0x21, + 0x97, 0x49, 0xda, 0xee, + ], + ivk: [ + 0xea, 0x3f, 0x1d, 0x80, 0xe4, 0x30, 0x7c, 0xa7, 0x3b, 0x9f, 0x37, 0x80, 0x1f, 0x91, + 0xfb, 0xa8, 0x10, 0xcc, 0x41, 0xd2, 0x79, 0xfc, 0x29, 0xf5, 0x64, 0x23, 0x56, 0x54, + 0xa2, 0x17, 0x8e, 0x03, + ], + default_d: [ + 0xeb, 0x51, 0x98, 0x82, 0xad, 0x1e, 0x5c, 0xc6, 0x54, 0xcd, 0x59, + ], + default_pk_d: [ + 0x6b, 0x27, 0xda, 0xcc, 0xb5, 0xa8, 0x20, 0x7f, 0x53, 0x2d, 0x10, 0xca, 0x23, 0x8f, + 0x97, 0x86, 0x64, 0x8a, 0x11, 0xb5, 0x96, 0x6e, 0x51, 0xa2, 0xf7, 0xd8, 0x9e, 0x15, + 0xd2, 0x9b, 0x8f, 0xdf, + ], + note_v: 5795906953514121792, + note_r: [ + 0x68, 0xf0, 0x61, 0x04, 0x60, 0x6b, 0x0c, 0x54, 0x49, 0x84, 0x5f, 0xf4, 0xc6, 0x5f, + 0x73, 0xe9, 0x0f, 0x45, 0xef, 0x5a, 0x43, 0xc9, 0xd7, 0x4c, 0xb2, 0xc8, 0x5c, 0xf5, + 0x6c, 0x94, 0xc0, 0x02, + ], + note_cm: [ + 0xe8, 0x26, 0x7d, 0x30, 0xac, 0x11, 0xc1, 0x00, 0xbc, 0x7a, 0x0f, 0xdf, 0x91, 0xf7, + 0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69, + 0xde, 0x1a, 0x5b, 0x4c, + ], + note_pos: 3818571480, + note_nf: [ + 0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2, + 0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20, + 0xb6, 0xc9, 0xa7, 0xe1, + ], + }, + TestVector { + sk: [ + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, + ], + ask: [ + 0xe8, 0xf8, 0x16, 0xb4, 0xbc, 0x08, 0xa7, 0xe5, 0x66, 0x75, 0x0c, 0xc2, 0x8a, 0xfe, + 0x82, 0xa4, 0xce, 0xa9, 0xc2, 0xbe, 0xf2, 0x44, 0xfa, 0x4b, 0x13, 0xc4, 0x73, 0x9b, + 0x28, 0x07, 0x4c, 0x0d, + ], + nsk: [ + 0x32, 0x61, 0x5b, 0x13, 0x7f, 0x28, 0x01, 0xed, 0x44, 0x6e, 0x48, 0x78, 0x1a, 0xb0, + 0x63, 0x45, 0x72, 0xe1, 0x8c, 0xfb, 0x06, 0x93, 0x72, 0x1b, 0x88, 0x03, 0xc0, 0x5b, + 0x82, 0x27, 0xd1, 0x07, + ], + ovk: [ + 0xf6, 0x2c, 0x05, 0xe8, 0x48, 0xa8, 0x73, 0xef, 0x88, 0x5e, 0x12, 0xb0, 0x8c, 0x5e, + 0x7c, 0xa2, 0xf3, 0x24, 0x24, 0xba, 0xcc, 0x75, 0x4c, 0xb6, 0x97, 0x50, 0x44, 0x4d, + 0x35, 0x5f, 0x51, 0x06, + ], + ak: [ + 0xff, 0x27, 0xdb, 0x07, 0x51, 0x94, 0x5d, 0x3e, 0xe4, 0xbe, 0x9c, 0xf1, 0x5c, 0x2e, + 0xa2, 0x11, 0xb2, 0x4b, 0x16, 0x4d, 0x5f, 0x2d, 0x7d, 0xdf, 0xf5, 0xe4, 0xa0, 0x70, + 0x8f, 0x10, 0xb9, 0x5e, + ], + nk: [ + 0x94, 0x38, 0x85, 0x95, 0x9d, 0x4e, 0xf8, 0xa9, 0xcf, 0xca, 0x07, 0xc4, 0x57, 0xf0, + 0x9e, 0xc7, 0x4b, 0x96, 0xf9, 0x93, 0xd8, 0xe0, 0xfa, 0x32, 0xb1, 0x9c, 0x03, 0xe3, + 0xb0, 0x7a, 0x42, 0x0f, + ], + ivk: [ + 0xb5, 0xc5, 0x89, 0x49, 0x43, 0x95, 0x69, 0x33, 0xc0, 0xe5, 0xc1, 0x2d, 0x31, 0x1f, + 0xc1, 0x2c, 0xba, 0x58, 0x35, 0x4b, 0x5c, 0x38, 0x9e, 0xdc, 0x03, 0xda, 0x55, 0x08, + 0x4f, 0x74, 0xc2, 0x05, + ], + default_d: [ + 0xbe, 0xbb, 0x0f, 0xb4, 0x6b, 0x8a, 0xaf, 0xf8, 0x90, 0x40, 0xf6, + ], + default_pk_d: [ + 0xd1, 0x1d, 0xa0, 0x1f, 0x0b, 0x43, 0xbd, 0xd5, 0x28, 0x8d, 0x32, 0x38, 0x5b, 0x87, + 0x71, 0xd2, 0x23, 0x49, 0x3c, 0x69, 0x80, 0x25, 0x44, 0x04, 0x3f, 0x77, 0xcf, 0x1d, + 0x71, 0xc1, 0xcb, 0x8c, + ], + note_v: 18023134788442677120, + note_r: [ + 0x49, 0xf9, 0x0b, 0x47, 0xfd, 0x52, 0xfe, 0xe7, 0xc1, 0xc8, 0x1f, 0x0d, 0xcb, 0x5b, + 0x74, 0xc3, 0xfb, 0x9b, 0x3e, 0x03, 0x97, 0x6f, 0x8b, 0x75, 0x24, 0xea, 0xba, 0xd0, + 0x08, 0x89, 0x21, 0x07, + ], + note_cm: [ + 0x57, 0x2b, 0xa2, 0x05, 0x25, 0xb0, 0xac, 0x4d, 0x6d, 0xc0, 0x1a, 0xc2, 0xea, 0x10, + 0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b, + 0x78, 0x3a, 0x1e, 0x55, + ], + note_pos: 287318480, + note_nf: [ + 0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae, + 0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4, + 0x07, 0xd5, 0x1e, 0x11, + ], + }, + TestVector { + sk: [ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, + ], + ask: [ + 0x74, 0xb4, 0x4a, 0x37, 0xf1, 0x50, 0x23, 0xc0, 0x60, 0x42, 0x7e, 0x1d, 0xae, 0xa3, + 0xf6, 0x43, 0x12, 0xdd, 0x8f, 0xeb, 0x7b, 0x2c, 0xed, 0xf0, 0xdd, 0x55, 0x44, 0x49, + 0x3f, 0x87, 0x2c, 0x06, + ], + nsk: [ + 0x07, 0x5c, 0x35, 0xdb, 0x8b, 0x1b, 0x25, 0x75, 0x42, 0x23, 0xec, 0xee, 0x34, 0xab, + 0x73, 0x0d, 0xdd, 0xd1, 0xf1, 0x4a, 0x6a, 0x54, 0xf4, 0xc6, 0xf4, 0x68, 0x45, 0x3c, + 0x3c, 0x6e, 0xd6, 0x0b, + ], + ovk: [ + 0xe9, 0xe0, 0xdc, 0x1e, 0xd3, 0x11, 0xda, 0xed, 0x64, 0xbd, 0x74, 0xda, 0x5d, 0x94, + 0xfe, 0x88, 0xa6, 0xea, 0x41, 0x4b, 0x73, 0x12, 0xde, 0x3d, 0x2a, 0x78, 0xf6, 0x46, + 0x32, 0xbb, 0xe3, 0x73, + ], + ak: [ + 0x28, 0x3f, 0x9a, 0xaf, 0xa9, 0xbc, 0xb3, 0xe6, 0xce, 0x17, 0xe6, 0x32, 0x12, 0x63, + 0x4c, 0xb3, 0xee, 0x55, 0x0c, 0x47, 0x6b, 0x67, 0x6b, 0xd3, 0x56, 0xa6, 0xdf, 0x8a, + 0xdf, 0x51, 0xd2, 0x5e, + ], + nk: [ + 0xdc, 0x4c, 0x67, 0xb1, 0x0d, 0x4b, 0x0a, 0x21, 0x8d, 0xc6, 0xe1, 0x48, 0x70, 0x66, + 0x74, 0x0a, 0x40, 0x93, 0x17, 0x86, 0x6c, 0x32, 0xe6, 0x64, 0xb5, 0x0e, 0x39, 0x7a, + 0xa8, 0x03, 0x89, 0xd4, + ], + ivk: [ + 0x87, 0x16, 0xc8, 0x28, 0x80, 0xe1, 0x36, 0x83, 0xe1, 0xbb, 0x05, 0x9d, 0xd0, 0x6c, + 0x80, 0xc9, 0x01, 0x34, 0xa9, 0x6d, 0x5a, 0xfc, 0xa8, 0xaa, 0xc2, 0xbb, 0xf6, 0x8b, + 0xb0, 0x5f, 0x84, 0x02, + ], + default_d: [ + 0xad, 0x6e, 0x2e, 0x18, 0x5a, 0x31, 0x00, 0xe3, 0xa6, 0xa8, 0xb3, + ], + default_pk_d: [ + 0x32, 0xcb, 0x28, 0x06, 0xb8, 0x82, 0xf1, 0x36, 0x8b, 0x0d, 0x4a, 0x89, 0x8f, 0x72, + 0xc4, 0xc8, 0xf7, 0x28, 0x13, 0x2c, 0xc1, 0x24, 0x56, 0x94, 0x6e, 0x7f, 0x4c, 0xb0, + 0xfb, 0x05, 0x8d, 0xa9, + ], + note_v: 11803618549661680832, + note_r: [ + 0x51, 0x65, 0xaf, 0xf2, 0x2d, 0xd4, 0xed, 0x56, 0xb4, 0xd8, 0x1d, 0x1f, 0x17, 0x1c, + 0xc3, 0xd6, 0x43, 0x2f, 0xed, 0x1b, 0xeb, 0xf2, 0x0a, 0x7b, 0xea, 0xb1, 0x2d, 0xb1, + 0x42, 0xf9, 0x4a, 0x0c, + ], + note_cm: [ + 0xab, 0x7f, 0xc5, 0x66, 0x87, 0x3c, 0xcd, 0xe6, 0x71, 0xf5, 0x98, 0x27, 0x67, 0x85, + 0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36, + 0xf7, 0x8c, 0x2b, 0x23, + ], + note_pos: 1051032776, + note_nf: [ + 0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f, + 0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc, + 0x8b, 0xb6, 0x98, 0x90, + ], + }, + TestVector { + sk: [ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, + ], + ask: [ + 0x03, 0x9d, 0xd9, 0x3d, 0xf3, 0x11, 0xff, 0x8f, 0xba, 0xb3, 0xfe, 0x23, 0x02, 0x19, + 0xcd, 0x42, 0xac, 0x87, 0x94, 0x84, 0xf3, 0x0b, 0x90, 0x3a, 0x3c, 0x1e, 0x67, 0xcc, + 0xca, 0x5a, 0x7b, 0x0d, + ], + nsk: [ + 0x04, 0x9f, 0xa1, 0x4f, 0x48, 0x6c, 0x75, 0xb9, 0xfa, 0xd7, 0xe3, 0xb6, 0x73, 0xa4, + 0x43, 0xdd, 0x07, 0x4e, 0xaa, 0x96, 0xed, 0xcb, 0x2a, 0x53, 0xea, 0xaa, 0xbd, 0xaf, + 0x70, 0xff, 0xbb, 0x08, + ], + ovk: [ + 0x14, 0x7d, 0xd1, 0x1d, 0x77, 0xeb, 0xa1, 0xb1, 0x63, 0x6f, 0xd6, 0x19, 0x0c, 0x62, + 0xb9, 0xa5, 0xd0, 0x48, 0x1b, 0xee, 0x7e, 0x91, 0x7f, 0xab, 0x02, 0xe2, 0x18, 0x58, + 0x06, 0x3a, 0xb5, 0x04, + ], + ak: [ + 0x36, 0x40, 0x48, 0xee, 0xdb, 0xe8, 0xca, 0x20, 0x5e, 0xb7, 0xe7, 0xba, 0x0a, 0x90, + 0x12, 0x16, 0x6c, 0x7c, 0x7b, 0xd9, 0xeb, 0x22, 0x8e, 0x08, 0x48, 0x14, 0x48, 0xc4, + 0x88, 0xaa, 0x21, 0xd2, + ], + nk: [ + 0xed, 0x60, 0xaf, 0x1c, 0xe7, 0xdf, 0x38, 0x07, 0x0d, 0x38, 0x51, 0x43, 0x2a, 0x96, + 0x48, 0x0d, 0xb0, 0xb4, 0x17, 0xc3, 0x68, 0x2a, 0x1d, 0x68, 0xe3, 0xe8, 0x93, 0x34, + 0x23, 0x5c, 0x0b, 0xdf, + ], + ivk: [ + 0x99, 0xc9, 0xb4, 0xb8, 0x4f, 0x4b, 0x4e, 0x35, 0x0f, 0x78, 0x7d, 0x1c, 0xf7, 0x05, + 0x1d, 0x50, 0xec, 0xc3, 0x4b, 0x1a, 0x5b, 0x20, 0xd2, 0xd2, 0x13, 0x9b, 0x4a, 0xf1, + 0xf1, 0x60, 0xe0, 0x01, + ], + default_d: [ + 0x21, 0xc9, 0x0e, 0x1c, 0x65, 0x8b, 0x3e, 0xfe, 0x86, 0xaf, 0x58, + ], + default_pk_d: [ + 0x9e, 0x64, 0x17, 0x4b, 0x4a, 0xb9, 0x81, 0x40, 0x5c, 0x32, 0x3b, 0x5e, 0x12, 0x47, + 0x59, 0x45, 0xa4, 0x6d, 0x4f, 0xed, 0xf8, 0x06, 0x08, 0x28, 0x04, 0x1c, 0xd2, 0x0e, + 0x62, 0xfd, 0x2c, 0xef, + ], + note_v: 5584102310880684544, + note_r: [ + 0x8c, 0x3e, 0x56, 0x44, 0x9d, 0xc8, 0x63, 0x54, 0xd3, 0x3b, 0x02, 0x5e, 0xf2, 0x79, + 0x34, 0x60, 0xbc, 0xb1, 0x69, 0xf3, 0x32, 0x4e, 0x4a, 0x6b, 0x64, 0xba, 0xa6, 0x08, + 0x32, 0x31, 0x57, 0x04, + ], + note_cm: [ + 0x7b, 0x48, 0xa8, 0x37, 0x5d, 0x3e, 0xbd, 0x56, 0xbc, 0x64, 0x9b, 0xb5, 0xb5, 0x24, + 0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07, + 0x8f, 0xea, 0x4d, 0x04, + ], + note_pos: 1814747072, + note_nf: [ + 0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20, + 0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe, + 0xb1, 0x7c, 0xd6, 0x20, + ], + }, + TestVector { + sk: [ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, + ], + ask: [ + 0xeb, 0xbb, 0x40, 0xa9, 0x80, 0xba, 0x3b, 0x88, 0x60, 0x94, 0x8d, 0x01, 0x1e, 0x1b, + 0xfb, 0x4a, 0xff, 0xe1, 0x6c, 0x65, 0x2e, 0x90, 0xe9, 0x82, 0x58, 0x30, 0x2f, 0x44, + 0x64, 0xc9, 0x1e, 0x0c, + ], + nsk: [ + 0x68, 0x43, 0x1b, 0x19, 0x91, 0x04, 0x21, 0x52, 0x00, 0xb9, 0x5e, 0xe5, 0xcb, 0x71, + 0xbf, 0x8b, 0x88, 0x3a, 0x3e, 0x95, 0xb7, 0x98, 0x9c, 0xad, 0x19, 0x70, 0x63, 0x14, + 0x1e, 0xbb, 0xfd, 0x00, + ], + ovk: [ + 0x57, 0x34, 0x67, 0xa7, 0xb3, 0x0e, 0xad, 0x6c, 0xcc, 0x50, 0x47, 0x44, 0xca, 0x9e, + 0x1a, 0x28, 0x1a, 0x0d, 0x1a, 0x08, 0x73, 0x8b, 0x06, 0xa0, 0x68, 0x4f, 0xea, 0xcd, + 0x1e, 0x9d, 0x12, 0x6d, + ], + ak: [ + 0x71, 0xc3, 0x52, 0x3e, 0xec, 0xa3, 0x53, 0x11, 0xfb, 0xd5, 0xd7, 0xe7, 0xd7, 0x0b, + 0x70, 0x9d, 0x6c, 0x35, 0xa2, 0x4f, 0x26, 0x2b, 0x34, 0xbf, 0x64, 0x05, 0x9b, 0xf2, + 0xc0, 0x2e, 0x0b, 0xa8, + ], + nk: [ + 0x62, 0x44, 0x00, 0x10, 0x3b, 0x65, 0x69, 0xb7, 0x35, 0x8f, 0xe8, 0x0f, 0x6f, 0x6c, + 0xad, 0x43, 0x25, 0xde, 0xfd, 0xa9, 0xd9, 0x49, 0x9c, 0x2b, 0x8f, 0x88, 0x6a, 0x62, + 0x69, 0xa2, 0xaa, 0x52, + ], + ivk: [ + 0xdb, 0x95, 0xea, 0x8b, 0xd9, 0xf9, 0x3d, 0x41, 0xb5, 0xab, 0x2b, 0xeb, 0xc9, 0x1a, + 0x38, 0xed, 0xd5, 0x27, 0x08, 0x3e, 0x2a, 0x6e, 0xf9, 0xf3, 0xc2, 0x97, 0x02, 0xd5, + 0xff, 0x89, 0xed, 0x00, + ], + default_d: [ + 0x23, 0x3c, 0x4a, 0xb8, 0x86, 0xa5, 0x5e, 0x3b, 0xa3, 0x74, 0xc0, + ], + default_pk_d: [ + 0xb6, 0x8e, 0x9e, 0xe0, 0xc0, 0x67, 0x8d, 0x7b, 0x30, 0x36, 0x93, 0x1c, 0x83, 0x1a, + 0x25, 0x25, 0x5f, 0x7e, 0xe4, 0x87, 0x38, 0x5a, 0x30, 0x31, 0x6e, 0x15, 0xf6, 0x48, + 0x2b, 0x87, 0x4f, 0xda, + ], + note_v: 17811330145809239872, + note_r: [ + 0x6e, 0xbb, 0xed, 0x74, 0x36, 0x19, 0xa2, 0x56, 0xf9, 0xad, 0x2e, 0x85, 0x88, 0x0c, + 0xfa, 0xa9, 0x09, 0x8a, 0x5f, 0xdb, 0x16, 0x29, 0x99, 0x0d, 0x9a, 0x7d, 0x3b, 0xb9, + 0x3f, 0xc9, 0x00, 0x03, + ], + note_cm: [ + 0xd3, 0x76, 0xa7, 0xbe, 0xe8, 0xce, 0x67, 0xf4, 0xef, 0xde, 0x56, 0xaa, 0x77, 0xcf, + 0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63, + 0xe4, 0x1d, 0xeb, 0x37, + ], + note_pos: 2578461368, + note_nf: [ + 0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe, + 0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71, + 0x0d, 0x93, 0xc9, 0xe9, + ], + }, + ]; + + for tv in test_vectors { + // Compute commitment and compare with test vector + let mut result = [0u8; 32]; + assert!(librustzcash_sapling_compute_cm( + &tv.default_d, + &tv.default_pk_d, + tv.note_v, + &tv.note_r, + &mut result + )); + assert_eq!(&result, &tv.note_cm); + + // Compute nullifier and compare with test vector + assert!(librustzcash_sapling_compute_nf( + &tv.default_d, + &tv.default_pk_d, + tv.note_v, + &tv.note_r, + &tv.ak, + &tv.nk, + tv.note_pos, + &mut result + )); + assert_eq!(&result, &tv.note_nf); + } +} diff --git a/src/rustzcash/src/tests/signatures.rs b/src/rustzcash/src/tests/signatures.rs new file mode 100644 index 000000000..68ac7997c --- /dev/null +++ b/src/rustzcash/src/tests/signatures.rs @@ -0,0 +1,514 @@ +use ff::{PrimeField, PrimeFieldRepr}; +use pairing::bls12_381::Bls12; +use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine}; +use zcash_primitives::redjubjub::{PrivateKey, PublicKey, Signature}; + +use super::JUBJUB; + +#[test] +fn redjubjub_signatures() { + struct TestVector { + sk: [u8; 32], + vk: [u8; 32], + alpha: [u8; 32], + rsk: [u8; 32], + rvk: [u8; 32], + m: [u8; 32], + sig: [u8; 64], + rsig: [u8; 64], + }; + + // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_signatures.py + let test_vectors = vec![ + TestVector { + sk: [ + 0x18, 0xe2, 0x8d, 0xea, 0x5c, 0x11, 0x81, 0x7a, 0xee, 0xb2, 0x1a, 0x19, 0x98, 0x1d, + 0x28, 0x36, 0x8e, 0xc4, 0x38, 0xaf, 0xc2, 0x5a, 0x8d, 0xb9, 0x4e, 0xbe, 0x08, 0xd7, + 0xa0, 0x28, 0x8e, 0x09, + ], + vk: [ + 0x9b, 0x01, 0x53, 0xb0, 0x3d, 0x32, 0x0f, 0xe2, 0x3e, 0x28, 0x34, 0xd5, 0xd6, 0x1d, + 0xbb, 0x1f, 0x51, 0x9b, 0x3f, 0x41, 0xf8, 0xf9, 0x46, 0x15, 0x2b, 0xf0, 0xc3, 0xf2, + 0x47, 0xd1, 0x18, 0x07, + ], + alpha: [ + 0xff, 0xd1, 0xa1, 0x27, 0x32, 0x52, 0xb1, 0x87, 0xf4, 0xed, 0x32, 0x6d, 0xfc, 0x98, + 0x85, 0x3e, 0x29, 0x17, 0xc2, 0xb3, 0x63, 0x79, 0xb1, 0x75, 0xda, 0x63, 0xb9, 0xef, + 0x6d, 0xda, 0x6c, 0x08, + ], + rsk: [ + 0x60, 0x87, 0x38, 0x3b, 0x30, 0x55, 0x9b, 0x31, 0x60, 0x90, 0x85, 0xb9, 0x00, 0x96, + 0x45, 0xce, 0xb6, 0xa0, 0xc6, 0x61, 0x25, 0x99, 0xd7, 0x28, 0x80, 0x72, 0x8e, 0x61, + 0x24, 0x4e, 0x7d, 0x03, + ], + rvk: [ + 0xc1, 0xba, 0xbc, 0xb6, 0xea, 0xe2, 0xb9, 0x94, 0xee, 0x6d, 0x65, 0xc1, 0x0b, 0x9d, + 0xad, 0x59, 0x40, 0xdc, 0x73, 0x5b, 0x07, 0x50, 0x4d, 0xae, 0xd1, 0xe4, 0x6b, 0x07, + 0x09, 0xb4, 0x51, 0x36, + ], + m: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ], + sig: [ + 0xea, 0xa0, 0x57, 0x47, 0x6b, 0x4a, 0xb4, 0x82, 0x28, 0x8b, 0x93, 0xdf, 0x8f, 0xe0, + 0xc5, 0xce, 0x9d, 0x78, 0x83, 0x67, 0xf2, 0xbe, 0x55, 0x1b, 0x7f, 0x7a, 0x82, 0xa6, + 0xdb, 0x36, 0x04, 0x68, 0xde, 0xb9, 0xa7, 0xb7, 0xaf, 0xaa, 0xdf, 0xec, 0xa6, 0xf4, + 0x81, 0x19, 0x3d, 0xc6, 0x57, 0x57, 0x47, 0xf6, 0x0a, 0x1a, 0x8a, 0x48, 0xff, 0x0a, + 0xd7, 0x0c, 0xf8, 0xcb, 0x8d, 0x52, 0x8e, 0x08, + ], + rsig: [ + 0xd5, 0x6f, 0x0d, 0x91, 0xaf, 0x42, 0x4e, 0x1f, 0x1c, 0x7f, 0xb8, 0x6b, 0xa4, 0xee, + 0xd1, 0x43, 0xcc, 0x16, 0x66, 0x0c, 0x5f, 0xe8, 0xd7, 0xdc, 0x0d, 0x28, 0x4b, 0xcf, + 0x65, 0xa0, 0x89, 0xe9, 0x8b, 0x56, 0x1f, 0x9f, 0x20, 0x1a, 0x63, 0x3d, 0x70, 0x0c, + 0xd3, 0x98, 0x1e, 0x8c, 0xac, 0x07, 0xb5, 0xa8, 0x7e, 0xfa, 0x61, 0x86, 0x06, 0x2d, + 0xd8, 0xe5, 0xd6, 0x32, 0x5e, 0x7b, 0x82, 0x02, + ], + }, + TestVector { + sk: [ + 0x05, 0x96, 0x54, 0xf9, 0x61, 0x27, 0x3d, 0xaf, 0xda, 0x3b, 0x26, 0x77, 0xb3, 0x5c, + 0x18, 0xaf, 0x6b, 0x11, 0xad, 0xfb, 0x9e, 0xe9, 0x0b, 0x48, 0x93, 0x5e, 0x55, 0x7c, + 0x8d, 0x5d, 0x9c, 0x04, + ], + vk: [ + 0xfa, 0xf6, 0xc3, 0xb7, 0x37, 0xe8, 0xe6, 0x11, 0xaa, 0xfe, 0xa5, 0x2f, 0x03, 0xbb, + 0x27, 0x86, 0xe1, 0x83, 0x53, 0xeb, 0xe0, 0xd3, 0x13, 0x9e, 0x3c, 0x54, 0x49, 0x87, + 0x80, 0xc8, 0xc1, 0x99, + ], + alpha: [ + 0xc3, 0x0b, 0x96, 0x20, 0x8d, 0xa8, 0x00, 0xe1, 0x0a, 0xf0, 0x25, 0x42, 0xce, 0x69, + 0x4b, 0x7e, 0xd7, 0x6a, 0x28, 0x29, 0x9f, 0x85, 0x99, 0x8e, 0x5d, 0x61, 0x08, 0x12, + 0x68, 0x1b, 0xf0, 0x03, + ], + rsk: [ + 0xc8, 0xa1, 0xea, 0x19, 0xef, 0xcf, 0x3d, 0x90, 0xe5, 0x2b, 0x4c, 0xb9, 0x81, 0xc6, + 0x63, 0x2d, 0x43, 0x7c, 0xd5, 0x24, 0x3e, 0x6f, 0xa5, 0xd6, 0xf0, 0xbf, 0x5d, 0x8e, + 0xf5, 0x78, 0x8c, 0x08, + ], + rvk: [ + 0xd5, 0x24, 0xdc, 0xe7, 0x73, 0x40, 0x69, 0x75, 0x8a, 0x91, 0xf0, 0x07, 0xa8, 0x69, + 0x50, 0x5d, 0xfc, 0x4a, 0xba, 0x17, 0x20, 0x59, 0x4d, 0x4d, 0x74, 0xf0, 0x07, 0x70, + 0x0e, 0x62, 0xee, 0x00, + ], + m: [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + ], + sig: [ + 0x22, 0x35, 0x54, 0x94, 0xa8, 0x31, 0x6a, 0xb1, 0x34, 0x73, 0xf5, 0x5e, 0x62, 0x66, + 0xb2, 0xfb, 0x41, 0x97, 0x31, 0x5e, 0xac, 0x62, 0xf8, 0x2c, 0xc7, 0x3d, 0xca, 0xca, + 0x19, 0x90, 0x90, 0xf1, 0x5b, 0xe1, 0x98, 0xce, 0x7d, 0x3f, 0x9f, 0xc8, 0xff, 0xf5, + 0x50, 0xe1, 0x08, 0x81, 0xec, 0x49, 0xff, 0x27, 0x36, 0x9e, 0x7d, 0x4f, 0xd9, 0x64, + 0x01, 0x53, 0x49, 0x2a, 0x0a, 0x06, 0x25, 0x08, + ], + rsig: [ + 0xf4, 0xb8, 0x94, 0xba, 0x84, 0xce, 0x1e, 0xc3, 0x8a, 0x63, 0x15, 0x2f, 0xc4, 0x09, + 0xf9, 0x47, 0xd6, 0x1a, 0xbb, 0x1f, 0x48, 0x91, 0x63, 0x6b, 0xc3, 0xee, 0x19, 0xef, + 0x6d, 0x4b, 0x30, 0xc0, 0xfd, 0x22, 0x86, 0x6b, 0x84, 0xff, 0xbc, 0x7e, 0x2a, 0x78, + 0xc4, 0x3f, 0x57, 0x83, 0xd2, 0xd2, 0xea, 0xd0, 0x78, 0x59, 0x55, 0x03, 0x74, 0x43, + 0xc2, 0xf4, 0xd5, 0x2f, 0x78, 0x5e, 0xee, 0x07, + ], + }, + TestVector { + sk: [ + 0xad, 0xe7, 0xab, 0xb5, 0x51, 0xc7, 0x9d, 0x0f, 0x0e, 0x42, 0xef, 0x7f, 0x12, 0x06, + 0xb8, 0x77, 0x12, 0xa8, 0x4a, 0x61, 0xde, 0xa3, 0xf3, 0x7b, 0x42, 0x49, 0x6d, 0x7e, + 0xfd, 0x12, 0x52, 0x0c, + ], + vk: [ + 0x36, 0x9e, 0xa7, 0x51, 0x76, 0x2f, 0x83, 0x9d, 0x25, 0x70, 0x1a, 0x5e, 0xeb, 0x55, + 0x1e, 0xc4, 0xf0, 0x6c, 0x12, 0x90, 0xb3, 0xb9, 0xc3, 0xa7, 0x24, 0x40, 0x2d, 0xec, + 0x02, 0x73, 0x92, 0x21, + ], + alpha: [ + 0x81, 0x92, 0x25, 0x29, 0xa6, 0x3e, 0xe7, 0x43, 0xfc, 0x4f, 0xbb, 0xac, 0x45, 0xc4, + 0x98, 0x83, 0x16, 0xbc, 0x9b, 0x6e, 0x42, 0x8b, 0x01, 0xa8, 0xd3, 0x1f, 0xc1, 0xc2, + 0xa6, 0xca, 0x62, 0x05, + ], + rsk: [ + 0x77, 0x4d, 0xda, 0x07, 0x99, 0xf7, 0xed, 0x82, 0x87, 0x81, 0xe2, 0x5f, 0xc4, 0xa9, + 0xe8, 0x54, 0x28, 0x29, 0xb2, 0xce, 0x1f, 0xf4, 0x8d, 0x1d, 0x6d, 0xb9, 0xfa, 0xdb, + 0xb9, 0x28, 0x37, 0x03, + ], + rvk: [ + 0x0d, 0x92, 0xad, 0x6d, 0x46, 0xed, 0xac, 0xd0, 0x23, 0xd4, 0xd2, 0xef, 0x70, 0x3a, + 0x6c, 0xa0, 0xa7, 0x92, 0xcf, 0xc4, 0xb7, 0xda, 0x11, 0xc2, 0x35, 0x3b, 0xc8, 0x45, + 0xa2, 0x7a, 0x97, 0x4d, + ], + m: [ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, + ], + sig: [ + 0xdd, 0x65, 0x21, 0x01, 0x4d, 0xff, 0x70, 0x6e, 0x3a, 0x38, 0x52, 0x7a, 0x86, 0xb6, + 0xc1, 0x6e, 0x94, 0x14, 0x80, 0xe7, 0x33, 0xef, 0xf7, 0x9e, 0xbe, 0x0c, 0x43, 0x03, + 0x79, 0xd7, 0x57, 0x04, 0x9d, 0xb7, 0x90, 0xcd, 0x5e, 0x14, 0x44, 0x7c, 0x38, 0x6f, + 0x5f, 0xcb, 0x41, 0x9f, 0x27, 0xc4, 0x41, 0x3f, 0x35, 0x88, 0xfa, 0x21, 0x42, 0xd2, + 0xcf, 0xba, 0xed, 0x08, 0x2c, 0xc6, 0xdb, 0x07, + ], + rsig: [ + 0xd8, 0x94, 0x45, 0xcb, 0x9b, 0xd1, 0x03, 0x35, 0x69, 0x23, 0x1d, 0xd6, 0x28, 0xaa, + 0x62, 0x81, 0x09, 0xfe, 0x93, 0x50, 0x2b, 0xf2, 0x2f, 0x9a, 0x5f, 0x37, 0xb1, 0x4e, + 0x51, 0x7f, 0x9a, 0x20, 0x54, 0xae, 0xe3, 0xc8, 0x1b, 0x60, 0xb3, 0xf0, 0x55, 0x1e, + 0x32, 0xf7, 0x93, 0x5a, 0xbc, 0x2f, 0x37, 0xb9, 0x9a, 0xb3, 0xec, 0x99, 0x68, 0x02, + 0xef, 0xd6, 0x50, 0x69, 0xe1, 0x28, 0x12, 0x08, + ], + }, + TestVector { + sk: [ + 0xc9, 0xd2, 0xae, 0x1f, 0x6d, 0x32, 0xa6, 0x75, 0xd0, 0x9e, 0xb0, 0x82, 0x3f, 0x46, + 0x7f, 0xa9, 0x21, 0xb3, 0x28, 0x4a, 0xcb, 0x35, 0xfa, 0xbd, 0xfc, 0x99, 0x4d, 0xe5, + 0x49, 0xb8, 0x59, 0x0d, + ], + vk: [ + 0x2d, 0x2f, 0x31, 0x6e, 0x5c, 0x36, 0x9a, 0xe4, 0xdd, 0x2c, 0x82, 0x5f, 0x3d, 0x86, + 0x46, 0x00, 0x58, 0x40, 0x71, 0x84, 0x60, 0x3b, 0x21, 0x2c, 0xf3, 0x45, 0x9f, 0x36, + 0xc8, 0x69, 0x7f, 0xd8, + ], + alpha: [ + 0xeb, 0xbc, 0x89, 0x03, 0x11, 0x07, 0xc4, 0x4f, 0x47, 0x88, 0x9e, 0xd4, 0xd4, 0x37, + 0x5a, 0x41, 0x14, 0xcf, 0x8a, 0x75, 0xdd, 0x33, 0xb9, 0x62, 0xf2, 0xd7, 0x59, 0xd3, + 0xf4, 0xc6, 0xdf, 0x06, + ], + rsk: [ + 0xfd, 0x62, 0x41, 0x4c, 0x1f, 0x2b, 0xd3, 0xf4, 0x94, 0x16, 0x87, 0x8a, 0x80, 0x5d, + 0x71, 0x44, 0x35, 0x47, 0x7f, 0xbe, 0xa7, 0x2e, 0x4c, 0x1a, 0x46, 0xc2, 0x73, 0x53, + 0x54, 0xca, 0xbb, 0x05, + ], + rvk: [ + 0xf0, 0x43, 0x0e, 0x95, 0x3b, 0xe6, 0x0b, 0xf4, 0x38, 0xdb, 0xdc, 0xc2, 0x30, 0x3f, + 0x0e, 0x32, 0xa6, 0xf7, 0xce, 0x2f, 0xbe, 0xdf, 0xb1, 0x3a, 0xc5, 0x18, 0xf7, 0x5a, + 0x3f, 0xd1, 0x0e, 0xb5, + ], + m: [ + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, + ], + sig: [ + 0x72, 0x79, 0xa7, 0x5c, 0x01, 0x36, 0x75, 0xb3, 0x29, 0x84, 0xe5, 0xc7, 0x3a, 0x98, + 0x91, 0xeb, 0xf0, 0xb2, 0x29, 0xb1, 0x6e, 0x62, 0x35, 0xba, 0x36, 0xdf, 0xa1, 0xb5, + 0xa1, 0x0c, 0x5e, 0x44, 0x57, 0x81, 0x91, 0x89, 0x7c, 0x06, 0xb8, 0x52, 0x4a, 0x26, + 0x74, 0xaa, 0x7a, 0x0c, 0x8c, 0x23, 0x5f, 0x52, 0xd3, 0x3a, 0xc9, 0x2c, 0x70, 0x56, + 0xb2, 0xbe, 0x95, 0x3c, 0x3f, 0xaa, 0x3d, 0x07, + ], + rsig: [ + 0xaa, 0xd4, 0x82, 0x8c, 0xb3, 0x42, 0xcf, 0x09, 0xb0, 0x0e, 0x30, 0x2c, 0xbb, 0xe7, + 0xcc, 0x3e, 0x95, 0xfe, 0x1f, 0xf8, 0x28, 0x74, 0x8e, 0x5f, 0x5b, 0xc6, 0x9c, 0xbf, + 0xde, 0x6e, 0x27, 0x22, 0xd7, 0x64, 0x35, 0x68, 0x7e, 0x85, 0x0c, 0xd3, 0x07, 0xa9, + 0xc1, 0x82, 0xec, 0x10, 0xe6, 0x88, 0x1d, 0xd6, 0x5e, 0xed, 0xc1, 0x1f, 0xa7, 0xb4, + 0x6d, 0xe3, 0xa7, 0x19, 0x59, 0xce, 0xc0, 0x02, + ], + }, + TestVector { + sk: [ + 0x33, 0xbc, 0xd2, 0x86, 0x45, 0x41, 0xb8, 0xbb, 0x7f, 0xdc, 0x77, 0xa1, 0x9d, 0x97, + 0x0f, 0x92, 0x4e, 0xae, 0xec, 0xf4, 0x10, 0x3c, 0x38, 0xc8, 0xd2, 0xb0, 0x66, 0x81, + 0x42, 0xf2, 0x7d, 0x09, + ], + vk: [ + 0x74, 0x17, 0x94, 0xe6, 0x2c, 0xf9, 0x32, 0x0c, 0x58, 0xba, 0xc5, 0x94, 0xa2, 0xb9, + 0x0e, 0x34, 0x0a, 0x6d, 0x8a, 0x68, 0x05, 0x6f, 0x6e, 0xd5, 0xc7, 0x86, 0x8c, 0x5f, + 0xf3, 0xe4, 0xd6, 0x16, + ], + alpha: [ + 0x7c, 0xe7, 0x25, 0xa5, 0xfe, 0xf6, 0x1b, 0xd4, 0xa1, 0xe9, 0xc7, 0x73, 0x28, 0xe8, + 0x21, 0x0e, 0xb7, 0x29, 0x2d, 0x95, 0x4c, 0x64, 0xe9, 0x9e, 0x8b, 0xed, 0xd0, 0x7a, + 0xb3, 0xab, 0x0e, 0x0d, + ], + rsk: [ + 0xf8, 0x76, 0x01, 0x55, 0xe5, 0x29, 0x3d, 0xbf, 0x9e, 0xb5, 0x77, 0x48, 0x32, 0x5f, + 0xc9, 0xf9, 0x04, 0x9d, 0xe5, 0x88, 0x5c, 0x65, 0xba, 0x60, 0xb5, 0xee, 0x03, 0x97, + 0x0b, 0xe9, 0x0e, 0x08, + ], + rvk: [ + 0x66, 0x62, 0xba, 0x09, 0x95, 0x0a, 0xcc, 0xd2, 0xce, 0xa3, 0xc7, 0xa8, 0x12, 0x90, + 0xcd, 0x59, 0x78, 0xa6, 0x2b, 0x5a, 0xc5, 0xbb, 0xc4, 0x8d, 0x9f, 0x58, 0x19, 0xcd, + 0xc9, 0x64, 0x6f, 0x0a, + ], + m: [ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, + ], + sig: [ + 0x51, 0x23, 0xb3, 0x1f, 0x84, 0xaf, 0x0c, 0x35, 0x5e, 0x13, 0xe7, 0x8a, 0x64, 0xd7, + 0xa3, 0xcd, 0xfd, 0x6b, 0xdf, 0xfd, 0xc7, 0x33, 0x38, 0xd9, 0x31, 0x7f, 0x73, 0x43, + 0x91, 0xa5, 0x5a, 0xe6, 0x25, 0x8f, 0x69, 0x80, 0xb9, 0xc7, 0xd1, 0x90, 0xcf, 0xa3, + 0x65, 0x81, 0xa9, 0xa4, 0x7a, 0x86, 0x3f, 0xd3, 0xbf, 0x76, 0x59, 0x42, 0x22, 0x95, + 0xb7, 0x5f, 0xd1, 0x22, 0xc3, 0xdd, 0x8a, 0x05, + ], + rsig: [ + 0x5b, 0xae, 0x25, 0x4f, 0xbd, 0xed, 0x60, 0x7a, 0x5c, 0x48, 0xb5, 0x30, 0x29, 0xf5, + 0x9b, 0xa7, 0x06, 0x32, 0x48, 0x79, 0xaa, 0x18, 0xd9, 0xc4, 0x73, 0x19, 0x00, 0x4b, + 0xe0, 0x2c, 0xec, 0xe0, 0xb8, 0xbb, 0x02, 0x4a, 0x7a, 0xab, 0xaa, 0x0a, 0x64, 0x0f, + 0x3a, 0x54, 0xdc, 0xda, 0xf2, 0x11, 0x31, 0x46, 0x9a, 0x50, 0x06, 0xbe, 0x27, 0x81, + 0xa5, 0x67, 0xff, 0xa6, 0x50, 0x3a, 0x35, 0x03, + ], + }, + TestVector { + sk: [ + 0xca, 0x35, 0x06, 0xd6, 0xaf, 0x77, 0x67, 0xb5, 0x79, 0x0e, 0xf0, 0xc5, 0x19, 0x0f, + 0xb3, 0xf3, 0x87, 0x7c, 0x4a, 0xab, 0x40, 0xe0, 0xdd, 0x65, 0x1a, 0xbb, 0xda, 0xcb, + 0x54, 0x4e, 0xd0, 0x05, + ], + vk: [ + 0xba, 0xb6, 0xcf, 0xb5, 0xc8, 0xea, 0x34, 0x91, 0x25, 0x1b, 0x46, 0xd5, 0x2a, 0xca, + 0x25, 0xd9, 0xe9, 0xaf, 0x69, 0xfa, 0xa9, 0xb4, 0xe4, 0x0b, 0x03, 0xad, 0x00, 0x86, + 0xde, 0x59, 0xb5, 0x1f, + ], + alpha: [ + 0xbe, 0xa3, 0x87, 0x20, 0x3f, 0x43, 0x76, 0x0a, 0xd3, 0x7d, 0x61, 0xde, 0x0e, 0xb5, + 0x9f, 0xca, 0x6c, 0xab, 0x75, 0x60, 0xdf, 0x64, 0xfa, 0xbb, 0x95, 0x11, 0x57, 0x9f, + 0x6f, 0x68, 0x26, 0x06, + ], + rsk: [ + 0x88, 0xd9, 0x8d, 0xf6, 0xee, 0xba, 0xdd, 0xbf, 0x4c, 0x8c, 0x51, 0xa4, 0x28, 0xc4, + 0x52, 0xbe, 0xf4, 0x27, 0xc0, 0x0b, 0x20, 0x45, 0xd8, 0x21, 0xb0, 0xcc, 0x31, 0x6b, + 0xc4, 0xb6, 0xf6, 0x0b, + ], + rvk: [ + 0x11, 0x26, 0x7d, 0x14, 0xd5, 0xe0, 0xb2, 0xbb, 0x3c, 0xe0, 0x99, 0xe8, 0xef, 0x84, + 0x49, 0x47, 0x1c, 0xbc, 0xfc, 0x69, 0x39, 0xa4, 0xb3, 0x48, 0xde, 0xa2, 0xc1, 0x73, + 0x56, 0xa1, 0xe8, 0xdd, + ], + m: [ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, + ], + sig: [ + 0xdc, 0x18, 0xc8, 0x8d, 0x96, 0x44, 0x42, 0x40, 0x6d, 0x65, 0x0a, 0xa2, 0xff, 0xbd, + 0x83, 0xd1, 0x13, 0xbf, 0x6a, 0x19, 0xda, 0x78, 0xf2, 0x66, 0x5b, 0x29, 0x4f, 0xa5, + 0xfa, 0x45, 0x0b, 0x92, 0x81, 0xa0, 0x7e, 0x32, 0x0c, 0x1a, 0xa3, 0x1d, 0x32, 0x44, + 0x9e, 0x00, 0xc5, 0xc3, 0x2d, 0xb2, 0xf4, 0x13, 0xdf, 0x0b, 0x63, 0xd0, 0x72, 0x8f, + 0xa4, 0x09, 0x41, 0xa8, 0xda, 0x02, 0x4f, 0x01, + ], + rsig: [ + 0x59, 0xe2, 0xe8, 0x18, 0x76, 0x6c, 0x50, 0xfc, 0x8f, 0x38, 0x40, 0xb2, 0x72, 0xaf, + 0x9a, 0xd9, 0x47, 0x56, 0xc8, 0x41, 0x32, 0x95, 0xfc, 0x79, 0x5f, 0xaf, 0xbc, 0xc0, + 0x71, 0x8e, 0x6c, 0x08, 0x16, 0x9a, 0x00, 0xd5, 0x83, 0x02, 0x77, 0x2a, 0x28, 0x28, + 0x43, 0xe8, 0x88, 0xd9, 0x81, 0xfa, 0x04, 0x79, 0x5d, 0x01, 0x4c, 0xf9, 0xc8, 0xcd, + 0xb9, 0x07, 0xff, 0x1b, 0x43, 0x0d, 0x92, 0x00, + ], + }, + TestVector { + sk: [ + 0xbc, 0x27, 0x83, 0x8d, 0xe2, 0xa6, 0x14, 0xcf, 0xba, 0x6c, 0x3e, 0x92, 0x2a, 0x8f, + 0x84, 0x24, 0xd9, 0x85, 0x6f, 0x68, 0x16, 0xf3, 0xbc, 0x61, 0x02, 0x31, 0x3b, 0x7f, + 0xaf, 0x5c, 0x3a, 0x0c, + ], + vk: [ + 0xd7, 0x9b, 0xe9, 0xff, 0x22, 0x9a, 0x2e, 0x35, 0xf5, 0xbc, 0xa4, 0x48, 0xe5, 0xeb, + 0x4a, 0x8a, 0xa9, 0x7f, 0xb4, 0x18, 0x02, 0x91, 0x25, 0xcf, 0xba, 0xa7, 0x8a, 0x91, + 0xa3, 0x82, 0xb0, 0x94, + ], + alpha: [ + 0x21, 0xa7, 0x15, 0x0e, 0x19, 0x4f, 0xed, 0xfe, 0xf9, 0x0c, 0x5d, 0x10, 0xe4, 0x20, + 0x85, 0x8b, 0xca, 0x40, 0x04, 0x04, 0x0e, 0xb6, 0x81, 0xd1, 0x4e, 0x75, 0xc4, 0x47, + 0x13, 0x51, 0xcb, 0x02, + ], + rsk: [ + 0x26, 0xa2, 0xa1, 0xc4, 0x9c, 0xe7, 0x6a, 0xfd, 0x31, 0x69, 0xd3, 0xd5, 0x7a, 0x8f, + 0xa1, 0x09, 0xa3, 0x8b, 0x3f, 0x6b, 0x23, 0x6e, 0xd7, 0x2c, 0xa8, 0xf6, 0xcb, 0x61, + 0xd8, 0xf8, 0x87, 0x00, + ], + rvk: [ + 0x54, 0xbf, 0x1b, 0xe7, 0x2e, 0x6d, 0x41, 0x20, 0x8b, 0x8a, 0xec, 0x11, 0x61, 0xd3, + 0xba, 0x59, 0x51, 0x9f, 0xb9, 0x3d, 0xa0, 0x1a, 0x55, 0xe6, 0x78, 0xe2, 0x75, 0x20, + 0x06, 0x60, 0x36, 0xc9, + ], + m: [ + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, + ], + sig: [ + 0x9a, 0xf6, 0xf2, 0x80, 0x0f, 0x4b, 0x80, 0xf7, 0x93, 0xbe, 0x64, 0x8a, 0x43, 0x9f, + 0x86, 0xe5, 0x7d, 0xa1, 0xb9, 0x19, 0x99, 0x9e, 0x41, 0x91, 0x09, 0x99, 0xd4, 0x2e, + 0xd0, 0xf3, 0x89, 0x6d, 0xb7, 0x6e, 0x06, 0x38, 0x8b, 0x27, 0x2c, 0x99, 0x85, 0x8b, + 0x55, 0x04, 0xd0, 0x2e, 0xc6, 0xb4, 0xd5, 0x25, 0xb8, 0x71, 0x38, 0x10, 0x50, 0x5f, + 0x4f, 0xc0, 0x31, 0x08, 0x3a, 0x14, 0xbf, 0x09, + ], + rsig: [ + 0x3f, 0x7d, 0x50, 0x71, 0xb8, 0x76, 0x17, 0x49, 0x05, 0x71, 0xa8, 0xbe, 0x91, 0x74, + 0x9e, 0x69, 0xf6, 0xbc, 0xba, 0x5a, 0xb6, 0x26, 0xe4, 0x2f, 0xf9, 0x2d, 0x0d, 0x7d, + 0xab, 0x73, 0xf3, 0x03, 0x61, 0xe5, 0xa2, 0x24, 0x99, 0x8e, 0x1f, 0x5e, 0xa1, 0xe5, + 0xf8, 0x68, 0x9a, 0x06, 0xa2, 0x77, 0x48, 0xbf, 0x74, 0x19, 0x63, 0xef, 0x51, 0x33, + 0x22, 0xf4, 0xa1, 0xba, 0x99, 0xaa, 0x36, 0x03, + ], + }, + TestVector { + sk: [ + 0xb2, 0x08, 0x59, 0xb8, 0x8e, 0xe3, 0x33, 0x8a, 0x64, 0x95, 0x4f, 0x8a, 0x9e, 0x8e, + 0x9b, 0xf3, 0xe7, 0x11, 0x5a, 0xcf, 0x7c, 0x6e, 0x7f, 0x01, 0x43, 0x2c, 0x5f, 0x76, + 0x96, 0xd2, 0xd0, 0x05, + ], + vk: [ + 0xa8, 0x1f, 0xe6, 0x84, 0x6d, 0xbe, 0x0a, 0x75, 0xc0, 0xf4, 0x9b, 0x21, 0x32, 0x32, + 0xbe, 0xad, 0xd1, 0xf9, 0xa5, 0x64, 0x67, 0x3d, 0x25, 0xb9, 0x1e, 0xe0, 0xf1, 0x7c, + 0xe9, 0xca, 0xa3, 0x63, + ], + alpha: [ + 0x44, 0xd9, 0x08, 0xe1, 0xc1, 0x5e, 0x6b, 0xd9, 0x38, 0x0a, 0x8b, 0x23, 0x5a, 0xce, + 0x02, 0xfa, 0xc1, 0xc0, 0x87, 0x94, 0x45, 0x4b, 0xcd, 0xb4, 0xa6, 0xf4, 0x8c, 0xea, + 0x78, 0xa7, 0x4a, 0x04, + ], + rsk: [ + 0xf6, 0xe1, 0x61, 0x99, 0x50, 0x42, 0x9f, 0x63, 0x9d, 0x9f, 0xda, 0xad, 0xf8, 0x5c, + 0x9e, 0xed, 0xa9, 0xd2, 0xe1, 0x63, 0xc2, 0xb9, 0x4c, 0xb6, 0xe9, 0x20, 0xec, 0x60, + 0x0f, 0x7a, 0x1b, 0x0a, + ], + rvk: [ + 0x0b, 0x68, 0xd5, 0x0f, 0x91, 0x3c, 0xd1, 0xb7, 0x8b, 0x59, 0x92, 0x1e, 0x16, 0x56, + 0xd5, 0x76, 0xb0, 0xeb, 0x17, 0x1e, 0xd3, 0x87, 0x0d, 0x39, 0xfe, 0xc6, 0x94, 0x41, + 0xb3, 0x4b, 0x25, 0x38, + ], + m: [ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, + ], + sig: [ + 0x64, 0x59, 0x67, 0x6a, 0x94, 0x16, 0x34, 0xec, 0xb6, 0x1e, 0x59, 0xb7, 0x9a, 0x98, + 0xab, 0xe5, 0x87, 0x6f, 0x35, 0x6f, 0x72, 0x8a, 0xa0, 0x9e, 0x0c, 0xca, 0x9e, 0xfe, + 0x05, 0x76, 0x1a, 0x33, 0x09, 0xaa, 0x88, 0xb2, 0xfa, 0x0e, 0xe2, 0xd0, 0x4c, 0x1c, + 0x46, 0xe9, 0xf2, 0xa0, 0x48, 0xd5, 0x9d, 0x55, 0x65, 0xaf, 0xa6, 0xc3, 0xf1, 0x5b, + 0xce, 0x70, 0x8d, 0xaa, 0xab, 0x7b, 0x34, 0x0e, + ], + rsig: [ + 0xc9, 0x66, 0x84, 0xec, 0x7e, 0xa6, 0x0b, 0xde, 0x87, 0x88, 0x22, 0xdd, 0xca, 0xf6, + 0xb8, 0xb0, 0xbd, 0x31, 0x98, 0x51, 0x54, 0xdf, 0x9a, 0xd4, 0xf6, 0x90, 0x7d, 0xf8, + 0xfe, 0xd9, 0x5c, 0x1d, 0x84, 0xfe, 0x67, 0xe6, 0x78, 0x75, 0xa5, 0x39, 0x55, 0x0e, + 0xb2, 0x51, 0x4f, 0x19, 0x3b, 0x8e, 0xd4, 0x57, 0x25, 0x6c, 0x8d, 0x30, 0x28, 0x1d, + 0x6f, 0x8b, 0xb9, 0x54, 0x49, 0x24, 0xca, 0x0c, + ], + }, + TestVector { + sk: [ + 0x32, 0x16, 0xae, 0x47, 0xe9, 0xf5, 0x3e, 0x8a, 0x52, 0x79, 0x6f, 0x24, 0xb6, 0x24, + 0x60, 0x77, 0x6b, 0xd5, 0xf2, 0x05, 0xa7, 0x8e, 0x15, 0x95, 0xbc, 0x8e, 0xfe, 0xdc, + 0x51, 0x9d, 0x36, 0x0b, + ], + vk: [ + 0xdf, 0x74, 0xbf, 0x04, 0x79, 0x61, 0xcc, 0x5c, 0xda, 0xc8, 0x28, 0x90, 0xc7, 0x6e, + 0xc6, 0x75, 0xbd, 0x4e, 0x89, 0xea, 0xd2, 0x80, 0xc9, 0x52, 0xd7, 0xc3, 0x3e, 0xea, + 0xf2, 0xb5, 0xa6, 0x6b, + ], + alpha: [ + 0xc9, 0x61, 0xf2, 0xdd, 0x93, 0x68, 0x2a, 0xdb, 0x93, 0xf5, 0xc0, 0x5a, 0x73, 0xfd, + 0xbc, 0x6d, 0x43, 0xc7, 0x0e, 0x1b, 0x15, 0xe8, 0xd5, 0x3e, 0x3f, 0x17, 0xa8, 0x24, + 0x94, 0xe3, 0xf2, 0x09, + ], + rsk: [ + 0x44, 0x4b, 0xa9, 0x4e, 0x1e, 0x50, 0xd2, 0x94, 0x63, 0x5e, 0x68, 0xb2, 0x95, 0x01, + 0xb5, 0x3e, 0xae, 0x61, 0xcd, 0x1f, 0xbb, 0x3b, 0x84, 0xcd, 0x52, 0xf6, 0x72, 0x9c, + 0xfb, 0xcb, 0xab, 0x06, + ], + rvk: [ + 0x0a, 0xfb, 0xe4, 0x06, 0xa8, 0x91, 0xc3, 0xb8, 0xc3, 0x10, 0xc2, 0x15, 0xbc, 0x68, + 0xa9, 0x13, 0xde, 0x7c, 0xda, 0x06, 0xaf, 0x29, 0x42, 0x00, 0x56, 0x46, 0x8d, 0x0c, + 0x08, 0x85, 0x5b, 0x28, + ], + m: [ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, + ], + sig: [ + 0x24, 0x93, 0x2c, 0x1f, 0xaa, 0x01, 0x63, 0xca, 0x9a, 0x7f, 0xcd, 0xe4, 0x76, 0x11, + 0x29, 0xd2, 0xe5, 0xe9, 0x9c, 0xf5, 0xef, 0xa2, 0x5d, 0x27, 0x04, 0x58, 0x8e, 0x1c, + 0x75, 0x67, 0x7b, 0x5e, 0xeb, 0xe4, 0x55, 0x04, 0x8d, 0x7c, 0xe1, 0xb0, 0xd2, 0x01, + 0x27, 0x53, 0xf7, 0x1b, 0x27, 0x25, 0x01, 0x2e, 0xe1, 0x85, 0x49, 0x28, 0x73, 0x18, + 0xf9, 0xcd, 0x73, 0xf0, 0x7f, 0x0f, 0xb5, 0x02, + ], + rsig: [ + 0xf7, 0xfa, 0x26, 0xca, 0x22, 0xf3, 0x86, 0xc4, 0x3c, 0x19, 0x1a, 0x0b, 0x3e, 0xa6, + 0x57, 0x7e, 0x8e, 0xea, 0xa3, 0xf3, 0x6b, 0x9b, 0xd1, 0xa3, 0xac, 0x3d, 0xf6, 0xf8, + 0x83, 0xa3, 0xff, 0xdb, 0x31, 0x32, 0x0b, 0xde, 0x62, 0x7f, 0xf4, 0x6f, 0xc2, 0x26, + 0x4a, 0x32, 0x63, 0xb9, 0xab, 0x67, 0x12, 0x3b, 0xa5, 0xe1, 0x08, 0x43, 0x20, 0xd9, + 0x10, 0xb3, 0x94, 0xef, 0x8c, 0x65, 0xba, 0x09, + ], + }, + TestVector { + sk: [ + 0x85, 0x83, 0x6f, 0x98, 0x32, 0xb2, 0x8d, 0xe7, 0xc6, 0x36, 0x13, 0xe2, 0xa6, 0xed, + 0x36, 0xfb, 0x1a, 0xb4, 0x4f, 0xb0, 0xc1, 0x3f, 0xa8, 0x79, 0x8c, 0xd9, 0xcd, 0x30, + 0x30, 0xd4, 0x55, 0x03, + ], + vk: [ + 0xbf, 0xd5, 0xbc, 0x00, 0xc7, 0xc0, 0x22, 0xaa, 0x89, 0x01, 0xae, 0x08, 0x3c, 0x12, + 0xd5, 0x4b, 0x82, 0xf0, 0xdd, 0xff, 0x8e, 0xd6, 0xdb, 0x9a, 0x12, 0xd5, 0x9a, 0x5e, + 0xf6, 0xa5, 0xa2, 0xe0, + ], + alpha: [ + 0xa2, 0xe8, 0xb9, 0xe1, 0x6d, 0x6f, 0xf3, 0xca, 0x6c, 0x53, 0xd4, 0xe8, 0x8a, 0xbb, + 0xb9, 0x9b, 0xe7, 0xaf, 0x7e, 0x36, 0x59, 0x63, 0x1f, 0x1e, 0xae, 0x1e, 0xff, 0x23, + 0x87, 0x4d, 0x8e, 0x0c, + ], + rsk: [ + 0x70, 0x3f, 0x32, 0xa3, 0x41, 0x13, 0xea, 0xe1, 0xb0, 0x79, 0x1f, 0xfe, 0x9d, 0x88, + 0x88, 0xf0, 0x01, 0x29, 0x9a, 0xe5, 0x19, 0x68, 0x60, 0x91, 0x91, 0x48, 0x99, 0xef, + 0xcc, 0x6c, 0x66, 0x01, + ], + rvk: [ + 0xeb, 0x92, 0x97, 0x03, 0x6c, 0xf5, 0x17, 0xe1, 0x5e, 0x9e, 0xfe, 0x39, 0x75, 0x32, + 0x8d, 0xb4, 0x8e, 0xe7, 0xc2, 0x69, 0x4e, 0x94, 0x6d, 0xb2, 0x5f, 0x52, 0x87, 0x88, + 0xf6, 0xa1, 0xdb, 0x14, + ], + m: [ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, + ], + sig: [ + 0x64, 0xab, 0xd1, 0x25, 0xbf, 0xc4, 0xc6, 0x54, 0xfa, 0xf2, 0xb6, 0xdd, 0x75, 0x3e, + 0xc6, 0x90, 0x22, 0x4d, 0xbc, 0xab, 0x8c, 0xd6, 0x32, 0xdd, 0x59, 0x3c, 0x91, 0xce, + 0x3a, 0xb0, 0xbc, 0xad, 0xca, 0x92, 0x76, 0x34, 0x02, 0x1c, 0x31, 0x47, 0x6c, 0x78, + 0xc5, 0xac, 0x7c, 0xcc, 0xab, 0xbd, 0x6f, 0x92, 0x7d, 0xf2, 0x05, 0xea, 0xa7, 0x07, + 0xcc, 0x00, 0xd4, 0x7d, 0x39, 0xf3, 0xe4, 0x0c, + ], + rsig: [ + 0xeb, 0x7a, 0x06, 0x5d, 0x75, 0xf8, 0x45, 0xdc, 0x09, 0x41, 0xb7, 0x09, 0xc0, 0xb1, + 0x49, 0xea, 0xfd, 0x80, 0x5e, 0xa5, 0x8f, 0x38, 0x0b, 0x92, 0xb9, 0xd3, 0x10, 0x8a, + 0x56, 0x1b, 0xda, 0x17, 0x85, 0xdf, 0x8f, 0x10, 0x1e, 0x0e, 0x14, 0x0f, 0xca, 0xee, + 0x99, 0xb7, 0xdb, 0xb7, 0xdf, 0xbf, 0x7e, 0x61, 0xf3, 0xa1, 0x2f, 0x46, 0x09, 0x50, + 0x69, 0xe0, 0x6e, 0x88, 0x96, 0xa9, 0xe4, 0x04, + ], + }, + ]; + + for tv in test_vectors { + let sk = PrivateKey::::read(&tv.sk[..]).unwrap(); + let vk = PublicKey::::read(&tv.vk[..], &JUBJUB).unwrap(); + let rvk = PublicKey::::read(&tv.rvk[..], &JUBJUB).unwrap(); + let sig = Signature::read(&tv.sig[..]).unwrap(); + let rsig = Signature::read(&tv.rsig[..]).unwrap(); + + let mut alpha_repr = <::Fs as PrimeField>::Repr::default(); + alpha_repr.read_le(&tv.alpha[..]).unwrap(); + let alpha = ::Fs::from_repr(alpha_repr).unwrap(); + + { + let mut vec = Vec::new(); + sk.randomize(alpha.clone()).write(&mut vec).unwrap(); + assert_eq!(&vec, &tv.rsk); + } + { + let mut vec = Vec::new(); + vk.randomize(alpha, FixedGenerators::SpendingKeyGenerator, &JUBJUB) + .write(&mut vec) + .unwrap(); + assert_eq!(&vec, &tv.rvk); + } + + assert!(vk.verify(&tv.m, &sig, FixedGenerators::SpendingKeyGenerator, &JUBJUB)); + assert!(rvk.verify(&tv.m, &rsig, FixedGenerators::SpendingKeyGenerator, &JUBJUB)); + assert!(!vk.verify(&tv.m, &rsig, FixedGenerators::SpendingKeyGenerator, &JUBJUB)); + assert!(!rvk.verify(&tv.m, &sig, FixedGenerators::SpendingKeyGenerator, &JUBJUB)); + } +} diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index 36bb13949..c60cc98c6 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -316,12 +316,16 @@ TransactionBuilderResult TransactionBuilder::Build() auto enc = res.value(); auto encryptor = enc.second; + libzcash::SaplingPaymentAddress address(output.note.d, output.note.pk_d); + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << address; + std::vector addressBytes(ss.begin(), ss.end()); + OutputDescription odesc; if (!librustzcash_sapling_output_proof( ctx, encryptor.get_esk().begin(), - output.note.d.data(), - output.note.pk_d.begin(), + addressBytes.data(), output.note.r.begin(), output.note.value(), odesc.cv.begin(), diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 099dd8b52..3e7fb876f 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -489,7 +489,6 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { // spending key is crypted, so we can't extract valid payment address libzcash::SaplingExtendedSpendingKey keyOut; EXPECT_FALSE(wallet2.GetSaplingExtendedSpendingKey(address, keyOut)); - ASSERT_FALSE(address == keyOut.DefaultAddress()); // address -> ivk mapping is not crypted libzcash::SaplingIncomingViewingKey ivkOut; diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index b82d76ee5..d4b50d8f8 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -659,6 +659,10 @@ double benchmark_create_sapling_output() auto enc = res.value(); auto encryptor = enc.second; + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss << address; + std::vector addressBytes(ss.begin(), ss.end()); + auto ctx = librustzcash_sapling_proving_ctx_init(); struct timeval tv_start; @@ -668,8 +672,7 @@ double benchmark_create_sapling_output() bool result = librustzcash_sapling_output_proof( ctx, encryptor.get_esk().begin(), - note.d.data(), - note.pk_d.begin(), + addressBytes.data(), note.r.begin(), note.value(), odesc.cv.begin(), From fa185e6ca81ff6429150a82b16a28050dc477cc0 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 14:08:37 +0000 Subject: [PATCH 015/146] Windows cross-build generates .lib files, which should be ignored by git and removed by clean. --- .gitignore | 3 ++- src/Makefile.am | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2b49146e9..90bf1e1d2 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ src/univalue/gen *.o-* .bitcoinz *.a +*.lib *.pb.cc *.pb.h .vscode @@ -78,7 +79,7 @@ src/univalue/gen # Rust .cargo/config.toml -src/rustzcash/target/ +src/rust/target/ # Compilation src/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index f5c341943..b6f35658b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -520,6 +520,7 @@ clean-local: rm -f fuzz.cpp rm -rf fuzzing/*/output -rm -f config.h + -rm -f *.lib bench/*.lib test/*.lib .rc.o: @test -f $(WINDRES) From 796aa3b41ecdbdd13eaaa04ed43a67506697643d Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 14:31:05 +0000 Subject: [PATCH 016/146] Replace BOOST_STATIC_ASSERT with static_assert --- src/crypto/equihash.cpp | 8 ++++---- src/crypto/equihash.h | 8 +++----- src/main.cpp | 9 ++++----- src/policy/policy.h | 4 ++-- src/zcash/IncrementalMerkleTree.hpp | 3 +-- src/zcash/NoteEncryption.cpp | 7 +++---- src/zcash/Proof.cpp | 1 - 7 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/crypto/equihash.cpp b/src/crypto/equihash.cpp index 5a4111838..4107883c1 100644 --- a/src/crypto/equihash.cpp +++ b/src/crypto/equihash.cpp @@ -143,7 +143,7 @@ void CompressArray(const unsigned char* in, size_t in_len, // comparison void EhIndexToArray(const eh_index i, unsigned char* array) { - BOOST_STATIC_ASSERT(sizeof(eh_index) == 4); + static_assert(sizeof(eh_index) == 4); eh_index bei = htobe32(i); memcpy(array, &bei, sizeof(eh_index)); } @@ -152,7 +152,7 @@ void EhIndexToArray(const eh_index i, unsigned char* array) // comparison eh_index ArrayToEhIndex(const unsigned char* array) { - BOOST_STATIC_ASSERT(sizeof(eh_index) == 4); + static_assert(sizeof(eh_index) == 4); eh_index bei; memcpy(&bei, array, sizeof(eh_index)); return be32toh(bei); @@ -161,7 +161,7 @@ eh_index ArrayToEhIndex(const unsigned char* array) eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen) { // Truncate to 8 bits - BOOST_STATIC_ASSERT(sizeof(eh_trunc) == 1); + static_assert(sizeof(eh_trunc) == 1); return (i >> (ilen - 8)) & 0xff; } @@ -215,7 +215,7 @@ StepRow::StepRow(const unsigned char* hashIn, size_t hInLen, template template StepRow::StepRow(const StepRow& a) { - BOOST_STATIC_ASSERT(W <= WIDTH); + static_assert(W <= WIDTH); std::copy(a.hash, a.hash+W, hash); } diff --git a/src/crypto/equihash.h b/src/crypto/equihash.h index 9f0244c77..0e6a64cde 100644 --- a/src/crypto/equihash.h +++ b/src/crypto/equihash.h @@ -19,8 +19,6 @@ #include #include -#include - typedef crypto_generichash_blake2b_state eh_HashState; typedef uint32_t eh_index; typedef uint8_t eh_trunc; @@ -164,9 +162,9 @@ template class Equihash { private: - BOOST_STATIC_ASSERT(K < N); - BOOST_STATIC_ASSERT(N % 8 == 0); - BOOST_STATIC_ASSERT((N/(K+1)) + 1 < 8*sizeof(eh_index)); + static_assert(K < N); + static_assert(N % 8 == 0); + static_assert((N/(K+1)) + 1 < 8*sizeof(eh_index)); public: enum : size_t { IndicesPerHashOutput=512/N }; diff --git a/src/main.cpp b/src/main.cpp index dc715ff80..34c6abc1c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,7 +46,6 @@ #include #include #include -#include using namespace std; @@ -853,7 +852,7 @@ bool ContextualCheckTransaction( // Rules that apply before Sapling: if (!saplingActive) { // Size limits - BOOST_STATIC_ASSERT(MAX_BLOCK_SIZE > MAX_TX_SIZE_BEFORE_SAPLING); // sanity + static_assert(MAX_BLOCK_SIZE > MAX_TX_SIZE_BEFORE_SAPLING); // sanity if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE_BEFORE_SAPLING) return state.DoS(100, error("ContextualCheckTransaction(): size limits failed"), REJECT_INVALID, "bad-txns-oversize"); @@ -878,7 +877,7 @@ bool ContextualCheckTransaction( if (!tx.vJoinSplit.empty()) { - BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); + static_assert(crypto_sign_PUBLICKEYBYTES == 32); // We rely on libsodium to check that the signature is canonical. // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 @@ -1028,8 +1027,8 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio REJECT_INVALID, "bad-txns-vout-empty"); // Size limits - BOOST_STATIC_ASSERT(MAX_BLOCK_SIZE >= MAX_TX_SIZE_AFTER_SAPLING); // sanity - BOOST_STATIC_ASSERT(MAX_TX_SIZE_AFTER_SAPLING > MAX_TX_SIZE_BEFORE_SAPLING); // sanity + static_assert(MAX_BLOCK_SIZE >= MAX_TX_SIZE_AFTER_SAPLING); // sanity + static_assert(MAX_TX_SIZE_AFTER_SAPLING > MAX_TX_SIZE_BEFORE_SAPLING); // sanity if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE_AFTER_SAPLING) return state.DoS(100, error("CheckTransaction(): size limits failed"), REJECT_INVALID, "bad-txns-oversize"); diff --git a/src/policy/policy.h b/src/policy/policy.h index 2a7a4fd56..510618d84 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -44,8 +44,8 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; // Sanity check the magic numbers when we change them -BOOST_STATIC_ASSERT(DEFAULT_BLOCK_MAX_SIZE <= MAX_BLOCK_SIZE); -BOOST_STATIC_ASSERT(DEFAULT_BLOCK_PRIORITY_SIZE <= DEFAULT_BLOCK_MAX_SIZE); +static_assert(DEFAULT_BLOCK_MAX_SIZE <= MAX_BLOCK_SIZE); +static_assert(DEFAULT_BLOCK_PRIORITY_SIZE <= DEFAULT_BLOCK_MAX_SIZE); bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); /** diff --git a/src/zcash/IncrementalMerkleTree.hpp b/src/zcash/IncrementalMerkleTree.hpp index 1be24fb14..807e6ee2c 100644 --- a/src/zcash/IncrementalMerkleTree.hpp +++ b/src/zcash/IncrementalMerkleTree.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include "uint256.h" #include "serialize.h" @@ -83,7 +82,7 @@ class IncrementalMerkleTree { friend class IncrementalWitness; public: - BOOST_STATIC_ASSERT(Depth >= 1); + static_assert(Depth >= 1); IncrementalMerkleTree() { } diff --git a/src/zcash/NoteEncryption.cpp b/src/zcash/NoteEncryption.cpp index e4fde317c..866483c59 100644 --- a/src/zcash/NoteEncryption.cpp +++ b/src/zcash/NoteEncryption.cpp @@ -1,7 +1,6 @@ #include "NoteEncryption.hpp" #include #include "sodium.h" -#include #include "prf.h" #include "librustzcash.h" @@ -292,9 +291,9 @@ NoteEncryption::NoteEncryption(uint256 hSig) : nonce(0), hSig(hSig) { // All of this code assumes crypto_scalarmult_BYTES is 32 // There's no reason that will _ever_ change, but for // completeness purposes, let's check anyway. - BOOST_STATIC_ASSERT(32 == crypto_scalarmult_BYTES); - BOOST_STATIC_ASSERT(32 == crypto_scalarmult_SCALARBYTES); - BOOST_STATIC_ASSERT(NOTEENCRYPTION_AUTH_BYTES == crypto_aead_chacha20poly1305_ABYTES); + static_assert(32 == crypto_scalarmult_BYTES); + static_assert(32 == crypto_scalarmult_SCALARBYTES); + static_assert(NOTEENCRYPTION_AUTH_BYTES == crypto_aead_chacha20poly1305_ABYTES); // Create the ephemeral keypair esk = random_uint256(); diff --git a/src/zcash/Proof.cpp b/src/zcash/Proof.cpp index 7ab1298a8..00cf40b11 100644 --- a/src/zcash/Proof.cpp +++ b/src/zcash/Proof.cpp @@ -2,7 +2,6 @@ #include "crypto/common.h" -#include #include namespace libzcash { From fa7959fe23deb3b33c196d62766e6db0d80d68b6 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 14:36:25 +0000 Subject: [PATCH 017/146] Remove unused visibility checks --- configure.ac | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/configure.ac b/configure.ac index f6a8c38ef..c3c7d4935 100644 --- a/configure.ac +++ b/configure.ac @@ -728,23 +728,6 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [ AC_MSG_RESULT(no)] ) -AC_MSG_CHECKING([for visibility attribute]) -AC_LINK_IFELSE([AC_LANG_SOURCE([ - int foo_def( void ) __attribute__((visibility("default"))); - int main(){} - ])], - [ - AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE,1,[Define if the visibility attribute is supported.]) - AC_MSG_RESULT(yes) - ], - [ - AC_MSG_RESULT(no) - if test x$use_reduce_exports = xyes; then - AC_MSG_ERROR([Cannot find a working visibility attribute. Use --disable-reduce-exports.]) - fi - ] -) - AC_MSG_CHECKING([for fdatasync]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ fdatasync(0); ]])], From ba980fa90c3d42185ab2982899ed6e09cdebf4ba Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 14:36:57 +0000 Subject: [PATCH 018/146] Remove redundant checks for MSG_* from configure.ac --- configure.ac | 8 -------- src/compat.h | 10 +++++----- src/net.cpp | 5 +---- src/netbase.cpp | 5 +---- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/configure.ac b/configure.ac index c3c7d4935..ff98ae1eb 100644 --- a/configure.ac +++ b/configure.ac @@ -720,14 +720,6 @@ AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, #include #endif]) -dnl Check for MSG_NOSIGNAL -AC_MSG_CHECKING(for MSG_NOSIGNAL) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[ int f = MSG_NOSIGNAL; ]])], - [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MSG_NOSIGNAL, 1,[Define this symbol if you have MSG_NOSIGNAL]) ], - [ AC_MSG_RESULT(no)] -) - AC_MSG_CHECKING([for fdatasync]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ fdatasync(0); ]])], diff --git a/src/compat.h b/src/compat.h index 5cab5a8c7..8f5b5bb3d 100644 --- a/src/compat.h +++ b/src/compat.h @@ -76,11 +76,6 @@ typedef int32_t ssize_t; #endif #endif -// As Solaris does not have the MSG_NOSIGNAL flag for send(2) syscall, it is defined as 0 -#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL 0 -#endif - #ifndef WIN32 // PRIO_MAX is not defined on Solaris #ifndef PRIO_MAX @@ -104,4 +99,9 @@ bool static inline IsSelectableSocket(SOCKET s) { #endif } +// MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0 +#if !defined(MSG_NOSIGNAL) +#define MSG_NOSIGNAL 0 +#endif + #endif // BITCOIN_COMPAT_H diff --git a/src/net.cpp b/src/net.cpp index 13f3ebbd0..2f92b837d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -13,6 +13,7 @@ #include "addrman.h" #include "chainparams.h" #include "clientversion.h" +#include "compat.h" #include "primitives/transaction.h" #include "scheduler.h" #include "ui_interface.h" @@ -30,10 +31,6 @@ // Dump addresses to peers.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 -#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL 0 -#endif - using namespace std; namespace { diff --git a/src/netbase.cpp b/src/netbase.cpp index 0a2992cf3..4d2d47c73 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -9,6 +9,7 @@ #include "netbase.h" +#include "compat.h" #include "hash.h" #include "sync.h" #include "uint256.h" @@ -31,10 +32,6 @@ #include // for startswith() and endswith() #include -#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL) -#define MSG_NOSIGNAL 0 -#endif - // Settings static proxyType proxyInfo[NET_MAX]; static proxyType nameProxy; From 2da72cb6da86bb4f6d8bff4b3c229d8f4a8616fb Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 14:37:22 +0000 Subject: [PATCH 019/146] compat: remove strnlen back-compat code --- configure.ac | 2 -- src/Makefile.am | 1 - src/compat.h | 4 ---- src/compat/strnlen.cpp | 18 ------------------ 4 files changed, 25 deletions(-) delete mode 100644 src/compat/strnlen.cpp diff --git a/configure.ac b/configure.ac index ff98ae1eb..c78379014 100644 --- a/configure.ac +++ b/configure.ac @@ -706,8 +706,6 @@ AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h str AC_SEARCH_LIBS([getaddrinfo_a], [anl], [AC_DEFINE(HAVE_GETADDRINFO_A, 1, [Define this symbol if you have getaddrinfo_a])]) AC_SEARCH_LIBS([inet_pton], [nsl resolv], [AC_DEFINE(HAVE_INET_PTON, 1, [Define this symbol if you have inet_pton])]) -AC_CHECK_DECLS([strnlen]) - AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,, [#if HAVE_ENDIAN_H #include diff --git a/src/Makefile.am b/src/Makefile.am index b6f35658b..8b7e2d58a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -381,7 +381,6 @@ libbitcoin_util_a_SOURCES = \ clientversion.cpp \ compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ - compat/strnlen.cpp \ random.cpp \ rpc/protocol.cpp \ support/cleanse.cpp \ diff --git a/src/compat.h b/src/compat.h index 8f5b5bb3d..59e859af6 100644 --- a/src/compat.h +++ b/src/compat.h @@ -87,10 +87,6 @@ typedef int32_t ssize_t; #define THREAD_PRIORITY_ABOVE_NORMAL (-2) #endif -#if HAVE_DECL_STRNLEN == 0 -size_t strnlen( const char *start, size_t max_len); -#endif // HAVE_DECL_STRNLEN - bool static inline IsSelectableSocket(SOCKET s) { #ifdef WIN32 return true; diff --git a/src/compat/strnlen.cpp b/src/compat/strnlen.cpp deleted file mode 100644 index 1a56310af..000000000 --- a/src/compat/strnlen.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2009-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#if defined(HAVE_CONFIG_H) -#include "config/bitcoin-config.h" -#endif - -#include - -#if HAVE_DECL_STRNLEN == 0 -size_t strnlen( const char *start, size_t max_len) -{ - const char *end = (const char *)memchr(start, '\0', max_len); - - return end ? (size_t)(end - start) : max_len; -} -#endif // HAVE_DECL_STRNLEN From 0db264fb063a632454e345dbeaa6e57e0ae30495 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 15:46:48 +0000 Subject: [PATCH 020/146] Better SigCache Implementation --- src/Makefile.test.include | 1 + src/cuckoocache.h | 457 +++++++++++++++++++++++++++++++++ src/init.cpp | 2 + src/script/sigcache.cpp | 76 +++--- src/script/sigcache.h | 9 +- src/test/cuckoocache_tests.cpp | 394 ++++++++++++++++++++++++++++ src/test/test_bitcoin.cpp | 2 + 7 files changed, 901 insertions(+), 40 deletions(-) create mode 100644 src/cuckoocache.h create mode 100644 src/test/cuckoocache_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index bf83e7d26..3c3fe6bc4 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -67,6 +67,7 @@ BITCOIN_TESTS =\ test/compress_tests.cpp \ test/convertbits_tests.cpp \ test/crypto_tests.cpp \ + test/cuckoocache_tests.cpp \ test/DoS_tests.cpp \ test/equihash_tests.cpp \ test/getarg_tests.cpp \ diff --git a/src/cuckoocache.h b/src/cuckoocache.h new file mode 100644 index 000000000..efd6a820b --- /dev/null +++ b/src/cuckoocache.h @@ -0,0 +1,457 @@ +// Copyright (c) 2016 Jeremy Rubin +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef _BITCOIN_CUCKOOCACHE_H_ +#define _BITCOIN_CUCKOOCACHE_H_ + +#include +#include +#include +#include +#include +#include +#include + + +/** namespace CuckooCache provides high performance cache primitives + * + * Summary: + * + * 1) bit_packed_atomic_flags is bit-packed atomic flags for garbage collection + * + * 2) cache is a cache which is performant in memory usage and lookup speed. It + * is lockfree for erase operations. Elements are lazily erased on the next + * insert. + */ +namespace CuckooCache +{ +/** bit_packed_atomic_flags implements a container for garbage collection flags + * that is only thread unsafe on calls to setup. This class bit-packs collection + * flags for memory efficiency. + * + * All operations are std::memory_order_relaxed so external mechanisms must + * ensure that writes and reads are properly synchronized. + * + * On setup(n), all bits up to n are marked as collected. + * + * Under the hood, because it is an 8-bit type, it makes sense to use a multiple + * of 8 for setup, but it will be safe if that is not the case as well. + * + */ +class bit_packed_atomic_flags +{ + std::unique_ptr[]> mem; + +public: + /** No default constructor as there must be some size */ + bit_packed_atomic_flags() = delete; + + /** + * bit_packed_atomic_flags constructor creates memory to sufficiently + * keep track of garbage collection information for size entries. + * + * @param size the number of elements to allocate space for + * + * @post bit_set, bit_unset, and bit_is_set function properly forall x. x < + * size + * @post All calls to bit_is_set (without subsequent bit_unset) will return + * true. + */ + bit_packed_atomic_flags(uint32_t size) + { + // pad out the size if needed + size = (size + 7) / 8; + mem.reset(new std::atomic[size]); + for (uint32_t i = 0; i < size; ++i) + mem[i].store(0xFF); + }; + + /** setup marks all entries and ensures that bit_packed_atomic_flags can store + * at least size entries + * + * @param b the number of elements to allocate space for + * @post bit_set, bit_unset, and bit_is_set function properly forall x. x < + * b + * @post All calls to bit_is_set (without subsequent bit_unset) will return + * true. + */ + inline void setup(uint32_t b) + { + bit_packed_atomic_flags d(b); + std::swap(mem, d.mem); + } + + /** bit_set sets an entry as discardable. + * + * @param s the index of the entry to bit_set. + * @post immediately subsequent call (assuming proper external memory + * ordering) to bit_is_set(s) == true. + * + */ + inline void bit_set(uint32_t s) + { + mem[s >> 3].fetch_or(1 << (s & 7), std::memory_order_relaxed); + } + + /** bit_unset marks an entry as something that should not be overwritten + * + * @param s the index of the entry to bit_unset. + * @post immediately subsequent call (assuming proper external memory + * ordering) to bit_is_set(s) == false. + */ + inline void bit_unset(uint32_t s) + { + mem[s >> 3].fetch_and(~(1 << (s & 7)), std::memory_order_relaxed); + } + + /** bit_is_set queries the table for discardability at s + * + * @param s the index of the entry to read. + * @returns if the bit at index s was set. + * */ + inline bool bit_is_set(uint32_t s) const + { + return (1 << (s & 7)) & mem[s >> 3].load(std::memory_order_relaxed); + } +}; + +/** cache implements a cache with properties similar to a cuckoo-set + * + * The cache is able to hold up to (~(uint32_t)0) - 1 elements. + * + * Read Operations: + * - contains(*, false) + * + * Read+Erase Operations: + * - contains(*, true) + * + * Erase Operations: + * - allow_erase() + * + * Write Operations: + * - setup() + * - setup_bytes() + * - insert() + * - please_keep() + * + * Synchronization Free Operations: + * - invalid() + * - compute_hashes() + * + * User Must Guarantee: + * + * 1) Write Requires synchronized access (e.g., a lock) + * 2) Read Requires no concurrent Write, synchronized with the last insert. + * 3) Erase requires no concurrent Write, synchronized with last insert. + * 4) An Erase caller must release all memory before allowing a new Writer. + * + * + * Note on function names: + * - The name "allow_erase" is used because the real discard happens later. + * - The name "please_keep" is used because elements may be erased anyways on insert. + * + * @tparam Element should be a movable and copyable type + * @tparam Hash should be a function/callable which takes a template parameter + * hash_select and an Element and extracts a hash from it. Should return + * high-entropy hashes for `Hash h; h<0>(e) ... h<7>(e)`. + */ +template +class cache +{ +private: + /** table stores all the elements */ + std::vector table; + + /** size stores the total available slots in the hash table */ + uint32_t size; + + /** The bit_packed_atomic_flags array is marked mutable because we want + * garbage collection to be allowed to occur from const methods */ + mutable bit_packed_atomic_flags collection_flags; + + /** epoch_flags tracks how recently an element was inserted into + * the cache. true denotes recent, false denotes not-recent. See insert() + * method for full semantics. + */ + mutable std::vector epoch_flags; + + /** epoch_heuristic_counter is used to determine when a epoch might be aged + * & an expensive scan should be done. epoch_heuristic_counter is + * decremented on insert and reset to the new number of inserts which would + * cause the epoch to reach epoch_size when it reaches zero. + */ + uint32_t epoch_heuristic_counter; + + /** epoch_size is set to be the number of elements supposed to be in a + * epoch. When the number of non-erased elements in a epoch + * exceeds epoch_size, a new epoch should be started and all + * current entries demoted. epoch_size is set to be 45% of size because + * we want to keep load around 90%, and we support 3 epochs at once -- + * one "dead" which has been erased, one "dying" which has been marked to be + * erased next, and one "living" which new inserts add to. + */ + uint32_t epoch_size; + + /** hash_mask should be set to appropriately mask out a hash such that every + * masked hash is [0,size), eg, if floor(log2(size)) == 20, then hash_mask + * should be (1<<20)-1 + */ + uint32_t hash_mask; + + /** depth_limit determines how many elements insert should try to replace. + * Should be set to log2(n)*/ + uint8_t depth_limit; + + /** hash_function is a const instance of the hash function. It cannot be + * static or initialized at call time as it may have internal state (such as + * a nonce). + * */ + const Hash hash_function; + + /** compute_hashes is convenience for not having to write out this + * expression everywhere we use the hash values of an Element. + * + * @param e the element whose hashes will be returned + * @returns std::array of deterministic hashes derived from e + */ + inline std::array compute_hashes(const Element& e) const + { + return {{hash_function.template operator()<0>(e) & hash_mask, + hash_function.template operator()<1>(e) & hash_mask, + hash_function.template operator()<2>(e) & hash_mask, + hash_function.template operator()<3>(e) & hash_mask, + hash_function.template operator()<4>(e) & hash_mask, + hash_function.template operator()<5>(e) & hash_mask, + hash_function.template operator()<6>(e) & hash_mask, + hash_function.template operator()<7>(e) & hash_mask}}; + } + + /* end + * @returns a constexpr index that can never be inserted to */ + constexpr uint32_t invalid() const + { + return ~(uint32_t)0; + } + + /** allow_erase marks the element at index n as discardable. Threadsafe + * without any concurrent insert. + * @param n the index to allow erasure of + */ + inline void allow_erase(uint32_t n) const + { + collection_flags.bit_set(n); + } + + /** please_keep marks the element at index n as an entry that should be kept. + * Threadsafe without any concurrent insert. + * @param n the index to prioritize keeping + */ + inline void please_keep(uint32_t n) const + { + collection_flags.bit_unset(n); + } + + /** epoch_check handles the changing of epochs for elements stored in the + * cache. epoch_check should be run before every insert. + * + * First, epoch_check decrements and checks the cheap heuristic, and then does + * a more expensive scan if the cheap heuristic runs out. If the expensive + * scan suceeds, the epochs are aged and old elements are allow_erased. The + * cheap heuristic is reset to retrigger after the worst case growth of the + * current epoch's elements would exceed the epoch_size. + */ + void epoch_check() + { + if (epoch_heuristic_counter != 0) { + --epoch_heuristic_counter; + return; + } + // count the number of elements from the latest epoch which + // have not been erased. + uint32_t epoch_unused_count = 0; + for (uint32_t i = 0; i < size; ++i) + epoch_unused_count += epoch_flags[i] && + !collection_flags.bit_is_set(i); + // If there are more non-deleted entries in the current epoch than the + // epoch size, then allow_erase on all elements in the old epoch (marked + // false) and move all elements in the current epoch to the old epoch + // but do not call allow_erase on their indices. + if (epoch_unused_count >= epoch_size) { + for (uint32_t i = 0; i < size; ++i) + if (epoch_flags[i]) + epoch_flags[i] = false; + else + allow_erase(i); + epoch_heuristic_counter = epoch_size; + } else + // reset the epoch_heuristic_counter to next do a scan when worst + // case behavior (no intermittent erases) would exceed epoch size, + // with a reasonable minimum scan size. + // Ordinarily, we would have to sanity check std::min(epoch_size, + // epoch_unused_count), but we already know that `epoch_unused_count + // < epoch_size` in this branch + epoch_heuristic_counter = std::max(1u, std::max(epoch_size / 16, + epoch_size - epoch_unused_count)); + } + +public: + /** You must always construct a cache with some elements via a subsequent + * call to setup or setup_bytes, otherwise operations may segfault. + */ + cache() : table(), size(), collection_flags(0), epoch_flags(), + epoch_heuristic_counter(), epoch_size(), depth_limit(0), hash_function() + { + } + + /** setup initializes the container to store no more than new_size + * elements. setup rounds down to a power of two size. + * + * setup should only be called once. + * + * @param new_size the desired number of elements to store + * @returns the maximum number of elements storable + **/ + uint32_t setup(uint32_t new_size) + { + // depth_limit must be at least one otherwise errors can occur. + depth_limit = static_cast(std::log2(static_cast(std::max((uint32_t)2, new_size)))); + size = 1 << depth_limit; + hash_mask = size-1; + table.resize(size); + collection_flags.setup(size); + epoch_flags.resize(size); + // Set to 45% as described above + epoch_size = std::max((uint32_t)1, (45 * size) / 100); + // Initially set to wait for a whole epoch + epoch_heuristic_counter = epoch_size; + return size; + } + + /** setup_bytes is a convenience function which accounts for internal memory + * usage when deciding how many elements to store. It isn't perfect because + * it doesn't account for any overhead (struct size, MallocUsage, collection + * and epoch flags). This was done to simplify selecting a power of two + * size. In the expected use case, an extra two bits per entry should be + * negligible compared to the size of the elements. + * + * @param bytes the approximate number of bytes to use for this data + * structure. + * @returns the maximum number of elements storable (see setup() + * documentation for more detail) + */ + uint32_t setup_bytes(size_t bytes) + { + return setup(bytes/sizeof(Element)); + } + + /** insert loops at most depth_limit times trying to insert a hash + * at various locations in the table via a variant of the Cuckoo Algorithm + * with eight hash locations. + * + * It drops the last tried element if it runs out of depth before + * encountering an open slot. + * + * Thus + * + * insert(x); + * return contains(x, false); + * + * is not guaranteed to return true. + * + * @param e the element to insert + * @post one of the following: All previously inserted elements and e are + * now in the table, one previously inserted element is evicted from the + * table, the entry attempted to be inserted is evicted. + * + */ + inline void insert(Element e) + { + epoch_check(); + uint32_t last_loc = invalid(); + bool last_epoch = true; + std::array locs = compute_hashes(e); + // Make sure we have not already inserted this element + // If we have, make sure that it does not get deleted + for (uint32_t loc : locs) + if (table[loc] == e) { + please_keep(loc); + epoch_flags[loc] = last_epoch; + return; + } + for (uint8_t depth = 0; depth < depth_limit; ++depth) { + // First try to insert to an empty slot, if one exists + for (uint32_t loc : locs) { + if (!collection_flags.bit_is_set(loc)) + continue; + table[loc] = std::move(e); + please_keep(loc); + epoch_flags[loc] = last_epoch; + return; + } + /** Swap with the element at the location that was + * not the last one looked at. Example: + * + * 1) On first iteration, last_loc == invalid(), find returns last, so + * last_loc defaults to locs[0]. + * 2) On further iterations, where last_loc == locs[k], last_loc will + * go to locs[k+1 % 8], i.e., next of the 8 indicies wrapping around + * to 0 if needed. + * + * This prevents moving the element we just put in. + * + * The swap is not a move -- we must switch onto the evicted element + * for the next iteration. + */ + last_loc = locs[(1 + (std::find(locs.begin(), locs.end(), last_loc) - locs.begin())) & 7]; + std::swap(table[last_loc], e); + // Can't std::swap a std::vector::reference and a bool&. + bool epoch = last_epoch; + last_epoch = epoch_flags[last_loc]; + epoch_flags[last_loc] = epoch; + + // Recompute the locs -- unfortunately happens one too many times! + locs = compute_hashes(e); + } + } + + /* contains iterates through the hash locations for a given element + * and checks to see if it is present. + * + * contains does not check garbage collected state (in other words, + * garbage is only collected when the space is needed), so: + * + * insert(x); + * if (contains(x, true)) + * return contains(x, false); + * else + * return true; + * + * executed on a single thread will always return true! + * + * This is a great property for re-org performance for example. + * + * contains returns a bool set true if the element was found. + * + * @param e the element to check + * @param erase + * + * @post if erase is true and the element is found, then the garbage collect + * flag is set + * @returns true if the element is found, false otherwise + */ + inline bool contains(const Element& e, const bool erase) const + { + std::array locs = compute_hashes(e); + for (uint32_t loc : locs) + if (table[loc] == e) { + if (erase) + allow_erase(loc); + return true; + } + return false; + } +}; +} // namespace CuckooCache + +#endif diff --git a/src/init.cpp b/src/init.cpp index 1f87870b0..889ad6413 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1217,6 +1217,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD); std::ostringstream strErrors; + InitSignatureCache(); + LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { for (int i=0; i -#include namespace { /** * We're hashing a nonce into the entries themselves, so we don't need extra * blinding in the set hash computation. + * + * This may exhibit platform endian dependent behavior but because these are + * nonced hashes (random) and this state is only ever used locally it is safe. + * All that matters is local consistency. */ -class CSignatureCacheHasher +class SignatureCacheHasher { public: - size_t operator()(const uint256& key) const { - return key.GetCheapHash(); + template + uint32_t operator()(const uint256& key) const + { + static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); + uint32_t u; + std::memcpy(&u, key.begin()+4*hash_select, 4); + return u; } }; @@ -38,11 +47,10 @@ class CSignatureCache private: //! Entries are SHA256(nonce || signature hash || public key || signature): uint256 nonce; - typedef boost::unordered_set map_type; + typedef CuckooCache::cache map_type; map_type setValid; boost::shared_mutex cs_sigcache; - public: CSignatureCache() { @@ -56,58 +64,52 @@ class CSignatureCache } bool - Get(const uint256& entry) + Get(const uint256& entry, const bool erase) { boost::shared_lock lock(cs_sigcache); - return setValid.count(entry); + return setValid.contains(entry, erase); } - void Erase(const uint256& entry) + void Set(uint256& entry) { boost::unique_lock lock(cs_sigcache); - setValid.erase(entry); + setValid.insert(entry); } - void Set(const uint256& entry) + uint32_t setup_bytes(size_t n) { - size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); - if (nMaxCacheSize <= 0) return; - - boost::unique_lock lock(cs_sigcache); - while (memusage::DynamicUsage(setValid) > nMaxCacheSize) - { - map_type::size_type s = GetRand(setValid.bucket_count()); - map_type::local_iterator it = setValid.begin(s); - if (it != setValid.end(s)) { - setValid.erase(*it); - } - } - - setValid.insert(entry); + return setValid.setup_bytes(n); } }; +/* In previous versions of this code, signatureCache was a local static variable + * in CachingTransactionSignatureChecker::VerifySignature. We initialize + * signatureCache outside of VerifySignature to avoid the atomic operation per + * call overhead associated with local static variables even though + * signatureCache could be made local to VerifySignature. +*/ +static CSignatureCache signatureCache; } -bool CachingTransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const +// To be called once in AppInit2/TestingSetup to initialize the signatureCache +void InitSignatureCache() { - static CSignatureCache signatureCache; + size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); + if (nMaxCacheSize <= 0) return; + size_t nElems = signatureCache.setup_bytes(nMaxCacheSize); + LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n", + (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems); +} +bool CachingTransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const +{ uint256 entry; signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey); - - if (signatureCache.Get(entry)) { - if (!store) { - signatureCache.Erase(entry); - } + if (signatureCache.Get(entry, !store)) return true; - } - if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash)) return false; - - if (store) { + if (store) signatureCache.Set(entry); - } return true; } diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 28de26751..02d48ac2a 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -10,9 +10,10 @@ #include -// DoS prevention: limit cache size to less than 40MB (over 500000 -// entries on 64-bit systems). -static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 40; +// DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit +// systems). Due to how we count cache size, actual memory usage is slightly +// more (~32.25 MB) +static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 32; class CPubKey; @@ -27,4 +28,6 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; +void InitSignatureCache(); + #endif // BITCOIN_SCRIPT_SIGCACHE_H diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp new file mode 100644 index 000000000..4d6d6d621 --- /dev/null +++ b/src/test/cuckoocache_tests.cpp @@ -0,0 +1,394 @@ +// Copyright (c) 2012-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include "cuckoocache.h" +#include "test/test_bitcoin.h" +#include "random.h" +#include +#include + + +/** Test Suite for CuckooCache + * + * 1) All tests should have a deterministic result (using insecure rand + * with deterministic seeds) + * 2) Some test methods are templated to allow for easier testing + * against new versions / comparing + * 3) Results should be treated as a regression test, i.e., did the behavior + * change significantly from what was expected. This can be OK, depending on + * the nature of the change, but requires updating the tests to reflect the new + * expected behavior. For example improving the hit rate may cause some tests + * using BOOST_CHECK_CLOSE to fail. + * + */ +FastRandomContext insecure_rand(true); + +BOOST_AUTO_TEST_SUITE(cuckoocache_tests); + + +/** insecure_GetRandHash fills in a uint256 from insecure_rand + */ +void insecure_GetRandHash(uint256& t) +{ + uint32_t* ptr = (uint32_t*)t.begin(); + for (uint8_t j = 0; j < 8; ++j) + *(ptr++) = insecure_rand.rand32(); +} + +/** Definition copied from /src/script/sigcache.cpp + */ +class uint256Hasher +{ +public: + template + uint32_t operator()(const uint256& key) const + { + static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); + uint32_t u; + std::memcpy(&u, key.begin() + 4 * hash_select, 4); + return u; + } +}; + + +/* Test that no values not inserted into the cache are read out of it. + * + * There are no repeats in the first 200000 insecure_GetRandHash calls + */ +BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes) +{ + insecure_rand = FastRandomContext(true); + CuckooCache::cache cc{}; + cc.setup_bytes(32 << 20); + uint256 v; + for (int x = 0; x < 100000; ++x) { + insecure_GetRandHash(v); + cc.insert(v); + } + for (int x = 0; x < 100000; ++x) { + insecure_GetRandHash(v); + BOOST_CHECK(!cc.contains(v, false)); + } +}; + +/** This helper returns the hit rate when megabytes*load worth of entries are + * inserted into a megabytes sized cache + */ +template +double test_cache(size_t megabytes, double load) +{ + insecure_rand = FastRandomContext(true); + std::vector hashes; + Cache set{}; + size_t bytes = megabytes * (1 << 20); + set.setup_bytes(bytes); + uint32_t n_insert = static_cast(load * (bytes / sizeof(uint256))); + hashes.resize(n_insert); + for (uint32_t i = 0; i < n_insert; ++i) { + uint32_t* ptr = (uint32_t*)hashes[i].begin(); + for (uint8_t j = 0; j < 8; ++j) + *(ptr++) = insecure_rand.rand32(); + } + /** We make a copy of the hashes because future optimizations of the + * cuckoocache may overwrite the inserted element, so the test is + * "future proofed". + */ + std::vector hashes_insert_copy = hashes; + /** Do the insert */ + for (uint256& h : hashes_insert_copy) + set.insert(h); + /** Count the hits */ + uint32_t count = 0; + for (uint256& h : hashes) + count += set.contains(h, false); + double hit_rate = ((double)count) / ((double)n_insert); + return hit_rate; +} + +/** The normalized hit rate for a given load. + * + * The semantics are a little confusing, so please see the below + * explanation. + * + * Examples: + * + * 1) at load 0.5, we expect a perfect hit rate, so we multiply by + * 1.0 + * 2) at load 2.0, we expect to see half the entries, so a perfect hit rate + * would be 0.5. Therefore, if we see a hit rate of 0.4, 0.4*2.0 = 0.8 is the + * normalized hit rate. + * + * This is basically the right semantics, but has a bit of a glitch depending on + * how you measure around load 1.0 as after load 1.0 your normalized hit rate + * becomes effectively perfect, ignoring freshness. + */ +double normalize_hit_rate(double hits, double load) +{ + return hits * std::max(load, 1.0); +} + +/** Check the hit rate on loads ranging from 0.1 to 2.0 */ +BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok) +{ + /** Arbitrarily selected Hit Rate threshold that happens to work for this test + * as a lower bound on performance. + */ + double HitRateThresh = 0.98; + size_t megabytes = 32; + for (double load = 0.1; load < 2; load *= 2) { + double hits = test_cache>(megabytes, load); + BOOST_CHECK(normalize_hit_rate(hits, load) > HitRateThresh); + } +} + + +/** This helper checks that erased elements are preferentially inserted onto and + * that the hit rate of "fresher" keys is reasonable*/ +template +void test_cache_erase(size_t megabytes) +{ + double load = 1; + insecure_rand = FastRandomContext(true); + std::vector hashes; + Cache set{}; + size_t bytes = megabytes * (1 << 20); + set.setup_bytes(bytes); + uint32_t n_insert = static_cast(load * (bytes / sizeof(uint256))); + hashes.resize(n_insert); + for (uint32_t i = 0; i < n_insert; ++i) { + uint32_t* ptr = (uint32_t*)hashes[i].begin(); + for (uint8_t j = 0; j < 8; ++j) + *(ptr++) = insecure_rand.rand32(); + } + /** We make a copy of the hashes because future optimizations of the + * cuckoocache may overwrite the inserted element, so the test is + * "future proofed". + */ + std::vector hashes_insert_copy = hashes; + + /** Insert the first half */ + for (uint32_t i = 0; i < (n_insert / 2); ++i) + set.insert(hashes_insert_copy[i]); + /** Erase the first quarter */ + for (uint32_t i = 0; i < (n_insert / 4); ++i) + set.contains(hashes[i], true); + /** Insert the second half */ + for (uint32_t i = (n_insert / 2); i < n_insert; ++i) + set.insert(hashes_insert_copy[i]); + + /** elements that we marked erased but that are still there */ + size_t count_erased_but_contained = 0; + /** elements that we did not erase but are older */ + size_t count_stale = 0; + /** elements that were most recently inserted */ + size_t count_fresh = 0; + + for (uint32_t i = 0; i < (n_insert / 4); ++i) + count_erased_but_contained += set.contains(hashes[i], false); + for (uint32_t i = (n_insert / 4); i < (n_insert / 2); ++i) + count_stale += set.contains(hashes[i], false); + for (uint32_t i = (n_insert / 2); i < n_insert; ++i) + count_fresh += set.contains(hashes[i], false); + + double hit_rate_erased_but_contained = double(count_erased_but_contained) / (double(n_insert) / 4.0); + double hit_rate_stale = double(count_stale) / (double(n_insert) / 4.0); + double hit_rate_fresh = double(count_fresh) / (double(n_insert) / 2.0); + + // Check that our hit_rate_fresh is perfect + BOOST_CHECK_EQUAL(hit_rate_fresh, 1.0); + // Check that we have a more than 2x better hit rate on stale elements than + // erased elements. + BOOST_CHECK(hit_rate_stale > 2 * hit_rate_erased_but_contained); +} + +BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok) +{ + size_t megabytes = 32; + test_cache_erase>(megabytes); +} + +template +void test_cache_erase_parallel(size_t megabytes) +{ + double load = 1; + insecure_rand = FastRandomContext(true); + std::vector hashes; + Cache set{}; + size_t bytes = megabytes * (1 << 20); + set.setup_bytes(bytes); + uint32_t n_insert = static_cast(load * (bytes / sizeof(uint256))); + hashes.resize(n_insert); + for (uint32_t i = 0; i < n_insert; ++i) { + uint32_t* ptr = (uint32_t*)hashes[i].begin(); + for (uint8_t j = 0; j < 8; ++j) + *(ptr++) = insecure_rand.rand32(); + } + /** We make a copy of the hashes because future optimizations of the + * cuckoocache may overwrite the inserted element, so the test is + * "future proofed". + */ + std::vector hashes_insert_copy = hashes; + boost::shared_mutex mtx; + + { + /** Grab lock to make sure we release inserts */ + boost::unique_lock l(mtx); + /** Insert the first half */ + for (uint32_t i = 0; i < (n_insert / 2); ++i) + set.insert(hashes_insert_copy[i]); + } + + /** Spin up 3 threads to run contains with erase. + */ + std::vector threads; + /** Erase the first quarter */ + for (uint32_t x = 0; x < 3; ++x) + /** Each thread is emplaced with x copy-by-value + */ + threads.emplace_back([&, x] { + boost::shared_lock l(mtx); + size_t ntodo = (n_insert/4)/3; + size_t start = ntodo*x; + size_t end = ntodo*(x+1); + for (uint32_t i = start; i < end; ++i) + set.contains(hashes[i], true); + }); + + /** Wait for all threads to finish + */ + for (std::thread& t : threads) + t.join(); + /** Grab lock to make sure we observe erases */ + boost::unique_lock l(mtx); + /** Insert the second half */ + for (uint32_t i = (n_insert / 2); i < n_insert; ++i) + set.insert(hashes_insert_copy[i]); + + /** elements that we marked erased but that are still there */ + size_t count_erased_but_contained = 0; + /** elements that we did not erase but are older */ + size_t count_stale = 0; + /** elements that were most recently inserted */ + size_t count_fresh = 0; + + for (uint32_t i = 0; i < (n_insert / 4); ++i) + count_erased_but_contained += set.contains(hashes[i], false); + for (uint32_t i = (n_insert / 4); i < (n_insert / 2); ++i) + count_stale += set.contains(hashes[i], false); + for (uint32_t i = (n_insert / 2); i < n_insert; ++i) + count_fresh += set.contains(hashes[i], false); + + double hit_rate_erased_but_contained = double(count_erased_but_contained) / (double(n_insert) / 4.0); + double hit_rate_stale = double(count_stale) / (double(n_insert) / 4.0); + double hit_rate_fresh = double(count_fresh) / (double(n_insert) / 2.0); + + // Check that our hit_rate_fresh is perfect + BOOST_CHECK_EQUAL(hit_rate_fresh, 1.0); + // Check that we have a more than 2x better hit rate on stale elements than + // erased elements. + BOOST_CHECK(hit_rate_stale > 2 * hit_rate_erased_but_contained); +} +BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok) +{ + size_t megabytes = 32; + test_cache_erase_parallel>(megabytes); +} + + +template +void test_cache_generations() +{ + // This test checks that for a simulation of network activity, the fresh hit + // rate is never below 99%, and the number of times that it is worse than + // 99.9% are less than 1% of the time. + double min_hit_rate = 0.99; + double tight_hit_rate = 0.999; + double max_rate_less_than_tight_hit_rate = 0.01; + // A cache that meets this specification is therefore shown to have a hit + // rate of at least tight_hit_rate * (1 - max_rate_less_than_tight_hit_rate) + + // min_hit_rate*max_rate_less_than_tight_hit_rate = 0.999*99%+0.99*1% == 99.89% + // hit rate with low variance. + + // We use deterministic values, but this test has also passed on many + // iterations with non-deterministic values, so it isn't "overfit" to the + // specific entropy in FastRandomContext(true) and implementation of the + // cache. + insecure_rand = FastRandomContext(true); + + // block_activity models a chunk of network activity. n_insert elements are + // adde to the cache. The first and last n/4 are stored for removal later + // and the middle n/2 are not stored. This models a network which uses half + // the signatures of recently (since the last block) added transactions + // immediately and never uses the other half. + struct block_activity { + std::vector reads; + block_activity(uint32_t n_insert, Cache& c) : reads() + { + std::vector inserts; + inserts.resize(n_insert); + reads.reserve(n_insert / 2); + for (uint32_t i = 0; i < n_insert; ++i) { + uint32_t* ptr = (uint32_t*)inserts[i].begin(); + for (uint8_t j = 0; j < 8; ++j) + *(ptr++) = insecure_rand.rand32(); + } + for (uint32_t i = 0; i < n_insert / 4; ++i) + reads.push_back(inserts[i]); + for (uint32_t i = n_insert - (n_insert / 4); i < n_insert; ++i) + reads.push_back(inserts[i]); + for (auto h : inserts) + c.insert(h); + } + }; + + const uint32_t BLOCK_SIZE = 10000; + // We expect window size 60 to perform reasonably given that each epoch + // stores 45% of the cache size (~472k). + const uint32_t WINDOW_SIZE = 60; + const uint32_t POP_AMOUNT = (BLOCK_SIZE / WINDOW_SIZE) / 2; + const double load = 10; + const size_t megabytes = 32; + const size_t bytes = megabytes * (1 << 20); + const uint32_t n_insert = static_cast(load * (bytes / sizeof(uint256))); + + std::vector hashes; + Cache set{}; + set.setup_bytes(bytes); + hashes.reserve(n_insert / BLOCK_SIZE); + std::deque last_few; + uint32_t out_of_tight_tolerance = 0; + uint32_t total = n_insert / BLOCK_SIZE; + // we use the deque last_few to model a sliding window of blocks. at each + // step, each of the last WINDOW_SIZE block_activities checks the cache for + // POP_AMOUNT of the hashes that they inserted, and marks these erased. + for (uint32_t i = 0; i < total; ++i) { + if (last_few.size() == WINDOW_SIZE) + last_few.pop_front(); + last_few.emplace_back(BLOCK_SIZE, set); + uint32_t count = 0; + for (auto& act : last_few) + for (uint32_t k = 0; k < POP_AMOUNT; ++k) { + count += set.contains(act.reads.back(), true); + act.reads.pop_back(); + } + // We use last_few.size() rather than WINDOW_SIZE for the correct + // behavior on the first WINDOW_SIZE iterations where the deque is not + // full yet. + double hit = (double(count)) / (last_few.size() * POP_AMOUNT); + // Loose Check that hit rate is above min_hit_rate + BOOST_CHECK(hit > min_hit_rate); + // Tighter check, count number of times we are less than tight_hit_rate + // (and implicitly, greater than min_hit_rate) + out_of_tight_tolerance += hit < tight_hit_rate; + } + // Check that being out of tolerance happens less than + // max_rate_less_than_tight_hit_rate of the time + BOOST_CHECK(double(out_of_tight_tolerance) / double(total) < max_rate_less_than_tight_hit_rate); +} +BOOST_AUTO_TEST_CASE(cuckoocache_generations) +{ + test_cache_generations>(); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index b4e5a2aa4..fa653a59a 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -24,6 +24,7 @@ #include "ui_interface.h" #include "rpc/server.h" #include "rpc/register.h" +#include "script/sigcache.h" #include #include @@ -79,6 +80,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) ECC_Start(); SetupEnvironment(); SetupNetworking(); + InitSignatureCache(); fPrintToDebugLog = false; // don't want to write to debug.log file fCheckBlockIndex = true; SelectParams(chainName); From 922e260ccecee32253fc6b2635cfcfa70a5d05c9 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 15:53:01 +0000 Subject: [PATCH 021/146] Speed Up CuckooCache tests --- src/test/cuckoocache_tests.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index 4d6d6d621..ccd715562 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -60,7 +60,8 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes) { insecure_rand = FastRandomContext(true); CuckooCache::cache cc{}; - cc.setup_bytes(32 << 20); + size_t megabytes = 4; + cc.setup_bytes(megabytes << 20); uint256 v; for (int x = 0; x < 100000; ++x) { insecure_GetRandHash(v); @@ -135,7 +136,7 @@ BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok) * as a lower bound on performance. */ double HitRateThresh = 0.98; - size_t megabytes = 32; + size_t megabytes = 4; for (double load = 0.1; load < 2; load *= 2) { double hits = test_cache>(megabytes, load); BOOST_CHECK(normalize_hit_rate(hits, load) > HitRateThresh); @@ -204,7 +205,7 @@ void test_cache_erase(size_t megabytes) BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok) { - size_t megabytes = 32; + size_t megabytes = 4; test_cache_erase>(megabytes); } @@ -291,7 +292,7 @@ void test_cache_erase_parallel(size_t megabytes) } BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok) { - size_t megabytes = 32; + size_t megabytes = 4; test_cache_erase_parallel>(megabytes); } @@ -342,13 +343,13 @@ void test_cache_generations() } }; - const uint32_t BLOCK_SIZE = 10000; + const uint32_t BLOCK_SIZE = 1000; // We expect window size 60 to perform reasonably given that each epoch // stores 45% of the cache size (~472k). const uint32_t WINDOW_SIZE = 60; const uint32_t POP_AMOUNT = (BLOCK_SIZE / WINDOW_SIZE) / 2; const double load = 10; - const size_t megabytes = 32; + const size_t megabytes = 4; const size_t bytes = megabytes * (1 << 20); const uint32_t n_insert = static_cast(load * (bytes / sizeof(uint256))); From 568c137efe5ba94653be33fe2d9ad6d2cd6e00ba Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 15:55:04 +0000 Subject: [PATCH 022/146] De-duplicate SignatureCacheHasher --- src/script/sigcache.cpp | 21 --------------------- src/script/sigcache.h | 21 +++++++++++++++++++++ src/test/cuckoocache_tests.cpp | 26 ++++++-------------------- 3 files changed, 27 insertions(+), 41 deletions(-) diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 8e72e0afe..89b518156 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -16,27 +16,6 @@ namespace { -/** - * We're hashing a nonce into the entries themselves, so we don't need extra - * blinding in the set hash computation. - * - * This may exhibit platform endian dependent behavior but because these are - * nonced hashes (random) and this state is only ever used locally it is safe. - * All that matters is local consistency. - */ -class SignatureCacheHasher -{ -public: - template - uint32_t operator()(const uint256& key) const - { - static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); - uint32_t u; - std::memcpy(&u, key.begin()+4*hash_select, 4); - return u; - } -}; - /** * Valid signature cache, to avoid doing expensive ECDSA signature checking * twice for every transaction (once when accepted into memory pool, and diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 02d48ac2a..1bbb9dc15 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -17,6 +17,27 @@ static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 32; class CPubKey; +/** + * We're hashing a nonce into the entries themselves, so we don't need extra + * blinding in the set hash computation. + * + * This may exhibit platform endian dependent behavior but because these are + * nonced hashes (random) and this state is only ever used locally it is safe. + * All that matters is local consistency. + */ +class SignatureCacheHasher +{ +public: + template + uint32_t operator()(const uint256& key) const + { + static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); + uint32_t u; + std::memcpy(&u, key.begin()+4*hash_select, 4); + return u; + } +}; + class CachingTransactionSignatureChecker : public TransactionSignatureChecker { private: diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index ccd715562..8636fb79c 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include "cuckoocache.h" +#include "script/sigcache.h" #include "test/test_bitcoin.h" #include "random.h" #include @@ -36,21 +37,6 @@ void insecure_GetRandHash(uint256& t) *(ptr++) = insecure_rand.rand32(); } -/** Definition copied from /src/script/sigcache.cpp - */ -class uint256Hasher -{ -public: - template - uint32_t operator()(const uint256& key) const - { - static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); - uint32_t u; - std::memcpy(&u, key.begin() + 4 * hash_select, 4); - return u; - } -}; - /* Test that no values not inserted into the cache are read out of it. * @@ -59,7 +45,7 @@ class uint256Hasher BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes) { insecure_rand = FastRandomContext(true); - CuckooCache::cache cc{}; + CuckooCache::cache cc{}; size_t megabytes = 4; cc.setup_bytes(megabytes << 20); uint256 v; @@ -138,7 +124,7 @@ BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok) double HitRateThresh = 0.98; size_t megabytes = 4; for (double load = 0.1; load < 2; load *= 2) { - double hits = test_cache>(megabytes, load); + double hits = test_cache>(megabytes, load); BOOST_CHECK(normalize_hit_rate(hits, load) > HitRateThresh); } } @@ -206,7 +192,7 @@ void test_cache_erase(size_t megabytes) BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok) { size_t megabytes = 4; - test_cache_erase>(megabytes); + test_cache_erase>(megabytes); } template @@ -293,7 +279,7 @@ void test_cache_erase_parallel(size_t megabytes) BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok) { size_t megabytes = 4; - test_cache_erase_parallel>(megabytes); + test_cache_erase_parallel>(megabytes); } @@ -389,7 +375,7 @@ void test_cache_generations() } BOOST_AUTO_TEST_CASE(cuckoocache_generations) { - test_cache_generations>(); + test_cache_generations>(); } BOOST_AUTO_TEST_SUITE_END(); From dbdd09d9d73c02e2fc24a5bbdb56385694d8c830 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 16:14:49 +0000 Subject: [PATCH 023/146] Use std::unordered_{map,set} (C++11) instead of boost::unordered_{map,set} --- src/coins.cpp | 2 +- src/coins.h | 10 +++++----- src/main.h | 4 +--- src/memusage.h | 17 +++++++---------- src/transaction_builder.cpp | 2 +- src/txmempool.cpp | 2 +- src/wallet/asyncrpcoperation_mergetoaddress.cpp | 2 +- src/wallet/asyncrpcoperation_sendmany.cpp | 2 +- 8 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/coins.cpp b/src/coins.cpp index a4be3d6bd..a95a69cad 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -564,7 +564,7 @@ CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const bool CCoinsViewCache::HaveShieldedRequirements(const CTransaction& tx) const { - boost::unordered_map intermediates; + std::unordered_map intermediates; for (const JSDescription &joinsplit : tx.vJoinSplit) { diff --git a/src/coins.h b/src/coins.h index 08efd020d..0a8e8294e 100644 --- a/src/coins.h +++ b/src/coins.h @@ -14,8 +14,8 @@ #include #include +#include -#include #include "zcash/IncrementalMerkleTree.hpp" /** @@ -316,10 +316,10 @@ enum ShieldedType SAPLING, }; -typedef boost::unordered_map CCoinsMap; -typedef boost::unordered_map CAnchorsSproutMap; -typedef boost::unordered_map CAnchorsSaplingMap; -typedef boost::unordered_map CNullifiersMap; +typedef std::unordered_map CCoinsMap; +typedef std::unordered_map CAnchorsSproutMap; +typedef std::unordered_map CAnchorsSaplingMap; +typedef std::unordered_map CNullifiersMap; struct CCoinsStats { diff --git a/src/main.h b/src/main.h index 4ed2f4b7c..430961ad5 100644 --- a/src/main.h +++ b/src/main.h @@ -40,8 +40,6 @@ #include #include -#include - class CBlockIndex; class CBlockTreeDB; class CBloomFilter; @@ -134,7 +132,7 @@ extern std::optional expiryDeltaArg; extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern CTxMemPool mempool; -typedef boost::unordered_map BlockMap; +typedef std::unordered_map BlockMap; extern BlockMap mapBlockIndex; extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; diff --git a/src/memusage.h b/src/memusage.h index c88589f22..574bce17a 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -10,9 +10,8 @@ #include #include #include - -#include -#include +#include +#include namespace memusage { @@ -93,25 +92,23 @@ static inline size_t DynamicUsage(const std::map& m) return MallocUsage(sizeof(stl_tree_node >)) * m.size(); } -// Boost data structures - template -struct boost_unordered_node : private X +struct unordered_node : private X { private: void* ptr; }; template -static inline size_t DynamicUsage(const boost::unordered_set& s) +static inline size_t DynamicUsage(const std::unordered_set& s) { - return MallocUsage(sizeof(boost_unordered_node)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count()); + return MallocUsage(sizeof(unordered_node)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count()); } template -static inline size_t DynamicUsage(const boost::unordered_map& m) +static inline size_t DynamicUsage(const std::unordered_map& m) { - return MallocUsage(sizeof(boost_unordered_node >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count()); + return MallocUsage(sizeof(unordered_node >)) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count()); } } diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index c60cc98c6..d3a75c2de 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -496,7 +496,7 @@ void TransactionBuilder::CreateJSDescriptions() CAmount vpubNewTarget = valueOut > 0 ? valueOut : 0; // Keep track of treestate within this transaction - boost::unordered_map intermediates; + std::unordered_map intermediates; std::vector previousCommitments; while (!vpubNewProcessed) { diff --git a/src/txmempool.cpp b/src/txmempool.cpp index b72ee3fbd..75a5113ec 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -530,7 +530,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const i++; } - boost::unordered_map intermediates; + std::unordered_map intermediates; for (const JSDescription &joinsplit : tx.vJoinSplit) { for (const uint256 &nf : joinsplit.nullifiers) { diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index 0a3ad6204..019276abd 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -481,7 +481,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } // Keep track of treestate within this transaction - boost::unordered_map intermediates; + std::unordered_map intermediates; std::vector previousCommitments; while (!vpubNewProcessed) { diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index ed62772f5..410b7d768 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -638,7 +638,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { } // Keep track of treestate within this transaction - boost::unordered_map intermediates; + std::unordered_map intermediates; std::vector previousCommitments; while (!vpubNewProcessed) { From 49adf9c0a1f8fcd9169c8b6731ca367b1f9ed187 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 17:26:51 +0000 Subject: [PATCH 024/146] Lightweight abstraction of boost::filesystem --- src/Makefile.am | 2 + src/bench/bench_bitcoin.cpp | 10 +- src/bitcoin-cli.cpp | 4 +- src/bitcoind.cpp | 4 +- src/dbwrapper.cpp | 5 +- src/dbwrapper.h | 5 +- src/fs.cpp | 19 ++++ src/fs.h | 24 +++++ src/gtest/main.cpp | 8 +- src/gtest/test_deprecation.cpp | 10 +- src/gtest/test_foundersreward.cpp | 6 +- src/init.cpp | 60 ++++++------ src/main.cpp | 18 ++-- src/main.h | 3 +- src/net.cpp | 10 +- src/net.h | 4 +- src/rpc/protocol.cpp | 12 +-- src/rpc/protocol.h | 5 +- src/rpc/server.cpp | 2 +- src/test/alert_tests.cpp | 11 +-- src/test/checkblock_tests.cpp | 6 +- src/test/dbwrapper_tests.cpp | 2 +- src/test/streams_tests.cpp | 6 +- src/test/test_bitcoin.cpp | 20 ++-- src/test/test_bitcoin.h | 6 +- src/torcontrol.cpp | 20 ++-- src/util.cpp | 102 +++++++------------- src/util.h | 23 +++-- src/wallet/db.cpp | 12 +-- src/wallet/db.h | 6 +- src/wallet/gtest/test_paymentdisclosure.cpp | 8 +- src/wallet/gtest/test_wallet.cpp | 7 +- src/wallet/gtest/test_wallet_zkeys.cpp | 19 ++-- src/wallet/paymentdisclosuredb.cpp | 9 +- src/wallet/paymentdisclosuredb.h | 2 +- src/wallet/rpcdump.cpp | 6 +- src/wallet/rpcwallet.cpp | 4 +- src/wallet/test/rpc_wallet_tests.cpp | 16 +-- src/wallet/wallet.cpp | 14 +-- src/wallet/walletdb.cpp | 12 +-- src/zcbenchmarks.cpp | 3 +- 41 files changed, 262 insertions(+), 263 deletions(-) create mode 100644 src/fs.cpp create mode 100644 src/fs.h diff --git a/src/Makefile.am b/src/Makefile.am index 8b7e2d58a..c1a596acd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -125,6 +125,7 @@ BITCOIN_CORE_H = \ core_memusage.h \ deprecation.h \ experimental_features.h \ + fs.h \ hash.h \ httprpc.h \ httpserver.h \ @@ -381,6 +382,7 @@ libbitcoin_util_a_SOURCES = \ clientversion.cpp \ compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ + fs.cpp \ random.cpp \ rpc/protocol.cpp \ support/cleanse.cpp \ diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 0e5c5d88f..93ae4d2f1 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -9,8 +9,6 @@ #include "util.h" #include "zcash/JoinSplit.hpp" -#include - #include "librustzcash.h" const std::function G_TRANSLATION_FUN = nullptr; @@ -23,12 +21,12 @@ main(int argc, char** argv) SetupEnvironment(); fPrintToDebugLog = false; // don't want to write to debug.log file - boost::filesystem::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; - boost::filesystem::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; - boost::filesystem::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; + fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; + fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; + fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; static_assert( - sizeof(boost::filesystem::path::value_type) == sizeof(codeunit), + sizeof(fs::path::value_type) == sizeof(codeunit), "librustzcash not configured correctly"); auto sapling_spend_str = sapling_spend.native(); auto sapling_output_str = sapling_output.native(); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 1425bc6d0..db9dcde1b 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -5,12 +5,12 @@ #include "chainparamsbase.h" #include "clientversion.h" +#include "fs.h" #include "rpc/client.h" #include "rpc/protocol.h" #include "util.h" #include "utilstrencodings.h" -#include #include #include @@ -97,7 +97,7 @@ static int AppInitRPC(int argc, char* argv[]) } return EXIT_SUCCESS; } - if (!boost::filesystem::is_directory(GetDataDir(false))) { + if (!fs::is_directory(GetDataDir(false))) { fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); return EXIT_FAILURE; } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 7ab05171d..28b2283c6 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -4,6 +4,7 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . #include "clientversion.h" +#include "fs.h" #include "rpc/server.h" #include "init.h" #include "main.h" @@ -14,7 +15,6 @@ #include "httprpc.h" #include -#include #include #include @@ -94,7 +94,7 @@ bool AppInit(int argc, char* argv[]) try { - if (!boost::filesystem::is_directory(GetDataDir(false))) + if (!fs::is_directory(GetDataDir(false))) { fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); return false; diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index c93d5d5b3..5f4379acf 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -4,10 +4,9 @@ #include "dbwrapper.h" +#include "fs.h" #include "util.h" -#include - #include #include #include @@ -32,7 +31,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) return options; } -CDBWrapper::CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe) +CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe) { penv = NULL; readoptions.verify_checksums = true; diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 0996e9cda..e80baccab 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -6,13 +6,12 @@ #define BITCOIN_DBWRAPPER_H #include "clientversion.h" +#include "fs.h" #include "serialize.h" #include "streams.h" #include "util.h" #include "version.h" -#include - #include #include @@ -173,7 +172,7 @@ class CDBWrapper * @param[in] fMemory If true, use leveldb's memory environment. * @param[in] fWipe If true, remove all existing data. */ - CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); + CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); ~CDBWrapper(); template diff --git a/src/fs.cpp b/src/fs.cpp new file mode 100644 index 000000000..b21919452 --- /dev/null +++ b/src/fs.cpp @@ -0,0 +1,19 @@ +// Copyright (c) 2018-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "fs.h" + +namespace fsbridge { + +FILE *fopen(const fs::path& p, const char *mode) +{ + return ::fopen(p.string().c_str(), mode); +} + +FILE *freopen(const fs::path& p, const char *mode, FILE *stream) +{ + return ::freopen(p.string().c_str(), mode, stream); +} + +} // fsbridge diff --git a/src/fs.h b/src/fs.h new file mode 100644 index 000000000..abb4be254 --- /dev/null +++ b/src/fs.h @@ -0,0 +1,24 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_FS_H +#define BITCOIN_FS_H + +#include +#include + +#include +#include +#include + +/** Filesystem operations and types */ +namespace fs = boost::filesystem; + +/** Bridge operations to C stdio */ +namespace fsbridge { + FILE *fopen(const fs::path& p, const char *mode); + FILE *freopen(const fs::path& p, const char *mode, FILE *stream); +}; + +#endif // BITCOIN_FS_H diff --git a/src/gtest/main.cpp b/src/gtest/main.cpp index e40bcec14..d3af84bc4 100644 --- a/src/gtest/main.cpp +++ b/src/gtest/main.cpp @@ -24,12 +24,12 @@ int main(int argc, char **argv) { params = ZCJoinSplit::Prepared(); - boost::filesystem::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; - boost::filesystem::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; - boost::filesystem::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; + fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; + fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; + fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; static_assert( - sizeof(boost::filesystem::path::value_type) == sizeof(codeunit), + sizeof(fs::path::value_type) == sizeof(codeunit), "librustzcash not configured correctly"); auto sapling_spend_str = sapling_spend.native(); auto sapling_output_str = sapling_output.native(); diff --git a/src/gtest/test_deprecation.cpp b/src/gtest/test_deprecation.cpp index 19a3da9ab..f5e7639f6 100644 --- a/src/gtest/test_deprecation.cpp +++ b/src/gtest/test_deprecation.cpp @@ -4,12 +4,12 @@ #include "chainparams.h" #include "clientversion.h" #include "deprecation.h" +#include "fs.h" #include "init.h" #include "ui_interface.h" #include "util.h" #include "utilstrencodings.h" -#include #include using namespace boost::placeholders; @@ -49,7 +49,7 @@ class DeprecationTest : public ::testing::Test { StrictMock mock_; - static std::vector read_lines(boost::filesystem::path filepath) { + static std::vector read_lines(fs::path filepath) { std::vector result; std::ifstream f(filepath.string().c_str()); @@ -123,8 +123,8 @@ TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnTestnet) { } TEST_F(DeprecationTest, AlertNotify) { - boost::filesystem::path temp = GetTempPath() / - boost::filesystem::unique_path("alertnotify-%%%%.txt"); + fs::path temp = fs::temp_directory_path() / + fs::unique_path("alertnotify-%%%%.txt"); mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string(); @@ -146,5 +146,5 @@ TEST_F(DeprecationTest, AlertNotify) { #else EXPECT_EQ(r[0], strprintf("'%s' ", expectedMsg)); #endif - boost::filesystem::remove(temp); + fs::remove(temp); } diff --git a/src/gtest/test_foundersreward.cpp b/src/gtest/test_foundersreward.cpp index 296ea6eb6..285d57411 100644 --- a/src/gtest/test_foundersreward.cpp +++ b/src/gtest/test_foundersreward.cpp @@ -3,6 +3,7 @@ #include "main.h" #include "utilmoneystr.h" #include "chainparams.h" +#include "fs.h" #include "utilstrencodings.h" #include "zcash/Address.hpp" #include "wallet/wallet.h" @@ -11,7 +12,6 @@ #include #include #include -#include #include "util.h" #include "utiltest.h" @@ -26,8 +26,8 @@ #if 0 TEST(FoundersRewardTest, create_testnet_2of3multisig) { SelectParams(CBaseChainParams::TESTNET); - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); + fs::path pathTemp = fs::temp_directory_path() / fs::unique_path(); + fs::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; auto pWallet = std::make_shared("wallet.dat"); diff --git a/src/init.cpp b/src/init.cpp index 889ad6413..17b11e278 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -16,6 +16,7 @@ #include "consensus/upgrades.h" #include "consensus/validation.h" #include "experimental_features.h" +#include "fs.h" #include "httpserver.h" #include "httprpc.h" #include "key.h" @@ -56,7 +57,6 @@ #include #include #include -#include #include #include @@ -210,8 +210,8 @@ void Shutdown() if (fFeeEstimatesInitialized) { - boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; - CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION); + fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; + CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK, CLIENT_VERSION); if (!est_fileout.IsNull()) mempool.WriteFeeEstimates(est_fileout); else @@ -248,8 +248,8 @@ void Shutdown() #ifndef WIN32 try { - boost::filesystem::remove(GetPidFile()); - } catch (const boost::filesystem::filesystem_error& e) { + fs::remove(GetPidFile()); + } catch (const fs::filesystem_error& e) { LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what()); } #endif @@ -543,7 +543,7 @@ struct CImportingNow // works correctly. void CleanupBlockRevFiles() { - using namespace boost::filesystem; + using namespace fs; map mapBlockFiles; // Glob all blk?????.dat and rev?????.dat files from the blocks directory. @@ -683,7 +683,7 @@ void ThreadStartWalletNotifier() ThreadNotifyWallets(pindexLastTip); } -void ThreadImport(std::vector vImportFiles) +void ThreadImport(std::vector vImportFiles) { const CChainParams& chainparams = Params(); RenameThread("bitcoinz-loadblk"); @@ -697,17 +697,17 @@ void ThreadImport(std::vector vImportFiles) size_t fullSize = 0; while (true) { CDiskBlockPos pos(nFile, 0); - boost::filesystem::path blkFile = GetBlockPosFilename(pos, "blk"); - if (!boost::filesystem::exists(blkFile)) + fs::path blkFile = GetBlockPosFilename(pos, "blk"); + if (!fs::exists(blkFile)) break; // No block files left to reindex nFile++; - fullSize += boost::filesystem::file_size(blkFile); + fullSize += fs::file_size(blkFile); } nFullSizeToReindex = std::max(1, fullSize); nFile = 0; while (true) { CDiskBlockPos pos(nFile, 0); - if (!boost::filesystem::exists(GetBlockPosFilename(pos, "blk"))) + if (!fs::exists(GetBlockPosFilename(pos, "blk"))) break; // No block files left to reindex FILE *file = OpenBlockFile(pos, true); if (!file) @@ -726,11 +726,11 @@ void ThreadImport(std::vector vImportFiles) } // hardcoded $DATADIR/bootstrap.dat - boost::filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; - if (boost::filesystem::exists(pathBootstrap)) { - FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); + fs::path pathBootstrap = GetDataDir() / "bootstrap.dat"; + if (fs::exists(pathBootstrap)) { + FILE *file = fsbridge::fopen(pathBootstrap, "rb"); if (file) { - boost::filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; + fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; LogPrintf("Importing bootstrap.dat...\n"); LoadExternalBlockFile(chainparams, file); RenameOver(pathBootstrap, pathBootstrapOld); @@ -740,8 +740,8 @@ void ThreadImport(std::vector vImportFiles) } // -loadblock= - for (const boost::filesystem::path& path : vImportFiles) { - FILE *file = fopen(path.string().c_str(), "rb"); + for (const fs::path& path : vImportFiles) { + FILE *file = fsbridge::fopen(path, "rb"); if (file) { LogPrintf("Importing blocks file %s...\n", path.string()); LoadExternalBlockFile(chainparams,file); @@ -787,14 +787,14 @@ static void ZC_LoadParams( struct timeval tv_start, tv_end; float elapsed; - boost::filesystem::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; - boost::filesystem::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; - boost::filesystem::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; + fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; + fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; + fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; if (!( - boost::filesystem::exists(sapling_spend) && - boost::filesystem::exists(sapling_output) && - boost::filesystem::exists(sprout_groth16) + fs::exists(sapling_spend) && + fs::exists(sapling_output) && + fs::exists(sprout_groth16) )) { uiInterface.ThreadSafeMessageBox(strprintf( _("Cannot find the BitcoinZ network parameters in the following directory:\n" @@ -809,7 +809,7 @@ static void ZC_LoadParams( pzcashParams = ZCJoinSplit::Prepared(); static_assert( - sizeof(boost::filesystem::path::value_type) == sizeof(codeunit), + sizeof(fs::path::value_type) == sizeof(codeunit), "librustzcash not configured correctly"); auto sapling_spend_str = sapling_spend.native(); auto sapling_output_str = sapling_output.native(); @@ -1185,8 +1185,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) std::string strDataDir = GetDataDir().string(); // Make sure only a single Bitcoin process is using the data directory. - boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; - FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. + fs::path pathLockFile = GetDataDir() / ".lock"; + FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist. if (file) fclose(file); try { @@ -1397,7 +1397,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fReindex = GetBoolArg("-reindex", false); bool fReindexChainState = GetBoolArg("-reindex-chainstate", false); - boost::filesystem::create_directories(GetDataDir() / "blocks"); + fs::create_directories(GetDataDir() / "blocks"); // cache size calculations int64_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); @@ -1567,8 +1567,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); - boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; - CAutoFile est_filein(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION); + fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; + CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION); // Allowed to fail as this file IS missing on first startup. if (!est_filein.IsNull()) mempool.ReadFeeEstimates(est_filein); @@ -1664,7 +1664,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!ActivateBestChain(state, chainparams)) strErrors << "Failed to connect best block"; - std::vector vImportFiles; + std::vector vImportFiles; if (mapArgs.count("-loadblock")) { for (const std::string& strFile : mapMultiArgs["-loadblock"]) diff --git a/src/main.cpp b/src/main.cpp index 34c6abc1c..11ed08f5c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,8 +42,6 @@ #include #include -#include -#include #include #include @@ -4136,8 +4134,8 @@ void UnlinkPrunedFiles(std::set& setFilesToPrune) { for (set::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { CDiskBlockPos pos(*it, 0); - boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); - boost::filesystem::remove(GetBlockPosFilename(pos, "rev")); + fs::remove(GetBlockPosFilename(pos, "blk")); + fs::remove(GetBlockPosFilename(pos, "rev")); LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it); } } @@ -4192,7 +4190,7 @@ void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight bool CheckDiskSpace(uint64_t nAdditionalBytes) { - uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; + uint64_t nFreeBytesAvailable = fs::space(GetDataDir()).available; // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) @@ -4205,11 +4203,11 @@ FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { if (pos.IsNull()) return NULL; - boost::filesystem::path path = GetBlockPosFilename(pos, prefix); - boost::filesystem::create_directories(path.parent_path()); - FILE* file = fopen(path.string().c_str(), "rb+"); + fs::path path = GetBlockPosFilename(pos, prefix); + fs::create_directories(path.parent_path()); + FILE* file = fsbridge::fopen(path, "rb+"); if (!file && !fReadOnly) - file = fopen(path.string().c_str(), "wb+"); + file = fsbridge::fopen(path, "wb+"); if (!file) { LogPrintf("Unable to open file %s\n", path.string()); return NULL; @@ -4232,7 +4230,7 @@ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { return OpenDiskFile(pos, "rev", fReadOnly); } -boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) +fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) { return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); } diff --git a/src/main.h b/src/main.h index 430961ad5..ea32af125 100644 --- a/src/main.h +++ b/src/main.h @@ -15,6 +15,7 @@ #include "chainparams.h" #include "coins.h" #include "consensus/upgrades.h" +#include "fs.h" #include "net.h" #include "primitives/block.h" #include "primitives/transaction.h" @@ -227,7 +228,7 @@ FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** Open an undo file (rev?????.dat) */ FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** Translation to a filesystem path */ -boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix); +fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix); /** Import blocks from an external file */ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = NULL); /** Initialize a new block tree database + block data on disk */ diff --git a/src/net.cpp b/src/net.cpp index 2f92b837d..bb3e02f94 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -7,6 +7,7 @@ #include "config/bitcoin-config.h" #endif +#include "fs.h" #include "main.h" #include "net.h" @@ -25,7 +26,6 @@ #include #endif -#include #include // Dump addresses to peers.dat every 15 minutes (900s) @@ -1963,8 +1963,8 @@ bool CAddrDB::Write(const CAddrMan& addr) ssPeers << hash; // open temp output file, and associate with CAutoFile - boost::filesystem::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fopen(pathTmp.string().c_str(), "wb"); + fs::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fsbridge::fopen(pathTmp, "wb"); CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) return error("%s: Failed to open file %s", __func__, pathTmp.string()); @@ -1989,13 +1989,13 @@ bool CAddrDB::Write(const CAddrMan& addr) bool CAddrDB::Read(CAddrMan& addr) { // open input file, and associate with CAutoFile - FILE *file = fopen(pathAddr.string().c_str(), "rb"); + FILE *file = fsbridge::fopen(pathAddr, "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); if (filein.IsNull()) return error("%s: Failed to open file %s", __func__, pathAddr.string()); // use file size to size memory buffer - int fileSize = boost::filesystem::file_size(pathAddr); + int fileSize = fs::file_size(pathAddr); int dataSize = fileSize - sizeof(uint256); // Don't try to resize to a negative number if file is small if (dataSize < 0) diff --git a/src/net.h b/src/net.h index 221a623ef..2be65891a 100644 --- a/src/net.h +++ b/src/net.h @@ -8,6 +8,7 @@ #include "bloom.h" #include "compat.h" +#include "fs.h" #include "hash.h" #include "limitedmap.h" #include "mruset.h" @@ -26,7 +27,6 @@ #include #endif -#include #include class CAddrMan; @@ -652,7 +652,7 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss); class CAddrDB { private: - boost::filesystem::path pathAddr; + fs::path pathAddr; public: CAddrDB(); bool Write(const CAddrMan& addr); diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index 4125757e1..cd92119e4 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -68,9 +68,9 @@ static const std::string COOKIEAUTH_USER = "__cookie__"; /** Default name for auth cookie file */ static const std::string COOKIEAUTH_FILE = ".cookie"; -boost::filesystem::path GetAuthCookieFile() +fs::path GetAuthCookieFile() { - boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE)); + fs::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE)); if (!path.is_complete()) path = GetDataDir() / path; return path; } @@ -85,7 +85,7 @@ bool GenerateAuthCookie(std::string *cookie_out) * these are set to 077 in init.cpp unless overridden with -sysperms. */ std::ofstream file; - boost::filesystem::path filepath = GetAuthCookieFile(); + fs::path filepath = GetAuthCookieFile(); file.open(filepath.string().c_str()); if (!file.is_open()) { LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string()); @@ -104,7 +104,7 @@ bool GetAuthCookie(std::string *cookie_out) { std::ifstream file; std::string cookie; - boost::filesystem::path filepath = GetAuthCookieFile(); + fs::path filepath = GetAuthCookieFile(); file.open(filepath.string().c_str()); if (!file.is_open()) return false; @@ -119,8 +119,8 @@ bool GetAuthCookie(std::string *cookie_out) void DeleteAuthCookie() { try { - boost::filesystem::remove(GetAuthCookieFile()); - } catch (const boost::filesystem::filesystem_error& e) { + fs::remove(GetAuthCookieFile()); + } catch (const fs::filesystem_error& e) { LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what()); } } diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 5e12d0a4f..e79e54888 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -6,11 +6,12 @@ #ifndef BITCOIN_RPCPROTOCOL_H #define BITCOIN_RPCPROTOCOL_H +#include "fs.h" + #include #include #include #include -#include #include @@ -82,7 +83,7 @@ std::string JSONRPCReply(const UniValue& result, const UniValue& error, const Un UniValue JSONRPCError(int code, const std::string& message); /** Get name of RPC authentication cookie file */ -boost::filesystem::path GetAuthCookieFile(); +fs::path GetAuthCookieFile(); /** Generate a new RPC authentication cookie and write it to disk */ bool GenerateAuthCookie(std::string *cookie_out); /** Read the RPC authentication cookie from disk */ diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index c6d48f80d..60f4d88ef 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -5,6 +5,7 @@ #include "rpc/server.h" +#include "fs.h" #include "init.h" #include "key_io.h" #include "random.h" @@ -19,7 +20,6 @@ #include #include -#include #include #include #include diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index a3a9eccc8..ea3ab9c45 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -10,6 +10,7 @@ #include "chain.h" #include "chainparams.h" #include "clientversion.h" +#include "fs.h" #include "data/alertTests.raw.h" #include "main.h" @@ -17,7 +18,6 @@ #include "rpc/server.h" #include "serialize.h" #include "streams.h" -#include "util.h" #include "utilstrencodings.h" #include "utiltest.h" #include "warnings.h" @@ -26,7 +26,6 @@ #include -#include #include #include "key.h" @@ -271,7 +270,7 @@ struct ReadAlerts : public TestingSetup } ~ReadAlerts() { } - static std::vector read_lines(boost::filesystem::path filepath) + static std::vector read_lines(fs::path filepath) { std::vector result; @@ -353,8 +352,8 @@ BOOST_AUTO_TEST_CASE(AlertNotify) SetMockTime(11); const std::vector& alertKey = Params(CBaseChainParams::MAIN).AlertKey(); - boost::filesystem::path temp = GetTempPath() / - boost::filesystem::unique_path("alertnotify-%%%%.txt"); + fs::path temp = fs::temp_directory_path() / + fs::unique_path("alertnotify-%%%%.txt"); mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string(); @@ -382,7 +381,7 @@ BOOST_AUTO_TEST_CASE(AlertNotify) BOOST_CHECK_EQUAL(r[4], "'Alert 4, reenables RPC' "); // dashes should be removed BOOST_CHECK_EQUAL(r[5], "'Evil Alert; /bin/ls; echo ' "); #endif - boost::filesystem::remove(temp); + fs::remove(temp); SetMockTime(0); mapAlerts.clear(); diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index 2563076f9..e9b0816bd 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -4,6 +4,7 @@ #include "clientversion.h" #include "consensus/validation.h" +#include "fs.h" #include "main.h" #include "test/test_bitcoin.h" #include "utiltime.h" @@ -11,8 +12,6 @@ #include -#include -#include #include @@ -20,7 +19,6 @@ BOOST_FIXTURE_TEST_SUITE(CheckBlock_tests, BasicTestingSetup) bool read_block(const std::string& filename, CBlock& block) { - namespace fs = boost::filesystem; fs::path testFile = fs::current_path() / "data" / filename; #ifdef TEST_DATA_DIR if (!fs::exists(testFile)) @@ -28,7 +26,7 @@ bool read_block(const std::string& filename, CBlock& block) testFile = fs::path(BOOST_PP_STRINGIZE(TEST_DATA_DIR)) / filename; } #endif - FILE* fp = fopen(testFile.string().c_str(), "rb"); + FILE* fp = fsbridge::fopen(testFile, "rb"); if (!fp) return false; fseek(fp, 8, SEEK_SET); // skip msgheader/size diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 1853cbf1d..611311379 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -14,7 +14,7 @@ using namespace std; using namespace boost::assign; // bring 'operator+=()' into scope -using namespace boost::filesystem; +using namespace fs; // Test if a string consists entirely of null characters bool is_null_key(const vector& key) { diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 1d9dcf550..99afd1fd0 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -2,12 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php. +#include "fs.h" #include "main.h" #include "test/test_bitcoin.h" #include "test/test_random.h" #include -#include BOOST_FIXTURE_TEST_SUITE(streams_tests, TestingSetup) @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file) // We can explicitly close the file, or the destructor will do it. bf.fclose(); - boost::filesystem::remove("streams_test_tmp"); + fs::remove("streams_test_tmp"); } BOOST_AUTO_TEST_CASE(streams_buffered_file_rand) @@ -251,7 +251,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand) maxPos = currentPos; } } - boost::filesystem::remove("streams_test_tmp"); + fs::remove("streams_test_tmp"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index fa653a59a..459111f0e 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -14,6 +14,7 @@ #ifdef ENABLE_MINING #include "crypto/equihash.h" #endif +#include "fs.h" #include "key.h" #include "main.h" #include "miner.h" @@ -26,7 +27,6 @@ #include "rpc/register.h" #include "script/sigcache.h" -#include #include #include @@ -45,12 +45,12 @@ JoinSplitTestingSetup::JoinSplitTestingSetup(const std::string& chainName) : Bas { pzcashParams = ZCJoinSplit::Prepared(); - boost::filesystem::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; - boost::filesystem::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; - boost::filesystem::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; + fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; + fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; + fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; static_assert( - sizeof(boost::filesystem::path::value_type) == sizeof(codeunit), + sizeof(fs::path::value_type) == sizeof(codeunit), "librustzcash not configured correctly"); auto sapling_spend_str = sapling_spend.native(); auto sapling_output_str = sapling_output.native(); @@ -100,11 +100,11 @@ TestingSetup::TestingSetup(const std::string& chainName) : JoinSplitTestingSetup RegisterAllCoreRPCCommands(tableRPC); // Save current path, in case a test changes it - orig_current_path = boost::filesystem::current_path(); + orig_current_path = fs::current_path(); ClearDatadirCache(); - pathTemp = GetTempPath() / strprintf("test_bitcoinz_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); - boost::filesystem::create_directories(pathTemp); + pathTemp = fs::temp_directory_path() / strprintf("test_bitcoinz_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); + fs::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); @@ -127,9 +127,9 @@ TestingSetup::~TestingSetup() delete pblocktree; // Restore the previous current path so temporary directory can be deleted - boost::filesystem::current_path(orig_current_path); + fs::current_path(orig_current_path); - boost::filesystem::remove_all(pathTemp); + fs::remove_all(pathTemp); } #ifdef ENABLE_MINING diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index f8ddc33ba..faa7d3b6b 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -5,10 +5,10 @@ #include "key.h" #include "consensus/upgrades.h" +#include "fs.h" #include "pubkey.h" #include "txdb.h" -#include #include /** Basic testing setup. @@ -32,8 +32,8 @@ struct JoinSplitTestingSetup: public BasicTestingSetup { */ struct TestingSetup: public JoinSplitTestingSetup { CCoinsViewDB *pcoinsdbview; - boost::filesystem::path orig_current_path; - boost::filesystem::path pathTemp; + fs::path orig_current_path; + fs::path pathTemp; boost::thread_group threadGroup; TestingSetup(const std::string& chainName = CBaseChainParams::MAIN); diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index a9ab6b746..18c04412b 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -365,9 +365,9 @@ static std::map ParseTorReplyMapping(const std::string * @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data * (with len > maxsize) will be returned. */ -static std::pair ReadBinaryFile(const std::string &filename, size_t maxsize=std::numeric_limits::max()) +static std::pair ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits::max()) { - FILE *f = fopen(filename.c_str(), "rb"); + FILE *f = fsbridge::fopen(filename, "rb"); if (f == NULL) return std::make_pair(false,""); std::string retval; @@ -391,9 +391,9 @@ static std::pair ReadBinaryFile(const std::string &filename, s /** Write contents of std::string to a file. * @return true on success. */ -static bool WriteBinaryFile(const std::string &filename, const std::string &data) +static bool WriteBinaryFile(const fs::path &filename, const std::string &data) { - FILE *f = fopen(filename.c_str(), "wb"); + FILE *f = fsbridge::fopen(filename, "wb"); if (f == NULL) return false; if (fwrite(data.data(), 1, data.size(), f) != data.size()) { @@ -416,7 +416,7 @@ class TorController ~TorController(); /** Get name for file to store private key in */ - std::string GetPrivateKeyFile(); + fs::path GetPrivateKeyFile(); /** Reconnect, after getting disconnected */ void Reconnect(); @@ -468,7 +468,7 @@ TorController::TorController(struct event_base* baseIn, const std::string& targe // Read service private key if cached std::pair pkf = ReadBinaryFile(GetPrivateKeyFile()); if (pkf.first) { - LogPrint("tor", "tor: Reading cached private key from %s\n", GetPrivateKeyFile()); + LogPrint("tor", "tor: Reading cached private key from %s\n", GetPrivateKeyFile().string()); private_key = pkf.second; } } @@ -507,9 +507,9 @@ void TorController::add_onion_cb(TorControlConnection& conn, const TorControlRep service = CService(service_id+".onion", GetListenPort(), false); LogPrintf("tor: Got service ID %s, advertizing service %s\n", service_id, service.ToString()); if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { - LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile()); + LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile().string()); } else { - LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile()); + LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile().string()); } AddLocal(service, LOCAL_MANUAL); // ... onion requested - keep connection open @@ -719,9 +719,9 @@ void TorController::Reconnect() } } -std::string TorController::GetPrivateKeyFile() +fs::path TorController::GetPrivateKeyFile() { - return (GetDataDir() / "onion_private_key").string(); + return GetDataDir() / "onion_private_key"; } void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) diff --git a/src/util.cpp b/src/util.cpp index fc90a2f71..ca384033c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -10,6 +10,7 @@ #include "util.h" #include "chainparamsbase.h" +#include "fs.h" #include "random.h" #include "serialize.h" #include "sync.h" @@ -65,8 +66,6 @@ #include // for to_lower() #include #include // for startswith() and endswith() -#include -#include #include #include #include @@ -160,8 +159,8 @@ void OpenDebugLog() assert(fileout == NULL); assert(vMsgsBeforeOpenLog); - boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; - fileout = fopen(pathDebug.string().c_str(), "a"); + fs::path pathDebug = GetDataDir() / "debug.log"; + fileout = fsbridge::fopen(pathDebug, "a"); if (fileout) setbuf(fileout, NULL); // unbuffered // dump buffered messages from before we opened the log @@ -256,8 +255,8 @@ int LogPrintStr(const std::string &str) // reopen the log file, if requested if (fReopenDebugLog) { fReopenDebugLog = false; - boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; - if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL) + fs::path pathDebug = GetDataDir() / "debug.log"; + if (fsbridge::freopen(pathDebug,"a",fileout) != NULL) setbuf(fileout, NULL); // unbuffered } @@ -395,9 +394,8 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread) fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); } -boost::filesystem::path GetDefaultDataDir() +fs::path GetDefaultDataDir() { - namespace fs = boost::filesystem; // Windows < Vista: C:\Documents and Settings\Username\Application Data\Zcash // Windows >= Vista: C:\Users\Username\AppData\Roaming\Zcash // Mac: ~/Library/Application Support/Zcash @@ -424,16 +422,13 @@ boost::filesystem::path GetDefaultDataDir() #endif } -static boost::filesystem::path pathCached; -static boost::filesystem::path pathCachedNetSpecific; -static boost::filesystem::path zc_paramsPathCached; +static fs::path pathCached; +static fs::path pathCachedNetSpecific; +static fs::path zc_paramsPathCached; static CCriticalSection csPathCached; -static boost::filesystem::path ZC_GetBaseParamsDir() +static fs::path ZC_GetBaseParamsDir() { - // Copied from GetDefaultDataDir and adapter for zcash params. - - namespace fs = boost::filesystem; // Windows < Vista: C:\Documents and Settings\Username\Application Data\ZcashParams // Windows >= Vista: C:\Users\Username\AppData\Roaming\ZcashParams // Mac: ~/Library/Application Support/ZcashParams @@ -460,10 +455,8 @@ static boost::filesystem::path ZC_GetBaseParamsDir() #endif } -const boost::filesystem::path &ZC_GetParamsDir() +const fs::path &ZC_GetParamsDir() { - namespace fs = boost::filesystem; - LOCK(csPathCached); // Reuse the same lock as upstream. fs::path &path = zc_paramsPathCached; @@ -481,9 +474,8 @@ const boost::filesystem::path &ZC_GetParamsDir() // Return the user specified export directory. Create directory if it doesn't exist. // If user did not set option, return an empty path. // If there is a filesystem problem, throw an exception. -const boost::filesystem::path GetExportDir() +const fs::path GetExportDir() { - namespace fs = boost::filesystem; fs::path path; if (mapArgs.count("-exportdir")) { path = fs::system_complete(mapArgs["-exportdir"]); @@ -498,10 +490,8 @@ const boost::filesystem::path GetExportDir() } -const boost::filesystem::path &GetDataDir(bool fNetSpecific) +const fs::path &GetDataDir(bool fNetSpecific) { - namespace fs = boost::filesystem; - LOCK(csPathCached); fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; @@ -530,13 +520,13 @@ const boost::filesystem::path &GetDataDir(bool fNetSpecific) void ClearDatadirCache() { - pathCached = boost::filesystem::path(); - pathCachedNetSpecific = boost::filesystem::path(); + pathCached = fs::path(); + pathCachedNetSpecific = fs::path(); } -boost::filesystem::path GetConfigFile(const std::string& confPath) +fs::path GetConfigFile(const std::string& confPath) { - boost::filesystem::path pathConfigFile(confPath); + fs::path pathConfigFile(confPath); if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile; @@ -547,7 +537,7 @@ void ReadConfigFile(const std::string& confPath, map& mapSettingsRet, map >& mapMultiSettingsRet) { - boost::filesystem::ifstream streamConfig(GetConfigFile(confPath)); + fs::ifstream streamConfig(GetConfigFile(confPath)); if (!streamConfig.good()) throw missing_zcash_conf(); @@ -569,16 +559,16 @@ void ReadConfigFile(const std::string& confPath, } #ifndef WIN32 -boost::filesystem::path GetPidFile() +fs::path GetPidFile() { - boost::filesystem::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME)); + fs::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME)); if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; return pathPidFile; } -void CreatePidFile(const boost::filesystem::path &path, pid_t pid) +void CreatePidFile(const fs::path &path, pid_t pid) { - FILE* file = fopen(path.string().c_str(), "w"); + FILE* file = fsbridge::fopen(path, "w"); if (file) { fprintf(file, "%d\n", pid); @@ -587,7 +577,7 @@ void CreatePidFile(const boost::filesystem::path &path, pid_t pid) } #endif -bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest) +bool RenameOver(fs::path src, fs::path dest) { #ifdef WIN32 return MoveFileExA(src.string().c_str(), dest.string().c_str(), @@ -603,13 +593,13 @@ bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest) * Specifically handles case where path p exists, but it wasn't possible for the user to * write to the parent directory. */ -bool TryCreateDirectory(const boost::filesystem::path& p) +bool TryCreateDirectory(const fs::path& p) { try { - return boost::filesystem::create_directory(p); - } catch (const boost::filesystem::filesystem_error&) { - if (!boost::filesystem::exists(p) || !boost::filesystem::is_directory(p)) + return fs::create_directory(p); + } catch (const fs::filesystem_error&) { + if (!fs::exists(p) || !fs::is_directory(p)) throw; } @@ -714,9 +704,9 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { void ShrinkDebugFile() { // Scroll debug.log if it's getting too big - boost::filesystem::path pathLog = GetDataDir() / "debug.log"; - FILE* file = fopen(pathLog.string().c_str(), "r"); - if (file && boost::filesystem::file_size(pathLog) > 10 * 1000000) + fs::path pathLog = GetDataDir() / "debug.log"; + FILE* file = fsbridge::fopen(pathLog, "r"); + if (file && fs::file_size(pathLog) > 10 * 1000000) { // Restart the file with some of the end std::vector vch(200000,0); @@ -736,10 +726,8 @@ void ShrinkDebugFile() } #ifdef WIN32 -boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) +fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { - namespace fs = boost::filesystem; - WCHAR pszPath[MAX_PATH] = L""; if(SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate)) @@ -752,28 +740,6 @@ boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate) } #endif -boost::filesystem::path GetTempPath() { -#if BOOST_FILESYSTEM_VERSION == 3 - return boost::filesystem::temp_directory_path(); -#else - // TODO: remove when we don't support filesystem v2 anymore - boost::filesystem::path path; -#ifdef WIN32 - char pszPath[MAX_PATH] = ""; - - if (GetTempPathA(MAX_PATH, pszPath)) - path = boost::filesystem::path(pszPath); -#else - path = boost::filesystem::path("/tmp"); -#endif - if (path.empty() || !boost::filesystem::is_directory(path)) { - LogPrintf("GetTempPath(): failed to find temp path\n"); - return boost::filesystem::path(""); - } - return path; -#endif -} - void runCommand(const std::string& strCommand) { int nErr = ::system(strCommand.c_str()); @@ -811,9 +777,9 @@ void SetupEnvironment() // The path locale is lazy initialized and to avoid deinitialization errors // in multithreading environments, it is set explicitly by the main thread. // A dummy locale is used to extract the internal default locale, used by - // boost::filesystem::path, which is then used to explicitly imbue the path. - std::locale loc = boost::filesystem::path::imbue(std::locale::classic()); - boost::filesystem::path::imbue(loc); + // fs::path, which is then used to explicitly imbue the path. + std::locale loc = fs::path::imbue(std::locale::classic()); + fs::path::imbue(loc); } bool SetupNetworking() diff --git a/src/util.h b/src/util.h index 1b754b80d..080259513 100644 --- a/src/util.h +++ b/src/util.h @@ -15,6 +15,7 @@ #endif #include "compat.h" +#include "fs.h" #include "tinyformat.h" #include "utiltime.h" @@ -26,7 +27,6 @@ #include #include -#include #include static const bool DEFAULT_LOGTIMEMICROS = false; @@ -108,7 +108,7 @@ static inline bool error(const char* format) return false; } -const boost::filesystem::path &ZC_GetParamsDir(); +const fs::path &ZC_GetParamsDir(); void PrintExceptionContinue(const std::exception *pex, const char* pszThread); void ParseParameters(int argc, const char*const argv[]); @@ -116,15 +116,15 @@ void FileCommit(FILE *fileout); bool TruncateFile(FILE *file, unsigned int length); int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); -bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest); -bool TryCreateDirectory(const boost::filesystem::path& p); -boost::filesystem::path GetDefaultDataDir(); -const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); +bool RenameOver(fs::path src, fs::path dest); +bool TryCreateDirectory(const fs::path& p); +fs::path GetDefaultDataDir(); +const fs::path &GetDataDir(bool fNetSpecific = true); void ClearDatadirCache(); -boost::filesystem::path GetConfigFile(const std::string& confPath); +fs::path GetConfigFile(const std::string& confPath); #ifndef WIN32 -boost::filesystem::path GetPidFile(); -void CreatePidFile(const boost::filesystem::path &path, pid_t pid); +fs::path GetPidFile(); +void CreatePidFile(const fs::path &path, pid_t pid); #endif class missing_zcash_conf : public std::runtime_error { public: @@ -132,13 +132,12 @@ class missing_zcash_conf : public std::runtime_error { }; void ReadConfigFile(const std::string& confPath, std::map& mapSettingsRet, std::map >& mapMultiSettingsRet); #ifdef WIN32 -boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); +fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif -boost::filesystem::path GetTempPath(); void OpenDebugLog(); void ShrinkDebugFile(); void runCommand(const std::string& strCommand); -const boost::filesystem::path GetExportDir(); +const fs::path GetExportDir(); /** Returns privacy notice (for -version, -help and metrics screen) */ std::string PrivacyInfo(); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index d2741f7c1..1e978a143 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -6,6 +6,7 @@ #include "db.h" #include "addrman.h" +#include "fs.h" #include "hash.h" #include "protocol.h" #include "util.h" @@ -17,7 +18,6 @@ #include #endif -#include #include #include @@ -71,7 +71,7 @@ void CDBEnv::Close() EnvShutdown(); } -bool CDBEnv::Open(const boost::filesystem::path& pathIn) +bool CDBEnv::Open(const fs::path& pathIn) { if (fDbEnvInit) return true; @@ -79,9 +79,9 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn) boost::this_thread::interruption_point(); strPath = pathIn.string(); - boost::filesystem::path pathLogDir = pathIn / "database"; + fs::path pathLogDir = pathIn / "database"; TryCreateDirectory(pathLogDir); - boost::filesystem::path pathErrorFile = pathIn / "db.log"; + fs::path pathErrorFile = pathIn / "db.log"; LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); unsigned int nEnvFlags = 0; @@ -94,7 +94,7 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn) dbenv->set_lg_max(1048576); dbenv->set_lk_max_locks(40000); dbenv->set_lk_max_objects(40000); - dbenv->set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug + dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug dbenv->set_flags(DB_AUTO_COMMIT, 1); dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1); dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1); @@ -455,7 +455,7 @@ void CDBEnv::Flush(bool fShutdown) dbenv->log_archive(&listp, DB_ARCH_REMOVE); Close(); if (!fMockDb) - boost::filesystem::remove_all(boost::filesystem::path(strPath) / "database"); + fs::remove_all(fs::path(strPath) / "database"); } } } diff --git a/src/wallet/db.h b/src/wallet/db.h index e4773b79e..b210d7b9c 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -7,6 +7,7 @@ #define BITCOIN_WALLET_DB_H #include "clientversion.h" +#include "fs.h" #include "serialize.h" #include "streams.h" #include "sync.h" @@ -16,7 +17,6 @@ #include #include -#include #include #include @@ -31,7 +31,7 @@ class CDBEnv private: bool fDbEnvInit; bool fMockDb; - // Don't change into boost::filesystem::path, as that can result in + // Don't change into fs::path, as that can result in // shutdown problems/crashes caused by a static initialized internal pointer. std::string strPath; @@ -70,7 +70,7 @@ class CDBEnv typedef std::pair, std::vector > KeyValPair; bool Salvage(const std::string& strFile, bool fAggressive, std::vector& vResult); - bool Open(const boost::filesystem::path& path); + bool Open(const fs::path& path); void Close(); void Flush(bool fShutdown); void CheckpointLSN(const std::string& strFile); diff --git a/src/wallet/gtest/test_paymentdisclosure.cpp b/src/wallet/gtest/test_paymentdisclosure.cpp index e964d4cb0..bda045935 100644 --- a/src/wallet/gtest/test_paymentdisclosure.cpp +++ b/src/wallet/gtest/test_paymentdisclosure.cpp @@ -1,5 +1,6 @@ #include +#include "fs.h" #include "main.h" #include "utilmoneystr.h" #include "chainparams.h" @@ -13,7 +14,6 @@ #include #include #include -#include #include #include "util.h" @@ -53,7 +53,7 @@ static uint256 random_uint256() // Subclass of PaymentDisclosureDB to add debugging methods class PaymentDisclosureDBTest : public PaymentDisclosureDB { public: - PaymentDisclosureDBTest(const boost::filesystem::path& dbPath) : PaymentDisclosureDB(dbPath) {} + PaymentDisclosureDBTest(const fs::path& dbPath) : PaymentDisclosureDB(dbPath) {} void DebugDumpAllStdout() { ASSERT_NE(db, nullptr); @@ -95,8 +95,8 @@ class PaymentDisclosureDBTest : public PaymentDisclosureDB { TEST(paymentdisclosure, mainnet) { SelectParams(CBaseChainParams::MAIN); - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); + fs::path pathTemp = fs::temp_directory_path() / fs::unique_path(); + fs::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); PaymentDisclosureDBTest mydb(pathTemp); diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 1531dc4a6..7b786ee16 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -5,6 +5,7 @@ #include "base58.h" #include "chainparams.h" #include "consensus/merkle.h" +#include "fs.h" #include "key_io.h" #include "main.h" #include "primitives/block.h" @@ -16,8 +17,6 @@ #include "zcash/Note.hpp" #include "zcash/NoteEncryption.hpp" -#include - using ::testing::Return; extern ZCJoinSplit* params; @@ -154,8 +153,8 @@ std::pair GetWitnessesAndAnchors( TEST(WalletTests, SetupDatadirLocationRunAsFirstTest) { // Get temporary and unique path for file. - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); + fs::path pathTemp = fs::temp_directory_path() / fs::unique_path(); + fs::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); } diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 3e7fb876f..374957130 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -1,12 +1,11 @@ #include +#include "fs.h" #include "zcash/Address.hpp" #include "wallet/wallet.h" #include "wallet/walletdb.h" #include "util.h" -#include - /** * This test covers Sapling methods on CWallet * GenerateNewSaplingZKey() @@ -220,8 +219,8 @@ TEST(WalletZkeysTest, WriteZkeyDirectToDb) { // Get temporary and unique path for file. // Note: / operator to append paths - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); + fs::path pathTemp = fs::temp_directory_path() / fs::unique_path(); + fs::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; @@ -293,8 +292,8 @@ TEST(WalletZkeysTest, WriteViewingKeyDirectToDB) { // Get temporary and unique path for file. // Note: / operator to append paths - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); + fs::path pathTemp = fs::temp_directory_path() / fs::unique_path(); + fs::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; @@ -339,8 +338,8 @@ TEST(WalletZkeysTest, WriteCryptedzkeyDirectToDb) { // Get temporary and unique path for file. // Note: / operator to append paths - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); + fs::path pathTemp = fs::temp_directory_path() / fs::unique_path(); + fs::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; @@ -414,8 +413,8 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { // Get temporary and unique path for file. // Note: / operator to append paths - boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - boost::filesystem::create_directories(pathTemp); + fs::path pathTemp = fs::temp_directory_path() / fs::unique_path(); + fs::create_directories(pathTemp); mapArgs["-datadir"] = pathTemp.string(); bool fFirstRun; diff --git a/src/wallet/paymentdisclosuredb.cpp b/src/wallet/paymentdisclosuredb.cpp index 21b489e3c..b6b12c43f 100644 --- a/src/wallet/paymentdisclosuredb.cpp +++ b/src/wallet/paymentdisclosuredb.cpp @@ -4,14 +4,13 @@ #include "wallet/paymentdisclosuredb.h" +#include "fs.h" #include "util.h" #include "dbwrapper.h" -#include - using namespace std; -static boost::filesystem::path emptyPath; +static fs::path emptyPath; /** * Static method to return the shared/default payment disclosure database. @@ -26,8 +25,8 @@ shared_ptr PaymentDisclosureDB::sharedInstance() { PaymentDisclosureDB::PaymentDisclosureDB() : PaymentDisclosureDB(emptyPath) { } -PaymentDisclosureDB::PaymentDisclosureDB(const boost::filesystem::path& dbPath) { - boost::filesystem::path path(dbPath); +PaymentDisclosureDB::PaymentDisclosureDB(const fs::path& dbPath) { + fs::path path(dbPath); if (path.empty()) { path = GetDataDir() / "paymentdisclosure"; LogPrintf("PaymentDisclosure: using default path for database: %s\n", path.string()); diff --git a/src/wallet/paymentdisclosuredb.h b/src/wallet/paymentdisclosuredb.h index 621c1a5a5..0672954ae 100644 --- a/src/wallet/paymentdisclosuredb.h +++ b/src/wallet/paymentdisclosuredb.h @@ -29,7 +29,7 @@ class PaymentDisclosureDB static std::shared_ptr sharedInstance(); PaymentDisclosureDB(); - PaymentDisclosureDB(const boost::filesystem::path& dbPath); + PaymentDisclosureDB(const fs::path& dbPath); ~PaymentDisclosureDB(); bool Put(const PaymentDisclosureKey& key, const PaymentDisclosureInfo& info); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 47543362b..7fb11856b 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -557,7 +557,7 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys) EnsureWalletIsUnlocked(); - boost::filesystem::path exportdir; + fs::path exportdir; try { exportdir = GetExportDir(); } catch (const std::runtime_error& e) { @@ -571,9 +571,9 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys) if (clean.compare(unclean) != 0) { throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Filename is invalid as only alphanumeric characters are allowed. Try '%s' instead.", clean)); } - boost::filesystem::path exportfilepath = exportdir / clean; + fs::path exportfilepath = exportdir / clean; - if (boost::filesystem::exists(exportfilepath)) { + if (fs::exists(exportfilepath)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot overwrite existing file " + exportfilepath.string()); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b375e8036..5333070cf 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1840,7 +1840,7 @@ UniValue backupwallet(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - boost::filesystem::path exportdir; + fs::path exportdir; try { exportdir = GetExportDir(); } catch (const std::runtime_error& e) { @@ -1854,7 +1854,7 @@ UniValue backupwallet(const UniValue& params, bool fHelp) if (clean.compare(unclean) != 0) { throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Filename is invalid as only alphanumeric characters are allowed. Try '%s' instead.", clean)); } - boost::filesystem::path exportfilepath = exportdir / clean; + fs::path exportfilepath = exportdir / clean; if (!BackupWallet(*pwalletMain, exportfilepath.string())) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 0432765d6..36aaf8de5 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -6,6 +6,7 @@ #include "rpc/client.h" #include "consensus/merkle.h" +#include "fs.h" #include "experimental_features.h" #include "key_io.h" #include "main.h" @@ -35,7 +36,6 @@ #include #include #include -#include #include @@ -472,9 +472,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet) BOOST_CHECK(addrs.size()==1); // Set up paths - boost::filesystem::path tmppath = boost::filesystem::temp_directory_path(); - boost::filesystem::path tmpfilename = boost::filesystem::unique_path("%%%%%%%%"); - boost::filesystem::path exportfilepath = tmppath / tmpfilename; + fs::path tmppath = fs::temp_directory_path(); + fs::path tmpfilename = fs::unique_path("%%%%%%%%"); + fs::path exportfilepath = tmppath / tmpfilename; // export will fail since exportdir is not set BOOST_CHECK_THROW(CallRPC(string("z_exportwallet ") + tmpfilename.string()), runtime_error); @@ -558,8 +558,8 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) std::string testWalletDump = (formatobject % testKey % testAddr).str(); // write test data to file - boost::filesystem::path temp = boost::filesystem::temp_directory_path() / - boost::filesystem::unique_path(); + fs::path temp = fs::temp_directory_path() / + fs::unique_path(); const std::string path = temp.string(); std::ofstream file(path); file << testWalletDump; @@ -1451,7 +1451,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys) strWalletPass.reserve(100); strWalletPass = "hello"; - boost::filesystem::current_path(GetArg("-datadir","/tmp/thisshouldnothappen")); + fs::current_path(GetArg("-datadir","/tmp/thisshouldnothappen")); BOOST_CHECK(pwalletMain->EncryptWallet(strWalletPass)); // Verify we can still list the keys imported @@ -1512,7 +1512,7 @@ BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) strWalletPass.reserve(100); strWalletPass = "hello"; - boost::filesystem::current_path(GetArg("-datadir","/tmp/thisshouldnothappen")); + fs::current_path(GetArg("-datadir","/tmp/thisshouldnothappen")); BOOST_CHECK(pwalletMain->EncryptWallet(strWalletPass)); // Verify we can still list the keys imported diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 794b1219b..9acae2db3 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -12,6 +12,7 @@ #include "consensus/upgrades.h" #include "consensus/validation.h" #include "consensus/consensus.h" +#include "fs.h" #include "init.h" #include "key_io.h" #include "main.h" @@ -33,7 +34,6 @@ #include #include -#include #include using namespace std; @@ -900,18 +900,18 @@ bool CWallet::Verify() uiInterface.InitMessage(_("Verifying wallet...")); // Wallet file must be a plain filename without a directory - if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile)) + if (walletFile != fs::basename(walletFile) + fs::extension(walletFile)) return UIError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string())); if (!bitdb.Open(GetDataDir())) { // try moving the database env out of the way - boost::filesystem::path pathDatabase = GetDataDir() / "database"; - boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime()); + fs::path pathDatabase = GetDataDir() / "database"; + fs::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime()); try { - boost::filesystem::rename(pathDatabase, pathDatabaseBak); + fs::rename(pathDatabase, pathDatabaseBak); LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); - } catch (const boost::filesystem::filesystem_error&) { + } catch (const fs::filesystem_error&) { // failure is ok (well, not really, but it's not worse than what we started with) } @@ -929,7 +929,7 @@ bool CWallet::Verify() return false; } - if (boost::filesystem::exists(GetDataDir() / walletFile)) + if (fs::exists(GetDataDir() / walletFile)) { CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover); if (r == CDBEnv::RECOVER_OK) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 4896905d4..d432c7b84 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -6,6 +6,7 @@ #include "wallet/walletdb.h" #include "consensus/validation.h" +#include "fs.h" #include "key_io.h" #include "main.h" #include "protocol.h" @@ -16,7 +17,6 @@ #include "wallet/wallet.h" #include "zcash/Proof.hpp" -#include #include #include @@ -1169,16 +1169,16 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) bitdb.mapFileUseCount.erase(wallet.strWalletFile); // Copy wallet file - boost::filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; - boost::filesystem::path pathDest(strDest); - if (boost::filesystem::is_directory(pathDest)) + fs::path pathSrc = GetDataDir() / wallet.strWalletFile; + fs::path pathDest(strDest); + if (fs::is_directory(pathDest)) pathDest /= wallet.strWalletFile; try { - boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); + fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists); LogPrintf("copied %s to %s\n", wallet.strWalletFile, pathDest.string()); return true; - } catch (const boost::filesystem::filesystem_error& e) { + } catch (const fs::filesystem_error& e) { LogPrintf("error copying %s to %s - %s\n", wallet.strWalletFile, pathDest.string(), e.what()); return false; } diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index d4b50d8f8..13013ba0f 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "coins.h" #include "util.h" @@ -517,7 +516,7 @@ double benchmark_connectblock_slow() // Test for issue 2017-05-01.a SelectParams(CBaseChainParams::MAIN); CBlock block; - FILE* fp = fopen((GetDataDir() / "benchmark/block-107134.dat").string().c_str(), "rb"); + FILE* fp = fsbridge::fopen(GetDataDir() / "benchmark/block-107134.dat", "rb"); if (!fp) throw new std::runtime_error("Failed to open block data file"); CAutoFile blkFile(fp, SER_DISK, CLIENT_VERSION); blkFile >> block; From 7dbc9a6e490a9bc7dea7ac7218e87ff73e9ec9d3 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 17:52:19 +0000 Subject: [PATCH 025/146] Remove Bitcoin release notes --- .../release-notes-0.10.0.md | 762 ------------------ .../release-notes-0.10.1.md | 143 ---- .../release-notes-0.10.2.md | 86 -- .../release-notes-0.10.3.md | 165 ---- .../release-notes-0.11.1.md | 172 ---- .../release-notes-0.11.2.md | 217 ----- .../release-notes-0.3.12.md | 13 - .../release-notes-0.3.13.md | 26 - .../release-notes-0.3.14.md | 11 - .../release-notes-0.3.15.md | 6 - .../release-notes-0.3.16.md | 1 - .../release-notes-0.3.17.md | 12 - .../release-notes-0.3.18.md | 11 - .../release-notes-0.3.19.md | 9 - .../release-notes-0.3.20.1.md | 1 - .../release-notes-0.3.20.2.md | 17 - .../release-notes-0.3.20.md | 22 - .../release-notes-0.3.21.md | 20 - .../release-notes-0.3.22.md | 16 - .../release-notes-0.3.23.md | 10 - .../release-notes-0.3.24.md | 20 - .../release-notes-0.4.0.md | 70 -- .../release-notes-0.4.1.md | 38 - .../release-notes-0.4.2.md | 1 - .../release-notes-0.4.3.md | 21 - .../release-notes-0.4.4.md | 30 - .../release-notes-0.4.5.md | 1 - .../release-notes-0.4.6.md | 37 - .../release-notes-0.5.0.md | 70 -- .../release-notes-0.5.1.md | 43 - .../release-notes-0.5.2.md | 22 - .../release-notes-0.5.3.md | 42 - .../release-notes-0.5.4.md | 39 - .../release-notes-0.5.5.md | 37 - .../release-notes-0.6.0.md | 138 ---- .../release-notes-0.6.1.md | 2 - .../release-notes-0.6.2.md | 50 -- .../release-notes-0.6.3.md | 29 - .../release-notes-0.7.0.md | 169 ---- .../release-notes-0.7.1.md | 110 --- .../release-notes-0.7.2.md | 68 -- .../release-notes-0.8.0.md | 139 ---- .../release-notes-0.8.1.md | 22 - .../release-notes-0.8.2.md | 137 ---- .../release-notes-0.8.3.md | 18 - .../release-notes-0.8.4.md | 83 -- .../release-notes-0.8.5.md | 44 - .../release-notes-0.8.6.md | 66 -- .../release-notes-0.9.0.md | 411 ---------- .../release-notes-0.9.1.md | 53 -- .../release-notes-0.9.2.1.md | 207 ----- .../release-notes-0.9.2.md | 207 ----- .../release-notes-0.9.3.md | 101 --- .../release-notes-0.9.4.md | 95 --- .../release-notes-0.9.5.md | 60 -- 55 files changed, 4400 deletions(-) delete mode 100644 doc/bitcoin-release-notes/release-notes-0.10.0.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.10.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.10.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.10.3.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.11.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.11.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.12.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.13.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.14.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.15.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.16.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.17.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.18.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.19.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.20.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.20.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.20.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.21.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.22.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.23.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.3.24.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.4.0.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.4.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.4.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.4.3.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.4.4.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.4.5.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.4.6.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.5.0.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.5.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.5.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.5.3.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.5.4.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.5.5.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.6.0.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.6.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.6.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.6.3.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.7.0.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.7.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.7.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.8.0.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.8.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.8.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.8.3.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.8.4.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.8.5.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.8.6.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.9.0.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.9.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.9.2.1.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.9.2.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.9.3.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.9.4.md delete mode 100644 doc/bitcoin-release-notes/release-notes-0.9.5.md diff --git a/doc/bitcoin-release-notes/release-notes-0.10.0.md b/doc/bitcoin-release-notes/release-notes-0.10.0.md deleted file mode 100644 index 986b8832e..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.10.0.md +++ /dev/null @@ -1,762 +0,0 @@ -Bitcoin Core version 0.10.0 is now available from: - - https://bitcoin.org/bin/0.10.0/ - -This is a new major version release, bringing both new features and -bug fixes. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -Upgrading and downgrading -========================= - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -Downgrading warning ---------------------- - -Because release 0.10.0 makes use of headers-first synchronization and parallel -block download (see further), the block files and databases are not -backwards-compatible with older versions of Bitcoin Core or other software: - -* Blocks will be stored on disk out of order (in the order they are -received, really), which makes it incompatible with some tools or -other programs. Reindexing using earlier versions will also not work -anymore as a result of this. - -* The block index database will now hold headers for which no block is -stored on disk, which earlier versions won't support. - -If you want to be able to downgrade smoothly, make a backup of your entire data -directory. Without this your node will need start syncing (or importing from -bootstrap.dat) anew afterwards. It is possible that the data from a completely -synchronised 0.10 node may be usable in older versions as-is, but this is not -supported and may break as soon as the older version attempts to reindex. - -This does not affect wallet forward or backward compatibility. - - -Notable changes -=============== - -Faster synchronization ----------------------- - -Bitcoin Core now uses 'headers-first synchronization'. This means that we first -ask peers for block headers (a total of 27 megabytes, as of December 2014) and -validate those. In a second stage, when the headers have been discovered, we -download the blocks. However, as we already know about the whole chain in -advance, the blocks can be downloaded in parallel from all available peers. - -In practice, this means a much faster and more robust synchronization. On -recent hardware with a decent network link, it can be as little as 3 hours -for an initial full synchronization. You may notice a slower progress in the -very first few minutes, when headers are still being fetched and verified, but -it should gain speed afterwards. - -A few RPCs were added/updated as a result of this: -- `getblockchaininfo` now returns the number of validated headers in addition to -the number of validated blocks. -- `getpeerinfo` lists both the number of blocks and headers we know we have in -common with each peer. While synchronizing, the heights of the blocks that we -have requested from peers (but haven't received yet) are also listed as -'inflight'. -- A new RPC `getchaintips` lists all known branches of the block chain, -including those we only have headers for. - -Transaction fee changes ------------------------ - -This release automatically estimates how high a transaction fee (or how -high a priority) transactions require to be confirmed quickly. The default -settings will create transactions that confirm quickly; see the new -'txconfirmtarget' setting to control the tradeoff between fees and -confirmation times. Fees are added by default unless the 'sendfreetransactions' -setting is enabled. - -Prior releases used hard-coded fees (and priorities), and would -sometimes create transactions that took a very long time to confirm. - -Statistics used to estimate fees and priorities are saved in the -data directory in the `fee_estimates.dat` file just before -program shutdown, and are read in at startup. - -New command line options for transaction fee changes: -- `-txconfirmtarget=n` : create transactions that have enough fees (or priority) -so they are likely to begin confirmation within n blocks (default: 1). This setting -is over-ridden by the -paytxfee option. -- `-sendfreetransactions` : Send transactions as zero-fee transactions if possible -(default: 0) - -New RPC commands for fee estimation: -- `estimatefee nblocks` : Returns approximate fee-per-1,000-bytes needed for -a transaction to begin confirmation within nblocks. Returns -1 if not enough -transactions have been observed to compute a good estimate. -- `estimatepriority nblocks` : Returns approximate priority needed for -a zero-fee transaction to begin confirmation within nblocks. Returns -1 if not -enough free transactions have been observed to compute a good -estimate. - -RPC access control changes --------------------------- - -Subnet matching for the purpose of access control is now done -by matching the binary network address, instead of with string wildcard matching. -For the user this means that `-rpcallowip` takes a subnet specification, which can be - -- a single IP address (e.g. `1.2.3.4` or `fe80::0012:3456:789a:bcde`) -- a network/CIDR (e.g. `1.2.3.0/24` or `fe80::0000/64`) -- a network/netmask (e.g. `1.2.3.4/255.255.255.0` or `fe80::0012:3456:789a:bcde/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff`) - -An arbitrary number of `-rpcallow` arguments can be given. An incoming connection will be accepted if its origin address -matches one of them. - -For example: - -| 0.9.x and before | 0.10.x | -|--------------------------------------------|---------------------------------------| -| `-rpcallowip=192.168.1.1` | `-rpcallowip=192.168.1.1` (unchanged) | -| `-rpcallowip=192.168.1.*` | `-rpcallowip=192.168.1.0/24` | -| `-rpcallowip=192.168.*` | `-rpcallowip=192.168.0.0/16` | -| `-rpcallowip=*` (dangerous!) | `-rpcallowip=::/0` (still dangerous!) | - -Using wildcards will result in the rule being rejected with the following error in debug.log: - - Error: Invalid -rpcallowip subnet specification: *. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). - - -REST interface --------------- - -A new HTTP API is exposed when running with the `-rest` flag, which allows -unauthenticated access to public node data. - -It is served on the same port as RPC, but does not need a password, and uses -plain HTTP instead of JSON-RPC. - -Assuming a local RPC server running on port 8332, it is possible to request: -- Blocks: http://localhost:8332/rest/block/*HASH*.*EXT* -- Blocks without transactions: http://localhost:8332/rest/block/notxdetails/*HASH*.*EXT* -- Transactions (requires `-txindex`): http://localhost:8332/rest/tx/*HASH*.*EXT* - -In every case, *EXT* can be `bin` (for raw binary data), `hex` (for hex-encoded -binary) or `json`. - -For more details, see the `doc/REST-interface.md` document in the repository. - -RPC Server "Warm-Up" Mode -------------------------- - -The RPC server is started earlier now, before most of the expensive -intialisations like loading the block index. It is available now almost -immediately after starting the process. However, until all initialisations -are done, it always returns an immediate error with code -28 to all calls. - -This new behaviour can be useful for clients to know that a server is already -started and will be available soon (for instance, so that they do not -have to start it themselves). - -Improved signing security -------------------------- - -For 0.10 the security of signing against unusual attacks has been -improved by making the signatures constant time and deterministic. - -This change is a result of switching signing to use libsecp256k1 -instead of OpenSSL. Libsecp256k1 is a cryptographic library -optimized for the curve Bitcoin uses which was created by Bitcoin -Core developer Pieter Wuille. - -There exist attacks[1] against most ECC implementations where an -attacker on shared virtual machine hardware could extract a private -key if they could cause a target to sign using the same key hundreds -of times. While using shared hosts and reusing keys are inadvisable -for other reasons, it's a better practice to avoid the exposure. - -OpenSSL has code in their source repository for derandomization -and reduction in timing leaks that we've eagerly wanted to use for a -long time, but this functionality has still not made its -way into a released version of OpenSSL. Libsecp256k1 achieves -significantly stronger protection: As far as we're aware this is -the only deployed implementation of constant time signing for -the curve Bitcoin uses and we have reason to believe that -libsecp256k1 is better tested and more thoroughly reviewed -than the implementation in OpenSSL. - -[1] https://eprint.iacr.org/2014/161.pdf - -Watch-only wallet support -------------------------- - -The wallet can now track transactions to and from wallets for which you know -all addresses (or scripts), even without the private keys. - -This can be used to track payments without needing the private keys online on a -possibly vulnerable system. In addition, it can help for (manual) construction -of multisig transactions where you are only one of the signers. - -One new RPC, `importaddress`, is added which functions similarly to -`importprivkey`, but instead takes an address or script (in hexadecimal) as -argument. After using it, outputs credited to this address or script are -considered to be received, and transactions consuming these outputs will be -considered to be sent. - -The following RPCs have optional support for watch-only: -`getbalance`, `listreceivedbyaddress`, `listreceivedbyaccount`, -`listtransactions`, `listaccounts`, `listsinceblock`, `gettransaction`. See the -RPC documentation for those methods for more information. - -Compared to using `getrawtransaction`, this mechanism does not require -`-txindex`, scales better, integrates better with the wallet, and is compatible -with future block chain pruning functionality. It does mean that all relevant -addresses need to added to the wallet before the payment, though. - -Consensus library ------------------ - -Starting from 0.10.0, the Bitcoin Core distribution includes a consensus library. - -The purpose of this library is to make the verification functionality that is -critical to Bitcoin's consensus available to other applications, e.g. to language -bindings such as [python-bitcoinlib](https://pypi.python.org/pypi/python-bitcoinlib) or -alternative node implementations. - -This library is called `libbitcoinconsensus.so` (or, `.dll` for Windows). -Its interface is defined in the C header [bitcoinconsensus.h](https://github.com/bitcoin/bitcoin/blob/0.10/src/script/bitcoinconsensus.h). - -In its initial version the API includes two functions: - -- `bitcoinconsensus_verify_script` verifies a script. It returns whether the indicated input of the provided serialized transaction -correctly spends the passed scriptPubKey under additional constraints indicated by flags -- `bitcoinconsensus_version` returns the API version, currently at an experimental `0` - -The functionality is planned to be extended to e.g. UTXO management in upcoming releases, but the interface -for existing methods should remain stable. - -Standard script rules relaxed for P2SH addresses ------------------------------------------------- - -The IsStandard() rules have been almost completely removed for P2SH -redemption scripts, allowing applications to make use of any valid -script type, such as "n-of-m OR y", hash-locked oracle addresses, etc. -While the Bitcoin protocol has always supported these types of script, -actually using them on mainnet has been previously inconvenient as -standard Bitcoin Core nodes wouldn't relay them to miners, nor would -most miners include them in blocks they mined. - -bitcoin-tx ----------- - -It has been observed that many of the RPC functions offered by bitcoind are -"pure functions", and operate independently of the bitcoind wallet. This -included many of the RPC "raw transaction" API functions, such as -createrawtransaction. - -bitcoin-tx is a newly introduced command line utility designed to enable easy -manipulation of bitcoin transactions. A summary of its operation may be -obtained via "bitcoin-tx --help" Transactions may be created or signed in a -manner similar to the RPC raw tx API. Transactions may be updated, deleting -inputs or outputs, or appending new inputs and outputs. Custom scripts may be -easily composed using a simple text notation, borrowed from the bitcoin test -suite. - -This tool may be used for experimenting with new transaction types, signing -multi-party transactions, and many other uses. Long term, the goal is to -deprecate and remove "pure function" RPC API calls, as those do not require a -server round-trip to execute. - -Other utilities "bitcoin-key" and "bitcoin-script" have been proposed, making -key and script operations easily accessible via command line. - -Mining and relay policy enhancements ------------------------------------- - -Bitcoin Core's block templates are now for version 3 blocks only, and any mining -software relying on its `getblocktemplate` must be updated in parallel to use -libblkmaker either version 0.4.2 or any version from 0.5.1 onward. -If you are solo mining, this will affect you the moment you upgrade Bitcoin -Core, which must be done prior to BIP66 achieving its 951/1001 status. -If you are mining with the stratum mining protocol: this does not affect you. -If you are mining with the getblocktemplate protocol to a pool: this will affect -you at the pool operator's discretion, which must be no later than BIP66 -achieving its 951/1001 status. - -The `prioritisetransaction` RPC method has been added to enable miners to -manipulate the priority of transactions on an individual basis. - -Bitcoin Core now supports BIP 22 long polling, so mining software can be -notified immediately of new templates rather than having to poll periodically. - -Support for BIP 23 block proposals is now available in Bitcoin Core's -`getblocktemplate` method. This enables miners to check the basic validity of -their next block before expending work on it, reducing risks of accidental -hardforks or mining invalid blocks. - -Two new options to control mining policy: -- `-datacarrier=0/1` : Relay and mine "data carrier" (OP_RETURN) transactions -if this is 1. -- `-datacarriersize=n` : Maximum size, in bytes, we consider acceptable for -"data carrier" outputs. - -The relay policy has changed to more properly implement the desired behavior of not -relaying free (or very low fee) transactions unless they have a priority above the -AllowFreeThreshold(), in which case they are relayed subject to the rate limiter. - -BIP 66: strict DER encoding for signatures ------------------------------------------- - -Bitcoin Core 0.10 implements BIP 66, which introduces block version 3, and a new -consensus rule, which prohibits non-DER signatures. Such transactions have been -non-standard since Bitcoin v0.8.0 (released in February 2013), but were -technically still permitted inside blocks. - -This change breaks the dependency on OpenSSL's signature parsing, and is -required if implementations would want to remove all of OpenSSL from the -consensus code. - -The same miner-voting mechanism as in BIP 34 is used: when 751 out of a -sequence of 1001 blocks have version number 3 or higher, the new consensus -rule becomes active for those blocks. When 951 out of a sequence of 1001 -blocks have version number 3 or higher, it becomes mandatory for all blocks. - -Backward compatibility with current mining software is NOT provided, thus miners -should read the first paragraph of "Mining and relay policy enhancements" above. - -0.10.0 Change log -================= - -Detailed release notes follow. This overview includes changes that affect external -behavior, not code moves, refactors or string updates. - -RPC: -- `f923c07` Support IPv6 lookup in bitcoin-cli even when IPv6 only bound on localhost -- `b641c9c` Fix addnode "onetry": Connect with OpenNetworkConnection -- `171ca77` estimatefee / estimatepriority RPC methods -- `b750cf1` Remove cli functionality from bitcoind -- `f6984e8` Add "chain" to getmininginfo, improve help in getblockchaininfo -- `99ddc6c` Add nLocalServices info to RPC getinfo -- `cf0c47b` Remove getwork() RPC call -- `2a72d45` prioritisetransaction -- `e44fea5` Add an option `-datacarrier` to allow users to disable relaying/mining data carrier transactions -- `2ec5a3d` Prevent easy RPC memory exhaustion attack -- `d4640d7` Added argument to getbalance to include watchonly addresses and fixed errors in balance calculation -- `83f3543` Added argument to listaccounts to include watchonly addresses -- `952877e` Showing 'involvesWatchonly' property for transactions returned by 'listtransactions' and 'listsinceblock'. It is only appended when the transaction involves a watchonly address -- `d7d5d23` Added argument to listtransactions and listsinceblock to include watchonly addresses -- `f87ba3d` added includeWatchonly argument to 'gettransaction' because it affects balance calculation -- `0fa2f88` added includedWatchonly argument to listreceivedbyaddress/...account -- `6c37f7f` `getrawchangeaddress`: fail when keypool exhausted and wallet locked -- `ff6a7af` getblocktemplate: longpolling support -- `c4a321f` Add peerid to getpeerinfo to allow correlation with the logs -- `1b4568c` Add vout to ListTransactions output -- `b33bd7a` Implement "getchaintips" RPC command to monitor blockchain forks -- `733177e` Remove size limit in RPC client, keep it in server -- `6b5b7cb` Categorize rpc help overview -- `6f2c26a` Closely track mempool byte total. Add "getmempoolinfo" RPC -- `aa82795` Add detailed network info to getnetworkinfo RPC -- `01094bd` Don't reveal whether password is <20 or >20 characters in RPC -- `57153d4` rpc: Compute number of confirmations of a block from block height -- `ff36cbe` getnetworkinfo: export local node's client sub-version string -- `d14d7de` SanitizeString: allow '(' and ')' -- `31d6390` Fixed setaccount accepting foreign address -- `b5ec5fe` update getnetworkinfo help with subversion -- `ad6e601` RPC additions after headers-first -- `33dfbf5` rpc: Fix leveldb iterator leak, and flush before `gettxoutsetinfo` -- `2aa6329` Enable customising node policy for datacarrier data size with a -datacarriersize option -- `f877aaa` submitblock: Use a temporary CValidationState to determine accurately the outcome of ProcessBlock -- `e69a587` submitblock: Support for returning specific rejection reasons -- `af82884` Add "warmup mode" for RPC server -- `e2655e0` Add unauthenticated HTTP REST interface to public blockchain data -- `683dc40` Disable SSLv3 (in favor of TLS) for the RPC client and server -- `44b4c0d` signrawtransaction: validate private key -- `9765a50` Implement BIP 23 Block Proposal -- `f9de17e` Add warning comment to getinfo - -Command-line options: -- `ee21912` Use netmasks instead of wildcards for IP address matching -- `deb3572` Add `-rpcbind` option to allow binding RPC port on a specific interface -- `96b733e` Add `-version` option to get just the version -- `1569353` Add `-stopafterblockimport` option -- `77cbd46` Let -zapwallettxes recover transaction meta data -- `1c750db` remove -tor compatibility code (only allow -onion) -- `4aaa017` rework help messages for fee-related options -- `4278b1d` Clarify error message when invalid -rpcallowip -- `6b407e4` -datadir is now allowed in config files -- `bdd5b58` Add option `-sysperms` to disable 077 umask (create new files with system default umask) -- `cbe39a3` Add "bitcoin-tx" command line utility and supporting modules -- `dbca89b` Trigger -alertnotify if network is upgrading without you -- `ad96e7c` Make -reindex cope with out-of-order blocks -- `16d5194` Skip reindexed blocks individually -- `ec01243` --tracerpc option for regression tests -- `f654f00` Change -genproclimit default to 1 -- `3c77714` Make -proxy set all network types, avoiding a connect leak -- `57be955` Remove -printblock, -printblocktree, and -printblockindex -- `ad3d208` remove -maxorphanblocks config parameter since it is no longer functional - -Block and transaction handling: -- `7a0e84d` ProcessGetData(): abort if a block file is missing from disk -- `8c93bf4` LoadBlockIndexDB(): Require block db reindex if any `blk*.dat` files are missing -- `77339e5` Get rid of the static chainMostWork (optimization) -- `4e0eed8` Allow ActivateBestChain to release its lock on cs_main -- `18e7216` Push cs_mains down in ProcessBlock -- `fa126ef` Avoid undefined behavior using CFlatData in CScript serialization -- `7f3b4e9` Relax IsStandard rules for pay-to-script-hash transactions -- `c9a0918` Add a skiplist to the CBlockIndex structure -- `bc42503` Use unordered_map for CCoinsViewCache with salted hash (optimization) -- `d4d3fbd` Do not flush the cache after every block outside of IBD (optimization) -- `ad08d0b` Bugfix: make CCoinsViewMemPool support pruned entries in underlying cache -- `5734d4d` Only remove actualy failed blocks from setBlockIndexValid -- `d70bc52` Rework block processing benchmark code -- `714a3e6` Only keep setBlockIndexValid entries that are possible improvements -- `ea100c7` Reduce maximum coinscache size during verification (reduce memory usage) -- `4fad8e6` Reject transactions with excessive numbers of sigops -- `b0875eb` Allow BatchWrite to destroy its input, reducing copying (optimization) -- `92bb6f2` Bypass reloading blocks from disk (optimization) -- `2e28031` Perform CVerifyDB on pcoinsdbview instead of pcoinsTip (reduce memory usage) -- `ab15b2e` Avoid copying undo data (optimization) -- `341735e` Headers-first synchronization -- `afc32c5` Fix rebuild-chainstate feature and improve its performance -- `e11b2ce` Fix large reorgs -- `ed6d1a2` Keep information about all block files in memory -- `a48f2d6` Abstract context-dependent block checking from acceptance -- `7e615f5` Fixed mempool sync after sending a transaction -- `51ce901` Improve chainstate/blockindex disk writing policy -- `a206950` Introduce separate flushing modes -- `9ec75c5` Add a locking mechanism to IsInitialBlockDownload to ensure it never goes from false to true -- `868d041` Remove coinbase-dependant transactions during reorg -- `723d12c` Remove txn which are invalidated by coinbase maturity during reorg -- `0cb8763` Check against MANDATORY flags prior to accepting to mempool -- `8446262` Reject headers that build on an invalid parent -- `008138c` Bugfix: only track UTXO modification after lookup - -P2P protocol and network code: -- `f80cffa` Do not trigger a DoS ban if SCRIPT_VERIFY_NULLDUMMY fails -- `c30329a` Add testnet DNS seed of Alex Kotenko -- `45a4baf` Add testnet DNS seed of Andreas Schildbach -- `f1920e8` Ping automatically every 2 minutes (unconditionally) -- `806fd19` Allocate receive buffers in on the fly -- `6ecf3ed` Display unknown commands received -- `aa81564` Track peers' available blocks -- `caf6150` Use async name resolving to improve net thread responsiveness -- `9f4da19` Use pong receive time rather than processing time -- `0127a9b` remove SOCKS4 support from core and GUI, use SOCKS5 -- `40f5cb8` Send rejects and apply DoS scoring for errors in direct block validation -- `dc942e6` Introduce whitelisted peers -- `c994d2e` prevent SOCKET leak in BindListenPort() -- `a60120e` Add built-in seeds for .onion -- `60dc8e4` Allow -onlynet=onion to be used -- `3a56de7` addrman: Do not propagate obviously poor addresses onto the network -- `6050ab6` netbase: Make SOCKS5 negotiation interruptible -- `604ee2a` Remove tx from AlreadyAskedFor list once we receive it, not when we process it -- `efad808` Avoid reject message feedback loops -- `71697f9` Separate protocol versioning from clientversion -- `20a5f61` Don't relay alerts to peers before version negotiation -- `b4ee0bd` Introduce preferred download peers -- `845c86d` Do not use third party services for IP detection -- `12a49ca` Limit the number of new addressses to accumulate -- `35e408f` Regard connection failures as attempt for addrman -- `a3a7317` Introduce 10 minute block download timeout -- `3022e7d` Require sufficent priority for relay of free transactions -- `58fda4d` Update seed IPs, based on bitcoin.sipa.be crawler data -- `18021d0` Remove bitnodes.io from dnsseeds. - -Validation: -- `6fd7ef2` Also switch the (unused) verification code to low-s instead of even-s -- `584a358` Do merkle root and txid duplicates check simultaneously -- `217a5c9` When transaction outputs exceed inputs, show the offending amounts so as to aid debugging -- `f74fc9b` Print input index when signature validation fails, to aid debugging -- `6fd59ee` script.h: set_vch() should shift a >32 bit value -- `d752ba8` Add SCRIPT_VERIFY_SIGPUSHONLY (BIP62 rule 2) (test only) -- `698c6ab` Add SCRIPT_VERIFY_MINIMALDATA (BIP62 rules 3 and 4) (test only) -- `ab9edbd` script: create sane error return codes for script validation and remove logging -- `219a147` script: check ScriptError values in script tests -- `0391423` Discourage NOPs reserved for soft-fork upgrades -- `98b135f` Make STRICTENC invalid pubkeys fail the script rather than the opcode -- `307f7d4` Report script evaluation failures in log and reject messages -- `ace39db` consensus: guard against openssl's new strict DER checks -- `12b7c44` Improve robustness of DER recoding code -- `76ce5c8` fail immediately on an empty signature - -Build system: -- `f25e3ad` Fix build in OS X 10.9 -- `65e8ba4` build: Switch to non-recursive make -- `460b32d` build: fix broken boost chrono check on some platforms -- `9ce0774` build: Fix windows configure when using --with-qt-libdir -- `ea96475` build: Add mention of --disable-wallet to bdb48 error messages -- `1dec09b` depends: add shared dependency builder -- `c101c76` build: Add --with-utils (bitcoin-cli and bitcoin-tx, default=yes). Help string consistency tweaks. Target sanity check fix -- `e432a5f` build: add option for reducing exports (v2) -- `6134b43` Fixing condition 'sabotaging' MSVC build -- `af0bd5e` osx: fix signing to make Gatekeeper happy (again) -- `a7d1f03` build: fix dynamic boost check when --with-boost= is used -- `d5fd094` build: fix qt test build when libprotobuf is in a non-standard path -- `2cf5f16` Add libbitcoinconsensus library -- `914868a` build: add a deterministic dmg signer -- `2d375fe` depends: bump openssl to 1.0.1k -- `b7a4ecc` Build: Only check for boost when building code that requires it - -Wallet: -- `b33d1f5` Use fee/priority estimates in wallet CreateTransaction -- `4b7b1bb` Sanity checks for estimates -- `c898846` Add support for watch-only addresses -- `d5087d1` Use script matching rather than destination matching for watch-only -- `d88af56` Fee fixes -- `a35b55b` Dont run full check every time we decrypt wallet -- `3a7c348` Fix make_change to not create half-satoshis -- `f606bb9` fix a possible memory leak in CWalletDB::Recover -- `870da77` fix possible memory leaks in CWallet::EncryptWallet -- `ccca27a` Watch-only fixes -- `9b1627d` [Wallet] Reduce minTxFee for transaction creation to 1000 satoshis -- `a53fd41` Deterministic signing -- `15ad0b5` Apply AreSane() checks to the fees from the network -- `11855c1` Enforce minRelayTxFee on wallet created tx and add a maxtxfee option - -GUI: -- `c21c74b` osx: Fix missing dock menu with qt5 -- `b90711c` Fix Transaction details shows wrong To: -- `516053c` Make links in 'About Bitcoin Core' clickable -- `bdc83e8` Ensure payment request network matches client network -- `65f78a1` Add GUI view of peer information -- `06a91d9` VerifyDB progress reporting -- `fe6bff2` Add BerkeleyDB version info to RPCConsole -- `b917555` PeerTableModel: Fix potential deadlock. #4296 -- `dff0e3b` Improve rpc console history behavior -- `95a9383` Remove CENT-fee-rule from coin control completely -- `56b07d2` Allow setting listen via GUI -- `d95ba75` Log messages with type>QtDebugMsg as non-debug -- `8969828` New status bar Unit Display Control and related changes -- `674c070` seed OpenSSL PNRG with Windows event data -- `509f926` Payment request parsing on startup now only changes network if a valid network name is specified -- `acd432b` Prevent balloon-spam after rescan -- `7007402` Implement SI-style (thin space) thoudands separator -- `91cce17` Use fixed-point arithmetic in amount spinbox -- `bdba2dd` Remove an obscure option no-one cares about -- `bd0aa10` Replace the temporary file hack currently used to change Bitcoin-Qt's dock icon (OS X) with a buffer-based solution -- `94e1b9e` Re-work overviewpage UI -- `8bfdc9a` Better looking trayicon -- `b197bf3` disable tray interactions when client model set to 0 -- `1c5f0af` Add column Watch-only to transactions list -- `21f139b` Fix tablet crash. closes #4854 -- `e84843c` Broken addresses on command line no longer trigger testnet -- `a49f11d` Change splash screen to normal window -- `1f9be98` Disable App Nap on OSX 10.9+ -- `27c3e91` Add proxy to options overridden if necessary -- `4bd1185` Allow "emergency" shutdown during startup -- `d52f072` Don't show wallet options in the preferences menu when running with -disablewallet -- `6093aa1` Qt: QProgressBar CPU-Issue workaround -- `0ed9675` [Wallet] Add global boolean whether to send free transactions (default=true) -- `ed3e5e4` [Wallet] Add global boolean whether to pay at least the custom fee (default=true) -- `e7876b2` [Wallet] Prevent user from paying a non-sense fee -- `c1c9d5b` Add Smartfee to GUI -- `e0a25c5` Make askpassphrase dialog behave more sanely -- `94b362d` On close of splashscreen interrupt verifyDB -- `b790d13` English translation update -- `8543b0d` Correct tooltip on address book page - -Tests: -- `b41e594` Fix script test handling of empty scripts -- `d3a33fc` Test CHECKMULTISIG with m == 0 and n == 0 -- `29c1749` Let tx (in)valid tests use any SCRIPT_VERIFY flag -- `6380180` Add rejection of non-null CHECKMULTISIG dummy values -- `21bf3d2` Add tests for BoostAsioToCNetAddr -- `b5ad5e7` Add Python test for -rpcbind and -rpcallowip -- `9ec0306` Add CODESEPARATOR/FindAndDelete() tests -- `75ebced` Added many rpc wallet tests -- `0193fb8` Allow multiple regression tests to run at once -- `92a6220` Hook up sanity checks -- `3820e01` Extend and move all crypto tests to crypto_tests.cpp -- `3f9a019` added list/get received by address/ account tests -- `a90689f` Remove timing-based signature cache unit test -- `236982c` Add skiplist unit tests -- `f4b00be` Add CChain::GetLocator() unit test -- `b45a6e8` Add test for getblocktemplate longpolling -- `cdf305e` Set -discover=0 in regtest framework -- `ed02282` additional test for OP_SIZE in script_valid.json -- `0072d98` script tests: BOOLAND, BOOLOR decode to integer -- `833ff16` script tests: values that overflow to 0 are true -- `4cac5db` script tests: value with trailing 0x00 is true -- `89101c6` script test: test case for 5-byte bools -- `d2d9dc0` script tests: add tests for CHECKMULTISIG limits -- `d789386` Add "it works" test for bitcoin-tx -- `df4d61e` Add bitcoin-tx tests -- `aa41ac2` Test IsPushOnly() with invalid push -- `6022b5d` Make `script_{valid,invalid}.json` validation flags configurable -- `8138cbe` Add automatic script test generation, and actual checksig tests -- `ed27e53` Add coins_tests with a large randomized CCoinViewCache test -- `9df9cf5` Make SCRIPT_VERIFY_STRICTENC compatible with BIP62 -- `dcb9846` Extend getchaintips RPC test -- `554147a` Ensure MINIMALDATA invalid tests can only fail one way -- `dfeec18` Test every numeric-accepting opcode for correct handling of the numeric minimal encoding rule -- `2b62e17` Clearly separate PUSHDATA and numeric argument MINIMALDATA tests -- `16d78bd` Add valid invert of invalid every numeric opcode tests -- `f635269` tests: enable alertnotify test for Windows -- `7a41614` tests: allow rpc-tests to get filenames for bitcoind and bitcoin-cli from the environment -- `5122ea7` tests: fix forknotify.py on windows -- `fa7f8cd` tests: remove old pull-tester scripts -- `7667850` tests: replace the old (unused since Travis) tests with new rpc test scripts -- `f4e0aef` Do signature-s negation inside the tests -- `1837987` Optimize -regtest setgenerate block generation -- `2db4c8a` Fix node ranges in the test framework -- `a8b2ce5` regression test only setmocktime RPC call -- `daf03e7` RPC tests: create initial chain with specific timestamps -- `8656dbb` Port/fix txnmall.sh regression test -- `ca81587` Test the exact order of CHECKMULTISIG sig/pubkey evaluation -- `7357893` Prioritize and display -testsafemode status in UI -- `f321d6b` Add key generation/verification to ECC sanity check -- `132ea9b` miner_tests: Disable checkpoints so they don't fail the subsidy-change test -- `bc6cb41` QA RPC tests: Add tests block block proposals -- `f67a9ce` Use deterministically generated script tests -- `11d7a7d` [RPC] add rpc-test for http keep-alive (persistent connections) -- `34318d7` RPC-test based on invalidateblock for mempool coinbase spends -- `76ec867` Use actually valid transactions for script tests -- `c8589bf` Add actual signature tests -- `e2677d7` Fix smartfees test for change to relay policy -- `263b65e` tests: run sanity checks in tests too - -Miscellaneous: -- `122549f` Fix incorrect checkpoint data for testnet3 -- `5bd02cf` Log used config file to debug.log on startup -- `68ba85f` Updated Debian example bitcoin.conf with config from wiki + removed some cruft and updated comments -- `e5ee8f0` Remove -beta suffix -- `38405ac` Add comment regarding experimental-use service bits -- `be873f6` Issue warning if collecting RandSeed data failed -- `8ae973c` Allocate more space if necessary in RandSeedAddPerfMon -- `675bcd5` Correct comment for 15-of-15 p2sh script size -- `fda3fed` libsecp256k1 integration -- `2e36866` Show nodeid instead of addresses in log (for anonymity) unless otherwise requested -- `cd01a5e` Enable paranoid corruption checks in LevelDB >= 1.16 -- `9365937` Add comment about never updating nTimeOffset past 199 samples -- `403c1bf` contrib: remove getwork-based pyminer (as getwork API call has been removed) -- `0c3e101` contrib: Added systemd .service file in order to help distributions integrate bitcoind -- `0a0878d` doc: Add new DNSseed policy -- `2887bff` Update coding style and add .clang-format -- `5cbda4f` Changed LevelDB cursors to use scoped pointers to ensure destruction when going out of scope -- `b4a72a7` contrib/linearize: split output files based on new-timestamp-year or max-file-size -- `e982b57` Use explicit fflush() instead of setvbuf() -- `234bfbf` contrib: Add init scripts and docs for Upstart and OpenRC -- `01c2807` Add warning about the merkle-tree algorithm duplicate txid flaw -- `d6712db` Also create pid file in non-daemon mode -- `772ab0e` contrib: use batched JSON-RPC in linarize-hashes (optimization) -- `7ab4358` Update bash-completion for v0.10 -- `6e6a36c` contrib: show pull # in prompt for github-merge script -- `5b9f842` Upgrade leveldb to 1.18, make chainstate databases compatible between ARM and x86 (issue #2293) -- `4e7c219` Catch UTXO set read errors and shutdown -- `867c600` Catch LevelDB errors during flush -- `06ca065` Fix CScriptID(const CScript& in) in empty script case - -Credits -======= - -Thanks to everyone who contributed to this release: - -- 21E14 -- Adam Weiss -- Aitor Pazos -- Alexander Jeng -- Alex Morcos -- Alon Muroch -- Andreas Schildbach -- Andrew Poelstra -- Andy Alness -- Ashley Holman -- Benedict Chan -- Ben Holden-Crowther -- Bryan Bishop -- BtcDrak -- Christian von Roques -- Clinton Christian -- Cory Fields -- Cozz Lovan -- daniel -- Daniel Kraft -- David Hill -- Derek701 -- dexX7 -- dllud -- Dominyk Tiller -- Doug -- elichai -- elkingtowa -- ENikS -- Eric Shaw -- Federico Bond -- Francis GASCHET -- Gavin Andresen -- Giuseppe Mazzotta -- Glenn Willen -- Gregory Maxwell -- gubatron -- HarryWu -- himynameismartin -- Huang Le -- Ian Carroll -- imharrywu -- Jameson Lopp -- Janusz Lenar -- JaSK -- Jeff Garzik -- JL2035 -- Johnathan Corgan -- Jonas Schnelli -- jtimon -- Julian Haight -- Kamil Domanski -- kazcw -- kevin -- kiwigb -- Kosta Zertsekel -- LongShao007 -- Luke Dashjr -- Mark Friedenbach -- Mathy Vanvoorden -- Matt Corallo -- Matthew Bogosian -- Micha -- Michael Ford -- Mike Hearn -- mrbandrews -- mruddy -- ntrgn -- Otto Allmendinger -- paveljanik -- Pavel Vasin -- Peter Todd -- phantomcircuit -- Philip Kaufmann -- Pieter Wuille -- pryds -- randy-waterhouse -- R E Broadley -- Rose Toomey -- Ross Nicoll -- Roy Badami -- Ruben Dario Ponticelli -- Rune K. Svendsen -- Ryan X. Charles -- Saivann -- sandakersmann -- SergioDemianLerner -- shshshsh -- sinetek -- Stuart Cardall -- Suhas Daftuar -- Tawanda Kembo -- Teran McKinney -- tm314159 -- Tom Harding -- Trevin Hofmann -- Whit J -- Wladimir J. van der Laan -- Yoichi Hirai -- Zak Wilcox - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). - diff --git a/doc/bitcoin-release-notes/release-notes-0.10.1.md b/doc/bitcoin-release-notes/release-notes-0.10.1.md deleted file mode 100644 index 8f59f1f68..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.10.1.md +++ /dev/null @@ -1,143 +0,0 @@ -Bitcoin Core version 0.10.1 is now available from: - - - -This is a new minor version release, bringing bug fixes and translation -updates. It is recommended to upgrade to this version. - -Please report bugs using the issue tracker at github: - - - -Upgrading and downgrading -========================= - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -Downgrade warning ------------------- - -Because release 0.10.0 and later makes use of headers-first synchronization and -parallel block download (see further), the block files and databases are not -backwards-compatible with pre-0.10 versions of Bitcoin Core or other software: - -* Blocks will be stored on disk out of order (in the order they are -received, really), which makes it incompatible with some tools or -other programs. Reindexing using earlier versions will also not work -anymore as a result of this. - -* The block index database will now hold headers for which no block is -stored on disk, which earlier versions won't support. - -If you want to be able to downgrade smoothly, make a backup of your entire data -directory. Without this your node will need start syncing (or importing from -bootstrap.dat) anew afterwards. It is possible that the data from a completely -synchronised 0.10 node may be usable in older versions as-is, but this is not -supported and may break as soon as the older version attempts to reindex. - -This does not affect wallet forward or backward compatibility. - -Notable changes -=============== - -This is a minor release and hence there are no notable changes. -For the notable changes in 0.10, refer to the release notes for the -0.10.0 release at https://github.com/bitcoin/bitcoin/blob/v0.10.0/doc/release-notes.md - -0.10.1 Change log -================= - -Detailed release notes follow. This overview includes changes that affect external -behavior, not code moves, refactors or string updates. - -RPC: -- `7f502be` fix crash: createmultisig and addmultisigaddress -- `eae305f` Fix missing lock in submitblock - -Block (database) and transaction handling: -- `1d2cdd2` Fix InvalidateBlock to add chainActive.Tip to setBlockIndexCandidates -- `c91c660` fix InvalidateBlock to repopulate setBlockIndexCandidates -- `002c8a2` fix possible block db breakage during re-index -- `a1f425b` Add (optional) consistency check for the block chain data structures -- `1c62e84` Keep mempool consistent during block-reorgs -- `57d1f46` Fix CheckBlockIndex for reindex -- `bac6fca` Set nSequenceId when a block is fully linked - -P2P protocol and network code: -- `78f64ef` don't trickle for whitelisted nodes -- `ca301bf` Reduce fingerprinting through timestamps in 'addr' messages. -- `200f293` Ignore getaddr messages on Outbound connections. -- `d5d8998` Limit message sizes before transfer -- `aeb9279` Better fingerprinting protection for non-main-chain getdatas. -- `cf0218f` Make addrman's bucket placement deterministic (countermeasure 1 against eclipse attacks, see http://cs-people.bu.edu/heilman/eclipse/) -- `0c6f334` Always use a 50% chance to choose between tried and new entries (countermeasure 2 against eclipse attacks) -- `214154e` Do not bias outgoing connections towards fresh addresses (countermeasure 2 against eclipse attacks) -- `aa587d4` Scale up addrman (countermeasure 6 against eclipse attacks) -- `139cd81` Cap nAttempts penalty at 8 and switch to pow instead of a division loop - -Validation: -- `d148f62` Acquire CCheckQueue's lock to avoid race condition - -Build system: -- `8752b5c` 0.10 fix for crashes on OSX 10.6 - -Wallet: -- N/A - -GUI: -- `2c08406` some mac specifiy cleanup (memory handling, unnecessary code) -- `81145a6` fix OSX dock icon window reopening -- `786cf72` fix a issue where "command line options"-action overwrite "Preference"-action (on OSX) - -Tests: -- `1117378` add RPC test for InvalidateBlock - -Miscellaneous: -- `c9e022b` Initialization: set Boost path locale in main thread -- `23126a0` Sanitize command strings before logging them. -- `323de27` Initialization: setup environment before starting Qt tests -- `7494e09` Initialization: setup environment before starting tests -- `df45564` Initialization: set fallback locale as environment variable - -Credits -======= - -Thanks to everyone who directly contributed to this release: - -- Alex Morcos -- Cory Fields -- dexX7 -- fsb4000 -- Gavin Andresen -- Gregory Maxwell -- Ivan Pustogarov -- Jonas Schnelli -- Matt Corallo -- mrbandrews -- Pieter Wuille -- Ruben de Vries -- Suhas Daftuar -- Wladimir J. van der Laan - -And all those who contributed additional code review and/or security research: -- 21E14 -- Alison Kendler -- Aviv Zohar -- Ethan Heilman -- Evil-Knievel -- fanquake -- Jeff Garzik -- Jonas Nick -- Luke Dashjr -- Patrick Strateman -- Philip Kaufmann -- Sergio Demian Lerner -- Sharon Goldberg - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/bitcoin-release-notes/release-notes-0.10.2.md b/doc/bitcoin-release-notes/release-notes-0.10.2.md deleted file mode 100644 index 192ed69d2..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.10.2.md +++ /dev/null @@ -1,86 +0,0 @@ -Bitcoin Core version 0.10.2 is now available from: - - - -This is a new minor version release, bringing minor bug fixes and translation -updates. It is recommended to upgrade to this version. - -Please report bugs using the issue tracker at github: - - - -Upgrading and downgrading -========================= - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -Downgrade warning ------------------- - -Because release 0.10.0 and later makes use of headers-first synchronization and -parallel block download (see further), the block files and databases are not -backwards-compatible with pre-0.10 versions of Bitcoin Core or other software: - -* Blocks will be stored on disk out of order (in the order they are -received, really), which makes it incompatible with some tools or -other programs. Reindexing using earlier versions will also not work -anymore as a result of this. - -* The block index database will now hold headers for which no block is -stored on disk, which earlier versions won't support. - -If you want to be able to downgrade smoothly, make a backup of your entire data -directory. Without this your node will need start syncing (or importing from -bootstrap.dat) anew afterwards. It is possible that the data from a completely -synchronised 0.10 node may be usable in older versions as-is, but this is not -supported and may break as soon as the older version attempts to reindex. - -This does not affect wallet forward or backward compatibility. - -Notable changes -=============== - -This fixes a serious problem on Windows with data directories that have non-ASCII -characters (https://github.com/bitcoin/bitcoin/issues/6078). - -For other platforms there are no notable changes. - -For the notable changes in 0.10, refer to the release notes -at https://github.com/bitcoin/bitcoin/blob/v0.10.0/doc/release-notes.md - -0.10.2 Change log -================= - -Detailed release notes follow. This overview includes changes that affect external -behavior, not code moves, refactors or string updates. - -Wallet: -- `824c011` fix boost::get usage with boost 1.58 - -Miscellaneous: -- `da65606` Avoid crash on start in TestBlockValidity with gen=1. -- `424ae66` don't imbue boost::filesystem::path with locale "C" on windows (fixes #6078) - -Credits -======= - -Thanks to everyone who directly contributed to this release: - -- Cory Fields -- Gregory Maxwell -- Jonas Schnelli -- Wladimir J. van der Laan - -And all those who contributed additional code review and/or security research: - -- dexX7 -- Pieter Wuille -- vayvanne - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/bitcoin-release-notes/release-notes-0.10.3.md b/doc/bitcoin-release-notes/release-notes-0.10.3.md deleted file mode 100644 index 8a110e562..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.10.3.md +++ /dev/null @@ -1,165 +0,0 @@ -Bitcoin Core version 0.10.3 is now available from: - - - -This is a new minor version release, bringing security fixes and translation -updates. It is recommended to upgrade to this version as soon as possible. - -Please report bugs using the issue tracker at github: - - - -Upgrading and downgrading -========================= - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -Downgrade warning ------------------- - -Because release 0.10.0 and later makes use of headers-first synchronization and -parallel block download (see further), the block files and databases are not -backwards-compatible with pre-0.10 versions of Bitcoin Core or other software: - -* Blocks will be stored on disk out of order (in the order they are -received, really), which makes it incompatible with some tools or -other programs. Reindexing using earlier versions will also not work -anymore as a result of this. - -* The block index database will now hold headers for which no block is -stored on disk, which earlier versions won't support. - -If you want to be able to downgrade smoothly, make a backup of your entire data -directory. Without this your node will need start syncing (or importing from -bootstrap.dat) anew afterwards. It is possible that the data from a completely -synchronised 0.10 node may be usable in older versions as-is, but this is not -supported and may break as soon as the older version attempts to reindex. - -This does not affect wallet forward or backward compatibility. - -Notable changes -=============== - -Fix buffer overflow in bundled upnp ------------------------------------- - -Bundled miniupnpc was updated to 1.9.20151008. This fixes a buffer overflow in -the XML parser during initial network discovery. - -Details can be found here: http://talosintel.com/reports/TALOS-2015-0035/ - -This applies to the distributed executables only, not when building from source or -using distribution provided packages. - -Additionally, upnp has been disabled by default. This may result in a lower -number of reachable nodes on IPv4, however this prevents future libupnpc -vulnerabilities from being a structural risk to the network -(see https://github.com/bitcoin/bitcoin/pull/6795). - -Test for LowS signatures before relaying ------------------------------------------ - -Make the node require the canonical 'low-s' encoding for ECDSA signatures when -relaying or mining. This removes a nuisance malleability vector. - -Consensus behavior is unchanged. - -If widely deployed this change would eliminate the last remaining known vector -for nuisance malleability on SIGHASH_ALL P2PKH transactions. On the down-side -it will block most transactions made by sufficiently out of date software. - -Unlike the other avenues to change txids on transactions this -one was randomly violated by all deployed bitcoin software prior to -its discovery. So, while other malleability vectors where made -non-standard as soon as they were discovered, this one has remained -permitted. Even BIP62 did not propose applying this rule to -old version transactions, but conforming implementations have become -much more common since BIP62 was initially written. - -Bitcoin Core has produced compatible signatures since a28fb70e in -September 2013, but this didn't make it into a release until 0.9 -in March 2014; Bitcoinj has done so for a similar span of time. -Bitcoinjs and electrum have been more recently updated. - -This does not replace the need for BIP62 or similar, as miners can -still cooperate to break transactions. Nor does it replace the -need for wallet software to handle malleability sanely[1]. This -only eliminates the cheap and irritating DOS attack. - -[1] On the Malleability of Bitcoin Transactions -Marcin Andrychowicz, Stefan Dziembowski, Daniel Malinowski, Łukasz Mazurek -http://fc15.ifca.ai/preproceedings/bitcoin/paper_9.pdf - -Minimum relay fee default increase ------------------------------------ - -The default for the `-minrelaytxfee` setting has been increased from `0.00001` -to `0.00005`. - -This is necessitated by the current transaction flooding, causing -outrageous memory usage on nodes due to the mempool ballooning. This is a -temporary measure, bridging the time until a dynamic method for determining -this fee is merged (which will be in 0.12). - -(see https://github.com/bitcoin/bitcoin/pull/6793, as well as the 0.11.0 -release notes, in which this value was suggested) - -0.10.3 Change log -================= - -Detailed release notes follow. This overview includes changes that affect external -behavior, not code moves, refactors or string updates. - -- #6186 `e4a7d51` Fix two problems in CSubnet parsing -- #6153 `ebd7d8d` Parameter interaction: disable upnp if -proxy set -- #6203 `ecc96f5` Remove P2SH coinbase flag, no longer interesting -- #6226 `181771b` json: fail read_string if string contains trailing garbage -- #6244 `09334e0` configure: Detect (and reject) LibreSSL -- #6276 `0fd8464` Fix getbalance * 0 -- #6274 `be64204` Add option `-alerts` to opt out of alert system -- #6319 `3f55638` doc: update mailing list address -- #6438 `7e66e9c` openssl: avoid config file load/race -- #6439 `255eced` Updated URL location of netinstall for Debian -- #6412 `0739e6e` Test whether created sockets are select()able -- #6694 `f696ea1` [QT] fix thin space word wrap line brake issue -- #6704 `743cc9e` Backport bugfixes to 0.10 -- #6769 `1cea6b0` Test LowS in standardness, removes nuisance malleability vector. -- #6789 `093d7b5` Update miniupnpc to 1.9.20151008 -- #6795 `f2778e0` net: Disable upnp by default -- #6797 `91ef4d9` Do not store more than 200 timedata samples -- #6793 `842c48d` Bump minrelaytxfee default - -Credits -======= - -Thanks to everyone who directly contributed to this release: - -- Adam Weiss -- Alex Morcos -- Casey Rodarmor -- Cory Fields -- fanquake -- Gregory Maxwell -- Jonas Schnelli -- J Ross Nicoll -- Luke Dashjr -- Pavel Vasin -- Pieter Wuille -- randy-waterhouse -- ฿tcDrak -- Tom Harding -- Veres Lajos -- Wladimir J. van der Laan - -And all those who contributed additional code review and/or security research: - -- timothy on IRC for reporting the issue -- Vulnerability in miniupnp discovered by Aleksandar Nikolic of Cisco Talos - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/bitcoin-release-notes/release-notes-0.11.1.md b/doc/bitcoin-release-notes/release-notes-0.11.1.md deleted file mode 100644 index 799205691..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.11.1.md +++ /dev/null @@ -1,172 +0,0 @@ -Bitcoin Core version 0.11.1 is now available from: - - - -This is a new minor version release, bringing security fixes. It is recommended -to upgrade to this version as soon as possible. - -Please report bugs using the issue tracker at github: - - - -Upgrading and downgrading -========================= - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -Downgrade warning ------------------- - -Because release 0.10.0 and later makes use of headers-first synchronization and -parallel block download (see further), the block files and databases are not -backwards-compatible with pre-0.10 versions of Bitcoin Core or other software: - -* Blocks will be stored on disk out of order (in the order they are -received, really), which makes it incompatible with some tools or -other programs. Reindexing using earlier versions will also not work -anymore as a result of this. - -* The block index database will now hold headers for which no block is -stored on disk, which earlier versions won't support. - -If you want to be able to downgrade smoothly, make a backup of your entire data -directory. Without this your node will need start syncing (or importing from -bootstrap.dat) anew afterwards. It is possible that the data from a completely -synchronised 0.10 node may be usable in older versions as-is, but this is not -supported and may break as soon as the older version attempts to reindex. - -This does not affect wallet forward or backward compatibility. There are no -known problems when downgrading from 0.11.x to 0.10.x. - -Notable changes -=============== - -Fix buffer overflow in bundled upnp ------------------------------------- - -Bundled miniupnpc was updated to 1.9.20151008. This fixes a buffer overflow in -the XML parser during initial network discovery. - -Details can be found here: http://talosintel.com/reports/TALOS-2015-0035/ - -This applies to the distributed executables only, not when building from source or -using distribution provided packages. - -Additionally, upnp has been disabled by default. This may result in a lower -number of reachable nodes on IPv4, however this prevents future libupnpc -vulnerabilities from being a structural risk to the network -(see https://github.com/bitcoin/bitcoin/pull/6795). - -Test for LowS signatures before relaying ------------------------------------------ - -Make the node require the canonical 'low-s' encoding for ECDSA signatures when -relaying or mining. This removes a nuisance malleability vector. - -Consensus behavior is unchanged. - -If widely deployed this change would eliminate the last remaining known vector -for nuisance malleability on SIGHASH_ALL P2PKH transactions. On the down-side -it will block most transactions made by sufficiently out of date software. - -Unlike the other avenues to change txids on transactions this -one was randomly violated by all deployed bitcoin software prior to -its discovery. So, while other malleability vectors where made -non-standard as soon as they were discovered, this one has remained -permitted. Even BIP62 did not propose applying this rule to -old version transactions, but conforming implementations have become -much more common since BIP62 was initially written. - -Bitcoin Core has produced compatible signatures since a28fb70e in -September 2013, but this didn't make it into a release until 0.9 -in March 2014; Bitcoinj has done so for a similar span of time. -Bitcoinjs and electrum have been more recently updated. - -This does not replace the need for BIP62 or similar, as miners can -still cooperate to break transactions. Nor does it replace the -need for wallet software to handle malleability sanely[1]. This -only eliminates the cheap and irritating DOS attack. - -[1] On the Malleability of Bitcoin Transactions -Marcin Andrychowicz, Stefan Dziembowski, Daniel Malinowski, Łukasz Mazurek -http://fc15.ifca.ai/preproceedings/bitcoin/paper_9.pdf - -Minimum relay fee default increase ------------------------------------ - -The default for the `-minrelaytxfee` setting has been increased from `0.00001` -to `0.00005`. - -This is necessitated by the current transaction flooding, causing -outrageous memory usage on nodes due to the mempool ballooning. This is a -temporary measure, bridging the time until a dynamic method for determining -this fee is merged (which will be in 0.12). - -(see https://github.com/bitcoin/bitcoin/pull/6793, as well as the 0.11 -release notes, in which this value was suggested) - -0.11.1 Change log -================= - -Detailed release notes follow. This overview includes changes that affect -behavior, not code moves, refactors and string updates. For convenience in locating -the code changes and accompanying discussion, both the pull request and -git merge commit are mentioned. - -- #6438 `2531438` openssl: avoid config file load/race -- #6439 `980f820` Updated URL location of netinstall for Debian -- #6384 `8e5a969` qt: Force TLS1.0+ for SSL connections -- #6471 `92401c2` Depends: bump to qt 5.5 -- #6224 `93b606a` Be even stricter in processing unrequested blocks -- #6571 `100ac4e` libbitcoinconsensus: avoid a crash in multi-threaded environments -- #6545 `649f5d9` Do not store more than 200 timedata samples. -- #6694 `834e299` [QT] fix thin space word wrap line break issue -- #6703 `1cd7952` Backport bugfixes to 0.11 -- #6750 `5ed8d0b` Recent rejects backport to v0.11 -- #6769 `71cc9d9` Test LowS in standardness, removes nuisance malleability vector. -- #6789 `b4ad73f` Update miniupnpc to 1.9.20151008 -- #6785 `b4dc33e` Backport to v0.11: In (strCommand == "tx"), return if AlreadyHave() -- #6412 `0095b9a` Test whether created sockets are select()able -- #6795 `4dbcec0` net: Disable upnp by default -- #6793 `e7bcc4a` Bump minrelaytxfee default - -Credits -======= - -Thanks to everyone who directly contributed to this release: - -- Adam Weiss -- Alex Morcos -- Casey Rodarmor -- Cory Fields -- fanquake -- Gregory Maxwell -- Jonas Schnelli -- J Ross Nicoll -- Luke Dashjr -- Pavel Janík -- Pavel Vasin -- Peter Todd -- Pieter Wuille -- randy-waterhouse -- Ross Nicoll -- Suhas Daftuar -- tailsjoin -- ฿tcDrak -- Tom Harding -- Veres Lajos -- Wladimir J. van der Laan - -And those who contributed additional code review and/or security research: - -- timothy on IRC for reporting the issue -- Vulnerability in miniupnp discovered by Aleksandar Nikolic of Cisco Talos - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). - diff --git a/doc/bitcoin-release-notes/release-notes-0.11.2.md b/doc/bitcoin-release-notes/release-notes-0.11.2.md deleted file mode 100644 index 2351b8065..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.11.2.md +++ /dev/null @@ -1,217 +0,0 @@ -Bitcoin Core version 0.11.2 is now available from: - - - -This is a new minor version release, bringing bug fixes, the BIP65 -(CLTV) consensus change, and relay policy preparation for BIP113. It is -recommended to upgrade to this version as soon as possible. - -Please report bugs using the issue tracker at github: - - - -Upgrading and downgrading -========================= - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -Downgrade warning ------------------- - -Because release 0.10.0 and later makes use of headers-first synchronization and -parallel block download (see further), the block files and databases are not -backwards-compatible with pre-0.10 versions of Bitcoin Core or other software: - -* Blocks will be stored on disk out of order (in the order they are -received, really), which makes it incompatible with some tools or -other programs. Reindexing using earlier versions will also not work -anymore as a result of this. - -* The block index database will now hold headers for which no block is -stored on disk, which earlier versions won't support. - -If you want to be able to downgrade smoothly, make a backup of your entire data -directory. Without this your node will need start syncing (or importing from -bootstrap.dat) anew afterwards. It is possible that the data from a completely -synchronised 0.10 node may be usable in older versions as-is, but this is not -supported and may break as soon as the older version attempts to reindex. - -This does not affect wallet forward or backward compatibility. There are no -known problems when downgrading from 0.11.x to 0.10.x. - -Notable changes since 0.11.1 -============================ - -BIP65 soft fork to enforce OP_CHECKLOCKTIMEVERIFY opcode --------------------------------------------------------- - -This release includes several changes related to the [BIP65][] soft fork -which redefines the existing OP_NOP2 opcode as OP_CHECKLOCKTIMEVERIFY -(CLTV) so that a transaction output can be made unspendable until a -specified point in the future. - -1. This release will only relay and mine transactions spending a CLTV - output if they comply with the BIP65 rules as provided in code. - -2. This release will produce version 4 blocks by default. Please see the - *notice to miners* below. - -3. Once 951 out of a sequence of 1,001 blocks on the local node's best block - chain contain version 4 (or higher) blocks, this release will no - longer accept new version 3 blocks and it will only accept version 4 - blocks if they comply with the BIP65 rules for CLTV. - -For more information about the soft-forking change, please see - - -Graphs showing the progress towards block version 4 adoption may be -found at the URLs below: - -- Block versions over the last 50,000 blocks as progress towards BIP65 - consensus enforcement: - -- Block versions over the last 2,000 blocks showing the days to the - earliest possible BIP65 consensus-enforced block: - -**Notice to miners:** Bitcoin Core’s block templates are now for -version 4 blocks only, and any mining software relying on its -getblocktemplate must be updated in parallel to use libblkmaker either -version 0.4.3 or any version from 0.5.2 onward. - -- If you are solo mining, this will affect you the moment you upgrade - Bitcoin Core, which must be done prior to BIP65 achieving its 951/1001 - status. - -- If you are mining with the stratum mining protocol: this does not - affect you. - -- If you are mining with the getblocktemplate protocol to a pool: this - will affect you at the pool operator’s discretion, which must be no - later than BIP65 achieving its 951/1001 status. - -[BIP65]: https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki - -BIP113 mempool-only locktime enforcement using GetMedianTimePast() ----------------------------------------------------------------- - -Bitcoin transactions currently may specify a locktime indicating when -they may be added to a valid block. Current consensus rules require -that blocks have a block header time greater than the locktime specified -in any transaction in that block. - -Miners get to choose what time they use for their header time, with the -consensus rule being that no node will accept a block whose time is more -than two hours in the future. This creates a incentive for miners to -set their header times to future values in order to include locktimed -transactions which weren't supposed to be included for up to two more -hours. - -The consensus rules also specify that valid blocks may have a header -time greater than that of the median of the 11 previous blocks. This -GetMedianTimePast() time has a key feature we generally associate with -time: it can't go backwards. - -[BIP113][] specifies a soft fork (**not enforced in this release**) that -weakens this perverse incentive for individual miners to use a future -time by requiring that valid blocks have a computed GetMedianTimePast() -greater than the locktime specified in any transaction in that block. - -Mempool inclusion rules currently require transactions to be valid for -immediate inclusion in a block in order to be accepted into the mempool. -This release begins applying the BIP113 rule to received transactions, -so transaction whose time is greater than the GetMedianTimePast() will -no longer be accepted into the mempool. - -**Implication for miners:** you will begin rejecting transactions that -would not be valid under BIP113, which will prevent you from producing -invalid blocks if/when BIP113 is enforced on the network. Any -transactions which are valid under the current rules but not yet valid -under the BIP113 rules will either be mined by other miners or delayed -until they are valid under BIP113. Note, however, that time-based -locktime transactions are more or less unseen on the network currently. - -**Implication for users:** GetMedianTimePast() always trails behind the -current time, so a transaction locktime set to the present time will be -rejected by nodes running this release until the median time moves -forward. To compensate, subtract one hour (3,600 seconds) from your -locktimes to allow those transactions to be included in mempools at -approximately the expected time. - -[BIP113]: https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki - -Windows bug fix for corrupted UTXO database on unclean shutdowns ----------------------------------------------------------------- - -Several Windows users reported that they often need to reindex the -entire blockchain after an unclean shutdown of Bitcoin Core on Windows -(or an unclean shutdown of Windows itself). Although unclean shutdowns -remain unsafe, this release no longer relies on memory-mapped files for -the UTXO database, which significantly reduced the frequency of unclean -shutdowns leading to required reindexes during testing. - -For more information, see: - -Other fixes for database corruption on Windows are expected in the -next major release. - -0.11.2 Change log -================= - -Detailed release notes follow. This overview includes changes that affect -behavior, not code moves, refactors and string updates. For convenience in locating -the code changes and accompanying discussion, both the pull request and -git merge commit are mentioned. - -- #6124 `684636b` Make CScriptNum() take nMaxNumSize as an argument -- #6124 `4fa7a04` Replace NOP2 with CHECKLOCKTIMEVERIFY (BIP65) -- #6124 `6ea5ca4` Enable CHECKLOCKTIMEVERIFY as a standard script verify flag -- #6351 `5e82e1c` Add CHECKLOCKTIMEVERIFY (BIP65) soft-fork logic -- #6353 `ba1da90` Show softfork status in getblockchaininfo -- #6351 `6af25b0` Add BIP65 to getblockchaininfo softforks list -- #6688 `01878c9` Fix locking in GetTransaction -- #6653 `b3eaa30` [Qt] Raise debug window when requested -- #6600 `1e672ae` Debian/Ubuntu: Include bitcoin-tx binary -- #6600 `2394f4d` Debian/Ubuntu: Split bitcoin-tx into its own package -- #5987 `33d6825` Bugfix: Allow mining on top of old tip blocks for testnet -- #6852 `21e58b8` build: make sure OpenSSL heeds noexecstack -- #6846 `af6edac` alias `-h` for `--help` -- #6867 `95a5039` Set TCP_NODELAY on P2P sockets. -- #6856 `dfe55bd` Do not allow blockfile pruning during reindex. -- #6566 `a1d3c6f` Add rules--presently disabled--for using GetMedianTimePast as end point for lock-time calculations -- #6566 `f720c5f` Enable policy enforcing GetMedianTimePast as the end point of lock-time constraints -- #6917 `0af5b8e` leveldb: Win32WritableFile without memory mapping -- #6948 `4e895b0` Always flush block and undo when switching to new file - -Credits -======= - -Thanks to everyone who directly contributed to this release: - -- Alex Morcos -- ฿tcDrak -- Chris Kleeschulte -- Daniel Cousens -- Diego Viola -- Eric Lombrozo -- Esteban Ordano -- Gregory Maxwell -- Luke Dashjr -- Marco Falke -- Mark Friedenbach -- Matt Corallo -- Micha -- Mitchell Cash -- Peter Todd -- Pieter Wuille -- Wladimir J. van der Laan -- Zak Wilcox - -And those who contributed additional code review and/or security research. - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/bitcoin-release-notes/release-notes-0.3.12.md b/doc/bitcoin-release-notes/release-notes-0.3.12.md deleted file mode 100644 index 38715bc75..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.12.md +++ /dev/null @@ -1,13 +0,0 @@ -Version 0.3.12 is now available. - -Features: -* json-rpc errors return a more standard error object. (thanks to Gavin Andresen) -* json-rpc command line returns exit codes. -* json-rpc "backupwallet" command. -* Recovers and continues if an exception is caused by a message you received. Other nodes shouldn't be able to cause an exception, and it hasn't happened before, but if a way is found to cause an exception, this would keep it from being used to stop network nodes. - -If you have json-rpc code that checks the contents of the error string, you need to change it to expect error objects of the form {"code":,"message":}, which is the standard. See this thread: -http://www.bitcoin.org/smf/index.php?topic=969.0 - -Download: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.3.12/ diff --git a/doc/bitcoin-release-notes/release-notes-0.3.13.md b/doc/bitcoin-release-notes/release-notes-0.3.13.md deleted file mode 100644 index 2b95ff233..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.13.md +++ /dev/null @@ -1,26 +0,0 @@ -Version 0.3.13 is now available. You should upgrade to prevent potential problems with 0/unconfirmed transactions. Note: 0.3.13 prevents problems if you haven't already spent a 0/unconfirmed transaction, but if that already happened, you need 0.3.13.2. - -Changes: -* Don't count or spend payments until they have 1 confirmation. -* Internal version number from 312 to 31300. -* Only accept transactions sent by IP address if -allowreceivebyip is specified. -* Dropped DB_PRIVATE Berkeley DB flag. -* Fix problem sending the last cent with sub-cent fractional change. -* Auto-detect whether to use 128-bit 4-way SSE2 on Linux. -Gavin Andresen: -* Option -rpcallowip= to accept json-rpc connections from another machine. -* Clean shutdown on SIGTERM on Linux. - -Download: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.3.13/ - -(Thanks Laszlo for the Mac OSX build!) - -Note: -The SSE2 auto-detect in the Linux 64-bit version doesn't work with AMD in 64-bit mode. Please try this instead and let me know if it gets it right: -http://www.bitcoin.org/download/bitcoin-0.3.13.1-specialbuild-linux64.tar.gz - -You can still control the SSE2 use manually with -4way and -4way=0. - -Version 0.3.13.2 (SVN rev 161) has improvements for the case where you already had 0/unconfirmed transactions that you might have already spent. Here's a Windows build of it: -http://www.bitcoin.org/download/bitcoin-0.3.13.2-win32-setup.exe diff --git a/doc/bitcoin-release-notes/release-notes-0.3.14.md b/doc/bitcoin-release-notes/release-notes-0.3.14.md deleted file mode 100644 index e73052ed9..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.14.md +++ /dev/null @@ -1,11 +0,0 @@ -Version 0.3.14 is now available -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.3.14/ - -Changes: -* Key pool feature for safer wallet backup -Gavin Andresen: -* TEST network mode with switch -testnet -* Option to use SSL for JSON-RPC connections on unix/osx -* validateaddress RPC command -eurekafag: -* Russian translation diff --git a/doc/bitcoin-release-notes/release-notes-0.3.15.md b/doc/bitcoin-release-notes/release-notes-0.3.15.md deleted file mode 100644 index b98052ef5..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.15.md +++ /dev/null @@ -1,6 +0,0 @@ -* paytxfee switch is now per KB, so it adds the correct fee for large transactions -* sending avoids using coins with less than 6 confirmations if it can -* BitcoinMiner processes transactions in priority order based on age of dependencies -* make sure generation doesn't start before block 74000 downloaded -* bugfixes by Dean Gores -* testnet, keypoololdest and paytxfee added to getinfo diff --git a/doc/bitcoin-release-notes/release-notes-0.3.16.md b/doc/bitcoin-release-notes/release-notes-0.3.16.md deleted file mode 100644 index 743f84f30..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.16.md +++ /dev/null @@ -1 +0,0 @@ -Never released. diff --git a/doc/bitcoin-release-notes/release-notes-0.3.17.md b/doc/bitcoin-release-notes/release-notes-0.3.17.md deleted file mode 100644 index d3604b8f8..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.17.md +++ /dev/null @@ -1,12 +0,0 @@ -Version 0.3.17 is now available. - -Changes: -* new getwork, thanks m0mchil -* added transaction fee setting in UI options menu -* free transaction limits -* sendtoaddress returns transaction id instead of "sent" -* getaccountaddress - -The UI transaction fee setting was easy since it was still there from 0.1.5 and all I had to do was re-enable it. - -The accounts-based commands: move, sendfrom and getbalance will be in the next release. We still have some more changes to make first. diff --git a/doc/bitcoin-release-notes/release-notes-0.3.18.md b/doc/bitcoin-release-notes/release-notes-0.3.18.md deleted file mode 100644 index ab1c2e069..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.18.md +++ /dev/null @@ -1,11 +0,0 @@ -Changes: -* Fixed a wallet.dat compatibility problem if you downgraded from 0.3.17 and then upgraded again -* IsStandard() check to only include known transaction types in blocks -* Jgarzik's optimisation to speed up the initial block download a little - -The main addition in this release is the Accounts-Based JSON-RPC commands that Gavin's been working on (more details at http://www.bitcoin.org/smf/index.php?topic=1886.0). -* getaccountaddress -* sendfrom -* move -* getbalance -* listtransactions diff --git a/doc/bitcoin-release-notes/release-notes-0.3.19.md b/doc/bitcoin-release-notes/release-notes-0.3.19.md deleted file mode 100644 index fcd867561..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.19.md +++ /dev/null @@ -1,9 +0,0 @@ -There's more work to do on DoS, but I'm doing a quick build of what I have so far in case it's needed, before venturing into more complex ideas. The build for this is version 0.3.19. - -- Added some DoS controls -As Gavin and I have said clearly before, the software is not at all resistant to DoS attack. This is one improvement, but there are still more ways to attack than I can count. - -I'm leaving the -limitfreerelay part as a switch for now and it's there if you need it. - -- Removed "safe mode" alerts -"safe mode" alerts was a temporary measure after the 0.3.9 overflow bug. We can say all we want that users can just run with "-disablesafemode", but it's better just not to have it for the sake of appearances. It was never intended as a long term feature. Safe mode can still be triggered by seeing a longer (greater total PoW) invalid block chain. diff --git a/doc/bitcoin-release-notes/release-notes-0.3.20.1.md b/doc/bitcoin-release-notes/release-notes-0.3.20.1.md deleted file mode 100644 index 6c5682ea4..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.20.1.md +++ /dev/null @@ -1 +0,0 @@ -Never released or release notes were lost. diff --git a/doc/bitcoin-release-notes/release-notes-0.3.20.2.md b/doc/bitcoin-release-notes/release-notes-0.3.20.2.md deleted file mode 100644 index 09ecb736b..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.20.2.md +++ /dev/null @@ -1,17 +0,0 @@ -The maxsendbuffer bug (0.3.20.1 clients not being able to download the block chain from other 0.3.20.1 clients) was only going to get -worse as people upgraded, so I cherry-picked the bug fix and created a minor release yesterday. - -The Amazon Machine Images I used to do the builds are available: - - ami-38a05251 Bitcoin-v0.3.20.2 Mingw (Windows; Administrator password 'bitcoin development') - ami-30a05259 Bitcoin_0.3.20.2 Linux32 - ami-8abc4ee3 Bitcoin_0.3.20.2 Linux64 - -(mac build will be done soon) - -If you have already downloaded version 0.3.20.1, please either add this to your bitcoin.conf file: - - maxsendbuffer=10000 - maxreceivebuffer=10000 - -... or download the new version. diff --git a/doc/bitcoin-release-notes/release-notes-0.3.20.md b/doc/bitcoin-release-notes/release-notes-0.3.20.md deleted file mode 100644 index 9ae21802e..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.20.md +++ /dev/null @@ -1,22 +0,0 @@ -Please checkout the git integration branch from: - -https://github.com/bitcoin/bitcoin - -... and help test. The new features that need testing are: - -* -nolisten : https://github.com/bitcoin/bitcoin/pull/11 -* -rescan : scan block chain for missing wallet transactions -* -printtoconsole : https://github.com/bitcoin/bitcoin/pull/37 -* RPC gettransaction details : https://github.com/bitcoin/bitcoin/pull/24 -* listtransactions new features : https://github.com/bitcoin/bitcoin/pull/10 - -Bug fixes that also need testing: - -* -maxconnections= : https://github.com/bitcoin/bitcoin/pull/42 -* RPC listaccounts minconf : https://github.com/bitcoin/bitcoin/pull/27 -* RPC move, add time to output : https://github.com/bitcoin/bitcoin/pull/21 -* ...and several improvements to --help output. - -This needs more testing on Windows! Please drop me a quick private message, email, or IRC message if you are able to do some testing. If you find bugs, please open an issue at: - -https://github.com/bitcoin/bitcoin/issues diff --git a/doc/bitcoin-release-notes/release-notes-0.3.21.md b/doc/bitcoin-release-notes/release-notes-0.3.21.md deleted file mode 100644 index f3b6bc426..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.21.md +++ /dev/null @@ -1,20 +0,0 @@ -Binaries for Bitcoin version 0.3.21 are available at: - https://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.3.21/ - -Changes and new features from the 0.3.20 release include: - -* Universal Plug and Play support. Enable automatic opening of a port for incoming connections by running bitcoin or bitcoind with the - -upnp=1 command line switch or using the Options dialog box. - -* Support for full-precision bitcoin amounts. You can now send, and bitcoin will display, bitcoin amounts smaller than 0.01. However, sending fewer than 0.01 bitcoins still requires a 0.01 bitcoin fee (so you can send 1.0001 bitcoins without a fee, but you will be asked to pay a fee if you try to send 0.0001). - -* A new method of finding bitcoin nodes to connect with, via DNS A records. Use the -dnsseed option to enable. - -For developers, changes to bitcoin's remote-procedure-call API: - -* New rpc command "sendmany" to send bitcoins to more than one address in a single transaction. - -* Several bug fixes, including a serious intermittent bug that would sometimes cause bitcoind to stop accepting rpc requests. - -* -logtimestamps option, to add a timestamp to each line in debug.log. - -* Immature blocks (newly generated, under 120 confirmations) are now shown in listtransactions. diff --git a/doc/bitcoin-release-notes/release-notes-0.3.22.md b/doc/bitcoin-release-notes/release-notes-0.3.22.md deleted file mode 100644 index 4c05e3e5e..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.22.md +++ /dev/null @@ -1,16 +0,0 @@ -Download URL: https://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.3.22/ - -This is largely a bugfix and TX fee schedule release. We also hope to make 0.3.23 a quick release, to fix problems that the network has seen due to explosive growth in the past week. - -Notable changes: -* Client will accept and relay TX's with 0.0005 BTC fee schedule (users still pay 0.01 BTC per kb, until next version) -* Non-standard transactions accepted on testnet -* Source code tree reorganized (prep for autotools build) -* Remove "Generate Coins" option from GUI, and remove 4way SSE miner. Internal reference CPU miner remains available, but users are directed to external miners for best hash production. -* IRC is overflowing. Client now bootstraps to channels #bitcoin00 - #bitcoin99 -* DNS names now may be used with -addnode, -connect (requires -dns to enable) - -RPC changes: -* 'listtransactions' adds 'from' param, for range queries -* 'move' may take account balances negative -* 'settxfee' added, to manually set TX fee diff --git a/doc/bitcoin-release-notes/release-notes-0.3.23.md b/doc/bitcoin-release-notes/release-notes-0.3.23.md deleted file mode 100644 index c1d520e49..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.23.md +++ /dev/null @@ -1,10 +0,0 @@ -Win32, Linux, MacOSX and source releases for bitcoin v0.3.23 have been uploaded to -https://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.3.23/ - -This is another quick bugfix release, trying to deal with the influx of new bitcoin users. - -Main items of note: - -* P2P connect-to-node logic changed to reduce timeout a bit. The network saw a huge influx of new users, who do not permit incoming connections. This change is a short-term hack, to more quickly hunt for useful P2P connections. Better "leaf node" logic is in the works, but this should let us limp along until then. One may use -upnp to properly forward ports, and help the network. -* Transaction fee reduced to 0.0005 for new transactions -* Client will relay transactions with fees as low as 0.0001 BTC diff --git a/doc/bitcoin-release-notes/release-notes-0.3.24.md b/doc/bitcoin-release-notes/release-notes-0.3.24.md deleted file mode 100644 index d35ac66f2..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.3.24.md +++ /dev/null @@ -1,20 +0,0 @@ -Bitcoin v0.3.24 is now available for download at -https://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.3.24/ - -This is another bug fix release. We had hoped to have wallet encryption ready for release, but more urgent fixes for existing clients were needed -- most notably block download problems were getting severe. Wallet encryption is ready for testing at https://github.com/bitcoin/bitcoin/pull/352 for the git-savvy, and hopefully will follow shortly in the next release, v0.4. - -Notable fixes in v0.3.24, and the main reasons for this release: - -F1) Block downloads were failing or taking unreasonable amounts of time to complete, because the increased size of the block chain was bumping up against some earlier buffer-size DoS limits. - -F2) Fix crash caused by loss/lack of network connection. - -Notable changes in v0.3.24: - -C1) DNS seeding enabled by default. - -C2) UPNP enabled by default in the GUI client. The percentage of bitcoin clients that accept incoming connections is quite small, and that is a problem. This should help. bitcoind, and unofficial builds, are unchanged (though we encourage use of "-upnp" to help the network!) - -C3) Initial unit testing framework. Bitcoin sorely needs automated tests, and this is a beginning. Contributions welcome. - -C4) Internal wallet code cleanup. While invisible to an end user, this change provides the basis for v0.4's wallet encryption. diff --git a/doc/bitcoin-release-notes/release-notes-0.4.0.md b/doc/bitcoin-release-notes/release-notes-0.4.0.md deleted file mode 100644 index 145072a36..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.4.0.md +++ /dev/null @@ -1,70 +0,0 @@ -Bitcoin version 0.4.0 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.4.0/ - -The main feature in this release is wallet private key encryption; -you can set a passphrase that must be entered before sending coins. -See below for more information; if you decide to encrypt your wallet, -WRITE DOWN YOUR PASSPHRASE AND PUT IT IN A SECURE LOCATION. If you -forget or lose your wallet passphrase, you lose your bitcoins. -Previous versions of bitcoin are unable to read encrypted wallets, -and will crash on startup if the wallet is encrypted. - -Also note: bitcoin version 0.4 uses a newer version of Berkeley DB -(bdb version 4.8) than previous versions (bdb 4.7). If you upgrade -to version 0.4 and then revert back to an earlier version of bitcoin -the it may be unable to start because bdb 4.7 cannot read bdb 4.8 -"log" files. - - -Notable bug fixes from version 0.3.24: - -Fix several bitcoin-becomes-unresponsive bugs due to multithreading -deadlocks. - -Optimize database writes for large (lots of inputs) transactions -(fixes a potential denial-of-service attack) - - -Wallet Encryption - -Bitcoin supports native wallet encryption so that people who steal your -wallet file don't automatically get access to all of your Bitcoins. -In order to enable this feature, choose "Encrypt Wallet" from the -Options menu. You will be prompted to enter a passphrase, which -will be used as the key to encrypt your wallet and will be needed -every time you wish to send Bitcoins. If you lose this passphrase, -you will lose access to spend all of the bitcoins in your wallet, -no one, not even the Bitcoin developers can recover your Bitcoins. -This means you are responsible for your own security, store your -passphrase in a secure location and do not forget it. - -Remember that the encryption built into bitcoin only encrypts the -actual keys which are required to send your bitcoins, not the full -wallet. This means that someone who steals your wallet file will -be able to see all the addresses which belong to you, as well as the -relevant transactions, you are only protected from someone spending -your coins. - -It is recommended that you backup your wallet file before you -encrypt your wallet. To do this, close the Bitcoin client and -copy the wallet.dat file from ~/.bitcoin/ on Linux, /Users/(user -name)/Application Support/Bitcoin/ on Mac OSX, and %APPDATA%/Bitcoin/ -on Windows (that is /Users/(user name)/AppData/Roaming/Bitcoin on -Windows Vista and 7 and /Documents and Settings/(user name)/Application -Data/Bitcoin on Windows XP). Once you have copied that file to a -safe location, reopen the Bitcoin client and Encrypt your wallet. -If everything goes fine, delete the backup and enjoy your encrypted -wallet. Note that once you encrypt your wallet, you will never be -able to go back to a version of the Bitcoin client older than 0.4. - -Keep in mind that you are always responsible for your own security. -All it takes is a slightly more advanced wallet-stealing trojan which -installs a keylogger to steal your wallet passphrase as you enter it -in addition to your wallet file and you have lost all your Bitcoins. -Wallet encryption cannot keep you safe if you do not practice -good security, such as running up-to-date antivirus software, only -entering your wallet passphrase in the Bitcoin client and using the -same passphrase only as your wallet passphrase. - -See the doc/README file in the bitcoin source for technical details -of wallet encryption. diff --git a/doc/bitcoin-release-notes/release-notes-0.4.1.md b/doc/bitcoin-release-notes/release-notes-0.4.1.md deleted file mode 100644 index ac471a8d7..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.4.1.md +++ /dev/null @@ -1,38 +0,0 @@ -Bitcoin version 0.4.1 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.4.1/ - -This is a bugfix only release based on 0.4.0. - -Please report bugs by replying to this forum thread. - -MAJOR BUG FIX (CVE-2011-4447) - -The wallet encryption feature introduced in Bitcoin version 0.4.0 did not sufficiently secure the private keys. An attacker who -managed to get a copy of your encrypted wallet.dat file might be able to recover some or all of the unencrypted keys and steal the -associated coins. - -If you have a previously encrypted wallet.dat, the first time you run wxbitcoin or bitcoind the wallet will be rewritten, Bitcoin will -shut down, and you will be prompted to restart it to run with the new, properly encrypted file. - -If you had a previously encrypted wallet.dat that might have been copied or stolen (for example, you backed it up to a public -location) you should send all of your bitcoins to yourself using a new bitcoin address and stop using any previously generated addresses. - -Wallets encrypted with this version of Bitcoin are written properly. - -Technical note: the encrypted wallet's 'keypool' will be regenerated the first time you request a new bitcoin address; to be certain that the -new private keys are properly backed up you should: - -1. Run Bitcoin and let it rewrite the wallet.dat file - -2. Run it again, then ask it for a new bitcoin address. -wxBitcoin: new address visible on main window -bitcoind: run the 'walletpassphrase' RPC command to unlock the wallet, then run the 'getnewaddress' RPC command. - -3. If your encrypted wallet.dat may have been copied or stolen, send all of your bitcoins to the new bitcoin address. - -4. Shut down Bitcoin, then backup the wallet.dat file. -IMPORTANT: be sure to request a new bitcoin address before backing up, so that the 'keypool' is regenerated and backed up. - -"Security in depth" is always a good idea, so choosing a secure location for the backup and/or encrypting the backup before uploading it is recommended. And as in previous releases, if your machine is infected by malware there are several ways an attacker might steal your bitcoins. - -Thanks to Alan Reiner (etotheipi) for finding and reporting this bug. diff --git a/doc/bitcoin-release-notes/release-notes-0.4.2.md b/doc/bitcoin-release-notes/release-notes-0.4.2.md deleted file mode 100644 index 6c5682ea4..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.4.2.md +++ /dev/null @@ -1 +0,0 @@ -Never released or release notes were lost. diff --git a/doc/bitcoin-release-notes/release-notes-0.4.3.md b/doc/bitcoin-release-notes/release-notes-0.4.3.md deleted file mode 100644 index fea1ae75b..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.4.3.md +++ /dev/null @@ -1,21 +0,0 @@ -bitcoind version 0.4.3 is now available for download at: -http://luke.dashjr.org/programs/bitcoin/files/bitcoind-0.4.3/ (until Gavin uploads to SourceForge) - -This is a bugfix-only release based on 0.4.0. - -Please note that the wxBitcoin GUI client is no longer maintained nor supported. If someone would like to step up to maintain this, they should contact Luke-Jr. - -Please report bugs for the daemon only using the issue tracker at github: -https://github.com/bitcoin/bitcoin/issues - -Stable source code is hosted at Gitorious: -http://gitorious.org/bitcoin/bitcoind-stable/archive-tarball/v0.4.3#.tar.gz - -BUG FIXES - -Cease locking memory used by non-sensitive information (this caused a huge performance hit on some platforms, especially noticable during initial blockchain download). -Fixed some address-handling deadlocks (client freezes). -No longer accept inbound connections over the internet when Bitcoin is being used with Tor (identity leak). -Use the correct base transaction fee of 0.0005 BTC for accepting transactions into mined blocks (since 0.4.0, it was incorrectly accepting 0.0001 BTC which was only meant to be relayed). -Add new DNS seeds (maintained by Pieter Wuille and Luke Dashjr). - diff --git a/doc/bitcoin-release-notes/release-notes-0.4.4.md b/doc/bitcoin-release-notes/release-notes-0.4.4.md deleted file mode 100644 index f435ba716..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.4.4.md +++ /dev/null @@ -1,30 +0,0 @@ -Bitcoin version 0.4.4 is now available for download at: -http://luke.dashjr.org/programs/bitcoin/files/bitcoind-0.4.4/ - -This is a bugfix-only release based on 0.4.0. - -Please note that the wxBitcoin GUI client is no longer maintained nor supported. If someone would like to step up to maintain this, they should contact Luke-Jr. - -Please report bugs for the daemon only using the issue tracker at github: -https://github.com/bitcoin/bitcoin/issues - -Stable source code is hosted at Gitorious: -http://gitorious.org/bitcoin/bitcoind-stable/archive-tarball/v0.4.4#.tar.gz - -BUG FIXES - -Limit the number of orphan transactions stored in memory, to prevent a potential denial-of-service attack by flooding orphan transactions. Also never store invalid transactions at all. -Fix possible buffer overflow on systems with very long application data paths. This is not exploitable. -Resolved multiple bugs preventing long-term unlocking of encrypted wallets (issue #922). -Only send local IP in "version" messages if it is globally routable (ie, not private), and try to get such an IP from UPnP if applicable. -Reannounce UPnP port forwards every 20 minutes, to workaround routers expiring old entries, and allow the -upnp option to override any stored setting. -Various memory leaks and potential null pointer deferences have been -fixed. -Several shutdown issues have been fixed. -Check that keys stored in the wallet are valid at startup, and if not, -report corruption. -Various build fixes. -If no password is specified to bitcoind, recommend a secure password. -Update hard-coded fallback seed nodes, choosing recent ones with long uptime and versions at least 0.4.0. -Add checkpoint at block 168,000. - diff --git a/doc/bitcoin-release-notes/release-notes-0.4.5.md b/doc/bitcoin-release-notes/release-notes-0.4.5.md deleted file mode 100644 index 6c5682ea4..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.4.5.md +++ /dev/null @@ -1 +0,0 @@ -Never released or release notes were lost. diff --git a/doc/bitcoin-release-notes/release-notes-0.4.6.md b/doc/bitcoin-release-notes/release-notes-0.4.6.md deleted file mode 100644 index 07c5e4b69..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.4.6.md +++ /dev/null @@ -1,37 +0,0 @@ -bitcoind version 0.4.6 is now available for download at: -Windows: installer | zip (sig) -Source: tar.gz -bitcoind and Bitcoin-Qt version 0.6.0.7 are also tagged in git, but it is recommended to upgrade to 0.6.1. - -These are bugfix-only releases. - -Please report bugs by replying to this forum thread. Note that the 0.4.x wxBitcoin GUI client is no longer maintained nor supported. If someone would like to step up to maintain this, they should contact Luke-Jr. - -BUG FIXES - -Version 0.6.0 allowed importing invalid "private keys", which would be unspendable; 0.6.0.7 will now verify the private key is valid, and refuse to import an invalid one -Verify status of encrypt/decrypt calls to detect failed padding -Check blocks for duplicate transactions earlier. Fixes #1167 -Upgrade Windows builds to OpenSSL 1.0.1b -Set label when selecting an address that already has a label. Fixes #1080 (Bitcoin-Qt) -JSON-RPC listtransactions's from/count handling is now fixed -Optimize and fix multithreaded access, when checking whether we already know about transactions -Fix potential networking deadlock -Proper support for Growl 1.3 notifications -Display an error, rather than crashing, if encoding a QR Code failed (0.6.0.7) -Don't erroneously set "Display addresses" for users who haven't explicitly enabled it (Bitcoin-Qt) -Some non-ASCII input in JSON-RPC expecting hexadecimal may have been misinterpreted rather than rejected -Missing error condition checking added -Do not show green tick unless all known blocks are downloaded. Fixes #921 (Bitcoin-Qt) -Increase time ago of last block for "up to date" status from 30 to 90 minutes -Show a message box when runaway exception happens (Bitcoin-Qt) -Use a messagebox to display the error when -server is provided without providing a rpc password -Show error message instead of exception crash when unable to bind RPC port (Bitcoin-Qt) -Correct sign message bitcoin address tooltip. Fixes #1050 (Bitcoin-Qt) -Removed "(no label)" from QR Code dialog titlebar if we have no label (0.6.0.7) -Removed an ugly line break in tooltip for mature transactions (0.6.0.7) -Add missing tooltip and key shortcut in settings dialog (part of #1088) (Bitcoin-Qt) -Work around issue in boost::program_options that prevents from compiling in clang -Fixed bugs occurring only on platforms with unsigned characters (such as ARM). -Rename make_windows_icon.py to .sh as it is a shell script. Fixes #1099 (Bitcoin-Qt) -Various trivial internal corrections to types used for counting/size loops and warnings diff --git a/doc/bitcoin-release-notes/release-notes-0.5.0.md b/doc/bitcoin-release-notes/release-notes-0.5.0.md deleted file mode 100644 index baa409b5f..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.5.0.md +++ /dev/null @@ -1,70 +0,0 @@ -Bitcoin version 0.5.0 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.5.0/ - -The major change for this release is a completely new graphical interface that uses the Qt user interface toolkit. - -This release include German, Spanish, Spanish-Castilian, Norwegian and Dutch translations. More translations are welcome; join the project at Transifex if you can help: -https://www.transifex.net/projects/p/bitcoin/ - -Please report bugs using the issue tracker at github: -https://github.com/bitcoin/bitcoin/issues - -For Ubuntu users, there is a new ppa maintained by Matt Corallo which you can add to your system so that it will automatically keep bitcoin up-to-date. Just type "sudo apt-add-repository ppa:bitcoin/bitcoin" in your terminal, then install the bitcoin-qt package. - -MAJOR BUG FIX (CVE-2011-4447) - -The wallet encryption feature introduced in Bitcoin version 0.4.0 did not sufficiently secure the private keys. An attacker who -managed to get a copy of your encrypted wallet.dat file might be able to recover some or all of the unencrypted keys and steal the -associated coins. - -If you have a previously encrypted wallet.dat, the first time you run bitcoin-qt or bitcoind the wallet will be rewritten, Bitcoin will -shut down, and you will be prompted to restart it to run with the new, properly encrypted file. - -If you had a previously encrypted wallet.dat that might have been copied or stolen (for example, you backed it up to a public -location) you should send all of your bitcoins to yourself using a new bitcoin address and stop using any previously generated addresses. - -Wallets encrypted with this version of Bitcoin are written properly. - -Technical note: the encrypted wallet's 'keypool' will be regenerated the first time you request a new bitcoin address; to be certain that the -new private keys are properly backed up you should: - -1. Run Bitcoin and let it rewrite the wallet.dat file - -2. Run it again, then ask it for a new bitcoin address. -Bitcoin-Qt: Address Book, then New Address... -bitcoind: run the 'walletpassphrase' RPC command to unlock the wallet, then run the 'getnewaddress' RPC command. - -3. If your encrypted wallet.dat may have been copied or stolen, send all of your bitcoins to the new bitcoin address. - -4. Shut down Bitcoin, then backup the wallet.dat file. -IMPORTANT: be sure to request a new bitcoin address before backing up, so that the 'keypool' is regenerated and backed up. - -"Security in depth" is always a good idea, so choosing a secure location for the backup and/or encrypting the backup before uploading it is recommended. And as in previous releases, if your machine is infected by malware there are several ways an attacker might steal your bitcoins. - -Thanks to Alan Reiner (etotheipi) for finding and reporting this bug. - -MAJOR GUI CHANGES - -"Splash" graphics at startup that show address/wallet/blockchain loading progress. - -"Synchronizing with network" progress bar to show block-chain download progress. - -Icons at the bottom of the window that show how well connected you are to the network, with tooltips to display details. - -Drag and drop support for bitcoin: URIs on web pages. - -Export transactions as a .csv file. - -Many other GUI improvements, large and small. - -RPC CHANGES - -getmemorypool : new RPC command, provides everything needed to construct a block with a custom generation transaction and submit a solution - -listsinceblock : new RPC command, list transactions since given block - -signmessage/verifymessage : new RPC commands to sign a message with one of your private keys or verify that a message signed by the private key associated with a bitcoin address. - -GENERAL CHANGES - -Faster initial block download. diff --git a/doc/bitcoin-release-notes/release-notes-0.5.1.md b/doc/bitcoin-release-notes/release-notes-0.5.1.md deleted file mode 100644 index d56bff6d9..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.5.1.md +++ /dev/null @@ -1,43 +0,0 @@ -Bitcoin version 0.5.1 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.5.1/ - -This is a bugfix-only release. - -This release includes 13 translations, including 5 new translations: -Italian, Hungarian, Ukranian, Portuguese (Brazilian) and Simplified Chinese. -More translations are welcome; join the project at Transifex if you can help: -https://www.transifex.net/projects/p/bitcoin/ - -Please report bugs using the issue tracker at github: -https://github.com/bitcoin/bitcoin/issues - -Project source code is hosted at github; we are no longer -distributing .tar.gz files here, you can get them -directly from github: -https://github.com/bitcoin/bitcoin/tarball/v0.5.1 # .tar.gz -https://github.com/bitcoin/bitcoin/zipball/v0.5.1 # .zip - -For Ubuntu users, there is a new ppa maintained by Matt Corallo which -you can add to your system so that it will automatically keep -bitcoin up-to-date. Just type -sudo apt-add-repository ppa:bitcoin/bitcoin -in your terminal, then install the bitcoin-qt package. - - -BUG FIXES - -Re-enable SSL support for the JSON-RPC interface (it was unintentionally -disabled for the 0.5.0 release binaries). - -The code that finds peers via "dns seeds" no longer stops bitcoin startup -if one of the dns seed machines is down. - -Tooltips on the transaction list view were rendering incorrectly (as black boxes -or with a transparent background). - -Prevent a denial-of-service attack involving flooding a bitcoin node with -orphan blocks. - -The wallet passphrase dialog now warns you if the caps lock key was pressed. - -Improved searching in addresses and labels in bitcoin-qt. diff --git a/doc/bitcoin-release-notes/release-notes-0.5.2.md b/doc/bitcoin-release-notes/release-notes-0.5.2.md deleted file mode 100644 index f79816668..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.5.2.md +++ /dev/null @@ -1,22 +0,0 @@ -Bitcoin version 0.5.2 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.5.2/ - -This is a bugfix-only release based on 0.5.1. - -Please report bugs using the issue tracker at github: -https://github.com/bitcoin/bitcoin/issues - -Stable source code is hosted at Gitorious: -http://gitorious.org/bitcoin/bitcoind-stable/archive-tarball/v0.5.2#.tar.gz - -BUG FIXES - -Check all transactions in blocks after the last checkpoint (0.5.0 and 0.5.1 skipped checking ECDSA signatures during initial blockchain download). -Cease locking memory used by non-sensitive information (this caused a huge performance hit on some platforms, especially noticable during initial blockchain download; this was -not a security vulnerability). -Fixed some address-handling deadlocks (client freezes). -No longer accept inbound connections over the internet when Bitcoin is being used with Tor (identity leak). -Re-enable SSL support for the JSON-RPC interface (it was unintentionally disabled for the 0.5.0 and 0.5.1 release Linux binaries). -Use the correct base transaction fee of 0.0005 BTC for accepting transactions into mined blocks (since 0.4.0, it was incorrectly accepting 0.0001 BTC which was only meant to be relayed). -Don't show "IP" for transactions which are not necessarily IP transactions. -Add new DNS seeds (maintained by Pieter Wuille and Luke Dashjr). diff --git a/doc/bitcoin-release-notes/release-notes-0.5.3.md b/doc/bitcoin-release-notes/release-notes-0.5.3.md deleted file mode 100644 index 7c84c5332..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.5.3.md +++ /dev/null @@ -1,42 +0,0 @@ -Bitcoin version 0.5.3 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.5.3/ - -This is a bugfix-only release based on 0.5.1. -It also includes a few protocol updates. - -Please report bugs using the issue tracker at github: -https://github.com/bitcoin/bitcoin/issues - -Stable source code is hosted at Gitorious: -http://gitorious.org/bitcoin/bitcoind-stable/archive-tarball/v0.5.3#.tar.gz - -PROTOCOL UPDATES - -BIP 30: Introduce a new network rule: "a block is not valid if it contains a transaction whose hash already exists in the block chain, unless all that transaction's outputs were already spent before said block" beginning on March 15, 2012, 00:00 UTC. -On testnet, allow mining of min-difficulty blocks if 20 minutes have gone by without mining a regular-difficulty block. This is to make testing Bitcoin easier, and will not affect normal mode. - -BUG FIXES - -Limit the number of orphan transactions stored in memory, to prevent a potential denial-of-service attack by flooding orphan transactions. Also never store invalid transactions at all. -Fix possible buffer overflow on systems with very long application data paths. This is not exploitable. -Resolved multiple bugs preventing long-term unlocking of encrypted wallets -(issue #922). -Only send local IP in "version" messages if it is globally routable (ie, not private), and try to get such an IP from UPnP if applicable. -Reannounce UPnP port forwards every 20 minutes, to workaround routers expiring old entries, and allow the -upnp option to override any stored setting. -Skip splash screen when -min is used, and fix Minimize to Tray function. -Do not blank "label" in Bitcoin-Qt "Send" tab, if the user has already entered something. -Correct various labels and messages. -Various memory leaks and potential null pointer deferences have been fixed. -Handle invalid Bitcoin URIs using "bitcoin://" instead of "bitcoin:". -Several shutdown issues have been fixed. -Revert to "global progress indication", as starting from zero every time was considered too confusing for many users. -Check that keys stored in the wallet are valid at startup, and if not, report corruption. -Enable accessible widgets on Windows, so that people with screen readers such as NVDA can make sense of it. -Various build fixes. -If no password is specified to bitcoind, recommend a secure password. -Automatically focus and scroll to new "Send coins" entries in Bitcoin-Qt. -Show a message box for --help on Windows, for Bitcoin-Qt. -Add missing "About Qt" menu option to show built-in Qt About dialog. -Don't show "-daemon" as an option for Bitcoin-Qt, since it isn't available. -Update hard-coded fallback seed nodes, choosing recent ones with long uptime and versions at least 0.4.0. -Add checkpoint at block 168,000. diff --git a/doc/bitcoin-release-notes/release-notes-0.5.4.md b/doc/bitcoin-release-notes/release-notes-0.5.4.md deleted file mode 100644 index fcde3ac4e..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.5.4.md +++ /dev/null @@ -1,39 +0,0 @@ -Bitcoin version 0.5.4 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.5.4/ -NOTE: 0.5.4rc3 is being renamed to 0.5.4 final with no changes. - -This is a bugfix-only release in the 0.5.x series, plus a few protocol updates. - -Please report bugs using the issue tracker at github: -https://github.com/bitcoin/bitcoin/issues - -Stable source code is hosted at Gitorious: -http://gitorious.org/bitcoin/bitcoind-stable/archive-tarball/v0.5.4#.tar.gz - -PROTOCOL UPDATES - -BIP 16: Special-case "pay to script hash" logic to enable minimal validation of new transactions. -Support for validating message signatures produced with compressed public keys. - -BUG FIXES - -Build with thread-safe MingW libraries for Windows, fixing a dangerous memory corruption scenario when exceptions are thrown. -Fix broken testnet mining. -Stop excess inventory relay during initial block download. -When disconnecting a node, clear the received buffer so that we do not process any already received messages. -Yet another attempt at implementing "minimize to tray" that works on all operating systems. -Fix Bitcoin-Qt notifications under Growl 1.3. -Increase required age of Bitcoin-Qt's "not up to date" status from 30 to 90 minutes. -Implemented missing verifications that led to crash on entering some wrong passphrases for encrypted wallets. -Fix default filename suffixes in GNOME save dialog. -Make the "Send coins" tab use the configured unit type, even on the first attempt. -Print detailed wallet loading errors to debug.log when it is corrupt. -Allocate exactly the amount of space needed for signing transactions, instead of a fixed 10k buffer. -Workaround for improbable memory access violation. -Check wallet's minimum version before trying to load it. -Remove wxBitcoin properly when installing Bitcoin-Qt over it. (Windows) -Detail reorganization information better in debug log. -Use a messagebox to display the error when -server is provided without configuring a RPC password. -Testing suite build now honours provided CXXFLAGS. -Removed an extraneous line-break in mature transaction tooltips. -Fix some grammatical errors in translation process documentation. diff --git a/doc/bitcoin-release-notes/release-notes-0.5.5.md b/doc/bitcoin-release-notes/release-notes-0.5.5.md deleted file mode 100644 index 75ebc3e6b..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.5.5.md +++ /dev/null @@ -1,37 +0,0 @@ -bitcoind and Bitcoin-Qt version 0.5.5 are now available for download at: -Windows: installer | zip (sig) -Source: tar.gz -bitcoind and Bitcoin-Qt version 0.6.0.7 are also tagged in git, but it is recommended to upgrade to 0.6.1. - -These are bugfix-only releases. - -Please report bugs by replying to this forum thread. Note that the 0.4.x wxBitcoin GUI client is no longer maintained nor supported. If someone would like to step up to maintain this, they should contact Luke-Jr. - -BUG FIXES - -Version 0.6.0 allowed importing invalid "private keys", which would be unspendable; 0.6.0.7 will now verify the private key is valid, and refuse to import an invalid one -Verify status of encrypt/decrypt calls to detect failed padding -Check blocks for duplicate transactions earlier. Fixes #1167 -Upgrade Windows builds to OpenSSL 1.0.1b -Set label when selecting an address that already has a label. Fixes #1080 (Bitcoin-Qt) -JSON-RPC listtransactions's from/count handling is now fixed -Optimize and fix multithreaded access, when checking whether we already know about transactions -Fix potential networking deadlock -Proper support for Growl 1.3 notifications -Display an error, rather than crashing, if encoding a QR Code failed (0.6.0.7) -Don't erroneously set "Display addresses" for users who haven't explicitly enabled it (Bitcoin-Qt) -Some non-ASCII input in JSON-RPC expecting hexadecimal may have been misinterpreted rather than rejected -Missing error condition checking added -Do not show green tick unless all known blocks are downloaded. Fixes #921 (Bitcoin-Qt) -Increase time ago of last block for "up to date" status from 30 to 90 minutes -Show a message box when runaway exception happens (Bitcoin-Qt) -Use a messagebox to display the error when -server is provided without providing a rpc password -Show error message instead of exception crash when unable to bind RPC port (Bitcoin-Qt) -Correct sign message bitcoin address tooltip. Fixes #1050 (Bitcoin-Qt) -Removed "(no label)" from QR Code dialog titlebar if we have no label (0.6.0.7) -Removed an ugly line break in tooltip for mature transactions (0.6.0.7) -Add missing tooltip and key shortcut in settings dialog (part of #1088) (Bitcoin-Qt) -Work around issue in boost::program_options that prevents from compiling in clang -Fixed bugs occurring only on platforms with unsigned characters (such as ARM). -Rename make_windows_icon.py to .sh as it is a shell script. Fixes #1099 (Bitcoin-Qt) -Various trivial internal corrections to types used for counting/size loops and warnings diff --git a/doc/bitcoin-release-notes/release-notes-0.6.0.md b/doc/bitcoin-release-notes/release-notes-0.6.0.md deleted file mode 100644 index 1963a3625..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.6.0.md +++ /dev/null @@ -1,138 +0,0 @@ -Bitcoin version 0.6.0 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.6.0/test/ - -This release includes more than 20 language localizations. -More translations are welcome; join the -project at Transifex to help: -https://www.transifex.net/projects/p/bitcoin/ - -Please report bugs using the issue tracker at github: -https://github.com/bitcoin/bitcoin/issues - -Project source code is hosted at github; we are no longer -distributing .tar.gz files here, you can get them -directly from github: -https://github.com/bitcoin/bitcoin/tarball/v0.6.0 # .tar.gz -https://github.com/bitcoin/bitcoin/zipball/v0.6.0 # .zip - -For Ubuntu users, there is a ppa maintained by Matt Corallo which -you can add to your system so that it will automatically keep -bitcoin up-to-date. Just type -sudo apt-add-repository ppa:bitcoin/bitcoin -in your terminal, then install the bitcoin-qt package. - - -KNOWN ISSUES - -Shutting down while synchronizing with the network -(downloading the blockchain) can take more than a minute, -because database writes are queued to speed up download -time. - - -NEW FEATURES SINCE BITCOIN VERSION 0.5 - -Initial network synchronization should be much faster -(one or two hours on a typical machine instead of ten or more -hours). - -Backup Wallet menu option. - -Bitcoin-Qt can display and save QR codes for sending -and receiving addresses. - -New context menu on addresses to copy/edit/delete them. - -New Sign Message dialog that allows you to prove that you -own a bitcoin address by creating a digital -signature. - -New wallets created with this version will -use 33-byte 'compressed' public keys instead of -65-byte public keys, resulting in smaller -transactions and less traffic on the bitcoin -network. The shorter keys are already supported -by the network but wallet.dat files containing -short keys are not compatible with earlier -versions of Bitcoin-Qt/bitcoind. - -New command-line argument -blocknotify= -that will spawn a shell process to run -when a new block is accepted. - -New command-line argument -splash=0 to disable -Bitcoin-Qt's initial splash screen - -validateaddress JSON-RPC api command output includes -two new fields for addresses in the wallet: -pubkey : hexadecimal public key -iscompressed : true if pubkey is a short 33-byte key - -New JSON-RPC api commands for dumping/importing -private keys from the wallet (dumprivkey, importprivkey). - -New JSON-RPC api command for getting information about -blocks (getblock, getblockhash). - -New JSON-RPC api command (getmininginfo) for getting -extra information related to mining. The getinfo -JSON-RPC command no longer includes mining-related -information (generate/genproclimit/hashespersec). - - - -NOTABLE CHANGES - -BIP30 implemented (security fix for an attack involving -duplicate "coinbase transactions"). - -The -nolisten, -noupnp and -nodnsseed command-line -options were renamed to -listen, -upnp and -dnsseed, -with a default value of 1. The old names are still -supported for compatibility (so specifying -nolisten -is automatically interpreted as -listen=0; every -boolean argument can now be specified as either --foo or -nofoo). - -The -noirc command-line options was renamed to --irc, with a default value of 0. Run -irc=1 to -get the old behavior. - -Three fill-up-available-memory denial-of-service -attacks were fixed. - - -NOT YET IMPLEMENTED FEATURES - -Support for clicking on bitcoin: URIs and -opening/launching Bitcoin-Qt is available only on Linux, -and only if you configure your desktop to launch -Bitcoin-Qt. All platforms support dragging and dropping -bitcoin: URIs onto the Bitcoin-Qt window to start -payment. - - -PRELIMINARY SUPPORT FOR MULTISIGNATURE TRANSACTIONS - -This release has preliminary support for multisignature -transactions-- transactions that require authorization -from more than one person or device before they -will be accepted by the bitcoin network. - -Prior to this release, multisignature transactions -were considered 'non-standard' and were ignored; -with this release multisignature transactions are -considered standard and will start to be relayed -and accepted into blocks. - -It is expected that future releases of Bitcoin-Qt -will support the creation of multisignature transactions, -once enough of the network has upgraded so relaying -and validating them is robust. - -For this release, creation and testing of multisignature -transactions is limited to the bitcoin test network using -the "addmultisigaddress" JSON-RPC api call. - -Short multisignature address support is included in this -release, as specified in BIP 13 and BIP 16. diff --git a/doc/bitcoin-release-notes/release-notes-0.6.1.md b/doc/bitcoin-release-notes/release-notes-0.6.1.md deleted file mode 100644 index ef7966ecd..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.6.1.md +++ /dev/null @@ -1,2 +0,0 @@ -Never released - diff --git a/doc/bitcoin-release-notes/release-notes-0.6.2.md b/doc/bitcoin-release-notes/release-notes-0.6.2.md deleted file mode 100644 index bb85fb23a..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.6.2.md +++ /dev/null @@ -1,50 +0,0 @@ -Bitcoin version 0.6.2 is now available for download at: -http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.6.2/ - -This is a bug-fix and code-cleanup release, with no major new features. - -Please report bugs using the github issue tracker at: -https://github.com/bitcoin/bitcoin/issues - - -NOTABLE CHANGES - -Much faster shutdowns. However, the blkindex.dat file is no longer -portable to different data directories by default. If you need a -portable blkindex.dat file then run with the new -detachdb=1 option -or the "Detach databases at shutdown" GUI preference. - -Fixed https://github.com/bitcoin/bitcoin/issues/1065, a bug that -could cause long-running nodes to crash. - -Mac and Windows binaries are compiled against OpenSSL 1.0.1b (Linux -binaries are dynamically linked to the version of OpenSSL on the system). - - -CHANGE SUMMARY - -Use 'git shortlog --no-merges v0.6.0..' for a summary of this release. - -Source codebase changes: -- Many source code cleanups and warnings fixes. Close to building with -Wall -- Locking overhaul, and several minor locking fixes -- Several source code portability fixes, e.g. FreeBSD - -JSON-RPC interface changes: -- addmultisigaddress enabled for mainnet (previously only enabled for testnet) - -Network protocol changes: -- protocol version 60001 -- added nonce value to "ping" message (BIP 31) -- added new "pong" message (BIP 31) - -Backend storage changes: -- Less redundant database flushing, especially during initial block download -- Shutdown improvements (see above) - -Qt user interface: -- minor URI handling improvements -- progressbar improvements -- error handling improvements (show message box rather than console exception, -etc.) -- by popular request, make 4th bar of connection icon green diff --git a/doc/bitcoin-release-notes/release-notes-0.6.3.md b/doc/bitcoin-release-notes/release-notes-0.6.3.md deleted file mode 100644 index 28bb20e10..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.6.3.md +++ /dev/null @@ -1,29 +0,0 @@ -Bitcoin version 0.6.3 is now available for download at: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.6.3/ - -This is a bug-fix release, with no new features. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - -CHANGE SUMMARY - -Fixed a serious denial-of-service attack that could cause the -bitcoin process to become unresponsive. Thanks to Sergio Lerner -for finding and responsibly reporting the problem. (CVE-2012-3789) - -Optimized the process of checking transaction signatures, to -speed up processing of new block messages and make propagating -blocks across the network faster. - -Fixed an obscure bug that could cause the bitcoin process to get -stuck on an invalid block-chain, if the invalid chain was -hundreds of blocks long. - -Bitcoin-Qt no longer automatically selects the first address -in the address book (Issue #1384). - -Fixed minimize-to-dock behavior of Bitcon-Qt on the Mac. - -Added a block checkpoint at block 185,333 to speed up initial -blockchain download. diff --git a/doc/bitcoin-release-notes/release-notes-0.7.0.md b/doc/bitcoin-release-notes/release-notes-0.7.0.md deleted file mode 100644 index d33a58f99..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.7.0.md +++ /dev/null @@ -1,169 +0,0 @@ -Bitcoin version 0.7.0 is now available for download at: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.7.0/ - -We recommend that everybody running prior versions of bitcoind/Bitcoin-Qt -upgrade to this release, except for users running Mac OSX 10.5. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - -Project source code is hosted at github; you can get -source-only tarballs/zipballs directly from there: - https://github.com/bitcoin/bitcoin/tarball/v0.7.0 # .tar.gz - https://github.com/bitcoin/bitcoin/zipball/v0.7.0 # .zip - -Ubuntu Linux users can use the "Personal Package Archive" (PPA) -maintained by Matt Corallo to automatically keep -bitcoin up-to-date. Just type - sudo apt-add-repository ppa:bitcoin/bitcoin - sudo apt-get update -in your terminal, then install the bitcoin-qt package: - sudo apt-get install bitcoin-qt - - -How to Upgrade - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -Code: -/Applications/Bitcoin-Qt -(on Mac) or -Code: -bitcoind/bitcoin-qt -(on Linux). - -If you were running on Linux with a version that might have been compiled -with a different version of Berkeley DB (for example, if you were using the -PPA and are switching to the binary release), then run the old version again -with the -detachdb argument and shut it down; if you do not, then the new -version will not be able to read the database files and will exit with an error. - -Incompatible Changes - -* Replaced the 'getmemorypool' RPC command with 'getblocktemplate/submitblock' - and 'getrawmempool' commands. -* Remove deprecated RPC 'getblocknumber' - -Bitcoin Improvement Proposals implemented - -BIP 22 - 'getblocktemplate', 'submitblock' RPCs -BIP 34 - block version 2, height in coinbase -BIP 35 - 'mempool' message, extended 'getdata' message behavior - - -Core bitcoin handling and blockchain database - -* Reduced CPU usage, by eliminating some redundant hash calculations -* Cache signature verifications, to eliminate redundant signature checks -* Transactions with zero-value outputs are considered non-standard -* Mining: when creating new blocks, sort 'paid' area by fee-per-kb -* Database: better validation of on-disk stored data -* Database: minor optimizations and reliability improvements -* -loadblock=FILE will import an external block file -* Additional DoS (denial-of-service) prevention measures -* New blockchain checkpoint at block 193,000 - - -JSON-RPC API - -* Internal HTTP server is now thread-per-connection, rather than - a single-threaded queue that would stall on network I/O. -* Internal HTTP server supports HTTP/1.1, pipelined requests and - connection keep-alive. -* Support JSON-RPC 2.0 batches, to encapsulate multiple JSON-RPC requests - within a single HTTP request. -* IPv6 support -* Added raw transaction API. See https://gist.github.com/2839617 -* Added 'getrawmempool', to list contents of TX memory pool -* Added 'getpeerinfo', to list data about each connected network peer -* Added 'listaddressgroupings' for better coin control -* Rework getblock call. -* Remove deprecated RPC 'getblocknumber' -* Remove superceded RPC 'getmemorypool' (see BIP 22, above) -* listtransactions output now displays "smart" times for transactions, - and 'blocktime' and 'timereceived' fields were added - - -P2P networking - -* IPv6 support -* Tor hidden service support (see doc/Tor.txt) -* Attempts to fix "stuck blockchain download" problems -* Replace BDB database "addr.dat" with internally-managed "peers.dat" - file containing peer address data. -* Lower default send buffer from 10MB to 1MB -* proxy: SOCKS5 by default -* Support connecting by hostnames passed to proxy -* Add -seednode connections, and use this instead of DNS seeds when proxied -* Added -externalip and -discover -* Add -onlynet to connect only to a given network (IPv4, IPv6, or Tor) -* Separate listening sockets, -bind= - - -Qt GUI - -* Add UI RPC console / debug window -* Re-Enable URI handling on Windows, add safety checks and tray-notifications -* Harmonize the use of ellipsis ("...") to be used in menus, but not on buttons -* Add 2 labels to the overviewpage that display Wallet and Transaction status (obsolete or current) -* Extend the optionsdialog (e.g. language selection) and re-work it to a tabbed UI -* Merge sign/verify message into a single window with tabbed UI -* Ensure a changed bitcoin unit immediately updates all GUI elements that use units -* Update QR Code dialog -* Improve error reporting at startup -* Fine-grained UI updates for a much smoother UI during block downloads -* Remove autocorrection of 0/i in addresses in UI -* Reorganize tray icon menu into more logical order -* Persistently poll for balance change when number of blocks changed -* Much better translations -* Override progress bar design on platforms with segmented progress bars to assist with readability -* Added 'immature balance' display on the overview page -* (Windows only): enable ASLR and DEP for bitcoin-qt.exe -* (Windows only): add meta-data to bitcoin-qt.exe (e.g. description) - -Internal codebase - -* Additional unit tests -* Compile warning fixes - - -Miscellaneous - -* Reopen debug.log upon SIGHUP -* Bash programmable completion for bitcoind(1) -* On supported OS's, each thread is given a useful name - - -Thanks to everybody who contributed to this release: - -Chris Moore -Christian von Roques -David Joel Schwartz -Douglas Huff -Fordy -Gavin Andresen -Giel van Schijndel -Gregory Maxwell -Jeff Garzik -Luke Dashjr -Matt Corallo -Michael Ford -Michael Hendricks -Peter Todd -Philip Kaufmann -Pieter Wuille -R E Broadley -Ricardo M. Correia -Rune K. Svendsen -Scott Ellis -Stephane Glondu -Wladimir J. van der Laan -cardpuncher -coderrr -fanquake -grimd34th -sje397 -xanatos - -Thanks to Sergio Lerner for reporting denial-of-service vulnerabilities fixed in this release. diff --git a/doc/bitcoin-release-notes/release-notes-0.7.1.md b/doc/bitcoin-release-notes/release-notes-0.7.1.md deleted file mode 100644 index 22e910c09..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.7.1.md +++ /dev/null @@ -1,110 +0,0 @@ -Bitcoin version 0.7.1 is now available from: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.7.1/ - -This is a bug-fix minor release. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - -Project source code is hosted at github; you can get -source-only tarballs/zipballs directly from there: - https://github.com/bitcoin/bitcoin/tarball/v0.7.1 # .tar.gz - https://github.com/bitcoin/bitcoin/zipball/v0.7.1 # .zip - -Ubuntu Linux users can use the "Personal Package Archive" (PPA) -maintained by Matt Corallo to automatically keep -up-to-date. Just type: - sudo apt-add-repository ppa:bitcoin/bitcoin - sudo apt-get update -in your terminal, then install the bitcoin-qt package: - sudo apt-get install bitcoin-qt - -KNOWN ISSUES ------------- - -Mac OSX 10.5 is no longer supported. - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you were running on Linux with a version that might have been compiled -with a different version of Berkeley DB (for example, if you were using an -Ubuntu PPA version), then run the old version again with the -detachdb -argument and shut it down; if you do not, then the new version will not -be able to read the database files and will exit with an error. - -Explanation of -detachdb (and the new "stop true" RPC command): -The Berkeley DB database library stores data in both ".dat" and -"log" files, so the database is always in a consistent state, -even in case of power failure or other sudden shutdown. The -format of the ".dat" files is portable between different -versions of Berkeley DB, but the "log" files are not-- even minor -version differences may have incompatible "log" files. The --detachdb option moves any pending changes from the "log" files -to the "blkindex.dat" file for maximum compatibility, but makes -shutdown much slower. Note that the "wallet.dat" file is always -detached, and versions prior to 0.6.0 detached all databases -at shutdown. - -New features ------------- - -* Added a boolean argument to the RPC 'stop' command, if true sets - -detachdb to create standalone database .dat files before shutting down. - -* -salvagewallet command-line option, which moves any existing wallet.dat - to wallet.{timestamp}.dat and then attempts to salvage public/private - keys and master encryption keys (if the wallet is encrypted) into - a new wallet.dat. This should only be used if your wallet becomes - corrupted, and is not intended to replace regular wallet backups. - -* Import $DataDir/bootstrap.dat automatically, if it exists. - -Dependency changes ------------------- - -* Qt 4.8.2 for Windows builds - -* openssl 1.0.1c - -Bug fixes ---------- - -* Clicking on a bitcoin: URI on Windows should now launch Bitcoin-Qt properly. - -* When running -testnet, use RPC port 18332 by default. - -* Better detection and handling of corrupt wallet.dat and blkindex.dat files. - Previous versions would crash with a DB_RUNRECOVERY exception, this - version detects most problems and tells you how to recover if it - cannot recover itself. - -* Fixed an uninitialized variable bug that could cause transactions to - be reported out of order. - -* Fixed a bug that could cause occasional crashes on exit. - -* Warn the user that they need to create fresh wallet backups after they - encrypt their wallet. - ----------------------------------------------------- -Thanks to everybody who contributed to this release: - -Gavin Andresen -Jeff Garzik -Luke Dashjr -Mark Friedenbach -Matt Corallo -Philip Kaufmann -Pieter Wuille -Rune K. Svendsen -Virgil Dupras -Wladimir J. van der Laan -fanquake -kjj2 -xanatos diff --git a/doc/bitcoin-release-notes/release-notes-0.7.2.md b/doc/bitcoin-release-notes/release-notes-0.7.2.md deleted file mode 100644 index 40af34962..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.7.2.md +++ /dev/null @@ -1,68 +0,0 @@ -Bitcoin version 0.7.2 is now available from: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.7.2 - -This is a bug-fix minor release. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you were running on Linux with a version that might have been compiled -with a different version of Berkeley DB (for example, if you were using an -Ubuntu PPA version), then run the old version again with the -detachdb -argument and shut it down; if you do not, then the new version will not -be able to read the database files and will exit with an error. - -Explanation of -detachdb (and the new "stop true" RPC command): -The Berkeley DB database library stores data in both ".dat" and -"log" files, so the database is always in a consistent state, -even in case of power failure or other sudden shutdown. The -format of the ".dat" files is portable between different -versions of Berkeley DB, but the "log" files are not-- even minor -version differences may have incompatible "log" files. The --detachdb option moves any pending changes from the "log" files -to the "blkindex.dat" file for maximum compatibility, but makes -shutdown much slower. Note that the "wallet.dat" file is always -detached, and versions prior to 0.6.0 detached all databases -at shutdown. - -Bug fixes ---------- - -* Prevent RPC 'move' from deadlocking. This was caused by trying to lock the - database twice. - -* Fix use-after-free problems in initialization and shutdown, the latter of - which caused Bitcoin-Qt to crash on Windows when exiting. - -* Correct library linking so building on Windows natively works. - -* Avoid a race condition and out-of-bounds read in block creation/mining code. - -* Improve platform compatibility quirks, including fix for 100% CPU utilization - on FreeBSD 9. - -* A few minor corrections to error handling, and updated translations. - -* OSX 10.5 supported again - ----------------------------------------------------- -Thanks to everybody who contributed to this release: - -Alex -dansmith -Gavin Andresen -Gregory Maxwell -Jeff Garzik -Luke Dashjr -Philip Kaufmann -Pieter Wuille -Wladimir J. van der Laan -grimd34th diff --git a/doc/bitcoin-release-notes/release-notes-0.8.0.md b/doc/bitcoin-release-notes/release-notes-0.8.0.md deleted file mode 100644 index 4e98a7740..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.8.0.md +++ /dev/null @@ -1,139 +0,0 @@ -Bitcoin-Qt version 0.8.0 is now available from: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.8.0/ - -This is a major release designed to improve performance and handle the -increasing volume of transactions on the network. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -The first time you run after the upgrade a re-indexing process will be -started that will take anywhere from 30 minutes to several hours, -depending on the speed of your machine. - -Incompatible Changes --------------------- - -This release no longer maintains a full index of historical transaction ids -by default, so looking up an arbitrary transaction using the getrawtransaction -RPC call will not work. If you need that functionality, you must run once -with -txindex=1 -reindex=1 to rebuild block-chain indices (see below for more -details). - -Improvements ------------- - -Mac and Windows binaries are signed with certificates owned by the Bitcoin -Foundation, to be compatible with the new security features in OSX 10.8 and -Windows 8. - -LevelDB, a fast, open-source, non-relational database from Google, is -now used to store transaction and block indices. LevelDB works much better -on machines with slow I/O and is faster in general. Berkeley DB is now only -used for the wallet.dat file (public and private wallet keys and transactions -relevant to you). - -Pieter Wuille implemented many optimizations to the way transactions are -verified, so a running, synchronized node uses less working memory and does -much less I/O. He also implemented parallel signature checking, so if you -have a multi-CPU machine all CPUs will be used to verify transactions. - -New Features ------------- - -"Bloom filter" support in the network protocol for sending only relevant transactions to -lightweight clients. - -contrib/verifysfbinaries is a shell-script to verify that the binary downloads -at sourceforge have not been tampered with. If you are able, you can help make -everybody's downloads more secure by running this occasionally to check PGP -signatures against download file checksums. - -contrib/spendfrom is a python-language command-line utility that demonstrates -how to use the "raw transactions" JSON-RPC api to send coins received from particular -addresses (also known as "coin control"). - -New/changed settings (command-line or bitcoin.conf file) --------------------------------------------------------- - -dbcache : controls LevelDB memory usage. - -par : controls how many threads to use to validate transactions. Defaults to the number -of CPUs on your machine, use -par=1 to limit to a single CPU. - -txindex : maintains an extra index of old, spent transaction ids so they will be found -by the getrawtransaction JSON-RPC method. - -reindex : rebuild block and transaction indices from the downloaded block data. - -New JSON-RPC API Features -------------------------- - -lockunspent / listlockunspent allow locking transaction outputs for a period of time so -they will not be spent by other processes that might be accessing the same wallet. - -addnode / getaddednodeinfo methods, to connect to specific peers without restarting. - -importprivkey now takes an optional boolean parameter (default true) to control whether -or not to rescan the blockchain for transactions after importing a new private key. - -Important Bug Fixes -------------------- - -Privacy leak: the position of the "change" output in most transactions was not being -properly randomized, making network analysis of the transaction graph to identify -users' wallets easier. - -Zero-confirmation transaction vulnerability: accepting zero-confirmation transactions -(transactions that have not yet been included in a block) from somebody you do not -trust is still not recommended, because there will always be ways for attackers to -double-spend zero-confirmation transactions. However, this release includes a bug -fix that makes it a little bit more difficult for attackers to double-spend a -certain type ("lockTime in the future") of zero-confirmation transaction. - -Dependency Changes ------------------- - -Qt 4.8.3 (compiling against older versions of Qt 4 should continue to work) - - -Thanks to everybody who contributed to this release: ----------------------------------------------------- - -Alexander Kjeldaas -Andrey Alekseenko -Arnav Singh -Christian von Roques -Eric Lombrozo -Forrest Voight -Gavin Andresen -Gregory Maxwell -Jeff Garzik -Luke Dashjr -Matt Corallo -Mike Cassano -Mike Hearn -Peter Todd -Philip Kaufmann -Pieter Wuille -Richard Schwab -Robert Backhaus -Rune K. Svendsen -Sergio Demian Lerner -Wladimir J. van der Laan -burger2 -default -fanquake -grimd34th -justmoon -redshark1802 -tucenaber -xanatos diff --git a/doc/bitcoin-release-notes/release-notes-0.8.1.md b/doc/bitcoin-release-notes/release-notes-0.8.1.md deleted file mode 100644 index 4fd546bee..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.8.1.md +++ /dev/null @@ -1,22 +0,0 @@ -Bitcoin-Qt/bitcoind version 0.8.1 is now available from: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.8.1/ - -This is a maintenance release that adds a new network rule to avoid -a chain-forking incompatibility with versions 0.7.2 and earlier. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you -run 0.8.1 your blockchain files will be re-indexed, which will take -anywhere from 30 minutes to several hours, depending on the speed of -your machine. diff --git a/doc/bitcoin-release-notes/release-notes-0.8.2.md b/doc/bitcoin-release-notes/release-notes-0.8.2.md deleted file mode 100644 index eea9ba2a2..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.8.2.md +++ /dev/null @@ -1,137 +0,0 @@ -Bitcoin-Qt version 0.8.2 is now available from: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.8.2/ - -This is a maintenance release that fixes many bugs and includes -a few small new features. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - - -How to Upgrade - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you -run 0.8.2 your blockchain files will be re-indexed, which will take -anywhere from 30 minutes to several hours, depending on the speed of -your machine. - -0.8.2 Release notes - -Fee Policy changes - -The default fee for low-priority transactions is lowered from 0.0005 BTC -(for each 1,000 bytes in the transaction; an average transaction is -about 500 bytes) to 0.0001 BTC. - -Payments (transaction outputs) of 0.543 times the minimum relay fee -(0.00005430 BTC) are now considered 'non-standard', because storing them -costs the network more than they are worth and spending them will usually -cost their owner more in transaction fees than they are worth. - -Non-standard transactions are not relayed across the network, are not included -in blocks by most miners, and will not show up in your wallet until they are -included in a block. - -The default fee policy can be overridden using the -mintxfee and -minrelaytxfee -command-line options, but note that we intend to replace the hard-coded fees -with code that automatically calculates and suggests appropriate fees in the -0.9 release and note that if you set a fee policy significantly different from -the rest of the network your transactions may never confirm. - -Bitcoin-Qt changes - -* New icon and splash screen -* Improve reporting of synchronization process -* Remove hardcoded fee recommendations -* Improve metadata of executable on MacOSX and Windows -* Move export button to individual tabs instead of toolbar -* Add "send coins" command to context menu in address book -* Add "copy txid" command to copy transaction IDs from transaction overview -* Save & restore window size and position when showing & hiding window -* New translations: Arabic (ar), Bosnian (bs), Catalan (ca), Welsh (cy), - Esperanto (eo), Interlingua (la), Latvian (lv) and many improvements - to current translations - -MacOSX: -* OSX support for click-to-pay (bitcoin:) links -* Fix GUI disappearing problem on MacOSX (issue #1522) - -Linux/Unix: -* Copy addresses to middle-mouse-button clipboard - - -Command-line options - -* -walletnotify will call a command on receiving transactions that affect the wallet. -* -alertnotify will call a command on receiving an alert from the network. -* -par now takes a negative number, to leave a certain amount of cores free. - -JSON-RPC API changes - -* fixed a getblocktemplate bug that caused excessive CPU creating blocks. -* listunspent now lists account and address information. -* getinfo now also returns the time adjustment estimated from your peers. -* getpeerinfo now returns bytessent, bytesrecv and syncnode. -* gettxoutsetinfo returns statistics about the unspent transaction output database. -* gettxout returns information about a specific unspent transaction output. - - -Networking changes - -* Significant changes to the networking code, reducing latency and memory consumption. -* Avoid initial block download stalling. -* Remove IRC seeding support. -* Performance tweaks. -* Added testnet DNS seeds. - -Wallet compatibility/rescuing - -* Cases where wallets cannot be opened in another version/installation should be reduced. -* -salvagewallet now works for encrypted wallets. - - -Known Bugs - -* Entering the 'getblocktemplate' or 'getwork' RPC commands into the Bitcoin-Qt debug -console will cause Bitcoin-Qt to crash. Run Bitcoin-Qt with the -server command-line -option to workaround. - -Thanks to everybody who contributed to the 0.8.2 release! - -APerson241 -Andrew Poelstra -Calvin Owens -Chuck LeDuc Díaz -Colin Dean -David Griffith -David Serrano -Eric Lombrozo -Gavin Andresen -Gregory Maxwell -Jeff Garzik -Jonas Schnelli -Larry Gilbert -Luke Dashjr -Matt Corallo -Michael Ford -Mike Hearn -Patrick Brown -Peter Todd -Philip Kaufmann -Pieter Wuille -Richard Schwab -Roman Mindalev -Scott Howard -Tariq Bashir -Warren Togami -Wladimir J. van der Laan -freewil -gladoscc -kjj2 -mb300sd -super3 diff --git a/doc/bitcoin-release-notes/release-notes-0.8.3.md b/doc/bitcoin-release-notes/release-notes-0.8.3.md deleted file mode 100644 index 856c20aa3..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.8.3.md +++ /dev/null @@ -1,18 +0,0 @@ -Bitcoin-Qt version 0.8.3 is now available from: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.8.3/ - -This is a maintenance release to fix a denial-of-service attack that -can cause nodes to crash. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - -0.8.3 Release notes - -Truncate over-size messages to prevent a memory exhaustion attack. - -Fix a regression that causes excessive re-writing of the 'peers.dat' file. - - -Thanks to Peter Todd for responsibly disclosing the vulnerability -( CVE-2013-4627 ) and creating a fix. diff --git a/doc/bitcoin-release-notes/release-notes-0.8.4.md b/doc/bitcoin-release-notes/release-notes-0.8.4.md deleted file mode 100644 index c6f31f1fa..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.8.4.md +++ /dev/null @@ -1,83 +0,0 @@ -Bitcoin-Qt version 0.8.4 is now available from: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.8.4/ - -This is a maintenance release to fix a critical bug and three -security issues; we urge all users to upgrade. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you -run 0.8.4 your blockchain files will be re-indexed, which will take -anywhere from 30 minutes to several hours, depending on the speed of -your machine. - -0.8.4 Release notes -=================== - -Security issues ---------------- - -An attacker could send a series of messages that resulted in -an integer division-by-zero error in the Bloom Filter handling -code, causing the Bitcoin-Qt or bitcoind process to crash. -Bloom filters were introduced with version 0.8, so versions 0.8.0 -through 0.8.3 are vulnerable to this critical denial-of-service attack. - -A constant-time algorithm is now used to check RPC password -guess attempts; fixes https://github.com/bitcoin/bitcoin/issues/2838 -(CVE-2013-4165) - -Implement a better fix for the fill-memory-with-orphan-transactions -attack that was fixed in 0.8.3. See -https://bitslog.wordpress.com/2013/07/18/buggy-cve-2013-4627-patch-open-new-vectors-of-attack/ -for a description of the weaknesses of the previous fix. -(CVE-2013-4627) - -Bugs fixed ----------- - -Fix multi-block reorg transaction resurrection. - -Fix non-standard disconnected transactions causing mempool orphans. -This bug could cause nodes running with the -debug flag to crash. - -OSX: use 'FD_FULLSYNC' with LevelDB, which will (hopefully!) -prevent the database corruption issues many people have -experienced on OSX. - -Linux: clicking on bitcoin: links was broken if you were using -a Gnome-based desktop. - -Fix a hang-at-shutdown bug that only affects users that compile -their own version of Bitcoin against Boost versions 1.50-1.52. - -Other changes -------------- - -Checkpoint at block 250,000 to speed up initial block downloads -and make the progress indicator when downloading more accurate. - - -Thanks to everybody who contributed to the 0.8.4 releases! ----------------------------------------------------------- - -Pieter Wuille -Warren Togami -Patrick Strateman -pakt -Gregory Maxwell -Sergio Demian Lerner -grayleonard -Cory Fields -Matt Corallo -Gavin Andresen diff --git a/doc/bitcoin-release-notes/release-notes-0.8.5.md b/doc/bitcoin-release-notes/release-notes-0.8.5.md deleted file mode 100644 index aa93fe7c7..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.8.5.md +++ /dev/null @@ -1,44 +0,0 @@ -Bitcoin-Qt version 0.8.5 is now available from: - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.8.5/ - -This is a maintenance release to fix a critical bug; -we urge all users to upgrade. - -Please report bugs using the issue tracker at github: - https://github.com/bitcoin/bitcoin/issues - - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you -run 0.8.5 your blockchain files will be re-indexed, which will take -anywhere from 30 minutes to several hours, depending on the speed of -your machine. - -0.8.5 Release notes -=================== - -Bugs fixed ----------- - -Transactions with version numbers larger than 0x7fffffff were -incorrectly being relayed and included in blocks. - -Blocks containing transactions with version numbers larger -than 0x7fffffff caused the code that checks for LevelDB database -inconsistencies at startup to erroneously report database -corruption and suggest that you reindex your database. - -This release also contains a non-critical fix to the code that -enforces BIP 34 (block height in the coinbase transaction). - --- - -Thanks to Gregory Maxwell and Pieter Wuille for quickly -identifying and fixing the transaction version number bug. diff --git a/doc/bitcoin-release-notes/release-notes-0.8.6.md b/doc/bitcoin-release-notes/release-notes-0.8.6.md deleted file mode 100644 index 39a45e0db..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.8.6.md +++ /dev/null @@ -1,66 +0,0 @@ -Bitcoin-Qt version 0.8.6 final is now available from: - - http://sourceforge.net/projects/bitcoin/files/Bitcoin/bitcoin-0.8.6/ - -This is a maintenance release to fix a critical bug; we urge all users to upgrade. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade --------------- - -If you already downloaded 0.8.6rc1 you do not need to re-download. This release is exactly the same. - -If you are running an older version, shut it down. Wait -until it has completely shut down (which might take a few minutes for older -versions), then run the installer (on Windows) or just copy over -/Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you -run 0.8.6 your blockchain files will be re-indexed, which will take -anywhere from 30 minutes to several hours, depending on the speed of -your machine. - -0.8.6 Release notes -=================== - -- Default block size increase for miners. - (see https://gist.github.com/gavinandresen/7670433#086-accept-into-block) - -- Remove the all-outputs-must-be-greater-than-CENT-to-qualify-as-free rule for relaying - (see https://gist.github.com/gavinandresen/7670433#086-relaying) - -- Lower maximum size for free transaction creation - (see https://gist.github.com/gavinandresen/7670433#086-wallet) - -- OSX block chain database corruption fixes - - Update leveldb to 1.13 - - Use fcntl with `F_FULLSYNC` instead of fsync on OSX - - Use native Darwin memory barriers - - Replace use of mmap in leveldb for improved reliability (only on OSX) - -- Fix nodes forwarding transactions with empty vins and getting banned - -- Network code performance and robustness improvements - -- Additional debug.log logging for diagnosis of network problems, log timestamps by default - -- Fix Bitcoin-Qt startup crash when clicking dock icon on OSX - -- Fix memory leaks in CKey::SetCompactSignature() and Key::SignCompact() - -- Fix rare GUI crash on send - -- Various small GUI, documentation and build fixes - -Warning -------- - -- There have been frequent reports of users running out of virtual memory on 32-bit systems - during the initial sync. - Hence it is recommended to use a 64-bit executable if possible. - A 64-bit executable for Windows is planned for 0.9. - -Note: Gavin Andresen's GPG signing key for SHA256SUMS.asc has been changed from key id 1FC730C1 to sub key 7BF6E212 (see https://github.com/bitcoin/bitcoin.org/pull/279). diff --git a/doc/bitcoin-release-notes/release-notes-0.9.0.md b/doc/bitcoin-release-notes/release-notes-0.9.0.md deleted file mode 100644 index 170410ca4..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.9.0.md +++ /dev/null @@ -1,411 +0,0 @@ -Bitcoin Core version 0.9.0 is now available from: - - https://bitcoin.org/bin/0.9.0/ - -This is a new major version release, bringing both new features and -bug fixes. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), uninstall all -earlier versions of Bitcoin, then run the installer (on Windows) or just copy -over /Applications/Bitcoin-Qt (on Mac) or bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you run -0.9.0 your blockchain files will be re-indexed, which will take anywhere from -30 minutes to several hours, depending on the speed of your machine. - -On Windows, do not forget to uninstall all earlier versions of the Bitcoin -client first, especially if you are switching to the 64-bit version. - -Windows 64-bit installer -------------------------- - -New in 0.9.0 is the Windows 64-bit version of the client. There have been -frequent reports of users running out of virtual memory on 32-bit systems -during the initial sync. Because of this it is recommended to install the -64-bit version if your system supports it. - -NOTE: Release candidate 2 Windows binaries are not code-signed; use PGP -and the SHA256SUMS.asc file to make sure your binaries are correct. -In the final 0.9.0 release, Windows setup.exe binaries will be code-signed. - -OSX 10.5 / 32-bit no longer supported -------------------------------------- - -0.9.0 drops support for older Macs. The minimum requirements are now: -* A 64-bit-capable CPU (see http://support.apple.com/kb/ht3696); -* Mac OS 10.6 or later (see https://support.apple.com/kb/ht1633). - -Downgrading warnings --------------------- - -The 'chainstate' for this release is not always compatible with previous -releases, so if you run 0.9 and then decide to switch back to a -0.8.x release you might get a blockchain validation error when starting the -old release (due to 'pruned outputs' being omitted from the index of -unspent transaction outputs). - -Running the old release with the -reindex option will rebuild the chainstate -data structures and correct the problem. - -Also, the first time you run a 0.8.x release on a 0.9 wallet it will rescan -the blockchain for missing spent coins, which will take a long time (tens -of minutes on a typical machine). - -Rebranding to Bitcoin Core ---------------------------- - -To reduce confusion between Bitcoin-the-network and Bitcoin-the-software we -have renamed the reference client to Bitcoin Core. - - -OP_RETURN and data in the block chain -------------------------------------- -On OP_RETURN: There was been some confusion and misunderstanding in -the community, regarding the OP_RETURN feature in 0.9 and data in the -blockchain. This change is not an endorsement of storing data in the -blockchain. The OP_RETURN change creates a provably-prunable output, -to avoid data storage schemes -- some of which were already deployed -- -that were storing arbitrary data such as images as forever-unspendable -TX outputs, bloating bitcoin's UTXO database. - -Storing arbitrary data in the blockchain is still a bad idea; it is less -costly and far more efficient to store non-currency data elsewhere. - -Autotools build system ------------------------ - -For 0.9.0 we switched to an autotools-based build system instead of individual -(q)makefiles. - -Using the standard "./autogen.sh; ./configure; make" to build Bitcoin-Qt and -bitcoind makes it easier for experienced open source developers to contribute -to the project. - -Be sure to check doc/build-*.md for your platform before building from source. - -Bitcoin-cli -------------- - -Another change in the 0.9 release is moving away from the bitcoind executable -functioning both as a server and as a RPC client. The RPC client functionality -("tell the running bitcoin daemon to do THIS") was split into a separate -executable, 'bitcoin-cli'. The RPC client code will eventually be removed from -bitcoind, but will be kept for backwards compatibility for a release or two. - -`walletpassphrase` RPC ------------------------ - -The behavior of the `walletpassphrase` RPC when the wallet is already unlocked -has changed between 0.8 and 0.9. - -The 0.8 behavior of `walletpassphrase` is to fail when the wallet is already unlocked: - - > walletpassphrase 1000 - walletunlocktime = now + 1000 - > walletpassphrase 10 - Error: Wallet is already unlocked (old unlock time stays) - -The new behavior of `walletpassphrase` is to set a new unlock time overriding -the old one: - - > walletpassphrase 1000 - walletunlocktime = now + 1000 - > walletpassphrase 10 - walletunlocktime = now + 10 (overriding the old unlock time) - -Transaction malleability-related fixes --------------------------------------- - -This release contains a few fixes for transaction ID (TXID) malleability -issues: - -- -nospendzeroconfchange command-line option, to avoid spending - zero-confirmation change -- IsStandard() transaction rules tightened to prevent relaying and mining of - mutated transactions -- Additional information in listtransactions/gettransaction output to - report wallet transactions that conflict with each other because - they spend the same outputs. -- Bug fixes to the getbalance/listaccounts RPC commands, which would report - incorrect balances for double-spent (or mutated) transactions. -- New option: -zapwallettxes to rebuild the wallet's transaction information - -Transaction Fees ----------------- - -This release drops the default fee required to relay transactions across the -network and for miners to consider the transaction in their blocks to -0.01mBTC per kilobyte. - -Note that getting a transaction relayed across the network does NOT guarantee -that the transaction will be accepted by a miner; by default, miners fill -their blocks with 50 kilobytes of high-priority transactions, and then with -700 kilobytes of the highest-fee-per-kilobyte transactions. - -The minimum relay/mining fee-per-kilobyte may be changed with the -minrelaytxfee option. Note that previous releases incorrectly used -the mintxfee setting to determine which low-priority transactions should -be considered for inclusion in blocks. - -The wallet code still uses a default fee for low-priority transactions of -0.1mBTC per kilobyte. During periods of heavy transaction volume, even this -fee may not be enough to get transactions confirmed quickly; the mintxfee -option may be used to override the default. - -0.9.0 Release notes -======================= - -RPC: - -- New notion of 'conflicted' transactions, reported as confirmations: -1 -- 'listreceivedbyaddress' now provides tx ids -- Add raw transaction hex to 'gettransaction' output -- Updated help and tests for 'getreceivedby(account|address)' -- In 'getblock', accept 2nd 'verbose' parameter, similar to getrawtransaction, - but defaulting to 1 for backward compatibility -- Add 'verifychain', to verify chain database at runtime -- Add 'dumpwallet' and 'importwallet' RPCs -- 'keypoolrefill' gains optional size parameter -- Add 'getbestblockhash', to return tip of best chain -- Add 'chainwork' (the total work done by all blocks since the genesis block) - to 'getblock' output -- Make RPC password resistant to timing attacks -- Clarify help messages and add examples -- Add 'getrawchangeaddress' call for raw transaction change destinations -- Reject insanely high fees by default in 'sendrawtransaction' -- Add RPC call 'decodescript' to decode a hex-encoded transaction script -- Make 'validateaddress' provide redeemScript -- Add 'getnetworkhashps' to get the calculated network hashrate -- New RPC 'ping' command to request ping, new 'pingtime' and 'pingwait' fields - in 'getpeerinfo' output -- Adding new 'addrlocal' field to 'getpeerinfo' output -- Add verbose boolean to 'getrawmempool' -- Add rpc command 'getunconfirmedbalance' to obtain total unconfirmed balance -- Explicitly ensure that wallet is unlocked in `importprivkey` -- Add check for valid keys in `importprivkey` - -Command-line options: - -- New option: -nospendzeroconfchange to never spend unconfirmed change outputs -- New option: -zapwallettxes to rebuild the wallet's transaction information -- Rename option '-tor' to '-onion' to better reflect what it does -- Add '-disablewallet' mode to let bitcoind run entirely without wallet (when - built with wallet) -- Update default '-rpcsslciphers' to include TLSv1.2 -- make '-logtimestamps' default on and rework help-message -- RPC client option: '-rpcwait', to wait for server start -- Remove '-logtodebugger' -- Allow `-noserver` with bitcoind - -Block-chain handling and storage: - -- Update leveldb to 1.15 -- Check for correct genesis (prevent cases where a datadir from the wrong - network is accidentally loaded) -- Allow txindex to be removed and add a reindex dialog -- Log aborted block database rebuilds -- Store orphan blocks in serialized form, to save memory -- Limit the number of orphan blocks in memory to 750 -- Fix non-standard disconnected transactions causing mempool orphans -- Add a new checkpoint at block 279,000 - -Wallet: - -- Bug fixes and new regression tests to correctly compute - the balance of wallets containing double-spent (or mutated) transactions -- Store key creation time. Calculate whole-wallet birthday. -- Optimize rescan to skip blocks prior to birthday -- Let user select wallet file with -wallet=foo.dat -- Consider generated coins mature at 101 instead of 120 blocks -- Improve wallet load time -- Don't count txins for priority to encourage sweeping -- Don't create empty transactions when reading a corrupted wallet -- Fix rescan to start from beginning after importprivkey -- Only create signatures with low S values - -Mining: - -- Increase default -blockmaxsize/prioritysize to 750K/50K -- 'getblocktemplate' does not require a key to create a block template -- Mining code fee policy now matches relay fee policy - -Protocol and network: - -- Drop the fee required to relay a transaction to 0.01mBTC per kilobyte -- Send tx relay flag with version -- New 'reject' P2P message (BIP 0061, see - https://gist.github.com/gavinandresen/7079034 for draft) -- Dump addresses every 15 minutes instead of 10 seconds -- Relay OP_RETURN data TxOut as standard transaction type -- Remove CENT-output free transaction rule when relaying -- Lower maximum size for free transaction creation -- Send multiple inv messages if mempool.size > MAX_INV_SZ -- Split MIN_PROTO_VERSION into INIT_PROTO_VERSION and MIN_PEER_PROTO_VERSION -- Do not treat fFromMe transaction differently when broadcasting -- Process received messages one at a time without sleeping between messages -- Improve logging of failed connections -- Bump protocol version to 70002 -- Add some additional logging to give extra network insight -- Added new DNS seed from bitcoinstats.com - -Validation: - -- Log reason for non-standard transaction rejection -- Prune provably-unspendable outputs, and adapt consistency check for it. -- Detect any sufficiently long fork and add a warning -- Call the -alertnotify script when we see a long or invalid fork -- Fix multi-block reorg transaction resurrection -- Reject non-canonically-encoded serialization sizes -- Reject dust amounts during validation -- Accept nLockTime transactions that finalize in the next block - -Build system: - -- Switch to autotools-based build system -- Build without wallet by passing `--disable-wallet` to configure, this - removes the BerkeleyDB dependency -- Upgrade gitian dependencies (libpng, libz, libupnpc, boost, openssl) to more - recent versions -- Windows 64-bit build support -- Solaris compatibility fixes -- Check integrity of gitian input source tarballs -- Enable full GCC Stack-smashing protection for all OSes - -GUI: - -- Switch to Qt 5.2.0 for Windows build -- Add payment request (BIP 0070) support -- Improve options dialog -- Show transaction fee in new send confirmation dialog -- Add total balance in overview page -- Allow user to choose data directory on first start, when data directory is - missing, or when the -choosedatadir option is passed -- Save and restore window positions -- Add vout index to transaction id in transactions details dialog -- Add network traffic graph in debug window -- Add open URI dialog -- Add Coin Control Features -- Improve receive coins workflow: make the 'Receive' tab into a form to request - payments, and move historical address list functionality to File menu. -- Rebrand to `Bitcoin Core` -- Move initialization/shutdown to a thread. This prevents "Not responding" - messages during startup. Also show a window during shutdown. -- Don't regenerate autostart link on every client startup -- Show and store message of normal bitcoin:URI -- Fix richtext detection hang issue on very old Qt versions -- OS X: Make use of the 10.8+ user notification center to display Growl-like - notifications -- OS X: Added NSHighResolutionCapable flag to Info.plist for better font - rendering on Retina displays. -- OS X: Fix bitcoin-qt startup crash when clicking dock icon -- Linux: Fix Gnome bitcoin: URI handler - -Miscellaneous: - -- Add Linux script (contrib/qos/tc.sh) to limit outgoing bandwidth -- Add '-regtest' mode, similar to testnet but private with instant block - generation with 'setgenerate' RPC. -- Add 'linearize.py' script to contrib, for creating bootstrap.dat -- Add separate bitcoin-cli client - -Credits --------- - -Thanks to everyone who contributed to this release: - -- Andrey -- Ashley Holman -- b6393ce9-d324-4fe1-996b-acf82dbc3d53 -- bitsofproof -- Brandon Dahler -- Calvin Tam -- Christian Decker -- Christian von Roques -- Christopher Latham -- Chuck -- coblee -- constantined -- Cory Fields -- Cozz Lovan -- daniel -- Daniel Larimer -- David Hill -- Dmitry Smirnov -- Drak -- Eric Lombrozo -- fanquake -- fcicq -- Florin -- frewil -- Gavin Andresen -- Gregory Maxwell -- gubatron -- Guillermo Céspedes Tabárez -- Haakon Nilsen -- HaltingState -- Han Lin Yap -- harry -- Ian Kelling -- Jeff Garzik -- Johnathan Corgan -- Jonas Schnelli -- Josh Lehan -- Josh Triplett -- Julian Langschaedel -- Kangmo -- Lake Denman -- Luke Dashjr -- Mark Friedenbach -- Matt Corallo -- Michael Bauer -- Michael Ford -- Michagogo -- Midnight Magic -- Mike Hearn -- Nils Schneider -- Noel Tiernan -- Olivier Langlois -- patrick s -- Patrick Strateman -- paveljanik -- Peter Todd -- phantomcircuit -- phelixbtc -- Philip Kaufmann -- Pieter Wuille -- Rav3nPL -- R E Broadley -- regergregregerrge -- Robert Backhaus -- Roman Mindalev -- Rune K. Svendsen -- Ryan Niebur -- Scott Ellis -- Scott Willeke -- Sergey Kazenyuk -- Shawn Wilkinson -- Sined -- sje -- Subo1978 -- super3 -- Tamas Blummer -- theuni -- Thomas Holenstein -- Timon Rapp -- Timothy Stranex -- Tom Geller -- Torstein Husebø -- Vaclav Vobornik -- vhf / victor felder -- Vinnie Falco -- Warren Togami -- Wil Bown -- Wladimir J. van der Laan diff --git a/doc/bitcoin-release-notes/release-notes-0.9.1.md b/doc/bitcoin-release-notes/release-notes-0.9.1.md deleted file mode 100644 index 0552053d2..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.9.1.md +++ /dev/null @@ -1,53 +0,0 @@ -Bitcoin Core version 0.9.1 is now available from: - - https://bitcoin.org/bin/0.9.1/ - -This is a security update. It is recommended to upgrade to this release -as soon as possible. - -It is especially important to upgrade if you currently have version -0.9.0 installed and are using the graphical interface OR you are using -bitcoind from any pre-0.9.1 version, and have enabled SSL for RPC and -have configured allowip to allow rpc connections from potentially -hostile hosts. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you run -0.9.1 your blockchain files will be re-indexed, which will take anywhere from -30 minutes to several hours, depending on the speed of your machine. - -0.9.1 Release notes -======================= - -No code changes were made between 0.9.0 and 0.9.1. Only the dependencies were changed. - -- Upgrade OpenSSL to 1.0.1g. This release fixes the following vulnerabilities which can - affect the Bitcoin Core software: - - - CVE-2014-0160 ("heartbleed") - A missing bounds check in the handling of the TLS heartbeat extension can - be used to reveal up to 64k of memory to a connected client or server. - - - CVE-2014-0076 - The Montgomery ladder implementation in OpenSSL does not ensure that - certain swap operations have a constant-time behavior, which makes it - easier for local users to obtain ECDSA nonces via a FLUSH+RELOAD cache - side-channel attack. - -- Add statically built executables to Linux build - -Credits --------- - -Credits go to the OpenSSL team for fixing the vulnerabilities quickly. diff --git a/doc/bitcoin-release-notes/release-notes-0.9.2.1.md b/doc/bitcoin-release-notes/release-notes-0.9.2.1.md deleted file mode 100644 index 3168ad1a5..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.9.2.1.md +++ /dev/null @@ -1,207 +0,0 @@ -Bitcoin Core version 0.9.2.1 is now available from: - - https://bitcoin.org/bin/0.9.2.1/ - -This is a new minor version release, bringing mostly bug fixes and some minor -improvements. OpenSSL has been updated because of a security issue (CVE-2014-0224). -Upgrading to this release is recommended. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you run -0.9.2.1 your blockchain files will be re-indexed, which will take anywhere from -30 minutes to several hours, depending on the speed of your machine. - -Downgrading warnings --------------------- - -The 'chainstate' for this release is not always compatible with previous -releases, so if you run 0.9.x and then decide to switch back to a -0.8.x release you might get a blockchain validation error when starting the -old release (due to 'pruned outputs' being omitted from the index of -unspent transaction outputs). - -Running the old release with the -reindex option will rebuild the chainstate -data structures and correct the problem. - -Also, the first time you run a 0.8.x release on a 0.9 wallet it will rescan -the blockchain for missing spent coins, which will take a long time (tens -of minutes on a typical machine). - -Important changes -================== - -Gitian OSX build ------------------ - -The deterministic build system that was already used for Windows and Linux -builds is now used for OSX as well. Although the resulting executables have -been tested quite a bit, there could be possible regressions. Be sure to report -these on the Github bug tracker mentioned above. - -Compatibility of Linux build ------------------------------ - -For Linux we now build against Qt 4.6, and filter the symbols for libstdc++ and glibc. -This brings back compatibility with - -- Debian 6+ / Tails -- Ubuntu 10.04 -- CentOS 6.5 - -0.9.2 - 0.9.2.1 Release notes -======================= - -The OpenSSL dependency in the gitian builds has been upgraded to 1.0.1h because of CVE-2014-0224. - -RPC: - -- Add `getwalletinfo`, `getblockchaininfo` and `getnetworkinfo` calls (will replace hodge-podge `getinfo` at some point) -- Add a `relayfee` field to `getnetworkinfo` -- Fix RPC related shutdown hangs and leaks -- Always show syncnode in `getpeerinfo` -- `sendrawtransaction`: report the reject code and reason, and make it possible to re-send transactions that are already in the mempool -- `getmininginfo` show right genproclimit - -Command-line options: - -- Fix `-printblocktree` output -- Show error message if ReadConfigFile fails - -Block-chain handling and storage: - -- Fix for GetBlockValue() after block 13,440,000 (BIP42) -- Upgrade leveldb to 1.17 - -Protocol and network code: - -- Per-peer block download tracking and stalled download detection -- Add new DNS seed from bitnodes.io -- Prevent socket leak in ThreadSocketHandler and correct some proxy related socket leaks -- Use pnode->nLastRecv as sync score (was the wrong way around) - -Wallet: - -- Make GetAvailableCredit run GetHash() only once per transaction (performance improvement) -- Lower paytxfee warning threshold from 0.25 BTC to 0.01 BTC -- Fix importwallet nTimeFirstKey (trigger necessary rescans) -- Log BerkeleyDB version at startup -- CWallet init fix - -Build system: - -- Add OSX build descriptors to gitian -- Fix explicit --disable-qt-dbus -- Don't require db_cxx.h when compiling with wallet disabled and GUI enabled -- Improve missing boost error reporting -- Upgrade miniupnpc version to 1.9 -- gitian-linux: --enable-glibc-back-compat for binary compatibility with old distributions -- gitian: don't export any symbols from executable -- gitian: build against Qt 4.6 -- devtools: add script to check symbols from Linux gitian executables -- Remove build-time no-IPv6 setting - -GUI: - -- Fix various coin control visual issues -- Show number of in/out connections in debug console -- Show weeks as well as years behind for long timespans behind -- Enable and disable the Show and Remove buttons for requested payments history based on whether any entry is selected. -- Show also value for options overridden on command line in options dialog -- Fill in label from address book also for URIs -- Fixes feel when resizing the last column on tables (issue #2862) -- Fix ESC in disablewallet mode -- Add expert section to wallet tab in optionsdialog -- Do proper boost::path conversion (fixes unicode in datadir) -- Only override -datadir if different from the default (fixes -datadir in config file) -- Show rescan progress at start-up -- Show importwallet progress -- Get required locks upfront in polling functions (avoids hanging on locks) -- Catch Windows shutdown events while client is running -- Optionally add third party links to transaction context menu -- Check for !pixmap() before trying to export QR code (avoids crashes when no QR code could be generated) -- Fix "Start bitcoin on system login" - -Miscellaneous: - -- Replace non-threadsafe C functions (gmtime, strerror and setlocale) -- Add missing cs_main and wallet locks -- Avoid exception at startup when system locale not recognized -- Changed bitrpc.py's raw_input to getpass for passwords to conceal characters during command line input -- devtools: add a script to fetch and postprocess translations - -Credits --------- - -Thanks to everyone who contributed to this release: - -- Addy Yeow -- Altoidnerd -- Andrea D'Amore -- Andreas Schildbach -- Bardi Harborow -- Brandon Dahler -- Bryan Bishop -- Chris Beams -- Christian von Roques -- Cory Fields -- Cozz Lovan -- daniel -- Daniel Newton -- David A. Harding -- ditto-b -- duanemoody -- Eric S. Bullington -- Fabian Raetz -- Gavin Andresen -- Gregory Maxwell -- gubatron -- Haakon Nilsen -- harry -- Hector Jusforgues -- Isidoro Ghezzi -- Jeff Garzik -- Johnathan Corgan -- jtimon -- Kamil Domanski -- langerhans -- Luke Dashjr -- Manuel Araoz -- Mark Friedenbach -- Matt Corallo -- Matthew Bogosian -- Meeh -- Michael Ford -- Michagogo -- Mikael Wikman -- Mike Hearn -- olalonde -- paveljanik -- peryaudo -- Philip Kaufmann -- philsong -- Pieter Wuille -- R E Broadley -- richierichrawr -- Rune K. Svendsen -- rxl -- shshshsh -- Simon de la Rouviere -- Stuart Cardall -- super3 -- Telepatheic -- Thomas Zander -- Torstein Husebø -- Warren Togami -- Wladimir J. van der Laan -- Yoichi Hirai diff --git a/doc/bitcoin-release-notes/release-notes-0.9.2.md b/doc/bitcoin-release-notes/release-notes-0.9.2.md deleted file mode 100644 index a2749e549..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.9.2.md +++ /dev/null @@ -1,207 +0,0 @@ -Bitcoin Core version 0.9.2 is now available from: - - https://bitcoin.org/bin/0.9.2/ - -This is a new minor version release, bringing mostly bug fixes and some minor -improvements. OpenSSL has been updated because of a security issue (CVE-2014-0224). -Upgrading to this release is recommended. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you run -0.9.2 your blockchain files will be re-indexed, which will take anywhere from -30 minutes to several hours, depending on the speed of your machine. - -Downgrading warnings --------------------- - -The 'chainstate' for this release is not always compatible with previous -releases, so if you run 0.9.x and then decide to switch back to a -0.8.x release you might get a blockchain validation error when starting the -old release (due to 'pruned outputs' being omitted from the index of -unspent transaction outputs). - -Running the old release with the -reindex option will rebuild the chainstate -data structures and correct the problem. - -Also, the first time you run a 0.8.x release on a 0.9 wallet it will rescan -the blockchain for missing spent coins, which will take a long time (tens -of minutes on a typical machine). - -Important changes -================== - -Gitian OSX build ------------------ - -The deterministic build system that was already used for Windows and Linux -builds is now used for OSX as well. Although the resulting executables have -been tested quite a bit, there could be possible regressions. Be sure to report -these on the Github bug tracker mentioned above. - -Compatibility of Linux build ------------------------------ - -For Linux we now build against Qt 4.6, and filter the symbols for libstdc++ and glibc. -This brings back compatibility with - -- Debian 6+ / Tails -- Ubuntu 10.04 -- CentOS 6.5 - -0.9.2 Release notes -======================= - -The OpenSSL dependency in the gitian builds has been upgraded to 1.0.1h because of CVE-2014-0224. - -RPC: - -- Add `getwalletinfo`, `getblockchaininfo` and `getnetworkinfo` calls (will replace hodge-podge `getinfo` at some point) -- Add a `relayfee` field to `getnetworkinfo` -- Fix RPC related shutdown hangs and leaks -- Always show syncnode in `getpeerinfo` -- `sendrawtransaction`: report the reject code and reason, and make it possible to re-send transactions that are already in the mempool -- `getmininginfo` show right genproclimit - -Command-line options: - -- Fix `-printblocktree` output -- Show error message if ReadConfigFile fails - -Block-chain handling and storage: - -- Fix for GetBlockValue() after block 13,440,000 (BIP42) -- Upgrade leveldb to 1.17 - -Protocol and network code: - -- Per-peer block download tracking and stalled download detection -- Add new DNS seed from bitnodes.io -- Prevent socket leak in ThreadSocketHandler and correct some proxy related socket leaks -- Use pnode->nLastRecv as sync score (was the wrong way around) - -Wallet: - -- Make GetAvailableCredit run GetHash() only once per transaction (performance improvement) -- Lower paytxfee warning threshold from 0.25 BTC to 0.01 BTC -- Fix importwallet nTimeFirstKey (trigger necessary rescans) -- Log BerkeleyDB version at startup -- CWallet init fix - -Build system: - -- Add OSX build descriptors to gitian -- Fix explicit --disable-qt-dbus -- Don't require db_cxx.h when compiling with wallet disabled and GUI enabled -- Improve missing boost error reporting -- Upgrade miniupnpc version to 1.9 -- gitian-linux: --enable-glibc-back-compat for binary compatibility with old distributions -- gitian: don't export any symbols from executable -- gitian: build against Qt 4.6 -- devtools: add script to check symbols from Linux gitian executables -- Remove build-time no-IPv6 setting - -GUI: - -- Fix various coin control visual issues -- Show number of in/out connections in debug console -- Show weeks as well as years behind for long timespans behind -- Enable and disable the Show and Remove buttons for requested payments history based on whether any entry is selected. -- Show also value for options overridden on command line in options dialog -- Fill in label from address book also for URIs -- Fixes feel when resizing the last column on tables (issue #2862) -- Fix ESC in disablewallet mode -- Add expert section to wallet tab in optionsdialog -- Do proper boost::path conversion (fixes unicode in datadir) -- Only override -datadir if different from the default (fixes -datadir in config file) -- Show rescan progress at start-up -- Show importwallet progress -- Get required locks upfront in polling functions (avoids hanging on locks) -- Catch Windows shutdown events while client is running -- Optionally add third party links to transaction context menu -- Check for !pixmap() before trying to export QR code (avoids crashes when no QR code could be generated) -- Fix "Start bitcoin on system login" - -Miscellaneous: - -- Replace non-threadsafe C functions (gmtime, strerror and setlocale) -- Add missing cs_main and wallet locks -- Avoid exception at startup when system locale not recognized -- Changed bitrpc.py's raw_input to getpass for passwords to conceal characters during command line input -- devtools: add a script to fetch and postprocess translations - -Credits --------- - -Thanks to everyone who contributed to this release: - -- Addy Yeow -- Altoidnerd -- Andrea D'Amore -- Andreas Schildbach -- Bardi Harborow -- Brandon Dahler -- Bryan Bishop -- Chris Beams -- Christian von Roques -- Cory Fields -- Cozz Lovan -- daniel -- Daniel Newton -- David A. Harding -- ditto-b -- duanemoody -- Eric S. Bullington -- Fabian Raetz -- Gavin Andresen -- Gregory Maxwell -- gubatron -- Haakon Nilsen -- harry -- Hector Jusforgues -- Isidoro Ghezzi -- Jeff Garzik -- Johnathan Corgan -- jtimon -- Kamil Domanski -- langerhans -- Luke Dashjr -- Manuel Araoz -- Mark Friedenbach -- Matt Corallo -- Matthew Bogosian -- Meeh -- Michael Ford -- Michagogo -- Mikael Wikman -- Mike Hearn -- olalonde -- paveljanik -- peryaudo -- Philip Kaufmann -- philsong -- Pieter Wuille -- R E Broadley -- richierichrawr -- Rune K. Svendsen -- rxl -- shshshsh -- Simon de la Rouviere -- Stuart Cardall -- super3 -- Telepatheic -- Thomas Zander -- Torstein Husebø -- Warren Togami -- Wladimir J. van der Laan -- Yoichi Hirai diff --git a/doc/bitcoin-release-notes/release-notes-0.9.3.md b/doc/bitcoin-release-notes/release-notes-0.9.3.md deleted file mode 100644 index 0765a360b..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.9.3.md +++ /dev/null @@ -1,101 +0,0 @@ -Bitcoin Core version 0.9.3 is now available from: - - https://bitcoin.org/bin/0.9.3/ - -This is a new minor version release, bringing only bug fixes and updated -translations. Upgrading to this release is recommended. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -Upgrading and downgrading -========================== - -How to Upgrade --------------- - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -If you are upgrading from version 0.7.2 or earlier, the first time you run -0.9.3 your blockchain files will be re-indexed, which will take anywhere from -30 minutes to several hours, depending on the speed of your machine. - -Downgrading warnings --------------------- - -The 'chainstate' for this release is not always compatible with previous -releases, so if you run 0.9.x and then decide to switch back to a -0.8.x release you might get a blockchain validation error when starting the -old release (due to 'pruned outputs' being omitted from the index of -unspent transaction outputs). - -Running the old release with the -reindex option will rebuild the chainstate -data structures and correct the problem. - -Also, the first time you run a 0.8.x release on a 0.9 wallet it will rescan -the blockchain for missing spent coins, which will take a long time (tens -of minutes on a typical machine). - -0.9.3 Release notes -======================= - -RPC: -- Avoid a segfault on getblock if it can't read a block from disk -- Add paranoid return value checks in base58 - -Protocol and network code: -- Don't poll showmyip.com, it doesn't exist anymore -- Add a way to limit deserialized string lengths and use it -- Add a new checkpoint at block 295,000 -- Increase IsStandard() scriptSig length -- Avoid querying DNS seeds, if we have open connections -- Remove a useless millisleep in socket handler -- Stricter memory limits on CNode -- Better orphan transaction handling -- Add `-maxorphantx=` and `-maxorphanblocks=` options for control over the maximum orphan transactions and blocks - -Wallet: -- Check redeemScript size does not exceed 520 byte limit -- Ignore (and warn about) too-long redeemScripts while loading wallet - -GUI: -- fix 'opens in testnet mode when presented with a BIP-72 link with no fallback' -- AvailableCoins: acquire cs_main mutex -- Fix unicode character display on MacOSX - -Miscellaneous: -- key.cpp: fail with a friendlier message on missing ssl EC support -- Remove bignum dependency for scripts -- Upgrade OpenSSL to 1.0.1i (see https://www.openssl.org/news/secadv_20140806.txt - just to be sure, no critical issues for Bitcoin Core) -- Upgrade miniupnpc to 1.9.20140701 -- Fix boost detection in build system on some platforms - -Credits --------- - -Thanks to everyone who contributed to this release: - -- Andrew Poelstra -- Cory Fields -- Gavin Andresen -- Jeff Garzik -- Johnathan Corgan -- Julian Haight -- Michael Ford -- Pavel Vasin -- Peter Todd -- phantomcircuit -- Pieter Wuille -- Rose Toomey -- Ruben Dario Ponticelli -- shshshsh -- Trevin Hofmann -- Warren Togami -- Wladimir J. van der Laan -- Zak Wilcox - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/bitcoin-release-notes/release-notes-0.9.4.md b/doc/bitcoin-release-notes/release-notes-0.9.4.md deleted file mode 100644 index 7ee73246a..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.9.4.md +++ /dev/null @@ -1,95 +0,0 @@ -Bitcoin Core version 0.9.4 is now available from: - - https://bitcoin.org/bin/0.9.4/ - -This is a new minor version release, bringing only bug fixes and updated -translations. Upgrading to this release is recommended. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade -=============== - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -OpenSSL Warning -================ - -OpenSSL 1.0.0p / 1.0.1k was recently released and is being pushed out by -various operating system maintainers. Review by Gregory Maxwell determined that -this update is incompatible with the Bitcoin system and could lead to consensus -forks. - -Bitcoin Core released binaries from https://bitcoin.org are unaffected, -as are any built with the gitian deterministic build system. - -However, if you are running either - -- The Ubuntu PPA from https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin -- A third-party or self-compiled Bitcoin Core - -upgrade to Bitcoin Core 0.9.4, which includes a workaround, **before** updating -OpenSSL. - -The incompatibility is due to the OpenSSL update changing the -behavior of ECDSA validation to reject any signature which is -not encoded in a very rigid manner. This was a result of -OpenSSL's change for CVE-2014-8275 "Certificate fingerprints -can be modified". - -We are specifically aware of potential hard-forks due to signature -encoding handling and had been hoping to close them via BIP62 in 0.10. -BIP62's purpose is to improve transaction malleability handling and -as a side effect rigidly defines the encoding for signatures, but the -overall scope of BIP62 has made it take longer than we'd like to -deploy. - -0.9.4 changelog -================ - -Validation: -- `b8e81b7` consensus: guard against openssl's new strict DER checks -- `60c51f1` fail immediately on an empty signature -- `037bfef` Improve robustness of DER recoding code - -Command-line options: -- `cd5164a` Make -proxy set all network types, avoiding a connect leak. - -P2P: -- `bb424e4` Limit the number of new addressses to accumulate - -RPC: -- `0a94661` Disable SSLv3 (in favor of TLS) for the RPC client and server. - -Build system: -- `f047dfa` gitian: openssl-1.0.1i.tar.gz -> openssl-1.0.1k.tar.gz -- `5b9f78d` build: Fix OSX build when using Homebrew and qt5 -- `ffab1dd` Keep symlinks when copying into .app bundle -- `613247f` osx: fix signing to make Gatekeeper happy (again) - -Miscellaneous: -- `25b49b5` Refactor -alertnotify code -- `2743529` doc: Add instructions for consistent Mac OS X build names - -Credits --------- - -Thanks to who contributed to this release, at least: - -- Cory Fields -- Gavin Andresen -- Gregory Maxwell -- Jeff Garzik -- Luke Dashjr -- Matt Corallo -- Pieter Wuille -- Saivann -- Sergio Demian Lerner -- Wladimir J. van der Laan - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/bitcoin-release-notes/release-notes-0.9.5.md b/doc/bitcoin-release-notes/release-notes-0.9.5.md deleted file mode 100644 index bed0af987..000000000 --- a/doc/bitcoin-release-notes/release-notes-0.9.5.md +++ /dev/null @@ -1,60 +0,0 @@ -Bitcoin Core version 0.9.5 is now available from: - - https://bitcoin.org/bin/0.9.5/ - -This is a new minor version release, with the goal of backporting BIP66. There -are also a few bug fixes and updated translations. Upgrading to this release is -recommended. - -Please report bugs using the issue tracker at github: - - https://github.com/bitcoin/bitcoin/issues - -How to Upgrade -=============== - -If you are running an older version, shut it down. Wait until it has completely -shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) or -bitcoind/bitcoin-qt (on Linux). - -Notable changes -================ - -Mining and relay policy enhancements ------------------------------------- - -Bitcoin Core's block templates are now for version 3 blocks only, and any mining -software relying on its `getblocktemplate` must be updated in parallel to use -libblkmaker either version 0.4.2 or any version from 0.5.1 onward. -If you are solo mining, this will affect you the moment you upgrade Bitcoin -Core, which must be done prior to BIP66 achieving its 951/1001 status. -If you are mining with the stratum mining protocol: this does not affect you. -If you are mining with the getblocktemplate protocol to a pool: this will affect -you at the pool operator's discretion, which must be no later than BIP66 -achieving its 951/1001 status. - -0.9.5 changelog -================ - -- `74f29c2` Check pindexBestForkBase for null -- `9cd1dd9` Fix priority calculation in CreateTransaction -- `6b4163b` Sanitize command strings before logging them. -- `3230b32` Raise version of created blocks, and enforce DERSIG in mempool -- `989d499` Backport of some of BIP66's tests -- `ab03660` Implement BIP 66 validation rules and switchover logic -- `8438074` build: fix dynamic boost check when --with-boost= is used - -Credits --------- - -Thanks to who contributed to this release, at least: - -- 21E14 -- Alex Morcos -- Cory Fields -- Gregory Maxwell -- Pieter Wuille -- Wladimir J. van der Laan - -As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). From aa29056d50bdb0715919bf6fb02ff6fb0194cc08 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 17:52:47 +0000 Subject: [PATCH 026/146] Remove deprecated init.md --- doc/init.md | 101 ---------------------------------------------------- 1 file changed, 101 deletions(-) delete mode 100644 doc/init.md diff --git a/doc/init.md b/doc/init.md deleted file mode 100644 index 794b827d0..000000000 --- a/doc/init.md +++ /dev/null @@ -1,101 +0,0 @@ -*** Warning: This document has not been updated for BitcoinZ and may be inaccurate. *** - -Sample init scripts and service configuration for bitcoind -========================================================== - -Sample scripts and configuration files for systemd, Upstart and OpenRC -can be found in the contrib/init folder. - - contrib/init/bitcoind.service: systemd service unit configuration - contrib/init/bitcoind.openrc: OpenRC compatible SysV style init script - contrib/init/bitcoind.openrcconf: OpenRC conf.d file - contrib/init/bitcoind.conf: Upstart service configuration file - contrib/init/bitcoind.init: CentOS compatible SysV style init script - -1. Service User ---------------------------------- - -All three startup configurations assume the existence of a "bitcoin" user -and group. They must be created before attempting to use these scripts. - -2. Configuration ---------------------------------- - -At a bare minimum, bitcoind requires that the rpcpassword setting be set -when running as a daemon. If the configuration file does not exist or this -setting is not set, bitcoind will shutdown promptly after startup. - -This password does not have to be remembered or typed as it is mostly used -as a fixed token that bitcoind and client programs read from the configuration -file, however it is recommended that a strong and secure password be used -as this password is security critical to securing the wallet should the -wallet be enabled. - -If bitcoind is run with "-daemon" flag, and no rpcpassword is set, it will -print a randomly generated suitable password to stderr. You can also -generate one from the shell yourself like this: - -bash -c 'tr -dc a-zA-Z0-9 < /dev/urandom | head -c32 && echo' - - -For an example configuration file that describes the configuration settings, -see contrib/debian/examples/bitcoin.conf. - -3. Paths ---------------------------------- - -All three configurations assume several paths that might need to be adjusted. - -Binary: /usr/bin/bitcoind -Configuration file: /etc/bitcoin/bitcoin.conf -Data directory: /var/lib/bitcoind -PID file: /var/run/bitcoind/bitcoind.pid (OpenRC and Upstart) - /var/lib/bitcoind/bitcoind.pid (systemd) -Lock file: /var/lock/subsys/bitcoind (CentOS) - -The configuration file, PID directory (if applicable) and data directory -should all be owned by the bitcoin user and group. It is advised for security -reasons to make the configuration file and data directory only readable by the -bitcoin user and group. Access to bitcoin-cli and other bitcoind rpc clients -can then be controlled by group membership. - -4. Installing Service Configuration ------------------------------------ - -4a) systemd - -Installing this .service file consists of just copying it to -/usr/lib/systemd/system directory, followed by the command -"systemctl daemon-reload" in order to update running systemd configuration. - -To test, run "systemctl start bitcoind" and to enable for system startup run -"systemctl enable bitcoind" - -4b) OpenRC - -Rename bitcoind.openrc to bitcoind and drop it in /etc/init.d. Double -check ownership and permissions and make it executable. Test it with -"/etc/init.d/bitcoind start" and configure it to run on startup with -"rc-update add bitcoind" - -4c) Upstart (for Debian/Ubuntu based distributions) - -Drop bitcoind.conf in /etc/init. Test by running "service bitcoind start" -it will automatically start on reboot. - -NOTE: This script is incompatible with CentOS 5 and Amazon Linux 2014 as they -use old versions of Upstart and do not supply the start-stop-daemon utility. - -4d) CentOS - -Copy bitcoind.init to /etc/init.d/bitcoind. Test by running "service bitcoind start". - -Using this script, you can adjust the path and flags to the bitcoind program by -setting the BITCOIND and FLAGS environment variables in the file -/etc/sysconfig/bitcoind. You can also use the DAEMONOPTS environment variable here. - -5. Auto-respawn ------------------------------------ - -Auto respawning is currently only configured for Upstart and systemd. -Reasonable defaults have been chosen but YMMV. From 8f20b5f1248acd71d4e6a1c020e1dcfb9b1ce88e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 17:54:47 +0000 Subject: [PATCH 027/146] Add a missing % to a string interpolation in rpc test framework --- qa/rpc-tests/test_framework/mininode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 47067eb63..8c5ad8255 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -771,7 +771,7 @@ def __repr__(self): r += " vJoinSplit=%r" % (self.vJoinSplit,) if len(self.vJoinSplit) > 0: r += " joinSplitPubKey=%064x joinSplitSig=%064x" \ - (self.joinSplitPubKey, self.joinSplitSig) + % (self.joinSplitPubKey, self.joinSplitSig) if len(self.shieldedSpends) > 0 or len(self.shieldedOutputs) > 0: r += " bindingSig=%064x" % (self.bindingSig,) r += ")" From 47ddc4d3d74e6689d75735c64b2e5eab64a4f461 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 17:56:02 +0000 Subject: [PATCH 028/146] Fix buffer overflows in P2PKH tests --- src/test/script_P2PKH_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/script_P2PKH_tests.cpp b/src/test/script_P2PKH_tests.cpp index 99d826345..9418b0d02 100644 --- a/src/test/script_P2PKH_tests.cpp +++ b/src/test/script_P2PKH_tests.cpp @@ -47,12 +47,12 @@ BOOST_AUTO_TEST_CASE(IsPayToPublicKeyHash) static const unsigned char missing2[] = { OP_DUP, OP_HASH160, 20, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; - BOOST_CHECK(!CScript(missing2, missing2+sizeof(missing)).IsPayToPublicKeyHash()); + BOOST_CHECK(!CScript(missing2, missing2+sizeof(missing2)).IsPayToPublicKeyHash()); static const unsigned char tooshort[] = { OP_DUP, OP_HASH160, 2, 0,0, OP_EQUALVERIFY, OP_CHECKSIG }; - BOOST_CHECK(!CScript(tooshort, tooshort+sizeof(direct)).IsPayToPublicKeyHash()); + BOOST_CHECK(!CScript(tooshort, tooshort+sizeof(tooshort)).IsPayToPublicKeyHash()); } From e15994074da20fe68e9f6c302ecabe9ff0cb93c7 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 18:41:25 +0000 Subject: [PATCH 029/146] Trivial: tiny c++11 refactors --- src/limitedmap.h | 7 +++++-- src/rpc/server.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/limitedmap.h b/src/limitedmap.h index 0e88c056e..40f6ff7e3 100644 --- a/src/limitedmap.h +++ b/src/limitedmap.h @@ -63,8 +63,11 @@ class limitedmap } void update(const_iterator itIn, const mapped_type& v) { - // TODO: When we switch to C++11, use map.erase(itIn, itIn) to get the non-const iterator. - iterator itTarget = map.find(itIn->first); + // Using map::erase() with empty range instead of map::find() to get a non-const iterator, + // since it is a constant time operation in C++11. For more details, see + // https://stackoverflow.com/questions/765148/how-to-remove-constness-of-const-iterator + iterator itTarget = map.erase(itIn, itIn); + if (itTarget == map.end()) return; std::pair itPair = rmap.equal_range(itTarget->second); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 60f4d88ef..a369c6f0c 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -27,6 +27,8 @@ #include #include // for to_upper() +#include // for unique_ptr + using namespace RPCServer; using namespace std; using namespace boost::placeholders; @@ -37,9 +39,8 @@ static std::string rpcWarmupStatus("RPC server started"); static CCriticalSection cs_rpcWarmup; /* Timer-creating functions */ static std::vector timerInterfaces; -/* Map of name to timer. - * @note Can be changed to std::unique_ptr when C++11 */ -static std::map > deadlineTimers; +/* Map of name to timer. */ +static std::map > deadlineTimers; static struct CRPCSignals { @@ -514,7 +515,7 @@ void RPCRunLater(const std::string& name, std::function func, int64_ deadlineTimers.erase(name); RPCTimerInterface* timerInterface = timerInterfaces[0]; LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); - deadlineTimers.insert(std::make_pair(name, boost::shared_ptr(timerInterface->NewTimer(func, nSeconds*1000)))); + deadlineTimers.emplace(name, std::unique_ptr(timerInterface->NewTimer(func, nSeconds*1000))); } CRPCTable tableRPC; From d10b72a5b63b91c897d3d384c258478f3e3a2004 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 18:42:14 +0000 Subject: [PATCH 030/146] Initialize size_t in CTxMemPool::DynamicMemoryUsage before using it --- src/txmempool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 75a5113ec..ae741a29a 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -813,7 +813,7 @@ size_t CTxMemPool::DynamicMemoryUsage() const { total += memusage::DynamicUsage(recentlyEvicted) + memusage::DynamicUsage(weightedTxTree); // Insight-related structures - size_t insight; + size_t insight = 0; insight += memusage::DynamicUsage(mapAddress); insight += memusage::DynamicUsage(mapAddressInserted); insight += memusage::DynamicUsage(mapSpent); From 764710736c8aca2f67498b980b2584bc9b8da14c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 18:42:53 +0000 Subject: [PATCH 031/146] Remove an unused CCriticalSection --- src/zcash/JoinSplit.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index ebea446e4..40818c1af 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -18,8 +18,6 @@ namespace libzcash { -static CCriticalSection cs_ParamsIO; - template class JoinSplitCircuit : public JoinSplit { public: From 8c40047f399c302cfa9ee90b959189de9018311f Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 19:25:00 +0000 Subject: [PATCH 032/146] Resolve Boost deprecation warnings --- src/main.h | 1 + src/rpc/protocol.cpp | 2 +- src/util.cpp | 4 ++-- src/wallet/wallet.cpp | 4 +++- src/wallet/walletdb.cpp | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main.h b/src/main.h index ea32af125..837729af2 100644 --- a/src/main.h +++ b/src/main.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index cd92119e4..1be6b2cb8 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -71,7 +71,7 @@ static const std::string COOKIEAUTH_FILE = ".cookie"; fs::path GetAuthCookieFile() { fs::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE)); - if (!path.is_complete()) path = GetDataDir() / path; + if (!path.is_absolute()) path = GetDataDir() / path; return path; } diff --git a/src/util.cpp b/src/util.cpp index ca384033c..184645783 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -527,7 +527,7 @@ void ClearDatadirCache() fs::path GetConfigFile(const std::string& confPath) { fs::path pathConfigFile(confPath); - if (!pathConfigFile.is_complete()) + if (!pathConfigFile.is_absolute()) pathConfigFile = GetDataDir(false) / pathConfigFile; return pathConfigFile; @@ -562,7 +562,7 @@ void ReadConfigFile(const std::string& confPath, fs::path GetPidFile() { fs::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME)); - if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; + if (!pathPidFile.is_absolute()) pathPidFile = GetDataDir() / pathPidFile; return pathPidFile; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9acae2db3..1d34e8e76 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -900,8 +900,10 @@ bool CWallet::Verify() uiInterface.InitMessage(_("Verifying wallet...")); // Wallet file must be a plain filename without a directory - if (walletFile != fs::basename(walletFile) + fs::extension(walletFile)) + fs::path walletPath(walletFile); + if (walletFile != walletPath.stem().string() + walletPath.extension().string()) { return UIError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string())); + } if (!bitdb.Open(GetDataDir())) { diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index d432c7b84..e48280921 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1175,7 +1175,7 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) pathDest /= wallet.strWalletFile; try { - fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists); + fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing); LogPrintf("copied %s to %s\n", wallet.strWalletFile, pathDest.string()); return true; } catch (const fs::filesystem_error& e) { From 645bc30e14d7d825f897b7fc9f70744f7e9585b5 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 22:13:22 +0000 Subject: [PATCH 033/146] Add -debuglogfile option --- doc/release-notes.md | 4 ++++ src/init.cpp | 8 ++++++-- src/util.cpp | 26 +++++++++++++++++++++----- src/util.h | 4 +++- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index e6452b8c0..5561d8fd7 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -8,6 +8,10 @@ Resolved a critical bug that prevented the program from starting up correctly in The issue occurred during the startup process when the program attempted to rescan the latest indexed blocks. A failure in the LoadBlockIndex() function, caused by a corrupted index file, was identified as the root cause. +Changed command-line options +----------------------------- +- `-debuglogfile=` can be used to specify an alternative debug logging file. + Upgrading ========= diff --git a/src/init.cpp b/src/init.cpp index 17b11e278..cacfbc40d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -342,6 +342,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-exportdir=

", _("Specify directory to be used when exporting data")); strUsage += HelpMessageOpt("-dbcache=", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); strUsage += HelpMessageOpt("-loadblock=", _("Imports blocks from external blk000??.dat file on startup")); + strUsage += HelpMessageOpt("-debuglogfile=", strprintf(_("Specify location of debug log file: this can be an absolute path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE)); strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-par=", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); @@ -1203,8 +1204,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (GetBoolArg("-shrinkdebugfile", !fDebug)) ShrinkDebugFile(); - if (fPrintToDebugLog) - OpenDebugLog(); + if (fPrintToDebugLog) { + if (!OpenDebugLog()) { + return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string())); + } + } #ifdef ENABLE_WALLET LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); diff --git a/src/util.cpp b/src/util.cpp index 184645783..d4b9c3da4 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -86,6 +86,7 @@ using namespace std; const char * const BITCOIN_CONF_FILENAME = "bitcoinz.conf"; const char * const BITCOIN_PID_FILENAME = "bitcoinzd.pid"; +const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; map mapArgs; map > mapMultiArgs; @@ -152,17 +153,31 @@ static void DebugPrintInit() vMsgsBeforeOpenLog = new list; } -void OpenDebugLog() +fs::path GetDebugLogPath() +{ + fs::path logfile(GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + if (logfile.is_absolute()) { + return logfile; + } else { + return GetDataDir() / logfile; + } +} + +bool OpenDebugLog() { boost::call_once(&DebugPrintInit, debugPrintInitFlag); boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); assert(fileout == NULL); assert(vMsgsBeforeOpenLog); - fs::path pathDebug = GetDataDir() / "debug.log"; + fs::path pathDebug = GetDebugLogPath(); + fileout = fsbridge::fopen(pathDebug, "a"); - if (fileout) setbuf(fileout, NULL); // unbuffered + if (!fileout) { + return false; + } + setbuf(fileout, nullptr); // unbuffered // dump buffered messages from before we opened the log while (!vMsgsBeforeOpenLog->empty()) { FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); @@ -171,6 +186,7 @@ void OpenDebugLog() delete vMsgsBeforeOpenLog; vMsgsBeforeOpenLog = NULL; + return true; } bool LogAcceptCategory(const char* category) @@ -255,7 +271,7 @@ int LogPrintStr(const std::string &str) // reopen the log file, if requested if (fReopenDebugLog) { fReopenDebugLog = false; - fs::path pathDebug = GetDataDir() / "debug.log"; + fs::path pathDebug = GetDebugLogPath(); if (fsbridge::freopen(pathDebug,"a",fileout) != NULL) setbuf(fileout, NULL); // unbuffered } @@ -704,7 +720,7 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { void ShrinkDebugFile() { // Scroll debug.log if it's getting too big - fs::path pathLog = GetDataDir() / "debug.log"; + fs::path pathLog = GetDebugLogPath(); FILE* file = fsbridge::fopen(pathLog, "r"); if (file && fs::file_size(pathLog) > 10 * 1000000) { diff --git a/src/util.h b/src/util.h index 080259513..b9d504977 100644 --- a/src/util.h +++ b/src/util.h @@ -32,6 +32,7 @@ static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; +extern const char * const DEFAULT_DEBUGLOGFILE; extern std::map mapArgs; extern std::map > mapMultiArgs; @@ -134,7 +135,8 @@ void ReadConfigFile(const std::string& confPath, std::map Date: Mon, 26 Aug 2024 22:28:05 +0000 Subject: [PATCH 034/146] [init] Add missing help for args --- src/init.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index cacfbc40d..3d1a15a8e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -325,7 +325,8 @@ std::string HelpMessage(HelpMessageMode mode) // Do not translate _(...) -help-debug options, many technical terms, and only a very small audience, so is unnecessary stress to translators string strUsage = HelpMessageGroup(_("Options:")); - strUsage += HelpMessageOpt("-?", _("This help message")); + strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); + strUsage += HelpMessageOpt("-version", _("Print version and exit")); strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS)); strUsage += HelpMessageOpt("-alertnotify=", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); @@ -409,8 +410,11 @@ std::string HelpMessage(HelpMessageMode mode) #endif strUsage += HelpMessageGroup(_("Debugging/Testing options:")); + strUsage += HelpMessageOpt("-uacomment=", _("Append comment to the user agent string")); if (showDebug) { + strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); + strUsage += HelpMessageOpt("-checkmempool=", strprintf("Run checks every transactions (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE)); strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE)); @@ -430,6 +434,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS)); if (showDebug) { + strUsage += HelpMessageOpt("-mocktime=", "Replace actual time with seconds since epoch (default: 0)"); strUsage += HelpMessageOpt("-limitfreerelay=", strprintf("Continuously rate-limit free transactions to *1000 bytes per minute (default: %u)", DEFAULT_LIMITFREERELAY)); strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", DEFAULT_RELAYPRIORITY)); strUsage += HelpMessageOpt("-maxsigcachesize=", strprintf("Limit size of signature cache to MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); @@ -459,6 +464,9 @@ std::string HelpMessage(HelpMessageMode mode) if (GetBoolArg("-help-debug", false)) strUsage += HelpMessageOpt("-blockversion=", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); + if (showDebug) + strUsage += HelpMessageOpt("-nodebug", "Turn off debugging messages, same as -debug=0"); + #ifdef ENABLE_MINING strUsage += HelpMessageGroup(_("Mining options:")); strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), DEFAULT_GENERATE)); @@ -479,6 +487,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE)); strUsage += HelpMessageOpt("-rpcbind=", _("Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)")); + strUsage += HelpMessageOpt("-rpccookiefile=", _("Location of the auth cookie (default: data dir)")); strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcauth=", _("Username and hashed password for JSON-RPC connections. The field comes in the format: :$. A canonical python script is included in share/rpcuser. This option can be specified multiple times")); From 1817a49997ed2769bcc2dae2ca230e583bb1ba2f Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 22:31:18 +0000 Subject: [PATCH 035/146] rpc: Write authcookie atomically --- src/rpc/protocol.cpp | 21 ++++++++++++++++----- src/rpc/protocol.h | 2 -- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index 1be6b2cb8..74e3410ad 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -68,9 +68,14 @@ static const std::string COOKIEAUTH_USER = "__cookie__"; /** Default name for auth cookie file */ static const std::string COOKIEAUTH_FILE = ".cookie"; -fs::path GetAuthCookieFile() +/** Get name of RPC authentication cookie file */ +static fs::path GetAuthCookieFile(bool temp=false) { - fs::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE)); + std::string arg = GetArg("-rpccookiefile", COOKIEAUTH_FILE); + if (temp) { + arg += ".tmp"; + } + fs::path path(arg); if (!path.is_absolute()) path = GetDataDir() / path; return path; } @@ -85,14 +90,20 @@ bool GenerateAuthCookie(std::string *cookie_out) * these are set to 077 in init.cpp unless overridden with -sysperms. */ std::ofstream file; - fs::path filepath = GetAuthCookieFile(); - file.open(filepath.string().c_str()); + fs::path filepath_tmp = GetAuthCookieFile(true); + file.open(filepath_tmp.string().c_str()); if (!file.is_open()) { - LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string()); + LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string()); return false; } file << cookie; file.close(); + + fs::path filepath = GetAuthCookieFile(false); + if (!RenameOver(filepath_tmp, filepath)) { + LogPrintf("Unable to rename cookie authentication file %s to %s\n", filepath_tmp.string(), filepath.string()); + return false; + } LogPrintf("Generated RPC authentication cookie %s\n", filepath.string()); if (cookie_out) diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index e79e54888..9c69eaefe 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -82,8 +82,6 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); UniValue JSONRPCError(int code, const std::string& message); -/** Get name of RPC authentication cookie file */ -fs::path GetAuthCookieFile(); /** Generate a new RPC authentication cookie and write it to disk */ bool GenerateAuthCookie(std::string *cookie_out); /** Read the RPC authentication cookie from disk */ From fa3fa606d7d5fa20e136ded31ab51f07f5aa1b2f Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 22:41:09 +0000 Subject: [PATCH 036/146] [docs] [refactor] Add help messages for datadir path mangling --- src/bitcoin-cli.cpp | 2 +- src/init.cpp | 8 ++++---- src/rpc/protocol.cpp | 4 +--- src/util.cpp | 23 +++++++++-------------- src/util.h | 10 ++++++++++ 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index db9dcde1b..851142ccd 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -30,7 +30,7 @@ std::string HelpMessageCli() std::string strUsage; strUsage += HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("This help message")); - strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); + strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); strUsage += HelpMessageOpt("-datadir=", _("Specify data directory")); AppendParamsHelpMessages(strUsage); strUsage += HelpMessageOpt("-rpcconnect=", strprintf(_("Send commands to node running on (default: %s)"), DEFAULT_RPCCONNECT)); diff --git a/src/init.cpp b/src/init.cpp index 3d1a15a8e..89118783c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -332,7 +332,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); strUsage += HelpMessageOpt("-checkblocks=", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); strUsage += HelpMessageOpt("-checklevel=", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); - strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); + strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); if (mode == HMM_BITCOIND) { #ifndef WIN32 @@ -343,12 +343,12 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-exportdir=", _("Specify directory to be used when exporting data")); strUsage += HelpMessageOpt("-dbcache=", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); strUsage += HelpMessageOpt("-loadblock=", _("Imports blocks from external blk000??.dat file on startup")); - strUsage += HelpMessageOpt("-debuglogfile=", strprintf(_("Specify location of debug log file: this can be an absolute path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE)); + strUsage += HelpMessageOpt("-debuglogfile=", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE)); strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-par=", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); #ifndef WIN32 - strUsage += HelpMessageOpt("-pid=", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME)); + strUsage += HelpMessageOpt("-pid=", strprintf(_("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), BITCOIN_PID_FILENAME)); #endif strUsage += HelpMessageOpt("-prune=", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " @@ -487,7 +487,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE)); strUsage += HelpMessageOpt("-rpcbind=", _("Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces)")); - strUsage += HelpMessageOpt("-rpccookiefile=", _("Location of the auth cookie (default: data dir)")); + strUsage += HelpMessageOpt("-rpccookiefile=", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)")); strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcauth=", _("Username and hashed password for JSON-RPC connections. The field comes in the format: :$. A canonical python script is included in share/rpcuser. This option can be specified multiple times")); diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index 74e3410ad..bc5b5822a 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -75,9 +75,7 @@ static fs::path GetAuthCookieFile(bool temp=false) if (temp) { arg += ".tmp"; } - fs::path path(arg); - if (!path.is_absolute()) path = GetDataDir() / path; - return path; + return AbsPathForConfigVal(fs::path(arg)); } bool GenerateAuthCookie(std::string *cookie_out) diff --git a/src/util.cpp b/src/util.cpp index d4b9c3da4..041b3817d 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -8,9 +8,9 @@ #endif #include "util.h" +#include "fs.h" #include "chainparamsbase.h" -#include "fs.h" #include "random.h" #include "serialize.h" #include "sync.h" @@ -156,11 +156,7 @@ static void DebugPrintInit() fs::path GetDebugLogPath() { fs::path logfile(GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); - if (logfile.is_absolute()) { - return logfile; - } else { - return GetDataDir() / logfile; - } + return AbsPathForConfigVal(logfile); } bool OpenDebugLog() @@ -542,11 +538,7 @@ void ClearDatadirCache() fs::path GetConfigFile(const std::string& confPath) { - fs::path pathConfigFile(confPath); - if (!pathConfigFile.is_absolute()) - pathConfigFile = GetDataDir(false) / pathConfigFile; - - return pathConfigFile; + return AbsPathForConfigVal(fs::path(confPath), false); } void ReadConfigFile(const std::string& confPath, @@ -577,9 +569,7 @@ void ReadConfigFile(const std::string& confPath, #ifndef WIN32 fs::path GetPidFile() { - fs::path pathPidFile(GetArg("-pid", BITCOIN_PID_FILENAME)); - if (!pathPidFile.is_absolute()) pathPidFile = GetDataDir() / pathPidFile; - return pathPidFile; + return AbsPathForConfigVal(fs::path(GetArg("-pid", BITCOIN_PID_FILENAME))); } void CreatePidFile(const fs::path &path, pid_t pid) @@ -810,6 +800,11 @@ bool SetupNetworking() return true; } +fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) +{ + return fs::absolute(path, GetDataDir(net_specific)); +} + void SetThreadPriority(int nPriority) { #ifdef WIN32 diff --git a/src/util.h b/src/util.h index b9d504977..683c2e02f 100644 --- a/src/util.h +++ b/src/util.h @@ -147,6 +147,16 @@ std::string PrivacyInfo(); /** Returns licensing information (for -version) */ std::string LicenseInfo(); +/** + * Most paths passed as configuration arguments are treated as relative to + * the datadir if they are not absolute. + * + * @param path The path to be conditionally prefixed with datadir. + * @param net_specific Forwarded to GetDataDir(). + * @return The normalized path. + */ +fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific = true); + inline bool IsSwitchChar(char c) { #ifdef WIN32 From ac98d743e5bce7a27f6660a8e5a9cc87c145f345 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 22:49:28 +0000 Subject: [PATCH 037/146] metrics: Add a progress bar when in Initial Block Download mode --- src/metrics.cpp | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/metrics.cpp b/src/metrics.cpp index 6013f65ef..a37de4cb6 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -15,6 +15,7 @@ #include "utilmoneystr.h" #include "utilstrencodings.h" +#include #include #include #include @@ -267,7 +268,7 @@ std::string DisplaySize(size_t value) return strprintf(_("%.2f TiB"), value / coef); } -int printStats(bool mining) +int printStats(bool isScreen, bool mining) { // Number of lines that are always displayed int lines = 5; @@ -292,13 +293,38 @@ int printStats(bool mining) int downloadPercent = nSizeReindexed * 100 / nFullSizeToReindex; std::cout << " " << _("Reindexing blocks") << " | " << DisplaySize(nSizeReindexed) << " / " << DisplaySize(nFullSizeToReindex) << " (" << downloadPercent << "%, " << height << " " << _("blocks") << ")" << std::endl; } else { + int nHeaders = currentHeadersHeight; + if (nHeaders < 0) + nHeaders = 0; int netheight = currentHeadersHeight == -1 || currentHeadersTime == 0 ? - 0 : EstimateNetHeight(params, currentHeadersHeight, currentHeadersTime); - int downloadPercent = 0; - if (netheight > 0) { - downloadPercent = height * 100 / netheight; + 0 : EstimateNetHeight(params, currentHeadersHeight, currentHeadersTime); + if (netheight < nHeaders) + netheight = nHeaders; + if (netheight <= 0) + netheight = 1; + int downloadPercent = height * 100 / netheight; + std::cout << " " << _("Downloading blocks") << " | " << height << " (" << nHeaders << " " << _("headers") << ") / ~" << netheight << " (" << downloadPercent << "%)" << std::endl; + + if (isScreen) { + // Draw 50-character progress bar, which will fit into a 79-character line. + int blockChars = downloadPercent / 2; + int headerChars = (nHeaders * 50) / netheight; + // Start with background colour reversed for "full" bar. + std::cout << " | [␛[7m"; + for (auto i : boost::irange(0, 50)) { + if (i == headerChars) { + // Switch to normal background colour for "empty" bar. + std::cout << "␛[0m"; + } else if (i == blockChars) { + // Switch to distinct colour for "headers" bar. + std::cout << "␛[0;43m"; + } + std::cout << " "; + } + // Ensure that colour is reset after the progress bar is printed. + std::cout << "␛[0m]" << std::endl; + lines++; } - std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%)" << std::endl; } } else { std::cout << " " << _("Block height") << " | " << height << std::endl; @@ -353,7 +379,7 @@ int printMiningStatus(bool mining) } } lines++; - } else { + } else if (Params().NetworkIDString() != "main") { std::cout << _("You are currently not mining.") << std::endl; std::cout << _("To enable mining, add 'gen=1' to your bitcoinz.conf and restart.") << std::endl; lines += 2; @@ -579,7 +605,7 @@ void ThreadShowMetricsScreen() #endif if (loaded) { - lines += printStats(mining); + lines += printStats(isScreen, mining); lines += printMiningStatus(mining); } lines += printMetrics(cols, mining); From 6a39c44386f353cf5582f4a1eca47a9fcfb2a99c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 22:50:45 +0000 Subject: [PATCH 038/146] Remove workaround affecting old Boost version --- src/util.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/util.cpp b/src/util.cpp index 041b3817d..23c48c68e 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -70,18 +70,6 @@ #include #include -// Work around clang compilation problem in Boost 1.46: -// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup -// See also: http://stackoverflow.com/questions/10020179/compilation-fail-in-boost-librairies-program-options -// http://clang.debian.net/status.php?version=3.0&key=CANNOT_FIND_FUNCTION -namespace boost { - - namespace program_options { - std::string to_internal(const std::string&); - } - -} // namespace boost - using namespace std; const char * const BITCOIN_CONF_FILENAME = "bitcoinz.conf"; From c18e105e5b144f3ce6488e2a11f614d1577bfeda Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 22:52:54 +0000 Subject: [PATCH 039/146] Update minimum chain work on mainnet to reflect Sapling activation --- src/chainparams.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 92c9d72d6..4da7183c1 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -115,7 +115,7 @@ class CMainParams : public CChainParams { Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; // The best chain should have at least this much work. - // consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000a95cc5099213e3"); + consensus.nMinimumChainWork = uint256S("0x000000aa11b9ee1025e94db2877ea0e238eea492ffb33b67df8ba5766711db76"); /** * The message start string should be awesome! ⓩ❤ From d1d6371ff618a4b6cdbd05f4aa383a10a352ca47 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 22:53:56 +0000 Subject: [PATCH 040/146] rpc: Add more version information to getinfo --- src/rpc/misc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index fb0b86852..cc70f8b61 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -84,6 +84,8 @@ UniValue getinfo(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); obj.pushKV("version", CLIENT_VERSION); + obj.pushKV("build", FormatFullVersion()); + obj.pushKV("subversion", strSubVersion); obj.pushKV("protocolversion", PROTOCOL_VERSION); #ifdef ENABLE_WALLET if (pwalletMain) { From 4c4adbdad493a3d81843e2d2000a14107802f0dd Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 22:55:54 +0000 Subject: [PATCH 041/146] [Feature] Include transaction hex in verbose getblock output --- src/rpc/rawtransaction.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 29e4b3111..c8e867a97 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -162,6 +162,9 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) if (tx.fOverwintered) { entry.pushKV("expiryheight", (int64_t)tx.nExpiryHeight); } + + entry.pushKV("hex", EncodeHexTx(tx)); + UniValue vin(UniValue::VARR); for (const CTxIn& txin : tx.vin) { UniValue in(UniValue::VOBJ); From 52d5a69bdc22752c051e470369e76f0c384e8b2d Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 23:00:24 +0000 Subject: [PATCH 042/146] rpc: Add transaction size to JSON output --- src/rpc/rawtransaction.cpp | 3 +++ src/test/rpc_tests.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index c8e867a97..023a75df9 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -153,6 +153,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { const uint256 txid = tx.GetHash(); entry.pushKV("txid", txid.GetHex()); + entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); entry.pushKV("overwintered", tx.fOverwintered); entry.pushKV("version", tx.nVersion); if (tx.fOverwintered) { @@ -276,6 +277,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) "{\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" + " \"size\" : n, (numeric) The transaction size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"expiryheight\" : ttt, (numeric, optional) The block height after which the transaction expires\n" @@ -630,6 +632,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"txid\" : \"id\", (string) The transaction id\n" + " \"size\" : n, (numeric) The transaction size\n" " \"overwintered\" : bool (boolean) The Overwintered flag\n" " \"version\" : n, (numeric) The version\n" " \"versiongroupid\": \"hex\" (string, optional) The version group id (Overwintered txs)\n" diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 8e555883e..951edd52d 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -96,6 +96,7 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), runtime_error); string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000"; BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx)); + BOOST_CHECK_EQUAL(r.get_obj().find_value("size").getInt(), 193); BOOST_CHECK_EQUAL(r.get_obj().find_value("version").getInt(), 1); BOOST_CHECK_EQUAL(r.get_obj().find_value("locktime").getInt(), 0); BOOST_CHECK_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx+" extra"), runtime_error); From 233301d12edeee2b3c7c6ab9ccdd6ee1e8867065 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 23:02:24 +0000 Subject: [PATCH 043/146] Add solution rates aproximation to metrics --- src/metrics.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/metrics.cpp b/src/metrics.cpp index a37de4cb6..c09fd95c5 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -277,6 +277,7 @@ int printStats(bool isScreen, bool mining) int64_t currentHeadersTime; size_t connections; int64_t netsolps; + double netsolpsLog = 1.0; const Consensus::Params& params = Params().GetConsensus(); { LOCK2(cs_main, cs_vNodes); @@ -285,8 +286,12 @@ int printStats(bool isScreen, bool mining) currentHeadersTime = pindexBestHeader ? pindexBestHeader->nTime : 0; connections = vNodes.size(); netsolps = GetNetworkHashPS(120, -1); + netsolpsLog = std::log2(netsolps); } auto localsolps = GetLocalSolPS(); + double localsolpsLog = 0.0; + if (localsolps > 0) + localsolpsLog = std::log2(localsolps); if (IsInitialBlockDownload(Params())) { if (fReindex) { @@ -343,9 +348,9 @@ int printStats(bool isScreen, bool mining) } std::cout << " " << _("Next upgrade") << " | " << strUpgradeTime << std::endl; std::cout << " " << _("Connections") << " | " << connections << std::endl; - std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl; + std::cout << " " << _("Network solution rate") << " | " << strprintf("~ 2^%.4f Sol/s", netsolpsLog) << std::endl; if (mining && miningTimer.running()) { - std::cout << " " << _("Local solution rate") << " | " << strprintf("%.4f Sol/s", localsolps) << std::endl; + std::cout << " " << _("Local solution rate") << " | " << strprintf("~ 2^%.4f Sol/s", localsolpsLog) << std::endl; lines++; } std::cout << std::endl; From eac84d7debc33736d61f02d9859c83e7bcad1e90 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 23:07:30 +0000 Subject: [PATCH 044/146] Add joinSplitPubKey and joinSplitSig to RPC --- qa/rpc-tests/wallet.py | 10 +++++++++- src/rpc/rawtransaction.cpp | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index dee944935..7abd1fbde 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -282,6 +282,8 @@ def run_test (self): mytxdetails = self.nodes[2].gettransaction(mytxid) myvJoinSplits = mytxdetails["vJoinSplit"] assert_equal(0, len(myvJoinSplits)) + assert("joinSplitPubKey" not in mytxdetails) + assert("joinSplitSig" not in mytxdetails) # z_sendmany is expected to fail if tx size breaks limit myzaddr = self.nodes[0].z_getnewaddress('sprout') @@ -349,13 +351,19 @@ def run_test (self): assert_greater_than(len(myvJoinSplits), 0) # the first (probably only) joinsplit should take in all the public value - myjoinsplit = self.nodes[2].getrawtransaction(mytxid, 1)["vJoinSplit"][0] + mytxdetails = self.nodes[2].getrawtransaction(mytxid, 1) + myjoinsplit = mytxdetails["vjoinsplit"][0] assert_equal(myjoinsplit["vpub_old"], zsendmanynotevalue) assert_equal(myjoinsplit["vpub_new"], 0) assert("onetimePubKey" in myjoinsplit.keys()) assert("randomSeed" in myjoinsplit.keys()) assert("ciphertexts" in myjoinsplit.keys()) + assert(len(mytxdetails["joinSplitPubKey"]) == 64) + int(mytxdetails["joinSplitPubKey"], 16) # throws if not a hex string + assert(len(mytxdetails["joinSplitSig"]) == 128) + int(mytxdetails["joinSplitSig"], 16) # hex string + # send from private note to node 0 and node 2 node0balance = self.nodes[0].getbalance() # 25.99794745 node2balance = self.nodes[2].getbalance() # 16.99790000 diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 023a75df9..64c2c1e47 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -236,6 +236,11 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) } } + if (tx.nVersion >= 2 && tx.vJoinSplit.size() > 0) { + entry.pushKV("joinSplitPubKey", tx.joinSplitPubKey.GetHex()); + entry.pushKV("joinSplitSig", HexStr(tx.joinSplitSig.begin(), tx.joinSplitSig.end())); + } + if (!hashBlock.IsNull()) { entry.pushKV("blockhash", hashBlock.GetHex()); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); From e33edad9c185e80f6378e79be4d1b89d928f38f3 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 23:24:52 +0000 Subject: [PATCH 045/146] Fix some benign races --- src/addrman.h | 53 +++++++++++++++++++++------------------------------ src/main.cpp | 2 +- src/main.h | 2 +- src/net.cpp | 9 +++++++-- src/net.h | 2 +- 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/addrman.h b/src/addrman.h index 2fd80e9f4..4acf02f9f 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -470,6 +470,7 @@ class CAddrMan //! Return the number of (unique) addresses in all tables. size_t size() const { + LOCK(cs); // TODO: Cache this in an atomic to avoid this overhead return vRandom.size(); } @@ -489,13 +490,11 @@ class CAddrMan //! Add a single address. bool Add(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty = 0) { + LOCK(cs); bool fRet = false; - { - LOCK(cs); - Check(); - fRet |= Add_(addr, source, nTimePenalty); - Check(); - } + Check(); + fRet |= Add_(addr, source, nTimePenalty); + Check(); if (fRet) LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew); return fRet; @@ -504,14 +503,12 @@ class CAddrMan //! Add multiple addresses. bool Add(const std::vector &vAddr, const CNetAddr& source, int64_t nTimePenalty = 0) { + LOCK(cs); int nAdd = 0; - { - LOCK(cs); - Check(); - for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) - nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; - Check(); - } + Check(); + for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) + nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; + Check(); if (nAdd) LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew); return nAdd > 0; @@ -520,23 +517,19 @@ class CAddrMan //! Mark an entry as accessible. void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Good_(addr, nTime); - Check(); - } + LOCK(cs); + Check(); + Good_(addr, nTime); + Check(); } //! Mark an entry as connection attempted to. void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Attempt_(addr, nTime); - Check(); - } + LOCK(cs); + Check(); + Attempt_(addr, nTime); + Check(); } /** @@ -570,12 +563,10 @@ class CAddrMan //! Mark an entry as currently-connected-to. void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) { - { - LOCK(cs); - Check(); - Connected_(addr, nTime); - Check(); - } + LOCK(cs); + Check(); + Connected_(addr, nTime); + Check(); } }; diff --git a/src/main.cpp b/src/main.cpp index 11ed08f5c..7b0f78654 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,7 +66,7 @@ static int64_t nTimeBestReceived = 0; CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; -bool fImporting = false; +std::atomic_bool fImporting(false); std::atomic_bool fReindex(false); bool fTxIndex = false; bool fAddressIndex = false; // insightexplorer || lightwalletd diff --git a/src/main.h b/src/main.h index 837729af2..d252172dd 100644 --- a/src/main.h +++ b/src/main.h @@ -141,7 +141,7 @@ extern uint64_t nLastBlockSize; extern const std::string strMessageMagic; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; -extern bool fImporting; +extern std::atomic_bool fImporting; extern std::atomic_bool fReindex; extern int nScriptCheckThreads; extern bool fTxIndex; diff --git a/src/net.cpp b/src/net.cpp index bb3e02f94..6e8ae6abf 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1021,8 +1021,13 @@ void ThreadSocketHandler() } } } - if(vNodes.size() != nPrevNodeCount) { - nPrevNodeCount = vNodes.size(); + size_t vNodesSize; + { + LOCK(cs_vNodes); + vNodesSize = vNodes.size(); + } + if (vNodesSize != nPrevNodeCount) { + nPrevNodeCount = vNodesSize; uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } diff --git a/src/net.h b/src/net.h index 2be65891a..148463caf 100644 --- a/src/net.h +++ b/src/net.h @@ -281,7 +281,7 @@ class CNode bool fInbound; bool fNetworkNode; bool fSuccessfullyConnected; - bool fDisconnect; + std::atomic_bool fDisconnect; // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version message // b) the peer may tell us in its version message that we should not relay tx invs From 6ac021cf0c4ce4238beb5e87e9b7ecb9cf0368a9 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 26 Aug 2024 23:42:49 +0000 Subject: [PATCH 046/146] Use SipHash-2-4 for various non-cryptographic hashes --- src/coins.cpp | 8 +- src/coins.h | 30 +++--- src/hash.cpp | 94 +++++++++++++++++++ src/hash.h | 15 +++ src/main.cpp | 20 ++-- src/test/hash_tests.cpp | 20 ++++ src/transaction_builder.cpp | 2 +- src/txmempool.cpp | 4 +- src/txmempool.h | 3 +- src/uint256.cpp | 64 ------------- src/uint256.h | 18 +++- .../asyncrpcoperation_mergetoaddress.cpp | 4 +- src/wallet/asyncrpcoperation_sendmany.cpp | 4 +- 13 files changed, 185 insertions(+), 101 deletions(-) diff --git a/src/coins.cpp b/src/coins.cpp index a95a69cad..3a807e0ee 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -80,7 +80,11 @@ bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, CNullifiersMap &mapSaplingNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, mapSproutAnchors, mapSaplingAnchors, mapSproutNullifiers, mapSaplingNullifiers); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); } -CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} +SaltedTxidHasher::SaltedTxidHasher() +{ + GetRandBytes((unsigned char*)&k0, sizeof(k0)); + GetRandBytes((unsigned char*)&k1, sizeof(k1)); +} CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { } @@ -564,7 +568,7 @@ CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const bool CCoinsViewCache::HaveShieldedRequirements(const CTransaction& tx) const { - std::unordered_map intermediates; + std::unordered_map intermediates; for (const JSDescription &joinsplit : tx.vJoinSplit) { diff --git a/src/coins.h b/src/coins.h index 0a8e8294e..bff7affbb 100644 --- a/src/coins.h +++ b/src/coins.h @@ -8,6 +8,7 @@ #include "compressor.h" #include "core_memusage.h" +#include "hash.h" #include "memusage.h" #include "serialize.h" #include "uint256.h" @@ -18,7 +19,7 @@ #include "zcash/IncrementalMerkleTree.hpp" -/** +/** * Pruned version of CTransaction: only retains metadata and unspent transaction outputs * * Serialized format: @@ -241,21 +242,22 @@ class CCoins } }; -class CCoinsKeyHasher +class SaltedTxidHasher { private: - uint256 salt; + /** Salt */ + uint64_t k0, k1; public: - CCoinsKeyHasher(); + SaltedTxidHasher(); /** * This *must* return size_t. With Boost 1.46 on 32-bit systems the * unordered_map will behave unpredictably if the custom hasher returns a * uint64_t, resulting in failures when syncing the chain (#4634). */ - size_t operator()(const uint256& key) const { - return key.GetHash(salt); + size_t operator()(const uint256& txid) const { + return SipHashUint256(k0, k1, txid); } }; @@ -316,10 +318,10 @@ enum ShieldedType SAPLING, }; -typedef std::unordered_map CCoinsMap; -typedef std::unordered_map CAnchorsSproutMap; -typedef std::unordered_map CAnchorsSaplingMap; -typedef std::unordered_map CNullifiersMap; +typedef std::unordered_map CCoinsMap; +typedef std::unordered_map CAnchorsSproutMap; +typedef std::unordered_map CAnchorsSaplingMap; +typedef std::unordered_map CNullifiersMap; struct CCoinsStats { @@ -410,10 +412,10 @@ class CCoinsViewBacked : public CCoinsView class CCoinsViewCache; -/** +/** * A reference to a mutable cache entry. Encapsulating it allows us to run * cleanup code after the modification is finished, and keeping track of - * concurrent modifications. + * concurrent modifications. */ class CCoinsModifier { @@ -440,7 +442,7 @@ class CCoinsViewCache : public CCoinsViewBacked /** * Make mutable so that we can "fill the cache" even from Get-methods - * declared as "const". + * declared as "const". */ mutable uint256 hashBlock; mutable CCoinsMap cacheCoins; @@ -525,7 +527,7 @@ class CCoinsViewCache : public CCoinsViewBacked //! Calculate the size of the cache (in bytes) size_t DynamicMemoryUsage() const; - /** + /** * Amount of bitcoins coming in to a transaction * Note that lightweight clients may not know anything besides the hash of previous transactions, * so may not be able to calculate this. diff --git a/src/hash.cpp b/src/hash.cpp index 2070e0918..e2b9f6069 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -81,3 +81,97 @@ void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char he num[3] = (nChild >> 0) & 0xFF; CHMAC_SHA512(chainCode.begin(), chainCode.size()).Write(&header, 1).Write(data, 32).Write(num, 4).Finalize(output); } + +#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define SIPROUND do { \ + v0 += v1; v1 = ROTL(v1, 13); v1 ^= v0; \ + v0 = ROTL(v0, 32); \ + v2 += v3; v3 = ROTL(v3, 16); v3 ^= v2; \ + v0 += v3; v3 = ROTL(v3, 21); v3 ^= v0; \ + v2 += v1; v1 = ROTL(v1, 17); v1 ^= v2; \ + v2 = ROTL(v2, 32); \ +} while (0) + +CSipHasher::CSipHasher(uint64_t k0, uint64_t k1) +{ + v[0] = 0x736f6d6570736575ULL ^ k0; + v[1] = 0x646f72616e646f6dULL ^ k1; + v[2] = 0x6c7967656e657261ULL ^ k0; + v[3] = 0x7465646279746573ULL ^ k1; + count = 0; +} + +CSipHasher& CSipHasher::Write(uint64_t data) +{ + uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; + + v3 ^= data; + SIPROUND; + SIPROUND; + v0 ^= data; + + v[0] = v0; + v[1] = v1; + v[2] = v2; + v[3] = v3; + + count++; + return *this; +} + +uint64_t CSipHasher::Finalize() const +{ + uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; + + v3 ^= ((uint64_t)count) << 59; + SIPROUND; + SIPROUND; + v0 ^= ((uint64_t)count) << 59; + v2 ^= 0xFF; + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + return v0 ^ v1 ^ v2 ^ v3; +} + +uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val) +{ + /* Specialized implementation for efficiency */ + uint64_t d = val.GetUint64(0); + + uint64_t v0 = 0x736f6d6570736575ULL ^ k0; + uint64_t v1 = 0x646f72616e646f6dULL ^ k1; + uint64_t v2 = 0x6c7967656e657261ULL ^ k0; + uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ d; + + SIPROUND; + SIPROUND; + v0 ^= d; + d = val.GetUint64(1); + v3 ^= d; + SIPROUND; + SIPROUND; + v0 ^= d; + d = val.GetUint64(2); + v3 ^= d; + SIPROUND; + SIPROUND; + v0 ^= d; + d = val.GetUint64(3); + v3 ^= d; + SIPROUND; + SIPROUND; + v0 ^= d; + v3 ^= ((uint64_t)4) << 59; + SIPROUND; + SIPROUND; + v0 ^= ((uint64_t)4) << 59; + v2 ^= 0xFF; + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + return v0 ^ v1 ^ v2 ^ v3; +} diff --git a/src/hash.h b/src/hash.h index fe1607277..fee750719 100644 --- a/src/hash.h +++ b/src/hash.h @@ -219,4 +219,19 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector mapMix; + multimap mapMix; FastRandomContext insecure_rand; + const CSipHasher hasher = CSipHasher(salt0, salt1).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); for (CNode* pnode : vNodes) { if (pnode->nVersion < CADDR_TIME_VERSION) continue; - unsigned int nPointer; - memcpy(&nPointer, &pnode, sizeof(nPointer)); - uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer); - hashKey = Hash(BEGIN(hashKey), END(hashKey)); + uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize(); mapMix.insert(make_pair(hashKey, pnode)); } int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) - for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) + for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) ((*mi).second)->PushAddress(addr, insecure_rand); } } diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp index 356fb245b..c5bd952f1 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -47,4 +47,24 @@ BOOST_AUTO_TEST_CASE(murmurhash3) #undef T } +BOOST_AUTO_TEST_CASE(siphash) +{ + CSipHasher hasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x726fdb47dd0e0e31ull); + hasher.Write(0x0706050403020100ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x93f5f5799a932462ull); + hasher.Write(0x0F0E0D0C0B0A0908ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x3f2acc7f57c29bdbull); + hasher.Write(0x1716151413121110ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0xb8ad50c6f649af94ull); + hasher.Write(0x1F1E1D1C1B1A1918ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x7127512f72f27cceull); + hasher.Write(0x2726252423222120ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x0e3ea96b5304a7d0ull); + hasher.Write(0x2F2E2D2C2B2A2928ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull); + + BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256S("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")), 0x7127512f72f27cceull); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index d3a75c2de..2421d068a 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -496,7 +496,7 @@ void TransactionBuilder::CreateJSDescriptions() CAmount vpubNewTarget = valueOut > 0 ? valueOut : 0; // Keep track of treestate within this transaction - std::unordered_map intermediates; + std::unordered_map intermediates; std::vector previousCommitments; while (!vpubNewProcessed) { diff --git a/src/txmempool.cpp b/src/txmempool.cpp index ae741a29a..1c53e9a46 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -530,7 +530,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const i++; } - std::unordered_map intermediates; + // The SaltedTxidHasher is fine to use here; it salts the map keys automatically + // with randomness generated on construction. + std::unordered_map intermediates; for (const JSDescription &joinsplit : tx.vJoinSplit) { for (const uint256 &nf : joinsplit.nullifiers) { diff --git a/src/txmempool.h b/src/txmempool.h index 02e36495a..dfa248c2a 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -20,6 +20,7 @@ #undef foreach #include "boost/multi_index_container.hpp" #include "boost/multi_index/ordered_index.hpp" +#include "boost/multi_index/hashed_index.hpp" class CAutoFile; @@ -151,7 +152,7 @@ class CTxMemPool CTxMemPoolEntry, boost::multi_index::indexed_by< // sorted by txid - boost::multi_index::ordered_unique, + boost::multi_index::hashed_unique, // sorted by fee rate boost::multi_index::ordered_non_unique< boost::multi_index::identity, diff --git a/src/uint256.cpp b/src/uint256.cpp index b16b0d904..5b5e2d1f9 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -80,67 +80,3 @@ template std::string base_blob<256>::GetHex() const; template std::string base_blob<256>::ToString() const; template void base_blob<256>::SetHex(const char*); template void base_blob<256>::SetHex(const std::string&); - -static void inline HashMix(uint32_t& a, uint32_t& b, uint32_t& c) -{ - // Taken from lookup3, by Bob Jenkins. - a -= c; - a ^= ((c << 4) | (c >> 28)); - c += b; - b -= a; - b ^= ((a << 6) | (a >> 26)); - a += c; - c -= b; - c ^= ((b << 8) | (b >> 24)); - b += a; - a -= c; - a ^= ((c << 16) | (c >> 16)); - c += b; - b -= a; - b ^= ((a << 19) | (a >> 13)); - a += c; - c -= b; - c ^= ((b << 4) | (b >> 28)); - b += a; -} - -static void inline HashFinal(uint32_t& a, uint32_t& b, uint32_t& c) -{ - // Taken from lookup3, by Bob Jenkins. - c ^= b; - c -= ((b << 14) | (b >> 18)); - a ^= c; - a -= ((c << 11) | (c >> 21)); - b ^= a; - b -= ((a << 25) | (a >> 7)); - c ^= b; - c -= ((b << 16) | (b >> 16)); - a ^= c; - a -= ((c << 4) | (c >> 28)); - b ^= a; - b -= ((a << 14) | (a >> 18)); - c ^= b; - c -= ((b << 24) | (b >> 8)); -} - -uint64_t uint256::GetHash(const uint256& salt) const -{ - uint32_t a, b, c; - const uint32_t *pn = (const uint32_t*)data; - const uint32_t *salt_pn = (const uint32_t*)salt.data; - a = b = c = 0xdeadbeef + WIDTH; - - a += pn[0] ^ salt_pn[0]; - b += pn[1] ^ salt_pn[1]; - c += pn[2] ^ salt_pn[2]; - HashMix(a, b, c); - a += pn[3] ^ salt_pn[3]; - b += pn[4] ^ salt_pn[4]; - c += pn[5] ^ salt_pn[5]; - HashMix(a, b, c); - a += pn[6] ^ salt_pn[6]; - b += pn[7] ^ salt_pn[7]; - HashFinal(a, b, c); - - return ((((uint64_t)b) << 32) | c); -} diff --git a/src/uint256.h b/src/uint256.h index b07047b04..a693f4567 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -75,6 +75,19 @@ class base_blob return sizeof(data); } + uint64_t GetUint64(int pos) const + { + const uint8_t* ptr = data + pos * 8; + return ((uint64_t)ptr[0]) | \ + ((uint64_t)ptr[1]) << 8 | \ + ((uint64_t)ptr[2]) << 16 | \ + ((uint64_t)ptr[3]) << 24 | \ + ((uint64_t)ptr[4]) << 32 | \ + ((uint64_t)ptr[5]) << 40 | \ + ((uint64_t)ptr[6]) << 48 | \ + ((uint64_t)ptr[7]) << 56; + } + template void Serialize(Stream& s) const { @@ -131,11 +144,6 @@ class uint256 : public base_blob<256> { memcpy((void*)&result, (void*)data, 8); return result; } - - /** A more secure, salted hash function. - * @note This hash is not stable between little and big endian. - */ - uint64_t GetHash(const uint256& salt) const; }; /* uint256 from const char *. diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index 019276abd..d6b9f37d3 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -481,7 +481,9 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } // Keep track of treestate within this transaction - std::unordered_map intermediates; + // The SaltedTxidHasher is fine to use here; it salts the map keys automatically + // with randomness generated on construction. + std::unordered_map intermediates; std::vector previousCommitments; while (!vpubNewProcessed) { diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 410b7d768..b46afa87e 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -638,7 +638,9 @@ bool AsyncRPCOperation_sendmany::main_impl() { } // Keep track of treestate within this transaction - std::unordered_map intermediates; + // The SaltedTxidHasher is fine to use here; it salts the map keys automatically + // with randomness generated on construction. + std::unordered_map intermediates; std::vector previousCommitments; while (!vpubNewProcessed) { From 1083d4866f9b48da70a9b1dad921aa4cb4ca78c0 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 00:19:40 +0000 Subject: [PATCH 047/146] Use SipHash for node eviction --- src/coins.cpp | 6 +- src/coins.h | 2 +- src/hash.cpp | 37 +++++++++- src/hash.h | 21 +++++- src/main.cpp | 7 +- src/net.cpp | 153 +++++++++++++++++----------------------- src/net.h | 8 ++- src/test/hash_tests.cpp | 64 +++++++++++++++-- 8 files changed, 187 insertions(+), 111 deletions(-) diff --git a/src/coins.cpp b/src/coins.cpp index 3a807e0ee..158499a59 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -80,11 +80,7 @@ bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, CNullifiersMap &mapSaplingNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, mapSproutAnchors, mapSaplingAnchors, mapSproutNullifiers, mapSaplingNullifiers); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); } -SaltedTxidHasher::SaltedTxidHasher() -{ - GetRandBytes((unsigned char*)&k0, sizeof(k0)); - GetRandBytes((unsigned char*)&k1, sizeof(k1)); -} +SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits::max())), k1(GetRand(std::numeric_limits::max())) {} CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { } diff --git a/src/coins.h b/src/coins.h index bff7affbb..4714796b1 100644 --- a/src/coins.h +++ b/src/coins.h @@ -246,7 +246,7 @@ class SaltedTxidHasher { private: /** Salt */ - uint64_t k0, k1; + const uint64_t k0, k1; public: SaltedTxidHasher(); diff --git a/src/hash.cpp b/src/hash.cpp index e2b9f6069..1ce7370bb 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -100,6 +100,7 @@ CSipHasher::CSipHasher(uint64_t k0, uint64_t k1) v[2] = 0x6c7967656e657261ULL ^ k0; v[3] = 0x7465646279746573ULL ^ k1; count = 0; + tmp = 0; } CSipHasher& CSipHasher::Write(uint64_t data) @@ -116,7 +117,35 @@ CSipHasher& CSipHasher::Write(uint64_t data) v[2] = v2; v[3] = v3; - count++; + count += 8; + return *this; +} + +CSipHasher& CSipHasher::Write(const unsigned char* data, size_t size) +{ + uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; + uint64_t t = tmp; + int c = count; + + while (size--) { + t |= ((uint64_t)(*(data++))) << (8 * (c % 8)); + c++; + if ((c & 7) == 0) { + v3 ^= t; + SIPROUND; + SIPROUND; + v0 ^= t; + t = 0; + } + } + + v[0] = v0; + v[1] = v1; + v[2] = v2; + v[3] = v3; + count = c; + tmp = t; + return *this; } @@ -124,10 +153,12 @@ uint64_t CSipHasher::Finalize() const { uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; - v3 ^= ((uint64_t)count) << 59; + uint64_t t = tmp | (((uint64_t)count) << 56); + + v3 ^= t; SIPROUND; SIPROUND; - v0 ^= ((uint64_t)count) << 59; + v0 ^= t; v2 ^= 0xFF; SIPROUND; SIPROUND; diff --git a/src/hash.h b/src/hash.h index fee750719..7e401d842 100644 --- a/src/hash.h +++ b/src/hash.h @@ -219,19 +219,38 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector::max()); + static const uint64_t salt1 = GetRand(std::numeric_limits::max()); uint64_t hashAddr = addr.GetHash(); multimap mapMix; FastRandomContext insecure_rand; diff --git a/src/net.cpp b/src/net.cpp index 6e8ae6abf..ed6eae76b 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -14,6 +14,10 @@ #include "addrman.h" #include "chainparams.h" #include "clientversion.h" +#include "consensus/consensus.h" +#include "crypto/common.h" +#include "crypto/sha256.h" +#include "hash.h" #include "compat.h" #include "primitives/transaction.h" #include "scheduler.h" @@ -699,87 +703,32 @@ void SocketSendData(CNode *pnode) static list vNodesDisconnected; -class CNodeRef { -public: - CNodeRef(CNode *pnode) : _pnode(pnode) { - LOCK(cs_vNodes); - _pnode->AddRef(); - } - - ~CNodeRef() { - LOCK(cs_vNodes); - _pnode->Release(); - } - - CNode& operator *() const {return *_pnode;}; - CNode* operator ->() const {return _pnode;}; - - CNodeRef& operator =(const CNodeRef& other) - { - if (this != &other) { - LOCK(cs_vNodes); - - _pnode->Release(); - _pnode = other._pnode; - _pnode->AddRef(); - } - return *this; - } - - CNodeRef(const CNodeRef& other): - _pnode(other._pnode) - { - LOCK(cs_vNodes); - _pnode->AddRef(); - } -private: - CNode *_pnode; +struct NodeEvictionCandidate +{ + NodeId id; + int64_t nTimeConnected; + int64_t nMinPingUsecTime; + CAddress addr; + uint64_t nKeyedNetGroup; + int nVersion; }; -static bool ReverseCompareNodeMinPingTime(const CNodeRef &a, const CNodeRef &b) +static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { - return a->nMinPingUsecTime > b->nMinPingUsecTime; + return a.nMinPingUsecTime > b.nMinPingUsecTime; } -static bool ReverseCompareNodeTimeConnected(const CNodeRef &a, const CNodeRef &b) +static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { - return a->nTimeConnected > b->nTimeConnected; + return a.nTimeConnected > b.nTimeConnected; } -class CompareNetGroupKeyed -{ - std::vector vchSecretKey; -public: - CompareNetGroupKeyed() - { - vchSecretKey.resize(32, 0); - GetRandBytes(vchSecretKey.data(), vchSecretKey.size()); - } - - bool operator()(const CNodeRef &a, const CNodeRef &b) - { - std::vector vchGroupA, vchGroupB; - CSHA256 hashA, hashB; - std::vector vchA(32), vchB(32); - - vchGroupA = a->addr.GetGroup(); - vchGroupB = b->addr.GetGroup(); - - hashA.Write(begin_ptr(vchGroupA), vchGroupA.size()); - hashB.Write(begin_ptr(vchGroupB), vchGroupB.size()); - - hashA.Write(begin_ptr(vchSecretKey), vchSecretKey.size()); - hashB.Write(begin_ptr(vchSecretKey), vchSecretKey.size()); - - hashA.Finalize(begin_ptr(vchA)); - hashB.Finalize(begin_ptr(vchB)); - - return vchA < vchB; - } -}; +static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { + return a.nKeyedNetGroup < b.nKeyedNetGroup; +} static bool AttemptToEvictConnection(bool fPreferNewConnection) { - std::vector vEvictionCandidates; + std::vector vEvictionCandidates; { LOCK(cs_vNodes); @@ -790,7 +739,15 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { continue; if (node->fDisconnect) continue; - vEvictionCandidates.push_back(CNodeRef(node)); + NodeEvictionCandidate candidate = { + .id = node->id, + .nTimeConnected = node->nTimeConnected, + .nMinPingUsecTime = node->nMinPingUsecTime, + .addr = node->addr, + .nKeyedNetGroup = node->nKeyedNetGroup, + .nVersion = node->nVersion + }; + vEvictionCandidates.push_back(candidate); } } @@ -799,7 +756,7 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { // Protect connections with certain characteristics // Check version of eviction candidates and prioritize nodes which do not support network upgrade. - std::vector vTmpEvictionCandidates; + std::vector vTmpEvictionCandidates; int height; { LOCK(cs_main); @@ -817,8 +774,8 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { height >= nActivationHeight - NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD) { // Find any nodes which don't support the protocol version for the next upgrade - for (const CNodeRef &node : vEvictionCandidates) { - if (node->nVersion < params.vUpgrades[idx].nProtocolVersion) { + for (const NodeEvictionCandidate &node : vEvictionCandidates) { + if (node.nVersion < params.vUpgrades[idx].nProtocolVersion) { vTmpEvictionCandidates.push_back(node); } } @@ -831,9 +788,8 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { } // Deterministically select 4 peers to protect by netgroup. - // An attacker cannot predict which netgroups will be protected. - static CompareNetGroupKeyed comparerNetGroupKeyed; - std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), comparerNetGroupKeyed); + // An attacker cannot predict which netgroups will be protected + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed); vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast(vEvictionCandidates.size())), vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; @@ -854,24 +810,24 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { // Identify the network group with the most connections and youngest member. // (vEvictionCandidates is already sorted by reverse connect time) - std::vector naMostConnections; + uint64_t naMostConnections; unsigned int nMostConnections = 0; int64_t nMostConnectionsTime = 0; - std::map, std::vector > mapAddrCounts; - for (const CNodeRef &node : vEvictionCandidates) { - mapAddrCounts[node->addr.GetGroup()].push_back(node); - int64_t grouptime = mapAddrCounts[node->addr.GetGroup()][0]->nTimeConnected; - size_t groupsize = mapAddrCounts[node->addr.GetGroup()].size(); + std::map > mapAddrCounts; + for(const NodeEvictionCandidate &node : vEvictionCandidates) { + mapAddrCounts[node.nKeyedNetGroup].push_back(node); + int64_t grouptime = mapAddrCounts[node.nKeyedNetGroup][0].nTimeConnected; + size_t groupsize = mapAddrCounts[node.nKeyedNetGroup].size(); if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { nMostConnections = groupsize; nMostConnectionsTime = grouptime; - naMostConnections = node->addr.GetGroup(); + naMostConnections = node.nKeyedNetGroup; } } // Reduce to the network group with the most connections - vEvictionCandidates = mapAddrCounts[naMostConnections]; + vEvictionCandidates = std::move(mapAddrCounts[naMostConnections]); // Do not disconnect peers if there is only one unprotected connection from their network group. if (vEvictionCandidates.size() <= 1) @@ -880,9 +836,15 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { return false; // Disconnect from the network group with the most connections - vEvictionCandidates[0]->fDisconnect = true; - - return true; + NodeId evicted = vEvictionCandidates.front().id; + LOCK(cs_vNodes); + for(std::vector::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) { + if ((*it)->GetId() == evicted) { + (*it)->fDisconnect = true; + return true; + } + } + return false; } static void AcceptConnection(const ListenSocket& hListenSocket) { @@ -2050,6 +2012,8 @@ unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAX CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), + addr(addrIn), + nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)), addrKnown(5000, 0.001), setInventoryKnown(SendBufferSize() / 1000) { @@ -2062,7 +2026,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nRecvBytes = 0; nTimeConnected = GetTime(); nTimeOffset = 0; - addr = addrIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; nVersion = 0; strSubVer = ""; @@ -2208,3 +2171,13 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) LEAVE_CRITICAL_SECTION(cs_vSend); } + +uint64_t CNode::CalculateKeyedNetGroup(const CAddress& ad) +{ + static const uint64_t k0 = GetRand(std::numeric_limits::max()); + static const uint64_t k1 = GetRand(std::numeric_limits::max()); + + std::vector vchNetGroup(ad.GetGroup()); + + return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize(); +} diff --git a/src/net.h b/src/net.h index 148463caf..e68ff78f2 100644 --- a/src/net.h +++ b/src/net.h @@ -9,7 +9,6 @@ #include "bloom.h" #include "compat.h" #include "fs.h" -#include "hash.h" #include "limitedmap.h" #include "mruset.h" #include "netbase.h" @@ -266,7 +265,7 @@ class CNode int64_t nLastRecv; int64_t nTimeConnected; int64_t nTimeOffset; - CAddress addr; + const CAddress addr; std::string addrName; CService addrLocal; int nVersion; @@ -293,6 +292,9 @@ class CNode CBloomFilter* pfilter; int nRefCount; NodeId id; + + const uint64_t nKeyedNetGroup; + protected: // Denial-of-service detection/prevention @@ -350,6 +352,8 @@ class CNode CNode(const CNode&); void operator=(const CNode&); + static uint64_t CalculateKeyedNetGroup(const CAddress& ad); + public: NodeId GetId() const { diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp index c5bd952f1..8ed358786 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -47,17 +47,57 @@ BOOST_AUTO_TEST_CASE(murmurhash3) #undef T } +/* + SipHash-2-4 output with + k = 00 01 02 ... + and + in = (empty string) + in = 00 (1 byte) + in = 00 01 (2 bytes) + in = 00 01 02 (3 bytes) + ... + in = 00 01 02 ... 3e (63 bytes) + from: https://131002.net/siphash/siphash24.c +*/ +uint64_t siphash_4_2_testvec[] = { + 0x726fdb47dd0e0e31, 0x74f839c593dc67fd, 0x0d6c8009d9a94f5a, 0x85676696d7fb7e2d, + 0xcf2794e0277187b7, 0x18765564cd99a68d, 0xcbc9466e58fee3ce, 0xab0200f58b01d137, + 0x93f5f5799a932462, 0x9e0082df0ba9e4b0, 0x7a5dbbc594ddb9f3, 0xf4b32f46226bada7, + 0x751e8fbc860ee5fb, 0x14ea5627c0843d90, 0xf723ca908e7af2ee, 0xa129ca6149be45e5, + 0x3f2acc7f57c29bdb, 0x699ae9f52cbe4794, 0x4bc1b3f0968dd39c, 0xbb6dc91da77961bd, + 0xbed65cf21aa2ee98, 0xd0f2cbb02e3b67c7, 0x93536795e3a33e88, 0xa80c038ccd5ccec8, + 0xb8ad50c6f649af94, 0xbce192de8a85b8ea, 0x17d835b85bbb15f3, 0x2f2e6163076bcfad, + 0xde4daaaca71dc9a5, 0xa6a2506687956571, 0xad87a3535c49ef28, 0x32d892fad841c342, + 0x7127512f72f27cce, 0xa7f32346f95978e3, 0x12e0b01abb051238, 0x15e034d40fa197ae, + 0x314dffbe0815a3b4, 0x027990f029623981, 0xcadcd4e59ef40c4d, 0x9abfd8766a33735c, + 0x0e3ea96b5304a7d0, 0xad0c42d6fc585992, 0x187306c89bc215a9, 0xd4a60abcf3792b95, + 0xf935451de4f21df2, 0xa9538f0419755787, 0xdb9acddff56ca510, 0xd06c98cd5c0975eb, + 0xe612a3cb9ecba951, 0xc766e62cfcadaf96, 0xee64435a9752fe72, 0xa192d576b245165a, + 0x0a8787bf8ecb74b2, 0x81b3e73d20b49b6f, 0x7fa8220ba3b2ecea, 0x245731c13ca42499, + 0xb78dbfaf3a8d83bd, 0xea1ad565322a1a0b, 0x60e61c23a3795013, 0x6606d7e446282b93, + 0x6ca4ecb15c5f91e1, 0x9f626da15c9625f3, 0xe51b38608ef25f57, 0x958a324ceb064572 +}; + BOOST_AUTO_TEST_CASE(siphash) { CSipHasher hasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x726fdb47dd0e0e31ull); - hasher.Write(0x0706050403020100ULL); + static const unsigned char t0[1] = {0}; + hasher.Write(t0, 1); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x74f839c593dc67fdull); + static const unsigned char t1[7] = {1,2,3,4,5,6,7}; + hasher.Write(t1, 7); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x93f5f5799a932462ull); hasher.Write(0x0F0E0D0C0B0A0908ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x3f2acc7f57c29bdbull); - hasher.Write(0x1716151413121110ULL); - BOOST_CHECK_EQUAL(hasher.Finalize(), 0xb8ad50c6f649af94ull); - hasher.Write(0x1F1E1D1C1B1A1918ULL); + static const unsigned char t2[2] = {16,17}; + hasher.Write(t2, 2); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x4bc1b3f0968dd39cull); + static const unsigned char t3[9] = {18,19,20,21,22,23,24,25,26}; + hasher.Write(t3, 9); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x2f2e6163076bcfadull); + static const unsigned char t4[5] = {27,28,29,30,31}; + hasher.Write(t4, 5); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x7127512f72f27cceull); hasher.Write(0x2726252423222120ULL); BOOST_CHECK_EQUAL(hasher.Finalize(), 0x0e3ea96b5304a7d0ull); @@ -65,6 +105,22 @@ BOOST_AUTO_TEST_CASE(siphash) BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull); BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256S("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")), 0x7127512f72f27cceull); + + // Check test vectors from spec, one byte at a time + CSipHasher hasher2(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); + for (uint8_t x=0; x Date: Tue, 27 Aug 2024 00:26:26 +0000 Subject: [PATCH 048/146] Fix prevector bugs --- src/prevector.h | 19 +++------- src/test/prevector_tests.cpp | 70 +++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/prevector.h b/src/prevector.h index 7f34bcee9..d1d30ed44 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -302,9 +302,8 @@ class prevector { } void resize(size_type new_size) { - while (size() > new_size) { - item_ptr(size() - 1)->~T(); - _size--; + if (size() > new_size) { + erase(item_ptr(new_size), end()); } if (new_size > capacity()) { change_capacity(new_size); @@ -372,10 +371,7 @@ class prevector { } iterator erase(iterator pos) { - (*pos).~T(); - memmove(&(*pos), &(*pos) + 1, ((char*)&(*end())) - ((char*)(1 + &(*pos)))); - _size--; - return pos; + return erase(pos, pos + 1); } iterator erase(iterator first, iterator last) { @@ -400,7 +396,7 @@ class prevector { } void pop_back() { - _size--; + erase(end() - 1, end()); } T& front() { @@ -420,12 +416,7 @@ class prevector { } void swap(prevector& other) { - if (_size & other._size & 1) { - std::swap(_union.capacity, other._union.capacity); - std::swap(_union.indirect, other._union.indirect); - } else { - std::swap(_union, other._union); - } + std::swap(_union, other._union); std::swap(_size, other._size); } diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index c255b0992..b367ee176 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -20,63 +20,75 @@ template class prevector_tester { typedef std::vector realtype; realtype real_vector; + realtype real_vector_alt; typedef prevector pretype; pretype pre_vector; + pretype pre_vector_alt; typedef typename pretype::size_type Size; + bool passed = true; FastRandomContext rand_cache; + template + void local_check_equal(A a, B b) + { + local_check(a == b); + } + void local_check(bool b) + { + passed &= b; + } void test() { const pretype& const_pre_vector = pre_vector; - BOOST_CHECK_EQUAL(real_vector.size(), pre_vector.size()); - BOOST_CHECK_EQUAL(real_vector.empty(), pre_vector.empty()); + local_check_equal(real_vector.size(), pre_vector.size()); + local_check_equal(real_vector.empty(), pre_vector.empty()); for (Size s = 0; s < real_vector.size(); s++) { - BOOST_CHECK(real_vector[s] == pre_vector[s]); - BOOST_CHECK(&(pre_vector[s]) == &(pre_vector.begin()[s])); - BOOST_CHECK(&(pre_vector[s]) == &*(pre_vector.begin() + s)); - BOOST_CHECK(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size())); + local_check(real_vector[s] == pre_vector[s]); + local_check(&(pre_vector[s]) == &(pre_vector.begin()[s])); + local_check(&(pre_vector[s]) == &*(pre_vector.begin() + s)); + local_check(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size())); } - // BOOST_CHECK(realtype(pre_vector) == real_vector); - BOOST_CHECK(pretype(real_vector.begin(), real_vector.end()) == pre_vector); - BOOST_CHECK(pretype(pre_vector.begin(), pre_vector.end()) == pre_vector); + // local_check(realtype(pre_vector) == real_vector); + local_check(pretype(real_vector.begin(), real_vector.end()) == pre_vector); + local_check(pretype(pre_vector.begin(), pre_vector.end()) == pre_vector); size_t pos = 0; for (const T& v : pre_vector) { - BOOST_CHECK(v == real_vector[pos++]); + local_check(v == real_vector[pos++]); } for (const T& v : reverse_iterate(pre_vector)) { - BOOST_CHECK(v == real_vector[--pos]); + local_check(v == real_vector[--pos]); } for (const T& v : const_pre_vector) { - BOOST_CHECK(v == real_vector[pos++]); + local_check(v == real_vector[pos++]); } for (const T& v : reverse_iterate(const_pre_vector)) { - BOOST_CHECK(v == real_vector[--pos]); + local_check(v == real_vector[--pos]); } CDataStream ss1(SER_DISK, 0); CDataStream ss2(SER_DISK, 0); ss1 << real_vector; ss2 << pre_vector; - BOOST_CHECK_EQUAL(ss1.size(), ss2.size()); + local_check_equal(ss1.size(), ss2.size()); for (Size s = 0; s < ss1.size(); s++) { - BOOST_CHECK_EQUAL(ss1[s], ss2[s]); + local_check_equal(ss1[s], ss2[s]); } } public: void resize(Size s) { real_vector.resize(s); - BOOST_CHECK_EQUAL(real_vector.size(), s); + local_check_equal(real_vector.size(), s); pre_vector.resize(s); - BOOST_CHECK_EQUAL(pre_vector.size(), s); + local_check_equal(pre_vector.size(), s); test(); } void reserve(Size s) { real_vector.reserve(s); - BOOST_CHECK(real_vector.capacity() >= s); + local_check(real_vector.capacity() >= s); pre_vector.reserve(s); - BOOST_CHECK(pre_vector.capacity() >= s); + local_check(pre_vector.capacity() >= s); test(); } @@ -152,6 +164,19 @@ class prevector_tester { test(); } + void swap() { + real_vector.swap(real_vector_alt); + pre_vector.swap(pre_vector_alt); + test(); + } + + ~prevector_tester() { + BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: " + << rand_cache.Rz + << ", insecure_rand_Rw: " + << rand_cache.Rw); + } + prevector_tester() { seed_insecure_rand(); rand_cache = insecure_rand_ctx; @@ -211,12 +236,15 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) if (test.size() > 0) { test.update(insecure_rand() % test.size(), insecure_rand()); } - if (((r >> 11) & 1024) == 11) { + if (((r >> 11) % 1024) == 11) { test.clear(); } - if (((r >> 21) & 512) == 12) { + if (((r >> 21) % 512) == 12) { test.assign(insecure_rand() % 32, insecure_rand()); } + if (((r >> 15) % 64) == 3) { + test.swap(); + } } } } From 6564424844334dfcce002e9a4788a8329ca9148d Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 00:41:50 +0000 Subject: [PATCH 049/146] Skip tx messages during initial block download --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 079610e79..ececb9d39 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5542,9 +5542,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); - if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK) - pfrom->AskFor(inv); - if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { @@ -5568,6 +5565,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } + } else { + if (!fAlreadyHave && !IsInitialBlockDownload(chainparams)) + pfrom->AskFor(inv); } // Track requests for our stuff @@ -5691,7 +5691,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "tx") + else if (strCommand == "tx" && !IsInitialBlockDownload(chainparams)) { vector vWorkQueue; vector vEraseQueue; @@ -6171,7 +6171,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // message would be undesirable as we transmit it ourselves. } - else { + else if (!(strCommand == "tx" || strCommand == "block" || strCommand == "headers" || strCommand == "alert")) { // Ignore unknown commands for extensibility LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } From bc6c96bdaa18f1b839ad21816d532c8e358b818e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 00:59:17 +0000 Subject: [PATCH 050/146] Introduce -maxuploadtarget --- doc/reduce-traffic.md | 38 ++++ doc/release-notes.md | 22 +++ qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/maxuploadtarget.py | 304 ++++++++++++++++++++++++++++++++ src/init.cpp | 6 + src/main.cpp | 16 ++ src/net.cpp | 91 ++++++++++ src/net.h | 29 +++ src/rpc/net.cpp | 26 ++- 9 files changed, 532 insertions(+), 1 deletion(-) create mode 100644 doc/reduce-traffic.md create mode 100755 qa/rpc-tests/maxuploadtarget.py diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md new file mode 100644 index 000000000..379fcad3f --- /dev/null +++ b/doc/reduce-traffic.md @@ -0,0 +1,38 @@ +Reduce Traffic +============== + +Some node operators need to deal with bandwidth caps imposed by their ISPs. + +By default, bitcoin-core allows up to 125 connections to different peers, 8 of +which are outbound. You can therefore, have at most 117 inbound connections. + +The default settings can result in relatively significant traffic consumption. + +Ways to reduce traffic: + +## 1. Use `-maxuploadtarget=` + +A major component of the traffic is caused by serving historic blocks to other nodes +during the initial blocks download phase (syncing up a new node). +This option can be specified in MiB per day and is turned off by default. +This is *not* a hard limit; only a threshold to minimize the outbound +traffic. When the limit is about to be reached, the uploaded data is cut by no +longer serving historic blocks (blocks older than one week). +Keep in mind that new nodes require other nodes that are willing to serve +historic blocks. **The recommended minimum is 1152 blocks per day (max. 2304MB +per day)** + +Whitelisted peers will never be disconnected, although their traffic counts for +calculating the target. + +## 2. Disable "listening" (`-listen=0`) + +Disabling listening will result in fewer nodes connected (remember the maximum of 8 +outbound peers). Fewer nodes will result in less traffic usage as you are relaying +blocks and transactions to fewer nodes. + +## 3. Reduce maximum connections (`-maxconnections=`) + +Reducing the maximum connected nodes to a minimum could be desirable if traffic +limits are tiny. Keep in mind that bitcoin's trustless model works best if you are +connected to a handful of nodes. diff --git a/doc/release-notes.md b/doc/release-notes.md index 5561d8fd7..5a8809021 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,6 +1,28 @@ Notable changes =============== +Reduce upload traffic +--------------------- + +A major part of the outbound traffic is caused by serving historic blocks to +other nodes in initial block download state. + +It is now possible to reduce the total upload traffic via the `-maxuploadtarget` +parameter. This is *not* a hard limit but a threshold to minimize the outbound +traffic. When the limit is about to be reached, the uploaded data is cut by not +serving historic blocks (blocks older than one week). +Moreover, any SPV peer is disconnected when they request a filtered block. + +This option can be specified in MiB per day and is turned off by default +(`-maxuploadtarget=0`). +The recommended minimum is 1152 * MAX_BLOCK_SIZE (currently 2304MB) per day. + +Whitelisted peers will never be disconnected, although their traffic counts for +calculating the target. + +A more detailed documentation about keeping traffic low can be found in +[/doc/reducetraffic.md](/doc/reducetraffic.md). + Fixes ----- diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 101c5063c..e689be9c1 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -95,6 +95,7 @@ testScriptsExt=( 'invalidblockrequest.py' # 'forknotify.py' 'p2p-acceptblock.py' + 'maxuploadtarget.py', 'wallet_db_flush.py' ); diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py new file mode 100755 index 000000000..23a41d17b --- /dev/null +++ b/qa/rpc-tests/maxuploadtarget.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from test_framework.mininode import ( \ + NodeConn, NodeConnCB, CInv, NetworkThread, \ + msg_ping, msg_pong, msg_getdata, \ + BLOSSOM_PROTO_VERSION + ) + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( \ + assert_equal, initialize_chain_clean, \ + start_node, stop_node, \ + p2p_port, \ + ) +from test_framework.comptool import wait_until +from decimal import Decimal +import os +import time + +''' +Test behavior of -maxuploadtarget. +* Verify that getdata requests for old blocks (>1week) are dropped +if uploadtarget has been reached. +* Verify that getdata requests for recent blocks are respected even +if uploadtarget has been reached. +* Verify that the upload counters are reset after 24 hours. +''' + +# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending +# p2p messages to a node, generating the messages in the main testing logic. +class TestNode(NodeConnCB): + def __init__(self): + NodeConnCB.__init__(self) + self.create_callback_map() + self.connection = None + self.ping_counter = 1 + self.last_pong = msg_pong() + self.block_receive_map = {} + + def add_connection(self, conn): + self.connection = conn + self.peer_disconnected = False + + def on_inv(self, conn, message): + pass + + # Track the last getdata message we receive (used in the test) + def on_getdata(self, conn, message): + self.last_getdata = message + + def on_block(self, conn, message): + message.block.calc_sha256() + try: + self.block_receive_map[message.block.sha256] += 1 + except KeyError: + self.block_receive_map[message.block.sha256] = 1 + + # Spin until verack message is received from the node. + # We use this to signal that our test can begin. This + # is called from the testing thread, so it needs to acquire + # the global lock. + def wait_for_verack(self): + def veracked(): + return self.verack_received + return wait_until(veracked, timeout=10) + + def wait_for_disconnect(self): + def disconnected(): + return self.peer_disconnected + return wait_until(disconnected, timeout=10) + + # Wrapper for the NodeConn's send_message function + def send_message(self, message): + self.connection.send_message(message) + + def on_pong(self, conn, message): + self.last_pong = message + + def on_close(self, conn): + self.peer_disconnected = True + + # Sync up with the node after delivery of a block + def sync_with_ping(self, timeout=30): + def received_pong(): + return (self.last_pong.nonce == self.ping_counter) + self.connection.send_message(msg_ping(nonce=self.ping_counter)) + success = wait_until(received_pong, timeout) + self.ping_counter += 1 + return success + +class MaxUploadTest(BitcoinTestFramework): + def __init__(self): + self.utxo = [] + + # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create + # So we have big transactions and full blocks to fill up our block files + # create one script_pubkey + script_pubkey = "6a4d0200" #OP_RETURN OP_PUSH2 512 bytes + for _ in range(512): + script_pubkey = script_pubkey + "01" + # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change + self.txouts = "81" + for _ in range(128): + # add txout value + self.txouts = self.txouts + "0000000000000000" + # add length of script_pubkey + self.txouts = self.txouts + "fd0402" + # add script_pubkey + self.txouts = self.txouts + script_pubkey + + def add_options(self, parser): + parser.add_option("--testbinary", dest="testbinary", + default=os.getenv("BITCOIND", "zcashd"), + help="zcashd binary to test") + + def setup_chain(self): + initialize_chain_clean(self.options.tmpdir, 2) + + def setup_network(self): + # Start a node with maxuploadtarget of 200 MB (/24h) + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, [ + "-debug", + '-nuparams=2bb40e60:1', # Blossom + "-maxuploadtarget=2200"])) + + def mine_full_block(self, node, address): + # Want to create a full block + # We'll generate a 66k transaction below, and 28 of them is close to the 2MB block limit + for _ in range(28): + if len(self.utxo) < 28: + self.utxo = node.listunspent() + inputs=[] + outputs = {} + t = self.utxo.pop() + inputs.append({ "txid" : t["txid"], "vout" : t["vout"]}) + remchange = t["amount"] - Decimal("0.001000") + outputs[address]=remchange + # Create a basic transaction that will send change back to ourself after account for a fee + # and then insert the 128 generated transaction outs in the middle. rawtx[100] is where the + # number of txouts is stored and is the only thing we overwrite from the original transaction + rawtx = node.createrawtransaction(inputs, outputs) + newtx = rawtx[0:100] + newtx = newtx + self.txouts + newtx = newtx + rawtx[102:] + # Appears to be ever so slightly faster to sign with SIGHASH_NONE + signresult = node.signrawtransaction(newtx,None,None,"NONE") + node.sendrawtransaction(signresult["hex"], True) + # Mine a full sized block which will be these transactions we just created + node.generate(1) + + def run_test(self): + # Before we connect anything, we first set the time on the node + # to be in the past, otherwise things break because the CNode + # time counters can't be reset backward after initialization + old_time = int(time.time() - 60*60*24*9) + self.nodes[0].setmocktime(old_time) + + # Generate some old blocks + self.nodes[0].generate(260) + + # test_nodes[0] will only request old blocks + # test_nodes[1] will only request new blocks + # test_nodes[2] will test resetting the counters + test_nodes = [] + connections = [] + + for i in range(3): + test_nodes.append(TestNode()) + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i], protocol_version=BLOSSOM_PROTO_VERSION)) + test_nodes[i].add_connection(connections[i]) + + NetworkThread().start() # Start up network handling in another thread + [x.wait_for_verack() for x in test_nodes] + + # Test logic begins here + + # Now mine a big block + self.mine_full_block(self.nodes[0], self.nodes[0].getnewaddress()) + + # Store the hash; we'll request this later + big_old_block = self.nodes[0].getbestblockhash() + old_block_size = self.nodes[0].getblock(big_old_block, True)['size'] + big_old_block = int(big_old_block, 16) + + # Advance to two days ago + self.nodes[0].setmocktime(int(time.time()) - 2*60*60*24) + + # Generate interim blocks. Due to the "max MTP" soft-forked rule, block timestamps + # can be no more than 1.5 hours ahead of the chain tip's MTP. Thus we need to mine + # enough blocks to advance the MTP forward to the desired mocked time. + self.nodes[0].generate(1000) + + # Mine one more block, so that the prior block looks old + self.mine_full_block(self.nodes[0], self.nodes[0].getnewaddress()) + + # We'll be requesting this new block too + big_new_block = self.nodes[0].getbestblockhash() + big_new_block = int(big_new_block, 16) + + # test_nodes[0] will test what happens if we just keep requesting the + # the same big old block too many times (expect: disconnect) + + getdata_request = msg_getdata() + getdata_request.inv.append(CInv(2, big_old_block)) + + max_bytes_per_day = 2200*1024*1024 + daily_buffer = 1152 * 2000000 + max_bytes_available = max_bytes_per_day - daily_buffer + success_count = max_bytes_available / old_block_size + + # 2304GB will be reserved for relaying new blocks, so expect this to + # succeed for ~14 tries. + for i in range(int(success_count)): + test_nodes[0].send_message(getdata_request) + test_nodes[0].sync_with_ping() + assert_equal(test_nodes[0].block_receive_map[big_old_block], i+1) + + assert_equal(len(self.nodes[0].getpeerinfo()), 3) + # At most a couple more tries should succeed (depending on how long + # the test has been running so far). + for i in range(3): + test_nodes[0].send_message(getdata_request) + test_nodes[0].wait_for_disconnect() + assert_equal(len(self.nodes[0].getpeerinfo()), 2) + print("Peer 0 disconnected after downloading old block too many times") + + # Requesting the current block on test_nodes[1] should succeed indefinitely, + # even when over the max upload target. + # We'll try 200 times + getdata_request.inv = [CInv(2, big_new_block)] + for i in range(200): + test_nodes[1].send_message(getdata_request) + test_nodes[1].sync_with_ping() + assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1) + + print("Peer 1 able to repeatedly download new block") + + # But if test_nodes[1] tries for an old block, it gets disconnected too. + getdata_request.inv = [CInv(2, big_old_block)] + test_nodes[1].send_message(getdata_request) + test_nodes[1].wait_for_disconnect() + assert_equal(len(self.nodes[0].getpeerinfo()), 1) + + print("Peer 1 disconnected after trying to download old block") + + print("Advancing system time on node to clear counters...") + + # If we advance the time by 24 hours, then the counters should reset, + # and test_nodes[2] should be able to retrieve the old block. + self.nodes[0].setmocktime(int(time.time())) + test_nodes[2].sync_with_ping() + test_nodes[2].send_message(getdata_request) + test_nodes[2].sync_with_ping() + assert_equal(test_nodes[2].block_receive_map[big_old_block], 1) + + print("Peer 2 able to download old block") + + [c.disconnect_node() for c in connections] + + #stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1 + print("Restarting nodes with -whitelist=127.0.0.1") + stop_node(self.nodes[0], 0) + self.nodes[0] = start_node(0, self.options.tmpdir, [ + "-debug", + '-nuparams=2bb40e60:1', # Blossom + "-whitelist=127.0.0.1", + "-maxuploadtarget=1", + ]) + + #recreate/reconnect 3 test nodes + test_nodes = [] + connections = [] + + for i in range(3): + test_nodes.append(TestNode()) + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_nodes[i], protocol_version=BLOSSOM_PROTO_VERSION)) + test_nodes[i].add_connection(connections[i]) + + NetworkThread().start() # Start up network handling in another thread + [x.wait_for_verack() for x in test_nodes] + + #retrieve 20 blocks which should be enough to break the 1MB limit + getdata_request.inv = [CInv(2, big_new_block)] + for i in range(20): + test_nodes[1].send_message(getdata_request) + test_nodes[1].sync_with_ping() + assert_equal(test_nodes[1].block_receive_map[big_new_block], i+1) + + getdata_request.inv = [CInv(2, big_old_block)] + test_nodes[1].send_message(getdata_request) + test_nodes[1].wait_for_disconnect() + assert_equal(len(self.nodes[0].getpeerinfo()), 3) #node is still connected because of the whitelist + + print("Peer 1 still connected after trying to download old block (whitelisted)") + + [c.disconnect_node() for c in connections] + +if __name__ == '__main__': + MaxUploadTest().main() diff --git a/src/init.cpp b/src/init.cpp index 89118783c..074cd7210 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -396,6 +396,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-whitebind=", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6")); strUsage += HelpMessageOpt("-whitelist=", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") + " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); + strUsage += HelpMessageOpt("-maxuploadtarget=", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); #ifdef ENABLE_WALLET strUsage += CWallet::GetWalletHelpString(showDebug); @@ -1404,6 +1405,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) RegisterValidationInterface(pzmqNotificationInterface); } #endif + if (mapArgs.count("-maxuploadtarget")) { + CNode::SetMaxOutboundTarget( + chainparams.GetConsensus().nPostBlossomPowTargetSpacing, + GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024); + } // ********************************************************* Step 7: load block chain diff --git a/src/main.cpp b/src/main.cpp index ececb9d39..ed2c263de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5165,6 +5165,22 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } } + // disconnect node in case we have reached the outbound limit for serving historical blocks + // never disconnect whitelisted nodes + static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical + if (send && CNode::OutboundTargetReached(consensusParams.PoWTargetSpacing(currentHeight), true) && ( + ( + (pindexBestHeader != NULL) && + (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek) + ) || inv.type == MSG_FILTERED_BLOCK + ) && !pfrom->fWhitelisted) + { + LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); + + //disconnect node + pfrom->fDisconnect = true; + send = false; + } // Pruned nodes may have deleted the block, so check whether // it's available before trying to send. if (send && (mi->second->nStatus & BLOCK_HAVE_DATA)) diff --git a/src/net.cpp b/src/net.cpp index ed6eae76b..597544fee 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -308,6 +308,11 @@ uint64_t CNode::nTotalBytesSent = 0; CCriticalSection CNode::cs_totalBytesRecv; CCriticalSection CNode::cs_totalBytesSent; +uint64_t CNode::nMaxOutboundLimit = 0; +uint64_t CNode::nMaxOutboundTotalBytesSentInCycle = 0; +uint64_t CNode::nMaxOutboundTimeframe = 60*60*24; //1 day +uint64_t CNode::nMaxOutboundCycleStartTime = 0; + CNode* FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); @@ -1857,6 +1862,92 @@ void CNode::RecordBytesSent(uint64_t bytes) { LOCK(cs_totalBytesSent); nTotalBytesSent += bytes; + + uint64_t now = GetTime(); + if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now) + { + // timeframe expired, reset cycle + nMaxOutboundCycleStartTime = now; + nMaxOutboundTotalBytesSentInCycle = 0; + } + + // TODO, exclude whitebind peers + nMaxOutboundTotalBytesSentInCycle += bytes; +} + +void CNode::SetMaxOutboundTarget(uint64_t targetSpacing, uint64_t limit) +{ + LOCK(cs_totalBytesSent); + uint64_t recommendedMinimum = (nMaxOutboundTimeframe / targetSpacing) * MAX_BLOCK_SIZE; + nMaxOutboundLimit = limit; + + if (limit > 0 && limit < recommendedMinimum) + LogPrintf("Max outbound target is very small (%s bytes) and will be overshot. Recommended minimum is %s bytes.\n", nMaxOutboundLimit, recommendedMinimum); +} + +uint64_t CNode::GetMaxOutboundTarget() +{ + LOCK(cs_totalBytesSent); + return nMaxOutboundLimit; +} + +uint64_t CNode::GetMaxOutboundTimeframe() +{ + LOCK(cs_totalBytesSent); + return nMaxOutboundTimeframe; +} + +uint64_t CNode::GetMaxOutboundTimeLeftInCycle() +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) + return 0; + + if (nMaxOutboundCycleStartTime == 0) + return nMaxOutboundTimeframe; + + uint64_t cycleEndTime = nMaxOutboundCycleStartTime + nMaxOutboundTimeframe; + uint64_t now = GetTime(); + return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); +} + +void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundTimeframe != timeframe) + { + // reset measure-cycle in case of changing + // the timeframe + nMaxOutboundCycleStartTime = GetTime(); + } + nMaxOutboundTimeframe = timeframe; +} + +bool CNode::OutboundTargetReached(uint64_t targetSpacing, bool historicalBlockServingLimit) +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) { + return false; + } + + if (historicalBlockServingLimit) + { + // keep a large enough buffer to at least relay each block once + uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); + uint64_t buffer = timeLeftInCycle / targetSpacing * MAX_BLOCK_SIZE; + return (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer); + } else { + return nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit; + } +} + +uint64_t CNode::GetOutboundTargetBytesLeft() +{ + LOCK(cs_totalBytesSent); + if (nMaxOutboundLimit == 0) + return 0; + + return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; } uint64_t CNode::GetTotalBytesRecv() diff --git a/src/net.h b/src/net.h index e68ff78f2..30ec7cb72 100644 --- a/src/net.h +++ b/src/net.h @@ -57,6 +57,8 @@ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ; static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ; /** The maximum number of peer connections to maintain. */ static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; +/** The default for -maxuploadtarget. 0 = Unlimited */ +static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0; /** The period before a network upgrade activates, where connections to upgrading peers are preferred (in blocks). */ static const int NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD = 24 * 24 * 3; @@ -349,6 +351,12 @@ class CNode static uint64_t nTotalBytesRecv; static uint64_t nTotalBytesSent; + // outbound limit & stats + static uint64_t nMaxOutboundTotalBytesSentInCycle; + static uint64_t nMaxOutboundCycleStartTime; + static uint64_t nMaxOutboundLimit; + static uint64_t nMaxOutboundTimeframe; + CNode(const CNode&); void operator=(const CNode&); @@ -644,6 +652,27 @@ class CNode static uint64_t GetTotalBytesRecv(); static uint64_t GetTotalBytesSent(); + + //!set the max outbound target in bytes + static void SetMaxOutboundTarget(uint64_t targetSpacing, uint64_t limit); + static uint64_t GetMaxOutboundTarget(); + + //!set the timeframe for the max outbound target + static void SetMaxOutboundTimeframe(uint64_t timeframe); + static uint64_t GetMaxOutboundTimeframe(); + + //!check if the outbound target is reached + // if param historicalBlockServingLimit is set true, the function will + // response true if the limit for serving historical blocks has been reached + static bool OutboundTargetReached(uint64_t targetSpacing, bool historicalBlockServingLimit); + + //!response the bytes left in the current max outbound cycle + // in case of no limit, it will always response 0 + static uint64_t GetOutboundTargetBytesLeft(); + + //!response the time in seconds left in the current max outbound cycle + // in case of no limit, it will always respond with 0 + static uint64_t GetMaxOutboundTimeLeftInCycle(); }; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 59c32f6fd..212803bb7 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -362,17 +362,41 @@ UniValue getnettotals(const UniValue& params, bool fHelp) "{\n" " \"totalbytesrecv\": n, (numeric) Total bytes received\n" " \"totalbytessent\": n, (numeric) Total bytes sent\n" - " \"timemillis\": t (numeric) Total cpu time\n" + " \"timemillis\": t, (numeric) Total cpu time\n" + " \"uploadtarget\":\n" + " {\n" + " \"timeframe\": n, (numeric) Length of the measuring timeframe in seconds\n" + " \"target\": n, (numeric) Target in bytes\n" + " \"target_reached\": true|false, (boolean) True if target is reached\n" + " \"serve_historical_blocks\": true|false, (boolean) True if serving historical blocks\n" + " \"bytes_left_in_cycle\": t, (numeric) Bytes left in current time cycle\n" + " \"time_left_in_cycle\": t (numeric) Seconds left in current time cycle\n" + " }\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnettotals", "") + HelpExampleRpc("getnettotals", "") ); + uint64_t targetSpacing; + { + LOCK(cs_main); + targetSpacing = Params().GetConsensus().PoWTargetSpacing(chainActive.Height()); + } + UniValue obj(UniValue::VOBJ); obj.pushKV("totalbytesrecv", CNode::GetTotalBytesRecv()); obj.pushKV("totalbytessent", CNode::GetTotalBytesSent()); obj.pushKV("timemillis", GetTimeMillis()); + + UniValue outboundLimit(UniValue::VOBJ); + outboundLimit.pushKV("timeframe", CNode::GetMaxOutboundTimeframe()); + outboundLimit.pushKV("target", CNode::GetMaxOutboundTarget()); + outboundLimit.pushKV("target_reached", CNode::OutboundTargetReached(targetSpacing, false)); + outboundLimit.pushKV("serve_historical_blocks", !CNode::OutboundTargetReached(targetSpacing, true)); + outboundLimit.pushKV("bytes_left_in_cycle", CNode::GetOutboundTargetBytesLeft()); + outboundLimit.pushKV("time_left_in_cycle", CNode::GetMaxOutboundTimeLeftInCycle()); + obj.pushKV("uploadtarget", outboundLimit); return obj; } From c41903739248fcb4717efcb051e130e269e8f125 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 01:10:01 +0000 Subject: [PATCH 051/146] Add -blocksonly option --- doc/reduce-traffic.md | 17 +++++++++++++++++ src/init.cpp | 22 ++++++++++++++++++++++ src/main.cpp | 24 +++++++++++++++++++++--- src/main.h | 4 ++++ src/net.cpp | 3 ++- src/net.h | 5 ++++- src/rpc/net.cpp | 2 ++ 7 files changed, 72 insertions(+), 5 deletions(-) diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md index 379fcad3f..d9cb6195b 100644 --- a/doc/reduce-traffic.md +++ b/doc/reduce-traffic.md @@ -36,3 +36,20 @@ blocks and transactions to fewer nodes. Reducing the maximum connected nodes to a minimum could be desirable if traffic limits are tiny. Keep in mind that bitcoin's trustless model works best if you are connected to a handful of nodes. + +## 4. Turn off transaction relay (`-blocksonly`) + +Forwarding transactions to peers increases the P2P traffic. To only sync blocks +with other peers, you can disable transaction relay. + +Be reminded of the effects of this setting. + +- Fee estimation will no longer work. +- It sets the flag "-walletbroadcast" to be "0", only if it is currently unset. + Doing so disables the automatic broadcasting of transactions from wallet. Not + relaying other's transactions could hurt your privacy if used while a wallet + is loaded or if you use the node to broadcast transactions. +- If a peer is whitelisted and "-whitelistforcerelay" is set to "1" (which will + also set "whitelistrelay" to "1"), we will still receive and relay their transactions. +- It makes block propagation slower because compact block relay can only be + used when transaction relay is enabled. diff --git a/src/init.cpp b/src/init.cpp index 074cd7210..c511d0921 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -330,6 +330,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS)); strUsage += HelpMessageOpt("-alertnotify=", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); + if (showDebug) + strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless '-whitelistforcerelay' is '1', in which case whitelisted peers' transactions will be relayed. RPC transactions are not affected. (default: %u)"), DEFAULT_BLOCKSONLY)); strUsage += HelpMessageOpt("-checkblocks=", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); strUsage += HelpMessageOpt("-checklevel=", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); @@ -396,6 +398,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-whitebind=", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6")); strUsage += HelpMessageOpt("-whitelist=", _("Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.") + " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); + strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted inbound peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); + strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted inbound peers even they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY)); strUsage += HelpMessageOpt("-maxuploadtarget=", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); #ifdef ENABLE_WALLET @@ -923,6 +927,22 @@ void InitParameterInteraction() LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); } #endif + + // disable walletbroadcast and whitelistrelay in blocksonly mode + if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) { + if (SoftSetBoolArg("-whitelistrelay", false)) + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__); +#ifdef ENABLE_WALLET + if (SoftSetBoolArg("-walletbroadcast", false)) + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); +#endif + } + + // Forcing relay from whitelisted hosts implies we will accept relays from them in the first place. + if (GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { + if (SoftSetBoolArg("-whitelistrelay", true)) + LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); + } } void InitLogging() @@ -994,6 +1014,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 2: parameter interactions const CChainParams& chainparams = Params(); + // also see: InitParameterInteraction() + // Set this early so that experimental features are correctly enabled/disabled auto err = InitExperimentalMode(); if (err) { diff --git a/src/main.cpp b/src/main.cpp index ed2c263de..e8dd75993 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5544,6 +5544,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return error("message inv size() = %u", vInv.size()); } + bool fBlocksOnly = GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); + + // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true + if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) + fBlocksOnly = false; + LOCK(cs_main); std::vector vToFetch; @@ -5581,8 +5587,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } - } else { - if (!fAlreadyHave && !IsInitialBlockDownload(chainparams)) + } + else + { + if (fBlocksOnly) + LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id); + else if (!fAlreadyHave && !IsInitialBlockDownload(chainparams)) pfrom->AskFor(inv); } @@ -5709,6 +5719,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == "tx" && !IsInitialBlockDownload(chainparams)) { + // Stop processing the transaction early if + // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off + if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) + { + LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id); + return true; + } + vector vWorkQueue; vector vEraseQueue; CTransaction tx; @@ -5807,7 +5825,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, assert(recentRejects); recentRejects->insert(tx.GetHash()); - if (pfrom->fWhitelisted) { + if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { // Always relay transactions received from whitelisted peers, even // if they were already in the mempool or rejected from it due // to policy, allowing the node to function as a gateway for diff --git a/src/main.h b/src/main.h index d252172dd..22a8b8ca9 100644 --- a/src/main.h +++ b/src/main.h @@ -58,6 +58,10 @@ struct CNodeStateStats; static const bool DEFAULT_ALERTS = true; /** Maximum reorg length we will accept before we shut down and alert the user. */ static const unsigned int MAX_REORG_LENGTH = COINBASE_MATURITY - 1; +/** Default for DEFAULT_WHITELISTRELAY. */ +static const bool DEFAULT_WHITELISTRELAY = true; +/** Default for DEFAULT_WHITELISTFORCERELAY. */ +static const bool DEFAULT_WHITELISTFORCERELAY = true; /** Default for -minrelaytxfee, minimum relay fee for transactions */ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100; //! -maxtxfee default diff --git a/src/net.cpp b/src/net.cpp index 597544fee..bc25ec5a5 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -432,7 +432,7 @@ void CNode::PushVersion() else LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, strSubVersion, nBestHeight, true); + nLocalHostNonce, strSubVersion, nBestHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)); } @@ -536,6 +536,7 @@ void CNode::copyStats(CNodeStats &stats) { stats.nodeid = this->GetId(); stats.nServices = nServices; + stats.fRelayTxes = fRelayTxes; stats.nLastSend = nLastSend; stats.nLastRecv = nLastRecv; stats.nTimeConnected = nTimeConnected; diff --git a/src/net.h b/src/net.h index 30ec7cb72..076314c23 100644 --- a/src/net.h +++ b/src/net.h @@ -59,6 +59,8 @@ static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ; static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; /** The default for -maxuploadtarget. 0 = Unlimited */ static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0; +/** Default for blocks only*/ +static const bool DEFAULT_BLOCKSONLY = false; /** The period before a network upgrade activates, where connections to upgrading peers are preferred (in blocks). */ static const int NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD = 24 * 24 * 3; @@ -181,6 +183,7 @@ class CNodeStats public: NodeId nodeid; uint64_t nServices; + bool fRelayTxes; int64_t nLastSend; int64_t nLastRecv; int64_t nTimeConnected; @@ -286,7 +289,7 @@ class CNode // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version message // b) the peer may tell us in its version message that we should not relay tx invs - // until it has initialized its bloom filter. + // unless it loads a bloom filter. bool fRelayTxes; bool fSentAddr; CSemaphoreGrant grantOutbound; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 212803bb7..bd0f3d3da 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -86,6 +86,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) " \"addr\":\"host:port\", (string) The IP address and port of the peer\n" " \"addrlocal\":\"ip:port\", (string) local address\n" " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" + " \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n" " \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n" " \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n" " \"bytessent\": n, (numeric) The total bytes sent\n" @@ -129,6 +130,7 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) if (!(stats.addrLocal.empty())) obj.pushKV("addrlocal", stats.addrLocal); obj.pushKV("services", strprintf("%016x", stats.nServices)); + obj.pushKV("relaytxes", stats.fRelayTxes); obj.pushKV("lastsend", stats.nLastSend); obj.pushKV("lastrecv", stats.nLastRecv); obj.pushKV("bytessent", stats.nSendBytes); From 404af83110994a1963eaaabba69f080e5a5aeee4 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 01:11:18 +0000 Subject: [PATCH 052/146] Fix build regression by adding #include --- src/net.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net.h b/src/net.h index 076314c23..cc8cf13dc 100644 --- a/src/net.h +++ b/src/net.h @@ -21,6 +21,7 @@ #include #include +#include #ifndef WIN32 #include From a9919d4668b8200194302727f43ca5a99e8d2343 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 01:31:18 +0000 Subject: [PATCH 053/146] Replace SigCache implementation with CuckooCache --- src/cuckoocache.h | 163 +++++++++++++++++++-------------- src/gtest/main.cpp | 2 + src/main.cpp | 1 - src/prevector.h | 1 + src/test/cuckoocache_tests.cpp | 13 +-- 5 files changed, 104 insertions(+), 76 deletions(-) diff --git a/src/cuckoocache.h b/src/cuckoocache.h index efd6a820b..33260a35a 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -2,54 +2,52 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef _BITCOIN_CUCKOOCACHE_H_ -#define _BITCOIN_CUCKOOCACHE_H_ +#ifndef BITCOIN_CUCKOOCACHE_H +#define BITCOIN_CUCKOOCACHE_H #include -#include #include -#include #include +#include #include +#include #include -/** namespace CuckooCache provides high performance cache primitives +/** High-performance cache primitives. * * Summary: * - * 1) bit_packed_atomic_flags is bit-packed atomic flags for garbage collection + * 1. @ref bit_packed_atomic_flags is bit-packed atomic flags for garbage collection * - * 2) cache is a cache which is performant in memory usage and lookup speed. It - * is lockfree for erase operations. Elements are lazily erased on the next - * insert. + * 2. @ref cache is a cache which is performant in memory usage and lookup speed. It + * is lockfree for erase operations. Elements are lazily erased on the next insert. */ namespace CuckooCache { -/** bit_packed_atomic_flags implements a container for garbage collection flags +/** @ref bit_packed_atomic_flags implements a container for garbage collection flags * that is only thread unsafe on calls to setup. This class bit-packs collection * flags for memory efficiency. * - * All operations are std::memory_order_relaxed so external mechanisms must + * All operations are `std::memory_order_relaxed` so external mechanisms must * ensure that writes and reads are properly synchronized. * - * On setup(n), all bits up to n are marked as collected. + * On setup(n), all bits up to `n` are marked as collected. * * Under the hood, because it is an 8-bit type, it makes sense to use a multiple * of 8 for setup, but it will be safe if that is not the case as well. - * */ class bit_packed_atomic_flags { std::unique_ptr[]> mem; public: - /** No default constructor as there must be some size */ + /** No default constructor, as there must be some size. */ bit_packed_atomic_flags() = delete; /** * bit_packed_atomic_flags constructor creates memory to sufficiently - * keep track of garbage collection information for size entries. + * keep track of garbage collection information for `size` entries. * * @param size the number of elements to allocate space for * @@ -68,7 +66,7 @@ class bit_packed_atomic_flags }; /** setup marks all entries and ensures that bit_packed_atomic_flags can store - * at least size entries + * at least `b` entries. * * @param b the number of elements to allocate space for * @post bit_set, bit_unset, and bit_is_set function properly forall x. x < @@ -84,19 +82,18 @@ class bit_packed_atomic_flags /** bit_set sets an entry as discardable. * - * @param s the index of the entry to bit_set. + * @param s the index of the entry to bit_set * @post immediately subsequent call (assuming proper external memory * ordering) to bit_is_set(s) == true. - * */ inline void bit_set(uint32_t s) { mem[s >> 3].fetch_or(1 << (s & 7), std::memory_order_relaxed); } - /** bit_unset marks an entry as something that should not be overwritten + /** bit_unset marks an entry as something that should not be overwritten. * - * @param s the index of the entry to bit_unset. + * @param s the index of the entry to bit_unset * @post immediately subsequent call (assuming proper external memory * ordering) to bit_is_set(s) == false. */ @@ -105,10 +102,10 @@ class bit_packed_atomic_flags mem[s >> 3].fetch_and(~(1 << (s & 7)), std::memory_order_relaxed); } - /** bit_is_set queries the table for discardability at s + /** bit_is_set queries the table for discardability at `s`. * - * @param s the index of the entry to read. - * @returns if the bit at index s was set. + * @param s the index of the entry to read + * @returns true if the bit at index `s` was set, false otherwise * */ inline bool bit_is_set(uint32_t s) const { @@ -116,15 +113,15 @@ class bit_packed_atomic_flags } }; -/** cache implements a cache with properties similar to a cuckoo-set +/** @ref cache implements a cache with properties similar to a cuckoo-set. * - * The cache is able to hold up to (~(uint32_t)0) - 1 elements. + * The cache is able to hold up to `(~(uint32_t)0) - 1` elements. * * Read Operations: - * - contains(*, false) + * - contains() for `erase=false` * * Read+Erase Operations: - * - contains(*, true) + * - contains() for `erase=true` * * Erase Operations: * - allow_erase() @@ -141,10 +138,10 @@ class bit_packed_atomic_flags * * User Must Guarantee: * - * 1) Write Requires synchronized access (e.g., a lock) - * 2) Read Requires no concurrent Write, synchronized with the last insert. - * 3) Erase requires no concurrent Write, synchronized with last insert. - * 4) An Erase caller must release all memory before allowing a new Writer. + * 1. Write requires synchronized access (e.g. a lock) + * 2. Read requires no concurrent Write, synchronized with last insert. + * 3. Erase requires no concurrent Write, synchronized with last insert. + * 4. An Erase caller must release all memory before allowing a new Writer. * * * Note on function names: @@ -154,7 +151,7 @@ class bit_packed_atomic_flags * @tparam Element should be a movable and copyable type * @tparam Hash should be a function/callable which takes a template parameter * hash_select and an Element and extracts a hash from it. Should return - * high-entropy hashes for `Hash h; h<0>(e) ... h<7>(e)`. + * high-entropy uint32_t hashes for `Hash h; h<0>(e) ... h<7>(e)`. */ template class cache @@ -176,8 +173,8 @@ class cache */ mutable std::vector epoch_flags; - /** epoch_heuristic_counter is used to determine when a epoch might be aged - * & an expensive scan should be done. epoch_heuristic_counter is + /** epoch_heuristic_counter is used to determine when an epoch might be aged + * & an expensive scan should be done. epoch_heuristic_counter is * decremented on insert and reset to the new number of inserts which would * cause the epoch to reach epoch_size when it reaches zero. */ @@ -193,48 +190,74 @@ class cache */ uint32_t epoch_size; - /** hash_mask should be set to appropriately mask out a hash such that every - * masked hash is [0,size), eg, if floor(log2(size)) == 20, then hash_mask - * should be (1<<20)-1 - */ - uint32_t hash_mask; - /** depth_limit determines how many elements insert should try to replace. - * Should be set to log2(n)*/ + * Should be set to log2(n). + */ uint8_t depth_limit; /** hash_function is a const instance of the hash function. It cannot be * static or initialized at call time as it may have internal state (such as * a nonce). - * */ + */ const Hash hash_function; /** compute_hashes is convenience for not having to write out this * expression everywhere we use the hash values of an Element. * - * @param e the element whose hashes will be returned - * @returns std::array of deterministic hashes derived from e + * We need to map the 32-bit input hash onto a hash bucket in a range [0, size) in a + * manner which preserves as much of the hash's uniformity as possible. Ideally + * this would be done by bitmasking but the size is usually not a power of two. + * + * The naive approach would be to use a mod -- which isn't perfectly uniform but so + * long as the hash is much larger than size it is not that bad. Unfortunately, + * mod/division is fairly slow on ordinary microprocessors (e.g. 90-ish cycles on + * haswell, ARM doesn't even have an instruction for it.); when the divisor is a + * constant the compiler will do clever tricks to turn it into a multiply+add+shift, + * but size is a run-time value so the compiler can't do that here. + * + * One option would be to implement the same trick the compiler uses and compute the + * constants for exact division based on the size, as described in "{N}-bit Unsigned + * Division via {N}-bit Multiply-Add" by Arch D. Robison in 2005. But that code is + * somewhat complicated and the result is still slower than other options: + * + * Instead we treat the 32-bit random number as a Q32 fixed-point number in the range + * [0, 1) and simply multiply it by the size. Then we just shift the result down by + * 32-bits to get our bucket number. The result has non-uniformity the same as a + * mod, but it is much faster to compute. More about this technique can be found at + * http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ . + * + * The resulting non-uniformity is also more equally distributed which would be + * advantageous for something like linear probing, though it shouldn't matter + * one way or the other for a cuckoo table. + * + * The primary disadvantage of this approach is increased intermediate precision is + * required but for a 32-bit random number we only need the high 32 bits of a + * 32*32->64 multiply, which means the operation is reasonably fast even on a + * typical 32-bit processor. + * + * @param e The element whose hashes will be returned + * @returns Deterministic hashes derived from `e` uniformly mapped onto the range [0, size) */ inline std::array compute_hashes(const Element& e) const { - return {{hash_function.template operator()<0>(e) & hash_mask, - hash_function.template operator()<1>(e) & hash_mask, - hash_function.template operator()<2>(e) & hash_mask, - hash_function.template operator()<3>(e) & hash_mask, - hash_function.template operator()<4>(e) & hash_mask, - hash_function.template operator()<5>(e) & hash_mask, - hash_function.template operator()<6>(e) & hash_mask, - hash_function.template operator()<7>(e) & hash_mask}}; + return {{(uint32_t)(((uint64_t)hash_function.template operator()<0>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<1>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<2>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<3>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<4>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<5>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<6>(e) * (uint64_t)size) >> 32), + (uint32_t)(((uint64_t)hash_function.template operator()<7>(e) * (uint64_t)size) >> 32)}}; } - /* end - * @returns a constexpr index that can never be inserted to */ + /** invalid returns a special index that can never be inserted to + * @returns the special constexpr index that can never be inserted to */ constexpr uint32_t invalid() const { return ~(uint32_t)0; } - /** allow_erase marks the element at index n as discardable. Threadsafe + /** allow_erase marks the element at index `n` as discardable. Threadsafe * without any concurrent insert. * @param n the index to allow erasure of */ @@ -243,7 +266,7 @@ class cache collection_flags.bit_set(n); } - /** please_keep marks the element at index n as an entry that should be kept. + /** please_keep marks the element at index `n` as an entry that should be kept. * Threadsafe without any concurrent insert. * @param n the index to prioritize keeping */ @@ -305,19 +328,18 @@ class cache } /** setup initializes the container to store no more than new_size - * elements. setup rounds down to a power of two size. + * elements. * * setup should only be called once. * * @param new_size the desired number of elements to store * @returns the maximum number of elements storable - **/ + */ uint32_t setup(uint32_t new_size) { // depth_limit must be at least one otherwise errors can occur. depth_limit = static_cast(std::log2(static_cast(std::max((uint32_t)2, new_size)))); - size = 1 << depth_limit; - hash_mask = size-1; + size = std::max(2, new_size); table.resize(size); collection_flags.setup(size); epoch_flags.resize(size); @@ -336,7 +358,7 @@ class cache * negligible compared to the size of the elements. * * @param bytes the approximate number of bytes to use for this data - * structure. + * structure * @returns the maximum number of elements storable (see setup() * documentation for more detail) */ @@ -352,10 +374,12 @@ class cache * It drops the last tried element if it runs out of depth before * encountering an open slot. * - * Thus + * Thus: * + * ``` * insert(x); * return contains(x, false); + * ``` * * is not guaranteed to return true. * @@ -363,7 +387,6 @@ class cache * @post one of the following: All previously inserted elements and e are * now in the table, one previously inserted element is evicted from the * table, the entry attempted to be inserted is evicted. - * */ inline void insert(Element e) { @@ -392,10 +415,10 @@ class cache /** Swap with the element at the location that was * not the last one looked at. Example: * - * 1) On first iteration, last_loc == invalid(), find returns last, so + * 1. On first iteration, last_loc == invalid(), find returns last, so * last_loc defaults to locs[0]. - * 2) On further iterations, where last_loc == locs[k], last_loc will - * go to locs[k+1 % 8], i.e., next of the 8 indicies wrapping around + * 2. On further iterations, where last_loc == locs[k], last_loc will + * go to locs[k+1 % 8], i.e., next of the 8 indices wrapping around * to 0 if needed. * * This prevents moving the element we just put in. @@ -415,17 +438,19 @@ class cache } } - /* contains iterates through the hash locations for a given element + /** contains iterates through the hash locations for a given element * and checks to see if it is present. * * contains does not check garbage collected state (in other words, * garbage is only collected when the space is needed), so: * + * ``` * insert(x); * if (contains(x, true)) * return contains(x, false); * else * return true; + * ``` * * executed on a single thread will always return true! * @@ -434,7 +459,7 @@ class cache * contains returns a bool set true if the element was found. * * @param e the element to check - * @param erase + * @param erase whether to attempt setting the garbage collect flag * * @post if erase is true and the element is found, then the garbage collect * flag is set @@ -454,4 +479,4 @@ class cache }; } // namespace CuckooCache -#endif +#endif // BITCOIN_CUCKOOCACHE_H diff --git a/src/gtest/main.cpp b/src/gtest/main.cpp index d3af84bc4..2f8c4b09a 100644 --- a/src/gtest/main.cpp +++ b/src/gtest/main.cpp @@ -2,6 +2,7 @@ #include "crypto/common.h" #include "key.h" #include "pubkey.h" +#include "script/sigcache.h" #include "zcash/JoinSplit.hpp" #include "util.h" @@ -21,6 +22,7 @@ ZCJoinSplit* params; int main(int argc, char **argv) { assert(init_and_check_sodium() != -1); ECC_Start(); + InitSignatureCache(); params = ZCJoinSplit::Prepared(); diff --git a/src/main.cpp b/src/main.cpp index e8dd75993..6f4c0d7d9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,7 +36,6 @@ #include "wallet/asyncrpcoperation_shieldcoinbase.h" #include "warnings.h" -#include #include #include #include diff --git a/src/prevector.h b/src/prevector.h index d1d30ed44..bfb5ba858 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -9,6 +9,7 @@ #include #include +#include #pragma pack(push, 1) /** Implements a drop-in replacement for std::vector which stores up to N diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index 8636fb79c..8b5454f71 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -12,11 +12,11 @@ /** Test Suite for CuckooCache * - * 1) All tests should have a deterministic result (using insecure rand + * 1. All tests should have a deterministic result (using insecure rand * with deterministic seeds) - * 2) Some test methods are templated to allow for easier testing + * 2. Some test methods are templated to allow for easier testing * against new versions / comparing - * 3) Results should be treated as a regression test, i.e., did the behavior + * 3. Results should be treated as a regression test, i.e., did the behavior * change significantly from what was expected. This can be OK, depending on * the nature of the change, but requires updating the tests to reflect the new * expected behavior. For example improving the hit rate may cause some tests @@ -38,6 +38,7 @@ void insecure_GetRandHash(uint256& t) } + /* Test that no values not inserted into the cache are read out of it. * * There are no repeats in the first 200000 insecure_GetRandHash calls @@ -100,9 +101,9 @@ double test_cache(size_t megabytes, double load) * * Examples: * - * 1) at load 0.5, we expect a perfect hit rate, so we multiply by + * 1. at load 0.5, we expect a perfect hit rate, so we multiply by * 1.0 - * 2) at load 2.0, we expect to see half the entries, so a perfect hit rate + * 2. at load 2.0, we expect to see half the entries, so a perfect hit rate * would be 0.5. Therefore, if we see a hit rate of 0.4, 0.4*2.0 = 0.8 is the * normalized hit rate. * @@ -366,7 +367,7 @@ void test_cache_generations() // Loose Check that hit rate is above min_hit_rate BOOST_CHECK(hit > min_hit_rate); // Tighter check, count number of times we are less than tight_hit_rate - // (and implicitly, greater than min_hit_rate) + // (and implicityly, greater than min_hit_rate) out_of_tight_tolerance += hit < tight_hit_rate; } // Check that being out of tolerance happens less than From 73b0de608de7ad72f3076ded471417a193396ff3 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 01:32:36 +0000 Subject: [PATCH 054/146] When rewinding, set pindexBestHeader to the highest-work block index --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 6f4c0d7d9..3247a03ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4678,6 +4678,14 @@ bool RewindBlockIndex(const CChainParams& chainparams, bool& clearWitnessCaches) PruneBlockIndexCandidates(); + // Ensure that pindexBestHeader points to the block index entry with the most work; + // setBlockIndexCandidates entries are sorted by work, highest at the end. + { + std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); + assert(it != setBlockIndexCandidates.rend()); + pindexBestHeader = *it; + } + CheckBlockIndex(chainparams.GetConsensus()); if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { From 49f9361bc240d46f49c26f6d155ed3e26701cc50 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 07:44:23 +0000 Subject: [PATCH 055/146] Revert "Add solution rates aproximation to metrics" This reverts commit 233301d12edeee2b3c7c6ab9ccdd6ee1e8867065. --- src/metrics.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/metrics.cpp b/src/metrics.cpp index c09fd95c5..a37de4cb6 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -277,7 +277,6 @@ int printStats(bool isScreen, bool mining) int64_t currentHeadersTime; size_t connections; int64_t netsolps; - double netsolpsLog = 1.0; const Consensus::Params& params = Params().GetConsensus(); { LOCK2(cs_main, cs_vNodes); @@ -286,12 +285,8 @@ int printStats(bool isScreen, bool mining) currentHeadersTime = pindexBestHeader ? pindexBestHeader->nTime : 0; connections = vNodes.size(); netsolps = GetNetworkHashPS(120, -1); - netsolpsLog = std::log2(netsolps); } auto localsolps = GetLocalSolPS(); - double localsolpsLog = 0.0; - if (localsolps > 0) - localsolpsLog = std::log2(localsolps); if (IsInitialBlockDownload(Params())) { if (fReindex) { @@ -348,9 +343,9 @@ int printStats(bool isScreen, bool mining) } std::cout << " " << _("Next upgrade") << " | " << strUpgradeTime << std::endl; std::cout << " " << _("Connections") << " | " << connections << std::endl; - std::cout << " " << _("Network solution rate") << " | " << strprintf("~ 2^%.4f Sol/s", netsolpsLog) << std::endl; + std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl; if (mining && miningTimer.running()) { - std::cout << " " << _("Local solution rate") << " | " << strprintf("~ 2^%.4f Sol/s", localsolpsLog) << std::endl; + std::cout << " " << _("Local solution rate") << " | " << strprintf("%.4f Sol/s", localsolps) << std::endl; lines++; } std::cout << std::endl; From 70a4ab3da564b848dcad3e7a289d174647337f20 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 08:10:20 +0000 Subject: [PATCH 056/146] Revert "metrics: Add a progress bar when in Initial Block Download mode" This reverts commit ac98d743e5bce7a27f6660a8e5a9cc87c145f345. --- src/metrics.cpp | 42 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/src/metrics.cpp b/src/metrics.cpp index a37de4cb6..6013f65ef 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -15,7 +15,6 @@ #include "utilmoneystr.h" #include "utilstrencodings.h" -#include #include #include #include @@ -268,7 +267,7 @@ std::string DisplaySize(size_t value) return strprintf(_("%.2f TiB"), value / coef); } -int printStats(bool isScreen, bool mining) +int printStats(bool mining) { // Number of lines that are always displayed int lines = 5; @@ -293,38 +292,13 @@ int printStats(bool isScreen, bool mining) int downloadPercent = nSizeReindexed * 100 / nFullSizeToReindex; std::cout << " " << _("Reindexing blocks") << " | " << DisplaySize(nSizeReindexed) << " / " << DisplaySize(nFullSizeToReindex) << " (" << downloadPercent << "%, " << height << " " << _("blocks") << ")" << std::endl; } else { - int nHeaders = currentHeadersHeight; - if (nHeaders < 0) - nHeaders = 0; int netheight = currentHeadersHeight == -1 || currentHeadersTime == 0 ? - 0 : EstimateNetHeight(params, currentHeadersHeight, currentHeadersTime); - if (netheight < nHeaders) - netheight = nHeaders; - if (netheight <= 0) - netheight = 1; - int downloadPercent = height * 100 / netheight; - std::cout << " " << _("Downloading blocks") << " | " << height << " (" << nHeaders << " " << _("headers") << ") / ~" << netheight << " (" << downloadPercent << "%)" << std::endl; - - if (isScreen) { - // Draw 50-character progress bar, which will fit into a 79-character line. - int blockChars = downloadPercent / 2; - int headerChars = (nHeaders * 50) / netheight; - // Start with background colour reversed for "full" bar. - std::cout << " | [␛[7m"; - for (auto i : boost::irange(0, 50)) { - if (i == headerChars) { - // Switch to normal background colour for "empty" bar. - std::cout << "␛[0m"; - } else if (i == blockChars) { - // Switch to distinct colour for "headers" bar. - std::cout << "␛[0;43m"; - } - std::cout << " "; - } - // Ensure that colour is reset after the progress bar is printed. - std::cout << "␛[0m]" << std::endl; - lines++; + 0 : EstimateNetHeight(params, currentHeadersHeight, currentHeadersTime); + int downloadPercent = 0; + if (netheight > 0) { + downloadPercent = height * 100 / netheight; } + std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%)" << std::endl; } } else { std::cout << " " << _("Block height") << " | " << height << std::endl; @@ -379,7 +353,7 @@ int printMiningStatus(bool mining) } } lines++; - } else if (Params().NetworkIDString() != "main") { + } else { std::cout << _("You are currently not mining.") << std::endl; std::cout << _("To enable mining, add 'gen=1' to your bitcoinz.conf and restart.") << std::endl; lines += 2; @@ -605,7 +579,7 @@ void ThreadShowMetricsScreen() #endif if (loaded) { - lines += printStats(isScreen, mining); + lines += printStats(mining); lines += printMiningStatus(mining); } lines += printMetrics(cols, mining); From 78d23a42d2e04c48cf2d63addc69fee3948a4314 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 08:21:51 +0000 Subject: [PATCH 057/146] Fix librustzcash warnings --- src/rustzcash/src/rustzcash.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/rustzcash/src/rustzcash.rs b/src/rustzcash/src/rustzcash.rs index 5c2481c3b..43837ce35 100644 --- a/src/rustzcash/src/rustzcash.rs +++ b/src/rustzcash/src/rustzcash.rs @@ -31,6 +31,7 @@ use std::fs::File; use std::io::BufReader; use std::path::{Path, PathBuf}; use std::slice; +use std::ptr::addr_of; #[cfg(not(target_os = "windows"))] use std::ffi::OsStr; @@ -772,12 +773,23 @@ pub extern "C" fn librustzcash_sprout_prove( vpub_new: u64, ) { // Load parameters from disk - let sprout_fs = File::open( - unsafe { &SPROUT_GROTH16_PARAMS_PATH } - .as_ref() - .expect("parameters should have been initialized"), - ) - .expect("couldn't load Sprout groth16 parameters file"); + let sprout_path = unsafe { addr_of!(SPROUT_GROTH16_PARAMS_PATH).as_ref() } + .expect("Only mutated during init, so this pointer can never be null") + .as_ref() + .expect( + "Parameters not loaded: SPROUT_GROTH16_PARAMS_PATH should have been initialized", + ); + const HOW_TO_FIX: &str = " +Please download this file from https://download.z.cash/downloads/sprout-groth16.params +and put it at "; + let sprout_fs = File::open(sprout_path).unwrap_or_else(|err| { + panic!( + "Couldn't load Sprout Groth16 parameters file: {}{}{}", + err, + HOW_TO_FIX, + &sprout_path.to_string_lossy() + ) + }); let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs); From 355ae8dc627b2416edc3dd3f5398207b2fb3b73b Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 09:38:17 +0000 Subject: [PATCH 058/146] [wallet] Kill accounts --- qa/rpc-tests/listtransactions.py | 28 +- qa/rpc-tests/txn_doublespend.py | 8 +- src/Makefile.test.include | 1 - src/rpc/client.cpp | 10 - src/wallet/gtest/test_wallet.cpp | 18 +- src/wallet/rpcwallet.cpp | 887 ++++----------------------- src/wallet/test/accounting_tests.cpp | 140 ----- src/wallet/test/rpc_wallet_tests.cpp | 73 +-- src/wallet/wallet.cpp | 320 +++++----- src/wallet/wallet.h | 178 ++---- src/wallet/walletdb.cpp | 185 +----- src/wallet/walletdb.h | 13 +- 12 files changed, 360 insertions(+), 1501 deletions(-) delete mode 100644 src/wallet/test/accounting_tests.cpp diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index 473c88306..1c6a990ba 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -40,19 +40,19 @@ def run_test(self): self.sync_all() check_array_result(self.nodes[0].listtransactions(), {"txid":txid}, - {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0}) + {"category":"send","amount":Decimal("-0.1"),"confirmations":0}) check_array_result(self.nodes[1].listtransactions(), {"txid":txid}, - {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0}) + {"category":"receive","amount":Decimal("0.1"),"confirmations":0}) # mine a block, confirmations should change: self.nodes[0].generate(1) self.sync_all() check_array_result(self.nodes[0].listtransactions(), {"txid":txid}, - {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1}) + {"category":"send","amount":Decimal("-0.1"),"confirmations":1}) check_array_result(self.nodes[1].listtransactions(), {"txid":txid}, - {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1}) + {"category":"receive","amount":Decimal("0.1"),"confirmations":1}) # send-to-self: txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) @@ -64,10 +64,14 @@ def run_test(self): {"amount":Decimal("0.2")}) # sendmany from node1: twice to self, twice to node2: - send_to = { self.nodes[0].getnewaddress() : 0.11, - self.nodes[1].getnewaddress() : 0.22, - self.nodes[0].getaccountaddress("") : 0.33, - self.nodes[1].getaccountaddress("") : 0.44 } + node_0_addr_0 = self.nodes[0].getnewaddress() + node_0_addr_1 = self.nodes[0].getnewaddress() + node_1_addr_0 = self.nodes[1].getnewaddress() + node_1_addr_1 = self.nodes[1].getnewaddress() + send_to = { node_0_addr_0 : 0.11, + node_1_addr_0 : 0.22, + node_0_addr_1 : 0.33, + node_1_addr_1 : 0.44 } txid = self.nodes[1].sendmany("", send_to) self.sync_all() check_array_result(self.nodes[1].listtransactions(), @@ -87,13 +91,13 @@ def run_test(self): {"txid":txid} ) check_array_result(self.nodes[0].listtransactions(), {"category":"receive","amount":Decimal("0.33")}, - {"txid":txid, "account" : ""} ) + {"txid":txid} ) check_array_result(self.nodes[1].listtransactions(), {"category":"send","amount":Decimal("-0.44")}, - {"txid":txid, "account" : ""} ) + {"txid":txid} ) check_array_result(self.nodes[1].listtransactions(), {"category":"receive","amount":Decimal("0.44")}, - {"txid":txid, "account" : ""} ) + {"txid":txid} ) multisig = self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()]) self.nodes[0].importaddress(multisig["redeemScript"], "watchonly", False, True) @@ -103,7 +107,7 @@ def run_test(self): assert(len(self.nodes[0].listtransactions("watchonly", 100, 0, False)) == 0) check_array_result(self.nodes[0].listtransactions("watchonly", 100, 0, True), {"category":"receive","amount":Decimal("0.1")}, - {"txid":txid, "account" : "watchonly"} ) + {"txid":txid, "involvesWatchonly": True} ) if __name__ == '__main__': ListTransactionsTest().main() diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py index da0b2c7be..5d1db20c0 100755 --- a/qa/rpc-tests/txn_doublespend.py +++ b/qa/rpc-tests/txn_doublespend.py @@ -49,8 +49,8 @@ def run_test(self): # Create two transaction from node[0] to node[1]; the # second must spend change from the first because the first # spends all mature inputs: - txid1 = self.nodes[0].sendfrom("", node1_address, (starting_balance - (mining_reward - 2)), 0) - txid2 = self.nodes[0].sendfrom("", node1_address, 5, 0) + txid1 = self.nodes[0].sendtoaddress(node1_address, (starting_balance - (mining_reward - 2))) + txid2 = self.nodes[0].sendtoaddress(node1_address, 5) # Have node0 mine a block: if (self.options.mine_block): @@ -72,7 +72,7 @@ def run_test(self): assert_equal(tx1["confirmations"], 1) assert_equal(tx2["confirmations"], 1) # Node1's total balance should be its starting balance plus both transaction amounts: - assert_equal(self.nodes[1].getbalance(""), starting_balance - (tx1["amount"]+tx2["amount"])) + assert_equal(self.nodes[1].getbalance("*"), starting_balance - (tx1["amount"]+tx2["amount"])) else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) @@ -102,7 +102,7 @@ def run_test(self): assert_equal(self.nodes[0].getbalance("*"), expected) # Node1's total balance should be its starting balance plus the amount of the mutated send: - assert_equal(self.nodes[1].getbalance(""), starting_balance + (starting_balance - (mining_reward - 2))) + assert_equal(self.nodes[1].getbalance("*"), starting_balance + (starting_balance - (mining_reward - 2))) if __name__ == '__main__': TxnMallTest().main() diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 3c3fe6bc4..b1d8f8ea3 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -114,7 +114,6 @@ if ENABLE_WALLET BITCOIN_TESTS += \ wallet/test/wallet_test_fixture.cpp \ wallet/test/wallet_test_fixture.h \ - wallet/test/accounting_tests.cpp \ wallet/test/crypto_tests.cpp \ wallet/test/wallet_tests.cpp \ wallet/test/rpc_wallet_tests.cpp diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 774aac80a..a4c189963 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -37,27 +37,17 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendtoaddress", 4 }, { "settxfee", 0 }, { "getreceivedbyaddress", 1 }, - { "getreceivedbyaccount", 1 }, { "listreceivedbyaddress", 0 }, { "listreceivedbyaddress", 1 }, { "listreceivedbyaddress", 2 }, - { "listreceivedbyaccount", 0 }, - { "listreceivedbyaccount", 1 }, - { "listreceivedbyaccount", 2 }, { "getbalance", 1 }, { "getbalance", 2 }, { "getblockhash", 0 }, { "dumpbootstrap", 1 }, { "dumpbootstrap", 2 }, - { "move", 2 }, - { "move", 3 }, - { "sendfrom", 2 }, - { "sendfrom", 3 }, { "listtransactions", 1 }, { "listtransactions", 2 }, { "listtransactions", 3 }, - { "listaccounts", 0 }, - { "listaccounts", 1 }, { "walletpassphrase", 1 }, { "getblocktemplate", 0 }, { "listsinceblock", 1 }, diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 7b786ee16..0ae878388 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -31,7 +31,7 @@ class MockWalletDB { MOCK_METHOD0(TxnCommit, bool()); MOCK_METHOD0(TxnAbort, bool()); - MOCK_METHOD2(WriteTx, bool(uint256 hash, const CWalletTx& wtx)); + MOCK_METHOD1(WriteTx, bool(const CWalletTx& wtx)); MOCK_METHOD1(WriteWitnessCacheSize, bool(int64_t nWitnessCacheSize)); MOCK_METHOD1(WriteBestBlock, bool(const CBlockLocator& loc)); }; @@ -1614,19 +1614,19 @@ TEST(WalletTests, WriteWitnessCache) { .WillRepeatedly(Return(true)); // WriteTx fails - EXPECT_CALL(walletdb, WriteTx(wtx.GetHash(), wtx)) + EXPECT_CALL(walletdb, WriteTx(wtx)) .WillOnce(Return(false)); EXPECT_CALL(walletdb, TxnAbort()) .Times(1); wallet.SetBestChain(walletdb, loc); // WriteTx throws - EXPECT_CALL(walletdb, WriteTx(wtx.GetHash(), wtx)) + EXPECT_CALL(walletdb, WriteTx(wtx)) .WillOnce(ThrowLogicError()); EXPECT_CALL(walletdb, TxnAbort()) .Times(1); wallet.SetBestChain(walletdb, loc); - EXPECT_CALL(walletdb, WriteTx(wtx.GetHash(), wtx)) + EXPECT_CALL(walletdb, WriteTx(wtx)) .WillRepeatedly(Return(true)); // WriteWitnessCacheSize fails @@ -1735,15 +1735,15 @@ TEST(WalletTests, SetBestChainIgnoresTxsWithoutShieldedData) { EXPECT_CALL(walletdb, TxnBegin()) .WillOnce(Return(true)); - EXPECT_CALL(walletdb, WriteTx(wtxTransparent.GetHash(), wtxTransparent)) + EXPECT_CALL(walletdb, WriteTx(wtxTransparent)) .Times(0); - EXPECT_CALL(walletdb, WriteTx(wtxSprout.GetHash(), wtxSprout)) + EXPECT_CALL(walletdb, WriteTx(wtxSprout)) .Times(1).WillOnce(Return(true)); - EXPECT_CALL(walletdb, WriteTx(wtxSproutTransparent.GetHash(), wtxSproutTransparent)) + EXPECT_CALL(walletdb, WriteTx(wtxSproutTransparent)) .Times(0); - EXPECT_CALL(walletdb, WriteTx(wtxSapling.GetHash(), wtxSapling)) + EXPECT_CALL(walletdb, WriteTx(wtxSapling)) .Times(1).WillOnce(Return(true)); - EXPECT_CALL(walletdb, WriteTx(wtxSaplingTransparent.GetHash(), wtxSaplingTransparent)) + EXPECT_CALL(walletdb, WriteTx(wtxSaplingTransparent)) .Times(0); EXPECT_CALL(walletdb, WriteWitnessCacheSize(0)) .WillOnce(Return(true)); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5333070cf..ef2db8eb9 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -125,14 +125,6 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) entry.pushKV("vJoinSplit", TxJoinSplitToJSON(wtx)); } -string AccountFromValue(const UniValue& value) -{ - string strAccount = value.get_str(); - if (strAccount != "") - throw JSONRPCError(RPC_WALLET_ACCOUNTS_UNSUPPORTED, "Accounts are unsupported"); - return strAccount; -} - UniValue getnewaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -140,23 +132,26 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( - "getnewaddress ( \"account\" )\n" + "getnewaddress ( \"\" )\n" "\nReturns a new BitcoinZ address for receiving payments.\n" + "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. If provided, it MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" + "1. (dummy) (string, optional) DEPRECATED. If provided, it MUST be set to the empty string \"\". Passing any other string will result in an error.\n" + "\nResult:\n" "\"address\" (string) The new BitcoinZ address\n" + "\nExamples:\n" + HelpExampleCli("getnewaddress", "") + HelpExampleRpc("getnewaddress", "") ); - LOCK2(cs_main, pwalletMain->cs_wallet); + const UniValue& dummy_value = params[0]; + if (!dummy_value.isNull() && dummy_value.get_str() != "") { + throw JSONRPCError(RPC_INVALID_PARAMETER, "dummy first argument must be excluded or set to \"\"."); + } - // Parse the account first so we don't generate a key if there's an error - string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); + LOCK2(cs_main, pwalletMain->cs_wallet); if (!pwalletMain->IsLocked()) pwalletMain->TopUpKeyPool(); @@ -167,81 +162,12 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); CKeyID keyID = newKey.GetID(); - pwalletMain->SetAddressBook(keyID, strAccount, "receive"); + std::string dummy_account; + pwalletMain->SetAddressBook(keyID, dummy_account, "receive"); return EncodeDestination(keyID); } - -CTxDestination GetAccountAddress(std::string strAccount, bool bForceNew=false) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - - CAccount account; - walletdb.ReadAccount(strAccount, account); - - bool bKeyUsed = false; - - // Check if the current key has been used - if (account.vchPubKey.IsValid()) - { - CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); - for (map::iterator it = pwalletMain->mapWallet.begin(); - it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); - ++it) - { - const CWalletTx& wtx = (*it).second; - for (const CTxOut& txout : wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - bKeyUsed = true; - } - } - - // Generate a new key - if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) - { - if (!pwalletMain->GetKeyFromPool(account.vchPubKey)) - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - - pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); - walletdb.WriteAccount(strAccount, account); - } - - return account.vchPubKey.GetID(); -} - -UniValue getaccountaddress(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccountaddress \"account\"\n" - "\nDEPRECATED. Returns the current BitcoinZ address for receiving payments to this account.\n" - "\nArguments:\n" - "1. \"account\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" - "\nResult:\n" - "\"address\" (string) The account BitcoinZ address\n" - "\nExamples:\n" - + HelpExampleCli("getaccountaddress", "") - + HelpExampleCli("getaccountaddress", "\"\"") - + HelpExampleCli("getaccountaddress", "\"myaccount\"") - + HelpExampleRpc("getaccountaddress", "\"myaccount\"") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - // Parse the account first so we don't generate a key if there's an error - string strAccount = AccountFromValue(params[0]); - - UniValue ret(UniValue::VSTR); - - ret = EncodeDestination(GetAccountAddress(strAccount)); - return ret; -} - - UniValue getrawchangeaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -276,124 +202,6 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp) return EncodeDestination(keyID); } - -UniValue setaccount(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "setaccount \"address\" \"account\"\n" - "\nDEPRECATED. Sets the account associated with the given address.\n" - "\nArguments:\n" - "1. \"address\" (string, required) The BitcoinZ address to be associated with an account.\n" - "2. \"account\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" - "\nExamples:\n" - + HelpExampleCli("setaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"tabby\"") - + HelpExampleRpc("setaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"tabby\"") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - CTxDestination dest = DecodeDestination(params[0].get_str()); - if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid BitcoinZ address"); - } - - string strAccount; - if (params.size() > 1) - strAccount = AccountFromValue(params[1]); - - // Only add the account if the address is yours. - if (IsMine(*pwalletMain, dest)) { - // Detect when changing the account of an address that is the 'unused current key' of another account: - if (pwalletMain->mapAddressBook.count(dest)) { - std::string strOldAccount = pwalletMain->mapAddressBook[dest].name; - if (dest == GetAccountAddress(strOldAccount)) { - GetAccountAddress(strOldAccount, true); - } - } - pwalletMain->SetAddressBook(dest, strAccount, "receive"); - } - else - throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); - - return NullUniValue; -} - - -UniValue getaccount(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() != 1) - throw runtime_error( - "getaccount \"address\"\n" - "\nDEPRECATED. Returns the account associated with the given address.\n" - "\nArguments:\n" - "1. \"address\" (string, required) The BitcoinZ address for account lookup.\n" - "\nResult:\n" - "\"accountname\" (string) the account address\n" - "\nExamples:\n" - + HelpExampleCli("getaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") - + HelpExampleRpc("getaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - CTxDestination dest = DecodeDestination(params[0].get_str()); - if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid BitcoinZ address"); - } - - std::string strAccount; - std::map::iterator mi = pwalletMain->mapAddressBook.find(dest); - if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) { - strAccount = (*mi).second.name; - } - return strAccount; -} - - -UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() != 1) - throw runtime_error( - "getaddressesbyaccount \"account\"\n" - "\nDEPRECATED. Returns the list of addresses for the given account.\n" - "\nArguments:\n" - "1. \"account\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" - "\nResult:\n" - "[ (json array of string)\n" - " \"address\" (string) a BitcoinZ address associated with the given account\n" - " ,...\n" - "]\n" - "\nExamples:\n" - + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") - + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - string strAccount = AccountFromValue(params[0]); - - // Find all addresses that have the given account - UniValue ret(UniValue::VARR); - for (const std::pair& item : pwalletMain->mapAddressBook) { - const CTxDestination& dest = item.first; - const std::string& strName = item.second.name; - if (strName == strAccount) { - ret.push_back(EncodeDestination(dest)); - } - } - return ret; -} - static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) { CAmount curBalance = pwalletMain->GetBalance(); @@ -501,7 +309,6 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp) " [\n" " \"address\", (string) The BitcoinZ address\n" " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"account\" (string, optional) The account (DEPRECATED)\n" " ]\n" " ,...\n" " ]\n" @@ -653,96 +460,6 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) return ValueFromAmount(nAmount); } - -UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() < 1 || params.size() > 2) - throw runtime_error( - "getreceivedbyaccount \"account\" ( minconf )\n" - "\nDEPRECATED. Returns the total amount received by addresses with in transactions with at least [minconf] confirmations.\n" - "\nArguments:\n" - "1. \"account\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" - "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" - "\nResult:\n" - "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" - "\nExamples:\n" - "\nAmount received by the default account with at least 1 confirmation\n" - + HelpExampleCli("getreceivedbyaccount", "\"\"") + - "\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n" - + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") + - "\nThe amount with at least 6 confirmation, very safe\n" - + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") + - "\nAs a json rpc call\n" - + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - // Minimum confirmations - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].getInt(); - - // Get the set of pub keys assigned to account - string strAccount = AccountFromValue(params[0]); - set setAddress = pwalletMain->GetAccountAddresses(strAccount); - - // Tally - CAmount nAmount = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) - continue; - - for (const CTxOut& txout : wtx.vout) - { - CTxDestination address; - if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; - } - } - - return ValueFromAmount(nAmount); -} - - -CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter) -{ - CAmount nBalance = 0; - - // Tally wallet transactions - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) - continue; - - CAmount nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); - - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; - nBalance -= nSent + nFee; - } - - // Tally internal accounting entries - nBalance += walletdb.GetAccountCreditDebit(strAccount); - - return nBalance; -} - -CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); -} - - UniValue getbalance(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -750,17 +467,17 @@ UniValue getbalance(const UniValue& params, bool fHelp) if (fHelp || params.size() > 3) throw runtime_error( - "getbalance ( \"account\" minconf includeWatchonly )\n" + "getbalance ( \"(dummy)\" minconf includeWatchonly )\n" "\nReturns the server's total available balance.\n" "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. If provided, it MUST be set to the empty string \"\" or to the string \"*\", either of which will give the total available balance. Passing any other string will result in an error.\n" + "1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\" or \"\".\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" "3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" "\nExamples:\n" "\nThe total amount in the wallet\n" - + HelpExampleCli("getbalance", "") + + + HelpExampleCli("getbalance", "*") + "\nThe total amount in the wallet at least 5 blocks confirmed\n" + HelpExampleCli("getbalance", "\"*\" 6") + "\nAs a json rpc call\n" @@ -769,50 +486,27 @@ UniValue getbalance(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - if (params.size() == 0) - return ValueFromAmount(pwalletMain->GetBalance()); - - int nMinDepth = 1; - if (params.size() > 1) - nMinDepth = params[1].getInt(); - isminefilter filter = ISMINE_SPENDABLE; - if(params.size() > 2) - if(params[2].get_bool()) - filter = filter | ISMINE_WATCH_ONLY; - - if (params[0].get_str() == "*") { - // Calculate total balance a different way from GetBalance() - // (GetBalance() sums up all unspent TxOuts) - // getbalance and "getbalance * 1 true" should return the same number - CAmount nBalance = 0; - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) - continue; - - CAmount allFee; - string strSentAccount; - list listReceived; - list listSent; - wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - for (const COutputEntry& r : listReceived) - nBalance += r.amount; - } - for (const COutputEntry& s : listSent) - nBalance -= s.amount; - nBalance -= allFee; - } - return ValueFromAmount(nBalance); + const UniValue& dummy_value = params[0]; + if (!dummy_value.isNull() && dummy_value.get_str() != "*" && dummy_value.get_str() != "") { + throw JSONRPCError(RPC_INVALID_PARAMETER, "dummy first argument must be excluded or set to \"*\" or \"\"."); } - string strAccount = AccountFromValue(params[0]); + int min_depth = 0; + if (!params[1].isNull()) { + min_depth = params[1].getInt(); + } - CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, filter); + isminefilter filter = ISMINE_SPENDABLE; + if (!params[2].isNull() && params[2].get_bool()) { + filter = filter | ISMINE_WATCH_ONLY; + } - return ValueFromAmount(nBalance); + CAmount nBalance = pwalletMain->GetBalance(filter, min_depth); + if (!params[3].isNull() && params[3].get_bool()) { + return nBalance; + } else { + return ValueFromAmount(nBalance); + } } UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) @@ -830,146 +524,6 @@ UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); } - -UniValue movecmd(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() < 3 || params.size() > 5) - throw runtime_error( - "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" - "\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n" - "\nArguments:\n" - "1. \"fromaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" - "2. \"toaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" - "3. amount (numeric) Quantity of " + CURRENCY_UNIT + " to move between accounts.\n" - "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" - "5. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" - "\nResult:\n" - "true|false (boolean) true if successful.\n" - "\nExamples:\n" - "\nMove 0.01 " + CURRENCY_UNIT + " from the default account to the account named tabby\n" - + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + - "\nMove 0.01 " + CURRENCY_UNIT + " timotei to akiko with a comment and funds have 6 confirmations\n" - + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + - "\nAs a json rpc call\n" - + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - string strFrom = AccountFromValue(params[0]); - string strTo = AccountFromValue(params[1]); - CAmount nAmount = AmountFromValue(params[2]); - if (nAmount <= 0) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); - if (params.size() > 3) - // unused parameter, used to be nMinDepth, keep type-checking it though - (void)params[3].getInt(); - string strComment; - if (params.size() > 4) - strComment = params[4].get_str(); - - CWalletDB walletdb(pwalletMain->strWalletFile); - if (!walletdb.TxnBegin()) - throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); - - int64_t nNow = GetAdjustedTime(); - - // Debit - CAccountingEntry debit; - debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); - debit.strAccount = strFrom; - debit.nCreditDebit = -nAmount; - debit.nTime = nNow; - debit.strOtherAccount = strTo; - debit.strComment = strComment; - walletdb.WriteAccountingEntry(debit); - - // Credit - CAccountingEntry credit; - credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); - credit.strAccount = strTo; - credit.nCreditDebit = nAmount; - credit.nTime = nNow; - credit.strOtherAccount = strFrom; - credit.strComment = strComment; - walletdb.WriteAccountingEntry(credit); - - if (!walletdb.TxnCommit()) - throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); - - return true; -} - - -UniValue sendfrom(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() < 3 || params.size() > 6) - throw runtime_error( - "sendfrom \"fromaccount\" \"toaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" - "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a BitcoinZ address.\n" - "The amount is a real and is rounded to the nearest 0.00000001." - + HelpRequiringPassphrase() + "\n" - "\nArguments:\n" - "1. \"fromaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" - "2. \"toaddress\" (string, required) The BitcoinZ address to send funds to.\n" - "3. amount (numeric, required) The amount in " + CURRENCY_UNIT + " (transaction fee is added on top).\n" - "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" - "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" - " This is not part of the transaction, just kept in your wallet.\n" - "6. \"comment-to\" (string, optional) An optional comment to store the name of the person or organization \n" - " to which you're sending the transaction. This is not part of the transaction, \n" - " it is just kept in your wallet.\n" - "\nResult:\n" - "\"transactionid\" (string) The transaction id.\n" - "\nExamples:\n" - "\nSend 0.01 " + CURRENCY_UNIT + " from the default account to the address, must have at least 1 confirmation\n" - + HelpExampleCli("sendfrom", "\"\" \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") + - "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" - + HelpExampleCli("sendfrom", "\"tabby\" \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + - "\nAs a json rpc call\n" - + HelpExampleRpc("sendfrom", "\"tabby\", \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - std::string strAccount = AccountFromValue(params[0]); - CTxDestination dest = DecodeDestination(params[1].get_str()); - if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid BitcoinZ address"); - } - CAmount nAmount = AmountFromValue(params[2]); - if (nAmount <= 0) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); - int nMinDepth = 1; - if (params.size() > 3) - nMinDepth = params[3].getInt(); - - CWalletTx wtx; - wtx.strFromAccount = strAccount; - if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty()) - wtx.mapValue["comment"] = params[4].get_str(); - if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty()) - wtx.mapValue["to"] = params[5].get_str(); - - EnsureWalletIsUnlocked(); - - // Check funds - CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); - if (nAmount > nBalance) - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); - - SendMoney(dest, nAmount, false, wtx); - - return wtx.GetHash().GetHex(); -} - - UniValue sendmany(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -977,11 +531,11 @@ UniValue sendmany(const UniValue& params, bool fHelp) if (fHelp || params.size() < 2 || params.size() > 5) throw runtime_error( - "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" + "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" "\nSend multiple times. Amounts are decimal numbers with at most 8 digits of precision." + HelpRequiringPassphrase() + "\n" "\nArguments:\n" - "1. \"fromaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" + "1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n" "2. \"amounts\" (string, required) A json object with addresses and amounts\n" " {\n" " \"address\":amount (numeric) The BitcoinZ address is the key, the numeric amount in " + CURRENCY_UNIT + " is the value\n" @@ -1013,14 +567,15 @@ UniValue sendmany(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - string strAccount = AccountFromValue(params[0]); + if (!params[0].isNull() && !params[0].get_str().empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\""); + } UniValue sendTo = params[1].get_obj(); int nMinDepth = 1; if (params.size() > 2) nMinDepth = params[2].getInt(); CWalletTx wtx; - wtx.strFromAccount = strAccount; if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) wtx.mapValue["comment"] = params[3].get_str(); @@ -1064,9 +619,9 @@ UniValue sendmany(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); // Check funds - CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); - if (totalAmount > nBalance) - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + if (totalAmount > pwalletMain->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth)) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds"); + } // Send CReserveKey keyChange(pwalletMain); @@ -1092,10 +647,9 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) if (fHelp || params.size() < 2 || params.size() > 3) { - string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" + string msg = "addmultisigaddress nrequired [\"key\",...] ( \"\" )\n" "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" "Each key is a BitcoinZ address or hex-encoded public key.\n" - "If 'account' is specified (DEPRECATED), assign address to that account.\n" "\nArguments:\n" "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" @@ -1104,7 +658,7 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) " \"address\" (string) BitcoinZ address or hex-encoded public key\n" " ...,\n" " ]\n" - "3. \"account\" (string, optional) DEPRECATED. If provided, MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" + "3. (dummy) (string, optional) DEPRECATED. If provided, MUST be set to the empty string \"\"." "\nResult:\n" "\"address\" (string) A BitcoinZ address associated with the keys.\n" @@ -1118,18 +672,21 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) throw runtime_error(msg); } - LOCK2(cs_main, pwalletMain->cs_wallet); + const UniValue& dummy_value = params[2]; + if (!dummy_value.isNull() && dummy_value.get_str() != "") { + throw JSONRPCError(RPC_INVALID_PARAMETER, "dummy argument must be excluded or set to \"\"."); + } - string strAccount; - if (params.size() > 2) - strAccount = AccountFromValue(params[2]); + LOCK2(cs_main, pwalletMain->cs_wallet); // Construct using pay-to-script-hash: CScript inner = _createmultisig_redeemScript(params); CScriptID innerID(inner); pwalletMain->AddCScript(inner); - pwalletMain->SetAddressBook(innerID, strAccount, "send"); + std::string dummy_account; + pwalletMain->SetAddressBook(innerID, dummy_account, "send"); + return EncodeDestination(innerID); } @@ -1148,7 +705,7 @@ struct tallyitem } }; -UniValue ListReceived(const UniValue& params, bool fByAccounts) +UniValue ListReceived(const UniValue& params) { // Minimum confirmations int nMinDepth = 1; @@ -1198,10 +755,8 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) // Reply UniValue ret(UniValue::VARR); - std::map mapAccountTally; for (const std::pair& item : pwalletMain->mapAddressBook) { const CTxDestination& dest = item.first; - const std::string& strAccount = item.second.name; std::map::iterator it = mapTally.find(dest); if (it == mapTally.end() && !fIncludeEmpty) continue; @@ -1216,49 +771,23 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) fIsWatchonly = (*it).second.fIsWatchonly; } - if (fByAccounts) - { - tallyitem& item = mapAccountTally[strAccount]; - item.nAmount += nAmount; - item.nConf = min(item.nConf, nConf); - item.fIsWatchonly = fIsWatchonly; - } - else + UniValue obj(UniValue::VOBJ); + if(fIsWatchonly) + obj.pushKV("involvesWatchonly", true); + obj.pushKV("address", EncodeDestination(dest)); + obj.pushKV("amount", ValueFromAmount(nAmount)); + obj.pushKV("amountZat", nAmount); + obj.pushKV("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf)); + UniValue transactions(UniValue::VARR); + if (it != mapTally.end()) { - UniValue obj(UniValue::VOBJ); - if(fIsWatchonly) - obj.pushKV("involvesWatchonly", true); - obj.pushKV("address", EncodeDestination(dest)); - obj.pushKV("account", strAccount); - obj.pushKV("amount", ValueFromAmount(nAmount)); - obj.pushKV("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf)); - UniValue transactions(UniValue::VARR); - if (it != mapTally.end()) + for (const uint256& item : (*it).second.txids) { - for (const uint256& item : (*it).second.txids) - { - transactions.push_back(item.GetHex()); - } + transactions.push_back(item.GetHex()); } - obj.pushKV("txids", transactions); - ret.push_back(obj); - } - } - - if (fByAccounts) - { - for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) - { - CAmount nAmount = (*it).second.nAmount; - int nConf = (*it).second.nConf; - UniValue obj(UniValue::VOBJ); - if((*it).second.fIsWatchonly) - obj.pushKV("involvesWatchonly", true); - obj.pushKV("account", (*it).first); - obj.pushKV("amount", ValueFromAmount(nAmount)); - obj.pushKV("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf)); - ret.push_back(obj); } + obj.pushKV("txids", transactions); + ret.push_back(obj); } return ret; @@ -1283,7 +812,6 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) " {\n" " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" " \"address\" : \"receivingaddress\", (string) The receiving address\n" - " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" " }\n" @@ -1298,43 +826,7 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); - return ListReceived(params, false); -} - -UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() > 3) - throw runtime_error( - "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" - "\nDEPRECATED. List balances by account.\n" - "\nArguments:\n" - "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" - "2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n" - "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" - - "\nResult:\n" - "[\n" - " {\n" - " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" - " \"account\" : \"accountname\", (string) The account name of the receiving account\n" - " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n" - " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" - " }\n" - " ,...\n" - "]\n" - - "\nExamples:\n" - + HelpExampleCli("listreceivedbyaccount", "") - + HelpExampleCli("listreceivedbyaccount", "6 true") - + HelpExampleRpc("listreceivedbyaccount", "6, true, true") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - return ListReceived(params, true); + return ListReceived(params); } static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) @@ -1344,27 +836,34 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) } } -void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) +/** + * List transactions based on the given criteria. + * + * @param wtx The wallet transaction. + * @param nMinDepth The minimum confirmation depth. + * @param fLong Whether to include the JSON version of the transaction. + * @param ret The UniValue into which the result is stored. + * @param filter The "is mine" filter flags. + */ +void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) { CAmount nFee; - string strSentAccount; - list listReceived; - list listSent; + std::list listReceived; + std::list listSent; - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); + wtx.GetAmounts(listReceived, listSent, nFee, filter); - bool fAllAccounts = (strAccount == string("*")); bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); // Sent - if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + if ((!listSent.empty() || nFee != 0)) { for (const COutputEntry& s : listSent) { UniValue entry(UniValue::VOBJ); - if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) + if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); - entry.pushKV("account", strSentAccount); + } MaybePushAddress(entry, s.destination); entry.pushKV("category", "send"); entry.pushKV("amount", ValueFromAmount(-s.amount)); @@ -1382,57 +881,39 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe { for (const COutputEntry& r : listReceived) { - string account; - if (pwalletMain->mapAddressBook.count(r.destination)) - account = pwalletMain->mapAddressBook[r.destination].name; - if (fAllAccounts || (account == strAccount)) + std::string label; + if (pwalletMain->mapAddressBook.count(r.destination)) { + label = pwalletMain->mapAddressBook[r.destination].name; + } + UniValue entry(UniValue::VOBJ); + if (involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) { + entry.pushKV("involvesWatchonly", true); + } + MaybePushAddress(entry, r.destination); + if (wtx.IsCoinBase()) { - UniValue entry(UniValue::VOBJ); - if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) - entry.pushKV("involvesWatchonly", true); - entry.pushKV("account", account); - MaybePushAddress(entry, r.destination); - if (wtx.IsCoinBase()) - { - if (wtx.GetDepthInMainChain() < 1) - entry.pushKV("category", "orphan"); - else if (wtx.GetBlocksToMaturity() > 0) - entry.pushKV("category", "immature"); - else - entry.pushKV("category", "generate"); - } + if (wtx.GetDepthInMainChain() < 1) + entry.pushKV("category", "orphan"); + else if (wtx.GetBlocksToMaturity() > 0) + entry.pushKV("category", "immature"); else - { - entry.pushKV("category", "receive"); - } - entry.pushKV("amount", ValueFromAmount(r.amount)); - entry.pushKV("vout", r.vout); - if (fLong) - WalletTxToJSON(wtx, entry); - entry.pushKV("size", static_cast(GetSerializeSize(static_cast(wtx), SER_NETWORK, PROTOCOL_VERSION))); - ret.push_back(entry); + entry.pushKV("category", "generate"); + } + else + { + entry.pushKV("category", "receive"); } + entry.pushKV("amount", ValueFromAmount(r.amount)); + entry.pushKV("amountZat", r.amount); + entry.pushKV("vout", r.vout); + if (fLong) + WalletTxToJSON(wtx, entry); + entry.pushKV("size", static_cast(GetSerializeSize(static_cast(wtx), SER_NETWORK, PROTOCOL_VERSION))); + ret.push_back(entry); } } } -void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, UniValue& ret) -{ - bool fAllAccounts = (strAccount == string("*")); - - if (fAllAccounts || acentry.strAccount == strAccount) - { - UniValue entry(UniValue::VOBJ); - entry.pushKV("account", acentry.strAccount); - entry.pushKV("category", "move"); - entry.pushKV("time", acentry.nTime); - entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit)); - entry.pushKV("otheraccount", acentry.strOtherAccount); - entry.pushKV("comment", acentry.strComment); - ret.push_back(entry); - } -} - UniValue listtransactions(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -1440,24 +921,20 @@ UniValue listtransactions(const UniValue& params, bool fHelp) if (fHelp || params.size() > 4) throw runtime_error( - "listtransactions ( \"account\" count from includeWatchonly)\n" - "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" + "listtransactions ( \"dummy\" count from includeWatchonly)\n" + "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n" "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" + "1. (dummy) (string, optional) If set, should be \"*\" for backwards compatibility.\n" "2. count (numeric, optional, default=10) The number of transactions to return\n" "3. from (numeric, optional, default=0) The number of transactions to skip\n" "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" "\nResult:\n" "[\n" " {\n" - " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n" - " It will be \"\" for the default account.\n" " \"address\":\"address\", (string) The BitcoinZ address of the transaction. Not present for \n" " move transactions (category = move).\n" - " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" - " transaction between accounts, and not associated with an address,\n" - " transaction id or block. 'send' and 'receive' transactions are \n" - " associated with an address, transaction id and block details\n" + " \"category\":\"send|receive\", (string) The transaction category. 'send' and 'receive' transactions are \n" + " associated with an address, transaction id and block details\n" " \"status\" : \"mined|waiting|expiringsoon|expired\", (string) The transaction status, can be 'mined', 'waiting', 'expiringsoon' \n" " or 'expired'. Available for 'send' and 'receive' category of transactions.\n" " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" @@ -1477,9 +954,6 @@ UniValue listtransactions(const UniValue& params, bool fHelp) " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" " for 'send' and 'receive' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" - " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n" - " from (for receiving funds, positive amounts), or went to (for sending funds,\n" - " negative amounts).\n" " \"size\": n, (numeric) Transaction size in bytes\n" " }\n" "]\n" @@ -1493,11 +967,10 @@ UniValue listtransactions(const UniValue& params, bool fHelp) + HelpExampleRpc("listtransactions", "\"*\", 20, 100") ); - LOCK2(cs_main, pwalletMain->cs_wallet); + if (!params[0].isNull() && params[0].get_str() != "*") { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\""); + } - string strAccount = "*"; - if (params.size() > 0) - strAccount = params[0].get_str(); int nCount = 10; if (params.size() > 1) nCount = params[1].getInt(); @@ -1516,21 +989,19 @@ UniValue listtransactions(const UniValue& params, bool fHelp) UniValue ret(UniValue::VARR); - std::list acentries; - CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); - - // iterate backwards until we have nCount items to return: - for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx != 0) - ListTransactions(*pwtx, strAccount, 0, true, ret, filter); - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != 0) - AcentryToJSON(*pacentry, strAccount, ret); + LOCK2(cs_main, pwalletMain->cs_wallet); + const CWallet::TxItems & txOrdered = pwalletMain->wtxOrdered; - if ((int)ret.size() >= (nCount+nFrom)) break; + // iterate backwards until we have nCount items to return: + for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second; + ListTransactions(*pwtx, 0, true, ret, filter); + if ((int)ret.size() >= (nCount+nFrom)) break; + } } + // ret is newest to oldest if (nFrom > (int)ret.size()) @@ -1557,86 +1028,6 @@ UniValue listtransactions(const UniValue& params, bool fHelp) return ret; } -UniValue listaccounts(const UniValue& params, bool fHelp) -{ - if (!EnsureWalletIsAvailable(fHelp)) - return NullUniValue; - - if (fHelp || params.size() > 2) - throw runtime_error( - "listaccounts ( minconf includeWatchonly)\n" - "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" - "\nArguments:\n" - "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" - "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" - "\nResult:\n" - "{ (json object where keys are account names, and values are numeric balances\n" - " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" - " ...\n" - "}\n" - "\nExamples:\n" - "\nList account balances where there at least 1 confirmation\n" - + HelpExampleCli("listaccounts", "") + - "\nList account balances including zero confirmation transactions\n" - + HelpExampleCli("listaccounts", "0") + - "\nList account balances for 6 or more confirmations\n" - + HelpExampleCli("listaccounts", "6") + - "\nAs json rpc call\n" - + HelpExampleRpc("listaccounts", "6") - ); - - LOCK2(cs_main, pwalletMain->cs_wallet); - - int nMinDepth = 1; - if (params.size() > 0) - nMinDepth = params[0].getInt(); - isminefilter includeWatchonly = ISMINE_SPENDABLE; - if(params.size() > 1) - if(params[1].get_bool()) - includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; - - map mapAccountBalances; - for (const std::pair& entry : pwalletMain->mapAddressBook) { - if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me - mapAccountBalances[entry.second.name] = 0; - } - - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - CAmount nFee; - string strSentAccount; - list listReceived; - list listSent; - int nDepth = wtx.GetDepthInMainChain(); - if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) - continue; - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); - mapAccountBalances[strSentAccount] -= nFee; - for (const COutputEntry& s : listSent) - mapAccountBalances[strSentAccount] -= s.amount; - if (nDepth >= nMinDepth) - { - for (const COutputEntry& r : listReceived) - if (pwalletMain->mapAddressBook.count(r.destination)) - mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; - else - mapAccountBalances[""] += r.amount; - } - } - - list acentries; - CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); - for (const CAccountingEntry& entry : acentries) - mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - - UniValue ret(UniValue::VOBJ); - for (const std::pair& accountBalance : mapAccountBalances) { - ret.pushKV(accountBalance.first, ValueFromAmount(accountBalance.second)); - } - return ret; -} - UniValue listsinceblock(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -1653,7 +1044,6 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"transactions\": [\n" - " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" " \"address\":\"address\", (string) The BitcoinZ address of the transaction. Not present for move transactions (category = move).\n" " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" " \"status\" : \"mined|waiting|expiringsoon|expired\", (string) The transaction status, can be 'mined', 'waiting', 'expiringsoon' \n" @@ -1712,12 +1102,12 @@ UniValue listsinceblock(const UniValue& params, bool fHelp) UniValue transactions(UniValue::VARR); - for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) - { - CWalletTx tx = (*it).second; + for (const std::pair& pairWtx : pwalletMain->mapWallet) { + CWalletTx tx = pairWtx.second; - if (depth == -1 || tx.GetDepthInMainChain() < depth) - ListTransactions(tx, "*", 0, true, transactions, filter); + if (depth == -1 || tx.GetDepthInMainChain() < depth) { + ListTransactions(tx, 0, true, transactions, filter); + } } CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; @@ -1755,7 +1145,6 @@ UniValue gettransaction(const UniValue& params, bool fHelp) " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" " \"details\" : [\n" " {\n" - " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" " \"address\" : \"address\", (string) The BitcoinZ address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" " \"amount\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" @@ -1810,7 +1199,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp) WalletTxToJSON(wtx, entry); UniValue details(UniValue::VARR); - ListTransactions(wtx, "*", 0, false, details, filter); + ListTransactions(wtx, 0, false, details, filter); entry.pushKV("details", details); string strHex = EncodeHexTx(static_cast(wtx)); @@ -2379,7 +1768,6 @@ UniValue listunspent(const UniValue& params, bool fHelp) " \"vout\" : n, (numeric) the vout value\n" " \"generated\" : true|false (boolean) true if txout is a coinbase transaction output\n" " \"address\" : \"address\", (string) the BitcoinZ address\n" - " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" @@ -2444,9 +1832,6 @@ UniValue listunspent(const UniValue& params, bool fHelp) if (fValidAddress) { entry.pushKV("address", EncodeDestination(address)); - if (pwalletMain->mapAddressBook.count(address)) - entry.pushKV("account", pwalletMain->mapAddressBook[address].name); - if (scriptPubKey.IsPayToScriptHash()) { const CScriptID& hash = std::get(address); CScript redeemScript; @@ -4905,7 +4290,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) return o; } - UniValue z_listoperationids(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -4979,13 +4363,9 @@ static const CRPCCommand commands[] = { "wallet", "dumpprivkey", &dumpprivkey, true }, { "wallet", "dumpwallet", &dumpwallet, true }, { "wallet", "encryptwallet", &encryptwallet, true }, - { "wallet", "getaccountaddress", &getaccountaddress, true }, - { "wallet", "getaccount", &getaccount, true }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true }, { "wallet", "getbalance", &getbalance, false }, { "wallet", "getnewaddress", &getnewaddress, true }, { "wallet", "getrawchangeaddress", &getrawchangeaddress, true }, - { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false }, { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false }, { "wallet", "gettransaction", &gettransaction, false }, { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false }, @@ -4995,20 +4375,15 @@ static const CRPCCommand commands[] = { "wallet", "importaddress", &importaddress, true }, { "wallet", "importpubkey", &importpubkey, true }, { "wallet", "keypoolrefill", &keypoolrefill, true }, - { "wallet", "listaccounts", &listaccounts, false }, { "wallet", "listaddressgroupings", &listaddressgroupings, false }, { "wallet", "listlockunspent", &listlockunspent, false }, - { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false }, { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false }, { "wallet", "listsinceblock", &listsinceblock, false }, { "wallet", "listtransactions", &listtransactions, false }, { "wallet", "listunspent", &listunspent, false }, { "wallet", "lockunspent", &lockunspent, true }, - { "wallet", "move", &movecmd, false }, - { "wallet", "sendfrom", &sendfrom, false }, { "wallet", "sendmany", &sendmany, false }, { "wallet", "sendtoaddress", &sendtoaddress, false }, - { "wallet", "setaccount", &setaccount, true }, { "wallet", "settxfee", &settxfee, true }, { "wallet", "signmessage", &signmessage, true }, { "wallet", "walletlock", &walletlock, true }, diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp deleted file mode 100644 index 92bd560fa..000000000 --- a/src/wallet/test/accounting_tests.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2012-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#include "wallet/wallet.h" -#include "wallet/walletdb.h" - -#include "wallet/test/wallet_test_fixture.h" - -#include - -#include - -extern CWallet* pwalletMain; - -BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup) - -static void -GetResults(CWalletDB& walletdb, std::map& results) -{ - std::list aes; - - results.clear(); - BOOST_CHECK(walletdb.ReorderTransactions(pwalletMain) == DB_LOAD_OK); - walletdb.ListAccountCreditDebit("", aes); - for (CAccountingEntry& ae : aes) - { - results[ae.nOrderPos] = ae; - } -} - -BOOST_AUTO_TEST_CASE(acc_orderupgrade) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - std::vector vpwtx; - CWalletTx wtx; - CAccountingEntry ae; - std::map results; - - LOCK(pwalletMain->cs_wallet); - - ae.strAccount = ""; - ae.nCreditDebit = 1; - ae.nTime = 1333333333; - ae.strOtherAccount = "b"; - ae.strComment = ""; - walletdb.WriteAccountingEntry(ae); - - wtx.mapValue["comment"] = "z"; - pwalletMain->AddToWallet(wtx, false, &walletdb); - vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); - vpwtx[0]->nTimeReceived = (unsigned int)1333333335; - vpwtx[0]->nOrderPos = -1; - - ae.nTime = 1333333336; - ae.strOtherAccount = "c"; - walletdb.WriteAccountingEntry(ae); - - GetResults(walletdb, results); - - BOOST_CHECK(pwalletMain->nOrderPosNext == 3); - BOOST_CHECK(2 == results.size()); - BOOST_CHECK(results[0].nTime == 1333333333); - BOOST_CHECK(results[0].strComment.empty()); - BOOST_CHECK(1 == vpwtx[0]->nOrderPos); - BOOST_CHECK(results[2].nTime == 1333333336); - BOOST_CHECK(results[2].strOtherAccount == "c"); - - - ae.nTime = 1333333330; - ae.strOtherAccount = "d"; - ae.nOrderPos = pwalletMain->IncOrderPosNext(); - walletdb.WriteAccountingEntry(ae); - - GetResults(walletdb, results); - - BOOST_CHECK(results.size() == 3); - BOOST_CHECK(pwalletMain->nOrderPosNext == 4); - BOOST_CHECK(results[0].nTime == 1333333333); - BOOST_CHECK(1 == vpwtx[0]->nOrderPos); - BOOST_CHECK(results[2].nTime == 1333333336); - BOOST_CHECK(results[3].nTime == 1333333330); - BOOST_CHECK(results[3].strComment.empty()); - - - wtx.mapValue["comment"] = "y"; - { - CMutableTransaction tx(wtx); - --tx.nLockTime; // Just to change the hash :) - *static_cast(&wtx) = CTransaction(tx); - } - pwalletMain->AddToWallet(wtx, false, &walletdb); - vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); - vpwtx[1]->nTimeReceived = (unsigned int)1333333336; - - wtx.mapValue["comment"] = "x"; - { - CMutableTransaction tx(wtx); - --tx.nLockTime; // Just to change the hash :) - *static_cast(&wtx) = CTransaction(tx); - } - pwalletMain->AddToWallet(wtx, false, &walletdb); - vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); - vpwtx[2]->nTimeReceived = (unsigned int)1333333329; - vpwtx[2]->nOrderPos = -1; - - GetResults(walletdb, results); - - BOOST_CHECK(results.size() == 3); - BOOST_CHECK(pwalletMain->nOrderPosNext == 6); - BOOST_CHECK(0 == vpwtx[2]->nOrderPos); - BOOST_CHECK(results[1].nTime == 1333333333); - BOOST_CHECK(2 == vpwtx[0]->nOrderPos); - BOOST_CHECK(results[3].nTime == 1333333336); - BOOST_CHECK(results[4].nTime == 1333333330); - BOOST_CHECK(results[4].strComment.empty()); - BOOST_CHECK(5 == vpwtx[1]->nOrderPos); - - - ae.nTime = 1333333334; - ae.strOtherAccount = "e"; - ae.nOrderPos = -1; - walletdb.WriteAccountingEntry(ae); - - GetResults(walletdb, results); - - BOOST_CHECK(results.size() == 4); - BOOST_CHECK(pwalletMain->nOrderPosNext == 7); - BOOST_CHECK(0 == vpwtx[2]->nOrderPos); - BOOST_CHECK(results[1].nTime == 1333333333); - BOOST_CHECK(2 == vpwtx[0]->nOrderPos); - BOOST_CHECK(results[3].nTime == 1333333336); - BOOST_CHECK(results[3].strComment.empty()); - BOOST_CHECK(results[4].nTime == 1333333330); - BOOST_CHECK(results[4].strComment.empty()); - BOOST_CHECK(results[5].nTime == 1333333334); - BOOST_CHECK(6 == vpwtx[1]->nOrderPos); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 36aaf8de5..3d0ecb3a2 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -108,32 +108,15 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) CPubKey demoPubkey = pwalletMain->GenerateNewKey(); CTxDestination demoAddress(CTxDestination(demoPubkey.GetID())); UniValue retValue; - string strAccount = ""; string strPurpose = "receive"; - BOOST_CHECK_NO_THROW({ /*Initialize Wallet with an account */ + BOOST_CHECK_NO_THROW({ /*Initialize Wallet with the demo pubkey*/ CWalletDB walletdb(pwalletMain->strWalletFile); - CAccount account; - account.vchPubKey = demoPubkey; - pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, strPurpose); - walletdb.WriteAccount(strAccount, account); + pwalletMain->SetAddressBook(demoPubkey.GetID(), "", strPurpose); }); CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey(); CTxDestination setaccountDemoAddress(CTxDestination(setaccountDemoPubkey.GetID())); - /********************************* - * setaccount - *********************************/ - BOOST_CHECK_NO_THROW(CallRPC("setaccount " + EncodeDestination(setaccountDemoAddress) + " \"\"")); - /* Accounts are disabled */ - BOOST_CHECK_THROW(CallRPC("setaccount " + EncodeDestination(setaccountDemoAddress) + " nullaccount"), runtime_error); - /* t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV is not owned by the test wallet. */ - BOOST_CHECK_THROW(CallRPC("setaccount t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV nullaccount"), runtime_error); - BOOST_CHECK_THROW(CallRPC("setaccount"), runtime_error); - /* t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1Gpg (34 chars) is an illegal address (should be 35 chars) */ - BOOST_CHECK_THROW(CallRPC("setaccount t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1Gpg nullaccount"), runtime_error); - - /********************************* * getbalance *********************************/ @@ -161,16 +144,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); - /********************************* - * listreceivedbyaccount - *********************************/ - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); - BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); - BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); - /********************************* * listsinceblock *********************************/ @@ -180,9 +153,9 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) * listtransactions *********************************/ BOOST_CHECK_NO_THROW(CallRPC("listtransactions")); - BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress))); - BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " 20")); - BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " 20 0")); + BOOST_CHECK_NO_THROW(CallRPC("listtransactions *")); + BOOST_CHECK_NO_THROW(CallRPC("listtransactions * 20")); + BOOST_CHECK_NO_THROW(CallRPC("listtransactions * 20 0")); BOOST_CHECK_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " not_int"), runtime_error); /********************************* @@ -190,11 +163,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) *********************************/ BOOST_CHECK_NO_THROW(CallRPC("listlockunspent")); - /********************************* - * listaccounts - *********************************/ - BOOST_CHECK_NO_THROW(CallRPC("listaccounts")); - /********************************* * listaddressgroupings *********************************/ @@ -209,24 +177,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) * getnewaddress *********************************/ BOOST_CHECK_NO_THROW(CallRPC("getnewaddress")); - BOOST_CHECK_NO_THROW(CallRPC("getnewaddress \"\"")); - /* Accounts are deprecated */ - BOOST_CHECK_THROW(CallRPC("getnewaddress getnewaddress_demoaccount"), runtime_error); - - /********************************* - * getaccountaddress - *********************************/ - BOOST_CHECK_NO_THROW(CallRPC("getaccountaddress \"\"")); - /* Accounts are deprecated */ - BOOST_CHECK_THROW(CallRPC("getaccountaddress accountThatDoesntExists"), runtime_error); - BOOST_CHECK_NO_THROW(retValue = CallRPC("getaccountaddress " + strAccount)); - BOOST_CHECK(DecodeDestination(retValue.get_str()) == demoAddress); - - /********************************* - * getaccount - *********************************/ - BOOST_CHECK_THROW(CallRPC("getaccount"), runtime_error); - BOOST_CHECK_NO_THROW(CallRPC("getaccount " + EncodeDestination(demoAddress))); /********************************* * signmessage + verifymessage @@ -248,19 +198,6 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) /* Correct address, message and signature*/ BOOST_CHECK(CallRPC("verifymessage " + EncodeDestination(demoAddress) + " " + retValue.get_str() + " mymessage").get_bool() == true); - /********************************* - * getaddressesbyaccount - *********************************/ - BOOST_CHECK_THROW(CallRPC("getaddressesbyaccount"), runtime_error); - BOOST_CHECK_NO_THROW(retValue = CallRPC("getaddressesbyaccount " + strAccount)); - UniValue arr = retValue.get_array(); - BOOST_CHECK_EQUAL(4, arr.size()); - bool notFound = true; - for (auto a : arr.getValues()) { - notFound &= DecodeDestination(a.get_str()) != demoAddress; - } - BOOST_CHECK(!notFound); - /********************************* * fundrawtransaction *********************************/ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1d34e8e76..cdc7804d8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -982,7 +982,6 @@ void CWallet::SyncMetaData(pair::iterator, typename TxSpe // nTimeReceived not copied on purpose copyTo->nTimeSmart = copyFrom->nTimeSmart; copyTo->fFromMe = copyFrom->fFromMe; - copyTo->strFromAccount = copyFrom->strFromAccount; // nOrderPos not copied on purpose // cached members not copied on purpose } @@ -1573,6 +1572,63 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) return true; } +DBErrors CWallet::ReorderTransactions() +{ + LOCK(cs_wallet); + CWalletDB walletdb(strWalletFile); + + // Old wallets didn't have any defined order for transactions + // Probably a bad idea to change the output of this + + // First: get all CWalletTx into a sorted-by-time multimap. + typedef std::multimap TxItems; + TxItems txByTime; + + for (auto &entry : mapWallet) + { + CWalletTx *wtx = &entry.second; + txByTime.insert(std::make_pair(wtx->nTimeReceived, wtx)); + } + + nOrderPosNext = 0; + std::vector nOrderPosOffsets; + for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) + { + CWalletTx *const pwtx = (*it).second; + int64_t& nOrderPos = pwtx->nOrderPos; + + if (nOrderPos == -1) + { + nOrderPos = nOrderPosNext++; + nOrderPosOffsets.push_back(nOrderPos); + + if (!walletdb.WriteTx(*pwtx)) + return DB_LOAD_FAIL; + } + else + { + int64_t nOrderPosOff = 0; + for(const int64_t& nOffsetStart : nOrderPosOffsets) + { + if (nOrderPos >= nOffsetStart) + ++nOrderPosOff; + } + nOrderPos += nOrderPosOff; + nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); + + if (!nOrderPosOff) + continue; + + // Since we're changing the order, write it back + if (!walletdb.WriteTx(*pwtx)) + return DB_LOAD_FAIL; + } + } + walletdb.WriteOrderPosNext(nOrderPosNext); + + return DB_LOAD_OK; +} + int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { AssertLockHeld(cs_wallet); // nOrderPosNext @@ -1585,31 +1641,6 @@ int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) return nRet; } -CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, std::string strAccount) -{ - AssertLockHeld(cs_wallet); // mapWallet - CWalletDB walletdb(strWalletFile); - - // First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap. - TxItems txOrdered; - - // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry - // would make this much faster for applications that do this a lot. - for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); - } - acentries.clear(); - walletdb.ListAccountCreditDebit(strAccount, acentries); - for (CAccountingEntry& entry : acentries) - { - txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); - } - - return txOrdered; -} - void CWallet::MarkDirty() { { @@ -1746,7 +1777,9 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD if (fFromLoadWallet) { mapWallet[hash] = wtxIn; - mapWallet[hash].BindWallet(this); + CWalletTx& wtx = mapWallet[hash]; + wtx.BindWallet(this); + wtxOrdered.insert(make_pair(wtx.nOrderPos, &wtx)); UpdateNullifierNoteMapWithTx(mapWallet[hash]); AddToSpends(hash); } @@ -1763,6 +1796,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD { wtx.nTimeReceived = GetAdjustedTime(); wtx.nOrderPos = IncOrderPosNext(pwalletdb); + wtxOrdered.insert(make_pair(wtx.nOrderPos, &wtx)); wtx.nTimeSmart = wtx.nTimeReceived; if (!wtxIn.hashBlock.IsNull()) @@ -1774,23 +1808,16 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD { // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future int64_t latestTolerated = latestNow + 300; - std::list acentries; - TxItems txOrdered = OrderedTxItems(acentries); - for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + const TxItems & txOrdered = wtxOrdered; + for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { - CWalletTx *const pwtx = (*it).second.first; + CWalletTx *const pwtx = (*it).second; if (pwtx == &wtx) continue; - CAccountingEntry *const pacentry = (*it).second.second; int64_t nSmartTime; - if (pwtx) - { - nSmartTime = pwtx->nTimeSmart; - if (!nSmartTime) - nSmartTime = pwtx->nTimeReceived; - } - else - nSmartTime = pacentry->nTime; + nSmartTime = pwtx->nTimeSmart; + if (!nSmartTime) + nSmartTime = pwtx->nTimeReceived; if (nSmartTime <= latestTolerated) { latestEntry = nSmartTime; @@ -1841,7 +1868,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD // Write to disk if (fInsertedNew || fUpdated) - if (!wtx.WriteToDisk(pwalletdb)) + if (!pwalletdb->WriteTx(wtx)) return false; // Break debit/credit balance caches: @@ -2632,13 +2659,12 @@ int CWalletTx::GetRequestCount() const } // GetAmounts will determine the transparent debits and credits for a given wallet tx. -void CWalletTx::GetAmounts(list& listReceived, - list& listSent, CAmount& nFee, string& strSentAccount, const isminefilter& filter) const +void CWalletTx::GetAmounts(std::list& listReceived, + std::list& listSent, CAmount& nFee, const isminefilter& filter) const { nFee = 0; listReceived.clear(); listSent.clear(); - strSentAccount = strFromAccount; // Is this tx sent/signed by me? CAmount nDebit = GetDebit(filter); @@ -2747,47 +2773,6 @@ void CWalletTx::GetAmounts(list& listReceived, } -void CWalletTx::GetAccountAmounts(const string& strAccount, CAmount& nReceived, - CAmount& nSent, CAmount& nFee, const isminefilter& filter) const -{ - nReceived = nSent = nFee = 0; - - CAmount allFee; - string strSentAccount; - list listReceived; - list listSent; - GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); - - if (strAccount == strSentAccount) - { - for (const COutputEntry& s : listSent) - nSent += s.amount; - nFee = allFee; - } - { - LOCK(pwallet->cs_wallet); - for (const COutputEntry& r : listReceived) - { - if (pwallet->mapAddressBook.count(r.destination)) - { - map::const_iterator mi = pwallet->mapAddressBook.find(r.destination); - if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount) - nReceived += r.amount; - } - else if (strAccount.empty()) - { - nReceived += r.amount; - } - } - } -} - - -bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb) -{ - return pwalletdb->WriteTx(GetHash(), *this); -} - void CWallet::WitnessNoteCommitment(std::vector commitments, std::vector>& witnesses, uint256 &final_anchor) @@ -2925,7 +2910,7 @@ std::optional CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, for (auto hash : myTxHashes) { CWalletTx wtx = mapWallet[hash]; if (!wtx.mapSaplingNoteData.empty()) { - if (!wtx.WriteToDisk(&walletdb)) { + if (!walletdb.WriteTx(wtx)) { LogPrintf("Rescanning... WriteToDisk failed to update Sapling note data for: %s\n", hash.ToString()); } } @@ -3072,17 +3057,29 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const return 0; } -CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const +CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const { - if (pwallet == 0) + if (pwallet == nullptr) return 0; // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; - if (fUseCache && fAvailableCreditCached) - return nAvailableCreditCached; + CAmount* cache = nullptr; + bool* cache_used = nullptr; + + if (filter == ISMINE_SPENDABLE) { + cache = &nAvailableCreditCached; + cache_used = &fAvailableCreditCached; + } else if (filter == ISMINE_WATCH_ONLY) { + cache = &nAvailableWatchCreditCached; + cache_used = &fAvailableWatchCreditCached; + } + + if (fUseCache && cache_used && *cache_used) { + return *cache; + } CAmount nCredit = 0; uint256 hashTx = GetHash(); @@ -3091,18 +3088,20 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const if (!pwallet->IsSpent(hashTx, i)) { const CTxOut &txout = vout[i]; - nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + nCredit += pwallet->GetCredit(txout, filter); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } } - nAvailableCreditCached = nCredit; - fAvailableCreditCached = true; + if (cache) { + *cache = nCredit; + *cache_used = true; + } return nCredit; } -CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const +CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const { if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) { @@ -3116,35 +3115,6 @@ CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const return 0; } -CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const -{ - if (pwallet == 0) - return 0; - - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) - return 0; - - if (fUseCache && fAvailableWatchCreditCached) - return nAvailableWatchCreditCached; - - CAmount nCredit = 0; - for (unsigned int i = 0; i < vout.size(); i++) - { - if (!pwallet->IsSpent(GetHash(), i)) - { - const CTxOut &txout = vout[i]; - nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); - if (!MoneyRange(nCredit)) - throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); - } - } - - nAvailableWatchCreditCached = nCredit; - fAvailableWatchCreditCached = true; - return nCredit; -} - CAmount CWalletTx::GetChange() const { if (fChangeCached) @@ -3239,16 +3209,17 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime) */ -CAmount CWallet::GetBalance() const +CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const { CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsTrusted()) - nTotal += pcoin->GetAvailableCredit(); + const CWalletTx* pcoin = &entry.second; + if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) { + nTotal += pcoin->GetAvailableCredit(true, filter); + } } } @@ -3260,9 +3231,9 @@ CAmount CWallet::GetUnconfirmedBalance() const CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &entry.second; if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) nTotal += pcoin->GetAvailableCredit(); } @@ -3275,58 +3246,81 @@ CAmount CWallet::GetImmatureBalance() const CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; + const CWalletTx* pcoin = &entry.second; nTotal += pcoin->GetImmatureCredit(); } } return nTotal; } -CAmount CWallet::GetWatchOnlyBalance() const +CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const { CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsTrusted()) - nTotal += pcoin->GetAvailableWatchOnlyCredit(); + const CWalletTx* pcoin = &entry.second; + if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY); } } - return nTotal; } -CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const +CAmount CWallet::GetImmatureWatchOnlyBalance() const { CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + for (const auto& entry : mapWallet) { - const CWalletTx* pcoin = &(*it).second; - if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) - nTotal += pcoin->GetAvailableWatchOnlyCredit(); + const CWalletTx* pcoin = &entry.second; + nTotal += pcoin->GetImmatureWatchOnlyCredit(); } } return nTotal; } -CAmount CWallet::GetImmatureWatchOnlyBalance() const +// Calculate total balance in a different way from GetBalance. The biggest +// difference is that GetBalance sums up all unspent TxOuts paying to the +// wallet, while this sums up both spent and unspent TxOuts paying to the +// wallet, and then subtracts the values of TxIns spending from the wallet. This +// also has fewer restrictions on which unconfirmed transactions are considered +// trusted. +CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const { - CAmount nTotal = 0; - { - LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; - nTotal += pcoin->GetImmatureWatchOnlyCredit(); + LOCK2(cs_main, cs_wallet); + + CAmount balance = 0; + for (const auto& entry : mapWallet) { + const CWalletTx& wtx = entry.second; + const int depth = wtx.GetDepthInMainChain(); + if (depth < 0 || !CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0) { + continue; + } + + // Loop through tx outputs and add incoming payments. For outgoing txs, + // treat change outputs specially, as part of the amount debited. + CAmount debit = wtx.GetDebit(filter); + const bool outgoing = debit > 0; + for (const CTxOut& out : wtx.vout) { + if (outgoing && IsChange(out)) { + debit -= out.nValue; + } else if (IsMine(out) & filter && depth >= minDepth) { + balance += out.nValue; + } + } + + // For outgoing txs, subtract amount debited. + if (outgoing) { + balance -= debit; } } - return nTotal; + + return balance; } void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, bool fIncludeCoinBase) const @@ -4429,20 +4423,6 @@ set< set > CWallet::GetAddressGroupings() return ret; } -std::set CWallet::GetAccountAddresses(const std::string& strAccount) const -{ - LOCK(cs_wallet); - set result; - for (const std::pair& item : mapAddressBook) - { - const CTxDestination& address = item.first; - const string& strName = item.second.name; - if (strName == strAccount) - result.insert(address); - } - return result; -} - bool CReserveKey::GetReservedKey(CPubKey& pubkey) { if (nIndex == -1) @@ -4707,8 +4687,9 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { } // Extract block timestamps for those keys - for (std::map::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) - mapKeyBirth[it->first] = it->second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off + for (const auto& entry : mapKeyFirstBlock) { + mapKeyBirth[entry.first] = entry.second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off + } } bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) @@ -4945,9 +4926,8 @@ bool CWallet::InitLoadWallet(bool clearWitnessCaches) copyTo->nTimeReceived = copyFrom->nTimeReceived; copyTo->nTimeSmart = copyFrom->nTimeSmart; copyTo->fFromMe = copyFrom->fFromMe; - copyTo->strFromAccount = copyFrom->strFromAccount; copyTo->nOrderPos = copyFrom->nOrderPos; - copyTo->WriteToDisk(&walletdb); + walletdb.WriteTx(*copyTo); } } } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4501c9be9..eb0975e58 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -438,6 +438,30 @@ class CWalletTx : public CMerkleTx const CWallet* pwallet; public: + /** + * Key/value map with information about the transaction. + * + * The following keys can be read and written through the map and are + * serialized in the wallet database: + * + * "comment", "to" - comment strings provided to sendtoaddress, + * and sendmany wallet RPCs + * "replaces_txid" - txid (as HexStr) of transaction replaced by + * bumpfee on transaction created by bumpfee + * "replaced_by_txid" - txid (as HexStr) of transaction created by + * bumpfee on transaction replaced by bumpfee + * "from", "message" - obsolete fields that could be set in UI prior to + * 2011 (removed in commit 4d9b223) + * + * The following keys are serialized in the wallet database, but shouldn't + * be read or written through the map (they will be temporarily added and + * removed from the map during serialization): + * + * "n" - serialized nOrderPos value + * "timesmart" - serialized nTimeSmart value + * "spent" - serialized vfSpent value that existed prior to + * 2014 (removed in commit 93a18a3) + */ mapValue_t mapValue; mapSproutNoteData_t mapSproutNoteData; mapSaplingNoteData_t mapSaplingNoteData; @@ -446,7 +470,6 @@ class CWalletTx : public CMerkleTx unsigned int nTimeReceived; //!< time received by this node unsigned int nTimeSmart; char fFromMe; - std::string strFromAccount; int64_t nOrderPos; //!< position in ordered transaction list // memory only @@ -500,7 +523,6 @@ class CWalletTx : public CMerkleTx nTimeReceived = 0; nTimeSmart = 0; fFromMe = false; - strFromAccount.clear(); fDebitCached = false; fCreditCached = false; fImmatureCreditCached = false; @@ -532,7 +554,7 @@ class CWalletTx : public CMerkleTx if (!ser_action.ForRead()) { - mapValue["fromaccount"] = strFromAccount; + mapValue["fromaccount"] = ""; WriteOrderPos(nOrderPos, mapValue); @@ -557,10 +579,7 @@ class CWalletTx : public CMerkleTx if (ser_action.ForRead()) { - strFromAccount = mapValue["fromaccount"]; - ReadOrderPos(nOrderPos, mapValue); - nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; } @@ -607,16 +626,12 @@ class CWalletTx : public CMerkleTx CAmount GetDebit(const isminefilter& filter) const; CAmount GetCredit(const isminefilter& filter) const; CAmount GetImmatureCredit(bool fUseCache=true) const; - CAmount GetAvailableCredit(bool fUseCache=true) const; - CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const; - CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const; + CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const; + CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const; CAmount GetChange() const; void GetAmounts(std::list& listReceived, - std::list& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const; - - void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived, - CAmount& nSent, CAmount& nFee, const isminefilter& filter) const; + std::list& listSent, CAmount& nFee, const isminefilter& filter) const; bool IsFromMe(const isminefilter& filter) const { @@ -625,8 +640,6 @@ class CWalletTx : public CMerkleTx bool IsTrusted() const; - bool WriteToDisk(CWalletDB *pwalletdb); - int64_t GetTxTime() const; int GetRequestCount() const; @@ -684,89 +697,6 @@ class CWalletKey } }; -/** - * Internal transfers. - * Database key is acentry. - */ -class CAccountingEntry -{ -public: - std::string strAccount; - CAmount nCreditDebit; - int64_t nTime; - std::string strOtherAccount; - std::string strComment; - mapValue_t mapValue; - int64_t nOrderPos; //!< position in ordered transaction list - uint64_t nEntryNo; - - CAccountingEntry() - { - SetNull(); - } - - void SetNull() - { - nCreditDebit = 0; - nTime = 0; - strAccount.clear(); - strOtherAccount.clear(); - strComment.clear(); - nOrderPos = -1; - nEntryNo = 0; - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (!(s.GetType() & SER_GETHASH)) - READWRITE(nVersion); - //! Note: strAccount is serialized as part of the key, not here. - READWRITE(nCreditDebit); - READWRITE(nTime); - READWRITE(LIMITED_STRING(strOtherAccount, 65536)); - - if (!ser_action.ForRead()) - { - WriteOrderPos(nOrderPos, mapValue); - - if (!(mapValue.empty() && _ssExtra.empty())) - { - CDataStream ss(s.GetType(), s.GetVersion()); - ss.insert(ss.begin(), '\0'); - ss << mapValue; - ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); - strComment.append(ss.str()); - } - } - - READWRITE(LIMITED_STRING(strComment, 65536)); - - size_t nSepPos = strComment.find("\0", 0, 1); - if (ser_action.ForRead()) - { - mapValue.clear(); - if (std::string::npos != nSepPos) - { - CDataStream ss(std::vector(strComment.begin() + nSepPos + 1, strComment.end()), s.GetType(), s.GetVersion()); - ss >> mapValue; - _ssExtra = std::vector(ss.begin(), ss.end()); - } - ReadOrderPos(nOrderPos, mapValue); - } - if (std::string::npos != nSepPos) - strComment.erase(nSepPos); - - mapValue.erase("n"); - } - -private: - std::vector _ssExtra; -}; - - /** * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. @@ -860,7 +790,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface // (i.e. are purely transparent), as well as shielding and unshielding // transactions in which we only have transparent addresses involved. if (!(wtx.mapSproutNoteData.empty() && wtx.mapSaplingNoteData.empty())) { - if (!walletdb.WriteTx(wtxItem.first, wtx)) { + if (!walletdb.WriteTx(wtx)) { LogPrintf("SetBestChain(): Failed to write CWalletTx, aborting atomic write\n"); walletdb.TxnAbort(); return; @@ -1015,6 +945,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapWallet; + typedef std::multimap TxItems; + TxItems wtxOrdered; + int64_t nOrderPosNext; std::map mapRequestCount; @@ -1176,15 +1109,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL); - typedef std::pair TxPair; - typedef std::multimap TxItems; - - /** - * Get the wallet's activity log - * @return multimap of ordered transactions and accounting entries - * @warning Returned pointers are *only* valid within the scope of passed acentries - */ - TxItems OrderedTxItems(std::list& acentries, std::string strAccount = ""); + DBErrors ReorderTransactions(); void MarkDirty(); bool UpdateNullifierNoteMap(); @@ -1203,12 +1128,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime); std::vector ResendWalletTransactionsBefore(int64_t nTime); - CAmount GetBalance() const; + CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const; CAmount GetUnconfirmedBalance() const; CAmount GetImmatureBalance() const; - CAmount GetWatchOnlyBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; + CAmount GetLegacyBalance(const isminefilter& filter, int minDepth) const; /** * Insert additional inputs into the transaction by @@ -1250,8 +1175,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::set< std::set > GetAddressGroupings(); std::map GetAddressBalances(); - std::set GetAccountAddresses(const std::string& strAccount) const; - std::optional GetSproutNoteNullifier( const JSDescription& jsdesc, const libzcash::SproutPaymentAddress& address, @@ -1468,37 +1391,6 @@ class CReserveKey : public CReserveScript void KeepScript() { KeepKey(); } }; - -/** - * Account information. - * Stored in wallet with key "acc"+string account name. - */ -class CAccount -{ -public: - CPubKey vchPubKey; - - CAccount() - { - SetNull(); - } - - void SetNull() - { - vchPubKey = CPubKey(); - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (!(s.GetType() & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPubKey); - } -}; - // // Shielded key and address generalizations // diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index e48280921..c06d9f5f4 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -19,6 +19,7 @@ #include #include +#include using namespace std; @@ -54,10 +55,10 @@ bool CWalletDB::ErasePurpose(const string& strPurpose) return Erase(make_pair(string("purpose"), strPurpose)); } -bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx) +bool CWalletDB::WriteTx(const CWalletTx& wtx) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("tx"), hash), wtx); + return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); } bool CWalletDB::EraseTx(uint256 hash) @@ -280,158 +281,6 @@ bool CWalletDB::WriteMinVersion(int nVersion) return Write(std::string("minversion"), nVersion); } -bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) -{ - account.SetNull(); - return Read(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) -{ - return Write(make_pair(string("acc"), strAccount), account); -} - -bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) -{ - return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); -} - -bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) -{ - return WriteAccountingEntry(++nAccountingEntryNumber, acentry); -} - -CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount) -{ - list entries; - ListAccountCreditDebit(strAccount, entries); - - CAmount nCreditDebit = 0; - for (const CAccountingEntry& entry : entries) - nCreditDebit += entry.nCreditDebit; - - return nCreditDebit; -} - -void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) -{ - bool fAllAccounts = (strAccount == "*"); - - Dbc* pcursor = GetCursor(); - if (!pcursor) - throw runtime_error("CWalletDB::ListAccountCreditDebit(): cannot create DB cursor"); - unsigned int fFlags = DB_SET_RANGE; - while (true) - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - if (fFlags == DB_SET_RANGE) - ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0))); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); - fFlags = DB_NEXT; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - throw runtime_error("CWalletDB::ListAccountCreditDebit(): error scanning DB"); - } - - // Unserialize - string strType; - ssKey >> strType; - if (strType != "acentry") - break; - CAccountingEntry acentry; - ssKey >> acentry.strAccount; - if (!fAllAccounts && acentry.strAccount != strAccount) - break; - - ssValue >> acentry; - ssKey >> acentry.nEntryNo; - entries.push_back(acentry); - } - - pcursor->close(); -} - -DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) -{ - LOCK(pwallet->cs_wallet); - // Old wallets didn't have any defined order for transactions - // Probably a bad idea to change the output of this - - // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. - typedef pair TxPair; - typedef multimap TxItems; - TxItems txByTime; - - for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) - { - CWalletTx* wtx = &((*it).second); - txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); - } - list acentries; - ListAccountCreditDebit("", acentries); - for (CAccountingEntry& entry : acentries) - { - txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); - } - - int64_t& nOrderPosNext = pwallet->nOrderPosNext; - nOrderPosNext = 0; - std::vector nOrderPosOffsets; - for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) - { - CWalletTx *const pwtx = (*it).second.first; - CAccountingEntry *const pacentry = (*it).second.second; - int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; - - if (nOrderPos == -1) - { - nOrderPos = nOrderPosNext++; - nOrderPosOffsets.push_back(nOrderPos); - - if (pwtx) - { - if (!WriteTx(pwtx->GetHash(), *pwtx)) - return DB_LOAD_FAIL; - } - else - if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) - return DB_LOAD_FAIL; - } - else - { - int64_t nOrderPosOff = 0; - for (const int64_t& nOffsetStart : nOrderPosOffsets) - { - if (nOrderPos >= nOffsetStart) - ++nOrderPosOff; - } - nOrderPos += nOrderPosOff; - nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); - - if (!nOrderPosOff) - continue; - - // Since we're changing the order, write it back - if (pwtx) - { - if (!WriteTx(pwtx->GetHash(), *pwtx)) - return DB_LOAD_FAIL; - } - else - if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) - return DB_LOAD_FAIL; - } - } - WriteOrderPosNext(nOrderPosNext); - - return DB_LOAD_OK; -} - class CWalletScanState { public: unsigned int nKeys; @@ -493,9 +342,10 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { char fTmp; char fUnused; - ssValue >> fTmp >> fUnused >> wtx.strFromAccount; - strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", - wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString()); + std::string unused_string; + ssValue >> fTmp >> fUnused >> unused_string; + strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s", + wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString()); wtx.fTimeReceivedIsTxTime = fTmp; } else @@ -511,23 +361,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, pwallet->AddToWallet(wtx, true, NULL); } - else if (strType == "acentry") - { - string strAccount; - ssKey >> strAccount; - uint64_t nNumber; - ssKey >> nNumber; - if (nNumber > nAccountingEntryNumber) - nAccountingEntryNumber = nNumber; - - if (!wss.fAnyUnordered) - { - CAccountingEntry acentry; - ssValue >> acentry; - if (acentry.nOrderPos == -1) - wss.fAnyUnordered = true; - } - } else if (strType == "watchs") { CScript script; @@ -984,7 +817,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value' for (uint256 hash : wss.vWalletUpgrade) - WriteTx(hash, pwallet->mapWallet[hash]); + WriteTx(pwallet->mapWallet[hash]); // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) @@ -994,7 +827,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) WriteVersion(CLIENT_VERSION); if (wss.fAnyUnordered) - result = ReorderTransactions(pwallet); + result = pwallet->ReorderTransactions(); return result; } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 087fda714..9e4cfd32b 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -20,8 +20,6 @@ static const bool DEFAULT_FLUSHWALLET = true; -class CAccount; -class CAccountingEntry; struct CBlockLocator; class CKeyPool; class CMasterKey; @@ -132,7 +130,7 @@ class CWalletDB : public CDB bool WritePurpose(const std::string& strAddress, const std::string& purpose); bool ErasePurpose(const std::string& strAddress); - bool WriteTx(uint256 hash, const CWalletTx& wtx); + bool WriteTx(const CWalletTx& wtx); bool EraseTx(uint256 hash); bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata &keyMeta); @@ -159,19 +157,11 @@ class CWalletDB : public CDB bool WriteMinVersion(int nVersion); - bool ReadAccount(const std::string& strAccount, CAccount& account); - bool WriteAccount(const std::string& strAccount, const CAccount& account); - /// Write destination data key,value tuple to database bool WriteDestData(const std::string &address, const std::string &key, const std::string &value); /// Erase destination data tuple from wallet database bool EraseDestData(const std::string &address, const std::string &key); - bool WriteAccountingEntry(const CAccountingEntry& acentry); - CAmount GetAccountCreditDebit(const std::string& strAccount); - void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); - - DBErrors ReorderTransactions(CWallet* pwallet); DBErrors LoadWallet(CWallet* pwallet); DBErrors FindWalletTxToZap(CWallet* pwallet, std::vector& vTxHash, std::vector& vWtx); DBErrors ZapWalletTx(CWallet* pwallet, std::vector& vWtx); @@ -207,7 +197,6 @@ class CWalletDB : public CDB CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); - bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); }; bool BackupWallet(const CWallet& wallet, const std::string& strDest); From 2c17f46db7e2f6a00fbc9a9bbe00ba5f077525fe Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 09:59:29 +0000 Subject: [PATCH 059/146] Update reduce-traffic.md - add one word --- doc/reduce-traffic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md index d9cb6195b..e5ccd8ca2 100644 --- a/doc/reduce-traffic.md +++ b/doc/reduce-traffic.md @@ -46,8 +46,8 @@ Be reminded of the effects of this setting. - Fee estimation will no longer work. - It sets the flag "-walletbroadcast" to be "0", only if it is currently unset. - Doing so disables the automatic broadcasting of transactions from wallet. Not - relaying other's transactions could hurt your privacy if used while a wallet + Doing so disables the automatic broadcasting of transactions from the wallet. Not + relaying other's transactions could hurt your privacy if used while the wallet is loaded or if you use the node to broadcast transactions. - If a peer is whitelisted and "-whitelistforcerelay" is set to "1" (which will also set "whitelistrelay" to "1"), we will still receive and relay their transactions. From 8e4bcbc31665496d9b62e03c9fb6a07a75fb2372 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 10:04:54 +0000 Subject: [PATCH 060/146] Remove unused AddDestData method --- src/wallet/wallet.cpp | 11 ----------- src/wallet/wallet.h | 2 -- 2 files changed, 13 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index cdc7804d8..c035cacf0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4692,17 +4692,6 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { } } -bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) -{ - if (std::get_if(&dest)) - return false; - - mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); - if (!fFileBacked) - return true; - return CWalletDB(strWalletFile).WriteDestData(EncodeDestination(dest), key, value); -} - bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) { if (!mapAddressBook[dest].destdata.erase(key)) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index eb0975e58..945551502 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1022,8 +1022,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool AddCScript(const CScript& redeemScript); bool LoadCScript(const CScript& redeemScript); - //! Adds a destination data tuple to the store, and saves it to disk - bool AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value); //! Erases a destination data tuple in the store and on disk bool EraseDestData(const CTxDestination &dest, const std::string &key); //! Adds a destination data tuple to the store, without saving it to disk From a8fdccc5e19f1a42bff4d97db74abccf1bd32529 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 10:33:10 +0000 Subject: [PATCH 061/146] Lock cs_main prior to calling blockToJSON --- src/gtest/test_rpc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gtest/test_rpc.cpp b/src/gtest/test_rpc.cpp index d7a8af33e..e193aef3a 100644 --- a/src/gtest/test_rpc.cpp +++ b/src/gtest/test_rpc.cpp @@ -4,6 +4,7 @@ #include "chain.h" #include "chainparams.h" #include "clientversion.h" +#include "main.h" #include "primitives/block.h" #include "rpc/server.h" #include "streams.h" @@ -23,6 +24,7 @@ TEST(rpc, CheckBlockToJSONReturnsMinifiedSolution) { CBlockIndex index {block}; index.nHeight = 1391; + LOCK(cs_main); UniValue obj = blockToJSON(block, &index); EXPECT_EQ("009f44ff7505d789b964d6817734b8ce1377d456255994370d06e59ac99bd5791b6ad174a66fd71c70e60cfc7fd88243ffe06f80b1ad181625f210779c745524629448e25348a5fce4f346a1735e60fdf53e144c0157dbc47c700a21a236f1efb7ee75f65b8d9d9e29026cfd09048233175202b211b9a49de4ab46f1cac71b6ea57a686377bd612378746e70c61a659c9cd683269e9c2a5cbc1d19f1149345302bbd0a1e62bf4bab01e9caeea789a1519441a61b146de35a4cc75dbdf01029127e311ad5073e7e96397f47226a7df9df66b2086b70756db013bbaeb068260157014b2602fc7dc71336e1439c887d2742d9730b4e79b08ec7839c3e2a037ae1565d04e05e351bb3531e5ef42cf7b71ca1482a9205245dd41f4db0f71644f8bdb88e845558537c03834c06ac83f336651e54e2edfc12e15ea9b7ea2c074e6155654d44c4d3bd90d9511050e9ad87d170db01448e5be6f45419cd86008978db5e3ceab79890234f992648d69bf1053855387db646ccdee5575c65f81dd0f670b016d9f9a84707d91f77b862f697b8bb08365ba71fbe6bfa47af39155a75ebdcb1e5d69f59c40c9e3a64988c1ec26f7f5159eef5c244d504a9e46125948ecc389c2ec3028ac4ff39ffd66e7743970819272b21e0c2df75b308bc62896873952147e57ed79446db4cdb5a563e76ec4c25899d41128afb9a5f8fc8063621efb7a58b9dd666d30c73e318cdcf3393bfec200e160f500e645f7baac263db99fa4a7c1cb4fea219fc512193102034d379f244c21a81821301b8d47c90247713a3e902c762d7bafa6cdb744eeb6d3b50dd175599d02b6e9f5bbda59366e04862aa765135968426e7ac0116de7351940dc57c0ae451d63f667e39891bc81e09e6c76f6f8a7582f7447c6f5945f717b0e52a7e3dd0c6db4061362123cc53fd8ede4abed4865201dc4d8eb4e5d48baa565183b69a5304a44c0600bb24dcaeee9d95ceebd27c1b0a33e0b46f23797d7d7907300b2bb7d62ef2fc5aa139250c73930c621bb5f41fc235534ee8014dfaddd5245aeb01198420ba7b5c076545329c94d54fa725a8e807579f5f0cc9d98170598023268f5930893620190275e6b3c6f5181e36310a9a475208316911d78f917d724c5946c553b7ec042c563c540114b6b78bd4c6e808ee391a4a9d93e127032983c5b3708037b14aa604cfb034e7c8b0ffdd6936446fe80216178506a87402653a373926eeff66e704daf992a0a9a5c3ad80566c0339be9e5b8e35b3b3226b2f7767e20d992ea6c3d6e322eca37b0c7f7e60060802f5abcc1975841365cadbdc3867063addfc803766ae525375ecddee61f9df9ffcd20343c83ab82b0e91de039c59cb435c8d3159cc338b4901f40c9b5c27043bcf2bd5fa9b685b65c9ba5a1e11a51dd3f773051560341f9ec81d05bf259e2d4b7161f896fbb6812cfc924a32120b7367d5e40439e267adda6a1315bb0d6200ce6a503174c8d2a638ea6fd6b1f486d68db11bdca63c4f4a725d1ab6231ea875484e70b27d293c05803386924f283d4c12bb953474d92b7dd43d2d97193bd96281ebb63fa075d2f9ecd310c70ee1d97b5330bd8fb5791c5943ecf084e5f2c83915acac57519c46b166136068d6f9ec0dd598616e32c591128ce13705a283ca39d5b211409600e07b3713113374d9700207a45394eac5b3b7afc9b1b2bad7d89fd3f35f6b2413ce615ee7869b3569009403b96fdacdb32ef0a7e5229e2b666d51e95bdfb009b892e88bde70621a9b6509f068781392df4bdbc5723bb15071993f0d9a11575af5ff6ef85eaea39bc86805b35d8beee91b779354147f2d85304b8b49d053e7444fdd3deb9d16de331f2552af5b3be7766bb8f3f6a78c62148efb231f2268", obj.find_value("solution").get_str()); } From f3ec45709aaa66641a717b1ef7aeb7b88d65bc3e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 10:38:31 +0000 Subject: [PATCH 062/146] rpc: Disable Sprout address generation --- src/wallet/rpcwallet.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ef2db8eb9..66285ebc6 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2568,6 +2568,7 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) "z_getnewaddress ( type )\n" "\nReturns a new shielded address for receiving payments.\n" "\nWith no arguments, returns a Sapling address.\n" + "Generating a Sprout address is not allowed after Sapling has activated.\n" "\nArguments:\n" "1. \"type\" (string, optional, default=\"" + defaultType + "\") The type of address. One of [\"" + ADDR_TYPE_SPROUT + "\", \"" + ADDR_TYPE_SAPLING + "\"].\n" @@ -2589,6 +2590,13 @@ UniValue z_getnewaddress(const UniValue& params, bool fHelp) } if (addrType == ADDR_TYPE_SPROUT) { + if (Params().GetConsensus().NetworkUpgradeActive(chainActive.Height(), Consensus::UPGRADE_SAPLING)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid address type, \"" + + ADDR_TYPE_SPROUT + "\" is not allowed after Sapling"); + } + if (IsInitialBlockDownload(Params())) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Error: Creating a Sprout address during initial block download is not supported."); + } return EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey()); } else if (addrType == ADDR_TYPE_SAPLING) { return EncodePaymentAddress(pwalletMain->GenerateNewSaplingZKey()); From f3e63e0302a27a63e3af11b311799e45f8acf856 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 10:47:13 +0000 Subject: [PATCH 063/146] Log calls to getblocktemplate --- src/rpc/server.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index a369c6f0c..80f6677f6 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -382,8 +382,7 @@ void JSONRequest::parse(const UniValue& valRequest) if (!valMethod.isStr()) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); - if (strMethod != "getblocktemplate") - LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); + LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); // Parse params UniValue valParams = request.find_value("params"); From 39bde4f2719815191ab7f2a0e2615d584b31ff76 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 11:47:51 +0000 Subject: [PATCH 064/146] Bitcoin 0.12 locking PRs --- src/rpc/rawtransaction.cpp | 8 +++++--- src/wallet/walletdb.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 64c2c1e47..497a111ba 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -531,7 +531,6 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") ); - LOCK(cs_main); RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM)(UniValue::VNUM), true); if (params[0].isNull() || params[1].isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); @@ -539,7 +538,11 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) UniValue inputs = params[0].get_array(); UniValue sendTo = params[1].get_obj(); - int nextBlockHeight = chainActive.Height() + 1; + int nextBlockHeight; + { + LOCK(cs_main); + nextBlockHeight = chainActive.Height() + 1; + } CMutableTransaction rawTx = CreateNewContextualCMutableTransaction( Params().GetConsensus(), nextBlockHeight); @@ -745,7 +748,6 @@ UniValue decodescript(const UniValue& params, bool fHelp) + HelpExampleRpc("decodescript", "\"hexstring\"") ); - LOCK(cs_main); RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); UniValue r(UniValue::VOBJ); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index c06d9f5f4..a4877ed5f 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1079,8 +1079,13 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKe CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); string strType, strErr; - bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, + bool fReadOK; + { + // Required in LoadKeyMetadata(): + LOCK(dummyWallet.cs_wallet); + fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, wss, strType, strErr); + } if (!IsKeyType(strType)) continue; if (!fReadOK) From 25958bea8a53a688c77b209bf6b441701285fd15 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 11:58:43 +0000 Subject: [PATCH 065/146] Bitcoin 0.13 locking PRs --- src/keystore.cpp | 1 + src/main.cpp | 29 ++++++++++++++++++---- src/scheduler.cpp | 1 + src/sync.cpp | 63 ++++++++++++++++++++++++++++++++++++++--------- src/sync.h | 33 ++++++++++++++++--------- 5 files changed, 98 insertions(+), 29 deletions(-) diff --git a/src/keystore.cpp b/src/keystore.cpp index cbbef8beb..93f090c3f 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -17,6 +17,7 @@ bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) con { CKey key; if (!GetKey(address, key)) { + LOCK(cs_KeyStore); WatchKeyMap::const_iterator it = mapWatchKeys.find(address); if (it != mapWatchKeys.end()) { vchPubKeyOut = it->second; diff --git a/src/main.cpp b/src/main.cpp index 3247a03ac..c7c8c17f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3296,13 +3296,13 @@ static void NotifyHeaderTip(const CChainParams& chainParams) { */ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock) { - CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; - + CBlockIndex *pindexNewTip = NULL; do { boost::this_thread::interruption_point(); bool fInitialDownload; + int nNewHeight; { LOCK(cs_main); if (pindexMostWork == NULL) { @@ -3323,6 +3323,7 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, } pindexNewTip = chainActive.Tip(); fInitialDownload = IsInitialBlockDownload(chainparams); + nNewHeight = chainActive.Height(); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). @@ -3338,13 +3339,13 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, { LOCK(cs_vNodes); for (CNode* pnode : vNodes) - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); } // Notify external listeners about the new tip. GetMainSignals().UpdatedBlockTip(pindexNewTip); } - } while(pindexMostWork != chainActive.Tip()); + } while (pindexNewTip != pindexMostWork); CheckBlockIndex(chainparams.GetConsensus()); // Write changes periodically to disk, after relay. @@ -5311,6 +5312,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion != 0) { pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); + LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } @@ -5379,7 +5381,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); // Potentially mark this peer as a preferred download peer. - UpdatePreferredDownload(pfrom, State(pfrom->GetId())); + { + LOCK(cs_main); + UpdatePreferredDownload(pfrom, State(pfrom->GetId())); + } // Change version pfrom->PushMessage("verack"); @@ -5445,6 +5450,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (pfrom->nVersion == 0) { // Must have a version message before anything else + LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } @@ -5488,6 +5494,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; if (vAddr.size() > 1000) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } @@ -5547,6 +5554,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } @@ -5623,6 +5631,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %u", vInv.size()); } @@ -5872,6 +5881,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("headers message size = %u", nCount); } @@ -6113,6 +6123,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // This isn't a Misbehaving(100) (immediate ban) because the // peer might be an older or different implementation with // a different signature key, etc. + LOCK(cs_main); Misbehaving(pfrom->GetId(), 10); } } @@ -6124,6 +6135,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, strCommand == "filteradd")) { if (pfrom->nVersion >= NO_BLOOM_VERSION) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } else if (GetBoolArg("-enforcenodebloom", DEFAULT_ENFORCENODEBLOOM)) { @@ -6139,8 +6151,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> filter; if (!filter.IsWithinSizeConstraints()) + { // There is no excuse for sending a too-large filter + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); + } else { LOCK(pfrom->cs_filter); @@ -6161,13 +6176,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) pfrom->pfilter->insert(vData); else + { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); + } } } diff --git a/src/scheduler.cpp b/src/scheduler.cpp index fc0696647..a4bc22dbb 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -69,6 +69,7 @@ void CScheduler::serviceQueue() } } --nThreadsServicingQueue; + newTaskScheduled.notify_one(); } void CScheduler::stop(bool drain) diff --git a/src/sync.cpp b/src/sync.cpp index 2546538cd..44f718e91 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -55,11 +55,28 @@ struct CLockLocation { }; typedef std::vector > LockStack; +typedef std::map, LockStack> LockOrders; +typedef std::set > InvLockOrders; + +struct LockData { + // Very ugly hack: as the global constructs and destructors run single + // threaded, we use this boolean to know whether LockData still exists, + // as DeleteLock can get called by global CCriticalSection destructors + // after LockData disappears. + bool available; + LockData() : available(true) {} + ~LockData() { available = false; } + + LockOrders lockorders; + InvLockOrders invlockorders; + boost::mutex dd_mutex; +}; +LockData& GetLockData() { + static LockData lockdata; + return lockdata; +} -static boost::mutex dd_mutex; -static std::map, LockStack> lockorders; -static boost::thread_specific_ptr lockstack; - +boost::thread_specific_ptr lockstack; static void potential_deadlock_detected(const std::pair& mismatch, const LockStack& s1, const LockStack& s2) { @@ -116,7 +133,8 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) if (lockstack.get() == NULL) lockstack.reset(new LockStack); - dd_mutex.lock(); + LockData& lockdata = GetLockData(); + boost::unique_lock lock(lockdata.dd_mutex); (*lockstack).push_back(std::make_pair(c, locklocation)); @@ -126,23 +144,21 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) break; std::pair p1 = std::make_pair(i.first, c); - if (lockorders.count(p1)) + if (lockdata.lockorders.count(p1)) continue; - lockorders[p1] = (*lockstack); + lockdata.lockorders[p1] = (*lockstack); std::pair p2 = std::make_pair(c, i.first); - if (lockorders.count(p2)) - potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]); + lockdata.invlockorders.insert(p2); + if (lockdata.lockorders.count(p2)) + potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]); } } - dd_mutex.unlock(); } static void pop_lock() { - dd_mutex.lock(); (*lockstack).pop_back(); - dd_mutex.unlock(); } void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) @@ -172,4 +188,27 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, abort(); } +void DeleteLock(void* cs) +{ + LockData& lockdata = GetLockData(); + if (!lockdata.available) { + // We're already shutting down. + return; + } + boost::unique_lock lock(lockdata.dd_mutex); + std::pair item = std::make_pair(cs, (void*)0); + LockOrders::iterator it = lockdata.lockorders.lower_bound(item); + while (it != lockdata.lockorders.end() && it->first.first == cs) { + std::pair invitem = std::make_pair(it->first.second, it->first.first); + lockdata.invlockorders.erase(invitem); + lockdata.lockorders.erase(it++); + } + InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item); + while (invit != lockdata.invlockorders.end() && invit->first == cs) { + std::pair invinvitem = std::make_pair(invit->second, invit->first); + lockdata.lockorders.erase(invinvitem); + lockdata.invlockorders.erase(invit++); + } +} + #endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h index 45a3bd3fd..75021ee4b 100644 --- a/src/sync.h +++ b/src/sync.h @@ -71,30 +71,39 @@ class LOCKABLE AnnotatedMixin : public PARENT } }; -/** - * Wrapped boost mutex: supports recursive locking, but no waiting - * TODO: We should move away from using the recursive lock by default. - */ -typedef AnnotatedMixin CCriticalSection; - -/** Wrapped boost mutex: supports waiting but not recursive locking */ -typedef AnnotatedMixin CWaitableCriticalSection; - -/** Just a typedef for boost::condition_variable, can be wrapped later if desired */ -typedef boost::condition_variable CConditionVariable; - #ifdef DEBUG_LOCKORDER void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); void LeaveCritical(); std::string LocksHeld(); void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); +void DeleteLock(void* cs); #else void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} void static inline LeaveCritical() {} void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} +void static inline DeleteLock(void* cs) {} #endif #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) +/** + * Wrapped boost mutex: supports recursive locking, but no waiting + * TODO: We should move away from using the recursive lock by default. + */ +class CCriticalSection : public AnnotatedMixin +{ +public: + ~CCriticalSection() { + DeleteLock((void*)this); + } +}; + +typedef CCriticalSection CDynamicCriticalSection; +/** Wrapped boost mutex: supports waiting but not recursive locking */ +typedef AnnotatedMixin CWaitableCriticalSection; + +/** Just a typedef for boost::condition_variable, can be wrapped later if desired */ +typedef boost::condition_variable CConditionVariable; + #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine); #endif From ff494b28272f7831a5d4581608c0ba410fa4d4ff Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 12:04:22 +0000 Subject: [PATCH 066/146] Comment and error message cleanups for transaction checks --- src/main.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c7c8c17f5..1bb9836fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -794,19 +794,19 @@ bool ContextualCheckTransaction( // Reject transactions with non-Sapling version group ID if (tx.fOverwintered && tx.nVersionGroupId != SAPLING_VERSION_GROUP_ID) { return state.DoS(isInitBlockDownload(chainparams) ? 0 : dosLevel, - error("CheckTransaction(): invalid Sapling tx version"), + error("ContextualCheckTransaction(): invalid Sapling tx version"), REJECT_INVALID, "bad-sapling-tx-version-group-id"); } // Reject transactions with invalid version if (tx.fOverwintered && tx.nVersion < SAPLING_MIN_TX_VERSION ) { - return state.DoS(100, error("CheckTransaction(): Sapling version too low"), + return state.DoS(100, error("ContextualCheckTransaction(): Sapling version too low"), REJECT_INVALID, "bad-tx-sapling-version-too-low"); } // Reject transactions with invalid version if (tx.fOverwintered && tx.nVersion > SAPLING_MAX_TX_VERSION ) { - return state.DoS(100, error("CheckTransaction(): Sapling version too high"), + return state.DoS(100, error("ContextualCheckTransaction(): Sapling version too high"), REJECT_INVALID, "bad-tx-sapling-version-too-high"); } } else if (overwinterActive) { @@ -819,13 +819,13 @@ bool ContextualCheckTransaction( // Reject transactions with non-Overwinter version group ID if (tx.fOverwintered && tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) { return state.DoS(isInitBlockDownload(chainparams) ? 0 : dosLevel, - error("CheckTransaction(): invalid Overwinter tx version"), + error("ContextualCheckTransaction(): invalid Overwinter tx version"), REJECT_INVALID, "bad-overwinter-tx-version-group-id"); } // Reject transactions with invalid version if (tx.fOverwintered && tx.nVersion > OVERWINTER_MAX_TX_VERSION ) { - return state.DoS(100, error("CheckTransaction(): overwinter version too high"), + return state.DoS(100, error("ContextualCheckTransaction(): overwinter version too high"), REJECT_INVALID, "bad-tx-overwinter-version-too-high"); } } @@ -834,7 +834,7 @@ bool ContextualCheckTransaction( if (overwinterActive) { // Reject transactions intended for Sprout if (!tx.fOverwintered) { - return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"), + return state.DoS(dosLevel, error("ContextualCheckTransaction(): overwinter is active"), REJECT_INVALID, "tx-overwinter-active"); } @@ -842,7 +842,8 @@ bool ContextualCheckTransaction( if (IsExpiredTx(tx, nHeight)) { // Don't increase banscore if the transaction only just expired int expiredDosLevel = IsExpiredTx(tx, nHeight - 1) ? dosLevel : 0; - return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired"); + return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction is expired"), + REJECT_INVALID, "tx-overwinter-expired"); } } @@ -867,7 +868,7 @@ bool ContextualCheckTransaction( try { dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); } catch (std::logic_error ex) { - return state.DoS(100, error("CheckTransaction(): error computing signature hash"), + return state.DoS(100, error("ContextualCheckTransaction(): error computing signature hash"), REJECT_INVALID, "error-computing-signature-hash"); } } @@ -883,7 +884,7 @@ bool ContextualCheckTransaction( tx.joinSplitPubKey.begin() ) != 0) { return state.DoS(isInitBlockDownload(chainparams) ? 0 : 100, - error("CheckTransaction(): invalid joinsplit signature"), + error("ContextualCheckTransaction(): invalid joinsplit signature"), REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); } } @@ -962,6 +963,10 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); } } + + // Sapling zk-SNARK proofs are checked in librustzcash_sapling_check_{spend,output}, + // called from ContextualCheckTransaction. + return true; } } From cefd5606d47f39a063ded93e3a6124a5a9d3a731 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 13:14:43 +0000 Subject: [PATCH 067/146] Bitcoin 0.15 locking PRs --- src/Makefile.test.include | 1 + src/checkqueue.h | 24 +- src/test/checkqueue_tests.cpp | 440 ++++++++++++++++++++++++++++++++++ 3 files changed, 455 insertions(+), 10 deletions(-) create mode 100644 src/test/checkqueue_tests.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index b1d8f8ea3..d5ded9e72 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -63,6 +63,7 @@ BITCOIN_TESTS =\ test/bloom_tests.cpp \ test/checkblock_tests.cpp \ test/Checkpoints_tests.cpp \ + test/checkqueue_tests.cpp \ test/coins_tests.cpp \ test/compress_tests.cpp \ test/convertbits_tests.cpp \ diff --git a/src/checkqueue.h b/src/checkqueue.h index e700c1774..d6476e8ca 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -12,6 +12,8 @@ #include #include +#include "sync.h" + template class CCheckQueueControl; @@ -126,6 +128,9 @@ class CCheckQueue } public: + //! Mutex to ensure only one concurrent CCheckQueueControl + boost::mutex ControlMutex; + //! Create a new check queue CCheckQueue(unsigned int nBatchSizeIn) : nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {} @@ -160,12 +165,6 @@ class CCheckQueue { } - bool IsIdle() - { - boost::unique_lock lock(mutex); - return (nTotal == nIdle && nTodo == 0 && fAllOk == true); - } - }; /** @@ -176,16 +175,18 @@ template class CCheckQueueControl { private: - CCheckQueue* pqueue; + CCheckQueue * const pqueue; bool fDone; public: - CCheckQueueControl(CCheckQueue* pqueueIn) : pqueue(pqueueIn), fDone(false) + CCheckQueueControl() = delete; + CCheckQueueControl(const CCheckQueueControl&) = delete; + CCheckQueueControl& operator=(const CCheckQueueControl&) = delete; + explicit CCheckQueueControl(CCheckQueue * const pqueueIn) : pqueue(pqueueIn), fDone(false) { // passed queue is supposed to be unused, or NULL if (pqueue != NULL) { - bool isIdle = pqueue->IsIdle(); - assert(isIdle); + ENTER_CRITICAL_SECTION(pqueue->ControlMutex); } } @@ -208,6 +209,9 @@ class CCheckQueueControl { if (!fDone) Wait(); + if (pqueue != NULL) { + LEAVE_CRITICAL_SECTION(pqueue->ControlMutex); + } } }; diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp new file mode 100644 index 000000000..627c9d172 --- /dev/null +++ b/src/test/checkqueue_tests.cpp @@ -0,0 +1,440 @@ +// Copyright (c) 2012-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "util.h" +#include "utiltime.h" + +#include "test/test_bitcoin.h" +#include "checkqueue.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "random.h" + +static const int nScriptCheckThreads = 8; + +BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup) + +static const int QUEUE_BATCH_SIZE = 128; + +struct FakeCheck { + bool operator()() + { + return true; + } + void swap(FakeCheck& x){}; +}; + +struct FakeCheckCheckCompletion { + static std::atomic n_calls; + bool operator()() + { + ++n_calls; + return true; + } + void swap(FakeCheckCheckCompletion& x){}; +}; + +struct FailingCheck { + bool fails; + FailingCheck(bool fails) : fails(fails){}; + FailingCheck() : fails(true){}; + bool operator()() + { + return !fails; + } + void swap(FailingCheck& x) + { + std::swap(fails, x.fails); + }; +}; + +struct UniqueCheck { + static std::mutex m; + static std::unordered_multiset results; + size_t check_id; + UniqueCheck(size_t check_id_in) : check_id(check_id_in){}; + UniqueCheck() : check_id(0){}; + bool operator()() + { + std::lock_guard l(m); + results.insert(check_id); + return true; + } + void swap(UniqueCheck& x) { std::swap(x.check_id, check_id); }; +}; + + +struct MemoryCheck { + static std::atomic fake_allocated_memory; + bool b {false}; + bool operator()() + { + return true; + } + MemoryCheck(){}; + MemoryCheck(const MemoryCheck& x) + { + // We have to do this to make sure that destructor calls are paired + // + // Really, copy constructor should be deletable, but CCheckQueue breaks + // if it is deleted because of internal push_back. + fake_allocated_memory += b; + }; + MemoryCheck(bool b_) : b(b_) + { + fake_allocated_memory += b; + }; + ~MemoryCheck(){ + fake_allocated_memory -= b; + + }; + void swap(MemoryCheck& x) { std::swap(b, x.b); }; +}; + +struct FrozenCleanupCheck { + static std::atomic nFrozen; + static std::condition_variable cv; + static std::mutex m; + // Freezing can't be the default initialized behavior given how the queue + // swaps in default initialized Checks. + bool should_freeze {false}; + bool operator()() + { + return true; + } + FrozenCleanupCheck() {} + ~FrozenCleanupCheck() + { + if (should_freeze) { + std::unique_lock l(m); + nFrozen = 1; + cv.notify_one(); + cv.wait(l, []{ return nFrozen == 0;}); + } + } + void swap(FrozenCleanupCheck& x){std::swap(should_freeze, x.should_freeze);}; +}; + +// Static Allocations +std::mutex FrozenCleanupCheck::m{}; +std::atomic FrozenCleanupCheck::nFrozen{0}; +std::condition_variable FrozenCleanupCheck::cv{}; +std::mutex UniqueCheck::m; +std::unordered_multiset UniqueCheck::results; +std::atomic FakeCheckCheckCompletion::n_calls{0}; +std::atomic MemoryCheck::fake_allocated_memory{0}; + +// Queue Typedefs +typedef CCheckQueue Correct_Queue; +typedef CCheckQueue Standard_Queue; +typedef CCheckQueue Failing_Queue; +typedef CCheckQueue Unique_Queue; +typedef CCheckQueue Memory_Queue; +typedef CCheckQueue FrozenCleanup_Queue; + + +/** This test case checks that the CCheckQueue works properly + * with each specified size_t Checks pushed. + */ +void Correct_Queue_range(std::vector range) +{ + auto small_queue = std::unique_ptr(new Correct_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{small_queue->Thread();}); + } + // Make vChecks here to save on malloc (this test can be slow...) + std::vector vChecks; + for (auto i : range) { + size_t total = i; + FakeCheckCheckCompletion::n_calls = 0; + CCheckQueueControl control(small_queue.get()); + while (total) { + vChecks.resize(std::min(total, (size_t) GetRand(10))); + total -= vChecks.size(); + control.Add(vChecks); + } + BOOST_REQUIRE(control.Wait()); + if (FakeCheckCheckCompletion::n_calls != i) { + BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i); + BOOST_TEST_MESSAGE("Failure on trial " << i << " expected, got " << FakeCheckCheckCompletion::n_calls); + } + } + tg.interrupt_all(); + tg.join_all(); +} + +/** Test that 0 checks is correct + */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Zero) +{ + std::vector range; + range.push_back((size_t)0); + Correct_Queue_range(range); +} +/** Test that 1 check is correct + */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_One) +{ + std::vector range; + range.push_back((size_t)1); + Correct_Queue_range(range); +} +/** Test that MAX check is correct + */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Max) +{ + std::vector range; + range.push_back(100000); + Correct_Queue_range(range); +} +/** Test that random numbers of checks are correct + */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) +{ + std::vector range; + range.reserve(100000/1000); + for (size_t i = 2; i < 100000; i += std::max((size_t)1, (size_t)GetRand(std::min((size_t)1000, ((size_t)100000) - i)))) + range.push_back(i); + Correct_Queue_range(range); +} + + +/** Test that failing checks are caught */ +BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) +{ + auto fail_queue = std::unique_ptr(new Failing_Queue {QUEUE_BATCH_SIZE}); + + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{fail_queue->Thread();}); + } + + for (size_t i = 0; i < 1001; ++i) { + CCheckQueueControl control(fail_queue.get()); + size_t remaining = i; + while (remaining) { + size_t r = GetRand(10); + + std::vector vChecks; + vChecks.reserve(r); + for (size_t k = 0; k < r && remaining; k++, remaining--) + vChecks.emplace_back(remaining == 1); + control.Add(vChecks); + } + bool success = control.Wait(); + if (i > 0) { + BOOST_REQUIRE(!success); + } else if (i == 0) { + BOOST_REQUIRE(success); + } + } + tg.interrupt_all(); + tg.join_all(); +} +// Test that a block validation which fails does not interfere with +// future blocks, ie, the bad state is cleared. +BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) +{ + auto fail_queue = std::unique_ptr(new Failing_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{fail_queue->Thread();}); + } + + for (auto times = 0; times < 10; ++times) { + for (bool end_fails : {true, false}) { + CCheckQueueControl control(fail_queue.get()); + { + std::vector vChecks; + vChecks.resize(100, false); + vChecks[99] = end_fails; + control.Add(vChecks); + } + bool r =control.Wait(); + BOOST_REQUIRE(r || end_fails); + } + } + tg.interrupt_all(); + tg.join_all(); +} + +// Test that unique checks are actually all called individually, rather than +// just one check being called repeatedly. Test that checks are not called +// more than once as well +BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) +{ + auto queue = std::unique_ptr(new Unique_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{queue->Thread();}); + + } + + size_t COUNT = 100000; + size_t total = COUNT; + { + CCheckQueueControl control(queue.get()); + while (total) { + size_t r = GetRand(10); + std::vector vChecks; + for (size_t k = 0; k < r && total; k++) + vChecks.emplace_back(--total); + control.Add(vChecks); + } + } + bool r = true; + BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT); + for (size_t i = 0; i < COUNT; ++i) + r = r && UniqueCheck::results.count(i) == 1; + BOOST_REQUIRE(r); + tg.interrupt_all(); + tg.join_all(); +} + + +// Test that blocks which might allocate lots of memory free their memory agressively. +// +// This test attempts to catch a pathological case where by lazily freeing +// checks might mean leaving a check un-swapped out, and decreasing by 1 each +// time could leave the data hanging across a sequence of blocks. +BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) +{ + auto queue = std::unique_ptr(new Memory_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{queue->Thread();}); + } + for (size_t i = 0; i < 1000; ++i) { + size_t total = i; + { + CCheckQueueControl control(queue.get()); + while (total) { + size_t r = GetRand(10); + std::vector vChecks; + for (size_t k = 0; k < r && total; k++) { + total--; + // Each iteration leaves data at the front, back, and middle + // to catch any sort of deallocation failure + vChecks.emplace_back(total == 0 || total == i || total == i/2); + } + control.Add(vChecks); + } + } + BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0); + } + tg.interrupt_all(); + tg.join_all(); +} + +// Test that a new verification cannot occur until all checks +// have been destructed +BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) +{ + auto queue = std::unique_ptr(new FrozenCleanup_Queue {QUEUE_BATCH_SIZE}); + boost::thread_group tg; + bool fails = false; + for (auto x = 0; x < nScriptCheckThreads; ++x) { + tg.create_thread([&]{queue->Thread();}); + } + std::thread t0([&]() { + CCheckQueueControl control(queue.get()); + std::vector vChecks(1); + // Freezing can't be the default initialized behavior given how the queue + // swaps in default initialized Checks (otherwise freezing destructor + // would get called twice). + vChecks[0].should_freeze = true; + control.Add(vChecks); + control.Wait(); // Hangs here + }); + { + std::unique_lock l(FrozenCleanupCheck::m); + // Wait until the queue has finished all jobs and frozen + FrozenCleanupCheck::cv.wait(l, [](){return FrozenCleanupCheck::nFrozen == 1;}); + // Try to get control of the queue a bunch of times + for (auto x = 0; x < 100 && !fails; ++x) { + fails = queue->ControlMutex.try_lock(); + } + // Unfreeze + FrozenCleanupCheck::nFrozen = 0; + } + // Awaken frozen destructor + FrozenCleanupCheck::cv.notify_one(); + // Wait for control to finish + t0.join(); + tg.interrupt_all(); + tg.join_all(); + BOOST_REQUIRE(!fails); +} + + +/** Test that CCheckQueueControl is threadsafe */ +BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) +{ + auto queue = std::unique_ptr(new Standard_Queue{QUEUE_BATCH_SIZE}); + { + boost::thread_group tg; + std::atomic nThreads {0}; + std::atomic fails {0}; + for (size_t i = 0; i < 3; ++i) { + tg.create_thread( + [&]{ + CCheckQueueControl control(queue.get()); + // While sleeping, no other thread should execute to this point + auto observed = ++nThreads; + MilliSleep(10); + fails += observed != nThreads; + }); + } + tg.join_all(); + BOOST_REQUIRE_EQUAL(fails, 0); + } + { + boost::thread_group tg; + std::mutex m; + bool has_lock {false}; + bool has_tried {false}; + bool done {false}; + bool done_ack {false}; + std::condition_variable cv; + { + std::unique_lock l(m); + tg.create_thread([&]{ + CCheckQueueControl control(queue.get()); + std::unique_lock l(m); + has_lock = true; + cv.notify_one(); + cv.wait(l, [&]{return has_tried;}); + done = true; + cv.notify_one(); + // Wait until the done is acknowledged + // + cv.wait(l, [&]{return done_ack;}); + }); + // Wait for thread to get the lock + cv.wait(l, [&](){return has_lock;}); + bool fails = false; + for (auto x = 0; x < 100 && !fails; ++x) { + fails = queue->ControlMutex.try_lock(); + } + has_tried = true; + cv.notify_one(); + cv.wait(l, [&](){return done;}); + // Acknowledge the done + done_ack = true; + cv.notify_one(); + BOOST_REQUIRE(!fails); + } + tg.join_all(); + } +} +BOOST_AUTO_TEST_SUITE_END() From b0723a313882b8b0f238ed8c4b8df829ce1406d3 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 13:28:46 +0000 Subject: [PATCH 068/146] Add data() method to CDataStream --- src/dbwrapper.h | 13 ++++++------- src/streams.h | 6 ++---- src/wallet/db.cpp | 8 ++++---- src/wallet/db.h | 14 +++++++------- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/dbwrapper.h b/src/dbwrapper.h index e80baccab..8c77fd224 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -57,12 +57,12 @@ class CDBBatch CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); + leveldb::Slice slKey(ssKey.data(), ssKey.size()); CDataStream ssValue(SER_DISK, CLIENT_VERSION); ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE); ssValue << value; - leveldb::Slice slValue(&ssValue[0], ssValue.size()); + leveldb::Slice slValue(ssValue.data(), ssValue.size()); batch.Put(slKey, slValue); } @@ -73,8 +73,7 @@ class CDBBatch CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); - + leveldb::Slice slKey(ssKey.data(), ssKey.size()); batch.Delete(slKey); } }; @@ -103,7 +102,7 @@ class CDBIterator CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(GetSerializeSize(ssKey, key)); ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); + leveldb::Slice slKey(ssKey.data(), ssKey.size()); piter->Seek(slKey); } @@ -181,7 +180,7 @@ class CDBWrapper CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); + leveldb::Slice slKey(ssKey.data(), ssKey.size()); std::string strValue; leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); @@ -214,7 +213,7 @@ class CDBWrapper CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey << key; - leveldb::Slice slKey(&ssKey[0], ssKey.size()); + leveldb::Slice slKey(ssKey.data(), ssKey.size()); std::string strValue; leveldb::Status status = pdb->Get(readoptions, slKey, &strValue); diff --git a/src/streams.h b/src/streams.h index 5dbcfaf21..9b39ba4c3 100644 --- a/src/streams.h +++ b/src/streams.h @@ -106,12 +106,10 @@ class CBaseDataStream Init(nTypeIn, nVersionIn); } -#if !defined(_MSC_VER) || _MSC_VER >= 1300 CBaseDataStream(const char* pbegin, const char* pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) { Init(nTypeIn, nVersionIn); } -#endif CBaseDataStream(const vector_type& vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) { @@ -177,6 +175,8 @@ class CBaseDataStream void clear() { vch.clear(); nReadPos = 0; } iterator insert(iterator it, const char& x=char()) { return vch.insert(it, x); } void insert(iterator it, size_type n, const char& x) { vch.insert(it, n, x); } + value_type* data() { return vch.data() + nReadPos; } + const value_type* data() const { return vch.data() + nReadPos; } void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) { @@ -192,7 +192,6 @@ class CBaseDataStream vch.insert(it, first, last); } -#if !defined(_MSC_VER) || _MSC_VER >= 1300 void insert(iterator it, const char* first, const char* last) { if (last == first) return; @@ -206,7 +205,6 @@ class CBaseDataStream else vch.insert(it, first, last); } -#endif iterator erase(iterator it) { diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 1e978a143..bb04435c5 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -381,15 +381,15 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) break; } if (pszSkip && - strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) + strncmp(ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) continue; - if (strncmp(&ssKey[0], "\x07version", 8) == 0) { + if (strncmp(ssKey.data(), "\x07version", 8) == 0) { // Update version: ssValue.clear(); ssValue << CLIENT_VERSION; } - Dbt datKey(&ssKey[0], ssKey.size()); - Dbt datValue(&ssValue[0], ssValue.size()); + Dbt datKey(ssKey.data(), ssKey.size()); + Dbt datValue(ssValue.data(), ssValue.size()); int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); if (ret2 > 0) fSuccess = false; diff --git a/src/wallet/db.h b/src/wallet/db.h index b210d7b9c..54110b756 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -123,7 +123,7 @@ class CDB CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datKey(ssKey.data(), ssKey.size()); // Read Dbt datValue; @@ -161,13 +161,13 @@ class CDB CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datKey(ssKey.data(), ssKey.size()); // Value CDataStream ssValue(SER_DISK, CLIENT_VERSION); ssValue.reserve(10000); ssValue << value; - Dbt datValue(&ssValue[0], ssValue.size()); + Dbt datValue(ssValue.data(), ssValue.size()); // Write int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); @@ -190,7 +190,7 @@ class CDB CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datKey(ssKey.data(), ssKey.size()); // Erase int ret = pdb->del(activeTxn, &datKey, 0); @@ -210,7 +210,7 @@ class CDB CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; - Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datKey(ssKey.data(), ssKey.size()); // Exists int ret = pdb->exists(activeTxn, &datKey, 0); @@ -236,12 +236,12 @@ class CDB // Read at cursor Dbt datKey; if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { - datKey.set_data(&ssKey[0]); + datKey.set_data(ssKey.data()); datKey.set_size(ssKey.size()); } Dbt datValue; if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { - datValue.set_data(&ssValue[0]); + datValue.set_data(ssValue.data()); datValue.set_size(ssValue.size()); } datKey.set_flags(DB_DBT_MALLOC); From e73d98770a0518b51730bb8249a88706eca83f3e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 13:43:09 +0000 Subject: [PATCH 069/146] [Wallet] remove unused code/conditions in ReadAtCursor --- src/wallet/db.cpp | 2 +- src/wallet/db.h | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index bb04435c5..487d245f5 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -371,7 +371,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) while (fSuccess) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); + int ret = db.ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) { pcursor->close(); break; diff --git a/src/wallet/db.h b/src/wallet/db.h index 54110b756..69099849d 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -231,19 +231,17 @@ class CDB return pcursor; } - int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags = DB_NEXT) + int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, bool setRange = false) { // Read at cursor Dbt datKey; - if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { + unsigned int fFlags = DB_NEXT; + if (setRange) { datKey.set_data(ssKey.data()); datKey.set_size(ssKey.size()); + fFlags = DB_SET_RANGE; } Dbt datValue; - if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { - datValue.set_data(ssValue.data()); - datValue.set_size(ssValue.size()); - } datKey.set_flags(DB_DBT_MALLOC); datValue.set_flags(DB_DBT_MALLOC); int ret = pcursor->get(&datKey, &datValue, fFlags); From 72b1e942f5a3783bdcedde7fff1493e49d63fca2 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 15:02:07 +0000 Subject: [PATCH 070/146] Refactor ProofVerifier --- src/Makefile.am | 3 +- src/bench/bench_bitcoin.cpp | 1 - src/fuzzing/CheckBlock/fuzz.cpp | 3 +- src/gtest/main.cpp | 5 - src/gtest/test_checkblock.cpp | 5 +- src/gtest/test_checktransaction.cpp | 4 +- src/gtest/test_dynamicusage.cpp | 4 +- src/gtest/test_joinsplit.cpp | 55 ++-- src/gtest/test_transaction.cpp | 7 +- src/gtest/test_transaction_builder.cpp | 20 +- src/gtest/test_validation.cpp | 6 +- src/init.cpp | 6 - src/init.h | 4 - src/main.cpp | 20 +- src/main.h | 5 +- src/primitives/transaction.cpp | 63 +--- src/primitives/transaction.h | 11 +- src/proof_verifier.cpp | 70 +++++ src/proof_verifier.h | 40 +++ src/test/checkblock_tests.cpp | 3 +- src/test/test_bitcoin.cpp | 4 - src/test/transaction_tests.cpp | 25 +- src/transaction_builder.cpp | 18 +- src/transaction_builder.h | 2 - src/utiltest.cpp | 24 +- src/utiltest.h | 13 +- .../asyncrpcoperation_mergetoaddress.cpp | 12 +- .../asyncrpcoperation_saplingmigration.cpp | 2 +- src/wallet/asyncrpcoperation_sendmany.cpp | 12 +- .../asyncrpcoperation_shieldcoinbase.cpp | 10 +- src/wallet/gtest/test_wallet.cpp | 39 +-- src/wallet/rpcdisclosure.cpp | 2 +- src/wallet/rpcwallet.cpp | 15 +- src/wallet/wallet.cpp | 9 +- src/wallet/walletdb.cpp | 3 +- src/zcash/JoinSplit.cpp | 287 +++++++++--------- src/zcash/JoinSplit.hpp | 23 +- src/zcash/Proof.cpp | 17 -- src/zcash/Proof.hpp | 46 +-- src/zcbenchmarks.cpp | 18 +- 40 files changed, 396 insertions(+), 520 deletions(-) create mode 100644 src/proof_verifier.cpp create mode 100644 src/proof_verifier.h delete mode 100644 src/zcash/Proof.cpp diff --git a/src/Makefile.am b/src/Makefile.am index c1a596acd..0b89cb4bc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -150,6 +150,7 @@ BITCOIN_CORE_H = \ prevector.h \ primitives/block.h \ primitives/transaction.h \ + proof_verifier.h \ protocol.h \ pubkey.h \ random.h \ @@ -255,6 +256,7 @@ libbitcoin_server_a_SOURCES = \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ + proof_verifier.cpp \ rpc/server.cpp \ script/sigcache.cpp \ script/ismine.cpp \ @@ -494,7 +496,6 @@ libzcash_a_SOURCES = \ zcash/address/sprout.cpp \ zcash/address/zip32.cpp \ zcash/JoinSplit.cpp \ - zcash/Proof.cpp \ zcash/Note.cpp \ zcash/prf.cpp \ zcash/util.cpp diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 93ae4d2f1..6027d3e89 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -7,7 +7,6 @@ #include "key.h" #include "main.h" #include "util.h" -#include "zcash/JoinSplit.hpp" #include "librustzcash.h" diff --git a/src/fuzzing/CheckBlock/fuzz.cpp b/src/fuzzing/CheckBlock/fuzz.cpp index 838f9cd10..57ab7b4eb 100644 --- a/src/fuzzing/CheckBlock/fuzz.cpp +++ b/src/fuzzing/CheckBlock/fuzz.cpp @@ -1,5 +1,6 @@ #include "consensus/validation.h" #include "chainparams.h" +#include "proof_verifier.h" int main (int argc, char *argv[]) { int retval = 0; @@ -18,7 +19,7 @@ int main (int argc, char *argv[]) { // valid blocks with shielded transactions will generate a crash. const CChainParams& chainparams = Params(); - auto verifier = libzcash::ProofVerifier::Disabled(); + auto verifier = ProofVerifier::Disabled(); CValidationState state; // We don't check the PoW or Merkle tree root in order to reach more code. if (!CheckBlock(block, state, chainparams, verifier, false, false)) { diff --git a/src/gtest/main.cpp b/src/gtest/main.cpp index 2f8c4b09a..2b431690c 100644 --- a/src/gtest/main.cpp +++ b/src/gtest/main.cpp @@ -3,7 +3,6 @@ #include "key.h" #include "pubkey.h" #include "script/sigcache.h" -#include "zcash/JoinSplit.hpp" #include "util.h" #include "librustzcash.h" @@ -17,15 +16,11 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; -ZCJoinSplit* params; - int main(int argc, char **argv) { assert(init_and_check_sodium() != -1); ECC_Start(); InitSignatureCache(); - params = ZCJoinSplit::Prepared(); - fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index 56caf6a67..ea4212da4 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -3,6 +3,7 @@ #include "consensus/validation.h" #include "main.h" +#include "proof_verifier.h" #include "utiltest.h" #include "zcash/Proof.hpp" @@ -24,7 +25,7 @@ class MockCValidationState : public CValidationState { }; TEST(CheckBlock, VersionTooLow) { - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); CBlock block; block.nVersion = 1; @@ -61,7 +62,7 @@ TEST(CheckBlock, BlockSproutRejectsBadVersion) { MockCValidationState state; CBlockIndex indexPrev {Params().GenesisBlock()}; - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1); EXPECT_FALSE(CheckBlock(block, state, Params(), verifier, false, false)); diff --git a/src/gtest/test_checktransaction.cpp b/src/gtest/test_checktransaction.cpp index 4068db42d..91b1991a2 100644 --- a/src/gtest/test_checktransaction.cpp +++ b/src/gtest/test_checktransaction.cpp @@ -7,8 +7,6 @@ #include "consensus/validation.h" #include "utiltest.h" -extern ZCJoinSplit* params; - TEST(ChecktransactionTests, CheckVpubNotBothNonzero) { CMutableTransaction tx; tx.nVersion = 2; @@ -748,7 +746,7 @@ TEST(ChecktransactionTests, SaplingSproutInputSumsTooLarge) { std::array outputMap; auto jsdesc = JSDescription::Randomized( - *params, joinSplitPubKey, rt, + joinSplitPubKey, rt, inputs, outputs, inputMap, outputMap, 0, 0, false); diff --git a/src/gtest/test_dynamicusage.cpp b/src/gtest/test_dynamicusage.cpp index 6cbb5d8ba..757eb53b3 100644 --- a/src/gtest/test_dynamicusage.cpp +++ b/src/gtest/test_dynamicusage.cpp @@ -11,8 +11,6 @@ #include "transaction_builder.h" #include "utiltest.h" -extern ZCJoinSplit* params; - TEST(RecursiveDynamicUsageTests, TestTransactionTransparent) { auto consensusParams = RegtestActivateSapling(); @@ -40,7 +38,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionJoinSplit) auto sproutSk = libzcash::SproutSpendingKey::random(); - auto wtx = GetValidSproutReceive(*params, sproutSk, 25000, true); + auto wtx = GetValidSproutReceive(sproutSk, 25000, true); // 2 vin + 1 vJoinSplit + 1 vShieldedOutput // 160 + 1856 + 976 EXPECT_EQ(2992, RecursiveDynamicUsage(wtx)); diff --git a/src/gtest/test_joinsplit.cpp b/src/gtest/test_joinsplit.cpp index da5433dde..e4538a93b 100644 --- a/src/gtest/test_joinsplit.cpp +++ b/src/gtest/test_joinsplit.cpp @@ -8,6 +8,7 @@ #include "version.h" #include "serialize.h" #include "primitives/transaction.h" +#include "proof_verifier.h" #include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" #include "zcash/NoteEncryption.hpp" @@ -17,12 +18,9 @@ using namespace libzcash; -extern ZCJoinSplit* params; - // Make the Groth proof for a Sprout statement, // and store the result in a JSDescription object. JSDescription makeSproutProof( - ZCJoinSplit& js, const std::array& inputs, const std::array& outputs, const uint256& joinSplitPubKey, @@ -30,24 +28,23 @@ JSDescription makeSproutProof( uint64_t vpub_new, const uint256& rt ){ - return JSDescription(js, joinSplitPubKey, rt, inputs, outputs, vpub_old, vpub_new); + return JSDescription(joinSplitPubKey, rt, inputs, outputs, vpub_old, vpub_new); } bool verifySproutProof( - ZCJoinSplit& js, const JSDescription& jsdesc, const uint256& joinSplitPubKey ) { - auto verifier = libzcash::ProofVerifier::Strict(); - return jsdesc.Verify(js, verifier, joinSplitPubKey); + auto verifier = ProofVerifier::Strict(); + return verifier.VerifySprout(jsdesc, joinSplitPubKey); } -void test_full_api(ZCJoinSplit* js) +void test_full_api() { // Create verification context. - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); // The recipient's information. SproutSpendingKey recipient_key = SproutSpendingKey::random(); @@ -78,7 +75,6 @@ void test_full_api(ZCJoinSplit* js) // Perform the proofs jsdesc = makeSproutProof( - *js, inputs, outputs, joinSplitPubKey, @@ -89,14 +85,14 @@ void test_full_api(ZCJoinSplit* js) } // Verify both PHGR and Groth Proof: - ASSERT_TRUE(verifySproutProof(*js, jsdesc, joinSplitPubKey)); + ASSERT_TRUE(verifySproutProof(jsdesc, joinSplitPubKey)); { SproutMerkleTree tree; JSDescription jsdesc2; // Recipient should decrypt // Now the recipient should spend the money again - auto h_sig = js->h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey); + auto h_sig = ZCJoinSplit::h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey); ZCNoteDecryption decryptor(recipient_key.receiving_key()); auto note_pt = SproutNotePlaintext::decrypt( @@ -140,7 +136,6 @@ void test_full_api(ZCJoinSplit* js) // Perform the proofs jsdesc2 = makeSproutProof( - *js, inputs, outputs, joinSplitPubKey2, @@ -153,14 +148,13 @@ void test_full_api(ZCJoinSplit* js) // Verify Groth Proof: - ASSERT_TRUE(verifySproutProof(*js, jsdesc2, joinSplitPubKey2)); + ASSERT_TRUE(verifySproutProof(jsdesc2, joinSplitPubKey2)); } } // Invokes the API (but does not compute a proof) // to test exceptions void invokeAPI( - ZCJoinSplit* js, const std::array& inputs, const std::array& outputs, uint64_t vpub_old, @@ -178,7 +172,7 @@ void invokeAPI( std::array output_notes; // Groth - SproutProof proof = js->prove( + SproutProof proof = ZCJoinSplit::prove( inputs, outputs, output_notes, @@ -197,7 +191,6 @@ void invokeAPI( } void invokeAPIFailure( - ZCJoinSplit* js, const std::array& inputs, const std::array& outputs, uint64_t vpub_old, @@ -207,7 +200,7 @@ void invokeAPIFailure( ) { try { - invokeAPI(js, inputs, outputs, vpub_old, vpub_new, rt); + invokeAPI(inputs, outputs, vpub_old, vpub_new, rt); FAIL() << "It worked, when it shouldn't have!"; } catch(std::invalid_argument const & err) { EXPECT_EQ(err.what(), reason); @@ -324,7 +317,7 @@ TEST(Joinsplit, FullApiTest) increment_note_witnesses(note5.cm(), witnesses, tree); // Should work - invokeAPI(params, + invokeAPI( { JSInput(), JSInput() @@ -338,7 +331,7 @@ TEST(Joinsplit, FullApiTest) tree.root()); // lhs > MAX_MONEY - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(), JSInput() @@ -353,7 +346,7 @@ TEST(Joinsplit, FullApiTest) "nonsensical vpub_old value"); // rhs > MAX_MONEY - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(), JSInput() @@ -368,7 +361,7 @@ TEST(Joinsplit, FullApiTest) "nonsensical vpub_new value"); // input witness for the wrong element - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(witnesses[0], note1, sk), JSInput() @@ -384,7 +377,7 @@ TEST(Joinsplit, FullApiTest) // input witness doesn't match up with // real root - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(witnesses[1], note1, sk), JSInput() @@ -399,7 +392,7 @@ TEST(Joinsplit, FullApiTest) "joinsplit not anchored to the correct root"); // input is in the tree now! this should work - invokeAPI(params, + invokeAPI( { JSInput(witnesses[1], note1, sk), JSInput() @@ -413,7 +406,7 @@ TEST(Joinsplit, FullApiTest) tree.root()); // Wrong secret key - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(witnesses[1], note1, SproutSpendingKey::random()), JSInput() @@ -428,7 +421,7 @@ TEST(Joinsplit, FullApiTest) "input note not authorized to spend with given key"); // Absurd input value - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(witnesses[3], note3, sk), JSInput() @@ -443,7 +436,7 @@ TEST(Joinsplit, FullApiTest) "nonsensical input note value"); // Absurd total input value - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(witnesses[4], note4, sk), JSInput(witnesses[5], note5, sk) @@ -458,7 +451,7 @@ TEST(Joinsplit, FullApiTest) "nonsensical left hand size of joinsplit balance"); // Absurd output value - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(), JSInput() @@ -473,7 +466,7 @@ TEST(Joinsplit, FullApiTest) "nonsensical output value"); // Absurd total output value - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(), JSInput() @@ -488,7 +481,7 @@ TEST(Joinsplit, FullApiTest) "nonsensical right hand side of joinsplit balance"); // Absurd total output value - invokeAPIFailure(params, + invokeAPIFailure( { JSInput(), JSInput() @@ -503,7 +496,7 @@ TEST(Joinsplit, FullApiTest) "invalid joinsplit balance"); } - test_full_api(params); + test_full_api(); } TEST(Joinsplit, NotePlaintexts) diff --git a/src/gtest/test_transaction.cpp b/src/gtest/test_transaction.cpp index 4ba48c3d2..c0ec9f0be 100644 --- a/src/gtest/test_transaction.cpp +++ b/src/gtest/test_transaction.cpp @@ -6,7 +6,6 @@ #include -extern ZCJoinSplit* params; extern int GenZero(int n); extern int GenMax(int n); @@ -45,7 +44,7 @@ TEST(Transaction, JSDescriptionRandomized) { { auto jsdesc = JSDescription::Randomized( - *params, joinSplitPubKey, rt, + joinSplitPubKey, rt, inputs, outputs, inputMap, outputMap, 0, 0, false); @@ -61,7 +60,7 @@ TEST(Transaction, JSDescriptionRandomized) { { auto jsdesc = JSDescription::Randomized( - *params, joinSplitPubKey, rt, + joinSplitPubKey, rt, inputs, outputs, inputMap, outputMap, 0, 0, false, nullptr, GenZero); @@ -74,7 +73,7 @@ TEST(Transaction, JSDescriptionRandomized) { { auto jsdesc = JSDescription::Randomized( - *params, joinSplitPubKey, rt, + joinSplitPubKey, rt, inputs, outputs, inputMap, outputMap, 0, 0, false, nullptr, GenMax); diff --git a/src/gtest/test_transaction_builder.cpp b/src/gtest/test_transaction_builder.cpp index a3ccc7fbf..56a3a2cff 100644 --- a/src/gtest/test_transaction_builder.cpp +++ b/src/gtest/test_transaction_builder.cpp @@ -12,8 +12,6 @@ #include #include -extern ZCJoinSplit* params; - // Fake an empty view class TransactionBuilderCoinsViewDB : public CCoinsView { public: @@ -166,7 +164,7 @@ TEST(TransactionBuilder, SaplingToSprout) { // - 0.0004 Sapling-ZEC in - 0.00025 Sprout-ZEC out // - 0.00005 Sapling-ZEC change // - 0.0001 t-ZEC fee - auto builder = TransactionBuilder(consensusParams, 2, nullptr, params); + auto builder = TransactionBuilder(consensusParams, 2, nullptr); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSproutOutput(sproutAddr, 25000); auto tx = builder.Build().GetTxOrThrow(); @@ -198,8 +196,8 @@ TEST(TransactionBuilder, SproutToSproutAndSapling) { auto sproutSk = libzcash::SproutSpendingKey::random(); auto sproutAddr = sproutSk.address(); - auto wtx = GetValidSproutReceive(*params, sproutSk, 25000, true); - auto sproutNote = GetSproutNote(*params, sproutSk, wtx, 0, 1); + auto wtx = GetValidSproutReceive(sproutSk, 25000, true); + auto sproutNote = GetSproutNote(sproutSk, wtx, 0, 1); SproutMerkleTree sproutTree; for (int i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { @@ -218,7 +216,7 @@ TEST(TransactionBuilder, SproutToSproutAndSapling) { // - 0.00005 Sprout-ZEC change // - 0.00005 Sapling-ZEC out // - 0.00005 t-ZEC fee - auto builder = TransactionBuilder(consensusParams, 2, nullptr, params, &view); + auto builder = TransactionBuilder(consensusParams, 2, nullptr, &view); builder.SetFee(5000); builder.AddSproutInput(sproutSk, sproutNote, sproutWitness); builder.AddSproutOutput(sproutAddr, 6000); @@ -249,16 +247,6 @@ TEST(TransactionBuilder, SproutToSproutAndSapling) { RegtestDeactivateSapling(); } -TEST(TransactionBuilder, ThrowsOnSproutOutputWithoutParams) -{ - auto consensusParams = Params().GetConsensus(); - auto sk = libzcash::SproutSpendingKey::random(); - auto addr = sk.address(); - - auto builder = TransactionBuilder(consensusParams, 1); - ASSERT_THROW(builder.AddSproutOutput(addr, 10), std::runtime_error); -} - TEST(TransactionBuilder, ThrowsOnTransparentInputWithoutKeyStore) { SelectParams(CBaseChainParams::REGTEST); diff --git a/src/gtest/test_validation.cpp b/src/gtest/test_validation.cpp index e6d68fdaa..06e801b2b 100644 --- a/src/gtest/test_validation.cpp +++ b/src/gtest/test_validation.cpp @@ -8,8 +8,6 @@ #include -extern ZCJoinSplit* params; - extern bool ReceivedBlockTransactions( const CBlock &block, CValidationState& state, @@ -100,14 +98,14 @@ TEST(Validation, ReceivedBlockTransactions) { // Create a fake genesis block CBlock block1; - block1.vtx.push_back(GetValidSproutReceive(*params, sk, 5, true)); + block1.vtx.push_back(GetValidSproutReceive(sk, 5, true)); block1.hashMerkleRoot = BlockMerkleRoot(block1); CBlockIndex fakeIndex1 {block1}; // Create a fake child block CBlock block2; block2.hashPrevBlock = block1.GetHash(); - block2.vtx.push_back(GetValidSproutReceive(*params, sk, 10, true)); + block2.vtx.push_back(GetValidSproutReceive(sk, 10, true)); block2.hashMerkleRoot = BlockMerkleRoot(block2); CBlockIndex fakeIndex2 {block2}; fakeIndex2.pprev = &fakeIndex1; diff --git a/src/init.cpp b/src/init.cpp index c511d0921..6636e2713 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -71,8 +71,6 @@ using namespace boost::placeholders; extern void ThreadSendAlert(); -ZCJoinSplit* pzcashParams = NULL; - bool fFeeEstimatesInitialized = false; static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; @@ -258,8 +256,6 @@ void Shutdown() delete pwalletMain; pwalletMain = NULL; #endif - delete pzcashParams; - pzcashParams = NULL; globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); @@ -821,8 +817,6 @@ static void ZC_LoadParams( return; } - pzcashParams = ZCJoinSplit::Prepared(); - static_assert( sizeof(fs::path::value_type) == sizeof(codeunit), "librustzcash not configured correctly"); diff --git a/src/init.h b/src/init.h index 1f3b1aea2..8e96ff9d6 100644 --- a/src/init.h +++ b/src/init.h @@ -8,8 +8,6 @@ #include -#include "zcash/JoinSplit.hpp" - class CScheduler; class CWallet; @@ -18,8 +16,6 @@ namespace boost class thread_group; } // namespace boost -extern ZCJoinSplit* pzcashParams; - void StartShutdown(); bool ShutdownRequested(); /** Interrupt threads */ diff --git a/src/main.cpp b/src/main.cpp index 1bb9836fe..a5c2920c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -946,7 +946,7 @@ bool ContextualCheckTransaction( bool CheckTransaction(const CTransaction& tx, CValidationState &state, - libzcash::ProofVerifier& verifier) + ProofVerifier& verifier) { // Don't count coinbase transactions because mining skews the count if (!tx.IsCoinBase()) { @@ -958,7 +958,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, } else { // Ensure that zk-SNARKs verify for (const JSDescription &joinsplit : tx.vJoinSplit) { - if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) { + if (!verifier.VerifySprout(joinsplit, tx.joinSplitPubKey)) { return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"), REJECT_INVALID, "bad-txns-joinsplit-verification-failed"); } @@ -1250,7 +1250,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; } - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); if (!CheckTransaction(tx, state, verifier)) return error("AcceptToMemoryPool: CheckTransaction failed"); @@ -2392,8 +2392,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } } - auto verifier = libzcash::ProofVerifier::Strict(); - auto disabledVerifier = libzcash::ProofVerifier::Disabled(); + auto verifier = ProofVerifier::Strict(); + auto disabledVerifier = ProofVerifier::Disabled(); bool fCheckPOW = !fJustCheck && (pindex->nHeight != 0); @@ -3716,7 +3716,7 @@ bool CheckBlockHeader( bool CheckBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, - libzcash::ProofVerifier& verifier, + ProofVerifier& verifier, bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context. @@ -3980,7 +3980,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha } // See method docstring for why this is always disabled - auto verifier = libzcash::ProofVerifier::Disabled(); + auto verifier = ProofVerifier::Disabled(); bool fCheckPOW = (pindex->nHeight != 0); if ((!CheckBlock(block, state, chainparams, verifier, fCheckPOW, true)) || !ContextualCheckBlock(block, state, chainparams, pindex->pprev)) { @@ -4032,7 +4032,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp) { // Preliminary checks - auto verifier = libzcash::ProofVerifier::Disabled(); + auto verifier = ProofVerifier::Disabled(); bool checked = CheckBlock(*pblock, state, chainparams, verifier); { @@ -4072,7 +4072,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; // JoinSplit proofs are verified in ConnectBlock - auto verifier = libzcash::ProofVerifier::Disabled(); + auto verifier = ProofVerifier::Disabled(); // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block.GetBlockHeader(), state, chainparams, pindexPrev, fCheckPOW)) @@ -4471,7 +4471,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nGoodTransactions = 0; CValidationState state; // No need to verify JoinSplits twice - auto verifier = libzcash::ProofVerifier::Disabled(); + auto verifier = ProofVerifier::Disabled(); for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); diff --git a/src/main.h b/src/main.h index 22a8b8ca9..c84964b0a 100644 --- a/src/main.h +++ b/src/main.h @@ -19,6 +19,7 @@ #include "net.h" #include "primitives/block.h" #include "primitives/transaction.h" +#include "proof_verifier.h" #include "script/script.h" #include "script/sigcache.h" #include "script/standard.h" @@ -350,7 +351,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); /** Transaction validation functions */ /** Context-independent validity checks */ -bool CheckTransaction(const CTransaction& tx, CValidationState& state, libzcash::ProofVerifier& verifier); +bool CheckTransaction(const CTransaction& tx, CValidationState& state, ProofVerifier& verifier); bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidationState &state); namespace Consensus { @@ -453,7 +454,7 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); bool CheckBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, - libzcash::ProofVerifier& verifier, + ProofVerifier& verifier, bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** Context-dependent validity checks. diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index ce20a53bc..d306c9c0e 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -9,10 +9,7 @@ #include "tinyformat.h" #include "utilstrencodings.h" -#include "librustzcash.h" - JSDescription::JSDescription( - ZCJoinSplit& params, const uint256& joinSplitPubKey, const uint256& anchor, const std::array& inputs, @@ -25,7 +22,7 @@ JSDescription::JSDescription( { std::array notes; - proof = params.prove( + proof = ZCJoinSplit::prove( inputs, outputs, notes, @@ -45,7 +42,6 @@ JSDescription::JSDescription( } JSDescription JSDescription::Randomized( - ZCJoinSplit& params, const uint256& joinSplitPubKey, const uint256& anchor, std::array& inputs, @@ -69,66 +65,15 @@ JSDescription JSDescription::Randomized( MappedShuffle(outputs.begin(), outputMap.begin(), ZC_NUM_JS_OUTPUTS, gen); return JSDescription( - params, joinSplitPubKey, anchor, inputs, outputs, + joinSplitPubKey, anchor, inputs, outputs, vpub_old, vpub_new, computeProof, esk // payment disclosure ); } -class SproutProofVerifier -{ - ZCJoinSplit& params; - libzcash::ProofVerifier& verifier; - const uint256& joinSplitPubKey; - const JSDescription& jsdesc; - -public: - SproutProofVerifier( - ZCJoinSplit& params, - libzcash::ProofVerifier& verifier, - const uint256& joinSplitPubKey, - const JSDescription& jsdesc - ) : params(params), jsdesc(jsdesc), verifier(verifier), joinSplitPubKey(joinSplitPubKey) {} - - bool operator()(const libzcash::PHGRProof& proof) const - { - // We checkpoint after Sapling activation, so we can skip verification - // for all Sprout proofs. - return true; - } - - bool operator()(const libzcash::GrothProof& proof) const - { - uint256 h_sig = params.h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey); - - return librustzcash_sprout_verify( - proof.begin(), - jsdesc.anchor.begin(), - h_sig.begin(), - jsdesc.macs[0].begin(), - jsdesc.macs[1].begin(), - jsdesc.nullifiers[0].begin(), - jsdesc.nullifiers[1].begin(), - jsdesc.commitments[0].begin(), - jsdesc.commitments[1].begin(), - jsdesc.vpub_old, - jsdesc.vpub_new - ); - } -}; - -bool JSDescription::Verify( - ZCJoinSplit& params, - libzcash::ProofVerifier& verifier, - const uint256& joinSplitPubKey -) const { - auto pv = SproutProofVerifier(params, verifier, joinSplitPubKey, *this); - return std::visit(pv, proof); -} - -uint256 JSDescription::h_sig(ZCJoinSplit& params, const uint256& joinSplitPubKey) const +uint256 JSDescription::h_sig(const uint256& joinSplitPubKey) const { - return params.h_sig(randomSeed, nullifiers, joinSplitPubKey); + return ZCJoinSplit::h_sig(randomSeed, nullifiers, joinSplitPubKey); } std::string COutPoint::ToString() const diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 971d1a4b2..a4cd3e7e2 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -234,7 +234,6 @@ class JSDescription JSDescription(): vpub_old(0), vpub_new(0) { } JSDescription( - ZCJoinSplit& params, const uint256& joinSplitPubKey, const uint256& rt, const std::array& inputs, @@ -246,7 +245,6 @@ class JSDescription ); static JSDescription Randomized( - ZCJoinSplit& params, const uint256& joinSplitPubKey, const uint256& rt, std::array& inputs, @@ -260,15 +258,8 @@ class JSDescription std::function gen = GetRandInt ); - // Verifies that the JoinSplit proof is correct. - bool Verify( - ZCJoinSplit& params, - libzcash::ProofVerifier& verifier, - const uint256& joinSplitPubKey - ) const; - // Returns the calculated h_sig - uint256 h_sig(ZCJoinSplit& params, const uint256& joinSplitPubKey) const; + uint256 h_sig(const uint256& joinSplitPubKey) const; ADD_SERIALIZE_METHODS; diff --git a/src/proof_verifier.cpp b/src/proof_verifier.cpp new file mode 100644 index 000000000..24793785e --- /dev/null +++ b/src/proof_verifier.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2016-2020 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include + +#include + +#include +#include + +class SproutProofVerifier +{ + ProofVerifier& verifier; + const uint256& joinSplitPubKey; + const JSDescription& jsdesc; + +public: + SproutProofVerifier( + ProofVerifier& verifier, + const uint256& joinSplitPubKey, + const JSDescription& jsdesc + ) : jsdesc(jsdesc), verifier(verifier), joinSplitPubKey(joinSplitPubKey) {} + + bool operator()(const libzcash::PHGRProof& proof) const + { + // We checkpoint after Sapling activation, so we can skip verification + // for all Sprout proofs. + return true; + } + + bool operator()(const libzcash::GrothProof& proof) const + { + uint256 h_sig = ZCJoinSplit::h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey); + + return librustzcash_sprout_verify( + proof.begin(), + jsdesc.anchor.begin(), + h_sig.begin(), + jsdesc.macs[0].begin(), + jsdesc.macs[1].begin(), + jsdesc.nullifiers[0].begin(), + jsdesc.nullifiers[1].begin(), + jsdesc.commitments[0].begin(), + jsdesc.commitments[1].begin(), + jsdesc.vpub_old, + jsdesc.vpub_new + ); + } +}; + +ProofVerifier ProofVerifier::Strict() { + return ProofVerifier(true); +} + +ProofVerifier ProofVerifier::Disabled() { + return ProofVerifier(false); +} + +bool ProofVerifier::VerifySprout( + const JSDescription& jsdesc, + const uint256& joinSplitPubKey +) { + if (!perform_verification) { + return true; + } + + auto pv = SproutProofVerifier(*this, joinSplitPubKey, jsdesc); + return std::visit(pv, jsdesc.proof); +} diff --git a/src/proof_verifier.h b/src/proof_verifier.h new file mode 100644 index 000000000..2e22d390c --- /dev/null +++ b/src/proof_verifier.h @@ -0,0 +1,40 @@ +// Copyright (c) 2016-2020 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef ZCASH_PROOF_VERIFIER_H +#define ZCASH_PROOF_VERIFIER_H + +#include +#include + +class ProofVerifier { +private: + bool perform_verification; + + ProofVerifier(bool perform_verification) : perform_verification(perform_verification) { } + +public: + // ProofVerifier should never be copied + ProofVerifier(const ProofVerifier&) = delete; + ProofVerifier& operator=(const ProofVerifier&) = delete; + ProofVerifier(ProofVerifier&&); + ProofVerifier& operator=(ProofVerifier&&); + + // Creates a verification context that strictly verifies + // all proofs. + static ProofVerifier Strict(); + + // Creates a verification context that performs no + // verification, used when avoiding duplicate effort + // such as during reindexing. + static ProofVerifier Disabled(); + + // Verifies that the JoinSplit proof is correct. + bool VerifySprout( + const JSDescription& jsdesc, + const uint256& joinSplitPubKey + ); +}; + +#endif // ZCASH_PROOF_VERIFIER_H diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index e9b0816bd..237b2fd89 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -6,6 +6,7 @@ #include "consensus/validation.h" #include "fs.h" #include "main.h" +#include "proof_verifier.h" #include "test/test_bitcoin.h" #include "utiltime.h" #include "zcash/Proof.hpp" @@ -55,7 +56,7 @@ BOOST_AUTO_TEST_CASE(May15) // After May 15'th, big blocks are OK: forkingBlock.nTime = tMay15; // Invalidates PoW - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); BOOST_CHECK(CheckBlock(forkingBlock, state, Params(), verifier, false, false)); } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 459111f0e..fd6b30fc3 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -35,7 +35,6 @@ const std::function G_TRANSLATION_FUN = nullptr; CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h -ZCJoinSplit *pzcashParams; FastRandomContext insecure_rand_ctx(true); extern bool fPrintToConsole; @@ -43,8 +42,6 @@ extern void noui_connect(); JoinSplitTestingSetup::JoinSplitTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) { - pzcashParams = ZCJoinSplit::Prepared(); - fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params"; fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params"; fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params"; @@ -71,7 +68,6 @@ JoinSplitTestingSetup::JoinSplitTestingSetup(const std::string& chainName) : Bas JoinSplitTestingSetup::~JoinSplitTestingSetup() { - delete pzcashParams; } BasicTestingSetup::BasicTestingSetup(const std::string& chainName) diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 4ffe12133..45bbdd3dd 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -16,6 +16,7 @@ #include "keystore.h" #include "main.h" #include "policy/policy.h" +#include "proof_verifier.h" #include "script/script.h" #include "script/script_error.h" #include "script/sign.h" @@ -108,7 +109,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid))); std::string comment(""); - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); ScriptError err; for (size_t idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; @@ -196,7 +197,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) UniValue tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid))); std::string comment(""); - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); ScriptError err; for (size_t idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; @@ -278,7 +279,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CMutableTransaction tx; stream >> tx; CValidationState state; - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, verifier) && state.IsValid(), "Simple deserialized transaction should be valid."); // Check that duplicate txins fail @@ -369,11 +370,11 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification) libzcash::JSOutput(addr, 50) }; - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); { - JSDescription jsdesc(*pzcashParams, joinSplitPubKey, rt, inputs, outputs, 0, 0); - BOOST_CHECK(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey)); + JSDescription jsdesc(joinSplitPubKey, rt, inputs, outputs, 0, 0); + BOOST_CHECK(verifier.VerifySprout(jsdesc, joinSplitPubKey)); CDataStream ss(SER_DISK, CLIENT_VERSION); auto os = WithVersion(&ss, SAPLING_TX_VERSION | 1 << 31); @@ -383,20 +384,20 @@ BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification) os >> jsdesc_deserialized; BOOST_CHECK(jsdesc_deserialized == jsdesc); - BOOST_CHECK(jsdesc_deserialized.Verify(*pzcashParams, verifier, joinSplitPubKey)); + BOOST_CHECK(verifier.VerifySprout(jsdesc_deserialized, joinSplitPubKey)); } { // Ensure that the balance equation is working. - BOOST_CHECK_THROW(JSDescription(*pzcashParams, joinSplitPubKey, rt, inputs, outputs, 10, 0), std::invalid_argument); - BOOST_CHECK_THROW(JSDescription(*pzcashParams, joinSplitPubKey, rt, inputs, outputs, 0, 10), std::invalid_argument); + BOOST_CHECK_THROW(JSDescription(joinSplitPubKey, rt, inputs, outputs, 10, 0), std::invalid_argument); + BOOST_CHECK_THROW(JSDescription(joinSplitPubKey, rt, inputs, outputs, 0, 10), std::invalid_argument); } { // Ensure that it won't verify if the root is changed. - auto test = JSDescription(*pzcashParams, joinSplitPubKey, rt, inputs, outputs, 0, 0); + auto test = JSDescription(joinSplitPubKey, rt, inputs, outputs, 0, 0); test.anchor = GetRandHash(); - BOOST_CHECK(!test.Verify(*pzcashParams, verifier, joinSplitPubKey)); + BOOST_CHECK(!verifier.VerifySprout(test, joinSplitPubKey)); } } @@ -465,7 +466,7 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransaction tx) { - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); { // Ensure that empty vin/vout remain invalid without // joinsplits. diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index 2421d068a..1c05ee17a 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -5,6 +5,7 @@ #include "transaction_builder.h" #include "main.h" +#include "proof_verifier.h" #include "pubkey.h" #include "rpc/protocol.h" #include "script/sign.h" @@ -50,13 +51,11 @@ TransactionBuilder::TransactionBuilder( const Consensus::Params& consensusParams, int nHeight, CKeyStore* keystore, - ZCJoinSplit* sproutParams, CCoinsViewCache* coinsView, CCriticalSection* cs_coinsView) : consensusParams(consensusParams), nHeight(nHeight), keystore(keystore), - sproutParams(sproutParams), coinsView(coinsView), cs_coinsView(cs_coinsView) { @@ -123,10 +122,6 @@ void TransactionBuilder::AddSproutInput( libzcash::SproutNote note, SproutWitness witness) { - if (sproutParams == nullptr) { - throw std::runtime_error("Cannot add Sprout inputs to a TransactionBuilder without Sprout params"); - } - // Consistency check: all anchors must equal the first one if (!jsInputs.empty()) { if (jsInputs[0].witness.root() != witness.root()) { @@ -142,10 +137,6 @@ void TransactionBuilder::AddSproutOutput( CAmount value, std::array memo) { - if (sproutParams == nullptr) { - throw std::runtime_error("Cannot add Sprout outputs to a TransactionBuilder without Sprout params"); - } - libzcash::JSOutput jsOutput(to, value); jsOutput.memo = memo; jsOutputs.push_back(jsOutput); @@ -567,7 +558,7 @@ void TransactionBuilder::CreateJSDescriptions() // Decrypt the change note's ciphertext to retrieve some data we need ZCNoteDecryption decryptor(changeKey.receiving_key()); - auto hSig = prevJoinSplit.h_sig(*sproutParams, mtx.joinSplitPubKey); + auto hSig = prevJoinSplit.h_sig(mtx.joinSplitPubKey); try { auto plaintext = libzcash::SproutNotePlaintext::decrypt( decryptor, @@ -699,7 +690,6 @@ void TransactionBuilder::CreateJSDescription( // Generate the proof, this can take over a minute. assert(mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)); JSDescription jsdesc = JSDescription::Randomized( - *sproutParams, mtx.joinSplitPubKey, vjsin[0].witness.root(), vjsin, @@ -712,8 +702,8 @@ void TransactionBuilder::CreateJSDescription( &esk); // parameter expects pointer to esk, so pass in address { - auto verifier = libzcash::ProofVerifier::Strict(); - if (!jsdesc.Verify(*sproutParams, verifier, mtx.joinSplitPubKey)) { + auto verifier = ProofVerifier::Strict(); + if (!verifier.VerifySprout(jsdesc, mtx.joinSplitPubKey)) { throw std::runtime_error("error verifying joinsplit"); } } diff --git a/src/transaction_builder.h b/src/transaction_builder.h index 06f5c2fac..204bc115e 100644 --- a/src/transaction_builder.h +++ b/src/transaction_builder.h @@ -74,7 +74,6 @@ class TransactionBuilder Consensus::Params consensusParams; int nHeight; const CKeyStore* keystore; - ZCJoinSplit* sproutParams; const CCoinsViewCache* coinsView; CCriticalSection* cs_coinsView; CMutableTransaction mtx; @@ -96,7 +95,6 @@ class TransactionBuilder const Consensus::Params& consensusParams, int nHeight, CKeyStore* keyStore = nullptr, - ZCJoinSplit* sproutParams = nullptr, CCoinsViewCache* coinsView = nullptr, CCriticalSection* cs_coinsView = nullptr); diff --git a/src/utiltest.cpp b/src/utiltest.cpp index 017dd8abb..31eedf618 100644 --- a/src/utiltest.cpp +++ b/src/utiltest.cpp @@ -10,7 +10,7 @@ #include // Sprout -CMutableTransaction GetValidSproutReceiveTransaction(ZCJoinSplit& params, +CMutableTransaction GetValidSproutReceiveTransaction( const libzcash::SproutSpendingKey& sk, CAmount value, bool randomInputs, @@ -52,7 +52,7 @@ CMutableTransaction GetValidSproutReceiveTransaction(ZCJoinSplit& params, // Prepare JoinSplits uint256 rt; - JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, + JSDescription jsdesc {mtx.joinSplitPubKey, rt, inputs, outputs, 2*value, 0, false}; mtx.vJoinSplit.push_back(jsdesc); @@ -80,30 +80,28 @@ CMutableTransaction GetValidSproutReceiveTransaction(ZCJoinSplit& params, return mtx; } -CWalletTx GetValidSproutReceive(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, +CWalletTx GetValidSproutReceive(const libzcash::SproutSpendingKey& sk, CAmount value, bool randomInputs, uint32_t versionGroupId, /* = SAPLING_VERSION_GROUP_ID */ int32_t version /* = SAPLING_TX_VERSION */) { CMutableTransaction mtx = GetValidSproutReceiveTransaction( - params, sk, value, randomInputs, versionGroupId, version + sk, value, randomInputs, versionGroupId, version ); CTransaction tx {mtx}; CWalletTx wtx {NULL, tx}; return wtx; } -CWalletTx GetInvalidCommitmentSproutReceive(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, +CWalletTx GetInvalidCommitmentSproutReceive(const libzcash::SproutSpendingKey& sk, CAmount value, bool randomInputs, uint32_t versionGroupId, /* = SAPLING_VERSION_GROUP_ID */ int32_t version /* = SAPLING_TX_VERSION */) { CMutableTransaction mtx = GetValidSproutReceiveTransaction( - params, sk, value, randomInputs, versionGroupId, version + sk, value, randomInputs, versionGroupId, version ); mtx.vJoinSplit[0].commitments[0] = uint256(); mtx.vJoinSplit[0].commitments[1] = uint256(); @@ -112,11 +110,10 @@ CWalletTx GetInvalidCommitmentSproutReceive(ZCJoinSplit& params, return wtx; } -libzcash::SproutNote GetSproutNote(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, +libzcash::SproutNote GetSproutNote(const libzcash::SproutSpendingKey& sk, const CTransaction& tx, size_t js, size_t n) { ZCNoteDecryption decryptor {sk.receiving_key()}; - auto hSig = tx.vJoinSplit[js].h_sig(params, tx.joinSplitPubKey); + auto hSig = tx.vJoinSplit[js].h_sig(tx.joinSplitPubKey); auto note_pt = libzcash::SproutNotePlaintext::decrypt( decryptor, tx.vJoinSplit[js].ciphertexts[n], @@ -126,8 +123,7 @@ libzcash::SproutNote GetSproutNote(ZCJoinSplit& params, return note_pt.note(sk.address()); } -CWalletTx GetValidSproutSpend(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, +CWalletTx GetValidSproutSpend(const libzcash::SproutSpendingKey& sk, const libzcash::SproutNote& note, CAmount value) { CMutableTransaction mtx; @@ -178,7 +174,7 @@ CWalletTx GetValidSproutSpend(ZCJoinSplit& params, // Prepare JoinSplits uint256 rt = tree.root(); - JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, + JSDescription jsdesc {mtx.joinSplitPubKey, rt, inputs, outputs, 0, value, false}; mtx.vJoinSplit.push_back(jsdesc); diff --git a/src/utiltest.h b/src/utiltest.h index 168ebf079..100c4bb5d 100644 --- a/src/utiltest.h +++ b/src/utiltest.h @@ -8,7 +8,6 @@ #include "key_io.h" #include "wallet/wallet.h" #include "zcash/Address.hpp" -#include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" #include "zcash/NoteEncryption.hpp" @@ -16,23 +15,19 @@ #include // Sprout -CWalletTx GetValidSproutReceive(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, +CWalletTx GetValidSproutReceive(const libzcash::SproutSpendingKey& sk, CAmount value, bool randomInputs, uint32_t versionGroupId = SAPLING_VERSION_GROUP_ID, int32_t version = SAPLING_TX_VERSION); -CWalletTx GetInvalidCommitmentSproutReceive(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, +CWalletTx GetInvalidCommitmentSproutReceive(const libzcash::SproutSpendingKey& sk, CAmount value, bool randomInputs, uint32_t versionGroupId = SAPLING_VERSION_GROUP_ID, int32_t version = SAPLING_TX_VERSION); -libzcash::SproutNote GetSproutNote(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, +libzcash::SproutNote GetSproutNote(const libzcash::SproutSpendingKey& sk, const CTransaction& tx, size_t js, size_t n); -CWalletTx GetValidSproutSpend(ZCJoinSplit& params, - const libzcash::SproutSpendingKey& sk, +CWalletTx GetValidSproutSpend(const libzcash::SproutSpendingKey& sk, const libzcash::SproutNote& note, CAmount value); diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.cpp b/src/wallet/asyncrpcoperation_mergetoaddress.cpp index d6b9f37d3..0f8fd3cd7 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.cpp +++ b/src/wallet/asyncrpcoperation_mergetoaddress.cpp @@ -15,6 +15,7 @@ #include "miner.h" #include "net.h" #include "netbase.h" +#include "proof_verifier.h" #include "rpc/protocol.h" #include "rpc/server.h" #include "script/interpreter.h" @@ -554,7 +555,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() // Decrypt the change note's ciphertext to retrieve some data we need ZCNoteDecryption decryptor(changeKey.receiving_key()); - auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); + auto hSig = prevJoinSplit.h_sig(tx_.joinSplitPubKey); try { SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( decryptor, @@ -806,7 +807,6 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( assert(mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)); JSDescription jsdesc = JSDescription::Randomized( - *pzcashParams, joinSplitPubKey_, anchor, inputs, @@ -818,8 +818,8 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( !this->testmode, &esk); // parameter expects pointer to esk, so pass in address { - auto verifier = libzcash::ProofVerifier::Strict(); - if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { + auto verifier = ProofVerifier::Strict(); + if (!(verifier.VerifySprout(jsdesc, joinSplitPubKey_))) { throw std::runtime_error("error verifying joinsplit"); } } @@ -858,7 +858,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( ss2 << ((unsigned char)0x00); ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[0]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + ss2 << jsdesc.h_sig(joinSplitPubKey_); encryptedNote1 = HexStr(ss2.begin(), ss2.end()); } @@ -867,7 +867,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( ss2 << ((unsigned char)0x01); ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[1]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + ss2 << jsdesc.h_sig(joinSplitPubKey_); encryptedNote2 = HexStr(ss2.begin(), ss2.end()); } diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 9960758d5..f219f43c2 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -112,7 +112,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { CCoinsViewCache coinsView(pcoinsTip); do { CAmount amountToSend = chooseAmount(availableFunds); - auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain, pzcashParams, &coinsView, &cs_main); + auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain, &coinsView, &cs_main); builder.SetExpiryHeight(targetHeight_ + MIGRATION_EXPIRY_DELTA); LogPrint("zrpcunsafe", "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - FEE)); std::vector fromNotes; diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index b46afa87e..c1c1a2481 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -15,6 +15,7 @@ #include "main.h" #include "net.h" #include "netbase.h" +#include "proof_verifier.h" #include "rpc/protocol.h" #include "rpc/server.h" #include "timedata.h" @@ -700,7 +701,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { // Decrypt the change note's ciphertext to retrieve some data we need ZCNoteDecryption decryptor(std::get(spendingkey_).receiving_key()); - auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); + auto hSig = prevJoinSplit.h_sig(tx_.joinSplitPubKey); try { SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( decryptor, @@ -1082,7 +1083,6 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( assert(mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)); JSDescription jsdesc = JSDescription::Randomized( - *pzcashParams, joinSplitPubKey_, anchor, inputs, @@ -1094,8 +1094,8 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( !this->testmode, &esk); // parameter expects pointer to esk, so pass in address { - auto verifier = libzcash::ProofVerifier::Strict(); - if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { + auto verifier = ProofVerifier::Strict(); + if (!(verifier.VerifySprout(jsdesc, joinSplitPubKey_))) { throw std::runtime_error("error verifying joinsplit"); } } @@ -1138,7 +1138,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( ss2 << ((unsigned char) 0x00); ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[0]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + ss2 << jsdesc.h_sig(joinSplitPubKey_); encryptedNote1 = HexStr(ss2.begin(), ss2.end()); } @@ -1147,7 +1147,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( ss2 << ((unsigned char) 0x01); ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[1]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + ss2 << jsdesc.h_sig(joinSplitPubKey_); encryptedNote2 = HexStr(ss2.begin(), ss2.end()); } diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index 8895ed417..a5c608f8b 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -15,6 +15,7 @@ #include "main.h" #include "net.h" #include "netbase.h" +#include "proof_verifier.h" #include "rpc/protocol.h" #include "rpc/server.h" #include "timedata.h" @@ -309,7 +310,6 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf assert(mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION)); JSDescription jsdesc = JSDescription::Randomized( - *pzcashParams, joinSplitPubKey_, anchor, inputs, @@ -321,8 +321,8 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf !this->testmode, &esk); // parameter expects pointer to esk, so pass in address { - auto verifier = libzcash::ProofVerifier::Strict(); - if (!(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey_))) { + auto verifier = ProofVerifier::Strict(); + if (!(verifier.VerifySprout(jsdesc, joinSplitPubKey_))) { throw std::runtime_error("error verifying joinsplit"); } } @@ -365,7 +365,7 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf ss2 << ((unsigned char) 0x00); ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[0]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + ss2 << jsdesc.h_sig(joinSplitPubKey_); encryptedNote1 = HexStr(ss2.begin(), ss2.end()); } @@ -374,7 +374,7 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf ss2 << ((unsigned char) 0x01); ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[1]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); + ss2 << jsdesc.h_sig(joinSplitPubKey_); encryptedNote2 = HexStr(ss2.begin(), ss2.end()); } diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index 0ae878388..a279935ca 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -13,14 +13,11 @@ #include "transaction_builder.h" #include "utiltest.h" #include "wallet/wallet.h" -#include "zcash/JoinSplit.hpp" #include "zcash/Note.hpp" #include "zcash/NoteEncryption.hpp" using ::testing::Return; -extern ZCJoinSplit* params; - ACTION(ThrowLogicError) { throw std::logic_error("Boom"); } @@ -71,36 +68,6 @@ class TestWallet : public CWallet { } }; -CWalletTx GetValidSproutReceive( - const libzcash::SproutSpendingKey& sk, - CAmount value, - bool randomInputs, - int32_t versionGroupId = SAPLING_VERSION_GROUP_ID, - int32_t version = SAPLING_TX_VERSION) -{ - return GetValidSproutReceive(*params, sk, value, randomInputs, versionGroupId, version); -} - -CWalletTx GetInvalidCommitmentSproutReceive( - const libzcash::SproutSpendingKey& sk, - CAmount value, - bool randomInputs, - int32_t versionGroupId = SAPLING_VERSION_GROUP_ID, - int32_t version = SAPLING_TX_VERSION) -{ - return GetInvalidCommitmentSproutReceive(*params, sk, value, randomInputs, versionGroupId, version); -} - -libzcash::SproutNote GetSproutNote(const libzcash::SproutSpendingKey& sk, - const CTransaction& tx, size_t js, size_t n) { - return GetSproutNote(*params, sk, tx, js, n); -} - -CWalletTx GetValidSproutSpend(const libzcash::SproutSpendingKey& sk, - const libzcash::SproutNote& note, CAmount value) { - return GetValidSproutSpend(*params, sk, note, value); -} - std::vector SetSaplingNoteData(CWalletTx& wtx) { mapSaplingNoteData_t saplingNoteData; SaplingOutPoint saplingOutPoint = {wtx.GetHash(), 0}; @@ -473,8 +440,7 @@ TEST(WalletTests, CheckSproutNoteCommitmentAgainstNotePlaintext) { auto note = GetSproutNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); - auto hSig = wtx.vJoinSplit[0].h_sig( - *params, wtx.joinSplitPubKey); + auto hSig = wtx.vJoinSplit[0].h_sig(wtx.joinSplitPubKey); ASSERT_THROW(wallet.GetSproutNoteNullifier( wtx.vJoinSplit[0], @@ -495,8 +461,7 @@ TEST(WalletTests, GetSproutNoteNullifier) { auto note = GetSproutNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); - auto hSig = wtx.vJoinSplit[0].h_sig( - *params, wtx.joinSplitPubKey); + auto hSig = wtx.vJoinSplit[0].h_sig(wtx.joinSplitPubKey); auto ret = wallet.GetSproutNoteNullifier( wtx.vJoinSplit[0], diff --git a/src/wallet/rpcdisclosure.cpp b/src/wallet/rpcdisclosure.cpp index 8d60b5146..13af5dfa7 100644 --- a/src/wallet/rpcdisclosure.cpp +++ b/src/wallet/rpcdisclosure.cpp @@ -258,7 +258,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp) try { // Decrypt the note to get value and memo field JSDescription jsdesc = tx.vJoinSplit[pd.payload.js]; - uint256 h_sig = jsdesc.h_sig(*pzcashParams, tx.joinSplitPubKey); + uint256 h_sig = jsdesc.h_sig(tx.joinSplitPubKey); ZCPaymentDisclosureNoteDecryption decrypter; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 66285ebc6..7937316e4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -12,6 +12,7 @@ #include "main.h" #include "net.h" #include "netbase.h" +#include "proof_verifier.h" #include "rpc/server.h" #include "timedata.h" #include "transaction_builder.h" @@ -2092,8 +2093,7 @@ UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp) uint256 joinSplitPubKey; uint256 anchor = SproutMerkleTree().root(); - JSDescription samplejoinsplit(*pzcashParams, - joinSplitPubKey, + JSDescription samplejoinsplit(joinSplitPubKey, anchor, {JSInput(), JSInput()}, {JSOutput(), JSOutput()}, @@ -2458,8 +2458,7 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID; mtx.joinSplitPubKey = joinSplitPubKey; - JSDescription jsdesc(*pzcashParams, - joinSplitPubKey, + JSDescription jsdesc(joinSplitPubKey, anchor, {vjsin[0], vjsin[1]}, {vjsout[0], vjsout[1]}, @@ -2467,8 +2466,8 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) vpub_new); { - auto verifier = libzcash::ProofVerifier::Strict(); - assert(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey)); + auto verifier = ProofVerifier::Strict(); + assert(verifier.VerifySprout(jsdesc, joinSplitPubKey)); } mtx.vJoinSplit.push_back(jsdesc); @@ -2503,7 +2502,7 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) ss2 << ((unsigned char) 0x00); ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[0]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey); + ss2 << jsdesc.h_sig(joinSplitPubKey); encryptedNote1 = HexStr(ss2.begin(), ss2.end()); } @@ -2512,7 +2511,7 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) ss2 << ((unsigned char) 0x01); ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ciphertexts[1]; - ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey); + ss2 << jsdesc.h_sig(joinSplitPubKey); encryptedNote2 = HexStr(ss2.begin(), ss2.end()); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c035cacf0..19d7e18d3 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1668,8 +1668,7 @@ bool CWallet::UpdateNullifierNoteMap() if (!item.second.nullifier) { if (GetNoteDecryptor(item.second.address, dec)) { auto i = item.first.js; - auto hSig = wtxItem.second.vJoinSplit[i].h_sig( - *pzcashParams, wtxItem.second.joinSplitPubKey); + auto hSig = wtxItem.second.vJoinSplit[i].h_sig(wtxItem.second.joinSplitPubKey); item.second.nullifier = GetSproutNoteNullifier( wtxItem.second.vJoinSplit[i], item.second.address, @@ -2083,7 +2082,7 @@ mapSproutNoteData_t CWallet::FindMySproutNotes(const CTransaction &tx) const mapSproutNoteData_t noteData; for (size_t i = 0; i < tx.vJoinSplit.size(); i++) { - auto hSig = tx.vJoinSplit[i].h_sig(*pzcashParams, tx.joinSplitPubKey); + auto hSig = tx.vJoinSplit[i].h_sig(tx.joinSplitPubKey); for (uint8_t j = 0; j < tx.vJoinSplit[i].ciphertexts.size(); j++) { for (const NoteDecryptorMap::value_type& item : mapNoteDecryptors) { try { @@ -2529,7 +2528,7 @@ std::pair CWalletTx::DecryptSproutNot EncodePaymentAddress(pa))); } - auto hSig = this->vJoinSplit[jsop.js].h_sig(*pzcashParams, this->joinSplitPubKey); + auto hSig = this->vJoinSplit[jsop.js].h_sig(this->joinSplitPubKey); try { SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( decryptor, @@ -5162,7 +5161,7 @@ void CWallet::GetFilteredNotes( } // determine amount of funds in the note - auto hSig = wtx.vJoinSplit[i].h_sig(*pzcashParams, wtx.joinSplitPubKey); + auto hSig = wtx.vJoinSplit[i].h_sig(wtx.joinSplitPubKey); try { SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( decryptor, diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index a4877ed5f..8f8fe043a 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -9,6 +9,7 @@ #include "fs.h" #include "key_io.h" #include "main.h" +#include "proof_verifier.h" #include "protocol.h" #include "serialize.h" #include "sync.h" @@ -331,7 +332,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletTx wtx; ssValue >> wtx; CValidationState state; - auto verifier = libzcash::ProofVerifier::Strict(); + auto verifier = ProofVerifier::Strict(); if (!(CheckTransaction(wtx, state, verifier) && (wtx.GetHash() == hash) && state.IsValid())) return false; diff --git a/src/zcash/JoinSplit.cpp b/src/zcash/JoinSplit.cpp index 40818c1af..3be599b0c 100644 --- a/src/zcash/JoinSplit.cpp +++ b/src/zcash/JoinSplit.cpp @@ -19,189 +19,178 @@ namespace libzcash { template -class JoinSplitCircuit : public JoinSplit { -public: - JoinSplitCircuit() {} - ~JoinSplitCircuit() {} - - SproutProof prove( - const std::array& inputs, - const std::array& outputs, - std::array& out_notes, - std::array& out_ciphertexts, - uint256& out_ephemeralKey, - const uint256& joinSplitPubKey, - uint256& out_randomSeed, - std::array& out_macs, - std::array& out_nullifiers, - std::array& out_commitments, - uint64_t vpub_old, - uint64_t vpub_new, - const uint256& rt, - bool computeProof, - uint256 *out_esk // Payment disclosure - ) { - if (vpub_old > MAX_MONEY) { - throw std::invalid_argument("nonsensical vpub_old value"); - } +SproutProof JoinSplit::prove( + const std::array& inputs, + const std::array& outputs, + std::array& out_notes, + std::array& out_ciphertexts, + uint256& out_ephemeralKey, + const uint256& joinSplitPubKey, + uint256& out_randomSeed, + std::array& out_macs, + std::array& out_nullifiers, + std::array& out_commitments, + uint64_t vpub_old, + uint64_t vpub_new, + const uint256& rt, + bool computeProof, + uint256 *out_esk // Payment disclosure +) { + if (vpub_old > MAX_MONEY) { + throw std::invalid_argument("nonsensical vpub_old value"); + } - if (vpub_new > MAX_MONEY) { - throw std::invalid_argument("nonsensical vpub_new value"); - } + if (vpub_new > MAX_MONEY) { + throw std::invalid_argument("nonsensical vpub_new value"); + } - uint64_t lhs_value = vpub_old; - uint64_t rhs_value = vpub_new; - - for (size_t i = 0; i < NumInputs; i++) { - // Sanity checks of input - { - // If note has nonzero value - if (inputs[i].note.value() != 0) { - // The witness root must equal the input root. - if (inputs[i].witness.root() != rt) { - throw std::invalid_argument("joinsplit not anchored to the correct root"); - } - - // The tree must witness the correct element - if (inputs[i].note.cm() != inputs[i].witness.element()) { - throw std::invalid_argument("witness of wrong element for joinsplit input"); - } - } + uint64_t lhs_value = vpub_old; + uint64_t rhs_value = vpub_new; - // Ensure we have the key to this note. - if (inputs[i].note.a_pk != inputs[i].key.address().a_pk) { - throw std::invalid_argument("input note not authorized to spend with given key"); + for (size_t i = 0; i < NumInputs; i++) { + // Sanity checks of input + { + // If note has nonzero value + if (inputs[i].note.value() != 0) { + // The witness root must equal the input root. + if (inputs[i].witness.root() != rt) { + throw std::invalid_argument("joinsplit not anchored to the correct root"); } - // Balance must be sensical - if (inputs[i].note.value() > MAX_MONEY) { - throw std::invalid_argument("nonsensical input note value"); + // The tree must witness the correct element + if (inputs[i].note.cm() != inputs[i].witness.element()) { + throw std::invalid_argument("witness of wrong element for joinsplit input"); } + } - lhs_value += inputs[i].note.value(); + // Ensure we have the key to this note. + if (inputs[i].note.a_pk != inputs[i].key.address().a_pk) { + throw std::invalid_argument("input note not authorized to spend with given key"); + } - if (lhs_value > MAX_MONEY) { - throw std::invalid_argument("nonsensical left hand size of joinsplit balance"); - } + // Balance must be sensical + if (inputs[i].note.value() > MAX_MONEY) { + throw std::invalid_argument("nonsensical input note value"); } - // Compute nullifier of input - out_nullifiers[i] = inputs[i].nullifier(); + lhs_value += inputs[i].note.value(); + + if (lhs_value > MAX_MONEY) { + throw std::invalid_argument("nonsensical left hand size of joinsplit balance"); + } } - // Sample randomSeed - out_randomSeed = random_uint256(); + // Compute nullifier of input + out_nullifiers[i] = inputs[i].nullifier(); + } - // Compute h_sig - uint256 h_sig = this->h_sig(out_randomSeed, out_nullifiers, joinSplitPubKey); + // Sample randomSeed + out_randomSeed = random_uint256(); - // Sample phi - uint252 phi = random_uint252(); + // Compute h_sig + uint256 h_sig = JoinSplit::h_sig( + out_randomSeed, out_nullifiers, joinSplitPubKey); - // Compute notes for outputs - for (size_t i = 0; i < NumOutputs; i++) { - // Sanity checks of output - { - if (outputs[i].value > MAX_MONEY) { - throw std::invalid_argument("nonsensical output value"); - } - - rhs_value += outputs[i].value; + // Sample phi + uint252 phi = random_uint252(); - if (rhs_value > MAX_MONEY) { - throw std::invalid_argument("nonsensical right hand side of joinsplit balance"); - } + // Compute notes for outputs + for (size_t i = 0; i < NumOutputs; i++) { + // Sanity checks of output + { + if (outputs[i].value > MAX_MONEY) { + throw std::invalid_argument("nonsensical output value"); } - // Sample r - uint256 r = random_uint256(); + rhs_value += outputs[i].value; - out_notes[i] = outputs[i].note(phi, r, i, h_sig); + if (rhs_value > MAX_MONEY) { + throw std::invalid_argument("nonsensical right hand side of joinsplit balance"); + } } - if (lhs_value != rhs_value) { - throw std::invalid_argument("invalid joinsplit balance"); - } + // Sample r + uint256 r = random_uint256(); - // Compute the output commitments - for (size_t i = 0; i < NumOutputs; i++) { - out_commitments[i] = out_notes[i].cm(); - } + out_notes[i] = outputs[i].note(phi, r, i, h_sig); + } - // Encrypt the ciphertexts containing the note - // plaintexts to the recipients of the value. - { - ZCNoteEncryption encryptor(h_sig); + if (lhs_value != rhs_value) { + throw std::invalid_argument("invalid joinsplit balance"); + } - for (size_t i = 0; i < NumOutputs; i++) { - SproutNotePlaintext pt(out_notes[i], outputs[i].memo); + // Compute the output commitments + for (size_t i = 0; i < NumOutputs; i++) { + out_commitments[i] = out_notes[i].cm(); + } - out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc); - } + // Encrypt the ciphertexts containing the note + // plaintexts to the recipients of the value. + { + ZCNoteEncryption encryptor(h_sig); - out_ephemeralKey = encryptor.get_epk(); + for (size_t i = 0; i < NumOutputs; i++) { + SproutNotePlaintext pt(out_notes[i], outputs[i].memo); - // !!! Payment disclosure START - if (out_esk != nullptr) { - *out_esk = encryptor.get_esk(); - } - // !!! Payment disclosure END + out_ciphertexts[i] = pt.encrypt(encryptor, outputs[i].addr.pk_enc); } - // Authenticate h_sig with each of the input - // spending keys, producing macs which protect - // against malleability. - for (size_t i = 0; i < NumInputs; i++) { - out_macs[i] = PRF_pk(inputs[i].key, i, h_sig); - } + out_ephemeralKey = encryptor.get_epk(); - if (!computeProof) { - return GrothProof(); + // !!! Payment disclosure START + if (out_esk != nullptr) { + *out_esk = encryptor.get_esk(); } + // !!! Payment disclosure END + } - GrothProof proof; - - CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION); - ss1 << inputs[0].witness.path(); - std::vector auth1(ss1.begin(), ss1.end()); - - CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); - ss2 << inputs[1].witness.path(); - std::vector auth2(ss2.begin(), ss2.end()); - - librustzcash_sprout_prove( - proof.begin(), - phi.begin(), - rt.begin(), - h_sig.begin(), - inputs[0].key.begin(), - inputs[0].note.value(), - inputs[0].note.rho.begin(), - inputs[0].note.r.begin(), - auth1.data(), - inputs[1].key.begin(), - inputs[1].note.value(), - inputs[1].note.rho.begin(), - inputs[1].note.r.begin(), - auth2.data(), - out_notes[0].a_pk.begin(), - out_notes[0].value(), - out_notes[0].r.begin(), - out_notes[1].a_pk.begin(), - out_notes[1].value(), - out_notes[1].r.begin(), - vpub_old, - vpub_new - ); - - return proof; + // Authenticate h_sig with each of the input + // spending keys, producing macs which protect + // against malleability. + for (size_t i = 0; i < NumInputs; i++) { + out_macs[i] = PRF_pk(inputs[i].key, i, h_sig); } -}; -template -JoinSplit* JoinSplit::Prepared() -{ - return new JoinSplitCircuit(); + if (!computeProof) { + return GrothProof(); + } + + GrothProof proof; + + CDataStream ss1(SER_NETWORK, PROTOCOL_VERSION); + ss1 << inputs[0].witness.path(); + std::vector auth1(ss1.begin(), ss1.end()); + + CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); + ss2 << inputs[1].witness.path(); + std::vector auth2(ss2.begin(), ss2.end()); + + librustzcash_sprout_prove( + proof.begin(), + phi.begin(), + rt.begin(), + h_sig.begin(), + inputs[0].key.begin(), + inputs[0].note.value(), + inputs[0].note.rho.begin(), + inputs[0].note.r.begin(), + auth1.data(), + inputs[1].key.begin(), + inputs[1].note.value(), + inputs[1].note.rho.begin(), + inputs[1].note.r.begin(), + auth2.data(), + out_notes[0].a_pk.begin(), + out_notes[0].value(), + out_notes[0].r.begin(), + out_notes[1].a_pk.begin(), + out_notes[1].value(), + out_notes[1].r.begin(), + vpub_old, + vpub_new + ); + + return proof; } template diff --git a/src/zcash/JoinSplit.hpp b/src/zcash/JoinSplit.hpp index 9018c2561..8aa2b5b43 100644 --- a/src/zcash/JoinSplit.hpp +++ b/src/zcash/JoinSplit.hpp @@ -16,18 +16,6 @@ namespace libzcash { -static constexpr size_t GROTH_PROOF_SIZE = ( - 48 + // π_A - 96 + // π_B - 48); // π_C - -typedef std::array GrothProof; - -// TODO: Because PHGRProof is listed first, using the default -// constructor for JSDescription() will create a JSDescription -// with a PHGRProof. The default however should be GrothProof. -typedef std::variant SproutProof; - class JSInput { public: SproutWitness witness; @@ -59,17 +47,13 @@ class JSOutput { template class JoinSplit { public: - virtual ~JoinSplit() {} - - static JoinSplit* Prepared(); - static uint256 h_sig(const uint256& randomSeed, const std::array& nullifiers, const uint256& joinSplitPubKey ); // Compute nullifiers, macs, note commitments & encryptions, and SNARK proof - virtual SproutProof prove( + static SproutProof prove( const std::array& inputs, const std::array& outputs, std::array& out_notes, @@ -88,10 +72,7 @@ class JoinSplit { // Reference as non-const parameter with default value leads to compile error. // So use pointer for simplicity. uint256 *out_esk = nullptr - ) = 0; - -protected: - JoinSplit() {} + ); }; } diff --git a/src/zcash/Proof.cpp b/src/zcash/Proof.cpp deleted file mode 100644 index 00cf40b11..000000000 --- a/src/zcash/Proof.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "Proof.hpp" - -#include "crypto/common.h" - -#include - -namespace libzcash { - -ProofVerifier ProofVerifier::Strict() { - return ProofVerifier(true); -} - -ProofVerifier ProofVerifier::Disabled() { - return ProofVerifier(false); -} - -} diff --git a/src/zcash/Proof.hpp b/src/zcash/Proof.hpp index 52a6df04d..fa6069581 100644 --- a/src/zcash/Proof.hpp +++ b/src/zcash/Proof.hpp @@ -202,42 +202,16 @@ class PHGRProof { } }; -void initialize_curve_params(); - -class ProofVerifier { -private: - bool perform_verification; - - ProofVerifier(bool perform_verification) : perform_verification(perform_verification) { } - -public: - // ProofVerifier should never be copied - ProofVerifier(const ProofVerifier&) = delete; - ProofVerifier& operator=(const ProofVerifier&) = delete; - ProofVerifier(ProofVerifier&&); - ProofVerifier& operator=(ProofVerifier&&); - - // Creates a verification context that strictly verifies - // all proofs using libsnark's API. - static ProofVerifier Strict(); - - // Creates a verification context that performs no - // verification, used when avoiding duplicate effort - // such as during reindexing. - static ProofVerifier Disabled(); - - template - bool check( - const VerificationKey& vk, - const ProcessedVerificationKey& pvk, - const PrimaryInput& pi, - const Proof& p - ); -}; +static constexpr size_t GROTH_PROOF_SIZE = ( + 48 + // π_A + 96 + // π_B + 48); // π_C + +typedef std::array GrothProof; +// TODO: Because PHGRProof is listed first, using the default +// constructor for JSDescription() will create a JSDescription +// with a PHGRProof. The default however should be GrothProof. +typedef std::variant SproutProof; } diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 13013ba0f..8136296a4 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -18,6 +18,7 @@ #include "miner.h" #include "policy/policy.h" #include "pow.h" +#include "proof_verifier.h" #include "rpc/server.h" #include "script/sign.h" #include "sodium.h" @@ -100,8 +101,7 @@ double benchmark_create_joinsplit() struct timeval tv_start; timer_start(tv_start); - JSDescription jsdesc(*pzcashParams, - joinSplitPubKey, + JSDescription jsdesc(joinSplitPubKey, anchor, {JSInput(), JSInput()}, {JSOutput(), JSOutput()}, @@ -109,8 +109,8 @@ double benchmark_create_joinsplit() 0); double ret = timer_stop(tv_start); - auto verifier = libzcash::ProofVerifier::Strict(); - assert(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey)); + auto verifier = ProofVerifier::Strict(); + assert(verifier.VerifySprout(jsdesc, joinSplitPubKey)); return ret; } @@ -140,8 +140,8 @@ double benchmark_verify_joinsplit(const JSDescription &joinsplit) struct timeval tv_start; timer_start(tv_start); uint256 joinSplitPubKey; - auto verifier = libzcash::ProofVerifier::Strict(); - joinsplit.Verify(*pzcashParams, verifier, joinSplitPubKey); + auto verifier = ProofVerifier::Strict(); + verifier.VerifySprout(joinsplit, joinSplitPubKey); return timer_stop(tv_start); } @@ -278,7 +278,7 @@ double benchmark_try_decrypt_sprout_notes(size_t nKeys) } auto sk = libzcash::SproutSpendingKey::random(); - auto tx = GetValidSproutReceive(*pzcashParams, sk, 10, true); + auto tx = GetValidSproutReceive(sk, 10, true); struct timeval tv_start; timer_start(tv_start); @@ -314,8 +314,8 @@ double benchmark_try_decrypt_sapling_notes(size_t nKeys) } CWalletTx CreateSproutTxWithNoteData(const libzcash::SproutSpendingKey& sk) { - auto wtx = GetValidSproutReceive(*pzcashParams, sk, 10, true); - auto note = GetSproutNote(*pzcashParams, sk, wtx, 0, 1); + auto wtx = GetValidSproutReceive(sk, 10, true); + auto note = GetSproutNote(sk, wtx, 0, 1); auto nullifier = note.nullifier(sk); mapSproutNoteData_t noteDataMap; From d715390fdd5e599cc8054b4cea359b8e46d5d0e7 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 16:13:24 +0000 Subject: [PATCH 071/146] Bitcoin 0.12 misc P2P/Net PRs --- doc/release-notes.md | 21 ++++++ src/Makefile.gtest.include | 9 +-- src/Makefile.test.include | 2 +- src/addrman.cpp | 2 +- src/addrman.h | 10 +-- src/alert.cpp | 4 +- src/chain.h | 4 +- src/gtest/test_timedata.cpp | 99 ++++++++++++++++++++++++++ src/init.cpp | 25 ++++--- src/limitedmap.h | 19 ++--- src/main.cpp | 37 +++++++--- src/mempool_limit.cpp | 4 +- src/metrics.cpp | 2 +- src/miner.cpp | 4 +- src/net.cpp | 8 +-- src/net.h | 1 + src/rpc/misc.cpp | 5 +- src/rpc/net.cpp | 5 +- src/test/addrman_tests.cpp | 12 ++-- src/test/limitedmap_tests.cpp | 101 +++++++++++++++++++++++++++ src/test/timedata_tests.cpp | 39 ----------- src/timedata.cpp | 128 +++++++++++----------------------- src/timedata.h | 75 +++++--------------- src/wallet/wallet.cpp | 4 +- 24 files changed, 370 insertions(+), 250 deletions(-) create mode 100644 src/gtest/test_timedata.cpp create mode 100644 src/test/limitedmap_tests.cpp delete mode 100644 src/test/timedata_tests.cpp diff --git a/doc/release-notes.md b/doc/release-notes.md index 5a8809021..17e3b4a86 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -23,6 +23,27 @@ calculating the target. A more detailed documentation about keeping traffic low can be found in [/doc/reducetraffic.md](/doc/reducetraffic.md). +Removal of time adjustment and the -maxtimeadjustment= option +------------------------------------------------------------- + +Prior to v2.1.0, `bitcoinzd` would adjust the local time that it used by up +to 70 minutes, according to a median of the times sent by the first 200 peers +to connect to it. This mechanism was inherently insecure, since an adversary +making multiple connections to the node could effectively control its time +within that +/- 70 minute window (this is called a "timejacking attack"). + +In the v2.1.0 security release, in addition to other mitigations for +timejacking attacks, the maximum time adjustment was set to zero by default. +This effectively disabled time adjustment; however, a `-maxtimeadjustment=` +option was provided to override this default. + +As a simplification the time adjustment code has now been completely removed, +together with `-maxtimeadjustment=`. Node operators should instead simply +ensure that local time is set reasonably accurately. + +If it appears that the node has a significantly different time than its peers, +a warning will still be logged and indicated on the metrics screen if enabled. + Fixes ----- diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 675f20577..00eb2bce4 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -27,6 +27,7 @@ bitcoinz_gtest_SOURCES += \ endif bitcoinz_gtest_SOURCES += \ gtest/test_tautology.cpp \ + gtest/test_checkblock.cpp \ gtest/test_deprecation.cpp \ gtest/test_dynamicusage.cpp \ gtest/test_equihash.cpp \ @@ -34,24 +35,24 @@ bitcoinz_gtest_SOURCES += \ gtest/test_joinsplit.cpp \ gtest/test_keys.cpp \ gtest/test_keystore.cpp \ + gtest/test_libzcash_utils.cpp \ gtest/test_noteencryption.cpp \ gtest/test_mempool.cpp \ gtest/test_mempoollimit.cpp \ gtest/test_merkletree.cpp \ gtest/test_metrics.cpp \ gtest/test_miner.cpp \ + gtest/test_pedersen_hash.cpp \ gtest/test_pow.cpp \ gtest/test_random.cpp \ gtest/test_rpc.cpp \ gtest/test_sapling_note.cpp \ + gtest/test_timedata.cpp \ gtest/test_transaction.cpp \ gtest/test_transaction_builder.cpp \ + gtest/test_txid.cpp \ gtest/test_upgrades.cpp \ gtest/test_validation.cpp \ - gtest/test_txid.cpp \ - gtest/test_libzcash_utils.cpp \ - gtest/test_pedersen_hash.cpp \ - gtest/test_checkblock.cpp \ gtest/test_zip32.cpp if ENABLE_WALLET bitcoinz_gtest_SOURCES += \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index d5ded9e72..8baaa8326 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -74,6 +74,7 @@ BITCOIN_TESTS =\ test/getarg_tests.cpp \ test/hash_tests.cpp \ test/key_tests.cpp \ + test/limitedmap_tests.cpp \ test/dbwrapper_tests.cpp \ test/main_tests.cpp \ test/mempool_tests.cpp \ @@ -103,7 +104,6 @@ BITCOIN_TESTS =\ test/streams_tests.cpp \ test/test_bitcoin.cpp \ test/test_bitcoin.h \ - test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ test/txvalidationcache_tests.cpp \ diff --git a/src/addrman.cpp b/src/addrman.cpp index 33b0c5481..c1ebea0a2 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -253,7 +253,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP if (pinfo) { // periodically update nTime - bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); + bool fCurrentlyOnline = (GetTime() - addr.nTime < 24 * 60 * 60); int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty); diff --git a/src/addrman.h b/src/addrman.h index 4acf02f9f..6a7c1634a 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -97,10 +97,10 @@ class CAddrInfo : public CAddress int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const; //! Determine whether the statistics about this entry are bad enough so that it can just be deleted - bool IsTerrible(int64_t nNow = GetAdjustedTime()) const; + bool IsTerrible(int64_t nNow = GetTime()) const; //! Calculate the relative chance this entry should be given when selecting nodes to connect to - double GetChance(int64_t nNow = GetAdjustedTime()) const; + double GetChance(int64_t nNow = GetTime()) const; }; @@ -515,7 +515,7 @@ class CAddrMan } //! Mark an entry as accessible. - void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Good(const CService &addr, int64_t nTime = GetTime()) { LOCK(cs); Check(); @@ -524,7 +524,7 @@ class CAddrMan } //! Mark an entry as connection attempted to. - void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Attempt(const CService &addr, int64_t nTime = GetTime()) { LOCK(cs); Check(); @@ -561,7 +561,7 @@ class CAddrMan } //! Mark an entry as currently-connected-to. - void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Connected(const CService &addr, int64_t nTime = GetTime()) { LOCK(cs); Check(); diff --git a/src/alert.cpp b/src/alert.cpp index 10d81a500..9014a222e 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -101,7 +101,7 @@ uint256 CAlert::GetHash() const bool CAlert::IsInEffect() const { - return (GetAdjustedTime() < nExpiration); + return (GetTime() < nExpiration); } bool CAlert::Cancels(const CAlert& alert) const @@ -154,7 +154,7 @@ bool CAlert::RelayTo(CNode* pnode) const { if (AppliesTo(pnode->nVersion, pnode->strSubVer) || AppliesToMe() || - GetAdjustedTime() < nRelayUntil) + GetTime() < nRelayUntil) { pnode->PushMessage("alert", *this); return true; diff --git a/src/chain.h b/src/chain.h index 3cdb8494d..25af8e5b3 100644 --- a/src/chain.h +++ b/src/chain.h @@ -21,7 +21,7 @@ static const int SAPLING_VALUE_VERSION = 1010100; /** * Maximum amount of time that a block timestamp is allowed to exceed the - * current network-adjusted time before the block will be accepted. + * current local time. */ static const int64_t MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60; @@ -31,7 +31,7 @@ static const int64_t MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60; * to block timestamps. This should be set at least as high as * MAX_FUTURE_BLOCK_TIME. */ -static const int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME; +static const int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME + 60; class CBlockFileInfo { diff --git a/src/gtest/test_timedata.cpp b/src/gtest/test_timedata.cpp new file mode 100644 index 000000000..10a275762 --- /dev/null +++ b/src/gtest/test_timedata.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2020 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include +#include + +#include "timedata.h" +#include "random.h" +#include "netbase.h" + +using ::testing::StrictMock; + +class MockCTimeWarning : public CTimeWarning +{ +public: + MOCK_METHOD2(Warn, void(size_t, size_t)); +}; + +CNetAddr GetUniqueAddr() { + uint8_t buf[16] = { 0xFD }; // RFC 4193 address in FC00::/7 with L = 1 (locally assigned) + GetRandBytes(&buf[1], 15); + CNetAddr ip; + ip.SetRaw(NET_IPV6, buf); + assert(ip.IsRFC4193()); + return ip; +} + +TEST(TimeWarning, Assertions) +{ + StrictMock tw; + EXPECT_DEATH(tw.AddTimeData(GetUniqueAddr(), 0, INT64_MIN), ""); + EXPECT_DEATH(tw.AddTimeData(GetUniqueAddr(), 0, -1), ""); + EXPECT_DEATH(tw.AddTimeData(GetUniqueAddr(), 0, INT64_MAX - CTimeWarning::TIMEDATA_IGNORE_THRESHOLD + 1), ""); + EXPECT_DEATH(tw.AddTimeData(GetUniqueAddr(), 0, INT64_MAX), ""); +} + +TEST(TimeWarning, NoWarning) +{ + StrictMock tw; + int64_t now = GetTime(); + + EXPECT_CALL(tw, Warn(CTimeWarning::TIMEDATA_WARNING_SAMPLES, 0)).Times(0); + + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_IGNORE_THRESHOLD, now); + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_IGNORE_THRESHOLD, now); + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_WARNING_THRESHOLD, now); + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD, now); + + for (size_t i = 0; i < CTimeWarning::TIMEDATA_WARNING_SAMPLES - 2; i++) { + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD + 1, now); + } + CNetAddr duplicateIp = GetUniqueAddr(); + for (size_t i = 0; i < 2; i++) { + tw.AddTimeData(duplicateIp, now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD + 1, now); + } +} + +TEST(TimeWarning, PeersAhead) +{ + StrictMock tw; + int64_t now = GetTime(); + + EXPECT_CALL(tw, Warn(CTimeWarning::TIMEDATA_WARNING_SAMPLES, 0)); + + for (size_t i = 0; i < CTimeWarning::TIMEDATA_WARNING_SAMPLES - 1; i++) { + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD + 1, now); + } + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_IGNORE_THRESHOLD - 1, now); +} + +TEST(TimeWarning, PeersBehind) +{ + StrictMock tw; + int64_t now = GetTime(); + + EXPECT_CALL(tw, Warn(0, CTimeWarning::TIMEDATA_WARNING_SAMPLES)); + + for (size_t i = 0; i < CTimeWarning::TIMEDATA_WARNING_SAMPLES - 1; i++) { + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_WARNING_THRESHOLD - 1, now); + } + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_IGNORE_THRESHOLD + 1, now); +} + +TEST(TimeWarning, PeersMixed) +{ + StrictMock tw; + int64_t now = GetTime(); + + EXPECT_CALL(tw, Warn(CTimeWarning::TIMEDATA_WARNING_SAMPLES/2, (CTimeWarning::TIMEDATA_WARNING_SAMPLES+1)/2)); + + for (size_t i = 0; i < CTimeWarning::TIMEDATA_WARNING_SAMPLES/2; i++) { + tw.AddTimeData(GetUniqueAddr(), now + CTimeWarning::TIMEDATA_WARNING_THRESHOLD + 1, now); + } + for (size_t i = 0; i < (CTimeWarning::TIMEDATA_WARNING_SAMPLES-1)/2; i++) { + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_WARNING_THRESHOLD - 1, now); + } + tw.AddTimeData(GetUniqueAddr(), now - CTimeWarning::TIMEDATA_IGNORE_THRESHOLD + 1, now); +} diff --git a/src/init.cpp b/src/init.cpp index 6636e2713..8d6c46ae9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1016,16 +1016,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) return InitError(err.value()); } - // Make sure enough file descriptors are available - int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); - nMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); - nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); - int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); - if (nFD < MIN_CORE_FILEDESCRIPTORS) - return InitError(_("Not enough file descriptors available.")); - if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections) - nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS; - // if using block pruning, then disable txindex if (GetArg("-prune", 0)) { if (GetBoolArg("-txindex", DEFAULT_TXINDEX)) @@ -1037,6 +1027,21 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #endif } + // Make sure enough file descriptors are available + int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); + int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); + nMaxConnections = std::max(nUserMaxConnections, 0); + + // Trim requested connection counts, to fit into system limitations + nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS), 0); + int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); + if (nFD < MIN_CORE_FILEDESCRIPTORS) + return InitError(_("Not enough file descriptors available.")); + nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS, nMaxConnections); + + if (nMaxConnections < nUserMaxConnections) + InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections)); + // ********************************************************* Step 3: parameter-to-internal-flags fDebug = !mapMultiArgs["-debug"].empty(); diff --git a/src/limitedmap.h b/src/limitedmap.h index 40f6ff7e3..85e254440 100644 --- a/src/limitedmap.h +++ b/src/limitedmap.h @@ -27,7 +27,11 @@ class limitedmap size_type nMaxSize; public: - limitedmap(size_type nMaxSizeIn = 0) { nMaxSize = nMaxSizeIn; } + limitedmap(size_type nMaxSizeIn) + { + assert(nMaxSizeIn > 0); + nMaxSize = nMaxSizeIn; + } const_iterator begin() const { return map.begin(); } const_iterator end() const { return map.end(); } size_type size() const { return map.size(); } @@ -38,13 +42,12 @@ class limitedmap { std::pair ret = map.insert(x); if (ret.second) { - if (nMaxSize && map.size() == nMaxSize) { + if (map.size() > nMaxSize) { map.erase(rmap.begin()->second); rmap.erase(rmap.begin()); } rmap.insert(make_pair(x.second, ret.first)); } - return; } void erase(const key_type& k) { @@ -84,11 +87,11 @@ class limitedmap size_type max_size() const { return nMaxSize; } size_type max_size(size_type s) { - if (s) - while (map.size() > s) { - map.erase(rmap.begin()->second); - rmap.erase(rmap.begin()); - } + assert(s > 0); + while (map.size() > s) { + map.erase(rmap.begin()->second); + rmap.erase(rmap.begin()); + } nMaxSize = s; return nMaxSize; } diff --git a/src/main.cpp b/src/main.cpp index a5c2920c2..54ccc5ee2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2015-2020 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -722,7 +723,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // However this changes once median past time-locks are enforced: const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? chainActive.Tip()->GetMedianTimePast() - : GetAdjustedTime(); + : GetTime(); return IsFinalTx(tx, nBlockHeight, nBlockTime); } @@ -3815,7 +3816,7 @@ bool ContextualCheckBlockHeader( REJECT_INVALID, "time-too-old"); // Check timestamp - auto nTimeLimit = GetAdjustedTime() + Params().GetFutureBlockTimeWindow(nHeight); + auto nTimeLimit = GetTime() + Params().GetFutureBlockTimeWindow(nHeight); if (block.GetBlockTime() > nTimeLimit) { return state.Invalid(error("%s: block at height %d, timestamp %d is too far ahead of local time, limit is %d", __func__, nHeight, block.GetBlockTime(), nTimeLimit), @@ -5446,9 +5447,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, remoteAddr); - int64_t nTimeOffset = nTime - GetTime(); - pfrom->nTimeOffset = nTimeOffset; - AddTimeData(pfrom->addr, nTimeOffset); + pfrom->nTimeOffset = timeWarning.AddTimeData(pfrom->addr, nTime, GetTime()); } @@ -5506,7 +5505,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Store the new addresses vector vAddrOk; - int64_t nNow = GetAdjustedTime(); + int64_t nNow = GetTime(); int64_t nSince = nNow - 10 * 60; for (CAddress& addr : vAddr) { @@ -5598,7 +5597,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash); CNodeState *nodestate = State(pfrom->GetId()); - if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - chainparams.GetConsensus().PoWTargetSpacing(pindexBestHeader->nHeight) * 20 && + if (chainActive.Tip()->GetBlockTime() > GetTime() - chainparams.GetConsensus().PoWTargetSpacing(pindexBestHeader->nHeight) * 20 && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { vToFetch.push_back(inv); // Mark block as in flight already, even though the actual "getdata" message only goes out @@ -5704,9 +5703,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); - if (IsInitialBlockDownload(chainparams)) + if (IsInitialBlockDownload(chainparams) && !pfrom->fWhitelisted) { + LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id); return true; - + } CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -5994,6 +5994,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == "mempool") { int currentHeight = GetHeight(); + if (CNode::OutboundTargetReached(chainparams.GetConsensus().PoWTargetSpacing(currentHeight), false) && !pfrom->fWhitelisted) + { + LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); + pfrom->fDisconnect = true; + return true; + } LOCK2(cs_main, pfrom->cs_filter); @@ -6481,10 +6487,19 @@ bool SendMessages(CNode* pto, bool fSendTrickle) bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) { // Only actively request headers from a single peer, unless we're close to today. - if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { + if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetTime() - 24 * 60 * 60) { state.fSyncStarted = true; nSyncStarted++; - CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; + const CBlockIndex *pindexStart = pindexBestHeader; + /* If possible, start at the block preceding the currently + best known header. This ensures that we always get a + non-empty list of headers back as long as the peer + is up-to-date. With a non-empty response, we can initialise + the peer's known best block. This wouldn't be possible + if we requested starting at pindexBestHeader and + got back an empty response. */ + if (pindexStart->pprev) + pindexStart = pindexStart->pprev; LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256()); } diff --git a/src/mempool_limit.cpp b/src/mempool_limit.cpp index 1f31aab60..31db349a4 100644 --- a/src/mempool_limit.cpp +++ b/src/mempool_limit.cpp @@ -17,7 +17,7 @@ void RecentlyEvictedList::pruneList() if (txIdSet.empty()) { return; } - int64_t now = GetAdjustedTime(); + int64_t now = GetTime(); while (txIdsAndTimes.size() > 0 && now - txIdsAndTimes.front().second > timeToKeep) { txIdSet.erase(txIdsAndTimes.front().first); txIdsAndTimes.pop_front(); @@ -31,7 +31,7 @@ void RecentlyEvictedList::add(const uint256& txId) txIdSet.erase(txIdsAndTimes.front().first); txIdsAndTimes.pop_front(); } - txIdsAndTimes.push_back(std::make_pair(txId, GetAdjustedTime())); + txIdsAndTimes.push_back(std::make_pair(txId, GetTime())); txIdSet.insert(txId); } diff --git a/src/metrics.cpp b/src/metrics.cpp index 6013f65ef..ccb0ef425 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -124,7 +124,7 @@ std::string WhichNetwork() int EstimateNetHeight(const Consensus::Params& params, int currentHeadersHeight, int64_t currentHeadersTime) { - int64_t now = GetAdjustedTime(); + int64_t now = GetTime(); if (currentHeadersTime >= now) { return currentHeadersHeight; } diff --git a/src/miner.cpp b/src/miner.cpp index cf1060d3a..4ff9c787e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -97,7 +97,7 @@ class TxPriorityCompare void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { - pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetTime()); // Updating time can change work required on testnet: if (consensusParams.nPowAllowMinDifficultyBlocksAfterHeight != std::nullopt) { @@ -146,7 +146,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s CBlockIndex* pindexPrev = chainActive.Tip(); const int nHeight = pindexPrev->nHeight + 1; uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); - pblock->nTime = GetAdjustedTime(); + pblock->nTime = GetTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); CCoinsViewCache view(pcoinsTip); diff --git a/src/net.cpp b/src/net.cpp index bc25ec5a5..b2e9e7169 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -161,7 +161,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer) ret = CAddress(addr); } ret.nServices = nLocalServices; - ret.nTime = GetAdjustedTime(); + ret.nTime = GetTime(); return ret; } @@ -367,7 +367,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) /// debug print LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", pszDest ? pszDest : addrConnect.ToString(), - pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + pszDest ? 0.0 : (double)(GetTime() - addrConnect.nTime)/3600.0); // Connect SOCKET hSocket; @@ -423,7 +423,7 @@ void CNode::PushVersion() { int nBestHeight = g_signals.GetHeight().get_value_or(0); - int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime()); + int64_t nTime = GetTime(); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); CAddress addrMe = GetLocalAddress(&addr); GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); @@ -1343,7 +1343,7 @@ void ThreadOpenConnections() } } - int64_t nANow = GetAdjustedTime(); + int64_t nANow = GetTime(); int nTries = 0; while (true) diff --git a/src/net.h b/src/net.h index cc8cf13dc..1f4036eb4 100644 --- a/src/net.h +++ b/src/net.h @@ -152,6 +152,7 @@ extern bool fListen; extern uint64_t nLocalServices; extern uint64_t nLocalHostNonce; extern CAddrMan addrman; + /** Maximum number of connections to simultaneously allow (aka connection slots) */ extern int nMaxConnections; diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index cc70f8b61..0acc9435f 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -11,7 +11,6 @@ #include "net.h" #include "netbase.h" #include "rpc/server.h" -#include "timedata.h" #include "txmempool.h" #include "util.h" #ifdef ENABLE_WALLET @@ -56,7 +55,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) " \"walletversion\": xxxxx, (numeric) the wallet version\n" " \"balance\": xxxxxxx, (numeric) the total BitcoinZ balance of the wallet\n" " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"timeoffset\": xxxxx, (numeric) the time offset (deprecated; always 0)\n" " \"connections\": xxxxx, (numeric) the number of connections\n" " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" @@ -94,7 +93,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) } #endif obj.pushKV("blocks", (int)chainActive.Height()); - obj.pushKV("timeoffset", GetTimeOffset()); + obj.pushKV("timeoffset", 0); obj.pushKV("connections", (int)vNodes.size()); obj.pushKV("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())); obj.pushKV("difficulty", (double)GetDifficulty()); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index bd0f3d3da..e1f9dc0dc 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -10,7 +10,6 @@ #include "netbase.h" #include "protocol.h" #include "sync.h" -#include "timedata.h" #include "util.h" #include "version.h" #include "deprecation.h" @@ -462,7 +461,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) " \"subversion\": \"/MagicBean:x.y.z[-v]/\", (string) the server subversion string\n" " \"protocolversion\": xxxxx, (numeric) the protocol version\n" " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"timeoffset\": xxxxx, (numeric) the time offset (deprecated; always 0)\n" " \"connections\": xxxxx, (numeric) the number of connections\n" " \"networks\": [ (array) information per network\n" " {\n" @@ -496,7 +495,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) obj.pushKV("subversion", strSubVersion); obj.pushKV("protocolversion",PROTOCOL_VERSION); obj.pushKV("localservices", strprintf("%016x", nLocalServices)); - obj.pushKV("timeoffset", GetTimeOffset()); + obj.pushKV("timeoffset", 0); obj.pushKV("connections", (int)vNodes.size()); obj.pushKV("networks", GetNetworksInfo()); obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index d3e5251fa..32932143b 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -345,15 +345,15 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) BOOST_CHECK(vAddr1.size() == 0); CAddress addr1 = CAddress(CService("250.250.2.1", 8333)); - addr1.nTime = GetAdjustedTime(); // Set time so isTerrible = false + addr1.nTime = GetTime(); // Set time so isTerrible = false CAddress addr2 = CAddress(CService("250.251.2.2", 9999)); - addr2.nTime = GetAdjustedTime(); + addr2.nTime = GetTime(); CAddress addr3 = CAddress(CService("251.252.2.3", 8333)); - addr3.nTime = GetAdjustedTime(); + addr3.nTime = GetTime(); CAddress addr4 = CAddress(CService("252.253.3.4", 8333)); - addr4.nTime = GetAdjustedTime(); + addr4.nTime = GetTime(); CAddress addr5 = CAddress(CService("252.254.4.5", 8333)); - addr5.nTime = GetAdjustedTime(); + addr5.nTime = GetTime(); CNetAddr source1 = CNetAddr("250.1.2.1"); CNetAddr source2 = CNetAddr("250.2.3.3"); @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) CAddress addr = CAddress(CService(strAddr)); // Ensure that for all addrs in addrman, isTerrible == false. - addr.nTime = GetAdjustedTime(); + addr.nTime = GetTime(); addrman.Add(addr, CNetAddr(strAddr)); if (i % 8 == 0) addrman.Good(addr); diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp new file mode 100644 index 000000000..47c8476c5 --- /dev/null +++ b/src/test/limitedmap_tests.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2012-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "limitedmap.h" + +#include "test/test_bitcoin.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(limitedmap_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(limitedmap_test) +{ + // create a limitedmap capped at 10 items + limitedmap map(10); + + // check that the max size is 10 + BOOST_CHECK(map.max_size() == 10); + + // check that it's empty + BOOST_CHECK(map.size() == 0); + + // insert (-1, -1) + map.insert(std::pair(-1, -1)); + + // make sure that the size is updated + BOOST_CHECK(map.size() == 1); + + // make sure that the new items is in the map + BOOST_CHECK(map.count(-1) == 1); + + // insert 10 new items + for (int i = 0; i < 10; i++) { + map.insert(std::pair(i, i + 1)); + } + + // make sure that the map now contains 10 items... + BOOST_CHECK(map.size() == 10); + + // ...and that the first item has been discarded + BOOST_CHECK(map.count(-1) == 0); + + // iterate over the map, both with an index and an iterator + limitedmap::const_iterator it = map.begin(); + for (int i = 0; i < 10; i++) { + // make sure the item is present + BOOST_CHECK(map.count(i) == 1); + + // use the iterator to check for the expected key adn value + BOOST_CHECK(it->first == i); + BOOST_CHECK(it->second == i + 1); + + // use find to check for the value + BOOST_CHECK(map.find(i)->second == i + 1); + + // update and recheck + map.update(it, i + 2); + BOOST_CHECK(map.find(i)->second == i + 2); + + it++; + } + + // check that we've exhausted the iterator + BOOST_CHECK(it == map.end()); + + // resize the map to 5 items + map.max_size(5); + + // check that the max size and size are now 5 + BOOST_CHECK(map.max_size() == 5); + BOOST_CHECK(map.size() == 5); + + // check that items less than 5 have been discarded + // and items greater than 5 are retained + for (int i = 0; i < 10; i++) { + if (i < 5) { + BOOST_CHECK(map.count(i) == 0); + } else { + BOOST_CHECK(map.count(i) == 1); + } + } + + // erase some items not in the map + for (int i = 100; i < 1000; i += 100) { + map.erase(i); + } + + // check that the size is unaffected + BOOST_CHECK(map.size() == 5); + + // erase the remaining elements + for (int i = 5; i < 10; i++) { + map.erase(i); + } + + // check that the map is now empty + BOOST_CHECK(map.empty()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp deleted file mode 100644 index b3420afea..000000000 --- a/src/test/timedata_tests.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2011-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . -// -#include "timedata.h" -#include "test/test_bitcoin.h" - -#include - -using namespace std; - -BOOST_FIXTURE_TEST_SUITE(timedata_tests, BasicTestingSetup) - -BOOST_AUTO_TEST_CASE(util_MedianFilter) -{ - CMedianFilter filter(5, 15); - - BOOST_CHECK_EQUAL(filter.median(), 15); - - filter.input(20); // [15 20] - BOOST_CHECK_EQUAL(filter.median(), 17); - - filter.input(30); // [15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 20); - - filter.input(3); // [3 15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 17); - - filter.input(7); // [3 7 15 20 30] - BOOST_CHECK_EQUAL(filter.median(), 15); - - filter.input(18); // [3 7 18 20 30] - BOOST_CHECK_EQUAL(filter.median(), 18); - - filter.input(0); // [0 3 7 18 30] - BOOST_CHECK_EQUAL(filter.median(), 7); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/timedata.cpp b/src/timedata.cpp index bf86b21a0..b05b9fd66 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2014 The Bitcoin Core developers +// Copyright (c) 2020 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -11,105 +12,56 @@ #include "utilstrencodings.h" #include "warnings.h" -using namespace std; - -static CCriticalSection cs_nTimeOffset; -static int64_t nTimeOffset = 0; +CTimeWarning timeWarning; /** - * "Never go to sea with two chronometers; take one or three." - * Our three time sources are: - * - System clock - * - Median of other nodes clocks - * - The user (asking the user to fix the system clock if the first two disagree) + * Warn if we have seen TIMEDATA_WARNING_SAMPLES peer times, in the version messages of the + * first TIMEDATA_MAX_SAMPLES unique (by IP address) peers that connect, that are more than + * TIMEDATA_WARNING_THRESHOLD seconds but less than TIMEDATA_IGNORE_THRESHOLD seconds away + * from local time. */ -int64_t GetTimeOffset() +int64_t CTimeWarning::AddTimeData(const CNetAddr& ip, int64_t nTime, int64_t now) { - LOCK(cs_nTimeOffset); - return nTimeOffset; -} + assert(now >= 0 && now <= INT64_MAX - TIMEDATA_IGNORE_THRESHOLD); -int64_t GetAdjustedTime() -{ - return GetTime() + GetTimeOffset(); -} - -static int64_t abs64(int64_t n) -{ - return (n >= 0 ? n : -n); -} + if (nTime <= now - TIMEDATA_IGNORE_THRESHOLD || nTime >= now + TIMEDATA_IGNORE_THRESHOLD) { + return 0; + } -#define BITCOIN_TIMEDATA_MAX_SAMPLES 200 + int64_t nTimeOffset = nTime - now; -void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) -{ - LOCK(cs_nTimeOffset); - // Ignore duplicates - static set setKnown; - if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES) - return; - if (!setKnown.insert(ip).second) - return; + LOCK(cs); + // Ignore duplicate IPs. + if (setKnown.size() == TIMEDATA_MAX_SAMPLES || !setKnown.insert(ip).second) { + return nTimeOffset; + } - // Add data - static CMedianFilter vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0); - vTimeOffsets.input(nOffsetSample); - LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); + LogPrint("net","added time data, samples %d, offset %+d (%+d minutes)\n", setKnown.size(), nTimeOffset, nTimeOffset/60); - // There is a known issue here (see issue #4521): - // - // - The structure vTimeOffsets contains up to 200 elements, after which - // any new element added to it will not increase its size, replacing the - // oldest element. - // - // - The condition to update nTimeOffset includes checking whether the - // number of elements in vTimeOffsets is odd, which will never happen after - // there are 200 elements. - // - // But in this case the 'bug' is protective against some attacks, and may - // actually explain why we've never seen attacks which manipulate the - // clock offset. - // - // So we should hold off on fixing this and clean it up as part of - // a timing cleanup that strengthens it in a number of other ways. - // - if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) - { - int64_t nMedian = vTimeOffsets.median(); - std::vector vSorted = vTimeOffsets.sorted(); - // Only let other nodes change our time by so much - if (abs64(nMedian) < 70 * 60) - { - nTimeOffset = nMedian; + if (nPeersBehind + nPeersAhead < TIMEDATA_WARNING_SAMPLES) { + if (nTimeOffset < -TIMEDATA_WARNING_THRESHOLD) { + nPeersBehind++; + } else if (nTimeOffset > TIMEDATA_WARNING_THRESHOLD) { + nPeersAhead++; } - else - { - nTimeOffset = 0; - - static bool fDone; - if (!fDone) - { - // If nobody has a time different than ours but within 5 minutes of ours, give a warning - bool fMatch = false; - for (int64_t nOffset : vSorted) - if (nOffset != 0 && abs64(nOffset) < 5 * 60) - fMatch = true; - if (!fMatch) - { - fDone = true; - std::string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong BitcoinZ will not work properly."); - SetMiscWarning(strMessage); - LogPrintf("*** %s\n", strMessage); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); - } - } + if (nPeersBehind + nPeersAhead == TIMEDATA_WARNING_SAMPLES) { + Warn(nPeersAhead, nPeersBehind); } - if (fDebug) { - for (int64_t n : vSorted) - LogPrintf("%+d ", n); - LogPrintf("| "); - } - LogPrintf("nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60); } + return nTimeOffset; +} + +void CTimeWarning::Warn(size_t peersAhead, size_t peersBehind) +{ + std::string strMessage; + if (peersBehind >= TIMEDATA_WARNING_MAJORITY) { + strMessage = _("Your computer's date and time may be ahead of the rest of the network! If your clock is wrong BitcoinZ will not work properly."); + } else if (peersAhead >= TIMEDATA_WARNING_MAJORITY) { + strMessage = _("Your computer's date and time may be behind the rest of the network! If your clock is wrong BitcoinZ will not work properly."); + } else { + strMessage = _("Please check that your computer's date and time are correct! If your clock is wrong BitcoinZ will not work properly."); + } + SetMiscWarning(strMessage); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); } diff --git a/src/timedata.h b/src/timedata.h index 69a1eb0f6..f32958ef4 100644 --- a/src/timedata.h +++ b/src/timedata.h @@ -1,76 +1,39 @@ // Copyright (c) 2014 The Bitcoin Core developers +// Copyright (c) 2020 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . #ifndef BITCOIN_TIMEDATA_H #define BITCOIN_TIMEDATA_H -#include -#include +#include #include -#include -class CNetAddr; +#include "netbase.h" +#include "sync.h" -/** - * Median filter over a stream of values. - * Returns the median of the last N numbers - */ -template -class CMedianFilter +class CTimeWarning { private: - std::vector vValues; - std::vector vSorted; - unsigned int nSize; + CCriticalSection cs; + std::set setKnown; + size_t nPeersAhead; + size_t nPeersBehind; public: - CMedianFilter(unsigned int size, T initial_value) : nSize(size) - { - vValues.reserve(size); - vValues.push_back(initial_value); - vSorted = vValues; - } + static const size_t TIMEDATA_WARNING_SAMPLES = 8; + static const size_t TIMEDATA_WARNING_MAJORITY = 6; + static const size_t TIMEDATA_MAX_SAMPLES = 20; + static const int64_t TIMEDATA_WARNING_THRESHOLD = 10 * 60; + static const int64_t TIMEDATA_IGNORE_THRESHOLD = 10 * 24 * 60 * 60; - void input(T value) - { - if (vValues.size() == nSize) { - vValues.erase(vValues.begin()); - } - vValues.push_back(value); + CTimeWarning() : nPeersBehind(0), nPeersAhead(0) {} + virtual ~CTimeWarning() {} - vSorted.resize(vValues.size()); - std::copy(vValues.begin(), vValues.end(), vSorted.begin()); - std::sort(vSorted.begin(), vSorted.end()); - } - - T median() const - { - int size = vSorted.size(); - assert(size > 0); - if (size & 1) // Odd number of elements - { - return vSorted[size / 2]; - } else // Even number of elements - { - return (vSorted[size / 2 - 1] + vSorted[size / 2]) / 2; - } - } - - int size() const - { - return vValues.size(); - } - - std::vector sorted() const - { - return vSorted; - } + int64_t AddTimeData(const CNetAddr& ip, int64_t nTime, int64_t now); + virtual void Warn(size_t peersAhead, size_t peersBehind); }; -/** Functions to keep track of adjusted P2P time */ -int64_t GetTimeOffset(); -int64_t GetAdjustedTime(); -void AddTimeData(const CNetAddr& ip, int64_t nTime); +extern CTimeWarning timeWarning; #endif // BITCOIN_TIMEDATA_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 19d7e18d3..7f7843d13 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -622,7 +622,7 @@ void CWallet::ChainTip(const CBlockIndex *pindex, // Prevent migration transactions from being created when node is syncing after launch, // and also when node wakes up from suspension/hibernation and incoming blocks are old. if (!IsInitialBlockDownload(Params()) && - pblock->GetBlockTime() > GetAdjustedTime() - 3 * 60 * 60) + pblock->GetBlockTime() > GetTime() - 3 * 60 * 60) { RunSaplingMigration(pindex->nHeight); } @@ -1793,7 +1793,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD bool fInsertedNew = ret.second; if (fInsertedNew) { - wtx.nTimeReceived = GetAdjustedTime(); + wtx.nTimeReceived = GetTime(); wtx.nOrderPos = IncOrderPosNext(pwalletdb); wtxOrdered.insert(make_pair(wtx.nOrderPos, &wtx)); From a1bfc38057c91cd190adad25967ad69488a1c617 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 16:35:08 +0000 Subject: [PATCH 072/146] Replace setInventoryKnown with a rolling bloom filter --- src/Makefile.am | 1 - src/Makefile.test.include | 1 - src/main.cpp | 19 ++++----- src/mruset.h | 65 ------------------------------- src/net.cpp | 3 +- src/net.h | 10 ++--- src/test/mruset_tests.cpp | 81 --------------------------------------- 7 files changed, 15 insertions(+), 165 deletions(-) delete mode 100644 src/mruset.h delete mode 100644 src/test/mruset_tests.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 0b89cb4bc..7c6bccfca 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -140,7 +140,6 @@ BITCOIN_CORE_H = \ merkleblock.h \ metrics.h \ miner.h \ - mruset.h \ net.h \ netbase.h \ noui.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 8baaa8326..94893ba5d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -80,7 +80,6 @@ BITCOIN_TESTS =\ test/mempool_tests.cpp \ test/merkle_tests.cpp \ test/miner_tests.cpp \ - test/mruset_tests.cpp \ test/multisig_tests.cpp \ test/netbase_tests.cpp \ test/pmt_tests.cpp \ diff --git a/src/main.cpp b/src/main.cpp index 54ccc5ee2..1b839015d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5220,8 +5220,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // however we MUST always provide at least what the remote peer needs typedef std::pair PairType; for (PairType& pair : merkleBlock.vMatchedTxn) - if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) - pfrom->PushMessage("tx", block.vtx[pair.first]); + pfrom->PushMessage("tx", block.vtx[pair.first]); } // else // no response @@ -6524,7 +6523,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vInvWait.reserve(pto->vInventoryToSend.size()); for (const CInv& inv : pto->vInventoryToSend) { - if (pto->setInventoryKnown.count(inv)) + if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash)) continue; // trickle out tx inv to protect privacy @@ -6545,15 +6544,13 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } - // returns true if wasn't already contained in the set - if (pto->setInventoryKnown.insert(inv).second) + pto->filterInventoryKnown.insert(inv.hash); + + vInv.push_back(inv); + if (vInv.size() >= 1000) { - vInv.push_back(inv); - if (vInv.size() >= 1000) - { - pto->PushMessage("inv", vInv); - vInv.clear(); - } + pto->PushMessage("inv", vInv); + vInv.clear(); } } pto->vInventoryToSend = vInvWait; diff --git a/src/mruset.h b/src/mruset.h deleted file mode 100644 index 40dee4c4b..000000000 --- a/src/mruset.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2012-2015 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef BITCOIN_MRUSET_H -#define BITCOIN_MRUSET_H - -#include -#include -#include - -/** STL-like set container that only keeps the most recent N elements. */ -template -class mruset -{ -public: - typedef T key_type; - typedef T value_type; - typedef typename std::set::iterator iterator; - typedef typename std::set::const_iterator const_iterator; - typedef typename std::set::size_type size_type; - -protected: - std::set set; - std::vector order; - size_type first_used; - size_type first_unused; - const size_type nMaxSize; - -public: - mruset(size_type nMaxSizeIn = 1) : nMaxSize(nMaxSizeIn) { clear(); } - iterator begin() const { return set.begin(); } - iterator end() const { return set.end(); } - size_type size() const { return set.size(); } - bool empty() const { return set.empty(); } - iterator find(const key_type& k) const { return set.find(k); } - size_type count(const key_type& k) const { return set.count(k); } - void clear() - { - set.clear(); - order.assign(nMaxSize, set.end()); - first_used = 0; - first_unused = 0; - } - bool inline friend operator==(const mruset& a, const mruset& b) { return a.set == b.set; } - bool inline friend operator==(const mruset& a, const std::set& b) { return a.set == b; } - bool inline friend operator<(const mruset& a, const mruset& b) { return a.set < b.set; } - std::pair insert(const key_type& x) - { - std::pair ret = set.insert(x); - if (ret.second) { - if (set.size() == nMaxSize + 1) { - set.erase(order[first_used]); - order[first_used] = set.end(); - if (++first_used == nMaxSize) first_used = 0; - } - order[first_unused] = ret.first; - if (++first_unused == nMaxSize) first_unused = 0; - } - return ret; - } - size_type max_size() const { return nMaxSize; } -}; - -#endif // BITCOIN_MRUSET_H diff --git a/src/net.cpp b/src/net.cpp index b2e9e7169..8078de2b6 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2107,7 +2107,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa addr(addrIn), nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)), addrKnown(5000, 0.001), - setInventoryKnown(SendBufferSize() / 1000) + filterInventoryKnown(50000, 0.000001) { nServices = 0; hSocket = hSocketIn; @@ -2133,6 +2133,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nSendOffset = 0; hashContinue = uint256(); nStartingHeight = -1; + filterInventoryKnown.reset(); fGetAddr = false; fRelayTxes = false; fSentAddr = false; diff --git a/src/net.h b/src/net.h index 1f4036eb4..cbd4930e6 100644 --- a/src/net.h +++ b/src/net.h @@ -10,7 +10,6 @@ #include "compat.h" #include "fs.h" #include "limitedmap.h" -#include "mruset.h" #include "netbase.h" #include "protocol.h" #include "random.h" @@ -328,7 +327,7 @@ class CNode std::set setKnown; // inventory based relay - mruset setInventoryKnown; + CRollingBloomFilter filterInventoryKnown; std::vector vInventoryToSend; CCriticalSection cs_inventory; std::set setAskFor; @@ -436,7 +435,7 @@ class CNode { { LOCK(cs_inventory); - setInventoryKnown.insert(inv); + filterInventoryKnown.insert(inv.hash); } } @@ -444,8 +443,9 @@ class CNode { { LOCK(cs_inventory); - if (!setInventoryKnown.count(inv)) - vInventoryToSend.push_back(inv); + if (inv.type == MSG_TX && filterInventoryKnown.contains(inv.hash)) + return; + vInventoryToSend.push_back(inv); } } diff --git a/src/test/mruset_tests.cpp b/src/test/mruset_tests.cpp deleted file mode 100644 index 0ac799de4..000000000 --- a/src/test/mruset_tests.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2012-2013 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#include "mruset.h" - -#include "random.h" -#include "util.h" -#include "test/test_bitcoin.h" - -#include - -#include - -#define NUM_TESTS 16 -#define MAX_SIZE 100 - -using namespace std; - -BOOST_FIXTURE_TEST_SUITE(mruset_tests, BasicTestingSetup) - -BOOST_AUTO_TEST_CASE(mruset_test) -{ - // The mruset being tested. - mruset mru(5000); - - // Run the test 10 times. - for (int test = 0; test < 10; test++) { - // Reset mru. - mru.clear(); - - // A deque + set to simulate the mruset. - std::deque rep; - std::set all; - - // Insert 10000 random integers below 15000. - for (int j=0; j<10000; j++) { - int add = GetRandInt(15000); - mru.insert(add); - - // Add the number to rep/all as well. - if (all.count(add) == 0) { - all.insert(add); - rep.push_back(add); - if (all.size() == 5001) { - all.erase(rep.front()); - rep.pop_front(); - } - } - - // Do a full comparison between mru and the simulated mru every 1000 and every 5001 elements. - if (j % 1000 == 0 || j % 5001 == 0) { - mruset mru2 = mru; // Also try making a copy - - // Check that all elements that should be in there, are in there. - for (int x : rep) { - BOOST_CHECK(mru.count(x)); - BOOST_CHECK(mru2.count(x)); - } - - // Check that all elements that are in there, should be in there. - for (int x : mru) { - BOOST_CHECK(all.count(x)); - } - - // Check that all elements that are in there, should be in there. - for (int x : mru2) { - BOOST_CHECK(all.count(x)); - } - - for (int t = 0; t < 10; t++) { - int r = GetRandInt(15000); - BOOST_CHECK(all.count(r) == mru.count(r)); - BOOST_CHECK(all.count(r) == mru2.count(r)); - } - } - } - } -} - -BOOST_AUTO_TEST_SUITE_END() From 7a4af4b3f449d97285f311a828803eeed8c49aa6 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 16:42:27 +0000 Subject: [PATCH 073/146] Prevent a pindex == NULL case in the ScanForWalletTransactions traversal --- src/wallet/wallet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7f7843d13..a9c32d440 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2856,8 +2856,9 @@ std::optional CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, // no need to read and scan block, if block was created before // our wallet birthday (as adjusted for block time variability) - while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - TIMESTAMP_WINDOW))) + while (chainActive.Next(pindex) != NULL && nTimeFirstKey && pindex->GetBlockTime() < nTimeFirstKey - TIMESTAMP_WINDOW) { pindex = chainActive.Next(pindex); + } ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup double dProgressStart = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false); From de43e4082c459abafc61d1eca02c68b0817a3180 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 16:47:03 +0000 Subject: [PATCH 074/146] Change error message in getHeightRange --- src/rpc/misc.cpp | 2 +- src/test/rpc_tests.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 0acc9435f..a43473020 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -782,7 +782,7 @@ static void getHeightRange(const UniValue& params, int& start, int& end) } if (end < start) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, - "End value is expected to be greater than start"); + "End value is expected to be greater than or equal to start"); } } } diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 951edd52d..85678f5c3 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -416,7 +416,7 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer) CheckRPCThrows("getaddressdeltas {\"addresses\":[],\"start\":0,\"end\":0,\"chainInfo\":true}", "Start and end are expected to be greater than zero"); CheckRPCThrows("getaddressdeltas {\"addresses\":[],\"start\":3,\"end\":2,\"chainInfo\":true}", - "End value is expected to be greater than start"); + "End value is expected to be greater than or equal to start"); // in this test environment, only the genesis block (0) exists CheckRPCThrows("getaddressdeltas {\"addresses\":[],\"start\":2,\"end\":3,\"chainInfo\":true}", "Start or end is outside chain range"); @@ -427,7 +427,7 @@ BOOST_AUTO_TEST_CASE(rpc_insightexplorer) CheckRPCThrows("getaddresstxids {\"addresses\":[],\"start\":0,\"end\":0,\"chainInfo\":true}", "Start and end are expected to be greater than zero"); CheckRPCThrows("getaddresstxids {\"addresses\":[],\"start\":3,\"end\":2,\"chainInfo\":true}", - "End value is expected to be greater than start"); + "End value is expected to be greater than or equal to start"); // in this test environment, only the genesis block (0) exists CheckRPCThrows("getaddresstxids {\"addresses\":[],\"start\":2,\"end\":3,\"chainInfo\":true}", "Start or end is outside chain range"); From c698c20d529e50ce621c08c160e7f3ad6d17da57 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 16:48:06 +0000 Subject: [PATCH 075/146] Fix missing null initialization of pindexLastTip pointer in ThreadStartWalletNotifier --- src/init.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 8d6c46ae9..11be9f204 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -590,7 +590,7 @@ void CleanupBlockRevFiles() void ThreadStartWalletNotifier() { - CBlockIndex *pindexLastTip; + CBlockIndex *pindexLastTip{nullptr}; // If the wallet is compiled in and enabled, we want to start notifying // from the block which corresponds with the wallet's view of the chain @@ -609,7 +609,7 @@ void ThreadStartWalletNotifier() } if (walletBestBlockHash.has_value()) { - int64_t slept; + int64_t slept{0}; auto timedOut = [&]() -> bool { MilliSleep(50); slept += 50; From 0ca296ad32298b8904f49fb85270ae5ccbf7d7e8 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 16:50:10 +0000 Subject: [PATCH 076/146] Don't advance the wallet init timer until we're done reindexing --- src/chain.cpp | 1 + src/init.cpp | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/chain.cpp b/src/chain.cpp index ced12d3b8..e00aca639 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -54,6 +54,7 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { } const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const { + assert(pindex != nullptr); if (pindex->nHeight > Height()) pindex = pindex->GetAncestor(Height()); while (pindex && !Contains(pindex)) diff --git a/src/init.cpp b/src/init.cpp index 11be9f204..29bd7a9eb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -610,9 +610,20 @@ void ThreadStartWalletNotifier() if (walletBestBlockHash.has_value()) { int64_t slept{0}; + bool isReindexing{true}; auto timedOut = [&]() -> bool { - MilliSleep(50); - slept += 50; + MilliSleep(500); + // re-check whether we're reindexing + if (isReindexing) { + LOCK(cs_main); + pblocktree->ReadReindexing(isReindexing); + } + + // once we're out of reindexing, we can start incrementing the slept counter + if (!isReindexing) { + slept += 500; + } + if (slept > WALLET_INITIAL_SYNC_TIMEOUT) { auto errmsg = strprintf( "The wallet's best block hash %s was not detected in restored chain state. " From 272b1573a56d2f948eba0a80f8b49bbb69fbd1e5 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 27 Aug 2024 23:23:34 +0000 Subject: [PATCH 077/146] Rollback to librustzcash 0.1.0 --- depends/packages/crate_aes.mk | 4 +- depends/packages/crate_aes_soft.mk | 4 +- depends/packages/crate_aesni.mk | 4 +- depends/packages/crate_arrayvec.mk | 4 +- depends/packages/crate_bellman.mk | 4 +- .../{crate_arrayref.mk => crate_bitflags.mk} | 8 +- depends/packages/crate_blake2_rfc.mk | 16 + depends/packages/crate_blake2s_simd.mk | 15 - depends/packages/crate_block_buffer.mk | 15 - depends/packages/crate_block_cipher_trait.mk | 4 +- depends/packages/crate_block_padding.mk | 15 - depends/packages/crate_byte_tools.mk | 4 +- depends/packages/crate_byteorder.mk | 4 +- depends/packages/crate_c2_chacha.mk | 15 - depends/packages/crate_cfg_if.mk | 15 - depends/packages/crate_constant_time_eq.mk | 4 +- depends/packages/crate_crossbeam.mk | 4 +- depends/packages/crate_crossbeam_channel.mk | 15 - depends/packages/crate_crossbeam_deque.mk | 15 - depends/packages/crate_crossbeam_epoch.mk | 15 - depends/packages/crate_crossbeam_queue.mk | 15 - depends/packages/crate_crossbeam_utils.mk | 15 - depends/packages/crate_crunchy.mk | 15 - depends/packages/crate_crypto_api.mk | 15 - .../packages/crate_crypto_api_chachapoly.mk | 15 - depends/packages/crate_digest.mk | 4 +- depends/packages/crate_directories.mk | 15 - depends/packages/crate_fake_simd.mk | 15 - depends/packages/crate_ff.mk | 15 - depends/packages/crate_ff_derive.mk | 15 - depends/packages/crate_fpe.mk | 4 +- ...rate_bigint.mk => crate_fuchsia_zircon.mk} | 8 +- ...2b_simd.mk => crate_fuchsia_zircon_sys.mk} | 8 +- depends/packages/crate_futures.mk | 4 +- depends/packages/crate_generic_array.mk | 4 +- depends/packages/crate_getrandom.mk | 15 - depends/packages/crate_group.mk | 15 - depends/packages/crate_hex.mk | 15 - depends/packages/crate_lazy_static.mk | 4 +- depends/packages/crate_libc.mk | 4 +- depends/packages/crate_log.mk | 15 - depends/packages/crate_memoffset.mk | 15 - depends/packages/crate_nodrop.mk | 4 +- depends/packages/crate_num_bigint.mk | 4 +- depends/packages/crate_num_cpus.mk | 4 +- depends/packages/crate_num_integer.mk | 4 +- depends/packages/crate_num_traits.mk | 4 +- depends/packages/crate_opaque_debug.mk | 4 +- depends/packages/crate_pairing.mk | 4 +- depends/packages/crate_ppv_lite86.mk | 15 - depends/packages/crate_proc_macro2.mk | 15 - depends/packages/crate_quote.mk | 15 - depends/packages/crate_rand.mk | 4 +- depends/packages/crate_rand_chacha.mk | 15 - depends/packages/crate_rand_core.mk | 15 - depends/packages/crate_rand_hc.mk | 15 - depends/packages/crate_rand_xorshift.mk | 15 - depends/packages/crate_rustc_version.mk | 15 - depends/packages/crate_sapling_crypto.mk | 16 + depends/packages/crate_scopeguard.mk | 15 - depends/packages/crate_semver.mk | 15 - depends/packages/crate_semver_parser.mk | 15 - depends/packages/crate_sha2.mk | 15 - ...rate_autocfg.mk => crate_stream_cipher.mk} | 8 +- depends/packages/crate_syn.mk | 15 - depends/packages/crate_typenum.mk | 4 +- depends/packages/crate_unicode_xid.mk | 15 - depends/packages/crate_wasi.mk | 15 - depends/packages/crate_winapi.mk | 4 +- depends/packages/crate_zcash_history.mk | 15 - depends/packages/crate_zcash_primitives.mk | 15 - depends/packages/crate_zcash_proofs.mk | 15 - depends/packages/crate_zip32.mk | 16 + depends/packages/packages.mk | 53 +- src/Makefile.am | 10 +- src/rust/.gitignore | 1 + src/rust/.gitlab-ci.yml | 66 + src/rust/Cargo.lock | 342 ++ src/rust/Cargo.toml | 16 + src/rust/LICENSE-APACHE | 202 ++ src/rust/LICENSE-MIT | 21 + src/rust/README.md | 24 + src/rust/bellman/.gitignore | 2 + src/rust/bellman/COPYRIGHT | 14 + src/rust/bellman/Cargo.toml | 22 + src/rust/bellman/LICENSE-APACHE | 201 ++ src/rust/bellman/LICENSE-MIT | 23 + src/rust/bellman/README.md | 19 + src/rust/bellman/src/domain.rs | 494 +++ src/rust/bellman/src/groth16/generator.rs | 482 +++ src/rust/bellman/src/groth16/mod.rs | 576 ++++ src/rust/bellman/src/groth16/prover.rs | 334 ++ .../bellman/src/groth16/tests/dummy_engine.rs | 451 +++ src/rust/bellman/src/groth16/tests/mod.rs | 400 +++ src/rust/bellman/src/groth16/verifier.rs | 66 + src/rust/bellman/src/lib.rs | 424 +++ src/rust/bellman/src/multicore.rs | 106 + src/rust/bellman/src/multiexp.rs | 303 ++ src/rust/bellman/tests/mimc.rs | 251 ++ src/rust/librustzcash/Cargo.toml | 28 + .../librustzcash}/README.md | 10 +- .../librustzcash}/include/librustzcash.h | 30 +- src/rust/librustzcash/src/equihash.rs | 468 +++ src/rust/librustzcash/src/hashreader.rs | 42 + src/rust/librustzcash/src/rustzcash.rs | 1711 ++++++++++ .../librustzcash}/src/tests/key_agreement.rs | 20 +- .../librustzcash}/src/tests/key_components.rs | 77 +- .../librustzcash}/src/tests/mod.rs | 3 +- .../librustzcash}/src/tests/notes.rs | 4 +- .../librustzcash}/src/tests/signatures.rs | 9 +- src/rust/pairing/.gitignore | 3 + src/rust/pairing/COPYRIGHT | 14 + src/rust/pairing/Cargo.toml | 22 + src/rust/pairing/LICENSE-APACHE | 201 ++ src/rust/pairing/LICENSE-MIT | 23 + src/rust/pairing/README.md | 35 + src/rust/pairing/benches/bls12_381/ec.rs | 127 + src/rust/pairing/benches/bls12_381/fq.rs | 268 ++ src/rust/pairing/benches/bls12_381/fq12.rs | 94 + src/rust/pairing/benches/bls12_381/fq2.rs | 110 + src/rust/pairing/benches/bls12_381/fr.rs | 268 ++ src/rust/pairing/benches/bls12_381/mod.rs | 107 + src/rust/pairing/benches/pairing_benches.rs | 7 + src/rust/pairing/src/bls12_381/README.md | 71 + src/rust/pairing/src/bls12_381/ec.rs | 2026 +++++++++++ src/rust/pairing/src/bls12_381/fq.rs | 2959 +++++++++++++++++ src/rust/pairing/src/bls12_381/fq12.rs | 189 ++ src/rust/pairing/src/bls12_381/fq2.rs | 908 +++++ src/rust/pairing/src/bls12_381/fq6.rs | 374 +++ src/rust/pairing/src/bls12_381/fr.rs | 1614 +++++++++ src/rust/pairing/src/bls12_381/mod.rs | 364 ++ .../g1_compressed_valid_test_vectors.dat | Bin 0 -> 48000 bytes .../g1_uncompressed_invalid_test_vectors.dat | 0 .../g1_uncompressed_valid_test_vectors.dat | Bin 0 -> 96000 bytes .../g2_compressed_valid_test_vectors.dat | Bin 0 -> 96000 bytes .../g2_uncompressed_valid_test_vectors.dat | Bin 0 -> 192000 bytes src/rust/pairing/src/bls12_381/tests/mod.rs | 611 ++++ src/rust/pairing/src/lib.rs | 757 +++++ src/rust/pairing/src/tests/curve.rs | 420 +++ src/rust/pairing/src/tests/engine.rs | 126 + src/rust/pairing/src/tests/field.rs | 266 ++ src/rust/pairing/src/tests/mod.rs | 4 + src/rust/pairing/src/tests/repr.rs | 98 + src/rust/pairing/src/wnaf.rs | 179 + src/rust/sapling-crypto/.gitignore | 3 + src/rust/sapling-crypto/COPYRIGHT | 14 + src/rust/sapling-crypto/Cargo.toml | 27 + src/rust/sapling-crypto/LICENSE-APACHE | 201 ++ src/rust/sapling-crypto/LICENSE-MIT | 23 + src/rust/sapling-crypto/README.md | 23 + .../sapling-crypto/benches/pedersen_hash.rs | 23 + src/rust/sapling-crypto/examples/bench.rs | 102 + .../sapling-crypto/src/circuit/blake2s.rs | 438 +++ .../sapling-crypto/src/circuit/boolean.rs | 1582 +++++++++ src/rust/sapling-crypto/src/circuit/ecc.rs | 1213 +++++++ src/rust/sapling-crypto/src/circuit/lookup.rs | 307 ++ src/rust/sapling-crypto/src/circuit/mod.rs | 39 + .../sapling-crypto/src/circuit/multieq.rs | 137 + .../sapling-crypto/src/circuit/multipack.rs | 113 + src/rust/sapling-crypto/src/circuit/num.rs | 622 ++++ .../src/circuit/pedersen_hash.rs | 194 ++ .../sapling-crypto/src/circuit/sapling/mod.rs | 815 +++++ src/rust/sapling-crypto/src/circuit/sha256.rs | 417 +++ .../src/circuit/sprout/commitment.rs | 42 + .../src/circuit/sprout/input.rs | 226 ++ .../sapling-crypto/src/circuit/sprout/mod.rs | 488 +++ .../src/circuit/sprout/output.rs | 54 + .../sapling-crypto/src/circuit/sprout/prfs.rs | 79 + .../src/circuit/sprout/test_vectors.dat | Bin 0 -> 10864 bytes .../sapling-crypto/src/circuit/test/mod.rs | 492 +++ src/rust/sapling-crypto/src/circuit/uint32.rs | 755 +++++ src/rust/sapling-crypto/src/constants.rs | 40 + src/rust/sapling-crypto/src/group_hash.rs | 46 + src/rust/sapling-crypto/src/jubjub/edwards.rs | 523 +++ src/rust/sapling-crypto/src/jubjub/fs.rs | 1232 +++++++ src/rust/sapling-crypto/src/jubjub/mod.rs | 435 +++ .../sapling-crypto/src/jubjub/montgomery.rs | 358 ++ src/rust/sapling-crypto/src/jubjub/tests.rs | 416 +++ src/rust/sapling-crypto/src/lib.rs | 22 + src/rust/sapling-crypto/src/pedersen_hash.rs | 103 + src/rust/sapling-crypto/src/primitives/mod.rs | 258 ++ src/rust/sapling-crypto/src/redjubjub.rs | 343 ++ src/rust/sapling-crypto/src/util.rs | 11 + src/rust/zcash_primitives/Cargo.toml | 8 + src/rust/zcash_primitives/LICENSE-APACHE | 202 ++ src/rust/zcash_primitives/LICENSE-MIT | 21 + src/rust/zcash_primitives/README.md | 20 + src/rust/zcash_primitives/src/lib.rs | 7 + src/rust/zcash_proofs/Cargo.toml | 8 + src/rust/zcash_proofs/LICENSE-APACHE | 202 ++ src/rust/zcash_proofs/LICENSE-MIT | 21 + src/rust/zcash_proofs/README.md | 21 + src/rust/zcash_proofs/src/lib.rs | 7 + src/rust/zcash_wallet/Cargo.toml | 8 + src/rust/zcash_wallet/LICENSE-APACHE | 202 ++ src/rust/zcash_wallet/LICENSE-MIT | 21 + src/rust/zcash_wallet/README.md | 20 + src/rust/zcash_wallet/src/lib.rs | 7 + src/rust/zip32/.gitignore | 3 + src/rust/zip32/COPYRIGHT | 14 + src/rust/zip32/Cargo.toml | 24 + src/rust/zip32/LICENSE-APACHE | 201 ++ src/rust/zip32/LICENSE-MIT | 23 + src/rust/zip32/README.md | 17 + src/rust/zip32/src/lib.rs | 1231 +++++++ src/rustzcash/.gitignore | 2 - src/rustzcash/Cargo.lock | 706 ---- src/rustzcash/Cargo.toml | 38 - src/rustzcash/src/rustzcash.rs | 1354 -------- src/rustzcash/src/tests/mmr.rs | 225 -- src/transaction_builder.cpp | 8 +- src/wallet/gtest/test_wallet_zkeys.cpp | 1 + src/zcbenchmarks.cpp | 7 +- 213 files changed, 33998 insertions(+), 3181 deletions(-) rename depends/packages/{crate_arrayref.mk => crate_bitflags.mk} (69%) create mode 100644 depends/packages/crate_blake2_rfc.mk delete mode 100644 depends/packages/crate_blake2s_simd.mk delete mode 100644 depends/packages/crate_block_buffer.mk delete mode 100644 depends/packages/crate_block_padding.mk delete mode 100644 depends/packages/crate_c2_chacha.mk delete mode 100644 depends/packages/crate_cfg_if.mk delete mode 100644 depends/packages/crate_crossbeam_channel.mk delete mode 100644 depends/packages/crate_crossbeam_deque.mk delete mode 100644 depends/packages/crate_crossbeam_epoch.mk delete mode 100644 depends/packages/crate_crossbeam_queue.mk delete mode 100644 depends/packages/crate_crossbeam_utils.mk delete mode 100644 depends/packages/crate_crunchy.mk delete mode 100644 depends/packages/crate_crypto_api.mk delete mode 100644 depends/packages/crate_crypto_api_chachapoly.mk delete mode 100644 depends/packages/crate_directories.mk delete mode 100644 depends/packages/crate_fake_simd.mk delete mode 100644 depends/packages/crate_ff.mk delete mode 100644 depends/packages/crate_ff_derive.mk rename depends/packages/{crate_bigint.mk => crate_fuchsia_zircon.mk} (67%) rename depends/packages/{crate_blake2b_simd.mk => crate_fuchsia_zircon_sys.mk} (66%) delete mode 100644 depends/packages/crate_getrandom.mk delete mode 100644 depends/packages/crate_group.mk delete mode 100644 depends/packages/crate_hex.mk delete mode 100644 depends/packages/crate_log.mk delete mode 100644 depends/packages/crate_memoffset.mk delete mode 100644 depends/packages/crate_ppv_lite86.mk delete mode 100644 depends/packages/crate_proc_macro2.mk delete mode 100644 depends/packages/crate_quote.mk delete mode 100644 depends/packages/crate_rand_chacha.mk delete mode 100644 depends/packages/crate_rand_core.mk delete mode 100644 depends/packages/crate_rand_hc.mk delete mode 100644 depends/packages/crate_rand_xorshift.mk delete mode 100644 depends/packages/crate_rustc_version.mk create mode 100644 depends/packages/crate_sapling_crypto.mk delete mode 100644 depends/packages/crate_scopeguard.mk delete mode 100644 depends/packages/crate_semver.mk delete mode 100644 depends/packages/crate_semver_parser.mk delete mode 100644 depends/packages/crate_sha2.mk rename depends/packages/{crate_autocfg.mk => crate_stream_cipher.mk} (67%) delete mode 100644 depends/packages/crate_syn.mk delete mode 100644 depends/packages/crate_unicode_xid.mk delete mode 100644 depends/packages/crate_wasi.mk delete mode 100644 depends/packages/crate_zcash_history.mk delete mode 100644 depends/packages/crate_zcash_primitives.mk delete mode 100644 depends/packages/crate_zcash_proofs.mk create mode 100644 depends/packages/crate_zip32.mk create mode 100644 src/rust/.gitignore create mode 100644 src/rust/.gitlab-ci.yml create mode 100644 src/rust/Cargo.lock create mode 100644 src/rust/Cargo.toml create mode 100644 src/rust/LICENSE-APACHE create mode 100644 src/rust/LICENSE-MIT create mode 100644 src/rust/README.md create mode 100644 src/rust/bellman/.gitignore create mode 100644 src/rust/bellman/COPYRIGHT create mode 100644 src/rust/bellman/Cargo.toml create mode 100644 src/rust/bellman/LICENSE-APACHE create mode 100644 src/rust/bellman/LICENSE-MIT create mode 100644 src/rust/bellman/README.md create mode 100644 src/rust/bellman/src/domain.rs create mode 100644 src/rust/bellman/src/groth16/generator.rs create mode 100644 src/rust/bellman/src/groth16/mod.rs create mode 100644 src/rust/bellman/src/groth16/prover.rs create mode 100644 src/rust/bellman/src/groth16/tests/dummy_engine.rs create mode 100644 src/rust/bellman/src/groth16/tests/mod.rs create mode 100644 src/rust/bellman/src/groth16/verifier.rs create mode 100644 src/rust/bellman/src/lib.rs create mode 100644 src/rust/bellman/src/multicore.rs create mode 100644 src/rust/bellman/src/multiexp.rs create mode 100644 src/rust/bellman/tests/mimc.rs create mode 100644 src/rust/librustzcash/Cargo.toml rename src/{rustzcash => rust/librustzcash}/README.md (65%) rename src/{rustzcash => rust/librustzcash}/include/librustzcash.h (93%) create mode 100644 src/rust/librustzcash/src/equihash.rs create mode 100644 src/rust/librustzcash/src/hashreader.rs create mode 100644 src/rust/librustzcash/src/rustzcash.rs rename src/{rustzcash => rust/librustzcash}/src/tests/key_agreement.rs (81%) rename src/{rustzcash => rust/librustzcash}/src/tests/key_components.rs (90%) rename src/{rustzcash => rust/librustzcash}/src/tests/mod.rs (97%) rename src/{rustzcash => rust/librustzcash}/src/tests/notes.rs (99%) rename src/{rustzcash => rust/librustzcash}/src/tests/signatures.rs (99%) create mode 100644 src/rust/pairing/.gitignore create mode 100644 src/rust/pairing/COPYRIGHT create mode 100644 src/rust/pairing/Cargo.toml create mode 100644 src/rust/pairing/LICENSE-APACHE create mode 100644 src/rust/pairing/LICENSE-MIT create mode 100644 src/rust/pairing/README.md create mode 100644 src/rust/pairing/benches/bls12_381/ec.rs create mode 100644 src/rust/pairing/benches/bls12_381/fq.rs create mode 100644 src/rust/pairing/benches/bls12_381/fq12.rs create mode 100644 src/rust/pairing/benches/bls12_381/fq2.rs create mode 100644 src/rust/pairing/benches/bls12_381/fr.rs create mode 100644 src/rust/pairing/benches/bls12_381/mod.rs create mode 100644 src/rust/pairing/benches/pairing_benches.rs create mode 100644 src/rust/pairing/src/bls12_381/README.md create mode 100644 src/rust/pairing/src/bls12_381/ec.rs create mode 100644 src/rust/pairing/src/bls12_381/fq.rs create mode 100644 src/rust/pairing/src/bls12_381/fq12.rs create mode 100644 src/rust/pairing/src/bls12_381/fq2.rs create mode 100644 src/rust/pairing/src/bls12_381/fq6.rs create mode 100644 src/rust/pairing/src/bls12_381/fr.rs create mode 100644 src/rust/pairing/src/bls12_381/mod.rs create mode 100644 src/rust/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat create mode 100644 src/rust/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat create mode 100644 src/rust/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat create mode 100644 src/rust/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat create mode 100644 src/rust/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat create mode 100644 src/rust/pairing/src/bls12_381/tests/mod.rs create mode 100644 src/rust/pairing/src/lib.rs create mode 100644 src/rust/pairing/src/tests/curve.rs create mode 100644 src/rust/pairing/src/tests/engine.rs create mode 100644 src/rust/pairing/src/tests/field.rs create mode 100644 src/rust/pairing/src/tests/mod.rs create mode 100644 src/rust/pairing/src/tests/repr.rs create mode 100644 src/rust/pairing/src/wnaf.rs create mode 100644 src/rust/sapling-crypto/.gitignore create mode 100644 src/rust/sapling-crypto/COPYRIGHT create mode 100644 src/rust/sapling-crypto/Cargo.toml create mode 100644 src/rust/sapling-crypto/LICENSE-APACHE create mode 100644 src/rust/sapling-crypto/LICENSE-MIT create mode 100644 src/rust/sapling-crypto/README.md create mode 100644 src/rust/sapling-crypto/benches/pedersen_hash.rs create mode 100644 src/rust/sapling-crypto/examples/bench.rs create mode 100644 src/rust/sapling-crypto/src/circuit/blake2s.rs create mode 100644 src/rust/sapling-crypto/src/circuit/boolean.rs create mode 100644 src/rust/sapling-crypto/src/circuit/ecc.rs create mode 100644 src/rust/sapling-crypto/src/circuit/lookup.rs create mode 100644 src/rust/sapling-crypto/src/circuit/mod.rs create mode 100644 src/rust/sapling-crypto/src/circuit/multieq.rs create mode 100644 src/rust/sapling-crypto/src/circuit/multipack.rs create mode 100644 src/rust/sapling-crypto/src/circuit/num.rs create mode 100644 src/rust/sapling-crypto/src/circuit/pedersen_hash.rs create mode 100644 src/rust/sapling-crypto/src/circuit/sapling/mod.rs create mode 100644 src/rust/sapling-crypto/src/circuit/sha256.rs create mode 100644 src/rust/sapling-crypto/src/circuit/sprout/commitment.rs create mode 100644 src/rust/sapling-crypto/src/circuit/sprout/input.rs create mode 100644 src/rust/sapling-crypto/src/circuit/sprout/mod.rs create mode 100644 src/rust/sapling-crypto/src/circuit/sprout/output.rs create mode 100644 src/rust/sapling-crypto/src/circuit/sprout/prfs.rs create mode 100644 src/rust/sapling-crypto/src/circuit/sprout/test_vectors.dat create mode 100644 src/rust/sapling-crypto/src/circuit/test/mod.rs create mode 100644 src/rust/sapling-crypto/src/circuit/uint32.rs create mode 100644 src/rust/sapling-crypto/src/constants.rs create mode 100644 src/rust/sapling-crypto/src/group_hash.rs create mode 100644 src/rust/sapling-crypto/src/jubjub/edwards.rs create mode 100644 src/rust/sapling-crypto/src/jubjub/fs.rs create mode 100644 src/rust/sapling-crypto/src/jubjub/mod.rs create mode 100644 src/rust/sapling-crypto/src/jubjub/montgomery.rs create mode 100644 src/rust/sapling-crypto/src/jubjub/tests.rs create mode 100644 src/rust/sapling-crypto/src/lib.rs create mode 100644 src/rust/sapling-crypto/src/pedersen_hash.rs create mode 100644 src/rust/sapling-crypto/src/primitives/mod.rs create mode 100644 src/rust/sapling-crypto/src/redjubjub.rs create mode 100644 src/rust/sapling-crypto/src/util.rs create mode 100644 src/rust/zcash_primitives/Cargo.toml create mode 100644 src/rust/zcash_primitives/LICENSE-APACHE create mode 100644 src/rust/zcash_primitives/LICENSE-MIT create mode 100644 src/rust/zcash_primitives/README.md create mode 100644 src/rust/zcash_primitives/src/lib.rs create mode 100644 src/rust/zcash_proofs/Cargo.toml create mode 100644 src/rust/zcash_proofs/LICENSE-APACHE create mode 100644 src/rust/zcash_proofs/LICENSE-MIT create mode 100644 src/rust/zcash_proofs/README.md create mode 100644 src/rust/zcash_proofs/src/lib.rs create mode 100644 src/rust/zcash_wallet/Cargo.toml create mode 100644 src/rust/zcash_wallet/LICENSE-APACHE create mode 100644 src/rust/zcash_wallet/LICENSE-MIT create mode 100644 src/rust/zcash_wallet/README.md create mode 100644 src/rust/zcash_wallet/src/lib.rs create mode 100644 src/rust/zip32/.gitignore create mode 100644 src/rust/zip32/COPYRIGHT create mode 100644 src/rust/zip32/Cargo.toml create mode 100644 src/rust/zip32/LICENSE-APACHE create mode 100644 src/rust/zip32/LICENSE-MIT create mode 100644 src/rust/zip32/README.md create mode 100644 src/rust/zip32/src/lib.rs delete mode 100644 src/rustzcash/.gitignore delete mode 100644 src/rustzcash/Cargo.lock delete mode 100644 src/rustzcash/Cargo.toml delete mode 100644 src/rustzcash/src/rustzcash.rs delete mode 100644 src/rustzcash/src/tests/mmr.rs diff --git a/depends/packages/crate_aes.mk b/depends/packages/crate_aes.mk index 0e2a29392..e53d72679 100644 --- a/depends/packages/crate_aes.mk +++ b/depends/packages/crate_aes.mk @@ -1,9 +1,9 @@ package=crate_aes $(package)_crate_name=aes -$(package)_version=0.3.2 +$(package)_version=0.2.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9 +$(package)_sha256_hash=e6fb1737cdc8da3db76e90ca817a194249a38fcb500c2e6ecec39b29448aa873 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_aes_soft.mk b/depends/packages/crate_aes_soft.mk index 5dcd153ee..bd5a9b585 100644 --- a/depends/packages/crate_aes_soft.mk +++ b/depends/packages/crate_aes_soft.mk @@ -1,9 +1,9 @@ package=crate_aes_soft $(package)_crate_name=aes-soft -$(package)_version=0.3.3 +$(package)_version=0.2.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d +$(package)_sha256_hash=67cc03b0a090a05cb01e96998a01905d7ceedce1bc23b756c0bb7faa0682ccb1 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_aesni.mk b/depends/packages/crate_aesni.mk index dfd93bce2..ccd6bb8c9 100644 --- a/depends/packages/crate_aesni.mk +++ b/depends/packages/crate_aesni.mk @@ -1,9 +1,9 @@ package=crate_aesni $(package)_crate_name=aesni -$(package)_version=0.6.0 +$(package)_version=0.4.1 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100 +$(package)_sha256_hash=6810b7fb9f2bb4f76f05ac1c170b8dde285b6308955dc3afd89710268c958d9e $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_arrayvec.mk b/depends/packages/crate_arrayvec.mk index faa3b4f55..7de373351 100644 --- a/depends/packages/crate_arrayvec.mk +++ b/depends/packages/crate_arrayvec.mk @@ -1,9 +1,9 @@ package=crate_arrayvec $(package)_crate_name=arrayvec -$(package)_version=0.4.11 +$(package)_version=0.4.7 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba +$(package)_sha256_hash=a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_bellman.mk b/depends/packages/crate_bellman.mk index 49f63e5b4..5c7d904ca 100644 --- a/depends/packages/crate_bellman.mk +++ b/depends/packages/crate_bellman.mk @@ -1,9 +1,9 @@ package=crate_bellman $(package)_crate_name=bellman -$(package)_version=0.6.0 +$(package)_version=0.1.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=2be536193834affcd8a6d362963e66dec8c6bca4d2009f5bac55ec9002776ff2 +$(package)_sha256_hash=eae372472c7ea8f7c8fc6a62f7d5535db8302de7f1aafda2e13a97c4830d3bcf $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_arrayref.mk b/depends/packages/crate_bitflags.mk similarity index 69% rename from depends/packages/crate_arrayref.mk rename to depends/packages/crate_bitflags.mk index 186f5e77b..844ec37e8 100644 --- a/depends/packages/crate_arrayref.mk +++ b/depends/packages/crate_bitflags.mk @@ -1,9 +1,9 @@ -package=crate_arrayref -$(package)_crate_name=arrayref -$(package)_version=0.3.5 +package=crate_bitflags +$(package)_crate_name=bitflags +$(package)_version=1.0.1 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee +$(package)_sha256_hash=b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_blake2_rfc.mk b/depends/packages/crate_blake2_rfc.mk new file mode 100644 index 000000000..73ef7edde --- /dev/null +++ b/depends/packages/crate_blake2_rfc.mk @@ -0,0 +1,16 @@ +package=crate_blake2_rfc +$(package)_crate_name=blake2-rfc +$(package)_download_path=https://github.com/gtank/$($(package)_crate_name)/archive/ +$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz +$(package)_download_file=$($(package)_git_commit).tar.gz +$(package)_sha256_hash=8a873cc41f02e669e8071ab5919931dd4263f050becf0c19820b0497c07b0ca3 +$(package)_git_commit=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_unpackaged_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_blake2s_simd.mk b/depends/packages/crate_blake2s_simd.mk deleted file mode 100644 index bea01c9b1..000000000 --- a/depends/packages/crate_blake2s_simd.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_blake2s_simd -$(package)_crate_name=blake2s_simd -$(package)_version=0.5.8 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=979da0ce13c897d6be19e005ea77ac12b0fea0157aeeee7feb8c49f91386f0ea -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_block_buffer.mk b/depends/packages/crate_block_buffer.mk deleted file mode 100644 index 26d0b5e48..000000000 --- a/depends/packages/crate_block_buffer.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_block_buffer -$(package)_crate_name=block-buffer -$(package)_version=0.7.3 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_block_cipher_trait.mk b/depends/packages/crate_block_cipher_trait.mk index 553bd52a9..a2ef96576 100644 --- a/depends/packages/crate_block_cipher_trait.mk +++ b/depends/packages/crate_block_cipher_trait.mk @@ -1,9 +1,9 @@ package=crate_block_cipher_trait $(package)_crate_name=block-cipher-trait -$(package)_version=0.6.2 +$(package)_version=0.5.3 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774 +$(package)_sha256_hash=370424437b9459f3dfd68428ed9376ddfe03d8b70ede29cc533b3557df186ab4 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_block_padding.mk b/depends/packages/crate_block_padding.mk deleted file mode 100644 index 310a14cf6..000000000 --- a/depends/packages/crate_block_padding.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_block_padding -$(package)_crate_name=block-padding -$(package)_version=0.1.4 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_byte_tools.mk b/depends/packages/crate_byte_tools.mk index a5dff7279..03edeeb70 100644 --- a/depends/packages/crate_byte_tools.mk +++ b/depends/packages/crate_byte_tools.mk @@ -1,9 +1,9 @@ package=crate_byte_tools $(package)_crate_name=byte-tools -$(package)_version=0.3.1 +$(package)_version=0.2.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7 +$(package)_sha256_hash=560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_byteorder.mk b/depends/packages/crate_byteorder.mk index cd991687d..0df286920 100644 --- a/depends/packages/crate_byteorder.mk +++ b/depends/packages/crate_byteorder.mk @@ -1,9 +1,9 @@ package=crate_byteorder $(package)_crate_name=byteorder -$(package)_version=1.3.2 +$(package)_version=1.2.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5 +$(package)_sha256_hash=73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_c2_chacha.mk b/depends/packages/crate_c2_chacha.mk deleted file mode 100644 index fe29c2101..000000000 --- a/depends/packages/crate_c2_chacha.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_c2_chacha -$(package)_crate_name=c2-chacha -$(package)_version=0.2.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_cfg_if.mk b/depends/packages/crate_cfg_if.mk deleted file mode 100644 index af67660f9..000000000 --- a/depends/packages/crate_cfg_if.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_cfg_if -$(package)_crate_name=cfg-if -$(package)_version=0.1.9 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_constant_time_eq.mk b/depends/packages/crate_constant_time_eq.mk index 2ed631238..b782dfb22 100644 --- a/depends/packages/crate_constant_time_eq.mk +++ b/depends/packages/crate_constant_time_eq.mk @@ -1,9 +1,9 @@ package=crate_constant_time_eq $(package)_crate_name=constant_time_eq -$(package)_version=0.1.4 +$(package)_version=0.1.3 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120 +$(package)_sha256_hash=8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_crossbeam.mk b/depends/packages/crate_crossbeam.mk index 144f35007..3b31aa53b 100644 --- a/depends/packages/crate_crossbeam.mk +++ b/depends/packages/crate_crossbeam.mk @@ -1,9 +1,9 @@ package=crate_crossbeam $(package)_crate_name=crossbeam -$(package)_version=0.7.2 +$(package)_version=0.3.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c +$(package)_sha256_hash=24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_crossbeam_channel.mk b/depends/packages/crate_crossbeam_channel.mk deleted file mode 100644 index 618268f41..000000000 --- a/depends/packages/crate_crossbeam_channel.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_crossbeam_channel -$(package)_crate_name=crossbeam-channel -$(package)_version=0.3.9 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_crossbeam_deque.mk b/depends/packages/crate_crossbeam_deque.mk deleted file mode 100644 index 881a2c75b..000000000 --- a/depends/packages/crate_crossbeam_deque.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_crossbeam_deque -$(package)_crate_name=crossbeam-deque -$(package)_version=0.7.1 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_crossbeam_epoch.mk b/depends/packages/crate_crossbeam_epoch.mk deleted file mode 100644 index b68701642..000000000 --- a/depends/packages/crate_crossbeam_epoch.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_crossbeam_epoch -$(package)_crate_name=crossbeam-epoch -$(package)_version=0.7.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_crossbeam_queue.mk b/depends/packages/crate_crossbeam_queue.mk deleted file mode 100644 index 70616caf5..000000000 --- a/depends/packages/crate_crossbeam_queue.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_crossbeam_queue -$(package)_crate_name=crossbeam-queue -$(package)_version=0.1.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_crossbeam_utils.mk b/depends/packages/crate_crossbeam_utils.mk deleted file mode 100644 index eabfa26c4..000000000 --- a/depends/packages/crate_crossbeam_utils.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_crossbeam_utils -$(package)_crate_name=crossbeam-utils -$(package)_version=0.6.6 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_crunchy.mk b/depends/packages/crate_crunchy.mk deleted file mode 100644 index d984ba848..000000000 --- a/depends/packages/crate_crunchy.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_crunchy -$(package)_crate_name=crunchy -$(package)_version=0.1.6 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_crypto_api.mk b/depends/packages/crate_crypto_api.mk deleted file mode 100644 index e65f6a659..000000000 --- a/depends/packages/crate_crypto_api.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_crypto_api -$(package)_crate_name=crypto_api -$(package)_version=0.2.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_crypto_api_chachapoly.mk b/depends/packages/crate_crypto_api_chachapoly.mk deleted file mode 100644 index 5ae64dcac..000000000 --- a/depends/packages/crate_crypto_api_chachapoly.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_crypto_api_chachapoly -$(package)_crate_name=crypto_api_chachapoly -$(package)_version=0.2.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_digest.mk b/depends/packages/crate_digest.mk index 63d7c74e1..029ccd7bd 100644 --- a/depends/packages/crate_digest.mk +++ b/depends/packages/crate_digest.mk @@ -1,9 +1,9 @@ package=crate_digest $(package)_crate_name=digest -$(package)_version=0.8.1 +$(package)_version=0.7.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5 +$(package)_sha256_hash=00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_directories.mk b/depends/packages/crate_directories.mk deleted file mode 100644 index 7a3a93d53..000000000 --- a/depends/packages/crate_directories.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_directories -$(package)_crate_name=directories -$(package)_version=1.0.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_fake_simd.mk b/depends/packages/crate_fake_simd.mk deleted file mode 100644 index 44331b71a..000000000 --- a/depends/packages/crate_fake_simd.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_fake_simd -$(package)_crate_name=fake-simd -$(package)_version=0.1.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_ff.mk b/depends/packages/crate_ff.mk deleted file mode 100644 index 05372070e..000000000 --- a/depends/packages/crate_ff.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_ff -$(package)_crate_name=ff -$(package)_version=0.6.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=c4b967a3ee6ae993f0094174257d404a5818f58be79d67a1aea1ec8996d28906 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_ff_derive.mk b/depends/packages/crate_ff_derive.mk deleted file mode 100644 index 2d3440472..000000000 --- a/depends/packages/crate_ff_derive.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_ff_derive -$(package)_crate_name=ff_derive -$(package)_version=0.6.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=a3776aaf60a45037a9c3cabdd8542b38693acaa3e241ff957181b72579d29feb -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_fpe.mk b/depends/packages/crate_fpe.mk index 12baf031c..ba6e344ac 100644 --- a/depends/packages/crate_fpe.mk +++ b/depends/packages/crate_fpe.mk @@ -1,9 +1,9 @@ package=crate_fpe $(package)_crate_name=fpe -$(package)_version=0.2.0 +$(package)_version=0.1.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4 +$(package)_sha256_hash=ce3371c82bfbd984f624cab093f55e7336f5a6e589f8518e1258f54f011b89ad $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_bigint.mk b/depends/packages/crate_fuchsia_zircon.mk similarity index 67% rename from depends/packages/crate_bigint.mk rename to depends/packages/crate_fuchsia_zircon.mk index 6dae98362..f8e10aa55 100644 --- a/depends/packages/crate_bigint.mk +++ b/depends/packages/crate_fuchsia_zircon.mk @@ -1,9 +1,9 @@ -package=crate_bigint -$(package)_crate_name=bigint -$(package)_version=4.4.1 +package=crate_fuchsia_zircon +$(package)_crate_name=fuchsia-zircon +$(package)_version=0.3.3 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282 +$(package)_sha256_hash=2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_blake2b_simd.mk b/depends/packages/crate_fuchsia_zircon_sys.mk similarity index 66% rename from depends/packages/crate_blake2b_simd.mk rename to depends/packages/crate_fuchsia_zircon_sys.mk index c9e3ce0c8..bc978bbb2 100644 --- a/depends/packages/crate_blake2b_simd.mk +++ b/depends/packages/crate_fuchsia_zircon_sys.mk @@ -1,9 +1,9 @@ -package=crate_blake2b_simd -$(package)_crate_name=blake2b_simd -$(package)_version=0.5.8 +package=crate_fuchsia_zircon_sys +$(package)_crate_name=fuchsia-zircon-sys +$(package)_version=0.3.3 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182 +$(package)_sha256_hash=3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_futures.mk b/depends/packages/crate_futures.mk index fba10a2a4..3e0e6f990 100644 --- a/depends/packages/crate_futures.mk +++ b/depends/packages/crate_futures.mk @@ -1,9 +1,9 @@ package=crate_futures $(package)_crate_name=futures -$(package)_version=0.1.29 +$(package)_version=0.1.21 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef +$(package)_sha256_hash=1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_generic_array.mk b/depends/packages/crate_generic_array.mk index c84854ab8..ab4a566ee 100644 --- a/depends/packages/crate_generic_array.mk +++ b/depends/packages/crate_generic_array.mk @@ -1,9 +1,9 @@ package=crate_generic_array $(package)_crate_name=generic-array -$(package)_version=0.12.3 +$(package)_version=0.9.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec +$(package)_sha256_hash=ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_getrandom.mk b/depends/packages/crate_getrandom.mk deleted file mode 100644 index 460ab8b15..000000000 --- a/depends/packages/crate_getrandom.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_getrandom -$(package)_crate_name=getrandom -$(package)_version=0.1.12 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_group.mk b/depends/packages/crate_group.mk deleted file mode 100644 index 6931c7a51..000000000 --- a/depends/packages/crate_group.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_group -$(package)_crate_name=group -$(package)_version=0.6.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=f15be54742789e36f03307c8fdf0621201e1345e94f1387282024178b5e9ec8c -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_hex.mk b/depends/packages/crate_hex.mk deleted file mode 100644 index f93417a30..000000000 --- a/depends/packages/crate_hex.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_hex -$(package)_crate_name=hex -$(package)_version=0.3.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_lazy_static.mk b/depends/packages/crate_lazy_static.mk index 50f4f6ffc..208dc1dad 100644 --- a/depends/packages/crate_lazy_static.mk +++ b/depends/packages/crate_lazy_static.mk @@ -1,9 +1,9 @@ package=crate_lazy_static $(package)_crate_name=lazy_static -$(package)_version=1.4.0 +$(package)_version=1.0.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646 +$(package)_sha256_hash=c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_libc.mk b/depends/packages/crate_libc.mk index a8752b964..2e1a2c074 100644 --- a/depends/packages/crate_libc.mk +++ b/depends/packages/crate_libc.mk @@ -1,9 +1,9 @@ package=crate_libc $(package)_crate_name=libc -$(package)_version=0.2.62 +$(package)_version=0.2.40 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba +$(package)_sha256_hash=6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_log.mk b/depends/packages/crate_log.mk deleted file mode 100644 index 3bc1ec01a..000000000 --- a/depends/packages/crate_log.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_log -$(package)_crate_name=log -$(package)_version=0.4.8 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_memoffset.mk b/depends/packages/crate_memoffset.mk deleted file mode 100644 index c4e9fcd27..000000000 --- a/depends/packages/crate_memoffset.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_memoffset -$(package)_crate_name=memoffset -$(package)_version=0.5.1 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_nodrop.mk b/depends/packages/crate_nodrop.mk index 70d589b5c..1997574c0 100644 --- a/depends/packages/crate_nodrop.mk +++ b/depends/packages/crate_nodrop.mk @@ -1,9 +1,9 @@ package=crate_nodrop $(package)_crate_name=nodrop -$(package)_version=0.1.13 +$(package)_version=0.1.12 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945 +$(package)_sha256_hash=9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_num_bigint.mk b/depends/packages/crate_num_bigint.mk index 3b97d94f7..bc1ff4a32 100644 --- a/depends/packages/crate_num_bigint.mk +++ b/depends/packages/crate_num_bigint.mk @@ -1,9 +1,9 @@ package=crate_num_bigint $(package)_crate_name=num-bigint -$(package)_version=0.2.3 +$(package)_version=0.2.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a +$(package)_sha256_hash=3eceac7784c5dc97c2d6edf30259b4e153e6e2b42b3c85e9a6e9f45d06caef6e $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_num_cpus.mk b/depends/packages/crate_num_cpus.mk index 63e07acfa..fdaebe4db 100644 --- a/depends/packages/crate_num_cpus.mk +++ b/depends/packages/crate_num_cpus.mk @@ -1,9 +1,9 @@ package=crate_num_cpus $(package)_crate_name=num_cpus -$(package)_version=1.10.1 +$(package)_version=1.8.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273 +$(package)_sha256_hash=c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_num_integer.mk b/depends/packages/crate_num_integer.mk index 5e9c455a5..ea479e8e7 100644 --- a/depends/packages/crate_num_integer.mk +++ b/depends/packages/crate_num_integer.mk @@ -1,9 +1,9 @@ package=crate_num_integer $(package)_crate_name=num-integer -$(package)_version=0.1.41 +$(package)_version=0.1.39 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09 +$(package)_sha256_hash=e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_num_traits.mk b/depends/packages/crate_num_traits.mk index 19d87838f..f0ffbe5a1 100644 --- a/depends/packages/crate_num_traits.mk +++ b/depends/packages/crate_num_traits.mk @@ -1,9 +1,9 @@ package=crate_num_traits $(package)_crate_name=num-traits -$(package)_version=0.2.8 +$(package)_version=0.2.5 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32 +$(package)_sha256_hash=630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_opaque_debug.mk b/depends/packages/crate_opaque_debug.mk index 7c493bba3..7d7a5e914 100644 --- a/depends/packages/crate_opaque_debug.mk +++ b/depends/packages/crate_opaque_debug.mk @@ -1,9 +1,9 @@ package=crate_opaque_debug $(package)_crate_name=opaque-debug -$(package)_version=0.2.3 +$(package)_version=0.1.1 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c +$(package)_sha256_hash=d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_pairing.mk b/depends/packages/crate_pairing.mk index 454b8f106..c81e23bed 100644 --- a/depends/packages/crate_pairing.mk +++ b/depends/packages/crate_pairing.mk @@ -1,9 +1,9 @@ package=crate_pairing $(package)_crate_name=pairing -$(package)_version=0.16.0 +$(package)_version=0.14.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b8290dea210a712682cd65031dc2b34fd132cf2729def3df7ee08f0737ff5ed6 +$(package)_sha256_hash=ceda21136251c6d5a422d3d798d8ac22515a6e8d3521bb60c59a8349d36d0d57 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_ppv_lite86.mk b/depends/packages/crate_ppv_lite86.mk deleted file mode 100644 index 30641f95a..000000000 --- a/depends/packages/crate_ppv_lite86.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_ppv_lite86 -$(package)_crate_name=ppv-lite86 -$(package)_version=0.2.5 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_proc_macro2.mk b/depends/packages/crate_proc_macro2.mk deleted file mode 100644 index 4e59111aa..000000000 --- a/depends/packages/crate_proc_macro2.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_proc_macro2 -$(package)_crate_name=proc-macro2 -$(package)_version=1.0.3 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_quote.mk b/depends/packages/crate_quote.mk deleted file mode 100644 index 48e0c8ea1..000000000 --- a/depends/packages/crate_quote.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_quote -$(package)_crate_name=quote -$(package)_version=1.0.2 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_rand.mk b/depends/packages/crate_rand.mk index 7832db1b3..16fb81753 100644 --- a/depends/packages/crate_rand.mk +++ b/depends/packages/crate_rand.mk @@ -1,9 +1,9 @@ package=crate_rand $(package)_crate_name=rand -$(package)_version=0.7.0 +$(package)_version=0.4.2 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c +$(package)_sha256_hash=eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_rand_chacha.mk b/depends/packages/crate_rand_chacha.mk deleted file mode 100644 index ed2953fab..000000000 --- a/depends/packages/crate_rand_chacha.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_rand_chacha -$(package)_crate_name=rand_chacha -$(package)_version=0.2.1 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_rand_core.mk b/depends/packages/crate_rand_core.mk deleted file mode 100644 index 230b257c9..000000000 --- a/depends/packages/crate_rand_core.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_rand_core -$(package)_crate_name=rand_core -$(package)_version=0.5.1 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_rand_hc.mk b/depends/packages/crate_rand_hc.mk deleted file mode 100644 index 3d5155359..000000000 --- a/depends/packages/crate_rand_hc.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_rand_hc -$(package)_crate_name=rand_hc -$(package)_version=0.2.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_rand_xorshift.mk b/depends/packages/crate_rand_xorshift.mk deleted file mode 100644 index 6daa22805..000000000 --- a/depends/packages/crate_rand_xorshift.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_rand_xorshift -$(package)_crate_name=rand_xorshift -$(package)_version=0.2.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_rustc_version.mk b/depends/packages/crate_rustc_version.mk deleted file mode 100644 index f3c7410ee..000000000 --- a/depends/packages/crate_rustc_version.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_rustc_version -$(package)_crate_name=rustc_version -$(package)_version=0.2.3 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_sapling_crypto.mk b/depends/packages/crate_sapling_crypto.mk new file mode 100644 index 000000000..2da5e2406 --- /dev/null +++ b/depends/packages/crate_sapling_crypto.mk @@ -0,0 +1,16 @@ +package=crate_sapling_crypto +$(package)_crate_name=sapling-crypto +$(package)_download_path=https://github.com/zcash-hackworks/$($(package)_crate_name)/archive/ +$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz +$(package)_download_file=$($(package)_git_commit).tar.gz +$(package)_sha256_hash=ae3a122b1f1ce97b4e80e0e8542e19aa1516e99e6c72875688c886af1a881558 +$(package)_git_commit=21084bde2019c04bd34208e63c3560fe2c02fb0e +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_unpackaged_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/crate_scopeguard.mk b/depends/packages/crate_scopeguard.mk deleted file mode 100644 index a3a778dc4..000000000 --- a/depends/packages/crate_scopeguard.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_scopeguard -$(package)_crate_name=scopeguard -$(package)_version=1.0.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_semver.mk b/depends/packages/crate_semver.mk deleted file mode 100644 index 2ef1ba869..000000000 --- a/depends/packages/crate_semver.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_semver -$(package)_crate_name=semver -$(package)_version=0.9.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_semver_parser.mk b/depends/packages/crate_semver_parser.mk deleted file mode 100644 index 9316860ac..000000000 --- a/depends/packages/crate_semver_parser.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_semver_parser -$(package)_crate_name=semver-parser -$(package)_version=0.7.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_sha2.mk b/depends/packages/crate_sha2.mk deleted file mode 100644 index 344582dbb..000000000 --- a/depends/packages/crate_sha2.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_sha2 -$(package)_crate_name=sha2 -$(package)_version=0.8.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_autocfg.mk b/depends/packages/crate_stream_cipher.mk similarity index 67% rename from depends/packages/crate_autocfg.mk rename to depends/packages/crate_stream_cipher.mk index 1b36846ed..b0ede4e55 100644 --- a/depends/packages/crate_autocfg.mk +++ b/depends/packages/crate_stream_cipher.mk @@ -1,9 +1,9 @@ -package=crate_autocfg -$(package)_crate_name=autocfg -$(package)_version=0.1.6 +package=crate_stream_cipher +$(package)_crate_name=stream-cipher +$(package)_version=0.1.1 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875 +$(package)_sha256_hash=30dc6118470d69ce0fdcf7e6f95e95853f7f4f72f80d835d4519577c323814ab $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_syn.mk b/depends/packages/crate_syn.mk deleted file mode 100644 index 37593b2e1..000000000 --- a/depends/packages/crate_syn.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_syn -$(package)_crate_name=syn -$(package)_version=1.0.5 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_typenum.mk b/depends/packages/crate_typenum.mk index f02fa5001..bc5a235a8 100644 --- a/depends/packages/crate_typenum.mk +++ b/depends/packages/crate_typenum.mk @@ -1,9 +1,9 @@ package=crate_typenum $(package)_crate_name=typenum -$(package)_version=1.11.2 +$(package)_version=1.10.0 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9 +$(package)_sha256_hash=612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_unicode_xid.mk b/depends/packages/crate_unicode_xid.mk deleted file mode 100644 index 16f7be517..000000000 --- a/depends/packages/crate_unicode_xid.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_unicode_xid -$(package)_crate_name=unicode-xid -$(package)_version=0.2.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_wasi.mk b/depends/packages/crate_wasi.mk deleted file mode 100644 index 613c52627..000000000 --- a/depends/packages/crate_wasi.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_wasi -$(package)_crate_name=wasi -$(package)_version=0.7.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_winapi.mk b/depends/packages/crate_winapi.mk index b8f86a00b..6aafb0251 100644 --- a/depends/packages/crate_winapi.mk +++ b/depends/packages/crate_winapi.mk @@ -1,9 +1,9 @@ package=crate_winapi $(package)_crate_name=winapi -$(package)_version=0.3.8 +$(package)_version=0.3.4 $(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) $(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6 +$(package)_sha256_hash=04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3 $(package)_crate_versioned_name=$($(package)_crate_name) define $(package)_preprocess_cmds diff --git a/depends/packages/crate_zcash_history.mk b/depends/packages/crate_zcash_history.mk deleted file mode 100644 index fd5570fa4..000000000 --- a/depends/packages/crate_zcash_history.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_zcash_history -$(package)_crate_name=zcash_history -$(package)_version=0.2.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=abfbab9accba014bbf3098d5aa66c1714d0db4abe25b999b8400bbd626ccd2f4 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_zcash_primitives.mk b/depends/packages/crate_zcash_primitives.mk deleted file mode 100644 index 8d22560ff..000000000 --- a/depends/packages/crate_zcash_primitives.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_zcash_primitives -$(package)_crate_name=zcash_primitives -$(package)_version=0.2.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=8f33b9e4f3b4db97234fc79ea67b12f2d5778bde8f3eab6dbba52eb54c596585 -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_zcash_proofs.mk b/depends/packages/crate_zcash_proofs.mk deleted file mode 100644 index c3e40b0f3..000000000 --- a/depends/packages/crate_zcash_proofs.mk +++ /dev/null @@ -1,15 +0,0 @@ -package=crate_zcash_proofs -$(package)_crate_name=zcash_proofs -$(package)_version=0.2.0 -$(package)_download_path=https://static.crates.io/crates/$($(package)_crate_name) -$(package)_file_name=$($(package)_crate_name)-$($(package)_version).crate -$(package)_sha256_hash=2011f78f14d5121248d3b4f921434207b1d870fb3bf2efc7d784cae79b19bfbc -$(package)_crate_versioned_name=$($(package)_crate_name) - -define $(package)_preprocess_cmds - $(call generate_crate_checksum,$(package)) -endef - -define $(package)_stage_cmds - $(call vendor_crate_source,$(package)) -endef diff --git a/depends/packages/crate_zip32.mk b/depends/packages/crate_zip32.mk new file mode 100644 index 000000000..e47f1fb94 --- /dev/null +++ b/depends/packages/crate_zip32.mk @@ -0,0 +1,16 @@ +package=crate_zip32 +$(package)_crate_name=zip32 +$(package)_download_path=https://github.com/zcash-hackworks/$($(package)_crate_name)/archive/ +$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz +$(package)_download_file=$($(package)_git_commit).tar.gz +$(package)_sha256_hash=b0b011ea96524f0d918a44c7ab8a3dec6270879d1ff03d7dbda6c676d25caa7e +$(package)_git_commit=176470ef41583b5bd0bd749bd1b61d417aa8ec79 +$(package)_crate_versioned_name=$($(package)_crate_name) + +define $(package)_preprocess_cmds + $(call generate_unpackaged_crate_checksum,$(package)) +endef + +define $(package)_stage_cmds + $(call vendor_crate_source,$(package)) +endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 4fec7a42c..2668433a6 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -2,77 +2,36 @@ rust_crates := \ crate_aes \ crate_aesni \ crate_aes_soft \ - crate_arrayref \ crate_arrayvec \ - crate_autocfg \ - crate_bellman \ - crate_bigint \ + crate_bitflags \ crate_bit_vec \ - crate_blake2b_simd \ - crate_blake2s_simd \ - crate_block_buffer \ + crate_blake2_rfc \ crate_block_cipher_trait \ - crate_block_padding \ crate_byte_tools \ crate_byteorder \ - crate_c2_chacha \ - crate_cfg_if \ crate_constant_time_eq \ - crate_crossbeam_channel \ - crate_crossbeam_deque \ - crate_crossbeam_epoch \ - crate_crossbeam_queue \ - crate_crossbeam_utils \ crate_crossbeam \ - crate_crunchy \ - crate_crypto_api_chachapoly \ - crate_crypto_api \ crate_digest \ - crate_directories \ - crate_fake_simd \ - crate_ff_derive \ - crate_ff \ crate_fpe \ + crate_fuchsia_zircon \ + crate_fuchsia_zircon_sys \ crate_futures_cpupool \ crate_futures \ crate_generic_array \ - crate_getrandom \ - crate_group \ - crate_hex \ crate_lazy_static \ crate_libc \ - crate_log \ - crate_memoffset \ crate_nodrop \ crate_num_bigint \ crate_num_cpus \ crate_num_integer \ crate_num_traits \ crate_opaque_debug \ - crate_pairing \ - crate_ppv_lite86 \ - crate_proc_macro2 \ - crate_quote \ - crate_rand_chacha \ - crate_rand_core \ - crate_rand_hc \ - crate_rand_xorshift \ crate_rand \ - crate_rustc_version \ - crate_scopeguard \ - crate_semver_parser \ - crate_semver \ - crate_sha2 \ - crate_syn \ + crate_stream_cipher \ crate_typenum \ - crate_unicode_xid \ - crate_wasi \ crate_winapi_i686_pc_windows_gnu \ crate_winapi \ - crate_winapi_x86_64_pc_windows_gnu \ - crate_zcash_history \ - crate_zcash_primitives \ - crate_zcash_proofs + crate_winapi_x86_64_pc_windows_gnu rust_packages := rust $(rust_crates) zcash_packages := libsodium utfcpp diff --git a/src/Makefile.am b/src/Makefile.am index 7c6bccfca..ac2e63739 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,7 @@ BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include -BITCOIN_INCLUDES += -I$(srcdir)/rustzcash/include +BITCOIN_INCLUDES += -I$(srcdir)/rust/librustzcash/include BITCOIN_INCLUDES += -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) LIBBITCOIN_SERVER=libbitcoin_server.a @@ -29,7 +29,7 @@ LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBSECP256K1=secp256k1/libsecp256k1.la -LIBRUSTZCASH=rustzcash/target/$(RUST_TARGET)/release/librustzcash.a +LIBRUSTZCASH=rust/target/$(RUST_TARGET)/release/librustzcash.a LIBZCASH=libzcash.a if ENABLE_ZMQ @@ -42,8 +42,8 @@ endif $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) -cargo-build: $(wildcard rustzcash/include/*.h) - RUSTC="$(RUSTC)" $(CARGO) build $(CARGO_FLAGS) --manifest-path=$(srcdir)/rustzcash/Cargo.toml --target=$(RUST_TARGET) +cargo-build: $(wildcard rust/librustzcash/include/*.h) + RUSTC="$(RUSTC)" $(CARGO) build $(CARGO_FLAGS) --manifest-path=$(srcdir)/rust/Cargo.toml --target=$(RUST_TARGET) $(LIBRUSTZCASH): cargo-build @@ -516,7 +516,7 @@ EXTRA_DIST = $(CTAES_DIST) clean-local: -$(MAKE) -C secp256k1 clean - -rm -rf rustzcash/target + -rm -rf rust/target -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno rm -f fuzz.cpp rm -rf fuzzing/*/output diff --git a/src/rust/.gitignore b/src/rust/.gitignore new file mode 100644 index 000000000..eb5a316cb --- /dev/null +++ b/src/rust/.gitignore @@ -0,0 +1 @@ +target diff --git a/src/rust/.gitlab-ci.yml b/src/rust/.gitlab-ci.yml new file mode 100644 index 000000000..9e74d5166 --- /dev/null +++ b/src/rust/.gitlab-ci.yml @@ -0,0 +1,66 @@ + +# /************************************************************************ + # File: .gitlab-ci.yml + # Author: mdr0id + # Date: 9/10/2018 + # Description: Used to setup runners/jobs for librustzcash + # Usage: Commit source and the pipeline will trigger the according jobs. + # For now the build and test are done in the same jobs. + # + # Known bugs/missing features: + # + # ************************************************************************/ + +stages: + - build + - test + - deploy + +rust-latest: + stage: build + image: rust:latest + script: + - cargo --verbose --version + - time cargo build --verbose + +rust-nightly: + stage: build + image: rustlang/rust:nightly + script: + - cargo --verbose --version + - cargo build --verbose + allow_failure: true + +librustzcash-test-latest: + stage: test + image: rust:latest + script: + - cargo --verbose --version + - time cargo test --release --verbose + +librustzcash-test-rust-nightly: + stage: test + image: rustlang/rust:nightly + script: + - cargo --verbose --version + - cargo test --release --verbose + allow_failure: true + +#used to manually deploy a given release +librustzcash-rust-rc: + stage: deploy + image: rust:latest + script: + - cargo --verbose --version + - time cargo build --release --verbose + when: manual + +#used to manually deploy a given release +librustzcash-rust-nightly-rc: + stage: deploy + image: rustlang/rust:nightly + script: + - cargo --verbose --version + - cargo build --release --verbose + allow_failure: true + when: manual diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock new file mode 100644 index 000000000..3c6c94bfb --- /dev/null +++ b/src/rust/Cargo.lock @@ -0,0 +1,342 @@ +[[package]] +name = "aes" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aes-soft 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aesni 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aes-soft" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aesni" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "stream-cipher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bellman" +version = "0.1.0" +dependencies = [ + "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pairing 0.14.2", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit-vec" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9#7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-cipher-trait" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byte-tools" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "constant_time_eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crossbeam" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "digest" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fpe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "librustzcash" +version = "0.1.0" +dependencies = [ + "bellman 0.1.0", + "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "pairing 0.14.2", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sapling-crypto 0.0.1", + "zip32 0.0.0", +] + +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-bigint" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pairing" +version = "0.14.2" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sapling-crypto" +version = "0.0.1" +dependencies = [ + "bellman 0.1.0", + "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pairing 0.14.2", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stream-cipher" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "zcash_primitives" +version = "0.0.0" + +[[package]] +name = "zcash_proofs" +version = "0.0.0" + +[[package]] +name = "zcash_wallet" +version = "0.0.0" + +[[package]] +name = "zip32" +version = "0.0.0" +dependencies = [ + "aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fpe 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pairing 0.14.2", + "sapling-crypto 0.0.1", +] + +[metadata] +"checksum aes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6fb1737cdc8da3db76e90ca817a194249a38fcb500c2e6ecec39b29448aa873" +"checksum aes-soft 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67cc03b0a090a05cb01e96998a01905d7ceedce1bc23b756c0bb7faa0682ccb1" +"checksum aesni 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6810b7fb9f2bb4f76f05ac1c170b8dde285b6308955dc3afd89710268c958d9e" +"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" +"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "" +"checksum block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "370424437b9459f3dfd68428ed9376ddfe03d8b70ede29cc533b3557df186ab4" +"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" +"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" +"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" +"checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603" +"checksum fpe 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce3371c82bfbd984f624cab093f55e7336f5a6e589f8518e1258f54f011b89ad" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" +"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" +"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum num-bigint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3eceac7784c5dc97c2d6edf30259b4e153e6e2b42b3c85e9a6e9f45d06caef6e" +"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" +"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +"checksum opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7" +"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum stream-cipher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "30dc6118470d69ce0fdcf7e6f95e95853f7f4f72f80d835d4519577c323814ab" +"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml new file mode 100644 index 000000000..3d1363de3 --- /dev/null +++ b/src/rust/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +members = [ + "bellman", + "librustzcash", + "pairing", + "sapling-crypto", + "zcash_primitives", + "zcash_proofs", + "zcash_wallet", + "zip32", +] + +[profile.release] +lto = true +panic = 'abort' +codegen-units = 1 diff --git a/src/rust/LICENSE-APACHE b/src/rust/LICENSE-APACHE new file mode 100644 index 000000000..1e5006dc1 --- /dev/null +++ b/src/rust/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/src/rust/LICENSE-MIT b/src/rust/LICENSE-MIT new file mode 100644 index 000000000..5b7be8e66 --- /dev/null +++ b/src/rust/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Zcash Company + +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. diff --git a/src/rust/README.md b/src/rust/README.md new file mode 100644 index 000000000..3df799fe0 --- /dev/null +++ b/src/rust/README.md @@ -0,0 +1,24 @@ +# Zcash Rust crates + +This repository contains a (work-in-progress) set of Rust crates for +working with Zcash. + +## Security Warnings + +These libraries are currently under development and have not been fully-reviewed. + +## License + +All code in this workspace is licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/bellman/.gitignore b/src/rust/bellman/.gitignore new file mode 100644 index 000000000..a9d37c560 --- /dev/null +++ b/src/rust/bellman/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/src/rust/bellman/COPYRIGHT b/src/rust/bellman/COPYRIGHT new file mode 100644 index 000000000..8b5f8cf37 --- /dev/null +++ b/src/rust/bellman/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "bellman" library are retained by their contributors. No +copyright assignment is required to contribute to the "bellman" library. + +The "bellman" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/bellman/Cargo.toml b/src/rust/bellman/Cargo.toml new file mode 100644 index 000000000..b7699cf76 --- /dev/null +++ b/src/rust/bellman/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["Sean Bowe "] +description = "zk-SNARK library" +documentation = "https://github.com/ebfull/bellman" +homepage = "https://github.com/ebfull/bellman" +license = "MIT/Apache-2.0" +name = "bellman" +repository = "https://github.com/ebfull/bellman" +version = "0.1.0" + +[dependencies] +rand = "0.4" +bit-vec = "0.4.4" +futures = "0.1" +futures-cpupool = "0.1" +num_cpus = "1" +crossbeam = "0.3" +pairing = { path = "../pairing" } +byteorder = "1" + +[features] +default = [] diff --git a/src/rust/bellman/LICENSE-APACHE b/src/rust/bellman/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/src/rust/bellman/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/rust/bellman/LICENSE-MIT b/src/rust/bellman/LICENSE-MIT new file mode 100644 index 000000000..31aa79387 --- /dev/null +++ b/src/rust/bellman/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/src/rust/bellman/README.md b/src/rust/bellman/README.md new file mode 100644 index 000000000..659a81c45 --- /dev/null +++ b/src/rust/bellman/README.md @@ -0,0 +1,19 @@ +# bellman [![Crates.io](https://img.shields.io/crates/v/bellman.svg)](https://crates.io/crates/bellman) # + +This is a research project being built for [Zcash](https://z.cash/). + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/bellman/src/domain.rs b/src/rust/bellman/src/domain.rs new file mode 100644 index 000000000..ff626e5d0 --- /dev/null +++ b/src/rust/bellman/src/domain.rs @@ -0,0 +1,494 @@ +//! This module contains an `EvaluationDomain` abstraction for +//! performing various kinds of polynomial arithmetic on top of +//! the scalar field. +//! +//! In pairing-based SNARKs like Groth16, we need to calculate +//! a quotient polynomial over a target polynomial with roots +//! at distinct points associated with each constraint of the +//! constraint system. In order to be efficient, we choose these +//! roots to be the powers of a 2^n root of unity in the field. +//! This allows us to perform polynomial operations in O(n) +//! by performing an O(n log n) FFT over such a domain. + +use pairing::{ + Engine, + Field, + PrimeField, + CurveProjective +}; + +use super::{ + SynthesisError +}; + +use super::multicore::Worker; + +pub struct EvaluationDomain> { + coeffs: Vec, + exp: u32, + omega: E::Fr, + omegainv: E::Fr, + geninv: E::Fr, + minv: E::Fr +} + +impl> EvaluationDomain { + pub fn as_ref(&self) -> &[G] { + &self.coeffs + } + + pub fn as_mut(&mut self) -> &mut [G] { + &mut self.coeffs + } + + pub fn into_coeffs(self) -> Vec { + self.coeffs + } + + pub fn from_coeffs(mut coeffs: Vec) -> Result, SynthesisError> + { + // Compute the size of our evaluation domain + let mut m = 1; + let mut exp = 0; + while m < coeffs.len() { + m *= 2; + exp += 1; + + // The pairing-friendly curve may not be able to support + // large enough (radix2) evaluation domains. + if exp >= E::Fr::S { + return Err(SynthesisError::PolynomialDegreeTooLarge) + } + } + + // Compute omega, the 2^exp primitive root of unity + let mut omega = E::Fr::root_of_unity(); + for _ in exp..E::Fr::S { + omega.square(); + } + + // Extend the coeffs vector with zeroes if necessary + coeffs.resize(m, G::group_zero()); + + Ok(EvaluationDomain { + coeffs: coeffs, + exp: exp, + omega: omega, + omegainv: omega.inverse().unwrap(), + geninv: E::Fr::multiplicative_generator().inverse().unwrap(), + minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap() + }) + } + + pub fn fft(&mut self, worker: &Worker) + { + best_fft(&mut self.coeffs, worker, &self.omega, self.exp); + } + + pub fn ifft(&mut self, worker: &Worker) + { + best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp); + + worker.scope(self.coeffs.len(), |scope, chunk| { + let minv = self.minv; + + for v in self.coeffs.chunks_mut(chunk) { + scope.spawn(move || { + for v in v { + v.group_mul_assign(&minv); + } + }); + } + }); + } + + pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) + { + worker.scope(self.coeffs.len(), |scope, chunk| { + for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { + scope.spawn(move || { + let mut u = g.pow(&[(i * chunk) as u64]); + for v in v.iter_mut() { + v.group_mul_assign(&u); + u.mul_assign(&g); + } + }); + } + }); + } + + pub fn coset_fft(&mut self, worker: &Worker) + { + self.distribute_powers(worker, E::Fr::multiplicative_generator()); + self.fft(worker); + } + + pub fn icoset_fft(&mut self, worker: &Worker) + { + let geninv = self.geninv; + + self.ifft(worker); + self.distribute_powers(worker, geninv); + } + + /// This evaluates t(tau) for this domain, which is + /// tau^m - 1 for these radix-2 domains. + pub fn z(&self, tau: &E::Fr) -> E::Fr { + let mut tmp = tau.pow(&[self.coeffs.len() as u64]); + tmp.sub_assign(&E::Fr::one()); + + tmp + } + + /// The target polynomial is the zero polynomial in our + /// evaluation domain, so we must perform division over + /// a coset. + pub fn divide_by_z_on_coset(&mut self, worker: &Worker) + { + let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap(); + + worker.scope(self.coeffs.len(), |scope, chunk| { + for v in self.coeffs.chunks_mut(chunk) { + scope.spawn(move || { + for v in v { + v.group_mul_assign(&i); + } + }); + } + }); + } + + /// Perform O(n) multiplication of two polynomials in the domain. + pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain>) { + assert_eq!(self.coeffs.len(), other.coeffs.len()); + + worker.scope(self.coeffs.len(), |scope, chunk| { + for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { + scope.spawn(move || { + for (a, b) in a.iter_mut().zip(b.iter()) { + a.group_mul_assign(&b.0); + } + }); + } + }); + } + + /// Perform O(n) subtraction of one polynomial from another in the domain. + pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain) { + assert_eq!(self.coeffs.len(), other.coeffs.len()); + + worker.scope(self.coeffs.len(), |scope, chunk| { + for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { + scope.spawn(move || { + for (a, b) in a.iter_mut().zip(b.iter()) { + a.group_sub_assign(&b); + } + }); + } + }); + } +} + +pub trait Group: Sized + Copy + Clone + Send + Sync { + fn group_zero() -> Self; + fn group_mul_assign(&mut self, by: &E::Fr); + fn group_add_assign(&mut self, other: &Self); + fn group_sub_assign(&mut self, other: &Self); +} + +pub struct Point(pub G); + +impl PartialEq for Point { + fn eq(&self, other: &Point) -> bool { + self.0 == other.0 + } +} + +impl Copy for Point { } + +impl Clone for Point { + fn clone(&self) -> Point { + *self + } +} + +impl Group for Point { + fn group_zero() -> Self { + Point(G::zero()) + } + fn group_mul_assign(&mut self, by: &G::Scalar) { + self.0.mul_assign(by.into_repr()); + } + fn group_add_assign(&mut self, other: &Self) { + self.0.add_assign(&other.0); + } + fn group_sub_assign(&mut self, other: &Self) { + self.0.sub_assign(&other.0); + } +} + +pub struct Scalar(pub E::Fr); + +impl PartialEq for Scalar { + fn eq(&self, other: &Scalar) -> bool { + self.0 == other.0 + } +} + +impl Copy for Scalar { } + +impl Clone for Scalar { + fn clone(&self) -> Scalar { + *self + } +} + +impl Group for Scalar { + fn group_zero() -> Self { + Scalar(E::Fr::zero()) + } + fn group_mul_assign(&mut self, by: &E::Fr) { + self.0.mul_assign(by); + } + fn group_add_assign(&mut self, other: &Self) { + self.0.add_assign(&other.0); + } + fn group_sub_assign(&mut self, other: &Self) { + self.0.sub_assign(&other.0); + } +} + +fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) +{ + let log_cpus = worker.log_num_cpus(); + + if log_n <= log_cpus { + serial_fft(a, omega, log_n); + } else { + parallel_fft(a, worker, omega, log_n, log_cpus); + } +} + +fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) +{ + fn bitreverse(mut n: u32, l: u32) -> u32 { + let mut r = 0; + for _ in 0..l { + r = (r << 1) | (n & 1); + n >>= 1; + } + r + } + + let n = a.len() as u32; + assert_eq!(n, 1 << log_n); + + for k in 0..n { + let rk = bitreverse(k, log_n); + if k < rk { + a.swap(rk as usize, k as usize); + } + } + + let mut m = 1; + for _ in 0..log_n { + let w_m = omega.pow(&[(n / (2*m)) as u64]); + + let mut k = 0; + while k < n { + let mut w = E::Fr::one(); + for j in 0..m { + let mut t = a[(k+j+m) as usize]; + t.group_mul_assign(&w); + let mut tmp = a[(k+j) as usize]; + tmp.group_sub_assign(&t); + a[(k+j+m) as usize] = tmp; + a[(k+j) as usize].group_add_assign(&t); + w.mul_assign(&w_m); + } + + k += 2*m; + } + + m *= 2; + } +} + +fn parallel_fft>( + a: &mut [T], + worker: &Worker, + omega: &E::Fr, + log_n: u32, + log_cpus: u32 +) +{ + assert!(log_n >= log_cpus); + + let num_cpus = 1 << log_cpus; + let log_new_n = log_n - log_cpus; + let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; + let new_omega = omega.pow(&[num_cpus as u64]); + + worker.scope(0, |scope, _| { + let a = &*a; + + for (j, tmp) in tmp.iter_mut().enumerate() { + scope.spawn(move || { + // Shuffle into a sub-FFT + let omega_j = omega.pow(&[j as u64]); + let omega_step = omega.pow(&[(j as u64) << log_new_n]); + + let mut elt = E::Fr::one(); + for i in 0..(1 << log_new_n) { + for s in 0..num_cpus { + let idx = (i + (s << log_new_n)) % (1 << log_n); + let mut t = a[idx]; + t.group_mul_assign(&elt); + tmp[i].group_add_assign(&t); + elt.mul_assign(&omega_step); + } + elt.mul_assign(&omega_j); + } + + // Perform sub-FFT + serial_fft(tmp, &new_omega, log_new_n); + }); + } + }); + + // TODO: does this hurt or help? + worker.scope(a.len(), |scope, chunk| { + let tmp = &tmp; + + for (idx, a) in a.chunks_mut(chunk).enumerate() { + scope.spawn(move || { + let mut idx = idx * chunk; + let mask = (1 << log_cpus) - 1; + for a in a { + *a = tmp[idx & mask][idx >> log_cpus]; + idx += 1; + } + }); + } + }); +} + +// Test multiplying various (low degree) polynomials together and +// comparing with naive evaluations. +#[test] +fn polynomial_arith() { + use pairing::bls12_381::Bls12; + use rand::{self, Rand}; + + fn test_mul(rng: &mut R) + { + let worker = Worker::new(); + + for coeffs_a in 0..70 { + for coeffs_b in 0..70 { + let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::(E::Fr::rand(rng))).collect(); + let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::(E::Fr::rand(rng))).collect(); + + // naive evaluation + let mut naive = vec![Scalar(E::Fr::zero()); coeffs_a + coeffs_b]; + for (i1, a) in a.iter().enumerate() { + for (i2, b) in b.iter().enumerate() { + let mut prod = *a; + prod.group_mul_assign(&b.0); + naive[i1 + i2].group_add_assign(&prod); + } + } + + a.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero())); + b.resize(coeffs_a + coeffs_b, Scalar(E::Fr::zero())); + + let mut a = EvaluationDomain::from_coeffs(a).unwrap(); + let mut b = EvaluationDomain::from_coeffs(b).unwrap(); + + a.fft(&worker); + b.fft(&worker); + a.mul_assign(&worker, &b); + a.ifft(&worker); + + for (naive, fft) in naive.iter().zip(a.coeffs.iter()) { + assert!(naive == fft); + } + } + } + } + + let rng = &mut rand::thread_rng(); + + test_mul::(rng); +} + +#[test] +fn fft_composition() { + use pairing::bls12_381::Bls12; + use rand; + + fn test_comp(rng: &mut R) + { + let worker = Worker::new(); + + for coeffs in 0..10 { + let coeffs = 1 << coeffs; + + let mut v = vec![]; + for _ in 0..coeffs { + v.push(Scalar::(rng.gen())); + } + + let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap(); + domain.ifft(&worker); + domain.fft(&worker); + assert!(v == domain.coeffs); + domain.fft(&worker); + domain.ifft(&worker); + assert!(v == domain.coeffs); + domain.icoset_fft(&worker); + domain.coset_fft(&worker); + assert!(v == domain.coeffs); + domain.coset_fft(&worker); + domain.icoset_fft(&worker); + assert!(v == domain.coeffs); + } + } + + let rng = &mut rand::thread_rng(); + + test_comp::(rng); +} + +#[test] +fn parallel_fft_consistency() { + use pairing::bls12_381::Bls12; + use rand::{self, Rand}; + use std::cmp::min; + + fn test_consistency(rng: &mut R) + { + let worker = Worker::new(); + + for _ in 0..5 { + for log_d in 0..10 { + let d = 1 << log_d; + + let v1 = (0..d).map(|_| Scalar::(E::Fr::rand(rng))).collect::>(); + let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap(); + let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap(); + + for log_cpus in log_d..min(log_d+1, 3) { + parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus); + serial_fft(&mut v2.coeffs, &v2.omega, log_d); + + assert!(v1.coeffs == v2.coeffs); + } + } + } + } + + let rng = &mut rand::thread_rng(); + + test_consistency::(rng); +} diff --git a/src/rust/bellman/src/groth16/generator.rs b/src/rust/bellman/src/groth16/generator.rs new file mode 100644 index 000000000..1eed62db0 --- /dev/null +++ b/src/rust/bellman/src/groth16/generator.rs @@ -0,0 +1,482 @@ +use rand::Rng; + +use std::sync::Arc; + +use pairing::{ + Engine, + PrimeField, + Field, + Wnaf, + CurveProjective, + CurveAffine +}; + +use super::{ + Parameters, + VerifyingKey +}; + +use ::{ + SynthesisError, + Circuit, + ConstraintSystem, + LinearCombination, + Variable, + Index +}; + +use ::domain::{ + EvaluationDomain, + Scalar +}; + +use ::multicore::{ + Worker +}; + +/// Generates a random common reference string for +/// a circuit. +pub fn generate_random_parameters( + circuit: C, + rng: &mut R +) -> Result, SynthesisError> + where E: Engine, C: Circuit, R: Rng +{ + let g1 = rng.gen(); + let g2 = rng.gen(); + let alpha = rng.gen(); + let beta = rng.gen(); + let gamma = rng.gen(); + let delta = rng.gen(); + let tau = rng.gen(); + + generate_parameters::( + circuit, + g1, + g2, + alpha, + beta, + gamma, + delta, + tau + ) +} + +/// This is our assembly structure that we'll use to synthesize the +/// circuit into a QAP. +struct KeypairAssembly { + num_inputs: usize, + num_aux: usize, + num_constraints: usize, + at_inputs: Vec>, + bt_inputs: Vec>, + ct_inputs: Vec>, + at_aux: Vec>, + bt_aux: Vec>, + ct_aux: Vec> +} + +impl ConstraintSystem for KeypairAssembly { + type Root = Self; + + fn alloc( + &mut self, + _: A, + _: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + // There is no assignment, so we don't even invoke the + // function for obtaining one. + + let index = self.num_aux; + self.num_aux += 1; + + self.at_aux.push(vec![]); + self.bt_aux.push(vec![]); + self.ct_aux.push(vec![]); + + Ok(Variable(Index::Aux(index))) + } + + fn alloc_input( + &mut self, + _: A, + _: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + // There is no assignment, so we don't even invoke the + // function for obtaining one. + + let index = self.num_inputs; + self.num_inputs += 1; + + self.at_inputs.push(vec![]); + self.bt_inputs.push(vec![]); + self.ct_inputs.push(vec![]); + + Ok(Variable(Index::Input(index))) + } + + fn enforce( + &mut self, + _: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination + { + fn eval( + l: LinearCombination, + inputs: &mut [Vec<(E::Fr, usize)>], + aux: &mut [Vec<(E::Fr, usize)>], + this_constraint: usize + ) + { + for (index, coeff) in l.0 { + match index { + Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)), + Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)) + } + } + } + + eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints); + eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints); + eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); + + self.num_constraints += 1; + } + + fn push_namespace(&mut self, _: N) + where NR: Into, N: FnOnce() -> NR + { + // Do nothing; we don't care about namespaces in this context. + } + + fn pop_namespace(&mut self) + { + // Do nothing; we don't care about namespaces in this context. + } + + fn get_root(&mut self) -> &mut Self::Root { + self + } +} + +/// Create parameters for a circuit, given some toxic waste. +pub fn generate_parameters( + circuit: C, + g1: E::G1, + g2: E::G2, + alpha: E::Fr, + beta: E::Fr, + gamma: E::Fr, + delta: E::Fr, + tau: E::Fr +) -> Result, SynthesisError> + where E: Engine, C: Circuit +{ + let mut assembly = KeypairAssembly { + num_inputs: 0, + num_aux: 0, + num_constraints: 0, + at_inputs: vec![], + bt_inputs: vec![], + ct_inputs: vec![], + at_aux: vec![], + bt_aux: vec![], + ct_aux: vec![] + }; + + // Allocate the "one" input variable + assembly.alloc_input(|| "", || Ok(E::Fr::one()))?; + + // Synthesize the circuit. + circuit.synthesize(&mut assembly)?; + + // Input constraints to ensure full density of IC query + // x * 0 = 0 + for i in 0..assembly.num_inputs { + assembly.enforce(|| "", + |lc| lc + Variable(Index::Input(i)), + |lc| lc, + |lc| lc, + ); + } + + // Create bases for blind evaluation of polynomials at tau + let powers_of_tau = vec![Scalar::(E::Fr::zero()); assembly.num_constraints]; + let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?; + + // Compute G1 window table + let mut g1_wnaf = Wnaf::new(); + let g1_wnaf = g1_wnaf.base(g1, { + // H query + (powers_of_tau.as_ref().len() - 1) + // IC/L queries + + assembly.num_inputs + assembly.num_aux + // A query + + assembly.num_inputs + assembly.num_aux + // B query + + assembly.num_inputs + assembly.num_aux + }); + + // Compute G2 window table + let mut g2_wnaf = Wnaf::new(); + let g2_wnaf = g2_wnaf.base(g2, { + // B query + assembly.num_inputs + assembly.num_aux + }); + + let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; + let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; + + let worker = Worker::new(); + + let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1]; + { + // Compute powers of tau + { + let powers_of_tau = powers_of_tau.as_mut(); + worker.scope(powers_of_tau.len(), |scope, chunk| { + for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() + { + scope.spawn(move || { + let mut current_tau_power = tau.pow(&[(i*chunk) as u64]); + + for p in powers_of_tau { + p.0 = current_tau_power; + current_tau_power.mul_assign(&tau); + } + }); + } + }); + } + + // coeff = t(x) / delta + let mut coeff = powers_of_tau.z(&tau); + coeff.mul_assign(&delta_inverse); + + // Compute the H query with multiple threads + worker.scope(h.len(), |scope, chunk| { + for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk)) + { + let mut g1_wnaf = g1_wnaf.shared(); + + scope.spawn(move || { + // Set values of the H query to g1^{(tau^i * t(tau)) / delta} + for (h, p) in h.iter_mut().zip(p.iter()) + { + // Compute final exponent + let mut exp = p.0; + exp.mul_assign(&coeff); + + // Exponentiate + *h = g1_wnaf.scalar(exp.into_repr()); + } + + // Batch normalize + E::G1::batch_normalization(h); + }); + } + }); + } + + // Use inverse FFT to convert powers of tau to Lagrange coefficients + powers_of_tau.ifft(&worker); + let powers_of_tau = powers_of_tau.into_coeffs(); + + let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; + let mut b_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; + let mut b_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux]; + let mut ic = vec![E::G1::zero(); assembly.num_inputs]; + let mut l = vec![E::G1::zero(); assembly.num_aux]; + + fn eval( + // wNAF window tables + g1_wnaf: &Wnaf>, + g2_wnaf: &Wnaf>, + + // Lagrange coefficients for tau + powers_of_tau: &[Scalar], + + // QAP polynomials + at: &[Vec<(E::Fr, usize)>], + bt: &[Vec<(E::Fr, usize)>], + ct: &[Vec<(E::Fr, usize)>], + + // Resulting evaluated QAP polynomials + a: &mut [E::G1], + b_g1: &mut [E::G1], + b_g2: &mut [E::G2], + ext: &mut [E::G1], + + // Inverse coefficient for ext elements + inv: &E::Fr, + + // Trapdoors + alpha: &E::Fr, + beta: &E::Fr, + + // Worker + worker: &Worker + ) + { + // Sanity check + assert_eq!(a.len(), at.len()); + assert_eq!(a.len(), bt.len()); + assert_eq!(a.len(), ct.len()); + assert_eq!(a.len(), b_g1.len()); + assert_eq!(a.len(), b_g2.len()); + assert_eq!(a.len(), ext.len()); + + // Evaluate polynomials in multiple threads + worker.scope(a.len(), |scope, chunk| { + for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk) + .zip(b_g1.chunks_mut(chunk)) + .zip(b_g2.chunks_mut(chunk)) + .zip(ext.chunks_mut(chunk)) + .zip(at.chunks(chunk)) + .zip(bt.chunks(chunk)) + .zip(ct.chunks(chunk)) + { + let mut g1_wnaf = g1_wnaf.shared(); + let mut g2_wnaf = g2_wnaf.shared(); + + scope.spawn(move || { + for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut() + .zip(b_g1.iter_mut()) + .zip(b_g2.iter_mut()) + .zip(ext.iter_mut()) + .zip(at.iter()) + .zip(bt.iter()) + .zip(ct.iter()) + { + fn eval_at_tau( + powers_of_tau: &[Scalar], + p: &[(E::Fr, usize)] + ) -> E::Fr + { + let mut acc = E::Fr::zero(); + + for &(ref coeff, index) in p { + let mut n = powers_of_tau[index].0; + n.mul_assign(coeff); + acc.add_assign(&n); + } + + acc + } + + // Evaluate QAP polynomials at tau + let mut at = eval_at_tau(powers_of_tau, at); + let mut bt = eval_at_tau(powers_of_tau, bt); + let ct = eval_at_tau(powers_of_tau, ct); + + // Compute A query (in G1) + if !at.is_zero() { + *a = g1_wnaf.scalar(at.into_repr()); + } + + // Compute B query (in G1/G2) + if !bt.is_zero() { + let bt_repr = bt.into_repr(); + *b_g1 = g1_wnaf.scalar(bt_repr); + *b_g2 = g2_wnaf.scalar(bt_repr); + } + + at.mul_assign(&beta); + bt.mul_assign(&alpha); + + let mut e = at; + e.add_assign(&bt); + e.add_assign(&ct); + e.mul_assign(inv); + + *ext = g1_wnaf.scalar(e.into_repr()); + } + + // Batch normalize + E::G1::batch_normalization(a); + E::G1::batch_normalization(b_g1); + E::G2::batch_normalization(b_g2); + E::G1::batch_normalization(ext); + }); + } + }); + } + + // Evaluate for inputs. + eval( + &g1_wnaf, + &g2_wnaf, + &powers_of_tau, + &assembly.at_inputs, + &assembly.bt_inputs, + &assembly.ct_inputs, + &mut a[0..assembly.num_inputs], + &mut b_g1[0..assembly.num_inputs], + &mut b_g2[0..assembly.num_inputs], + &mut ic, + &gamma_inverse, + &alpha, + &beta, + &worker + ); + + // Evaluate for auxillary variables. + eval( + &g1_wnaf, + &g2_wnaf, + &powers_of_tau, + &assembly.at_aux, + &assembly.bt_aux, + &assembly.ct_aux, + &mut a[assembly.num_inputs..], + &mut b_g1[assembly.num_inputs..], + &mut b_g2[assembly.num_inputs..], + &mut l, + &delta_inverse, + &alpha, + &beta, + &worker + ); + + // Don't allow any elements be unconstrained, so that + // the L query is always fully dense. + for e in l.iter() { + if e.is_zero() { + return Err(SynthesisError::UnconstrainedVariable); + } + } + + let g1 = g1.into_affine(); + let g2 = g2.into_affine(); + + let vk = VerifyingKey:: { + alpha_g1: g1.mul(alpha).into_affine(), + beta_g1: g1.mul(beta).into_affine(), + beta_g2: g2.mul(beta).into_affine(), + gamma_g2: g2.mul(gamma).into_affine(), + delta_g1: g1.mul(delta).into_affine(), + delta_g2: g2.mul(delta).into_affine(), + ic: ic.into_iter().map(|e| e.into_affine()).collect() + }; + + Ok(Parameters { + vk: vk, + h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()), + l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), + + // Filter points at infinity away from A/B queries + a: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), + b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), + b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) + }) +} diff --git a/src/rust/bellman/src/groth16/mod.rs b/src/rust/bellman/src/groth16/mod.rs new file mode 100644 index 000000000..c5ee4e9be --- /dev/null +++ b/src/rust/bellman/src/groth16/mod.rs @@ -0,0 +1,576 @@ +use pairing::{ + Engine, + CurveAffine, + EncodedPoint +}; + +use ::{ + SynthesisError +}; + +use multiexp::SourceBuilder; +use std::io::{self, Read, Write}; +use std::sync::Arc; +use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + +#[cfg(test)] +mod tests; + +mod generator; +mod prover; +mod verifier; + +pub use self::generator::*; +pub use self::prover::*; +pub use self::verifier::*; + +#[derive(Clone)] +pub struct Proof { + pub a: E::G1Affine, + pub b: E::G2Affine, + pub c: E::G1Affine +} + +impl PartialEq for Proof { + fn eq(&self, other: &Self) -> bool { + self.a == other.a && + self.b == other.b && + self.c == other.c + } +} + +impl Proof { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + writer.write_all(self.a.into_compressed().as_ref())?; + writer.write_all(self.b.into_compressed().as_ref())?; + writer.write_all(self.c.into_compressed().as_ref())?; + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g1_repr = ::Compressed::empty(); + let mut g2_repr = ::Compressed::empty(); + + reader.read_exact(g1_repr.as_mut())?; + let a = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + reader.read_exact(g2_repr.as_mut())?; + let b = g2_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + reader.read_exact(g1_repr.as_mut())?; + let c = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + Ok(Proof { + a: a, + b: b, + c: c + }) + } +} + +#[derive(Clone)] +pub struct VerifyingKey { + // alpha in g1 for verifying and for creating A/C elements of + // proof. Never the point at infinity. + pub alpha_g1: E::G1Affine, + + // beta in g1 and g2 for verifying and for creating B/C elements + // of proof. Never the point at infinity. + pub beta_g1: E::G1Affine, + pub beta_g2: E::G2Affine, + + // gamma in g2 for verifying. Never the point at infinity. + pub gamma_g2: E::G2Affine, + + // delta in g1/g2 for verifying and proving, essentially the magic + // trapdoor that forces the prover to evaluate the C element of the + // proof with only components from the CRS. Never the point at + // infinity. + pub delta_g1: E::G1Affine, + pub delta_g2: E::G2Affine, + + // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / gamma + // for all public inputs. Because all public inputs have a dummy constraint, + // this is the same size as the number of inputs, and never contains points + // at infinity. + pub ic: Vec +} + +impl PartialEq for VerifyingKey { + fn eq(&self, other: &Self) -> bool { + self.alpha_g1 == other.alpha_g1 && + self.beta_g1 == other.beta_g1 && + self.beta_g2 == other.beta_g2 && + self.gamma_g2 == other.gamma_g2 && + self.delta_g1 == other.delta_g1 && + self.delta_g2 == other.delta_g2 && + self.ic == other.ic + } +} + +impl VerifyingKey { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?; + writer.write_all(self.beta_g1.into_uncompressed().as_ref())?; + writer.write_all(self.beta_g2.into_uncompressed().as_ref())?; + writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?; + writer.write_all(self.delta_g1.into_uncompressed().as_ref())?; + writer.write_all(self.delta_g2.into_uncompressed().as_ref())?; + writer.write_u32::(self.ic.len() as u32)?; + for ic in &self.ic { + writer.write_all(ic.into_uncompressed().as_ref())?; + } + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g1_repr = ::Uncompressed::empty(); + let mut g2_repr = ::Uncompressed::empty(); + + reader.read_exact(g1_repr.as_mut())?; + let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g1_repr.as_mut())?; + let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g1_repr.as_mut())?; + let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let ic_len = reader.read_u32::()? as usize; + + let mut ic = vec![]; + + for _ in 0..ic_len { + reader.read_exact(g1_repr.as_mut())?; + let g1 = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + ic.push(g1); + } + + Ok(VerifyingKey { + alpha_g1: alpha_g1, + beta_g1: beta_g1, + beta_g2: beta_g2, + gamma_g2: gamma_g2, + delta_g1: delta_g1, + delta_g2: delta_g2, + ic: ic + }) + } +} + +#[derive(Clone)] +pub struct Parameters { + pub vk: VerifyingKey, + + // Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and + // m-2 inclusive. Never contains points at infinity. + pub h: Arc>, + + // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta + // for all auxillary inputs. Variables can never be unconstrained, so this + // never contains points at infinity. + pub l: Arc>, + + // QAP "A" polynomials evaluated at tau in the Lagrange basis. Never contains + // points at infinity: polynomials that evaluate to zero are omitted from + // the CRS and the prover can deterministically skip their evaluation. + pub a: Arc>, + + // QAP "B" polynomials evaluated at tau in the Lagrange basis. Needed in + // G1 and G2 for C/B queries, respectively. Never contains points at + // infinity for the same reason as the "A" polynomials. + pub b_g1: Arc>, + pub b_g2: Arc> +} + +impl PartialEq for Parameters { + fn eq(&self, other: &Self) -> bool { + self.vk == other.vk && + self.h == other.h && + self.l == other.l && + self.a == other.a && + self.b_g1 == other.b_g1 && + self.b_g2 == other.b_g2 + } +} + +impl Parameters { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + self.vk.write(&mut writer)?; + + writer.write_u32::(self.h.len() as u32)?; + for g in &self.h[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + writer.write_u32::(self.l.len() as u32)?; + for g in &self.l[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + writer.write_u32::(self.a.len() as u32)?; + for g in &self.a[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + writer.write_u32::(self.b_g1.len() as u32)?; + for g in &self.b_g1[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + writer.write_u32::(self.b_g2.len() as u32)?; + for g in &self.b_g2[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + Ok(()) + } + + pub fn read( + mut reader: R, + checked: bool + ) -> io::Result + { + let read_g1 = |reader: &mut R| -> io::Result { + let mut repr = ::Uncompressed::empty(); + reader.read_exact(repr.as_mut())?; + + if checked { + repr + .into_affine() + } else { + repr + .into_affine_unchecked() + } + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + }) + }; + + let read_g2 = |reader: &mut R| -> io::Result { + let mut repr = ::Uncompressed::empty(); + reader.read_exact(repr.as_mut())?; + + if checked { + repr + .into_affine() + } else { + repr + .into_affine_unchecked() + } + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + }) + }; + + let vk = VerifyingKey::::read(&mut reader)?; + + let mut h = vec![]; + let mut l = vec![]; + let mut a = vec![]; + let mut b_g1 = vec![]; + let mut b_g2 = vec![]; + + { + let len = reader.read_u32::()? as usize; + for _ in 0..len { + h.push(read_g1(&mut reader)?); + } + } + + { + let len = reader.read_u32::()? as usize; + for _ in 0..len { + l.push(read_g1(&mut reader)?); + } + } + + { + let len = reader.read_u32::()? as usize; + for _ in 0..len { + a.push(read_g1(&mut reader)?); + } + } + + { + let len = reader.read_u32::()? as usize; + for _ in 0..len { + b_g1.push(read_g1(&mut reader)?); + } + } + + { + let len = reader.read_u32::()? as usize; + for _ in 0..len { + b_g2.push(read_g2(&mut reader)?); + } + } + + Ok(Parameters { + vk: vk, + h: Arc::new(h), + l: Arc::new(l), + a: Arc::new(a), + b_g1: Arc::new(b_g1), + b_g2: Arc::new(b_g2) + }) + } +} + +pub struct PreparedVerifyingKey { + /// Pairing result of alpha*beta + alpha_g1_beta_g2: E::Fqk, + /// -gamma in G2 + neg_gamma_g2: ::Prepared, + /// -delta in G2 + neg_delta_g2: ::Prepared, + /// Copy of IC from `VerifiyingKey`. + ic: Vec +} + +pub trait ParameterSource { + type G1Builder: SourceBuilder; + type G2Builder: SourceBuilder; + + fn get_vk( + &mut self, + num_ic: usize + ) -> Result, SynthesisError>; + fn get_h( + &mut self, + num_h: usize + ) -> Result; + fn get_l( + &mut self, + num_l: usize + ) -> Result; + fn get_a( + &mut self, + num_inputs: usize, + num_aux: usize + ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; + fn get_b_g1( + &mut self, + num_inputs: usize, + num_aux: usize + ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; + fn get_b_g2( + &mut self, + num_inputs: usize, + num_aux: usize + ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>; +} + +impl<'a, E: Engine> ParameterSource for &'a Parameters { + type G1Builder = (Arc>, usize); + type G2Builder = (Arc>, usize); + + fn get_vk( + &mut self, + _: usize + ) -> Result, SynthesisError> + { + Ok(self.vk.clone()) + } + + fn get_h( + &mut self, + _: usize + ) -> Result + { + Ok((self.h.clone(), 0)) + } + + fn get_l( + &mut self, + _: usize + ) -> Result + { + Ok((self.l.clone(), 0)) + } + + fn get_a( + &mut self, + num_inputs: usize, + _: usize + ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> + { + Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs))) + } + + fn get_b_g1( + &mut self, + num_inputs: usize, + _: usize + ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> + { + Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs))) + } + + fn get_b_g2( + &mut self, + num_inputs: usize, + _: usize + ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> + { + Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) + } +} + +#[cfg(test)] +mod test_with_bls12_381 { + use super::*; + use {Circuit, SynthesisError, ConstraintSystem}; + + use rand::{Rand, thread_rng}; + use pairing::{Field}; + use pairing::bls12_381::{Bls12, Fr}; + + #[test] + fn serialization() { + struct MySillyCircuit { + a: Option, + b: Option + } + + impl Circuit for MySillyCircuit { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; + let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; + let c = cs.alloc_input(|| "c", || { + let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; + let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; + + a.mul_assign(&b); + Ok(a) + })?; + + cs.enforce( + || "a*b=c", + |lc| lc + a, + |lc| lc + b, + |lc| lc + c + ); + + Ok(()) + } + } + + let rng = &mut thread_rng(); + + let params = generate_random_parameters::( + MySillyCircuit { a: None, b: None }, + rng + ).unwrap(); + + { + let mut v = vec![]; + + params.write(&mut v).unwrap(); + assert_eq!(v.len(), 2136); + + let de_params = Parameters::read(&v[..], true).unwrap(); + assert!(params == de_params); + + let de_params = Parameters::read(&v[..], false).unwrap(); + assert!(params == de_params); + } + + let pvk = prepare_verifying_key::(¶ms.vk); + + for _ in 0..100 { + let a = Fr::rand(rng); + let b = Fr::rand(rng); + let mut c = a; + c.mul_assign(&b); + + let proof = create_random_proof( + MySillyCircuit { + a: Some(a), + b: Some(b) + }, + ¶ms, + rng + ).unwrap(); + + let mut v = vec![]; + proof.write(&mut v).unwrap(); + + assert_eq!(v.len(), 192); + + let de_proof = Proof::read(&v[..]).unwrap(); + assert!(proof == de_proof); + + assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); + assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); + } + } +} diff --git a/src/rust/bellman/src/groth16/prover.rs b/src/rust/bellman/src/groth16/prover.rs new file mode 100644 index 000000000..3c9401660 --- /dev/null +++ b/src/rust/bellman/src/groth16/prover.rs @@ -0,0 +1,334 @@ +use rand::Rng; + +use std::sync::Arc; + +use futures::Future; + +use pairing::{ + Engine, + PrimeField, + Field, + CurveProjective, + CurveAffine +}; + +use super::{ + ParameterSource, + Proof +}; + +use ::{ + SynthesisError, + Circuit, + ConstraintSystem, + LinearCombination, + Variable, + Index +}; + +use ::domain::{ + EvaluationDomain, + Scalar +}; + +use ::multiexp::{ + DensityTracker, + FullDensity, + multiexp +}; + +use ::multicore::{ + Worker +}; + +fn eval( + lc: &LinearCombination, + mut input_density: Option<&mut DensityTracker>, + mut aux_density: Option<&mut DensityTracker>, + input_assignment: &[E::Fr], + aux_assignment: &[E::Fr] +) -> E::Fr +{ + let mut acc = E::Fr::zero(); + + for &(index, coeff) in lc.0.iter() { + let mut tmp; + + match index { + Variable(Index::Input(i)) => { + tmp = input_assignment[i]; + if let Some(ref mut v) = input_density { + v.inc(i); + } + }, + Variable(Index::Aux(i)) => { + tmp = aux_assignment[i]; + if let Some(ref mut v) = aux_density { + v.inc(i); + } + } + } + + if coeff == E::Fr::one() { + acc.add_assign(&tmp); + } else { + tmp.mul_assign(&coeff); + acc.add_assign(&tmp); + } + } + + acc +} + +struct ProvingAssignment { + // Density of queries + a_aux_density: DensityTracker, + b_input_density: DensityTracker, + b_aux_density: DensityTracker, + + // Evaluations of A, B, C polynomials + a: Vec>, + b: Vec>, + c: Vec>, + + // Assignments of variables + input_assignment: Vec, + aux_assignment: Vec +} + +impl ConstraintSystem for ProvingAssignment { + type Root = Self; + + fn alloc( + &mut self, + _: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.aux_assignment.push(f()?); + self.a_aux_density.add_element(); + self.b_aux_density.add_element(); + + Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) + } + + fn alloc_input( + &mut self, + _: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.input_assignment.push(f()?); + self.b_input_density.add_element(); + + Ok(Variable(Index::Input(self.input_assignment.len() - 1))) + } + + fn enforce( + &mut self, + _: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination + { + let a = a(LinearCombination::zero()); + let b = b(LinearCombination::zero()); + let c = c(LinearCombination::zero()); + + self.a.push(Scalar(eval( + &a, + // Inputs have full density in the A query + // because there are constraints of the + // form x * 0 = 0 for each input. + None, + Some(&mut self.a_aux_density), + &self.input_assignment, + &self.aux_assignment + ))); + self.b.push(Scalar(eval( + &b, + Some(&mut self.b_input_density), + Some(&mut self.b_aux_density), + &self.input_assignment, + &self.aux_assignment + ))); + self.c.push(Scalar(eval( + &c, + // There is no C polynomial query, + // though there is an (beta)A + (alpha)B + C + // query for all aux variables. + // However, that query has full density. + None, + None, + &self.input_assignment, + &self.aux_assignment + ))); + } + + fn push_namespace(&mut self, _: N) + where NR: Into, N: FnOnce() -> NR + { + // Do nothing; we don't care about namespaces in this context. + } + + fn pop_namespace(&mut self) + { + // Do nothing; we don't care about namespaces in this context. + } + + fn get_root(&mut self) -> &mut Self::Root { + self + } +} + +pub fn create_random_proof>( + circuit: C, + params: P, + rng: &mut R +) -> Result, SynthesisError> + where E: Engine, C: Circuit, R: Rng +{ + let r = rng.gen(); + let s = rng.gen(); + + create_proof::(circuit, params, r, s) +} + +pub fn create_proof>( + circuit: C, + mut params: P, + r: E::Fr, + s: E::Fr +) -> Result, SynthesisError> + where E: Engine, C: Circuit +{ + let mut prover = ProvingAssignment { + a_aux_density: DensityTracker::new(), + b_input_density: DensityTracker::new(), + b_aux_density: DensityTracker::new(), + a: vec![], + b: vec![], + c: vec![], + input_assignment: vec![], + aux_assignment: vec![] + }; + + prover.alloc_input(|| "", || Ok(E::Fr::one()))?; + + circuit.synthesize(&mut prover)?; + + for i in 0..prover.input_assignment.len() { + prover.enforce(|| "", + |lc| lc + Variable(Index::Input(i)), + |lc| lc, + |lc| lc, + ); + } + + let worker = Worker::new(); + + let vk = params.get_vk(prover.input_assignment.len())?; + + let h = { + let mut a = EvaluationDomain::from_coeffs(prover.a)?; + let mut b = EvaluationDomain::from_coeffs(prover.b)?; + let mut c = EvaluationDomain::from_coeffs(prover.c)?; + a.ifft(&worker); + a.coset_fft(&worker); + b.ifft(&worker); + b.coset_fft(&worker); + c.ifft(&worker); + c.coset_fft(&worker); + + a.mul_assign(&worker, &b); + drop(b); + a.sub_assign(&worker, &c); + drop(c); + a.divide_by_z_on_coset(&worker); + a.icoset_fft(&worker); + let mut a = a.into_coeffs(); + let a_len = a.len() - 1; + a.truncate(a_len); + // TODO: parallelize if it's even helpful + let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::>()); + + multiexp(&worker, params.get_h(a.len())?, FullDensity, a) + }; + + // TODO: parallelize if it's even helpful + let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::>()); + let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::>()); + + let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone()); + + let a_aux_density_total = prover.a_aux_density.get_total_density(); + + let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?; + + let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone()); + let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone()); + + let b_input_density = Arc::new(prover.b_input_density); + let b_input_density_total = b_input_density.get_total_density(); + let b_aux_density = Arc::new(prover.b_aux_density); + let b_aux_density_total = b_aux_density.get_total_density(); + + let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?; + + let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone()); + let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone()); + + let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?; + + let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment); + let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); + + if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() { + // If this element is zero, someone is trying to perform a + // subversion-CRS attack. + return Err(SynthesisError::UnexpectedIdentity); + } + + let mut g_a = vk.delta_g1.mul(r); + g_a.add_assign_mixed(&vk.alpha_g1); + let mut g_b = vk.delta_g2.mul(s); + g_b.add_assign_mixed(&vk.beta_g2); + let mut g_c; + { + let mut rs = r; + rs.mul_assign(&s); + + g_c = vk.delta_g1.mul(rs); + g_c.add_assign(&vk.alpha_g1.mul(s)); + g_c.add_assign(&vk.beta_g1.mul(r)); + } + let mut a_answer = a_inputs.wait()?; + a_answer.add_assign(&a_aux.wait()?); + g_a.add_assign(&a_answer); + a_answer.mul_assign(s); + g_c.add_assign(&a_answer); + + let mut b1_answer = b_g1_inputs.wait()?; + b1_answer.add_assign(&b_g1_aux.wait()?); + let mut b2_answer = b_g2_inputs.wait()?; + b2_answer.add_assign(&b_g2_aux.wait()?); + + g_b.add_assign(&b2_answer); + b1_answer.mul_assign(r); + g_c.add_assign(&b1_answer); + g_c.add_assign(&h.wait()?); + g_c.add_assign(&l.wait()?); + + Ok(Proof { + a: g_a.into_affine(), + b: g_b.into_affine(), + c: g_c.into_affine() + }) +} diff --git a/src/rust/bellman/src/groth16/tests/dummy_engine.rs b/src/rust/bellman/src/groth16/tests/dummy_engine.rs new file mode 100644 index 000000000..b2de2ee4b --- /dev/null +++ b/src/rust/bellman/src/groth16/tests/dummy_engine.rs @@ -0,0 +1,451 @@ +use pairing::{ + Engine, + PrimeField, + PrimeFieldRepr, + Field, + SqrtField, + LegendreSymbol, + CurveProjective, + CurveAffine, + PrimeFieldDecodingError, + GroupDecodingError, + EncodedPoint +}; + +use std::cmp::Ordering; +use std::fmt; +use rand::{Rand, Rng}; +use std::num::Wrapping; + +const MODULUS_R: Wrapping = Wrapping(64513); + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Fr(Wrapping); + +impl fmt::Display for Fr { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", (self.0).0) + } +} + +impl Rand for Fr { + fn rand(rng: &mut R) -> Self { + Fr(Wrapping(rng.gen()) % MODULUS_R) + } +} + +impl Field for Fr { + fn zero() -> Self { + Fr(Wrapping(0)) + } + + fn one() -> Self { + Fr(Wrapping(1)) + } + + fn is_zero(&self) -> bool { + (self.0).0 == 0 + } + + fn square(&mut self) { + self.0 = (self.0 * self.0) % MODULUS_R; + } + + fn double(&mut self) { + self.0 = (self.0 << 1) % MODULUS_R; + } + + fn negate(&mut self) { + if !::is_zero(self) { + self.0 = MODULUS_R - self.0; + } + } + + fn add_assign(&mut self, other: &Self) { + self.0 = (self.0 + other.0) % MODULUS_R; + } + + fn sub_assign(&mut self, other: &Self) { + self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R; + } + + fn mul_assign(&mut self, other: &Self) { + self.0 = (self.0 * other.0) % MODULUS_R; + } + + fn inverse(&self) -> Option { + if ::is_zero(self) { + None + } else { + Some(self.pow(&[(MODULUS_R.0 as u64) - 2])) + } + } + + fn frobenius_map(&mut self, _: usize) { + // identity + } +} + +impl SqrtField for Fr { + fn legendre(&self) -> LegendreSymbol { + // s = self^((r - 1) // 2) + let s = self.pow([32256]); + if s == ::zero() { LegendreSymbol::Zero } + else if s == ::one() { LegendreSymbol::QuadraticResidue } + else { LegendreSymbol::QuadraticNonResidue } + } + + fn sqrt(&self) -> Option { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + match self.legendre() { + LegendreSymbol::Zero => Some(*self), + LegendreSymbol::QuadraticNonResidue => None, + LegendreSymbol::QuadraticResidue => { + let mut c = Fr::root_of_unity(); + // r = self^((t + 1) // 2) + let mut r = self.pow([32]); + // t = self^t + let mut t = self.pow([63]); + let mut m = Fr::S; + + while t != ::one() { + let mut i = 1; + { + let mut t2i = t; + t2i.square(); + loop { + if t2i == ::one() { + break; + } + t2i.square(); + i += 1; + } + } + + for _ in 0..(m - i - 1) { + c.square(); + } + ::mul_assign(&mut r, &c); + c.square(); + ::mul_assign(&mut t, &c); + m = i; + } + + Some(r) + } + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct FrRepr([u64; 1]); + +impl Ord for FrRepr { + fn cmp(&self, other: &FrRepr) -> Ordering { + (self.0)[0].cmp(&(other.0)[0]) + } +} + +impl PartialOrd for FrRepr { + fn partial_cmp(&self, other: &FrRepr) -> Option { + Some(self.cmp(other)) + } +} + +impl Rand for FrRepr { + fn rand(rng: &mut R) -> Self { + FrRepr([rng.gen()]) + } +} + +impl fmt::Display for FrRepr { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", (self.0)[0]) + } +} + +impl From for FrRepr { + fn from(v: u64) -> FrRepr { + FrRepr([v]) + } +} + +impl From for FrRepr { + fn from(v: Fr) -> FrRepr { + FrRepr([(v.0).0 as u64]) + } +} + +impl AsMut<[u64]> for FrRepr { + fn as_mut(&mut self) -> &mut [u64] { + &mut self.0[..] + } +} + +impl AsRef<[u64]> for FrRepr { + fn as_ref(&self) -> &[u64] { + &self.0[..] + } +} + +impl Default for FrRepr { + fn default() -> FrRepr { + FrRepr::from(0u64) + } +} + +impl PrimeFieldRepr for FrRepr { + fn sub_noborrow(&mut self, other: &Self) { + self.0[0] = self.0[0].wrapping_sub(other.0[0]); + } + fn add_nocarry(&mut self, other: &Self) { + self.0[0] = self.0[0].wrapping_add(other.0[0]); + } + fn num_bits(&self) -> u32 { + 64 - self.0[0].leading_zeros() + } + fn is_zero(&self) -> bool { + self.0[0] == 0 + } + fn is_odd(&self) -> bool { + !self.is_even() + } + fn is_even(&self) -> bool { + self.0[0] % 2 == 0 + } + fn div2(&mut self) { + self.shr(1) + } + fn shr(&mut self, amt: u32) { + self.0[0] >>= amt; + } + fn mul2(&mut self) { + self.shl(1) + } + fn shl(&mut self, amt: u32) { + self.0[0] <<= amt; + } +} + +impl PrimeField for Fr { + type Repr = FrRepr; + + const NUM_BITS: u32 = 16; + const CAPACITY: u32 = 15; + const S: u32 = 10; + + fn from_repr(repr: FrRepr) -> Result { + if repr.0[0] >= (MODULUS_R.0 as u64) { + Err(PrimeFieldDecodingError::NotInField(format!("{}", repr))) + } else { + Ok(Fr(Wrapping(repr.0[0] as u32))) + } + } + + fn into_repr(&self) -> FrRepr { + FrRepr::from(*self) + } + + fn char() -> FrRepr { + Fr(MODULUS_R).into() + } + + fn multiplicative_generator() -> Fr { + Fr(Wrapping(5)) + } + + fn root_of_unity() -> Fr { + Fr(Wrapping(57751)) + } +} + +#[derive(Clone)] +pub struct DummyEngine; + +impl Engine for DummyEngine { + type Fr = Fr; + type G1 = Fr; + type G1Affine = Fr; + type G2 = Fr; + type G2Affine = Fr; + type Fq = Fr; + type Fqe = Fr; + + // TODO: This should be F_645131 or something. Doesn't matter for now. + type Fqk = Fr; + + fn miller_loop<'a, I>(i: I) -> Self::Fqk + where I: IntoIterator::Prepared, + &'a ::Prepared + )> + { + let mut acc = ::zero(); + + for &(a, b) in i { + let mut tmp = *a; + ::mul_assign(&mut tmp, b); + ::add_assign(&mut acc, &tmp); + } + + acc + } + + /// Perform final exponentiation of the result of a miller loop. + fn final_exponentiation(this: &Self::Fqk) -> Option + { + Some(*this) + } +} + +impl CurveProjective for Fr { + type Affine = Fr; + type Base = Fr; + type Scalar = Fr; + type Engine = DummyEngine; + + fn zero() -> Self { + ::zero() + } + + fn one() -> Self { + ::one() + } + + fn is_zero(&self) -> bool { + ::is_zero(self) + } + + fn batch_normalization(_: &mut [Self]) { + + } + + fn is_normalized(&self) -> bool { + true + } + + fn double(&mut self) { + ::double(self); + } + + fn add_assign(&mut self, other: &Self) { + ::add_assign(self, other); + } + + fn add_assign_mixed(&mut self, other: &Self) { + ::add_assign(self, other); + } + + fn negate(&mut self) { + ::negate(self); + } + + fn mul_assign::Repr>>(&mut self, other: S) + { + let tmp = Fr::from_repr(other.into()).unwrap(); + + ::mul_assign(self, &tmp); + } + + fn into_affine(&self) -> Fr { + *self + } + + fn recommended_wnaf_for_scalar(_: ::Repr) -> usize { + 3 + } + + fn recommended_wnaf_for_num_scalars(_: usize) -> usize { + 3 + } +} + +#[derive(Copy, Clone)] +pub struct FakePoint; + +impl AsMut<[u8]> for FakePoint { + fn as_mut(&mut self) -> &mut [u8] { + unimplemented!() + } +} + +impl AsRef<[u8]> for FakePoint { + fn as_ref(&self) -> &[u8] { + unimplemented!() + } +} + +impl EncodedPoint for FakePoint { + type Affine = Fr; + + fn empty() -> Self { + unimplemented!() + } + + fn size() -> usize { + unimplemented!() + } + + fn into_affine(&self) -> Result { + unimplemented!() + } + + fn into_affine_unchecked(&self) -> Result { + unimplemented!() + } + + fn from_affine(_: Self::Affine) -> Self { + unimplemented!() + } +} + +impl CurveAffine for Fr { + type Pair = Fr; + type PairingResult = Fr; + type Compressed = FakePoint; + type Uncompressed = FakePoint; + type Prepared = Fr; + type Projective = Fr; + type Base = Fr; + type Scalar = Fr; + type Engine = DummyEngine; + + fn zero() -> Self { + ::zero() + } + + fn one() -> Self { + ::one() + } + + fn is_zero(&self) -> bool { + ::is_zero(self) + } + + fn negate(&mut self) { + ::negate(self); + } + + fn mul::Repr>>(&self, other: S) -> Self::Projective + { + let mut res = *self; + let tmp = Fr::from_repr(other.into()).unwrap(); + + ::mul_assign(&mut res, &tmp); + + res + } + + fn prepare(&self) -> Self::Prepared { + *self + } + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + self.mul(*other) + } + + fn into_projective(&self) -> Self::Projective { + *self + } +} diff --git a/src/rust/bellman/src/groth16/tests/mod.rs b/src/rust/bellman/src/groth16/tests/mod.rs new file mode 100644 index 000000000..a8e291477 --- /dev/null +++ b/src/rust/bellman/src/groth16/tests/mod.rs @@ -0,0 +1,400 @@ +use pairing::{ + Engine, + Field, + PrimeField +}; + +mod dummy_engine; +use self::dummy_engine::*; + +use std::marker::PhantomData; + +use ::{ + Circuit, + ConstraintSystem, + SynthesisError +}; + +use super::{ + generate_parameters, + prepare_verifying_key, + create_proof, + verify_proof +}; + +struct XORDemo { + a: Option, + b: Option, + _marker: PhantomData +} + +impl Circuit for XORDemo { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + let a_var = cs.alloc(|| "a", || { + if self.a.is_some() { + if self.a.unwrap() { + Ok(E::Fr::one()) + } else { + Ok(E::Fr::zero()) + } + } else { + Err(SynthesisError::AssignmentMissing) + } + })?; + + cs.enforce( + || "a_boolean_constraint", + |lc| lc + CS::one() - a_var, + |lc| lc + a_var, + |lc| lc + ); + + let b_var = cs.alloc(|| "b", || { + if self.b.is_some() { + if self.b.unwrap() { + Ok(E::Fr::one()) + } else { + Ok(E::Fr::zero()) + } + } else { + Err(SynthesisError::AssignmentMissing) + } + })?; + + cs.enforce( + || "b_boolean_constraint", + |lc| lc + CS::one() - b_var, + |lc| lc + b_var, + |lc| lc + ); + + let c_var = cs.alloc_input(|| "c", || { + if self.a.is_some() && self.b.is_some() { + if self.a.unwrap() ^ self.b.unwrap() { + Ok(E::Fr::one()) + } else { + Ok(E::Fr::zero()) + } + } else { + Err(SynthesisError::AssignmentMissing) + } + })?; + + cs.enforce( + || "c_xor_constraint", + |lc| lc + a_var + a_var, + |lc| lc + b_var, + |lc| lc + a_var + b_var - c_var + ); + + Ok(()) + } +} + +#[test] +fn test_xordemo() { + let g1 = Fr::one(); + let g2 = Fr::one(); + let alpha = Fr::from_str("48577").unwrap(); + let beta = Fr::from_str("22580").unwrap(); + let gamma = Fr::from_str("53332").unwrap(); + let delta = Fr::from_str("5481").unwrap(); + let tau = Fr::from_str("3673").unwrap(); + + let params = { + let c = XORDemo:: { + a: None, + b: None, + _marker: PhantomData + }; + + generate_parameters( + c, + g1, + g2, + alpha, + beta, + gamma, + delta, + tau + ).unwrap() + }; + + // This will synthesize the constraint system: + // + // public inputs: a_0 = 1, a_1 = c + // aux inputs: a_2 = a, a_3 = b + // constraints: + // (a_0 - a_2) * (a_2) = 0 + // (a_0 - a_3) * (a_3) = 0 + // (a_2 + a_2) * (a_3) = (a_2 + a_3 - a_1) + // (a_0) * 0 = 0 + // (a_1) * 0 = 0 + + // The evaluation domain is 8. The H query should + // have 7 elements (it's a quotient polynomial) + assert_eq!(7, params.h.len()); + + let mut root_of_unity = Fr::root_of_unity(); + + // We expect this to be a 2^10 root of unity + assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 10])); + + // Let's turn it into a 2^3 root of unity. + root_of_unity = root_of_unity.pow(&[1 << 7]); + assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 3])); + assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity); + + // Let's compute all the points in our evaluation domain. + let mut points = Vec::with_capacity(8); + for i in 0..8 { + points.push(root_of_unity.pow(&[i])); + } + + // Let's compute t(tau) = (tau - p_0)(tau - p_1)... + // = tau^8 - 1 + let mut t_at_tau = tau.pow(&[8]); + t_at_tau.sub_assign(&Fr::one()); + { + let mut tmp = Fr::one(); + for p in &points { + let mut term = tau; + term.sub_assign(p); + tmp.mul_assign(&term); + } + assert_eq!(tmp, t_at_tau); + } + + // We expect our H query to be 7 elements of the form... + // {tau^i t(tau) / delta} + let delta_inverse = delta.inverse().unwrap(); + let gamma_inverse = gamma.inverse().unwrap(); + { + let mut coeff = delta_inverse; + coeff.mul_assign(&t_at_tau); + + let mut cur = Fr::one(); + for h in params.h.iter() { + let mut tmp = cur; + tmp.mul_assign(&coeff); + + assert_eq!(*h, tmp); + + cur.mul_assign(&tau); + } + } + + // The density of the IC query is 2 (2 inputs) + assert_eq!(2, params.vk.ic.len()); + + // The density of the L query is 2 (2 aux variables) + assert_eq!(2, params.l.len()); + + // The density of the A query is 4 (each variable is in at least one A term) + assert_eq!(4, params.a.len()); + + // The density of the B query is 2 (two variables are in at least one B term) + assert_eq!(2, params.b_g1.len()); + assert_eq!(2, params.b_g2.len()); + + /* + Lagrange interpolation polynomials in our evaluation domain: + + ,-------------------------------. ,-------------------------------. ,-------------------------------. + | A TERM | | B TERM | | C TERM | + `-------------------------------. `-------------------------------' `-------------------------------' + | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | + | 1 | 0 | 64512 | 0 | | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 | + | 1 | 0 | 0 | 64512 | | 0 | 0 | 0 | 1 | | 0 | 0 | 0 | 0 | + | 0 | 0 | 2 | 0 | | 0 | 0 | 0 | 1 | | 0 | 64512 | 1 | 1 | + | 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | + | 0 | 1 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | + `-------'-------'-------'-------' `-------'-------'-------'-------' `-------'-------'-------'-------' + + Example for u_0: + + sage: r = 64513 + sage: Fr = GF(r) + sage: omega = (Fr(5)^63)^(2^7) + sage: tau = Fr(3673) + sage: R. = PolynomialRing(Fr, 'x') + sage: def eval(tau, c0, c1, c2, c3, c4): + ....: p = R.lagrange_polynomial([(omega^0, c0), (omega^1, c1), (omega^2, c2), (omega^3, c3), (omega^4, c4), (omega^5, 0), (omega^6, 0), (omega^7, 0)]) + ....: return p.substitute(tau) + sage: eval(tau, 1, 1, 0, 1, 0) + 59158 + */ + + let u_i = [59158, 48317, 21767, 10402].iter().map(|e| { + Fr::from_str(&format!("{}", e)).unwrap() + }).collect::>(); + let v_i = [0, 0, 60619, 30791].iter().map(|e| { + Fr::from_str(&format!("{}", e)).unwrap() + }).collect::>(); + let w_i = [0, 23320, 41193, 41193].iter().map(|e| { + Fr::from_str(&format!("{}", e)).unwrap() + }).collect::>(); + + for (u, a) in u_i.iter() + .zip(¶ms.a[..]) + { + assert_eq!(u, a); + } + + for (v, b) in v_i.iter() + .filter(|&&e| e != Fr::zero()) + .zip(¶ms.b_g1[..]) + { + assert_eq!(v, b); + } + + for (v, b) in v_i.iter() + .filter(|&&e| e != Fr::zero()) + .zip(¶ms.b_g2[..]) + { + assert_eq!(v, b); + } + + for i in 0..4 { + let mut tmp1 = beta; + tmp1.mul_assign(&u_i[i]); + + let mut tmp2 = alpha; + tmp2.mul_assign(&v_i[i]); + + tmp1.add_assign(&tmp2); + tmp1.add_assign(&w_i[i]); + + if i < 2 { + // Check the correctness of the IC query elements + tmp1.mul_assign(&gamma_inverse); + + assert_eq!(tmp1, params.vk.ic[i]); + } else { + // Check the correctness of the L query elements + tmp1.mul_assign(&delta_inverse); + + assert_eq!(tmp1, params.l[i - 2]); + } + } + + // Check consistency of the other elements + assert_eq!(alpha, params.vk.alpha_g1); + assert_eq!(beta, params.vk.beta_g1); + assert_eq!(beta, params.vk.beta_g2); + assert_eq!(gamma, params.vk.gamma_g2); + assert_eq!(delta, params.vk.delta_g1); + assert_eq!(delta, params.vk.delta_g2); + + let pvk = prepare_verifying_key(¶ms.vk); + + let r = Fr::from_str("27134").unwrap(); + let s = Fr::from_str("17146").unwrap(); + + let proof = { + let c = XORDemo { + a: Some(true), + b: Some(false), + _marker: PhantomData + }; + + create_proof( + c, + ¶ms, + r, + s + ).unwrap() + }; + + // A(x) = + // a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) + + // a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) + + // a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) + + // a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) + + { + // proof A = alpha + A(tau) + delta * r + let mut expected_a = delta; + expected_a.mul_assign(&r); + expected_a.add_assign(&alpha); + expected_a.add_assign(&u_i[0]); // a_0 = 1 + expected_a.add_assign(&u_i[1]); // a_1 = 1 + expected_a.add_assign(&u_i[2]); // a_2 = 1 + // a_3 = 0 + assert_eq!(proof.a, expected_a); + } + + // B(x) = + // a_0 * (0) + + // a_1 * (0) + + // a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) + + // a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385) + { + // proof B = beta + B(tau) + delta * s + let mut expected_b = delta; + expected_b.mul_assign(&s); + expected_b.add_assign(&beta); + expected_b.add_assign(&v_i[0]); // a_0 = 1 + expected_b.add_assign(&v_i[1]); // a_1 = 1 + expected_b.add_assign(&v_i[2]); // a_2 = 1 + // a_3 = 0 + assert_eq!(proof.b, expected_b); + } + + // C(x) = + // a_0 * (0) + + // a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) + + // a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) + + // a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) + // + // If A * B = C at each point in the domain, then the following polynomial... + // P(x) = A(x) * B(x) - C(x) + // = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481 + // + // ... should be divisible by t(x), producing the quotient polynomial: + // h(x) = P(x) / t(x) + // = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032 + { + let mut expected_c = Fr::zero(); + + // A * s + let mut tmp = proof.a; + tmp.mul_assign(&s); + expected_c.add_assign(&tmp); + + // B * r + let mut tmp = proof.b; + tmp.mul_assign(&r); + expected_c.add_assign(&tmp); + + // delta * r * s + let mut tmp = delta; + tmp.mul_assign(&r); + tmp.mul_assign(&s); + expected_c.sub_assign(&tmp); + + // L query answer + // a_2 = 1, a_3 = 0 + expected_c.add_assign(¶ms.l[0]); + + // H query answer + for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() { + let coeff = Fr::from_str(&format!("{}", coeff)).unwrap(); + + let mut tmp = params.h[i]; + tmp.mul_assign(&coeff); + expected_c.add_assign(&tmp); + } + + assert_eq!(expected_c, proof.c); + } + + assert!(verify_proof( + &pvk, + &proof, + &[Fr::one()] + ).unwrap()); +} diff --git a/src/rust/bellman/src/groth16/verifier.rs b/src/rust/bellman/src/groth16/verifier.rs new file mode 100644 index 000000000..083e1d025 --- /dev/null +++ b/src/rust/bellman/src/groth16/verifier.rs @@ -0,0 +1,66 @@ +use pairing::{ + Engine, + CurveProjective, + CurveAffine, + PrimeField +}; + +use super::{ + Proof, + VerifyingKey, + PreparedVerifyingKey +}; + +use ::{ + SynthesisError +}; + +pub fn prepare_verifying_key( + vk: &VerifyingKey +) -> PreparedVerifyingKey +{ + let mut gamma = vk.gamma_g2; + gamma.negate(); + let mut delta = vk.delta_g2; + delta.negate(); + + PreparedVerifyingKey { + alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2), + neg_gamma_g2: gamma.prepare(), + neg_delta_g2: delta.prepare(), + ic: vk.ic.clone() + } +} + +pub fn verify_proof<'a, E: Engine>( + pvk: &'a PreparedVerifyingKey, + proof: &Proof, + public_inputs: &[E::Fr] +) -> Result +{ + if (public_inputs.len() + 1) != pvk.ic.len() { + return Err(SynthesisError::MalformedVerifyingKey); + } + + let mut acc = pvk.ic[0].into_projective(); + + for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { + acc.add_assign(&b.mul(i.into_repr())); + } + + // The original verification equation is: + // A * B = alpha * beta + inputs * gamma + C * delta + // ... however, we rearrange it so that it is: + // A * B - inputs * gamma - C * delta = alpha * beta + // or equivalently: + // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta + // which allows us to do a single final exponentiation. + + Ok(E::final_exponentiation( + &E::miller_loop([ + (&proof.a.prepare(), &proof.b.prepare()), + (&acc.into_affine().prepare(), &pvk.neg_gamma_g2), + (&proof.c.prepare(), &pvk.neg_delta_g2) + ].into_iter()) + ).unwrap() == pvk.alpha_g1_beta_g2) +} diff --git a/src/rust/bellman/src/lib.rs b/src/rust/bellman/src/lib.rs new file mode 100644 index 000000000..fb8d0431b --- /dev/null +++ b/src/rust/bellman/src/lib.rs @@ -0,0 +1,424 @@ +extern crate pairing; +extern crate rand; +extern crate num_cpus; +extern crate futures; +extern crate futures_cpupool; +extern crate bit_vec; +extern crate crossbeam; +extern crate byteorder; + +pub mod multicore; +mod multiexp; +pub mod domain; +pub mod groth16; + +use pairing::{Engine, Field}; + +use std::ops::{Add, Sub}; +use std::fmt; +use std::error::Error; +use std::io; +use std::marker::PhantomData; + +/// Computations are expressed in terms of arithmetic circuits, in particular +/// rank-1 quadratic constraint systems. The `Circuit` trait represents a +/// circuit that can be synthesized. The `synthesize` method is called during +/// CRS generation and during proving. +pub trait Circuit { + /// Synthesize the circuit into a rank-1 quadratic constraint system + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError>; +} + +/// Represents a variable in our constraint system. +#[derive(Copy, Clone, Debug)] +pub struct Variable(Index); + +impl Variable { + /// This constructs a variable with an arbitrary index. + /// Circuit implementations are not recommended to use this. + pub fn new_unchecked(idx: Index) -> Variable { + Variable(idx) + } + + /// This returns the index underlying the variable. + /// Circuit implementations are not recommended to use this. + pub fn get_unchecked(&self) -> Index { + self.0 + } +} + +/// Represents the index of either an input variable or +/// auxillary variable. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Index { + Input(usize), + Aux(usize) +} + +/// This represents a linear combination of some variables, with coefficients +/// in the scalar field of a pairing-friendly elliptic curve group. +#[derive(Clone)] +pub struct LinearCombination(Vec<(Variable, E::Fr)>); + +impl AsRef<[(Variable, E::Fr)]> for LinearCombination { + fn as_ref(&self) -> &[(Variable, E::Fr)] { + &self.0 + } +} + +impl LinearCombination { + pub fn zero() -> LinearCombination { + LinearCombination(vec![]) + } +} + +impl Add<(E::Fr, Variable)> for LinearCombination { + type Output = LinearCombination; + + fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { + self.0.push((var, coeff)); + + self + } +} + +impl Sub<(E::Fr, Variable)> for LinearCombination { + type Output = LinearCombination; + + fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination { + coeff.negate(); + + self + (coeff, var) + } +} + +impl Add for LinearCombination { + type Output = LinearCombination; + + fn add(self, other: Variable) -> LinearCombination { + self + (E::Fr::one(), other) + } +} + +impl Sub for LinearCombination { + type Output = LinearCombination; + + fn sub(self, other: Variable) -> LinearCombination { + self - (E::Fr::one(), other) + } +} + +impl<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; + + fn add(mut self, other: &'a LinearCombination) -> LinearCombination { + for s in &other.0 { + self = self + (s.1, s.0); + } + + self + } +} + +impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; + + fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { + for s in &other.0 { + self = self - (s.1, s.0); + } + + self + } +} + +impl<'a, E: Engine> Add<(E::Fr, &'a LinearCombination)> for LinearCombination { + type Output = LinearCombination; + + fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { + for s in &other.0 { + let mut tmp = s.1; + tmp.mul_assign(&coeff); + self = self + (tmp, s.0); + } + + self + } +} + +impl<'a, E: Engine> Sub<(E::Fr, &'a LinearCombination)> for LinearCombination { + type Output = LinearCombination; + + fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { + for s in &other.0 { + let mut tmp = s.1; + tmp.mul_assign(&coeff); + self = self - (tmp, s.0); + } + + self + } +} + +/// This is an error that could occur during circuit synthesis contexts, +/// such as CRS generation, proving or verification. +#[derive(Debug)] +pub enum SynthesisError { + /// During synthesis, we lacked knowledge of a variable assignment. + AssignmentMissing, + /// During synthesis, we divided by zero. + DivisionByZero, + /// During synthesis, we constructed an unsatisfiable constraint system. + Unsatisfiable, + /// During synthesis, our polynomials ended up being too high of degree + PolynomialDegreeTooLarge, + /// During proof generation, we encountered an identity in the CRS + UnexpectedIdentity, + /// During proof generation, we encountered an I/O error with the CRS + IoError(io::Error), + /// During verification, our verifying key was malformed. + MalformedVerifyingKey, + /// During CRS generation, we observed an unconstrained auxillary variable + UnconstrainedVariable +} + +impl From for SynthesisError { + fn from(e: io::Error) -> SynthesisError { + SynthesisError::IoError(e) + } +} + +impl Error for SynthesisError { + fn description(&self) -> &str { + match *self { + SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed", + SynthesisError::DivisionByZero => "division by zero", + SynthesisError::Unsatisfiable => "unsatisfiable constraint system", + SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", + SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", + SynthesisError::IoError(_) => "encountered an I/O error", + SynthesisError::MalformedVerifyingKey => "malformed verifying key", + SynthesisError::UnconstrainedVariable => "auxillary variable was unconstrained" + } + } +} + +impl fmt::Display for SynthesisError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + if let &SynthesisError::IoError(ref e) = self { + write!(f, "I/O error: ")?; + e.fmt(f) + } else { + write!(f, "{}", self.description()) + } + } +} + +/// Represents a constraint system which can have new variables +/// allocated and constrains between them formed. +pub trait ConstraintSystem: Sized { + /// Represents the type of the "root" of this constraint system + /// so that nested namespaces can minimize indirection. + type Root: ConstraintSystem; + + /// Return the "one" input variable + fn one() -> Variable { + Variable::new_unchecked(Index::Input(0)) + } + + /// Allocate a private variable in the constraint system. The provided function is used to + /// determine the assignment of the variable. The given `annotation` function is invoked + /// in testing contexts in order to derive a unique name for this variable in the current + /// namespace. + fn alloc( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; + + /// Allocate a public variable in the constraint system. The provided function is used to + /// determine the assignment of the variable. + fn alloc_input( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; + + /// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts + /// in order to derive a unique name for the constraint in the current namespace. + fn enforce( + &mut self, + annotation: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination; + + /// Create a new (sub)namespace and enter into it. Not intended + /// for downstream use; use `namespace` instead. + fn push_namespace(&mut self, name_fn: N) + where NR: Into, N: FnOnce() -> NR; + + /// Exit out of the existing namespace. Not intended for + /// downstream use; use `namespace` instead. + fn pop_namespace(&mut self); + + /// Gets the "root" constraint system, bypassing the namespacing. + /// Not intended for downstream use; use `namespace` instead. + fn get_root(&mut self) -> &mut Self::Root; + + /// Begin a namespace for this constraint system. + fn namespace<'a, NR, N>( + &'a mut self, + name_fn: N + ) -> Namespace<'a, E, Self::Root> + where NR: Into, N: FnOnce() -> NR + { + self.get_root().push_namespace(name_fn); + + Namespace(self.get_root(), PhantomData) + } +} + +/// This is a "namespaced" constraint system which borrows a constraint system (pushing +/// a namespace context) and, when dropped, pops out of the namespace context. +pub struct Namespace<'a, E: Engine, CS: ConstraintSystem + 'a>(&'a mut CS, PhantomData); + +impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { + type Root = CS::Root; + + fn one() -> Variable { + CS::one() + } + + fn alloc( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.0.alloc(annotation, f) + } + + fn alloc_input( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.0.alloc_input(annotation, f) + } + + fn enforce( + &mut self, + annotation: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination + { + self.0.enforce(annotation, a, b, c) + } + + // Downstream users who use `namespace` will never interact with these + // functions and they will never be invoked because the namespace is + // never a root constraint system. + + fn push_namespace(&mut self, _: N) + where NR: Into, N: FnOnce() -> NR + { + panic!("only the root's push_namespace should be called"); + } + + fn pop_namespace(&mut self) + { + panic!("only the root's pop_namespace should be called"); + } + + fn get_root(&mut self) -> &mut Self::Root + { + self.0.get_root() + } +} + +impl<'a, E: Engine, CS: ConstraintSystem> Drop for Namespace<'a, E, CS> { + fn drop(&mut self) { + self.get_root().pop_namespace() + } +} + +/// Convenience implementation of ConstraintSystem for mutable references to +/// constraint systems. +impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut CS { + type Root = CS::Root; + + fn one() -> Variable { + CS::one() + } + + fn alloc( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + (**self).alloc(annotation, f) + } + + fn alloc_input( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + (**self).alloc_input(annotation, f) + } + + fn enforce( + &mut self, + annotation: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination + { + (**self).enforce(annotation, a, b, c) + } + + fn push_namespace(&mut self, name_fn: N) + where NR: Into, N: FnOnce() -> NR + { + (**self).push_namespace(name_fn) + } + + fn pop_namespace(&mut self) + { + (**self).pop_namespace() + } + + fn get_root(&mut self) -> &mut Self::Root + { + (**self).get_root() + } +} diff --git a/src/rust/bellman/src/multicore.rs b/src/rust/bellman/src/multicore.rs new file mode 100644 index 000000000..c0062fc0f --- /dev/null +++ b/src/rust/bellman/src/multicore.rs @@ -0,0 +1,106 @@ +//! This is an interface for dealing with the kinds of +//! parallel computations involved in bellman. It's +//! currently just a thin wrapper around CpuPool and +//! crossbeam but may be extended in the future to +//! allow for various parallelism strategies. + +use num_cpus; +use futures::{Future, IntoFuture, Poll}; +use futures_cpupool::{CpuPool, CpuFuture}; +use crossbeam::{self, Scope}; + +#[derive(Clone)] +pub struct Worker { + cpus: usize, + pool: CpuPool +} + +impl Worker { + // We don't expose this outside the library so that + // all `Worker` instances have the same number of + // CPUs configured. + pub(crate) fn new_with_cpus(cpus: usize) -> Worker { + Worker { + cpus: cpus, + pool: CpuPool::new(cpus) + } + } + + pub fn new() -> Worker { + Self::new_with_cpus(num_cpus::get()) + } + + pub fn log_num_cpus(&self) -> u32 { + log2_floor(self.cpus) + } + + pub fn compute( + &self, f: F + ) -> WorkerFuture + where F: FnOnce() -> R + Send + 'static, + R: IntoFuture + 'static, + R::Future: Send + 'static, + R::Item: Send + 'static, + R::Error: Send + 'static + { + WorkerFuture { + future: self.pool.spawn_fn(f) + } + } + + pub fn scope<'a, F, R>( + &self, + elements: usize, + f: F + ) -> R + where F: FnOnce(&Scope<'a>, usize) -> R + { + let chunk_size = if elements < self.cpus { + 1 + } else { + elements / self.cpus + }; + + crossbeam::scope(|scope| { + f(scope, chunk_size) + }) + } +} + +pub struct WorkerFuture { + future: CpuFuture +} + +impl Future for WorkerFuture { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Poll + { + self.future.poll() + } +} + +fn log2_floor(num: usize) -> u32 { + assert!(num > 0); + + let mut pow = 0; + + while (1 << (pow+1)) <= num { + pow += 1; + } + + pow +} + +#[test] +fn test_log2_floor() { + assert_eq!(log2_floor(1), 0); + assert_eq!(log2_floor(2), 1); + assert_eq!(log2_floor(3), 1); + assert_eq!(log2_floor(4), 2); + assert_eq!(log2_floor(5), 2); + assert_eq!(log2_floor(6), 2); + assert_eq!(log2_floor(7), 2); + assert_eq!(log2_floor(8), 3); +} diff --git a/src/rust/bellman/src/multiexp.rs b/src/rust/bellman/src/multiexp.rs new file mode 100644 index 000000000..b1dc1f1fc --- /dev/null +++ b/src/rust/bellman/src/multiexp.rs @@ -0,0 +1,303 @@ +use pairing::{ + CurveAffine, + CurveProjective, + Engine, + PrimeField, + Field, + PrimeFieldRepr +}; +use std::sync::Arc; +use std::io; +use bit_vec::{self, BitVec}; +use std::iter; +use futures::{Future}; +use super::multicore::Worker; + +use super::SynthesisError; + +/// An object that builds a source of bases. +pub trait SourceBuilder: Send + Sync + 'static + Clone { + type Source: Source; + + fn new(self) -> Self::Source; +} + +/// A source of bases, like an iterator. +pub trait Source { + /// Parses the element from the source. Fails if the point is at infinity. + fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError>; + + /// Skips `amt` elements from the source, avoiding deserialization. + fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; +} + +impl SourceBuilder for (Arc>, usize) { + type Source = (Arc>, usize); + + fn new(self) -> (Arc>, usize) { + (self.0.clone(), self.1) + } +} + +impl Source for (Arc>, usize) { + fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError> { + if self.0.len() <= self.1 { + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into()); + } + + if self.0[self.1].is_zero() { + return Err(SynthesisError::UnexpectedIdentity) + } + + to.add_assign_mixed(&self.0[self.1]); + + self.1 += 1; + + Ok(()) + } + + fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { + if self.0.len() <= self.1 { + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into()); + } + + self.1 += amt; + + Ok(()) + } +} + +pub trait QueryDensity { + /// Returns whether the base exists. + type Iter: Iterator; + + fn iter(self) -> Self::Iter; + fn get_query_size(self) -> Option; +} + +#[derive(Clone)] +pub struct FullDensity; + +impl AsRef for FullDensity { + fn as_ref(&self) -> &FullDensity { + self + } +} + +impl<'a> QueryDensity for &'a FullDensity { + type Iter = iter::Repeat; + + fn iter(self) -> Self::Iter { + iter::repeat(true) + } + + fn get_query_size(self) -> Option { + None + } +} + +pub struct DensityTracker { + bv: BitVec, + total_density: usize +} + +impl<'a> QueryDensity for &'a DensityTracker { + type Iter = bit_vec::Iter<'a>; + + fn iter(self) -> Self::Iter { + self.bv.iter() + } + + fn get_query_size(self) -> Option { + Some(self.bv.len()) + } +} + +impl DensityTracker { + pub fn new() -> DensityTracker { + DensityTracker { + bv: BitVec::new(), + total_density: 0 + } + } + + pub fn add_element(&mut self) { + self.bv.push(false); + } + + pub fn inc(&mut self, idx: usize) { + if !self.bv.get(idx).unwrap() { + self.bv.set(idx, true); + self.total_density += 1; + } + } + + pub fn get_total_density(&self) -> usize { + self.total_density + } +} + +fn multiexp_inner( + pool: &Worker, + bases: S, + density_map: D, + exponents: Arc::Fr as PrimeField>::Repr>>, + mut skip: u32, + c: u32, + handle_trivial: bool +) -> Box::Projective, Error=SynthesisError>> + where for<'a> &'a Q: QueryDensity, + D: Send + Sync + 'static + Clone + AsRef, + G: CurveAffine, + S: SourceBuilder +{ + // Perform this region of the multiexp + let this = { + let bases = bases.clone(); + let exponents = exponents.clone(); + let density_map = density_map.clone(); + + pool.compute(move || { + // Accumulate the result + let mut acc = G::Projective::zero(); + + // Build a source for the bases + let mut bases = bases.new(); + + // Create space for the buckets + let mut buckets = vec![::Projective::zero(); (1 << c) - 1]; + + let zero = ::Fr::zero().into_repr(); + let one = ::Fr::one().into_repr(); + + // Sort the bases into buckets + for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { + if density { + if exp == zero { + bases.skip(1)?; + } else if exp == one { + if handle_trivial { + bases.add_assign_mixed(&mut acc)?; + } else { + bases.skip(1)?; + } + } else { + let mut exp = exp; + exp.shr(skip); + let exp = exp.as_ref()[0] % (1 << c); + + if exp != 0 { + bases.add_assign_mixed(&mut buckets[(exp - 1) as usize])?; + } else { + bases.skip(1)?; + } + } + } + } + + // Summation by parts + // e.g. 3a + 2b + 1c = a + + // (a) + b + + // ((a) + b) + c + let mut running_sum = G::Projective::zero(); + for exp in buckets.into_iter().rev() { + running_sum.add_assign(&exp); + acc.add_assign(&running_sum); + } + + Ok(acc) + }) + }; + + skip += c; + + if skip >= ::Fr::NUM_BITS { + // There isn't another region. + Box::new(this) + } else { + // There's another region more significant. Calculate and join it with + // this region recursively. + Box::new( + this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false)) + .map(move |(this, mut higher)| { + for _ in 0..c { + higher.double(); + } + + higher.add_assign(&this); + + higher + }) + ) + } +} + +/// Perform multi-exponentiation. The caller is responsible for ensuring the +/// query size is the same as the number of exponents. +pub fn multiexp( + pool: &Worker, + bases: S, + density_map: D, + exponents: Arc::Fr as PrimeField>::Repr>> +) -> Box::Projective, Error=SynthesisError>> + where for<'a> &'a Q: QueryDensity, + D: Send + Sync + 'static + Clone + AsRef, + G: CurveAffine, + S: SourceBuilder +{ + let c = if exponents.len() < 32 { + 3u32 + } else { + (f64::from(exponents.len() as u32)).ln().ceil() as u32 + }; + + if let Some(query_size) = density_map.as_ref().get_query_size() { + // If the density map has a known query size, it should not be + // inconsistent with the number of exponents. + + assert!(query_size == exponents.len()); + } + + multiexp_inner(pool, bases, density_map, exponents, 0, c, true) +} + +#[test] +fn test_with_bls12() { + fn naive_multiexp( + bases: Arc>, + exponents: Arc::Repr>> + ) -> G::Projective + { + assert_eq!(bases.len(), exponents.len()); + + let mut acc = G::Projective::zero(); + + for (base, exp) in bases.iter().zip(exponents.iter()) { + acc.add_assign(&base.mul(*exp)); + } + + acc + } + + use rand::{self, Rand}; + use pairing::bls12_381::Bls12; + + const SAMPLES: usize = 1 << 14; + + let rng = &mut rand::thread_rng(); + let v = Arc::new((0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>()); + let g = Arc::new((0..SAMPLES).map(|_| ::G1::rand(rng).into_affine()).collect::>()); + + let naive = naive_multiexp(g.clone(), v.clone()); + + let pool = Worker::new(); + + let fast = multiexp( + &pool, + (g, 0), + FullDensity, + v + ).wait().unwrap(); + + assert_eq!(naive, fast); +} diff --git a/src/rust/bellman/tests/mimc.rs b/src/rust/bellman/tests/mimc.rs new file mode 100644 index 000000000..7429d5f86 --- /dev/null +++ b/src/rust/bellman/tests/mimc.rs @@ -0,0 +1,251 @@ +extern crate bellman; +extern crate pairing; +extern crate rand; + +// For randomness (during paramgen and proof generation) +use rand::{thread_rng, Rng}; + +// For benchmarking +use std::time::{Duration, Instant}; + +// Bring in some tools for using pairing-friendly curves +use pairing::{ + Engine, + Field +}; + +// We're going to use the BLS12-381 pairing-friendly elliptic curve. +use pairing::bls12_381::{ + Bls12 +}; + +// We'll use these interfaces to construct our circuit. +use bellman::{ + Circuit, + ConstraintSystem, + SynthesisError +}; + +// We're going to use the Groth16 proving system. +use bellman::groth16::{ + Proof, + generate_random_parameters, + prepare_verifying_key, + create_random_proof, + verify_proof, +}; + +const MIMC_ROUNDS: usize = 322; + +/// This is an implementation of MiMC, specifically a +/// variant named `LongsightF322p3` for BLS12-381. +/// See http://eprint.iacr.org/2016/492 for more +/// information about this construction. +/// +/// ``` +/// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) { +/// for i from 0 up to 321 { +/// xL, xR := xR + (xL + Ci)^3, xL +/// } +/// return xL +/// } +/// ``` +fn mimc( + mut xl: E::Fr, + mut xr: E::Fr, + constants: &[E::Fr] +) -> E::Fr +{ + assert_eq!(constants.len(), MIMC_ROUNDS); + + for i in 0..MIMC_ROUNDS { + let mut tmp1 = xl; + tmp1.add_assign(&constants[i]); + let mut tmp2 = tmp1; + tmp2.square(); + tmp2.mul_assign(&tmp1); + tmp2.add_assign(&xr); + xr = xl; + xl = tmp2; + } + + xl +} + +/// This is our demo circuit for proving knowledge of the +/// preimage of a MiMC hash invocation. +struct MiMCDemo<'a, E: Engine> { + xl: Option, + xr: Option, + constants: &'a [E::Fr] +} + +/// Our demo circuit implements this `Circuit` trait which +/// is used during paramgen and proving in order to +/// synthesize the constraint system. +impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + assert_eq!(self.constants.len(), MIMC_ROUNDS); + + // Allocate the first component of the preimage. + let mut xl_value = self.xl; + let mut xl = cs.alloc(|| "preimage xl", || { + xl_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + // Allocate the second component of the preimage. + let mut xr_value = self.xr; + let mut xr = cs.alloc(|| "preimage xr", || { + xr_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + for i in 0..MIMC_ROUNDS { + // xL, xR := xR + (xL + Ci)^3, xL + let cs = &mut cs.namespace(|| format!("round {}", i)); + + // tmp = (xL + Ci)^2 + let mut tmp_value = xl_value.map(|mut e| { + e.add_assign(&self.constants[i]); + e.square(); + e + }); + let mut tmp = cs.alloc(|| "tmp", || { + tmp_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + cs.enforce( + || "tmp = (xL + Ci)^2", + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + tmp + ); + + // new_xL = xR + (xL + Ci)^3 + // new_xL = xR + tmp * (xL + Ci) + // new_xL - xR = tmp * (xL + Ci) + let mut new_xl_value = xl_value.map(|mut e| { + e.add_assign(&self.constants[i]); + e.mul_assign(&tmp_value.unwrap()); + e.add_assign(&xr_value.unwrap()); + e + }); + + let mut new_xl = if i == (MIMC_ROUNDS-1) { + // This is the last round, xL is our image and so + // we allocate a public input. + cs.alloc_input(|| "image", || { + new_xl_value.ok_or(SynthesisError::AssignmentMissing) + })? + } else { + cs.alloc(|| "new_xl", || { + new_xl_value.ok_or(SynthesisError::AssignmentMissing) + })? + }; + + cs.enforce( + || "new_xL = xR + (xL + Ci)^3", + |lc| lc + tmp, + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + new_xl - xr + ); + + // xR = xL + xr = xl; + xr_value = xl_value; + + // xL = new_xL + xl = new_xl; + xl_value = new_xl_value; + } + + Ok(()) + } +} + +#[test] +fn test_mimc() { + // This may not be cryptographically safe, use + // `OsRng` (for example) in production software. + let rng = &mut thread_rng(); + + // Generate the MiMC round constants + let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); + + println!("Creating parameters..."); + + // Create parameters for our circuit + let params = { + let c = MiMCDemo:: { + xl: None, + xr: None, + constants: &constants + }; + + generate_random_parameters(c, rng).unwrap() + }; + + // Prepare the verification key (for proof verification) + let pvk = prepare_verifying_key(¶ms.vk); + + println!("Creating proofs..."); + + // Let's benchmark stuff! + const SAMPLES: u32 = 50; + let mut total_proving = Duration::new(0, 0); + let mut total_verifying = Duration::new(0, 0); + + // Just a place to put the proof data, so we can + // benchmark deserialization. + let mut proof_vec = vec![]; + + for _ in 0..SAMPLES { + // Generate a random preimage and compute the image + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(xl, xr, &constants); + + proof_vec.truncate(0); + + let start = Instant::now(); + { + // Create an instance of our circuit (with the + // witness) + let c = MiMCDemo { + xl: Some(xl), + xr: Some(xr), + constants: &constants + }; + + // Create a groth16 proof with our parameters. + let proof = create_random_proof(c, ¶ms, rng).unwrap(); + + proof.write(&mut proof_vec).unwrap(); + } + + total_proving += start.elapsed(); + + let start = Instant::now(); + let proof = Proof::read(&proof_vec[..]).unwrap(); + // Check the proof + assert!(verify_proof( + &pvk, + &proof, + &[image] + ).unwrap()); + total_verifying += start.elapsed(); + } + let proving_avg = total_proving / SAMPLES; + let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + + (proving_avg.as_secs() as f64); + + let verifying_avg = total_verifying / SAMPLES; + let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + + (verifying_avg.as_secs() as f64); + + println!("Average proving time: {:?} seconds", proving_avg); + println!("Average verifying time: {:?} seconds", verifying_avg); +} diff --git a/src/rust/librustzcash/Cargo.toml b/src/rust/librustzcash/Cargo.toml new file mode 100644 index 000000000..b9b433487 --- /dev/null +++ b/src/rust/librustzcash/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "librustzcash" +version = "0.1.0" +authors = [ + "Sean Bowe ", + "Jack Grigg ", + "Jay Graber ", + "Simon Liu " + ] + +[lib] +name = "rustzcash" +path = "src/rustzcash.rs" +crate-type = ["staticlib"] + +[dependencies] +bellman = { path = "../bellman" } +libc = "0.2" +pairing = { path = "../pairing" } +lazy_static = "1" +byteorder = "1" +rand = "0.4" +sapling-crypto = { path = "../sapling-crypto" } +zip32 = { path = "../zip32" } + +[dependencies.blake2-rfc] +git = "https://github.com/gtank/blake2-rfc" +rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" diff --git a/src/rustzcash/README.md b/src/rust/librustzcash/README.md similarity index 65% rename from src/rustzcash/README.md rename to src/rust/librustzcash/README.md index 7cdca84d9..c21ca8e56 100644 --- a/src/rustzcash/README.md +++ b/src/rust/librustzcash/README.md @@ -1,17 +1,12 @@ # librustzcash -`librustzcash` is an FFI library crate that exposes the Zcash Rust components to -the `zcashd` full node. - -The FFI API does not have any stability guarantees, and will change as required -by `zcashd`. +This repository contains librustzcash, a static library for Zcash code assets written in Rust. ## License Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) + * Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. @@ -22,3 +17,4 @@ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + diff --git a/src/rustzcash/include/librustzcash.h b/src/rust/librustzcash/include/librustzcash.h similarity index 93% rename from src/rustzcash/include/librustzcash.h rename to src/rust/librustzcash/include/librustzcash.h index 1aa85ed20..4efb544d3 100644 --- a/src/rustzcash/include/librustzcash.h +++ b/src/rust/librustzcash/include/librustzcash.h @@ -112,7 +112,8 @@ extern "C" { bool librustzcash_sapling_output_proof( void *ctx, const unsigned char *esk, - const unsigned char *payment_address, + const unsigned char *diversifier, + const unsigned char *pk_d, const unsigned char *rcm, const uint64_t value, unsigned char *cv, @@ -307,33 +308,6 @@ extern "C" { unsigned char *j_ret, unsigned char *addr_ret ); - - uint32_t librustzcash_mmr_append( - uint32_t cbranch, - uint32_t t_len, - const uint32_t *ni_ptr, - const unsigned char *n_ptr, - size_t p_len, - const unsigned char *nn_ptr, - unsigned char *rt_ret, - unsigned char *buf_ret - ); - - uint32_t librustzcash_mmr_delete( - uint32_t cbranch, - uint32_t t_len, - const uint32_t *ni_ptr, - const unsigned char *n_ptr, - size_t p_len, - size_t e_len, - unsigned char *rt_ret - ); - - uint32_t librustzcash_mmr_hash_node( - uint32_t cbranch, - const unsigned char *n_ptr, - unsigned char *h_ret - ); } #endif // LIBRUSTZCASH_INCLUDE_H_ diff --git a/src/rust/librustzcash/src/equihash.rs b/src/rust/librustzcash/src/equihash.rs new file mode 100644 index 000000000..0509fc256 --- /dev/null +++ b/src/rust/librustzcash/src/equihash.rs @@ -0,0 +1,468 @@ +use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use std::io::Cursor; +use std::mem::size_of; + +struct Params { + n: u32, + k: u32, +} + +#[derive(Clone)] +struct Node { + hash: Vec, + indices: Vec, +} + +impl Params { + fn indices_per_hash_output(&self) -> u32 { + 512 / self.n + } + fn hash_output(&self) -> u8 { + (self.indices_per_hash_output() * self.n / 8) as u8 + } + fn collision_bit_length(&self) -> usize { + (self.n / (self.k + 1)) as usize + } + fn collision_byte_length(&self) -> usize { + (self.collision_bit_length() + 7) / 8 + } + fn hash_length(&self) -> usize { + ((self.k as usize) + 1) * self.collision_byte_length() + } +} + +impl Node { + fn new(p: &Params, state: &Blake2b, i: u32) -> Self { + let hash = generate_hash(state, i / p.indices_per_hash_output()); + let start = ((i % p.indices_per_hash_output()) * p.n / 8) as usize; + let end = start + (p.n as usize) / 8; + Node { + hash: expand_array(&hash.as_bytes()[start..end], p.collision_bit_length(), 0), + indices: vec![i], + } + } + + fn from_children(a: Node, b: Node, trim: usize) -> Self { + let hash: Vec<_> = a + .hash + .iter() + .zip(b.hash.iter()) + .skip(trim) + .map(|(a, b)| a ^ b) + .collect(); + let indices = if a.indices_before(&b) { + let mut indices = a.indices; + indices.extend(b.indices.iter()); + indices + } else { + let mut indices = b.indices; + indices.extend(a.indices.iter()); + indices + }; + Node { + hash: hash, + indices: indices, + } + } + + fn from_children_ref(a: &Node, b: &Node, trim: usize) -> Self { + let hash: Vec<_> = a + .hash + .iter() + .zip(b.hash.iter()) + .skip(trim) + .map(|(a, b)| a ^ b) + .collect(); + let mut indices = Vec::with_capacity(a.indices.len() + b.indices.len()); + if a.indices_before(b) { + indices.extend(a.indices.iter()); + indices.extend(b.indices.iter()); + } else { + indices.extend(b.indices.iter()); + indices.extend(a.indices.iter()); + } + Node { + hash: hash, + indices: indices, + } + } + + fn indices_before(&self, other: &Node) -> bool { + // Indices are serialized in big-endian so that integer + // comparison is equivalent to array comparison + self.indices[0] < other.indices[0] + } + + fn is_zero(&self, len: usize) -> bool { + self.hash.iter().take(len).all(|v| *v == 0) + } +} + +fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2b { + let mut personalization: Vec = Vec::from("ZcashPoW"); + personalization.write_u32::(n).unwrap(); + personalization.write_u32::(k).unwrap(); + + Blake2b::with_params(digest_len as usize, &[], &[], &personalization) +} + +fn generate_hash(base_state: &Blake2b, i: u32) -> Blake2bResult { + let mut lei = [0u8; 4]; + (&mut lei[..]).write_u32::(i).unwrap(); + + let mut state = base_state.clone(); + state.update(&lei); + state.finalize() +} + +fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { + assert!(bit_len >= 8); + assert!(8 * size_of::() >= 7 + bit_len); + + let out_width = (bit_len + 7) / 8 + byte_pad; + let out_len = 8 * out_width * vin.len() / bit_len; + + // Shortcut for parameters where expansion is a no-op + if out_len == vin.len() { + return vin.to_vec(); + } + + let mut vout: Vec = vec![0; out_len]; + let bit_len_mask: u32 = (1 << bit_len) - 1; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + let mut acc_bits = 0; + let mut acc_value: u32 = 0; + + let mut j = 0; + for b in vin { + acc_value = (acc_value << 8) | *b as u32; + acc_bits += 8; + + // When we have bit_len or more bits in the accumulator, write the next + // output element. + if acc_bits >= bit_len { + acc_bits -= bit_len; + for x in byte_pad..out_width { + vout[j + x] = (( + // Big-endian + acc_value >> (acc_bits + (8 * (out_width - x - 1))) + ) + & ( + // Apply bit_len_mask across byte boundaries + (bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF + )) as u8; + } + j += out_width; + } + } + + vout +} + +fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec { + assert!(((c_bit_len + 1) + 7) / 8 <= size_of::()); + let len_indices = 8 * size_of::() * minimal.len() / (c_bit_len + 1); + let byte_pad = size_of::() - ((c_bit_len + 1) + 7) / 8; + + let mut csr = Cursor::new(expand_array(minimal, c_bit_len + 1, byte_pad)); + let mut ret = Vec::with_capacity(len_indices); + + // Big-endian so that lexicographic array comparison is equivalent to integer + // comparison + while let Ok(i) = csr.read_u32::() { + ret.push(i); + } + + ret +} + +fn has_collision(a: &Node, b: &Node, len: usize) -> bool { + a.hash + .iter() + .zip(b.hash.iter()) + .take(len) + .all(|(a, b)| a == b) +} + +fn distinct_indices(a: &Node, b: &Node) -> bool { + for i in &(a.indices) { + for j in &(b.indices) { + if i == j { + return false; + } + } + } + return true; +} + +fn validate_subtrees(p: &Params, a: &Node, b: &Node) -> bool { + if !has_collision(a, b, p.collision_byte_length()) { + // error!("Invalid solution: invalid collision length between StepRows"); + false + } else if b.indices_before(a) { + // error!("Invalid solution: Index tree incorrectly ordered"); + false + } else if !distinct_indices(a, b) { + // error!("Invalid solution: duplicate indices"); + false + } else { + true + } +} + +pub fn is_valid_solution_iterative( + n: u32, + k: u32, + input: &[u8], + nonce: &[u8], + indices: &[u32], +) -> bool { + let p = Params { n: n, k: k }; + + let mut state = initialise_state(p.n, p.k, p.hash_output()); + state.update(input); + state.update(nonce); + + let mut rows = Vec::new(); + for i in indices { + rows.push(Node::new(&p, &state, *i)); + } + + let mut hash_len = p.hash_length(); + while rows.len() > 1 { + let mut cur_rows = Vec::new(); + for pair in rows.chunks(2) { + let a = &pair[0]; + let b = &pair[1]; + if !validate_subtrees(&p, a, b) { + return false; + } + cur_rows.push(Node::from_children_ref(a, b, p.collision_byte_length())); + } + rows = cur_rows; + hash_len -= p.collision_byte_length(); + } + + assert!(rows.len() == 1); + return rows[0].is_zero(hash_len); +} + +fn tree_validator(p: &Params, state: &Blake2b, indices: &[u32]) -> Option { + if indices.len() > 1 { + let end = indices.len(); + let mid = end / 2; + match tree_validator(p, state, &indices[0..mid]) { + Some(a) => match tree_validator(p, state, &indices[mid..end]) { + Some(b) => { + if validate_subtrees(p, &a, &b) { + Some(Node::from_children(a, b, p.collision_byte_length())) + } else { + None + } + } + None => None, + }, + None => None, + } + } else { + Some(Node::new(&p, &state, indices[0])) + } +} + +pub fn is_valid_solution_recursive( + n: u32, + k: u32, + input: &[u8], + nonce: &[u8], + indices: &[u32], +) -> bool { + let p = Params { n: n, k: k }; + + let mut state = initialise_state(p.n, p.k, p.hash_output()); + state.update(input); + state.update(nonce); + + match tree_validator(&p, &state, indices) { + Some(root) => { + // Hashes were trimmed, so only need to check remaining length + root.is_zero(p.collision_byte_length()) + } + None => false, + } +} + +pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { + let p = Params { n: n, k: k }; + let indices = indices_from_minimal(soln, p.collision_bit_length()); + + // Recursive validation is faster + is_valid_solution_recursive(n, k, input, nonce, &indices) +} + +#[cfg(test)] +mod tests { + use super::is_valid_solution_iterative; + use super::is_valid_solution_recursive; + + fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], indices: &[u32]) -> bool { + let a = is_valid_solution_iterative(n, k, input, nonce, indices); + let b = is_valid_solution_recursive(n, k, input, nonce, indices); + assert!(a == b); + a + } + + #[test] + fn equihash_test_cases() { + let input = b"block header"; + let mut nonce = [0 as u8; 32]; + let mut indices = vec![ + 976, 126621, 100174, 123328, 38477, 105390, 38834, 90500, 6411, 116489, 51107, 129167, + 25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, 36473, + 17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894, + ]; + assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + + indices = vec![ + 1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, 123026, + 64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, 94769, 118158, + 25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667, + ]; + assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + + indices = vec![ + 4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660, + 724628, 1319625, 632093, 1474613, 665376, 1222606, 244013, 528281, 1741992, 1779660, + 313314, 996273, 435612, 1270863, 337273, 1385279, 1031587, 1147423, 349396, 734528, + 902268, 1678799, 10902, 1231236, 1454381, 1873452, 120530, 2034017, 948243, 1160178, + 198008, 1704079, 1087419, 1734550, 457535, 698704, 649903, 1029510, 75564, 1860165, + 1057819, 1609847, 449808, 527480, 1106201, 1252890, 207200, 390061, 1557573, 1711408, + 396772, 1026145, 652307, 1712346, 10680, 1027631, 232412, 974380, 457702, 1827006, + 1316524, 1400456, 91745, 2032682, 192412, 710106, 556298, 1963798, 1329079, 1504143, + 102455, 974420, 639216, 1647860, 223846, 529637, 425255, 680712, 154734, 541808, + 443572, 798134, 322981, 1728849, 1306504, 1696726, 57884, 913814, 607595, 1882692, + 236616, 1439683, 420968, 943170, 1014827, 1446980, 1468636, 1559477, 1203395, 1760681, + 1439278, 1628494, 195166, 198686, 349906, 1208465, 917335, 1361918, 937682, 1885495, + 494922, 1745948, 1320024, 1826734, 847745, 894084, 1484918, 1523367, 7981, 1450024, + 861459, 1250305, 226676, 329669, 339783, 1935047, 369590, 1564617, 939034, 1908111, + 1147449, 1315880, 1276715, 1428599, 168956, 1442649, 766023, 1171907, 273361, 1902110, + 1169410, 1786006, 413021, 1465354, 707998, 1134076, 977854, 1604295, 1369720, 1486036, + 330340, 1587177, 502224, 1313997, 400402, 1667228, 889478, 946451, 470672, 2019542, + 1023489, 2067426, 658974, 876859, 794443, 1667524, 440815, 1099076, 897391, 1214133, + 953386, 1932936, 1100512, 1362504, 874364, 975669, 1277680, 1412800, 1227580, 1857265, + 1312477, 1514298, 12478, 219890, 534265, 1351062, 65060, 651682, 627900, 1331192, + 123915, 865936, 1218072, 1732445, 429968, 1097946, 947293, 1323447, 157573, 1212459, + 923792, 1943189, 488881, 1697044, 915443, 2095861, 333566, 732311, 336101, 1600549, + 575434, 1978648, 1071114, 1473446, 50017, 54713, 367891, 2055483, 561571, 1714951, + 715652, 1347279, 584549, 1642138, 1002587, 1125289, 1364767, 1382627, 1387373, 2054399, + 97237, 1677265, 707752, 1265819, 121088, 1810711, 1755448, 1858538, 444653, 1130822, + 514258, 1669752, 578843, 729315, 1164894, 1691366, 15609, 1917824, 173620, 587765, + 122779, 2024998, 804857, 1619761, 110829, 1514369, 410197, 493788, 637666, 1765683, + 782619, 1186388, 494761, 1536166, 1582152, 1868968, 825150, 1709404, 1273757, 1657222, + 817285, 1955796, 1014018, 1961262, 873632, 1689675, 985486, 1008905, 130394, 897076, + 419669, 535509, 980696, 1557389, 1244581, 1738170, 197814, 1879515, 297204, 1165124, + 883018, 1677146, 1545438, 2017790, 345577, 1821269, 761785, 1014134, 746829, 751041, + 930466, 1627114, 507500, 588000, 1216514, 1501422, 991142, 1378804, 1797181, 1976685, + 60742, 780804, 383613, 645316, 770302, 952908, 1105447, 1878268, 504292, 1961414, + 693833, 1198221, 906863, 1733938, 1315563, 2049718, 230826, 2064804, 1224594, 1434135, + 897097, 1961763, 993758, 1733428, 306643, 1402222, 532661, 627295, 453009, 973231, + 1746809, 1857154, 263652, 1683026, 1082106, 1840879, 768542, 1056514, 888164, 1529401, + 327387, 1708909, 961310, 1453127, 375204, 878797, 1311831, 1969930, 451358, 1229838, + 583937, 1537472, 467427, 1305086, 812115, 1065593, 532687, 1656280, 954202, 1318066, + 1164182, 1963300, 1232462, 1722064, 17572, 923473, 1715089, 2079204, 761569, 1557392, + 1133336, 1183431, 175157, 1560762, 418801, 927810, 734183, 825783, 1844176, 1951050, + 317246, 336419, 711727, 1630506, 634967, 1595955, 683333, 1461390, 458765, 1834140, + 1114189, 1761250, 459168, 1897513, 1403594, 1478683, 29456, 1420249, 877950, 1371156, + 767300, 1848863, 1607180, 1819984, 96859, 1601334, 171532, 2068307, 980009, 2083421, + 1329455, 2030243, 69434, 1965626, 804515, 1339113, 396271, 1252075, 619032, 2080090, + 84140, 658024, 507836, 772757, 154310, 1580686, 706815, 1024831, 66704, 614858, 256342, + 957013, 1488503, 1615769, 1515550, 1888497, 245610, 1333432, 302279, 776959, 263110, + 1523487, 623933, 2013452, 68977, 122033, 680726, 1849411, 426308, 1292824, 460128, + 1613657, 234271, 971899, 1320730, 1559313, 1312540, 1837403, 1690310, 2040071, 149918, + 380012, 785058, 1675320, 267071, 1095925, 1149690, 1318422, 361557, 1376579, 1587551, + 1715060, 1224593, 1581980, 1354420, 1850496, 151947, 748306, 1987121, 2070676, 273794, + 981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044, + 1971986, + ]; + assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); + assert!(is_valid_solution(200, 9, input, &nonce, &indices)); + + nonce[0] = 1; + assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); + assert!(!is_valid_solution(200, 9, input, &nonce, &indices)); + + indices = vec![ + 1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776, + 27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337, + 5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505, + ]; + assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + + indices = vec![ + 1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496, + 264180, 637056, 734225, 1882676, 1112004, 2093109, 193394, 1459136, 525171, 657480, + 214528, 1221365, 574444, 594726, 501919, 1309358, 1740268, 1989610, 654491, 1068055, + 919416, 1993208, 17599, 1858176, 1315176, 1901532, 108258, 109600, 1117445, 1936058, + 70247, 1036984, 628234, 1800109, 149791, 365740, 345683, 563554, 21678, 822781, + 1423722, 1644228, 792912, 1409641, 805060, 2041985, 453824, 1003179, 934427, 1068834, + 629003, 1456111, 670049, 1558594, 19016, 1343657, 1698188, 1865216, 45723, 1820952, + 1160970, 1585983, 422549, 1973097, 1296271, 2006382, 650084, 809838, 871727, 1080419, + 28500, 1471829, 384406, 619459, 212041, 1466258, 481435, 866461, 145340, 1403843, + 1339592, 1405761, 163425, 1073771, 285027, 1488210, 167744, 1182267, 1354059, 2089602, + 921700, 2059931, 1704721, 1853088, 585171, 739246, 747551, 1520527, 590255, 1175747, + 705292, 998433, 522014, 1931179, 1629531, 1692879, 588830, 1799457, 963672, 1664237, + 775408, 1926741, 907030, 1466738, 784179, 1972599, 1494787, 1598114, 1736, 1039487, + 88704, 1302687, 579526, 1476728, 1677992, 1854526, 432470, 2062305, 1471132, 1747579, + 1521894, 1917599, 1590975, 1936227, 151871, 1999775, 224664, 461809, 704084, 1306665, + 1316156, 1529628, 876811, 2086004, 1986383, 2012147, 1039505, 1637502, 1432721, + 1565477, 110385, 342650, 659137, 1285167, 367416, 2007586, 445677, 2084877, 285692, + 1144365, 988840, 1990372, 748425, 1617758, 1267712, 1510433, 152291, 1256291, 1722179, + 1995439, 864844, 1623380, 1071853, 1731862, 699978, 1407662, 1048047, 1849702, 962900, + 1083340, 1378752, 1534902, 11843, 115329, 454796, 548919, 148184, 1686936, 862432, + 873854, 60753, 999864, 385959, 1528101, 534420, 678401, 590419, 1962518, 54984, + 1141820, 243305, 1349970, 599681, 1817233, 1632537, 1698724, 580004, 673073, 1403350, + 2026104, 758881, 970056, 1717966, 2062827, 19624, 148580, 609748, 1588928, 456321, + 834920, 700532, 1682606, 20012, 441139, 1591072, 1923394, 194034, 1741063, 1156906, + 1983067, 20703, 1939972, 604581, 963600, 128170, 731716, 606773, 1626824, 139460, + 1386775, 521911, 2043473, 392180, 449532, 895678, 1453340, 7085, 598416, 1514260, + 2061068, 279532, 678363, 943255, 1405306, 119114, 2075865, 592839, 1972064, 254647, + 2078288, 946282, 1567138, 120422, 767626, 213242, 448366, 438457, 1768467, 853790, + 1509505, 735780, 1979631, 1461410, 1462050, 739008, 1572606, 920754, 1507358, 12883, + 1681167, 1308399, 1839490, 85599, 1387522, 703262, 1949514, 18523, 1236125, 669105, + 1464132, 68670, 2085647, 333393, 1731573, 21714, 637827, 985912, 2091029, 84065, + 1688993, 1574405, 1899543, 134032, 179206, 671016, 1118310, 288960, 861994, 622074, + 1738892, 10936, 343910, 598016, 1741971, 586348, 1956071, 851053, 1715626, 531385, + 1213667, 1093995, 1863757, 630365, 1851894, 1328101, 1770446, 31900, 734027, 1078651, + 1701535, 123276, 1916343, 581822, 1681706, 573135, 818091, 1454710, 2052521, 1150284, + 1451159, 1482280, 1811430, 26321, 785837, 877980, 2073103, 107324, 727248, 1785460, + 1840517, 184560, 185640, 364103, 1878753, 518459, 1984029, 964109, 1884200, 74003, + 527272, 516232, 711247, 148582, 209254, 634610, 1534140, 376714, 1573267, 421225, + 1265101, 1078858, 1374310, 1806283, 2091298, 23392, 389637, 413663, 1066737, 226164, + 762552, 1048220, 1583397, 40092, 277435, 775449, 1533894, 202582, 390703, 346741, + 1027320, 523034, 809424, 584882, 1296934, 528062, 733331, 1212771, 1958651, 653372, + 1313962, 1366332, 1784489, 1542466, 1580386, 1628948, 2000957, 57069, 1398636, 1250431, + 1698486, 57289, 596009, 582428, 966130, 167657, 1025537, 1227498, 1630134, 234060, + 1285209, 265623, 1165779, 68485, 632055, 96019, 1854676, 98410, 158575, 168035, + 1296171, 158847, 1243959, 977212, 1113647, 363568, 891940, 954593, 1987111, 90101, + 133251, 1136222, 1255117, 543075, 732768, 749576, 1174878, 422226, 1854657, 1143029, + 1457135, 927105, 1137382, 1566306, 1661926, 103057, 425126, 698089, 1774942, 911019, + 1793511, 1623559, 2002409, 457796, 1196971, 724257, 1811147, 956269, 1165590, 1137531, + 1381215, 201063, 1938529, 986021, 1297857, 921334, 1259083, 1440074, 1939366, 232907, + 747213, 1349009, 1945364, 689906, 1116453, 1904207, 1916192, 229793, 1576982, 1420059, + 1644978, 278248, 2024807, 297914, 419798, 555747, 712605, 1012424, 1428921, 890113, + 1822645, 1082368, 1392894, + ]; + assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); + assert!(is_valid_solution(200, 9, input, &nonce, &indices)); + + let input2 = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; + indices = vec![ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ]; + assert!(is_valid_solution(96, 5, input2, &nonce, &indices)); + } +} diff --git a/src/rust/librustzcash/src/hashreader.rs b/src/rust/librustzcash/src/hashreader.rs new file mode 100644 index 000000000..a422d5203 --- /dev/null +++ b/src/rust/librustzcash/src/hashreader.rs @@ -0,0 +1,42 @@ +use blake2_rfc::blake2b::Blake2b; +use std::io::{self, Read}; + +/// Abstraction over a reader which hashes the data being read. +pub struct HashReader { + reader: R, + hasher: Blake2b, +} + +impl HashReader { + /// Construct a new `HashReader` given an existing `reader` by value. + pub fn new(reader: R) -> Self { + HashReader { + reader: reader, + hasher: Blake2b::new(64), + } + } + + /// Destroy this reader and return the hash of what was read. + pub fn into_hash(self) -> String { + let hash = self.hasher.finalize(); + + let mut s = String::new(); + for c in hash.as_bytes().iter() { + s += &format!("{:02x}", c); + } + + s + } +} + +impl Read for HashReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let bytes = self.reader.read(buf)?; + + if bytes > 0 { + self.hasher.update(&buf[0..bytes]); + } + + Ok(bytes) + } +} diff --git a/src/rust/librustzcash/src/rustzcash.rs b/src/rust/librustzcash/src/rustzcash.rs new file mode 100644 index 000000000..dd903bee2 --- /dev/null +++ b/src/rust/librustzcash/src/rustzcash.rs @@ -0,0 +1,1711 @@ +extern crate bellman; +extern crate blake2_rfc; +extern crate byteorder; +extern crate libc; +extern crate pairing; +extern crate rand; +extern crate sapling_crypto; +extern crate zip32; + +mod hashreader; + +#[macro_use] +extern crate lazy_static; + +use pairing::{ + bls12_381::{Bls12, Fr, FrRepr}, + BitIterator, Field, PrimeField, PrimeFieldRepr, +}; + +use sapling_crypto::{ + circuit::multipack, + constants::CRH_IVK_PERSONALIZATION, + jubjub::{ + edwards, + fs::{Fs, FsRepr}, + FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown, + }, + pedersen_hash::{pedersen_hash, Personalization}, + redjubjub::{self, Signature}, +}; + +use sapling_crypto::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}; +// TODO: make these consistent +const SAPLING_TREE_DEPTH: usize = 32; + +use bellman::groth16::{ + create_random_proof, prepare_verifying_key, verify_proof, Parameters, PreparedVerifyingKey, + Proof, VerifyingKey, +}; + +use blake2_rfc::blake2s::Blake2s; + +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; + +use rand::{OsRng, Rand, Rng}; +use std::io::{self, BufReader}; + +use libc::{c_char, c_uchar, int64_t, size_t, uint32_t, uint64_t}; +use std::ffi::CStr; +use std::fs::File; +use std::path::{Path, PathBuf}; +use std::slice; + +#[cfg(not(target_os = "windows"))] +use std::ffi::OsStr; +#[cfg(not(target_os = "windows"))] +use std::os::unix::ffi::OsStrExt; + +#[cfg(target_os = "windows")] +use std::ffi::OsString; +#[cfg(target_os = "windows")] +use std::os::windows::ffi::OsStringExt; + +use sapling_crypto::primitives::{ProofGenerationKey, ValueCommitment, ViewingKey}; + +pub mod equihash; + +#[cfg(test)] +mod tests; + +lazy_static! { + static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; +} + +static mut SAPLING_SPEND_VK: Option> = None; +static mut SAPLING_OUTPUT_VK: Option> = None; +static mut SPROUT_GROTH16_VK: Option> = None; + +static mut SAPLING_SPEND_PARAMS: Option> = None; +static mut SAPLING_OUTPUT_PARAMS: Option> = None; +static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; + +fn is_small_order(p: &edwards::Point) -> bool { + p.double(&JUBJUB).double(&JUBJUB).double(&JUBJUB) == edwards::Point::zero() +} + +/// Writes an FrRepr to [u8] of length 32 +fn write_le(f: FrRepr, to: &mut [u8]) { + assert_eq!(to.len(), 32); + + f.write_le(to).expect("length is 32 bytes"); +} + +/// Reads an FrRepr from a [u8] of length 32. +/// This will panic (abort) if length provided is +/// not correct. +fn read_le(from: &[u8]) -> FrRepr { + assert_eq!(from.len(), 32); + + let mut f = FrRepr::default(); + f.read_le(from).expect("length is 32 bytes"); + + f +} + +/// Reads an FsRepr from [u8] of length 32 +/// This will panic (abort) if length provided is +/// not correct +fn read_fs(from: &[u8]) -> FsRepr { + assert_eq!(from.len(), 32); + + let mut f = <::Fs as PrimeField>::Repr::default(); + f.read_le(from).expect("length is 32 bytes"); + + f +} + +/// Reads an FsRepr from [u8] of length 32 +/// and multiplies it by the given base. +/// This will panic (abort) if length provided is +/// not correct +fn fixed_scalar_mult(from: &[u8], p_g: FixedGenerators) -> edwards::Point { + let f = read_fs(from); + + JUBJUB.generator(p_g).mul(f, &JUBJUB) +} + +#[cfg(not(target_os = "windows"))] +#[no_mangle] +pub extern "system" fn librustzcash_init_zksnark_params( + spend_path: *const u8, + spend_path_len: usize, + spend_hash: *const c_char, + output_path: *const u8, + output_path_len: usize, + output_hash: *const c_char, + sprout_path: *const u8, + sprout_path_len: usize, + sprout_hash: *const c_char, +) { + let spend_path = Path::new(OsStr::from_bytes(unsafe { + slice::from_raw_parts(spend_path, spend_path_len) + })); + let output_path = Path::new(OsStr::from_bytes(unsafe { + slice::from_raw_parts(output_path, output_path_len) + })); + let sprout_path = Path::new(OsStr::from_bytes(unsafe { + slice::from_raw_parts(sprout_path, sprout_path_len) + })); + + init_zksnark_params( + spend_path, + spend_hash, + output_path, + output_hash, + sprout_path, + sprout_hash, + ) +} + +#[cfg(target_os = "windows")] +#[no_mangle] +pub extern "system" fn librustzcash_init_zksnark_params( + spend_path: *const u16, + spend_path_len: usize, + spend_hash: *const c_char, + output_path: *const u16, + output_path_len: usize, + output_hash: *const c_char, + sprout_path: *const u16, + sprout_path_len: usize, + sprout_hash: *const c_char, +) { + let spend_path = + OsString::from_wide(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }); + let output_path = + OsString::from_wide(unsafe { slice::from_raw_parts(output_path, output_path_len) }); + let sprout_path = + OsString::from_wide(unsafe { slice::from_raw_parts(sprout_path, sprout_path_len) }); + + init_zksnark_params( + Path::new(&spend_path), + spend_hash, + Path::new(&output_path), + output_hash, + Path::new(&sprout_path), + sprout_hash, + ) +} + +fn init_zksnark_params( + spend_path: &Path, + spend_hash: *const c_char, + output_path: &Path, + output_hash: *const c_char, + sprout_path: &Path, + sprout_hash: *const c_char, +) { + // Initialize jubjub parameters here + lazy_static::initialize(&JUBJUB); + + let spend_hash = unsafe { CStr::from_ptr(spend_hash) } + .to_str() + .expect("hash should be a valid string") + .to_string(); + + let output_hash = unsafe { CStr::from_ptr(output_hash) } + .to_str() + .expect("hash should be a valid string") + .to_string(); + + let sprout_hash = unsafe { CStr::from_ptr(sprout_hash) } + .to_str() + .expect("hash should be a valid string") + .to_string(); + + // Load from each of the paths + let spend_fs = File::open(spend_path).expect("couldn't load Sapling spend parameters file"); + let output_fs = File::open(output_path).expect("couldn't load Sapling output parameters file"); + let sprout_fs = File::open(sprout_path).expect("couldn't load Sprout groth16 parameters file"); + + let mut spend_fs = hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, spend_fs)); + let mut output_fs = + hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, output_fs)); + let mut sprout_fs = + hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, sprout_fs)); + + // Deserialize params + let spend_params = Parameters::::read(&mut spend_fs, false) + .expect("couldn't deserialize Sapling spend parameters file"); + let output_params = Parameters::::read(&mut output_fs, false) + .expect("couldn't deserialize Sapling spend parameters file"); + + // We only deserialize the verifying key for the Sprout parameters, which + // appears at the beginning of the parameter file. The rest is loaded + // during proving time. + let sprout_vk = VerifyingKey::::read(&mut sprout_fs) + .expect("couldn't deserialize Sprout Groth16 verifying key"); + + // There is extra stuff (the transcript) at the end of the parameter file which is + // used to verify the parameter validity, but we're not interested in that. We do + // want to read it, though, so that the BLAKE2b computed afterward is consistent + // with `b2sum` on the files. + let mut sink = io::sink(); + io::copy(&mut spend_fs, &mut sink) + .expect("couldn't finish reading Sapling spend parameter file"); + io::copy(&mut output_fs, &mut sink) + .expect("couldn't finish reading Sapling output parameter file"); + io::copy(&mut sprout_fs, &mut sink) + .expect("couldn't finish reading Sprout groth16 parameter file"); + + if spend_fs.into_hash() != spend_hash { + panic!("Sapling spend parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); + } + + if output_fs.into_hash() != output_hash { + panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); + } + + if sprout_fs.into_hash() != sprout_hash { + panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); + } + + // Prepare verifying keys + let spend_vk = prepare_verifying_key(&spend_params.vk); + let output_vk = prepare_verifying_key(&output_params.vk); + let sprout_vk = prepare_verifying_key(&sprout_vk); + + // Caller is responsible for calling this function once, so + // these global mutations are safe. + unsafe { + SAPLING_SPEND_PARAMS = Some(spend_params); + SAPLING_OUTPUT_PARAMS = Some(output_params); + SPROUT_GROTH16_PARAMS_PATH = Some(sprout_path.to_owned()); + + SAPLING_SPEND_VK = Some(spend_vk); + SAPLING_OUTPUT_VK = Some(output_vk); + SPROUT_GROTH16_VK = Some(sprout_vk); + } +} + +#[no_mangle] +pub extern "system" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) { + let tmp = sapling_crypto::primitives::Note::::uncommitted().into_repr(); + + // Should be okay, caller is responsible for ensuring the pointer + // is a valid pointer to 32 bytes that can be mutated. + let result = unsafe { &mut *result }; + + write_le(tmp, &mut result[..]); +} + +#[no_mangle] +pub extern "system" fn librustzcash_merkle_hash( + depth: size_t, + a: *const [c_uchar; 32], + b: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) { + // Should be okay, because caller is responsible for ensuring + // the pointer is a valid pointer to 32 bytes, and that is the + // size of the representation + let a_repr = read_le(unsafe { &(&*a)[..] }); + + // Should be okay, because caller is responsible for ensuring + // the pointer is a valid pointer to 32 bytes, and that is the + // size of the representation + let b_repr = read_le(unsafe { &(&*b)[..] }); + + let mut lhs = [false; 256]; + let mut rhs = [false; 256]; + + for (a, b) in lhs.iter_mut().rev().zip(BitIterator::new(a_repr)) { + *a = b; + } + + for (a, b) in rhs.iter_mut().rev().zip(BitIterator::new(b_repr)) { + *a = b; + } + + let tmp = pedersen_hash::( + Personalization::MerkleTree(depth), + lhs.iter() + .map(|&x| x) + .take(Fr::NUM_BITS as usize) + .chain(rhs.iter().map(|&x| x).take(Fr::NUM_BITS as usize)), + &JUBJUB, + ).into_xy() + .0 + .into_repr(); + + // Should be okay, caller is responsible for ensuring the pointer + // is a valid pointer to 32 bytes that can be mutated. + let result = unsafe { &mut *result }; + + write_le(tmp, &mut result[..]); +} + +#[no_mangle] // ToScalar +pub extern "system" fn librustzcash_to_scalar( + input: *const [c_uchar; 64], + result: *mut [c_uchar; 32], +) { + // Should be okay, because caller is responsible for ensuring + // the pointer is a valid pointer to 32 bytes, and that is the + // size of the representation + let scalar = ::Fs::to_uniform(unsafe { &(&*input)[..] }).into_repr(); + + let result = unsafe { &mut *result }; + + scalar + .write_le(&mut result[..]) + .expect("length is 32 bytes"); +} + +#[no_mangle] +pub extern "system" fn librustzcash_ask_to_ak( + ask: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) { + let ask = unsafe { &*ask }; + let ak = fixed_scalar_mult(ask, FixedGenerators::SpendingKeyGenerator); + + let result = unsafe { &mut *result }; + + ak.write(&mut result[..]).expect("length is 32 bytes"); +} + +#[no_mangle] +pub extern "system" fn librustzcash_nsk_to_nk( + nsk: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) { + let nsk = unsafe { &*nsk }; + let nk = fixed_scalar_mult(nsk, FixedGenerators::ProofGenerationKey); + + let result = unsafe { &mut *result }; + + nk.write(&mut result[..]).expect("length is 32 bytes"); +} + +#[no_mangle] +pub extern "system" fn librustzcash_crh_ivk( + ak: *const [c_uchar; 32], + nk: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) { + let ak = unsafe { &*ak }; + let nk = unsafe { &*nk }; + + let mut h = Blake2s::with_params(32, &[], &[], CRH_IVK_PERSONALIZATION); + h.update(ak); + h.update(nk); + let mut h = h.finalize().as_ref().to_vec(); + + // Drop the last five bits, so it can be interpreted as a scalar. + h[31] &= 0b0000_0111; + + let result = unsafe { &mut *result }; + + result.copy_from_slice(&h); +} + +#[no_mangle] +pub extern "system" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool { + let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier }); + diversifier.g_d::(&JUBJUB).is_some() +} + +#[no_mangle] +pub extern "system" fn librustzcash_ivk_to_pkd( + ivk: *const [c_uchar; 32], + diversifier: *const [c_uchar; 11], + result: *mut [c_uchar; 32], +) -> bool { + let ivk = read_fs(unsafe { &*ivk }); + let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier }); + if let Some(g_d) = diversifier.g_d::(&JUBJUB) { + let pk_d = g_d.mul(ivk, &JUBJUB); + + let result = unsafe { &mut *result }; + + pk_d.write(&mut result[..]).expect("length is 32 bytes"); + + true + } else { + false + } +} + +/// Test generation of commitment randomness +#[test] +fn test_gen_r() { + let mut r1 = [0u8; 32]; + let mut r2 = [0u8; 32]; + + // Verify different r values are generated + librustzcash_sapling_generate_r(&mut r1); + librustzcash_sapling_generate_r(&mut r2); + assert_ne!(r1, r2); + + // Verify r values are valid in the field + let mut repr = FsRepr::default(); + repr.read_le(&r1[..]).expect("length is not 32 bytes"); + let _ = Fs::from_repr(repr).unwrap(); + repr.read_le(&r2[..]).expect("length is not 32 bytes"); + let _ = Fs::from_repr(repr).unwrap(); +} + +/// Return 32 byte random scalar, uniformly. +#[no_mangle] +pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { + // create random 64 byte buffer + let mut rng = OsRng::new().expect("should be able to construct RNG"); + let mut buffer = [0u8; 64]; + for i in 0..buffer.len() { + buffer[i] = rng.gen(); + } + + // reduce to uniform value + let r = ::Fs::to_uniform(&buffer[..]); + let result = unsafe { &mut *result }; + r.into_repr() + .write_le(&mut result[..]) + .expect("result must be 32 bytes"); +} + +// Private utility function to get Note from C parameters +fn priv_get_note( + diversifier: *const [c_uchar; 11], + pk_d: *const [c_uchar; 32], + value: uint64_t, + r: *const [c_uchar; 32], +) -> Result, ()> { + let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier }); + let g_d = match diversifier.g_d::(&JUBJUB) { + Some(g_d) => g_d, + None => return Err(()), + }; + + let pk_d = match edwards::Point::::read(&(unsafe { &*pk_d })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return Err(()), + }; + + let pk_d = match pk_d.as_prime_order(&JUBJUB) { + Some(pk_d) => pk_d, + None => return Err(()), + }; + + // Deserialize randomness + let r = match Fs::from_repr(read_fs(&(unsafe { &*r })[..])) { + Ok(r) => r, + Err(_) => return Err(()), + }; + + let note = sapling_crypto::primitives::Note { + value, + g_d, + pk_d, + r, + }; + + Ok(note) +} + +/// Compute Sapling note nullifier. +#[no_mangle] +pub extern "system" fn librustzcash_sapling_compute_nf( + diversifier: *const [c_uchar; 11], + pk_d: *const [c_uchar; 32], + value: uint64_t, + r: *const [c_uchar; 32], + ak: *const [c_uchar; 32], + nk: *const [c_uchar; 32], + position: uint64_t, + result: *mut [c_uchar; 32], +) -> bool { + let note = match priv_get_note(diversifier, pk_d, value, r) { + Ok(p) => p, + Err(_) => return false, + }; + + let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + let ak = match ak.as_prime_order(&JUBJUB) { + Some(ak) => ak, + None => return false, + }; + + let nk = match edwards::Point::::read(&(unsafe { &*nk })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + let nk = match nk.as_prime_order(&JUBJUB) { + Some(nk) => nk, + None => return false, + }; + + let vk = ViewingKey { ak, nk }; + let nf = note.nf(&vk, position, &JUBJUB); + let result = unsafe { &mut *result }; + result.copy_from_slice(&nf); + + true +} + +/// Compute Sapling note commitment. +#[no_mangle] +pub extern "system" fn librustzcash_sapling_compute_cm( + diversifier: *const [c_uchar; 11], + pk_d: *const [c_uchar; 32], + value: uint64_t, + r: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) -> bool { + let note = match priv_get_note(diversifier, pk_d, value, r) { + Ok(p) => p, + Err(_) => return false, + }; + + let result = unsafe { &mut *result }; + write_le(note.cm(&JUBJUB).into_repr(), &mut result[..]); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_ka_agree( + p: *const [c_uchar; 32], + sk: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) -> bool { + // Deserialize p + let p = match edwards::Point::::read(&(unsafe { &*p })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // Deserialize sk + let sk = match Fs::from_repr(read_fs(&(unsafe { &*sk })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // Multiply by 8 + let p = p.mul_by_cofactor(&JUBJUB); + + // Multiply by sk + let p = p.mul(sk, &JUBJUB); + + // Produce result + let result = unsafe { &mut *result }; + p.write(&mut result[..]).expect("length is not 32 bytes"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_ka_derivepublic( + diversifier: *const [c_uchar; 11], + esk: *const [c_uchar; 32], + result: *mut [c_uchar; 32], +) -> bool { + let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier }); + + // Compute g_d from the diversifier + let g_d = match diversifier.g_d::(&JUBJUB) { + Some(g) => g, + None => return false, + }; + + // Deserialize esk + let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + let p = g_d.mul(esk, &JUBJUB); + + let result = unsafe { &mut *result }; + p.write(&mut result[..]).expect("length is not 32 bytes"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_eh_isvalid( + n: uint32_t, + k: uint32_t, + input: *const c_uchar, + input_len: size_t, + nonce: *const c_uchar, + nonce_len: size_t, + soln: *const c_uchar, + soln_len: size_t, +) -> bool { + if (k >= n) || (n % 8 != 0) || (soln_len != (1 << k) * ((n / (k + 1)) as usize + 1) / 8) { + return false; + } + let rs_input = unsafe { slice::from_raw_parts(input, input_len) }; + let rs_nonce = unsafe { slice::from_raw_parts(nonce, nonce_len) }; + let rs_soln = unsafe { slice::from_raw_parts(soln, soln_len) }; + equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln) +} + +pub struct SaplingVerificationContext { + bvk: edwards::Point, +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_verification_ctx_init( +) -> *mut SaplingVerificationContext { + let ctx = Box::new(SaplingVerificationContext { + bvk: edwards::Point::zero(), + }); + + Box::into_raw(ctx) +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_verification_ctx_free( + ctx: *mut SaplingVerificationContext, +) { + drop(unsafe { Box::from_raw(ctx) }); +} + +const GROTH_PROOF_SIZE: usize = 48 // π_A + + 96 // π_B + + 48; // π_C + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_check_spend( + ctx: *mut SaplingVerificationContext, + cv: *const [c_uchar; 32], + anchor: *const [c_uchar; 32], + nullifier: *const [c_uchar; 32], + rk: *const [c_uchar; 32], + zkproof: *const [c_uchar; GROTH_PROOF_SIZE], + spend_auth_sig: *const [c_uchar; 64], + sighash_value: *const [c_uchar; 32], +) -> bool { + // Deserialize the value commitment + let cv = match edwards::Point::::read(&(unsafe { &*cv })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + if is_small_order(&cv) { + return false; + } + + // Accumulate the value commitment in the context + { + let mut tmp = cv.clone(); + tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB); + + // Update the context + unsafe { &mut *ctx }.bvk = tmp; + } + + // Deserialize the anchor, which should be an element + // of Fr. + let anchor = match Fr::from_repr(read_le(&(unsafe { &*anchor })[..])) { + Ok(a) => a, + Err(_) => return false, + }; + + // Grab the nullifier as a sequence of bytes + let nullifier = &unsafe { &*nullifier }[..]; + + // Compute the signature's message for rk/spend_auth_sig + let mut data_to_be_signed = [0u8; 64]; + (&mut data_to_be_signed[0..32]).copy_from_slice(&(unsafe { &*rk })[..]); + (&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash_value })[..]); + + // Deserialize rk + let rk = match redjubjub::PublicKey::::read(&(unsafe { &*rk })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + if is_small_order(&rk.0) { + return false; + } + + // Deserialize the signature + let spend_auth_sig = match Signature::read(&(unsafe { &*spend_auth_sig })[..]) { + Ok(sig) => sig, + Err(_) => return false, + }; + + // Verify the spend_auth_sig + if !rk.verify( + &data_to_be_signed, + &spend_auth_sig, + FixedGenerators::SpendingKeyGenerator, + &JUBJUB, + ) { + return false; + } + + // Construct public input for circuit + let mut public_input = [Fr::zero(); 7]; + { + let (x, y) = rk.0.into_xy(); + public_input[0] = x; + public_input[1] = y; + } + { + let (x, y) = cv.into_xy(); + public_input[2] = x; + public_input[3] = y; + } + public_input[4] = anchor; + + // Add the nullifier through multiscalar packing + { + let nullifier = multipack::bytes_to_bits_le(nullifier); + let nullifier = multipack::compute_multipacking::(&nullifier); + + assert_eq!(nullifier.len(), 2); + + public_input[5] = nullifier[0]; + public_input[6] = nullifier[1]; + } + + // Deserialize the proof + let zkproof = match Proof::::read(&(unsafe { &*zkproof })[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + // Verify the proof + match verify_proof( + unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), + &zkproof, + &public_input[..], + ) { + // No error, and proof verification successful + Ok(true) => true, + + // Any other case + _ => false, + } +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_check_output( + ctx: *mut SaplingVerificationContext, + cv: *const [c_uchar; 32], + cm: *const [c_uchar; 32], + epk: *const [c_uchar; 32], + zkproof: *const [c_uchar; GROTH_PROOF_SIZE], +) -> bool { + // Deserialize the value commitment + let cv = match edwards::Point::::read(&(unsafe { &*cv })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + if is_small_order(&cv) { + return false; + } + + // Accumulate the value commitment in the context + { + let mut tmp = cv.clone(); + tmp = tmp.negate(); // Outputs subtract from the total. + tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB); + + // Update the context + unsafe { &mut *ctx }.bvk = tmp; + } + + // Deserialize the commitment, which should be an element + // of Fr. + let cm = match Fr::from_repr(read_le(&(unsafe { &*cm })[..])) { + Ok(a) => a, + Err(_) => return false, + }; + + // Deserialize the ephemeral key + let epk = match edwards::Point::::read(&(unsafe { &*epk })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + if is_small_order(&epk) { + return false; + } + + // Construct public input for circuit + let mut public_input = [Fr::zero(); 5]; + { + let (x, y) = cv.into_xy(); + public_input[0] = x; + public_input[1] = y; + } + { + let (x, y) = epk.into_xy(); + public_input[2] = x; + public_input[3] = y; + } + public_input[4] = cm; + + // Deserialize the proof + let zkproof = match Proof::::read(&(unsafe { &*zkproof })[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + // Verify the proof + match verify_proof( + unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(), + &zkproof, + &public_input[..], + ) { + // No error, and proof verification successful + Ok(true) => true, + + // Any other case + _ => false, + } +} + +// This function computes `value` in the exponent of the value commitment base +fn compute_value_balance(value: int64_t) -> Option> { + // Compute the absolute value (failing if -i64::MAX is + // the value) + let abs = match value.checked_abs() { + Some(a) => a as u64, + None => return None, + }; + + // Is it negative? We'll have to negate later if so. + let is_negative = value.is_negative(); + + // Compute it in the exponent + let mut value_balance = JUBJUB + .generator(FixedGenerators::ValueCommitmentValue) + .mul(FsRepr::from(abs), &JUBJUB); + + // Negate if necessary + if is_negative { + value_balance = value_balance.negate(); + } + + // Convert to unknown order point + Some(value_balance.into()) +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_final_check( + ctx: *mut SaplingVerificationContext, + value_balance: int64_t, + binding_sig: *const [c_uchar; 64], + sighash_value: *const [c_uchar; 32], +) -> bool { + // Obtain current bvk from the context + let mut bvk = redjubjub::PublicKey(unsafe { &*ctx }.bvk.clone()); + + // Compute value balance + let mut value_balance = match compute_value_balance(value_balance) { + Some(a) => a, + None => return false, + }; + + // Subtract value_balance from current bvk to get final bvk + value_balance = value_balance.negate(); + bvk.0 = bvk.0.add(&value_balance, &JUBJUB); + + // Compute the signature's message for bvk/binding_sig + let mut data_to_be_signed = [0u8; 64]; + bvk.0 + .write(&mut data_to_be_signed[0..32]) + .expect("bvk is 32 bytes"); + (&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash_value })[..]); + + // Deserialize the signature + let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) { + Ok(sig) => sig, + Err(_) => return false, + }; + + // Verify the binding_sig + if !bvk.verify( + &data_to_be_signed, + &binding_sig, + FixedGenerators::ValueCommitmentRandomness, + &JUBJUB, + ) { + return false; + } + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sprout_prove( + proof_out: *mut [c_uchar; GROTH_PROOF_SIZE], + + phi: *const [c_uchar; 32], + rt: *const [c_uchar; 32], + h_sig: *const [c_uchar; 32], + + // First input + in_sk1: *const [c_uchar; 32], + in_value1: uint64_t, + in_rho1: *const [c_uchar; 32], + in_r1: *const [c_uchar; 32], + in_auth1: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8], + + // Second input + in_sk2: *const [c_uchar; 32], + in_value2: uint64_t, + in_rho2: *const [c_uchar; 32], + in_r2: *const [c_uchar; 32], + in_auth2: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8], + + // First output + out_pk1: *const [c_uchar; 32], + out_value1: uint64_t, + out_r1: *const [c_uchar; 32], + + // Second output + out_pk2: *const [c_uchar; 32], + out_value2: uint64_t, + out_r2: *const [c_uchar; 32], + + // Public value + vpub_old: uint64_t, + vpub_new: uint64_t, +) { + let phi = unsafe { *phi }; + let rt = unsafe { *rt }; + let h_sig = unsafe { *h_sig }; + let in_sk1 = unsafe { *in_sk1 }; + let in_rho1 = unsafe { *in_rho1 }; + let in_r1 = unsafe { *in_r1 }; + let in_auth1 = unsafe { *in_auth1 }; + let in_sk2 = unsafe { *in_sk2 }; + let in_rho2 = unsafe { *in_rho2 }; + let in_r2 = unsafe { *in_r2 }; + let in_auth2 = unsafe { *in_auth2 }; + let out_pk1 = unsafe { *out_pk1 }; + let out_r1 = unsafe { *out_r1 }; + let out_pk2 = unsafe { *out_pk2 }; + let out_r2 = unsafe { *out_r2 }; + + let mut inputs = Vec::with_capacity(2); + { + let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| { + let value = Some(value); + let rho = Some(sprout::UniqueRandomness(rho)); + let r = Some(sprout::CommitmentRandomness(r)); + let a_sk = Some(sprout::SpendingKey(sk)); + + // skip the first byte + assert_eq!(auth[0], SPROUT_TREE_DEPTH as u8); + auth = &auth[1..]; + + let mut auth_path = [None; SPROUT_TREE_DEPTH]; + for i in (0..SPROUT_TREE_DEPTH).rev() { + // skip length of inner vector + assert_eq!(auth[0], 32); + auth = &auth[1..]; + + let mut sibling = [0u8; 32]; + sibling.copy_from_slice(&auth[0..32]); + auth = &auth[32..]; + + auth_path[i] = Some((sibling, false)); + } + + let mut position = auth + .read_u64::() + .expect("should have had index at the end"); + + for i in 0..SPROUT_TREE_DEPTH { + auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1); + + position >>= 1; + } + + inputs.push(sprout::JSInput { + value: value, + a_sk: a_sk, + rho: rho, + r: r, + auth_path: auth_path, + }); + }; + + handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]); + handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]); + } + + let mut outputs = Vec::with_capacity(2); + { + let mut handle_output = |a_pk, value, r| { + outputs.push(sprout::JSOutput { + value: Some(value), + a_pk: Some(sprout::PayingKey(a_pk)), + r: Some(sprout::CommitmentRandomness(r)), + }); + }; + + handle_output(out_pk1, out_value1, out_r1); + handle_output(out_pk2, out_value2, out_r2); + } + + let js = sprout::JoinSplit { + vpub_old: Some(vpub_old), + vpub_new: Some(vpub_new), + h_sig: Some(h_sig), + phi: Some(phi), + inputs: inputs, + outputs: outputs, + rt: Some(rt), + }; + + // Load parameters from disk + let sprout_fs = File::open( + unsafe { &SPROUT_GROTH16_PARAMS_PATH } + .as_ref() + .expect("parameters should have been initialized"), + ).expect("couldn't load Sprout groth16 parameters file"); + + let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs); + + let params = Parameters::::read(&mut sprout_fs, false) + .expect("couldn't deserialize Sprout JoinSplit parameters file"); + + drop(sprout_fs); + + // Initialize secure RNG + let mut rng = OsRng::new().expect("should be able to construct RNG"); + + let proof = create_random_proof(js, ¶ms, &mut rng).expect("proving should not fail"); + + proof + .write(&mut (unsafe { &mut *proof_out })[..]) + .expect("should be able to serialize a proof"); +} + +#[no_mangle] +pub extern "system" fn librustzcash_sprout_verify( + proof: *const [c_uchar; GROTH_PROOF_SIZE], + rt: *const [c_uchar; 32], + h_sig: *const [c_uchar; 32], + mac1: *const [c_uchar; 32], + mac2: *const [c_uchar; 32], + nf1: *const [c_uchar; 32], + nf2: *const [c_uchar; 32], + cm1: *const [c_uchar; 32], + cm2: *const [c_uchar; 32], + vpub_old: uint64_t, + vpub_new: uint64_t, +) -> bool { + // Prepare the public input for the verifier + let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2)); + public_input.extend(unsafe { &(&*rt)[..] }); + public_input.extend(unsafe { &(&*h_sig)[..] }); + public_input.extend(unsafe { &(&*nf1)[..] }); + public_input.extend(unsafe { &(&*mac1)[..] }); + public_input.extend(unsafe { &(&*nf2)[..] }); + public_input.extend(unsafe { &(&*mac2)[..] }); + public_input.extend(unsafe { &(&*cm1)[..] }); + public_input.extend(unsafe { &(&*cm2)[..] }); + public_input.write_u64::(vpub_old).unwrap(); + public_input.write_u64::(vpub_new).unwrap(); + + let public_input = multipack::bytes_to_bits(&public_input); + let public_input = multipack::compute_multipacking::(&public_input); + + let proof = match Proof::read(unsafe { &(&*proof)[..] }) { + Ok(p) => p, + Err(_) => return false, + }; + + // Verify the proof + match verify_proof( + unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"), + &proof, + &public_input[..], + ) { + // No error, and proof verification successful + Ok(true) => true, + + // Any other case + _ => false, + } +} + +pub struct SaplingProvingContext { + bsk: Fs, + bvk: edwards::Point, +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_output_proof( + ctx: *mut SaplingProvingContext, + esk: *const [c_uchar; 32], + diversifier: *const [c_uchar; 11], + pk_d: *const [c_uchar; 32], + rcm: *const [c_uchar; 32], + value: uint64_t, + cv: *mut [c_uchar; 32], + zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], +) -> bool { + // Grab `esk`, which the caller should have constructed for the DH key exchange. + let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // Grab the diversifier from the caller. + let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier }); + + // Grab pk_d from the caller. + let pk_d = match edwards::Point::::read(&(unsafe { &*pk_d })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // pk_d should be prime order. + let pk_d = match pk_d.as_prime_order(&JUBJUB) { + Some(p) => p, + None => return false, + }; + + // Construct a payment address + let payment_address = sapling_crypto::primitives::PaymentAddress { + pk_d: pk_d, + diversifier: diversifier, + }; + + // Initialize secure RNG + let mut rng = OsRng::new().expect("should be able to construct RNG"); + + // The caller provides the commitment randomness for the output note + let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // We construct ephemeral randomness for the value commitment. This + // randomness is not given back to the caller, but the synthetic + // blinding factor `bsk` is accumulated in the context. + let rcv = Fs::rand(&mut rng); + + // Accumulate the value commitment randomness in the context + { + let mut tmp = rcv.clone(); + tmp.negate(); // Outputs subtract from the total. + tmp.add_assign(&unsafe { &*ctx }.bsk); + + // Update the context + unsafe { &mut *ctx }.bsk = tmp; + } + + // Construct the value commitment for the proof instance + let value_commitment = sapling_crypto::primitives::ValueCommitment:: { + value: value, + randomness: rcv, + }; + + // We now have a full witness for the output proof. + let instance = sapling_crypto::circuit::sapling::Output { + params: &*JUBJUB, + value_commitment: Some(value_commitment.clone()), + payment_address: Some(payment_address.clone()), + commitment_randomness: Some(rcm), + esk: Some(esk.clone()), + }; + + // Create proof + let proof = create_random_proof( + instance, + unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(), + &mut rng, + ).expect("proving should not fail"); + + // Write the proof out to the caller + proof + .write(&mut (unsafe { &mut *zkproof })[..]) + .expect("should be able to serialize a proof"); + + // Compute the value commitment + let value_commitment: edwards::Point = value_commitment.cm(&JUBJUB).into(); + + // Accumulate the value commitment in the context. We do this to check internal consistency. + { + let mut tmp = value_commitment.clone(); + tmp = tmp.negate(); // Outputs subtract from the total. + tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB); + + // Update the context + unsafe { &mut *ctx }.bvk = tmp; + } + + // Write the value commitment to the caller + value_commitment + .write(&mut (unsafe { &mut *cv })[..]) + .expect("should be able to serialize rcv"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_spend_sig( + ask: *const [c_uchar; 32], + ar: *const [c_uchar; 32], + sighash: *const [c_uchar; 32], + result: *mut [c_uchar; 64], +) -> bool { + // The caller provides the re-randomization of `ak`. + let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // The caller provides `ask`, the spend authorizing key. + let ask = match redjubjub::PrivateKey::::read(&(unsafe { &*ask })[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + // We compute `rsk`... + let rsk = ask.randomize(ar); + + // We compute `rk` from there (needed for key prefixing) + let rk = + redjubjub::PublicKey::from_private(&rsk, FixedGenerators::SpendingKeyGenerator, &JUBJUB); + + // Compute the signature's message for rk/spend_auth_sig + let mut data_to_be_signed = [0u8; 64]; + rk.0 + .write(&mut data_to_be_signed[0..32]) + .expect("message buffer should be 32 bytes"); + (&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash })[..]); + + // Do the signing + let mut rng = OsRng::new().expect("should be able to construct RNG"); + let sig = rsk.sign( + &data_to_be_signed, + &mut rng, + FixedGenerators::SpendingKeyGenerator, + &JUBJUB, + ); + + // Write out the signature + sig.write(&mut (unsafe { &mut *result })[..]) + .expect("result should be 64 bytes"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_binding_sig( + ctx: *const SaplingProvingContext, + value_balance: int64_t, + sighash: *const [c_uchar; 32], + result: *mut [c_uchar; 64], +) -> bool { + // Grab the current `bsk` from the context + let bsk = redjubjub::PrivateKey::(unsafe { &*ctx }.bsk); + + // Grab the `bvk` using DerivePublic. + let bvk = redjubjub::PublicKey::from_private( + &bsk, + FixedGenerators::ValueCommitmentRandomness, + &JUBJUB, + ); + + // In order to check internal consistency, let's use the accumulated value + // commitments (as the verifier would) and apply valuebalance to compare + // against our derived bvk. + { + // Compute value balance + let mut value_balance = match compute_value_balance(value_balance) { + Some(a) => a, + None => return false, + }; + + // Subtract value_balance from current bvk to get final bvk + value_balance = value_balance.negate(); + let mut tmp = unsafe { &*ctx }.bvk.clone(); + tmp = tmp.add(&value_balance, &JUBJUB); + + // The result should be the same, unless the provided valueBalance is wrong. + if bvk.0 != tmp { + return false; + } + } + + // Construct signature message + let mut data_to_be_signed = [0u8; 64]; + bvk.0 + .write(&mut data_to_be_signed[0..32]) + .expect("message buffer should be 32 bytes"); + (&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash })[..]); + + // Sign + let mut rng = OsRng::new().expect("should be able to construct RNG"); + let sig = bsk.sign( + &data_to_be_signed, + &mut rng, + FixedGenerators::ValueCommitmentRandomness, + &JUBJUB, + ); + + // Write out signature + sig.write(&mut (unsafe { &mut *result })[..]) + .expect("result should be 64 bytes"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_spend_proof( + ctx: *mut SaplingProvingContext, + ak: *const [c_uchar; 32], + nsk: *const [c_uchar; 32], + diversifier: *const [c_uchar; 11], + rcm: *const [c_uchar; 32], + ar: *const [c_uchar; 32], + value: uint64_t, + anchor: *const [c_uchar; 32], + witness: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8], + cv: *mut [c_uchar; 32], + rk_out: *mut [c_uchar; 32], + zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], +) -> bool { + let mut rng = OsRng::new().expect("should be able to construct RNG"); + + // We create the randomness of the value commitment + let rcv = Fs::rand(&mut rng); + + // Accumulate the value commitment randomness in the context + { + let mut tmp = rcv.clone(); + tmp.add_assign(&unsafe { &*ctx }.bsk); + + // Update the context + unsafe { &mut *ctx }.bsk = tmp; + } + + // Construct the value commitment + let value_commitment = ValueCommitment:: { + value: value, + randomness: rcv, + }; + + // Grab `ak` from the caller, which should be a point. + let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // `ak` should be prime order. + let ak = match ak.as_prime_order(&JUBJUB) { + Some(p) => p, + None => return false, + }; + + // Grab `nsk` from the caller + let nsk = match Fs::from_repr(read_fs(&(unsafe { &*nsk })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // Construct the proof generation key + let proof_generation_key = ProofGenerationKey { + ak: ak.clone(), + nsk, + }; + + // Construct the viewing key + let viewing_key = proof_generation_key.into_viewing_key(&JUBJUB); + + // Grab the diversifier from the caller + let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier }); + + // Construct the payment address with the viewing key / diversifier + let payment_address = match viewing_key.into_payment_address(diversifier, &JUBJUB) { + Some(p) => p, + None => return false, + }; + + // The caller chooses the note randomness + let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // The caller also chooses the re-randomization of ak + let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // This is the result of the re-randomization, we compute it for the caller + let rk = redjubjub::PublicKey::(ak.into()).randomize( + ar, + FixedGenerators::SpendingKeyGenerator, + &JUBJUB, + ); + + // Write out `rk` to the caller + rk.write(&mut unsafe { &mut *rk_out }[..]) + .expect("should be able to write to rk_out"); + + // We need to compute the anchor of the Spend. + let anchor = match Fr::from_repr(read_le(unsafe { &(&*anchor)[..] })) { + Ok(p) => p, + Err(_) => return false, + }; + + // The witness contains the incremental tree witness information, in a + // weird serialized format. + let mut witness = unsafe { &(&*witness)[..] }; + + // Skip the first byte, which should be "32" to signify the length of + // the following vector of Pedersen hashes. + assert_eq!(witness[0], SAPLING_TREE_DEPTH as u8); + witness = &witness[1..]; + + // Begin to construct the authentication path + let mut auth_path = vec![None; SAPLING_TREE_DEPTH]; + + // The vector works in reverse + for i in (0..SAPLING_TREE_DEPTH).rev() { + // skip length of inner vector + assert_eq!(witness[0], 32); // the length of a pedersen hash + witness = &witness[1..]; + + // Grab the sibling node at this depth in the tree + let mut sibling = [0u8; 32]; + sibling.copy_from_slice(&witness[0..32]); + witness = &witness[32..]; + + // Sibling node should be an element of Fr + let sibling = match Fr::from_repr(read_le(&sibling)) { + Ok(p) => p, + Err(_) => return false, + }; + + // Set the value in the auth path; we put false here + // for now (signifying the position bit) which we'll + // fill in later. + auth_path[i] = Some((sibling, false)); + } + + // Read the position from the witness + let mut position = witness + .read_u64::() + .expect("should have had index at the end"); + + // Let's compute the nullifier while we have the position + let note = sapling_crypto::primitives::Note { + value: value, + g_d: diversifier + .g_d::(&JUBJUB) + .expect("was a valid diversifier before"), + pk_d: payment_address.pk_d.clone(), + r: rcm, + }; + + let nullifier = note.nf(&viewing_key, position, &JUBJUB); + + // Given the position, let's finish constructing the authentication + // path + for i in 0..SAPLING_TREE_DEPTH { + auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1); + + position >>= 1; + } + + // The witness should be empty now; if it wasn't, the caller would + // have provided more information than they should have, indicating + // a bug downstream + assert_eq!(witness.len(), 0); + + // We now have the full witness for our circuit + let instance = sapling_crypto::circuit::sapling::Spend { + params: &*JUBJUB, + value_commitment: Some(value_commitment.clone()), + proof_generation_key: Some(proof_generation_key), + payment_address: Some(payment_address), + commitment_randomness: Some(rcm), + ar: Some(ar), + auth_path: auth_path, + anchor: Some(anchor), + }; + + // Create proof + let proof = create_random_proof( + instance, + unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(), + &mut rng, + ).expect("proving should not fail"); + + // Try to verify the proof: + // Construct public input for circuit + let mut public_input = [Fr::zero(); 7]; + { + let (x, y) = rk.0.into_xy(); + public_input[0] = x; + public_input[1] = y; + } + { + let (x, y) = value_commitment.cm(&JUBJUB).into_xy(); + public_input[2] = x; + public_input[3] = y; + } + public_input[4] = anchor; + + // Add the nullifier through multiscalar packing + { + let nullifier = multipack::bytes_to_bits_le(&nullifier); + let nullifier = multipack::compute_multipacking::(&nullifier); + + assert_eq!(nullifier.len(), 2); + + public_input[5] = nullifier[0]; + public_input[6] = nullifier[1]; + } + + // Verify the proof + match verify_proof( + unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), + &proof, + &public_input[..], + ) { + // No error, and proof verification successful + Ok(true) => {} + + // Any other case + _ => { + return false; + } + } + + // Compute value commitment + let value_commitment: edwards::Point = value_commitment.cm(&JUBJUB).into(); + + // Accumulate the value commitment in the context + { + let mut tmp = value_commitment.clone(); + tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB); + + // Update the context + unsafe { &mut *ctx }.bvk = tmp; + } + + // Write value commitment to caller + value_commitment + .write(&mut unsafe { &mut *cv }[..]) + .expect("should be able to serialize cv"); + + // Write proof out to caller + proof + .write(&mut (unsafe { &mut *zkproof })[..]) + .expect("should be able to serialize a proof"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext { + let ctx = Box::new(SaplingProvingContext { + bsk: Fs::zero(), + bvk: edwards::Point::zero(), + }); + + Box::into_raw(ctx) +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) { + drop(unsafe { Box::from_raw(ctx) }); +} + +#[no_mangle] +pub extern "system" fn librustzcash_zip32_xsk_master( + seed: *const c_uchar, + seedlen: size_t, + xsk_master: *mut [c_uchar; 169], +) { + let seed = unsafe { std::slice::from_raw_parts(seed, seedlen) }; + + let xsk = zip32::ExtendedSpendingKey::master(seed); + + xsk.write(&mut (unsafe { &mut *xsk_master })[..]) + .expect("should be able to serialize an ExtendedSpendingKey"); +} + +#[no_mangle] +pub extern "system" fn librustzcash_zip32_xsk_derive( + xsk_parent: *const [c_uchar; 169], + i: uint32_t, + xsk_i: *mut [c_uchar; 169], +) { + let xsk_parent = zip32::ExtendedSpendingKey::read(&unsafe { *xsk_parent }[..]) + .expect("valid ExtendedSpendingKey"); + let i = zip32::ChildIndex::from_index(i); + + let xsk = xsk_parent.derive_child(i); + + xsk.write(&mut (unsafe { &mut *xsk_i })[..]) + .expect("should be able to serialize an ExtendedSpendingKey"); +} + +#[no_mangle] +pub extern "system" fn librustzcash_zip32_xfvk_derive( + xfvk_parent: *const [c_uchar; 169], + i: uint32_t, + xfvk_i: *mut [c_uchar; 169], +) -> bool { + let xfvk_parent = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk_parent }[..]) + .expect("valid ExtendedFullViewingKey"); + let i = zip32::ChildIndex::from_index(i); + + let xfvk = match xfvk_parent.derive_child(i) { + Ok(xfvk) => xfvk, + Err(_) => return false, + }; + + xfvk.write(&mut (unsafe { &mut *xfvk_i })[..]) + .expect("should be able to serialize an ExtendedFullViewingKey"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_zip32_xfvk_address( + xfvk: *const [c_uchar; 169], + j: *const [c_uchar; 11], + j_ret: *mut [c_uchar; 11], + addr_ret: *mut [c_uchar; 43], +) -> bool { + let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..]) + .expect("valid ExtendedFullViewingKey"); + let j = zip32::DiversifierIndex(unsafe { *j }); + + let addr = match xfvk.address(j) { + Ok(addr) => addr, + Err(_) => return false, + }; + + let j_ret = unsafe { &mut *j_ret }; + let addr_ret = unsafe { &mut *addr_ret }; + + j_ret.copy_from_slice(&(addr.0).0); + addr_ret + .get_mut(..11) + .unwrap() + .copy_from_slice(&addr.1.diversifier.0); + addr.1 + .pk_d + .write(addr_ret.get_mut(11..).unwrap()) + .expect("should be able to serialize a PaymentAddress"); + + true +} diff --git a/src/rustzcash/src/tests/key_agreement.rs b/src/rust/librustzcash/src/tests/key_agreement.rs similarity index 81% rename from src/rustzcash/src/tests/key_agreement.rs rename to src/rust/librustzcash/src/tests/key_agreement.rs index c4f56a139..01657d14b 100644 --- a/src/rustzcash/src/tests/key_agreement.rs +++ b/src/rust/librustzcash/src/tests/key_agreement.rs @@ -1,10 +1,10 @@ -use ff::{PrimeField, PrimeFieldRepr}; use pairing::bls12_381::Bls12; -use rand_core::{OsRng, RngCore}; -use zcash_primitives::jubjub::{edwards, JubjubBls12}; -use zcash_primitives::primitives::{Diversifier, ViewingKey}; +use pairing::{PrimeField, PrimeFieldRepr}; +use rand::{OsRng, Rng}; +use sapling_crypto::jubjub::{edwards, JubjubBls12}; +use sapling_crypto::primitives::{Diversifier, ViewingKey}; -use crate::{ +use { librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, librustzcash_sapling_ka_derivepublic, }; @@ -12,7 +12,7 @@ use crate::{ #[test] fn test_key_agreement() { let params = JubjubBls12::new(); - let mut rng = OsRng; + let mut rng = OsRng::new().unwrap(); // Create random viewing key let vk = ViewingKey:: { @@ -22,9 +22,7 @@ fn test_key_agreement() { // Create a random address with the viewing key let addr = loop { - let mut d = [0; 11]; - rng.fill_bytes(&mut d); - match vk.to_payment_address(Diversifier(d), ¶ms) { + match vk.into_payment_address(Diversifier(rng.gen()), ¶ms) { Some(a) => break a, None => {} } @@ -46,7 +44,7 @@ fn test_key_agreement() { // Serialize pk_d for the call to librustzcash_sapling_ka_agree let mut addr_pk_d = [0u8; 32]; - addr.pk_d().write(&mut addr_pk_d[..]).unwrap(); + addr.pk_d.write(&mut addr_pk_d[..]).unwrap(); assert!(librustzcash_sapling_ka_agree( &addr_pk_d, @@ -58,7 +56,7 @@ fn test_key_agreement() { // using the diversifier and esk. let mut epk = [0u8; 32]; assert!(librustzcash_sapling_ka_derivepublic( - &addr.diversifier().0, + &addr.diversifier.0, &esk, &mut epk )); diff --git a/src/rustzcash/src/tests/key_components.rs b/src/rust/librustzcash/src/tests/key_components.rs similarity index 90% rename from src/rustzcash/src/tests/key_components.rs rename to src/rust/librustzcash/src/tests/key_components.rs index 5975e5eb1..d63c4d480 100644 --- a/src/rustzcash/src/tests/key_components.rs +++ b/src/rust/librustzcash/src/tests/key_components.rs @@ -1,13 +1,12 @@ -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::Bls12; -use zcash_primitives::{ +use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr}; +use sapling_crypto::{ jubjub::{fs::FsRepr, FixedGenerators, JubjubEngine, JubjubParams}, primitives::{Diversifier, ProofGenerationKey}, }; use super::JUBJUB; -use crate::{ +use { librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk, librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk, }; @@ -28,8 +27,6 @@ fn key_components() { note_v: u64, note_r: [u8; 32], note_cm: [u8; 32], - note_pos: u64, - note_nf: [u8; 32], }; // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py @@ -89,12 +86,6 @@ fn key_components() { 0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2, 0xdd, 0x07, 0x64, 0x39, ], - note_pos: 0, - note_nf: [ - 0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86, - 0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27, - 0x47, 0xab, 0x40, 0x63, - ], }, TestVector { sk: [ @@ -151,12 +142,6 @@ fn key_components() { 0x89, 0xe1, 0x0e, 0x26, 0x6b, 0xcf, 0xa3, 0x1c, 0x31, 0xb2, 0x9a, 0x53, 0xae, 0x72, 0xca, 0xd4, 0x69, 0x50, ], - note_pos: 763714296, - note_nf: [ - 0x67, 0x9e, 0xb0, 0xc3, 0xa7, 0x57, 0xe2, 0xae, 0x83, 0xcd, 0xb4, 0x2a, 0x1a, 0xb2, - 0x59, 0xd7, 0x83, 0x88, 0x31, 0x54, 0x19, 0xad, 0xc7, 0x1d, 0x2e, 0x37, 0x63, 0x17, - 0x4c, 0x2e, 0x9d, 0x93, - ], }, TestVector { sk: [ @@ -213,12 +198,6 @@ fn key_components() { 0xb7, 0x40, 0x82, 0x96, 0x66, 0x17, 0x70, 0xb1, 0x01, 0xb0, 0xaa, 0x87, 0x83, 0x9f, 0x4e, 0x55, 0xf1, 0x51, ], - note_pos: 1527428592, - note_nf: [ - 0xe9, 0x8f, 0x6a, 0x8f, 0x34, 0xff, 0x49, 0x80, 0x59, 0xb3, 0xc7, 0x31, 0xb9, 0x1f, - 0x45, 0x11, 0x08, 0xc4, 0x95, 0x4d, 0x91, 0x94, 0x84, 0x36, 0x1c, 0xf9, 0xb4, 0x8f, - 0x59, 0xae, 0x1d, 0x14, - ], }, TestVector { sk: [ @@ -275,12 +254,6 @@ fn key_components() { 0xbd, 0x10, 0x5d, 0x88, 0x39, 0x21, 0x2e, 0x0d, 0x16, 0x44, 0xb9, 0xd5, 0x5c, 0xaa, 0x60, 0xd1, 0x9b, 0x6c, ], - note_pos: 2291142888, - note_nf: [ - 0x55, 0x47, 0xaa, 0x12, 0xff, 0x80, 0xa6, 0xb3, 0x30, 0x4e, 0x3b, 0x05, 0x86, 0x56, - 0x47, 0x2a, 0xbd, 0x2c, 0x81, 0x83, 0xb5, 0x9d, 0x07, 0x37, 0xb9, 0x3c, 0xee, 0x75, - 0x8b, 0xec, 0x47, 0xa1, - ], }, TestVector { sk: [ @@ -337,12 +310,6 @@ fn key_components() { 0xcf, 0x1e, 0x67, 0x15, 0xbf, 0xe7, 0x0b, 0x63, 0x2d, 0x04, 0x4b, 0x26, 0xfb, 0x2b, 0xc7, 0x1b, 0x7f, 0x36, ], - note_pos: 3054857184, - note_nf: [ - 0x8a, 0x9a, 0xbd, 0xa3, 0xd4, 0xef, 0x85, 0xca, 0xf2, 0x2b, 0xfa, 0xf2, 0xc4, 0x8f, - 0x62, 0x38, 0x2a, 0x73, 0xa1, 0x62, 0x4e, 0xb8, 0xeb, 0x2b, 0xd0, 0x0d, 0x27, 0x03, - 0x01, 0xbf, 0x3d, 0x13, - ], }, TestVector { sk: [ @@ -399,12 +366,6 @@ fn key_components() { 0x1d, 0x74, 0xc5, 0xbc, 0xf2, 0xe1, 0xef, 0x95, 0x66, 0x90, 0x44, 0x73, 0x01, 0x69, 0xde, 0x1a, 0x5b, 0x4c, ], - note_pos: 3818571480, - note_nf: [ - 0x33, 0x2a, 0xd9, 0x9e, 0xb9, 0xe9, 0x77, 0xeb, 0x62, 0x7a, 0x12, 0x2d, 0xbf, 0xb2, - 0xf2, 0x5f, 0xe5, 0x88, 0xe5, 0x97, 0x75, 0x3e, 0xc5, 0x58, 0x0f, 0xf2, 0xbe, 0x20, - 0xb6, 0xc9, 0xa7, 0xe1, - ], }, TestVector { sk: [ @@ -461,12 +422,6 @@ fn key_components() { 0x90, 0xb6, 0xe0, 0xf2, 0xf4, 0xbf, 0x4e, 0xc4, 0xa0, 0xdb, 0x5b, 0xbc, 0xcb, 0x5b, 0x78, 0x3a, 0x1e, 0x55, ], - note_pos: 287318480, - note_nf: [ - 0xfc, 0x74, 0xcd, 0x0e, 0x4b, 0xe0, 0x49, 0x57, 0xb1, 0x96, 0xcf, 0x87, 0x34, 0xae, - 0x99, 0x23, 0x96, 0xaf, 0x4c, 0xfa, 0x8f, 0xec, 0xbb, 0x86, 0xf9, 0x61, 0xe6, 0xb4, - 0x07, 0xd5, 0x1e, 0x11, - ], }, TestVector { sk: [ @@ -523,12 +478,6 @@ fn key_components() { 0x60, 0xa0, 0x06, 0xf8, 0x2b, 0xb7, 0xad, 0xcd, 0x75, 0x22, 0x3f, 0xa8, 0x59, 0x36, 0xf7, 0x8c, 0x2b, 0x23, ], - note_pos: 1051032776, - note_nf: [ - 0xd2, 0xe8, 0x87, 0xbd, 0x85, 0x4a, 0x80, 0x2b, 0xce, 0x85, 0x70, 0x53, 0x02, 0x0f, - 0x5d, 0x3e, 0x7c, 0x8a, 0xe5, 0x26, 0x7c, 0x5b, 0x65, 0x83, 0xb3, 0xd2, 0x12, 0xcc, - 0x8b, 0xb6, 0x98, 0x90, - ], }, TestVector { sk: [ @@ -585,12 +534,6 @@ fn key_components() { 0x23, 0x36, 0xc2, 0xa0, 0x5a, 0x08, 0x03, 0x23, 0x9b, 0x5b, 0x88, 0xfd, 0x92, 0x07, 0x8f, 0xea, 0x4d, 0x04, ], - note_pos: 1814747072, - note_nf: [ - 0xa8, 0x2f, 0x17, 0x50, 0xcc, 0x5b, 0x2b, 0xee, 0x64, 0x9a, 0x36, 0x5c, 0x04, 0x20, - 0xed, 0x87, 0x07, 0x5b, 0x88, 0x71, 0xfd, 0xa4, 0xa7, 0xf5, 0x84, 0x0d, 0x6b, 0xbe, - 0xb1, 0x7c, 0xd6, 0x20, - ], }, TestVector { sk: [ @@ -647,12 +590,6 @@ fn key_components() { 0x64, 0x41, 0x9b, 0x0e, 0x55, 0x0a, 0xbb, 0xcb, 0x8e, 0x2b, 0xcb, 0xda, 0x8b, 0x63, 0xe4, 0x1d, 0xeb, 0x37, ], - note_pos: 2578461368, - note_nf: [ - 0x65, 0x36, 0x74, 0x87, 0x3b, 0x3c, 0x67, 0x0c, 0x58, 0x85, 0x84, 0x73, 0xe7, 0xfe, - 0x72, 0x19, 0x72, 0xfb, 0x96, 0xe2, 0x15, 0xb8, 0x73, 0x77, 0xa1, 0x7c, 0xa3, 0x71, - 0x0d, 0x93, 0xc9, 0xe9, - ], }, ]; @@ -678,7 +615,7 @@ fn key_components() { } let pgk = ProofGenerationKey { ak, nsk }; - let fvk = pgk.to_viewing_key(&JUBJUB); + let fvk = pgk.into_viewing_key(&JUBJUB); { let mut vec = Vec::new(); fvk.nk.write(&mut vec).unwrap(); @@ -704,10 +641,10 @@ fn key_components() { let diversifier = Diversifier(tv.default_d); assert!(librustzcash_check_diversifier(&tv.default_d)); - let addr = fvk.to_payment_address(diversifier, &JUBJUB).unwrap(); + let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap(); { let mut vec = Vec::new(); - addr.pk_d().write(&mut vec).unwrap(); + addr.pk_d.write(&mut vec).unwrap(); assert_eq!(&vec, &tv.default_pk_d); } { @@ -725,7 +662,5 @@ fn key_components() { note.cm(&JUBJUB).into_repr().write_le(&mut vec).unwrap(); assert_eq!(&vec, &tv.note_cm); } - - assert_eq!(note.nf(&fvk, tv.note_pos, &JUBJUB), tv.note_nf); } } diff --git a/src/rustzcash/src/tests/mod.rs b/src/rust/librustzcash/src/tests/mod.rs similarity index 97% rename from src/rustzcash/src/tests/mod.rs rename to src/rust/librustzcash/src/tests/mod.rs index bbaee0abb..a8cdcb794 100644 --- a/src/rustzcash/src/tests/mod.rs +++ b/src/rust/librustzcash/src/tests/mod.rs @@ -1,10 +1,9 @@ -use zcash_primitives::jubjub::{FixedGenerators, JubjubParams}; +use sapling_crypto::jubjub::{FixedGenerators, JubjubParams}; use super::JUBJUB; mod key_agreement; mod key_components; -mod mmr; mod notes; mod signatures; diff --git a/src/rustzcash/src/tests/notes.rs b/src/rust/librustzcash/src/tests/notes.rs similarity index 99% rename from src/rustzcash/src/tests/notes.rs rename to src/rust/librustzcash/src/tests/notes.rs index 6b48b5ff8..6f8c0f589 100644 --- a/src/rustzcash/src/tests/notes.rs +++ b/src/rust/librustzcash/src/tests/notes.rs @@ -1,5 +1,5 @@ -use crate::librustzcash_sapling_compute_cm; -use crate::librustzcash_sapling_compute_nf; +use librustzcash_sapling_compute_cm; +use librustzcash_sapling_compute_nf; #[test] fn notes() { diff --git a/src/rustzcash/src/tests/signatures.rs b/src/rust/librustzcash/src/tests/signatures.rs similarity index 99% rename from src/rustzcash/src/tests/signatures.rs rename to src/rust/librustzcash/src/tests/signatures.rs index 68ac7997c..23fc75bc8 100644 --- a/src/rustzcash/src/tests/signatures.rs +++ b/src/rust/librustzcash/src/tests/signatures.rs @@ -1,7 +1,8 @@ -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::Bls12; -use zcash_primitives::jubjub::{FixedGenerators, JubjubEngine}; -use zcash_primitives::redjubjub::{PrivateKey, PublicKey, Signature}; +use pairing::{bls12_381::Bls12, PrimeField, PrimeFieldRepr}; +use sapling_crypto::{ + jubjub::{FixedGenerators, JubjubEngine}, + redjubjub::{PrivateKey, PublicKey, Signature}, +}; use super::JUBJUB; diff --git a/src/rust/pairing/.gitignore b/src/rust/pairing/.gitignore new file mode 100644 index 000000000..4308d8220 --- /dev/null +++ b/src/rust/pairing/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/src/rust/pairing/COPYRIGHT b/src/rust/pairing/COPYRIGHT new file mode 100644 index 000000000..c3876a4d7 --- /dev/null +++ b/src/rust/pairing/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "pairing" library are retained by their contributors. No +copyright assignment is required to contribute to the "pairing" library. + +The "pairing" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/pairing/Cargo.toml b/src/rust/pairing/Cargo.toml new file mode 100644 index 000000000..dedce802e --- /dev/null +++ b/src/rust/pairing/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "pairing" + +# Remember to change version string in README.md. +version = "0.14.2" +authors = ["Sean Bowe "] +license = "MIT/Apache-2.0" + +description = "Pairing-friendly elliptic curve library" +documentation = "https://docs.rs/pairing/" +homepage = "https://github.com/ebfull/pairing" +repository = "https://github.com/ebfull/pairing" + +[dependencies] +rand = "0.4" +byteorder = "1" + +[features] +unstable-features = ["expose-arith"] +expose-arith = [] +u128-support = [] +default = [] diff --git a/src/rust/pairing/LICENSE-APACHE b/src/rust/pairing/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/src/rust/pairing/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/rust/pairing/LICENSE-MIT b/src/rust/pairing/LICENSE-MIT new file mode 100644 index 000000000..31aa79387 --- /dev/null +++ b/src/rust/pairing/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/src/rust/pairing/README.md b/src/rust/pairing/README.md new file mode 100644 index 000000000..d71d0c512 --- /dev/null +++ b/src/rust/pairing/README.md @@ -0,0 +1,35 @@ +# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # + +This is a Rust crate for using pairing-friendly elliptic curves. Currently, only the [BLS12-381](https://z.cash/blog/new-snark-curve.html) construction is implemented. + +## [Documentation](https://docs.rs/pairing/) + +Bring the `pairing` crate into your project just as you normally would. + +If you're using a supported platform and the nightly Rust compiler, you can enable the `u128-support` feature for faster arithmetic. + +```toml +[dependencies.pairing] +version = "0.14" +features = ["u128-support"] +``` + +## Security Warnings + +This library does not make any guarantees about constant-time operations, memory access patterns, or resistance to side-channel attacks. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/pairing/benches/bls12_381/ec.rs b/src/rust/pairing/benches/bls12_381/ec.rs new file mode 100644 index 000000000..cbd059015 --- /dev/null +++ b/src/rust/pairing/benches/bls12_381/ec.rs @@ -0,0 +1,127 @@ +mod g1 { + use rand::{Rand, SeedableRng, XorShiftRng}; + + use pairing::bls12_381::*; + use pairing::CurveProjective; + + #[bench] + fn bench_g1_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1, Fr)> = (0..SAMPLES) + .map(|_| (G1::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } + + #[bench] + fn bench_g1_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1, G1)> = (0..SAMPLES) + .map(|_| (G1::rand(&mut rng), G1::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } + + #[bench] + fn bench_g1_add_assign_mixed(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1, G1Affine)> = (0..SAMPLES) + .map(|_| (G1::rand(&mut rng), G1::rand(&mut rng).into())) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign_mixed(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } +} + +mod g2 { + use rand::{Rand, SeedableRng, XorShiftRng}; + + use pairing::bls12_381::*; + use pairing::CurveProjective; + + #[bench] + fn bench_g2_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G2, Fr)> = (0..SAMPLES) + .map(|_| (G2::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } + + #[bench] + fn bench_g2_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G2, G2)> = (0..SAMPLES) + .map(|_| (G2::rand(&mut rng), G2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } + + #[bench] + fn bench_g2_add_assign_mixed(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G2, G2Affine)> = (0..SAMPLES) + .map(|_| (G2::rand(&mut rng), G2::rand(&mut rng).into())) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign_mixed(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); + } +} diff --git a/src/rust/pairing/benches/bls12_381/fq.rs b/src/rust/pairing/benches/bls12_381/fq.rs new file mode 100644 index 000000000..af4dba4f8 --- /dev/null +++ b/src/rust/pairing/benches/bls12_381/fq.rs @@ -0,0 +1,268 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::{Field, PrimeField, PrimeFieldRepr, SqrtField}; + +#[bench] +fn bench_fq_repr_add_nocarry(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) + .map(|_| { + let mut tmp1 = FqRepr::rand(&mut rng); + let mut tmp2 = FqRepr::rand(&mut rng); + // Shave a few bits off to avoid overflow. + for _ in 0..3 { + tmp1.div2(); + tmp2.div2(); + } + (tmp1, tmp2) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_nocarry(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_repr_sub_noborrow(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(FqRepr, FqRepr)> = (0..SAMPLES) + .map(|_| { + let tmp1 = FqRepr::rand(&mut rng); + let mut tmp2 = tmp1; + // Ensure tmp2 is smaller than tmp1. + for _ in 0..10 { + tmp2.div2(); + } + (tmp1, tmp2) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_noborrow(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_repr_num_bits(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].num_bits(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_repr_mul2(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.mul2(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_repr_div2(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FqRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.div2(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq, Fq)> = (0..SAMPLES) + .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_sub_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq, Fq)> = (0..SAMPLES) + .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq, Fq)> = (0..SAMPLES) + .map(|_| (Fq::rand(&mut rng), Fq::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_square(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.square(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_inverse(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].inverse() + }); +} + +#[bench] +fn bench_fq_negate(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.negate(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq_sqrt(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| { + let mut tmp = Fq::rand(&mut rng); + tmp.square(); + tmp + }) + .collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].sqrt() + }); +} + +#[bench] +fn bench_fq_into_repr(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].into_repr() + }); +} + +#[bench] +fn bench_fq_from_repr(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| Fq::rand(&mut rng).into_repr()) + .collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + Fq::from_repr(v[count]) + }); +} diff --git a/src/rust/pairing/benches/bls12_381/fq12.rs b/src/rust/pairing/benches/bls12_381/fq12.rs new file mode 100644 index 000000000..226b850dc --- /dev/null +++ b/src/rust/pairing/benches/bls12_381/fq12.rs @@ -0,0 +1,94 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::Field; + +#[bench] +fn bench_fq12_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) + .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq12_sub_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) + .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq12_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq12, Fq12)> = (0..SAMPLES) + .map(|_| (Fq12::rand(&mut rng), Fq12::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq12_squaring(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.square(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq12_inverse(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq12::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].inverse(); + count = (count + 1) % SAMPLES; + tmp + }); +} diff --git a/src/rust/pairing/benches/bls12_381/fq2.rs b/src/rust/pairing/benches/bls12_381/fq2.rs new file mode 100644 index 000000000..ec26e98a1 --- /dev/null +++ b/src/rust/pairing/benches/bls12_381/fq2.rs @@ -0,0 +1,110 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::{Field, SqrtField}; + +#[bench] +fn bench_fq2_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) + .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_sub_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) + .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fq2, Fq2)> = (0..SAMPLES) + .map(|_| (Fq2::rand(&mut rng), Fq2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_squaring(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.square(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_inverse(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].inverse(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fq2_sqrt(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fq2::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].sqrt(); + count = (count + 1) % SAMPLES; + tmp + }); +} diff --git a/src/rust/pairing/benches/bls12_381/fr.rs b/src/rust/pairing/benches/bls12_381/fr.rs new file mode 100644 index 000000000..7278629ea --- /dev/null +++ b/src/rust/pairing/benches/bls12_381/fr.rs @@ -0,0 +1,268 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::{Field, PrimeField, PrimeFieldRepr, SqrtField}; + +#[bench] +fn bench_fr_repr_add_nocarry(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) + .map(|_| { + let mut tmp1 = FrRepr::rand(&mut rng); + let mut tmp2 = FrRepr::rand(&mut rng); + // Shave a few bits off to avoid overflow. + for _ in 0..3 { + tmp1.div2(); + tmp2.div2(); + } + (tmp1, tmp2) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_nocarry(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_repr_sub_noborrow(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(FrRepr, FrRepr)> = (0..SAMPLES) + .map(|_| { + let tmp1 = FrRepr::rand(&mut rng); + let mut tmp2 = tmp1; + // Ensure tmp2 is smaller than tmp1. + for _ in 0..10 { + tmp2.div2(); + } + (tmp1, tmp2) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_noborrow(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_repr_num_bits(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = v[count].num_bits(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_repr_mul2(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.mul2(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_repr_div2(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| FrRepr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.div2(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_add_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fr, Fr)> = (0..SAMPLES) + .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.add_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_sub_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fr, Fr)> = (0..SAMPLES) + .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.sub_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_mul_assign(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(Fr, Fr)> = (0..SAMPLES) + .map(|_| (Fr::rand(&mut rng), Fr::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count].0; + tmp.mul_assign(&v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_square(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.square(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_inverse(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].inverse() + }); +} + +#[bench] +fn bench_fr_negate(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let mut tmp = v[count]; + tmp.negate(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_fr_sqrt(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| { + let mut tmp = Fr::rand(&mut rng); + tmp.square(); + tmp + }) + .collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].sqrt() + }); +} + +#[bench] +fn bench_fr_into_repr(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| Fr::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + v[count].into_repr() + }); +} + +#[bench] +fn bench_fr_from_repr(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| Fr::rand(&mut rng).into_repr()) + .collect(); + + let mut count = 0; + b.iter(|| { + count = (count + 1) % SAMPLES; + Fr::from_repr(v[count]) + }); +} diff --git a/src/rust/pairing/benches/bls12_381/mod.rs b/src/rust/pairing/benches/bls12_381/mod.rs new file mode 100644 index 000000000..9b46c859b --- /dev/null +++ b/src/rust/pairing/benches/bls12_381/mod.rs @@ -0,0 +1,107 @@ +mod ec; +mod fq; +mod fq12; +mod fq2; +mod fr; + +use rand::{Rand, SeedableRng, XorShiftRng}; + +use pairing::bls12_381::*; +use pairing::{CurveAffine, Engine}; + +#[bench] +fn bench_pairing_g1_preparation(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| G1::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = G1Affine::from(v[count]).prepare(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_pairing_g2_preparation(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES).map(|_| G2::rand(&mut rng)).collect(); + + let mut count = 0; + b.iter(|| { + let tmp = G2Affine::from(v[count]).prepare(); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_pairing_miller_loop(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1Prepared, G2Prepared)> = (0..SAMPLES) + .map(|_| { + ( + G1Affine::from(G1::rand(&mut rng)).prepare(), + G2Affine::from(G2::rand(&mut rng)).prepare(), + ) + }) + .collect(); + + let mut count = 0; + b.iter(|| { + let tmp = Bls12::miller_loop(&[(&v[count].0, &v[count].1)]); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_pairing_final_exponentiation(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec = (0..SAMPLES) + .map(|_| { + ( + G1Affine::from(G1::rand(&mut rng)).prepare(), + G2Affine::from(G2::rand(&mut rng)).prepare(), + ) + }) + .map(|(ref p, ref q)| Bls12::miller_loop(&[(p, q)])) + .collect(); + + let mut count = 0; + b.iter(|| { + let tmp = Bls12::final_exponentiation(&v[count]); + count = (count + 1) % SAMPLES; + tmp + }); +} + +#[bench] +fn bench_pairing_full(b: &mut ::test::Bencher) { + const SAMPLES: usize = 1000; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let v: Vec<(G1, G2)> = (0..SAMPLES) + .map(|_| (G1::rand(&mut rng), G2::rand(&mut rng))) + .collect(); + + let mut count = 0; + b.iter(|| { + let tmp = Bls12::pairing(v[count].0, v[count].1); + count = (count + 1) % SAMPLES; + tmp + }); +} diff --git a/src/rust/pairing/benches/pairing_benches.rs b/src/rust/pairing/benches/pairing_benches.rs new file mode 100644 index 000000000..424c4e715 --- /dev/null +++ b/src/rust/pairing/benches/pairing_benches.rs @@ -0,0 +1,7 @@ +#![feature(test)] + +extern crate pairing; +extern crate rand; +extern crate test; + +mod bls12_381; diff --git a/src/rust/pairing/src/bls12_381/README.md b/src/rust/pairing/src/bls12_381/README.md new file mode 100644 index 000000000..d3811a142 --- /dev/null +++ b/src/rust/pairing/src/bls12_381/README.md @@ -0,0 +1,71 @@ +# BLS12-381 + +This is an implementation of the BLS12-381 pairing-friendly elliptic curve construction. + +## BLS12 Parameterization + +BLS12 curves are parameterized by a value *x* such that the base field modulus *q* and subgroup *r* can be computed by: + +* q = (x - 1)2 ((x4 - x2 + 1) / 3) + x +* r = (x4 - x2 + 1) + +Given primes *q* and *r* parameterized as above, we can easily construct an elliptic curve over the prime field F*q* which contains a subgroup of order *r* such that *r* | (*q*12 - 1), giving it an embedding degree of 12. Instantiating its sextic twist over an extension field Fq2 gives rise to an efficient bilinear pairing function between elements of the order *r* subgroups of either curves, into an order *r* multiplicative subgroup of Fq12. + +In zk-SNARK schemes, we require Fr with large 2n roots of unity for performing efficient fast-fourier transforms. As such, guaranteeing that large 2n | (r - 1), or equivalently that *x* has a large 2n factor, gives rise to BLS12 curves suitable for zk-SNARKs. + +Due to recent research, it is estimated by many that *q* should be approximately 384 bits to target 128-bit security. Conveniently, *r* is approximately 256 bits when *q* is approximately 384 bits, making BLS12 curves ideal for 128-bit security. It also makes them ideal for many zk-SNARK applications, as the scalar field can be used for keying material such as embedded curve constructions. + +Many curves match our descriptions, but we require some extra properties for efficiency purposes: + +* *q* should be smaller than 2383, and *r* should be smaller than 2255, so that the most significant bit is unset when using 64-bit or 32-bit limbs. This allows for cheap reductions. +* Fq12 is typically constructed using towers of extension fields. As a byproduct of [research](https://eprint.iacr.org/2011/465.pdf) for BLS curves of embedding degree 24, we can identify subfamilies of BLS12 curves (for our purposes, where x mod 72 = {16, 64}) that produce efficient extension field towers and twisting isomorphisms. +* We desire *x* of small Hamming weight, to increase the performance of the pairing function. + +## BLS12-381 Instantiation + +The BLS12-381 construction is instantiated by `x = -0xd201000000010000`, which produces the largest `q` and smallest Hamming weight of `x` that meets the above requirements. This produces: + +* q = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` (381 bits) +* r = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` (255 bits) + +Our extension field tower is constructed as follows: + +1. Fq2 is constructed as Fq(u) / (u2 - β) where β = -1. +2. Fq6 is constructed as Fq2(v) / (v3 - ξ) where ξ = u + 1 +3. Fq12 is constructed as Fq6(w) / (w2 - γ) where γ = v + +Now, we instantiate the elliptic curve E(Fq) : y2 = x3 + 4, and the elliptic curve E'(Fq2) : y2 = x3 + 4(u + 1). + +The group G1 is the *r* order subgroup of E, which has cofactor (x - 1)2 / 3. The group G2 is the *r* order subgroup of E', which has cofactor (x8 - 4x7 + 5x6 - 4x4 + 6x3 - 4x2 - 4x + 13) / 9. + +### Generators + +The generators of G1 and G2 are computed by finding the lexicographically smallest valid `x`-coordinate, and its lexicographically smallest `y`-coordinate and scaling it by the cofactor such that the result is not the point at infinity. + +#### G1 + +``` +x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 +y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 +``` + +#### G2 + +``` +x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 +y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 +``` + +### Serialization + +* Fq elements are encoded in big-endian form. They occupy 48 bytes in this form. +* Fq2 elements are encoded in big-endian form, meaning that the Fq element c0 + c1 * u is represented by the Fq element c1 followed by the Fq element c0. This means Fq2 elements occupy 96 bytes in this form. +* The group G1 uses Fq elements for coordinates. The group G2 uses Fq2 elements for coordinates. +* G1 and G2 elements can be encoded in uncompressed form (the x-coordinate followed by the y-coordinate) or in compressed form (just the x-coordinate). G1 elements occupy 96 bytes in uncompressed form, and 48 bytes in compressed form. G2 elements occupy 192 bytes in uncompressed form, and 96 bytes in compressed form. + +The most-significant three bits of a G1 or G2 encoding should be masked away before the coordinate(s) are interpreted. These bits are used to unambiguously represent the underlying element: + +* The most significant bit, when set, indicates that the point is in compressed form. Otherwise, the point is in uncompressed form. +* The second-most significant bit indicates that the point is at infinity. If this bit is set, the remaining bits of the group element's encoding should be set to zero. +* The third-most significant bit is set if (and only if) this point is in compressed form _and_ it is not the point at infinity _and_ its y-coordinate is the lexicographically largest of the two associated with the encoded x-coordinate. + diff --git a/src/rust/pairing/src/bls12_381/ec.rs b/src/rust/pairing/src/bls12_381/ec.rs new file mode 100644 index 000000000..5cd5091ac --- /dev/null +++ b/src/rust/pairing/src/bls12_381/ec.rs @@ -0,0 +1,2026 @@ +macro_rules! curve_impl { + ( + $name:expr, + $projective:ident, + $affine:ident, + $prepared:ident, + $basefield:ident, + $scalarfield:ident, + $uncompressed:ident, + $compressed:ident, + $pairing:ident + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub struct $affine { + pub(crate) x: $basefield, + pub(crate) y: $basefield, + pub(crate) infinity: bool + } + + impl ::std::fmt::Display for $affine + { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + if self.infinity { + write!(f, "{}(Infinity)", $name) + } else { + write!(f, "{}(x={}, y={})", $name, self.x, self.y) + } + } + } + + #[derive(Copy, Clone, Debug, Eq)] + pub struct $projective { + pub(crate) x: $basefield, + pub(crate) y: $basefield, + pub(crate) z: $basefield + } + + impl ::std::fmt::Display for $projective + { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{}", self.into_affine()) + } + } + + impl PartialEq for $projective { + fn eq(&self, other: &$projective) -> bool { + if self.is_zero() { + return other.is_zero(); + } + + if other.is_zero() { + return false; + } + + // The points (X, Y, Z) and (X', Y', Z') + // are equal when (X * Z^2) = (X' * Z'^2) + // and (Y * Z^3) = (Y' * Z'^3). + + let mut z1 = self.z; + z1.square(); + let mut z2 = other.z; + z2.square(); + + let mut tmp1 = self.x; + tmp1.mul_assign(&z2); + + let mut tmp2 = other.x; + tmp2.mul_assign(&z1); + + if tmp1 != tmp2 { + return false; + } + + z1.mul_assign(&self.z); + z2.mul_assign(&other.z); + z2.mul_assign(&self.y); + z1.mul_assign(&other.y); + + if z1 != z2 { + return false; + } + + true + } + } + + impl $affine { + fn mul_bits>(&self, bits: BitIterator) -> $projective { + let mut res = $projective::zero(); + for i in bits { + res.double(); + if i { res.add_assign_mixed(self) } + } + res + } + + /// Attempts to construct an affine point given an x-coordinate. The + /// point is not guaranteed to be in the prime order subgroup. + /// + /// If and only if `greatest` is set will the lexicographically + /// largest y-coordinate be selected. + fn get_point_from_x(x: $basefield, greatest: bool) -> Option<$affine> { + // Compute x^3 + b + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&$affine::get_coeff_b()); + + x3b.sqrt().map(|y| { + let mut negy = y; + negy.negate(); + + $affine { + x: x, + y: if (y < negy) ^ greatest { + y + } else { + negy + }, + infinity: false + } + }) + } + + fn is_on_curve(&self) -> bool { + if self.is_zero() { + true + } else { + // Check that the point is on the curve + let mut y2 = self.y; + y2.square(); + + let mut x3b = self.x; + x3b.square(); + x3b.mul_assign(&self.x); + x3b.add_assign(&Self::get_coeff_b()); + + y2 == x3b + } + } + + fn is_in_correct_subgroup_assuming_on_curve(&self) -> bool { + self.mul($scalarfield::char()).is_zero() + } + } + + impl CurveAffine for $affine { + type Engine = Bls12; + type Scalar = $scalarfield; + type Base = $basefield; + type Prepared = $prepared; + type Projective = $projective; + type Uncompressed = $uncompressed; + type Compressed = $compressed; + type Pair = $pairing; + type PairingResult = Fq12; + + fn zero() -> Self { + $affine { + x: $basefield::zero(), + y: $basefield::one(), + infinity: true + } + } + + fn one() -> Self { + Self::get_generator() + } + + fn is_zero(&self) -> bool { + self.infinity + } + + fn mul::Repr>>(&self, by: S) -> $projective { + let bits = BitIterator::new(by.into()); + self.mul_bits(bits) + } + + fn negate(&mut self) { + if !self.is_zero() { + self.y.negate(); + } + } + + fn prepare(&self) -> Self::Prepared { + $prepared::from_affine(*self) + } + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + self.perform_pairing(other) + } + + fn into_projective(&self) -> $projective { + (*self).into() + } + + } + + impl Rand for $projective { + fn rand(rng: &mut R) -> Self { + loop { + let x = rng.gen(); + let greatest = rng.gen(); + + if let Some(p) = $affine::get_point_from_x(x, greatest) { + let p = p.scale_by_cofactor(); + + if !p.is_zero() { + return p; + } + } + } + } + } + + impl CurveProjective for $projective { + type Engine = Bls12; + type Scalar = $scalarfield; + type Base = $basefield; + type Affine = $affine; + + // The point at infinity is always represented by + // Z = 0. + fn zero() -> Self { + $projective { + x: $basefield::zero(), + y: $basefield::one(), + z: $basefield::zero() + } + } + + fn one() -> Self { + $affine::one().into() + } + + // The point at infinity is always represented by + // Z = 0. + fn is_zero(&self) -> bool { + self.z.is_zero() + } + + fn is_normalized(&self) -> bool { + self.is_zero() || self.z == $basefield::one() + } + + fn batch_normalization(v: &mut [Self]) + { + // Montgomery’s Trick and Fast Implementation of Masked AES + // Genelle, Prouff and Quisquater + // Section 3.2 + + // First pass: compute [a, ab, abc, ...] + let mut prod = Vec::with_capacity(v.len()); + let mut tmp = $basefield::one(); + for g in v.iter_mut() + // Ignore normalized elements + .filter(|g| !g.is_normalized()) + { + tmp.mul_assign(&g.z); + prod.push(tmp); + } + + // Invert `tmp`. + tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. + + // Second pass: iterate backwards to compute inverses + for (g, s) in v.iter_mut() + // Backwards + .rev() + // Ignore normalized elements + .filter(|g| !g.is_normalized()) + // Backwards, skip last element, fill in one for last term. + .zip(prod.into_iter().rev().skip(1).chain(Some($basefield::one()))) + { + // tmp := tmp * g.z; g.z := tmp * s = 1/z + let mut newtmp = tmp; + newtmp.mul_assign(&g.z); + g.z = tmp; + g.z.mul_assign(&s); + tmp = newtmp; + } + + // Perform affine transformations + for g in v.iter_mut() + .filter(|g| !g.is_normalized()) + { + let mut z = g.z; // 1/z + z.square(); // 1/z^2 + g.x.mul_assign(&z); // x/z^2 + z.mul_assign(&g.z); // 1/z^3 + g.y.mul_assign(&z); // y/z^3 + g.z = $basefield::one(); // z = 1 + } + } + + fn double(&mut self) { + if self.is_zero() { + return; + } + + // Other than the point at infinity, no points on E or E' + // can double to equal the point at infinity, as y=0 is + // never true for points on the curve. (-4 and -4u-4 + // are not cubic residue in their respective fields.) + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + // A = X1^2 + let mut a = self.x; + a.square(); + + // B = Y1^2 + let mut b = self.y; + b.square(); + + // C = B^2 + let mut c = b; + c.square(); + + // D = 2*((X1+B)2-A-C) + let mut d = self.x; + d.add_assign(&b); + d.square(); + d.sub_assign(&a); + d.sub_assign(&c); + d.double(); + + // E = 3*A + let mut e = a; + e.double(); + e.add_assign(&a); + + // F = E^2 + let mut f = e; + f.square(); + + // Z3 = 2*Y1*Z1 + self.z.mul_assign(&self.y); + self.z.double(); + + // X3 = F-2*D + self.x = f; + self.x.sub_assign(&d); + self.x.sub_assign(&d); + + // Y3 = E*(D-X3)-8*C + self.y = d; + self.y.sub_assign(&self.x); + self.y.mul_assign(&e); + c.double(); + c.double(); + c.double(); + self.y.sub_assign(&c); + } + + fn add_assign(&mut self, other: &Self) { + if self.is_zero() { + *self = *other; + return; + } + + if other.is_zero() { + return; + } + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + + // Z1Z1 = Z1^2 + let mut z1z1 = self.z; + z1z1.square(); + + // Z2Z2 = Z2^2 + let mut z2z2 = other.z; + z2z2.square(); + + // U1 = X1*Z2Z2 + let mut u1 = self.x; + u1.mul_assign(&z2z2); + + // U2 = X2*Z1Z1 + let mut u2 = other.x; + u2.mul_assign(&z1z1); + + // S1 = Y1*Z2*Z2Z2 + let mut s1 = self.y; + s1.mul_assign(&other.z); + s1.mul_assign(&z2z2); + + // S2 = Y2*Z1*Z1Z1 + let mut s2 = other.y; + s2.mul_assign(&self.z); + s2.mul_assign(&z1z1); + + if u1 == u2 && s1 == s2 { + // The two points are equal, so we double. + self.double(); + } else { + // If we're adding -a and a together, self.z becomes zero as H becomes zero. + + // H = U2-U1 + let mut h = u2; + h.sub_assign(&u1); + + // I = (2*H)^2 + let mut i = h; + i.double(); + i.square(); + + // J = H*I + let mut j = h; + j.mul_assign(&i); + + // r = 2*(S2-S1) + let mut r = s2; + r.sub_assign(&s1); + r.double(); + + // V = U1*I + let mut v = u1; + v.mul_assign(&i); + + // X3 = r^2 - J - 2*V + self.x = r; + self.x.square(); + self.x.sub_assign(&j); + self.x.sub_assign(&v); + self.x.sub_assign(&v); + + // Y3 = r*(V - X3) - 2*S1*J + self.y = v; + self.y.sub_assign(&self.x); + self.y.mul_assign(&r); + s1.mul_assign(&j); // S1 = S1 * J * 2 + s1.double(); + self.y.sub_assign(&s1); + + // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H + self.z.add_assign(&other.z); + self.z.square(); + self.z.sub_assign(&z1z1); + self.z.sub_assign(&z2z2); + self.z.mul_assign(&h); + } + } + + fn add_assign_mixed(&mut self, other: &Self::Affine) { + if other.is_zero() { + return; + } + + if self.is_zero() { + self.x = other.x; + self.y = other.y; + self.z = $basefield::one(); + return; + } + + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl + + // Z1Z1 = Z1^2 + let mut z1z1 = self.z; + z1z1.square(); + + // U2 = X2*Z1Z1 + let mut u2 = other.x; + u2.mul_assign(&z1z1); + + // S2 = Y2*Z1*Z1Z1 + let mut s2 = other.y; + s2.mul_assign(&self.z); + s2.mul_assign(&z1z1); + + if self.x == u2 && self.y == s2 { + // The two points are equal, so we double. + self.double(); + } else { + // If we're adding -a and a together, self.z becomes zero as H becomes zero. + + // H = U2-X1 + let mut h = u2; + h.sub_assign(&self.x); + + // HH = H^2 + let mut hh = h; + hh.square(); + + // I = 4*HH + let mut i = hh; + i.double(); + i.double(); + + // J = H*I + let mut j = h; + j.mul_assign(&i); + + // r = 2*(S2-Y1) + let mut r = s2; + r.sub_assign(&self.y); + r.double(); + + // V = X1*I + let mut v = self.x; + v.mul_assign(&i); + + // X3 = r^2 - J - 2*V + self.x = r; + self.x.square(); + self.x.sub_assign(&j); + self.x.sub_assign(&v); + self.x.sub_assign(&v); + + // Y3 = r*(V-X3)-2*Y1*J + j.mul_assign(&self.y); // J = 2*Y1*J + j.double(); + self.y = v; + self.y.sub_assign(&self.x); + self.y.mul_assign(&r); + self.y.sub_assign(&j); + + // Z3 = (Z1+H)^2-Z1Z1-HH + self.z.add_assign(&h); + self.z.square(); + self.z.sub_assign(&z1z1); + self.z.sub_assign(&hh); + } + } + + fn negate(&mut self) { + if !self.is_zero() { + self.y.negate() + } + } + + fn mul_assign::Repr>>(&mut self, other: S) { + let mut res = Self::zero(); + + let mut found_one = false; + + for i in BitIterator::new(other.into()) + { + if found_one { + res.double(); + } else { + found_one = i; + } + + if i { + res.add_assign(self); + } + } + + *self = res; + } + + fn into_affine(&self) -> $affine { + (*self).into() + } + + fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize { + Self::empirical_recommended_wnaf_for_scalar(scalar) + } + + fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + Self::empirical_recommended_wnaf_for_num_scalars(num_scalars) + } + } + + // The affine point X, Y is represented in the jacobian + // coordinates with Z = 1. + impl From<$affine> for $projective { + fn from(p: $affine) -> $projective { + if p.is_zero() { + $projective::zero() + } else { + $projective { + x: p.x, + y: p.y, + z: $basefield::one() + } + } + } + } + + // The projective point X, Y, Z is represented in the affine + // coordinates as X/Z^2, Y/Z^3. + impl From<$projective> for $affine { + fn from(p: $projective) -> $affine { + if p.is_zero() { + $affine::zero() + } else if p.z == $basefield::one() { + // If Z is one, the point is already normalized. + $affine { + x: p.x, + y: p.y, + infinity: false + } + } else { + // Z is nonzero, so it must have an inverse in a field. + let zinv = p.z.inverse().unwrap(); + let mut zinv_powered = zinv; + zinv_powered.square(); + + // X/Z^2 + let mut x = p.x; + x.mul_assign(&zinv_powered); + + // Y/Z^3 + let mut y = p.y; + zinv_powered.mul_assign(&zinv); + y.mul_assign(&zinv_powered); + + $affine { + x: x, + y: y, + infinity: false + } + } + } + } + } +} + +pub mod g1 { + use super::super::{Bls12, Fq, Fq12, FqRepr, Fr, FrRepr}; + use super::g2::G2Affine; + use rand::{Rand, Rng}; + use std::fmt; + use { + BitIterator, CurveAffine, CurveProjective, EncodedPoint, Engine, Field, GroupDecodingError, + PrimeField, PrimeFieldRepr, SqrtField, + }; + + curve_impl!( + "G1", + G1, + G1Affine, + G1Prepared, + Fq, + Fr, + G1Uncompressed, + G1Compressed, + G2Affine + ); + + #[derive(Copy, Clone)] + pub struct G1Uncompressed([u8; 96]); + + impl AsRef<[u8]> for G1Uncompressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsMut<[u8]> for G1Uncompressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + impl fmt::Debug for G1Uncompressed { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0[..].fmt(formatter) + } + } + + impl EncodedPoint for G1Uncompressed { + type Affine = G1Affine; + + fn empty() -> Self { + G1Uncompressed([0; 96]) + } + fn size() -> usize { + 96 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + if !affine.is_on_curve() { + Err(GroupDecodingError::NotOnCurve) + } else if !affine.is_in_correct_subgroup_assuming_on_curve() { + Err(GroupDecodingError::NotInSubgroup) + } else { + Ok(affine) + } + } + fn into_affine_unchecked(&self) -> Result { + // Create a copy of this representation. + let mut copy = self.0; + + if copy[0] & (1 << 7) != 0 { + // Distinguisher bit is set, but this should be uncompressed! + return Err(GroupDecodingError::UnexpectedCompressionMode); + } + + if copy[0] & (1 << 6) != 0 { + // This is the point at infinity, which means that if we mask away + // the first two bits, the entire representation should consist + // of zeroes. + copy[0] &= 0x3f; + + if copy.iter().all(|b| *b == 0) { + Ok(G1Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + if copy[0] & (1 << 5) != 0 { + // The bit indicating the y-coordinate should be lexicographically + // largest is set, but this is an uncompressed element. + return Err(GroupDecodingError::UnexpectedInformation); + } + + // Unset the three most significant bits. + copy[0] &= 0x1f; + + let mut x = FqRepr([0; 6]); + let mut y = FqRepr([0; 6]); + + { + let mut reader = ©[..]; + + x.read_be(&mut reader).unwrap(); + y.read_be(&mut reader).unwrap(); + } + + Ok(G1Affine { + x: Fq::from_repr(x).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate", e) + })?, + y: Fq::from_repr(y).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("y coordinate", e) + })?, + infinity: false, + }) + } + } + fn from_affine(affine: G1Affine) -> Self { + let mut res = Self::empty(); + + if affine.is_zero() { + // Set the second-most significant bit to indicate this point + // is at infinity. + res.0[0] |= 1 << 6; + } else { + let mut writer = &mut res.0[..]; + + affine.x.into_repr().write_be(&mut writer).unwrap(); + affine.y.into_repr().write_be(&mut writer).unwrap(); + } + + res + } + } + + #[derive(Copy, Clone)] + pub struct G1Compressed([u8; 48]); + + impl AsRef<[u8]> for G1Compressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsMut<[u8]> for G1Compressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + impl fmt::Debug for G1Compressed { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0[..].fmt(formatter) + } + } + + impl EncodedPoint for G1Compressed { + type Affine = G1Affine; + + fn empty() -> Self { + G1Compressed([0; 48]) + } + fn size() -> usize { + 48 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + // NB: Decompression guarantees that it is on the curve already. + + if !affine.is_in_correct_subgroup_assuming_on_curve() { + Err(GroupDecodingError::NotInSubgroup) + } else { + Ok(affine) + } + } + fn into_affine_unchecked(&self) -> Result { + // Create a copy of this representation. + let mut copy = self.0; + + if copy[0] & (1 << 7) == 0 { + // Distinguisher bit isn't set. + return Err(GroupDecodingError::UnexpectedCompressionMode); + } + + if copy[0] & (1 << 6) != 0 { + // This is the point at infinity, which means that if we mask away + // the first two bits, the entire representation should consist + // of zeroes. + copy[0] &= 0x3f; + + if copy.iter().all(|b| *b == 0) { + Ok(G1Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + // Determine if the intended y coordinate must be greater + // lexicographically. + let greatest = copy[0] & (1 << 5) != 0; + + // Unset the three most significant bits. + copy[0] &= 0x1f; + + let mut x = FqRepr([0; 6]); + + { + let mut reader = ©[..]; + + x.read_be(&mut reader).unwrap(); + } + + // Interpret as Fq element. + let x = Fq::from_repr(x) + .map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?; + + G1Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve) + } + } + fn from_affine(affine: G1Affine) -> Self { + let mut res = Self::empty(); + + if affine.is_zero() { + // Set the second-most significant bit to indicate this point + // is at infinity. + res.0[0] |= 1 << 6; + } else { + { + let mut writer = &mut res.0[..]; + + affine.x.into_repr().write_be(&mut writer).unwrap(); + } + + let mut negy = affine.y; + negy.negate(); + + // Set the third most significant bit if the correct y-coordinate + // is lexicographically largest. + if affine.y > negy { + res.0[0] |= 1 << 5; + } + } + + // Set highest bit to distinguish this as a compressed element. + res.0[0] |= 1 << 7; + + res + } + } + + impl G1Affine { + fn scale_by_cofactor(&self) -> G1 { + // G1 cofactor = (x - 1)^2 / 3 = 76329603384216526031706109802092473003 + let cofactor = BitIterator::new([0x8c00aaab0000aaab, 0x396c8c005555e156]); + self.mul_bits(cofactor) + } + + fn get_generator() -> Self { + G1Affine { + x: super::super::fq::G1_GENERATOR_X, + y: super::super::fq::G1_GENERATOR_Y, + infinity: false, + } + } + + fn get_coeff_b() -> Fq { + super::super::fq::B_COEFF + } + + fn perform_pairing(&self, other: &G2Affine) -> Fq12 { + super::super::Bls12::pairing(*self, *other) + } + } + + impl G1 { + fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { + let num_bits = scalar.num_bits() as usize; + + if num_bits >= 130 { + 4 + } else if num_bits >= 34 { + 3 + } else { + 2 + } + } + + fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + const RECOMMENDATIONS: [usize; 12] = + [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; + + let mut ret = 4; + for r in &RECOMMENDATIONS { + if num_scalars > *r { + ret += 1; + } else { + break; + } + } + + ret + } + } + + #[derive(Clone, Debug)] + pub struct G1Prepared(pub(crate) G1Affine); + + impl G1Prepared { + pub fn is_zero(&self) -> bool { + self.0.is_zero() + } + + pub fn from_affine(p: G1Affine) -> Self { + G1Prepared(p) + } + } + + #[test] + fn g1_generator() { + use SqrtField; + + let mut x = Fq::zero(); + let mut i = 0; + loop { + // y^2 = x^3 + b + let mut rhs = x; + rhs.square(); + rhs.mul_assign(&x); + rhs.add_assign(&G1Affine::get_coeff_b()); + + if let Some(y) = rhs.sqrt() { + let yrepr = y.into_repr(); + let mut negy = y; + negy.negate(); + let negyrepr = negy.into_repr(); + + let p = G1Affine { + x: x, + y: if yrepr < negyrepr { y } else { negy }, + infinity: false, + }; + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + + let g1 = p.scale_by_cofactor(); + if !g1.is_zero() { + assert_eq!(i, 4); + let g1 = G1Affine::from(g1); + + assert!(g1.is_in_correct_subgroup_assuming_on_curve()); + + assert_eq!(g1, G1Affine::one()); + break; + } + } + + i += 1; + x.add_assign(&Fq::one()); + } + } + + #[test] + fn g1_test_is_valid() { + // Reject point on isomorphic twist (b = 24) + { + let p = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xc58d887b66c035dc, + 0x10cbfd301d553822, + 0xaf23e064f1131ee5, + 0x9fe83b1b4a5d648d, + 0xf583cc5a508f6a40, + 0xc3ad2aefde0bb13, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x60aa6f9552f03aae, + 0xecd01d5181300d35, + 0x8af1cdb8aa8ce167, + 0xe760f57922998c9d, + 0x953703f5795a39e5, + 0xfe3ae0922df702c, + ])).unwrap(), + infinity: false, + }; + assert!(!p.is_on_curve()); + assert!(p.is_in_correct_subgroup_assuming_on_curve()); + } + + // Reject point on a twist (b = 3) + { + let p = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xee6adf83511e15f5, + 0x92ddd328f27a4ba6, + 0xe305bd1ac65adba7, + 0xea034ee2928b30a8, + 0xbd8833dc7c79a7f7, + 0xe45c9f0c0438675, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x3b450eb1ab7b5dad, + 0xa65cb81e975e8675, + 0xaa548682b21726e5, + 0x753ddf21a2601d20, + 0x532d0b640bd3ff8b, + 0x118d2c543f031102, + ])).unwrap(), + infinity: false, + }; + assert!(!p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + } + + // Reject point in an invalid subgroup + // There is only one r-order subgroup, as r does not divide the cofactor. + { + let p = G1Affine { + x: Fq::from_repr(FqRepr([ + 0x76e1c971c6db8fe8, + 0xe37e1a610eff2f79, + 0x88ae9c499f46f0c0, + 0xf35de9ce0d6b4e84, + 0x265bddd23d1dec54, + 0x12a8778088458308, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x8a22defa0d526256, + 0xc57ca55456fcb9ae, + 0x1ba194e89bab2610, + 0x921beef89d4f29df, + 0x5b6fda44ad85fa78, + 0xed74ab9f302cbe0, + ])).unwrap(), + infinity: false, + }; + assert!(p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + } + } + + #[test] + fn test_g1_addition_correctness() { + let mut p = G1 { + x: Fq::from_repr(FqRepr([ + 0x47fd1f891d6e8bbf, + 0x79a3b0448f31a2aa, + 0x81f3339e5f9968f, + 0x485e77d50a5df10d, + 0x4c6fcac4b55fd479, + 0x86ed4d9906fb064, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xd25ee6461538c65, + 0x9f3bbb2ecd3719b9, + 0xa06fd3f1e540910d, + 0xcefca68333c35288, + 0x570c8005f8573fa6, + 0x152ca696fe034442, + ])).unwrap(), + z: Fq::one(), + }; + + p.add_assign(&G1 { + x: Fq::from_repr(FqRepr([ + 0xeec78f3096213cbf, + 0xa12beb1fea1056e6, + 0xc286c0211c40dd54, + 0x5f44314ec5e3fb03, + 0x24e8538737c6e675, + 0x8abd623a594fba8, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x6b0528f088bb7044, + 0x2fdeb5c82917ff9e, + 0x9a5181f2fac226ad, + 0xd65104c6f95a872a, + 0x1f2998a5a9c61253, + 0xe74846154a9e44, + ])).unwrap(), + z: Fq::one(), + }); + + let p = G1Affine::from(p); + + assert_eq!( + p, + G1Affine { + x: Fq::from_repr(FqRepr([ + 0x6dd3098f22235df, + 0xe865d221c8090260, + 0xeb96bb99fa50779f, + 0xc4f9a52a428e23bb, + 0xd178b28dd4f407ef, + 0x17fb8905e9183c69 + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xd0de9d65292b7710, + 0xf6a05f2bcf1d9ca7, + 0x1040e27012f20b64, + 0xeec8d1a5b7466c58, + 0x4bc362649dce6376, + 0x430cbdc5455b00a + ])).unwrap(), + infinity: false, + } + ); + } + + #[test] + fn test_g1_doubling_correctness() { + let mut p = G1 { + x: Fq::from_repr(FqRepr([ + 0x47fd1f891d6e8bbf, + 0x79a3b0448f31a2aa, + 0x81f3339e5f9968f, + 0x485e77d50a5df10d, + 0x4c6fcac4b55fd479, + 0x86ed4d9906fb064, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xd25ee6461538c65, + 0x9f3bbb2ecd3719b9, + 0xa06fd3f1e540910d, + 0xcefca68333c35288, + 0x570c8005f8573fa6, + 0x152ca696fe034442, + ])).unwrap(), + z: Fq::one(), + }; + + p.double(); + + let p = G1Affine::from(p); + + assert_eq!( + p, + G1Affine { + x: Fq::from_repr(FqRepr([ + 0xf939ddfe0ead7018, + 0x3b03942e732aecb, + 0xce0e9c38fdb11851, + 0x4b914c16687dcde0, + 0x66c8baf177d20533, + 0xaf960cff3d83833 + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x3f0675695f5177a8, + 0x2b6d82ae178a1ba0, + 0x9096380dd8e51b11, + 0x1771a65b60572f4e, + 0x8b547c1313b27555, + 0x135075589a687b1e + ])).unwrap(), + infinity: false, + } + ); + } + + #[test] + fn test_g1_same_y() { + // Test the addition of two points with different x coordinates + // but the same y coordinate. + + // x1 = 128100205326445210408953809171070606737678357140298133325128175840781723996595026100005714405541449960643523234125 + // x2 = 3821408151224848222394078037104966877485040835569514006839342061575586899845797797516352881516922679872117658572470 + // y = 2291134451313223670499022936083127939567618746216464377735567679979105510603740918204953301371880765657042046687078 + + let a = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xea431f2cc38fc94d, + 0x3ad2354a07f5472b, + 0xfe669f133f16c26a, + 0x71ffa8021531705, + 0x7418d484386d267, + 0xd5108d8ff1fbd6, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xa776ccbfe9981766, + 0x255632964ff40f4a, + 0xc09744e650b00499, + 0x520f74773e74c8c3, + 0x484c8fc982008f0, + 0xee2c3d922008cc6, + ])).unwrap(), + infinity: false, + }; + + let b = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xe06cdb156b6356b6, + 0xd9040b2d75448ad9, + 0xe702f14bb0e2aca5, + 0xc6e05201e5f83991, + 0xf7c75910816f207c, + 0x18d4043e78103106, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0xa776ccbfe9981766, + 0x255632964ff40f4a, + 0xc09744e650b00499, + 0x520f74773e74c8c3, + 0x484c8fc982008f0, + 0xee2c3d922008cc6, + ])).unwrap(), + infinity: false, + }; + + // Expected + // x = 52901198670373960614757979459866672334163627229195745167587898707663026648445040826329033206551534205133090753192 + // y = 1711275103908443722918766889652776216989264073722543507596490456144926139887096946237734327757134898380852225872709 + let c = G1Affine { + x: Fq::from_repr(FqRepr([ + 0xef4f05bdd10c8aa8, + 0xad5bf87341a2df9, + 0x81c7424206b78714, + 0x9676ff02ec39c227, + 0x4c12c15d7e55b9f3, + 0x57fd1e317db9bd, + ])).unwrap(), + y: Fq::from_repr(FqRepr([ + 0x1288334016679345, + 0xf955cd68615ff0b5, + 0xa6998dbaa600f18a, + 0x1267d70db51049fb, + 0x4696deb9ab2ba3e7, + 0xb1e4e11177f59d4, + ])).unwrap(), + infinity: false, + }; + + assert!(a.is_on_curve() && a.is_in_correct_subgroup_assuming_on_curve()); + assert!(b.is_on_curve() && b.is_in_correct_subgroup_assuming_on_curve()); + assert!(c.is_on_curve() && c.is_in_correct_subgroup_assuming_on_curve()); + + let mut tmp1 = a.into_projective(); + tmp1.add_assign(&b.into_projective()); + assert_eq!(tmp1.into_affine(), c); + assert_eq!(tmp1, c.into_projective()); + + let mut tmp2 = a.into_projective(); + tmp2.add_assign_mixed(&b); + assert_eq!(tmp2.into_affine(), c); + assert_eq!(tmp2, c.into_projective()); + } + + #[test] + fn g1_curve_tests() { + ::tests::curve::curve_tests::(); + } +} + +pub mod g2 { + use super::super::{Bls12, Fq, Fq12, Fq2, FqRepr, Fr, FrRepr}; + use super::g1::G1Affine; + use rand::{Rand, Rng}; + use std::fmt; + use { + BitIterator, CurveAffine, CurveProjective, EncodedPoint, Engine, Field, GroupDecodingError, + PrimeField, PrimeFieldRepr, SqrtField, + }; + + curve_impl!( + "G2", + G2, + G2Affine, + G2Prepared, + Fq2, + Fr, + G2Uncompressed, + G2Compressed, + G1Affine + ); + + #[derive(Copy, Clone)] + pub struct G2Uncompressed([u8; 192]); + + impl AsRef<[u8]> for G2Uncompressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsMut<[u8]> for G2Uncompressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + impl fmt::Debug for G2Uncompressed { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0[..].fmt(formatter) + } + } + + impl EncodedPoint for G2Uncompressed { + type Affine = G2Affine; + + fn empty() -> Self { + G2Uncompressed([0; 192]) + } + fn size() -> usize { + 192 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + if !affine.is_on_curve() { + Err(GroupDecodingError::NotOnCurve) + } else if !affine.is_in_correct_subgroup_assuming_on_curve() { + Err(GroupDecodingError::NotInSubgroup) + } else { + Ok(affine) + } + } + fn into_affine_unchecked(&self) -> Result { + // Create a copy of this representation. + let mut copy = self.0; + + if copy[0] & (1 << 7) != 0 { + // Distinguisher bit is set, but this should be uncompressed! + return Err(GroupDecodingError::UnexpectedCompressionMode); + } + + if copy[0] & (1 << 6) != 0 { + // This is the point at infinity, which means that if we mask away + // the first two bits, the entire representation should consist + // of zeroes. + copy[0] &= 0x3f; + + if copy.iter().all(|b| *b == 0) { + Ok(G2Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + if copy[0] & (1 << 5) != 0 { + // The bit indicating the y-coordinate should be lexicographically + // largest is set, but this is an uncompressed element. + return Err(GroupDecodingError::UnexpectedInformation); + } + + // Unset the three most significant bits. + copy[0] &= 0x1f; + + let mut x_c0 = FqRepr([0; 6]); + let mut x_c1 = FqRepr([0; 6]); + let mut y_c0 = FqRepr([0; 6]); + let mut y_c1 = FqRepr([0; 6]); + + { + let mut reader = ©[..]; + + x_c1.read_be(&mut reader).unwrap(); + x_c0.read_be(&mut reader).unwrap(); + y_c1.read_be(&mut reader).unwrap(); + y_c0.read_be(&mut reader).unwrap(); + } + + Ok(G2Affine { + x: Fq2 { + c0: Fq::from_repr(x_c0).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) + })?, + c1: Fq::from_repr(x_c1).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) + })?, + }, + y: Fq2 { + c0: Fq::from_repr(y_c0).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("y coordinate (c0)", e) + })?, + c1: Fq::from_repr(y_c1).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("y coordinate (c1)", e) + })?, + }, + infinity: false, + }) + } + } + fn from_affine(affine: G2Affine) -> Self { + let mut res = Self::empty(); + + if affine.is_zero() { + // Set the second-most significant bit to indicate this point + // is at infinity. + res.0[0] |= 1 << 6; + } else { + let mut writer = &mut res.0[..]; + + affine.x.c1.into_repr().write_be(&mut writer).unwrap(); + affine.x.c0.into_repr().write_be(&mut writer).unwrap(); + affine.y.c1.into_repr().write_be(&mut writer).unwrap(); + affine.y.c0.into_repr().write_be(&mut writer).unwrap(); + } + + res + } + } + + #[derive(Copy, Clone)] + pub struct G2Compressed([u8; 96]); + + impl AsRef<[u8]> for G2Compressed { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsMut<[u8]> for G2Compressed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } + } + + impl fmt::Debug for G2Compressed { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0[..].fmt(formatter) + } + } + + impl EncodedPoint for G2Compressed { + type Affine = G2Affine; + + fn empty() -> Self { + G2Compressed([0; 96]) + } + fn size() -> usize { + 96 + } + fn into_affine(&self) -> Result { + let affine = self.into_affine_unchecked()?; + + // NB: Decompression guarantees that it is on the curve already. + + if !affine.is_in_correct_subgroup_assuming_on_curve() { + Err(GroupDecodingError::NotInSubgroup) + } else { + Ok(affine) + } + } + fn into_affine_unchecked(&self) -> Result { + // Create a copy of this representation. + let mut copy = self.0; + + if copy[0] & (1 << 7) == 0 { + // Distinguisher bit isn't set. + return Err(GroupDecodingError::UnexpectedCompressionMode); + } + + if copy[0] & (1 << 6) != 0 { + // This is the point at infinity, which means that if we mask away + // the first two bits, the entire representation should consist + // of zeroes. + copy[0] &= 0x3f; + + if copy.iter().all(|b| *b == 0) { + Ok(G2Affine::zero()) + } else { + Err(GroupDecodingError::UnexpectedInformation) + } + } else { + // Determine if the intended y coordinate must be greater + // lexicographically. + let greatest = copy[0] & (1 << 5) != 0; + + // Unset the three most significant bits. + copy[0] &= 0x1f; + + let mut x_c1 = FqRepr([0; 6]); + let mut x_c0 = FqRepr([0; 6]); + + { + let mut reader = ©[..]; + + x_c1.read_be(&mut reader).unwrap(); + x_c0.read_be(&mut reader).unwrap(); + } + + // Interpret as Fq element. + let x = Fq2 { + c0: Fq::from_repr(x_c0).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c0)", e) + })?, + c1: Fq::from_repr(x_c1).map_err(|e| { + GroupDecodingError::CoordinateDecodingError("x coordinate (c1)", e) + })?, + }; + + G2Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve) + } + } + fn from_affine(affine: G2Affine) -> Self { + let mut res = Self::empty(); + + if affine.is_zero() { + // Set the second-most significant bit to indicate this point + // is at infinity. + res.0[0] |= 1 << 6; + } else { + { + let mut writer = &mut res.0[..]; + + affine.x.c1.into_repr().write_be(&mut writer).unwrap(); + affine.x.c0.into_repr().write_be(&mut writer).unwrap(); + } + + let mut negy = affine.y; + negy.negate(); + + // Set the third most significant bit if the correct y-coordinate + // is lexicographically largest. + if affine.y > negy { + res.0[0] |= 1 << 5; + } + } + + // Set highest bit to distinguish this as a compressed element. + res.0[0] |= 1 << 7; + + res + } + } + + impl G2Affine { + fn get_generator() -> Self { + G2Affine { + x: Fq2 { + c0: super::super::fq::G2_GENERATOR_X_C0, + c1: super::super::fq::G2_GENERATOR_X_C1, + }, + y: Fq2 { + c0: super::super::fq::G2_GENERATOR_Y_C0, + c1: super::super::fq::G2_GENERATOR_Y_C1, + }, + infinity: false, + } + } + + fn get_coeff_b() -> Fq2 { + Fq2 { + c0: super::super::fq::B_COEFF, + c1: super::super::fq::B_COEFF, + } + } + + fn scale_by_cofactor(&self) -> G2 { + // G2 cofactor = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // 9 + // 0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5 + let cofactor = BitIterator::new([ + 0xcf1c38e31c7238e5, + 0x1616ec6e786f0c70, + 0x21537e293a6691ae, + 0xa628f1cb4d9e82ef, + 0xa68a205b2e5a7ddf, + 0xcd91de4547085aba, + 0x91d50792876a202, + 0x5d543a95414e7f1, + ]); + self.mul_bits(cofactor) + } + + fn perform_pairing(&self, other: &G1Affine) -> Fq12 { + super::super::Bls12::pairing(*other, *self) + } + } + + impl G2 { + fn empirical_recommended_wnaf_for_scalar(scalar: FrRepr) -> usize { + let num_bits = scalar.num_bits() as usize; + + if num_bits >= 103 { + 4 + } else if num_bits >= 37 { + 3 + } else { + 2 + } + } + + fn empirical_recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { + const RECOMMENDATIONS: [usize; 11] = + [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071]; + + let mut ret = 4; + for r in &RECOMMENDATIONS { + if num_scalars > *r { + ret += 1; + } else { + break; + } + } + + ret + } + } + + #[derive(Clone, Debug)] + pub struct G2Prepared { + pub(crate) coeffs: Vec<(Fq2, Fq2, Fq2)>, + pub(crate) infinity: bool, + } + + #[test] + fn g2_generator() { + use SqrtField; + + let mut x = Fq2::zero(); + let mut i = 0; + loop { + // y^2 = x^3 + b + let mut rhs = x; + rhs.square(); + rhs.mul_assign(&x); + rhs.add_assign(&G2Affine::get_coeff_b()); + + if let Some(y) = rhs.sqrt() { + let mut negy = y; + negy.negate(); + + let p = G2Affine { + x: x, + y: if y < negy { y } else { negy }, + infinity: false, + }; + + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + + let g2 = p.scale_by_cofactor(); + if !g2.is_zero() { + assert_eq!(i, 2); + let g2 = G2Affine::from(g2); + + assert!(g2.is_in_correct_subgroup_assuming_on_curve()); + assert_eq!(g2, G2Affine::one()); + break; + } + } + + i += 1; + x.add_assign(&Fq2::one()); + } + } + + #[test] + fn g2_test_is_valid() { + // Reject point on isomorphic twist (b = 3 * (u + 1)) + { + let p = G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xa757072d9fa35ba9, + 0xae3fb2fb418f6e8a, + 0xc1598ec46faa0c7c, + 0x7a17a004747e3dbe, + 0xcc65406a7c2e5a73, + 0x10b8c03d64db4d0c, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xd30e70fe2f029778, + 0xda30772df0f5212e, + 0x5b47a9ff9a233a50, + 0xfb777e5b9b568608, + 0x789bac1fec71a2b9, + 0x1342f02e2da54405, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xfe0812043de54dca, + 0xe455171a3d47a646, + 0xa493f36bc20be98a, + 0x663015d9410eb608, + 0x78e82a79d829a544, + 0x40a00545bb3c1e, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4709802348e79377, + 0xb5ac4dc9204bcfbd, + 0xda361c97d02f42b2, + 0x15008b1dc399e8df, + 0x68128fd0548a3829, + 0x16a613db5c873aaa, + ])).unwrap(), + }, + infinity: false, + }; + assert!(!p.is_on_curve()); + assert!(p.is_in_correct_subgroup_assuming_on_curve()); + } + + // Reject point on a twist (b = 2 * (u + 1)) + { + let p = G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xf4fdfe95a705f917, + 0xc2914df688233238, + 0x37c6b12cca35a34b, + 0x41abba710d6c692c, + 0xffcc4b2b62ce8484, + 0x6993ec01b8934ed, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xb94e92d5f874e26, + 0x44516408bc115d95, + 0xe93946b290caa591, + 0xa5a0c2b7131f3555, + 0x83800965822367e7, + 0x10cf1d3ad8d90bfa, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xbf00334c79701d97, + 0x4fe714f9ff204f9a, + 0xab70b28002f3d825, + 0x5a9171720e73eb51, + 0x38eb4fd8d658adb7, + 0xb649051bbc1164d, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x9225814253d7df75, + 0xc196c2513477f887, + 0xe05e2fbd15a804e0, + 0x55f2b8efad953e04, + 0x7379345eda55265e, + 0x377f2e6208fd4cb, + ])).unwrap(), + }, + infinity: false, + }; + assert!(!p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + } + + // Reject point in an invalid subgroup + // There is only one r-order subgroup, as r does not divide the cofactor. + { + let p = G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x262cea73ea1906c, + 0x2f08540770fabd6, + 0x4ceb92d0a76057be, + 0x2199bc19c48c393d, + 0x4a151b732a6075bf, + 0x17762a3b9108c4a7, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x26f461e944bbd3d1, + 0x298f3189a9cf6ed6, + 0x74328ad8bc2aa150, + 0x7e147f3f9e6e241, + 0x72a9b63583963fff, + 0x158b0083c000462, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x91fb0b225ecf103b, + 0x55d42edc1dc46ba0, + 0x43939b11997b1943, + 0x68cad19430706b4d, + 0x3ccfb97b924dcea8, + 0x1660f93434588f8d, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xaaed3985b6dcb9c7, + 0xc1e985d6d898d9f4, + 0x618bd2ac3271ac42, + 0x3940a2dbb914b529, + 0xbeb88137cf34f3e7, + 0x1699ee577c61b694, + ])).unwrap(), + }, + infinity: false, + }; + assert!(p.is_on_curve()); + assert!(!p.is_in_correct_subgroup_assuming_on_curve()); + } + } + + #[test] + fn test_g2_addition_correctness() { + let mut p = G2 { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x6c994cc1e303094e, + 0xf034642d2c9e85bd, + 0x275094f1352123a9, + 0x72556c999f3707ac, + 0x4617f2e6774e9711, + 0x100b2fe5bffe030b, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x7a33555977ec608, + 0xe23039d1fe9c0881, + 0x19ce4678aed4fcb5, + 0x4637c4f417667e2e, + 0x93ebe7c3e41f6acc, + 0xde884f89a9a371b, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xe073119472e1eb62, + 0x44fb3391fe3c9c30, + 0xaa9b066d74694006, + 0x25fd427b4122f231, + 0xd83112aace35cae, + 0x191b2432407cbb7f, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xf68ae82fe97662f5, + 0xe986057068b50b7d, + 0x96c30f0411590b48, + 0x9eaa6d19de569196, + 0xf6a03d31e2ec2183, + 0x3bdafaf7ca9b39b, + ])).unwrap(), + }, + z: Fq2::one(), + }; + + p.add_assign(&G2 { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xa8c763d25910bdd3, + 0x408777b30ca3add4, + 0x6115fcc12e2769e, + 0x8e73a96b329ad190, + 0x27c546f75ee1f3ab, + 0xa33d27add5e7e82, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x93b1ebcd54870dfe, + 0xf1578300e1342e11, + 0x8270dca3a912407b, + 0x2089faf462438296, + 0x828e5848cd48ea66, + 0x141ecbac1deb038b, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xf5d2c28857229c3f, + 0x8c1574228757ca23, + 0xe8d8102175f5dc19, + 0x2767032fc37cc31d, + 0xd5ee2aba84fd10fe, + 0x16576ccd3dd0a4e8, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4da9b6f6a96d1dd2, + 0x9657f7da77f1650e, + 0xbc150712f9ffe6da, + 0x31898db63f87363a, + 0xabab040ddbd097cc, + 0x11ad236b9ba02990, + ])).unwrap(), + }, + z: Fq2::one(), + }); + + let p = G2Affine::from(p); + + assert_eq!( + p, + G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xcde7ee8a3f2ac8af, + 0xfc642eb35975b069, + 0xa7de72b7dd0e64b7, + 0xf1273e6406eef9cc, + 0xababd760ff05cb92, + 0xd7c20456617e89 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xd1a50b8572cbd2b8, + 0x238f0ac6119d07df, + 0x4dbe924fe5fd6ac2, + 0x8b203284c51edf6b, + 0xc8a0b730bbb21f5e, + 0x1a3b59d29a31274 + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x9e709e78a8eaa4c9, + 0xd30921c93ec342f4, + 0x6d1ef332486f5e34, + 0x64528ab3863633dc, + 0x159384333d7cba97, + 0x4cb84741f3cafe8 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x242af0dc3640e1a4, + 0xe90a73ad65c66919, + 0x2bd7ca7f4346f9ec, + 0x38528f92b689644d, + 0xb6884deec59fb21f, + 0x3c075d3ec52ba90 + ])).unwrap(), + }, + infinity: false, + } + ); + } + + #[test] + fn test_g2_doubling_correctness() { + let mut p = G2 { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x6c994cc1e303094e, + 0xf034642d2c9e85bd, + 0x275094f1352123a9, + 0x72556c999f3707ac, + 0x4617f2e6774e9711, + 0x100b2fe5bffe030b, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x7a33555977ec608, + 0xe23039d1fe9c0881, + 0x19ce4678aed4fcb5, + 0x4637c4f417667e2e, + 0x93ebe7c3e41f6acc, + 0xde884f89a9a371b, + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xe073119472e1eb62, + 0x44fb3391fe3c9c30, + 0xaa9b066d74694006, + 0x25fd427b4122f231, + 0xd83112aace35cae, + 0x191b2432407cbb7f, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xf68ae82fe97662f5, + 0xe986057068b50b7d, + 0x96c30f0411590b48, + 0x9eaa6d19de569196, + 0xf6a03d31e2ec2183, + 0x3bdafaf7ca9b39b, + ])).unwrap(), + }, + z: Fq2::one(), + }; + + p.double(); + + let p = G2Affine::from(p); + + assert_eq!( + p, + G2Affine { + x: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x91ccb1292727c404, + 0x91a6cb182438fad7, + 0x116aee59434de902, + 0xbcedcfce1e52d986, + 0x9755d4a3926e9862, + 0x18bab73760fd8024 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4e7c5e0a2ae5b99e, + 0x96e582a27f028961, + 0xc74d1cf4ef2d5926, + 0xeb0cf5e610ef4fe7, + 0x7b4c2bae8db6e70b, + 0xf136e43909fca0 + ])).unwrap(), + }, + y: Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x954d4466ab13e58, + 0x3ee42eec614cf890, + 0x853bb1d28877577e, + 0xa5a2a51f7fde787b, + 0x8b92866bc6384188, + 0x81a53fe531d64ef + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4c5d607666239b34, + 0xeddb5f48304d14b3, + 0x337167ee6e8e3cb6, + 0xb271f52f12ead742, + 0x244e6c2015c83348, + 0x19e2deae6eb9b441 + ])).unwrap(), + }, + infinity: false, + } + ); + } + + #[test] + fn g2_curve_tests() { + ::tests::curve::curve_tests::(); + } +} + +pub use self::g1::*; +pub use self::g2::*; diff --git a/src/rust/pairing/src/bls12_381/fq.rs b/src/rust/pairing/src/bls12_381/fq.rs new file mode 100644 index 000000000..738da38f6 --- /dev/null +++ b/src/rust/pairing/src/bls12_381/fq.rs @@ -0,0 +1,2959 @@ +use super::fq2::Fq2; +use std::cmp::Ordering; +use {Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField}; + +// q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +const MODULUS: FqRepr = FqRepr([ + 0xb9feffffffffaaab, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, +]); + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 381; + +// The number of bits that must be shaved from the beginning of +// the representation when randomly sampling. +const REPR_SHAVE_BITS: u32 = 3; + +// R = 2**384 % q +const R: FqRepr = FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, +]); + +// R2 = R^2 % q +const R2: FqRepr = FqRepr([ + 0xf4df1f341c341746, + 0xa76e6a609d104f1, + 0x8de5476c4c95b6d5, + 0x67eb88a9939d83c0, + 0x9a793e85b519952d, + 0x11988fe592cae3aa, +]); + +// INV = -(q^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0x89f3fffcfffcfffd; + +// GENERATOR = 2 (multiplicative generator of q-1 order, that is also quadratic nonresidue) +const GENERATOR: FqRepr = FqRepr([ + 0x321300000006554f, + 0xb93c0018d6c40005, + 0x57605e0db0ddbb51, + 0x8b256521ed1f9bcb, + 0x6cf28d7901622c03, + 0x11ebab9dbb81e28c, +]); + +// 2^s * t = MODULUS - 1 with t odd +const S: u32 = 1; + +// 2^s root of unity computed by GENERATOR^t +const ROOT_OF_UNITY: FqRepr = FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, +]); + +// B coefficient of BLS12-381 curve, 4. +pub const B_COEFF: Fq = Fq(FqRepr([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, +])); + +// The generators of G1/G2 are computed by finding the lexicographically smallest valid x coordinate, +// and its lexicographically smallest y coordinate and multiplying it by the cofactor such that the +// result is nonzero. + +// Generator of G1 +// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 +// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 +pub const G1_GENERATOR_X: Fq = Fq(FqRepr([ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, +])); +pub const G1_GENERATOR_Y: Fq = Fq(FqRepr([ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0xe1c8c3fad0059c0, + 0xbbc3efc5008a26a, +])); + +// Generator of G2 +// x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 +// y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 +pub const G2_GENERATOR_X_C0: Fq = Fq(FqRepr([ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x58191924350bcd7, +])); +pub const G2_GENERATOR_X_C1: Fq = Fq(FqRepr([ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, +])); +pub const G2_GENERATOR_Y_C0: Fq = Fq(FqRepr([ + 0x4c730af860494c4a, + 0x597cfa1f5e369c5a, + 0xe7e6856caa0a635a, + 0xbbefb5e96e0d495f, + 0x7d3a975f0ef25a2, + 0x83fd8e7e80dae5, +])); +pub const G2_GENERATOR_Y_C1: Fq = Fq(FqRepr([ + 0xadc0fc92df64b05d, + 0x18aa270a2b1461dc, + 0x86adac6a3be4eba0, + 0x79495c4ec93da33a, + 0xe7175850a43ccaed, + 0xb2bc2a163de1bf2, +])); + +// Coefficients for the Frobenius automorphism. +pub const FROBENIUS_COEFF_FQ2_C1: [Fq; 2] = [ + // Fq(-1)**(((q^0) - 1) / 2) + Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + // Fq(-1)**(((q^1) - 1) / 2) + Fq(FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, + ])), +]; + +pub const FROBENIUS_COEFF_FQ6_C1: [Fq2; 6] = [ + // Fq2(u + 1)**(((q^0) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^1) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + c1: Fq(FqRepr([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ])), + }, + // Fq2(u + 1)**(((q^2) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^3) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + c1: Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + }, + // Fq2(u + 1)**(((q^4) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^5) - 1) / 3) + Fq2 { + c0: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + c1: Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ])), + }, +]; + +pub const FROBENIUS_COEFF_FQ6_C2: [Fq2; 6] = [ + // Fq2(u + 1)**(((2q^0) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^1) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^2) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^3) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^4) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((2q^5) - 2) / 3) + Fq2 { + c0: Fq(FqRepr([ + 0xecfb361b798dba3a, + 0xc100ddb891865a2c, + 0xec08ff1232bda8e, + 0xd5c13cc6f1ca4721, + 0x47222a47bf7b5c04, + 0x110f184e51c5f59, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, +]; + +// non_residue^((modulus^i-1)/6) for i=0,...,11 +pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ + // Fq2(u + 1)**(((q^0) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^1) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x7089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x8f2220fb0fb66eb, + ])), + c1: Fq(FqRepr([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ])), + }, + // Fq2(u + 1)**(((q^2) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xecfb361b798dba3a, + 0xc100ddb891865a2c, + 0xec08ff1232bda8e, + 0xd5c13cc6f1ca4721, + 0x47222a47bf7b5c04, + 0x110f184e51c5f59, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^3) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0xbd592fc7d825ec8, + ])), + c1: Fq(FqRepr([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0xe2b7eedbbfd87d2, + ])), + }, + // Fq2(u + 1)**(((q^4) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x30f1361b798a64e8, + 0xf3b8ddab7ece5a2a, + 0x16a8ca3ac61577f7, + 0xc26a2ff874fd029b, + 0x3636b76660701c6e, + 0x51ba4ab241b6160, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^5) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x3726c30af242c66c, + 0x7c2ac1aad1b6fe70, + 0xa04007fbba4b14a2, + 0xef517c3266341429, + 0x95ba654ed2226b, + 0x2e370eccc86f7dd, + ])), + c1: Fq(FqRepr([ + 0x82d83cf50dbce43f, + 0xa2813e53df9d018f, + 0xc6f0caa53c65e181, + 0x7525cf528d50fe95, + 0x4a85ed50f4798a6b, + 0x171da0fd6cf8eebd, + ])), + }, + // Fq2(u + 1)**(((q^6) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^7) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xb2f66aad4ce5d646, + 0x5842a06bfc497cec, + 0xcf4895d42599d394, + 0xc11b9cba40a8e8d0, + 0x2e3813cbe5a0de89, + 0x110eefda88847faf, + ])), + c1: Fq(FqRepr([ + 0x7089552b319d465, + 0xc6695f92b50a8313, + 0x97e83cccd117228f, + 0xa35baecab2dc29ee, + 0x1ce393ea5daace4d, + 0x8f2220fb0fb66eb, + ])), + }, + // Fq2(u + 1)**(((q^8) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0xcd03c9e48671f071, + 0x5dab22461fcda5d2, + 0x587042afd3851b95, + 0x8eb60ebe01bacb9e, + 0x3f97d6e83d050d2, + 0x18f0206554638741, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^9) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x7bcfa7a25aa30fda, + 0xdc17dec12a927e7c, + 0x2f088dd86b4ebef1, + 0xd1ca2087da74d4a7, + 0x2da2596696cebc1d, + 0xe2b7eedbbfd87d2, + ])), + c1: Fq(FqRepr([ + 0x3e2f585da55c9ad1, + 0x4294213d86c18183, + 0x382844c88b623732, + 0x92ad2afd19103e18, + 0x1d794e4fac7cf0b9, + 0xbd592fc7d825ec8, + ])), + }, + // Fq2(u + 1)**(((q^10) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x890dc9e4867545c3, + 0x2af322533285a5d5, + 0x50880866309b7e2c, + 0xa20d1b8c7e881024, + 0x14e4f04fe2db9068, + 0x14e56d3f1564853a, + ])), + c1: Fq(FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x0])), + }, + // Fq2(u + 1)**(((q^11) - 1) / 6) + Fq2 { + c0: Fq(FqRepr([ + 0x82d83cf50dbce43f, + 0xa2813e53df9d018f, + 0xc6f0caa53c65e181, + 0x7525cf528d50fe95, + 0x4a85ed50f4798a6b, + 0x171da0fd6cf8eebd, + ])), + c1: Fq(FqRepr([ + 0x3726c30af242c66c, + 0x7c2ac1aad1b6fe70, + 0xa04007fbba4b14a2, + 0xef517c3266341429, + 0x95ba654ed2226b, + 0x2e370eccc86f7dd, + ])), + }, +]; + +// -((2**384) mod q) mod q +pub const NEGATIVE_ONE: Fq = Fq(FqRepr([ + 0x43f5fffffffcaaae, + 0x32b7fff2ed47fffd, + 0x7e83a49a2e99d69, + 0xeca8f3318332bb7a, + 0xef148d1ea0f4c069, + 0x40ab3263eff0206, +])); + +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] +pub struct FqRepr(pub [u64; 6]); + +impl ::rand::Rand for FqRepr { + #[inline(always)] + fn rand(rng: &mut R) -> Self { + FqRepr(rng.gen()) + } +} + +impl ::std::fmt::Display for FqRepr { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + try!(write!(f, "0x")); + for i in self.0.iter().rev() { + try!(write!(f, "{:016x}", *i)); + } + + Ok(()) + } +} + +impl AsRef<[u64]> for FqRepr { + #[inline(always)] + fn as_ref(&self) -> &[u64] { + &self.0 + } +} + +impl AsMut<[u64]> for FqRepr { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u64] { + &mut self.0 + } +} + +impl From for FqRepr { + #[inline(always)] + fn from(val: u64) -> FqRepr { + let mut repr = Self::default(); + repr.0[0] = val; + repr + } +} + +impl Ord for FqRepr { + #[inline(always)] + fn cmp(&self, other: &FqRepr) -> Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return Ordering::Less; + } else if a > b { + return Ordering::Greater; + } + } + + Ordering::Equal + } +} + +impl PartialOrd for FqRepr { + #[inline(always)] + fn partial_cmp(&self, other: &FqRepr) -> Option { + Some(self.cmp(other)) + } +} + +impl PrimeFieldRepr for FqRepr { + #[inline(always)] + fn is_odd(&self) -> bool { + self.0[0] & 1 == 1 + } + + #[inline(always)] + fn is_even(&self) -> bool { + !self.is_odd() + } + + #[inline(always)] + fn is_zero(&self) -> bool { + self.0.iter().all(|&e| e == 0) + } + + #[inline(always)] + fn shr(&mut self, mut n: u32) { + if n >= 64 * 6 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn div2(&mut self) { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; + } + } + + #[inline(always)] + fn mul2(&mut self) { + let mut last = 0; + for i in &mut self.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } + } + + #[inline(always)] + fn shl(&mut self, mut n: u32) { + if n >= 64 * 6 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in &mut self.0 { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in &mut self.0 { + let t2 = *i >> (64 - n); + *i <<= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn num_bits(&self) -> u32 { + let mut ret = (6 as u32) * 64; + for i in self.0.iter().rev() { + let leading = i.leading_zeros(); + ret -= leading; + if leading != 64 { + break; + } + } + + ret + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &FqRepr) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &FqRepr) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::sbb(*a, *b, &mut borrow); + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Fq(FqRepr); + +/// `Fq` elements are ordered lexicographically. +impl Ord for Fq { + #[inline(always)] + fn cmp(&self, other: &Fq) -> Ordering { + self.into_repr().cmp(&other.into_repr()) + } +} + +impl PartialOrd for Fq { + #[inline(always)] + fn partial_cmp(&self, other: &Fq) -> Option { + Some(self.cmp(other)) + } +} + +impl ::std::fmt::Display for Fq { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fq({})", self.into_repr()) + } +} + +impl ::rand::Rand for Fq { + fn rand(rng: &mut R) -> Self { + loop { + let mut tmp = Fq(FqRepr::rand(rng)); + + // Mask away the unused bits at the beginning. + tmp.0.as_mut()[5] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; + + if tmp.is_valid() { + return tmp; + } + } + } +} + +impl From for FqRepr { + fn from(e: Fq) -> FqRepr { + e.into_repr() + } +} + +impl PrimeField for Fq { + type Repr = FqRepr; + + fn from_repr(r: FqRepr) -> Result { + let mut r = Fq(r); + if r.is_valid() { + r.mul_assign(&Fq(R2)); + + Ok(r) + } else { + Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0))) + } + } + + fn into_repr(&self) -> FqRepr { + let mut r = *self; + r.mont_reduce( + (self.0).0[0], + (self.0).0[1], + (self.0).0[2], + (self.0).0[3], + (self.0).0[4], + (self.0).0[5], + 0, + 0, + 0, + 0, + 0, + 0, + ); + r.0 + } + + fn char() -> FqRepr { + MODULUS + } + + const NUM_BITS: u32 = MODULUS_BITS; + + const CAPACITY: u32 = Self::NUM_BITS - 1; + + fn multiplicative_generator() -> Self { + Fq(GENERATOR) + } + + const S: u32 = S; + + fn root_of_unity() -> Self { + Fq(ROOT_OF_UNITY) + } +} + +impl Field for Fq { + #[inline] + fn zero() -> Self { + Fq(FqRepr::from(0)) + } + + #[inline] + fn one() -> Self { + Fq(R) + } + + #[inline] + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[inline] + fn add_assign(&mut self, other: &Fq) { + // This cannot exceed the backing capacity. + self.0.add_nocarry(&other.0); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn double(&mut self) { + // This cannot exceed the backing capacity. + self.0.mul2(); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn sub_assign(&mut self, other: &Fq) { + // If `other` is larger than `self`, we'll need to add the modulus to self first. + if other.0 > self.0 { + self.0.add_nocarry(&MODULUS); + } + + self.0.sub_noborrow(&other.0); + } + + #[inline] + fn negate(&mut self) { + if !self.is_zero() { + let mut tmp = MODULUS; + tmp.sub_noborrow(&self.0); + self.0 = tmp; + } + } + + fn inverse(&self) -> Option { + if self.is_zero() { + None + } else { + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications to Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + let one = FqRepr::from(1); + + let mut u = self.0; + let mut v = MODULUS; + let mut b = Fq(R2); // Avoids unnecessary reduction step. + let mut c = Self::zero(); + + while u != one && v != one { + while u.is_even() { + u.div2(); + + if b.0.is_even() { + b.0.div2(); + } else { + b.0.add_nocarry(&MODULUS); + b.0.div2(); + } + } + + while v.is_even() { + v.div2(); + + if c.0.is_even() { + c.0.div2(); + } else { + c.0.add_nocarry(&MODULUS); + c.0.div2(); + } + } + + if v < u { + u.sub_noborrow(&v); + b.sub_assign(&c); + } else { + v.sub_noborrow(&u); + c.sub_assign(&b); + } + } + + if u == one { + Some(b) + } else { + Some(c) + } + } + } + + #[inline(always)] + fn frobenius_map(&mut self, _: usize) { + // This has no effect in a prime field. + } + + #[inline] + fn mul_assign(&mut self, other: &Fq) { + let mut carry = 0; + let r0 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); + let r1 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); + let r2 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[2], &mut carry); + let r3 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[3], &mut carry); + let r4 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[4], &mut carry); + let r5 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[5], &mut carry); + let r6 = carry; + let mut carry = 0; + let r1 = ::mac_with_carry(r1, (self.0).0[1], (other.0).0[0], &mut carry); + let r2 = ::mac_with_carry(r2, (self.0).0[1], (other.0).0[1], &mut carry); + let r3 = ::mac_with_carry(r3, (self.0).0[1], (other.0).0[2], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[1], (other.0).0[3], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[1], (other.0).0[4], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[1], (other.0).0[5], &mut carry); + let r7 = carry; + let mut carry = 0; + let r2 = ::mac_with_carry(r2, (self.0).0[2], (other.0).0[0], &mut carry); + let r3 = ::mac_with_carry(r3, (self.0).0[2], (other.0).0[1], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[2], (other.0).0[2], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[2], (other.0).0[3], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[2], (other.0).0[4], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[2], (other.0).0[5], &mut carry); + let r8 = carry; + let mut carry = 0; + let r3 = ::mac_with_carry(r3, (self.0).0[3], (other.0).0[0], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[3], (other.0).0[1], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[3], (other.0).0[2], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[3], (other.0).0[3], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[3], (other.0).0[4], &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[3], (other.0).0[5], &mut carry); + let r9 = carry; + let mut carry = 0; + let r4 = ::mac_with_carry(r4, (self.0).0[4], (other.0).0[0], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[4], (other.0).0[1], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[4], (other.0).0[2], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[4], (other.0).0[3], &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[4], (other.0).0[4], &mut carry); + let r9 = ::mac_with_carry(r9, (self.0).0[4], (other.0).0[5], &mut carry); + let r10 = carry; + let mut carry = 0; + let r5 = ::mac_with_carry(r5, (self.0).0[5], (other.0).0[0], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[5], (other.0).0[1], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[5], (other.0).0[2], &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[5], (other.0).0[3], &mut carry); + let r9 = ::mac_with_carry(r9, (self.0).0[5], (other.0).0[4], &mut carry); + let r10 = ::mac_with_carry(r10, (self.0).0[5], (other.0).0[5], &mut carry); + let r11 = carry; + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11); + } + + #[inline] + fn square(&mut self) { + let mut carry = 0; + let r1 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); + let r2 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); + let r3 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[3], &mut carry); + let r4 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[4], &mut carry); + let r5 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[5], &mut carry); + let r6 = carry; + let mut carry = 0; + let r3 = ::mac_with_carry(r3, (self.0).0[1], (self.0).0[2], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[1], (self.0).0[3], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[1], (self.0).0[4], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[1], (self.0).0[5], &mut carry); + let r7 = carry; + let mut carry = 0; + let r5 = ::mac_with_carry(r5, (self.0).0[2], (self.0).0[3], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[2], (self.0).0[4], &mut carry); + let r7 = ::mac_with_carry(r7, (self.0).0[2], (self.0).0[5], &mut carry); + let r8 = carry; + let mut carry = 0; + let r7 = ::mac_with_carry(r7, (self.0).0[3], (self.0).0[4], &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[3], (self.0).0[5], &mut carry); + let r9 = carry; + let mut carry = 0; + let r9 = ::mac_with_carry(r9, (self.0).0[4], (self.0).0[5], &mut carry); + let r10 = carry; + + let r11 = r10 >> 63; + let r10 = (r10 << 1) | (r9 >> 63); + let r9 = (r9 << 1) | (r8 >> 63); + let r8 = (r8 << 1) | (r7 >> 63); + let r7 = (r7 << 1) | (r6 >> 63); + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let mut carry = 0; + let r0 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[0], &mut carry); + let r1 = ::adc(r1, 0, &mut carry); + let r2 = ::mac_with_carry(r2, (self.0).0[1], (self.0).0[1], &mut carry); + let r3 = ::adc(r3, 0, &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[2], (self.0).0[2], &mut carry); + let r5 = ::adc(r5, 0, &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[3], (self.0).0[3], &mut carry); + let r7 = ::adc(r7, 0, &mut carry); + let r8 = ::mac_with_carry(r8, (self.0).0[4], (self.0).0[4], &mut carry); + let r9 = ::adc(r9, 0, &mut carry); + let r10 = ::mac_with_carry(r10, (self.0).0[5], (self.0).0[5], &mut carry); + let r11 = ::adc(r11, 0, &mut carry); + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11); + } +} + +impl Fq { + /// Determines if the element is really in the field. This is only used + /// internally. + #[inline(always)] + fn is_valid(&self) -> bool { + self.0 < MODULUS + } + + /// Subtracts the modulus from this element if this element is not in the + /// field. Only used internally. + #[inline(always)] + fn reduce(&mut self) { + if !self.is_valid() { + self.0.sub_noborrow(&MODULUS); + } + } + + #[inline(always)] + fn mont_reduce( + &mut self, + r0: u64, + mut r1: u64, + mut r2: u64, + mut r3: u64, + mut r4: u64, + mut r5: u64, + mut r6: u64, + mut r7: u64, + mut r8: u64, + mut r9: u64, + mut r10: u64, + mut r11: u64, + ) { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r0, k, MODULUS.0[0], &mut carry); + r1 = ::mac_with_carry(r1, k, MODULUS.0[1], &mut carry); + r2 = ::mac_with_carry(r2, k, MODULUS.0[2], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[3], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[4], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[5], &mut carry); + r6 = ::adc(r6, 0, &mut carry); + let carry2 = carry; + let k = r1.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r1, k, MODULUS.0[0], &mut carry); + r2 = ::mac_with_carry(r2, k, MODULUS.0[1], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[2], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[3], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[4], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[5], &mut carry); + r7 = ::adc(r7, carry2, &mut carry); + let carry2 = carry; + let k = r2.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r2, k, MODULUS.0[0], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[1], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[2], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[3], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[4], &mut carry); + r7 = ::mac_with_carry(r7, k, MODULUS.0[5], &mut carry); + r8 = ::adc(r8, carry2, &mut carry); + let carry2 = carry; + let k = r3.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r3, k, MODULUS.0[0], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[1], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[2], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[3], &mut carry); + r7 = ::mac_with_carry(r7, k, MODULUS.0[4], &mut carry); + r8 = ::mac_with_carry(r8, k, MODULUS.0[5], &mut carry); + r9 = ::adc(r9, carry2, &mut carry); + let carry2 = carry; + let k = r4.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r4, k, MODULUS.0[0], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[1], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[2], &mut carry); + r7 = ::mac_with_carry(r7, k, MODULUS.0[3], &mut carry); + r8 = ::mac_with_carry(r8, k, MODULUS.0[4], &mut carry); + r9 = ::mac_with_carry(r9, k, MODULUS.0[5], &mut carry); + r10 = ::adc(r10, carry2, &mut carry); + let carry2 = carry; + let k = r5.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r5, k, MODULUS.0[0], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[1], &mut carry); + r7 = ::mac_with_carry(r7, k, MODULUS.0[2], &mut carry); + r8 = ::mac_with_carry(r8, k, MODULUS.0[3], &mut carry); + r9 = ::mac_with_carry(r9, k, MODULUS.0[4], &mut carry); + r10 = ::mac_with_carry(r10, k, MODULUS.0[5], &mut carry); + r11 = ::adc(r11, carry2, &mut carry); + (self.0).0[0] = r6; + (self.0).0[1] = r7; + (self.0).0[2] = r8; + (self.0).0[3] = r9; + (self.0).0[4] = r10; + (self.0).0[5] = r11; + self.reduce(); + } +} + +impl SqrtField for Fq { + fn legendre(&self) -> ::LegendreSymbol { + use LegendreSymbol::*; + + // s = self^((q - 1) // 2) + let s = self.pow([ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d, + ]); + if s == Fq::zero() { + Zero + } else if s == Fq::one() { + QuadraticResidue + } else { + QuadraticNonResidue + } + } + + fn sqrt(&self) -> Option { + // Shank's algorithm for q mod 4 = 3 + // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) + + // a1 = self^((q - 3) // 4) + let mut a1 = self.pow([ + 0xee7fbfffffffeaaa, + 0x7aaffffac54ffff, + 0xd9cc34a83dac3d89, + 0xd91dd2e13ce144af, + 0x92c6e9ed90d2eb35, + 0x680447a8e5ff9a6, + ]); + let mut a0 = a1; + a0.square(); + a0.mul_assign(self); + + if a0 == NEGATIVE_ONE { + None + } else { + a1.mul_assign(self); + Some(a1) + } + } +} + +#[test] +fn test_b_coeff() { + assert_eq!(Fq::from_repr(FqRepr::from(4)).unwrap(), B_COEFF); +} + +#[test] +fn test_frob_coeffs() { + let mut nqr = Fq::one(); + nqr.negate(); + + assert_eq!(FROBENIUS_COEFF_FQ2_C1[0], Fq::one()); + assert_eq!( + FROBENIUS_COEFF_FQ2_C1[1], + nqr.pow([ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d + ]) + ); + + let nqr = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; + + assert_eq!(FROBENIUS_COEFF_FQ6_C1[0], Fq2::one()); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[1], + nqr.pow([ + 0x9354ffffffffe38e, + 0xa395554e5c6aaaa, + 0xcd104635a790520c, + 0xcc27c3d6fbd7063f, + 0x190937e76bc3e447, + 0x8ab05f8bdd54cde + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[2], + nqr.pow([ + 0xb78e0000097b2f68, + 0xd44f23b47cbd64e3, + 0x5cb9668120b069a9, + 0xccea85f9bf7b3d16, + 0xdba2c8d7adb356d, + 0x9cd75ded75d7429, + 0xfc65c31103284fab, + 0xc58cb9a9b249ee24, + 0xccf734c3118a2e9a, + 0xa0f4304c5a256ce6, + 0xc3f0d2f8e0ba61f8, + 0xe167e192ebca97 + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[3], + nqr.pow([ + 0xdbc6fcd6f35b9e06, + 0x997dead10becd6aa, + 0x9dbbd24c17206460, + 0x72b97acc6057c45e, + 0xf8e9a230bf0c628e, + 0x647ccb1885c63a7, + 0xce80264fc55bf6ee, + 0x94d8d716c3939fc4, + 0xad78f0eb77ee6ee1, + 0xd6fe49bfe57dc5f9, + 0x2656d6c15c63647, + 0xdf6282f111fa903, + 0x1bdba63e0632b4bb, + 0x6883597bcaa505eb, + 0xa56d4ec90c34a982, + 0x7e4c42823bbe90b2, + 0xf64728aa6dcb0f20, + 0x16e57e16ef152f + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[4], + nqr.pow([ + 0x4649add3c71c6d90, + 0x43caa6528972a865, + 0xcda8445bbaaa0fbb, + 0xc93dea665662aa66, + 0x2863bc891834481d, + 0x51a0c3f5d4ccbed8, + 0x9210e660f90ccae9, + 0xe2bd6836c546d65e, + 0xf223abbaa7cf778b, + 0xd4f10b222cf11680, + 0xd540f5eff4a1962e, + 0xa123a1f140b56526, + 0x31ace500636a59f6, + 0x3a82bc8c8dfa57a9, + 0x648c511e217fc1f8, + 0x36c17ffd53a4558f, + 0x881bef5fd684eefd, + 0x5d648dbdc5dbb522, + 0x8fd07bf06e5e59b8, + 0x8ddec8a9acaa4b51, + 0x4cc1f8688e2def26, + 0xa74e63cb492c03de, + 0x57c968173d1349bb, + 0x253674e02a866 + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C1[5], + nqr.pow([ + 0xf896f792732eb2be, + 0x49c86a6d1dc593a1, + 0xe5b31e94581f91c3, + 0xe3da5cc0a6b20d7f, + 0x822caef950e0bfed, + 0x317ed950b9ee67cd, + 0xffd664016ee3f6cd, + 0x77d991c88810b122, + 0x62e72e635e698264, + 0x905e1a1a2d22814a, + 0xf5b7ab3a3f33d981, + 0x175871b0bc0e25dd, + 0x1e2e9a63df5c3772, + 0xe888b1f7445b149d, + 0x9551c19e5e7e2c24, + 0xecf21939a3d2d6be, + 0xd830dbfdab72dbd4, + 0x7b34af8d622d40c0, + 0x3df6d20a45671242, + 0xaf86bee30e21d98, + 0x41064c1534e5df5d, + 0xf5f6cabd3164c609, + 0xa5d14bdf2b7ee65, + 0xa718c069defc9138, + 0xdb1447e770e3110e, + 0xc1b164a9e90af491, + 0x7180441f9d251602, + 0x1fd3a5e6a9a893e, + 0x1e17b779d54d5db, + 0x3c7afafe3174 + ]) + ); + + assert_eq!(FROBENIUS_COEFF_FQ6_C2[0], Fq2::one()); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[1], + nqr.pow([ + 0x26a9ffffffffc71c, + 0x1472aaa9cb8d5555, + 0x9a208c6b4f20a418, + 0x984f87adf7ae0c7f, + 0x32126fced787c88f, + 0x11560bf17baa99bc + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[2], + nqr.pow([ + 0x6f1c000012f65ed0, + 0xa89e4768f97ac9c7, + 0xb972cd024160d353, + 0x99d50bf37ef67a2c, + 0x1b74591af5b66adb, + 0x139aebbdaebae852, + 0xf8cb862206509f56, + 0x8b1973536493dc49, + 0x99ee698623145d35, + 0x41e86098b44ad9cd, + 0x87e1a5f1c174c3f1, + 0x1c2cfc325d7952f + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[3], + nqr.pow([ + 0xb78df9ade6b73c0c, + 0x32fbd5a217d9ad55, + 0x3b77a4982e40c8c1, + 0xe572f598c0af88bd, + 0xf1d344617e18c51c, + 0xc8f996310b8c74f, + 0x9d004c9f8ab7eddc, + 0x29b1ae2d87273f89, + 0x5af1e1d6efdcddc3, + 0xadfc937fcafb8bf3, + 0x4cadad82b8c6c8f, + 0x1bec505e223f5206, + 0x37b74c7c0c656976, + 0xd106b2f7954a0bd6, + 0x4ada9d9218695304, + 0xfc988504777d2165, + 0xec8e5154db961e40, + 0x2dcafc2dde2a5f + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[4], + nqr.pow([ + 0x8c935ba78e38db20, + 0x87954ca512e550ca, + 0x9b5088b775541f76, + 0x927bd4ccacc554cd, + 0x50c779123068903b, + 0xa34187eba9997db0, + 0x2421ccc1f21995d2, + 0xc57ad06d8a8dacbd, + 0xe44757754f9eef17, + 0xa9e2164459e22d01, + 0xaa81ebdfe9432c5d, + 0x424743e2816aca4d, + 0x6359ca00c6d4b3ed, + 0x750579191bf4af52, + 0xc918a23c42ff83f0, + 0x6d82fffaa748ab1e, + 0x1037debfad09ddfa, + 0xbac91b7b8bb76a45, + 0x1fa0f7e0dcbcb370, + 0x1bbd9153595496a3, + 0x9983f0d11c5bde4d, + 0x4e9cc796925807bc, + 0xaf92d02e7a269377, + 0x4a6ce9c0550cc + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ6_C2[5], + nqr.pow([ + 0xf12def24e65d657c, + 0x9390d4da3b8b2743, + 0xcb663d28b03f2386, + 0xc7b4b9814d641aff, + 0x4595df2a1c17fdb, + 0x62fdb2a173dccf9b, + 0xffacc802ddc7ed9a, + 0xefb3239110216245, + 0xc5ce5cc6bcd304c8, + 0x20bc34345a450294, + 0xeb6f56747e67b303, + 0x2eb0e361781c4bbb, + 0x3c5d34c7beb86ee4, + 0xd11163ee88b6293a, + 0x2aa3833cbcfc5849, + 0xd9e4327347a5ad7d, + 0xb061b7fb56e5b7a9, + 0xf6695f1ac45a8181, + 0x7beda4148ace2484, + 0x15f0d7dc61c43b30, + 0x820c982a69cbbeba, + 0xebed957a62c98c12, + 0x14ba297be56fdccb, + 0x4e3180d3bdf92270, + 0xb6288fcee1c6221d, + 0x8362c953d215e923, + 0xe300883f3a4a2c05, + 0x3fa74bcd535127c, + 0x3c2f6ef3aa9abb6, + 0x78f5f5fc62e8 + ]) + ); + + assert_eq!(FROBENIUS_COEFF_FQ12_C1[0], Fq2::one()); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[1], + nqr.pow([ + 0x49aa7ffffffff1c7, + 0x51caaaa72e35555, + 0xe688231ad3c82906, + 0xe613e1eb7deb831f, + 0xc849bf3b5e1f223, + 0x45582fc5eeaa66f + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[2], + nqr.pow([ + 0xdbc7000004bd97b4, + 0xea2791da3e5eb271, + 0x2e5cb340905834d4, + 0xe67542fcdfbd9e8b, + 0x86dd1646bd6d9ab6, + 0x84e6baef6baeba14, + 0x7e32e188819427d5, + 0x62c65cd4d924f712, + 0x667b9a6188c5174d, + 0x507a18262d12b673, + 0xe1f8697c705d30fc, + 0x70b3f0c975e54b + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[3], + nqr.pow(vec![ + 0x6de37e6b79adcf03, + 0x4cbef56885f66b55, + 0x4edde9260b903230, + 0x395cbd66302be22f, + 0xfc74d1185f863147, + 0x323e658c42e31d3, + 0x67401327e2adfb77, + 0xca6c6b8b61c9cfe2, + 0xd6bc7875bbf73770, + 0xeb7f24dff2bee2fc, + 0x8132b6b60ae31b23, + 0x86fb1417888fd481, + 0x8dedd31f03195a5d, + 0x3441acbde55282f5, + 0x52b6a764861a54c1, + 0x3f2621411ddf4859, + 0xfb23945536e58790, + 0xb72bf0b778a97, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[4], + nqr.pow(vec![ + 0xa324d6e9e38e36c8, + 0xa1e5532944b95432, + 0x66d4222ddd5507dd, + 0xe49ef5332b315533, + 0x1431de448c1a240e, + 0xa8d061faea665f6c, + 0x490873307c866574, + 0xf15eb41b62a36b2f, + 0x7911d5dd53e7bbc5, + 0x6a78859116788b40, + 0x6aa07af7fa50cb17, + 0x5091d0f8a05ab293, + 0x98d6728031b52cfb, + 0x1d415e4646fd2bd4, + 0xb246288f10bfe0fc, + 0x9b60bffea9d22ac7, + 0x440df7afeb42777e, + 0x2eb246dee2edda91, + 0xc7e83df8372f2cdc, + 0x46ef6454d65525a8, + 0x2660fc344716f793, + 0xd3a731e5a49601ef, + 0x2be4b40b9e89a4dd, + 0x129b3a7015433, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[5], + nqr.pow(vec![ + 0xfc4b7bc93997595f, + 0xa4e435368ee2c9d0, + 0xf2d98f4a2c0fc8e1, + 0xf1ed2e60535906bf, + 0xc116577ca8705ff6, + 0x98bf6ca85cf733e6, + 0x7feb3200b771fb66, + 0x3becc8e444085891, + 0x31739731af34c132, + 0xc82f0d0d169140a5, + 0xfadbd59d1f99ecc0, + 0xbac38d85e0712ee, + 0x8f174d31efae1bb9, + 0x744458fba22d8a4e, + 0x4aa8e0cf2f3f1612, + 0x76790c9cd1e96b5f, + 0x6c186dfed5b96dea, + 0x3d9a57c6b116a060, + 0x1efb690522b38921, + 0x857c35f718710ecc, + 0xa083260a9a72efae, + 0xfafb655e98b26304, + 0x52e8a5ef95bf732, + 0x538c6034ef7e489c, + 0xed8a23f3b8718887, + 0x60d8b254f4857a48, + 0x38c0220fce928b01, + 0x80fe9d2f354d449f, + 0xf0bdbbceaa6aed, + 0x1e3d7d7f18ba, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[6], + nqr.pow(vec![ + 0x21219610a012ba3c, + 0xa5c19ad35375325, + 0x4e9df1e497674396, + 0xfb05b717c991c6ef, + 0x4a1265bca93a32f2, + 0xd875ff2a7bdc1f66, + 0xc6d8754736c771b2, + 0x2d80c759ba5a2ae7, + 0x138a20df4b03cc1a, + 0xc22d07fe68e93024, + 0xd1dc474d3b433133, + 0xc22aa5e75044e5c, + 0xf657c6fbf9c17ebf, + 0xc591a794a58660d, + 0x2261850ee1453281, + 0xd17d3bd3b7f5efb4, + 0xf00cec8ec507d01, + 0x2a6a775657a00ae6, + 0x5f098a12ff470719, + 0x409d194e7b5c5afa, + 0x1d66478e982af5b, + 0xda425a5b5e01ca3f, + 0xf77e4f78747e903c, + 0x177d49f73732c6fc, + 0xa9618fecabe0e1f4, + 0xba5337eac90bd080, + 0x66fececdbc35d4e7, + 0xa4cd583203d9206f, + 0x98391632ceeca596, + 0x4946b76e1236ad3f, + 0xa0dec64e60e711a1, + 0xfcb41ed3605013, + 0x8ca8f9692ae1e3a9, + 0xd3078bfc28cc1baf, + 0xf0536f764e982f82, + 0x3125f1a2656, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[7], + nqr.pow(vec![ + 0x742754a1f22fdb, + 0x2a1955c2dec3a702, + 0x9747b28c796d134e, + 0xc113a0411f59db79, + 0x3bb0fa929853bfc1, + 0x28c3c25f8f6fb487, + 0xbc2b6c99d3045b34, + 0x98fb67d6badde1fd, + 0x48841d76a24d2073, + 0xd49891145fe93ae6, + 0xc772b9c8e74d4099, + 0xccf4e7b9907755bb, + 0x9cf47b25d42fd908, + 0x5616a0c347fc445d, + 0xff93b7a7ad1b8a6d, + 0xac2099256b78a77a, + 0x7804a95b02892e1c, + 0x5cf59ca7bfd69776, + 0xa7023502acd3c866, + 0xc76f4982fcf8f37, + 0x51862a5a57ac986e, + 0x38b80ed72b1b1023, + 0x4a291812066a61e1, + 0xcd8a685eff45631, + 0x3f40f708764e4fa5, + 0x8aa0441891285092, + 0x9eff60d71cdf0a9, + 0x4fdd9d56517e2bfa, + 0x1f3c80d74a28bc85, + 0x24617417c064b648, + 0x7ddda1e4385d5088, + 0xf9e132b11dd32a16, + 0xcc957cb8ef66ab99, + 0xd4f206d37cb752c5, + 0x40de343f28ad616b, + 0x8d1f24379068f0e3, + 0x6f31d7947ea21137, + 0x27311f9c32184061, + 0x9eea0664cc78ce5f, + 0x7d4151f6fea9a0da, + 0x454096fa75bd571a, + 0x4fe0f20ecb, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[8], + nqr.pow(vec![ + 0x802f5720d0b25710, + 0x6714f0a258b85c7c, + 0x31394c90afdf16e, + 0xe9d2b0c64f957b19, + 0xe67c0d9c5e7903ee, + 0x3156fdc5443ea8ef, + 0x7c4c50524d88c892, + 0xc99dc8990c0ad244, + 0xd37ababf3649a896, + 0x76fe4b838ff7a20c, + 0xcf69ee2cec728db3, + 0xb83535548e5f41, + 0x371147684ccb0c23, + 0x194f6f4fa500db52, + 0xc4571dc78a4c5374, + 0xe4d46d479999ca97, + 0x76b6785a615a151c, + 0xcceb8bcea7eaf8c1, + 0x80d87a6fbe5ae687, + 0x6a97ddddb85ce85, + 0xd783958f26034204, + 0x7144506f2e2e8590, + 0x948693d377aef166, + 0x8364621ed6f96056, + 0xf021777c4c09ee2d, + 0xc6cf5e746ecd50b, + 0xa2337b7aa22743df, + 0xae753f8bbacab39c, + 0xfc782a9e34d3c1cc, + 0x21b827324fe494d9, + 0x5692ce350ed03b38, + 0xf323a2b3cd0481b0, + 0xe859c97a4ccad2e3, + 0x48434b70381e4503, + 0x46042d62e4132ed8, + 0x48c4d6f56122e2f2, + 0xf87711ab9f5c1af7, + 0xb14b7a054759b469, + 0x8eb0a96993ffa9aa, + 0x9b21fb6fc58b760c, + 0xf3abdd115d2e7d25, + 0xf7beac3d4d12409c, + 0x40a5585cce69bf03, + 0x697881e1ba22d5a8, + 0x3d6c04e6ad373fd9, + 0x849871bf627be886, + 0x550f4b9b71b28ef9, + 0x81d2e0d78, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[9], + nqr.pow(vec![ + 0x4af4accf7de0b977, + 0x742485e21805b4ee, + 0xee388fbc4ac36dec, + 0x1e199da57ad178a, + 0xc27c12b292c6726a, + 0x162e6ed84505b5e8, + 0xe191683f336e09df, + 0x17deb7e8d1e0fce6, + 0xd944f19ad06f5836, + 0x4c5f5e59f6276026, + 0xf1ba9c7c148a38a8, + 0xd205fe2dba72b326, + 0x9a2cf2a4c289824e, + 0x4f47ad512c39e24d, + 0xc5894d984000ea09, + 0x2974c03ff7cf01fa, + 0xfcd243b48cb99a22, + 0x2b5150c9313ac1e8, + 0x9089f37c7fc80eda, + 0x989540cc9a7aea56, + 0x1ab1d4e337e63018, + 0x42b546c30d357e43, + 0x1c6abc04f76233d9, + 0x78b3b8d88bf73e47, + 0x151c4e4c45dc68e6, + 0x519a79c4f54397ed, + 0x93f5b51535a127c5, + 0x5fc51b6f52fa153e, + 0x2e0504f2d4a965c3, + 0xc85bd3a3da52bffe, + 0x98c60957a46a89ef, + 0x48c03b5976b91cae, + 0xc6598040a0a61438, + 0xbf0b49dc255953af, + 0xb78dff905b628ab4, + 0x68140b797ba74ab8, + 0x116cf037991d1143, + 0x2f7fe82e58acb0b8, + 0xc20bf7a8f7be5d45, + 0x86c2905c338d5709, + 0xff13a3ae6c8ace3d, + 0xb6f95e2282d08337, + 0xd49f7b313e9cbf29, + 0xf794517193a1ce8c, + 0x39641fecb596a874, + 0x411c4c4edf462fb3, + 0x3f8cd55c10cf25b4, + 0x2bdd7ea165e860b6, + 0xacd7d2cef4caa193, + 0x6558a1d09a05f96, + 0x1f52b5f5b546fc20, + 0x4ee22a5a8c250c12, + 0xd3a63a54a205b6b3, + 0xd2ff5be8, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[10], + nqr.pow(vec![ + 0xe5953a4f96cdda44, + 0x336b2d734cbc32bb, + 0x3f79bfe3cd7410e, + 0x267ae19aaa0f0332, + 0x85a9c4db78d5c749, + 0x90996b046b5dc7d8, + 0x8945eae9820afc6a, + 0x2644ddea2b036bd, + 0x39898e35ac2e3819, + 0x2574eab095659ab9, + 0x65953d51ac5ea798, + 0xc6b8c7afe6752466, + 0x40e9e993e9286544, + 0x7e0ad34ad9700ea0, + 0xac1015eba2c69222, + 0x24f057a19239b5d8, + 0x2043b48c8a3767eb, + 0x1117c124a75d7ff4, + 0x433cfd1a09fb3ce7, + 0x25b087ce4bcf7fb, + 0xbcee0dc53a3e5bdb, + 0xbffda040cf028735, + 0xf7cf103a25512acc, + 0x31d4ecda673130b9, + 0xea0906dab18461e6, + 0x5a40585a5ac3050d, + 0x803358fc14fd0eda, + 0x3678ca654eada770, + 0x7b91a1293a45e33e, + 0xcd5e5b8ea8530e43, + 0x21ae563ab34da266, + 0xecb00dad60df8894, + 0x77fe53e652facfef, + 0x9b7d1ad0b00244ec, + 0xe695df5ca73f801, + 0x23cdb21feeab0149, + 0x14de113e7ea810d9, + 0x52600cd958dac7e7, + 0xc83392c14667e488, + 0x9f808444bc1717fc, + 0x56facb4bcf7c788f, + 0x8bcad53245fc3ca0, + 0xdef661e83f27d81c, + 0x37d4ebcac9ad87e5, + 0x6fe8b24f5cdb9324, + 0xee08a26c1197654c, + 0xc98b22f65f237e9a, + 0xf54873a908ed3401, + 0x6e1cb951d41f3f3, + 0x290b2250a54e8df6, + 0x7f36d51eb1db669e, + 0xb08c7ed81a6ee43e, + 0x95e1c90fb092f680, + 0x429e4afd0e8b820, + 0x2c14a83ee87d715c, + 0xf37267575cfc8af5, + 0xb99e9afeda3c2c30, + 0x8f0f69da75792d5a, + 0x35074a85a533c73, + 0x156ed119, + ]) + ); + assert_eq!( + FROBENIUS_COEFF_FQ12_C1[11], + nqr.pow(vec![ + 0x107db680942de533, + 0x6262b24d2052393b, + 0x6136df824159ebc, + 0xedb052c9970c5deb, + 0xca813aea916c3777, + 0xf49dacb9d76c1788, + 0x624941bd372933bb, + 0xa5e60c2520638331, + 0xb38b661683411074, + 0x1d2c9af4c43d962b, + 0x17d807a0f14aa830, + 0x6e6581a51012c108, + 0x668a537e5b35e6f5, + 0x6c396cf3782dca5d, + 0x33b679d1bff536ed, + 0x736cce41805d90aa, + 0x8a562f369eb680bf, + 0x9f61aa208a11ded8, + 0x43dd89dd94d20f35, + 0xcf84c6610575c10a, + 0x9f318d49cf2fe8e6, + 0xbbc6e5f25a6e434e, + 0x6528c433d11d987b, + 0xffced71cc48c0e8a, + 0x4cbb1474f4cb2a26, + 0x66a035c0b28b7231, + 0xa6f2875faa1a82ae, + 0xdd1ea3deff818b02, + 0xe0cfdf0dcdecf701, + 0x9aefa231f2f6d23, + 0xfb251297efa06746, + 0x5a40d367df985538, + 0x1ea31d69ab506fed, + 0xc64ea8280e89a73f, + 0x969acf9f2d4496f4, + 0xe84c9181ee60c52c, + 0xc60f27fc19fc6ca4, + 0x760b33d850154048, + 0x84f69080f66c8457, + 0xc0192ba0fabf640e, + 0xd2c338765c23a3a8, + 0xa7838c20f02cec6c, + 0xb7cf01d020572877, + 0xd63ffaeba0be200a, + 0xf7492baeb5f041ac, + 0x8602c5212170d117, + 0xad9b2e83a5a42068, + 0x2461829b3ba1083e, + 0x7c34650da5295273, + 0xdc824ba800a8265a, + 0xd18d9b47836af7b2, + 0x3af78945c58cbf4d, + 0x7ed9575b8596906c, + 0x6d0c133895009a66, + 0x53bc1247ea349fe1, + 0x6b3063078d41aa7a, + 0x6184acd8cd880b33, + 0x76f4d15503fd1b96, + 0x7a9afd61eef25746, + 0xce974aadece60609, + 0x88ca59546a8ceafd, + 0x6d29391c41a0ac07, + 0x443843a60e0f46a6, + 0xa1590f62fd2602c7, + 0x536d5b15b514373f, + 0x22d582b, + ]) + ); +} + +#[test] +fn test_neg_one() { + let mut o = Fq::one(); + o.negate(); + + assert_eq!(NEGATIVE_ONE, o); +} + +#[cfg(test)] +use rand::{Rand, SeedableRng, XorShiftRng}; + +#[test] +fn test_fq_repr_ordering() { + fn assert_equality(a: FqRepr, b: FqRepr) { + assert_eq!(a, b); + assert!(a.cmp(&b) == Ordering::Equal); + } + + fn assert_lt(a: FqRepr, b: FqRepr) { + assert!(a < b); + assert!(b > a); + } + + assert_equality( + FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), + FqRepr([9999, 9999, 9999, 9999, 9999, 9999]), + ); + assert_equality( + FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), + FqRepr([9999, 9998, 9999, 9999, 9999, 9999]), + ); + assert_equality( + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + ); + assert_lt( + FqRepr([9999, 9999, 9999, 9997, 9999, 9998]), + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + ); + assert_lt( + FqRepr([9999, 9999, 9999, 9997, 9998, 9999]), + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + ); + assert_lt( + FqRepr([9, 9999, 9999, 9997, 9998, 9999]), + FqRepr([9999, 9999, 9999, 9997, 9999, 9999]), + ); +} + +#[test] +fn test_fq_repr_from() { + assert_eq!(FqRepr::from(100), FqRepr([100, 0, 0, 0, 0, 0])); +} + +#[test] +fn test_fq_repr_is_odd() { + assert!(!FqRepr::from(0).is_odd()); + assert!(FqRepr::from(0).is_even()); + assert!(FqRepr::from(1).is_odd()); + assert!(!FqRepr::from(1).is_even()); + assert!(!FqRepr::from(324834872).is_odd()); + assert!(FqRepr::from(324834872).is_even()); + assert!(FqRepr::from(324834873).is_odd()); + assert!(!FqRepr::from(324834873).is_even()); +} + +#[test] +fn test_fq_repr_is_zero() { + assert!(FqRepr::from(0).is_zero()); + assert!(!FqRepr::from(1).is_zero()); + assert!(!FqRepr([0, 0, 0, 0, 1, 0]).is_zero()); +} + +#[test] +fn test_fq_repr_div2() { + let mut a = FqRepr([ + 0x8b0ad39f8dd7482a, + 0x147221c9a7178b69, + 0x54764cb08d8a6aa0, + 0x8519d708e1d83041, + 0x41f82777bd13fdb, + 0xf43944578f9b771b, + ]); + a.div2(); + assert_eq!( + a, + FqRepr([ + 0xc58569cfc6eba415, + 0xa3910e4d38bc5b4, + 0xaa3b265846c53550, + 0xc28ceb8470ec1820, + 0x820fc13bbde89fed, + 0x7a1ca22bc7cdbb8d + ]) + ); + for _ in 0..10 { + a.div2(); + } + assert_eq!( + a, + FqRepr([ + 0x6d31615a73f1bae9, + 0x54028e443934e2f1, + 0x82a8ec99611b14d, + 0xfb70a33ae11c3b06, + 0xe36083f04eef7a27, + 0x1e87288af1f36e + ]) + ); + for _ in 0..300 { + a.div2(); + } + assert_eq!(a, FqRepr([0x7288af1f36ee3608, 0x1e8, 0x0, 0x0, 0x0, 0x0])); + for _ in 0..50 { + a.div2(); + } + assert_eq!(a, FqRepr([0x7a1ca2, 0x0, 0x0, 0x0, 0x0, 0x0])); + for _ in 0..22 { + a.div2(); + } + assert_eq!(a, FqRepr([0x1, 0x0, 0x0, 0x0, 0x0, 0x0])); + a.div2(); + assert!(a.is_zero()); +} + +#[test] +fn test_fq_repr_shr() { + let mut a = FqRepr([ + 0xaa5cdd6172847ffd, + 0x43242c06aed55287, + 0x9ddd5b312f3dd104, + 0xc5541fd48046b7e7, + 0x16080cf4071e0b05, + 0x1225f2901aea514e, + ]); + a.shr(0); + assert_eq!( + a, + FqRepr([ + 0xaa5cdd6172847ffd, + 0x43242c06aed55287, + 0x9ddd5b312f3dd104, + 0xc5541fd48046b7e7, + 0x16080cf4071e0b05, + 0x1225f2901aea514e + ]) + ); + a.shr(1); + assert_eq!( + a, + FqRepr([ + 0xd52e6eb0b9423ffe, + 0x21921603576aa943, + 0xceeead98979ee882, + 0xe2aa0fea40235bf3, + 0xb04067a038f0582, + 0x912f9480d7528a7 + ]) + ); + a.shr(50); + assert_eq!( + a, + FqRepr([ + 0x8580d5daaa50f54b, + 0xab6625e7ba208864, + 0x83fa9008d6fcf3bb, + 0x19e80e3c160b8aa, + 0xbe52035d4a29c2c1, + 0x244 + ]) + ); + a.shr(130); + assert_eq!( + a, + FqRepr([ + 0xa0fea40235bf3cee, + 0x4067a038f0582e2a, + 0x2f9480d7528a70b0, + 0x91, + 0x0, + 0x0 + ]) + ); + a.shr(64); + assert_eq!( + a, + FqRepr([0x4067a038f0582e2a, 0x2f9480d7528a70b0, 0x91, 0x0, 0x0, 0x0]) + ); +} + +#[test] +fn test_fq_repr_mul2() { + let mut a = FqRepr::from(23712937547); + a.mul2(); + assert_eq!(a, FqRepr([0xb0acd6c96, 0x0, 0x0, 0x0, 0x0, 0x0])); + for _ in 0..60 { + a.mul2(); + } + assert_eq!( + a, + FqRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0, 0x0, 0x0]) + ); + for _ in 0..300 { + a.mul2(); + } + assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0xcd6c960000000000])); + for _ in 0..17 { + a.mul2(); + } + assert_eq!(a, FqRepr([0x0, 0x0, 0x0, 0x0, 0x0, 0x2c00000000000000])); + for _ in 0..6 { + a.mul2(); + } + assert!(a.is_zero()); +} + +#[test] +fn test_fq_repr_num_bits() { + let mut a = FqRepr::from(0); + assert_eq!(0, a.num_bits()); + a = FqRepr::from(1); + for i in 1..385 { + assert_eq!(i, a.num_bits()); + a.mul2(); + } + assert_eq!(0, a.num_bits()); +} + +#[test] +fn test_fq_repr_sub_noborrow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FqRepr([ + 0x827a4a08041ebd9, + 0x3c239f3dcc8f0d6b, + 0x9ab46a912d555364, + 0x196936b17b43910b, + 0xad0eb3948a5c34fd, + 0xd56f7b5ab8b5ce8, + ]); + t.sub_noborrow(&FqRepr([ + 0xc7867917187ca02b, + 0x5d75679d4911ffef, + 0x8c5b3e48b1a71c15, + 0x6a427ae846fd66aa, + 0x7a37e7265ee1eaf9, + 0x7c0577a26f59d5, + ])); + assert!( + t == FqRepr([ + 0x40a12b8967c54bae, + 0xdeae37a0837d0d7b, + 0xe592c487bae374e, + 0xaf26bbc934462a61, + 0x32d6cc6e2b7a4a03, + 0xcdaf23e091c0313 + ]) + ); + + for _ in 0..1000 { + let mut a = FqRepr::rand(&mut rng); + a.0[5] >>= 30; + let mut b = a; + for _ in 0..10 { + b.mul2(); + } + let mut c = b; + for _ in 0..10 { + c.mul2(); + } + + assert!(a < b); + assert!(b < c); + + let mut csub_ba = c; + csub_ba.sub_noborrow(&b); + csub_ba.sub_noborrow(&a); + + let mut csub_ab = c; + csub_ab.sub_noborrow(&a); + csub_ab.sub_noborrow(&b); + + assert_eq!(csub_ab, csub_ba); + } + + // Subtracting q+1 from q should produce -1 (mod 2**384) + let mut qplusone = FqRepr([ + 0xb9feffffffffaaab, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + ]); + qplusone.sub_noborrow(&FqRepr([ + 0xb9feffffffffaaac, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + ])); + assert_eq!( + qplusone, + FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ]) + ); +} + +#[test] +fn test_fq_repr_add_nocarry() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FqRepr([ + 0x827a4a08041ebd9, + 0x3c239f3dcc8f0d6b, + 0x9ab46a912d555364, + 0x196936b17b43910b, + 0xad0eb3948a5c34fd, + 0xd56f7b5ab8b5ce8, + ]); + t.add_nocarry(&FqRepr([ + 0xc7867917187ca02b, + 0x5d75679d4911ffef, + 0x8c5b3e48b1a71c15, + 0x6a427ae846fd66aa, + 0x7a37e7265ee1eaf9, + 0x7c0577a26f59d5, + ])); + assert!( + t == FqRepr([ + 0xcfae1db798be8c04, + 0x999906db15a10d5a, + 0x270fa8d9defc6f79, + 0x83abb199c240f7b6, + 0x27469abae93e1ff6, + 0xdd2fd2d4dfab6be + ]) + ); + + // Test for the associativity of addition. + for _ in 0..1000 { + let mut a = FqRepr::rand(&mut rng); + let mut b = FqRepr::rand(&mut rng); + let mut c = FqRepr::rand(&mut rng); + + // Unset the first few bits, so that overflow won't occur. + a.0[5] >>= 3; + b.0[5] >>= 3; + c.0[5] >>= 3; + + let mut abc = a; + abc.add_nocarry(&b); + abc.add_nocarry(&c); + + let mut acb = a; + acb.add_nocarry(&c); + acb.add_nocarry(&b); + + let mut bac = b; + bac.add_nocarry(&a); + bac.add_nocarry(&c); + + let mut bca = b; + bca.add_nocarry(&c); + bca.add_nocarry(&a); + + let mut cab = c; + cab.add_nocarry(&a); + cab.add_nocarry(&b); + + let mut cba = c; + cba.add_nocarry(&b); + cba.add_nocarry(&a); + + assert_eq!(abc, acb); + assert_eq!(abc, bac); + assert_eq!(abc, bca); + assert_eq!(abc, cab); + assert_eq!(abc, cba); + } + + // Adding 1 to (2^384 - 1) should produce zero + let mut x = FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]); + x.add_nocarry(&FqRepr::from(1)); + assert!(x.is_zero()); +} + +#[test] +fn test_fq_is_valid() { + let mut a = Fq(MODULUS); + assert!(!a.is_valid()); + a.0.sub_noborrow(&FqRepr::from(1)); + assert!(a.is_valid()); + assert!(Fq(FqRepr::from(0)).is_valid()); + assert!( + Fq(FqRepr([ + 0xdf4671abd14dab3e, + 0xe2dc0c9f534fbd33, + 0x31ca6c880cc444a6, + 0x257a67e70ef33359, + 0xf9b29e493f899b36, + 0x17c8be1800b9f059 + ])).is_valid() + ); + assert!(!Fq(FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ])).is_valid()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = Fq::rand(&mut rng); + assert!(a.is_valid()); + } +} + +#[test] +fn test_fq_add_assign() { + { + // Random number + let mut tmp = Fq(FqRepr([ + 0x624434821df92b69, + 0x503260c04fd2e2ea, + 0xd9df726e0d16e8ce, + 0xfbcb39adfd5dfaeb, + 0x86b8a22b0c88b112, + 0x165a2ed809e4201b, + ])); + assert!(tmp.is_valid()); + // Test that adding zero has no effect. + tmp.add_assign(&Fq(FqRepr::from(0))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x624434821df92b69, + 0x503260c04fd2e2ea, + 0xd9df726e0d16e8ce, + 0xfbcb39adfd5dfaeb, + 0x86b8a22b0c88b112, + 0x165a2ed809e4201b + ])) + ); + // Add one and test for the result. + tmp.add_assign(&Fq(FqRepr::from(1))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x624434821df92b6a, + 0x503260c04fd2e2ea, + 0xd9df726e0d16e8ce, + 0xfbcb39adfd5dfaeb, + 0x86b8a22b0c88b112, + 0x165a2ed809e4201b + ])) + ); + // Add another random number that exercises the reduction. + tmp.add_assign(&Fq(FqRepr([ + 0x374d8f8ea7a648d8, + 0xe318bb0ebb8bfa9b, + 0x613d996f0a95b400, + 0x9fac233cb7e4fef1, + 0x67e47552d253c52, + 0x5c31b227edf25da, + ]))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0xdf92c410c59fc997, + 0x149f1bd05a0add85, + 0xd3ec393c20fba6ab, + 0x37001165c1bde71d, + 0x421b41c9f662408e, + 0x21c38104f435f5b + ])) + ); + // Add one to (q - 1) and test for the result. + tmp = Fq(FqRepr([ + 0xb9feffffffffaaaa, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a, + ])); + tmp.add_assign(&Fq(FqRepr::from(1))); + assert!(tmp.0.is_zero()); + // Add a random number to another one such that the result is q - 1 + tmp = Fq(FqRepr([ + 0x531221a410efc95b, + 0x72819306027e9717, + 0x5ecefb937068b746, + 0x97de59cd6feaefd7, + 0xdc35c51158644588, + 0xb2d176c04f2100, + ])); + tmp.add_assign(&Fq(FqRepr([ + 0x66ecde5bef0fe14f, + 0xac2a6cf8aed568e8, + 0x861d70d86483edd, + 0xcc98f1b7839a22e8, + 0x6ee5e2a4eae7674e, + 0x194e40737930c599, + ]))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0xb9feffffffffaaaa, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])) + ); + // Add one to the result and test for it. + tmp.add_assign(&Fq(FqRepr::from(1))); + assert!(tmp.0.is_zero()); + } + + // Test associativity + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Generate a, b, c and ensure (a + b) + c == a + (b + c). + let a = Fq::rand(&mut rng); + let b = Fq::rand(&mut rng); + let c = Fq::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + + let mut tmp2 = b; + tmp2.add_assign(&c); + tmp2.add_assign(&a); + + assert!(tmp1.is_valid()); + assert!(tmp2.is_valid()); + assert_eq!(tmp1, tmp2); + } +} + +#[test] +fn test_fq_sub_assign() { + { + // Test arbitrary subtraction that tests reduction. + let mut tmp = Fq(FqRepr([ + 0x531221a410efc95b, + 0x72819306027e9717, + 0x5ecefb937068b746, + 0x97de59cd6feaefd7, + 0xdc35c51158644588, + 0xb2d176c04f2100, + ])); + tmp.sub_assign(&Fq(FqRepr([ + 0x98910d20877e4ada, + 0x940c983013f4b8ba, + 0xf677dc9b8345ba33, + 0xbef2ce6b7f577eba, + 0xe1ae288ac3222c44, + 0x5968bb602790806, + ]))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x748014838971292c, + 0xfd20fad49fddde5c, + 0xcf87f198e3d3f336, + 0x3d62d6e6e41883db, + 0x45a3443cd88dc61b, + 0x151d57aaf755ff94 + ])) + ); + + // Test the opposite subtraction which doesn't test reduction. + tmp = Fq(FqRepr([ + 0x98910d20877e4ada, + 0x940c983013f4b8ba, + 0xf677dc9b8345ba33, + 0xbef2ce6b7f577eba, + 0xe1ae288ac3222c44, + 0x5968bb602790806, + ])); + tmp.sub_assign(&Fq(FqRepr([ + 0x531221a410efc95b, + 0x72819306027e9717, + 0x5ecefb937068b746, + 0x97de59cd6feaefd7, + 0xdc35c51158644588, + 0xb2d176c04f2100, + ]))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x457eeb7c768e817f, + 0x218b052a117621a3, + 0x97a8e10812dd02ed, + 0x2714749e0f6c8ee3, + 0x57863796abde6bc, + 0x4e3ba3f4229e706 + ])) + ); + + // Test for sensible results with zero + tmp = Fq(FqRepr::from(0)); + tmp.sub_assign(&Fq(FqRepr::from(0))); + assert!(tmp.is_zero()); + + tmp = Fq(FqRepr([ + 0x98910d20877e4ada, + 0x940c983013f4b8ba, + 0xf677dc9b8345ba33, + 0xbef2ce6b7f577eba, + 0xe1ae288ac3222c44, + 0x5968bb602790806, + ])); + tmp.sub_assign(&Fq(FqRepr::from(0))); + assert_eq!( + tmp, + Fq(FqRepr([ + 0x98910d20877e4ada, + 0x940c983013f4b8ba, + 0xf677dc9b8345ba33, + 0xbef2ce6b7f577eba, + 0xe1ae288ac3222c44, + 0x5968bb602790806 + ])) + ); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure that (a - b) + (b - a) = 0. + let a = Fq::rand(&mut rng); + let b = Fq::rand(&mut rng); + + let mut tmp1 = a; + tmp1.sub_assign(&b); + + let mut tmp2 = b; + tmp2.sub_assign(&a); + + tmp1.add_assign(&tmp2); + assert!(tmp1.is_zero()); + } +} + +#[test] +fn test_fq_mul_assign() { + let mut tmp = Fq(FqRepr([ + 0xcc6200000020aa8a, + 0x422800801dd8001a, + 0x7f4f5e619041c62c, + 0x8a55171ac70ed2ba, + 0x3f69cc3a3d07d58b, + 0xb972455fd09b8ef, + ])); + tmp.mul_assign(&Fq(FqRepr([ + 0x329300000030ffcf, + 0x633c00c02cc40028, + 0xbef70d925862a942, + 0x4f7fa2a82a963c17, + 0xdf1eb2575b8bc051, + 0x1162b680fb8e9566, + ]))); + assert!( + tmp == Fq(FqRepr([ + 0x9dc4000001ebfe14, + 0x2850078997b00193, + 0xa8197f1abb4d7bf, + 0xc0309573f4bfe871, + 0xf48d0923ffaf7620, + 0x11d4b58c7a926e66 + ])) + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * b) * c = a * (b * c) + let a = Fq::rand(&mut rng); + let b = Fq::rand(&mut rng); + let c = Fq::rand(&mut rng); + + let mut tmp1 = a; + tmp1.mul_assign(&b); + tmp1.mul_assign(&c); + + let mut tmp2 = b; + tmp2.mul_assign(&c); + tmp2.mul_assign(&a); + + assert_eq!(tmp1, tmp2); + } + + for _ in 0..1000000 { + // Ensure that r * (a + b + c) = r*a + r*b + r*c + + let r = Fq::rand(&mut rng); + let mut a = Fq::rand(&mut rng); + let mut b = Fq::rand(&mut rng); + let mut c = Fq::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + tmp1.mul_assign(&r); + + a.mul_assign(&r); + b.mul_assign(&r); + c.mul_assign(&r); + + a.add_assign(&b); + a.add_assign(&c); + + assert_eq!(tmp1, a); + } +} + +#[test] +fn test_fq_squaring() { + let mut a = Fq(FqRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x19ffffffffffffff, + ])); + assert!(a.is_valid()); + a.square(); + assert_eq!( + a, + Fq::from_repr(FqRepr([ + 0x1cfb28fe7dfbbb86, + 0x24cbe1731577a59, + 0xcce1d4edc120e66e, + 0xdc05c659b4e15b27, + 0x79361e5a802c6a23, + 0x24bcbe5d51b9a6f + ])).unwrap() + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * a) = a^2 + let a = Fq::rand(&mut rng); + + let mut tmp = a; + tmp.square(); + + let mut tmp2 = a; + tmp2.mul_assign(&a); + + assert_eq!(tmp, tmp2); + } +} + +#[test] +fn test_fq_inverse() { + assert!(Fq::zero().inverse().is_none()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let one = Fq::one(); + + for _ in 0..1000 { + // Ensure that a * a^-1 = 1 + let mut a = Fq::rand(&mut rng); + let ainv = a.inverse().unwrap(); + a.mul_assign(&ainv); + assert_eq!(a, one); + } +} + +#[test] +fn test_fq_double() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure doubling a is equivalent to adding a to itself. + let mut a = Fq::rand(&mut rng); + let mut b = a; + b.add_assign(&a); + a.double(); + assert_eq!(a, b); + } +} + +#[test] +fn test_fq_negate() { + { + let mut a = Fq::zero(); + a.negate(); + + assert!(a.is_zero()); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure (a - (-a)) = 0. + let mut a = Fq::rand(&mut rng); + let mut b = a; + b.negate(); + a.add_assign(&b); + + assert!(a.is_zero()); + } +} + +#[test] +fn test_fq_pow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for i in 0..1000 { + // Exponentiate by various small numbers and ensure it consists with repeated + // multiplication. + let a = Fq::rand(&mut rng); + let target = a.pow(&[i]); + let mut c = Fq::one(); + for _ in 0..i { + c.mul_assign(&a); + } + assert_eq!(c, target); + } + + for _ in 0..1000 { + // Exponentiating by the modulus should have no effect in a prime field. + let a = Fq::rand(&mut rng); + + assert_eq!(a, a.pow(Fq::char())); + } +} + +#[test] +fn test_fq_sqrt() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + assert_eq!(Fq::zero().sqrt().unwrap(), Fq::zero()); + + for _ in 0..1000 { + // Ensure sqrt(a^2) = a or -a + let a = Fq::rand(&mut rng); + let mut nega = a; + nega.negate(); + let mut b = a; + b.square(); + + let b = b.sqrt().unwrap(); + + assert!(a == b || nega == b); + } + + for _ in 0..1000 { + // Ensure sqrt(a)^2 = a for random a + let a = Fq::rand(&mut rng); + + if let Some(mut tmp) = a.sqrt() { + tmp.square(); + + assert_eq!(a, tmp); + } + } +} + +#[test] +fn test_fq_from_into_repr() { + // q + 1 should not be in the field + assert!( + Fq::from_repr(FqRepr([ + 0xb9feffffffffaaac, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])).is_err() + ); + + // q should not be in the field + assert!(Fq::from_repr(Fq::char()).is_err()); + + // Multiply some arbitrary representations to see if the result is as expected. + let a = FqRepr([ + 0x4a49dad4ff6cde2d, + 0xac62a82a8f51cd50, + 0x2b1f41ab9f36d640, + 0x908a387f480735f1, + 0xae30740c08a875d7, + 0x6c80918a365ef78, + ]); + let mut a_fq = Fq::from_repr(a).unwrap(); + let b = FqRepr([ + 0xbba57917c32f0cf0, + 0xe7f878cf87f05e5d, + 0x9498b4292fd27459, + 0xd59fd94ee4572cfa, + 0x1f607186d5bb0059, + 0xb13955f5ac7f6a3, + ]); + let b_fq = Fq::from_repr(b).unwrap(); + let c = FqRepr([ + 0xf5f70713b717914c, + 0x355ea5ac64cbbab1, + 0xce60dd43417ec960, + 0xf16b9d77b0ad7d10, + 0xa44c204c1de7cdb7, + 0x1684487772bc9a5a, + ]); + a_fq.mul_assign(&b_fq); + assert_eq!(a_fq.into_repr(), c); + + // Zero should be in the field. + assert!(Fq::from_repr(FqRepr::from(0)).unwrap().is_zero()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Try to turn Fq elements into representations and back again, and compare. + let a = Fq::rand(&mut rng); + let a_repr = a.into_repr(); + let b_repr = FqRepr::from(a); + assert_eq!(a_repr, b_repr); + let a_again = Fq::from_repr(a_repr).unwrap(); + + assert_eq!(a, a_again); + } +} + +#[test] +fn test_fq_repr_display() { + assert_eq!( + format!("{}", FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])), + "0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24".to_string() + ); + assert_eq!( + format!("{}", FqRepr([0xb4171485fd8622dd, 0x864229a6edec7ec5, 0xc57f7bdcf8dfb707, 0x6db7ff0ecea4584a, 0xf8d8578c4a57132d, 0x6eb66d42d9fcaaa])), + "0x06eb66d42d9fcaaaf8d8578c4a57132d6db7ff0ecea4584ac57f7bdcf8dfb707864229a6edec7ec5b4171485fd8622dd".to_string() + ); + assert_eq!( + format!("{}", FqRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() + ); + assert_eq!( + format!("{}", FqRepr([0, 0, 0, 0, 0, 0])), + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string() + ); +} + +#[test] +fn test_fq_display() { + assert_eq!( + format!("{}", Fq::from_repr(FqRepr([0xa956babf9301ea24, 0x39a8f184f3535c7b, 0xb38d35b3f6779585, 0x676cc4eef4c46f2c, 0xb1d4aad87651e694, 0x1947f0d5f4fe325a])).unwrap()), + "Fq(0x1947f0d5f4fe325ab1d4aad87651e694676cc4eef4c46f2cb38d35b3f677958539a8f184f3535c7ba956babf9301ea24)".to_string() + ); + assert_eq!( + format!("{}", Fq::from_repr(FqRepr([0xe28e79396ac2bbf8, 0x413f6f7f06ea87eb, 0xa4b62af4a792a689, 0xb7f89f88f59c1dc5, 0x9a551859b1e43a9a, 0x6c9f5a1060de974])).unwrap()), + "Fq(0x06c9f5a1060de9749a551859b1e43a9ab7f89f88f59c1dc5a4b62af4a792a689413f6f7f06ea87ebe28e79396ac2bbf8)".to_string() + ); +} + +#[test] +fn test_fq_num_bits() { + assert_eq!(Fq::NUM_BITS, 381); + assert_eq!(Fq::CAPACITY, 380); +} + +#[test] +fn test_fq_root_of_unity() { + assert_eq!(Fq::S, 1); + assert_eq!( + Fq::multiplicative_generator(), + Fq::from_repr(FqRepr::from(2)).unwrap() + ); + assert_eq!( + Fq::multiplicative_generator().pow([ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d + ]), + Fq::root_of_unity() + ); + assert_eq!(Fq::root_of_unity().pow([1 << Fq::S]), Fq::one()); + assert!(Fq::multiplicative_generator().sqrt().is_none()); +} + +#[test] +fn fq_field_tests() { + ::tests::field::random_field_tests::(); + ::tests::field::random_sqrt_tests::(); + ::tests::field::random_frobenius_tests::(Fq::char(), 13); + ::tests::field::from_str_tests::(); +} + +#[test] +fn test_fq_ordering() { + // FqRepr's ordering is well-tested, but we still need to make sure the Fq + // elements aren't being compared in Montgomery form. + for i in 0..100 { + assert!( + Fq::from_repr(FqRepr::from(i + 1)).unwrap() > Fq::from_repr(FqRepr::from(i)).unwrap() + ); + } +} + +#[test] +fn fq_repr_tests() { + ::tests::repr::random_repr_tests::(); +} + +#[test] +fn test_fq_legendre() { + use LegendreSymbol::*; + + assert_eq!(QuadraticResidue, Fq::one().legendre()); + assert_eq!(Zero, Fq::zero().legendre()); + + assert_eq!( + QuadraticNonResidue, + Fq::from_repr(FqRepr::from(2)).unwrap().legendre() + ); + assert_eq!( + QuadraticResidue, + Fq::from_repr(FqRepr::from(4)).unwrap().legendre() + ); + + let e = FqRepr([ + 0x52a112f249778642, + 0xd0bedb989b7991f, + 0xdad3b6681aa63c05, + 0xf2efc0bb4721b283, + 0x6057a98f18c24733, + 0x1022c2fd122889e4, + ]); + assert_eq!(QuadraticNonResidue, Fq::from_repr(e).unwrap().legendre()); + let e = FqRepr([ + 0x6dae594e53a96c74, + 0x19b16ca9ba64b37b, + 0x5c764661a59bfc68, + 0xaa346e9b31c60a, + 0x346059f9d87a9fa9, + 0x1d61ac6bfd5c88b, + ]); + assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre()); +} diff --git a/src/rust/pairing/src/bls12_381/fq12.rs b/src/rust/pairing/src/bls12_381/fq12.rs new file mode 100644 index 000000000..2bec0b184 --- /dev/null +++ b/src/rust/pairing/src/bls12_381/fq12.rs @@ -0,0 +1,189 @@ +use super::fq::FROBENIUS_COEFF_FQ12_C1; +use super::fq2::Fq2; +use super::fq6::Fq6; +use rand::{Rand, Rng}; +use Field; + +/// An element of Fq12, represented by c0 + c1 * w. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Fq12 { + pub c0: Fq6, + pub c1: Fq6, +} + +impl ::std::fmt::Display for Fq12 { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fq12({} + {} * w)", self.c0, self.c1) + } +} + +impl Rand for Fq12 { + fn rand(rng: &mut R) -> Self { + Fq12 { + c0: rng.gen(), + c1: rng.gen(), + } + } +} + +impl Fq12 { + pub fn conjugate(&mut self) { + self.c1.negate(); + } + + pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) { + let mut aa = self.c0; + aa.mul_by_01(c0, c1); + let mut bb = self.c1; + bb.mul_by_1(c4); + let mut o = *c1; + o.add_assign(c4); + self.c1.add_assign(&self.c0); + self.c1.mul_by_01(c0, &o); + self.c1.sub_assign(&aa); + self.c1.sub_assign(&bb); + self.c0 = bb; + self.c0.mul_by_nonresidue(); + self.c0.add_assign(&aa); + } +} + +impl Field for Fq12 { + fn zero() -> Self { + Fq12 { + c0: Fq6::zero(), + c1: Fq6::zero(), + } + } + + fn one() -> Self { + Fq12 { + c0: Fq6::one(), + c1: Fq6::zero(), + } + } + + fn is_zero(&self) -> bool { + self.c0.is_zero() && self.c1.is_zero() + } + + fn double(&mut self) { + self.c0.double(); + self.c1.double(); + } + + fn negate(&mut self) { + self.c0.negate(); + self.c1.negate(); + } + + fn add_assign(&mut self, other: &Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + } + + fn sub_assign(&mut self, other: &Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + } + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + + self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); + } + + fn square(&mut self) { + let mut ab = self.c0; + ab.mul_assign(&self.c1); + let mut c0c1 = self.c0; + c0c1.add_assign(&self.c1); + let mut c0 = self.c1; + c0.mul_by_nonresidue(); + c0.add_assign(&self.c0); + c0.mul_assign(&c0c1); + c0.sub_assign(&ab); + self.c1 = ab; + self.c1.add_assign(&ab); + ab.mul_by_nonresidue(); + c0.sub_assign(&ab); + self.c0 = c0; + } + + fn mul_assign(&mut self, other: &Self) { + let mut aa = self.c0; + aa.mul_assign(&other.c0); + let mut bb = self.c1; + bb.mul_assign(&other.c1); + let mut o = other.c0; + o.add_assign(&other.c1); + self.c1.add_assign(&self.c0); + self.c1.mul_assign(&o); + self.c1.sub_assign(&aa); + self.c1.sub_assign(&bb); + self.c0 = bb; + self.c0.mul_by_nonresidue(); + self.c0.add_assign(&aa); + } + + fn inverse(&self) -> Option { + let mut c0s = self.c0; + c0s.square(); + let mut c1s = self.c1; + c1s.square(); + c1s.mul_by_nonresidue(); + c0s.sub_assign(&c1s); + + c0s.inverse().map(|t| { + let mut tmp = Fq12 { c0: t, c1: t }; + tmp.c0.mul_assign(&self.c0); + tmp.c1.mul_assign(&self.c1); + tmp.c1.negate(); + + tmp + }) + } +} + +#[cfg(test)] +use rand::{SeedableRng, XorShiftRng}; + +#[test] +fn test_fq12_mul_by_014() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let c0 = Fq2::rand(&mut rng); + let c1 = Fq2::rand(&mut rng); + let c5 = Fq2::rand(&mut rng); + let mut a = Fq12::rand(&mut rng); + let mut b = a; + + a.mul_by_014(&c0, &c1, &c5); + b.mul_assign(&Fq12 { + c0: Fq6 { + c0: c0, + c1: c1, + c2: Fq2::zero(), + }, + c1: Fq6 { + c0: Fq2::zero(), + c1: c5, + c2: Fq2::zero(), + }, + }); + + assert_eq!(a, b); + } +} + +#[test] +fn fq12_field_tests() { + use PrimeField; + + ::tests::field::random_field_tests::(); + ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/src/rust/pairing/src/bls12_381/fq2.rs b/src/rust/pairing/src/bls12_381/fq2.rs new file mode 100644 index 000000000..18cd58068 --- /dev/null +++ b/src/rust/pairing/src/bls12_381/fq2.rs @@ -0,0 +1,908 @@ +use super::fq::{FROBENIUS_COEFF_FQ2_C1, Fq, NEGATIVE_ONE}; +use rand::{Rand, Rng}; +use {Field, SqrtField}; + +use std::cmp::Ordering; + +/// An element of Fq2, represented by c0 + c1 * u. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Fq2 { + pub c0: Fq, + pub c1: Fq, +} + +impl ::std::fmt::Display for Fq2 { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fq2({} + {} * u)", self.c0, self.c1) + } +} + +/// `Fq2` elements are ordered lexicographically. +impl Ord for Fq2 { + #[inline(always)] + fn cmp(&self, other: &Fq2) -> Ordering { + match self.c1.cmp(&other.c1) { + Ordering::Greater => Ordering::Greater, + Ordering::Less => Ordering::Less, + Ordering::Equal => self.c0.cmp(&other.c0), + } + } +} + +impl PartialOrd for Fq2 { + #[inline(always)] + fn partial_cmp(&self, other: &Fq2) -> Option { + Some(self.cmp(other)) + } +} + +impl Fq2 { + /// Multiply this element by the cubic and quadratic nonresidue 1 + u. + pub fn mul_by_nonresidue(&mut self) { + let t0 = self.c0; + self.c0.sub_assign(&self.c1); + self.c1.add_assign(&t0); + } + + /// Norm of Fq2 as extension field in i over Fq + pub fn norm(&self) -> Fq { + let mut t0 = self.c0; + let mut t1 = self.c1; + t0.square(); + t1.square(); + t1.add_assign(&t0); + + t1 + } +} + +impl Rand for Fq2 { + fn rand(rng: &mut R) -> Self { + Fq2 { + c0: rng.gen(), + c1: rng.gen(), + } + } +} + +impl Field for Fq2 { + fn zero() -> Self { + Fq2 { + c0: Fq::zero(), + c1: Fq::zero(), + } + } + + fn one() -> Self { + Fq2 { + c0: Fq::one(), + c1: Fq::zero(), + } + } + + fn is_zero(&self) -> bool { + self.c0.is_zero() && self.c1.is_zero() + } + + fn square(&mut self) { + let mut ab = self.c0; + ab.mul_assign(&self.c1); + let mut c0c1 = self.c0; + c0c1.add_assign(&self.c1); + let mut c0 = self.c1; + c0.negate(); + c0.add_assign(&self.c0); + c0.mul_assign(&c0c1); + c0.sub_assign(&ab); + self.c1 = ab; + self.c1.add_assign(&ab); + c0.add_assign(&ab); + self.c0 = c0; + } + + fn double(&mut self) { + self.c0.double(); + self.c1.double(); + } + + fn negate(&mut self) { + self.c0.negate(); + self.c1.negate(); + } + + fn add_assign(&mut self, other: &Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + } + + fn sub_assign(&mut self, other: &Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + } + + fn mul_assign(&mut self, other: &Self) { + let mut aa = self.c0; + aa.mul_assign(&other.c0); + let mut bb = self.c1; + bb.mul_assign(&other.c1); + let mut o = other.c0; + o.add_assign(&other.c1); + self.c1.add_assign(&self.c0); + self.c1.mul_assign(&o); + self.c1.sub_assign(&aa); + self.c1.sub_assign(&bb); + self.c0 = aa; + self.c0.sub_assign(&bb); + } + + fn inverse(&self) -> Option { + let mut t1 = self.c1; + t1.square(); + let mut t0 = self.c0; + t0.square(); + t0.add_assign(&t1); + t0.inverse().map(|t| { + let mut tmp = Fq2 { + c0: self.c0, + c1: self.c1, + }; + tmp.c0.mul_assign(&t); + tmp.c1.mul_assign(&t); + tmp.c1.negate(); + + tmp + }) + } + + fn frobenius_map(&mut self, power: usize) { + self.c1.mul_assign(&FROBENIUS_COEFF_FQ2_C1[power % 2]); + } +} + +impl SqrtField for Fq2 { + fn legendre(&self) -> ::LegendreSymbol { + self.norm().legendre() + } + + fn sqrt(&self) -> Option { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + + if self.is_zero() { + Some(Self::zero()) + } else { + // a1 = self^((q - 3) / 4) + let mut a1 = self.pow([ + 0xee7fbfffffffeaaa, + 0x7aaffffac54ffff, + 0xd9cc34a83dac3d89, + 0xd91dd2e13ce144af, + 0x92c6e9ed90d2eb35, + 0x680447a8e5ff9a6, + ]); + let mut alpha = a1; + alpha.square(); + alpha.mul_assign(self); + let mut a0 = alpha; + a0.frobenius_map(1); + a0.mul_assign(&alpha); + + let neg1 = Fq2 { + c0: NEGATIVE_ONE, + c1: Fq::zero(), + }; + + if a0 == neg1 { + None + } else { + a1.mul_assign(self); + + if alpha == neg1 { + a1.mul_assign(&Fq2 { + c0: Fq::zero(), + c1: Fq::one(), + }); + } else { + alpha.add_assign(&Fq2::one()); + // alpha = alpha^((q - 1) / 2) + alpha = alpha.pow([ + 0xdcff7fffffffd555, + 0xf55ffff58a9ffff, + 0xb39869507b587b12, + 0xb23ba5c279c2895f, + 0x258dd3db21a5d66b, + 0xd0088f51cbff34d, + ]); + a1.mul_assign(&alpha); + } + + Some(a1) + } + } + } +} + +#[test] +fn test_fq2_ordering() { + let mut a = Fq2 { + c0: Fq::zero(), + c1: Fq::zero(), + }; + + let mut b = a.clone(); + + assert!(a.cmp(&b) == Ordering::Equal); + b.c0.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Less); + a.c0.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Equal); + b.c1.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Less); + a.c0.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Less); + a.c1.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Greater); + b.c0.add_assign(&Fq::one()); + assert!(a.cmp(&b) == Ordering::Equal); +} + +#[test] +fn test_fq2_basics() { + assert_eq!( + Fq2 { + c0: Fq::zero(), + c1: Fq::zero(), + }, + Fq2::zero() + ); + assert_eq!( + Fq2 { + c0: Fq::one(), + c1: Fq::zero(), + }, + Fq2::one() + ); + assert!(Fq2::zero().is_zero()); + assert!(!Fq2::one().is_zero()); + assert!(!Fq2 { + c0: Fq::zero(), + c1: Fq::one(), + }.is_zero()); +} + +#[test] +fn test_fq2_squaring() { + use super::fq::FqRepr; + use PrimeField; + + let mut a = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; // u + 1 + a.square(); + assert_eq!( + a, + Fq2 { + c0: Fq::zero(), + c1: Fq::from_repr(FqRepr::from(2)).unwrap(), + } + ); // 2u + + let mut a = Fq2 { + c0: Fq::zero(), + c1: Fq::one(), + }; // u + a.square(); + assert_eq!(a, { + let mut neg1 = Fq::one(); + neg1.negate(); + Fq2 { + c0: neg1, + c1: Fq::zero(), + } + }); // -1 + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x9c2c6309bbf8b598, + 0x4eef5c946536f602, + 0x90e34aab6fb6a6bd, + 0xf7f295a94e58ae7c, + 0x41b76dcc1c3fbe5e, + 0x7080c5fa1d8e042, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x38f473b3c870a4ab, + 0x6ad3291177c8c7e5, + 0xdac5a4c911a4353e, + 0xbfb99020604137a0, + 0xfc58a7b7be815407, + 0x10d1615e75250a21, + ])).unwrap(), + }; + a.square(); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xf262c28c538bcf68, + 0xb9f2a66eae1073ba, + 0xdc46ab8fad67ae0, + 0xcb674157618da176, + 0x4cf17b5893c3d327, + 0x7eac81369c43361 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xc1579cf58e980cf8, + 0xa23eb7e12dd54d98, + 0xe75138bce4cec7aa, + 0x38d0d7275a9689e1, + 0x739c983042779a65, + 0x1542a61c8a8db994 + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_mul() { + use super::fq::FqRepr; + use PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x85c9f989e1461f03, + 0xa2e33c333449a1d6, + 0x41e461154a7354a3, + 0x9ee53e7e84d7532e, + 0x1c202d8ed97afb45, + 0x51d3f9253e2516f, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xa7348a8b511aedcf, + 0x143c215d8176b319, + 0x4cc48081c09b8903, + 0x9533e4a9a5158be, + 0x7a5e1ecb676d65f9, + 0x180c3ee46656b008, + ])).unwrap(), + }; + a.mul_assign(&Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xe21f9169805f537e, + 0xfc87e62e179c285d, + 0x27ece175be07a531, + 0xcd460f9f0c23e430, + 0x6c9110292bfa409, + 0x2c93a72eb8af83e, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x4b1c3f936d8992d4, + 0x1d2a72916dba4c8a, + 0x8871c508658d1e5f, + 0x57a06d3135a752ae, + 0x634cd3c6c565096d, + 0x19e17334d4e93558, + ])).unwrap(), + }); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x95b5127e6360c7e4, + 0xde29c31a19a6937e, + 0xf61a96dacf5a39bc, + 0x5511fe4d84ee5f78, + 0x5310a202d92f9963, + 0x1751afbe166e5399 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x84af0e1bd630117a, + 0x6c63cd4da2c2aa7, + 0x5ba6e5430e883d40, + 0xc975106579c275ee, + 0x33a9ac82ce4c5083, + 0x1ef1a36c201589d + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_inverse() { + use super::fq::FqRepr; + use PrimeField; + + assert!(Fq2::zero().inverse().is_none()); + + let a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x85c9f989e1461f03, + 0xa2e33c333449a1d6, + 0x41e461154a7354a3, + 0x9ee53e7e84d7532e, + 0x1c202d8ed97afb45, + 0x51d3f9253e2516f, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xa7348a8b511aedcf, + 0x143c215d8176b319, + 0x4cc48081c09b8903, + 0x9533e4a9a5158be, + 0x7a5e1ecb676d65f9, + 0x180c3ee46656b008, + ])).unwrap(), + }; + let a = a.inverse().unwrap(); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x70300f9bcb9e594, + 0xe5ecda5fdafddbb2, + 0x64bef617d2915a8f, + 0xdfba703293941c30, + 0xa6c3d8f9586f2636, + 0x1351ef01941b70c4 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x8c39fd76a8312cb4, + 0x15d7b6b95defbff0, + 0x947143f89faedee9, + 0xcbf651a0f367afb2, + 0xdf4e54f0d3ef15a6, + 0x103bdf241afb0019 + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_addition() { + use super::fq::FqRepr; + use PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.add_assign(&Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x619a02d78dc70ef2, + 0xb93adfc9119e33e8, + 0x4bf0b99a9f0dca12, + 0x3b88899a42a6318f, + 0x986a4a62fa82a49d, + 0x13ce433fa26027f5, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x66323bf80b58b9b9, + 0xa1379b6facf6e596, + 0x402aef1fb797e32f, + 0x2236f55246d0d44d, + 0x4c8c1800eb104566, + 0x11d6e20e986c2085, + ])).unwrap(), + }); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x8e9a7adaf6eb0eb9, + 0xcb207e6b3341eaba, + 0xd70b0c7b481d23ff, + 0xf4ef57d604b6bca2, + 0x65309427b3d5d090, + 0x14c715d5553f01d2 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xfdb032e7d9079a94, + 0x35a2809d15468d83, + 0xfe4b23317e0796d5, + 0xd62fa51334f560fa, + 0x9ad265eb46e01984, + 0x1303f3465112c8bc + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_subtraction() { + use super::fq::FqRepr; + use PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.sub_assign(&Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x619a02d78dc70ef2, + 0xb93adfc9119e33e8, + 0x4bf0b99a9f0dca12, + 0x3b88899a42a6318f, + 0x986a4a62fa82a49d, + 0x13ce433fa26027f5, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x66323bf80b58b9b9, + 0xa1379b6facf6e596, + 0x402aef1fb797e32f, + 0x2236f55246d0d44d, + 0x4c8c1800eb104566, + 0x11d6e20e986c2085, + ])).unwrap(), + }); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x8565752bdb5c9b80, + 0x7756bed7c15982e9, + 0xa65a6be700b285fe, + 0xe255902672ef6c43, + 0x7f77a718021c342d, + 0x72ba14049fe9881 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xeb4abaf7c255d1cd, + 0x11df49bc6cacc256, + 0xe52617930588c69a, + 0xf63905f39ad8cb1f, + 0x4cd5dd9fb40b3b8f, + 0x957411359ba6e4c + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_negation() { + use super::fq::FqRepr; + use PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.negate(); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x8cfe87fc96dbaae4, + 0xcc6615c8fb0492d, + 0xdc167fc04da19c37, + 0xab107d49317487ab, + 0x7e555df189f880e3, + 0x19083f5486a10cbd + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x228109103250c9d0, + 0x8a411ad149045812, + 0xa9109e8f3041427e, + 0xb07e9bc405608611, + 0xfcd559cbe77bd8b8, + 0x18d400b280d93e62 + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_doubling() { + use super::fq::FqRepr; + use PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.double(); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x5a00f006d247ff8e, + 0x23cb3d4443476da4, + 0x1634a5c1521eb3da, + 0x72cd9c7784211627, + 0x998c938972a657e7, + 0x1f1a52b65bdb3b9 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x2efbeddf9b5dc1b6, + 0x28d5ca5ad09f4fdb, + 0x7c4068238cdf674b, + 0x67f15f81dc49195b, + 0x9c8c9bd4b79fa83d, + 0x25a226f714d506e + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_frobenius_map() { + use super::fq::FqRepr; + use PrimeField; + + let mut a = Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc, + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837, + ])).unwrap(), + }; + a.frobenius_map(0); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837 + ])).unwrap(), + } + ); + a.frobenius_map(1); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x228109103250c9d0, + 0x8a411ad149045812, + 0xa9109e8f3041427e, + 0xb07e9bc405608611, + 0xfcd559cbe77bd8b8, + 0x18d400b280d93e62 + ])).unwrap(), + } + ); + a.frobenius_map(1); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837 + ])).unwrap(), + } + ); + a.frobenius_map(2); + assert_eq!( + a, + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x2d0078036923ffc7, + 0x11e59ea221a3b6d2, + 0x8b1a52e0a90f59ed, + 0xb966ce3bc2108b13, + 0xccc649c4b9532bf3, + 0xf8d295b2ded9dc + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0x977df6efcdaee0db, + 0x946ae52d684fa7ed, + 0xbe203411c66fb3a5, + 0xb3f8afc0ee248cad, + 0x4e464dea5bcfd41e, + 0x12d1137b8a6a837 + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_sqrt() { + use super::fq::FqRepr; + use PrimeField; + + assert_eq!( + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x476b4c309720e227, + 0x34c2d04faffdab6, + 0xa57e6fc1bab51fd9, + 0xdb4a116b5bf74aa1, + 0x1e58b2159dfe10e2, + 0x7ca7da1f13606ac + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xfa8de88b7516d2c3, + 0x371a75ed14f41629, + 0x4cec2dca577a3eb6, + 0x212611bca4e99121, + 0x8ee5394d77afb3d, + 0xec92336650e49d5 + ])).unwrap(), + }.sqrt() + .unwrap(), + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0x40b299b2704258c5, + 0x6ef7de92e8c68b63, + 0x6d2ddbe552203e82, + 0x8d7f1f723d02c1d3, + 0x881b3e01b611c070, + 0x10f6963bbad2ebc5 + ])).unwrap(), + c1: Fq::from_repr(FqRepr([ + 0xc099534fc209e752, + 0x7670594665676447, + 0x28a20faed211efe7, + 0x6b852aeaf2afcb1b, + 0xa4c93b08105d71a9, + 0x8d7cfff94216330 + ])).unwrap(), + } + ); + + assert_eq!( + Fq2 { + c0: Fq::from_repr(FqRepr([ + 0xb9f78429d1517a6b, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])).unwrap(), + c1: Fq::zero(), + }.sqrt() + .unwrap(), + Fq2 { + c0: Fq::zero(), + c1: Fq::from_repr(FqRepr([ + 0xb9fefffffd4357a3, + 0x1eabfffeb153ffff, + 0x6730d2a0f6b0f624, + 0x64774b84f38512bf, + 0x4b1ba7b6434bacd7, + 0x1a0111ea397fe69a + ])).unwrap(), + } + ); +} + +#[test] +fn test_fq2_legendre() { + use LegendreSymbol::*; + + assert_eq!(Zero, Fq2::zero().legendre()); + // i^2 = -1 + let mut m1 = Fq2::one(); + m1.negate(); + assert_eq!(QuadraticResidue, m1.legendre()); + m1.mul_by_nonresidue(); + assert_eq!(QuadraticNonResidue, m1.legendre()); +} + +#[cfg(test)] +use rand::{SeedableRng, XorShiftRng}; + +#[test] +fn test_fq2_mul_nonresidue() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let nqr = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; + + for _ in 0..1000 { + let mut a = Fq2::rand(&mut rng); + let mut b = a; + a.mul_by_nonresidue(); + b.mul_assign(&nqr); + + assert_eq!(a, b); + } +} + +#[test] +fn fq2_field_tests() { + use PrimeField; + + ::tests::field::random_field_tests::(); + ::tests::field::random_sqrt_tests::(); + ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/src/rust/pairing/src/bls12_381/fq6.rs b/src/rust/pairing/src/bls12_381/fq6.rs new file mode 100644 index 000000000..c065f27e3 --- /dev/null +++ b/src/rust/pairing/src/bls12_381/fq6.rs @@ -0,0 +1,374 @@ +use super::fq::{FROBENIUS_COEFF_FQ6_C1, FROBENIUS_COEFF_FQ6_C2}; +use super::fq2::Fq2; +use rand::{Rand, Rng}; +use Field; + +/// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Fq6 { + pub c0: Fq2, + pub c1: Fq2, + pub c2: Fq2, +} + +impl ::std::fmt::Display for Fq6 { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fq6({} + {} * v, {} * v^2)", self.c0, self.c1, self.c2) + } +} + +impl Rand for Fq6 { + fn rand(rng: &mut R) -> Self { + Fq6 { + c0: rng.gen(), + c1: rng.gen(), + c2: rng.gen(), + } + } +} + +impl Fq6 { + /// Multiply by quadratic nonresidue v. + pub fn mul_by_nonresidue(&mut self) { + use std::mem::swap; + swap(&mut self.c0, &mut self.c1); + swap(&mut self.c0, &mut self.c2); + + self.c0.mul_by_nonresidue(); + } + + pub fn mul_by_1(&mut self, c1: &Fq2) { + let mut b_b = self.c1; + b_b.mul_assign(c1); + + let mut t1 = *c1; + { + let mut tmp = self.c1; + tmp.add_assign(&self.c2); + + t1.mul_assign(&tmp); + t1.sub_assign(&b_b); + t1.mul_by_nonresidue(); + } + + let mut t2 = *c1; + { + let mut tmp = self.c0; + tmp.add_assign(&self.c1); + + t2.mul_assign(&tmp); + t2.sub_assign(&b_b); + } + + self.c0 = t1; + self.c1 = t2; + self.c2 = b_b; + } + + pub fn mul_by_01(&mut self, c0: &Fq2, c1: &Fq2) { + let mut a_a = self.c0; + let mut b_b = self.c1; + a_a.mul_assign(c0); + b_b.mul_assign(c1); + + let mut t1 = *c1; + { + let mut tmp = self.c1; + tmp.add_assign(&self.c2); + + t1.mul_assign(&tmp); + t1.sub_assign(&b_b); + t1.mul_by_nonresidue(); + t1.add_assign(&a_a); + } + + let mut t3 = *c0; + { + let mut tmp = self.c0; + tmp.add_assign(&self.c2); + + t3.mul_assign(&tmp); + t3.sub_assign(&a_a); + t3.add_assign(&b_b); + } + + let mut t2 = *c0; + t2.add_assign(c1); + { + let mut tmp = self.c0; + tmp.add_assign(&self.c1); + + t2.mul_assign(&tmp); + t2.sub_assign(&a_a); + t2.sub_assign(&b_b); + } + + self.c0 = t1; + self.c1 = t2; + self.c2 = t3; + } +} + +impl Field for Fq6 { + fn zero() -> Self { + Fq6 { + c0: Fq2::zero(), + c1: Fq2::zero(), + c2: Fq2::zero(), + } + } + + fn one() -> Self { + Fq6 { + c0: Fq2::one(), + c1: Fq2::zero(), + c2: Fq2::zero(), + } + } + + fn is_zero(&self) -> bool { + self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero() + } + + fn double(&mut self) { + self.c0.double(); + self.c1.double(); + self.c2.double(); + } + + fn negate(&mut self) { + self.c0.negate(); + self.c1.negate(); + self.c2.negate(); + } + + fn add_assign(&mut self, other: &Self) { + self.c0.add_assign(&other.c0); + self.c1.add_assign(&other.c1); + self.c2.add_assign(&other.c2); + } + + fn sub_assign(&mut self, other: &Self) { + self.c0.sub_assign(&other.c0); + self.c1.sub_assign(&other.c1); + self.c2.sub_assign(&other.c2); + } + + fn frobenius_map(&mut self, power: usize) { + self.c0.frobenius_map(power); + self.c1.frobenius_map(power); + self.c2.frobenius_map(power); + + self.c1.mul_assign(&FROBENIUS_COEFF_FQ6_C1[power % 6]); + self.c2.mul_assign(&FROBENIUS_COEFF_FQ6_C2[power % 6]); + } + + fn square(&mut self) { + let mut s0 = self.c0; + s0.square(); + let mut ab = self.c0; + ab.mul_assign(&self.c1); + let mut s1 = ab; + s1.double(); + let mut s2 = self.c0; + s2.sub_assign(&self.c1); + s2.add_assign(&self.c2); + s2.square(); + let mut bc = self.c1; + bc.mul_assign(&self.c2); + let mut s3 = bc; + s3.double(); + let mut s4 = self.c2; + s4.square(); + + self.c0 = s3; + self.c0.mul_by_nonresidue(); + self.c0.add_assign(&s0); + + self.c1 = s4; + self.c1.mul_by_nonresidue(); + self.c1.add_assign(&s1); + + self.c2 = s1; + self.c2.add_assign(&s2); + self.c2.add_assign(&s3); + self.c2.sub_assign(&s0); + self.c2.sub_assign(&s4); + } + + fn mul_assign(&mut self, other: &Self) { + let mut a_a = self.c0; + let mut b_b = self.c1; + let mut c_c = self.c2; + a_a.mul_assign(&other.c0); + b_b.mul_assign(&other.c1); + c_c.mul_assign(&other.c2); + + let mut t1 = other.c1; + t1.add_assign(&other.c2); + { + let mut tmp = self.c1; + tmp.add_assign(&self.c2); + + t1.mul_assign(&tmp); + t1.sub_assign(&b_b); + t1.sub_assign(&c_c); + t1.mul_by_nonresidue(); + t1.add_assign(&a_a); + } + + let mut t3 = other.c0; + t3.add_assign(&other.c2); + { + let mut tmp = self.c0; + tmp.add_assign(&self.c2); + + t3.mul_assign(&tmp); + t3.sub_assign(&a_a); + t3.add_assign(&b_b); + t3.sub_assign(&c_c); + } + + let mut t2 = other.c0; + t2.add_assign(&other.c1); + { + let mut tmp = self.c0; + tmp.add_assign(&self.c1); + + t2.mul_assign(&tmp); + t2.sub_assign(&a_a); + t2.sub_assign(&b_b); + c_c.mul_by_nonresidue(); + t2.add_assign(&c_c); + } + + self.c0 = t1; + self.c1 = t2; + self.c2 = t3; + } + + fn inverse(&self) -> Option { + let mut c0 = self.c2; + c0.mul_by_nonresidue(); + c0.mul_assign(&self.c1); + c0.negate(); + { + let mut c0s = self.c0; + c0s.square(); + c0.add_assign(&c0s); + } + let mut c1 = self.c2; + c1.square(); + c1.mul_by_nonresidue(); + { + let mut c01 = self.c0; + c01.mul_assign(&self.c1); + c1.sub_assign(&c01); + } + let mut c2 = self.c1; + c2.square(); + { + let mut c02 = self.c0; + c02.mul_assign(&self.c2); + c2.sub_assign(&c02); + } + + let mut tmp1 = self.c2; + tmp1.mul_assign(&c1); + let mut tmp2 = self.c1; + tmp2.mul_assign(&c2); + tmp1.add_assign(&tmp2); + tmp1.mul_by_nonresidue(); + tmp2 = self.c0; + tmp2.mul_assign(&c0); + tmp1.add_assign(&tmp2); + + match tmp1.inverse() { + Some(t) => { + let mut tmp = Fq6 { + c0: t, + c1: t, + c2: t, + }; + tmp.c0.mul_assign(&c0); + tmp.c1.mul_assign(&c1); + tmp.c2.mul_assign(&c2); + + Some(tmp) + } + None => None, + } + } +} + +#[cfg(test)] +use rand::{SeedableRng, XorShiftRng}; + +#[test] +fn test_fq6_mul_nonresidue() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let nqr = Fq6 { + c0: Fq2::zero(), + c1: Fq2::one(), + c2: Fq2::zero(), + }; + + for _ in 0..1000 { + let mut a = Fq6::rand(&mut rng); + let mut b = a; + a.mul_by_nonresidue(); + b.mul_assign(&nqr); + + assert_eq!(a, b); + } +} + +#[test] +fn test_fq6_mul_by_1() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let c1 = Fq2::rand(&mut rng); + let mut a = Fq6::rand(&mut rng); + let mut b = a; + + a.mul_by_1(&c1); + b.mul_assign(&Fq6 { + c0: Fq2::zero(), + c1: c1, + c2: Fq2::zero(), + }); + + assert_eq!(a, b); + } +} + +#[test] +fn test_fq6_mul_by_01() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let c0 = Fq2::rand(&mut rng); + let c1 = Fq2::rand(&mut rng); + let mut a = Fq6::rand(&mut rng); + let mut b = a; + + a.mul_by_01(&c0, &c1); + b.mul_assign(&Fq6 { + c0: c0, + c1: c1, + c2: Fq2::zero(), + }); + + assert_eq!(a, b); + } +} + +#[test] +fn fq6_field_tests() { + use PrimeField; + + ::tests::field::random_field_tests::(); + ::tests::field::random_frobenius_tests::(super::fq::Fq::char(), 13); +} diff --git a/src/rust/pairing/src/bls12_381/fr.rs b/src/rust/pairing/src/bls12_381/fr.rs new file mode 100644 index 000000000..4e9d6ab4f --- /dev/null +++ b/src/rust/pairing/src/bls12_381/fr.rs @@ -0,0 +1,1614 @@ +use LegendreSymbol::*; +use {Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField}; + +// r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 +const MODULUS: FrRepr = FrRepr([ + 0xffffffff00000001, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, +]); + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 255; + +// The number of bits that must be shaved from the beginning of +// the representation when randomly sampling. +const REPR_SHAVE_BITS: u32 = 1; + +// R = 2**256 % r +const R: FrRepr = FrRepr([ + 0x1fffffffe, + 0x5884b7fa00034802, + 0x998c4fefecbc4ff5, + 0x1824b159acc5056f, +]); + +// R2 = R^2 % r +const R2: FrRepr = FrRepr([ + 0xc999e990f3f29c6d, + 0x2b6cedcb87925c23, + 0x5d314967254398f, + 0x748d9d99f59ff11, +]); + +// INV = -(r^{-1} mod 2^64) mod 2^64 +const INV: u64 = 0xfffffffeffffffff; + +// GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) +const GENERATOR: FrRepr = FrRepr([ + 0xefffffff1, + 0x17e363d300189c0f, + 0xff9c57876f8457b0, + 0x351332208fc5a8c4, +]); + +// 2^s * t = MODULUS - 1 with t odd +const S: u32 = 32; + +// 2^s root of unity computed by GENERATOR^t +const ROOT_OF_UNITY: FrRepr = FrRepr([ + 0xb9b58d8c5f0e466a, + 0x5b1b4c801819d7ec, + 0xaf53ae352a31e64, + 0x5bf3adda19e9b27b, +]); + +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] +pub struct FrRepr(pub [u64; 4]); + +impl ::rand::Rand for FrRepr { + #[inline(always)] + fn rand(rng: &mut R) -> Self { + FrRepr(rng.gen()) + } +} + +impl ::std::fmt::Display for FrRepr { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + try!(write!(f, "0x")); + for i in self.0.iter().rev() { + try!(write!(f, "{:016x}", *i)); + } + + Ok(()) + } +} + +impl AsRef<[u64]> for FrRepr { + #[inline(always)] + fn as_ref(&self) -> &[u64] { + &self.0 + } +} + +impl AsMut<[u64]> for FrRepr { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u64] { + &mut self.0 + } +} + +impl From for FrRepr { + #[inline(always)] + fn from(val: u64) -> FrRepr { + let mut repr = Self::default(); + repr.0[0] = val; + repr + } +} + +impl Ord for FrRepr { + #[inline(always)] + fn cmp(&self, other: &FrRepr) -> ::std::cmp::Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return ::std::cmp::Ordering::Less; + } else if a > b { + return ::std::cmp::Ordering::Greater; + } + } + + ::std::cmp::Ordering::Equal + } +} + +impl PartialOrd for FrRepr { + #[inline(always)] + fn partial_cmp(&self, other: &FrRepr) -> Option<::std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl PrimeFieldRepr for FrRepr { + #[inline(always)] + fn is_odd(&self) -> bool { + self.0[0] & 1 == 1 + } + + #[inline(always)] + fn is_even(&self) -> bool { + !self.is_odd() + } + + #[inline(always)] + fn is_zero(&self) -> bool { + self.0.iter().all(|&e| e == 0) + } + + #[inline(always)] + fn shr(&mut self, mut n: u32) { + if n >= 64 * 4 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn div2(&mut self) { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; + } + } + + #[inline(always)] + fn mul2(&mut self) { + let mut last = 0; + for i in &mut self.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } + } + + #[inline(always)] + fn shl(&mut self, mut n: u32) { + if n >= 64 * 4 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in &mut self.0 { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in &mut self.0 { + let t2 = *i >> (64 - n); + *i <<= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn num_bits(&self) -> u32 { + let mut ret = (4 as u32) * 64; + for i in self.0.iter().rev() { + let leading = i.leading_zeros(); + ret -= leading; + if leading != 64 { + break; + } + } + + ret + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &FrRepr) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &FrRepr) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = ::sbb(*a, *b, &mut borrow); + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Fr(FrRepr); + +impl ::std::fmt::Display for Fr { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fr({})", self.into_repr()) + } +} + +impl ::rand::Rand for Fr { + fn rand(rng: &mut R) -> Self { + loop { + let mut tmp = Fr(FrRepr::rand(rng)); + + // Mask away the unused bits at the beginning. + tmp.0.as_mut()[3] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; + + if tmp.is_valid() { + return tmp; + } + } + } +} + +impl From for FrRepr { + fn from(e: Fr) -> FrRepr { + e.into_repr() + } +} + +impl PrimeField for Fr { + type Repr = FrRepr; + + fn from_repr(r: FrRepr) -> Result { + let mut r = Fr(r); + if r.is_valid() { + r.mul_assign(&Fr(R2)); + + Ok(r) + } else { + Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0))) + } + } + + fn into_repr(&self) -> FrRepr { + let mut r = *self; + r.mont_reduce( + (self.0).0[0], + (self.0).0[1], + (self.0).0[2], + (self.0).0[3], + 0, + 0, + 0, + 0, + ); + r.0 + } + + fn char() -> FrRepr { + MODULUS + } + + const NUM_BITS: u32 = MODULUS_BITS; + + const CAPACITY: u32 = Self::NUM_BITS - 1; + + fn multiplicative_generator() -> Self { + Fr(GENERATOR) + } + + const S: u32 = S; + + fn root_of_unity() -> Self { + Fr(ROOT_OF_UNITY) + } +} + +impl Field for Fr { + #[inline] + fn zero() -> Self { + Fr(FrRepr::from(0)) + } + + #[inline] + fn one() -> Self { + Fr(R) + } + + #[inline] + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[inline] + fn add_assign(&mut self, other: &Fr) { + // This cannot exceed the backing capacity. + self.0.add_nocarry(&other.0); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn double(&mut self) { + // This cannot exceed the backing capacity. + self.0.mul2(); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn sub_assign(&mut self, other: &Fr) { + // If `other` is larger than `self`, we'll need to add the modulus to self first. + if other.0 > self.0 { + self.0.add_nocarry(&MODULUS); + } + + self.0.sub_noborrow(&other.0); + } + + #[inline] + fn negate(&mut self) { + if !self.is_zero() { + let mut tmp = MODULUS; + tmp.sub_noborrow(&self.0); + self.0 = tmp; + } + } + + fn inverse(&self) -> Option { + if self.is_zero() { + None + } else { + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications to Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + let one = FrRepr::from(1); + + let mut u = self.0; + let mut v = MODULUS; + let mut b = Fr(R2); // Avoids unnecessary reduction step. + let mut c = Self::zero(); + + while u != one && v != one { + while u.is_even() { + u.div2(); + + if b.0.is_even() { + b.0.div2(); + } else { + b.0.add_nocarry(&MODULUS); + b.0.div2(); + } + } + + while v.is_even() { + v.div2(); + + if c.0.is_even() { + c.0.div2(); + } else { + c.0.add_nocarry(&MODULUS); + c.0.div2(); + } + } + + if v < u { + u.sub_noborrow(&v); + b.sub_assign(&c); + } else { + v.sub_noborrow(&u); + c.sub_assign(&b); + } + } + + if u == one { + Some(b) + } else { + Some(c) + } + } + } + + #[inline(always)] + fn frobenius_map(&mut self, _: usize) { + // This has no effect in a prime field. + } + + #[inline] + fn mul_assign(&mut self, other: &Fr) { + let mut carry = 0; + let r0 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); + let r1 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); + let r2 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[2], &mut carry); + let r3 = ::mac_with_carry(0, (self.0).0[0], (other.0).0[3], &mut carry); + let r4 = carry; + let mut carry = 0; + let r1 = ::mac_with_carry(r1, (self.0).0[1], (other.0).0[0], &mut carry); + let r2 = ::mac_with_carry(r2, (self.0).0[1], (other.0).0[1], &mut carry); + let r3 = ::mac_with_carry(r3, (self.0).0[1], (other.0).0[2], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[1], (other.0).0[3], &mut carry); + let r5 = carry; + let mut carry = 0; + let r2 = ::mac_with_carry(r2, (self.0).0[2], (other.0).0[0], &mut carry); + let r3 = ::mac_with_carry(r3, (self.0).0[2], (other.0).0[1], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[2], (other.0).0[2], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[2], (other.0).0[3], &mut carry); + let r6 = carry; + let mut carry = 0; + let r3 = ::mac_with_carry(r3, (self.0).0[3], (other.0).0[0], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[3], (other.0).0[1], &mut carry); + let r5 = ::mac_with_carry(r5, (self.0).0[3], (other.0).0[2], &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[3], (other.0).0[3], &mut carry); + let r7 = carry; + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + } + + #[inline] + fn square(&mut self) { + let mut carry = 0; + let r1 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); + let r2 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); + let r3 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[3], &mut carry); + let r4 = carry; + let mut carry = 0; + let r3 = ::mac_with_carry(r3, (self.0).0[1], (self.0).0[2], &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[1], (self.0).0[3], &mut carry); + let r5 = carry; + let mut carry = 0; + let r5 = ::mac_with_carry(r5, (self.0).0[2], (self.0).0[3], &mut carry); + let r6 = carry; + + let r7 = r6 >> 63; + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let mut carry = 0; + let r0 = ::mac_with_carry(0, (self.0).0[0], (self.0).0[0], &mut carry); + let r1 = ::adc(r1, 0, &mut carry); + let r2 = ::mac_with_carry(r2, (self.0).0[1], (self.0).0[1], &mut carry); + let r3 = ::adc(r3, 0, &mut carry); + let r4 = ::mac_with_carry(r4, (self.0).0[2], (self.0).0[2], &mut carry); + let r5 = ::adc(r5, 0, &mut carry); + let r6 = ::mac_with_carry(r6, (self.0).0[3], (self.0).0[3], &mut carry); + let r7 = ::adc(r7, 0, &mut carry); + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + } +} + +impl Fr { + /// Determines if the element is really in the field. This is only used + /// internally. + #[inline(always)] + fn is_valid(&self) -> bool { + self.0 < MODULUS + } + + /// Subtracts the modulus from this element if this element is not in the + /// field. Only used internally. + #[inline(always)] + fn reduce(&mut self) { + if !self.is_valid() { + self.0.sub_noborrow(&MODULUS); + } + } + + #[inline(always)] + fn mont_reduce( + &mut self, + r0: u64, + mut r1: u64, + mut r2: u64, + mut r3: u64, + mut r4: u64, + mut r5: u64, + mut r6: u64, + mut r7: u64, + ) { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r0, k, MODULUS.0[0], &mut carry); + r1 = ::mac_with_carry(r1, k, MODULUS.0[1], &mut carry); + r2 = ::mac_with_carry(r2, k, MODULUS.0[2], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[3], &mut carry); + r4 = ::adc(r4, 0, &mut carry); + let carry2 = carry; + let k = r1.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r1, k, MODULUS.0[0], &mut carry); + r2 = ::mac_with_carry(r2, k, MODULUS.0[1], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[2], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[3], &mut carry); + r5 = ::adc(r5, carry2, &mut carry); + let carry2 = carry; + let k = r2.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r2, k, MODULUS.0[0], &mut carry); + r3 = ::mac_with_carry(r3, k, MODULUS.0[1], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[2], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[3], &mut carry); + r6 = ::adc(r6, carry2, &mut carry); + let carry2 = carry; + let k = r3.wrapping_mul(INV); + let mut carry = 0; + ::mac_with_carry(r3, k, MODULUS.0[0], &mut carry); + r4 = ::mac_with_carry(r4, k, MODULUS.0[1], &mut carry); + r5 = ::mac_with_carry(r5, k, MODULUS.0[2], &mut carry); + r6 = ::mac_with_carry(r6, k, MODULUS.0[3], &mut carry); + r7 = ::adc(r7, carry2, &mut carry); + (self.0).0[0] = r4; + (self.0).0[1] = r5; + (self.0).0[2] = r6; + (self.0).0[3] = r7; + self.reduce(); + } +} + +impl SqrtField for Fr { + fn legendre(&self) -> ::LegendreSymbol { + // s = self^((r - 1) // 2) + let s = self.pow([ + 0x7fffffff80000000, + 0xa9ded2017fff2dff, + 0x199cec0404d0ec02, + 0x39f6d3a994cebea4, + ]); + if s == Self::zero() { + Zero + } else if s == Self::one() { + QuadraticResidue + } else { + QuadraticNonResidue + } + } + + fn sqrt(&self) -> Option { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + match self.legendre() { + Zero => Some(*self), + QuadraticNonResidue => None, + QuadraticResidue => { + let mut c = Fr(ROOT_OF_UNITY); + // r = self^((t + 1) // 2) + let mut r = self.pow([ + 0x7fff2dff80000000, + 0x4d0ec02a9ded201, + 0x94cebea4199cec04, + 0x39f6d3a9, + ]); + // t = self^t + let mut t = self.pow([ + 0xfffe5bfeffffffff, + 0x9a1d80553bda402, + 0x299d7d483339d808, + 0x73eda753, + ]); + let mut m = S; + + while t != Self::one() { + let mut i = 1; + { + let mut t2i = t; + t2i.square(); + loop { + if t2i == Self::one() { + break; + } + t2i.square(); + i += 1; + } + } + + for _ in 0..(m - i - 1) { + c.square(); + } + r.mul_assign(&c); + c.square(); + t.mul_assign(&c); + m = i; + } + + Some(r) + } + } + } +} + +#[cfg(test)] +use rand::{Rand, SeedableRng, XorShiftRng}; + +#[test] +fn test_fr_repr_ordering() { + fn assert_equality(a: FrRepr, b: FrRepr) { + assert_eq!(a, b); + assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal); + } + + fn assert_lt(a: FrRepr, b: FrRepr) { + assert!(a < b); + assert!(b > a); + } + + assert_equality( + FrRepr([9999, 9999, 9999, 9999]), + FrRepr([9999, 9999, 9999, 9999]), + ); + assert_equality( + FrRepr([9999, 9998, 9999, 9999]), + FrRepr([9999, 9998, 9999, 9999]), + ); + assert_equality( + FrRepr([9999, 9999, 9999, 9997]), + FrRepr([9999, 9999, 9999, 9997]), + ); + assert_lt( + FrRepr([9999, 9997, 9999, 9998]), + FrRepr([9999, 9997, 9999, 9999]), + ); + assert_lt( + FrRepr([9999, 9997, 9998, 9999]), + FrRepr([9999, 9997, 9999, 9999]), + ); + assert_lt( + FrRepr([9, 9999, 9999, 9997]), + FrRepr([9999, 9999, 9999, 9997]), + ); +} + +#[test] +fn test_fr_repr_from() { + assert_eq!(FrRepr::from(100), FrRepr([100, 0, 0, 0])); +} + +#[test] +fn test_fr_repr_is_odd() { + assert!(!FrRepr::from(0).is_odd()); + assert!(FrRepr::from(0).is_even()); + assert!(FrRepr::from(1).is_odd()); + assert!(!FrRepr::from(1).is_even()); + assert!(!FrRepr::from(324834872).is_odd()); + assert!(FrRepr::from(324834872).is_even()); + assert!(FrRepr::from(324834873).is_odd()); + assert!(!FrRepr::from(324834873).is_even()); +} + +#[test] +fn test_fr_repr_is_zero() { + assert!(FrRepr::from(0).is_zero()); + assert!(!FrRepr::from(1).is_zero()); + assert!(!FrRepr([0, 0, 1, 0]).is_zero()); +} + +#[test] +fn test_fr_repr_div2() { + let mut a = FrRepr([ + 0xbd2920b19c972321, + 0x174ed0466a3be37e, + 0xd468d5e3b551f0b5, + 0xcb67c072733beefc, + ]); + a.div2(); + assert_eq!( + a, + FrRepr([ + 0x5e949058ce4b9190, + 0x8ba76823351df1bf, + 0x6a346af1daa8f85a, + 0x65b3e039399df77e + ]) + ); + for _ in 0..10 { + a.div2(); + } + assert_eq!( + a, + FrRepr([ + 0x6fd7a524163392e4, + 0x16a2e9da08cd477c, + 0xdf9a8d1abc76aa3e, + 0x196cf80e4e677d + ]) + ); + for _ in 0..200 { + a.div2(); + } + assert_eq!(a, FrRepr([0x196cf80e4e67, 0x0, 0x0, 0x0])); + for _ in 0..40 { + a.div2(); + } + assert_eq!(a, FrRepr([0x19, 0x0, 0x0, 0x0])); + for _ in 0..4 { + a.div2(); + } + assert_eq!(a, FrRepr([0x1, 0x0, 0x0, 0x0])); + a.div2(); + assert!(a.is_zero()); +} + +#[test] +fn test_fr_repr_shr() { + let mut a = FrRepr([ + 0xb33fbaec482a283f, + 0x997de0d3a88cb3df, + 0x9af62d2a9a0e5525, + 0x36003ab08de70da1, + ]); + a.shr(0); + assert_eq!( + a, + FrRepr([ + 0xb33fbaec482a283f, + 0x997de0d3a88cb3df, + 0x9af62d2a9a0e5525, + 0x36003ab08de70da1 + ]) + ); + a.shr(1); + assert_eq!( + a, + FrRepr([ + 0xd99fdd762415141f, + 0xccbef069d44659ef, + 0xcd7b16954d072a92, + 0x1b001d5846f386d0 + ]) + ); + a.shr(50); + assert_eq!( + a, + FrRepr([ + 0xbc1a7511967bf667, + 0xc5a55341caa4b32f, + 0x75611bce1b4335e, + 0x6c0 + ]) + ); + a.shr(130); + assert_eq!(a, FrRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0])); + a.shr(64); + assert_eq!(a, FrRepr([0x1b0, 0x0, 0x0, 0x0])); +} + +#[test] +fn test_fr_repr_mul2() { + let mut a = FrRepr::from(23712937547); + a.mul2(); + assert_eq!(a, FrRepr([0xb0acd6c96, 0x0, 0x0, 0x0])); + for _ in 0..60 { + a.mul2(); + } + assert_eq!(a, FrRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0])); + for _ in 0..128 { + a.mul2(); + } + assert_eq!(a, FrRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9])); + for _ in 0..60 { + a.mul2(); + } + assert_eq!(a, FrRepr([0x0, 0x0, 0x0, 0x9600000000000000])); + for _ in 0..7 { + a.mul2(); + } + assert!(a.is_zero()); +} + +#[test] +fn test_fr_repr_num_bits() { + let mut a = FrRepr::from(0); + assert_eq!(0, a.num_bits()); + a = FrRepr::from(1); + for i in 1..257 { + assert_eq!(i, a.num_bits()); + a.mul2(); + } + assert_eq!(0, a.num_bits()); +} + +#[test] +fn test_fr_repr_sub_noborrow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FrRepr([ + 0x8e62a7e85264e2c3, + 0xb23d34c1941d3ca, + 0x5976930b7502dd15, + 0x600f3fb517bf5495, + ]); + t.sub_noborrow(&FrRepr([ + 0xd64f669809cbc6a4, + 0xfa76cb9d90cf7637, + 0xfefb0df9038d43b3, + 0x298a30c744b31acf, + ])); + assert!( + t == FrRepr([ + 0xb813415048991c1f, + 0x10ad07ae88725d92, + 0x5a7b851271759961, + 0x36850eedd30c39c5 + ]) + ); + + for _ in 0..1000 { + let mut a = FrRepr::rand(&mut rng); + a.0[3] >>= 30; + let mut b = a; + for _ in 0..10 { + b.mul2(); + } + let mut c = b; + for _ in 0..10 { + c.mul2(); + } + + assert!(a < b); + assert!(b < c); + + let mut csub_ba = c; + csub_ba.sub_noborrow(&b); + csub_ba.sub_noborrow(&a); + + let mut csub_ab = c; + csub_ab.sub_noborrow(&a); + csub_ab.sub_noborrow(&b); + + assert_eq!(csub_ab, csub_ba); + } + + // Subtracting r+1 from r should produce -1 (mod 2**256) + let mut qplusone = FrRepr([ + 0xffffffff00000001, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, + ]); + qplusone.sub_noborrow(&FrRepr([ + 0xffffffff00000002, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, + ])); + assert_eq!( + qplusone, + FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ]) + ); +} + +#[test] +fn test_fr_legendre() { + assert_eq!(QuadraticResidue, Fr::one().legendre()); + assert_eq!(Zero, Fr::zero().legendre()); + + let e = FrRepr([ + 0x0dbc5349cd5664da, + 0x8ac5b6296e3ae29d, + 0x127cb819feceaa3b, + 0x3a6b21fb03867191, + ]); + assert_eq!(QuadraticResidue, Fr::from_repr(e).unwrap().legendre()); + let e = FrRepr([ + 0x96341aefd047c045, + 0x9b5f4254500a4d65, + 0x1ee08223b68ac240, + 0x31d9cd545c0ec7c6, + ]); + assert_eq!(QuadraticNonResidue, Fr::from_repr(e).unwrap().legendre()); +} + +#[test] +fn test_fr_repr_add_nocarry() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FrRepr([ + 0xd64f669809cbc6a4, + 0xfa76cb9d90cf7637, + 0xfefb0df9038d43b3, + 0x298a30c744b31acf, + ]); + t.add_nocarry(&FrRepr([ + 0x8e62a7e85264e2c3, + 0xb23d34c1941d3ca, + 0x5976930b7502dd15, + 0x600f3fb517bf5495, + ])); + assert_eq!( + t, + FrRepr([ + 0x64b20e805c30a967, + 0x59a9ee9aa114a02, + 0x5871a104789020c9, + 0x8999707c5c726f65 + ]) + ); + + // Test for the associativity of addition. + for _ in 0..1000 { + let mut a = FrRepr::rand(&mut rng); + let mut b = FrRepr::rand(&mut rng); + let mut c = FrRepr::rand(&mut rng); + + // Unset the first few bits, so that overflow won't occur. + a.0[3] >>= 3; + b.0[3] >>= 3; + c.0[3] >>= 3; + + let mut abc = a; + abc.add_nocarry(&b); + abc.add_nocarry(&c); + + let mut acb = a; + acb.add_nocarry(&c); + acb.add_nocarry(&b); + + let mut bac = b; + bac.add_nocarry(&a); + bac.add_nocarry(&c); + + let mut bca = b; + bca.add_nocarry(&c); + bca.add_nocarry(&a); + + let mut cab = c; + cab.add_nocarry(&a); + cab.add_nocarry(&b); + + let mut cba = c; + cba.add_nocarry(&b); + cba.add_nocarry(&a); + + assert_eq!(abc, acb); + assert_eq!(abc, bac); + assert_eq!(abc, bca); + assert_eq!(abc, cab); + assert_eq!(abc, cba); + } + + // Adding 1 to (2^256 - 1) should produce zero + let mut x = FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + ]); + x.add_nocarry(&FrRepr::from(1)); + assert!(x.is_zero()); +} + +#[test] +fn test_fr_is_valid() { + let mut a = Fr(MODULUS); + assert!(!a.is_valid()); + a.0.sub_noborrow(&FrRepr::from(1)); + assert!(a.is_valid()); + assert!(Fr(FrRepr::from(0)).is_valid()); + assert!( + Fr(FrRepr([ + 0xffffffff00000000, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48 + ])).is_valid() + ); + assert!(!Fr(FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ])).is_valid()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = Fr::rand(&mut rng); + assert!(a.is_valid()); + } +} + +#[test] +fn test_fr_add_assign() { + { + // Random number + let mut tmp = Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca, + ])); + assert!(tmp.is_valid()); + // Test that adding zero has no effect. + tmp.add_assign(&Fr(FrRepr::from(0))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca + ])) + ); + // Add one and test for the result. + tmp.add_assign(&Fr(FrRepr::from(1))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0x437ce7616d580766, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca + ])) + ); + // Add another random number that exercises the reduction. + tmp.add_assign(&Fr(FrRepr([ + 0x946f435944f7dc79, + 0xb55e7ee6533a9b9b, + 0x1e43b84c2f6194ca, + 0x58717ab525463496, + ]))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0xd7ec2abbb24fe3de, + 0x35cdf7ae7d0d62f7, + 0xd899557c477cd0e9, + 0x3371b52bc43de018 + ])) + ); + // Add one to (r - 1) and test for the result. + tmp = Fr(FrRepr([ + 0xffffffff00000000, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48, + ])); + tmp.add_assign(&Fr(FrRepr::from(1))); + assert!(tmp.0.is_zero()); + // Add a random number to another one such that the result is r - 1 + tmp = Fr(FrRepr([ + 0xade5adacdccb6190, + 0xaa21ee0f27db3ccd, + 0x2550f4704ae39086, + 0x591d1902e7c5ba27, + ])); + tmp.add_assign(&Fr(FrRepr([ + 0x521a525223349e70, + 0xa99bb5f3d8231f31, + 0xde8e397bebe477e, + 0x1ad08e5041d7c321, + ]))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0xffffffff00000000, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48 + ])) + ); + // Add one to the result and test for it. + tmp.add_assign(&Fr(FrRepr::from(1))); + assert!(tmp.0.is_zero()); + } + + // Test associativity + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Generate a, b, c and ensure (a + b) + c == a + (b + c). + let a = Fr::rand(&mut rng); + let b = Fr::rand(&mut rng); + let c = Fr::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + + let mut tmp2 = b; + tmp2.add_assign(&c); + tmp2.add_assign(&a); + + assert!(tmp1.is_valid()); + assert!(tmp2.is_valid()); + assert_eq!(tmp1, tmp2); + } +} + +#[test] +fn test_fr_sub_assign() { + { + // Test arbitrary subtraction that tests reduction. + let mut tmp = Fr(FrRepr([ + 0x6a68c64b6f735a2b, + 0xd5f4d143fe0a1972, + 0x37c17f3829267c62, + 0xa2f37391f30915c, + ])); + tmp.sub_assign(&Fr(FrRepr([ + 0xade5adacdccb6190, + 0xaa21ee0f27db3ccd, + 0x2550f4704ae39086, + 0x591d1902e7c5ba27, + ]))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0xbc83189d92a7f89c, + 0x7f908737d62d38a3, + 0x45aa62cfe7e4c3e1, + 0x24ffc5896108547d + ])) + ); + + // Test the opposite subtraction which doesn't test reduction. + tmp = Fr(FrRepr([ + 0xade5adacdccb6190, + 0xaa21ee0f27db3ccd, + 0x2550f4704ae39086, + 0x591d1902e7c5ba27, + ])); + tmp.sub_assign(&Fr(FrRepr([ + 0x6a68c64b6f735a2b, + 0xd5f4d143fe0a1972, + 0x37c17f3829267c62, + 0xa2f37391f30915c, + ]))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca + ])) + ); + + // Test for sensible results with zero + tmp = Fr(FrRepr::from(0)); + tmp.sub_assign(&Fr(FrRepr::from(0))); + assert!(tmp.is_zero()); + + tmp = Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca, + ])); + tmp.sub_assign(&Fr(FrRepr::from(0))); + assert_eq!( + tmp, + Fr(FrRepr([ + 0x437ce7616d580765, + 0xd42d1ccb29d1235b, + 0xed8f753821bd1423, + 0x4eede1c9c89528ca + ])) + ); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure that (a - b) + (b - a) = 0. + let a = Fr::rand(&mut rng); + let b = Fr::rand(&mut rng); + + let mut tmp1 = a; + tmp1.sub_assign(&b); + + let mut tmp2 = b; + tmp2.sub_assign(&a); + + tmp1.add_assign(&tmp2); + assert!(tmp1.is_zero()); + } +} + +#[test] +fn test_fr_mul_assign() { + let mut tmp = Fr(FrRepr([ + 0x6b7e9b8faeefc81a, + 0xe30a8463f348ba42, + 0xeff3cb67a8279c9c, + 0x3d303651bd7c774d, + ])); + tmp.mul_assign(&Fr(FrRepr([ + 0x13ae28e3bc35ebeb, + 0xa10f4488075cae2c, + 0x8160e95a853c3b5d, + 0x5ae3f03b561a841d, + ]))); + assert!( + tmp == Fr(FrRepr([ + 0x23717213ce710f71, + 0xdbee1fe53a16e1af, + 0xf565d3e1c2a48000, + 0x4426507ee75df9d7 + ])) + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * b) * c = a * (b * c) + let a = Fr::rand(&mut rng); + let b = Fr::rand(&mut rng); + let c = Fr::rand(&mut rng); + + let mut tmp1 = a; + tmp1.mul_assign(&b); + tmp1.mul_assign(&c); + + let mut tmp2 = b; + tmp2.mul_assign(&c); + tmp2.mul_assign(&a); + + assert_eq!(tmp1, tmp2); + } + + for _ in 0..1000000 { + // Ensure that r * (a + b + c) = r*a + r*b + r*c + + let r = Fr::rand(&mut rng); + let mut a = Fr::rand(&mut rng); + let mut b = Fr::rand(&mut rng); + let mut c = Fr::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + tmp1.mul_assign(&r); + + a.mul_assign(&r); + b.mul_assign(&r); + c.mul_assign(&r); + + a.add_assign(&b); + a.add_assign(&c); + + assert_eq!(tmp1, a); + } +} + +#[test] +fn test_fr_squaring() { + let mut a = Fr(FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0x73eda753299d7d47, + ])); + assert!(a.is_valid()); + a.square(); + assert_eq!( + a, + Fr::from_repr(FrRepr([ + 0xc0d698e7bde077b8, + 0xb79a310579e76ec2, + 0xac1da8d0a9af4e5f, + 0x13f629c49bf23e97 + ])).unwrap() + ); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * a) = a^2 + let a = Fr::rand(&mut rng); + + let mut tmp = a; + tmp.square(); + + let mut tmp2 = a; + tmp2.mul_assign(&a); + + assert_eq!(tmp, tmp2); + } +} + +#[test] +fn test_fr_inverse() { + assert!(Fr::zero().inverse().is_none()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let one = Fr::one(); + + for _ in 0..1000 { + // Ensure that a * a^-1 = 1 + let mut a = Fr::rand(&mut rng); + let ainv = a.inverse().unwrap(); + a.mul_assign(&ainv); + assert_eq!(a, one); + } +} + +#[test] +fn test_fr_double() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure doubling a is equivalent to adding a to itself. + let mut a = Fr::rand(&mut rng); + let mut b = a; + b.add_assign(&a); + a.double(); + assert_eq!(a, b); + } +} + +#[test] +fn test_fr_negate() { + { + let mut a = Fr::zero(); + a.negate(); + + assert!(a.is_zero()); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure (a - (-a)) = 0. + let mut a = Fr::rand(&mut rng); + let mut b = a; + b.negate(); + a.add_assign(&b); + + assert!(a.is_zero()); + } +} + +#[test] +fn test_fr_pow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for i in 0..1000 { + // Exponentiate by various small numbers and ensure it consists with repeated + // multiplication. + let a = Fr::rand(&mut rng); + let target = a.pow(&[i]); + let mut c = Fr::one(); + for _ in 0..i { + c.mul_assign(&a); + } + assert_eq!(c, target); + } + + for _ in 0..1000 { + // Exponentiating by the modulus should have no effect in a prime field. + let a = Fr::rand(&mut rng); + + assert_eq!(a, a.pow(Fr::char())); + } +} + +#[test] +fn test_fr_sqrt() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + assert_eq!(Fr::zero().sqrt().unwrap(), Fr::zero()); + + for _ in 0..1000 { + // Ensure sqrt(a^2) = a or -a + let a = Fr::rand(&mut rng); + let mut nega = a; + nega.negate(); + let mut b = a; + b.square(); + + let b = b.sqrt().unwrap(); + + assert!(a == b || nega == b); + } + + for _ in 0..1000 { + // Ensure sqrt(a)^2 = a for random a + let a = Fr::rand(&mut rng); + + if let Some(mut tmp) = a.sqrt() { + tmp.square(); + + assert_eq!(a, tmp); + } + } +} + +#[test] +fn test_fr_from_into_repr() { + // r + 1 should not be in the field + assert!( + Fr::from_repr(FrRepr([ + 0xffffffff00000002, + 0x53bda402fffe5bfe, + 0x3339d80809a1d805, + 0x73eda753299d7d48 + ])).is_err() + ); + + // r should not be in the field + assert!(Fr::from_repr(Fr::char()).is_err()); + + // Multiply some arbitrary representations to see if the result is as expected. + let a = FrRepr([ + 0x25ebe3a3ad3c0c6a, + 0x6990e39d092e817c, + 0x941f900d42f5658e, + 0x44f8a103b38a71e0, + ]); + let mut a_fr = Fr::from_repr(a).unwrap(); + let b = FrRepr([ + 0x264e9454885e2475, + 0x46f7746bb0308370, + 0x4683ef5347411f9, + 0x58838d7f208d4492, + ]); + let b_fr = Fr::from_repr(b).unwrap(); + let c = FrRepr([ + 0x48a09ab93cfc740d, + 0x3a6600fbfc7a671, + 0x838567017501d767, + 0x7161d6da77745512, + ]); + a_fr.mul_assign(&b_fr); + assert_eq!(a_fr.into_repr(), c); + + // Zero should be in the field. + assert!(Fr::from_repr(FrRepr::from(0)).unwrap().is_zero()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Try to turn Fr elements into representations and back again, and compare. + let a = Fr::rand(&mut rng); + let a_repr = a.into_repr(); + let b_repr = FrRepr::from(a); + assert_eq!(a_repr, b_repr); + let a_again = Fr::from_repr(a_repr).unwrap(); + + assert_eq!(a, a_again); + } +} + +#[test] +fn test_fr_repr_display() { + assert_eq!( + format!( + "{}", + FrRepr([ + 0x2829c242fa826143, + 0x1f32cf4dd4330917, + 0x932e4e479d168cd9, + 0x513c77587f563f64 + ]) + ), + "0x513c77587f563f64932e4e479d168cd91f32cf4dd43309172829c242fa826143".to_string() + ); + assert_eq!( + format!( + "{}", + FrRepr([ + 0x25ebe3a3ad3c0c6a, + 0x6990e39d092e817c, + 0x941f900d42f5658e, + 0x44f8a103b38a71e0 + ]) + ), + "0x44f8a103b38a71e0941f900d42f5658e6990e39d092e817c25ebe3a3ad3c0c6a".to_string() + ); + assert_eq!( + format!( + "{}", + FrRepr([ + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff + ]) + ), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() + ); + assert_eq!( + format!("{}", FrRepr([0, 0, 0, 0])), + "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() + ); +} + +#[test] +fn test_fr_display() { + assert_eq!( + format!( + "{}", + Fr::from_repr(FrRepr([ + 0xc3cae746a3b5ecc7, + 0x185ec8eb3f5b5aee, + 0x684499ffe4b9dd99, + 0x7c9bba7afb68faa + ])).unwrap() + ), + "Fr(0x07c9bba7afb68faa684499ffe4b9dd99185ec8eb3f5b5aeec3cae746a3b5ecc7)".to_string() + ); + assert_eq!( + format!( + "{}", + Fr::from_repr(FrRepr([ + 0x44c71298ff198106, + 0xb0ad10817df79b6a, + 0xd034a80a2b74132b, + 0x41cf9a1336f50719 + ])).unwrap() + ), + "Fr(0x41cf9a1336f50719d034a80a2b74132bb0ad10817df79b6a44c71298ff198106)".to_string() + ); +} + +#[test] +fn test_fr_num_bits() { + assert_eq!(Fr::NUM_BITS, 255); + assert_eq!(Fr::CAPACITY, 254); +} + +#[test] +fn test_fr_root_of_unity() { + assert_eq!(Fr::S, 32); + assert_eq!( + Fr::multiplicative_generator(), + Fr::from_repr(FrRepr::from(7)).unwrap() + ); + assert_eq!( + Fr::multiplicative_generator().pow([ + 0xfffe5bfeffffffff, + 0x9a1d80553bda402, + 0x299d7d483339d808, + 0x73eda753 + ]), + Fr::root_of_unity() + ); + assert_eq!(Fr::root_of_unity().pow([1 << Fr::S]), Fr::one()); + assert!(Fr::multiplicative_generator().sqrt().is_none()); +} + +#[test] +fn fr_field_tests() { + ::tests::field::random_field_tests::(); + ::tests::field::random_sqrt_tests::(); + ::tests::field::random_frobenius_tests::(Fr::char(), 13); + ::tests::field::from_str_tests::(); +} + +#[test] +fn fr_repr_tests() { + ::tests::repr::random_repr_tests::(); +} diff --git a/src/rust/pairing/src/bls12_381/mod.rs b/src/rust/pairing/src/bls12_381/mod.rs new file mode 100644 index 000000000..a5db4b5a0 --- /dev/null +++ b/src/rust/pairing/src/bls12_381/mod.rs @@ -0,0 +1,364 @@ +mod ec; +mod fq; +mod fq12; +mod fq2; +mod fq6; +mod fr; + +#[cfg(test)] +mod tests; + +pub use self::ec::{ + G1, G1Affine, G1Compressed, G1Prepared, G1Uncompressed, G2, G2Affine, G2Compressed, G2Prepared, + G2Uncompressed, +}; +pub use self::fq::{Fq, FqRepr}; +pub use self::fq12::Fq12; +pub use self::fq2::Fq2; +pub use self::fq6::Fq6; +pub use self::fr::{Fr, FrRepr}; + +use super::{BitIterator, CurveAffine, Engine, Field}; + +// The BLS parameter x for BLS12-381 is -0xd201000000010000 +const BLS_X: u64 = 0xd201000000010000; +const BLS_X_IS_NEGATIVE: bool = true; + +#[derive(Clone, Debug)] +pub struct Bls12; + +impl Engine for Bls12 { + type Fr = Fr; + type G1 = G1; + type G1Affine = G1Affine; + type G2 = G2; + type G2Affine = G2Affine; + type Fq = Fq; + type Fqe = Fq2; + type Fqk = Fq12; + + fn miller_loop<'a, I>(i: I) -> Self::Fqk + where + I: IntoIterator< + Item = &'a ( + &'a ::Prepared, + &'a ::Prepared, + ), + >, + { + let mut pairs = vec![]; + for &(p, q) in i { + if !p.is_zero() && !q.is_zero() { + pairs.push((p, q.coeffs.iter())); + } + } + + // Twisting isomorphism from E to E' + fn ell(f: &mut Fq12, coeffs: &(Fq2, Fq2, Fq2), p: &G1Affine) { + let mut c0 = coeffs.0; + let mut c1 = coeffs.1; + + c0.c0.mul_assign(&p.y); + c0.c1.mul_assign(&p.y); + + c1.c0.mul_assign(&p.x); + c1.c1.mul_assign(&p.x); + + // Sparse multiplication in Fq12 + f.mul_by_014(&coeffs.2, &c1, &c0); + } + + let mut f = Fq12::one(); + + let mut found_one = false; + for i in BitIterator::new(&[BLS_X >> 1]) { + if !found_one { + found_one = i; + continue; + } + + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + + if i { + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + } + + f.square(); + } + + for &mut (p, ref mut coeffs) in &mut pairs { + ell(&mut f, coeffs.next().unwrap(), &p.0); + } + + if BLS_X_IS_NEGATIVE { + f.conjugate(); + } + + f + } + + fn final_exponentiation(r: &Fq12) -> Option { + let mut f1 = *r; + f1.conjugate(); + + match r.inverse() { + Some(mut f2) => { + let mut r = f1; + r.mul_assign(&f2); + f2 = r; + r.frobenius_map(2); + r.mul_assign(&f2); + + fn exp_by_x(f: &mut Fq12, x: u64) { + *f = f.pow(&[x]); + if BLS_X_IS_NEGATIVE { + f.conjugate(); + } + } + + let mut x = BLS_X; + let mut y0 = r; + y0.square(); + let mut y1 = y0; + exp_by_x(&mut y1, x); + x >>= 1; + let mut y2 = y1; + exp_by_x(&mut y2, x); + x <<= 1; + let mut y3 = r; + y3.conjugate(); + y1.mul_assign(&y3); + y1.conjugate(); + y1.mul_assign(&y2); + y2 = y1; + exp_by_x(&mut y2, x); + y3 = y2; + exp_by_x(&mut y3, x); + y1.conjugate(); + y3.mul_assign(&y1); + y1.conjugate(); + y1.frobenius_map(3); + y2.frobenius_map(2); + y1.mul_assign(&y2); + y2 = y3; + exp_by_x(&mut y2, x); + y2.mul_assign(&y0); + y2.mul_assign(&r); + y1.mul_assign(&y2); + y2 = y3; + y2.frobenius_map(1); + y1.mul_assign(&y2); + + Some(y1) + } + None => None, + } + } +} + +impl G2Prepared { + pub fn is_zero(&self) -> bool { + self.infinity + } + + pub fn from_affine(q: G2Affine) -> Self { + if q.is_zero() { + return G2Prepared { + coeffs: vec![], + infinity: true, + }; + } + + fn doubling_step(r: &mut G2) -> (Fq2, Fq2, Fq2) { + // Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf + let mut tmp0 = r.x; + tmp0.square(); + + let mut tmp1 = r.y; + tmp1.square(); + + let mut tmp2 = tmp1; + tmp2.square(); + + let mut tmp3 = tmp1; + tmp3.add_assign(&r.x); + tmp3.square(); + tmp3.sub_assign(&tmp0); + tmp3.sub_assign(&tmp2); + tmp3.double(); + + let mut tmp4 = tmp0; + tmp4.double(); + tmp4.add_assign(&tmp0); + + let mut tmp6 = r.x; + tmp6.add_assign(&tmp4); + + let mut tmp5 = tmp4; + tmp5.square(); + + let mut zsquared = r.z; + zsquared.square(); + + r.x = tmp5; + r.x.sub_assign(&tmp3); + r.x.sub_assign(&tmp3); + + r.z.add_assign(&r.y); + r.z.square(); + r.z.sub_assign(&tmp1); + r.z.sub_assign(&zsquared); + + r.y = tmp3; + r.y.sub_assign(&r.x); + r.y.mul_assign(&tmp4); + + tmp2.double(); + tmp2.double(); + tmp2.double(); + + r.y.sub_assign(&tmp2); + + tmp3 = tmp4; + tmp3.mul_assign(&zsquared); + tmp3.double(); + tmp3.negate(); + + tmp6.square(); + tmp6.sub_assign(&tmp0); + tmp6.sub_assign(&tmp5); + + tmp1.double(); + tmp1.double(); + + tmp6.sub_assign(&tmp1); + + tmp0 = r.z; + tmp0.mul_assign(&zsquared); + tmp0.double(); + + (tmp0, tmp3, tmp6) + } + + fn addition_step(r: &mut G2, q: &G2Affine) -> (Fq2, Fq2, Fq2) { + // Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf + let mut zsquared = r.z; + zsquared.square(); + + let mut ysquared = q.y; + ysquared.square(); + + let mut t0 = zsquared; + t0.mul_assign(&q.x); + + let mut t1 = q.y; + t1.add_assign(&r.z); + t1.square(); + t1.sub_assign(&ysquared); + t1.sub_assign(&zsquared); + t1.mul_assign(&zsquared); + + let mut t2 = t0; + t2.sub_assign(&r.x); + + let mut t3 = t2; + t3.square(); + + let mut t4 = t3; + t4.double(); + t4.double(); + + let mut t5 = t4; + t5.mul_assign(&t2); + + let mut t6 = t1; + t6.sub_assign(&r.y); + t6.sub_assign(&r.y); + + let mut t9 = t6; + t9.mul_assign(&q.x); + + let mut t7 = t4; + t7.mul_assign(&r.x); + + r.x = t6; + r.x.square(); + r.x.sub_assign(&t5); + r.x.sub_assign(&t7); + r.x.sub_assign(&t7); + + r.z.add_assign(&t2); + r.z.square(); + r.z.sub_assign(&zsquared); + r.z.sub_assign(&t3); + + let mut t10 = q.y; + t10.add_assign(&r.z); + + let mut t8 = t7; + t8.sub_assign(&r.x); + t8.mul_assign(&t6); + + t0 = r.y; + t0.mul_assign(&t5); + t0.double(); + + r.y = t8; + r.y.sub_assign(&t0); + + t10.square(); + t10.sub_assign(&ysquared); + + let mut ztsquared = r.z; + ztsquared.square(); + + t10.sub_assign(&ztsquared); + + t9.double(); + t9.sub_assign(&t10); + + t10 = r.z; + t10.double(); + + t6.negate(); + + t1 = t6; + t1.double(); + + (t10, t1, t9) + } + + let mut coeffs = vec![]; + let mut r: G2 = q.into(); + + let mut found_one = false; + for i in BitIterator::new([BLS_X >> 1]) { + if !found_one { + found_one = i; + continue; + } + + coeffs.push(doubling_step(&mut r)); + + if i { + coeffs.push(addition_step(&mut r, &q)); + } + } + + coeffs.push(doubling_step(&mut r)); + + G2Prepared { + coeffs, + infinity: false, + } + } +} + +#[test] +fn bls12_engine_tests() { + ::tests::engine::engine_tests::(); +} diff --git a/src/rust/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat b/src/rust/pairing/src/bls12_381/tests/g1_compressed_valid_test_vectors.dat new file mode 100644 index 0000000000000000000000000000000000000000..ea8cd67652d133010e79df8488452450b6f5cb17 GIT binary patch literal 48000 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 Rm?nepfGSPSF*XppvfIk?3d#Tg literal 0 HcmV?d00001 diff --git a/src/rust/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat b/src/rust/pairing/src/bls12_381/tests/g1_uncompressed_invalid_test_vectors.dat new file mode 100644 index 000000000..e69de29bb diff --git a/src/rust/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat b/src/rust/pairing/src/bls12_381/tests/g1_uncompressed_valid_test_vectors.dat new file mode 100644 index 0000000000000000000000000000000000000000..86abfba945c7b701750d637bffe6701330e10e88 GIT binary patch literal 96000 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%0A9l>2- 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 literal 0 HcmV?d00001 diff --git a/src/rust/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat b/src/rust/pairing/src/bls12_381/tests/g2_compressed_valid_test_vectors.dat new file mode 100644 index 0000000000000000000000000000000000000000..a40bbe251d90e576060adb7eaa2456197878804c GIT binary patch literal 96000 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 literal 0 HcmV?d00001 diff --git a/src/rust/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat b/src/rust/pairing/src/bls12_381/tests/g2_uncompressed_valid_test_vectors.dat new file mode 100644 index 0000000000000000000000000000000000000000..92e4bc528e8937a311b502e4940e5e363a1f7c57 GIT binary patch literal 192000 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�uc*gWwz@d=S2~bA~LrFNXzUL(>hGG#V zY3-}D`Hm^5oO>(>D+j6>2!OxbF*N7Vm46J+nX-n3Yt6?7FHFT#4(mWisGICV!AtkT zX5ud%fx9AgD!9rjz40u~m)5r$an@HpB$DedYwd3W2PwQYOmHc%1<=&QPk0T~&kqml zulp*RCKK-F?G;F*H|^nSl)B0=!G3gnHLG70Mp6_GVxp@?pD`<{=pDqwuCh638|0N* zEyrimS+P}lk4B|>K=0oLgvOA-YA#m}N6s8tS;XU(?VK=mY982GDv^GozI{d8P?C8b zNCwx4%h4lN*rXf_nd&JHZ86#bP6IjbdWlNV5Fm0RUPEacy#IglekatpWYNy$H>Io? zoH5hW@C*liD+`chJ2KZ38&C*f*Eb1Dd*wH6NZC#b09LgJ)rY2^0<{Y8WajFlqu4hA zPDV-zh*6Tj=z*dSR=1`cA9|K~0N1>XZS7k>C8y6V`zJQfMIA!E0v&o4I-SU`5%a<8 zj-&*cw~AQ}gTsbGSaci4V8O?5uluqGL4Zd&~m~5xY@qMoYPT||7Q4kvBgh(tae6~s-nRH|GYK>1WRfO zTTKP$Rb%oe&_8Umwt$u|Ke-I$k%_T5xDq^wk#ggC>L8;^P#IqXFw2Z%ZC!1Q#oRxD zUm(m#(I)Immn{8Q15kwlR5sUVXt{u0df3^9N5Lq%&}exOh8%%x0qvsp)>J&-sB_U( zhk0KNv*WKdp01x+s5MRhgBfNn_U(^0nf5kKbj7$1V-3%!2$Da+L5mZ6E?~mjfz=iL zZEs`Y{8S=r-uNRZAvsvD^0+nIrj z(Vp}9;eb8Q65QPnxn`{KuZ9{4`3qNqOYHGUcWAxd8N#}d)1~pIZu5P2CI1Ypbbl&= zev^Ac=@AKJDOla1zHiwMjX}#*870CzT6`@EI=VAczNfj-(JjU0N%Fzx=_$3A=PstO zm_0>y7@Q+`yOv-GW&9YZWl8$Y!Frj0Qljevg6%y_9^)ifs(86TU`Z0;@VkHkQww%- zJ*2^DaFGH94@ur^hxlec&_7j^z_uR@wuqzWfFBKzmcl7dM^K12Ums5znQaE5RFJ0` zDR%!4>n0Dy;Z6RoG$P4-%?A9$=zovBX}NFp4ph=Iwo{RP5Nq0GX#t+U*oD-k1iO#| zedX9n(qNJdUtia0gu4?8wdjbBN)`YHmOJ?HOKAD>OYYvV#yQ|V4Rrxl&cmAu@Fo!= zunr5&`6|5ue!GYW9$y2b?lc;EkAtLl^y*kKAl!yn8pwnVV*WN(qzkbN96wT3o5nu- z5fzhNVj=i1RVc;;)D|%JyxomePl+=e+Myr3bIIdqDZHQZ+F!U7n`xCMPnpiS-a{x@ z#4JCQ@||p*Q*`RdV^+iNEht`YJg@xvtd%5-%3ap*YWrvtk07j8B)Y)Qma&Hp%k*71 zNuP2&#`@+exmP+DJv=(a~PvEcGeyx;aI*)hAkmgY~SIlToADXv1c4gh)xNj3N%l%N6?L7VCQ$`=%~ zhN~#+kttPT=0rviGiavhjniZT|L2VX9t-B^*EJ0H4fJ4+;eQ*eYZ_9JmfESfp1k9e ze_@jKWzE=7HRa9X$uDQSof{+x-z;5o{!!1S)dx?4Vi^jnGsaE{ElG`OSkATZB9cSO zgGFemx!|qWVAy3M7=a256V9G{sKgb}iI5#Z_nz6x+$VA~K99p>zj;%P%pC zD?7MaT}apG+_KsfE}$`G(8T6y?o9(&y(e%(2Np;DR>3Yj?R@csjCxJ?3x__n8xv6f z+2d(IPQ>8_yV2dw~_ zcMZ)(=g#Ull&Wt-bJ}Ro(DaZlh2J-U(wlW81ii^=_3jZ(U3ub$(+H|iJrIQq^cL@M z)TY`6|JNh@(qys=9Fn`A)$2+%IIjIQL&^A?MyNtpBTiz`Rnk6+6y^N{JLhMh-6ZoP z6t|=yryvy|jhx4-9X>g|4sWEPLo2gcpSQB# z46q&LO32sgjeP^7o=NI#Jtv#8HaG6Nv^;XW480PrtJLulszSM=M`QdgTfg53-hzH* zK}Qtd?Bdr7Vzts8B(*v?@70dbX;4SLMA^D0O1d%vRLfMk_hq$p6P}s6NaVGD}%1xRQyce4IOv^v(e&y?mP`38#OeM&YMD;J#I+_sG}}>~A_+Z**ILZ8$7Eg`UI~d0yWy z^4t3J2^L=&&0{p0o7-3=)$!LLZpTF#OnXhf$upG+(Mdkt7$*KvMs)9rSK=V656+eI39Cq5;G`+M=U|SK`}OaGUW^%Ft4DzI>oi*i_0{uWNJx z#HxVr$*E?_Qq;Zq+4PyA@lkF(15wMwhJl*m0~S;+FdVsI`KSIVSLav?kNxcsV|R-L zM*Kr3lxJ(uN*elFpB~95`=SDx#*`#%+8&;@po=}2ESWC)CoR^ST$6o8z&bJ=E-3*sy(dhYlLdcT747KcrLDCGNr zSox)S#WGByLd;~henwqFlpe^5xB7&Jo}}vsy>BXSV4)Zn0Naz~@!|bn;rY_5Q6r>z zCQ}p+p3eH7<8dVBN}CPgoIY^ihx6zN)y_`m{pwV1PPnF6W|P83nPqa$v|$6EIt zh8(6R9{#34@FL%@Ugh<1rN6}w<-wN~N1P)22$05%7T!YQ^wzA(Jc>Bp$o*%Lfv(i@ z+oxm#T``MR50K&C^2WRw_mE9!3A1mxA6JbXc2TAa-;5fmAIr>KzaOE|&8%@qRni7&K8y0;pS;ePx)3wS3sbd!w@bU2 ziH7H`Zh43X+6(?tARCf&A9D=y9fQ!Q)td%}S`Y_KPeZPrzAOyJ0IfKkbq90)Q@9`F zjLMx8xFLDQ&v{Z<+rB0G!;_Q^Q#o04I{_H=&^^ZRr5vUoT|?z5i%+lw-L8s(xl0BG ziJoq)Zy-@Jok;lhc7t7=e8n2To_!vqt22We0xrgT*h7F&NI)-zP$v>|S!3x5*?Y{# z3cF{B!H4UQZ)e0UdNree;mZ_)4U+S52jL7csC{wFL}&%i$g${`_5#QkT22`$i`HdJ zxA8s%qH)!LhLR!Ei~dJ~nRQ3}V*JUM!LlV`c_eSxqWeqmAIymXF_JjX9P&QcXHCy6 zOSP{ap*XRFCy_^k2$4sWfha`;XRh6xdvnv6V6vmZ(H-v!q#|^yw*=5hO5b;brK1_z z07d;*3kNW%*>wf#Xy7p>aT{91kvy69g3e zPG4rWyD%&`H*#DT7)pZ=N0WXje6G83{=!SgjL+E0-FeWXHY4JIo24dSeh~R< zAaP|#k5#>ZD-&rJD2q~4XA>$b8o(PfY?LL>I4#>&Bg|9?A1(9b(c9Th83W`oz#whf zw)n|FfU`gcCYETj2L|0bhh1g@w`c`6by2h%z;!jc4sP)kWERgI<%=B;8xE+mP;{7$ z8^JvdND}gDAy&!+vtHw-H>R=X2Oh@z{RBBTJbdWoju?(hjl?2mEGnJKY{h-3g=elA zvb)jCy61b?w=}{OC+rHZ@yTE9dt0^m&HNl}KaozUHbfW}yuk3*k>vWT?otvL7HcS7 zoD%-)_ap+QLuj?}Ct zy*BS>U|}G{-tT{5TBQd>I53mc{@Re=ToV@&J~r^5i;80dEC?glrl70Ufs=djki>{1 zw2}DD9`)X27Uh*Y0l)pugj%)Fqo*}KIFTv=P}e6CvTEOednqA`T{V6!A$=aH7;RZ& zFmFkk2)v}#PX`oUs;=McWK><#eZI^3g)na!9m0=^F$=OH4~W4|c=v2V6+amTSzbCd z3!W#gplNibdq;3dgfvbnMQ=!4EUY^gsnChi91yht*DoIg=_;EaVpO~OE2!9R0;DgV zgCGwDOL89&(F7b-PdxmpyE=~qmTuCU=%}L~WI+h(%oV)V@2{LgM|oShG7w`;z@UPR zarU*CL~!!45(5x><2VS zbSK#ktrp$`C47QKCda2)_$9qen%*)59z4MiT|A_K^#+<~C+du~2wkMQwD(R4kP{_P zl^k*sxalyjb~0O`E#^1G(3D+c?>ogF1XW!O@Z3`=NKKVhuV}05_0RUmk@3bU&xD2* z%J@H`ePJe6uM|y4tw-Ff59(}BZ?0!@T=C!kD&1Q=H_Xh!7c0YScwRVWkcQ+10ZJV! zWKJ3w-A(2cG7p!C)&Tzinq;0@bVY)jZSh`7@T9E*1_?b(@>};wrjQO1_S_G)d4~E<_rNmur;TPfRd-_2ilWO~*EF&5jcNWAC6qssUPu*l>O{nSE)rmNR~dCwl~Hs^R{)`3+* zMu*a2O-F%ARIq z<6kNkoYV!VNgh$i29c&PViEuJj;br@!8w?Zn(!tG+zt{1EkshQqCRF_90teB7*gxx zNH%l>nMyfyk3&&&m?POeKz;Fa@osQ->_EAIlR!oa1qnDAs0lKAJQm~Rd}#PIMww*; z4c$AFFA?;&Fu-6r-bmC!hKbSa5Ug!6Q&3m1d!A8x!%(<_w z2D)Z`=k$He7?N~ljv%SJm`2_Ou&v{84-;FBbgjfCfzhU(%R5KU#gC1%et#;eq6D&g z4rRancd*mA<;lt>O`zBfdrj;)1_K3|TIR0&Zu4cHacVQ(di7bs4!74Nw$)8))+J@u z!`yJWm#_GDq9Y3y7fjC)<&ghsnvBJ%nkjsABuAK+65)5Pt8>0<$~9@9H3@g+zhwE` zAK+SxS&<+UZArF?>1WwYL>TL;U*GY+Q$6}pXw`{HB>jiPfH>pQ+QV#AV}nUqEF-lD;|#-_r7CymnvD=-O53NWw8L#c|RR_Ne!dpdk6` zLEM}xRN(`BclDjikXWJj|COK+EikvO7~rj;-r|eEQG=`aeA^xkollh`5LW$v%ua;b z)Tz}{Gv!7hG!cqS@=m~0LsqOYk_gEOG^V=?lENT zrMZ?K1+8{yh{D-hbBXRPr7YulTFzfO92$iH!^5Bq1!vuNP*?sqe$;g$qRP~nx(#7| zt~#ll;8A-2hn&0$p7Ca}EQh+e63q9vsxUqP;3j!Z#k+^KdAO4K+iZ*=Fg_2ggqXGL zi23Q&(kVC~dHP+-9YrFJxUxQd;PjLg!@C10&)Rl|rA9h*&ktGC)~kECq6Ilu22CSx zRc`GvcXibo4p?d{ucFxUhRJCIuS%+oW*rwV4S%?)=`KC=b6#8hiAvq)Th{V4nv0v) zl4ywY0cq|b`oXyD!br9Ta3?Cmw78N0v%!Vky*WT}i~P(9)bF77=y6h$`S}Ck3Ilhs z$dv6X9QEJ8r6)xT&i%Zk|HEm3Zq(>q)3W$lMb1`1f|s<%5OY>xE#Kry?fA{YTw4C^ zHXe?&MTZ$+Zz#>rGatw8X`xNQra`@&PDNHd62Sb_U>G5^gT&mPJbUT${`AS zx9;5r-HO#Bx3tWJpVF-*mkrsh6~~G$A6NL}ik^l=4C}wJxUKUeUE;`A!}JQi<5QY;6kIr?5Pbr31W{+_f6%96tSe&O#7z?m2W{l&2d8xPiE zm0vlW&!()`h<7g~^)?2`{F<)PMZfZ;?OIb+CYgX!kYEjge>6)Ki`9Q<$Qig7Lrt8o zkQYDtUqopTc=tig3O@kIm>qiJsE0)@FdvJ7GA}i{N0X!lqOCE}al}c$R%so}=Z|l% z50*%L_CVV~LD_1ntzTP=ya8hAojdc{BD` za;>Xope1F=Vu{TpDHvwC>OJQ!28WfynQU7VS$cUig~R7TiG*n!U-d@OW*$@|<3mI+ z_MObXBHErgvR4>!B+v6DBk3IQv`cvyrGe92+S+y2YqiJZ?YvegU>$gKJImdy`LYEno2=N;fg+JBGs zclhwx^WJ2m;a(CF2D2E+8j&_0#nRT_{x&WsHeFp)g^zBI3hS=D@EVTWhGsLuD}RI> zEQQf;65`qxoJV(Wo~NaY3AUKpD-`mgdrWj`?5{2}a_e66T&&_)ZF2zS@OYZ(eFtb` zfQ)1Vhr_Jz@zwk7?Yy`Vwa>`G!Xzs+pBZ3besuzYp2(c+?N?Xra5X#n0z2{dY>uH9 zE>bKShwtA6MYa5ixT{<{LgB@L(PM|b-GSQ_}AqfU}% zK+hQTiWCgl>{P4sfWh4wwuS!2LjQ?Z!+<~ZKsi%ZKjWG8vrX0(w|=eH()pOGI?z6e zzq}`f^Q1Z5_*6{##+?Oki^~{#{?1p1!f#jMBLSv93+L|{R@@2P_GrE^c$ORn38wy< zpop$8b9a9244kd~Ql$0FxjY(?((ezpIph^?>W@bSvzdy*ZW+^;Qmyn=C0f_^y3Vm_ z95zblV}=2Z#x-^ID!d<=k&rDb)$tS`N*iqnEYA7an{lUog%mFiUI;jke5b^WL_)Oc79i z6{{_#LTh$8a9`PnJ*Nt%btwM`1j8RNy!4dHpHP)xLN!?!JTFbBua)GS>TllTk4bSU zYgcCFv=;kg!*OhnOs!`Q_M|XFOm3tqX@yi*+!C6&^`c3jsAUBTuQogbL831Dmza}Y zNHAZepLtyeKbc|yx*PXaFy5^?yYO>MCL_Mg;io=E?z4ns8uV6-0= zl#j-ASlx;FRqFROi#mD0>aC}0|LEa*e9dYL0q92+#)B$ro&&~Iyf?!EUO^W!OHw`1 zsHlyc_%*4gq#tR*@0xcFO_G#aA){p|n}VH|S&la0SH4zt3Unh4Vg_|Jg@Dzrd^A2% zsiNzSi*iAPKdcp=Y#w8ITj|oKY#x%WHTeNPR}I_NNt!7F)zF+ZT?_r1HX)0+C;cll zd1lW?-m!@E?13!$6YIdZJW?H1vu`q+ib0@4^@nT&iAj+vR$Gu_{$EzOF$L6DNHEgn z0_;bAYy+u*x<@XT>rTDtfVH2RI5^ys^5&=z#P8F%F~O^Q0v+JK)h}H_tZhoPD$WUj zvF{RT{Ps~|mzyzibN`4GNZsC$LHdRZo9Y!fopcVA>Hy`*9#>|05bvzI3+_sH+-3`I zCW?o6IDY0=>?UO($7rMRFJ} z3%d~3mqCFJfBzVL%C!-%<+H1TBegQ6z} zWrH|jJ#)SkXDFdv948y@E&6*$nD(nW!fyj1*xAVSP}^uY%h;n;O%J!HrGWGow?OBM zN=QW&{(%*?8oXuZq$!(D1RmvVt&_ELOX%Xi|LW1^NHffFwVyEVLK>wRQPxbI&CpN) z@eGgaOKZEe84SU6ah=jcbN1BNF1EtqXl3n~al=1a4;o!-I_RgNpNq>^(3yjSu`znVM z&q+9sM|XY%0TVA06NA8ClbStB3bW}e=?J1d0%cp8gUnV!iAq-IS1nH~)W zRy2&(yWcOri?!|sZOHGBGcuqhAlKmFJsn&8o~$|+1H6~O|7$%gk+2!LRz^<=!1FFh zAyq`USnhcyU2rLW*B9*6F6?+lh6n?8Zqer7~T==O?X0= zn6rO2s2r`I5!=yS)YhiYq6*Chwn9bX6Ha{Vb0db4`0W6?;h|u%dk>eVr7_#eKI#4K z2BzsGLgbKIkN>%2sAsbloeaT>fEhy;-M(12-3fiU+I}m`=)hAkBGZ7oIGI{oRDnsn zNK35V*{>RWi@K&%7vkV{mpgZw_XP&u&Sd;q&Yx3Pj5Is;-wB93Z` z^X`ZTOBv{%DJdP;BhYETHsiRXM)q`D1(;1nKr)=BVV0tKw6X! z__&#T@w+Js<208)KZYo%>~Q^{`j}m8HhVC+h%tA3(9~eUQ!!6(kCqPjBB(zK%55v0 zOYUJkQ%{-0hSP2!W%w~Xa&P+ll51TMhr-zR7wM@+wM?-`nc_X;ZMgvizcaB^E9X1^ z0!O=_O?`s+WQBhwnjr=nQ7Ox7mp&k#ZWW@_BwNdR{$cr0vWN;16BY5l@rkaZVGDsh z6t#lr&ZzxLyMY67&Sfexv%@RR2?d*v3atDwVPKHDv1(@kA|7KMUPoLpvEOB^l93*Z zCv<1%$10DC_KGGEIku8+8Sc6rZCQ^5$hKy}cfss}y}mQhyVo}SPFQc` z%9&<-vOE`QY!=W5;Mmp6!TsArcnB}c6^4%yE1syrjEq@14?G37O{DB}VbrXMgz2LW zP6WzxiXQ0Cuu(E-;j%?DRk)!^$@L8nzDMxU5qQrU3!x45Zxfu1%_+)c?@4M)1!CTz z-3_Xr7=I(+NgQbTO!Tk&?jnT<+M*?nQLCH7cCEaw;F~e+GQnR<0jKD0))E%cBqQ^`f zW9BSak>7O@06~tP=|gVB_QL+~oZIjb;V%s-Cq8MTg=tkq@TgD`$s{R7hxRKg%XW)^QmSe!D2S3d$C8$=w`HRc>-WqCIXww{of_%ZoM$8z{H zU4i5s2eXv&Tso6VeEm%klid65S29u(y=(4p;oihUy+z9gQ;7TNYOo>wAGQt zv4uWD(nd7y8?s!st=BI_A2pspn&Yq*74gg#1UY*~c>ULfyqx_1%+9{gTyoMc6ab1l zibqA@Cv-NVG(51amWhLH1VX+SWzgwu^VZSNjs&5&qD;hphTV!cj{UP*lbTO|`vS3q z=s0Z<8hS>DPcM6YzceBdKN|5@h_BiAOdql@LMWokl4Lw1@IF?9vPa#1mRN!Y~dm^r$_O`ojzz*f9kb`5EV&pXY)pW zQQz2iCh+C0tax_jw&=N$7)d{g2EiZ!cz&P`s@>X1r{bdaGa+sk{Crh?*#>gTooX%P z-sFle1-ud~2Af9XC^`$~&3E<%<~H6Nfr9Ae0;0`7e?XZ4MT!kPIRP?E81dm%AeAFR z@Hr}8Y}TleFBC4XJo@Qu+b{q*&RmGc!B*Miq7oh#7L|}Q4>WHv+D@@xB zj*Z@m-xj+t$VP+_d9L#W`Q0QXM9R#WwDrT&9ZclE_h_YQo_xl7%uTHBI~T64q^iRG zqYvYr;nL3-tj(z^&EDAcQ*n~9+6xz5P_iscySOMh0Y%Z8-AVR>MJ^}o_jAPHOe>Kd zz5DSH2Rs-Ed4Qw2E`YH!go^oR?KS(~GXUz&{Md`%C!s|rd*$`TsmAanU5lT*1iPjU z7Yf{!i=Ssk=mg$w$eyH&8)!!uI~-w4F~OOtoURj(vZ`)2(VJP%9{%p5!_KwG3(vV@fSH$E>4cyt!_qj(hz6U?$4`~{Vdwe4@B>i>jAv!+Vf zA!MJ)vK@-cR3Tba|!$to0S;jPFNQ`1(6^%%3N_nWgm znQt~m`>0qWhL_s=9;0MkH)O_|5Uo8Jl)3)uN3)4 zu5a&fPW!Zu{e&<*YLcfIQHrNiClqqP&!aU2e)^`o#PRT+vBw$VyxM*Eoa!MJu6PUU zo3A#Xf15@iB#-1O#p;=4(kD8S_sr^(ruh0!lxZ2(?Vs{jW zeC|{HYNB7YgTtl+dQZbZQ+CXn_pzui5t*75@dix#@HO>C0B#s-$OBKZ(oCljtI89) zDuHYFZLl2yyuX$oWk-G;30F^Vf#q#4Xr!3EdpO80Aa5x9z@|dWX}x{dMd|d`K2c= zGYi_5ogLUd4OFup4B$7Y7V?x0(=HKHTzsZ&(J+{~AxHZIB}@6{)D5{sn2|qD4s6u$ z`CqWOX{EiMDVq5qDynD^4CxR{cuMlRDbTK*IQ2e?E$E`1coM9sTi-x@k1#j5n zXoT08J9RT3MIpR;?5B_Y}Vc|F{u>geW+J>55- zD;+)xb(#h96$W2Wm>Tf&DK zV6LFb5rvXzQKqL!uF1w9E;%EHsdFy<;rX>ObB>a3t3|pMp*jI(Q1@_T(oOdc@=G-J zb=5Yhh{z5@E&*ySZnB0WM)r&xPhG>{I8t6~`0hZnU|4ZlQ={{)t3Rm=8^fJTlsv*S z84t`eyrpcpmA49V<7@e+!(Q0B4r@RWEux+QF0S-o3q`#~;Hyy>#HDi_ln2CkXE#uUyuOp)`FZ*Z3 zrHo99=FW&bYhOm)N|oSDxmywXTnT~Q%Thv_D$eWy;HslDdx24@b8io0Ok^&HoUvZ4 zO~opKF%~Lw!}905$0Jhl>->#iI>mM7&wh*sPydl-N>%O^fY8-*kcwVVwa+`)iGFIy z56ezPy4Y$1v2z&gmuL=w>CxKwcIg-c$#wON`#V|a`QPJTGBkMxIF?6pcZc6QcJ8o; zxP3oOu_*RNg*L4zwrrK4aS|*50ReO4t@C<;fuA_PF3*^ zOQ0O?BCxEvDqt~U7q2u^{TgB!+3LzH{ZkE~?+fpd1Li8IJVj<&J_CpbUD6e@z z1i9kfsLUDiRnC_D246wR0cwq@BVWayFso$<`KJEWboCTwG<-GtP#pOG;m6Z$V=fk+ z5l;U+(VS5Ucg+r3WZ39L21hrO`#`Y}OrJpNyYeW(mCufT>h*Z-M)}H498#M`plZ`TUx{J^*@mgVGn=XYrYn!pu^lc z6dH=b=7T7+KyRWH2sx!ZP75qHJ@xXpH$L#MfHe>|c62&A$>^V(bEJ>Zfg5FX?v=%? ze{z$o;;>>00I;Oop(T}V?@%#2T@&mD1c8`ja_vh13?#e=wjkVTj-EhAabDrf7D>D> zbYf!^SgFwPZgtAZ1%_XmsBvI-q|i*c6FblNpP=iE35xok9@*>=H%~3=QN;tHQJvlf zgf06v`4R?C{W`2E%|^3tR0ESCoP0$N`HERfQm$1n+sPTdeP zEEJ78g7jQ8_B&I;do2gt<6<;hbxyWWWuicenjB~FEdz83shgS60@ zm&~=5Z-G#IDEl_)8{=l;KWC8YnW2;kzL&D?b?em@p{*AfTRaieM&#q(6}({SKRvj) z0eLMYsr2R2m*lIU`{Hs8*A>!$2OaTj{SizLY!n9Bhl6{NbrAGsI`Zz&GF|YMAJotX zjwQi`KBoBhY%9`Q6<>g<>h4<1J>0tsmL34bZs^ooA*(EMr^Q06{>$zdhQCK4^vFq`GwhfEYYt2lRX| z=T+FD3KmO#v5BSlIPK)o?Tm*$5i+~{e!fW21CLF?gZMKRbb$U&WgoN^ISVP(D*HT7 zOJ0OvoO!H47Hsu~!x-9r5b_kE4RCTQSh$HZtINoRxp)QKT;6}(37P9V(-N2O7I*`q zm_}9>H#Dp`(>otV1`JW`HENQ624ACPu*rlgcq_YFd6|Q-6jH0*qN&HnMD>w{yToJS zqH+>hlf5O>1<|<*juRxzp5zd}imtn}q?qj10x|Wz-9&Z9)l{v$9tv%a3QA|#0Mx(* z9h^F(3dW~Fj%OPzSwwGhownY+5bUfv%BYXD7LKl^{(awKUckxTphz-w_ z1WmcN)QNH~qYZ{`dA}=_m@-i)IPy`NPt!LsQ+P8hQ@&#~CkP0fd9P zRyy&{c6~&q3D<85lOgXrpLE#y-~t!Z>4)Qrv{zEu;oCZS1hzxBG3_@54JZL+-5fG? zi8ve@J@0|d9P7Qc>^BRF61O9E%vep4ZttL4UCDIuQm0RA>jjd;6>9c%WIm4+x1R?T zGSCvLXD(cfDy@^;wneq;|Qb26Jmga{a~ap3{eGHReh@o z4G9wWjpYw9Ua6(PP+SU>Xec^3H+gD)@SwZ3er3dp2rt`5$?Icxc0@&N8tyb0<4;_Z z&jwB4vuFhtPM69PNXq?KqoVMZ9_^iHC#6vc1U+K+N7m6X`?uzIkEcLbD-_Vki2eM| zm9_UKG~pTH>OPCXOI772WC2_=(=H6-2K&EU8mSGkJHnwENvyQwGkUOD5PJ&cXiYRx zK2?p;>MS~8v&3^9gtq}x&y4=? zN!r^wV_P%MJ83J8)7=G0Lx1wJsBw~;>!TFzwp6w8GBV1z8hxTq<#r_X7 z!QMU)$Oc^6B0OE#1v?g_MS1|b77chxiXL*HEYbk)Yy1)G`J+@T_1z{Eacr`nEnF6S*cfOue5D)%qbFCu~L4`Bv~H zGLQsiDY)4z4!xNd!?TbAG?TZ4%Z>ge^Y zjc8Z81zxi@@V#6Qk@oTYRDcUF{>0xRL)ivfJ_l&Ent@309yT`F3L0aP>Fv6xX79xB z7<|`hZg~XJZ-5)rbf6ZE7_EXYh|ANYt8jx#)&rJnCtRLx5o&dXYc<;`wb~|84Wve` zjkRg%^@4S_MiGnfrMArC>pj8G-V{U%W6H5?<3tUCUI!aV5DdSs=2oFY2v$Y#8+Qet z?EkSHgOxO8g6*v{k2+jX^GZU=`HwX29ZR0upzgz3)bj&Hf!M|L76-h8XKKB^Za!TN z2&i>bdxmYQnvgmn`@s0qGgt>#+Xh!#dF=)MaE13-2*%8c4g0@nZ1SvA{G?~oaN*Oz z%dO6=ClsWH=^GQY$j@wKE}5AgaZjWoY7!-ZCM!&O3g7R3Y4e5@uR%T7*^Mc%Qz=dF z<&VDhbQFqt=W08F{~?&|9tY7EYLx-1Q2~2(6CdrYHKsBR%vCma77QfVJMRTt5O7@W z*R}VDYhGQPuXZYgQ(tp<45tNs;ZOmj1hgsLL>1Bd>|iPNx786vqW*Sbuu|i&!BU3T zb6HFY8R4<|{0zTqs>ttclSHOH1F`E_JG+#yE0aoN>#d3l2+bqky`5kI66(C4@_3&tQ>VBu567X$6J>)0lm-$| zK?Eo}C#ezE7(R|__>8X1_sk2P7JJa1x-Sd`8Kl`s5a-~`CC z8K7=iTo`ggJ!yA}fPiEHE~aiQ@ZTD4C%Q37NhTg!U~+)VzfK)AN|W1MtS+mz7919| zI7a7h8@hgO))+k(!Qz(B-?=#sCqxjK_o(4kdc<^K#61iow^43L|Khw^7V$ZqE;g=p zzF2OaCaYPorlU3nqv2%A-sE@)!#fQIWD+_q2+MGjgi{K3*Le=z7rl_lO7n+(dHRpn zEHOCIuoqnh-R9!?q;Uu;$^NY z<-)Rgz2!SWn+1Om-O@j(`??WFr8JUgl}pa-mygV~2>FDFJ+>P0S;HTS+!i&5)m25E8%+nQWVGQ?@-AS;QKA9T}adUkAs!VGob#26<3d} zBWMAsOQeB_R$@Er5Yxd3hp=0DT;X^}hOR@~rx7<~>+*))^opwh#QPUFp&>~~-dKhP zmW<6WUb5e~2d;+lc%_Vh#*W&HW_zn=uczv(>%~5FT5?brgM7p+%XwX+Gt<^UtKN;7 z+Oe*h578;frg{_*0)5COar=dLldwH9pzqC;$W4FA{(|@X+@0UItK6&x-o(Z;JmC^` z2z6#gjeieXOA-fnCVc?pfb%&vPAF7EbV5~M=%)oqqR5RrmLdw&*E>y)Y@(%h5rkqw zrV+svO&0Zb+}Ba8oXY#{I}RDm z+g+-8adUz-OX#PfmvLRn66yO1nFR zAI^FtWx5d94fOB|m2N(P3O!#TZ64uK%B97kWzYYJzIO5Vji;0AKda41@_j7?%Dxzx zOtq~mPbgLarW_242Rzb_-5?Vio5SaE&-5J$m>qip37Xzy@RqPw!86s6y{zY+!Q??# zkv9#)N}lt~14x1!63~Vd_#%5ug^l4Cpg6k@7u&wW!+VpjYo^?_-7l%7L0kcC4ktn2 zY)u4Y3YNTMn%<6Ur1=`hx=oE3>fHm?Kfj(?^%pBpYOXdsg7j^*Q8*9P&DAy>t-23M z7t_fgjYg^RwU;fuc6c%EoNSz$OPWe?JSTIm8?!DJBzKU&5FLR`RnKP++6ZtI38i~b ziiMAkCQ&w7U_6AY>y=K5upVIh)XU~@3w?%tXi!#OMB-ZI#_X<;PPk?*3~|taXJ9p2 z#Rp#Phza4(wTQX5ZR;jqoBBc4?0FxUqufqhY*zVjxXxq}b50O051nV2Z}~uL1>DE5 zx)>e3GZU=j0}wqPd->Q&1Y*7!$Ogy$7CcEDRA#lXb#gg{3xF!}*x~!F$ZGX9XFexw z35Px+B~SuvW3fj~CQ=p8lb{?#x+lbRswOf~!%d<^2i}cVMds<5tw0EP)0$%1B2MAW zp1oQY;!CWDpc0@H#D2KFS$JFN5EUiA>|-q0lA{Oj}RnJgX=s|}6f z0;%SF8WQHtQ;v=SIhWY;5=sw<)Yx{jekepaxESeqY)|uMQboLVArzVppmj~zVM&d% zQ2w8#3a@4e^DMbzAzi{!eCX|GxM9%Vw~jSK$WHc8q$O9xw6W0km-O z6Fow?09`Ioedc+C9nMPET%3E9{blbJO&U=Y(J|h?Imkf9uR(4_D0>8?4L;nb^XMlK zk=RsfwY3gx-Nxl_$I{@}Zy)t=$h&l-oAhw`Us~CBGh2V#o15IZ4GmRe2s~G&vc53V z7|X~f6y|{P1><*Qu@p{D!-=;|^I$446^_S?eegA85b5$d4SqrT^tNj(CH=%cFD6d5 zdIej>TFkh8eB1AaqxSjW|3)+cH1x#Q6ht(@G081r64T=l5Kd2nRoP;6-6Qm#*v@@C zPLYJN(zSJ;$&l{UV~EGP#e6KGAH0q;fuf`37FSh0vC4LWi6+??XxjG?i78I)9_urq zH@|@+rzkM+SIJ|`F)*&5k@>Em5irKM2ATXbn&H?!S3OT??4s4Wl(9%1L(}c)z)_Ij z6TZAPb$@TT^7qh*xj9E~QB__x6Qmk^;eLRtH;Lv#>{f!?4urQ5QU^oPjy?&Lh$Md({@ zR`}uR_sww+73k$uH|u&ri{_(aCHqTk<0@@L@&5X9$&i}q;I7SWbp9(E_v9B_OJ^nFg=gIH)?Y)uM$_uQp3IuO? zf5T-l+p?~2qEmn5n4yGx_|nK=@ezpl%xshyF8t4dOGN_P`HqqnkhmF!6Y?9B8zjyE zAm;R{akIED&C3MIQ|M3BUcI6)taipOrVN}z)3MwW1{klM!k>l~0*e`hsG4W0%0DJH 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�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 zy6->(expected: &[u8]) { + let mut e = G::zero(); + + let mut v = vec![]; + { + let mut expected = expected; + for _ in 0..1000 { + let e_affine = e.into_affine(); + let encoded = E::from_affine(e_affine); + v.extend_from_slice(encoded.as_ref()); + + let mut decoded = E::empty(); + decoded.as_mut().copy_from_slice(&expected[0..E::size()]); + expected = &expected[E::size()..]; + let decoded = decoded.into_affine().unwrap(); + assert_eq!(e_affine, decoded); + + e.add_assign(&G::one()); + } + } + + assert_eq!(&v[..], expected); +} + +#[test] +fn test_g1_uncompressed_valid_vectors() { + test_vectors::(include_bytes!("g1_uncompressed_valid_test_vectors.dat")); +} + +#[test] +fn test_g1_compressed_valid_vectors() { + test_vectors::(include_bytes!("g1_compressed_valid_test_vectors.dat")); +} + +#[test] +fn test_g2_uncompressed_valid_vectors() { + test_vectors::(include_bytes!("g2_uncompressed_valid_test_vectors.dat")); +} + +#[test] +fn test_g2_compressed_valid_vectors() { + test_vectors::(include_bytes!("g2_compressed_valid_test_vectors.dat")); +} + +#[test] +fn test_g1_uncompressed_invalid_vectors() { + { + let z = G1Affine::zero().into_uncompressed(); + + { + let mut z = z; + z.as_mut()[0] |= 0b1000_0000; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected an uncompressed point"); + } + } + + { + let mut z = z; + z.as_mut()[0] |= 0b0010_0000; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); + } + } + + for i in 0..G1Uncompressed::size() { + let mut z = z; + z.as_mut()[i] |= 0b0000_0001; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); + } + } + } + + let o = G1Affine::one().into_uncompressed(); + + { + let mut o = o; + o.as_mut()[0] |= 0b1000_0000; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected an uncompressed point"); + } + } + + let m = Fq::char(); + + { + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[48..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "y coordinate"); + } else { + panic!("should have rejected the point") + } + } + + { + let m = Fq::zero().into_repr(); + + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + + if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because it isn't on the curve") + } + } + + { + let mut o = o; + let mut x = Fq::one(); + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + + if let Some(y) = x3b.sqrt() { + // We know this is on the curve, but it's likely not going to be in the correct subgroup. + x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + + if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + break; + } else { + panic!( + "should have rejected the point because it isn't in the correct subgroup" + ) + } + } else { + x.add_assign(&Fq::one()); + } + } + } +} + +#[test] +fn test_g2_uncompressed_invalid_vectors() { + { + let z = G2Affine::zero().into_uncompressed(); + + { + let mut z = z; + z.as_mut()[0] |= 0b1000_0000; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected an uncompressed point"); + } + } + + { + let mut z = z; + z.as_mut()[0] |= 0b0010_0000; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); + } + } + + for i in 0..G2Uncompressed::size() { + let mut z = z; + z.as_mut()[i] |= 0b0000_0001; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); + } + } + } + + let o = G2Affine::one().into_uncompressed(); + + { + let mut o = o; + o.as_mut()[0] |= 0b1000_0000; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected an uncompressed point"); + } + } + + let m = Fq::char(); + + { + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate (c1)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[48..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate (c0)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[96..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "y coordinate (c1)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[144..]).unwrap(); + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "y coordinate (c0)"); + } else { + panic!("should have rejected the point") + } + } + + { + let m = Fq::zero().into_repr(); + + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + m.write_be(&mut o.as_mut()[48..]).unwrap(); + + if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because it isn't on the curve") + } + } + + { + let mut o = o; + let mut x = Fq2::one(); + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq2 { + c0: Fq::from_repr(FqRepr::from(4)).unwrap(), + c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + }); // TODO: perhaps expose coeff_b through API? + + if let Some(y) = x3b.sqrt() { + // We know this is on the curve, but it's likely not going to be in the correct subgroup. + x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + y.c1.into_repr().write_be(&mut o.as_mut()[96..]).unwrap(); + y.c0.into_repr().write_be(&mut o.as_mut()[144..]).unwrap(); + + if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + break; + } else { + panic!( + "should have rejected the point because it isn't in the correct subgroup" + ) + } + } else { + x.add_assign(&Fq2::one()); + } + } + } +} + +#[test] +fn test_g1_compressed_invalid_vectors() { + { + let z = G1Affine::zero().into_compressed(); + + { + let mut z = z; + z.as_mut()[0] &= 0b0111_1111; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected a compressed point"); + } + } + + { + let mut z = z; + z.as_mut()[0] |= 0b0010_0000; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); + } + } + + for i in 0..G1Compressed::size() { + let mut z = z; + z.as_mut()[i] |= 0b0000_0001; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); + } + } + } + + let o = G1Affine::one().into_compressed(); + + { + let mut o = o; + o.as_mut()[0] &= 0b0111_1111; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected a compressed point"); + } + } + + let m = Fq::char(); + + { + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + let mut x = Fq::one(); + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + + if let Some(_) = x3b.sqrt() { + x.add_assign(&Fq::one()); + } else { + x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + break; + } else { + panic!("should have rejected the point because it isn't on the curve") + } + } + } + } + + { + let mut o = o; + let mut x = Fq::one(); + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? + + if let Some(_) = x3b.sqrt() { + // We know this is on the curve, but it's likely not going to be in the correct subgroup. + x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + break; + } else { + panic!( + "should have rejected the point because it isn't in the correct subgroup" + ) + } + } else { + x.add_assign(&Fq::one()); + } + } + } +} + +#[test] +fn test_g2_compressed_invalid_vectors() { + { + let z = G2Affine::zero().into_compressed(); + + { + let mut z = z; + z.as_mut()[0] &= 0b0111_1111; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected a compressed point"); + } + } + + { + let mut z = z; + z.as_mut()[0] |= 0b0010_0000; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the parity bit should not be set if the point is at infinity"); + } + } + + for i in 0..G2Compressed::size() { + let mut z = z; + z.as_mut()[i] |= 0b0000_0001; + if let Err(GroupDecodingError::UnexpectedInformation) = z.into_affine() { + // :) + } else { + panic!("should have rejected the point because the coordinates should be zeroes at the point at infinity"); + } + } + } + + let o = G2Affine::one().into_compressed(); + + { + let mut o = o; + o.as_mut()[0] &= 0b0111_1111; + if let Err(GroupDecodingError::UnexpectedCompressionMode) = o.into_affine() { + // :) + } else { + panic!("should have rejected the point because we expected a compressed point"); + } + } + + let m = Fq::char(); + + { + let mut o = o; + m.write_be(&mut o.as_mut()[0..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate (c1)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + m.write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::CoordinateDecodingError(coordinate, _)) = o.into_affine() { + assert_eq!(coordinate, "x coordinate (c0)"); + } else { + panic!("should have rejected the point") + } + } + + { + let mut o = o; + let mut x = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq2 { + c0: Fq::from_repr(FqRepr::from(4)).unwrap(), + c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + }); // TODO: perhaps expose coeff_b through API? + + if let Some(_) = x3b.sqrt() { + x.add_assign(&Fq2::one()); + } else { + x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::NotOnCurve) = o.into_affine() { + break; + } else { + panic!("should have rejected the point because it isn't on the curve") + } + } + } + } + + { + let mut o = o; + let mut x = Fq2 { + c0: Fq::one(), + c1: Fq::one(), + }; + + loop { + let mut x3b = x; + x3b.square(); + x3b.mul_assign(&x); + x3b.add_assign(&Fq2 { + c0: Fq::from_repr(FqRepr::from(4)).unwrap(), + c1: Fq::from_repr(FqRepr::from(4)).unwrap(), + }); // TODO: perhaps expose coeff_b through API? + + if let Some(_) = x3b.sqrt() { + // We know this is on the curve, but it's likely not going to be in the correct subgroup. + x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); + x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); + o.as_mut()[0] |= 0b1000_0000; + + if let Err(GroupDecodingError::NotInSubgroup) = o.into_affine() { + break; + } else { + panic!( + "should have rejected the point because it isn't in the correct subgroup" + ) + } + } else { + x.add_assign(&Fq2::one()); + } + } + } +} diff --git a/src/rust/pairing/src/lib.rs b/src/rust/pairing/src/lib.rs new file mode 100644 index 000000000..cc241514b --- /dev/null +++ b/src/rust/pairing/src/lib.rs @@ -0,0 +1,757 @@ +// `clippy` is a code linting tool for improving code quality by catching +// common mistakes or strange code patterns. If the `cargo-clippy` feature +// is provided, all compiler warnings are prohibited. +#![cfg_attr(feature = "cargo-clippy", deny(warnings))] +#![cfg_attr(feature = "cargo-clippy", allow(inline_always))] +#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] +#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] +#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))] +#![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))] +#![cfg_attr(feature = "cargo-clippy", allow(write_literal))] + +// Force public structures to implement Debug +#![deny(missing_debug_implementations)] + +extern crate byteorder; +extern crate rand; + +#[cfg(test)] +pub mod tests; + +pub mod bls12_381; + +mod wnaf; +pub use self::wnaf::Wnaf; + +use std::error::Error; +use std::fmt; +use std::io::{self, Read, Write}; + +/// An "engine" is a collection of types (fields, elliptic curve groups, etc.) +/// with well-defined relationships. In particular, the G1/G2 curve groups are +/// of prime order `r`, and are equipped with a bilinear pairing function. +pub trait Engine: Sized + 'static + Clone { + /// This is the scalar field of the G1/G2 groups. + type Fr: PrimeField + SqrtField; + + /// The projective representation of an element in G1. + type G1: CurveProjective< + Engine = Self, + Base = Self::Fq, + Scalar = Self::Fr, + Affine = Self::G1Affine, + > + + From; + + /// The affine representation of an element in G1. + type G1Affine: CurveAffine< + Engine = Self, + Base = Self::Fq, + Scalar = Self::Fr, + Projective = Self::G1, + Pair = Self::G2Affine, + PairingResult = Self::Fqk, + > + + From; + + /// The projective representation of an element in G2. + type G2: CurveProjective< + Engine = Self, + Base = Self::Fqe, + Scalar = Self::Fr, + Affine = Self::G2Affine, + > + + From; + + /// The affine representation of an element in G2. + type G2Affine: CurveAffine< + Engine = Self, + Base = Self::Fqe, + Scalar = Self::Fr, + Projective = Self::G2, + Pair = Self::G1Affine, + PairingResult = Self::Fqk, + > + + From; + + /// The base field that hosts G1. + type Fq: PrimeField + SqrtField; + + /// The extension field that hosts G2. + type Fqe: SqrtField; + + /// The extension field that hosts the target group of the pairing. + type Fqk: Field; + + /// Perform a miller loop with some number of (G1, G2) pairs. + fn miller_loop<'a, I>(i: I) -> Self::Fqk + where + I: IntoIterator< + Item = &'a ( + &'a ::Prepared, + &'a ::Prepared, + ), + >; + + /// Perform final exponentiation of the result of a miller loop. + fn final_exponentiation(&Self::Fqk) -> Option; + + /// Performs a complete pairing operation `(p, q)`. + fn pairing(p: G1, q: G2) -> Self::Fqk + where + G1: Into, + G2: Into, + { + Self::final_exponentiation(&Self::miller_loop( + [(&(p.into().prepare()), &(q.into().prepare()))].into_iter(), + )).unwrap() + } +} + +/// Projective representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait CurveProjective: + PartialEq + + Eq + + Sized + + Copy + + Clone + + Send + + Sync + + fmt::Debug + + fmt::Display + + rand::Rand + + 'static +{ + type Engine: Engine; + type Scalar: PrimeField + SqrtField; + type Base: SqrtField; + type Affine: CurveAffine; + + /// Returns the additive identity. + fn zero() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn one() -> Self; + + /// Determines if this point is the point at infinity. + fn is_zero(&self) -> bool; + + /// Normalizes a slice of projective elements so that + /// conversion to affine is cheap. + fn batch_normalization(v: &mut [Self]); + + /// Checks if the point is already "normalized" so that + /// cheap affine conversion is possible. + fn is_normalized(&self) -> bool; + + /// Doubles this element. + fn double(&mut self); + + /// Adds another element to this element. + fn add_assign(&mut self, other: &Self); + + /// Subtracts another element from this element. + fn sub_assign(&mut self, other: &Self) { + let mut tmp = *other; + tmp.negate(); + self.add_assign(&tmp); + } + + /// Adds an affine element to this element. + fn add_assign_mixed(&mut self, other: &Self::Affine); + + /// Negates this element. + fn negate(&mut self); + + /// Performs scalar multiplication of this element. + fn mul_assign::Repr>>(&mut self, other: S); + + /// Converts this element into its affine representation. + fn into_affine(&self) -> Self::Affine; + + /// Recommends a wNAF window table size given a scalar. Always returns a number + /// between 2 and 22, inclusive. + fn recommended_wnaf_for_scalar(scalar: ::Repr) -> usize; + + /// Recommends a wNAF window size given the number of scalars you intend to multiply + /// a base by. Always returns a number between 2 and 22, inclusive. + fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; +} + +/// Affine representation of an elliptic curve point guaranteed to be +/// in the correct prime order subgroup. +pub trait CurveAffine: + Copy + Clone + Sized + Send + Sync + fmt::Debug + fmt::Display + PartialEq + Eq + 'static +{ + type Engine: Engine; + type Scalar: PrimeField + SqrtField; + type Base: SqrtField; + type Projective: CurveProjective; + type Prepared: Clone + Send + Sync + 'static; + type Uncompressed: EncodedPoint; + type Compressed: EncodedPoint; + type Pair: CurveAffine; + type PairingResult: Field; + + /// Returns the additive identity. + fn zero() -> Self; + + /// Returns a fixed generator of unknown exponent. + fn one() -> Self; + + /// Determines if this point represents the point at infinity; the + /// additive identity. + fn is_zero(&self) -> bool; + + /// Negates this element. + fn negate(&mut self); + + /// Performs scalar multiplication of this element with mixed addition. + fn mul::Repr>>(&self, other: S) -> Self::Projective; + + /// Prepares this element for pairing purposes. + fn prepare(&self) -> Self::Prepared; + + /// Perform a pairing + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; + + /// Converts this element into its affine representation. + fn into_projective(&self) -> Self::Projective; + + /// Converts this element into its compressed encoding, so long as it's not + /// the point at infinity. + fn into_compressed(&self) -> Self::Compressed { + ::from_affine(*self) + } + + /// Converts this element into its uncompressed encoding, so long as it's not + /// the point at infinity. + fn into_uncompressed(&self) -> Self::Uncompressed { + ::from_affine(*self) + } +} + +/// An encoded elliptic curve point, which should essentially wrap a `[u8; N]`. +pub trait EncodedPoint: + Sized + Send + Sync + AsRef<[u8]> + AsMut<[u8]> + Clone + Copy + 'static +{ + type Affine: CurveAffine; + + /// Creates an empty representation. + fn empty() -> Self; + + /// Returns the number of bytes consumed by this representation. + fn size() -> usize; + + /// Converts an `EncodedPoint` into a `CurveAffine` element, + /// if the encoding represents a valid element. + fn into_affine(&self) -> Result; + + /// Converts an `EncodedPoint` into a `CurveAffine` element, + /// without guaranteeing that the encoding represents a valid + /// element. This is useful when the caller knows the encoding is + /// valid already. + /// + /// If the encoding is invalid, this can break API invariants, + /// so caution is strongly encouraged. + fn into_affine_unchecked(&self) -> Result; + + /// Creates an `EncodedPoint` from an affine point, as long as the + /// point is not the point at infinity. + fn from_affine(affine: Self::Affine) -> Self; +} + +/// This trait represents an element of a field. +pub trait Field: + Sized + Eq + Copy + Clone + Send + Sync + fmt::Debug + fmt::Display + 'static + rand::Rand +{ + /// Returns the zero element of the field, the additive identity. + fn zero() -> Self; + + /// Returns the one element of the field, the multiplicative identity. + fn one() -> Self; + + /// Returns true iff this element is zero. + fn is_zero(&self) -> bool; + + /// Squares this element. + fn square(&mut self); + + /// Doubles this element. + fn double(&mut self); + + /// Negates this element. + fn negate(&mut self); + + /// Adds another element to this element. + fn add_assign(&mut self, other: &Self); + + /// Subtracts another element from this element. + fn sub_assign(&mut self, other: &Self); + + /// Multiplies another element by this element. + fn mul_assign(&mut self, other: &Self); + + /// Computes the multiplicative inverse of this element, if nonzero. + fn inverse(&self) -> Option; + + /// Exponentiates this element by a power of the base prime modulus via + /// the Frobenius automorphism. + fn frobenius_map(&mut self, power: usize); + + /// Exponentiates this element by a number represented with `u64` limbs, + /// least significant digit first. + fn pow>(&self, exp: S) -> Self { + let mut res = Self::one(); + + let mut found_one = false; + + for i in BitIterator::new(exp) { + if found_one { + res.square(); + } else { + found_one = i; + } + + if i { + res.mul_assign(self); + } + } + + res + } +} + +/// This trait represents an element of a field that has a square root operation described for it. +pub trait SqrtField: Field { + /// Returns the Legendre symbol of the field element. + fn legendre(&self) -> LegendreSymbol; + + /// Returns the square root of the field element, if it is + /// quadratic residue. + fn sqrt(&self) -> Option; +} + +/// This trait represents a wrapper around a biginteger which can encode any element of a particular +/// prime field. It is a smart wrapper around a sequence of `u64` limbs, least-significant digit +/// first. +pub trait PrimeFieldRepr: + Sized + + Copy + + Clone + + Eq + + Ord + + Send + + Sync + + Default + + fmt::Debug + + fmt::Display + + 'static + + rand::Rand + + AsRef<[u64]> + + AsMut<[u64]> + + From +{ + /// Subtract another represetation from this one. + fn sub_noborrow(&mut self, other: &Self); + + /// Add another representation to this one. + fn add_nocarry(&mut self, other: &Self); + + /// Compute the number of bits needed to encode this number. Always a + /// multiple of 64. + fn num_bits(&self) -> u32; + + /// Returns true iff this number is zero. + fn is_zero(&self) -> bool; + + /// Returns true iff this number is odd. + fn is_odd(&self) -> bool; + + /// Returns true iff this number is even. + fn is_even(&self) -> bool; + + /// Performs a rightwise bitshift of this number, effectively dividing + /// it by 2. + fn div2(&mut self); + + /// Performs a rightwise bitshift of this number by some amount. + fn shr(&mut self, amt: u32); + + /// Performs a leftwise bitshift of this number, effectively multiplying + /// it by 2. Overflow is ignored. + fn mul2(&mut self); + + /// Performs a leftwise bitshift of this number by some amount. + fn shl(&mut self, amt: u32); + + /// Writes this `PrimeFieldRepr` as a big endian integer. + fn write_be(&self, mut writer: W) -> io::Result<()> { + use byteorder::{BigEndian, WriteBytesExt}; + + for digit in self.as_ref().iter().rev() { + writer.write_u64::(*digit)?; + } + + Ok(()) + } + + /// Reads a big endian integer into this representation. + fn read_be(&mut self, mut reader: R) -> io::Result<()> { + use byteorder::{BigEndian, ReadBytesExt}; + + for digit in self.as_mut().iter_mut().rev() { + *digit = reader.read_u64::()?; + } + + Ok(()) + } + + /// Writes this `PrimeFieldRepr` as a little endian integer. + fn write_le(&self, mut writer: W) -> io::Result<()> { + use byteorder::{LittleEndian, WriteBytesExt}; + + for digit in self.as_ref().iter() { + writer.write_u64::(*digit)?; + } + + Ok(()) + } + + /// Reads a little endian integer into this representation. + fn read_le(&mut self, mut reader: R) -> io::Result<()> { + use byteorder::{LittleEndian, ReadBytesExt}; + + for digit in self.as_mut().iter_mut() { + *digit = reader.read_u64::()?; + } + + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub enum LegendreSymbol { + Zero = 0, + QuadraticResidue = 1, + QuadraticNonResidue = -1, +} + +/// An error that may occur when trying to interpret a `PrimeFieldRepr` as a +/// `PrimeField` element. +#[derive(Debug)] +pub enum PrimeFieldDecodingError { + /// The encoded value is not in the field + NotInField(String), +} + +impl Error for PrimeFieldDecodingError { + fn description(&self) -> &str { + match *self { + PrimeFieldDecodingError::NotInField(..) => "not an element of the field", + } + } +} + +impl fmt::Display for PrimeFieldDecodingError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + PrimeFieldDecodingError::NotInField(ref repr) => { + write!(f, "{} is not an element of the field", repr) + } + } + } +} + +/// An error that may occur when trying to decode an `EncodedPoint`. +#[derive(Debug)] +pub enum GroupDecodingError { + /// The coordinate(s) do not lie on the curve. + NotOnCurve, + /// The element is not part of the r-order subgroup. + NotInSubgroup, + /// One of the coordinates could not be decoded + CoordinateDecodingError(&'static str, PrimeFieldDecodingError), + /// The compression mode of the encoded element was not as expected + UnexpectedCompressionMode, + /// The encoding contained bits that should not have been set + UnexpectedInformation, +} + +impl Error for GroupDecodingError { + fn description(&self) -> &str { + match *self { + GroupDecodingError::NotOnCurve => "coordinate(s) do not lie on the curve", + GroupDecodingError::NotInSubgroup => "the element is not part of an r-order subgroup", + GroupDecodingError::CoordinateDecodingError(..) => "coordinate(s) could not be decoded", + GroupDecodingError::UnexpectedCompressionMode => { + "encoding has unexpected compression mode" + } + GroupDecodingError::UnexpectedInformation => "encoding has unexpected information", + } + } +} + +impl fmt::Display for GroupDecodingError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + GroupDecodingError::CoordinateDecodingError(description, ref err) => { + write!(f, "{} decoding error: {}", description, err) + } + _ => write!(f, "{}", self.description()), + } + } +} + +/// This represents an element of a prime field. +pub trait PrimeField: Field { + /// The prime field can be converted back and forth into this biginteger + /// representation. + type Repr: PrimeFieldRepr + From; + + /// Interpret a string of numbers as a (congruent) prime field element. + /// Does not accept unnecessary leading zeroes or a blank string. + fn from_str(s: &str) -> Option { + if s.is_empty() { + return None; + } + + if s == "0" { + return Some(Self::zero()); + } + + let mut res = Self::zero(); + + let ten = Self::from_repr(Self::Repr::from(10)).unwrap(); + + let mut first_digit = true; + + for c in s.chars() { + match c.to_digit(10) { + Some(c) => { + if first_digit { + if c == 0 { + return None; + } + + first_digit = false; + } + + res.mul_assign(&ten); + res.add_assign(&Self::from_repr(Self::Repr::from(u64::from(c))).unwrap()); + } + None => { + return None; + } + } + } + + Some(res) + } + + /// Convert this prime field element into a biginteger representation. + fn from_repr(Self::Repr) -> Result; + + /// Convert a biginteger representation into a prime field element, if + /// the number is an element of the field. + fn into_repr(&self) -> Self::Repr; + + /// Returns the field characteristic; the modulus. + fn char() -> Self::Repr; + + /// How many bits are needed to represent an element of this field. + const NUM_BITS: u32; + + /// How many bits of information can be reliably stored in the field element. + const CAPACITY: u32; + + /// Returns the multiplicative generator of `char()` - 1 order. This element + /// must also be quadratic nonresidue. + fn multiplicative_generator() -> Self; + + /// 2^s * t = `char()` - 1 with t odd. + const S: u32; + + /// Returns the 2^s root of unity computed by exponentiating the `multiplicative_generator()` + /// by t. + fn root_of_unity() -> Self; +} + +#[derive(Debug)] +pub struct BitIterator { + t: E, + n: usize, +} + +impl> BitIterator { + pub fn new(t: E) -> Self { + let n = t.as_ref().len() * 64; + + BitIterator { t, n } + } +} + +impl> Iterator for BitIterator { + type Item = bool; + + fn next(&mut self) -> Option { + if self.n == 0 { + None + } else { + self.n -= 1; + let part = self.n / 64; + let bit = self.n - (64 * part); + + Some(self.t.as_ref()[part] & (1 << bit) > 0) + } + } +} + +#[test] +fn test_bit_iterator() { + let mut a = BitIterator::new([0xa953d79b83f6ab59, 0x6dea2059e200bd39]); + let expected = "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001"; + + for e in expected.chars() { + assert!(a.next().unwrap() == (e == '1')); + } + + assert!(a.next().is_none()); + + let expected = "1010010101111110101010000101101011101000011101110101001000011001100100100011011010001011011011010001011011101100110100111011010010110001000011110100110001100110011101101000101100011100100100100100001010011101010111110011101011000011101000111011011101011001"; + + let mut a = BitIterator::new([ + 0x429d5f3ac3a3b759, + 0xb10f4c66768b1c92, + 0x92368b6d16ecd3b4, + 0xa57ea85ae8775219, + ]); + + for e in expected.chars() { + assert!(a.next().unwrap() == (e == '1')); + } + + assert!(a.next().is_none()); +} + +#[cfg(not(feature = "expose-arith"))] +use self::arith_impl::*; + +#[cfg(feature = "expose-arith")] +pub use self::arith_impl::*; + +#[cfg(feature = "u128-support")] +mod arith_impl { + /// Calculate a - b - borrow, returning the result and modifying + /// the borrow value. + #[inline(always)] + pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 { + let tmp = (1u128 << 64) + u128::from(a) - u128::from(b) - u128::from(*borrow); + + *borrow = if tmp >> 64 == 0 { 1 } else { 0 }; + + tmp as u64 + } + + /// Calculate a + b + carry, returning the sum and modifying the + /// carry value. + #[inline(always)] + pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 { + let tmp = u128::from(a) + u128::from(b) + u128::from(*carry); + + *carry = (tmp >> 64) as u64; + + tmp as u64 + } + + /// Calculate a + (b * c) + carry, returning the least significant digit + /// and setting carry to the most significant digit. + #[inline(always)] + pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { + let tmp = (u128::from(a)) + u128::from(b) * u128::from(c) + u128::from(*carry); + + *carry = (tmp >> 64) as u64; + + tmp as u64 + } +} + +#[cfg(not(feature = "u128-support"))] +mod arith_impl { + #[inline(always)] + fn split_u64(i: u64) -> (u64, u64) { + (i >> 32, i & 0xFFFFFFFF) + } + + #[inline(always)] + fn combine_u64(hi: u64, lo: u64) -> u64 { + (hi << 32) | lo + } + + /// Calculate a - b - borrow, returning the result and modifying + /// the borrow value. + #[inline(always)] + pub fn sbb(a: u64, b: u64, borrow: &mut u64) -> u64 { + let (a_hi, a_lo) = split_u64(a); + let (b_hi, b_lo) = split_u64(b); + let (b, r0) = split_u64((1 << 32) + a_lo - b_lo - *borrow); + let (b, r1) = split_u64((1 << 32) + a_hi - b_hi - ((b == 0) as u64)); + + *borrow = (b == 0) as u64; + + combine_u64(r1, r0) + } + + /// Calculate a + b + carry, returning the sum and modifying the + /// carry value. + #[inline(always)] + pub fn adc(a: u64, b: u64, carry: &mut u64) -> u64 { + let (a_hi, a_lo) = split_u64(a); + let (b_hi, b_lo) = split_u64(b); + let (carry_hi, carry_lo) = split_u64(*carry); + + let (t, r0) = split_u64(a_lo + b_lo + carry_lo); + let (t, r1) = split_u64(t + a_hi + b_hi + carry_hi); + + *carry = t; + + combine_u64(r1, r0) + } + + /// Calculate a + (b * c) + carry, returning the least significant digit + /// and setting carry to the most significant digit. + #[inline(always)] + pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { + /* + [ b_hi | b_lo ] + [ c_hi | c_lo ] * + ------------------------------------------- + [ b_lo * c_lo ] <-- w + [ b_hi * c_lo ] <-- x + [ b_lo * c_hi ] <-- y + [ b_hi * c_lo ] <-- z + [ a_hi | a_lo ] + [ C_hi | C_lo ] + */ + + let (a_hi, a_lo) = split_u64(a); + let (b_hi, b_lo) = split_u64(b); + let (c_hi, c_lo) = split_u64(c); + let (carry_hi, carry_lo) = split_u64(*carry); + + let (w_hi, w_lo) = split_u64(b_lo * c_lo); + let (x_hi, x_lo) = split_u64(b_hi * c_lo); + let (y_hi, y_lo) = split_u64(b_lo * c_hi); + let (z_hi, z_lo) = split_u64(b_hi * c_hi); + + let (t, r0) = split_u64(w_lo + a_lo + carry_lo); + let (t, r1) = split_u64(t + w_hi + x_lo + y_lo + a_hi + carry_hi); + let (t, r2) = split_u64(t + x_hi + y_hi + z_lo); + let (_, r3) = split_u64(t + z_hi); + + *carry = combine_u64(r3, r2); + + combine_u64(r1, r0) + } +} diff --git a/src/rust/pairing/src/tests/curve.rs b/src/rust/pairing/src/tests/curve.rs new file mode 100644 index 000000000..1480b74fe --- /dev/null +++ b/src/rust/pairing/src/tests/curve.rs @@ -0,0 +1,420 @@ +use rand::{Rand, Rng, SeedableRng, XorShiftRng}; + +use {CurveAffine, CurveProjective, EncodedPoint, Field}; + +pub fn curve_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + // Negation edge case with zero. + { + let mut z = G::zero(); + z.negate(); + assert!(z.is_zero()); + } + + // Doubling edge case with zero. + { + let mut z = G::zero(); + z.double(); + assert!(z.is_zero()); + } + + // Addition edge cases with zero + { + let mut r = G::rand(&mut rng); + let rcopy = r; + r.add_assign(&G::zero()); + assert_eq!(r, rcopy); + r.add_assign_mixed(&G::Affine::zero()); + assert_eq!(r, rcopy); + + let mut z = G::zero(); + z.add_assign(&G::zero()); + assert!(z.is_zero()); + z.add_assign_mixed(&G::Affine::zero()); + assert!(z.is_zero()); + + let mut z2 = z; + z2.add_assign(&r); + + z.add_assign_mixed(&r.into_affine()); + + assert_eq!(z, z2); + assert_eq!(z, r); + } + + // Transformations + { + let a = G::rand(&mut rng); + let b = a.into_affine().into_projective(); + let c = a + .into_affine() + .into_projective() + .into_affine() + .into_projective(); + assert_eq!(a, b); + assert_eq!(b, c); + } + + random_addition_tests::(); + random_multiplication_tests::(); + random_doubling_tests::(); + random_negation_tests::(); + random_transformation_tests::(); + random_wnaf_tests::(); + random_encoding_tests::(); +} + +fn random_wnaf_tests() { + use wnaf::*; + use PrimeField; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + { + let mut table = vec![]; + let mut wnaf = vec![]; + + for w in 2..14 { + for _ in 0..100 { + let g = G::rand(&mut rng); + let s = G::Scalar::rand(&mut rng).into_repr(); + let mut g1 = g; + g1.mul_assign(s); + + wnaf_table(&mut table, g, w); + wnaf_form(&mut wnaf, s, w); + let g2 = wnaf_exp(&table, &wnaf); + + assert_eq!(g1, g2); + } + } + } + + { + fn only_compiles_if_send(_: &S) {} + + for _ in 0..100 { + let g = G::rand(&mut rng); + let s = G::Scalar::rand(&mut rng).into_repr(); + let mut g1 = g; + g1.mul_assign(s); + + let g2 = { + let mut wnaf = Wnaf::new(); + wnaf.base(g, 1).scalar(s) + }; + let g3 = { + let mut wnaf = Wnaf::new(); + wnaf.scalar(s).base(g) + }; + let g4 = { + let mut wnaf = Wnaf::new(); + let mut shared = wnaf.base(g, 1).shared(); + + only_compiles_if_send(&shared); + + shared.scalar(s) + }; + let g5 = { + let mut wnaf = Wnaf::new(); + let mut shared = wnaf.scalar(s).shared(); + + only_compiles_if_send(&shared); + + shared.base(g) + }; + + let g6 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + wnaf.base(g, 1).scalar(s) + }; + let g7 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + wnaf.scalar(s).base(g) + }; + let g8 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + let mut shared = wnaf.base(g, 1).shared(); + + only_compiles_if_send(&shared); + + shared.scalar(s) + }; + let g9 = { + let mut wnaf = Wnaf::new(); + { + // Populate the vectors. + wnaf.base(rng.gen(), 1).scalar(rng.gen()); + } + let mut shared = wnaf.scalar(s).shared(); + + only_compiles_if_send(&shared); + + shared.base(g) + }; + + assert_eq!(g1, g2); + assert_eq!(g1, g3); + assert_eq!(g1, g4); + assert_eq!(g1, g5); + assert_eq!(g1, g6); + assert_eq!(g1, g7); + assert_eq!(g1, g8); + assert_eq!(g1, g9); + } + } +} + +fn random_negation_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let r = G::rand(&mut rng); + + let s = G::Scalar::rand(&mut rng); + let mut sneg = s; + sneg.negate(); + + let mut t1 = r; + t1.mul_assign(s); + + let mut t2 = r; + t2.mul_assign(sneg); + + let mut t3 = t1; + t3.add_assign(&t2); + assert!(t3.is_zero()); + + let mut t4 = t1; + t4.add_assign_mixed(&t2.into_affine()); + assert!(t4.is_zero()); + + t1.negate(); + assert_eq!(t1, t2); + } +} + +fn random_doubling_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut a = G::rand(&mut rng); + let mut b = G::rand(&mut rng); + + // 2(a + b) + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.double(); + + // 2a + 2b + a.double(); + b.double(); + + let mut tmp2 = a; + tmp2.add_assign(&b); + + let mut tmp3 = a; + tmp3.add_assign_mixed(&b.into_affine()); + + assert_eq!(tmp1, tmp2); + assert_eq!(tmp1, tmp3); + } +} + +fn random_multiplication_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut a = G::rand(&mut rng); + let mut b = G::rand(&mut rng); + let a_affine = a.into_affine(); + let b_affine = b.into_affine(); + + let s = G::Scalar::rand(&mut rng); + + // s ( a + b ) + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.mul_assign(s); + + // sa + sb + a.mul_assign(s); + b.mul_assign(s); + + let mut tmp2 = a; + tmp2.add_assign(&b); + + // Affine multiplication + let mut tmp3 = a_affine.mul(s); + tmp3.add_assign(&b_affine.mul(s)); + + assert_eq!(tmp1, tmp2); + assert_eq!(tmp1, tmp3); + } +} + +fn random_addition_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = G::rand(&mut rng); + let b = G::rand(&mut rng); + let c = G::rand(&mut rng); + let a_affine = a.into_affine(); + let b_affine = b.into_affine(); + let c_affine = c.into_affine(); + + // a + a should equal the doubling + { + let mut aplusa = a; + aplusa.add_assign(&a); + + let mut aplusamixed = a; + aplusamixed.add_assign_mixed(&a.into_affine()); + + let mut adouble = a; + adouble.double(); + + assert_eq!(aplusa, adouble); + assert_eq!(aplusa, aplusamixed); + } + + let mut tmp = vec![G::zero(); 6]; + + // (a + b) + c + tmp[0] = a; + tmp[0].add_assign(&b); + tmp[0].add_assign(&c); + + // a + (b + c) + tmp[1] = b; + tmp[1].add_assign(&c); + tmp[1].add_assign(&a); + + // (a + c) + b + tmp[2] = a; + tmp[2].add_assign(&c); + tmp[2].add_assign(&b); + + // Mixed addition + + // (a + b) + c + tmp[3] = a_affine.into_projective(); + tmp[3].add_assign_mixed(&b_affine); + tmp[3].add_assign_mixed(&c_affine); + + // a + (b + c) + tmp[4] = b_affine.into_projective(); + tmp[4].add_assign_mixed(&c_affine); + tmp[4].add_assign_mixed(&a_affine); + + // (a + c) + b + tmp[5] = a_affine.into_projective(); + tmp[5].add_assign_mixed(&c_affine); + tmp[5].add_assign_mixed(&b_affine); + + // Comparisons + for i in 0..6 { + for j in 0..6 { + assert_eq!(tmp[i], tmp[j]); + assert_eq!(tmp[i].into_affine(), tmp[j].into_affine()); + } + + assert!(tmp[i] != a); + assert!(tmp[i] != b); + assert!(tmp[i] != c); + + assert!(a != tmp[i]); + assert!(b != tmp[i]); + assert!(c != tmp[i]); + } + } +} + +fn random_transformation_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let g = G::rand(&mut rng); + let g_affine = g.into_affine(); + let g_projective = g_affine.into_projective(); + assert_eq!(g, g_projective); + } + + // Batch normalization + for _ in 0..10 { + let mut v = (0..1000).map(|_| G::rand(&mut rng)).collect::>(); + + for i in &v { + assert!(!i.is_normalized()); + } + + use rand::distributions::{IndependentSample, Range}; + let between = Range::new(0, 1000); + // Sprinkle in some normalized points + for _ in 0..5 { + v[between.ind_sample(&mut rng)] = G::zero(); + } + for _ in 0..5 { + let s = between.ind_sample(&mut rng); + v[s] = v[s].into_affine().into_projective(); + } + + let expected_v = v + .iter() + .map(|v| v.into_affine().into_projective()) + .collect::>(); + G::batch_normalization(&mut v); + + for i in &v { + assert!(i.is_normalized()); + } + + assert_eq!(v, expected_v); + } +} + +fn random_encoding_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + assert_eq!( + G::zero().into_uncompressed().into_affine().unwrap(), + G::zero() + ); + + assert_eq!( + G::zero().into_compressed().into_affine().unwrap(), + G::zero() + ); + + for _ in 0..1000 { + let mut r = G::Projective::rand(&mut rng).into_affine(); + + let uncompressed = r.into_uncompressed(); + let de_uncompressed = uncompressed.into_affine().unwrap(); + assert_eq!(de_uncompressed, r); + + let compressed = r.into_compressed(); + let de_compressed = compressed.into_affine().unwrap(); + assert_eq!(de_compressed, r); + + r.negate(); + + let compressed = r.into_compressed(); + let de_compressed = compressed.into_affine().unwrap(); + assert_eq!(de_compressed, r); + } +} diff --git a/src/rust/pairing/src/tests/engine.rs b/src/rust/pairing/src/tests/engine.rs new file mode 100644 index 000000000..52ff4e070 --- /dev/null +++ b/src/rust/pairing/src/tests/engine.rs @@ -0,0 +1,126 @@ +use rand::{Rand, SeedableRng, XorShiftRng}; + +use {CurveAffine, CurveProjective, Engine, Field, PrimeField}; + +pub fn engine_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..10 { + let a = E::G1::rand(&mut rng).into_affine(); + let b = E::G2::rand(&mut rng).into_affine(); + + assert!(a.pairing_with(&b) == b.pairing_with(&a)); + assert!(a.pairing_with(&b) == E::pairing(a, b)); + } + + for _ in 0..1000 { + let z1 = E::G1Affine::zero().prepare(); + let z2 = E::G2Affine::zero().prepare(); + + let a = E::G1::rand(&mut rng).into_affine().prepare(); + let b = E::G2::rand(&mut rng).into_affine().prepare(); + let c = E::G1::rand(&mut rng).into_affine().prepare(); + let d = E::G2::rand(&mut rng).into_affine().prepare(); + + assert_eq!( + E::Fqk::one(), + E::final_exponentiation(&E::miller_loop(&[(&z1, &b)])).unwrap() + ); + + assert_eq!( + E::Fqk::one(), + E::final_exponentiation(&E::miller_loop(&[(&a, &z2)])).unwrap() + ); + + assert_eq!( + E::final_exponentiation(&E::miller_loop(&[(&z1, &b), (&c, &d)])).unwrap(), + E::final_exponentiation(&E::miller_loop(&[(&a, &z2), (&c, &d)])).unwrap() + ); + + assert_eq!( + E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&z1, &d)])).unwrap(), + E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &z2)])).unwrap() + ); + } + + random_bilinearity_tests::(); + random_miller_loop_tests::(); +} + +fn random_miller_loop_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + // Exercise the miller loop for a reduced pairing + for _ in 0..1000 { + let a = E::G1::rand(&mut rng); + let b = E::G2::rand(&mut rng); + + let p2 = E::pairing(a, b); + + let a = a.into_affine().prepare(); + let b = b.into_affine().prepare(); + + let p1 = E::final_exponentiation(&E::miller_loop(&[(&a, &b)])).unwrap(); + + assert_eq!(p1, p2); + } + + // Exercise a double miller loop + for _ in 0..1000 { + let a = E::G1::rand(&mut rng); + let b = E::G2::rand(&mut rng); + let c = E::G1::rand(&mut rng); + let d = E::G2::rand(&mut rng); + + let ab = E::pairing(a, b); + let cd = E::pairing(c, d); + + let mut abcd = ab; + abcd.mul_assign(&cd); + + let a = a.into_affine().prepare(); + let b = b.into_affine().prepare(); + let c = c.into_affine().prepare(); + let d = d.into_affine().prepare(); + + let abcd_with_double_loop = + E::final_exponentiation(&E::miller_loop(&[(&a, &b), (&c, &d)])).unwrap(); + + assert_eq!(abcd, abcd_with_double_loop); + } +} + +fn random_bilinearity_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = E::G1::rand(&mut rng); + let b = E::G2::rand(&mut rng); + + let c = E::Fr::rand(&mut rng); + let d = E::Fr::rand(&mut rng); + + let mut ac = a; + ac.mul_assign(c); + + let mut ad = a; + ad.mul_assign(d); + + let mut bc = b; + bc.mul_assign(c); + + let mut bd = b; + bd.mul_assign(d); + + let acbd = E::pairing(ac, bd); + let adbc = E::pairing(ad, bc); + + let mut cd = c; + cd.mul_assign(&d); + + let abcd = E::pairing(a, b).pow(cd.into_repr()); + + assert_eq!(acbd, adbc); + assert_eq!(acbd, abcd); + } +} diff --git a/src/rust/pairing/src/tests/field.rs b/src/rust/pairing/src/tests/field.rs new file mode 100644 index 000000000..74422fd3a --- /dev/null +++ b/src/rust/pairing/src/tests/field.rs @@ -0,0 +1,266 @@ +use rand::{Rng, SeedableRng, XorShiftRng}; +use {Field, LegendreSymbol, PrimeField, SqrtField}; + +pub fn random_frobenius_tests>(characteristic: C, maxpower: usize) { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + for i in 0..(maxpower + 1) { + let mut a = F::rand(&mut rng); + let mut b = a; + + for _ in 0..i { + a = a.pow(&characteristic); + } + b.frobenius_map(i); + + assert_eq!(a, b); + } + } +} + +pub fn random_sqrt_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..10000 { + let a = F::rand(&mut rng); + let mut b = a; + b.square(); + assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); + + let b = b.sqrt().unwrap(); + let mut negb = b; + negb.negate(); + + assert!(a == b || a == negb); + } + + let mut c = F::one(); + for _ in 0..10000 { + let mut b = c; + b.square(); + assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); + + b = b.sqrt().unwrap(); + + if b != c { + b.negate(); + } + + assert_eq!(b, c); + + c.add_assign(&F::one()); + } +} + +pub fn random_field_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + random_multiplication_tests::(&mut rng); + random_addition_tests::(&mut rng); + random_subtraction_tests::(&mut rng); + random_negation_tests::(&mut rng); + random_doubling_tests::(&mut rng); + random_squaring_tests::(&mut rng); + random_inversion_tests::(&mut rng); + random_expansion_tests::(&mut rng); + + assert!(F::zero().is_zero()); + { + let mut z = F::zero(); + z.negate(); + assert!(z.is_zero()); + } + + assert!(F::zero().inverse().is_none()); + + // Multiplication by zero + { + let mut a = F::rand(&mut rng); + a.mul_assign(&F::zero()); + assert!(a.is_zero()); + } + + // Addition by zero + { + let mut a = F::rand(&mut rng); + let copy = a; + a.add_assign(&F::zero()); + assert_eq!(a, copy); + } +} + +pub fn from_str_tests() { + { + let a = "84395729384759238745923745892374598234705297301958723458712394587103249587213984572934750213947582345792304758273458972349582734958273495872304598234"; + let b = "38495729084572938457298347502349857029384609283450692834058293405982304598230458230495820394850293845098234059823049582309485203948502938452093482039"; + let c = "3248875134290623212325429203829831876024364170316860259933542844758450336418538569901990710701240661702808867062612075657861768196242274635305077449545396068598317421057721935408562373834079015873933065667961469731886739181625866970316226171512545167081793907058686908697431878454091011239990119126"; + + let mut a = F::from_str(a).unwrap(); + let b = F::from_str(b).unwrap(); + let c = F::from_str(c).unwrap(); + + a.mul_assign(&b); + + assert_eq!(a, c); + } + + { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let n: u64 = rng.gen(); + + let a = F::from_str(&format!("{}", n)).unwrap(); + let b = F::from_repr(n.into()).unwrap(); + + assert_eq!(a, b); + } + } + + assert!(F::from_str("").is_none()); + assert!(F::from_str("0").unwrap().is_zero()); + assert!(F::from_str("00").is_none()); + assert!(F::from_str("00000000000").is_none()); +} + +fn random_multiplication_tests(rng: &mut R) { + for _ in 0..10000 { + let a = F::rand(rng); + let b = F::rand(rng); + let c = F::rand(rng); + + let mut t0 = a; // (a * b) * c + t0.mul_assign(&b); + t0.mul_assign(&c); + + let mut t1 = a; // (a * c) * b + t1.mul_assign(&c); + t1.mul_assign(&b); + + let mut t2 = b; // (b * c) * a + t2.mul_assign(&c); + t2.mul_assign(&a); + + assert_eq!(t0, t1); + assert_eq!(t1, t2); + } +} + +fn random_addition_tests(rng: &mut R) { + for _ in 0..10000 { + let a = F::rand(rng); + let b = F::rand(rng); + let c = F::rand(rng); + + let mut t0 = a; // (a + b) + c + t0.add_assign(&b); + t0.add_assign(&c); + + let mut t1 = a; // (a + c) + b + t1.add_assign(&c); + t1.add_assign(&b); + + let mut t2 = b; // (b + c) + a + t2.add_assign(&c); + t2.add_assign(&a); + + assert_eq!(t0, t1); + assert_eq!(t1, t2); + } +} + +fn random_subtraction_tests(rng: &mut R) { + for _ in 0..10000 { + let a = F::rand(rng); + let b = F::rand(rng); + + let mut t0 = a; // (a - b) + t0.sub_assign(&b); + + let mut t1 = b; // (b - a) + t1.sub_assign(&a); + + let mut t2 = t0; // (a - b) + (b - a) = 0 + t2.add_assign(&t1); + + assert!(t2.is_zero()); + } +} + +fn random_negation_tests(rng: &mut R) { + for _ in 0..10000 { + let a = F::rand(rng); + let mut b = a; + b.negate(); + b.add_assign(&a); + + assert!(b.is_zero()); + } +} + +fn random_doubling_tests(rng: &mut R) { + for _ in 0..10000 { + let mut a = F::rand(rng); + let mut b = a; + a.add_assign(&b); + b.double(); + + assert_eq!(a, b); + } +} + +fn random_squaring_tests(rng: &mut R) { + for _ in 0..10000 { + let mut a = F::rand(rng); + let mut b = a; + a.mul_assign(&b); + b.square(); + + assert_eq!(a, b); + } +} + +fn random_inversion_tests(rng: &mut R) { + assert!(F::zero().inverse().is_none()); + + for _ in 0..10000 { + let mut a = F::rand(rng); + let b = a.inverse().unwrap(); // probablistically nonzero + a.mul_assign(&b); + + assert_eq!(a, F::one()); + } +} + +fn random_expansion_tests(rng: &mut R) { + for _ in 0..10000 { + // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) + + let a = F::rand(rng); + let b = F::rand(rng); + let c = F::rand(rng); + let d = F::rand(rng); + + let mut t0 = a; + t0.add_assign(&b); + let mut t1 = c; + t1.add_assign(&d); + t0.mul_assign(&t1); + + let mut t2 = a; + t2.mul_assign(&c); + let mut t3 = b; + t3.mul_assign(&c); + let mut t4 = a; + t4.mul_assign(&d); + let mut t5 = b; + t5.mul_assign(&d); + + t2.add_assign(&t3); + t2.add_assign(&t4); + t2.add_assign(&t5); + + assert_eq!(t0, t2); + } +} diff --git a/src/rust/pairing/src/tests/mod.rs b/src/rust/pairing/src/tests/mod.rs new file mode 100644 index 000000000..bc83958ca --- /dev/null +++ b/src/rust/pairing/src/tests/mod.rs @@ -0,0 +1,4 @@ +pub mod curve; +pub mod engine; +pub mod field; +pub mod repr; diff --git a/src/rust/pairing/src/tests/repr.rs b/src/rust/pairing/src/tests/repr.rs new file mode 100644 index 000000000..681a4767b --- /dev/null +++ b/src/rust/pairing/src/tests/repr.rs @@ -0,0 +1,98 @@ +use rand::{SeedableRng, XorShiftRng}; +use PrimeFieldRepr; + +pub fn random_repr_tests() { + random_encoding_tests::(); + random_shl_tests::(); + random_shr_tests::(); +} + +fn random_encoding_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let r = R::rand(&mut rng); + + // Big endian + { + let mut rdecoded = R::default(); + + let mut v: Vec = vec![]; + r.write_be(&mut v).unwrap(); + rdecoded.read_be(&v[0..]).unwrap(); + + assert_eq!(r, rdecoded); + } + + // Little endian + { + let mut rdecoded = R::default(); + + let mut v: Vec = vec![]; + r.write_le(&mut v).unwrap(); + rdecoded.read_le(&v[0..]).unwrap(); + + assert_eq!(r, rdecoded); + } + + { + let mut rdecoded_le = R::default(); + let mut rdecoded_be_flip = R::default(); + + let mut v: Vec = vec![]; + r.write_le(&mut v).unwrap(); + + // This reads in little-endian, so we are done. + rdecoded_le.read_le(&v[..]).unwrap(); + + // This reads in big-endian, so we perform a swap of the + // bytes beforehand. + let v: Vec = v.into_iter().rev().collect(); + rdecoded_be_flip.read_be(&v[..]).unwrap(); + + assert_eq!(rdecoded_le, rdecoded_be_flip); + } + } +} + +fn random_shl_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let r = R::rand(&mut rng); + + for shift in 0..(r.num_bits() + 1) { + let mut r1 = r; + let mut r2 = r; + + for _ in 0..shift { + r1.mul2(); + } + + r2.shl(shift); + + assert_eq!(r1, r2); + } + } +} + +fn random_shr_tests() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let r = R::rand(&mut rng); + + for shift in 0..(r.num_bits() + 1) { + let mut r1 = r; + let mut r2 = r; + + for _ in 0..shift { + r1.div2(); + } + + r2.shr(shift); + + assert_eq!(r1, r2); + } + } +} diff --git a/src/rust/pairing/src/wnaf.rs b/src/rust/pairing/src/wnaf.rs new file mode 100644 index 000000000..69c6fd91d --- /dev/null +++ b/src/rust/pairing/src/wnaf.rs @@ -0,0 +1,179 @@ +use super::{CurveProjective, PrimeField, PrimeFieldRepr}; + +/// Replaces the contents of `table` with a w-NAF window table for the given window size. +pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { + table.truncate(0); + table.reserve(1 << (window - 1)); + + let mut dbl = base; + dbl.double(); + + for _ in 0..(1 << (window - 1)) { + table.push(base); + base.add_assign(&dbl); + } +} + +/// Replaces the contents of `wnaf` with the w-NAF representation of a scalar. +pub(crate) fn wnaf_form(wnaf: &mut Vec, mut c: S, window: usize) { + wnaf.truncate(0); + + while !c.is_zero() { + let mut u; + if c.is_odd() { + u = (c.as_ref()[0] % (1 << (window + 1))) as i64; + + if u > (1 << window) { + u -= 1 << (window + 1); + } + + if u > 0 { + c.sub_noborrow(&S::from(u as u64)); + } else { + c.add_nocarry(&S::from((-u) as u64)); + } + } else { + u = 0; + } + + wnaf.push(u); + + c.div2(); + } +} + +/// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar. +/// +/// This function must be provided a `table` and `wnaf` that were constructed with +/// the same window size; otherwise, it may panic or produce invalid results. +pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { + let mut result = G::zero(); + + let mut found_one = false; + + for n in wnaf.iter().rev() { + if found_one { + result.double(); + } + + if *n != 0 { + found_one = true; + + if *n > 0 { + result.add_assign(&table[(n / 2) as usize]); + } else { + result.sub_assign(&table[((-n) / 2) as usize]); + } + } + } + + result +} + +/// A "w-ary non-adjacent form" exponentiation context. +#[derive(Debug)] +pub struct Wnaf { + base: B, + scalar: S, + window_size: W, +} + +impl Wnaf<(), Vec, Vec> { + /// Construct a new wNAF context without allocating. + pub fn new() -> Self { + Wnaf { + base: vec![], + scalar: vec![], + window_size: (), + } + } + + /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that + /// can perform exponentiations with `.scalar(..)`. + pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf> { + // Compute the appropriate window size based on the number of scalars. + let window_size = G::recommended_wnaf_for_num_scalars(num_scalars); + + // Compute a wNAF table for the provided base and window size. + wnaf_table(&mut self.base, base, window_size); + + // Return a Wnaf object that immutably borrows the computed base storage location, + // but mutably borrows the scalar storage location. + Wnaf { + base: &self.base[..], + scalar: &mut self.scalar, + window_size, + } + } + + /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform + /// exponentiations with `.base(..)`. + pub fn scalar( + &mut self, + scalar: <::Scalar as PrimeField>::Repr, + ) -> Wnaf, &[i64]> { + // Compute the appropriate window size for the scalar. + let window_size = G::recommended_wnaf_for_scalar(scalar); + + // Compute the wNAF form of the scalar. + wnaf_form(&mut self.scalar, scalar, window_size); + + // Return a Wnaf object that mutably borrows the base storage location, but + // immutably borrows the computed wNAF form scalar location. + Wnaf { + base: &mut self.base, + scalar: &self.scalar[..], + window_size, + } + } +} + +impl<'a, G: CurveProjective> Wnaf> { + /// Constructs new space for the scalar representation while borrowing + /// the computed window table, for sending the window table across threads. + pub fn shared(&self) -> Wnaf> { + Wnaf { + base: self.base, + scalar: vec![], + window_size: self.window_size, + } + } +} + +impl<'a, G: CurveProjective> Wnaf, &'a [i64]> { + /// Constructs new space for the window table while borrowing + /// the computed scalar representation, for sending the scalar representation + /// across threads. + pub fn shared(&self) -> Wnaf, &'a [i64]> { + Wnaf { + base: vec![], + scalar: self.scalar, + window_size: self.window_size, + } + } +} + +impl> Wnaf { + /// Performs exponentiation given a base. + pub fn base(&mut self, base: G) -> G + where + B: AsMut>, + { + wnaf_table(self.base.as_mut(), base, self.window_size); + wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) + } +} + +impl>> Wnaf { + /// Performs exponentiation given a scalar. + pub fn scalar( + &mut self, + scalar: <::Scalar as PrimeField>::Repr, + ) -> G + where + B: AsRef<[G]>, + { + wnaf_form(self.scalar.as_mut(), scalar, self.window_size); + wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) + } +} diff --git a/src/rust/sapling-crypto/.gitignore b/src/rust/sapling-crypto/.gitignore new file mode 100644 index 000000000..6aa106405 --- /dev/null +++ b/src/rust/sapling-crypto/.gitignore @@ -0,0 +1,3 @@ +/target/ +**/*.rs.bk +Cargo.lock diff --git a/src/rust/sapling-crypto/COPYRIGHT b/src/rust/sapling-crypto/COPYRIGHT new file mode 100644 index 000000000..f2c6a3b05 --- /dev/null +++ b/src/rust/sapling-crypto/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "sapling-crypto" library are retained by their contributors. No +copyright assignment is required to contribute to the "sapling-crypto" library. + +The "sapling-crypto" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/sapling-crypto/Cargo.toml b/src/rust/sapling-crypto/Cargo.toml new file mode 100644 index 000000000..33e21bf65 --- /dev/null +++ b/src/rust/sapling-crypto/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["Sean Bowe "] +description = "Cryptographic library for Zcash Sapling" +documentation = "https://github.com/zcash-hackworks/sapling" +homepage = "https://github.com/zcash-hackworks/sapling" +license = "MIT/Apache-2.0" +name = "sapling-crypto" +repository = "https://github.com/zcash-hackworks/sapling" +version = "0.0.1" + +[dependencies.pairing] +path = "../pairing" +features = ["expose-arith"] + +[dependencies] +bellman = { path = "../bellman" } +rand = "0.4" +digest = "0.7" +byteorder = "1" + +[dependencies.blake2-rfc] +git = "https://github.com/gtank/blake2-rfc" +rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" + +[features] +default = ["u128-support"] +u128-support = ["pairing/u128-support"] diff --git a/src/rust/sapling-crypto/LICENSE-APACHE b/src/rust/sapling-crypto/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/src/rust/sapling-crypto/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/rust/sapling-crypto/LICENSE-MIT b/src/rust/sapling-crypto/LICENSE-MIT new file mode 100644 index 000000000..31aa79387 --- /dev/null +++ b/src/rust/sapling-crypto/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/src/rust/sapling-crypto/README.md b/src/rust/sapling-crypto/README.md new file mode 100644 index 000000000..f5d3bceaa --- /dev/null +++ b/src/rust/sapling-crypto/README.md @@ -0,0 +1,23 @@ +# sapling-crypto + +This repository contains a (work-in-progress) implementation of Zcash's "Sapling" cryptography. + +## Security Warnings + +This library is currently under development and has not been reviewed. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/sapling-crypto/benches/pedersen_hash.rs b/src/rust/sapling-crypto/benches/pedersen_hash.rs new file mode 100644 index 000000000..c5968dec4 --- /dev/null +++ b/src/rust/sapling-crypto/benches/pedersen_hash.rs @@ -0,0 +1,23 @@ +#![feature(test)] + +extern crate rand; +extern crate test; +extern crate pairing; +extern crate sapling_crypto; + +use rand::{Rand, thread_rng}; +use pairing::bls12_381::Bls12; +use sapling_crypto::jubjub::JubjubBls12; +use sapling_crypto::pedersen_hash::{pedersen_hash, Personalization}; + +#[bench] +fn bench_pedersen_hash(b: &mut test::Bencher) { + let params = JubjubBls12::new(); + let rng = &mut thread_rng(); + let bits = (0..510).map(|_| bool::rand(rng)).collect::>(); + let personalization = Personalization::MerkleTree(31); + + b.iter(|| { + pedersen_hash::(personalization, bits.clone(), ¶ms) + }); +} diff --git a/src/rust/sapling-crypto/examples/bench.rs b/src/rust/sapling-crypto/examples/bench.rs new file mode 100644 index 000000000..4b7a707b4 --- /dev/null +++ b/src/rust/sapling-crypto/examples/bench.rs @@ -0,0 +1,102 @@ +extern crate sapling_crypto; +extern crate bellman; +extern crate rand; +extern crate pairing; + +use std::time::{Duration, Instant}; +use sapling_crypto::jubjub::{ + JubjubBls12, + edwards, + fs, +}; +use sapling_crypto::circuit::sapling::{ + Spend +}; +use sapling_crypto::primitives::{ + Diversifier, + ProofGenerationKey, + ValueCommitment +}; +use bellman::groth16::*; +use rand::{XorShiftRng, SeedableRng, Rng}; +use pairing::bls12_381::{Bls12, Fr}; + +const TREE_DEPTH: usize = 32; + +fn main() { + let jubjub_params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + println!("Creating sample parameters..."); + let groth_params = generate_random_parameters::( + Spend { + params: jubjub_params, + value_commitment: None, + proof_generation_key: None, + payment_address: None, + commitment_randomness: None, + ar: None, + auth_path: vec![None; TREE_DEPTH], + anchor: None + }, + rng + ).unwrap(); + + const SAMPLES: u32 = 50; + + let mut total_time = Duration::new(0, 0); + for _ in 0..SAMPLES { + let value_commitment = ValueCommitment { + value: 1, + randomness: rng.gen() + }; + + let nsk: fs::Fs = rng.gen(); + let ak = edwards::Point::rand(rng, jubjub_params).mul_by_cofactor(jubjub_params); + + let proof_generation_key = ProofGenerationKey { + ak: ak.clone(), + nsk: nsk.clone() + }; + + let viewing_key = proof_generation_key.into_viewing_key(jubjub_params); + + let payment_address; + + loop { + let diversifier = Diversifier(rng.gen()); + + if let Some(p) = viewing_key.into_payment_address( + diversifier, + jubjub_params + ) + { + payment_address = p; + break; + } + } + + let commitment_randomness: fs::Fs = rng.gen(); + let auth_path = vec![Some((rng.gen(), rng.gen())); TREE_DEPTH]; + let ar: fs::Fs = rng.gen(); + let anchor: Fr = rng.gen(); + + let start = Instant::now(); + let _ = create_random_proof(Spend { + params: jubjub_params, + value_commitment: Some(value_commitment), + proof_generation_key: Some(proof_generation_key), + payment_address: Some(payment_address), + commitment_randomness: Some(commitment_randomness), + ar: Some(ar), + auth_path: auth_path, + anchor: Some(anchor) + }, &groth_params, rng).unwrap(); + total_time += start.elapsed(); + } + let avg = total_time / SAMPLES; + let avg = avg.subsec_nanos() as f64 / 1_000_000_000f64 + + (avg.as_secs() as f64); + + println!("Average proving time (in seconds): {}", avg); +} diff --git a/src/rust/sapling-crypto/src/circuit/blake2s.rs b/src/rust/sapling-crypto/src/circuit/blake2s.rs new file mode 100644 index 000000000..93af8069c --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/blake2s.rs @@ -0,0 +1,438 @@ +use pairing::{ + Engine, +}; + +use bellman::{ + SynthesisError, + ConstraintSystem +}; + +use super::boolean::{ + Boolean +}; + +use super::uint32::{ + UInt32 +}; + +use super::multieq::MultiEq; + +/* +2.1. Parameters + The following table summarizes various parameters and their ranges: + | BLAKE2b | BLAKE2s | + --------------+------------------+------------------+ + Bits in word | w = 64 | w = 32 | + Rounds in F | r = 12 | r = 10 | + Block bytes | bb = 128 | bb = 64 | + Hash bytes | 1 <= nn <= 64 | 1 <= nn <= 32 | + Key bytes | 0 <= kk <= 64 | 0 <= kk <= 32 | + Input bytes | 0 <= ll < 2**128 | 0 <= ll < 2**64 | + --------------+------------------+------------------+ + G Rotation | (R1, R2, R3, R4) | (R1, R2, R3, R4) | + constants = | (32, 24, 16, 63) | (16, 12, 8, 7) | + --------------+------------------+------------------+ +*/ + +const R1: usize = 16; +const R2: usize = 12; +const R3: usize = 8; +const R4: usize = 7; + +/* + Round | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | + ----------+-------------------------------------------------+ + SIGMA[0] | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | + SIGMA[1] | 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 | + SIGMA[2] | 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 | + SIGMA[3] | 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 | + SIGMA[4] | 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 | + SIGMA[5] | 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 | + SIGMA[6] | 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 | + SIGMA[7] | 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 | + SIGMA[8] | 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 | + SIGMA[9] | 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 | + ----------+-------------------------------------------------+ +*/ + +const SIGMA: [[usize; 16]; 10] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0] +]; + +/* +3.1. Mixing Function G + The G primitive function mixes two input words, "x" and "y", into + four words indexed by "a", "b", "c", and "d" in the working vector + v[0..15]. The full modified vector is returned. The rotation + constants (R1, R2, R3, R4) are given in Section 2.1. + FUNCTION G( v[0..15], a, b, c, d, x, y ) + | + | v[a] := (v[a] + v[b] + x) mod 2**w + | v[d] := (v[d] ^ v[a]) >>> R1 + | v[c] := (v[c] + v[d]) mod 2**w + | v[b] := (v[b] ^ v[c]) >>> R2 + | v[a] := (v[a] + v[b] + y) mod 2**w + | v[d] := (v[d] ^ v[a]) >>> R3 + | v[c] := (v[c] + v[d]) mod 2**w + | v[b] := (v[b] ^ v[c]) >>> R4 + | + | RETURN v[0..15] + | + END FUNCTION. +*/ + +fn mixing_g, M>( + mut cs: M, + v: &mut [UInt32], + a: usize, + b: usize, + c: usize, + d: usize, + x: &UInt32, + y: &UInt32 +) -> Result<(), SynthesisError> + where M: ConstraintSystem> +{ + v[a] = UInt32::addmany(cs.namespace(|| "mixing step 1"), &[v[a].clone(), v[b].clone(), x.clone()])?; + v[d] = v[d].xor(cs.namespace(|| "mixing step 2"), &v[a])?.rotr(R1); + v[c] = UInt32::addmany(cs.namespace(|| "mixing step 3"), &[v[c].clone(), v[d].clone()])?; + v[b] = v[b].xor(cs.namespace(|| "mixing step 4"), &v[c])?.rotr(R2); + v[a] = UInt32::addmany(cs.namespace(|| "mixing step 5"), &[v[a].clone(), v[b].clone(), y.clone()])?; + v[d] = v[d].xor(cs.namespace(|| "mixing step 6"), &v[a])?.rotr(R3); + v[c] = UInt32::addmany(cs.namespace(|| "mixing step 7"), &[v[c].clone(), v[d].clone()])?; + v[b] = v[b].xor(cs.namespace(|| "mixing step 8"), &v[c])?.rotr(R4); + + Ok(()) +} + +/* +3.2. Compression Function F + Compression function F takes as an argument the state vector "h", + message block vector "m" (last block is padded with zeros to full + block size, if required), 2w-bit offset counter "t", and final block + indicator flag "f". Local vector v[0..15] is used in processing. F + returns a new state vector. The number of rounds, "r", is 12 for + BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1. + FUNCTION F( h[0..7], m[0..15], t, f ) + | + | // Initialize local work vector v[0..15] + | v[0..7] := h[0..7] // First half from state. + | v[8..15] := IV[0..7] // Second half from IV. + | + | v[12] := v[12] ^ (t mod 2**w) // Low word of the offset. + | v[13] := v[13] ^ (t >> w) // High word. + | + | IF f = TRUE THEN // last block flag? + | | v[14] := v[14] ^ 0xFF..FF // Invert all bits. + | END IF. + | + | // Cryptographic mixing + | FOR i = 0 TO r - 1 DO // Ten or twelve rounds. + | | + | | // Message word selection permutation for this round. + | | s[0..15] := SIGMA[i mod 10][0..15] + | | + | | v := G( v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]] ) + | | v := G( v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]] ) + | | v := G( v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]] ) + | | v := G( v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]] ) + | | + | | v := G( v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]] ) + | | v := G( v, 1, 6, 11, 12, m[s[10]], m[s[11]] ) + | | v := G( v, 2, 7, 8, 13, m[s[12]], m[s[13]] ) + | | v := G( v, 3, 4, 9, 14, m[s[14]], m[s[15]] ) + | | + | END FOR + | + | FOR i = 0 TO 7 DO // XOR the two halves. + | | h[i] := h[i] ^ v[i] ^ v[i + 8] + | END FOR. + | + | RETURN h[0..7] // New state. + | + END FUNCTION. +*/ + + +fn blake2s_compression>( + mut cs: CS, + h: &mut [UInt32], + m: &[UInt32], + t: u64, + f: bool +) -> Result<(), SynthesisError> +{ + assert_eq!(h.len(), 8); + assert_eq!(m.len(), 16); + + /* + static const uint32_t blake2s_iv[8] = + { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 + }; + */ + + let mut v = Vec::with_capacity(16); + v.extend_from_slice(h); + v.push(UInt32::constant(0x6A09E667)); + v.push(UInt32::constant(0xBB67AE85)); + v.push(UInt32::constant(0x3C6EF372)); + v.push(UInt32::constant(0xA54FF53A)); + v.push(UInt32::constant(0x510E527F)); + v.push(UInt32::constant(0x9B05688C)); + v.push(UInt32::constant(0x1F83D9AB)); + v.push(UInt32::constant(0x5BE0CD19)); + + assert_eq!(v.len(), 16); + + v[12] = v[12].xor(cs.namespace(|| "first xor"), &UInt32::constant(t as u32))?; + v[13] = v[13].xor(cs.namespace(|| "second xor"), &UInt32::constant((t >> 32) as u32))?; + + if f { + v[14] = v[14].xor(cs.namespace(|| "third xor"), &UInt32::constant(u32::max_value()))?; + } + + { + let mut cs = MultiEq::new(&mut cs); + + for i in 0..10 { + let mut cs = cs.namespace(|| format!("round {}", i)); + + let s = SIGMA[i % 10]; + + mixing_g(cs.namespace(|| "mixing invocation 1"), &mut v, 0, 4, 8, 12, &m[s[ 0]], &m[s[ 1]])?; + mixing_g(cs.namespace(|| "mixing invocation 2"), &mut v, 1, 5, 9, 13, &m[s[ 2]], &m[s[ 3]])?; + mixing_g(cs.namespace(|| "mixing invocation 3"), &mut v, 2, 6, 10, 14, &m[s[ 4]], &m[s[ 5]])?; + mixing_g(cs.namespace(|| "mixing invocation 4"), &mut v, 3, 7, 11, 15, &m[s[ 6]], &m[s[ 7]])?; + + mixing_g(cs.namespace(|| "mixing invocation 5"), &mut v, 0, 5, 10, 15, &m[s[ 8]], &m[s[ 9]])?; + mixing_g(cs.namespace(|| "mixing invocation 6"), &mut v, 1, 6, 11, 12, &m[s[10]], &m[s[11]])?; + mixing_g(cs.namespace(|| "mixing invocation 7"), &mut v, 2, 7, 8, 13, &m[s[12]], &m[s[13]])?; + mixing_g(cs.namespace(|| "mixing invocation 8"), &mut v, 3, 4, 9, 14, &m[s[14]], &m[s[15]])?; + } + } + + for i in 0..8 { + let mut cs = cs.namespace(|| format!("h[{i}] ^ v[{i}] ^ v[{i} + 8]", i=i)); + + h[i] = h[i].xor(cs.namespace(|| "first xor"), &v[i])?; + h[i] = h[i].xor(cs.namespace(|| "second xor"), &v[i + 8])?; + } + + Ok(()) +} + +/* + FUNCTION BLAKE2( d[0..dd-1], ll, kk, nn ) + | + | h[0..7] := IV[0..7] // Initialization Vector. + | + | // Parameter block p[0] + | h[0] := h[0] ^ 0x01010000 ^ (kk << 8) ^ nn + | + | // Process padded key and data blocks + | IF dd > 1 THEN + | | FOR i = 0 TO dd - 2 DO + | | | h := F( h, d[i], (i + 1) * bb, FALSE ) + | | END FOR. + | END IF. + | + | // Final block. + | IF kk = 0 THEN + | | h := F( h, d[dd - 1], ll, TRUE ) + | ELSE + | | h := F( h, d[dd - 1], ll + bb, TRUE ) + | END IF. + | + | RETURN first "nn" bytes from little-endian word array h[]. + | + END FUNCTION. +*/ + +pub fn blake2s>( + mut cs: CS, + input: &[Boolean], + personalization: &[u8] +) -> Result, SynthesisError> +{ + use byteorder::{ByteOrder, LittleEndian}; + + assert_eq!(personalization.len(), 8); + assert!(input.len() % 8 == 0); + + let mut h = Vec::with_capacity(8); + h.push(UInt32::constant(0x6A09E667 ^ 0x01010000 ^ 32)); + h.push(UInt32::constant(0xBB67AE85)); + h.push(UInt32::constant(0x3C6EF372)); + h.push(UInt32::constant(0xA54FF53A)); + h.push(UInt32::constant(0x510E527F)); + h.push(UInt32::constant(0x9B05688C)); + + // Personalization is stored here + h.push(UInt32::constant(0x1F83D9AB ^ LittleEndian::read_u32(&personalization[0..4]))); + h.push(UInt32::constant(0x5BE0CD19 ^ LittleEndian::read_u32(&personalization[4..8]))); + + let mut blocks: Vec> = vec![]; + + for block in input.chunks(512) { + let mut this_block = Vec::with_capacity(16); + for word in block.chunks(32) { + let mut tmp = word.to_vec(); + while tmp.len() < 32 { + tmp.push(Boolean::constant(false)); + } + this_block.push(UInt32::from_bits(&tmp)); + } + while this_block.len() < 16 { + this_block.push(UInt32::constant(0)); + } + blocks.push(this_block); + } + + if blocks.len() == 0 { + blocks.push((0..16).map(|_| UInt32::constant(0)).collect()); + } + + for (i, block) in blocks[0..blocks.len() - 1].iter().enumerate() { + let cs = cs.namespace(|| format!("block {}", i)); + + blake2s_compression(cs, &mut h, block, ((i as u64) + 1) * 64, false)?; + } + + { + let cs = cs.namespace(|| "final block"); + + blake2s_compression(cs, &mut h, &blocks[blocks.len() - 1], (input.len() / 8) as u64, true)?; + } + + Ok(h.iter().flat_map(|b| b.into_bits()).collect()) +} + +#[cfg(test)] +mod test { + use rand::{XorShiftRng, SeedableRng, Rng}; + use pairing::bls12_381::{Bls12}; + use ::circuit::boolean::{Boolean, AllocatedBit}; + use ::circuit::test::TestConstraintSystem; + use super::blake2s; + use bellman::{ConstraintSystem}; + use blake2_rfc::blake2s::Blake2s; + + #[test] + fn test_blank_hash() { + let mut cs = TestConstraintSystem::::new(); + let input_bits = vec![]; + let out = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + + // >>> import blake2s from hashlib + // >>> h = blake2s(digest_size=32, person=b'12345678') + // >>> h.hexdigest() + let expected = hex!("c59f682376d137f3f255e671e207d1f2374ebe504e9314208a52d9f88d69e8c8"); + + let mut out = out.into_iter(); + for b in expected.into_iter() { + for i in 0..8 { + let c = out.next().unwrap().get_value().unwrap(); + + assert_eq!(c, (b >> i) & 1u8 == 1u8); + } + } + } + + #[test] + fn test_blake2s_constraints() { + let mut cs = TestConstraintSystem::::new(); + let input_bits: Vec<_> = (0..512).map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into()).collect(); + blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 21518); + } + + #[test] + fn test_blake2s_precomp_constraints() { + // Test that 512 fixed leading bits (constants) + // doesn't result in more constraints. + + let mut cs = TestConstraintSystem::::new(); + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let input_bits: Vec<_> = (0..512) + .map(|_| Boolean::constant(rng.gen())) + .chain((0..512) + .map(|i| AllocatedBit::alloc(cs.namespace(|| format!("input bit {}", i)), Some(true)).unwrap().into())) + .collect(); + blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 21518); + } + + #[test] + fn test_blake2s_constant_constraints() { + let mut cs = TestConstraintSystem::::new(); + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let input_bits: Vec<_> = (0..512).map(|_| Boolean::constant(rng.gen())).collect(); + blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + assert_eq!(cs.num_constraints(), 0); + } + + #[test] + fn test_blake2s() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) + { + let mut h = Blake2s::with_params(32, &[], &[], b"12345678"); + + let data: Vec = (0..input_len).map(|_| rng.gen()).collect(); + + h.update(&data); + + let hash_result = h.finalize(); + + let mut cs = TestConstraintSystem::::new(); + + let mut input_bits = vec![]; + + for (byte_i, input_byte) in data.into_iter().enumerate() { + for bit_i in 0..8 { + let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); + + input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into()); + } + } + + let r = blake2s(&mut cs, &input_bits, b"12345678").unwrap(); + + assert!(cs.is_satisfied()); + + let mut s = hash_result.as_ref().iter() + .flat_map(|&byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8)); + + for b in r { + match b { + Boolean::Is(b) => { + assert!(s.next().unwrap() == b.get_value().unwrap()); + }, + Boolean::Not(b) => { + assert!(s.next().unwrap() != b.get_value().unwrap()); + }, + Boolean::Constant(b) => { + assert!(input_len == 0); + assert!(s.next().unwrap() == b); + } + } + } + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/boolean.rs b/src/rust/sapling-crypto/src/circuit/boolean.rs new file mode 100644 index 000000000..08f407edf --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/boolean.rs @@ -0,0 +1,1582 @@ +use pairing::{ + Engine, + Field, + PrimeField, + BitIterator +}; + +use bellman::{ + ConstraintSystem, + SynthesisError, + LinearCombination, + Variable +}; + +use super::{ + Assignment +}; + +/// Represents a variable in the constraint system which is guaranteed +/// to be either zero or one. +#[derive(Clone)] +pub struct AllocatedBit { + variable: Variable, + value: Option +} + +impl AllocatedBit { + pub fn get_value(&self) -> Option { + self.value + } + + pub fn get_variable(&self) -> Variable { + self.variable + } + + /// Allocate a variable in the constraint system which can only be a + /// boolean value. Further, constrain that the boolean is false + /// unless the condition is false. + pub fn alloc_conditionally( + mut cs: CS, + value: Option, + must_be_false: &AllocatedBit + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let var = cs.alloc(|| "boolean", || { + if *value.get()? { + Ok(E::Fr::one()) + } else { + Ok(E::Fr::zero()) + } + })?; + + // Constrain: (1 - must_be_false - a) * a = 0 + // if must_be_false is true, the equation + // reduces to -a * a = 0, which implies a = 0. + // if must_be_false is false, the equation + // reduces to (1 - a) * a = 0, which is a + // traditional boolean constraint. + cs.enforce( + || "boolean constraint", + |lc| lc + CS::one() - must_be_false.variable - var, + |lc| lc + var, + |lc| lc + ); + + Ok(AllocatedBit { + variable: var, + value: value + }) + } + + /// Allocate a variable in the constraint system which can only be a + /// boolean value. + pub fn alloc( + mut cs: CS, + value: Option, + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let var = cs.alloc(|| "boolean", || { + if *value.get()? { + Ok(E::Fr::one()) + } else { + Ok(E::Fr::zero()) + } + })?; + + // Constrain: (1 - a) * a = 0 + // This constrains a to be either 0 or 1. + cs.enforce( + || "boolean constraint", + |lc| lc + CS::one() - var, + |lc| lc + var, + |lc| lc + ); + + Ok(AllocatedBit { + variable: var, + value: value + }) + } + + /// Performs an XOR operation over the two operands, returning + /// an `AllocatedBit`. + pub fn xor( + mut cs: CS, + a: &Self, + b: &Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let mut result_value = None; + + let result_var = cs.alloc(|| "xor result", || { + if *a.value.get()? ^ *b.value.get()? { + result_value = Some(true); + + Ok(E::Fr::one()) + } else { + result_value = Some(false); + + Ok(E::Fr::zero()) + } + })?; + + // Constrain (a + a) * (b) = (a + b - c) + // Given that a and b are boolean constrained, if they + // are equal, the only solution for c is 0, and if they + // are different, the only solution for c is 1. + // + // ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c + // (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c + // (1 - ab) * (1 - (1 - a - b + ab)) = c + // (1 - ab) * (a + b - ab) = c + // a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c + // a + b - ab - ab - ab + ab = c + // a + b - 2ab = c + // -2a * b = c - a - b + // 2a * b = a + b - c + // (a + a) * b = a + b - c + cs.enforce( + || "xor constraint", + |lc| lc + a.variable + a.variable, + |lc| lc + b.variable, + |lc| lc + a.variable + b.variable - result_var + ); + + Ok(AllocatedBit { + variable: result_var, + value: result_value + }) + } + + /// Performs an AND operation over the two operands, returning + /// an `AllocatedBit`. + pub fn and( + mut cs: CS, + a: &Self, + b: &Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let mut result_value = None; + + let result_var = cs.alloc(|| "and result", || { + if *a.value.get()? & *b.value.get()? { + result_value = Some(true); + + Ok(E::Fr::one()) + } else { + result_value = Some(false); + + Ok(E::Fr::zero()) + } + })?; + + // Constrain (a) * (b) = (c), ensuring c is 1 iff + // a AND b are both 1. + cs.enforce( + || "and constraint", + |lc| lc + a.variable, + |lc| lc + b.variable, + |lc| lc + result_var + ); + + Ok(AllocatedBit { + variable: result_var, + value: result_value + }) + } + + /// Calculates `a AND (NOT b)`. + pub fn and_not( + mut cs: CS, + a: &Self, + b: &Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let mut result_value = None; + + let result_var = cs.alloc(|| "and not result", || { + if *a.value.get()? & !*b.value.get()? { + result_value = Some(true); + + Ok(E::Fr::one()) + } else { + result_value = Some(false); + + Ok(E::Fr::zero()) + } + })?; + + // Constrain (a) * (1 - b) = (c), ensuring c is 1 iff + // a is true and b is false, and otherwise c is 0. + cs.enforce( + || "and not constraint", + |lc| lc + a.variable, + |lc| lc + CS::one() - b.variable, + |lc| lc + result_var + ); + + Ok(AllocatedBit { + variable: result_var, + value: result_value + }) + } + + /// Calculates `(NOT a) AND (NOT b)`. + pub fn nor( + mut cs: CS, + a: &Self, + b: &Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let mut result_value = None; + + let result_var = cs.alloc(|| "nor result", || { + if !*a.value.get()? & !*b.value.get()? { + result_value = Some(true); + + Ok(E::Fr::one()) + } else { + result_value = Some(false); + + Ok(E::Fr::zero()) + } + })?; + + // Constrain (1 - a) * (1 - b) = (c), ensuring c is 1 iff + // a and b are both false, and otherwise c is 0. + cs.enforce( + || "nor constraint", + |lc| lc + CS::one() - a.variable, + |lc| lc + CS::one() - b.variable, + |lc| lc + result_var + ); + + Ok(AllocatedBit { + variable: result_var, + value: result_value + }) + } +} + +pub fn u64_into_boolean_vec_le>( + mut cs: CS, + value: Option +) -> Result, SynthesisError> +{ + let values = match value { + Some(ref value) => { + let mut tmp = Vec::with_capacity(64); + + for i in 0..64 { + tmp.push(Some(*value >> i & 1 == 1)); + } + + tmp + }, + None => { + vec![None; 64] + } + }; + + let bits = values.into_iter().enumerate().map(|(i, b)| { + Ok(Boolean::from(AllocatedBit::alloc( + cs.namespace(|| format!("bit {}", i)), + b + )?)) + }).collect::, SynthesisError>>()?; + + Ok(bits) +} + +pub fn field_into_boolean_vec_le, F: PrimeField>( + cs: CS, + value: Option +) -> Result, SynthesisError> +{ + let v = field_into_allocated_bits_le::(cs, value)?; + + Ok(v.into_iter().map(|e| Boolean::from(e)).collect()) +} + +pub fn field_into_allocated_bits_le, F: PrimeField>( + mut cs: CS, + value: Option +) -> Result, SynthesisError> +{ + // Deconstruct in big-endian bit order + let values = match value { + Some(ref value) => { + let mut field_char = BitIterator::new(F::char()); + + let mut tmp = Vec::with_capacity(F::NUM_BITS as usize); + + let mut found_one = false; + for b in BitIterator::new(value.into_repr()) { + // Skip leading bits + found_one |= field_char.next().unwrap(); + if !found_one { + continue; + } + + tmp.push(Some(b)); + } + + assert_eq!(tmp.len(), F::NUM_BITS as usize); + + tmp + }, + None => { + vec![None; F::NUM_BITS as usize] + } + }; + + // Allocate in little-endian order + let bits = values.into_iter().rev().enumerate().map(|(i, b)| { + AllocatedBit::alloc( + cs.namespace(|| format!("bit {}", i)), + b + ) + }).collect::, SynthesisError>>()?; + + Ok(bits) +} + +/// This is a boolean value which may be either a constant or +/// an interpretation of an `AllocatedBit`. +#[derive(Clone)] +pub enum Boolean { + /// Existential view of the boolean variable + Is(AllocatedBit), + /// Negated view of the boolean variable + Not(AllocatedBit), + /// Constant (not an allocated variable) + Constant(bool) +} + +impl Boolean { + pub fn is_constant(&self) -> bool { + match *self { + Boolean::Constant(_) => true, + _ => false + } + } + + pub fn enforce_equal( + mut cs: CS, + a: &Self, + b: &Self + ) -> Result<(), SynthesisError> + where E: Engine, + CS: ConstraintSystem + { + match (a, b) { + (&Boolean::Constant(a), &Boolean::Constant(b)) => { + if a == b { + Ok(()) + } else { + Err(SynthesisError::Unsatisfiable) + } + }, + (&Boolean::Constant(true), a) | (a, &Boolean::Constant(true)) => { + cs.enforce( + || "enforce equal to one", + |lc| lc, + |lc| lc, + |lc| lc + CS::one() - &a.lc(CS::one(), E::Fr::one()) + ); + + Ok(()) + }, + (&Boolean::Constant(false), a) | (a, &Boolean::Constant(false)) => { + cs.enforce( + || "enforce equal to zero", + |lc| lc, + |lc| lc, + |_| a.lc(CS::one(), E::Fr::one()) + ); + + Ok(()) + }, + (a, b) => { + cs.enforce( + || "enforce equal", + |lc| lc, + |lc| lc, + |_| a.lc(CS::one(), E::Fr::one()) - &b.lc(CS::one(), E::Fr::one()) + ); + + Ok(()) + } + } + } + + pub fn get_value(&self) -> Option { + match self { + &Boolean::Constant(c) => Some(c), + &Boolean::Is(ref v) => v.get_value(), + &Boolean::Not(ref v) => v.get_value().map(|b| !b) + } + } + + pub fn lc( + &self, + one: Variable, + coeff: E::Fr + ) -> LinearCombination + { + match self { + &Boolean::Constant(c) => { + if c { + LinearCombination::::zero() + (coeff, one) + } else { + LinearCombination::::zero() + } + }, + &Boolean::Is(ref v) => { + LinearCombination::::zero() + (coeff, v.get_variable()) + }, + &Boolean::Not(ref v) => { + LinearCombination::::zero() + (coeff, one) - (coeff, v.get_variable()) + } + } + } + + /// Construct a boolean from a known constant + pub fn constant(b: bool) -> Self { + Boolean::Constant(b) + } + + /// Return a negated interpretation of this boolean. + pub fn not(&self) -> Self { + match self { + &Boolean::Constant(c) => Boolean::Constant(!c), + &Boolean::Is(ref v) => Boolean::Not(v.clone()), + &Boolean::Not(ref v) => Boolean::Is(v.clone()) + } + } + + /// Perform XOR over two boolean operands + pub fn xor<'a, E, CS>( + cs: CS, + a: &'a Self, + b: &'a Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + match (a, b) { + (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(x.clone()), + (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.not()), + // a XOR (NOT b) = NOT(a XOR b) + (is @ &Boolean::Is(_), not @ &Boolean::Not(_)) | (not @ &Boolean::Not(_), is @ &Boolean::Is(_)) => { + Ok(Boolean::xor( + cs, + is, + ¬.not() + )?.not()) + }, + // a XOR b = (NOT a) XOR (NOT b) + (&Boolean::Is(ref a), &Boolean::Is(ref b)) | (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { + Ok(Boolean::Is(AllocatedBit::xor(cs, a, b)?)) + } + } + } + + /// Perform AND over two boolean operands + pub fn and<'a, E, CS>( + cs: CS, + a: &'a Self, + b: &'a Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + match (a, b) { + // false AND x is always false + (&Boolean::Constant(false), _) | (_, &Boolean::Constant(false)) => Ok(Boolean::Constant(false)), + // true AND x is always x + (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.clone()), + // a AND (NOT b) + (&Boolean::Is(ref is), &Boolean::Not(ref not)) | (&Boolean::Not(ref not), &Boolean::Is(ref is)) => { + Ok(Boolean::Is(AllocatedBit::and_not(cs, is, not)?)) + }, + // (NOT a) AND (NOT b) = a NOR b + (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { + Ok(Boolean::Is(AllocatedBit::nor(cs, a, b)?)) + }, + // a AND b + (&Boolean::Is(ref a), &Boolean::Is(ref b)) => { + Ok(Boolean::Is(AllocatedBit::and(cs, a, b)?)) + } + } + } + + /// Computes (a and b) xor ((not a) and c) + pub fn sha256_ch<'a, E, CS>( + mut cs: CS, + a: &'a Self, + b: &'a Self, + c: &'a Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let ch_value = match (a.get_value(), b.get_value(), c.get_value()) { + (Some(a), Some(b), Some(c)) => { + // (a and b) xor ((not a) and c) + Some((a & b) ^ ((!a) & c)) + }, + _ => None + }; + + match (a, b, c) { + (&Boolean::Constant(_), + &Boolean::Constant(_), + &Boolean::Constant(_)) => { + // They're all constants, so we can just compute the value. + + return Ok(Boolean::Constant(ch_value.expect("they're all constants"))); + }, + (&Boolean::Constant(false), _, c) => { + // If a is false + // (a and b) xor ((not a) and c) + // equals + // (false) xor (c) + // equals + // c + return Ok(c.clone()); + }, + (a, &Boolean::Constant(false), c) => { + // If b is false + // (a and b) xor ((not a) and c) + // equals + // ((not a) and c) + return Boolean::and( + cs, + &a.not(), + &c + ); + }, + (a, b, &Boolean::Constant(false)) => { + // If c is false + // (a and b) xor ((not a) and c) + // equals + // (a and b) + return Boolean::and( + cs, + &a, + &b + ); + }, + (a, b, &Boolean::Constant(true)) => { + // If c is true + // (a and b) xor ((not a) and c) + // equals + // (a and b) xor (not a) + // equals + // not (a and (not b)) + return Ok(Boolean::and( + cs, + &a, + &b.not() + )?.not()); + }, + (a, &Boolean::Constant(true), c) => { + // If b is true + // (a and b) xor ((not a) and c) + // equals + // a xor ((not a) and c) + // equals + // not ((not a) and (not c)) + return Ok(Boolean::and( + cs, + &a.not(), + &c.not() + )?.not()); + }, + (&Boolean::Constant(true), _, _) => { + // If a is true + // (a and b) xor ((not a) and c) + // equals + // b xor ((not a) and c) + // So we just continue! + }, + (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) | + (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) | + (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) | + (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) | + (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) | + (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) | + (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) | + (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) + => {} + } + + let ch = cs.alloc(|| "ch", || { + ch_value.get().map(|v| { + if *v { + E::Fr::one() + } else { + E::Fr::zero() + } + }) + })?; + + // a(b - c) = ch - c + cs.enforce( + || "ch computation", + |_| b.lc(CS::one(), E::Fr::one()) + - &c.lc(CS::one(), E::Fr::one()), + |_| a.lc(CS::one(), E::Fr::one()), + |lc| lc + ch - &c.lc(CS::one(), E::Fr::one()) + ); + + Ok(AllocatedBit { + value: ch_value, + variable: ch + }.into()) + } + + /// Computes (a and b) xor (a and c) xor (b and c) + pub fn sha256_maj<'a, E, CS>( + mut cs: CS, + a: &'a Self, + b: &'a Self, + c: &'a Self, + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let maj_value = match (a.get_value(), b.get_value(), c.get_value()) { + (Some(a), Some(b), Some(c)) => { + // (a and b) xor (a and c) xor (b and c) + Some((a & b) ^ (a & c) ^ (b & c)) + }, + _ => None + }; + + match (a, b, c) { + (&Boolean::Constant(_), + &Boolean::Constant(_), + &Boolean::Constant(_)) => { + // They're all constants, so we can just compute the value. + + return Ok(Boolean::Constant(maj_value.expect("they're all constants"))); + }, + (&Boolean::Constant(false), b, c) => { + // If a is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (b and c) + return Boolean::and( + cs, + b, + c + ); + }, + (a, &Boolean::Constant(false), c) => { + // If b is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and c) + return Boolean::and( + cs, + a, + c + ); + }, + (a, b, &Boolean::Constant(false)) => { + // If c is false, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and b) + return Boolean::and( + cs, + a, + b + ); + }, + (a, b, &Boolean::Constant(true)) => { + // If c is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a and b) xor (a) xor (b) + // equals + // not ((not a) and (not b)) + return Ok(Boolean::and( + cs, + &a.not(), + &b.not() + )?.not()); + }, + (a, &Boolean::Constant(true), c) => { + // If b is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (a) xor (a and c) xor (c) + return Ok(Boolean::and( + cs, + &a.not(), + &c.not() + )?.not()); + }, + (&Boolean::Constant(true), b, c) => { + // If a is true, + // (a and b) xor (a and c) xor (b and c) + // equals + // (b) xor (c) xor (b and c) + return Ok(Boolean::and( + cs, + &b.not(), + &c.not() + )?.not()); + }, + (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) | + (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) | + (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) | + (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) | + (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) | + (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) | + (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) | + (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) + => {} + } + + let maj = cs.alloc(|| "maj", || { + maj_value.get().map(|v| { + if *v { + E::Fr::one() + } else { + E::Fr::zero() + } + }) + })?; + + // ¬(¬a ∧ ¬b) ∧ ¬(¬a ∧ ¬c) ∧ ¬(¬b ∧ ¬c) + // (1 - ((1 - a) * (1 - b))) * (1 - ((1 - a) * (1 - c))) * (1 - ((1 - b) * (1 - c))) + // (a + b - ab) * (a + c - ac) * (b + c - bc) + // -2abc + ab + ac + bc + // a (-2bc + b + c) + bc + // + // (b) * (c) = (bc) + // (2bc - b - c) * (a) = bc - maj + + let bc = Self::and( + cs.namespace(|| "b and c"), + b, + c + )?; + + cs.enforce( + || "maj computation", + |_| bc.lc(CS::one(), E::Fr::one()) + + &bc.lc(CS::one(), E::Fr::one()) + - &b.lc(CS::one(), E::Fr::one()) + - &c.lc(CS::one(), E::Fr::one()), + |_| a.lc(CS::one(), E::Fr::one()), + |_| bc.lc(CS::one(), E::Fr::one()) - maj + ); + + Ok(AllocatedBit { + value: maj_value, + variable: maj + }.into()) + } +} + +impl From for Boolean { + fn from(b: AllocatedBit) -> Boolean { + Boolean::Is(b) + } +} + +#[cfg(test)] +mod test { + use bellman::{ConstraintSystem}; + use pairing::bls12_381::{Bls12, Fr}; + use pairing::{Field, PrimeField}; + use ::circuit::test::*; + use super::{ + AllocatedBit, + Boolean, + field_into_allocated_bits_le, + u64_into_boolean_vec_le + }; + + #[test] + fn test_allocated_bit() { + let mut cs = TestConstraintSystem::::new(); + + AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); + assert!(cs.get("boolean") == Fr::one()); + assert!(cs.is_satisfied()); + cs.set("boolean", Fr::zero()); + assert!(cs.is_satisfied()); + cs.set("boolean", Fr::from_str("2").unwrap()); + assert!(!cs.is_satisfied()); + assert!(cs.which_is_unsatisfied() == Some("boolean constraint")); + } + + #[test] + fn test_xor() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); + let c = AllocatedBit::xor(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), *a_val ^ *b_val); + + assert!(cs.is_satisfied()); + assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); + assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); + assert!(cs.get("xor result") == if *a_val ^ *b_val { Field::one() } else { Field::zero() }); + + // Invert the result and check if the constraint system is still satisfied + cs.set("xor result", if *a_val ^ *b_val { Field::zero() } else { Field::one() }); + assert!(!cs.is_satisfied()); + } + } + } + + #[test] + fn test_and() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); + let c = AllocatedBit::and(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), *a_val & *b_val); + + assert!(cs.is_satisfied()); + assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); + assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); + assert!(cs.get("and result") == if *a_val & *b_val { Field::one() } else { Field::zero() }); + + // Invert the result and check if the constraint system is still satisfied + cs.set("and result", if *a_val & *b_val { Field::zero() } else { Field::one() }); + assert!(!cs.is_satisfied()); + } + } + } + + #[test] + fn test_and_not() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); + let c = AllocatedBit::and_not(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), *a_val & !*b_val); + + assert!(cs.is_satisfied()); + assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); + assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); + assert!(cs.get("and not result") == if *a_val & !*b_val { Field::one() } else { Field::zero() }); + + // Invert the result and check if the constraint system is still satisfied + cs.set("and not result", if *a_val & !*b_val { Field::zero() } else { Field::one() }); + assert!(!cs.is_satisfied()); + } + } + } + + #[test] + fn test_nor() { + for a_val in [false, true].iter() { + for b_val in [false, true].iter() { + let mut cs = TestConstraintSystem::::new(); + let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); + let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); + let c = AllocatedBit::nor(&mut cs, &a, &b).unwrap(); + assert_eq!(c.value.unwrap(), !*a_val & !*b_val); + + assert!(cs.is_satisfied()); + assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); + assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); + assert!(cs.get("nor result") == if !*a_val & !*b_val { Field::one() } else { Field::zero() }); + + // Invert the result and check if the constraint system is still satisfied + cs.set("nor result", if !*a_val & !*b_val { Field::zero() } else { Field::one() }); + assert!(!cs.is_satisfied()); + } + } + } + + #[test] + fn test_enforce_equal() { + for a_bool in [false, true].iter().cloned() { + for b_bool in [false, true].iter().cloned() { + for a_neg in [false, true].iter().cloned() { + for b_neg in [false, true].iter().cloned() { + { + let mut cs = TestConstraintSystem::::new(); + + let mut a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap()); + let mut b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_bool)).unwrap()); + + if a_neg { + a = a.not(); + } + if b_neg { + b = b.not(); + } + + Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); + + assert_eq!( + cs.is_satisfied(), + (a_bool ^ a_neg) == (b_bool ^ b_neg) + ); + } + { + let mut cs = TestConstraintSystem::::new(); + + let mut a = Boolean::Constant(a_bool); + let mut b = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_bool)).unwrap()); + + if a_neg { + a = a.not(); + } + if b_neg { + b = b.not(); + } + + Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); + + assert_eq!( + cs.is_satisfied(), + (a_bool ^ a_neg) == (b_bool ^ b_neg) + ); + } + { + let mut cs = TestConstraintSystem::::new(); + + let mut a = Boolean::from(AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap()); + let mut b = Boolean::Constant(b_bool); + + if a_neg { + a = a.not(); + } + if b_neg { + b = b.not(); + } + + Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); + + assert_eq!( + cs.is_satisfied(), + (a_bool ^ a_neg) == (b_bool ^ b_neg) + ); + } + { + let mut cs = TestConstraintSystem::::new(); + + let mut a = Boolean::Constant(a_bool); + let mut b = Boolean::Constant(b_bool); + + if a_neg { + a = a.not(); + } + if b_neg { + b = b.not(); + } + + let result = Boolean::enforce_equal(&mut cs, &a, &b); + + if (a_bool ^ a_neg) == (b_bool ^ b_neg) { + assert!(result.is_ok()); + assert!(cs.is_satisfied()); + } else { + assert!(result.is_err()); + } + } + } + } + } + } + } + + #[test] + fn test_boolean_negation() { + let mut cs = TestConstraintSystem::::new(); + + let mut b = Boolean::from(AllocatedBit::alloc(&mut cs, Some(true)).unwrap()); + + match b { + Boolean::Is(_) => {}, + _ => panic!("unexpected value") + } + + b = b.not(); + + match b { + Boolean::Not(_) => {}, + _ => panic!("unexpected value") + } + + b = b.not(); + + match b { + Boolean::Is(_) => {}, + _ => panic!("unexpected value") + } + + b = Boolean::constant(true); + + match b { + Boolean::Constant(true) => {}, + _ => panic!("unexpected value") + } + + b = b.not(); + + match b { + Boolean::Constant(false) => {}, + _ => panic!("unexpected value") + } + + b = b.not(); + + match b { + Boolean::Constant(true) => {}, + _ => panic!("unexpected value") + } + } + + #[derive(Copy, Clone, Debug)] + enum OperandType { + True, + False, + AllocatedTrue, + AllocatedFalse, + NegatedAllocatedTrue, + NegatedAllocatedFalse + } + + impl OperandType { + fn is_constant(&self) -> bool { + match *self { + OperandType::True => true, + OperandType::False => true, + OperandType::AllocatedTrue => false, + OperandType::AllocatedFalse => false, + OperandType::NegatedAllocatedTrue => false, + OperandType::NegatedAllocatedFalse => false + } + } + + fn val(&self) -> bool { + match *self { + OperandType::True => true, + OperandType::False => false, + OperandType::AllocatedTrue => true, + OperandType::AllocatedFalse => false, + OperandType::NegatedAllocatedTrue => false, + OperandType::NegatedAllocatedFalse => true + } + } + } + + + #[test] + fn test_boolean_xor() { + let variants = [ + OperandType::True, + OperandType::False, + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse + ]; + + for first_operand in variants.iter().cloned() { + for second_operand in variants.iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + let a; + let b; + + { + let mut dyn_construct = |operand, name| { + let cs = cs.namespace(|| name); + + match operand { + OperandType::True => Boolean::constant(true), + OperandType::False => Boolean::constant(false), + OperandType::AllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()), + OperandType::AllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()), + OperandType::NegatedAllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not(), + OperandType::NegatedAllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not(), + } + }; + + a = dyn_construct(first_operand, "a"); + b = dyn_construct(second_operand, "b"); + } + + let c = Boolean::xor(&mut cs, &a, &b).unwrap(); + + assert!(cs.is_satisfied()); + + match (first_operand, second_operand, c) { + (OperandType::True, OperandType::True, Boolean::Constant(false)) => {}, + (OperandType::True, OperandType::False, Boolean::Constant(true)) => {}, + (OperandType::True, OperandType::AllocatedTrue, Boolean::Not(_)) => {}, + (OperandType::True, OperandType::AllocatedFalse, Boolean::Not(_)) => {}, + (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Is(_)) => {}, + (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Is(_)) => {}, + + (OperandType::False, OperandType::True, Boolean::Constant(true)) => {}, + (OperandType::False, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::AllocatedTrue, Boolean::Is(_)) => {}, + (OperandType::False, OperandType::AllocatedFalse, Boolean::Is(_)) => {}, + (OperandType::False, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {}, + (OperandType::False, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {}, + + (OperandType::AllocatedTrue, OperandType::True, Boolean::Not(_)) => {}, + (OperandType::AllocatedTrue, OperandType::False, Boolean::Is(_)) => {}, + (OperandType::AllocatedTrue, OperandType::AllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::AllocatedTrue, OperandType::AllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + (OperandType::AllocatedTrue, OperandType::NegatedAllocatedTrue, Boolean::Not(ref v)) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::AllocatedTrue, OperandType::NegatedAllocatedFalse, Boolean::Not(ref v)) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + + (OperandType::AllocatedFalse, OperandType::True, Boolean::Not(_)) => {}, + (OperandType::AllocatedFalse, OperandType::False, Boolean::Is(_)) => {}, + (OperandType::AllocatedFalse, OperandType::AllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + (OperandType::AllocatedFalse, OperandType::AllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::AllocatedFalse, OperandType::NegatedAllocatedTrue, Boolean::Not(ref v)) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + (OperandType::AllocatedFalse, OperandType::NegatedAllocatedFalse, Boolean::Not(ref v)) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + + (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Is(_)) => {}, + (OperandType::NegatedAllocatedTrue, OperandType::False, Boolean::Not(_)) => {}, + (OperandType::NegatedAllocatedTrue, OperandType::AllocatedTrue, Boolean::Not(ref v)) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::NegatedAllocatedTrue, OperandType::AllocatedFalse, Boolean::Not(ref v)) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + (OperandType::NegatedAllocatedTrue, OperandType::NegatedAllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::NegatedAllocatedTrue, OperandType::NegatedAllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + + (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Is(_)) => {}, + (OperandType::NegatedAllocatedFalse, OperandType::False, Boolean::Not(_)) => {}, + (OperandType::NegatedAllocatedFalse, OperandType::AllocatedTrue, Boolean::Not(ref v)) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + (OperandType::NegatedAllocatedFalse, OperandType::AllocatedFalse, Boolean::Not(ref v)) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::NegatedAllocatedFalse, OperandType::NegatedAllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("xor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + (OperandType::NegatedAllocatedFalse, OperandType::NegatedAllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("xor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + + _ => panic!("this should never be encountered") + } + } + } + } + + #[test] + fn test_boolean_and() { + let variants = [ + OperandType::True, + OperandType::False, + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse + ]; + + for first_operand in variants.iter().cloned() { + for second_operand in variants.iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + let a; + let b; + + { + let mut dyn_construct = |operand, name| { + let cs = cs.namespace(|| name); + + match operand { + OperandType::True => Boolean::constant(true), + OperandType::False => Boolean::constant(false), + OperandType::AllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()), + OperandType::AllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()), + OperandType::NegatedAllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not(), + OperandType::NegatedAllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not(), + } + }; + + a = dyn_construct(first_operand, "a"); + b = dyn_construct(second_operand, "b"); + } + + let c = Boolean::and(&mut cs, &a, &b).unwrap(); + + assert!(cs.is_satisfied()); + + match (first_operand, second_operand, c) { + (OperandType::True, OperandType::True, Boolean::Constant(true)) => {}, + (OperandType::True, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::True, OperandType::AllocatedTrue, Boolean::Is(_)) => {}, + (OperandType::True, OperandType::AllocatedFalse, Boolean::Is(_)) => {}, + (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {}, + (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {}, + + (OperandType::False, OperandType::True, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::AllocatedTrue, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::AllocatedFalse, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::NegatedAllocatedTrue, Boolean::Constant(false)) => {}, + (OperandType::False, OperandType::NegatedAllocatedFalse, Boolean::Constant(false)) => {}, + + (OperandType::AllocatedTrue, OperandType::True, Boolean::Is(_)) => {}, + (OperandType::AllocatedTrue, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::AllocatedTrue, OperandType::AllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("and result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + (OperandType::AllocatedTrue, OperandType::AllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("and result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::AllocatedTrue, OperandType::NegatedAllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::AllocatedTrue, OperandType::NegatedAllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("and not result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + + (OperandType::AllocatedFalse, OperandType::True, Boolean::Is(_)) => {}, + (OperandType::AllocatedFalse, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::AllocatedFalse, OperandType::AllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("and result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::AllocatedFalse, OperandType::AllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("and result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::AllocatedFalse, OperandType::NegatedAllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::AllocatedFalse, OperandType::NegatedAllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + + (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Not(_)) => {}, + (OperandType::NegatedAllocatedTrue, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::NegatedAllocatedTrue, OperandType::AllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::NegatedAllocatedTrue, OperandType::AllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::NegatedAllocatedTrue, OperandType::NegatedAllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("nor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::NegatedAllocatedTrue, OperandType::NegatedAllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("nor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + + (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Not(_)) => {}, + (OperandType::NegatedAllocatedFalse, OperandType::False, Boolean::Constant(false)) => {}, + (OperandType::NegatedAllocatedFalse, OperandType::AllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("and not result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + (OperandType::NegatedAllocatedFalse, OperandType::AllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("and not result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::NegatedAllocatedFalse, OperandType::NegatedAllocatedTrue, Boolean::Is(ref v)) => { + assert!(cs.get("nor result") == Field::zero()); + assert_eq!(v.value, Some(false)); + }, + (OperandType::NegatedAllocatedFalse, OperandType::NegatedAllocatedFalse, Boolean::Is(ref v)) => { + assert!(cs.get("nor result") == Field::one()); + assert_eq!(v.value, Some(true)); + }, + + _ => { + panic!("unexpected behavior at {:?} AND {:?}", first_operand, second_operand); + } + } + } + } + } + + #[test] + fn test_u64_into_boolean_vec_le() { + let mut cs = TestConstraintSystem::::new(); + + let bits = u64_into_boolean_vec_le(&mut cs, Some(17234652694787248421)).unwrap(); + + assert!(cs.is_satisfied()); + + assert_eq!(bits.len(), 64); + + assert_eq!(bits[63 - 0].get_value().unwrap(), true); + assert_eq!(bits[63 - 1].get_value().unwrap(), true); + assert_eq!(bits[63 - 2].get_value().unwrap(), true); + assert_eq!(bits[63 - 3].get_value().unwrap(), false); + assert_eq!(bits[63 - 4].get_value().unwrap(), true); + assert_eq!(bits[63 - 5].get_value().unwrap(), true); + assert_eq!(bits[63 - 20].get_value().unwrap(), true); + assert_eq!(bits[63 - 21].get_value().unwrap(), false); + assert_eq!(bits[63 - 22].get_value().unwrap(), false); + } + + #[test] + fn test_field_into_allocated_bits_le() { + let mut cs = TestConstraintSystem::::new(); + + let r = Fr::from_str("9147677615426976802526883532204139322118074541891858454835346926874644257775").unwrap(); + + let bits = field_into_allocated_bits_le(&mut cs, Some(r)).unwrap(); + + assert!(cs.is_satisfied()); + + assert_eq!(bits.len(), 255); + + assert_eq!(bits[254 - 0].value.unwrap(), false); + assert_eq!(bits[254 - 1].value.unwrap(), false); + assert_eq!(bits[254 - 2].value.unwrap(), true); + assert_eq!(bits[254 - 3].value.unwrap(), false); + assert_eq!(bits[254 - 4].value.unwrap(), true); + assert_eq!(bits[254 - 5].value.unwrap(), false); + assert_eq!(bits[254 - 20].value.unwrap(), true); + assert_eq!(bits[254 - 23].value.unwrap(), true); + } + + #[test] + fn test_boolean_sha256_ch() { + let variants = [ + OperandType::True, + OperandType::False, + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse + ]; + + for first_operand in variants.iter().cloned() { + for second_operand in variants.iter().cloned() { + for third_operand in variants.iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + let a; + let b; + let c; + + // ch = (a and b) xor ((not a) and c) + let expected = (first_operand.val() & second_operand.val()) ^ + ((!first_operand.val()) & third_operand.val()); + + { + let mut dyn_construct = |operand, name| { + let cs = cs.namespace(|| name); + + match operand { + OperandType::True => Boolean::constant(true), + OperandType::False => Boolean::constant(false), + OperandType::AllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()), + OperandType::AllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()), + OperandType::NegatedAllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not(), + OperandType::NegatedAllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not(), + } + }; + + a = dyn_construct(first_operand, "a"); + b = dyn_construct(second_operand, "b"); + c = dyn_construct(third_operand, "c"); + } + + let maj = Boolean::sha256_ch(&mut cs, &a, &b, &c).unwrap(); + + assert!(cs.is_satisfied()); + + assert_eq!(maj.get_value().unwrap(), expected); + + if first_operand.is_constant() || + second_operand.is_constant() || + third_operand.is_constant() + { + if first_operand.is_constant() && + second_operand.is_constant() && + third_operand.is_constant() + { + assert_eq!(cs.num_constraints(), 0); + } + } + else + { + assert_eq!(cs.get("ch"), { + if expected { + Fr::one() + } else { + Fr::zero() + } + }); + cs.set("ch", { + if expected { + Fr::zero() + } else { + Fr::one() + } + }); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "ch computation"); + } + } + } + } + } + + #[test] + fn test_boolean_sha256_maj() { + let variants = [ + OperandType::True, + OperandType::False, + OperandType::AllocatedTrue, + OperandType::AllocatedFalse, + OperandType::NegatedAllocatedTrue, + OperandType::NegatedAllocatedFalse + ]; + + for first_operand in variants.iter().cloned() { + for second_operand in variants.iter().cloned() { + for third_operand in variants.iter().cloned() { + let mut cs = TestConstraintSystem::::new(); + + let a; + let b; + let c; + + // maj = (a and b) xor (a and c) xor (b and c) + let expected = (first_operand.val() & second_operand.val()) ^ + (first_operand.val() & third_operand.val()) ^ + (second_operand.val() & third_operand.val()); + + { + let mut dyn_construct = |operand, name| { + let cs = cs.namespace(|| name); + + match operand { + OperandType::True => Boolean::constant(true), + OperandType::False => Boolean::constant(false), + OperandType::AllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()), + OperandType::AllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()), + OperandType::NegatedAllocatedTrue => Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not(), + OperandType::NegatedAllocatedFalse => Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not(), + } + }; + + a = dyn_construct(first_operand, "a"); + b = dyn_construct(second_operand, "b"); + c = dyn_construct(third_operand, "c"); + } + + let maj = Boolean::sha256_maj(&mut cs, &a, &b, &c).unwrap(); + + assert!(cs.is_satisfied()); + + assert_eq!(maj.get_value().unwrap(), expected); + + if first_operand.is_constant() || + second_operand.is_constant() || + third_operand.is_constant() + { + if first_operand.is_constant() && + second_operand.is_constant() && + third_operand.is_constant() + { + assert_eq!(cs.num_constraints(), 0); + } + } + else + { + assert_eq!(cs.get("maj"), { + if expected { + Fr::one() + } else { + Fr::zero() + } + }); + cs.set("maj", { + if expected { + Fr::zero() + } else { + Fr::one() + } + }); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "maj computation"); + } + } + } + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/ecc.rs b/src/rust/sapling-crypto/src/circuit/ecc.rs new file mode 100644 index 000000000..71f1caa7a --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/ecc.rs @@ -0,0 +1,1213 @@ +use pairing::{ + Engine, + Field +}; + +use bellman::{ + SynthesisError, + ConstraintSystem +}; + +use super::{ + Assignment +}; + +use super::num::{ + AllocatedNum, + Num +}; + +use ::jubjub::{ + edwards, + JubjubEngine, + JubjubParams, + FixedGenerators +}; + +use super::lookup::{ + lookup3_xy +}; + +use super::boolean::Boolean; + +#[derive(Clone)] +pub struct EdwardsPoint { + x: AllocatedNum, + y: AllocatedNum +} + +/// Perform a fixed-base scalar multiplication with +/// `by` being in little-endian bit order. +pub fn fixed_base_multiplication( + mut cs: CS, + base: FixedGenerators, + by: &[Boolean], + params: &E::Params +) -> Result, SynthesisError> + where CS: ConstraintSystem, + E: JubjubEngine +{ + // Represents the result of the multiplication + let mut result = None; + + for (i, (chunk, window)) in by.chunks(3) + .zip(params.circuit_generators(base).iter()) + .enumerate() + { + let chunk_a = chunk.get(0).map(|e| e.clone()).unwrap_or(Boolean::constant(false)); + let chunk_b = chunk.get(1).map(|e| e.clone()).unwrap_or(Boolean::constant(false)); + let chunk_c = chunk.get(2).map(|e| e.clone()).unwrap_or(Boolean::constant(false)); + + let (x, y) = lookup3_xy( + cs.namespace(|| format!("window table lookup {}", i)), + &[chunk_a, chunk_b, chunk_c], + window + )?; + + let p = EdwardsPoint { + x: x, + y: y + }; + + if result.is_none() { + result = Some(p); + } else { + result = Some(result.unwrap().add( + cs.namespace(|| format!("addition {}", i)), + &p, + params + )?); + } + } + + Ok(result.get()?.clone()) +} + +impl EdwardsPoint { + pub fn get_x(&self) -> &AllocatedNum { + &self.x + } + + pub fn get_y(&self) -> &AllocatedNum { + &self.y + } + + pub fn assert_not_small_order( + &self, + mut cs: CS, + params: &E::Params + ) -> Result<(), SynthesisError> + where CS: ConstraintSystem + { + let tmp = self.double( + cs.namespace(|| "first doubling"), + params + )?; + let tmp = tmp.double( + cs.namespace(|| "second doubling"), + params + )?; + let tmp = tmp.double( + cs.namespace(|| "third doubling"), + params + )?; + + // (0, -1) is a small order point, but won't ever appear here + // because cofactor is 2^3, and we performed three doublings. + // (0, 1) is the neutral element, so checking if x is nonzero + // is sufficient to prevent small order points here. + tmp.x.assert_nonzero(cs.namespace(|| "check x != 0"))?; + + Ok(()) + } + + pub fn inputize( + &self, + mut cs: CS + ) -> Result<(), SynthesisError> + where CS: ConstraintSystem + { + self.x.inputize(cs.namespace(|| "x"))?; + self.y.inputize(cs.namespace(|| "y"))?; + + Ok(()) + } + + /// This converts the point into a representation. + pub fn repr( + &self, + mut cs: CS + ) -> Result, SynthesisError> + where CS: ConstraintSystem + { + let mut tmp = vec![]; + + let x = self.x.into_bits_le_strict( + cs.namespace(|| "unpack x") + )?; + + let y = self.y.into_bits_le_strict( + cs.namespace(|| "unpack y") + )?; + + tmp.extend(y); + tmp.push(x[0].clone()); + + Ok(tmp) + } + + /// This 'witnesses' a point inside the constraint system. + /// It guarantees the point is on the curve. + pub fn witness( + mut cs: CS, + p: Option>, + params: &E::Params + ) -> Result + where CS: ConstraintSystem + { + let p = p.map(|p| p.into_xy()); + + // Allocate x + let x = AllocatedNum::alloc( + cs.namespace(|| "x"), + || { + Ok(p.get()?.0) + } + )?; + + // Allocate y + let y = AllocatedNum::alloc( + cs.namespace(|| "y"), + || { + Ok(p.get()?.1) + } + )?; + + Self::interpret( + cs.namespace(|| "point interpretation"), + &x, + &y, + params + ) + } + + /// Returns `self` if condition is true, and the neutral + /// element (0, 1) otherwise. + pub fn conditionally_select( + &self, + mut cs: CS, + condition: &Boolean + ) -> Result + where CS: ConstraintSystem + { + // Compute x' = self.x if condition, and 0 otherwise + let x_prime = AllocatedNum::alloc(cs.namespace(|| "x'"), || { + if *condition.get_value().get()? { + Ok(*self.x.get_value().get()?) + } else { + Ok(E::Fr::zero()) + } + })?; + + // condition * x = x' + // if condition is 0, x' must be 0 + // if condition is 1, x' must be x + let one = CS::one(); + cs.enforce( + || "x' computation", + |lc| lc + self.x.get_variable(), + |_| condition.lc(one, E::Fr::one()), + |lc| lc + x_prime.get_variable() + ); + + // Compute y' = self.y if condition, and 1 otherwise + let y_prime = AllocatedNum::alloc(cs.namespace(|| "y'"), || { + if *condition.get_value().get()? { + Ok(*self.y.get_value().get()?) + } else { + Ok(E::Fr::one()) + } + })?; + + // condition * y = y' - (1 - condition) + // if condition is 0, y' must be 1 + // if condition is 1, y' must be y + cs.enforce( + || "y' computation", + |lc| lc + self.y.get_variable(), + |_| condition.lc(one, E::Fr::one()), + |lc| lc + y_prime.get_variable() + - &condition.not().lc(one, E::Fr::one()) + ); + + Ok(EdwardsPoint { + x: x_prime, + y: y_prime + }) + } + + /// Performs a scalar multiplication of this twisted Edwards + /// point by a scalar represented as a sequence of booleans + /// in little-endian bit order. + pub fn mul( + &self, + mut cs: CS, + by: &[Boolean], + params: &E::Params + ) -> Result + where CS: ConstraintSystem + { + // Represents the current "magnitude" of the base + // that we're operating over. Starts at self, + // then 2*self, then 4*self, ... + let mut curbase = None; + + // Represents the result of the multiplication + let mut result = None; + + for (i, bit) in by.iter().enumerate() { + if curbase.is_none() { + curbase = Some(self.clone()); + } else { + // Double the previous value + curbase = Some( + curbase.unwrap() + .double(cs.namespace(|| format!("doubling {}", i)), params)? + ); + } + + // Represents the select base. If the bit for this magnitude + // is true, this will return `curbase`. Otherwise it will + // return the neutral element, which will have no effect on + // the result. + let thisbase = curbase.as_ref() + .unwrap() + .conditionally_select( + cs.namespace(|| format!("selection {}", i)), + bit + )?; + + if result.is_none() { + result = Some(thisbase); + } else { + result = Some(result.unwrap().add( + cs.namespace(|| format!("addition {}", i)), + &thisbase, + params + )?); + } + } + + Ok(result.get()?.clone()) + } + + pub fn interpret( + mut cs: CS, + x: &AllocatedNum, + y: &AllocatedNum, + params: &E::Params + ) -> Result + where CS: ConstraintSystem + { + // -x^2 + y^2 = 1 + dx^2y^2 + + let x2 = x.square(cs.namespace(|| "x^2"))?; + let y2 = y.square(cs.namespace(|| "y^2"))?; + let x2y2 = x2.mul(cs.namespace(|| "x^2 y^2"), &y2)?; + + let one = CS::one(); + cs.enforce( + || "on curve check", + |lc| lc - x2.get_variable() + + y2.get_variable(), + |lc| lc + one, + |lc| lc + one + + (*params.edwards_d(), x2y2.get_variable()) + ); + + Ok(EdwardsPoint { + x: x.clone(), + y: y.clone() + }) + } + + pub fn double( + &self, + mut cs: CS, + params: &E::Params + ) -> Result + where CS: ConstraintSystem + { + // Compute T = (x1 + y1) * (x1 + y1) + let t = AllocatedNum::alloc(cs.namespace(|| "T"), || { + let mut t0 = *self.x.get_value().get()?; + t0.add_assign(self.y.get_value().get()?); + + let mut t1 = *self.x.get_value().get()?; + t1.add_assign(self.y.get_value().get()?); + + t0.mul_assign(&t1); + + Ok(t0) + })?; + + cs.enforce( + || "T computation", + |lc| lc + self.x.get_variable() + + self.y.get_variable(), + |lc| lc + self.x.get_variable() + + self.y.get_variable(), + |lc| lc + t.get_variable() + ); + + // Compute A = x1 * y1 + let a = self.x.mul(cs.namespace(|| "A computation"), &self.y)?; + + // Compute C = d*A*A + let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { + let mut t0 = *a.get_value().get()?; + t0.square(); + t0.mul_assign(params.edwards_d()); + + Ok(t0) + })?; + + cs.enforce( + || "C computation", + |lc| lc + (*params.edwards_d(), a.get_variable()), + |lc| lc + a.get_variable(), + |lc| lc + c.get_variable() + ); + + // Compute x3 = (2.A) / (1 + C) + let x3 = AllocatedNum::alloc(cs.namespace(|| "x3"), || { + let mut t0 = *a.get_value().get()?; + t0.double(); + + let mut t1 = E::Fr::one(); + t1.add_assign(c.get_value().get()?); + + match t1.inverse() { + Some(t1) => { + t0.mul_assign(&t1); + + Ok(t0) + }, + None => { + Err(SynthesisError::DivisionByZero) + } + } + })?; + + let one = CS::one(); + cs.enforce( + || "x3 computation", + |lc| lc + one + c.get_variable(), + |lc| lc + x3.get_variable(), + |lc| lc + a.get_variable() + + a.get_variable() + ); + + // Compute y3 = (U - 2.A) / (1 - C) + let y3 = AllocatedNum::alloc(cs.namespace(|| "y3"), || { + let mut t0 = *a.get_value().get()?; + t0.double(); + t0.negate(); + t0.add_assign(t.get_value().get()?); + + let mut t1 = E::Fr::one(); + t1.sub_assign(c.get_value().get()?); + + match t1.inverse() { + Some(t1) => { + t0.mul_assign(&t1); + + Ok(t0) + }, + None => { + Err(SynthesisError::DivisionByZero) + } + } + })?; + + cs.enforce( + || "y3 computation", + |lc| lc + one - c.get_variable(), + |lc| lc + y3.get_variable(), + |lc| lc + t.get_variable() + - a.get_variable() + - a.get_variable() + ); + + Ok(EdwardsPoint { + x: x3, + y: y3 + }) + } + + /// Perform addition between any two points + pub fn add( + &self, + mut cs: CS, + other: &Self, + params: &E::Params + ) -> Result + where CS: ConstraintSystem + { + // Compute U = (x1 + y1) * (x2 + y2) + let u = AllocatedNum::alloc(cs.namespace(|| "U"), || { + let mut t0 = *self.x.get_value().get()?; + t0.add_assign(self.y.get_value().get()?); + + let mut t1 = *other.x.get_value().get()?; + t1.add_assign(other.y.get_value().get()?); + + t0.mul_assign(&t1); + + Ok(t0) + })?; + + cs.enforce( + || "U computation", + |lc| lc + self.x.get_variable() + + self.y.get_variable(), + |lc| lc + other.x.get_variable() + + other.y.get_variable(), + |lc| lc + u.get_variable() + ); + + // Compute A = y2 * x1 + let a = other.y.mul(cs.namespace(|| "A computation"), &self.x)?; + + // Compute B = x2 * y1 + let b = other.x.mul(cs.namespace(|| "B computation"), &self.y)?; + + // Compute C = d*A*B + let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { + let mut t0 = *a.get_value().get()?; + t0.mul_assign(b.get_value().get()?); + t0.mul_assign(params.edwards_d()); + + Ok(t0) + })?; + + cs.enforce( + || "C computation", + |lc| lc + (*params.edwards_d(), a.get_variable()), + |lc| lc + b.get_variable(), + |lc| lc + c.get_variable() + ); + + // Compute x3 = (A + B) / (1 + C) + let x3 = AllocatedNum::alloc(cs.namespace(|| "x3"), || { + let mut t0 = *a.get_value().get()?; + t0.add_assign(b.get_value().get()?); + + let mut t1 = E::Fr::one(); + t1.add_assign(c.get_value().get()?); + + match t1.inverse() { + Some(t1) => { + t0.mul_assign(&t1); + + Ok(t0) + }, + None => { + Err(SynthesisError::DivisionByZero) + } + } + })?; + + let one = CS::one(); + cs.enforce( + || "x3 computation", + |lc| lc + one + c.get_variable(), + |lc| lc + x3.get_variable(), + |lc| lc + a.get_variable() + + b.get_variable() + ); + + // Compute y3 = (U - A - B) / (1 - C) + let y3 = AllocatedNum::alloc(cs.namespace(|| "y3"), || { + let mut t0 = *u.get_value().get()?; + t0.sub_assign(a.get_value().get()?); + t0.sub_assign(b.get_value().get()?); + + let mut t1 = E::Fr::one(); + t1.sub_assign(c.get_value().get()?); + + match t1.inverse() { + Some(t1) => { + t0.mul_assign(&t1); + + Ok(t0) + }, + None => { + Err(SynthesisError::DivisionByZero) + } + } + })?; + + cs.enforce( + || "y3 computation", + |lc| lc + one - c.get_variable(), + |lc| lc + y3.get_variable(), + |lc| lc + u.get_variable() + - a.get_variable() + - b.get_variable() + ); + + Ok(EdwardsPoint { + x: x3, + y: y3 + }) + } +} + +pub struct MontgomeryPoint { + x: Num, + y: Num +} + +impl MontgomeryPoint { + /// Converts an element in the prime order subgroup into + /// a point in the birationally equivalent twisted + /// Edwards curve. + pub fn into_edwards( + &self, + mut cs: CS, + params: &E::Params + ) -> Result, SynthesisError> + where CS: ConstraintSystem + { + // Compute u = (scale*x) / y + let u = AllocatedNum::alloc(cs.namespace(|| "u"), || { + let mut t0 = *self.x.get_value().get()?; + t0.mul_assign(params.scale()); + + match self.y.get_value().get()?.inverse() { + Some(invy) => { + t0.mul_assign(&invy); + + Ok(t0) + }, + None => { + Err(SynthesisError::DivisionByZero) + } + } + })?; + + cs.enforce( + || "u computation", + |lc| lc + &self.y.lc(E::Fr::one()), + |lc| lc + u.get_variable(), + |lc| lc + &self.x.lc(*params.scale()) + ); + + // Compute v = (x - 1) / (x + 1) + let v = AllocatedNum::alloc(cs.namespace(|| "v"), || { + let mut t0 = *self.x.get_value().get()?; + let mut t1 = t0; + t0.sub_assign(&E::Fr::one()); + t1.add_assign(&E::Fr::one()); + + match t1.inverse() { + Some(t1) => { + t0.mul_assign(&t1); + + Ok(t0) + }, + None => { + Err(SynthesisError::DivisionByZero) + } + } + })?; + + let one = CS::one(); + cs.enforce( + || "v computation", + |lc| lc + &self.x.lc(E::Fr::one()) + + one, + |lc| lc + v.get_variable(), + |lc| lc + &self.x.lc(E::Fr::one()) + - one, + ); + + Ok(EdwardsPoint { + x: u, + y: v + }) + } + + /// Interprets an (x, y) pair as a point + /// in Montgomery, does not check that it's + /// on the curve. Useful for constants and + /// window table lookups. + pub fn interpret_unchecked( + x: Num, + y: Num + ) -> Self + { + MontgomeryPoint { + x: x, + y: y + } + } + + /// Performs an affine point addition, not defined for + /// coincident points. + pub fn add( + &self, + mut cs: CS, + other: &Self, + params: &E::Params + ) -> Result + where CS: ConstraintSystem + { + // Compute lambda = (y' - y) / (x' - x) + let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { + let mut n = *other.y.get_value().get()?; + n.sub_assign(self.y.get_value().get()?); + + let mut d = *other.x.get_value().get()?; + d.sub_assign(self.x.get_value().get()?); + + match d.inverse() { + Some(d) => { + n.mul_assign(&d); + Ok(n) + }, + None => { + Err(SynthesisError::DivisionByZero) + } + } + })?; + + cs.enforce( + || "evaluate lambda", + |lc| lc + &other.x.lc(E::Fr::one()) + - &self.x.lc(E::Fr::one()), + + |lc| lc + lambda.get_variable(), + + |lc| lc + &other.y.lc(E::Fr::one()) + - &self.y.lc(E::Fr::one()) + ); + + // Compute x'' = lambda^2 - A - x - x' + let xprime = AllocatedNum::alloc(cs.namespace(|| "xprime"), || { + let mut t0 = *lambda.get_value().get()?; + t0.square(); + t0.sub_assign(params.montgomery_a()); + t0.sub_assign(self.x.get_value().get()?); + t0.sub_assign(other.x.get_value().get()?); + + Ok(t0) + })?; + + // (lambda) * (lambda) = (A + x + x' + x'') + let one = CS::one(); + cs.enforce( + || "evaluate xprime", + |lc| lc + lambda.get_variable(), + |lc| lc + lambda.get_variable(), + |lc| lc + (*params.montgomery_a(), one) + + &self.x.lc(E::Fr::one()) + + &other.x.lc(E::Fr::one()) + + xprime.get_variable() + ); + + // Compute y' = -(y + lambda(x' - x)) + let yprime = AllocatedNum::alloc(cs.namespace(|| "yprime"), || { + let mut t0 = *xprime.get_value().get()?; + t0.sub_assign(self.x.get_value().get()?); + t0.mul_assign(lambda.get_value().get()?); + t0.add_assign(self.y.get_value().get()?); + t0.negate(); + + Ok(t0) + })?; + + // y' + y = lambda(x - x') + cs.enforce( + || "evaluate yprime", + |lc| lc + &self.x.lc(E::Fr::one()) + - xprime.get_variable(), + + |lc| lc + lambda.get_variable(), + + |lc| lc + yprime.get_variable() + + &self.y.lc(E::Fr::one()) + ); + + Ok(MontgomeryPoint { + x: xprime.into(), + y: yprime.into() + }) + } +} + +#[cfg(test)] +mod test { + use bellman::{ConstraintSystem}; + use rand::{XorShiftRng, SeedableRng, Rand, Rng}; + use pairing::bls12_381::{Bls12, Fr}; + use pairing::{BitIterator, Field, PrimeField}; + use ::circuit::test::*; + use ::jubjub::{ + montgomery, + edwards, + JubjubBls12, + JubjubParams, + FixedGenerators + }; + use ::jubjub::fs::Fs; + use super::{ + MontgomeryPoint, + EdwardsPoint, + AllocatedNum, + fixed_base_multiplication + }; + use super::super::boolean::{ + Boolean, + AllocatedBit + }; + + #[test] + fn test_into_edwards() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let mut cs = TestConstraintSystem::::new(); + + let p = montgomery::Point::::rand(rng, params); + let (u, v) = edwards::Point::from_montgomery(&p, params).into_xy(); + let (x, y) = p.into_xy().unwrap(); + + let numx = AllocatedNum::alloc(cs.namespace(|| "mont x"), || { + Ok(x) + }).unwrap(); + let numy = AllocatedNum::alloc(cs.namespace(|| "mont y"), || { + Ok(y) + }).unwrap(); + + let p = MontgomeryPoint::interpret_unchecked(numx.into(), numy.into()); + + let q = p.into_edwards(&mut cs, params).unwrap(); + + assert!(cs.is_satisfied()); + assert!(q.x.get_value().unwrap() == u); + assert!(q.y.get_value().unwrap() == v); + + cs.set("u/num", rng.gen()); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "u computation"); + cs.set("u/num", u); + assert!(cs.is_satisfied()); + + cs.set("v/num", rng.gen()); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "v computation"); + cs.set("v/num", v); + assert!(cs.is_satisfied()); + } + } + + #[test] + fn test_interpret() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let p = edwards::Point::::rand(rng, ¶ms); + + let mut cs = TestConstraintSystem::::new(); + let q = EdwardsPoint::witness( + &mut cs, + Some(p.clone()), + ¶ms + ).unwrap(); + + let p = p.into_xy(); + + assert!(cs.is_satisfied()); + assert_eq!(q.x.get_value().unwrap(), p.0); + assert_eq!(q.y.get_value().unwrap(), p.1); + } + + for _ in 0..100 { + let p = edwards::Point::::rand(rng, ¶ms); + let (x, y) = p.into_xy(); + + let mut cs = TestConstraintSystem::::new(); + let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || { + Ok(x) + }).unwrap(); + let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || { + Ok(y) + }).unwrap(); + + let p = EdwardsPoint::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(p.x.get_value().unwrap(), x); + assert_eq!(p.y.get_value().unwrap(), y); + } + + // Random (x, y) are unlikely to be on the curve. + for _ in 0..100 { + let x = rng.gen(); + let y = rng.gen(); + + let mut cs = TestConstraintSystem::::new(); + let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || { + Ok(x) + }).unwrap(); + let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || { + Ok(y) + }).unwrap(); + + EdwardsPoint::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + + assert_eq!(cs.which_is_unsatisfied().unwrap(), "on curve check"); + } + } + + #[test] + fn test_edwards_fixed_base_multiplication() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let mut cs = TestConstraintSystem::::new(); + + let p = params.generator(FixedGenerators::NoteCommitmentRandomness); + let s = Fs::rand(rng); + let q = p.mul(s, params); + let (x1, y1) = q.into_xy(); + + let mut s_bits = BitIterator::new(s.into_repr()).collect::>(); + s_bits.reverse(); + s_bits.truncate(Fs::NUM_BITS as usize); + + let s_bits = s_bits.into_iter() + .enumerate() + .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {}", i)), Some(b)).unwrap()) + .map(|v| Boolean::from(v)) + .collect::>(); + + let q = fixed_base_multiplication( + cs.namespace(|| "multiplication"), + FixedGenerators::NoteCommitmentRandomness, + &s_bits, + params + ).unwrap(); + + assert_eq!(q.x.get_value().unwrap(), x1); + assert_eq!(q.y.get_value().unwrap(), y1); + } + } + + #[test] + fn test_edwards_multiplication() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let mut cs = TestConstraintSystem::::new(); + + let p = edwards::Point::::rand(rng, params); + let s = Fs::rand(rng); + let q = p.mul(s, params); + + let (x0, y0) = p.into_xy(); + let (x1, y1) = q.into_xy(); + + let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || { + Ok(x0) + }).unwrap(); + let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || { + Ok(y0) + }).unwrap(); + + let p = EdwardsPoint { + x: num_x0, + y: num_y0 + }; + + let mut s_bits = BitIterator::new(s.into_repr()).collect::>(); + s_bits.reverse(); + s_bits.truncate(Fs::NUM_BITS as usize); + + let s_bits = s_bits.into_iter() + .enumerate() + .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("scalar bit {}", i)), Some(b)).unwrap()) + .map(|v| Boolean::from(v)) + .collect::>(); + + let q = p.mul( + cs.namespace(|| "scalar mul"), + &s_bits, + params + ).unwrap(); + + assert!(cs.is_satisfied()); + + assert_eq!( + q.x.get_value().unwrap(), + x1 + ); + + assert_eq!( + q.y.get_value().unwrap(), + y1 + ); + } + } + + #[test] + fn test_conditionally_select() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let p = edwards::Point::::rand(rng, params); + + let (x0, y0) = p.into_xy(); + + let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || { + Ok(x0) + }).unwrap(); + let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || { + Ok(y0) + }).unwrap(); + + let p = EdwardsPoint { + x: num_x0, + y: num_y0 + }; + + let mut should_we_select = rng.gen(); + + // Conditionally allocate + let mut b = if rng.gen() { + Boolean::from(AllocatedBit::alloc( + cs.namespace(|| "condition"), + Some(should_we_select) + ).unwrap()) + } else { + Boolean::constant(should_we_select) + }; + + // Conditionally negate + if rng.gen() { + b = b.not(); + should_we_select = !should_we_select; + } + + let q = p.conditionally_select(cs.namespace(|| "select"), &b).unwrap(); + + assert!(cs.is_satisfied()); + + if should_we_select { + assert_eq!(q.x.get_value().unwrap(), x0); + assert_eq!(q.y.get_value().unwrap(), y0); + + cs.set("select/y'/num", Fr::one()); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/y' computation"); + cs.set("select/x'/num", Fr::zero()); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/x' computation"); + } else { + assert_eq!(q.x.get_value().unwrap(), Fr::zero()); + assert_eq!(q.y.get_value().unwrap(), Fr::one()); + + cs.set("select/y'/num", x0); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/y' computation"); + cs.set("select/x'/num", y0); + assert_eq!(cs.which_is_unsatisfied().unwrap(), "select/x' computation"); + } + } + } + + #[test] + fn test_edwards_addition() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let p1 = edwards::Point::::rand(rng, params); + let p2 = edwards::Point::::rand(rng, params); + + let p3 = p1.add(&p2, params); + + let (x0, y0) = p1.into_xy(); + let (x1, y1) = p2.into_xy(); + let (x2, y2) = p3.into_xy(); + + let mut cs = TestConstraintSystem::::new(); + + let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || { + Ok(x0) + }).unwrap(); + let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || { + Ok(y0) + }).unwrap(); + + let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || { + Ok(x1) + }).unwrap(); + let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || { + Ok(y1) + }).unwrap(); + + let p1 = EdwardsPoint { + x: num_x0, + y: num_y0 + }; + + let p2 = EdwardsPoint { + x: num_x1, + y: num_y1 + }; + + let p3 = p1.add(cs.namespace(|| "addition"), &p2, params).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(p3.x.get_value().unwrap() == x2); + assert!(p3.y.get_value().unwrap() == y2); + + let u = cs.get("addition/U/num"); + cs.set("addition/U/num", rng.gen()); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/U computation")); + cs.set("addition/U/num", u); + assert!(cs.is_satisfied()); + + let x3 = cs.get("addition/x3/num"); + cs.set("addition/x3/num", rng.gen()); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/x3 computation")); + cs.set("addition/x3/num", x3); + assert!(cs.is_satisfied()); + + let y3 = cs.get("addition/y3/num"); + cs.set("addition/y3/num", rng.gen()); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/y3 computation")); + cs.set("addition/y3/num", y3); + assert!(cs.is_satisfied()); + } + } + + #[test] + fn test_edwards_doubling() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let p1 = edwards::Point::::rand(rng, params); + let p2 = p1.double(params); + + let (x0, y0) = p1.into_xy(); + let (x1, y1) = p2.into_xy(); + + let mut cs = TestConstraintSystem::::new(); + + let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || { + Ok(x0) + }).unwrap(); + let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || { + Ok(y0) + }).unwrap(); + + let p1 = EdwardsPoint { + x: num_x0, + y: num_y0 + }; + + let p2 = p1.double(cs.namespace(|| "doubling"), params).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(p2.x.get_value().unwrap() == x1); + assert!(p2.y.get_value().unwrap() == y1); + } + } + + #[test] + fn test_montgomery_addition() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let p1 = loop { + let x: Fr = rng.gen(); + let s: bool = rng.gen(); + + if let Some(p) = montgomery::Point::::get_for_x(x, s, params) { + break p; + } + }; + + let p2 = loop { + let x: Fr = rng.gen(); + let s: bool = rng.gen(); + + if let Some(p) = montgomery::Point::::get_for_x(x, s, params) { + break p; + } + }; + + let p3 = p1.add(&p2, params); + + let (x0, y0) = p1.into_xy().unwrap(); + let (x1, y1) = p2.into_xy().unwrap(); + let (x2, y2) = p3.into_xy().unwrap(); + + let mut cs = TestConstraintSystem::::new(); + + let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || { + Ok(x0) + }).unwrap(); + let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || { + Ok(y0) + }).unwrap(); + + let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || { + Ok(x1) + }).unwrap(); + let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || { + Ok(y1) + }).unwrap(); + + let p1 = MontgomeryPoint { + x: num_x0.into(), + y: num_y0.into() + }; + + let p2 = MontgomeryPoint { + x: num_x1.into(), + y: num_y1.into() + }; + + let p3 = p1.add(cs.namespace(|| "addition"), &p2, params).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(p3.x.get_value().unwrap() == x2); + assert!(p3.y.get_value().unwrap() == y2); + + cs.set("addition/yprime/num", rng.gen()); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate yprime")); + cs.set("addition/yprime/num", y2); + assert!(cs.is_satisfied()); + + cs.set("addition/xprime/num", rng.gen()); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate xprime")); + cs.set("addition/xprime/num", x2); + assert!(cs.is_satisfied()); + + cs.set("addition/lambda/num", rng.gen()); + assert_eq!(cs.which_is_unsatisfied(), Some("addition/evaluate lambda")); + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/lookup.rs b/src/rust/sapling-crypto/src/circuit/lookup.rs new file mode 100644 index 000000000..1ffc7f7d9 --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/lookup.rs @@ -0,0 +1,307 @@ +use pairing::{Engine, Field}; +use super::*; +use super::num::{ + AllocatedNum, + Num +}; +use super::boolean::Boolean; +use bellman::{ + ConstraintSystem +}; + +// Synthesize the constants for each base pattern. +fn synth<'a, E: Engine, I>( + window_size: usize, + constants: I, + assignment: &mut [E::Fr] +) + where I: IntoIterator +{ + assert_eq!(assignment.len(), 1 << window_size); + + for (i, constant) in constants.into_iter().enumerate() { + let mut cur = assignment[i]; + cur.negate(); + cur.add_assign(constant); + assignment[i] = cur; + for (j, eval) in assignment.iter_mut().enumerate().skip(i + 1) { + if j & i == i { + eval.add_assign(&cur); + } + } + } +} + +/// Performs a 3-bit window table lookup. `bits` is in +/// little-endian order. +pub fn lookup3_xy( + mut cs: CS, + bits: &[Boolean], + coords: &[(E::Fr, E::Fr)] +) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> + where CS: ConstraintSystem +{ + assert_eq!(bits.len(), 3); + assert_eq!(coords.len(), 8); + + // Calculate the index into `coords` + let i = + match (bits[0].get_value(), bits[1].get_value(), bits[2].get_value()) { + (Some(a_value), Some(b_value), Some(c_value)) => { + let mut tmp = 0; + if a_value { + tmp += 1; + } + if b_value { + tmp += 2; + } + if c_value { + tmp += 4; + } + Some(tmp) + }, + _ => None + }; + + // Allocate the x-coordinate resulting from the lookup + let res_x = AllocatedNum::alloc( + cs.namespace(|| "x"), + || { + Ok(coords[*i.get()?].0) + } + )?; + + // Allocate the y-coordinate resulting from the lookup + let res_y = AllocatedNum::alloc( + cs.namespace(|| "y"), + || { + Ok(coords[*i.get()?].1) + } + )?; + + // Compute the coefficients for the lookup constraints + let mut x_coeffs = [E::Fr::zero(); 8]; + let mut y_coeffs = [E::Fr::zero(); 8]; + synth::(3, coords.iter().map(|c| &c.0), &mut x_coeffs); + synth::(3, coords.iter().map(|c| &c.1), &mut y_coeffs); + + let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[1], &bits[2])?; + + let one = CS::one(); + + cs.enforce( + || "x-coordinate lookup", + |lc| lc + (x_coeffs[0b001], one) + + &bits[1].lc::(one, x_coeffs[0b011]) + + &bits[2].lc::(one, x_coeffs[0b101]) + + &precomp.lc::(one, x_coeffs[0b111]), + |lc| lc + &bits[0].lc::(one, E::Fr::one()), + |lc| lc + res_x.get_variable() + - (x_coeffs[0b000], one) + - &bits[1].lc::(one, x_coeffs[0b010]) + - &bits[2].lc::(one, x_coeffs[0b100]) + - &precomp.lc::(one, x_coeffs[0b110]), + ); + + cs.enforce( + || "y-coordinate lookup", + |lc| lc + (y_coeffs[0b001], one) + + &bits[1].lc::(one, y_coeffs[0b011]) + + &bits[2].lc::(one, y_coeffs[0b101]) + + &precomp.lc::(one, y_coeffs[0b111]), + |lc| lc + &bits[0].lc::(one, E::Fr::one()), + |lc| lc + res_y.get_variable() + - (y_coeffs[0b000], one) + - &bits[1].lc::(one, y_coeffs[0b010]) + - &bits[2].lc::(one, y_coeffs[0b100]) + - &precomp.lc::(one, y_coeffs[0b110]), + ); + + Ok((res_x, res_y)) +} + +/// Performs a 3-bit window table lookup, where +/// one of the bits is a sign bit. +pub fn lookup3_xy_with_conditional_negation( + mut cs: CS, + bits: &[Boolean], + coords: &[(E::Fr, E::Fr)] +) -> Result<(Num, Num), SynthesisError> + where CS: ConstraintSystem +{ + assert_eq!(bits.len(), 3); + assert_eq!(coords.len(), 4); + + // Calculate the index into `coords` + let i = + match (bits[0].get_value(), bits[1].get_value()) { + (Some(a_value), Some(b_value)) => { + let mut tmp = 0; + if a_value { + tmp += 1; + } + if b_value { + tmp += 2; + } + Some(tmp) + }, + _ => None + }; + + // Allocate the y-coordinate resulting from the lookup + // and conditional negation + let y = AllocatedNum::alloc( + cs.namespace(|| "y"), + || { + let mut tmp = coords[*i.get()?].1; + if *bits[2].get_value().get()? { + tmp.negate(); + } + Ok(tmp) + } + )?; + + let one = CS::one(); + + // Compute the coefficients for the lookup constraints + let mut x_coeffs = [E::Fr::zero(); 4]; + let mut y_coeffs = [E::Fr::zero(); 4]; + synth::(2, coords.iter().map(|c| &c.0), &mut x_coeffs); + synth::(2, coords.iter().map(|c| &c.1), &mut y_coeffs); + + let precomp = Boolean::and(cs.namespace(|| "precomp"), &bits[0], &bits[1])?; + + let x = Num::zero() + .add_bool_with_coeff(one, &Boolean::constant(true), x_coeffs[0b00]) + .add_bool_with_coeff(one, &bits[0], x_coeffs[0b01]) + .add_bool_with_coeff(one, &bits[1], x_coeffs[0b10]) + .add_bool_with_coeff(one, &precomp, x_coeffs[0b11]); + + let y_lc = precomp.lc::(one, y_coeffs[0b11]) + + &bits[1].lc::(one, y_coeffs[0b10]) + + &bits[0].lc::(one, y_coeffs[0b01]) + + (y_coeffs[0b00], one); + + cs.enforce( + || "y-coordinate lookup", + |lc| lc + &y_lc + &y_lc, + |lc| lc + &bits[2].lc::(one, E::Fr::one()), + |lc| lc + &y_lc - y.get_variable() + ); + + Ok((x, y.into())) +} + +#[cfg(test)] +mod test { + use rand::{SeedableRng, Rand, Rng, XorShiftRng}; + use super::*; + use ::circuit::test::*; + use ::circuit::boolean::{Boolean, AllocatedBit}; + use pairing::bls12_381::{Bls12, Fr}; + + #[test] + fn test_lookup3_xy() { + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0656]); + + for _ in 0..100 { + let mut cs = TestConstraintSystem::::new(); + + let a_val = rng.gen(); + let a = Boolean::from( + AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap() + ); + + let b_val = rng.gen(); + let b = Boolean::from( + AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap() + ); + + let c_val = rng.gen(); + let c = Boolean::from( + AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap() + ); + + let bits = vec![a, b, c]; + + let points: Vec<(Fr, Fr)> = (0..8).map(|_| (rng.gen(), rng.gen())).collect(); + + let res = lookup3_xy(&mut cs, &bits, &points).unwrap(); + + assert!(cs.is_satisfied()); + + let mut index = 0; + if a_val { index += 1 } + if b_val { index += 2 } + if c_val { index += 4 } + + assert_eq!(res.0.get_value().unwrap(), points[index].0); + assert_eq!(res.1.get_value().unwrap(), points[index].1); + } + } + + #[test] + fn test_lookup3_xy_with_conditional_negation() { + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let mut cs = TestConstraintSystem::::new(); + + let a_val = rng.gen(); + let a = Boolean::from( + AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_val)).unwrap() + ); + + let b_val = rng.gen(); + let b = Boolean::from( + AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_val)).unwrap() + ); + + let c_val = rng.gen(); + let c = Boolean::from( + AllocatedBit::alloc(cs.namespace(|| "c"), Some(c_val)).unwrap() + ); + + let bits = vec![a, b, c]; + + let points: Vec<(Fr, Fr)> = (0..4).map(|_| (rng.gen(), rng.gen())).collect(); + + let res = lookup3_xy_with_conditional_negation(&mut cs, &bits, &points).unwrap(); + + assert!(cs.is_satisfied()); + + let mut index = 0; + if a_val { index += 1 } + if b_val { index += 2 } + + assert_eq!(res.0.get_value().unwrap(), points[index].0); + let mut tmp = points[index].1; + if c_val { tmp.negate() } + assert_eq!(res.1.get_value().unwrap(), tmp); + } + } + + #[test] + fn test_synth() { + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let window_size = 4; + + let mut assignment = vec![Fr::zero(); 1 << window_size]; + let constants: Vec<_> = (0..(1 << window_size)).map(|_| Fr::rand(&mut rng)).collect(); + + synth::(window_size, &constants, &mut assignment); + + for b in 0..(1 << window_size) { + let mut acc = Fr::zero(); + + for j in 0..(1 << window_size) { + if j & b == j { + acc.add_assign(&assignment[j]); + } + } + + assert_eq!(acc, constants[b]); + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/mod.rs b/src/rust/sapling-crypto/src/circuit/mod.rs new file mode 100644 index 000000000..fe0fe50ef --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/mod.rs @@ -0,0 +1,39 @@ +#[cfg(test)] +pub mod test; + +pub mod boolean; +pub mod multieq; +pub mod uint32; +pub mod blake2s; +pub mod num; +pub mod lookup; +pub mod ecc; +pub mod pedersen_hash; +pub mod multipack; +pub mod sha256; + +pub mod sapling; +pub mod sprout; + +use bellman::{ + SynthesisError +}; + +// TODO: This should probably be removed and we +// should use existing helper methods on `Option` +// for mapping with an error. +/// This basically is just an extension to `Option` +/// which allows for a convenient mapping to an +/// error on `None`. +trait Assignment { + fn get(&self) -> Result<&T, SynthesisError>; +} + +impl Assignment for Option { + fn get(&self) -> Result<&T, SynthesisError> { + match *self { + Some(ref v) => Ok(v), + None => Err(SynthesisError::AssignmentMissing) + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/multieq.rs b/src/rust/sapling-crypto/src/circuit/multieq.rs new file mode 100644 index 000000000..0f9c75568 --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/multieq.rs @@ -0,0 +1,137 @@ +use pairing::{ + Engine, + Field, + PrimeField +}; + +use bellman::{ + SynthesisError, + ConstraintSystem, + LinearCombination, + Variable +}; + +pub struct MultiEq>{ + cs: CS, + ops: usize, + bits_used: usize, + lhs: LinearCombination, + rhs: LinearCombination, +} + +impl> MultiEq { + pub fn new(cs: CS) -> Self { + MultiEq { + cs: cs, + ops: 0, + bits_used: 0, + lhs: LinearCombination::zero(), + rhs: LinearCombination::zero() + } + } + + fn accumulate(&mut self) + { + let ops = self.ops; + let lhs = self.lhs.clone(); + let rhs = self.rhs.clone(); + self.cs.enforce( + || format!("multieq {}", ops), + |_| lhs, + |lc| lc + CS::one(), + |_| rhs + ); + self.lhs = LinearCombination::zero(); + self.rhs = LinearCombination::zero(); + self.bits_used = 0; + self.ops += 1; + } + + pub fn enforce_equal( + &mut self, + num_bits: usize, + lhs: &LinearCombination, + rhs: &LinearCombination + ) + { + // Check if we will exceed the capacity + if (E::Fr::CAPACITY as usize) <= (self.bits_used + num_bits) { + self.accumulate(); + } + + assert!((E::Fr::CAPACITY as usize) > (self.bits_used + num_bits)); + + let coeff = E::Fr::from_str("2").unwrap().pow(&[self.bits_used as u64]); + self.lhs = self.lhs.clone() + (coeff, lhs); + self.rhs = self.rhs.clone() + (coeff, rhs); + self.bits_used += num_bits; + } +} + +impl> Drop for MultiEq { + fn drop(&mut self) { + if self.bits_used > 0 { + self.accumulate(); + } + } +} + +impl> ConstraintSystem for MultiEq +{ + type Root = Self; + + fn one() -> Variable { + CS::one() + } + + fn alloc( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.cs.alloc(annotation, f) + } + + fn alloc_input( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.cs.alloc_input(annotation, f) + } + + fn enforce( + &mut self, + annotation: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination + { + self.cs.enforce(annotation, a, b, c) + } + + fn push_namespace(&mut self, name_fn: N) + where NR: Into, N: FnOnce() -> NR + { + self.cs.get_root().push_namespace(name_fn) + } + + fn pop_namespace(&mut self) + { + self.cs.get_root().pop_namespace() + } + + fn get_root(&mut self) -> &mut Self::Root + { + self + } +} diff --git a/src/rust/sapling-crypto/src/circuit/multipack.rs b/src/rust/sapling-crypto/src/circuit/multipack.rs new file mode 100644 index 000000000..54d41385a --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/multipack.rs @@ -0,0 +1,113 @@ +use pairing::{Engine, Field, PrimeField}; +use bellman::{ConstraintSystem, SynthesisError}; +use super::boolean::{Boolean}; +use super::num::Num; +use super::Assignment; + +/// Takes a sequence of booleans and exposes them as compact +/// public inputs +pub fn pack_into_inputs( + mut cs: CS, + bits: &[Boolean] +) -> Result<(), SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + for (i, bits) in bits.chunks(E::Fr::CAPACITY as usize).enumerate() + { + let mut num = Num::::zero(); + let mut coeff = E::Fr::one(); + for bit in bits { + num = num.add_bool_with_coeff(CS::one(), bit, coeff); + + coeff.double(); + } + + let input = cs.alloc_input(|| format!("input {}", i), || { + Ok(*num.get_value().get()?) + })?; + + // num * 1 = input + cs.enforce( + || format!("packing constraint {}", i), + |_| num.lc(E::Fr::one()), + |lc| lc + CS::one(), + |lc| lc + input + ); + } + + Ok(()) +} + +pub fn bytes_to_bits(bytes: &[u8]) -> Vec +{ + bytes.iter() + .flat_map(|&v| (0..8).rev().map(move |i| (v >> i) & 1 == 1)) + .collect() +} + +pub fn bytes_to_bits_le(bytes: &[u8]) -> Vec +{ + bytes.iter() + .flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1)) + .collect() +} + +pub fn compute_multipacking( + bits: &[bool] +) -> Vec +{ + let mut result = vec![]; + + for bits in bits.chunks(E::Fr::CAPACITY as usize) + { + let mut cur = E::Fr::zero(); + let mut coeff = E::Fr::one(); + + for bit in bits { + if *bit { + cur.add_assign(&coeff); + } + + coeff.double(); + } + + result.push(cur); + } + + result +} + +#[test] +fn test_multipacking() { + use rand::{SeedableRng, Rng, XorShiftRng}; + use bellman::{ConstraintSystem}; + use pairing::bls12_381::{Bls12}; + use ::circuit::test::*; + use super::boolean::{AllocatedBit, Boolean}; + + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for num_bits in 0..1500 { + let mut cs = TestConstraintSystem::::new(); + + let bits: Vec = (0..num_bits).map(|_| rng.gen()).collect(); + + let circuit_bits = bits.iter().enumerate() + .map(|(i, &b)| { + Boolean::from( + AllocatedBit::alloc( + cs.namespace(|| format!("bit {}", i)), + Some(b) + ).unwrap() + ) + }) + .collect::>(); + + let expected_inputs = compute_multipacking::(&bits); + + pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap(); + + assert!(cs.is_satisfied()); + assert!(cs.verify(&expected_inputs)); + } +} diff --git a/src/rust/sapling-crypto/src/circuit/num.rs b/src/rust/sapling-crypto/src/circuit/num.rs new file mode 100644 index 000000000..f6b92d693 --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/num.rs @@ -0,0 +1,622 @@ +use pairing::{ + Engine, + Field, + PrimeField, + PrimeFieldRepr, + BitIterator +}; + +use bellman::{ + SynthesisError, + ConstraintSystem, + LinearCombination, + Variable +}; + +use super::{ + Assignment +}; + +use super::boolean::{ + self, + Boolean, + AllocatedBit +}; + +pub struct AllocatedNum { + value: Option, + variable: Variable +} + +impl Clone for AllocatedNum { + fn clone(&self) -> Self { + AllocatedNum { + value: self.value, + variable: self.variable + } + } +} + +impl AllocatedNum { + pub fn alloc( + mut cs: CS, + value: F, + ) -> Result + where CS: ConstraintSystem, + F: FnOnce() -> Result + { + let mut new_value = None; + let var = cs.alloc(|| "num", || { + let tmp = value()?; + + new_value = Some(tmp); + + Ok(tmp) + })?; + + Ok(AllocatedNum { + value: new_value, + variable: var + }) + } + + pub fn inputize( + &self, + mut cs: CS + ) -> Result<(), SynthesisError> + where CS: ConstraintSystem + { + let input = cs.alloc_input( + || "input variable", + || { + Ok(*self.value.get()?) + } + )?; + + cs.enforce( + || "enforce input is correct", + |lc| lc + input, + |lc| lc + CS::one(), + |lc| lc + self.variable + ); + + Ok(()) + } + + /// Deconstructs this allocated number into its + /// boolean representation in little-endian bit + /// order, requiring that the representation + /// strictly exists "in the field" (i.e., a + /// congruency is not allowed.) + pub fn into_bits_le_strict( + &self, + mut cs: CS + ) -> Result, SynthesisError> + where CS: ConstraintSystem + { + pub fn kary_and( + mut cs: CS, + v: &[AllocatedBit] + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + assert!(v.len() > 0); + + // Let's keep this simple for now and just AND them all + // manually + let mut cur = None; + + for (i, v) in v.iter().enumerate() { + if cur.is_none() { + cur = Some(v.clone()); + } else { + cur = Some(AllocatedBit::and( + cs.namespace(|| format!("and {}", i)), + cur.as_ref().unwrap(), + v + )?); + } + } + + Ok(cur.expect("v.len() > 0")) + } + + // We want to ensure that the bit representation of a is + // less than or equal to r - 1. + let mut a = self.value.map(|e| BitIterator::new(e.into_repr())); + let mut b = E::Fr::char(); + b.sub_noborrow(&1.into()); + + let mut result = vec![]; + + // Runs of ones in r + let mut last_run = None; + let mut current_run = vec![]; + + let mut found_one = false; + let mut i = 0; + for b in BitIterator::new(b) { + let a_bit = a.as_mut().map(|e| e.next().unwrap()); + + // Skip over unset bits at the beginning + found_one |= b; + if !found_one { + // a_bit should also be false + a_bit.map(|e| assert!(!e)); + continue; + } + + if b { + // This is part of a run of ones. Let's just + // allocate the boolean with the expected value. + let a_bit = AllocatedBit::alloc( + cs.namespace(|| format!("bit {}", i)), + a_bit + )?; + // ... and add it to the current run of ones. + current_run.push(a_bit.clone()); + result.push(a_bit); + } else { + if current_run.len() > 0 { + // This is the start of a run of zeros, but we need + // to k-ary AND against `last_run` first. + + if last_run.is_some() { + current_run.push(last_run.clone().unwrap()); + } + last_run = Some(kary_and( + cs.namespace(|| format!("run ending at {}", i)), + ¤t_run + )?); + current_run.truncate(0); + } + + // If `last_run` is true, `a` must be false, or it would + // not be in the field. + // + // If `last_run` is false, `a` can be true or false. + + let a_bit = AllocatedBit::alloc_conditionally( + cs.namespace(|| format!("bit {}", i)), + a_bit, + &last_run.as_ref().expect("char always starts with a one") + )?; + result.push(a_bit); + } + + i += 1; + } + + // char is prime, so we'll always end on + // a run of zeros. + assert_eq!(current_run.len(), 0); + + // Now, we have `result` in big-endian order. + // However, now we have to unpack self! + + let mut lc = LinearCombination::zero(); + let mut coeff = E::Fr::one(); + + for bit in result.iter().rev() { + lc = lc + (coeff, bit.get_variable()); + + coeff.double(); + } + + lc = lc - self.variable; + + cs.enforce( + || "unpacking constraint", + |lc| lc, + |lc| lc, + |_| lc + ); + + // Convert into booleans, and reverse for little-endian bit order + Ok(result.into_iter().map(|b| Boolean::from(b)).rev().collect()) + } + + /// Convert the allocated number into its little-endian representation. + /// Note that this does not strongly enforce that the commitment is + /// "in the field." + pub fn into_bits_le( + &self, + mut cs: CS + ) -> Result, SynthesisError> + where CS: ConstraintSystem + { + let bits = boolean::field_into_allocated_bits_le( + &mut cs, + self.value + )?; + + let mut lc = LinearCombination::zero(); + let mut coeff = E::Fr::one(); + + for bit in bits.iter() { + lc = lc + (coeff, bit.get_variable()); + + coeff.double(); + } + + lc = lc - self.variable; + + cs.enforce( + || "unpacking constraint", + |lc| lc, + |lc| lc, + |_| lc + ); + + Ok(bits.into_iter().map(|b| Boolean::from(b)).collect()) + } + + pub fn mul( + &self, + mut cs: CS, + other: &Self + ) -> Result + where CS: ConstraintSystem + { + let mut value = None; + + let var = cs.alloc(|| "product num", || { + let mut tmp = *self.value.get()?; + tmp.mul_assign(other.value.get()?); + + value = Some(tmp); + + Ok(tmp) + })?; + + // Constrain: a * b = ab + cs.enforce( + || "multiplication constraint", + |lc| lc + self.variable, + |lc| lc + other.variable, + |lc| lc + var + ); + + Ok(AllocatedNum { + value: value, + variable: var + }) + } + + pub fn square( + &self, + mut cs: CS + ) -> Result + where CS: ConstraintSystem + { + let mut value = None; + + let var = cs.alloc(|| "squared num", || { + let mut tmp = *self.value.get()?; + tmp.square(); + + value = Some(tmp); + + Ok(tmp) + })?; + + // Constrain: a * a = aa + cs.enforce( + || "squaring constraint", + |lc| lc + self.variable, + |lc| lc + self.variable, + |lc| lc + var + ); + + Ok(AllocatedNum { + value: value, + variable: var + }) + } + + pub fn assert_nonzero( + &self, + mut cs: CS + ) -> Result<(), SynthesisError> + where CS: ConstraintSystem + { + let inv = cs.alloc(|| "ephemeral inverse", || { + let tmp = *self.value.get()?; + + if tmp.is_zero() { + Err(SynthesisError::DivisionByZero) + } else { + Ok(tmp.inverse().unwrap()) + } + })?; + + // Constrain a * inv = 1, which is only valid + // iff a has a multiplicative inverse, untrue + // for zero. + cs.enforce( + || "nonzero assertion constraint", + |lc| lc + self.variable, + |lc| lc + inv, + |lc| lc + CS::one() + ); + + Ok(()) + } + + /// Takes two allocated numbers (a, b) and returns + /// (b, a) if the condition is true, and (a, b) + /// otherwise. + pub fn conditionally_reverse( + mut cs: CS, + a: &Self, + b: &Self, + condition: &Boolean + ) -> Result<(Self, Self), SynthesisError> + where CS: ConstraintSystem + { + let c = Self::alloc( + cs.namespace(|| "conditional reversal result 1"), + || { + if *condition.get_value().get()? { + Ok(*b.value.get()?) + } else { + Ok(*a.value.get()?) + } + } + )?; + + cs.enforce( + || "first conditional reversal", + |lc| lc + a.variable - b.variable, + |_| condition.lc(CS::one(), E::Fr::one()), + |lc| lc + a.variable - c.variable + ); + + let d = Self::alloc( + cs.namespace(|| "conditional reversal result 2"), + || { + if *condition.get_value().get()? { + Ok(*a.value.get()?) + } else { + Ok(*b.value.get()?) + } + } + )?; + + cs.enforce( + || "second conditional reversal", + |lc| lc + b.variable - a.variable, + |_| condition.lc(CS::one(), E::Fr::one()), + |lc| lc + b.variable - d.variable + ); + + Ok((c, d)) + } + + pub fn get_value(&self) -> Option { + self.value + } + + pub fn get_variable(&self) -> Variable { + self.variable + } +} + +pub struct Num { + value: Option, + lc: LinearCombination +} + +impl From> for Num { + fn from(num: AllocatedNum) -> Num { + Num { + value: num.value, + lc: LinearCombination::::zero() + num.variable + } + } +} + +impl Num { + pub fn zero() -> Self { + Num { + value: Some(E::Fr::zero()), + lc: LinearCombination::zero() + } + } + + pub fn get_value(&self) -> Option { + self.value + } + + pub fn lc(&self, coeff: E::Fr) -> LinearCombination { + LinearCombination::zero() + (coeff, &self.lc) + } + + pub fn add_bool_with_coeff( + self, + one: Variable, + bit: &Boolean, + coeff: E::Fr + ) -> Self + { + let newval = match (self.value, bit.get_value()) { + (Some(mut curval), Some(bval)) => { + if bval { + curval.add_assign(&coeff); + } + + Some(curval) + }, + _ => None + }; + + Num { + value: newval, + lc: self.lc + &bit.lc(one, coeff) + } + } +} + +#[cfg(test)] +mod test { + use rand::{SeedableRng, Rand, Rng, XorShiftRng}; + use bellman::{ConstraintSystem}; + use pairing::bls12_381::{Bls12, Fr}; + use pairing::{Field, PrimeField, BitIterator}; + use ::circuit::test::*; + use super::{AllocatedNum, Boolean}; + + #[test] + fn test_allocated_num() { + let mut cs = TestConstraintSystem::::new(); + + AllocatedNum::alloc(&mut cs, || Ok(Fr::one())).unwrap(); + + assert!(cs.get("num") == Fr::one()); + } + + #[test] + fn test_num_squaring() { + let mut cs = TestConstraintSystem::::new(); + + let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap(); + let n2 = n.square(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + assert!(cs.get("squared num") == Fr::from_str("9").unwrap()); + assert!(n2.value.unwrap() == Fr::from_str("9").unwrap()); + cs.set("squared num", Fr::from_str("10").unwrap()); + assert!(!cs.is_satisfied()); + } + + #[test] + fn test_num_multiplication() { + let mut cs = TestConstraintSystem::::new(); + + let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap(); + let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap(); + let n3 = n.mul(&mut cs, &n2).unwrap(); + + assert!(cs.is_satisfied()); + assert!(cs.get("product num") == Fr::from_str("120").unwrap()); + assert!(n3.value.unwrap() == Fr::from_str("120").unwrap()); + cs.set("product num", Fr::from_str("121").unwrap()); + assert!(!cs.is_satisfied()); + } + + #[test] + fn test_num_conditional_reversal() { + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + { + let mut cs = TestConstraintSystem::::new(); + + let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap(); + let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap(); + let condition = Boolean::constant(false); + let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); + + assert!(cs.is_satisfied()); + + assert_eq!(a.value.unwrap(), c.value.unwrap()); + assert_eq!(b.value.unwrap(), d.value.unwrap()); + } + + { + let mut cs = TestConstraintSystem::::new(); + + let a = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(rng.gen())).unwrap(); + let b = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(rng.gen())).unwrap(); + let condition = Boolean::constant(true); + let (c, d) = AllocatedNum::conditionally_reverse(&mut cs, &a, &b, &condition).unwrap(); + + assert!(cs.is_satisfied()); + + assert_eq!(a.value.unwrap(), d.value.unwrap()); + assert_eq!(b.value.unwrap(), c.value.unwrap()); + } + } + + #[test] + fn test_num_nonzero() { + { + let mut cs = TestConstraintSystem::::new(); + + let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap(); + n.assert_nonzero(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + cs.set("ephemeral inverse", Fr::from_str("3").unwrap()); + assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint")); + } + { + let mut cs = TestConstraintSystem::::new(); + + let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::zero())).unwrap(); + assert!(n.assert_nonzero(&mut cs).is_err()); + } + } + + #[test] + fn test_into_bits_strict() { + let mut negone = Fr::one(); + negone.negate(); + + let mut cs = TestConstraintSystem::::new(); + + let n = AllocatedNum::alloc(&mut cs, || Ok(negone)).unwrap(); + n.into_bits_le_strict(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + + // make the bit representation the characteristic + cs.set("bit 254/boolean", Fr::one()); + + // this makes the conditional boolean constraint fail + assert_eq!(cs.which_is_unsatisfied().unwrap(), "bit 254/boolean constraint"); + } + + #[test] + fn test_into_bits() { + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for i in 0..200 { + let r = Fr::rand(&mut rng); + let mut cs = TestConstraintSystem::::new(); + + let n = AllocatedNum::alloc(&mut cs, || Ok(r)).unwrap(); + + let bits = if i % 2 == 0 { + n.into_bits_le(&mut cs).unwrap() + } else { + n.into_bits_le_strict(&mut cs).unwrap() + }; + + assert!(cs.is_satisfied()); + + for (b, a) in BitIterator::new(r.into_repr()).skip(1).zip(bits.iter().rev()) { + if let &Boolean::Is(ref a) = a { + assert_eq!(b, a.get_value().unwrap()); + } else { + unreachable!() + } + } + + cs.set("num", Fr::rand(&mut rng)); + assert!(!cs.is_satisfied()); + cs.set("num", r); + assert!(cs.is_satisfied()); + + for i in 0..Fr::NUM_BITS { + let name = format!("bit {}/boolean", i); + let cur = cs.get(&name); + let mut tmp = Fr::one(); + tmp.sub_assign(&cur); + cs.set(&name, tmp); + assert!(!cs.is_satisfied()); + cs.set(&name, cur); + assert!(cs.is_satisfied()); + } + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/pedersen_hash.rs b/src/rust/sapling-crypto/src/circuit/pedersen_hash.rs new file mode 100644 index 000000000..eb1745fd7 --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/pedersen_hash.rs @@ -0,0 +1,194 @@ +use super::*; +use super::ecc::{ + MontgomeryPoint, + EdwardsPoint +}; +use super::boolean::Boolean; +use ::jubjub::*; +use bellman::{ + ConstraintSystem +}; +use super::lookup::*; +pub use pedersen_hash::Personalization; + +impl Personalization { + fn get_constant_bools(&self) -> Vec { + self.get_bits() + .into_iter() + .map(|e| Boolean::constant(e)) + .collect() + } +} + +pub fn pedersen_hash( + mut cs: CS, + personalization: Personalization, + bits: &[Boolean], + params: &E::Params +) -> Result, SynthesisError> + where CS: ConstraintSystem +{ + let personalization = personalization.get_constant_bools(); + assert_eq!(personalization.len(), 6); + + let mut edwards_result = None; + let mut bits = personalization.iter().chain(bits.iter()); + let mut segment_generators = params.pedersen_circuit_generators().iter(); + let boolean_false = Boolean::constant(false); + + let mut segment_i = 0; + loop { + let mut segment_result = None; + let mut segment_windows = &segment_generators.next() + .expect("enough segments")[..]; + + let mut window_i = 0; + while let Some(a) = bits.next() { + let b = bits.next().unwrap_or(&boolean_false); + let c = bits.next().unwrap_or(&boolean_false); + + let tmp = lookup3_xy_with_conditional_negation( + cs.namespace(|| format!("segment {}, window {}", segment_i, window_i)), + &[a.clone(), b.clone(), c.clone()], + &segment_windows[0] + )?; + + let tmp = MontgomeryPoint::interpret_unchecked(tmp.0, tmp.1); + + match segment_result { + None => { + segment_result = Some(tmp); + }, + Some(ref mut segment_result) => { + *segment_result = tmp.add( + cs.namespace(|| format!("addition of segment {}, window {}", segment_i, window_i)), + segment_result, + params + )?; + } + } + + segment_windows = &segment_windows[1..]; + + if segment_windows.len() == 0 { + break; + } + + window_i += 1; + } + + match segment_result { + Some(segment_result) => { + // Convert this segment into twisted Edwards form. + let segment_result = segment_result.into_edwards( + cs.namespace(|| format!("conversion of segment {} into edwards", segment_i)), + params + )?; + + match edwards_result { + Some(ref mut edwards_result) => { + *edwards_result = segment_result.add( + cs.namespace(|| format!("addition of segment {} to accumulator", segment_i)), + edwards_result, + params + )?; + }, + None => { + edwards_result = Some(segment_result); + } + } + }, + None => { + // We didn't process any new bits. + break; + } + } + + segment_i += 1; + } + + Ok(edwards_result.unwrap()) +} + +#[cfg(test)] +mod test { + use rand::{SeedableRng, Rng, XorShiftRng}; + use super::*; + use ::circuit::test::*; + use ::circuit::boolean::{Boolean, AllocatedBit}; + use pairing::bls12_381::{Bls12, Fr}; + use pairing::PrimeField; + + #[test] + fn test_pedersen_hash_constraints() { + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let params = &JubjubBls12::new(); + let mut cs = TestConstraintSystem::::new(); + + let input: Vec = (0..(Fr::NUM_BITS * 2)).map(|_| rng.gen()).collect(); + + let input_bools: Vec = input.iter().enumerate().map(|(i, b)| { + Boolean::from( + AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap() + ) + }).collect(); + + pedersen_hash( + cs.namespace(|| "pedersen hash"), + Personalization::NoteCommitment, + &input_bools, + params + ).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 1377); + } + + #[test] + fn test_pedersen_hash() { + let mut rng = XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let params = &JubjubBls12::new(); + + for length in 0..751 { + for _ in 0..5 { + let mut input: Vec = (0..length).map(|_| rng.gen()).collect(); + + let mut cs = TestConstraintSystem::::new(); + + let input_bools: Vec = input.iter().enumerate().map(|(i, b)| { + Boolean::from( + AllocatedBit::alloc(cs.namespace(|| format!("input {}", i)), Some(*b)).unwrap() + ) + }).collect(); + + let res = pedersen_hash( + cs.namespace(|| "pedersen hash"), + Personalization::MerkleTree(1), + &input_bools, + params + ).unwrap(); + + assert!(cs.is_satisfied()); + + let expected = ::pedersen_hash::pedersen_hash::( + Personalization::MerkleTree(1), + input.clone().into_iter(), + params + ).into_xy(); + + assert_eq!(res.get_x().get_value().unwrap(), expected.0); + assert_eq!(res.get_y().get_value().unwrap(), expected.1); + + // Test against the output of a different personalization + let unexpected = ::pedersen_hash::pedersen_hash::( + Personalization::MerkleTree(0), + input.into_iter(), + params + ).into_xy(); + + assert!(res.get_x().get_value().unwrap() != unexpected.0); + assert!(res.get_y().get_value().unwrap() != unexpected.1); + } + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/sapling/mod.rs b/src/rust/sapling-crypto/src/circuit/sapling/mod.rs new file mode 100644 index 000000000..11469bcdb --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/sapling/mod.rs @@ -0,0 +1,815 @@ +use pairing::{ + PrimeField, + PrimeFieldRepr, + Field, +}; + +use bellman::{ + SynthesisError, + ConstraintSystem, + Circuit +}; + +use jubjub::{ + JubjubEngine, + FixedGenerators +}; + +use constants; + +use primitives::{ + ValueCommitment, + ProofGenerationKey, + PaymentAddress +}; + +use super::Assignment; +use super::boolean; +use super::ecc; +use super::pedersen_hash; +use super::blake2s; +use super::num; +use super::multipack; + +/// This is an instance of the `Spend` circuit. +pub struct Spend<'a, E: JubjubEngine> { + pub params: &'a E::Params, + + /// Pedersen commitment to the value being spent + pub value_commitment: Option>, + + /// Key required to construct proofs for spending notes + /// for a particular spending key + pub proof_generation_key: Option>, + + /// The payment address associated with the note + pub payment_address: Option>, + + /// The randomness of the note commitment + pub commitment_randomness: Option, + + /// Re-randomization of the public key + pub ar: Option, + + /// The authentication path of the commitment in the tree + pub auth_path: Vec>, + + /// The anchor; the root of the tree. If the note being + /// spent is zero-value, this can be anything. + pub anchor: Option +} + +/// This is an output circuit instance. +pub struct Output<'a, E: JubjubEngine> { + pub params: &'a E::Params, + + /// Pedersen commitment to the value being spent + pub value_commitment: Option>, + + /// The payment address of the recipient + pub payment_address: Option>, + + /// The randomness used to hide the note commitment data + pub commitment_randomness: Option, + + /// The ephemeral secret key for DH with recipient + pub esk: Option +} + +/// Exposes a Pedersen commitment to the value as an +/// input to the circuit +fn expose_value_commitment( + mut cs: CS, + value_commitment: Option>, + params: &E::Params +) -> Result, SynthesisError> + where E: JubjubEngine, + CS: ConstraintSystem +{ + // Booleanize the value into little-endian bit order + let value_bits = boolean::u64_into_boolean_vec_le( + cs.namespace(|| "value"), + value_commitment.as_ref().map(|c| c.value) + )?; + + // Compute the note value in the exponent + let value = ecc::fixed_base_multiplication( + cs.namespace(|| "compute the value in the exponent"), + FixedGenerators::ValueCommitmentValue, + &value_bits, + params + )?; + + // Booleanize the randomness. This does not ensure + // the bit representation is "in the field" because + // it doesn't matter for security. + let rcv = boolean::field_into_boolean_vec_le( + cs.namespace(|| "rcv"), + value_commitment.as_ref().map(|c| c.randomness) + )?; + + // Compute the randomness in the exponent + let rcv = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of rcv"), + FixedGenerators::ValueCommitmentRandomness, + &rcv, + params + )?; + + // Compute the Pedersen commitment to the value + let cv = value.add( + cs.namespace(|| "computation of cv"), + &rcv, + params + )?; + + // Expose the commitment as an input to the circuit + cv.inputize(cs.namespace(|| "commitment point"))?; + + Ok(value_bits) +} + +impl<'a, E: JubjubEngine> Circuit for Spend<'a, E> { + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> + { + // Prover witnesses ak (ensures that it's on the curve) + let ak = ecc::EdwardsPoint::witness( + cs.namespace(|| "ak"), + self.proof_generation_key.as_ref().map(|k| k.ak.clone()), + self.params + )?; + + // There are no sensible attacks on small order points + // of ak (that we're aware of!) but it's a cheap check, + // so we do it. + ak.assert_not_small_order( + cs.namespace(|| "ak not small order"), + self.params + )?; + + // Rerandomize ak and expose it as an input to the circuit + { + let ar = boolean::field_into_boolean_vec_le( + cs.namespace(|| "ar"), + self.ar + )?; + + // Compute the randomness in the exponent + let ar = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of randomization for the signing key"), + FixedGenerators::SpendingKeyGenerator, + &ar, + self.params + )?; + + let rk = ak.add( + cs.namespace(|| "computation of rk"), + &ar, + self.params + )?; + + rk.inputize(cs.namespace(|| "rk"))?; + } + + // Compute nk = [nsk] ProofGenerationKey + let nk; + { + // Witness nsk as bits + let nsk = boolean::field_into_boolean_vec_le( + cs.namespace(|| "nsk"), + self.proof_generation_key.as_ref().map(|k| k.nsk.clone()) + )?; + + // NB: We don't ensure that the bit representation of nsk + // is "in the field" (Fs) because it's not used except to + // demonstrate the prover knows it. If they know a + // congruency then that's equivalent. + + // Compute nk = [nsk] ProvingPublicKey + nk = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of nk"), + FixedGenerators::ProofGenerationKey, + &nsk, + self.params + )?; + } + + // This is the "viewing key" preimage for CRH^ivk + let mut ivk_preimage = vec![]; + + // Place ak in the preimage for CRH^ivk + ivk_preimage.extend( + ak.repr(cs.namespace(|| "representation of ak"))? + ); + + // This is the nullifier preimage for PRF^nf + let mut nf_preimage = vec![]; + + // Extend ivk and nf preimages with the representation of + // nk. + { + let repr_nk = nk.repr( + cs.namespace(|| "representation of nk") + )?; + + ivk_preimage.extend(repr_nk.iter().cloned()); + nf_preimage.extend(repr_nk); + } + + assert_eq!(ivk_preimage.len(), 512); + assert_eq!(nf_preimage.len(), 256); + + // Compute the incoming viewing key ivk + let mut ivk = blake2s::blake2s( + cs.namespace(|| "computation of ivk"), + &ivk_preimage, + constants::CRH_IVK_PERSONALIZATION + )?; + + // drop_5 to ensure it's in the field + ivk.truncate(E::Fs::CAPACITY as usize); + + // Witness g_d, checking that it's on the curve. + let g_d = { + // This binding is to avoid a weird edge case in Rust's + // ownership/borrowing rules. self is partially moved + // above, but the closure for and_then will have to + // move self (or a reference to self) to reference + // self.params, so we have to copy self.params here. + let params = self.params; + + ecc::EdwardsPoint::witness( + cs.namespace(|| "witness g_d"), + self.payment_address.as_ref().and_then(|a| a.g_d(params)), + self.params + )? + }; + + // Check that g_d is not small order. Technically, this check + // is already done in the Output circuit, and this proof ensures + // g_d is bound to a product of that check, but for defense in + // depth let's check it anyway. It's cheap. + g_d.assert_not_small_order( + cs.namespace(|| "g_d not small order"), + self.params + )?; + + // Compute pk_d = g_d^ivk + let pk_d = g_d.mul( + cs.namespace(|| "compute pk_d"), + &ivk, + self.params + )?; + + // Compute note contents: + // value (in big endian) followed by g_d and pk_d + let mut note_contents = vec![]; + + // Handle the value; we'll need it later for the + // dummy input check. + let mut value_num = num::Num::zero(); + { + // Get the value in little-endian bit order + let value_bits = expose_value_commitment( + cs.namespace(|| "value commitment"), + self.value_commitment, + self.params + )?; + + // Compute the note's value as a linear combination + // of the bits. + let mut coeff = E::Fr::one(); + for bit in &value_bits { + value_num = value_num.add_bool_with_coeff( + CS::one(), + bit, + coeff + ); + coeff.double(); + } + + // Place the value in the note + note_contents.extend(value_bits); + } + + // Place g_d in the note + note_contents.extend( + g_d.repr(cs.namespace(|| "representation of g_d"))? + ); + + // Place pk_d in the note + note_contents.extend( + pk_d.repr(cs.namespace(|| "representation of pk_d"))? + ); + + assert_eq!( + note_contents.len(), + 64 + // value + 256 + // g_d + 256 // p_d + ); + + // Compute the hash of the note contents + let mut cm = pedersen_hash::pedersen_hash( + cs.namespace(|| "note content hash"), + pedersen_hash::Personalization::NoteCommitment, + ¬e_contents, + self.params + )?; + + { + // Booleanize the randomness for the note commitment + let rcm = boolean::field_into_boolean_vec_le( + cs.namespace(|| "rcm"), + self.commitment_randomness + )?; + + // Compute the note commitment randomness in the exponent + let rcm = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of commitment randomness"), + FixedGenerators::NoteCommitmentRandomness, + &rcm, + self.params + )?; + + // Randomize the note commitment. Pedersen hashes are not + // themselves hiding commitments. + cm = cm.add( + cs.namespace(|| "randomization of note commitment"), + &rcm, + self.params + )?; + } + + // This will store (least significant bit first) + // the position of the note in the tree, for use + // in nullifier computation. + let mut position_bits = vec![]; + + // This is an injective encoding, as cur is a + // point in the prime order subgroup. + let mut cur = cm.get_x().clone(); + + // Ascend the merkle tree authentication path + for (i, e) in self.auth_path.into_iter().enumerate() { + let cs = &mut cs.namespace(|| format!("merkle tree hash {}", i)); + + // Determines if the current subtree is the "right" leaf at this + // depth of the tree. + let cur_is_right = boolean::Boolean::from(boolean::AllocatedBit::alloc( + cs.namespace(|| "position bit"), + e.map(|e| e.1) + )?); + + // Push this boolean for nullifier computation later + position_bits.push(cur_is_right.clone()); + + // Witness the authentication path element adjacent + // at this depth. + let path_element = num::AllocatedNum::alloc( + cs.namespace(|| "path element"), + || { + Ok(e.get()?.0) + } + )?; + + // Swap the two if the current subtree is on the right + let (xl, xr) = num::AllocatedNum::conditionally_reverse( + cs.namespace(|| "conditional reversal of preimage"), + &cur, + &path_element, + &cur_is_right + )?; + + // We don't need to be strict, because the function is + // collision-resistant. If the prover witnesses a congruency, + // they will be unable to find an authentication path in the + // tree with high probability. + let mut preimage = vec![]; + preimage.extend(xl.into_bits_le(cs.namespace(|| "xl into bits"))?); + preimage.extend(xr.into_bits_le(cs.namespace(|| "xr into bits"))?); + + // Compute the new subtree value + cur = pedersen_hash::pedersen_hash( + cs.namespace(|| "computation of pedersen hash"), + pedersen_hash::Personalization::MerkleTree(i), + &preimage, + self.params + )?.get_x().clone(); // Injective encoding + } + + { + let real_anchor_value = self.anchor; + + // Allocate the "real" anchor that will be exposed. + let rt = num::AllocatedNum::alloc( + cs.namespace(|| "conditional anchor"), + || { + Ok(*real_anchor_value.get()?) + } + )?; + + // (cur - rt) * value = 0 + // if value is zero, cur and rt can be different + // if value is nonzero, they must be equal + cs.enforce( + || "conditionally enforce correct root", + |lc| lc + cur.get_variable() - rt.get_variable(), + |lc| lc + &value_num.lc(E::Fr::one()), + |lc| lc + ); + + // Expose the anchor + rt.inputize(cs.namespace(|| "anchor"))?; + } + + // Compute the cm + g^position for preventing + // faerie gold attacks + let mut rho = cm; + { + // Compute the position in the exponent + let position = ecc::fixed_base_multiplication( + cs.namespace(|| "g^position"), + FixedGenerators::NullifierPosition, + &position_bits, + self.params + )?; + + // Add the position to the commitment + rho = rho.add( + cs.namespace(|| "faerie gold prevention"), + &position, + self.params + )?; + } + + // Let's compute nf = BLAKE2s(nk || rho) + nf_preimage.extend( + rho.repr(cs.namespace(|| "representation of rho"))? + ); + + assert_eq!(nf_preimage.len(), 512); + + // Compute nf + let nf = blake2s::blake2s( + cs.namespace(|| "nf computation"), + &nf_preimage, + constants::PRF_NF_PERSONALIZATION + )?; + + multipack::pack_into_inputs(cs.namespace(|| "pack nullifier"), &nf) + } +} + +impl<'a, E: JubjubEngine> Circuit for Output<'a, E> { + fn synthesize>(self, cs: &mut CS) -> Result<(), SynthesisError> + { + // Let's start to construct our note, which contains + // value (big endian) + let mut note_contents = vec![]; + + // Expose the value commitment and place the value + // in the note. + note_contents.extend(expose_value_commitment( + cs.namespace(|| "value commitment"), + self.value_commitment, + self.params + )?); + + // Let's deal with g_d + { + let params = self.params; + + // Prover witnesses g_d, ensuring it's on the + // curve. + let g_d = ecc::EdwardsPoint::witness( + cs.namespace(|| "witness g_d"), + self.payment_address.as_ref().and_then(|a| a.g_d(params)), + self.params + )?; + + // g_d is ensured to be large order. The relationship + // between g_d and pk_d ultimately binds ivk to the + // note. If this were a small order point, it would + // not do this correctly, and the prover could + // double-spend by finding random ivk's that satisfy + // the relationship. + // + // Further, if it were small order, epk would be + // small order too! + g_d.assert_not_small_order( + cs.namespace(|| "g_d not small order"), + self.params + )?; + + // Extend our note contents with the representation of + // g_d. + note_contents.extend( + g_d.repr(cs.namespace(|| "representation of g_d"))? + ); + + // Booleanize our ephemeral secret key + let esk = boolean::field_into_boolean_vec_le( + cs.namespace(|| "esk"), + self.esk + )?; + + // Create the ephemeral public key from g_d. + let epk = g_d.mul( + cs.namespace(|| "epk computation"), + &esk, + self.params + )?; + + // Expose epk publicly. + epk.inputize(cs.namespace(|| "epk"))?; + } + + // Now let's deal with pk_d. We don't do any checks and + // essentially allow the prover to witness any 256 bits + // they would like. + { + // Just grab pk_d from the witness + let pk_d = self.payment_address.as_ref().map(|e| e.pk_d.into_xy()); + + // Witness the y-coordinate, encoded as little + // endian bits (to match the representation) + let y_contents = boolean::field_into_boolean_vec_le( + cs.namespace(|| "pk_d bits of y"), + pk_d.map(|e| e.1) + )?; + + // Witness the sign bit + let sign_bit = boolean::Boolean::from(boolean::AllocatedBit::alloc( + cs.namespace(|| "pk_d bit of x"), + pk_d.map(|e| e.0.into_repr().is_odd()) + )?); + + // Extend the note with pk_d representation + note_contents.extend(y_contents); + note_contents.push(sign_bit); + } + + assert_eq!( + note_contents.len(), + 64 + // value + 256 + // g_d + 256 // pk_d + ); + + // Compute the hash of the note contents + let mut cm = pedersen_hash::pedersen_hash( + cs.namespace(|| "note content hash"), + pedersen_hash::Personalization::NoteCommitment, + ¬e_contents, + self.params + )?; + + { + // Booleanize the randomness + let rcm = boolean::field_into_boolean_vec_le( + cs.namespace(|| "rcm"), + self.commitment_randomness + )?; + + // Compute the note commitment randomness in the exponent + let rcm = ecc::fixed_base_multiplication( + cs.namespace(|| "computation of commitment randomness"), + FixedGenerators::NoteCommitmentRandomness, + &rcm, + self.params + )?; + + // Randomize our note commitment + cm = cm.add( + cs.namespace(|| "randomization of note commitment"), + &rcm, + self.params + )?; + } + + // Only the x-coordinate of the output is revealed, + // since we know it is prime order, and we know that + // the x-coordinate is an injective encoding for + // prime-order elements. + cm.get_x().inputize(cs.namespace(|| "commitment"))?; + + Ok(()) + } +} + +#[test] +fn test_input_circuit_with_bls12_381() { + use pairing::{Field, BitIterator}; + use pairing::bls12_381::*; + use rand::{SeedableRng, Rng, XorShiftRng}; + use ::circuit::test::*; + use jubjub::{JubjubBls12, fs, edwards}; + + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let tree_depth = 32; + + for _ in 0..10 { + let value_commitment = ValueCommitment { + value: rng.gen(), + randomness: rng.gen() + }; + + let nsk: fs::Fs = rng.gen(); + let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + + let proof_generation_key = ::primitives::ProofGenerationKey { + ak: ak.clone(), + nsk: nsk.clone() + }; + + let viewing_key = proof_generation_key.into_viewing_key(params); + + let payment_address; + + loop { + let diversifier = ::primitives::Diversifier(rng.gen()); + + if let Some(p) = viewing_key.into_payment_address( + diversifier, + params + ) + { + payment_address = p; + break; + } + } + + let g_d = payment_address.diversifier.g_d(params).unwrap(); + let commitment_randomness: fs::Fs = rng.gen(); + let auth_path = vec![Some((rng.gen(), rng.gen())); tree_depth]; + let ar: fs::Fs = rng.gen(); + + { + let rk = viewing_key.rk(ar, params).into_xy(); + let expected_value_cm = value_commitment.cm(params).into_xy(); + let note = ::primitives::Note { + value: value_commitment.value, + g_d: g_d.clone(), + pk_d: payment_address.pk_d.clone(), + r: commitment_randomness.clone() + }; + + let mut position = 0u64; + let cm: Fr = note.cm(params); + let mut cur = cm.clone(); + + for (i, val) in auth_path.clone().into_iter().enumerate() + { + let (uncle, b) = val.unwrap(); + + let mut lhs = cur; + let mut rhs = uncle; + + if b { + ::std::mem::swap(&mut lhs, &mut rhs); + } + + let mut lhs: Vec = BitIterator::new(lhs.into_repr()).collect(); + let mut rhs: Vec = BitIterator::new(rhs.into_repr()).collect(); + + lhs.reverse(); + rhs.reverse(); + + cur = ::pedersen_hash::pedersen_hash::( + ::pedersen_hash::Personalization::MerkleTree(i), + lhs.into_iter() + .take(Fr::NUM_BITS as usize) + .chain(rhs.into_iter().take(Fr::NUM_BITS as usize)), + params + ).into_xy().0; + + if b { + position |= 1 << i; + } + } + + let expected_nf = note.nf(&viewing_key, position, params); + let expected_nf = multipack::bytes_to_bits_le(&expected_nf); + let expected_nf = multipack::compute_multipacking::(&expected_nf); + assert_eq!(expected_nf.len(), 2); + + let mut cs = TestConstraintSystem::::new(); + + let instance = Spend { + params: params, + value_commitment: Some(value_commitment.clone()), + proof_generation_key: Some(proof_generation_key.clone()), + payment_address: Some(payment_address.clone()), + commitment_randomness: Some(commitment_randomness), + ar: Some(ar), + auth_path: auth_path.clone(), + anchor: Some(cur) + }; + + instance.synthesize(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 98777); + assert_eq!(cs.hash(), "d37c738e83df5d9b0bb6495ac96abf21bcb2697477e2c15c2c7916ff7a3b6a89"); + + assert_eq!(cs.get("randomization of note commitment/x3/num"), cm); + + assert_eq!(cs.num_inputs(), 8); + assert_eq!(cs.get_input(0, "ONE"), Fr::one()); + assert_eq!(cs.get_input(1, "rk/x/input variable"), rk.0); + assert_eq!(cs.get_input(2, "rk/y/input variable"), rk.1); + assert_eq!(cs.get_input(3, "value commitment/commitment point/x/input variable"), expected_value_cm.0); + assert_eq!(cs.get_input(4, "value commitment/commitment point/y/input variable"), expected_value_cm.1); + assert_eq!(cs.get_input(5, "anchor/input variable"), cur); + assert_eq!(cs.get_input(6, "pack nullifier/input 0"), expected_nf[0]); + assert_eq!(cs.get_input(7, "pack nullifier/input 1"), expected_nf[1]); + } + } +} + +#[test] +fn test_output_circuit_with_bls12_381() { + use pairing::{Field}; + use pairing::bls12_381::*; + use rand::{SeedableRng, Rng, XorShiftRng}; + use ::circuit::test::*; + use jubjub::{JubjubBls12, fs, edwards}; + + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let value_commitment = ValueCommitment { + value: rng.gen(), + randomness: rng.gen() + }; + + let nsk: fs::Fs = rng.gen(); + let ak = edwards::Point::rand(rng, params).mul_by_cofactor(params); + + let proof_generation_key = ::primitives::ProofGenerationKey { + ak: ak.clone(), + nsk: nsk.clone() + }; + + let viewing_key = proof_generation_key.into_viewing_key(params); + + let payment_address; + + loop { + let diversifier = ::primitives::Diversifier(rng.gen()); + + if let Some(p) = viewing_key.into_payment_address( + diversifier, + params + ) + { + payment_address = p; + break; + } + } + + let commitment_randomness: fs::Fs = rng.gen(); + let esk: fs::Fs = rng.gen(); + + { + let mut cs = TestConstraintSystem::::new(); + + let instance = Output { + params: params, + value_commitment: Some(value_commitment.clone()), + payment_address: Some(payment_address.clone()), + commitment_randomness: Some(commitment_randomness), + esk: Some(esk.clone()) + }; + + instance.synthesize(&mut cs).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 7827); + assert_eq!(cs.hash(), "c26d5cdfe6ccd65c03390902c02e11393ea6bb96aae32a7f2ecb12eb9103faee"); + + let expected_cm = payment_address.create_note( + value_commitment.value, + commitment_randomness, + params + ).expect("should be valid").cm(params); + + let expected_value_cm = value_commitment.cm(params).into_xy(); + + let expected_epk = payment_address.g_d(params).expect("should be valid").mul(esk, params); + let expected_epk_xy = expected_epk.into_xy(); + + assert_eq!(cs.num_inputs(), 6); + assert_eq!(cs.get_input(0, "ONE"), Fr::one()); + assert_eq!(cs.get_input(1, "value commitment/commitment point/x/input variable"), expected_value_cm.0); + assert_eq!(cs.get_input(2, "value commitment/commitment point/y/input variable"), expected_value_cm.1); + assert_eq!(cs.get_input(3, "epk/x/input variable"), expected_epk_xy.0); + assert_eq!(cs.get_input(4, "epk/y/input variable"), expected_epk_xy.1); + assert_eq!(cs.get_input(5, "commitment/input variable"), expected_cm); + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/sha256.rs b/src/rust/sapling-crypto/src/circuit/sha256.rs new file mode 100644 index 000000000..7b55fc89b --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/sha256.rs @@ -0,0 +1,417 @@ +use super::uint32::UInt32; +use super::multieq::MultiEq; +use super::boolean::Boolean; +use bellman::{ConstraintSystem, SynthesisError}; +use pairing::Engine; + +const ROUND_CONSTANTS: [u32; 64] = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +]; + +const IV: [u32; 8] = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +]; + +pub fn sha256_block_no_padding( + mut cs: CS, + input: &[Boolean] +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + assert_eq!(input.len(), 512); + + Ok(sha256_compression_function( + &mut cs, + &input, + &get_sha256_iv() + )? + .into_iter() + .flat_map(|e| e.into_bits_be()) + .collect()) +} + +pub fn sha256( + mut cs: CS, + input: &[Boolean] +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + assert!(input.len() % 8 == 0); + + let mut padded = input.to_vec(); + let plen = padded.len() as u64; + // append a single '1' bit + padded.push(Boolean::constant(true)); + // append K '0' bits, where K is the minimum number >= 0 such that L + 1 + K + 64 is a multiple of 512 + while (padded.len() + 64) % 512 != 0 { + padded.push(Boolean::constant(false)); + } + // append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits + for b in (0..64).rev().map(|i| (plen >> i) & 1 == 1) { + padded.push(Boolean::constant(b)); + } + assert!(padded.len() % 512 == 0); + + let mut cur = get_sha256_iv(); + for (i, block) in padded.chunks(512).enumerate() { + cur = sha256_compression_function( + cs.namespace(|| format!("block {}", i)), + block, + &cur + )?; + } + + Ok(cur.into_iter() + .flat_map(|e| e.into_bits_be()) + .collect()) +} + +fn get_sha256_iv() -> Vec { + IV.iter().map(|&v| UInt32::constant(v)).collect() +} + +fn sha256_compression_function( + cs: CS, + input: &[Boolean], + current_hash_value: &[UInt32] +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + assert_eq!(input.len(), 512); + assert_eq!(current_hash_value.len(), 8); + + let mut w = input.chunks(32) + .map(|e| UInt32::from_bits_be(e)) + .collect::>(); + + // We can save some constraints by combining some of + // the constraints in different u32 additions + let mut cs = MultiEq::new(cs); + + for i in 16..64 { + let cs = &mut cs.namespace(|| format!("w extension {}", i)); + + // s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3) + let mut s0 = w[i-15].rotr(7); + s0 = s0.xor( + cs.namespace(|| "first xor for s0"), + &w[i-15].rotr(18) + )?; + s0 = s0.xor( + cs.namespace(|| "second xor for s0"), + &w[i-15].shr(3) + )?; + + // s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10) + let mut s1 = w[i-2].rotr(17); + s1 = s1.xor( + cs.namespace(|| "first xor for s1"), + &w[i-2].rotr(19) + )?; + s1 = s1.xor( + cs.namespace(|| "second xor for s1"), + &w[i-2].shr(10) + )?; + + let tmp = UInt32::addmany( + cs.namespace(|| "computation of w[i]"), + &[w[i-16].clone(), s0, w[i-7].clone(), s1] + )?; + + // w[i] := w[i-16] + s0 + w[i-7] + s1 + w.push(tmp); + } + + assert_eq!(w.len(), 64); + + enum Maybe { + Deferred(Vec), + Concrete(UInt32) + } + + impl Maybe { + fn compute( + self, + cs: M, + others: &[UInt32] + ) -> Result + where E: Engine, + CS: ConstraintSystem, + M: ConstraintSystem> + { + Ok(match self { + Maybe::Concrete(ref v) => { + return Ok(v.clone()) + }, + Maybe::Deferred(mut v) => { + v.extend(others.into_iter().cloned()); + UInt32::addmany( + cs, + &v + )? + } + }) + } + } + + let mut a = Maybe::Concrete(current_hash_value[0].clone()); + let mut b = current_hash_value[1].clone(); + let mut c = current_hash_value[2].clone(); + let mut d = current_hash_value[3].clone(); + let mut e = Maybe::Concrete(current_hash_value[4].clone()); + let mut f = current_hash_value[5].clone(); + let mut g = current_hash_value[6].clone(); + let mut h = current_hash_value[7].clone(); + + for i in 0..64 { + let cs = &mut cs.namespace(|| format!("compression round {}", i)); + + // S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25) + let new_e = e.compute(cs.namespace(|| "deferred e computation"), &[])?; + let mut s1 = new_e.rotr(6); + s1 = s1.xor( + cs.namespace(|| "first xor for s1"), + &new_e.rotr(11) + )?; + s1 = s1.xor( + cs.namespace(|| "second xor for s1"), + &new_e.rotr(25) + )?; + + // ch := (e and f) xor ((not e) and g) + let ch = UInt32::sha256_ch( + cs.namespace(|| "ch"), + &new_e, + &f, + &g + )?; + + // temp1 := h + S1 + ch + k[i] + w[i] + let temp1 = vec![ + h.clone(), + s1, + ch, + UInt32::constant(ROUND_CONSTANTS[i]), + w[i].clone() + ]; + + // S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22) + let new_a = a.compute(cs.namespace(|| "deferred a computation"), &[])?; + let mut s0 = new_a.rotr(2); + s0 = s0.xor( + cs.namespace(|| "first xor for s0"), + &new_a.rotr(13) + )?; + s0 = s0.xor( + cs.namespace(|| "second xor for s0"), + &new_a.rotr(22) + )?; + + // maj := (a and b) xor (a and c) xor (b and c) + let maj = UInt32::sha256_maj( + cs.namespace(|| "maj"), + &new_a, + &b, + &c + )?; + + // temp2 := S0 + maj + let temp2 = vec![s0, maj]; + + /* + h := g + g := f + f := e + e := d + temp1 + d := c + c := b + b := a + a := temp1 + temp2 + */ + + h = g; + g = f; + f = new_e; + e = Maybe::Deferred(temp1.iter().cloned().chain(Some(d)).collect::>()); + d = c; + c = b; + b = new_a; + a = Maybe::Deferred(temp1.iter().cloned().chain(temp2.iter().cloned()).collect::>()); + } + + /* + Add the compressed chunk to the current hash value: + h0 := h0 + a + h1 := h1 + b + h2 := h2 + c + h3 := h3 + d + h4 := h4 + e + h5 := h5 + f + h6 := h6 + g + h7 := h7 + h + */ + + let h0 = a.compute( + cs.namespace(|| "deferred h0 computation"), + &[current_hash_value[0].clone()] + )?; + + let h1 = UInt32::addmany( + cs.namespace(|| "new h1"), + &[current_hash_value[1].clone(), b] + )?; + + let h2 = UInt32::addmany( + cs.namespace(|| "new h2"), + &[current_hash_value[2].clone(), c] + )?; + + let h3 = UInt32::addmany( + cs.namespace(|| "new h3"), + &[current_hash_value[3].clone(), d] + )?; + + let h4 = e.compute( + cs.namespace(|| "deferred h4 computation"), + &[current_hash_value[4].clone()] + )?; + + let h5 = UInt32::addmany( + cs.namespace(|| "new h5"), + &[current_hash_value[5].clone(), f] + )?; + + let h6 = UInt32::addmany( + cs.namespace(|| "new h6"), + &[current_hash_value[6].clone(), g] + )?; + + let h7 = UInt32::addmany( + cs.namespace(|| "new h7"), + &[current_hash_value[7].clone(), h] + )?; + + Ok(vec![h0, h1, h2, h3, h4, h5, h6, h7]) +} + +#[cfg(test)] +mod test { + use super::*; + use circuit::boolean::AllocatedBit; + use pairing::bls12_381::Bls12; + use circuit::test::TestConstraintSystem; + use rand::{XorShiftRng, SeedableRng, Rng}; + + #[test] + fn test_blank_hash() { + let iv = get_sha256_iv(); + + let mut cs = TestConstraintSystem::::new(); + let mut input_bits: Vec<_> = (0..512).map(|_| Boolean::Constant(false)).collect(); + input_bits[0] = Boolean::Constant(true); + let out = sha256_compression_function( + &mut cs, + &input_bits, + &iv + ).unwrap(); + let out_bits: Vec<_> = out.into_iter().flat_map(|e| e.into_bits_be()).collect(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + + let expected = hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + + let mut out = out_bits.into_iter(); + for b in expected.into_iter() { + for i in (0..8).rev() { + let c = out.next().unwrap().get_value().unwrap(); + + assert_eq!(c, (b >> i) & 1u8 == 1u8); + } + } + } + + #[test] + fn test_full_block() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let iv = get_sha256_iv(); + + let mut cs = TestConstraintSystem::::new(); + let input_bits: Vec<_> = (0..512).map(|i| { + Boolean::from( + AllocatedBit::alloc( + cs.namespace(|| format!("input bit {}", i)), + Some(rng.gen()) + ).unwrap() + ) + }).collect(); + + sha256_compression_function( + cs.namespace(|| "sha256"), + &input_bits, + &iv + ).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints() - 512, 25840); + } + + #[test] + fn test_against_vectors() { + use crypto::sha2::Sha256; + use crypto::digest::Digest; + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for input_len in (0..32).chain((32..256).filter(|a| a % 8 == 0)) + { + let mut h = Sha256::new(); + let data: Vec = (0..input_len).map(|_| rng.gen()).collect(); + h.input(&data); + let mut hash_result = [0u8; 32]; + h.result(&mut hash_result[..]); + + let mut cs = TestConstraintSystem::::new(); + let mut input_bits = vec![]; + + for (byte_i, input_byte) in data.into_iter().enumerate() { + for bit_i in (0..8).rev() { + let cs = cs.namespace(|| format!("input bit {} {}", byte_i, bit_i)); + + input_bits.push(AllocatedBit::alloc(cs, Some((input_byte >> bit_i) & 1u8 == 1u8)).unwrap().into()); + } + } + + let r = sha256(&mut cs, &input_bits).unwrap(); + + assert!(cs.is_satisfied()); + + let mut s = hash_result.as_ref().iter() + .flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8)); + + for b in r { + match b { + Boolean::Is(b) => { + assert!(s.next().unwrap() == b.get_value().unwrap()); + }, + Boolean::Not(b) => { + assert!(s.next().unwrap() != b.get_value().unwrap()); + }, + Boolean::Constant(b) => { + assert!(input_len == 0); + assert!(s.next().unwrap() == b); + } + } + } + } + } +} diff --git a/src/rust/sapling-crypto/src/circuit/sprout/commitment.rs b/src/rust/sapling-crypto/src/circuit/sprout/commitment.rs new file mode 100644 index 000000000..a32f05c30 --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/sprout/commitment.rs @@ -0,0 +1,42 @@ +use pairing::{Engine}; +use bellman::{ConstraintSystem, SynthesisError}; +use circuit::sha256::{ + sha256 +}; +use circuit::boolean::{ + Boolean +}; + +pub fn note_comm( + cs: CS, + a_pk: &[Boolean], + value: &[Boolean], + rho: &[Boolean], + r: &[Boolean] +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + assert_eq!(a_pk.len(), 256); + assert_eq!(value.len(), 64); + assert_eq!(rho.len(), 256); + assert_eq!(r.len(), 256); + + let mut image = vec![]; + image.push(Boolean::constant(true)); + image.push(Boolean::constant(false)); + image.push(Boolean::constant(true)); + image.push(Boolean::constant(true)); + image.push(Boolean::constant(false)); + image.push(Boolean::constant(false)); + image.push(Boolean::constant(false)); + image.push(Boolean::constant(false)); + image.extend(a_pk.iter().cloned()); + image.extend(value.iter().cloned()); + image.extend(rho.iter().cloned()); + image.extend(r.iter().cloned()); + + sha256( + cs, + &image + ) +} diff --git a/src/rust/sapling-crypto/src/circuit/sprout/input.rs b/src/rust/sapling-crypto/src/circuit/sprout/input.rs new file mode 100644 index 000000000..ce69bc00d --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/sprout/input.rs @@ -0,0 +1,226 @@ +use pairing::{Engine}; +use bellman::{ConstraintSystem, SynthesisError}; +use circuit::sha256::{ + sha256_block_no_padding +}; +use circuit::boolean::{ + AllocatedBit, + Boolean +}; + +use super::*; +use super::prfs::*; +use super::commitment::note_comm; + +pub struct InputNote { + pub nf: Vec, + pub mac: Vec, +} + +impl InputNote { + pub fn compute( + mut cs: CS, + a_sk: Option, + rho: Option, + r: Option, + value: &NoteValue, + h_sig: &[Boolean], + nonce: bool, + auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH], + rt: &[Boolean] + ) -> Result + where E: Engine, CS: ConstraintSystem + { + let a_sk = witness_u252( + cs.namespace(|| "a_sk"), + a_sk.as_ref().map(|a_sk| &a_sk.0[..]) + )?; + + let rho = witness_u256( + cs.namespace(|| "rho"), + rho.as_ref().map(|rho| &rho.0[..]) + )?; + + let r = witness_u256( + cs.namespace(|| "r"), + r.as_ref().map(|r| &r.0[..]) + )?; + + let a_pk = prf_a_pk( + cs.namespace(|| "a_pk computation"), + &a_sk + )?; + + let nf = prf_nf( + cs.namespace(|| "nf computation"), + &a_sk, + &rho + )?; + + let mac = prf_pk( + cs.namespace(|| "mac computation"), + &a_sk, + h_sig, + nonce + )?; + + let cm = note_comm( + cs.namespace(|| "cm computation"), + &a_pk, + &value.bits_le(), + &rho, + &r + )?; + + // Witness into the merkle tree + let mut cur = cm.clone(); + + for (i, layer) in auth_path.into_iter().enumerate() { + let cs = &mut cs.namespace(|| format!("layer {}", i)); + + let cur_is_right = AllocatedBit::alloc( + cs.namespace(|| "cur is right"), + layer.as_ref().map(|&(_, p)| p) + )?; + + let lhs = cur; + let rhs = witness_u256( + cs.namespace(|| "sibling"), + layer.as_ref().map(|&(ref sibling, _)| &sibling[..]) + )?; + + // Conditionally swap if cur is right + let preimage = conditionally_swap_u256( + cs.namespace(|| "conditional swap"), + &lhs[..], + &rhs[..], + &cur_is_right + )?; + + cur = sha256_block_no_padding( + cs.namespace(|| "hash of this layer"), + &preimage + )?; + } + + // enforce must be true if the value is nonzero + let enforce = AllocatedBit::alloc( + cs.namespace(|| "enforce"), + value.get_value().map(|n| n != 0) + )?; + + // value * (1 - enforce) = 0 + // If `value` is zero, `enforce` _can_ be zero. + // If `value` is nonzero, `enforce` _must_ be one. + cs.enforce( + || "enforce validity", + |_| value.lc(), + |lc| lc + CS::one() - enforce.get_variable(), + |lc| lc + ); + + assert_eq!(cur.len(), rt.len()); + + // Check that the anchor (exposed as a public input) + // is equal to the merkle tree root that we calculated + // for this note + for (i, (cur, rt)) in cur.into_iter().zip(rt.iter()).enumerate() { + // (cur - rt) * enforce = 0 + // if enforce is zero, cur and rt can be different + // if enforce is one, they must be equal + cs.enforce( + || format!("conditionally enforce correct root for bit {}", i), + |_| cur.lc(CS::one(), E::Fr::one()) - &rt.lc(CS::one(), E::Fr::one()), + |lc| lc + enforce.get_variable(), + |lc| lc + ); + } + + Ok(InputNote { + mac: mac, + nf: nf + }) + } +} + +/// Swaps two 256-bit blobs conditionally, returning the +/// 512-bit concatenation. +pub fn conditionally_swap_u256( + mut cs: CS, + lhs: &[Boolean], + rhs: &[Boolean], + condition: &AllocatedBit +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem, +{ + assert_eq!(lhs.len(), 256); + assert_eq!(rhs.len(), 256); + + let mut new_lhs = vec![]; + let mut new_rhs = vec![]; + + for (i, (lhs, rhs)) in lhs.iter().zip(rhs.iter()).enumerate() { + let cs = &mut cs.namespace(|| format!("bit {}", i)); + + let x = Boolean::from(AllocatedBit::alloc( + cs.namespace(|| "x"), + condition.get_value().and_then(|v| { + if v { + rhs.get_value() + } else { + lhs.get_value() + } + }) + )?); + + // x = (1-condition)lhs + (condition)rhs + // x = lhs - lhs(condition) + rhs(condition) + // x - lhs = condition (rhs - lhs) + // if condition is zero, we don't swap, so + // x - lhs = 0 + // x = lhs + // if condition is one, we do swap, so + // x - lhs = rhs - lhs + // x = rhs + cs.enforce( + || "conditional swap for x", + |lc| lc + &rhs.lc(CS::one(), E::Fr::one()) + - &lhs.lc(CS::one(), E::Fr::one()), + |lc| lc + condition.get_variable(), + |lc| lc + &x.lc(CS::one(), E::Fr::one()) + - &lhs.lc(CS::one(), E::Fr::one()) + ); + + let y = Boolean::from(AllocatedBit::alloc( + cs.namespace(|| "y"), + condition.get_value().and_then(|v| { + if v { + lhs.get_value() + } else { + rhs.get_value() + } + }) + )?); + + // y = (1-condition)rhs + (condition)lhs + // y - rhs = condition (lhs - rhs) + cs.enforce( + || "conditional swap for y", + |lc| lc + &lhs.lc(CS::one(), E::Fr::one()) + - &rhs.lc(CS::one(), E::Fr::one()), + |lc| lc + condition.get_variable(), + |lc| lc + &y.lc(CS::one(), E::Fr::one()) + - &rhs.lc(CS::one(), E::Fr::one()) + ); + + new_lhs.push(x); + new_rhs.push(y); + } + + let mut f = new_lhs; + f.extend(new_rhs); + + assert_eq!(f.len(), 512); + + Ok(f) +} diff --git a/src/rust/sapling-crypto/src/circuit/sprout/mod.rs b/src/rust/sapling-crypto/src/circuit/sprout/mod.rs new file mode 100644 index 000000000..586de8c7d --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/sprout/mod.rs @@ -0,0 +1,488 @@ +use pairing::{Engine, Field}; +use bellman::{ConstraintSystem, SynthesisError, Circuit, LinearCombination}; +use circuit::boolean::{ + AllocatedBit, + Boolean +}; +use circuit::multipack::pack_into_inputs; + +mod prfs; +mod commitment; +mod input; +mod output; + +use self::input::*; +use self::output::*; + +pub const TREE_DEPTH: usize = 29; + +pub struct SpendingKey(pub [u8; 32]); +pub struct PayingKey(pub [u8; 32]); +pub struct UniqueRandomness(pub [u8; 32]); +pub struct CommitmentRandomness(pub [u8; 32]); + +pub struct JoinSplit { + pub vpub_old: Option, + pub vpub_new: Option, + pub h_sig: Option<[u8; 32]>, + pub phi: Option<[u8; 32]>, + pub inputs: Vec, + pub outputs: Vec, + pub rt: Option<[u8; 32]>, +} + +pub struct JSInput { + pub value: Option, + pub a_sk: Option, + pub rho: Option, + pub r: Option, + pub auth_path: [Option<([u8; 32], bool)>; TREE_DEPTH] +} + +pub struct JSOutput { + pub value: Option, + pub a_pk: Option, + pub r: Option +} + +impl Circuit for JoinSplit { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + assert_eq!(self.inputs.len(), 2); + assert_eq!(self.outputs.len(), 2); + + // vpub_old is the value entering the + // JoinSplit from the "outside" value + // pool + let vpub_old = NoteValue::new( + cs.namespace(|| "vpub_old"), + self.vpub_old + )?; + + // vpub_new is the value leaving the + // JoinSplit into the "outside" value + // pool + let vpub_new = NoteValue::new( + cs.namespace(|| "vpub_new"), + self.vpub_new + )?; + + // The left hand side of the balance equation + // vpub_old + inputs[0].value + inputs[1].value + let mut lhs = vpub_old.lc(); + + // The right hand side of the balance equation + // vpub_old + inputs[0].value + inputs[1].value + let mut rhs = vpub_new.lc(); + + // Witness rt (merkle tree root) + let rt = witness_u256( + cs.namespace(|| "rt"), + self.rt.as_ref().map(|v| &v[..]) + ).unwrap(); + + // Witness h_sig + let h_sig = witness_u256( + cs.namespace(|| "h_sig"), + self.h_sig.as_ref().map(|v| &v[..]) + ).unwrap(); + + // Witness phi + let phi = witness_u252( + cs.namespace(|| "phi"), + self.phi.as_ref().map(|v| &v[..]) + ).unwrap(); + + let mut input_notes = vec![]; + let mut lhs_total = self.vpub_old; + + // Iterate over the JoinSplit inputs + for (i, input) in self.inputs.into_iter().enumerate() { + let cs = &mut cs.namespace(|| format!("input {}", i)); + + // Accumulate the value of the left hand side + if let Some(value) = input.value { + lhs_total = lhs_total.map(|v| v.wrapping_add(value)); + } + + // Allocate the value of the note + let value = NoteValue::new( + cs.namespace(|| "value"), + input.value + )?; + + // Compute the nonce (for PRF inputs) which is false + // for the first input, and true for the second input. + let nonce = match i { + 0 => false, + 1 => true, + _ => unreachable!() + }; + + // Perform input note computations + input_notes.push(InputNote::compute( + cs.namespace(|| "note"), + input.a_sk, + input.rho, + input.r, + &value, + &h_sig, + nonce, + input.auth_path, + &rt + )?); + + // Add the note value to the left hand side of + // the balance equation + lhs = lhs + &value.lc(); + } + + // Rebind lhs so that it isn't mutable anymore + let lhs = lhs; + + // See zcash/zcash/issues/854 + { + // Expected sum of the left hand side of the balance + // equation, expressed as a 64-bit unsigned integer + let lhs_total = NoteValue::new( + cs.namespace(|| "total value of left hand side"), + lhs_total + )?; + + // Enforce that the left hand side can be expressed as a 64-bit + // integer + cs.enforce( + || "left hand side can be expressed as a 64-bit unsigned integer", + |_| lhs.clone(), + |lc| lc + CS::one(), + |_| lhs_total.lc() + ); + } + + let mut output_notes = vec![]; + + // Iterate over the JoinSplit outputs + for (i, output) in self.outputs.into_iter().enumerate() { + let cs = &mut cs.namespace(|| format!("output {}", i)); + + let value = NoteValue::new( + cs.namespace(|| "value"), + output.value + )?; + + // Compute the nonce (for PRF inputs) which is false + // for the first output, and true for the second output. + let nonce = match i { + 0 => false, + 1 => true, + _ => unreachable!() + }; + + // Perform output note computations + output_notes.push(OutputNote::compute( + cs.namespace(|| "note"), + output.a_pk, + &value, + output.r, + &phi, + &h_sig, + nonce + )?); + + // Add the note value to the right hand side of + // the balance equation + rhs = rhs + &value.lc(); + } + + // Enforce that balance is equal + cs.enforce( + || "balance equation", + |_| lhs.clone(), + |lc| lc + CS::one(), + |_| rhs + ); + + let mut public_inputs = vec![]; + public_inputs.extend(rt); + public_inputs.extend(h_sig); + + for note in input_notes { + public_inputs.extend(note.nf); + public_inputs.extend(note.mac); + } + + for note in output_notes { + public_inputs.extend(note.cm); + } + + public_inputs.extend(vpub_old.bits_le()); + public_inputs.extend(vpub_new.bits_le()); + + pack_into_inputs(cs.namespace(|| "input packing"), &public_inputs) + } +} + +pub struct NoteValue { + value: Option, + // Least significant digit first + bits: Vec +} + +impl NoteValue { + fn new( + mut cs: CS, + value: Option + ) -> Result + where E: Engine, CS: ConstraintSystem, + { + let mut values; + match value { + Some(mut val) => { + values = vec![]; + for _ in 0..64 { + values.push(Some(val & 1 == 1)); + val >>= 1; + } + }, + None => { + values = vec![None; 64]; + } + } + + let mut bits = vec![]; + for (i, value) in values.into_iter().enumerate() { + bits.push( + AllocatedBit::alloc( + cs.namespace(|| format!("bit {}", i)), + value + )? + ); + } + + Ok(NoteValue { + value: value, + bits: bits + }) + } + + /// Encodes the bits of the value into little-endian + /// byte order. + fn bits_le(&self) -> Vec { + self.bits.chunks(8) + .flat_map(|v| v.iter().rev()) + .cloned() + .map(|e| Boolean::from(e)) + .collect() + } + + /// Computes this value as a linear combination of + /// its bits. + fn lc(&self) -> LinearCombination { + let mut tmp = LinearCombination::zero(); + + let mut coeff = E::Fr::one(); + for b in &self.bits { + tmp = tmp + (coeff, b.get_variable()); + coeff.double(); + } + + tmp + } + + fn get_value(&self) -> Option { + self.value + } +} + +/// Witnesses some bytes in the constraint system, +/// skipping the first `skip_bits`. +fn witness_bits( + mut cs: CS, + value: Option<&[u8]>, + num_bits: usize, + skip_bits: usize +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem, +{ + let bit_values = if let Some(value) = value { + let mut tmp = vec![]; + for b in value.iter() + .flat_map(|&m| (0..8).rev().map(move |i| m >> i & 1 == 1)) + .skip(skip_bits) + { + tmp.push(Some(b)); + } + tmp + } else { + vec![None; num_bits] + }; + assert_eq!(bit_values.len(), num_bits); + + let mut bits = vec![]; + + for (i, value) in bit_values.into_iter().enumerate() { + bits.push(Boolean::from(AllocatedBit::alloc( + cs.namespace(|| format!("bit {}", i)), + value + )?)); + } + + Ok(bits) +} + +fn witness_u256( + cs: CS, + value: Option<&[u8]>, +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem, +{ + witness_bits(cs, value, 256, 0) +} + +fn witness_u252( + cs: CS, + value: Option<&[u8]>, +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem, +{ + witness_bits(cs, value, 252, 4) +} + +#[test] +fn test_sprout_constraints() { + use pairing::bls12_381::{Bls12}; + use ::circuit::test::*; + + use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian}; + + let test_vector = include_bytes!("test_vectors.dat"); + let mut test_vector = &test_vector[..]; + + fn get_u256(mut reader: R) -> [u8; 32] { + let mut result = [0u8; 32]; + + for i in 0..32 { + result[i] = reader.read_u8().unwrap(); + } + + result + } + + while test_vector.len() != 0 { + let mut cs = TestConstraintSystem::::new(); + + let phi = Some(get_u256(&mut test_vector)); + let rt = Some(get_u256(&mut test_vector)); + let h_sig = Some(get_u256(&mut test_vector)); + + let mut inputs = vec![]; + for _ in 0..2 { + test_vector.read_u8().unwrap(); + + let mut auth_path = [None; TREE_DEPTH]; + for i in (0..TREE_DEPTH).rev() { + test_vector.read_u8().unwrap(); + + let sibling = get_u256(&mut test_vector); + + auth_path[i] = Some((sibling, false)); + } + let mut position = test_vector.read_u64::().unwrap(); + for i in 0..TREE_DEPTH { + auth_path[i].as_mut().map(|p| { + p.1 = (position & 1) == 1 + }); + + position >>= 1; + } + + // a_pk + let _ = Some(SpendingKey(get_u256(&mut test_vector))); + let value = Some(test_vector.read_u64::().unwrap()); + let rho = Some(UniqueRandomness(get_u256(&mut test_vector))); + let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); + let a_sk = Some(SpendingKey(get_u256(&mut test_vector))); + + inputs.push( + JSInput { + value: value, + a_sk: a_sk, + rho: rho, + r: r, + auth_path: auth_path + } + ); + } + + let mut outputs = vec![]; + + for _ in 0..2 { + let a_pk = Some(PayingKey(get_u256(&mut test_vector))); + let value = Some(test_vector.read_u64::().unwrap()); + get_u256(&mut test_vector); + let r = Some(CommitmentRandomness(get_u256(&mut test_vector))); + + outputs.push( + JSOutput { + value: value, + a_pk: a_pk, + r: r + } + ); + } + + let vpub_old = Some(test_vector.read_u64::().unwrap()); + let vpub_new = Some(test_vector.read_u64::().unwrap()); + + let nf1 = get_u256(&mut test_vector); + let nf2 = get_u256(&mut test_vector); + + let cm1 = get_u256(&mut test_vector); + let cm2 = get_u256(&mut test_vector); + + let mac1 = get_u256(&mut test_vector); + let mac2 = get_u256(&mut test_vector); + + let js = JoinSplit { + vpub_old: vpub_old, + vpub_new: vpub_new, + h_sig: h_sig, + phi: phi, + inputs: inputs, + outputs: outputs, + rt: rt + }; + + js.synthesize(&mut cs).unwrap(); + + if let Some(s) = cs.which_is_unsatisfied() { + panic!("{:?}", s); + } + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 1989085); + assert_eq!(cs.num_inputs(), 10); + assert_eq!(cs.hash(), "1a228d3c6377130d1778c7885811dc8b8864049cb5af8aff7e6cd46c5bc4b84c"); + + let mut expected_inputs = vec![]; + expected_inputs.extend(rt.unwrap().to_vec()); + expected_inputs.extend(h_sig.unwrap().to_vec()); + expected_inputs.extend(nf1.to_vec()); + expected_inputs.extend(mac1.to_vec()); + expected_inputs.extend(nf2.to_vec()); + expected_inputs.extend(mac2.to_vec()); + expected_inputs.extend(cm1.to_vec()); + expected_inputs.extend(cm2.to_vec()); + expected_inputs.write_u64::(vpub_old.unwrap()).unwrap(); + expected_inputs.write_u64::(vpub_new.unwrap()).unwrap(); + + use circuit::multipack; + + let expected_inputs = multipack::bytes_to_bits(&expected_inputs); + let expected_inputs = multipack::compute_multipacking::(&expected_inputs); + + assert!(cs.verify(&expected_inputs)); + } +} diff --git a/src/rust/sapling-crypto/src/circuit/sprout/output.rs b/src/rust/sapling-crypto/src/circuit/sprout/output.rs new file mode 100644 index 000000000..9cdbf527c --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/sprout/output.rs @@ -0,0 +1,54 @@ +use pairing::{Engine}; +use bellman::{ConstraintSystem, SynthesisError}; +use circuit::boolean::{Boolean}; + +use super::*; +use super::prfs::*; +use super::commitment::note_comm; + +pub struct OutputNote { + pub cm: Vec +} + +impl OutputNote { + pub fn compute<'a, E, CS>( + mut cs: CS, + a_pk: Option, + value: &NoteValue, + r: Option, + phi: &[Boolean], + h_sig: &[Boolean], + nonce: bool + ) -> Result + where E: Engine, CS: ConstraintSystem, + { + let rho = prf_rho( + cs.namespace(|| "rho"), + phi, + h_sig, + nonce + )?; + + let a_pk = witness_u256( + cs.namespace(|| "a_pk"), + a_pk.as_ref().map(|a_pk| &a_pk.0[..]) + )?; + + let r = witness_u256( + cs.namespace(|| "r"), + r.as_ref().map(|r| &r.0[..]) + )?; + + let cm = note_comm( + cs.namespace(|| "cm computation"), + &a_pk, + &value.bits_le(), + &rho, + &r + )?; + + Ok(OutputNote { + cm: cm + }) + } +} diff --git a/src/rust/sapling-crypto/src/circuit/sprout/prfs.rs b/src/rust/sapling-crypto/src/circuit/sprout/prfs.rs new file mode 100644 index 000000000..fff86481d --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/sprout/prfs.rs @@ -0,0 +1,79 @@ +use pairing::{Engine}; +use bellman::{ConstraintSystem, SynthesisError}; +use circuit::sha256::{ + sha256_block_no_padding +}; +use circuit::boolean::{ + Boolean +}; + +fn prf( + cs: CS, + a: bool, + b: bool, + c: bool, + d: bool, + x: &[Boolean], + y: &[Boolean] +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + assert_eq!(x.len(), 252); + assert_eq!(y.len(), 256); + + let mut image = vec![]; + image.push(Boolean::constant(a)); + image.push(Boolean::constant(b)); + image.push(Boolean::constant(c)); + image.push(Boolean::constant(d)); + image.extend(x.iter().cloned()); + image.extend(y.iter().cloned()); + + assert_eq!(image.len(), 512); + + sha256_block_no_padding( + cs, + &image + ) +} + +pub fn prf_a_pk( + cs: CS, + a_sk: &[Boolean] +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + prf(cs, true, true, false, false, a_sk, &(0..256).map(|_| Boolean::constant(false)).collect::>()) +} + +pub fn prf_nf( + cs: CS, + a_sk: &[Boolean], + rho: &[Boolean] +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + prf(cs, true, true, true, false, a_sk, rho) +} + +pub fn prf_pk( + cs: CS, + a_sk: &[Boolean], + h_sig: &[Boolean], + nonce: bool +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + prf(cs, false, nonce, false, false, a_sk, h_sig) +} + +pub fn prf_rho( + cs: CS, + phi: &[Boolean], + h_sig: &[Boolean], + nonce: bool +) -> Result, SynthesisError> + where E: Engine, CS: ConstraintSystem +{ + prf(cs, false, nonce, true, false, phi, h_sig) +} diff --git a/src/rust/sapling-crypto/src/circuit/sprout/test_vectors.dat b/src/rust/sapling-crypto/src/circuit/sprout/test_vectors.dat new file mode 100644 index 0000000000000000000000000000000000000000..1316955771eb17e9a3e11352f1252b6591c151da GIT binary patch literal 10864 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 E1!z(1KmY&$ literal 0 HcmV?d00001 diff --git a/src/rust/sapling-crypto/src/circuit/test/mod.rs b/src/rust/sapling-crypto/src/circuit/test/mod.rs new file mode 100644 index 000000000..23461ebcd --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/test/mod.rs @@ -0,0 +1,492 @@ +use pairing::{ + Engine, + Field, + PrimeField, + PrimeFieldRepr +}; + +use bellman::{ + LinearCombination, + SynthesisError, + ConstraintSystem, + Variable, + Index +}; + +use std::collections::HashMap; +use std::fmt::Write; + +use byteorder::{BigEndian, ByteOrder}; +use std::cmp::Ordering; +use std::collections::BTreeMap; + +use blake2_rfc::blake2s::Blake2s; + +#[derive(Debug)] +enum NamedObject { + Constraint(usize), + Var(Variable), + Namespace +} + +/// Constraint system for testing purposes. +pub struct TestConstraintSystem { + named_objects: HashMap, + current_namespace: Vec, + constraints: Vec<( + LinearCombination, + LinearCombination, + LinearCombination, + String + )>, + inputs: Vec<(E::Fr, String)>, + aux: Vec<(E::Fr, String)> +} + +#[derive(Clone, Copy)] +struct OrderedVariable(Variable); + +impl Eq for OrderedVariable {} +impl PartialEq for OrderedVariable { + fn eq(&self, other: &OrderedVariable) -> bool { + match (self.0.get_unchecked(), other.0.get_unchecked()) { + (Index::Input(ref a), Index::Input(ref b)) => a == b, + (Index::Aux(ref a), Index::Aux(ref b)) => a == b, + _ => false + } + } +} +impl PartialOrd for OrderedVariable { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for OrderedVariable { + fn cmp(&self, other: &Self) -> Ordering { + match (self.0.get_unchecked(), other.0.get_unchecked()) { + (Index::Input(ref a), Index::Input(ref b)) => a.cmp(b), + (Index::Aux(ref a), Index::Aux(ref b)) => a.cmp(b), + (Index::Input(_), Index::Aux(_)) => Ordering::Less, + (Index::Aux(_), Index::Input(_)) => Ordering::Greater + } + } +} + +fn proc_lc( + terms: &[(Variable, E::Fr)], +) -> BTreeMap +{ + let mut map = BTreeMap::new(); + for &(var, coeff) in terms { + map.entry(OrderedVariable(var)) + .or_insert(E::Fr::zero()) + .add_assign(&coeff); + } + + // Remove terms that have a zero coefficient to normalize + let mut to_remove = vec![]; + for (var, coeff) in map.iter() { + if coeff.is_zero() { + to_remove.push(var.clone()) + } + } + + for var in to_remove { + map.remove(&var); + } + + map +} + +fn hash_lc( + terms: &[(Variable, E::Fr)], + h: &mut Blake2s +) +{ + let map = proc_lc::(terms); + + let mut buf = [0u8; 9 + 32]; + BigEndian::write_u64(&mut buf[0..8], map.len() as u64); + h.update(&buf[0..8]); + + for (var, coeff) in map { + match var.0.get_unchecked() { + Index::Input(i) => { + buf[0] = b'I'; + BigEndian::write_u64(&mut buf[1..9], i as u64); + }, + Index::Aux(i) => { + buf[0] = b'A'; + BigEndian::write_u64(&mut buf[1..9], i as u64); + } + } + + coeff.into_repr().write_be(&mut buf[9..]).unwrap(); + + h.update(&buf); + } +} + +fn eval_lc( + terms: &[(Variable, E::Fr)], + inputs: &[(E::Fr, String)], + aux: &[(E::Fr, String)] +) -> E::Fr +{ + let mut acc = E::Fr::zero(); + + for &(var, ref coeff) in terms { + let mut tmp = match var.get_unchecked() { + Index::Input(index) => inputs[index].0, + Index::Aux(index) => aux[index].0 + }; + + tmp.mul_assign(&coeff); + acc.add_assign(&tmp); + } + + acc +} + +impl TestConstraintSystem { + pub fn new() -> TestConstraintSystem { + let mut map = HashMap::new(); + map.insert("ONE".into(), NamedObject::Var(TestConstraintSystem::::one())); + + TestConstraintSystem { + named_objects: map, + current_namespace: vec![], + constraints: vec![], + inputs: vec![(E::Fr::one(), "ONE".into())], + aux: vec![] + } + } + + pub fn pretty_print(&self) -> String { + let mut s = String::new(); + + let negone = { + let mut tmp = E::Fr::one(); + tmp.negate(); + tmp + }; + + let powers_of_two = (0..E::Fr::NUM_BITS).map(|i| { + E::Fr::from_str("2").unwrap().pow(&[i as u64]) + }).collect::>(); + + let pp = |s: &mut String, lc: &LinearCombination| { + write!(s, "(").unwrap(); + let mut is_first = true; + for (var, coeff) in proc_lc::(lc.as_ref()) { + if coeff == negone { + write!(s, " - ").unwrap(); + } else if !is_first { + write!(s, " + ").unwrap(); + } + is_first = false; + + if coeff != E::Fr::one() && coeff != negone { + for (i, x) in powers_of_two.iter().enumerate() { + if x == &coeff { + write!(s, "2^{} . ", i).unwrap(); + break; + } + } + + write!(s, "{} . ", coeff).unwrap(); + } + + match var.0.get_unchecked() { + Index::Input(i) => { + write!(s, "`{}`", &self.inputs[i].1).unwrap(); + }, + Index::Aux(i) => { + write!(s, "`{}`", &self.aux[i].1).unwrap(); + } + } + } + if is_first { + // Nothing was visited, print 0. + write!(s, "0").unwrap(); + } + write!(s, ")").unwrap(); + }; + + for &(ref a, ref b, ref c, ref name) in &self.constraints { + write!(&mut s, "\n").unwrap(); + + write!(&mut s, "{}: ", name).unwrap(); + pp(&mut s, a); + write!(&mut s, " * ").unwrap(); + pp(&mut s, b); + write!(&mut s, " = ").unwrap(); + pp(&mut s, c); + } + + write!(&mut s, "\n").unwrap(); + + s + } + + pub fn hash(&self) -> String { + let mut h = Blake2s::new(32); + { + let mut buf = [0u8; 24]; + + BigEndian::write_u64(&mut buf[0..8], self.inputs.len() as u64); + BigEndian::write_u64(&mut buf[8..16], self.aux.len() as u64); + BigEndian::write_u64(&mut buf[16..24], self.constraints.len() as u64); + h.update(&buf); + } + + for constraint in &self.constraints { + hash_lc::(constraint.0.as_ref(), &mut h); + hash_lc::(constraint.1.as_ref(), &mut h); + hash_lc::(constraint.2.as_ref(), &mut h); + } + + let mut s = String::new(); + for b in h.finalize().as_ref() { + s += &format!("{:02x}", b); + } + + s + } + + pub fn which_is_unsatisfied(&self) -> Option<&str> { + for &(ref a, ref b, ref c, ref path) in &self.constraints { + let mut a = eval_lc::(a.as_ref(), &self.inputs, &self.aux); + let b = eval_lc::(b.as_ref(), &self.inputs, &self.aux); + let c = eval_lc::(c.as_ref(), &self.inputs, &self.aux); + + a.mul_assign(&b); + + if a != c { + return Some(&*path) + } + } + + None + } + + pub fn is_satisfied(&self) -> bool + { + self.which_is_unsatisfied().is_none() + } + + pub fn num_constraints(&self) -> usize + { + self.constraints.len() + } + + pub fn set(&mut self, path: &str, to: E::Fr) + { + match self.named_objects.get(path) { + Some(&NamedObject::Var(ref v)) => { + match v.get_unchecked() { + Index::Input(index) => self.inputs[index].0 = to, + Index::Aux(index) => self.aux[index].0 = to + } + } + Some(e) => panic!("tried to set path `{}` to value, but `{:?}` already exists there.", path, e), + _ => panic!("no variable exists at path: {}", path) + } + } + + pub fn verify(&self, expected: &[E::Fr]) -> bool + { + assert_eq!(expected.len() + 1, self.inputs.len()); + + for (a, b) in self.inputs.iter().skip(1).zip(expected.iter()) + { + if &a.0 != b { + return false + } + } + + return true; + } + + pub fn num_inputs(&self) -> usize { + self.inputs.len() + } + + pub fn get_input(&mut self, index: usize, path: &str) -> E::Fr + { + let (assignment, name) = self.inputs[index].clone(); + + assert_eq!(path, name); + + assignment + } + + pub fn get(&mut self, path: &str) -> E::Fr + { + match self.named_objects.get(path) { + Some(&NamedObject::Var(ref v)) => { + match v.get_unchecked() { + Index::Input(index) => self.inputs[index].0, + Index::Aux(index) => self.aux[index].0 + } + } + Some(e) => panic!("tried to get value of path `{}`, but `{:?}` exists there (not a variable)", path, e), + _ => panic!("no variable exists at path: {}", path) + } + } + + fn set_named_obj(&mut self, path: String, to: NamedObject) { + if self.named_objects.contains_key(&path) { + panic!("tried to create object at existing path: {}", path); + } + + self.named_objects.insert(path, to); + } +} + +fn compute_path(ns: &[String], this: String) -> String { + if this.chars().any(|a| a == '/') { + panic!("'/' is not allowed in names"); + } + + let mut name = String::new(); + + let mut needs_separation = false; + for ns in ns.iter().chain(Some(&this).into_iter()) + { + if needs_separation { + name += "/"; + } + + name += ns; + needs_separation = true; + } + + name +} + +impl ConstraintSystem for TestConstraintSystem { + type Root = Self; + + fn alloc( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + let index = self.aux.len(); + let path = compute_path(&self.current_namespace, annotation().into()); + self.aux.push((f()?, path.clone())); + let var = Variable::new_unchecked(Index::Aux(index)); + self.set_named_obj(path, NamedObject::Var(var)); + + Ok(var) + } + + fn alloc_input( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + let index = self.inputs.len(); + let path = compute_path(&self.current_namespace, annotation().into()); + self.inputs.push((f()?, path.clone())); + let var = Variable::new_unchecked(Index::Input(index)); + self.set_named_obj(path, NamedObject::Var(var)); + + Ok(var) + } + + fn enforce( + &mut self, + annotation: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination + { + let path = compute_path(&self.current_namespace, annotation().into()); + let index = self.constraints.len(); + self.set_named_obj(path.clone(), NamedObject::Constraint(index)); + + let a = a(LinearCombination::zero()); + let b = b(LinearCombination::zero()); + let c = c(LinearCombination::zero()); + + self.constraints.push((a, b, c, path)); + } + + fn push_namespace(&mut self, name_fn: N) + where NR: Into, N: FnOnce() -> NR + { + let name = name_fn().into(); + let path = compute_path(&self.current_namespace, name.clone()); + self.set_named_obj(path.clone(), NamedObject::Namespace); + self.current_namespace.push(name); + } + + fn pop_namespace(&mut self) + { + assert!(self.current_namespace.pop().is_some()); + } + + fn get_root(&mut self) -> &mut Self::Root + { + self + } +} + +#[test] +fn test_cs() { + use pairing::bls12_381::{Bls12, Fr}; + use pairing::PrimeField; + + let mut cs = TestConstraintSystem::::new(); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 0); + let a = cs.namespace(|| "a").alloc(|| "var", || Ok(Fr::from_str("10").unwrap())).unwrap(); + let b = cs.namespace(|| "b").alloc(|| "var", || Ok(Fr::from_str("4").unwrap())).unwrap(); + let c = cs.alloc(|| "product", || Ok(Fr::from_str("40").unwrap())).unwrap(); + + cs.enforce( + || "mult", + |lc| lc + a, + |lc| lc + b, + |lc| lc + c + ); + assert!(cs.is_satisfied()); + assert_eq!(cs.num_constraints(), 1); + + cs.set("a/var", Fr::from_str("4").unwrap()); + + let one = TestConstraintSystem::::one(); + cs.enforce( + || "eq", + |lc| lc + a, + |lc| lc + one, + |lc| lc + b + ); + + assert!(!cs.is_satisfied()); + assert!(cs.which_is_unsatisfied() == Some("mult")); + + assert!(cs.get("product") == Fr::from_str("40").unwrap()); + + cs.set("product", Fr::from_str("16").unwrap()); + assert!(cs.is_satisfied()); + + { + let mut cs = cs.namespace(|| "test1"); + let mut cs = cs.namespace(|| "test2"); + cs.alloc(|| "hehe", || Ok(Fr::one())).unwrap(); + } + + assert!(cs.get("test1/test2/hehe") == Fr::one()); +} diff --git a/src/rust/sapling-crypto/src/circuit/uint32.rs b/src/rust/sapling-crypto/src/circuit/uint32.rs new file mode 100644 index 000000000..fb0bfa922 --- /dev/null +++ b/src/rust/sapling-crypto/src/circuit/uint32.rs @@ -0,0 +1,755 @@ +use pairing::{ + Engine, + Field, + PrimeField +}; + +use bellman::{ + SynthesisError, + ConstraintSystem, + LinearCombination +}; + +use super::boolean::{ + Boolean, + AllocatedBit +}; + +use super::multieq::MultiEq; + +/// Represents an interpretation of 32 `Boolean` objects as an +/// unsigned integer. +#[derive(Clone)] +pub struct UInt32 { + // Least significant bit first + bits: Vec, + value: Option +} + +impl UInt32 { + /// Construct a constant `UInt32` from a `u32` + pub fn constant(value: u32) -> Self + { + let mut bits = Vec::with_capacity(32); + + let mut tmp = value; + for _ in 0..32 { + if tmp & 1 == 1 { + bits.push(Boolean::constant(true)) + } else { + bits.push(Boolean::constant(false)) + } + + tmp >>= 1; + } + + UInt32 { + bits: bits, + value: Some(value) + } + } + + /// Allocate a `UInt32` in the constraint system + pub fn alloc( + mut cs: CS, + value: Option + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let values = match value { + Some(mut val) => { + let mut v = Vec::with_capacity(32); + + for _ in 0..32 { + v.push(Some(val & 1 == 1)); + val >>= 1; + } + + v + }, + None => vec![None; 32] + }; + + let bits = values.into_iter() + .enumerate() + .map(|(i, v)| { + Ok(Boolean::from(AllocatedBit::alloc( + cs.namespace(|| format!("allocated bit {}", i)), + v + )?)) + }) + .collect::, SynthesisError>>()?; + + Ok(UInt32 { + bits: bits, + value: value + }) + } + + pub fn into_bits_be(&self) -> Vec { + self.bits.iter().rev().cloned().collect() + } + + pub fn from_bits_be(bits: &[Boolean]) -> Self { + assert_eq!(bits.len(), 32); + + let mut value = Some(0u32); + for b in bits { + value.as_mut().map(|v| *v <<= 1); + + match b.get_value() { + Some(true) => { value.as_mut().map(|v| *v |= 1); }, + Some(false) => {}, + None => { value = None; } + } + } + + UInt32 { + value: value, + bits: bits.iter().rev().cloned().collect() + } + } + + + /// Turns this `UInt32` into its little-endian byte order representation. + pub fn into_bits(&self) -> Vec { + self.bits.clone() + } + + /// Converts a little-endian byte order representation of bits into a + /// `UInt32`. + pub fn from_bits(bits: &[Boolean]) -> Self + { + assert_eq!(bits.len(), 32); + + let new_bits = bits.to_vec(); + + let mut value = Some(0u32); + for b in new_bits.iter().rev() { + value.as_mut().map(|v| *v <<= 1); + + match b { + &Boolean::Constant(b) => { + if b { + value.as_mut().map(|v| *v |= 1); + } + }, + &Boolean::Is(ref b) => { + match b.get_value() { + Some(true) => { value.as_mut().map(|v| *v |= 1); }, + Some(false) => {}, + None => { value = None } + } + }, + &Boolean::Not(ref b) => { + match b.get_value() { + Some(false) => { value.as_mut().map(|v| *v |= 1); }, + Some(true) => {}, + None => { value = None } + } + } + } + } + + UInt32 { + value: value, + bits: new_bits + } + } + + pub fn rotr(&self, by: usize) -> Self { + let by = by % 32; + + let new_bits = self.bits.iter() + .skip(by) + .chain(self.bits.iter()) + .take(32) + .cloned() + .collect(); + + UInt32 { + bits: new_bits, + value: self.value.map(|v| v.rotate_right(by as u32)) + } + } + + pub fn shr(&self, by: usize) -> Self { + let by = by % 32; + + let fill = Boolean::constant(false); + + let new_bits = self.bits + .iter() // The bits are least significant first + .skip(by) // Skip the bits that will be lost during the shift + .chain(Some(&fill).into_iter().cycle()) // Rest will be zeros + .take(32) // Only 32 bits needed! + .cloned() + .collect(); + + UInt32 { + bits: new_bits, + value: self.value.map(|v| v >> by as u32) + } + } + + fn triop( + mut cs: CS, + a: &Self, + b: &Self, + c: &Self, + tri_fn: F, + circuit_fn: U + ) -> Result + where E: Engine, + CS: ConstraintSystem, + F: Fn(u32, u32, u32) -> u32, + U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result + { + let new_value = match (a.value, b.value, c.value) { + (Some(a), Some(b), Some(c)) => { + Some(tri_fn(a, b, c)) + }, + _ => None + }; + + let bits = a.bits.iter() + .zip(b.bits.iter()) + .zip(c.bits.iter()) + .enumerate() + .map(|(i, ((a, b), c))| circuit_fn(&mut cs, i, a, b, c)) + .collect::>()?; + + Ok(UInt32 { + bits: bits, + value: new_value + }) + } + + /// Compute the `maj` value (a and b) xor (a and c) xor (b and c) + /// during SHA256. + pub fn sha256_maj( + cs: CS, + a: &Self, + b: &Self, + c: &Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ (a & c) ^ (b & c), + |cs, i, a, b, c| { + Boolean::sha256_maj( + cs.namespace(|| format!("maj {}", i)), + a, + b, + c + ) + } + ) + } + + /// Compute the `ch` value `(a and b) xor ((not a) and c)` + /// during SHA256. + pub fn sha256_ch( + cs: CS, + a: &Self, + b: &Self, + c: &Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + Self::triop(cs, a, b, c, |a, b, c| (a & b) ^ ((!a) & c), + |cs, i, a, b, c| { + Boolean::sha256_ch( + cs.namespace(|| format!("ch {}", i)), + a, + b, + c + ) + } + ) + } + + /// XOR this `UInt32` with another `UInt32` + pub fn xor( + &self, + mut cs: CS, + other: &Self + ) -> Result + where E: Engine, + CS: ConstraintSystem + { + let new_value = match (self.value, other.value) { + (Some(a), Some(b)) => { + Some(a ^ b) + }, + _ => None + }; + + let bits = self.bits.iter() + .zip(other.bits.iter()) + .enumerate() + .map(|(i, (a, b))| { + Boolean::xor( + cs.namespace(|| format!("xor of bit {}", i)), + a, + b + ) + }) + .collect::>()?; + + Ok(UInt32 { + bits: bits, + value: new_value + }) + } + + /// Perform modular addition of several `UInt32` objects. + pub fn addmany( + mut cs: M, + operands: &[Self] + ) -> Result + where E: Engine, + CS: ConstraintSystem, + M: ConstraintSystem> + { + // Make some arbitrary bounds for ourselves to avoid overflows + // in the scalar field + assert!(E::Fr::NUM_BITS >= 64); + assert!(operands.len() >= 2); // Weird trivial cases that should never happen + assert!(operands.len() <= 10); + + // Compute the maximum value of the sum so we allocate enough bits for + // the result + let mut max_value = (operands.len() as u64) * (u32::max_value() as u64); + + // Keep track of the resulting value + let mut result_value = Some(0u64); + + // This is a linear combination that we will enforce to equal the + // output + let mut lc = LinearCombination::zero(); + + let mut all_constants = true; + + // Iterate over the operands + for op in operands { + // Accumulate the value + match op.value { + Some(val) => { + result_value.as_mut().map(|v| *v += val as u64); + }, + None => { + // If any of our operands have unknown value, we won't + // know the value of the result + result_value = None; + } + } + + // Iterate over each bit of the operand and add the operand to + // the linear combination + let mut coeff = E::Fr::one(); + for bit in &op.bits { + lc = lc + &bit.lc(CS::one(), coeff); + + all_constants &= bit.is_constant(); + + coeff.double(); + } + } + + // The value of the actual result is modulo 2^32 + let modular_value = result_value.map(|v| v as u32); + + if all_constants && modular_value.is_some() { + // We can just return a constant, rather than + // unpacking the result into allocated bits. + + return Ok(UInt32::constant(modular_value.unwrap())); + } + + // Storage area for the resulting bits + let mut result_bits = vec![]; + + // Linear combination representing the output, + // for comparison with the sum of the operands + let mut result_lc = LinearCombination::zero(); + + // Allocate each bit of the result + let mut coeff = E::Fr::one(); + let mut i = 0; + while max_value != 0 { + // Allocate the bit + let b = AllocatedBit::alloc( + cs.namespace(|| format!("result bit {}", i)), + result_value.map(|v| (v >> i) & 1 == 1) + )?; + + // Add this bit to the result combination + result_lc = result_lc + (coeff, b.get_variable()); + + result_bits.push(b.into()); + + max_value >>= 1; + i += 1; + coeff.double(); + } + + // Enforce equality between the sum and result + cs.get_root().enforce_equal(i, &lc, &result_lc); + + // Discard carry bits that we don't care about + result_bits.truncate(32); + + Ok(UInt32 { + bits: result_bits, + value: modular_value + }) + } +} + +#[cfg(test)] +mod test { + use rand::{XorShiftRng, SeedableRng, Rng}; + use ::circuit::boolean::{Boolean}; + use super::{UInt32}; + use pairing::bls12_381::{Bls12}; + use pairing::{Field}; + use ::circuit::test::*; + use bellman::{ConstraintSystem}; + use circuit::multieq::MultiEq; + + #[test] + fn test_uint32_from_bits_be() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::>(); + + let b = UInt32::from_bits_be(&v); + + for (i, bit) in b.bits.iter().enumerate() { + match bit { + &Boolean::Constant(bit) => { + assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); + }, + _ => unreachable!() + } + } + + let expected_to_be_same = b.into_bits_be(); + + for x in v.iter().zip(expected_to_be_same.iter()) + { + match x { + (&Boolean::Constant(true), &Boolean::Constant(true)) => {}, + (&Boolean::Constant(false), &Boolean::Constant(false)) => {}, + _ => unreachable!() + } + } + } + } + + #[test] + fn test_uint32_from_bits() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let mut v = (0..32).map(|_| Boolean::constant(rng.gen())).collect::>(); + + let b = UInt32::from_bits(&v); + + for (i, bit) in b.bits.iter().enumerate() { + match bit { + &Boolean::Constant(bit) => { + assert!(bit == ((b.value.unwrap() >> i) & 1 == 1)); + }, + _ => unreachable!() + } + } + + let expected_to_be_same = b.into_bits(); + + for x in v.iter().zip(expected_to_be_same.iter()) + { + match x { + (&Boolean::Constant(true), &Boolean::Constant(true)) => {}, + (&Boolean::Constant(false), &Boolean::Constant(false)) => {}, + _ => unreachable!() + } + } + } + } + + #[test] + fn test_uint32_xor() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u32 = rng.gen(); + let b: u32 = rng.gen(); + let c: u32 = rng.gen(); + + let mut expected = a ^ b ^ c; + + let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); + + let r = a_bit.xor(cs.namespace(|| "first xor"), &b_bit).unwrap(); + let r = r.xor(cs.namespace(|| "second xor"), &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match b { + &Boolean::Is(ref b) => { + assert!(b.get_value().unwrap() == (expected & 1 == 1)); + }, + &Boolean::Not(ref b) => { + assert!(!b.get_value().unwrap() == (expected & 1 == 1)); + }, + &Boolean::Constant(b) => { + assert!(b == (expected & 1 == 1)); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_addmany_constants() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u32 = rng.gen(); + let b: u32 = rng.gen(); + let c: u32 = rng.gen(); + + let a_bit = UInt32::constant(a); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::constant(c); + + let mut expected = a.wrapping_add(b).wrapping_add(c); + + let r = { + let mut cs = MultiEq::new(&mut cs); + let r = UInt32::addmany(cs.namespace(|| "addition"), &[a_bit, b_bit, c_bit]).unwrap(); + r + }; + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match b { + &Boolean::Is(_) => panic!(), + &Boolean::Not(_) => panic!(), + &Boolean::Constant(b) => { + assert!(b == (expected & 1 == 1)); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_addmany() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u32 = rng.gen(); + let b: u32 = rng.gen(); + let c: u32 = rng.gen(); + let d: u32 = rng.gen(); + + let mut expected = (a ^ b).wrapping_add(c).wrapping_add(d); + + let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::constant(c); + let d_bit = UInt32::alloc(cs.namespace(|| "d_bit"), Some(d)).unwrap(); + + let r = a_bit.xor(cs.namespace(|| "xor"), &b_bit).unwrap(); + let r = { + let mut cs = MultiEq::new(&mut cs); + let r = UInt32::addmany(cs.namespace(|| "addition"), &[r, c_bit, d_bit]).unwrap(); + r + }; + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match b { + &Boolean::Is(ref b) => { + assert!(b.get_value().unwrap() == (expected & 1 == 1)); + }, + &Boolean::Not(ref b) => { + assert!(!b.get_value().unwrap() == (expected & 1 == 1)); + }, + &Boolean::Constant(_) => { + unreachable!() + } + } + + expected >>= 1; + } + + // Flip a bit and see if the addition constraint still works + if cs.get("addition/result bit 0/boolean").is_zero() { + cs.set("addition/result bit 0/boolean", Field::one()); + } else { + cs.set("addition/result bit 0/boolean", Field::zero()); + } + + assert!(!cs.is_satisfied()); + } + } + + #[test] + fn test_uint32_rotr() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut num = rng.gen(); + + let a = UInt32::constant(num); + + for i in 0..32 { + let b = a.rotr(i); + assert_eq!(a.bits.len(), b.bits.len()); + + assert!(b.value.unwrap() == num); + + let mut tmp = num; + for b in &b.bits { + match b { + &Boolean::Constant(b) => { + assert_eq!(b, tmp & 1 == 1); + }, + _ => unreachable!() + } + + tmp >>= 1; + } + + num = num.rotate_right(1); + } + } + + #[test] + fn test_uint32_shr() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..50 { + for i in 0..60 { + let num = rng.gen(); + let a = UInt32::constant(num).shr(i); + let b = UInt32::constant(num >> i); + + assert_eq!(a.value.unwrap(), num >> i); + + assert_eq!(a.bits.len(), b.bits.len()); + for (a, b) in a.bits.iter().zip(b.bits.iter()) { + assert_eq!(a.get_value().unwrap(), b.get_value().unwrap()); + } + } + } + } + + #[test] + fn test_uint32_sha256_maj() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u32 = rng.gen(); + let b: u32 = rng.gen(); + let c: u32 = rng.gen(); + + let mut expected = (a & b) ^ (a & c) ^ (b & c); + + let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); + + let r = UInt32::sha256_maj(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match b { + &Boolean::Is(ref b) => { + assert!(b.get_value().unwrap() == (expected & 1 == 1)); + }, + &Boolean::Not(ref b) => { + assert!(!b.get_value().unwrap() == (expected & 1 == 1)); + }, + &Boolean::Constant(b) => { + assert!(b == (expected & 1 == 1)); + } + } + + expected >>= 1; + } + } + } + + #[test] + fn test_uint32_sha256_ch() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0653]); + + for _ in 0..1000 { + let mut cs = TestConstraintSystem::::new(); + + let a: u32 = rng.gen(); + let b: u32 = rng.gen(); + let c: u32 = rng.gen(); + + let mut expected = (a & b) ^ ((!a) & c); + + let a_bit = UInt32::alloc(cs.namespace(|| "a_bit"), Some(a)).unwrap(); + let b_bit = UInt32::constant(b); + let c_bit = UInt32::alloc(cs.namespace(|| "c_bit"), Some(c)).unwrap(); + + let r = UInt32::sha256_ch(&mut cs, &a_bit, &b_bit, &c_bit).unwrap(); + + assert!(cs.is_satisfied()); + + assert!(r.value == Some(expected)); + + for b in r.bits.iter() { + match b { + &Boolean::Is(ref b) => { + assert!(b.get_value().unwrap() == (expected & 1 == 1)); + }, + &Boolean::Not(ref b) => { + assert!(!b.get_value().unwrap() == (expected & 1 == 1)); + }, + &Boolean::Constant(b) => { + assert!(b == (expected & 1 == 1)); + } + } + + expected >>= 1; + } + } + } +} diff --git a/src/rust/sapling-crypto/src/constants.rs b/src/rust/sapling-crypto/src/constants.rs new file mode 100644 index 000000000..dfdd36ff7 --- /dev/null +++ b/src/rust/sapling-crypto/src/constants.rs @@ -0,0 +1,40 @@ +/// First 64 bytes of the BLAKE2s input during group hash. +/// This is chosen to be some random string that we couldn't have anticipated when we designed +/// the algorithm, for rigidity purposes. +/// We deliberately use an ASCII hex string of 32 bytes here. +pub const GH_FIRST_BLOCK: &'static [u8; 64] + = b"096b36a5804bfacef1691e173c366a47ff5ba84a44f26ddd7e8d9f79d5b42df0"; + +// BLAKE2s invocation personalizations +/// BLAKE2s Personalization for CRH^ivk = BLAKE2s(ak | nk) +pub const CRH_IVK_PERSONALIZATION: &'static [u8; 8] + = b"Zcashivk"; + +/// BLAKE2s Personalization for PRF^nf = BLAKE2s(nk | rho) +pub const PRF_NF_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_nf"; + +// Group hash personalizations +/// BLAKE2s Personalization for Pedersen hash generators. +pub const PEDERSEN_HASH_GENERATORS_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_PH"; + +/// BLAKE2s Personalization for the group hash for key diversification +pub const KEY_DIVERSIFICATION_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_gd"; + +/// BLAKE2s Personalization for the spending key base point +pub const SPENDING_KEY_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_G_"; + +/// BLAKE2s Personalization for the proof generation key base point +pub const PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_H_"; + +/// BLAKE2s Personalization for the value commitment generator for the value +pub const VALUE_COMMITMENT_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_cv"; + +/// BLAKE2s Personalization for the nullifier position generator (for computing rho) +pub const NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION: &'static [u8; 8] + = b"Zcash_J_"; diff --git a/src/rust/sapling-crypto/src/group_hash.rs b/src/rust/sapling-crypto/src/group_hash.rs new file mode 100644 index 000000000..25e65f90e --- /dev/null +++ b/src/rust/sapling-crypto/src/group_hash.rs @@ -0,0 +1,46 @@ +use jubjub::{ + JubjubEngine, + PrimeOrder, + edwards +}; + +use pairing::{ + PrimeField +}; + +use blake2_rfc::blake2s::Blake2s; +use constants; + +/// Produces a random point in the Jubjub curve. +/// The point is guaranteed to be prime order +/// and not the identity. +pub fn group_hash( + tag: &[u8], + personalization: &[u8], + params: &E::Params +) -> Option> +{ + assert_eq!(personalization.len(), 8); + + // Check to see that scalar field is 255 bits + assert!(E::Fr::NUM_BITS == 255); + + let mut h = Blake2s::with_params(32, &[], &[], personalization); + h.update(constants::GH_FIRST_BLOCK); + h.update(tag); + let h = h.finalize().as_ref().to_vec(); + assert!(h.len() == 32); + + match edwards::Point::::read(&h[..], params) { + Ok(p) => { + let p = p.mul_by_cofactor(params); + + if p != edwards::Point::zero() { + Some(p) + } else { + None + } + }, + Err(_) => None + } +} diff --git a/src/rust/sapling-crypto/src/jubjub/edwards.rs b/src/rust/sapling-crypto/src/jubjub/edwards.rs new file mode 100644 index 000000000..e91455c81 --- /dev/null +++ b/src/rust/sapling-crypto/src/jubjub/edwards.rs @@ -0,0 +1,523 @@ +use pairing::{ + Field, + SqrtField, + PrimeField, + PrimeFieldRepr, + BitIterator +}; + +use super::{ + JubjubEngine, + JubjubParams, + Unknown, + PrimeOrder, + montgomery +}; + +use rand::{ + Rng +}; + +use std::marker::PhantomData; + +use std::io::{ + self, + Write, + Read +}; + +// Represents the affine point (X/Z, Y/Z) via the extended +// twisted Edwards coordinates. +// +// See "Twisted Edwards Curves Revisited" +// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson +pub struct Point { + x: E::Fr, + y: E::Fr, + t: E::Fr, + z: E::Fr, + _marker: PhantomData +} + +fn convert_subgroup(from: &Point) -> Point +{ + Point { + x: from.x, + y: from.y, + t: from.t, + z: from.z, + _marker: PhantomData + } +} + +impl From> for Point +{ + fn from(p: Point) -> Point + { + convert_subgroup(&p) + } +} + +impl Clone for Point +{ + fn clone(&self) -> Self { + convert_subgroup(self) + } +} + +impl PartialEq for Point { + fn eq(&self, other: &Point) -> bool { + // p1 = (x1/z1, y1/z1) + // p2 = (x2/z2, y2/z2) + // Deciding that these two points are equal is a matter of + // determining that x1/z1 = x2/z2, or equivalently that + // x1*z2 = x2*z1, and similarly for y. + + let mut x1 = self.x; + x1.mul_assign(&other.z); + + let mut y1 = self.y; + y1.mul_assign(&other.z); + + let mut x2 = other.x; + x2.mul_assign(&self.z); + + let mut y2 = other.y; + y2.mul_assign(&self.z); + + x1 == x2 && y1 == y2 + } +} + +impl Point { + pub fn read( + reader: R, + params: &E::Params + ) -> io::Result + { + let mut y_repr = ::Repr::default(); + y_repr.read_le(reader)?; + + let x_sign = (y_repr.as_ref()[3] >> 63) == 1; + y_repr.as_mut()[3] &= 0x7fffffffffffffff; + + match E::Fr::from_repr(y_repr) { + Ok(y) => { + match Self::get_for_y(y, x_sign, params) { + Some(p) => Ok(p), + None => { + Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")) + } + } + }, + Err(_) => { + Err(io::Error::new(io::ErrorKind::InvalidInput, "y is not in field")) + } + } + } + + pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option + { + // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) + // This is defined for all valid y-coordinates, + // as dy^2 + 1 = 0 has no solution in Fr. + + // tmp1 = y^2 + let mut tmp1 = y; + tmp1.square(); + + // tmp2 = (y^2 * d) + 1 + let mut tmp2 = tmp1; + tmp2.mul_assign(params.edwards_d()); + tmp2.add_assign(&E::Fr::one()); + + // tmp1 = y^2 - 1 + tmp1.sub_assign(&E::Fr::one()); + + match tmp2.inverse() { + Some(tmp2) => { + // tmp1 = (y^2 - 1) / (dy^2 + 1) + tmp1.mul_assign(&tmp2); + + match tmp1.sqrt() { + Some(mut x) => { + if x.into_repr().is_odd() != sign { + x.negate(); + } + + let mut t = x; + t.mul_assign(&y); + + Some(Point { + x: x, + y: y, + t: t, + z: E::Fr::one(), + _marker: PhantomData + }) + }, + None => None + } + }, + None => None + } + } + + /// This guarantees the point is in the prime order subgroup + #[must_use] + pub fn mul_by_cofactor(&self, params: &E::Params) -> Point + { + let tmp = self.double(params) + .double(params) + .double(params); + + convert_subgroup(&tmp) + } + + pub fn rand(rng: &mut R, params: &E::Params) -> Self + { + loop { + let y: E::Fr = rng.gen(); + + if let Some(p) = Self::get_for_y(y, rng.gen(), params) { + return p; + } + } + } +} + +impl Point { + pub fn write( + &self, + writer: W + ) -> io::Result<()> + { + let (x, y) = self.into_xy(); + + assert_eq!(E::Fr::NUM_BITS, 255); + + let x_repr = x.into_repr(); + let mut y_repr = y.into_repr(); + if x_repr.is_odd() { + y_repr.as_mut()[3] |= 0x8000000000000000u64; + } + + y_repr.write_le(writer) + } + + /// Convert from a Montgomery point + pub fn from_montgomery( + m: &montgomery::Point, + params: &E::Params + ) -> Self + { + match m.into_xy() { + None => { + // Map the point at infinity to the neutral element. + Point::zero() + }, + Some((x, y)) => { + // The map from a Montgomery curve is defined as: + // (x, y) -> (u, v) where + // u = x / y + // v = (x - 1) / (x + 1) + // + // This map is not defined for y = 0 and x = -1. + // + // y = 0 is a valid point only for x = 0: + // y^2 = x^3 + A.x^2 + x + // 0 = x^3 + A.x^2 + x + // 0 = x(x^2 + A.x + 1) + // We have: x = 0 OR x^2 + A.x + 1 = 0 + // x^2 + A.x + 1 = 0 + // (2.x + A)^2 = A^2 - 4 (Complete the square.) + // The left hand side is a square, and so if A^2 - 4 + // is nonsquare, there is no solution. Indeed, A^2 - 4 + // is nonsquare. + // + // (0, 0) is a point of order 2, and so we map it to + // (0, -1) in the twisted Edwards curve, which is the + // only point of order 2 that is not the neutral element. + if y.is_zero() { + // This must be the point (0, 0) as above. + let mut neg1 = E::Fr::one(); + neg1.negate(); + + Point { + x: E::Fr::zero(), + y: neg1, + t: E::Fr::zero(), + z: E::Fr::one(), + _marker: PhantomData + } + } else { + // Otherwise, as stated above, the mapping is still + // not defined at x = -1. However, x = -1 is not + // on the curve when A - 2 is nonsquare: + // y^2 = x^3 + A.x^2 + x + // y^2 = (-1) + A + (-1) + // y^2 = A - 2 + // Indeed, A - 2 is nonsquare. + // + // We need to map into (projective) extended twisted + // Edwards coordinates (X, Y, T, Z) which represents + // the point (X/Z, Y/Z) with Z nonzero and T = XY/Z. + // + // Thus, we compute... + // + // u = x(x + 1) + // v = y(x - 1) + // t = x(x - 1) + // z = y(x + 1) (Cannot be nonzero, as above.) + // + // ... which represents the point ( x / y , (x - 1) / (x + 1) ) + // as required by the mapping and preserves the property of + // the auxiliary coordinate t. + // + // We need to scale the coordinate, so u and t will have + // an extra factor s. + + // u = xs + let mut u = x; + u.mul_assign(params.scale()); + + // v = x - 1 + let mut v = x; + v.sub_assign(&E::Fr::one()); + + // t = xs(x - 1) + let mut t = u; + t.mul_assign(&v); + + // z = (x + 1) + let mut z = x; + z.add_assign(&E::Fr::one()); + + // u = xs(x + 1) + u.mul_assign(&z); + + // z = y(x + 1) + z.mul_assign(&y); + + // v = y(x - 1) + v.mul_assign(&y); + + Point { + x: u, + y: v, + t: t, + z: z, + _marker: PhantomData + } + } + } + } + } + + /// Attempts to cast this as a prime order element, failing if it's + /// not in the prime order subgroup. + pub fn as_prime_order(&self, params: &E::Params) -> Option> { + if self.mul(E::Fs::char(), params) == Point::zero() { + Some(convert_subgroup(self)) + } else { + None + } + } + + pub fn zero() -> Self { + Point { + x: E::Fr::zero(), + y: E::Fr::one(), + t: E::Fr::zero(), + z: E::Fr::one(), + _marker: PhantomData + } + } + + pub fn into_xy(&self) -> (E::Fr, E::Fr) + { + let zinv = self.z.inverse().unwrap(); + + let mut x = self.x; + x.mul_assign(&zinv); + + let mut y = self.y; + y.mul_assign(&zinv); + + (x, y) + } + + #[must_use] + pub fn negate(&self) -> Self { + let mut p = self.clone(); + + p.x.negate(); + p.t.negate(); + + p + } + + #[must_use] + pub fn double(&self, _: &E::Params) -> Self { + // See "Twisted Edwards Curves Revisited" + // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson + // Section 3.3 + // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd + + // A = X1^2 + let mut a = self.x; + a.square(); + + // B = Y1^2 + let mut b = self.y; + b.square(); + + // C = 2*Z1^2 + let mut c = self.z; + c.square(); + c.double(); + + // D = a*A + // = -A + let mut d = a; + d.negate(); + + // E = (X1+Y1)^2 - A - B + let mut e = self.x; + e.add_assign(&self.y); + e.square(); + e.add_assign(&d); // -A = D + e.sub_assign(&b); + + // G = D+B + let mut g = d; + g.add_assign(&b); + + // F = G-C + let mut f = g; + f.sub_assign(&c); + + // H = D-B + let mut h = d; + h.sub_assign(&b); + + // X3 = E*F + let mut x3 = e; + x3.mul_assign(&f); + + // Y3 = G*H + let mut y3 = g; + y3.mul_assign(&h); + + // T3 = E*H + let mut t3 = e; + t3.mul_assign(&h); + + // Z3 = F*G + let mut z3 = f; + z3.mul_assign(&g); + + Point { + x: x3, + y: y3, + t: t3, + z: z3, + _marker: PhantomData + } + } + + #[must_use] + pub fn add(&self, other: &Self, params: &E::Params) -> Self + { + // See "Twisted Edwards Curves Revisited" + // Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson + // 3.1 Unified Addition in E^e + + // A = x1 * x2 + let mut a = self.x; + a.mul_assign(&other.x); + + // B = y1 * y2 + let mut b = self.y; + b.mul_assign(&other.y); + + // C = d * t1 * t2 + let mut c = params.edwards_d().clone(); + c.mul_assign(&self.t); + c.mul_assign(&other.t); + + // D = z1 * z2 + let mut d = self.z; + d.mul_assign(&other.z); + + // H = B - aA + // = B + A + let mut h = b; + h.add_assign(&a); + + // E = (x1 + y1) * (x2 + y2) - A - B + // = (x1 + y1) * (x2 + y2) - H + let mut e = self.x; + e.add_assign(&self.y); + { + let mut tmp = other.x; + tmp.add_assign(&other.y); + e.mul_assign(&tmp); + } + e.sub_assign(&h); + + // F = D - C + let mut f = d; + f.sub_assign(&c); + + // G = D + C + let mut g = d; + g.add_assign(&c); + + // x3 = E * F + let mut x3 = e; + x3.mul_assign(&f); + + // y3 = G * H + let mut y3 = g; + y3.mul_assign(&h); + + // t3 = E * H + let mut t3 = e; + t3.mul_assign(&h); + + // z3 = F * G + let mut z3 = f; + z3.mul_assign(&g); + + Point { + x: x3, + y: y3, + t: t3, + z: z3, + _marker: PhantomData + } + } + + #[must_use] + pub fn mul::Repr>>( + &self, + scalar: S, + params: &E::Params + ) -> Self + { + // Standard double-and-add scalar multiplication + + let mut res = Self::zero(); + + for b in BitIterator::new(scalar.into()) { + res = res.double(params); + + if b { + res = res.add(self, params); + } + } + + res + } +} diff --git a/src/rust/sapling-crypto/src/jubjub/fs.rs b/src/rust/sapling-crypto/src/jubjub/fs.rs new file mode 100644 index 000000000..eb10e6505 --- /dev/null +++ b/src/rust/sapling-crypto/src/jubjub/fs.rs @@ -0,0 +1,1232 @@ +use byteorder::{ByteOrder, LittleEndian}; +use pairing::{BitIterator, Field, PrimeField, SqrtField, PrimeFieldRepr, PrimeFieldDecodingError, LegendreSymbol}; +use pairing::LegendreSymbol::*; +use pairing::{adc, sbb, mac_with_carry}; + +use super::ToUniform; + +// s = 6554484396890773809930967563523245729705921265872317281365359162392183254199 +const MODULUS: FsRepr = FsRepr([0xd0970e5ed6f72cb7, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9]); + +// The number of bits needed to represent the modulus. +const MODULUS_BITS: u32 = 252; + +// The number of bits that must be shaved from the beginning of +// the representation when randomly sampling. +const REPR_SHAVE_BITS: u32 = 4; + +// R = 2**256 % s +const R: FsRepr = FsRepr([0x25f80bb3b99607d9, 0xf315d62f66b6e750, 0x932514eeeb8814f4, 0x9a6fc6f479155c6]); + +// R2 = R^2 % s +const R2: FsRepr = FsRepr([0x67719aa495e57731, 0x51b0cef09ce3fc26, 0x69dab7fac026e9a5, 0x4f6547b8d127688]); + +// INV = -(s^{-1} mod 2^64) mod s +const INV: u64 = 0x1ba3a358ef788ef9; + +// GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) +const GENERATOR: FsRepr = FsRepr([0x720b1b19d49ea8f1, 0xbf4aa36101f13a58, 0x5fa8cc968193ccbb, 0xe70cbdc7dccf3ac]); + +// 2^S * t = MODULUS - 1 with t odd +const S: u32 = 1; + +// 2^S root of unity computed by GENERATOR^t +const ROOT_OF_UNITY: FsRepr = FsRepr([0xaa9f02ab1d6124de, 0xb3524a6466112932, 0x7342261215ac260b, 0x4d6b87b1da259e2]); + +// -((2**256) mod s) mod s +const NEGATIVE_ONE: Fs = Fs(FsRepr([0xaa9f02ab1d6124de, 0xb3524a6466112932, 0x7342261215ac260b, 0x4d6b87b1da259e2])); + +/// This is the underlying representation of an element of `Fs`. +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)] +pub struct FsRepr(pub [u64; 4]); + +impl ::rand::Rand for FsRepr { + #[inline(always)] + fn rand(rng: &mut R) -> Self { + FsRepr(rng.gen()) + } +} + +impl ::std::fmt::Display for FsRepr +{ + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + try!(write!(f, "0x")); + for i in self.0.iter().rev() { + try!(write!(f, "{:016x}", *i)); + } + + Ok(()) + } +} + +impl AsRef<[u64]> for FsRepr { + #[inline(always)] + fn as_ref(&self) -> &[u64] { + &self.0 + } +} + +impl AsMut<[u64]> for FsRepr { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u64] { + &mut self.0 + } +} + +impl From for FsRepr { + #[inline(always)] + fn from(val: u64) -> FsRepr { + let mut repr = Self::default(); + repr.0[0] = val; + repr + } +} + +impl Ord for FsRepr { + #[inline(always)] + fn cmp(&self, other: &FsRepr) -> ::std::cmp::Ordering { + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if a < b { + return ::std::cmp::Ordering::Less + } else if a > b { + return ::std::cmp::Ordering::Greater + } + } + + ::std::cmp::Ordering::Equal + } +} + +impl PartialOrd for FsRepr { + #[inline(always)] + fn partial_cmp(&self, other: &FsRepr) -> Option<::std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl PrimeFieldRepr for FsRepr { + #[inline(always)] + fn is_odd(&self) -> bool { + self.0[0] & 1 == 1 + } + + #[inline(always)] + fn is_even(&self) -> bool { + !self.is_odd() + } + + #[inline(always)] + fn is_zero(&self) -> bool { + self.0.iter().all(|&e| e == 0) + } + + #[inline(always)] + fn shr(&mut self, mut n: u32) { + if n >= 64 * 4 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn div2(&mut self) { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << 63; + *i >>= 1; + *i |= t; + t = t2; + } + } + + #[inline(always)] + fn mul2(&mut self) { + let mut last = 0; + for i in &mut self.0 { + let tmp = *i >> 63; + *i <<= 1; + *i |= last; + last = tmp; + } + } + + #[inline(always)] + fn shl(&mut self, mut n: u32) { + if n >= 64 * 4 { + *self = Self::from(0); + return; + } + + while n >= 64 { + let mut t = 0; + for i in &mut self.0 { + ::std::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in &mut self.0 { + let t2 = *i >> (64 - n); + *i <<= n; + *i |= t; + t = t2; + } + } + } + + #[inline(always)] + fn num_bits(&self) -> u32 { + let mut ret = (4 as u32) * 64; + for i in self.0.iter().rev() { + let leading = i.leading_zeros(); + ret -= leading; + if leading != 64 { + break; + } + } + + ret + } + + #[inline(always)] + fn add_nocarry(&mut self, other: &FsRepr) { + let mut carry = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = adc(*a, *b, &mut carry); + } + } + + #[inline(always)] + fn sub_noborrow(&mut self, other: &FsRepr) { + let mut borrow = 0; + + for (a, b) in self.0.iter_mut().zip(other.0.iter()) { + *a = sbb(*a, *b, &mut borrow); + } + } +} + +/// This is an element of the scalar field of the Jubjub curve. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Fs(FsRepr); + +impl ::std::fmt::Display for Fs +{ + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Fs({})", self.into_repr()) + } +} + +impl ::rand::Rand for Fs { + fn rand(rng: &mut R) -> Self { + loop { + let mut tmp = Fs(FsRepr::rand(rng)); + + // Mask away the unused bits at the beginning. + tmp.0.as_mut()[3] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; + + if tmp.is_valid() { + return tmp + } + } + } +} + +impl From for FsRepr { + fn from(e: Fs) -> FsRepr { + e.into_repr() + } +} + +impl PrimeField for Fs { + type Repr = FsRepr; + + fn from_repr(r: FsRepr) -> Result { + let mut r = Fs(r); + if r.is_valid() { + r.mul_assign(&Fs(R2)); + + Ok(r) + } else { + Err(PrimeFieldDecodingError::NotInField(format!("{}", r.0))) + } + } + + fn into_repr(&self) -> FsRepr { + let mut r = *self; + r.mont_reduce((self.0).0[0], (self.0).0[1], + (self.0).0[2], (self.0).0[3], + 0, 0, 0, 0); + r.0 + } + + fn char() -> FsRepr { + MODULUS + } + + const NUM_BITS: u32 = MODULUS_BITS; + + const CAPACITY: u32 = Self::NUM_BITS - 1; + + fn multiplicative_generator() -> Self { + Fs(GENERATOR) + } + + const S: u32 = S; + + fn root_of_unity() -> Self { + Fs(ROOT_OF_UNITY) + } +} + +impl Field for Fs { + #[inline] + fn zero() -> Self { + Fs(FsRepr::from(0)) + } + + #[inline] + fn one() -> Self { + Fs(R) + } + + #[inline] + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[inline] + fn add_assign(&mut self, other: &Fs) { + // This cannot exceed the backing capacity. + self.0.add_nocarry(&other.0); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn double(&mut self) { + // This cannot exceed the backing capacity. + self.0.mul2(); + + // However, it may need to be reduced. + self.reduce(); + } + + #[inline] + fn sub_assign(&mut self, other: &Fs) { + // If `other` is larger than `self`, we'll need to add the modulus to self first. + if other.0 > self.0 { + self.0.add_nocarry(&MODULUS); + } + + self.0.sub_noborrow(&other.0); + } + + #[inline] + fn negate(&mut self) { + if !self.is_zero() { + let mut tmp = MODULUS; + tmp.sub_noborrow(&self.0); + self.0 = tmp; + } + } + + fn inverse(&self) -> Option { + if self.is_zero() { + None + } else { + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications to Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + let one = FsRepr::from(1); + + let mut u = self.0; + let mut v = MODULUS; + let mut b = Fs(R2); // Avoids unnecessary reduction step. + let mut c = Self::zero(); + + while u != one && v != one { + while u.is_even() { + u.div2(); + + if b.0.is_even() { + b.0.div2(); + } else { + b.0.add_nocarry(&MODULUS); + b.0.div2(); + } + } + + while v.is_even() { + v.div2(); + + if c.0.is_even() { + c.0.div2(); + } else { + c.0.add_nocarry(&MODULUS); + c.0.div2(); + } + } + + if v < u { + u.sub_noborrow(&v); + b.sub_assign(&c); + } else { + v.sub_noborrow(&u); + c.sub_assign(&b); + } + } + + if u == one { + Some(b) + } else { + Some(c) + } + } + } + + #[inline(always)] + fn frobenius_map(&mut self, _: usize) { + // This has no effect in a prime field. + } + + #[inline] + fn mul_assign(&mut self, other: &Fs) + { + let mut carry = 0; + let r0 = mac_with_carry(0, (self.0).0[0], (other.0).0[0], &mut carry); + let r1 = mac_with_carry(0, (self.0).0[0], (other.0).0[1], &mut carry); + let r2 = mac_with_carry(0, (self.0).0[0], (other.0).0[2], &mut carry); + let r3 = mac_with_carry(0, (self.0).0[0], (other.0).0[3], &mut carry); + let r4 = carry; + let mut carry = 0; + let r1 = mac_with_carry(r1, (self.0).0[1], (other.0).0[0], &mut carry); + let r2 = mac_with_carry(r2, (self.0).0[1], (other.0).0[1], &mut carry); + let r3 = mac_with_carry(r3, (self.0).0[1], (other.0).0[2], &mut carry); + let r4 = mac_with_carry(r4, (self.0).0[1], (other.0).0[3], &mut carry); + let r5 = carry; + let mut carry = 0; + let r2 = mac_with_carry(r2, (self.0).0[2], (other.0).0[0], &mut carry); + let r3 = mac_with_carry(r3, (self.0).0[2], (other.0).0[1], &mut carry); + let r4 = mac_with_carry(r4, (self.0).0[2], (other.0).0[2], &mut carry); + let r5 = mac_with_carry(r5, (self.0).0[2], (other.0).0[3], &mut carry); + let r6 = carry; + let mut carry = 0; + let r3 = mac_with_carry(r3, (self.0).0[3], (other.0).0[0], &mut carry); + let r4 = mac_with_carry(r4, (self.0).0[3], (other.0).0[1], &mut carry); + let r5 = mac_with_carry(r5, (self.0).0[3], (other.0).0[2], &mut carry); + let r6 = mac_with_carry(r6, (self.0).0[3], (other.0).0[3], &mut carry); + let r7 = carry; + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + } + + #[inline] + fn square(&mut self) + { + let mut carry = 0; + let r1 = mac_with_carry(0, (self.0).0[0], (self.0).0[1], &mut carry); + let r2 = mac_with_carry(0, (self.0).0[0], (self.0).0[2], &mut carry); + let r3 = mac_with_carry(0, (self.0).0[0], (self.0).0[3], &mut carry); + let r4 = carry; + let mut carry = 0; + let r3 = mac_with_carry(r3, (self.0).0[1], (self.0).0[2], &mut carry); + let r4 = mac_with_carry(r4, (self.0).0[1], (self.0).0[3], &mut carry); + let r5 = carry; + let mut carry = 0; + let r5 = mac_with_carry(r5, (self.0).0[2], (self.0).0[3], &mut carry); + let r6 = carry; + + let r7 = r6 >> 63; + let r6 = (r6 << 1) | (r5 >> 63); + let r5 = (r5 << 1) | (r4 >> 63); + let r4 = (r4 << 1) | (r3 >> 63); + let r3 = (r3 << 1) | (r2 >> 63); + let r2 = (r2 << 1) | (r1 >> 63); + let r1 = r1 << 1; + + let mut carry = 0; + let r0 = mac_with_carry(0, (self.0).0[0], (self.0).0[0], &mut carry); + let r1 = adc(r1, 0, &mut carry); + let r2 = mac_with_carry(r2, (self.0).0[1], (self.0).0[1], &mut carry); + let r3 = adc(r3, 0, &mut carry); + let r4 = mac_with_carry(r4, (self.0).0[2], (self.0).0[2], &mut carry); + let r5 = adc(r5, 0, &mut carry); + let r6 = mac_with_carry(r6, (self.0).0[3], (self.0).0[3], &mut carry); + let r7 = adc(r7, 0, &mut carry); + self.mont_reduce(r0, r1, r2, r3, r4, r5, r6, r7); + } +} + +impl Fs { + /// Determines if the element is really in the field. This is only used + /// internally. + #[inline(always)] + fn is_valid(&self) -> bool { + self.0 < MODULUS + } + + /// Subtracts the modulus from this element if this element is not in the + /// field. Only used internally. + #[inline(always)] + fn reduce(&mut self) { + if !self.is_valid() { + self.0.sub_noborrow(&MODULUS); + } + } + + #[inline(always)] + fn mont_reduce( + &mut self, + r0: u64, + mut r1: u64, + mut r2: u64, + mut r3: u64, + mut r4: u64, + mut r5: u64, + mut r6: u64, + mut r7: u64 + ) + { + // The Montgomery reduction here is based on Algorithm 14.32 in + // Handbook of Applied Cryptography + // . + + let k = r0.wrapping_mul(INV); + let mut carry = 0; + mac_with_carry(r0, k, MODULUS.0[0], &mut carry); + r1 = mac_with_carry(r1, k, MODULUS.0[1], &mut carry); + r2 = mac_with_carry(r2, k, MODULUS.0[2], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS.0[3], &mut carry); + r4 = adc(r4, 0, &mut carry); + let carry2 = carry; + let k = r1.wrapping_mul(INV); + let mut carry = 0; + mac_with_carry(r1, k, MODULUS.0[0], &mut carry); + r2 = mac_with_carry(r2, k, MODULUS.0[1], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS.0[2], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS.0[3], &mut carry); + r5 = adc(r5, carry2, &mut carry); + let carry2 = carry; + let k = r2.wrapping_mul(INV); + let mut carry = 0; + mac_with_carry(r2, k, MODULUS.0[0], &mut carry); + r3 = mac_with_carry(r3, k, MODULUS.0[1], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS.0[2], &mut carry); + r5 = mac_with_carry(r5, k, MODULUS.0[3], &mut carry); + r6 = adc(r6, carry2, &mut carry); + let carry2 = carry; + let k = r3.wrapping_mul(INV); + let mut carry = 0; + mac_with_carry(r3, k, MODULUS.0[0], &mut carry); + r4 = mac_with_carry(r4, k, MODULUS.0[1], &mut carry); + r5 = mac_with_carry(r5, k, MODULUS.0[2], &mut carry); + r6 = mac_with_carry(r6, k, MODULUS.0[3], &mut carry); + r7 = adc(r7, carry2, &mut carry); + (self.0).0[0] = r4; + (self.0).0[1] = r5; + (self.0).0[2] = r6; + (self.0).0[3] = r7; + self.reduce(); + } + + fn mul_bits>(&self, bits: BitIterator) -> Self { + let mut res = Self::zero(); + for bit in bits { + res.double(); + + if bit { + res.add_assign(self) + } + } + res + } +} + +impl ToUniform for Fs { + /// Convert a little endian byte string into a uniform + /// field element. The number is reduced mod s. The caller + /// is responsible for ensuring the input is 64 bytes of + /// Random Oracle output. + fn to_uniform(digest: &[u8]) -> Self { + assert_eq!(digest.len(), 64); + let mut repr: [u64; 8] = [0; 8]; + LittleEndian::read_u64_into(digest, &mut repr); + Self::one().mul_bits(BitIterator::new(repr)) + } +} + +impl SqrtField for Fs { + + fn legendre(&self) -> LegendreSymbol { + // s = self^((s - 1) // 2) + let s = self.pow([0x684b872f6b7b965b, 0x53341049e6640841, 0x83339d80809a1d80, 0x73eda753299d7d4]); + if s == Self::zero() { Zero } + else if s == Self::one() { QuadraticResidue } + else { QuadraticNonResidue } + } + + fn sqrt(&self) -> Option { + // Shank's algorithm for s mod 4 = 3 + // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) + + // a1 = self^((s - 3) // 4) + let mut a1 = self.pow([0xb425c397b5bdcb2d, 0x299a0824f3320420, 0x4199cec0404d0ec0, 0x39f6d3a994cebea]); + let mut a0 = a1; + a0.square(); + a0.mul_assign(self); + + if a0 == NEGATIVE_ONE + { + None + } + else + { + a1.mul_assign(self); + Some(a1) + } + } +} + + +#[test] +fn test_neg_one() { + let mut o = Fs::one(); + o.negate(); + + assert_eq!(NEGATIVE_ONE, o); +} + +#[cfg(test)] +use rand::{SeedableRng, XorShiftRng, Rand}; + +#[test] +fn test_fs_repr_ordering() { + fn assert_equality(a: FsRepr, b: FsRepr) { + assert_eq!(a, b); + assert!(a.cmp(&b) == ::std::cmp::Ordering::Equal); + } + + fn assert_lt(a: FsRepr, b: FsRepr) { + assert!(a < b); + assert!(b > a); + } + + assert_equality(FsRepr([9999, 9999, 9999, 9999]), FsRepr([9999, 9999, 9999, 9999])); + assert_equality(FsRepr([9999, 9998, 9999, 9999]), FsRepr([9999, 9998, 9999, 9999])); + assert_equality(FsRepr([9999, 9999, 9999, 9997]), FsRepr([9999, 9999, 9999, 9997])); + assert_lt(FsRepr([9999, 9997, 9999, 9998]), FsRepr([9999, 9997, 9999, 9999])); + assert_lt(FsRepr([9999, 9997, 9998, 9999]), FsRepr([9999, 9997, 9999, 9999])); + assert_lt(FsRepr([9, 9999, 9999, 9997]), FsRepr([9999, 9999, 9999, 9997])); +} + +#[test] +fn test_fs_repr_from() { + assert_eq!(FsRepr::from(100), FsRepr([100, 0, 0, 0])); +} + +#[test] +fn test_fs_repr_is_odd() { + assert!(!FsRepr::from(0).is_odd()); + assert!(FsRepr::from(0).is_even()); + assert!(FsRepr::from(1).is_odd()); + assert!(!FsRepr::from(1).is_even()); + assert!(!FsRepr::from(324834872).is_odd()); + assert!(FsRepr::from(324834872).is_even()); + assert!(FsRepr::from(324834873).is_odd()); + assert!(!FsRepr::from(324834873).is_even()); +} + +#[test] +fn test_fs_repr_is_zero() { + assert!(FsRepr::from(0).is_zero()); + assert!(!FsRepr::from(1).is_zero()); + assert!(!FsRepr([0, 0, 1, 0]).is_zero()); +} + +#[test] +fn test_fs_repr_div2() { + let mut a = FsRepr([0xbd2920b19c972321, 0x174ed0466a3be37e, 0xd468d5e3b551f0b5, 0xcb67c072733beefc]); + a.div2(); + assert_eq!(a, FsRepr([0x5e949058ce4b9190, 0x8ba76823351df1bf, 0x6a346af1daa8f85a, 0x65b3e039399df77e])); + for _ in 0..10 { + a.div2(); + } + assert_eq!(a, FsRepr([0x6fd7a524163392e4, 0x16a2e9da08cd477c, 0xdf9a8d1abc76aa3e, 0x196cf80e4e677d])); + for _ in 0..200 { + a.div2(); + } + assert_eq!(a, FsRepr([0x196cf80e4e67, 0x0, 0x0, 0x0])); + for _ in 0..40 { + a.div2(); + } + assert_eq!(a, FsRepr([0x19, 0x0, 0x0, 0x0])); + for _ in 0..4 { + a.div2(); + } + assert_eq!(a, FsRepr([0x1, 0x0, 0x0, 0x0])); + a.div2(); + assert!(a.is_zero()); +} + +#[test] +fn test_fs_repr_shr() { + let mut a = FsRepr([0xb33fbaec482a283f, 0x997de0d3a88cb3df, 0x9af62d2a9a0e5525, 0x36003ab08de70da1]); + a.shr(0); + assert_eq!( + a, + FsRepr([0xb33fbaec482a283f, 0x997de0d3a88cb3df, 0x9af62d2a9a0e5525, 0x36003ab08de70da1]) + ); + a.shr(1); + assert_eq!( + a, + FsRepr([0xd99fdd762415141f, 0xccbef069d44659ef, 0xcd7b16954d072a92, 0x1b001d5846f386d0]) + ); + a.shr(50); + assert_eq!( + a, + FsRepr([0xbc1a7511967bf667, 0xc5a55341caa4b32f, 0x75611bce1b4335e, 0x6c0]) + ); + a.shr(130); + assert_eq!( + a, + FsRepr([0x1d5846f386d0cd7, 0x1b0, 0x0, 0x0]) + ); + a.shr(64); + assert_eq!( + a, + FsRepr([0x1b0, 0x0, 0x0, 0x0]) + ); +} + +#[test] +fn test_fs_repr_mul2() { + let mut a = FsRepr::from(23712937547); + a.mul2(); + assert_eq!(a, FsRepr([0xb0acd6c96, 0x0, 0x0, 0x0])); + for _ in 0..60 { + a.mul2(); + } + assert_eq!(a, FsRepr([0x6000000000000000, 0xb0acd6c9, 0x0, 0x0])); + for _ in 0..128 { + a.mul2(); + } + assert_eq!(a, FsRepr([0x0, 0x0, 0x6000000000000000, 0xb0acd6c9])); + for _ in 0..60 { + a.mul2(); + } + assert_eq!(a, FsRepr([0x0, 0x0, 0x0, 0x9600000000000000])); + for _ in 0..7 { + a.mul2(); + } + assert!(a.is_zero()); +} + +#[test] +fn test_fs_repr_num_bits() { + let mut a = FsRepr::from(0); + assert_eq!(0, a.num_bits()); + a = FsRepr::from(1); + for i in 1..257 { + assert_eq!(i, a.num_bits()); + a.mul2(); + } + assert_eq!(0, a.num_bits()); +} + +#[test] +fn test_fs_repr_sub_noborrow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FsRepr([0x8e62a7e85264e2c3, 0xb23d34c1941d3ca, 0x5976930b7502dd15, 0x600f3fb517bf5495]); + t.sub_noborrow(&FsRepr([0xd64f669809cbc6a4, 0xfa76cb9d90cf7637, 0xfefb0df9038d43b3, 0x298a30c744b31acf])); + assert!(t == FsRepr([0xb813415048991c1f, 0x10ad07ae88725d92, 0x5a7b851271759961, 0x36850eedd30c39c5])); + + for _ in 0..1000 { + let mut a = FsRepr::rand(&mut rng); + a.0[3] >>= 30; + let mut b = a; + for _ in 0..10 { + b.mul2(); + } + let mut c = b; + for _ in 0..10 { + c.mul2(); + } + + assert!(a < b); + assert!(b < c); + + let mut csub_ba = c; + csub_ba.sub_noborrow(&b); + csub_ba.sub_noborrow(&a); + + let mut csub_ab = c; + csub_ab.sub_noborrow(&a); + csub_ab.sub_noborrow(&b); + + assert_eq!(csub_ab, csub_ba); + } +} + +#[test] +fn test_fs_legendre() { + assert_eq!(QuadraticResidue, Fs::one().legendre()); + assert_eq!(Zero, Fs::zero().legendre()); + + let e = FsRepr([0x8385eec23df1f88e, 0x9a01fb412b2dba16, 0x4c928edcdd6c22f, 0x9f2df7ef69ecef9]); + assert_eq!(QuadraticResidue, Fs::from_repr(e).unwrap().legendre()); + let e = FsRepr([0xe8ed9f299da78568, 0x35efdebc88b2209, 0xc82125cb1f916dbe, 0x6813d2b38c39bd0]); + assert_eq!(QuadraticNonResidue, Fs::from_repr(e).unwrap().legendre()); +} + +#[test] +fn test_fr_repr_add_nocarry() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let mut t = FsRepr([0xd64f669809cbc6a4, 0xfa76cb9d90cf7637, 0xfefb0df9038d43b3, 0x298a30c744b31acf]); + t.add_nocarry(&FsRepr([0x8e62a7e85264e2c3, 0xb23d34c1941d3ca, 0x5976930b7502dd15, 0x600f3fb517bf5495])); + assert_eq!(t, FsRepr([0x64b20e805c30a967, 0x59a9ee9aa114a02, 0x5871a104789020c9, 0x8999707c5c726f65])); + + // Test for the associativity of addition. + for _ in 0..1000 { + let mut a = FsRepr::rand(&mut rng); + let mut b = FsRepr::rand(&mut rng); + let mut c = FsRepr::rand(&mut rng); + + // Unset the first few bits, so that overflow won't occur. + a.0[3] >>= 3; + b.0[3] >>= 3; + c.0[3] >>= 3; + + let mut abc = a; + abc.add_nocarry(&b); + abc.add_nocarry(&c); + + let mut acb = a; + acb.add_nocarry(&c); + acb.add_nocarry(&b); + + let mut bac = b; + bac.add_nocarry(&a); + bac.add_nocarry(&c); + + let mut bca = b; + bca.add_nocarry(&c); + bca.add_nocarry(&a); + + let mut cab = c; + cab.add_nocarry(&a); + cab.add_nocarry(&b); + + let mut cba = c; + cba.add_nocarry(&b); + cba.add_nocarry(&a); + + assert_eq!(abc, acb); + assert_eq!(abc, bac); + assert_eq!(abc, bca); + assert_eq!(abc, cab); + assert_eq!(abc, cba); + } +} + +#[test] +fn test_fs_is_valid() { + let mut a = Fs(MODULUS); + assert!(!a.is_valid()); + a.0.sub_noborrow(&FsRepr::from(1)); + assert!(a.is_valid()); + assert!(Fs(FsRepr::from(0)).is_valid()); + assert!(Fs(FsRepr([0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9])).is_valid()); + assert!(!Fs(FsRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])).is_valid()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let a = Fs::rand(&mut rng); + assert!(a.is_valid()); + } +} + +#[test] +fn test_fs_add_assign() { + { + // Random number + let mut tmp = Fs::from_str("4577408157467272683998459759522778614363623736323078995109579213719612604198").unwrap(); + assert!(tmp.is_valid()); + // Test that adding zero has no effect. + tmp.add_assign(&Fs(FsRepr::from(0))); + assert_eq!(tmp, Fs(FsRepr([0x8e6bfff4722d6e67, 0x5643da5c892044f9, 0x9465f4b281921a69, 0x25f752d3edd7162]))); + // Add one and test for the result. + tmp.add_assign(&Fs(FsRepr::from(1))); + assert_eq!(tmp, Fs(FsRepr([0x8e6bfff4722d6e68, 0x5643da5c892044f9, 0x9465f4b281921a69, 0x25f752d3edd7162]))); + // Add another random number that exercises the reduction. + tmp.add_assign(&Fs(FsRepr([0xb634d07bc42d4a70, 0xf724f0c008411f5f, 0x456d4053d865af34, 0x24ce814e8c63027]))); + assert_eq!(tmp, Fs(FsRepr([0x44a0d070365ab8d8, 0x4d68cb1c91616459, 0xd9d3350659f7c99e, 0x4ac5d4227a3a189]))); + // Add one to (s - 1) and test for the result. + tmp = Fs(FsRepr([0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9])); + tmp.add_assign(&Fs(FsRepr::from(1))); + assert!(tmp.0.is_zero()); + // Add a random number to another one such that the result is s - 1 + tmp = Fs(FsRepr([0xa11fda5950ce3636, 0x922e0dbccfe0ca0e, 0xacebb6e215b82d4a, 0x97ffb8cdc3aee93])); + tmp.add_assign(&Fs(FsRepr([0x2f7734058628f680, 0x143a12d6fce74674, 0x597b841eeb7c0db6, 0x4fdb95d88f8c115]))); + assert_eq!(tmp, Fs(FsRepr([0xd0970e5ed6f72cb6, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9]))); + // Add one to the result and test for it. + tmp.add_assign(&Fs(FsRepr::from(1))); + assert!(tmp.0.is_zero()); + } + + // Test associativity + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Generate a, b, c and ensure (a + b) + c == a + (b + c). + let a = Fs::rand(&mut rng); + let b = Fs::rand(&mut rng); + let c = Fs::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + + let mut tmp2 = b; + tmp2.add_assign(&c); + tmp2.add_assign(&a); + + assert!(tmp1.is_valid()); + assert!(tmp2.is_valid()); + assert_eq!(tmp1, tmp2); + } +} + +#[test] +fn test_fs_sub_assign() { + { + // Test arbitrary subtraction that tests reduction. + let mut tmp = Fs(FsRepr([0xb384d9f6877afd99, 0x4442513958e1a1c1, 0x352c4b8a95eccc3f, 0x2db62dee4b0f2])); + tmp.sub_assign(&Fs(FsRepr([0xec5bd2d13ed6b05a, 0x2adc0ab3a39b5fa, 0x82d3360a493e637e, 0x53ccff4a64d6679]))); + assert_eq!(tmp, Fs(FsRepr([0x97c015841f9b79f6, 0xe7fcb121eb6ffc49, 0xb8c050814de2a3c1, 0x943c0589dcafa21]))); + + // Test the opposite subtraction which doesn't test reduction. + tmp = Fs(FsRepr([0xec5bd2d13ed6b05a, 0x2adc0ab3a39b5fa, 0x82d3360a493e637e, 0x53ccff4a64d6679])); + tmp.sub_assign(&Fs(FsRepr([0xb384d9f6877afd99, 0x4442513958e1a1c1, 0x352c4b8a95eccc3f, 0x2db62dee4b0f2]))); + assert_eq!(tmp, Fs(FsRepr([0x38d6f8dab75bb2c1, 0xbe6b6f71e1581439, 0x4da6ea7fb351973e, 0x539f491c768b587]))); + + // Test for sensible results with zero + tmp = Fs(FsRepr::from(0)); + tmp.sub_assign(&Fs(FsRepr::from(0))); + assert!(tmp.is_zero()); + + tmp = Fs(FsRepr([0x361e16aef5cce835, 0x55bbde2536e274c1, 0x4dc77a63fd15ee75, 0x1e14bb37c14f230])); + tmp.sub_assign(&Fs(FsRepr::from(0))); + assert_eq!(tmp, Fs(FsRepr([0x361e16aef5cce835, 0x55bbde2536e274c1, 0x4dc77a63fd15ee75, 0x1e14bb37c14f230]))); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure that (a - b) + (b - a) = 0. + let a = Fs::rand(&mut rng); + let b = Fs::rand(&mut rng); + + let mut tmp1 = a; + tmp1.sub_assign(&b); + + let mut tmp2 = b; + tmp2.sub_assign(&a); + + tmp1.add_assign(&tmp2); + assert!(tmp1.is_zero()); + } +} + +#[test] +fn test_fs_mul_assign() { + let mut tmp = Fs(FsRepr([0xb433b01287f71744, 0x4eafb86728c4d108, 0xfdd52c14b9dfbe65, 0x2ff1f3434821118])); + tmp.mul_assign(&Fs(FsRepr([0xdae00fc63c9fa90f, 0x5a5ed89b96ce21ce, 0x913cd26101bd6f58, 0x3f0822831697fe9]))); + assert!(tmp == Fs(FsRepr([0xb68ecb61d54d2992, 0x5ff95874defce6a6, 0x3590eb053894657d, 0x53823a118515933]))); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * b) * c = a * (b * c) + let a = Fs::rand(&mut rng); + let b = Fs::rand(&mut rng); + let c = Fs::rand(&mut rng); + + let mut tmp1 = a; + tmp1.mul_assign(&b); + tmp1.mul_assign(&c); + + let mut tmp2 = b; + tmp2.mul_assign(&c); + tmp2.mul_assign(&a); + + assert_eq!(tmp1, tmp2); + } + + for _ in 0..1000000 { + // Ensure that r * (a + b + c) = r*a + r*b + r*c + + let r = Fs::rand(&mut rng); + let mut a = Fs::rand(&mut rng); + let mut b = Fs::rand(&mut rng); + let mut c = Fs::rand(&mut rng); + + let mut tmp1 = a; + tmp1.add_assign(&b); + tmp1.add_assign(&c); + tmp1.mul_assign(&r); + + a.mul_assign(&r); + b.mul_assign(&r); + c.mul_assign(&r); + + a.add_assign(&b); + a.add_assign(&c); + + assert_eq!(tmp1, a); + } +} + +#[test] +fn test_fr_squaring() { + let mut a = Fs(FsRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xe7db4ea6533afa8])); + assert!(a.is_valid()); + a.square(); + assert_eq!(a, Fs::from_repr(FsRepr([0x12c7f55cbc52fbaa, 0xdedc98a0b5e6ce9e, 0xad2892726a5396a, 0x9fe82af8fee77b3])).unwrap()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000000 { + // Ensure that (a * a) = a^2 + let a = Fs::rand(&mut rng); + + let mut tmp = a; + tmp.square(); + + let mut tmp2 = a; + tmp2.mul_assign(&a); + + assert_eq!(tmp, tmp2); + } +} + +#[test] +fn test_fs_inverse() { + assert!(Fs::zero().inverse().is_none()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + let one = Fs::one(); + + for _ in 0..1000 { + // Ensure that a * a^-1 = 1 + let mut a = Fs::rand(&mut rng); + let ainv = a.inverse().unwrap(); + a.mul_assign(&ainv); + assert_eq!(a, one); + } +} + +#[test] +fn test_fs_double() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure doubling a is equivalent to adding a to itself. + let mut a = Fs::rand(&mut rng); + let mut b = a; + b.add_assign(&a); + a.double(); + assert_eq!(a, b); + } +} + +#[test] +fn test_fs_negate() { + { + let mut a = Fs::zero(); + a.negate(); + + assert!(a.is_zero()); + } + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Ensure (a - (-a)) = 0. + let mut a = Fs::rand(&mut rng); + let mut b = a; + b.negate(); + a.add_assign(&b); + + assert!(a.is_zero()); + } +} + +#[test] +fn test_fs_pow() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for i in 0..1000 { + // Exponentiate by various small numbers and ensure it consists with repeated + // multiplication. + let a = Fs::rand(&mut rng); + let target = a.pow(&[i]); + let mut c = Fs::one(); + for _ in 0..i { + c.mul_assign(&a); + } + assert_eq!(c, target); + } + + for _ in 0..1000 { + // Exponentiating by the modulus should have no effect in a prime field. + let a = Fs::rand(&mut rng); + + assert_eq!(a, a.pow(Fs::char())); + } +} + +#[test] +fn test_fs_sqrt() { + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + assert_eq!(Fs::zero().sqrt().unwrap(), Fs::zero()); + + for _ in 0..1000 { + // Ensure sqrt(a^2) = a or -a + let a = Fs::rand(&mut rng); + let mut nega = a; + nega.negate(); + let mut b = a; + b.square(); + + let b = b.sqrt().unwrap(); + + assert!(a == b || nega == b); + } + + for _ in 0..1000 { + // Ensure sqrt(a)^2 = a for random a + let a = Fs::rand(&mut rng); + + if let Some(mut tmp) = a.sqrt() { + tmp.square(); + + assert_eq!(a, tmp); + } + } +} + +#[test] +fn test_fs_from_into_repr() { + // r + 1 should not be in the field + assert!(Fs::from_repr(FsRepr([0xd0970e5ed6f72cb8, 0xa6682093ccc81082, 0x6673b0101343b00, 0xe7db4ea6533afa9])).is_err()); + + // r should not be in the field + assert!(Fs::from_repr(Fs::char()).is_err()); + + // Multiply some arbitrary representations to see if the result is as expected. + let a = FsRepr([0x5f2d0c05d0337b71, 0xa1df2b0f8a20479, 0xad73785e71bb863, 0x504a00480c9acec]); + let mut a_fs = Fs::from_repr(a).unwrap(); + let b = FsRepr([0x66356ff51e477562, 0x60a92ab55cf7603, 0x8e4273c7364dd192, 0x36df8844a344dc5]); + let b_fs = Fs::from_repr(b).unwrap(); + let c = FsRepr([0x7eef61708f4f2868, 0x747a7e6cf52946fb, 0x83dd75d7c9120017, 0x762f5177f0f3df7]); + a_fs.mul_assign(&b_fs); + assert_eq!(a_fs.into_repr(), c); + + // Zero should be in the field. + assert!(Fs::from_repr(FsRepr::from(0)).unwrap().is_zero()); + + let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + // Try to turn Fs elements into representations and back again, and compare. + let a = Fs::rand(&mut rng); + let a_repr = a.into_repr(); + let b_repr = FsRepr::from(a); + assert_eq!(a_repr, b_repr); + let a_again = Fs::from_repr(a_repr).unwrap(); + + assert_eq!(a, a_again); + } +} + +#[test] +fn test_fs_repr_display() { + assert_eq!( + format!("{}", FsRepr([0xa296db59787359df, 0x8d3e33077430d318, 0xd1abf5c606102eb7, 0xcbc33ee28108f0])), + "0x00cbc33ee28108f0d1abf5c606102eb78d3e33077430d318a296db59787359df".to_string() + ); + assert_eq!( + format!("{}", FsRepr([0x14cb03535054a620, 0x312aa2bf2d1dff52, 0x970fe98746ab9361, 0xc1e18acf82711e6])), + "0x0c1e18acf82711e6970fe98746ab9361312aa2bf2d1dff5214cb03535054a620".to_string() + ); + assert_eq!( + format!("{}", FsRepr([0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff])), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string() + ); + assert_eq!( + format!("{}", FsRepr([0, 0, 0, 0])), + "0x0000000000000000000000000000000000000000000000000000000000000000".to_string() + ); +} + +#[test] +fn test_fs_display() { + assert_eq!( + format!("{}", Fs::from_repr(FsRepr([0x5528efb9998a01a3, 0x5bd2add5cb357089, 0xc061fa6adb491f98, 0x70db9d143db03d9])).unwrap()), + "Fs(0x070db9d143db03d9c061fa6adb491f985bd2add5cb3570895528efb9998a01a3)".to_string() + ); + assert_eq!( + format!("{}", Fs::from_repr(FsRepr([0xd674745e2717999e, 0xbeb1f52d3e96f338, 0x9c7ae147549482b9, 0x999706024530d22])).unwrap()), + "Fs(0x0999706024530d229c7ae147549482b9beb1f52d3e96f338d674745e2717999e)".to_string() + ); +} + +#[test] +fn test_fs_num_bits() { + assert_eq!(Fs::NUM_BITS, 252); + assert_eq!(Fs::CAPACITY, 251); +} + +#[test] +fn test_fs_root_of_unity() { + assert_eq!(Fs::S, 1); + assert_eq!(Fs::multiplicative_generator(), Fs::from_repr(FsRepr::from(6)).unwrap()); + assert_eq!( + Fs::multiplicative_generator().pow([0x684b872f6b7b965b, 0x53341049e6640841, 0x83339d80809a1d80, 0x73eda753299d7d4]), + Fs::root_of_unity() + ); + assert_eq!( + Fs::root_of_unity().pow([1 << Fs::S]), + Fs::one() + ); + assert!(Fs::multiplicative_generator().sqrt().is_none()); +} diff --git a/src/rust/sapling-crypto/src/jubjub/mod.rs b/src/rust/sapling-crypto/src/jubjub/mod.rs new file mode 100644 index 000000000..51a000a29 --- /dev/null +++ b/src/rust/sapling-crypto/src/jubjub/mod.rs @@ -0,0 +1,435 @@ +//! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar +//! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with +//! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery +//! curve of the form `y^2 = x^3 + Ax^2 + x` with `A = 40962`. This +//! value `A` is the smallest integer choice such that: +//! +//! * `(A - 2) / 4` is a small integer (`10240`). +//! * `A^2 - 4` is quadratic nonresidue. +//! * The group order of the curve and its quadratic twist has a large +//! prime factor. +//! +//! Jubjub has `s = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` +//! as the prime subgroup order, with cofactor 8. (The twist has +//! cofactor 4.) +//! +//! It is a complete twisted Edwards curve, so the equivalence with +//! the Montgomery curve forms a group isomorphism, allowing points +//! to be freely converted between the two forms. + +use pairing::{ + Engine, + Field, + PrimeField, + SqrtField +}; + +use group_hash::group_hash; + +use constants; + +use pairing::bls12_381::{ + Bls12, + Fr +}; + +/// This is an implementation of the twisted Edwards Jubjub curve. +pub mod edwards; + +/// This is an implementation of the birationally equivalent +/// Montgomery curve. +pub mod montgomery; + +/// This is an implementation of the scalar field for Jubjub. +pub mod fs; + +#[cfg(test)] +pub mod tests; + +/// Point of unknown order. +pub enum Unknown { } + +/// Point of prime order. +pub enum PrimeOrder { } + +/// Fixed generators of the Jubjub curve of unknown +/// exponent. +#[derive(Copy, Clone)] +pub enum FixedGenerators { + /// The prover will demonstrate knowledge of discrete log + /// with respect to this base when they are constructing + /// a proof, in order to authorize proof construction. + ProofGenerationKey = 0, + + /// The note commitment is randomized over this generator. + NoteCommitmentRandomness = 1, + + /// The node commitment is randomized again by the position + /// in order to supply the nullifier computation with a + /// unique input w.r.t. the note being spent, to prevent + /// Faerie gold attacks. + NullifierPosition = 2, + + /// The value commitment is used to check balance between + /// inputs and outputs. The value is placed over this + /// generator. + ValueCommitmentValue = 3, + /// The value commitment is randomized over this generator, + /// for privacy. + ValueCommitmentRandomness = 4, + + /// The spender proves discrete log with respect to this + /// base at spend time. + SpendingKeyGenerator = 5, + + Max = 6 +} + +pub trait ToUniform { + fn to_uniform(digest: &[u8]) -> Self; +} + +/// This is an extension to the pairing Engine trait which +/// offers a scalar field for the embedded curve (Jubjub) +/// and some pre-computed parameters. +pub trait JubjubEngine: Engine { + /// The scalar field of the Jubjub curve + type Fs: PrimeField + SqrtField + ToUniform; + /// The parameters of Jubjub and the Sapling protocol + type Params: JubjubParams; +} + +/// The pre-computed parameters for Jubjub, including curve +/// constants and various limits and window tables. +pub trait JubjubParams: Sized { + /// The `d` constant of the twisted Edwards curve. + fn edwards_d(&self) -> &E::Fr; + /// The `A` constant of the birationally equivalent Montgomery curve. + fn montgomery_a(&self) -> &E::Fr; + /// The `A` constant, doubled. + fn montgomery_2a(&self) -> &E::Fr; + /// The scaling factor used for conversion from the Montgomery form. + fn scale(&self) -> &E::Fr; + /// Returns the generators (for each segment) used in all Pedersen commitments. + fn pedersen_hash_generators(&self) -> &[edwards::Point]; + /// Returns the exp table for Pedersen hashes. + fn pedersen_hash_exp_table(&self) -> &[Vec>>]; + /// Returns the maximum number of chunks per segment of the Pedersen hash. + fn pedersen_hash_chunks_per_generator(&self) -> usize; + /// Returns the pre-computed window tables [-4, 3, 2, 1, 1, 2, 3, 4] of different + /// magnitudes of the Pedersen hash segment generators. + fn pedersen_circuit_generators(&self) -> &[Vec>]; + + /// Returns the number of chunks needed to represent a full scalar during fixed-base + /// exponentiation. + fn fixed_base_chunks_per_generator(&self) -> usize; + /// Returns a fixed generator. + fn generator(&self, base: FixedGenerators) -> &edwards::Point; + /// Returns a window table [0, 1, ..., 8] for different magnitudes of some + /// fixed generator. + fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>]; + /// Returns the window size for exponentiation of Pedersen hash generators + /// outside the circuit + fn pedersen_hash_exp_window_size() -> u32; +} + +impl JubjubEngine for Bls12 { + type Fs = self::fs::Fs; + type Params = JubjubBls12; +} + +pub struct JubjubBls12 { + edwards_d: Fr, + montgomery_a: Fr, + montgomery_2a: Fr, + scale: Fr, + + pedersen_hash_generators: Vec>, + pedersen_hash_exp: Vec>>>, + pedersen_circuit_generators: Vec>>, + + fixed_base_generators: Vec>, + fixed_base_circuit_generators: Vec>>, +} + +impl JubjubParams for JubjubBls12 { + fn edwards_d(&self) -> &Fr { &self.edwards_d } + fn montgomery_a(&self) -> &Fr { &self.montgomery_a } + fn montgomery_2a(&self) -> &Fr { &self.montgomery_2a } + fn scale(&self) -> &Fr { &self.scale } + fn pedersen_hash_generators(&self) -> &[edwards::Point] { + &self.pedersen_hash_generators + } + fn pedersen_hash_exp_table(&self) -> &[Vec>>] { + &self.pedersen_hash_exp + } + fn pedersen_hash_chunks_per_generator(&self) -> usize { + 63 + } + fn fixed_base_chunks_per_generator(&self) -> usize { + 84 + } + fn pedersen_circuit_generators(&self) -> &[Vec>] { + &self.pedersen_circuit_generators + } + fn generator(&self, base: FixedGenerators) -> &edwards::Point + { + &self.fixed_base_generators[base as usize] + } + fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>] + { + &self.fixed_base_circuit_generators[base as usize][..] + } + fn pedersen_hash_exp_window_size() -> u32 { + 8 + } +} + +impl JubjubBls12 { + pub fn new() -> Self { + let montgomery_a = Fr::from_str("40962").unwrap(); + let mut montgomery_2a = montgomery_a; + montgomery_2a.double(); + + let mut tmp_params = JubjubBls12 { + // d = -(10240/10241) + edwards_d: Fr::from_str("19257038036680949359750312669786877991949435402254120286184196891950884077233").unwrap(), + // A = 40962 + montgomery_a: montgomery_a, + // 2A = 2.A + montgomery_2a: montgomery_2a, + // scaling factor = sqrt(4 / (a - d)) + scale: Fr::from_str("17814886934372412843466061268024708274627479829237077604635722030778476050649").unwrap(), + + // We'll initialize these below + pedersen_hash_generators: vec![], + pedersen_hash_exp: vec![], + pedersen_circuit_generators: vec![], + fixed_base_generators: vec![], + fixed_base_circuit_generators: vec![], + }; + + fn find_group_hash( + m: &[u8], + personalization: &[u8; 8], + params: &E::Params + ) -> edwards::Point + { + let mut tag = m.to_vec(); + let i = tag.len(); + tag.push(0u8); + + loop { + let gh = group_hash( + &tag, + personalization, + params + ); + + // We don't want to overflow and start reusing generators + assert!(tag[i] != u8::max_value()); + tag[i] += 1; + + if let Some(gh) = gh { + break gh; + } + } + } + + // Create the bases for the Pedersen hashes + { + let mut pedersen_hash_generators = vec![]; + + for m in 0..5 { + use byteorder::{WriteBytesExt, LittleEndian}; + + let mut segment_number = [0u8; 4]; + (&mut segment_number[0..4]).write_u32::(m).unwrap(); + + pedersen_hash_generators.push( + find_group_hash( + &segment_number, + constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, + &tmp_params + ) + ); + } + + // Check for duplicates, far worse than spec inconsistencies! + for (i, p1) in pedersen_hash_generators.iter().enumerate() { + if p1 == &edwards::Point::zero() { + panic!("Neutral element!"); + } + + for p2 in pedersen_hash_generators.iter().skip(i+1) { + if p1 == p2 { + panic!("Duplicate generator!"); + } + } + } + + tmp_params.pedersen_hash_generators = pedersen_hash_generators; + } + + // Create the exp table for the Pedersen hash generators + { + let mut pedersen_hash_exp = vec![]; + + for g in &tmp_params.pedersen_hash_generators { + let mut g = g.clone(); + + let window = JubjubBls12::pedersen_hash_exp_window_size(); + + let mut tables = vec![]; + + let mut num_bits = 0; + while num_bits <= fs::Fs::NUM_BITS { + let mut table = Vec::with_capacity(1 << window); + + let mut base = edwards::Point::zero(); + + for _ in 0..(1 << window) { + table.push(base.clone()); + base = base.add(&g, &tmp_params); + } + + tables.push(table); + num_bits += window; + + for _ in 0..window { + g = g.double(&tmp_params); + } + } + + pedersen_hash_exp.push(tables); + } + + tmp_params.pedersen_hash_exp = pedersen_hash_exp; + } + + // Create the bases for other parts of the protocol + { + let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize]; + + fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] = + find_group_hash(&[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params); + + fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] = + find_group_hash(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params); + + fixed_base_generators[FixedGenerators::NullifierPosition as usize] = + find_group_hash(&[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params); + + fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] = + find_group_hash(b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params); + + fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] = + find_group_hash(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params); + + fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] = + find_group_hash(&[], constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params); + + // Check for duplicates, far worse than spec inconsistencies! + for (i, p1) in fixed_base_generators.iter().enumerate() { + if p1 == &edwards::Point::zero() { + panic!("Neutral element!"); + } + + for p2 in fixed_base_generators.iter().skip(i+1) { + if p1 == p2 { + panic!("Duplicate generator!"); + } + } + } + + tmp_params.fixed_base_generators = fixed_base_generators; + } + + // Create the 2-bit window table lookups for each 4-bit + // "chunk" in each segment of the Pedersen hash + { + let mut pedersen_circuit_generators = vec![]; + + // Process each segment + for mut gen in tmp_params.pedersen_hash_generators.iter().cloned() { + let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params); + let mut windows = vec![]; + for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() { + // Create (x, y) coeffs for this chunk + let mut coeffs = vec![]; + let mut g = gen.clone(); + + // coeffs = g, g*2, g*3, g*4 + for _ in 0..4 { + coeffs.push(g.into_xy().expect("cannot produce O")); + g = g.add(&gen, &tmp_params); + } + windows.push(coeffs); + + // Our chunks are separated by 2 bits to prevent overlap. + for _ in 0..4 { + gen = gen.double(&tmp_params); + } + } + pedersen_circuit_generators.push(windows); + } + + tmp_params.pedersen_circuit_generators = pedersen_circuit_generators; + } + + // Create the 3-bit window table lookups for fixed-base + // exp of each base in the protocol. + { + let mut fixed_base_circuit_generators = vec![]; + + for mut gen in tmp_params.fixed_base_generators.iter().cloned() { + let mut windows = vec![]; + for _ in 0..tmp_params.fixed_base_chunks_per_generator() { + let mut coeffs = vec![(Fr::zero(), Fr::one())]; + let mut g = gen.clone(); + for _ in 0..7 { + coeffs.push(g.into_xy()); + g = g.add(&gen, &tmp_params); + } + windows.push(coeffs); + + // gen = gen * 8 + gen = g; + } + fixed_base_circuit_generators.push(windows); + } + + tmp_params.fixed_base_circuit_generators = fixed_base_circuit_generators; + } + + tmp_params + } +} + +#[test] +fn test_jubjub_bls12() { + let params = JubjubBls12::new(); + + tests::test_suite::(¶ms); + + let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31"); + let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); + let q = edwards::Point::::get_for_y( + Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(), + false, + ¶ms + ).unwrap(); + + assert!(p == q); + + // Same thing, but sign bit set + let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1"); + let p = edwards::Point::::read(&test_repr[..], ¶ms).unwrap(); + let q = edwards::Point::::get_for_y( + Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(), + true, + ¶ms + ).unwrap(); + + assert!(p == q); +} diff --git a/src/rust/sapling-crypto/src/jubjub/montgomery.rs b/src/rust/sapling-crypto/src/jubjub/montgomery.rs new file mode 100644 index 000000000..18d0fcb0a --- /dev/null +++ b/src/rust/sapling-crypto/src/jubjub/montgomery.rs @@ -0,0 +1,358 @@ +use pairing::{ + Field, + SqrtField, + PrimeField, + PrimeFieldRepr, + BitIterator +}; + +use super::{ + JubjubEngine, + JubjubParams, + Unknown, + PrimeOrder, + edwards +}; + +use rand::{ + Rng +}; + +use std::marker::PhantomData; + +// Represents the affine point (X, Y) +pub struct Point { + x: E::Fr, + y: E::Fr, + infinity: bool, + _marker: PhantomData +} + +fn convert_subgroup(from: &Point) -> Point +{ + Point { + x: from.x, + y: from.y, + infinity: from.infinity, + _marker: PhantomData + } +} + +impl From> for Point +{ + fn from(p: Point) -> Point + { + convert_subgroup(&p) + } +} + +impl Clone for Point +{ + fn clone(&self) -> Self { + convert_subgroup(self) + } +} + +impl PartialEq for Point { + fn eq(&self, other: &Point) -> bool { + match (self.infinity, other.infinity) { + (true, true) => true, + (true, false) | (false, true) => false, + (false, false) => { + self.x == other.x && self.y == other.y + } + } + } +} + +impl Point { + pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option + { + // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) + + let mut x2 = x; + x2.square(); + + let mut rhs = x2; + rhs.mul_assign(params.montgomery_a()); + rhs.add_assign(&x); + x2.mul_assign(&x); + rhs.add_assign(&x2); + + match rhs.sqrt() { + Some(mut y) => { + if y.into_repr().is_odd() != sign { + y.negate(); + } + + return Some(Point { + x: x, + y: y, + infinity: false, + _marker: PhantomData + }) + }, + None => None + } + } + + /// This guarantees the point is in the prime order subgroup + #[must_use] + pub fn mul_by_cofactor(&self, params: &E::Params) -> Point + { + let tmp = self.double(params) + .double(params) + .double(params); + + convert_subgroup(&tmp) + } + + pub fn rand(rng: &mut R, params: &E::Params) -> Self + { + loop { + let x: E::Fr = rng.gen(); + + match Self::get_for_x(x, rng.gen(), params) { + Some(p) => { + return p + }, + None => {} + } + } + } +} + +impl Point { + /// Convert from an Edwards point + pub fn from_edwards( + e: &edwards::Point, + params: &E::Params + ) -> Self + { + let (x, y) = e.into_xy(); + + if y == E::Fr::one() { + // The only solution for y = 1 is x = 0. (0, 1) is + // the neutral element, so we map this to the point + // at infinity. + + Point::zero() + } else { + // The map from a twisted Edwards curve is defined as + // (x, y) -> (u, v) where + // u = (1 + y) / (1 - y) + // v = u / x + // + // This mapping is not defined for y = 1 and for x = 0. + // + // We have that y != 1 above. If x = 0, the only + // solutions for y are 1 (contradiction) or -1. + if x.is_zero() { + // (0, -1) is the point of order two which is not + // the neutral element, so we map it to (0, 0) which is + // the only affine point of order 2. + + Point { + x: E::Fr::zero(), + y: E::Fr::zero(), + infinity: false, + _marker: PhantomData + } + } else { + // The mapping is defined as above. + // + // (x, y) -> (u, v) where + // u = (1 + y) / (1 - y) + // v = u / x + + let mut u = E::Fr::one(); + u.add_assign(&y); + { + let mut tmp = E::Fr::one(); + tmp.sub_assign(&y); + u.mul_assign(&tmp.inverse().unwrap()) + } + + let mut v = u; + v.mul_assign(&x.inverse().unwrap()); + + // Scale it into the correct curve constants + v.mul_assign(params.scale()); + + Point { + x: u, + y: v, + infinity: false, + _marker: PhantomData + } + } + } + } + + /// Attempts to cast this as a prime order element, failing if it's + /// not in the prime order subgroup. + pub fn as_prime_order(&self, params: &E::Params) -> Option> { + if self.mul(E::Fs::char(), params) == Point::zero() { + Some(convert_subgroup(self)) + } else { + None + } + } + + pub fn zero() -> Self { + Point { + x: E::Fr::zero(), + y: E::Fr::zero(), + infinity: true, + _marker: PhantomData + } + } + + pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)> + { + if self.infinity { + None + } else { + Some((self.x, self.y)) + } + } + + #[must_use] + pub fn negate(&self) -> Self { + let mut p = self.clone(); + + p.y.negate(); + + p + } + + #[must_use] + pub fn double(&self, params: &E::Params) -> Self { + if self.infinity { + return Point::zero(); + } + + // (0, 0) is the point of order 2. Doubling + // produces the point at infinity. + if self.y == E::Fr::zero() { + return Point::zero(); + } + + // This is a standard affine point doubling formula + // See 4.3.2 The group law for Weierstrass curves + // Montgomery curves and the Montgomery Ladder + // Daniel J. Bernstein and Tanja Lange + + let mut delta = E::Fr::one(); + { + let mut tmp = params.montgomery_a().clone(); + tmp.mul_assign(&self.x); + tmp.double(); + delta.add_assign(&tmp); + } + { + let mut tmp = self.x; + tmp.square(); + delta.add_assign(&tmp); + tmp.double(); + delta.add_assign(&tmp); + } + { + let mut tmp = self.y; + tmp.double(); + delta.mul_assign(&tmp.inverse().expect("y is nonzero so this must be nonzero")); + } + + let mut x3 = delta; + x3.square(); + x3.sub_assign(params.montgomery_a()); + x3.sub_assign(&self.x); + x3.sub_assign(&self.x); + + let mut y3 = x3; + y3.sub_assign(&self.x); + y3.mul_assign(&delta); + y3.add_assign(&self.y); + y3.negate(); + + Point { + x: x3, + y: y3, + infinity: false, + _marker: PhantomData + } + } + + #[must_use] + pub fn add(&self, other: &Self, params: &E::Params) -> Self + { + // This is a standard affine point addition formula + // See 4.3.2 The group law for Weierstrass curves + // Montgomery curves and the Montgomery Ladder + // Daniel J. Bernstein and Tanja Lange + + match (self.infinity, other.infinity) { + (true, true) => Point::zero(), + (true, false) => other.clone(), + (false, true) => self.clone(), + (false, false) => { + if self.x == other.x { + if self.y == other.y { + self.double(params) + } else { + Point::zero() + } + } else { + let mut delta = other.y; + delta.sub_assign(&self.y); + { + let mut tmp = other.x; + tmp.sub_assign(&self.x); + delta.mul_assign(&tmp.inverse().expect("self.x != other.x, so this must be nonzero")); + } + + let mut x3 = delta; + x3.square(); + x3.sub_assign(params.montgomery_a()); + x3.sub_assign(&self.x); + x3.sub_assign(&other.x); + + let mut y3 = x3; + y3.sub_assign(&self.x); + y3.mul_assign(&delta); + y3.add_assign(&self.y); + y3.negate(); + + Point { + x: x3, + y: y3, + infinity: false, + _marker: PhantomData + } + } + } + } + } + + #[must_use] + pub fn mul::Repr>>( + &self, + scalar: S, + params: &E::Params + ) -> Self + { + // Standard double-and-add scalar multiplication + + let mut res = Self::zero(); + + for b in BitIterator::new(scalar.into()) { + res = res.double(params); + + if b { + res = res.add(self, params); + } + } + + res + } +} diff --git a/src/rust/sapling-crypto/src/jubjub/tests.rs b/src/rust/sapling-crypto/src/jubjub/tests.rs new file mode 100644 index 000000000..421a8f704 --- /dev/null +++ b/src/rust/sapling-crypto/src/jubjub/tests.rs @@ -0,0 +1,416 @@ +use super::{ + JubjubEngine, + JubjubParams, + PrimeOrder, + montgomery, + edwards +}; + +use pairing::{ + Field, + PrimeField, + PrimeFieldRepr, + SqrtField, + LegendreSymbol +}; + +use rand::{XorShiftRng, SeedableRng, Rand}; + +pub fn test_suite(params: &E::Params) { + test_back_and_forth::(params); + test_jubjub_params::(params); + test_rand::(params); + test_get_for::(params); + test_identities::(params); + test_addition_associativity::(params); + test_order::(params); + test_mul_associativity::(params); + test_loworder::(params); + test_read_write::(params); +} + +fn is_on_mont_curve>( + x: E::Fr, + y: E::Fr, + params: &P +) -> bool +{ + let mut lhs = y; + lhs.square(); + + let mut x2 = x; + x2.square(); + + let mut x3 = x2; + x3.mul_assign(&x); + + let mut rhs = x2; + rhs.mul_assign(params.montgomery_a()); + rhs.add_assign(&x); + rhs.add_assign(&x3); + + lhs == rhs +} + +fn is_on_twisted_edwards_curve>( + x: E::Fr, + y: E::Fr, + params: &P +) -> bool +{ + let mut x2 = x; + x2.square(); + + let mut y2 = y; + y2.square(); + + // -x^2 + y^2 + let mut lhs = y2; + lhs.sub_assign(&x2); + + // 1 + d x^2 y^2 + let mut rhs = y2; + rhs.mul_assign(&x2); + rhs.mul_assign(params.edwards_d()); + rhs.add_assign(&E::Fr::one()); + + lhs == rhs +} + +fn test_loworder(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let inf = montgomery::Point::zero(); + + // try to find a point of order 8 + let p = loop { + let r = montgomery::Point::::rand(rng, params).mul(E::Fs::char(), params); + + let r2 = r.double(params); + let r4 = r2.double(params); + let r8 = r4.double(params); + + if r2 != inf && r4 != inf && r8 == inf { + break r; + } + }; + + let mut loworder_points = vec![]; + { + let mut tmp = p.clone(); + + for _ in 0..8 { + assert!(!loworder_points.contains(&tmp)); + loworder_points.push(tmp.clone()); + tmp = tmp.add(&p, params); + } + } + assert!(loworder_points[7] == inf); +} + +fn test_mul_associativity(params: &E::Params) { + use self::edwards::Point; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + // Pick a random point and multiply it by the cofactor + let base = Point::::rand(rng, params).mul_by_cofactor(params); + + let mut a = E::Fs::rand(rng); + let b = E::Fs::rand(rng); + let c = E::Fs::rand(rng); + + let res1 = base.mul(a, params).mul(b, params).mul(c, params); + let res2 = base.mul(b, params).mul(c, params).mul(a, params); + let res3 = base.mul(c, params).mul(a, params).mul(b, params); + a.mul_assign(&b); + a.mul_assign(&c); + let res4 = base.mul(a, params); + + assert!(res1 == res2); + assert!(res2 == res3); + assert!(res3 == res4); + + let (x, y) = res1.into_xy(); + assert!(is_on_twisted_edwards_curve(x, y, params)); + + let (x, y) = res2.into_xy(); + assert!(is_on_twisted_edwards_curve(x, y, params)); + + let (x, y) = res3.into_xy(); + assert!(is_on_twisted_edwards_curve(x, y, params)); + } +} + +fn test_order(params: &E::Params) { + use self::edwards::Point; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + // The neutral element is in the prime order subgroup. + assert!(Point::::zero().as_prime_order(params).is_some()); + + for _ in 0..50 { + // Pick a random point and multiply it by the cofactor + let base = Point::::rand(rng, params).mul_by_cofactor(params); + + // Any point multiplied by the cofactor will be in the prime + // order subgroup + assert!(base.as_prime_order(params).is_some()); + } + + // It's very likely that at least one out of 50 random points on the curve + // is not in the prime order subgroup. + let mut at_least_one_not_in_prime_order_subgroup = false; + for _ in 0..50 { + // Pick a random point. + let base = Point::::rand(rng, params); + + at_least_one_not_in_prime_order_subgroup |= base.as_prime_order(params).is_none(); + } + assert!(at_least_one_not_in_prime_order_subgroup); +} + +fn test_addition_associativity(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + use self::montgomery::Point; + + let a = Point::::rand(rng, params); + let b = Point::::rand(rng, params); + let c = Point::::rand(rng, params); + + assert!(a.add(&b, ¶ms).add(&c, ¶ms) == c.add(&a, ¶ms).add(&b, ¶ms)); + } + + for _ in 0..1000 { + use self::edwards::Point; + + let a = Point::::rand(rng, params); + let b = Point::::rand(rng, params); + let c = Point::::rand(rng, params); + + assert!(a.add(&b, ¶ms).add(&c, ¶ms) == c.add(&a, ¶ms).add(&b, ¶ms)); + } +} + +fn test_identities(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + { + use self::edwards::Point; + + let z = Point::::zero(); + assert!(z.double(¶ms) == z); + assert!(z.negate() == z); + + for _ in 0..100 { + let r = Point::::rand(rng, params); + + assert!(r.add(&Point::zero(), ¶ms) == r); + assert!(r.add(&r.negate(), ¶ms) == Point::zero()); + } + } + + { + use self::montgomery::Point; + + let z = Point::::zero(); + assert!(z.double(¶ms) == z); + assert!(z.negate() == z); + + for _ in 0..100 { + let r = Point::::rand(rng, params); + + assert!(r.add(&Point::zero(), ¶ms) == r); + assert!(r.add(&r.negate(), ¶ms) == Point::zero()); + } + } +} + +fn test_get_for(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let y = E::Fr::rand(rng); + let sign = bool::rand(rng); + + if let Some(mut p) = edwards::Point::::get_for_y(y, sign, params) { + assert!(p.into_xy().0.into_repr().is_odd() == sign); + p = p.negate(); + assert!( + edwards::Point::::get_for_y(y, !sign, params).unwrap() + == + p + ); + } + } +} + +fn test_read_write(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let e = edwards::Point::::rand(rng, params); + + let mut v = vec![]; + e.write(&mut v).unwrap(); + + let e2 = edwards::Point::read(&v[..], params).unwrap(); + + assert!(e == e2); + } +} + +fn test_rand(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let p = montgomery::Point::::rand(rng, params); + let e = edwards::Point::::rand(rng, params); + + { + let (x, y) = p.into_xy().unwrap(); + assert!(is_on_mont_curve(x, y, params)); + } + + { + let (x, y) = e.into_xy(); + assert!(is_on_twisted_edwards_curve(x, y, params)); + } + } +} + +fn test_back_and_forth(params: &E::Params) { + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..1000 { + let s = E::Fs::rand(rng); + let edwards_p1 = edwards::Point::::rand(rng, params); + let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params); + let mont_p2 = montgomery::Point::::rand(rng, params); + let edwards_p2 = edwards::Point::from_montgomery(&mont_p2, params); + + let mont = mont_p1.add(&mont_p2, params).mul(s, params); + let edwards = edwards_p1.add(&edwards_p2, params).mul(s, params); + + assert!( + montgomery::Point::from_edwards(&edwards, params) == mont + ); + + assert!( + edwards::Point::from_montgomery(&mont, params) == edwards + ); + } +} + +fn test_jubjub_params(params: &E::Params) { + // a = -1 + let mut a = E::Fr::one(); + a.negate(); + + { + // Check that 2A is consistent with A + let mut tmp = *params.montgomery_a(); + tmp.double(); + + assert_eq!(&tmp, params.montgomery_2a()); + } + + { + // The twisted Edwards addition law is complete when d is nonsquare + // and a is square. + + assert!(params.edwards_d().legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(a.legendre() == LegendreSymbol::QuadraticResidue); + } + + { + // Other convenient sanity checks regarding d + + // tmp = d + let mut tmp = *params.edwards_d(); + + // 1 / d is nonsquare + assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); + + // tmp = -d + tmp.negate(); + + // -d is nonsquare + assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + + // 1 / -d is nonsquare + assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); + } + + { + // Check that A^2 - 4 is nonsquare: + let mut tmp = params.montgomery_a().clone(); + tmp.square(); + tmp.sub_assign(&E::Fr::from_str("4").unwrap()); + assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + } + + { + // Check that A - 2 is nonsquare: + let mut tmp = params.montgomery_a().clone(); + tmp.sub_assign(&E::Fr::from_str("2").unwrap()); + assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + } + + { + // Check the validity of the scaling factor + let mut tmp = a; + tmp.sub_assign(¶ms.edwards_d()); + tmp = tmp.inverse().unwrap(); + tmp.mul_assign(&E::Fr::from_str("4").unwrap()); + tmp = tmp.sqrt().unwrap(); + assert_eq!(&tmp, params.scale()); + } + + { + // Check that the number of windows per generator + // in the Pedersen hash does not allow for collisions + + let mut cur = E::Fs::one().into_repr(); + + let mut max = E::Fs::char(); + { + max.sub_noborrow(&E::Fs::one().into_repr()); + max.div2(); + } + + let mut pacc = E::Fs::zero().into_repr(); + let mut nacc = E::Fs::char(); + + for _ in 0..params.pedersen_hash_chunks_per_generator() + { + // tmp = cur * 4 + let mut tmp = cur; + tmp.mul2(); + tmp.mul2(); + + pacc.add_nocarry(&tmp); + nacc.sub_noborrow(&tmp); + + assert!(pacc < max); + assert!(pacc < nacc); + + // cur = cur * 16 + for _ in 0..4 { + cur.mul2(); + } + } + } + + { + // Check that the number of windows for fixed-base + // scalar multiplication is sufficient for all scalars. + + assert!(params.fixed_base_chunks_per_generator() * 3 >= E::Fs::NUM_BITS as usize); + + // ... and that it's *just* efficient enough. + + assert!((params.fixed_base_chunks_per_generator() - 1) * 3 < E::Fs::NUM_BITS as usize); + } +} diff --git a/src/rust/sapling-crypto/src/lib.rs b/src/rust/sapling-crypto/src/lib.rs new file mode 100644 index 000000000..27d306cf8 --- /dev/null +++ b/src/rust/sapling-crypto/src/lib.rs @@ -0,0 +1,22 @@ +extern crate pairing; +extern crate bellman; +extern crate blake2_rfc; +extern crate digest; +extern crate rand; +extern crate byteorder; + +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + +#[cfg(test)] +extern crate crypto; + +pub mod jubjub; +pub mod group_hash; +pub mod circuit; +pub mod pedersen_hash; +pub mod primitives; +pub mod constants; +pub mod redjubjub; +pub mod util; diff --git a/src/rust/sapling-crypto/src/pedersen_hash.rs b/src/rust/sapling-crypto/src/pedersen_hash.rs new file mode 100644 index 000000000..6e06cdfae --- /dev/null +++ b/src/rust/sapling-crypto/src/pedersen_hash.rs @@ -0,0 +1,103 @@ +use jubjub::*; +use pairing::*; + +#[derive(Copy, Clone)] +pub enum Personalization { + NoteCommitment, + MerkleTree(usize) +} + +impl Personalization { + pub fn get_bits(&self) -> Vec { + match *self { + Personalization::NoteCommitment => + vec![true, true, true, true, true, true], + Personalization::MerkleTree(num) => { + assert!(num < 63); + + (0..6).map(|i| (num >> i) & 1 == 1).collect() + } + } + } +} + +pub fn pedersen_hash( + personalization: Personalization, + bits: I, + params: &E::Params +) -> edwards::Point + where I: IntoIterator, + E: JubjubEngine +{ + let mut bits = personalization.get_bits().into_iter().chain(bits.into_iter()); + + let mut result = edwards::Point::zero(); + let mut generators = params.pedersen_hash_exp_table().iter(); + + loop { + let mut acc = E::Fs::zero(); + let mut cur = E::Fs::one(); + let mut chunks_remaining = params.pedersen_hash_chunks_per_generator(); + let mut encountered_bits = false; + + // Grab three bits from the input + while let Some(a) = bits.next() { + encountered_bits = true; + + let b = bits.next().unwrap_or(false); + let c = bits.next().unwrap_or(false); + + // Start computing this portion of the scalar + let mut tmp = cur; + if a { + tmp.add_assign(&cur); + } + cur.double(); // 2^1 * cur + if b { + tmp.add_assign(&cur); + } + + // conditionally negate + if c { + tmp.negate(); + } + + acc.add_assign(&tmp); + + chunks_remaining -= 1; + + if chunks_remaining == 0 { + break; + } else { + cur.double(); // 2^2 * cur + cur.double(); // 2^3 * cur + cur.double(); // 2^4 * cur + } + } + + if !encountered_bits { + break; + } + + let mut table: &[Vec>] = &generators.next().expect("we don't have enough generators"); + let window = JubjubBls12::pedersen_hash_exp_window_size(); + let window_mask = (1 << window) - 1; + + let mut acc = acc.into_repr(); + + let mut tmp = edwards::Point::zero(); + + while !acc.is_zero() { + let i = (acc.as_ref()[0] & window_mask) as usize; + + tmp = tmp.add(&table[0][i], params); + + acc.shr(window); + table = &table[1..]; + } + + result = result.add(&tmp, params); + } + + result +} diff --git a/src/rust/sapling-crypto/src/primitives/mod.rs b/src/rust/sapling-crypto/src/primitives/mod.rs new file mode 100644 index 000000000..a1ffb472c --- /dev/null +++ b/src/rust/sapling-crypto/src/primitives/mod.rs @@ -0,0 +1,258 @@ +use pairing::{ + Field, + PrimeField, + PrimeFieldRepr +}; + +use constants; + +use group_hash::group_hash; + +use pedersen_hash::{ + pedersen_hash, + Personalization +}; + +use byteorder::{ + LittleEndian, + WriteBytesExt +}; + +use jubjub::{ + JubjubEngine, + JubjubParams, + edwards, + PrimeOrder, + FixedGenerators +}; + +use blake2_rfc::blake2s::Blake2s; + +#[derive(Clone)] +pub struct ValueCommitment { + pub value: u64, + pub randomness: E::Fs +} + +impl ValueCommitment { + pub fn cm( + &self, + params: &E::Params + ) -> edwards::Point + { + params.generator(FixedGenerators::ValueCommitmentValue) + .mul(self.value, params) + .add( + ¶ms.generator(FixedGenerators::ValueCommitmentRandomness) + .mul(self.randomness, params), + params + ) + } +} + +#[derive(Clone)] +pub struct ProofGenerationKey { + pub ak: edwards::Point, + pub nsk: E::Fs +} + +impl ProofGenerationKey { + pub fn into_viewing_key(&self, params: &E::Params) -> ViewingKey { + ViewingKey { + ak: self.ak.clone(), + nk: params.generator(FixedGenerators::ProofGenerationKey) + .mul(self.nsk, params) + } + } +} + +pub struct ViewingKey { + pub ak: edwards::Point, + pub nk: edwards::Point +} + +impl ViewingKey { + pub fn rk( + &self, + ar: E::Fs, + params: &E::Params + ) -> edwards::Point { + self.ak.add( + ¶ms.generator(FixedGenerators::SpendingKeyGenerator) + .mul(ar, params), + params + ) + } + + pub fn ivk(&self) -> E::Fs { + let mut preimage = [0; 64]; + + self.ak.write(&mut preimage[0..32]).unwrap(); + self.nk.write(&mut preimage[32..64]).unwrap(); + + let mut h = Blake2s::with_params(32, &[], &[], constants::CRH_IVK_PERSONALIZATION); + h.update(&preimage); + let mut h = h.finalize().as_ref().to_vec(); + + // Drop the most significant five bits, so it can be interpreted as a scalar. + h[31] &= 0b0000_0111; + + let mut e = ::Repr::default(); + e.read_le(&h[..]).unwrap(); + + E::Fs::from_repr(e).expect("should be a valid scalar") + } + + pub fn into_payment_address( + &self, + diversifier: Diversifier, + params: &E::Params + ) -> Option> + { + diversifier.g_d(params).map(|g_d| { + let pk_d = g_d.mul(self.ivk(), params); + + PaymentAddress { + pk_d: pk_d, + diversifier: diversifier + } + }) + } +} + +#[derive(Copy, Clone)] +pub struct Diversifier(pub [u8; 11]); + +impl Diversifier { + pub fn g_d( + &self, + params: &E::Params + ) -> Option> + { + group_hash::(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION, params) + } +} + +#[derive(Clone)] +pub struct PaymentAddress { + pub pk_d: edwards::Point, + pub diversifier: Diversifier +} + +impl PaymentAddress { + pub fn g_d( + &self, + params: &E::Params + ) -> Option> + { + self.diversifier.g_d(params) + } + + pub fn create_note( + &self, + value: u64, + randomness: E::Fs, + params: &E::Params + ) -> Option> + { + self.g_d(params).map(|g_d| { + Note { + value: value, + r: randomness, + g_d: g_d, + pk_d: self.pk_d.clone() + } + }) + } +} + +pub struct Note { + /// The value of the note + pub value: u64, + /// The diversified base of the address, GH(d) + pub g_d: edwards::Point, + /// The public key of the address, g_d^ivk + pub pk_d: edwards::Point, + /// The commitment randomness + pub r: E::Fs +} + +impl Note { + pub fn uncommitted() -> E::Fr { + // The smallest u-coordinate that is not on the curve + // is one. + // TODO: This should be relocated to JubjubEngine as + // it's specific to the curve we're using, not all + // twisted edwards curves. + E::Fr::one() + } + + /// Computes the note commitment, returning the full point. + fn cm_full_point(&self, params: &E::Params) -> edwards::Point + { + // Calculate the note contents, as bytes + let mut note_contents = vec![]; + + // Writing the value in little endian + (&mut note_contents).write_u64::(self.value).unwrap(); + + // Write g_d + self.g_d.write(&mut note_contents).unwrap(); + + // Write pk_d + self.pk_d.write(&mut note_contents).unwrap(); + + assert_eq!(note_contents.len(), 32 + 32 + 8); + + // Compute the Pedersen hash of the note contents + let hash_of_contents = pedersen_hash( + Personalization::NoteCommitment, + note_contents.into_iter() + .flat_map(|byte| { + (0..8).map(move |i| ((byte >> i) & 1) == 1) + }), + params + ); + + // Compute final commitment + params.generator(FixedGenerators::NoteCommitmentRandomness) + .mul(self.r, params) + .add(&hash_of_contents, params) + } + + /// Computes the nullifier given the viewing key and + /// note position + pub fn nf( + &self, + viewing_key: &ViewingKey, + position: u64, + params: &E::Params + ) -> Vec + { + // Compute rho = cm + position.G + let rho = self + .cm_full_point(params) + .add( + ¶ms.generator(FixedGenerators::NullifierPosition) + .mul(position, params), + params + ); + + // Compute nf = BLAKE2s(nk | rho) + let mut nf_preimage = [0u8; 64]; + viewing_key.nk.write(&mut nf_preimage[0..32]).unwrap(); + rho.write(&mut nf_preimage[32..64]).unwrap(); + let mut h = Blake2s::with_params(32, &[], &[], constants::PRF_NF_PERSONALIZATION); + h.update(&nf_preimage); + + h.finalize().as_ref().to_vec() + } + + /// Computes the note commitment + pub fn cm(&self, params: &E::Params) -> E::Fr + { + // The commitment is in the prime order subgroup, so mapping the + // commitment to the x-coordinate is an injective encoding. + self.cm_full_point(params).into_xy().0 + } +} diff --git a/src/rust/sapling-crypto/src/redjubjub.rs b/src/rust/sapling-crypto/src/redjubjub.rs new file mode 100644 index 000000000..dfae28c05 --- /dev/null +++ b/src/rust/sapling-crypto/src/redjubjub.rs @@ -0,0 +1,343 @@ +//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve. +//! See section 5.4.6 of the Sapling protocol specification. + +use pairing::{Field, PrimeField, PrimeFieldRepr}; +use rand::{Rng, Rand}; +use std::io::{self, Read, Write}; + +use jubjub::{FixedGenerators, JubjubEngine, JubjubParams, Unknown, edwards::Point}; +use util::{hash_to_scalar}; + +fn read_scalar(reader: R) -> io::Result { + let mut s_repr = ::Repr::default(); + s_repr.read_le(reader)?; + + match E::Fs::from_repr(s_repr) { + Ok(s) => Ok(s), + Err(_) => Err(io::Error::new( + io::ErrorKind::InvalidInput, + "scalar is not in field", + )), + } +} + +fn write_scalar(s: &E::Fs, writer: W) -> io::Result<()> { + s.into_repr().write_le(writer) +} + +fn h_star(a: &[u8], b: &[u8]) -> E::Fs { + hash_to_scalar::(b"Zcash_RedJubjubH", a, b) +} + +#[derive(Copy, Clone)] +pub struct Signature { + rbar: [u8; 32], + sbar: [u8; 32], +} + +pub struct PrivateKey(pub E::Fs); + +pub struct PublicKey(pub Point); + +impl Signature { + pub fn read(mut reader: R) -> io::Result { + let mut rbar = [0u8; 32]; + let mut sbar = [0u8; 32]; + reader.read_exact(&mut rbar)?; + reader.read_exact(&mut sbar)?; + Ok(Signature { rbar, sbar }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_all(&self.rbar)?; + writer.write_all(&self.sbar) + } +} + +impl PrivateKey { + pub fn randomize(&self, alpha: E::Fs) -> Self { + let mut tmp = self.0; + tmp.add_assign(&alpha); + PrivateKey(tmp) + } + + pub fn read(reader: R) -> io::Result { + let pk = read_scalar::(reader)?; + Ok(PrivateKey(pk)) + } + + pub fn write(&self, writer: W) -> io::Result<()> { + write_scalar::(&self.0, writer) + } + + pub fn sign( + &self, + msg: &[u8], + rng: &mut R, + p_g: FixedGenerators, + params: &E::Params, + ) -> Signature { + // T = (l_H + 128) bits of randomness + // For H*, l_H = 512 bits + let mut t = [0u8; 80]; + rng.fill_bytes(&mut t[..]); + + // r = H*(T || M) + let r = h_star::(&t[..], msg); + + // R = r . P_G + let r_g = params.generator(p_g).mul(r, params); + let mut rbar = [0u8; 32]; + r_g.write(&mut rbar[..]) + .expect("Jubjub points should serialize to 32 bytes"); + + // S = r + H*(Rbar || M) . sk + let mut s = h_star::(&rbar[..], msg); + s.mul_assign(&self.0); + s.add_assign(&r); + let mut sbar = [0u8; 32]; + write_scalar::(&s, &mut sbar[..]) + .expect("Jubjub scalars should serialize to 32 bytes"); + + Signature { rbar, sbar } + } +} + +impl PublicKey { + pub fn from_private(privkey: &PrivateKey, p_g: FixedGenerators, params: &E::Params) -> Self { + let res = params.generator(p_g).mul(privkey.0, params).into(); + PublicKey(res) + } + + pub fn randomize(&self, alpha: E::Fs, p_g: FixedGenerators, params: &E::Params) -> Self { + let res: Point = params.generator(p_g).mul(alpha, params).into(); + let res = res.add(&self.0, params); + PublicKey(res) + } + + pub fn read(reader: R, params: &E::Params) -> io::Result { + let p = Point::read(reader, params)?; + Ok(PublicKey(p)) + } + + pub fn write(&self, writer: W) -> io::Result<()> { + self.0.write(writer) + } + + pub fn verify( + &self, + msg: &[u8], + sig: &Signature, + p_g: FixedGenerators, + params: &E::Params, + ) -> bool { + // c = H*(Rbar || M) + let c = h_star::(&sig.rbar[..], msg); + + // Signature checks: + // R != invalid + let r = match Point::read(&sig.rbar[..], params) { + Ok(r) => r, + Err(_) => return false, + }; + // S < order(G) + // (E::Fs guarantees its representation is in the field) + let s = match read_scalar::(&sig.sbar[..]) { + Ok(s) => s, + Err(_) => return false, + }; + // 0 = h_G(-S . P_G + R + c . vk) + self.0.mul(c, params).add(&r, params).add( + ¶ms.generator(p_g).mul(s, params).negate().into(), + params + ).mul_by_cofactor(params).eq(&Point::zero()) + } +} + +pub struct BatchEntry<'a, E: JubjubEngine> { + vk: PublicKey, + msg: &'a [u8], + sig: Signature, +} + +// TODO: #82: This is a naive implementation currently, +// and doesn't use multiexp. +pub fn batch_verify<'a, E: JubjubEngine, R: Rng>( + rng: &mut R, + batch: &[BatchEntry<'a, E>], + p_g: FixedGenerators, + params: &E::Params, +) -> bool +{ + let mut acc = Point::::zero(); + + for entry in batch { + let mut r = match Point::::read(&entry.sig.rbar[..], params) { + Ok(r) => r, + Err(_) => return false, + }; + let mut s = match read_scalar::(&entry.sig.sbar[..]) { + Ok(s) => s, + Err(_) => return false, + }; + + let mut c = h_star::(&entry.sig.rbar[..], entry.msg); + + let z = E::Fs::rand(rng); + + s.mul_assign(&z); + s.negate(); + + r = r.mul(z, params); + + c.mul_assign(&z); + + acc = acc.add(&r, params); + acc = acc.add(&entry.vk.0.mul(c, params), params); + acc = acc.add(¶ms.generator(p_g).mul(s, params).into(), params); + } + + acc = acc.mul_by_cofactor(params).into(); + + acc.eq(&Point::zero()) +} + +#[cfg(test)] +mod tests { + use pairing::bls12_381::Bls12; + use rand::thread_rng; + + use jubjub::{JubjubBls12, fs::Fs, edwards}; + + use super::*; + + #[test] + fn test_batch_verify() { + let rng = &mut thread_rng(); + let params = &JubjubBls12::new(); + let p_g = FixedGenerators::SpendingKeyGenerator; + + let sk1 = PrivateKey::(rng.gen()); + let vk1 = PublicKey::from_private(&sk1, p_g, params); + let msg1 = b"Foo bar"; + let sig1 = sk1.sign(msg1, rng, p_g, params); + assert!(vk1.verify(msg1, &sig1, p_g, params)); + + let sk2 = PrivateKey::(rng.gen()); + let vk2 = PublicKey::from_private(&sk2, p_g, params); + let msg2 = b"Foo bar"; + let sig2 = sk2.sign(msg2, rng, p_g, params); + assert!(vk2.verify(msg2, &sig2, p_g, params)); + + let mut batch = vec![ + BatchEntry { vk: vk1, msg: msg1, sig: sig1 }, + BatchEntry { vk: vk2, msg: msg2, sig: sig2 } + ]; + + assert!(batch_verify(rng, &batch, p_g, params)); + + batch[0].sig = sig2; + + assert!(!batch_verify(rng, &batch, p_g, params)); + } + + #[test] + fn cofactor_check() { + let rng = &mut thread_rng(); + let params = &JubjubBls12::new(); + let zero = edwards::Point::zero(); + let p_g = FixedGenerators::SpendingKeyGenerator; + + // Get a point of order 8 + let p8 = loop { + let r = edwards::Point::::rand(rng, params).mul(Fs::char(), params); + + let r2 = r.double(params); + let r4 = r2.double(params); + let r8 = r4.double(params); + + if r2 != zero && r4 != zero && r8 == zero { + break r; + } + }; + + let sk = PrivateKey::(rng.gen()); + let vk = PublicKey::from_private(&sk, p_g, params); + + // TODO: This test will need to change when #77 is fixed + let msg = b"Foo bar"; + let sig = sk.sign(msg, rng, p_g, params); + assert!(vk.verify(msg, &sig, p_g, params)); + + let vktorsion = PublicKey(vk.0.add(&p8, params)); + assert!(vktorsion.verify(msg, &sig, p_g, params)); + } + + #[test] + fn round_trip_serialization() { + let rng = &mut thread_rng(); + let p_g = FixedGenerators::SpendingKeyGenerator; + let params = &JubjubBls12::new(); + + for _ in 0..1000 { + let sk = PrivateKey::(rng.gen()); + let vk = PublicKey::from_private(&sk, p_g, params); + let msg = b"Foo bar"; + let sig = sk.sign(msg, rng, p_g, params); + + let mut sk_bytes = [0u8; 32]; + let mut vk_bytes = [0u8; 32]; + let mut sig_bytes = [0u8; 64]; + sk.write(&mut sk_bytes[..]).unwrap(); + vk.write(&mut vk_bytes[..]).unwrap(); + sig.write(&mut sig_bytes[..]).unwrap(); + + let sk_2 = PrivateKey::::read(&sk_bytes[..]).unwrap(); + let vk_2 = PublicKey::from_private(&sk_2, p_g, params); + let mut vk_2_bytes = [0u8; 32]; + vk_2.write(&mut vk_2_bytes[..]).unwrap(); + assert!(vk_bytes == vk_2_bytes); + + let vk_2 = PublicKey::::read(&vk_bytes[..], params).unwrap(); + let sig_2 = Signature::read(&sig_bytes[..]).unwrap(); + assert!(vk.verify(msg, &sig_2, p_g, params)); + assert!(vk_2.verify(msg, &sig, p_g, params)); + assert!(vk_2.verify(msg, &sig_2, p_g, params)); + } + } + + #[test] + fn random_signatures() { + let rng = &mut thread_rng(); + let p_g = FixedGenerators::SpendingKeyGenerator; + let params = &JubjubBls12::new(); + + for _ in 0..1000 { + let sk = PrivateKey::(rng.gen()); + let vk = PublicKey::from_private(&sk, p_g, params); + + let msg1 = b"Foo bar"; + let msg2 = b"Spam eggs"; + + let sig1 = sk.sign(msg1, rng, p_g, params); + let sig2 = sk.sign(msg2, rng, p_g, params); + + assert!(vk.verify(msg1, &sig1, p_g, params)); + assert!(vk.verify(msg2, &sig2, p_g, params)); + assert!(!vk.verify(msg1, &sig2, p_g, params)); + assert!(!vk.verify(msg2, &sig1, p_g, params)); + + let alpha = rng.gen(); + let rsk = sk.randomize(alpha); + let rvk = vk.randomize(alpha, p_g, params); + + let sig1 = rsk.sign(msg1, rng, p_g, params); + let sig2 = rsk.sign(msg2, rng, p_g, params); + + assert!(rvk.verify(msg1, &sig1, p_g, params)); + assert!(rvk.verify(msg2, &sig2, p_g, params)); + assert!(!rvk.verify(msg1, &sig2, p_g, params)); + assert!(!rvk.verify(msg2, &sig1, p_g, params)); + } + } +} diff --git a/src/rust/sapling-crypto/src/util.rs b/src/rust/sapling-crypto/src/util.rs new file mode 100644 index 000000000..e67e66089 --- /dev/null +++ b/src/rust/sapling-crypto/src/util.rs @@ -0,0 +1,11 @@ +use blake2_rfc::blake2b::Blake2b; + +use jubjub::{JubjubEngine, ToUniform}; + +pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> E::Fs { + let mut hasher = Blake2b::with_params(64, &[], &[], persona); + hasher.update(a); + hasher.update(b); + let ret = hasher.finalize(); + E::Fs::to_uniform(ret.as_ref()) +} diff --git a/src/rust/zcash_primitives/Cargo.toml b/src/rust/zcash_primitives/Cargo.toml new file mode 100644 index 000000000..1816cc73d --- /dev/null +++ b/src/rust/zcash_primitives/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "zcash_primitives" +version = "0.0.0" +authors = [ + "Jack Grigg ", +] + +[dependencies] diff --git a/src/rust/zcash_primitives/LICENSE-APACHE b/src/rust/zcash_primitives/LICENSE-APACHE new file mode 100644 index 000000000..1e5006dc1 --- /dev/null +++ b/src/rust/zcash_primitives/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/src/rust/zcash_primitives/LICENSE-MIT b/src/rust/zcash_primitives/LICENSE-MIT new file mode 100644 index 000000000..5b7be8e66 --- /dev/null +++ b/src/rust/zcash_primitives/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Zcash Company + +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. diff --git a/src/rust/zcash_primitives/README.md b/src/rust/zcash_primitives/README.md new file mode 100644 index 000000000..b284820ce --- /dev/null +++ b/src/rust/zcash_primitives/README.md @@ -0,0 +1,20 @@ +# zcash_primitives + +This library contains Rust implementations of the Zcash primitives. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. + diff --git a/src/rust/zcash_primitives/src/lib.rs b/src/rust/zcash_primitives/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/src/rust/zcash_primitives/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/src/rust/zcash_proofs/Cargo.toml b/src/rust/zcash_proofs/Cargo.toml new file mode 100644 index 000000000..353bff855 --- /dev/null +++ b/src/rust/zcash_proofs/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "zcash_proofs" +version = "0.0.0" +authors = [ + "Jack Grigg ", +] + +[dependencies] diff --git a/src/rust/zcash_proofs/LICENSE-APACHE b/src/rust/zcash_proofs/LICENSE-APACHE new file mode 100644 index 000000000..1e5006dc1 --- /dev/null +++ b/src/rust/zcash_proofs/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/src/rust/zcash_proofs/LICENSE-MIT b/src/rust/zcash_proofs/LICENSE-MIT new file mode 100644 index 000000000..5b7be8e66 --- /dev/null +++ b/src/rust/zcash_proofs/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Zcash Company + +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. diff --git a/src/rust/zcash_proofs/README.md b/src/rust/zcash_proofs/README.md new file mode 100644 index 000000000..92d9c7736 --- /dev/null +++ b/src/rust/zcash_proofs/README.md @@ -0,0 +1,21 @@ +# zcash_proofs + +This library contains the zk-SNARK circuits for Zcash, and the APIs for creating +and verifying proofs. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. + diff --git a/src/rust/zcash_proofs/src/lib.rs b/src/rust/zcash_proofs/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/src/rust/zcash_proofs/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/src/rust/zcash_wallet/Cargo.toml b/src/rust/zcash_wallet/Cargo.toml new file mode 100644 index 000000000..a7b6c91ea --- /dev/null +++ b/src/rust/zcash_wallet/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "zcash_wallet" +version = "0.0.0" +authors = [ + "Jack Grigg ", +] + +[dependencies] diff --git a/src/rust/zcash_wallet/LICENSE-APACHE b/src/rust/zcash_wallet/LICENSE-APACHE new file mode 100644 index 000000000..1e5006dc1 --- /dev/null +++ b/src/rust/zcash_wallet/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/src/rust/zcash_wallet/LICENSE-MIT b/src/rust/zcash_wallet/LICENSE-MIT new file mode 100644 index 000000000..5b7be8e66 --- /dev/null +++ b/src/rust/zcash_wallet/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Zcash Company + +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. diff --git a/src/rust/zcash_wallet/README.md b/src/rust/zcash_wallet/README.md new file mode 100644 index 000000000..14c1a389e --- /dev/null +++ b/src/rust/zcash_wallet/README.md @@ -0,0 +1,20 @@ +# zcash_wallet + +This library contains Rust structs and traits for creating shielded Zcash wallets. + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. + diff --git a/src/rust/zcash_wallet/src/lib.rs b/src/rust/zcash_wallet/src/lib.rs new file mode 100644 index 000000000..31e1bb209 --- /dev/null +++ b/src/rust/zcash_wallet/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/src/rust/zip32/.gitignore b/src/rust/zip32/.gitignore new file mode 100644 index 000000000..693699042 --- /dev/null +++ b/src/rust/zip32/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/src/rust/zip32/COPYRIGHT b/src/rust/zip32/COPYRIGHT new file mode 100644 index 000000000..e58e86893 --- /dev/null +++ b/src/rust/zip32/COPYRIGHT @@ -0,0 +1,14 @@ +Copyrights in the "zip32" library are retained by their contributors. No +copyright assignment is required to contribute to the "zip32" library. + +The "zip32" library is licensed under either of + + * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/zip32/Cargo.toml b/src/rust/zip32/Cargo.toml new file mode 100644 index 000000000..031d6364c --- /dev/null +++ b/src/rust/zip32/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "zip32" +version = "0.0.0" +authors = [ + "Jack Grigg ", +] +license = "MIT/Apache-2.0" + +description = "Library for implementing shielded hierarchical deterministic wallets" +documentation = "https://docs.rs/zip32/" +homepage = "https://github.com/zcash-hackworks/zip32" +repository = "https://github.com/zcash-hackworks/zip32" + +[dependencies] +aes = "0.2" +byteorder = "1" +fpe = "0.1" +lazy_static = "1.0" +pairing = { path = "../pairing" } +sapling-crypto = { path = "../sapling-crypto" } + +[dependencies.blake2-rfc] +git = "https://github.com/gtank/blake2-rfc" +rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" diff --git a/src/rust/zip32/LICENSE-APACHE b/src/rust/zip32/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/src/rust/zip32/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/src/rust/zip32/LICENSE-MIT b/src/rust/zip32/LICENSE-MIT new file mode 100644 index 000000000..31aa79387 --- /dev/null +++ b/src/rust/zip32/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/src/rust/zip32/README.md b/src/rust/zip32/README.md new file mode 100644 index 000000000..898d56cdf --- /dev/null +++ b/src/rust/zip32/README.md @@ -0,0 +1,17 @@ +# zip32 [![Crates.io](https://img.shields.io/crates/v/zip32.svg)](https://crates.io/crates/zip32) # + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/src/rust/zip32/src/lib.rs b/src/rust/zip32/src/lib.rs new file mode 100644 index 000000000..70173ea3b --- /dev/null +++ b/src/rust/zip32/src/lib.rs @@ -0,0 +1,1231 @@ +extern crate aes; +extern crate blake2_rfc; +extern crate byteorder; +extern crate fpe; +#[macro_use] +extern crate lazy_static; +extern crate pairing; +extern crate sapling_crypto; + +use aes::Aes256; +use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; +use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; +use fpe::ff1::{BinaryNumeralString, FF1}; +use pairing::{bls12_381::Bls12, Field, PrimeField, PrimeFieldRepr}; +use sapling_crypto::{ + jubjub::{ + edwards, FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, ToUniform, Unknown, + }, + primitives::{Diversifier, PaymentAddress, ViewingKey}, +}; +use std::io::{self, Read, Write}; + +lazy_static! { + static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; +} + +pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed"; +pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &'static [u8; 16] = b"ZcashIP32Sapling"; +pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &'static [u8; 16] = b"ZcashSaplingFVFP"; + +// Sapling key components + +/// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t) +fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bResult { + prf_expand_vec(sk, &vec![t]) +} + +fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> Blake2bResult { + let mut h = Blake2b::with_params(64, &[], &[], PRF_EXPAND_PERSONALIZATION); + h.update(sk); + for t in ts { + h.update(t); + } + h.finalize() +} + +/// An outgoing viewing key +#[derive(Clone, Copy, PartialEq)] +struct OutgoingViewingKey([u8; 32]); + +impl OutgoingViewingKey { + fn derive_child(&self, i_l: &[u8]) -> Self { + let mut ovk = [0u8; 32]; + ovk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x15], &self.0]).as_bytes()[..32]); + OutgoingViewingKey(ovk) + } +} + +/// A Sapling expanded spending key +#[derive(Clone)] +pub struct ExpandedSpendingKey { + ask: E::Fs, + nsk: E::Fs, + ovk: OutgoingViewingKey, +} + +/// A Sapling full viewing key +pub struct FullViewingKey { + vk: ViewingKey, + ovk: OutgoingViewingKey, +} + +impl ExpandedSpendingKey { + fn from_spending_key(sk: &[u8]) -> Self { + let ask = E::Fs::to_uniform(prf_expand(sk, &[0x00]).as_bytes()); + let nsk = E::Fs::to_uniform(prf_expand(sk, &[0x01]).as_bytes()); + let mut ovk = OutgoingViewingKey([0u8; 32]); + ovk.0 + .copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]); + ExpandedSpendingKey { ask, nsk, ovk } + } + + fn derive_child(&self, i_l: &[u8]) -> Self { + let mut ask = E::Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); + let mut nsk = E::Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); + ask.add_assign(&self.ask); + nsk.add_assign(&self.nsk); + let ovk = self.ovk.derive_child(i_l); + ExpandedSpendingKey { ask, nsk, ovk } + } + + pub fn read(mut reader: R) -> io::Result { + let mut ask_repr = ::Repr::default(); + ask_repr.read_le(&mut reader)?; + let ask = + E::Fs::from_repr(ask_repr).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let mut nsk_repr = ::Repr::default(); + nsk_repr.read_le(&mut reader)?; + let nsk = + E::Fs::from_repr(nsk_repr).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let mut ovk = [0; 32]; + reader.read_exact(&mut ovk)?; + + Ok(ExpandedSpendingKey { + ask, + nsk, + ovk: OutgoingViewingKey(ovk), + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + self.ask.into_repr().write_le(&mut writer)?; + self.nsk.into_repr().write_le(&mut writer)?; + writer.write_all(&self.ovk.0)?; + + Ok(()) + } + + fn to_bytes(&self) -> [u8; 96] { + let mut result = [0u8; 96]; + self.write(&mut result[..]) + .expect("should be able to serialize an ExpandedSpendingKey"); + result + } +} + +impl FullViewingKey { + fn from_expanded_spending_key(expsk: &ExpandedSpendingKey, params: &E::Params) -> Self { + FullViewingKey { + vk: ViewingKey { + ak: params + .generator(FixedGenerators::SpendingKeyGenerator) + .mul(expsk.ask, params), + nk: params + .generator(FixedGenerators::ProofGenerationKey) + .mul(expsk.nsk, params), + }, + ovk: expsk.ovk, + } + } + + fn derive_child(&self, i_l: &[u8], params: &E::Params) -> Self { + let i_ask = E::Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes()); + let i_nsk = E::Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes()); + let ak = params + .generator(FixedGenerators::SpendingKeyGenerator) + .mul(i_ask, params) + .add(&self.vk.ak, params); + let nk = params + .generator(FixedGenerators::ProofGenerationKey) + .mul(i_nsk, params) + .add(&self.vk.nk, params); + + FullViewingKey { + vk: ViewingKey { ak, nk }, + ovk: self.ovk.derive_child(i_l), + } + } + + pub fn read(mut reader: R, params: &E::Params) -> io::Result { + let ak = edwards::Point::::read(&mut reader, params)?; + let ak = match ak.as_prime_order(params) { + Some(p) => p, + None => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "ak not of prime order", + )) + } + }; + + let nk = edwards::Point::::read(&mut reader, params)?; + let nk = match nk.as_prime_order(params) { + Some(p) => p, + None => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "nk not of prime order", + )) + } + }; + + let mut ovk = [0; 32]; + reader.read_exact(&mut ovk)?; + + Ok(FullViewingKey { + vk: ViewingKey { ak, nk }, + ovk: OutgoingViewingKey(ovk), + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + self.vk.ak.write(&mut writer)?; + self.vk.nk.write(&mut writer)?; + writer.write_all(&self.ovk.0)?; + + Ok(()) + } + + fn to_bytes(&self) -> [u8; 96] { + let mut result = [0u8; 96]; + self.write(&mut result[..]) + .expect("should be able to serialize a FullViewingKey"); + result + } + + fn fingerprint(&self) -> FVKFingerprint { + let mut h = Blake2b::with_params(32, &[], &[], ZIP32_SAPLING_FVFP_PERSONALIZATION); + h.update(&self.to_bytes()); + let mut fvfp = [0u8; 32]; + fvfp.copy_from_slice(h.finalize().as_bytes()); + FVKFingerprint(fvfp) + } +} + +// ZIP 32 structures + +/// A Sapling full viewing key fingerprint +struct FVKFingerprint([u8; 32]); + +/// A Sapling full viewing key tag +#[derive(Clone, Copy, Debug, PartialEq)] +struct FVKTag([u8; 4]); + +impl FVKFingerprint { + fn tag(&self) -> FVKTag { + let mut tag = [0u8; 4]; + tag.copy_from_slice(&self.0[..4]); + FVKTag(tag) + } +} + +impl FVKTag { + fn master() -> Self { + FVKTag([0u8; 4]) + } +} + +/// A child index for a derived key +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum ChildIndex { + NonHardened(u32), + Hardened(u32), // Hardened(n) == n + (1 << 31) == n' in path notation +} + +impl ChildIndex { + pub fn from_index(i: u32) -> Self { + match i { + n if n >= (1 << 31) => ChildIndex::Hardened(n - (1 << 31)), + n => ChildIndex::NonHardened(n), + } + } + + fn master() -> Self { + ChildIndex::from_index(0) + } + + fn to_index(&self) -> u32 { + match self { + &ChildIndex::Hardened(i) => i + (1 << 31), + &ChildIndex::NonHardened(i) => i, + } + } +} + +/// A chain code +#[derive(Clone, Copy, Debug, PartialEq)] +struct ChainCode([u8; 32]); + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct DiversifierIndex(pub [u8; 11]); + +impl DiversifierIndex { + fn new() -> Self { + DiversifierIndex([0; 11]) + } + + pub fn increment(&mut self) -> Result<(), ()> { + for k in 0..11 { + self.0[k] = self.0[k].wrapping_add(1); + if self.0[k] != 0 { + // No overflow + return Ok(()); + } + } + // Overflow + Err(()) + } +} + +/// A key used to derive diversifiers for a particular child key +#[derive(Clone, Copy, Debug, PartialEq)] +struct DiversifierKey([u8; 32]); + +impl DiversifierKey { + fn master(sk_m: &[u8]) -> Self { + let mut dk_m = [0u8; 32]; + dk_m.copy_from_slice(&prf_expand(sk_m, &[0x10]).as_bytes()[..32]); + DiversifierKey(dk_m) + } + + fn derive_child(&self, i_l: &[u8]) -> Self { + let mut dk = [0u8; 32]; + dk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x16], &self.0]).as_bytes()[..32]); + DiversifierKey(dk) + } + + /// Returns the first index starting from j that generates a valid + /// diversifier, along with the corresponding diversifier. Returns + /// an error if the diversifier space is exhausted. + fn diversifier(&self, mut j: DiversifierIndex) -> Result<(DiversifierIndex, Diversifier), ()> { + let ff = FF1::::new(&self.0, 2).unwrap(); + loop { + // Generate d_j + let enc = ff + .encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.0[..])) + .unwrap(); + let mut d_j = [0; 11]; + d_j.copy_from_slice(&enc.to_bytes_le()); + let d_j = Diversifier(d_j); + + // Return (j, d_j) if valid, else increment j and try again + match d_j.g_d::(&JUBJUB) { + Some(_) => return Ok((j, d_j)), + None => if j.increment().is_err() { + return Err(()); + }, + } + } + } +} + +/// A Sapling extended spending key +#[derive(Clone)] +pub struct ExtendedSpendingKey { + depth: u8, + parent_fvk_tag: FVKTag, + child_index: ChildIndex, + chain_code: ChainCode, + pub expsk: ExpandedSpendingKey, + dk: DiversifierKey, +} + +// A Sapling extended full viewing key +pub struct ExtendedFullViewingKey { + depth: u8, + parent_fvk_tag: FVKTag, + child_index: ChildIndex, + chain_code: ChainCode, + pub fvk: FullViewingKey, + dk: DiversifierKey, +} + +impl std::cmp::PartialEq for ExtendedSpendingKey { + fn eq(&self, rhs: &ExtendedSpendingKey) -> bool { + self.depth == rhs.depth + && self.parent_fvk_tag == rhs.parent_fvk_tag + && self.child_index == rhs.child_index + && self.chain_code == rhs.chain_code + && self.expsk.ask == rhs.expsk.ask + && self.expsk.nsk == rhs.expsk.nsk + && self.expsk.ovk == rhs.expsk.ovk + && self.dk == rhs.dk + } +} + +impl std::fmt::Debug for ExtendedSpendingKey { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!( + f, + "ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})", + self.depth, self.parent_fvk_tag, self.child_index + ) + } +} + +impl std::cmp::PartialEq for ExtendedFullViewingKey { + fn eq(&self, rhs: &ExtendedFullViewingKey) -> bool { + self.depth == rhs.depth + && self.parent_fvk_tag == rhs.parent_fvk_tag + && self.child_index == rhs.child_index + && self.chain_code == rhs.chain_code + && self.fvk.vk.ak == rhs.fvk.vk.ak + && self.fvk.vk.nk == rhs.fvk.vk.nk + && self.fvk.ovk == rhs.fvk.ovk + && self.dk == rhs.dk + } +} + +impl std::fmt::Debug for ExtendedFullViewingKey { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!( + f, + "ExtendedFullViewingKey(d = {}, tag_p = {:?}, i = {:?})", + self.depth, self.parent_fvk_tag, self.child_index + ) + } +} + +impl ExtendedSpendingKey { + pub fn master(seed: &[u8]) -> Self { + let mut h = Blake2b::with_params(64, &[], &[], ZIP32_SAPLING_MASTER_PERSONALIZATION); + h.update(seed); + let i = h.finalize(); + + let sk_m = &i.as_bytes()[..32]; + let mut c_m = [0u8; 32]; + c_m.copy_from_slice(&i.as_bytes()[32..]); + + ExtendedSpendingKey { + depth: 0, + parent_fvk_tag: FVKTag::master(), + child_index: ChildIndex::master(), + chain_code: ChainCode(c_m), + expsk: ExpandedSpendingKey::from_spending_key(sk_m), + dk: DiversifierKey::master(sk_m), + } + } + + pub fn read(mut reader: R) -> io::Result { + let depth = reader.read_u8()?; + let mut tag = [0; 4]; + reader.read_exact(&mut tag)?; + let i = reader.read_u32::()?; + let mut c = [0; 32]; + reader.read_exact(&mut c)?; + let expsk = ExpandedSpendingKey::read(&mut reader)?; + let mut dk = [0; 32]; + reader.read_exact(&mut dk)?; + + Ok(ExtendedSpendingKey { + depth, + parent_fvk_tag: FVKTag(tag), + child_index: ChildIndex::from_index(i), + chain_code: ChainCode(c), + expsk, + dk: DiversifierKey(dk), + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_u8(self.depth)?; + writer.write_all(&self.parent_fvk_tag.0)?; + writer.write_u32::(self.child_index.to_index())?; + writer.write_all(&self.chain_code.0)?; + writer.write_all(&self.expsk.to_bytes())?; + writer.write_all(&self.dk.0)?; + + Ok(()) + } + + /// Returns the child key corresponding to the path derived from the master key + pub fn from_path(master: &ExtendedSpendingKey, path: &[ChildIndex]) -> Self { + let mut xsk = master.clone(); + for &i in path.iter() { + xsk = xsk.derive_child(i); + } + xsk + } + + pub fn derive_child(&self, i: ChildIndex) -> Self { + let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk, &JUBJUB); + let tmp = match i { + ChildIndex::Hardened(i) => { + let mut le_i = [0; 4]; + LittleEndian::write_u32(&mut le_i, i + (1 << 31)); + prf_expand_vec( + &self.chain_code.0, + &[&[0x11], &self.expsk.to_bytes(), &self.dk.0, &le_i], + ) + } + ChildIndex::NonHardened(i) => { + let mut le_i = [0; 4]; + LittleEndian::write_u32(&mut le_i, i); + prf_expand_vec( + &self.chain_code.0, + &[&[0x12], &fvk.to_bytes(), &self.dk.0, &le_i], + ) + } + }; + let i_l = &tmp.as_bytes()[..32]; + let mut c_i = [0u8; 32]; + c_i.copy_from_slice(&tmp.as_bytes()[32..]); + + ExtendedSpendingKey { + depth: self.depth + 1, + parent_fvk_tag: fvk.fingerprint().tag(), + child_index: i, + chain_code: ChainCode(c_i), + expsk: self.expsk.derive_child(i_l), + dk: self.dk.derive_child(i_l), + } + } + + pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { + ExtendedFullViewingKey::from(self).default_address() + } +} + +impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { + fn from(xsk: &ExtendedSpendingKey) -> Self { + ExtendedFullViewingKey { + depth: xsk.depth, + parent_fvk_tag: xsk.parent_fvk_tag, + child_index: xsk.child_index, + chain_code: xsk.chain_code, + fvk: FullViewingKey::from_expanded_spending_key(&xsk.expsk, &JUBJUB), + dk: xsk.dk, + } + } +} + +impl ExtendedFullViewingKey { + pub fn read(mut reader: R) -> io::Result { + let depth = reader.read_u8()?; + let mut tag = [0; 4]; + reader.read_exact(&mut tag)?; + let i = reader.read_u32::()?; + let mut c = [0; 32]; + reader.read_exact(&mut c)?; + let fvk = FullViewingKey::read(&mut reader, &*JUBJUB)?; + let mut dk = [0; 32]; + reader.read_exact(&mut dk)?; + + Ok(ExtendedFullViewingKey { + depth, + parent_fvk_tag: FVKTag(tag), + child_index: ChildIndex::from_index(i), + chain_code: ChainCode(c), + fvk, + dk: DiversifierKey(dk), + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_u8(self.depth)?; + writer.write_all(&self.parent_fvk_tag.0)?; + writer.write_u32::(self.child_index.to_index())?; + writer.write_all(&self.chain_code.0)?; + writer.write_all(&self.fvk.to_bytes())?; + writer.write_all(&self.dk.0)?; + + Ok(()) + } + + pub fn derive_child(&self, i: ChildIndex) -> Result { + let tmp = match i { + ChildIndex::Hardened(_) => return Err(()), + ChildIndex::NonHardened(i) => { + let mut le_i = [0; 4]; + LittleEndian::write_u32(&mut le_i, i); + prf_expand_vec( + &self.chain_code.0, + &[&[0x12], &self.fvk.to_bytes(), &self.dk.0, &le_i], + ) + } + }; + let i_l = &tmp.as_bytes()[..32]; + let mut c_i = [0u8; 32]; + c_i.copy_from_slice(&tmp.as_bytes()[32..]); + + Ok(ExtendedFullViewingKey { + depth: self.depth + 1, + parent_fvk_tag: self.fvk.fingerprint().tag(), + child_index: i, + chain_code: ChainCode(c_i), + fvk: self.fvk.derive_child(i_l, &JUBJUB), + dk: self.dk.derive_child(i_l), + }) + } + + pub fn address( + &self, + j: DiversifierIndex, + ) -> Result<(DiversifierIndex, PaymentAddress), ()> { + let (j, d_j) = match self.dk.diversifier(j) { + Ok(ret) => ret, + Err(()) => return Err(()), + }; + match self.fvk.vk.into_payment_address(d_j, &JUBJUB) { + Some(addr) => Ok((j, addr)), + None => Err(()), + } + } + + pub fn default_address(&self) -> Result<(DiversifierIndex, PaymentAddress), ()> { + self.address(DiversifierIndex::new()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn derive_nonhardened_child() { + let seed = [0; 32]; + let xsk_m = ExtendedSpendingKey::master(&seed); + let xfvk_m = ExtendedFullViewingKey::from(&xsk_m); + + let i_5 = ChildIndex::NonHardened(5); + let xsk_5 = xsk_m.derive_child(i_5); + let xfvk_5 = xfvk_m.derive_child(i_5); + + assert!(xfvk_5.is_ok()); + assert_eq!(ExtendedFullViewingKey::from(&xsk_5), xfvk_5.unwrap()); + } + + #[test] + fn derive_hardened_child() { + let seed = [0; 32]; + let xsk_m = ExtendedSpendingKey::master(&seed); + let xfvk_m = ExtendedFullViewingKey::from(&xsk_m); + + let i_5h = ChildIndex::Hardened(5); + let xsk_5h = xsk_m.derive_child(i_5h); + let xfvk_5h = xfvk_m.derive_child(i_5h); + + // Cannot derive a hardened child from an ExtendedFullViewingKey + assert!(xfvk_5h.is_err()); + let xfvk_5h = ExtendedFullViewingKey::from(&xsk_5h); + + let i_7 = ChildIndex::NonHardened(7); + let xsk_5h_7 = xsk_5h.derive_child(i_7); + let xfvk_5h_7 = xfvk_5h.derive_child(i_7); + + // But we *can* derive a non-hardened child from a hardened parent + assert!(xfvk_5h_7.is_ok()); + assert_eq!(ExtendedFullViewingKey::from(&xsk_5h_7), xfvk_5h_7.unwrap()); + } + + #[test] + fn path() { + let seed = [0; 32]; + let xsk_m = ExtendedSpendingKey::master(&seed); + + let xsk_5h = xsk_m.derive_child(ChildIndex::Hardened(5)); + assert_eq!( + ExtendedSpendingKey::from_path(&xsk_m, &[ChildIndex::Hardened(5)]), + xsk_5h + ); + + let xsk_5h_7 = xsk_5h.derive_child(ChildIndex::NonHardened(7)); + assert_eq!( + ExtendedSpendingKey::from_path( + &xsk_m, + &[ChildIndex::Hardened(5), ChildIndex::NonHardened(7)] + ), + xsk_5h_7 + ); + } + + #[test] + fn diversifier() { + let dk = DiversifierKey([0; 32]); + let j_0 = DiversifierIndex::new(); + let j_1 = DiversifierIndex([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_2 = DiversifierIndex([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let j_3 = DiversifierIndex([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + // Computed using this Rust implementation + let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140]; + let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34]; + + // j = 0 + let (j, d_j) = dk.diversifier(j_0).unwrap(); + assert_eq!(j, j_0); + assert_eq!(d_j.0, d_0); + + // j = 1 + let (j, d_j) = dk.diversifier(j_1).unwrap(); + assert_eq!(j, j_3); + assert_eq!(d_j.0, d_3); + + // j = 2 + let (j, d_j) = dk.diversifier(j_2).unwrap(); + assert_eq!(j, j_3); + assert_eq!(d_j.0, d_3); + + // j = 3 + let (j, d_j) = dk.diversifier(j_3).unwrap(); + assert_eq!(j, j_3); + assert_eq!(d_j.0, d_3); + } + + #[test] + fn default_address() { + let seed = [0; 32]; + let xsk_m = ExtendedSpendingKey::master(&seed); + let (j_m, addr_m) = xsk_m.default_address().unwrap(); + assert_eq!(j_m.0, [0; 11]); + assert_eq!( + addr_m.diversifier.0, + // Computed using this Rust implementation + [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19] + ); + } + + #[test] + fn read_write() { + let seed = [0; 32]; + let xsk = ExtendedSpendingKey::master(&seed); + let fvk = ExtendedFullViewingKey::from(&xsk); + + let mut ser = vec![]; + xsk.write(&mut ser).unwrap(); + let xsk2 = ExtendedSpendingKey::read(&ser[..]).unwrap(); + assert_eq!(xsk2, xsk); + + let mut ser = vec![]; + fvk.write(&mut ser).unwrap(); + let fvk2 = ExtendedFullViewingKey::read(&ser[..]).unwrap(); + assert_eq!(fvk2, fvk); + } + + #[test] + fn test_vectors() { + struct TestVector { + ask: Option<[u8; 32]>, + nsk: Option<[u8; 32]>, + ovk: [u8; 32], + dk: [u8; 32], + c: [u8; 32], + ak: [u8; 32], + nk: [u8; 32], + ivk: [u8; 32], + xsk: Option<[u8; 169]>, + xfvk: [u8; 169], + fp: [u8; 32], + d0: Option<[u8; 11]>, + d1: Option<[u8; 11]>, + d2: Option<[u8; 11]>, + dmax: Option<[u8; 11]>, + }; + + // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_zip32.py + let test_vectors = vec![ + TestVector { + ask: Some([ + 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9, 0x9e, 0x86, + 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b, 0x51, 0xf6, + 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, + ]), + nsk: Some([ + 0x82, 0x04, 0xed, 0xe8, 0x3b, 0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f, + 0x99, 0x6e, 0x2e, 0xbd, 0x0a, 0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f, + 0x74, 0x8a, 0x88, 0x21, 0xea, 0x06, + ]), + ovk: [ + 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, 0xb8, + 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, 0x83, + 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, + ], + dk: [ + 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, + 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, + 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72, + ], + c: [ + 0xd0, 0x94, 0x7c, 0x4b, 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, + 0x6d, 0x1c, 0xf3, 0xfd, 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, + 0x0d, 0x75, 0x20, 0x18, 0x66, 0x8e, + ], + ak: [ + 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72, 0x02, 0xdc, + 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc, 0xe2, 0x64, + 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, + ], + nk: [ + 0xdc, 0xe8, 0xe7, 0xed, 0xec, 0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba, + 0x57, 0x69, 0x1b, 0x78, 0x3c, 0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93, + 0xdc, 0xeb, 0x67, 0xb1, 0x01, 0x06, + ], + ivk: [ + 0x48, 0x47, 0xa1, 0x30, 0xe7, 0x99, 0xd3, 0xdb, 0xea, 0x36, 0xa1, 0xc1, 0x64, + 0x67, 0xd6, 0x21, 0xfb, 0x2d, 0x80, 0xe3, 0x0b, 0x3b, 0x1d, 0x1a, 0x42, 0x68, + 0x93, 0x41, 0x5d, 0xad, 0x66, 0x01, + ], + xsk: Some([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b, + 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd, + 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18, + 0x66, 0x8e, 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9, + 0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b, + 0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, 0x82, 0x04, 0xed, 0xe8, 0x3b, + 0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f, 0x99, 0x6e, 0x2e, 0xbd, 0x0a, + 0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f, 0x74, 0x8a, 0x88, 0x21, 0xea, + 0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, + 0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, + 0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, + 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, + 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72, + ]), + xfvk: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b, + 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd, + 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18, + 0x66, 0x8e, 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72, + 0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc, + 0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, 0xdc, 0xe8, 0xe7, 0xed, 0xec, + 0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba, 0x57, 0x69, 0x1b, 0x78, 0x3c, + 0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93, 0xdc, 0xeb, 0x67, 0xb1, 0x01, + 0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, + 0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, + 0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, + 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, + 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72, + ], + fp: [ + 0x14, 0xc2, 0x71, 0x3a, 0xdc, 0xe9, 0x3a, 0x83, 0x0e, 0xa8, 0x3a, 0x05, 0x19, + 0x08, 0xb7, 0x44, 0x77, 0x83, 0xf5, 0xd1, 0x06, 0xc0, 0x98, 0x5e, 0x02, 0x55, + 0x0e, 0x42, 0x6f, 0x27, 0x59, 0x7c, + ], + d0: Some([ + 0xd8, 0x62, 0x1b, 0x98, 0x1c, 0xf3, 0x00, 0xe9, 0xd4, 0xcc, 0x89, + ]), + d1: Some([ + 0x48, 0xea, 0x17, 0xa1, 0x99, 0xc8, 0x4b, 0xd1, 0xba, 0xa5, 0xd4, + ]), + d2: None, + dmax: None, + }, + TestVector { + ask: Some([ + 0x28, 0x2b, 0xc1, 0x97, 0xa5, 0x16, 0x28, 0x7c, 0x8e, 0xa8, 0xf6, 0x8c, 0x42, + 0x4a, 0xba, 0xd3, 0x02, 0xb4, 0x5c, 0xdf, 0x95, 0x40, 0x79, 0x61, 0xd7, 0xb8, + 0xb4, 0x55, 0x26, 0x7a, 0x35, 0x0c, + ]), + nsk: Some([ + 0xe7, 0xa3, 0x29, 0x88, 0xfd, 0xca, 0x1e, 0xfc, 0xd6, 0xd1, 0xc4, 0xc5, 0x62, + 0xe6, 0x29, 0xc2, 0xe9, 0x6b, 0x2c, 0x3f, 0x7e, 0xda, 0x04, 0xac, 0x4e, 0xfd, + 0x18, 0x10, 0xff, 0x6b, 0xba, 0x01, + ]), + ovk: [ + 0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef, 0xcf, + 0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8, 0x1b, + 0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74, + ], + dk: [ + 0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7, 0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b, + 0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7, 0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51, + 0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc, + ], + c: [ + 0x01, 0x47, 0x11, 0x0c, 0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, + 0xc5, 0xe7, 0x90, 0xa5, 0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, + 0x8a, 0x67, 0x05, 0xda, 0xbc, 0xe6, + ], + ak: [ + 0xdc, 0x14, 0xb5, 0x14, 0xd3, 0xa9, 0x25, 0x94, 0xc2, 0x19, 0x25, 0xaf, 0x2f, + 0x77, 0x65, 0xa5, 0x47, 0xb3, 0x0e, 0x73, 0xfa, 0x7b, 0x70, 0x0e, 0xa1, 0xbf, + 0xf2, 0xe5, 0xef, 0xaa, 0xa8, 0x8b, + ], + nk: [ + 0x61, 0x52, 0xeb, 0x7f, 0xdb, 0x25, 0x27, 0x79, 0xdd, 0xcb, 0x95, 0xd2, 0x17, + 0xea, 0x4b, 0x6f, 0xd3, 0x40, 0x36, 0xe9, 0xad, 0xad, 0xb3, 0xb5, 0xc9, 0xcb, + 0xec, 0xeb, 0x41, 0xba, 0x45, 0x2a, + ], + ivk: [ + 0x15, 0x5a, 0x8e, 0xe2, 0x05, 0xd3, 0x87, 0x2d, 0x12, 0xf8, 0xa3, 0xe6, 0x39, + 0x91, 0x46, 0x33, 0xc2, 0x3c, 0xde, 0x1f, 0x30, 0xed, 0x50, 0x51, 0xe5, 0x21, + 0x30, 0xb1, 0xd0, 0x10, 0x4c, 0x06, + ], + xsk: Some([ + 0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x47, 0x11, 0x0c, + 0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5, + 0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda, + 0xbc, 0xe6, 0x28, 0x2b, 0xc1, 0x97, 0xa5, 0x16, 0x28, 0x7c, 0x8e, 0xa8, 0xf6, + 0x8c, 0x42, 0x4a, 0xba, 0xd3, 0x02, 0xb4, 0x5c, 0xdf, 0x95, 0x40, 0x79, 0x61, + 0xd7, 0xb8, 0xb4, 0x55, 0x26, 0x7a, 0x35, 0x0c, 0xe7, 0xa3, 0x29, 0x88, 0xfd, + 0xca, 0x1e, 0xfc, 0xd6, 0xd1, 0xc4, 0xc5, 0x62, 0xe6, 0x29, 0xc2, 0xe9, 0x6b, + 0x2c, 0x3f, 0x7e, 0xda, 0x04, 0xac, 0x4e, 0xfd, 0x18, 0x10, 0xff, 0x6b, 0xba, + 0x01, 0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef, + 0xcf, 0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8, + 0x1b, 0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74, 0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7, + 0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b, 0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7, + 0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51, 0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc, + ]), + xfvk: [ + 0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x00, 0x01, 0x47, 0x11, 0x0c, + 0x69, 0x1a, 0x03, 0xb9, 0xd9, 0xf0, 0xba, 0x90, 0x05, 0xc5, 0xe7, 0x90, 0xa5, + 0x95, 0xb7, 0xf0, 0x4e, 0x33, 0x29, 0xd2, 0xfa, 0x43, 0x8a, 0x67, 0x05, 0xda, + 0xbc, 0xe6, 0xdc, 0x14, 0xb5, 0x14, 0xd3, 0xa9, 0x25, 0x94, 0xc2, 0x19, 0x25, + 0xaf, 0x2f, 0x77, 0x65, 0xa5, 0x47, 0xb3, 0x0e, 0x73, 0xfa, 0x7b, 0x70, 0x0e, + 0xa1, 0xbf, 0xf2, 0xe5, 0xef, 0xaa, 0xa8, 0x8b, 0x61, 0x52, 0xeb, 0x7f, 0xdb, + 0x25, 0x27, 0x79, 0xdd, 0xcb, 0x95, 0xd2, 0x17, 0xea, 0x4b, 0x6f, 0xd3, 0x40, + 0x36, 0xe9, 0xad, 0xad, 0xb3, 0xb5, 0xc9, 0xcb, 0xec, 0xeb, 0x41, 0xba, 0x45, + 0x2a, 0x5f, 0x13, 0x81, 0xfc, 0x88, 0x86, 0xda, 0x6a, 0x02, 0xdf, 0xfe, 0xef, + 0xcf, 0x50, 0x3c, 0x40, 0xfa, 0x8f, 0x5a, 0x36, 0xf7, 0xa7, 0x14, 0x2f, 0xd8, + 0x1b, 0x55, 0x18, 0xc5, 0xa4, 0x74, 0x74, 0xe0, 0x4d, 0xe8, 0x32, 0xa2, 0xd7, + 0x91, 0xec, 0x12, 0x9a, 0xb9, 0x00, 0x2b, 0x91, 0xc9, 0xe9, 0xcd, 0xee, 0xd7, + 0x92, 0x41, 0xa7, 0xc4, 0x96, 0x0e, 0x51, 0x78, 0xd8, 0x70, 0xc1, 0xb4, 0xdc, + ], + fp: [ + 0xdb, 0x99, 0x9e, 0x07, 0x1d, 0xcb, 0x58, 0xdd, 0x93, 0x02, 0x9a, 0xe6, 0x97, + 0x05, 0x3e, 0x90, 0xed, 0xb3, 0x59, 0xd1, 0xa1, 0xb7, 0xa1, 0x25, 0x16, 0x7e, + 0xfb, 0xe9, 0x28, 0x06, 0x84, 0x23, + ], + d0: Some([ + 0x8b, 0x41, 0x38, 0x32, 0x0d, 0xfa, 0xfd, 0x7b, 0x39, 0x97, 0x81, + ]), + d1: None, + d2: Some([ + 0x57, 0x49, 0xa1, 0x33, 0x52, 0xbc, 0x22, 0x3e, 0x30, 0x80, 0x78, + ]), + dmax: Some([ + 0x63, 0x89, 0x57, 0x4c, 0xde, 0x0f, 0xbb, 0xc6, 0x36, 0x81, 0x31, + ]), + }, + TestVector { + ask: Some([ + 0x8b, 0xe8, 0x11, 0x3c, 0xee, 0x34, 0x13, 0xa7, 0x1f, 0x82, 0xc4, 0x1f, 0xc8, + 0xda, 0x51, 0x7b, 0xe1, 0x34, 0x04, 0x98, 0x32, 0xe6, 0x82, 0x5c, 0x92, 0xda, + 0x6b, 0x84, 0xfe, 0xe4, 0xc6, 0x0d, + ]), + nsk: Some([ + 0x37, 0x78, 0x05, 0x9d, 0xc5, 0x69, 0xe7, 0xd0, 0xd3, 0x23, 0x91, 0x57, 0x3f, + 0x95, 0x1b, 0xbd, 0xe9, 0x2f, 0xc6, 0xb9, 0xcf, 0x61, 0x47, 0x73, 0x66, 0x1c, + 0x5c, 0x27, 0x3a, 0xa6, 0x99, 0x0c, + ]), + ovk: [ + 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47, + 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93, + 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, + ], + dk: [ + 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, + 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, + 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, + ], + c: [ + 0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, + 0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, + 0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35, + ], + ak: [ + 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, 0x3a, 0x49, + 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, 0x52, 0x0e, + 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, + ], + nk: [ + 0x30, 0x4e, 0x30, 0x59, 0x16, 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, + 0x50, 0xec, 0xd1, 0x88, 0xfc, 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, + 0x30, 0x77, 0x25, 0xe2, 0xee, 0x11, + ], + ivk: [ + 0xa2, 0xa1, 0x3c, 0x1e, 0x38, 0xb4, 0x59, 0x84, 0x44, 0x58, 0x03, 0xe4, 0x30, + 0xa6, 0x83, 0xc9, 0x0b, 0xb2, 0xe1, 0x4d, 0x4c, 0x86, 0x92, 0xff, 0x25, 0x3a, + 0x64, 0x84, 0xdd, 0x9b, 0xb5, 0x04, + ], + xsk: Some([ + 0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4, + 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3, + 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8, + 0xd4, 0x35, 0x8b, 0xe8, 0x11, 0x3c, 0xee, 0x34, 0x13, 0xa7, 0x1f, 0x82, 0xc4, + 0x1f, 0xc8, 0xda, 0x51, 0x7b, 0xe1, 0x34, 0x04, 0x98, 0x32, 0xe6, 0x82, 0x5c, + 0x92, 0xda, 0x6b, 0x84, 0xfe, 0xe4, 0xc6, 0x0d, 0x37, 0x78, 0x05, 0x9d, 0xc5, + 0x69, 0xe7, 0xd0, 0xd3, 0x23, 0x91, 0x57, 0x3f, 0x95, 0x1b, 0xbd, 0xe9, 0x2f, + 0xc6, 0xb9, 0xcf, 0x61, 0x47, 0x73, 0x66, 0x1c, 0x5c, 0x27, 0x3a, 0xa6, 0x99, + 0x0c, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, + 0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, + 0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, + 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, + 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, + ]), + xfvk: [ + 0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4, + 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3, + 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8, + 0xd4, 0x35, 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, + 0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, + 0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, 0x30, 0x4e, 0x30, 0x59, 0x16, + 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, 0x50, 0xec, 0xd1, 0x88, 0xfc, + 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, 0x30, 0x77, 0x25, 0xe2, 0xee, + 0x11, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, + 0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, + 0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, + 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, + 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, + ], + fp: [ + 0x48, 0xc1, 0x83, 0x75, 0x7b, 0x5d, 0xa6, 0x61, 0x2a, 0x81, 0xb3, 0x0e, 0x40, + 0xb4, 0xac, 0xaa, 0x2d, 0x9e, 0x73, 0x95, 0x12, 0xe1, 0xd2, 0xd0, 0x01, 0x0e, + 0x92, 0xa7, 0xf7, 0xf2, 0xfc, 0xdf, + ], + d0: Some([ + 0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41, + ]), + d1: Some([ + 0x02, 0x0a, 0x7a, 0x6b, 0x0b, 0xf8, 0x4d, 0x3e, 0x89, 0x9f, 0x68, + ]), + d2: None, + dmax: None, + }, + TestVector { + ask: None, + nsk: None, + ovk: [ + 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, 0x47, + 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, 0x93, + 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, + ], + dk: [ + 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, + 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, + 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, + ], + c: [ + 0x97, 0xce, 0x15, 0xf4, 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, + 0xcb, 0x3d, 0xc9, 0xb3, 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, + 0x77, 0x73, 0x83, 0xa8, 0xd4, 0x35, + ], + ak: [ + 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, 0x3a, 0x49, + 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, 0x52, 0x0e, + 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, + ], + nk: [ + 0x30, 0x4e, 0x30, 0x59, 0x16, 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, + 0x50, 0xec, 0xd1, 0x88, 0xfc, 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, + 0x30, 0x77, 0x25, 0xe2, 0xee, 0x11, + ], + ivk: [ + 0xa2, 0xa1, 0x3c, 0x1e, 0x38, 0xb4, 0x59, 0x84, 0x44, 0x58, 0x03, 0xe4, 0x30, + 0xa6, 0x83, 0xc9, 0x0b, 0xb2, 0xe1, 0x4d, 0x4c, 0x86, 0x92, 0xff, 0x25, 0x3a, + 0x64, 0x84, 0xdd, 0x9b, 0xb5, 0x04, + ], + xsk: None, + xfvk: [ + 0x02, 0xdb, 0x99, 0x9e, 0x07, 0x02, 0x00, 0x00, 0x80, 0x97, 0xce, 0x15, 0xf4, + 0xed, 0x1b, 0x97, 0x39, 0xb0, 0x26, 0x2a, 0x46, 0x3b, 0xcb, 0x3d, 0xc9, 0xb3, + 0xbd, 0x23, 0x23, 0xa9, 0xba, 0xa4, 0x41, 0xca, 0x42, 0x77, 0x73, 0x83, 0xa8, + 0xd4, 0x35, 0xa6, 0xc5, 0x92, 0x5a, 0x0f, 0x85, 0xfa, 0x4f, 0x1e, 0x40, 0x5e, + 0x3a, 0x49, 0x70, 0xd0, 0xc4, 0xa4, 0xb4, 0x81, 0x44, 0x38, 0xf4, 0xe9, 0xd4, + 0x52, 0x0e, 0x20, 0xf7, 0xfd, 0xcf, 0x38, 0x41, 0x30, 0x4e, 0x30, 0x59, 0x16, + 0x21, 0x6b, 0xeb, 0x7b, 0x65, 0x4d, 0x8a, 0xae, 0x50, 0xec, 0xd1, 0x88, 0xfc, + 0xb3, 0x84, 0xbc, 0x36, 0xc0, 0x0c, 0x66, 0x4f, 0x30, 0x77, 0x25, 0xe2, 0xee, + 0x11, 0xcf, 0x81, 0x18, 0x2e, 0x96, 0x22, 0x3c, 0x02, 0x8c, 0xe3, 0xd6, 0xeb, + 0x47, 0x94, 0xd3, 0x11, 0x3b, 0x95, 0x06, 0x9d, 0x14, 0xc5, 0x75, 0x88, 0xe1, + 0x93, 0xb6, 0x5e, 0xfc, 0x28, 0x13, 0xbc, 0xa3, 0xed, 0xa1, 0x9f, 0x9e, 0xff, + 0x46, 0xca, 0x12, 0xdf, 0xa1, 0xbf, 0x10, 0x37, 0x1b, 0x48, 0xd1, 0xb4, 0xa4, + 0x0c, 0x4d, 0x05, 0xa0, 0xd8, 0xdc, 0xe0, 0xe7, 0xdc, 0x62, 0xb0, 0x7b, 0x37, + ], + fp: [ + 0x48, 0xc1, 0x83, 0x75, 0x7b, 0x5d, 0xa6, 0x61, 0x2a, 0x81, 0xb3, 0x0e, 0x40, + 0xb4, 0xac, 0xaa, 0x2d, 0x9e, 0x73, 0x95, 0x12, 0xe1, 0xd2, 0xd0, 0x01, 0x0e, + 0x92, 0xa7, 0xf7, 0xf2, 0xfc, 0xdf, + ], + d0: Some([ + 0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41, + ]), + d1: Some([ + 0x02, 0x0a, 0x7a, 0x6b, 0x0b, 0xf8, 0x4d, 0x3e, 0x89, 0x9f, 0x68, + ]), + d2: None, + dmax: None, + }, + TestVector { + ask: None, + nsk: None, + ovk: [ + 0x69, 0xb9, 0xe0, 0xfa, 0x1c, 0x4b, 0x3d, 0xeb, 0x91, 0xd5, 0x3b, 0xee, 0xe8, + 0x71, 0x15, 0x61, 0x21, 0x47, 0x4b, 0x8b, 0x62, 0xef, 0x24, 0x13, 0x44, 0x78, + 0xdc, 0x34, 0x99, 0x69, 0x1a, 0xf6, + ], + dk: [ + 0xbe, 0xcb, 0x50, 0xc3, 0x63, 0xbb, 0x2e, 0xd9, 0xda, 0x5c, 0x30, 0x43, 0xce, + 0xb0, 0xf1, 0xa0, 0x52, 0x7b, 0xf8, 0x36, 0xb2, 0x9a, 0x35, 0xf7, 0xc0, 0xc9, + 0xf2, 0x61, 0x12, 0x3b, 0xe5, 0x6e, + ], + c: [ + 0x8d, 0x93, 0x7b, 0xcf, 0x81, 0xba, 0x43, 0x0d, 0x5b, 0x49, 0xaf, 0xc0, 0xa4, + 0x03, 0x36, 0x7b, 0x1f, 0xd9, 0x98, 0x79, 0xec, 0xba, 0x41, 0xbe, 0x05, 0x1c, + 0x5a, 0x4a, 0xa7, 0xd6, 0xe7, 0xe8, + ], + ak: [ + 0xb1, 0x85, 0xc5, 0x7b, 0x50, 0x9c, 0x25, 0x36, 0xc4, 0xf2, 0xd3, 0x26, 0xd7, + 0x66, 0xc8, 0xfa, 0xb2, 0x54, 0x47, 0xde, 0x53, 0x75, 0xa9, 0x32, 0x8d, 0x64, + 0x9d, 0xda, 0xbd, 0x97, 0xa6, 0xa3, + ], + nk: [ + 0xdb, 0x88, 0x04, 0x9e, 0x02, 0xd2, 0x07, 0x56, 0x8a, 0xfc, 0x42, 0xe0, 0x7d, + 0xb2, 0xab, 0xed, 0x50, 0x0b, 0x27, 0x01, 0xc0, 0x1b, 0xbf, 0xf3, 0x63, 0x99, + 0x76, 0x4b, 0x81, 0xc0, 0x66, 0x4f, + ], + ivk: [ + 0xb0, 0xa5, 0xf3, 0x37, 0x23, 0x2f, 0x2c, 0x3d, 0xac, 0x70, 0xc2, 0xa4, 0x10, + 0xfa, 0x56, 0x1f, 0xc4, 0x5d, 0x8c, 0xc5, 0x9c, 0xda, 0x24, 0x6d, 0x31, 0xc8, + 0xb1, 0x71, 0x5a, 0x57, 0xd9, 0x00, + ], + xsk: None, + xfvk: [ + 0x03, 0x48, 0xc1, 0x83, 0x75, 0x03, 0x00, 0x00, 0x00, 0x8d, 0x93, 0x7b, 0xcf, + 0x81, 0xba, 0x43, 0x0d, 0x5b, 0x49, 0xaf, 0xc0, 0xa4, 0x03, 0x36, 0x7b, 0x1f, + 0xd9, 0x98, 0x79, 0xec, 0xba, 0x41, 0xbe, 0x05, 0x1c, 0x5a, 0x4a, 0xa7, 0xd6, + 0xe7, 0xe8, 0xb1, 0x85, 0xc5, 0x7b, 0x50, 0x9c, 0x25, 0x36, 0xc4, 0xf2, 0xd3, + 0x26, 0xd7, 0x66, 0xc8, 0xfa, 0xb2, 0x54, 0x47, 0xde, 0x53, 0x75, 0xa9, 0x32, + 0x8d, 0x64, 0x9d, 0xda, 0xbd, 0x97, 0xa6, 0xa3, 0xdb, 0x88, 0x04, 0x9e, 0x02, + 0xd2, 0x07, 0x56, 0x8a, 0xfc, 0x42, 0xe0, 0x7d, 0xb2, 0xab, 0xed, 0x50, 0x0b, + 0x27, 0x01, 0xc0, 0x1b, 0xbf, 0xf3, 0x63, 0x99, 0x76, 0x4b, 0x81, 0xc0, 0x66, + 0x4f, 0x69, 0xb9, 0xe0, 0xfa, 0x1c, 0x4b, 0x3d, 0xeb, 0x91, 0xd5, 0x3b, 0xee, + 0xe8, 0x71, 0x15, 0x61, 0x21, 0x47, 0x4b, 0x8b, 0x62, 0xef, 0x24, 0x13, 0x44, + 0x78, 0xdc, 0x34, 0x99, 0x69, 0x1a, 0xf6, 0xbe, 0xcb, 0x50, 0xc3, 0x63, 0xbb, + 0x2e, 0xd9, 0xda, 0x5c, 0x30, 0x43, 0xce, 0xb0, 0xf1, 0xa0, 0x52, 0x7b, 0xf8, + 0x36, 0xb2, 0x9a, 0x35, 0xf7, 0xc0, 0xc9, 0xf2, 0x61, 0x12, 0x3b, 0xe5, 0x6e, + ], + fp: [ + 0x2e, 0x08, 0x15, 0x6d, 0xf8, 0xdf, 0xa2, 0x5b, 0x50, 0x55, 0xfc, 0x06, 0x3c, + 0x67, 0x15, 0x35, 0xa6, 0xa6, 0x5a, 0x60, 0x43, 0x7d, 0x96, 0xe7, 0x93, 0x08, + 0x15, 0xd0, 0x90, 0xf6, 0x2d, 0x67, + ], + d0: None, + d1: Some([ + 0x03, 0x0f, 0xfb, 0x26, 0x3a, 0x93, 0x9e, 0x23, 0x0e, 0x96, 0xdd, + ]), + d2: Some([ + 0x7b, 0xbf, 0x63, 0x93, 0x4c, 0x7e, 0x92, 0x67, 0x0c, 0xdb, 0x55, + ]), + dmax: Some([ + 0x1a, 0x73, 0x0f, 0xeb, 0x00, 0x59, 0xcf, 0x1f, 0x5b, 0xde, 0xa8, + ]), + }, + ]; + + let seed = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + ]; + + let i1 = ChildIndex::NonHardened(1); + let i2h = ChildIndex::Hardened(2); + let i3 = ChildIndex::NonHardened(3); + + let m = ExtendedSpendingKey::master(&seed); + let m_1 = m.derive_child(i1); + let m_1_2h = ExtendedSpendingKey::from_path(&m, &[i1, i2h]); + let m_1_2hv = ExtendedFullViewingKey::from(&m_1_2h); + let m_1_2hv_3 = m_1_2hv.derive_child(i3).unwrap(); + + let xfvks = [ + ExtendedFullViewingKey::from(&m), + ExtendedFullViewingKey::from(&m_1), + ExtendedFullViewingKey::from(&m_1_2h), + m_1_2hv, // Appears twice so we can de-duplicate test code below + m_1_2hv_3, + ]; + assert_eq!(test_vectors.len(), xfvks.len()); + + let xsks = [m, m_1, m_1_2h]; + + for j in 0..xsks.len() { + let xsk = &xsks[j]; + let tv = &test_vectors[j]; + + let mut buf = [0; 32]; + xsk.expsk.ask.into_repr().write_le(&mut buf[..]).unwrap(); + assert_eq!(buf, tv.ask.unwrap()); + xsk.expsk.nsk.into_repr().write_le(&mut buf[..]).unwrap(); + assert_eq!(buf, tv.nsk.unwrap()); + + assert_eq!(xsk.expsk.ovk.0, tv.ovk); + assert_eq!(xsk.dk.0, tv.dk); + assert_eq!(xsk.chain_code.0, tv.c); + + let mut ser = vec![]; + xsk.write(&mut ser).unwrap(); + assert_eq!(&ser[..], &tv.xsk.unwrap()[..]); + } + + for j in 0..xfvks.len() { + let xfvk = &xfvks[j]; + let tv = &test_vectors[j]; + + let mut buf = [0; 32]; + xfvk.fvk.vk.ak.write(&mut buf[..]).unwrap(); + assert_eq!(buf, tv.ak); + xfvk.fvk.vk.nk.write(&mut buf[..]).unwrap(); + assert_eq!(buf, tv.nk); + + assert_eq!(xfvk.fvk.ovk.0, tv.ovk); + assert_eq!(xfvk.dk.0, tv.dk); + assert_eq!(xfvk.chain_code.0, tv.c); + + xfvk.fvk + .vk + .ivk() + .into_repr() + .write_le(&mut buf[..]) + .unwrap(); + assert_eq!(buf, tv.ivk); + + let mut ser = vec![]; + xfvk.write(&mut ser).unwrap(); + assert_eq!(&ser[..], &tv.xfvk[..]); + assert_eq!(xfvk.fvk.fingerprint().0, tv.fp); + + // d0 + let mut di = DiversifierIndex::new(); + match xfvk.dk.diversifier(di) { + Ok((l, d)) if l == di => assert_eq!(d.0, tv.d0.unwrap()), + Ok((_, _)) => assert!(tv.d0.is_none()), + Err(_) => panic!(), + } + + // d1 + di.increment().unwrap(); + match xfvk.dk.diversifier(di) { + Ok((l, d)) if l == di => assert_eq!(d.0, tv.d1.unwrap()), + Ok((_, _)) => assert!(tv.d1.is_none()), + Err(_) => panic!(), + } + + // d2 + di.increment().unwrap(); + match xfvk.dk.diversifier(di) { + Ok((l, d)) if l == di => assert_eq!(d.0, tv.d2.unwrap()), + Ok((_, _)) => assert!(tv.d2.is_none()), + Err(_) => panic!(), + } + + // dmax + let dmax = DiversifierIndex([0xff; 11]); + match xfvk.dk.diversifier(dmax) { + Ok((l, d)) if l == dmax => assert_eq!(d.0, tv.dmax.unwrap()), + Ok((_, _)) => panic!(), + Err(_) => assert!(tv.dmax.is_none()), + } + } + } +} diff --git a/src/rustzcash/.gitignore b/src/rustzcash/.gitignore deleted file mode 100644 index f61ba9b79..000000000 --- a/src/rustzcash/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -librustzcash.a diff --git a/src/rustzcash/Cargo.lock b/src/rustzcash/Cargo.lock deleted file mode 100644 index 5e38bfb3e..000000000 --- a/src/rustzcash/Cargo.lock +++ /dev/null @@ -1,706 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "aes" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aes-soft" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "aesni" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "arrayvec" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "autocfg" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bellman" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "group 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bigint" -version = "4.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bit-vec" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "blake2b_simd" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "blake2s_simd" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-cipher-trait" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-padding" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "byteorder" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "c2-chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cfg-if" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "constant_time_eq" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crossbeam" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crunchy" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crypto_api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crypto_api_chachapoly" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "directories" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ff" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff_derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ff_derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fpe" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "getrandom" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "group" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.62" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "librustzcash" -version = "0.2.0" -dependencies = [ - "bellman 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_history 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_primitives 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_proofs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "memoffset" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nodrop" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num-bigint" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-integer" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num_cpus" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "pairing" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "group 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "proc-macro2" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sha2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "wasi" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "zcash_history" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zcash_primitives" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zcash_proofs" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bellman 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zcash_primitives 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" -"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" -"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" -"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum bellman 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2be536193834affcd8a6d362963e66dec8c6bca4d2009f5bac55ec9002776ff2" -"checksum bigint 4.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebecac13b3c745150d7b6c3ea7572d372f09d627c2077e893bf26c5c7f70d282" -"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" -"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" -"checksum blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "979da0ce13c897d6be19e005ea77ac12b0fea0157aeeee7feb8c49f91386f0ea" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" -"checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" -"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" -"checksum crypto_api_chachapoly 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95b2ad7cab08fd71addba81df5077c49df208effdfb3118a1519f9cdeac5aaf2" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72d337a64190607d4fcca2cb78982c5dd57f4916e19696b48a575fa746b6cb0f" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum ff 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4b967a3ee6ae993f0094174257d404a5818f58be79d67a1aea1ec8996d28906" -"checksum ff_derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a3776aaf60a45037a9c3cabdd8542b38693acaa3e241ff957181b72579d29feb" -"checksum fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4" -"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" -"checksum group 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f15be54742789e36f03307c8fdf0621201e1345e94f1387282024178b5e9ec8c" -"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum pairing 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8290dea210a712682cd65031dc2b34fd132cf2729def3df7ee08f0737ff5ed6" -"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" -"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_xorshift 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum zcash_history 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "abfbab9accba014bbf3098d5aa66c1714d0db4abe25b999b8400bbd626ccd2f4" -"checksum zcash_primitives 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f33b9e4f3b4db97234fc79ea67b12f2d5778bde8f3eab6dbba52eb54c596585" -"checksum zcash_proofs 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2011f78f14d5121248d3b4f921434207b1d870fb3bf2efc7d784cae79b19bfbc" diff --git a/src/rustzcash/Cargo.toml b/src/rustzcash/Cargo.toml deleted file mode 100644 index 9cb295a1d..000000000 --- a/src/rustzcash/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "librustzcash" -description = "Rust FFI used by the zcashd binary. Not an official API." -version = "0.2.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", - "Jay Graber ", - "Simon Liu " -] -homepage = "https://github.com/zcash/zcash" -repository = "https://github.com/zcash/zcash" -readme = "README.md" -license = "MIT OR Apache-2.0" -edition = "2018" - -[lib] -name = "rustzcash" -path = "src/rustzcash.rs" -crate-type = ["staticlib"] - -[dependencies] -bellman = "0.6" -blake2b_simd = "0.5" -blake2s_simd = "0.5" -ff = "0.6" -libc = "0.2" -pairing = "0.16" -lazy_static = "1" -rand_core = "0.5.1" -zcash_history = "0.2" -zcash_primitives = "0.2" -zcash_proofs = "0.2" - -[profile.release] -lto = true -panic = 'abort' -codegen-units = 1 diff --git a/src/rustzcash/src/rustzcash.rs b/src/rustzcash/src/rustzcash.rs deleted file mode 100644 index 43837ce35..000000000 --- a/src/rustzcash/src/rustzcash.rs +++ /dev/null @@ -1,1354 +0,0 @@ -//! FFI between the C++ zcashd codebase and the Rust Zcash crates. -//! -//! This is internal to zcashd and is not an officially-supported API. - -// Catch documentation errors caused by code changes. -#![deny(broken_intra_doc_links)] -// Clippy has a default-deny lint to prevent dereferencing raw pointer arguments -// in a non-unsafe function. However, declaring a function as unsafe has the -// side-effect that the entire function body is treated as an unsafe {} block, -// and rustc will not enforce full safety checks on the parts of the function -// that would otherwise be safe. -// -// The functions in this crate are all for FFI usage, so it's obvious to the -// caller (which is only ever zcashd) that the arguments must satisfy the -// necessary assumptions. We therefore ignore this lint to retain the benefit of -// explicitly annotating the parts of each function that must themselves satisfy -// assumptions of underlying code. -// -// See https://github.com/rust-lang/rfcs/pull/2585 for more background. -#![allow(clippy::not_unsafe_ptr_arg_deref)] - -use bellman::groth16::{Parameters, PreparedVerifyingKey, Proof}; -use blake2s_simd::Params as Blake2sParams; -use ff::{PrimeField, PrimeFieldRepr}; -use lazy_static; -use libc::{c_char, c_uchar, size_t}; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; -use rand_core::{OsRng, RngCore}; -use std::ffi::CStr; -use std::fs::File; -use std::io::BufReader; -use std::path::{Path, PathBuf}; -use std::slice; -use std::ptr::addr_of; - -#[cfg(not(target_os = "windows"))] -use std::ffi::OsStr; -#[cfg(not(target_os = "windows"))] -use std::os::unix::ffi::OsStrExt; - -#[cfg(target_os = "windows")] -use std::ffi::OsString; -#[cfg(target_os = "windows")] -use std::os::windows::ffi::OsStringExt; - -use zcash_primitives::{ - block::equihash, - constants::CRH_IVK_PERSONALIZATION, - jubjub::{ - edwards, - fs::{Fs, FsRepr}, - FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown, - }, - merkle_tree::MerklePath, - note_encryption::sapling_ka_agree, - primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ViewingKey}, - redjubjub::{self, Signature}, - sapling::{merkle_hash, spend_sig}, - transaction::components::Amount, - zip32, JUBJUB, -}; -use zcash_proofs::{ - circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH, - load_parameters, - sapling::{SaplingProvingContext, SaplingVerificationContext}, - sprout, -}; - -use zcash_history::{Entry as MMREntry, NodeData as MMRNodeData, Tree as MMRTree}; - -#[cfg(test)] -mod tests; - -static mut SAPLING_SPEND_VK: Option> = None; -static mut SAPLING_OUTPUT_VK: Option> = None; -static mut SPROUT_GROTH16_VK: Option> = None; - -static mut SAPLING_SPEND_PARAMS: Option> = None; -static mut SAPLING_OUTPUT_PARAMS: Option> = None; -static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; - -/// Reads an FrRepr from a [u8; 32]. -fn read_fr(from: &[u8; 32]) -> FrRepr { - let mut f = FrRepr::default(); - f.read_le(&from[..]).expect("length is 32 bytes"); - f -} - -/// Reads an FsRepr from a [u8; 32]. -fn read_fs(from: &[u8; 32]) -> FsRepr { - let mut f = <::Fs as PrimeField>::Repr::default(); - f.read_le(&from[..]).expect("length is 32 bytes"); - f -} - -/// Reads an FsRepr from a [u8; 32] -/// and multiplies it by the given base. -fn fixed_scalar_mult(from: &[u8; 32], p_g: FixedGenerators) -> edwards::Point { - let f = read_fs(from); - - JUBJUB.generator(p_g).mul(f, &JUBJUB) -} - -/// Loads the zk-SNARK parameters into memory and saves paths as necessary. -/// Only called once. -#[cfg(not(target_os = "windows"))] -#[no_mangle] -pub extern "C" fn librustzcash_init_zksnark_params( - spend_path: *const u8, - spend_path_len: usize, - spend_hash: *const c_char, - output_path: *const u8, - output_path_len: usize, - output_hash: *const c_char, - sprout_path: *const u8, - sprout_path_len: usize, - sprout_hash: *const c_char, -) { - let spend_path = Path::new(OsStr::from_bytes(unsafe { - slice::from_raw_parts(spend_path, spend_path_len) - })); - let output_path = Path::new(OsStr::from_bytes(unsafe { - slice::from_raw_parts(output_path, output_path_len) - })); - let sprout_path = if sprout_path.is_null() { - None - } else { - Some(Path::new(OsStr::from_bytes(unsafe { - slice::from_raw_parts(sprout_path, sprout_path_len) - }))) - }; - - init_zksnark_params( - spend_path, - spend_hash, - output_path, - output_hash, - sprout_path, - sprout_hash, - ) -} - -/// Loads the zk-SNARK parameters into memory and saves paths as necessary. -/// Only called once. -#[cfg(target_os = "windows")] -#[no_mangle] -pub extern "C" fn librustzcash_init_zksnark_params( - spend_path: *const u16, - spend_path_len: usize, - spend_hash: *const c_char, - output_path: *const u16, - output_path_len: usize, - output_hash: *const c_char, - sprout_path: *const u16, - sprout_path_len: usize, - sprout_hash: *const c_char, -) { - let spend_path = - OsString::from_wide(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }); - let output_path = - OsString::from_wide(unsafe { slice::from_raw_parts(output_path, output_path_len) }); - let sprout_path = if sprout_path.is_null() { - None - } else { - Some(OsString::from_wide(unsafe { - slice::from_raw_parts(sprout_path, sprout_path_len) - })) - }; - - init_zksnark_params( - Path::new(&spend_path), - spend_hash, - Path::new(&output_path), - output_hash, - sprout_path.as_ref().map(|p| Path::new(p)), - sprout_hash, - ) -} - -fn init_zksnark_params( - spend_path: &Path, - spend_hash: *const c_char, - output_path: &Path, - output_hash: *const c_char, - sprout_path: Option<&Path>, - sprout_hash: *const c_char, -) { - // Initialize jubjub parameters here - lazy_static::initialize(&JUBJUB); - - let spend_hash = unsafe { CStr::from_ptr(spend_hash) } - .to_str() - .expect("hash should be a valid string"); - - let output_hash = unsafe { CStr::from_ptr(output_hash) } - .to_str() - .expect("hash should be a valid string"); - - let sprout_hash = if sprout_path.is_none() { - None - } else { - Some( - unsafe { CStr::from_ptr(sprout_hash) } - .to_str() - .expect("hash should be a valid string"), - ) - }; - - // Load params - let (spend_params, spend_vk, output_params, output_vk, sprout_vk) = load_parameters( - spend_path, - spend_hash, - output_path, - output_hash, - sprout_path, - sprout_hash, - ); - - // Caller is responsible for calling this function once, so - // these global mutations are safe. - unsafe { - SAPLING_SPEND_PARAMS = Some(spend_params); - SAPLING_OUTPUT_PARAMS = Some(output_params); - SPROUT_GROTH16_PARAMS_PATH = sprout_path.map(|p| p.to_owned()); - - SAPLING_SPEND_VK = Some(spend_vk); - SAPLING_OUTPUT_VK = Some(output_vk); - SPROUT_GROTH16_VK = sprout_vk; - } -} - -/// Writes the "uncommitted" note value for empty leaves of the Merkle tree. -/// -/// `result` must be a valid pointer to 32 bytes which will be written. -#[no_mangle] -pub extern "C" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) { - let tmp = Note::::uncommitted().into_repr(); - - // Should be okay, caller is responsible for ensuring the pointer - // is a valid pointer to 32 bytes that can be mutated. - let result = unsafe { &mut *result }; - tmp.write_le(&mut result[..]).expect("length is 32 bytes"); -} - -/// Computes a merkle tree hash for a given depth. The `depth` parameter should -/// not be larger than 62. -/// -/// `a` and `b` each must be of length 32, and must each be scalars of BLS12-381. -/// -/// The result of the merkle tree hash is placed in `result`, which must also be -/// of length 32. -#[no_mangle] -pub extern "C" fn librustzcash_merkle_hash( - depth: size_t, - a: *const [c_uchar; 32], - b: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) { - // Should be okay, because caller is responsible for ensuring - // the pointer is a valid pointer to 32 bytes, and that is the - // size of the representation - let a_repr = read_fr(unsafe { &*a }); - - // Should be okay, because caller is responsible for ensuring - // the pointer is a valid pointer to 32 bytes, and that is the - // size of the representation - let b_repr = read_fr(unsafe { &*b }); - - let tmp = merkle_hash(depth, &a_repr, &b_repr); - - // Should be okay, caller is responsible for ensuring the pointer - // is a valid pointer to 32 bytes that can be mutated. - let result = unsafe { &mut *result }; - tmp.write_le(&mut result[..]).expect("length is 32 bytes"); -} - -#[no_mangle] // ToScalar -pub extern "C" fn librustzcash_to_scalar(input: *const [c_uchar; 64], result: *mut [c_uchar; 32]) { - // Should be okay, because caller is responsible for ensuring - // the pointer is a valid pointer to 32 bytes, and that is the - // size of the representation - let scalar = ::Fs::to_uniform(unsafe { &(&*input)[..] }).into_repr(); - - let result = unsafe { &mut *result }; - - scalar - .write_le(&mut result[..]) - .expect("length is 32 bytes"); -} - -#[no_mangle] -pub extern "C" fn librustzcash_ask_to_ak(ask: *const [c_uchar; 32], result: *mut [c_uchar; 32]) { - let ask = unsafe { &*ask }; - let ak = fixed_scalar_mult(ask, FixedGenerators::SpendingKeyGenerator); - - let result = unsafe { &mut *result }; - - ak.write(&mut result[..]).expect("length is 32 bytes"); -} - -#[no_mangle] -pub extern "C" fn librustzcash_nsk_to_nk(nsk: *const [c_uchar; 32], result: *mut [c_uchar; 32]) { - let nsk = unsafe { &*nsk }; - let nk = fixed_scalar_mult(nsk, FixedGenerators::ProofGenerationKey); - - let result = unsafe { &mut *result }; - - nk.write(&mut result[..]).expect("length is 32 bytes"); -} - -#[no_mangle] -pub extern "C" fn librustzcash_crh_ivk( - ak: *const [c_uchar; 32], - nk: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) { - let ak = unsafe { &*ak }; - let nk = unsafe { &*nk }; - - let mut h = Blake2sParams::new() - .hash_length(32) - .personal(CRH_IVK_PERSONALIZATION) - .to_state(); - h.update(ak); - h.update(nk); - let mut h = h.finalize().as_ref().to_vec(); - - // Drop the last five bits, so it can be interpreted as a scalar. - h[31] &= 0b0000_0111; - - let result = unsafe { &mut *result }; - - result.copy_from_slice(&h); -} - -#[no_mangle] -pub extern "C" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool { - let diversifier = Diversifier(unsafe { *diversifier }); - diversifier.g_d::(&JUBJUB).is_some() -} - -#[no_mangle] -pub extern "C" fn librustzcash_ivk_to_pkd( - ivk: *const [c_uchar; 32], - diversifier: *const [c_uchar; 11], - result: *mut [c_uchar; 32], -) -> bool { - let ivk = read_fs(unsafe { &*ivk }); - let diversifier = Diversifier(unsafe { *diversifier }); - if let Some(g_d) = diversifier.g_d::(&JUBJUB) { - let pk_d = g_d.mul(ivk, &JUBJUB); - - let result = unsafe { &mut *result }; - - pk_d.write(&mut result[..]).expect("length is 32 bytes"); - - true - } else { - false - } -} - -/// Test generation of commitment randomness -#[test] -fn test_gen_r() { - let mut r1 = [0u8; 32]; - let mut r2 = [0u8; 32]; - - // Verify different r values are generated - librustzcash_sapling_generate_r(&mut r1); - librustzcash_sapling_generate_r(&mut r2); - assert_ne!(r1, r2); - - // Verify r values are valid in the field - let mut repr = FsRepr::default(); - repr.read_le(&r1[..]).expect("length is not 32 bytes"); - let _ = Fs::from_repr(repr).unwrap(); - repr.read_le(&r2[..]).expect("length is not 32 bytes"); - let _ = Fs::from_repr(repr).unwrap(); -} - -/// Generate uniformly random scalar in Jubjub. The result is of length 32. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { - // create random 64 byte buffer - let mut rng = OsRng; - let mut buffer = [0u8; 64]; - rng.fill_bytes(&mut buffer); - - // reduce to uniform value - let r = ::Fs::to_uniform(&buffer[..]); - let result = unsafe { &mut *result }; - r.into_repr() - .write_le(&mut result[..]) - .expect("result must be 32 bytes"); -} - -// Private utility function to get Note from C parameters -fn priv_get_note( - diversifier: *const [c_uchar; 11], - pk_d: *const [c_uchar; 32], - value: u64, - r: *const [c_uchar; 32], -) -> Result, ()> { - let diversifier = Diversifier(unsafe { *diversifier }); - let g_d = diversifier.g_d::(&JUBJUB).ok_or(())?; - - let pk_d = edwards::Point::::read(&(unsafe { &*pk_d })[..], &JUBJUB) - .map_err(|_| ())?; - - let pk_d = pk_d.as_prime_order(&JUBJUB).ok_or(())?; - - // Deserialize randomness - let r = Fs::from_repr(read_fs(unsafe { &*r })).map_err(|_| ())?; - - let note = Note { - value, - g_d, - pk_d, - r, - }; - - Ok(note) -} - -/// Compute a Sapling nullifier. -/// -/// The `diversifier` parameter must be 11 bytes in length. -/// The `pk_d`, `r`, `ak` and `nk` parameters must be of length 32. -/// The result is also of length 32 and placed in `result`. -/// Returns false if `diversifier` or `pk_d` is not valid. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_compute_nf( - diversifier: *const [c_uchar; 11], - pk_d: *const [c_uchar; 32], - value: u64, - r: *const [c_uchar; 32], - ak: *const [c_uchar; 32], - nk: *const [c_uchar; 32], - position: u64, - result: *mut [c_uchar; 32], -) -> bool { - let note = match priv_get_note(diversifier, pk_d, value, r) { - Ok(p) => p, - Err(_) => return false, - }; - - let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - let ak = match ak.as_prime_order(&JUBJUB) { - Some(ak) => ak, - None => return false, - }; - - let nk = match edwards::Point::::read(&(unsafe { &*nk })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - let nk = match nk.as_prime_order(&JUBJUB) { - Some(nk) => nk, - None => return false, - }; - - let vk = ViewingKey { ak, nk }; - let nf = note.nf(&vk, position, &JUBJUB); - let result = unsafe { &mut *result }; - result.copy_from_slice(&nf); - - true -} - -/// Compute a Sapling commitment. -/// -/// The `diversifier` parameter must be 11 bytes in length. -/// The `pk_d` and `r` parameters must be of length 32. -/// The result is also of length 32 and placed in `result`. -/// Returns false if `diversifier` or `pk_d` is not valid. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_compute_cm( - diversifier: *const [c_uchar; 11], - pk_d: *const [c_uchar; 32], - value: u64, - r: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) -> bool { - let note = match priv_get_note(diversifier, pk_d, value, r) { - Ok(p) => p, - Err(_) => return false, - }; - - let result = unsafe { &mut *result }; - note.cm(&JUBJUB) - .into_repr() - .write_le(&mut result[..]) - .expect("length is 32 bytes"); - - true -} - -/// Computes \[sk\] \[8\] P for some 32-byte point P, and 32-byte Fs. -/// -/// If P or sk are invalid, returns false. Otherwise, the result is written to -/// the 32-byte `result` buffer. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_ka_agree( - p: *const [c_uchar; 32], - sk: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) -> bool { - // Deserialize p - let p = match edwards::Point::::read(&(unsafe { &*p })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize sk - let sk = match Fs::from_repr(read_fs(unsafe { &*sk })) { - Ok(p) => p, - Err(_) => return false, - }; - - // Compute key agreement - let ka = sapling_ka_agree(&sk, &p); - - // Produce result - let result = unsafe { &mut *result }; - ka.write(&mut result[..]).expect("length is not 32 bytes"); - - true -} - -/// Compute g_d = GH(diversifier) and returns false if the diversifier is -/// invalid. Computes \[esk\] g_d and writes the result to the 32-byte `result` -/// buffer. Returns false if `esk` is not a valid scalar. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_ka_derivepublic( - diversifier: *const [c_uchar; 11], - esk: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) -> bool { - let diversifier = Diversifier(unsafe { *diversifier }); - - // Compute g_d from the diversifier - let g_d = match diversifier.g_d::(&JUBJUB) { - Some(g) => g, - None => return false, - }; - - // Deserialize esk - let esk = match Fs::from_repr(read_fs(unsafe { &*esk })) { - Ok(p) => p, - Err(_) => return false, - }; - - let p = g_d.mul(esk, &JUBJUB); - - let result = unsafe { &mut *result }; - p.write(&mut result[..]).expect("length is not 32 bytes"); - - true -} - -/// Validates the provided Equihash solution against the given parameters, input -/// and nonce. -#[no_mangle] -pub extern "C" fn librustzcash_eh_isvalid( - n: u32, - k: u32, - input: *const c_uchar, - input_len: size_t, - nonce: *const c_uchar, - nonce_len: size_t, - soln: *const c_uchar, - soln_len: size_t, -) -> bool { - if (k >= n) || (n % 8 != 0) || (soln_len != (1 << k) * ((n / (k + 1)) as usize + 1) / 8) { - return false; - } - let rs_input = unsafe { slice::from_raw_parts(input, input_len) }; - let rs_nonce = unsafe { slice::from_raw_parts(nonce, nonce_len) }; - let rs_soln = unsafe { slice::from_raw_parts(soln, soln_len) }; - equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln) -} - -/// Creates a Sapling verification context. Please free this when you're done. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_verification_ctx_init() -> *mut SaplingVerificationContext { - let ctx = Box::new(SaplingVerificationContext::new()); - - Box::into_raw(ctx) -} - -/// Frees a Sapling verification context returned from -/// [`librustzcash_sapling_verification_ctx_init`]. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_verification_ctx_free(ctx: *mut SaplingVerificationContext) { - drop(unsafe { Box::from_raw(ctx) }); -} - -const GROTH_PROOF_SIZE: usize = 48 // π_A - + 96 // π_B - + 48; // π_C - -/// Check the validity of a Sapling Spend description, accumulating the value -/// commitment into the context. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_check_spend( - ctx: *mut SaplingVerificationContext, - cv: *const [c_uchar; 32], - anchor: *const [c_uchar; 32], - nullifier: *const [c_uchar; 32], - rk: *const [c_uchar; 32], - zkproof: *const [c_uchar; GROTH_PROOF_SIZE], - spend_auth_sig: *const [c_uchar; 64], - sighash_value: *const [c_uchar; 32], -) -> bool { - // Deserialize the value commitment - let cv = match edwards::Point::::read(&(unsafe { &*cv })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize the anchor, which should be an element - // of Fr. - let anchor = match Fr::from_repr(read_fr(unsafe { &*anchor })) { - Ok(a) => a, - Err(_) => return false, - }; - - // Deserialize rk - let rk = match redjubjub::PublicKey::::read(&(unsafe { &*rk })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize the signature - let spend_auth_sig = match Signature::read(&(unsafe { &*spend_auth_sig })[..]) { - Ok(sig) => sig, - Err(_) => return false, - }; - - // Deserialize the proof - let zkproof = match Proof::::read(&(unsafe { &*zkproof })[..]) { - Ok(p) => p, - Err(_) => return false, - }; - - unsafe { &mut *ctx }.check_spend( - cv, - anchor, - unsafe { &*nullifier }, - rk, - unsafe { &*sighash_value }, - spend_auth_sig, - zkproof, - unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), - &JUBJUB, - ) -} - -/// Check the validity of a Sapling Output description, accumulating the value -/// commitment into the context. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_check_output( - ctx: *mut SaplingVerificationContext, - cv: *const [c_uchar; 32], - cm: *const [c_uchar; 32], - epk: *const [c_uchar; 32], - zkproof: *const [c_uchar; GROTH_PROOF_SIZE], -) -> bool { - // Deserialize the value commitment - let cv = match edwards::Point::::read(&(unsafe { &*cv })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize the commitment, which should be an element - // of Fr. - let cm = match Fr::from_repr(read_fr(unsafe { &*cm })) { - Ok(a) => a, - Err(_) => return false, - }; - - // Deserialize the ephemeral key - let epk = match edwards::Point::::read(&(unsafe { &*epk })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // Deserialize the proof - let zkproof = match Proof::::read(&(unsafe { &*zkproof })[..]) { - Ok(p) => p, - Err(_) => return false, - }; - - unsafe { &mut *ctx }.check_output( - cv, - cm, - epk, - zkproof, - unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(), - &JUBJUB, - ) -} - -/// Finally checks the validity of the entire Sapling transaction given -/// valueBalance and the binding signature. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_final_check( - ctx: *mut SaplingVerificationContext, - value_balance: i64, - binding_sig: *const [c_uchar; 64], - sighash_value: *const [c_uchar; 32], -) -> bool { - let value_balance = match Amount::from_i64(value_balance) { - Ok(vb) => vb, - Err(()) => return false, - }; - - // Deserialize the signature - let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) { - Ok(sig) => sig, - Err(_) => return false, - }; - - unsafe { &*ctx }.final_check( - value_balance, - unsafe { &*sighash_value }, - binding_sig, - &JUBJUB, - ) -} - -/// Sprout JoinSplit proof generation. -#[no_mangle] -pub extern "C" fn librustzcash_sprout_prove( - proof_out: *mut [c_uchar; GROTH_PROOF_SIZE], - - phi: *const [c_uchar; 32], - rt: *const [c_uchar; 32], - h_sig: *const [c_uchar; 32], - - // First input - in_sk1: *const [c_uchar; 32], - in_value1: u64, - in_rho1: *const [c_uchar; 32], - in_r1: *const [c_uchar; 32], - in_auth1: *const [c_uchar; sprout::WITNESS_PATH_SIZE], - - // Second input - in_sk2: *const [c_uchar; 32], - in_value2: u64, - in_rho2: *const [c_uchar; 32], - in_r2: *const [c_uchar; 32], - in_auth2: *const [c_uchar; sprout::WITNESS_PATH_SIZE], - - // First output - out_pk1: *const [c_uchar; 32], - out_value1: u64, - out_r1: *const [c_uchar; 32], - - // Second output - out_pk2: *const [c_uchar; 32], - out_value2: u64, - out_r2: *const [c_uchar; 32], - - // Public value - vpub_old: u64, - vpub_new: u64, -) { - // Load parameters from disk - let sprout_path = unsafe { addr_of!(SPROUT_GROTH16_PARAMS_PATH).as_ref() } - .expect("Only mutated during init, so this pointer can never be null") - .as_ref() - .expect( - "Parameters not loaded: SPROUT_GROTH16_PARAMS_PATH should have been initialized", - ); - const HOW_TO_FIX: &str = " -Please download this file from https://download.z.cash/downloads/sprout-groth16.params -and put it at "; - let sprout_fs = File::open(sprout_path).unwrap_or_else(|err| { - panic!( - "Couldn't load Sprout Groth16 parameters file: {}{}{}", - err, - HOW_TO_FIX, - &sprout_path.to_string_lossy() - ) - }); - - let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs); - - let params = Parameters::::read(&mut sprout_fs, false) - .expect("couldn't deserialize Sprout JoinSplit parameters file"); - - drop(sprout_fs); - - let proof = sprout::create_proof( - unsafe { *phi }, - unsafe { *rt }, - unsafe { *h_sig }, - unsafe { *in_sk1 }, - in_value1, - unsafe { *in_rho1 }, - unsafe { *in_r1 }, - unsafe { &*in_auth1 }, - unsafe { *in_sk2 }, - in_value2, - unsafe { *in_rho2 }, - unsafe { *in_r2 }, - unsafe { &*in_auth2 }, - unsafe { *out_pk1 }, - out_value1, - unsafe { *out_r1 }, - unsafe { *out_pk2 }, - out_value2, - unsafe { *out_r2 }, - vpub_old, - vpub_new, - ¶ms, - ); - - proof - .write(&mut (unsafe { &mut *proof_out })[..]) - .expect("should be able to serialize a proof"); -} - -/// Sprout JoinSplit proof verification. -#[no_mangle] -pub extern "C" fn librustzcash_sprout_verify( - proof: *const [c_uchar; GROTH_PROOF_SIZE], - rt: *const [c_uchar; 32], - h_sig: *const [c_uchar; 32], - mac1: *const [c_uchar; 32], - mac2: *const [c_uchar; 32], - nf1: *const [c_uchar; 32], - nf2: *const [c_uchar; 32], - cm1: *const [c_uchar; 32], - cm2: *const [c_uchar; 32], - vpub_old: u64, - vpub_new: u64, -) -> bool { - sprout::verify_proof( - unsafe { &*proof }, - unsafe { &*rt }, - unsafe { &*h_sig }, - unsafe { &*mac1 }, - unsafe { &*mac2 }, - unsafe { &*nf1 }, - unsafe { &*nf2 }, - unsafe { &*cm1 }, - unsafe { &*cm2 }, - vpub_old, - vpub_new, - unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"), - ) -} - -/// This function (using the proving context) constructs an Output proof given -/// the necessary witness information. It outputs `cv` and the `zkproof`. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_output_proof( - ctx: *mut SaplingProvingContext, - esk: *const [c_uchar; 32], - payment_address: *const [c_uchar; 43], - rcm: *const [c_uchar; 32], - value: u64, - cv: *mut [c_uchar; 32], - zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], -) -> bool { - // Grab `esk`, which the caller should have constructed for the DH key exchange. - let esk = match Fs::from_repr(read_fs(unsafe { &*esk })) { - Ok(p) => p, - Err(_) => return false, - }; - - // Grab the payment address from the caller - let payment_address = - match PaymentAddress::::from_bytes(unsafe { &*payment_address }, &JUBJUB) { - Some(pa) => pa, - None => return false, - }; - - // The caller provides the commitment randomness for the output note - let rcm = match Fs::from_repr(read_fs(unsafe { &*rcm })) { - Ok(p) => p, - Err(_) => return false, - }; - - // Create proof - let (proof, value_commitment) = unsafe { &mut *ctx }.output_proof( - esk, - payment_address, - rcm, - value, - unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(), - &JUBJUB, - ); - - // Write the proof out to the caller - proof - .write(&mut (unsafe { &mut *zkproof })[..]) - .expect("should be able to serialize a proof"); - - // Write the value commitment to the caller - value_commitment - .write(&mut (unsafe { &mut *cv })[..]) - .expect("should be able to serialize rcv"); - - true -} - -/// Computes the signature for each Spend description, given the key `ask`, the -/// re-randomization `ar`, the 32-byte sighash `sighash`, and an output `result` -/// buffer of 64-bytes for the signature. -/// -/// This function will fail if the provided `ask` or `ar` are invalid. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_spend_sig( - ask: *const [c_uchar; 32], - ar: *const [c_uchar; 32], - sighash: *const [c_uchar; 32], - result: *mut [c_uchar; 64], -) -> bool { - // The caller provides the re-randomization of `ak`. - let ar = match Fs::from_repr(read_fs(unsafe { &*ar })) { - Ok(p) => p, - Err(_) => return false, - }; - - // The caller provides `ask`, the spend authorizing key. - let ask = match redjubjub::PrivateKey::::read(&(unsafe { &*ask })[..]) { - Ok(p) => p, - Err(_) => return false, - }; - - // Initialize secure RNG - let mut rng = OsRng; - - // Do the signing - let sig = spend_sig(ask, ar, unsafe { &*sighash }, &mut rng, &JUBJUB); - - // Write out the signature - sig.write(&mut (unsafe { &mut *result })[..]) - .expect("result should be 64 bytes"); - - true -} - -/// This function (using the proving context) constructs a binding signature. -/// -/// You must provide the intended valueBalance so that we can internally check -/// consistency. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_binding_sig( - ctx: *const SaplingProvingContext, - value_balance: i64, - sighash: *const [c_uchar; 32], - result: *mut [c_uchar; 64], -) -> bool { - let value_balance = match Amount::from_i64(value_balance) { - Ok(vb) => vb, - Err(()) => return false, - }; - - // Sign - let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }, &JUBJUB) { - Ok(s) => s, - Err(_) => return false, - }; - - // Write out signature - sig.write(&mut (unsafe { &mut *result })[..]) - .expect("result should be 64 bytes"); - - true -} - -/// This function (using the proving context) constructs a Spend proof given the -/// necessary witness information. It outputs `cv` (the value commitment) and -/// `rk` (so that you don't have to compute it) along with the proof. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_spend_proof( - ctx: *mut SaplingProvingContext, - ak: *const [c_uchar; 32], - nsk: *const [c_uchar; 32], - diversifier: *const [c_uchar; 11], - rcm: *const [c_uchar; 32], - ar: *const [c_uchar; 32], - value: u64, - anchor: *const [c_uchar; 32], - merkle_path: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8], - cv: *mut [c_uchar; 32], - rk_out: *mut [c_uchar; 32], - zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], -) -> bool { - // Grab `ak` from the caller, which should be a point. - let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { - Ok(p) => p, - Err(_) => return false, - }; - - // `ak` should be prime order. - let ak = match ak.as_prime_order(&JUBJUB) { - Some(p) => p, - None => return false, - }; - - // Grab `nsk` from the caller - let nsk = match Fs::from_repr(read_fs(unsafe { &*nsk })) { - Ok(p) => p, - Err(_) => return false, - }; - - // Construct the proof generation key - let proof_generation_key = ProofGenerationKey { - ak: ak.clone(), - nsk, - }; - - // Grab the diversifier from the caller - let diversifier = Diversifier(unsafe { *diversifier }); - - // The caller chooses the note randomness - let rcm = match Fs::from_repr(read_fs(unsafe { &*rcm })) { - Ok(p) => p, - Err(_) => return false, - }; - - // The caller also chooses the re-randomization of ak - let ar = match Fs::from_repr(read_fs(unsafe { &*ar })) { - Ok(p) => p, - Err(_) => return false, - }; - - // We need to compute the anchor of the Spend. - let anchor = match Fr::from_repr(read_fr(unsafe { &*anchor })) { - Ok(p) => p, - Err(_) => return false, - }; - - // Parse the Merkle path from the caller - let merkle_path = match MerklePath::from_slice(unsafe { &(&*merkle_path)[..] }) { - Ok(w) => w, - Err(_) => return false, - }; - - // Create proof - let (proof, value_commitment, rk) = unsafe { &mut *ctx } - .spend_proof( - proof_generation_key, - diversifier, - rcm, - ar, - value, - anchor, - merkle_path, - unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(), - unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), - &JUBJUB, - ) - .expect("proving should not fail"); - - // Write value commitment to caller - value_commitment - .write(&mut unsafe { &mut *cv }[..]) - .expect("should be able to serialize cv"); - - // Write proof out to caller - proof - .write(&mut (unsafe { &mut *zkproof })[..]) - .expect("should be able to serialize a proof"); - - // Write out `rk` to the caller - rk.write(&mut unsafe { &mut *rk_out }[..]) - .expect("should be able to write to rk_out"); - - true -} - -/// Creates a Sapling proving context. Please free this when you're done. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext { - let ctx = Box::new(SaplingProvingContext::new()); - - Box::into_raw(ctx) -} - -/// Frees a Sapling proving context returned from -/// [`librustzcash_sapling_proving_ctx_init`]. -#[no_mangle] -pub extern "C" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) { - drop(unsafe { Box::from_raw(ctx) }); -} - -/// Derive the master ExtendedSpendingKey from a seed. -#[no_mangle] -pub extern "C" fn librustzcash_zip32_xsk_master( - seed: *const c_uchar, - seedlen: size_t, - xsk_master: *mut [c_uchar; 169], -) { - let seed = unsafe { std::slice::from_raw_parts(seed, seedlen) }; - - let xsk = zip32::ExtendedSpendingKey::master(seed); - - xsk.write(&mut (unsafe { &mut *xsk_master })[..]) - .expect("should be able to serialize an ExtendedSpendingKey"); -} - -/// Derive a child ExtendedSpendingKey from a parent. -#[no_mangle] -pub extern "C" fn librustzcash_zip32_xsk_derive( - xsk_parent: *const [c_uchar; 169], - i: u32, - xsk_i: *mut [c_uchar; 169], -) { - let xsk_parent = zip32::ExtendedSpendingKey::read(&unsafe { *xsk_parent }[..]) - .expect("valid ExtendedSpendingKey"); - let i = zip32::ChildIndex::from_index(i); - - let xsk = xsk_parent.derive_child(i); - - xsk.write(&mut (unsafe { &mut *xsk_i })[..]) - .expect("should be able to serialize an ExtendedSpendingKey"); -} - -/// Derive a child ExtendedFullViewingKey from a parent. -#[no_mangle] -pub extern "C" fn librustzcash_zip32_xfvk_derive( - xfvk_parent: *const [c_uchar; 169], - i: u32, - xfvk_i: *mut [c_uchar; 169], -) -> bool { - let xfvk_parent = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk_parent }[..]) - .expect("valid ExtendedFullViewingKey"); - let i = zip32::ChildIndex::from_index(i); - - let xfvk = match xfvk_parent.derive_child(i) { - Ok(xfvk) => xfvk, - Err(_) => return false, - }; - - xfvk.write(&mut (unsafe { &mut *xfvk_i })[..]) - .expect("should be able to serialize an ExtendedFullViewingKey"); - - true -} - -/// Derive a PaymentAddress from an ExtendedFullViewingKey. -#[no_mangle] -pub extern "C" fn librustzcash_zip32_xfvk_address( - xfvk: *const [c_uchar; 169], - j: *const [c_uchar; 11], - j_ret: *mut [c_uchar; 11], - addr_ret: *mut [c_uchar; 43], -) -> bool { - let xfvk = zip32::ExtendedFullViewingKey::read(&unsafe { *xfvk }[..]) - .expect("valid ExtendedFullViewingKey"); - let j = zip32::DiversifierIndex(unsafe { *j }); - - let addr = match xfvk.address(j) { - Ok(addr) => addr, - Err(_) => return false, - }; - - let j_ret = unsafe { &mut *j_ret }; - let addr_ret = unsafe { &mut *addr_ret }; - - j_ret.copy_from_slice(&(addr.0).0); - addr_ret.copy_from_slice(&addr.1.to_bytes()); - - true -} - -fn construct_mmr_tree( - // Consensus branch id - cbranch: u32, - // Length of tree in array representation - t_len: u32, - - // Indices of provided tree nodes, length of p_len+e_len - ni_ptr: *const u32, - // Provided tree nodes data, length of p_len+e_len - n_ptr: *const [c_uchar; zcash_history::MAX_ENTRY_SIZE], - - // Peaks count - p_len: size_t, - // Extra nodes loaded (for deletion) count - e_len: size_t, -) -> Result { - let (indices, nodes) = unsafe { - ( - slice::from_raw_parts(ni_ptr, p_len + e_len), - slice::from_raw_parts(n_ptr, p_len + e_len), - ) - }; - - let mut peaks: Vec<_> = indices - .iter() - .zip(nodes.iter()) - .map( - |(index, node)| match MMREntry::from_bytes(cbranch, &node[..]) { - Ok(entry) => Ok((*index, entry)), - Err(_) => Err("Invalid encoding"), - }, - ) - .collect::>()?; - let extra = peaks.split_off(p_len); - - Ok(MMRTree::new(t_len, peaks, extra)) -} - -#[no_mangle] -pub extern "system" fn librustzcash_mmr_append( - // Consensus branch id - cbranch: u32, - // Length of tree in array representation - t_len: u32, - // Indices of provided tree nodes, length of p_len - ni_ptr: *const u32, - // Provided tree nodes data, length of p_len - n_ptr: *const [c_uchar; zcash_history::MAX_ENTRY_SIZE], - // Peaks count - p_len: size_t, - // New node pointer - nn_ptr: *const [u8; zcash_history::MAX_NODE_DATA_SIZE], - // Return of root commitment - rt_ret: *mut [u8; 32], - // Return buffer for appended leaves, should be pre-allocated of ceiling(log2(t_len)) length - buf_ret: *mut [c_uchar; zcash_history::MAX_NODE_DATA_SIZE], -) -> u32 { - let new_node_bytes: &[u8; zcash_history::MAX_NODE_DATA_SIZE] = unsafe { - match nn_ptr.as_ref() { - Some(r) => r, - None => { - return 0; - } // Null pointer passed, error - } - }; - - let mut tree = match construct_mmr_tree(cbranch, t_len, ni_ptr, n_ptr, p_len, 0) { - Ok(t) => t, - _ => { - return 0; - } // error - }; - - let node = match MMRNodeData::from_bytes(cbranch, &new_node_bytes[..]) { - Ok(node) => node, - _ => { - return 0; - } // error - }; - - let appended = match tree.append_leaf(node) { - Ok(appended) => appended, - _ => { - return 0; - } - }; - - let return_count = appended.len(); - - let root_node = tree - .root_node() - .expect("Just added, should resolve always; qed"); - unsafe { - *rt_ret = root_node.data().subtree_commitment; - - for (idx, next_buf) in slice::from_raw_parts_mut(buf_ret, return_count as usize) - .iter_mut() - .enumerate() - { - tree.resolve_link(appended[idx]) - .expect("This was generated by the tree and thus resolvable; qed") - .data() - .write(&mut &mut next_buf[..]) - .expect("Write using cursor with enough buffer size cannot fail; qed"); - } - } - - return_count as u32 -} - -#[no_mangle] -pub extern "system" fn librustzcash_mmr_delete( - // Consensus branch id - cbranch: u32, - // Length of tree in array representation - t_len: u32, - // Indices of provided tree nodes, length of p_len+e_len - ni_ptr: *const u32, - // Provided tree nodes data, length of p_len+e_len - n_ptr: *const [c_uchar; zcash_history::MAX_ENTRY_SIZE], - // Peaks count - p_len: size_t, - // Extra nodes loaded (for deletion) count - e_len: size_t, - // Return of root commitment - rt_ret: *mut [u8; 32], -) -> u32 { - let mut tree = match construct_mmr_tree(cbranch, t_len, ni_ptr, n_ptr, p_len, e_len) { - Ok(t) => t, - _ => { - return 0; - } // error - }; - - let truncate_len = match tree.truncate_leaf() { - Ok(v) => v, - _ => { - return 0; - } // Error - }; - - unsafe { - *rt_ret = tree - .root_node() - .expect("Just generated without errors, root should be resolving") - .data() - .subtree_commitment; - } - - truncate_len -} - -#[no_mangle] -pub extern "system" fn librustzcash_mmr_hash_node( - cbranch: u32, - n_ptr: *const [u8; zcash_history::MAX_NODE_DATA_SIZE], - h_ret: *mut [u8; 32], -) -> u32 { - let node_bytes: &[u8; zcash_history::MAX_NODE_DATA_SIZE] = unsafe { - match n_ptr.as_ref() { - Some(r) => r, - None => return 1, - } - }; - - let node = match MMRNodeData::from_bytes(cbranch, &node_bytes[..]) { - Ok(n) => n, - _ => return 1, // error - }; - - unsafe { - *h_ret = node.hash(); - } - - 0 -} diff --git a/src/rustzcash/src/tests/mmr.rs b/src/rustzcash/src/tests/mmr.rs deleted file mode 100644 index aa83aa37d..000000000 --- a/src/rustzcash/src/tests/mmr.rs +++ /dev/null @@ -1,225 +0,0 @@ -use zcash_history::{Entry, EntryLink, NodeData}; - -use crate::{librustzcash_mmr_append, librustzcash_mmr_delete}; - -const NODE_DATA_16L: &[u8] = include_bytes!("./res/tree16.dat"); -const NODE_DATA_1023L: &[u8] = include_bytes!("./res/tree1023.dat"); - -struct TreeView { - peaks: Vec<(u32, Entry)>, - extra: Vec<(u32, Entry)>, -} - -fn draft(into: &mut Vec<(u32, Entry)>, nodes: &[NodeData], peak_pos: usize, h: u32) { - let node_data = nodes[peak_pos - 1].clone(); - let peak: Entry = match h { - 0 => node_data.into(), - _ => Entry::new( - node_data, - EntryLink::Stored((peak_pos - (1 << h) - 1) as u32), - EntryLink::Stored((peak_pos - 2) as u32), - ), - }; - - into.push(((peak_pos - 1) as u32, peak)); -} - -fn prepare_tree(nodes: &[NodeData]) -> TreeView { - assert!(!nodes.is_empty()); - - // integer log2 of (nodes.len()+1), -1 - let mut h = (32 - ((nodes.len() + 1) as u32).leading_zeros() - 1) - 1; - let mut peak_pos = (1 << (h + 1)) - 1; - let mut peaks = Vec::new(); - - // used later - let mut last_peak_pos = 0; - let mut last_peak_h = 0; - - loop { - if peak_pos > nodes.len() { - // left child, -2^h - peak_pos -= 1 << h; - h -= 1; - } - - if peak_pos <= nodes.len() { - draft(&mut peaks, nodes, peak_pos, h); - - // save to be used in next loop - last_peak_pos = peak_pos; - last_peak_h = h; - - // right sibling - peak_pos += (1 << (h + 1)) - 1; - } - - if h == 0 { - break; - } - } - - // for deletion, everything on the right slope of the last peak should be pre-loaded - let mut extra = Vec::new(); - let mut h = last_peak_h; - let mut peak_pos = last_peak_pos; - - while h > 0 { - let left_pos = peak_pos - (1 << h); - let right_pos = peak_pos - 1; - h -= 1; - - // drafting left child - draft(&mut extra, nodes, left_pos, h); - - // drafting right child - draft(&mut extra, nodes, right_pos, h); - - // continuing on right slope - peak_pos = right_pos; - } - - TreeView { peaks, extra } -} - -fn preload_tree_append(nodes: &[NodeData]) -> (Vec, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>) { - assert!(!nodes.is_empty()); - - let tree_view = prepare_tree(nodes); - - let mut indices = Vec::new(); - let mut bytes = Vec::new(); - - for (idx, entry) in tree_view.peaks.into_iter() { - let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE]; - entry - .write(&mut &mut buf[..]) - .expect("Cannot fail if enough buffer length"); - indices.push(idx); - bytes.push(buf); - } - - (indices, bytes) -} - -// also returns number of peaks -fn preload_tree_delete( - nodes: &[NodeData], -) -> (Vec, Vec<[u8; zcash_history::MAX_ENTRY_SIZE]>, usize) { - assert!(!nodes.is_empty()); - - let tree_view = prepare_tree(nodes); - - let mut indices = Vec::new(); - let mut bytes = Vec::new(); - - let peak_count = tree_view.peaks.len(); - - for (idx, entry) in tree_view - .peaks - .into_iter() - .chain(tree_view.extra.into_iter()) - { - let mut buf = [0u8; zcash_history::MAX_ENTRY_SIZE]; - entry - .write(&mut &mut buf[..]) - .expect("Cannot fail if enough buffer length"); - indices.push(idx); - bytes.push(buf); - } - - (indices, bytes, peak_count) -} - -fn load_nodes(bytes: &'static [u8]) -> Vec { - let mut res = Vec::new(); - let mut cursor = std::io::Cursor::new(bytes); - while (cursor.position() as usize) < bytes.len() { - let node_data = zcash_history::NodeData::read(0, &mut cursor) - .expect("Statically checked to be correct"); - res.push(node_data); - } - - res -} - -#[test] -fn append() { - let nodes = load_nodes(NODE_DATA_16L); - let (indices, peaks) = preload_tree_append(&nodes); - - let mut rt_ret = [0u8; 32]; - - let mut buf_ret = Vec::<[u8; zcash_history::MAX_NODE_DATA_SIZE]>::with_capacity(32); - - let mut new_node_data = [0u8; zcash_history::MAX_NODE_DATA_SIZE]; - let new_node = NodeData { - consensus_branch_id: 0, - subtree_commitment: [0u8; 32], - start_time: 101, - end_time: 110, - start_target: 190, - end_target: 200, - start_sapling_root: [0u8; 32], - end_sapling_root: [0u8; 32], - subtree_total_work: Default::default(), - start_height: 10, - end_height: 10, - sapling_tx: 13, - }; - new_node - .write(&mut &mut new_node_data[..]) - .expect("Failed to write node data"); - - let result = librustzcash_mmr_append( - 0, - nodes.len() as u32, - indices.as_ptr(), - peaks.as_ptr(), - peaks.len(), - &new_node_data, - &mut rt_ret, - buf_ret.as_mut_ptr(), - ); - - unsafe { - buf_ret.set_len(result as usize); - } - - assert_eq!(result, 2); - - let new_node_1 = - NodeData::from_bytes(0, &buf_ret[0][..]).expect("Failed to reconstruct return node #1"); - - let new_node_2 = - NodeData::from_bytes(0, &buf_ret[1][..]).expect("Failed to reconstruct return node #2"); - - assert_eq!(new_node_1.start_height, 10); - assert_eq!(new_node_1.end_height, 10); - - // this is combined new node (which is `new_node_1`) + the one which was there before (for block #9) - assert_eq!(new_node_2.start_height, 9); - assert_eq!(new_node_2.end_height, 10); - assert_eq!(new_node_2.sapling_tx, 27); -} - -#[test] -fn delete() { - let nodes = load_nodes(NODE_DATA_1023L); - let (indices, nodes, peak_count) = preload_tree_delete(&nodes); - - let mut rt_ret = [0u8; 32]; - - let result = librustzcash_mmr_delete( - 0, - nodes.len() as u32, - indices.as_ptr(), - nodes.as_ptr(), - peak_count, - indices.len() - peak_count, - &mut rt_ret, - ); - - // Deleting from full tree of 9 height would result in cascade deleting of 10 nodes - assert_eq!(result, 10); -} diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index 1c05ee17a..a68fdc97f 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -307,16 +307,12 @@ TransactionBuilderResult TransactionBuilder::Build() auto enc = res.value(); auto encryptor = enc.second; - libzcash::SaplingPaymentAddress address(output.note.d, output.note.pk_d); - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << address; - std::vector addressBytes(ss.begin(), ss.end()); - OutputDescription odesc; if (!librustzcash_sapling_output_proof( ctx, encryptor.get_esk().begin(), - addressBytes.data(), + output.note.d.data(), + output.note.pk_d.begin(), output.note.r.begin(), output.note.value(), odesc.cv.begin(), diff --git a/src/wallet/gtest/test_wallet_zkeys.cpp b/src/wallet/gtest/test_wallet_zkeys.cpp index 374957130..e36c05078 100644 --- a/src/wallet/gtest/test_wallet_zkeys.cpp +++ b/src/wallet/gtest/test_wallet_zkeys.cpp @@ -488,6 +488,7 @@ TEST(wallet_zkeys_tests, WriteCryptedSaplingZkeyDirectToDb) { // spending key is crypted, so we can't extract valid payment address libzcash::SaplingExtendedSpendingKey keyOut; EXPECT_FALSE(wallet2.GetSaplingExtendedSpendingKey(address, keyOut)); + ASSERT_FALSE(address == keyOut.DefaultAddress()); // address -> ivk mapping is not crypted libzcash::SaplingIncomingViewingKey ivkOut; diff --git a/src/zcbenchmarks.cpp b/src/zcbenchmarks.cpp index 8136296a4..ada6f5860 100644 --- a/src/zcbenchmarks.cpp +++ b/src/zcbenchmarks.cpp @@ -658,10 +658,6 @@ double benchmark_create_sapling_output() auto enc = res.value(); auto encryptor = enc.second; - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << address; - std::vector addressBytes(ss.begin(), ss.end()); - auto ctx = librustzcash_sapling_proving_ctx_init(); struct timeval tv_start; @@ -671,7 +667,8 @@ double benchmark_create_sapling_output() bool result = librustzcash_sapling_output_proof( ctx, encryptor.get_esk().begin(), - addressBytes.data(), + note.d.data(), + note.pk_d.begin(), note.r.begin(), note.value(), odesc.cv.begin(), From a2da2a6ebc620d79f3cb495939cbebabd37b463e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 00:50:10 +0000 Subject: [PATCH 078/146] rpc: Add RPC method --- src/rpc/blockchain.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++ src/rpc/client.cpp | 1 + 2 files changed, 81 insertions(+) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 216fa4e1d..d01c8f9e2 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1486,10 +1486,90 @@ UniValue dumpbootstrap(const UniValue& params, bool fHelp) return strprintf("dumped %d blocks from %d to %d into %s", nEndBlock - nStartBlock + 1, nStartBlock, nEndBlock, pathDest); } +static UniValue getchaintxstats(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw std::runtime_error( + "getchaintxstats ( nblocks blockhash )\n" + "\nCompute statistics about the total number and rate of transactions in the chain.\n" + "\nArguments:\n" + "1. nblocks (numeric, optional) Size of the window in number of blocks (default: one month).\n" + "2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n" + "\nResult:\n" + "{\n" + " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" + " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" + " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" + " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" + " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" + " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getchaintxstats", "") + + HelpExampleRpc("getchaintxstats", "2016") + ); + + const CBlockIndex* pindex; + int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().PoWTargetSpacing(chainActive.Height()); // By default: 1 month + + bool havehash = !params[1].isNull(); + uint256 hash; + if (havehash) { + hash = uint256S(params[1].get_str()); + } + + { + LOCK(cs_main); + if (havehash) { + auto it = mapBlockIndex.find(hash); + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + pindex = it->second; + if (!chainActive.Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); + } + } else { + pindex = chainActive.Tip(); + } + } + + assert(pindex != nullptr); + + if (params[0].isNull()) { + blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1)); + } else { + blockcount = params[0].getInt(); + + if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1"); + } + } + + const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount); + int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast(); + int nTxDiff = pindex->nChainTx - pindexPast->nChainTx; + + UniValue ret(UniValue::VOBJ); + ret.pushKV("time", (int64_t)pindex->nTime); + ret.pushKV("txcount", (int64_t)pindex->nChainTx); + ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff); + if (blockcount > 0) { + ret.pushKV("window_tx_count", nTxDiff); + ret.pushKV("window_interval", nTimeDiff); + if (nTimeDiff > 0) { + ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff); + } + } + + return ret; +} + static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, + { "blockchain", "getchaintxstats", &getchaintxstats, true }, { "blockchain", "getbestblockhash", &getbestblockhash, true }, { "blockchain", "getblockcount", &getblockcount, true }, { "blockchain", "getblock", &getblock, true }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index a4c189963..8fab29ed7 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -64,6 +64,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listunspent", 2 }, { "getblock", 1 }, { "getblockheader", 1 }, + { "getchaintxstats", 0 }, { "gettransaction", 1 }, { "getrawtransaction", 1 }, { "createrawtransaction", 0 }, From 43b6c6ff781d127a6fa3a57b380b329d0e2fe531 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 01:17:10 +0000 Subject: [PATCH 079/146] Checkpoint at block 1,400,000 --- src/chainparams.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 4da7183c1..b32dc775b 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -207,12 +207,22 @@ class CMainParams : public CChainParams { ( 352540, uint256S("0x00000006838b961606dad5a3da08b595a69cb8fc78684d9a4d3d3727bc96eb2b")) ( 352640, uint256S("0x000000c4a4a131d358a4b5419171c627cfb219367a810ca1780ef3119f634b6b")) ( 352740, uint256S("0x0000006bcc7d38424a1cf996b3b4ee61c44f941523af16c26c22c2708151a977")) - ( 357600, uint256S("0x0000003b302a1ecfa6555b64981b1950853f49e022c923e98f94535225c6c54a")), - - 1530166456, // * UNIX timestamp of last checkpoint block - 662217, // * total number of transactions between genesis and last checkpoint - 2290.912865 // * estimated number of transactions per day after checkpoint - // total number of tx / (checkpoint block height / (24 * 24)) + ( 357600, uint256S("0x0000003b302a1ecfa6555b64981b1950853f49e022c923e98f94535225c6c54a")) + ( 500000, uint256S("0x0000002bf33f0c63ce3d9a9ff15c154b27dc80de1bd8ac891eb24acf13cdad40")) + ( 600000, uint256S("0x000000ed96988f1b3bd63e65dc933f226ae2e0e722f8bdcf43937f58929a9e3d")) + ( 700000, uint256S("0x00000295a14a32e2d0ca939b22295ea093bc73ee73664c6900ff1278a2e4067b")) + ( 800000, uint256S("0x0000003c5772dfbeb950307478a53a5392c2e7b3e58fc9cfea79c83583284618")) + ( 900000, uint256S("0x0000014ab739fe933f697b9a32e8aae19a3629937bd6e3f419626c49e880880f")) + ( 1000000, uint256S("0x0000007de5a17a38a4f43e44947acf084949080715e9dd6a235af9c704246cf0")) + ( 1100000, uint256S("0x000000211ead28f86240b83fff913a04caddb3a2f9e19d42a898526f30002429")) + ( 1200000, uint256S("0x00000093b2415c696284232f27a9c3a363115b197068e6788e3435d09fc77a40")) + ( 1300000, uint256S("0x000000c525dea4be1246e9b5b1a0db728d7f8ac2fa28dfe0de0d9c82ba79c934")) + ( 1400000, uint256S("0x000000d6ec96846f53f6c17b1d6c450be50ad54a25ed047deb3076cd31c0f5fd")), + + 1722854257, // * UNIX timestamp of last checkpoint block + 3896307, // * total number of transactions between genesis and last checkpoint + 2244.272832 // * estimated number of transactions per day after checkpoint + // total number of tx / (checkpoint block height / (24 * 24)) }; // Hardcoded fallback value for the Sprout shielded value pool balance From 609ea0fe68d4b5f763ff73c0d888cc9ca834f984 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 07:56:22 +0000 Subject: [PATCH 080/146] Revert "Update minimum chain work on mainnet to reflect Sapling activation" This reverts commit c18e105e5b144f3ce6488e2a11f614d1577bfeda. --- src/chainparams.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index b32dc775b..c9815d323 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -115,7 +115,7 @@ class CMainParams : public CChainParams { Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; // The best chain should have at least this much work. - consensus.nMinimumChainWork = uint256S("0x000000aa11b9ee1025e94db2877ea0e238eea492ffb33b67df8ba5766711db76"); + // consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000a95cc5099213e3"); /** * The message start string should be awesome! ⓩ❤ From 9cd4fc6bc3ef40c9f636200ab38feb7ef0366365 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 08:29:54 +0000 Subject: [PATCH 081/146] banlist.dat: store banlist on disk --- qa/rpc-tests/nodehandling.py | 23 ++- src/Makefile.am | 2 + src/Makefile.test.include | 1 + src/addrdb.cpp | 216 ++++++++++++++++++++++++ src/addrdb.h | 102 ++++++++++++ src/main.cpp | 2 +- src/net.cpp | 308 ++++++++++++++++++----------------- src/net.h | 31 ++-- src/netbase.cpp | 63 ++++++- src/netbase.h | 12 ++ src/rpc/net.cpp | 18 +- src/test/net_tests.cpp | 137 ++++++++++++++++ src/test/netbase_tests.cpp | 89 ++++++++++ src/test/rpc_tests.cpp | 20 +-- src/ui_interface.h | 3 + 15 files changed, 834 insertions(+), 193 deletions(-) create mode 100644 src/addrdb.cpp create mode 100644 src/addrdb.h create mode 100644 src/test/net_tests.cpp diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py index 424583bfb..6d25ecd3f 100755 --- a/qa/rpc-tests/nodehandling.py +++ b/qa/rpc-tests/nodehandling.py @@ -10,7 +10,8 @@ import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, connect_nodes_bi, p2p_port +from test_framework.util import assert_equal, connect_nodes_bi, p2p_port, \ + start_node, stop_node import time @@ -47,7 +48,25 @@ def run_test(self): assert_equal(len(self.nodes[2].listbanned()), 0) self.nodes[2].clearbanned() assert_equal(len(self.nodes[2].listbanned()), 0) - + + ##test persisted banlist + self.nodes[2].setban("127.0.0.0/32", "add") + self.nodes[2].setban("127.0.0.0/24", "add") + self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds + self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds + listBeforeShutdown = self.nodes[2].listbanned(); + assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) #must be here + time.sleep(2) #make 100% sure we expired 192.168.0.1 node time + + #stop node + stop_node(self.nodes[2], 2) + + self.nodes[2] = start_node(2, self.options.tmpdir) + listAfterShutdown = self.nodes[2].listbanned(); + assert_equal("127.0.0.0/24", listAfterShutdown[0]['address']) + assert_equal("127.0.0.0/32", listAfterShutdown[1]['address']) + assert_equal("/19" in listAfterShutdown[2]['address'], True) + ########################### # RPC disconnectnode test # ########################### diff --git a/src/Makefile.am b/src/Makefile.am index ac2e63739..fdcd8a825 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -92,6 +92,7 @@ LIBZCASH_H = \ .PHONY: FORCE cargo-build check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ + addrdb.h \ addressindex.h \ addrman.h \ alert.h \ @@ -227,6 +228,7 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_server_a_SOURCES = \ sendalert.cpp \ addrman.cpp \ + addrdb.cpp \ alert.cpp \ alertkeys.h \ asyncrpcoperation.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 94893ba5d..191c3f349 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -81,6 +81,7 @@ BITCOIN_TESTS =\ test/merkle_tests.cpp \ test/miner_tests.cpp \ test/multisig_tests.cpp \ + test/net_tests.cpp \ test/netbase_tests.cpp \ test/pmt_tests.cpp \ test/policyestimator_tests.cpp \ diff --git a/src/addrdb.cpp b/src/addrdb.cpp new file mode 100644 index 000000000..b65507d8f --- /dev/null +++ b/src/addrdb.cpp @@ -0,0 +1,216 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "addrdb.h" + +#include "addrman.h" +#include "chainparams.h" +#include "clientversion.h" +#include "hash.h" +#include "random.h" +#include "streams.h" +#include "tinyformat.h" +#include "util.h" + +CBanDB::CBanDB() +{ + pathBanlist = GetDataDir() / "banlist.dat"; +} + +bool CBanDB::Write(const banmap_t& banSet) +{ + // Generate random temporary filename + unsigned short randv = 0; + GetRandBytes((unsigned char*)&randv, sizeof(randv)); + std::string tmpfn = strprintf("banlist.dat.%04x", randv); + + // serialize banlist, checksum data up to that point, then append csum + CDataStream ssBanlist(SER_DISK, CLIENT_VERSION); + ssBanlist << FLATDATA(Params().MessageStart()); + ssBanlist << banSet; + uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end()); + ssBanlist << hash; + + // open temp output file, and associate with CAutoFile + fs::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fsbridge::fopen(pathTmp, "wb"); + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: Failed to open file %s", __func__, pathTmp.string()); + + // Write and commit header, data + try { + fileout << ssBanlist; + } + catch (const std::exception& e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + FileCommit(fileout.Get()); + fileout.fclose(); + + // replace existing banlist.dat, if any, with new banlist.dat.XXXX + if (!RenameOver(pathTmp, pathBanlist)) + return error("%s: Rename-into-place failed", __func__); + + return true; +} + +bool CBanDB::Read(banmap_t& banSet) +{ + // open input file, and associate with CAutoFile + FILE *file = fsbridge::fopen(pathBanlist, "rb"); + CAutoFile filein(file, SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: Failed to open file %s", __func__, pathBanlist.string()); + + // use file size to size memory buffer + uint64_t fileSize = fs::file_size(pathBanlist); + uint64_t dataSize = 0; + // Don't try to resize to a negative number if file is small + if (fileSize >= sizeof(uint256)) + dataSize = fileSize - sizeof(uint256); + std::vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + filein.fclose(); + + CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end()); + if (hashIn != hashTmp) + return error("%s: Checksum mismatch, data corrupted", __func__); + + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (network specific magic number) and .. + ssBanlist >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + return error("%s: Invalid network magic number", __func__); + + // de-serialize address data into one CAddrMan object + ssBanlist >> banSet; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + + return true; +} + +CAddrDB::CAddrDB() +{ + pathAddr = GetDataDir() / "peers.dat"; +} + +bool CAddrDB::Write(const CAddrMan& addr) +{ + // Generate random temporary filename + unsigned short randv = 0; + GetRandBytes((unsigned char*)&randv, sizeof(randv)); + std::string tmpfn = strprintf("peers.dat.%04x", randv); + + // serialize addresses, checksum data up to that point, then append csum + CDataStream ssPeers(SER_DISK, CLIENT_VERSION); + ssPeers << FLATDATA(Params().MessageStart()); + ssPeers << addr; + uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); + ssPeers << hash; + + // open temp output file, and associate with CAutoFile + fs::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fsbridge::fopen(pathTmp, "wb"); + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: Failed to open file %s", __func__, pathTmp.string()); + + // Write and commit header, data + try { + fileout << ssPeers; + } + catch (const std::exception& e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + FileCommit(fileout.Get()); + fileout.fclose(); + + // replace existing peers.dat, if any, with new peers.dat.XXXX + if (!RenameOver(pathTmp, pathAddr)) + return error("%s: Rename-into-place failed", __func__); + + return true; +} + +bool CAddrDB::Read(CAddrMan& addr) +{ + // open input file, and associate with CAutoFile + FILE *file = fsbridge::fopen(pathAddr, "rb"); + CAutoFile filein(file, SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: Failed to open file %s", __func__, pathAddr.string()); + + // use file size to size memory buffer + uint64_t fileSize = fs::file_size(pathAddr); + uint64_t dataSize = 0; + // Don't try to resize to a negative number if file is small + if (fileSize >= sizeof(uint256)) + dataSize = fileSize - sizeof(uint256); + std::vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + filein.fclose(); + + CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); + if (hashIn != hashTmp) + return error("%s: Checksum mismatch, data corrupted", __func__); + + return Read(addr, ssPeers); +} + +bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) +{ + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (network specific magic number) and .. + ssPeers >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + return error("%s: Invalid network magic number", __func__); + + // de-serialize address data into one CAddrMan object + ssPeers >> addr; + } + catch (const std::exception& e) { + // de-serialization has failed, ensure addrman is left in a clean state + addr.Clear(); + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + + return true; +} diff --git a/src/addrdb.h b/src/addrdb.h new file mode 100644 index 000000000..739377acf --- /dev/null +++ b/src/addrdb.h @@ -0,0 +1,102 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#ifndef BITCOIN_ADDRDB_H +#define BITCOIN_ADDRDB_H + +#include "fs.h" +#include "serialize.h" +#include "streams.h" + +#include +#include + +class CSubNet; +class CAddrMan; + +typedef enum BanReason +{ + BanReasonUnknown = 0, + BanReasonNodeMisbehaving = 1, + BanReasonManuallyAdded = 2 +} BanReason; + +class CBanEntry +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + int64_t nCreateTime; + int64_t nBanUntil; + uint8_t banReason; + + CBanEntry() + { + SetNull(); + } + + CBanEntry(int64_t nCreateTimeIn) + { + SetNull(); + nCreateTime = nCreateTimeIn; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(this->nVersion); + READWRITE(nCreateTime); + READWRITE(nBanUntil); + READWRITE(banReason); + } + + void SetNull() + { + nVersion = CBanEntry::CURRENT_VERSION; + nCreateTime = 0; + nBanUntil = 0; + banReason = BanReasonUnknown; + } + + std::string banReasonToString() + { + switch (banReason) { + case BanReasonNodeMisbehaving: + return "node misbehaving"; + case BanReasonManuallyAdded: + return "manually added"; + default: + return "unknown"; + } + } +}; + +typedef std::map banmap_t; + +/** Access to the (IP) address database (peers.dat) */ +class CAddrDB +{ +private: + fs::path pathAddr; +public: + CAddrDB(); + bool Write(const CAddrMan& addr); + bool Read(CAddrMan& addr); + bool Read(CAddrMan& addr, CDataStream& ssPeers); +}; + +/** Access to the banlist database (banlist.dat) */ +class CBanDB +{ +private: + fs::path pathBanlist; +public: + CBanDB(); + bool Write(const banmap_t& banSet); + bool Read(banmap_t& banSet); +}; + +#endif // BITCOIN_ADDRDB_H diff --git a/src/main.cpp b/src/main.cpp index 1b839015d..451c572eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6470,7 +6470,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); else { - CNode::Ban(pto->addr); + CNode::Ban(pto->addr, BanReasonNodeMisbehaving); } } state.fShouldBan = false; diff --git a/src/net.cpp b/src/net.cpp index 8078de2b6..cf1b083b5 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -32,7 +32,7 @@ #include -// Dump addresses to peers.dat every 15 minutes (900s) +// Dump addresses to peers.dat and banlist.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 using namespace std; @@ -404,6 +404,26 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) return NULL; } +static void DumpBanlist() +{ + CNode::SweepBanned(); // clean unused entries (if bantime has expired) + + if (!CNode::BannedSetIsDirty()) + return; + + int64_t nStart = GetTimeMillis(); + + CBanDB bandb; + banmap_t banmap; + CNode::SetBannedSetDirty(false); + CNode::GetBanned(banmap); + if (!bandb.Write(banmap)) + CNode::SetBannedSetDirty(true); + + LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); +} + void CNode::CloseSocketDisconnect() { fDisconnect = true; @@ -439,81 +459,148 @@ void CNode::PushVersion() -std::map CNode::setBanned; +banmap_t CNode::setBanned; CCriticalSection CNode::cs_setBanned; +bool CNode::setBannedIsDirty; void CNode::ClearBanned() { - LOCK(cs_setBanned); - setBanned.clear(); + { + LOCK(cs_setBanned); + setBanned.clear(); + setBannedIsDirty = true; + } + DumpBanlist(); //store banlist to disk + uiInterface.BannedListChanged(); } bool CNode::IsBanned(CNetAddr ip) { - bool fResult = false; + LOCK(cs_setBanned); + for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) { - LOCK(cs_setBanned); - for (std::map::iterator it = setBanned.begin(); it != setBanned.end(); it++) - { - CSubNet subNet = (*it).first; - int64_t t = (*it).second; + CSubNet subNet = (*it).first; + CBanEntry banEntry = (*it).second; - if(subNet.Match(ip) && GetTime() < t) - fResult = true; + if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) { + return true; } } - return fResult; + return false; } bool CNode::IsBanned(CSubNet subnet) { - bool fResult = false; + LOCK(cs_setBanned); + banmap_t::iterator i = setBanned.find(subnet); + if (i != setBanned.end()) { - LOCK(cs_setBanned); - std::map::iterator i = setBanned.find(subnet); - if (i != setBanned.end()) - { - int64_t t = (*i).second; - if (GetTime() < t) - fResult = true; + CBanEntry banEntry = (*i).second; + if (GetTime() < banEntry.nBanUntil) { + return true; } } - return fResult; + return false; } -void CNode::Ban(const CNetAddr& addr, int64_t bantimeoffset, bool sinceUnixEpoch) { - CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128")); - Ban(subNet, bantimeoffset, sinceUnixEpoch); +void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { + CSubNet subNet(addr); + Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); } -void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) { - int64_t banTime = GetTime()+GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME); - if (bantimeoffset > 0) - banTime = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; +void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { + CBanEntry banEntry(GetTime()); + banEntry.banReason = banReason; + if (bantimeoffset <= 0) + { + bantimeoffset = GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME); + sinceUnixEpoch = false; + } + banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; - LOCK(cs_setBanned); - if (setBanned[subNet] < banTime) - setBanned[subNet] = banTime; + { + LOCK(cs_setBanned); + if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) { + setBanned[subNet] = banEntry; + setBannedIsDirty = true; + } + else + return; + } + uiInterface.BannedListChanged(); + { + LOCK(cs_vNodes); + for(CNode* pnode : vNodes) { + if (subNet.Match((CNetAddr)pnode->addr)) + pnode->fDisconnect = true; + } + } + if(banReason == BanReasonManuallyAdded) + DumpBanlist(); //store banlist to disk immediately if user requested ban } bool CNode::Unban(const CNetAddr &addr) { - CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128")); + CSubNet subNet(addr); return Unban(subNet); } bool CNode::Unban(const CSubNet &subNet) { - LOCK(cs_setBanned); - if (setBanned.erase(subNet)) - return true; - return false; + { + LOCK(cs_setBanned); + if (!setBanned.erase(subNet)) + return false; + setBannedIsDirty = true; + } + uiInterface.BannedListChanged(); + DumpBanlist(); //store banlist to disk immediately + return true; } -void CNode::GetBanned(std::map &banMap) +void CNode::GetBanned(banmap_t &banMap) { LOCK(cs_setBanned); banMap = setBanned; //create a thread safe copy } +void CNode::SetBanned(const banmap_t &banMap) +{ + LOCK(cs_setBanned); + setBanned = banMap; + setBannedIsDirty = true; +} + +void CNode::SweepBanned() +{ + int64_t now = GetTime(); + + LOCK(cs_setBanned); + banmap_t::iterator it = setBanned.begin(); + while(it != setBanned.end()) + { + CSubNet subNet = (*it).first; + CBanEntry banEntry = (*it).second; + if(now > banEntry.nBanUntil) + { + setBanned.erase(it++); + setBannedIsDirty = true; + LogPrint("net", "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString()); + } + else + ++it; + } +} + +bool CNode::BannedSetIsDirty() +{ + LOCK(cs_setBanned); + return setBannedIsDirty; +} + +void CNode::SetBannedSetDirty(bool dirty) +{ + LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag + setBannedIsDirty = dirty; +} std::vector CNode::vWhitelistedRange; CCriticalSection CNode::cs_vWhitelistedRange; @@ -1264,6 +1351,12 @@ void DumpAddresses() addrman.size(), GetTimeMillis() - nStart); } +void DumpData() +{ + DumpAddresses(); + DumpBanlist(); +} + void static ProcessOneShot() { string strDest; @@ -1707,20 +1800,44 @@ void static Discover(boost::thread_group& threadGroup) void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) { uiInterface.InitMessage(_("Loading addresses...")); - // Load addresses for peers.dat + // Load addresses from peers.dat int64_t nStart = GetTimeMillis(); { CAddrDB adb; - if (!adb.Read(addrman)) + if (adb.Read(addrman)) + LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); + else { + addrman.Clear(); // Addrman can be in an inconsistent state after failure, reset it LogPrintf("Invalid or missing peers.dat; recreating\n"); + DumpAddresses(); + } } - LogPrintf("Loaded %i addresses from peers.dat %dms\n", - addrman.size(), GetTimeMillis() - nStart); + + uiInterface.InitMessage(_("Loading banlist...")); + // Load addresses from banlist.dat + nStart = GetTimeMillis(); + CBanDB bandb; + banmap_t banmap; + if (bandb.Read(banmap)) { + CNode::SetBanned(banmap); // thread save setter + CNode::SetBannedSetDirty(false); // no need to write down, just read data + CNode::SweepBanned(); // sweep out unused entries + + LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); + } else { + LogPrintf("Invalid or missing banlist.dat; recreating\n"); + CNode::SetBannedSetDirty(true); // force write + DumpBanlist(); + } + + uiInterface.InitMessage(_("Starting network threads...")); + fAddressesInitialized = true; if (semOutbound == NULL) { // initialize semaphore - int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); + int nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, nMaxConnections); semOutbound = new CSemaphore(nMaxOutbound); } @@ -1752,7 +1869,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) threadGroup.create_thread(boost::bind(&TraceThread, "msghand", &ThreadMessageHandler)); // Dump network addresses - scheduler.scheduleEvery(&DumpAddresses, DUMP_ADDRESSES_INTERVAL); + scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL); } bool StopNode() @@ -1764,7 +1881,7 @@ bool StopNode() if (fAddressesInitialized) { - DumpAddresses(); + DumpData(); fAddressesInitialized = false; } @@ -1998,107 +2115,6 @@ void CNode::Fuzz(int nChance) Fuzz(2); } -// -// CAddrDB -// - -CAddrDB::CAddrDB() -{ - pathAddr = GetDataDir() / "peers.dat"; -} - -bool CAddrDB::Write(const CAddrMan& addr) -{ - // Generate random temporary filename - unsigned short randv = 0; - GetRandBytes((unsigned char*)&randv, sizeof(randv)); - std::string tmpfn = strprintf("peers.dat.%04x", randv); - - // serialize addresses, checksum data up to that point, then append csum - CDataStream ssPeers(SER_DISK, CLIENT_VERSION); - ssPeers << FLATDATA(Params().MessageStart()); - ssPeers << addr; - uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); - ssPeers << hash; - - // open temp output file, and associate with CAutoFile - fs::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fsbridge::fopen(pathTmp, "wb"); - CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: Failed to open file %s", __func__, pathTmp.string()); - - // Write and commit header, data - try { - fileout << ssPeers; - } - catch (const std::exception& e) { - return error("%s: Serialize or I/O error - %s", __func__, e.what()); - } - FileCommit(fileout.Get()); - fileout.fclose(); - - // replace existing peers.dat, if any, with new peers.dat.XXXX - if (!RenameOver(pathTmp, pathAddr)) - return error("%s: Rename-into-place failed", __func__); - - return true; -} - -bool CAddrDB::Read(CAddrMan& addr) -{ - // open input file, and associate with CAutoFile - FILE *file = fsbridge::fopen(pathAddr, "rb"); - CAutoFile filein(file, SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: Failed to open file %s", __func__, pathAddr.string()); - - // use file size to size memory buffer - int fileSize = fs::file_size(pathAddr); - int dataSize = fileSize - sizeof(uint256); - // Don't try to resize to a negative number if file is small - if (dataSize < 0) - dataSize = 0; - vector vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - filein.fclose(); - - CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); - if (hashIn != hashTmp) - return error("%s: Checksum mismatch, data corrupted", __func__); - - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (network specific magic number) and .. - ssPeers >> FLATDATA(pchMsgTmp); - - // ... verify the network matches ours - if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) - return error("%s: Invalid network magic number", __func__); - - // de-serialize address data into one CAddrMan object - ssPeers >> addr; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - - return true; -} - unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); } unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); } diff --git a/src/net.h b/src/net.h index cbd4930e6..d5f6fe7ca 100644 --- a/src/net.h +++ b/src/net.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_NET_H #define BITCOIN_NET_H +#include "addrdb.h" #include "bloom.h" #include "compat.h" #include "fs.h" @@ -244,9 +245,6 @@ class CNetMessage { }; - - - /** Information about a peer */ class CNode { @@ -305,8 +303,9 @@ class CNode // Denial-of-service detection/prevention // Key is IP address, value is banned-until-time - static std::map setBanned; + static banmap_t setBanned; static CCriticalSection cs_setBanned; + static bool setBannedIsDirty; // Whitelisted ranges. Any node connecting from these is automatically // whitelisted (as well as those connecting to whitelisted binds). @@ -640,11 +639,19 @@ class CNode static void ClearBanned(); // needed for unit testing static bool IsBanned(CNetAddr ip); static bool IsBanned(CSubNet subnet); - static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); - static void Ban(const CSubNet &subNet, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + static void Ban(const CNetAddr &ip, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + static void Ban(const CSubNet &subNet, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); static bool Unban(const CNetAddr &ip); static bool Unban(const CSubNet &ip); - static void GetBanned(std::map &banmap); + static void GetBanned(banmap_t &banmap); + static void SetBanned(const banmap_t &banmap); + + //!check is the banlist has unwritten changes + static bool BannedSetIsDirty(); + //!set the "dirty" flag for the banlist + static void SetBannedSetDirty(bool dirty=true); + //!clean unused entires (if bantime has expired) + static void SweepBanned(); void copyStats(CNodeStats &stats); @@ -686,15 +693,5 @@ class CTransaction; void RelayTransaction(const CTransaction& tx); void RelayTransaction(const CTransaction& tx, const CDataStream& ss); -/** Access to the (IP) address database (peers.dat) */ -class CAddrDB -{ -private: - fs::path pathAddr; -public: - CAddrDB(); - bool Write(const CAddrMan& addr); - bool Read(CAddrMan& addr); -}; #endif // BITCOIN_NET_H diff --git a/src/netbase.cpp b/src/netbase.cpp index 4d2d47c73..1b02bac1d 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -1297,6 +1297,13 @@ CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup) network.ip[x] &= netmask[x]; } +CSubNet::CSubNet(const CNetAddr &addr): + valid(addr.IsValid()) +{ + memset(netmask, 255, sizeof(netmask)); + network = addr; +} + bool CSubNet::Match(const CNetAddr &addr) const { if (!valid || !addr.IsValid()) @@ -1307,17 +1314,57 @@ bool CSubNet::Match(const CNetAddr &addr) const return true; } +static inline int NetmaskBits(uint8_t x) +{ + switch(x) { + case 0x00: return 0; break; + case 0x80: return 1; break; + case 0xc0: return 2; break; + case 0xe0: return 3; break; + case 0xf0: return 4; break; + case 0xf8: return 5; break; + case 0xfc: return 6; break; + case 0xfe: return 7; break; + case 0xff: return 8; break; + default: return -1; break; + } +} + std::string CSubNet::ToString() const { + /* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */ + int cidr = 0; + bool valid_cidr = true; + int n = network.IsIPv4() ? 12 : 0; + for (; n < 16 && netmask[n] == 0xff; ++n) + cidr += 8; + if (n < 16) { + int bits = NetmaskBits(netmask[n]); + if (bits < 0) + valid_cidr = false; + else + cidr += bits; + ++n; + } + for (; n < 16 && valid_cidr; ++n) + if (netmask[n] != 0x00) + valid_cidr = false; + + /* Format output */ std::string strNetmask; - if (network.IsIPv4()) - strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]); - else - strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x", - netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3], - netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7], - netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11], - netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]); + if (valid_cidr) { + strNetmask = strprintf("%u", cidr); + } else { + if (network.IsIPv4()) + strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]); + else + strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x", + netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3], + netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7], + netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11], + netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]); + } + return network.ToString() + "/" + strNetmask; } diff --git a/src/netbase.h b/src/netbase.h index fcf986a67..e66b17c16 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -120,6 +120,9 @@ class CSubNet CSubNet(); explicit CSubNet(const std::string &strSubnet, bool fAllowLookup = false); + //constructor for single ip subnet (/32 or /128) + explicit CSubNet(const CNetAddr &addr); + bool Match(const CNetAddr &addr) const; std::string ToString() const; @@ -128,6 +131,15 @@ class CSubNet friend bool operator==(const CSubNet& a, const CSubNet& b); friend bool operator!=(const CSubNet& a, const CSubNet& b); friend bool operator<(const CSubNet& a, const CSubNet& b); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(network); + READWRITE(FLATDATA(netmask)); + READWRITE(FLATDATA(valid)); + } }; /** A combination of a network address (CNetAddr) and a (TCP) port */ diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index e1f9dc0dc..d8631bed4 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -10,6 +10,7 @@ #include "netbase.h" #include "protocol.h" #include "sync.h" +#include "ui_interface.h" #include "util.h" #include "version.h" #include "deprecation.h" @@ -565,18 +566,13 @@ UniValue setban(const UniValue& params, bool fHelp) if (params.size() == 4 && params[3].isTrue()) absolute = true; - isSubnet ? CNode::Ban(subNet, banTime, absolute) : CNode::Ban(netAddr, banTime, absolute); - - //disconnect possible nodes - while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) - bannedNode->fDisconnect = true; + isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); } else if(strCommand == "remove") { if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) )) throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); } - return NullUniValue; } @@ -591,15 +587,19 @@ UniValue listbanned(const UniValue& params, bool fHelp) + HelpExampleRpc("listbanned", "") ); - std::map banMap; + banmap_t banMap; CNode::GetBanned(banMap); UniValue bannedAddresses(UniValue::VARR); - for (std::map::iterator it = banMap.begin(); it != banMap.end(); it++) + for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) { + CBanEntry banEntry = (*it).second; UniValue rec(UniValue::VOBJ); rec.pushKV("address", (*it).first.ToString()); - rec.pushKV("banned_until", (*it).second); + rec.pushKV("banned_until", banEntry.nBanUntil); + rec.pushKV("ban_created", banEntry.nCreateTime); + rec.pushKV("ban_reason", banEntry.banReasonToString()); + bannedAddresses.push_back(rec); } diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp new file mode 100644 index 000000000..654d5c8f4 --- /dev/null +++ b/src/test/net_tests.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2012-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://www.opensource.org/licenses/mit-license.php . + +#include "addrman.h" +#include "test/test_bitcoin.h" +#include +#include +#include "hash.h" +#include "serialize.h" +#include "streams.h" +#include "net.h" +#include "chainparams.h" + +using namespace std; + +class CAddrManSerializationMock : public CAddrMan +{ +public: + virtual void Serialize(CBaseDataStream& s) const = 0; +}; + +class CAddrManUncorrupted : public CAddrManSerializationMock +{ +public: + void Serialize(CBaseDataStream& s) const + { + CAddrMan::Serialize(s); + } +}; + +class CAddrManCorrupted : public CAddrManSerializationMock +{ +public: + void Serialize(CBaseDataStream& s) const + { + // Produces corrupt output that claims addrman has 20 addrs when it only has one addr. + unsigned char nVersion = 1; + s << nVersion; + s << ((unsigned char)32); + s << nKey; + s << 10; // nNew + s << 10; // nTried + + int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30); + s << nUBuckets; + + CAddress addr = CAddress(CService("252.1.1.1", 7777)); + CAddrInfo info = CAddrInfo(addr, CNetAddr("252.2.2.2")); + s << info; + } +}; + +CDataStream AddrmanToStream(CAddrManSerializationMock& addrman) +{ + CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION); + ssPeersIn << FLATDATA(Params().MessageStart()); + ssPeersIn << addrman; + std::string str = ssPeersIn.str(); + vector vchData(str.begin(), str.end()); + return CDataStream(vchData, SER_DISK, CLIENT_VERSION); +} + +BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(caddrdb_read) +{ + CAddrManUncorrupted addrmanUncorrupted; + + CService addr1 = CService("250.7.1.1", 8333); + CService addr2 = CService("250.7.2.2", 9999); + CService addr3 = CService("250.7.3.3", 9999); + + // Add three addresses to new table. + addrmanUncorrupted.Add(CAddress(addr1), CService("252.5.1.1", 8333)); + addrmanUncorrupted.Add(CAddress(addr2), CService("252.5.1.1", 8333)); + addrmanUncorrupted.Add(CAddress(addr3), CService("252.5.1.1", 8333)); + + // Test that the de-serialization does not throw an exception. + CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted); + bool exceptionThrown = false; + CAddrMan addrman1; + + BOOST_CHECK(addrman1.size() == 0); + try { + unsigned char pchMsgTmp[4]; + ssPeers1 >> FLATDATA(pchMsgTmp); + ssPeers1 >> addrman1; + } catch (const std::exception& e) { + exceptionThrown = true; + } + + BOOST_CHECK(addrman1.size() == 3); + BOOST_CHECK(exceptionThrown == false); + + // Test that CAddrDB::Read creates an addrman with the correct number of addrs. + CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted); + + CAddrMan addrman2; + CAddrDB adb; + BOOST_CHECK(addrman2.size() == 0); + adb.Read(addrman2, ssPeers2); + BOOST_CHECK(addrman2.size() == 3); +} + + +BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) +{ + CAddrManCorrupted addrmanCorrupted; + + // Test that the de-serialization of corrupted addrman throws an exception. + CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted); + bool exceptionThrown = false; + CAddrMan addrman1; + BOOST_CHECK(addrman1.size() == 0); + try { + unsigned char pchMsgTmp[4]; + ssPeers1 >> FLATDATA(pchMsgTmp); + ssPeers1 >> addrman1; + } catch (const std::exception& e) { + exceptionThrown = true; + } + // Even through de-serialization failed adddrman is not left in a clean state. + BOOST_CHECK(addrman1.size() == 1); + BOOST_CHECK(exceptionThrown); + + // Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails. + CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted); + + CAddrMan addrman2; + CAddrDB adb; + BOOST_CHECK(addrman2.size() == 0); + adb.Read(addrman2, ssPeers2); + BOOST_CHECK(addrman2.size() == 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index d81a10038..e10f95382 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -144,6 +144,95 @@ BOOST_AUTO_TEST_CASE(subnet_test) BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8/128").IsValid()); BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8/129").IsValid()); BOOST_CHECK(!CSubNet("fuzzy").IsValid()); + + //CNetAddr constructor test + BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).IsValid()); + BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.1"))); + BOOST_CHECK(!CSubNet(CNetAddr("127.0.0.1")).Match(CNetAddr("127.0.0.2"))); + BOOST_CHECK(CSubNet(CNetAddr("127.0.0.1")).ToString() == "127.0.0.1/32"); + + BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).IsValid()); + BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:8"))); + BOOST_CHECK(!CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).Match(CNetAddr("1:2:3:4:5:6:7:9"))); + BOOST_CHECK(CSubNet(CNetAddr("1:2:3:4:5:6:7:8")).ToString() == "1:2:3:4:5:6:7:8/128"); + + CSubNet subnet = CSubNet("1.2.3.4/255.255.255.255"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/32"); + subnet = CSubNet("1.2.3.4/255.255.255.254"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/31"); + subnet = CSubNet("1.2.3.4/255.255.255.252"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.4/30"); + subnet = CSubNet("1.2.3.4/255.255.255.248"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/29"); + subnet = CSubNet("1.2.3.4/255.255.255.240"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/28"); + subnet = CSubNet("1.2.3.4/255.255.255.224"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/27"); + subnet = CSubNet("1.2.3.4/255.255.255.192"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/26"); + subnet = CSubNet("1.2.3.4/255.255.255.128"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/25"); + subnet = CSubNet("1.2.3.4/255.255.255.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.3.0/24"); + subnet = CSubNet("1.2.3.4/255.255.254.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.2.0/23"); + subnet = CSubNet("1.2.3.4/255.255.252.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/22"); + subnet = CSubNet("1.2.3.4/255.255.248.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/21"); + subnet = CSubNet("1.2.3.4/255.255.240.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/20"); + subnet = CSubNet("1.2.3.4/255.255.224.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/19"); + subnet = CSubNet("1.2.3.4/255.255.192.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/18"); + subnet = CSubNet("1.2.3.4/255.255.128.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/17"); + subnet = CSubNet("1.2.3.4/255.255.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/16"); + subnet = CSubNet("1.2.3.4/255.254.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/15"); + subnet = CSubNet("1.2.3.4/255.252.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/14"); + subnet = CSubNet("1.2.3.4/255.248.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/13"); + subnet = CSubNet("1.2.3.4/255.240.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/12"); + subnet = CSubNet("1.2.3.4/255.224.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/11"); + subnet = CSubNet("1.2.3.4/255.192.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/10"); + subnet = CSubNet("1.2.3.4/255.128.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/9"); + subnet = CSubNet("1.2.3.4/255.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.0.0.0/8"); + subnet = CSubNet("1.2.3.4/254.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/7"); + subnet = CSubNet("1.2.3.4/252.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/6"); + subnet = CSubNet("1.2.3.4/248.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/5"); + subnet = CSubNet("1.2.3.4/240.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/4"); + subnet = CSubNet("1.2.3.4/224.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/3"); + subnet = CSubNet("1.2.3.4/192.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/2"); + subnet = CSubNet("1.2.3.4/128.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/1"); + subnet = CSubNet("1.2.3.4/0.0.0.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "0.0.0.0/0"); + + subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/128"); + subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:0000:0000:0000:0000:0000:0000:0000"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1::/16"); + subnet = CSubNet("1:2:3:4:5:6:7:8/0000:0000:0000:0000:0000:0000:0000:0000"); + BOOST_CHECK_EQUAL(subnet.ToString(), "::/0"); + subnet = CSubNet("1.2.3.4/255.255.232.0"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1.2.0.0/255.255.232.0"); + subnet = CSubNet("1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f"); + BOOST_CHECK_EQUAL(subnet.ToString(), "1:2:3:4:5:6:7:8/ffff:ffff:ffff:fffe:ffff:ffff:ffff:ff0f"); } BOOST_AUTO_TEST_CASE(netbase_getgroup) diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 85678f5c3..4924db23c 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -244,20 +244,20 @@ BOOST_AUTO_TEST_CASE(rpc_ban) UniValue ar = r.get_array(); UniValue o1 = ar[0].get_obj(); UniValue adr = o1.find_value("address"); - BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.255"); + BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/32"); BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0 remove")));; BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); ar = r.get_array(); BOOST_CHECK_EQUAL(ar.size(), 0); - BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/24 add 1607731200 true"))); - BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + BOOST_CHECK_NO_THROW(r = CallRPC(std::string("setban 127.0.0.0/24 add 9907731200 true"))); + BOOST_CHECK_NO_THROW(r = CallRPC(std::string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); adr = o1.find_value("address"); UniValue banned_until = o1.find_value("banned_until"); - BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0"); - BOOST_CHECK_EQUAL(banned_until.getInt(), 1607731200); // absolute time check + BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); + BOOST_CHECK_EQUAL(banned_until.getInt(), 9907731200); // absolute time check BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); @@ -267,7 +267,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) o1 = ar[0].get_obj(); adr = o1.find_value("address"); banned_until = o1.find_value("banned_until"); - BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0"); + BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); int64_t now = GetTime(); BOOST_CHECK(banned_until.getInt() > now); BOOST_CHECK(banned_until.getInt()-now <= 200); @@ -297,15 +297,15 @@ BOOST_AUTO_TEST_CASE(rpc_ban) ar = r.get_array(); o1 = ar[0].get_obj(); adr = o1.find_value("address"); - BOOST_CHECK_EQUAL(adr.get_str(), "fe80::202:b3ff:fe1e:8329/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + BOOST_CHECK_EQUAL(adr.get_str(), "fe80::202:b3ff:fe1e:8329/128"); BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); - BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:db8::/30 add"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:db8::/ffff:fffc:0:0:0:0:0:0 add"))); BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); ar = r.get_array(); o1 = ar[0].get_obj(); adr = o1.find_value("address"); - BOOST_CHECK_EQUAL(adr.get_str(), "2001:db8::/ffff:fffc:0:0:0:0:0:0"); + BOOST_CHECK_EQUAL(adr.get_str(), "2001:db8::/30"); BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128 add"))); @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) ar = r.get_array(); o1 = ar[0].get_obj(); adr = o1.find_value("address"); - BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128"); } diff --git a/src/ui_interface.h b/src/ui_interface.h index 7c48d8fdb..b94a65fa8 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -103,6 +103,9 @@ class CClientUIInterface /** Best header has changed */ boost::signals2::signal NotifyHeaderTip; + /** Banlist did change. */ + boost::signals2::signal BannedListChanged; + /** Transaction expired */ boost::signals2::signal NotifyTxExpiration; }; From 8f1d9c1e0327fc713f6414788a985752910d6534 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 09:22:32 +0000 Subject: [PATCH 082/146] Fix several node initialization issues --- src/init.cpp | 45 ++++++++++++++++++++++++++++----------- src/main.cpp | 7 ++++-- src/test/test_bitcoin.cpp | 5 +++++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 29bd7a9eb..e2c1fd0f7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -532,6 +532,21 @@ static void TxExpiryNotifyCallback(const uint256& txid) boost::thread t(runCommand, strCmd); // thread runs free } +static bool fHaveGenesis = false; +static boost::mutex cs_GenesisWait; +static CConditionVariable condvar_GenesisWait; + +static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) +{ + if (pBlockIndex != NULL) { + { + boost::unique_lock lock_GenesisWait(cs_GenesisWait); + fHaveGenesis = true; + } + condvar_GenesisWait.notify_all(); + } +} + struct CImportingNow { CImportingNow() { @@ -1552,7 +1567,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) break; } - if (!fReindex) { + if (!fReindex && chainActive.Tip() != NULL) { uiInterface.InitMessage(_("Rewinding blocks if needed...")); if (!RewindBlockIndex(chainparams, clearWitnessCaches)) { strLoadError = _("Unable to rewind the database to a pre-upgrade state. You will need to redownload the blockchain"); @@ -1703,6 +1718,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 10: import blocks + if (!CheckDiskSpace()) + return false; + + // Either install a handler to notify us when genesis activates, or set fHaveGenesis directly. + // No locking, as this happens before any background thread is started. + if (chainActive.Tip() == NULL) { + uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait); + } else { + fHaveGenesis = true; + } + if (mapArgs.count("-blocknotify")) uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); @@ -1724,16 +1750,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); // Wait for genesis block to be processed - bool fHaveGenesis = false; - while (!fHaveGenesis && !fRequestShutdown) { - { - LOCK(cs_main); - fHaveGenesis = (chainActive.Tip() != NULL); - } - - if (!fHaveGenesis) { - MilliSleep(10); + { + boost::unique_lock lock(cs_GenesisWait); + while (!fHaveGenesis) { + condvar_GenesisWait.wait(lock); } + uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); } if (!fHaveGenesis) { return false; @@ -1741,9 +1763,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 11: start node - if (!CheckDiskSpace()) - return false; - if (!strErrors.str().empty()) return InitError(strErrors.str()); diff --git a/src/main.cpp b/src/main.cpp index 451c572eb..423d1773a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4785,8 +4785,11 @@ bool InitBlockIndex(const CChainParams& chainparams) CBlockIndex *pindex = AddToBlockIndex(block); if (!ReceivedBlockTransactions(block, state, chainparams, pindex, blockPos)) return error("LoadBlockIndex(): genesis block not accepted"); - if (!ActivateBestChain(state, chainparams, &block)) - return error("LoadBlockIndex(): genesis block cannot be activated"); + // Before the genesis block, there was an empty tree. We set its root here so + // that the block import thread doesn't race other methods that need to query + // the Sprout tree (namely CWallet::ScanForWalletTransactions). + SproutMerkleTree tree; + pindex->hashSproutAnchor = tree.root(); // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); } catch (const std::runtime_error& e) { diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index fd6b30fc3..e8718394f 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -106,6 +106,11 @@ TestingSetup::TestingSetup(const std::string& chainName) : JoinSplitTestingSetup pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(pcoinsdbview); InitBlockIndex(chainparams); + { + CValidationState state; + bool ok = ActivateBestChain(state, chainparams); + BOOST_CHECK(ok); + } nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); From 3214a90107460ad554bc5d4f9e218e03eed1855a Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 09:29:52 +0000 Subject: [PATCH 083/146] Always enforce strict lock ordering (try or not) --- src/net.cpp | 20 +++++++------------- src/sync.cpp | 50 ++++++++++++-------------------------------------- 2 files changed, 19 insertions(+), 51 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index cf1b083b5..551fcda7f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1052,24 +1052,18 @@ void ThreadSocketHandler() for (CNode* pnode : vNodesDisconnectedCopy) { // wait until threads are done using it - if (pnode->GetRefCount() <= 0) - { + if (pnode->GetRefCount() <= 0) { bool fDelete = false; { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { - TRY_LOCK(pnode->cs_inventory, lockInv); - if (lockInv) - fDelete = true; + TRY_LOCK(pnode->cs_inventory, lockInv); + if (lockInv) { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) { + fDelete = true; } } } - if (fDelete) - { + if (fDelete) { vNodesDisconnected.remove(pnode); delete pnode; } diff --git a/src/sync.cpp b/src/sync.cpp index 44f718e91..1dc62a26a 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -80,52 +80,28 @@ boost::thread_specific_ptr lockstack; static void potential_deadlock_detected(const std::pair& mismatch, const LockStack& s1, const LockStack& s2) { - // We attempt to not assert on probably-not deadlocks by assuming that - // a try lock will immediately have otherwise bailed if it had - // failed to get the lock - // We do this by, for the locks which triggered the potential deadlock, - // in either lockorder, checking that the second of the two which is locked - // is only a TRY_LOCK, ignoring locks if they are reentrant. - bool firstLocked = false; - bool secondLocked = false; - bool onlyMaybeDeadlock = false; - LogPrintf("POTENTIAL DEADLOCK DETECTED\n"); LogPrintf("Previous lock order was:\n"); for (const std::pair & i : s2) { if (i.first == mismatch.first) { LogPrintf(" (1)"); - if (!firstLocked && secondLocked && i.second.fTry) - onlyMaybeDeadlock = true; - firstLocked = true; } if (i.first == mismatch.second) { LogPrintf(" (2)"); - if (!secondLocked && firstLocked && i.second.fTry) - onlyMaybeDeadlock = true; - secondLocked = true; } LogPrintf(" %s\n", i.second.ToString()); } - firstLocked = false; - secondLocked = false; LogPrintf("Current lock order is:\n"); for (const std::pair & i : s1) { if (i.first == mismatch.first) { LogPrintf(" (1)"); - if (!firstLocked && secondLocked && i.second.fTry) - onlyMaybeDeadlock = true; - firstLocked = true; } if (i.first == mismatch.second) { LogPrintf(" (2)"); - if (!secondLocked && firstLocked && i.second.fTry) - onlyMaybeDeadlock = true; - secondLocked = true; } LogPrintf(" %s\n", i.second.ToString()); } - assert(onlyMaybeDeadlock); + assert(false); } static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) @@ -138,21 +114,19 @@ static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) (*lockstack).push_back(std::make_pair(c, locklocation)); - if (!fTry) { - for (const std::pair & i : (*lockstack)) { - if (i.first == c) - break; + for (const std::pair & i : (*lockstack)) { + if (i.first == c) + break; - std::pair p1 = std::make_pair(i.first, c); - if (lockdata.lockorders.count(p1)) - continue; - lockdata.lockorders[p1] = (*lockstack); + std::pair p1 = std::make_pair(i.first, c); + if (lockdata.lockorders.count(p1)) + continue; + lockdata.lockorders[p1] = (*lockstack); - std::pair p2 = std::make_pair(c, i.first); - lockdata.invlockorders.insert(p2); - if (lockdata.lockorders.count(p2)) - potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]); - } + std::pair p2 = std::make_pair(c, i.first); + lockdata.invlockorders.insert(p2); + if (lockdata.lockorders.count(p2)) + potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]); } } From c43bfd6a9abce4fb4318ee020666771726a3c46a Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 09:38:05 +0000 Subject: [PATCH 084/146] Remove unused code --- src/bitcoin-tx.cpp | 16 ---------------- src/dbwrapper.h | 4 ---- src/httpserver.cpp | 7 ------- src/sync.cpp | 2 -- src/sync.h | 1 - src/test/key_tests.cpp | 28 ---------------------------- src/test/script_tests.cpp | 5 ----- 7 files changed, 63 deletions(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 163c51fab..33c60697c 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -331,22 +331,6 @@ static bool findSighashFlags(int& flags, const std::string& flagStr) return false; } -uint256 ParseHashUO(std::map& o, std::string strKey) -{ - if (!o.count(strKey)) - return uint256(); - return ParseHashUV(o[strKey], strKey); -} - -std::vector ParseHexUO(std::map& o, std::string strKey) -{ - if (!o.count(strKey)) { - std::vector emptyVec; - return emptyVec; - } - return ParseHexUV(o[strKey], strKey); -} - static CAmount AmountFromValue(const UniValue& value) { if (!value.isNum() && !value.isStr()) diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 8c77fd224..c6bc1ab7b 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -119,10 +119,6 @@ class CDBIterator return true; } - unsigned int GetKeySize() { - return piter->key().size(); - } - template bool GetValue(V& value) { leveldb::Slice slValue = piter->value(); try { diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 3e21bce8e..44e68bc13 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -147,13 +147,6 @@ class WorkQueue while (numThreads > 0) cond.wait(lock); } - - /** Return current depth of queue */ - size_t Depth() - { - std::unique_lock lock(cs); - return queue.size(); - } }; struct HTTPPathHandler diff --git a/src/sync.cpp b/src/sync.cpp index 1dc62a26a..3e033e429 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -45,8 +45,6 @@ struct CLockLocation { return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : ""); } - std::string MutexName() const { return mutexName; } - bool fTry; private: std::string mutexName; diff --git a/src/sync.h b/src/sync.h index 75021ee4b..b78b40db8 100644 --- a/src/sync.h +++ b/src/sync.h @@ -97,7 +97,6 @@ class CCriticalSection : public AnnotatedMixin } }; -typedef CCriticalSection CDynamicCriticalSection; /** Wrapped boost mutex: supports waiting but not recursive locking */ typedef AnnotatedMixin CWaitableCriticalSection; diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index b16f2d05f..a49a47681 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -36,34 +36,6 @@ static const std::string addr2C = "t1VJL2dPUyXK7avDRGqhqQA5bw2eEMdhyg6"; static const std::string strAddressBad = "t1aMkLwU1LcMZYN7TgXUJAwzA1r44dbLkSp"; -#ifdef KEY_TESTS_DUMPINFO -void dumpKeyInfo(uint256 privkey) -{ - CKey key; - key.resize(32); - memcpy(&secret[0], &privkey, 32); - vector sec; - sec.resize(32); - memcpy(&sec[0], &secret[0], 32); - printf(" * secret (hex): %s\n", HexStr(sec).c_str()); - - for (int nCompressed=0; nCompressed<2; nCompressed++) - { - bool fCompressed = nCompressed == 1; - printf(" * %s:\n", fCompressed ? "compressed" : "uncompressed"); - CBitcoinSecret bsecret; - bsecret.SetSecret(secret, fCompressed); - printf(" * secret (base58): %s\n", bsecret.ToString().c_str()); - CKey key; - key.SetSecret(secret, fCompressed); - vector vchPubKey = key.GetPubKey(); - printf(" * pubkey (hex): %s\n", HexStr(vchPubKey).c_str()); - printf(" * address (base58): %s\n", EncodeDestination(vchPubKey).c_str()); - } -} -#endif - - BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(key_test1) diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 4a9145af9..2e99436c5 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -302,11 +302,6 @@ class TestBuilder { return comment; } - - const CScript& GetScriptPubKey() - { - return creditTx.vout[0].scriptPubKey; - } }; } From 1044a80081b7726d903403563577b6b60d4c700f Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 09:59:00 +0000 Subject: [PATCH 085/146] net: fix socket close race --- src/net.cpp | 64 ++++++++++++++++++++++++++++++++--------------------- src/net.h | 3 +++ 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 551fcda7f..64c71c306 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -427,6 +427,7 @@ static void DumpBanlist() void CNode::CloseSocketDisconnect() { fDisconnect = true; + LOCK(cs_hSocket); if (hSocket != INVALID_SOCKET) { LogPrint("net", "disconnecting peer=%d\n", id); @@ -758,7 +759,13 @@ void SocketSendData(CNode *pnode) while (it != pnode->vSendMsg.end()) { const CSerializeData &data = *it; assert(data.size() > pnode->nSendOffset); - int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + int nBytes = 0; + { + LOCK(pnode->cs_hSocket); + if (pnode->hSocket == INVALID_SOCKET) + break; + nBytes = send(pnode->hSocket, reinterpret_cast(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + } if (nBytes > 0) { pnode->nLastSend = GetTime(); pnode->nSendBytes += nBytes; @@ -1106,12 +1113,6 @@ void ThreadSocketHandler() LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if (pnode->hSocket == INVALID_SOCKET) - continue; - FD_SET(pnode->hSocket, &fdsetError); - hSocketMax = max(hSocketMax, pnode->hSocket); - have_fds = true; - // Implement the following logic: // * If there is data to send, select() for sending data. As this only // happens when optimistic write failed, we choose to first drain the @@ -1127,19 +1128,30 @@ void ThreadSocketHandler() // * We send some data. // * We wait for data to be received (and disconnect after timeout). // * We process a message in the buffer (message handler thread). + + bool select_send; { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend && !pnode->vSendMsg.empty()) { - FD_SET(pnode->hSocket, &fdsetSend); - continue; - } + LOCK(pnode->cs_vSend); + select_send = !pnode->vSendMsg.empty(); } + + FD_SET(pnode->hSocket, &fdsetError); + hSocketMax = max(hSocketMax, pnode->hSocket); + have_fds = true; + + if (select_send) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; + } + bool select_recv; { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv && ( + select_recv = lockRecv && ( pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || - pnode->GetTotalRecvSize() <= ReceiveFloodSize())) - FD_SET(pnode->hSocket, &fdsetRecv); + pnode->GetTotalRecvSize() <= ReceiveFloodSize()); + } + if (select_recv) { + FD_SET(pnode->hSocket, &fdsetRecv); } } } @@ -1190,9 +1202,15 @@ void ThreadSocketHandler() // // Receive // + bool recvSet = false; + bool sendSet = false; + bool errorSet = false; if (pnode->hSocket == INVALID_SOCKET) continue; - if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) + recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); + sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); + errorSet = FD_ISSET(pnode->hSocket, &fdsetError); + if (recvSet || errorSet) { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) @@ -1234,13 +1252,10 @@ void ThreadSocketHandler() // // Send // - if (pnode->hSocket == INVALID_SOCKET) - continue; - if (FD_ISSET(pnode->hSocket, &fdsetSend)) + if (sendSet) { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - SocketSendData(pnode); + LOCK(pnode->cs_vSend); + SocketSendData(pnode); } // @@ -1619,9 +1634,8 @@ void ThreadMessageHandler() // Send messages { - TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - g_signals.SendMessages(pnode, pnode == pnodeTrickle || pnode->fWhitelisted); + LOCK(pnode->cs_sendProcessing); + g_signals.SendMessages(pnode, pnode == pnodeTrickle || pnode->fWhitelisted); } boost::this_thread::interruption_point(); } diff --git a/src/net.h b/src/net.h index d5f6fe7ca..8e9c20c0c 100644 --- a/src/net.h +++ b/src/net.h @@ -258,6 +258,9 @@ class CNode uint64_t nSendBytes; std::deque vSendMsg; CCriticalSection cs_vSend; + CCriticalSection cs_hSocket; + + CCriticalSection cs_sendProcessing; std::deque vRecvGetData; std::deque vRecvMsg; From f56efd74c3f6f6038662348fbbb387aeea20f0d9 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 10:28:21 +0000 Subject: [PATCH 086/146] Replace global trickle node with random delays --- src/main.cpp | 34 ++++++++++++++-------------------- src/main.h | 11 +++++++++-- src/net.cpp | 16 ++++++++++------ src/net.h | 7 ++++++- src/test/DoS_tests.cpp | 14 +++++++------- 5 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 423d1773a..a111e5d23 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6377,7 +6377,7 @@ bool ProcessMessages(CNode* pfrom) } -bool SendMessages(CNode* pto, bool fSendTrickle) +bool SendMessages(CNode* pto) { const CChainParams& chainParams = Params(); const Consensus::Params& consensusParams = chainParams.GetConsensus(); @@ -6420,28 +6420,17 @@ bool SendMessages(CNode* pto, bool fSendTrickle) return true; // Address refresh broadcast - static int64_t nLastRebroadcast; - if (!IsInitialBlockDownload(chainParams) && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) - { - LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - { - // Periodically clear addrKnown to allow refresh broadcasts - if (nLastRebroadcast) - pnode->addrKnown.reset(); - - // Rebroadcast our address - AdvertizeLocal(pnode); - } - if (!vNodes.empty()) - nLastRebroadcast = GetTime(); + int64_t nNow = GetTimeMicros(); + if (!IsInitialBlockDownload(chainParams) && pto->nNextLocalAddrSend < nNow) { + AdvertizeLocal(pto); + pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL); } // // Message: addr // - if (fSendTrickle) - { + if (pto->nNextAddrSend < nNow) { + pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL); vector vAddr; vAddr.reserve(pto->vAddrToSend.size()); for (const CAddress& addr : pto->vAddrToSend) @@ -6521,8 +6510,13 @@ bool SendMessages(CNode* pto, bool fSendTrickle) vector vInv; vector vInvWait; { + bool fSendTrickle = pto->fWhitelisted; + if (pto->nNextInvSend < nNow) { + fSendTrickle = true; + pto->nNextInvSend = PoissonNextSend(nNow, AVG_INVENTORY_BROADCAST_INTERVAL); + } LOCK(pto->cs_inventory); - vInv.reserve(pto->vInventoryToSend.size()); + vInv.reserve(std::min(1000, pto->vInventoryToSend.size())); vInvWait.reserve(pto->vInventoryToSend.size()); for (const CInv& inv : pto->vInventoryToSend) { @@ -6562,7 +6556,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("inv", vInv); // Detect whether we're stalling - int64_t nNow = GetTimeMicros(); + nNow = GetTimeMicros(); if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { // Stalling only triggers when the block download window cannot move. During normal steady state, // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection diff --git a/src/main.h b/src/main.h index c84964b0a..ed4483a4d 100644 --- a/src/main.h +++ b/src/main.h @@ -111,6 +111,14 @@ static const unsigned int WITNESS_WRITE_INTERVAL = 10 * 60; static const unsigned int WITNESS_WRITE_UPDATES = 10000; /** Maximum length of reject messages. */ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; +/** Average delay between local address broadcasts in seconds. */ +static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 24 * 60; +/** Average delay between peer address broadcasts in seconds. */ +static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30; +/** Average delay between trickled inventory broadcasts in seconds. + * Blocks, whitelisted receivers, and a random 25% of transactions bypass this. */ +static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5; + static const unsigned int DEFAULT_LIMITFREERELAY = 15; static const bool DEFAULT_RELAYPRIORITY = false; static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; @@ -249,9 +257,8 @@ bool ProcessMessages(CNode* pfrom); * Send queued protocol messages to be sent to a give node. * * @param[in] pto The node which we are sending messages to. - * @param[in] fSendTrickle When true send the trickled data, otherwise trickle the data until true. */ -bool SendMessages(CNode* pto, bool fSendTrickle); +bool SendMessages(CNode* pto); /** Run an instance of the script checking thread */ void ThreadScriptCheck(); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ diff --git a/src/net.cpp b/src/net.cpp index 64c71c306..bc36b6e4d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -32,6 +32,8 @@ #include +#include + // Dump addresses to peers.dat and banlist.dat every 15 minutes (900s) #define DUMP_ADDRESSES_INTERVAL 900 @@ -1601,11 +1603,6 @@ void ThreadMessageHandler() } } - // Poll the connected nodes for messages - CNode* pnodeTrickle = NULL; - if (!vNodesCopy.empty()) - pnodeTrickle = vNodesCopy[GetRand(vNodesCopy.size())]; - bool fSleep = true; for (CNode* pnode : vNodesCopy) @@ -1635,7 +1632,7 @@ void ThreadMessageHandler() // Send messages { LOCK(pnode->cs_sendProcessing); - g_signals.SendMessages(pnode, pnode == pnodeTrickle || pnode->fWhitelisted); + g_signals.SendMessages(pnode); } boost::this_thread::interruption_point(); } @@ -2159,6 +2156,9 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nStartingHeight = -1; filterInventoryKnown.reset(); fGetAddr = false; + nNextLocalAddrSend = 0; + nNextAddrSend = 0; + nNextInvSend = 0; fRelayTxes = false; fSentAddr = false; pfilter = new CBloomFilter(); @@ -2298,3 +2298,7 @@ uint64_t CNode::CalculateKeyedNetGroup(const CAddress& ad) return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize(); } + +int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { + return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); +} diff --git a/src/net.h b/src/net.h index 8e9c20c0c..4e8fd1d44 100644 --- a/src/net.h +++ b/src/net.h @@ -111,7 +111,7 @@ struct CNodeSignals { boost::signals2::signal GetHeight; boost::signals2::signal ProcessMessages; - boost::signals2::signal SendMessages; + boost::signals2::signal SendMessages; boost::signals2::signal InitializeNode; boost::signals2::signal FinalizeNode; }; @@ -327,6 +327,8 @@ class CNode CRollingBloomFilter addrKnown; bool fGetAddr; std::set setKnown; + int64_t nNextAddrSend; + int64_t nNextLocalAddrSend; // inventory based relay CRollingBloomFilter filterInventoryKnown; @@ -334,6 +336,7 @@ class CNode CCriticalSection cs_inventory; std::set setAskFor; std::multimap mapAskFor; + int64_t nNextInvSend; // Ping time measurement: // The pong reply we're expecting, or 0 if no pong expected. @@ -696,5 +699,7 @@ class CTransaction; void RelayTransaction(const CTransaction& tx); void RelayTransaction(const CTransaction& tx, const CDataStream& ss); +/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */ +int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds); #endif // BITCOIN_NET_H diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index c3a05866e..53fb20aaf 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning) CNode dummyNode1(INVALID_SOCKET, addr1, "", true); dummyNode1.nVersion = 1; Misbehaving(dummyNode1.GetId(), 100); // Should get banned - SendMessages(&dummyNode1, false); + SendMessages(&dummyNode1); BOOST_CHECK(CNode::IsBanned(addr1)); BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned @@ -61,11 +61,11 @@ BOOST_AUTO_TEST_CASE(DoS_banning) CNode dummyNode2(INVALID_SOCKET, addr2, "", true); dummyNode2.nVersion = 1; Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2, false); + SendMessages(&dummyNode2); BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2, false); + SendMessages(&dummyNode2); BOOST_CHECK(CNode::IsBanned(addr2)); } @@ -77,13 +77,13 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) CNode dummyNode1(INVALID_SOCKET, addr1, "", true); dummyNode1.nVersion = 1; Misbehaving(dummyNode1.GetId(), 100); - SendMessages(&dummyNode1, false); + SendMessages(&dummyNode1); BOOST_CHECK(!CNode::IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 10); - SendMessages(&dummyNode1, false); + SendMessages(&dummyNode1); BOOST_CHECK(!CNode::IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 1); - SendMessages(&dummyNode1, false); + SendMessages(&dummyNode1); BOOST_CHECK(CNode::IsBanned(addr1)); mapArgs.erase("-banscore"); } @@ -99,7 +99,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) dummyNode.nVersion = 1; Misbehaving(dummyNode.GetId(), 100); - SendMessages(&dummyNode, false); + SendMessages(&dummyNode); BOOST_CHECK(CNode::IsBanned(addr)); SetMockTime(nStartTime+60*60); From 2bb47c6742cff96ef40a227f116d883adb1d10d0 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 11:59:46 +0000 Subject: [PATCH 087/146] Bitcoin 0.14 locking PRs --- src/httpserver.cpp | 2 +- src/main.cpp | 67 ++++++++++++------- src/net.cpp | 138 +++++++++++++++++++++++++++++----------- src/net.h | 41 ++++++++---- src/rpc/net.cpp | 5 +- src/sync.h | 5 +- src/util.cpp | 60 ++++++++++++++--- src/utiltime.cpp | 3 +- src/wallet/db.cpp | 3 - src/wallet/db.h | 2 - src/wallet/wallet.cpp | 2 +- src/wallet/walletdb.cpp | 91 ++++++++++++++------------ src/wallet/walletdb.h | 2 + 13 files changed, 285 insertions(+), 136 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 44e68bc13..f1ef39223 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -119,7 +119,7 @@ class WorkQueue void Run() { ThreadCounter count(*this); - while (running) { + while (true) { std::unique_ptr i; { std::unique_lock lock(cs); diff --git a/src/main.cpp b/src/main.cpp index a111e5d23..bf2cba670 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,7 +62,7 @@ CCriticalSection cs_main; BlockMap mapBlockIndex; CChain chainActive; CBlockIndex *pindexBestHeader = NULL; -static int64_t nTimeBestReceived = 0; +static std::atomic nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; @@ -337,7 +337,7 @@ int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consens void InitializeNode(NodeId nodeid, const CNode *pnode) { LOCK(cs_main); CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; - state.name = pnode->addrName; + state.name = pnode->GetAddrName(); state.address = pnode->addr; } @@ -5210,10 +5210,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam pfrom->PushMessage("block", block); else // MSG_FILTERED_BLOCK) { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) + bool send = false; + CMerkleBlock merkleBlock; { - CMerkleBlock merkleBlock(block, *pfrom->pfilter); + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) { + send = true; + merkleBlock = CMerkleBlock(block, *pfrom->pfilter); + } + } + if (send) { pfrom->PushMessage("merkleblock", merkleBlock); // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see // This avoids hurting performance by pointlessly requiring a round-trip @@ -5329,7 +5335,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; - vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + std::string strSubVer; + std::string cleanSubVer; + uint64_t nServices; + vRecv >> pfrom->nVersion >> nServices >> nTime >> addrMe; + pfrom->nServices = nServices; if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version @@ -5358,11 +5368,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { - vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH); - pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); + vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH); + cleanSubVer = SanitizeString(strSubVer); + } + if (!vRecv.empty()) { + int nStartingHeight; + vRecv >> nStartingHeight; + pfrom->nStartingHeight = nStartingHeight; } - if (!vRecv.empty()) - vRecv >> pfrom->nStartingHeight; if (!vRecv.empty()) vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message else @@ -5376,7 +5389,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - pfrom->addrLocal = addrMe; + pfrom->SetAddrLocal(addrMe); if (pfrom->fInbound && addrMe.IsRoutable()) { SeenLocal(addrMe); @@ -5386,6 +5399,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->fInbound) pfrom->PushVersion(); + { + LOCK(pfrom->cs_SubVer); + pfrom->strSubVer = strSubVer; + pfrom->cleanSubVer = cleanSubVer; + } pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); // Potentially mark this peer as a preferred download peer. @@ -5410,7 +5428,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); pfrom->PushAddress(addr, insecure_rand); } else if (IsPeerAddrLocalGood(pfrom)) { - addr.SetIP(pfrom->addrLocal); + addr.SetIP(addrMe); LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); pfrom->PushAddress(addr, insecure_rand); } @@ -5445,7 +5463,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n", - pfrom->cleanSubVer, pfrom->nVersion, + cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, remoteAddr); @@ -6073,7 +6091,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pingUsecTime > 0) { // Successful ping time measurement, replace previous pfrom->nPingUsecTime = pingUsecTime; - pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime); + pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime.load(), pingUsecTime); } else { // This should never happen sProblem = "Timing mishap"; @@ -6175,8 +6193,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); pfrom->pfilter->UpdateEmptyFull(); + pfrom->fRelayTxes = true; } - pfrom->fRelayTxes = true; } @@ -6187,20 +6205,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, // and thus, the maximum size any matched object can have) in a filteradd message - if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) - { - LOCK(cs_main); - Misbehaving(pfrom->GetId(), 100); + bool bad = false; + if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { + bad = true; } else { LOCK(pfrom->cs_filter); - if (pfrom->pfilter) + if (pfrom->pfilter) { pfrom->pfilter->insert(vData); - else - { - LOCK(cs_main); - Misbehaving(pfrom->GetId(), 100); + } else { + bad = true; } } + if (bad) { + LOCK(cs_main); + Misbehaving(pfrom->GetId(), 100); + } } diff --git a/src/net.cpp b/src/net.cpp index bc36b6e4d..f8677465e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -178,8 +178,9 @@ int GetnScore(const CService& addr) // Is our peer's addrLocal potentially useful as an external IP source? bool IsPeerAddrLocalGood(CNode *pnode) { - return fDiscover && pnode->addr.IsRoutable() && pnode->addrLocal.IsRoutable() && - !IsLimited(pnode->addrLocal.GetNetwork()); + CService addrLocal = pnode->GetAddrLocal(); + return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() && + !IsLimited(addrLocal.GetNetwork()); } // pushes our own address to a peer @@ -194,7 +195,7 @@ void AdvertizeLocal(CNode *pnode) if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() || GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0)) { - addrLocal.SetIP(pnode->addrLocal); + addrLocal.SetIP(pnode->GetAddrLocal()); } if (addrLocal.IsRoutable()) { @@ -336,9 +337,11 @@ CNode* FindNode(const CSubNet& subNet) CNode* FindNode(const std::string& addrName) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if (pnode->addrName == addrName) + for (CNode* pnode : vNodes) { + if (pnode->GetAddrName() == addrName) { return (pnode); + } + } return NULL; } @@ -358,6 +361,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) return NULL; // Look for an existing connection + LOCK(cs_vNodes); CNode* pnode = FindNode((CService)addrConnect); if (pnode) { @@ -394,8 +398,6 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) vNodes.push_back(pnode); } - pnode->nTimeConnected = GetTime(); - return pnode; } else if (!proxyConnectionFailed) { // If connecting to the node failed, and failure is not caused by a problem connecting to @@ -429,11 +431,12 @@ static void DumpBanlist() void CNode::CloseSocketDisconnect() { fDisconnect = true; - LOCK(cs_hSocket); - if (hSocket != INVALID_SOCKET) { - LogPrint("net", "disconnecting peer=%d\n", id); - CloseSocket(hSocket); + LOCK(cs_hSocket); + if (hSocket != INVALID_SOCKET) { + LogPrint("net", "disconnecting peer=%d\n", id); + CloseSocket(hSocket); + } } // in case this fails, we'll empty the recv buffer when the CNode is deleted @@ -622,22 +625,60 @@ void CNode::AddWhitelistedRange(const CSubNet &subnet) { vWhitelistedRange.push_back(subnet); } +std::string CNode::GetAddrName() const { + LOCK(cs_addrName); + return addrName; +} + +void CNode::MaybeSetAddrName(const std::string& addrNameIn) { + LOCK(cs_addrName); + if (addrName.empty()) { + addrName = addrNameIn; + } +} + +CService CNode::GetAddrLocal() const { + LOCK(cs_addrLocal); + return addrLocal; +} + +void CNode::SetAddrLocal(const CService& addrLocalIn) { + LOCK(cs_addrLocal); + if (addrLocal.IsValid()) { + error("Addr local already set for node: %i. Refusing to change from %s to %s", id, addrLocal.ToString(), addrLocalIn.ToString()); + } else { + addrLocal = addrLocalIn; + } +} + void CNode::copyStats(CNodeStats &stats) { stats.nodeid = this->GetId(); stats.nServices = nServices; - stats.fRelayTxes = fRelayTxes; + { + LOCK(cs_filter); + stats.fRelayTxes = fRelayTxes; + } stats.nLastSend = nLastSend; stats.nLastRecv = nLastRecv; stats.nTimeConnected = nTimeConnected; stats.nTimeOffset = nTimeOffset; - stats.addrName = addrName; + stats.addrName = GetAddrName(); stats.nVersion = nVersion; - stats.cleanSubVer = cleanSubVer; + { + LOCK(cs_SubVer); + stats.cleanSubVer = cleanSubVer; + } stats.fInbound = fInbound; stats.nStartingHeight = nStartingHeight; - stats.nSendBytes = nSendBytes; - stats.nRecvBytes = nRecvBytes; + { + LOCK(cs_vSend); + stats.nSendBytes = nSendBytes; + } + { + LOCK(cs_vRecv); + stats.nRecvBytes = nRecvBytes; + } stats.fWhitelisted = fWhitelisted; // It is common for nodes with good ping times to suddenly become lagged, @@ -656,7 +697,8 @@ void CNode::copyStats(CNodeStats &stats) stats.dPingWait = (((double)nPingUsecWait) / 1e6); // Leave string empty if addrLocal invalid (not filled in yet) - stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : ""; + CService addrLocalUnlocked = GetAddrLocal(); + stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : ""; } // requires LOCK(cs_vRecvMsg) @@ -766,11 +808,14 @@ void SocketSendData(CNode *pnode) LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) break; - nBytes = send(pnode->hSocket, reinterpret_cast(data.data()) + pnode->nSendOffset, data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); } if (nBytes > 0) { pnode->nLastSend = GetTime(); - pnode->nSendBytes += nBytes; + { + LOCK(pnode->cs_vSend); + pnode->nSendBytes += nBytes; + } pnode->nSendOffset += nBytes; pnode->RecordBytesSent(nBytes); if (pnode->nSendOffset == data.size()) { @@ -1133,10 +1178,22 @@ void ThreadSocketHandler() bool select_send; { - LOCK(pnode->cs_vSend); - select_send = !pnode->vSendMsg.empty(); + TRY_LOCK(pnode->cs_vSend, lockSend); + select_send = lockSend && !pnode->vSendMsg.empty(); } + bool select_recv; + { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + select_recv = lockRecv && ( + pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || + pnode->GetTotalRecvSize() <= ReceiveFloodSize()); + } + + LOCK(pnode->cs_hSocket); + if (pnode->hSocket == INVALID_SOCKET) + continue; + FD_SET(pnode->hSocket, &fdsetError); hSocketMax = max(hSocketMax, pnode->hSocket); have_fds = true; @@ -1145,13 +1202,6 @@ void ThreadSocketHandler() FD_SET(pnode->hSocket, &fdsetSend); continue; } - bool select_recv; - { - TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - select_recv = lockRecv && ( - pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || - pnode->GetTotalRecvSize() <= ReceiveFloodSize()); - } if (select_recv) { FD_SET(pnode->hSocket, &fdsetRecv); } @@ -1207,11 +1257,14 @@ void ThreadSocketHandler() bool recvSet = false; bool sendSet = false; bool errorSet = false; - if (pnode->hSocket == INVALID_SOCKET) - continue; - recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); - sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); - errorSet = FD_ISSET(pnode->hSocket, &fdsetError); + { + LOCK(pnode->cs_hSocket); + if (pnode->hSocket == INVALID_SOCKET) + continue; + recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); + sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); + errorSet = FD_ISSET(pnode->hSocket, &fdsetError); + } if (recvSet || errorSet) { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); @@ -1220,13 +1273,22 @@ void ThreadSocketHandler() { // typical socket buffer is 8K-64K char pchBuf[0x10000]; - int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + int nBytes = 0; + { + LOCK(pnode->cs_hSocket); + if (pnode->hSocket == INVALID_SOCKET) + continue; + nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + } if (nBytes > 0) { if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) pnode->CloseSocketDisconnect(); pnode->nLastRecv = GetTime(); - pnode->nRecvBytes += nBytes; + { + LOCK(pnode->cs_vRecv); + pnode->nRecvBytes += nBytes; + } pnode->RecordBytesRecv(nBytes); } else if (nBytes == 0) @@ -1901,9 +1963,11 @@ static class CNetCleanup ~CNetCleanup() { // Close sockets - for (CNode* pnode : vNodes) + for (CNode* pnode : vNodes) { + LOCK(pnode->cs_hSocket); if (pnode->hSocket != INVALID_SOCKET) CloseSocket(pnode->hSocket); + } for (ListenSocket& hListenSocket : vhListenSocket) if (hListenSocket.socket != INVALID_SOCKET) if (!CloseSocket(hListenSocket.socket)) @@ -2125,6 +2189,7 @@ unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAX CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), + nTimeConnected(GetTime()), addr(addrIn), nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)), addrKnown(5000, 0.001), @@ -2137,7 +2202,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nLastRecv = 0; nSendBytes = 0; nRecvBytes = 0; - nTimeConnected = GetTime(); nTimeOffset = 0; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; nVersion = 0; diff --git a/src/net.h b/src/net.h index 4e8fd1d44..cd226ccfe 100644 --- a/src/net.h +++ b/src/net.h @@ -250,7 +250,7 @@ class CNode { public: // socket - uint64_t nServices; + std::atomic nServices; SOCKET hSocket; CDataStream ssSend; size_t nSendSize; // total size of all vSendMsg entries @@ -259,6 +259,7 @@ class CNode std::deque vSendMsg; CCriticalSection cs_vSend; CCriticalSection cs_hSocket; + CCriticalSection cs_vRecv; CCriticalSection cs_sendProcessing; @@ -268,19 +269,18 @@ class CNode uint64_t nRecvBytes; int nRecvVersion; - int64_t nLastSend; - int64_t nLastRecv; - int64_t nTimeConnected; - int64_t nTimeOffset; + std::atomic nLastSend; + std::atomic nLastRecv; + const int64_t nTimeConnected; + std::atomic nTimeOffset; const CAddress addr; - std::string addrName; - CService addrLocal; int nVersion; // strSubVer is whatever byte array we read from the wire. However, this field is intended // to be printed out, displayed to humans in various forms and so on. So we sanitize it and // store the sanitized version in cleanSubVer. The original should be used when dealing with // the network or wire types and the cleaned string used when displayed or logged. std::string strSubVer, cleanSubVer; + CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer bool fWhitelisted; // This peer can bypass DoS banning. bool fOneShot; bool fClient; @@ -297,8 +297,8 @@ class CNode CSemaphoreGrant grantOutbound; CCriticalSection cs_filter; CBloomFilter* pfilter; - int nRefCount; NodeId id; + std::atomic nRefCount; const uint64_t nKeyedNetGroup; @@ -320,7 +320,7 @@ class CNode public: uint256 hashContinue; - int nStartingHeight; + std::atomic nStartingHeight; // flood relay std::vector vAddrToSend; @@ -340,15 +340,15 @@ class CNode // Ping time measurement: // The pong reply we're expecting, or 0 if no pong expected. - uint64_t nPingNonceSent; + std::atomic nPingNonceSent; // Time (in usec) the last ping was sent, or 0 if no ping was ever sent. - int64_t nPingUsecStart; + std::atomic nPingUsecStart; // Last measured round-trip time. - int64_t nPingUsecTime; + std::atomic nPingUsecTime; // Best measured round-trip time. - int64_t nMinPingUsecTime; + std::atomic nMinPingUsecTime; // Whether a ping is requested. - bool fPingQueued; + std::atomic fPingQueued; CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); @@ -371,6 +371,11 @@ class CNode static uint64_t CalculateKeyedNetGroup(const CAddress& ad); + mutable CCriticalSection cs_addrName; + std::string addrName; + + CService addrLocal; + mutable CCriticalSection cs_addrLocal; public: NodeId GetId() const { @@ -403,6 +408,10 @@ class CNode msg.SetVersion(nVersionIn); } + CService GetAddrLocal() const; + //! May not be called more than once + void SetAddrLocal(const CService& addrLocalIn); + CNode* AddRef() { nRefCount++; @@ -691,6 +700,10 @@ class CNode //!response the time in seconds left in the current max outbound cycle // in case of no limit, it will always respond with 0 static uint64_t GetMaxOutboundTimeLeftInCycle(); + + std::string GetAddrName() const; + //! Sets the addrName only if it was not previously set + void MaybeSetAddrName(const std::string& addrNameIn); }; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index d8631bed4..1c38d4b96 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -67,9 +67,8 @@ static void CopyNodeStats(std::vector& vstats) LOCK(cs_vNodes); vstats.reserve(vNodes.size()); for (CNode* pnode : vNodes) { - CNodeStats stats; - pnode->copyStats(stats); - vstats.push_back(stats); + vstats.emplace_back(); + pnode->copyStats(vstats.back()); } } diff --git a/src/sync.h b/src/sync.h index b78b40db8..29992c83e 100644 --- a/src/sync.h +++ b/src/sync.h @@ -170,7 +170,10 @@ class SCOPED_LOCKABLE CMutexLock typedef CMutexLock CCriticalBlock; -#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) +#define PASTE(x, y) x ## y +#define PASTE2(x, y) PASTE(x, y) + +#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) #define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) #define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) diff --git a/src/util.cpp b/src/util.cpp index 23c48c68e..eeb7bc65a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -76,6 +76,7 @@ const char * const BITCOIN_CONF_FILENAME = "bitcoinz.conf"; const char * const BITCOIN_PID_FILENAME = "bitcoinzd.pid"; const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; +CCriticalSection cs_args; map mapArgs; map > mapMultiArgs; bool fDebug = false; @@ -286,6 +287,7 @@ static void InterpretNegativeSetting(std::string& strKey, std::string& strValue) void ParseParameters(int argc, const char* const argv[]) { + LOCK(cs_args); mapArgs.clear(); mapMultiArgs.clear(); @@ -321,6 +323,7 @@ void ParseParameters(int argc, const char* const argv[]) std::string GetArg(const std::string& strArg, const std::string& strDefault) { + LOCK(cs_args); if (mapArgs.count(strArg)) return mapArgs[strArg]; return strDefault; @@ -328,6 +331,7 @@ std::string GetArg(const std::string& strArg, const std::string& strDefault) int64_t GetArg(const std::string& strArg, int64_t nDefault) { + LOCK(cs_args); if (mapArgs.count(strArg)) return atoi64(mapArgs[strArg]); return nDefault; @@ -335,6 +339,7 @@ int64_t GetArg(const std::string& strArg, int64_t nDefault) bool GetBoolArg(const std::string& strArg, bool fDefault) { + LOCK(cs_args); if (mapArgs.count(strArg)) return InterpretBool(mapArgs[strArg]); return fDefault; @@ -342,6 +347,7 @@ bool GetBoolArg(const std::string& strArg, bool fDefault) bool SoftSetArg(const std::string& strArg, const std::string& strValue) { + LOCK(cs_args); if (mapArgs.count(strArg)) return false; mapArgs[strArg] = strValue; @@ -457,7 +463,7 @@ static fs::path ZC_GetBaseParamsDir() const fs::path &ZC_GetParamsDir() { - LOCK(csPathCached); // Reuse the same lock as upstream. + LOCK2(cs_args, csPathCached); fs::path &path = zc_paramsPathCached; @@ -477,6 +483,7 @@ const fs::path &ZC_GetParamsDir() const fs::path GetExportDir() { fs::path path; + LOCK(cs_args); if (mapArgs.count("-exportdir")) { path = fs::system_complete(mapArgs["-exportdir"]); if (fs::exists(path) && !fs::is_directory(path)) { @@ -492,7 +499,7 @@ const fs::path GetExportDir() const fs::path &GetDataDir(bool fNetSpecific) { - LOCK(csPathCached); + LOCK2(cs_args, csPathCached); fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; @@ -520,6 +527,7 @@ const fs::path &GetDataDir(bool fNetSpecific) void ClearDatadirCache() { + LOCK(csPathCached); pathCached = fs::path(); pathCachedNetSpecific = fs::path(); } @@ -540,15 +548,47 @@ void ReadConfigFile(const std::string& confPath, set setOptions; setOptions.insert("*"); - for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) + const vector allowed_duplicates = { + "addnode", + "bind", + "connect", + "debug", + "externalip", + "fundingstream", + "loadblock", + "metricsallowip", + "nuparams", + "onlynet", + "rpcallowip", + "rpcauth", + "rpcbind", + "seednode", + "uacomment", + "whitebind", + "whitelist" + }; + set unique_options; + { - string strKey = string("-") + it->string_key; - string strValue = it->value[0]; - InterpretNegativeSetting(strKey, strValue); - // Don't overwrite existing settings so command line settings override bitcoinz.conf - if (mapSettingsRet.count(strKey) == 0) - mapSettingsRet[strKey] = strValue; - mapMultiSettingsRet[strKey].push_back(strValue); + LOCK(cs_args); + for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) + { + string strKey = string("-") + it->string_key; + string strValue = it->value[0]; + + if (find(allowed_duplicates.begin(), allowed_duplicates.end(), it->string_key) == allowed_duplicates.end()) + { + if (!unique_options.insert(strKey).second) { + throw std::runtime_error(strprintf("Option '%s' is duplicated, which is not allowed.", strKey)); + } + } + + InterpretNegativeSetting(strKey, strValue); + // Don't overwrite existing settings so command line settings override zcash.conf + if (mapSettingsRet.count(strKey) == 0) + mapSettingsRet[strKey] = strValue; + mapMultiSettingsRet[strKey].push_back(strValue); + } } // If datadir is changed in .conf file: ClearDatadirCache(); diff --git a/src/utiltime.cpp b/src/utiltime.cpp index b99b6be86..64a757726 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -50,8 +50,9 @@ void MilliSleep(int64_t n) std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) { + static std::locale classic(std::locale::classic()); // std::locale takes ownership of the pointer - std::locale loc(std::locale::classic(), new boost::posix_time::time_facet(pszFormat)); + std::locale loc(classic, new boost::posix_time::time_facet(pszFormat)); std::stringstream ss; ss.imbue(loc); ss << boost::posix_time::from_time_t(nTime); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 487d245f5..7017b7a71 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -24,9 +24,6 @@ using namespace std; -unsigned int nWalletDBUpdated; - - // // CDB // diff --git a/src/wallet/db.h b/src/wallet/db.h index 69099849d..08af47609 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -24,8 +24,6 @@ static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; static const bool DEFAULT_WALLET_PRIVDB = true; -extern unsigned int nWalletDBUpdated; - class CDBEnv { private: diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a9c32d440..042dd00a8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4895,7 +4895,7 @@ bool CWallet::InitLoadWallet(bool clearWitnessCaches) LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); walletInstance->SetBestChain(chainActive.GetLocator()); - nWalletDBUpdated++; + CWalletDB::IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 8f8fe043a..f896baaba 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -20,19 +20,22 @@ #include #include +#include #include using namespace std; static uint64_t nAccountingEntryNumber = 0; +static std::atomic nWalletDBUpdateCounter; + // // CWalletDB // bool CWalletDB::WriteName(const string& strAddress, const string& strName) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(make_pair(string("name"), strAddress), strName); } @@ -40,37 +43,37 @@ bool CWalletDB::EraseName(const string& strAddress) { // This should only be used for sending addresses, never for receiving addresses, // receiving addresses must always have an address book entry if they're not change return. - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(make_pair(string("name"), strAddress)); } bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(make_pair(string("purpose"), strAddress), strPurpose); } bool CWalletDB::ErasePurpose(const string& strPurpose) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(make_pair(string("purpose"), strPurpose)); } bool CWalletDB::WriteTx(const CWalletTx& wtx) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx); } bool CWalletDB::EraseTx(uint256 hash) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("tx"), hash)); } bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) @@ -90,7 +93,7 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, const CKeyMetadata &keyMeta) { const bool fEraseUnencryptedKey = true; - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) @@ -112,7 +115,7 @@ bool CWalletDB::WriteCryptedZKey(const libzcash::SproutPaymentAddress & addr, const CKeyMetadata &keyMeta) { const bool fEraseUnencryptedKey = true; - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta)) return false; @@ -132,7 +135,7 @@ bool CWalletDB::WriteCryptedSaplingZKey( const CKeyMetadata &keyMeta) { const bool fEraseUnencryptedKey = true; - nWalletDBUpdated++; + nWalletDBUpdateCounter++; auto ivk = extfvk.fvk.in_viewing_key(); if (!Write(std::make_pair(std::string("sapzkeymeta"), ivk), keyMeta)) @@ -150,13 +153,13 @@ bool CWalletDB::WriteCryptedSaplingZKey( bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } bool CWalletDB::WriteZKey(const libzcash::SproutPaymentAddress& addr, const libzcash::SproutSpendingKey& key, const CKeyMetadata &keyMeta) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta)) return false; @@ -168,7 +171,7 @@ bool CWalletDB::WriteSaplingZKey(const libzcash::SaplingIncomingViewingKey &ivk, const libzcash::SaplingExtendedSpendingKey &key, const CKeyMetadata &keyMeta) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("sapzkeymeta"), ivk), keyMeta)) return false; @@ -180,58 +183,58 @@ bool CWalletDB::WriteSaplingPaymentAddress( const libzcash::SaplingPaymentAddress &addr, const libzcash::SaplingIncomingViewingKey &ivk) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("sapzaddr"), addr), ivk, false); } bool CWalletDB::WriteSproutViewingKey(const libzcash::SproutViewingKey &vk) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("vkey"), vk), '1'); } bool CWalletDB::EraseSproutViewingKey(const libzcash::SproutViewingKey &vk) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("vkey"), vk)); } bool CWalletDB::WriteSaplingExtendedFullViewingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("sapextfvk"), extfvk), '1'); } bool CWalletDB::EraseSaplingExtendedFullViewingKey( const libzcash::SaplingExtendedFullViewingKey &extfvk) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("sapextfvk"), extfvk)); } bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false); } bool CWalletDB::WriteWatchOnly(const CScript &dest) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1'); } bool CWalletDB::EraseWatchOnly(const CScript &dest) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest))); } bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan return Write(std::string("bestblock_nomerkle"), locator); } @@ -244,19 +247,19 @@ bool CWalletDB::ReadBestBlock(CBlockLocator& locator) bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("orderposnext"), nOrderPosNext); } bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("defaultkey"), vchPubKey); } bool CWalletDB::WriteWitnessCacheSize(int64_t nWitnessCacheSize) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("witnesscachesize"), nWitnessCacheSize); } @@ -267,13 +270,13 @@ bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("pool"), nPool), keypool); } bool CWalletDB::ErasePool(int64_t nPool) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("pool"), nPool)); } @@ -736,8 +739,8 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; + LOCK(pwallet->cs_wallet); try { - LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (Read((string)"minversion", nMinVersion)) { @@ -937,20 +940,20 @@ void ThreadFlushWalletDB(const string& strFile) if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) return; - unsigned int nLastSeen = nWalletDBUpdated; - unsigned int nLastFlushed = nWalletDBUpdated; + unsigned int nLastSeen = CWalletDB::GetUpdateCounter(); + unsigned int nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nLastWalletUpdate = GetTime(); while (true) { MilliSleep(500); - if (nLastSeen != nWalletDBUpdated) + if (nLastSeen != CWalletDB::GetUpdateCounter()) { - nLastSeen = nWalletDBUpdated; + nLastSeen = CWalletDB::GetUpdateCounter(); nLastWalletUpdate = GetTime(); } - if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) + if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2) { TRY_LOCK(bitdb.cs_db,lockDb); if (lockDb) @@ -971,7 +974,7 @@ void ThreadFlushWalletDB(const string& strFile) if (mi != bitdb.mapFileUseCount.end()) { LogPrint("db", "Flushing %s\n", strFile); - nLastFlushed = nWalletDBUpdated; + nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nStart = GetTimeMillis(); // Flush wallet file so it's self contained @@ -1114,31 +1117,41 @@ bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename) bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); } bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } bool CWalletDB::WriteHDSeed(const HDSeed& seed) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("hdseed"), seed.Fingerprint()), seed.RawSeed()); } bool CWalletDB::WriteCryptedHDSeed(const uint256& seedFp, const std::vector& vchCryptedSecret) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("chdseed"), seedFp), vchCryptedSecret); } bool CWalletDB::WriteHDChain(const CHDChain& chain) { - nWalletDBUpdated++; + nWalletDBUpdateCounter++; return Write(std::string("hdchain"), chain); } + +void CWalletDB::IncrementUpdateCounter() +{ + nWalletDBUpdateCounter++; +} + +unsigned int CWalletDB::GetUpdateCounter() +{ + return nWalletDBUpdateCounter; +} diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 9e4cfd32b..6c03c14ed 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -193,6 +193,8 @@ class CWalletDB : public CDB bool WriteSaplingExtendedFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk); bool EraseSaplingExtendedFullViewingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk); + static void IncrementUpdateCounter(); + static unsigned int GetUpdateCounter(); private: CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); From d68eafc7516ef29bad1cabcd62b742c318c74701 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 12:46:17 +0000 Subject: [PATCH 088/146] Backport bloom filter improvements --- src/bench/rollingbloom.cpp | 8 ++ src/bloom.cpp | 179 +++++++++++++++++++------------------ src/bloom.h | 44 ++++----- src/chain.cpp | 2 - src/core_read.cpp | 30 +++---- src/core_write.cpp | 46 +++++----- src/init.cpp | 27 +++--- src/key.cpp | 6 +- src/main.cpp | 11 ++- src/merkleblock.cpp | 12 ++- src/pubkey.cpp | 6 +- src/pubkey.h | 4 +- src/reverselock.h | 6 +- src/test/bloom_tests.cpp | 26 +++--- src/test/crypto_tests.cpp | 10 +-- src/test/net_tests.cpp | 4 +- src/test/script_tests.cpp | 4 +- src/test/util_tests.cpp | 2 +- src/torcontrol.cpp | 46 +++++----- src/txmempool.cpp | 6 +- src/wallet/wallet.cpp | 16 ++-- 21 files changed, 252 insertions(+), 243 deletions(-) diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp index e9fa2ebe5..29116b25f 100644 --- a/src/bench/rollingbloom.cpp +++ b/src/bench/rollingbloom.cpp @@ -39,4 +39,12 @@ static void RollingBloom(benchmark::State& state) } } +static void RollingBloomReset(benchmark::State& state) +{ + CRollingBloomFilter filter(120000, 0.000001); + while (state.KeepRunning()) { + filter.reset(); + } +} + BENCHMARK(RollingBloom); diff --git a/src/bloom.cpp b/src/bloom.cpp index 1de10526f..0a56e72c3 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -14,51 +14,38 @@ #include #include +#include + #define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455 #define LN2 0.6931471805599453094172321214581765680755001343602552 -using namespace std; - -CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) : +CBloomFilter::CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweakIn, unsigned char nFlagsIn) : /** * The ideal size for a bloom filter with a given number of elements and false positive rate is: * - nElements * log(fp rate) / ln(2)^2 * We ignore filter parameters which will create a bloom filter larger than the protocol limits */ - vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), + vData(std::min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8), /** * The ideal number of hash functions is filter size * ln(2) / number of elements * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas */ - isFull(false), - isEmpty(false), - nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), + nHashFuncs(std::min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), nTweak(nTweakIn), nFlags(nFlagsIn) { } -// Private constructor used by CRollingBloomFilter -CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn) : - vData((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)) / 8), - isFull(false), - isEmpty(true), - nHashFuncs((unsigned int)(vData.size() * 8 / nElements * LN2)), - nTweak(nTweakIn), - nFlags(BLOOM_UPDATE_NONE) -{ -} - inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector& vDataToHash) const { // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values. return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8); } -void CBloomFilter::insert(const vector& vKey) +void CBloomFilter::insert(const std::vector& vKey) { - if (isFull) + if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700) return; for (unsigned int i = 0; i < nHashFuncs; i++) { @@ -66,29 +53,26 @@ void CBloomFilter::insert(const vector& vKey) // Sets bit nIndex of vData vData[nIndex >> 3] |= (1 << (7 & nIndex)); } - isEmpty = false; } void CBloomFilter::insert(const COutPoint& outpoint) { CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << outpoint; - vector data(stream.begin(), stream.end()); + std::vector data(stream.begin(), stream.end()); insert(data); } void CBloomFilter::insert(const uint256& hash) { - vector data(hash.begin(), hash.end()); + std::vector data(hash.begin(), hash.end()); insert(data); } -bool CBloomFilter::contains(const vector& vKey) const +bool CBloomFilter::contains(const std::vector& vKey) const { - if (isFull) + if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700) return true; - if (isEmpty) - return false; for (unsigned int i = 0; i < nHashFuncs; i++) { unsigned int nIndex = Hash(i, vKey); @@ -103,29 +87,16 @@ bool CBloomFilter::contains(const COutPoint& outpoint) const { CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << outpoint; - vector data(stream.begin(), stream.end()); + std::vector data(stream.begin(), stream.end()); return contains(data); } bool CBloomFilter::contains(const uint256& hash) const { - vector data(hash.begin(), hash.end()); + std::vector data(hash.begin(), hash.end()); return contains(data); } -void CBloomFilter::clear() -{ - vData.assign(vData.size(),0); - isFull = false; - isEmpty = true; -} - -void CBloomFilter::reset(unsigned int nNewTweak) -{ - clear(); - nTweak = nNewTweak; -} - bool CBloomFilter::IsWithinSizeConstraints() const { return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; @@ -136,10 +107,8 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) bool fFound = false; // Match if the filter contains the hash of tx // for finding tx when they appear in a block - if (isFull) + if (vData.empty()) // zero-size = "match-all" filter return true; - if (isEmpty) - return false; const uint256& hash = tx.GetHash(); if (contains(hash)) fFound = true; @@ -152,7 +121,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) // This means clients don't have to update the filter themselves when a new relevant tx // is discovered in order to find spending transactions, which avoids round-tripping and race conditions. CScript::const_iterator pc = txout.scriptPubKey.begin(); - vector data; + std::vector data; while (pc < txout.scriptPubKey.end()) { opcodetype opcode; @@ -166,7 +135,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) { txnouttype type; - vector > vSolutions; + std::vector > vSolutions; if (Solver(txout.scriptPubKey, type, vSolutions) && (type == TX_PUBKEY || type == TX_MULTISIG)) insert(COutPoint(hash, i)); @@ -187,7 +156,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) // Match if the filter contains any arbitrary script data element in any scriptSig in tx CScript::const_iterator pc = txin.scriptSig.begin(); - vector data; + std::vector data; while (pc < txin.scriptSig.end()) { opcodetype opcode; @@ -201,70 +170,106 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) return false; } -void CBloomFilter::UpdateEmptyFull() +CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const double fpRate) { - bool full = true; - bool empty = true; - for (unsigned int i = 0; i < vData.size(); i++) - { - full &= vData[i] == 0xff; - empty &= vData[i] == 0; - } - isFull = full; - isEmpty = empty; + double logFpRate = log(fpRate); + /* The optimal number of hash functions is log(fpRate) / log(0.5), but + * restrict it to the range 1-50. */ + nHashFuncs = std::max(1, std::min((int)round(logFpRate / log(0.5)), 50)); + /* In this rolling bloom filter, we'll store between 2 and 3 generations of nElements / 2 entries. */ + nEntriesPerGeneration = (nElements + 1) / 2; + uint32_t nMaxElements = nEntriesPerGeneration * 3; + /* The maximum fpRate = pow(1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits), nHashFuncs) + * => pow(fpRate, 1.0 / nHashFuncs) = 1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits) + * => 1.0 - pow(fpRate, 1.0 / nHashFuncs) = exp(-nHashFuncs * nMaxElements / nFilterBits) + * => log(1.0 - pow(fpRate, 1.0 / nHashFuncs)) = -nHashFuncs * nMaxElements / nFilterBits + * => nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - pow(fpRate, 1.0 / nHashFuncs)) + * => nFilterBits = -nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs)) + */ + uint32_t nFilterBits = (uint32_t)ceil(-1.0 * nHashFuncs * nMaxElements / log(1.0 - exp(logFpRate / nHashFuncs))); + data.clear(); + /* For each data element we need to store 2 bits. If both bits are 0, the + * bit is treated as unset. If the bits are (01), (10), or (11), the bit is + * treated as set in generation 1, 2, or 3 respectively. + * These bits are stored in separate integers: position P corresponds to bit + * (P & 63) of the integers data[(P >> 6) * 2] and data[(P >> 6) * 2 + 1]. */ + data.resize(((nFilterBits + 63) / 64) << 1); + reset(); } -CRollingBloomFilter::CRollingBloomFilter(unsigned int nElements, double fpRate) : - b1(nElements * 2, fpRate, 0), b2(nElements * 2, fpRate, 0) -{ - // Implemented using two bloom filters of 2 * nElements each. - // We fill them up, and clear them, staggered, every nElements - // inserted, so at least one always contains the last nElements - // inserted. - nInsertions = 0; - nBloomSize = nElements * 2; +/* Similar to CBloomFilter::Hash */ +static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, const std::vector& vDataToHash) { + return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash); +} - reset(); +// A replacement for x % n. This assumes that x and n are 32bit integers, and x is a uniformly random distributed 32bit value +// which should be the case for a good hash. +// See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ +static inline uint32_t FastMod(uint32_t x, size_t n) { + return ((uint64_t)x * (uint64_t)n) >> 32; } void CRollingBloomFilter::insert(const std::vector& vKey) { - if (nInsertions == 0) { - b1.clear(); - } else if (nInsertions == nBloomSize / 2) { - b2.clear(); + if (nEntriesThisGeneration == nEntriesPerGeneration) { + nEntriesThisGeneration = 0; + nGeneration++; + if (nGeneration == 4) { + nGeneration = 1; + } + uint64_t nGenerationMask1 = 0 - (uint64_t)(nGeneration & 1); + uint64_t nGenerationMask2 = 0 - (uint64_t)(nGeneration >> 1); + /* Wipe old entries that used this generation number. */ + for (uint32_t p = 0; p < data.size(); p += 2) { + uint64_t p1 = data[p], p2 = data[p + 1]; + uint64_t mask = (p1 ^ nGenerationMask1) | (p2 ^ nGenerationMask2); + data[p] = p1 & mask; + data[p + 1] = p2 & mask; + } } - b1.insert(vKey); - b2.insert(vKey); - if (++nInsertions == nBloomSize) { - nInsertions = 0; + nEntriesThisGeneration++; + + for (int n = 0; n < nHashFuncs; n++) { + uint32_t h = RollingBloomHash(n, nTweak, vKey); + int bit = h & 0x3F; + /* FastMod works with the upper bits of h, so it is safe to ignore that the lower bits of h are already used for bit. */ + uint32_t pos = FastMod(h, data.size()); + /* The lowest bit of pos is ignored, and set to zero for the first bit, and to one for the second. */ + data[pos & ~1] = (data[pos & ~1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration & 1)) << bit; + data[pos | 1] = (data[pos | 1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration >> 1)) << bit; } } void CRollingBloomFilter::insert(const uint256& hash) { - vector data(hash.begin(), hash.end()); - insert(data); + std::vector vData(hash.begin(), hash.end()); + insert(vData); } bool CRollingBloomFilter::contains(const std::vector& vKey) const { - if (nInsertions < nBloomSize / 2) { - return b2.contains(vKey); + for (int n = 0; n < nHashFuncs; n++) { + uint32_t h = RollingBloomHash(n, nTweak, vKey); + int bit = h & 0x3F; + uint32_t pos = FastMod(h, data.size()); + /* If the relevant bit is not set in either data[pos & ~1] or data[pos | 1], the filter does not contain vKey */ + if (!(((data[pos & ~1] | data[pos | 1]) >> bit) & 1)) { + return false; + } } - return b1.contains(vKey); + return true; } bool CRollingBloomFilter::contains(const uint256& hash) const { - vector data(hash.begin(), hash.end()); - return contains(data); + std::vector vData(hash.begin(), hash.end()); + return contains(vData); } void CRollingBloomFilter::reset() { - unsigned int nNewTweak = GetRand(std::numeric_limits::max()); - b1.reset(nNewTweak); - b2.reset(nNewTweak); - nInsertions = 0; + nTweak = GetRand(std::numeric_limits::max()); + nEntriesThisGeneration = 0; + nGeneration = 1; + std::fill(data.begin(), data.end(), 0); } diff --git a/src/bloom.h b/src/bloom.h index 009436104..e4c01fe49 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -45,18 +45,12 @@ class CBloomFilter { private: std::vector vData; - bool isFull; - bool isEmpty; unsigned int nHashFuncs; unsigned int nTweak; unsigned char nFlags; unsigned int Hash(unsigned int nHashNum, const std::vector& vDataToHash) const; - // Private constructor for CRollingBloomFilter, no restrictions on size - CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak); - friend class CRollingBloomFilter; - public: /** * Creates a new bloom filter which will provide the given fp rate when filled with the given number of elements @@ -67,8 +61,8 @@ class CBloomFilter * It should generally always be a random value (and is largely only exposed for unit testing) * nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK) */ - CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak, unsigned char nFlagsIn); - CBloomFilter() : isFull(true), isEmpty(false), nHashFuncs(0), nTweak(0), nFlags(0) {} + CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweak, unsigned char nFlagsIn); + CBloomFilter() : nHashFuncs(0), nTweak(0), nFlags(0) {} ADD_SERIALIZE_METHODS; @@ -88,18 +82,12 @@ class CBloomFilter bool contains(const COutPoint& outpoint) const; bool contains(const uint256& hash) const; - void clear(); - void reset(unsigned int nNewTweak); - //! True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS //! (catch a filter which was just deserialized which was too big) bool IsWithinSizeConstraints() const; //! Also adds any outputs which match the filter to the filter (to match their spending txes) bool IsRelevantAndUpdate(const CTransaction& tx); - - //! Checks for empty and full filters to avoid wasting cpu - void UpdateEmptyFull(); }; /** @@ -110,8 +98,22 @@ class CBloomFilter * reset() is provided, which also changes nTweak to decrease the impact of * false-positives. * - * contains(item) will always return true if item was one of the last N things + * contains(item) will always return true if item was one of the last N to 1.5*N * insert()'ed ... but may also return true for items that were not inserted. + * + * It needs around 1.8 bytes per element per factor 0.1 of false positive rate. + * For example, if we want 1000 elements, we'd need: + * - ~1800 bytes for a false positive rate of 0.1 + * - ~3600 bytes for a false positive rate of 0.01 + * - ~5400 bytes for a false positive rate of 0.001 + * + * If we make these simplifying assumptions: + * - logFpRate / log(0.5) doesn't get rounded or clamped in the nHashFuncs calculation + * - nElements is even, so that nEntriesPerGeneration == nElements / 2 + * + * Then we get a more accurate estimate for filter bytes: + * + * 3/(log(256)*log(2)) * log(1/fpRate) * nElements */ class CRollingBloomFilter { @@ -119,7 +121,7 @@ class CRollingBloomFilter // A random bloom filter calls GetRand() at creation time. // Don't create global CRollingBloomFilter objects, as they may be // constructed before the randomizer is properly initialized. - CRollingBloomFilter(unsigned int nElements, double nFPRate); + CRollingBloomFilter(const unsigned int nElements, const double nFPRate); void insert(const std::vector& vKey); void insert(const uint256& hash); @@ -129,10 +131,12 @@ class CRollingBloomFilter void reset(); private: - unsigned int nBloomSize; - unsigned int nInsertions; - CBloomFilter b1, b2; + int nEntriesPerGeneration; + int nEntriesThisGeneration; + int nGeneration; + std::vector data; + unsigned int nTweak; + int nHashFuncs; }; - #endif // BITCOIN_BLOOM_H diff --git a/src/chain.cpp b/src/chain.cpp index e00aca639..43d5b8bc2 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -8,8 +8,6 @@ #include "main.h" #include "txdb.h" -using namespace std; - /** * CChain implementation */ diff --git a/src/core_read.cpp b/src/core_read.cpp index 23b4a610c..19a5b6952 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -20,13 +20,11 @@ #include #include -using namespace std; - CScript ParseScript(const std::string& s) { CScript result; - static map mapOpNames; + static std::map mapOpNames; if (mapOpNames.empty()) { @@ -39,7 +37,7 @@ CScript ParseScript(const std::string& s) const char* name = GetOpName((opcodetype)op); if (strcmp(name, "OP_UNKNOWN") == 0) continue; - string strName(name); + std::string strName(name); mapOpNames[strName] = (opcodetype)op; // Convenience: OP_ADD and just ADD are both recognized: boost::algorithm::replace_first(strName, "OP_", ""); @@ -47,7 +45,7 @@ CScript ParseScript(const std::string& s) } } - vector words; + std::vector words; boost::algorithm::split(words, s, boost::algorithm::is_any_of(" \t\n"), boost::algorithm::token_compress_on); for (std::vector::const_iterator w = words.begin(); w != words.end(); ++w) @@ -57,16 +55,16 @@ CScript ParseScript(const std::string& s) // Empty string, ignore. (boost::split given '' will return one word) } else if (all(*w, boost::algorithm::is_digit()) || - (boost::algorithm::starts_with(*w, "-") && all(string(w->begin()+1, w->end()), boost::algorithm::is_digit()))) + (boost::algorithm::starts_with(*w, "-") && all(std::string(w->begin()+1, w->end()), boost::algorithm::is_digit()))) { // Number int64_t n = atoi64(*w); result << n; } - else if (boost::algorithm::starts_with(*w, "0x") && (w->begin()+2 != w->end()) && IsHex(string(w->begin()+2, w->end()))) + else if (boost::algorithm::starts_with(*w, "0x") && (w->begin()+2 != w->end()) && IsHex(std::string(w->begin()+2, w->end()))) { // Raw hex data, inserted NOT pushed onto stack: - std::vector raw = ParseHex(string(w->begin()+2, w->end())); + std::vector raw = ParseHex(std::string(w->begin()+2, w->end())); result.insert(result.end(), raw.begin(), raw.end()); } else if (w->size() >= 2 && boost::algorithm::starts_with(*w, "'") && boost::algorithm::ends_with(*w, "'")) @@ -83,7 +81,7 @@ CScript ParseScript(const std::string& s) } else { - throw runtime_error("script parse error"); + throw std::runtime_error("script parse error"); } } @@ -95,7 +93,7 @@ bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx) if (!IsHex(strHexTx)) return false; - vector txData(ParseHex(strHexTx)); + std::vector txData(ParseHex(strHexTx)); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); try { ssData >> tx; @@ -124,9 +122,9 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) return true; } -uint256 ParseHashUV(const UniValue& v, const string& strName) +uint256 ParseHashUV(const UniValue& v, const std::string& strName) { - string strHex; + std::string strHex; if (v.isStr()) strHex = v.getValStr(); return ParseHashStr(strHex, strName); // Note: ParseHashStr("") throws a runtime_error @@ -135,19 +133,19 @@ uint256 ParseHashUV(const UniValue& v, const string& strName) uint256 ParseHashStr(const std::string& strHex, const std::string& strName) { if (!IsHex(strHex)) // Note: IsHex("") is false - throw runtime_error(strName+" must be hexadecimal string (not '"+strHex+"')"); + throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')"); uint256 result; result.SetHex(strHex); return result; } -vector ParseHexUV(const UniValue& v, const string& strName) +std::vector ParseHexUV(const UniValue& v, const std::string& strName) { - string strHex; + std::string strHex; if (v.isStr()) strHex = v.getValStr(); if (!IsHex(strHex)) - throw runtime_error(strName+" must be hexadecimal string (not '"+strHex+"')"); + throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')"); return ParseHex(strHex); } diff --git a/src/core_write.cpp b/src/core_write.cpp index a94e64658..70ae82b25 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -17,16 +17,14 @@ #include -using namespace std; - -string FormatScript(const CScript& script) +std::string FormatScript(const CScript& script) { - string ret; + std::string ret; CScript::const_iterator it = script.begin(); opcodetype op; while (it != script.end()) { CScript::const_iterator it2 = it; - vector vch; + std::vector vch; if (script.GetOp2(it, op, &vch)) { if (op == OP_0) { ret += "0 "; @@ -34,17 +32,17 @@ string FormatScript(const CScript& script) } else if ((op >= OP_1 && op <= OP_16) || op == OP_1NEGATE) { ret += strprintf("%i ", op - OP_1NEGATE - 1); continue; - } else if (op >= OP_NOP && op <= OP_CHECKMULTISIGVERIFY) { - string str(GetOpName(op)); - if (str.substr(0, 3) == string("OP_")) { - ret += str.substr(3, string::npos) + " "; + } else if (op >= OP_NOP && op <= OP_NOP10) { + std::string str(GetOpName(op)); + if (str.substr(0, 3) == std::string("OP_")) { + ret += str.substr(3, std::string::npos) + " "; continue; } } if (vch.size() > 0) { ret += strprintf("0x%x 0x%x ", HexStr(it2, it - vch.size()), HexStr(it - vch.size(), it)); } else { - ret += strprintf("0x%x", HexStr(it2, it)); + ret += strprintf("0x%x ", HexStr(it2, it)); } continue; } @@ -54,14 +52,14 @@ string FormatScript(const CScript& script) return ret.substr(0, ret.size() - 1); } -const map mapSigHashTypes = +const std::map mapSigHashTypes = boost::assign::map_list_of - (static_cast(SIGHASH_ALL), string("ALL")) - (static_cast(SIGHASH_ALL|SIGHASH_ANYONECANPAY), string("ALL|ANYONECANPAY")) - (static_cast(SIGHASH_NONE), string("NONE")) - (static_cast(SIGHASH_NONE|SIGHASH_ANYONECANPAY), string("NONE|ANYONECANPAY")) - (static_cast(SIGHASH_SINGLE), string("SINGLE")) - (static_cast(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), string("SINGLE|ANYONECANPAY")) + (static_cast(SIGHASH_ALL), std::string("ALL")) + (static_cast(SIGHASH_ALL|SIGHASH_ANYONECANPAY), std::string("ALL|ANYONECANPAY")) + (static_cast(SIGHASH_NONE), std::string("NONE")) + (static_cast(SIGHASH_NONE|SIGHASH_ANYONECANPAY), std::string("NONE|ANYONECANPAY")) + (static_cast(SIGHASH_SINGLE), std::string("SINGLE")) + (static_cast(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), std::string("SINGLE|ANYONECANPAY")) ; /** @@ -71,11 +69,11 @@ const map mapSigHashTypes = * of a signature. Only pass true for scripts you believe could contain signatures. For example, * pass false, or omit the this argument (defaults to false), for scriptPubKeys. */ -string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) +std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) { - string str; + std::string str; opcodetype opcode; - vector vch; + std::vector vch; CScript::const_iterator pc = script.begin(); while (pc < script.end()) { if (!str.empty()) { @@ -86,12 +84,12 @@ string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) return str; } if (0 <= opcode && opcode <= OP_PUSHDATA4) { - if (vch.size() <= static_cast::size_type>(4)) { + if (vch.size() <= static_cast::size_type>(4)) { str += strprintf("%d", CScriptNum(vch, false).getint()); } else { // the IsUnspendable check makes sure not to try to decode OP_RETURN data that may match the format of a signature if (fAttemptSighashDecode && !script.IsUnspendable()) { - string strSigHashDecode; + std::string strSigHashDecode; // goal: only attempt to decode a defined sighash type from data that looks like a signature within a scriptSig. // this won't decode correctly formatted public keys in Pubkey or Multisig scripts due to // the restrictions on the pubkey formats (see IsCompressedOrUncompressedPubKey) being incongruous with the @@ -115,7 +113,7 @@ string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) return str; } -string EncodeHexTx(const CTransaction& tx) +std::string EncodeHexTx(const CTransaction& tx) { CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; @@ -126,7 +124,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) { txnouttype type; - vector addresses; + std::vector addresses; int nRequired; out.pushKV("asm", ScriptToAsmStr(scriptPubKey)); diff --git a/src/init.cpp b/src/init.cpp index e2c1fd0f7..0731d5a60 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -66,7 +66,6 @@ #include "librustzcash.h" -using namespace std; using namespace boost::placeholders; extern void ThreadSendAlert(); @@ -307,10 +306,10 @@ void OnRPCStopped() void OnRPCPreCommand(const CRPCCommand& cmd) { // Observe safe mode - string strWarning = GetWarnings("rpc"); + std::string strWarning = GetWarnings("rpc"); if (strWarning != "" && !GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) && !cmd.okSafeMode) - throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning); } std::string HelpMessage(HelpMessageMode mode) @@ -320,7 +319,7 @@ std::string HelpMessage(HelpMessageMode mode) // When adding new options to the categories, please keep and ensure alphabetical ordering. // Do not translate _(...) -help-debug options, many technical terms, and only a very small audience, so is unnecessary stress to translators - string strUsage = HelpMessageGroup(_("Options:")); + std::string strUsage = HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); strUsage += HelpMessageOpt("-version", _("Print version and exit")); strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS)); @@ -424,7 +423,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT)); strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)"); } - string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, partitioncheck, pow, proxy, prune, " + std::string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, pow, proxy, prune, " "rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these strUsage += HelpMessageOpt("-debug=", strprintf(_("Output debugging information (default: %u, supplying is optional)"), 0) + ". " + _("If is not supplied or if = 1, output all debugging information.") + " " + _(" can be:") + " " + debugCategories + ". " + @@ -570,7 +569,7 @@ struct CImportingNow void CleanupBlockRevFiles() { using namespace fs; - map mapBlockFiles; + std::map mapBlockFiles; // Glob all blk?????.dat and rev?????.dat files from the blocks directory. // Remove the rev files immediately and insert the blk file paths into an @@ -594,7 +593,7 @@ void CleanupBlockRevFiles() // keeping a separate counter. Once we hit a gap (or if 0 doesn't exist) // start removing block files. int nContigCounter = 0; - for (const std::pair& item : mapBlockFiles) { + for (const std::pair& item : mapBlockFiles) { if (atoi(item.first) == nContigCounter) { nContigCounter++; continue; @@ -1072,15 +1071,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fDebug = !mapMultiArgs["-debug"].empty(); // Special-case: if -debug=0/-nodebug is set, turn off debugging messages - const vector& categories = mapMultiArgs["-debug"]; - if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end()) + const std::vector& categories = mapMultiArgs["-debug"]; + if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), std::string("0")) != categories.end()) fDebug = false; // Special case: if debug=zrpcunsafe, implies debug=zrpc, so add it to debug categories - if (find(categories.begin(), categories.end(), string("zrpcunsafe")) != categories.end()) { - if (find(categories.begin(), categories.end(), string("zrpc")) == categories.end()) { + if (find(categories.begin(), categories.end(), std::string("zrpcunsafe")) != categories.end()) { + if (find(categories.begin(), categories.end(), std::string("zrpc")) == categories.end()) { LogPrintf("%s: parameter interaction: setting -debug=zrpcunsafe -> -debug=zrpc\n", __func__); - vector& v = mapMultiArgs["-debug"]; + std::vector& v = mapMultiArgs["-debug"]; v.push_back("zrpc"); } } @@ -1197,7 +1196,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (Params().NetworkIDString() != "regtest") { return InitError("Network upgrade parameters may only be overridden on regtest."); } - const vector& deployments = mapMultiArgs["-nuparams"]; + const std::vector& deployments = mapMultiArgs["-nuparams"]; for (auto i : deployments) { std::vector vDeploymentParams; boost::split(vDeploymentParams, i, boost::is_any_of(":")); @@ -1331,7 +1330,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // sanitize comments per BIP-0014, format user agent and check total size std::vector uacomments; - for (string cmt : mapMultiArgs["-uacomment"]) + for (std::string cmt : mapMultiArgs["-uacomment"]) { if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) return InitError(strprintf("User Agent comment (%s) contains unsafe characters.", cmt)); diff --git a/src/key.cpp b/src/key.cpp index 573023a1d..8cb249957 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -277,12 +277,12 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const return ret; } -bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const { +bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const { out.nDepth = nDepth + 1; CKeyID id = key.GetPubKey().GetID(); memcpy(&out.vchFingerprint[0], &id, 4); - out.nChild = nChild; - return key.Derive(out.key, out.chaincode, nChild, chaincode); + out.nChild = _nChild; + return key.Derive(out.key, out.chaincode, _nChild, chaincode); } void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) { diff --git a/src/main.cpp b/src/main.cpp index bf2cba670..067005880 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -209,7 +209,7 @@ namespace { * million to make it highly unlikely for users to have issues with this * filter. * - * Memory used: 1.7MB + * Memory used: 1.3 MB */ boost::scoped_ptr recentRejects; uint256 hashRecentRejectsChainTip; @@ -2663,14 +2663,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) { if (pindex->GetUndoPos().IsNull()) { - CDiskBlockPos pos; - if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) + CDiskBlockPos _pos; + if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock(): FindUndoPos failed"); - if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) + if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) return AbortNode(state, "Failed to write undo data"); // update nUndoPos in block index - pindex->nUndoPos = pos.nPos; + pindex->nUndoPos = _pos.nPos; pindex->nStatus |= BLOCK_HAVE_UNDO; } @@ -6192,7 +6192,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); - pfrom->pfilter->UpdateEmptyFull(); pfrom->fRelayTxes = true; } } diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index 25d3dc697..383a4585f 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -9,14 +9,12 @@ #include "consensus/consensus.h" #include "utilstrencodings.h" -using namespace std; - CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) { header = block.GetBlockHeader(); - vector vMatch; - vector vHashes; + std::vector vMatch; + std::vector vHashes; vMatch.reserve(block.vtx.size()); vHashes.reserve(block.vtx.size()); @@ -27,7 +25,7 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) if (filter.IsRelevantAndUpdate(block.vtx[i])) { vMatch.push_back(true); - vMatchedTxn.push_back(make_pair(i, hash)); + vMatchedTxn.push_back(std::make_pair(i, hash)); } else vMatch.push_back(false); @@ -41,8 +39,8 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set& txids) { header = block.GetBlockHeader(); - vector vMatch; - vector vHashes; + std::vector vMatch; + std::vector vHashes; vMatch.reserve(block.vtx.size()); vHashes.reserve(block.vtx.size()); diff --git a/src/pubkey.cpp b/src/pubkey.cpp index adabd11d1..32808a999 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -116,12 +116,12 @@ void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) { pubkey.Set(code+41, code+BIP32_EXTKEY_SIZE); } -bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const { +bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const { out.nDepth = nDepth + 1; CKeyID id = pubkey.GetID(); memcpy(&out.vchFingerprint[0], &id, 4); - out.nChild = nChild; - return pubkey.Derive(out.pubkey, out.chaincode, nChild, chaincode); + out.nChild = _nChild; + return pubkey.Derive(out.pubkey, out.chaincode, _nChild, chaincode); } /* static */ bool CPubKey::CheckLowS(const std::vector& vchSig) { diff --git a/src/pubkey.h b/src/pubkey.h index 2f5bd6aaa..b5fb1fb78 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -100,9 +100,9 @@ class CPubKey } //! Construct a public key from a byte vector. - CPubKey(const std::vector& vch) + CPubKey(const std::vector& _vch) { - Set(vch.begin(), vch.end()); + Set(_vch.begin(), _vch.end()); } //! Simple read-only vector-like interface to the pubkey data. diff --git a/src/reverselock.h b/src/reverselock.h index 85d2d964f..7c0131f35 100644 --- a/src/reverselock.h +++ b/src/reverselock.h @@ -13,9 +13,9 @@ class reverse_lock { public: - explicit reverse_lock(Lock& lock) : lock(lock) { - lock.unlock(); - lock.swap(templock); + explicit reverse_lock(Lock& _lock) : lock(_lock) { + _lock.unlock(); + _lock.swap(templock); } ~reverse_lock() { diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 62500cce4..4f9ab5ce1 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -29,16 +29,17 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize) { CBloomFilter filter(3, 0.01, 0, BLOOM_UPDATE_ALL); + BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter should be empty!"); filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")); - BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); + BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!"); // One bit different in first byte - BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!"); + BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter contains something it shouldn't!"); filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")); - BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!"); + BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "Bloom filter doesn't contain just-inserted object (2)!"); filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")); - BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!"); + BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "Bloom filter doesn't contain just-inserted object (3)!"); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << filter; @@ -51,9 +52,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize) BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end()); - BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); - filter.clear(); - BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter should be empty!"); + BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!"); } BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) @@ -62,15 +61,15 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) CBloomFilter filter(3, 0.01, 2147483649UL, BLOOM_UPDATE_ALL); filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")); - BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); + BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!"); // One bit different in first byte - BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!"); + BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter contains something it shouldn't!"); filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")); - BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!"); + BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "Bloom filter doesn't contain just-inserted object (2)!"); filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")); - BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!"); + BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "Bloom filter doesn't contain just-inserted object (3)!"); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << filter; @@ -506,11 +505,14 @@ BOOST_AUTO_TEST_CASE(rolling_bloom) if (i >= 100) BOOST_CHECK(rb1.contains(data[i-100])); rb1.insert(data[i]); + BOOST_CHECK(rb1.contains(data[i])); } // Insert 999 more random entries: for (int i = 0; i < 999; i++) { - rb1.insert(RandomData()); + std::vector d = RandomData(); + rb1.insert(d); + BOOST_CHECK(rb1.contains(d)); } // Sanity check to make sure the filter isn't just filling up: nHits = 0; diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 2da7bdef0..d507dc0f4 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -111,13 +111,13 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad { std::vector sub(i, in.end()); std::vector subout(sub.size() + AES_BLOCKSIZE); - int size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); - if (size != 0) + int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); + if (_size != 0) { - subout.resize(size); + subout.resize(_size); std::vector subdecrypted(subout.size()); - size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); - subdecrypted.resize(size); + _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); + subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); } diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 654d5c8f4..032639ef9 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -51,11 +51,11 @@ class CAddrManCorrupted : public CAddrManSerializationMock } }; -CDataStream AddrmanToStream(CAddrManSerializationMock& addrman) +CDataStream AddrmanToStream(CAddrManSerializationMock& _addrman) { CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION); ssPeersIn << FLATDATA(Params().MessageStart()); - ssPeersIn << addrman; + ssPeersIn << _addrman; std::string str = ssPeersIn.str(); vector vchData(str.begin(), str.end()); return CDataStream(vchData, SER_DISK, CLIENT_VERSION); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 2e99436c5..37ee96947 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -208,10 +208,10 @@ class TestBuilder spendTx = BuildSpendingTransaction(CScript(), creditTx); } - TestBuilder& Add(const CScript& script) + TestBuilder& Add(const CScript& _script) { DoPush(); - spendTx.vin[0].scriptSig += script; + spendTx.vin[0].scriptSig += _script; return *this; } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index a42a0631d..b1d16a062 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -312,7 +312,7 @@ BOOST_AUTO_TEST_CASE(test_ParseInt32) BOOST_CHECK(ParseInt32("1234", &n) && n == 1234); BOOST_CHECK(ParseInt32("01234", &n) && n == 1234); // no octal BOOST_CHECK(ParseInt32("2147483647", &n) && n == 2147483647); - BOOST_CHECK(ParseInt32("-2147483648", &n) && n == -2147483648); + BOOST_CHECK(ParseInt32("-2147483648", &n) && n == (-2147483647 - 1)); // (-2147483647 - 1) equals INT_MIN BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234); // Invalid values BOOST_CHECK(!ParseInt32("", &n)); diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 18c04412b..21f746765 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -122,8 +122,8 @@ class TorControlConnection static void eventcb(struct bufferevent *bev, short what, void *ctx); }; -TorControlConnection::TorControlConnection(struct event_base *base): - base(base), b_conn(0) +TorControlConnection::TorControlConnection(struct event_base *_base): + base(_base), b_conn(0) { } @@ -194,7 +194,7 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct } } -bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected) +bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& _connected, const ConnectionCB& _disconnected) { if (b_conn) Disconnect(); @@ -213,8 +213,8 @@ bool TorControlConnection::Connect(const std::string &target, const ConnectionCB return false; bufferevent_setcb(b_conn, TorControlConnection::readcb, NULL, TorControlConnection::eventcb, this); bufferevent_enable(b_conn, EV_READ|EV_WRITE); - this->connected = connected; - this->disconnected = disconnected; + this->connected = _connected; + this->disconnected = _disconnected; // Finally, connect to target if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) { @@ -452,18 +452,18 @@ class TorController static void reconnect_cb(evutil_socket_t fd, short what, void *arg); }; -TorController::TorController(struct event_base* baseIn, const std::string& target): - base(baseIn), - target(target), conn(base), reconnect(true), reconnect_ev(0), +TorController::TorController(struct event_base* _base, const std::string& _target): + base(_base), + target(_target), conn(base), reconnect(true), reconnect_ev(0), reconnect_timeout(RECONNECT_TIMEOUT_START) { reconnect_ev = event_new(base, -1, 0, reconnect_cb, this); if (!reconnect_ev) LogPrintf("tor: Failed to create event for reconnection: out of memory?\n"); // Start connection attempts immediately - if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1), + if (!conn.Connect(_target, boost::bind(&TorController::connected_cb, this, _1), boost::bind(&TorController::disconnected_cb, this, _1) )) { - LogPrintf("tor: Initiating connection to Tor control port %s failed\n", target); + LogPrintf("tor: Initiating connection to Tor control port %s failed\n", _target); } // Read service private key if cached std::pair pkf = ReadBinaryFile(GetPrivateKeyFile()); @@ -484,7 +484,7 @@ TorController::~TorController() } } -void TorController::add_onion_cb(TorControlConnection& conn, const TorControlReply& reply) +void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { LogPrint("tor", "tor: ADD_ONION successful\n"); @@ -520,7 +520,7 @@ void TorController::add_onion_cb(TorControlConnection& conn, const TorControlRep } } -void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& reply) +void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { LogPrint("tor", "tor: Authentication successful\n"); @@ -539,7 +539,7 @@ void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& r // Request hidden service, redirect port. // Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient // choice. TODO; refactor the shutdown sequence some day. - conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()), + _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()), boost::bind(&TorController::add_onion_cb, this, _1, _2)); } else { LogPrintf("tor: Authentication failed\n"); @@ -573,7 +573,7 @@ static std::vector ComputeResponse(const std::string &key, const std::v return computedHash; } -void TorController::authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply) +void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { LogPrint("tor", "tor: SAFECOOKIE authentication challenge successful\n"); @@ -599,7 +599,7 @@ void TorController::authchallenge_cb(TorControlConnection& conn, const TorContro } std::vector computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce); - conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2)); + _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2)); } else { LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n"); } @@ -608,7 +608,7 @@ void TorController::authchallenge_cb(TorControlConnection& conn, const TorContro } } -void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply) +void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { std::set methods; @@ -648,23 +648,23 @@ void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControl if (methods.count("HASHEDPASSWORD")) { LogPrint("tor", "tor: Using HASHEDPASSWORD authentication\n"); boost::replace_all(torpassword, "\"", "\\\""); - conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); + _conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); } else { LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n"); } } else if (methods.count("NULL")) { LogPrint("tor", "tor: Using NULL authentication\n"); - conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2)); + _conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2)); } else if (methods.count("SAFECOOKIE")) { // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie LogPrint("tor", "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); std::pair status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { - // conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2)); + // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2)); cookie = std::vector(status_cookie.second.begin(), status_cookie.second.end()); clientNonce = std::vector(TOR_NONCE_SIZE, 0); GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE); - conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2)); + _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2)); } else { if (status_cookie.first) { LogPrintf("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE); @@ -682,15 +682,15 @@ void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControl } } -void TorController::connected_cb(TorControlConnection& conn) +void TorController::connected_cb(TorControlConnection& _conn) { reconnect_timeout = RECONNECT_TIMEOUT_START; // First send a PROTOCOLINFO command to figure out what authentication is expected - if (!conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2))) + if (!_conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2))) LogPrintf("tor: Error sending initial protocolinfo command\n"); } -void TorController::disconnected_cb(TorControlConnection& conn) +void TorController::disconnected_cb(TorControlConnection& _conn) { // Stop advertizing service when disconnected if (service.IsValid()) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 1c53e9a46..2053de3ed 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -504,7 +504,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate); LOCK(cs); - list waitingOnDependants; + std::list waitingOnDependants; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; checkTotal += it->GetTxSize(); @@ -626,7 +626,7 @@ void CTxMemPool::checkNullifiers(ShieldedType type) const } } -void CTxMemPool::queryHashes(vector& vtxid) +void CTxMemPool::queryHashes(std::vector& vtxid) { vtxid.clear(); @@ -691,7 +691,7 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) return true; } -void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, double dPriorityDelta, const CAmount& nFeeDelta) +void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta) { { LOCK(cs); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 042dd00a8..390241fed 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -838,8 +838,8 @@ set CWallet::GetConflicts(const uint256& txid) const if (mapTxSpends.count(txin.prevout) <= 1) continue; // No conflict if zero or one spends range = mapTxSpends.equal_range(txin.prevout); - for (TxSpends::const_iterator it = range.first; it != range.second; ++it) - result.insert(it->second); + for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) + result.insert(_it->second); } std::pair range_n; @@ -2629,9 +2629,9 @@ int CWalletTx::GetRequestCount() const // Generated block if (!hashBlock.IsNull()) { - map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); - if (mi != pwallet->mapRequestCount.end()) - nRequests = (*mi).second; + map::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock); + if (_mi != pwallet->mapRequestCount.end()) + nRequests = (*_mi).second; } } else @@ -4389,17 +4389,17 @@ set< set > CWallet::GetAddressGroupings() set< set* > uniqueGroupings; // a set of pointers to groups of addresses map< CTxDestination, set* > setmap; // map addresses to the unique group containing it - for (set grouping : groupings) + for (set _grouping : groupings) { // make a set of all the groups hit by this new group set< set* > hits; map< CTxDestination, set* >::iterator it; - for (CTxDestination address : grouping) + for (CTxDestination address : _grouping) if ((it = setmap.find(address)) != setmap.end()) hits.insert((*it).second); // merge all hit groups into a new single group and delete old groups - set* merged = new set(grouping); + set* merged = new set(_grouping); for (set* hit : hits) { merged->insert(hit->begin(), hit->end()); From b441dc495201b4627bdc38e247f28ea27a34b00e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 12:58:42 +0000 Subject: [PATCH 089/146] rpc: Add tx version in 'gettransaction' --- src/wallet/rpcwallet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 7937316e4..e4b074438 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1136,6 +1136,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"status\" : \"mined|waiting|expiringsoon|expired\", (string) The transaction status, can be 'mined', 'waiting', 'expiringsoon' or 'expired'\n" + " \"version\" : \"x\", (string) The transaction version\n" " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"blockhash\" : \"hash\", (string) The block hash\n" @@ -1193,6 +1194,7 @@ UniValue gettransaction(const UniValue& params, bool fHelp) CAmount nNet = nCredit - nDebit; CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); + entry.pushKV("version", wtx.nVersion); entry.pushKV("amount", ValueFromAmount(nNet - nFee)); if (wtx.IsFromMe(filter)) entry.pushKV("fee", ValueFromAmount(nFee)); From af9f11e2615f65414def90aacc8ccc1b3c53be15 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 13:44:26 +0000 Subject: [PATCH 090/146] Refatoring utiltime.{cpp,h} --- src/net.cpp | 6 +++--- src/utiltime.cpp | 42 ++++++++++++++++++++++++++++-------------- src/utiltime.h | 14 +++++++++++++- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index f8677465e..5547cd486 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -811,7 +811,7 @@ void SocketSendData(CNode *pnode) nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); } if (nBytes > 0) { - pnode->nLastSend = GetTime(); + pnode->nLastSend = GetSystemTimeInSeconds(); { LOCK(pnode->cs_vSend); pnode->nSendBytes += nBytes; @@ -1325,7 +1325,7 @@ void ThreadSocketHandler() // // Inactivity checking // - int64_t nTime = GetTime(); + int64_t nTime = GetSystemTimeInSeconds(); if (nTime - pnode->nTimeConnected > 60) { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) @@ -2189,7 +2189,7 @@ unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAX CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), - nTimeConnected(GetTime()), + nTimeConnected(GetSystemTimeInSeconds()), addr(addrIn), nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)), addrKnown(5000, 0.001), diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 64a757726..f8319ffb1 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -9,42 +9,56 @@ #include "utiltime.h" -#include +#include + #include #include -using namespace std; - -static int64_t nMockTime = 0; //!< For unit testing +static std::atomic nMockTime(0); //!< For unit testing int64_t GetTime() { - if (nMockTime) return nMockTime; + int64_t mocktime = nMockTime.load(std::memory_order_relaxed); + if (mocktime) return mocktime; - return time(NULL); + time_t now = time(nullptr); + assert(now > 0); + return now; } void SetMockTime(int64_t nMockTimeIn) { - nMockTime = nMockTimeIn; + nMockTime.store(nMockTimeIn, std::memory_order_relaxed); +} + +int64_t GetMockTime() +{ + return nMockTime.load(std::memory_order_relaxed); } int64_t GetTimeMillis() { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = (boost::posix_time::microsec_clock::universal_time() - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); + assert(now > 0); + return now; } int64_t GetTimeMicros() { - return std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = (boost::posix_time::microsec_clock::universal_time() - + boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); + assert(now > 0); + return now; +} + +int64_t GetSystemTimeInSeconds() +{ + return GetTimeMicros()/1000000; } void MilliSleep(int64_t n) { - // This is defined to be an interruption point. - // boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); } diff --git a/src/utiltime.h b/src/utiltime.h index b1267112e..f4fd89755 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -9,10 +9,22 @@ #include #include +/** + * GetTimeMicros() and GetTimeMillis() both return the system time, but in + * different units. GetTime() returns the system time in seconds, but also + * supports mocktime, where the time can be specified by the user, eg for + * testing (eg with the setmocktime rpc, or -mocktime argument). + * + * TODO: Rework these functions to be type-safe (so that we don't inadvertently + * compare numbers with different units, or compare a mocktime to system time). + */ + int64_t GetTime(); int64_t GetTimeMillis(); int64_t GetTimeMicros(); +int64_t GetSystemTimeInSeconds(); // Like GetTime(), but not mockable void SetMockTime(int64_t nMockTimeIn); +int64_t GetMockTime(); void MilliSleep(int64_t n); std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); From 2aa382918bec5fe9725fd8abf31d71dd140cfc7d Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 13:50:39 +0000 Subject: [PATCH 091/146] Include algorithm.h in cuckoocache.h --- src/cuckoocache.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 33260a35a..14bcd98c6 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_CUCKOOCACHE_H #define BITCOIN_CUCKOOCACHE_H +#include #include #include #include From 1ee184292917b3ca8fdd72b764c80894c016f949 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 13:56:27 +0000 Subject: [PATCH 092/146] wallet: Remove lock on cs_main from CWallet::ChainTip --- src/wallet/wallet.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 390241fed..ceb8dc5fd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -621,8 +621,11 @@ void CWallet::ChainTip(const CBlockIndex *pindex, ChainTipAdded(pindex, pblock, added->first, added->second); // Prevent migration transactions from being created when node is syncing after launch, // and also when node wakes up from suspension/hibernation and incoming blocks are old. - if (!IsInitialBlockDownload(Params()) && - pblock->GetBlockTime() > GetTime() - 3 * 60 * 60) + // We do not call IsInitialBlockDownload() because during IBD that locks on cs_main, + // which we must not do during wallet sync. However, IBD does not end until the chain + // tip is within nMaxTipAge of the current time, so we use that as a proxy. + const int64_t hibernationOld = 3 * 60 * 60; + if (pblock->GetBlockTime() > GetTime() - std::min(nMaxTipAge, hibernationOld)) { RunSaplingMigration(pindex->nHeight); } From 5fded8555d1bfecb71703be604dc78629ec05231 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 13:58:37 +0000 Subject: [PATCH 093/146] Qualify `string` --- src/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index 0731d5a60..8c83383eb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1329,7 +1329,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) RegisterNodeSignals(GetNodeSignals()); // sanitize comments per BIP-0014, format user agent and check total size - std::vector uacomments; + std::vector uacomments; for (std::string cmt : mapMultiArgs["-uacomment"]) { if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) From 1c5e44ac6bddecab72d225ea75f12fff6507320a Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 14:04:41 +0000 Subject: [PATCH 094/146] Fix typos --- src/cuckoocache.h | 2 +- src/test/merkle_tests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 14bcd98c6..e34ddd18e 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -281,7 +281,7 @@ class cache * * First, epoch_check decrements and checks the cheap heuristic, and then does * a more expensive scan if the cheap heuristic runs out. If the expensive - * scan suceeds, the epochs are aged and old elements are allow_erased. The + * scan succeeds, the epochs are aged and old elements are allow_erased. The * cheap heuristic is reset to retrigger after the worst case growth of the * current epoch's elements would exceed the epoch_size. */ diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp index 7c074e378..bc2617c5f 100644 --- a/src/test/merkle_tests.cpp +++ b/src/test/merkle_tests.cpp @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(merkle_test) // If no mutation was done (once for every ntx value), try up to 16 branches. if (mutate == 0) { for (int loop = 0; loop < std::min(ntx, 16); loop++) { - // If ntx <= 16, try all branches. Otherise, try 16 random ones. + // If ntx <= 16, try all branches. Otherwise, try 16 random ones. int mtx = loop; if (ntx > 16) { mtx = insecure_rand() % ntx; From 0006019ff4765bb73719f10097e1a9cc18f2952a Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 15:19:55 +0000 Subject: [PATCH 095/146] Start with an empty banlist if -reindex is set --- src/net.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 5547cd486..c7297de22 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -417,15 +417,26 @@ static void DumpBanlist() int64_t nStart = GetTimeMillis(); - CBanDB bandb; - banmap_t banmap; - CNode::SetBannedSetDirty(false); - CNode::GetBanned(banmap); - if (!bandb.Write(banmap)) - CNode::SetBannedSetDirty(true); - - LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", - banmap.size(), GetTimeMillis() - nStart); + if (!GetBoolArg("-reindex", false)) { + CBanDB bandb; + banmap_t banmap; + if (bandb.Read(banmap)) { + CNode::SetBanned(banmap); // thread save setter + CNode::SetBannedSetDirty(false); // no need to write down, just read data + CNode::SweepBanned(); // sweep out unused entries + + LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); + } else { + LogPrintf("Invalid or missing banlist.dat; recreating\n"); + CNode::SetBannedSetDirty(true); // force write + DumpBanlist(); + } + } else { + LogPrintf("Clearing banlist.dat for reindex\n"); + CNode::SetBannedSetDirty(true); // force write + DumpBanlist(); + } } void CNode::CloseSocketDisconnect() From e74f6d71b9956a382feab886b5cdd60688cff9e7 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 15:37:40 +0000 Subject: [PATCH 096/146] Make -reindex and -reindex-chainstate imply -rescan --- doc/release-notes.md | 7 +++++++ src/init.cpp | 17 +++++++++++++++++ src/wallet/wallet.cpp | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index 17e3b4a86..aa77aa079 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -44,6 +44,13 @@ ensure that local time is set reasonably accurately. If it appears that the node has a significantly different time than its peers, a warning will still be logged and indicated on the metrics screen if enabled. +Option handling +--------------- + +- The `-reindex` and `-reindex-chainstate` options now imply `-rescan` + (provided that the wallet is enabled and pruning is disabled, and unless + `-rescan=0` is specified explicitly). + Fixes ----- diff --git a/src/init.cpp b/src/init.cpp index 8c83383eb..78e4832a4 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -350,8 +350,13 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-prune=", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode disables wallet support and is incompatible with -txindex. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " "(default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); +#ifdef ENABLE_WALLET + strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks (implies -rescan unless pruning or unless -rescan=0 is explicitly specified")); + strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk (implies -rescan unless pruning or unless -rescan=0 is explicitly specified)")); +#else strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); +#endif #ifndef WIN32 strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); #endif @@ -934,6 +939,8 @@ void InitParameterInteraction() } #ifdef ENABLE_WALLET + // -rescan only affects the wallet. + if (GetBoolArg("-salvagewallet", false)) { // Rewrite just private keys: rescan to find transactions if (SoftSetBoolArg("-rescan", true)) @@ -945,6 +952,16 @@ void InitParameterInteraction() if (SoftSetBoolArg("-rescan", true)) LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting -rescan=1\n", __func__); } + + if (GetBoolArg("-reindex", false) && !GetArg("-prune", 0)) { + if (SoftSetBoolArg("-rescan", true)) + LogPrintf("%s: parameter interaction: -reindex=1 and not pruning -> setting -rescan=1\n", __func__); + } + + if (GetBoolArg("-reindex-chainstate", false) && !GetArg("-prune", 0)) { + if (SoftSetBoolArg("-rescan", true)) + LogPrintf("%s: parameter interaction: -reindex-chainstate=1 and not pruning -> setting -rescan=1\n", __func__); + } #endif // disable walletbroadcast and whitelistrelay in blocksonly mode diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ceb8dc5fd..99f618e87 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4739,7 +4739,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); - strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); + strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup (implies -rescan)")); strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); From 58f9410af0e83e73ee792917b8e311b882f5cefb Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 16:01:39 +0000 Subject: [PATCH 097/146] Add mediantime to RPC results --- src/main.cpp | 9 +++++---- src/rpc/blockchain.cpp | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 067005880..e33f67f8a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -717,10 +717,11 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // IsFinalTx() with one more than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; - // Timestamps on the other hand don't get any special treatment, - // because we can't know what timestamp the next block will have, - // and there aren't timestamp applications where it matters. - // However this changes once median past time-locks are enforced: + // Require that time-locked transactions have nLockTime set to less than the + // median time of the previous block they're contained in. When the next + // block is created its previous block will be the current chain tip, so we + // use that to calculate the median time passed to IsFinalTx() if + // LOCKTIME_MEDIAN_TIME_PAST is set. const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? chainActive.Tip()->GetMedianTimePast() : GetTime(); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d01c8f9e2..d0451ce0a 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -115,6 +115,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex()); result.pushKV("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex()); result.pushKV("time", (int64_t)blockindex->nTime); + result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); result.pushKV("nonce", blockindex->nNonce.GetHex()); result.pushKV("solution", HexStr(blockindex->GetBlockHeader().nSolution)); result.pushKV("bits", strprintf("%08x", blockindex->nBits)); @@ -246,6 +247,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx } result.pushKV("tx", txs); result.pushKV("time", block.GetBlockTime()); + result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); result.pushKV("nonce", block.nNonce.GetHex()); result.pushKV("solution", HexStr(block.nSolution)); result.pushKV("bits", strprintf("%08x", block.nBits)); @@ -642,6 +644,7 @@ UniValue getblockheader(const UniValue& params, bool fHelp) " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" " \"finalsaplingroot\" : \"xxxx\", (string) The root of the Sapling commitment tree after applying this block\n" " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"mediantime\" : ttt, (numeric) The median block time expressed in seconds since epoch (Jan 1 1970 GMT)\n" " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" @@ -710,6 +713,7 @@ UniValue getblock(const UniValue& params, bool fHelp) " ,...\n" " ],\n" " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"mediantime\" : ttt, (numeric) The median block time expressed in seconds since epoch (Jan 1 1970 GMT)\n" " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" @@ -1001,6 +1005,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"mediantime\" : ttt, (numeric) The median block time of the current best block\n" " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" @@ -1043,6 +1048,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) obj.pushKV("blocks", (int)chainActive.Height()); obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()); + obj.pushKV("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast()); obj.pushKV("difficulty", (double)GetNetworkDifficulty()); obj.pushKV("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip())); obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex()); From 181a4fab0ada52332b2026a83970b6e03622e092 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 16:32:01 +0000 Subject: [PATCH 098/146] Fix banlist issue --- src/net.cpp | 51 +++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index c7297de22..e4cf73380 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -417,26 +417,15 @@ static void DumpBanlist() int64_t nStart = GetTimeMillis(); - if (!GetBoolArg("-reindex", false)) { - CBanDB bandb; - banmap_t banmap; - if (bandb.Read(banmap)) { - CNode::SetBanned(banmap); // thread save setter - CNode::SetBannedSetDirty(false); // no need to write down, just read data - CNode::SweepBanned(); // sweep out unused entries + CBanDB bandb; + banmap_t banmap; + CNode::SetBannedSetDirty(false); + CNode::GetBanned(banmap); + if (!bandb.Write(banmap)) + CNode::SetBannedSetDirty(true); - LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", - banmap.size(), GetTimeMillis() - nStart); - } else { - LogPrintf("Invalid or missing banlist.dat; recreating\n"); - CNode::SetBannedSetDirty(true); // force write - DumpBanlist(); - } - } else { - LogPrintf("Clearing banlist.dat for reindex\n"); - CNode::SetBannedSetDirty(true); // force write - DumpBanlist(); - } + LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); } void CNode::CloseSocketDisconnect() @@ -1894,17 +1883,23 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Loading banlist...")); // Load addresses from banlist.dat nStart = GetTimeMillis(); - CBanDB bandb; - banmap_t banmap; - if (bandb.Read(banmap)) { - CNode::SetBanned(banmap); // thread save setter - CNode::SetBannedSetDirty(false); // no need to write down, just read data - CNode::SweepBanned(); // sweep out unused entries + if (!GetBoolArg("-reindex", false)) { + CBanDB bandb; + banmap_t banmap; + if (bandb.Read(banmap)) { + CNode::SetBanned(banmap); // thread save setter + CNode::SetBannedSetDirty(false); // no need to write down, just read data + CNode::SweepBanned(); // sweep out unused entries - LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", - banmap.size(), GetTimeMillis() - nStart); + LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); + } else { + LogPrintf("Invalid or missing banlist.dat; recreating\n"); + CNode::SetBannedSetDirty(true); // force write + DumpBanlist(); + } } else { - LogPrintf("Invalid or missing banlist.dat; recreating\n"); + LogPrintf("Clearing banlist.dat for reindex\n"); CNode::SetBannedSetDirty(true); // force write DumpBanlist(); } From 7fecf7764c99b59c95322609a04714518f406de6 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Wed, 28 Aug 2024 22:51:30 +0000 Subject: [PATCH 099/146] Code cleanup --- src/chainparams.cpp | 6 ------ src/chainparams.h | 2 -- src/main.cpp | 40 +++------------------------------------- 3 files changed, 3 insertions(+), 45 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c9815d323..d371ae987 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -137,8 +137,6 @@ class CMainParams : public CChainParams { ( 159300, 30 ) // 30 minutes ( 364400, 5 ); // 5 minutes - vRollingCheckpointStartHeight = 364400; - genesis = CreateGenesisBlock( 1478403829, uint256S("0x000000000000000000000000000000000000000000000000000000000000021d"), @@ -401,8 +399,6 @@ class CTestNetParams : public CChainParams { ( 13999, 30 ) // 30 minutes ( 14000, 5 ); // 5 minutes - vRollingCheckpointStartHeight = 14000; - genesis = CreateGenesisBlock( 1479443947, uint256S("0x0000000000000000000000000000000000000000000000000000000000000013"), @@ -634,8 +630,6 @@ class CRegTestParams : public CChainParams { ( 159300, 30 ) // 30 minutes ( 364400, 5 ); // 5 minutes - vRollingCheckpointStartHeight = 364400; - genesis = CreateGenesisBlock( 1482971059, uint256S("0x0000000000000000000000000000000000000000000000000000000000000009"), diff --git a/src/chainparams.h b/src/chainparams.h index eaacb138c..d855c5ebc 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -130,7 +130,6 @@ class CChainParams /** Enforce coinbase consensus rule in regtest mode */ void SetRegTestCoinbaseMustBeShielded() { consensus.fCoinbaseMustBeShielded = true; } int GetFutureBlockTimeWindow(int height) const; - int GetRollingCheckpointStartHeight() const { return vRollingCheckpointStartHeight; } protected: CChainParams() {} @@ -166,7 +165,6 @@ class CChainParams CAmount nSproutValuePoolCheckpointBalance = 0; uint256 hashSproutValuePoolCheckpointBlock; bool fZIP209Enabled = false; - int vRollingCheckpointStartHeight; }; /** diff --git a/src/main.cpp b/src/main.cpp index e33f67f8a..70bdc87e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3066,7 +3066,6 @@ bool ChainIsFullyNotified() { static CBlockIndex* FindMostWorkChain() { do { CBlockIndex *pindexNew = NULL; - CBlockIndex *pindexOldTip = chainActive.Tip(); // Find the best candidate header. { @@ -3089,47 +3088,14 @@ static CBlockIndex* FindMostWorkChain() { // to a chain unless we have all the non-active-chain parent blocks. bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK; bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); - - bool fInvalidChain = false; - - // check last few blocks for reorg - const CChainParams& chainParams = Params(); - if (pindexOldTip != NULL && - pindexOldTip->nHeight > chainParams.GetRollingCheckpointStartHeight() && - !GetBoolArg("-disablereorgprotection", false)) - { - // check some last hash CHECK_REORG - int heightCheck = pindexOldTip->nHeight - DEFAULT_REORG_CHECK; - - const CBlockIndex *pindexOldTipCheck = FindBlockAtHeight(heightCheck, (const CBlockIndex*)pindexOldTip); - const CBlockIndex *pindexTestCheck = FindBlockAtHeight(heightCheck, (const CBlockIndex*)pindexTest); - if (pindexOldTipCheck->phashBlock != pindexTestCheck->phashBlock) { - auto msg = strprintf( - "Invalid block hash" - "\n\n") + - _("Block details") + ":\n" + - "- " + strprintf(_("Current tip: %s, height %d"), - pindexOldTipCheck->phashBlock->GetHex(), pindexOldTipCheck->nHeight) + "\n" + - "- " + strprintf(_("New tip: %s, height %d"), - pindexTestCheck->phashBlock->GetHex(), pindexTestCheck->nHeight) + "\n"; - LogPrintf("*** %s\n", msg); - fInvalidChain = true; - } else { - LogPrintf("Block hash is correct %s, height %d\n", - pindexOldTipCheck->phashBlock->GetHex(), pindexOldTipCheck->nHeight); - fInvalidChain = false; - } - } - - if (fFailedChain || fMissingData || fInvalidChain) { + if (fFailedChain || fMissingData) { // Candidate chain is not usable (either invalid or missing data) - if ((fFailedChain || fInvalidChain) - && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) + if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindexNew; CBlockIndex *pindexFailed = pindexNew; // Remove the entire chain from the set. while (pindexTest != pindexFailed) { - if (fFailedChain || fInvalidChain) { + if (fFailedChain) { pindexFailed->nStatus |= BLOCK_FAILED_CHILD; } else if (fMissingData) { // If we're missing data, then add back to mapBlocksUnlinked, From 2f697e54b296e70ddefd9e3a61c330e893bccee3 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 07:48:11 +0000 Subject: [PATCH 100/146] Remove some unused functions: - SetPort - GetMiscWarning - GetfLargeWorkInvalidChainFound --- src/netbase.cpp | 5 ----- src/netbase.h | 6 ------ src/warnings.cpp | 12 ------------ src/warnings.h | 2 -- 4 files changed, 25 deletions(-) diff --git a/src/netbase.cpp b/src/netbase.cpp index 1b02bac1d..6d1d856f5 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -1227,11 +1227,6 @@ std::string CService::ToString() const return ToStringIPPort(); } -void CService::SetPort(unsigned short portIn) -{ - port = portIn; -} - CSubNet::CSubNet(): valid(false) { diff --git a/src/netbase.h b/src/netbase.h index e66b17c16..920996630 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -24,11 +24,6 @@ static const int DEFAULT_CONNECT_TIMEOUT = 5000; //! -dns default static const int DEFAULT_NAME_LOOKUP = true; -#ifdef WIN32 -// In MSVC, this is defined as a macro, undefine it to prevent a compile and link error -#undef SetPort -#endif - enum Network { NET_UNROUTABLE = 0, @@ -158,7 +153,6 @@ class CService : public CNetAddr explicit CService(const std::string& strIpPort, int portDefault, bool fAllowLookup = false); explicit CService(const std::string& strIpPort, bool fAllowLookup = false); void Init(); - void SetPort(unsigned short portIn); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; bool SetSockAddr(const struct sockaddr* paddr); diff --git a/src/warnings.cpp b/src/warnings.cpp index 28e62010b..df61765f8 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -21,12 +21,6 @@ void SetMiscWarning(const std::string& strWarning) strMiscWarning = strWarning; } -std::string GetMiscWarning() -{ - LOCK(cs_warnings); - return strMiscWarning; -} - void SetfLargeWorkForkFound(bool flag) { LOCK(cs_warnings); @@ -45,12 +39,6 @@ void SetfLargeWorkInvalidChainFound(bool flag) fLargeWorkInvalidChainFound = flag; } -bool GetfLargeWorkInvalidChainFound() -{ - LOCK(cs_warnings); - return fLargeWorkInvalidChainFound; -} - std::string GetWarnings(const std::string& strFor) { int nPriority = 0; diff --git a/src/warnings.h b/src/warnings.h index 0b6482913..41770cd2e 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -10,11 +10,9 @@ #include void SetMiscWarning(const std::string& strWarning); -std::string GetMiscWarning(); void SetfLargeWorkForkFound(bool flag); bool GetfLargeWorkForkFound(); void SetfLargeWorkInvalidChainFound(bool flag); -bool GetfLargeWorkInvalidChainFound(); std::string GetWarnings(const std::string& strFor); static const bool DEFAULT_TESTSAFEMODE = false; From 78416db6e47f424f32dab895313ea4f493a2cb32 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 07:54:16 +0000 Subject: [PATCH 101/146] One-line comments for GetWarnings --- src/warnings.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/warnings.h b/src/warnings.h index 41770cd2e..f611e651b 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -13,6 +13,13 @@ void SetMiscWarning(const std::string& strWarning); void SetfLargeWorkForkFound(bool flag); bool GetfLargeWorkForkFound(); void SetfLargeWorkInvalidChainFound(bool flag); +/** Format a string that describes several potential problems detected by the core. + * strFor can have three values: + * - "rpc": get critical warnings, which should put the client in safe mode if non-empty + * - "statusbar": get all warnings + * - "gui": get all warnings, translated (where possible) for GUI + * This function only returns the highest priority warning of the set selected by strFor. + */ std::string GetWarnings(const std::string& strFor); static const bool DEFAULT_TESTSAFEMODE = false; From 80f2a5a26e57166f142e37ee8c42db7babc1f637 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 08:43:30 +0000 Subject: [PATCH 102/146] Remove p2p alert system --- doc/release-notes.md | 24 ++ src/Makefile.am | 3 - src/Makefile.test.include | 3 +- src/alert.cpp | 284 ------------------------ src/alert.h | 115 ---------- src/chainparams.cpp | 2 - src/chainparams.h | 3 - src/deprecation.cpp | 3 - src/init.cpp | 8 - src/main.cpp | 64 ++---- src/main.h | 3 - src/sendalert.cpp | 174 --------------- src/test/alert_tests.cpp | 416 ----------------------------------- src/test/data/alertTests.raw | Bin 2111 -> 0 bytes src/ui_interface.h | 5 +- src/warnings.cpp | 22 -- 16 files changed, 46 insertions(+), 1083 deletions(-) delete mode 100644 src/alert.cpp delete mode 100644 src/alert.h delete mode 100644 src/sendalert.cpp delete mode 100644 src/test/alert_tests.cpp delete mode 100644 src/test/data/alertTests.raw diff --git a/doc/release-notes.md b/doc/release-notes.md index aa77aa079..0c8b61edf 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -82,3 +82,27 @@ is not necessary. On Windows, do not forget to uninstall all earlier versions of the Bitcoin client first. + + +### RPC and REST + +### Configuration and command-line options + +### Block and transaction handling + +### P2P protocol and network code + +The p2p alert system has been removed in #7692 and the 'alert' message is no longer supported. + +### Validation + +### Build system + +### Wallet + +### GUI + +### Tests + +### Miscellaneous + diff --git a/src/Makefile.am b/src/Makefile.am index fdcd8a825..1daf6cdad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -95,7 +95,6 @@ BITCOIN_CORE_H = \ addrdb.h \ addressindex.h \ addrman.h \ - alert.h \ amount.h \ arith_uint256.h \ asyncrpcoperation.h \ @@ -226,10 +225,8 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_server_a_SOURCES = \ - sendalert.cpp \ addrman.cpp \ addrdb.cpp \ - alert.cpp \ alertkeys.h \ asyncrpcoperation.cpp \ asyncrpcqueue.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 191c3f349..1f8f67ac8 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -45,7 +45,7 @@ JSON_TEST_FILES = \ test/data/merkle_commitments_sapling.json \ test/data/sapling_key_components.json -RAW_TEST_FILES = test/data/alertTests.raw +RAW_TEST_FILES = GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) @@ -53,7 +53,6 @@ BITCOIN_TESTS =\ test/arith_uint256_tests.cpp \ test/scriptnum10.h \ test/addrman_tests.cpp \ - test/alert_tests.cpp \ test/allocator_tests.cpp \ test/base32_tests.cpp \ test/base58_tests.cpp \ diff --git a/src/alert.cpp b/src/alert.cpp deleted file mode 100644 index 9014a222e..000000000 --- a/src/alert.cpp +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#include "alert.h" - -#include "clientversion.h" -#include "net.h" -#include "pubkey.h" -#include "timedata.h" -#include "ui_interface.h" -#include "util.h" - -#include -#include -#include - -#include -#include -#include - -using namespace std; - -map mapAlerts; -CCriticalSection cs_mapAlerts; - -void CUnsignedAlert::SetNull() -{ - nVersion = 1; - nRelayUntil = 0; - nExpiration = 0; - nID = 0; - nCancel = 0; - setCancel.clear(); - nMinVer = 0; - nMaxVer = 0; - setSubVer.clear(); - nPriority = 0; - - strComment.clear(); - strStatusBar.clear(); - strRPCError.clear(); -} - -std::string CUnsignedAlert::ToString() const -{ - std::string strSetCancel; - for (int n : setCancel) - strSetCancel += strprintf("%d ", n); - std::string strSetSubVer; - for (const std::string& str : setSubVer) - strSetSubVer += "\"" + str + "\" "; - return strprintf( - "CAlert(\n" - " nVersion = %d\n" - " nRelayUntil = %d\n" - " nExpiration = %d\n" - " nID = %d\n" - " nCancel = %d\n" - " setCancel = %s\n" - " nMinVer = %d\n" - " nMaxVer = %d\n" - " setSubVer = %s\n" - " nPriority = %d\n" - " strComment = \"%s\"\n" - " strStatusBar = \"%s\"\n" - " strRPCError = \"%s\"\n" - ")\n", - nVersion, - nRelayUntil, - nExpiration, - nID, - nCancel, - strSetCancel, - nMinVer, - nMaxVer, - strSetSubVer, - nPriority, - strComment, - strStatusBar, - strRPCError); -} - -void CAlert::SetNull() -{ - CUnsignedAlert::SetNull(); - vchMsg.clear(); - vchSig.clear(); -} - -bool CAlert::IsNull() const -{ - return (nExpiration == 0); -} - -uint256 CAlert::GetHash() const -{ - return Hash(this->vchMsg.begin(), this->vchMsg.end()); -} - -bool CAlert::IsInEffect() const -{ - return (GetTime() < nExpiration); -} - -bool CAlert::Cancels(const CAlert& alert) const -{ - if (!IsInEffect()) - return false; // this was a no-op before 31403 - return (alert.nID <= nCancel || setCancel.count(alert.nID)); -} - -bool CAlert::AppliesTo(int nVersion, const std::string& strSubVerIn) const -{ - // Get a subversion without comments - std::string strSubVer = ""; - auto start = 0; - auto end = 0; - while (start < strSubVerIn.length() && end < strSubVerIn.length()) { - end = strSubVerIn.find('(', start); - if (end == std::string::npos) { - // Ensure we get the section of strSubVerIn after the final comment - end = strSubVerIn.length(); - } - strSubVer.append(strSubVerIn.substr(start, end - start)); - start = strSubVerIn.find(')', end); - if (start != std::string::npos) { - // Finish with start pointing at the next character we want - start += 1; - } - } - // Check against both the commented and uncommented subversion - // TODO: rework for client-version-embedded-in-strSubVer ? - return (IsInEffect() && - nMinVer <= nVersion && nVersion <= nMaxVer && - (setSubVer.empty() || setSubVer.count(strSubVerIn) || setSubVer.count(strSubVer))); -} - -bool CAlert::AppliesToMe() const -{ - return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); -} - -bool CAlert::RelayTo(CNode* pnode) const -{ - if (!IsInEffect()) - return false; - // don't relay to nodes which haven't sent their version message - if (pnode->nVersion == 0) - return false; - // returns true if wasn't already contained in the set - if (pnode->setKnown.insert(GetHash()).second) - { - if (AppliesTo(pnode->nVersion, pnode->strSubVer) || - AppliesToMe() || - GetTime() < nRelayUntil) - { - pnode->PushMessage("alert", *this); - return true; - } - } - return false; -} - -bool CAlert::CheckSignature(const std::vector& alertKey) const -{ - CPubKey key(alertKey); - if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) - return error("CAlert::CheckSignature(): verify signature failed"); - - // Now unserialize the data - CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); - sMsg >> *(CUnsignedAlert*)this; - return true; -} - -CAlert CAlert::getAlertByHash(const uint256 &hash) -{ - CAlert retval; - { - LOCK(cs_mapAlerts); - map::iterator mi = mapAlerts.find(hash); - if(mi != mapAlerts.end()) - retval = mi->second; - } - return retval; -} - -bool CAlert::ProcessAlert(const std::vector& alertKey, bool fThread) -{ - if (!CheckSignature(alertKey)) - return false; - if (!IsInEffect()) - return false; - - // alert.nID=max is reserved for if the alert key is - // compromised. It must have a pre-defined message, - // must never expire, must apply to all versions, - // and must cancel all previous - // alerts or it will be ignored (so an attacker can't - // send an "everything is OK, don't panic" version that - // cannot be overridden): - int maxInt = std::numeric_limits::max(); - if (nID == maxInt) - { - if (!( - nExpiration == maxInt && - nCancel == (maxInt-1) && - nMinVer == 0 && - nMaxVer == maxInt && - setSubVer.empty() && - nPriority == maxInt && - strStatusBar == "URGENT: Alert key compromised, upgrade required" - )) - return false; - } - - { - LOCK(cs_mapAlerts); - // Cancel previous alerts - for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) - { - const CAlert& alert = (*mi).second; - if (Cancels(alert)) - { - LogPrint("alert", "cancelling alert %d\n", alert.nID); - uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); - mapAlerts.erase(mi++); - } - else if (!alert.IsInEffect()) - { - LogPrint("alert", "expiring alert %d\n", alert.nID); - uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); - mapAlerts.erase(mi++); - } - else - mi++; - } - - // Check if this alert has been cancelled - for (std::pair& item : mapAlerts) - { - const CAlert& alert = item.second; - if (alert.Cancels(*this)) - { - LogPrint("alert", "alert already cancelled by %d\n", alert.nID); - return false; - } - } - - // Add to mapAlerts - mapAlerts.insert(make_pair(GetHash(), *this)); - // Notify UI and -alertnotify if it applies to me - if(AppliesToMe()) - { - uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); - Notify(strStatusBar, fThread); - } - } - - LogPrint("alert", "accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); - return true; -} - -void -CAlert::Notify(const std::string& strMessage, bool fThread) -{ - std::string strCmd = GetArg("-alertnotify", ""); - if (strCmd.empty()) return; - - // Alert text should be plain ascii coming from a trusted source, but to - // be safe we first strip anything not in safeChars, then add single quotes around - // the whole string before passing it to the shell: - std::string singleQuote("'"); - std::string safeStatus = SanitizeString(strMessage); - safeStatus = singleQuote+safeStatus+singleQuote; - boost::replace_all(strCmd, "%s", safeStatus); - - if (fThread) - boost::thread t(runCommand, strCmd); // thread runs free - else - runCommand(strCmd); -} diff --git a/src/alert.h b/src/alert.h deleted file mode 100644 index 8cc1165b1..000000000 --- a/src/alert.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2013 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -#ifndef BITCOIN_ALERT_H -#define BITCOIN_ALERT_H - -#include "serialize.h" -#include "sync.h" - -#include -#include -#include -#include - -/** Minimum alert priority for enabling safe mode. */ -static const int ALERT_PRIORITY_SAFE_MODE = 4000; - -class CAlert; -class CNode; -class uint256; - -extern std::map mapAlerts; -extern CCriticalSection cs_mapAlerts; - -/** Alerts are for notifying old versions if they become too obsolete and - * need to upgrade. The message is displayed in the status bar. - * Alert messages are broadcast as a vector of signed data. Unserializing may - * not read the entire buffer if the alert is for a newer version, but older - * versions can still relay the original data. - */ -class CUnsignedAlert -{ -public: - int nVersion; - int64_t nRelayUntil; // when newer nodes stop relaying to newer nodes - int64_t nExpiration; - int nID; - int nCancel; - std::set setCancel; - int nMinVer; // lowest version inclusive - int nMaxVer; // highest version inclusive - std::set setSubVer; // empty matches all - int nPriority; - - // Actions - std::string strComment; - std::string strStatusBar; - std::string strRPCError; - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); - READWRITE(nRelayUntil); - READWRITE(nExpiration); - READWRITE(nID); - READWRITE(nCancel); - READWRITE(setCancel); - READWRITE(nMinVer); - READWRITE(nMaxVer); - READWRITE(setSubVer); - READWRITE(nPriority); - - READWRITE(LIMITED_STRING(strComment, 65536)); - READWRITE(LIMITED_STRING(strStatusBar, 256)); - READWRITE(LIMITED_STRING(strRPCError, 256)); - } - - void SetNull(); - - std::string ToString() const; -}; - -/** An alert is a combination of a serialized CUnsignedAlert and a signature. */ -class CAlert : public CUnsignedAlert -{ -public: - std::vector vchMsg; - std::vector vchSig; - - CAlert() - { - SetNull(); - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(vchMsg); - READWRITE(vchSig); - } - - void SetNull(); - bool IsNull() const; - uint256 GetHash() const; - bool IsInEffect() const; - bool Cancels(const CAlert& alert) const; - bool AppliesTo(int nVersion, const std::string& strSubVerIn) const; - bool AppliesToMe() const; - bool RelayTo(CNode* pnode) const; - bool CheckSignature(const std::vector& alertKey) const; - bool ProcessAlert(const std::vector& alertKey, bool fThread = true); // fThread means run -alertnotify in a free-running thread - static void Notify(const std::string& strMessage, bool fThread); - - /* - * Get copy of (active) alert object by hash. Returns a null alert if it is not found. - */ - static CAlert getAlertByHash(const uint256 &hash); -}; - -#endif // BITCOIN_ALERT_H diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d371ae987..aeb22dc42 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -124,7 +124,6 @@ class CMainParams : public CChainParams { pchMessageStart[1] = 0xe9; pchMessageStart[2] = 0x27; pchMessageStart[3] = 0x64; - vAlertPubKey = ParseHex("0408f4f463e4018498cfdfc68e44ec92559be4202e2b342bddcab958cd16cdbc9f161a75002088de70e911672a42ffd89ad2024523f14da4bac8950db11b12c2f0"); nDefaultPort = 1989; nPruneAfterHeight = 100000; eh_epoch_1 = eh200_9; @@ -386,7 +385,6 @@ class CTestNetParams : public CChainParams { pchMessageStart[1] = 0x1a; pchMessageStart[2] = 0xf9; pchMessageStart[3] = 0xbf; - vAlertPubKey = ParseHex("04736ee9ddbbede6420b0cff0db9d95a98b89b4e9669a97801f66a2e43ea9a0b46ee3380c41d8270ab649f6fb64f5a3e79c87dce5b62824f2588f01893c91466ee"); nDefaultPort = 11989; nPruneAfterHeight = 1000; eh_epoch_1 = eh200_9; diff --git a/src/chainparams.h b/src/chainparams.h index d855c5ebc..8b9461842 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -86,7 +86,6 @@ class CChainParams const Consensus::Params& GetConsensus() const { return consensus; } const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } - const std::vector& AlertKey() const { return vAlertPubKey; } int GetDefaultPort() const { return nDefaultPort; } CAmount SproutValuePoolCheckpointHeight() const { return nSproutValuePoolCheckpointHeight; } @@ -135,8 +134,6 @@ class CChainParams Consensus::Params consensus; CMessageHeader::MessageStartChars pchMessageStart; - //! Raw pub key bytes for the broadcast alert signing key. - std::vector vAlertPubKey; int nDefaultPort = 0; uint64_t nPruneAfterHeight = 0; EHparameters eh_epoch_1 = eh200_9; diff --git a/src/deprecation.cpp b/src/deprecation.cpp index 8f8cfc9b0..79583a55b 100644 --- a/src/deprecation.cpp +++ b/src/deprecation.cpp @@ -4,7 +4,6 @@ #include "deprecation.h" -#include "alert.h" #include "clientversion.h" #include "init.h" #include "ui_interface.h" @@ -32,7 +31,6 @@ void EnforceNodeDeprecation(int nHeight, bool forceLogging, bool fThread) { DEPRECATION_HEIGHT) + " " + _("You should upgrade to the latest version of BitcoinZ."); LogPrintf("*** %s\n", msg); - CAlert::Notify(msg, fThread); uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); } StartShutdown(); @@ -42,7 +40,6 @@ void EnforceNodeDeprecation(int nHeight, bool forceLogging, bool fThread) { DEPRECATION_HEIGHT) + " " + _("You should upgrade to the latest version of BitcoinZ."); LogPrintf("*** %s\n", msg); - CAlert::Notify(msg, fThread); uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_WARNING); } } diff --git a/src/init.cpp b/src/init.cpp index 78e4832a4..82967064e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -68,8 +68,6 @@ using namespace boost::placeholders; -extern void ThreadSendAlert(); - bool fFeeEstimatesInitialized = false; static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; @@ -322,7 +320,6 @@ std::string HelpMessage(HelpMessageMode mode) std::string strUsage = HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); strUsage += HelpMessageOpt("-version", _("Print version and exit")); - strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS)); strUsage += HelpMessageOpt("-alertnotify=", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); if (showDebug) @@ -1187,8 +1184,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes); - fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS); - // Option to startup with mocktime set (used for regression testing): SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op @@ -1823,8 +1818,5 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif - // SENDALERT - threadGroup.create_thread(boost::bind(ThreadSendAlert)); - return !fRequestShutdown; } diff --git a/src/main.cpp b/src/main.cpp index 70bdc87e2..49ca00e02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,6 @@ #include "sodium.h" #include "addrman.h" -#include "alert.h" #include "arith_uint256.h" #include "chainparams.h" #include "checkpoints.h" @@ -80,7 +79,6 @@ bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; bool fCoinbaseEnforcedShieldingEnabled = true; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; -bool fAlerts = DEFAULT_ALERTS; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; std::optional expiryDeltaArg = std::nullopt; @@ -1764,6 +1762,23 @@ bool IsInitialBlockDownload(const CChainParams& chainParams) static CBlockIndex *pindexBestForkTip = NULL; static CBlockIndex *pindexBestForkBase = NULL; +static void AlertNotify(const std::string& strMessage) +{ + uiInterface.NotifyAlertChanged(); + std::string strCmd = GetArg("-alertnotify", ""); + if (strCmd.empty()) return; + + // Alert text should be plain ascii coming from a trusted source, but to + // be safe we first strip anything not in safeChars, then add single quotes around + // the whole string before passing it to the shell: + std::string singleQuote("'"); + std::string safeStatus = SanitizeString(strMessage); + safeStatus = singleQuote+safeStatus+singleQuote; + boost::replace_all(strCmd, "%s", safeStatus); + + boost::thread t(runCommand, strCmd); // thread runs free +} + void CheckForkWarningConditions(const CChainParams& chainParams) { AssertLockHeld(cs_main); @@ -1783,7 +1798,7 @@ void CheckForkWarningConditions(const CChainParams& chainParams) { std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + pindexBestForkBase->phashBlock->ToString() + std::string("'"); - CAlert::Notify(warning, true); + AlertNotify(warning); } if (pindexBestForkTip && pindexBestForkBase) { @@ -1794,9 +1809,7 @@ void CheckForkWarningConditions(const CChainParams& chainParams) } else { - std::string warning = std::string("Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely."); - LogPrintf("%s: %s\n", warning.c_str(), __func__); - CAlert::Notify(warning, true); + LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); SetfLargeWorkInvalidChainFound(true); } } @@ -5416,13 +5429,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - // Relay alerts - { - LOCK(cs_mapAlerts); - for (std::pair& item : mapAlerts) - item.second.RelayTo(pfrom); - } - pfrom->fSuccessfullyConnected = true; string remoteAddr; @@ -6096,38 +6102,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (fAlerts && strCommand == "alert") - { - CAlert alert; - vRecv >> alert; - - uint256 alertHash = alert.GetHash(); - if (pfrom->setKnown.count(alertHash) == 0) - { - if (alert.ProcessAlert(chainparams.AlertKey())) - { - // Relay - pfrom->setKnown.insert(alertHash); - { - LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - alert.RelayTo(pnode); - } - } - else { - // Small DoS penalty so peers that send us lots of - // duplicate/expired/invalid-signature/whatever alerts - // eventually get banned. - // This isn't a Misbehaving(100) (immediate ban) because the - // peer might be an older or different implementation with - // a different signature key, etc. - LOCK(cs_main); - Misbehaving(pfrom->GetId(), 10); - } - } - } - - else if (!(nLocalServices & NODE_BLOOM) && (strCommand == "filterload" || strCommand == "filteradd")) diff --git a/src/main.h b/src/main.h index ed4483a4d..b0d3a56d2 100644 --- a/src/main.h +++ b/src/main.h @@ -55,8 +55,6 @@ class PrecomputedTransactionData; struct CNodeStateStats; -/** Default for accepting alerts from the P2P network. */ -static const bool DEFAULT_ALERTS = true; /** Maximum reorg length we will accept before we shut down and alert the user. */ static const unsigned int MAX_REORG_LENGTH = COINBASE_MATURITY - 1; /** Default for DEFAULT_WHITELISTRELAY. */ @@ -184,7 +182,6 @@ extern size_t nCoinCacheUsage; extern CFeeRate minRelayTxFee; /** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */ extern CAmount maxTxFee; -extern bool fAlerts; /** If the tip is older than this (in seconds), the node is considered to be in initial block download. */ extern int64_t nMaxTipAge; diff --git a/src/sendalert.cpp b/src/sendalert.cpp deleted file mode 100644 index 8e5d9ed5e..000000000 --- a/src/sendalert.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2018-2020 The BitcoinZ Community -// Copyright (c) 2016-2018 The Zcash developers -// Original code from: https://gist.github.com/laanwj/0e689cfa37b52bcbbb44 - -/* - -To set up a new alert system ----------------------------- - -Create a new alert key pair: -openssl ecparam -name secp256k1 -genkey -param_enc explicit -outform PEM -out data.pem - -Get the private key in hex: -openssl ec -in data.pem -outform DER | tail -c 279 | xxd -p -c 279 - -Get the public key in hex: -openssl ec -in data.pem -pubout -outform DER | tail -c 65 | xxd -p -c 65 - -Update the public keys found in chainparams.cpp. - - -To send an alert message ------------------------- - -Copy the private keys into alertkeys.h. - -Modify the alert parameters, id and message found in this file. - -Build and run with -sendalert or -printalert. - -./bitcoinzd -printtoconsole -sendalert - -One minute after starting up, the alert will be broadcast. It is then -flooded through the network until the nRelayUntil time, and will be -active until nExpiration OR the alert is cancelled. - -If you make a mistake, send another alert with nCancel set to cancel -the bad alert. - -*/ - -#include "main.h" -#include "net.h" -#include "alert.h" -#include "init.h" - -#include "util.h" -#include "utiltime.h" -#include "key.h" -#include "clientversion.h" -#include "chainparams.h" - -#include "alertkeys.h" - - -static const int64_t DAYS = 24 * 60 * 60; - -void ThreadSendAlert() -{ - if (!mapArgs.count("-sendalert") && !mapArgs.count("-printalert")) - return; - - MilliSleep(60*1000); // Wait a minute so we get connected - - // - // Alerts are relayed around the network until nRelayUntil, flood - // filling to every node. - // After the relay time is past, new nodes are told about alerts - // when they connect to peers, until either nExpiration or - // the alert is cancelled by a newer alert. - // Nodes never save alerts to disk, they are in-memory-only. - // - CAlert alert; - alert.nRelayUntil = GetTime() + 15 * 60; - alert.nExpiration = GetTime() + 10 * 365 * 24 * 60 * 60; - alert.nID = 1006; // use https://github.com/zcash/zcash/wiki/specification#assigned-numbers to keep track of alert IDs - alert.nCancel = 1005; // cancels previous messages up to this ID number - - // These versions are protocol versions - // 170002 : 1.0.0 - // 170006 : 1.1.2 - // 170007 : 2.0.0 - // 170008 : 2.0.7 - alert.nMinVer = 170002; - alert.nMaxVer = 770008; - - // - // main.cpp: - // 1000 for Misc warnings like out of disk space and clock is wrong - // 2000 for longer invalid proof-of-work chain - // Higher numbers mean higher priority - // 4000 or higher will put the RPC into safe mode - alert.nPriority = 4000; - alert.strComment = ""; - alert.strStatusBar = "Your client is out of date and incompatible with the Sapling network upgrade. Please update to a recent version of BitcoinZ (2.0.1 or later)."; - alert.strRPCError = alert.strStatusBar; - - // Set specific client version/versions here. If setSubVer is empty, no filtering on subver is done: - // alert.setSubVer.insert(std::string("/MagicBean:0.7.2/")); - const std::vector useragents = {}; //{"MagicBean", "BeanStalk", "AppleSeed", "EleosZcash"}; - - for (const std::string& useragent : useragents) { - } - - // Sanity check - assert(alert.strComment.length() <= 65536); // max length in alert.h - assert(alert.strStatusBar.length() <= 256); - assert(alert.strRPCError.length() <= 256); - - // Sign - const CChainParams& chainparams = Params(); - std::string networkID = chainparams.NetworkIDString(); - bool fIsTestNet = networkID.compare("test") == 0; - std::vector vchTmp(ParseHex(fIsTestNet ? pszTestNetPrivKey : pszPrivKey)); - CPrivKey vchPrivKey(vchTmp.begin(), vchTmp.end()); - - CDataStream sMsg(SER_NETWORK, CLIENT_VERSION); - sMsg << *(CUnsignedAlert*)&alert; - alert.vchMsg = std::vector(sMsg.begin(), sMsg.end()); - CKey key; - if (!key.SetPrivKey(vchPrivKey, false)) - { - printf("ThreadSendAlert() : key.SetPrivKey failed\n"); - return; - } - if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) - { - printf("ThreadSendAlert() : key.Sign failed\n"); - return; - } - - // Test - CDataStream sBuffer(SER_NETWORK, CLIENT_VERSION); - sBuffer << alert; - CAlert alert2; - sBuffer >> alert2; - if (!alert2.CheckSignature(chainparams.AlertKey())) - { - printf("ThreadSendAlert() : CheckSignature failed\n"); - return; - } - assert(alert2.vchMsg == alert.vchMsg); - assert(alert2.vchSig == alert.vchSig); - alert.SetNull(); - printf("\nThreadSendAlert:\n"); - printf("hash=%s\n", alert2.GetHash().ToString().c_str()); - printf("%s\n", alert2.ToString().c_str()); - printf("vchMsg=%s\n", HexStr(alert2.vchMsg).c_str()); - printf("vchSig=%s\n", HexStr(alert2.vchSig).c_str()); - - // Confirm - if (!mapArgs.count("-sendalert")) - return; - while (vNodes.size() < 1 && !ShutdownRequested()) - MilliSleep(500); - if (ShutdownRequested()) - return; - - // Send - printf("ThreadSendAlert() : Sending alert\n"); - int nSent = 0; - { - LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - { - if (alert2.RelayTo(pnode)) - { - printf("ThreadSendAlert() : Sent alert to %s\n", pnode->addr.ToString().c_str()); - nSent++; - } - } - } - printf("ThreadSendAlert() : Alert sent to %d nodes\n", nSent); -} diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp deleted file mode 100644 index ea3ab9c45..000000000 --- a/src/test/alert_tests.cpp +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright (c) 2013 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or https://www.opensource.org/licenses/mit-license.php . - -// -// Unit tests for alert system -// - -#include "alert.h" -#include "chain.h" -#include "chainparams.h" -#include "clientversion.h" -#include "fs.h" -#include "data/alertTests.raw.h" - -#include "main.h" -#include "rpc/protocol.h" -#include "rpc/server.h" -#include "serialize.h" -#include "streams.h" -#include "utilstrencodings.h" -#include "utiltest.h" -#include "warnings.h" - -#include "test/test_bitcoin.h" - -#include - -#include - -#include "key.h" -#include "alertkeys.h" -#include - -/* - * If the alert key pairs have changed, the test suite will fail as the - * test data is now invalid. To create valid test data, signed with a - * new alert private key, follow these steps: - * - * 1. Copy your private key into alertkeys.h. Don't commit this file! - * See sendalert.cpp for more info. - * - * 2. Set the GENERATE_ALERTS_FLAG to true. - * - * 3. Build and run: - * test_bitcoinz -t Generate_Alert_Test_Data - * - * 4. Test data is saved in your current directory as alertTests.raw.NEW - * Copy this file to: src/test/data/alertTests.raw - * - * For debugging purposes, terminal output can be copied into: - * src/test/data/alertTests.raw.h - * - * 5. Clean up... - * - Set GENERATE_ALERTS_FLAG back to false. - * - Remove your private key from alertkeys.h - * - * 6. Build and verify the new test data: - * test_bitcoinz -t Alert_tests - * - */ -#define GENERATE_ALERTS_FLAG false - -#if GENERATE_ALERTS_FLAG - -// NOTE: -// A function SignAndSave() was used by Bitcoin Core to create alert test data -// but it has not been made publicly available. So instead, we have adapted -// some publicly available code which achieves the intended result: -// https://gist.github.com/lukem512/9b272bd35e2cdefbf386 - - -// Code to output a C-style array of values -template -std::string HexStrArray(const T itbegin, const T itend, int lineLength) -{ - std::string rv; - static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - rv.reserve((itend-itbegin)*3); - int i = 0; - for(T it = itbegin; it < itend; ++it) - { - unsigned char val = (unsigned char)(*it); - if(it != itbegin) - { - if (i % lineLength == 0) - rv.push_back('\n'); - else - rv.push_back(' '); - } - rv.push_back('0'); - rv.push_back('x'); - rv.push_back(hexmap[val>>4]); - rv.push_back(hexmap[val&15]); - rv.push_back(','); - i++; - } - - return rv; -} - -template -inline std::string HexStrArray(const T& vch, int lineLength) -{ - return HexStrArray(vch.begin(), vch.end(), lineLength); -} - - -// Sign CAlert with alert private key -bool SignAlert(CAlert &alert) -{ - // serialize alert data - CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); - sMsg << *(CUnsignedAlert*)&alert; - alert.vchMsg = std::vector(sMsg.begin(), sMsg.end()); - - // sign alert - std::vector vchTmp(ParseHex(pszPrivKey)); - CPrivKey vchPrivKey(vchTmp.begin(), vchTmp.end()); - CKey key; - if (!key.SetPrivKey(vchPrivKey, false)) - { - printf("key.SetPrivKey failed\n"); - return false; - } - if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) - { - printf("SignAlert() : key.Sign failed\n"); - return false; - } - return true; -} - -// Sign a CAlert and serialize it -bool SignAndSerialize(CAlert &alert, CDataStream &buffer) -{ - // Sign - if(!SignAlert(alert)) - { - printf("SignAndSerialize() : could not sign alert\n"); - return false; - } - // ...and save! - buffer << alert; - return true; -} - -void GenerateAlertTests() -{ - CDataStream sBuffer(SER_DISK, CLIENT_VERSION); - - CAlert alert; - alert.nRelayUntil = 60; - alert.nExpiration = 24 * 60 * 60; - alert.nID = 1; - alert.nCancel = 0; // cancels previous messages up to this ID number - alert.nMinVer = 0; // These versions are protocol versions - alert.nMaxVer = 999001; - alert.nPriority = 1; - alert.strComment = "Alert comment"; - alert.strStatusBar = "Alert 1"; - - // Replace SignAndSave with SignAndSerialize - SignAndSerialize(alert, sBuffer); - - // More tests go here ... - alert.setSubVer.insert(std::string("/MagicBean:0.1.0/")); - alert.strStatusBar = "Alert 1 for MagicBean 0.1.0"; - SignAndSerialize(alert, sBuffer); - - alert.setSubVer.insert(std::string("/MagicBean:0.2.0/")); - alert.strStatusBar = "Alert 1 for MagicBean 0.1.0, 0.2.0"; - SignAndSerialize(alert, sBuffer); - - alert.setSubVer.insert(std::string("/MagicBean:0.2.1(foo)/")); - alert.strStatusBar = "Alert 1 for MagicBean 0.1.0, 0.2.0, 0.2.1(foo)"; - SignAndSerialize(alert, sBuffer); - - alert.setSubVer.insert(std::string("/MagicBean:0.2.1/")); - alert.strStatusBar = "Alert 1 for MagicBean 0.1.0, 0.2.0, 0.2.1(foo), 0.2.1"; - SignAndSerialize(alert, sBuffer); - - alert.setSubVer.clear(); - ++alert.nID; - alert.nCancel = 1; - alert.nPriority = 100; - alert.strStatusBar = "Alert 2, cancels 1"; - SignAndSerialize(alert, sBuffer); - - alert.nExpiration += 60; - ++alert.nID; - SignAndSerialize(alert, sBuffer); - - ++alert.nID; - alert.nPriority = 5000; - alert.strStatusBar = "Alert 3, disables RPC"; - alert.strRPCError = "RPC disabled"; - SignAndSerialize(alert, sBuffer); - - ++alert.nID; - alert.nPriority = 5000; - alert.strStatusBar = "Alert 4, re-enables RPC"; - alert.strRPCError = ""; - SignAndSerialize(alert, sBuffer); - - ++alert.nID; - alert.nMinVer = 11; - alert.nMaxVer = 22; - alert.nPriority = 100; - SignAndSerialize(alert, sBuffer); - - ++alert.nID; - alert.strStatusBar = "Alert 2 for MagicBean 0.1.0"; - alert.setSubVer.insert(std::string("/MagicBean:0.1.0/")); - SignAndSerialize(alert, sBuffer); - - ++alert.nID; - alert.nMinVer = 0; - alert.nMaxVer = 999999; - alert.strStatusBar = "Evil Alert'; /bin/ls; echo '"; - alert.setSubVer.clear(); - bool b = SignAndSerialize(alert, sBuffer); - - if (b) { - // Print the hex array, which will become the contents of alertTest.raw.h - std::vector vch = std::vector(sBuffer.begin(), sBuffer.end()); - printf("%s\n", HexStrArray(vch, 8).c_str()); - - // Write the data to alertTests.raw.NEW, to be copied to src/test/data/alertTests.raw - std::ofstream outfile("alertTests.raw.NEW", std::ios::out | std::ios::binary); - outfile.write((const char*)&vch[0], vch.size()); - outfile.close(); - } -} - - - -struct GenerateAlertTestsFixture : public TestingSetup { - GenerateAlertTestsFixture() {} - ~GenerateAlertTestsFixture() {} -}; - -BOOST_FIXTURE_TEST_SUITE(Generate_Alert_Test_Data, GenerateAlertTestsFixture); -BOOST_AUTO_TEST_CASE(GenerateTheAlertTests) -{ - GenerateAlertTests(); -} -BOOST_AUTO_TEST_SUITE_END() - - -#else - - -struct ReadAlerts : public TestingSetup -{ - ReadAlerts() - { - std::vector vch(alert_tests::alertTests, alert_tests::alertTests + sizeof(alert_tests::alertTests)); - CDataStream stream(vch, SER_DISK, CLIENT_VERSION); - try { - while (!stream.eof()) - { - CAlert alert; - stream >> alert; - alerts.push_back(alert); - } - } - catch (const std::exception&) { } - } - ~ReadAlerts() { } - - static std::vector read_lines(fs::path filepath) - { - std::vector result; - - std::ifstream f(filepath.string().c_str()); - std::string line; - while (std::getline(f,line)) - result.push_back(line); - - return result; - } - - std::vector alerts; -}; - -BOOST_FIXTURE_TEST_SUITE(Alert_tests, ReadAlerts) - - -BOOST_AUTO_TEST_CASE(AlertApplies) -{ - SetMockTime(11); - const std::vector& alertKey = Params(CBaseChainParams::MAIN).AlertKey(); - - for (const CAlert& alert : alerts) - { - BOOST_CHECK(alert.CheckSignature(alertKey)); - } - - BOOST_CHECK(alerts.size() >= 3); - - // Matches: - BOOST_CHECK(alerts[0].AppliesTo(1, "")); - BOOST_CHECK(alerts[0].AppliesTo(999001, "")); - BOOST_CHECK(alerts[0].AppliesTo(1, "/MagicBean:11.11.11/")); - - BOOST_CHECK(alerts[1].AppliesTo(1, "/MagicBean:0.1.0/")); - BOOST_CHECK(alerts[1].AppliesTo(999001, "/MagicBean:0.1.0/")); - - BOOST_CHECK(alerts[2].AppliesTo(1, "/MagicBean:0.1.0/")); - BOOST_CHECK(alerts[2].AppliesTo(1, "/MagicBean:0.2.0/")); - BOOST_CHECK(alerts[2].AppliesTo(1, "/MagicBean:0.2.0(foo)/")); - BOOST_CHECK(alerts[2].AppliesTo(1, "/MagicBean:0.2.0(bar)/")); - - BOOST_CHECK(alerts[3].AppliesTo(1, "/MagicBean:0.1.0/")); - BOOST_CHECK(alerts[3].AppliesTo(1, "/MagicBean:0.2.0/")); - BOOST_CHECK(alerts[2].AppliesTo(1, "/MagicBean:0.2.0(foo)/")); - BOOST_CHECK(alerts[3].AppliesTo(1, "/MagicBean:0.2.1(foo)/")); - - BOOST_CHECK(alerts[4].AppliesTo(1, "/MagicBean:0.1.0/")); - BOOST_CHECK(alerts[4].AppliesTo(1, "/MagicBean:0.2.0/")); - BOOST_CHECK(alerts[4].AppliesTo(1, "/MagicBean:0.2.1(foo)/")); - BOOST_CHECK(alerts[4].AppliesTo(1, "/MagicBean:0.2.1/")); - - // Don't match: - BOOST_CHECK(!alerts[0].AppliesTo(-1, "")); - BOOST_CHECK(!alerts[0].AppliesTo(999002, "")); - - BOOST_CHECK(!alerts[1].AppliesTo(1, "")); - BOOST_CHECK(!alerts[1].AppliesTo(1, "MagicBean:0.1.0")); - BOOST_CHECK(!alerts[1].AppliesTo(1, "/MagicBean:0.1.0")); - BOOST_CHECK(!alerts[1].AppliesTo(1, "MagicBean:0.1.0/")); - BOOST_CHECK(!alerts[1].AppliesTo(-1, "/MagicBean:0.1.0/")); - BOOST_CHECK(!alerts[1].AppliesTo(999002, "/MagicBean:0.1.0/")); - BOOST_CHECK(!alerts[1].AppliesTo(1, "/MagicBean:0.1.0/FlowerPot:0.0.1/")); - BOOST_CHECK(!alerts[1].AppliesTo(1, "/MagicBean:0.2.0/")); - - // SubVer with comment doesn't match SubVer pattern without - BOOST_CHECK(!alerts[2].AppliesTo(1, "/MagicBean:0.2.1/")); - BOOST_CHECK(!alerts[2].AppliesTo(1, "/MagicBean:0.3.0/")); - - // SubVer without comment doesn't match SubVer pattern with - BOOST_CHECK(!alerts[3].AppliesTo(1, "/MagicBean:0.2.1/")); - - SetMockTime(0); -} - - -BOOST_AUTO_TEST_CASE(AlertNotify) -{ - SetMockTime(11); - const std::vector& alertKey = Params(CBaseChainParams::MAIN).AlertKey(); - - fs::path temp = fs::temp_directory_path() / - fs::unique_path("alertnotify-%%%%.txt"); - - mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string(); - - for (CAlert alert : alerts) - alert.ProcessAlert(alertKey, false); - - std::vector r = read_lines(temp); - BOOST_CHECK_EQUAL(r.size(), 6u); - -// Windows built-in echo semantics are different than posixy shells. Quotes and -// whitespace are printed literally. - -#ifndef WIN32 - BOOST_CHECK_EQUAL(r[0], "Alert 1"); - BOOST_CHECK_EQUAL(r[1], "Alert 2, cancels 1"); - BOOST_CHECK_EQUAL(r[2], "Alert 2, cancels 1"); - BOOST_CHECK_EQUAL(r[3], "Alert 3, disables RPC"); - BOOST_CHECK_EQUAL(r[4], "Alert 4, reenables RPC"); // dashes should be removed - BOOST_CHECK_EQUAL(r[5], "Evil Alert; /bin/ls; echo "); // single-quotes should be removed -#else - BOOST_CHECK_EQUAL(r[0], "'Alert 1' "); - BOOST_CHECK_EQUAL(r[1], "'Alert 2, cancels 1' "); - BOOST_CHECK_EQUAL(r[2], "'Alert 2, cancels 1' "); - BOOST_CHECK_EQUAL(r[3], "'Alert 3, disables RPC' "); - BOOST_CHECK_EQUAL(r[4], "'Alert 4, reenables RPC' "); // dashes should be removed - BOOST_CHECK_EQUAL(r[5], "'Evil Alert; /bin/ls; echo ' "); -#endif - fs::remove(temp); - - SetMockTime(0); - mapAlerts.clear(); -} - -BOOST_AUTO_TEST_CASE(AlertDisablesRPC) -{ - SetMockTime(11); - const std::vector& alertKey = Params(CBaseChainParams::MAIN).AlertKey(); - - // Command should work before alerts - BOOST_CHECK_EQUAL(GetWarnings("rpc"), ""); - - // First alert should disable RPC - alerts[7].ProcessAlert(alertKey, false); - BOOST_CHECK_EQUAL(alerts[7].strRPCError, "RPC disabled"); - BOOST_CHECK_EQUAL(GetWarnings("rpc"), "RPC disabled"); - - // Second alert should re-enable RPC - alerts[8].ProcessAlert(alertKey, false); - BOOST_CHECK_EQUAL(alerts[8].strRPCError, ""); - BOOST_CHECK_EQUAL(GetWarnings("rpc"), ""); - - SetMockTime(0); - mapAlerts.clear(); -} - -static bool falseFunc(const CChainParams&) { return false; } - -BOOST_AUTO_TEST_SUITE_END() - -#endif diff --git a/src/test/data/alertTests.raw b/src/test/data/alertTests.raw deleted file mode 100644 index 72e2785301cd631000ec1fc062dcea7c97af7e3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2111 zcmZ={WME*h0b&qn2n6vM7$78=$-oe4#}5(Vb<9aEDp5$z&&^HED`AK53>n-FT$vOZ zp8pq=4pBUr#~`nAygHg=X;y_N|NM^HU#r=g^7(R-f~`KMv^|^Cta4RnZ8}jdVHDK&O-#>Bc1lgmvog>#)HBcr=|gp!G}LVhY57G8 zaODbMD(di+z0(*q6H`q3M8AEXKO?j})HPRFmPtWCWJBT1 zl=MA|mFC7iUuK{5Pu*M4ev)bOvU58+jr>0qG!W%nCQRp|iWp(_fD#@L=m7lz)CCRq z3&Jg@&r7w;%-_ZT&HSHx`Gl0q-;cYj@H~>^XS46h&lDyF+v_2dKU<0?KV6`lV&$M& zupvrVChEz_7Y+x)CyLbf2`?tfBh0AI28BDS2*@L1C~;$?XQ+{upRb7}g7gUc58_RT z36MCGxm@yf-Y#p~>y^?Whlsp{RkcPQFhHKwrh(C?#v?U)p5-_3cbbH;S~idj0- z|1fj~S!>3r1%02AF6Jjc`~05Z^?Qi&DhsN|Nb)MGR$vHV@w_Q%o`(hmBn(*kB0WC! zJG|~?5${~SM$PuD@`aP%mJ|y+EtAQ59(Kx~Nx|vRr}_5|*(Exi_mSM~`egT7Zf4a# zK{iv@8f={y_(%DJ54JF1LeBKy44;CLse~ZTHquc@PRvVA%_&xZ<^5C2zUr3!a)v2| zNmG?C{AgnKYf;*m^CiDDeCyT3<&qgp3Ud4&l_`a9RMnQOzOky)F+1xna1j+CIKSX?EZe0E4n}N z(ZBl^vZ9of726wJ~)6oqT4PMuy;kf)(B{C$bXZ zL~)1{O>`8BQgu`F;EseOGOd>%J&d-+`OmMNGAFbB{bFltmo2}p2)z3JN3G(Gg`z}FZ!h?Ivfb>Nz;u=l+lR{(B%F@agfn4v7#qxC+(3pH5HnyVGMo;B z)G~9~n!ha$wVw0T-!6%5fy~0bm?noooIVJ_~vLif(w{O_3YyO(F+0XZug;S~!|CGOX zq9niX`ybua9EL3wa=_dR3PgJ+VEv5gIvLlp%p3)9JgZwP=qF|7>E{$%E2Ji8&?G^y3x3fNx^dW$J|BI{Ze_UdA)P| XPItHDa5rd|uTfvGA*pg>sUIT%|L2AE diff --git a/src/ui_interface.h b/src/ui_interface.h index b94a65fa8..244c92c97 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -86,10 +86,9 @@ class CClientUIInterface boost::signals2::signal NotifyNumConnectionsChanged; /** - * New, updated or cancelled alert. - * @note called with lock cs_mapAlerts held. + * Status bar alerts changed. */ - boost::signals2::signal NotifyAlertChanged; + boost::signals2::signal NotifyAlertChanged; /** A wallet has been loaded. */ boost::signals2::signal LoadWallet; diff --git a/src/warnings.cpp b/src/warnings.cpp index df61765f8..c46121222 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -7,7 +7,6 @@ #include "clientversion.h" #include "util.h" #include "warnings.h" -#include "alert.h" #include "uint256.h" CCriticalSection cs_warnings; @@ -41,7 +40,6 @@ void SetfLargeWorkInvalidChainFound(bool flag) std::string GetWarnings(const std::string& strFor) { - int nPriority = 0; std::string strStatusBar; std::string strRPC; @@ -56,38 +54,18 @@ std::string GetWarnings(const std::string& strFor) // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { - nPriority = 1000; strStatusBar = strMiscWarning; } if (fLargeWorkForkFound) { - nPriority = 2000; strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); } else if (fLargeWorkInvalidChainFound) { - nPriority = 2000; strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); } - // Alerts - { - LOCK(cs_mapAlerts); - for (const std::pair& item : mapAlerts) - { - const CAlert& alert = item.second; - if (alert.AppliesToMe() && alert.nPriority > nPriority) - { - nPriority = alert.nPriority; - strStatusBar = alert.strStatusBar; - if (alert.nPriority >= ALERT_PRIORITY_SAFE_MODE) { - strRPC = alert.strRPCError; - } - } - } - } - if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") From d728bab5f9e6dfdd8bec20d85dec64fc7aad0901 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 08:48:11 +0000 Subject: [PATCH 103/146] rpc: Don't translate warning messages --- src/main.h | 7 ++++++- src/warnings.cpp | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main.h b/src/main.h index b0d3a56d2..ba9c82342 100644 --- a/src/main.h +++ b/src/main.h @@ -260,7 +260,12 @@ bool SendMessages(CNode* pto); void ThreadScriptCheck(); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(const CChainParams& chainParams); -/** Format a string that describes several potential problems detected by the core */ +/** Format a string that describes several potential problems detected by the core. + * strFor can have three values: + * - "rpc": get critical warnings, which should put the client in safe mode if non-empty + * - "statusbar": get all warnings + * This function only returns the highest priority warning of the set selected by strFor. + */ std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); diff --git a/src/warnings.cpp b/src/warnings.cpp index c46121222..785a8264e 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -7,7 +7,6 @@ #include "clientversion.h" #include "util.h" #include "warnings.h" -#include "uint256.h" CCriticalSection cs_warnings; std::string strMiscWarning; @@ -45,8 +44,9 @@ std::string GetWarnings(const std::string& strFor) LOCK(cs_warnings); - if (!CLIENT_VERSION_IS_RELEASE) - strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); + if (!CLIENT_VERSION_IS_RELEASE) { + strStatusBar = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"; + } if (GetBoolArg("-testsafemode", DEFAULT_TESTSAFEMODE)) strStatusBar = strRPC = "testsafemode enabled"; @@ -59,11 +59,11 @@ std::string GetWarnings(const std::string& strFor) if (fLargeWorkForkFound) { - strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); + strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."; } else if (fLargeWorkInvalidChainFound) { - strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); + strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; } if (strFor == "statusbar") From a69bf1f09024e1570e2b38560f913e60bdba3077 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 08:56:58 +0000 Subject: [PATCH 104/146] Remove unused constants --- src/consensus/validation.h | 1 - src/version.h | 6 ------ 2 files changed, 7 deletions(-) diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 0ad7701b1..65fc36553 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -14,7 +14,6 @@ static const unsigned char REJECT_INVALID = 0x10; static const unsigned char REJECT_OBSOLETE = 0x11; static const unsigned char REJECT_DUPLICATE = 0x12; static const unsigned char REJECT_NONSTANDARD = 0x40; -static const unsigned char REJECT_DUST = 0x41; static const unsigned char REJECT_INSUFFICIENTFEE = 0x42; static const unsigned char REJECT_CHECKPOINT = 0x43; diff --git a/src/version.h b/src/version.h index 235b1a94d..0a6c93f5c 100644 --- a/src/version.h +++ b/src/version.h @@ -14,9 +14,6 @@ static const int PROTOCOL_VERSION = 770009; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; -//! In this version, 'getheaders' was introduced. -static const int GETHEADERS_VERSION = 31800; - //! disconnect from peers older than this proto version static const int MIN_PEER_PROTO_VERSION = 770007; @@ -27,9 +24,6 @@ static const int CADDR_TIME_VERSION = 31402; //! BIP 0031, pong message, is enabled for all versions AFTER this one static const int BIP0031_VERSION = 60000; -//! "mempool" command, enhanced "getdata" behavior starts with this version -static const int MEMPOOL_GD_VERSION = 60002; - //! "filter*" commands are disabled without NODE_BLOOM after and including this version static const int NO_BLOOM_VERSION = 170004; From 4c9216bd74221eadc9324579f3633410e8faf9c0 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 10:14:54 +0000 Subject: [PATCH 105/146] Direct headers announcement --- doc/bips.md | 1 + doc/release-notes.md | 9 + qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/sendheaders.py | 519 ++++++++++++++++++++++++ qa/rpc-tests/test_framework/mininode.py | 34 +- src/chain.cpp | 4 +- src/main.cpp | 221 +++++++++- src/main.h | 3 + src/net.h | 9 + src/version.h | 3 + 10 files changed, 790 insertions(+), 14 deletions(-) create mode 100755 qa/rpc-tests/sendheaders.py diff --git a/doc/bips.md b/doc/bips.md index 14c7e372f..10f9936e9 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -2,3 +2,4 @@ BIPs that are implemented by Zcash (up-to-date up to **v1.1.0**): * Numerous historic BIPs were present in **v1.0.0** at launch; see [the protocol spec](https://github.com/zcash/zips/blob/master/protocol/protocol.pdf) for details. * [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, but only enforced for peer versions `>=170004` as of **v1.1.0** ([PR #2814](https://github.com/zcash/zcash/pull/2814)). +* [`BIP 130`](https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki): direct headers announcement is negotiated with peer versions `>=770009` as of **v0.12.0** ([PR 6494](https://github.com/bitcoin/bitcoin/pull/6494)). diff --git a/doc/release-notes.md b/doc/release-notes.md index 0c8b61edf..a475c7e8b 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -94,6 +94,15 @@ client first. The p2p alert system has been removed in #7692 and the 'alert' message is no longer supported. +Direct headers announcement (BIP 130) +------------------------------------- + +Between compatible peers, BIP 130 direct headers announcement is used. This +means that blocks are advertized by announcing their headers directly, instead +of just announcing the hash. In a reorganization, all new headers are sent, +instead of just the new tip. This can often prevent an extra roundtrip before +the actual block is downloaded. + ### Validation ### Build system diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index e689be9c1..a316d2009 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -62,6 +62,7 @@ testScripts=( 'decodescript.py' 'blockchain.py' 'disablewallet.py' + 'sendheaders.py', 'zcjoinsplit.py' 'zcjoinsplitdoublespend.py' 'zkey_import_export.py' diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py new file mode 100755 index 000000000..d7f429209 --- /dev/null +++ b/qa/rpc-tests/sendheaders.py @@ -0,0 +1,519 @@ +#!/usr/bin/env python2 +# +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import time +from test_framework.blocktools import create_block, create_coinbase + +''' +SendHeadersTest -- test behavior of headers messages to announce blocks. + +Setup: + +- Two nodes, two p2p connections to node0. One p2p connection should only ever + receive inv's (omitted from testing description below, this is our control). + Second node is used for creating reorgs. + +Part 1: No headers announcements before "sendheaders" +a. node mines a block [expect: inv] + send getdata for the block [expect: block] +b. node mines another block [expect: inv] + send getheaders and getdata [expect: headers, then block] +c. node mines another block [expect: inv] + peer mines a block, announces with header [expect: getdata] +d. node mines another block [expect: inv] + +Part 2: After "sendheaders", headers announcements should generally work. +a. peer sends sendheaders [expect: no response] + peer sends getheaders with current tip [expect: no response] +b. node mines a block [expect: tip header] +c. for N in 1, ..., 10: + * for announce-type in {inv, header} + - peer mines N blocks, announces with announce-type + [ expect: getheaders/getdata or getdata, deliver block(s) ] + - node mines a block [ expect: 1 header ] + +Part 3: Headers announcements stop after large reorg and resume after getheaders or inv from peer. +- For response-type in {inv, getheaders} + * node mines a 7 block reorg [ expect: headers announcement of 8 blocks ] + * node mines an 8-block reorg [ expect: inv at tip ] + * peer responds with getblocks/getdata [expect: inv, blocks ] + * node mines another block [ expect: inv at tip, peer sends getdata, expect: block ] + * node mines another block at tip [ expect: inv ] + * peer responds with getheaders with an old hashstop more than 8 blocks back [expect: headers] + * peer requests block [ expect: block ] + * node mines another block at tip [ expect: inv, peer sends getdata, expect: block ] + * peer sends response-type [expect headers if getheaders, getheaders/getdata if mining new block] + * node mines 1 block [expect: 1 header, peer responds with getdata] + +Part 4: Test direct fetch behavior +a. Announce 2 old block headers. + Expect: no getdata requests. +b. Announce 3 new blocks via 1 headers message. + Expect: one getdata request for all 3 blocks. + (Send blocks.) +c. Announce 1 header that forks off the last two blocks. + Expect: no response. +d. Announce 1 more header that builds on that fork. + Expect: one getdata request for two blocks. +e. Announce 16 more headers that build on that fork. + Expect: getdata request for 14 more blocks. +f. Announce 1 more header that builds on that fork. + Expect: no response. +''' + +class BaseNode(NodeConnCB): + def __init__(self): + NodeConnCB.__init__(self) + self.create_callback_map() + self.connection = None + self.last_inv = None + self.last_headers = None + self.last_block = None + self.ping_counter = 1 + self.last_pong = msg_pong(0) + self.last_getdata = None + self.sleep_time = 0.05 + self.block_announced = False + + def clear_last_announcement(self): + with mininode_lock: + self.block_announced = False + self.last_inv = None + self.last_headers = None + + def add_connection(self, conn): + self.connection = conn + + # Request data for a list of block hashes + def get_data(self, block_hashes): + msg = msg_getdata() + for x in block_hashes: + msg.inv.append(CInv(2, x)) + self.connection.send_message(msg) + + def get_headers(self, locator, hashstop): + msg = msg_getheaders() + msg.locator.vHave = locator + msg.hashstop = hashstop + self.connection.send_message(msg) + + def send_block_inv(self, blockhash): + msg = msg_inv() + msg.inv = [CInv(2, blockhash)] + self.connection.send_message(msg) + + # Wrapper for the NodeConn's send_message function + def send_message(self, message): + self.connection.send_message(message) + + def on_inv(self, conn, message): + self.last_inv = message + self.block_announced = True + + def on_headers(self, conn, message): + self.last_headers = message + self.block_announced = True + + def on_block(self, conn, message): + self.last_block = message.block + self.last_block.calc_sha256() + + def on_getdata(self, conn, message): + self.last_getdata = message + + def on_pong(self, conn, message): + self.last_pong = message + + # Test whether the last announcement we received had the + # right header or the right inv + # inv and headers should be lists of block hashes + def check_last_announcement(self, headers=None, inv=None): + expect_headers = headers if headers != None else [] + expect_inv = inv if inv != None else [] + test_function = lambda: self.block_announced + self.sync(test_function) + with mininode_lock: + self.block_announced = False + + success = True + compare_inv = [] + if self.last_inv != None: + compare_inv = [x.hash for x in self.last_inv.inv] + if compare_inv != expect_inv: + success = False + + hash_headers = [] + if self.last_headers != None: + # treat headers as a list of block hashes + hash_headers = [ x.sha256 for x in self.last_headers.headers ] + if hash_headers != expect_headers: + success = False + + self.last_inv = None + self.last_headers = None + return success + + # Syncing helpers + def sync(self, test_function, timeout=60): + while timeout > 0: + with mininode_lock: + if test_function(): + return + time.sleep(self.sleep_time) + timeout -= self.sleep_time + raise AssertionError("Sync failed to complete") + + def sync_with_ping(self, timeout=60): + self.send_message(msg_ping(nonce=self.ping_counter)) + test_function = lambda: self.last_pong.nonce == self.ping_counter + self.sync(test_function, timeout) + self.ping_counter += 1 + return + + def wait_for_block(self, blockhash, timeout=60): + test_function = lambda: self.last_block != None and self.last_block.sha256 == blockhash + self.sync(test_function, timeout) + return + + def wait_for_getdata(self, hash_list, timeout=60): + if hash_list == []: + return + + test_function = lambda: self.last_getdata != None and [x.hash for x in self.last_getdata.inv] == hash_list + self.sync(test_function, timeout) + return + + def send_header_for_blocks(self, new_blocks): + headers_message = msg_headers() + headers_message.headers = [ CBlockHeader(b) for b in new_blocks ] + self.send_message(headers_message) + + def send_getblocks(self, locator): + getblocks_message = msg_getblocks() + getblocks_message.locator.vHave = locator + self.send_message(getblocks_message) + +# InvNode: This peer should only ever receive inv's, because it doesn't ever send a +# "sendheaders" message. +class InvNode(BaseNode): + def __init__(self): + BaseNode.__init__(self) + +# TestNode: This peer is the one we use for most of the testing. +class TestNode(BaseNode): + def __init__(self): + BaseNode.__init__(self) + +class SendHeadersTest(BitcoinTestFramework): + def setup_chain(self): + initialize_chain_clean(self.options.tmpdir, 2) + + def setup_network(self): + self.nodes = [] + self.nodes = start_nodes(2, self.options.tmpdir, [["-debug", "-logtimemicros=1"]]*2) + connect_nodes(self.nodes[0], 1) + + # mine count blocks and return the new tip + def mine_blocks(self, count): + self.nodes[0].generate(count) + return int(self.nodes[0].getbestblockhash(), 16) + + # mine a reorg that invalidates length blocks (replacing them with + # length+1 blocks). + # peers is the p2p nodes we're using; we clear their state after the + # to-be-reorged-out blocks are mined, so that we don't break later tests. + # return the list of block hashes newly mined + def mine_reorg(self, length, peers): + self.nodes[0].generate(length) # make sure all invalidated blocks are node0's + sync_blocks(self.nodes, wait=0.1) + [x.clear_last_announcement() for x in peers] + + tip_height = self.nodes[1].getblockcount() + hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1)) + self.nodes[1].invalidateblock(hash_to_invalidate) + all_hashes = self.nodes[1].generate(length+1) # Must be longer than the orig chain + sync_blocks(self.nodes, wait=0.1) + return [int(x, 16) for x in all_hashes] + + def run_test(self): + # Setup the p2p connections and start up the network thread. + inv_node = InvNode() + test_node = TestNode() + + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], inv_node)) + # Set nServices to 0 for test_node, so no block download will occur outside of + # direct fetching + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node, services=0)) + inv_node.add_connection(connections[0]) + test_node.add_connection(connections[1]) + + NetworkThread().start() # Start up network handling in another thread + + # Test logic begins here + inv_node.wait_for_verack() + test_node.wait_for_verack() + + tip = int(self.nodes[0].getbestblockhash(), 16) + + # PART 1 + # 1. Mine a block; expect inv announcements each time + print "Part 1: headers don't start before sendheaders message..." + for i in xrange(4): + old_tip = tip + tip = self.mine_blocks(1) + assert_equal(inv_node.check_last_announcement(inv=[tip]), True) + assert_equal(test_node.check_last_announcement(inv=[tip]), True) + # Try a few different responses; none should affect next announcement + if i == 0: + # first request the block + test_node.get_data([tip]) + test_node.wait_for_block(tip, timeout=5) + elif i == 1: + # next try requesting header and block + test_node.get_headers(locator=[old_tip], hashstop=tip) + test_node.get_data([tip]) + test_node.wait_for_block(tip) + test_node.clear_last_announcement() # since we requested headers... + elif i == 2: + # this time announce own block via headers + height = self.nodes[0].getblockcount() + last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + block_time = last_time + 1 + new_block = create_block(tip, create_coinbase(height+1), block_time) + new_block.solve() + test_node.send_header_for_blocks([new_block]) + test_node.wait_for_getdata([new_block.sha256], timeout=5) + test_node.send_message(msg_block(new_block)) + test_node.sync_with_ping() # make sure this block is processed + inv_node.clear_last_announcement() + test_node.clear_last_announcement() + + print "Part 1: success!" + print "Part 2: announce blocks with headers after sendheaders message..." + # PART 2 + # 2. Send a sendheaders message and test that headers announcements + # commence and keep working. + test_node.send_message(msg_sendheaders()) + prev_tip = int(self.nodes[0].getbestblockhash(), 16) + test_node.get_headers(locator=[prev_tip], hashstop=0L) + test_node.sync_with_ping() + test_node.clear_last_announcement() # Clear out empty headers response + + # Now that we've synced headers, headers announcements should work + tip = self.mine_blocks(1) + assert_equal(inv_node.check_last_announcement(inv=[tip]), True) + assert_equal(test_node.check_last_announcement(headers=[tip]), True) + + height = self.nodes[0].getblockcount()+1 + block_time += 10 # Advance far enough ahead + for i in xrange(10): + # Mine i blocks, and alternate announcing either via + # inv (of tip) or via headers. After each, new blocks + # mined by the node should successfully be announced + # with block header, even though the blocks are never requested + for j in xrange(2): + blocks = [] + for b in xrange(i+1): + blocks.append(create_block(tip, create_coinbase(height), block_time)) + blocks[-1].solve() + tip = blocks[-1].sha256 + block_time += 1 + height += 1 + if j == 0: + # Announce via inv + test_node.send_block_inv(tip) + test_node.wait_for_getdata([tip], timeout=5) + # Test that duplicate inv's won't result in duplicate + # getdata requests, or duplicate headers announcements + inv_node.send_block_inv(tip) + # Should have received a getheaders as well! + test_node.send_header_for_blocks(blocks) + test_node.wait_for_getdata([x.sha256 for x in blocks[0:-1]], timeout=5) + [ inv_node.send_block_inv(x.sha256) for x in blocks[0:-1] ] + inv_node.sync_with_ping() + else: + # Announce via headers + test_node.send_header_for_blocks(blocks) + test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=5) + # Test that duplicate headers won't result in duplicate + # getdata requests (the check is further down) + inv_node.send_header_for_blocks(blocks) + inv_node.sync_with_ping() + [ test_node.send_message(msg_block(x)) for x in blocks ] + test_node.sync_with_ping() + inv_node.sync_with_ping() + # This block should not be announced to the inv node (since it also + # broadcast it) + assert_equal(inv_node.last_inv, None) + assert_equal(inv_node.last_headers, None) + inv_node.clear_last_announcement() + test_node.clear_last_announcement() + tip = self.mine_blocks(1) + assert_equal(inv_node.check_last_announcement(inv=[tip]), True) + assert_equal(test_node.check_last_announcement(headers=[tip]), True) + height += 1 + block_time += 1 + + print "Part 2: success!" + + print "Part 3: headers announcements can stop after large reorg, and resume after headers/inv from peer..." + + # PART 3. Headers announcements can stop after large reorg, and resume after + # getheaders or inv from peer. + for j in xrange(2): + # First try mining a reorg that can propagate with header announcement + new_block_hashes = self.mine_reorg(length=7, peers=[test_node, inv_node]) + tip = new_block_hashes[-1] + assert_equal(inv_node.check_last_announcement(inv=[tip]), True) + assert_equal(test_node.check_last_announcement(headers=new_block_hashes), True) + + block_time += 8 + + # Mine a too-large reorg, which should be announced with a single inv + new_block_hashes = self.mine_reorg(length=8, peers=[test_node, inv_node]) + tip = new_block_hashes[-1] + assert_equal(inv_node.check_last_announcement(inv=[tip]), True) + assert_equal(test_node.check_last_announcement(inv=[tip]), True) + + block_time += 9 + + fork_point = self.nodes[0].getblock("%02x" % new_block_hashes[0])["previousblockhash"] + fork_point = int(fork_point, 16) + + # Use getblocks/getdata + test_node.send_getblocks(locator = [fork_point]) + assert_equal(test_node.check_last_announcement(inv=new_block_hashes[0:-1]), True) + test_node.get_data(new_block_hashes) + test_node.wait_for_block(new_block_hashes[-1]) + + for i in xrange(3): + # Mine another block, still should get only an inv + tip = self.mine_blocks(1) + assert_equal(inv_node.check_last_announcement(inv=[tip]), True) + assert_equal(test_node.check_last_announcement(inv=[tip]), True) + if i == 0: + # Just get the data -- shouldn't cause headers announcements to resume + test_node.get_data([tip]) + test_node.wait_for_block(tip) + elif i == 1: + # Send a getheaders message that shouldn't trigger headers announcements + # to resume (best header sent will be too old) + test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) + test_node.get_data([tip]) + test_node.wait_for_block(tip) + test_node.clear_last_announcement() + elif i == 2: + test_node.get_data([tip]) + test_node.wait_for_block(tip) + # This time, try sending either a getheaders to trigger resumption + # of headers announcements, or mine a new block and inv it, also + # triggering resumption of headers announcements. + if j == 0: + test_node.get_headers(locator=[tip], hashstop=0L) + test_node.sync_with_ping() + else: + test_node.send_block_inv(tip) + test_node.sync_with_ping() + # New blocks should now be announced with header + tip = self.mine_blocks(1) + assert_equal(inv_node.check_last_announcement(inv=[tip]), True) + assert_equal(test_node.check_last_announcement(headers=[tip]), True) + + print "Part 3: success!" + + print "Part 4: Testing direct fetch behavior..." + tip = self.mine_blocks(1) + height = self.nodes[0].getblockcount() + 1 + last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + block_time = last_time + 1 + + # Create 2 blocks. Send the blocks, then send the headers. + blocks = [] + for b in xrange(2): + blocks.append(create_block(tip, create_coinbase(height), block_time)) + blocks[-1].solve() + tip = blocks[-1].sha256 + block_time += 1 + height += 1 + inv_node.send_message(msg_block(blocks[-1])) + + inv_node.sync_with_ping() # Make sure blocks are processed + test_node.last_getdata = None + test_node.send_header_for_blocks(blocks); + test_node.sync_with_ping() + # should not have received any getdata messages + with mininode_lock: + assert_equal(test_node.last_getdata, None) + + # This time, direct fetch should work + blocks = [] + for b in xrange(3): + blocks.append(create_block(tip, create_coinbase(height), block_time)) + blocks[-1].solve() + tip = blocks[-1].sha256 + block_time += 1 + height += 1 + + test_node.send_header_for_blocks(blocks) + test_node.sync_with_ping() + test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=test_node.sleep_time) + + [ test_node.send_message(msg_block(x)) for x in blocks ] + + test_node.sync_with_ping() + + # Now announce a header that forks the last two blocks + tip = blocks[0].sha256 + height -= 1 + blocks = [] + + # Create extra blocks for later + for b in xrange(20): + blocks.append(create_block(tip, create_coinbase(height), block_time)) + blocks[-1].solve() + tip = blocks[-1].sha256 + block_time += 1 + height += 1 + + # Announcing one block on fork should not trigger direct fetch + # (less work than tip) + test_node.last_getdata = None + test_node.send_header_for_blocks(blocks[0:1]) + test_node.sync_with_ping() + with mininode_lock: + assert_equal(test_node.last_getdata, None) + + # Announcing one more block on fork should trigger direct fetch for + # both blocks (same work as tip) + test_node.send_header_for_blocks(blocks[1:2]) + test_node.sync_with_ping() + test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=test_node.sleep_time) + + # Announcing 16 more headers should trigger direct fetch for 14 more + # blocks + test_node.send_header_for_blocks(blocks[2:18]) + test_node.sync_with_ping() + test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=test_node.sleep_time) + + # Announcing 1 more header should not trigger any response + test_node.last_getdata = None + test_node.send_header_for_blocks(blocks[18:19]) + test_node.sync_with_ping() + with mininode_lock: + assert_equal(test_node.last_getdata, None) + + print "Part 4: success!" + + # Finally, check that the inv node never received a getdata request, + # throughout the test + assert_equal(inv_node.last_getdata, None) + +if __name__ == '__main__': + SendHeadersTest().main() diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 8c5ad8255..411743c34 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -1133,8 +1133,8 @@ def __repr__(self): class msg_getdata(object): command = "getdata" - def __init__(self): - self.inv = [] + def __init__(self, inv=None): + self.inv = inv if inv != None else [] def deserialize(self, f): self.inv = deser_vector(f, CInv) @@ -1304,6 +1304,22 @@ def __repr__(self): return "msg_mempool()" +class msg_sendheaders(object): + command = "sendheaders" + + def __init__(self): + pass + + def deserialize(self, f): + pass + + def serialize(self): + return "" + + def __repr__(self): + return "msg_sendheaders()" + + # getheaders message has # number of entries # vector of hashes @@ -1420,6 +1436,17 @@ class NodeConnCB(object): def __init__(self): self.verack_received = False + # Spin until verack message is received from the node. + # Tests may want to use this as a signal that the test can begin. + # This can be called from the testing thread, so it needs to acquire the + # global lock. + def wait_for_verack(self): + while True: + with mininode_lock: + if self.verack_received: + return + time.sleep(0.05) + # Derived classes should call this function once to set the message map # which associates the derived classes' functions to incoming messages def create_callback_map(self): @@ -1517,7 +1544,7 @@ class NodeConn(asyncore.dispatcher): "regtest": "\xaa\xe8\x3f\x5f" # regtest } - def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", protocol_version=SAPLING_PROTO_VERSION): + def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=1, protocol_version=SAPLING_PROTO_VERSION): asyncore.dispatcher.__init__(self, map=mininode_socket_map) self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport)) self.dstaddr = dstaddr @@ -1535,6 +1562,7 @@ def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", protocol_vers # stuff version msg into sendbuf vt = msg_version(protocol_version) + vt.nServices = services vt.addrTo.ip = self.dstaddr vt.addrTo.port = self.dstport vt.addrFrom.ip = "0.0.0.0" diff --git a/src/chain.cpp b/src/chain.cpp index 43d5b8bc2..dbe4d017a 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -52,7 +52,9 @@ CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const { } const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const { - assert(pindex != nullptr); + if (pindex == NULL) { + return NULL; + } if (pindex->nHeight > Height()) pindex = pindex->GetAncestor(Height()); while (pindex && !Contains(pindex)) diff --git a/src/main.cpp b/src/main.cpp index 49ca00e02..079756080 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -271,6 +271,8 @@ struct CNodeState { CBlockIndex *pindexBestKnownBlock; //! The hash of the last unknown block this peer has announced. uint256 hashLastUnknownBlock; + //! The best header we have sent our peer. + CBlockIndex *pindexBestHeaderSent; //! The last full block we both have. CBlockIndex *pindexLastCommonBlock; //! Whether we've started headers synchronization with this peer. @@ -282,6 +284,8 @@ struct CNodeState { int nBlocksInFlightValidHeaders; //! Whether we consider this a preferred download peer. bool fPreferredDownload; + //! Whether this peer wants invs or headers (when possible) for block announcements. + bool fPreferHeaders; CNodeState() { fCurrentlyConnected = false; @@ -290,11 +294,13 @@ struct CNodeState { pindexBestKnownBlock = NULL; hashLastUnknownBlock.SetNull(); pindexLastCommonBlock = NULL; + pindexBestHeaderSent = NULL; fSyncStarted = false; nStallingSince = 0; nBlocksInFlight = 0; nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; + fPreferHeaders = false; } }; @@ -426,6 +432,22 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } } +// Requires cs_main +bool CanDirectFetch(const Consensus::Params &consensusParams) +{ + return chainActive.Tip()->GetBlockTime() > GetTime() - consensusParams.PoWTargetSpacing(chainActive.Tip()->nHeight) * 20; +} + +// Requires cs_main +bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex) +{ + if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight)) + return true; + if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight)) + return true; + return false; +} + /** Find the last common ancestor two blocks have. * Both pa and pb must be non-NULL. */ CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { @@ -3280,17 +3302,17 @@ static void NotifyHeaderTip(const CChainParams& chainParams) { * or an activated best chain. pblock is either NULL or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ -bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock) -{ +bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock) { CBlockIndex *pindexMostWork = NULL; CBlockIndex *pindexNewTip = NULL; do { boost::this_thread::interruption_point(); + const CBlockIndex *pindexFork; bool fInitialDownload; - int nNewHeight; { LOCK(cs_main); + CBlockIndex *pindexOldTip = chainActive.Tip(); if (pindexMostWork == NULL) { pindexMostWork = FindMostWorkChain(); } @@ -3308,8 +3330,8 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, pindexMostWork = NULL; } pindexNewTip = chainActive.Tip(); + pindexFork = chainActive.FindFork(pindexOldTip); fInitialDownload = IsInitialBlockDownload(chainparams); - nNewHeight = chainActive.Height(); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). @@ -3317,19 +3339,36 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, // Always notify the UI if a new block tip was connected uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); if (!fInitialDownload) { - uint256 hashNewTip = pindexNewTip->GetBlockHash(); + // Find the hashes of all blocks that weren't previously in the best chain. + std::vector vHashes; + CBlockIndex *pindexToAnnounce = pindexNewTip; + while (pindexToAnnounce != pindexFork) { + vHashes.push_back(pindexToAnnounce->GetBlockHash()); + pindexToAnnounce = pindexToAnnounce->pprev; + if (vHashes.size() == MAX_BLOCKS_TO_ANNOUNCE) { + // Limit announcements in case of a huge reorganization. + // Rely on the peer's synchronization mechanism in that case. + break; + } + } // Relay inventory, but don't relay old inventory during initial block download. int nBlockEstimate = 0; if (fCheckpointsEnabled) nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()); { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); + for (CNode* pnode : vNodes) { + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) { + for (const uint256& hash : reverse_iterate(vHashes)) { + pnode->PushBlockHash(hash); + } + } + } } // Notify external listeners about the new tip. - GetMainSignals().UpdatedBlockTip(pindexNewTip); + if (!vHashes.empty()) { + GetMainSignals().UpdatedBlockTip(pindexNewTip); + } } } while (pindexNewTip != pindexMostWork); CheckBlockIndex(chainparams.GetConsensus()); @@ -5462,6 +5501,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); State(pfrom->GetId())->fCurrentlyConnected = true; } + + if (pfrom->nVersion >= SENDHEADERS_VERSION) { + // Tell our peer we prefer to receive headers rather than inv's + // We send this to non-NODE NETWORK peers as well, because even + // non-NODE NETWORK peers can announce blocks (such as pruning + // nodes) + pfrom->PushMessage("sendheaders"); + } } @@ -5544,6 +5591,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; } + else if (strCommand == "sendheaders") + { + LOCK(cs_main); + State(pfrom->GetId())->fPreferHeaders = true; + } + else if (strCommand == "inv") { @@ -5590,7 +5643,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash); CNodeState *nodestate = State(pfrom->GetId()); - if (chainActive.Tip()->GetBlockTime() > GetTime() - chainparams.GetConsensus().PoWTargetSpacing(pindexBestHeader->nHeight) * 20 && + if (CanDirectFetch(chainparams.GetConsensus()) && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { vToFetch.push_back(inv); // Mark block as in flight already, even though the actual "getdata" message only goes out @@ -5700,6 +5753,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id); return true; } + + CNodeState *nodestate = State(pfrom->GetId()); CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -5727,6 +5782,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } + // pindex can be NULL either if we sent chainActive.Tip() OR + // if our peer has chainActive.Tip() (and thus we are sending an empty + // headers message). In both cases it's safe to update + // pindexBestHeaderSent to be our tip. + nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); pfrom->PushMessage("headers", vHeaders); } @@ -5925,6 +5985,53 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); } + bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); + CNodeState *nodestate = State(pfrom->GetId()); + // If this set of headers is valid and ends in a block with at least as + // much work as our tip, download as much as possible. + if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) { + vector vToFetch; + CBlockIndex *pindexWalk = pindexLast; + // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. + while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && + !mapBlocksInFlight.count(pindexWalk->GetBlockHash())) { + // We don't have this block, and it's not yet in flight. + vToFetch.push_back(pindexWalk); + } + pindexWalk = pindexWalk->pprev; + } + // If pindexWalk still isn't on our main chain, we're looking at a + // very large reorg at a time we think we're close to caught up to + // the main chain -- this shouldn't really happen. Bail out on the + // direct fetch and rely on parallel download instead. + if (!chainActive.Contains(pindexWalk)) { + LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n", + pindexLast->GetBlockHash().ToString(), + pindexLast->nHeight); + } else { + vector vGetData; + // Download as much as possible, from earliest to latest. + for (CBlockIndex *pindex : reverse_iterate(vToFetch)) { + if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + // Can't download any more from this peer + break; + } + vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); + MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex); + LogPrint("net", "Requesting block %s from peer=%d\n", + pindex->GetBlockHash().ToString(), pfrom->id); + } + if (vGetData.size() > 1) { + LogPrint("net", "Downloading blocks toward %s (%d) via headers direct fetch\n", + pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); + } + if (vGetData.size() > 0) { + pfrom->PushMessage("getdata", vGetData); + } + } + } + CheckBlockIndex(chainparams.GetConsensus()); } @@ -6463,6 +6570,100 @@ bool SendMessages(CNode* pto) GetMainSignals().Broadcast(nTimeBestReceived); } + // + // Try sending block announcements via headers + // + { + // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our + // list of block hashes we're relaying, and our peer wants + // headers announcements, then find the first header + // not yet known to our peer but would connect, and send. + // If no header would connect, or if we have too many + // blocks, or if the peer doesn't want headers, just + // add all to the inv queue. + LOCK(pto->cs_inventory); + vector vHeaders; + bool fRevertToInv = (!state.fPreferHeaders || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); + CBlockIndex *pBestIndex = NULL; // last header queued for delivery + ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date + + if (!fRevertToInv) { + bool fFoundStartingHeader = false; + // Try to find first header that our peer doesn't have, and + // then send all headers past that one. If we come across any + // headers that aren't on chainActive, give up. + for (const uint256 &hash : pto->vBlockHashesToAnnounce) { + BlockMap::iterator mi = mapBlockIndex.find(hash); + assert(mi != mapBlockIndex.end()); + CBlockIndex *pindex = mi->second; + if (chainActive[pindex->nHeight] != pindex) { + // Bail out if we reorged away from this block + fRevertToInv = true; + break; + } + assert(pBestIndex == NULL || pindex->pprev == pBestIndex); + pBestIndex = pindex; + if (fFoundStartingHeader) { + // add this to the headers message + vHeaders.push_back(pindex->GetBlockHeader()); + } else if (PeerHasHeader(&state, pindex)) { + continue; // keep looking for the first new block + } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) { + // Peer doesn't have this header but they do have the prior one. + // Start sending headers. + fFoundStartingHeader = true; + vHeaders.push_back(pindex->GetBlockHeader()); + } else { + // Peer doesn't have this header or the prior one -- nothing will + // connect, so bail out. + fRevertToInv = true; + break; + } + } + } + if (fRevertToInv) { + // If falling back to using an inv, just try to inv the tip. + // The last entry in vBlockHashesToAnnounce was our tip at some point + // in the past. + if (!pto->vBlockHashesToAnnounce.empty()) { + const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back(); + BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce); + assert(mi != mapBlockIndex.end()); + CBlockIndex *pindex = mi->second; + + // Warn if we're announcing a block that is not on the main chain. + // This should be very rare and could be optimized out. + // Just log for now. + if (chainActive[pindex->nHeight] != pindex) { + LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n", + hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString()); + } + + // If the peer announced this block to us, don't inv it back. + // (Since block announcements may not be via inv's, we can't solely rely on + // setInventoryKnown to track this.) + if (!PeerHasHeader(&state, pindex)) { + pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce)); + LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__, + pto->id, hashToAnnounce.ToString()); + } + } + } else if (!vHeaders.empty()) { + if (vHeaders.size() > 1) { + LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__, + vHeaders.size(), + vHeaders.front().GetHash().ToString(), + vHeaders.back().GetHash().ToString(), pto->id); + } else { + LogPrint("net", "%s: sending header %s to peer=%d\n", __func__, + vHeaders.front().GetHash().ToString(), pto->id); + } + pto->PushMessage("headers", vHeaders); + state.pindexBestHeaderSent = pBestIndex; + } + pto->vBlockHashesToAnnounce.clear(); + } + // // Message: inventory // diff --git a/src/main.h b/src/main.h index ba9c82342..2409e24a1 100644 --- a/src/main.h +++ b/src/main.h @@ -136,6 +136,9 @@ static const unsigned int DEFAULT_REORG_CHECK = 6; static const bool DEFAULT_PEERBLOOMFILTERS = true; static const bool DEFAULT_ENFORCENODEBLOOM = false; +/** Maximum number of headers to announce when relaying blocks with headers message.*/ +static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8; + struct BlockHasher { size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); } diff --git a/src/net.h b/src/net.h index cd226ccfe..9820e6330 100644 --- a/src/net.h +++ b/src/net.h @@ -337,6 +337,9 @@ class CNode std::set setAskFor; std::multimap mapAskFor; int64_t nNextInvSend; + // Used for headers announcements - unfiltered blocks to relay + // Also protected by cs_inventory + std::vector vBlockHashesToAnnounce; // Ping time measurement: // The pong reply we're expecting, or 0 if no pong expected. @@ -463,6 +466,12 @@ class CNode } } + void PushBlockHash(const uint256 &hash) + { + LOCK(cs_inventory); + vBlockHashesToAnnounce.push_back(hash); + } + void AskFor(const CInv& inv); // TODO: Document the postcondition of this function. Is cs_vSend locked? diff --git a/src/version.h b/src/version.h index 0a6c93f5c..ebd371662 100644 --- a/src/version.h +++ b/src/version.h @@ -27,4 +27,7 @@ static const int BIP0031_VERSION = 60000; //! "filter*" commands are disabled without NODE_BLOOM after and including this version static const int NO_BLOOM_VERSION = 170004; +//! "sendheaders" command and announcing blocks with headers starts with this version +static const int SENDHEADERS_VERSION = 770009; + #endif // BITCOIN_VERSION_H From 94197cbe96e1c0db2c087d098df4f0f751d1f292 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 16:05:46 +0000 Subject: [PATCH 106/146] Mempool package tracking --- qa/pull-tester/rpc-tests.sh | 1 + qa/rpc-tests/mempool_packages.py | 193 ++++++++++ qa/rpc-tests/mergetoaddress_helper.py | 5 +- qa/rpc-tests/prioritisetransaction.py | 12 +- src/init.cpp | 4 + src/main.cpp | 27 +- src/main.h | 8 + src/memusage.h | 20 +- src/rpc/blockchain.cpp | 6 + src/test/mempool_tests.cpp | 146 +++++++- src/txmempool.cpp | 513 +++++++++++++++++++++++--- src/txmempool.h | 287 +++++++++++++- 12 files changed, 1138 insertions(+), 84 deletions(-) create mode 100644 qa/rpc-tests/mempool_packages.py diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index a316d2009..16d67bc32 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -96,6 +96,7 @@ testScriptsExt=( 'invalidblockrequest.py' # 'forknotify.py' 'p2p-acceptblock.py' + 'mempool_packages.py' 'maxuploadtarget.py', 'wallet_db_flush.py' ); diff --git a/qa/rpc-tests/mempool_packages.py b/qa/rpc-tests/mempool_packages.py new file mode 100644 index 000000000..5b549a585 --- /dev/null +++ b/qa/rpc-tests/mempool_packages.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# Test descendant package tracking code + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + ROUND_DOWN, + Decimal, + JSONRPCException, + assert_equal, + connect_nodes, + start_node, + sync_blocks, + sync_mempools, +) + +def satoshi_round(amount): + return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) + +class MempoolPackagesTest(BitcoinTestFramework): + + def setup_network(self): + base_args = [ + "-maxorphantx=1000", + "-relaypriority=0", + "-debug", + "-allowdeprecated=getnewaddress", + ] + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, base_args)) + self.nodes.append(start_node(1, self.options.tmpdir, base_args + ["-limitancestorcount=5"])) + connect_nodes(self.nodes[0], 1) + self.is_network_split = False + self.sync_all() + + # Build a transaction that spends parent_txid:vout + # Return amount sent + def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs): + send_value = satoshi_round((value - fee)/num_outputs) + inputs = [ {'txid' : parent_txid, 'vout' : vout} ] + outputs = {} + for i in range(num_outputs): + outputs[node.getnewaddress()] = send_value + rawtx = node.createrawtransaction(inputs, outputs) + signedtx = node.signrawtransaction(rawtx) + txid = node.sendrawtransaction(signedtx['hex']) + fulltx = node.getrawtransaction(txid, 1) + assert(len(fulltx['vout']) == num_outputs) # make sure we didn't generate a change output + return (txid, send_value) + + def run_test(self): + ''' Mine some blocks and have them mature. ''' + self.nodes[0].generate(101) + utxo = self.nodes[0].listunspent(10) + txid = utxo[0]['txid'] + vout = utxo[0]['vout'] + value = utxo[0]['amount'] + + fee = Decimal("0.00005") + # 100 transactions off a confirmed tx should be fine + chain = [] + for i in range(100): + (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, 0, value, fee, 1) + value = sent_value + chain.append(txid) + + # Check mempool has 100 transactions in it, and descendant + # count and fees should look correct + mempool = self.nodes[0].getrawmempool(True) + assert_equal(len(mempool), 100) + descendant_count = 1 + descendant_fees = 0 + descendant_size = 0 + SATOSHIS = 100000000 + + for x in reversed(chain): + assert_equal(mempool[x]['descendantcount'], descendant_count) + descendant_fees += mempool[x]['fee'] + assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees) + descendant_size += mempool[x]['size'] + assert_equal(mempool[x]['descendantsize'], descendant_size) + descendant_count += 1 + + # Adding one more transaction on to the chain should fail. + try: + self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1) + except JSONRPCException: + print("too-long-ancestor-chain successfully rejected") + + # TODO: check that node1's mempool is as expected + + # TODO: test ancestor size limits + + # Now test descendant chain limits + txid = utxo[1]['txid'] + value = utxo[1]['amount'] + vout = utxo[1]['vout'] + + transaction_package = [] + # First create one parent tx with 10 children + (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10) + parent_transaction = txid + for i in range(10): + transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value}) + + for i in range(1000): + utxo = transaction_package.pop(0) + try: + (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) + for j in range(10): + transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value}) + if i == 998: + mempool = self.nodes[0].getrawmempool(True) + assert_equal(mempool[parent_transaction]['descendantcount'], 1000) + except JSONRPCException as e: + print(e.error['message']) + assert_equal(i, 999) + print("tx that would create too large descendant package successfully rejected") + + # TODO: check that node1's mempool is as expected + + # TODO: test descendant size limits + + # Test reorg handling + # First, the basics: + self.nodes[0].generate(1) + sync_blocks(self.nodes) + self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash()) + self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash()) + + # Now test the case where node1 has a transaction T in its mempool that + # depends on transactions A and B which are in a mined block, and the + # block containing A and B is disconnected, AND B is not accepted back + # into node1's mempool because its ancestor count is too high. + + # Create 8 transactions, like so: + # Tx0 -> Tx1 (vout0) + # \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7 + # + # Mine them in the next block, then generate a new tx8 that spends + # Tx1 and Tx7, and add to node1's mempool, then disconnect the + # last block. + + # Create tx0 with 2 outputs + utxo = self.nodes[0].listunspent() + txid = utxo[0]['txid'] + value = utxo[0]['amount'] + vout = utxo[0]['vout'] + + send_value = satoshi_round((value - fee)/2) + inputs = [ {'txid' : txid, 'vout' : vout} ] + outputs = {} + for i in range(2): + outputs[self.nodes[0].getnewaddress()] = send_value + rawtx = self.nodes[0].createrawtransaction(inputs, outputs) + signedtx = self.nodes[0].signrawtransaction(rawtx) + txid = self.nodes[0].sendrawtransaction(signedtx['hex']) + tx0_id = txid + value = send_value + + # Create tx1 + (tx1_id, tx1_value) = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1) + + # Create tx2-7 + vout = 1 + txid = tx0_id + for i in range(6): + (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1) + vout = 0 + value = sent_value + + # Mine these in a block + self.nodes[0].generate(1) + self.sync_all() + + # Now generate tx8, with a big fee + inputs = [ {'txid' : tx1_id, 'vout': 0}, {'txid' : txid, 'vout': 0} ] + outputs = { self.nodes[0].getnewaddress() : send_value + value - 4*fee } + rawtx = self.nodes[0].createrawtransaction(inputs, outputs) + signedtx = self.nodes[0].signrawtransaction(rawtx) + txid = self.nodes[0].sendrawtransaction(signedtx['hex']) + sync_mempools(self.nodes) + + # Now try to disconnect the tip on each node... + self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + sync_blocks(self.nodes) + +if __name__ == '__main__': + MempoolPackagesTest().main() diff --git a/qa/rpc-tests/mergetoaddress_helper.py b/qa/rpc-tests/mergetoaddress_helper.py index 3369ebd9a..8399ac8bb 100755 --- a/qa/rpc-tests/mergetoaddress_helper.py +++ b/qa/rpc-tests/mergetoaddress_helper.py @@ -41,7 +41,10 @@ def setup_chain(self, test): initialize_chain_clean(test.options.tmpdir, 4) def setup_network(self, test, additional_args=[]): - args = ['-debug=zrpcunsafe'] + args = [ + '-debug=zrpcunsafe', + '-limitancestorcount=%d' % self.utxos_to_generate, + ] args += additional_args test.nodes = [] test.nodes.append(start_node(0, test.options.tmpdir, args)) diff --git a/qa/rpc-tests/prioritisetransaction.py b/qa/rpc-tests/prioritisetransaction.py index cdb5f5858..ec54ec490 100755 --- a/qa/rpc-tests/prioritisetransaction.py +++ b/qa/rpc-tests/prioritisetransaction.py @@ -22,8 +22,16 @@ def setup_chain(self): def setup_network(self, split=False): self.nodes = [] # Start nodes with tiny block size of 11kb - self.nodes.append(start_node(0, self.options.tmpdir, ["-blockprioritysize=7000", "-blockmaxsize=11000", "-maxorphantx=1000", "-relaypriority=true", "-printpriority=1"])) - self.nodes.append(start_node(1, self.options.tmpdir, ["-blockprioritysize=7000", "-blockmaxsize=11000", "-maxorphantx=1000", "-relaypriority=true", "-printpriority=1"])) + args = [ + "-blockprioritysize=7000", + "-blockmaxsize=11000", + "-maxorphantx=1000", + "-relaypriority=true", + "-printpriority=1", + "-limitancestorcount=900", + ] + self.nodes.append(start_node(0, self.options.tmpdir, args)) + self.nodes.append(start_node(1, self.options.tmpdir, args)) connect_nodes(self.nodes[1], 0) self.is_network_split=False self.sync_all() diff --git a/src/init.cpp b/src/init.cpp index 82967064e..5063f82fe 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -423,6 +423,10 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-dropmessagestest=", "Randomly drop 1 of every network messages"); strUsage += HelpMessageOpt("-fuzzmessagestest=", "Randomly fuzz 1 of every network messages"); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT)); + strUsage += HelpMessageOpt("-limitancestorcount=", strprintf("Do not accept transactions if number of in-mempool ancestors is or more (default: %u)", DEFAULT_ANCESTOR_LIMIT)); + strUsage += HelpMessageOpt("-limitancestorsize=", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT)); + strUsage += HelpMessageOpt("-limitdescendantcount=", strprintf("Do not accept transactions if any ancestor would have or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); + strUsage += HelpMessageOpt("-limitdescendantsize=", strprintf("Do not accept transactions if any ancestor would have more than kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)"); } std::string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, pow, proxy, prune, " diff --git a/src/main.cpp b/src/main.cpp index 079756080..be50fcfb8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1470,6 +1470,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.Error("AcceptToMemoryPool: " + errmsg); } + // Calculate in-mempool ancestors, up to a limit. + CTxMemPool::setEntries setAncestors; + size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); + size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; + size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); + size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; + std::string errString; + if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { + return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false); + } + // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. PrecomputedTransactionData txdata(tx); @@ -1497,7 +1508,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa LOCK(pool.cs); // Store transaction in memory - pool.addUnchecked(hash, entry, !IsInitialBlockDownload(Params())); + pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload(Params())); // Add memory address index if (fAddressIndex) { @@ -2950,13 +2961,23 @@ bool static DisconnectTip(CValidationState &state, const CChainParams& chainpara if (!fBare) { // Resurrect mempool transactions from the disconnected block. + std::vector vHashUpdate; for (const CTransaction &tx : block.vtx) { // ignore validation errors in resurrected transactions list removed; CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) { mempool.remove(tx, removed, true); + } else if (mempool.exists(tx.GetHash())) { + vHashUpdate.push_back(tx.GetHash()); + } } + // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have + // no in-mempool children, which is generally not true when adding + // previously-confirmed transactions back to the mempool. + // UpdateTransactionsFromBlock finds descendants of any transactions in this + // block that were added back and cleans up the mempool state. + mempool.UpdateTransactionsFromBlock(vHashUpdate); if (sproutAnchorBeforeDisconnect != sproutAnchorAfterDisconnect) { // The anchor may not change between block disconnects, // in which case we don't want to evict from the mempool yet! @@ -5826,7 +5847,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", pfrom->id, pfrom->cleanSubVer, tx.GetHash().ToString(), - mempool.mapTx.size()); + mempool.size()); // Recursively process any orphan transactions that depended on this one set setMisbehaving; diff --git a/src/main.h b/src/main.h index 2409e24a1..e7b3d2838 100644 --- a/src/main.h +++ b/src/main.h @@ -71,6 +71,14 @@ static const CAmount HIGH_TX_FEE_PER_KB = 0.01 * COIN; static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB; /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; +/** Default for -limitancestorcount, max number of in-mempool ancestors */ +static const unsigned int DEFAULT_ANCESTOR_LIMIT = 100; +/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */ +static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 900; +/** Default for -limitdescendantcount, max number of in-mempool descendants */ +static const unsigned int DEFAULT_DESCENDANT_LIMIT = 1000; +/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */ +static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 2500; /** Default for -txexpirydelta, in number of blocks */ static const unsigned int DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA = 20; static const unsigned int DEFAULT_POST_BLOSSOM_TX_EXPIRY_DELTA = DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA * Consensus::BLOSSOM_POW_TARGET_SPACING_RATIO; diff --git a/src/memusage.h b/src/memusage.h index 574bce17a..e920fc373 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -80,18 +80,30 @@ static inline size_t DynamicUsage(const prevector& v) return MallocUsage(v.allocated_memory()); } -template -static inline size_t DynamicUsage(const std::set& s) +template +static inline size_t DynamicUsage(const std::set& s) { return MallocUsage(sizeof(stl_tree_node)) * s.size(); } -template -static inline size_t DynamicUsage(const std::map& m) +template +static inline size_t IncrementalDynamicUsage(const std::set& s) +{ + return MallocUsage(sizeof(stl_tree_node)); +} + +template +static inline size_t DynamicUsage(const std::map& m) { return MallocUsage(sizeof(stl_tree_node >)) * m.size(); } +template +static inline size_t IncrementalDynamicUsage(const std::map& m) +{ + return MallocUsage(sizeof(stl_tree_node >)); +} + template struct unordered_node : private X { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d0451ce0a..a09690c80 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -335,6 +335,9 @@ UniValue mempoolToJSON(bool fVerbose = false) info.pushKV("height", (int)e.GetHeight()); info.pushKV("startingpriority", e.GetPriority(e.GetHeight())); info.pushKV("currentpriority", e.GetPriority(chainActive.Height())); + info.pushKV("descendantcount", e.GetCountWithDescendants()); + info.pushKV("descendantsize", e.GetSizeWithDescendants()); + info.pushKV("descendantfees", e.GetFeesWithDescendants()); const CTransaction& tx = e.GetTx(); set setDepends; for (const CTxIn& txin : tx.vin) @@ -389,6 +392,9 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) " \"height\" : n, (numeric) block height when transaction entered pool\n" " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" + " \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n" + " \"descendantfees\" : n, (numeric) fees of in-mempool descendants (including this one)\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n" diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index f448d92e0..2bdfccfaa 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2011-2014 The Bitcoin Core developers +// Copyright (c) 2018-2022 The Zcash developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -11,6 +12,7 @@ #include #include +#include BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup) @@ -103,6 +105,16 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) removed.clear(); } +void CheckSort(CTxMemPool &pool, std::vector &sortedOrder) +{ + BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size()); + CTxMemPool::indexed_transaction_set::nth_index<1>::type::iterator it = pool.mapTx.get<1>().begin(); + int count=0; + for (; it != pool.mapTx.get<1>().end(); ++it, ++count) { + BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]); + } +} + BOOST_AUTO_TEST_CASE(MempoolIndexingTest) { CTxMemPool pool(CFeeRate(0)); @@ -147,14 +159,132 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5)); BOOST_CHECK_EQUAL(pool.size(), 5); - // Check the fee-rate index is in order, should be tx2, tx4, tx1, tx5, tx3 - CTxMemPool::indexed_transaction_set::nth_index<1>::type::iterator it = pool.mapTx.get<1>().begin(); - BOOST_CHECK_EQUAL(it++->GetTx().GetHash().ToString(), tx2.GetHash().ToString()); - BOOST_CHECK_EQUAL(it++->GetTx().GetHash().ToString(), tx4.GetHash().ToString()); - BOOST_CHECK_EQUAL(it++->GetTx().GetHash().ToString(), tx1.GetHash().ToString()); - BOOST_CHECK_EQUAL(it++->GetTx().GetHash().ToString(), tx5.GetHash().ToString()); - BOOST_CHECK_EQUAL(it++->GetTx().GetHash().ToString(), tx3.GetHash().ToString()); - BOOST_CHECK(it == pool.mapTx.get<1>().end()); + std::vector sortedOrder; + sortedOrder.resize(5); + sortedOrder[0] = tx3.GetHash().ToString(); // 0 + sortedOrder[1] = tx5.GetHash().ToString(); // 10000 + sortedOrder[2] = tx1.GetHash().ToString(); // 10000 + sortedOrder[3] = tx4.GetHash().ToString(); // 15000 + sortedOrder[4] = tx2.GetHash().ToString(); // 20000 + CheckSort(pool, sortedOrder); + + /* low fee but with high fee child */ + /* tx6 -> tx7 -> tx8, tx9 -> tx10 */ + CMutableTransaction tx6 = CMutableTransaction(); + tx6.vout.resize(1); + tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx6.vout[0].nValue = 20 * COIN; + pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6)); + BOOST_CHECK_EQUAL(pool.size(), 6); + // Check that at this point, tx6 is sorted low + sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString()); + CheckSort(pool, sortedOrder); + + CTxMemPool::setEntries setAncestors; + setAncestors.insert(pool.mapTx.find(tx6.GetHash())); + CMutableTransaction tx7 = CMutableTransaction(); + tx7.vin.resize(1); + tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0); + tx7.vin[0].scriptSig = CScript() << OP_11; + tx7.vout.resize(2); + tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx7.vout[0].nValue = 10 * COIN; + tx7.vout[1].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx7.vout[1].nValue = 1 * COIN; + + CTxMemPool::setEntries setAncestorsCalculated; + std::string dummy; + BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true); + BOOST_CHECK(setAncestorsCalculated == setAncestors); + + pool.addUnchecked(tx7.GetHash(), entry.FromTx(tx7), setAncestors); + BOOST_CHECK_EQUAL(pool.size(), 7); + + // Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ... + sortedOrder.erase(sortedOrder.begin()); + sortedOrder.push_back(tx6.GetHash().ToString()); + sortedOrder.push_back(tx7.GetHash().ToString()); + CheckSort(pool, sortedOrder); + + /* low fee child of tx7 */ + CMutableTransaction tx8 = CMutableTransaction(); + tx8.vin.resize(1); + tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0); + tx8.vin[0].scriptSig = CScript() << OP_11; + tx8.vout.resize(1); + tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx8.vout[0].nValue = 10 * COIN; + setAncestors.insert(pool.mapTx.find(tx7.GetHash())); + pool.addUnchecked(tx8.GetHash(), entry.Fee(0LL).Time(2).FromTx(tx8), setAncestors); + + // Now tx8 should be sorted low, but tx6/tx both high + sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString()); + CheckSort(pool, sortedOrder); + + /* low fee child of tx7 */ + CMutableTransaction tx9 = CMutableTransaction(); + tx9.vin.resize(1); + tx9.vin[0].prevout = COutPoint(tx7.GetHash(), 1); + tx9.vin[0].scriptSig = CScript() << OP_11; + tx9.vout.resize(1); + tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx9.vout[0].nValue = 1 * COIN; + pool.addUnchecked(tx9.GetHash(), entry.Fee(0LL).Time(3).FromTx(tx9), setAncestors); + + // tx9 should be sorted low + BOOST_CHECK_EQUAL(pool.size(), 9); + sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString()); + CheckSort(pool, sortedOrder); + + std::vector snapshotOrder = sortedOrder; + + setAncestors.insert(pool.mapTx.find(tx8.GetHash())); + setAncestors.insert(pool.mapTx.find(tx9.GetHash())); + /* tx10 depends on tx8 and tx9 and has a high fee*/ + CMutableTransaction tx10 = CMutableTransaction(); + tx10.vin.resize(2); + tx10.vin[0].prevout = COutPoint(tx8.GetHash(), 0); + tx10.vin[0].scriptSig = CScript() << OP_11; + tx10.vin[1].prevout = COutPoint(tx9.GetHash(), 0); + tx10.vin[1].scriptSig = CScript() << OP_11; + tx10.vout.resize(1); + tx10.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; + tx10.vout[0].nValue = 10 * COIN; + + setAncestorsCalculated.clear(); + BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(4).FromTx(tx10), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true); + BOOST_CHECK(setAncestorsCalculated == setAncestors); + + pool.addUnchecked(tx10.GetHash(), entry.FromTx(tx10), setAncestors); + + /** + * tx8 and tx9 should both now be sorted higher + * Final order after tx10 is added: + * + * tx3 = 0 (1) + * tx5 = 10000 (1) + * tx1 = 10000 (1) + * tx4 = 15000 (1) + * tx2 = 20000 (1) + * tx9 = 200k (2 txs) + * tx8 = 200k (2 txs) + * tx10 = 200k (1 tx) + * tx6 = 2.2M (5 txs) + * tx7 = 2.2M (4 txs) + */ + sortedOrder.erase(sortedOrder.begin(), sortedOrder.begin()+2); // take out tx9, tx8 from the beginning + sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString()); + sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString()); + sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6 + CheckSort(pool, sortedOrder); + + // there should be 10 transactions in the mempool + BOOST_CHECK_EQUAL(pool.size(), 10); + + // Now try removing tx10 and verify the sort order returns to normal + std::list removed; + pool.remove(pool.mapTx.find(tx10.GetHash())->GetTx(), removed, true); + CheckSort(pool, snapshotOrder); } BOOST_AUTO_TEST_CASE(RemoveWithoutBranchId) { diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 2053de3ed..370048b0d 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -10,6 +10,7 @@ #include "consensus/validation.h" #include "main.h" #include "policy/fees.h" +#include "reverse_iterator.h" #include "streams.h" #include "timedata.h" #include "util.h" @@ -19,13 +20,6 @@ using namespace std; -CTxMemPoolEntry::CTxMemPoolEntry(): - nFee(0), nTxSize(0), nModSize(0), nUsageSize(0), nTime(0), dPriority(0.0), - hadNoDependencies(false), spendsCoinbase(false) -{ - nHeight = MEMPOOL_HEIGHT; -} - CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _dPriority, unsigned int _nHeight, bool poolHasNoInputsOf, @@ -38,6 +32,10 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, nModSize = tx.CalculateModifiedSize(nTxSize); nUsageSize = RecursiveDynamicUsage(tx); feeRate = CFeeRate(nFee, nTxSize); + + nCountWithDescendants = 1; + nSizeWithDescendants = nTxSize; + nFeesWithDescendants = nFee; } CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) @@ -54,6 +52,265 @@ CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const return dResult; } +// Update the given tx for any in-mempool descendants. +// Assumes that setMemPoolChildren is correct for the given tx and all +// descendants. +bool CTxMemPool::UpdateForDescendants(txiter updateIt, int maxDescendantsToVisit, cacheMap &cachedDescendants, const std::set &setExclude) +{ + // Track the number of entries (outside setExclude) that we'd need to visit + // (will bail out if it exceeds maxDescendantsToVisit) + int nChildrenToVisit = 0; + + setEntries stageEntries, setAllDescendants; + stageEntries = GetMemPoolChildren(updateIt); + + while (!stageEntries.empty()) { + const txiter cit = *stageEntries.begin(); + if (cit->IsDirty()) { + // Don't consider any more children if any descendant is dirty + return false; + } + setAllDescendants.insert(cit); + stageEntries.erase(cit); + const setEntries &setChildren = GetMemPoolChildren(cit); + for (const txiter childEntry : setChildren) { + cacheMap::iterator cacheIt = cachedDescendants.find(childEntry); + if (cacheIt != cachedDescendants.end()) { + // We've already calculated this one, just add the entries for this set + // but don't traverse again. + for (const txiter cacheEntry : cacheIt->second) { + // update visit count only for new child transactions + // (outside of setExclude and stageEntries) + if (setAllDescendants.insert(cacheEntry).second && + !setExclude.count(cacheEntry->GetTx().GetHash()) && + !stageEntries.count(cacheEntry)) { + nChildrenToVisit++; + } + } + } else if (!setAllDescendants.count(childEntry)) { + // Schedule for later processing and update our visit count + if (stageEntries.insert(childEntry).second && !setExclude.count(childEntry->GetTx().GetHash())) { + nChildrenToVisit++; + } + } + if (nChildrenToVisit > maxDescendantsToVisit) { + return false; + } + } + } + // setAllDescendants now contains all in-mempool descendants of updateIt. + // Update and add to cached descendant map + int64_t modifySize = 0; + CAmount modifyFee = 0; + int64_t modifyCount = 0; + for (txiter cit : setAllDescendants) { + if (!setExclude.count(cit->GetTx().GetHash())) { + modifySize += cit->GetTxSize(); + modifyFee += cit->GetFee(); + modifyCount++; + cachedDescendants[updateIt].insert(cit); + } + } + mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount)); + return true; +} + +// vHashesToUpdate is the set of transaction hashes from a disconnected block +// which has been re-added to the mempool. +// for each entry, look for descendants that are outside hashesToUpdate, and +// add fee/size information for such descendants to the parent. +void CTxMemPool::UpdateTransactionsFromBlock(const std::vector &vHashesToUpdate) +{ + LOCK(cs); + // For each entry in vHashesToUpdate, store the set of in-mempool, but not + // in-vHashesToUpdate transactions, so that we don't have to recalculate + // descendants when we come across a previously seen entry. + cacheMap mapMemPoolDescendantsToUpdate; + + // Use a set for lookups into vHashesToUpdate (these entries are already + // accounted for in the state of their ancestors) + std::set setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end()); + + // Iterate in reverse, so that whenever we are looking at at a transaction + // we are sure that all in-mempool descendants have already been processed. + // This maximizes the benefit of the descendant cache and guarantees that + // setMemPoolChildren will be updated, an assumption made in + // UpdateForDescendants. + for (const uint256 &hash : reverse_iterate(vHashesToUpdate)) { + // we cache the in-mempool children to avoid duplicate updates + setEntries setChildren; + // calculate children from mapNextTx + txiter it = mapTx.find(hash); + if (it == mapTx.end()) { + continue; + } + std::map::iterator iter = mapNextTx.lower_bound(COutPoint(hash, 0)); + // First calculate the children, and update setMemPoolChildren to + // include them, and update their setMemPoolParents to include this tx. + for (; iter != mapNextTx.end() && iter->first.hash == hash; ++iter) { + const uint256 &childHash = iter->second.ptx->GetHash(); + txiter childIter = mapTx.find(childHash); + assert(childIter != mapTx.end()); + // We can skip updating entries we've encountered before or that + // are in the block (which are already accounted for). + if (setChildren.insert(childIter).second && !setAlreadyIncluded.count(childHash)) { + UpdateChild(it, childIter, true); + UpdateParent(childIter, it, true); + } + } + if (!UpdateForDescendants(it, 100, mapMemPoolDescendantsToUpdate, setAlreadyIncluded)) { + // Mark as dirty if we can't do the calculation. + mapTx.modify(it, set_dirty()); + } + } +} + +bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents /* = true */) +{ + setEntries parentHashes; + const CTransaction &tx = entry.GetTx(); + + if (fSearchForParents) { + // Get parents of this transaction that are in the mempool + // GetMemPoolParents() is only valid for entries in the mempool, so we + // iterate mapTx to find parents. + for (unsigned int i = 0; i < tx.vin.size(); i++) { + txiter piter = mapTx.find(tx.vin[i].prevout.hash); + if (piter != mapTx.end()) { + parentHashes.insert(piter); + if (parentHashes.size() + 1 > limitAncestorCount) { + errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount); + return false; + } + } + } + } else { + // If we're not searching for parents, we require this to be an + // entry in the mempool already. + txiter it = mapTx.iterator_to(entry); + parentHashes = GetMemPoolParents(it); + } + + size_t totalSizeWithAncestors = entry.GetTxSize(); + + while (!parentHashes.empty()) { + txiter stageit = *parentHashes.begin(); + + setAncestors.insert(stageit); + parentHashes.erase(stageit); + totalSizeWithAncestors += stageit->GetTxSize(); + + if (stageit->GetSizeWithDescendants() + entry.GetTxSize() > limitDescendantSize) { + errString = strprintf("exceeds descendant size limit for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limitDescendantSize); + return false; + } else if (stageit->GetCountWithDescendants() + 1 > limitDescendantCount) { + errString = strprintf("too many descendants for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limitDescendantCount); + return false; + } else if (totalSizeWithAncestors > limitAncestorSize) { + errString = strprintf("exceeds ancestor size limit [limit: %u]", limitAncestorSize); + return false; + } + + const setEntries & setMemPoolParents = GetMemPoolParents(stageit); + for (const txiter &phash : setMemPoolParents) { + // If this is a new ancestor, add it. + if (setAncestors.count(phash) == 0) { + parentHashes.insert(phash); + } + if (parentHashes.size() + setAncestors.size() + 1 > limitAncestorCount) { + errString = strprintf("too many unconfirmed ancestors [limit: %u]", limitAncestorCount); + return false; + } + } + } + + return true; +} + +void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors) +{ + setEntries parentIters = GetMemPoolParents(it); + // add or remove this tx as a child of each parent + for (txiter piter : parentIters) { + UpdateChild(piter, it, add); + } + const int64_t updateCount = (add ? 1 : -1); + const int64_t updateSize = updateCount * it->GetTxSize(); + const CAmount updateFee = updateCount * it->GetFee(); + for (txiter ancestorIt : setAncestors) { + mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount)); + } +} + +void CTxMemPool::UpdateChildrenForRemoval(txiter it) +{ + const setEntries &setMemPoolChildren = GetMemPoolChildren(it); + for (txiter updateIt : setMemPoolChildren) { + UpdateParent(updateIt, it, false); + } +} + +void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove) +{ + // For each entry, walk back all ancestors and decrement size associated with this + // transaction + const uint64_t nNoLimit = std::numeric_limits::max(); + for (txiter removeIt : entriesToRemove) { + setEntries setAncestors; + const CTxMemPoolEntry &entry = *removeIt; + std::string dummy; + // Since this is a tx that is already in the mempool, we can call CMPA + // with fSearchForParents = false. If the mempool is in a consistent + // state, then using true or false should both be correct, though false + // should be a bit faster. + // However, if we happen to be in the middle of processing a reorg, then + // the mempool can be in an inconsistent state. In this case, the set + // of ancestors reachable via mapLinks will be the same as the set of + // ancestors whose packages include this transaction, because when we + // add a new transaction to the mempool in addUnchecked(), we assume it + // has no children, and in the case of a reorg where that assumption is + // false, the in-mempool children aren't linked to the in-block tx's + // until UpdateTransactionsFromBlock() is called. + // So if we're being called during a reorg, ie before + // UpdateTransactionsFromBlock() has been called, then mapLinks[] will + // differ from the set of mempool parents we'd calculate by searching, + // and it's important that we use the mapLinks[] notion of ancestor + // transactions as the set of things to update for removal. + CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false); + // Note that UpdateAncestorsOf severs the child links that point to + // removeIt in the entries for the parents of removeIt. This is + // fine since we don't need to use the mempool children of any entries + // to walk back over our ancestors (but we do need the mempool + // parents!) + UpdateAncestorsOf(false, removeIt, setAncestors); + } + // After updating all the ancestor sizes, we can now sever the link between each + // transaction being removed and any mempool children (ie, update setMemPoolParents + // for each direct child of a transaction being removed). + for (txiter removeIt : entriesToRemove) { + UpdateChildrenForRemoval(removeIt); + } +} + +void CTxMemPoolEntry::SetDirty() +{ + nCountWithDescendants = 0; + nSizeWithDescendants = nTxSize; + nFeesWithDescendants = nFee; +} + +void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount) +{ + if (!IsDirty()) { + nSizeWithDescendants += modifySize; + assert(int64_t(nSizeWithDescendants) > 0); + nFeesWithDescendants += modifyFee; + assert(nFeesWithDescendants >= 0); + nCountWithDescendants += modifyCount; + assert(int64_t(nCountWithDescendants) > 0); + } +} + CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee) : nTransactionsUpdated(0) { @@ -98,19 +355,45 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n) } -bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate) +bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate) { // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); weightedTxTree->add(WeightedTxInfo::from(entry.GetTx(), entry.GetFee())); - mapTx.insert(entry); + indexed_transaction_set::iterator newit = mapTx.insert(entry).first; + mapLinks.insert(make_pair(newit, TxLinks())); + + // Update cachedInnerUsage to include contained transaction's usage. + // (When we update the entry for in-mempool parents, memory usage will be + // further updated.) + cachedInnerUsage += entry.DynamicMemoryUsage(); + const CTransaction& tx = mapTx.find(hash)->GetTx(); mapRecentlyAddedTx[tx.GetHash()] = &tx; nRecentlyAddedSequence += 1; - for (unsigned int i = 0; i < tx.vin.size(); i++) + std::set setParentTransactions; + for (unsigned int i = 0; i < tx.vin.size(); i++) { mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); + setParentTransactions.insert(tx.vin[i].prevout.hash); + } + // Don't bother worrying about child transactions of this one. + // Normal case of a new transaction arriving is that there can't be any + // children, because such children would be orphans. + // An exception to that is if a transaction enters that used to be in a block. + // In that case, our disconnect block logic will call UpdateTransactionsFromBlock + // to clean up the mess we're leaving here. + + // Update ancestors with information about this tx + for (const uint256 &phash : setParentTransactions) { + txiter pit = mapTx.find(phash); + if (pit != mapTx.end()) { + UpdateParent(newit, pit, true); + } + } + UpdateAncestorsOf(true, newit, setAncestors); + for (const JSDescription &joinsplit : tx.vJoinSplit) { for (const uint256 &nf : joinsplit.nullifiers) { mapSproutNullifiers[nf] = &tx; @@ -127,6 +410,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, return true; } +// START insightexplorer void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view) { LOCK(cs); @@ -234,14 +518,75 @@ void CTxMemPool::removeSpentIndex(const uint256 txhash) } // END insightexplorer +void CTxMemPool::removeUnchecked(txiter it) +{ + const uint256 hash = it->GetTx().GetHash(); + mapRecentlyAddedTx.erase(hash); + for (const CTxIn& txin : it->GetTx().vin) + mapNextTx.erase(txin.prevout); + for (const JSDescription& joinsplit : it->GetTx().vJoinSplit) { + for (const uint256& nf : joinsplit.nullifiers) { + mapSproutNullifiers.erase(nf); + } + } + for (const SpendDescription &spendDescription : it->GetTx().vShieldedSpend) { + mapSaplingNullifiers.erase(spendDescription.nullifier); + } + + totalTxSize -= it->GetTxSize(); + cachedInnerUsage -= it->DynamicMemoryUsage(); + cachedInnerUsage -= memusage::DynamicUsage(mapLinks[it].parents) + memusage::DynamicUsage(mapLinks[it].children); + mapLinks.erase(it); + mapTx.erase(it); + nTransactionsUpdated++; + minerPolicyEstimator->removeTx(hash); + + // insightexplorer + if (fAddressIndex) + removeAddressIndex(hash); + if (fSpentIndex) + removeSpentIndex(hash); +} + +// Calculates descendants of entry that are not already in setDescendants, and adds to +// setDescendants. Assumes entryit is already a tx in the mempool and setMemPoolChildren +// is correct for tx and all descendants. +// Also assumes that if an entry is in setDescendants already, then all +// in-mempool descendants of it are already in setDescendants as well, so that we +// can save time by not iterating over those entries. +void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants) +{ + setEntries stage; + if (setDescendants.count(entryit) == 0) { + stage.insert(entryit); + } + // Traverse down the children of entry, only adding children that are not + // accounted for in setDescendants already (because those children have either + // already been walked, or will be walked in this iteration). + while (!stage.empty()) { + txiter it = *stage.begin(); + setDescendants.insert(it); + stage.erase(it); + + const setEntries &setChildren = GetMemPoolChildren(it); + for (const txiter &childiter : setChildren) { + if (!setDescendants.count(childiter)) { + stage.insert(childiter); + } + } + } +} + void CTxMemPool::remove(const CTransaction &origTx, std::list& removed, bool fRecursive) { // Remove transaction from memory pool { LOCK(cs); - std::deque txToRemove; - txToRemove.push_back(origTx.GetHash()); - if (fRecursive && !mapTx.count(origTx.GetHash())) { + setEntries txToRemove; + txiter origit = mapTx.find(origTx.GetHash()); + if (origit != mapTx.end()) { + txToRemove.insert(origit); + } else if (fRecursive) { // If recursively removing but origTx isn't in the mempool // be sure to remove any children that are in the pool. This can // happen during chain re-orgs if origTx isn't re-accepted into @@ -250,48 +595,23 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem std::map::iterator it = mapNextTx.find(COutPoint(origTx.GetHash(), i)); if (it == mapNextTx.end()) continue; - txToRemove.push_back(it->second.ptx->GetHash()); + txiter nextit = mapTx.find(it->second.ptx->GetHash()); + assert(nextit != mapTx.end()); + txToRemove.insert(nextit); } } - while (!txToRemove.empty()) - { - uint256 hash = txToRemove.front(); - txToRemove.pop_front(); - if (!mapTx.count(hash)) - continue; - const CTransaction& tx = mapTx.find(hash)->GetTx(); - if (fRecursive) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it == mapNextTx.end()) - continue; - txToRemove.push_back(it->second.ptx->GetHash()); - } - } - mapRecentlyAddedTx.erase(hash); - for (const CTxIn& txin : tx.vin) - mapNextTx.erase(txin.prevout); - for (const JSDescription& joinsplit : tx.vJoinSplit) { - for (const uint256& nf : joinsplit.nullifiers) { - mapSproutNullifiers.erase(nf); - } - } - for (const SpendDescription &spendDescription : tx.vShieldedSpend) { - mapSaplingNullifiers.erase(spendDescription.nullifier); + setEntries setAllRemoves; + if (fRecursive) { + for (txiter it : txToRemove) { + CalculateDescendants(it, setAllRemoves); } - removed.push_back(tx); - totalTxSize -= mapTx.find(hash)->GetTxSize(); - cachedInnerUsage -= mapTx.find(hash)->DynamicMemoryUsage(); - mapTx.erase(hash); - nTransactionsUpdated++; - minerPolicyEstimator->removeTx(hash); - - // insightexplorer - if (fAddressIndex) - removeAddressIndex(hash); - if (fSpentIndex) - removeSpentIndex(hash); + } else { + setAllRemoves.swap(txToRemove); + } + for (txiter it : setAllRemoves) { + removed.push_back(it->GetTx()); } + RemoveStaged(setAllRemoves); for (CTransaction tx : removed) { weightedTxTree->remove(tx.GetHash()); } @@ -480,6 +800,7 @@ void CTxMemPool::removeWithoutBranchId(uint32_t nMemPoolBranchId) void CTxMemPool::clear() { LOCK(cs); + mapLinks.clear(); mapTx.clear(); mapNextTx.clear(); totalTxSize = 0; @@ -510,7 +831,12 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const checkTotal += it->GetTxSize(); innerUsage += it->DynamicMemoryUsage(); const CTransaction& tx = it->GetTx(); + txlinksMap::const_iterator linksiter = mapLinks.find(it); + assert(linksiter != mapLinks.end()); + const TxLinks &links = linksiter->second; + innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children); bool fDependsWait = false; + setEntries setParentCheck; for (const CTxIn &txin : tx.vin) { // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); @@ -518,6 +844,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const const CTransaction& tx2 = it2->GetTx(); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); fDependsWait = true; + setParentCheck.insert(it2); } else { const CCoins* coins = pcoins->AccessCoins(txin.prevout.hash); assert(coins && coins->IsAvailable(txin.prevout.n)); @@ -529,6 +856,32 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const assert(it3->second.n == i); i++; } + assert(setParentCheck == GetMemPoolParents(it)); + // Check children against mapNextTx + CTxMemPool::setEntries setChildrenCheck; + std::map::const_iterator iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0)); + int64_t childSizes = 0; + CAmount childFees = 0; + for (; iter != mapNextTx.end() && iter->first.hash == it->GetTx().GetHash(); ++iter) { + txiter childit = mapTx.find(iter->second.ptx->GetHash()); + assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions + if (setChildrenCheck.insert(childit).second) { + childSizes += childit->GetTxSize(); + childFees += childit->GetFee(); + } + } + assert(setChildrenCheck == GetMemPoolChildren(it)); + // Also check to make sure size/fees is greater than sum with immediate children. + // just a sanity check, not definitive that this calc is correct... + // also check that the size is less than the size of the entire mempool. + if (!it->IsDirty()) { + assert(it->GetSizeWithDescendants() >= childSizes + it->GetTxSize()); + assert(it->GetFeesWithDescendants() >= childFees + it->GetFee()); + } else { + assert(it->GetSizeWithDescendants() == it->GetTxSize()); + assert(it->GetFeesWithDescendants() == it->GetFee()); + } + assert(it->GetFeesWithDescendants() >= 0); // The SaltedTxidHasher is fine to use here; it salts the map keys automatically // with randomness generated on construction. @@ -799,8 +1152,8 @@ size_t CTxMemPool::DynamicMemoryUsage() const { // boost::multi_index_contained is implemented. total += memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 6 * sizeof(void*)) * mapTx.size(); - // Two metadata maps inherited from Bitcoin Core - total += memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas); + // Three metadata maps inherited from Bitcoin Core + total += memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks); // Saves iterating over the full map total += cachedInnerUsage; @@ -849,3 +1202,57 @@ void CTxMemPool::EnsureSizeLimit() { remove(mapTx.find(txId)->GetTx(), removed, true); } } + +void CTxMemPool::RemoveStaged(setEntries &stage) { + AssertLockHeld(cs); + UpdateForRemoveFromMempool(stage); + for (const txiter& it : stage) { + removeUnchecked(it); + } +} + +bool CTxMemPool::addUnchecked(const uint256&hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate) +{ + LOCK(cs); + setEntries setAncestors; + uint64_t nNoLimit = std::numeric_limits::max(); + std::string dummy; + CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy); + return addUnchecked(hash, entry, setAncestors, fCurrentEstimate); +} + +void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add) +{ + setEntries s; + if (add && mapLinks[entry].children.insert(child).second) { + cachedInnerUsage += memusage::IncrementalDynamicUsage(s); + } else if (!add && mapLinks[entry].children.erase(child)) { + cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); + } +} + +void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add) +{ + setEntries s; + if (add && mapLinks[entry].parents.insert(parent).second) { + cachedInnerUsage += memusage::IncrementalDynamicUsage(s); + } else if (!add && mapLinks[entry].parents.erase(parent)) { + cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); + } +} + +const CTxMemPool::setEntries & CTxMemPool::GetMemPoolParents(txiter entry) const +{ + assert (entry != mapTx.end()); + txlinksMap::const_iterator it = mapLinks.find(entry); + assert(it != mapLinks.end()); + return it->second.parents; +} + +const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) const +{ + assert (entry != mapTx.end()); + txlinksMap::const_iterator it = mapLinks.find(entry); + assert(it != mapLinks.end()); + return it->second.children; +} diff --git a/src/txmempool.h b/src/txmempool.h index dfa248c2a..d180ec4ef 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -7,6 +7,7 @@ #define BITCOIN_TXMEMPOOL_H #include +#include #include "amount.h" #include "coins.h" @@ -39,9 +40,25 @@ inline bool AllowFree(double dPriority) /** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; -/** - * CTxMemPool stores these: +class CTxMemPool; + +/** \class CTxMemPoolEntry + * + * CTxMemPoolEntry stores data about the correponding transaction, as well + * as data about all in-mempool transactions that depend on the transaction + * ("descendant" transactions). + * + * When a new entry is added to the mempool, we update the descendant state + * (nCountWithDescendants, nSizeWithDescendants, and nFeesWithDescendants) for + * all ancestors of the newly added transaction. + * + * If updating the descendant state is skipped, we can mark the entry as + * "dirty", and set nSizeWithDescendants/nFeesWithDescendants to equal nTxSize/ + * nTxFee. (This can potentially happen during a reorg, where we limit the + * amount of work we're willing to do to avoid consuming too much CPU.) + * */ + class CTxMemPoolEntry { private: @@ -58,11 +75,19 @@ class CTxMemPoolEntry bool spendsCoinbase; //!< keep track of transactions that spend a coinbase uint32_t nBranchId; //!< Branch ID this transaction is known to commit to, cached for efficiency + // Information about descendants of this transaction that are in the + // mempool; if we remove this transaction we must remove all of these + // descendants as well. if nCountWithDescendants is 0, treat this entry as + // dirty, and nSizeWithDescendants and nFeesWithDescendants will not be + // correct. + uint64_t nCountWithDescendants; //! number of descendant transactions + uint64_t nSizeWithDescendants; //! ... and size + CAmount nFeesWithDescendants; //! ... and total fees (all including us) + public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _dPriority, unsigned int _nHeight, bool poolHasNoInputsOf, bool spendsCoinbase, uint32_t nBranchId); - CTxMemPoolEntry(); CTxMemPoolEntry(const CTxMemPoolEntry& other); const CTransaction& GetTx() const { return this->tx; } @@ -73,12 +98,49 @@ class CTxMemPoolEntry int64_t GetTime() const { return nTime; } unsigned int GetHeight() const { return nHeight; } bool WasClearAtEntry() const { return hadNoDependencies; } + size_t DynamicMemoryUsage() const { return nUsageSize; } + // Adjusts the descendant state, if this entry is not dirty. + void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); + + /** We can set the entry to be dirty if doing the full calculation of in- + * mempool descendants will be too expensive, which can potentially happen + * when re-adding transactions from a block back to the mempool. + */ + void SetDirty(); + bool IsDirty() const { return nCountWithDescendants == 0; } + + uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } + uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } + CAmount GetFeesWithDescendants() const { return nFeesWithDescendants; } + bool GetSpendsCoinbase() const { return spendsCoinbase; } uint32_t GetValidatedBranchId() const { return nBranchId; } }; +// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index. +struct update_descendant_state +{ + update_descendant_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount) : + modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount) + {} + + void operator() (CTxMemPoolEntry &e) + { e.UpdateState(modifySize, modifyFee, modifyCount); } + + private: + int64_t modifySize; + CAmount modifyFee; + int64_t modifyCount; +}; + +struct set_dirty +{ + void operator() (CTxMemPoolEntry &e) + { e.SetDirty(); } +}; + // extracts a TxMemPoolEntry's transaction hash struct mempoolentry_txid { @@ -89,14 +151,49 @@ struct mempoolentry_txid } }; +/** \class CompareTxMemPoolEntryByFee + * + * Sort an entry by max(feerate of entry's tx, feerate with all descendants). + */ class CompareTxMemPoolEntryByFee { +public: + bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const + { + bool fUseADescendants = UseDescendantFeeRate(a); + bool fUseBDescendants = UseDescendantFeeRate(b); + + double aFees = fUseADescendants ? a.GetFeesWithDescendants() : a.GetFee(); + double aSize = fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize(); + + double bFees = fUseBDescendants ? b.GetFeesWithDescendants() : b.GetFee(); + double bSize = fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize(); + + // Avoid division by rewriting (a/b > c/d) as (a*d > c*b). + double f1 = aFees * bSize; + double f2 = aSize * bFees; + + if (f1 == f2) { + return a.GetTime() >= b.GetTime(); + } + return f1 < f2; + } + + // Calculate which feerate to use for an entry (avoiding division). + bool UseDescendantFeeRate(const CTxMemPoolEntry &a) const + { + double f1 = (double)a.GetFee() * a.GetSizeWithDescendants(); + double f2 = (double)a.GetFeesWithDescendants() * a.GetTxSize(); + return f2 > f1; + } +}; + +class CompareTxMemPoolEntryByEntryTime +{ public: bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) { - if (a.GetFeeRate() == b.GetFeeRate()) - return a.GetTime() < b.GetTime(); - return a.GetFeeRate() > b.GetFeeRate(); + return a.GetTime() < b.GetTime(); } }; @@ -125,6 +222,72 @@ class CInPoint * are added to the pool: if a new transaction double-spends * an input of a transaction in the pool, it is dropped, * as are non-standard transactions. + * + * CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping: + * + * mapTx is a boost::multi_index that sorts the mempool on 2 criteria: + * - transaction hash + * - feerate [we use max(feerate of tx, feerate of tx with all descendants)] + * - mining score (feerate modified by any fee deltas from PrioritiseTransaction) + * + * Note: the term "descendant" refers to in-mempool transactions that depend on + * this one, while "ancestor" refers to in-mempool transactions that a given + * transaction depends on. + * + * In order for the feerate sort to remain correct, we must update transactions + * in the mempool when new descendants arrive. To facilitate this, we track + * the set of in-mempool direct parents and direct children in mapLinks. Within + * each CTxMemPoolEntry, we track the size and fees of all descendants. + * + * Usually when a new transaction is added to the mempool, it has no in-mempool + * children (because any such children would be an orphan). So in + * addUnchecked(), we: + * - update a new entry's setMemPoolParents to include all in-mempool parents + * - update the new entry's direct parents to include the new tx as a child + * - update all ancestors of the transaction to include the new tx's size/fee + * + * When a transaction is removed from the mempool, we must: + * - update all in-mempool parents to not track the tx in setMemPoolChildren + * - update all ancestors to not include the tx's size/fees in descendant state + * - update all in-mempool children to not include it as a parent + * + * These happen in UpdateForRemoveFromMempool(). (Note that when removing a + * transaction along with its descendants, we must calculate that set of + * transactions to be removed before doing the removal, or else the mempool can + * be in an inconsistent state where it's impossible to walk the ancestors of + * a transaction.) + * + * In the event of a reorg, the assumption that a newly added tx has no + * in-mempool children is false. In particular, the mempool is in an + * inconsistent state while new transactions are being added, because there may + * be descendant transactions of a tx coming from a disconnected block that are + * unreachable from just looking at transactions in the mempool (the linking + * transactions may also be in the disconnected block, waiting to be added). + * Because of this, there's not much benefit in trying to search for in-mempool + * children in addUnchecked(). Instead, in the special case of transactions + * being added from a disconnected block, we require the caller to clean up the + * state, to account for in-mempool, out-of-block descendants for all the + * in-block transactions by calling UpdateTransactionsFromBlock(). Note that + * until this is called, the mempool state is not consistent, and in particular + * mapLinks may not be correct (and therefore functions like + * CalculateMemPoolAncestors() and CalculateDescendants() that rely + * on them to walk the mempool are not generally safe to use). + * + * Computational limits: + * + * Updating all in-mempool ancestors of a newly added transaction can be slow, + * if no bound exists on how many in-mempool ancestors there may be. + * CalculateMemPoolAncestors() takes configurable limits that are designed to + * prevent these calculations from being too CPU intensive. + * + * Adding transactions from a disconnected block can be very time consuming, + * because we don't have a way to limit the number of in-mempool descendants. + * To bound CPU processing, we limit the amount of work we're willing to do + * to properly update the descendant information for a tx being added from + * a disconnected block. If we would exceed the limit, then we instead mark + * the entry as "dirty", and set the feerate for sorting purposes to be equal + * the feerate of the transaction without any descendants. + * */ class CTxMemPool { @@ -163,15 +326,37 @@ class CTxMemPool mutable CCriticalSection cs; indexed_transaction_set mapTx; + typedef indexed_transaction_set::nth_index<0>::type::iterator txiter; + struct CompareIteratorByHash { + bool operator()(const txiter &a, const txiter &b) const { + return a->GetTx().GetHash() < b->GetTx().GetHash(); + } + }; + typedef std::set setEntries; - private: - // insightexplorer - std::map mapAddress; - std::map > mapAddressInserted; - std::map mapSpent; - std::map> mapSpentInserted; +private: + typedef std::map cacheMap; + + struct TxLinks { + setEntries parents; + setEntries children; + }; - public: + typedef std::map txlinksMap; + txlinksMap mapLinks; + + const setEntries & GetMemPoolParents(txiter entry) const; + const setEntries & GetMemPoolChildren(txiter entry) const; + void UpdateParent(txiter entry, txiter parent, bool add); + void UpdateChild(txiter entry, txiter child, bool add); + + // insightexplorer + std::map mapAddress; + std::map > mapAddressInserted; + std::map mapSpent; + std::map> mapSpentInserted; + +public: std::map mapNextTx; std::map > mapDeltas; @@ -187,7 +372,12 @@ class CTxMemPool void check(const CCoinsViewCache *pcoins) const; void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast(dFrequency * 4294967295.0); } + // addUnchecked must updated state for all ancestors of a given transaction, + // to track size/count of descendant transactions. First version of + // addUnchecked can be used to have it call CalculateMemPoolAncestors(), and + // then invoke the second version. bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true); + bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true); // START insightexplorer void addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view); @@ -224,6 +414,35 @@ class CTxMemPool void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta); void ClearPrioritisation(const uint256 hash); +public: + /** Remove a set of transactions from the mempool. + * If a transaction is in this set, then all in-mempool descendants must + * also be in the set.*/ + void RemoveStaged(setEntries &stage); + + /** When adding transactions from a disconnected block back to the mempool, + * new mempool entries may have children in the mempool (which is generally + * not the case when otherwise adding transactions). + * UpdateTransactionsFromBlock() will find child transactions and update the + * descendant state for each transaction in hashesToUpdate (excluding any + * child transactions present in hashesToUpdate, which are already accounted + * for). Note: hashesToUpdate should be the set of transactions from the + * disconnected block that have been accepted back into the mempool. + */ + void UpdateTransactionsFromBlock(const std::vector &hashesToUpdate); + + /** Try to calculate all in-mempool ancestors of entry. + * (these are all calculated including the tx itself) + * limitAncestorCount = max number of ancestors + * limitAncestorSize = max size of ancestors + * limitDescendantCount = max number of descendants any ancestor can have + * limitDescendantSize = max size of descendants any ancestor can have + * errString = populated with error reason if any limits are hit + * fSearchForParents = whether to search a tx's vin for in-mempool parents, or + * look up parents from mapLinks. Must be true for entries not in the mempool + */ + bool CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents = true); + bool nullifierExists(const uint256& nullifier, ShieldedType type) const; std::pair, uint64_t> DrainRecentlyAdded(); @@ -272,6 +491,48 @@ class CTxMemPool bool IsRecentlyEvicted(const uint256& txId); // If the mempool size limit is exceeded, this evicts transactions from the mempool until it is below capacity void EnsureSizeLimit(); + +private: + /** UpdateForDescendants is used by UpdateTransactionsFromBlock to update + * the descendants for a single transaction that has been added to the + * mempool but may have child transactions in the mempool, eg during a + * chain reorg. setExclude is the set of descendant transactions in the + * mempool that must not be accounted for (because any descendants in + * setExclude were added to the mempool after the transaction being + * updated and hence their state is already reflected in the parent + * state). + * + * If updating an entry requires looking at more than maxDescendantsToVisit + * transactions, outside of the ones in setExclude, then give up. + * + * cachedDescendants will be updated with the descendants of the transaction + * being updated, so that future invocations don't need to walk the + * same transaction again, if encountered in another transaction chain. + */ + bool UpdateForDescendants(txiter updateIt, + int maxDescendantsToVisit, + cacheMap &cachedDescendants, + const std::set &setExclude); + /** Update ancestors of hash to add/remove it as a descendant transaction. */ + void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors); + /** For each transaction being removed, update ancestors and any direct children. */ + void UpdateForRemoveFromMempool(const setEntries &entriesToRemove); + /** Sever link between specified transaction and direct children. */ + void UpdateChildrenForRemoval(txiter entry); + /** Populate setDescendants with all in-mempool descendants of hash. + * Assumes that setDescendants includes all in-mempool descendants of anything + * already in it. */ + void CalculateDescendants(txiter it, setEntries &setDescendants); + + /** Before calling removeUnchecked for a given transaction, + * UpdateForRemoveFromMempool must be called on the entire (dependent) set + * of transactions being removed at the same time. We use each + * CTxMemPoolEntry's setMemPoolParents in order to walk ancestors of a + * given transaction that is removed, so we can't remove intermediate + * transactions in a chain before we've updated all the state for the + * removal. + */ + void removeUnchecked(txiter entry); }; /** From 34b999ac8c64ad530ae48bae1ef5a4a42ae265bd Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 16:38:17 +0000 Subject: [PATCH 107/146] Limit mempool by throwing away the cheapest txn and setting min relay fee to it --- src/amount.h | 1 + src/init.cpp | 8 ++ src/main.cpp | 51 +++++---- src/main.h | 6 +- src/rpc/rawtransaction.cpp | 2 +- src/test/mempool_tests.cpp | 155 +++++++++++++++++++++++++++ src/test/txvalidationcache_tests.cpp | 2 +- src/txmempool.cpp | 92 +++++++++++++++- src/txmempool.h | 49 +++++++-- src/wallet/wallet.cpp | 2 +- 10 files changed, 335 insertions(+), 33 deletions(-) diff --git a/src/amount.h b/src/amount.h index fb1f9e2eb..88e283d10 100644 --- a/src/amount.h +++ b/src/amount.h @@ -51,6 +51,7 @@ class CFeeRate friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; } friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; } friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; } + CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; } std::string ToString() const; ADD_SERIALIZE_METHODS; diff --git a/src/init.cpp b/src/init.cpp index 5063f82fe..de5e5c35f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -339,6 +339,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-loadblock=", _("Imports blocks from external blk000??.dat file on startup")); strUsage += HelpMessageOpt("-debuglogfile=", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE)); strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); + strUsage += HelpMessageOpt("-maxmempool=", strprintf(_("Keep the transaction memory pool below megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); + strUsage += HelpMessageOpt("-mempoolexpiry=", strprintf(_("Do not keep transactions in the mempool longer than hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); strUsage += HelpMessageOpt("-par=", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); #ifndef WIN32 @@ -1128,6 +1130,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); + // -mempoollimit limits + int64_t nMempoolSizeLimit = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + int64_t nMempoolDescendantSizeLimit = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; + if (nMempoolSizeLimit < 0 || nMempoolSizeLimit < nMempoolDescendantSizeLimit * 40) + return InitError(strprintf(_("Error: -maxmempool must be at least %d MB"), GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) / 25)); + // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); if (nScriptCheckThreads <= 0) diff --git a/src/main.cpp b/src/main.cpp index be50fcfb8..e7039c3fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1227,17 +1227,14 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return true; } -CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree) +CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree) { - { - LOCK(mempool.cs); - uint256 hash = tx.GetHash(); - double dPriorityDelta = 0; - CAmount nFeeDelta = 0; - mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); - if (dPriorityDelta > 0 || nFeeDelta > 0) - return 0; - } + uint256 hash = tx.GetHash(); + double dPriorityDelta = 0; + CAmount nFeeDelta = 0; + pool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); + if (dPriorityDelta > 0 || nFeeDelta > 0) + return 0; CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); @@ -1258,7 +1255,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectAbsurdFee) + bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -1419,7 +1416,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // it has passed ContextualCheckInputs and therefore this is correct. auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId); unsigned int nSize = entry.GetTxSize(); // Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany. @@ -1427,14 +1424,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // In future we will we have more accurate and dynamic computation of fees for tx with joinsplits. } else { // Don't accept it if it can't get into a block - CAmount txMinFee = GetMinRelayFee(tx, nSize, true); + CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); if (fLimitFree && nFees < txMinFee) return state.DoS(0, error("%s: not enough fees %s, %d < %d", __func__, hash.ToString(), nFees, txMinFee), REJECT_INSUFFICIENTFEE, "insufficient fee"); } - // Require that free transactions have sufficient priority to be mined in the next block. - if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { + CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); + if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) { + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false); + } else if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { + // Require that free transactions have sufficient priority to be mined in the next block. return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } @@ -1510,6 +1510,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload(Params())); + // trim mempool and check if tx was trimmed + if (!fOverrideMempoolLimit) { + int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + + pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + if (!pool.exists(tx.GetHash())) + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); + } + // Add memory address index if (fAddressIndex) { pool.addAddressIndex(entry, view); @@ -2966,7 +2977,7 @@ bool static DisconnectTip(CValidationState &state, const CChainParams& chainpara // ignore validation errors in resurrected transactions list removed; CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) { + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { mempool.remove(tx, removed, true); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); @@ -3282,7 +3293,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c if (fBlocksDisconnected) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); + mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); } + mempool.removeWithoutBranchId( CurrentEpochBranchId(chainActive.Tip()->nHeight + 1, chainparams.GetConsensus())); mempool.check(pcoinsTip); @@ -3426,6 +3439,8 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C } } + mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. BlockMap::iterator it = mapBlockIndex.begin(); @@ -5844,10 +5859,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx); vWorkQueue.push_back(inv.hash); - LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", + LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", pfrom->id, pfrom->cleanSubVer, tx.GetHash().ToString(), - mempool.size()); + mempool.size(), mempool.DynamicMemoryUsage() / 1000); // Recursively process any orphan transactions that depended on this one set setMisbehaving; diff --git a/src/main.h b/src/main.h index e7b3d2838..0b5603c93 100644 --- a/src/main.h +++ b/src/main.h @@ -79,6 +79,10 @@ static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 900; static const unsigned int DEFAULT_DESCENDANT_LIMIT = 1000; /** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */ static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 2500; +/** Default for -maxmempool, maximum megabytes of mempool memory usage */ +static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; +/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */ +static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 72; /** Default for -txexpirydelta, in number of blocks */ static const unsigned int DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA = 20; static const unsigned int DEFAULT_POST_BLOSSOM_TX_EXPIRY_DELTA = DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA * Consensus::BLOSSOM_POW_TARGET_SPACING_RATIO; @@ -319,7 +323,7 @@ void PruneAndFlush(); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectAbsurdFee=false); + bool* pfMissingInputs, bool fOverrideMempoolLimit=false, bool fRejectAbsurdFee=false); /** Find block at height in a fork **/ const CBlockIndex* FindBlockAtHeight(int nHeight, const CBlockIndex* pIndex); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 497a111ba..91a3910d4 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1100,7 +1100,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) { + if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, !fOverrideFees)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 2bdfccfaa..4ec84f3b0 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -287,6 +287,161 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) CheckSort(pool, snapshotOrder); } +BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) +{ + CTxMemPool pool(CFeeRate(1000)); + TestMemPoolEntryHelper entry; + entry.dPriority = 10.0; + + CMutableTransaction tx1 = CMutableTransaction(); + tx1.vin.resize(1); + tx1.vin[0].scriptSig = CScript() << OP_1; + tx1.vout.resize(1); + tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; + tx1.vout[0].nValue = 10 * COIN; + pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1, &pool)); + + CMutableTransaction tx2 = CMutableTransaction(); + tx2.vin.resize(1); + tx2.vin[0].scriptSig = CScript() << OP_2; + tx2.vout.resize(1); + tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; + tx2.vout[0].nValue = 10 * COIN; + pool.addUnchecked(tx2.GetHash(), entry.Fee(5000LL).FromTx(tx2, &pool)); + + pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing + BOOST_CHECK(pool.exists(tx1.GetHash())); + BOOST_CHECK(pool.exists(tx2.GetHash())); + + pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction + BOOST_CHECK(pool.exists(tx1.GetHash())); + BOOST_CHECK(!pool.exists(tx2.GetHash())); + + pool.addUnchecked(tx2.GetHash(), entry.FromTx(tx2, &pool)); + CMutableTransaction tx3 = CMutableTransaction(); + tx3.vin.resize(1); + tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0); + tx3.vin[0].scriptSig = CScript() << OP_2; + tx3.vout.resize(1); + tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; + tx3.vout[0].nValue = 10 * COIN; + pool.addUnchecked(tx3.GetHash(), entry.Fee(20000LL).FromTx(tx3, &pool)); + + pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP) + BOOST_CHECK(!pool.exists(tx1.GetHash())); + BOOST_CHECK(pool.exists(tx2.GetHash())); + BOOST_CHECK(pool.exists(tx3.GetHash())); + + pool.TrimToSize(::GetSerializeSize(CTransaction(tx1), SER_NETWORK, PROTOCOL_VERSION)); // mempool is limited to tx1's size in memory usage, so nothing fits + BOOST_CHECK(!pool.exists(tx1.GetHash())); + BOOST_CHECK(!pool.exists(tx2.GetHash())); + BOOST_CHECK(!pool.exists(tx3.GetHash())); + + CFeeRate maxFeeRateRemoved(25000, ::GetSerializeSize(CTransaction(tx3), SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize(CTransaction(tx2), SER_NETWORK, PROTOCOL_VERSION)); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); + + CMutableTransaction tx4 = CMutableTransaction(); + tx4.vin.resize(2); + tx4.vin[0].prevout.SetNull(); + tx4.vin[0].scriptSig = CScript() << OP_4; + tx4.vin[1].prevout.SetNull(); + tx4.vin[1].scriptSig = CScript() << OP_4; + tx4.vout.resize(2); + tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL; + tx4.vout[0].nValue = 10 * COIN; + tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL; + tx4.vout[1].nValue = 10 * COIN; + + CMutableTransaction tx5 = CMutableTransaction(); + tx5.vin.resize(2); + tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0); + tx5.vin[0].scriptSig = CScript() << OP_4; + tx5.vin[1].prevout.SetNull(); + tx5.vin[1].scriptSig = CScript() << OP_5; + tx5.vout.resize(2); + tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL; + tx5.vout[0].nValue = 10 * COIN; + tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL; + tx5.vout[1].nValue = 10 * COIN; + + CMutableTransaction tx6 = CMutableTransaction(); + tx6.vin.resize(2); + tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1); + tx6.vin[0].scriptSig = CScript() << OP_4; + tx6.vin[1].prevout.SetNull(); + tx6.vin[1].scriptSig = CScript() << OP_6; + tx6.vout.resize(2); + tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL; + tx6.vout[0].nValue = 10 * COIN; + tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL; + tx6.vout[1].nValue = 10 * COIN; + + CMutableTransaction tx7 = CMutableTransaction(); + tx7.vin.resize(2); + tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0); + tx7.vin[0].scriptSig = CScript() << OP_5; + tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0); + tx7.vin[1].scriptSig = CScript() << OP_6; + tx7.vout.resize(2); + tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; + tx7.vout[0].nValue = 10 * COIN; + tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; + tx7.vout[0].nValue = 10 * COIN; + + pool.addUnchecked(tx4.GetHash(), entry.Fee(7000LL).FromTx(tx4, &pool)); + pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool)); + pool.addUnchecked(tx6.GetHash(), entry.Fee(1100LL).FromTx(tx6, &pool)); + pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool)); + + // we only require this remove, at max, 2 txn, because its not clear what we're really optimizing for aside from that + pool.TrimToSize(pool.DynamicMemoryUsage() - 1); + BOOST_CHECK(pool.exists(tx4.GetHash())); + BOOST_CHECK(pool.exists(tx6.GetHash())); + BOOST_CHECK(!pool.exists(tx7.GetHash())); + + if (!pool.exists(tx5.GetHash())) + pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool)); + pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool)); + + pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7 + BOOST_CHECK(pool.exists(tx4.GetHash())); + BOOST_CHECK(!pool.exists(tx5.GetHash())); + BOOST_CHECK(pool.exists(tx6.GetHash())); + BOOST_CHECK(!pool.exists(tx7.GetHash())); + + pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool)); + pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool)); + + std::vector vtx; + std::list conflicts; + SetMockTime(42); + SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); + // ... we should keep the same min fee until we get a block + pool.removeForBlock(vtx, 1, conflicts); + SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2); + // ... then feerate should drop 1/2 each halflife + + SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4); + // ... with a 1/2 halflife when mempool is < 1/2 its target size + + SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8); + // ... with a 1/4 halflife when mempool is < 1/4 its target size + + SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000); + // ... but feerate should never drop below 1000 + + SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0); + // ... unless it has gone all the way to 0 (after getting past 1000/2) + + SetMockTime(0); +} + BOOST_AUTO_TEST_CASE(RemoveWithoutBranchId) { CTxMemPool pool(CFeeRate(0)); TestMemPoolEntryHelper entry; diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index cdf25a00d..d58d9ed77 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -23,7 +23,7 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, tx, false, NULL, false); + return AcceptToMemoryPool(mempool, state, tx, false, NULL, true, false); } #ifdef ENABLE_MINING diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 370048b0d..f83ba037e 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -15,6 +15,7 @@ #include "timedata.h" #include "util.h" #include "utilmoneystr.h" +#include "utiltime.h" #include "validationinterface.h" #include "version.h" @@ -311,15 +312,18 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t } } -CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee) : +CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) : nTransactionsUpdated(0) { + clear(); + // Sanity checks off by default for performance, because otherwise // accepting transactions becomes O(N^2) where N is the number // of transactions in the pool nCheckFrequency = 0; - minerPolicyEstimator = new CBlockPolicyEstimator(_minRelayFee); + minerPolicyEstimator = new CBlockPolicyEstimator(_minReasonableRelayFee); + minReasonableRelayFee = _minReasonableRelayFee; } CTxMemPool::~CTxMemPool() @@ -773,6 +777,8 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigned i } // After the txs in the new block have been removed from the mempool, update policy estimates minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate); + lastRollingFeeUpdate = GetTime(); + blockSinceLastRollingFeeBump = true; } /** @@ -805,6 +811,9 @@ void CTxMemPool::clear() mapNextTx.clear(); totalTxSize = 0; cachedInnerUsage = 0; + lastRollingFeeUpdate = GetTime(); + blockSinceLastRollingFeeBump = false; + rollingMinimumFeeRate = 0; ++nTransactionsUpdated; } @@ -1055,10 +1064,10 @@ void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string str LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta)); } -void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) +void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) const { LOCK(cs); - std::map >::iterator pos = mapDeltas.find(hash); + std::map >::const_iterator pos = mapDeltas.find(hash); if (pos == mapDeltas.end()) return; const std::pair &deltas = pos->second; @@ -1211,6 +1220,22 @@ void CTxMemPool::RemoveStaged(setEntries &stage) { } } +int CTxMemPool::Expire(int64_t time) { + LOCK(cs); + indexed_transaction_set::nth_index<2>::type::iterator it = mapTx.get<2>().begin(); + setEntries toremove; + while (it != mapTx.get<2>().end() && it->GetTime() < time) { + toremove.insert(mapTx.project<0>(it)); + it++; + } + setEntries stage; + for (txiter removeit : toremove) { + CalculateDescendants(removeit, stage); + } + RemoveStaged(stage); + return stage.size(); +} + bool CTxMemPool::addUnchecked(const uint256&hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate) { LOCK(cs); @@ -1256,3 +1281,62 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons assert(it != mapLinks.end()); return it->second.children; } + +CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { + LOCK(cs); + if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0) + return CFeeRate(rollingMinimumFeeRate); + + int64_t time = GetTime(); + if (time > lastRollingFeeUpdate + 10) { + double halflife = ROLLING_FEE_HALFLIFE; + if (DynamicMemoryUsage() < sizelimit / 4) + halflife /= 4; + else if (DynamicMemoryUsage() < sizelimit / 2) + halflife /= 2; + + rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife); + lastRollingFeeUpdate = time; + + if (rollingMinimumFeeRate < minReasonableRelayFee.GetFeePerK() / 2) { + rollingMinimumFeeRate = 0; + return CFeeRate(0); + } + } + return std::max(CFeeRate(rollingMinimumFeeRate), minReasonableRelayFee); +} + +void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { + AssertLockHeld(cs); + if (rate.GetFeePerK() > rollingMinimumFeeRate) { + rollingMinimumFeeRate = rate.GetFeePerK(); + blockSinceLastRollingFeeBump = false; + } +} + +void CTxMemPool::TrimToSize(size_t sizelimit) { + LOCK(cs); + + unsigned nTxnRemoved = 0; + CFeeRate maxFeeRateRemoved(0); + while (DynamicMemoryUsage() > sizelimit) { + indexed_transaction_set::nth_index<1>::type::iterator it = mapTx.get<1>().begin(); + + // We set the new mempool min fee to the feerate of the removed set, plus the + // "minimum reasonable fee rate" (ie some value under which we consider txn + // to have 0 fee). This way, we don't allow txn to enter mempool with feerate + // equal to txn which were removed with no block in between. + CFeeRate removed(it->GetFeesWithDescendants(), it->GetSizeWithDescendants()); + removed += minReasonableRelayFee; + trackPackageRemoved(removed); + maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed); + + setEntries stage; + CalculateDescendants(mapTx.project<0>(it), stage); + RemoveStaged(stage); + nTxnRemoved += stage.size(); + } + + if (maxFeeRateRemoved > CFeeRate(0)) + LogPrint("mempool", "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString()); +} diff --git a/src/txmempool.h b/src/txmempool.h index d180ec4ef..bea3dab2f 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -92,7 +92,7 @@ class CTxMemPoolEntry const CTransaction& GetTx() const { return this->tx; } double GetPriority(unsigned int currentHeight) const; - CAmount GetFee() const { return nFee; } + const CAmount& GetFee() const { return nFee; } CFeeRate GetFeeRate() const { return feeRate; } size_t GetTxSize() const { return nTxSize; } int64_t GetTime() const { return nTime; } @@ -191,7 +191,7 @@ class CompareTxMemPoolEntryByFee class CompareTxMemPoolEntryByEntryTime { public: - bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) + bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const { return a.GetTime() < b.GetTime(); } @@ -225,10 +225,10 @@ class CInPoint * * CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping: * - * mapTx is a boost::multi_index that sorts the mempool on 2 criteria: + * mapTx is a boost::multi_index that sorts the mempool on 3 criteria: * - transaction hash * - feerate [we use max(feerate of tx, feerate of tx with all descendants)] - * - mining score (feerate modified by any fee deltas from PrioritiseTransaction) + * - time in mempool * * Note: the term "descendant" refers to in-mempool transactions that depend on * this one, while "ancestor" refers to in-mempool transactions that a given @@ -299,6 +299,14 @@ class CTxMemPool uint64_t totalTxSize = 0; //!< sum of all mempool tx' byte sizes uint64_t cachedInnerUsage; //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves) + CFeeRate minReasonableRelayFee; + + mutable int64_t lastRollingFeeUpdate; + mutable bool blockSinceLastRollingFeeBump; + mutable double rollingMinimumFeeRate; //! minimum fee to get into the pool, decreases exponentially + + void trackPackageRemoved(const CFeeRate& rate); + std::map mapRecentlyAddedTx; uint64_t nRecentlyAddedSequence = 0; uint64_t nNotifiedSequence = 0; @@ -311,6 +319,9 @@ class CTxMemPool void checkNullifiers(ShieldedType type) const; public: + + static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing + typedef boost::multi_index_container< CTxMemPoolEntry, boost::multi_index::indexed_by< @@ -320,6 +331,11 @@ class CTxMemPool boost::multi_index::ordered_non_unique< boost::multi_index::identity, CompareTxMemPoolEntryByFee + >, + // sorted by entry time + boost::multi_index::ordered_non_unique< + boost::multi_index::identity, + CompareTxMemPoolEntryByEntryTime > > > indexed_transaction_set; @@ -360,7 +376,12 @@ class CTxMemPool std::map mapNextTx; std::map > mapDeltas; - CTxMemPool(const CFeeRate& _minRelayFee); + /** Create a new CTxMemPool. + * minReasonableRelayFee should be a feerate which is, roughly, somewhere + * around what it "costs" to relay a transaction around the network and + * below which we would reasonably say a transaction has 0-effective-fee. + */ + CTxMemPool(const CFeeRate& _minReasonableRelayFee); ~CTxMemPool(); /** @@ -411,7 +432,7 @@ class CTxMemPool /** Affect CreateNewBlock prioritisation of transactions */ void PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta); - void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta); + void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) const; void ClearPrioritisation(const uint256 hash); public: @@ -448,7 +469,21 @@ class CTxMemPool std::pair, uint64_t> DrainRecentlyAdded(); void SetNotifiedSequence(uint64_t recentlyAddedSequence); bool IsFullyNotified(); - + + /** The minimum fee to get into the mempool, which may itself not be enough + * for larger-sized transactions. + * The minReasonableRelayFee constructor arg is used to bound the time it + * takes the fee rate to go back down all the way to 0. When the feerate + * would otherwise be half of this, it is set to 0 instead. + */ + CFeeRate GetMinFee(size_t sizelimit) const; + + /** Remove transactions from the mempool until its dynamic size is <= sizelimit. */ + void TrimToSize(size_t sizelimit); + + /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */ + int Expire(int64_t time); + unsigned long size() { LOCK(cs); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 99f618e87..81a950df8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5077,7 +5077,7 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) { CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, fRejectAbsurdFee); } /** From 25d200b6bff12ecd8f77ba269eaf414b25c41b37 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 17:12:32 +0000 Subject: [PATCH 108/146] Remove UTXO cache entries when the tx they were added for is removed/does not enter mempool --- src/coins.cpp | 14 +++++++++++++ src/coins.h | 13 ++++++++++++ src/main.cpp | 52 +++++++++++++++++++++++++++++++++++------------ src/txmempool.cpp | 22 ++++++++++++++++++-- src/txmempool.h | 7 +++++-- 5 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/coins.cpp b/src/coins.cpp index 158499a59..de8324aaa 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -387,6 +387,11 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { return (it != cacheCoins.end() && !it->second.coins.vout.empty()); } +bool CCoinsViewCache::HaveCoinsInCache(const uint256 &txid) const { + CCoinsMap::const_iterator it = cacheCoins.find(txid); + return it != cacheCoins.end(); +} + uint256 CCoinsViewCache::GetBestBlock() const { if (hashBlock.IsNull()) hashBlock = base->GetBestBlock(); @@ -537,6 +542,15 @@ bool CCoinsViewCache::Flush() { return fOk; } +void CCoinsViewCache::Uncache(const uint256& hash) +{ + CCoinsMap::iterator it = cacheCoins.find(hash); + if (it != cacheCoins.end() && it->second.flags == 0) { + cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage(); + cacheCoins.erase(it); + } +} + unsigned int CCoinsViewCache::GetCacheSize() const { return cacheCoins.size(); } diff --git a/src/coins.h b/src/coins.h index 4714796b1..088900e6d 100644 --- a/src/coins.h +++ b/src/coins.h @@ -490,6 +490,13 @@ class CCoinsViewCache : public CCoinsViewBacked // Marks nullifiers for a given transaction as spent or not. void SetNullifiers(const CTransaction& tx, bool spent); + /** + * Check if we have the given tx already loaded in this cache. + * The semantics are the same as HaveCoins(), but no calls to + * the backing CCoinsView are made. + */ + bool HaveCoinsInCache(const uint256 &txid) const; + /** * Return a pointer to CCoins in the cache, or NULL if not found. This is * more efficient than GetCoins. Modifications to other cache entries are @@ -521,6 +528,12 @@ class CCoinsViewCache : public CCoinsViewBacked */ bool Flush(); + /** + * Removes the transaction with the given hash from the cache, if it is + * not modified. + */ + void Uncache(const uint256 &txid); + //! Calculate the size of the cache (in number of transactions) unsigned int GetCacheSize() const; diff --git a/src/main.cpp b/src/main.cpp index e7039c3fe..423141258 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1227,6 +1227,17 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return true; } +void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { + int expired = pool.Expire(GetTime() - age); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + + std::vector vNoSpendsRemaining; + pool.TrimToSize(limit, &vNoSpendsRemaining); + for(const uint256& removed : vNoSpendsRemaining) + pcoinsTip->Uncache(removed); +} + CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree) { uint256 hash = tx.GetHash(); @@ -1253,9 +1264,9 @@ CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned return nMinFee; } - -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) +bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, + bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, + std::vector& vHashTxnToUncache) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -1346,13 +1357,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa view.SetBackend(viewMemPool); // do we already have it? - if (view.HaveCoins(hash)) - return false; + bool fHadTxInCache = pcoinsTip->HaveCoinsInCache(hash); + if (view.HaveCoins(hash)) { + if (!fHadTxInCache) + vHashTxnToUncache.push_back(hash); + return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known"); + } // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), // and only helps with filling in pfMissingInputs (to determine missing vs spent). for (const CTxIn txin : tx.vin) { + if (!pcoinsTip->HaveCoinsInCache(txin.prevout.hash)) + vHashTxnToUncache.push_back(txin.prevout.hash); if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) *pfMissingInputs = true; @@ -1512,12 +1529,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // trim mempool and check if tx was trimmed if (!fOverrideMempoolLimit) { - int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - if (expired != 0) - LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); - - pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); - if (!pool.exists(tx.GetHash())) + LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + if (!pool.exists(hash)) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); } @@ -1589,6 +1602,18 @@ bool GetAddressUnspent(const uint160& addressHash, int type, return true; } +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, + bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) +{ + std::vector vHashTxToUncache; + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, fRejectAbsurdFee, vHashTxToUncache); + if (!res) { + for (const uint256& hashTx : vHashTxToUncache) + pcoinsTip->Uncache(hashTx); + } + return res; +} + /** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) { @@ -3293,7 +3318,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c if (fBlocksDisconnected) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); - mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); } mempool.removeWithoutBranchId( @@ -3439,7 +3464,7 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C } } - mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); + LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. @@ -5965,6 +5990,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } + FlushStateToDisk(state, FLUSH_STATE_PERIODIC); } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f83ba037e..addebc43c 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1314,7 +1314,7 @@ void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { } } -void CTxMemPool::TrimToSize(size_t sizelimit) { +void CTxMemPool::TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRemaining) { LOCK(cs); unsigned nTxnRemoved = 0; @@ -1333,8 +1333,26 @@ void CTxMemPool::TrimToSize(size_t sizelimit) { setEntries stage; CalculateDescendants(mapTx.project<0>(it), stage); - RemoveStaged(stage); nTxnRemoved += stage.size(); + + std::vector txn; + if (pvNoSpendsRemaining) { + txn.reserve(stage.size()); + for(txiter it : stage) + txn.push_back(it->GetTx()); + } + RemoveStaged(stage); + if (pvNoSpendsRemaining) { + for (const CTransaction& tx : txn) { + for (const CTxIn& txin : tx.vin) { + if (exists(txin.prevout.hash)) + continue; + std::map::iterator it = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0)); + if (it == mapNextTx.end() || it->first.hash != txin.prevout.hash) + pvNoSpendsRemaining->push_back(txin.prevout.hash); + } + } + } } if (maxFeeRateRemoved > CFeeRate(0)) diff --git a/src/txmempool.h b/src/txmempool.h index bea3dab2f..dbf1e6dea 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -478,8 +478,11 @@ class CTxMemPool */ CFeeRate GetMinFee(size_t sizelimit) const; - /** Remove transactions from the mempool until its dynamic size is <= sizelimit. */ - void TrimToSize(size_t sizelimit); + /** Remove transactions from the mempool until its dynamic size is <= sizelimit. + * pvNoSpendsRemaining, if set, will be populated with the list of transactions + * which are not in mempool which no longer have any spends in this mempool. + */ + void TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRemaining=NULL); /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */ int Expire(int64_t time); From b67d5b27158b6275bcff1fdca82965dfacef7902 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 21:40:12 +0000 Subject: [PATCH 109/146] Implement feefilter P2P message --- doc/bips.md | 1 + doc/release-notes.md | 9 +++ qa/pull-tester/rpc-tests.sh | 3 +- qa/rpc-tests/maxuploadtarget.py | 1 - qa/rpc-tests/p2p-feefilter.py | 99 +++++++++++++++++++++++++ qa/rpc-tests/test_framework/comptool.py | 14 ---- qa/rpc-tests/test_framework/mininode.py | 61 ++++++++++++++- src/consensus/validation.h | 8 +- src/gtest/test_checkblock.cpp | 2 +- src/gtest/test_checktransaction.cpp | 2 +- src/gtest/test_mempool.cpp | 18 +++-- src/init.cpp | 1 + src/main.cpp | 85 +++++++++++++++------ src/main.h | 11 ++- src/net.cpp | 19 +++-- src/net.h | 10 ++- src/policy/fees.cpp | 18 +++++ src/policy/fees.h | 15 ++++ src/rpc/rawtransaction.cpp | 11 +-- src/test/txvalidationcache_tests.cpp | 2 +- src/txmempool.cpp | 10 +++ src/txmempool.h | 1 + src/version.h | 3 + src/wallet/wallet.cpp | 12 +-- src/wallet/wallet.h | 4 +- 25 files changed, 345 insertions(+), 75 deletions(-) create mode 100755 qa/rpc-tests/p2p-feefilter.py diff --git a/doc/bips.md b/doc/bips.md index 10f9936e9..d19723360 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -3,3 +3,4 @@ BIPs that are implemented by Zcash (up-to-date up to **v1.1.0**): * Numerous historic BIPs were present in **v1.0.0** at launch; see [the protocol spec](https://github.com/zcash/zips/blob/master/protocol/protocol.pdf) for details. * [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, but only enforced for peer versions `>=170004` as of **v1.1.0** ([PR #2814](https://github.com/zcash/zcash/pull/2814)). * [`BIP 130`](https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki): direct headers announcement is negotiated with peer versions `>=770009` as of **v0.12.0** ([PR 6494](https://github.com/bitcoin/bitcoin/pull/6494)). +* [`BIP 133`](https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki): feefilter messages are respected and sent for peer versions `>=770009` as of **v0.13.0** ([PR 7542](https://github.com/bitcoin/bitcoin/pull/7542)). diff --git a/doc/release-notes.md b/doc/release-notes.md index a475c7e8b..1bce09644 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -103,6 +103,15 @@ of just announcing the hash. In a reorganization, all new headers are sent, instead of just the new tip. This can often prevent an extra roundtrip before the actual block is downloaded. + +Fee filtering of invs (BIP 133) +------------------------------------ + +The optional new p2p message "feefilter" is implemented and the protocol +version is bumped to 70013. Upon receiving a feefilter message from a peer, +a node will not send invs for any transactions which do not meet the filter +feerate. [BIP 133](https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki) + ### Validation ### Build system diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 16d67bc32..fec028055 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -83,7 +83,6 @@ testScripts=( testScriptsExt=( 'getblocktemplate_longpoll.py' 'getblocktemplate_proposals.py' - 'pruning.py' 'forknotify.py' 'hardforkdetection.py' 'invalidateblock.py' @@ -96,6 +95,8 @@ testScriptsExt=( 'invalidblockrequest.py' # 'forknotify.py' 'p2p-acceptblock.py' + 'p2p-feefilter.py', + 'pruning.py', # leave pruning last as it takes a REALLY long time 'mempool_packages.py' 'maxuploadtarget.py', 'wallet_db_flush.py' diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py index 23a41d17b..c81e8ede0 100755 --- a/qa/rpc-tests/maxuploadtarget.py +++ b/qa/rpc-tests/maxuploadtarget.py @@ -16,7 +16,6 @@ start_node, stop_node, \ p2p_port, \ ) -from test_framework.comptool import wait_until from decimal import Decimal import os import time diff --git a/qa/rpc-tests/p2p-feefilter.py b/qa/rpc-tests/p2p-feefilter.py new file mode 100755 index 000000000..f85c18dcd --- /dev/null +++ b/qa/rpc-tests/p2p-feefilter.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python2 +# Copyright (c) 2016 The Bitcoin Core developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import time + +''' +FeeFilterTest -- test processing of feefilter messages +''' + +def hashToHex(hash): + return format(hash, '064x').decode('utf-8') + +# Wait up to 60 secs to see if the testnode has received all the expected invs +def allInvsMatch(invsExpected, testnode): + for x in xrange(60): + with mininode_lock: + if (sorted(invsExpected) == sorted(testnode.txinvs)): + return True; + time.sleep(1) + return False; + +# TestNode: bare-bones "peer". Used to track which invs are received from a node +# and to send the node feefilter messages. +class TestNode(SingleNodeConnCB): + def __init__(self): + SingleNodeConnCB.__init__(self) + self.txinvs = [] + + def on_inv(self, conn, message): + for i in message.inv: + if (i.type == 1): + self.txinvs.append(hashToHex(i.hash)) + + def clear_invs(self): + with mininode_lock: + self.txinvs = [] + + def send_filter(self, feerate): + self.send_message(msg_feefilter(feerate)) + self.sync_with_ping() + +class FeeFilterTest(BitcoinTestFramework): + def setup_network(self): + # Node1 will be used to generate txs which should be relayed from Node0 + # to our test node + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-debug", "-logtimemicros"])) + connect_nodes(self.nodes[0], 1) + + def run_test(self): + node1 = self.nodes[1] + # Get out of IBD + node1.generate(1) + sync_blocks(self.nodes) + + # Setup the p2p connections and start up the network thread. + test_node = TestNode() + connection = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node) + test_node.add_connection(connection) + NetworkThread().start() + test_node.wait_for_verack() + + # Test that invs are received for all txs at feerate of 20 sat/byte + node1.settxfee(Decimal("0.00020000")) + txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in xrange(3)] + assert(allInvsMatch(txids, test_node)) + test_node.clear_invs() + + # Set a filter of 15 sat/byte + test_node.send_filter(15000) + + # Test that txs are still being received (paying 20 sat/byte) + txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in xrange(3)] + assert(allInvsMatch(txids, test_node)) + test_node.clear_invs() + + # Change tx fee rate to 10 sat/byte and test they are no longer received + node1.settxfee(Decimal("0.00010000")) + [node1.sendtoaddress(node1.getnewaddress(), 1) for x in xrange(3)] + sync_mempools(self.nodes) # must be sure node 0 has received all txs + time.sleep(10) # wait 10 secs to be sure its doesn't relay any + assert(allInvsMatch([], test_node)) + test_node.clear_invs() + + # Remove fee filter and check that txs are received again + test_node.send_filter(0) + txids = [node1.sendtoaddress(node1.getnewaddress(), 1) for x in xrange(3)] + assert(allInvsMatch(txids, test_node)) + test_node.clear_invs() + +if __name__ == '__main__': + FeeFilterTest().main() diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index 153ded981..3d82151e1 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -28,20 +28,6 @@ # on_getheaders: provide headers via BlockStore # on_getdata: provide blocks via BlockStore -def wait_until(predicate, attempts=float('inf'), timeout=float('inf')): - attempt = 0 - elapsed = 0 - - while attempt < attempts and elapsed < timeout: - with mininode_lock: - if predicate(): - return True - attempt += 1 - elapsed += 0.05 - time.sleep(0.05) - - return False - class TestNode(NodeConnCB): def __init__(self, block_store, tx_store): diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 411743c34..d7d6d67f2 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -1397,6 +1397,37 @@ def __repr__(self): return "msg_reject: %s %d %s [%064x]" \ % (self.message, self.code, self.reason, self.data) +# Helper function +def wait_until(predicate, attempts=float('inf'), timeout=float('inf')): + attempt = 0 + elapsed = 0 + + while attempt < attempts and elapsed < timeout: + with mininode_lock: + if predicate(): + return True + attempt += 1 + elapsed += 0.05 + time.sleep(0.05) + + return False + +class msg_feefilter(object): + command = "feefilter" + + def __init__(self, feerate=0L): + self.feerate = feerate + + def deserialize(self, f): + self.feerate = struct.unpack("", _("Specify data directory")); strUsage += HelpMessageOpt("-exportdir=", _("Specify directory to be used when exporting data")); strUsage += HelpMessageOpt("-dbcache=", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); + strUsage += HelpMessageOpt("-feefilter", strprintf(_("Tell other nodes to filter invs to us by our mempool min fee (default: %u)"), DEFAULT_FEEFILTER)); strUsage += HelpMessageOpt("-loadblock=", _("Imports blocks from external blk000??.dat file on startup")); strUsage += HelpMessageOpt("-debuglogfile=", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE)); strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); diff --git a/src/main.cpp b/src/main.cpp index 423141258..a8daed9b5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,8 +23,10 @@ #include "merkleblock.h" #include "metrics.h" #include "net.h" +#include "policy/fees.h" #include "policy/policy.h" #include "pow.h" +#include "random.h" #include "reverse_iterator.h" #include "txmempool.h" #include "ui_interface.h" @@ -87,6 +89,7 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; CTxMemPool mempool(::minRelayTxFee); +FeeFilterRounder filterRounder(::minRelayTxFee); struct COrphanTx { CTransaction tx; @@ -1265,7 +1268,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned } bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, + bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector& vHashTxnToUncache) { AssertLockHeld(cs_main); @@ -1435,6 +1438,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId); unsigned int nSize = entry.GetTxSize(); + if (txFeeRate) { + *txFeeRate = CFeeRate(nFees, nSize); + } // Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany. if (tx.vJoinSplit.size() > 0 && nFees >= ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE) { @@ -1479,13 +1485,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C dFreeCount += nSize; } - if (fRejectAbsurdFee && nFees > maxTxFee) { - string errmsg = strprintf("absurdly high fees %s, %d > %d", - hash.ToString(), - nFees, maxTxFee); - LogPrint("mempool", errmsg.c_str()); - return state.Error("AcceptToMemoryPool: " + errmsg); - } + if (nAbsurdFee && nFees > nAbsurdFee) + return state.Invalid(false, REJECT_HIGHFEE, strprintf("absurdly-high-fee %d > %d", nFees, nAbsurdFee)); // Calculate in-mempool ancestors, up to a limit. CTxMemPool::setEntries setAncestors; @@ -1603,10 +1604,10 @@ bool GetAddressUnspent(const uint160& addressHash, int type, } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee) + bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { std::vector vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, fRejectAbsurdFee, vHashTxToUncache); + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, txFeeRate, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); if (!res) { for (const uint256& hashTx : vHashTxToUncache) pcoinsTip->Uncache(hashTx); @@ -1964,7 +1965,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state if (state.IsInvalid(nDoS)) { std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); if (it != mapBlockSource.end() && State(it->second)) { - CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; + CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; State(it->second)->rejects.push_back(reject); if (nDoS > 0) Misbehaving(it->second, nDoS); @@ -3002,7 +3003,7 @@ bool static DisconnectTip(CValidationState &state, const CChainParams& chainpara // ignore validation errors in resurrected transactions list removed; CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, NULL, true)) { mempool.remove(tx, removed, true); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); @@ -5878,10 +5879,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv); - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) - { + CFeeRate txFeeRate = CFeeRate(0); + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, &txFeeRate)) { mempool.check(pcoinsTip); - RelayTransaction(tx); + RelayTransaction(tx, txFeeRate); vWorkQueue.push_back(inv.hash); LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", @@ -5912,10 +5913,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) - { + CFeeRate orphanFeeRate = CFeeRate(0); + if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2, &orphanFeeRate)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx); + RelayTransaction(orphanTx, orphanFeeRate); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); } @@ -5972,7 +5973,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS = 0; if (!state.IsInvalid(nDoS) || nDoS == 0) { LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id); - RelayTransaction(tx); + RelayTransaction(tx, txFeeRate); } else { LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s (code %d))\n", tx.GetHash().ToString(), pfrom->id, state.GetRejectReason(), state.GetRejectCode()); @@ -6180,6 +6181,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... if (!pfrom->pfilter->IsRelevantAndUpdate(tx)) continue; } + if (pfrom->minFeeFilter) { + CFeeRate feeRate; + mempool.lookupFeeRate(hash, feeRate); + LOCK(pfrom->cs_feeFilter); + if (feeRate.GetFeePerK() < pfrom->minFeeFilter) + continue; + } vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { pfrom->PushMessage("inv", vInv); @@ -6372,13 +6380,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // message would be undesirable as we transmit it ourselves. } - else if (!(strCommand == "tx" || strCommand == "block" || strCommand == "headers" || strCommand == "alert")) { + else if (strCommand == "feefilter") { + CAmount newFeeFilter = 0; + vRecv >> newFeeFilter; + if (MoneyRange(newFeeFilter)) { + { + LOCK(pfrom->cs_feeFilter); + pfrom->minFeeFilter = newFeeFilter; + } + LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id); + } + } + + else { // Ignore unknown commands for extensibility LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } - - return true; } @@ -6856,6 +6874,29 @@ bool SendMessages(CNode* pto) if (!vGetData.empty()) pto->PushMessage("getdata", vGetData); + // + // Message: feefilter + // + // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay + if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && + !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) { + CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); + int64_t timeNow = GetTimeMicros(); + if (timeNow > pto->nextSendTimeFeeFilter) { + CAmount filterToSend = filterRounder.round(currentFilter); + if (filterToSend != pto->lastSentFeeFilter) { + pto->PushMessage("feefilter", filterToSend); + pto->lastSentFeeFilter = filterToSend; + } + pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); + } + // If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY + // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY. + else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter && + (currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) { + pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000; + } + } } return true; } diff --git a/src/main.h b/src/main.h index 0b5603c93..aaa364088 100644 --- a/src/main.h +++ b/src/main.h @@ -128,6 +128,10 @@ static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30; /** Average delay between trickled inventory broadcasts in seconds. * Blocks, whitelisted receivers, and a random 25% of transactions bypass this. */ static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5; +/** Average delay between feefilter broadcasts in seconds. */ +static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; +/** Maximum feefilter broadcast delay after significant change. */ +static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60; static const unsigned int DEFAULT_LIMITFREERELAY = 15; static const bool DEFAULT_RELAYPRIORITY = false; @@ -148,6 +152,9 @@ static const unsigned int DEFAULT_REORG_CHECK = 6; static const bool DEFAULT_PEERBLOOMFILTERS = true; static const bool DEFAULT_ENFORCENODEBLOOM = false; +/** Default for using fee filter */ +static const bool DEFAULT_FEEFILTER = true; + /** Maximum number of headers to announce when relaying blocks with headers message.*/ static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8; @@ -323,7 +330,7 @@ void PruneAndFlush(); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit=false, bool fRejectAbsurdFee=false); + bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); /** Find block at height in a fork **/ const CBlockIndex* FindBlockAtHeight(int nHeight, const CBlockIndex* pIndex); @@ -544,6 +551,8 @@ int GetSpendHeight(const CCoinsViewCache& inputs); uint64_t CalculateCurrentUsage(); +/** Too high fee. Can not be triggered by P2P transactions */ +static const unsigned int REJECT_HIGHFEE = 0x100; /** * Return a CMutableTransaction with contextual default values based on set of consensus rules at nHeight. The expiryDelta will diff --git a/src/net.cpp b/src/net.cpp index e4cf73380..b718e8f01 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2001,20 +2001,15 @@ static class CNetCleanup instance_of_cnetcleanup; - - - - - -void RelayTransaction(const CTransaction& tx) +void RelayTransaction(const CTransaction& tx, CFeeRate feerate) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(10000); ss << tx; - RelayTransaction(tx, ss); + RelayTransaction(tx, feerate, ss); } -void RelayTransaction(const CTransaction& tx, const CDataStream& ss) +void RelayTransaction(const CTransaction& tx, CFeeRate feerate, const CDataStream& ss) { CInv inv(MSG_TX, tx.GetHash()); { @@ -2035,6 +2030,11 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss) { if(!pnode->fRelayTxes) continue; + { + LOCK(pnode->cs_feeFilter); + if (feerate.GetFeePerK() < pnode->minFeeFilter) + continue; + } LOCK(pnode->cs_filter); if (pnode->pfilter) { @@ -2237,6 +2237,9 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nPingUsecTime = 0; fPingQueued = false; nMinPingUsecTime = std::numeric_limits::max(); + minFeeFilter = 0; + lastSentFeeFilter = 0; + nextSendTimeFeeFilter = 0; { LOCK(cs_nLastNodeId); diff --git a/src/net.h b/src/net.h index 9820e6330..4ad4b8089 100644 --- a/src/net.h +++ b/src/net.h @@ -7,6 +7,7 @@ #define BITCOIN_NET_H #include "addrdb.h" +#include "amount.h" #include "bloom.h" #include "compat.h" #include "fs.h" @@ -352,6 +353,11 @@ class CNode std::atomic nMinPingUsecTime; // Whether a ping is requested. std::atomic fPingQueued; + // Minimum fee rate with which to filter inv's to this node + CAmount minFeeFilter; + CCriticalSection cs_feeFilter; + CAmount lastSentFeeFilter; + int64_t nextSendTimeFeeFilter; CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); @@ -718,8 +724,8 @@ class CNode class CTransaction; -void RelayTransaction(const CTransaction& tx); -void RelayTransaction(const CTransaction& tx, const CDataStream& ss); +void RelayTransaction(const CTransaction& tx, CFeeRate feerate); +void RelayTransaction(const CTransaction& tx, CFeeRate feerate, const CDataStream& ss); /** Return a timestamp in the future (in microseconds) for exponentially distributed events. */ int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds); diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index da182b08e..d7f5501f4 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -536,3 +536,21 @@ void CBlockPolicyEstimator::Read(CAutoFile& filein) priStats.Read(filein); nBestSeenHeight = nFileBestSeenHeight; } + +FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee) +{ + CAmount minFeeLimit = minIncrementalFee.GetFeePerK() / 2; + feeset.insert(0); + for (double bucketBoundary = minFeeLimit; bucketBoundary <= MAX_FEERATE; bucketBoundary *= FEE_SPACING) { + feeset.insert(bucketBoundary); + } +} + +CAmount FeeFilterRounder::round(CAmount currentMinFee) +{ + std::set::iterator it = feeset.lower_bound(currentMinFee); + if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) { + it--; + } + return *it; +} diff --git a/src/policy/fees.h b/src/policy/fees.h index 34b2d87c0..4529941a7 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -6,6 +6,7 @@ #define BITCOIN_POLICYESTIMATOR_H #include "amount.h" +#include "random.h" #include "uint256.h" #include @@ -280,4 +281,18 @@ class CBlockPolicyEstimator CFeeRate feeLikely, feeUnlikely; double priLikely, priUnlikely; }; + +class FeeFilterRounder +{ +public: + /** Create new FeeFilterRounder */ + FeeFilterRounder(const CFeeRate& minIncrementalFee); + + /** Quantize a minimum fee for privacy purpose before broadcast **/ + CAmount round(CAmount currentMinFee); + +private: + std::set feeset; + FastRandomContext insecure_rand; +}; #endif /*BITCOIN_POLICYESTIMATOR_H */ diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 91a3910d4..be90d1754 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1088,19 +1088,20 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) } } - bool fOverrideFees = false; - if (params.size() > 1) - fOverrideFees = params[1].get_bool(); + CAmount nMaxRawTxFee = maxTxFee; + if (params.size() > 1 && params[1].get_bool()) + nMaxRawTxFee = 0; CCoinsViewCache &view = *pcoinsTip; const CCoins* existingCoins = view.AccessCoins(hashTx); bool fHaveMempool = mempool.exists(hashTx); bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; + CFeeRate txFeeRate = CFeeRate(0); if (!fHaveMempool && !fHaveChain) { // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, !fOverrideFees)) { + if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, &txFeeRate, false, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { @@ -1113,7 +1114,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } - RelayTransaction(tx); + RelayTransaction(tx, txFeeRate); return hashTx.GetHex(); } diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index d58d9ed77..29f95ddc7 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -23,7 +23,7 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, tx, false, NULL, true, false); + return AcceptToMemoryPool(mempool, state, tx, false, NULL, NULL, true, 0); } #ifdef ENABLE_MINING diff --git a/src/txmempool.cpp b/src/txmempool.cpp index addebc43c..b07f158ba 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1007,6 +1007,16 @@ bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const return true; } +bool CTxMemPool::lookupFeeRate(const uint256& hash, CFeeRate& feeRate) const +{ + LOCK(cs); + indexed_transaction_set::const_iterator i = mapTx.find(hash); + if (i == mapTx.end()) + return false; + feeRate = CFeeRate(i->GetFee(), i->GetTxSize()); + return true; +} + CFeeRate CTxMemPool::estimateFee(int nBlocks) const { LOCK(cs); diff --git a/src/txmempool.h b/src/txmempool.h index dbf1e6dea..cbaedf224 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -506,6 +506,7 @@ class CTxMemPool } bool lookup(uint256 hash, CTransaction& result) const; + bool lookupFeeRate(const uint256& hash, CFeeRate& feeRate) const; /** Estimate fee rate needed to get into the next nBlocks */ CFeeRate estimateFee(int nBlocks) const; diff --git a/src/version.h b/src/version.h index ebd371662..aedcba15f 100644 --- a/src/version.h +++ b/src/version.h @@ -30,4 +30,7 @@ static const int NO_BLOOM_VERSION = 170004; //! "sendheaders" command and announcing blocks with headers starts with this version static const int SENDHEADERS_VERSION = 770009; +//! "feefilter" tells peers to filter invs to you by fee starts with this version +static const int FEEFILTER_VERSION = 770009; + #endif // BITCOIN_VERSION_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 81a950df8..be4c49bbe 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2952,7 +2952,7 @@ void CWallet::ReacceptWalletTransactions() CWalletTx& wtx = *(item.second); LOCK(mempool.cs); - wtx.AcceptToMemoryPool(false); + wtx.AcceptToMemoryPool(false, maxTxFee); } } @@ -2963,7 +2963,9 @@ bool CWalletTx::RelayWalletTransaction() { if (GetDepthInMainChain() == 0) { LogPrintf("Relaying wtx %s\n", GetHash().ToString()); - RelayTransaction((CTransaction)*this); + CFeeRate feeRate; + mempool.lookupFeeRate(GetHash(), feeRate); + RelayTransaction((CTransaction)*this, feeRate); return true; } } @@ -4005,7 +4007,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, std::optional 0; } int GetBlocksToMaturity() const; - /** Pass this transaction to the mempool. Fails if absolute fee exceeds maxTxFee. */ - bool AcceptToMemoryPool(bool fLimitFree=true, bool fRejectAbsurdFee=true); + /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ + bool AcceptToMemoryPool(bool fLimitFree, const CAmount nAbsurdFee); }; /** From 09d3982251fe25516df528500ae6364440061552 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 22:09:03 +0000 Subject: [PATCH 110/146] Remove GetFeeRate/feeRate from txmempool.{cpp,h} --- src/txmempool.cpp | 1 - src/txmempool.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index b07f158ba..8b3c7d813 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -32,7 +32,6 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); nModSize = tx.CalculateModifiedSize(nTxSize); nUsageSize = RecursiveDynamicUsage(tx); - feeRate = CFeeRate(nFee, nTxSize); nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; diff --git a/src/txmempool.h b/src/txmempool.h index cbaedf224..14cf7c55a 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -67,7 +67,6 @@ class CTxMemPoolEntry size_t nTxSize; //!< ... and avoid recomputing tx size size_t nModSize; //!< ... and modified size for priority size_t nUsageSize; //!< ... and total memory usage - CFeeRate feeRate; //!< ... and fee per kB int64_t nTime; //!< Local time when entering the mempool double dPriority; //!< Priority when entering the mempool unsigned int nHeight; //!< Chain height when entering the mempool @@ -93,7 +92,6 @@ class CTxMemPoolEntry const CTransaction& GetTx() const { return this->tx; } double GetPriority(unsigned int currentHeight) const; const CAmount& GetFee() const { return nFee; } - CFeeRate GetFeeRate() const { return feeRate; } size_t GetTxSize() const { return nTxSize; } int64_t GetTime() const { return nTime; } unsigned int GetHeight() const { return nHeight; } From 5dd3e1e213e2c497d1da5b1635dffd5794a70fb3 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Thu, 29 Aug 2024 23:29:39 +0000 Subject: [PATCH 111/146] Track block download times per individual block --- src/main.cpp | 71 ++++++++++++++++++++++++++++------------------------ src/main.h | 4 +++ 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a8daed9b5..b24646cb8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -219,15 +219,10 @@ namespace { struct QueuedBlock { uint256 hash; CBlockIndex* pindex; //!< Optional. - int64_t nTime; //!< Time of "getdata" request in microseconds. bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. - int64_t nTimeDisconnect; //!< The timeout for this block request (for disconnecting a slow peer) }; map::iterator> > mapBlocksInFlight; - /** Number of blocks in flight with validated headers. */ - int nQueuedValidatedHeaders = 0; - /** Number of preferable block download peers. */ int nPreferredDownload = 0; @@ -236,6 +231,9 @@ namespace { /** Dirty block file entries. */ set setDirtyFileInfo; + + /** Number of peers from which we're downloading blocks. */ + int nPeersWithValidatedDownloads = 0; } // anon namespace ////////////////////////////////////////////////////////////////////////////// @@ -283,6 +281,8 @@ struct CNodeState { //! Since when we're stalling block download progress (in microseconds), or 0. int64_t nStallingSince; list vBlocksInFlight; + //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty. + int64_t nDownloadingSince; int nBlocksInFlight; int nBlocksInFlightValidHeaders; //! Whether we consider this a preferred download peer. @@ -300,6 +300,7 @@ struct CNodeState { pindexBestHeaderSent = NULL; fSyncStarted = false; nStallingSince = 0; + nDownloadingSince = 0; nBlocksInFlight = 0; nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; @@ -334,13 +335,6 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state) nPreferredDownload += state->fPreferredDownload; } -// Returns time at which to timeout block request (nTime in microseconds) -int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams, int nHeight) -{ - return nTime + 500000 * consensusParams.PoWTargetSpacing(nHeight) * (4 + nValidatedQueuedBefore); -} - - void InitializeNode(NodeId nodeid, const CNode *pnode) { LOCK(cs_main); CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; @@ -359,12 +353,22 @@ void FinalizeNode(NodeId nodeid) { AddressCurrentlyConnected(state->address); } - for (const QueuedBlock& entry : state->vBlocksInFlight) + for (const QueuedBlock& entry : state->vBlocksInFlight) { mapBlocksInFlight.erase(entry.hash); + } EraseOrphansFor(nodeid); nPreferredDownload -= state->fPreferredDownload; + nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); + assert(nPeersWithValidatedDownloads >= 0); mapNodeState.erase(nodeid); + + if (mapNodeState.empty()) { + // Do a consistency check after the last peer is removed. + assert(mapBlocksInFlight.empty()); + assert(nPreferredDownload == 0); + assert(nPeersWithValidatedDownloads == 0); + } } // Requires cs_main. @@ -373,8 +377,15 @@ bool MarkBlockAsReceived(const uint256& hash) { map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); - nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; + if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) { + // Last validated block on the queue was received. + nPeersWithValidatedDownloads--; + } + if (state->vBlocksInFlight.begin() == itInFlight->second.second) { + // First block on the queue was received, update the start download time for the next one + state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros()); + } state->vBlocksInFlight.erase(itInFlight->second.second); state->nBlocksInFlight--; state->nStallingSince = 0; @@ -392,13 +403,17 @@ void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Pa // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - int64_t nNow = GetTimeMicros(); - int nHeight = pindex != NULL ? pindex->nHeight : chainActive.Height(); // Help block timeout computation - QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams, nHeight)}; - nQueuedValidatedHeaders += newentry.fValidatedHeaders; + QueuedBlock newentry = {hash, pindex, pindex != NULL}; list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); state->nBlocksInFlight++; state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; + if (state->nBlocksInFlight == 1) { + // We're starting a block download (batch) from this peer. + state->nDownloadingSince = GetTimeMicros(); + } + if (state->nBlocksInFlightValidHeaders == 1 && pindex != NULL) { + nPeersWithValidatedDownloads++; + } mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } @@ -4800,7 +4815,6 @@ void UnloadBlockIndex() nBlockSequenceId = 1; mapBlockSource.clear(); mapBlocksInFlight.clear(); - nQueuedValidatedHeaders = 0; nPreferredDownload = 0; setDirtyBlockIndex.clear(); setDirtyFileInfo.clear(); @@ -6804,24 +6818,15 @@ bool SendMessages(CNode* pto) LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); pto->fDisconnect = true; } - // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval - // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to - // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link + // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval + // (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout. + // We compensate for other peers to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes // to unreasonably increase our timeout. - // We also compare the block download timeout originally calculated against the time at which we'd disconnect - // if we assumed the block were being requested now (ignoring blocks we've requested from this peer, since we're - // only looking at this peer's oldest request). This way a large queue in the past doesn't result in a - // permanently large window for this block to be delivered (ie if the number of blocks in flight is decreasing - // more quickly than once every 5 minutes, then we'll shorten the download window for this block). if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); - int64_t nTimeoutIfRequestedNow = GetBlockTimeout(nNow, nQueuedValidatedHeaders - state.nBlocksInFlightValidHeaders, consensusParams, pindexBestHeader->nHeight); - if (queuedBlock.nTimeDisconnect > nTimeoutIfRequestedNow) { - LogPrint("net", "Reducing block download timeout for peer=%d block=%s, orig=%d new=%d\n", pto->id, queuedBlock.hash.ToString(), queuedBlock.nTimeDisconnect, nTimeoutIfRequestedNow); - queuedBlock.nTimeDisconnect = nTimeoutIfRequestedNow; - } - if (queuedBlock.nTimeDisconnect < nNow) { + int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0); + if (nNow > state.nDownloadingSince + consensusParams.PoWTargetSpacing(pindexBestHeader->nHeight) * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) { LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id); pto->fDisconnect = true; } diff --git a/src/main.h b/src/main.h index aaa364088..af5152cfc 100644 --- a/src/main.h +++ b/src/main.h @@ -132,6 +132,10 @@ static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5; static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; /** Maximum feefilter broadcast delay after significant change. */ static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60; +/** Block download timeout base, expressed in millionths of the block interval (i.e. 20 min) */ +static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 2000000; +/** Additional block download timeout per parallel downloading peer (i.e. 5 min) */ +static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000; static const unsigned int DEFAULT_LIMITFREERELAY = 15; static const bool DEFAULT_RELAYPRIORITY = false; From 168af4dbb3f543e06b9dff90060bf18a22307f5c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 07:29:19 +0000 Subject: [PATCH 112/146] Code clenaup --- src/noui.cpp | 4 +--- src/noui.h | 2 +- src/test/test_bitcoin.cpp | 4 ++-- src/version.h | 7 +++++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/noui.cpp b/src/noui.cpp index 83ca8b4a6..2dfb6bfd7 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -12,8 +12,6 @@ #include #include -#include - static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { bool fSecure = style & CClientUIInterface::SECURE; diff --git a/src/noui.h b/src/noui.h index 935a1c5dc..4442539df 100644 --- a/src/noui.h +++ b/src/noui.h @@ -1,4 +1,4 @@ -// Copyright (c) 2013 The Bitcoin Core developers +// Copyright (c) 2013-2014 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index e8718394f..f96f36752 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -22,10 +22,10 @@ #include "random.h" #include "txdb.h" #include "txmempool.h" -#include "ui_interface.h" -#include "rpc/server.h" #include "rpc/register.h" +#include "rpc/server.h" #include "script/sigcache.h" +#include "ui_interface.h" #include #include diff --git a/src/version.h b/src/version.h index aedcba15f..b09ab4458 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014 The Bitcoin Core developers +// Copyright (c) 2012-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . @@ -14,8 +14,11 @@ static const int PROTOCOL_VERSION = 770009; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; +//! In this version, 'getheaders' was introduced. +static const int GETHEADERS_VERSION = 770007; + //! disconnect from peers older than this proto version -static const int MIN_PEER_PROTO_VERSION = 770007; +static const int MIN_PEER_PROTO_VERSION = GETHEADERS_VERSION; //! nTime field added to CAddress, starting with this version; //! if possible, avoid requesting addresses nodes older than this From 77d9027250a43f9d76780a4b8ebf93493618d12d Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 08:34:16 +0000 Subject: [PATCH 113/146] net: Add and document network messages in protocol.h --- src/main.cpp | 121 +++++++++++++++++----------------- src/net.cpp | 2 +- src/protocol.cpp | 69 ++++++++++++++++++-- src/protocol.h | 164 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+), 65 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b24646cb8..0dbfd8b77 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5302,7 +5302,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if (!ReadBlockFromDisk(block, (*mi).second, consensusParams)) assert(!"cannot load block from disk"); if (inv.type == MSG_BLOCK) - pfrom->PushMessage("block", block); + pfrom->PushMessage(NetMsgType::BLOCK, block); else // MSG_FILTERED_BLOCK) { bool send = false; @@ -5315,7 +5315,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } if (send) { - pfrom->PushMessage("merkleblock", merkleBlock); + pfrom->PushMessage(NetMsgType::MERKLEBLOCK, merkleBlock); // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see // This avoids hurting performance by pointlessly requiring a round-trip // Note that there is currently no way for a node to request any single transactions we didn't send here - @@ -5324,7 +5324,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // however we MUST always provide at least what the remote peer needs typedef std::pair PairType; for (PairType& pair : merkleBlock.vMatchedTxn) - pfrom->PushMessage("tx", block.vtx[pair.first]); + pfrom->PushMessage(NetMsgType::TX, block.vtx[pair.first]); } // else // no response @@ -5338,7 +5338,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // wait for other stuff first. vector vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); - pfrom->PushMessage("inv", vInv); + pfrom->PushMessage(NetMsgType::INV, vInv); pfrom->hashContinue.SetNull(); } } @@ -5371,7 +5371,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; - pfrom->PushMessage("tx", ss); + pfrom->PushMessage(NetMsgType::TX, ss); pushed = true; } } @@ -5400,7 +5400,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // do that because they want to know about (and store and rebroadcast and // risk analyze) the dependencies of transactions relevant to them, without // having to download the entire memory pool. - pfrom->PushMessage("notfound", vNotFound); + pfrom->PushMessage(NetMsgType::NOTFOUND, vNotFound); } } @@ -5415,12 +5415,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - if (strCommand == "version") + if (strCommand == NetMsgType::VERSION) { // Each connection can only send one version message if (pfrom->nVersion != 0) { - pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); + pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message")); LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; @@ -5439,7 +5439,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { // disconnect from peers older than this proto version LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); - pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); pfrom->fDisconnect = true; return false; @@ -5451,7 +5451,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion < consensusParams.vUpgrades[currentEpoch].nProtocolVersion) { LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); - pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", consensusParams.vUpgrades[currentEpoch].nProtocolVersion)); pfrom->fDisconnect = true; @@ -5508,7 +5508,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // Change version - pfrom->PushMessage("verack"); + pfrom->PushMessage(NetMsgType::VERACK); pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); if (!pfrom->fInbound) @@ -5532,7 +5532,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { - pfrom->PushMessage("getaddr"); + pfrom->PushMessage(NetMsgType::GETADDR); pfrom->fGetAddr = true; } addrman.Good(pfrom->addr); @@ -5568,7 +5568,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "verack") + else if (strCommand == NetMsgType::VERACK) { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); @@ -5583,7 +5583,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning // nodes) - pfrom->PushMessage("sendheaders"); + pfrom->PushMessage(NetMsgType::SENDHEADERS); } } @@ -5595,7 +5595,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion) { LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); - pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", chainparams.GetConsensus().vUpgrades[ CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion)); @@ -5604,7 +5604,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "addr") + else if (strCommand == NetMsgType::ADDR) { vector vAddr; vRecv >> vAddr; @@ -5667,14 +5667,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; } - else if (strCommand == "sendheaders") + else if (strCommand == NetMsgType::SENDHEADERS) { LOCK(cs_main); State(pfrom->GetId())->fPreferHeaders = true; } - else if (strCommand == "inv") + else if (strCommand == NetMsgType::INV) { vector vInv; vRecv >> vInv; @@ -5716,7 +5716,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // time the block arrives, the header chain leading up to it is already validated. Not // doing this will result in the received block being rejected as an orphan in case it is // not a direct successor. - pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash); + pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash); CNodeState *nodestate = State(pfrom->GetId()); if (CanDirectFetch(chainparams.GetConsensus()) && @@ -5747,11 +5747,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (!vToFetch.empty()) - pfrom->PushMessage("getdata", vToFetch); + pfrom->PushMessage(NetMsgType::GETDATA, vToFetch); } - else if (strCommand == "getdata") + else if (strCommand == NetMsgType::GETDATA) { vector vInv; vRecv >> vInv; @@ -5773,7 +5773,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getblocks") + else if (strCommand == NetMsgType::GETBLOCKS) { CBlockLocator locator; uint256 hashStop; @@ -5817,7 +5817,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "getheaders") + else if (strCommand == NetMsgType::GETHEADERS) { CBlockLocator locator; uint256 hashStop; @@ -5863,11 +5863,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); - pfrom->PushMessage("headers", vHeaders); + pfrom->PushMessage(NetMsgType::HEADERS, vHeaders); } - else if (strCommand == "tx" && !IsInitialBlockDownload(chainparams)) + else if (strCommand == NetMsgType::TX && !IsInitialBlockDownload(chainparams)) { // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off @@ -6000,7 +6000,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->id, pfrom->cleanSubVer, state.GetRejectReason()); - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + pfrom->PushMessage(NetMsgType::REJECT, strCommand, state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); @@ -6009,7 +6009,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing + else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing { std::vector headers; @@ -6059,7 +6059,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. LogPrint("net", "more getheaders (%d) to send to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); - pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); + pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()); } bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); @@ -6104,7 +6104,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } if (vGetData.size() > 0) { - pfrom->PushMessage("getdata", vGetData); + pfrom->PushMessage(NetMsgType::GETDATA, vGetData); } } } @@ -6115,7 +6115,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, NotifyHeaderTip(chainparams); } - else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing + else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; vRecv >> block; @@ -6134,7 +6134,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL); int nDoS; if (state.IsInvalid(nDoS)) { - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + pfrom->PushMessage(NetMsgType::REJECT, strCommand, state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) { LOCK(cs_main); @@ -6150,7 +6150,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // to users' AddrMan and later request them by sending getaddr messages. // Making nodes which are behind NAT and can only make outgoing connections ignore // the getaddr message mitigates the attack. - else if ((strCommand == "getaddr") && (pfrom->fInbound)) + else if ((strCommand == NetMsgType::GETADDR) && (pfrom->fInbound)) { // Only send one GetAddr response per connection to reduce resource waste // and discourage addr stamping of INV announcements. @@ -6168,7 +6168,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "mempool") + else if (strCommand == NetMsgType::MEMPOOL) { int currentHeight = GetHeight(); if (CNode::OutboundTargetReached(chainparams.GetConsensus().PoWTargetSpacing(currentHeight), false) && !pfrom->fWhitelisted) @@ -6204,16 +6204,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { - pfrom->PushMessage("inv", vInv); + pfrom->PushMessage(NetMsgType::INV, vInv); vInv.clear(); } } if (vInv.size() > 0) - pfrom->PushMessage("inv", vInv); + pfrom->PushMessage(NetMsgType::INV, vInv); } - else if (strCommand == "ping") + else if (strCommand == NetMsgType::PING) { if (pfrom->nVersion > BIP0031_VERSION) { @@ -6230,12 +6230,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. - pfrom->PushMessage("pong", nonce); + pfrom->PushMessage(NetMsgType::PONG, nonce); } } - else if (strCommand == "pong") + else if (strCommand == NetMsgType::PONG) { int64_t pingUsecEnd = nTimeReceived; uint64_t nonce = 0; @@ -6294,8 +6294,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (!(nLocalServices & NODE_BLOOM) && - (strCommand == "filterload" || - strCommand == "filteradd")) + (strCommand == NetMsgType::FILTERLOAD || + strCommand == NetMsgType::FILTERADD || + strCommand == NetMsgType::FILTERCLEAR)) { if (pfrom->nVersion >= NO_BLOOM_VERSION) { LOCK(cs_main); @@ -6308,7 +6309,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "filterload") + else if (strCommand == NetMsgType::FILTERLOAD) { CBloomFilter filter; vRecv >> filter; @@ -6329,7 +6330,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "filteradd") + else if (strCommand == NetMsgType::FILTERADD) { vector vData; vRecv >> vData; @@ -6354,7 +6355,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "filterclear") + else if (strCommand == NetMsgType::FILTERCLEAR) { LOCK(pfrom->cs_filter); if (nLocalServices & NODE_BLOOM) { @@ -6365,7 +6366,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "reject") + else if (strCommand == NetMsgType::REJECT) { if (fDebug) { try { @@ -6375,7 +6376,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, ostringstream ss; ss << strMsg << " code " << itostr(ccode) << ": " << strReason; - if (strMsg == "block" || strMsg == "tx") + if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX) { uint256 hash; vRecv >> hash; @@ -6389,12 +6390,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - else if (strCommand == "notfound") { + else if (strCommand == NetMsgType::NOTFOUND) { // We do not care about the NOTFOUND message, but logging an Unknown Command // message would be undesirable as we transmit it ourselves. } - else if (strCommand == "feefilter") { + else if (strCommand == NetMsgType::FEEFILTER) { CAmount newFeeFilter = 0; vRecv >> newFeeFilter; if (MoneyRange(newFeeFilter)) { @@ -6498,7 +6499,7 @@ bool ProcessMessages(CNode* pfrom) } catch (const std::ios_base::failure& e) { - pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message")); + pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message")); if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv @@ -6567,11 +6568,11 @@ bool SendMessages(CNode* pto) pto->nPingUsecStart = GetTimeMicros(); if (pto->nVersion > BIP0031_VERSION) { pto->nPingNonceSent = nonce; - pto->PushMessage("ping", nonce); + pto->PushMessage(NetMsgType::PING, nonce); } else { // Peer is too old to support ping command with nonce, pong will never arrive. pto->nPingNonceSent = 0; - pto->PushMessage("ping"); + pto->PushMessage(NetMsgType::PING); } } @@ -6602,14 +6603,14 @@ bool SendMessages(CNode* pto) // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { - pto->PushMessage("addr", vAddr); + pto->PushMessage(NetMsgType::ADDR, vAddr); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) - pto->PushMessage("addr", vAddr); + pto->PushMessage(NetMsgType::ADDR, vAddr); } CNodeState &state = *State(pto->GetId()); @@ -6629,7 +6630,7 @@ bool SendMessages(CNode* pto) } for (const CBlockReject& reject : state.rejects) - pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); + pto->PushMessage(NetMsgType::REJECT, (string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock); state.rejects.clear(); // Start block sync @@ -6652,7 +6653,7 @@ bool SendMessages(CNode* pto) if (pindexStart->pprev) pindexStart = pindexStart->pprev; LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); - pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256()); + pto->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()); } } @@ -6752,7 +6753,7 @@ bool SendMessages(CNode* pto) LogPrint("net", "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->id); } - pto->PushMessage("headers", vHeaders); + pto->PushMessage(NetMsgType::HEADERS, vHeaders); state.pindexBestHeaderSent = pBestIndex; } pto->vBlockHashesToAnnounce.clear(); @@ -6800,14 +6801,14 @@ bool SendMessages(CNode* pto) vInv.push_back(inv); if (vInv.size() >= 1000) { - pto->PushMessage("inv", vInv); + pto->PushMessage(NetMsgType::INV, vInv); vInv.clear(); } } pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) - pto->PushMessage("inv", vInv); + pto->PushMessage(NetMsgType::INV, vInv); // Detect whether we're stalling nNow = GetTimeMicros(); @@ -6867,7 +6868,7 @@ bool SendMessages(CNode* pto) vGetData.push_back(inv); if (vGetData.size() >= 1000) { - pto->PushMessage("getdata", vGetData); + pto->PushMessage(NetMsgType::GETDATA, vGetData); vGetData.clear(); } } else { @@ -6877,7 +6878,7 @@ bool SendMessages(CNode* pto) pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) - pto->PushMessage("getdata", vGetData); + pto->PushMessage(NetMsgType::GETDATA, vGetData); // // Message: feefilter @@ -6890,7 +6891,7 @@ bool SendMessages(CNode* pto) if (timeNow > pto->nextSendTimeFeeFilter) { CAmount filterToSend = filterRounder.round(currentFilter); if (filterToSend != pto->lastSentFeeFilter) { - pto->PushMessage("feefilter", filterToSend); + pto->PushMessage(NetMsgType::FEEFILTER, filterToSend); pto->lastSentFeeFilter = filterToSend; } pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); diff --git a/src/net.cpp b/src/net.cpp index b718e8f01..0ecf69923 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -457,7 +457,7 @@ void CNode::PushVersion() LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); else LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); - PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, + PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, strSubVersion, nBestHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)); } diff --git a/src/protocol.cpp b/src/protocol.cpp index ec2fbbfca..cdac35b7a 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -12,13 +12,69 @@ # include #endif +namespace NetMsgType { +const char *VERSION="version"; +const char *VERACK="verack"; +const char *ADDR="addr"; +const char *INV="inv"; +const char *GETDATA="getdata"; +const char *MERKLEBLOCK="merkleblock"; +const char *GETBLOCKS="getblocks"; +const char *GETHEADERS="getheaders"; +const char *TX="tx"; +const char *HEADERS="headers"; +const char *BLOCK="block"; +const char *GETADDR="getaddr"; +const char *MEMPOOL="mempool"; +const char *PING="ping"; +const char *PONG="pong"; +const char *ALERT="alert"; +const char *NOTFOUND="notfound"; +const char *FILTERLOAD="filterload"; +const char *FILTERADD="filteradd"; +const char *FILTERCLEAR="filterclear"; +const char *REJECT="reject"; +const char *SENDHEADERS="sendheaders"; +const char *FEEFILTER="feefilter"; +}; + static const char* ppszTypeName[] = { - "ERROR", - "tx", - "block", - "filtered block" + "ERROR", // Should never occur + NetMsgType::TX, + NetMsgType::BLOCK, + "filtered block" // Should never occur +}; + +/** All known message types. Keep this in the same order as the list of + * messages above and in protocol.h. + */ +const static std::string allNetMessageTypes[] = { + NetMsgType::VERSION, + NetMsgType::VERACK, + NetMsgType::ADDR, + NetMsgType::INV, + NetMsgType::GETDATA, + NetMsgType::MERKLEBLOCK, + NetMsgType::GETBLOCKS, + NetMsgType::GETHEADERS, + NetMsgType::TX, + NetMsgType::HEADERS, + NetMsgType::BLOCK, + NetMsgType::GETADDR, + NetMsgType::MEMPOOL, + NetMsgType::PING, + NetMsgType::PONG, + NetMsgType::ALERT, + NetMsgType::NOTFOUND, + NetMsgType::FILTERLOAD, + NetMsgType::FILTERADD, + NetMsgType::FILTERCLEAR, + NetMsgType::REJECT, + NetMsgType::SENDHEADERS, + NetMsgType::FEEFILTER, }; +const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn) { @@ -140,3 +196,8 @@ std::string CInv::ToString() const { return strprintf("%s %s", GetCommand(), hash.ToString()); } + +const std::vector &getAllNetMessageTypes() +{ + return allNetMessageTypesVec; +} diff --git a/src/protocol.h b/src/protocol.h index 6d9e928b1..5d08b77e5 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -65,6 +65,170 @@ class CMessageHeader unsigned int nChecksum; }; +/** + * Bitcoin protocol message types. When adding new message types, don't forget + * to update allNetMessageTypes in protocol.cpp. + */ +namespace NetMsgType { + +/** + * The version message provides information about the transmitting node to the + * receiving node at the beginning of a connection. + * @see https://bitcoin.org/en/developer-reference#version + */ +extern const char *VERSION; +/** + * The verack message acknowledges a previously-received version message, + * informing the connecting node that it can begin to send other messages. + * @see https://bitcoin.org/en/developer-reference#verack + */ +extern const char *VERACK; +/** + * The addr (IP address) message relays connection information for peers on the + * network. + * @see https://bitcoin.org/en/developer-reference#addr + */ +extern const char *ADDR; +/** + * The inv message (inventory message) transmits one or more inventories of + * objects known to the transmitting peer. + * @see https://bitcoin.org/en/developer-reference#inv + */ +extern const char *INV; +/** + * The getdata message requests one or more data objects from another node. + * @see https://bitcoin.org/en/developer-reference#getdata + */ +extern const char *GETDATA; +/** + * The merkleblock message is a reply to a getdata message which requested a + * block using the inventory type MSG_MERKLEBLOCK. + * @since protocol version 70001 as described by BIP37. + * @see https://bitcoin.org/en/developer-reference#merkleblock + */ +extern const char *MERKLEBLOCK; +/** + * The getblocks message requests an inv message that provides block header + * hashes starting from a particular point in the block chain. + * @see https://bitcoin.org/en/developer-reference#getblocks + */ +extern const char *GETBLOCKS; +/** + * The getheaders message requests a headers message that provides block + * headers starting from a particular point in the block chain. + * @since protocol version 31800. + * @see https://bitcoin.org/en/developer-reference#getheaders + */ +extern const char *GETHEADERS; +/** + * The tx message transmits a single transaction. + * @see https://bitcoin.org/en/developer-reference#tx + */ +extern const char *TX; +/** + * The headers message sends one or more block headers to a node which + * previously requested certain headers with a getheaders message. + * @since protocol version 31800. + * @see https://bitcoin.org/en/developer-reference#headers + */ +extern const char *HEADERS; +/** + * The block message transmits a single serialized block. + * @see https://bitcoin.org/en/developer-reference#block + */ +extern const char *BLOCK; +/** + * The getaddr message requests an addr message from the receiving node, + * preferably one with lots of IP addresses of other receiving nodes. + * @see https://bitcoin.org/en/developer-reference#getaddr + */ +extern const char *GETADDR; +/** + * The mempool message requests the TXIDs of transactions that the receiving + * node has verified as valid but which have not yet appeared in a block. + * @since protocol version 60002. + * @see https://bitcoin.org/en/developer-reference#mempool + */ +extern const char *MEMPOOL; +/** + * The ping message is sent periodically to help confirm that the receiving + * peer is still connected. + * @see https://bitcoin.org/en/developer-reference#ping + */ +extern const char *PING; +/** + * The pong message replies to a ping message, proving to the pinging node that + * the ponging node is still alive. + * @since protocol version 60001 as described by BIP31. + * @see https://bitcoin.org/en/developer-reference#pong + */ +extern const char *PONG; +/** + * The alert message warns nodes of problems that may affect them or the rest + * of the network. + * @since protocol version 311. + * @see https://bitcoin.org/en/developer-reference#alert + */ +extern const char *ALERT; +/** + * The notfound message is a reply to a getdata message which requested an + * object the receiving node does not have available for relay. + * @ince protocol version 70001. + * @see https://bitcoin.org/en/developer-reference#notfound + */ +extern const char *NOTFOUND; +/** + * The filterload message tells the receiving peer to filter all relayed + * transactions and requested merkle blocks through the provided filter. + * @since protocol version 70001 as described by BIP37. + * Only available with service bit NODE_BLOOM since protocol version + * 70011 as described by BIP111. + * @see https://bitcoin.org/en/developer-reference#filterload + */ +extern const char *FILTERLOAD; +/** + * The filteradd message tells the receiving peer to add a single element to a + * previously-set bloom filter, such as a new public key. + * @since protocol version 70001 as described by BIP37. + * Only available with service bit NODE_BLOOM since protocol version + * 70011 as described by BIP111. + * @see https://bitcoin.org/en/developer-reference#filteradd + */ +extern const char *FILTERADD; +/** + * The filterclear message tells the receiving peer to remove a previously-set + * bloom filter. + * @since protocol version 70001 as described by BIP37. + * Only available with service bit NODE_BLOOM since protocol version + * 70011 as described by BIP111. + * @see https://bitcoin.org/en/developer-reference#filterclear + */ +extern const char *FILTERCLEAR; +/** + * The reject message informs the receiving node that one of its previous + * messages has been rejected. + * @since protocol version 70002 as described by BIP61. + * @see https://bitcoin.org/en/developer-reference#reject + */ +extern const char *REJECT; +/** + * Indicates that a node prefers to receive new block announcements via a + * "headers" message rather than an "inv". + * @since protocol version 70012 as described by BIP130. + * @see https://bitcoin.org/en/developer-reference#sendheaders + */ +extern const char *SENDHEADERS; +/** + * The feefilter message tells the receiving peer not to inv us any txs + * which do not meet the specified min fee rate. + * @since protocol version 70013 as described by BIP133 + */ +extern const char *FEEFILTER; +}; + +/* Get a vector of all valid message types (see above) */ +const std::vector &getAllNetMessageTypes(); + /** nServices flags */ enum { // NODE_NETWORK means that the node is capable of serving the block chain. It is currently From 74599c4001d4e5808f4301b8ad4c85768fc74193 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 08:45:33 +0000 Subject: [PATCH 114/146] net: Consistent checksum handling --- src/main.cpp | 9 +++++---- src/net.cpp | 6 ++---- src/protocol.cpp | 4 ++-- src/protocol.h | 10 +++++----- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0dbfd8b77..9acceef87 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6482,11 +6482,12 @@ bool ProcessMessages(CNode* pfrom) // Checksum CDataStream& vRecv = msg.vRecv; uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); - unsigned int nChecksum = ReadLE32((unsigned char*)&hash); - if (nChecksum != hdr.nChecksum) + if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) { - LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", __func__, - SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); + LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR expected %s was %s\n", __func__, + SanitizeString(strCommand), nMessageSize, + HexStr(hash.begin(), hash.begin()+CMessageHeader::CHECKSUM_SIZE), + HexStr(hdr.pchChecksum, hdr.pchChecksum+CMessageHeader::CHECKSUM_SIZE)); continue; } diff --git a/src/net.cpp b/src/net.cpp index 0ecf69923..a38a238f3 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2344,10 +2344,8 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) // Set the checksum uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); - memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); + assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + CMessageHeader::CHECKSUM_SIZE); + memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], hash.begin(), CMessageHeader::CHECKSUM_SIZE); LogPrint("net", "(%d bytes) peer=%d\n", nSize, id); diff --git a/src/protocol.cpp b/src/protocol.cpp index cdac35b7a..10321ed40 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -81,7 +81,7 @@ CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn) memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); memset(pchCommand, 0, sizeof(pchCommand)); nMessageSize = -1; - nChecksum = 0; + memset(pchChecksum, 0, CHECKSUM_SIZE); } CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn) @@ -90,7 +90,7 @@ CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const memset(pchCommand, 0, sizeof(pchCommand)); strncpy(pchCommand, pszCommand, COMMAND_SIZE); nMessageSize = nMessageSizeIn; - nChecksum = 0; + memset(pchChecksum, 0, CHECKSUM_SIZE); } std::string CMessageHeader::GetCommand() const diff --git a/src/protocol.h b/src/protocol.h index 5d08b77e5..14cd29def 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -45,15 +45,15 @@ class CMessageHeader READWRITE(FLATDATA(pchMessageStart)); READWRITE(FLATDATA(pchCommand)); READWRITE(nMessageSize); - READWRITE(nChecksum); + READWRITE(FLATDATA(pchChecksum)); } // TODO: make private (improves encapsulation) public: enum { COMMAND_SIZE = 12, - MESSAGE_SIZE_SIZE = sizeof(int), - CHECKSUM_SIZE = sizeof(int), + MESSAGE_SIZE_SIZE = 4, + CHECKSUM_SIZE = 4, MESSAGE_SIZE_OFFSET = MESSAGE_START_SIZE + COMMAND_SIZE, CHECKSUM_OFFSET = MESSAGE_SIZE_OFFSET + MESSAGE_SIZE_SIZE, @@ -61,8 +61,8 @@ class CMessageHeader }; char pchMessageStart[MESSAGE_START_SIZE]; char pchCommand[COMMAND_SIZE]; - unsigned int nMessageSize; - unsigned int nChecksum; + uint32_t nMessageSize; + uint8_t pchChecksum[CHECKSUM_SIZE]; }; /** From 6c8ea3d33cc59b39135008e1163b30847a983eee Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 09:49:10 +0000 Subject: [PATCH 115/146] net: Bitcoin backports: - Add support for dnsseeds with option to filter by servicebits - Do not set extra flags for unfiltered DNS seed results - net: Split DNS resolving functionality out of net structures --- src/chainparams.h | 3 ++- src/init.cpp | 7 ++++--- src/net.cpp | 29 +++++++++++++++++++++++++---- src/netbase.cpp | 38 ++++++++++++++++++++------------------ src/netbase.h | 20 ++++++++++---------- src/protocol.h | 4 +++- src/torcontrol.cpp | 2 +- 7 files changed, 65 insertions(+), 38 deletions(-) diff --git a/src/chainparams.h b/src/chainparams.h index 8b9461842..a83e1c097 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -16,7 +16,8 @@ struct CDNSSeedData { std::string name, host; - CDNSSeedData(const std::string &strName, const std::string &strHost) : name(strName), host(strHost) {} + bool supportsServiceBitsFiltering; + CDNSSeedData(const std::string &strName, const std::string &strHost, bool supportsServiceBitsFilteringIn = false) : name(strName), host(strHost), supportsServiceBitsFiltering(supportsServiceBitsFilteringIn) {} }; struct SeedSpec6 { diff --git a/src/init.cpp b/src/init.cpp index 2b95c2593..d7967b8c6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1459,10 +1459,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (mapArgs.count("-externalip")) { for (const std::string& strAddr : mapMultiArgs["-externalip"]) { - CService addrLocal(strAddr, GetListenPort(), fNameLookup); - if (!addrLocal.IsValid()) + CService addrLocal; + if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) + AddLocal(addrLocal, LOCAL_MANUAL); + else return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr)); - AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); } } diff --git a/src/net.cpp b/src/net.cpp index a38a238f3..20ee16b5a 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1359,6 +1359,18 @@ void ThreadSocketHandler() } +static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredServiceBits) +{ + //use default host for non-filter-capable seeds or if we use the default service bits (NODE_NETWORK) + if (!data.supportsServiceBitsFiltering || *requiredServiceBits == NODE_NETWORK) { + *requiredServiceBits = NODE_NETWORK; + return data.host; + } + + return strprintf("x%x.%s", *requiredServiceBits, data.host); +} + + void ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute @@ -1384,18 +1396,27 @@ void ThreadDNSAddressSeed() } else { vector vIPs; vector vAdd; - if (LookupHost(seed.host.c_str(), vIPs)) + ServiceFlags requiredServiceBits = NODE_NETWORK; + if (LookupHost(GetDNSHost(seed, &requiredServiceBits).c_str(), vIPs, 0, true)) { for (const CNetAddr& ip : vIPs) { int nOneDay = 24*3600; - CAddress addr = CAddress(CService(ip, Params().GetDefaultPort())); + CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits); addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old vAdd.push_back(addr); found++; } } - addrman.Add(vAdd, CNetAddr(seed.name, true)); + // TODO: The seed name resolve may fail, yielding an IP of [::], which results in + // addrman assigning the same source to results from different seeds. + // This should switch to a hard-coded stable dummy IP for each seed name, so that the + // resolve is not required at all. + if (!vIPs.empty()) { + CService seedSource; + Lookup(seed.name.c_str(), seedSource, 0, true); + addrman.Add(vAdd, seedSource); + } } } @@ -1824,7 +1845,7 @@ void static Discover(boost::thread_group& threadGroup) if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { vector vaddr; - if (LookupHost(pszHostName, vaddr)) + if (LookupHost(pszHostName, vaddr, 0, true)) { for (const CNetAddr &addr : vaddr) { diff --git a/src/netbase.cpp b/src/netbase.cpp index 6d1d856f5..054871b16 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -612,10 +612,12 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest proxyType nameProxy; GetNameProxy(nameProxy); - CService addrResolved(CNetAddr(strDest, fNameLookup && !HaveNameProxy()), port); - if (addrResolved.IsValid()) { - addr = addrResolved; - return ConnectSocket(addr, hSocketRet, nTimeout); + CService addrResolved; + if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy())) { + if (addrResolved.IsValid()) { + addr = addrResolved; + return ConnectSocket(addr, hSocketRet, nTimeout); + } } addr = CService("0.0.0.0:0"); @@ -682,19 +684,19 @@ CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr) SetRaw(NET_IPV6, (const uint8_t*)&ipv6Addr); } -CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup) +CNetAddr::CNetAddr(const char *pszIp) { Init(); std::vector vIP; - if (LookupHost(pszIp, vIP, 1, fAllowLookup)) + if (LookupHost(pszIp, vIP, 1, false)) *this = vIP[0]; } -CNetAddr::CNetAddr(const std::string &strIp, bool fAllowLookup) +CNetAddr::CNetAddr(const std::string &strIp) { Init(); std::vector vIP; - if (LookupHost(strIp.c_str(), vIP, 1, fAllowLookup)) + if (LookupHost(strIp.c_str(), vIP, 1, false)) *this = vIP[0]; } @@ -1117,35 +1119,35 @@ bool CService::SetSockAddr(const struct sockaddr *paddr) } } -CService::CService(const char *pszIpPort, bool fAllowLookup) +CService::CService(const char *pszIpPort) { Init(); CService ip; - if (Lookup(pszIpPort, ip, 0, fAllowLookup)) + if (Lookup(pszIpPort, ip, 0, false)) *this = ip; } -CService::CService(const char *pszIpPort, int portDefault, bool fAllowLookup) +CService::CService(const char *pszIpPort, int portDefault) { Init(); CService ip; - if (Lookup(pszIpPort, ip, portDefault, fAllowLookup)) + if (Lookup(pszIpPort, ip, portDefault, false)) *this = ip; } -CService::CService(const std::string &strIpPort, bool fAllowLookup) +CService::CService(const std::string &strIpPort) { Init(); CService ip; - if (Lookup(strIpPort.c_str(), ip, 0, fAllowLookup)) + if (Lookup(strIpPort.c_str(), ip, 0, false)) *this = ip; } -CService::CService(const std::string &strIpPort, int portDefault, bool fAllowLookup) +CService::CService(const std::string &strIpPort, int portDefault) { Init(); CService ip; - if (Lookup(strIpPort.c_str(), ip, portDefault, fAllowLookup)) + if (Lookup(strIpPort.c_str(), ip, portDefault, false)) *this = ip; } @@ -1233,7 +1235,7 @@ CSubNet::CSubNet(): memset(netmask, 0, sizeof(netmask)); } -CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup) +CSubNet::CSubNet(const std::string &strSubnet) { size_t slash = strSubnet.find_last_of('/'); std::vector vIP; @@ -1243,7 +1245,7 @@ CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup) memset(netmask, 255, sizeof(netmask)); std::string strAddress = strSubnet.substr(0, slash); - if (LookupHost(strAddress.c_str(), vIP, 1, fAllowLookup)) + if (LookupHost(strAddress.c_str(), vIP, 1, false)) { network = vIP[0]; if (slash != strSubnet.npos) diff --git a/src/netbase.h b/src/netbase.h index 920996630..97b65516f 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -43,8 +43,8 @@ class CNetAddr public: CNetAddr(); CNetAddr(const struct in_addr& ipv4Addr); - explicit CNetAddr(const char *pszIp, bool fAllowLookup = false); - explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false); + explicit CNetAddr(const char *pszIp); + explicit CNetAddr(const std::string &strIp); void Init(); void SetIP(const CNetAddr& ip); @@ -113,7 +113,7 @@ class CSubNet public: CSubNet(); - explicit CSubNet(const std::string &strSubnet, bool fAllowLookup = false); + explicit CSubNet(const std::string &strSubnet); //constructor for single ip subnet (/32 or /128) explicit CSubNet(const CNetAddr &addr); @@ -148,10 +148,10 @@ class CService : public CNetAddr CService(const CNetAddr& ip, unsigned short port); CService(const struct in_addr& ipv4Addr, unsigned short port); CService(const struct sockaddr_in& addr); - explicit CService(const char *pszIpPort, int portDefault, bool fAllowLookup = false); - explicit CService(const char *pszIpPort, bool fAllowLookup = false); - explicit CService(const std::string& strIpPort, int portDefault, bool fAllowLookup = false); - explicit CService(const std::string& strIpPort, bool fAllowLookup = false); + explicit CService(const char *pszIpPort, int portDefault); + explicit CService(const char *pszIpPort); + explicit CService(const std::string& strIpPort, int portDefault); + explicit CService(const std::string& strIpPort); void Init(); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; @@ -199,9 +199,9 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); -bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); -bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true); -bool Lookup(const char *pszName, std::vector& vAddr, int portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0); +bool LookupHost(const char *pszName, std::vector& vIP, unsigned int nMaxSolutions, bool fAllowLookup); +bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup); +bool Lookup(const char *pszName, std::vector& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions); bool LookupNumeric(const char *pszName, CService& addr, int portDefault = 0); bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = 0); bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = 0); diff --git a/src/protocol.h b/src/protocol.h index 14cd29def..821fa7b74 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -230,7 +230,9 @@ extern const char *FEEFILTER; const std::vector &getAllNetMessageTypes(); /** nServices flags */ -enum { +enum ServiceFlags : uint64_t { + // Nothing + NODE_NONE = 0, // NODE_NETWORK means that the node is capable of serving the block chain. It is currently // set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want // network services but don't provide them. diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 21f746765..08378e35a 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -504,7 +504,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe return; } - service = CService(service_id+".onion", GetListenPort(), false); + service = CService(service_id+".onion", GetListenPort()); LogPrintf("tor: Got service ID %s, advertizing service %s\n", service_id, service.ToString()); if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile().string()); From 648e4f9b1789c015993205d490a2fb3a40b748ca Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 10:00:04 +0000 Subject: [PATCH 116/146] protocol.h/cpp: Removes NetMsgType::ALERT --- src/protocol.cpp | 2 -- src/protocol.h | 7 ------- 2 files changed, 9 deletions(-) diff --git a/src/protocol.cpp b/src/protocol.cpp index 10321ed40..f373f2036 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -28,7 +28,6 @@ const char *GETADDR="getaddr"; const char *MEMPOOL="mempool"; const char *PING="ping"; const char *PONG="pong"; -const char *ALERT="alert"; const char *NOTFOUND="notfound"; const char *FILTERLOAD="filterload"; const char *FILTERADD="filteradd"; @@ -65,7 +64,6 @@ const static std::string allNetMessageTypes[] = { NetMsgType::MEMPOOL, NetMsgType::PING, NetMsgType::PONG, - NetMsgType::ALERT, NetMsgType::NOTFOUND, NetMsgType::FILTERLOAD, NetMsgType::FILTERADD, diff --git a/src/protocol.h b/src/protocol.h index 821fa7b74..0c9eb12b0 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -163,13 +163,6 @@ extern const char *PING; * @see https://bitcoin.org/en/developer-reference#pong */ extern const char *PONG; -/** - * The alert message warns nodes of problems that may affect them or the rest - * of the network. - * @since protocol version 311. - * @see https://bitcoin.org/en/developer-reference#alert - */ -extern const char *ALERT; /** * The notfound message is a reply to a getdata message which requested an * object the receiving node does not have available for relay. From df7388019a51840e8e07ed581f4036a71bfd0fa1 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 11:34:47 +0000 Subject: [PATCH 117/146] Alter assumptions in CCoinsViewCache::BatchWrite --- src/coins.cpp | 19 ++++++++++++------- src/test/coins_tests.cpp | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/coins.cpp b/src/coins.cpp index de8324aaa..e3f7eddd9 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -488,18 +488,23 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization). CCoinsMap::iterator itUs = cacheCoins.find(it->first); if (itUs == cacheCoins.end()) { - if (!it->second.coins.IsPruned()) { - // The parent cache does not have an entry, while the child - // cache does have (a non-pruned) one. Move the data up, and - // mark it as fresh (if the grandparent did have it, we - // would have pulled it in at first GetCoins). - assert(it->second.flags & CCoinsCacheEntry::FRESH); + // The parent cache does not have an entry, while the child does + // We can ignore it if it's both FRESH and pruned in the child + if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coins.IsPruned())) { + // Otherwise we will need to create it in the parent + // and move the data up and mark it as dirty CCoinsCacheEntry& entry = cacheCoins[it->first]; entry.coins.swap(it->second.coins); cachedCoinsUsage += entry.coins.DynamicMemoryUsage(); - entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH; + entry.flags = CCoinsCacheEntry::DIRTY; + // We can mark it FRESH in the parent if it was FRESH in the child + // Otherwise it might have just been flushed from the parent's cache + // and already exist in the grandparent + if (it->second.flags & CCoinsCacheEntry::FRESH) + entry.flags |= CCoinsCacheEntry::FRESH; } } else { + // Found the entry in the parent cache if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { // The grandparent does not have an entry, and the child is // modified and being pruned. This means we can just delete diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 383959b57..59eb5e4d1 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -841,14 +841,23 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) } } + if (insecure_rand() % 100 == 0) { + // Every 100 iterations, flush an intermediate cache + if (stack.size() > 1 && insecure_rand() % 2 == 0) { + unsigned int flushIndex = insecure_rand() % (stack.size() - 1); + stack[flushIndex]->Flush(); + } + } if (insecure_rand() % 100 == 0) { // Every 100 iterations, change the cache stack. if (stack.size() > 0 && insecure_rand() % 2 == 0) { + //Remove the top cache stack.back()->Flush(); delete stack.back(); stack.pop_back(); } if (stack.size() == 0 || (stack.size() < 4 && insecure_rand() % 2)) { + //Add a new cache CCoinsView* tip = &base; if (stack.size() > 0) { tip = stack.back(); @@ -998,6 +1007,13 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) } } + if (insecure_rand() % 100 == 0) { + // Every 100 iterations, flush an intermediate cache + if (stack.size() > 1 && insecure_rand() % 2 == 0) { + unsigned int flushIndex = insecure_rand() % (stack.size() - 1); + stack[flushIndex]->Flush(); + } + } if (insecure_rand() % 100 == 0) { // Every 100 iterations, change the cache stack. if (stack.size() > 0 && insecure_rand() % 2 == 0) { From 9be7f5875029bc5e9f67a5fd5e9e40f9cfa35d47 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 12:13:25 +0000 Subject: [PATCH 118/146] Fix invalid instantiation and possibly unsafe accesses of array in class base_uint --- src/arith_uint256.cpp | 2 ++ src/arith_uint256.h | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp index 6c2f8dbea..2fff6dda9 100644 --- a/src/arith_uint256.cpp +++ b/src/arith_uint256.cpp @@ -15,6 +15,8 @@ template base_uint::base_uint(const std::string& str) { + static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); + SetHex(str); } diff --git a/src/arith_uint256.h b/src/arith_uint256.h index 833d41ef2..af0d9b79d 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -31,12 +31,16 @@ class base_uint base_uint() { + static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); + for (int i = 0; i < WIDTH; i++) pn[i] = 0; } base_uint(const base_uint& b) { + static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); + for (int i = 0; i < WIDTH; i++) pn[i] = b.pn[i]; } @@ -50,6 +54,8 @@ class base_uint base_uint(uint64_t b) { + static_assert(BITS/32 > 0 && BITS%32 == 0, "Template parameter BITS must be a positive multiple of 32."); + pn[0] = (unsigned int)b; pn[1] = (unsigned int)(b >> 32); for (int i = 2; i < WIDTH; i++) @@ -174,7 +180,7 @@ class base_uint { // prefix operator int i = 0; - while (++pn[i] == 0 && i < WIDTH-1) + while (i < WIDTH && ++pn[i] == 0) i++; return *this; } @@ -191,7 +197,7 @@ class base_uint { // prefix operator int i = 0; - while (--pn[i] == (uint32_t)-1 && i < WIDTH-1) + while (i < WIDTH && --pn[i] == (uint32_t)-1) i++; return *this; } From 7a68e2ca283e92475b43e92f625edf48f584ec2d Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 12:41:01 +0000 Subject: [PATCH 119/146] Addrman offline attempts --- src/addrman.cpp | 10 ++++++++-- src/addrman.h | 22 ++++++++++++++++------ src/net.cpp | 20 ++++++++++---------- src/net.h | 2 +- src/rpc/net.cpp | 2 +- 5 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/addrman.cpp b/src/addrman.cpp index c1ebea0a2..4f1280a55 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -196,6 +196,9 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId) void CAddrMan::Good_(const CService& addr, int64_t nTime) { int nId; + + nLastGood = nTime; + CAddrInfo* pinfo = Find(addr, &nId); // if not found, bail out @@ -310,7 +313,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP return fNew; } -void CAddrMan::Attempt_(const CService& addr, int64_t nTime) +void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime) { CAddrInfo* pinfo = Find(addr); @@ -326,7 +329,10 @@ void CAddrMan::Attempt_(const CService& addr, int64_t nTime) // update info info.nLastTry = nTime; - info.nAttempts++; + if (fCountFailure && info.nLastCountAttempt < nLastGood) { + info.nLastCountAttempt = nTime; + info.nAttempts++; + } } CAddrInfo CAddrMan::Select_(bool newOnly) diff --git a/src/addrman.h b/src/addrman.h index 6a7c1634a..e551568bd 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -28,6 +28,9 @@ class CAddrInfo : public CAddress //! last try whatsoever by us (memory only) int64_t nLastTry; + //! last counted attempt (memory only) + int64_t nLastCountAttempt; + private: //! where knowledge about this address first came from CNetAddr source; @@ -65,6 +68,7 @@ class CAddrInfo : public CAddress { nLastSuccess = 0; nLastTry = 0; + nLastCountAttempt = 0; nAttempts = 0; nRefCount = 0; fInTried = false; @@ -199,6 +203,9 @@ class CAddrMan //! list of "new" buckets int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE]; + //! last time Good was called (memory only) + int64_t nLastGood; + protected: //! secret key to randomize bucket select with uint256 nKey; @@ -232,7 +239,7 @@ class CAddrMan bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty); //! Mark an entry as attempted to connect. - void Attempt_(const CService &addr, int64_t nTime); + void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime); //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. CAddrInfo Select_(bool newOnly); @@ -455,6 +462,7 @@ class CAddrMan nIdCount = 0; nTried = 0; nNew = 0; + nLastGood = 1; //Initially at 1 so that "never" is strictly worse. } CAddrMan() @@ -524,12 +532,14 @@ class CAddrMan } //! Mark an entry as connection attempted to. - void Attempt(const CService &addr, int64_t nTime = GetTime()) + void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetTime()) { - LOCK(cs); - Check(); - Attempt_(addr, nTime); - Check(); + { + LOCK(cs); + Check(); + Attempt_(addr, fCountFailure, nTime); + Check(); + } } /** diff --git a/src/net.cpp b/src/net.cpp index 20ee16b5a..08bf2e8fe 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -354,7 +354,7 @@ CNode* FindNode(const CService& addr) return NULL; } -CNode* ConnectNode(CAddress addrConnect, const char *pszDest) +CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) { if (pszDest == NULL) { if (IsLocal(addrConnect)) @@ -387,7 +387,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) return NULL; } - addrman.Attempt(addrConnect); + addrman.Attempt(addrConnect, fCountFailure); // Add node CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); @@ -402,7 +402,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) } else if (!proxyConnectionFailed) { // If connecting to the node failed, and failure is not caused by a problem connecting to // the proxy, mark this as an attempt. - addrman.Attempt(addrConnect); + addrman.Attempt(addrConnect, fCountFailure); } return NULL; @@ -1464,7 +1464,7 @@ void static ProcessOneShot() CAddress addr; CSemaphoreGrant grant(*semOutbound, true); if (grant) { - if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true)) + if (!OpenNetworkConnection(addr, false, &grant, strDest.c_str(), true)) AddOneShot(strDest); } } @@ -1480,7 +1480,7 @@ void ThreadOpenConnections() for (const std::string& strAddr : mapMultiArgs["-connect"]) { CAddress addr; - OpenNetworkConnection(addr, NULL, strAddr.c_str()); + OpenNetworkConnection(addr, false, NULL, strAddr.c_str()); for (int i = 0; i < 10 && i < nLoop; i++) { MilliSleep(500); @@ -1564,7 +1564,7 @@ void ThreadOpenConnections() } if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect, &grant); + OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant); } } @@ -1586,7 +1586,7 @@ void ThreadOpenAddedConnections() for (const std::string& strAddNode : lAddresses) { CAddress addr; CSemaphoreGrant grant(*semOutbound); - OpenNetworkConnection(addr, &grant, strAddNode.c_str()); + OpenNetworkConnection(addr, false, &grant, strAddNode.c_str()); MilliSleep(500); } MilliSleep(120000); // Retry every 2 minutes @@ -1632,7 +1632,7 @@ void ThreadOpenAddedConnections() for (vector& vserv : lservAddressesToAdd) { CSemaphoreGrant grant(*semOutbound); - OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); + OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), false, &grant); MilliSleep(500); } MilliSleep(120000); // Retry every 2 minutes @@ -1640,7 +1640,7 @@ void ThreadOpenAddedConnections() } // if successful, this moves the passed grant to the constructed node -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot) +bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot) { // // Initiate outbound network connection @@ -1654,7 +1654,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu } else if (FindNode(std::string(pszDest))) return false; - CNode* pnode = ConnectNode(addrConnect, pszDest); + CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure); boost::this_thread::interruption_point(); if (!pnode) diff --git a/src/net.h b/src/net.h index 4ad4b8089..75c856fe3 100644 --- a/src/net.h +++ b/src/net.h @@ -83,7 +83,7 @@ CNode* FindNode(const CSubNet& subNet); CNode* FindNode(const std::string& addrName); CNode* FindNode(const CService& ip); CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL); -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); +bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 1c38d4b96..30201e3c0 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -188,7 +188,7 @@ UniValue addnode(const UniValue& params, bool fHelp) if (strCommand == "onetry") { CAddress addr; - OpenNetworkConnection(addr, NULL, strNode.c_str()); + OpenNetworkConnection(addr, false, NULL, strNode.c_str()); return NullUniValue; } From efbbb17902c70fd044fbba05339e91cebd15b0bb Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 13:26:02 +0000 Subject: [PATCH 120/146] Remove unused variables --- contrib/testgen/base58.py | 1 - contrib/testgen/gen_base58_test_vectors.py | 1 - src/addrman.cpp | 3 --- 3 files changed, 5 deletions(-) diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py index b71649514..07073fc30 100644 --- a/contrib/testgen/base58.py +++ b/contrib/testgen/base58.py @@ -81,7 +81,6 @@ def b58decode_chk(v): result = b58decode(v) if result is None: return None - h3 = checksum(result[:-4]) if result[-4:] == checksum(result[:-4]): return result[:-4] else: diff --git a/contrib/testgen/gen_base58_test_vectors.py b/contrib/testgen/gen_base58_test_vectors.py index 17b85667a..294dea5cf 100755 --- a/contrib/testgen/gen_base58_test_vectors.py +++ b/contrib/testgen/gen_base58_test_vectors.py @@ -42,7 +42,6 @@ def is_valid(v): result = b58decode_chk(v) if result is None: return False - valid = False for template in templates: prefix = str(bytearray(template[0])) suffix = str(bytearray(template[2])) diff --git a/src/addrman.cpp b/src/addrman.cpp index 4f1280a55..2347141c3 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -53,11 +53,8 @@ double CAddrInfo::GetChance(int64_t nNow) const { double fChance = 1.0; - int64_t nSinceLastSeen = nNow - nTime; int64_t nSinceLastTry = nNow - nLastTry; - if (nSinceLastSeen < 0) - nSinceLastSeen = 0; if (nSinceLastTry < 0) nSinceLastTry = 0; From ea60b2a8d772099f2ae022c25dbc162d24d20971 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 13:27:47 +0000 Subject: [PATCH 121/146] Use z = std::max(x - y, 0) instead of z = x - y; if (z < 0) z = 0; --- src/addrman.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/addrman.cpp b/src/addrman.cpp index 2347141c3..d664e1d7a 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -52,11 +52,7 @@ bool CAddrInfo::IsTerrible(int64_t nNow) const double CAddrInfo::GetChance(int64_t nNow) const { double fChance = 1.0; - - int64_t nSinceLastTry = nNow - nLastTry; - - if (nSinceLastTry < 0) - nSinceLastTry = 0; + int64_t nSinceLastTry = std::max(nNow - nLastTry, 0); // deprioritize very recent attempts away if (nSinceLastTry < 60 * 10) From 07a1f138394532c7ebd4a89f2c1c64dc50a67645 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 13:49:56 +0000 Subject: [PATCH 122/146] [LevelDB] Plug leveldb logs to BitcoinZ logs --- src/dbwrapper.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++ src/init.cpp | 2 +- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 5f4379acf..48e7b801f 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -12,9 +12,67 @@ #include #include #include +#include #include +class CBitcoinLevelDBLogger : public leveldb::Logger { +public: + // This code is adapted from posix_logger.h, which is why it is using vsprintf. + // Please do not do this in normal code + virtual void Logv(const char * format, va_list ap) override { + if (!LogAcceptCategory("leveldb")) + return; + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char* base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } + else { + bufsize = 30000; + base = new char[bufsize]; + } + char* p = base; + char* limit = base + bufsize; + + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + // Do not use vsnprintf elsewhere in bitcoin source code, see above. + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } + + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } + else { + p = limit - 1; + } + } + + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } + + assert(p <= limit); + base[std::min(bufsize - 1, (int)(p - base))] = '\0'; + LogPrintStr(base); + if (base != buffer) { + delete[] base; + } + break; + } + } +}; + static leveldb::Options GetOptions(size_t nCacheSize) { leveldb::Options options; @@ -23,6 +81,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) options.filter_policy = leveldb::NewBloomFilterPolicy(10); options.compression = leveldb::kNoCompression; options.max_open_files = 64; + options.info_log = new CBitcoinLevelDBLogger(); if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error // on corruption in later versions. @@ -63,6 +122,8 @@ CDBWrapper::~CDBWrapper() pdb = NULL; delete options.filter_policy; options.filter_policy = NULL; + delete options.info_log; + options.info_log = NULL; delete options.block_cache; options.block_cache = NULL; delete penv; diff --git a/src/init.cpp b/src/init.cpp index d7967b8c6..51dbd80be 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -432,7 +432,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-limitdescendantsize=", strprintf("Do not accept transactions if any ancestor would have more than kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)"); } - std::string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, libevent, lock, mempool, net, pow, proxy, prune, " + std::string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, leveldb, libevent, lock, mempool, net, pow, proxy, prune, " "rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these strUsage += HelpMessageOpt("-debug=", strprintf(_("Output debugging information (default: %u, supplying is optional)"), 0) + ". " + _("If is not supplied or if = 1, output all debugging information.") + " " + _(" can be:") + " " + debugCategories + ". " + From 1d3a12af95e2bd7a3443ae930fcf5753ea1c842e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Fri, 30 Aug 2024 16:50:49 +0000 Subject: [PATCH 123/146] Change LogAcceptCategory to use uint32_t rather than sets of strings --- src/Makefile.am | 2 + src/addrman.cpp | 2 +- src/addrman.h | 12 +- src/chainparams.cpp | 2 +- src/crypto/equihash.cpp | 50 ++-- src/dbwrapper.cpp | 3 +- src/httprpc.cpp | 6 +- src/httpserver.cpp | 37 +-- src/init.cpp | 50 ++-- src/logging.cpp | 277 ++++++++++++++++++ src/logging.h | 129 ++++++++ src/main.cpp | 135 ++++----- src/mempool_limit.cpp | 4 +- src/miner.cpp | 10 +- src/net.cpp | 48 +-- src/netbase.cpp | 4 +- src/policy/fees.cpp | 38 +-- src/pow.cpp | 4 +- src/rpc/server.cpp | 10 +- src/test/miner_tests.cpp | 2 +- src/timedata.cpp | 2 +- src/tinyformat.h | 15 +- src/torcontrol.cpp | 37 +-- src/transaction_builder.cpp | 6 +- src/txdb.cpp | 2 +- src/txmempool.cpp | 11 +- src/util.cpp | 197 +------------ src/util.h | 62 +--- .../asyncrpcoperation_mergetoaddress.cpp | 32 +- .../asyncrpcoperation_saplingmigration.cpp | 16 +- src/wallet/asyncrpcoperation_sendmany.cpp | 40 +-- .../asyncrpcoperation_shieldcoinbase.cpp | 16 +- src/wallet/db.cpp | 14 +- src/wallet/paymentdisclosure.cpp | 4 +- src/wallet/rpcdump.cpp | 4 +- src/wallet/wallet.cpp | 18 +- src/wallet/walletdb.cpp | 4 +- src/zmq/zmqnotificationinterface.cpp | 12 +- src/zmq/zmqpublishnotifier.cpp | 14 +- 39 files changed, 764 insertions(+), 567 deletions(-) create mode 100644 src/logging.cpp create mode 100644 src/logging.h diff --git a/src/Makefile.am b/src/Makefile.am index 1daf6cdad..a5ec751a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -135,6 +135,7 @@ BITCOIN_CORE_H = \ keystore.h \ dbwrapper.h \ limitedmap.h \ + logging.h \ main.h \ memusage.h \ merkleblock.h \ @@ -383,6 +384,7 @@ libbitcoin_util_a_SOURCES = \ compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ fs.cpp \ + logging.cpp \ random.cpp \ rpc/protocol.cpp \ support/cleanse.cpp \ diff --git a/src/addrman.cpp b/src/addrman.cpp index d664e1d7a..1e22df694 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -232,7 +232,7 @@ void CAddrMan::Good_(const CService& addr, int64_t nTime) if (nUBucket == -1) return; - LogPrint("addrman", "Moving %s to tried\n", addr.ToString()); + LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString()); // move nId to the tried tables MakeTried(info, nId); diff --git a/src/addrman.h b/src/addrman.h index e551568bd..c36935b90 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -438,7 +438,7 @@ class CAddrMan } } if (nLost + nLostUnk > 0) { - LogPrint("addrman", "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost); + LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost); } Check(); @@ -503,8 +503,9 @@ class CAddrMan Check(); fRet |= Add_(addr, source, nTimePenalty); Check(); - if (fRet) - LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew); + if (fRet) { + LogPrint(BCLog::ADDRMAN, "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew); + } return fRet; } @@ -517,8 +518,9 @@ class CAddrMan for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; Check(); - if (nAdd) - LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew); + if (nAdd) { + LogPrint(BCLog::ADDRMAN, "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew); + } return nAdd > 0; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index aeb22dc42..2f0772cca 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -810,7 +810,7 @@ bool checkEHParamaters(int solSize, int height, const CChainParams& params) { EHparameters ehparams[MAX_EH_PARAM_LIST_LEN]; int listlength = validEHparameterList(ehparams, height, params); for(int i = 0; i < listlength; i++){ - LogPrint("pow", "checkEHParamaters height: %d n:%d k:%d solsize: %d \n", + LogPrint(BCLog::POW, "checkEHParamaters height: %d n:%d k:%d solsize: %d \n", height, ehparams[i].n, ehparams[i].k, ehparams[i].nSolSize); if (ehparams[i].nSolSize == solSize) return true; diff --git a/src/crypto/equihash.cpp b/src/crypto/equihash.cpp index 4107883c1..c524e6236 100644 --- a/src/crypto/equihash.cpp +++ b/src/crypto/equihash.cpp @@ -335,7 +335,7 @@ bool Equihash::BasicSolve(const eh_HashState& base_state, eh_index init_size { 1 << (CollisionBitLength + 1) }; // 1) Generate first list - LogPrint("pow", "Generating first list\n"); + LogPrint(BCLog::POW, "Generating first list\n"); size_t hashLen = HashLength; size_t lenIndices = sizeof(eh_index); std::vector> X; @@ -352,13 +352,13 @@ bool Equihash::BasicSolve(const eh_HashState& base_state, // 3) Repeat step 2 until 2n/(k+1) bits remain for (int r = 1; r < K && X.size() > 0; r++) { - LogPrint("pow", "Round %d:\n", r); + LogPrint(BCLog::POW, "Round %d:\n", r); // 2a) Sort the list - LogPrint("pow", "- Sorting list\n"); + LogPrint(BCLog::POW, "- Sorting list\n"); std::sort(X.begin(), X.end(), CompareSR(CollisionByteLength)); if (cancelled(ListSorting)) throw solver_cancelled; - LogPrint("pow", "- Finding collisions\n"); + LogPrint(BCLog::POW, "- Finding collisions\n"); int i = 0; int posFree = 0; std::vector> Xc; @@ -410,12 +410,12 @@ bool Equihash::BasicSolve(const eh_HashState& base_state, } // k+1) Find a collision on last 2n(k+1) bits - LogPrint("pow", "Final round:\n"); + LogPrint(BCLog::POW, "Final round:\n"); if (X.size() > 1) { - LogPrint("pow", "- Sorting list\n"); + LogPrint(BCLog::POW, "- Sorting list\n"); std::sort(X.begin(), X.end(), CompareSR(hashLen)); if (cancelled(FinalSorting)) throw solver_cancelled; - LogPrint("pow", "- Finding collisions\n"); + LogPrint(BCLog::POW, "- Finding collisions\n"); int i = 0; while (i < X.size() - 1) { int j = 1; @@ -441,7 +441,7 @@ bool Equihash::BasicSolve(const eh_HashState& base_state, if (cancelled(FinalColliding)) throw solver_cancelled; } } else - LogPrint("pow", "- List is empty\n"); + LogPrint(BCLog::POW, "- List is empty\n"); return false; } @@ -514,7 +514,7 @@ bool Equihash::OptimisedSolve(const eh_HashState& base_state, { // 1) Generate first list - LogPrint("pow", "Generating first list\n"); + LogPrint(BCLog::POW, "Generating first list\n"); size_t hashLen = HashLength; size_t lenIndices = sizeof(eh_trunc); std::vector> Xt; @@ -531,13 +531,13 @@ bool Equihash::OptimisedSolve(const eh_HashState& base_state, // 3) Repeat step 2 until 2n/(k+1) bits remain for (int r = 1; r < K && Xt.size() > 0; r++) { - LogPrint("pow", "Round %d:\n", r); + LogPrint(BCLog::POW, "Round %d:\n", r); // 2a) Sort the list - LogPrint("pow", "- Sorting list\n"); + LogPrint(BCLog::POW, "- Sorting list\n"); std::sort(Xt.begin(), Xt.end(), CompareSR(CollisionByteLength)); if (cancelled(ListSorting)) throw solver_cancelled; - LogPrint("pow", "- Finding collisions\n"); + LogPrint(BCLog::POW, "- Finding collisions\n"); int i = 0; int posFree = 0; std::vector> Xc; @@ -596,12 +596,12 @@ bool Equihash::OptimisedSolve(const eh_HashState& base_state, } // k+1) Find a collision on last 2n(k+1) bits - LogPrint("pow", "Final round:\n"); + LogPrint(BCLog::POW, "Final round:\n"); if (Xt.size() > 1) { - LogPrint("pow", "- Sorting list\n"); + LogPrint(BCLog::POW, "- Sorting list\n"); std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen)); if (cancelled(FinalSorting)) throw solver_cancelled; - LogPrint("pow", "- Finding collisions\n"); + LogPrint(BCLog::POW, "- Finding collisions\n"); int i = 0; while (i < Xt.size() - 1) { int j = 1; @@ -625,14 +625,14 @@ bool Equihash::OptimisedSolve(const eh_HashState& base_state, if (cancelled(FinalColliding)) throw solver_cancelled; } } else - LogPrint("pow", "- List is empty\n"); + LogPrint(BCLog::POW, "- List is empty\n"); } // Ensure Xt goes out of scope and is destroyed - LogPrint("pow", "Found %d partial solutions\n", partialSolns.size()); + LogPrint(BCLog::POW, "Found %d partial solutions\n", partialSolns.size()); // Now for each solution run the algorithm again to recreate the indices - LogPrint("pow", "Culling solutions\n"); + LogPrint(BCLog::POW, "Culling solutions\n"); for (std::shared_ptr partialSoln : partialSolns) { std::set> solns; size_t hashLen; @@ -715,7 +715,7 @@ bool Equihash::OptimisedSolve(const eh_HashState& base_state, invalidsolution: invalidCount++; } - LogPrint("pow", "- Number of invalid solutions found: %d\n", invalidCount); + LogPrint(BCLog::POW, "- Number of invalid solutions found: %d\n", invalidCount); return false; } @@ -725,7 +725,7 @@ template bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector soln) { if (soln.size() != SolutionWidth) { - LogPrint("pow", "Invalid solution length: %d (expected %d)\n", + LogPrint(BCLog::POW, "Invalid solution length: %d (expected %d)\n", soln.size(), SolutionWidth); return false; } @@ -745,17 +745,17 @@ bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector< std::vector> Xc; for (int i = 0; i < X.size(); i += 2) { if (!HasCollision(X[i], X[i+1], CollisionByteLength)) { - LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n"); - LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen)); - LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex(hashLen)); + LogPrint(BCLog::POW, "Invalid solution: invalid collision length between StepRows\n"); + LogPrint(BCLog::POW, "X[i] = %s\n", X[i].GetHex(hashLen)); + LogPrint(BCLog::POW, "X[i+1] = %s\n", X[i+1].GetHex(hashLen)); return false; } if (X[i+1].IndicesBefore(X[i], hashLen, lenIndices)) { - LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n"); + LogPrint(BCLog::POW, "Invalid solution: Index tree incorrectly ordered\n"); return false; } if (!DistinctIndices(X[i], X[i+1], hashLen, lenIndices)) { - LogPrint("pow", "Invalid solution: duplicate indices\n"); + LogPrint(BCLog::POW, "Invalid solution: duplicate indices\n"); return false; } Xc.emplace_back(X[i], X[i+1], hashLen, lenIndices, CollisionByteLength); diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 48e7b801f..ae80d59fc 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -21,8 +21,9 @@ class CBitcoinLevelDBLogger : public leveldb::Logger { // This code is adapted from posix_logger.h, which is why it is using vsprintf. // Please do not do this in normal code virtual void Logv(const char * format, va_list ap) override { - if (!LogAcceptCategory("leveldb")) + if (!LogAcceptCategory(BCLog::LEVELDB)) { return; + } char buffer[500]; for (int iter = 0; iter < 2; iter++) { char* base; diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 5552c7c44..9a951747e 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -222,7 +222,7 @@ static bool InitRPCAuthentication() bool StartHTTPRPC() { - LogPrint("rpc", "Starting HTTP RPC server\n"); + LogPrint(BCLog::RPC, "Starting HTTP RPC server\n"); if (!InitRPCAuthentication()) return false; @@ -236,12 +236,12 @@ bool StartHTTPRPC() void InterruptHTTPRPC() { - LogPrint("rpc", "Interrupting HTTP RPC server\n"); + LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n"); } void StopHTTPRPC() { - LogPrint("rpc", "Stopping HTTP RPC server\n"); + LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n"); UnregisterHTTPHandler("/", true); if (httpRPCTimerInterface) { RPCUnregisterTimerInterface(httpRPCTimerInterface); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index f1ef39223..09482556a 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -209,7 +209,7 @@ static bool InitHTTPAllowList() std::string strAllowed; for (const CSubNet& subnet : rpc_allow_subnets) strAllowed += subnet.ToString() + " "; - LogPrint("http", "Allowing HTTP connections from: %s\n", strAllowed); + LogPrint(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed); return true; } @@ -239,7 +239,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg) { std::unique_ptr hreq(new HTTPRequest(req)); - LogPrint("http", "Received a %s request for %s from %s\n", + LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n", RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString()); // Early address-based allow check @@ -290,7 +290,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg) /** Callback to reject HTTP requests after shutdown. */ static void http_reject_request_cb(struct evhttp_request* req, void*) { - LogPrint("http", "Rejecting request while shutting down\n"); + LogPrint(BCLog::HTTP, "Rejecting request while shutting down\n"); evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL); } @@ -298,10 +298,10 @@ static void http_reject_request_cb(struct evhttp_request* req, void*) static bool ThreadHTTP(struct event_base* base, struct evhttp* http) { RenameThread("bitcoinz-http"); - LogPrint("http", "Entering http event loop\n"); + LogPrint(BCLog::HTTP, "Entering http event loop\n"); event_base_dispatch(base); // Event loop will be interrupted by InterruptHTTPServer() - LogPrint("http", "Exited http event loop\n"); + LogPrint(BCLog::HTTP, "Exited http event loop\n"); return event_base_got_break(base) == 0; } @@ -333,7 +333,7 @@ static bool HTTPBindAddresses(struct evhttp* http) // Bind addresses for (std::vector >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { - LogPrint("http", "Binding RPC on address %s port %i\n", i->first, i->second); + LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second); evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? NULL : i->first.c_str(), i->second); if (bind_handle) { boundSockets.push_back(bind_handle); @@ -361,7 +361,7 @@ static void libevent_log_cb(int severity, const char *msg) if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category LogPrintf("libevent: %s\n", msg); else - LogPrint("libevent", "libevent: %s\n", msg); + LogPrint(BCLog::LIBEVENT, "libevent: %s\n", msg); } bool InitHTTPServer() @@ -384,10 +384,11 @@ bool InitHTTPServer() #if LIBEVENT_VERSION_NUMBER >= 0x02010100 // If -debug=libevent, set full libevent debugging. // Otherwise, disable all libevent debugging. - if (LogAcceptCategory("libevent")) + if (LogAcceptCategory(BCLog::LIBEVENT)) { event_enable_debug_logging(EVENT_DBG_ALL); - else + } else { event_enable_debug_logging(EVENT_DBG_NONE); + } #endif #ifdef WIN32 evthread_use_windows_threads(); @@ -421,7 +422,7 @@ bool InitHTTPServer() return false; } - LogPrint("http", "Initialized HTTP server\n"); + LogPrint(BCLog::HTTP, "Initialized HTTP server\n"); int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L); LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth); @@ -436,7 +437,7 @@ std::future threadResult; bool StartHTTPServer() { - LogPrint("http", "Starting HTTP server\n"); + LogPrint(BCLog::HTTP, "Starting HTTP server\n"); int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); std::packaged_task task(ThreadHTTP); @@ -452,7 +453,7 @@ bool StartHTTPServer() void InterruptHTTPServer() { - LogPrint("http", "Interrupting HTTP server\n"); + LogPrint(BCLog::HTTP, "Interrupting HTTP server\n"); if (eventHTTP) { // Unlisten sockets for (evhttp_bound_socket *socket : boundSockets) { @@ -467,14 +468,14 @@ void InterruptHTTPServer() void StopHTTPServer() { - LogPrint("http", "Stopping HTTP server\n"); + LogPrint(BCLog::HTTP, "Stopping HTTP server\n"); if (workQueue) { - LogPrint("http", "Waiting for HTTP worker threads to exit\n"); + LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n"); workQueue->WaitExit(); delete workQueue; } if (eventBase) { - LogPrint("http", "Waiting for HTTP event thread to exit\n"); + LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n"); // Exit the event loop as soon as there are no active events. event_base_loopexit(eventBase, nullptr); // Give event loop a few seconds to exit (to send back last RPC responses), then break it @@ -497,7 +498,7 @@ void StopHTTPServer() event_base_free(eventBase); eventBase = 0; } - LogPrint("http", "Stopped HTTP server\n"); + LogPrint(BCLog::HTTP, "Stopped HTTP server\n"); } struct event_base* EventBase() @@ -644,7 +645,7 @@ HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler) { - LogPrint("http", "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); + LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); pathHandlers.push_back(HTTPPathHandler(prefix, exactMatch, handler)); } @@ -657,7 +658,7 @@ void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) break; if (i != iend) { - LogPrint("http", "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); + LogPrint(BCLog::HTTP, "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); pathHandlers.erase(i); } } diff --git a/src/init.cpp b/src/init.cpp index 51dbd80be..bb9035cbe 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -298,7 +298,7 @@ bool static Bind(const CService &addr, unsigned int flags) { void OnRPCStopped() { cvBlockChange.notify_all(); - LogPrint("rpc", "RPC stopped.\n"); + LogPrint(BCLog::RPC, "RPC stopped.\n"); } void OnRPCPreCommand(const CRPCCommand& cmd) @@ -432,11 +432,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-limitdescendantsize=", strprintf("Do not accept transactions if any ancestor would have more than kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)"); } - std::string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, http, leveldb, libevent, lock, mempool, net, pow, proxy, prune, " - "rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these strUsage += HelpMessageOpt("-debug=", strprintf(_("Output debugging information (default: %u, supplying is optional)"), 0) + ". " + - _("If is not supplied or if = 1, output all debugging information.") + " " + _(" can be:") + " " + debugCategories + ". " + - _("For multiple specific categories use -debug= multiple times.")); + _("If is not supplied or if = 1, output all debugging information.") + " " + _(" can be:") + " " + ListLogCategories() + "."); strUsage += HelpMessageOpt("-experimentalfeatures", _("Enable use of experimental features")); strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)")); strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS)); @@ -1090,18 +1087,30 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 3: parameter-to-internal-flags - fDebug = !mapMultiArgs["-debug"].empty(); - // Special-case: if -debug=0/-nodebug is set, turn off debugging messages - const std::vector& categories = mapMultiArgs["-debug"]; - if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), std::string("0")) != categories.end()) - fDebug = false; - - // Special case: if debug=zrpcunsafe, implies debug=zrpc, so add it to debug categories - if (find(categories.begin(), categories.end(), std::string("zrpcunsafe")) != categories.end()) { - if (find(categories.begin(), categories.end(), std::string("zrpc")) == categories.end()) { - LogPrintf("%s: parameter interaction: setting -debug=zrpcunsafe -> -debug=zrpc\n", __func__); - std::vector& v = mapMultiArgs["-debug"]; - v.push_back("zrpc"); + if (mapMultiArgs.count("-debug") > 0) { + // Special-case: if -debug=0/-nodebug is set, turn off debugging messages + const std::vector& categories = mapMultiArgs.at("-debug"); + + if (!(GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), std::string("0")) != categories.end())) { + for (const auto& cat : categories) { + uint32_t flag; + if (!GetLogCategory(&flag, &cat)) { + InitWarning(strprintf(_("Unsupported logging category %s.\n"), cat)); + } + logCategories |= flag; + } + } + + // Special case: if debug=zrpcunsafe, implies debug=zrpc, so add it to debug categories + if (!(GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), std::string("zrpcunsafe")) != categories.end())) { + std::string zrpcString = "zrpc"; + if (find(categories.begin(), categories.end(), zrpcString) == categories.end()) { + InitWarning(_("Logging category 'zrpcunsafe' requires 'zrpc'\n")); + uint32_t flag; + if (GetLogCategory(&flag, &zrpcString)) { + logCategories |= flag; + } + } } } @@ -1282,8 +1291,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif - if (GetBoolArg("-shrinkdebugfile", !fDebug)) + if (GetBoolArg("-shrinkdebugfile", logCategories != BCLog::NONE)) { + // Do this first since it both loads a bunch of debug.log into memory, + // and because this needs to happen before any other debug.log printing ShrinkDebugFile(); + } if (fPrintToDebugLog) { if (!OpenDebugLog()) { @@ -1620,7 +1632,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) break; } } catch (const std::exception& e) { - if (fDebug) LogPrintf("%s\n", e.what()); + LogPrintf("%s\n", e.what()); strLoadError = _("Error opening block database"); break; } diff --git a/src/logging.cpp b/src/logging.cpp new file mode 100644 index 000000000..dab1828d3 --- /dev/null +++ b/src/logging.cpp @@ -0,0 +1,277 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include + +#include +#include + +const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; + +bool fPrintToConsole = false; +bool fPrintToDebugLog = true; + +bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; +bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; +bool fLogIPs = DEFAULT_LOGIPS; +std::atomic fReopenDebugLog(false); + +/** Log categories bitfield. */ +std::atomic logCategories(0); +/** + * LogPrintf() has been broken a couple of times now + * by well-meaning people adding mutexes in the most straightforward way. + * It breaks because it may be called by global destructors during shutdown. + * Since the order of destruction of static/global objects is undefined, + * defining a mutex as a global object doesn't work (the mutex gets + * destroyed, and then some later destructor calls OutputDebugStringF, + * maybe indirectly, and you get a core dump at shutdown trying to lock + * the mutex). + */ + +static std::once_flag debugPrintInitFlag; + +/** + * We use std::call_once() to make sure mutexDebugLog and + * vMsgsBeforeOpenLog are initialized in a thread-safe manner. + * + * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog + * are leaked on exit. This is ugly, but will be cleaned up by + * the OS/libc. When the shutdown sequence is fully audited and + * tested, explicit destruction of these objects can be implemented. + */ +static FILE* fileout = nullptr; +static std::mutex* mutexDebugLog = nullptr; +static std::list* vMsgsBeforeOpenLog; + +static int FileWriteStr(const std::string &str, FILE *fp) +{ + return fwrite(str.data(), 1, str.size(), fp); +} + +static void DebugPrintInit() +{ + assert(mutexDebugLog == nullptr); + mutexDebugLog = new std::mutex(); + vMsgsBeforeOpenLog = new std::list; +} + +fs::path GetDebugLogPath() +{ + fs::path logfile(GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + return AbsPathForConfigVal(logfile); +} + +bool OpenDebugLog() +{ + std::call_once(debugPrintInitFlag, &DebugPrintInit); + std::lock_guard scoped_lock(*mutexDebugLog); + + assert(fileout == nullptr); + assert(vMsgsBeforeOpenLog); + fs::path pathDebug = GetDebugLogPath(); + + fileout = fsbridge::fopen(pathDebug, "a"); + if (!fileout) { + return false; + } + + setbuf(fileout, nullptr); // unbuffered + // dump buffered messages from before we opened the log + while (!vMsgsBeforeOpenLog->empty()) { + FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); + vMsgsBeforeOpenLog->pop_front(); + } + + delete vMsgsBeforeOpenLog; + vMsgsBeforeOpenLog = nullptr; + return true; +} + +struct CLogCategoryDesc +{ + uint32_t flag; + std::string category; +}; + +const CLogCategoryDesc LogCategories[] = +{ + {BCLog::NONE, "0"}, + {BCLog::NONE, "none"}, + {BCLog::NET, "net"}, + {BCLog::TOR, "tor"}, + {BCLog::MEMPOOL, "mempool"}, + {BCLog::HTTP, "http"}, + {BCLog::BENCH, "bench"}, + {BCLog::ZMQ, "zmq"}, + {BCLog::DB, "db"}, + {BCLog::RPC, "rpc"}, + {BCLog::ESTIMATEFEE, "estimatefee"}, + {BCLog::ADDRMAN, "addrman"}, + {BCLog::SELECTCOINS, "selectcoins"}, + {BCLog::REINDEX, "reindex"}, + {BCLog::CMPCTBLOCK, "cmpctblock"}, + {BCLog::RAND, "rand"}, + {BCLog::PRUNE, "prune"}, + {BCLog::PROXY, "proxy"}, + {BCLog::MEMPOOLREJ, "mempoolrej"}, + {BCLog::LIBEVENT, "libevent"}, + {BCLog::COINDB, "coindb"}, + {BCLog::QT, "qt"}, + {BCLog::LEVELDB, "leveldb"}, + {BCLog::POW, "pow"}, + {BCLog::ZRPC, "zrpc"}, + {BCLog::ZRPCUNSAFE, "zrpcunsafe"}, + {BCLog::ZPAYMENT, "paymentdisclosure"}, + {BCLog::ALL, "1"}, + {BCLog::ALL, "all"}, +}; + +bool GetLogCategory(uint32_t *f, const std::string *str) +{ + if (f && str) { + if (*str == "") { + *f = BCLog::ALL; + return true; + } + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + if (LogCategories[i].category == *str) { + *f = LogCategories[i].flag; + return true; + } + } + } + return false; +} + +std::string ListLogCategories() +{ + std::string ret; + int outcount = 0; + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + // Omit the special cases. + if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { + if (outcount != 0) ret += ", "; + ret += LogCategories[i].category; + outcount++; + } + } + return ret; +} + +std::vector ListActiveLogCategories() +{ + std::vector ret; + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + // Omit the special cases. + if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { + CLogCategoryActive catActive; + catActive.category = LogCategories[i].category; + catActive.active = LogAcceptCategory(LogCategories[i].flag); + ret.push_back(catActive); + } + } + return ret; +} + +/** + * fStartedNewLine is a state variable held by the calling context that will + * suppress printing of the timestamp when multiple calls are made that don't + * end in a newline. Initialize it to true, and hold it, in the calling context. + */ +static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine) +{ + std::string strStamped; + + if (!fLogTimestamps) + return str; + + if (*fStartedNewLine) { + strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()) + ' ' + str; + } else + strStamped = str; + + if (!str.empty() && str[str.size()-1] == '\n') + *fStartedNewLine = true; + else + *fStartedNewLine = false; + + return strStamped; +} + +int LogPrintStr(const std::string &str) +{ + int ret = 0; // Returns total number of characters written + static std::atomic_bool fStartedNewLine(true); + + std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine); + + if (fPrintToConsole) { + // print to console + ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); + fflush(stdout); + } + if (fPrintToDebugLog) { + std::call_once(debugPrintInitFlag, &DebugPrintInit); + std::lock_guard scoped_lock(*mutexDebugLog); + + // buffer if we haven't opened the log yet + if (fileout == nullptr) { + assert(vMsgsBeforeOpenLog); + ret = strTimestamped.length(); + vMsgsBeforeOpenLog->push_back(strTimestamped); + } + else + { + // reopen the log file, if requested + if (fReopenDebugLog) { + fReopenDebugLog = false; + fs::path pathDebug = GetDebugLogPath(); + if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr) + setbuf(fileout, nullptr); // unbuffered + } + + ret = FileWriteStr(strTimestamped, fileout); + } + } + return ret; +} + +void ShrinkDebugFile() +{ + // Amount of debug.log to save at end when shrinking (must fit in memory) + constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; + // Scroll debug.log if it's getting too big + fs::path pathLog = GetDebugLogPath(); + FILE* file = fsbridge::fopen(pathLog, "r"); + + // Special files (e.g. device nodes) may not have a size. + size_t log_size = 0; + try { + log_size = fs::file_size(pathLog); + } catch (boost::filesystem::filesystem_error &) {} + + // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE + // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes + if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) + { + // Restart the file with some of the end + std::vector vch(RECENT_DEBUG_HISTORY_SIZE, 0); + fseek(file, -((long)vch.size()), SEEK_END); + int nBytes = fread(vch.data(), 1, vch.size(), file); + fclose(file); + + file = fsbridge::fopen(pathLog, "w"); + if (file) + { + fwrite(vch.data(), 1, nBytes, file); + fclose(file); + } + } + else if (file != nullptr) + fclose(file); +} diff --git a/src/logging.h b/src/logging.h new file mode 100644 index 000000000..20ebd86fe --- /dev/null +++ b/src/logging.h @@ -0,0 +1,129 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_LOGGING_H +#define BITCOIN_LOGGING_H + +#include +#include + +#include +#include +#include +#include + +static const bool DEFAULT_LOGTIMEMICROS = false; +static const bool DEFAULT_LOGIPS = false; +static const bool DEFAULT_LOGTIMESTAMPS = true; +extern const char * const DEFAULT_DEBUGLOGFILE; + +extern bool fPrintToConsole; +extern bool fPrintToDebugLog; + +extern bool fLogTimestamps; +extern bool fLogTimeMicros; +extern bool fLogIPs; +extern std::atomic fReopenDebugLog; + +extern std::atomic logCategories; + +struct CLogCategoryActive +{ + std::string category; + bool active; +}; + +namespace BCLog { + enum LogFlags : uint32_t { + NONE = 0, + NET = (1 << 0), + TOR = (1 << 1), + MEMPOOL = (1 << 2), + HTTP = (1 << 3), + BENCH = (1 << 4), + ZMQ = (1 << 5), + DB = (1 << 6), + RPC = (1 << 7), + ESTIMATEFEE = (1 << 8), + ADDRMAN = (1 << 9), + SELECTCOINS = (1 << 10), + REINDEX = (1 << 11), + CMPCTBLOCK = (1 << 12), + RAND = (1 << 13), + PRUNE = (1 << 14), + PROXY = (1 << 15), + MEMPOOLREJ = (1 << 16), + LIBEVENT = (1 << 17), + COINDB = (1 << 18), + QT = (1 << 19), + LEVELDB = (1 << 20), + POW = (1 << 21), + ZRPC = (1 << 22), + ZRPCUNSAFE = (1 << 23), + ZPAYMENT = (1 << 24), + ALL = ~(uint32_t)0, + }; +} +/** Return true if log accepts specified category */ +static inline bool LogAcceptCategory(uint32_t category) +{ + return (logCategories.load(std::memory_order_relaxed) & category) != 0; +} + +/** Returns a string with the log categories. */ +std::string ListLogCategories(); + +/** Returns a vector of the active log categories. */ +std::vector ListActiveLogCategories(); + +/** Return true if str parses as a log category and set the flags in f */ +bool GetLogCategory(uint32_t *f, const std::string *str); + +/** Send a string to the log output */ +int LogPrintStr(const std::string &str); + +/** Get format string from VA_ARGS for error reporting */ +template std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } + +static inline void MarkUsed() {} +template static inline void MarkUsed(const T& t, const Args&... args) +{ + (void)t; + MarkUsed(args...); +} + +// Be conservative when using LogPrintf/error or other things which +// unconditionally log to debug.log! It should not be the case that an inbound +// peer can fill up a user's disk with debug.log entries. + +#ifdef USE_COVERAGE +#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) +#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) +#else +#define LogPrintf(...) do { \ + if (fPrintToConsole || fPrintToDebugLog) { \ + std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ + try { \ + _log_msg_ = tfm::format(__VA_ARGS__); \ + } catch (tinyformat::format_error &fmterr) { \ + /* Original format string will have newline so don't add one here */ \ + _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \ + } \ + LogPrintStr(_log_msg_); \ + } \ +} while(0) + +#define LogPrint(category, ...) do { \ + if (LogAcceptCategory((category))) { \ + LogPrintf(__VA_ARGS__); \ + } \ +} while(0) +#endif + +fs::path GetDebugLogPath(); +bool OpenDebugLog(); +void ShrinkDebugFile(); + +#endif // BITCOIN_LOGGING_H diff --git a/src/main.cpp b/src/main.cpp index 9acceef87..2b1001420 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -646,7 +646,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c unsigned int sz = GetSerializeSize(tx, SER_NETWORK, tx.nVersion); if (sz > 5000) { - LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); + LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; } @@ -655,7 +655,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c for (const CTxIn& txin : tx.vin) mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); - LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), + LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); return true; } @@ -690,7 +690,7 @@ void EraseOrphansFor(NodeId peer) ++nErased; } } - if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); + if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer %d\n", nErased, peer); } @@ -1247,8 +1247,9 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { int expired = pool.Expire(GetTime() - age); - if (expired != 0) - LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + if (expired != 0) { + LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired); + } std::vector vNoSpendsRemaining; pool.TrimToSize(limit, &vNoSpendsRemaining); @@ -1294,7 +1295,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C auto consensusBranchId = CurrentEpochBranchId(nextBlockHeight, Params().GetConsensus()); if (pool.IsRecentlyEvicted(tx.GetHash())) { - LogPrint("mempool", "Dropping txid %s : recently evicted", tx.GetHash().ToString()); + LogPrint(BCLog::MEMPOOL, "Dropping txid %s : recently evicted", tx.GetHash().ToString()); return false; } @@ -1496,7 +1497,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C if (dFreeCount >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000) return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), REJECT_INSUFFICIENTFEE, "rate limited free transaction"); - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + LogPrint(BCLog::MEMPOOL, "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } @@ -2741,7 +2742,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; - LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); + LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); if (block.vtx[0].GetValueOut() > blockReward) @@ -2753,7 +2754,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (!control.Wait()) return state.DoS(100, false); int64_t nTime2 = GetTimeMicros(); nTimeVerify += nTime2 - nTimeStart; - LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs-1), nTimeVerify * 0.000001); + LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs-1), nTimeVerify * 0.000001); if (fJustCheck) return true; @@ -2832,7 +2833,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin view.SetBestBlock(pindex->GetBlockHash()); int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2; - LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); + LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; @@ -2840,7 +2841,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin hashPrevBestCoinBase = block.vtx[0].GetHash(); int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3; - LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); + LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); return true; } @@ -3004,7 +3005,7 @@ bool static DisconnectTip(CValidationState &state, const CChainParams& chainpara return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); assert(view.Flush()); } - LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); uint256 sproutAnchorAfterDisconnect = pcoinsTip->GetBestAnchor(SPROUT); uint256 saplingAnchorAfterDisconnect = pcoinsTip->GetBestAnchor(SAPLING); // Write the chain state to disk, if necessary. @@ -3080,7 +3081,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Apply the block atomically to the chain state. int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; - LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); + LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); bool rv = ConnectBlock(*pblock, state, pindexNew, view, chainparams); @@ -3092,16 +3093,16 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, } mapBlockSource.erase(pindexNew->GetBlockHash()); nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; - LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); + LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); assert(view.Flush()); } int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; - LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); + LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; - LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); + LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); // Remove conflicting transactions from the mempool. std::list txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload(chainparams)); @@ -3126,8 +3127,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, EnforceNodeDeprecation(pindexNew->nHeight); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; - LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); - LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); + LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); + LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); return true; } @@ -4284,7 +4285,7 @@ void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight } } - LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", + LogPrint(BCLog::PRUNE, "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", nPruneTarget/1024/1024, nCurrentUsage/1024/1024, ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, nLastBlockWeCanPrune, count); @@ -4947,7 +4948,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB // detect out of order blocks, and store them for later uint256 hash = block.GetHash(); if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { - LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), + LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), block.hashPrevBlock.ToString()); if (dbp) mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); @@ -4963,7 +4964,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB if (state.IsError()) break; } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { - LogPrint("reindex", "Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); + LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); } // Activate the genesis block so normal node progress can continue @@ -4986,7 +4987,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB while (range.first != range.second) { if (ReadBlockFromDisk(block, range.first->second, chainparams.GetConsensus())) { - LogPrint("reindex", "%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), + LogPrint(BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), head.ToString()); LOCK(cs_main); CValidationState dummy; @@ -5287,7 +5288,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam ) || inv.type == MSG_FILTERED_BLOCK ) && !pfrom->fWhitelisted) { - LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); + LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); //disconnect node pfrom->fDisconnect = true; @@ -5407,7 +5408,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { const CChainParams& chainparams = Params(); - LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); + LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); @@ -5703,7 +5704,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); - LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); + LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); @@ -5726,13 +5727,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // later (within the same cs_main lock, though). MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus()); } - LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); + LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); } } else { if (fBlocksOnly) - LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id); + LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id); else if (!fAlreadyHave && !IsInitialBlockDownload(chainparams)) pfrom->AskFor(inv); } @@ -5762,11 +5763,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return error("message getdata size() = %u", vInv.size()); } - if (fDebug || (vInv.size() != 1)) - LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id); + LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id); - if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) - LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); + if (vInv.size() > 0) { + LogPrint(BCLog::NET, "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); + } pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom, chainparams.GetConsensus()); @@ -5788,12 +5789,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pindex) pindex = chainActive.Next(pindex); int nLimit = 500; - LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id); + LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) { - LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + LogPrint(BCLog::NET, " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; } // If pruning, don't inv blocks unless we have on disk and are likely to still have @@ -5801,7 +5802,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().PoWTargetSpacing(pindex->nHeight); if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave)) { - LogPrint("net", " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); @@ -5809,7 +5810,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { // When this block is requested, we'll send an inv that'll // trigger the peer to getblocks the next batch of inventory. - LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); + LogPrint(BCLog::NET, " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); pfrom->hashContinue = pindex->GetBlockHash(); break; } @@ -5826,7 +5827,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_main); if (IsInitialBlockDownload(chainparams) && !pfrom->fWhitelisted) { - LogPrint("net", "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id); + LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->id); return true; } @@ -5851,7 +5852,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector vHeaders; int nLimit = MAX_HEADERS_RESULTS; - LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id); + LogPrint(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { vHeaders.push_back(pindex->GetBlockHeader()); @@ -5873,7 +5874,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) { - LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id); + LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->id); return true; } @@ -5899,7 +5900,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx, txFeeRate); vWorkQueue.push_back(inv.hash); - LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", + LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", pfrom->id, pfrom->cleanSubVer, tx.GetHash().ToString(), mempool.size(), mempool.DynamicMemoryUsage() / 1000); @@ -5929,7 +5930,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, continue; CFeeRate orphanFeeRate = CFeeRate(0); if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2, &orphanFeeRate)) { - LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); + LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx, orphanFeeRate); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); @@ -5942,11 +5943,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Punish peer that gave us an invalid orphan tx Misbehaving(fromPeer, nDos); setMisbehaving.insert(fromPeer); - LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString()); + LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString()); } // Has inputs but not accepted to mempool // Probably non-standard or insufficient fee/priority - LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); + LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString()); vEraseQueue.push_back(orphanHash); assert(recentRejects); recentRejects->insert(orphanHash); @@ -5969,8 +5970,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // DoS prevention: do not allow mapOrphanTransactions to grow unbounded unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); - if (nEvicted > 0) - LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); + if (nEvicted > 0) { + LogPrint(BCLog::MEMPOOL, "mapOrphan overflow, removed %u tx\n", nEvicted); + } } else { assert(recentRejects); recentRejects->insert(tx.GetHash()); @@ -5997,7 +5999,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS = 0; if (state.IsInvalid(nDoS)) { - LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), + LogPrint(BCLog::MEMPOOL, "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->id, pfrom->cleanSubVer, state.GetRejectReason()); pfrom->PushMessage(NetMsgType::REJECT, strCommand, state.GetRejectCode(), @@ -6058,7 +6060,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. - LogPrint("net", "more getheaders (%d) to send to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); + LogPrint(BCLog::NET, "more getheaders (%d) to send to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()); } @@ -6083,7 +6085,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // the main chain -- this shouldn't really happen. Bail out on the // direct fetch and rely on parallel download instead. if (!chainActive.Contains(pindexWalk)) { - LogPrint("net", "Large reorg, won't direct fetch to %s (%d)\n", + LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n", pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } else { @@ -6096,11 +6098,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex); - LogPrint("net", "Requesting block %s from peer=%d\n", + LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n", pindex->GetBlockHash().ToString(), pfrom->id); } if (vGetData.size() > 1) { - LogPrint("net", "Downloading blocks toward %s (%d) via headers direct fetch\n", + LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n", pindexLast->GetBlockHash().ToString(), pindexLast->nHeight); } if (vGetData.size() > 0) { @@ -6121,7 +6123,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> block; CInv inv(MSG_BLOCK, block.GetHash()); - LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); + LogPrint(BCLog::NET, "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); pfrom->AddInventoryKnown(inv); @@ -6155,7 +6157,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Only send one GetAddr response per connection to reduce resource waste // and discourage addr stamping of INV announcements. if (pfrom->fSentAddr) { - LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id); + LogPrint(BCLog::NET, "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id); return true; } pfrom->fSentAddr = true; @@ -6173,7 +6175,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int currentHeight = GetHeight(); if (CNode::OutboundTargetReached(chainparams.GetConsensus().PoWTargetSpacing(currentHeight), false) && !pfrom->fWhitelisted) { - LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); + LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); pfrom->fDisconnect = true; return true; } @@ -6279,7 +6281,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } if (!(sProblem.empty())) { - LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", + LogPrint(BCLog::NET, "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", pfrom->id, pfrom->cleanSubVer, sProblem, @@ -6368,7 +6370,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == NetMsgType::REJECT) { - if (fDebug) { + if (LogAcceptCategory(BCLog::NET)) { try { string strMsg; unsigned char ccode; string strReason; vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); @@ -6382,10 +6384,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> hash; ss << ": hash " << hash.ToString(); } - LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); + LogPrint(BCLog::NET, "Reject %s\n", SanitizeString(ss.str())); } catch (const std::ios_base::failure&) { // Avoid feedback loops by preventing reject messages from triggering a new reject message. - LogPrint("net", "Unparsable reject message received\n"); + LogPrint(BCLog::NET, "Unparsable reject message received\n"); } } } @@ -6403,13 +6405,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(pfrom->cs_feeFilter); pfrom->minFeeFilter = newFeeFilter; } - LogPrint("net", "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id); + LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->id); } } else { // Ignore unknown commands for extensibility - LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); + LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } return true; @@ -6653,7 +6655,7 @@ bool SendMessages(CNode* pto) got back an empty response. */ if (pindexStart->pprev) pindexStart = pindexStart->pprev; - LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); + LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); pto->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()); } } @@ -6731,7 +6733,7 @@ bool SendMessages(CNode* pto) // This should be very rare and could be optimized out. // Just log for now. if (chainActive[pindex->nHeight] != pindex) { - LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n", + LogPrint(BCLog::NET, "Announcing block %s not on main chain (tip=%s)\n", hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString()); } @@ -6740,18 +6742,18 @@ bool SendMessages(CNode* pto) // setInventoryKnown to track this.) if (!PeerHasHeader(&state, pindex)) { pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce)); - LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__, + LogPrint(BCLog::NET, "%s: sending inv peer=%d hash=%s\n", __func__, pto->id, hashToAnnounce.ToString()); } } } else if (!vHeaders.empty()) { if (vHeaders.size() > 1) { - LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__, + LogPrint(BCLog::NET, "%s: %u headers, range (%s, %s), to peer=%d\n", __func__, vHeaders.size(), vHeaders.front().GetHash().ToString(), vHeaders.back().GetHash().ToString(), pto->id); } else { - LogPrint("net", "%s: sending header %s to peer=%d\n", __func__, + LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->id); } pto->PushMessage(NetMsgType::HEADERS, vHeaders); @@ -6845,13 +6847,13 @@ bool SendMessages(CNode* pto) for (CBlockIndex *pindex : vToDownload) { vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); - LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), + LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->id); } if (state.nBlocksInFlight == 0 && staller != -1) { if (State(staller)->nStallingSince == 0) { State(staller)->nStallingSince = nNow; - LogPrint("net", "Stall started peer=%d\n", staller); + LogPrint(BCLog::NET, "Stall started peer=%d\n", staller); } } } @@ -6864,8 +6866,7 @@ bool SendMessages(CNode* pto) const CInv& inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(inv)) { - if (fDebug) - LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id); + LogPrint(BCLog::NET, "Requesting %s peer=%d\n", inv.ToString(), pto->id); vGetData.push_back(inv); if (vGetData.size() >= 1000) { diff --git a/src/mempool_limit.cpp b/src/mempool_limit.cpp index 31db349a4..5bf1cebf1 100644 --- a/src/mempool_limit.cpp +++ b/src/mempool_limit.cpp @@ -124,10 +124,10 @@ std::optional WeightedTxTree::maybeDropRandom() if (totalTxWeight.cost <= capacity) { return std::nullopt; } - LogPrint("mempool", "Mempool cost limit exceeded (cost=%d, limit=%d)\n", totalTxWeight.cost, capacity); + LogPrint(BCLog::MEMPOOL, "Mempool cost limit exceeded (cost=%d, limit=%d)\n", totalTxWeight.cost, capacity); int randomWeight = GetRand(totalTxWeight.evictionWeight); WeightedTxInfo drop = txIdAndWeights[findByEvictionWeight(0, randomWeight)]; - LogPrint("mempool", "Evicting transaction (txid=%s, cost=%d, evictionWeight=%d)\n", + LogPrint(BCLog::MEMPOOL, "Evicting transaction (txid=%s, cost=%d, evictionWeight=%d)\n", drop.txId.ToString(), drop.txWeight.cost, drop.txWeight.evictionWeight); remove(drop.txId); return drop.txId; diff --git a/src/miner.cpp b/src/miner.cpp index 4ff9c787e..94729fe9e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -188,7 +188,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s if (!mempool.mapTx.count(txin.prevout.hash)) { LogPrintf("ERROR: mempool transaction missing input\n"); - if (fDebug) assert("mempool transaction missing input" == 0); + assert("mempool transaction missing input" == 0); fMissingInputs = true; if (porphan) vOrphan.pop_back(); @@ -548,7 +548,7 @@ void static BitcoinMiner(const CChainParams& chainparams) std::string solver = GetArg("-equihashsolver", "default"); assert(solver == "tromp" || solver == "default"); - LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k); + LogPrint(BCLog::POW, "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k); std::mutex m_cs; bool cancelSolver = false; @@ -633,14 +633,14 @@ void static BitcoinMiner(const CChainParams& chainparams) pblock->nNonce.size()); // (x_1, x_2, ...) = A(I, V, n, k) - LogPrint("pow", "Running Equihash solver \"%s\" with nNonce = %s\n", + LogPrint(BCLog::POW, "Running Equihash solver \"%s\" with nNonce = %s\n", solver, pblock->nNonce.ToString()); std::function)> validBlock = [&pblock, &hashTarget, &chainparams, &m_cs, &cancelSolver, &coinbaseScript] (std::vector soln) { // Write the solution to the hash and compute the result. - LogPrint("pow", "- Checking solution against target\n"); + LogPrint(BCLog::POW, "- Checking solution against target\n"); pblock->nSolution = soln; solutionTargetChecks.increment(); @@ -682,7 +682,7 @@ void static BitcoinMiner(const CChainParams& chainparams) break; } } catch (EhSolverCancelledException&) { - LogPrint("pow", "Equihash solver cancelled\n"); + LogPrint(BCLog::POW, "Equihash solver cancelled\n"); std::lock_guard lock{m_cs}; cancelSolver = false; } diff --git a/src/net.cpp b/src/net.cpp index 08bf2e8fe..31f00217e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -371,7 +371,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure } /// debug print - LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", + LogPrint(BCLog::NET, "trying connection %s lastseen=%.1fhrs\n", pszDest ? pszDest : addrConnect.ToString(), pszDest ? 0.0 : (double)(GetTime() - addrConnect.nTime)/3600.0); @@ -424,7 +424,7 @@ static void DumpBanlist() if (!bandb.Write(banmap)) CNode::SetBannedSetDirty(true); - LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", + LogPrint(BCLog::NET, "Flushed %d banned node ips/subnets to banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); } @@ -434,7 +434,7 @@ void CNode::CloseSocketDisconnect() { LOCK(cs_hSocket); if (hSocket != INVALID_SOCKET) { - LogPrint("net", "disconnecting peer=%d\n", id); + LogPrint(BCLog::NET, "disconnecting peer=%d\n", id); CloseSocket(hSocket); } } @@ -454,9 +454,9 @@ void CNode::PushVersion() CAddress addrMe = GetLocalAddress(&addr); GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); if (fLogIPs) - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); + LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); else - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); + LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, strSubVersion, nBestHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)); } @@ -589,7 +589,7 @@ void CNode::SweepBanned() { setBanned.erase(it++); setBannedIsDirty = true; - LogPrint("net", "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString()); + LogPrint(BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString()); } else ++it; @@ -724,7 +724,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) return false; if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) { - LogPrint("net", "Oversized message from peer=%i, disconnecting\n", GetId()); + LogPrint(BCLog::NET, "Oversized message from peer=%i, disconnecting\n", GetId()); return false; } @@ -1040,7 +1040,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { { if (!AttemptToEvictConnection(whitelisted)) { // No connection to evict, disconnect the new connection - LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n"); + LogPrint(BCLog::NET, "failed to find an eviction candidate - connection dropped (full)\n"); CloseSocket(hSocket); return; } @@ -1059,7 +1059,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { pnode->AddRef(); pnode->fWhitelisted = whitelisted; - LogPrint("net", "connection from %s accepted\n", addr.ToString()); + LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); { LOCK(cs_vNodes); @@ -1294,8 +1294,9 @@ void ThreadSocketHandler() else if (nBytes == 0) { // socket closed gracefully - if (!pnode->fDisconnect) - LogPrint("net", "socket closed\n"); + if (!pnode->fDisconnect) { + LogPrint(BCLog::NET, "socket closed\n"); + } pnode->CloseSocketDisconnect(); } else if (nBytes < 0) @@ -1330,7 +1331,7 @@ void ThreadSocketHandler() { if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) { - LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id); + LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id); pnode->fDisconnect = true; } else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) @@ -1441,7 +1442,7 @@ void DumpAddresses() CAddrDB adb; adb.Write(addrman); - LogPrint("net", "Flushed %d addresses to peers.dat %dms\n", + LogPrint(BCLog::NET, "Flushed %d addresses to peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); } @@ -1912,7 +1913,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) CNode::SetBannedSetDirty(false); // no need to write down, just read data CNode::SweepBanned(); // sweep out unused entries - LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", + LogPrint(BCLog::NET, "Loaded %d banned node ips/subnets from banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); } else { LogPrintf("Invalid or missing banlist.dat; recreating\n"); @@ -2267,10 +2268,11 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa id = nLastNodeId++; } - if (fLogIPs) - LogPrint("net", "Added connection to %s peer=%d\n", addrName, id); - else - LogPrint("net", "Added connection peer=%d\n", id); + if (fLogIPs) { + LogPrint(BCLog::NET, "Added connection to %s peer=%d\n", addrName, id); + } else { + LogPrint(BCLog::NET, "Added connection peer=%d\n", id); + } // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) @@ -2305,7 +2307,7 @@ void CNode::AskFor(const CInv& inv) nRequestTime = it->second; else nRequestTime = 0; - LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id); + LogPrint(BCLog::NET, "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id); // Make sure not to reuse time indexes to keep things in the same order int64_t nNow = GetTimeMicros() - 1000000; @@ -2328,7 +2330,7 @@ void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSen ENTER_CRITICAL_SECTION(cs_vSend); assert(ssSend.size() == 0); ssSend << CMessageHeader(Params().MessageStart(), pszCommand, 0); - LogPrint("net", "sending: %s ", SanitizeString(pszCommand)); + LogPrint(BCLog::NET, "sending: %s ", SanitizeString(pszCommand)); } void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) @@ -2337,7 +2339,7 @@ void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) LEAVE_CRITICAL_SECTION(cs_vSend); - LogPrint("net", "(aborted)\n"); + LogPrint(BCLog::NET, "(aborted)\n"); } void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) @@ -2347,7 +2349,7 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) // not intended for end-users. if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) { - LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); + LogPrint(BCLog::NET, "dropmessages DROPPING SEND MESSAGE\n"); AbortMessage(); return; } @@ -2368,7 +2370,7 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + CMessageHeader::CHECKSUM_SIZE); memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], hash.begin(), CMessageHeader::CHECKSUM_SIZE); - LogPrint("net", "(%d bytes) peer=%d\n", nSize, id); + LogPrint(BCLog::NET, "(%d bytes) peer=%d\n", nSize, id); std::deque::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData()); ssSend.GetAndClear(*it); diff --git a/src/netbase.cpp b/src/netbase.cpp index 054871b16..de143199c 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -337,7 +337,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Error sending authentication to proxy"); } - LogPrint("proxy", "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); + LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); uint8_t pchRetA[2]; if (!InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) { CloseSocket(hSocket); @@ -470,7 +470,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout); if (nRet == 0) { - LogPrint("net", "connection to %s timeout\n", addrConnect.ToString()); + LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString()); CloseSocket(hSocket); return false; } diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index d7f5501f4..783effa8e 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -174,7 +174,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, } } - LogPrint("estimatefee", "%3d: For conf success %s %4.2f need %s %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n", + LogPrint(BCLog::ESTIMATEFEE, "%3d: For conf success %s %4.2f need %s %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n", confTarget, requireGreater ? ">" : "<", successBreakPoint, dataTypeString, requireGreater ? ">" : "<", median, buckets[minBucket], buckets[maxBucket], 100 * nConf / (totalNum + extraNum), nConf, totalNum, extraNum); @@ -250,7 +250,7 @@ void TxConfirmStats::Read(CAutoFile& filein) for (unsigned int i = 0; i < buckets.size(); i++) bucketMap[buckets[i]] = i; - LogPrint("estimatefee", "Reading estimates: %u %s buckets counting confirms up to %u blocks\n", + LogPrint(BCLog::ESTIMATEFEE, "Reading estimates: %u %s buckets counting confirms up to %u blocks\n", numBuckets, dataTypeString, maxConfirms); } @@ -259,7 +259,7 @@ unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) unsigned int bucketindex = FindBucketIndex(val); unsigned int blockIndex = nBlockHeight % unconfTxs.size(); unconfTxs[blockIndex][bucketindex]++; - LogPrint("estimatefee", "adding to %s", dataTypeString); + LogPrint(BCLog::ESTIMATEFEE, "adding to %s", dataTypeString); return bucketindex; } @@ -270,24 +270,26 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe if (nBestSeenHeight == 0) // the BlockPolicyEstimator hasn't seen any blocks yet blocksAgo = 0; if (blocksAgo < 0) { - LogPrint("estimatefee", "Blockpolicy error, blocks ago is negative for mempool tx\n"); + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, blocks ago is negative for mempool tx\n"); return; //This can't happen because we call this with our best seen height, no entries can have higher } if (blocksAgo >= (int)unconfTxs.size()) { - if (oldUnconfTxs[bucketindex] > 0) + if (oldUnconfTxs[bucketindex] > 0) { oldUnconfTxs[bucketindex]--; - else - LogPrint("estimatefee", "Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n", + } else { + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n", bucketindex); + } } else { unsigned int blockIndex = entryHeight % unconfTxs.size(); - if (unconfTxs[blockIndex][bucketindex] > 0) + if (unconfTxs[blockIndex][bucketindex] > 0) { unconfTxs[blockIndex][bucketindex]--; - else - LogPrint("estimatefee", "Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n", + } else { + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n", blockIndex, bucketindex); + } } } @@ -295,7 +297,7 @@ void CBlockPolicyEstimator::removeTx(uint256 hash) { std::map::iterator pos = mapMemPoolTxs.find(hash); if (pos == mapMemPoolTxs.end()) { - LogPrint("estimatefee", "Blockpolicy error mempool tx %s not found for removeTx\n", + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s not found for removeTx\n", hash.ToString().c_str()); return; } @@ -354,7 +356,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo unsigned int txHeight = entry.GetHeight(); uint256 hash = entry.GetTx().GetHash(); if (mapMemPoolTxs[hash].stats != NULL) { - LogPrint("estimatefee", "Blockpolicy error mempool tx %s already being tracked\n", + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n", hash.ToString().c_str()); return; } @@ -386,7 +388,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo double curPri = entry.GetPriority(txHeight); mapMemPoolTxs[hash].blockHeight = txHeight; - LogPrint("estimatefee", "Blockpolicy mempool tx %s ", hash.ToString().substr(0,10)); + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy mempool tx %s ", hash.ToString().substr(0,10)); // Record this as a priority estimate if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { mapMemPoolTxs[hash].stats = &priStats; @@ -398,9 +400,9 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); } else { - LogPrint("estimatefee", "not adding"); + LogPrint(BCLog::ESTIMATEFEE, "not adding"); } - LogPrint("estimatefee", "\n"); + LogPrint(BCLog::ESTIMATEFEE, "\n"); } void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry) @@ -419,7 +421,7 @@ void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM if (blocksToConfirm <= 0) { // This can't happen because we don't process transactions from a block with a height // lower than our greatest seen height - LogPrint("estimatefee", "Blockpolicy error Transaction had negative blocksToConfirm\n"); + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error Transaction had negative blocksToConfirm\n"); return; } @@ -461,7 +463,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, // Update the dynamic cutoffs // a fee/priority is "likely" the reason your tx was included in a block if >85% of such tx's // were confirmed in 2 blocks and is "unlikely" if <50% were confirmed in 10 blocks - LogPrint("estimatefee", "Blockpolicy recalculating dynamic cutoffs:\n"); + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy recalculating dynamic cutoffs:\n"); priLikely = priStats.EstimateMedianVal(2, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBlockHeight); if (priLikely == -1) priLikely = INF_PRIORITY; @@ -494,7 +496,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, feeStats.UpdateMovingAverages(); priStats.UpdateMovingAverages(); - LogPrint("estimatefee", "Blockpolicy after updating estimates for %u confirmed entries, new mempool map size %u\n", + LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy after updating estimates for %u confirmed entries, new mempool map size %u\n", entries.size(), mapMemPoolTxs.size()); } diff --git a/src/pow.cpp b/src/pow.cpp index 18c950e0d..3ef89e8a6 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -28,7 +28,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead // Reset the difficulty after the algo fork if (pindexLast->nHeight > chainParams.eh_epoch_1_end() - 1 && pindexLast->nHeight < chainParams.eh_epoch_1_end() + params.nPowAveragingWindow) { - LogPrint("pow", "Reset the difficulty for the eh_epoch_2 algo change: %d\n", nProofOfWorkLimit); + LogPrint(BCLog::POW, "Reset the difficulty for the eh_epoch_2 algo change: %d\n", nProofOfWorkLimit); return nProofOfWorkLimit; } @@ -115,7 +115,7 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params& default: return error("CheckEquihashSolution: Unsupported solution size of %d", nSolSize); } - LogPrint("pow", "selected n,k: %d,%d \n", n, k); + LogPrint(BCLog::POW, "selected n,k: %d,%d \n", n, k); // Hash state crypto_generichash_blake2b_state state; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 80f6677f6..fdb2cfd19 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -300,7 +300,7 @@ bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd) bool StartRPC() { - LogPrint("rpc", "Starting RPC\n"); + LogPrint(BCLog::RPC, "Starting RPC\n"); fRPCRunning = true; g_rpcSignals.Started(); @@ -323,14 +323,14 @@ bool StartRPC() void InterruptRPC() { - LogPrint("rpc", "Interrupting RPC\n"); + LogPrint(BCLog::RPC, "Interrupting RPC\n"); // Interrupt e.g. running longpolls fRPCRunning = false; } void StopRPC() { - LogPrint("rpc", "Stopping RPC\n"); + LogPrint(BCLog::RPC, "Stopping RPC\n"); deadlineTimers.clear(); g_rpcSignals.Stopped(); @@ -382,7 +382,7 @@ void JSONRequest::parse(const UniValue& valRequest) if (!valMethod.isStr()) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); - LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); + LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); // Parse params UniValue valParams = request.find_value("params"); @@ -513,7 +513,7 @@ void RPCRunLater(const std::string& name, std::function func, int64_ throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); deadlineTimers.erase(name); RPCTimerInterface* timerInterface = timerInterfaces[0]; - LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); + LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); deadlineTimers.emplace(name, std::unique_ptr(timerInterface->NewTimer(func, nSeconds*1000))); } diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index ccd5efcbf..318e766f2 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // Convert solution indices to byte array (decompress) and pass it to validBlock method. std::set> solns; for (size_t s = 0; s < eq.nsols; s++) { - LogPrint("pow", "Checking solution %d\n", s+1); + LogPrint(BCLog::POW, "Checking solution %d\n", s+1); std::vector index_vector(PROOFSIZE); for (size_t i = 0; i < PROOFSIZE; i++) { index_vector[i] = eq.sols[s][i]; diff --git a/src/timedata.cpp b/src/timedata.cpp index b05b9fd66..5fad0f7d7 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -36,7 +36,7 @@ int64_t CTimeWarning::AddTimeData(const CNetAddr& ip, int64_t nTime, int64_t now return nTimeOffset; } - LogPrint("net","added time data, samples %d, offset %+d (%+d minutes)\n", setKnown.size(), nTimeOffset, nTimeOffset/60); + LogPrint(BCLog::NET,"added time data, samples %d, offset %+d (%+d minutes)\n", setKnown.size(), nTimeOffset, nTimeOffset/60); if (nPeersBehind + nPeersAhead < TIMEDATA_WARNING_SAMPLES) { if (nTimeOffset < -TIMEDATA_WARNING_THRESHOLD) { diff --git a/src/tinyformat.h b/src/tinyformat.h index 57f767250..5022d4680 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -123,11 +123,11 @@ namespace tinyformat {} namespace tfm = tinyformat; // Error handling; calls assert() by default. -#define TINYFORMAT_ERROR(reasonString) throw std::runtime_error(reasonString) +#define TINYFORMAT_ERROR(reasonString) throw tinyformat::format_error(reasonString) // Define for C++11 variadic templates which make the code shorter & more // general. If you don't define this, C++11 support is autodetected below. -// #define TINYFORMAT_USE_VARIADIC_TEMPLATES +#define TINYFORMAT_USE_VARIADIC_TEMPLATES //------------------------------------------------------------------------------ @@ -155,7 +155,7 @@ namespace tfm = tinyformat; #endif #ifdef __APPLE__ -// Workaround macOS linker warning: Xcode uses different default symbol +// Workaround OSX linker warning: xcode uses different default symbol // visibilities for static libs vs executables (see issue #25) # define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) #else @@ -164,6 +164,13 @@ namespace tfm = tinyformat; namespace tinyformat { +class format_error: public std::runtime_error +{ +public: + format_error(const std::string &what): std::runtime_error(what) { + } +}; + //------------------------------------------------------------------------------ namespace detail { @@ -577,7 +584,7 @@ inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) // Formatting options which can't be natively represented using the ostream // state are returned in spacePadPositive (for space padded positive numbers) // and ntrunc (for truncating conversions). argIndex is incremented if -// necessary to pull out variable width and precision. The function returns a +// necessary to pull out variable width and precision . The function returns a // pointer to the character after the end of the current format spec. inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive, int& ntrunc, const char* fmtStart, diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 08378e35a..8d8b04dea 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -163,7 +163,7 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) self->reply_handlers.front()(*self, self->message); self->reply_handlers.pop_front(); } else { - LogPrint("tor", "tor: Received unexpected sync reply %i\n", self->message.code); + LogPrint(BCLog::TOR, "tor: Received unexpected sync reply %i\n", self->message.code); } } self->message.Clear(); @@ -182,13 +182,14 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct { TorControlConnection *self = (TorControlConnection*)ctx; if (what & BEV_EVENT_CONNECTED) { - LogPrint("tor", "tor: Successfully connected!\n"); + LogPrint(BCLog::TOR, "tor: Successfully connected!\n"); self->connected(*self); } else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { - if (what & BEV_EVENT_ERROR) - LogPrint("tor", "tor: Error connecting to Tor control socket\n"); - else - LogPrint("tor", "tor: End of stream\n"); + if (what & BEV_EVENT_ERROR) { + LogPrint(BCLog::TOR, "tor: Error connecting to Tor control socket\n"); + } else { + LogPrint(BCLog::TOR, "tor: End of stream\n"); + } self->Disconnect(); self->disconnected(*self); } @@ -468,7 +469,7 @@ TorController::TorController(struct event_base* _base, const std::string& _targe // Read service private key if cached std::pair pkf = ReadBinaryFile(GetPrivateKeyFile()); if (pkf.first) { - LogPrint("tor", "tor: Reading cached private key from %s\n", GetPrivateKeyFile().string()); + LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", GetPrivateKeyFile().string()); private_key = pkf.second; } } @@ -487,7 +488,7 @@ TorController::~TorController() void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { - LogPrint("tor", "tor: ADD_ONION successful\n"); + LogPrint(BCLog::TOR, "tor: ADD_ONION successful\n"); for (const std::string &s : reply.lines) { std::map m = ParseTorReplyMapping(s); std::map::iterator i; @@ -507,7 +508,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe service = CService(service_id+".onion", GetListenPort()); LogPrintf("tor: Got service ID %s, advertizing service %s\n", service_id, service.ToString()); if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { - LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile().string()); + LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile().string()); } else { LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile().string()); } @@ -523,7 +524,7 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { - LogPrint("tor", "tor: Authentication successful\n"); + LogPrint(BCLog::TOR, "tor: Authentication successful\n"); // Now that we know Tor is running setup the proxy for onion addresses // if -onion isn't set to something else. @@ -576,7 +577,7 @@ static std::vector ComputeResponse(const std::string &key, const std::v void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { - LogPrint("tor", "tor: SAFECOOKIE authentication challenge successful\n"); + LogPrint(BCLog::TOR, "tor: SAFECOOKIE authentication challenge successful\n"); std::pair l = SplitTorReplyLine(reply.lines[0]); if (l.first == "AUTHCHALLENGE") { std::map m = ParseTorReplyMapping(l.second); @@ -586,7 +587,7 @@ void TorController::authchallenge_cb(TorControlConnection& _conn, const TorContr } std::vector serverHash = ParseHex(m["SERVERHASH"]); std::vector serverNonce = ParseHex(m["SERVERNONCE"]); - LogPrint("tor", "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce)); + LogPrint(BCLog::TOR, "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce)); if (serverNonce.size() != 32) { LogPrintf("tor: ServerNonce is not 32 bytes, as required by spec\n"); return; @@ -631,12 +632,12 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro std::map m = ParseTorReplyMapping(l.second); std::map::iterator i; if ((i = m.find("Tor")) != m.end()) { - LogPrint("tor", "tor: Connected to Tor version %s\n", i->second); + LogPrint(BCLog::TOR, "tor: Connected to Tor version %s\n", i->second); } } } for (const std::string &s : methods) { - LogPrint("tor", "tor: Supported authentication method: %s\n", s); + LogPrint(BCLog::TOR, "tor: Supported authentication method: %s\n", s); } // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD /* Authentication: @@ -646,18 +647,18 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro std::string torpassword = GetArg("-torpassword", ""); if (!torpassword.empty()) { if (methods.count("HASHEDPASSWORD")) { - LogPrint("tor", "tor: Using HASHEDPASSWORD authentication\n"); + LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n"); boost::replace_all(torpassword, "\"", "\\\""); _conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); } else { LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n"); } } else if (methods.count("NULL")) { - LogPrint("tor", "tor: Using NULL authentication\n"); + LogPrint(BCLog::TOR, "tor: Using NULL authentication\n"); _conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2)); } else if (methods.count("SAFECOOKIE")) { // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie - LogPrint("tor", "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); + LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); std::pair status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2)); @@ -699,7 +700,7 @@ void TorController::disconnected_cb(TorControlConnection& _conn) if (!reconnect) return; - LogPrint("tor", "tor: Not connected to Tor control port %s, trying to reconnect\n", target); + LogPrint(BCLog::TOR, "tor: Not connected to Tor control port %s, trying to reconnect\n", target); // Single-shot timer for reconnect. Use exponential backoff. struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0)); diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp index a68fdc97f..3c2dc4699 100644 --- a/src/transaction_builder.cpp +++ b/src/transaction_builder.cpp @@ -568,7 +568,7 @@ void TransactionBuilder::CreateJSDescriptions() jsInputValue += plaintext.value(); - LogPrint("zrpcunsafe", "spending change (amount=%s)\n", FormatMoney(plaintext.value())); + LogPrint(BCLog::ZRPCUNSAFE, "spending change (amount=%s)\n", FormatMoney(plaintext.value())); } catch (const std::exception& e) { throw JSDescException("Error decrypting output note of previous JoinSplit"); @@ -648,7 +648,7 @@ void TransactionBuilder::CreateJSDescriptions() if (jsChange > 0) { vjsout[1] = libzcash::JSOutput(changeAddress, jsChange); - LogPrint("zrpcunsafe", "generating note for change (amount=%s)\n", FormatMoney(jsChange)); + LogPrint(BCLog::ZRPCUNSAFE, "generating note for change (amount=%s)\n", FormatMoney(jsChange)); } std::array inputMap; @@ -675,7 +675,7 @@ void TransactionBuilder::CreateJSDescription( std::array& inputMap, std::array& outputMap) { - LogPrint("zrpcunsafe", "CreateJSDescription: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "CreateJSDescription: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", mtx.vJoinSplit.size(), FormatMoney(vpub_old), FormatMoney(vpub_new), FormatMoney(vjsin[0].note.value()), FormatMoney(vjsin[1].note.value()), diff --git a/src/txdb.cpp b/src/txdb.cpp index b03426b52..40e28c59b 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -195,7 +195,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, if (!hashSaplingAnchor.IsNull()) batch.Write(DB_BEST_SAPLING_ANCHOR, hashSaplingAnchor); - LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); + LogPrint(BCLog::COINDB, "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); return db.WriteBatch(batch); } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8b3c7d813..f0e39c77c 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -746,7 +746,7 @@ std::vector CTxMemPool::removeExpired(unsigned int nBlockHeight) list removed; remove(tx, removed, true); ids.push_back(tx.GetHash()); - LogPrint("mempool", "Removing expired txid: %s\n", tx.GetHash().ToString()); + LogPrint(BCLog::MEMPOOL, "Removing expired txid: %s\n", tx.GetHash().ToString()); } return ids; } @@ -824,7 +824,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const if (GetRand(std::numeric_limits::max()) >= nCheckFrequency) return; - LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); + LogPrint(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); uint64_t checkTotal = 0; uint64_t innerUsage = 0; @@ -1198,7 +1198,7 @@ size_t CTxMemPool::DynamicMemoryUsage() const { void CTxMemPool::SetMempoolCostLimit(int64_t totalCostLimit, int64_t evictionMemorySeconds) { LOCK(cs); - LogPrint("mempool", "Setting mempool cost limit: (limit=%d, time=%d)\n", totalCostLimit, evictionMemorySeconds); + LogPrint(BCLog::MEMPOOL, "Setting mempool cost limit: (limit=%d, time=%d)\n", totalCostLimit, evictionMemorySeconds); delete recentlyEvicted; delete weightedTxTree; recentlyEvicted = new RecentlyEvictedList(evictionMemorySeconds); @@ -1364,6 +1364,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRe } } - if (maxFeeRateRemoved > CFeeRate(0)) - LogPrint("mempool", "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString()); + if (maxFeeRateRemoved > CFeeRate(0)) { + LogPrint(BCLog::MEMPOOL, "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString()); + } } diff --git a/src/util.cpp b/src/util.cpp index eeb7bc65a..022c0936a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -74,48 +74,14 @@ using namespace std; const char * const BITCOIN_CONF_FILENAME = "bitcoinz.conf"; const char * const BITCOIN_PID_FILENAME = "bitcoinzd.pid"; -const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; CCriticalSection cs_args; map mapArgs; map > mapMultiArgs; -bool fDebug = false; -bool fPrintToConsole = false; -bool fPrintToDebugLog = true; + bool fDaemon = false; bool fServer = false; -bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; -bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; -bool fLogIPs = DEFAULT_LOGIPS; -std::atomic fReopenDebugLog(false); - -/** - * LogPrintf() has been broken a couple of times now - * by well-meaning people adding mutexes in the most straightforward way. - * It breaks because it may be called by global destructors during shutdown. - * Since the order of destruction of static/global objects is undefined, - * defining a mutex as a global object doesn't work (the mutex gets - * destroyed, and then some later destructor calls OutputDebugStringF, - * maybe indirectly, and you get a core dump at shutdown trying to lock - * the mutex). - */ - -static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT; - -/** - * We use boost::call_once() to make sure mutexDebugLog and - * vMsgsBeforeOpenLog are initialized in a thread-safe manner. - * - * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog - * are leaked on exit. This is ugly, but will be cleaned up by - * the OS/libc. When the shutdown sequence is fully audited and - * tested, explicit destruction of these objects can be implemented. - */ -static FILE* fileout = NULL; -static boost::mutex* mutexDebugLog = NULL; -static list *vMsgsBeforeOpenLog; - [[noreturn]] void new_handler_terminate() { // Rather than throwing std::bad-alloc if allocation fails, terminate @@ -130,143 +96,6 @@ static list *vMsgsBeforeOpenLog; std::terminate(); }; -static int FileWriteStr(const std::string &str, FILE *fp) -{ - return fwrite(str.data(), 1, str.size(), fp); -} - -static void DebugPrintInit() -{ - assert(mutexDebugLog == NULL); - mutexDebugLog = new boost::mutex(); - vMsgsBeforeOpenLog = new list; -} - -fs::path GetDebugLogPath() -{ - fs::path logfile(GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); - return AbsPathForConfigVal(logfile); -} - -bool OpenDebugLog() -{ - boost::call_once(&DebugPrintInit, debugPrintInitFlag); - boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); - - assert(fileout == NULL); - assert(vMsgsBeforeOpenLog); - fs::path pathDebug = GetDebugLogPath(); - - fileout = fsbridge::fopen(pathDebug, "a"); - if (!fileout) { - return false; - } - - setbuf(fileout, nullptr); // unbuffered - // dump buffered messages from before we opened the log - while (!vMsgsBeforeOpenLog->empty()) { - FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); - vMsgsBeforeOpenLog->pop_front(); - } - - delete vMsgsBeforeOpenLog; - vMsgsBeforeOpenLog = NULL; - return true; -} - -bool LogAcceptCategory(const char* category) -{ - if (category != NULL) - { - if (!fDebug) - return false; - - // Give each thread quick access to -debug settings. - // This helps prevent issues debugging global destructors, - // where mapMultiArgs might be deleted before another - // global destructor calls LogPrint() - static boost::thread_specific_ptr > ptrCategory; - if (ptrCategory.get() == NULL) - { - const vector& categories = mapMultiArgs["-debug"]; - ptrCategory.reset(new set(categories.begin(), categories.end())); - // thread_specific_ptr automatically deletes the set when the thread ends. - } - const set& setCategories = *ptrCategory.get(); - - // if not debugging everything and not debugging specific category, LogPrint does nothing. - if (setCategories.count(string("")) == 0 && - setCategories.count(string("1")) == 0 && - setCategories.count(string(category)) == 0) - return false; - } - return true; -} - -/** - * fStartedNewLine is a state variable held by the calling context that will - * suppress printing of the timestamp when multiple calls are made that don't - * end in a newline. Initialize it to true, and hold it, in the calling context. - */ -static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine) -{ - string strStamped; - - if (!fLogTimestamps) - return str; - - if (*fStartedNewLine) - strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()) + ' ' + str; - else - strStamped = str; - - if (!str.empty() && str[str.size()-1] == '\n') - *fStartedNewLine = true; - else - *fStartedNewLine = false; - - return strStamped; -} - -int LogPrintStr(const std::string &str) -{ - int ret = 0; // Returns total number of characters written - static bool fStartedNewLine = true; - if (fPrintToConsole) - { - // print to console - ret = fwrite(str.data(), 1, str.size(), stdout); - fflush(stdout); - } - else if (fPrintToDebugLog) - { - boost::call_once(&DebugPrintInit, debugPrintInitFlag); - boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); - - string strTimestamped = LogTimestampStr(str, &fStartedNewLine); - - // buffer if we haven't opened the log yet - if (fileout == NULL) { - assert(vMsgsBeforeOpenLog); - ret = strTimestamped.length(); - vMsgsBeforeOpenLog->push_back(strTimestamped); - } - else - { - // reopen the log file, if requested - if (fReopenDebugLog) { - fReopenDebugLog = false; - fs::path pathDebug = GetDebugLogPath(); - if (fsbridge::freopen(pathDebug,"a",fileout) != NULL) - setbuf(fileout, NULL); // unbuffered - } - - ret = FileWriteStr(strTimestamped, fileout); - } - } - return ret; -} - /** Interpret string as boolean, for argument parsing */ static bool InterpretBool(const std::string& strValue) { @@ -735,30 +564,6 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { #endif } -void ShrinkDebugFile() -{ - // Scroll debug.log if it's getting too big - fs::path pathLog = GetDebugLogPath(); - FILE* file = fsbridge::fopen(pathLog, "r"); - if (file && fs::file_size(pathLog) > 10 * 1000000) - { - // Restart the file with some of the end - std::vector vch(200000,0); - fseek(file, -((long)vch.size()), SEEK_END); - int nBytes = fread(begin_ptr(vch), 1, vch.size(), file); - fclose(file); - - file = fopen(pathLog.string().c_str(), "w"); - if (file) - { - fwrite(begin_ptr(vch), 1, nBytes, file); - fclose(file); - } - } - else if (file != NULL) - fclose(file); -} - #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { diff --git a/src/util.h b/src/util.h index 683c2e02f..37bc9b4df 100644 --- a/src/util.h +++ b/src/util.h @@ -5,7 +5,7 @@ /** * Server/client environment: argument handling, config file parsing, - * logging, thread wrappers + * thread wrappers, startup time */ #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H @@ -16,6 +16,7 @@ #include "compat.h" #include "fs.h" +#include #include "tinyformat.h" #include "utiltime.h" @@ -29,21 +30,10 @@ #include -static const bool DEFAULT_LOGTIMEMICROS = false; -static const bool DEFAULT_LOGIPS = false; -static const bool DEFAULT_LOGTIMESTAMPS = true; -extern const char * const DEFAULT_DEBUGLOGFILE; - extern std::map mapArgs; extern std::map > mapMultiArgs; -extern bool fDebug; -extern bool fPrintToConsole; -extern bool fPrintToDebugLog; -extern bool fServer; -extern bool fLogTimestamps; -extern bool fLogIPs; -extern std::atomic fReopenDebugLog; +extern bool fServer; [[noreturn]] extern void new_handler_terminate(); @@ -65,47 +55,10 @@ inline std::string _(const char* psz) void SetupEnvironment(); bool SetupNetworking(); -/** Return true if log accepts specified category */ -bool LogAcceptCategory(const char* category); -/** Send a string to the log output */ -int LogPrintStr(const std::string &str); - -#define LogPrintf(...) LogPrint(NULL, __VA_ARGS__) - -/** - * When we switch to C++11, this can be switched to variadic templates instead - * of this macro-based construction (see tinyformat.h). - */ -#define MAKE_ERROR_AND_LOG_FUNC(n) \ - /** Print to debug.log if -debug=category switch is given OR category is NULL. */ \ - template \ - static inline int LogPrint(const char* category, const char* format, TINYFORMAT_VARARGS(n)) \ - { \ - if(!LogAcceptCategory(category)) return 0; \ - return LogPrintStr(tfm::format(format, TINYFORMAT_PASSARGS(n))); \ - } \ - /** Log error and return false */ \ - template \ - static inline bool error(const char* format, TINYFORMAT_VARARGS(n)) \ - { \ - LogPrintStr("ERROR: " + tfm::format(format, TINYFORMAT_PASSARGS(n)) + "\n"); \ - return false; \ - } - -TINYFORMAT_FOREACH_ARGNUM(MAKE_ERROR_AND_LOG_FUNC) - -/** - * Zero-arg versions of logging and error, these are not covered by - * TINYFORMAT_FOREACH_ARGNUM - */ -static inline int LogPrint(const char* category, const char* format) -{ - if(!LogAcceptCategory(category)) return 0; - return LogPrintStr(format); -} -static inline bool error(const char* format) +template +bool error(const char* fmt, const Args&... args) { - LogPrintStr(std::string("ERROR: ") + format + "\n"); + LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n"); return false; } @@ -135,9 +88,6 @@ void ReadConfigFile(const std::string& confPath, std::mapPut(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); } } } @@ -254,16 +254,16 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() tx_ = CTransaction(rawTx); } - LogPrint(isPureTaddrOnlyTx ? "zrpc" : "zrpcunsafe", "%s: spending %s to send %s with fee %s\n", + LogPrint(isPureTaddrOnlyTx ? BCLog::ZRPC : BCLog::ZRPCUNSAFE, "%s: spending %s to send %s with fee %s\n", getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); - LogPrint("zrpc", "%s: transparent input: %s\n", getId(), FormatMoney(t_inputs_total)); - LogPrint("zrpcunsafe", "%s: private input: %s\n", getId(), FormatMoney(z_inputs_total)); + LogPrint(BCLog::ZRPC, "%s: transparent input: %s\n", getId(), FormatMoney(t_inputs_total)); + LogPrint(BCLog::ZRPCUNSAFE, "%s: private input: %s\n", getId(), FormatMoney(z_inputs_total)); if (isToTaddr_) { - LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(sendAmount)); + LogPrint(BCLog::ZRPC, "%s: transparent output: %s\n", getId(), FormatMoney(sendAmount)); } else { - LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(sendAmount)); + LogPrint(BCLog::ZRPCUNSAFE, "%s: private output: %s\n", getId(), FormatMoney(sendAmount)); } - LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); + LogPrint(BCLog::ZRPC, "%s: fee: %s\n", getId(), FormatMoney(minersFee)); // Grab the current consensus branch ID { @@ -570,7 +570,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() jsInputValue += plaintext.value(); - LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: spending change (amount=%s)\n", getId(), FormatMoney(plaintext.value())); @@ -623,7 +623,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; wtxDepth = wtx.GetDepthInMainChain(); } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, height=%d, confirmations=%d)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: spending note (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, height=%d, confirmations=%d)\n", getId(), jso.hash.ToString().substr(0, 10), jso.js, @@ -700,7 +700,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl() } info.vjsout.push_back(jso); - LogPrint("zrpcunsafe", "%s: generating note for %s (amount=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: generating note for %s (amount=%s)\n", getId(), outputType, FormatMoney(jsChange)); @@ -790,7 +790,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( CMutableTransaction mtx(tx_); - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", getId(), tx_.vJoinSplit.size(), FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), @@ -898,7 +898,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit( PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); } // !!! Payment disclosure END diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index f219f43c2..74d3f5ab4 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -70,11 +70,11 @@ void AsyncRPCOperation_saplingmigration::main() { } bool AsyncRPCOperation_saplingmigration::main_impl() { - LogPrint("zrpcunsafe", "%s: Beginning AsyncRPCOperation_saplingmigration.\n", getId()); + LogPrint(BCLog::ZRPCUNSAFE, "%s: Beginning AsyncRPCOperation_saplingmigration.\n", getId()); auto consensusParams = Params().GetConsensus(); auto nextActivationHeight = NextActivationHeight(targetHeight_, consensusParams); if (nextActivationHeight && targetHeight_ + MIGRATION_EXPIRY_DELTA >= nextActivationHeight.value()) { - LogPrint("zrpcunsafe", "%s: Migration txs would be created before a NU activation but may expire after. Skipping this round.\n", getId()); + LogPrint(BCLog::ZRPCUNSAFE, "%s: Migration txs would be created before a NU activation but may expire after. Skipping this round.\n", getId()); setMigrationResult(0, 0, std::vector()); return true; } @@ -94,7 +94,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { } // If the remaining amount to be migrated is less than 0.01 ZEC, end the migration. if (availableFunds < CENT) { - LogPrint("zrpcunsafe", "%s: Available Sprout balance (%s) less than required minimum (%s). Stopping.\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: Available Sprout balance (%s) less than required minimum (%s). Stopping.\n", getId(), FormatMoney(availableFunds), FormatMoney(CENT)); setMigrationResult(0, 0, std::vector()); return true; @@ -114,7 +114,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { CAmount amountToSend = chooseAmount(availableFunds); auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain, &coinsView, &cs_main); builder.SetExpiryHeight(targetHeight_ + MIGRATION_EXPIRY_DELTA); - LogPrint("zrpcunsafe", "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - FEE)); + LogPrint(BCLog::ZRPCUNSAFE, "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - FEE)); std::vector fromNotes; CAmount fromNoteAmount = 0; while (fromNoteAmount < amountToSend) { @@ -125,7 +125,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { availableFunds -= fromNoteAmount; for (const SproutNoteEntry& sproutEntry : fromNotes) { std::string data(sproutEntry.memo.begin(), sproutEntry.memo.end()); - LogPrint("zrpcunsafe", "%s: Adding Sprout note input (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, memo=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: Adding Sprout note input (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, memo=%s)\n", getId(), sproutEntry.jsop.hash.ToString().substr(0, 10), sproutEntry.jsop.js, @@ -153,17 +153,17 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { builder.AddSaplingOutput(ovkForShieldingFromTaddr(seed), migrationDestAddress, amountToSend - FEE); CTransaction tx = builder.Build().GetTxOrThrow(); if (isCancelled()) { - LogPrint("zrpcunsafe", "%s: Canceled. Stopping.\n", getId()); + LogPrint(BCLog::ZRPCUNSAFE, "%s: Canceled. Stopping.\n", getId()); break; } pwalletMain->AddPendingSaplingMigrationTx(tx); - LogPrint("zrpcunsafe", "%s: Added pending migration transaction with txid=%s\n", getId(), tx.GetHash().ToString()); + LogPrint(BCLog::ZRPCUNSAFE, "%s: Added pending migration transaction with txid=%s\n", getId(), tx.GetHash().ToString()); ++numTxCreated; amountMigrated += amountToSend - FEE; migrationTxIds.push_back(tx.GetHash().ToString()); } while (numTxCreated < 5 && availableFunds > CENT); - LogPrint("zrpcunsafe", "%s: Created %d transactions with total Sapling output amount=%s\n", getId(), numTxCreated, FormatMoney(amountMigrated)); + LogPrint(BCLog::ZRPCUNSAFE, "%s: Created %d transactions with total Sapling output amount=%s\n", getId(), numTxCreated, FormatMoney(amountMigrated)); setMigrationResult(numTxCreated, amountMigrated, migrationTxIds); return true; } diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index c1c1a2481..e1232d5de 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -113,10 +113,10 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( } // Log the context info i.e. the call parameters to z_sendmany - if (LogAcceptCategory("zrpcunsafe")) { - LogPrint("zrpcunsafe", "%s: z_sendmany initialized (params=%s)\n", getId(), contextInfo.write()); + if (LogAcceptCategory(BCLog::ZRPCUNSAFE)) { + LogPrint(BCLog::ZRPCUNSAFE, "%s: z_sendmany initialized (params=%s)\n", getId(), contextInfo.write()); } else { - LogPrint("zrpc", "%s: z_sendmany initialized\n", getId()); + LogPrint(BCLog::ZRPC, "%s: z_sendmany initialized\n", getId()); } // Enable payment disclosure if requested @@ -187,9 +187,9 @@ void AsyncRPCOperation_sendmany::main() { for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { p.first.hash = txidhash; if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); } } } @@ -338,13 +338,13 @@ bool AsyncRPCOperation_sendmany::main_impl() { } } - LogPrint((isfromtaddr_) ? "zrpc" : "zrpcunsafe", "%s: spending %s to send %s with fee %s\n", + LogPrint((isfromtaddr_) ? BCLog::ZRPC : BCLog::ZRPCUNSAFE, "%s: spending %s to send %s with fee %s\n", getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); - LogPrint("zrpc", "%s: transparent input: %s (to choose from)\n", getId(), FormatMoney(t_inputs_total)); - LogPrint("zrpcunsafe", "%s: private input: %s (to choose from)\n", getId(), FormatMoney(z_inputs_total)); - LogPrint("zrpc", "%s: transparent output: %s\n", getId(), FormatMoney(t_outputs_total)); - LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(z_outputs_total)); - LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); + LogPrint(BCLog::ZRPC, "%s: transparent input: %s (to choose from)\n", getId(), FormatMoney(t_inputs_total)); + LogPrint(BCLog::ZRPCUNSAFE, "%s: private input: %s (to choose from)\n", getId(), FormatMoney(z_inputs_total)); + LogPrint(BCLog::ZRPC, "%s: transparent output: %s\n", getId(), FormatMoney(t_outputs_total)); + LogPrint(BCLog::ZRPCUNSAFE, "%s: private output: %s\n", getId(), FormatMoney(z_outputs_total)); + LogPrint(BCLog::ZRPC, "%s: fee: %s\n", getId(), FormatMoney(minersFee)); /** @@ -486,7 +486,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { if (change > 0) { add_taddr_change_output_to_tx(keyChange, change); - LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", + LogPrint(BCLog::ZRPC, "%s: transparent change in transaction output (amount=%s)\n", getId(), FormatMoney(change) ); @@ -573,7 +573,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { "in z_sendmany.", FormatMoney(change))); } else { add_taddr_change_output_to_tx(keyChange, change); - LogPrint("zrpc", "%s: transparent change in transaction output (amount=%s)\n", + LogPrint(BCLog::ZRPC, "%s: transparent change in transaction output (amount=%s)\n", getId(), FormatMoney(change) ); @@ -715,7 +715,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { jsInputValue += plaintext.value(); - LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: spending change (amount=%s)\n", getId(), FormatMoney(plaintext.value()) ); @@ -766,7 +766,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight; wtxDepth = wtx.GetDepthInMainChain(); } - LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, height=%d, confirmations=%d)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: spending note (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, height=%d, confirmations=%d)\n", getId(), jso.hash.ToString().substr(0, 10), jso.js, @@ -871,7 +871,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { if (jsChange>0) { info.vjsout.push_back(JSOutput(std::get(frompaymentaddress_), jsChange)); - LogPrint("zrpcunsafe", "%s: generating note for change (amount=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: generating note for change (amount=%s)\n", getId(), FormatMoney(jsChange) ); @@ -965,7 +965,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { for (SproutNoteEntry & entry : sproutEntries) { z_sprout_inputs_.push_back(SendManyInputJSOP(entry.jsop, entry.note, CAmount(entry.note.value()))); std::string data(entry.memo.begin(), entry.memo.end()); - LogPrint("zrpcunsafe", "%s: found unspent Sprout note (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, memo=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: found unspent Sprout note (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, memo=%s)\n", getId(), entry.jsop.hash.ToString().substr(0, 10), entry.jsop.js, @@ -978,7 +978,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() { for (auto entry : saplingEntries) { z_sapling_inputs_.push_back(entry); std::string data(entry.memo.begin(), entry.memo.end()); - LogPrint("zrpcunsafe", "%s: found unspent Sapling note (txid=%s, vShieldedSpend=%d, amount=%s, memo=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: found unspent Sapling note (txid=%s, vShieldedSpend=%d, amount=%s, memo=%s)\n", getId(), entry.op.hash.ToString().substr(0, 10), entry.op.n, @@ -1063,7 +1063,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( CMutableTransaction mtx(tx_); - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", getId(), tx_.vJoinSplit.size(), FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), @@ -1178,7 +1178,7 @@ UniValue AsyncRPCOperation_sendmany::perform_joinsplit( PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); } // !!! Payment disclosure END diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp index a5c608f8b..f1b1abe4a 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.cpp +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.cpp @@ -85,10 +85,10 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase( } // Log the context info - if (LogAcceptCategory("zrpcunsafe")) { - LogPrint("zrpcunsafe", "%s: z_shieldcoinbase initialized (context=%s)\n", getId(), contextInfo.write()); + if (LogAcceptCategory(BCLog::ZRPCUNSAFE)) { + LogPrint(BCLog::ZRPCUNSAFE, "%s: z_shieldcoinbase initialized (context=%s)\n", getId(), contextInfo.write()); } else { - LogPrint("zrpc", "%s: z_shieldcoinbase initialized\n", getId()); + LogPrint(BCLog::ZRPC, "%s: z_shieldcoinbase initialized\n", getId()); } // Lock UTXOs @@ -166,9 +166,9 @@ void AsyncRPCOperation_shieldcoinbase::main() { for (PaymentDisclosureKeyInfo p : paymentDisclosureData_) { p.first.hash = txidhash; if (!db->Put(p.first, p.second)) { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: Error writing entry to database for key %s\n", getId(), p.first.ToString()); } else { - LogPrint("paymentdisclosure", "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: Successfully added entry to database for key %s\n", getId(), p.first.ToString()); } } } @@ -193,7 +193,7 @@ bool AsyncRPCOperation_shieldcoinbase::main_impl() { } CAmount sendAmount = targetAmount - minersFee; - LogPrint("zrpc", "%s: spending %s to shield %s with fee %s\n", + LogPrint(BCLog::ZRPC, "%s: spending %s to shield %s with fee %s\n", getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); return std::visit(ShieldToAddress(this, sendAmount), tozaddr_); @@ -290,7 +290,7 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf CMutableTransaction mtx(tx_); - LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", + LogPrint(BCLog::ZRPCUNSAFE, "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", getId(), tx_.vJoinSplit.size(), FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), @@ -404,7 +404,7 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); - LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); + LogPrint(BCLog::ZPAYMENT, "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); } // !!! Payment disclosure END diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 7017b7a71..a9b21ebc8 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -120,7 +120,7 @@ void CDBEnv::MakeMock() boost::this_thread::interruption_point(); - LogPrint("db", "CDBEnv::MakeMock\n"); + LogPrint(BCLog::DB, "CDBEnv::MakeMock\n"); dbenv->set_cachesize(1, 0, 1); dbenv->set_lg_bsize(10485760 * 4); @@ -422,7 +422,7 @@ void CDBEnv::Flush(bool fShutdown) { int64_t nStart = GetTimeMillis(); // Flush log data to the actual data file on all files that are not in use - LogPrint("db", "CDBEnv::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); + LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); if (!fDbEnvInit) return; { @@ -431,21 +431,21 @@ void CDBEnv::Flush(bool fShutdown) while (mi != mapFileUseCount.end()) { string strFile = (*mi).first; int nRefCount = (*mi).second; - LogPrint("db", "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); + LogPrint(BCLog::DB, "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); if (nRefCount == 0) { // Move log data to the dat file CloseDb(strFile); - LogPrint("db", "CDBEnv::Flush: %s checkpoint\n", strFile); + LogPrint(BCLog::DB, "CDBEnv::Flush: %s checkpoint\n", strFile); dbenv->txn_checkpoint(0, 0, 0); - LogPrint("db", "CDBEnv::Flush: %s detach\n", strFile); + LogPrint(BCLog::DB, "CDBEnv::Flush: %s detach\n", strFile); if (!fMockDb) dbenv->lsn_reset(strFile.c_str(), 0); - LogPrint("db", "CDBEnv::Flush: %s closed\n", strFile); + LogPrint(BCLog::DB, "CDBEnv::Flush: %s closed\n", strFile); mapFileUseCount.erase(mi++); } else mi++; } - LogPrint("db", "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); + LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); if (fShutdown) { char** listp; if (mapFileUseCount.empty()) { diff --git a/src/wallet/paymentdisclosure.cpp b/src/wallet/paymentdisclosure.cpp index ddce48cc2..1676c1653 100644 --- a/src/wallet/paymentdisclosure.cpp +++ b/src/wallet/paymentdisclosure.cpp @@ -36,7 +36,7 @@ PaymentDisclosure::PaymentDisclosure(const uint256 &joinSplitPubKey, const Payme // Serialize and hash the payload to generate a signature uint256 dataToBeSigned = SerializeHash(payload, SER_GETHASH, 0); - LogPrint("paymentdisclosure", "Payment Disclosure: signing raw payload = %s\n", dataToBeSigned.ToString()); + LogPrint(BCLog::ZPAYMENT, "Payment Disclosure: signing raw payload = %s\n", dataToBeSigned.ToString()); // Prepare buffer to store ed25519 key pair in libsodium-compatible format unsigned char bufferKeyPair[64]; @@ -61,5 +61,5 @@ PaymentDisclosure::PaymentDisclosure(const uint256 &joinSplitPubKey, const Payme } std::string sigString = HexStr(payloadSig.data(), payloadSig.data() + payloadSig.size()); - LogPrint("paymentdisclosure", "Payment Disclosure: signature = %s\n", sigString); + LogPrint(BCLog::ZPAYMENT, "Payment Disclosure: signature = %s\n", sigString); } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 7fb11856b..44b766a25 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -399,14 +399,14 @@ UniValue importwallet_impl(const UniValue& params, bool fHelp, bool fImportZKeys auto addResult = std::visit( AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus(), nTime, hdKeypath, seedFpStr, true), spendingkey); if (addResult == KeyAlreadyExists){ - LogPrint("zrpc", "Skipping import of zaddr (key already present)\n"); + LogPrint(BCLog::ZRPC, "Skipping import of zaddr (key already present)\n"); } else if (addResult == KeyNotAdded) { // Something went wrong fGood = false; } continue; } else { - LogPrint("zrpc", "Importing detected an error: invalid spending key. Trying as a transparent key...\n"); + LogPrint(BCLog::ZRPC, "Importing detected an error: invalid spending key. Trying as a transparent key...\n"); // Not a valid spending key, so carry on and see if it's a BitcoinZ style t-address. } } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index be4c49bbe..9af557d2a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3505,11 +3505,15 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nValueRet += vValue[i].first; } - LogPrint("selectcoins", "SelectCoins() best subset: "); - for (unsigned int i = 0; i < vValue.size(); i++) - if (vfBest[i]) - LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first)); - LogPrint("selectcoins", "total %s\n", FormatMoney(nBest)); + if (LogAcceptCategory(BCLog::SELECTCOINS)) { + LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); + for (unsigned int i = 0; i < vValue.size(); i++) { + if (vfBest[i]) { + LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].first)); + } + } + LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest)); + } } return true; @@ -5375,7 +5379,7 @@ KeyAddResult AddViewingKeyToWallet::operator()(const libzcash::InvalidEncoding& KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const { auto addr = sk.address(); if (log){ - LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(addr)); + LogPrint(BCLog::ZRPC, "Importing zaddr %s...\n", EncodePaymentAddress(addr)); } if (m_wallet->HaveSproutSpendingKey(addr)) { return KeyAlreadyExists; @@ -5392,7 +5396,7 @@ KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedS auto ivk = extfvk.fvk.in_viewing_key(); { if (log){ - LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(sk.DefaultAddress())); + LogPrint(BCLog::ZRPC, "Importing zaddr %s...\n", EncodePaymentAddress(sk.DefaultAddress())); } // Don't throw error in case a key is already there diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index f896baaba..5b45f9759 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -973,7 +973,7 @@ void ThreadFlushWalletDB(const string& strFile) map::iterator mi = bitdb.mapFileUseCount.find(strFile); if (mi != bitdb.mapFileUseCount.end()) { - LogPrint("db", "Flushing %s\n", strFile); + LogPrint(BCLog::DB, "Flushing %s\n", strFile); nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nStart = GetTimeMillis(); @@ -982,7 +982,7 @@ void ThreadFlushWalletDB(const string& strFile) bitdb.CheckpointLSN(strFile); bitdb.mapFileUseCount.erase(mi++); - LogPrint("db", "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart); + LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart); } } } diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index e427e6b97..09a01b862 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -12,7 +12,7 @@ void zmqError(const char *str) { - LogPrint("zmq", "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno)); + LogPrint(BCLog::ZMQ, "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno)); } CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(NULL) @@ -73,7 +73,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::CreateWithArguments(const // Called at startup to conditionally set up ZMQ socket(s) bool CZMQNotificationInterface::Initialize() { - LogPrint("zmq", "zmq: Initialize notification interface\n"); + LogPrint(BCLog::ZMQ, "zmq: Initialize notification interface\n"); assert(!pcontext); pcontext = zmq_init(1); @@ -90,11 +90,11 @@ bool CZMQNotificationInterface::Initialize() CZMQAbstractNotifier *notifier = *i; if (notifier->Initialize(pcontext)) { - LogPrint("zmq", " Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress()); + LogPrint(BCLog::ZMQ, " Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress()); } else { - LogPrint("zmq", " Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress()); + LogPrint(BCLog::ZMQ, " Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress()); break; } } @@ -110,13 +110,13 @@ bool CZMQNotificationInterface::Initialize() // Called during shutdown sequence void CZMQNotificationInterface::Shutdown() { - LogPrint("zmq", "zmq: Shutdown notification interface\n"); + LogPrint(BCLog::ZMQ, "zmq: Shutdown notification interface\n"); if (pcontext) { for (std::list::iterator i=notifiers.begin(); i!=notifiers.end(); ++i) { CZMQAbstractNotifier *notifier = *i; - LogPrint("zmq", " Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress()); + LogPrint(BCLog::ZMQ, " Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress()); notifier->Shutdown(); } zmq_ctx_destroy(pcontext); diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp index a2a6758ca..9662a5536 100644 --- a/src/zmq/zmqpublishnotifier.cpp +++ b/src/zmq/zmqpublishnotifier.cpp @@ -85,7 +85,7 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext) } else { - LogPrint("zmq", "zmq: Reusing socket for address %s\n", address); + LogPrint(BCLog::ZMQ, "zmq: Reusing socket for address %s\n", address); psocket = i->second->psocket; mapPublishNotifiers.insert(std::make_pair(address, this)); @@ -115,7 +115,7 @@ void CZMQAbstractPublishNotifier::Shutdown() if (count == 1) { - LogPrint("zmq", "Close socket at address %s\n", address); + LogPrint(BCLog::ZMQ, "Close socket at address %s\n", address); int linger = 0; zmq_setsockopt(psocket, ZMQ_LINGER, &linger, sizeof(linger)); zmq_close(psocket); @@ -144,7 +144,7 @@ bool CZMQAbstractPublishNotifier::SendMessage(const char *command, const void* d bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex) { uint256 hash = pindex->GetBlockHash(); - LogPrint("zmq", "zmq: Publish hashblock %s\n", hash.GetHex()); + LogPrint(BCLog::ZMQ, "zmq: Publish hashblock %s\n", hash.GetHex()); char data[32]; for (unsigned int i = 0; i < 32; i++) data[31 - i] = hash.begin()[i]; @@ -154,7 +154,7 @@ bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex) bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction) { uint256 hash = transaction.GetHash(); - LogPrint("zmq", "zmq: Publish hashtx %s\n", hash.GetHex()); + LogPrint(BCLog::ZMQ, "zmq: Publish hashtx %s\n", hash.GetHex()); char data[32]; for (unsigned int i = 0; i < 32; i++) data[31 - i] = hash.begin()[i]; @@ -163,7 +163,7 @@ bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &t bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex) { - LogPrint("zmq", "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex()); + LogPrint(BCLog::ZMQ, "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex()); const Consensus::Params& consensusParams = Params().GetConsensus(); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); @@ -184,7 +184,7 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex) bool CZMQPublishCheckedBlockNotifier::NotifyBlock(const CBlock& block) { - LogPrint("zmq", "zmq: Publish checkedblock %s\n", block.GetHash().GetHex()); + LogPrint(BCLog::ZMQ, "zmq: Publish checkedblock %s\n", block.GetHash().GetHex()); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); { @@ -198,7 +198,7 @@ bool CZMQPublishCheckedBlockNotifier::NotifyBlock(const CBlock& block) bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction) { uint256 hash = transaction.GetHash(); - LogPrint("zmq", "zmq: Publish rawtx %s\n", hash.GetHex()); + LogPrint(BCLog::ZMQ, "zmq: Publish rawtx %s\n", hash.GetHex()); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << transaction; return SendMessage(MSG_RAWTX, &(*ss.begin()), ss.size()); From 52b3b558b71f9f6d67019cdebd0a2514d60a6549 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Sat, 31 Aug 2024 10:08:16 +0000 Subject: [PATCH 124/146] Fix 'bitcoinz-gtest' errors --- src/gtest/test_checkblock.cpp | 7 +++++-- src/gtest/test_checktransaction.cpp | 4 ++-- src/gtest/test_deprecation.cpp | 27 --------------------------- 3 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index b3d21e8b9..1aca72b9b 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -10,10 +10,10 @@ class MockCValidationState : public CValidationState { public: MOCK_METHOD5(DoS, bool(int level, bool ret, - unsigned char chRejectCodeIn, std::string strRejectReasonIn, + unsigned int chRejectCodeIn, std::string strRejectReasonIn, bool corruptionIn)); MOCK_METHOD3(Invalid, bool(bool ret, - unsigned char _chRejectCode, std::string _strRejectReason)); + unsigned int _chRejectCode, std::string _strRejectReason)); MOCK_METHOD1(Error, bool(std::string strRejectReasonIn)); MOCK_CONST_METHOD0(IsValid, bool()); MOCK_CONST_METHOD0(IsInvalid, bool()); @@ -31,6 +31,9 @@ TEST(CheckBlock, VersionTooLow) { block.nVersion = 1; MockCValidationState state; + + SelectParams(CBaseChainParams::MAIN); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false)).Times(1); EXPECT_FALSE(CheckBlock(block, state, Params(), verifier, false, false)); } diff --git a/src/gtest/test_checktransaction.cpp b/src/gtest/test_checktransaction.cpp index 16f7a1405..6b4fec8bf 100644 --- a/src/gtest/test_checktransaction.cpp +++ b/src/gtest/test_checktransaction.cpp @@ -30,10 +30,10 @@ TEST(ChecktransactionTests, CheckVpubNotBothNonzero) { class MockCValidationState : public CValidationState { public: MOCK_METHOD5(DoS, bool(int level, bool ret, - unsigned char chRejectCodeIn, std::string strRejectReasonIn, + unsigned int chRejectCodeIn, std::string strRejectReasonIn, bool corruptionIn)); MOCK_METHOD3(Invalid, bool(bool ret, - unsigned char _chRejectCode, std::string _strRejectReason)); + unsigned int _chRejectCode, std::string _strRejectReason)); MOCK_METHOD1(Error, bool(std::string strRejectReasonIn)); MOCK_CONST_METHOD0(IsValid, bool()); MOCK_CONST_METHOD0(IsInvalid, bool()); diff --git a/src/gtest/test_deprecation.cpp b/src/gtest/test_deprecation.cpp index f5e7639f6..a57631e52 100644 --- a/src/gtest/test_deprecation.cpp +++ b/src/gtest/test_deprecation.cpp @@ -121,30 +121,3 @@ TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnTestnet) { EnforceNodeDeprecation(DEPRECATION_HEIGHT+1); EXPECT_FALSE(ShutdownRequested()); } - -TEST_F(DeprecationTest, AlertNotify) { - fs::path temp = fs::temp_directory_path() / - fs::unique_path("alertnotify-%%%%.txt"); - - mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string(); - - EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING)); - EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT, false, false); - - std::vector r = read_lines(temp); - EXPECT_EQ(r.size(), 1u); - - // -alertnotify restricts the message to safe characters. - auto expectedMsg = strprintf( - "This version will be deprecated at block height %d, and will automatically shut down. You should upgrade to the latest version of BitcoinZ.", - DEPRECATION_HEIGHT); - - // Windows built-in echo semantics are different than posixy shells. Quotes and - // whitespace are printed literally. -#ifndef WIN32 - EXPECT_EQ(r[0], expectedMsg); -#else - EXPECT_EQ(r[0], strprintf("'%s' ", expectedMsg)); -#endif - fs::remove(temp); -} From 47c19c983a84c9dc6326e8caa3e2e158eb5f725c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Sat, 31 Aug 2024 13:53:48 +0000 Subject: [PATCH 125/146] Change GetPriority calculation --- src/coins.cpp | 6 ++++-- src/coins.h | 8 ++++++-- src/gtest/test_mempool.cpp | 5 +++-- src/main.cpp | 7 ++++--- src/test/test_bitcoin.cpp | 7 ++++++- src/txmempool.cpp | 17 ++++++++++------- src/txmempool.h | 32 +++++++++++++++++++------------- 7 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/coins.cpp b/src/coins.cpp index e3f7eddd9..ea75b1a00 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -639,8 +639,9 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const return true; } -double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const +double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const { + inChainInputValue = 0; if (tx.IsCoinBase()) return 0.0; @@ -660,8 +661,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const const CCoins* coins = AccessCoins(txin.prevout.hash); assert(coins); if (!coins->IsAvailable(txin.prevout.n)) continue; - if (coins->nHeight < nHeight) { + if (coins->nHeight <= nHeight) { dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); + inChainInputValue += coins->vout[txin.prevout.n].nValue; } } diff --git a/src/coins.h b/src/coins.h index 088900e6d..2a95a3fe4 100644 --- a/src/coins.h +++ b/src/coins.h @@ -556,8 +556,12 @@ class CCoinsViewCache : public CCoinsViewBacked //! Check whether all joinsplit and sapling spend requirements (anchors/nullifiers) are satisfied bool HaveShieldedRequirements(const CTransaction& tx) const; - //! Return priority of tx at height nHeight - double GetPriority(const CTransaction &tx, int nHeight) const; + /** + * Return priority of tx at height nHeight. Also calculate the sum of the values of the inputs + * that are already in the chain. These are the inputs that will age and increase priority as + * new blocks are added to the chain. + */ + double GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const; const CTxOut &GetOutputFor(const CTxIn& input) const; diff --git a/src/gtest/test_mempool.cpp b/src/gtest/test_mempool.cpp index 60b1658ab..715ff7bf3 100644 --- a/src/gtest/test_mempool.cpp +++ b/src/gtest/test_mempool.cpp @@ -92,9 +92,10 @@ TEST(Mempool, PriorityStatsDoNotCrash) { CAmount nFees = 0; int64_t nTime = 0x58e5fed9; unsigned int nHeight = 92045; - double dPriority = view.GetPriority(tx, nHeight); + CAmount inChainInputValue; + double dPriority = view.GetPriority(tx, nHeight, inChainInputValue); - CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, false, SPROUT_BRANCH_ID); + CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, inChainInputValue, false, SPROUT_BRANCH_ID); // Check it does not crash (ie. the death test fails) EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), ""); diff --git a/src/main.cpp b/src/main.cpp index 2b1001420..c415e288f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1434,7 +1434,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; - double dPriority = view.GetPriority(tx, chainActive.Height()); + CAmount inChainInputValue; + double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue); // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. @@ -1452,7 +1453,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // it has passed ContextualCheckInputs and therefore this is correct. auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, consensusBranchId); unsigned int nSize = entry.GetTxSize(); if (txFeeRate) { *txFeeRate = CFeeRate(nFees, nSize); @@ -1472,7 +1473,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false); - } else if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { + } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { // Require that free transactions have sufficient priority to be mined in the next block. return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f96f36752..faf5775dd 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -222,8 +222,13 @@ TestChain100Setup::~TestChain100Setup() CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) { + CTransaction txn(tx); + bool hasNoDependencies = pool ? pool->HasNoInputsOf(tx) : hadNoDependencies; + // Hack to assume either its completely dependent on other mempool txs or not at all + CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0; + return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight, - pool ? pool->HasNoInputsOf(tx) : hadNoDependencies, + hasNoDependencies, inChainValue, spendsCoinbase, nBranchId); } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f0e39c77c..81e338b8b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -22,11 +22,11 @@ using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, - int64_t _nTime, double _dPriority, - unsigned int _nHeight, bool poolHasNoInputsOf, + int64_t _nTime, double _entryPriority, unsigned int _entryHeight, + bool poolHasNoInputsOf, CAmount _inChainInputValue, bool _spendsCoinbase, uint32_t _nBranchId): - tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight), - hadNoDependencies(poolHasNoInputsOf), + tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), + hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), spendsCoinbase(_spendsCoinbase), nBranchId(_nBranchId) { nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); @@ -36,6 +36,8 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; nFeesWithDescendants = nFee; + CAmount nValueIn = tx.GetValueOut()+nFee; + assert(inChainInputValue <= nValueIn); } CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) @@ -46,9 +48,10 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) double CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const { - CAmount nValueIn = tx.GetValueOut()+nFee; - double deltaPriority = ((double)(currentHeight-nHeight)*nValueIn)/nModSize; - double dResult = dPriority + deltaPriority; + double deltaPriority = ((double)(currentHeight-entryHeight)*inChainInputValue)/nModSize; + double dResult = entryPriority + deltaPriority; + if (dResult < 0) // This should only happen if it was called with a height below entry height + dResult = 0; return dResult; } diff --git a/src/txmempool.h b/src/txmempool.h index 14cf7c55a..b4ce4bffc 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -63,16 +63,17 @@ class CTxMemPoolEntry { private: CTransaction tx; - CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups - size_t nTxSize; //!< ... and avoid recomputing tx size - size_t nModSize; //!< ... and modified size for priority - size_t nUsageSize; //!< ... and total memory usage - int64_t nTime; //!< Local time when entering the mempool - double dPriority; //!< Priority when entering the mempool - unsigned int nHeight; //!< Chain height when entering the mempool - bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool - bool spendsCoinbase; //!< keep track of transactions that spend a coinbase - uint32_t nBranchId; //!< Branch ID this transaction is known to commit to, cached for efficiency + CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups + size_t nTxSize; //!< ... and avoid recomputing tx size + size_t nModSize; //!< ... and modified size for priority + size_t nUsageSize; //!< ... and total memory usage + int64_t nTime; //!< Local time when entering the mempool + double entryPriority; //!< Priority when entering the mempool + unsigned int entryHeight; //!< Chain height when entering the mempool + bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool + CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain + bool spendsCoinbase; //!< keep track of transactions that spend a coinbase + uint32_t nBranchId; //!< Branch ID this transaction is known to commit to, cached for efficiency // Information about descendants of this transaction that are in the // mempool; if we remove this transaction we must remove all of these @@ -85,16 +86,21 @@ class CTxMemPoolEntry public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, - int64_t _nTime, double _dPriority, unsigned int _nHeight, - bool poolHasNoInputsOf, bool spendsCoinbase, uint32_t nBranchId); + int64_t _nTime, double _entryPriority, unsigned int _entryHeight, + bool poolHasNoInputsOf, CAmount _inChainInputValue, + bool spendsCoinbase, uint32_t nBranchId); CTxMemPoolEntry(const CTxMemPoolEntry& other); const CTransaction& GetTx() const { return this->tx; } + /** + * Fast calculation of lower bound of current priority as update + * from entry priority. Only inputs that were originally in-chain will age. + */ double GetPriority(unsigned int currentHeight) const; const CAmount& GetFee() const { return nFee; } size_t GetTxSize() const { return nTxSize; } int64_t GetTime() const { return nTime; } - unsigned int GetHeight() const { return nHeight; } + unsigned int GetHeight() const { return entryHeight; } bool WasClearAtEntry() const { return hadNoDependencies; } size_t DynamicMemoryUsage() const { return nUsageSize; } From f4488122591476ee22e35c47d9b692234753df07 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Sat, 31 Aug 2024 17:16:08 +0000 Subject: [PATCH 126/146] Bitcoin's backports: - Add absurdly high fee message to validation state (for RPC propagation) - Add assertion and cast before sending reject code - Add debug message to CValidationState for optional extra information - Introduce REJECT_INTERNAL codes for local AcceptToMempool errors - Add function to convert CValidationState to a human-readable message - Remove most logging from transaction validation - Add information to errors in ConnectBlock, CheckBlock - Move mempool rejections to new debug category - net: Fix sent reject messages for blocks and transactions - Only use AddInventoryKnown for transactions --- src/consensus/validation.h | 11 ++- src/gtest/test_checkblock.cpp | 23 ++--- src/gtest/test_checktransaction.cpp | 91 ++++++++++---------- src/main.cpp | 128 +++++++++++++--------------- src/main.h | 9 ++ 5 files changed, 138 insertions(+), 124 deletions(-) diff --git a/src/consensus/validation.h b/src/consensus/validation.h index b5eb6a0ac..09f5797f0 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -29,14 +29,17 @@ class CValidationState { std::string strRejectReason; unsigned int chRejectCode; bool corruptionPossible; + std::string strDebugMessage; public: CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {} virtual bool DoS(int level, bool ret = false, unsigned int chRejectCodeIn=0, std::string strRejectReasonIn="", - bool corruptionIn=false) { + bool corruptionIn=false, + const std::string &strDebugMessageIn="") { chRejectCode = chRejectCodeIn; strRejectReason = strRejectReasonIn; corruptionPossible = corruptionIn; + strDebugMessage = strDebugMessageIn; if (mode == MODE_ERROR) return ret; nDoS += level; @@ -44,8 +47,9 @@ class CValidationState { return ret; } virtual bool Invalid(bool ret = false, - unsigned int _chRejectCode=0, std::string _strRejectReason="") { - return DoS(0, ret, _chRejectCode, _strRejectReason); + unsigned int _chRejectCode=0, const std::string &_strRejectReason="", + const std::string &_strDebugMessage="") { + return DoS(0, ret, _chRejectCode, _strRejectReason, false, _strDebugMessage); } virtual bool Error(const std::string& strRejectReasonIn) { if (mode == MODE_VALID) @@ -74,6 +78,7 @@ class CValidationState { } virtual unsigned int GetRejectCode() const { return chRejectCode; } virtual std::string GetRejectReason() const { return strRejectReason; } + virtual std::string GetDebugMessage() const { return strDebugMessage; } }; #endif // BITCOIN_CONSENSUS_VALIDATION_H diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index 1aca72b9b..ad52c9cf2 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -9,11 +9,13 @@ class MockCValidationState : public CValidationState { public: - MOCK_METHOD5(DoS, bool(int level, bool ret, - unsigned int chRejectCodeIn, std::string strRejectReasonIn, - bool corruptionIn)); - MOCK_METHOD3(Invalid, bool(bool ret, - unsigned int _chRejectCode, std::string _strRejectReason)); + MOCK_METHOD6(DoS, bool(int level, bool ret, + unsigned int chRejectCodeIn, const std::string strRejectReasonIn, + bool corruptionIn, + const std::string &strDebugMessageIn)); + MOCK_METHOD4(Invalid, bool(bool ret, + unsigned int _chRejectCode, const std::string _strRejectReason, + const std::string &_strDebugMessage)); MOCK_METHOD1(Error, bool(std::string strRejectReasonIn)); MOCK_CONST_METHOD0(IsValid, bool()); MOCK_CONST_METHOD0(IsInvalid, bool()); @@ -22,6 +24,7 @@ class MockCValidationState : public CValidationState { MOCK_CONST_METHOD0(CorruptionPossible, bool()); MOCK_CONST_METHOD0(GetRejectCode, unsigned int()); MOCK_CONST_METHOD0(GetRejectReason, std::string()); + MOCK_CONST_METHOD0(GetDebugMessage, std::string()); }; TEST(CheckBlock, VersionTooLow) { @@ -34,7 +37,7 @@ TEST(CheckBlock, VersionTooLow) { SelectParams(CBaseChainParams::MAIN); - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false, "")).Times(1); EXPECT_FALSE(CheckBlock(block, state, Params(), verifier, false, false)); } @@ -67,7 +70,7 @@ TEST(CheckBlock, BlockSproutRejectsBadVersion) { auto verifier = ProofVerifier::Strict(); - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false, "")).Times(1); EXPECT_FALSE(CheckBlock(block, state, Params(), verifier, false, false)); } @@ -138,7 +141,7 @@ class ContextualCheckBlockTest : public ::testing::Test { // We now expect this to be an invalid block, for the given reason. MockCValidationState state; - EXPECT_CALL(state, DoS(level, false, REJECT_INVALID, reason, false)).Times(1); + EXPECT_CALL(state, DoS(level, false, REJECT_INVALID, reason, false, "")).Times(1); EXPECT_FALSE(ContextualCheckBlock(block, state, Params(), &indexPrev)); } @@ -169,14 +172,14 @@ TEST_F(ContextualCheckBlockTest, BadCoinbaseHeight) { CBlock prev; CBlockIndex indexPrev {prev}; indexPrev.nHeight = 50; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-height", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "")).Times(1); EXPECT_FALSE(ContextualCheckBlock(block, state, Params(), &indexPrev)); // Setting to an incorrect height should fail mtx.vin[0].scriptSig = CScript() << 52 << OP_0; CTransaction tx3 {mtx}; block.vtx[0] = tx3; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-height", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "")).Times(1); EXPECT_FALSE(ContextualCheckBlock(block, state, Params(), &indexPrev)); // After correcting the scriptSig, should pass diff --git a/src/gtest/test_checktransaction.cpp b/src/gtest/test_checktransaction.cpp index 6b4fec8bf..d0bbb4fa5 100644 --- a/src/gtest/test_checktransaction.cpp +++ b/src/gtest/test_checktransaction.cpp @@ -29,11 +29,13 @@ TEST(ChecktransactionTests, CheckVpubNotBothNonzero) { class MockCValidationState : public CValidationState { public: - MOCK_METHOD5(DoS, bool(int level, bool ret, - unsigned int chRejectCodeIn, std::string strRejectReasonIn, - bool corruptionIn)); - MOCK_METHOD3(Invalid, bool(bool ret, - unsigned int _chRejectCode, std::string _strRejectReason)); + MOCK_METHOD6(DoS, bool(int level, bool ret, + unsigned int chRejectCodeIn, const std::string &strRejectReasonIn, + bool corruptionIn, + const std::string &strDebugMessageIn)); + MOCK_METHOD4(Invalid, bool(bool ret, + unsigned int _chRejectCode, const std::string _strRejectReason, + const std::string &_strDebugMessage)); MOCK_METHOD1(Error, bool(std::string strRejectReasonIn)); MOCK_CONST_METHOD0(IsValid, bool()); MOCK_CONST_METHOD0(IsInvalid, bool()); @@ -42,6 +44,7 @@ class MockCValidationState : public CValidationState { MOCK_CONST_METHOD0(CorruptionPossible, bool()); MOCK_CONST_METHOD0(GetRejectCode, unsigned int()); MOCK_CONST_METHOD0(GetRejectReason, std::string()); + MOCK_CONST_METHOD0(GetDebugMessage, std::string()); }; void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranchId); @@ -107,7 +110,7 @@ TEST(ChecktransactionTests, BadVersionTooLow) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -118,7 +121,7 @@ TEST(ChecktransactionTests, BadTxnsVinEmpty) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty", false)).Times(1); + EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -130,7 +133,7 @@ TEST(ChecktransactionTests, BadTxnsVoutEmpty) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty", false)).Times(1); + EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -164,7 +167,7 @@ TEST(ChecktransactionTests, BadTxnsOversize) { EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state)); // ... but fails contextual ones! - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false, "")).Times(1); EXPECT_FALSE(ContextualCheckTransaction(tx, state, Params(), 1, 100)); } @@ -242,7 +245,7 @@ TEST(ChecktransactionTests, OversizeSaplingTxns) { EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), MAX_TX_SIZE_AFTER_SAPLING + 1); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false, "")).Times(1); EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state)); } @@ -257,7 +260,7 @@ TEST(ChecktransactionTests, BadTxnsVoutNegative) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -268,7 +271,7 @@ TEST(ChecktransactionTests, BadTxnsVoutToolarge) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -280,7 +283,7 @@ TEST(ChecktransactionTests, BadTxnsTxouttotalToolargeOutputs) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -291,7 +294,7 @@ TEST(ChecktransactionTests, ValueBalanceNonZero) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-nonzero", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-nonzero", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -303,7 +306,7 @@ TEST(ChecktransactionTests, PositiveValueBalanceTooLarge) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -315,7 +318,7 @@ TEST(ChecktransactionTests, NegativeValueBalanceTooLarge) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -328,7 +331,7 @@ TEST(ChecktransactionTests, ValueBalanceOverflowsTotal) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -340,7 +343,7 @@ TEST(ChecktransactionTests, BadTxnsTxouttotalToolargeJoinsplit) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -352,7 +355,7 @@ TEST(ChecktransactionTests, BadTxnsTxintotalToolargeJoinsplit) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txintotal-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txintotal-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -363,7 +366,7 @@ TEST(ChecktransactionTests, BadTxnsVpubOldNegative) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_old-negative", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_old-negative", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -374,7 +377,7 @@ TEST(ChecktransactionTests, BadTxnsVpubNewNegative) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_new-negative", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_new-negative", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -385,7 +388,7 @@ TEST(ChecktransactionTests, BadTxnsVpubOldToolarge) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_old-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_old-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -396,7 +399,7 @@ TEST(ChecktransactionTests, BadTxnsVpubNewToolarge) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_new-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_new-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -408,7 +411,7 @@ TEST(ChecktransactionTests, BadTxnsVpubsBothNonzero) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpubs-both-nonzero", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpubs-both-nonzero", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -420,7 +423,7 @@ TEST(ChecktransactionTests, BadTxnsInputsDuplicate) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -432,7 +435,7 @@ TEST(ChecktransactionTests, BadJoinsplitsNullifiersDuplicateSameJoinsplit) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -444,7 +447,7 @@ TEST(ChecktransactionTests, BadJoinsplitsNullifiersDuplicateDifferentJoinsplit) CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -460,7 +463,7 @@ TEST(ChecktransactionTests, BadCbHasJoinsplits) { EXPECT_TRUE(tx.IsCoinBase()); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-has-joinsplits", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-has-joinsplits", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -476,7 +479,7 @@ TEST(ChecktransactionTests, BadCbEmptyScriptsig) { EXPECT_TRUE(tx.IsCoinBase()); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-length", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-length", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -488,7 +491,7 @@ TEST(ChecktransactionTests, BadTxnsPrevoutNull) { EXPECT_FALSE(tx.IsCoinBase()); MockCValidationState state; - EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null", false)).Times(1); + EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -502,9 +505,9 @@ TEST(ChecktransactionTests, BadTxnsInvalidJoinsplitSignature) { MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 - EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); + EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1); ContextualCheckTransaction(tx, state, chainparams, 0, 100, [](const CChainParams&) { return true; }); - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1); ContextualCheckTransaction(tx, state, chainparams, 0, 100, [](const CChainParams&) { return false; }); } @@ -539,9 +542,9 @@ TEST(ChecktransactionTests, NonCanonicalEd25519Signature) { MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 - EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); + EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1); ContextualCheckTransaction(tx, state, chainparams, 0, 100, [](const CChainParams&) { return true; }); - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false, "")).Times(1); ContextualCheckTransaction(tx, state, chainparams, 0, 100, [](const CChainParams&) { return false; }); } @@ -676,7 +679,7 @@ TEST(ChecktransactionTests, OverwinterExpiryHeight) { mtx.nExpiryHeight = TX_EXPIRY_HEIGHT_THRESHOLD; CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-expiry-height-too-high", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-expiry-height-too-high", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -684,7 +687,7 @@ TEST(ChecktransactionTests, OverwinterExpiryHeight) { mtx.nExpiryHeight = std::numeric_limits::max(); CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-expiry-height-too-high", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-expiry-height-too-high", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } } @@ -708,7 +711,7 @@ TEST(ChecktransactionTests, SproutTxVersionTooLow) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -769,7 +772,7 @@ TEST(ChecktransactionTests, SaplingSproutInputSumsTooLarge) { { UNSAFE_CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txintotal-toolarge", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txintotal-toolarge", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } } @@ -785,7 +788,7 @@ TEST(ChecktransactionTests, OverwinterVersionNumberLow) { UNSAFE_CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-low", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-low", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -803,7 +806,7 @@ TEST(ChecktransactionTests, OverwinterVersionNumberHigh) { UNSAFE_CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-high", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-high", false, "")).Times(1); ContextualCheckTransaction(tx, state, Params(), 1, 100); // Revert to default @@ -822,7 +825,7 @@ TEST(ChecktransactionTests, OverwinterBadVersionGroupId) { UNSAFE_CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-version-group-id", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-version-group-id", false, "")).Times(1); CheckTransactionWithoutProofVerification(tx, state); } @@ -840,9 +843,9 @@ TEST(ChecktransactionTests, OverwinterNotActive) { CTransaction tx(mtx); MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 - EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1); + EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false, "")).Times(1); ContextualCheckTransaction(tx, state, chainparams, 1, 100, [](const CChainParams&) { return true; }); - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false, "")).Times(1); ContextualCheckTransaction(tx, state, chainparams, 1, 100, [](const CChainParams&) { return false; }); } @@ -859,7 +862,7 @@ TEST(ChecktransactionTests, OverwinterFlagNotSet) { CTransaction tx(mtx); MockCValidationState state; - EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-flag-not-set", false)).Times(1); + EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-flag-not-set", false, "")).Times(1); ContextualCheckTransaction(tx, state, Params(), 1, 100); // Revert to default diff --git a/src/main.cpp b/src/main.cpp index c415e288f..8f272cff8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1072,23 +1072,19 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio static_assert(MAX_BLOCK_SIZE >= MAX_TX_SIZE_AFTER_SAPLING); // sanity static_assert(MAX_TX_SIZE_AFTER_SAPLING > MAX_TX_SIZE_BEFORE_SAPLING); // sanity if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE_AFTER_SAPLING) - return state.DoS(100, error("CheckTransaction(): size limits failed"), - REJECT_INVALID, "bad-txns-oversize"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values CAmount nValueOut = 0; for (const CTxOut& txout : tx.vout) { if (txout.nValue < 0) - return state.DoS(100, error("CheckTransaction(): txout.nValue negative"), - REJECT_INVALID, "bad-txns-vout-negative"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CheckTransaction(): txout.nValue too high"), - REJECT_INVALID, "bad-txns-vout-toolarge"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CheckTransaction(): txout total out of range"), - REJECT_INVALID, "bad-txns-txouttotal-toolarge"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } // Check for non-zero valueBalance when there are no Sapling inputs or outputs @@ -1181,8 +1177,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio for (const CTxIn& txin : tx.vin) { if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CheckTransaction(): duplicate inputs"), - REJECT_INVALID, "bad-txns-inputs-duplicate"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } @@ -1231,15 +1226,13 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio REJECT_INVALID, "bad-cb-has-output-description"); if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CheckTransaction(): coinbase script size"), - REJECT_INVALID, "bad-cb-length"); + return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); } else { for (const CTxIn& txin : tx.vin) if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction(): prevout is null"), - REJECT_INVALID, "bad-txns-prevout-null"); + return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); } return true; @@ -1283,6 +1276,15 @@ CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned return nMinFee; } +/** Convert CValidationState to a human-readable message for logging */ +static std::string FormatStateMessage(const CValidationState &state) +{ + return strprintf("%s%s (code %i)", + state.GetRejectReason(), + state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(), + state.GetRejectCode()); +} + bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector& vHashTxnToUncache) @@ -1301,12 +1303,12 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C auto verifier = ProofVerifier::Strict(); if (!CheckTransaction(tx, state, verifier)) - return error("AcceptToMemoryPool: CheckTransaction failed"); + return false; // DoS level set to 10 to be more forgiving. // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. if (!ContextualCheckTransaction(tx, state, Params(), nextBlockHeight, 10)) { - return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); + return false; } // DoS mitigation: reject transactions expiring soon @@ -1318,15 +1320,12 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), - REJECT_INVALID, "coinbase"); + return state.DoS(100, false, REJECT_INVALID, "coinbase"); // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (Params().RequireStandard() && !IsStandardTx(tx, reason, Params(), nextBlockHeight)) - return state.DoS(0, - error("AcceptToMemoryPool: nonstandard transaction: %s", reason), - REJECT_NONSTANDARD, reason); + return state.DoS(0, false, REJECT_NONSTANDARD, reason); // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't @@ -1337,7 +1336,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) - return false; + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); // Check for conflicts with in-memory transactions { @@ -1348,7 +1347,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now - return false; + return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); } } for (const JSDescription &joinsplit : tx.vJoinSplit) { @@ -1380,7 +1379,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C if (view.HaveCoins(hash)) { if (!fHadTxInCache) vHashTxnToUncache.push_back(hash); - return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known"); + return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known"); } // do all inputs exist? @@ -1392,14 +1391,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) *pfMissingInputs = true; - return false; + return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() } } // are the actual inputs available? if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptToMemoryPool: inputs already spent"), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); + return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent"); // are the joinsplits' and sapling spends' requirements met in tx(valid anchors/nullifiers)? if (!view.HaveShieldedRequirements(tx)) @@ -1417,7 +1415,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // Check for non-standard pay-to-script-hash in inputs if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId)) - return error("AcceptToMemoryPool: nonstandard transaction input"); + return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction @@ -1427,10 +1425,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C unsigned int nSigOps = GetLegacySigOpCount(tx); nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.DoS(0, - error("AcceptToMemoryPool: too many sigops %s, %d > %d", - hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS), - REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); + return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, + strprintf("%d > %d", nSigOps, MAX_STANDARD_TX_SIGOPS)); CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; @@ -1466,8 +1462,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // Don't accept it if it can't get into a block CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); if (fLimitFree && nFees < txMinFee) - return state.DoS(0, error("%s: not enough fees %s, %d < %d", __func__, hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, + strprintf("%d < %d", nFees, txMinFee)); } CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); @@ -1496,14 +1492,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000) - return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction"); LogPrint(BCLog::MEMPOOL, "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } if (nAbsurdFee && nFees > nAbsurdFee) - return state.Invalid(false, REJECT_HIGHFEE, strprintf("absurdly-high-fee %d > %d", nFees, nAbsurdFee)); + return state.Invalid(false, REJECT_HIGHFEE, "absurdly-high-fee", strprintf("%d > %d", nFees, maxTxFee)); // Calculate in-mempool ancestors, up to a limit. CTxMemPool::setEntries setAncestors; @@ -1521,7 +1516,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C PrecomputedTransactionData txdata(tx); if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { - return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); + return false; } // Check again against just the consensus-critical mandatory script @@ -1535,7 +1530,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // can be exploited as a DoS attack. if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { - return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); + return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s", + __func__, hash.ToString(), FormatStateMessage(state)); } { @@ -1982,6 +1978,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state if (state.IsInvalid(nDoS)) { std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); if (it != mapBlockSource.end() && State(it->second)) { + assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; State(it->second)->rejects.push_back(reject); if (nDoS > 0) @@ -2035,7 +2032,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) { - return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); + return false; } return true; } @@ -2053,7 +2050,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) - return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); + return state.Invalid(false, 0, "", "Inputs unavailable"); // are the JoinSplit's requirements met? if (!inputs.HaveShieldedRequirements(tx)) @@ -2070,9 +2067,9 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins if (coins->IsCoinBase()) { // Ensure that coinbases are matured if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { - return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + return state.Invalid(false, + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", + strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight)); } // Ensure that coinbases cannot be spent to transparent outputs @@ -2089,9 +2086,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, error("CheckInputs(): txin values out of range"), - REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } nValueIn += tx.GetShieldedValueIn(); @@ -2100,19 +2095,16 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins REJECT_INVALID, "bad-txns-inputvalues-outofrange"); if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)", - tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), - REJECT_INVALID, "bad-txns-in-belowout"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); // Tally transaction fees CAmount nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs(): %s nTxFee < 0", tx.GetHash().ToString()), - REJECT_INVALID, "bad-txns-fee-negative"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); nFees += nTxFee; if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs(): nFees out of range"), - REJECT_INVALID, "bad-txns-fee-outofrange"); + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); return true; } }// namespace Consensus @@ -2677,7 +2669,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, fCacheResults, txdata[i], chainparams.GetConsensus(), consensusBranchId, nScriptCheckThreads ? &vChecks : NULL)) - return false; + return error("ConnectBlock(): CheckInputs on %s failed with %s", + tx.GetHash().ToString(), FormatStateMessage(state)); control.Add(vChecks); } @@ -3865,7 +3858,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, // Check transactions for (const CTransaction& tx : block.vtx) if (!CheckTransaction(tx, state, verifier)) - return error("%s: CheckTransaction failed", __func__); + return error("%s: CheckTransaction of %s failed with %s", __func__, + tx.GetHash().ToString(), + FormatStateMessage(state)); unsigned int nSigOps = 0; for (const CTransaction& tx : block.vtx) @@ -5702,7 +5697,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, const CInv &inv = vInv[nInv]; boost::this_thread::interruption_point(); - pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); @@ -5733,6 +5727,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { + pfrom->AddInventoryKnown(inv); if (fBlocksOnly) LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id); else if (!fAlreadyHave && !IsInitialBlockDownload(chainparams)) @@ -6000,11 +5995,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS = 0; if (state.IsInvalid(nDoS)) { - LogPrint(BCLog::MEMPOOL, "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), + LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->id, pfrom->cleanSubVer, - state.GetRejectReason()); - pfrom->PushMessage(NetMsgType::REJECT, strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + FormatStateMessage(state)); + if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P + pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } @@ -6123,10 +6119,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CBlock block; vRecv >> block; - CInv inv(MSG_BLOCK, block.GetHash()); - LogPrint(BCLog::NET, "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - - pfrom->AddInventoryKnown(inv); + LogPrint(BCLog::NET, "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id); CValidationState state; // Process all blocks from whitelisted peers, even if not requested, @@ -6137,8 +6130,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL); int nDoS; if (state.IsInvalid(nDoS)) { - pfrom->PushMessage(NetMsgType::REJECT, strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes + pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash()); if (nDoS > 0) { LOCK(cs_main); Misbehaving(pfrom->GetId(), nDoS); diff --git a/src/main.h b/src/main.h index af5152cfc..3b6b75a94 100644 --- a/src/main.h +++ b/src/main.h @@ -555,8 +555,17 @@ int GetSpendHeight(const CCoinsViewCache& inputs); uint64_t CalculateCurrentUsage(); +/** Reject codes greater or equal to this can be returned by AcceptToMemPool + * for transactions, to signal internal conditions. They cannot and should not + * be sent over the P2P network. + */ +static const unsigned int REJECT_INTERNAL = 0x100; /** Too high fee. Can not be triggered by P2P transactions */ static const unsigned int REJECT_HIGHFEE = 0x100; +/** Transaction is already known (either in mempool or blockchain) */ +static const unsigned int REJECT_ALREADY_KNOWN = 0x101; +/** Transaction conflicts with a transaction already known */ +static const unsigned int REJECT_CONFLICT = 0x102; /** * Return a CMutableTransaction with contextual default values based on set of consensus rules at nHeight. The expiryDelta will From 69f7e85e42af7ded0c311bb05137e83ae22e365c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Sun, 1 Sep 2024 12:44:29 +0000 Subject: [PATCH 127/146] ZIP 239 preparations 2 --- qa/rpc-tests/prioritisetransaction.py | 10 +- src/gtest/test_mempool.cpp | 2 +- src/init.cpp | 8 - src/main.cpp | 227 ++++++++++++++------------ src/main.h | 15 +- src/net.cpp | 16 +- src/net.h | 24 ++- src/rpc/blockchain.cpp | 2 + src/rpc/rawtransaction.cpp | 2 +- src/test/mempool_tests.cpp | 155 ------------------ src/test/test_bitcoin.cpp | 2 +- src/test/test_bitcoin.h | 4 +- src/test/txvalidationcache_tests.cpp | 2 +- src/txmempool.cpp | 168 +++++++------------ src/txmempool.h | 89 ++++++---- src/wallet/wallet.cpp | 2 +- 16 files changed, 291 insertions(+), 437 deletions(-) diff --git a/qa/rpc-tests/prioritisetransaction.py b/qa/rpc-tests/prioritisetransaction.py index ec54ec490..f66ed452b 100755 --- a/qa/rpc-tests/prioritisetransaction.py +++ b/qa/rpc-tests/prioritisetransaction.py @@ -6,8 +6,14 @@ import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, initialize_chain_clean, \ - start_node, connect_nodes +from test_framework.util import ( + assert_equal, + connect_nodes, + initialize_chain_clean, + start_node, + sync_blocks, + sync_mempools, +) from test_framework.mininode import COIN import time diff --git a/src/gtest/test_mempool.cpp b/src/gtest/test_mempool.cpp index 715ff7bf3..872ce1924 100644 --- a/src/gtest/test_mempool.cpp +++ b/src/gtest/test_mempool.cpp @@ -95,7 +95,7 @@ TEST(Mempool, PriorityStatsDoNotCrash) { CAmount inChainInputValue; double dPriority = view.GetPriority(tx, nHeight, inChainInputValue); - CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, inChainInputValue, false, SPROUT_BRANCH_ID); + CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, inChainInputValue, false, 0, SPROUT_BRANCH_ID); // Check it does not crash (ie. the death test fails) EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), ""); diff --git a/src/init.cpp b/src/init.cpp index bb9035cbe..1b8499643 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -340,8 +340,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-loadblock=", _("Imports blocks from external blk000??.dat file on startup")); strUsage += HelpMessageOpt("-debuglogfile=", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE)); strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); - strUsage += HelpMessageOpt("-maxmempool=", strprintf(_("Keep the transaction memory pool below megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); - strUsage += HelpMessageOpt("-mempoolexpiry=", strprintf(_("Do not keep transactions in the mempool longer than hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); strUsage += HelpMessageOpt("-par=", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); #ifndef WIN32 @@ -1140,12 +1138,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); - // -mempoollimit limits - int64_t nMempoolSizeLimit = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; - int64_t nMempoolDescendantSizeLimit = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; - if (nMempoolSizeLimit < 0 || nMempoolSizeLimit < nMempoolDescendantSizeLimit * 40) - return InitError(strprintf(_("Error: -maxmempool must be at least %d MB"), GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) / 25)); - // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); if (nScriptCheckThreads <= 0) diff --git a/src/main.cpp b/src/main.cpp index 8f272cff8..48b7ba0db 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1238,18 +1238,6 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return true; } -void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { - int expired = pool.Expire(GetTime() - age); - if (expired != 0) { - LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired); - } - - std::vector vNoSpendsRemaining; - pool.TrimToSize(limit, &vNoSpendsRemaining); - for(const uint256& removed : vNoSpendsRemaining) - pcoinsTip->Uncache(removed); -} - CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree) { uint256 hash = tx.GetHash(); @@ -1286,7 +1274,7 @@ static std::string FormatStateMessage(const CValidationState &state) } bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, + bool* pfMissingInputs, CFeeRate* txFeeRate, const CAmount& nAbsurdFee, std::vector& vHashTxnToUncache) { AssertLockHeld(cs_main); @@ -1449,7 +1437,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // it has passed ContextualCheckInputs and therefore this is correct. auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, consensusBranchId); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, consensusBranchId); unsigned int nSize = entry.GetTxSize(); if (txFeeRate) { *txFeeRate = CFeeRate(nFees, nSize); @@ -1466,11 +1454,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C strprintf("%d < %d", nFees, txMinFee)); } - CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); - if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false); - } else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { - // Require that free transactions have sufficient priority to be mined in the next block. + // Require that free transactions have sufficient priority to be mined in the next block. + if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } @@ -1541,13 +1526,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload(Params())); - // trim mempool and check if tx was trimmed - if (!fOverrideMempoolLimit) { - LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - if (!pool.exists(hash)) - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); - } - // Add memory address index if (fAddressIndex) { pool.addAddressIndex(entry, view); @@ -1617,10 +1595,10 @@ bool GetAddressUnspent(const uint160& addressHash, int type, } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool* pfMissingInputs, CFeeRate* txFeeRate, const CAmount nAbsurdFee) { std::vector vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, txFeeRate, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, txFeeRate, nAbsurdFee, vHashTxToUncache); if (!res) { for (const uint256& hashTx : vHashTxToUncache) pcoinsTip->Uncache(hashTx); @@ -3329,7 +3307,6 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c if (fBlocksDisconnected) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); - LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); } mempool.removeWithoutBranchId( @@ -3475,8 +3452,6 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C } } - LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. BlockMap::iterator it = mapBlockIndex.begin(); @@ -5468,10 +5443,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> nStartingHeight; pfrom->nStartingHeight = nStartingHeight; } - if (!vRecv.empty()) - vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message - else - pfrom->fRelayTxes = true; + { + LOCK(pfrom->cs_filter); + if (!vRecv.empty()) + vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message + else + pfrom->fRelayTxes = true; + } // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) @@ -5896,7 +5874,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx, txFeeRate); vWorkQueue.push_back(inv.hash); - LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", + LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u txn, %u kB)\n", pfrom->id, pfrom->cleanSubVer, tx.GetHash().ToString(), mempool.size(), mempool.DynamicMemoryUsage() / 1000); @@ -6175,38 +6153,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - LOCK2(cs_main, pfrom->cs_filter); - - std::vector vtxid; - mempool.queryHashes(vtxid); - vector vInv; - for (uint256& hash : vtxid) { - CTransaction tx; - bool fInMemPool = mempool.lookup(hash, tx); - if (fInMemPool && IsExpiringSoonTx(tx, currentHeight + 1)) { - continue; - } - - CInv inv(MSG_TX, hash); - if (pfrom->pfilter) { - if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... - if (!pfrom->pfilter->IsRelevantAndUpdate(tx)) continue; - } - if (pfrom->minFeeFilter) { - CFeeRate feeRate; - mempool.lookupFeeRate(hash, feeRate); - LOCK(pfrom->cs_feeFilter); - if (feeRate.GetFeePerK() < pfrom->minFeeFilter) - continue; - } - vInv.push_back(inv); - if (vInv.size() == MAX_INV_SZ) { - pfrom->PushMessage(NetMsgType::INV, vInv); - vInv.clear(); - } - } - if (vInv.size() > 0) - pfrom->PushMessage(NetMsgType::INV, vInv); + LOCK(pfrom->cs_inventory); + pfrom->fSendMempool = true; } @@ -6311,6 +6259,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CBloomFilter filter; vRecv >> filter; + LOCK(pfrom->cs_filter); + if (!filter.IsWithinSizeConstraints()) { // There is no excuse for sending a too-large filter @@ -6319,7 +6269,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { - LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); pfrom->fRelayTxes = true; @@ -6535,6 +6484,22 @@ bool ProcessMessages(CNode* pfrom) return fOk; } +class CompareInvMempoolOrder +{ + CTxMemPool *mp; +public: + CompareInvMempoolOrder(CTxMemPool *mempool) + { + mp = mempool; + } + + bool operator()(std::set::iterator a, std::set::iterator b) + { + /* As std::make_heap produces a max-heap, we want the entries with the + * highest feerate to sort later. */ + return mp->CompareDepthAndScore(*b, *a); + } +}; bool SendMessages(CNode* pto) { @@ -6761,49 +6726,109 @@ bool SendMessages(CNode* pto) // Message: inventory // vector vInv; - vector vInvWait; { + LOCK(pto->cs_inventory); + vInv.reserve(std::max(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX)); + + // Add blocks + for (const uint256& hash : pto->vInventoryBlockToSend) { + vInv.push_back(CInv(MSG_BLOCK, hash)); + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage("inv", vInv); + vInv.clear(); + } + } + pto->vInventoryBlockToSend.clear(); + + // Check whether periodic sends should happen bool fSendTrickle = pto->fWhitelisted; if (pto->nNextInvSend < nNow) { fSendTrickle = true; - pto->nNextInvSend = PoissonNextSend(nNow, AVG_INVENTORY_BROADCAST_INTERVAL); + // Use half the delay for outbound peers, as there is less privacy concern for them. + pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound); } - LOCK(pto->cs_inventory); - vInv.reserve(std::min(1000, pto->vInventoryToSend.size())); - vInvWait.reserve(pto->vInventoryToSend.size()); - for (const CInv& inv : pto->vInventoryToSend) - { - if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash)) - continue; - // trickle out tx inv to protect privacy - if (inv.type == MSG_TX && !fSendTrickle) - { - // 1/4 of tx invs blast to all immediately - static uint256 hashSalt; - if (hashSalt.IsNull()) - hashSalt = GetRandHash(); - uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt)); - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - bool fTrickleWait = ((UintToArith256(hashRand) & 3) != 0); - - if (fTrickleWait) - { - vInvWait.push_back(inv); + // Time to send but the peer has requested we not relay transactions. + if (fSendTrickle) { + LOCK(pto->cs_filter); + if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear(); + } + + // Respond to BIP35 mempool requests + if (fSendTrickle && pto->fSendMempool) { + std::vector vtxid; + mempool.queryHashes(vtxid); + pto->fSendMempool = false; + + LOCK(pto->cs_filter); + + int currentHeight = GetHeight(); + for (const uint256& hash : vtxid) { + CTransaction tx; + bool fInMemPool = mempool.lookup(hash, tx); + if (fInMemPool && IsExpiringSoonTx(tx, currentHeight + 1)) { continue; } - } - pto->filterInventoryKnown.insert(inv.hash); + CInv inv(MSG_TX, hash); + pto->setInventoryTxToSend.erase(hash); + if (pto->pfilter) { + if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... + if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; + } + pto->filterInventoryKnown.insert(hash); + vInv.push_back(inv); + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage("inv", vInv); + vInv.clear(); + } + } + } - vInv.push_back(inv); - if (vInv.size() >= 1000) - { - pto->PushMessage(NetMsgType::INV, vInv); - vInv.clear(); + // Determine transactions to relay + if (fSendTrickle) { + // Produce a vector with all candidates for sending + vector::iterator> vInvTx; + vInvTx.reserve(pto->setInventoryTxToSend.size()); + for (std::set::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) { + vInvTx.push_back(it); + } + // Sort the inventory we send for privacy and priority reasons. + // A heap is used so that not all items need sorting if only a few are being sent. + CompareInvMempoolOrder compareInvMempoolOrder(&mempool); + std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); + // No reason to drain out at many times the network's capacity, + // especially since we have many peers and some will draw much shorter delays. + unsigned int nRelayedTransactions = 0; + LOCK(pto->cs_filter); + while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) { + // Fetch the top element from the heap + std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); + std::set::iterator it = vInvTx.back(); + vInvTx.pop_back(); + uint256 hash = *it; + // Remove it from the to-be-sent set + pto->setInventoryTxToSend.erase(it); + // Check if not in the filter already + if (pto->filterInventoryKnown.contains(hash)) { + continue; + } + // Not in the mempool anymore? don't bother sending it. + if (pto->pfilter) { + CTransaction tx; + if (!mempool.lookup(hash, tx)) continue; + if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; + } + // Send + vInv.push_back(CInv(MSG_TX, hash)); + nRelayedTransactions++; + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage("inv", vInv); + vInv.clear(); + } + pto->filterInventoryKnown.insert(hash); } } - pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) pto->PushMessage(NetMsgType::INV, vInv); @@ -6883,7 +6908,7 @@ bool SendMessages(CNode* pto) // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay if (pto->nVersion >= FEEFILTER_VERSION && GetBoolArg("-feefilter", DEFAULT_FEEFILTER) && !(pto->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) { - CAmount currentFilter = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); + CAmount currentFilter = ::minRelayTxFee.GetFeePerK(); int64_t timeNow = GetTimeMicros(); if (timeNow > pto->nextSendTimeFeeFilter) { CAmount filterToSend = filterRounder.round(currentFilter); diff --git a/src/main.h b/src/main.h index 3b6b75a94..dfae6ff55 100644 --- a/src/main.h +++ b/src/main.h @@ -79,10 +79,6 @@ static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 900; static const unsigned int DEFAULT_DESCENDANT_LIMIT = 1000; /** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */ static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 2500; -/** Default for -maxmempool, maximum megabytes of mempool memory usage */ -static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; -/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */ -static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 72; /** Default for -txexpirydelta, in number of blocks */ static const unsigned int DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA = 20; static const unsigned int DEFAULT_POST_BLOSSOM_TX_EXPIRY_DELTA = DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA * Consensus::BLOSSOM_POW_TARGET_SPACING_RATIO; @@ -125,9 +121,12 @@ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 24 * 60; /** Average delay between peer address broadcasts in seconds. */ static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30; -/** Average delay between trickled inventory broadcasts in seconds. - * Blocks, whitelisted receivers, and a random 25% of transactions bypass this. */ -static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5; +/** Average delay between trickled inventory transmissions in seconds. + * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */ +static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5; +/** Maximum number of inventory items to send per transmission. + * Limits the impact of low-fee transaction floods. */ +static const unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL; /** Average delay between feefilter broadcasts in seconds. */ static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; /** Maximum feefilter broadcast delay after significant change. */ @@ -334,7 +333,7 @@ void PruneAndFlush(); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, CFeeRate* txFeeRate, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); + bool* pfMissingInputs, CFeeRate* txFeeRate, const CAmount nAbsurdFee=0); /** Find block at height in a fork **/ const CBlockIndex* FindBlockAtHeight(int nHeight, const CBlockIndex* pIndex); diff --git a/src/net.cpp b/src/net.cpp index 31f00217e..92a0bba9e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2050,20 +2050,7 @@ void RelayTransaction(const CTransaction& tx, CFeeRate feerate, const CDataStrea LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if(!pnode->fRelayTxes) - continue; - { - LOCK(pnode->cs_feeFilter); - if (feerate.GetFeePerK() < pnode->minFeeFilter) - continue; - } - LOCK(pnode->cs_filter); - if (pnode->pfilter) - { - if (pnode->pfilter->IsRelevantAndUpdate(tx)) - pnode->PushInventory(inv); - } else - pnode->PushInventory(inv); + pnode->PushInventory(inv); } } @@ -2247,6 +2234,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa hashContinue = uint256(); nStartingHeight = -1; filterInventoryKnown.reset(); + fSendMempool = false; fGetAddr = false; nNextLocalAddrSend = 0; nNextAddrSend = 0; diff --git a/src/net.h b/src/net.h index 75c856fe3..0552a7565 100644 --- a/src/net.h +++ b/src/net.h @@ -186,7 +186,7 @@ class CNodeStats public: NodeId nodeid; uint64_t nServices; - bool fRelayTxes; + bool fRelayTxes; //protected by cs_filter int64_t nLastSend; int64_t nLastRecv; int64_t nTimeConnected; @@ -333,11 +333,19 @@ class CNode // inventory based relay CRollingBloomFilter filterInventoryKnown; - std::vector vInventoryToSend; + // Set of transaction ids we still have to announce. + // They are sorted by the mempool before relay, so the order is not important. + std::set setInventoryTxToSend; + // List of block ids we still have announce. + // There is no final sorting before sending, as they are always sent immediately + // and in the order requested. + std::vector vInventoryBlockToSend; CCriticalSection cs_inventory; std::set setAskFor; std::multimap mapAskFor; int64_t nNextInvSend; + // Used for BIP35 mempool sending, also protected by cs_inventory + bool fSendMempool; // Used for headers announcements - unfiltered blocks to relay // Also protected by cs_inventory std::vector vBlockHashesToAnnounce; @@ -464,11 +472,13 @@ class CNode void PushInventory(const CInv& inv) { - { - LOCK(cs_inventory); - if (inv.type == MSG_TX && filterInventoryKnown.contains(inv.hash)) - return; - vInventoryToSend.push_back(inv); + LOCK(cs_inventory); + if (inv.type == MSG_TX) { + if (!filterInventoryKnown.contains(inv.hash)) { + setInventoryTxToSend.insert(inv.hash); + } + } else if (inv.type == MSG_BLOCK) { + vInventoryBlockToSend.push_back(inv.hash); } } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a09690c80..d0e481ccc 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -331,6 +331,7 @@ UniValue mempoolToJSON(bool fVerbose = false) UniValue info(UniValue::VOBJ); info.pushKV("size", (int)e.GetTxSize()); info.pushKV("fee", ValueFromAmount(e.GetFee())); + info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); info.pushKV("time", e.GetTime()); info.pushKV("height", (int)e.GetHeight()); info.pushKV("startingpriority", e.GetPriority(e.GetHeight())); @@ -388,6 +389,7 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) " \"transactionid\" : { (json object)\n" " \"size\" : n, (numeric) transaction size in bytes\n" " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" + " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when transaction entered pool\n" " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index be90d1754..e416ae7ed 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1101,7 +1101,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, &txFeeRate, false, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, &txFeeRate, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 4ec84f3b0..2bdfccfaa 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -287,161 +287,6 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) CheckSort(pool, snapshotOrder); } -BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) -{ - CTxMemPool pool(CFeeRate(1000)); - TestMemPoolEntryHelper entry; - entry.dPriority = 10.0; - - CMutableTransaction tx1 = CMutableTransaction(); - tx1.vin.resize(1); - tx1.vin[0].scriptSig = CScript() << OP_1; - tx1.vout.resize(1); - tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; - tx1.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1, &pool)); - - CMutableTransaction tx2 = CMutableTransaction(); - tx2.vin.resize(1); - tx2.vin[0].scriptSig = CScript() << OP_2; - tx2.vout.resize(1); - tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; - tx2.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx2.GetHash(), entry.Fee(5000LL).FromTx(tx2, &pool)); - - pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing - BOOST_CHECK(pool.exists(tx1.GetHash())); - BOOST_CHECK(pool.exists(tx2.GetHash())); - - pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction - BOOST_CHECK(pool.exists(tx1.GetHash())); - BOOST_CHECK(!pool.exists(tx2.GetHash())); - - pool.addUnchecked(tx2.GetHash(), entry.FromTx(tx2, &pool)); - CMutableTransaction tx3 = CMutableTransaction(); - tx3.vin.resize(1); - tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0); - tx3.vin[0].scriptSig = CScript() << OP_2; - tx3.vout.resize(1); - tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; - tx3.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx3.GetHash(), entry.Fee(20000LL).FromTx(tx3, &pool)); - - pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP) - BOOST_CHECK(!pool.exists(tx1.GetHash())); - BOOST_CHECK(pool.exists(tx2.GetHash())); - BOOST_CHECK(pool.exists(tx3.GetHash())); - - pool.TrimToSize(::GetSerializeSize(CTransaction(tx1), SER_NETWORK, PROTOCOL_VERSION)); // mempool is limited to tx1's size in memory usage, so nothing fits - BOOST_CHECK(!pool.exists(tx1.GetHash())); - BOOST_CHECK(!pool.exists(tx2.GetHash())); - BOOST_CHECK(!pool.exists(tx3.GetHash())); - - CFeeRate maxFeeRateRemoved(25000, ::GetSerializeSize(CTransaction(tx3), SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize(CTransaction(tx2), SER_NETWORK, PROTOCOL_VERSION)); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); - - CMutableTransaction tx4 = CMutableTransaction(); - tx4.vin.resize(2); - tx4.vin[0].prevout.SetNull(); - tx4.vin[0].scriptSig = CScript() << OP_4; - tx4.vin[1].prevout.SetNull(); - tx4.vin[1].scriptSig = CScript() << OP_4; - tx4.vout.resize(2); - tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL; - tx4.vout[0].nValue = 10 * COIN; - tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL; - tx4.vout[1].nValue = 10 * COIN; - - CMutableTransaction tx5 = CMutableTransaction(); - tx5.vin.resize(2); - tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0); - tx5.vin[0].scriptSig = CScript() << OP_4; - tx5.vin[1].prevout.SetNull(); - tx5.vin[1].scriptSig = CScript() << OP_5; - tx5.vout.resize(2); - tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL; - tx5.vout[0].nValue = 10 * COIN; - tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL; - tx5.vout[1].nValue = 10 * COIN; - - CMutableTransaction tx6 = CMutableTransaction(); - tx6.vin.resize(2); - tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1); - tx6.vin[0].scriptSig = CScript() << OP_4; - tx6.vin[1].prevout.SetNull(); - tx6.vin[1].scriptSig = CScript() << OP_6; - tx6.vout.resize(2); - tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL; - tx6.vout[0].nValue = 10 * COIN; - tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL; - tx6.vout[1].nValue = 10 * COIN; - - CMutableTransaction tx7 = CMutableTransaction(); - tx7.vin.resize(2); - tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0); - tx7.vin[0].scriptSig = CScript() << OP_5; - tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0); - tx7.vin[1].scriptSig = CScript() << OP_6; - tx7.vout.resize(2); - tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; - tx7.vout[0].nValue = 10 * COIN; - tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; - tx7.vout[0].nValue = 10 * COIN; - - pool.addUnchecked(tx4.GetHash(), entry.Fee(7000LL).FromTx(tx4, &pool)); - pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool)); - pool.addUnchecked(tx6.GetHash(), entry.Fee(1100LL).FromTx(tx6, &pool)); - pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool)); - - // we only require this remove, at max, 2 txn, because its not clear what we're really optimizing for aside from that - pool.TrimToSize(pool.DynamicMemoryUsage() - 1); - BOOST_CHECK(pool.exists(tx4.GetHash())); - BOOST_CHECK(pool.exists(tx6.GetHash())); - BOOST_CHECK(!pool.exists(tx7.GetHash())); - - if (!pool.exists(tx5.GetHash())) - pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool)); - pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool)); - - pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7 - BOOST_CHECK(pool.exists(tx4.GetHash())); - BOOST_CHECK(!pool.exists(tx5.GetHash())); - BOOST_CHECK(pool.exists(tx6.GetHash())); - BOOST_CHECK(!pool.exists(tx7.GetHash())); - - pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool)); - pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool)); - - std::vector vtx; - std::list conflicts; - SetMockTime(42); - SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); - // ... we should keep the same min fee until we get a block - pool.removeForBlock(vtx, 1, conflicts); - SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2); - // ... then feerate should drop 1/2 each halflife - - SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4); - // ... with a 1/2 halflife when mempool is < 1/2 its target size - - SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8); - // ... with a 1/4 halflife when mempool is < 1/4 its target size - - SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000); - // ... but feerate should never drop below 1000 - - SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0); - // ... unless it has gone all the way to 0 (after getting past 1000/2) - - SetMockTime(0); -} - BOOST_AUTO_TEST_CASE(RemoveWithoutBranchId) { CTxMemPool pool(CFeeRate(0)); TestMemPoolEntryHelper entry; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index faf5775dd..2db25947b 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -229,7 +229,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPo return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight, hasNoDependencies, inChainValue, - spendsCoinbase, nBranchId); + spendsCoinbase, sigOpCount, nBranchId); } void Shutdown(void* parg) diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index faa7d3b6b..fe329213f 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -76,11 +76,12 @@ struct TestMemPoolEntryHelper unsigned int nHeight; bool hadNoDependencies; bool spendsCoinbase; + unsigned int sigOpCount; uint32_t nBranchId; TestMemPoolEntryHelper() : nFee(0), nTime(0), dPriority(0.0), nHeight(1), - hadNoDependencies(false), spendsCoinbase(false), + hadNoDependencies(false), spendsCoinbase(false), sigOpCount(1), nBranchId(SPROUT_BRANCH_ID) { } CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL); @@ -92,6 +93,7 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; } TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } + TestMemPoolEntryHelper &SigOps(unsigned int _sigops) { sigOpCount = _sigops; return *this; } TestMemPoolEntryHelper &BranchId(uint32_t _branchId) { nBranchId = _branchId; return *this; } }; diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 29f95ddc7..4efe7285a 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -23,7 +23,7 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, tx, false, NULL, NULL, true, 0); + return AcceptToMemoryPool(mempool, state, tx, false, NULL, NULL, 0); } #ifdef ENABLE_MINING diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 81e338b8b..7a3fd4562 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -15,7 +15,6 @@ #include "timedata.h" #include "util.h" #include "utilmoneystr.h" -#include "utiltime.h" #include "validationinterface.h" #include "version.h" @@ -24,15 +23,17 @@ using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _entryPriority, unsigned int _entryHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue, - bool _spendsCoinbase, uint32_t _nBranchId): + bool _spendsCoinbase, unsigned int _sigOps, uint32_t _nBranchId): tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), - spendsCoinbase(_spendsCoinbase), nBranchId(_nBranchId) + spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps), nBranchId(_nBranchId) { nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); nModSize = tx.CalculateModifiedSize(nTxSize); nUsageSize = RecursiveDynamicUsage(tx); + feeDelta = 0; + nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; nFeesWithDescendants = nFee; @@ -314,10 +315,15 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t } } +void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta) +{ + feeDelta = newFeeDelta; +} + CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) : nTransactionsUpdated(0) { - clear(); + _clear(); // unlocked clear // Sanity checks off by default for performance, because otherwise // accepting transactions becomes O(N^2) where N is the number @@ -408,9 +414,18 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, for (const SpendDescription &spendDescription : tx.vShieldedSpend) { mapSaplingNullifiers[spendDescription.nullifier] = &tx; } + + // Update transaction's score for any feeDelta created by PrioritiseTransaction + std::map >::const_iterator pos = mapDeltas.find(hash); + if (pos != mapDeltas.end()) { + const std::pair &deltas = pos->second; + if (deltas.second) { + mapTx.modify(newit, update_fee_delta(deltas.second)); + } + } + nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); - cachedInnerUsage += entry.DynamicMemoryUsage(); minerPolicyEstimator->processTransaction(entry, fCurrentEstimate); return true; @@ -779,8 +794,6 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigned i } // After the txs in the new block have been removed from the mempool, update policy estimates minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate); - lastRollingFeeUpdate = GetTime(); - blockSinceLastRollingFeeBump = true; } /** @@ -805,20 +818,22 @@ void CTxMemPool::removeWithoutBranchId(uint32_t nMemPoolBranchId) } } -void CTxMemPool::clear() +void CTxMemPool::_clear() { - LOCK(cs); mapLinks.clear(); mapTx.clear(); mapNextTx.clear(); totalTxSize = 0; cachedInnerUsage = 0; - lastRollingFeeUpdate = GetTime(); - blockSinceLastRollingFeeBump = false; - rollingMinimumFeeRate = 0; ++nTransactionsUpdated; } +void CTxMemPool::clear() +{ + LOCK(cs); + _clear(); +} + void CTxMemPool::check(const CCoinsViewCache *pcoins) const { if (nCheckFrequency == 0) @@ -990,6 +1005,31 @@ void CTxMemPool::checkNullifiers(ShieldedType type) const } } +bool CTxMemPool::CompareDepthAndScore(const uint256& hasha, const uint256& hashb) +{ + LOCK(cs); + indexed_transaction_set::const_iterator i = mapTx.find(hasha); + if (i == mapTx.end()) return false; + indexed_transaction_set::const_iterator j = mapTx.find(hashb); + if (j == mapTx.end()) return true; + // We don't actually compare by depth here because we haven't backported + // https://github.com/bitcoin/bitcoin/pull/6654 + // + // But the method name is left as-is because renaming it is not worth the + // merge conflicts. + return CompareTxMemPoolEntryByScore()(*i, *j); +} + +namespace { +class DepthAndScoreComparator +{ + CTxMemPool *mp; +public: + DepthAndScoreComparator(CTxMemPool *mempool) : mp(mempool) {} + bool operator()(const uint256& a, const uint256& b) { return mp->CompareDepthAndScore(a, b); } +}; +} + void CTxMemPool::queryHashes(std::vector& vtxid) { vtxid.clear(); @@ -998,6 +1038,8 @@ void CTxMemPool::queryHashes(std::vector& vtxid) vtxid.reserve(mapTx.size()); for (indexed_transaction_set::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) vtxid.push_back(mi->GetTx().GetHash()); + + std::sort(vtxid.begin(), vtxid.end(), DepthAndScoreComparator(this)); } bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const @@ -1072,6 +1114,10 @@ void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string str std::pair &deltas = mapDeltas[hash]; deltas.first += dPriorityDelta; deltas.second += nFeeDelta; + txiter it = mapTx.find(hash); + if (it != mapTx.end()) { + mapTx.modify(it, update_fee_delta(deltas.second)); + } } LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta)); } @@ -1169,9 +1215,9 @@ size_t CTxMemPool::DynamicMemoryUsage() const { size_t total = 0; - // Estimate the overhead of mapTx to be 6 pointers + an allocation, as no exact formula for + // Estimate the overhead of mapTx to be 9 pointers + an allocation, as no exact formula for // boost::multi_index_contained is implemented. - total += memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 6 * sizeof(void*)) * mapTx.size(); + total += memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 9 * sizeof(void*)) * mapTx.size(); // Three metadata maps inherited from Bitcoin Core total += memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks); @@ -1232,22 +1278,6 @@ void CTxMemPool::RemoveStaged(setEntries &stage) { } } -int CTxMemPool::Expire(int64_t time) { - LOCK(cs); - indexed_transaction_set::nth_index<2>::type::iterator it = mapTx.get<2>().begin(); - setEntries toremove; - while (it != mapTx.get<2>().end() && it->GetTime() < time) { - toremove.insert(mapTx.project<0>(it)); - it++; - } - setEntries stage; - for (txiter removeit : toremove) { - CalculateDescendants(removeit, stage); - } - RemoveStaged(stage); - return stage.size(); -} - bool CTxMemPool::addUnchecked(const uint256&hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate) { LOCK(cs); @@ -1293,81 +1323,3 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons assert(it != mapLinks.end()); return it->second.children; } - -CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { - LOCK(cs); - if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0) - return CFeeRate(rollingMinimumFeeRate); - - int64_t time = GetTime(); - if (time > lastRollingFeeUpdate + 10) { - double halflife = ROLLING_FEE_HALFLIFE; - if (DynamicMemoryUsage() < sizelimit / 4) - halflife /= 4; - else if (DynamicMemoryUsage() < sizelimit / 2) - halflife /= 2; - - rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife); - lastRollingFeeUpdate = time; - - if (rollingMinimumFeeRate < minReasonableRelayFee.GetFeePerK() / 2) { - rollingMinimumFeeRate = 0; - return CFeeRate(0); - } - } - return std::max(CFeeRate(rollingMinimumFeeRate), minReasonableRelayFee); -} - -void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { - AssertLockHeld(cs); - if (rate.GetFeePerK() > rollingMinimumFeeRate) { - rollingMinimumFeeRate = rate.GetFeePerK(); - blockSinceLastRollingFeeBump = false; - } -} - -void CTxMemPool::TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRemaining) { - LOCK(cs); - - unsigned nTxnRemoved = 0; - CFeeRate maxFeeRateRemoved(0); - while (DynamicMemoryUsage() > sizelimit) { - indexed_transaction_set::nth_index<1>::type::iterator it = mapTx.get<1>().begin(); - - // We set the new mempool min fee to the feerate of the removed set, plus the - // "minimum reasonable fee rate" (ie some value under which we consider txn - // to have 0 fee). This way, we don't allow txn to enter mempool with feerate - // equal to txn which were removed with no block in between. - CFeeRate removed(it->GetFeesWithDescendants(), it->GetSizeWithDescendants()); - removed += minReasonableRelayFee; - trackPackageRemoved(removed); - maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed); - - setEntries stage; - CalculateDescendants(mapTx.project<0>(it), stage); - nTxnRemoved += stage.size(); - - std::vector txn; - if (pvNoSpendsRemaining) { - txn.reserve(stage.size()); - for(txiter it : stage) - txn.push_back(it->GetTx()); - } - RemoveStaged(stage); - if (pvNoSpendsRemaining) { - for (const CTransaction& tx : txn) { - for (const CTxIn& txin : tx.vin) { - if (exists(txin.prevout.hash)) - continue; - std::map::iterator it = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0)); - if (it == mapNextTx.end() || it->first.hash != txin.prevout.hash) - pvNoSpendsRemaining->push_back(txin.prevout.hash); - } - } - } - } - - if (maxFeeRateRemoved > CFeeRate(0)) { - LogPrint(BCLog::MEMPOOL, "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString()); - } -} diff --git a/src/txmempool.h b/src/txmempool.h index b4ce4bffc..087ef090c 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -73,6 +73,8 @@ class CTxMemPoolEntry bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain bool spendsCoinbase; //!< keep track of transactions that spend a coinbase + unsigned int sigOpCount; //!< Legacy sig ops plus P2SH sig op count + int64_t feeDelta; //!< Used for determining the priority of the transaction for mining in a block uint32_t nBranchId; //!< Branch ID this transaction is known to commit to, cached for efficiency // Information about descendants of this transaction that are in the @@ -87,8 +89,8 @@ class CTxMemPoolEntry public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _entryPriority, unsigned int _entryHeight, - bool poolHasNoInputsOf, CAmount _inChainInputValue, - bool spendsCoinbase, uint32_t nBranchId); + bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase, + unsigned int nSigOps, uint32_t nBranchId); CTxMemPoolEntry(const CTxMemPoolEntry& other); const CTransaction& GetTx() const { return this->tx; } @@ -101,10 +103,15 @@ class CTxMemPoolEntry size_t GetTxSize() const { return nTxSize; } int64_t GetTime() const { return nTime; } unsigned int GetHeight() const { return entryHeight; } + unsigned int GetSigOpCount() const { return sigOpCount; } + int64_t GetModifiedFee() const { return nFee + feeDelta; } bool WasClearAtEntry() const { return hadNoDependencies; } size_t DynamicMemoryUsage() const { return nUsageSize; } + // Updates the fee delta used for mining priority score + void UpdateFeeDelta(int64_t feeDelta); + // Adjusts the descendant state, if this entry is not dirty. void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount); @@ -123,6 +130,16 @@ class CTxMemPoolEntry uint32_t GetValidatedBranchId() const { return nBranchId; } }; +struct update_fee_delta +{ + update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { } + + void operator() (CTxMemPoolEntry &e) { e.UpdateFeeDelta(feeDelta); } + +private: + int64_t feeDelta; +}; + // Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index. struct update_descendant_state { @@ -192,6 +209,24 @@ class CompareTxMemPoolEntryByFee } }; +/** \class CompareTxMemPoolEntryByScore + * + * Sort by score of entry ((fee+delta)/size) in descending order + */ +class CompareTxMemPoolEntryByScore +{ +public: + bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const + { + double f1 = (double)a.GetModifiedFee() * b.GetTxSize(); + double f2 = (double)b.GetModifiedFee() * a.GetTxSize(); + if (f1 == f2) { + return b.GetTx().GetHash() < a.GetTx().GetHash(); + } + return f1 > f2; + } +}; + class CompareTxMemPoolEntryByEntryTime { public: @@ -229,10 +264,11 @@ class CInPoint * * CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping: * - * mapTx is a boost::multi_index that sorts the mempool on 3 criteria: + * mapTx is a boost::multi_index that sorts the mempool on 4 criteria: * - transaction hash * - feerate [we use max(feerate of tx, feerate of tx with all descendants)] * - time in mempool + * - mining score (feerate modified by any fee deltas from PrioritiseTransaction) * * Note: the term "descendant" refers to in-mempool transactions that depend on * this one, while "ancestor" refers to in-mempool transactions that a given @@ -303,14 +339,6 @@ class CTxMemPool uint64_t totalTxSize = 0; //!< sum of all mempool tx' byte sizes uint64_t cachedInnerUsage; //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves) - CFeeRate minReasonableRelayFee; - - mutable int64_t lastRollingFeeUpdate; - mutable bool blockSinceLastRollingFeeBump; - mutable double rollingMinimumFeeRate; //! minimum fee to get into the pool, decreases exponentially - - void trackPackageRemoved(const CFeeRate& rate); - std::map mapRecentlyAddedTx; uint64_t nRecentlyAddedSequence = 0; uint64_t nNotifiedSequence = 0; @@ -322,6 +350,8 @@ class CTxMemPool void checkNullifiers(ShieldedType type) const; + CFeeRate minReasonableRelayFee; + public: static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing @@ -340,6 +370,11 @@ class CTxMemPool boost::multi_index::ordered_non_unique< boost::multi_index::identity, CompareTxMemPoolEntryByEntryTime + >, + // sorted by score (for mining prioritization) + boost::multi_index::ordered_unique< + boost::multi_index::identity, + CompareTxMemPoolEntryByScore > > > indexed_transaction_set; @@ -424,6 +459,8 @@ class CTxMemPool std::list& conflicts, bool fCurrentEstimate = true); void removeWithoutBranchId(uint32_t nMemPoolBranchId); void clear(); + void _clear(); // unlocked + bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb); void queryHashes(std::vector& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); unsigned int GetTransactionsUpdated() const; @@ -474,23 +511,6 @@ class CTxMemPool void SetNotifiedSequence(uint64_t recentlyAddedSequence); bool IsFullyNotified(); - /** The minimum fee to get into the mempool, which may itself not be enough - * for larger-sized transactions. - * The minReasonableRelayFee constructor arg is used to bound the time it - * takes the fee rate to go back down all the way to 0. When the feerate - * would otherwise be half of this, it is set to 0 instead. - */ - CFeeRate GetMinFee(size_t sizelimit) const; - - /** Remove transactions from the mempool until its dynamic size is <= sizelimit. - * pvNoSpendsRemaining, if set, will be populated with the list of transactions - * which are not in mempool which no longer have any spends in this mempool. - */ - void TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRemaining=NULL); - - /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */ - int Expire(int64_t time); - unsigned long size() { LOCK(cs); @@ -594,4 +614,17 @@ class CCoinsViewMemPool : public CCoinsViewBacked bool HaveCoins(const uint256 &txid) const; }; +// We want to sort transactions by coin age priority +typedef std::pair TxCoinAgePriority; + +struct TxCoinAgePriorityCompare +{ + bool operator()(const TxCoinAgePriority& a, const TxCoinAgePriority& b) + { + if (a.first == b.first) + return CompareTxMemPoolEntryByScore()(*(b.second), *(a.second)); //Reverse order to make sort less than + return a.first < b.first; + } +}; + #endif // BITCOIN_TXMEMPOOL_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9af557d2a..7dbf448e0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5083,7 +5083,7 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee) { CValidationState state; - return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, NULL, false, nAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, NULL, nAbsurdFee); } /** From 5f3f66a7ffa8c1624b73fb0510d31515b320166b Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Sun, 1 Sep 2024 13:48:09 +0000 Subject: [PATCH 128/146] Reduce default fee to 1000 zatoshis --- qa/rpc-tests/mempool_limit.py | 11 +++--- qa/rpc-tests/mempool_resurrect_test.py | 5 +-- qa/rpc-tests/mempool_tx_expiry.py | 10 +++--- qa/rpc-tests/mergetoaddress_helper.py | 28 +++++++-------- qa/rpc-tests/paymentdisclosure.py | 8 ++--- qa/rpc-tests/signrawtransaction_offline.py | 7 ++-- qa/rpc-tests/sprout_sapling_migration.py | 6 ++-- qa/rpc-tests/test_framework/util.py | 3 ++ qa/rpc-tests/wallet.py | 12 ++++--- qa/rpc-tests/wallet_1941.py | 8 ++--- qa/rpc-tests/wallet_anchorfork.py | 6 ++-- qa/rpc-tests/wallet_listnotes.py | 15 ++++---- qa/rpc-tests/wallet_listreceived.py | 13 ++++--- qa/rpc-tests/wallet_nullifiers.py | 12 +++---- qa/rpc-tests/wallet_overwintertx.py | 13 +++---- qa/rpc-tests/wallet_sapling.py | 6 ++-- qa/rpc-tests/wallet_shieldcoinbase.py | 12 +++---- qa/rpc-tests/wallet_shieldingcoinbase.py | 35 ++++++++++--------- qa/rpc-tests/wallet_treestate.py | 14 ++++---- qa/rpc-tests/zkey_import_export.py | 5 +-- src/amount.cpp | 6 ++++ src/amount.h | 9 ++--- src/gtest/test_mempoollimit.cpp | 3 +- src/gtest/test_transaction_builder.cpp | 12 +++---- src/main.cpp | 20 +++++------ src/mempool_limit.cpp | 1 - src/mempool_limit.h | 5 +-- src/policy/fees.h | 2 ++ src/wallet/asyncrpcoperation_mergetoaddress.h | 5 +-- .../asyncrpcoperation_saplingmigration.cpp | 13 ++++--- src/wallet/asyncrpcoperation_sendmany.h | 5 +-- src/wallet/asyncrpcoperation_shieldcoinbase.h | 5 +-- src/wallet/gtest/test_wallet.cpp | 4 +-- src/wallet/rpcwallet.cpp | 12 +++---- src/wallet/rpcwallet.h | 2 ++ src/wallet/test/rpc_wallet_tests.cpp | 14 ++++---- src/wallet/wallet.cpp | 8 ++--- 37 files changed, 187 insertions(+), 168 deletions(-) diff --git a/qa/rpc-tests/mempool_limit.py b/qa/rpc-tests/mempool_limit.py index 918c61414..3c3dc12c6 100644 --- a/qa/rpc-tests/mempool_limit.py +++ b/qa/rpc-tests/mempool_limit.py @@ -13,6 +13,7 @@ initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, + DEFAULT_FEE ) from decimal import Decimal @@ -63,16 +64,16 @@ def run_test(self): zaddr3 = self.nodes[0].z_getnewaddress('sapling') print("Filling mempool...") - opid1 = self.nodes[1].z_sendmany(get_coinbase_address(self.nodes[1]), [{"address": zaddr1, "amount": Decimal('9.999')}]) + opid1 = self.nodes[1].z_sendmany(get_coinbase_address(self.nodes[1]), [{"address": zaddr1, "amount": Decimal('10.0') - DEFAULT_FEE}]) wait_and_assert_operationid_status(self.nodes[1], opid1) - opid2 = self.nodes[2].z_sendmany(get_coinbase_address(self.nodes[2]), [{"address": zaddr2, "amount": Decimal('9.999')}]) + opid2 = self.nodes[2].z_sendmany(get_coinbase_address(self.nodes[2]), [{"address": zaddr2, "amount": Decimal('10.0') - DEFAULT_FEE}]) wait_and_assert_operationid_status(self.nodes[2], opid2) self.sync_all() self.check_mempool_sizes(2) print("Adding one more transaction...") - opid3 = self.nodes[3].z_sendmany(get_coinbase_address(self.nodes[3]), [{"address": zaddr3, "amount": Decimal('9.999')}]) + opid3 = self.nodes[3].z_sendmany(get_coinbase_address(self.nodes[3]), [{"address": zaddr3, "amount": Decimal('10.0') - DEFAULT_FEE}]) wait_and_assert_operationid_status(self.nodes[3], opid3) # The mempools are no longer guaranteed to be in a consistent state, so we cannot sync sleep(5) @@ -90,9 +91,9 @@ def run_test(self): print("Checking mempool size reset after block mined...") self.check_mempool_sizes(0) zaddr4 = self.nodes[0].z_getnewaddress('sapling') - opid4 = self.nodes[0].z_sendmany(zaddr1, [{"address": zaddr4, "amount": Decimal('9.998')}]) + opid4 = self.nodes[0].z_sendmany(zaddr1, [{"address": zaddr4, "amount": Decimal('10.0') - 2*DEFAULT_FEE}]) wait_and_assert_operationid_status(self.nodes[0], opid4) - opid5 = self.nodes[0].z_sendmany(zaddr2, [{"address": zaddr4, "amount": Decimal('9.998')}]) + opid5 = self.nodes[0].z_sendmany(zaddr2, [{"address": zaddr4, "amount": Decimal('10.0') - 2*DEFAULT_FEE}]) wait_and_assert_operationid_status(self.nodes[0], opid5) self.sync_all() diff --git a/qa/rpc-tests/mempool_resurrect_test.py b/qa/rpc-tests/mempool_resurrect_test.py index dac7d199f..95155b60c 100755 --- a/qa/rpc-tests/mempool_resurrect_test.py +++ b/qa/rpc-tests/mempool_resurrect_test.py @@ -11,8 +11,9 @@ import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, start_node +from test_framework.util import assert_equal, start_node, DEFAULT_FEE +from decimal import Decimal # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): @@ -52,7 +53,7 @@ def run_test(self): blocks = [] blocks.extend(self.nodes[0].generate(1)) - spends2_raw = [ self.create_tx(txid, node0_address, 12499.999) for txid in spends1_id ] + spends2_raw = [ self.create_tx(txid, node0_address, Decimal('10.0') - DEFAULT_FEE) for txid in spends1_id ] spends2_id = [ self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw ] blocks.extend(self.nodes[0].generate(1)) diff --git a/qa/rpc-tests/mempool_tx_expiry.py b/qa/rpc-tests/mempool_tx_expiry.py index 04e24be7c..e77f52006 100755 --- a/qa/rpc-tests/mempool_tx_expiry.py +++ b/qa/rpc-tests/mempool_tx_expiry.py @@ -13,7 +13,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, \ connect_nodes_bi, sync_blocks, start_nodes, \ - wait_and_assert_operationid_status + wait_and_assert_operationid_status, DEFAULT_FEE from decimal import Decimal @@ -77,7 +77,7 @@ def run_test(self): assert_equal(set(self.nodes[2].getrawmempool()), set()) ## Shield one of Alice's coinbase funds to her zaddr - res = self.nodes[0].z_shieldcoinbase("*", z_alice, 0.0001, 1) + res = self.nodes[0].z_shieldcoinbase("*", z_alice, DEFAULT_FEE, 1) wait_and_assert_operationid_status(self.nodes[0], res['opid']) self.nodes[0].generate(1) self.sync_all() @@ -85,14 +85,14 @@ def run_test(self): # Get balance on node 0 bal = self.nodes[0].z_gettotalbalance() print "Balance before zsend, after shielding 10: ", bal - assert_equal(Decimal(bal["private"]), Decimal("9.9999")) + assert_equal(Decimal(bal["private"]), Decimal('10.0') - DEFAULT_FEE) print "Splitting network..." self.split_network() # Create transactions blockheight = self.nodes[0].getblockchaininfo()['blocks'] - zsendamount = Decimal('1.0') - Decimal('0.0001') + zsendamount = Decimal('1.0') - DEFAULT_FEE recipients = [] recipients.append({"address": z_bob, "amount": zsendamount}) myopid = self.nodes[0].z_sendmany(z_alice, recipients) @@ -218,7 +218,7 @@ def run_test(self): print "Ensure balance of node 0 is correct" bal = self.nodes[0].z_gettotalbalance() print "Balance after expire_shielded has expired: ", bal - assert_equal(Decimal(bal["private"]), Decimal("7.9999")) + assert_equal(Decimal(bal["private"]), Decimal('8.0') - DEFAULT_FEE) print "Splitting network..." self.split_network() diff --git a/qa/rpc-tests/mergetoaddress_helper.py b/qa/rpc-tests/mergetoaddress_helper.py index 8399ac8bb..92a3bcb78 100755 --- a/qa/rpc-tests/mergetoaddress_helper.py +++ b/qa/rpc-tests/mergetoaddress_helper.py @@ -9,7 +9,7 @@ from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, connect_nodes_bi, fail, \ initialize_chain_clean, start_node, sync_blocks, sync_mempools, \ - wait_and_assert_operationid_status + wait_and_assert_operationid_status, DEFAULT_FEE from decimal import Decimal @@ -161,7 +161,7 @@ def run_test(self, test): "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress", lambda: test.nodes[0].z_mergetoaddress(["ANY_SPROUT", "ANY_SAPLING"], mytaddr)) - # Merge UTXOs from node 0 of value 30, standard fee of 0.00010000 + # Merge UTXOs from node 0 of value 30, default fee result = test.nodes[0].z_mergetoaddress([mytaddr, mytaddr2, mytaddr3], myzaddr) wait_and_assert_operationid_status(test.nodes[0], result['opid']) test.sync_all() @@ -171,7 +171,7 @@ def run_test(self, test): # Confirm balances and that do_not_shield_taddr containing funds of 10 was left alone assert_equal(test.nodes[0].getbalance(), 10) assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) - assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('39.99990000')) + assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('40.0') - DEFAULT_FEE) assert_equal(test.nodes[1].getbalance(), 40) assert_equal(test.nodes[2].getbalance(), 30) @@ -189,7 +189,7 @@ def run_test(self, test): assert_equal(len(test.nodes[0].getblock(blockhash[0])['tx']), 2) assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) - assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('39.99990000')) + assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('40.0') - DEFAULT_FEE) # Shield coinbase UTXOs from any node 2 taddr, and set fee to 0 result = test.nodes[2].z_shieldcoinbase("*", myzaddr, 0) @@ -200,7 +200,7 @@ def run_test(self, test): assert_equal(test.nodes[0].getbalance(), 10) assert_equal(test.nodes[0].z_getbalance(myzaddr), Decimal('30')) - assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('39.99990000')) + assert_equal(test.nodes[0].z_getbalance(myzaddr2), Decimal('40.0') - DEFAULT_FEE) assert_equal(test.nodes[1].getbalance(), 60) assert_equal(test.nodes[2].getbalance(), 0) @@ -211,9 +211,9 @@ def run_test(self, test): test.nodes[1].generate(1) test.sync_all() - assert_equal(test.nodes[0].getbalance(), Decimal('79.99990000')) + assert_equal(test.nodes[0].getbalance(), Decimal('80.0') - DEFAULT_FEE) assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('10.0')) - assert_equal(test.nodes[0].z_getbalance(mytaddr), Decimal('69.99990000')) + assert_equal(test.nodes[0].z_getbalance(mytaddr), Decimal('70.0') - DEFAULT_FEE) assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) assert_equal(test.nodes[0].z_getbalance(myzaddr2), 0) assert_equal(test.nodes[1].getbalance(), 70) @@ -232,8 +232,8 @@ def run_test(self, test): assert_equal(test.nodes[0].z_getbalance(do_not_shield_taddr), 0) assert_equal(test.nodes[0].z_getbalance(mytaddr), 0) assert_equal(test.nodes[0].z_getbalance(myzaddr), 0) - assert_equal(test.nodes[1].getbalance(), Decimal('159.99990000')) - assert_equal(test.nodes[1].z_getbalance(n1taddr), Decimal('79.99990000')) + assert_equal(test.nodes[1].getbalance(), Decimal('160.0') - DEFAULT_FEE) + assert_equal(test.nodes[1].z_getbalance(n1taddr), Decimal('80.0') - DEFAULT_FEE) assert_equal(test.nodes[2].getbalance(), 0) # Generate self.utxos_to_generate regular UTXOs on node 0, and 20 regular UTXOs on node 2 @@ -290,7 +290,7 @@ def run_test(self, test): expected_to_merge = 20 expected_remaining = 0 - result = test.nodes[2].z_mergetoaddress([n2taddr], myzaddr, Decimal('0.0001'), 0) + result = test.nodes[2].z_mergetoaddress([n2taddr], myzaddr, DEFAULT_FEE, 0) assert_equal(result["mergingUTXOs"], expected_to_merge) assert_equal(result["remainingUTXOs"], expected_remaining) assert_equal(result["mergingNotes"], Decimal('0')) @@ -306,7 +306,7 @@ def run_test(self, test): test.nodes[1].sendtoaddress(mytaddr, 1) test.nodes[1].generate(1) test.sync_all() - result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, Decimal('0.0001')) + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, DEFAULT_FEE) assert_equal(result["mergingUTXOs"], Decimal('50')) assert_equal(result["remainingUTXOs"], Decimal('50')) assert_equal(result["mergingNotes"], Decimal('0')) @@ -315,7 +315,7 @@ def run_test(self, test): wait_and_assert_operationid_status(test.nodes[0], result['opid']) # Verify maximum number of UTXOs which node 0 can shield can be set by the limit parameter - result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, Decimal('0.0001'), 33) + result = test.nodes[0].z_mergetoaddress([mytaddr], myzaddr, DEFAULT_FEE, 33) assert_equal(result["mergingUTXOs"], Decimal('33')) assert_equal(result["remainingUTXOs"], Decimal('17')) assert_equal(result["mergingNotes"], Decimal('0')) @@ -331,8 +331,8 @@ def run_test(self, test): # myzaddr will have 5 notes if testing before to Sapling activation and 4 otherwise num_notes = len(test.nodes[0].z_listunspent(0)) - result1 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, 0.0001, 50, 2) - result2 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, 0.0001, 50, 2) + result1 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, DEFAULT_FEE, 50, 2) + result2 = test.nodes[0].z_mergetoaddress([myzaddr], myzaddr, DEFAULT_FEE, 50, 2) # First merge should select from all notes assert_equal(result1["mergingUTXOs"], Decimal('0')) diff --git a/qa/rpc-tests/paymentdisclosure.py b/qa/rpc-tests/paymentdisclosure.py index 4fe106c6e..81c7b92c2 100755 --- a/qa/rpc-tests/paymentdisclosure.py +++ b/qa/rpc-tests/paymentdisclosure.py @@ -9,7 +9,7 @@ from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, initialize_chain_clean, \ start_node, connect_nodes_bi, wait_and_assert_operationid_status, \ - get_coinbase_address + get_coinbase_address, DEFAULT_FEE from decimal import Decimal @@ -69,8 +69,8 @@ def run_test (self): errorString = e.error['message'] assert("No information available about transaction" in errorString) - # Shield coinbase utxos from node 0 of value 50000, standard fee of 0.00010000 - recipients = [{"address":myzaddr, "amount":Decimal('50000.0')-Decimal('0.0001')}] + # Shield coinbase utxos from node 0 of value 40, default fee + recipients = [{"address": myzaddr, "amount": Decimal('40.0') - DEFAULT_FEE}] myopid = self.nodes[0].z_sendmany(mytaddr, recipients) txid = wait_and_assert_operationid_status(self.nodes[0], myopid) @@ -165,7 +165,7 @@ def run_test (self): pd = self.nodes[0].z_getpaymentdisclosure(txid, 0, 1) result = self.nodes[0].z_validatepaymentdisclosure(pd) output_value_sum += Decimal(result["value"]) - assert_equal(output_value_sum, Decimal('49999.99990000')) + assert_equal(output_value_sum, Decimal('40.0') - DEFAULT_FEE) # Create a z->z transaction, sending shielded funds from node 0 to node 1 node1zaddr = self.nodes[1].z_getnewaddress('sprout') diff --git a/qa/rpc-tests/signrawtransaction_offline.py b/qa/rpc-tests/signrawtransaction_offline.py index f2e498815..8e84690ce 100755 --- a/qa/rpc-tests/signrawtransaction_offline.py +++ b/qa/rpc-tests/signrawtransaction_offline.py @@ -3,9 +3,12 @@ import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x." from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_true, initialize_chain_clean, start_node +from test_framework.util import assert_equal, assert_true, initialize_chain_clean, start_node, DEFAULT_FEE from test_framework.authproxy import JSONRPCException +from decimal import Decimal + + class SignOfflineTest (BitcoinTestFramework): # Setup Methods def setup_chain(self): @@ -37,7 +40,7 @@ def run_test(self): create_inputs = [{'txid': txid, 'vout': 0}] sign_inputs = [{'txid': txid, 'vout': 0, 'scriptPubKey': scriptpubkey, 'amount': 10}] - create_hex = self.nodes[0].createrawtransaction(create_inputs, {taddr: 9.9999}) + create_hex = self.nodes[0].createrawtransaction(create_inputs, {taddr: Decimal('10.0') - DEFAULT_FEE}) # An offline regtest node does not rely on the approx release height of the software # to determine the consensus rules to be used for signing. diff --git a/qa/rpc-tests/sprout_sapling_migration.py b/qa/rpc-tests/sprout_sapling_migration.py index 22fd158a6..dbc7cf565 100755 --- a/qa/rpc-tests/sprout_sapling_migration.py +++ b/qa/rpc-tests/sprout_sapling_migration.py @@ -10,7 +10,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_true, get_coinbase_address, \ initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, \ - wait_and_assert_operationid_status_result + wait_and_assert_operationid_status_result, DEFAULT_FEE SAPLING_ADDR = 'zregtestsapling1ssqj3f3majnl270985gqcdqedd9t4nlttjqskccwevj2v20sc25deqspv3masufnwcdy67cydyy' SAPLING_KEY = 'secret-extended-key-regtest1qv62zt2fqyqqpqrh2qzc08h7gncf4447jh9kvnnnhjg959fkwt7mhw9j8e9at7attx8z6u3953u86vcnsujdc2ckdlcmztjt44x3uxpah5mxtncxd0mqcnz9eq8rghh5m4j44ep5d9702sdvvwawqassulktfegrcp4twxgqdxx4eww3lau0mywuaeztpla2cmvagr5nj98elt45zh6fjznadl6wz52n2uyhdwcm2wlsu8fnxstrk6s4t55t8dy6jkgx5g0cwpchh5qffp8x5' @@ -126,7 +126,7 @@ def run_migration_test(self, node, sproutAddr, saplingAddr, target_height): # Check that unmigrated amount + unfinalized = starting balance - fee status = node.z_getmigrationstatus() print("status: {}".format(status)) - assert_equal(Decimal('9.9999'), Decimal(status['unmigrated_amount']) + Decimal(status['unfinalized_migrated_amount'])) + assert_equal(Decimal('10.0') - DEFAULT_FEE, Decimal(status['unmigrated_amount']) + Decimal(status['unfinalized_migrated_amount'])) # The transaction in the mempool should be the one listed in migration_txids, # and it should expire at the next 450 % 500. @@ -145,7 +145,7 @@ def run_migration_test(self, node, sproutAddr, saplingAddr, target_height): print("sprout balance: {}, sapling balance: {}".format(sprout_balance, sapling_balance)) assert_true(sprout_balance < Decimal('10'), "Should have less Sprout funds") assert_true(sapling_balance > Decimal('0'), "Should have more Sapling funds") - assert_true(sprout_balance + sapling_balance, Decimal('9.9999')) + assert_true(sprout_balance + sapling_balance, Decimal('10.0') - DEFAULT_FEE) check_migration_status(node, saplingAddr, DURING_MIGRATION) # At 10 % 500 the transactions will be considered 'finalized' diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index cd30a53db..0c0303414 100755 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -23,6 +23,9 @@ from authproxy import AuthServiceProxy +DEFAULT_FEE = Decimal('0.00001') +DEFAULT_FEE_ZATS = 1000 + def p2p_port(n): return 11000 + n + os.getpid()%999 def rpc_port(n): diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index 7abd1fbde..d94238ac8 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -10,7 +10,7 @@ from test_framework.util import assert_equal, assert_greater_than, \ initialize_chain_clean, start_nodes, start_node, connect_nodes_bi, \ stop_nodes, sync_blocks, sync_mempools, wait_and_assert_operationid_status, \ - wait_bitcoinds + wait_bitcoinds, DEFAULT_FEE from decimal import Decimal @@ -330,7 +330,7 @@ def run_test (self): # check balances zsendmanynotevalue = Decimal('7.0') - zsendmanyfee = Decimal('0.0001') + zsendmanyfee = DEFAULT_FEE node2utxobalance = Decimal('25003.998') - zsendmanynotevalue - zsendmanyfee assert_equal(self.nodes[2].getbalance(), node2utxobalance) @@ -365,8 +365,12 @@ def run_test (self): int(mytxdetails["joinSplitSig"], 16) # hex string # send from private note to node 0 and node 2 - node0balance = self.nodes[0].getbalance() # 25.99794745 - node2balance = self.nodes[2].getbalance() # 16.99790000 + node0balance = self.nodes[0].getbalance() + # The following assertion fails nondeterministically + # assert_equal(node0balance, Decimal('25.99798873')) + node2balance = self.nodes[2].getbalance() + # The following assertion might fail nondeterministically + # assert_equal(node2balance, Decimal('16.99799000')) recipients = [] recipients.append({"address":self.nodes[0].getnewaddress(), "amount":1}) diff --git a/qa/rpc-tests/wallet_1941.py b/qa/rpc-tests/wallet_1941.py index 35d2d908a..b629c8c3e 100755 --- a/qa/rpc-tests/wallet_1941.py +++ b/qa/rpc-tests/wallet_1941.py @@ -11,7 +11,7 @@ from test_framework.util import assert_equal, initialize_chain_clean, \ initialize_datadir, start_nodes, start_node, connect_nodes_bi, \ bitcoind_processes, wait_and_assert_operationid_status, \ - get_coinbase_address + get_coinbase_address, DEFAULT_FEE from decimal import Decimal @@ -55,7 +55,7 @@ def run_test (self): # Send 12500 coins to our zaddr. recipients = [] - recipients.append({"address":myzaddr, "amount":Decimal('12500.0') - Decimal('0.0001')}) + recipients.append({"address":myzaddr, "amount":Decimal('12500.0') - DEFAULT_FEE}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) wait_and_assert_operationid_status(self.nodes[0], myopid) self.nodes[0].generate(1) @@ -71,7 +71,7 @@ def run_test (self): # Confirm the balance on node 0. resp = self.nodes[0].z_getbalance(myzaddr) - assert_equal(Decimal(resp), Decimal('12500.0') - Decimal('0.0001')) + assert_equal(Decimal(resp), Decimal('12500.0') - DEFAULT_FEE}) # Export the key for the zaddr from node 0. key = self.nodes[0].z_exportkey(myzaddr) @@ -98,7 +98,7 @@ def run_test (self): # Confirm that the balance on node 1 is valid now (node 1 must # have rescanned) resp = self.nodes[1].z_getbalance(myzaddr) - assert_equal(Decimal(resp), Decimal('12500.0') - Decimal('0.0001')) + assert_equal(Decimal(resp), Decimal('12500.0') - DEFAULT_FEE}) if __name__ == '__main__': diff --git a/qa/rpc-tests/wallet_anchorfork.py b/qa/rpc-tests/wallet_anchorfork.py index 69d0bcaee..ecaebd05e 100755 --- a/qa/rpc-tests/wallet_anchorfork.py +++ b/qa/rpc-tests/wallet_anchorfork.py @@ -9,7 +9,7 @@ from test_framework.util import assert_equal, initialize_chain_clean, \ start_nodes, stop_nodes, connect_nodes_bi, \ wait_and_assert_operationid_status, wait_bitcoinds, get_coinbase_address, \ - sync_blocks, sync_mempools + sync_blocks, sync_mempools, DEFAULT_FEE from decimal import Decimal class WalletAnchorForkTest (BitcoinTestFramework): @@ -50,7 +50,7 @@ def run_test (self): mytaddr0 = get_coinbase_address(self.nodes[0]) myzaddr0 = self.nodes[0].z_getnewaddress('sprout') recipients = [] - recipients.append({"address":myzaddr0, "amount": Decimal('10.0') - Decimal('0.0001')}) + recipients.append({"address":myzaddr0, "amount": Decimal('10.0') - DEFAULT_FEE}) myopid = self.nodes[0].z_sendmany(mytaddr0, recipients) wait_and_assert_operationid_status(self.nodes[0], myopid) @@ -74,7 +74,7 @@ def run_test (self): # Partition A, node 0 creates a joinsplit transaction recipients = [] - recipients.append({"address":myzaddr0, "amount": Decimal('10.0') - Decimal('0.0001')}) + recipients.append({"address":myzaddr0, "amount": Decimal('10.0') - DEFAULT_FEE}) myopid = self.nodes[0].z_sendmany(mytaddr0, recipients) txid = wait_and_assert_operationid_status(self.nodes[0], myopid) rawhex = self.nodes[0].getrawtransaction(txid) diff --git a/qa/rpc-tests/wallet_listnotes.py b/qa/rpc-tests/wallet_listnotes.py index 169d4be56..1b0c514f3 100755 --- a/qa/rpc-tests/wallet_listnotes.py +++ b/qa/rpc-tests/wallet_listnotes.py @@ -10,6 +10,7 @@ assert_equal, get_coinbase_address, wait_and_assert_operationid_status, + DEFAULT_FEE ) from decimal import Decimal @@ -32,7 +33,7 @@ def run_test(self): assert_equal(201, self.nodes[0].getblockcount()) # Shield coinbase funds (must be a multiple of 10, no change allowed) - receive_amount_10 = Decimal('10.0') - Decimal('0.0001') + receive_amount_10 = Decimal('10.0') - DEFAULT_FEE recipients = [{"address":sproutzaddr, "amount":receive_amount_10}] myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients) txid_1 = wait_and_assert_operationid_status(self.nodes[0], myopid) @@ -67,7 +68,7 @@ def run_test(self): # Send 1.0 (actually 0.9999) from sproutzaddr to a new zaddr sproutzaddr2 = self.nodes[0].z_getnewaddress('sprout') - receive_amount_1 = Decimal('1.0') - Decimal('0.0001') + receive_amount_1 = Decimal('1.0') - DEFAULT_FEE change_amount_9 = receive_amount_10 - Decimal('1.0') assert_equal('sprout', self.nodes[0].z_validateaddress(sproutzaddr2)['type']) recipients = [{"address": sproutzaddr2, "amount":receive_amount_1}] @@ -103,10 +104,10 @@ def run_test(self): # No funds in saplingzaddr yet assert_equal(0, len(self.nodes[0].z_listunspent(0, 9999, False, [saplingzaddr]))) - # Send 0.9999 to our sapling zaddr + # Send 2.0 minus default fee to our sapling zaddr # (sending from a sprout zaddr to a sapling zaddr is disallowed, # so send from coin base) - receive_amount_2 = Decimal('2.0') - Decimal('0.0001') + receive_amount_2 = Decimal('2.0') - DEFAULT_FEE recipients = [{"address": saplingzaddr, "amount":receive_amount_2}] myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients) txid_3 = wait_and_assert_operationid_status(self.nodes[0], myopid) @@ -135,12 +136,12 @@ def run_test(self): assert_equal(sproutzaddr, unspent_tx[2]['address']) assert_equal(change_amount_9, unspent_tx[2]['amount']) - unspent_tx_filter = self.nodes[0].z_listunspent(0, 9999, False, [saplingzaddr]) + unspent_tx_filter = self.nodes[0].z_listunspent(0, 999, False, [saplingzaddr]) assert_equal(1, len(unspent_tx_filter)) assert_equal(unspent_tx[1], unspent_tx_filter[0]) # test that pre- and post-sapling can be filtered in a single call - unspent_tx_filter = self.nodes[0].z_listunspent(0, 9999, False, + unspent_tx_filter = self.nodes[0].z_listunspent(0, 999, False, [sproutzaddr, saplingzaddr]) assert_equal(2, len(unspent_tx_filter)) unspent_tx_filter = sorted(unspent_tx_filter, key=lambda k: k['amount']) @@ -148,7 +149,7 @@ def run_test(self): assert_equal(unspent_tx[2], unspent_tx_filter[1]) # so far, this node has no watchonly addresses, so results are the same - unspent_tx_watchonly = self.nodes[0].z_listunspent(0, 9999, True) + unspent_tx_watchonly = self.nodes[0].z_listunspent(0, 999, True) unspent_tx_watchonly = sorted(unspent_tx_watchonly, key=lambda k: k['amount']) assert_equal(unspent_tx, unspent_tx_watchonly) diff --git a/qa/rpc-tests/wallet_listreceived.py b/qa/rpc-tests/wallet_listreceived.py index a3da3d8ef..ab38bc570 100755 --- a/qa/rpc-tests/wallet_listreceived.py +++ b/qa/rpc-tests/wallet_listreceived.py @@ -7,7 +7,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_true, assert_false -from test_framework.util import wait_and_assert_operationid_status +from test_framework.util import assert_equal, assert_true, assert_false, DEFAULT_FEE, DEFAULT_FEE_ZATS from decimal import Decimal my_memo_str = 'c0ffee' # stay awake @@ -16,8 +16,6 @@ no_memo = 'f6' + ('0'*1022) # see section 5.5 of the protocol spec -fee = Decimal('0.0001') - class ListReceivedTest (BitcoinTestFramework): def generate_and_sync(self, new_height): @@ -178,8 +176,8 @@ def run_test_release(self, release, height): } in outputs) assert({ 'address': zaddr1, - 'value': Decimal('0.3999'), - 'valueZat': 39990000, + 'value': Decimal('0.4') - DEFAULT_FEE, + 'valueZat': 40000000 - DEFAULT_FEE_ZATS, 'memo': no_memo, } in outputs) @@ -189,8 +187,9 @@ def run_test_release(self, release, height): assert_equal(2, len(r), "zaddr1 Should have received 2 notes") assert_equal(txid, r[0]['txid']) - assert_equal(Decimal('0.4')-fee, r[0]['amount']) - assert_true(r[0]['change'], "Note valued at (0.4-fee) should be change") + assert_equal(Decimal('0.4')-DEFAULT_FEE, r[0]['amount']) + assert_equal(40000000-DEFAULT_FEE_ZATS, r[0]['amountZat']) + assert_true(r[0]['change'], "Note valued at (0.4-"+str(DEFAULT_FEE)+") should be change") assert_equal(no_memo, r[0]['memo']) # The old note still exists (it's immutable), even though it is spent diff --git a/qa/rpc-tests/wallet_nullifiers.py b/qa/rpc-tests/wallet_nullifiers.py index f23c60121..f3c3fd70b 100755 --- a/qa/rpc-tests/wallet_nullifiers.py +++ b/qa/rpc-tests/wallet_nullifiers.py @@ -8,7 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_true, bitcoind_processes, \ connect_nodes_bi, start_node, start_nodes, wait_and_assert_operationid_status, \ - get_coinbase_address + get_coinbase_address, DEFAULT_FEE from decimal import Decimal @@ -26,8 +26,8 @@ def run_test (self): # Tests using the default cached chain have one address per coinbase output mytaddr = get_coinbase_address(self.nodes[0]) recipients = [] - recipients.append({"address":myzaddr0, "amount":Decimal('10.0')-Decimal('0.0001')}) # utxo amount less fee - + recipients.append({"address": myzaddr0, "amount": Decimal('10.0') - DEFAULT_FEE}) # utxo amount less fee + wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(mytaddr, recipients), timeout=120) self.sync_all() @@ -54,7 +54,7 @@ def run_test (self): # send node 0 zaddr to note 2 zaddr recipients = [] recipients.append({"address":myzaddr, "amount":7.0}) - + wait_and_assert_operationid_status(self.nodes[0], self.nodes[0].z_sendmany(myzaddr0, recipients), timeout=120) self.sync_all() @@ -81,7 +81,7 @@ def run_test (self): # check zaddr balance zsendmany2notevalue = Decimal('2500.0') - zsendmanyfee = Decimal('0.0001') + zsendmanyfee = DEFAULT_FEE zaddrremaining = zsendmanynotevalue - zsendmany2notevalue - zsendmanyfee assert_equal(self.nodes[3].z_getbalance(myzaddr3), zsendmany2notevalue) assert_equal(self.nodes[2].z_getbalance(myzaddr), zaddrremaining) @@ -99,7 +99,7 @@ def run_test (self): mytaddr1 = self.nodes[1].getnewaddress() recipients = [] recipients.append({"address":mytaddr1, "amount":1250.0}) - + wait_and_assert_operationid_status(self.nodes[1], self.nodes[1].z_sendmany(myzaddr, recipients), timeout=120) self.sync_all() diff --git a/qa/rpc-tests/wallet_overwintertx.py b/qa/rpc-tests/wallet_overwintertx.py index 92b14d78f..90c3a2549 100755 --- a/qa/rpc-tests/wallet_overwintertx.py +++ b/qa/rpc-tests/wallet_overwintertx.py @@ -14,6 +14,7 @@ initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, + DEFAULT_FEE ) from test_framework.authproxy import JSONRPCException @@ -71,8 +72,8 @@ def run_test (self): myopid = self.nodes[2].z_sendmany(taddr2, recipients, 0) txid_zsendmany = wait_and_assert_operationid_status(self.nodes[2], myopid) - # Node 0 shields to Node 2, a coinbase utxo of value 10.0 less fee 0.00010000 - zsendamount = Decimal('10.0') - Decimal('0.0001') + # Node 0 shields to Node 2, a coinbase utxo of value 10.0 less default fee + zsendamount = Decimal('10.0') - DEFAULT_FEE recipients = [] recipients.append({"address":zaddr2, "amount": zsendamount}) myopid = self.nodes[0].z_sendmany(taddr0, recipients) @@ -86,7 +87,7 @@ def run_test (self): # Verify balance assert_equal(self.nodes[1].z_getbalance(taddr1), Decimal('0.5')) - assert_equal(self.nodes[2].getbalance(), Decimal('0.4999')) + assert_equal(self.nodes[2].getbalance(), Decimal('0.5') - DEFAULT_FEE) assert_equal(self.nodes[2].z_getbalance(zaddr2), zsendamount) # Verify transaction version is 4 (intended for Sapling+) @@ -142,8 +143,8 @@ def run_test (self): myopid = self.nodes[3].z_sendmany(taddr3, recipients, 0) txid_zsendmany = wait_and_assert_operationid_status(self.nodes[3], myopid) - # Node 0 shields to Node 3, a coinbase utxo of value 10.0 less fee 0.00010000 - zsendamount = Decimal('10.0') - Decimal('0.0001') + # Node 0 shields to Node 3, a coinbase utxo of value 10.0 less default fee + zsendamount = Decimal('10.0') - DEFAULT_FEE recipients = [] recipients.append({"address":zaddr3, "amount": zsendamount}) myopid = self.nodes[0].z_sendmany(taddr0, recipients) @@ -164,7 +165,7 @@ def run_test (self): # Verify balance assert_equal(self.nodes[1].z_getbalance(taddr1), Decimal('1.0')) - assert_equal(self.nodes[3].getbalance(), Decimal('0.4999')) + assert_equal(self.nodes[3].getbalance(), Decimal('0.5') - DEFAULT_FEE) assert_equal(self.nodes[3].z_getbalance(zaddr3), zsendamount) # Verify transaction version is 4 (intended for Sapling+) diff --git a/qa/rpc-tests/wallet_sapling.py b/qa/rpc-tests/wallet_sapling.py index 161676800..d9c2e7321 100755 --- a/qa/rpc-tests/wallet_sapling.py +++ b/qa/rpc-tests/wallet_sapling.py @@ -12,6 +12,7 @@ get_coinbase_address, start_nodes, wait_and_assert_operationid_status, + DEFAULT_FEE ) from decimal import Decimal @@ -168,8 +169,9 @@ def run_test(self): try: self.nodes[1].z_sendmany( taddr1, - [{'address': node4_sproutaddr, 'amount': 2.5}, {'address': node4_saplingaddr, 'amount': 2.4999}], - 1, 0.0001 + [{'address': node4_sproutaddr, 'amount': Decimal('2.5')}, + {'address': node4_saplingaddr, 'amount': Decimal('2.5') - DEFAULT_FEE}], + 1, DEFAULT_FEE ) raise AssertionError("Should have thrown an exception") except JSONRPCException as e: diff --git a/qa/rpc-tests/wallet_shieldcoinbase.py b/qa/rpc-tests/wallet_shieldcoinbase.py index 2045f201e..6822e413e 100755 --- a/qa/rpc-tests/wallet_shieldcoinbase.py +++ b/qa/rpc-tests/wallet_shieldcoinbase.py @@ -9,7 +9,7 @@ from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, initialize_chain_clean, \ start_node, connect_nodes_bi, sync_blocks, sync_mempools, \ - wait_and_assert_operationid_status, get_coinbase_address + wait_and_assert_operationid_status, get_coinbase_address, DEFAULT_FEE from decimal import Decimal @@ -103,7 +103,7 @@ def run_test (self): errorString = e.error['message'] assert_equal("JSON integer out of range" in errorString, True) - # Shield coinbase utxos from node 0 of value 40, standard fee of 0.00010000 + # Shield coinbase utxos from node 0 of value 40, standard fee result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr) wait_and_assert_operationid_status(self.nodes[0], result['opid']) self.sync_all() @@ -113,7 +113,7 @@ def run_test (self): # Confirm balances and that do_not_shield_taddr containing funds of 10 was left alone assert_equal(self.nodes[0].getbalance(), 12500) assert_equal(self.nodes[0].z_getbalance(do_not_shield_taddr), Decimal('12500.0')) - assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('49999.99990000')) + assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('50000.0') - DEFAULT_FEE) assert_equal(self.nodes[1].getbalance(), 25000) assert_equal(self.nodes[2].getbalance(), 37500) @@ -125,7 +125,7 @@ def run_test (self): self.sync_all() assert_equal(self.nodes[0].getbalance(), 12500) - assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('87499.99990000')) + assert_equal(self.nodes[0].z_getbalance(myzaddr), Decimal('87500.0') - DEFAULT_FEE) assert_equal(self.nodes[1].getbalance(), 37500) assert_equal(self.nodes[2].getbalance(), 0) @@ -172,13 +172,13 @@ def verify_locking(first, second, limit): self.nodes[0].generate(200) self.sync_all() mytaddr = get_coinbase_address(self.nodes[0], 100) - result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, Decimal('0.0001')) + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, DEFAULT_FEE) assert_equal(result["shieldingUTXOs"], Decimal('50')) assert_equal(result["remainingUTXOs"], Decimal('50')) wait_and_assert_operationid_status(self.nodes[0], result['opid']) # Verify maximum number of utxos which node 0 can shield can be set by the limit parameter - result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, Decimal('0.0001'), 33) + result = self.nodes[0].z_shieldcoinbase(mytaddr, myzaddr, DEFAULT_FEE, 33) assert_equal(result["shieldingUTXOs"], Decimal('33')) assert_equal(result["remainingUTXOs"], Decimal('17')) wait_and_assert_operationid_status(self.nodes[0], result['opid']) diff --git a/qa/rpc-tests/wallet_shieldingcoinbase.py b/qa/rpc-tests/wallet_shieldingcoinbase.py index 3d6a0e6e4..a0505b950 100755 --- a/qa/rpc-tests/wallet_shieldingcoinbase.py +++ b/qa/rpc-tests/wallet_shieldingcoinbase.py @@ -9,7 +9,8 @@ from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, initialize_chain_clean, \ start_nodes, connect_nodes_bi, wait_and_assert_operationid_status, \ - wait_and_assert_operationid_status_result, get_coinbase_address + wait_and_assert_operationid_status_result, get_coinbase_address, \ + check_node_log, DEFAULT_FEE import sys import timeit @@ -91,14 +92,14 @@ def run_test (self): recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - error_result = wait_and_assert_operationid_status_result(self.nodes[0], myopid, "failed", ("Change 8.76533211 not allowed. " + error_result = wait_and_assert_operationid_status_result(self.nodes[0], myopid, "failed", ("Change 8.76542211 not allowed. " "When shielding coinbase funds, the wallet does not allow any change " "as there is currently no way to specify a change address in z_sendmany."), 10) # Test that the returned status object contains a params field with the operation's input parameters assert_equal(error_result["method"], "z_sendmany") params = error_result["params"] - assert_equal(params["fee"], Decimal('0.0001')) # default + assert_equal(params["fee"], DEFAULT_FEE) # default assert_equal(params["minconf"], Decimal('1')) # default assert_equal(params["fromaddress"], mytaddr) assert_equal(params["amounts"][0]["address"], myzaddr) @@ -109,7 +110,7 @@ def run_test (self): self.nodes[3].z_importviewingkey(myviewingkey, "no") # This send will succeed. We send two coinbase utxos totalling 20.0 less a fee of 0.00010000, with no change. - shieldvalue = Decimal('25000.0') - Decimal('0.0001') + shieldvalue = Decimal('25000.0') - DEFAULT_FEE recipients = [] recipients.append({"address":myzaddr, "amount": shieldvalue}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) @@ -169,8 +170,8 @@ def run_test (self): # check balances (the z_sendmany consumes 3 coinbase utxos) resp = self.nodes[0].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), Decimal('25000.0')) - assert_equal(Decimal(resp["private"]), Decimal('24999.9999')) - assert_equal(Decimal(resp["total"]), Decimal('49999.9999')) + assert_equal(Decimal(resp["private"]), Decimal('25000.0') - DEFAULT_FEE) + assert_equal(Decimal(resp["total"]), Decimal('50000.0') - DEFAULT_FEE) # The Sprout value pool should reflect the send sproutvalue = shieldvalue @@ -178,7 +179,7 @@ def run_test (self): # A custom fee of 0 is okay. Here the node will send the note value back to itself. recipients = [] - recipients.append({"address":myzaddr, "amount": Decimal('24999.9999')}) + recipients.append({"address":myzaddr, "amount": Decimal('25000.0') - DEFAULT_FEE}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients, 1, Decimal('0.0')) mytxid = wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() @@ -186,8 +187,8 @@ def run_test (self): self.sync_all() resp = self.nodes[0].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), Decimal('25000.0')) - assert_equal(Decimal(resp["private"]), Decimal('24999.9999')) - assert_equal(Decimal(resp["total"]), Decimal('49999.9999')) + assert_equal(Decimal(resp["private"]), Decimal('25000.0') - DEFAULT_FEE) + assert_equal(Decimal(resp["total"]), Decimal('50000.0') - DEFAULT_FEE) # The Sprout value pool should be unchanged check_value_pool(self.nodes[0], 'sprout', sproutvalue) @@ -209,18 +210,18 @@ def run_test (self): self.sync_all() # check balances - sproutvalue -= unshieldvalue + Decimal('0.0001') + sproutvalue -= unshieldvalue + DEFAULT_FEE resp = self.nodes[0].z_gettotalbalance() assert_equal(Decimal(resp["transparent"]), Decimal('37500.0')) - assert_equal(Decimal(resp["private"]), Decimal('12499.9998')) - assert_equal(Decimal(resp["total"]), Decimal('49999.9998')) + assert_equal(Decimal(resp["private"]), Decimal('12500.0') - 2*DEFAULT_FEE) + assert_equal(Decimal(resp["total"]), Decimal('50000.0') - 2*DEFAULT_FEE) check_value_pool(self.nodes[0], 'sprout', sproutvalue) # z_sendmany will return an error if there is transparent change output considered dust. # UTXO selection in z_sendmany sorts in ascending order, so smallest utxos are consumed first. - # At this point in time, unspent notes all have a value of 10.0 and standard z_sendmany fee is 0.0001. + # At this point in time, unspent notes all have a value of 10.0. recipients = [] - amount = Decimal('12500.0') - Decimal('0.00010000') - Decimal('0.00000001') # this leaves change at 1 zatoshi less than dust threshold + amount = Decimal('12500.0') - DEFAULT_FEE - Decimal('0.00000001') # this leaves change at 1 zatoshi less than dust threshold recipients.append({"address":self.nodes[0].getnewaddress(), "amount":amount }) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 12500.00, need 0.00000053 more to avoid creating invalid change output 0.00000001 (dust threshold is 0.00000054)") @@ -237,9 +238,9 @@ def run_test (self): recipients = [] recipients.append({"address":self.nodes[1].getnewaddress(), "amount":Decimal('10000000.0')}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) - wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 12500.00, need 10000000.0001") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient transparent funds, have 12500.00, need 10000000.00001") myopid = self.nodes[0].z_sendmany(myzaddr, recipients) - wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient shielded funds, have 12499.9998, need 10000000.0001") + wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "Insufficient shielded funds, have 12499.99998, need 10000000.00001") # Send will fail because of insufficient funds unless sender uses coinbase utxos try: @@ -290,7 +291,7 @@ def run_test (self): # check balance node2balance = amount_per_recipient * num_t_recipients - sproutvalue -= node2balance + Decimal('0.0001') + sproutvalue -= node2balance + DEFAULT_FEE assert_equal(self.nodes[2].getbalance(), node2balance) check_value_pool(self.nodes[0], 'sprout', sproutvalue) diff --git a/qa/rpc-tests/wallet_treestate.py b/qa/rpc-tests/wallet_treestate.py index a21da4f64..02a372e9c 100755 --- a/qa/rpc-tests/wallet_treestate.py +++ b/qa/rpc-tests/wallet_treestate.py @@ -8,7 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, initialize_chain_clean, \ start_nodes, connect_nodes_bi, wait_and_assert_operationid_status, \ - get_coinbase_address + get_coinbase_address, DEFAULT_FEE import time from decimal import Decimal @@ -39,9 +39,9 @@ def run_test (self): mytaddr = get_coinbase_address(self.nodes[0]) myzaddr = self.nodes[0].z_getnewaddress('sprout') - # Spend coinbase utxos to create three notes of 9.99990000 each + # Spend coinbase utxos to create three notes of 10 BTCZ minus default fee each recipients = [] - recipients.append({"address":myzaddr, "amount":Decimal('12500.0') - Decimal('0.0001')}) + recipients.append({"address": myzaddr, "amount": Decimal('12500.0') - DEFAULT_FEE}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) wait_and_assert_operationid_status(self.nodes[0], myopid) self.sync_all() @@ -60,7 +60,7 @@ def run_test (self): # Check balance resp = self.nodes[0].z_getbalance(myzaddr) - assert_equal(Decimal(resp), Decimal('12499.9999') * 3 ) + assert_equal(Decimal(resp), (Decimal('12500.0') - DEFAULT_FEE) * 3) # We want to test a real-world situation where during the time spent creating a transaction # with joinsplits, other transactions containing joinsplits have been mined into new blocks, @@ -68,15 +68,15 @@ def run_test (self): # Tx 1 will change the treestate while Tx 2 containing chained joinsplits is still being generated recipients = [] - recipients.append({"address":self.nodes[2].z_getnewaddress('sprout'), "amount":Decimal('12500.0') - Decimal('0.0001')}) + recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('12500.0') - DEFAULT_FEE}) myopid = self.nodes[0].z_sendmany(mytaddr, recipients) wait_and_assert_operationid_status(self.nodes[0], myopid) # Tx 2 will consume all three notes, which must take at least two joinsplits. This is regardless of # the z_sendmany implementation because there are only two inputs per joinsplit. recipients = [] - recipients.append({"address":self.nodes[2].z_getnewaddress('sprout'), "amount":Decimal('26000')}) - recipients.append({"address":self.nodes[2].z_getnewaddress('sprout'), "amount":Decimal('11.9997') - Decimal('0.0001')}) + recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('18.0')}) + recipients.append({"address": self.nodes[2].z_getnewaddress(), "amount": Decimal('12.0') - 4*DEFAULT_FEE}) myopid = self.nodes[0].z_sendmany(myzaddr, recipients) # Wait for Tx 2 to begin executing... diff --git a/qa/rpc-tests/zkey_import_export.py b/qa/rpc-tests/zkey_import_export.py index 7d8646be2..ceda2a966 100755 --- a/qa/rpc-tests/zkey_import_export.py +++ b/qa/rpc-tests/zkey_import_export.py @@ -8,13 +8,14 @@ from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_greater_than, start_nodes,\ - initialize_chain_clean, connect_nodes_bi, wait_and_assert_operationid_status + initialize_chain_clean, connect_nodes_bi, wait_and_assert_operationid_status, \ + DEFAULT_FEE import logging logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO) -fee = Decimal('0.0001') # constant (but can be changed within reason) +fee = DEFAULT_FEE # constant (but can be changed within reason) class ZkeyImportExportTest (BitcoinTestFramework): diff --git a/src/amount.cpp b/src/amount.cpp index dd799c85e..354bb6c00 100644 --- a/src/amount.cpp +++ b/src/amount.cpp @@ -4,6 +4,7 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . #include "amount.h" +#include "policy/fees.h" #include "tinyformat.h" @@ -17,6 +18,11 @@ CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nSize) nSatoshisPerK = 0; } +CAmount CFeeRate::GetFeeForRelay(size_t nSize) const +{ + return std::min(GetFee(nSize), DEFAULT_FEE); +} + CAmount CFeeRate::GetFee(size_t nSize) const { CAmount nFee = nSatoshisPerK*nSize / 1000; diff --git a/src/amount.h b/src/amount.h index 88e283d10..54d00b52f 100644 --- a/src/amount.h +++ b/src/amount.h @@ -18,7 +18,7 @@ static const CAmount CENT = 1000000; extern const std::string CURRENCY_UNIT; -/** No amount larger than this (in satoshi) is valid. +/** No amount larger than this (in zatoshi) is valid. * * Note that this constant is *not* the total money supply, which in Bitcoin * currently happens to be less than 21,000,000,000 BTCZ for various reasons, but @@ -36,15 +36,16 @@ inline bool MoneyRange(const CAmount& nValue) { return (nValue >= 0 && nValue <= class CFeeRate { private: - CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes + CAmount nSatoshisPerK; // unit is zatoshis-per-1,000-bytes public: CFeeRate() : nSatoshisPerK(0) { } explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } CFeeRate(const CAmount& nFeePaid, size_t nSize); CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; } - CAmount GetFee(size_t size) const; // unit returned is satoshis - CAmount GetFeePerK() const { return GetFee(1000); } // satoshis-per-1000-bytes + CAmount GetFeeForRelay(size_t size) const; // unit returned is zatoshis + CAmount GetFee(size_t size) const; // unit returned is zatoshis + CAmount GetFeePerK() const { return GetFee(1000); } // zatoshis-per-1000-bytes friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; } friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; } diff --git a/src/gtest/test_mempoollimit.cpp b/src/gtest/test_mempoollimit.cpp index 718b24962..64c4c9716 100644 --- a/src/gtest/test_mempoollimit.cpp +++ b/src/gtest/test_mempoollimit.cpp @@ -135,7 +135,8 @@ TEST(MempoolLimitTests, WeightedTxInfoFromTx) builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 25000, {}); builder.SetFee(9999); - WeightedTxInfo info = WeightedTxInfo::from(builder.Build().GetTxOrThrow(), 9999); + static_assert(DEFAULT_FEE == 1000); + WeightedTxInfo info = WeightedTxInfo::from(builder.Build().GetTxOrThrow(), DEFAULT_FEE-1); EXPECT_EQ(MIN_TX_COST, info.txWeight.cost); EXPECT_EQ(MIN_TX_COST + LOW_FEE_PENALTY, info.txWeight.evictionWeight); } diff --git a/src/gtest/test_transaction_builder.cpp b/src/gtest/test_transaction_builder.cpp index 56a3a2cff..08138dfed 100644 --- a/src/gtest/test_transaction_builder.cpp +++ b/src/gtest/test_transaction_builder.cpp @@ -90,7 +90,7 @@ TEST(TransactionBuilder, TransparentToSapling) auto pk = *ivk.address(d); // Create a shielding transaction from transparent to Sapling - // 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee + // 0.0005 t-ZEC in, 0.0004 z-ZEC out, default fee auto builder = TransactionBuilder(consensusParams, 1, &keystore); builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000); builder.AddSaplingOutput(fvk_from.ovk, pk, 40000, {}); @@ -122,7 +122,7 @@ TEST(TransactionBuilder, SaplingToSapling) { auto testNote = GetTestSaplingNote(pa, 40000); // Create a Sapling-only transaction - // 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change + // 0.0004 z-ZEC in, 0.00025 z-ZEC out, default fee, 0.00005 z-ZEC change auto builder = TransactionBuilder(consensusParams, 2); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); @@ -163,7 +163,7 @@ TEST(TransactionBuilder, SaplingToSprout) { // Create a Sapling-to-Sprout transaction (reusing the note from above) // - 0.0004 Sapling-ZEC in - 0.00025 Sprout-ZEC out // - 0.00005 Sapling-ZEC change - // - 0.0001 t-ZEC fee + // - default t-ZEC fee auto builder = TransactionBuilder(consensusParams, 2, nullptr); builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); builder.AddSproutOutput(sproutAddr, 25000); @@ -298,19 +298,19 @@ TEST(TransactionBuilder, FailsWithNegativeChange) auto testNote = GetTestSaplingNote(pa, 59999); // Fail if there is only a Sapling output - // 0.0005 z-ZEC out, 0.0001 t-ZEC fee + // 0.0005 z-ZEC out, default fee auto builder = TransactionBuilder(consensusParams, 1); builder.AddSaplingOutput(fvk.ovk, pa, 50000, {}); EXPECT_EQ("Change cannot be negative", builder.Build().GetError()); // Fail if there is only a transparent output - // 0.0005 t-ZEC out, 0.0001 t-ZEC fee + // 0.0005 t-ZEC out, default fee builder = TransactionBuilder(consensusParams, 1, &keystore); builder.AddTransparentOutput(taddr, 50000); EXPECT_EQ("Change cannot be negative", builder.Build().GetError()); // Fails if there is insufficient input - // 0.0005 t-ZEC out, 0.0001 t-ZEC fee, 0.00059999 z-ZEC in + // 0.0005 t-ZEC out, default fee, 0.00059999 z-ZEC in builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness()); EXPECT_EQ("Change cannot be negative", builder.Build().GetError()); diff --git a/src/main.cpp b/src/main.cpp index 48b7ba0db..3fb266756 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1247,7 +1247,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned if (dPriorityDelta > 0 || nFeeDelta > 0) return 0; - CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); + CAmount nMinFee = ::minRelayTxFee.GetFeeForRelay(nBytes); if (fAllowFree) { @@ -1443,26 +1443,26 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C *txFeeRate = CFeeRate(nFees, nSize); } - // Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany. - if (tx.vJoinSplit.size() > 0 && nFees >= ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE) { - // In future we will we have more accurate and dynamic computation of fees for tx with joinsplits. - } else { - // Don't accept it if it can't get into a block - CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); - if (fLimitFree && nFees < txMinFee) + // Before zcashd 4.2.0, we had a condition here to always accept a tx if it contained + // JoinSplits and had at least the default fee. It is no longer necessary to treat + // that as a special case, because the fee returned by GetMinRelayFee is always at + // most DEFAULT_FEE. + + CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); + if (fLimitFree && nFees < txMinFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, strprintf("%d < %d", nFees, txMinFee)); } // Require that free transactions have sufficient priority to be mined in the next block. - if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { + if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFeeForRelay(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } // Continuously rate-limit free (really, very-low-fee) transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize)) + if (fLimitFree && nFees < ::minRelayTxFee.GetFeeForRelay(nSize)) { static CCriticalSection csFreeLimiter; static double dFreeCount; diff --git a/src/mempool_limit.cpp b/src/mempool_limit.cpp index 5bf1cebf1..e53f68bc3 100644 --- a/src/mempool_limit.cpp +++ b/src/mempool_limit.cpp @@ -9,7 +9,6 @@ #include "timedata.h" #include "version.h" -const CAmount DEFAULT_FEE = 10000; const TxWeight ZERO_WEIGHT = TxWeight(0, 0); void RecentlyEvictedList::pruneList() diff --git a/src/mempool_limit.h b/src/mempool_limit.h index 991d3133b..f1df13b49 100644 --- a/src/mempool_limit.h +++ b/src/mempool_limit.h @@ -11,6 +11,7 @@ #include #include "primitives/transaction.h" +#include "policy/fees.h" #include "uint256.h" const size_t DEFAULT_MEMPOOL_TOTAL_COST_LIMIT = 80000000; @@ -22,7 +23,7 @@ const uint64_t LOW_FEE_PENALTY = 16000; // This class keeps track of transactions which have been recently evicted from the mempool -// in order to prevent them from being re-accepted for a given amount of time. +// in order to prevent them from being re-accepted for a given amount of time. class RecentlyEvictedList { const size_t capacity; @@ -37,7 +38,7 @@ class RecentlyEvictedList void pruneList(); public: - RecentlyEvictedList(size_t capacity_, int64_t timeToKeep_) : capacity(capacity_), timeToKeep(timeToKeep_) + RecentlyEvictedList(size_t capacity_, int64_t timeToKeep_) : capacity(capacity_), timeToKeep(timeToKeep_) { assert(capacity <= EVICTION_MEMORY_ENTRIES); } diff --git a/src/policy/fees.h b/src/policy/fees.h index 4529941a7..b5498f928 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -13,6 +13,8 @@ #include #include +static const CAmount DEFAULT_FEE = 1000; + class CAutoFile; class CFeeRate; class CTxMemPoolEntry; diff --git a/src/wallet/asyncrpcoperation_mergetoaddress.h b/src/wallet/asyncrpcoperation_mergetoaddress.h index d927f9c92..0a34f0c6f 100644 --- a/src/wallet/asyncrpcoperation_mergetoaddress.h +++ b/src/wallet/asyncrpcoperation_mergetoaddress.h @@ -21,9 +21,6 @@ #include -// Default transaction fee if caller does not specify one. -#define MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE 10000 - using namespace libzcash; // Input UTXO is a tuple of txid, vout, amount, script @@ -63,7 +60,7 @@ class AsyncRPCOperation_mergetoaddress : public AsyncRPCOperation std::vector sproutNoteInputs, std::vector saplingNoteInputs, MergeToAddressRecipient recipient, - CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE, + CAmount fee = DEFAULT_FEE, UniValue contextInfo = NullUniValue); virtual ~AsyncRPCOperation_mergetoaddress(); diff --git a/src/wallet/asyncrpcoperation_saplingmigration.cpp b/src/wallet/asyncrpcoperation_saplingmigration.cpp index 74d3f5ab4..4a5e63b2f 100644 --- a/src/wallet/asyncrpcoperation_saplingmigration.cpp +++ b/src/wallet/asyncrpcoperation_saplingmigration.cpp @@ -14,7 +14,6 @@ #include #include -const CAmount FEE = 10000; const int MIGRATION_EXPIRY_DELTA = 450; AsyncRPCOperation_saplingmigration::AsyncRPCOperation_saplingmigration(int targetHeight) : targetHeight_(targetHeight) {} @@ -114,7 +113,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { CAmount amountToSend = chooseAmount(availableFunds); auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain, &coinsView, &cs_main); builder.SetExpiryHeight(targetHeight_ + MIGRATION_EXPIRY_DELTA); - LogPrint(BCLog::ZRPCUNSAFE, "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - FEE)); + LogPrint(BCLog::ZRPCUNSAFE, "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), FormatMoney(amountToSend - DEFAULT_FEE)); std::vector fromNotes; CAmount fromNoteAmount = 0; while (fromNoteAmount < amountToSend) { @@ -147,10 +146,10 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { } builder.AddSproutInput(sproutSk, sproutEntry.note, vInputWitnesses[0].value()); } - // The amount chosen *includes* the 0.0001 ZEC fee for this transaction, i.e. - // the value of the Sapling output will be 0.0001 ZEC less. - builder.SetFee(FEE); - builder.AddSaplingOutput(ovkForShieldingFromTaddr(seed), migrationDestAddress, amountToSend - FEE); + // The amount chosen *includes* the default fee for this transaction, i.e. + // the value of the Sapling output will be 0.00001 ZEC less. + builder.SetFee(DEFAULT_FEE); + builder.AddSaplingOutput(ovkForShieldingFromTaddr(seed), migrationDestAddress, amountToSend - DEFAULT_FEE); CTransaction tx = builder.Build().GetTxOrThrow(); if (isCancelled()) { LogPrint(BCLog::ZRPCUNSAFE, "%s: Canceled. Stopping.\n", getId()); @@ -159,7 +158,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { pwalletMain->AddPendingSaplingMigrationTx(tx); LogPrint(BCLog::ZRPCUNSAFE, "%s: Added pending migration transaction with txid=%s\n", getId(), tx.GetHash().ToString()); ++numTxCreated; - amountMigrated += amountToSend - FEE; + amountMigrated += amountToSend - DEFAULT_FEE; migrationTxIds.push_back(tx.GetHash().ToString()); } while (numTxCreated < 5 && availableFunds > CENT); diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index 2afde0f3e..3bb32b829 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -21,9 +21,6 @@ #include -// Default transaction fee if caller does not specify one. -#define ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE 10000 - using namespace libzcash; class SendManyRecipient { @@ -83,7 +80,7 @@ class AsyncRPCOperation_sendmany : public AsyncRPCOperation { std::vector zOutputs, int minDepth, unsigned int anchorDepth, - CAmount fee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE, + CAmount fee = DEFAULT_FEE, UniValue contextInfo = NullUniValue); virtual ~AsyncRPCOperation_sendmany(); diff --git a/src/wallet/asyncrpcoperation_shieldcoinbase.h b/src/wallet/asyncrpcoperation_shieldcoinbase.h index b3895d371..ce2b50028 100644 --- a/src/wallet/asyncrpcoperation_shieldcoinbase.h +++ b/src/wallet/asyncrpcoperation_shieldcoinbase.h @@ -19,9 +19,6 @@ #include -// Default transaction fee if caller does not specify one. -#define SHIELD_COINBASE_DEFAULT_MINERS_FEE 10000 - using namespace libzcash; struct ShieldCoinbaseUTXO { @@ -47,7 +44,7 @@ class AsyncRPCOperation_shieldcoinbase : public AsyncRPCOperation { CMutableTransaction contextualTx, std::vector inputs, std::string toAddress, - CAmount fee = SHIELD_COINBASE_DEFAULT_MINERS_FEE, + CAmount fee = DEFAULT_FEE, UniValue contextInfo = NullUniValue); virtual ~AsyncRPCOperation_shieldcoinbase(); diff --git a/src/wallet/gtest/test_wallet.cpp b/src/wallet/gtest/test_wallet.cpp index a279935ca..0248ea637 100644 --- a/src/wallet/gtest/test_wallet.cpp +++ b/src/wallet/gtest/test_wallet.cpp @@ -1968,7 +1968,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID()); // Generate shielding tx from transparent to Sapling - // 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee + // 0.0005 t-ZEC in, 0.0004 z-ZEC out, default fee auto builder = TransactionBuilder(consensusParams, 1, &keystore); builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000); builder.AddSaplingOutput(extfvk.fvk.ovk, pk, 40000, {}); @@ -2023,7 +2023,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) { auto witness = saplingTree.witness(); // Create a Sapling-only transaction - // 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change + // 0.0004 z-ZEC in, 0.00025 z-ZEC out, default fee, 0.00005 z-ZEC change auto builder2 = TransactionBuilder(consensusParams, 2); builder2.AddSaplingSpend(expsk, note, anchor, witness); builder2.AddSaplingOutput(extfvk.fvk.ovk, pk, 25000, {}); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e4b074438..5c4d1b509 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3286,7 +3286,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) " }, ... ]\n" "3. minconf (numeric, optional, default=" + strprintf("%u", DEFAULT_NOTE_CONFIRMATIONS) + ") Only use funds confirmed at least this many times.\n" "4. fee (numeric, optional, default=" - + strprintf("%s", FormatMoney(ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" + + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n" "\nResult:\n" "\"operationid\" (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" "\nExamples:\n" @@ -3480,7 +3480,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) } // Fee in Zatoshis, not currency format) - CAmount nFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE; + CAmount nFee = DEFAULT_FEE; CAmount nDefaultFee = nFee; if (params.size() > 3) { @@ -3706,7 +3706,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) "1. \"fromaddress\" (string, required) The address is a taddr or \"*\" for all taddrs belonging to the wallet.\n" "2. \"toaddress\" (string, required) The address is a zaddr.\n" "3. fee (numeric, optional, default=" - + strprintf("%s", FormatMoney(SHIELD_COINBASE_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" + + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n" "4. limit (numeric, optional, default=" + strprintf("%d", SHIELD_COINBASE_DEFAULT_LIMIT) + ") Limit on the maximum number of utxos to shield. Set to 0 to use as many as will fit in the transaction.\n" "\nResult:\n" @@ -3742,7 +3742,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) } // Convert fee from currency format to zatoshis - CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; + CAmount nFee = DEFAULT_FEE; if (params.size() > 2) { if (params[2].get_real() == 0.0) { nFee = 0; @@ -3923,7 +3923,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) " ]\n" "2. \"toaddress\" (string, required) The taddr or zaddr to send the funds to.\n" "3. fee (numeric, optional, default=" - + strprintf("%s", FormatMoney(MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" + + strprintf("%s", FormatMoney(DEFAULT_FEE)) + ") The fee amount to attach to this transaction.\n" "4. transparent_limit (numeric, optional, default=" + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use as many as will fit in the transaction.\n" "5. shielded_limit (numeric, optional, default=" @@ -4027,7 +4027,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) } // Convert fee from currency format to zatoshis - CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; + CAmount nFee = DEFAULT_FEE; if (params.size() > 2) { if (params[2].get_real() == 0.0) { nFee = 0; diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index fda75db17..3eb5ffb77 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_WALLET_RPCWALLET_H #define BITCOIN_WALLET_RPCWALLET_H +#include "policy/fees.h" // for DEFAULT_FEE + class CRPCTable; void RegisterWalletRPCCommands(CRPCTable &tableRPC); diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 3d0ecb3a2..5610d1b80 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -957,7 +957,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) BOOST_CHECK_THROW(CallRPC("z_sendmany " "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " - "1 -0.0001" + "1 -0.00001" ), runtime_error); // invalid fee amount, bigger than MAX_MONEY @@ -1553,7 +1553,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters) BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " - "-0.0001" + "-0.00001" ), runtime_error); // invalid fee amount, bigger than MAX_MONEY @@ -1710,7 +1710,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters) "Invalid parameter, duplicated address: " + taddr1); // invalid fee amount, cannot be negative - CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " -0.0001", + CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " -0.00001", "Amount out of range"); // invalid fee amount, bigger than MAX_MONEY @@ -1719,11 +1719,11 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters) //FIXME "Amount out of range"); // invalid transparent limit, must be at least 0 - CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " 0.0001 -1", + CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " 0.00001 -1", "Limit on maximum number of UTXOs cannot be negative"); // invalid shielded limit, must be at least 0 - CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " 0.0001 100 -1", + CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " 0.00001 100 -1", "Limit on maximum number of notes cannot be negative"); CheckRPCThrows("z_mergetoaddress [\"ANY_TADDR\",\"" + taddr1 + "\"] " + taddr2, @@ -1739,7 +1739,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters) std::vector v (2 * (ZC_MEMO_SIZE+1)); // x2 for hexadecimal string format std::fill(v.begin(),v.end(), 'A'); std::string badmemo(v.begin(), v.end()); - CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + aSproutAddr + " 0.0001 100 100 " + badmemo, + CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + aSproutAddr + " 0.00001 100 100 " + badmemo, "Invalid parameter, size of memo is larger than maximum allowed 512"); // Mutable tx containing contextual information we need to build tx @@ -1840,7 +1840,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals) operation->main(); BOOST_CHECK(operation->isFailed()); std::string msg = operation->getErrorMessage(); - BOOST_CHECK( msg.find("Insufficient funds, have 0.00 and miners fee is 0.0001") != string::npos); + BOOST_CHECK( msg.find("Insufficient funds, have 0.00 and miners fee is 0.00001") != string::npos); } // get_memo_from_hex_string()) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7dbf448e0..2e421801f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3950,7 +3950,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. - if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) + if (nFeeNeeded < ::minRelayTxFee.GetFeeForRelay(nBytes)) { strFailReason = _("Transaction too large for fee policy"); return false; @@ -4025,7 +4025,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, std::optional maxTxFee) nFeeNeeded = maxTxFee; From d23c692e67cdfc2512e78e3e36220604e35cfd91 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 2 Sep 2024 09:38:36 +0000 Subject: [PATCH 129/146] Fix 'CheckBlock.BlockSproutRejectsBadVersion' --- src/consensus/validation.h | 2 +- src/main.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 09f5797f0..0bb458480 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -33,7 +33,7 @@ class CValidationState { public: CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {} virtual bool DoS(int level, bool ret = false, - unsigned int chRejectCodeIn=0, std::string strRejectReasonIn="", + unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="", bool corruptionIn=false, const std::string &strDebugMessageIn="") { chRejectCode = chRejectCodeIn; diff --git a/src/main.cpp b/src/main.cpp index 3fb266756..2f6f8f107 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3833,9 +3833,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, // Check transactions for (const CTransaction& tx : block.vtx) if (!CheckTransaction(tx, state, verifier)) - return error("%s: CheckTransaction of %s failed with %s", __func__, - tx.GetHash().ToString(), - FormatStateMessage(state)); + return error("%s: CheckTransaction failed", __func__); unsigned int nSigOps = 0; for (const CTransaction& tx : block.vtx) From 2b2e1fad459c94ee584e196fc1df7eb8980fa559 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 2 Sep 2024 12:07:15 +0000 Subject: [PATCH 130/146] Backport CreateNewBlock rewrite --- qa/rpc-tests/mergetoaddress_helper.py | 4 +- qa/rpc-tests/wallet_1941.py | 12 +- src/main.cpp | 2 +- src/main.h | 3 + src/miner.cpp | 330 +++++++++++--------------- src/miner.h | 2 +- src/test/mempool_tests.cpp | 51 +++- src/test/miner_tests.cpp | 85 ++++--- src/txmempool.cpp | 1 - src/txmempool.h | 4 +- 10 files changed, 253 insertions(+), 241 deletions(-) diff --git a/qa/rpc-tests/mergetoaddress_helper.py b/qa/rpc-tests/mergetoaddress_helper.py index 92a3bcb78..2541b6f17 100755 --- a/qa/rpc-tests/mergetoaddress_helper.py +++ b/qa/rpc-tests/mergetoaddress_helper.py @@ -49,9 +49,7 @@ def setup_network(self, test, additional_args=[]): test.nodes = [] test.nodes.append(start_node(0, test.options.tmpdir, args)) test.nodes.append(start_node(1, test.options.tmpdir, args)) - args2 = ['-debug=zrpcunsafe'] - args2 += additional_args - test.nodes.append(start_node(2, test.options.tmpdir, args2)) + test.nodes.append(start_node(2, test.options.tmpdir, args)) connect_nodes_bi(test.nodes, 0, 1) connect_nodes_bi(test.nodes, 1, 2) connect_nodes_bi(test.nodes, 0, 2) diff --git a/qa/rpc-tests/wallet_1941.py b/qa/rpc-tests/wallet_1941.py index b629c8c3e..17de4769b 100755 --- a/qa/rpc-tests/wallet_1941.py +++ b/qa/rpc-tests/wallet_1941.py @@ -61,12 +61,10 @@ def run_test (self): self.nodes[0].generate(1) # Ensure the block times of the latest blocks exceed the variability - self.nodes[0].setmocktime(starttime + 3000) - self.nodes[0].generate(1) - self.nodes[0].setmocktime(starttime + 6000) - self.nodes[0].generate(1) - self.nodes[0].setmocktime(starttime + 9000) - self.nodes[0].generate(1) + # but fall inside the future timestamp soft fork rule. + for i in range(10): + self.nodes[0].setmocktime(starttime + (900 * (i + 1))) + self.nodes[0].generate(1) self.sync_all() # Confirm the balance on node 0. @@ -93,7 +91,7 @@ def run_test (self): assert_equal(Decimal(resp), 0) # Re-import the key on node 1, scanning from before the transaction. - self.nodes[1].z_importkey(key, 'yes', self.nodes[1].getblockchaininfo()['blocks'] - 110) + self.nodes[1].z_importkey(key, 'yes', self.nodes[1].getblockchaininfo()['blocks'] - 120) # Confirm that the balance on node 1 is valid now (node 1 must # have rescanned) diff --git a/src/main.cpp b/src/main.cpp index 2f6f8f107..02d587572 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1265,7 +1265,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned } /** Convert CValidationState to a human-readable message for logging */ -static std::string FormatStateMessage(const CValidationState &state) +std::string FormatStateMessage(const CValidationState &state) { return strprintf("%s%s (code %i)", state.GetRejectReason(), diff --git a/src/main.h b/src/main.h index dfae6ff55..bd9318792 100644 --- a/src/main.h +++ b/src/main.h @@ -338,6 +338,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa /** Find block at height in a fork **/ const CBlockIndex* FindBlockAtHeight(int nHeight, const CBlockIndex* pIndex); +/** Convert CValidationState to a human-readable message for logging */ +std::string FormatStateMessage(const CValidationState &state); + struct CNodeStateStats { int nMisbehavior; int nSyncHeight; diff --git a/src/miner.cpp b/src/miner.cpp index 94729fe9e..ce508bd36 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -37,6 +37,7 @@ #include #endif #include +#include using namespace std; @@ -50,59 +51,35 @@ using namespace std; // transactions in the memory pool. When we select transactions from the // pool, we select by highest priority or fee rate, so we might consider // transactions that depend on transactions that aren't yet in the block. -// The COrphan class keeps track of these 'temporary orphans' while -// CreateBlock is figuring out which transactions to include. -// -class COrphan -{ -public: - const CTransaction* ptx; - set setDependsOn; - CFeeRate feeRate; - double dPriority; - - COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0) - { - } -}; uint64_t nLastBlockTx = 0; uint64_t nLastBlockSize = 0; -// We want to sort transactions by priority and fee rate, so: -typedef boost::tuple TxPriority; -class TxPriorityCompare +class ScoreCompare { - bool byFee; - public: - TxPriorityCompare(bool _byFee) : byFee(_byFee) { } + ScoreCompare() {} - bool operator()(const TxPriority& a, const TxPriority& b) + bool operator()(const CTxMemPool::txiter a, const CTxMemPool::txiter b) { - if (byFee) - { - if (a.get<1>() == b.get<1>()) - return a.get<0>() < b.get<0>(); - return a.get<1>() < b.get<1>(); - } - else - { - if (a.get<0>() == b.get<0>()) - return a.get<1>() < b.get<1>(); - return a.get<0>() < b.get<0>(); - } + return CompareTxMemPoolEntryByScore()(*b,*a); // Convert to less than } }; -void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) +int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { - pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, GetTime()); + int64_t nOldTime = pblock->nTime; + int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetTime()); + + if (nOldTime < nNewTime) + pblock->nTime = nNewTime; // Updating time can change work required on testnet: if (consensusParams.nPowAllowMinDifficultyBlocksAfterHeight != std::nullopt) { pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); } + + return nNewTime - nOldTime; } CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn) @@ -139,6 +116,22 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); // Collect memory pool transactions into the block + CTxMemPool::setEntries inBlock; + CTxMemPool::setEntries waitSet; + + // This vector will be sorted into a priority queue: + vector vecPriority; + TxCoinAgePriorityCompare pricomparer; + std::map waitPriMap; + typedef std::map::iterator waitPriIter; + double actualPriority = -1; + + std::priority_queue, ScoreCompare> clearedTxs; + bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); + uint64_t nBlockSize = 1000; + uint64_t nBlockTx = 0; + unsigned int nBlockSigOps = 100; + int lastFewTxs = 0; CAmount nFees = 0; { @@ -153,100 +146,26 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s SaplingMerkleTree sapling_tree; assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); - // Priority order to process transactions - list vOrphan; // list memory doesn't move - map > mapDependers; - bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); - - // This vector will be sorted into a priority queue: - vector vecPriority; - vecPriority.reserve(mempool.mapTx.size()); - for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); - mi != mempool.mapTx.end(); ++mi) - { - const CTransaction& tx = mi->GetTx(); - - int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) - ? nMedianTimePast - : pblock->GetBlockTime(); - - if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight)) - continue; + int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) + ? nMedianTimePast + : pblock->GetBlockTime(); - COrphan* porphan = NULL; - double dPriority = 0; - CAmount nTotalIn = 0; - bool fMissingInputs = false; - for (const CTxIn& txin : tx.vin) + bool fPriorityBlock = nBlockPrioritySize > 0; + if (fPriorityBlock) { + vecPriority.reserve(mempool.mapTx.size()); + for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); + mi != mempool.mapTx.end(); ++mi) { - // Read prev transaction - if (!view.HaveCoins(txin.prevout.hash)) - { - // This should never happen; all transactions in the memory - // pool should connect to either transactions in the chain - // or other transactions in the memory pool. - if (!mempool.mapTx.count(txin.prevout.hash)) - { - LogPrintf("ERROR: mempool transaction missing input\n"); - assert("mempool transaction missing input" == 0); - fMissingInputs = true; - if (porphan) - vOrphan.pop_back(); - break; - } - - // Has to wait for dependencies - if (!porphan) - { - // Use list for automatic deletion - vOrphan.push_back(COrphan(&tx)); - porphan = &vOrphan.back(); - } - mapDependers[txin.prevout.hash].push_back(porphan); - porphan->setDependsOn.insert(txin.prevout.hash); - nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue; - continue; - } - const CCoins* coins = view.AccessCoins(txin.prevout.hash); - assert(coins); - - CAmount nValueIn = coins->vout[txin.prevout.n].nValue; - nTotalIn += nValueIn; - - int nConf = nHeight - coins->nHeight; - - dPriority += (double)nValueIn * nConf; + double dPriority = mi->GetPriority(nHeight); + CAmount dummy; + mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy); + vecPriority.push_back(TxCoinAgePriority(dPriority, mi)); } - nTotalIn += tx.GetShieldedValueIn(); - - if (fMissingInputs) continue; - - // Priority is sum(valuein * age) / modified_txsize - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - dPriority = tx.ComputePriority(dPriority, nTxSize); - - uint256 hash = tx.GetHash(); - mempool.ApplyDeltas(hash, dPriority, nTotalIn); - - CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize); - - if (porphan) - { - porphan->dPriority = dPriority; - porphan->feeRate = feeRate; - } - else - vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx()))); + std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer); } - // Collect transactions into block - uint64_t nBlockSize = 1000; - uint64_t nBlockTx = 0; - int nBlockSigOps = 100; - bool fSortedByFee = (nBlockPrioritySize <= 0); - - TxPriorityCompare comparer(fSortedByFee); - std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); + CTxMemPool::indexed_transaction_set::nth_index<2>::type::iterator mi = mempool.mapTx.get<2>().begin(); + CTxMemPool::txiter iter; // We want to track the value pool, but if the miner gets // invoked on an old block before the hardcoded fallback @@ -270,60 +189,82 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s } } - while (!vecPriority.empty()) + while (mi != mempool.mapTx.get<2>().end() || !clearedTxs.empty()) { - // Take highest priority transaction off the priority queue: - double dPriority = vecPriority.front().get<0>(); - CFeeRate feeRate = vecPriority.front().get<1>(); - const CTransaction& tx = *(vecPriority.front().get<2>()); - - std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); - vecPriority.pop_back(); - - // Size limits - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - if (nBlockSize + nTxSize >= nBlockMaxSize) - continue; + bool priorityTx = false; + if (fPriorityBlock && !vecPriority.empty()) { // add a tx from priority queue to fill the blockprioritysize + priorityTx = true; + iter = vecPriority.front().second; + actualPriority = vecPriority.front().first; + std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + vecPriority.pop_back(); + } + else if (clearedTxs.empty()) { // add tx with next highest score + iter = mempool.mapTx.project<0>(mi); + mi++; + } + else { // try to add a previously postponed child tx + iter = clearedTxs.top(); + clearedTxs.pop(); + } - // Legacy limits on sigOps: - unsigned int nTxSigOps = GetLegacySigOpCount(tx); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) - continue; + if (inBlock.count(iter)) + continue; // could have been added to the priorityBlock - // Skip free transactions if we're past the minimum block size: + const CTransaction& tx = iter->GetTx(); const uint256& hash = tx.GetHash(); - double dPriorityDelta = 0; - CAmount nFeeDelta = 0; - mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); - if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) - continue; - // Prioritise by fee once past the priority size or we run out of high-priority - // transactions: - if (!fSortedByFee && - ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority))) + bool fOrphan = false; + for (CTxMemPool::txiter parent : mempool.GetMemPoolParents(iter)) { - fSortedByFee = true; - comparer = TxPriorityCompare(fSortedByFee); - std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); + if (!inBlock.count(parent)) { + fOrphan = true; + break; + } } - if (!view.HaveInputs(tx)) + if (fOrphan) { + if (priorityTx) + waitPriMap.insert(std::make_pair(iter,actualPriority)); + else + waitSet.insert(iter); continue; + } - CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut(); + unsigned int nTxSize = iter->GetTxSize(); + if (fPriorityBlock && + (nBlockSize + nTxSize >= nBlockPrioritySize || !AllowFree(actualPriority))) { + fPriorityBlock = false; + waitPriMap.clear(); + } + if (!priorityTx && + (iter->GetModifiedFee() < ::minRelayTxFee.GetFee(nTxSize)) && + (iter->GetModifiedFee() < DEFAULT_FEE) && + (nBlockSize >= nBlockMinSize)) { + break; + } + if (nBlockSize + nTxSize >= nBlockMaxSize) { + if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { + break; + } + // Once we're within 1000 bytes of a full block, only look at 50 more txs + // to try to fill the remaining space. + if (nBlockSize > nBlockMaxSize - 1000) { + lastFewTxs++; + } + continue; + } - nTxSigOps += GetP2SHSigOpCount(tx, view); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) + if (!IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight)) continue; - // Note that flags: we don't want to set mempool/IsStandard() - // policy here, but we still have to ensure that the block we - // create only contains transactions that are valid in new blocks. - CValidationState state; - PrecomputedTransactionData txdata(tx); - if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId)) + unsigned int nTxSigOps = iter->GetSigOpCount(); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) { + if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { + break; + } continue; + } if (chainparams.ZIP209Enabled() && monitoring_pool_balances) { // Does this transaction lead to a turnstile violation? @@ -351,12 +292,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s saplingValue = saplingValueDummy; } - UpdateCoins(tx, view, nHeight); - - for (const OutputDescription &outDescription : tx.vShieldedOutput) { - sapling_tree.append(outDescription.cmu); - } - + CAmount nTxFees = iter->GetFee(); // Added pblock->vtx.push_back(tx); pblocktemplate->vTxFees.push_back(nTxFees); @@ -368,31 +304,37 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s if (fPrintPriority) { - LogPrintf("priority %.1f fee %s txid %s\n", - dPriority, feeRate.ToString(), tx.GetHash().ToString()); + double dPriority = iter->GetPriority(nHeight); + CAmount dummy; + mempool.ApplyDeltas(tx.GetHash(), dPriority, dummy); + LogPrintf("%s: priority %.1f fee %s txid %s\n", + __func__, dPriority , CFeeRate(iter->GetModifiedFee(), nTxSize).ToString(), tx.GetHash().ToString()); } + inBlock.insert(iter); + // Add transactions that depend on this one to the priority queue - if (mapDependers.count(hash)) + for (CTxMemPool::txiter child : mempool.GetMemPoolChildren(iter)) { - for (COrphan* porphan : mapDependers[hash]) - { - if (!porphan->setDependsOn.empty()) - { - porphan->setDependsOn.erase(hash); - if (porphan->setDependsOn.empty()) - { - vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx)); - std::push_heap(vecPriority.begin(), vecPriority.end(), comparer); - } + if (fPriorityBlock) { + waitPriIter wpiter = waitPriMap.find(child); + if (wpiter != waitPriMap.end()) { + vecPriority.push_back(TxCoinAgePriority(wpiter->second,child)); + std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + waitPriMap.erase(wpiter); + } + } + else { + if (waitSet.count(child)) { + clearedTxs.push(child); + waitSet.erase(child); } } } } - nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; - LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); + LogPrintf("%s: total size %u txs: %u fees: %ld sigops %d\n", __func__, nBlockSize, nBlockTx, nFees, nBlockSigOps); // Create coinbase tx CMutableTransaction txNew = CreateNewContextualCMutableTransaction(chainparams.GetConsensus(), nHeight); @@ -420,6 +362,13 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; + // Update the Sapling commitment tree. + for (const CTransaction& tx : pblock->vtx) { + for (const OutputDescription& odesc : tx.vShieldedOutput) { + sapling_tree.append(odesc.cmu); + } + } + // Randomise nonce arith_uint256 nonce = UintToArith256(GetRandHash()); // Clear the top and bottom 16 bits (for local use as thread flags and counters) @@ -437,8 +386,9 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CValidationState state; - if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) - throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed"); + if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { + throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); + } } return pblocktemplate.release(); @@ -701,7 +651,9 @@ void static BitcoinMiner(const CChainParams& chainparams) // Update nNonce and nTime pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + if (UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev) < 0) + break; // Recreate the block if the clock has run backwards, + // so that we can use the correct time. if (chainparams.GetConsensus().nPowAllowMinDifficultyBlocksAfterHeight != std::nullopt) { // Changing pblock->nTime can change work required on testnet: diff --git a/src/miner.h b/src/miner.h index abb8e0b0b..109a69b63 100644 --- a/src/miner.h +++ b/src/miner.h @@ -39,6 +39,6 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams); #endif -void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); +int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); #endif // BITCOIN_MINER_H diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 2bdfccfaa..62f621802 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -105,12 +105,13 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) removed.clear(); } +template void CheckSort(CTxMemPool &pool, std::vector &sortedOrder) { BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size()); - CTxMemPool::indexed_transaction_set::nth_index<1>::type::iterator it = pool.mapTx.get<1>().begin(); + typename CTxMemPool::indexed_transaction_set::nth_index::type::iterator it = pool.mapTx.get().begin(); int count=0; - for (; it != pool.mapTx.get<1>().end(); ++it, ++count) { + for (; it != pool.mapTx.get().end(); ++it, ++count) { BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]); } } @@ -166,7 +167,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder[2] = tx1.GetHash().ToString(); // 10000 sortedOrder[3] = tx4.GetHash().ToString(); // 15000 sortedOrder[4] = tx2.GetHash().ToString(); // 20000 - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); /* low fee but with high fee child */ /* tx6 -> tx7 -> tx8, tx9 -> tx10 */ @@ -178,7 +179,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) BOOST_CHECK_EQUAL(pool.size(), 6); // Check that at this point, tx6 is sorted low sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString()); - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); CTxMemPool::setEntries setAncestors; setAncestors.insert(pool.mapTx.find(tx6.GetHash())); @@ -204,7 +205,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.erase(sortedOrder.begin()); sortedOrder.push_back(tx6.GetHash().ToString()); sortedOrder.push_back(tx7.GetHash().ToString()); - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); /* low fee child of tx7 */ CMutableTransaction tx8 = CMutableTransaction(); @@ -219,7 +220,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // Now tx8 should be sorted low, but tx6/tx both high sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString()); - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); /* low fee child of tx7 */ CMutableTransaction tx9 = CMutableTransaction(); @@ -234,7 +235,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // tx9 should be sorted low BOOST_CHECK_EQUAL(pool.size(), 9); sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString()); - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); std::vector snapshotOrder = sortedOrder; @@ -276,7 +277,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString()); sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString()); sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6 - CheckSort(pool, sortedOrder); + CheckSort<1>(pool, sortedOrder); // there should be 10 transactions in the mempool BOOST_CHECK_EQUAL(pool.size(), 10); @@ -284,7 +285,39 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // Now try removing tx10 and verify the sort order returns to normal std::list removed; pool.remove(pool.mapTx.find(tx10.GetHash())->GetTx(), removed, true); - CheckSort(pool, snapshotOrder); + CheckSort<1>(pool, snapshotOrder); + + pool.remove(pool.mapTx.find(tx9.GetHash())->GetTx(), removed, true); + pool.remove(pool.mapTx.find(tx8.GetHash())->GetTx(), removed, true); + /* Now check the sort on the mining score index. + * Final order should be: + * + * tx7 (2M) + * tx2 (20k) + * tx4 (15000) + * tx1/tx5 (10000) + * tx3/6 (0) + * (Ties resolved by hash) + */ + sortedOrder.clear(); + sortedOrder.push_back(tx7.GetHash().ToString()); + sortedOrder.push_back(tx2.GetHash().ToString()); + sortedOrder.push_back(tx4.GetHash().ToString()); + if (tx1.GetHash() < tx5.GetHash()) { + sortedOrder.push_back(tx5.GetHash().ToString()); + sortedOrder.push_back(tx1.GetHash().ToString()); + } else { + sortedOrder.push_back(tx1.GetHash().ToString()); + sortedOrder.push_back(tx5.GetHash().ToString()); + } + if (tx3.GetHash() < tx6.GetHash()) { + sortedOrder.push_back(tx6.GetHash().ToString()); + sortedOrder.push_back(tx3.GetHash().ToString()); + } else { + sortedOrder.push_back(tx3.GetHash().ToString()); + sortedOrder.push_back(tx6.GetHash().ToString()); + } + CheckSort<2>(pool, sortedOrder); } BOOST_AUTO_TEST_CASE(RemoveWithoutBranchId) { diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 318e766f2..484763914 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -155,14 +155,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCheckpointsEnabled = false; fCoinbaseEnforcedShieldingEnabled = false; + // Simple block creation, nothing special yet: + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + // We can't make transactions until we have inputs // Therefore, load 100 blocks :) std::vectortxFirst; for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i) { - // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - CBlock *pblock = &pblocktemplate->block; // pointer for convenience pblock->nVersion = 4; // Fake the blocks taking at least nPowTargetSpacing to be mined. @@ -171,10 +171,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // one spacing ahead of the tip. Within 11 blocks of genesis, the median // will be closer to the tip, and blocks will appear slower. pblock->nTime = chainActive.Tip()->GetMedianTimePast()+6*Params().GetConsensus().PoWTargetSpacing(i); + pblock->nBits = GetNextWorkRequired(chainActive.Tip(), pblock, chainparams.GetConsensus()); CMutableTransaction txCoinbase(pblock->vtx[0]); txCoinbase.nVersion = 1; txCoinbase.vin[0].scriptSig = CScript() << (chainActive.Height()+1) << OP_0; txCoinbase.vout[0].scriptPubKey = CScript(); + txCoinbase.vout[0].nValue = 50000 * (i + 1); + txCoinbase.vout[1].nValue = 12500 * (i + 1); pblock->vtx[0] = CTransaction(txCoinbase); if (txFirst.size() < 2) txFirst.push_back(new CTransaction(pblock->vtx[0])); @@ -268,15 +271,28 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL)); BOOST_CHECK_MESSAGE(state.IsValid(), state.GetRejectReason()); pblock->hashPrevBlock = pblock->GetHash(); - - // Need to recreate the template each round because of mining slow start - delete pblocktemplate; } + // Set the clock to be just ahead of the last "mined" block, to ensure we satisfy the + // future timestamp soft fork rule. + auto curTime = GetTime(); + OffsetClock::SetGlobal(); + OffsetClock::Instance()->Set(std::chrono::seconds(-curTime + pblocktemplate->block.nTime)); + + delete pblocktemplate; + // Just to make sure we can still make simple blocks BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; + // Define a helper function to check the kind of failure from TestBlockValidity. + auto err_is = [](const std::string& msg) { + return [&](std::runtime_error const& e) { + BOOST_TEST_INFO(e.what()); + return std::string(e.what()).find(msg) != std::string::npos; + }; + }; + // block sigops > limit: 1000 CHECKMULTISIG + 1 tx.vin.resize(1); // NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG @@ -290,7 +306,22 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= 10; hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails + mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + tx.vin[0].prevout.hash = hash; + } + BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-blk-sigops")); + mempool.clear(); + + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vout[0].nValue = 50000LL; + for (unsigned int i = 0; i < 1001; ++i) + { + tx.vout[0].nValue -= 10; + hash = tx.GetHash(); + bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase + // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes + mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); @@ -311,18 +342,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= 350; hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); - // orphan in mempool + // orphan in mempool, template creation fails hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - delete pblocktemplate; + mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).FromTx(tx)); + BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); mempool.clear(); // child with higher priority than parent @@ -330,7 +360,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = txFirst[1]->GetHash(); tx.vout[0].nValue = 39000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(1000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin.resize(2); tx.vin[1].scriptSig = CScript() << OP_1; @@ -338,23 +368,23 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[1].prevout.n = 0; tx.vout[0].nValue = 49000LL; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(4000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); - // coinbase in mempool + // coinbase in mempool, template creation fails tx.vin.resize(1); tx.vin[0].prevout.SetNull(); tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; tx.vout[0].nValue = 0; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - delete pblocktemplate; + // give it a fee so it'll get mined + mempool.addUnchecked(hash, entry.Fee(1).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-cb-multiple")); mempool.clear(); - // invalid (pre-p2sh) txn in mempool + // P2SH txn in mempool (valid, Zcash always had P2SH enabled) tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vin[0].scriptSig = CScript() << OP_1; @@ -362,28 +392,27 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(100L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); tx.vout[0].nValue -= 10000; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); - // double spend txn pair in mempool + // double spend txn pair in mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; tx.vout[0].nValue = 49000LL; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); - delete pblocktemplate; + mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); mempool.clear(); // subsidy changing @@ -407,7 +436,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = chainActive.Tip()->nHeight+1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST)); // time locked @@ -421,7 +450,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx2.vout[0].scriptPubKey = CScript() << OP_1; tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1; hash = tx2.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); + mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST)); BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 7a3fd4562..1d208df95 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -464,7 +464,6 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC mapAddressInserted.insert(make_pair(txhash, inserted)); } -// START insightexplorer void CTxMemPool::getAddressIndex( const std::vector>& addresses, std::vector>& results) diff --git a/src/txmempool.h b/src/txmempool.h index 087ef090c..195559bc3 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -389,6 +389,8 @@ class CTxMemPool }; typedef std::set setEntries; + const setEntries & GetMemPoolParents(txiter entry) const; + const setEntries & GetMemPoolChildren(txiter entry) const; private: typedef std::map cacheMap; @@ -400,8 +402,6 @@ class CTxMemPool typedef std::map txlinksMap; txlinksMap mapLinks; - const setEntries & GetMemPoolParents(txiter entry) const; - const setEntries & GetMemPoolChildren(txiter entry) const; void UpdateParent(txiter entry, txiter parent, bool add); void UpdateChild(txiter entry, txiter child, bool add); From 729a293e428863719566a1a7fd2829bdc297f799 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 2 Sep 2024 13:39:05 +0000 Subject: [PATCH 131/146] Backport BlockAssembler class --- src/miner.cpp | 607 +++++++++++++++++++++---------------- src/miner.h | 62 +++- src/rpc/mining.cpp | 4 +- src/test/mempool_tests.cpp | 22 +- src/test/miner_tests.cpp | 85 +++--- src/test/test_bitcoin.cpp | 2 +- src/txmempool.h | 8 + 7 files changed, 474 insertions(+), 316 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index ce508bd36..d8072cec4 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -82,316 +82,401 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam return nNewTime - nOldTime; } -CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn) +BlockAssembler::BlockAssembler(const CChainParams& _chainparams) + : chainparams(_chainparams) { - // Create new block - std::unique_ptr pblocktemplate(new CBlockTemplate()); + // Largest block you're willing to create: + nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); + // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: + nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + + // Minimum block size you want to create; block will be filled with free transactions + // until there are no more or the block reaches this size: + nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); + nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); +} + +void BlockAssembler::resetBlock() +{ + inBlock.clear(); + + // Reserve space for coinbase tx + nBlockSize = 1000; + nBlockSigOps = 100; + + // These counters do not include coinbase tx + nBlockTx = 0; + nFees = 0; + + sproutValue = 0; + saplingValue = 0; + monitoring_pool_balances = true; + + lastFewTxs = 0; + blockFinished = false; +} + +CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) +{ + resetBlock(); + + pblocktemplate.reset(new CBlockTemplate()); + if(!pblocktemplate.get()) return NULL; - CBlock *pblock = &pblocktemplate->block; // pointer for convenience + pblock = &pblocktemplate->block; // pointer for convenience + + // Add dummy coinbase tx as first transaction + pblock->vtx.push_back(CTransaction()); + pblocktemplate->vTxFees.push_back(-1); // updated at end + pblocktemplate->vTxSigOps.push_back(-1); // updated at end + + LOCK2(cs_main, mempool.cs); + CBlockIndex* pindexPrev = chainActive.Tip(); + nHeight = pindexPrev->nHeight + 1; + uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) pblock->nVersion = GetArg("-blockversion", pblock->nVersion); - // Add dummy coinbase tx as first transaction - pblock->vtx.push_back(CTransaction()); - pblocktemplate->vTxFees.push_back(-1); // updated at end - pblocktemplate->vTxSigOps.push_back(-1); // updated at end + pblock->nTime = GetTime(); + const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); + + CCoinsViewCache view(pcoinsTip); + + SaplingMerkleTree sapling_tree; + assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); + + nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) + ? nMedianTimePast + : pblock->GetBlockTime(); + + // We want to track the value pool, but if the miner gets + // invoked on an old block before the hardcoded fallback + // is active we don't want to trip up any assertions. So, + // we only adhere to the turnstile (as a miner) if we + // actually have all of the information necessary to do + // so. + if (chainparams.ZIP209Enabled()) { + if (pindexPrev->nChainSproutValue) { + sproutValue = *pindexPrev->nChainSproutValue; + } else { + monitoring_pool_balances = false; + } + if (pindexPrev->nChainSaplingValue) { + saplingValue = *pindexPrev->nChainSaplingValue; + } else { + monitoring_pool_balances = false; + } + } - // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); - // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: - nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + addPriorityTxs(); + addScoreTxs(); + + nLastBlockTx = nBlockTx; + nLastBlockSize = nBlockSize; + LogPrintf("%s: total size %u txs: %u fees: %ld sigops %d\n", __func__, nBlockSize, nBlockTx, nFees, nBlockSigOps); + + // Create coinbase transaction. + CMutableTransaction coinbaseTx = CreateNewContextualCMutableTransaction(chainparams.GetConsensus(), nHeight); + coinbaseTx.vin.resize(1); + coinbaseTx.vin[0].prevout.SetNull(); + coinbaseTx.vout.resize(1); + coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; + coinbaseTx.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + + if ((nHeight > chainparams.GetCommunityFeeStartHeight()) && (nHeight <= chainparams.GetLastCommunityFeeBlockHeight())) { + // Community Fee is 5% of the block subsidy + auto vCommunityFee = coinbaseTx.vout[0].nValue * 0.05; + // Take some reward away from us + coinbaseTx.vout[0].nValue -= vCommunityFee; + // And give it to the community + coinbaseTx.vout.push_back(CTxOut(vCommunityFee, chainparams.GetCommunityFeeScriptAtHeight(nHeight))); + } - // How much of the block should be dedicated to high-priority transactions, - // included regardless of the fees they pay - unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); - nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); + // Set to 0 so expiry height does not apply to coinbase txs + coinbaseTx.nExpiryHeight = 0; - // Minimum block size you want to create; block will be filled with free transactions - // until there are no more or the block reaches this size: - unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); - nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); + // Add fees + coinbaseTx.vout[0].nValue += nFees; + coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; - // Collect memory pool transactions into the block - CTxMemPool::setEntries inBlock; - CTxMemPool::setEntries waitSet; + pblock->vtx[0] = coinbaseTx; + pblocktemplate->vTxFees[0] = -nFees; - // This vector will be sorted into a priority queue: - vector vecPriority; - TxCoinAgePriorityCompare pricomparer; - std::map waitPriMap; - typedef std::map::iterator waitPriIter; - double actualPriority = -1; + // Update the Sapling commitment tree. + for (const CTransaction& tx : pblock->vtx) { + for (const OutputDescription& odesc : tx.vShieldedOutput) { + sapling_tree.append(odesc.cmu); + } + } - std::priority_queue, ScoreCompare> clearedTxs; - bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); - uint64_t nBlockSize = 1000; - uint64_t nBlockTx = 0; - unsigned int nBlockSigOps = 100; - int lastFewTxs = 0; - CAmount nFees = 0; + // Randomise nonce + arith_uint256 nonce = UintToArith256(GetRandHash()); + // Clear the top and bottom 16 bits (for local use as thread flags and counters) + nonce <<= 32; + nonce >>= 16; + pblock->nNonce = ArithToUint256(nonce); + + uint32_t prevConsensusBranchId = CurrentEpochBranchId(pindexPrev->nHeight, chainparams.GetConsensus()); + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + pblock->hashFinalSaplingRoot = sapling_tree.root(); + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); + pblock->nSolution.clear(); + pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); + + CValidationState state; + if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { + throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); + } + + return pblocktemplate.release(); +} + +bool BlockAssembler::isStillDependent(CTxMemPool::txiter iter) +{ + for (CTxMemPool::txiter parent : mempool.GetMemPoolParents(iter)) { - LOCK2(cs_main, mempool.cs); - CBlockIndex* pindexPrev = chainActive.Tip(); - const int nHeight = pindexPrev->nHeight + 1; - uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, chainparams.GetConsensus()); - pblock->nTime = GetTime(); - const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); - CCoinsViewCache view(pcoinsTip); - - SaplingMerkleTree sapling_tree; - assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree)); - - int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) - ? nMedianTimePast - : pblock->GetBlockTime(); - - bool fPriorityBlock = nBlockPrioritySize > 0; - if (fPriorityBlock) { - vecPriority.reserve(mempool.mapTx.size()); - for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); - mi != mempool.mapTx.end(); ++mi) - { - double dPriority = mi->GetPriority(nHeight); - CAmount dummy; - mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy); - vecPriority.push_back(TxCoinAgePriority(dPriority, mi)); - } - std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + if (!inBlock.count(parent)) { + return true; } + } + return false; +} - CTxMemPool::indexed_transaction_set::nth_index<2>::type::iterator mi = mempool.mapTx.get<2>().begin(); - CTxMemPool::txiter iter; - - // We want to track the value pool, but if the miner gets - // invoked on an old block before the hardcoded fallback - // is active we don't want to trip up any assertions. So, - // we only adhere to the turnstile (as a miner) if we - // actually have all of the information necessary to do - // so. - CAmount sproutValue = 0; - CAmount saplingValue = 0; - bool monitoring_pool_balances = true; - if (chainparams.ZIP209Enabled()) { - if (pindexPrev->nChainSproutValue) { - sproutValue = *pindexPrev->nChainSproutValue; - } else { - monitoring_pool_balances = false; - } - if (pindexPrev->nChainSaplingValue) { - saplingValue = *pindexPrev->nChainSaplingValue; - } else { - monitoring_pool_balances = false; - } +bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter) +{ + if (nBlockSize + iter->GetTxSize() >= nBlockMaxSize) { + // If the block is so close to full that no more txs will fit + // or if we've tried more than 50 times to fill remaining space + // then flag that the block is finished + if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { + blockFinished = true; + return false; + } + // Once we're within 1000 bytes of a full block, only look at 50 more txs + // to try to fill the remaining space. + if (nBlockSize > nBlockMaxSize - 1000) { + lastFewTxs++; } + return false; + } - while (mi != mempool.mapTx.get<2>().end() || !clearedTxs.empty()) - { - bool priorityTx = false; - if (fPriorityBlock && !vecPriority.empty()) { // add a tx from priority queue to fill the blockprioritysize - priorityTx = true; - iter = vecPriority.front().second; - actualPriority = vecPriority.front().first; - std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - vecPriority.pop_back(); - } - else if (clearedTxs.empty()) { // add tx with next highest score - iter = mempool.mapTx.project<0>(mi); - mi++; - } - else { // try to add a previously postponed child tx - iter = clearedTxs.top(); - clearedTxs.pop(); - } + if (nBlockSigOps + iter->GetSigOpCount() >= MAX_BLOCK_SIGOPS) { + // If the block has room for no more sig ops then + // flag that the block is finished + if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { + blockFinished = true; + return false; + } + // Otherwise attempt to find another tx with fewer sigops + // to put in the block. + return false; + } - if (inBlock.count(iter)) - continue; // could have been added to the priorityBlock + // Must check that lock times are still valid + // This can be removed once MTP is always enforced + // as long as reorgs keep the mempool consistent. + if (!IsFinalTx(iter->GetTx(), nHeight, nLockTimeCutoff)) + return false; - const CTransaction& tx = iter->GetTx(); - const uint256& hash = tx.GetHash(); + // Must check that expiry heights are still valid. + if (IsExpiredTx(iter->GetTx(), nHeight)) + return false; - bool fOrphan = false; - for (CTxMemPool::txiter parent : mempool.GetMemPoolParents(iter)) - { - if (!inBlock.count(parent)) { - fOrphan = true; - break; - } - } + if (chainparams.ZIP209Enabled() && monitoring_pool_balances) { + // Does this transaction lead to a turnstile violation? - if (fOrphan) { - if (priorityTx) - waitPriMap.insert(std::make_pair(iter,actualPriority)); - else - waitSet.insert(iter); - continue; - } + CAmount sproutValueDummy = sproutValue; + CAmount saplingValueDummy = saplingValue; - unsigned int nTxSize = iter->GetTxSize(); - if (fPriorityBlock && - (nBlockSize + nTxSize >= nBlockPrioritySize || !AllowFree(actualPriority))) { - fPriorityBlock = false; - waitPriMap.clear(); - } - if (!priorityTx && - (iter->GetModifiedFee() < ::minRelayTxFee.GetFee(nTxSize)) && - (iter->GetModifiedFee() < DEFAULT_FEE) && - (nBlockSize >= nBlockMinSize)) { - break; - } - if (nBlockSize + nTxSize >= nBlockMaxSize) { - if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { - break; - } - // Once we're within 1000 bytes of a full block, only look at 50 more txs - // to try to fill the remaining space. - if (nBlockSize > nBlockMaxSize - 1000) { - lastFewTxs++; - } - continue; - } + saplingValueDummy += -iter->GetTx().valueBalance; - if (!IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight)) - continue; + for (auto js : iter->GetTx().vJoinSplit) { + sproutValueDummy += js.vpub_old; + sproutValueDummy -= js.vpub_new; + } - unsigned int nTxSigOps = iter->GetSigOpCount(); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) { - if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { - break; - } - continue; - } + if (sproutValueDummy < 0) { + LogPrintf("CreateNewBlock: tx %s appears to violate Sprout turnstile\n", + iter->GetTx().GetHash().GetHex()); + return false; + } + if (saplingValueDummy < 0) { + LogPrintf("CreateNewBlock: tx %s appears to violate Sapling turnstile\n", + iter->GetTx().GetHash().GetHex()); + return false; + } - if (chainparams.ZIP209Enabled() && monitoring_pool_balances) { - // Does this transaction lead to a turnstile violation? + // We update this here instead of in AddToBlock to avoid recalculating + // the deltas, because there are no more checks and we know that the + // transaction will be added to the block. + sproutValue = sproutValueDummy; + saplingValue = saplingValueDummy; + } - CAmount sproutValueDummy = sproutValue; - CAmount saplingValueDummy = saplingValue; + return true; +} - saplingValueDummy += -tx.valueBalance; +void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) +{ + pblock->vtx.push_back(iter->GetTx()); + pblocktemplate->vTxFees.push_back(iter->GetFee()); + pblocktemplate->vTxSigOps.push_back(iter->GetSigOpCount()); + nBlockSize += iter->GetTxSize(); + ++nBlockTx; + nBlockSigOps += iter->GetSigOpCount(); + nFees += iter->GetFee(); + inBlock.insert(iter); - for (auto js : tx.vJoinSplit) { - sproutValueDummy += js.vpub_old; - sproutValueDummy -= js.vpub_new; - } + bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); + if (fPrintPriority) { + double dPriority = iter->GetPriority(nHeight); + CAmount dummy; + mempool.ApplyDeltas(iter->GetTx().GetHash(), dPriority, dummy); + LogPrintf("%s: priority %.1f fee %s txid %s\n", + __func__, + dPriority, + CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(), + iter->GetTx().GetHash().ToString()); + } +} - if (sproutValueDummy < 0) { - LogPrintf("CreateNewBlock(): tx %s appears to violate Sprout turnstile\n", tx.GetHash().ToString()); - continue; - } - if (saplingValueDummy < 0) { - LogPrintf("CreateNewBlock(): tx %s appears to violate Sapling turnstile\n", tx.GetHash().ToString()); - continue; - } +void BlockAssembler::addScoreTxs() +{ + std::priority_queue, ScoreCompare> clearedTxs; + CTxMemPool::setEntries waitSet; + CTxMemPool::indexed_transaction_set::index::type::iterator mi = mempool.mapTx.get().begin(); + CTxMemPool::txiter iter; + while (!blockFinished && (mi != mempool.mapTx.get().end() || !clearedTxs.empty())) + { + // If no txs that were previously postponed are available to try + // again, then try the next highest score tx + if (clearedTxs.empty()) { + iter = mempool.mapTx.project<0>(mi); + mi++; + } + // If a previously postponed tx is available to try again, then it + // has higher score than all untried so far txs + else { + iter = clearedTxs.top(); + clearedTxs.pop(); + } - sproutValue = sproutValueDummy; - saplingValue = saplingValueDummy; - } + // If tx already in block, skip (added by addPriorityTxs) + if (inBlock.count(iter)) { + continue; + } - CAmount nTxFees = iter->GetFee(); - // Added - pblock->vtx.push_back(tx); - pblocktemplate->vTxFees.push_back(nTxFees); - pblocktemplate->vTxSigOps.push_back(nTxSigOps); - nBlockSize += nTxSize; - ++nBlockTx; - nBlockSigOps += nTxSigOps; - nFees += nTxFees; - - if (fPrintPriority) - { - double dPriority = iter->GetPriority(nHeight); - CAmount dummy; - mempool.ApplyDeltas(tx.GetHash(), dPriority, dummy); - LogPrintf("%s: priority %.1f fee %s txid %s\n", - __func__, dPriority , CFeeRate(iter->GetModifiedFee(), nTxSize).ToString(), tx.GetHash().ToString()); - } + // If tx is dependent on other mempool txs which haven't yet been included + // then put it in the waitSet + if (isStillDependent(iter)) { + waitSet.insert(iter); + continue; + } - inBlock.insert(iter); + // If the fee rate is below the min fee rate for mining, then we're done + // adding txs based on score (fee rate) + if ((iter->GetModifiedFee() < ::minRelayTxFee.GetFee(iter->GetTxSize())) && + (iter->GetModifiedFee() < DEFAULT_FEE) && + (nBlockSize >= nBlockMinSize)) { + return; + } - // Add transactions that depend on this one to the priority queue + // If this tx fits in the block add it, otherwise keep looping + if (TestForBlock(iter)) { + AddToBlock(iter); + + // This tx was successfully added, so + // add transactions that depend on this one to the priority queue to try again for (CTxMemPool::txiter child : mempool.GetMemPoolChildren(iter)) { - if (fPriorityBlock) { - waitPriIter wpiter = waitPriMap.find(child); - if (wpiter != waitPriMap.end()) { - vecPriority.push_back(TxCoinAgePriority(wpiter->second,child)); - std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - waitPriMap.erase(wpiter); - } - } - else { - if (waitSet.count(child)) { - clearedTxs.push(child); - waitSet.erase(child); - } + if (waitSet.count(child)) { + clearedTxs.push(child); + waitSet.erase(child); } } } - nLastBlockTx = nBlockTx; - nLastBlockSize = nBlockSize; - LogPrintf("%s: total size %u txs: %u fees: %ld sigops %d\n", __func__, nBlockSize, nBlockTx, nFees, nBlockSigOps); - - // Create coinbase tx - CMutableTransaction txNew = CreateNewContextualCMutableTransaction(chainparams.GetConsensus(), nHeight); - txNew.vin.resize(1); - txNew.vin[0].prevout.SetNull(); - txNew.vout.resize(1); - txNew.vout[0].scriptPubKey = scriptPubKeyIn; - txNew.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus()); - // Set to 0 so expiry height does not apply to coinbase txs - txNew.nExpiryHeight = 0; - - if ((nHeight > chainparams.GetCommunityFeeStartHeight()) && (nHeight <= chainparams.GetLastCommunityFeeBlockHeight())) { - // Community Fee is 5% of the block subsidy - auto vCommunityFee = txNew.vout[0].nValue * 0.05; - // Take some reward away from us - txNew.vout[0].nValue -= vCommunityFee; - // And give it to the community - txNew.vout.push_back(CTxOut(vCommunityFee, chainparams.GetCommunityFeeScriptAtHeight(nHeight))); + } +} + +void BlockAssembler::addPriorityTxs() +{ + // How much of the block should be dedicated to high-priority transactions, + // included regardless of the fees they pay + unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); + nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); + + if (nBlockPrioritySize == 0) { + return; + } + + // This vector will be sorted into a priority queue: + vector vecPriority; + TxCoinAgePriorityCompare pricomparer; + std::map waitPriMap; + typedef std::map::iterator waitPriIter; + double actualPriority = -1; + + vecPriority.reserve(mempool.mapTx.size()); + for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); + mi != mempool.mapTx.end(); ++mi) + { + double dPriority = mi->GetPriority(nHeight); + CAmount dummy; + mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy); + vecPriority.push_back(TxCoinAgePriority(dPriority, mi)); + } + std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + + CTxMemPool::txiter iter; + while (!vecPriority.empty() && !blockFinished) { // add a tx from priority queue to fill the blockprioritysize + iter = vecPriority.front().second; + actualPriority = vecPriority.front().first; + std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + vecPriority.pop_back(); + + // If tx already in block, skip + if (inBlock.count(iter)) { + assert(false); // shouldn't happen for priority txs + continue; } - // Add fees - txNew.vout[0].nValue += nFees; - txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; + // If tx is dependent on other mempool txs which haven't yet been included + // then put it in the waitSet + if (isStillDependent(iter)) { + waitPriMap.insert(std::make_pair(iter, actualPriority)); + continue; + } - pblock->vtx[0] = txNew; - pblocktemplate->vTxFees[0] = -nFees; + // If this tx fits in the block add it, otherwise keep looping + if (TestForBlock(iter)) { + AddToBlock(iter); - // Update the Sapling commitment tree. - for (const CTransaction& tx : pblock->vtx) { - for (const OutputDescription& odesc : tx.vShieldedOutput) { - sapling_tree.append(odesc.cmu); + // If now that this txs is added we've surpassed our desired priority size + // or have dropped below the AllowFreeThreshold, then we're done adding priority txs + if (nBlockSize >= nBlockPrioritySize || !AllowFree(actualPriority)) { + return; } - } - // Randomise nonce - arith_uint256 nonce = UintToArith256(GetRandHash()); - // Clear the top and bottom 16 bits (for local use as thread flags and counters) - nonce <<= 32; - nonce >>= 16; - pblock->nNonce = ArithToUint256(nonce); - - - // Fill in header - pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->hashFinalSaplingRoot = sapling_tree.root(); - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); - pblock->nSolution.clear(); - pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); - - CValidationState state; - if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { - throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); + // This tx was successfully added, so + // add transactions that depend on this one to the priority queue to try again + for (CTxMemPool::txiter child : mempool.GetMemPoolChildren(iter)) + { + waitPriIter wpiter = waitPriMap.find(child); + if (wpiter != waitPriMap.end()) { + vecPriority.push_back(TxCoinAgePriority(wpiter->second,child)); + std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + waitPriMap.erase(wpiter); + } + } } } - - return pblocktemplate.release(); } ////////////////////////////////////////////////////////////////////////////// @@ -539,7 +624,7 @@ void static BitcoinMiner(const CChainParams& chainparams) unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex* pindexPrev = chainActive.Tip(); - unique_ptr pblocktemplate(CreateNewBlock(chainparams, coinbaseScript->reserveScript)); + unique_ptr pblocktemplate(BlockAssembler(chainparams).CreateNewBlock(coinbaseScript->reserveScript)); if (!pblocktemplate.get()) { if (GetArg("-mineraddress", "").empty()) { diff --git a/src/miner.h b/src/miner.h index 109a69b63..e19819c72 100644 --- a/src/miner.h +++ b/src/miner.h @@ -7,13 +7,18 @@ #define BITCOIN_MINER_H #include "primitives/block.h" +#include "txmempool.h" #include + #include +#include +#include class CBlockIndex; class CChainParams; class CScript; + namespace Consensus { struct Params; }; static const bool DEFAULT_GENERATE = false; @@ -28,7 +33,62 @@ struct CBlockTemplate }; /** Generate a new block, without valid proof-of-work */ -CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn); +class BlockAssembler +{ +private: + // The constructed block template + std::unique_ptr pblocktemplate; + // A convenience pointer that always refers to the CBlock in pblocktemplate + CBlock* pblock; + + // Configuration parameters for the block size + unsigned int nBlockMaxSize, nBlockMinSize; + + // Information on the current status of the block + uint64_t nBlockSize; + uint64_t nBlockTx; + unsigned int nBlockSigOps; + CAmount nFees; + CTxMemPool::setEntries inBlock; + + // Information on the current chain state after this block + CAmount sproutValue; + CAmount saplingValue; + bool monitoring_pool_balances; + + // Chain context for the block + int nHeight; + int64_t nLockTimeCutoff; + const CChainParams& chainparams; + + // Variables used for addScoreTxs and addPriorityTxs + int lastFewTxs; + bool blockFinished; + +public: + BlockAssembler(const CChainParams& chainparams); + /** Construct a new block template with coinbase to scriptPubKeyIn */ + CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); + +private: + // utility functions + /** Clear the block's state and prepare for assembling a new block */ + void resetBlock(); + /** Add a tx to the block */ + void AddToBlock(CTxMemPool::txiter iter); + + // Methods for how to add transactions to a block. + /** Add transactions based on modified feerate */ + void addScoreTxs(); + /** Add transactions based on tx "priority" */ + void addPriorityTxs(); + + // helper function for addScoreTxs and addPriorityTxs + /** Test if tx will still "fit" in the block */ + bool TestForBlock(CTxMemPool::txiter iter); + /** Test if tx still has unconfirmed parents not yet in block */ + bool isStillDependent(CTxMemPool::txiter iter); +}; #ifdef ENABLE_MINING /** Get script for -mineraddress */ diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index c385dc204..b0bf43386 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -212,7 +212,7 @@ UniValue generate(const UniValue& params, bool fHelp) unsigned int n = ehparams[0].n; unsigned int k = ehparams[0].k; - std::unique_ptr pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript)); + std::unique_ptr pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -628,7 +628,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (!coinbaseScript->reserveScript.size()) throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet or -mineraddress)"); - pblocktemplate = CreateNewBlock(Params(), coinbaseScript->reserveScript); + pblocktemplate = BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 62f621802..d8f866af6 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -105,13 +105,13 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) removed.clear(); } -template +template void CheckSort(CTxMemPool &pool, std::vector &sortedOrder) { BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size()); - typename CTxMemPool::indexed_transaction_set::nth_index::type::iterator it = pool.mapTx.get().begin(); + typename CTxMemPool::indexed_transaction_set::index::type::iterator it = pool.mapTx.get().begin(); int count=0; - for (; it != pool.mapTx.get().end(); ++it, ++count) { + for (; it != pool.mapTx.get().end(); ++it, ++count) { BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]); } } @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder[2] = tx1.GetHash().ToString(); // 10000 sortedOrder[3] = tx4.GetHash().ToString(); // 15000 sortedOrder[4] = tx2.GetHash().ToString(); // 20000 - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); /* low fee but with high fee child */ /* tx6 -> tx7 -> tx8, tx9 -> tx10 */ @@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) BOOST_CHECK_EQUAL(pool.size(), 6); // Check that at this point, tx6 is sorted low sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString()); - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); CTxMemPool::setEntries setAncestors; setAncestors.insert(pool.mapTx.find(tx6.GetHash())); @@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.erase(sortedOrder.begin()); sortedOrder.push_back(tx6.GetHash().ToString()); sortedOrder.push_back(tx7.GetHash().ToString()); - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); /* low fee child of tx7 */ CMutableTransaction tx8 = CMutableTransaction(); @@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // Now tx8 should be sorted low, but tx6/tx both high sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString()); - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); /* low fee child of tx7 */ CMutableTransaction tx9 = CMutableTransaction(); @@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // tx9 should be sorted low BOOST_CHECK_EQUAL(pool.size(), 9); sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString()); - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); std::vector snapshotOrder = sortedOrder; @@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString()); sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString()); sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6 - CheckSort<1>(pool, sortedOrder); + CheckSort(pool, sortedOrder); // there should be 10 transactions in the mempool BOOST_CHECK_EQUAL(pool.size(), 10); @@ -285,7 +285,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) // Now try removing tx10 and verify the sort order returns to normal std::list removed; pool.remove(pool.mapTx.find(tx10.GetHash())->GetTx(), removed, true); - CheckSort<1>(pool, snapshotOrder); + CheckSort(pool, snapshotOrder); pool.remove(pool.mapTx.find(tx9.GetHash())->GetTx(), removed, true); pool.remove(pool.mapTx.find(tx8.GetHash())->GetTx(), removed, true); @@ -317,7 +317,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) sortedOrder.push_back(tx3.GetHash().ToString()); sortedOrder.push_back(tx6.GetHash().ToString()); } - CheckSort<2>(pool, sortedOrder); + CheckSort(pool, sortedOrder); } BOOST_AUTO_TEST_CASE(RemoveWithoutBranchId) { diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 484763914..ab661eeda 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -156,7 +156,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCoinbaseEnforcedShieldingEnabled = false; // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); // We can't make transactions until we have inputs // Therefore, load 100 blocks :) @@ -282,9 +282,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) delete pblocktemplate; // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; + const CAmount BLOCKSUBSIDY = 50000; // First slow start subsidy. Upstream: 50*COIN + const CAmount LOWFEE = 10; // Upstream: CENT + const CAmount HIGHFEE = 1000; // Upstream: COIN + const CAmount HIGHERFEE = 4000; // Upstream: 4*COIN + // Define a helper function to check the kind of failure from TestBlockValidity. auto err_is = [](const std::string& msg) { return [&](std::runtime_error const& e) { @@ -300,31 +305,31 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vout.resize(1); - tx.vout[0].nValue = 50000LL; + tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 1001; ++i) { - tx.vout[0].nValue -= 10; + tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-blk-sigops")); + BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-blk-sigops")); mempool.clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); - tx.vout[0].nValue = 50000LL; + tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 1001; ++i) { - tx.vout[0].nValue -= 10; + tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -336,40 +341,40 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].scriptSig << vchData << OP_DROP; tx.vin[0].scriptSig << OP_1; tx.vin[0].prevout.hash = txFirst[0]->GetHash(); - tx.vout[0].nValue = 50000LL; + tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 128; ++i) { - tx.vout[0].nValue -= 350; + tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); // orphan in mempool, template creation fails hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); + BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); mempool.clear(); // child with higher priority than parent tx.vin[0].scriptSig = CScript() << OP_1; tx.vin[0].prevout.hash = txFirst[1]->GetHash(); - tx.vout[0].nValue = 39000LL; + tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin.resize(2); tx.vin[1].scriptSig = CScript() << OP_1; tx.vin[1].prevout.hash = txFirst[0]->GetHash(); tx.vin[1].prevout.n = 0; - tx.vout[0].nValue = 49000LL; + tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(4000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -380,48 +385,48 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 0; hash = tx.GetHash(); // give it a fee so it'll get mined - mempool.addUnchecked(hash, entry.Fee(1).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-cb-multiple")); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-cb-multiple")); mempool.clear(); // P2SH txn in mempool (valid, Zcash always had P2SH enabled) tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vin[0].scriptSig = CScript() << OP_1; - tx.vout[0].nValue = 49000LL; + tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE; script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(100L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); - tx.vout[0].nValue -= 10000; + tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(10).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); // double spend txn pair in mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; - tx.vout[0].nValue = 49000LL; + tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_EXCEPTION(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); mempool.clear(); // subsidy changing int nHeight = chainActive.Height(); chainActive.Tip()->nHeight = 209999; - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = 210000; - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; chainActive.Tip()->nHeight = nHeight; @@ -432,11 +437,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; tx.vin[0].nSequence = 0; - tx.vout[0].nValue = 49000LL; + tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = chainActive.Tip()->nHeight+1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST)); // time locked @@ -446,14 +451,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx2.vin[0].scriptSig = CScript() << OP_1; tx2.vin[0].nSequence = 0; tx2.vout.resize(1); - tx2.vout[0].nValue = 79000LL; + tx2.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; tx2.vout[0].scriptPubKey = CScript() << OP_1; tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1; hash = tx2.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2)); BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); // Neither tx should have made it into the template. BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); @@ -468,7 +473,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) //BOOST_CHECK(CheckFinalTx(tx)); //BOOST_CHECK(CheckFinalTx(tx2)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 2); delete pblocktemplate; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 2db25947b..abd1ff54c 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -164,7 +164,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector& unsigned int n = ehparams[0].n; unsigned int k = ehparams[0].k; - CBlockTemplate *pblocktemplate = CreateNewBlock(chainparams, scriptPubKey); + CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); CBlock& block = pblocktemplate->block; // Replace mempool-selected txns with just coinbase plus passed-in txns: diff --git a/src/txmempool.h b/src/txmempool.h index 195559bc3..adf3cf567 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -236,6 +236,11 @@ class CompareTxMemPoolEntryByEntryTime } }; +// Multi_index tag names +struct descendant_score {}; +struct entry_time {}; +struct mining_score {}; + class CBlockPolicyEstimator; /** An inpoint - a combination of a transaction and an index n into its vin */ @@ -363,16 +368,19 @@ class CTxMemPool boost::multi_index::hashed_unique, // sorted by fee rate boost::multi_index::ordered_non_unique< + boost::multi_index::tag, boost::multi_index::identity, CompareTxMemPoolEntryByFee >, // sorted by entry time boost::multi_index::ordered_non_unique< + boost::multi_index::tag, boost::multi_index::identity, CompareTxMemPoolEntryByEntryTime >, // sorted by score (for mining prioritization) boost::multi_index::ordered_unique< + boost::multi_index::tag, boost::multi_index::identity, CompareTxMemPoolEntryByScore > From 3833367127fb442331e2d0573316967d918d0be3 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 2 Sep 2024 16:52:28 +0000 Subject: [PATCH 132/146] Remove priority and free transactions --- contrib/debian/examples/bitcoinz.conf | 45 ++++--- doc/release-notes.md | 33 +++++ qa/rpc-tests/addressindex.py | 11 +- qa/rpc-tests/finalsaplingroot.py | 4 +- qa/rpc-tests/fundrawtransaction.py | 2 +- qa/rpc-tests/mempool_packages.py | 46 +++++-- qa/rpc-tests/mempool_reorg.py | 6 +- qa/rpc-tests/mempool_resurrect_test.py | 6 +- qa/rpc-tests/mempool_spendcoinbase.py | 6 +- src/amount.cpp | 3 +- src/amount.h | 3 +- src/coins.cpp | 31 ----- src/coins.h | 15 +-- src/gtest/test_mempool.cpp | 5 +- src/init.cpp | 23 ++-- src/main.cpp | 82 +++--------- src/main.h | 16 +-- src/miner.cpp | 85 +----------- src/miner.h | 2 - src/policy/fees.cpp | 155 +++++----------------- src/policy/fees.h | 109 ++++++--------- src/policy/policy.cpp | 2 +- src/policy/policy.h | 51 ++++++- src/primitives/transaction.cpp | 43 +++--- src/primitives/transaction.h | 29 +--- src/rpc/blockchain.cpp | 8 +- src/rpc/client.cpp | 1 - src/rpc/mining.cpp | 51 ++----- src/rpc/misc.cpp | 18 +-- src/rpc/net.cpp | 2 +- src/rpc/rawtransaction.cpp | 2 +- src/test/mempool_tests.cpp | 9 +- src/test/miner_tests.cpp | 5 +- src/test/policyestimator_tests.cpp | 54 +++----- src/test/rpc_tests.cpp | 2 +- src/test/test_bitcoin.cpp | 2 +- src/test/test_bitcoin.h | 4 +- src/test/transaction_tests.cpp | 16 +-- src/txmempool.cpp | 100 ++++++-------- src/txmempool.h | 89 +++++-------- src/wallet/asyncrpcoperation_sendmany.cpp | 2 +- src/wallet/rpcwallet.cpp | 10 +- src/wallet/test/wallet_tests.cpp | 4 +- src/wallet/wallet.cpp | 77 ++++------- src/wallet/wallet.h | 9 +- 45 files changed, 474 insertions(+), 804 deletions(-) diff --git a/contrib/debian/examples/bitcoinz.conf b/contrib/debian/examples/bitcoinz.conf index 70ee50bd2..82e3a0c9d 100644 --- a/contrib/debian/examples/bitcoinz.conf +++ b/contrib/debian/examples/bitcoinz.conf @@ -2,7 +2,9 @@ ## bitcoinz.conf configuration file. Lines beginning with # are comments. ## -# Network-related settings: +## +## Network-related settings +## # Run on the test network instead of the real BitcoinZ network. #testnet=0 @@ -56,9 +58,9 @@ # Maximum number of inbound+outbound connections. #maxconnections= -# -# JSON-RPC options (for controlling a running BitcoinZ/bitcoinzd process) -# +## +## JSON-RPC options (for controlling a running Zcash/zcashd process) +## # server=1 tells bitcoinzd to accept JSON-RPC commands (set as default if not specified) #server=1 @@ -95,18 +97,31 @@ # running on another host using this option: #rpcconnect=127.0.0.1 -# Transaction Fee +## +## Transaction Fee +## + +# The preferred fee rate (in BTCZ per 1000 bytes) used for transactions +# created by APIs. If the transaction is less than 1000 bytes then +# the fee rate is applied as though it were 1000 bytes. +#paytxfee= -# Send transactions as zero-fee transactions if possible (default: 0) -#sendfreetransactions=0 +# If paytxfee is not set, include enough fee that transactions created by +# APIs begin confirmation on average within n blocks. This is only +# used if there is sufficient mempool data to estimate the fee; if not, +# the fallback fee set by mintxfee is used. +#txconfirmtarget=2 -# Create transactions that have enough fees (or priority) so they are likely to # begin confirmation within n blocks (default: 1). -# This setting is overridden by the -paytxfee option. -#txconfirmtarget=n +# The fallback fee rate (in BTCZ per 1000 bytes) used by APIs when +# paytxfee has not been set and there is insufficient mempool data to +# estimate a fee according to the txconfirmtarget option. +#mintxfee=0.00001 -# Miscellaneous options +## +## Miscellaneous options +## -# Enable attempt to mine BitcoinZ. +# Enable attempt to CPU-mine BitcoinZ. This is only useful on testnet. #gen=0 # Set the number of threads to be used for mining BitcoinZ (-1 = all cores). @@ -119,9 +134,3 @@ # Pre-generate this many public/private key pairs, so wallet backups will be valid for # both prior transactions and several dozen future transactions. #keypool=100 - -# Pay an optional transaction fee every time you send BitcoinZ. Transactions with fees -# are more likely than free transactions to be included in generated blocks, so may -# be validated sooner. This setting does not affect private transactions created with -# 'z_sendmany'. -#paytxfee=0.00 diff --git a/doc/release-notes.md b/doc/release-notes.md index 1bce09644..0841351fd 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -86,6 +86,39 @@ client first. ### RPC and REST +RPC Changes +----------- + +- The `estimatepriority` RPC call has been removed. +- The `priority_delta` argument to the `prioritisetransaction` RPC call now has + no effect and must be set to a dummy value (0 or null). + +Changes to Transaction Fee Selection +------------------------------------ + +- The `-sendfreetransactions` option has been removed. This option used to + instruct the wallet's legacy transaction creation APIs (`sendtoaddress`, + `sendmany`, and `fundrawtransaction`) to use a zero fee for "small" transactions + that spend "old" inputs. It will now cause a warning on node startup if used. + +Changes to Block Template Construction +-------------------------------------- + +- The block template construction algorithm no longer favours transactions that + were previously considered "high priority" because they spent older inputs. The + `-blockprioritysize` config option, which configured the portion of the block + reserved for these transactions, has been removed and will now cause a warning + if used. + +Removal of Priority Estimation +------------------------------ + +- Estimation of "priority" needed for a transaction to be included within a target + number of blocks, and the associated `estimatepriority` RPC call, have been + removed. The format for `fee_estimates.dat` has also changed to no longer save + these priority estimates. It will automatically be converted to the new format + which is not readable by prior versions of the software. + ### Configuration and command-line options ### Block and transaction handling diff --git a/qa/rpc-tests/addressindex.py b/qa/rpc-tests/addressindex.py index 066c8be0c..163b01f30 100755 --- a/qa/rpc-tests/addressindex.py +++ b/qa/rpc-tests/addressindex.py @@ -51,10 +51,17 @@ def setup_chain(self): initialize_chain_clean(self.options.tmpdir, 4) def setup_network(self): + base_args = [ + '-minrelaytxfee=0', + '-debug', + '-txindex', + '-experimentalfeatures', + '-allowdeprecated=getnewaddress', + ] # -insightexplorer causes addressindex to be enabled (fAddressIndex = true) - args_insight = ('-debug', '-txindex', '-experimentalfeatures', '-insightexplorer') + args_insight = base_args + ['-insightexplorer'] # -lightwallet also causes addressindex to be enabled - args_lightwallet = ('-debug', '-txindex', '-experimentalfeatures', '-lightwalletd') + args_lightwallet = base_args + ['-lightwalletd'] self.nodes = start_nodes(4, self.options.tmpdir, [args_insight] * 3 + [args_lightwallet]) connect_nodes(self.nodes[0], 1) diff --git a/qa/rpc-tests/finalsaplingroot.py b/qa/rpc-tests/finalsaplingroot.py index 1fa918767..53a921526 100755 --- a/qa/rpc-tests/finalsaplingroot.py +++ b/qa/rpc-tests/finalsaplingroot.py @@ -31,7 +31,9 @@ def setup_chain(self): def setup_network(self, split=False): self.nodes = start_nodes(4, self.options.tmpdir, extra_args=[[ - '-txindex' # Avoid JSONRPC error: No information available about transaction + '-minrelaytxfee=0', + '-txindex', # Avoid JSONRPC error: No information available about transaction + '-reindex', # Required due to enabling -txindex ]] * 4 ) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 09da3d810..7d44ea9c3 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -416,7 +416,7 @@ def run_test(self): mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) - # send 1.2 BTC to msig addr + # send 1.2 BTCZ to multisig address txId = self.nodes[0].sendtoaddress(mSigObj, 1.2); self.sync_all() self.nodes[1].generate(1) diff --git a/qa/rpc-tests/mempool_packages.py b/qa/rpc-tests/mempool_packages.py index 5b549a585..1a63f40e3 100644 --- a/qa/rpc-tests/mempool_packages.py +++ b/qa/rpc-tests/mempool_packages.py @@ -16,6 +16,7 @@ sync_blocks, sync_mempools, ) +from test_framework.mininode import COIN def satoshi_round(amount): return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) @@ -24,10 +25,9 @@ class MempoolPackagesTest(BitcoinTestFramework): def setup_network(self): base_args = [ - "-maxorphantx=1000", - "-relaypriority=0", - "-debug", - "-allowdeprecated=getnewaddress", + '-minrelaytxfee=0', + '-maxorphantx=%d' % (self.maxorphantx,), + '-debug', ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, base_args)) @@ -74,22 +74,45 @@ def run_test(self): descendant_count = 1 descendant_fees = 0 descendant_size = 0 - SATOSHIS = 100000000 for x in reversed(chain): assert_equal(mempool[x]['descendantcount'], descendant_count) descendant_fees += mempool[x]['fee'] - assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees) + assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']) + assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN) descendant_size += mempool[x]['size'] assert_equal(mempool[x]['descendantsize'], descendant_size) descendant_count += 1 + # Check that descendant modified fees includes fee deltas from + # prioritisetransaction + self.nodes[0].prioritisetransaction(chain[-1], 0, 1000) + mempool = self.nodes[0].getrawmempool(True) + + descendant_fees = 0 + for x in reversed(chain): + descendant_fees += mempool[x]['fee'] + assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000) + # Adding one more transaction on to the chain should fail. try: self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1) except JSONRPCException: print("too-long-ancestor-chain successfully rejected") + # Check that prioritising a tx before it's added to the mempool works + self.nodes[0].generate(1) + self.nodes[0].prioritisetransaction(chain[-1], None, 2000) + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + mempool = self.nodes[0].getrawmempool(True) + + descendant_fees = 0 + for x in reversed(chain): + descendant_fees += mempool[x]['fee'] + if (x == chain[-1]): + assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002)) + assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 2000) + # TODO: check that node1's mempool is as expected # TODO: test ancestor size limits @@ -106,18 +129,18 @@ def run_test(self): for i in range(10): transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value}) - for i in range(1000): + for i in range(self.maxorphantx): utxo = transaction_package.pop(0) try: (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) for j in range(10): transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value}) - if i == 998: + if i == self.maxorphantx-2: mempool = self.nodes[0].getrawmempool(True) - assert_equal(mempool[parent_transaction]['descendantcount'], 1000) + assert_equal(mempool[parent_transaction]['descendantcount'], self.maxorphantx) except JSONRPCException as e: print(e.error['message']) - assert_equal(i, 999) + assert_equal(i, self.maxorphantx-1) print("tx that would create too large descendant package successfully rejected") # TODO: check that node1's mempool is as expected @@ -127,7 +150,8 @@ def run_test(self): # Test reorg handling # First, the basics: self.nodes[0].generate(1) - sync_blocks(self.nodes) + print("syncing blocks") + sync_blocks(self.nodes, timeout=480) self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash()) self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash()) diff --git a/qa/rpc-tests/mempool_reorg.py b/qa/rpc-tests/mempool_reorg.py index dd966f07c..6b327f40d 100644 --- a/qa/rpc-tests/mempool_reorg.py +++ b/qa/rpc-tests/mempool_reorg.py @@ -21,7 +21,11 @@ class MempoolCoinbaseTest(BitcoinTestFramework): alert_filename = None # Set by setup_network def setup_network(self): - args = ["-checkmempool", "-debug=mempool"] + args = [ + '-minrelaytxfee=0', + '-checkmempool', + '-debug=mempool', + ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, args)) self.nodes.append(start_node(1, self.options.tmpdir, args)) diff --git a/qa/rpc-tests/mempool_resurrect_test.py b/qa/rpc-tests/mempool_resurrect_test.py index 95155b60c..a7fa259a4 100755 --- a/qa/rpc-tests/mempool_resurrect_test.py +++ b/qa/rpc-tests/mempool_resurrect_test.py @@ -20,7 +20,11 @@ class MempoolCoinbaseTest(BitcoinTestFramework): def setup_network(self): # Just need one node for this test - args = ["-checkmempool", "-debug=mempool"] + args = [ + '-minrelaytxfee=0', + '-checkmempool', + '-debug=mempool', + ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, args)) self.is_network_split = False diff --git a/qa/rpc-tests/mempool_spendcoinbase.py b/qa/rpc-tests/mempool_spendcoinbase.py index 2a999e8e1..d963cb80b 100755 --- a/qa/rpc-tests/mempool_spendcoinbase.py +++ b/qa/rpc-tests/mempool_spendcoinbase.py @@ -26,7 +26,11 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework): def setup_network(self): # Just need one node for this test - args = ["-checkmempool", "-debug=mempool"] + args = [ + '-minrelaytxfee=0', + '-checkmempool', + '-debug=mempool', + ] self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, args)) self.is_network_split = False diff --git a/src/amount.cpp b/src/amount.cpp index 354bb6c00..f719a6bf8 100644 --- a/src/amount.cpp +++ b/src/amount.cpp @@ -9,6 +9,7 @@ #include "tinyformat.h" const std::string CURRENCY_UNIT = "BTCZ"; +const std::string MINOR_CURRENCY_UNIT = "zatoshis"; CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nSize) { @@ -35,5 +36,5 @@ CAmount CFeeRate::GetFee(size_t nSize) const std::string CFeeRate::ToString() const { - return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT); + return strprintf("%d.%08d %s per 1000 bytes", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT); } diff --git a/src/amount.h b/src/amount.h index 54d00b52f..890b931a1 100644 --- a/src/amount.h +++ b/src/amount.h @@ -17,10 +17,11 @@ static const CAmount COIN = 100000000; static const CAmount CENT = 1000000; extern const std::string CURRENCY_UNIT; +extern const std::string MINOR_CURRENCY_UNIT; /** No amount larger than this (in zatoshi) is valid. * - * Note that this constant is *not* the total money supply, which in Bitcoin + * Note that this constant is *not* the total money supply, which in BitcoinZ * currently happens to be less than 21,000,000,000 BTCZ for various reasons, but * rather a sanity check. As this sanity check is used by consensus-critical * validation code, the exact value of the MAX_MONEY constant is consensus diff --git a/src/coins.cpp b/src/coins.cpp index ea75b1a00..554f90260 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -639,37 +639,6 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const return true; } -double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const -{ - inChainInputValue = 0; - if (tx.IsCoinBase()) - return 0.0; - - // Shielded transfers do not reveal any information about the value or age of a note, so we - // cannot apply the priority algorithm used for transparent utxos. Instead, we just - // use the maximum priority for all (partially or fully) shielded transactions. - // (Note that coinbase transactions cannot contain JoinSplits, or Sapling shielded Spends or Outputs.) - - if (tx.vJoinSplit.size() > 0 || tx.vShieldedSpend.size() > 0 || tx.vShieldedOutput.size() > 0) { - return MAX_PRIORITY; - } - - // FIXME: this logic is partially duplicated between here and CreateNewBlock in miner.cpp. - double dResult = 0.0; - for (const CTxIn& txin : tx.vin) - { - const CCoins* coins = AccessCoins(txin.prevout.hash); - assert(coins); - if (!coins->IsAvailable(txin.prevout.n)) continue; - if (coins->nHeight <= nHeight) { - dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); - inChainInputValue += coins->vout[txin.prevout.n].nValue; - } - } - - return tx.ComputePriority(dResult); -} - CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage) : cache(cache_), it(it_), cachedCoinUsage(usage) { assert(!cache.hasModifier); cache.hasModifier = true; diff --git a/src/coins.h b/src/coins.h index 2a95a3fe4..2cb3f2650 100644 --- a/src/coins.h +++ b/src/coins.h @@ -46,7 +46,7 @@ * - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow) * - unspentness bitvector: as 0 non-zero bytes follow, it has length 0 * - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35 - * * 8358: compact amount representation for 60000000000 (600 BTC) + * * 8358: compact amount representation for 60000000000 (600 BTCZ) * * 00: special txout type pay-to-pubkey-hash * * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160 * - height = 203998 @@ -62,11 +62,11 @@ * 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow) * - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent * - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee - * * 86ef97d579: compact amount representation for 234925952 (2.35 BTC) + * * 86ef97d579: compact amount representation for 234925952 (2.35 BTCZ) * * 00: special txout type pay-to-pubkey-hash * * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160 * - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 - * * bbd123: compact amount representation for 110397 (0.001 BTC) + * * bbd123: compact amount representation for 110397 (0.001 BTCZ) * * 00: special txout type pay-to-pubkey-hash * * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160 * - height = 120891 @@ -553,16 +553,9 @@ class CCoinsViewCache : public CCoinsViewBacked //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx) const; - //! Check whether all joinsplit and sapling spend requirements (anchors/nullifiers) are satisfied + //! Check whether all shielded spend requirements (anchors/nullifiers) are satisfied bool HaveShieldedRequirements(const CTransaction& tx) const; - /** - * Return priority of tx at height nHeight. Also calculate the sum of the values of the inputs - * that are already in the chain. These are the inputs that will age and increase priority as - * new blocks are added to the chain. - */ - double GetPriority(const CTransaction &tx, int nHeight, CAmount &inChainInputValue) const; - const CTxOut &GetOutputFor(const CTxIn& input) const; friend class CCoinsModifier; diff --git a/src/gtest/test_mempool.cpp b/src/gtest/test_mempool.cpp index 872ce1924..1b46dc851 100644 --- a/src/gtest/test_mempool.cpp +++ b/src/gtest/test_mempool.cpp @@ -93,14 +93,11 @@ TEST(Mempool, PriorityStatsDoNotCrash) { int64_t nTime = 0x58e5fed9; unsigned int nHeight = 92045; CAmount inChainInputValue; - double dPriority = view.GetPriority(tx, nHeight, inChainInputValue); - CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, inChainInputValue, false, 0, SPROUT_BRANCH_ID); + CTxMemPoolEntry entry(tx, nFees, nTime, nHeight, true, inChainInputValue, false, 0, SPROUT_BRANCH_ID); // Check it does not crash (ie. the death test fails) EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), ""); - - EXPECT_EQ(dPriority, MAX_PRIORITY); } // Valid overwinter v3 format tx gets rejected because overwinter hasn't activated yet. diff --git a/src/init.cpp b/src/init.cpp index 1b8499643..848026b73 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -439,19 +439,17 @@ std::string HelpMessage(HelpMessageMode mode) if (showDebug) { strUsage += HelpMessageOpt("-mocktime=", "Replace actual time with seconds since epoch (default: 0)"); - strUsage += HelpMessageOpt("-limitfreerelay=", strprintf("Continuously rate-limit free transactions to *1000 bytes per minute (default: %u)", DEFAULT_LIMITFREERELAY)); - strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", DEFAULT_RELAYPRIORITY)); strUsage += HelpMessageOpt("-maxsigcachesize=", strprintf("Limit size of signature cache to MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); strUsage += HelpMessageOpt("-maxtipage=", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE)); } - strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), + strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Transactions must have at least this fee rate (in %s per 1000 bytes) for relaying, mining and transaction creation (default: %s). This is not the only fee constraint."), CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE))); strUsage += HelpMessageOpt("-maxtxfee=", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE))); strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); if (showDebug) { - strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction priority and fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY)); + strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY)); } strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); @@ -464,7 +462,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Block creation options:")); strUsage += HelpMessageOpt("-blockminsize=", strprintf(_("Set minimum block size in bytes (default: %u)"), DEFAULT_BLOCK_MIN_SIZE)); strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); - strUsage += HelpMessageOpt("-blockprioritysize=", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); if (GetBoolArg("-help-debug", false)) strUsage += HelpMessageOpt("-blockversion=", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); @@ -1174,16 +1171,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; - // Fee-per-kilobyte amount considered the same as "free" - // If you are mining, be careful setting this: - // if you set it to zero then - // a transaction spammer can cheaply fill blocks using - // 1-satoshi-fee transactions. It should be set above the real - // cost to you of processing a transaction. + // Fee rate in zatoshis per 1000 bytes required for mempool acceptance and relay. + // TODO(update when ZIP 317 is implemented): + // If you are mining, be careful setting this. If you set it too low then a + // transaction spammer can cheaply fill blocks. It should be set above the real if (mapArgs.count("-minrelaytxfee")) { CAmount n = 0; - if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) + if (ParseMoney(mapArgs["-minrelaytxfee"], n)) ::minRelayTxFee = CFeeRate(n); else return InitError(strprintf(_("Invalid amount for -minrelaytxfee=: '%s'"), mapArgs["-minrelaytxfee"])); @@ -1217,6 +1212,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif + if (GetArg("-blockprioritysize", 0) != 0) { + InitWarning(_("The argument -blockprioritysize is no longer supported.")); + } + if (!mapMultiArgs["-nuparams"].empty()) { // Allow overriding network upgrade parameters for testing if (Params().NetworkIDString() != "regtest") { diff --git a/src/main.cpp b/src/main.cpp index 02d587572..4ba22ea07 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1238,32 +1238,6 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return true; } -CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree) -{ - uint256 hash = tx.GetHash(); - double dPriorityDelta = 0; - CAmount nFeeDelta = 0; - pool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); - if (dPriorityDelta > 0 || nFeeDelta > 0) - return 0; - - CAmount nMinFee = ::minRelayTxFee.GetFeeForRelay(nBytes); - - if (fAllowFree) - { - // There is a free transaction area in blocks created by most miners, - // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 - // to be considered to fall into this category. We don't want to encourage sending - // multiple transactions instead of one big transaction to avoid fees. - if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) - nMinFee = 0; - } - - if (!MoneyRange(nMinFee)) - nMinFee = MAX_MONEY; - return nMinFee; -} - /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state) { @@ -1419,7 +1393,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; CAmount inChainInputValue; - double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue); + // nModifiedFees includes any fee deltas from PrioritiseTransaction + CAmount nModifiedFees = nFees; + pool.ApplyDelta(hash, nModifiedFees); // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. @@ -1437,49 +1413,21 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // it has passed ContextualCheckInputs and therefore this is correct. auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, consensusBranchId); + CTxMemPoolEntry entry(tx, nFees, GetTime(), chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, consensusBranchId); unsigned int nSize = entry.GetTxSize(); if (txFeeRate) { *txFeeRate = CFeeRate(nFees, nSize); } - // Before zcashd 4.2.0, we had a condition here to always accept a tx if it contained - // JoinSplits and had at least the default fee. It is no longer necessary to treat - // that as a special case, because the fee returned by GetMinRelayFee is always at - // most DEFAULT_FEE. - - CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true); - if (fLimitFree && nFees < txMinFee) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, - strprintf("%d < %d", nFees, txMinFee)); - } - - // Require that free transactions have sufficient priority to be mined in the next block. - if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFeeForRelay(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); - } - - // Continuously rate-limit free (really, very-low-fee) transactions - // This mitigates 'penny-flooding' -- sending thousands of free transactions just to - // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFeeForRelay(nSize)) - { - static CCriticalSection csFreeLimiter; - static double dFreeCount; - static int64_t nLastTime; - int64_t nNow = GetTime(); - - LOCK(csFreeLimiter); - - // Use an exponentially decaying ~10-minute window: - dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - // -limitfreerelay unit is thousand-bytes-per-minute - // At default rate it would take over a month to fill 1GB - if (dFreeCount >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000) - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction"); - LogPrint(BCLog::MEMPOOL, "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); - dFreeCount += nSize; + // No transactions are allowed with modified fee below the minimum relay fee, + // except from disconnected blocks. The minimum relay fee will never be more + // than DEFAULT_FEE zatoshis. + CAmount minRelayFee = ::minRelayTxFee.GetFeeForRelay(nSize); + if (fLimitFree && nModifiedFees < minRelayFee) { + LogPrint(BCLog::MEMPOOL, + "Not accepting transaction with txid %s, size %d bytes, effective fee %d zatoshis and fee delta %d zatoshis to the mempool due to insufficient fee. The minimum acceptance/relay fee for this transaction is %d zatoshis", + tx.GetHash().ToString(), nSize, nModifiedFees, nModifiedFees - nFees, minRelayFee); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met"); } if (nAbsurdFee && nFees > nAbsurdFee) @@ -5918,7 +5866,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString()); } // Has inputs but not accepted to mempool - // Probably non-standard or insufficient fee/priority + // Probably non-standard or insufficient fee LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString()); vEraseQueue.push_back(orphanHash); assert(recentRejects); @@ -6910,6 +6858,8 @@ bool SendMessages(CNode* pto) int64_t timeNow = GetTimeMicros(); if (timeNow > pto->nextSendTimeFeeFilter) { CAmount filterToSend = filterRounder.round(currentFilter); + // We always have a fee filter of at least minRelayTxFee + filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK()); if (filterToSend != pto->lastSentFeeFilter) { pto->PushMessage(NetMsgType::FEEFILTER, filterToSend); pto->lastSentFeeFilter = filterToSend; diff --git a/src/main.h b/src/main.h index bd9318792..d4888d5d3 100644 --- a/src/main.h +++ b/src/main.h @@ -61,13 +61,13 @@ static const unsigned int MAX_REORG_LENGTH = COINBASE_MATURITY - 1; static const bool DEFAULT_WHITELISTRELAY = true; /** Default for DEFAULT_WHITELISTFORCERELAY. */ static const bool DEFAULT_WHITELISTFORCERELAY = true; -/** Default for -minrelaytxfee, minimum relay fee for transactions */ +/** Default for -minrelaytxfee, minimum relay fee rate for transactions in zatoshis per 1000 bytes. TODO(misnamed, this is a rate) */ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100; -//! -maxtxfee default +/** Default for -maxtxfee in zatoshis. */ static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN; -//! Discourage users to set fees higher than this amount (in satoshis) per kB +/** Discourage users from setting fee rates higher than this in zatoshis per 1000 bytes. */ static const CAmount HIGH_TX_FEE_PER_KB = 0.01 * COIN; -//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis) +/** Warn if -maxtxfee is set to a fee higher than this in zatoshis. */ static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB; /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; @@ -136,8 +136,6 @@ static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 2000000; /** Additional block download timeout per parallel downloading peer (i.e. 5 min) */ static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000; -static const unsigned int DEFAULT_LIMITFREERELAY = 15; -static const bool DEFAULT_RELAYPRIORITY = false; static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; /** Default for -permitbaremultisig */ @@ -203,9 +201,9 @@ extern bool fCheckpointsEnabled; // it is unneeded for testing extern bool fCoinbaseEnforcedShieldingEnabled; extern size_t nCoinCacheUsage; -/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */ +/** Transactions must have at least this fee rate (in zatoshis per 1000 bytes) for relaying, mining and transaction creation. */ extern CFeeRate minRelayTxFee; -/** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */ +/** Absolute maximum transaction fee (in zatoshis) used by wallet and mempool (rejects high fee in sendrawtransaction). */ extern CAmount maxTxFee; /** If the tip is older than this (in seconds), the node is considered to be in initial block download. */ extern int64_t nMaxTipAge; @@ -348,8 +346,6 @@ struct CNodeStateStats { std::vector vHeightInFlight; }; -CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree); - /** * Count ECDSA signature operations the old-fashioned (pre-0.6) way * @return number of sigops this transaction's outputs will produce when spent diff --git a/src/miner.cpp b/src/miner.cpp index d8072cec4..b7f17933f 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -49,8 +49,8 @@ using namespace std; // // Unconfirmed transactions in the memory pool often depend on other // transactions in the memory pool. When we select transactions from the -// pool, we select by highest priority or fee rate, so we might consider -// transactions that depend on transactions that aren't yet in the block. +// pool, we select by highest fee rate, so we might consider transactions +// that depend on transactions that aren't yet in the block. uint64_t nLastBlockTx = 0; uint64_t nLastBlockSize = 0; @@ -172,7 +172,6 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) } } - addPriorityTxs(); addScoreTxs(); nLastBlockTx = nBlockTx; @@ -336,12 +335,8 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); if (fPrintPriority) { - double dPriority = iter->GetPriority(nHeight); - CAmount dummy; - mempool.ApplyDeltas(iter->GetTx().GetHash(), dPriority, dummy); - LogPrintf("%s: priority %.1f fee %s txid %s\n", + LogPrintf("%s: fee %s txid %s\n", __func__, - dPriority, CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(), iter->GetTx().GetHash().ToString()); } @@ -405,80 +400,6 @@ void BlockAssembler::addScoreTxs() } } -void BlockAssembler::addPriorityTxs() -{ - // How much of the block should be dedicated to high-priority transactions, - // included regardless of the fees they pay - unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); - nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); - - if (nBlockPrioritySize == 0) { - return; - } - - // This vector will be sorted into a priority queue: - vector vecPriority; - TxCoinAgePriorityCompare pricomparer; - std::map waitPriMap; - typedef std::map::iterator waitPriIter; - double actualPriority = -1; - - vecPriority.reserve(mempool.mapTx.size()); - for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); - mi != mempool.mapTx.end(); ++mi) - { - double dPriority = mi->GetPriority(nHeight); - CAmount dummy; - mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy); - vecPriority.push_back(TxCoinAgePriority(dPriority, mi)); - } - std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - - CTxMemPool::txiter iter; - while (!vecPriority.empty() && !blockFinished) { // add a tx from priority queue to fill the blockprioritysize - iter = vecPriority.front().second; - actualPriority = vecPriority.front().first; - std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - vecPriority.pop_back(); - - // If tx already in block, skip - if (inBlock.count(iter)) { - assert(false); // shouldn't happen for priority txs - continue; - } - - // If tx is dependent on other mempool txs which haven't yet been included - // then put it in the waitSet - if (isStillDependent(iter)) { - waitPriMap.insert(std::make_pair(iter, actualPriority)); - continue; - } - - // If this tx fits in the block add it, otherwise keep looping - if (TestForBlock(iter)) { - AddToBlock(iter); - - // If now that this txs is added we've surpassed our desired priority size - // or have dropped below the AllowFreeThreshold, then we're done adding priority txs - if (nBlockSize >= nBlockPrioritySize || !AllowFree(actualPriority)) { - return; - } - - // This tx was successfully added, so - // add transactions that depend on this one to the priority queue to try again - for (CTxMemPool::txiter child : mempool.GetMemPoolChildren(iter)) - { - waitPriIter wpiter = waitPriMap.find(child); - if (wpiter != waitPriMap.end()) { - vecPriority.push_back(TxCoinAgePriority(wpiter->second,child)); - std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - waitPriMap.erase(wpiter); - } - } - } - } -} - ////////////////////////////////////////////////////////////////////////////// // // Internal miner diff --git a/src/miner.h b/src/miner.h index e19819c72..64fc56eb1 100644 --- a/src/miner.h +++ b/src/miner.h @@ -80,8 +80,6 @@ class BlockAssembler // Methods for how to add transactions to a block. /** Add transactions based on modified feerate */ void addScoreTxs(); - /** Add transactions based on tx "priority" */ - void addPriorityTxs(); // helper function for addScoreTxs and addPriorityTxs /** Test if tx will still "fit" in the block */ diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 783effa8e..d8dea32a3 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -12,10 +12,9 @@ #include "util.h" void TxConfirmStats::Initialize(std::vector& defaultBuckets, - unsigned int maxConfirms, double _decay, std::string _dataTypeString) + unsigned int maxConfirms, double _decay) { decay = _decay; - dataTypeString = _dataTypeString; buckets.insert(buckets.end(), defaultBuckets.begin(), defaultBuckets.end()); buckets.push_back(std::numeric_limits::infinity()); @@ -95,10 +94,10 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, int maxbucketindex = buckets.size() - 1; - // requireGreater means we are looking for the lowest fee/priority such that all higher - // values pass, so we start at maxbucketindex (highest fee) and look at succesively + // requireGreater means we are looking for the lowest feerate such that all higher + // values pass, so we start at maxbucketindex (highest feerate) and look at successively // smaller buckets until we reach failure. Otherwise, we are looking for the highest - // fee/priority such that all lower values fail, and we go in the opposite direction. + // feerate such that all lower values fail, and we go in the opposite direction. unsigned int startbucket = requireGreater ? maxbucketindex : 0; int step = requireGreater ? -1 : 1; @@ -115,7 +114,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, bool foundAnswer = false; unsigned int bins = unconfTxs.size(); - // Start counting from highest(default) or lowest fee/pri transactions + // Start counting from highest(default) or lowest feerate transactions for (int bucket = startbucket; bucket >= 0 && bucket <= maxbucketindex; bucket += step) { curFarBucket = bucket; nConf += confAvg[confTarget - 1][bucket]; @@ -153,8 +152,8 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, double median = -1; double txSum = 0; - // Calculate the "average" fee of the best bucket range that met success conditions - // Find the bucket with the median transaction and then report the average fee from that bucket + // Calculate the "average" feerate of the best bucket range that met success conditions + // Find the bucket with the median transaction and then report the average feerate from that bucket // This is a compromise between finding the median which we can't since we don't save all tx's // and reporting the average which is less accurate unsigned int minBucket = bestNearBucket < bestFarBucket ? bestNearBucket : bestFarBucket; @@ -174,8 +173,8 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal, } } - LogPrint(BCLog::ESTIMATEFEE, "%3d: For conf success %s %4.2f need %s %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n", - confTarget, requireGreater ? ">" : "<", successBreakPoint, dataTypeString, + LogPrint(BCLog::ESTIMATEFEE, "%3d: For conf success %s %4.2f need feerate %s: %12.5g from buckets %8g - %8g Cur Bucket stats %6.2f%% %8.1f/(%.1f+%d mempool)\n", + confTarget, requireGreater ? ">" : "<", successBreakPoint, requireGreater ? ">" : "<", median, buckets[minBucket], buckets[maxBucket], 100 * nConf / (totalNum + extraNum), nConf, totalNum, extraNum); @@ -208,10 +207,10 @@ void TxConfirmStats::Read(CAutoFile& filein) filein >> fileBuckets; numBuckets = fileBuckets.size(); if (numBuckets <= 1 || numBuckets > 1000) - throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 fee/pri buckets"); + throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets"); filein >> fileAvg; if (fileAvg.size() != numBuckets) - throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri average bucket count"); + throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count"); filein >> fileTxCtAvg; if (fileTxCtAvg.size() != numBuckets) throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count"); @@ -221,9 +220,9 @@ void TxConfirmStats::Read(CAutoFile& filein) throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms"); for (unsigned int i = 0; i < maxConfirms; i++) { if (fileConfAvg[i].size() != numBuckets) - throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri conf average bucket count"); + throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count"); } - // Now that we've processed the entire fee estimate data file and not + // Now that we've processed the entire feerate estimate data file and not // thrown any errors, we can copy it to our data structures decay = fileDecay; buckets = fileBuckets; @@ -250,8 +249,8 @@ void TxConfirmStats::Read(CAutoFile& filein) for (unsigned int i = 0; i < buckets.size(); i++) bucketMap[buckets[i]] = i; - LogPrint(BCLog::ESTIMATEFEE, "Reading estimates: %u %s buckets counting confirms up to %u blocks\n", - numBuckets, dataTypeString, maxConfirms); + LogPrint(BCLog::ESTIMATEFEE, "Reading estimates: %u buckets counting confirms up to %u blocks\n", + numBuckets, maxConfirms); } unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) @@ -259,7 +258,6 @@ unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) unsigned int bucketindex = FindBucketIndex(val); unsigned int blockIndex = nBlockHeight % unconfTxs.size(); unconfTxs[blockIndex][bucketindex]++; - LogPrint(BCLog::ESTIMATEFEE, "adding to %s", dataTypeString); return bucketindex; } @@ -301,12 +299,10 @@ void CBlockPolicyEstimator::removeTx(uint256 hash) hash.ToString().c_str()); return; } - TxConfirmStats *stats = pos->second.stats; unsigned int entryHeight = pos->second.blockHeight; unsigned int bucketIndex = pos->second.bucketIndex; - if (stats != NULL) - stats->removeTx(entryHeight, nBestSeenHeight, bucketIndex); + feeStats.removeTx(entryHeight, nBestSeenHeight, bucketIndex); mapMemPoolTxs.erase(hash); } @@ -318,47 +314,18 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee) for (double bucketBoundary = minTrackedFee.GetFeePerK(); bucketBoundary <= MAX_FEERATE; bucketBoundary *= FEE_SPACING) { vfeelist.push_back(bucketBoundary); } - feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "FeeRate"); - minTrackedPriority = AllowFreeThreshold() < MIN_PRIORITY ? MIN_PRIORITY : AllowFreeThreshold(); - std::vector vprilist; - for (double bucketBoundary = minTrackedPriority; bucketBoundary <= MAX_PRIORITY; bucketBoundary *= PRI_SPACING) { - vprilist.push_back(bucketBoundary); - } - priStats.Initialize(vprilist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "Priority"); - - feeUnlikely = CFeeRate(0); - feeLikely = CFeeRate(INF_FEERATE); - priUnlikely = 0; - priLikely = INF_PRIORITY; -} - -bool CBlockPolicyEstimator::isFeeDataPoint(const CFeeRate &fee, double pri) -{ - if ((pri < minTrackedPriority && fee >= minTrackedFee) || - (pri < priUnlikely && fee > feeLikely)) { - return true; - } - return false; -} - -bool CBlockPolicyEstimator::isPriDataPoint(const CFeeRate &fee, double pri) -{ - if ((fee < minTrackedFee && pri >= minTrackedPriority) || - (fee < feeUnlikely && pri > priLikely)) { - return true; - } - return false; + feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY); } void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool fCurrentEstimate) { unsigned int txHeight = entry.GetHeight(); uint256 hash = entry.GetTx().GetHash(); - if (mapMemPoolTxs[hash].stats != NULL) { + if (mapMemPoolTxs.count(hash)) { LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n", hash.ToString().c_str()); - return; + return; } if (txHeight < nBestSeenHeight) { @@ -379,30 +346,12 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo return; } - // Fees are stored and reported as BTC-per-kb: + // Feerates are stored and reported as zatoshis per 1000 bytes: CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); - // Want the priority of the tx at confirmation. However we don't know - // what that will be and its too hard to continue updating it - // so use starting priority as a proxy - double curPri = entry.GetPriority(txHeight); mapMemPoolTxs[hash].blockHeight = txHeight; - LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy mempool tx %s ", hash.ToString().substr(0,10)); - // Record this as a priority estimate - if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { - mapMemPoolTxs[hash].stats = &priStats; - mapMemPoolTxs[hash].bucketIndex = priStats.NewTx(txHeight, curPri); - } - // Record this as a fee estimate - else if (isFeeDataPoint(feeRate, curPri)) { - mapMemPoolTxs[hash].stats = &feeStats; - mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); - } - else { - LogPrint(BCLog::ESTIMATEFEE, "not adding"); - } - LogPrint(BCLog::ESTIMATEFEE, "\n"); + mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); } void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry) @@ -425,21 +374,10 @@ void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM return; } - // Fees are stored and reported as BTC-per-kb: + // Feerates are stored and reported as zatoshis per 1000 bytes: CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); - // Want the priority of the tx at confirmation. The priority when it - // entered the mempool could easily be very small and change quickly - double curPri = entry.GetPriority(nBlockHeight); - - // Record this as a priority estimate - if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { - priStats.Record(blocksToConfirm, curPri); - } - // Record this as a fee estimate - else if (isFeeDataPoint(feeRate, curPri)) { - feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK()); - } + feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK()); } void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, @@ -460,41 +398,15 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, if (!fCurrentEstimate) return; - // Update the dynamic cutoffs - // a fee/priority is "likely" the reason your tx was included in a block if >85% of such tx's - // were confirmed in 2 blocks and is "unlikely" if <50% were confirmed in 10 blocks - LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy recalculating dynamic cutoffs:\n"); - priLikely = priStats.EstimateMedianVal(2, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBlockHeight); - if (priLikely == -1) - priLikely = INF_PRIORITY; - - double feeLikelyEst = feeStats.EstimateMedianVal(2, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBlockHeight); - if (feeLikelyEst == -1) - feeLikely = CFeeRate(INF_FEERATE); - else - feeLikely = CFeeRate(feeLikelyEst); - - priUnlikely = priStats.EstimateMedianVal(10, SUFFICIENT_PRITXS, UNLIKELY_PCT, false, nBlockHeight); - if (priUnlikely == -1) - priUnlikely = 0; - - double feeUnlikelyEst = feeStats.EstimateMedianVal(10, SUFFICIENT_FEETXS, UNLIKELY_PCT, false, nBlockHeight); - if (feeUnlikelyEst == -1) - feeUnlikely = CFeeRate(0); - else - feeUnlikely = CFeeRate(feeUnlikelyEst); - - // Clear the current block states + // Clear the current block state feeStats.ClearCurrent(nBlockHeight); - priStats.ClearCurrent(nBlockHeight); // Repopulate the current block states for (unsigned int i = 0; i < entries.size(); i++) processBlockTx(nBlockHeight, entries[i]); - // Update all exponential averages with the current block states + // Update all exponential averages with the current block state feeStats.UpdateMovingAverages(); - priStats.UpdateMovingAverages(); LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy after updating estimates for %u confirmed entries, new mempool map size %u\n", entries.size(), mapMemPoolTxs.size()); @@ -514,29 +426,22 @@ CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) return CFeeRate(median); } -double CBlockPolicyEstimator::estimatePriority(int confTarget) -{ - // Return failure if trying to analyze a target we're not tracking - if (confTarget <= 0 || (unsigned int)confTarget > priStats.GetMaxConfirms()) - return -1; - - return priStats.EstimateMedianVal(confTarget, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight); -} - void CBlockPolicyEstimator::Write(CAutoFile& fileout) { fileout << nBestSeenHeight; feeStats.Write(fileout); - priStats.Write(fileout); } -void CBlockPolicyEstimator::Read(CAutoFile& filein) +void CBlockPolicyEstimator::Read(CAutoFile& filein, int nFileVersion) { int nFileBestSeenHeight; filein >> nFileBestSeenHeight; feeStats.Read(filein); - priStats.Read(filein); nBestSeenHeight = nFileBestSeenHeight; + if (nFileVersion < FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION) { + TxConfirmStats priStats; + priStats.Read(filein); + } } FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee) diff --git a/src/policy/fees.h b/src/policy/fees.h index b5498f928..2136959a3 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -20,51 +20,42 @@ class CFeeRate; class CTxMemPoolEntry; /** \class CBlockPolicyEstimator - * The BlockPolicyEstimator is used for estimating the fee or priority needed + * The BlockPolicyEstimator is used for estimating the feerate needed * for a transaction to be included in a block within a certain number of * blocks. * * At a high level the algorithm works by grouping transactions into buckets - * based on having similar priorities or fees and then tracking how long it + * based on having similar feerates and then tracking how long it * takes transactions in the various buckets to be mined. It operates under - * the assumption that in general transactions of higher fee/priority will be - * included in blocks before transactions of lower fee/priority. So for - * example if you wanted to know what fee you should put on a transaction to + * the assumption that in general transactions of higher feerate will be + * included in blocks before transactions of lower feerate. So for + * example if you wanted to know what feerate you should put on a transaction to * be included in a block within the next 5 blocks, you would start by looking - * at the bucket with the highest fee transactions and verifying that a + * at the bucket with the highest feerate transactions and verifying that a * sufficiently high percentage of them were confirmed within 5 blocks and - * then you would look at the next highest fee bucket, and so on, stopping at - * the last bucket to pass the test. The average fee of transactions in this - * bucket will give you an indication of the lowest fee you can put on a + * then you would look at the next highest feerate bucket, and so on, stopping at + * the last bucket to pass the test. The average feerate of transactions in this + * bucket will give you an indication of the lowest feerate you can put on a * transaction and still have a sufficiently high chance of being confirmed * within your desired 5 blocks. * - * When a transaction enters the mempool or is included within a block we - * decide whether it can be used as a data point for fee estimation, priority - * estimation or neither. If the value of exactly one of those properties was - * below the required minimum it can be used to estimate the other. In - * addition, if a priori our estimation code would indicate that the - * transaction would be much more quickly included in a block because of one - * of the properties compared to the other, we can also decide to use it as - * an estimate for that property. - * - * Here is a brief description of the implementation for fee estimation. - * When a transaction that counts for fee estimation enters the mempool, we + * Here is a brief description of the implementation: + * When a transaction enters the mempool, we * track the height of the block chain at entry. Whenever a block comes in, - * we count the number of transactions in each bucket and the total amount of fee + * we count the number of transactions in each bucket and the total amount of feerate * paid in each bucket. Then we calculate how many blocks Y it took each * transaction to be mined and we track an array of counters in each bucket * for how long it to took transactions to get confirmed from 1 to a max of 25 * and we increment all the counters from Y up to 25. This is because for any * number Z>=Y the transaction was successfully mined within Z blocks. We * want to save a history of this information, so at any time we have a - * counter of the total number of transactions that happened in a given fee + * counter of the total number of transactions that happened in a given feerate * bucket and the total number that were confirmed in each number 1-25 blocks * or less for any bucket. We save this history by keeping an exponentially * decaying moving average of each one of these stats. Furthermore we also * keep track of the number unmined (in mempool) transactions in each bucket * and for how many blocks they have been outstanding and use that to increase - * the number of transactions we've seen in that fee bucket when calculating + * the number of transactions we've seen in that feerate bucket when calculating * an estimate for any number of confirmations below the number of blocks * they've been outstanding. */ @@ -73,12 +64,11 @@ class CTxMemPoolEntry; static const double DEFAULT_DECAY = .998; /** - * We will instantiate two instances of this class, one to track transactions - * that were included in a block due to fee, and one for txs included due to - * priority. We will lump transactions into a bucket according to their approximate - * fee or priority and then track how long it took for those txs to be included + * We will instantiate an instance of this class to track transactions that were + * included in a block. We will lump transactions into a bucket according to their + * approximate feerate and then track how long it took for those txs to be included * in a block. There is always a bucket into which any given double value - * (representing a fee or priority) falls. + * (representing a fee) falls. * * The tracking of unconfirmed (mempool) transactions is completely independent of the * historical tracking of transactions that have been confirmed in a block. @@ -86,7 +76,7 @@ static const double DEFAULT_DECAY = .998; class TxConfirmStats { private: - //Define the buckets we will group transactions into (both fee buckets and priority buckets) + //Define the buckets we will group transactions into std::vector buckets; // The upper-bound of the range for the bucket (inclusive) std::map bucketMap; // Map of bucket upper-bound to index into all vectors by bucket @@ -103,16 +93,15 @@ class TxConfirmStats // and calculate the totals for the current block to update the moving averages std::vector > curBlockConf; // curBlockConf[Y][X] - // Sum the total priority/fee of all txs in each bucket + // Sum the total feerate of all tx's in each bucket // Track the historical moving average of this total over blocks std::vector avg; // and calculate the total for the current block to update the moving average std::vector curBlockVal; // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X - // Combine the total value with the tx counts to calculate the avg fee/priority per bucket + // Combine the total value with the tx counts to calculate the avg feerate per bucket - std::string dataTypeString; double decay = DEFAULT_DECAY; // Mempool counts of outstanding transactions @@ -134,9 +123,8 @@ class TxConfirmStats * @param defaultBuckets contains the upper limits for the bucket boundaries * @param maxConfirms max number of confirms to track * @param decay how much to decay the historical moving average per block - * @param dataTypeString for logging purposes */ - void Initialize(std::vector& defaultBuckets, unsigned int maxConfirms, double decay, std::string dataTypeString); + void Initialize(std::vector& defaultBuckets, unsigned int maxConfirms, double decay); /** Clear the state of the curBlock variables to start counting for the new block */ void ClearCurrent(unsigned int nBlockHeight); @@ -144,7 +132,7 @@ class TxConfirmStats /** * Record a new transaction data point in the current block stats * @param blocksToConfirm the number of blocks it took this transaction to confirm - * @param val either the fee or the priority when entered of the transaction + * @param val the feerate of the transaction * @warning blocksToConfirm is 1-based and has to be >= 1 */ void Record(int blocksToConfirm, double val); @@ -161,14 +149,14 @@ class TxConfirmStats void UpdateMovingAverages(); /** - * Calculate a fee or priority estimate. Find the lowest value bucket (or range of buckets + * Calculate a feerate estimate. Find the lowest value bucket (or range of buckets * to make sure we have enough data points) whose transactions still have sufficient likelihood * of being confirmed within the target number of confirmations * @param confTarget target number of confirmations * @param sufficientTxVal required average number of transactions per block in a bucket range * @param minSuccess the success probability we require - * @param requireGreater return the lowest fee/pri such that all higher values pass minSuccess OR - * return the highest fee/pri such that all lower values fail minSuccess + * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR + * return the highest feerate such that all lower values fail minSuccess * @param nBlockHeight the current block height */ double EstimateMedianVal(int confTarget, double sufficientTxVal, @@ -192,35 +180,27 @@ class TxConfirmStats /** Track confirm delays up to 25 blocks, can't estimate beyond that */ static const unsigned int MAX_BLOCK_CONFIRMS = 25; -/** Require greater than 85% of X fee transactions to be confirmed within Y blocks for X to be big enough */ +/** Require greater than 85% of X feerate transactions to be confirmed within Y blocks for X to be big enough */ static const double MIN_SUCCESS_PCT = .85; static const double UNLIKELY_PCT = .5; -/** Require an avg of 1 tx in the combined fee bucket per block to have stat significance */ +/** Require an avg of 1 tx in the combined feerate bucket per block to have stat significance */ static const double SUFFICIENT_FEETXS = 1; -/** Require only an avg of 1 tx every 5 blocks in the combined pri bucket (way less pri txs) */ -static const double SUFFICIENT_PRITXS = .2; - -// Minimum and Maximum values for tracking fees and priorities +// Minimum and Maximum values for tracking feerates static const double MIN_FEERATE = 10; static const double MAX_FEERATE = 1e7; static const double INF_FEERATE = MAX_MONEY; -static const double MIN_PRIORITY = 10; -static const double MAX_PRIORITY = 1e16; -static const double INF_PRIORITY = 1e9 * MAX_MONEY; -// We have to lump transactions into buckets based on fee or priority, but we want to be able -// to give accurate estimates over a large range of potential fees and priorities +// We have to lump transactions into buckets based on feerate, but we want to be able +// to give accurate estimates over a large range of potential feerates // Therefore it makes sense to exponentially space the buckets + /** Spacing of FeeRate buckets */ static const double FEE_SPACING = 1.1; -/** Spacing of Priority buckets */ -static const double PRI_SPACING = 2; - /** - * We want to be able to estimate fees or priorities that are needed on txs to be included in + * We want to be able to estimate feerates that are needed on tx's to be included in * a certain number of blocks. Every time a block is added to the best chain, this class records * stats on the transactions included in that block */ @@ -243,45 +223,30 @@ class CBlockPolicyEstimator /** Remove a transaction from the mempool tracking stats*/ void removeTx(uint256 hash); - /** Is this transaction likely included in a block because of its fee?*/ - bool isFeeDataPoint(const CFeeRate &fee, double pri); - - /** Is this transaction likely included in a block because of its priority?*/ - bool isPriDataPoint(const CFeeRate &fee, double pri); - - /** Return a fee estimate */ + /** Return a feerate estimate */ CFeeRate estimateFee(int confTarget); - /** Return a priority estimate */ - double estimatePriority(int confTarget); - /** Write estimation data to a file */ void Write(CAutoFile& fileout); /** Read estimation data from a file */ - void Read(CAutoFile& filein); + void Read(CAutoFile& filein, int nFileVersion); private: CFeeRate minTrackedFee; //!< Passed to constructor to avoid dependency on main - double minTrackedPriority; //!< Set to AllowFreeThreshold unsigned int nBestSeenHeight; struct TxStatsInfo { - TxConfirmStats *stats; unsigned int blockHeight; unsigned int bucketIndex; - TxStatsInfo() : stats(NULL), blockHeight(0), bucketIndex(0) {} + TxStatsInfo() : blockHeight(0), bucketIndex(0) {} }; // map of txids to information about that transaction std::map mapMemPoolTxs; /** Classes to track historical data on transaction confirmations */ - TxConfirmStats feeStats, priStats; - - /** Breakpoints to help determine whether a transaction was confirmed by priority or Fee */ - CFeeRate feeLikely, feeUnlikely; - double priLikely, priUnlikely; + TxConfirmStats feeStats; }; class FeeFilterRounder diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index da201c583..3a40249bd 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -110,7 +110,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const CChainParam else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { reason = "bare-multisig"; return false; - } else if (txout.IsDust(::minRelayTxFee)) { + } else if (txout.IsDust()) { reason = "dust"; return false; } diff --git a/src/policy/policy.h b/src/policy/policy.h index 510618d84..abc73a1d7 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -18,8 +18,6 @@ class CCoinsViewCache; /** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = MAX_BLOCK_SIZE; static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0; -/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ -static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = DEFAULT_BLOCK_MAX_SIZE / 2; /** Maximum number of signature check operations in an IsStandard() P2SH script */ static const unsigned int MAX_P2SH_SIGOPS = 15; /** The maximum number of sigops we're willing to relay/mine in a single tx */ @@ -43,9 +41,56 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY /** For convenience, standard but not mandatory verify flags. */ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; +/** + * The rate used to calculate the dust threshold, in zatoshis per 1000 bytes. + * This will effectively be multiplied by 3 (it is necessary to express it this + * way to ensure that rounding will be calculated the same as before #6542). + * + * Bitcoin Core added the concept of "dust" in bitcoin/bitcoin#2577. At that + * point the dust threshold rate was tied to three times the minRelayTxFee rate, + * with the motivation that if you'd pay more than a third of the minimum relay + * fee to spend something, it should be considered dust. This was implemented + * as a standard rule rejecting dust outputs. + * + * This motivation will not apply after ZIP 317 block construction is implemented: + * at that point the ZIP 317 marginal fee will be 5000 zats per logical action, + * but the dust threshold rate will still be 300 zats per 1000 bytes. Those costs + * would only coincide if the marginal size per logical action were + * 5000/300 * 1000 ~= 16667 bytes, and in practice the marginal size for any + * kind of input is much smaller than that. + * + * However, to avoid interoperability problems (older wallets creating transactions + * that newer nodes will reject because they view the outputs as dust), we will have + * to coordinate any increase in the dust threshold carefully. + * + * More history: in Zcash the minRelayTxFee rate was 5000 zats/1000 bytes at launch, + * changed to 1000 zats/1000 bytes in zcashd v1.0.3 and to 100 zats/1000 bytes in + * zcashd v1.0.7-1 (#2141). The relaying problem for shielded transactions (#1969) + * that prompted the latter change was fixed more thoroughly by the addition of + * `CFeeRate::GetFeeForRelay` in #4916, ensuring that a transaction paying + * `DEFAULT_FEE` can always be relayed. At the same time the default fee was set + * to 1000 zats, per ZIP 313. + * + * #6542 changed relaying policy to be more strict about enforcing minRelayTxFee. + * It also allowed `-minrelaytxfee=0`, which we are using to avoid some test + * breakage. But if the dust threshold rate were still set to three times the + * minRelayTxFee rate, then setting `-minrelaytxfee=0` would have the side effect + * of setting the dust threshold to zero, which is not intended. + * + * Bitcoin Core took a different approach to disentangling the dust threshold from + * the relay threshold, adding a `-dustrelayfee` option (bitcoin/bitcoin#9380). + * We don't want to do that because it is likely that we will change the dust policy + * again, and adding a user-visible config option might conflict with that. Also, + * it isn't a good idea for the dust threshold rate to be configurable per node; + * it's a standard rule parameter and should only be changed with network-wide + * coordination (if it is increased then wallets have to change before nodes, and + * vice versa if it is decreased). So for now we set it to a constant that matches + * the behaviour before #6542. + */ +static const unsigned int ONE_THIRD_DUST_THRESHOLD_RATE = 100; + // Sanity check the magic numbers when we change them static_assert(DEFAULT_BLOCK_MAX_SIZE <= MAX_BLOCK_SIZE); -static_assert(DEFAULT_BLOCK_PRIORITY_SIZE <= DEFAULT_BLOCK_MAX_SIZE); bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); /** diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index d306c9c0e..68fee5a7b 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -4,6 +4,7 @@ // file COPYING or https://www.opensource.org/licenses/mit-license.php . #include "primitives/transaction.h" +#include "policy/policy.h" #include "hash.h" #include "tinyformat.h" @@ -121,6 +122,22 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn) scriptPubKey = scriptPubKeyIn; } +CAmount CTxOut::GetDustThreshold() const +{ + // See the comment on ONE_THIRD_DUST_THRESHOLD_RATE in policy.h. + static const CFeeRate oneThirdDustThresholdRate {ONE_THIRD_DUST_THRESHOLD_RATE}; + + if (scriptPubKey.IsUnspendable()) + return 0; + + // A typical spendable txout is 34 bytes, and will need a txin of at + // least 148 bytes to spend. With ONE_THIRD_DUST_THRESHOLD_RATE == 100, + // the dust threshold for such a txout would be + // 3*floor(100*(34 + 148)/1000) zats = 54 zats. + size_t nSize = GetSerializeSize(*this, SER_DISK, 0) + 148u; + return 3*oneThirdDustThresholdRate.GetFee(nSize); +} + uint256 CTxOut::GetHash() const { return SerializeHash(*this); @@ -259,32 +276,6 @@ CAmount CTransaction::GetShieldedValueIn() const return nValue; } -double CTransaction::ComputePriority(double dPriorityInputs, unsigned int nTxSize) const -{ - nTxSize = CalculateModifiedSize(nTxSize); - if (nTxSize == 0) return 0.0; - - return dPriorityInputs / nTxSize; -} - -unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const -{ - // In order to avoid disincentivizing cleaning up the UTXO set we don't count - // the constant overhead for each txin and up to 110 bytes of scriptSig (which - // is enough to cover a compressed pubkey p2sh redemption) for priority. - // Providing any more cleanup incentive than making additional inputs free would - // risk encouraging people to create junk outputs to redeem later. - if (nTxSize == 0) - nTxSize = ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); - for (std::vector::const_iterator it(vin.begin()); it != vin.end(); ++it) - { - unsigned int offset = 41U + std::min(110U, (unsigned int)it->scriptSig.size()); - if (nTxSize > offset) - nTxSize -= offset; - } - return nTxSize; -} - std::string CTransaction::ToString() const { std::string str; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index a4cd3e7e2..6bf352e2b 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -445,26 +445,11 @@ class CTxOut uint256 GetHash() const; - CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const - { - // "Dust" is defined in terms of CTransaction::minRelayTxFee, - // which has units satoshis-per-kilobyte. - // If you'd pay more than 1/3 in fees - // to spend something, then we consider it dust. - // A typical spendable txout is 34 bytes big, and will - // need a CTxIn of at least 148 bytes to spend: - // so dust is a spendable txout less than - // 54*minRelayTxFee/1000 (in satoshis) - if (scriptPubKey.IsUnspendable()) - return 0; - - size_t nSize = GetSerializeSize(*this, SER_DISK, 0) + 148u; - return 3*minRelayTxFee.GetFee(nSize); - } - - bool IsDust(const CFeeRate &minRelayTxFee) const + CAmount GetDustThreshold() const; + + bool IsDust() const { - return (nValue < GetDustThreshold(minRelayTxFee)); + return nValue < GetDustThreshold(); } friend bool operator==(const CTxOut& a, const CTxOut& b) @@ -661,12 +646,6 @@ class CTransaction // Return sum of (positive valueBalance or zero) and JoinSplit vpub_new CAmount GetShieldedValueIn() const; - // Compute priority, given priority of inputs and (optionally) tx size - double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const; - - // Compute modified tx size for priority calculation (optionally given tx size) - unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const; - bool IsCoinBase() const { return (vin.size() == 1 && vin[0].prevout.IsNull()); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d0e481ccc..dddc56ee0 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -334,11 +334,9 @@ UniValue mempoolToJSON(bool fVerbose = false) info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); info.pushKV("time", e.GetTime()); info.pushKV("height", (int)e.GetHeight()); - info.pushKV("startingpriority", e.GetPriority(e.GetHeight())); - info.pushKV("currentpriority", e.GetPriority(chainActive.Height())); info.pushKV("descendantcount", e.GetCountWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants()); - info.pushKV("descendantfees", e.GetFeesWithDescendants()); + info.pushKV("descendantfees", e.GetModFeesWithDescendants()); const CTransaction& tx = e.GetTx(); set setDepends; for (const CTxIn& txin : tx.vin) @@ -392,11 +390,9 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" " \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n" - " \"descendantfees\" : n, (numeric) fees of in-mempool descendants (including this one)\n" + " \"descendantfees\" : n, (numeric) modified fees (see \"modifiedfee\" above) of in-mempool descendants (including this one)\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n" diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 8fab29ed7..fede2fe7d 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -89,7 +89,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "keypoolrefill", 0 }, { "getrawmempool", 0 }, { "estimatefee", 0 }, - { "estimatepriority", 0 }, { "prioritisetransaction", 1 }, { "prioritisetransaction", 2 }, { "setban", 2 }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index b0bf43386..ec8a9ff85 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -365,20 +365,18 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) } -// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts +// NOTE: Unlike wallet RPCs (which use BTCZ values), mining RPCs follow GBT (BIP 22) in using zatoshi amounts. UniValue prioritisetransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) - throw runtime_error( + throw std::runtime_error( "prioritisetransaction \n" "Accepts the transaction into mined blocks at a higher (or lower) priority\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id.\n" - "2. priority delta (numeric, required) The priority to add or subtract.\n" - " The transaction selection algorithm considers the tx as it would have a higher priority.\n" - " (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n" - "3. fee delta (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n" - " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" + "2. priority_delta (numeric, optional) Fee-independent priority adjustment. Not supported, so must be zero or null.\n" + "3. fee_delta (numeric, required) The fee value (in " + MINOR_CURRENCY_UNIT + ") to add (or subtract, if negative).\n" + " The modified fee is not actually paid, only the algorithm for selecting transactions into a block\n" " considers the transaction as it would have paid a higher (or lower) fee.\n" "\nResult\n" "true (boolean) Returns true\n" @@ -392,7 +390,11 @@ UniValue prioritisetransaction(const UniValue& params, bool fHelp) uint256 hash = ParseHashStr(params[0].get_str(), "txid"); CAmount nAmount = params[2].getInt(); - mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount); + if (!(params[1].isNull() || params[1].get_real() == 0)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is not supported, and adjustment thereof must be zero."); + } + + mempool.PrioritiseTransaction(hash, params[0].get_str(), nAmount); return true; } @@ -450,7 +452,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " ,...\n" " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" + " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in " + MINOR_CURRENCY_UNIT + "); for coinbase transactions, this is a negative Number of the total collected block fees (i.e., not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" " \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n" " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" " }\n" @@ -459,7 +461,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) // " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" // " \"flags\" : \"flags\" (string) \n" // " },\n" -// " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n" +// " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in " + MINOR_CURRENCY_UNIT + ")\n" " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" " \"target\" : \"xxxx\", (string) The hash target\n" " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" @@ -860,34 +862,6 @@ UniValue estimatefee(const UniValue& params, bool fHelp) return ValueFromAmount(feeRate.GetFeePerK()); } -UniValue estimatepriority(const UniValue& params, bool fHelp) -{ - if (fHelp || params.size() != 1) - throw runtime_error( - "estimatepriority nblocks\n" - "\nEstimates the approximate priority\n" - "a zero-fee transaction needs to begin confirmation\n" - "within nblocks blocks.\n" - "\nArguments:\n" - "1. nblocks (numeric)\n" - "\nResult:\n" - "n : (numeric) estimated priority\n" - "\n" - "-1.0 is returned if not enough transactions and\n" - "blocks have been observed to make an estimate.\n" - "\nExample:\n" - + HelpExampleCli("estimatepriority", "6") - ); - - RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); - - int nBlocks = params[0].getInt(); - if (nBlocks < 1) - nBlocks = 1; - - return mempool.estimatePriority(nBlocks); -} - UniValue getblocksubsidy(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -942,7 +916,6 @@ static const CRPCCommand commands[] = #endif { "util", "estimatefee", &estimatefee, true }, - { "util", "estimatepriority", &estimatepriority, true }, }; void RegisterMiningRPCCommands(CRPCTable &tableRPC) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index a43473020..9703f8ffa 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -63,8 +63,8 @@ UniValue getinfo(const UniValue& params, bool fHelp) " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" - " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee rate set in " + CURRENCY_UNIT + " per 1000 bytes\n" + " \"relayfee\": x.xxxx, (numeric) minimum relay fee rate for transactions in " + CURRENCY_UNIT + " per 1000 bytes\n" " \"errors\": \"...\" (string) any error messages\n" "}\n" "\nExamples:\n" @@ -603,7 +603,7 @@ UniValue getaddressmempool(const UniValue& params, bool fHelp) " \"address\" (string) The base58check encoded address\n" " \"txid\" (string) The related txid\n" " \"index\" (number) The related input or output index\n" - " \"satoshis\" (number) The difference of zatoshis\n" + " \"satoshis\" (number) The difference of " + MINOR_CURRENCY_UNIT + "\n" " \"timestamp\" (number) The time the transaction entered the mempool (seconds)\n" " \"prevtxid\" (string) The previous txid (if spending)\n" " \"prevout\" (string) The previous transaction output index (if spending)\n" @@ -685,7 +685,7 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) " \"height\" (number) The block height\n" " \"outputIndex\" (number) The output index\n" " \"script\" (string) The script hex encoded\n" - " \"satoshis\" (number) The number of zatoshis of the output\n" + " \"satoshis\" (number) The number of " + MINOR_CURRENCY_UNIT + " of the output\n" " }, ...\n" "]\n\n" "(or, if chainInfo is true):\n\n" @@ -698,7 +698,7 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) " \"height\" (number) The block height\n" " \"outputIndex\" (number) The output index\n" " \"script\" (string) The script hex encoded\n" - " \"satoshis\" (number) The number of zatoshis of the output\n" + " \"satoshis\" (number) The number of " + MINOR_CURRENCY_UNIT + " of the output\n" " }, ...\n" " ],\n" " \"hash\" (string) The block hash\n" @@ -841,7 +841,7 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) "\nResult:\n" "[\n" " {\n" - " \"satoshis\" (number) The difference of zatoshis\n" + " \"satoshis\" (number) The difference of " + MINOR_CURRENCY_UNIT + "\n" " \"txid\" (string) The related txid\n" " \"index\" (number) The related input or output index\n" " \"height\" (number) The block height\n" @@ -853,7 +853,7 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) " \"deltas\":\n" " [\n" " {\n" - " \"satoshis\" (number) The difference of zatoshis\n" + " \"satoshis\" (number) The difference of " + MINOR_CURRENCY_UNIT + "\n" " \"txid\" (string) The related txid\n" " \"index\" (number) The related input or output index\n" " \"height\" (number) The block height\n" @@ -964,8 +964,8 @@ UniValue getaddressbalance(const UniValue& params, bool fHelp) "\"address\" (string) The base58check encoded address\n" "\nResult:\n" "{\n" - " \"balance\" (string) The current balance in zatoshis\n" - " \"received\" (string) The total number of zatoshis received (including change)\n" + " \"balance\" (string) The current balance in " + MINOR_CURRENCY_UNIT + "\n" + " \"received\" (string) The total number of " + MINOR_CURRENCY_UNIT + " received (including change)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"]}'") diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 30201e3c0..e14b3ac9f 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -472,7 +472,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) " }\n" " ,...\n" " ],\n" - " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" + " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee rate for transactions in " + CURRENCY_UNIT + " per 1000 bytes\n" " \"localaddresses\": [ (array) list of local addresses\n" " {\n" " \"address\": \"xxxx\", (string) network address\n" diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index e416ae7ed..754ea9c22 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1101,7 +1101,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, &txFeeRate, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, &txFeeRate, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index d8f866af6..4d5bd42f1 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -127,28 +127,28 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).Priority(10.0).FromTx(tx1)); + pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1)); /* highest fee */ CMutableTransaction tx2 = CMutableTransaction(); tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx2.vout[0].nValue = 2 * COIN; - pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).Priority(9.0).FromTx(tx2)); + pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).FromTx(tx2)); /* lowest fee */ CMutableTransaction tx3 = CMutableTransaction(); tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx3.vout[0].nValue = 5 * COIN; - pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).Priority(100.0).FromTx(tx3)); + pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).FromTx(tx3)); /* 2nd highest fee */ CMutableTransaction tx4 = CMutableTransaction(); tx4.vout.resize(1); tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx4.vout[0].nValue = 6 * COIN; - pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).Priority(1.0).FromTx(tx4)); + pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).FromTx(tx4)); /* equal fee rate to tx1, but newer */ CMutableTransaction tx5 = CMutableTransaction(); @@ -156,7 +156,6 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx5.vout[0].nValue = 11 * COIN; entry.nTime = 1; - entry.dPriority = 10.0; pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5)); BOOST_CHECK_EQUAL(pool.size(), 5); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index ab661eeda..73e1cc5c2 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -148,7 +148,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) uint256 hash; TestMemPoolEntryHelper entry; entry.nFee = 11; - entry.dPriority = 111.0; entry.nHeight = 11; LOCK(cs_main); @@ -356,11 +355,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // orphan in mempool, template creation fails hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).FromTx(tx)); BOOST_CHECK_EXCEPTION(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, err_is("bad-txns-inputs-missingorspent")); mempool.clear(); - // child with higher priority than parent + // child with higher feerate than parent tx.vin[0].scriptSig = CScript() << OP_1; tx.vin[0].prevout.hash = txFirst[1]->GetHash(); tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 382f9d614..a73e3676a 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -18,26 +18,18 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) CTxMemPool mpool(CFeeRate(1000)); TestMemPoolEntryHelper entry; CAmount basefee(2000); - double basepri = 1e6; CAmount deltaFee(100); - double deltaPri=5e5; - std::vector feeV[2]; - std::vector priV[2]; + std::vector feeV; - // Populate vectors of increasing fees or priorities + // Populate vectors of increasing fees for (int j = 0; j < 10; j++) { - //V[0] is for fee transactions - feeV[0].push_back(basefee * (j+1)); - priV[0].push_back(0); - //V[1] is for priority transactions - feeV[1].push_back(CAmount(0)); - priV[1].push_back(basepri * pow(10, j+1)); + feeV.push_back(basefee * (j+1)); } // Store the hashes of transactions that have been - // added to the mempool by their associate fee/pri + // added to the mempool by their associate fee // txHashes[j] is populated with transactions either of - // fee = basefee * (j+1) OR pri = 10^6 * 10^(j+1) + // fee = basefee * (j+1) std::vector txHashes[10]; // Create a transaction template @@ -60,19 +52,19 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) // At a decay .998 and 4 fee transactions per block // This makes the tx count about 1.33 per bucket, above the 1 threshold while (blocknum < 200) { - for (int j = 0; j < 10; j++) { // For each fee/pri multiple - for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + for (int j = 0; j < 10; j++) { // For each fee + for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); + mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx, &mpool)); txHashes[j].push_back(hash); } } - //Create blocks where higher fee/pri txs are included more often + //Create blocks where higher fee txs are included more often for (int h = 0; h <= blocknum%10; h++) { - // 10/10 blocks add highest fee/pri transactions + // 10/10 blocks add highest fee transactions // 9/10 blocks add 2nd highest and so on until ... - // 1/10 blocks add lowest fee/pri transactions + // 1/10 blocks add lowest fee transactions while (txHashes[9-h].size()) { CTransaction btx; if (mpool.lookup(txHashes[9-h].back(), btx)) @@ -93,7 +85,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) } std::vector origFeeEst; - std::vector origPriEst; // Highest feerate is 10*baseRate and gets in all blocks, // second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%, // third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%, @@ -102,15 +93,11 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) // so estimateFee(2) should return 8*baseRate etc... for (int i = 1; i < 10;i++) { origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK()); - origPriEst.push_back(mpool.estimatePriority(i)); if (i > 1) { // Fee estimates should be monotonically decreasing BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]); - BOOST_CHECK(origPriEst[i-1] <= origPriEst[i-2]); } BOOST_CHECK(origFeeEst[i-1] < (10-i)*baseRate.GetFeePerK() + deltaFee); BOOST_CHECK(origFeeEst[i-1] > (10-i)*baseRate.GetFeePerK() - deltaFee); - BOOST_CHECK(origPriEst[i-1] < pow(10,10-i) * basepri + deltaPri); - BOOST_CHECK(origPriEst[i-1] > pow(10,10-i) * basepri - deltaPri); } // Mine 50 more blocks with no transactions happening, estimates shouldn't change @@ -121,19 +108,17 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int i = 1; i < 10;i++) { BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee); BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); - BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] + deltaPri); - BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); } // Mine 15 more blocks with lots of transactions happening and not getting mined // Estimates should go up while (blocknum < 265) { - for (int j = 0; j < 10; j++) { // For each fee/pri multiple - for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + for (int j = 0; j < 10; j++) { // For each fee multiple + for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); + mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx, &mpool)); txHashes[j].push_back(hash); } } @@ -142,7 +127,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int i = 1; i < 10;i++) { BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); - BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); } // Mine all those transactions @@ -159,17 +143,16 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) block.clear(); for (int i = 1; i < 10;i++) { BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee); - BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri); } // Mine 100 more blocks where everything is mined every block // Estimates should be below original estimates (not possible for last estimate) while (blocknum < 365) { - for (int j = 0; j < 10; j++) { // For each fee/pri multiple - for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx + for (int j = 0; j < 10; j++) { // For each fee multiple + for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); + mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx, &mpool)); CTransaction btx; if (mpool.lookup(hash, btx)) block.push_back(btx); @@ -180,7 +163,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) } for (int i = 1; i < 9; i++) { BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee); - BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] - deltaPri); } } @@ -190,7 +172,7 @@ BOOST_AUTO_TEST_CASE(TxConfirmStats_FindBucketIndex) std::vector buckets {0.0, 3.5, 42.0}; TxConfirmStats txcs; - txcs.Initialize(buckets, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "Test"); + txcs.Initialize(buckets, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY); BOOST_CHECK_EQUAL(txcs.FindBucketIndex(-1.0), 0); BOOST_CHECK_EQUAL(txcs.FindBucketIndex(0.0), 0); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 4924db23c..3c7de85bf 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(json_parse_errors) // Invalid, trailing garbage BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0sds"), std::runtime_error); BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0]"), std::runtime_error); - // BTC addresses should fail parsing + // Bitcoin addresses should fail parsing BOOST_CHECK_THROW(ParseNonRFCJSONValue("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"), std::runtime_error); BOOST_CHECK_THROW(ParseNonRFCJSONValue("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNL"), std::runtime_error); } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index abd1ff54c..ed827bcdc 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -227,7 +227,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPo // Hack to assume either its completely dependent on other mempool txs or not at all CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0; - return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight, + return CTxMemPoolEntry(tx, nFee, nTime, nHeight, hasNoDependencies, inChainValue, spendsCoinbase, sigOpCount, nBranchId); } diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index fe329213f..0bc7f52c6 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -72,7 +72,6 @@ struct TestMemPoolEntryHelper // Default values CAmount nFee; int64_t nTime; - double dPriority; unsigned int nHeight; bool hadNoDependencies; bool spendsCoinbase; @@ -80,7 +79,7 @@ struct TestMemPoolEntryHelper uint32_t nBranchId; TestMemPoolEntryHelper() : - nFee(0), nTime(0), dPriority(0.0), nHeight(1), + nFee(0), nTime(0), nHeight(1), hadNoDependencies(false), spendsCoinbase(false), sigOpCount(1), nBranchId(SPROUT_BRANCH_ID) { } @@ -89,7 +88,6 @@ struct TestMemPoolEntryHelper // Change the default value TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; } TestMemPoolEntryHelper &Time(int64_t _time) { nTime = _time; return *this; } - TestMemPoolEntryHelper &Priority(double _priority) { dPriority = _priority; return *this; } TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; } TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 45bbdd3dd..5d9e01efb 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -769,8 +769,9 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) string reason; BOOST_CHECK(IsStandardTx(t, reason, chainparams)); - // Check dust with default relay fee: - CAmount nDustThreshold = 182 * minRelayTxFee.GetFeePerK()/1000 * 3; + // Check dust threshold: + CFeeRate oneThirdDustThresholdRate{ONE_THIRD_DUST_THRESHOLD_RATE}; + CAmount nDustThreshold = (34 + 148) * oneThirdDustThresholdRate.GetFeePerK()/1000 * 3; BOOST_CHECK_EQUAL(nDustThreshold, 54); // dust: t.vout[0].nValue = nDustThreshold - 1; @@ -779,17 +780,6 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].nValue = nDustThreshold; BOOST_CHECK(IsStandardTx(t, reason, chainparams)); - // Check dust with odd relay fee to verify rounding: - // nDustThreshold = 182 * 1234 / 1000 * 3 - minRelayTxFee = CFeeRate(1234); - // dust: - t.vout[0].nValue = 672 - 1; - BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); - // not dust: - t.vout[0].nValue = 672; - BOOST_CHECK(IsStandardTx(t, reason, chainparams)); - minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); - t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason, chainparams)); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 1d208df95..49c0c3c9a 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -21,22 +21,21 @@ using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, - int64_t _nTime, double _entryPriority, unsigned int _entryHeight, + int64_t _nTime, unsigned int _nHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue, bool _spendsCoinbase, unsigned int _sigOps, uint32_t _nBranchId): - tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), + tx(_tx), nFee(_nFee), nTime(_nTime), nHeight(_nHeight), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps), nBranchId(_nBranchId) { nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - nModSize = tx.CalculateModifiedSize(nTxSize); nUsageSize = RecursiveDynamicUsage(tx); feeDelta = 0; nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; - nFeesWithDescendants = nFee; + nModFeesWithDescendants = nFee; CAmount nValueIn = tx.GetValueOut()+nFee; assert(inChainInputValue <= nValueIn); } @@ -46,16 +45,6 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) *this = other; } -double -CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const -{ - double deltaPriority = ((double)(currentHeight-entryHeight)*inChainInputValue)/nModSize; - double dResult = entryPriority + deltaPriority; - if (dResult < 0) // This should only happen if it was called with a height below entry height - dResult = 0; - return dResult; -} - // Update the given tx for any in-mempool descendants. // Assumes that setMemPoolChildren is correct for the given tx and all // descendants. @@ -110,7 +99,7 @@ bool CTxMemPool::UpdateForDescendants(txiter updateIt, int maxDescendantsToVisit for (txiter cit : setAllDescendants) { if (!setExclude.count(cit->GetTx().GetHash())) { modifySize += cit->GetTxSize(); - modifyFee += cit->GetFee(); + modifyFee += cit->GetModifiedFee(); modifyCount++; cachedDescendants[updateIt].insert(cit); } @@ -240,7 +229,7 @@ void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors } const int64_t updateCount = (add ? 1 : -1); const int64_t updateSize = updateCount * it->GetTxSize(); - const CAmount updateFee = updateCount * it->GetFee(); + const CAmount updateFee = updateCount * it->GetModifiedFee(); for (txiter ancestorIt : setAncestors) { mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount)); } @@ -300,7 +289,7 @@ void CTxMemPoolEntry::SetDirty() { nCountWithDescendants = 0; nSizeWithDescendants = nTxSize; - nFeesWithDescendants = nFee; + nModFeesWithDescendants = GetModifiedFee(); } void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount) @@ -308,8 +297,7 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t if (!IsDirty()) { nSizeWithDescendants += modifySize; assert(int64_t(nSizeWithDescendants) > 0); - nFeesWithDescendants += modifyFee; - assert(nFeesWithDescendants >= 0); + nModFeesWithDescendants += modifyFee; nCountWithDescendants += modifyCount; assert(int64_t(nCountWithDescendants) > 0); } @@ -317,6 +305,7 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta) { + nModFeesWithDescendants += newFeeDelta - feeDelta; feeDelta = newFeeDelta; } @@ -377,6 +366,17 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, indexed_transaction_set::iterator newit = mapTx.insert(entry).first; mapLinks.insert(make_pair(newit, TxLinks())); + // Update transaction for any feeDelta created by PrioritiseTransaction + // TODO: refactor so that the fee delta is calculated before inserting + // into mapTx. + std::map::const_iterator pos = mapDeltas.find(hash); + if (pos != mapDeltas.end()) { + const CAmount &delta = pos->second; + if (delta) { + mapTx.modify(newit, update_fee_delta(delta)); + } + } + // Update cachedInnerUsage to include contained transaction's usage. // (When we update the entry for in-mempool parents, memory usage will be // further updated.) @@ -415,15 +415,6 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, mapSaplingNullifiers[spendDescription.nullifier] = &tx; } - // Update transaction's score for any feeDelta created by PrioritiseTransaction - std::map >::const_iterator pos = mapDeltas.find(hash); - if (pos != mapDeltas.end()) { - const std::pair &deltas = pos->second; - if (deltas.second) { - mapTx.modify(newit, update_fee_delta(deltas.second)); - } - } - nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); minerPolicyEstimator->processTransaction(entry, fCurrentEstimate); @@ -886,27 +877,24 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const CTxMemPool::setEntries setChildrenCheck; std::map::const_iterator iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0)); int64_t childSizes = 0; - CAmount childFees = 0; + CAmount childModFee = 0; for (; iter != mapNextTx.end() && iter->first.hash == it->GetTx().GetHash(); ++iter) { txiter childit = mapTx.find(iter->second.ptx->GetHash()); assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions if (setChildrenCheck.insert(childit).second) { childSizes += childit->GetTxSize(); - childFees += childit->GetFee(); + childModFee += childit->GetModifiedFee(); } } assert(setChildrenCheck == GetMemPoolChildren(it)); - // Also check to make sure size/fees is greater than sum with immediate children. + // Also check to make sure size is greater than sum with immediate children. // just a sanity check, not definitive that this calc is correct... - // also check that the size is less than the size of the entire mempool. if (!it->IsDirty()) { assert(it->GetSizeWithDescendants() >= childSizes + it->GetTxSize()); - assert(it->GetFeesWithDescendants() >= childFees + it->GetFee()); } else { assert(it->GetSizeWithDescendants() == it->GetTxSize()); - assert(it->GetFeesWithDescendants() == it->GetFee()); + assert(it->GetModFeesWithDescendants() == it->GetModifiedFee()); } - assert(it->GetFeesWithDescendants() >= 0); // The SaltedTxidHasher is fine to use here; it salts the map keys automatically // with randomness generated on construction. @@ -1065,19 +1053,14 @@ CFeeRate CTxMemPool::estimateFee(int nBlocks) const LOCK(cs); return minerPolicyEstimator->estimateFee(nBlocks); } -double CTxMemPool::estimatePriority(int nBlocks) const -{ - LOCK(cs); - return minerPolicyEstimator->estimatePriority(nBlocks); -} bool CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const { try { LOCK(cs); - fileout << 109900; // version required to read: 0.10.99 or later - fileout << CLIENT_VERSION; // version that wrote the file + fileout << FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION; // version required to read + fileout << std::max(FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION, CLIENT_VERSION); // version that wrote the file minerPolicyEstimator->Write(fileout); } catch (const std::exception&) { @@ -1093,11 +1076,10 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) try { int nVersionRequired, nVersionThatWrote; filein >> nVersionRequired >> nVersionThatWrote; - if (nVersionRequired > CLIENT_VERSION) + if (nVersionRequired > std::max(FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION, CLIENT_VERSION)) return error("CTxMemPool::ReadFeeEstimates(): up-version (%d) fee estimate file", nVersionRequired); - LOCK(cs); - minerPolicyEstimator->Read(filein); + minerPolicyEstimator->Read(filein, nVersionThatWrote); } catch (const std::exception&) { LogPrintf("CTxMemPool::ReadFeeEstimates(): unable to read policy estimator data (non-fatal)\n"); @@ -1106,30 +1088,36 @@ CTxMemPool::ReadFeeEstimates(CAutoFile& filein) return true; } -void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta) +void CTxMemPool::PrioritiseTransaction(const uint256 hash, const std::string strHash, const CAmount& nFeeDelta) { { LOCK(cs); - std::pair &deltas = mapDeltas[hash]; - deltas.first += dPriorityDelta; - deltas.second += nFeeDelta; + CAmount &delta = mapDeltas[hash]; + delta += nFeeDelta; txiter it = mapTx.find(hash); if (it != mapTx.end()) { - mapTx.modify(it, update_fee_delta(deltas.second)); + mapTx.modify(it, update_fee_delta(delta)); + // Now update all ancestors' modified fees with descendants + setEntries setAncestors; + uint64_t nNoLimit = std::numeric_limits::max(); + std::string dummy; + CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false); + for (txiter ancestorIt : setAncestors) { + mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0)); + } } } - LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta)); + LogPrintf("PrioritiseTransaction: %s feerate += %s\n", strHash, FormatMoney(nFeeDelta)); } -void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) const +void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const { LOCK(cs); - std::map >::const_iterator pos = mapDeltas.find(hash); + std::map::const_iterator pos = mapDeltas.find(hash); if (pos == mapDeltas.end()) return; - const std::pair &deltas = pos->second; - dPriorityDelta += deltas.first; - nFeeDelta += deltas.second; + const CAmount &delta = pos->second; + nFeeDelta += delta; } void CTxMemPool::ClearPrioritisation(const uint256 hash) diff --git a/src/txmempool.h b/src/txmempool.h index adf3cf567..80642b208 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -25,17 +25,8 @@ class CAutoFile; -inline double AllowFreeThreshold() -{ - return COIN * 144 / 250; -} - -inline bool AllowFree(double dPriority) -{ - // Large (in bytes) low-priority (new, small-coin) transactions - // need a fee. - return dPriority > AllowFreeThreshold(); -} +/** Version from which we write `fee_estimates.dat` without priority information: 5.5.0-beta1 or later */ +static const int FEE_ESTIMATES_WITHOUT_PRIORITY_VERSION = 5050000; /** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; @@ -49,12 +40,12 @@ class CTxMemPool; * ("descendant" transactions). * * When a new entry is added to the mempool, we update the descendant state - * (nCountWithDescendants, nSizeWithDescendants, and nFeesWithDescendants) for + * (nCountWithDescendants, nSizeWithDescendants, and nModFeesWithDescendants) for * all ancestors of the newly added transaction. * * If updating the descendant state is skipped, we can mark the entry as - * "dirty", and set nSizeWithDescendants/nFeesWithDescendants to equal nTxSize/ - * nTxFee. (This can potentially happen during a reorg, where we limit the + * "dirty", and set nSizeWithDescendants/nModFeesWithDescendants to equal nTxSize/ + * nFee+feeDelta. (This can potentially happen during a reorg, where we limit the * amount of work we're willing to do to avoid consuming too much CPU.) * */ @@ -65,11 +56,9 @@ class CTxMemPoolEntry CTransaction tx; CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups size_t nTxSize; //!< ... and avoid recomputing tx size - size_t nModSize; //!< ... and modified size for priority size_t nUsageSize; //!< ... and total memory usage int64_t nTime; //!< Local time when entering the mempool - double entryPriority; //!< Priority when entering the mempool - unsigned int entryHeight; //!< Chain height when entering the mempool + unsigned int nHeight; //!< Chain height when entering the mempool bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain bool spendsCoinbase; //!< keep track of transactions that spend a coinbase @@ -80,15 +69,15 @@ class CTxMemPoolEntry // Information about descendants of this transaction that are in the // mempool; if we remove this transaction we must remove all of these // descendants as well. if nCountWithDescendants is 0, treat this entry as - // dirty, and nSizeWithDescendants and nFeesWithDescendants will not be + // dirty, and nSizeWithDescendants and nModFeesWithDescendants will not be // correct. uint64_t nCountWithDescendants; //! number of descendant transactions uint64_t nSizeWithDescendants; //! ... and size - CAmount nFeesWithDescendants; //! ... and total fees (all including us) + CAmount nModFeesWithDescendants; //! ... and total fees (all including us) public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, - int64_t _nTime, double _entryPriority, unsigned int _entryHeight, + int64_t _nTime, unsigned int _nHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase, unsigned int nSigOps, uint32_t nBranchId); CTxMemPoolEntry(const CTxMemPoolEntry& other); @@ -98,18 +87,18 @@ class CTxMemPoolEntry * Fast calculation of lower bound of current priority as update * from entry priority. Only inputs that were originally in-chain will age. */ - double GetPriority(unsigned int currentHeight) const; const CAmount& GetFee() const { return nFee; } size_t GetTxSize() const { return nTxSize; } int64_t GetTime() const { return nTime; } - unsigned int GetHeight() const { return entryHeight; } + unsigned int GetHeight() const { return nHeight; } unsigned int GetSigOpCount() const { return sigOpCount; } int64_t GetModifiedFee() const { return nFee + feeDelta; } bool WasClearAtEntry() const { return hadNoDependencies; } size_t DynamicMemoryUsage() const { return nUsageSize; } - // Updates the fee delta used for mining priority score + // Updates the fee delta used for mining priority score, and the + // modified fees with descendants. void UpdateFeeDelta(int64_t feeDelta); // Adjusts the descendant state, if this entry is not dirty. @@ -124,7 +113,7 @@ class CTxMemPoolEntry uint64_t GetCountWithDescendants() const { return nCountWithDescendants; } uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; } - CAmount GetFeesWithDescendants() const { return nFeesWithDescendants; } + CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; } bool GetSpendsCoinbase() const { return spendsCoinbase; } uint32_t GetValidatedBranchId() const { return nBranchId; } @@ -172,27 +161,27 @@ struct mempoolentry_txid } }; -/** \class CompareTxMemPoolEntryByFee +/** \class CompareTxMemPoolEntryByDescendantScore * - * Sort an entry by max(feerate of entry's tx, feerate with all descendants). + * Sort an entry by max(score/size of entry's tx, score/size with all descendants). */ -class CompareTxMemPoolEntryByFee +class CompareTxMemPoolEntryByDescendantScore { public: bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const { - bool fUseADescendants = UseDescendantFeeRate(a); - bool fUseBDescendants = UseDescendantFeeRate(b); + bool fUseADescendants = UseDescendantScore(a); + bool fUseBDescendants = UseDescendantScore(b); - double aFees = fUseADescendants ? a.GetFeesWithDescendants() : a.GetFee(); + double aModFee = fUseADescendants ? a.GetModFeesWithDescendants() : a.GetModifiedFee(); double aSize = fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize(); - double bFees = fUseBDescendants ? b.GetFeesWithDescendants() : b.GetFee(); + double bModFee = fUseBDescendants ? b.GetModFeesWithDescendants() : b.GetModifiedFee(); double bSize = fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize(); // Avoid division by rewriting (a/b > c/d) as (a*d > c*b). - double f1 = aFees * bSize; - double f2 = aSize * bFees; + double f1 = aModFee * bSize; + double f2 = aSize * bModFee; if (f1 == f2) { return a.GetTime() >= b.GetTime(); @@ -200,11 +189,11 @@ class CompareTxMemPoolEntryByFee return f1 < f2; } - // Calculate which feerate to use for an entry (avoiding division). - bool UseDescendantFeeRate(const CTxMemPoolEntry &a) const + // Calculate which score to use for an entry (avoiding division). + bool UseDescendantScore(const CTxMemPoolEntry &a) const { - double f1 = (double)a.GetFee() * a.GetSizeWithDescendants(); - double f2 = (double)a.GetFeesWithDescendants() * a.GetTxSize(); + double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants(); + double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize(); return f2 > f1; } }; @@ -370,7 +359,7 @@ class CTxMemPool boost::multi_index::ordered_non_unique< boost::multi_index::tag, boost::multi_index::identity, - CompareTxMemPoolEntryByFee + CompareTxMemPoolEntryByDescendantScore >, // sorted by entry time boost::multi_index::ordered_non_unique< @@ -378,7 +367,7 @@ class CTxMemPool boost::multi_index::identity, CompareTxMemPoolEntryByEntryTime >, - // sorted by score (for mining prioritization) + // sorted by score (for mining prioritisation) boost::multi_index::ordered_unique< boost::multi_index::tag, boost::multi_index::identity, @@ -421,7 +410,7 @@ class CTxMemPool public: std::map mapNextTx; - std::map > mapDeltas; + std::map mapDeltas; /** Create a new CTxMemPool. * minReasonableRelayFee should be a feerate which is, roughly, somewhere @@ -480,8 +469,8 @@ class CTxMemPool bool HasNoInputsOf(const CTransaction& tx) const; /** Affect CreateNewBlock prioritisation of transactions */ - void PrioritiseTransaction(const uint256 hash, const std::string strHash, double dPriorityDelta, const CAmount& nFeeDelta); - void ApplyDeltas(const uint256 hash, double &dPriorityDelta, CAmount &nFeeDelta) const; + void PrioritiseTransaction(const uint256 hash, const std::string strHash, const CAmount& nFeeDelta); + void ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const; void ClearPrioritisation(const uint256 hash); public: @@ -543,9 +532,6 @@ class CTxMemPool /** Estimate fee rate needed to get into the next nBlocks */ CFeeRate estimateFee(int nBlocks) const; - /** Estimate priority needed to get into the next nBlocks */ - double estimatePriority(int nBlocks) const; - /** Write/Read estimates to disk */ bool WriteFeeEstimates(CAutoFile& fileout) const; bool ReadFeeEstimates(CAutoFile& filein); @@ -622,17 +608,4 @@ class CCoinsViewMemPool : public CCoinsViewBacked bool HaveCoins(const uint256 &txid) const; }; -// We want to sort transactions by coin age priority -typedef std::pair TxCoinAgePriority; - -struct TxCoinAgePriorityCompare -{ - bool operator()(const TxCoinAgePriority& a, const TxCoinAgePriority& b) - { - if (a.first == b.first) - return CompareTxMemPoolEntryByScore()(*(b.second), *(a.second)); //Reverse order to make sort less than - return a.first < b.first; - } -}; - #endif // BITCOIN_TXMEMPOOL_H diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index e1232d5de..8784c7df8 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -286,7 +286,7 @@ bool AsyncRPCOperation_sendmany::main_impl() { secret.MakeNewKey(true); CScript scriptPubKey = GetScriptForDestination(secret.GetPubKey().GetID()); CTxOut out(CAmount(1), scriptPubKey); - CAmount dustThreshold = out.GetDustThreshold(minRelayTxFee); + CAmount dustThreshold = out.GetDustThreshold(); CAmount dustChange = -1; std::vector selectedTInputs; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5c4d1b509..b76a98c73 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1653,9 +1653,9 @@ UniValue settxfee(const UniValue& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 1) throw runtime_error( "settxfee amount\n" - "\nSet the transaction fee per kB. Overwrites the paytxfee parameter.\n" + "\nSet the preferred transaction fee rate per 1000 bytes. This is only used by legacy transaction creation APIs (sendtoaddress, sendmany, and fundrawtransaction). Overwrites the paytxfee parameter.\n" "\nArguments:\n" - "1. amount (numeric, required) The transaction fee in " + CURRENCY_UNIT + "/kB rounded to the nearest 0.00000001\n" + "1. amount (numeric, required) The transaction fee rate in " + CURRENCY_UNIT + " per 1000 bytes rounded to the nearest 0.00000001\n" "\nResult\n" "true|false (boolean) Returns true if successful\n" "\nExamples:\n" @@ -1691,7 +1691,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" + " \"paytxfee\": x.xxxx, (numeric) the preferred transaction fee rate used for transactions created by legacy APIs, set in " + CURRENCY_UNIT + " per 1000 bytes\n" " \"seedfp\": \"uint256\", (string) the BLAKE2b-256 hash of the HD seed\n" "}\n" "\nExamples:\n" @@ -2953,7 +2953,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"outputPrev\" : n, (numeric, sapling) the index of the output within the vShieldedOutput\n" " \"address\" : \"address\", (string) The BitcoinZ address involved in the transaction\n" " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"valueZat\" : xxxx (numeric) The amount in zatoshis\n" + " \"valueZat\" : xxxx (numeric) The amount in " + MINOR_CURRENCY_UNIT + "\n" " }\n" " ,...\n" " ],\n" @@ -2966,7 +2966,7 @@ UniValue z_viewtransaction(const UniValue& params, bool fHelp) " \"address\" : \"address\", (string) The BitcoinZ address involved in the transaction\n" " \"outgoing\" : true|false (boolean, sapling) True if the output is not for an address in the wallet\n" " \"value\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" - " \"valueZat\" : xxxx (numeric) The amount in zatoshis\n" + " \"valueZat\" : xxxx (numeric) The amount in " + MINOR_CURRENCY_UNIT + "\n" " \"memo\" : \"hexmemo\", (string) hexadecimal string representation of the memo field\n" " \"memoStr\" : \"memo\", (string) Only returned if memo contains valid UTF-8 text.\n" " }\n" diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 99bb3c447..6c2fe4e7a 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -177,11 +177,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin( 3*COIN); add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin + BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTCZ in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin + BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTCZ in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // empty the wallet and start again, now with fractions of a cent, to test small change avoidance diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2e421801f..37c29b706 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -44,15 +44,15 @@ CWallet* pwalletMain = NULL; CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; -bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS; bool fPayAtLeastCustomFee = true; unsigned int nAnchorConfirmations = DEFAULT_ANCHOR_CONFIRMATIONS; const char * DEFAULT_WALLET_DAT = "wallet.dat"; /** - * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) - * Override with -mintxfee + * -mintxfee: the fallback fee rate (in ZEC per 1000 bytes) used by legacy APIs + * when -paytxfee has not been set and there is insufficient mempool data to + * estimate a fee according to the -txconfirmtarget option. */ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); @@ -3739,7 +3739,6 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt CAmount nTotalValue = nValue; if (nSubtractFeeFromAmount == 0) nTotalValue += nFeeRet; - double dPriority = 0; // vouts to the payees for (const CRecipient& recipient : vecSend) { @@ -3756,7 +3755,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt } } - if (txout.IsDust(::minRelayTxFee)) + if (txout.IsDust()) { if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) { @@ -3788,18 +3787,6 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt } return false; } - for (std::pair pcoin : setCoins) - { - CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; - //The coin age after the next block (depth+1) is used instead of the current, - //reflecting an assumption the user would accept a bit more delay for - //a chance at a free transaction. - //But mempool inputs might still be in the mempool, so their age stays 0 - int age = pcoin.first->GetDepthInMainChain(); - if (age != 0) - age += 1; - dPriority += (double)nCredit * age; - } CAmount nChange = nValueIn - nValue; if (nSubtractFeeFromAmount == 0) @@ -3840,16 +3827,16 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt // We do not move dust-change to fees, because the sender would end up paying more than requested. // This would be against the purpose of the all-inclusive feature. // So instead we raise the change and deduct from the recipient. - if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee)) + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust()) { - CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue; + CAmount nDust = newTxOut.GetDustThreshold() - newTxOut.nValue; newTxOut.nValue += nDust; // raise change until no more dust for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient { if (vecSend[i].fSubtractFeeFromAmount) { txNew.vout[i].nValue -= nDust; - if (txNew.vout[i].IsDust(::minRelayTxFee)) + if (txNew.vout[i].IsDust()) { strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); return false; @@ -3861,7 +3848,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt // Never create dust outputs; if we would, just // add the dust to the fee. - if (newTxOut.IsDust(::minRelayTxFee)) + if (newTxOut.IsDust()) { nFeeRet += nChange; reservekey.ReturnKey(); @@ -3930,22 +3917,6 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt return false; } - dPriority = wtxNew.ComputePriority(dPriority, nBytes); - - // Can we complete this as a free transaction? - if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) - { - // Not enough fee: enough priority? - double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); - // Not enough mempool history to estimate: use hard-coded AllowFree. - if (dPriorityNeeded <= 0 && AllowFree(dPriority)) - break; - - // Small enough, and priority high enough, to send for free - if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded) - break; - } - CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); // If we made it here and we aren't even able to meet the relay fee on the next pass, give up @@ -4032,7 +4003,9 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge { // payTxFee is user-set "I want to pay this much" CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); - // user selected total at least (default=true) + // TODO: fPayAtLeastCustomFee is always true so we could simplify this by saying + // `payTxFee.GetFee(std::max(1000, nTxBytes))` above, if we do not remove this code + // completely. if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK()) nFeeNeeded = payTxFee.GetFeePerK(); // User didn't set: use -txconfirmtarget to estimate... @@ -4740,15 +4713,20 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); strUsage += HelpMessageOpt("-migration", _("Enable the Sprout to Sapling migration")); strUsage += HelpMessageOpt("-migrationdestaddress=", _("Set the Sapling migration address")); - strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), + strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("The fallback fee rate (in %s per 1000 bytes) used by APIs (sendtoaddress, sendmany, and fundrawtransaction) when -paytxfee " + "has not been set and there is insufficient mempool data to estimate a fee according to the -txconfirmtarget option (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); - strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), - CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("The preferred fee rate (in %s per 1000 bytes) used for transactions created by APIs (sendtoaddress, sendmany, and fundrawtransaction). " + "If the transaction is less than 1000 bytes then the fee rate is applied as though it were 1000 bytes. See the descriptions of -txconfirmtarget " + "and -mintxfee options for how the fee is calculated when this option is not set."), + CURRENCY_UNIT)); strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup (implies -rescan)")); - strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); - strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); + strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If -paytxfee is not set, include enough fee that transactions created by APIs (sendtoaddress, sendmany, and fundrawtransaction) " + "begin confirmation on average within n blocks. This is only used if there is sufficient mempool data to estimate the fee; if not, the " + "fallback fee set by -mintxfee is used. (default: %u)"), + DEFAULT_TX_CONFIRM_TARGET)); strUsage += HelpMessageOpt("-txexpirydelta", strprintf(_("Set the number of blocks after which a transaction that has not been mined will become invalid (min: %u, default: %u (pre-Blossom) or %u (post-Blossom))"), TX_EXPIRING_SOON_THRESHOLD + 1, DEFAULT_PRE_BLOSSOM_TX_EXPIRY_DELTA, DEFAULT_POST_BLOSSOM_TX_EXPIRY_DELTA)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); @@ -4952,11 +4930,11 @@ bool CWallet::ParameterInteraction() if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK)) return UIError(AmountErrMsg("paytxfee", mapArgs["-paytxfee"])); if (nFeePerK > HIGH_TX_FEE_PER_KB) - UIWarning(_("-paytxfee is set very high! This is the transaction fee you will pay if you send a transaction.")); + UIWarning(_("-paytxfee is set to a very high fee rate! This is the fee rate you will pay if you send a transaction.")); payTxFee = CFeeRate(nFeePerK, 1000); if (payTxFee < ::minRelayTxFee) { - return UIError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), + return UIError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least the minimum relay fee rate %s)"), mapArgs["-paytxfee"], ::minRelayTxFee.ToString())); } } @@ -4966,11 +4944,11 @@ bool CWallet::ParameterInteraction() if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee)) return UIError(AmountErrMsg("maxtxfee", mapArgs["-maxtxfee"])); if (nMaxFee > HIGH_MAX_TX_FEE) - UIWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + UIWarning(_("-maxtxfee is set to a very high fee rate! Fee rates this large could be paid on a single transaction.")); maxTxFee = nMaxFee; if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) { - return UIError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + return UIError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minimum relay fee rate of %s to prevent stuck transactions)"), mapArgs["-maxtxfee"], ::minRelayTxFee.ToString())); } } @@ -4984,7 +4962,10 @@ bool CWallet::ParameterInteraction() expiryDeltaArg = expiryDelta; } bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); + + if (GetBoolArg("-sendfreetransactions", false)) { + return UIError(_("The argument -sendfreetransactions is no longer supported.")); + } // Check Sapling migration address if set and is a valid Sapling address if (mapArgs.count("-migrationdestaddress")) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 16258dba0..bd69d57b2 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -46,7 +46,6 @@ extern CWallet* pwalletMain; extern CFeeRate payTxFee; extern unsigned int nTxConfirmTarget; extern bool bSpendZeroConfChange; -extern bool fSendFreeTransactions; extern bool fPayAtLeastCustomFee; extern unsigned int nAnchorConfirmations; @@ -59,12 +58,8 @@ static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000; static const CAmount MIN_CHANGE = CENT; //! Default for -spendzeroconfchange static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true; -//! Default for -sendfreetransactions -static const bool DEFAULT_SEND_FREE_TRANSACTIONS = false; //! -txconfirmtarget default static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2; -//! Largest (in bytes) free transaction we're willing to create -static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; static const bool DEFAULT_WALLETBROADCAST = true; //! Size of witness cache // Should be large enough that we can expect not to reorg beyond our cache @@ -1151,13 +1146,13 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** * Estimate the minimum fee considering user set parameters - * and the required fee + * and the required fee. */ static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool); /** * Return the minimum required fee taking into account the - * floating relay fee and user set minimum transaction fee + * floating relay fee rate and user set minimum transaction fee rate. */ static CAmount GetRequiredFee(unsigned int nTxBytes); From 5dc6592a53b8255f2323cc5150a7eba4f1866c7b Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 2 Sep 2024 21:43:13 +0000 Subject: [PATCH 133/146] Update boost to 1.83.0 --- depends/packages/boost.mk | 15 +-- depends/packages/native_b2.mk | 6 + .../boost/6753-signals2-function-fix.patch | 104 ++++++++++++++++++ 3 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 depends/patches/boost/6753-signals2-function-fix.patch diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 0216c4a89..7ea80cd62 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,14 +1,15 @@ package=boost -$(package)_version=1.81.0 +$(package)_version=1.83.0 $(package)_download_path=https://archives.boost.io/release/$($(package)_version)/source/ $(package)_file_name=boost_$(subst .,_,$($(package)_version)).tar.gz -$(package)_sha256_hash=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6 +$(package)_sha256_hash=c0685b68dd44cc46574cce86c4e17c0f611b15e195be9848dfd0769a0a207628 $(package)_dependencies=native_b2 +$(package)_patches=6753-signals2-function-fix.patch define $(package)_set_vars $(package)_config_opts_release=variant=release $(package)_config_opts_debug=variant=debug -$(package)_config_opts=--layout=tagged --build-type=complete --user-config=user-config.jam +$(package)_config_opts=--layout=system --user-config=user-config.jam $(package)_config_opts+=threading=multi link=static -sNO_COMPRESSION=1 $(package)_config_opts_linux=target-os=linux threadapi=pthread runtime-link=shared $(package)_config_opts_darwin=target-os=darwin runtime-link=shared @@ -23,20 +24,20 @@ else $(package)_toolset_$(host_os)=gcc endif $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test -$(package)_cxxflags+=-std=c++17 +$(package)_cxxflags+=-std=c++17 -fvisibility=hidden $(package)_cxxflags_linux=-fPIC $(package)_cxxflags_freebsd=-fPIC $(package)_cxxflags_openbsd=-fPIC $(package)_cxxflags_android=-fPIC -$(package)_cxxflags_x86_64=-fcf-protection=full endef define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/6753-signals2-function-fix.patch && \ echo "using $($(package)_toolset_$(host_os)) : : $($(package)_cxx) : \"$($(package)_cflags)\" \"$($(package)_cxxflags)\" \"$($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$($(package)_ar)\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" : ;" > user-config.jam endef define $(package)_config_cmds - ./bootstrap.sh --without-icu --with-libraries=$($(package)_config_libraries) --with-toolset=$($(package)_toolset_$(host_os)) --with-bjam=b2 + ./bootstrap.sh --without-icu --with-libraries=$($(package)_config_libraries) --with-toolset=$($(package)_toolset_$(host_os)) --with-bjam=b2 --libdir=lib endef define $(package)_build_cmds @@ -44,5 +45,5 @@ define $(package)_build_cmds endef define $(package)_stage_cmds - b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) toolset=$($(package)_toolset_$(host_os)) --no-cmake-config install + b2 -d0 -j4 --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) toolset=$($(package)_toolset_$(host_os)) install endef diff --git a/depends/packages/native_b2.mk b/depends/packages/native_b2.mk index aaa37cdcf..0ee855047 100644 --- a/depends/packages/native_b2.mk +++ b/depends/packages/native_b2.mk @@ -10,9 +10,15 @@ else $(package)_toolset_$(host_os)=gcc endif +ifneq ($(WITH_GUIX),) +define $(package)_build_cmds + CXX="$($(package)_cxx)" CXXFLAGS="$($(package)_cxxflags)" ./build.sh "$($(package)_toolset_$(host_os))" "--cxxflags=-fno-use-cxa-atexit" +endef +else define $(package)_build_cmds CXX="$($(package)_cxx)" CXXFLAGS="$($(package)_cxxflags)" ./build.sh "$($(package)_toolset_$(host_os))" endef +endif define $(package)_stage_cmds mkdir -p "$($(package)_staging_prefix_dir)"/bin/ && \ diff --git a/depends/patches/boost/6753-signals2-function-fix.patch b/depends/patches/boost/6753-signals2-function-fix.patch new file mode 100644 index 000000000..dfc4530ab --- /dev/null +++ b/depends/patches/boost/6753-signals2-function-fix.patch @@ -0,0 +1,104 @@ +From 7ca2310b15e387258e97e4cd16887f5ee4906b28 Mon Sep 17 00:00:00 2001 +From: Peter Dimov +Date: Sun, 3 Sep 2023 17:55:50 +0300 +Subject: [PATCH] Support fn.contains(f) where f is a function. Fixes #46. + +--- + boost/function/function_base.hpp | 17 +++++++++++- + libs/function/test/Jamfile.v2 | 3 +++ + libs/function/test/contains3_test.cpp | 33 ++++++++++++++++++++++++ + 3 files changed, 52 insertions(+), 1 deletion(-) + create mode 100644 test/contains3_test.cpp + +diff --git a/boost/function/function_base.hpp b/boost/function/function_base.hpp +index 5693e11e..00c7ce8e 100644 +--- a/boost/function/function_base.hpp ++++ b/boost/function/function_base.hpp +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -652,7 +653,8 @@ class function_base + } + + template +- bool contains(const F& f) const ++ typename boost::enable_if_< !boost::is_function::value, bool >::type ++ contains(const F& f) const + { + if (const F* fp = this->template target()) + { +@@ -662,6 +664,19 @@ class function_base + } + } + ++ template ++ typename boost::enable_if_< boost::is_function::value, bool >::type ++ contains(Fn& f) const ++ { ++ typedef Fn* F; ++ if (const F* fp = this->template target()) ++ { ++ return function_equal(*fp, &f); ++ } else { ++ return false; ++ } ++ } ++ + #if defined(__GNUC__) && __GNUC__ == 3 && __GNUC_MINOR__ <= 3 + // GCC 3.3 and newer cannot copy with the global operator==, due to + // problems with instantiation of function return types before it +diff --git a/libs/function/test/Jamfile.v2 b/libs/function/test/Jamfile.v2 +index 15d401ea..7be87a11 100644 +--- a/libs/function/test/Jamfile.v2 ++++ b/libs/function/test/Jamfile.v2 +@@ -90,3 +90,6 @@ run fn_eq_bind_test.cpp ; + # /usr/include/c++/4.4/bits/shared_ptr.h:146: error: cannot use typeid with -fno-rtti + run contains_test.cpp : : : off gcc-4.4,0x:no : contains_test_no_rtti ; + run contains2_test.cpp : : : off gcc-4.4,0x:no : contains2_test_no_rtti ; ++ ++run contains3_test.cpp ; ++run contains3_test.cpp : : : off gcc-4.4,0x:no : contains3_test_no_rtti ; +diff --git a/libs/function/test/contains3_test.cpp b/libs/function/test/contains3_test.cpp +new file mode 100644 +index 00000000..e6130bbe +--- /dev/null ++++ b/libs/function/test/contains3_test.cpp +@@ -0,0 +1,33 @@ ++// Copyright 2023 Peter Dimov ++// Distributed under the Boost Software License, Version 1.0. ++// https://www.boost.org/LICENSE_1_0.txt ++ ++#include ++#include ++ ++static int f() ++{ ++ return 1; ++} ++ ++static int g() ++{ ++ return 2; ++} ++ ++int main() ++{ ++ { ++ boost::function fn; ++ BOOST_TEST( !fn.contains( f ) ); ++ BOOST_TEST( !fn.contains( g ) ); ++ } ++ ++ { ++ boost::function fn( f ); ++ BOOST_TEST( fn.contains( f ) ); ++ BOOST_TEST( !fn.contains( g ) ); ++ } ++ ++ return boost::report_errors(); ++} From 7466e41663f3cc3e0cae52fc9da1b468e70b5113 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 07:54:55 +0000 Subject: [PATCH 134/146] More BitcoinZ rebranding --- qa/rpc-tests/multi_rpc.py | 4 ++-- qa/rpc-tests/test_framework/util.py | 2 +- src/gtest/test_rpc.cpp | 12 ++++++------ src/rpc/server.cpp | 4 ++-- src/util.cpp | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/qa/rpc-tests/multi_rpc.py b/qa/rpc-tests/multi_rpc.py index d9390afbf..ed6d5ec5e 100755 --- a/qa/rpc-tests/multi_rpc.py +++ b/qa/rpc-tests/multi_rpc.py @@ -32,10 +32,10 @@ def setup_nodes(self): def setup_chain(self): print("Initializing test directory "+self.options.tmpdir) initialize_chain(self.options.tmpdir) - #Append rpcauth to zcash.conf before initialization + #Append rpcauth to bitcoinz.conf before initialization rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144" rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e" - with open(os.path.join(self.options.tmpdir+"/node0", "zcash.conf"), 'a') as f: + with open(os.path.join(self.options.tmpdir+"/node0", "bitcoinz.conf"), 'a') as f: f.write(rpcauth+"\n") f.write(rpcauth2+"\n") diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 0c0303414..c37ca6070 100755 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -95,7 +95,7 @@ def initialize_datadir(dirname, n): datadir = os.path.join(dirname, "node"+str(n)) if not os.path.isdir(datadir): os.makedirs(datadir) - with open(os.path.join(datadir, "zcash.conf"), 'w') as f: + with open(os.path.join(datadir, "bitcoinz.conf"), 'w') as f: f.write("regtest=1\n") f.write("showmetrics=0\n") f.write("rpcuser=rt\n") diff --git a/src/gtest/test_rpc.cpp b/src/gtest/test_rpc.cpp index e193aef3a..bc7bbfd74 100644 --- a/src/gtest/test_rpc.cpp +++ b/src/gtest/test_rpc.cpp @@ -33,19 +33,19 @@ TEST(rpc, CheckExperimentalDisabledHelpMsg) { EXPECT_EQ(experimentalDisabledHelpMsg("somerpc", {"somevalue"}), "\nWARNING: somerpc is disabled.\n" - "To enable it, restart zcashd with the following command line options:\n" + "To enable it, restart bitcoinzd with the following command line options:\n" "-experimentalfeatures and -somevalue\n\n" - "Alternatively add these two lines to the zcash.conf file:\n\n" + "Alternatively add these two lines to the bitcoinz.conf file:\n\n" "experimentalfeatures=1\n" "somevalue=1\n"); EXPECT_EQ(experimentalDisabledHelpMsg("somerpc", {"somevalue", "someothervalue"}), "\nWARNING: somerpc is disabled.\n" - "To enable it, restart zcashd with the following command line options:\n" + "To enable it, restart bitcoinzd with the following command line options:\n" "-experimentalfeatures and -somevalue" " or:\n-experimentalfeatures and -someothervalue" "\n\n" - "Alternatively add these two lines to the zcash.conf file:\n\n" + "Alternatively add these two lines to the bitcoinz.conf file:\n\n" "experimentalfeatures=1\n" "somevalue=1\n" "\nor:\n\n" @@ -54,12 +54,12 @@ TEST(rpc, CheckExperimentalDisabledHelpMsg) { EXPECT_EQ(experimentalDisabledHelpMsg("somerpc", {"somevalue", "someothervalue", "athirdvalue"}), "\nWARNING: somerpc is disabled.\n" - "To enable it, restart zcashd with the following command line options:\n" + "To enable it, restart bitcoinzd with the following command line options:\n" "-experimentalfeatures and -somevalue" " or:\n-experimentalfeatures and -someothervalue" " or:\n-experimentalfeatures and -athirdvalue" "\n\n" - "Alternatively add these two lines to the zcash.conf file:\n\n" + "Alternatively add these two lines to the bitcoinz.conf file:\n\n" "experimentalfeatures=1\n" "somevalue=1\n" "\nor:\n\n" diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index fdb2cfd19..0424cdfae 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -489,9 +489,9 @@ std::string experimentalDisabledHelpMsg(const std::string& rpc, const std::vecto } } return "\nWARNING: " + rpc + " is disabled.\n" + - "To enable it, restart zcashd with the following command line options:\n" + "To enable it, restart bitcoinzd with the following command line options:\n" + cmd + "\n\n" + - "Alternatively add these two lines to the zcash.conf file:\n\n" + "Alternatively add these two lines to the bitcoinz.conf file:\n\n" + config; } diff --git a/src/util.cpp b/src/util.cpp index 022c0936a..e3f04568d 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -413,7 +413,7 @@ void ReadConfigFile(const std::string& confPath, } InterpretNegativeSetting(strKey, strValue); - // Don't overwrite existing settings so command line settings override zcash.conf + // Don't overwrite existing settings so command line settings override bitcoinz.conf if (mapSettingsRet.count(strKey) == 0) mapSettingsRet[strKey] = strValue; mapMultiSettingsRet[strKey].push_back(strValue); From b000f32595dcbd00ea424feb2c870b587a2b3260 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 07:55:22 +0000 Subject: [PATCH 135/146] Fix crash when mining with empty keypool. --- qa/rpc-tests/keypool.py | 15 +++++++++++++++ src/miner.cpp | 6 ++++-- src/rpc/mining.cpp | 6 +++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index ddebf3a55..bdbffbc04 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -74,6 +74,21 @@ def run_test(nodes, tmpdir): except JSONRPCException,e: assert(e.error['code']==-12) + # refill keypool with three new addresses + nodes[0].walletpassphrase('test', 12000) + nodes[0].keypoolrefill(3) + nodes[0].walletlock() + + # drain them by mining + nodes[0].generate(1) + nodes[0].generate(1) + nodes[0].generate(1) + nodes[0].generate(1) + try: + nodes[0].generate(1) + raise AssertionError('Keypool should be exhausted after three addesses') + except JSONRPCException,e: + assert(e.error['code']==-12) def main(): import optparse diff --git a/src/miner.cpp b/src/miner.cpp index b7f17933f..3d7c34ddc 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -517,8 +517,10 @@ void static BitcoinMiner(const CChainParams& chainparams) miningTimer.start(); try { - //throw an error if no script was provided - if (!coinbaseScript->reserveScript.size()) + // Throw an error if no script was provided. This can happen + // due to some internal error but also if the keypool is empty. + // In the latter case, already the pointer is NULL. + if (!coinbaseScript || coinbaseScript->reserveScript.empty()) throw std::runtime_error("No coinbase script available (mining requires a wallet or -mineraddress)"); while (true) { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index ec8a9ff85..95752fe90 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -188,8 +188,12 @@ UniValue generate(const UniValue& params, bool fHelp) boost::shared_ptr coinbaseScript; GetMainSignals().ScriptForMining(coinbaseScript); + // If the keypool is exhausted, no script is returned at all. Catch this. + if (!coinbaseScript) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + //throw an error if no script was provided - if (!coinbaseScript->reserveScript.size()) + if (coinbaseScript->reserveScript.empty()) throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available (mining requires a wallet or -mineraddress)"); { // Don't keep cs_main locked From eddd49c527d4304cce4dd3a9ad6dc597a933419c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 08:01:23 +0000 Subject: [PATCH 136/146] Enable wallet key imports without rescan in pruned mode. --- src/wallet/rpcdump.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 44b766a25..f725b27be 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -101,9 +101,6 @@ UniValue importprivkey(const UniValue& params, bool fHelp) + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false") ); - if (fPruneMode) - throw JSONRPCError(RPC_WALLET_ERROR, "Importing keys is disabled in pruned mode"); - LOCK2(cs_main, pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); @@ -118,6 +115,9 @@ UniValue importprivkey(const UniValue& params, bool fHelp) if (params.size() > 2) fRescan = params[2].get_bool(); + if (fRescan && fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); + CKey key = DecodeSecret(strSecret); if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); @@ -210,9 +210,6 @@ UniValue importaddress(const UniValue& params, bool fHelp) + HelpExampleRpc("importaddress", "\"myscript\", \"testing\", false") ); - if (fPruneMode) - throw JSONRPCError(RPC_WALLET_ERROR, "Importing addresses is disabled in pruned mode"); - string strLabel = ""; if (params.size() > 1) strLabel = params[1].get_str(); @@ -222,6 +219,9 @@ UniValue importaddress(const UniValue& params, bool fHelp) if (params.size() > 2) fRescan = params[2].get_bool(); + if (fRescan && fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); + // Whether to import a p2sh version, too bool fP2SH = false; if (params.size() > 3) @@ -274,9 +274,6 @@ UniValue importpubkey(const UniValue& params, bool fHelp) + HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false") ); - if (fPruneMode) - throw JSONRPCError(RPC_WALLET_ERROR, "Importing public keys is disabled in pruned mode"); - string strLabel = ""; if (params.size() > 1) strLabel = params[1].get_str(); @@ -286,6 +283,9 @@ UniValue importpubkey(const UniValue& params, bool fHelp) if (params.size() > 2) fRescan = params[2].get_bool(); + if (fRescan && fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); + if (!IsHex(params[0].get_str())) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string"); std::vector data(ParseHex(params[0].get_str())); @@ -699,9 +699,6 @@ UniValue z_importkey(const UniValue& params, bool fHelp) + HelpExampleRpc("z_importkey", "\"mykey\", \"no\"") ); - if (fPruneMode) - throw JSONRPCError(RPC_WALLET_ERROR, "Importing keys is disabled in pruned mode"); - LOCK2(cs_main, pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); @@ -731,6 +728,9 @@ UniValue z_importkey(const UniValue& params, bool fHelp) } } + if (fRescan && fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); + // Height to rescan from int nRescanHeight = 0; if (params.size() > 2) From d27c03e33c0f36af4208bc05ce0721d393a778e9 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 08:05:10 +0000 Subject: [PATCH 137/146] Clean up change computation in CreateTransaction. --- src/wallet/wallet.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 37c29b706..ef1382aee 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3736,9 +3736,9 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt nChangePosRet = -1; bool fFirst = true; - CAmount nTotalValue = nValue; + CAmount nValueToSelect = nValue; if (nSubtractFeeFromAmount == 0) - nTotalValue += nFeeRet; + nValueToSelect += nFeeRet; // vouts to the payees for (const CRecipient& recipient : vecSend) { @@ -3776,7 +3776,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt CAmount nValueIn = 0; bool fOnlyCoinbaseCoins = false; bool fNeedCoinbaseCoins = false; - if (!SelectCoins(nTotalValue, setCoins, nValueIn, fOnlyCoinbaseCoins, fNeedCoinbaseCoins, coinControl)) + if (!SelectCoins(nValueToSelect, setCoins, nValueIn, fOnlyCoinbaseCoins, fNeedCoinbaseCoins, coinControl)) { if (fOnlyCoinbaseCoins && Params().GetConsensus().fCoinbaseMustBeShielded) { strFailReason = _("Coinbase funds can only be sent to a zaddr"); @@ -3788,10 +3788,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt return false; } - CAmount nChange = nValueIn - nValue; - if (nSubtractFeeFromAmount == 0) - nChange -= nFeeRet; - + const CAmount nChange = nValueIn - nValueToSelect; if (nChange > 0) { // Fill a vout to ourself From 027fc475e6d1cf0cfc4ca7314a29eef0d9789679 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 08:07:41 +0000 Subject: [PATCH 138/146] Reject invalid pubkeys when reading ckey items from the wallet. --- src/wallet/walletdb.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 5b45f9759..373a6c134 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -517,8 +517,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == "ckey") { - vector vchPubKey; + CPubKey vchPubKey; ssKey >> vchPubKey; + if (!vchPubKey.IsValid()) + { + strErr = "Error reading wallet database: CPubKey corrupt"; + return false; + } vector vchPrivKey; ssValue >> vchPrivKey; wss.nCKeys++; From 9aa6dbe3d738a38c2a076d84bf292b037fb71ef2 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 08:09:08 +0000 Subject: [PATCH 139/146] Fix fundrawtransaction handling of includeWatching --- src/wallet/rpcwallet.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b76a98c73..ae81aeb43 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2062,9 +2062,12 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) if (!DecodeHexTx(origTx, params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + if (origTx.vout.size() == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output"); + bool includeWatching = false; if (params.size() > 1) - includeWatching = true; + includeWatching = params[1].get_bool(); CMutableTransaction tx(origTx); CAmount nFee; From 8428e97b505a795bd9a1ccc1156dc008f109f155 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 08:28:41 +0000 Subject: [PATCH 140/146] [wallet, rpc tests] Fix settxfee, paytxfee --- qa/rpc-tests/test_framework/util.py | 3 ++ qa/rpc-tests/wallet.py | 48 ++++++++++++++++------------- src/coincontrol.h | 3 ++ src/wallet/wallet.cpp | 9 ++---- src/wallet/wallet.h | 1 - 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index c37ca6070..d4e847b06 100755 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -47,6 +47,9 @@ def hex_str_to_bytes(hex_str): def str_to_b64str(string): return b64encode(string.encode('utf-8')).decode('ascii') +def count_bytes(hex_string): + return len(bytearray.fromhex(hex_string)) + def sync_blocks(rpc_connections, wait=1): """ Wait until everybody has the same block count, and has notified diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index d94238ac8..d9d76cfcf 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -16,6 +16,17 @@ class WalletTest (BitcoinTestFramework): + def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): + """Return curr_balance after asserting the fee was in range""" + fee = balance_with_fee - curr_balance + target_fee = fee_per_byte * tx_size + if fee < target_fee: + raise AssertionError("Fee of %s BTCZ too low! (Should be %s BTCZ)"%(str(fee), str(target_fee))) + # allow the node's estimation to be at most 2 bytes off + if fee > fee_per_byte * (tx_size + 2): + raise AssertionError("Fee of %s BTCZ too high! (Should be %s BTCZ)"%(str(fee), str(target_fee))) + return curr_balance + def setup_chain(self): print("Initializing test directory "+self.options.tmpdir) initialize_chain_clean(self.options.tmpdir, 4) @@ -138,15 +149,14 @@ def run_test (self): # Send 12500 BTCZ normal address = self.nodes[0].getnewaddress("") - self.nodes[2].settxfee(Decimal('0.001')) + fee_per_byte = Decimal('0.001') / 1000 + self.nodes[2].settxfee(fee_per_byte * 1000) self.nodes[2].sendtoaddress(address, 12500, "", "", False) self.sync_all() self.nodes[2].generate(1) self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('49999.99900000')) - assert_equal(self.nodes[0].getbalance(), Decimal('12500.00000000')) - assert_equal(self.nodes[2].getbalance("*"), Decimal('49999.99900000')) - assert_equal(self.nodes[0].getbalance("*"), Decimal('12500.00000000')) + node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('50000'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) + assert_equal(self.nodes[0].getbalance(), Decimal('12500')) # Send 12500 BTCZ with subtract fee from amount self.nodes[2].sendtoaddress(address, 12500, "", "", True) @@ -155,28 +165,27 @@ def run_test (self): self.sync_all() assert_equal(self.nodes[2].getbalance(), Decimal('37499.99900000')) assert_equal(self.nodes[0].getbalance(), Decimal('24999.99900000')) - assert_equal(self.nodes[2].getbalance("*"), Decimal('37499.99900000')) - assert_equal(self.nodes[0].getbalance("*"), Decimal('24999.99900000')) + node_2_bal -= Decimal('12500') + assert_equal(self.nodes[2].getbalance(), node_2_bal) + node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('25000'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) # Sendmany 12500 BTCZ self.nodes[2].sendmany("", {address: 12500}, 0, "", []) self.sync_all() self.nodes[2].generate(1) self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('24999.99800000')) - assert_equal(self.nodes[0].getbalance(), Decimal('37499.99900000')) - assert_equal(self.nodes[2].getbalance("*"), Decimal('24999.99800000')) - assert_equal(self.nodes[0].getbalance("*"), Decimal('37499.99900000')) + node_0_bal += Decimal('12500') + node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('12500'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) + assert_equal(self.nodes[0].getbalance(), node_0_bal) # Sendmany 12500 BTCZ with subtract fee from amount self.nodes[2].sendmany("", {address: 12500}, 0, "", [address]) self.sync_all() self.nodes[2].generate(1) self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('12499.99800000')) - assert_equal(self.nodes[0].getbalance(), Decimal('49999.99800000')) - assert_equal(self.nodes[2].getbalance("*"), Decimal('12499.99800000')) - assert_equal(self.nodes[0].getbalance("*"), Decimal('49999.99800000')) + node_2_bal -= Decimal('12500') + assert_equal(self.nodes[2].getbalance(), node_2_bal) + node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('12500'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) # Test ResendWalletTransactions: # Create a couple of transactions, then start up a fourth @@ -238,8 +247,7 @@ def run_test (self): self.sync_all() self.nodes[1].generate(1) #mine a block, tx should not be in there self.sync_all() - assert_equal(self.nodes[2].getbalance(), Decimal('12499.99800000')) #should not be changed because tx was not broadcasted - assert_equal(self.nodes[2].getbalance("*"), Decimal('12499.99800000')) #should not be changed because tx was not broadcasted + assert_equal(self.nodes[2].getbalance(), node_2_bal); #should not be changed because tx was not broadcasted #now broadcast from another node, mine a block, sync, and check the balance self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex']) @@ -247,8 +255,7 @@ def run_test (self): self.nodes[1].generate(1) self.sync_all() txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) - assert_equal(self.nodes[2].getbalance(), Decimal('12501.99800000')) #should not be - assert_equal(self.nodes[2].getbalance("*"), Decimal('12501.99800000')) #should not be + assert_equal(self.nodes[2].getbalance(), node_2_bal + Decimal('2')); #should not be #create another tx txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) @@ -266,8 +273,7 @@ def run_test (self): sync_blocks(self.nodes) #tx should be added to balance because after restarting the nodes tx should be broadcastet - assert_equal(self.nodes[2].getbalance(), Decimal('12503.99800000')) #should not be - assert_equal(self.nodes[2].getbalance("*"), Decimal('12503.99800000')) #should not be + assert_equal(self.nodes[2].getbalance(), node_2_bal + Decimal('4')); #should not be # send from node 0 to node 2 taddr mytaddr = self.nodes[2].getnewaddress() diff --git a/src/coincontrol.h b/src/coincontrol.h index 1adc84757..c0b840438 100644 --- a/src/coincontrol.h +++ b/src/coincontrol.h @@ -16,6 +16,8 @@ class CCoinControl bool fAllowOtherInputs; //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria bool fAllowWatchOnly; + //! Minimum absolute fee (not per kilobyte) + CAmount nMinimumTotalFee; CCoinControl() { @@ -28,6 +30,7 @@ class CCoinControl fAllowOtherInputs = false; fAllowWatchOnly = false; setSelected.clear(); + nMinimumTotalFee = 0; } bool HasSelected() const diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ef1382aee..9beeea625 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -44,7 +44,6 @@ CWallet* pwalletMain = NULL; CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; -bool fPayAtLeastCustomFee = true; unsigned int nAnchorConfirmations = DEFAULT_ANCHOR_CONFIRMATIONS; const char * DEFAULT_WALLET_DAT = "wallet.dat"; @@ -3915,6 +3914,9 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt } CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); + if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { + nFeeNeeded = coinControl->nMinimumTotalFee; + } // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. @@ -4000,11 +4002,6 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge { // payTxFee is user-set "I want to pay this much" CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); - // TODO: fPayAtLeastCustomFee is always true so we could simplify this by saying - // `payTxFee.GetFee(std::max(1000, nTxBytes))` above, if we do not remove this code - // completely. - if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK()) - nFeeNeeded = payTxFee.GetFeePerK(); // User didn't set: use -txconfirmtarget to estimate... if (nFeeNeeded == 0) nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index bd69d57b2..1bb500bc0 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -46,7 +46,6 @@ extern CWallet* pwalletMain; extern CFeeRate payTxFee; extern unsigned int nTxConfirmTarget; extern bool bSpendZeroConfChange; -extern bool fPayAtLeastCustomFee; extern unsigned int nAnchorConfirmations; static const unsigned int DEFAULT_KEYPOOL_SIZE = 100; From 37daf1c148363380d19bd70369d9e8a19e7c131e Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 09:19:52 +0000 Subject: [PATCH 141/146] Use 'fs.h' --- src/logging.cpp | 3 ++- src/rpc/blockchain.cpp | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/logging.cpp b/src/logging.cpp index dab1828d3..f9f6ad49b 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include @@ -253,7 +254,7 @@ void ShrinkDebugFile() size_t log_size = 0; try { log_size = fs::file_size(pathLog); - } catch (boost::filesystem::filesystem_error &) {} + } catch (fs::filesystem_error &) {} // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index dddc56ee0..c0600bfdc 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -10,6 +10,7 @@ #include "clientversion.h" #include "consensus/validation.h" #include "experimental_features.h" +#include "fs.h" #include "key_io.h" #include "main.h" #include "primitives/transaction.h" @@ -1461,9 +1462,11 @@ UniValue dumpbootstrap(const UniValue& params, bool fHelp) throw std::runtime_error("Start block number out of range."); } - boost::filesystem::path pathDest(strDest); - if (boost::filesystem::is_directory(pathDest)) + fs::path pathDest; + pathDest = fs::system_complete(strDest); + if (fs::exists(pathDest) && fs::is_directory(pathDest)) { pathDest /= "bootstrap.dat"; + } try { FILE* file = fopen(pathDest.string().c_str(), "wb"); @@ -1489,7 +1492,7 @@ UniValue dumpbootstrap(const UniValue& params, bool fHelp) << (unsigned int)GetSerializeSize(fileout, block) << block; } - } catch(const boost::filesystem::filesystem_error &e) { + } catch(const fs::filesystem_error& e) { throw JSONRPCError(RPC_MISC_ERROR, "Error: Bootstrap dump failed!"); } From f23c5e5a73347f34db158f9e6e742a8081c56d09 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 3 Sep 2024 10:40:37 +0000 Subject: [PATCH 142/146] Add blockhash parameter to getrawtransaction --- qa/rpc-tests/rawtransactions.py | 26 ++++++++++- src/main.cpp | 81 +++++++++++++++++---------------- src/main.h | 2 +- src/rpc/rawtransaction.cpp | 55 +++++++++++++++++----- 4 files changed, 112 insertions(+), 52 deletions(-) diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py index 0dd779183..0bb711dcd 100755 --- a/qa/rpc-tests/rawtransactions.py +++ b/qa/rpc-tests/rawtransactions.py @@ -13,7 +13,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, initialize_chain_clean, \ - start_nodes, connect_nodes_bi + start_nodes, connect_nodes_bi, assert_raises from decimal import Decimal @@ -70,6 +70,30 @@ def run_test(self): assert_equal("Missing inputs" in errorString, True); + ##################################### + # getrawtransaction with block hash # + ##################################### + + # make a tx by sending then generate 2 blocks; block1 has the tx in it + tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1) + block1, block2 = self.nodes[2].generate(2) + self.sync_all() + # We should be able to get the raw transaction by providing the correct block + gottx = self.nodes[0].getrawtransaction(tx, 1, block1) + assert_equal(gottx['txid'], tx) + assert_equal(gottx['in_active_chain'], True) + # We should not have the 'in_active_chain' flag when we don't provide a block + gottx = self.nodes[0].getrawtransaction(tx, 1) + assert_equal(gottx['txid'], tx) + assert 'in_active_chain' not in gottx + # We should not get the tx if we provide an unrelated block + assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, block2) + # An invalid block hash should raise errors + assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, True) + assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "foobar") + assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "abcd1234") + assert_raises(JSONRPCException, self.nodes[0].getrawtransaction, tx, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ######################### # RAW TX MULTISIG TESTS # ######################### diff --git a/src/main.cpp b/src/main.cpp index 4ba22ea07..ebe653d7f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1554,49 +1554,57 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return res; } -/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ -bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) +/** + * Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock. + * If blockIndex is provided, the transaction is fetched from the corresponding block. + */ +bool GetTransaction(const uint256& hash, CTransaction& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, bool fAllowSlow, const CBlockIndex* blockIndex) { - CBlockIndex *pindexSlow = NULL; + const CBlockIndex* pindexSlow = blockIndex; LOCK(cs_main); - if (mempool.lookup(hash, txOut)) - { - return true; - } + if (!blockIndex) { + if (mempool.lookup(hash, txOut)) + { + return true; + } - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); - if (file.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - CBlockHeader header; - try { - file >> header; - fseek(file.Get(), postx.nTxOffset, SEEK_CUR); - file >> txOut; - } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(hash, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + if (file.IsNull()) + return error("%s: OpenBlockFile failed", __func__); + CBlockHeader header; + try { + file >> header; + fseek(file.Get(), postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + hashBlock = header.GetHash(); + if (txOut.GetHash() != hash) + return error("%s: txid mismatch", __func__); + return true; } - hashBlock = header.GetHash(); - if (txOut.GetHash() != hash) - return error("%s: txid mismatch", __func__); - return true; + + // transaction not found in index, nothing more can be done + return false; } - } - if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it - int nHeight = -1; - { - CCoinsViewCache &view = *pcoinsTip; - const CCoins* coins = view.AccessCoins(hash); - if (coins) - nHeight = coins->nHeight; + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it + int nHeight = -1; + { + CCoinsViewCache &view = *pcoinsTip; + const CCoins* coins = view.AccessCoins(hash); + if (coins) + nHeight = coins->nHeight; + } + if (nHeight > 0) + pindexSlow = chainActive[nHeight]; } - if (nHeight > 0) - pindexSlow = chainActive[nHeight]; } if (pindexSlow) { @@ -1615,11 +1623,6 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P return false; } - - - - - ////////////////////////////////////////////////////////////////////////////// // // CBlock and CBlockIndex diff --git a/src/main.h b/src/main.h index d4888d5d3..9cdec11f6 100644 --- a/src/main.h +++ b/src/main.h @@ -291,7 +291,7 @@ bool IsInitialBlockDownload(const CChainParams& chainParams); */ std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ -bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); +bool GetTransaction(const uint256& hash, CTransaction& tx, const Consensus::Params& params, uint256& hashBlock, bool fAllowSlow = false, const CBlockIndex* blockIndex = nullptr); /** Find the best known block, and make it the tip of the block chain */ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 754ea9c22..45df96665 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -261,25 +261,30 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) UniValue getrawtransaction(const UniValue& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 2) + if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( - "getrawtransaction \"txid\" ( verbose )\n" - "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" - "or there is an unspent output in the utxo for this transaction. To make it always work,\n" - "you need to maintain a transaction index, using the -txindex command line option.\n" + "getrawtransaction \"txid\" ( verbose \"blockhash\" )\n" + "\nNOTE: If \"blockhash\" is not provided and the -txindex option is not enabled, then this call only\n" + "works for mempool transactions. If either \"blockhash\" is provided or the -txindex option is\n" + "enabled, it also works for blockchain transactions. If the block which contains the transaction\n" + "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n" + "provided, only that block will be searched and if the transaction is in the mempool or other\n" + "blocks, or if this node does not have the given block available, the transaction will not be found.\n" "\nReturn the raw transaction data.\n" "\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n" "If verbose is non-zero, returns an Object with information about 'txid'.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" - "2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n" + "2. verbose (numeric, optional, default=0) If 0, return a string of hex-encoded data, otherwise return a JSON object\n" + "3. \"blockhash\" (string, optional) The block in which to look for the transaction\n" "\nResult (if verbose is not set or set to 0):\n" "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" "\nResult (if verbose > 0):\n" "{\n" + " \"in_active_chain\": b, (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" " \"size\" : n, (numeric) The transaction size\n" @@ -352,21 +357,48 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) + HelpExampleCli("getrawtransaction", "\"mytxid\"") + HelpExampleCli("getrawtransaction", "\"mytxid\" 1") + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1") + + HelpExampleCli("getrawtransaction", "\"mytxid\" 0 \"myblockhash\"") + + HelpExampleCli("getrawtransaction", "\"mytxid\" 1 \"myblockhash\"") ); + LOCK(cs_main); + bool in_active_chain = true; uint256 hash = ParseHashV(params[0], "parameter 1"); + CBlockIndex* blockindex = nullptr; bool fVerbose = false; if (params.size() > 1) fVerbose = (params[1].getInt() != 0); - LOCK(cs_main); + if (params.size() > 2) { + uint256 blockhash = ParseHashV(params[2], "parameter 3"); + if (!blockhash.IsNull()) { + BlockMap::iterator it = mapBlockIndex.find(blockhash); + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found"); + } + blockindex = it->second; + in_active_chain = chainActive.Contains(blockindex); + } + } CTransaction tx; - uint256 hashBlock; - if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + uint256 hash_block; + if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, true, blockindex)) { + std::string errmsg; + if (blockindex) { + if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) { + throw JSONRPCError(RPC_MISC_ERROR, "Block not available"); + } + errmsg = "No such transaction found in the provided block"; + } else { + errmsg = fTxIndex + ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries"; + } + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions."); + } string strHex = EncodeHexTx(tx); @@ -374,8 +406,9 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) return strHex; UniValue result(UniValue::VOBJ); + if (blockindex) result.pushKV("in_active_chain", in_active_chain); result.pushKV("hex", strHex); - TxToJSON(tx, hashBlock, result); + TxToJSON(tx, hash_block, result); return result; } From 3223111909d2b4b60dd6f42a8c55df55f3d18291 Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Sun, 8 Sep 2024 10:51:06 +0000 Subject: [PATCH 143/146] Versioning changes for 2.1.0-rc1 --- README.md | 2 +- configure.ac | 6 +- contrib/debian/changelog | 6 + doc/man/bitcoinz-cli.1 | 6 +- doc/man/bitcoinz-tx.1 | 6 +- doc/man/bitcoinzd.1 | 6 +- doc/man/zcash-fetch-params.1 | 2 +- doc/release-notes/release-notes-2.1.0-rc1.md | 159 +++++++++++++++++++ src/clientversion.h | 6 +- 9 files changed, 182 insertions(+), 17 deletions(-) create mode 100644 doc/release-notes/release-notes-2.1.0-rc1.md diff --git a/README.md b/README.md index c691525f7..6d958887a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# BitcoinZ 2.0.10 +# BitcoinZ 2.1.0-rc1 **Keep running wallet to strengthen the BitcoinZ network. Backup your wallet in many locations & keep your coins wallet offline.** ### Ports: diff --git a/configure.ac b/configure.ac index c78379014..78d74b055 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ AC_PREREQ([2.69]) define(_CLIENT_VERSION_MAJOR, 2) -define(_CLIENT_VERSION_MINOR, 0) -define(_CLIENT_VERSION_REVISION, 10) -define(_CLIENT_VERSION_BUILD, 50) +define(_CLIENT_VERSION_MINOR, 1) +define(_CLIENT_VERSION_REVISION, 0) +define(_CLIENT_VERSION_BUILD, 25) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/debian/changelog b/contrib/debian/changelog index e9ef3fd14..55bd3ac85 100644 --- a/contrib/debian/changelog +++ b/contrib/debian/changelog @@ -1,3 +1,9 @@ +bitcoinz (2.1.0-rc1) stable; urgency=medium + + * 2.1.0-rc1 release. + + -- The BitcoinZ Community Sep 2024 + bitcoinz (2.0.10) stable; urgency=high * 2.0.10 release. diff --git a/doc/man/bitcoinz-cli.1 b/doc/man/bitcoinz-cli.1 index d1941e036..201e3922d 100644 --- a/doc/man/bitcoinz-cli.1 +++ b/doc/man/bitcoinz-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH BITCOINZ-CLI "1" "August 2024" "bitcoinz-cli v2.0.10" "User Commands" +.TH BITCOINZ-CLI "1" "September 2024" "bitcoinz-cli v2.1.0-rc1" "User Commands" .SH NAME -bitcoinz-cli \- manual page for bitcoinz-cli v2.0.10 +bitcoinz-cli \- manual page for bitcoinz-cli v2.1.0\-rc1 .SH DESCRIPTION -BitcoinZ RPC client version v2.0.10 +BitcoinZ RPC client version v2.1.0\-rc1 .PP In order to ensure you are adequately protecting your privacy when using BitcoinZ, please see . diff --git a/doc/man/bitcoinz-tx.1 b/doc/man/bitcoinz-tx.1 index f243b6435..d8775b095 100644 --- a/doc/man/bitcoinz-tx.1 +++ b/doc/man/bitcoinz-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH BITCOINZ-TX "1" "August 2024" "bitcoinz-tx v2.0.10" "User Commands" +.TH BITCOINZ-TX "1" "September 2024" "bitcoinz-tx v2.1.0-rc1" "User Commands" .SH NAME -bitcoinz-tx \- manual page for bitcoinz-tx v2.0.10 +bitcoinz-tx \- manual page for bitcoinz-tx v2.1.0\-rc1 .SH DESCRIPTION -BitcoinZ bitcoinz\-tx utility version v2.0.10 +BitcoinZ bitcoinz\-tx utility version v2.1.0\-rc1 .SS "Usage:" .TP bitcoinz\-tx [options] [commands] diff --git a/doc/man/bitcoinzd.1 b/doc/man/bitcoinzd.1 index 57f0576a7..c0a7df870 100644 --- a/doc/man/bitcoinzd.1 +++ b/doc/man/bitcoinzd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH BITCOINZD "1" "August 2024" "bitcoinzd v2.0.10" "User Commands" +.TH BITCOINZD "1" "September 2024" "bitcoinzd v2.1.0-rc1" "User Commands" .SH NAME -bitcoinzd \- manual page for bitcoinzd v2.0.10 +bitcoinzd \- manual page for bitcoinzd v2.1.0\-rc1 .SH DESCRIPTION -BitcoinZ Daemon version v2.0.10 +BitcoinZ Daemon version v2.1.0\-rc1 .PP In order to ensure you are adequately protecting your privacy when using BitcoinZ, please see . diff --git a/doc/man/zcash-fetch-params.1 b/doc/man/zcash-fetch-params.1 index 204d6c49a..cc3b8f243 100644 --- a/doc/man/zcash-fetch-params.1 +++ b/doc/man/zcash-fetch-params.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6. -.TH ZCASH-FETCH-PARAMS "1" "August 2024" "Zcash - zcash-fetch-params" "User Commands" +.TH ZCASH-FETCH-PARAMS "1" "September 2024" "Zcash - zcash-fetch-params" "User Commands" .SH NAME zcash-fetch-params \- Downloads the Zcash network parameters .SH DESCRIPTION diff --git a/doc/release-notes/release-notes-2.1.0-rc1.md b/doc/release-notes/release-notes-2.1.0-rc1.md new file mode 100644 index 000000000..0841351fd --- /dev/null +++ b/doc/release-notes/release-notes-2.1.0-rc1.md @@ -0,0 +1,159 @@ +Notable changes +=============== + +Reduce upload traffic +--------------------- + +A major part of the outbound traffic is caused by serving historic blocks to +other nodes in initial block download state. + +It is now possible to reduce the total upload traffic via the `-maxuploadtarget` +parameter. This is *not* a hard limit but a threshold to minimize the outbound +traffic. When the limit is about to be reached, the uploaded data is cut by not +serving historic blocks (blocks older than one week). +Moreover, any SPV peer is disconnected when they request a filtered block. + +This option can be specified in MiB per day and is turned off by default +(`-maxuploadtarget=0`). +The recommended minimum is 1152 * MAX_BLOCK_SIZE (currently 2304MB) per day. + +Whitelisted peers will never be disconnected, although their traffic counts for +calculating the target. + +A more detailed documentation about keeping traffic low can be found in +[/doc/reducetraffic.md](/doc/reducetraffic.md). + +Removal of time adjustment and the -maxtimeadjustment= option +------------------------------------------------------------- + +Prior to v2.1.0, `bitcoinzd` would adjust the local time that it used by up +to 70 minutes, according to a median of the times sent by the first 200 peers +to connect to it. This mechanism was inherently insecure, since an adversary +making multiple connections to the node could effectively control its time +within that +/- 70 minute window (this is called a "timejacking attack"). + +In the v2.1.0 security release, in addition to other mitigations for +timejacking attacks, the maximum time adjustment was set to zero by default. +This effectively disabled time adjustment; however, a `-maxtimeadjustment=` +option was provided to override this default. + +As a simplification the time adjustment code has now been completely removed, +together with `-maxtimeadjustment=`. Node operators should instead simply +ensure that local time is set reasonably accurately. + +If it appears that the node has a significantly different time than its peers, +a warning will still be logged and indicated on the metrics screen if enabled. + +Option handling +--------------- + +- The `-reindex` and `-reindex-chainstate` options now imply `-rescan` + (provided that the wallet is enabled and pruning is disabled, and unless + `-rescan=0` is specified explicitly). + +Fixes +----- + +Resolved a critical bug that prevented the program from starting up correctly in some cases. +The issue occurred during the startup process when the program attempted to rescan the latest indexed blocks. +A failure in the LoadBlockIndex() function, caused by a corrupted index file, was identified as the root cause. + +Changed command-line options +----------------------------- +- `-debuglogfile=` can be used to specify an alternative debug logging file. + +Upgrading +========= + +How to Upgrade +-------------- + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over or bitcoinzd (on Linux). + +If you are upgrading from version v2.0.9, due a bug on that version, to fix +a chainstate database corruption, you need to run this release with `-reindex` +option to rebuild the chainstate data structures. This will take anywhere from +30 minutes to several hours, depending on the speed of your machine. + +If you are upgrading from a version prior to v2.0.9, then the '-reindex' operation +is not necessary. + +On Windows, do not forget to uninstall all earlier versions of the Bitcoin +client first. + + +### RPC and REST + +RPC Changes +----------- + +- The `estimatepriority` RPC call has been removed. +- The `priority_delta` argument to the `prioritisetransaction` RPC call now has + no effect and must be set to a dummy value (0 or null). + +Changes to Transaction Fee Selection +------------------------------------ + +- The `-sendfreetransactions` option has been removed. This option used to + instruct the wallet's legacy transaction creation APIs (`sendtoaddress`, + `sendmany`, and `fundrawtransaction`) to use a zero fee for "small" transactions + that spend "old" inputs. It will now cause a warning on node startup if used. + +Changes to Block Template Construction +-------------------------------------- + +- The block template construction algorithm no longer favours transactions that + were previously considered "high priority" because they spent older inputs. The + `-blockprioritysize` config option, which configured the portion of the block + reserved for these transactions, has been removed and will now cause a warning + if used. + +Removal of Priority Estimation +------------------------------ + +- Estimation of "priority" needed for a transaction to be included within a target + number of blocks, and the associated `estimatepriority` RPC call, have been + removed. The format for `fee_estimates.dat` has also changed to no longer save + these priority estimates. It will automatically be converted to the new format + which is not readable by prior versions of the software. + +### Configuration and command-line options + +### Block and transaction handling + +### P2P protocol and network code + +The p2p alert system has been removed in #7692 and the 'alert' message is no longer supported. + +Direct headers announcement (BIP 130) +------------------------------------- + +Between compatible peers, BIP 130 direct headers announcement is used. This +means that blocks are advertized by announcing their headers directly, instead +of just announcing the hash. In a reorganization, all new headers are sent, +instead of just the new tip. This can often prevent an extra roundtrip before +the actual block is downloaded. + + +Fee filtering of invs (BIP 133) +------------------------------------ + +The optional new p2p message "feefilter" is implemented and the protocol +version is bumped to 70013. Upon receiving a feefilter message from a peer, +a node will not send invs for any transactions which do not meet the filter +feerate. [BIP 133](https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki) + +### Validation + +### Build system + +### Wallet + +### GUI + +### Tests + +### Miscellaneous + diff --git a/src/clientversion.h b/src/clientversion.h index c523ef40b..591c86557 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -17,9 +17,9 @@ //! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 2 -#define CLIENT_VERSION_MINOR 0 -#define CLIENT_VERSION_REVISION 10 -#define CLIENT_VERSION_BUILD 50 +#define CLIENT_VERSION_MINOR 1 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 25 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true From 15aa8eb129c8b7b6f90a430deb5e5d9f4c137d4c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 9 Sep 2024 08:20:47 +0000 Subject: [PATCH 144/146] Add support for 'arm64-apple-darwin' building --- contrib/guix/README.md | 4 ++-- contrib/guix/guix-build | 2 +- contrib/guix/guix-codesign | 2 +- depends/packages/bdb.mk | 3 ++- depends/packages/rust.mk | 7 +++++++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/contrib/guix/README.md b/contrib/guix/README.md index 76ce34a4e..ab184c824 100644 --- a/contrib/guix/README.md +++ b/contrib/guix/README.md @@ -75,7 +75,7 @@ crucial differences: 1. Since only Windows and macOS build outputs require codesigning, the `HOSTS` environment variable will have a sane default value of `x86_64-w64-mingw32 - x86_64-apple-darwin` instead of all the platforms. + x86_64-apple-darwin arm64-apple-darwin` instead of all the platforms. 2. The `guix-codesign` command ***requires*** a `DETACHED_SIGS_REPO` flag. * _**DETACHED_SIGS_REPO**_ @@ -223,7 +223,7 @@ details. bootstrappable build. _(defaults to "x86\_64-linux-gnu aarch64-linux-gnu - x86\_64-w64-mingw32 x86\_64-apple-darwin")_ + x86\_64-w64-mingw32 x86\_64-apple-darwin arm64-apple-darwin")_ * _**SOURCES_PATH**_ diff --git a/contrib/guix/guix-build b/contrib/guix/guix-build index 97417adcf..82b3e3455 100755 --- a/contrib/guix/guix-build +++ b/contrib/guix/guix-build @@ -75,7 +75,7 @@ mkdir -p "$VERSION_BASE" # Default to building for all supported HOSTs (overridable by environment) # powerpc64le-linux-gnu currently disabled due non-determinism issues across build arches. -export HOSTS="${HOSTS:-x86_64-linux-gnu aarch64-linux-gnu x86_64-w64-mingw32 x86_64-apple-darwin}" +export HOSTS="${HOSTS:-x86_64-linux-gnu aarch64-linux-gnu x86_64-w64-mingw32 x86_64-apple-darwin arm64-apple-darwin}" # Usage: distsrc_for_host HOST # diff --git a/contrib/guix/guix-codesign b/contrib/guix/guix-codesign index cb1278ed8..85a79c274 100755 --- a/contrib/guix/guix-codesign +++ b/contrib/guix/guix-codesign @@ -91,7 +91,7 @@ fi ################ # Default to building for all supported HOSTs (overridable by environment) -export HOSTS="${HOSTS:-x86_64-w64-mingw32 x86_64-apple-darwin}" +export HOSTS="${HOSTS:-x86_64-w64-mingw32 x86_64-apple-darwin arm64-apple-darwin}" # Usage: distsrc_for_host HOST # diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index aec3c3b79..b904faa3a 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -23,7 +23,8 @@ endef define $(package)_preprocess_cmds patch -p1 <$($(package)_patch_dir)/clang-12-stpcpy-issue.diff && \ - patch -p1 <$($(package)_patch_dir)/winioctl-and-atomic_init_db.patch + patch -p1 <$($(package)_patch_dir)/winioctl-and-atomic_init_db.patch && \ + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist endef define $(package)_config_cmds diff --git a/depends/packages/rust.mk b/depends/packages/rust.mk index 39744f764..534688a39 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -18,6 +18,10 @@ $(package)_rust-std-x86_64-pc-linux-gnu_sha256_hash=ed301dff3a26da496784ca3de523 $(package)_rust-std-x86_64-apple-darwin_file_name=rust-std-$($(package)_version)-x86_64-apple-darwin.tar.gz $(package)_rust-std-x86_64-apple-darwin_sha256_hash=069dcd20861c1031a2e1484ef4085503b1e239fdca6b7c6dd4d834c9cc8aff70 +# Rust-std aarch64-apple-darwin +$(package)_rust-std-aarch64-apple-darwin_file_name=rust-std-$($(package)_version)-aarch64-apple-darwin.tar.gz +$(package)_rust-std-aarch64-apple-darwin_sha256_hash=ffdbca3f1eaec4fefa6dd461bea44cb2e57a5e1ad0637fc0eabc02ccb8e7e804 + # Rust-std aarch64-unknown-linux-gnu $(package)_rust-std-aarch64-unknown-linux-gnu_file_name=rust-std-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz $(package)_rust-std-aarch64-unknown-linux-gnu_sha256_hash=6cdbe8c2b502ca90f42c581b8906b725ccc55bbb3427a332379236bf22be59b3 @@ -28,6 +32,7 @@ $(package)_rust-std-x86_64-w64-mingw32_sha256_hash=0d6a58268bd94d66280812c042c41 $(package)_extra_sources = $($(package)_rust-std-x86_64-pc-linux-gnu_file_name) $(package)_extra_sources += $($(package)_rust-std-x86_64-apple-darwin_file_name) +$(package)_extra_sources += $($(package)_rust-std-aarch64-apple-darwin_file_name) $(package)_extra_sources += $($(package)_rust-std-aarch64-unknown-linux-gnu_file_name) $(package)_extra_sources += $($(package)_rust-std-x86_64-w64-mingw32_file_name) @@ -36,6 +41,7 @@ ifneq ($(WITH_GUIX),) $(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu $(package)_rust_target_aarch64-unknown-linux-gnu=aarch64-unknown-linux-gnu $(package)_rust_target_x86_64-apple-darwin=x86_64-apple-darwin +$(package)_rust_target_aarch64-apple-darwin=aarch64-apple-darwin $(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu define rust_target @@ -73,6 +79,7 @@ define $(package)_fetch_cmds echo "DEBUG: canonical_host=$(canonical_host) host_os=$(host_os) build=$(build) build_os=$(build_os) CC=$(CC) CXX=$(CXX)" && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_file_name_$(build_os)),$($(package)_file_name_$(build_os)),$($(package)_sha256_hash_$(build_os))) && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-x86_64-pc-linux-gnu_file_name),$($(package)_rust-std-x86_64-pc-linux-gnu_file_name),$($(package)_rust-std-x86_64-pc-linux-gnu_sha256_hash)) && \ +$(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-aarch64-apple-darwin_file_name),$($(package)_rust-std-aarch64-apple-darwin_file_name),$($(package)_rust-std-aarch64-apple-darwin_sha256_hash)) && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-x86_64-apple-darwin_file_name),$($(package)_rust-std-x86_64-apple-darwin_file_name),$($(package)_rust-std-x86_64-apple-darwin_sha256_hash)) && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-aarch64-unknown-linux-gnu_file_name),$($(package)_rust-std-aarch64-unknown-linux-gnu_file_name),$($(package)_rust-std-aarch64-unknown-linux-gnu_sha256_hash)) && \ $(call fetch_file,$(package),$($(package)_download_path),$($(package)_rust-std-x86_64-w64-mingw32_file_name),$($(package)_rust-std-x86_64-w64-mingw32_file_name),$($(package)_rust-std-x86_64-w64-mingw32_sha256_hash)) From a6770b3e82867cd0cec5e463ef1f2d13dee2673c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Mon, 9 Sep 2024 16:54:01 +0000 Subject: [PATCH 145/146] [cleanup] Remove coin age priority completely --- src/gtest/test_mempool.cpp | 3 +-- src/main.cpp | 4 ++-- src/test/test_bitcoin.cpp | 6 +----- src/txmempool.cpp | 10 ++++------ src/txmempool.h | 3 +-- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/gtest/test_mempool.cpp b/src/gtest/test_mempool.cpp index 1b46dc851..9c2b8bfa8 100644 --- a/src/gtest/test_mempool.cpp +++ b/src/gtest/test_mempool.cpp @@ -92,9 +92,8 @@ TEST(Mempool, PriorityStatsDoNotCrash) { CAmount nFees = 0; int64_t nTime = 0x58e5fed9; unsigned int nHeight = 92045; - CAmount inChainInputValue; - CTxMemPoolEntry entry(tx, nFees, nTime, nHeight, true, inChainInputValue, false, 0, SPROUT_BRANCH_ID); + CTxMemPoolEntry entry(tx, nFees, nTime, nHeight, true, false, 0, SPROUT_BRANCH_ID); // Check it does not crash (ie. the death test fails) EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), ""); diff --git a/src/main.cpp b/src/main.cpp index ebe653d7f..2aa96dfa7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1392,7 +1392,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; - CAmount inChainInputValue; + // nModifiedFees includes any fee deltas from PrioritiseTransaction CAmount nModifiedFees = nFees; pool.ApplyDelta(hash, nModifiedFees); @@ -1413,7 +1413,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // it has passed ContextualCheckInputs and therefore this is correct. auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - CTxMemPoolEntry entry(tx, nFees, GetTime(), chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, consensusBranchId); + CTxMemPoolEntry entry(tx, nFees, GetTime(), chainActive.Height(), pool.HasNoInputsOf(tx), fSpendsCoinbase, nSigOps, consensusBranchId); unsigned int nSize = entry.GetTxSize(); if (txFeeRate) { *txFeeRate = CFeeRate(nFees, nSize); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index ed827bcdc..44841677b 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -224,12 +224,8 @@ TestChain100Setup::~TestChain100Setup() CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) { CTransaction txn(tx); bool hasNoDependencies = pool ? pool->HasNoInputsOf(tx) : hadNoDependencies; - // Hack to assume either its completely dependent on other mempool txs or not at all - CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0; - return CTxMemPoolEntry(tx, nFee, nTime, nHeight, - hasNoDependencies, inChainValue, - spendsCoinbase, sigOpCount, nBranchId); + return CTxMemPoolEntry(tx, nFee, nTime, nHeight, hasNoDependencies, spendsCoinbase, sigOpCount, nBranchId); } void Shutdown(void* parg) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 49c0c3c9a..90d71e102 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -22,11 +22,11 @@ using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, unsigned int _nHeight, - bool poolHasNoInputsOf, CAmount _inChainInputValue, - bool _spendsCoinbase, unsigned int _sigOps, uint32_t _nBranchId): + bool poolHasNoInputsOf, bool _spendsCoinbase, + unsigned int _sigOps, uint32_t _nBranchId): tx(_tx), nFee(_nFee), nTime(_nTime), nHeight(_nHeight), - hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), - spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps), nBranchId(_nBranchId) + hadNoDependencies(poolHasNoInputsOf), spendsCoinbase(_spendsCoinbase), + sigOpCount(_sigOps), nBranchId(_nBranchId) { nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); nUsageSize = RecursiveDynamicUsage(tx); @@ -36,8 +36,6 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; nModFeesWithDescendants = nFee; - CAmount nValueIn = tx.GetValueOut()+nFee; - assert(inChainInputValue <= nValueIn); } CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) diff --git a/src/txmempool.h b/src/txmempool.h index 80642b208..bd13268f5 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -60,7 +60,6 @@ class CTxMemPoolEntry int64_t nTime; //!< Local time when entering the mempool unsigned int nHeight; //!< Chain height when entering the mempool bool hadNoDependencies; //!< Not dependent on any other txs when it entered the mempool - CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain bool spendsCoinbase; //!< keep track of transactions that spend a coinbase unsigned int sigOpCount; //!< Legacy sig ops plus P2SH sig op count int64_t feeDelta; //!< Used for determining the priority of the transaction for mining in a block @@ -78,7 +77,7 @@ class CTxMemPoolEntry public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, unsigned int _nHeight, - bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase, + bool poolHasNoInputsOf, bool spendsCoinbase, unsigned int nSigOps, uint32_t nBranchId); CTxMemPoolEntry(const CTxMemPoolEntry& other); From 29fa24f09debcbb8778cb9e1072098f809c21a9c Mon Sep 17 00:00:00 2001 From: MarkLTZ Date: Tue, 10 Sep 2024 10:15:27 +0000 Subject: [PATCH 146/146] Add listaddresses RPC call --- src/wallet/rpcwallet.cpp | 31 ++++++++++++++++++++++++++++ src/wallet/test/rpc_wallet_tests.cpp | 6 ++++++ 2 files changed, 37 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ae81aeb43..56daa4dd8 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -203,6 +203,36 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp) return EncodeDestination(keyID); } +UniValue listaddresses(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() != 0) + throw runtime_error( + "listaddresses\n" + "\nReturns the list of transparent addresses belonging to the wallet.\n" + "\nResult:\n" + "[ (json array of string)\n" + " \"address\" (string) a transparent address belonging to the wallet\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("listaddresses", "") + + HelpExampleRpc("listaddresses", "") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + // Find all addresses + UniValue ret(UniValue::VARR); + for (const std::pair& item : pwalletMain->mapAddressBook) { + const CTxDestination& dest = item.first; + ret.push_back(EncodeDestination(dest)); + } + return ret; +} + static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) { CAmount curBalance = pwalletMain->GetBalance(); @@ -4387,6 +4417,7 @@ static const CRPCCommand commands[] = { "wallet", "importaddress", &importaddress, true }, { "wallet", "importpubkey", &importpubkey, true }, { "wallet", "keypoolrefill", &keypoolrefill, true }, + { "wallet", "listaddresses", &listaddresses, true }, { "wallet", "listaddressgroupings", &listaddressgroupings, false }, { "wallet", "listlockunspent", &listlockunspent, false }, { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false }, diff --git a/src/wallet/test/rpc_wallet_tests.cpp b/src/wallet/test/rpc_wallet_tests.cpp index 5610d1b80..b457f0182 100644 --- a/src/wallet/test/rpc_wallet_tests.cpp +++ b/src/wallet/test/rpc_wallet_tests.cpp @@ -198,6 +198,12 @@ BOOST_AUTO_TEST_CASE(rpc_wallet) /* Correct address, message and signature*/ BOOST_CHECK(CallRPC("verifymessage " + EncodeDestination(demoAddress) + " " + retValue.get_str() + " mymessage").get_bool() == true); + /********************************* + * listaddresses + *********************************/ + BOOST_CHECK_THROW(CallRPC("listaddresses true"), runtime_error); + BOOST_CHECK_NO_THROW(retValue = CallRPC("listaddresses")); + /********************************* * fundrawtransaction *********************************/

MGPN8c99)~lKtc$L#o`!F@tI8Kq&KrpGY8*o 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 JRKBnb`5$=MrF#GX literal 0 HcmV?d00001 diff --git a/share/pixmaps/bitcoinz256.xpm b/share/pixmaps/bitcoinz256.xpm new file mode 100644 index 000000000..5868cd032 --- /dev/null +++ b/share/pixmaps/bitcoinz256.xpm @@ -0,0 +1,516 @@ +/* XPM */ +static char *_9949e1c85544efe925357d3dbb87890wda0Geqs2qMDm3ll[] = { +/* columns rows colors chars-per-pixel */ +"256 256 254 2 ", +" c None", +". c #151937", +"X c #161B39", +"o c #1A1E39", +"O c #181B37", +"+ c #0C143E", +"@ c #1D223B", +"# c #2C2C2C", +"$ c #272727", +"% c #22253C", +"& c #25293D", +"* c #2A2D3D", +"= c #2E313E", +"- c #343434", +"; c #33343E", +": c #3B3C3D", +"> c #37383E", +", c #382F33", +"< c #433A38", +"1 c #503836", +"2 c #613D3D", +"3 c #48443E", +"4 c #554C3C", +"5 c #5A523C", +"6 c #665939", +"7 c #725C33", +"8 c #6E613A", +"9 c #766739", +"0 c #7C652E", +"q c #151B43", +"w c #1D2542", +"e c #1C2A4D", +"r c #19264A", +"t c #192A51", +"y c #222542", +"u c #242B44", +"i c #2A2D43", +"p c #232D4B", +"a c #2B2D4B", +"s c #23224D", +"d c #2D3243", +"f c #24314C", +"g c #2B334B", +"h c #2F394F", +"j c #333543", +"k c #363A44", +"l c #3C3D42", +"z c #343A4B", +"x c #37384B", +"c c #272C52", +"v c #2A3354", +"b c #323353", +"n c #373B54", +"m c #322F4C", +"M c #373B62", +"N c #413E47", +"B c #423E56", +"V c #3F4043", +"C c #3E424C", +"Z c #3C4054", +"A c #3A4063", +"S c #444444", +"D c #494643", +"F c #4C4A43", +"G c #44444B", +"H c #4B4B4B", +"J c #47474B", +"K c #524C43", +"L c #574847", +"P c #555243", +"I c #5A5544", +"U c #57554A", +"Y c #474853", +"T c #4C5256", +"R c #535353", +"E c #5B5B5B", +"W c #585857", +"Q c #5B4F51", +"! c #634545", +"~ c #644B4B", +"^ c #644948", +"/ c #635D44", +"( c #645C4A", +") c #645857", +"_ c #634E51", +"` c #6A6547", +"' c #746C47", +"] c #7C744A", +"[ c #797348", +"{ c #66615B", +"} c #756F51", +"| c #4C5661", +" . c #636363", +".. c #6A6A6A", +"X. c #676767", +"o. c #657176", +"O. c #5E6368", +"+. c #3F403F", +"@. c #886A2B", +"#. c #997626", +"$. c #89773A", +"%. c #8D6F2C", +"&. c #A57B21", +"*. c #847B4B", +"=. c #867A45", +"-. c #857F50", +";. c #B4861C", +":. c #9A8539", +">. c #A68728", +",. c #B59939", +"<. c #AD9031", +"1. c #B9A63A", +"2. c #CF9513", +"3. c #D8A816", +"4. c #FEAE01", +"5. c #F6AA06", +"6. c #FFB301", +"7. c #FFBB00", +"8. c #FDB40B", +"9. c #FCBA0A", +"0. c #FDB611", +"q. c #FDB915", +"w. c #FDBC1B", +"e. c #F1B81F", +"r. c #E9AE0F", +"t. c #C39D29", +"y. c #C8AA37", +"u. c #D6B63A", +"i. c #CFAC2E", +"p. c #FDBF21", +"a. c #F3BC24", +"s. c #E7BA31", +"d. c #EA9F07", +"f. c #FFC301", +"g. c #FFCC02", +"h. c #FEC80B", +"j. c #FFD304", +"k. c #FFDB04", +"l. c #FFDC09", +"z. c #FFD40C", +"x. c #FFC719", +"c. c #FFD518", +"v. c #E7CB1B", +"b. c #FFE506", +"n. c #FFF902", +"m. c #DBC93B", +"M. c #FDC224", +"N. c #FDC52B", +"B. c #FDCD2D", +"V. c #FDCC25", +"C. c #FED32D", +"Z. c #FFDB2C", +"A. c #FFD924", +"S. c #FDCB33", +"D. c #FDCE3A", +"F. c #F3C837", +"G. c #FDD334", +"H. c #FFDB34", +"J. c #FDD43C", +"K. c #FFDC3C", +"L. c #F5DB37", +"P. c #E9CA38", +"I. c #FFE728", +"U. c #FFFC2D", +"Y. c #FFE335", +"T. c #FFED36", +"R. c #FFE43B", +"E. c #FFEC3C", +"W. c #F7E238", +"Q. c #FFF336", +"!. c #FFFD34", +"~. c #FFF43C", +"^. c #FFFC3C", +"/. c #8B824B", +"(. c #96894A", +"). c #9C9652", +"_. c #978F4E", +"`. c #A69749", +"'. c #A99B51", +"]. c #B29E4A", +"[. c #B7A648", +"{. c #ACA553", +"}. c #B7A854", +"|. c #BDB456", +" X c #B5AA49", +".X c #C9B849", +"XX c #C6B655", +"oX c #D3BB57", +"OX c #CEB446", +"+X c #D6C54A", +"@X c #D6C658", +"#X c #D3CB52", +"$X c #E7CA47", +"%X c #FDD443", +"&X c #FEDA44", +"*X c #FDD749", +"=X c #FDDC4C", +"-X c #F4D84A", +";X c #E5CB58", +":X c #E8D75A", +">X c #FDDD53", +",X c #FDDD5D", +" S S H H H H H H R S S S S S S S S S S S S H H H H H H H S S : - - ", +" - > S H H R H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H R H H S : - ", +" - : S H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H R H S : - ", +" - : H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H S - ", +" # > S R H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H R H > # ", +" # S H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R H S # ", +" # S H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R R S # ", +" S H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R H S ", +" - H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R H > ", +" S H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R S ", +" - S R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R H # ", +" - H R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R H - ", +" > H R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R H - ", +" - H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R H - ", +" - H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H > ", +" # H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R H - ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" : R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R S ", +" # H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l k k d d u u u u u u u u u u u u u u d d j k l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R - ", +" S H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l j d u w u u d l S 4 5 8 8 8 $.$.$.$.$.$.$.$.9 8 8 5 4 3 l d u u w p d j l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R H ", +" - R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l j u w u j S I 9 $.:.,.y.s.F.S.S.G.H.H.H.H.Z.Z.Z.Z.Z.Z.C.C.C.B.N.a.s.t.,.>.$.9 5 3 k p w u j l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R - ", +" H R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l d % y ; 3 ` $.].u.P.J.R.R.R.R.Y.K.H.G.G.G.S.S.S.S.S.S.S.S.S.B.B.B.B.B.B.C.C.A.A.A.A.C.N.a.i.<.$.8 3 j u u d l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R H ", +" - R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j y w ; 4 =.`.OX%XK.3X3XR.K.J.J.J.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.S.S.N.N.N.N.N.N.N.N.N.N.N.N.V.V.A.A.c.V.a.i.<.@.4 k u p j S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R > ", +" S R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S V j @ % S ' `.OX%X3X3X2X&X%X%XJ.D.D.D.D.D.D.D.D.D.D.D.S.D.S.S.S.S.S.S.S.S.S.N.N.N.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.N.V.A.c.c.M.i.<.9 3 u w d V S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S d @ & F =.[.$X3X9X3X&X%X%X%X%X%X%XJ.D.D.D.D.D.D.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.N.N.N.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.M.p.M.x.c.c.c.e.<.%.4 u e d S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" - R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S k @ @ F =.[.-X9X9X2X*X*X%X%X%X%X%X%X%XJ.J.D.D.D.D.D.D.D.D.D.D.D.D.S.D.S.S.S.S.S.S.S.S.S.N.N.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.M.M.p.p.p.w.w.x.c.c.w.t.%.3 u p k S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R > ", +" S R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S = @ > ` ].-X9X9X4X*X*X*X*X*X%X%X%X%X%X%X%XJ.J.J.J.J.D.D.D.D.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.N.N.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.M.M.p.p.p.p.w.w.w.w.x.c.c.w.<.8 l r d S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l & % F (.$X5X9X4X*X*X*X*X*X*X*X*X%X*X%X%X%X%X%XJ.%X%XJ.J.J.D.D.D.D.D.D.D.D.D.S.S.S.S.S.S.S.S.N.S.S.N.N.N.N.N.N.N.N.N.N.M.N.M.M.M.M.M.M.p.M.p.p.p.p.p.p.w.w.w.w.w.x.c.x.r.#.4 u p l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l @ & I [.=XuX5X=X*X=X*X*X*X*X*X*X*X*X*X*X*X%X%X%X%X%X%X%XJ.J.J.D.D.D.D.D.D.D.D.D.D.S.S.S.S.S.S.S.N.N.S.N.N.N.N.N.N.N.N.N.N.M.N.M.M.M.M.M.M.p.M.p.p.p.p.p.p.w.w.w.w.w.w.w.w.x.z.q.<.6 i e l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R # ", +" > R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l @ & ' .XwXuX4X=X=X=X=X=X*X*X*X*X*X*X*X*X%X%X%X%X%X%X%X%X%XJ.%XJ.J.D.D.D.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.S.S.N.N.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.p.p.p.p.p.w.w.w.w.w.w.w.w.w.w.w.x.z.h.2.0 d r k S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R > ", +" S R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l @ & ' oXyXyX4X=X=X=X=X=X=X=X*X*X*X*X*X*X*X*X%X*X*X%X%X%X%X%X%XJ.%XJ.J.D.D.D.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.S.S.B.B.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.M.M.M.p.p.w.p.w.w.w.w.w.w.w.w.w.q.w.q.h.h.3.@.j r l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" S H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S V @ * ' oXyXyX>X>X>X=X=X=X=X=X=X*X*X*X*X*X*X*X*X*X*X%X%X%X%X%X%X%XJ.J.D.D.D.D.D.D.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.S.B.N.N.N.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.M.p.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.q.q.q.h.z.3.0 d e l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S & % ` oXpXyX>X>X>X>X>X=X=X=X=X=X=X*X=X*X*X*X*X*X*X*X*X%X%X%X%X%X%X%X%X%XJ.D.D.D.D.D.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.S.S.N.N.N.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.p.p.M.w.w.w.w.w.w.w.w.w.w.w.w.w.w.q.q.q.q.q.h.z.3.7 p p S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S = O P XpXyX>X>X>X>X>X>X>X=X=X=X=X=X=X=X=X*X*X*X*X*X*X*X*X%X%X%X%X%X%X%X%XJ.J.D.J.J.D.J.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.S.S.B.N.B.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.M.M.M.p.w.w.w.w.w.w.w.w.w.w.w.w.w.q.q.q.q.q.q.0.h.h.;.5 r d 3 S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S l @ > ).rXpXeX>X>X>X>X>X>X>X>X>X=X=X=X=X=X=X=X=X*X*X*X*X*X*X*X%X%X%X%X%X%X%X%X%XJ.J.J.J.J.J.D.D.D.D.D.D.D.S.S.S.S.S.S.S.S.S.S.S.S.N.B.N.B.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.M.M.M.w.w.w.w.w.w.w.w.w.w.w.w.w.w.q.q.q.q.q.0.0.0.0.g.h.>.l r l S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S * @ ' :XpXwX>X>X>X>X>X>X>X>X=X>X=X>X=X=X=X=X*X=X=X*X*X*X*X*X*X*X*X%X%X%X%X%X%X%X%XJ.J.J.J.D.D.D.D.D.D.D.J.J.G.G.G.G.G.G.G.G.G.S.B.S.B.B.N.N.N.N.N.N.N.N.N.N.M.M.M.M.M.M.M.M.M.M.p.p.p.w.w.w.w.w.w.w.w.w.w.w.q.q.q.q.q.0.0.0.0.0.9.g.r.0 p p S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S S S l O F |.pXiX>X>X>X>X>X>X>X>X>X>X>X>X>X>X=X=X=X=X*X=X=X*X*X*X*X*X*X*X*X*X%X%X%X%X%X%X%XJ.J.J.J.J.K.R.R.E.R.R.Y.K.H.H.S.S.S.S.S.S.G.C.C.Z.Z.Z.I.Z.Z.C.B.N.N.N.N.N.M.M.M.M.M.M.M.M.M.M.p.p.w.w.w.w.w.w.w.w.w.w.w.q.q.q.q.q.q.q.0.0.0.0.0.0.h.g.2.4 t l S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S S S & @ *.rXlXeX>XeX>X>X>X>X>X>X>X>X>X>X>X>X=X=X=X=X=X=X=X=X*X*X*X*X*X*X*X*X*X%X%X%X%X%X%X&X2XE.E.R.J.P.u.1.<.:.$.9 8 8 8 4 4 4 4 4 4 6 8 8 9 $.:.<.t.i.s.N.C.Z.Z.A.V.N.M.M.M.M.M.M.M.M.M.p.p.w.p.w.w.w.w.w.w.w.w.w.q.q.q.q.q.q.q.0.0.0.0.0.8.0.8.g.9.%.p p S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S S : . S XXlXiX>XeXeXeX>X>X>X>X>X>X>X>X>X>X>X>X=X=X=X=X=X=X=X=X*X*X*X*X*X*X*X*X*X%X&X2X3X3X2X$Xu.,.:.9 5 3 > u w w w w u u u i d d d d d u u u w w w w i k 3 5 9 $.<.i.a.C.A.A.V.M.M.M.M.M.M.M.p.w.M.w.w.w.w.w.w.w.w.w.q.q.q.q.q.q.q.0.0.0.0.8.8.8.8.8.9.g.2.3 r l S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S = o ' tXlXeX>XeXeXeXeX>X>X>X>X>X>X>X>X>X>X>X>X=X=X=X=X=X=X*X*X*X*X*X*X*X*X*X=X3X9X2X$XOX:.8 3 = % @ % & j l l S S S S S S S S S S S S S S S S S S S S l l j i u w u d 3 8 #.,.s.V.c.c.V.M.M.M.w.w.M.w.w.w.w.w.w.w.w.w.w.w.q.q.q.q.q.q.0.0.0.0.8.8.0.8.8.8.g.9.0 e d S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S % * {.lXkX>XeXeXeXeXeXeX>X>X>X>X>X>X>X>X>X>X>X>X=X=X=X=X=X=X*X*X*X*X*X*X=X5X9X&XOX(.6 : & @ % ; > l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l j u w u : 6 #.i.M.c.c.x.x.w.M.w.w.w.w.w.w.w.w.w.w.w.q.q.q.q.q.q.0.0.0.0.0.8.0.8.8.8.8.f.g.;.j e S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S l O F vXlXrXeXeXeXeXeXeXeXeX>X>X>X>X>X>X>X>X>X>X>X>X=X=X=X=X=X=X*X=X*X=X5X9X=XOX/.4 * @ & ; : 3 S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S k u w d 4 $.t.x.c.c.x.p.w.w.w.w.w.w.w.w.w.q.q.q.q.q.q.q.0.0.0.0.0.8.0.8.8.8.8.8.8.g.r.4 t l S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" S H S S S S S S S S S S S S S S S S S S S S S S S S S > . ' kXlXeXeXeXeXeXeXeXeXeXeX>X>X>X>X>X>X>X>X>X>X>X>X>X=X=X=X=X=X=X4X9X5XOX/.4 * @ * > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l d w u 3 $.i.c.c.x.w.w.w.w.w.w.w.w.q.q.q.q.q.q.q.0.0.0.0.0.8.0.8.8.8.8.8.8.8.f.7.0 r j S S S S S S S S S S S S S S S S S S S S S S S S S H S ", +" : H S S S S S S S S S S S S S S S S S S S S S S S S S * % ).lXlXeXeXeXeXeXeXeXeXeXeXeXeX>X>X>X>X>X>X>X>X>X>X>X>X=X=X=X=X9X0X-X`.5 ; @ * l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S V d r d 6 >.e.c.c.x.w.w.w.w.w.q.w.q.q.q.q.q.q.0.0.0.0.0.8.8.8.8.8.8.8.8.8.7.g.>.p g F S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" - R S S S S S S S S S S S S S S S S S S S S S S S S S % = |.zXgXeXeXeXeXeXeXeXeXeXeXeXeXeX>X>X>X>X>X>X>X>X>X>X>X>X=X=XyXyX+X=.3 O % > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l u r 3 %.i.c.c.w.w.w.w.q.w.q.q.q.q.q.q.q.0.0.0.0.8.8.8.8.8.8.8.8.8.8.6.g.2.k p S S S S S S S S S S S S S S S S S S S S S S S S S R > ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S O < vXzXgXfXfXfXeXeXeXeXeXeXeXeXeXeXeX>X>X>X>X>X>X>X>X>X>X=X>XyXyX.X' = O = S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j r d 8 t.c.c.w.w.w.w.q.q.q.q.q.q.q.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.8.g.r.3 e S S S S S S S S S S S S S S S S S S S S S S S S S R # ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S O K jXzXfXfXfXfXfXeXeXeXeXeXeXeXeXeXeXeX>X>X>X>X>X>X>X>X>X>XyXyX.X' & o > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l w i 8 t.c.c.w.w.q.q.q.q.q.q.q.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.8.8.f.9.5 e S S S S S S S S S S S S S S S S S S S S S S S S S R ", +" R S S S S S S S S S S S S S S S S S S S S S S S S : O / kXzXfXfXfXfXfXfXeXeXeXeXeXeXeXeXeXeX>X>X>X>X>X>X>X>X>XyXuX+X` & @ l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l e u 8 3.c.x.w.q.q.q.q.q.q.0.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.6.4.f.7.7 t l S S S S S S S S S S S S S S S S S S S S S S S H R ", +" S H S S S S S S S S S S S S S S S S S S S S S S S > . ' zXzXfXfXfXfXfXfXfXeXeXeXeXeXeXeXeXeXeX>X>X>X>X>X>X>XwXuX;X[ * @ > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l w i 0 3.z.x.q.q.q.q.q.0.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.6.6.4.7.f.0 e l S S S S S S S S S S S S S S S S S S S S S S S H H ", +" - H S S S S S S S S S S S S S S S S S S S S S S S > . [ zXzXfXfXfXfXfXfXfXfXfXfXeXeXeXeXeXeXeXeXeXeX>XeX>XeXpXwX(.; O > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l r j #.x.z.q.q.q.0.0.0.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.8.4.4.7.f.%.t l S S S S S S S S S S S S S S S S S S S S S S S R > ", +" R S S S S S S S S S S S S S S S S S S S S S S S > . /.zXxXfXfXfXfXfXfXfXfXfXfXfXeXeXeXeXeXeXeXeXeXeX>X>XiXpXXX3 . = S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j r 3 ;.z.h.q.0.0.0.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.8.6.4.4.7.f.%.t l S S S S S S S S S S S S S S S S S S S S S S S R ", +" R S S S S S S S S S S S S S S S S S S S S S S S l . /.zXxXfXgXgXgXgXfXfXfXfXfXfXfXeXeXeXeXeXeXeXeXeX>XeXpXtX] @ % S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S p u 9 e.z.q.q.0.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.8.6.6.6.4.7.f.%.t l S S S S S S S S S S S S S S S S S S S S S S S R ", +" S H S S S S S S S S S S S S S S S S S S S S S S l . [ zXxXfXgXgXgXgXfXfXfXfXfXfXfXfXeXeXeXeXeXeXeXeX>XiXpX|.3 . > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l r l ;.z.h.0.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.8.8.6.6.4.4.7.f.%.e l S S S S S S S S S S S S S S S S S S S S S S H H ", +" - R S S S S S S S S S S S S S S S S S S S S S S V O ' zXxXfXgXgXgXgXgXgXfXfXfXfXfXfXfXeXeXeXeXeXeXeXeXpXrX] o & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S d r 9 q.z.0.0.0.0.0.0.8.8.8.8.8.8.8.8.8.8.8.6.6.4.4.4.7.f.@.t S S S S S S S S S S S S S S S S S S S S S S S R > ", +" R S S S S S S S S S S S S S S S S S S S S S S S . ` zXzXfXfXgXgXgXgXgXgXfXfXfXfXfXfXfXeXeXeXeXeXeXrXlX@XF . l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l r 3 3.z.q.0.0.0.0.0.8.8.8.8.8.8.8.8.6.6.6.6.6.4.4.4.7.7.7 e S S S S S S S S S S S S S S S S S S S S S S S R ", +" H H S S S S S S S S S S S S S S S S S S S S S S O P xXzXfXfXfXgXgXgXfXfXgXfXfXfXfXfXfXfXeXeXeXeX>XiXlX'.& % S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S u u >.z.h.0.0.0.8.8.8.8.8.8.8.8.8.8.6.6.6.6.4.4.4.4.7.7.5 e S S S S S S S S S S S S S S S S S S S S S S H H ", +" > H S S S S S S S S S S S S S S S S S S S S S S @ < jXzXfXfXfXfXgXgXgXgXfXfXfXfXfXfXfXfXfXeXeXeXeXlXiX[ . ; S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j r 8 h.z.0.0.8.8.8.8.8.8.8.8.8.8.8.6.6.6.4.4.4.4.4.7.6.3 p S S S S S S S S S S S S S S S S S S S S S S R : ", +" R S S S S S S S S S S S S S S S S S S S S S S & * :XzXfXfXfXfXfXfXgXgXgXfXfXfXfXfXfXfXfXfXeXeXeXlX:X4 . : S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l r 4 r.j.0.8.0.8.8.8.8.8.8.8.8.8.6.8.6.4.4.6.4.4.4.f.d.l f S S S S S S S S S S S S S S S S S S S S S S R ", +" H H S S S S S S S S S S S S S S S S S S S S S ; @ XXzXfXfXfXfXfXfXfXgXgXgXfXfXfXfXfXfXfXfXfXeXrXlX@X> O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S e k 3.j.0.8.8.8.8.8.8.8.8.8.8.6.8.6.4.4.6.4.4.4.4.f.2.p j S S S S S S S S S S S S S S S S S S S S S H H ", +" - R S S S S S S S S S S S S S S S S S S S S S : . '.zXfXfXfXfXgXgXgXgXfXfXgXgXfXfXfXfXfXfXfXfXgXlX|.& % S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S u u ;.j.9.8.8.8.8.8.8.8.8.8.6.8.6.6.4.4.4.4.4.4.4.f.;.e l S S S S S S S S S S S S S S S S S S S S S R - ", +" H S S S S S S S S S S S S S S S S S S S S S S . ] zXhXfXfXfXfXfXgXgXgXgXgXgXgXfXfXfXfXgXfXfXkXlX{.. * S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S d r >.j.9.8.8.8.8.8.8.8.8.6.8.6.6.4.6.4.4.4.4.4.6.f.@.t S S S S S S S S S S S S S S S S S S S S S S R ", +" : H S S S S S S S S S S S S S S S S S S S S S @ K kXkXfXfXfXfXfXfXgXgXfXgXgXgXgXgXfXgXfXfXfXkXlX_.O ; S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S k r #.j.h.8.8.8.8.8.8.6.6.8.6.6.4.6.4.4.4.4.4.4.7.7.5 e S S S S S S S S S S S S S S S S S S S S S H S ", +" R S S S S S S S S S S S S S S S S S S S S S * * :XzXfXfXfXfXfXfXfXgXgXfXfXgXgXgXgXgXgXfXfXkXzX_.. > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l r %.g.f.8.8.8.8.8.8.6.8.6.6.6.6.6.4.4.4.4.4.4.7.5.k g S S S S S S S S S S S S S S S S S S S S S R ", +" H S S S S S S S S S S S S S S S S S S S S S > @ }.zXfXfXfXfXfXfXfXfXgXfXfXfXgXgXgXgXgXfXfXkXzX_.. > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l t @.g.f.8.8.8.8.8.8.8.8.8.6.6.6.4.4.4.4.4.4.4.f.2.e l S S S S S S S S S S S S S S S S S S S S S H ", +" # R S S S S S S S S S S S S S S S S S S S S S . *.zXgXfXfXfXfXfXfXfXfXfXfXgXfXgXgXgXgXgXfXkXzX_.. > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l r %.j.7.8.8.8.8.8.8.8.8.6.6.6.6.4.4.4.4.4.4.4.f.%.t S S S S S S S S S S S S S S S S S S S S S R - ", +" H S S S S S S S S S S S S S S S S S S S S S % F hXxXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXfXfXkXzX_.. > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l e #.g.7.8.8.8.8.8.8.6.6.6.6.6.6.4.4.4.4.4.4.7.7.4 p S S S S S S S S S S S S S S S S S S S S S H ", +" > H S S S S S S S S S S S S S S S S S S S S > @ oXzXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXfXgXzX'.. > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l t &.j.7.8.8.8.8.8.6.6.6.6.6.6.6.4.4.4.4.4.4.f.d.f k S S S S S S S S S S S S S S S S S S S S H > ", +" R S S S S S S S S S S S S S S S S S S S S S . _.zXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXgXzX|.. > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S k e ;.j.8.8.8.8.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.f.&.e S S S S S S S S S S S S S S S S S S S S S R ", +" - H S S S S S S S S S S S S S S S S S S S S % F kXkXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXfXzX@X% = S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j p 2.g.8.8.8.8.6.6.6.6.6.6.6.6.6.6.4.4.4.4.7.7.5 p S S S S S S S S S S S S S S S S S S S S H > ", +" H S S S S S S S S S S S S S S S S S S S S > @ oXzXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXfXzXjX> & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S V S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S S S S S S S S S S S S S S S S S S g j r.g.8.8.6.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.f.d.f l S S S S S S S S S S S S S S S S S S S S R ", +" - H S S S S S S S S S S S S S S S S S S S S . *.zXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXfXzXxXK o S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S k d u u j i d D S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H S S H H H H H H H H H H H H S S H H H H S S S S S S S S e D 9.f.8.8.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.f.%.e S S S S S S S S S S S S S S S S S S S S H : ", +" H S S S S S S S S S S S S S S S S S S S S * > fXkXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXfXxXzX' . 3 S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l u w u l / $.,.u.1.I u S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H S S S S S S S S S S S S S H H H H H S S H H H H S S S S S S S S S e 7 g.7.8.6.6.6.6.6.6.6.6.4.4.4.4.4.4.4.4.7.6.3 g S S S S S S S S S S S S S S S S S S S S R ", +" > H S S S S S S S S S S S S S S S S S S S : O '.zXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXgXzX'.. : S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S d l ' :.1.P.K.E.T.Y.Q.G.F j S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H S S S H S S S S H H S S S S S S S S H H S H S S H H H H S S S H S S S S S S t &.j.7.6.6.6.6.6.6.6.6.4.4.4.4.4.4.4.4.4.f.2.e S S S S S S S S S S S S S S S S S S S S H - ", +" H S S S S S S S S S S S S S S S S S S S S @ I kXgXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXfXfXzX@X@ ; S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S d F m.E.E.E.Y.J.J.J.J.S.Q.<.w S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H S S S S S S H H S S S S H H H S H H H H S S S S S S S S H S S S S S H H S S S S S S k e 2.g.8.6.6.6.6.6.6.6.6.4.6.4.4.4.4.4.4.6.7.6 e S S S S S S S S S S S S S S S S S S S S H ", +" - R S S S S S S S S S S S S S S S S S S S : % oXzXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXfXzXhXS % S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l d u.^.J.J.J.J.J.J.J.J.J.Y.u.d S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H H S S S S S S H H H S S S H H S S H H H H S S S S S S S S S S S S S S H H S S S S S S S f N 9.f.8.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.4.f.2.g l S S S S S S S S S S S S S S S S S S S H - ", +" H S S S S S S S S S S S S S S S S S S S S O ' zXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXhXzX] . S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j F J.R.J.J.J.J.J.J.J.J.J.H.F.S k S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H S S S H H S S S S S S H H H J S S H H H S S S H J S S S S S S S S S S S H H S H H D S S S S S e 7 g.7.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.4.f.@.e S S S S S S S S S S S S S S S S S S S S H ", +" # R S S S S S S S S S S S S S S S S S S S > * ;XlXdXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXzX|.. l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l l u.R.J.J.J.J.J.J.J.J.J.J.H.6 i S S S S S S S S S S S S S S S S S S S S S S H S H S S H H S S S S H H S S S S H H H H H H S S H H S S S S H H S S S S S S S S S S S S H D H H H S S S S S V t ;.j.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.4.7.d.k k S S S S S S S S S S S S S S S S S S S R # ", +" S S S S S S S S S S S S S S S S S S S S S O *.lXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXzXdX> * S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S u [.E.J.J.J.J.J.J.J.J.J.J.T.$.w S S S S S S S S S S S S S S S S S S S S S H H H H S S S S S S S S H H H S J H H H S H S S S S H H S S S S H H S S S S H H H H S S S S S S J J S S S S S S S h k r.f.8.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.f.%.t S S S S S S S S S S S S S S S S S S S S S ", +" R S S S S S S S S S S S S S S S S S S S > ; ;XkXdXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXzX' . S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S w =.E.J.J.J.J.J.J.J.J.J.J.T.,.u S S S S S S S S S S S S S S S S S S S S S S H H H S S S S S S S S H H S S S H H H S S S S S S H H S S S S H H H S S S H H H H S S S S S H J J S D D S S S S D e 7 g.7.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.7.d.k k S S S S S S S S S S S S S S S S S S S R ", +" > H S S S S S S S S S S S S S S S S S S S O /.lXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXzXXXO : S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S i / R.K.J.J.J.J.J.J.J.J.J.R.s.j l S S S S S S l d u u u u u u j l S S S S S S S S H J J S S S S S H H H S S S H H H H H H S S S H H H S S S S S S S S S S S H H D S S S J J S S S S S S S S S S e 2.j.6.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.f.%.e S S S S S S S S S S S S S S S S S S S H > ", +" H S S S S S S S S S S S S S S S S S S S > = ;XkXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXkXhXS & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S k S $XR.J.J.J.J.J.J.J.J.J.K.H.4 q S l j u u d S I 9 $.:.:.$.9 P l u d S S S S S S H J J S S S S S H H H S S S S S H H S S S S S J H H S S S S S H S S S S S S H H S S S S S S S S S S S S S S D g S 7.f.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.7.d.k k S S S S S S S S S S S S S S S S S S S H ", +" # R S S S S S S S S S S S S S S S S S S S @ ] lXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXzX_.. S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l d OXE.J.J.J.J.J.J.J.J.J.J.R.u.3 w d F 8 :.y.P.G.H.I.I.I.I.Z.C.u.:.6 d p S D S S S S S S S H S S H S S S S S S S S S S S S S S S H H H H S H H H H H H H H H S S S S S S D S S S S S S S S S S D t %.j.6.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.4.f.%.e S S S S S S S S S S S S S S S S S S S H - ", +" S H S S S S S S S S S S S S S S S S S S l * ;XlXdXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXzX:X* ; S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S w (.~.J.J.J.J.J.J.J.J.J.J.J.Y.G.,.y.F.Y.T.Y.H.G.G.G.S.S.S.C.C.I.I.Z.y.8 a g D S S S S S H H S S H S S S S S S S S S S H S S S S H H H H S H H H H H H H H J S S H H H H H H S S S S S S S S S S z g r.g.6.6.7.6.6.6.6.6.6.4.4.4.4.4.4.4.7.d.j l S S S S S S S S S S S S S S S S S S S S ", +" H S S S S S S S S S S S S S S S S S S S @ ` kXfXdX,XfXfXfXfXfXfXfXfXfXfXfXfXfXgXzX' O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S @ 9 3XJ.J.J.J.J.J.J.J.J.J.J.J.K.T.Y.H.G.G.S.S.G.S.S.S.S.S.S.B.B.B.C.I.I.y.K r V D S S S H H H H S S S S S S S S S H H H H H H D S S S H H H H H H H H H H S S S S S S S H H S S S S H H S S S S S e 7 g.7.6.6.6.6.6.6.6.6.6.4.4.4.4.4.4.4.7.0 e S S S S S S S S S S S S S S S S S S S R ", +" - H S S S S S S S S S S S S S S S S S S V % XXlX,X,X,XfXfXfXfXfXfXfXfXfXfXfXfXfXzX@X% > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j + ,.3XJ.J.J.J.J.J.J.J.J.J.J.J.J.J.G.G.G.G.G.S.G.G.S.S.S.S.S.B.B.B.B.B.C.I.Z.$.p k D S S S H S S S S S H H S S S H H H H H H H H S S S H H H H H H H S H H H S S S S S S H H H J S S H S S S S S S V p 2.g.6.7.6.7.6.6.6.6.6.4.6.6.4.4.4.4.f.2.g V S S S S S S S S S S S S S S S S S S H - ", +" S S S S S S S S S S S S S S S S S S S S & I gXeXeX,X,XfXfXfXfXfXfXfXfXfXfXfXfXgXzX` @ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S V j i @ % * =.2X&XJ.J.J.J.J.J.J.J.J.J.J.J.J.J.G.G.G.G.G.G.G.G.G.G.G.S.S.B.B.B.B.B.B.N.A.I.,.a j D S S S S S S S H S H H S S H H H H H H H H S S H H H H H H S S S S H H H H H H S S H H H H S D H H H S S S S S p 6 f.7.7.7.7.7.6.6.6.6.6.6.6.4.4.4.4.6.7.6 f S S S S S S S S S S S S S S S S S S S S ", +" R S S S S S S S S S S S S S S S S S S S O '.lX,XfX,X,XfXfXfXfXfXfXfXfXfXfXfXfXzXXX@ l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l j u w i S / =.].+X3X&X%X%X%XJ.%XJ.J.J.J.J.J.J.J.J.J.G.G.G.G.G.G.G.G.G.S.S.S.S.B.B.B.B.B.B.B.B.C.I.y.u j D S S S H H H H S H H S S H H H H H H H J S S H H H H H S S S S S H H H H H H H H H H H H H H H H H H S S S S S e 2.g.6.7.7.6.6.6.6.6.6.6.4.4.6.4.4.4.f.;.e S S S S S S S S S S S S S S S S S S S R ", +" - H S S S S S S S S S S S S S S S S S S > +. ; S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S @ =.uXwX>X=X=X=X=X=X=X=X=X=X=X=X=X=X=X&X&X&X&X&X%X%X%X%X%X%X%X%XJ.J.J.J.J.J.J.J.J.J.J.J.J.J.G.G.G.G.G.G.G.G.G.G.G.G.G.B.B.B.B.B.B.B.B.C.I.8 p D S S H H S S H H S H S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S S l l r.f.7.7.7.7.6.6.6.6.6.6.6.6.6.6.4.7.d.d l S S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S * I eXeX,X,X,X,X,X,XfXfXfXfXfXfXfXfXfXzX_.O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S i P wXyX>X>X>X=X=X=X=X=X=X=X=X=X=X=X=X=X&X&X&X=X&X%X%X%X%X%X%X%XK.J.J.J.K.J.J.J.J.J.J.J.J.J.J.G.G.G.G.G.G.G.G.G.G.G.G.C.G.C.B.B.C.B.B.B.B.I.i.a S S S S H H H H H S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S H D S D t #.g.7.7.7.7.7.6.6.6.6.6.6.6.4.6.4.6.7.6 f S S S S S S S S S S S S S S S S S S H ", +" - H S S S S S S S S S S S S S S S S S S @ _.lX,X,X,X,X,X,X,XfXfXfXfXfXfXfXfXgXhXI & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S % [.uX>X>X>X>X>X=X=X=X=X=X=X=X=X=X=X=X=X=X=X&X=X&X=X=X%X%X%X%X%X%X%XK.K.K.K.J.J.J.J.J.J.J.J.J.G.G.G.G.G.G.G.G.G.G.G.G.C.G.C.C.C.C.C.C.C.B.C.Z.6 g S S S S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S S S D h 4 7.7.7.7.7.7.7.6.6.6.6.6.6.4.4.6.4.f.&.e S S S S S S S S S S S S S S S S S S H > ", +" S S S S S S S S S S S S S S S S S S S l * oXkX,X,X,X,X,X,X,X,X,XfXfXfXfXfXfXzXoX& l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l S ;XyX>XqX>X>X>X=X=X=X=X=X=X=X=X=X=X=X=X=X&X&X&X&X&X&X&X%X%X%X%X%X%X%X%XJ.J.J.J.J.J.J.J.J.J.J.G.G.G.G.G.G.G.G.G.G.G.G.G.C.C.C.C.B.B.B.B.B.B.I.<.p S S S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S S S S S g 2.g.7.7.7.6.6.6.6.6.6.6.6.6.4.6.4.7.d.d l S S S S S S S S S S S S S S S S S S S ", +" H S S S S S S S S S S S S S S S S S S = K ,XeX,X,X,X,X,X,X,X,X,XfXfXfXfXfXfXzX*.O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S = I qXwXqXqXqX>X>X=X>X=X=X=X=X=X=X=X=X=X=X=X&X&X&X&X&X&X&X&X%X%X%X%X%X%X%XJ.K.J.J.K.J.J.J.J.J.J.G.J.G.G.G.G.G.G.G.G.G.G.G.C.C.C.C.C.B.B.C.B.B.Z.N.S l S S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S H S D p 0 g.7.7.7.7.6.6.6.6.6.6.6.6.4.4.4.6.7.5 g S S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S @ *.lX,X,X,XeX,X,X,XfXfXfXfXfXfXfXfXhXdXS = S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S i I eXwXqXqXqX>X>X=X>X=X=X=X=X=X=X=X=X=X=X=X=X=X&X&X&X&X&X&X%X%X%X%X%X%XK.K.K.K.K.K.K.J.J.J.J.J.J.J.G.G.G.G.G.G.G.G.G.G.G.G.C.C.C.C.C.C.C.C.C.C.I.8 u S D H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S z S 8.f.7.7.7.6.6.6.6.6.6.6.6.6.4.4.4.f.#.e S S S S S S S S S S S S S S S S S S H $ ", +" - H S S S S S S S S S S S S S S S S S S % }.lX,X,X,XeX,X,X,XfXfXfXfXfXfXfXfXlXXX% : S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j S :XwXqXqXqX>XqXqX>X>X=X=X=X=X=X=X=X=X=X=X=X=X&X&X&X&X&X&X=X%X%X%X%X%X%XK.K.K.K.K.J.K.Y.E.E.E.Y.J.G.G.G.G.G.G.G.G.G.G.G.G.C.C.C.C.C.C.C.C.C.C.I.:.p S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S S f 2.j.7.7.7.7.6.6.6.6.6.6.6.6.4.4.4.f.2.g S S S S S S S S S S S S S S S S S S H - ", +" S S S S S S S S S S S S S S S S S S S > > ;XgX,X,X,X,X,X,X,XfXfXfXfXfXfXfXfXlX] @ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l * .XyXqXqXqXqX>X>X4X4X4X4X=X=X=X=X=X=X=X=X=X=X=X&X&X&X&X%X=X%X%X%X%X%XK.K.K.K.R.~.~.E.J.u.y.u.J.Q.Y.G.G.G.G.G.G.G.G.G.G.G.G.G.C.C.C.C.C.C.C.C.I.i.d S D H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S S p @.g.7.7.7.7.7.6.6.6.6.6.6.6.6.6.6.7.5.3 k S S S S S S S S S S S S S S S S S S S ", +" H S S S S S S S S S S S S S S S S S S & / gXfX,X,X,X,X,X,X,X,XfXfXfXfXfXfXhXdXS ; S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S @ `.uXqXqXqXqX>X4XqX=X4X4X=X=X=X=X=X=X=X=X=X=X=X&X&X=X&X&X%X%X%X%X&X2X~.~.~.R.P.y.:.' K j i j F :.L.Q.G.G.G.G.G.G.G.G.G.G.G.G.G.C.C.C.C.C.C.C.I.F.S l D H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H S z S 8.f.7.7.7.7.7.6.6.6.6.6.6.6.6.6.6.7.7 f S S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S @ _.lX,X,X,X,X,X,X,X,X,X,X,XfXfXfXfXlXXX& l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S % [ yXqXqX>XqXqXqXqX=X=X=X4X=X=X=X=X=X=X=X=X=X=X=X=X=X&X&X2X5X7X7X3X%Xu.,.=./ S i y u j l S l d q l u.Q.G.G.G.G.G.G.G.G.G.G.G.G.C.C.C.C.C.C.C.Z.C.I z D H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H J S g 2.j.7.7.7.7.7.6.6.6.6.6.6.6.6.6.4.f.#.e S S S S S S S S S S S S S S S S S S H ", +" $ H S S S S S S S S S S S S S S S S S l & XXlX,X,X,X,X,X,X,X,X,X,X,X,XfXfXfXlX/.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S = P qXwXqX>XqXqXqX4X4X=X=X4X=X=X=X=X=X=X=X=X=X=X=X=X&X=X7X5X$X[.(.9 K j u y i k S S S S S S S S S u y u.Q.G.G.G.G.G.G.G.G.G.G.G.G.C.C.C.C.C.C.Z.Z./ m D H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H D p %.g.7.7.7.7.7.7.6.6.6.6.6.6.6.6.4.f.2.g V S S S S S S S S S S S S S S S S S H $ ", +" - H S S S S S S S S S S S S S S S S S l > ;XrX,X,X,X,X,X,X,X,X,XfXfXfXfXeXgXgXI * S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l > +XyXqXqXqXqXqX>X=X=X=X4X4X4X4X=X=X=X=X=X=X=X=X=X5X9X[./ l u w u j l S S S S S S S S S S S S S D u S L.Y.G.G.G.G.G.G.G.G.G.G.C.C.C.C.C.C.C.Z.Z./ m H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H J H z 5 7.7.7.7.7.7.7.6.6.6.6.6.6.6.6.4.7.5.S k S S S S S S S S S S S S S S S S S H > ", +" S S S S S S S S S S S S S S S S S S S * I eXeX,X,X,X,X,X,X,X,X,X,XfXfXfXfXkX@X> > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S % [.pXeXqXqXqXqX>X=X>X>X4X4X=X=X=X=X=X=X=X=X=X=X5X5X[ o @ k S S S S S S S S S S S S S H S S S S S S w :.Q.J.G.G.G.G.G.G.G.G.C.C.C.C.C.C.C.C.Z.Z./ m H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H D H G z 3.g.7.7.7.7.7.7.6.6.6.6.6.6.6.4.6.7.6 g S S S S S S S S S S S S S S S S S S S ", +" S S S S S S S S S S S S S S S S S S S @ ] kX,X,X,X,X,X,X,X,X,X,X,XfXfXfXfXlX}.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S @ /.pXeXqXqXqXqXqX>X>X>XqX4X=X=X=X=X=X=X=X=X=X4X0X=.+ l S S S S S S S S S S S S S S H H S S S S S S d K K.H.G.G.G.G.G.G.G.G.H.G.G.C.C.C.Z.Z.Z.Z./ m H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H J f ;.g.7.7.7.7.7.7.7.6.6.6.6.6.6.6.6.f.@.e S S S S S S S S S S S S S S S S S S S ", +" H S S S S S S S S S S S S S S S S S S @ ).lX,X,X,X,X,X,X,X,X,X,X,XfXfXfXfXlX/.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S & ` iXqXqXqXqXqXqX>XqX>X>X4X4X=X=X=X=X=X=X=X=X0XOX@ l S S S S S S S S S S S S S S S S J S S S S S S S d u.T.H.G.G.G.G.G.H.H.H.H.H.H.H.Z.Z.Z.I.Z./ z H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H J J f %.g.7.7.7.7.7.7.7.6.6.6.6.6.6.6.4.f.;.p S S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S l * XXkX,X,X,X,X,X,X,X,X,X,X,XfXfXfXfXgXI & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j F : ;XgX,X,X,X,X,X,X,X,X,X,X,XfXfXfXkX;X: > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l * .XpXeXeXqXqXqXqXqX4XqXqX=X=X=X=X4X4X=X=X5X$XV k S S S S S S S S S S S S S S H H H H H H H S S S S u $.Q.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.Z.I.u.x G H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H C l r.f.7.7.7.7.7.7.7.7.6.6.6.6.6.6.7.5.S k S S S S S S S S S S S S S S S S S H $ ", +" - H S S S S S S S S S S S S S S S S S = P ,XeX,X,X,X,X,X,X,X,X,X,XeXeXfXfXlXXX& l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S @ `.pXeXeXeXeXqXqXqX4XqXqXqX4X=X4X4X4X=X=X9XOXj l S S S S S S S S H H H H S S H H H H H H H S S S S u [ R.H.K.H.H.H.H.H.H.H.H.H.H.H.H.H.H.U.,.a H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H J g 2.g.7.7.7.7.7.7.7.7.7.6.6.6.6.6.7.6.5 g S S S S S S S S S S S S S S S S S H - ", +" : H S S S S S S S S S S S S S S S S S & ` gXeX,X,X,X,X,X,X,X,X,X,XeXeXfXfXlX`.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S % ] pXqXeXeXqXqXqXqXqXqXqXqX4X=X4X4X=X=X=X9X$Xl l S S S S S S S H H H H H S S S H H H H H H S S S S u [ E.K.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.U.$.p H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H J p &.j.7.7.7.7.7.7.7.7.7.6.6.6.6.6.6.7.0 f S S S S S S S S S S S S S S S S S S > ", +" S S S S S S S S S S S S S S S S S S S @ *.kX,X,X,X,X,X,X,X,X,X,XeXeX,XfXfXlX*.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S = I wXwXqXeXeXqXqXqXqXqXqXqX4X4X=X=X=X4X4X5X=XK j S S S S H H S S S J S D D H J S S S H H H D D S S w =.~.K.H.K.K.K.H.H.H.H.H.H.H.H.H.H.I.H.P x H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H f @.g.7.7.7.7.7.7.7.7.6.6.6.6.6.6.6.f.%.e S S S S S S S S S S S S S S S S S S S ", +" S S S S S S S S S S S S S S S S S S S @ _.lX,X,X,X,X,X,X,X,X,X,XfX,XeXfXfXgX` & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l l ;XiXqXeXeXeXqXqXqXqXqXqXqXqX=X=X4X4X4X4X9X' u S S S S H H S S J J H H D H S S S S H H S S S S S u y.~.K.K.K.H.K.K.H.H.Y.H.H.H.H.H.H.U.y.a H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H h 6 f.f.7.7.7.7.7.7.7.6.6.6.6.6.6.6.f.&.p S S S S S S S S S S S S S S S S S S S ", +" H S S S S S S S S S S S S S S S S S l % {.lX,X,X,X,X,X,X,X,X,X,XeXfXfXfXgX,XF ; S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S & }.pXqXqXqXeXqXeXqXqXqXqXqXqXqX4X4X4X=X4X0X(.y S S S S H H S H H H H H H H H H J S J S S S S D u K Y.E.K.K.K.K.K.Y.Y.H.Y.Y.Y.Y.Y.H.Y.U.[ p H H H H H H H H H H H H H R R R R R H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H z L 8.f.7.7.7.7.7.7.7.7.6.6.6.6.6.6.f.2.f S S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S l ; oXkX,X,X,X,X,X,X,X,X,X,XeXfX,XfXkX@X> > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S @ /.pXqXqXqXeXeXeXqXqXqXqXqX4X4X4X4X4X=X4X0X.Xi S S S S H H H H H H H H H H H S S S S S S S D j w u.~.K.K.Y.K.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.H.U.m.m G H H H H H H H H H H H R R R R R R R R H H H H H H H H H R R R R H H H H H H R R R H H H H H H H H H H H H H H H H H H G n 3.g.7.7.7.7.7.7.7.7.6.6.6.6.6.6.7.2.k l S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S > > @XrX,X,X,X,X,X,X,X,X,X,X,X,XfXeXlXXX& l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S & ` iXwXqXeXeXqXqXqXqXqXqXqXqX4X4X4X4X=X=X5X-XS j S S S S S S H H D S S S S D H H H S S S S u y 1.^.R.K.R.R.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.Y.T.!.[ p H H H H H H H H H H R R R H H H H R H H H R H H H H H H H R R H H H H R R R R R R R R R R R R H H H H H H H H H H H H H J h 2.j.7.7.7.7.7.7.7.7.7.7.7.6.6.6.7.5.3 z S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S ; 3 ;XrX,X,X,X,X,X,X,X,X,X,X,XfX,XeXlX'.% V S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S j F >XiXqXqXqXqXqXqXqXqXqXqXqXqX4X4X4X4X4X4X0X' w D S S S D S S H J S S S S S D S S S k u w D u.^.R.K.Y.Y.Y.R.Y.Y.R.Y.R.Y.Y.Y.Y.Y.U.y.p J H H H H H H H H R H H R R R H H H R R H R R R R H H H H H R R H H H H R R R R R R R H R R R R R H H H H H H H H H H H H H f >.j.7.7.7.7.7.7.7.7.7.7.7.6.6.6.7.4.4 z S S S S S S S S S S S S S S S S S H # ", +"$ H S S S S S S S S S S S S S S S S S * I ,XeX,X,X,X,X,X,X,X,X,X,XfXeXeXeXlX_.% S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l = oXpXqXqXqXqXqXqXqXqXqXqXqXqX4X4X4X4X4X4X0X+Xu j S S S S S S J H S S S S l j u u d D ` `.R.^.R.R.R.R.R.R.R.R.R.R.R.R.R.R.Y.Y.!.L.N z H H H H H H H R R R H H H H R H H R H H R R R R R R H H H H R H H H H R R R H H R R H H H H H H R R H H H H H H H H H H H H f &.j.7.7.7.7.7.7.7.7.7.7.7.6.6.6.7.6.6 h S S S S S S S S S S S S S S S S S H $ ", +"- H S S S S S S S S S S S S S S S S S & / eX,X,X,X,X,X,X,X,X,X,X,XeXeXeXeXlX*.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S % `.pXqXqXqXqXqXqXqXqXqXqXqXqXqX4X4X4X4X4X4X0X`.+ d S S S S S S S k i u i l I [ `.OX1XE.8X3X&XR.R.R.R.R.R.R.R.R.R.R.R.R.R.Y.!.Q.I p H H H H H H R R R R R H H H H H H R R R H H R R R R R R H H H R R H R R R R R H R R R R H H H H H H R H H H H H H H H H H H H v %.j.7.7.7.7.7.7.7.7.7.7.7.6.6.6.7.7.7 f S S S S S S S S S S S S S S S S S H $ ", +"- H S S S S S S S S S S S S S S S S S & ' rX,X,X,X,X,X,X,X,X,X,X,X,XfXfXfXkX] % S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S % ] pXwXqXqXqXqXqXqXqXqXqXqXqXqXqX4X4XqX4X4X5X0X].j o u j d u y j D ` /.[.$X2X7X7X7X3XR.R.R.R.R.R.R.R.R.R.R.R.R.E.E.R.R.R.!.!.` r H H H H H H H R R R R R H H H H R R R R H H H H H H H H H R R R H H R R R H H H H H R R R R R H R R H H H H H H H H H H H H H H v 9 g.f.7.7.7.7.7.7.7.7.7.7.6.6.6.6.f.0 p S S S S S S S S S S S S S S S S S S - ", +"- H S S S S S S S S S S S S S S S S S % ] rX,X,X,X,X,X,X,X,X,X,X,X,XfXfXfXgX` & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S d I wXiXqXqXqXqXqXqXqXqXqXqXqXqXqXqX4XqX4X4X4X4X0X1X(.` F I ] `..X-X5X8X8X7X2X2X&X&X2X2X2XR.R.2XR.R.R.R.E.R.E.E.E.E.E.E.Q.m.N q S H H H H H H R R R R R R H H H H R R R R H H H H H H R R R R R R H R R R R H H H H H R R R R H H R R R R R H H H H H H H H H H H h 9 g.f.7.7.7.7.7.7.7.7.7.7.6.6.6.6.f.@.f S S S S S S S S S S S S S S S S S : S ", +"- H S S S S S S S S S S S S S S S S S % *.gX,X,X,X,X,X,X,X,X,X,X,X,XeXeXfXgX/ & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l l ;XpXeXqXqXqXqXqXqXqXqXqXqXqXqXqX4X4X4X4X4X4X=X9X0X5X-X4X9X0X7X5X2X2X2X&X&X&X&X2X2X2X2X2X2X2XR.2XR.E.E.E.E.E.E.E.E.E.Q.(.S P x a a C H H H H R H H R R R R R H R R R R R R H R R R R R R R R R H H R R R R H H H R R R R R H H H H R H R R H H H H H H H H H H h 6 f.f.7.7.7.7.7.7.7.7.7.7.7.6.7.6.f.#.f S S S S S S S S S S S S S S S S S S S ", +"- H S S S S S S S S S S S S S S S S S @ /.kX,X,X,X,X,X,X,X,X,X,X,X,XeXeXfXfXI * S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S & |.pXeXeXeXqXqXqXqXqXqXqXqXqXqXqX4X4X4X4X4X4X4X4X4X4X5X5X4X2X=X4X2X2X2X&X=X2X2X2X2X2X2X2X2X3X3X3X3XE.E.E.E.E.E.E.E.E.E.Q.~.W.m.1.=.H c z H R H H H H R R R R R R R R R R R R R R R R R R R R R R H R R R R R R R R R R R R R H H R R H R R R R H H H H H H H H x P 9.g.f.7.7.7.7.7.7.7.7.7.7.6.6.6.f.#.f S S S S S S S S S S S S S S S S S S S ", +"- H S S S S S S S S S S S S S S S S S @ /.kX,X,X,X,X,X,X,X,X,X,XeX,XeXeXgXX,X,X,X,X,X,X,X,X,X,X,X,X,XgX H S S S S S S S S S S S S S S S S S @ /.kX>X,X,X,X,X,X,X,X,X,X,X,X,X,XgXXeX,X,X,X,X,X,X,X,X,X,XeX,X,X,XlX_.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S % '.lXwXwXwXwXwXwXwXwXwXqXwXwXwXqXqXqXqXqX4X4X4X4X4X5XuX5X|._.] I l i a m l H H H H H H H H R H H H H R x m 1X8X7X7X7X7X^.^.~.^.~.~.~.~.~.~.~.~.Q.Q.Q.U.].v R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R R H H v >.b.g.g.g.f.f.f.f.7.7.7.7.7.7.7.7.7.6 g S S S S S S S S S S S S S S S S S H $ ", +" H S S S S S S S S S S S S S S S S S ; F : ;XrX,X,X,X,X,X,X,X,X,X,X,X,X,X,XlX}.& V S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S d I iXiXwXrXwXwXwXwXwXwXwXwXwXwXwXwXwXwXwX5X4XqX5XuX(.q i S H H H H H H H H H H R R R R R R R R R R R R R H a .X8X8X8X7X7X^.^.^.^.^.^.~.~.~.~.~.Q.Q.Q.!.!.' n R R R R R R R R R R R R R R R W W W W W W W W W R R R R R R R R R R R R R R R R R R R R R H b t.b.g.g.g.g.f.f.f.7.7.7.7.7.7.7.7.5.D z S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S > ; oXrX,X,X,X,X,X,X,X,X,X,X,X,X,X,XkX@X> > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l N :XpXwXrXwXrXwXwXwXwXwXwXqXwXqXwXwXwXwXwXqXwXuX[.q l D H H H H H H H H H R R R R R R R R R R R R R R R R g ' 8X8X7X8X7X^.^.^.^.^.^.^.~.~.!.!.!.!.!.Q.!.(.b R R R R R R W W W R R R W W W W W W W W W W W W W W W W W W W W W R R R R R R R R R R R R Y G 3.b.g.g.g.g.f.f.f.f.7.7.7.7.7.7.f.d.l l S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S V & }.iX,X,X,X,X,X,X,X,X,X,X,X,X,XeXgX.b.j.j.j.g.g.g.g.f.f.f.7.7.7.7.7.f.@.f S S S S S S S S S S S S S S S S S S : ", +" - H S S S S S S S S S S S S S S S S S * I ,XeX,X,X,X,X,X,X,X,X,X,X,X,X,X,XlX}.% S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S D J J J S S S S l j @XlXrXrXwXwXwXwXwXwXwXwXwXwXwXwXwXwXwXwXuX*.u H H H H H H H R R R R R R R R R R R R R R R R R R R R R R b |.8X8X8X8X8X8X8X^.^.^.^.^.^.^.^.^.^.^.^.!.!.*.n R R R W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W R R R R n 1.n.j.j.j.g.g.g.g.f.f.f.f.7.7.7.7.7.6 g S S S S S S S S S S S S S S S S S H - ", +" # H S S S S S S S S S S S S S S S S S > S ;XrX,X,X,X,X,X,X,X,X,X,X,X,X,XeXkX@X> l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S D D S J S S S S S % {.lXrXrXrXwXrXwXwXwXwXwXwXwXwXwXwXwXwXwXuX[.u J H H H H R H R R R R R R R R R R R R R R R R R R R R R Y B 1X8X8X8X8X8X8X8X^.^.^.^.^.^.^.^.^.^.^.^.^.!.*.n R R W W W W W W W W W W W W W W W W W W W W E W E W W W W W W W W W W W W W W W R W R T Y e.b.j.j.j.j.g.g.g.f.f.f.f.7.7.7.f.6.4 z S S S S S S S S S S S S S S S S S H # ", +" H S S S S S S S S S S S S S S S S S l = oXiX,X,X,X,X,X,X,X,X,X,X,X,X,XeXgXfXP = S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S D D J S D S S S S % *.lXrXrXrXrXrXrXrXwXwXwXwXwXwXwXwXwXwXwXuX;Xl S H H H H R H R R R R R R R R R R R R R R R R R R R R R b ` 8X8X8X8X8X8X8X8X8X8X8X^.^.^.^.^.^.^.^.^.^.^.] n R R W W W W W W W W W W W W W W W W E E E E E E E W W W W W W W W W W W W W W W W R R Y ( z.b.z.j.j.j.g.g.g.f.f.f.f.7.7.7.f.3.l l S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S @ '.kX,X,X,X,X,X,X,X,X,X,X,X,X,XeXeXlX] @ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S S S S D D S S S d I iXiXrXrXrXwXrXrXwXrXrXrXwXwXwXwXwXwXwXyXwXI k H H H H H R R R R R R R R R R R R R R R R R R R R R R c .X8X8X8X8X8X8X8X8X8X8X8X8X^.^.^.^.^.^.^.^.^.^.` Y R W W W W W W W W W W W W W W W E E E E E E E E E E W W W E E E W W W W W W W W W R R n $.b.k.l.j.j.j.g.g.g.g.f.f.f.7.7.7.g.;.g S S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S @ *.iX,X,X,X,X,X,X,X,X,X,X,X,X,XeXeXlX'.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H D D S S H D S S S l N :XlXrXrXrXrXrXrXwXrXrXrXwXwXwXwXwXwXwXyXuX] i H H H H H R R R R R R R R R R R R R R R R R R R R R s *.0X0X8X8X8X8X8X8X8X8X8X8X8X^.^.^.^.^.^.^.^.!.E.W Y R W W W W W W W W E W W W E E E E E E E E E E E E E W W W E E E E E E E W W W W W W R v <.n.l.k.j.j.j.j.g.g.g.f.f.f.7.7.7.g.#.p S S S S S S S S S S S S S S S S S S H ", +" S S S S S S S S S S S S S S S S S S S & / eX,X,X,X,X,X,X,X,X,X,X,X,X,XeX,XlXoX* : S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H D D J S J J S S S S & XXlXrXrXrXrXrXrXrXrXrXrXwXwXwXwXwXwXwXwXpX).u H H H H H R R R R R R R R R R R R R R R R R R R Y s ` 0X0X0X0X8X8X8X8X8X8X8X8X^.8X8X8X^.^.^.^.^.^.^.1XY R R W W W W W W E E E W E E E W E E E E E E E E E E E E E E E E E E E E E E E W W W R R Y 3.n.l.l.k.j.j.j.g.g.g.f.f.f.7.7.7.f.7 f S S S S S S S S S S S S S S S S S S S ", +" > H S S S S S S S S S S S S S S S S S > 3 ;XrX,X,X,X,X,X,X,X,X,X,X,X,X,XeXgXfXF = S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H D D J J J J S S S S % _.lXrXrXrXrXrXrXrXrXrXrXrXwXwXwXwXwXwXyXpXoXi H H H H H R R R R R R R R R R R R R R R R Y n c n /.0X0X0X0X0X8X8X8X8X8X8X8X8X8X8X8X8X8X^.^.^.^.^.!.].b R W W W W W W E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E W W W Y W l.b.l.k.k.j.j.j.g.g.g.f.f.f.7.7.f.6.4 z S S S S S S S S S S S S S S S S S H > ", +" - H S S S S S S S S S S S S S S S S S l * XXiX,X,X,X,X,X,X,X,X,X,X,X,XeXeXeXlX] @ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H S S S S S S S S S u ' lXiXrXrXrXrXrXrXrXrXrXrXwXwXwXwXwXwXyXpXyXI i H H H H R R R R R R R R R R R H G x b b Y ( /.#X0X0X0X0X0X8X8X8X8X8X8X8X8X8X8X8X8X8X8X^.^.^.^.^.^.} n R W W W W E E E W E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E W W W M =.n.b.l.l.l.l.j.j.j.g.g.f.f.f.7.7.g.3.z V S S S S S S S S S S S S S S S S S H - ", +" H S S S S S S S S S S S S S S S S S S @ `.lX,X,X,X,X,X,X,X,X,X,X,X,XeXeX,XlX}.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H D S S S S S S S S j F eXlXrXrXrXrXrXrXrXrXrXrXrXwXwXwXwXyXyXyXpX#Xy l H H H R H H R R R H Y n b m x R ' _.XXsXuX0X0X0X0X0X0X0X0X8X8X8X8X8X8X8X8X8X8X8X8X8X^.^.^.^.^.1XB T R W W W E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E E W W W M ,.n.b.b.l.l.l.j.j.j.g.g.f.f.f.7.7.g.;.p S S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S % ` rX,X,X,X,X,X,X,X,X,X,X,X,XeXeXeXkX;X> > S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S J S S S S S S S S S S S j @XlXrXrXrXrXrXrXrXrXrXrXrXrXwXwXwXyXyXyXyXpX{.y g H H H H H G x a m G ( -.{.#XtXuXuXuXuXuX0X0X0X0X0X0X0X0X0X0X8X0X8X8X8X8X8X8X8X8X8X8X^.^.^.^.(.b W W W W W E E E E E E E E E E E E E E E E E .E E E E . .E E E E E E E E E E E E W W W M i.n.b.b.l.l.l.j.j.j.g.g.f.f.f.7.7.g.@.f S S S S S S S S S S S S S S S S S S H ", +" S S S S S S S S S S S S S S S S S S S ; 3 H S S S S S S S S S S S S S S S S S l & XXiX,X,X,X,X>X,X,X,X,X,X,X,XeXeX,XlX{.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S S S H H H H H H S S S S % /.lXiXrXrXrXrXrXrXrXrXrXrXrXwXwXwXiXiXyXyXyXpXpXpX#X'._.{.#XtXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X8X8X8X8X8X8X8X8X8X8X^.^.*.n W W W W E E E E E E E E E E E E E E E E . . . .E E . . .E E E . . . .E E E E E E E E W M <.n.b.b.b.l.l.k.j.j.g.g.g.f.f.7.g.3.m V S S S S S S S S S S S S S S S S S H > ", +" # H S S S S S S S S S S S S S S S S S S @ (.lX,X,X,X,X,X,X,X,X,X,X,X,X,XeX,XkX S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S J H H H H H H S S S S i I kXlXrXrXrXrXrXrXrXrXrXrXrXwXyXrXiXiXiXyXyXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X8X8X8X8X8X8X8X8X8X8X^.+Xv R W W W E E E E E E E E E E E E E E E E E . . . . . . . . . . .E . . . . . .E E .E E E W Y ' n.b.b.b.l.l.k.j.j.g.g.g.f.f.7.j.;.t S S S S S S S S S S S S S S S S S S H - ", +" H S S S S S S S S S S S S S S S S S S & / eX,X,X,X,X,X,X,X,X,X,X,X,X,XeXeXfXkX` % S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S S S S H H H H S S S S S l l :XlXrXrXrXrXrXrXrXrXrXrXrXrXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X0X0X0X0X0X8X8X8X8X8X8X8X8X^.6XY Y W W W W E E E E E E E E E .E E .E E E E E . . . . . . . . . . . . . . . . . . . . . .E W W Y v.n.b.b.b.l.k.j.j.j.g.g.f.f.g.f.6 h S S S S S S S S S S S S S S S S S S H ", +" S S S S S S S S S S S S S S S S S S S l ; ;XrX,X,X,X,X,X,X,X,X,X,X,XeXeXeXfXlX}.@ S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S S S S S S J D S S S S S S i XXlXrXrXiXrXrXrXrXrXiXrXrXrXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X0X0X0X0X0X8X8X8X8X8X8X8X8X7XW n W W W W W E E E E E E E E E . . . .E .E . . . . . . . . . . . . . . . . . . . . . . .E E E E A [ n.n.b.b.l.k.k.j.j.g.g.f.f.k.;.t S S S S S S S S S S S S S S S S S S S S ", +" > H S S S S S S S S S S S S S S S S S S @ '.lX,X,X,X,X,X,X,X,X,X,X,XeXeX,XfXgX,XS ; S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S S S S S S H S S S S S S S @ ).lXrXrXiXrXrXiXrXrXiXrXrXrXrXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X8X8X8X8X8X8X} v W R W W W E E E E E E E . . . .E E . . . . . . . . . . . . . . . . . . . . . . . . . . .E E W W W M ,.n.n.l.l.k.k.j.j.g.g.f.k.3.x z S S S S S S S S S S S S S S S S S S H : ", +" $ R S S S S S S S S S S S S S S S S S S % ` rX,X,X,X,X,X,X,X,X,X,X,XeXeX,X,XfXlX-.O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S H S S S S H H S S S S S S u [ lXkXrXiXiXrXiXrXrXiXrXrXrXrXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X8X8X8X8X7X{ n W W W W W E E E E E E E . .E . .E E . . . . . . . . . . . . . . . . . . . . . . . . . . . .E E E E T M <.n.n.b.k.k.j.j.j.k.b.3.x h D S S S S S S S S S S S S S S S S S S H # ", +" H S S S S S S S S S S S S S S S S S S > ; ;XrX,X,X,X,X,X,X,X,X,X,X,XeX,X,X,XlX@X* l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H H S S H H H S S S S S j F tXlXiXrXrXrXiXiXrXiXrXrXrXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuX0XuX0XuXuX0X0X0X0X0X0X0X0X0X0X0X0X8X8X1XW b W R W W W W E E E E E E .E E . . .E E . . . . . . . . . . . . . . . . . . . . . . . . . . . .E E E W E T M [ i.b.n.n.n.b.k.r.#.b z D S S S S S S S S S S S S S S S S S S S H ", +" S S S S S S S S S S S S S S S S S S S S O `.lX,X,X,X,X,X,X,X,X,X,X,XeX,X,XfXfXkX` % S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H H S S H H H S S S S S S j @XlXkXgXrXrXrXrXiXrXrXrXrXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXpXpXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X0X0X8X8X XB n W W W W W W E E E E E E . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .E E W E W M B ' :.<.>.>.9 G v C F H S S S S S S S S S S S S S S S S S S S S ", +" - H S S S S S S S S S S S S S S S S S S % ` rX,X,X,X,X,X,X,X,X,X,X,XeXeXeXfX,XlX}.O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H H H S S S S S S S S S S & {.lXrXgXrXiXrXrXiXrXiXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X8X0X6X] b Y E W W E E W E E E E E E E E .E E . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .E E E E W W R A n v v v b G H H H H S S S S S S S S S S S S S S S S S S H - ", +" R S S S S S S S S S S S S S S S S S S > = oXkX,X,X,X,X,X,X,X,X,X,XeXeXeXeXfXgXgXP & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H H S S S S S S H S S S S y /.lXkXgXgXiXrXrXiXrXiXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X0X9X'.Y b R W W W E E W E E E E E E E E . .E . . . . . . . . . . . . . . . . .X.X.X.X.X. . . . . . . . . . . . . . . .E E E W W R R R R R R H H H H H H S S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S S O (.lX,X,X,X,X,X,X,X,X,X,XeXeXeXfXfX,XzX}.O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H S S S i ` lXkXgXkXkXkXiXiXrXiXiXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXuXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X0X0X XQ b Y W W W W W E W E E E E E E .E E E ) . . . . . . . . . . . . . . . . .X.X.X.X.X. . . . .X.X. . . . . . . . . . . .E E E W W R R R R R R H H H H H S S S S S S S S S S S S S S S S S S H ", +" - H S S S S S S S S S S S S S S S S S S * F eXeX,X,X,X,X,X,X,X,X,XeXeXeXfXfXfXgXfXF * S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H S S S l S :XlXkXkXkXkXiXiXiXiXiXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X0X0X0X9X XW b Y W W W W W W E E E E E E E E E .E .E . . . . . . . . . . . . . . . . . . . . .X.X. . . . . . . . . . . . . . . . . . .E E W W R R R R R R H H H H H S S S S S S S S S S S S S S S S S H > ", +" R S S S S S S S S S S S S S S S S S S l @ }.lX,X,X,X,X,X,X,X,X,XeXeXeXeXfXfX,XlX'.O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S J H H H H S S S H H S S S S i XXlXkXkXkXrXiXrXiXrXrXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuXuXuXuXuXuX0X0X0X#X_.W b Z R R W W W W W W E E E E E E E E . .E E . . . . . . . . . . . . . . . . . .X.X.X.X.X.X.X. .X.X. . . . . . . . . . . . . . .E E W W R R R R R H H H H H H S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S S @ ` iX,X>X,X,X,X,X,X,X,X,XeXeXeXfXfX,XgXgXF * S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H S S H H S S S S % ).zXkXkXkXrXiXrXiXiXrXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXuXpXuXuXuXuXuXuXuXuXuXuXyX#X'.*.( n b G R R R W W W W W E E E E E E E E E .E .E . . . . . . . . . . . . . . . . . . .X.X.X.X.X.X. . .X.X.X. . . . . . . . . . . . . .E E W W W W R R R R H H H H H S S S S S S S S S S S S S S S S S H ", +" - H S S S S S S S S S S S S S S S S S S l * oXkX,X,X,X,X,X,X,X,X,X,XeXeXfXfXfX,XlX}.O S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S H S S S S S S S S S S S S S S & ] lXkXkXkXkXiXiXiXiXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXuXuXuX{.H n a b G R R R R W W W W W W E E E E E E E E E E E . . . . . . . . .E ^ ! ! ^ ^ ^ ^ ^ ^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ) X. . . . . . . . . .E E W W W W R R R R H H H H D S S S S S S S S S S S S S S S S H > ", +" R S S S S S S S S S S S S S S S S S S S O *.lX>X,X,X,X,X,X,X,X,X,XeXeXfXfXeX,XgXkXP & S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H S S S S S S S S S H H S S S j I kXlXkXkXkXiXiXiXiXiXiXiXiXiXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXpXuXuXuXuXuXuXXXs n R R R R R R R W W W W W W E E E E E E E E E E E . . . . . . . . .Q Q nXnXnXnXnXnXnXnXbXbXbXbXbXbXbXbXbXbXbXbXbXbXbXbXbXo.~ X. . . . . . . . . .E W W W W R R R R H H H H S S S S S S S S S S S S S S S S S H ", +" H S S S S S S S S S S S S S S S S S S S ; > H S S S S S S S S S S S S S S S S S S S ; ; ;XkX,X,X,X,X,X,XeXeX,XfXfXfXfXfXfXfX,XlX;X& ; S S S S S S S S S S S S S S S S S S S S S S S S S H H S S S S S S S S S H H H H H S S S S J S S S S S S S S H H H H H H H H H H H H H R H R R R x ( pXpXpXpXpXpXpXpXpXpXuXuXyXW Y R W R W W W W W W W E E E E E E E E E .E . . . . . . . . . . . .) ^ ! ! ! ! ^ ^ ^ ^ ^ ^ 2 ^ VXJXJXJXJXJXJXJXJXJXJXJXnX^ X.X.X.X. . . . . .E E W R R R R H H H S S S S S S S S S S S S S S S S S H : ", +" R S S S S S S S S S S S S S S S S S S S S O ] lX,X,X,X,X,X,X,XeX,XfXfXfXfXfXfXfXfXfXzX'.O S S S S S S S S S S S S S S S S S S S S S S S S S H H S S S S S S S S S H H H H H S S S S H H J S S S J S H H H H H H H H H H H H R R H H R R H Y G :XpXpXpXpXpXpXpXpXpXuXpX:XB T R R W W W W W W W E E E E E E E E .E .E . . . . . . . . . . . . . . . . . . .X.X.X.X.X.X.! o.JXJXJXJXJXJXJXJXJXJXmX! X.X.X.X. . . . . . .E E W R R R H H H H S S S S S S S S S S S S S S S S S R ", +" S H S S S S S S S S S S S S S S S S S S S > & oXlX,X,X,X,X,X,XeX,X,XfXfXfXfXfXfXfXfXgXzX[ O S S S S S S S S S S S S S S S S S S S S S S S S H S J S H H H S S S S H S S H J J S D H H H S S H H H H H H H H H H H H H H H R R R H H H R H H a XXpXpXpXpXpXpXpXpXpXpXyX} n R R R W W W W W W W E E E E E E E E .E . . . . . . . . . . . . . . .X.X. .X.X.X.X.X.X.X.X.X.^ o.JXJXJXJXJXJXJXJXJXVX^ .X.X.X.X.X.X. . . . .E W W R R R H H H S S S S S S S S S S S S S S S S S H : ", +" R S S S S S S S S S S S S S S S S S S S S % I kXfX,X,X,X,X,XeX,X,XfXfXfXfXfXfXfXfXfXkXkXF @ S S S S S S S S S S S S S S S S S S S S S S S S S H H H H H S S S S H H S H J J J D H H H H H H H H H H H H H H H H H H H H R R R H H H R R R h ' pXpXpXpXpXpXpXsXXX'.` n R R R R W W W W W W W E E E E E E E E . . . . . . . . . . . . . . . . .X.X.X.X.X.X.X.X.X.X.X.) ^ VXJXJXJXJXJXJXJXJXLX) ) ..X.X.X.X.X.X. . . .E E W R R R R H H H S S S S S S S S S S S S S S S S S R ", +" : H S S S S S S S S S S S S S S S S S S S S O '.lX,X,X,X,X,XeXeXeXfXfXfXfXfXfXfXfXfXfXzX:X* * S S S S S S S S S S S S S S S S S S S S S S S H H S S S S S S H H H S S H H H H S D H H H H H H H H H H H H H H H H H H H R H R H H R H R R H m /.:XsXvX}._.` G m g n R R R R R W W W W W W W E W E E E E E . . . . . . . . . . . . . .X.X.X.X.X.X.X.X.............X.^ mXJXJXJXJXJXJXJXJXJXo.~ ..X...X.X.X.X. . . . .E W W R R R H H H S S S S S S S S S S S S S S S S S S S ", +" R S S S S S S S S S S S S S S S S S S S S = ; S S S S S S S S S S S S S S S S S S S S S S H H J S S S S S H H H S H H H H H S H H H H H H H H H H H H H H H H H H H H R R R H H R H H H R H x G H N a a x G R R R R R R R W W W W W W W W E E E E E E E . . . . . . . . . . . . . .X.X.X.X.X.X...X.X...........^ bXJXJXJXJXJXJXJXJXJXbX^ ........X.X.X.X. . . . .E W W R R R H H H S S S S S S S S S S S S S S S S S R ", +" S H S S S S S S S S S S S S S S S S S S S S O ' lXeX,X,X,X,XeXeXeXfXfXfXfXfXfXfXfXfXfXfXzX'.. l S S S S S S S S S S S S S S S S S S S S S H S S S S S S S S H S H H H H H H S S H H H H H H H H H H H H H H H H H H H H H H H H R R H H H H R H G H R R R R R R R R R R W W W W W W W W W E E E E E E E . .E E .E . . . . . . . .X.X.X.X.X.......X.........~ o.JXJXJXJXJXJXJXJXJXmX^ X.........X.X.X. . . . .E E W R R R H H H S S S S S S S S S S S S S S S S S S S ", +" R S S S S S S S S S S S S S S S S S S S S l O }.lX,X,X,X,XeXeXeXfXeXeXfXfXfXfXfXfXfXfXgXzX/.. S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S H H S S H H J S H H H H H H H H H H H H H H H H H H H H H H H H R R H H R R H R H R R R R R R R R R R R W W W W W W W W W E E E E E E E E .E E E . . . . . . . . .X.X.X.X.X.......X.......) X.KXJXJXJXJXJXJXJXJXVX^ .........X.X.X.X. . . . .E W W R R R H H H S S S S S S S S S S S S S S S S S R ", +" S H S S S S S S S S S S S S S S S S S S S S * > H S S S S S S S S S S S S S S S S S S S S l O '.lX,X,X,X,X,XeXfXfXfXfXfXfXfXfXfXfXfXfXfXzXlX/ O S S S S S S S S S S S S S S S S S S S S S H H H H S S S S S H H H H H S S S S H H H H H H H H H H H H H H H H H H H H R R R R R R R R R R R R R R R R R R R R R R W W W W W W W W W E E E E E E E E E E . . . . . . . . . .X. .X.X.X.X.X...X...X.^ mXJXJXJXJXJXJXJXJXJXbX^ ..............X.X.X. . . .E W W R R R H H J S S S S S S S S S S S S S S S S S H : ", +" H S S S S S S S S S S S S S S S S S S S S S = * ;XlX,X,X,X,XeXeXfXfXfXfXfXfXfXfXfXfXfXfXfXzXzX/ O S S S S S S S S S S S S S S S S S S S S J H S S S S S S H H H H H H S S S S H H H H H H H H H H H H H H H H H H H H H R R R R R R R R R R R R R R R R R R R R R W W W W W W W W W W W E E E E E E E E . . . . . . . . . . . .X.X.X.X.X.X.X.X.^ bXJXJXJXJXJXJXJXJXJXmX^ X.............X.X. . . . .E E W R R R H H H S S S S S S S S S S S S S S S S S S H ", +" # R S S S S S S S S S S S S S S S S S S S S S @ F gXgX,X,X,XeXeXfXfXfXfXfXfXfXfXfXfXfXfXfXfXzXzX/ O S S S S S S S S S S S S S S S S S S S H S S H S S S S S S H H S S S S H H H H H H H H H H H H H H H H H H H H H H H H R R H H R R R R R R R R R R R R R R R R R R R W W W W W W W W E E E E E E E . . . . . . . . . . . .X.X.X.X.X.X.X...^ o.JXJXJXJXJXJXJXJXJXAX~ { ..............X.X. . . . .E W W R R R H H S S S S S S S S S S S S S S S S S S H - ", +" H H S S S S S S S S S S S S S S S S S S S S S O [ lXeX,X,X,XeXeXfXfXfXfXfXfXfXfXfXfXfXfXgXfXzXzX/ . S S S S S S S S S S S S S S S S S S H S H H S S S S H H S H S S H H H H S H H H H H H H H H H H H H H H H H H H H H H R H H R R R R R R R R R R R R R R R R R R R W W W W W W W W W E E E E E E . . . . . . . . . . . . .X.X.X.X.X..._ .KXJXJXHXJXJXJXJXJXKX .) ........X.....X.X. . . . .E E W R R R H H S S S S S S S S S S S S S S S S S S S H ", +" R S S S S S S S S S S S S S S S S S S S S S : X '.lX,X,X,XeXeXeXfXfXfXfXfXfXfXfXfXfXfXfXgXfXzXzX' . l S S S S S S S S S S S S S S S S S H H S S S H H H H S H H H H H H H S H H H H H H H H H H H H H H H H H H H H H H H H R R R R R R R R R R R R R R R R R R R R W W W W W W W W W E E E E E E E . . . . . . . . . . . .X. .X.X.X.) _ AXJXHXHXHXHXJXJXJXJXo.^ ..X.....X.X...X.X. . . . .E E W R R R H H H S S S S S S S S S S S S S S S S S S R ", +" : H S S S S S S S S S S S S S S S S S S S S S ; % @XlX,X,XeXeXeXfXfXfXfXfXfXfXfXfXfXfXfXfXfXfXzXzX*.. > S S S S S S S S S S S S S S S S H H H S S S H H H H H H H H H H H S S H H H H H H H H H H H H H H H H H H H H H H H H R R R R R R R R R R R R R R R R R R R R W W W W W W W W E E E E E E E .E . . . . . . . . . . . . .X.) ! VXHXHXHXHXHXHXHXJXJXbX! X.X.X.....X.X.X.X. . . . .E E W R R R H H H S S S S S S S S S S S S S S S S S S H S ", +" H S S S S S S S S S S S S S S S S S S S S S S & > O '.lXfX,XfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXfXfXgXgXfXxXzX@XF O l S S S S S S S S S S S S H H H S S S S S S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H R H H H H R R R R R R R R R R R R R R R R R W W W W W W W W E E E E E E E E E E E . . . .E L BXHXHXHXHXHXHXHXHXHXHXHXHXHXHXAXVXVXVXVXVXBXBXBXBXBXBXMXL D R H H H S S S S S S S S S S S S S S S S S S S H S ", +" H S S S S S S S S S S S S S S S S S S S S S S S ; O }.lXeXeXfXfXfXfXfXfXfXfXfXfXfXfXgXgXfXgXgXgXgXgXgXzXxX*.@ & S S S S S S S S S S S H S S S S S S S S H H H H H H S S H H H H H H H H H H H H H H H H H H H H H H H H R H R R R R R R R R R R R R R R R R R R R W W W W W W W W W E E E E E E E E E E E E . .L nXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXHXFXFXFXFXFXFXGXGXGXNX3 D H S S S S S S S S S S S S S S S S S S S S S H ", +" R S S S S S S S S S S S S S S S S S S S S S S S ; % XXlXfXeXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXgXgXgXgXgXgXzXzX|.F O k D S S S S S S S S S S S S S S S S H H H H H H H S S S S H H H H H H H H H H H H H H H H H H H H H H H R R R H R R R R R R R R R R R R R R R R R W W W W W W W W W W W E E E E E E E E E E E 4 MXFXFXFXHXHXHXFXHXHXHXHXHXHXHXHXHXFXDXDXDXDXDXSXSXSXCXCXCXCX| < S S S S S S S S S S S S S S S S S S S S S R ", +" > R S S S S S S S S S S S S S S S S S S S S S S S * @ |.lXfXfXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXgXgXgXgXgXgXhXzXxX/.* % S S S S S S S S S S H H H H H H H S H H H H S S S S S S H H H H H H H H H H H H H H H H H H H H H H R R R H H R R R R R R R R R R R R R R R R R R R R W W W W W W W W E E E E E E E E E E L nXFXDXDXDXFXFXFXFXFXFXFXFXDXDXDXDXDXDXDXDXSXSXSXCXCXCXZXZXZX| < S S S S S S S S S S S S S S S S S S S S R > ", +" S H S S S S S S S S S S S S S S S S S S S S S S S * % }.lXgXfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXgXgXgXgXgXgXhXgXzXzXvX` @ & S S S S S S S S S H H H S H H S H H H H H S S S S S S H H H H H H H H H H H H H H H H H H H H H H R R H H R R R R R R R R R R R R R R R R R R R R R W W W W W W W W E W E E E E E E E 4 nXFXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXSXSXSXSXCXCXCXCXZXZXZXNXT < S S S S S S S S S S S S S S S S S S S H S ", +" H S S S S S S S S S S S S S S S S S S S S S S S S = O '.lXgXeXfXfXfXfXfXfXfXfXfXfXfXgXgXgXgXgXgXgXgXgXhXhXhXzXzX}.F o ; S S S S S S S S S S S S S S S H H S H S S S S H H S H H H H H H H H H H H H H H H H H H H H H H R H H H R R R R R R R R R R R R R R R R R R R R R W W W W W W W W W E E E E E E E 2 MXFXDXDXDXDXDXDXDXDXDXDXDXDXDXDXSXSXSXSXSXCXCXCXCXZXZXNXNXNXZ < S S S S S S S S S S S S S S S S S S S R ", +" R S S S S S S S S S S S S S S S S S S S S S S S S ; O ).lXgXeXfXfXfXfXfXfXfXfXfXfXgXgXgXgXgXgXgXgXgXhXhXhXgXxXzXxX).S o ; S S S S S S S S S S S S S S S S S S S H H H H S S H H H H H H H H H H H H H H H H H H H H H H H H H H R R R R R R R R R R R R R R R R R R R R R R W W W W W W W W W W E W W W L O.DXFXFXFXFXFXFXFXFXFXFXFXFXFXGXGXSXSXSXSXSXCXCXCXZXZXZXZXMX: < S S S S S S S S S S S S S S S S S S R ", +" - R S S S S S S S S S S S S S S S S S S S S S S S S > . -.lXkX,XfXfXfXfXfXfXfXfXfXfXfXfXgXgXgXgXgXgXgXgXhXhXhXhXzXzXjX).S O j S S S S S S S S S S D H H J S S H H H H H H H S H H H H H H H H H H H H H H H H H H H H H H H H H R R R R R R R R R R R R R R R R R R R R R R R R R W W W W W W W W W W W W L o.BXSXSXCXCXCXCXCXCXCXCXCXCXCXCXZXZXZXZXZXZXNXNXNXNXNXMXV , S S S S S S S S S S S S S S S S S S R - ", +" : H S S S S S S S S S S S S S S S S S S S S S S S S l . ' kXlX,XfXfXfXfXfXfXfXfXfXfXfXgXgXgXgXgXgXgXgXhXhXhXhXhXhXzXzXjX).S o & S S S S S S S S S H H J S S S H H H H H J S S H H H H H H H H H H H H H H H H H H H H H H H H R R R R R R R R R R R R R R R R R R R R R R R R R R W W W W W W W W W W W W L L E E E | E W W W W W T T T T T T T T Y H J G S V V , , V S S S S S S S S S S S S S S S S S R S ", +" S H S S S S S S S S S S S S S S S S S S S S S S S S l O P dXzXfXfXfXfXfXfXfXfXgXfXfXfXgXgXgXgXgXgXgXhXhXhXhXhXhXhXhXzXzXjX{.I & @ k S S S S S S S S S S S S S H H H H H H D H H H H H H H H H H H H H H H H H H H H H H H H R H H R H H H H R R R R R R R R R R R R R R R R R R R R W W W W W W R W W R R R L L L L L L F F F F F D D 3 3 1 < < < < < < < , : < S S S S S S S S S S S S S S S S S S H S ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S O : @XzXgXfXfXfXfXfXfXgXfXfXfXgXgXgXgXgXgXgXhXhXhXhXhXhXhXhXhXhXzXzXzX|.[ l @ & l S S S S S S S S S S S S H H H H H S S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H R H R R H R R R R R R R R R R R R R R R R R R W R R R R R R R R R R R R R R R R R R R H H H H H H D S S S S +.S S S S S S S S S S S S S S S S S S S S H H ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S % & {.lXkXfXfXfXfXfXfXgXgXfXgXgXgXgXgXgXgXgXhXhXhXhXhXhXhXhXhXhXgXkXzXzX:X).I ; @ i l S S S S S S S S S S S H S S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H R R H H n b b b b n Y R R R R R R R R R R R R R R R R R R R R R R R R R R R R H H H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S H ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S = . *.kXzXfXfXfXfXfXfXgXfXgXgXgXgXgXgXgXgXgXhXhXhXhXhXhXhXhXhXhXhXgXhXlXzXlX@X_.I j % u l S S S S S S S S S S S H H S H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H H G h a n ( /.].].(.` n b H R R R R R R R R R R R R R R R R R R R R R R R R H H H H H H H D S S S S S S S S S S S S S S S S S S S S S S S S S S R ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S > . K :XzXgXfXfXfXfXfXfXgXgXgXgXgXgXgXgXgXgXhXhXhXhXhXhXhXhXhXhXgXgXgXgXkXlXlXkX@X).` V & % i l S S S S S S S S S S H J S S S H H H H H H H H H H H H H H H H H H H H H H H G h a g G ` `.+X7X^.^.^.^.^.+X] b G R R R R R R R R R R R R R R R R R R R H H H H H H H H H D S S S S S S S S S S S S S S S S S S S S S S S S S S S R ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S S @ ; {.zXlXfXfXfXfXfXfXgXgXgXgXgXgXgXgXgXgXhXhXhXhXhXhXhXhXhXgXgXgXgXgXgXgXkXlXlXlX:X|.(.` S i % y i j l S S S S S H S S S J J H H H H H H H H H H H H H H H G l m a a x J ` (. X1X8X8X8X7X7X7X~.~.^.^.^. Xb Z R R R R R R R R R R R R R R R R R H H H H H H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S R $ ", +" - R S S S S S S S S S S S S S S S S S S S S S S S S S S S * O [ jXzXgXfXfXfXfXgXgXgXgXgXgXgXgXgXgXhXhXhXhXhXhXhXgXhXgXgXgXgXgXgXgXgXrXrXrXlXlXlXiX:XXX`.*.` F l i y y u i d j l l l S S S G G S l l l x j d u u u i l K ` =.`..X1X8X8X8X8X7X3X3X3XE.3XE.E.E.E.~.!..Xm G H H H R R R R H R R H H H H H H H H H H H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S R $ ", +" - R S S S S S S S S S S S S S S S S S S S S S S S S S S S l . < XXzXlXfXfXfXfXgXgXgXgXgXgXgXgXgXgXhXhXhXhXhXhXgXgXgXgXgXgXgXgXgXrXrXrXrXrXrXrXrXpXpXpXpXiXtX@X|.'.(.] ' / I F F S j k k k k j S K F U / ' =.(.`.|.+X6X9X8X8X8X8X7X3X5X3X3X3X3X3XE.E.E.E.E.E.E.Q.!.(.p H H H H H H H H H H H H H H H H H H H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R # ", +" - R S S S S S S S S S S S S S S S S S S S S S S S S S S S S & @ ] jXzXhXfXfXfXfXgXgXgXgXgXgXgXgXhXhXhXgXhXhXgXgXgXgXgXgXgXgXrXrXrXrXrXrXrXrXrXeXrXwXwXrXiXpXpXpXuXuXyXyXwX . : {.zXzXfXfXfXfXgXgXgXgXgXgXgXgXhXhXgXgXgXgXgXgXgXgXgXgXgXrXrXrXrXrXrXrXeXeXrXeXwXwXwXwXeXqXwXwXwXwXyXyXyXyXuXuXuXuXuXuXuX0XuX0X9X9X5X5X5X5X5X5X5X3X5X2X3X3X3X3X3X3X2X3X3XR.E.E.R.E.R.R.R.R.R.!.(.p H H H H H H H H H H H H H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R # ", +" - R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S * O / @XzXzXfXfXgXgXgXgXgXgXgXhXhXhXhXgXgXgXgXgXgXgXgXgXgXrXrXrXrXrXeXrXrXeXwXeXwXqXwXwXeXwXwXqXwXqXwXwXwXwXqXqXwX5XwX5X5X5X4X4X4X5X5X5X5X5X5X5X5X3X2X2X3X2X2X2X2X2X2X2X2X2X2XR.R.R.R.R.R.Y.Y.!.y.g J H H H H H H H H H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R # ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l @ * /.dXzXxXfXgXgXgXgXgXgXgXhXhXgXgXgXgXgXgXgXgXgXgXgXrXgXrXrXrXeXeXeXeXwXeXeXqXeXwXwXqXqXqXwXqXqXwXwXwXqXqXqX4XwX5X5X5X4X4X4X4X4X4X5X4X4X2X4X4X2X2X2X2X2X2X2X2X2X2X2X2XR.R.R.R.R.Y.Y.Y.Y.Q.m.l G H H H H H H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R $ ", +" R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S > . : ).kXzXxXfXgXgXgXgXgXgXgXgXgXgXgXgXgXgXgXgXfXgXfXgXgXrXeXeXeXeXeXeXeXeXeXeXeXqXqXqXqXqXqXqXqXqXqXqXqXqX4XqXwX4X4X4X4X4X4X4X4X4X4X4X2X4X4X2X2X2X2X2X2X2X2X2X2X2XR.R.R.R.R.Y.K.K.K.K.T.P.V V S H H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R ", +" R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S * . F {.kXzXxXfXgXgXgXgXgXgXgXgXgXgXgXgXgXgXfXgXfXfXgXrXrXrXeXeXeXeXeXeXeXeXeXeXeXqXqXqXqXqXqXqXqXqXqX4XqXqXqX4X4X4X4X4X4X4X4X4X4X2X4X4X4X2X2X2X2X2X2X2X2X2X2X2X&XK.K.K.K.K.K.K.K.K.T.u.k S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S & . P {.gXzXxXgXfXgXgXhXgXgXgXgXgXgXgXgXfXgXgXeXrXeXrXrXeXeXeXeXeXeXeXqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXqX4X4X4X4X4X4X4X4X4X2X2X2X2X2X2X2X2X2X2X2X2X2XR.K.K.R.K.K.K.K.K.K.K.J.G.Q.,.u S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S & O F {.cXzXzXgXgXhXgXgXgXgXgXgXgXfXfXfXfXeXrXeXeXeXeXeXeXeXeXeXeXqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXqXqX4X4X4X4X4X4X4X4X4X4X2X2X2X=X2X2X2X2X&X&X&X&XK.K.K.K.K.K.K.K.J.J.J.G.G.Y.8 u S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" : R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S 3 & O F _.:XzXzXhXgXgXgXgXgXgXgXgXfXfXeXeXeXeXeXeXeXeXeXeXeXeXeXqXqXeXqXeXeXqXqXqXqXqXqXqXqXqXqX4X4X4X4X4X4X4X4X4X4X4X2X2X=X=X=X2X2X&X&X&X%X%X%XK.K.K.K.J.J.J.J.J.G.J.S.Q.u.u l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H : ", +" - R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S * O > ] @XzXzXzXgXgXgXgXfXfXfXfXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXqXeXeXeXeXqXqXqXqXqXqXqX4X4X4X4X4X4X=X4X4X=X=X=X=X=X=X=X=X=X=X&X&X%X%X%X%XK.K.K.K.J.J.J.J.G.G.S.Y.H.3 u S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R - ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S ; O & I ).:XzXzXkXgXfXgXrXfXfXeXfXeXeXeXeXeXeXeXeXeXeXeXeXeXqXeXeXqXqXqXqXqXqX>XqXqX4X4X4X4X=X=X=X=X4X=X=X=X=X=X=X=X=X&X&X%X%X%X%X%XJ.J.J.J.J.J.J.J.J.S.G.T.Y.8 w S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R ", +" S R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S > & O : [ }.jXzXzXkXrXfXfXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXqXqXqXqXqXqX>X>XqX4X4X=X=X=X=X=X=X=X=X=X=X=X=X=X%X%X%X%X%X%X%X%XJ.J.J.J.J.J.J.J.J.K.T.Y.u.5 w V S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H S ", +" > R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S > o @ S *.|.:XlXlXlXgXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXeXqXqX>XqX>XqXqX=X=X=X=X=X=X=X=X=X=X=X=X=X=X=X&X&X&X%X%X%X%X%X%X%X%X%XJ.J.J.J.J.Y.E.Y.F.,.9 * u S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R > ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S l = o % 3 [ {.@XgXlXlXlXrXeXeXeXeXeXeXeXeXeXeXeXeXqXeX>XqX>X>X>X=X=X=X=X=X=X=X=X=X=X=X=X=X=X&X&X&X&X%X%X%X%X%X%X%XJ.J.K.R.E.E.J.u.,.9 3 % w l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H ", +" S R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S : ; @ @ ; I *.}.@XeXpXlXlXiXwXeXeXeXeXeXeX>X>X>X>X>XqX>X>X>X=X=X=X=X=X=X=X=X=X=X=X&X&X*X&X*X*X*X%X%X%X%X2X3XE.R.J.u.,.$.5 ; @ @ = : 3 : S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" - R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S > * @ % > P [ ).XX;XeXiXpXpXpXiXwXwXqXeXqX>X>X>X>X=X=X=X=X=X=X=X=X=X=X=X=X&X&X=X2X2X3X5X5X3X2X%X$Xy.:.9 4 > % @ & > : 3 : S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R - ", +" S R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S 3 S : > * @ @ * : P ` /.`.}.oX:X>XwXyXyXyXuXuXyXyXyXyX9X9X9X9X9X9X9X9X5X5X3X2X*X$XOX[.`.$.8 4 : * @ @ * > : S S : l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" - R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S : 3 3 S : > * % @ @ % ; 3 P / ' =.=.(.(.`.[.[.[.[.[.[.[.[.`.(.(.=.=.9 6 4 3 > & @ @ % * ; : S S S +.S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R - ", +" S R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S 3 3 : > ; * & % @ @ @ @ % & & & & & & & & % @ @ @ @ @ % * ; > : 3 : 3 S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S : : : : : : 3 : 3 : : : : : : : : : : : : : : : : l 3 3 3 3 S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H # ", +" - H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S : : : : N N S : S S S S : : l S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H > ", +" : R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S : S S S S S S S S S S S V S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H : ", +" S R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R H ", +" S R S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S 3 S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R S ", +" S R H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R S ", +" S H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R S ", +" S H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H : ", +" > H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S R H : ", +" # S H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H S # ", +" > H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H R H : ", +" : H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H : $ ", +" $ : H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H : # ", +" $ > S H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H : $ ", +" - : S H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H : - ", +" - : S H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H S - ", +" # > : S H H H H S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S H H H H S S - $ ", +" # - S S H H H H H H H S H S S S S S S S S S S S S S S H S S H H H H H H S : - # ", +" $ # > - - - S S S S S S S S S S S S > - - > - $ " +}; diff --git a/share/pixmaps/bitcoinz32.png b/share/pixmaps/bitcoinz32.png new file mode 100644 index 0000000000000000000000000000000000000000..4c1b85362cf9ca3613488c1332fd10988ea49f3c GIT binary patch literal 4570 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 literal 0 HcmV?d00001 diff --git a/share/pixmaps/bitcoinz32.xpm b/share/pixmaps/bitcoinz32.xpm new file mode 100644 index 000000000..cea5700f8 --- /dev/null +++ b/share/pixmaps/bitcoinz32.xpm @@ -0,0 +1,244 @@ +/* XPM */ +static char *result[] = { +/* columns rows colors chars-per-pixel */ +"32 32 206 2 ", +" c #1B1F3B", +". c #1D223D", +"X c #252525", +"o c #2B2B2B", +"O c #3F2D2B", +"+ c #22263E", +"@ c #24293E", +"# c #292A3E", +"$ c #363636", +"% c #3D3D3C", +"& c #3E3737", +"* c #42302F", +"= c #43373B", +"- c #6B3534", +"; c #574D3B", +": c #64593B", +"> c #6A5F39", +", c #735E32", +"< c #7B6A36", +"1 c #00004C", +"2 c #0C1748", +"3 c #1E2443", +"4 c #1D2A4D", +"5 c #1D2C51", +"6 c #232741", +"7 c #242A43", +"8 c #2A2E43", +"9 c #222D4D", +"0 c #282F49", +"q c #26264B", +"w c #2E3243", +"e c #2C334B", +"r c #26324D", +"t c #333543", +"y c #363843", +"u c #3B3D45", +"i c #313549", +"p c #34394C", +"a c #3A3C4A", +"s c #272A54", +"d c #243252", +"f c #2C3753", +"g c #2E3955", +"h c #263458", +"j c #353954", +"k c #3B3F56", +"l c #3A3A59", +"z c #3E3E65", +"x c #423E44", +"c c #3F4046", +"v c #3E414D", +"b c #3C4254", +"n c #3D5963", +"m c #454545", +"M c #494644", +"N c #4A4945", +"B c #44454D", +"V c #47484E", +"C c #4C4C4B", +"Z c #474843", +"A c #514F46", +"S c #545145", +"D c #5B5543", +"F c #52524D", +"G c #5A5B4C", +"H c #434552", +"J c #4C4D52", +"K c #4C4C5C", +"L c #454858", +"P c #4F5155", +"I c #545454", +"U c #5C5C52", +"Y c #54555C", +"T c #5C5C5C", +"R c #585353", +"E c #634B49", +"W c #6A4B49", +"Q c #635E46", +"! c #63574B", +"~ c #634D50", +"^ c #645553", +"/ c #675757", +"( c #6C6443", +") c #6C6947", +"_ c #6C674B", +"` c #66634B", +"' c #736A49", +"] c #7B6F42", +"[ c #74714C", +"{ c #79734A", +"} c #666554", +"| c #494D66", +" . c #4C5064", +".. c #5B5E64", +"X. c #546B6D", +"o. c #467176", +"O. c #636263", +"+. c #656B6B", +"@. c #706C6B", +"#. c #8A6A2A", +"$. c #9A7424", +"%. c #83733C", +"&. c #927D3B", +"*. c #8A7C4C", +"=. c #857B48", +"-. c #BE8F1C", +";. c #BA911F", +":. c #978D39", +">. c #B99428", +",. c #A48C39", +"<. c #B29B3C", +"1. c #B0A43F", +"2. c #C99816", +"3. c #CE8E11", +"4. c #DFB311", +"5. c #D7A514", +"6. c #E2A40E", +"7. c #FFBC03", +"8. c #FCB601", +"9. c #FAAA01", +"0. c #DCAE2F", +"q. c #CCAC39", +"w. c #C7A834", +"e. c #DBB53B", +"r. c #EDB721", +"t. c #FFC400", +"y. c #FFCB00", +"u. c #FFD400", +"i. c #FFDB01", +"p. c #FFD00D", +"a. c #FFCE11", +"s. c #FFE100", +"d. c #FFED0D", +"f. c yellow", +"g. c #FFE019", +"h. c #FBC728", +"j. c #FFD520", +"k. c #E6D83A", +"l. c #F8D73A", +"z. c #EEC937", +"x. c #FFE923", +"c. c #FFF229", +"v. c #FFF928", +"b. c #EEE234", +"n. c #FBE83C", +"m. c #FFFF33", +"M. c #FFFF3B", +"N. c #FFF635", +"B. c #8A824B", +"V. c #80814B", +"C. c #98884A", +"Z. c #999653", +"A. c #968C52", +"S. c #A9994A", +"D. c #A99744", +"F. c #B9A647", +"G. c #BCB14B", +"H. c #BBAC55", +"J. c #B3A955", +"K. c #BAB358", +"L. c #C9B94E", +"P. c #C1BE47", +"I. c #C3AB54", +"U. c #C6B454", +"Y. c #CAB857", +"T. c #D7BB57", +"R. c #D4BC48", +"E. c #C6C54A", +"W. c #D8C947", +"Q. c #DFDE42", +"!. c #CBC659", +"~. c #DAC657", +"^. c #D4D458", +"/. c #E8CA45", +"(. c #F8D844", +"). c #E6D65D", +"_. c #F8D95C", +"`. c #E0D548", +"'. c #FDE747", +"]. c #FFFE42", +"[. c #FFFF4C", +"{. c #FFF540", +"}. c #F9E259", +"|. c #FFF554", +" X c #FFFD54", +".X c #FDF55D", +"XX c #FFFF5C", +"oX c #F2F257", +"OX c #EAE553", +"+X c #FFF463", +"@X c #FFFD65", +"#X c #FFFF6C", +"$X c #FFFF72", +"%X c #6A8D8D", +"&X c #6C908F", +"*X c #659FA0", +"=X c #4EA5BF", +"-X c #65ACAD", +";X c #6AB8B7", +":X c #5ED5DB", +">X c #65CFD0", +",X c #5FE0E6", +" =.&.&.%.> Z 8 d P C X 3X3X3X3X3X3X3X", +"3X3X3X3X3X3X% I 8 @ ) F./.'.N.N.c.x.j.r.>.> r f R m 3X3X3X3X3X3X", +"3X3X3X3X3Xm J . S U. X[.].(.e.q.w.0.h.g.d.p.2.; 4 P N 3X3X3X3X3X", +"3X3X3X3XN J =..XXX|.F.] Z y w w u Z < >.a.s.7.#.4 L N 3X3X3X3X", +"3X3X3X% J A.#X@X~.D 6 6 t u m m c p 9 9 : 5.i.y.$.4 P % 3X3X3X", +"3X3XX I =.$X#XH.8 6 m m m u m m m m N m 9 f >.u.y.#.5 I X 3X3X", +"3X3XC w S +X$XK. w M m m c x m m m M m m N p 5 ;.u.7.; g C 3X3X", +"3XX I @ T.$X).6 t M m m 3 ] k.Z 4 i i N N m N p r 6.i.3.r P o 3X", +"3Xm 8 _ @X+XQ + M C t 8 6 D.M.q.w.w.Q e C m M C d : t.t., h m 3X", +"3XC + H.#XU.. m m t Q G./.(.'.m.m.c.h.N a V N C V d 3.i.3.d F 3X", +"o V m _.@X{ 6 m m 3 S. X'.'./.<.q.l.c.,.d C C C N h < u.9.m H o ", +"$ i _ @X}.N t m m 8 { |. XF.6 2 4 z.v.<.s C C C C b S 8.t., g $ ", +"x @ *.@X~.y x m m t A }. XS.2 p ' b.b.N g C F C C L v 6.i.#.d % ", +"m + C.@XY.w x m m m t ~. X'.R.(.].M.k.:.H V I I F P b 2.s.$.5 m ", +"m + C.@XY.w m m m m 6 J.XX'.[.[.Q.W.{.m.F.g I I I I b 2.d.$.9 m ", +"c 7 *.@XT.y u m m m 6 B.@X XG.F H s G.M.m.} L R Y P L 4.s.#.d % ", +"$ i ( +X_.A y m M m t ` .XXXB.1 f j E.M.m.{ L T T | ` i.u., g $ ", +"o V Z _.@X{ 6 m m V c Z ).XXOXZ.K.OX[.M.Q.| Y T O.| { f.y.m H o ", +"3XP + I.#XI.+ m M V m 8 Y.@XXXXX X X XE.T .~ E E ~ ..:.< p F 3X", +"3Xm w ' @X+XQ + N m N 6 Z.#X.X^.XX X} b K ^ -X>X;X;XO.z j m m 3X", +"3Xo F + T.#X~.6 t N M m C ' G q !.oXb K } / *X-X1X m w m F N o 3X3X3X3X3X3X3X", +"3X3X3X3X3X3X3X3X3Xo m P v w @ . 3 6 p b I m o 3X3X3X3X3X3X3X3X3X", +"3X3X3X3X3X3X3X3X3X3X3X3Xo $ % m m % % o 3X3X3X3X3X3X3X3X3X3X3X3X" +}; diff --git a/share/pixmaps/bitcoinz64.png b/share/pixmaps/bitcoinz64.png new file mode 100644 index 0000000000000000000000000000000000000000..0e9cd52ecd84ce9cd99717074aeee378c1bbd6eb GIT binary patch literal 9550 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 c #42413F", +", c #554E3C", +"< c #5C523C", +"1 c #655838", +"2 c #765F2F", +"3 c #796736", +"4 c #6F653D", +"5 c #081445", +"6 c #161E49", +"7 c #0E0E4C", +"8 c #1C294C", +"9 c #1E2441", +"0 c #1B2C51", +"q c #222541", +"w c #252A43", +"e c #2A2D42", +"r c #242D4A", +"t c #2A2E4B", +"y c #2D3243", +"u c #2B334B", +"i c #26324D", +"p c #323543", +"a c #363944", +"s c #3B3D43", +"d c #33394B", +"f c #393C4C", +"g c #34354B", +"h c #252C51", +"j c #223052", +"k c #2C3657", +"l c #333453", +"z c #3B3D53", +"x c #373B59", +"c c #393E69", +"v c #413F51", +"b c #3F4045", +"n c #3D434A", +"m c #3E4155", +"M c #444444", +"N c #484745", +"B c #4A4945", +"V c #44454A", +"C c #4B4B4B", +"Z c #47484B", +"A c #554B45", +"S c #555243", +"D c #5A5647", +"F c #4B4B53", +"G c #434455", +"H c #4E5057", +"J c #535353", +"K c #5C5C5B", +"L c #555458", +"P c #674A4A", +"I c #625D44", +"U c #655554", +"Y c #655D5D", +"T c #695757", +"R c #674F50", +"E c #696548", +"W c #756C47", +"Q c #7C754A", +"! c #777247", +"~ c #696851", +"^ c #727151", +"/ c #444865", +"( c #5D5E62", +") c #4C6873", +"_ c #626262", +"` c #6A6B6B", +"' c #686867", +"] c #667778", +"[ c #615F60", +"{ c #83672B", +"} c #967628", +"| c #967E35", +" . c #84753C", +".. c #A27920", +"X. c #A77F1F", +"o. c #837B4A", +"O. c #BA8A1A", +"+. c #99853C", +"@. c #9F9036", +"#. c #A88926", +"$. c #A68B39", +"%. c #A4943A", +"&. c #B69A37", +"*. c #BC9922", +"=. c #B7A83B", +"-. c #D9990D", +";. c #CA9918", +":. c #C69215", +">. c #D7AB13", +",. c #E6A80A", +"<. c #FEAF02", +"1. c #F2A903", +"2. c #FFB301", +"3. c #FFBB01", +"4. c #FDB30C", +"5. c #FFBB0E", +"6. c #FDBA16", +"7. c #C6A435", +"8. c #D3AE38", +"9. c #D9B737", +"0. c #D1A82A", +"q. c #F1BD28", +"w. c #C09722", +"e. c #FFC300", +"r. c #FFCC01", +"t. c #FFCB0B", +"y. c #FFD301", +"u. c #FFD905", +"i. c #FFC518", +"p. c #FFD418", +"a. c #E8CE0E", +"s. c #FFE601", +"d. c #FFFA00", +"f. c #D9C43A", +"g. c #FEC727", +"h. c #FFDB2B", +"j. c #FFD528", +"k. c #FDCD33", +"l. c #FDCE3B", +"z. c #FED334", +"x. c #FEDC34", +"c. c #FED43C", +"v. c #FFDC3C", +"b. c #F2D030", +"n. c #E8CD3B", +"m. c #FFE82A", +"M. c #FFFC2B", +"N. c #FFE534", +"B. c #FFE43B", +"V. c #FFEB3D", +"C. c #FCEA35", +"Z. c #FFFE35", +"A. c #FFF33C", +"S. c #FFFC3C", +"D. c #FBF138", +"F. c #86814D", +"G. c #928C4D", +"H. c #968947", +"J. c #98914A", +"K. c #9B9752", +"L. c #948C50", +"P. c #A2954D", +"I. c #A79944", +"U. c #A59851", +"Y. c #A6944F", +"T. c #B8A84B", +"R. c #BCB649", +"E. c #B7A754", +"W. c #B8B353", +"Q. c #A9A04F", +"!. c #C8B847", +"~. c #D3B948", +"^. c #CAB756", +"/. c #D8BC57", +"(. c #C2AA4A", +"). c #D6C34A", +"_. c #CCC558", +"`. c #D8C75A", +"'. c #CDC74D", +"]. c #E4C64A", +"[. c #E7D748", +"{. c #FED543", +"}. c #FEDC44", +"|. c #FDDD4C", +" X c #F8D647", +".X c #E3CA56", +"XX c #E5D759", +"oX c #FDDD5D", +"OX c #FADC55", +"+X c #FFE343", +"@X c #FFEC44", +"#X c #FEE24C", +"$X c #FFEC4C", +"%X c #FFF445", +"&X c #FFFF43", +"*X c #FFF44A", +"=X c #FFFE4C", +"-X c #E8E756", +";X c #FEE352", +":X c #FEEA54", +">X c #FFE45D", +",X c #FEEC5B", +" > v s s s b M M M M M M M N C J M vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXM J N > M M M M M p w q r y a s s a y w 8 r a b N M M M M N J M vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvX% J C > M M M M y 9 $ B 4 H.&.7.8.9.9.0.7.&.| 3 , y 8 u M M M M M C J * vXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXN J M > M > v + w D H.!.{.+XV.V.B.x.z.j.j.j.h.p.j.g.w.} < r 8 s N M M M C C vXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXo J N M M M M # @ E R.#X*X@X}.c.c.c.l.l.k.k.k.g.g.g.g.i.p.p.i.*.2 i r M M M M C J o vXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXO J N M > M p X I ^.1X=X|. X X{.{.+XB.B.N.N.h.h.h.h.g.i.g.g.6.6.p.t.;.1 8 d M M b N J % vXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvX% J M b M M + a Q.3X2XOX|.}.|.$X%X+Xn.8.&.$.$.$.$.&.0.q.j.p.p.g.6.6.6.r.t.#.s r M M M N J % vXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXO J M M M M X D XX4X>XOXOX#X1X$X~.+.1 M p e w 8 8 w e p M 1 | 0.p.p.i.6.4.6.y.,.< 8 b N M M J O vXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXo J N M M * . Q wXwX>XOX;X2X:XT.< y 9 w b b n M V M V M M a e 8 y 1 #.i.t.6.6.4.t.e.2 0 s M M M J o vXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXJ N > M s . L.rXeXoXoX3X4XR.S X w s M M N V M M M M M M M N M N n i 8 , *.y.t.4.4.e.e.} 0 n M M N J vXvXvXvXvXvXvX", +"vXvXvXvXvXvXC C M M b X L.rXeXoX>X4X,XW w M N M M M M M M N M M N V M M M V M N N u 8 3 6.r.4.4.3.e.} 0 b M M C C vXvXvXvXvXvX", +"vXvXvXvXvX% J M M M . Q rXeX8X8XwXyXM . s N M M M M M M M N M N M N M M N M N M V V B n 8 > >.y.4.<.2.e.{ 8 M M N J % vXvXvXvXvX", +"vXvXvXvXvXJ > > M X D rXeX8X8XrX^.# + N N N M M M M N M M M M M M M M N V N V N M N N N V i u ;.y.4.4.3.3.1 j M M M J vXvXvXvXvX", +"vXvXvXvXM N M M p & 7XrX6X8XrX_.+ # N M M M M M M M M N b f N V M N N N N V V N V N V N N C u i ;.r.4.<.e.1.a d N M N M vXvXvXvX", +"vXvXvXvXJ M b M . E.wX8X8XtXyX# # N M M M M M M N M M w a s s C V V N V M N N N N M N V V N B u i ,.r.<.<.e.O.8 M M M J o vXvXvX", +"vXvXvXM N M M + E wX9X8XrX9Xv + B M M M M M M M M M v H.9.9.S d N M N N M V V N N V V N N N N Z e v 3.e.<.2.3.2 0 N M C M vXvXvX", +"vXvXvXJ M M > @ /.rX6X8XrXQ . M M M M M N M N M M * > B.S.Z.3 5 r r r i N N N C N N Z Z N N Z N N 0 3 r.2.<.e.-.i M M M J vXvXvX", +"vXvX* B b M X W wX8X8XrX^. s M M M M M M > M N M s 5 I.V.B.7.S +.&.%.4 e a B N N Z N N Z N N N N M 0 ;.r.<.<.3.2 j N M C * vXvX", +"vXvXC > > M @ ^.wX6X9XeXS @ M M M N M M N v e + @ b S 8.V.c.v.N.N.m.m.m.&.p d B Z N N Z N N Z Z N C i , 3.3.2.e.-.i n M M C vXvX", +"vXo J v M e D ,X9XoXrXE.. N M M M M M N s a E H.T.].+X+X{.c.v.z.c.z.k.j.M.0.i n Z N Z Z N C Z C C N C 0 O.r.2.2.2.< u M N C o vX", +"vX* C M N K.wXoX8XeXI @ N M M M M > v p T.2X=X*X$X+X X{.c.c.v.z.z.k.j.h.m. .i B C N Z C Z C N C N C k 1 e.2.2.e...0 N M C * vX", +"vXN N M v % /.qX8XwX/.e s M M M M M N a , :X1X|.|.}.}.}.@X%XA.B.B.N.k.k.k.m.0.d n C C Z C Z N C C C C C d -.e.2.e.-.a b M M N vX", +"vXC M M p S oX>XoXwXL.X M M N M M M N s v ).1X|.|.|.}.$X[.7.+.4 I 9.C.z.j.h.j.S n C C C C C C C C C C C j } r.2.2.2., d M M C vX", +"o C M M q Q qXoX>X9XI e M M V M M N N M w T.2X#X;X|.$XI.p y 8 y 6 , x.x.j.N.h.D n C C C C C C C C C C C m < e.3.2.3.{ j M M C o ", +"O C M M + J.wX>X9XXXM a M M N M M M M M + F.4X;XOX$X Xy r N N C V r 8.D.x.m.b.C Z C J C J C C J C C C C m Z 1.e.2.r...j M M J O ", +"* C M M @ E.qXoXqX^.y M M M M M M M N M e E 3X;X#X$X|.N y Z V d 6 v n.C.x.M.=.i C C J C C C C C C C J C C d ;.r.2.e.:.u M M N % ", +"s N M b & ^.qXoXwXE.+ M M M N M N M M M a N OX1X;X;X=XH.5 r s B 4 f.S.B.A.n.M z J J C J J J J J J C J C C k *.y.2.e.-.d b b N * ", +"* M M s * /.qXoXwXY.+ M M M N M M M M N V y ^.2X;X;X$X#XI.I.).v.S.%XB.V.Z.J.f l G J C J J J J C J J C J F k #.y.3.e.-.s s > M b ", +"N M M s * /.9XoXwXK.+ N M M M M V M N V N 9 P.4X;X;XOX$X=X=X%X@X@X@X@XA.B.C.D.=.n m J J C J J C J C J J J j #.y.3.e.,.b n M > M ", +"M M M * - `.,XoXwXK.X M M N M M M M M N N w Q 4X:X;X:X;X#X#X#X*X&X&X&X&X%XA.S.M.f.m F J J J J J J J J J H k @.u.3.e.,.s s M > M ", +"- M M * s /.9XoXwXU.+ M V M M M M N V V N p D :X3X:X;X;X#X*X=X*X[.R.J.!.S.S.A.A.M.%.k J L J J J J J J J H k #.s.3.e.,.b f M b > ", +"* N M v $ ^.qXoXqXE.+ N M M M N M N N N N s s .X4X;X:X;X2X'.Q E C d h h !.S.S.A.Z.C.F H L J L K J J K J L m *.u.e.r.-.d b > M * ", +"% C M M # E.qXoXqX^.y b N M M V M V N V N V w W.4X,X:X1X[.a 6 f F H J l ~ &XS.S.S.Z.Q x K L K J K K J K L m >.u.e.r.O.i b M C % ", +"O J M M + P.qXoX,X.XM s M M M M N V N M N N q G.wX,X;X2X).d F J J J J j E &X&XS.Z.Z.G.x K L K K K K L L L H a.u.e.y.X.j M > C O ", +"o J M M + o.,XoX8X9XI # M M N M M N N V V N e ~ 4X3X,X3X:XS u F z l l m '.&X&X&XS.Z.F.m K K K K K K K K / ! s.u.r.r.{ i M M J o ", +"vXF M M $ S oX>XoXwXG. V N M N V N N M N N s N .< i M b B * vX", +"vXo J M M @ I ,X>X>XwXQ. N M M M Z N V V N N w Q wXqX,X3X4X4X4X4X2X2X2X2X2X'.~ k K K _ Y J U U T T Y ~ ' ( / F m m , M > J O vX", +"vXvXC M M M @ /.3XoX9X9XA r N N M M M M N V N d I wX3X3X4XwX4X4X4X2X2X-XL.~ z m K Y ( _ Y ` ] ] uX] ] ` _ _ K H Z B > > M J vXvX", +"vXvX> C M N X W qXoXoXwX(.9 M M N Z N Z N N V M y `.rXwXwX-X_.K.XX4X2XW.7 v K K K K K P pXlXxXxXlXxXlXfXU _ K J F n n > N * vXvX", +"vXvXvXJ v M b # .XqXoX8XrXW X N M N V N N V N C a C U.K.W C g 7 G.4X4X2XH F K K K _ [ D iXjXjXjXlXxXlXfXR _ K J N M M b J vXvXvX", +"vXvXvXN M > N X E wX>XoXwX M M J o vXvXvX", +"vXvXvXvXM C > N y * M b N N vXvXvXvX", +"vXvXvXvXvXJ N M N X I wX9X>X8XrXW.w e C N N N Z N Z C N C C C J J J J J J K L K ( [ [ ` U U jXlXdXP o.` [ J N b M > J vXvXvXvXvX", +"vXvXvXvXvX* F M M M X o.wX9X8X8XtX`.s X M N N Z Z N N Z C C C C C C J J J J L K K K _ Y P fXlXzXP 1 R P J N b M > J * vXvXvXvXvX", +"vXvXvXvXvXvXC C M M b X P.rX9X8X8XrX0X~ y N N N Z Z N C C C C J C J J J K L K K K Y A pXgXkXkXfXsXsXaX- b > b N C vXvXvXvXvXvX", +"vXvXvXvXvXvXvXJ M M M * . P.rX9X8X8XrXtXE.C X e V N C Z C C C C J C C J J J J J L K U , sXgXgXkXkXgXhXsX= - M N J vXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXO J M M M s o.wXwX8X8XeXtXeXK.D e q y b N Z C C C J C C Z z l x z L K J ( iX) uXuX) ) b * > M J O vXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvX% J M M M s . E 0XtX9X8X9XrXtXwX^.F.D b y r w t y r i f n E J.R.H.m J K S - - ; ; = = * M M J % vXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvX% J M M M M + * W.rXtX9XeX8XeXwXwX3XXX^.Q.P.J.J.K.T.!.[.%X&XS.Z.H.l C C C N M s M M M M J % vXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvX% J M M M N e X E `.tXtXeX8X9X9X,X3X4X2X2X2X=X=X=X&X%X@X+Xv.Z.f.y V M M b M v - > M J % vXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXO J C M M M b X # Q _.wXrXwX9X:X>X;X;X;X;X#X#X#X}.}.+XB.B.Z.+.w M b > b > M M C J O vXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXC J * M M N s X # E U.`.,XwX4X4X3X1X*X1X$X$X*X%XV.v.9.+.p * M M M M M M C C vXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvX* J N M M M N s @ X & D o.K.(./.)..X].].~.7.I.3 < a X s > M > M M N C * vXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXN C N M M M M M b $ + X @ & * > > * & @ 9 9 y b M M M M b N J M vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXO N J C M > M M M M > > s * * & a s b M M > M M M M C J N O vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX* C C C M > > b M b b b M > M M M b M N C J C * vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXO * N C J Z N M M > > M M N C J C N * O vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX", +"vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXo % * * > b M > > * O o vXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvXvX" +}; diff --git a/share/pixmaps/nsis-header.bmp b/share/pixmaps/nsis-header.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7ea23f9366e28634e05cb3a041d0826dd9940625 GIT binary patch literal 25818 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 ZKnZL!h@pUR0;DU5VSoT7u+5N={{cY1b9?{* literal 0 HcmV?d00001 diff --git a/share/pixmaps/nsis-wizard.bmp b/share/pixmaps/nsis-wizard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..947c4993519f7bb027551a528aa5d1310b6a9e8c GIT binary patch literal 154542 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